vexp-cli 2.2.4 → 2.3.1

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.
@@ -1487,61 +1613,58 @@ export function installClaudeCodeHook(workspaceRoot) {
1487
1613
  const hookDir = path.join(workspaceRoot, ".claude", "hooks");
1488
1614
  const hookPath = path.join(hookDir, "vexp-guard.sh");
1489
1615
  const settingsPath = path.join(workspaceRoot, ".claude", "settings.json");
1490
- // 1. Write hook script
1616
+ // 1. Write hook script. An identical script must NOT short-circuit the
1617
+ // settings merge below: the 2.3.0→2.3.1 fix changed only the settings
1618
+ // ENTRY (shell form → exec form), so skipping on identical script would
1619
+ // leave every existing install on the broken word-splitting entry forever.
1491
1620
  fs.mkdirSync(hookDir, { recursive: true });
1492
1621
  const existed = fs.existsSync(hookPath);
1493
- if (existed) {
1494
- const current = fs.readFileSync(hookPath, "utf-8");
1495
- if (current === VEXP_GUARD_HOOK)
1496
- return null; // identical - skip
1622
+ const scriptIdentical = existed && fs.readFileSync(hookPath, "utf-8") === VEXP_GUARD_HOOK;
1623
+ if (!scriptIdentical) {
1624
+ fs.writeFileSync(hookPath, VEXP_GUARD_HOOK, { mode: 0o755 });
1497
1625
  }
1498
- fs.writeFileSync(hookPath, VEXP_GUARD_HOOK, { mode: 0o755 });
1499
1626
  // 2. Merge hook config into .claude/settings.json
1500
1627
  const read = readJsonConfigSafe(settingsPath);
1501
1628
  if (!read.ok) {
1502
1629
  warnUnparseable(settingsPath);
1503
- return existed ? "updated" : "created";
1630
+ return scriptIdentical ? null : existed ? "updated" : "created";
1504
1631
  }
1505
1632
  const settings = read.data;
1506
1633
  const hooks = (settings.hooks ?? {});
1507
1634
  const existingPreToolUse = Array.isArray(hooks.PreToolUse) ? hooks.PreToolUse : [];
1508
1635
  // 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
- });
1636
+ const filtered = existingPreToolUse.filter((h) => !isVexpGuardHookEntry(h));
1637
+ // Exec form (`args` present → direct spawn, no `sh -c`): the old shell-form
1638
+ // `$CLAUDE_PROJECT_DIR/...` word-split on project paths containing spaces,
1639
+ // so sh tried to exec the path's first fragment and the guard NEVER ran —
1640
+ // non-blocking failure, every search went through unguarded. In exec form
1641
+ // Claude Code substitutes `${CLAUDE_PROJECT_DIR}` itself (brace form
1642
+ // required) before spawning. `timeout` is SECONDS (default 600), not ms:
1643
+ // 3000 configured a 50-minute hook timeout.
1531
1644
  filtered.push({
1532
1645
  matcher: "Grep|Glob|Regex",
1533
1646
  hooks: [
1534
1647
  {
1535
1648
  type: "command",
1536
- command: "$CLAUDE_PROJECT_DIR/.claude/hooks/vexp-guard.sh",
1537
- timeout: 3000,
1649
+ command: "${CLAUDE_PROJECT_DIR}/.claude/hooks/vexp-guard.sh",
1650
+ args: [],
1651
+ timeout: 5,
1538
1652
  },
1539
1653
  ],
1540
1654
  });
