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.
@@ -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
- args.push("--allowedTools", "Glob,Grep");
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
- tools: "Read, Grep, Glob, Bash, Edit, Write, Agent, LSP",
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
- tools: "Read, Grep, Glob, Bash, Edit, Write, Agent, LSP",
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
- tools: "Read, Grep, Glob, Bash, Write, LSP",
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
- architect: ["Grep", "Agent", "LSP"],
104
- coder: ["Grep", "Agent", "LSP"],
105
- reviewer: ["Grep", "LSP"]
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 = ensureAgentTool(nextContent, requiredTool);
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 tools = frontmatter.tools ?? "Read, Grep, Glob, Bash, Edit, Write";
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}\ntools: ${tools}${model}${effort}${skills}\n---\n\n# ${definition.title}\n\n${block}${suffix ? `\n\n${suffix}` : ""}\n`;
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 ensureAgentTool(content, requiredTool) {
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 toolsMatch = frontmatterMatch[0].match(/^tools:\s*(.*)$/m);
756
- if (!toolsMatch) {
757
- return content.replace(/^(---\r?\n[\s\S]*?)(\r?\n---)/, `$1\ntools: ${requiredTool}$2`);
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 tools = toolsMatch[1].split(",").map((tool) => tool.trim()).filter(Boolean);
760
- if (tools.includes(requiredTool)) {
799
+ const disallowedMatch = frontmatter.match(/^disallowedTools:\s*(.*)$/m);
800
+ if (!disallowedMatch) {
761
801
  return content;
762
802
  }
763
- const nextTools = [...tools, requiredTool].join(", ");
764
- return content.replace(frontmatterMatch[0], frontmatterMatch[0].replace(toolsMatch[0], `tools: ${nextTools}`));
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
- requiredTools: ["Grep", "LSP"],
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.", { tools: "Read, Grep, Glob, Bash, Write, LSP", skills: ["vcm-code-navigation"] }),
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
- frontmatter: renderAgentFrontmatter("harness-engineer", "VCM task-scoped harness maintenance role for harness diagnosis, diff proposals, and VCM issue drafts."),
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
- frontmatter: renderAgentFrontmatter("project-manager", "User-facing VCM orchestration role for task clarification, role routing, handoffs, acceptance, and PR preparation."),
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
- requiredTools: ["Grep", "Agent", "LSP"],
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.", { tools: "Read, Grep, Glob, Bash, Edit, Write, Agent, LSP", skills: ["vcm-code-navigation"] }),
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
- requiredTools: ["Grep", "Agent", "LSP"],
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.", { tools: "Read, Grep, Glob, Bash, Edit, Write, Agent, LSP", skills: ["vcm-code-navigation"] }),
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
- frontmatter: renderAgentFrontmatter("tester", "VCM testing role for validation, test adequacy, approved-scope validation, and risk findings."),
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 ensureAgentTool(content, requiredTool) {
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 toolsMatch = frontmatterMatch[0].match(/^tools:\s*(.*)$/m);
1395
- if (!toolsMatch) {
1396
- return content.replace(/^(---\r?\n[\s\S]*?)(\r?\n---)/, `$1\ntools: ${requiredTool}$2`);
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 tools = toolsMatch[1].split(",").map((tool) => tool.trim()).filter(Boolean);
1399
- if (tools.includes(requiredTool)) {
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 nextTools = [...tools, requiredTool].join(", ");
1403
- return content.replace(frontmatterMatch[0], frontmatterMatch[0].replace(toolsMatch[0], `tools: ${nextTools}`));
1404
- }
1405
- function ensureAgentTools(content, requiredTools) {
1406
- return (requiredTools ?? []).reduce(ensureAgentTool, content);
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 toolsUpdated = ensureAgentTools(content, definition.requiredTools);
1410
- return (definition.requiredSkills ?? []).reduce(ensureAgentSkill, toolsUpdated);
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 tools = options.tools ?? "Read, Grep, Glob, Bash, Edit, Write";
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}\ntools: ${tools}${model}${effort}${skills}\n---`;
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) ? { ENABLE_LSP_TOOL: "true" } : {})
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
- - Use LSP for semantic relationships. Use Glob to locate files, Read to inspect complete code, and generated context, architecture documents, or runtime evidence for boundaries LSP does not model.
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, do not conclude that the symbol is absent. 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.
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibe-coding-master",
3
- "version": "0.7.40",
3
+ "version": "0.7.42",
4
4
  "description": "Local GUI session cockpit for Claude Code role sessions.",
5
5
  "type": "module",
6
6
  "files": [
@@ -10,7 +10,8 @@
10
10
  "command": "rust-analyzer",
11
11
  "extensionToLanguage": {
12
12
  ".rs": "rust"
13
- }
13
+ },
14
+ "startupTimeout": 180000
14
15
  },
15
16
  "typescript": {
16
17
  "command": "typescript-language-server",