vexp-cli 2.3.0 → 2.4.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.
@@ -10,7 +10,7 @@ import * as fs from "fs";
10
10
  import * as path from "path";
11
11
  import * as os from "os";
12
12
  import * as crypto from "crypto";
13
- import { VEXP_GUARD_HOOK, VEXP_OPENCODE_GUARD, VEXP_CURSOR_GUARD } from "./hook-template.js";
13
+ import { VEXP_GUARD_HOOK, VEXP_OPENCODE_GUARD, VEXP_CURSOR_GUARD, vexpHintHookScript, vexpOpencodeHintPlugin } from "./hook-template.js";
14
14
  // ---------------------------------------------------------------------------
15
15
  // Constants
16
16
  // ---------------------------------------------------------------------------
@@ -320,6 +320,18 @@ export function configureAgents(workspaceRoot, binaryPath, version, agentFilter,
320
320
  const wrote = configureClaudeCodeGlobal(binaryPath, mcpServerPath, workspaceRoot);
321
321
  if (wrote)
322
322
  mcpConfigs.push("~/.claude.json");
323
+ // Event-driven hint hook: default-on (non-blocking, fail-open — the
324
+ // 2.3 A2 opt-in applies to DENY hooks, this one cannot block).
325
+ const hintResult = installClaudeCodeHintHook(workspaceRoot, binaryPath);
326
+ if (hintResult) {
327
+ results.push({
328
+ agent: "Claude Code Hint",
329
+ configFile: ".claude/hooks/vexp-hint.sh",
330
+ content: vexpHintHookScript(binaryPath),
331
+ alreadyExists: false,
332
+ action: hintResult,
333
+ });
334
+ }
323
335
  // Guard is OPT-IN from 2.3 (see GuardMode): strict installs the
324
336
  // PreToolUse deny hook; default removes any previously installed one.
325
337
  if (guardMode() === "strict") {
@@ -349,6 +361,7 @@ export function configureAgents(workspaceRoot, binaryPath, version, agentFilter,
349
361
  const wrote = configureCodexGlobal(binaryPath, mcpServerPath, workspaceRoot);
350
362
  if (wrote)
351
363
  mcpConfigs.push("~/.codex/config.toml");
364
+ installCodexHintHook(workspaceRoot, binaryPath);
352
365
  }
353
366
  // Antigravity: machine-global MCP config (~/.gemini/antigravity/mcp_config.json)
354
367
  if (detector.agent === "Antigravity") {
@@ -376,6 +389,7 @@ export function configureAgents(workspaceRoot, binaryPath, version, agentFilter,
376
389
  const wrote = configureKiloMcp(workspaceRoot, binaryPath, mcpServerPath);
377
390
  if (wrote)
378
391
  mcpConfigs.push(wrote);
392
+ installOpencodeHintPlugin(workspaceRoot, binaryPath, ".kilo/plugin");
379
393
  // Kilo v7 vendors opencode, so it takes the same guard plugin — the rules
380
394
  // markdown alone was demonstrably not enough (a reported session loaded
381
395
  // vexp.md, quoted it back, and still read five files by hand).
@@ -437,6 +451,7 @@ export function configureAgents(workspaceRoot, binaryPath, version, agentFilter,
437
451
  // Claude Code PreToolUse hook) — which is only safe once MCP is registered.
438
452
  if (detector.agent === "Opencode") {
439
453
  const wroteMcp = configureOpencodeMcp(workspaceRoot, binaryPath, mcpServerPath);
454
+ installOpencodeHintPlugin(workspaceRoot, binaryPath, ".opencode/plugin");
440
455
  if (wroteMcp)
441
456
  mcpConfigs.push(wroteMcp);
442
457
  if (guardMode() === "strict") {
@@ -607,6 +622,7 @@ export function configureSelectedAgents(workspaceRoot, binaryPath, version, sele
607
622
  const wrote = configureClaudeCodeGlobal(binaryPath, mcpServerPath, workspaceRoot);
608
623
  if (wrote)
609
624
  mcpConfigs.push("~/.claude.json");
625
+ installClaudeCodeHintHook(workspaceRoot, binaryPath);
610
626
  if (guardMode() === "strict")
611
627
  installClaudeCodeHook(workspaceRoot);
612
628
  else
@@ -616,6 +632,7 @@ export function configureSelectedAgents(workspaceRoot, binaryPath, version, sele
616
632
  const wrote = configureCodexGlobal(binaryPath, mcpServerPath, workspaceRoot);
617
633
  if (wrote)
618
634
  mcpConfigs.push("~/.codex/config.toml");
635
+ installCodexHintHook(workspaceRoot, binaryPath);
619
636
  }
620
637
  if (detector.agent === "Antigravity") {
621
638
  const wrote = configureAntigravityGlobal(binaryPath, mcpServerPath);
@@ -636,6 +653,7 @@ export function configureSelectedAgents(workspaceRoot, binaryPath, version, sele
636
653
  const wrote = configureOpencodeMcp(workspaceRoot, binaryPath, mcpServerPath);
637
654
  if (wrote)
638
655
  mcpConfigs.push(wrote);
656
+ installOpencodeHintPlugin(workspaceRoot, binaryPath, ".opencode/plugin");
639
657
  if (guardMode() === "strict")
640
658
  installOpencodePlugin(workspaceRoot);
641
659
  else
@@ -655,6 +673,7 @@ export function configureSelectedAgents(workspaceRoot, binaryPath, version, sele
655
673
  const wrote = configureKiloMcp(workspaceRoot, binaryPath, mcpServerPath);
656
674
  if (wrote)
657
675
  mcpConfigs.push(wrote);
676
+ installOpencodeHintPlugin(workspaceRoot, binaryPath, ".kilo/plugin");
658
677
  if (guardMode() === "strict")
659
678
  installKiloPlugin(workspaceRoot);
660
679
  else
@@ -1613,40 +1632,199 @@ export function installClaudeCodeHook(workspaceRoot) {
1613
1632
  const hookDir = path.join(workspaceRoot, ".claude", "hooks");
1614
1633
  const hookPath = path.join(hookDir, "vexp-guard.sh");
1615
1634
  const settingsPath = path.join(workspaceRoot, ".claude", "settings.json");
1616
- // 1. Write hook script
1635
+ // 1. Write hook script. An identical script must NOT short-circuit the
1636
+ // settings merge below: the 2.3.0→2.3.1 fix changed only the settings
1637
+ // ENTRY (shell form → exec form), so skipping on identical script would
1638
+ // leave every existing install on the broken word-splitting entry forever.
1617
1639
  fs.mkdirSync(hookDir, { recursive: true });
1618
1640
  const existed = fs.existsSync(hookPath);
1619
- if (existed) {
1620
- const current = fs.readFileSync(hookPath, "utf-8");
1621
- if (current === VEXP_GUARD_HOOK)
1622
- return null; // identical - skip
1641
+ const scriptIdentical = existed && fs.readFileSync(hookPath, "utf-8") === VEXP_GUARD_HOOK;
1642
+ if (!scriptIdentical) {
1643
+ fs.writeFileSync(hookPath, VEXP_GUARD_HOOK, { mode: 0o755 });
1623
1644
  }
1624
- fs.writeFileSync(hookPath, VEXP_GUARD_HOOK, { mode: 0o755 });
1625
1645
  // 2. Merge hook config into .claude/settings.json
1626
1646
  const read = readJsonConfigSafe(settingsPath);
1627
1647
  if (!read.ok) {
1628
1648
  warnUnparseable(settingsPath);
1629
- return existed ? "updated" : "created";
1649
+ return scriptIdentical ? null : existed ? "updated" : "created";
1630
1650
  }
1631
1651
  const settings = read.data;
1632
1652
  const hooks = (settings.hooks ?? {});
1633
1653
  const existingPreToolUse = Array.isArray(hooks.PreToolUse) ? hooks.PreToolUse : [];
1634
1654
  // Aggressively remove ALL vexp-related hook entries (old format, stale matchers, malformed)
1635
1655
  const filtered = existingPreToolUse.filter((h) => !isVexpGuardHookEntry(h));
1656
+ // Shell form with an explicit `bash` prefix and a QUOTED path — the one
1657
+ // shape that works on every OS Claude Code supports:
1658
+ // - the quotes fix the historical spaces bug (unquoted
1659
+ // `$CLAUDE_PROJECT_DIR/...` word-split and the guard never ran);
1660
+ // - exec form (`args` present → direct spawn) fixed spaces but broke
1661
+ // Windows entirely: the OS cannot spawn a `.sh` without a shell, the
1662
+ // hook errored, failed open, and enforced nothing (field report) —
1663
+ // Claude Code on Windows runs shell-form hooks through Git Bash, so
1664
+ // `bash "$VAR"` works there and on unix alike.
1665
+ // `timeout` is SECONDS (default 600), not ms: 3000 once configured a
1666
+ // 50-minute hook timeout.
1636
1667
  filtered.push({
1637
1668
  matcher: "Grep|Glob|Regex",
1638
1669
  hooks: [
1639
1670
  {
1640
1671
  type: "command",
1641
- command: "$CLAUDE_PROJECT_DIR/.claude/hooks/vexp-guard.sh",
1642
- timeout: 3000,
1672
+ command: 'bash "$CLAUDE_PROJECT_DIR/.claude/hooks/vexp-guard.sh"',
1673
+ timeout: 5,
1643
1674
  },
1644
1675
  ],
1645
1676
  });
1646
- settings.hooks = { ...hooks, PreToolUse: filtered };
1647
- if (read.existed)
1648
- backupConfig(settingsPath);
1649
- fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
1677
+ // Idempotence check AFTER the merge is computed: skip the write (and the
1678
+ // backup) only when settings already contain exactly the entry we would
1679
+ // write AND the script was already current.
1680
+ const merged = { ...hooks, PreToolUse: filtered };
1681
+ const settingsIdentical = JSON.stringify(merged) === JSON.stringify(settings.hooks ?? {});
1682
+ if (scriptIdentical && settingsIdentical)
1683
+ return null;
1684
+ settings.hooks = merged;
1685
+ if (!settingsIdentical) {
1686
+ if (read.existed)
1687
+ backupConfig(settingsPath);
1688
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
1689
+ }
1690
+ return existed ? "updated" : "created";
1691
+ }
1692
+ function isVexpHintHookEntry(h) {
1693
+ if (!h || typeof h !== "object")
1694
+ return false;
1695
+ const entry = h;
1696
+ const own = entry.command?.includes("vexp-hint");
1697
+ if (own)
1698
+ return true;
1699
+ const hks = entry.hooks;
1700
+ return Array.isArray(hks) && hks.some((hook) => {
1701
+ if (!hook || typeof hook !== "object")
1702
+ return false;
1703
+ return hook.command?.includes("vexp-hint") ?? false;
1704
+ });
1705
+ }
1706
+ /**
1707
+ * Install the event-driven hint hook for Claude Code (2.3.3 default).
1708
+ * Replaces the resident CLAUDE.md strategy text: a UserPromptSubmit hook
1709
+ * asks the daemon to classify each prompt against the index and injects a
1710
+ * one-line run_pipeline suggestion ONLY for unfamiliar-area prompts. On
1711
+ * oriented prompts the injection is empty — zero context cost. Fail-open by
1712
+ * construction (see VEXP_HINT_HOOK): any failure = vanilla behavior.
1713
+ * Non-blocking and additive, so it is NOT gated by guardMode (the 2.3 A2
1714
+ * opt-in applies to DENY hooks, which add a failure mode; this one cannot
1715
+ * block anything).
1716
+ */
1717
+ export function installClaudeCodeHintHook(workspaceRoot, binaryPath) {
1718
+ const hookDir = path.join(workspaceRoot, ".claude", "hooks");
1719
+ const hookPath = path.join(hookDir, "vexp-hint.sh");
1720
+ const settingsPath = path.join(workspaceRoot, ".claude", "settings.json");
1721
+ const script = vexpHintHookScript(binaryPath);
1722
+ fs.mkdirSync(hookDir, { recursive: true });
1723
+ const existed = fs.existsSync(hookPath);
1724
+ const scriptIdentical = existed && fs.readFileSync(hookPath, "utf-8") === script;
1725
+ if (!scriptIdentical) {
1726
+ fs.writeFileSync(hookPath, script, { mode: 0o755 });
1727
+ }
1728
+ const read = readJsonConfigSafe(settingsPath);
1729
+ if (!read.ok) {
1730
+ warnUnparseable(settingsPath);
1731
+ return scriptIdentical ? null : existed ? "updated" : "created";
1732
+ }
1733
+ const settings = read.data;
1734
+ const hooks = (settings.hooks ?? {});
1735
+ const existing = Array.isArray(hooks.UserPromptSubmit)
1736
+ ? hooks.UserPromptSubmit
1737
+ : [];
1738
+ const filtered = existing.filter((h) => !isVexpHintHookEntry(h));
1739
+ // Same cross-OS shape as the guard entry: bash prefix + QUOTED path
1740
+ // (spaces-safe on unix, Git Bash on Windows). timeout is SECONDS.
1741
+ filtered.push({
1742
+ hooks: [
1743
+ {
1744
+ type: "command",
1745
+ command: 'bash "$CLAUDE_PROJECT_DIR/.claude/hooks/vexp-hint.sh"',
1746
+ timeout: 5,
1747
+ },
1748
+ ],
1749
+ });
1750
+ const merged = { ...hooks, UserPromptSubmit: filtered };
1751
+ const settingsIdentical = JSON.stringify(merged) === JSON.stringify(settings.hooks ?? {});
1752
+ if (scriptIdentical && settingsIdentical)
1753
+ return null;
1754
+ settings.hooks = merged;
1755
+ if (!settingsIdentical) {
1756
+ if (read.existed)
1757
+ backupConfig(settingsPath);
1758
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
1759
+ }
1760
+ return existed ? "updated" : "created";
1761
+ }
1762
+ /**
1763
+ * opencode/Kilo hint plugin (2.4.0): default-ON, non-blocking, fail-open —
1764
+ * unlike the deny guard (strict-only), this cannot break a session; it only
1765
+ * ever appends an orientation line the daemon judged useful.
1766
+ */
1767
+ export function installOpencodeHintPlugin(workspaceRoot, binaryPath, pluginDir) {
1768
+ const pluginPath = path.join(workspaceRoot, pluginDir, "vexp-hint.js");
1769
+ const content = vexpOpencodeHintPlugin(binaryPath);
1770
+ fs.mkdirSync(path.dirname(pluginPath), { recursive: true });
1771
+ const existed = fs.existsSync(pluginPath);
1772
+ if (existed && fs.readFileSync(pluginPath, "utf-8") === content)
1773
+ return null;
1774
+ fs.writeFileSync(pluginPath, content);
1775
+ return existed ? "updated" : "created";
1776
+ }
1777
+ /**
1778
+ * Codex hint hook (2.4.0). Codex >=0.129 discovers `<repo>/.codex/hooks.json`
1779
+ * and its UserPromptSubmit stdout envelope is byte-identical to Claude
1780
+ * Code's (hookSpecificOutput.additionalContext), so the same prompt-hint
1781
+ * backend serves both: PROJECT-scope file, no global config touched.
1782
+ * Fail-open: missing bash/binary/daemon => no output => vanilla.
1783
+ */
1784
+ export function installCodexHintHook(workspaceRoot, binaryPath) {
1785
+ const dir = path.join(workspaceRoot, ".codex");
1786
+ const hookPath = path.join(dir, "vexp-hint.sh");
1787
+ const hooksJsonPath = path.join(dir, "hooks.json");
1788
+ const script = vexpHintHookScript(binaryPath);
1789
+ fs.mkdirSync(dir, { recursive: true });
1790
+ const existed = fs.existsSync(hookPath);
1791
+ const scriptIdentical = existed && fs.readFileSync(hookPath, "utf-8") === script;
1792
+ if (!scriptIdentical) {
1793
+ fs.writeFileSync(hookPath, script, { mode: 0o755 });
1794
+ }
1795
+ let root = {};
1796
+ if (fs.existsSync(hooksJsonPath)) {
1797
+ try {
1798
+ root = JSON.parse(fs.readFileSync(hooksJsonPath, "utf-8"));
1799
+ }
1800
+ catch {
1801
+ warnUnparseable(hooksJsonPath);
1802
+ return scriptIdentical ? null : existed ? "updated" : "created";
1803
+ }
1804
+ }
1805
+ // Codex hooks.json shape: EVENT NAME at top level (no "hooks" wrapper).
1806
+ const existing = Array.isArray(root.UserPromptSubmit)
1807
+ ? root.UserPromptSubmit
1808
+ : [];
1809
+ const filtered = existing.filter((h) => !isVexpHintHookEntry(h));
1810
+ // Absolute path: Codex has no $CLAUDE_PROJECT_DIR substitution.
1811
+ filtered.push({
1812
+ hooks: [
1813
+ {
1814
+ type: "command",
1815
+ command: `bash "${hookPath.replace(/\\/g, "/")}"`,
1816
+ timeout: 5,
1817
+ },
1818
+ ],
1819
+ });
1820
+ const before = JSON.stringify(root.UserPromptSubmit ?? null);
1821
+ root.UserPromptSubmit = filtered;
1822
+ const identical = before === JSON.stringify(filtered);
1823
+ if (scriptIdentical && identical)
1824
+ return null;
1825
+ if (!identical) {
1826
+ fs.writeFileSync(hooksJsonPath, JSON.stringify(root, null, 2) + "\n");
1827
+ }
1650
1828
  return existed ? "updated" : "created";
1651
1829
  }
1652
1830
  // ---------------------------------------------------------------------------
@@ -1911,90 +2089,30 @@ function generateAgentConfig(template, vars) {
1911
2089
  // ---------------------------------------------------------------------------
1912
2090
  function claudeCodeTemplate(vars) {
1913
2091
  return `## vexp - Context-Aware AI Coding <!-- vexp v${vars.version} -->
1914
-
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
2092
+ vexp runs entirely on this machine: local daemon, index in \`.vexp/\`.
2093
+ \`run_pipeline\` transmits nothing to any external service.
2094
+ - \`run_pipeline({ "task": "..." })\` - orientation in one call (ranked pivot
2095
+ files with line ranges + blast radius + session notes) when a task does NOT
2096
+ name the files/symbols to touch. If it does, SKIP vexp - use your normal tools.
2097
+ - \`get_skeleton\` - file structure at 70-90% token savings for files you only
1929
2098
  need to understand, not edit.
2099
+ - vexp may append a one-line hint to a prompt when orientation would help;
2100
+ otherwise it stays silent.
1930
2101
 
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.
1939
-
1940
- ### Primary Tool
1941
- - \`run_pipeline\` - **USE THIS FOR EVERYTHING**. Single call that runs
1942
- capsule + impact + memory server-side. Returns compressed results.
1943
- Auto-detects intent (debug/modify/refactor/explore) from your task.
1944
- Includes full file content for pivots.
1945
- Examples:
1946
- - \`run_pipeline({ "task": "fix JWT validation bug" })\` - auto-detect
1947
- - \`run_pipeline({ "task": "refactor db layer", "preset": "refactor" })\` - explicit
1948
- - \`run_pipeline({ "task": "add auth", "observation": "using JWT" })\` - save insight in same call
1949
-
1950
- ### Other MCP tools (use only when run_pipeline is insufficient)
1951
- - \`get_skeleton\` - **preferred over Read** for inspecting files (minimal/standard/detailed detail levels, 70-90% token savings)
1952
- - \`index_status\` - indexing status and health check
1953
- - \`expand_vexp_ref\` - expand V-REF hash placeholders in v2 compact output
1954
-
1955
- ${QUERY_SHAPE}
1956
-
1957
- ### Workflow
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
1963
-
1964
- ### Subagent / Explore / Plan mode
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
1969
-
1970
- ### Smart Features (automatic - no action needed)
1971
- - **Intent Detection**: auto-detects from your task keywords. "fix bug" -> Debug, "refactor" -> blast-radius, "add" -> Modify
1972
- - **Hybrid Search**: keyword + semantic + graph centrality ranking
1973
- - **Session Memory**: auto-captures observations; memories auto-surfaced in results
1974
- - **LSP Bridge**: VS Code captures type-resolved call edges
1975
- - **Change Coupling**: co-changed files included as related context
1976
-
1977
- ### Advanced Parameters
1978
- - \`preset: "debug"\` - forces debug mode (capsule+tests+impact+memory)
1979
- - \`preset: "refactor"\` - deep impact analysis (depth 5)
1980
- - \`max_tokens: 12000\` - increase total budget for complex tasks
1981
- - \`include_tests: true\` - include test files in results
1982
- - \`include_file_content: false\` - omit full file content (lighter response)
1983
-
1984
- ### Fallback
1985
- If \`run_pipeline\` returns \`status: "degraded"\` or 0 pivots with an INDEX EMPTY warning,
1986
- the index is empty or rebuilding. Use Grep, Glob, and Read directly until the index is ready.
1987
-
1988
- ### Multi-Repo Workspaces
1989
- \`run_pipeline\` auto-queries all indexed repos. Use \`repos: ["alias"]\` to scope.
1990
- Use \`index_status\` to discover available repo aliases.
2102
+ ### Query shape (do this)
2103
+ Anchor the task on real identifiers (ClassName, functionName) or file paths:
2104
+ \`run_pipeline({ "task": "fix JWT expiry in AuthService.validateToken" })\`
1991
2105
  <!-- /vexp -->`;
1992
2106
  }
1993
2107
  function cursorTemplate(vars) {
1994
2108
  return `## vexp rules for Cursor <!-- vexp v${vars.version} -->
1995
2109
 
1996
2110
  **Call \`run_pipeline\` ONCE at task start for orientation - then use your normal tools.**
2111
+ If the task already names the files/symbols to touch, skip vexp and work
2112
+ with your normal tools - a skipped call costs nothing.
1997
2113
  vexp returns graph-ranked context (relevant files, blast radius, memories) in one call.
2114
+ On non-trivial tasks pass \`eager: true\`: the single response then carries full file
2115
+ bodies, covering tests and impact - work directly from it instead of re-reading sources.
1998
2116
  For literal string sweeps (constants, log messages, config keys) use native search
1999
2117
  directly - do NOT route text sweeps through vexp.
2000
2118
  vexp runs entirely on this machine: local daemon, local index stored inside the
@@ -2014,6 +2132,10 @@ treat it like a local build tool; no data-sharing consent is needed to call it.
2014
2132
  - \`index_status\` - indexing status
2015
2133
  - \`expand_vexp_ref\` - expand V-REF placeholders in v2 output
2016
2134
 
2135
+ (Recommended core set, not the full schema: paid plans advertise 14 MCP tools -
2136
+ capsule, impact graph, logic flow, memory and more. \`run_pipeline\` already runs
2137
+ those server-side, so the four above cover the normal workflow.)
2138
+
2017
2139
  ${QUERY_SHAPE}
2018
2140
 
2019
2141
  ### Agentic search
@@ -2041,7 +2163,11 @@ function windsurfTemplate(vars) {
2041
2163
  return `## vexp for Windsurf <!-- vexp v${vars.version} -->
2042
2164
 
2043
2165
  **Call \`run_pipeline\` ONCE at task start for orientation - then use your normal tools.**
2166
+ If the task already names the files/symbols to touch, skip vexp and work
2167
+ with your normal tools - a skipped call costs nothing.
2044
2168
  vexp returns graph-ranked context (relevant files, blast radius, memories) in one call.
2169
+ On non-trivial tasks pass \`eager: true\`: the single response then carries full file
2170
+ bodies, covering tests and impact - work directly from it instead of re-reading sources.
2045
2171
  For literal string sweeps (constants, log messages, config keys) use native search
2046
2172
  directly - do NOT route text sweeps through vexp.
2047
2173
  vexp runs entirely on this machine: local daemon, local index stored inside the
@@ -2061,6 +2187,10 @@ treat it like a local build tool; no data-sharing consent is needed to call it.
2061
2187
  - \`index_status\` - indexing status
2062
2188
  - \`expand_vexp_ref\` - expand V-REF placeholders in v2 output
2063
2189
 
2190
+ (Recommended core set, not the full schema: paid plans advertise 14 MCP tools -
2191
+ capsule, impact graph, logic flow, memory and more. \`run_pipeline\` already runs
2192
+ those server-side, so the four above cover the normal workflow.)
2193
+
2064
2194
  ${QUERY_SHAPE}
2065
2195
 
2066
2196
  ### Agentic search
@@ -2099,7 +2229,11 @@ function augmentTemplate(vars) {
2099
2229
  return `## vexp for Augment <!-- vexp v${vars.version} -->
2100
2230
 
2101
2231
  **Call \`run_pipeline\` ONCE at task start for orientation - then use your normal tools.**
2232
+ If the task already names the files/symbols to touch, skip vexp and work
2233
+ with your normal tools - a skipped call costs nothing.
2102
2234
  vexp returns graph-ranked context (relevant files, blast radius, memories) in one call.
2235
+ On non-trivial tasks pass \`eager: true\`: the single response then carries full file
2236
+ bodies, covering tests and impact - work directly from it instead of re-reading sources.
2103
2237
  For literal string sweeps (constants, log messages, config keys) use native search
2104
2238
  directly - do NOT route text sweeps through vexp.
2105
2239
  vexp runs entirely on this machine: local daemon, local index stored inside the
@@ -2119,6 +2253,10 @@ When working on this codebase:
2119
2253
  - \`index_status\` - indexing status
2120
2254
  - \`expand_vexp_ref\` - expand V-REF placeholders in v2 output
2121
2255
 
2256
+ (Recommended core set, not the full schema: paid plans advertise 14 MCP tools -
2257
+ capsule, impact graph, logic flow, memory and more. \`run_pipeline\` already runs
2258
+ those server-side, so the four above cover the normal workflow.)
2259
+
2122
2260
  ${QUERY_SHAPE}
2123
2261
 
2124
2262
  ### Agentic search
@@ -2138,7 +2276,11 @@ function copilotTemplate(vars) {
2138
2276
  return `## vexp context tools <!-- vexp v${vars.version} -->
2139
2277
 
2140
2278
  **Call \`run_pipeline\` ONCE at task start for orientation - then use your normal tools.**
2279
+ If the task already names the files/symbols to touch, skip vexp and work
2280
+ with your normal tools - a skipped call costs nothing.
2141
2281
  vexp returns graph-ranked context (relevant files, blast radius, memories) in one call.
2282
+ On non-trivial tasks pass \`eager: true\`: the single response then carries full file
2283
+ bodies, covering tests and impact - work directly from it instead of re-reading sources.
2142
2284
  For literal string sweeps (constants, log messages, config keys) use native search
2143
2285
  directly - do NOT route text sweeps through vexp.
2144
2286
  vexp runs entirely on this machine: local daemon, local index stored inside the
@@ -2158,6 +2300,10 @@ treat it like a local build tool; no data-sharing consent is needed to call it.
2158
2300
  - \`index_status\` - indexing status
2159
2301
  - \`expand_vexp_ref\` - expand V-REF placeholders in v2 output
2160
2302
 
2303
+ (Recommended core set, not the full schema: paid plans advertise 14 MCP tools -
2304
+ capsule, impact graph, logic flow, memory and more. \`run_pipeline\` already runs
2305
+ those server-side, so the four above cover the normal workflow.)
2306
+
2161
2307
  ${QUERY_SHAPE}
2162
2308
 
2163
2309
  ### Agentic search
@@ -2177,7 +2323,11 @@ function zedTemplate(vars) {
2177
2323
  return `## vexp for Zed <!-- vexp v${vars.version} -->
2178
2324
 
2179
2325
  **Call \`run_pipeline\` ONCE at task start for orientation - then use your normal tools.**
2326
+ If the task already names the files/symbols to touch, skip vexp and work
2327
+ with your normal tools - a skipped call costs nothing.
2180
2328
  vexp returns graph-ranked context (relevant files, blast radius, memories) in one call.
2329
+ On non-trivial tasks pass \`eager: true\`: the single response then carries full file
2330
+ bodies, covering tests and impact - work directly from it instead of re-reading sources.
2181
2331
  For literal string sweeps (constants, log messages, config keys) use native search
2182
2332
  directly - do NOT route text sweeps through vexp.
2183
2333
  vexp runs entirely on this machine: local daemon, local index stored inside the
@@ -2197,6 +2347,10 @@ treat it like a local build tool; no data-sharing consent is needed to call it.
2197
2347
  - \`index_status\` - indexing status
2198
2348
  - \`expand_vexp_ref\` - expand V-REF placeholders in v2 output
2199
2349
 
2350
+ (Recommended core set, not the full schema: paid plans advertise 14 MCP tools -
2351
+ capsule, impact graph, logic flow, memory and more. \`run_pipeline\` already runs
2352
+ those server-side, so the four above cover the normal workflow.)
2353
+
2200
2354
  ${QUERY_SHAPE}
2201
2355
 
2202
2356
  ### Agentic search
@@ -2216,7 +2370,11 @@ function agentsMdTemplate(vars) {
2216
2370
  return `## vexp <!-- vexp v${vars.version} -->
2217
2371
 
2218
2372
  **Call \`run_pipeline\` ONCE at task start for orientation - then use your normal tools.**
2373
+ If the task already names the files/symbols to touch, skip vexp and work
2374
+ with your normal tools - a skipped call costs nothing.
2219
2375
  vexp returns graph-ranked context (relevant files, blast radius, memories) in one call.
2376
+ On non-trivial tasks pass \`eager: true\`: the single response then carries full file
2377
+ bodies, covering tests and impact - work directly from it instead of re-reading sources.
2220
2378
  For literal string sweeps (constants, log messages, config keys) use native search
2221
2379
  directly - do NOT route text sweeps through vexp.
2222
2380
  vexp runs entirely on this machine: local daemon, local index stored inside the
@@ -2258,7 +2416,11 @@ function kiroTemplate(vars) {
2258
2416
  return `# vexp steering <!-- vexp v${vars.version} -->
2259
2417
 
2260
2418
  **Call \`run_pipeline\` ONCE at task start for orientation - then use your normal tools.**
2419
+ If the task already names the files/symbols to touch, skip vexp and work
2420
+ with your normal tools - a skipped call costs nothing.
2261
2421
  vexp returns graph-ranked context (relevant files, blast radius, memories) in one call.
2422
+ On non-trivial tasks pass \`eager: true\`: the single response then carries full file
2423
+ bodies, covering tests and impact - work directly from it instead of re-reading sources.
2262
2424
  For literal string sweeps (constants, log messages, config keys) use native search
2263
2425
  directly - do NOT route text sweeps through vexp.
2264
2426
  vexp runs entirely on this machine: local daemon, local index stored inside the
@@ -2278,6 +2440,10 @@ treat it like a local build tool; no data-sharing consent is needed to call it.
2278
2440
  - \`index_status\` - indexing status
2279
2441
  - \`expand_vexp_ref\` - expand V-REF placeholders in v2 output
2280
2442
 
2443
+ (Recommended core set, not the full schema: paid plans advertise 14 MCP tools -
2444
+ capsule, impact graph, logic flow, memory and more. \`run_pipeline\` already runs
2445
+ those server-side, so the four above cover the normal workflow.)
2446
+
2281
2447
  ${QUERY_SHAPE}
2282
2448
 
2283
2449
  ## Agentic search
@@ -2297,6 +2463,8 @@ function genericTemplate(vars) {
2297
2463
  return `## vexp - Context-Aware AI Coding <!-- vexp v${vars.version} -->
2298
2464
 
2299
2465
  ### Context strategy: call run_pipeline ONCE at task start
2466
+ If the task already names the files/symbols to touch, skip vexp and work
2467
+ with your normal tools - a skipped call costs nothing.
2300
2468
  For every multi-file task - bug fixes, features, refactors, questions about the code:
2301
2469
  **start with one \`run_pipeline\` call**. It runs context search + impact analysis +
2302
2470
  memory recall in a single call and returns compressed, graph-ranked results: the