#14186·Cline

VS Code extension ignores `.cline/rules/`, which the SDK resolver supports

Author: franjorubCreated Sep 16, 2026Updated Sep 16, 2026
LabelsVS Code

Cline Surface

VSCode Extension

Cline Version

4.1.18

Beta version

  • I am using a beta version of Cline

What happened?

This issue was spotted meanwhile working with Cline with human supervision, I supported myself with the agent to write the issue detailed as possible

Summary

Project rules placed in .cline/rules/ are loaded by the SDK/CLI but silently ignored by the VS Code extension. The extension's rule loader is hardcoded to .clinerules and never consults the SDK's resolveRulesConfigSearchPaths(), which searches both locations.

Because .cline/skills/ is supported by the extension, a project that adopts the .cline/ layout ends up in a state where its skills load and its rules do not — with no warning, no log line, and no entry in the Rules panel. The rules read as binding to anyone reviewing the repo, but never reach the model.

Why this looks like a bug rather than "just use .clinerules/"

Three things make the current behavior surprising rather than merely undocumented:

  1. The SDK explicitly supports <workspace>/.cline/rules. It is not an accident or a leftover — it is a named constant combination in the shared resolver.
  2. The SDK labels .clinerules as DEPRECATED_CONFIG_DIR. The location the extension exclusively supports is the one the SDK calls deprecated, so the two halves of the product point in opposite directions.
  3. The extension already depends on @cline/shared and @cline/core (workspace:* in apps/vscode/package.json, v4.1.18) and already imports from @cline/shared/storage elsewhere — just not for rules.

Evidence

SDK — sdk/packages/shared/src/storage/paths.ts supports both locations:

const DEPRECATED_CONFIG_DIR = ".clinerules";
const CLINE_CONFIG_DIR = ".cline";
export const RULES_CONFIG_DIRECTORY_NAME = "rules";

export function resolveRulesConfigSearchPaths(workspacePath?: string): string[] {
	const wsPaths = workspacePath
		? [
				join(workspacePath, DEPRECATED_CONFIG_DIR),                         // .clinerules
				join(workspacePath, CLINE_CONFIG_DIR, RULES_CONFIG_DIRECTORY_NAME), // .cline/rules
			]
		: [];
	// ...
}

VS Code extension — apps/vscode/src/core/storage/disk.ts supports only one:

export const GlobalFileNames = {
	clineRules: ".clinerules",
	workflows: ".clinerules/workflows",
	hooksDir: ".clinerules/hooks",
	clineruleSkillsDir: ".clinerules/skills",
	// ...
}

The extension's rule loader (apps/vscode/src/core/context/instructions/user-instructions/cline-rules.ts) resolves that constant and nothing else:

const localClineRulesFilePath = path.resolve(workingDirectory, GlobalFileNames.clineRules)

apps/vscode/src/core/context/instructions/user-instructions/rule-helpers.ts repeats the same hardcoded literals.

By contrast, skills are handled correctlyapps/vscode/src/core/storage/skill-directories.ts scans several roots including .cline/skills:

const SKILL_DIRECTORY_NAMES = {
	clineruleSkillsDir: ".clinerules/skills",
	clineSkillsDir: ".cline/skills",
	claudeSkillsDir: ".claude/skills",
	agentsSkillsDir: ".agents/skills",
}

…and getSkillsDirectoriesForScan() in the same file walks all of them:

{ path: path.join(cwd, SKILL_DIRECTORY_NAMES.clineruleSkillsDir), source: "project" },
{ path: path.join(cwd, SKILL_DIRECTORY_NAMES.clineSkillsDir), source: "project" },

This is the inconsistency: for skills the extension deliberately scans multiple roots including .cline/, while for rules it resolves a single hardcoded constant.

The extension already consumes SDK storage helpers, e.g. apps/vscode/src/sdk/sdk-task-history.ts:

import { resolveSessionDataDir } from "@cline/shared/storage"

