vibe-coding-master 0.7.36 → 0.7.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -455,6 +455,7 @@ Harness Studio is the UI for VCM harness maintenance.
455
455
  Use it to:
456
456
 
457
457
  - inspect fixed harness status
458
+ - inspect detected project languages and verified language-server readiness
458
459
  - run bootstrap
459
460
  - open Harness Engineer
460
461
  - review harness files
@@ -466,6 +467,13 @@ Use it to:
466
467
  - merge task harness commits back to the connected repository branch when
467
468
  appropriate
468
469
 
470
+ VCM bundles the Claude Code LSP bridge and loads it for Architect, Coder, and
471
+ Reviewer sessions, including CCR launches. The project environment must still
472
+ provide the language server for each detected language: `rust-analyzer`,
473
+ `typescript-language-server`, `pyright-langserver`, `gopls`, `clangd`, or
474
+ `jdtls`. Harness Studio reports a missing executable or failed startup probe
475
+ with the backend diagnostic.
476
+
469
477
  Harness Engineer is task-scoped and runs from the active task worktree. The
470
478
  backend automatically starts a fresh Harness Engineer for each active task or
471
479
  resumes its saved Session. Durable memory is versioned with the project harness
@@ -18,8 +18,9 @@ export function createClaudeAdapter(runner) {
18
18
  }
19
19
  return result.stdout.trim();
20
20
  },