1541
- settings.hooks = { ...hooks, PreToolUse: filtered };
1542
- if (read.existed)
1543
- backupConfig(settingsPath);
1544
- fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
1655
+ // Idempotence check AFTER the merge is computed: skip the write (and the
1656
+ // backup) only when settings already contain exactly the entry we would
1657
+ // write AND the script was already current.
1658
+ const merged = { ...hooks, PreToolUse: filtered };
1659
+ const settingsIdentical = JSON.stringify(merged) === JSON.stringify(settings.hooks ?? {});
1660
+ if (scriptIdentical && settingsIdentical)
1661
+ return null;
1662
+ settings.hooks = merged;
1663
+ if (!settingsIdentical) {
1664
+ if (read.existed)
1665
+ backupConfig(settingsPath);
1666
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
1667
+ }
1545
1668
  return existed ? "updated" : "created";
1546
1669
  }
1547
1670
  // ---------------------------------------------------------------------------
@@ -1576,6 +1699,25 @@ function writeGuardScript(scriptPath, content) {
1576
1699
  export function installOpencodePlugin(workspaceRoot) {
1577
1700
  return writeGuardScript(path.join(workspaceRoot, ".opencode", "plugins", "vexp-guard.js"), VEXP_OPENCODE_GUARD);
1578
1701
  }
1702
+ /** Delete a guard script if present. Returns true when a file was removed. */
1703
+ function removeGuardScript(scriptPath) {
1704
+ if (!fs.existsSync(scriptPath))
1705
+ return false;
1706
+ try {
1707
+ fs.unlinkSync(scriptPath);
1708
+ return true;
1709
+ }
1710
+ catch {
1711
+ return false;
1712
+ }
1713
+ }
1714
+ /** Remove the opencode guard plugin (2.3 A2 default). Both directory
1715
+ * spellings are checked — older builds wrote `.opencode/plugin/`. */
1716
+ export function uninstallOpencodePlugin(workspaceRoot) {
1717
+ const a = removeGuardScript(path.join(workspaceRoot, ".opencode", "plugins", "vexp-guard.js"));
1718
+ const b = removeGuardScript(path.join(workspaceRoot, ".opencode", "plugin", "vexp-guard.js"));
1719
+ return a || b;
1720
+ }
1579
1721
  /**
1580
1722
  * Cursor enforcement — `preToolUse` hook with matcher `Grep`.
1581
1723
  *
@@ -1682,6 +1824,59 @@ export function installKiloPlugin(workspaceRoot) {
1682
1824
  fs.writeFileSync(target, JSON.stringify(cfg, null, 2), "utf-8");
1683
1825
  return { plugin, registered: true };
1684
1826
  }
1827
+ /** Remove the Kilo guard plugin (2.3 A2 default): delete the script and drop
1828
+ * the explicit `plugin` array entry from a root kilo.jsonc when present. */
1829
+ export function uninstallKiloPlugin(workspaceRoot) {
1830
+ const pluginPath = path.join(workspaceRoot, ".kilo", "plugins", "vexp-guard.js");
1831
+ let removed = removeGuardScript(pluginPath);
1832
+ const target = kiloConfigTarget(workspaceRoot);
1833
+ const read = readJsonConfigSafe(target);
1834
+ if (read.ok && read.existed) {
1835
+ const cfg = read.data;
1836
+ const list = Array.isArray(cfg.plugin) ? cfg.plugin : [];
1837
+ const filtered = list.filter((e) => typeof e !== "string" || !e.includes("vexp-guard"));
1838
+ if (filtered.length !== list.length) {
1839
+ if (filtered.length > 0)
1840
+ cfg.plugin = filtered;
1841
+ else
1842
+ delete cfg.plugin;
1843
+ backupConfig(target);
1844
+ fs.writeFileSync(target, JSON.stringify(cfg, null, 2), "utf-8");
1845
+ removed = true;
1846
+ }
1847
+ }
1848
+ return removed;
1849
+ }
1850
+ /** Remove the Cursor guard (2.3 A2 default): delete the hook script and drop
1851
+ * our preToolUse entry from .cursor/hooks.json. */
1852
+ export function uninstallCursorHook(workspaceRoot) {
1853
+ const rel = path.join(".cursor", "hooks", "vexp-guard.js");
1854
+ let removed = removeGuardScript(path.join(workspaceRoot, rel));
1855
+ const cfgPath = path.join(workspaceRoot, ".cursor", "hooks.json");
1856
+ const read = readJsonConfigSafe(cfgPath);
1857
+ if (read.ok && read.existed) {
1858
+ const cfg = read.data;
1859
+ const hooks = cfg.hooks ?? {};
1860
+ const preToolUse = Array.isArray(hooks.preToolUse) ? hooks.preToolUse : [];
1861
+ const filtered = preToolUse.filter((e) => {
1862
+ if (!e || typeof e !== "object")
1863
+ return true;
1864
+ const cmd = e.command;
1865
+ return typeof cmd !== "string" || !cmd.includes("vexp-guard");
1866
+ });
1867
+ if (filtered.length !== preToolUse.length) {
1868
+ if (filtered.length > 0)
1869
+ hooks.preToolUse = filtered;
1870
+ else
1871
+ delete hooks.preToolUse;
1872
+ cfg.hooks = hooks;
1873
+ backupConfig(cfgPath);
1874
+ fs.writeFileSync(cfgPath, JSON.stringify(cfg, null, 2), "utf-8");
1875
+ removed = true;
1876
+ }
1877
+ }
1878
+ return removed;
1879
+ }
1685
1880
  /**
1686
1881
  * The single highest-leverage instruction we ship, and for a long time the one
1687
1882
  * most agents never saw.
@@ -1735,22 +1930,30 @@ function generateAgentConfig(template, vars) {
1735
1930
  function claudeCodeTemplate(vars) {
1736
1931
  return `## vexp - Context-Aware AI Coding <!-- vexp v${vars.version} -->
1737
1932
 
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.
1933
+ ### Context strategy: one pipeline call up front, then your normal tools
1934
+ For every multi-file task - bug fixes, features, refactors, debugging:
1935
+ **call \`run_pipeline\` ONCE at the start**. It executes context search + impact
1936
+ analysis + memory recall in a single call: which files matter, what breaks,
1937
+ what was learned before. Then implement with your normal tools.
1938
+
1939
+ Division of labor (each tool where it wins):
1940
+ - **Architecture, impact, "where is X wired"**: \`run_pipeline\` - the graph
1941
+ answers these better and cheaper than exploratory reading.
1942
+ - **Literal text sweeps** (string constants, log messages, config keys, e.g.
1943
+ \`localStorage\`, API paths): Grep/Glob directly - text search is what they
1944
+ are best at, do NOT route text sweeps through vexp.
1945
+ - **Editing**: Read the files you are about to modify; use \`get_skeleton\`
1946
+ (detail: minimal/standard/detailed, 70-90% token savings) for files you only
1947
+ need to understand, not edit.
1948
+
1949
+ Every extra tool call costs a turn: one good \`run_pipeline\` call beats five
1950
+ small ones. Each result opens with a coverage header (index: N files, M nodes) -
1951
+ if it does not match this repo, run \`vexp index\`.
1952
+ vexp only covers indexed source inside the workspace: for runtime logs, build
1953
+ output (dist/, .vite/, node_modules/) or files outside the repo use Bash/Read.
1954
+ vexp runs entirely on this machine: local daemon, local index stored inside the
1955
+ workspace (\`.vexp/\`). \`run_pipeline\` transmits nothing to any external service -
1956
+ treat it like a local build tool; no data-sharing consent is needed to call it.
1754
1957
 
1755
1958
  ### Primary Tool
1756
1959
  - \`run_pipeline\` - **USE THIS FOR EVERYTHING**. Single call that runs
@@ -1770,18 +1973,17 @@ no answer - use Bash/Read there, those are never blocked.
1770
1973
  ${QUERY_SHAPE}
1771
1974
 
1772
1975
  ### 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
1976
+ 1. \`run_pipeline("your task")\` - ONCE at task start. Returns pivots + impact + memories in 1 call
1977
+ 2. Literal string sweeps? Grep/Glob directly. Editing a file? Read it directly.
1978
+ 3. Structural overview of a non-edit file? \`get_skeleton({ files: [...], detail: "detailed" })\`
1979
+ 4. Make targeted changes based on the context returned
1980
+ 5. \`run_pipeline\` again ONLY when the task moves to a new area - do NOT chain vexp calls per turn
1778
1981
 
1779
1982
  ### 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
1983
+ - Subagents CAN call \`run_pipeline\` - always include the task description
1984
+ - Before spawning Agent(Explore) for architecture questions, call \`run_pipeline\`
1985
+ and pass the returned context into the agent prompt - it usually replaces the
1986
+ exploration entirely
1785
1987
 
1786
1988
  ### Smart Features (automatic - no action needed)
1787
1989
  - **Intent Detection**: auto-detects from your task keywords. "fix bug" -> Debug, "refactor" -> blast-radius, "add" -> Modify
@@ -1809,13 +2011,19 @@ Use \`index_status\` to discover available repo aliases.
1809
2011
  function cursorTemplate(vars) {
1810
2012
  return `## vexp rules for Cursor <!-- vexp v${vars.version} -->
1811
2013
 
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.
2014
+ **Call \`run_pipeline\` ONCE at task start for orientation - then use your normal tools.**
2015
+ vexp returns graph-ranked context (relevant files, blast radius, memories) in one call.
2016
+ For literal string sweeps (constants, log messages, config keys) use native search
2017
+ directly - do NOT route text sweeps through vexp.
2018
+ vexp runs entirely on this machine: local daemon, local index stored inside the
2019
+ workspace (\`.vexp/\`). \`run_pipeline\` transmits nothing to any external service -
2020
+ treat it like a local build tool; no data-sharing consent is needed to call it.
1814
2021
 
1815
2022
  ### 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
2023
+ 1. \`run_pipeline\` with your task description - ONCE at task start
2024
+ 2. Literal text sweeps with native search; Read the files you will edit
2025
+ 3. Make targeted changes based on the context returned
2026
+ 4. \`run_pipeline\` again ONLY when the task moves to a new area - not per turn
1819
2027
 
1820
2028
  ### Available MCP tools
1821
2029
  - \`run_pipeline\` - **PRIMARY TOOL**. Runs capsule + impact + memory in 1 call.
@@ -1827,9 +2035,10 @@ vexp returns pre-indexed, graph-ranked context in a single call.
1827
2035
  ${QUERY_SHAPE}
1828
2036
 
1829
2037
  ### Agentic search
1830
- - Do NOT use built-in file search, grep, or codebase indexing - always call \`run_pipeline\` first
2038
+ - Ask vexp first for architecture/impact questions; native search remains the right
2039
+ tool for literal text sweeps
1831
2040
  - If you spawn sub-agents or background tasks, pass them the context from \`run_pipeline\`
1832
- rather than letting them search the codebase independently
2041
+ so they do not re-explore from scratch
1833
2042
 
1834
2043
  ### Tips
1835
2044
  - Add \`include_tests: true\` when debugging
@@ -1849,13 +2058,19 @@ Use file search and read tools directly until the index is ready.
1849
2058
  function windsurfTemplate(vars) {
1850
2059
  return `## vexp for Windsurf <!-- vexp v${vars.version} -->
1851
2060
 
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.
2061
+ **Call \`run_pipeline\` ONCE at task start for orientation - then use your normal tools.**
2062
+ vexp returns graph-ranked context (relevant files, blast radius, memories) in one call.
2063
+ For literal string sweeps (constants, log messages, config keys) use native search
2064
+ directly - do NOT route text sweeps through vexp.
2065
+ vexp runs entirely on this machine: local daemon, local index stored inside the
2066
+ workspace (\`.vexp/\`). \`run_pipeline\` transmits nothing to any external service -
2067
+ treat it like a local build tool; no data-sharing consent is needed to call it.
1854
2068
 
1855
2069
  ### 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
2070
+ 1. \`run_pipeline\` with your task description - ONCE at task start
2071
+ 2. Literal text sweeps with native search; Read the files you will edit
2072
+ 3. Make targeted changes based on the context returned
2073
+ 4. \`run_pipeline\` again ONLY when the task moves to a new area - not per turn
1859
2074
 
1860
2075
  ### Available MCP tools
1861
2076
  - \`run_pipeline\` - **PRIMARY TOOL**. Runs capsule + impact + memory in 1 call.
@@ -1867,9 +2082,10 @@ vexp returns pre-indexed, graph-ranked context in a single call.
1867
2082
  ${QUERY_SHAPE}
1868
2083
 
1869
2084
  ### Agentic search
1870
- - Do NOT use built-in file search, grep, or codebase indexing - always call \`run_pipeline\` first
2085
+ - Ask vexp first for architecture/impact questions; native search remains the right
2086
+ tool for literal text sweeps
1871
2087
  - If you spawn sub-agents or background tasks, pass them the context from \`run_pipeline\`
1872
- rather than letting them search the codebase independently
2088
+ so they do not re-explore from scratch
1873
2089
 
1874
2090
  ### Smart Features
1875
2091
  Intent auto-detection, hybrid ranking, session memory, auto-expanding budget.
@@ -1900,13 +2116,19 @@ function continueTemplate(vars) {
1900
2116
  function augmentTemplate(vars) {
1901
2117
  return `## vexp for Augment <!-- vexp v${vars.version} -->
1902
2118
 
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.
2119
+ **Call \`run_pipeline\` ONCE at task start for orientation - then use your normal tools.**
2120
+ vexp returns graph-ranked context (relevant files, blast radius, memories) in one call.
2121
+ For literal string sweeps (constants, log messages, config keys) use native search
2122
+ directly - do NOT route text sweeps through vexp.
2123
+ vexp runs entirely on this machine: local daemon, local index stored inside the
2124
+ workspace (\`.vexp/\`). \`run_pipeline\` transmits nothing to any external service -
2125
+ treat it like a local build tool; no data-sharing consent is needed to call it.
1905
2126
 
1906
2127
  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
2128
+ 1. \`run_pipeline\` with task description - ONCE at task start
2129
+ 2. Literal text sweeps with native search; Read the files you will edit
2130
+ 3. Make targeted changes based on the context returned
2131
+ 4. \`run_pipeline\` again ONLY when the task moves to a new area
1910
2132
 
1911
2133
  ### Available MCP tools
1912
2134
  - \`run_pipeline\` - **PRIMARY TOOL**. Runs capsule + impact + memory in 1 call.
@@ -1918,9 +2140,10 @@ When working on this codebase:
1918
2140
  ${QUERY_SHAPE}
1919
2141
 
1920
2142
  ### Agentic search
1921
- - Do NOT use built-in file search, grep, or codebase indexing - always call \`run_pipeline\` first
2143
+ - Ask vexp first for architecture/impact questions; native search remains the right
2144
+ tool for literal text sweeps
1922
2145
  - If you spawn sub-agents or background tasks, pass them the context from \`run_pipeline\`
1923
- rather than letting them search the codebase independently
2146
+ so they do not re-explore from scratch
1924
2147
 
1925
2148
  ### Smart Features
1926
2149
  Intent auto-detection, hybrid ranking, session memory, auto-expanding budget.
@@ -1932,13 +2155,19 @@ Intent auto-detection, hybrid ranking, session memory, auto-expanding budget.
1932
2155
  function copilotTemplate(vars) {
1933
2156
  return `## vexp context tools <!-- vexp v${vars.version} -->
1934
2157
 
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.
2158
+ **Call \`run_pipeline\` ONCE at task start for orientation - then use your normal tools.**
2159
+ vexp returns graph-ranked context (relevant files, blast radius, memories) in one call.
2160
+ For literal string sweeps (constants, log messages, config keys) use native search
2161
+ directly - do NOT route text sweeps through vexp.
2162
+ vexp runs entirely on this machine: local daemon, local index stored inside the
2163
+ workspace (\`.vexp/\`). \`run_pipeline\` transmits nothing to any external service -
2164
+ treat it like a local build tool; no data-sharing consent is needed to call it.
1937
2165
 
1938
2166
  ### 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
2167
+ 1. \`run_pipeline\` with your task description - ONCE at task start
2168
+ 2. Literal text sweeps with native search; Read the files you will edit
2169
+ 3. Make targeted changes based on the context returned
2170
+ 4. \`run_pipeline\` again ONLY when the task moves to a new area - not per turn
1942
2171
 
1943
2172
  ### Available MCP tools
1944
2173
  - \`run_pipeline\` - **PRIMARY TOOL**. Runs capsule + impact + memory in 1 call.
@@ -1950,9 +2179,10 @@ vexp returns pre-indexed, graph-ranked context in a single call.
1950
2179
  ${QUERY_SHAPE}
1951
2180
 
1952
2181
  ### Agentic search
1953
- - Do NOT use built-in file search, grep, or codebase indexing - always call \`run_pipeline\` first
2182
+ - Ask vexp first for architecture/impact questions; native search remains the right
2183
+ tool for literal text sweeps
1954
2184
  - If you spawn sub-agents or background tasks, pass them the context from \`run_pipeline\`
1955
- rather than letting them search the codebase independently
2185
+ so they do not re-explore from scratch
1956
2186
 
1957
2187
  ### Smart Features
1958
2188
  Intent auto-detection, hybrid ranking, session memory, auto-expanding budget.
@@ -1964,13 +2194,19 @@ Intent auto-detection, hybrid ranking, session memory, auto-expanding budget.
1964
2194
  function zedTemplate(vars) {
1965
2195
  return `## vexp for Zed <!-- vexp v${vars.version} -->
1966
2196
 
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.
2197
+ **Call \`run_pipeline\` ONCE at task start for orientation - then use your normal tools.**
2198
+ vexp returns graph-ranked context (relevant files, blast radius, memories) in one call.
2199
+ For literal string sweeps (constants, log messages, config keys) use native search
2200
+ directly - do NOT route text sweeps through vexp.
2201
+ vexp runs entirely on this machine: local daemon, local index stored inside the
2202
+ workspace (\`.vexp/\`). \`run_pipeline\` transmits nothing to any external service -
2203
+ treat it like a local build tool; no data-sharing consent is needed to call it.
1969
2204
 
1970
2205
  ### 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
2206
+ 1. \`run_pipeline\` with your task description - ONCE at task start
2207
+ 2. Literal text sweeps with native search; Read the files you will edit
2208
+ 3. Make targeted changes based on the context returned
2209
+ 4. \`run_pipeline\` again ONLY when the task moves to a new area - not per turn
1974
2210
 
1975
2211
  ### Available MCP tools
1976
2212
  - \`run_pipeline\` - **PRIMARY TOOL**. Runs capsule + impact + memory in 1 call.
@@ -1982,9 +2218,10 @@ vexp returns pre-indexed, graph-ranked context in a single call.
1982
2218
  ${QUERY_SHAPE}
1983
2219
 
1984
2220
  ### Agentic search
1985
- - Do NOT use built-in file search, grep, or codebase indexing - always call \`run_pipeline\` first
2221
+ - Ask vexp first for architecture/impact questions; native search remains the right
2222
+ tool for literal text sweeps
1986
2223
  - If you spawn sub-agents or background tasks, pass them the context from \`run_pipeline\`
1987
- rather than letting them search the codebase independently
2224
+ so they do not re-explore from scratch
1988
2225
 
1989
2226
  ### Smart Features
1990
2227
  Intent auto-detection, hybrid ranking, session memory, auto-expanding budget.
@@ -1996,13 +2233,19 @@ Intent auto-detection, hybrid ranking, session memory, auto-expanding budget.
1996
2233
  function agentsMdTemplate(vars) {
1997
2234
  return `## vexp <!-- vexp v${vars.version} -->
1998
2235
 
1999
- **MANDATORY: use \`run_pipeline\` - do NOT grep or glob the codebase.**
2000
- vexp returns pre-indexed, graph-ranked context in a single call.
2236
+ **Call \`run_pipeline\` ONCE at task start for orientation - then use your normal tools.**
2237
+ vexp returns graph-ranked context (relevant files, blast radius, memories) in one call.
2238
+ For literal string sweeps (constants, log messages, config keys) use native search
2239
+ directly - do NOT route text sweeps through vexp.
2240
+ vexp runs entirely on this machine: local daemon, local index stored inside the
2241
+ workspace (\`.vexp/\`). \`run_pipeline\` transmits nothing to any external service -
2242
+ treat it like a local build tool; no data-sharing consent is needed to call it.
2001
2243
 
2002
2244
  ### 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
2245
+ 1. \`run_pipeline\` with your task description - ONCE at task start
2246
+ 2. Literal text sweeps with native search; Read the files you will edit
2247
+ 3. Make targeted changes based on the context returned
2248
+ 4. \`run_pipeline\` again ONLY when the task moves to a new area - not per turn
2006
2249
 
2007
2250
  ### Available MCP tools
2008
2251
  - \`run_pipeline\` - **PRIMARY TOOL**. Runs capsule + impact + memory in 1 call.
@@ -2014,14 +2257,13 @@ vexp returns pre-indexed, graph-ranked context in a single call.
2014
2257
  ${QUERY_SHAPE}
2015
2258
 
2016
2259
  ### 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.
2260
+ - Ask vexp first for architecture/impact questions; native search remains the right
2261
+ tool for literal text sweeps
2020
2262
  - vexp only covers indexed source inside the workspace. For runtime logs, build output
2021
2263
  (dist/, .vite/, node_modules/) or files outside the repo it has no answer - use your
2022
- normal tools there; those searches are never blocked.
2264
+ normal tools there.
2023
2265
  - If you spawn sub-agents or background tasks, pass them the context from \`run_pipeline\`
2024
- rather than letting them search the codebase independently
2266
+ so they do not re-explore from scratch
2025
2267
 
2026
2268
  ### Smart Features
2027
2269
  Intent auto-detection, hybrid ranking, session memory, auto-expanding budget.
@@ -2033,13 +2275,19 @@ Intent auto-detection, hybrid ranking, session memory, auto-expanding budget.
2033
2275
  function kiroTemplate(vars) {
2034
2276
  return `# vexp steering <!-- vexp v${vars.version} -->
2035
2277
 
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.
2278
+ **Call \`run_pipeline\` ONCE at task start for orientation - then use your normal tools.**
2279
+ vexp returns graph-ranked context (relevant files, blast radius, memories) in one call.
2280
+ For literal string sweeps (constants, log messages, config keys) use native search
2281
+ directly - do NOT route text sweeps through vexp.
2282
+ vexp runs entirely on this machine: local daemon, local index stored inside the
2283
+ workspace (\`.vexp/\`). \`run_pipeline\` transmits nothing to any external service -
2284
+ treat it like a local build tool; no data-sharing consent is needed to call it.
2038
2285
 
2039
2286
  ## 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
2287
+ 1. \`run_pipeline\` with your task description - ONCE at task start
2288
+ 2. Literal text sweeps with native search; Read the files you will edit
2289
+ 3. Make targeted changes based on the context returned
2290
+ 4. \`run_pipeline\` again ONLY when the task moves to a new area - not per turn
2043
2291
 
2044
2292
  ## Available vexp tools
2045
2293
  - \`run_pipeline\` - **PRIMARY TOOL**. Runs capsule + impact + memory in 1 call.
@@ -2051,9 +2299,10 @@ vexp returns pre-indexed, graph-ranked context in a single call.
2051
2299
  ${QUERY_SHAPE}
2052
2300
 
2053
2301
  ## Agentic search
2054
- - Do NOT use built-in file search, grep, or codebase indexing - always call \`run_pipeline\` first
2302
+ - Ask vexp first for architecture/impact questions; native search remains the right
2303
+ tool for literal text sweeps
2055
2304
  - If you spawn sub-agents or background tasks, pass them the context from \`run_pipeline\`
2056
- rather than letting them search the codebase independently
2305
+ so they do not re-explore from scratch
2057
2306
 
2058
2307
  ## Smart Features
2059
2308
  Intent auto-detection, hybrid ranking, session memory, auto-expanding budget.
@@ -2065,22 +2314,24 @@ Intent auto-detection, hybrid ranking, session memory, auto-expanding budget.
2065
2314
  function genericTemplate(vars) {
2066
2315
  return `## vexp - Context-Aware AI Coding <!-- vexp v${vars.version} -->
2067
2316
 
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.
2317
+ ### Context strategy: call run_pipeline ONCE at task start
2318
+ For every multi-file task - bug fixes, features, refactors, questions about the code:
2319
+ **start with one \`run_pipeline\` call**. It runs context search + impact analysis +
2320
+ memory recall in a single call and returns compressed, graph-ranked results: the
2321
+ callers, the blast radius and the co-changed files that opening a file cannot show.
2322
+
2323
+ Then implement with your normal tools:
2324
+ - Literal text sweeps (string constants, log messages, config keys): use your
2325
+ native search directly - do NOT route text sweeps through vexp.
2326
+ - Read the files you are about to edit; use \`get_skeleton\`
2327
+ (detail: minimal/standard/detailed, 70-90% fewer tokens) for files you only
2328
+ need to understand, not edit.
2329
+ - Do NOT open files one by one to find your way around - one good pipeline call
2330
+ beats five exploratory reads; every extra tool call costs a turn.
2331
+
2332
+ vexp runs entirely on this machine: local daemon, local index stored inside the
2333
+ workspace (\`.vexp/\`). \`run_pipeline\` transmits nothing to any external service -
2334
+ treat it like a local build tool; no data-sharing consent is needed to call it.
2084
2335
 
2085
2336
  vexp indexes source inside the workspace and nothing else. Runtime logs, build
2086
2337
  output (dist/, .vite/, node_modules/) and files outside the repo are NOT indexed -
@@ -2101,16 +2352,16 @@ read those directly, this rule does not cover them.
2101
2352
  ${QUERY_SHAPE}
2102
2353
 
2103
2354
  ### 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
2355
+ 1. \`run_pipeline("your task")\` - ONCE at task start. Returns pivots + impact + memories in 1 call
2356
+ 2. Literal string sweeps with native search; Read the files you will edit
2357
+ 3. Structural overview without editing? \`get_skeleton({ files: [...], detail: "detailed" })\`
2358
+ 4. Make targeted changes based on the context returned
2359
+ 5. \`run_pipeline\` again ONLY when the task moves to a new area - do NOT chain vexp calls
2109
2360
 
2110
2361
  ### 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
2362
+ - Sub-agents CAN call \`run_pipeline\` - always give them the task description
2363
+ - For architecture exploration, call \`run_pipeline\` first and pass the returned
2364
+ context into the agent prompt - it usually replaces the exploration entirely
2114
2365
 
2115
2366
  ### Fallback
2116
2367
  If \`run_pipeline\` returns \`status: "degraded"\` or 0 pivots with an INDEX EMPTY warning,