vexp-cli 2.2.4 → 2.3.0

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.
@@ -95,6 +95,13 @@ const VEXP_TOOLS = [
95
95
  "save_observation",
96
96
  "get_token_savings",
97
97
  ];
98
+ let activeGuardMode = "off";
99
+ export function setGuardMode(mode) {
100
+ activeGuardMode = mode;
101
+ }
102
+ export function guardMode() {
103
+ return activeGuardMode;
104
+ }
98
105
  // ---------------------------------------------------------------------------
99
106
  // Agent detectors - mirrors VS Code extension's AGENT_DETECTORS
100
107
  // ---------------------------------------------------------------------------
@@ -313,15 +320,27 @@ export function configureAgents(workspaceRoot, binaryPath, version, agentFilter,
313
320
  const wrote = configureClaudeCodeGlobal(binaryPath, mcpServerPath, workspaceRoot);
314
321
  if (wrote)
315
322
  mcpConfigs.push("~/.claude.json");
316
- // Install PreToolUse hook: blocks Grep/Glob when vexp daemon is available
317
- const hookResult = installClaudeCodeHook(workspaceRoot);
318
- if (hookResult) {
323
+ // Guard is OPT-IN from 2.3 (see GuardMode): strict installs the
324
+ // PreToolUse deny hook; default removes any previously installed one.
325
+ if (guardMode() === "strict") {
326
+ const hookResult = installClaudeCodeHook(workspaceRoot);
327
+ if (hookResult) {
328
+ results.push({
329
+ agent: "Claude Code Hook",
330
+ configFile: ".claude/hooks/vexp-guard.sh",
331
+ content: VEXP_GUARD_HOOK,
332
+ alreadyExists: false,
333
+ action: hookResult,
334
+ });
335
+ }
336
+ }
337
+ else if (uninstallClaudeCodeHook(workspaceRoot)) {
319
338
  results.push({
320
339
  agent: "Claude Code Hook",
321
340
  configFile: ".claude/hooks/vexp-guard.sh",
322
- content: VEXP_GUARD_HOOK,
341
+ content: "",
323
342
  alreadyExists: false,
324
- action: hookResult,
343
+ action: "removed",
325
344
  });
326
345
  }
327
346
  }
@@ -360,34 +379,56 @@ export function configureAgents(workspaceRoot, binaryPath, version, agentFilter,
360
379
  // Kilo v7 vendors opencode, so it takes the same guard plugin — the rules
361
380
  // markdown alone was demonstrably not enough (a reported session loaded
362
381
  // vexp.md, quoted it back, and still read five files by hand).
363
- const kilo = installKiloPlugin(workspaceRoot);
364
- if (kilo.plugin) {
382
+ if (guardMode() === "strict") {
383
+ const kilo = installKiloPlugin(workspaceRoot);
384
+ if (kilo.plugin) {
385
+ results.push({
386
+ agent: "Kilo Code Guard",
387
+ configFile: path.join(".kilo", "plugins", "vexp-guard.js"),
388
+ content: VEXP_OPENCODE_GUARD,
389
+ alreadyExists: kilo.plugin === "updated",
390
+ action: kilo.plugin,
391
+ });
392
+ }
393
+ if (kilo.registered)
394
+ mcpConfigs.push(path.relative(workspaceRoot, kiloConfigTarget(workspaceRoot)));
395
+ }
396
+ else if (uninstallKiloPlugin(workspaceRoot)) {
365
397
  results.push({
366
398
  agent: "Kilo Code Guard",
367
399
  configFile: path.join(".kilo", "plugins", "vexp-guard.js"),
368
- content: VEXP_OPENCODE_GUARD,
369
- alreadyExists: kilo.plugin === "updated",
370
- action: kilo.plugin,
400
+ content: "",
401
+ alreadyExists: false,
402
+ action: "removed",
371
403
  });
372
404
  }
373
- if (kilo.registered)
374
- mcpConfigs.push(path.relative(workspaceRoot, kiloConfigTarget(workspaceRoot)));
375
405
  }
376
406
  // Cursor: preToolUse hook matching Grep. Text search only - Cursor's native
377
407
  // semantic search is not hookable.
378
408
  if (detector.agent === "Cursor") {
379
- const cur = installCursorHook(workspaceRoot);
380
- if (cur.hook) {
409
+ if (guardMode() === "strict") {
410
+ const cur = installCursorHook(workspaceRoot);
411
+ if (cur.hook) {
412
+ results.push({
413
+ agent: "Cursor Guard",
414
+ configFile: path.join(".cursor", "hooks", "vexp-guard.js"),
415
+ content: VEXP_CURSOR_GUARD,
416
+ alreadyExists: cur.hook === "updated",
417
+ action: cur.hook,
418
+ });
419
+ }
420
+ if (cur.registered)
421
+ mcpConfigs.push(path.join(".cursor", "hooks.json"));
422
+ }
423
+ else if (uninstallCursorHook(workspaceRoot)) {
381
424
  results.push({
382
425
  agent: "Cursor Guard",
383
426
  configFile: path.join(".cursor", "hooks", "vexp-guard.js"),
384
- content: VEXP_CURSOR_GUARD,
385
- alreadyExists: cur.hook === "updated",
386
- action: cur.hook,
427
+ content: "",
428
+ alreadyExists: false,
429
+ action: "removed",
387
430
  });
388
431
  }
389
- if (cur.registered)
390
- mcpConfigs.push(path.join(".cursor", "hooks.json"));
391
432
  }
392
433
  // Opencode: MCP lives under the `mcp` key in opencode.json(c) — opencode
393
434
  // carries no `mcpConfigFile`, so without this call the generic writer above
@@ -398,14 +439,25 @@ export function configureAgents(workspaceRoot, binaryPath, version, agentFilter,
398
439
  const wroteMcp = configureOpencodeMcp(workspaceRoot, binaryPath, mcpServerPath);
399
440
  if (wroteMcp)
400
441
  mcpConfigs.push(wroteMcp);
401
- const pluginResult = installOpencodePlugin(workspaceRoot);
402
- if (pluginResult) {
442
+ if (guardMode() === "strict") {
443
+ const pluginResult = installOpencodePlugin(workspaceRoot);
444
+ if (pluginResult) {
445
+ results.push({
446
+ agent: "Opencode Guard",
447
+ configFile: ".opencode/plugins/vexp-guard.js",
448
+ content: VEXP_OPENCODE_GUARD,
449
+ alreadyExists: pluginResult === "updated",
450
+ action: pluginResult,
451
+ });
452
+ }
453
+ }
454
+ else if (uninstallOpencodePlugin(workspaceRoot)) {
403
455
  results.push({
404
456
  agent: "Opencode Guard",
405
457
  configFile: ".opencode/plugins/vexp-guard.js",
406
- content: VEXP_OPENCODE_GUARD,
407
- alreadyExists: pluginResult === "updated",
408
- action: pluginResult,
458
+ content: "",
459
+ alreadyExists: false,
460
+ action: "removed",
409
461
  });
410
462
  }
411
463
  }
@@ -555,7 +607,10 @@ export function configureSelectedAgents(workspaceRoot, binaryPath, version, sele
555
607
  const wrote = configureClaudeCodeGlobal(binaryPath, mcpServerPath, workspaceRoot);
556
608
  if (wrote)
557
609
  mcpConfigs.push("~/.claude.json");
558
- installClaudeCodeHook(workspaceRoot);
610
+ if (guardMode() === "strict")
611
+ installClaudeCodeHook(workspaceRoot);
612
+ else
613
+ uninstallClaudeCodeHook(workspaceRoot);
559
614
  }
560
615
  if (detector.agent === "Codex") {
561
616
  const wrote = configureCodexGlobal(binaryPath, mcpServerPath, workspaceRoot);
@@ -581,10 +636,16 @@ export function configureSelectedAgents(workspaceRoot, binaryPath, version, sele
581
636
  const wrote = configureOpencodeMcp(workspaceRoot, binaryPath, mcpServerPath);
582
637
  if (wrote)
583
638
  mcpConfigs.push(wrote);
584
- installOpencodePlugin(workspaceRoot);
639
+ if (guardMode() === "strict")
640
+ installOpencodePlugin(workspaceRoot);
641
+ else
642
+ uninstallOpencodePlugin(workspaceRoot);
585
643
  }
586
644
  if (detector.agent === "Cursor") {
587
- installCursorHook(workspaceRoot);
645
+ if (guardMode() === "strict")
646
+ installCursorHook(workspaceRoot);
647
+ else
648
+ uninstallCursorHook(workspaceRoot);
588
649
  }
589
650
  // Kilo carries no `mcpConfigFile` (its MCP lives under the `mcp` key inside
590
651
  // kilo.jsonc), so without this branch the generic writer above skips it and
@@ -594,7 +655,10 @@ export function configureSelectedAgents(workspaceRoot, binaryPath, version, sele
594
655
  const wrote = configureKiloMcp(workspaceRoot, binaryPath, mcpServerPath);
595
656
  if (wrote)
596
657
  mcpConfigs.push(wrote);
597
- installKiloPlugin(workspaceRoot);
658
+ if (guardMode() === "strict")
659
+ installKiloPlugin(workspaceRoot);
660
+ else
661
+ uninstallKiloPlugin(workspaceRoot);
598
662
  }
599
663
  results.push({ agent: detector.agent, configFile: detector.configFile, content, alreadyExists, action });
600
664
  }
@@ -1478,6 +1542,68 @@ export function migrateClaudeCodeUnpin() {
1478
1542
  // ---------------------------------------------------------------------------
1479
1543
  // Claude Code PreToolUse hook - blocks Grep/Glob when daemon is available
1480
1544
  // ---------------------------------------------------------------------------
1545
+ /**
1546
+ * True for any vexp-related PreToolUse entry in .claude/settings.json —
1547
+ * old format, stale matchers, malformed "suggestion" hooks. Shared by the
1548
+ * strict installer (dedup before re-adding) and the uninstaller (guard off).
1549
+ */
1550
+ function isVexpGuardHookEntry(h) {
1551
+ if (!h || typeof h !== "object")
1552
+ return false;
1553
+ const entry = h;
1554
+ const cmd = entry.command;
1555
+ if (cmd && cmd.includes("vexp-guard"))
1556
+ return true;
1557
+ const hks = entry.hooks;
1558
+ if (hks && Array.isArray(hks) && hks.some((hook) => {
1559
+ if (!hook || typeof hook !== "object")
1560
+ return false;
1561
+ const hCmd = hook.command;
1562
+ return hCmd?.includes("vexp-guard") ?? false;
1563
+ }))
1564
+ return true;
1565
+ if (cmd && cmd.includes("run_pipeline") && cmd.includes("allow"))
1566
+ return true;
1567
+ return false;
1568
+ }
1569
+ /**
1570
+ * Remove the Claude Code guard (2.3 A2 default): drop vexp entries from
1571
+ * .claude/settings.json PreToolUse and delete the hook scripts. Returns true
1572
+ * when anything was actually removed.
1573
+ */
1574
+ export function uninstallClaudeCodeHook(workspaceRoot) {
1575
+ let removed = false;
1576
+ const hookDir = path.join(workspaceRoot, ".claude", "hooks");
1577
+ for (const name of ["vexp-guard.sh", "vexp-guard.cmd"]) {
1578
+ const p = path.join(hookDir, name);
1579
+ if (fs.existsSync(p)) {
1580
+ try {
1581
+ fs.unlinkSync(p);
1582
+ removed = true;
1583
+ }
1584
+ catch { /* leave in place */ }
1585
+ }
1586
+ }
1587
+ const settingsPath = path.join(workspaceRoot, ".claude", "settings.json");
1588
+ const read = readJsonConfigSafe(settingsPath);
1589
+ if (read.ok && read.existed) {
1590
+ const settings = read.data;
1591
+ const hooks = (settings.hooks ?? {});
1592
+ const existingPreToolUse = Array.isArray(hooks.PreToolUse) ? hooks.PreToolUse : [];
1593
+ const filtered = existingPreToolUse.filter((h) => !isVexpGuardHookEntry(h));
1594
+ if (filtered.length !== existingPreToolUse.length) {
1595
+ if (filtered.length > 0)
1596
+ hooks.PreToolUse = filtered;
1597
+ else
1598
+ delete hooks.PreToolUse;
1599
+ settings.hooks = hooks;
1600
+ backupConfig(settingsPath);
1601
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
1602
+ removed = true;
1603
+ }
1604
+ }
1605
+ return removed;
1606
+ }
1481
1607
  /**
1482
1608
  * Install the vexp-guard hook for Claude Code.
1483
1609
  * Creates .claude/hooks/vexp-guard.sh and merges hook config into .claude/settings.json.
@@ -1506,28 +1632,7 @@ export function installClaudeCodeHook(workspaceRoot) {
1506
1632
  const hooks = (settings.hooks ?? {});
1507
1633
  const existingPreToolUse = Array.isArray(hooks.PreToolUse) ? hooks.PreToolUse : [];
1508
1634
  // Aggressively remove ALL vexp-related hook entries (old format, stale matchers, malformed)
1509
- const filtered = existingPreToolUse.filter((h) => {
1510
- if (!h || typeof h !== "object")
1511
- return true;
1512
- const entry = h;
1513
- // Remove flat-format entries referencing vexp-guard
1514
- const cmd = entry.command;
1515
- if (cmd && cmd.includes("vexp-guard"))
1516
- return false;
1517
- // Remove nested-format entries referencing vexp-guard
1518
- const hks = entry.hooks;
1519
- if (hks && Array.isArray(hks) && hks.some((hook) => {
1520
- if (!hook || typeof hook !== "object")
1521
- return false;
1522
- const hCmd = hook.command;
1523
- return hCmd?.includes("vexp-guard") ?? false;
1524
- }))
1525
- return false;
1526
- // Remove malformed "suggestion" hooks that output allow + mention run_pipeline
1527
- if (cmd && cmd.includes("run_pipeline") && cmd.includes("allow"))
1528
- return false;
1529
- return true;
1530
- });
1635
+ const filtered = existingPreToolUse.filter((h) => !isVexpGuardHookEntry(h));
1531
1636
  filtered.push({
1532
1637
  matcher: "Grep|Glob|Regex",
1533
1638
  hooks: [
@@ -1576,6 +1681,25 @@ function writeGuardScript(scriptPath, content) {
1576
1681
  export function installOpencodePlugin(workspaceRoot) {
1577
1682
  return writeGuardScript(path.join(workspaceRoot, ".opencode", "plugins", "vexp-guard.js"), VEXP_OPENCODE_GUARD);
1578
1683
  }
1684
+ /** Delete a guard script if present. Returns true when a file was removed. */
1685
+ function removeGuardScript(scriptPath) {
1686
+ if (!fs.existsSync(scriptPath))
1687
+ return false;
1688
+ try {
1689
+ fs.unlinkSync(scriptPath);
1690
+ return true;
1691
+ }
1692
+ catch {
1693
+ return false;
1694
+ }
1695
+ }
1696
+ /** Remove the opencode guard plugin (2.3 A2 default). Both directory
1697
+ * spellings are checked — older builds wrote `.opencode/plugin/`. */
1698
+ export function uninstallOpencodePlugin(workspaceRoot) {
1699
+ const a = removeGuardScript(path.join(workspaceRoot, ".opencode", "plugins", "vexp-guard.js"));
1700
+ const b = removeGuardScript(path.join(workspaceRoot, ".opencode", "plugin", "vexp-guard.js"));
1701
+ return a || b;
1702
+ }
1579
1703
  /**
1580
1704
  * Cursor enforcement — `preToolUse` hook with matcher `Grep`.
1581
1705
  *
@@ -1682,6 +1806,59 @@ export function installKiloPlugin(workspaceRoot) {
1682
1806
  fs.writeFileSync(target, JSON.stringify(cfg, null, 2), "utf-8");
1683
1807
  return { plugin, registered: true };
1684
1808
  }
1809
+ /** Remove the Kilo guard plugin (2.3 A2 default): delete the script and drop
1810
+ * the explicit `plugin` array entry from a root kilo.jsonc when present. */
1811
+ export function uninstallKiloPlugin(workspaceRoot) {
1812
+ const pluginPath = path.join(workspaceRoot, ".kilo", "plugins", "vexp-guard.js");
1813
+ let removed = removeGuardScript(pluginPath);
1814
+ const target = kiloConfigTarget(workspaceRoot);
1815
+ const read = readJsonConfigSafe(target);
1816
+ if (read.ok && read.existed) {
1817
+ const cfg = read.data;
1818
+ const list = Array.isArray(cfg.plugin) ? cfg.plugin : [];
1819
+ const filtered = list.filter((e) => typeof e !== "string" || !e.includes("vexp-guard"));
1820
+ if (filtered.length !== list.length) {
1821
+ if (filtered.length > 0)
1822
+ cfg.plugin = filtered;
1823
+ else
1824
+ delete cfg.plugin;
1825
+ backupConfig(target);
1826
+ fs.writeFileSync(target, JSON.stringify(cfg, null, 2), "utf-8");
1827
+ removed = true;
1828
+ }
1829
+ }
1830
+ return removed;
1831
+ }
1832
+ /** Remove the Cursor guard (2.3 A2 default): delete the hook script and drop
1833
+ * our preToolUse entry from .cursor/hooks.json. */
1834
+ export function uninstallCursorHook(workspaceRoot) {
1835
+ const rel = path.join(".cursor", "hooks", "vexp-guard.js");
1836
+ let removed = removeGuardScript(path.join(workspaceRoot, rel));
1837
+ const cfgPath = path.join(workspaceRoot, ".cursor", "hooks.json");
1838
+ const read = readJsonConfigSafe(cfgPath);
1839
+ if (read.ok && read.existed) {
1840
+ const cfg = read.data;
1841
+ const hooks = cfg.hooks ?? {};
1842
+ const preToolUse = Array.isArray(hooks.preToolUse) ? hooks.preToolUse : [];
1843
+ const filtered = preToolUse.filter((e) => {
1844
+ if (!e || typeof e !== "object")
1845
+ return true;
1846
+ const cmd = e.command;
1847
+ return typeof cmd !== "string" || !cmd.includes("vexp-guard");
1848
+ });
1849
+ if (filtered.length !== preToolUse.length) {
1850
+ if (filtered.length > 0)
1851
+ hooks.preToolUse = filtered;
1852
+ else
1853
+ delete hooks.preToolUse;
1854
+ cfg.hooks = hooks;
1855
+ backupConfig(cfgPath);
1856
+ fs.writeFileSync(cfgPath, JSON.stringify(cfg, null, 2), "utf-8");
1857
+ removed = true;
1858
+ }
1859
+ }
1860
+ return removed;
1861
+ }
1685
1862
  /**
1686
1863
  * The single highest-leverage instruction we ship, and for a long time the one
1687
1864
  * most agents never saw.
@@ -1735,22 +1912,30 @@ function generateAgentConfig(template, vars) {
1735
1912
  function claudeCodeTemplate(vars) {
1736
1913
  return `## vexp - Context-Aware AI Coding <!-- vexp v${vars.version} -->
1737
1914
 
1738
- ### MANDATORY: use vexp pipeline - do NOT grep or glob the codebase
1739
- For every task - bug fixes, features, refactors, debugging:
1740
- **call \`run_pipeline\` FIRST**. It executes context search + impact analysis +
1741
- memory recall in a single call, returning compressed results.
1742
-
1743
- Do NOT use grep, glob, Bash, or cat to search/explore the codebase.
1744
- vexp returns pre-indexed, graph-ranked context that is more relevant and
1745
- uses fewer tokens than manual searching. Prefer \`get_skeleton\` over Read to
1746
- inspect files (detail: minimal/standard/detailed, 70-90% token savings).
1747
- Only use Read when you need exact raw content to edit a specific line.
1748
-
1749
- The hook denies Grep/Glob while the daemon is up. That is policy, not a transient
1750
- failure: call \`run_pipeline\` instead - do NOT work around it by shelling out or
1751
- writing a script. vexp only covers indexed source inside the workspace: for runtime
1752
- logs, build output (dist/, .vite/, node_modules/) or files outside the repo it has
1753
- no answer - use Bash/Read there, those are never blocked.
1915
+ ### Context strategy: one pipeline call up front, then your normal tools
1916
+ For every multi-file task - bug fixes, features, refactors, debugging:
1917
+ **call \`run_pipeline\` ONCE at the start**. It executes context search + impact
1918
+ analysis + memory recall in a single call: which files matter, what breaks,
1919
+ what was learned before. Then implement with your normal tools.
1920
+
1921
+ Division of labor (each tool where it wins):
1922
+ - **Architecture, impact, "where is X wired"**: \`run_pipeline\` - the graph
1923
+ answers these better and cheaper than exploratory reading.
1924
+ - **Literal text sweeps** (string constants, log messages, config keys, e.g.
1925
+ \`localStorage\`, API paths): Grep/Glob directly - text search is what they
1926
+ are best at, do NOT route text sweeps through vexp.
1927
+ - **Editing**: Read the files you are about to modify; use \`get_skeleton\`
1928
+ (detail: minimal/standard/detailed, 70-90% token savings) for files you only
1929
+ need to understand, not edit.
1930
+
1931
+ Every extra tool call costs a turn: one good \`run_pipeline\` call beats five
1932
+ small ones. Each result opens with a coverage header (index: N files, M nodes) -
1933
+ if it does not match this repo, run \`vexp index\`.
1934
+ vexp only covers indexed source inside the workspace: for runtime logs, build
1935
+ output (dist/, .vite/, node_modules/) or files outside the repo use Bash/Read.
1936
+ vexp runs entirely on this machine: local daemon, local index stored inside the
1937
+ workspace (\`.vexp/\`). \`run_pipeline\` transmits nothing to any external service -
1938
+ treat it like a local build tool; no data-sharing consent is needed to call it.
1754
1939
 
1755
1940
  ### Primary Tool
1756
1941
  - \`run_pipeline\` - **USE THIS FOR EVERYTHING**. Single call that runs
@@ -1770,18 +1955,17 @@ no answer - use Bash/Read there, those are never blocked.
1770
1955
  ${QUERY_SHAPE}
1771
1956
 
1772
1957
  ### Workflow
1773
- 1. \`run_pipeline("your task")\` - ALWAYS FIRST. Returns pivots + impact + memories in 1 call
1774
- 2. Need more detail on a file? Use \`get_skeleton({ files: [...], detail: "detailed" })\` - avoid Read unless editing
1775
- 3. Make targeted changes based on the context returned
1776
- 4. \`run_pipeline\` again ONLY if you need more context during implementation
1777
- 5. Do NOT chain multiple vexp calls - one \`run_pipeline\` replaces capsule + impact + memory + observation
1958
+ 1. \`run_pipeline("your task")\` - ONCE at task start. Returns pivots + impact + memories in 1 call
1959
+ 2. Literal string sweeps? Grep/Glob directly. Editing a file? Read it directly.
1960
+ 3. Structural overview of a non-edit file? \`get_skeleton({ files: [...], detail: "detailed" })\`
1961
+ 4. Make targeted changes based on the context returned
1962
+ 5. \`run_pipeline\` again ONLY when the task moves to a new area - do NOT chain vexp calls per turn
1778
1963
 
1779
1964
  ### Subagent / Explore / Plan mode
1780
- - Subagents CAN and MUST call \`run_pipeline\` - always include the task description
1781
- - The PreToolUse hook blocks Grep/Glob when vexp daemon is running
1782
- - Do NOT spawn Agent(Explore) to freely search - call \`run_pipeline\` first,
1783
- then pass the returned context into the agent prompt if needed
1784
- - Always: \`run_pipeline\` -> get context -> spawn agent with context
1965
+ - Subagents CAN call \`run_pipeline\` - always include the task description
1966
+ - Before spawning Agent(Explore) for architecture questions, call \`run_pipeline\`
1967
+ and pass the returned context into the agent prompt - it usually replaces the
1968
+ exploration entirely
1785
1969
 
1786
1970
  ### Smart Features (automatic - no action needed)
1787
1971
  - **Intent Detection**: auto-detects from your task keywords. "fix bug" -> Debug, "refactor" -> blast-radius, "add" -> Modify
@@ -1809,13 +1993,19 @@ Use \`index_status\` to discover available repo aliases.
1809
1993
  function cursorTemplate(vars) {
1810
1994
  return `## vexp rules for Cursor <!-- vexp v${vars.version} -->
1811
1995
 
1812
- **MANDATORY: use \`run_pipeline\` - do NOT grep, glob, or read files manually.**
1813
- vexp returns pre-indexed, graph-ranked context in a single call.
1996
+ **Call \`run_pipeline\` ONCE at task start for orientation - then use your normal tools.**
1997
+ vexp returns graph-ranked context (relevant files, blast radius, memories) in one call.
1998
+ For literal string sweeps (constants, log messages, config keys) use native search
1999
+ directly - do NOT route text sweeps through vexp.
2000
+ vexp runs entirely on this machine: local daemon, local index stored inside the
2001
+ workspace (\`.vexp/\`). \`run_pipeline\` transmits nothing to any external service -
2002
+ treat it like a local build tool; no data-sharing consent is needed to call it.
1814
2003
 
1815
2004
  ### Workflow
1816
- 1. \`run_pipeline\` with your task description - ALWAYS FIRST (replaces all other tools)
1817
- 2. Make targeted changes based on the context returned
1818
- 3. \`run_pipeline\` again only if you need more context
2005
+ 1. \`run_pipeline\` with your task description - ONCE at task start
2006
+ 2. Literal text sweeps with native search; Read the files you will edit
2007
+ 3. Make targeted changes based on the context returned
2008
+ 4. \`run_pipeline\` again ONLY when the task moves to a new area - not per turn
1819
2009
 
1820
2010
  ### Available MCP tools
1821
2011
  - \`run_pipeline\` - **PRIMARY TOOL**. Runs capsule + impact + memory in 1 call.
@@ -1827,9 +2017,10 @@ vexp returns pre-indexed, graph-ranked context in a single call.
1827
2017
  ${QUERY_SHAPE}
1828
2018
 
1829
2019
  ### Agentic search
1830
- - Do NOT use built-in file search, grep, or codebase indexing - always call \`run_pipeline\` first
2020
+ - Ask vexp first for architecture/impact questions; native search remains the right
2021
+ tool for literal text sweeps
1831
2022
  - If you spawn sub-agents or background tasks, pass them the context from \`run_pipeline\`
1832
- rather than letting them search the codebase independently
2023
+ so they do not re-explore from scratch
1833
2024
 
1834
2025
  ### Tips
1835
2026
  - Add \`include_tests: true\` when debugging
@@ -1849,13 +2040,19 @@ Use file search and read tools directly until the index is ready.
1849
2040
  function windsurfTemplate(vars) {
1850
2041
  return `## vexp for Windsurf <!-- vexp v${vars.version} -->
1851
2042
 
1852
- **MANDATORY: use \`run_pipeline\` - do NOT grep, glob, or read files manually.**
1853
- vexp returns pre-indexed, graph-ranked context in a single call.
2043
+ **Call \`run_pipeline\` ONCE at task start for orientation - then use your normal tools.**
2044
+ vexp returns graph-ranked context (relevant files, blast radius, memories) in one call.
2045
+ For literal string sweeps (constants, log messages, config keys) use native search
2046
+ directly - do NOT route text sweeps through vexp.
2047
+ vexp runs entirely on this machine: local daemon, local index stored inside the
2048
+ workspace (\`.vexp/\`). \`run_pipeline\` transmits nothing to any external service -
2049
+ treat it like a local build tool; no data-sharing consent is needed to call it.
1854
2050
 
1855
2051
  ### Workflow
1856
- 1. \`run_pipeline\` with your task description - ALWAYS FIRST (replaces all other tools)
1857
- 2. Make targeted changes based on the context returned
1858
- 3. \`run_pipeline\` again only if you need more context
2052
+ 1. \`run_pipeline\` with your task description - ONCE at task start
2053
+ 2. Literal text sweeps with native search; Read the files you will edit
2054
+ 3. Make targeted changes based on the context returned
2055
+ 4. \`run_pipeline\` again ONLY when the task moves to a new area - not per turn
1859
2056
 
1860
2057
  ### Available MCP tools
1861
2058
  - \`run_pipeline\` - **PRIMARY TOOL**. Runs capsule + impact + memory in 1 call.
@@ -1867,9 +2064,10 @@ vexp returns pre-indexed, graph-ranked context in a single call.
1867
2064
  ${QUERY_SHAPE}
1868
2065
 
1869
2066
  ### Agentic search
1870
- - Do NOT use built-in file search, grep, or codebase indexing - always call \`run_pipeline\` first
2067
+ - Ask vexp first for architecture/impact questions; native search remains the right
2068
+ tool for literal text sweeps
1871
2069
  - If you spawn sub-agents or background tasks, pass them the context from \`run_pipeline\`
1872
- rather than letting them search the codebase independently
2070
+ so they do not re-explore from scratch
1873
2071
 
1874
2072
  ### Smart Features
1875
2073
  Intent auto-detection, hybrid ranking, session memory, auto-expanding budget.
@@ -1900,13 +2098,19 @@ function continueTemplate(vars) {
1900
2098
  function augmentTemplate(vars) {
1901
2099
  return `## vexp for Augment <!-- vexp v${vars.version} -->
1902
2100
 
1903
- **MANDATORY: use \`run_pipeline\` - do NOT grep, glob, or read files manually.**
1904
- vexp returns pre-indexed, graph-ranked context in a single call.
2101
+ **Call \`run_pipeline\` ONCE at task start for orientation - then use your normal tools.**
2102
+ vexp returns graph-ranked context (relevant files, blast radius, memories) in one call.
2103
+ For literal string sweeps (constants, log messages, config keys) use native search
2104
+ directly - do NOT route text sweeps through vexp.
2105
+ vexp runs entirely on this machine: local daemon, local index stored inside the
2106
+ workspace (\`.vexp/\`). \`run_pipeline\` transmits nothing to any external service -
2107
+ treat it like a local build tool; no data-sharing consent is needed to call it.
1905
2108
 
1906
2109
  When working on this codebase:
1907
- 1. \`run_pipeline\` with task description - ALWAYS FIRST
1908
- 2. Make targeted changes based on the context returned
1909
- 3. \`run_pipeline\` again only if you need more context
2110
+ 1. \`run_pipeline\` with task description - ONCE at task start
2111
+ 2. Literal text sweeps with native search; Read the files you will edit
2112
+ 3. Make targeted changes based on the context returned
2113
+ 4. \`run_pipeline\` again ONLY when the task moves to a new area
1910
2114
 
1911
2115
  ### Available MCP tools
1912
2116
  - \`run_pipeline\` - **PRIMARY TOOL**. Runs capsule + impact + memory in 1 call.
@@ -1918,9 +2122,10 @@ When working on this codebase:
1918
2122
  ${QUERY_SHAPE}
1919
2123
 
1920
2124
  ### Agentic search
1921
- - Do NOT use built-in file search, grep, or codebase indexing - always call \`run_pipeline\` first
2125
+ - Ask vexp first for architecture/impact questions; native search remains the right
2126
+ tool for literal text sweeps
1922
2127
  - If you spawn sub-agents or background tasks, pass them the context from \`run_pipeline\`
1923
- rather than letting them search the codebase independently
2128
+ so they do not re-explore from scratch
1924
2129
 
1925
2130
  ### Smart Features
1926
2131
  Intent auto-detection, hybrid ranking, session memory, auto-expanding budget.
@@ -1932,13 +2137,19 @@ Intent auto-detection, hybrid ranking, session memory, auto-expanding budget.
1932
2137
  function copilotTemplate(vars) {
1933
2138
  return `## vexp context tools <!-- vexp v${vars.version} -->
1934
2139
 
1935
- **MANDATORY: use \`run_pipeline\` - do NOT grep, glob, or read files manually.**
1936
- vexp returns pre-indexed, graph-ranked context in a single call.
2140
+ **Call \`run_pipeline\` ONCE at task start for orientation - then use your normal tools.**
2141
+ vexp returns graph-ranked context (relevant files, blast radius, memories) in one call.
2142
+ For literal string sweeps (constants, log messages, config keys) use native search
2143
+ directly - do NOT route text sweeps through vexp.
2144
+ vexp runs entirely on this machine: local daemon, local index stored inside the
2145
+ workspace (\`.vexp/\`). \`run_pipeline\` transmits nothing to any external service -
2146
+ treat it like a local build tool; no data-sharing consent is needed to call it.
1937
2147
 
1938
2148
  ### Workflow
1939
- 1. \`run_pipeline\` with your task description - ALWAYS FIRST (replaces all other tools)
1940
- 2. Make targeted changes based on the context returned
1941
- 3. \`run_pipeline\` again only if you need more context
2149
+ 1. \`run_pipeline\` with your task description - ONCE at task start
2150
+ 2. Literal text sweeps with native search; Read the files you will edit
2151
+ 3. Make targeted changes based on the context returned
2152
+ 4. \`run_pipeline\` again ONLY when the task moves to a new area - not per turn
1942
2153
 
1943
2154
  ### Available MCP tools
1944
2155
  - \`run_pipeline\` - **PRIMARY TOOL**. Runs capsule + impact + memory in 1 call.
@@ -1950,9 +2161,10 @@ vexp returns pre-indexed, graph-ranked context in a single call.
1950
2161
  ${QUERY_SHAPE}
1951
2162
 
1952
2163
  ### Agentic search
1953
- - Do NOT use built-in file search, grep, or codebase indexing - always call \`run_pipeline\` first
2164
+ - Ask vexp first for architecture/impact questions; native search remains the right
2165
+ tool for literal text sweeps
1954
2166
  - If you spawn sub-agents or background tasks, pass them the context from \`run_pipeline\`
1955
- rather than letting them search the codebase independently
2167
+ so they do not re-explore from scratch
1956
2168
 
1957
2169
  ### Smart Features
1958
2170
  Intent auto-detection, hybrid ranking, session memory, auto-expanding budget.
@@ -1964,13 +2176,19 @@ Intent auto-detection, hybrid ranking, session memory, auto-expanding budget.
1964
2176
  function zedTemplate(vars) {
1965
2177
  return `## vexp for Zed <!-- vexp v${vars.version} -->
1966
2178
 
1967
- **MANDATORY: use \`run_pipeline\` - do NOT grep, glob, or read files manually.**
1968
- vexp returns pre-indexed, graph-ranked context in a single call.
2179
+ **Call \`run_pipeline\` ONCE at task start for orientation - then use your normal tools.**
2180
+ vexp returns graph-ranked context (relevant files, blast radius, memories) in one call.
2181
+ For literal string sweeps (constants, log messages, config keys) use native search
2182
+ directly - do NOT route text sweeps through vexp.
2183
+ vexp runs entirely on this machine: local daemon, local index stored inside the
2184
+ workspace (\`.vexp/\`). \`run_pipeline\` transmits nothing to any external service -
2185
+ treat it like a local build tool; no data-sharing consent is needed to call it.
1969
2186
 
1970
2187
  ### Workflow
1971
- 1. \`run_pipeline\` with your task description - ALWAYS FIRST (replaces all other tools)
1972
- 2. Make targeted changes based on the context returned
1973
- 3. \`run_pipeline\` again only if you need more context
2188
+ 1. \`run_pipeline\` with your task description - ONCE at task start
2189
+ 2. Literal text sweeps with native search; Read the files you will edit
2190
+ 3. Make targeted changes based on the context returned
2191
+ 4. \`run_pipeline\` again ONLY when the task moves to a new area - not per turn
1974
2192
 
1975
2193
  ### Available MCP tools
1976
2194
  - \`run_pipeline\` - **PRIMARY TOOL**. Runs capsule + impact + memory in 1 call.
@@ -1982,9 +2200,10 @@ vexp returns pre-indexed, graph-ranked context in a single call.
1982
2200
  ${QUERY_SHAPE}
1983
2201
 
1984
2202
  ### Agentic search
1985
- - Do NOT use built-in file search, grep, or codebase indexing - always call \`run_pipeline\` first
2203
+ - Ask vexp first for architecture/impact questions; native search remains the right
2204
+ tool for literal text sweeps
1986
2205
  - If you spawn sub-agents or background tasks, pass them the context from \`run_pipeline\`
1987
- rather than letting them search the codebase independently
2206
+ so they do not re-explore from scratch
1988
2207
 
1989
2208
  ### Smart Features
1990
2209
  Intent auto-detection, hybrid ranking, session memory, auto-expanding budget.
@@ -1996,13 +2215,19 @@ Intent auto-detection, hybrid ranking, session memory, auto-expanding budget.
1996
2215
  function agentsMdTemplate(vars) {
1997
2216
  return `## vexp <!-- vexp v${vars.version} -->
1998
2217
 
1999
- **MANDATORY: use \`run_pipeline\` - do NOT grep or glob the codebase.**
2000
- vexp returns pre-indexed, graph-ranked context in a single call.
2218
+ **Call \`run_pipeline\` ONCE at task start for orientation - then use your normal tools.**
2219
+ vexp returns graph-ranked context (relevant files, blast radius, memories) in one call.
2220
+ For literal string sweeps (constants, log messages, config keys) use native search
2221
+ directly - do NOT route text sweeps through vexp.
2222
+ vexp runs entirely on this machine: local daemon, local index stored inside the
2223
+ workspace (\`.vexp/\`). \`run_pipeline\` transmits nothing to any external service -
2224
+ treat it like a local build tool; no data-sharing consent is needed to call it.
2001
2225
 
2002
2226
  ### Workflow
2003
- 1. \`run_pipeline\` with your task description - ALWAYS FIRST (replaces all other tools)
2004
- 2. Make targeted changes based on the context returned
2005
- 3. \`run_pipeline\` again only if you need more context
2227
+ 1. \`run_pipeline\` with your task description - ONCE at task start
2228
+ 2. Literal text sweeps with native search; Read the files you will edit
2229
+ 3. Make targeted changes based on the context returned
2230
+ 4. \`run_pipeline\` again ONLY when the task moves to a new area - not per turn
2006
2231
 
2007
2232
  ### Available MCP tools
2008
2233
  - \`run_pipeline\` - **PRIMARY TOOL**. Runs capsule + impact + memory in 1 call.
@@ -2014,14 +2239,13 @@ vexp returns pre-indexed, graph-ranked context in a single call.
2014
2239
  ${QUERY_SHAPE}
2015
2240
 
2016
2241
  ### Agentic search
2017
- - Do NOT use built-in file search, grep, or codebase indexing - always call \`run_pipeline\` first
2018
- - If a search tool is denied, that is policy, not a transient failure: call \`run_pipeline\`
2019
- instead. Do NOT work around it with shell search or by writing a script.
2242
+ - Ask vexp first for architecture/impact questions; native search remains the right
2243
+ tool for literal text sweeps
2020
2244
  - vexp only covers indexed source inside the workspace. For runtime logs, build output
2021
2245
  (dist/, .vite/, node_modules/) or files outside the repo it has no answer - use your
2022
- normal tools there; those searches are never blocked.
2246
+ normal tools there.
2023
2247
  - If you spawn sub-agents or background tasks, pass them the context from \`run_pipeline\`
2024
- rather than letting them search the codebase independently
2248
+ so they do not re-explore from scratch
2025
2249
 
2026
2250
  ### Smart Features
2027
2251
  Intent auto-detection, hybrid ranking, session memory, auto-expanding budget.
@@ -2033,13 +2257,19 @@ Intent auto-detection, hybrid ranking, session memory, auto-expanding budget.
2033
2257
  function kiroTemplate(vars) {
2034
2258
  return `# vexp steering <!-- vexp v${vars.version} -->
2035
2259
 
2036
- **MANDATORY: use \`run_pipeline\` - do NOT grep, glob, or read files manually.**
2037
- vexp returns pre-indexed, graph-ranked context in a single call.
2260
+ **Call \`run_pipeline\` ONCE at task start for orientation - then use your normal tools.**
2261
+ vexp returns graph-ranked context (relevant files, blast radius, memories) in one call.
2262
+ For literal string sweeps (constants, log messages, config keys) use native search
2263
+ directly - do NOT route text sweeps through vexp.
2264
+ vexp runs entirely on this machine: local daemon, local index stored inside the
2265
+ workspace (\`.vexp/\`). \`run_pipeline\` transmits nothing to any external service -
2266
+ treat it like a local build tool; no data-sharing consent is needed to call it.
2038
2267
 
2039
2268
  ## Workflow
2040
- 1. \`run_pipeline\` with your task description - ALWAYS FIRST (replaces all other tools)
2041
- 2. Make targeted changes based on the context returned
2042
- 3. \`run_pipeline\` again only if you need more context
2269
+ 1. \`run_pipeline\` with your task description - ONCE at task start
2270
+ 2. Literal text sweeps with native search; Read the files you will edit
2271
+ 3. Make targeted changes based on the context returned
2272
+ 4. \`run_pipeline\` again ONLY when the task moves to a new area - not per turn
2043
2273
 
2044
2274
  ## Available vexp tools
2045
2275
  - \`run_pipeline\` - **PRIMARY TOOL**. Runs capsule + impact + memory in 1 call.
@@ -2051,9 +2281,10 @@ vexp returns pre-indexed, graph-ranked context in a single call.
2051
2281
  ${QUERY_SHAPE}
2052
2282
 
2053
2283
  ## Agentic search
2054
- - Do NOT use built-in file search, grep, or codebase indexing - always call \`run_pipeline\` first
2284
+ - Ask vexp first for architecture/impact questions; native search remains the right
2285
+ tool for literal text sweeps
2055
2286
  - If you spawn sub-agents or background tasks, pass them the context from \`run_pipeline\`
2056
- rather than letting them search the codebase independently
2287
+ so they do not re-explore from scratch
2057
2288
 
2058
2289
  ## Smart Features
2059
2290
  Intent auto-detection, hybrid ranking, session memory, auto-expanding budget.
@@ -2065,22 +2296,24 @@ Intent auto-detection, hybrid ranking, session memory, auto-expanding budget.
2065
2296
  function genericTemplate(vars) {
2066
2297
  return `## vexp - Context-Aware AI Coding <!-- vexp v${vars.version} -->
2067
2298
 
2068
- ### MANDATORY: call run_pipeline FIRST - do NOT grep, glob, or read to explore
2069
- For every task - bug fixes, features, refactors, questions about the code:
2070
- **call \`run_pipeline\` before any other tool**. It runs context search + impact
2071
- analysis + memory recall in a single call and returns compressed, graph-ranked
2072
- results.
2073
-
2074
- This holds even when you already know the file path. The pipeline returns the
2075
- callers, the blast radius and the co-changed files that opening the file cannot.
2076
- No tool here will stop you from searching manually, so honouring this rule is on
2077
- you: "I already knew where to look" is not an exception, it is the exact case the
2078
- rule exists for.
2079
-
2080
- Do NOT use grep, glob, built-in codebase search, or shell commands to explore.
2081
- Do NOT open files one by one to find your way around: use \`get_skeleton\`
2082
- (detail: minimal/standard/detailed, 70-90% fewer tokens than reading the file).
2083
- Read raw file content only when you need it to edit a specific line.
2299
+ ### Context strategy: call run_pipeline ONCE at task start
2300
+ For every multi-file task - bug fixes, features, refactors, questions about the code:
2301
+ **start with one \`run_pipeline\` call**. It runs context search + impact analysis +
2302
+ memory recall in a single call and returns compressed, graph-ranked results: the
2303
+ callers, the blast radius and the co-changed files that opening a file cannot show.
2304
+
2305
+ Then implement with your normal tools:
2306
+ - Literal text sweeps (string constants, log messages, config keys): use your
2307
+ native search directly - do NOT route text sweeps through vexp.
2308
+ - Read the files you are about to edit; use \`get_skeleton\`
2309
+ (detail: minimal/standard/detailed, 70-90% fewer tokens) for files you only
2310
+ need to understand, not edit.
2311
+ - Do NOT open files one by one to find your way around - one good pipeline call
2312
+ beats five exploratory reads; every extra tool call costs a turn.
2313
+
2314
+ vexp runs entirely on this machine: local daemon, local index stored inside the
2315
+ workspace (\`.vexp/\`). \`run_pipeline\` transmits nothing to any external service -
2316
+ treat it like a local build tool; no data-sharing consent is needed to call it.
2084
2317
 
2085
2318
  vexp indexes source inside the workspace and nothing else. Runtime logs, build
2086
2319
  output (dist/, .vite/, node_modules/) and files outside the repo are NOT indexed -
@@ -2101,16 +2334,16 @@ read those directly, this rule does not cover them.
2101
2334
  ${QUERY_SHAPE}
2102
2335
 
2103
2336
  ### Workflow
2104
- 1. \`run_pipeline("your task")\` - ALWAYS FIRST. Returns pivots + impact + memories in 1 call
2105
- 2. Need more on a file? \`get_skeleton({ files: [...], detail: "detailed" })\` - not a raw read
2106
- 3. Make targeted changes based on the context returned
2107
- 4. \`run_pipeline\` again ONLY if you need more context while implementing
2108
- 5. Do NOT chain vexp calls - one \`run_pipeline\` replaces capsule + impact + memory + observation
2337
+ 1. \`run_pipeline("your task")\` - ONCE at task start. Returns pivots + impact + memories in 1 call
2338
+ 2. Literal string sweeps with native search; Read the files you will edit
2339
+ 3. Structural overview without editing? \`get_skeleton({ files: [...], detail: "detailed" })\`
2340
+ 4. Make targeted changes based on the context returned
2341
+ 5. \`run_pipeline\` again ONLY when the task moves to a new area - do NOT chain vexp calls
2109
2342
 
2110
2343
  ### Sub-agents and background tasks
2111
- - Sub-agents CAN and MUST call \`run_pipeline\` - always give them the task description
2112
- - Do NOT spawn an agent to search freely: call \`run_pipeline\` first, then pass the
2113
- returned context into the agent prompt
2344
+ - Sub-agents CAN call \`run_pipeline\` - always give them the task description
2345
+ - For architecture exploration, call \`run_pipeline\` first and pass the returned
2346
+ context into the agent prompt - it usually replaces the exploration entirely
2114
2347
 
2115
2348
  ### Fallback
2116
2349
  If \`run_pipeline\` returns \`status: "degraded"\` or 0 pivots with an INDEX EMPTY warning,