21
- buildRoleStartCommand(role, command = "claude", permissionMode = "default", claudeSessionId, resume = false, model = "default", effort = "default", settingsOverride, appendSystemPrompt) {
22
- const args = ["--agent", role];
21
+ buildRoleStartCommand(role, command = "claude", permissionMode = "default", claudeSessionId, resume = false, model = "default", effort = "default", settingsOverride, appendSystemPrompt, pluginDirs = []) {
22
+ const args = pluginDirs.flatMap((pluginDir) => ["--plugin-dir", pluginDir]);
23
+ args.push("--agent", role);
23
24
  const sessionSettings = { ...settingsOverride };
24
25
  if (claudeSessionId) {
25
26
  args.push(resume ? "--resume" : "--session-id", claudeSessionId);
@@ -6,6 +6,7 @@ export function createCommandRunner() {
6
6
  const result = await execa(command, args, {
7
7
  cwd: options.cwd,
8
8
  env: options.env,
9
+ timeout: options.timeoutMs,
9
10
  reject: false
10
11
  });
11
12
  return {
@@ -20,6 +20,7 @@ import { renderProjectManagerHarnessRules } from "../templates/harness/project-m
20
20
  import { renderPullRequestTemplateHarnessRules } from "../templates/harness/pull-request-template.js";
21
21
  import { renderTesterHarnessRules } from "../templates/harness/tester-agent.js";
22
22
  import { renderVcmArchitectureInterviewSkillRules } from "../templates/harness/vcm-architecture-interview-skill.js";
23
+ import { renderVcmCodeNavigationSkillRules } from "../templates/harness/vcm-code-navigation-skill.js";
23
24
  import { renderVcmFinalAcceptanceSkillRules } from "../templates/harness/vcm-final-acceptance-skill.js";
24
25
  import { renderVcmHarnessBootstrapSkillRules } from "../templates/harness/vcm-harness-bootstrap-skill.js";
25
26
  import { renderVcmLongRunningValidationSkillRules } from "../templates/harness/vcm-long-running-validation-skill.js";
@@ -64,18 +65,18 @@ const AGENT_FRONTMATTER = {
64
65
  },
65
66
  architect: {
66
67
  description: "VCM architecture role for plans, module boundaries, public contracts, verifiable behavior, and docs sync.",
67
- tools: "Read, Grep, Glob, Bash, Edit, Write, Agent"
68
+ tools: "Read, Grep, Glob, Bash, Edit, Write, Agent, LSP"
68
69
  },
69
70
  coder: {
70
71
  description: "VCM implementation role for scoped code changes and focused tests.",
71
- tools: "Read, Grep, Glob, Bash, Edit, Write, Agent"
72
+ tools: "Read, Grep, Glob, Bash, Edit, Write, Agent, LSP"
72
73
  },
73
74
  tester: {
74
75
  description: "VCM testing role for validation, test adequacy, approved-scope validation, and risk findings."
75
76
  },
76
77
  reviewer: {
77
78
  description: "VCM independent gate review role for architecture plans, validation adequacy, and code diffs.",
78
- tools: "Read, Grep, Glob, Bash, Write"
79
+ tools: "Read, Grep, Glob, Bash, Write, LSP"
79
80
  },
80
81
  translator: {
81
82
  description: "VCM task-scoped translation tool role for conversation translation, file translation, bootstrap, and memory updates."
@@ -93,6 +94,11 @@ const AGENT_FRONTMATTER = {
93
94
  effort: "xhigh"
94
95
  }
95
96
  };
97
+ const REQUIRED_AGENT_TOOLS = {
98
+ architect: ["Agent", "LSP"],
99
+ coder: ["Agent", "LSP"],
100
+ reviewer: ["LSP"]
101
+ };
96
102
  const MANAGED_FILES = [
97
103
  {
98
104
  path: "CLAUDE.md",
@@ -256,6 +262,12 @@ const WHOLE_FILES = [
256
262
  mode: 0o644,
257
263
  content: renderSkillFile("VCM Architecture Interview Skill", "vcm-architecture-interview", "Use when Architect must confirm user-owned behavior and contract decisions before architecture planning.", renderVcmArchitectureInterviewSkillRules())
258
264
  },
265
+ {
266
+ path: ".claude/skills/vcm-code-navigation/SKILL.md",
267
+ category: "skill",
268
+ mode: 0o644,
269
+ content: renderSkillFile("VCM Code Navigation Skill", "vcm-code-navigation", "Use when Architect or Reviewer must resolve code symbols, references, implementations, call hierarchies, or bounded dependency paths.", renderVcmCodeNavigationSkillRules())
270
+ },
259
271
  {
260
272
  path: ".claude/skills/vcm-final-acceptance/SKILL.md",
261
273
  category: "skill",
@@ -657,8 +669,8 @@ async function installManagedFile({ projectRoot, definition, dryRun, operations
657
669
  if (definition.memoryBlock) {
658
670
  nextContent = ensureVcmMemoryBlock(nextContent);
659
671
  }
660
- if (definition.agentName === "architect") {
661
- nextContent = ensureAgentTool(nextContent, "Agent");
672
+ for (const requiredTool of REQUIRED_AGENT_TOOLS[definition.agentName] ?? []) {
673
+ nextContent = ensureAgentTool(nextContent, requiredTool);
662
674
  }
663
675
  await writeIfChanged({
664
676
  targetPath,
@@ -246,6 +246,7 @@ export function createDefaultServerDeps(options = {}) {
246
246
  });
247
247
  const harnessService = createHarnessService({
248
248
  fs,
249
+ commandRunner: runner,
249
250
  git,
250
251
  runtime,
251
252
  harnessEngineerSessions: sessionService,
@@ -0,0 +1,253 @@
1
+ import path from "node:path";
2
+ import { VCM_LSP_PLUGIN_DIR, VCM_LSP_PLUGIN_MANIFEST, VCM_LSP_PLUGIN_NAME } from "./lsp-plugin.js";
3
+ const LANGUAGE_DEFINITIONS = [
4
+ {
5
+ language: "rust",
6
+ label: "Rust",
7
+ extensions: [".rs"],
8
+ manifests: ["Cargo.toml"],
9
+ serverCommand: "rust-analyzer",
10
+ pluginServerName: "rust-analyzer",
11
+ probeArgs: ["--version"]
12
+ },
13
+ {
14
+ language: "typescript",
15
+ label: "TypeScript / JavaScript",
16
+ extensions: [".ts", ".tsx", ".js", ".jsx", ".mts", ".cts", ".mjs", ".cjs"],
17
+ manifests: ["package.json", "tsconfig.json", "jsconfig.json"],
18
+ serverCommand: "typescript-language-server",
19
+ pluginServerName: "typescript",
20
+ probeArgs: ["--version"]
21
+ },
22
+ {
23
+ language: "python",
24
+ label: "Python",
25
+ extensions: [".py", ".pyi"],
26
+ manifests: ["pyproject.toml", "requirements.txt", "setup.py"],
27
+ serverCommand: "pyright-langserver",
28
+ pluginServerName: "pyright",
29
+ probeArgs: ["--version"]
30
+ },
31
+ {
32
+ language: "go",
33
+ label: "Go",
34
+ extensions: [".go"],
35
+ manifests: ["go.mod"],
36
+ serverCommand: "gopls",
37
+ pluginServerName: "gopls",
38
+ probeArgs: ["version"]
39
+ },
40
+ {
41
+ language: "cpp",
42
+ label: "C / C++",
43
+ extensions: [".c", ".cc", ".cpp", ".cxx", ".h", ".hh", ".hpp", ".hxx"],
44
+ manifests: ["CMakeLists.txt", "compile_commands.json"],
45
+ serverCommand: "clangd",
46
+ pluginServerName: "clangd",
47
+ probeArgs: ["--version"]
48
+ },
49
+ {
50
+ language: "java",
51
+ label: "Java",
52
+ extensions: [".java"],
53
+ manifests: ["pom.xml", "build.gradle", "build.gradle.kts", "settings.gradle", "settings.gradle.kts"],
54
+ serverCommand: "jdtls",
55
+ pluginServerName: "jdtls",
56
+ probeArgs: ["--version"]
57
+ }
58
+ ];
59
+ const DEFAULT_PROBE_TIMEOUT_MS = 3_000;
60
+ const DEFAULT_CACHE_TTL_MS = 60_000;
61
+ export function createHarnessCodeIntelligenceDetector(fs, options = {}) {
62
+ const probeCache = new Map();
63
+ const probesInFlight = new Map();
64
+ const now = options.now ?? Date.now;
65
+ const cacheTtlMs = options.cacheTtlMs ?? DEFAULT_CACHE_TTL_MS;
66
+ return {
67
+ async detect(repoRoot) {
68
+ return detectCodeIntelligence(fs, repoRoot, options, async (definition, executablePath) => {
69
+ const cacheKey = `${executablePath}\u0000${definition.probeArgs.join("\u0000")}`;
70
+ const cached = probeCache.get(cacheKey);
71
+ const currentTime = now();
72
+ if (cached && cached.expiresAt > currentTime) {
73
+ return cached.result;
74
+ }
75
+ if (!options.runner) {
76
+ return undefined;
77
+ }
78
+ const existingProbe = probesInFlight.get(cacheKey);
79
+ if (existingProbe) {
80
+ return existingProbe;
81
+ }
82
+ const probePromise = options.runner.run(executablePath, definition.probeArgs, {
83
+ cwd: repoRoot,
84
+ timeoutMs: options.probeTimeoutMs ?? DEFAULT_PROBE_TIMEOUT_MS
85
+ });
86
+ probesInFlight.set(cacheKey, probePromise);
87
+ const result = await probePromise.finally(() => probesInFlight.delete(cacheKey));
88
+ probeCache.set(cacheKey, {
89
+ expiresAt: currentTime + cacheTtlMs,
90
+ result
91
+ });
92
+ return result;
93
+ });
94
+ }
95
+ };
96
+ }
97
+ export async function detectHarnessCodeIntelligence(fs, repoRoot, options = {}) {
98
+ return createHarnessCodeIntelligenceDetector(fs, options).detect(repoRoot);
99
+ }
100
+ async function detectCodeIntelligence(fs, repoRoot, options, probe) {
101
+ const indexPaths = await readModuleIndexPaths(fs, repoRoot);
102
+ const pluginServers = await readPluginServers(fs, options.pluginDir ?? VCM_LSP_PLUGIN_DIR);
103
+ const detectedLanguages = await Promise.all(LANGUAGE_DEFINITIONS.map(async (definition) => {
104
+ const detectedBy = await detectLanguage(fs, repoRoot, indexPaths, definition);
105
+ if (detectedBy.length === 0) {
106
+ return undefined;
107
+ }
108
+ const pluginReady = pluginServers.get(definition.pluginServerName) === definition.serverCommand;
109
+ const executablePath = await findExecutable(fs, repoRoot, definition.serverCommand, options);
110
+ const probeResult = executablePath ? await probe(definition, executablePath) : undefined;
111
+ const serverFound = Boolean(executablePath);
112
+ const serverRunnable = probeResult?.exitCode === 0;
113
+ const state = languageState(pluginReady, serverFound, serverRunnable, Boolean(probeResult));
114
+ const status = {
115
+ language: definition.language,
116
+ label: definition.label,
117
+ serverCommand: definition.serverCommand,
118
+ pluginName: VCM_LSP_PLUGIN_NAME,
119
+ detected: true,
120
+ pluginReady,
121
+ serverFound,
122
+ serverRunnable,
123
+ state,
124
+ error: languageError(state, definition.serverCommand, probeResult),
125
+ detectedBy
126
+ };
127
+ return status;
128
+ }));
129
+ const languages = detectedLanguages.filter((language) => language !== undefined);
130
+ const readyCount = languages.filter((language) => language.state === "ready").length;
131
+ return {
132
+ state: languages.length === 0
133
+ ? "not_detected"
134
+ : readyCount === languages.length
135
+ ? "ready"
136
+ : readyCount === 0
137
+ ? "missing"
138
+ : "partial",
139
+ languages
140
+ };
141
+ }
142
+ function languageState(pluginReady, serverFound, serverRunnable, probeCompleted) {
143
+ if (!pluginReady) {
144
+ return "plugin_missing";
145
+ }
146
+ if (!serverFound) {
147
+ return "server_missing";
148
+ }
149
+ if (!probeCompleted) {
150
+ return "server_unverified";
151
+ }
152
+ return serverRunnable ? "ready" : "server_failed";
153
+ }
154
+ function languageError(state, command, probeResult) {
155
+ if (state === "plugin_missing") {
156
+ return `VCM LSP plugin manifest is missing or does not declare ${command}.`;
157
+ }
158
+ if (state === "server_missing") {
159
+ return `${command} was not found in the VCM backend PATH.`;
160
+ }
161
+ if (state === "server_unverified") {
162
+ return `${command} was found but has not been probed by this runtime.`;
163
+ }
164
+ if (state !== "server_failed") {
165
+ return undefined;
166
+ }
167
+ const detail = firstNonEmptyLine(probeResult?.stderr, probeResult?.stdout);
168
+ return detail
169
+ ? `${command} failed its startup probe. ${detail}`
170
+ : `${command} failed its startup probe with exit code ${probeResult?.exitCode ?? 1}.`;
171
+ }
172
+ async function readPluginServers(fs, pluginDir) {
173
+ const manifestPath = pluginDir === VCM_LSP_PLUGIN_DIR
174
+ ? VCM_LSP_PLUGIN_MANIFEST
175
+ : path.join(pluginDir, ".claude-plugin", "plugin.json");
176
+ if (!await fs.pathExists(manifestPath)) {
177
+ return new Map();
178
+ }
179
+ try {
180
+ const manifest = await fs.readJson(manifestPath);
181
+ return new Map(Object.entries(manifest.lspServers ?? {})
182
+ .filter((entry) => typeof entry[1]?.command === "string")
183
+ .map(([name, server]) => [name, server.command]));
184
+ }
185
+ catch {
186
+ return new Map();
187
+ }
188
+ }
189
+ async function readModuleIndexPaths(fs, repoRoot) {
190
+ const indexPath = path.join(repoRoot, ".ai/generated/module-index.json");
191
+ if (!await fs.pathExists(indexPath)) {
192
+ return [];
193
+ }
194
+ try {
195
+ const value = await fs.readJson(indexPath);
196
+ return collectPathLikeStrings(value);
197
+ }
198
+ catch {
199
+ return [];
200
+ }
201
+ }
202
+ async function detectLanguage(fs, repoRoot, indexPaths, definition) {
203
+ const evidence = new Set();
204
+ for (const manifest of definition.manifests) {
205
+ if (await fs.pathExists(path.join(repoRoot, manifest))) {
206
+ evidence.add(manifest);
207
+ }
208
+ }
209
+ for (const filePath of indexPaths) {
210
+ if (definition.extensions.includes(path.extname(filePath).toLowerCase())) {
211
+ evidence.add(".ai/generated/module-index.json");
212
+ break;
213
+ }
214
+ }
215
+ return Array.from(evidence);
216
+ }
217
+ async function findExecutable(fs, repoRoot, command, options) {
218
+ const platform = options.platform ?? process.platform;
219
+ const pathEnv = options.pathEnv ?? process.env.PATH ?? "";
220
+ const extensions = platform === "win32"
221
+ ? (options.pathExt ?? process.env.PATHEXT ?? ".EXE;.CMD;.BAT;.COM").split(";")
222
+ : [""];
223
+ for (const directory of pathEnv.split(path.delimiter)) {
224
+ for (const extension of extensions) {
225
+ const candidate = path.resolve(directory || repoRoot, `${command}${extension.toLowerCase()}`);
226
+ if (await fs.pathExists(candidate)) {
227
+ return candidate;
228
+ }
229
+ }
230
+ }
231
+ return undefined;
232
+ }
233
+ function firstNonEmptyLine(...values) {
234
+ for (const value of values) {
235
+ const line = value?.split(/\r?\n/).map((part) => part.trim()).find(Boolean);
236
+ if (line) {
237
+ return line.length > 300 ? `${line.slice(0, 297)}...` : line;
238
+ }
239
+ }
240
+ return undefined;
241
+ }
242
+ function collectPathLikeStrings(value) {
243
+ if (typeof value === "string") {
244
+ return value.includes("/") || path.extname(value) ? [value] : [];
245
+ }
246
+ if (Array.isArray(value)) {
247
+ return value.flatMap(collectPathLikeStrings);
248
+ }
249
+ if (value && typeof value === "object") {
250
+ return Object.values(value).flatMap(collectPathLikeStrings);
251
+ }
252
+ return [];
253
+ }
@@ -17,6 +17,7 @@ import { renderProjectManagerHarnessRules } from "../templates/harness/project-m
17
17
  import { renderPullRequestTemplateHarnessRules } from "../templates/harness/pull-request-template.js";
18
18
  import { renderTesterHarnessRules } from "../templates/harness/tester-agent.js";
19
19
  import { renderVcmArchitectureInterviewSkillRules } from "../templates/harness/vcm-architecture-interview-skill.js";
20
+ import { renderVcmCodeNavigationSkillRules } from "../templates/harness/vcm-code-navigation-skill.js";
20
21
  import { renderVcmFinalAcceptanceSkillRules } from "../templates/harness/vcm-final-acceptance-skill.js";
21
22
  import { renderVcmHarnessBootstrapSkillRules } from "../templates/harness/vcm-harness-bootstrap-skill.js";
22
23
  import { renderVcmLongRunningValidationSkillRules } from "../templates/harness/vcm-long-running-validation-skill.js";
@@ -29,6 +30,7 @@ import { renderRequestArchitectRestartTool, renderRestartArchitectSkillRules } f
29
30
  import { submitTerminalInput } from "../runtime/terminal-submit.js";
30
31
  import { VcmError } from "../errors.js";
31
32
  import { bumpHarnessRevision, readHarnessRevisionState } from "./harness-revision.js";
33
+ import { createHarnessCodeIntelligenceDetector } from "./code-intelligence-service.js";
32
34
  const execFileAsync = promisify(execFile);
33
35
  const BOOTSTRAP_SESSION_PATH = ".ai/vcm/bootstrap/session.json";
34
36
  const HARNESS_ENGINEER_SESSION_PATH = ".ai/vcm/harness-engineer/session.json";
@@ -110,6 +112,14 @@ const HARNESS_FILES = [
110
112
  ownership: "whole-file",
111
113
  renderRules: renderVcmArchitectureInterviewSkillRules
112
114
  },
115
+ {
116
+ kind: "skill-vcm-code-navigation",
117
+ path: ".claude/skills/vcm-code-navigation/SKILL.md",
118
+ title: "VCM Code Navigation Skill",
119
+ frontmatter: renderSkillFrontmatter("vcm-code-navigation", "Use when Architect or Reviewer must resolve code symbols, references, implementations, call hierarchies, or bounded dependency paths."),
120
+ ownership: "whole-file",
121
+ renderRules: renderVcmCodeNavigationSkillRules
122
+ },
113
123
  {
114
124
  kind: "skill-vcm-route-message",
115
125
  path: ".claude/skills/vcm-route-message/SKILL.md",
@@ -187,7 +197,8 @@ const HARNESS_FILES = [
187
197
  path: ".claude/agents/reviewer.md",
188
198
  title: "Reviewer Agent",
189
199
  memoryBlock: true,
190
- frontmatter: renderAgentFrontmatter("reviewer", "VCM independent gate review role for architecture plans, validation adequacy, and code diffs.", { tools: "Read, Grep, Glob, Bash, Write" }),
200
+ requiredTools: ["LSP"],
201
+ frontmatter: renderAgentFrontmatter("reviewer", "VCM independent gate review role for architecture plans, validation adequacy, and code diffs.", { tools: "Read, Grep, Glob, Bash, Write, LSP" }),
191
202
  renderRules: renderReviewerAgentRules
192
203
  },
193
204
  {
@@ -260,8 +271,9 @@ const HARNESS_FILES = [
260
271
  path: ".claude/agents/architect.md",
261
272
  title: "Architect Agent",
262
273
  memoryBlock: true,
274
+ requiredTools: ["Agent", "LSP"],
263
275
  blankLineBeforeEnd: true,
264
- frontmatter: renderAgentFrontmatter("architect", "VCM architecture role for plans, module boundaries, public contracts, verifiable behavior, and docs sync.", { tools: "Read, Grep, Glob, Bash, Edit, Write, Agent" }),
276
+ frontmatter: renderAgentFrontmatter("architect", "VCM architecture role for plans, module boundaries, public contracts, verifiable behavior, and docs sync.", { tools: "Read, Grep, Glob, Bash, Edit, Write, Agent, LSP" }),
265
277
  renderRules: renderArchitectHarnessRules
266
278
  },
267
279
  {
@@ -269,7 +281,8 @@ const HARNESS_FILES = [
269
281
  path: ".claude/agents/coder.md",
270
282
  title: "Coder Agent",
271
283
  memoryBlock: true,
272
- frontmatter: renderAgentFrontmatter("coder", "VCM implementation role for scoped code changes and focused tests.", { tools: "Read, Grep, Glob, Bash, Edit, Write, Agent" }),
284
+ requiredTools: ["Agent", "LSP"],
285
+ frontmatter: renderAgentFrontmatter("coder", "VCM implementation role for scoped code changes and focused tests.", { tools: "Read, Grep, Glob, Bash, Edit, Write, Agent, LSP" }),
273
286
  renderRules: renderCoderHarnessRules
274
287
  },
275
288
  {
@@ -284,14 +297,20 @@ const HARNESS_FILES = [
284
297
  export function createHarnessService(deps) {
285
298
  const now = deps.now ?? (() => new Date().toISOString());
286
299
  const vcmVersion = deps.vcmVersion ?? "unknown";
300
+ const codeIntelligenceDetector = createHarnessCodeIntelligenceDetector(deps.fs, {
301
+ runner: deps.commandRunner
302
+ });
287
303
  return {
288
304
  async getHarnessStatus(repoRoot) {
289
- const analyses = await analyzeHarnessFiles(deps.fs, repoRoot);
305
+ const [analyses, codeIntelligence] = await Promise.all([
306
+ analyzeHarnessFiles(deps.fs, repoRoot),
307
+ codeIntelligenceDetector.detect(repoRoot)
308
+ ]);
290
309
  const legacyChanges = await analyzeLegacyCodexHarnessPaths(deps.fs, repoRoot);
291
310
  const manifestChange = deps.runFixedInstaller
292
311
  ? await analyzeHarnessManifest(deps.fs, repoRoot, vcmVersion)
293
312
  : undefined;
294
- return renderHarnessStatus(await readHarnessRevisionValue(deps.fs, repoRoot), analyses, legacyChanges, manifestChange);
313
+ return renderHarnessStatus(await readHarnessRevisionValue(deps.fs, repoRoot), analyses, legacyChanges, manifestChange, codeIntelligence);
295
314
  },
296
315
  async getHarnessFileContent(repoRoot, filePath) {
297
316
  return readHarnessFileContent(deps.fs, repoRoot, filePath);
@@ -320,14 +339,17 @@ export function createHarnessService(deps) {
320
339
  harnessCommit = (await commitHarnessVisibleChanges(deps.git, repoRoot, "chore(vcm-harness): update harness file")).harnessCommit;
321
340
  }
322
341
  const file = await readHarnessFileContent(deps.fs, repoRoot, definition.path);
323
- const analyses = await analyzeHarnessFiles(deps.fs, repoRoot);
342
+ const [analyses, codeIntelligence] = await Promise.all([
343
+ analyzeHarnessFiles(deps.fs, repoRoot),
344
+ codeIntelligenceDetector.detect(repoRoot)
345
+ ]);
324
346
  const legacyChanges = await analyzeLegacyCodexHarnessPaths(deps.fs, repoRoot);
325
347
  const manifestChange = deps.runFixedInstaller
326
348
  ? await analyzeHarnessManifest(deps.fs, repoRoot, vcmVersion)
327
349
  : undefined;
328
350
  return {
329
351
  file,
330
- status: renderHarnessStatus(await readHarnessRevisionValue(deps.fs, repoRoot), analyses, legacyChanges, manifestChange),
352
+ status: renderHarnessStatus(await readHarnessRevisionValue(deps.fs, repoRoot), analyses, legacyChanges, manifestChange, codeIntelligence),
331
353
  harnessCommit
332
354
  };
333
355
  },
@@ -1197,7 +1219,8 @@ async function analyzeHarnessFile(fs, repoRoot, definition) {
1197
1219
  if (!match) {
1198
1220
  const migratedContent = migrateLegacyHarnessFile(definition, currentContent, expectedBlock);
1199
1221
  if (migratedContent) {
1200
- const nextContent = definition.memoryBlock ? ensureVcmMemoryBlock(migratedContent) : migratedContent;
1222
+ const memoryUpdatedContent = definition.memoryBlock ? ensureVcmMemoryBlock(migratedContent) : migratedContent;
1223
+ const nextContent = ensureAgentTools(memoryUpdatedContent, definition.requiredTools);
1201
1224
  return {
1202
1225
  definition,
1203
1226
  status: {
@@ -1216,9 +1239,7 @@ async function analyzeHarnessFile(fs, repoRoot, definition) {
1216
1239
  };
1217
1240
  }
1218
1241
  const insertedContent = `${currentContent.trimEnd()}\n\n${expectedBlock}\n`;
1219
- const nextContent = definition.kind === "agent-architect"
1220
- ? ensureAgentTool(insertedContent, "Agent")
1221
- : insertedContent;
1242
+ const nextContent = ensureAgentTools(insertedContent, definition.requiredTools);
1222
1243
  return {
1223
1244
  definition,
1224
1245
  status: {
@@ -1240,9 +1261,7 @@ async function analyzeHarnessFile(fs, repoRoot, definition) {
1240
1261
  const currentBlock = match[0];
1241
1262
  const blockUpdatedContent = currentContent.replace(managedBlockPattern, expectedBlock);
1242
1263
  const memoryUpdatedContent = definition.memoryBlock ? ensureVcmMemoryBlock(blockUpdatedContent) : blockUpdatedContent;
1243
- const nextContent = definition.kind === "agent-architect"
1244
- ? ensureAgentTool(memoryUpdatedContent, "Agent")
1245
- : memoryUpdatedContent;
1264
+ const nextContent = ensureAgentTools(memoryUpdatedContent, definition.requiredTools);
1246
1265
  const action = currentContent === nextContent ? "ok" : "update";
1247
1266
  return {
1248
1267
  definition,
@@ -1300,7 +1319,7 @@ async function removeLegacyCodexHarnessPaths(fs, repoRoot) {
1300
1319
  }
1301
1320
  return changes;
1302
1321
  }
1303
- function renderHarnessStatus(harnessRevision, analyses, legacyChanges = [], manifestChange) {
1322
+ function renderHarnessStatus(harnessRevision, analyses, legacyChanges = [], manifestChange, codeIntelligence) {
1304
1323
  const files = analyses.map((analysis) => analysis.status);
1305
1324
  const plannedChanges = analyses
1306
1325
  .map((analysis) => analysis.plannedChange)
@@ -1326,6 +1345,7 @@ function renderHarnessStatus(harnessRevision, analyses, legacyChanges = [], mani
1326
1345
  files,
1327
1346
  needsApply: plannedChanges.length > 0,
1328
1347
  plannedChanges,
1348
+ codeIntelligence,
1329
1349
  warnings: plannedChanges.length > 0
1330
1350
  ? ["Review and commit VCM Harness changes before starting long-running work."]
1331
1351
  : []
@@ -1369,6 +1389,9 @@ function ensureAgentTool(content, requiredTool) {
1369
1389
  const nextTools = [...tools, requiredTool].join(", ");
1370
1390
  return content.replace(frontmatterMatch[0], frontmatterMatch[0].replace(toolsMatch[0], `tools: ${nextTools}`));
1371
1391
  }
1392
+ function ensureAgentTools(content, requiredTools) {
1393
+ return (requiredTools ?? []).reduce(ensureAgentTool, content);
1394
+ }
1372
1395
  function migrateLegacyHarnessFile(definition, currentContent, block) {
1373
1396
  const legacyContent = definition.legacyWholeFile?.trimEnd();
1374
1397
  if (!legacyContent) {
@@ -0,0 +1,9 @@
1
+ import path from "node:path";
2
+ import { fileURLToPath } from "node:url";
3
+ export const VCM_LSP_PLUGIN_NAME = "vcm-lsp-bridge";
4
+ export const VCM_LSP_PLUGIN_DIR = fileURLToPath(new URL("../../../scripts/claude-plugins/vcm-lsp-bridge", import.meta.url));
5
+ export const VCM_LSP_PLUGIN_MANIFEST = path.join(VCM_LSP_PLUGIN_DIR, ".claude-plugin", "plugin.json");
6
+ const LSP_ROLES = new Set(["architect", "coder", "reviewer"]);
7
+ export function roleUsesLsp(role) {
8
+ return LSP_ROLES.has(role);
9
+ }
@@ -8,6 +8,7 @@ import { submitTerminalInput } from "../runtime/terminal-submit.js";
8
8
  import { claudeTranscriptPath } from "./claude-transcript-service.js";
9
9
  import { readHarnessRevisionState } from "./harness-revision.js";
10
10
  import { getTaskRuntimeRepoRoot } from "./task-service.js";
11
+ import { roleUsesLsp, VCM_LSP_PLUGIN_DIR } from "./lsp-plugin.js";
11
12
  const TRANSLATOR_ROLE = "translator";
12
13
  const HARNESS_ENGINEER_ROLE = "harness-engineer";
13
14
  const TRANSLATION_DIR = ".ai/vcm/translations";
@@ -82,7 +83,7 @@ export function createSessionService(deps) {
82
83
  ? claudeTranscriptPath(taskRepoRoot, resumeClaudeSessionId, persisted?.claudeConfigDir)
83
84
  : undefined;
84
85
  const startCommand = {
85
- ...deps.claude.buildRoleStartCommand(role, config.claudeCommand, permissionMode, resumeClaudeSessionId, launchMode === "resume", model, effort, modelSettingsOverride, input.appendSystemPrompt),
86
+ ...deps.claude.buildRoleStartCommand(role, config.claudeCommand, permissionMode, resumeClaudeSessionId, launchMode === "resume", model, effort, modelSettingsOverride, input.appendSystemPrompt, roleUsesLsp(role) ? [VCM_LSP_PLUGIN_DIR] : []),
86
87
  cwd: taskRepoRoot
87
88
  };
88
89
  const runtimeSessionToken = randomUUID();
@@ -101,7 +102,10 @@ export function createSessionService(deps) {
101
102
  VCM_ROLE: role,
102
103
  VCM_SESSION_ID: claudeSessionId || undefined,
103
104
  VCM_RUNTIME_SESSION_TOKEN: runtimeSessionToken
104
- }, modelEnvironment, buildUsageTelemetryEnvironment(deps.apiUrl, role, model)),
105
+ }, modelEnvironment, {
106
+ ...buildUsageTelemetryEnvironment(deps.apiUrl, role, model),
107
+ ...(roleUsesLsp(role) ? { ENABLE_LSP_TOOL: "true" } : {})
108
+ }),
105
109
  cols: input.cols,
106
110
  rows: input.rows
107
111
  });
@@ -19,6 +19,13 @@ ${renderRoleMemoryRules("architect")}
19
19
  - In Debug Mode and Architecture Diagnosis Mode, writing baseline unit tests for changed code and running required L0/L1 plus applicable L2/L3 checks are part of the implementation duty; tester still owns final validation.
20
20
  - Do not make product priority or approval decisions; route those questions back to project-manager.
21
21
 
22
+ ### Work Persistence
23
+
24
+ - Treat Architect artifacts, not Session memory, as continuation state.
25
+ - Write each verified code fact, confirmed user decision, architecture decision, root cause, and validation result to its owning artifact as soon as it becomes an input to later work.
26
+ - Before ending any turn, ensure all information required to continue the current Architect work is present in the current artifacts.
27
+ - Keep artifacts current and self-contained. Replace superseded content instead of appending conversation history or investigation logs.
28
+
22
29
  ### Architecture Interview
23
30
 
24
31
  - Before the first Architecture Planning step of Code-Change Flow, use \`vcm-architecture-interview\` and complete \`.ai/vcm/handoffs/architecture-brief.md\` and \`.ai/vcm/handoffs/architecture-evidence.md\`.
@@ -38,6 +45,9 @@ ${renderRoleMemoryRules("architect")}
38
45
  ### Planning Code Reading
39
46
 
40
47
  - Do not plan from session memory, architecture docs, generated context, or code comments alone. Re-read current-worktree source and verify actual behavior from implementation.
48
+ - Use \`vcm-code-navigation\` for symbol definitions, implementations, references, call hierarchies, and bounded behavior paths. Start from generated indexes, use LSP semantic navigation when available, then read every resolved callable unit in full.
49
+ - Use structural search when available and Grep for dynamic registrations, configuration or string edges, macros not resolved by LSP, documentation, and explicit fallback discovery. When LSP is available, do not treat Grep as proof of a complete symbol, caller, implementation, or reference set.
50
+ - If LSP is unavailable or cannot resolve a required relationship, record the limitation and exact fallback evidence in \`architecture-evidence.md\`; do not claim compiler-accurate completeness from text search alone.
41
51
  - Define the planning boundary as the affected feature or module and identify every existing or intended observable entry point for the behavior being changed.
42
52
  - Read the complete implementation of each relevant existing entry point.
43
53
  - Follow every project-owned call path the plan will change through cross-module calls, state reads and writes, persistence, side effects, completion and failure signals, and consumers.
@@ -66,6 +76,8 @@ ${renderRoleMemoryRules("architect")}
66
76
  - When starting architecture planning, first write an \`Architecture Evidence Verification\` row to \`.ai/vcm/handoffs/planning-progress.md\`, with the evidence artifact, verified worktree revision, done criterion, and status. Complete it by checking that the evidence covers the accepted feature boundary and still matches the current worktree.
67
77
  - After \`Architecture Evidence Verification\` is complete, add the remaining planning steps: one head step for cross-module work (architecture decision, boundaries, ownership, invariants, build-configuration proofs), one middle step per affected module from the module index in dependency order — split a module into per-file steps when it exceeds one round — and one tail step for cross-module wiring, whole-plan ledger reconciliation, and final build evidence. A small task degrades to head, one middle step, and tail.
68
78
  - Bind every step to repository facts and machine checks: scope is module or file paths from the module index; deliverable is plan sections or ledger ID ranges; done criterion is a tool output or recorded check result — never a self-assessment.
79
+ - Complete a planning step only after its architecture conclusions, affected files, callable surfaces, ledger items, risks, and evidence references have been written to the corresponding sections of \`architecture-plan.md\`.
80
+ - Update \`architecture-plan.md\` before marking the corresponding \`planning-progress.md\` step complete. A conclusion that exists only in Session context is not completed planning work.
69
81
  - Update \`planning-progress.md\` at the end of every planning round: mark completed steps with their evidence and leave remaining steps unchanged. Do not shrink, merge, or drop a remaining step without recording the change and its reason.
70
82
  - If the round ends before all steps are done, report \`Planning Result: incomplete\` with the progress record; project-manager routes continuation. Never compress remaining enumeration or scaffolding into summary rows to reach \`Planning Result: complete\` within the current round — an honest \`incomplete\` with recorded progress is the required outcome.
71
83
  - \`planning-progress.md\` is task-runtime state for continuation and routing, not part of the reviewed plan; \`architecture-plan.md\` alone remains the executable plan of record.
@@ -135,6 +147,8 @@ ${renderRoleMemoryRules("architect")}
135
147
  ### Debug Mode
136
148
 
137
149
  - Project-manager may route bugs, failing tests, build/runtime failures, or unclear defects directly to architect Debug Mode.
150
+ - Maintain \`architect-debug.md\` throughout Debug Mode. Record the triggering failure and diagnostic evidence while investigating, write the confirmed root cause before implementing the fix, and add implementation and validation evidence as each stage completes.
151
+ - Do not postpone the first durable Debug record until the final handoff.
138
152
  - Architect may read source/tests, edit code, and run focused diagnostics until root cause is known. Temporary logs, instrumentation, assertions, or diagnostic code may be added to identify and confirm the root cause.
139
153
  - Once the root cause is confirmed, architect owns the technical change boundary for the fix. Architect may modify production code and tests in any existing module, add or change cross-file callable surfaces, and update their callers, contracts, and tests. No pre-approved module or file list limits Debug Mode implementation.
140
154
  - When editing production code or tests in Debug Mode, read and follow \`docs/CODING_STANDARDS.md\`.
@@ -164,6 +178,7 @@ Do not assume existing code or comments are correct. Read the implementation to
164
178
  Before choosing or implementing a fix:
165
179
 
166
180
  - Define the affected feature or module and identify every observable entry point for the failing behavior.
181
+ - Use \`vcm-code-navigation\` to resolve definitions, implementations, references, callers, callees, and call hierarchy before following the complete implementation path.
167
182
  - Read the relevant project and module architecture documents, public contracts, generated context, tests, handoff artifacts, and runtime evidence.
168
183
  - Starting from each entry point, read the complete implementation of every reachable project-owned function, method, handler, callback, or command.
169
184
  - Recursively follow every project-owned call until no unresolved project-owned callee remains. Read each symbol once and record recursive or cyclic calls.
@@ -175,8 +190,8 @@ Before choosing or implementing a fix:
175
190
 
176
191
  Maintain a \`Code Reading Closure\` in \`.ai/vcm/handoffs/architecture-diagnosis.md\`:
177
192
 
178
- | Symbol | File | Called By | Calls | State Read/Written | Side Effects | Status |
179
- |---|---|---|---|---|---|---|
193
+ | Symbol | Definition | Implementations | Called By | Calls | State Read/Written | Side Effects | Resolution Evidence | Status |
194
+ |---|---|---|---|---|---|---|---|---|
180
195
 
181
196
  \`Status\` must be \`read\`, \`external-boundary\`, or \`generated-boundary\`.
182
197
 
@@ -187,10 +202,15 @@ The code-reading phase is complete only when:
187
202
  - every indirect callback, event, hook, queue, route, and dynamic dispatch path has been resolved
188
203
  - every relevant state reader and writer has been read
189
204
  - every relevant cross-file surface caller and consumer has been read
205
+ - every semantic relationship records LSP, structural-search, Grep-fallback, runtime, external-boundary, or generated-boundary evidence
190
206
  - no unresolved project-owned symbol remains
191
207
 
192
208
  Do not diagnose the root cause or choose a fix before the Code Reading Closure is complete.
193
209
 
210
+ After Code Reading Closure is complete, write \`Current Architecture\`, \`Failure Trace\`, \`Architecture Assessment\`, and \`Required Architecture Direction\` before modifying code.
211
+
212
+ Update \`Implementation And Validation\` as implementation and validation stages complete. Do not keep the diagnosis direction or completed validation evidence only in Session context.
213
+
194
214
  After completing the code-reading closure, reconstruct and analyze:
195
215
 
196
216
  - **Ownership:** owners of state, decisions, lifecycle transitions, side effects, and durable artifacts.