So wiring resolveRulesConfigSearchPaths in would follow an existing pattern rather than introduce a new dependency.

Support matrix (as of apps/vscode v4.1.18)

Path VS Code extension SDK / CLI
.clinerules/ ✅ supported ✅ supported (DEPRECATED_CONFIG_DIR)
.cline/rules/ ignored ✅ supported
.cline/skills/ ✅ supported ✅ supported

Steps to reproduce

  1. In any workspace, create .cline/rules/test-rule.md with no frontmatter (so it should always be active):

    # Test rule
    
    Always begin your reply with the exact token RULE-LOADED.
    
  2. Also create .cline/skills/test-skill/SKILL.md, so a working .cline/ artifact can be compared against a non-working one in the same workspace.

  3. Make sure there is no .clinerules/ directory in the workspace.

  4. Open the workspace in the VS Code extension and start a new task.

  5. For the comparison, run a task over the same workspace through the CLI.

Expected: the rule is active (the reply starts with RULE-LOADED) and is listed in the Rules panel, matching the SDK/CLI behavior.

Actual:

  • VS Code extension — the rule is absent from the Rules panel and has no effect on the reply. The skill in .cline/skills/ is picked up normally in the same session.
  • CLI — the same rule is applied.

Moving the file to .clinerules/test-rule.md makes it work in the extension, confirming the content and frontmatter are not at fault.

Impact

The failure mode is silent, which is what makes it costly. A team that standardizes on .cline/ gets:

  • Rules that are ignored for every developer on the extension, while working for anyone on the CLI — so behavior differs per teammate with no visible cause.
  • No diagnostic anywhere: the directory exists, the files are valid, nothing warns.
  • False confidence in review: rules describing guardrails are read as enforced when they are inert.

This is easy to hit in practice. A repository with its rule set in .cline/rules/ will have those rules apply in a CLI-based session and be entirely absent in an extension-based one — so two people reviewing the same code get different agent behavior, with nothing in either UI to indicate why. Reading the extension and SDK sources is currently the only way to explain the discrepancy.

Possible resolutions

Any one of these would resolve the confusion; listed in order of preference:

  1. Make the extension use the SDK resolver — have the rule loader call resolveRulesConfigSearchPaths() from @cline/shared/storage, as it already does for other storage paths. This aligns both clients on one implementation and removes the duplicated literals in disk.ts / rule-helpers.ts.
  2. Add .cline/rules to the extension's lookup — mirroring how skill-directories.ts already scans multiple roots. Smaller change, but keeps two resolvers that can drift again.
  3. Documentation-only — if .cline/rules/ is intentionally SDK/CLI-only, say so explicitly in the Rules docs, which currently list .clinerules/ without mentioning that .cline/rules/ exists and works elsewhere. This is the least satisfying option given the SDK marks .clinerules deprecated.

Happy to open a PR for option 1 or 2 if you can confirm which direction you'd prefer — in particular whether .clinerules being DEPRECATED_CONFIG_DIR reflects a planned migration to .cline/ across all clients.

Environment

  • apps/vscode version inspected: 4.1.18 (apps/vscode/package.json)
  • Sources read from main as of 2026-09-16.

Files referenced above:

File Relevance
apps/vscode/src/core/storage/disk.ts GlobalFileNames.clineRules, hardcoded to .clinerules
.../user-instructions/cline-rules.ts the rule loader that resolves only that constant
.../user-instructions/rule-helpers.ts repeats the same literals
apps/vscode/src/core/storage/skill-directories.ts multi-root scan that does include .cline/skills
sdk/packages/shared/src/storage/paths.ts resolveRulesConfigSearchPaths(), searches both roots
sdk/packages/core/.../user-instruction-config-loader.ts SDK consumer of that resolver
apps/vscode/src/sdk/sdk-task-history.ts existing example of the extension importing @cline/shared/storage

Steps to reproduce

Provider/Model

No response

Diagnostics

No response

System Information

No response