vibe-coding-master 0.8.11 → 0.8.12
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/dist/backend/api/message-routes.js +4 -0
- package/dist/backend/services/code-intelligence-service.js +3 -1
- package/dist/backend/services/gate-review-service.js +24 -15
- package/dist/backend/services/harness-service.js +2 -1
- package/dist/backend/services/lsp-plugin.js +26 -0
- package/dist/backend/services/message-service.js +17 -1
- package/dist/backend/services/session-service.js +8 -5
- package/dist/backend/services/workflow-control-service.js +17 -5
- package/dist/backend/templates/harness/architect-agent.js +1 -1
- package/dist/backend/templates/harness/vcm-code-navigation-skill.js +1 -1
- package/dist-frontend/assets/{index-Db0OTAez.js → index-DqANomt1.js} +24 -24
- package/dist-frontend/index.html +1 -1
- package/package.json +1 -1
|
@@ -24,6 +24,10 @@ export function registerMessageRoutes(app, deps) {
|
|
|
24
24
|
const context = await getRouteContext(deps, request.params.taskSlug);
|
|
25
25
|
return deps.messageService.deleteMessageHistory(context);
|
|
26
26
|
});
|
|
27
|
+
app.post("/api/tasks/:taskSlug/messages/:messageId/confirm-delivered", async (request) => {
|
|
28
|
+
const context = await getRouteContext(deps, request.params.taskSlug);
|
|
29
|
+
return deps.messageService.confirmDeliveredDispatch(context, request.params.messageId);
|
|
30
|
+
});
|
|
27
31
|
app.get("/api/tasks/:taskSlug/orchestration", async (request) => {
|
|
28
32
|
return withOpenFileLimitFallback(async () => {
|
|
29
33
|
const context = await getRouteContext(deps, request.params.taskSlug);
|
|
@@ -64,7 +64,9 @@ export function createHarnessCodeIntelligenceDetector(fs, options = {}) {
|
|
|
64
64
|
const now = options.now ?? Date.now;
|
|
65
65
|
const cacheTtlMs = options.cacheTtlMs ?? DEFAULT_CACHE_TTL_MS;
|
|
66
66
|
return {
|
|
67
|
-
async detect(repoRoot) {
|
|
67
|
+
async detect(repoRoot, detectOptions) {
|
|
68
|
+
if (detectOptions?.refresh)
|
|
69
|
+
probeCache.clear();
|
|
68
70
|
return detectCodeIntelligence(fs, repoRoot, options, async (definition, executablePath) => {
|
|
69
71
|
const cacheKey = `${executablePath}\u0000${definition.probeArgs.join("\u0000")}`;
|
|
70
72
|
const cached = probeCache.get(cacheKey);
|
|
@@ -1173,7 +1173,7 @@ async function resolveCodeDiffBaseCommit(deps, context, record, headCommit) {
|
|
|
1173
1173
|
"merge-base",
|
|
1174
1174
|
"HEAD",
|
|
1175
1175
|
rootBranch
|
|
1176
|
-
], {
|
|
1176
|
+
], { expectedFailure: "no-common-ancestor" })).trim();
|
|
1177
1177
|
if (mergeBase
|
|
1178
1178
|
&& mergeBase !== headCommit
|
|
1179
1179
|
&& await isAncestor(deps.runner, context.taskRepoRoot, mergeBase, headCommit)) {
|
|
@@ -1185,13 +1185,13 @@ async function resolveCodeDiffBaseCommit(deps, context, record, headCommit) {
|
|
|
1185
1185
|
"--abbrev-ref",
|
|
1186
1186
|
"--symbolic-full-name",
|
|
1187
1187
|
"@{upstream}"
|
|
1188
|
-
], {
|
|
1188
|
+
], { expectedFailure: "missing-upstream" })).trim();
|
|
1189
1189
|
if (upstream) {
|
|
1190
1190
|
const mergeBase = (await commandStdout(deps.runner, context.taskRepoRoot, [
|
|
1191
1191
|
"merge-base",
|
|
1192
1192
|
"HEAD",
|
|
1193
1193
|
upstream
|
|
1194
|
-
], {
|
|
1194
|
+
], { expectedFailure: "no-common-ancestor" })).trim();
|
|
1195
1195
|
if (mergeBase
|
|
1196
1196
|
&& mergeBase !== headCommit
|
|
1197
1197
|
&& await isAncestor(deps.runner, context.taskRepoRoot, mergeBase, headCommit)) {
|
|
@@ -1201,7 +1201,9 @@ async function resolveCodeDiffBaseCommit(deps, context, record, headCommit) {
|
|
|
1201
1201
|
return headCommit;
|
|
1202
1202
|
}
|
|
1203
1203
|
async function isAncestor(runner, cwd, ancestor, descendant) {
|
|
1204
|
-
const result = await runner
|
|
1204
|
+
const result = await readGateGit(runner, cwd, ["merge-base", "--is-ancestor", ancestor, descendant], {
|
|
1205
|
+
expectedFailure: "not-ancestor"
|
|
1206
|
+
});
|
|
1205
1207
|
return result.exitCode === 0;
|
|
1206
1208
|
}
|
|
1207
1209
|
async function computeInputHash(deps, taskRepoRoot, gate, codeDiffInput, codeDiffSources) {
|
|
@@ -1412,18 +1414,19 @@ async function readCodeDiffSourceArtifactError(fs, taskRepoRoot, source) {
|
|
|
1412
1414
|
return undefined;
|
|
1413
1415
|
}
|
|
1414
1416
|
async function commandStdout(runner, cwd, args, options = {}) {
|
|
1415
|
-
const
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1417
|
+
const result = await readGateGit(runner, cwd, args, options);
|
|
1418
|
+
return result.exitCode === 0 ? result.stdout : "";
|
|
1419
|
+
}
|
|
1420
|
+
async function readGateGit(runner, cwd, args, options = {}) {
|
|
1421
|
+
const retryDelays = [500, 1000, 2000];
|
|
1422
|
+
for (let attempt = 0;; attempt += 1) {
|
|
1423
|
+
const result = await runner.run("git", ["-c", `safe.directory=${path.resolve(cwd)}`, ...args], { cwd });
|
|
1424
|
+
if (result.exitCode === 0 || isExpectedGitFailure(result, options.expectedFailure))
|
|
1425
|
+
return result;
|
|
1426
|
+
if (attempt < retryDelays.length) {
|
|
1427
|
+
await delay(retryDelays[attempt]);
|
|
1423
1428
|
continue;
|
|
1424
1429
|
}
|
|
1425
|
-
if (options.allowFailure)
|
|
1426
|
-
return "";
|
|
1427
1430
|
const detail = result.stderr.trim() || result.stdout.trim() || "Git did not provide an error message.";
|
|
1428
1431
|
throw new VcmError({
|
|
1429
1432
|
code: "GATE_REVIEW_GIT_COMMAND_FAILED",
|
|
@@ -1431,7 +1434,13 @@ async function commandStdout(runner, cwd, args, options = {}) {
|
|
|
1431
1434
|
statusCode: 409
|
|
1432
1435
|
});
|
|
1433
1436
|
}
|
|
1434
|
-
|
|
1437
|
+
}
|
|
1438
|
+
function isExpectedGitFailure(result, expected) {
|
|
1439
|
+
if (expected === "not-ancestor" || expected === "no-common-ancestor") {
|
|
1440
|
+
return result.exitCode === 1 && !result.stderr.trim() && !result.stdout.trim();
|
|
1441
|
+
}
|
|
1442
|
+
return expected === "missing-upstream" && result.exitCode === 128
|
|
1443
|
+
&& /^fatal: (no upstream configured for branch |HEAD does not point to a branch)/.test(result.stderr.trim());
|
|
1435
1444
|
}
|
|
1436
1445
|
function splitLines(value) {
|
|
1437
1446
|
return value
|
|
@@ -37,6 +37,7 @@ import { submitTerminalInput } from "../runtime/terminal-submit.js";
|
|
|
37
37
|
import { VcmError } from "../errors.js";
|
|
38
38
|
import { bumpHarnessRevision, readHarnessRevisionState } from "./harness-revision.js";
|
|
39
39
|
import { createHarnessCodeIntelligenceDetector } from "./code-intelligence-service.js";
|
|
40
|
+
import { lspUnavailableWarning } from "./lsp-plugin.js";
|
|
40
41
|
const execFileAsync = promisify(execFile);
|
|
41
42
|
const BOOTSTRAP_SESSION_PATH = ".ai/vcm/bootstrap/session.json";
|
|
42
43
|
const HARNESS_ENGINEER_SESSION_PATH = ".ai/vcm/harness-engineer/session.json";
|
|
@@ -1410,7 +1411,7 @@ function renderHarnessStatus(harnessRevision, analyses, legacyChanges = [], mani
|
|
|
1410
1411
|
...(plannedChanges.length > 0 ? ["Review and commit VCM Harness changes before starting long-running work."] : []),
|
|
1411
1412
|
...((codeIntelligence?.languages ?? [])
|
|
1412
1413
|
.filter((language) => language.state !== "server_runnable")
|
|
1413
|
-
.map(
|
|
1414
|
+
.map(lspUnavailableWarning))
|
|
1414
1415
|
]
|
|
1415
1416
|
};
|
|
1416
1417
|
}
|
|
@@ -17,3 +17,29 @@ export async function prepareTaskLspPlugin(fs, taskRepoRoot, status) {
|
|
|
17
17
|
await fs.writeJsonAtomic(pluginManifestPath, { ...manifest, lspServers });
|
|
18
18
|
return [pluginDir];
|
|
19
19
|
}
|
|
20
|
+
export function lspUnavailableWarning(language) {
|
|
21
|
+
const installCommands = {
|
|
22
|
+
"rust-analyzer": "rustup component add rust-analyzer",
|
|
23
|
+
"typescript-language-server": "npm install -g typescript typescript-language-server",
|
|
24
|
+
"pyright-langserver": "npm install -g pyright",
|
|
25
|
+
"gopls": "go install golang.org/x/tools/gopls@latest"
|
|
26
|
+
};
|
|
27
|
+
const recovery = language.state === "plugin_missing"
|
|
28
|
+
? "Refresh the VCM Harness to restore the LSP plugin."
|
|
29
|
+
: `Install or repair ${language.serverCommand} in the environment running the VCM backend${installCommands[language.serverCommand] ? ` (${installCommands[language.serverCommand]})` : ""} and ensure it is executable on PATH.`;
|
|
30
|
+
return `Architect LSP for ${language.label} is unavailable: ${language.error ?? `${language.serverCommand} is not runnable.`} ${recovery} Restart Architect after fixing the environment so VCM can detect and register the server again.`;
|
|
31
|
+
}
|
|
32
|
+
export function renderArchitectLspNotice(status) {
|
|
33
|
+
if (status.languages.length === 0)
|
|
34
|
+
return undefined;
|
|
35
|
+
const available = status.languages.filter((language) => language.state === "server_runnable");
|
|
36
|
+
const unavailable = status.languages.filter((language) => language.state !== "server_runnable");
|
|
37
|
+
return [
|
|
38
|
+
"[VCM Architect LSP Availability]",
|
|
39
|
+
`Registered language servers: ${available.map((language) => `${language.label} (${language.serverCommand})`).join(", ") || "none"}.`,
|
|
40
|
+
...unavailable.map(lspUnavailableWarning),
|
|
41
|
+
...(unavailable.length > 0 ? [
|
|
42
|
+
"If your assignment requires a semantic query in an unavailable language, stop that work and report the environment prerequisite through the current communication channel. Do not substitute source text search or claim the relationship is verified. Continue only after the server is available."
|
|
43
|
+
] : [])
|
|
44
|
+
].join("\n");
|
|
45
|
+
}
|
|
@@ -11,7 +11,7 @@ const DEFAULT_PRE_DISPATCH_SWITCH_DELAY_MS = 500;
|
|
|
11
11
|
const DEFAULT_AUTO_DISPATCH_ENTER_DELAY_MS = 500;
|
|
12
12
|
const DEFAULT_DISPATCH_CONFIRMATION_RETRY_DELAYS_MS = [1500, 3000];
|
|
13
13
|
const DEFAULT_DISPATCH_CONFIRMATION_FAILURE_DELAY_MS = 3000;
|
|
14
|
-
const DISPATCH_NOT_CONFIRMED_REASON = "Auto orchestration pasted the message, but Claude Code did not confirm submission.
|
|
14
|
+
const DISPATCH_NOT_CONFIRMED_REASON = "Auto orchestration pasted the message, but Claude Code did not confirm submission. Inspect the target terminal: press Enter if the prompt was not submitted, or use Confirm Executed if the role already executed it.";
|
|
15
15
|
const INVALID_ROUTE_REASON = "Invalid route: non-PM roles must route through project-manager.";
|
|
16
16
|
export function createMessageService(deps) {
|
|
17
17
|
const now = deps.now ?? (() => new Date().toISOString());
|
|
@@ -208,6 +208,22 @@ export function createMessageService(deps) {
|
|
|
208
208
|
return accepted;
|
|
209
209
|
});
|
|
210
210
|
},
|
|
211
|
+
async confirmDeliveredDispatch(input, messageId) {
|
|
212
|
+
return withTaskLock(taskLocks, getMessagesPath(getStateRepoRoot(input), input.stateRoot, input.taskSlug), async () => {
|
|
213
|
+
const messages = await readLatestMessages(deps.fs, getMessagesPath(getStateRepoRoot(input), input.stateRoot, input.taskSlug));
|
|
214
|
+
const message = messages.find((entry) => entry.id === messageId);
|
|
215
|
+
if (!message || message.fromRole !== PM_ROLE || !message.deliveredAt || !deps.workflowControlService) {
|
|
216
|
+
throw new VcmError({ code: "MESSAGE_NOT_DELIVERED", message: `Message ${messageId} is not a delivered PM workflow dispatch.`, statusCode: 409 });
|
|
217
|
+
}
|
|
218
|
+
if (message.acceptedAt)
|
|
219
|
+
return message;
|
|
220
|
+
await deps.workflowControlService.confirmDispatch(toWorkflowContext(input), messageId, "manual");
|
|
221
|
+
const accepted = { ...message, acceptedAt: now(), confirmationSource: "manual", failureReason: undefined };
|
|
222
|
+
await appendMessageSnapshot(deps.fs, input, accepted);
|
|
223
|
+
await clearRouteFileIfStillMatchesMessage(deps.fs, input, accepted);
|
|
224
|
+
return accepted;
|
|
225
|
+
});
|
|
226
|
+
},
|
|
211
227
|
async markAllDone(input) {
|
|
212
228
|
return withTaskLock(taskLocks, getMessagesPath(getStateRepoRoot(input), input.stateRoot, input.taskSlug), async () => {
|
|
213
229
|
const messages = await readLatestMessages(deps.fs, getMessagesPath(getStateRepoRoot(input), input.stateRoot, input.taskSlug));
|
|
@@ -8,7 +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, prepareTaskLspPlugin, VCM_LSP_PLUGIN_DIR } from "./lsp-plugin.js";
|
|
11
|
+
import { roleUsesLsp, prepareTaskLspPlugin, renderArchitectLspNotice, VCM_LSP_PLUGIN_DIR } from "./lsp-plugin.js";
|
|
12
12
|
const TRANSLATOR_ROLE = "translator";
|
|
13
13
|
const HARNESS_ENGINEER_ROLE = "harness-engineer";
|
|
14
14
|
const TRANSLATION_DIR = ".ai/vcm/translations";
|
|
@@ -82,13 +82,16 @@ export function createSessionService(deps) {
|
|
|
82
82
|
: resumeClaudeSessionId
|
|
83
83
|
? claudeTranscriptPath(taskRepoRoot, resumeClaudeSessionId, persisted?.claudeConfigDir)
|
|
84
84
|
: undefined;
|
|
85
|
+
const lspStatus = roleUsesLsp(role) && deps.codeIntelligenceDetector
|
|
86
|
+
? await deps.codeIntelligenceDetector.detect(taskRepoRoot, { refresh: true })
|
|
87
|
+
: undefined;
|
|
85
88
|
const lspPluginDirs = roleUsesLsp(role)
|
|
86
|
-
? deps.
|
|
87
|
-
? await prepareTaskLspPlugin(deps.fs, taskRepoRoot, await deps.codeIntelligenceDetector.detect(taskRepoRoot))
|
|
88
|
-
: [VCM_LSP_PLUGIN_DIR]
|
|
89
|
+
? lspStatus ? await prepareTaskLspPlugin(deps.fs, taskRepoRoot, lspStatus) : [VCM_LSP_PLUGIN_DIR]
|
|
89
90
|
: [];
|
|
91
|
+
const appendSystemPrompt = [input.appendSystemPrompt, lspStatus && renderArchitectLspNotice(lspStatus)]
|
|
92
|
+
.filter(Boolean).join("\n\n") || undefined;
|
|
90
93
|
const startCommand = {
|
|
91
|
-
...deps.claude.buildRoleStartCommand(role, config.claudeCommand, permissionMode, resumeClaudeSessionId, launchMode === "resume", model, effort, modelSettingsOverride,
|
|
94
|
+
...deps.claude.buildRoleStartCommand(role, config.claudeCommand, permissionMode, resumeClaudeSessionId, launchMode === "resume", model, effort, modelSettingsOverride, appendSystemPrompt, lspPluginDirs),
|
|
92
95
|
cwd: taskRepoRoot
|
|
93
96
|
};
|
|
94
97
|
const runtimeSessionToken = randomUUID();
|
|
@@ -189,6 +189,9 @@ export function createWorkflowControlService(deps) {
|
|
|
189
189
|
await assertRouteAuthorized(input);
|
|
190
190
|
const state = await getState(input);
|
|
191
191
|
const pending = state.pendingDispatch;
|
|
192
|
+
const timestamp = now();
|
|
193
|
+
const progress = await readProgress(deps.fs, input);
|
|
194
|
+
const evidenceBaseline = await captureEvidenceBaseline(deps.fs, input, progress.history.length + 1, pending.effectiveFlow, pending.targetRole, timestamp);
|
|
192
195
|
await saveState(input, {
|
|
193
196
|
...state,
|
|
194
197
|
pendingDispatch: {
|
|
@@ -196,7 +199,8 @@ export function createWorkflowControlService(deps) {
|
|
|
196
199
|
status: "dispatching",
|
|
197
200
|
routeContentHash: input.routeContentHash,
|
|
198
201
|
messageId: input.messageId,
|
|
199
|
-
|
|
202
|
+
evidenceBaseline,
|
|
203
|
+
updatedAt: timestamp
|
|
200
204
|
},
|
|
201
205
|
updatedAt: now()
|
|
202
206
|
});
|
|
@@ -216,6 +220,7 @@ export function createWorkflowControlService(deps) {
|
|
|
216
220
|
routeContentHash: undefined,
|
|
217
221
|
messageId: undefined,
|
|
218
222
|
confirmationError: undefined,
|
|
223
|
+
evidenceBaseline: undefined,
|
|
219
224
|
updatedAt: now()
|
|
220
225
|
},
|
|
221
226
|
updatedAt: now()
|
|
@@ -257,7 +262,7 @@ export function createWorkflowControlService(deps) {
|
|
|
257
262
|
});
|
|
258
263
|
});
|
|
259
264
|
}
|
|
260
|
-
async function confirmDispatch(input, messageId) {
|
|
265
|
+
async function confirmDispatch(input, messageId, source = "hook") {
|
|
261
266
|
await withLock(statePath(input), async () => {
|
|
262
267
|
const state = await getState(input);
|
|
263
268
|
failOnStateWarnings({
|
|
@@ -269,6 +274,9 @@ export function createWorkflowControlService(deps) {
|
|
|
269
274
|
if (!pending || pending.status !== "dispatching" || pending.messageId !== messageId) {
|
|
270
275
|
throw workflowError("WORKFLOW_DISPATCH_CONFIRMATION_MISMATCH", `Message ${messageId} does not own the pending workflow dispatch.`, "Do not advance Workflow Progress from an unrelated UserPromptSubmit event.");
|
|
271
276
|
}
|
|
277
|
+
if (source === "manual" && !pending.confirmationError) {
|
|
278
|
+
throw workflowError("WORKFLOW_DISPATCH_NOT_UNCONFIRMED", "Manual confirmation is only available for a delivered dispatch whose confirmation failed.");
|
|
279
|
+
}
|
|
272
280
|
const current = await readProgress(deps.fs, input);
|
|
273
281
|
if (current.revision !== pending.revision || historyHash(current.history) !== pending.baseHistoryHash) {
|
|
274
282
|
throw workflowError("WORKFLOW_APPROVAL_STALE", "Workflow Progress changed before the approved dispatch was confirmed.", "Repair workflow-progress.md before continuing.");
|
|
@@ -278,12 +286,14 @@ export function createWorkflowControlService(deps) {
|
|
|
278
286
|
sequence: current.history.length + 1,
|
|
279
287
|
flow: pending.effectiveFlow,
|
|
280
288
|
targetRole: pending.targetRole,
|
|
281
|
-
evidence:
|
|
289
|
+
evidence: source === "manual"
|
|
290
|
+
? `${pending.evidence} [User manually confirmed execution of message ${messageId}.]`
|
|
291
|
+
: pending.evidence,
|
|
282
292
|
overrideAuthorizationId: pending.overrideAuthorizationId,
|
|
283
293
|
followUpApprovalId: pending.followUpApprovalId,
|
|
284
294
|
confirmedAt: timestamp
|
|
285
295
|
};
|
|
286
|
-
const activeDispatch = await captureEvidenceBaseline(deps.fs, input, entry.sequence, entry.flow, entry.targetRole, timestamp);
|
|
296
|
+
const activeDispatch = pending.evidenceBaseline ?? await captureEvidenceBaseline(deps.fs, input, entry.sequence, entry.flow, entry.targetRole, timestamp);
|
|
287
297
|
const flowRun = await advanceFlowRun(deps.fs, input, state, current, pending.effectiveFlow, entry.sequence);
|
|
288
298
|
const completed = {
|
|
289
299
|
...current,
|
|
@@ -339,6 +349,7 @@ export function createWorkflowControlService(deps) {
|
|
|
339
349
|
routeContentHash: undefined,
|
|
340
350
|
messageId: undefined,
|
|
341
351
|
confirmationError: undefined,
|
|
352
|
+
evidenceBaseline: undefined,
|
|
342
353
|
updatedAt: now()
|
|
343
354
|
},
|
|
344
355
|
updatedAt: now()
|
|
@@ -1292,7 +1303,7 @@ function stateProgressConsistencyWarnings(state, progress) {
|
|
|
1292
1303
|
}
|
|
1293
1304
|
function unconfirmedDispatchWarning(pending) {
|
|
1294
1305
|
return pending?.confirmationError
|
|
1295
|
-
? `PM dispatch ${pending.messageId ?? "unknown"} to ${pending.targetRole} was delivered but not confirmed: ${pending.confirmationError} Inspect the target session
|
|
1306
|
+
? `PM dispatch ${pending.messageId ?? "unknown"} to ${pending.targetRole} was delivered but not confirmed: ${pending.confirmationError} Inspect the target session and use Confirm Executed if it already ran; do not resend executed work.`
|
|
1296
1307
|
: undefined;
|
|
1297
1308
|
}
|
|
1298
1309
|
function abandonPendingApprovals(state, pending, reason, timestamp) {
|
|
@@ -1569,6 +1580,7 @@ function isPendingDispatch(value) {
|
|
|
1569
1580
|
&& (value.routeContentHash === undefined || typeof value.routeContentHash === "string")
|
|
1570
1581
|
&& (value.messageId === undefined || typeof value.messageId === "string")
|
|
1571
1582
|
&& (value.confirmationError === undefined || typeof value.confirmationError === "string")
|
|
1583
|
+
&& (value.evidenceBaseline === undefined || Boolean(normalizeDispatchEvidenceBaseline(value.evidenceBaseline)))
|
|
1572
1584
|
&& typeof value.createdAt === "string"
|
|
1573
1585
|
&& typeof value.updatedAt === "string";
|
|
1574
1586
|
}
|
|
@@ -22,7 +22,7 @@ ${renderRoleMemoryRules("architect")}
|
|
|
22
22
|
### Semantic Code Navigation
|
|
23
23
|
|
|
24
24
|
- Follow the preloaded \`vcm-code-navigation\` skill whenever work requires code definitions, implementations, references, callers, callees, or behavior paths. This applies in every Architect mode and in direct user communication.
|
|
25
|
-
- LSP is mandatory
|
|
25
|
+
- LSP is mandatory for navigating project source code. Start source-code navigation with a registered LSP server. If a required language server is unavailable, stop the affected work and report its language, command, error, and installation hint through the current communication channel. Resume after the environment is fixed and Architect is restarted; do not substitute source text search.
|
|
26
26
|
- Use LSP definitions, implementations, references, document or workspace symbols, and incoming or outgoing calls for all source-code symbol and relationship queries.
|
|
27
27
|
- LSP reference results are symbol-specific. An accessor, its backing field, a trait declaration, its implementation method, a wrapper, and an alias are separate symbols. Query every relevant symbol separately; one symbol's reference result cannot prove a complete semantic class. When starting from an accessor or wrapper, read its implementation, resolve its backing field, delegate, or trait item with LSP, then query those symbols too.
|
|
28
28
|
- Do not use the built-in \`Grep\` tool or shell text-search commands such as \`grep\`, \`rg\`, \`git grep\`, \`ag\`, or \`ack\` to search project source code, test code, or executable scripts, except for the bounded fallback below. This prohibition includes locating or inferring definitions, implementations, references, callers, callees, symbols, and behavior paths.
|
|
@@ -6,7 +6,7 @@ Use this skill when Architect must establish symbol definitions, implementations
|
|
|
6
6
|
## Navigation Order
|
|
7
7
|
|
|
8
8
|
1. Define the affected feature or module boundary and locate entry symbols with \`.ai/generated/module-index.json\` and \`.ai/generated/public-surface.json\` when available.
|
|
9
|
-
2. Use LSP workspace or file symbols, definitions, implementations, references, and incoming or outgoing call hierarchy to resolve project-owned relationships. If
|
|
9
|
+
2. Use LSP workspace or file symbols, definitions, implementations, references, and incoming or outgoing call hierarchy to resolve project-owned relationships. If a required language server is unavailable, stop the affected work and report its language, command, error, and installation hint through the current communication channel. Resume after the environment is fixed and Architect is restarted; do not substitute text search.
|
|
10
10
|
3. Treat LSP results as symbol-specific. An accessor, backing field, trait declaration, implementation method, wrapper, and alias are separate symbols. Query every relevant symbol separately. When starting from an accessor or wrapper, read its implementation, resolve the backing field, delegate, or trait item with LSP, then query those symbols too. Never use one symbol's references as proof of a complete semantic class.
|
|
11
11
|
4. If an initial workspace-symbol request is empty or reports indexing, wait and retry; startup or indexing time does not permit replacing a required semantic query with text matching. Retry the same bounded workspace query at most two more times. If it still cannot resolve, record the operation and result as unresolved.
|
|
12
12
|
5. Read the complete project-owned callable unit at every resolved location.
|