vibe-coding-master 0.7.37 → 0.7.38

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
@@ -471,7 +471,10 @@ VCM bundles the Claude Code LSP bridge and loads it for Architect, Coder, and
471
471
  Reviewer sessions, including CCR launches. The project environment must still
472
472
  provide the language server for each detected language: `rust-analyzer`,
473
473
  `typescript-language-server`, `pyright-langserver`, `gopls`, `clangd`, or
474
- `jdtls`. Harness Studio reports a missing executable or failed startup probe
474
+ `jdtls`. Architect, Coder, and Reviewer do not receive the Grep tool, and the
475
+ Harness guard rejects shell text-search commands for those roles so unresolved
476
+ semantic relationships cannot be hidden by textual fallback. Harness Studio
477
+ reports a missing executable or failed readiness probe
475
478
  with the backend diagnostic.
476
479
 
477
480
  Harness Engineer is task-scoped and runs from the active task worktree. The
@@ -53,6 +53,7 @@ const VCM_BASH_DEFAULT_TIMEOUT_MS = "600000";
53
53
  const VCM_AUTO_MEMORY_ENABLED = false;
54
54
  const VCM_HOOK_DEFINITIONS = [
55
55
  { eventName: "PreToolUse", matcher: "Bash", command: VCM_BASH_GUARD_HOOK_COMMAND, timeout: 10 },
56
+ { eventName: "PreToolUse", matcher: "Grep", command: VCM_BASH_GUARD_HOOK_COMMAND, timeout: 10 },
56
57
  { eventName: "UserPromptSubmit", command: VCM_HOOK_COMMAND, timeout: 5 },
57
58
  { eventName: "Stop", command: VCM_STOP_HOOK_COMMAND, timeout: 10 },
58
59
  { eventName: "StopFailure", command: VCM_HOOK_COMMAND, timeout: 5 },
@@ -65,18 +66,18 @@ const AGENT_FRONTMATTER = {
65
66
  },
66
67
  architect: {
67
68
  description: "VCM architecture role for plans, module boundaries, public contracts, verifiable behavior, and docs sync.",
68
- tools: "Read, Grep, Glob, Bash, Edit, Write, Agent, LSP"
69
+ tools: "Read, Glob, Bash, Edit, Write, Agent, LSP"
69
70
  },
70
71
  coder: {
71
72
  description: "VCM implementation role for scoped code changes and focused tests.",
72
- tools: "Read, Grep, Glob, Bash, Edit, Write, Agent, LSP"
73
+ tools: "Read, Glob, Bash, Edit, Write, Agent, LSP"
73
74
  },
74
75
  tester: {
75
76
  description: "VCM testing role for validation, test adequacy, approved-scope validation, and risk findings."
76
77
  },
77
78
  reviewer: {
78
79
  description: "VCM independent gate review role for architecture plans, validation adequacy, and code diffs.",
79
- tools: "Read, Grep, Glob, Bash, Write, LSP"
80
+ tools: "Read, Glob, Bash, Write, LSP"
80
81
  },
81
82
  translator: {
82
83
  description: "VCM task-scoped translation tool role for conversation translation, file translation, bootstrap, and memory updates."
@@ -99,6 +100,11 @@ const REQUIRED_AGENT_TOOLS = {
99
100
  coder: ["Agent", "LSP"],
100
101
  reviewer: ["LSP"]
101
102
  };
103
+ const FORBIDDEN_AGENT_TOOLS = {
104
+ architect: ["Grep"],
105
+ coder: ["Grep"],
106
+ reviewer: ["Grep"]
107
+ };
102
108
  const MANAGED_FILES = [
103
109
  {
104
110
  path: "CLAUDE.md",
@@ -266,7 +272,7 @@ const WHOLE_FILES = [
266
272
  path: ".claude/skills/vcm-code-navigation/SKILL.md",
267
273
  category: "skill",
268
274
  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())
275
+ content: renderSkillFile("VCM Code Navigation Skill", "vcm-code-navigation", "Use when Architect, Coder, or Reviewer must resolve code symbols, references, implementations, call hierarchies, or bounded dependency paths.", renderVcmCodeNavigationSkillRules())
270
276
  },
271
277
  {
272
278
  path: ".claude/skills/vcm-final-acceptance/SKILL.md",
@@ -672,6 +678,9 @@ async function installManagedFile({ projectRoot, definition, dryRun, operations
672
678
  for (const requiredTool of REQUIRED_AGENT_TOOLS[definition.agentName] ?? []) {
673
679
  nextContent = ensureAgentTool(nextContent, requiredTool);
674
680
  }
681
+ for (const forbiddenTool of FORBIDDEN_AGENT_TOOLS[definition.agentName] ?? []) {
682
+ nextContent = removeAgentTool(nextContent, forbiddenTool);
683
+ }
675
684
  await writeIfChanged({
676
685
  targetPath,
677
686
  relativePath: definition.path,
@@ -734,6 +743,22 @@ function ensureAgentTool(content, requiredTool) {
734
743
  const nextTools = [...tools, requiredTool].join(", ");
735
744
  return content.replace(frontmatterMatch[0], frontmatterMatch[0].replace(toolsMatch[0], `tools: ${nextTools}`));
736
745
  }
746
+ function removeAgentTool(content, forbiddenTool) {
747
+ const frontmatterMatch = content.match(/^---\r?\n[\s\S]*?\r?\n---/);
748
+ if (!frontmatterMatch) {
749
+ return content;
750
+ }
751
+ const toolsMatch = frontmatterMatch[0].match(/^tools:\s*(.*)$/m);
752
+ if (!toolsMatch) {
753
+ return content;
754
+ }
755
+ const tools = toolsMatch[1].split(",").map((tool) => tool.trim()).filter(Boolean);
756
+ const nextTools = tools.filter((tool) => tool !== forbiddenTool);
757
+ if (nextTools.length === tools.length) {
758
+ return content;
759
+ }
760
+ return content.replace(frontmatterMatch[0], frontmatterMatch[0].replace(toolsMatch[0], `tools: ${nextTools.join(", ")}`));
761
+ }
737
762
  function migrateLegacyManagedFile(definition, currentContent, block) {
738
763
  const legacyContent = definition.legacyWholeFile?.trimEnd();
739
764
  if (!legacyContent) {
@@ -53,6 +53,7 @@ const VCM_BASH_DEFAULT_TIMEOUT_MS = "600000";
53
53
  const VCM_AUTO_MEMORY_ENABLED = false;
54
54
  const VCM_HOOK_DEFINITIONS = [
55
55
  { eventName: "PreToolUse", matcher: "Bash", command: VCM_BASH_GUARD_HOOK_COMMAND, timeout: 10 },
56
+ { eventName: "PreToolUse", matcher: "Grep", command: VCM_BASH_GUARD_HOOK_COMMAND, timeout: 10 },
56
57
  { eventName: "UserPromptSubmit", command: VCM_HOOK_COMMAND, timeout: 5 },
57
58
  { eventName: "Stop", command: VCM_STOP_HOOK_COMMAND, timeout: 10 },
58
59
  { eventName: "StopFailure", command: VCM_HOOK_COMMAND, timeout: 5 },
@@ -116,7 +117,7 @@ const HARNESS_FILES = [
116
117
  kind: "skill-vcm-code-navigation",
117
118
  path: ".claude/skills/vcm-code-navigation/SKILL.md",
118
119
  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
+ frontmatter: renderSkillFrontmatter("vcm-code-navigation", "Use when Architect, Coder, or Reviewer must resolve code symbols, references, implementations, call hierarchies, or bounded dependency paths."),
120
121
  ownership: "whole-file",
121
122
  renderRules: renderVcmCodeNavigationSkillRules
122
123
  },
@@ -198,7 +199,8 @@ const HARNESS_FILES = [
198
199
  title: "Reviewer Agent",
199
200
  memoryBlock: true,
200
201
  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" }),
202
+ forbiddenTools: ["Grep"],
203
+ frontmatter: renderAgentFrontmatter("reviewer", "VCM independent gate review role for architecture plans, validation adequacy, and code diffs.", { tools: "Read, Glob, Bash, Write, LSP" }),
202
204
  renderRules: renderReviewerAgentRules
203
205
  },
204
206
  {
@@ -272,8 +274,9 @@ const HARNESS_FILES = [
272
274
  title: "Architect Agent",
273
275
  memoryBlock: true,
274
276
  requiredTools: ["Agent", "LSP"],
277
+ forbiddenTools: ["Grep"],
275
278
  blankLineBeforeEnd: true,
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" }),
279
+ frontmatter: renderAgentFrontmatter("architect", "VCM architecture role for plans, module boundaries, public contracts, verifiable behavior, and docs sync.", { tools: "Read, Glob, Bash, Edit, Write, Agent, LSP" }),
277
280
  renderRules: renderArchitectHarnessRules
278
281
  },
279
282
  {
@@ -282,7 +285,8 @@ const HARNESS_FILES = [
282
285
  title: "Coder Agent",
283
286
  memoryBlock: true,
284
287
  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" }),
288
+ forbiddenTools: ["Grep"],
289
+ frontmatter: renderAgentFrontmatter("coder", "VCM implementation role for scoped code changes and focused tests.", { tools: "Read, Glob, Bash, Edit, Write, Agent, LSP" }),
286
290
  renderRules: renderCoderHarnessRules
287
291
  },
288
292
  {
@@ -1220,7 +1224,7 @@ async function analyzeHarnessFile(fs, repoRoot, definition) {
1220
1224
  const migratedContent = migrateLegacyHarnessFile(definition, currentContent, expectedBlock);
1221
1225
  if (migratedContent) {
1222
1226
  const memoryUpdatedContent = definition.memoryBlock ? ensureVcmMemoryBlock(migratedContent) : migratedContent;
1223
- const nextContent = ensureAgentTools(memoryUpdatedContent, definition.requiredTools);
1227
+ const nextContent = normalizeAgentTools(memoryUpdatedContent, definition.requiredTools, definition.forbiddenTools);
1224
1228
  return {
1225
1229
  definition,
1226
1230
  status: {
@@ -1239,7 +1243,7 @@ async function analyzeHarnessFile(fs, repoRoot, definition) {
1239
1243
  };
1240
1244
  }
1241
1245
  const insertedContent = `${currentContent.trimEnd()}\n\n${expectedBlock}\n`;
1242
- const nextContent = ensureAgentTools(insertedContent, definition.requiredTools);
1246
+ const nextContent = normalizeAgentTools(insertedContent, definition.requiredTools, definition.forbiddenTools);
1243
1247
  return {
1244
1248
  definition,
1245
1249
  status: {
@@ -1261,7 +1265,7 @@ async function analyzeHarnessFile(fs, repoRoot, definition) {
1261
1265
  const currentBlock = match[0];
1262
1266
  const blockUpdatedContent = currentContent.replace(managedBlockPattern, expectedBlock);
1263
1267
  const memoryUpdatedContent = definition.memoryBlock ? ensureVcmMemoryBlock(blockUpdatedContent) : blockUpdatedContent;
1264
- const nextContent = ensureAgentTools(memoryUpdatedContent, definition.requiredTools);
1268
+ const nextContent = normalizeAgentTools(memoryUpdatedContent, definition.requiredTools, definition.forbiddenTools);
1265
1269
  const action = currentContent === nextContent ? "ok" : "update";
1266
1270
  return {
1267
1271
  definition,
@@ -1392,6 +1396,26 @@ function ensureAgentTool(content, requiredTool) {
1392
1396
  function ensureAgentTools(content, requiredTools) {
1393
1397
  return (requiredTools ?? []).reduce(ensureAgentTool, content);
1394
1398
  }
1399
+ function removeAgentTool(content, forbiddenTool) {
1400
+ const frontmatterMatch = content.match(/^---\r?\n[\s\S]*?\r?\n---/);
1401
+ if (!frontmatterMatch) {
1402
+ return content;
1403
+ }
1404
+ const toolsMatch = frontmatterMatch[0].match(/^tools:\s*(.*)$/m);
1405
+ if (!toolsMatch) {
1406
+ return content;
1407
+ }
1408
+ const tools = toolsMatch[1].split(",").map((tool) => tool.trim()).filter(Boolean);
1409
+ const nextTools = tools.filter((tool) => tool !== forbiddenTool);
1410
+ if (nextTools.length === tools.length) {
1411
+ return content;
1412
+ }
1413
+ return content.replace(frontmatterMatch[0], frontmatterMatch[0].replace(toolsMatch[0], `tools: ${nextTools.join(", ")}`));
1414
+ }
1415
+ function normalizeAgentTools(content, requiredTools, forbiddenTools) {
1416
+ const required = ensureAgentTools(content, requiredTools);
1417
+ return (forbiddenTools ?? []).reduce(removeAgentTool, required);
1418
+ }
1395
1419
  function migrateLegacyHarnessFile(definition, currentContent, block) {
1396
1420
  const legacyContent = definition.legacyWholeFile?.trimEnd();
1397
1421
  if (!legacyContent) {
@@ -1479,7 +1503,7 @@ function withVcmClaudeHooks(settings) {
1479
1503
  ? hooks[definition.eventName]
1480
1504
  : [];
1481
1505
  hooks[definition.eventName] = [
1482
- ...existingMatchers.filter((entry) => !isVcmHookMatcher(entry)),
1506
+ ...existingMatchers,
1483
1507
  {
1484
1508
  ...(definition.matcher ? { matcher: definition.matcher } : {}),
1485
1509
  hooks: [
@@ -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
+ ### Semantic Code Navigation
23
+
24
+ - Use \`vcm-code-navigation\` 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.
26
+ - Do not use the Grep tool or shell text-search commands such as \`grep\`, \`rg\`, or \`git grep\`.
27
+ - If LSP cannot resolve a required project-owned relationship, record it as unresolved. Do not replace semantic evidence with text matches.
28
+
22
29
  ### Work Persistence
23
30
 
24
31
  - Treat Architect artifacts, not Session memory, as continuation state.
@@ -45,9 +52,8 @@ ${renderRoleMemoryRules("architect")}
45
52
  ### Planning Code Reading
46
53
 
47
54
  - 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.
55
+ - Use \`vcm-code-navigation\` for symbol definitions, implementations, references, call hierarchies, and bounded behavior paths. Start from generated indexes, use LSP semantic navigation, then read every resolved callable unit in full.
56
+ - If LSP cannot resolve a required project-owned relationship, record the limitation in \`architecture-evidence.md\` and leave it unresolved.
51
57
  - Define the planning boundary as the affected feature or module and identify every existing or intended observable entry point for the behavior being changed.
52
58
  - Read the complete implementation of each relevant existing entry point.
53
59
  - 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.
@@ -202,7 +208,7 @@ The code-reading phase is complete only when:
202
208
  - every indirect callback, event, hook, queue, route, and dynamic dispatch path has been resolved
203
209
  - every relevant state reader and writer has been read
204
210
  - 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
211
+ - every semantic relationship records LSP, runtime, external-boundary, or generated-boundary evidence
206
212
  - no unresolved project-owned symbol remains
207
213
 
208
214
  Do not diagnose the root cause or choose a fix before the Code Reading Closure is complete.
@@ -12,6 +12,13 @@ ${renderRoleMemoryRules("coder")}
12
12
  - Implement assigned file/function-level scaffold items; do not analyze, review, dispute, or redesign architecture, module boundaries, public contracts, dependency direction, durable docs strategy, validation strategy, or final test adequacy.
13
13
  - Treat the architecture plan and scaffold as execution instructions, not review targets. Do not critique, reinterpret, or challenge them during Coder work.
14
14
 
15
+ ### Semantic Code Navigation
16
+
17
+ - Use \`vcm-code-navigation\` whenever implementation requires code definitions, implementations, references, callers, callees, or behavior paths.
18
+ - Use LSP for semantic relationships. Use Glob to locate files and Read to inspect complete code.
19
+ - Do not use the Grep tool or shell text-search commands such as \`grep\`, \`rg\`, or \`git grep\`.
20
+ - If LSP cannot resolve a required project-owned relationship, record it as unresolved in the completion evidence. Do not replace semantic evidence with text matches.
21
+
15
22
  ### Shared Coding Standards
16
23
 
17
24
  - Before editing production code or tests, read and follow \`docs/CODING_STANDARDS.md\`.
@@ -26,7 +33,7 @@ ${renderRoleMemoryRules("coder")}
26
33
  - Do not stop before editing because of predicted architecture, design, contract, validation, or test failure; implement the assigned scaffold first.
27
34
  - Use \`.ai/generated/module-index.json\` to locate approved module source and test files.
28
35
  - Use \`.ai/generated/public-surface.json\` to avoid accidental public API drift.
29
- - When LSP is available, use definitions and references to locate assigned callable surfaces precisely. Use Grep for text or dynamic edges, not to broaden or reinterpret the approved implementation scope.
36
+ - Use LSP definitions and references to locate assigned callable surfaces precisely.
30
37
 
31
38
  ### Implementation
32
39
 
@@ -15,20 +15,23 @@ Use only these decisions:
15
15
  - \`approve\`: required gate evidence is present, current, internally consistent, sufficient for that gate, and has no gate-blocking finding.
16
16
  - \`request_changes\`: evidence is missing, stale, contradictory, incomplete, insufficient, not reviewable, or unsafe.
17
17
 
18
+ ## Semantic Code Navigation
19
+
20
+ Use \`vcm-code-navigation\` whenever a gate requires code definitions,
21
+ implementations, references, callers, callees, or behavior paths. Use LSP for
22
+ semantic relationships, Glob to locate files, Read to inspect complete code,
23
+ and generated context, architecture documents, or runtime evidence for
24
+ boundaries LSP does not model. Do not use the Grep tool or shell text-search
25
+ commands such as \`grep\`, \`rg\`, or \`git grep\`. If LSP cannot resolve
26
+ a required project-owned relationship, treat that evidence as unresolved and
27
+ return \`request_changes\`; do not replace semantic evidence with text matches.
28
+
18
29
  Every Gate Review is a complete review of the current gate inputs. Review all
19
30
  required evidence and rerun every required mechanical check before deciding.
20
31
  Do not carry forward prior conclusions, closed checks, or partial verification.
21
32
  Resolving prior findings does not replace the complete review. Return \`approve\`
22
33
  or \`request_changes\` only after the review is complete.
23
34
 
24
- Use \`vcm-code-navigation\` whenever a gate requires definitions, implementations,
25
- references, call hierarchies, or a bounded behavior path. Use LSP semantic
26
- navigation when available and read every resolved callable unit in full. Use
27
- structural search when available and Grep for dynamic or textual edges and
28
- explicit fallback discovery. Do not treat Grep as proof of a complete symbol,
29
- caller, implementation, or reference set when LSP is available. If LSP cannot
30
- resolve required evidence, record the limitation and exact fallback basis.
31
-
32
35
  ## Architecture Plan Gate
33
36
 
34
37
  Format is necessary but not sufficient. Do not approve an architecture plan
@@ -469,7 +472,7 @@ If there are no findings, write:
469
472
  None.
470
473
  \`\`\`
471
474
 
472
- Use Bash only for read-only inspection such as \`git diff\`, \`git status\`, \`git show\`, \`ls\`, \`rg\`, \`sed\`, or \`cat\`. Do not run tests, builds, formatters, generators, package managers, or commands that modify files.
475
+ Use Bash only for read-only inspection such as \`git diff\`, \`git status\`, \`git show\`, \`ls\`, \`sed\`, or \`cat\`. Do not run shell text-search commands, tests, builds, formatters, generators, package managers, or commands that modify files.
473
476
 
474
477
  Review only code, architecture, and documents; do not perform validation. Do not edit code, tests, durable docs, role files, route files, or handoff artifacts. Do not assign findings or remediation work to VCM roles, choose fixes, decide Replan, or decide whether user intervention is needed.
475
478
 
@@ -1,22 +1,22 @@
1
1
  export function renderVcmCodeNavigationSkillRules() {
2
2
  return `## Purpose
3
3
 
4
- Use this skill when Architect or Reviewer must establish symbol definitions, implementations, references, callers, callees, or a bounded behavior path from current-worktree evidence.
4
+ Use this skill when Architect, Coder, or Reviewer must establish symbol definitions, implementations, references, callers, callees, or a bounded behavior path from current-worktree evidence.
5
5
 
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. When LSP is available, use workspace or file symbols, definitions, implementations, references, and incoming or outgoing call hierarchy to resolve project-owned relationships.
9
+ 2. Use LSP workspace or file symbols, definitions, implementations, references, and incoming or outgoing call hierarchy to resolve project-owned relationships.
10
10
  3. Read the complete project-owned callable unit at every resolved location.
11
11
  4. Expand one project-owned dependency hop at a time until the required behavior path has no unresolved symbol.
12
- 5. Use structural search when available, and use Grep for dynamic registrations, configuration or string edges, macros not resolved by LSP, documentation, and explicit fallback discovery.
12
+ 5. 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.
13
13
 
14
14
  ## Evidence
15
15
 
16
- - Record each resolved relationship and whether it came from LSP, structural search, Grep fallback, runtime evidence, or a generated boundary.
17
- - Do not treat a Grep result as proof of a complete definition, caller, implementation, or reference set when LSP is available.
16
+ - Record each resolved relationship and whether it came from LSP, runtime evidence, an external boundary, or a generated boundary.
18
17
  - When an empty LSP result contradicts a direct call in the code, another LSP result, or runtime evidence, treat the relationship as unresolved. Record the contradiction and use exact fallback evidence.
19
- - If LSP is unavailable or cannot resolve a relationship, record that limitation and the exact fallback evidence. Do not describe text-search results as compiler-accurate or complete.
18
+ - If LSP is unavailable or cannot resolve a required project-owned relationship, record that limitation and leave the relationship unresolved. Do not substitute text search for semantic navigation.
19
+ - Do not use the Grep tool or shell text-search commands such as \`grep\`, \`rg\`, or \`git grep\`.
20
20
  - Generated indexes and architecture docs locate likely code; reading current-worktree implementation establishes behavior.
21
21
 
22
22
  ## Context Boundary
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibe-coding-master",
3
- "version": "0.7.37",
3
+ "version": "0.7.38",
4
4
  "description": "Local GUI session cockpit for Claude Code role sessions.",
5
5
  "type": "module",
6
6
  "files": [
@@ -1,11 +1,9 @@
1
1
  #!/usr/bin/env python3
2
- """VCM PreToolUse guard for supervised Bash inside VCM role sessions.
2
+ """VCM PreToolUse guard for supervised tools inside VCM role sessions.
3
3
 
4
- Reads the Claude Code PreToolUse hook payload on stdin. When the Bash tool
5
- call would start background work (run_in_background, nohup, setsid, disown,
6
- or a lone '&'), or composes a supervised long-job tool with another shell
7
- command, it prints a deny decision that redirects the role to the
8
- vcm-long-running-validation skill. Anything else is allowed by staying silent.
4
+ Reads the Claude Code PreToolUse hook payload on stdin. It enforces supervised
5
+ Bash execution for every VCM role and prevents LSP-enabled roles from replacing
6
+ semantic code navigation with the Grep tool or shell text-search commands.
9
7
 
10
8
  Quoted payloads of `sh -c` / `bash -lc` style invocations are executable
11
9
  shell code, so they are scanned recursively. `.ai/tools/run-long-check` is the
@@ -13,6 +11,7 @@ only sanctioned detached worker; the command it runs must still stay in the
13
11
  supervised foreground process group.
14
12
  """
15
13
  import json
14
+ import os
16
15
  import re
17
16
  import sys
18
17
 
@@ -22,6 +21,11 @@ SKILL_HINT = (
22
21
  "`.ai/tools/watch-job <job-id>` in the same turn, repeating watch-job "
23
22
  "until it reports a terminal result."
24
23
  )
24
+ CODE_NAV_HINT = (
25
+ "Use LSP, Glob, Read, generated context, architecture documents, or runtime "
26
+ "evidence; do not substitute text search."
27
+ )
28
+ LSP_ROLES = {"architect", "coder", "reviewer"}
25
29
 
26
30
  MAX_NESTED_SHELL_DEPTH = 3
27
31
  QUOTED = re.compile(r"'([^']*)'|\"([^\"]*)\"")
@@ -40,6 +44,20 @@ RUN_LONG_CHECK_SHELL_STRING = re.compile(
40
44
  r"(?:-\w+\s+)*-\w*c\w*(?:\s|$)"
41
45
  )
42
46
  COMMAND_SUBSTITUTION = re.compile(r"\$\(([^()]*)\)|`([^`]*)`")
47
+ TEXT_SEARCH_COMMAND = re.compile(
48
+ r"(?:^|[;&|()\n`])\s*"
49
+ r"(?:(?:[A-Za-z_][A-Za-z0-9_]*=[^\s;&|()]+)\s+)*"
50
+ r"(?:env(?:\s+(?:-[^\s]+|[A-Za-z_][A-Za-z0-9_]*=[^\s]+))*\s+)?"
51
+ r"(?:command\s+|sudo(?:\s+-[^\s]+)*\s+)?"
52
+ r"(?:[^\s;&|()]*/)?(?:grep|egrep|fgrep|rg|ripgrep)(?=\s|$|[;&|()])"
53
+ )
54
+ GIT_GREP_COMMAND = re.compile(
55
+ r"(?:^|[;&|()\n`])\s*(?:git)(?:\s+-[^\s]+)*\s+grep(?=\s|$|[;&|()])"
56
+ )
57
+ WRAPPED_TEXT_SEARCH_COMMAND = re.compile(
58
+ r"(?:^|\s)(?:xargs(?:\s+-[^\s]+)*|-exec|-execdir)\s+"
59
+ r"(?:[^\s;&|()]*/)?(?:grep|egrep|fgrep|rg|ripgrep)(?=\s|$|[;&|()])"
60
+ )
43
61
 
44
62
 
45
63
  def strip_quoted(command: str) -> str:
@@ -83,7 +101,7 @@ def protected_tool_invoked(command_without_quotes: str) -> bool:
83
101
  return bool(PROTECTED_TOOL_INVOCATION.search(command_without_quotes))
84
102
 
85
103
 
86
- def scan_shell_command(command: str, depth: int = 0) -> list[str]:
104
+ def scan_shell_command(command: str, depth: int = 0, forbid_text_search: bool = False) -> list[str]:
87
105
  reasons = []
88
106
  stripped = strip_escaped_characters(strip_quoted(command))
89
107
  if re.search(r"(?:^|[\s;&|(])(?:nohup|setsid)(?:\s|$)", stripped):
@@ -92,6 +110,12 @@ def scan_shell_command(command: str, depth: int = 0) -> list[str]:
92
110
  reasons.append("disown is forbidden")
93
111
  if unquoted_ampersand(stripped):
94
112
  reasons.append("'&' background execution is forbidden")
113
+ if forbid_text_search and (
114
+ TEXT_SEARCH_COMMAND.search(stripped)
115
+ or GIT_GREP_COMMAND.search(stripped)
116
+ or WRAPPED_TEXT_SEARCH_COMMAND.search(stripped)
117
+ ):
118
+ reasons.append("text-search commands are forbidden for this LSP-enabled role")
95
119
 
96
120
  if protected_tool_invoked(stripped):
97
121
  if SHELL_CONTROL_OPERATOR.search(stripped):
@@ -104,17 +128,17 @@ def scan_shell_command(command: str, depth: int = 0) -> list[str]:
104
128
  for segment in quoted_segments(command):
105
129
  if protected_tool_invoked(strip_escaped_characters(strip_quoted(segment))):
106
130
  reasons.append("run-long-check and watch-job must not be invoked through a shell command string")
107
- reasons.extend(scan_shell_command(segment, depth + 1))
131
+ reasons.extend(scan_shell_command(segment, depth + 1, forbid_text_search))
108
132
  if depth < MAX_NESTED_SHELL_DEPTH:
109
133
  for substitution in double_quoted_command_substitutions(command):
110
134
  nested_stripped = strip_escaped_characters(strip_quoted(substitution))
111
135
  if protected_tool_invoked(nested_stripped):
112
136
  reasons.append("run-long-check and watch-job must not be invoked through command substitution")
113
- reasons.extend(scan_shell_command(substitution, depth + 1))
137
+ reasons.extend(scan_shell_command(substitution, depth + 1, forbid_text_search))
114
138
  return reasons
115
139
 
116
140
 
117
- def guard_reasons(tool_input: dict) -> list[str]:
141
+ def guard_reasons(tool_input: dict, forbid_text_search: bool = False) -> list[str]:
118
142
  reasons = []
119
143
  if tool_input.get("run_in_background"):
120
144
  reasons.append("Bash run_in_background is forbidden")
@@ -122,7 +146,7 @@ def guard_reasons(tool_input: dict) -> list[str]:
122
146
  command = tool_input.get("command")
123
147
  command = command if isinstance(command, str) else ""
124
148
 
125
- reasons.extend(scan_shell_command(command))
149
+ reasons.extend(scan_shell_command(command, forbid_text_search=forbid_text_search))
126
150
  return list(dict.fromkeys(reasons))
127
151
 
128
152
 
@@ -132,15 +156,26 @@ def main() -> int:
132
156
  payload = json.loads(raw) if raw.strip() else {}
133
157
  except ValueError:
134
158
  return 0
135
- if payload.get("tool_name") != "Bash":
159
+ tool_name = payload.get("tool_name")
160
+ role = os.environ.get("VCM_ROLE", "").strip()
161
+ forbid_text_search = role in LSP_ROLES
162
+ if tool_name == "Grep" and forbid_text_search:
163
+ reasons = ["the Grep tool is forbidden for this LSP-enabled role"]
164
+ elif tool_name == "Bash":
165
+ tool_input = payload.get("tool_input")
166
+ tool_input = tool_input if isinstance(tool_input, dict) else {}
167
+ reasons = guard_reasons(tool_input, forbid_text_search=forbid_text_search)
168
+ else:
136
169
  return 0
137
-
138
- tool_input = payload.get("tool_input")
139
- tool_input = tool_input if isinstance(tool_input, dict) else {}
140
- reasons = guard_reasons(tool_input)
141
170
  if not reasons:
142
171
  return 0
143
172
 
173
+ hints = []
174
+ if any("text-search" in reason or "Grep tool" in reason for reason in reasons):
175
+ hints.append(CODE_NAV_HINT)
176
+ if any("text-search" not in reason and "Grep tool" not in reason for reason in reasons):
177
+ hints.append(SKILL_HINT)
178
+
144
179
  print(
145
180
  json.dumps(
146
181
  {
@@ -148,7 +183,7 @@ def main() -> int:
148
183
  "hookEventName": "PreToolUse",
149
184
  "permissionDecision": "deny",
150
185
  "permissionDecisionReason": (
151
- "VCM denied this Bash call (" + "; ".join(reasons) + "). " + SKILL_HINT
186
+ "VCM denied this tool call (" + "; ".join(reasons) + "). " + " ".join(hints)
152
187
  ),
153
188
  }
154
189
  }