vibe-coding-master 0.7.40 → 0.7.42
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/adapters/claude-adapter.js +8 -1
- package/dist/backend/cli/install-vcm-harness.js +66 -20
- package/dist/backend/role-tool-policy.js +59 -0
- package/dist/backend/services/harness-service.js +63 -24
- package/dist/backend/services/lsp-plugin.js +1 -4
- package/dist/backend/services/session-service.js +6 -1
- package/dist/backend/templates/harness/architect-agent.js +5 -1
- package/dist/backend/templates/harness/vcm-code-navigation-skill.js +2 -2
- package/package.json +1 -1
- package/scripts/claude-plugins/vcm-lsp-bridge/.claude-plugin/plugin.json +2 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { roleRuntimeDisallowedTools, roleUsesLsp } from "../role-tool-policy.js";
|
|
1
2
|
import { isCcrSessionModel } from "../../shared/types/session.js";
|
|
2
3
|
import { VcmError } from "../errors.js";
|
|
3
4
|
export function createClaudeAdapter(runner) {
|
|
@@ -20,7 +21,13 @@ export function createClaudeAdapter(runner) {
|
|
|
20
21
|
},
|
|
21
22
|
buildRoleStartCommand(role, command = "claude", permissionMode = "default", claudeSessionId, resume = false, model = "default", effort = "default", settingsOverride, appendSystemPrompt, pluginDirs = []) {
|
|
22
23
|
const args = pluginDirs.flatMap((pluginDir) => ["--plugin-dir", pluginDir]);
|
|
23
|
-
|
|
24
|
+
const lspEnabled = roleUsesLsp(role) && pluginDirs.length > 0;
|
|
25
|
+
if (lspEnabled) {
|
|
26
|
+
args.push("--disallowedTools", roleRuntimeDisallowedTools(role).join(","));
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
args.push("--allowedTools", "Glob,Grep");
|
|
30
|
+
}
|
|
24
31
|
args.push("--agent", role);
|
|
25
32
|
const sessionSettings = { ...settingsOverride };
|
|
26
33
|
if (claudeSessionId) {
|
|
@@ -5,6 +5,7 @@ import path from "node:path";
|
|
|
5
5
|
import process from "node:process";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
import { renderArchitectHarnessRules } from "../templates/harness/architect-agent.js";
|
|
8
|
+
import { CODE_ROLE_DISALLOWED_TOOLS, REVIEWER_DISALLOWED_TOOLS } from "../role-tool-policy.js";
|
|
8
9
|
import { renderCoderHarnessRules } from "../templates/harness/coder-agent.js";
|
|
9
10
|
import { renderCoderWorkerHarnessRules } from "../templates/harness/coder-worker-agent.js";
|
|
10
11
|
import { renderArchitectScaffoldWorkerHarnessRules } from "../templates/harness/architect-scaffold-worker-agent.js";
|
|
@@ -63,31 +64,34 @@ const VCM_HOOK_DEFINITIONS = [
|
|
|
63
64
|
];
|
|
64
65
|
const AGENT_FRONTMATTER = {
|
|
65
66
|
"project-manager": {
|
|
66
|
-
description: "User-facing VCM orchestration role for task clarification, role routing, handoffs, acceptance, and PR preparation."
|
|
67
|
+
description: "User-facing VCM orchestration role for task clarification, role routing, handoffs, acceptance, and PR preparation.",
|
|
68
|
+
tools: "Read, Grep, Glob, Bash, Edit, Write, Skill"
|
|
67
69
|
},
|
|
68
70
|
architect: {
|
|
69
71
|
description: "VCM architecture role for plans, module boundaries, public contracts, verifiable behavior, and docs sync.",
|
|
70
|
-
|
|
72
|
+
disallowedTools: CODE_ROLE_DISALLOWED_TOOLS.join(", "),
|
|
71
73
|
skills: ["vcm-code-navigation"]
|
|
72
74
|
},
|
|
73
75
|
coder: {
|
|
74
76
|
description: "VCM implementation role for scoped code changes and focused tests.",
|
|
75
|
-
|
|
77
|
+
disallowedTools: CODE_ROLE_DISALLOWED_TOOLS.join(", "),
|
|
76
78
|
skills: ["vcm-code-navigation"]
|
|
77
79
|
},
|
|
78
80
|
tester: {
|
|
79
|
-
description: "VCM testing role for validation, test adequacy, approved-scope validation, and risk findings."
|
|
81
|
+
description: "VCM testing role for validation, test adequacy, approved-scope validation, and risk findings.",
|
|
82
|
+
tools: "Read, Grep, Glob, Bash, Edit, Write, Skill"
|
|
80
83
|
},
|
|
81
84
|
reviewer: {
|
|
82
85
|
description: "VCM independent gate review role for architecture plans, validation adequacy, and code diffs.",
|
|
83
|
-
|
|
86
|
+
disallowedTools: REVIEWER_DISALLOWED_TOOLS.join(", "),
|
|
84
87
|
skills: ["vcm-code-navigation"]
|
|
85
88
|
},
|
|
86
89
|
translator: {
|
|
87
90
|
description: "VCM task-scoped translation tool role for conversation translation, file translation, bootstrap, and memory updates."
|
|
88
91
|
},
|
|
89
92
|
"harness-engineer": {
|
|
90
|
-
description: "VCM task-scoped harness maintenance role for harness diagnosis, diff proposals, and VCM issue drafts."
|
|
93
|
+
description: "VCM task-scoped harness maintenance role for harness diagnosis, diff proposals, and VCM issue drafts.",
|
|
94
|
+
tools: "Read, Grep, Glob, Bash, Edit, Write, Skill"
|
|
91
95
|
},
|
|
92
96
|
"vcm-coder-worker": {
|
|
93
97
|
description: "Bounded VCM implementation worker for assigned modules, files, and VCM:CODE markers from Coder.",
|
|
@@ -99,10 +103,15 @@ const AGENT_FRONTMATTER = {
|
|
|
99
103
|
effort: "xhigh"
|
|
100
104
|
}
|
|
101
105
|
};
|
|
106
|
+
const REQUIRED_AGENT_DISALLOWED_TOOLS = {
|
|
107
|
+
architect: CODE_ROLE_DISALLOWED_TOOLS,
|
|
108
|
+
coder: CODE_ROLE_DISALLOWED_TOOLS,
|
|
109
|
+
reviewer: REVIEWER_DISALLOWED_TOOLS
|
|
110
|
+
};
|
|
102
111
|
const REQUIRED_AGENT_TOOLS = {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
112
|
+
"project-manager": ["Skill"],
|
|
113
|
+
tester: ["Skill"],
|
|
114
|
+
"harness-engineer": ["Skill"]
|
|
106
115
|
};
|
|
107
116
|
const REQUIRED_AGENT_SKILLS = {
|
|
108
117
|
architect: ["vcm-code-navigation"],
|
|
@@ -693,7 +702,11 @@ async function installManagedFile({ projectRoot, definition, dryRun, operations
|
|
|
693
702
|
nextContent = ensureVcmMemoryBlock(nextContent);
|
|
694
703
|
}
|
|
695
704
|
for (const requiredTool of REQUIRED_AGENT_TOOLS[definition.agentName] ?? []) {
|
|
696
|
-
nextContent =
|
|
705
|
+
nextContent = ensureAgentAllowedTool(nextContent, requiredTool);
|
|
706
|
+
}
|
|
707
|
+
const requiredDisallowedTools = REQUIRED_AGENT_DISALLOWED_TOOLS[definition.agentName];
|
|
708
|
+
if (requiredDisallowedTools) {
|
|
709
|
+
nextContent = ensureAgentDisallowedTools(nextContent, requiredDisallowedTools);
|
|
697
710
|
}
|
|
698
711
|
for (const requiredSkill of REQUIRED_AGENT_SKILLS[definition.agentName] ?? []) {
|
|
699
712
|
nextContent = ensureAgentSkill(nextContent, requiredSkill);
|
|
@@ -737,31 +750,64 @@ function renderNewManagedFile(definition, block) {
|
|
|
737
750
|
const suffix = definition.contentAfterBlock?.trim();
|
|
738
751
|
if (definition.agentName) {
|
|
739
752
|
const frontmatter = AGENT_FRONTMATTER[definition.agentName];
|
|
740
|
-
const
|
|
753
|
+
const toolPolicy = frontmatter.disallowedTools
|
|
754
|
+
? `disallowedTools: ${frontmatter.disallowedTools}`
|
|
755
|
+
: `tools: ${frontmatter.tools ?? "Read, Grep, Glob, Bash, Edit, Write"}`;
|
|
741
756
|
const model = frontmatter.model ? `\nmodel: ${frontmatter.model}` : "";
|
|
742
757
|
const effort = frontmatter.effort ? `\neffort: ${frontmatter.effort}` : "";
|
|
743
758
|
const skills = frontmatter.skills?.length
|
|
744
759
|
? `\nskills:\n${frontmatter.skills.map((skill) => ` - ${skill}`).join("\n")}`
|
|
745
760
|
: "";
|
|
746
|
-
return `---\nname: ${definition.agentName}\ndescription: ${frontmatter.description}\
|
|
761
|
+
return `---\nname: ${definition.agentName}\ndescription: ${frontmatter.description}\n${toolPolicy}${model}${effort}${skills}\n---\n\n# ${definition.title}\n\n${block}${suffix ? `\n\n${suffix}` : ""}\n`;
|
|
747
762
|
}
|
|
748
763
|
return `# ${definition.title}\n\n${block}${suffix ? `\n\n${suffix}` : ""}\n`;
|
|
749
764
|
}
|
|
750
|
-
function
|
|
765
|
+
function ensureAgentDisallowedTools(content, requiredTools) {
|
|
751
766
|
const frontmatterMatch = content.match(/^---\r?\n[\s\S]*?\r?\n---/);
|
|
752
767
|
if (!frontmatterMatch) {
|
|
753
768
|
return content;
|
|
754
769
|
}
|
|
755
|
-
const
|
|
756
|
-
|
|
757
|
-
|
|
770
|
+
const withoutAllowedTools = frontmatterMatch[0].replace(/^tools:\s*.*\r?\n?/m, "");
|
|
771
|
+
const disallowedMatch = withoutAllowedTools.match(/^disallowedTools:\s*(.*)$/m);
|
|
772
|
+
const existing = disallowedMatch?.[1]
|
|
773
|
+
.split(",")
|
|
774
|
+
.map((tool) => tool.trim())
|
|
775
|
+
.filter(Boolean) ?? [];
|
|
776
|
+
const disallowedTools = [...new Set([...existing, ...requiredTools])].join(", ");
|
|
777
|
+
const nextFrontmatter = disallowedMatch
|
|
778
|
+
? withoutAllowedTools.replace(disallowedMatch[0], `disallowedTools: ${disallowedTools}`)
|
|
779
|
+
: withoutAllowedTools.replace(/\r?\n---$/, `\ndisallowedTools: ${disallowedTools}\n---`);
|
|
780
|
+
return content.replace(frontmatterMatch[0], nextFrontmatter);
|
|
781
|
+
}
|
|
782
|
+
function ensureAgentAllowedTool(content, requiredTool) {
|
|
783
|
+
const frontmatterMatch = content.match(/^---\r?\n[\s\S]*?\r?\n---/);
|
|
784
|
+
if (!frontmatterMatch) {
|
|
785
|
+
return content;
|
|
786
|
+
}
|
|
787
|
+
const frontmatter = frontmatterMatch[0];
|
|
788
|
+
const toolsMatch = frontmatter.match(/^tools:\s*(.*)$/m);
|
|
789
|
+
if (toolsMatch) {
|
|
790
|
+
const existing = toolsMatch[1]
|
|
791
|
+
.split(",")
|
|
792
|
+
.map((tool) => tool.trim())
|
|
793
|
+
.filter(Boolean);
|
|
794
|
+
if (existing.includes(requiredTool)) {
|
|
795
|
+
return content;
|
|
796
|
+
}
|
|
797
|
+
return content.replace(frontmatter, frontmatter.replace(toolsMatch[0], `tools: ${[...existing, requiredTool].join(", ")}`));
|
|
758
798
|
}
|
|
759
|
-
const
|
|
760
|
-
if (
|
|
799
|
+
const disallowedMatch = frontmatter.match(/^disallowedTools:\s*(.*)$/m);
|
|
800
|
+
if (!disallowedMatch) {
|
|
761
801
|
return content;
|
|
762
802
|
}
|
|
763
|
-
const
|
|
764
|
-
|
|
803
|
+
const disallowedTools = disallowedMatch[1]
|
|
804
|
+
.split(",")
|
|
805
|
+
.map((tool) => tool.trim())
|
|
806
|
+
.filter((tool) => tool && tool !== requiredTool);
|
|
807
|
+
const nextFrontmatter = disallowedTools.length > 0
|
|
808
|
+
? frontmatter.replace(disallowedMatch[0], `disallowedTools: ${disallowedTools.join(", ")}`)
|
|
809
|
+
: frontmatter.replace(/^disallowedTools:\s*.*\r?\n?/m, "");
|
|
810
|
+
return content.replace(frontmatter, nextFrontmatter);
|
|
765
811
|
}
|
|
766
812
|
function ensureAgentSkill(content, requiredSkill) {
|
|
767
813
|
const frontmatterMatch = content.match(/^---\r?\n[\s\S]*?\r?\n---/);
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
const LSP_FRONTMATTER_DISALLOWED_TOOLS = [
|
|
2
|
+
"NotebookEdit",
|
|
3
|
+
"WebFetch",
|
|
4
|
+
"WebSearch"
|
|
5
|
+
];
|
|
6
|
+
const NON_VCM_ROLE_RUNTIME_TOOLS = [
|
|
7
|
+
"AskUserQuestion",
|
|
8
|
+
"CronCreate",
|
|
9
|
+
"CronDelete",
|
|
10
|
+
"CronList",
|
|
11
|
+
"DesignSync",
|
|
12
|
+
"EnterPlanMode",
|
|
13
|
+
"EnterWorktree",
|
|
14
|
+
"ExitPlanMode",
|
|
15
|
+
"ExitWorktree",
|
|
16
|
+
"KillShell",
|
|
17
|
+
"Monitor",
|
|
18
|
+
"NotebookEdit",
|
|
19
|
+
"PushNotification",
|
|
20
|
+
"RemoteTrigger",
|
|
21
|
+
"ReportFindings",
|
|
22
|
+
"ScheduleWakeup",
|
|
23
|
+
"SendMessage",
|
|
24
|
+
"TaskCreate",
|
|
25
|
+
"TaskGet",
|
|
26
|
+
"TaskList",
|
|
27
|
+
"TaskOutput",
|
|
28
|
+
"TaskStop",
|
|
29
|
+
"TaskUpdate",
|
|
30
|
+
"TodoWrite",
|
|
31
|
+
"WebFetch",
|
|
32
|
+
"WebSearch",
|
|
33
|
+
"Workflow"
|
|
34
|
+
];
|
|
35
|
+
export const CODE_ROLE_DISALLOWED_TOOLS = [...LSP_FRONTMATTER_DISALLOWED_TOOLS];
|
|
36
|
+
export const REVIEWER_DISALLOWED_TOOLS = [
|
|
37
|
+
"Agent",
|
|
38
|
+
"Edit",
|
|
39
|
+
...LSP_FRONTMATTER_DISALLOWED_TOOLS
|
|
40
|
+
];
|
|
41
|
+
export const CODE_ROLE_RUNTIME_DISALLOWED_TOOLS = [...NON_VCM_ROLE_RUNTIME_TOOLS];
|
|
42
|
+
export const REVIEWER_RUNTIME_DISALLOWED_TOOLS = [
|
|
43
|
+
"Agent",
|
|
44
|
+
"Edit",
|
|
45
|
+
...NON_VCM_ROLE_RUNTIME_TOOLS
|
|
46
|
+
];
|
|
47
|
+
const LSP_ROLE_NAMES = new Set(["architect", "coder", "reviewer"]);
|
|
48
|
+
export function roleUsesLsp(role) {
|
|
49
|
+
return LSP_ROLE_NAMES.has(role);
|
|
50
|
+
}
|
|
51
|
+
export function roleRuntimeDisallowedTools(role) {
|
|
52
|
+
if (role === "reviewer") {
|
|
53
|
+
return REVIEWER_RUNTIME_DISALLOWED_TOOLS;
|
|
54
|
+
}
|
|
55
|
+
if (role === "architect" || role === "coder") {
|
|
56
|
+
return CODE_ROLE_RUNTIME_DISALLOWED_TOOLS;
|
|
57
|
+
}
|
|
58
|
+
return [];
|
|
59
|
+
}
|
|
@@ -2,6 +2,7 @@ import { execFile } from "node:child_process";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { promisify } from "node:util";
|
|
4
4
|
import { renderArchitectHarnessRules } from "../templates/harness/architect-agent.js";
|
|
5
|
+
import { CODE_ROLE_DISALLOWED_TOOLS, REVIEWER_DISALLOWED_TOOLS } from "../role-tool-policy.js";
|
|
5
6
|
import { renderCoderHarnessRules } from "../templates/harness/coder-agent.js";
|
|
6
7
|
import { renderCoderWorkerHarnessRules } from "../templates/harness/coder-worker-agent.js";
|
|
7
8
|
import { renderArchitectScaffoldWorkerHarnessRules } from "../templates/harness/architect-scaffold-worker-agent.js";
|
|
@@ -207,9 +208,9 @@ const HARNESS_FILES = [
|
|
|
207
208
|
path: ".claude/agents/reviewer.md",
|
|
208
209
|
title: "Reviewer Agent",
|
|
209
210
|
memoryBlock: true,
|
|
210
|
-
|
|
211
|
+
requiredDisallowedTools: REVIEWER_DISALLOWED_TOOLS,
|
|
211
212
|
requiredSkills: ["vcm-code-navigation"],
|
|
212
|
-
frontmatter: renderAgentFrontmatter("reviewer", "VCM independent gate review role for architecture plans, validation adequacy, and code diffs.", {
|
|
213
|
+
frontmatter: renderAgentFrontmatter("reviewer", "VCM independent gate review role for architecture plans, validation adequacy, and code diffs.", { disallowedTools: REVIEWER_DISALLOWED_TOOLS.join(", "), skills: ["vcm-code-navigation"] }),
|
|
213
214
|
renderRules: renderReviewerAgentRules
|
|
214
215
|
},
|
|
215
216
|
{
|
|
@@ -224,7 +225,8 @@ const HARNESS_FILES = [
|
|
|
224
225
|
path: ".claude/agents/harness-engineer.md",
|
|
225
226
|
title: "Harness Engineer Agent",
|
|
226
227
|
memoryBlock: true,
|
|
227
|
-
|
|
228
|
+
requiredTools: ["Skill"],
|
|
229
|
+
frontmatter: renderAgentFrontmatter("harness-engineer", "VCM task-scoped harness maintenance role for harness diagnosis, diff proposals, and VCM issue drafts.", { tools: "Read, Grep, Glob, Bash, Edit, Write, Skill" }),
|
|
228
230
|
renderRules: renderHarnessEngineerHarnessRules
|
|
229
231
|
},
|
|
230
232
|
{
|
|
@@ -274,7 +276,8 @@ const HARNESS_FILES = [
|
|
|
274
276
|
path: ".claude/agents/project-manager.md",
|
|
275
277
|
title: "Project Manager Agent",
|
|
276
278
|
memoryBlock: true,
|
|
277
|
-
|
|
279
|
+
requiredTools: ["Skill"],
|
|
280
|
+
frontmatter: renderAgentFrontmatter("project-manager", "User-facing VCM orchestration role for task clarification, role routing, handoffs, acceptance, and PR preparation.", { tools: "Read, Grep, Glob, Bash, Edit, Write, Skill" }),
|
|
278
281
|
renderRules: renderProjectManagerHarnessRules
|
|
279
282
|
},
|
|
280
283
|
{
|
|
@@ -282,10 +285,10 @@ const HARNESS_FILES = [
|
|
|
282
285
|
path: ".claude/agents/architect.md",
|
|
283
286
|
title: "Architect Agent",
|
|
284
287
|
memoryBlock: true,
|
|
285
|
-
|
|
288
|
+
requiredDisallowedTools: CODE_ROLE_DISALLOWED_TOOLS,
|
|
286
289
|
requiredSkills: ["vcm-code-navigation"],
|
|
287
290
|
blankLineBeforeEnd: true,
|
|
288
|
-
frontmatter: renderAgentFrontmatter("architect", "VCM architecture role for plans, module boundaries, public contracts, verifiable behavior, and docs sync.", {
|
|
291
|
+
frontmatter: renderAgentFrontmatter("architect", "VCM architecture role for plans, module boundaries, public contracts, verifiable behavior, and docs sync.", { disallowedTools: CODE_ROLE_DISALLOWED_TOOLS.join(", "), skills: ["vcm-code-navigation"] }),
|
|
289
292
|
renderRules: renderArchitectHarnessRules
|
|
290
293
|
},
|
|
291
294
|
{
|
|
@@ -293,9 +296,9 @@ const HARNESS_FILES = [
|
|
|
293
296
|
path: ".claude/agents/coder.md",
|
|
294
297
|
title: "Coder Agent",
|
|
295
298
|
memoryBlock: true,
|
|
296
|
-
|
|
299
|
+
requiredDisallowedTools: CODE_ROLE_DISALLOWED_TOOLS,
|
|
297
300
|
requiredSkills: ["vcm-code-navigation"],
|
|
298
|
-
frontmatter: renderAgentFrontmatter("coder", "VCM implementation role for scoped code changes and focused tests.", {
|
|
301
|
+
frontmatter: renderAgentFrontmatter("coder", "VCM implementation role for scoped code changes and focused tests.", { disallowedTools: CODE_ROLE_DISALLOWED_TOOLS.join(", "), skills: ["vcm-code-navigation"] }),
|
|
299
302
|
renderRules: renderCoderHarnessRules
|
|
300
303
|
},
|
|
301
304
|
{
|
|
@@ -303,7 +306,8 @@ const HARNESS_FILES = [
|
|
|
303
306
|
path: ".claude/agents/tester.md",
|
|
304
307
|
title: "Tester Agent",
|
|
305
308
|
memoryBlock: true,
|
|
306
|
-
|
|
309
|
+
requiredTools: ["Skill"],
|
|
310
|
+
frontmatter: renderAgentFrontmatter("tester", "VCM testing role for validation, test adequacy, approved-scope validation, and risk findings.", { tools: "Read, Grep, Glob, Bash, Edit, Write, Skill" }),
|
|
307
311
|
renderRules: renderTesterHarnessRules
|
|
308
312
|
}
|
|
309
313
|
];
|
|
@@ -1386,28 +1390,61 @@ function renderNewHarnessFile(definition, block, contentAfterBlock = definition.
|
|
|
1386
1390
|
const suffix = contentAfterBlock?.trim();
|
|
1387
1391
|
return `${frontmatter}# ${definition.title}\n\n${block}${suffix ? `\n\n${suffix}` : ""}\n`;
|
|
1388
1392
|
}
|
|
1389
|
-
function
|
|
1393
|
+
function ensureAgentDisallowedTools(content, requiredTools) {
|
|
1394
|
+
if (!requiredTools) {
|
|
1395
|
+
return content;
|
|
1396
|
+
}
|
|
1390
1397
|
const frontmatterMatch = content.match(/^---\r?\n[\s\S]*?\r?\n---/);
|
|
1391
1398
|
if (!frontmatterMatch) {
|
|
1392
1399
|
return content;
|
|
1393
1400
|
}
|
|
1394
|
-
const
|
|
1395
|
-
|
|
1396
|
-
|
|
1401
|
+
const withoutAllowedTools = frontmatterMatch[0].replace(/^tools:\s*.*\r?\n?/m, "");
|
|
1402
|
+
const disallowedMatch = withoutAllowedTools.match(/^disallowedTools:\s*(.*)$/m);
|
|
1403
|
+
const existing = disallowedMatch?.[1]
|
|
1404
|
+
.split(",")
|
|
1405
|
+
.map((tool) => tool.trim())
|
|
1406
|
+
.filter(Boolean) ?? [];
|
|
1407
|
+
const disallowedTools = [...new Set([...existing, ...requiredTools])].join(", ");
|
|
1408
|
+
const nextFrontmatter = disallowedMatch
|
|
1409
|
+
? withoutAllowedTools.replace(disallowedMatch[0], `disallowedTools: ${disallowedTools}`)
|
|
1410
|
+
: withoutAllowedTools.replace(/\r?\n---$/, `\ndisallowedTools: ${disallowedTools}\n---`);
|
|
1411
|
+
return content.replace(frontmatterMatch[0], nextFrontmatter);
|
|
1412
|
+
}
|
|
1413
|
+
function ensureAgentAllowedTool(content, requiredTool) {
|
|
1414
|
+
const frontmatterMatch = content.match(/^---\r?\n[\s\S]*?\r?\n---/);
|
|
1415
|
+
if (!frontmatterMatch) {
|
|
1416
|
+
return content;
|
|
1397
1417
|
}
|
|
1398
|
-
const
|
|
1399
|
-
|
|
1418
|
+
const frontmatter = frontmatterMatch[0];
|
|
1419
|
+
const toolsMatch = frontmatter.match(/^tools:\s*(.*)$/m);
|
|
1420
|
+
if (toolsMatch) {
|
|
1421
|
+
const existing = toolsMatch[1]
|
|
1422
|
+
.split(",")
|
|
1423
|
+
.map((tool) => tool.trim())
|
|
1424
|
+
.filter(Boolean);
|
|
1425
|
+
if (existing.includes(requiredTool)) {
|
|
1426
|
+
return content;
|
|
1427
|
+
}
|
|
1428
|
+
const tools = [...existing, requiredTool].join(", ");
|
|
1429
|
+
return content.replace(frontmatter, frontmatter.replace(toolsMatch[0], `tools: ${tools}`));
|
|
1430
|
+
}
|
|
1431
|
+
const disallowedMatch = frontmatter.match(/^disallowedTools:\s*(.*)$/m);
|
|
1432
|
+
if (!disallowedMatch) {
|
|
1400
1433
|
return content;
|
|
1401
1434
|
}
|
|
1402
|
-
const
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1435
|
+
const disallowedTools = disallowedMatch[1]
|
|
1436
|
+
.split(",")
|
|
1437
|
+
.map((tool) => tool.trim())
|
|
1438
|
+
.filter((tool) => tool && tool !== requiredTool);
|
|
1439
|
+
const nextFrontmatter = disallowedTools.length > 0
|
|
1440
|
+
? frontmatter.replace(disallowedMatch[0], `disallowedTools: ${disallowedTools.join(", ")}`)
|
|
1441
|
+
: frontmatter.replace(/^disallowedTools:\s*.*\r?\n?/m, "");
|
|
1442
|
+
return content.replace(frontmatter, nextFrontmatter);
|
|
1407
1443
|
}
|
|
1408
1444
|
function normalizeAgentFrontmatter(content, definition) {
|
|
1409
|
-
const
|
|
1410
|
-
|
|
1445
|
+
const allowedToolsUpdated = (definition.requiredTools ?? []).reduce(ensureAgentAllowedTool, content);
|
|
1446
|
+
const disallowedToolsUpdated = ensureAgentDisallowedTools(allowedToolsUpdated, definition.requiredDisallowedTools);
|
|
1447
|
+
return (definition.requiredSkills ?? []).reduce(ensureAgentSkill, disallowedToolsUpdated);
|
|
1411
1448
|
}
|
|
1412
1449
|
function ensureAgentSkill(content, requiredSkill) {
|
|
1413
1450
|
const frontmatterMatch = content.match(/^---\r?\n[\s\S]*?\r?\n---/);
|
|
@@ -1558,13 +1595,15 @@ function isPlainObject(value) {
|
|
|
1558
1595
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
1559
1596
|
}
|
|
1560
1597
|
function renderAgentFrontmatter(name, description, options = {}) {
|
|
1561
|
-
const
|
|
1598
|
+
const toolPolicy = options.disallowedTools
|
|
1599
|
+
? `disallowedTools: ${options.disallowedTools}`
|
|
1600
|
+
: `tools: ${options.tools ?? "Read, Grep, Glob, Bash, Edit, Write"}`;
|
|
1562
1601
|
const model = options.model ? `\nmodel: ${options.model}` : "";
|
|
1563
1602
|
const effort = options.effort ? `\neffort: ${options.effort}` : "";
|
|
1564
1603
|
const skills = options.skills?.length
|
|
1565
1604
|
? `\nskills:\n${options.skills.map((skill) => ` - ${skill}`).join("\n")}`
|
|
1566
1605
|
: "";
|
|
1567
|
-
return `---\nname: ${name}\ndescription: ${description}\
|
|
1606
|
+
return `---\nname: ${name}\ndescription: ${description}\n${toolPolicy}${model}${effort}${skills}\n---`;
|
|
1568
1607
|
}
|
|
1569
1608
|
function renderSkillFrontmatter(name, description) {
|
|
1570
1609
|
return `---\nname: ${name}\ndescription: ${description}\n---`;
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { fileURLToPath } from "node:url";
|
|
3
|
+
export { roleUsesLsp } from "../role-tool-policy.js";
|
|
3
4
|
export const VCM_LSP_PLUGIN_NAME = "vcm-lsp-bridge";
|
|
4
5
|
export const VCM_LSP_PLUGIN_DIR = fileURLToPath(new URL("../../../scripts/claude-plugins/vcm-lsp-bridge", import.meta.url));
|
|
5
6
|
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
|
-
}
|
|
@@ -104,7 +104,12 @@ export function createSessionService(deps) {
|
|
|
104
104
|
VCM_RUNTIME_SESSION_TOKEN: runtimeSessionToken
|
|
105
105
|
}, modelEnvironment, {
|
|
106
106
|
...buildUsageTelemetryEnvironment(deps.apiUrl, role, model),
|
|
107
|
-
...(roleUsesLsp(role)
|
|
107
|
+
...(roleUsesLsp(role)
|
|
108
|
+
? {
|
|
109
|
+
ENABLE_LSP_TOOL: "true",
|
|
110
|
+
ENABLE_TOOL_SEARCH: "false"
|
|
111
|
+
}
|
|
112
|
+
: {})
|
|
108
113
|
}),
|
|
109
114
|
cols: input.cols,
|
|
110
115
|
rows: input.rows
|
|
@@ -22,7 +22,11 @@ ${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
|
-
-
|
|
25
|
+
- LSP is mandatory and directly available for navigating project source code. Start source-code navigation with LSP. If it is unavailable, report a VCM LSP configuration failure; do not substitute source text search.
|
|
26
|
+
- Use LSP definitions, implementations, references, document or workspace symbols, and incoming or outgoing calls for all source-code symbol and relationship queries.
|
|
27
|
+
- 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. This prohibition includes locating or inferring definitions, implementations, references, callers, callees, symbols, and behavior paths.
|
|
28
|
+
- Use Glob to locate files and Read to inspect complete code. Text search is allowed only for non-source artifacts that LSP does not model, such as documentation, configuration or data files, generated context, and logs. To inspect source comments, locate the source through LSP or Glob and use Read.
|
|
29
|
+
- Cost, latency, round trips, batching, parallelism, or an expectation of equivalent results never permits source-code text search. Wait for the role-session LSP and follow the skill's bounded retry procedure.
|
|
26
30
|
- If LSP cannot resolve a required project-owned relationship, record it as unresolved. Do not replace semantic evidence with text matches.
|
|
27
31
|
|
|
28
32
|
### Work Persistence
|
|
@@ -6,9 +6,9 @@ Use this skill when Architect, Coder, or Reviewer must establish symbol definiti
|
|
|
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. Start each navigation run with LSP \`documentSymbol\` on a known affected project source file. This initializes the actual role-session language server and proves that it can parse that file; an executable version probe is not workspace readiness.
|
|
9
|
+
2. Start each navigation run with LSP \`documentSymbol\` on a known affected project source file. This initializes the actual role-session language server and proves that it can parse that file; an executable version probe is not workspace readiness. If \`LSP\` is unavailable, report a VCM LSP configuration failure instead of substituting text search.
|
|
10
10
|
3. Use LSP workspace or file symbols, definitions, implementations, references, and incoming or outgoing call hierarchy to resolve project-owned relationships.
|
|
11
|
-
4. If an initial workspace-symbol request is empty or reports indexing,
|
|
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. After a successful file-symbol request, 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.
|
|
13
13
|
6. Expand one project-owned dependency hop at a time until the required behavior path has no unresolved symbol.
|
|
14
14
|
7. Use Glob, generated context, architecture documents, and runtime evidence to locate dynamic registrations, configuration or string edges, macros, documentation, and external boundaries that LSP does not model.
|
package/package.json
CHANGED