vexp-cli 2.3.1 → 2.5.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.
- package/README.md +49 -79
- package/dist/agent-config.js +320 -86
- package/dist/cli.js +15 -0
- package/dist/doctor.js +60 -8
- package/dist/hook-template.js +135 -0
- package/dist/mcp-supervisor.js +15 -3
- package/dist/serve.js +10 -0
- package/dist/update-check.js +18 -6
- package/mcp/mcp-server.cjs +43 -43
- package/package.json +51 -39
package/dist/agent-config.js
CHANGED
|
@@ -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, vexpStopGateHookScript, vexpOpencodeHintPlugin } from "./hook-template.js";
|
|
14
14
|
// ---------------------------------------------------------------------------
|
|
15
15
|
// Constants
|
|
16
16
|
// ---------------------------------------------------------------------------
|
|
@@ -320,6 +320,19 @@ 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
|
+
installClaudeCodeStopGate(workspaceRoot, binaryPath);
|
|
327
|
+
if (hintResult) {
|
|
328
|
+
results.push({
|
|
329
|
+
agent: "Claude Code Hint",
|
|
330
|
+
configFile: ".claude/hooks/vexp-hint.sh",
|
|
331
|
+
content: vexpHintHookScript(binaryPath),
|
|
332
|
+
alreadyExists: false,
|
|
333
|
+
action: hintResult,
|
|
334
|
+
});
|
|
335
|
+
}
|
|
323
336
|
// Guard is OPT-IN from 2.3 (see GuardMode): strict installs the
|
|
324
337
|
// PreToolUse deny hook; default removes any previously installed one.
|
|
325
338
|
if (guardMode() === "strict") {
|
|
@@ -349,6 +362,7 @@ export function configureAgents(workspaceRoot, binaryPath, version, agentFilter,
|
|
|
349
362
|
const wrote = configureCodexGlobal(binaryPath, mcpServerPath, workspaceRoot);
|
|
350
363
|
if (wrote)
|
|
351
364
|
mcpConfigs.push("~/.codex/config.toml");
|
|
365
|
+
installCodexHintHook(workspaceRoot, binaryPath);
|
|
352
366
|
}
|
|
353
367
|
// Antigravity: machine-global MCP config (~/.gemini/antigravity/mcp_config.json)
|
|
354
368
|
if (detector.agent === "Antigravity") {
|
|
@@ -376,6 +390,7 @@ export function configureAgents(workspaceRoot, binaryPath, version, agentFilter,
|
|
|
376
390
|
const wrote = configureKiloMcp(workspaceRoot, binaryPath, mcpServerPath);
|
|
377
391
|
if (wrote)
|
|
378
392
|
mcpConfigs.push(wrote);
|
|
393
|
+
installOpencodeHintPlugin(workspaceRoot, binaryPath, ".kilo/plugin");
|
|
379
394
|
// Kilo v7 vendors opencode, so it takes the same guard plugin — the rules
|
|
380
395
|
// markdown alone was demonstrably not enough (a reported session loaded
|
|
381
396
|
// vexp.md, quoted it back, and still read five files by hand).
|
|
@@ -437,6 +452,7 @@ export function configureAgents(workspaceRoot, binaryPath, version, agentFilter,
|
|
|
437
452
|
// Claude Code PreToolUse hook) — which is only safe once MCP is registered.
|
|
438
453
|
if (detector.agent === "Opencode") {
|
|
439
454
|
const wroteMcp = configureOpencodeMcp(workspaceRoot, binaryPath, mcpServerPath);
|
|
455
|
+
installOpencodeHintPlugin(workspaceRoot, binaryPath, ".opencode/plugin");
|
|
440
456
|
if (wroteMcp)
|
|
441
457
|
mcpConfigs.push(wroteMcp);
|
|
442
458
|
if (guardMode() === "strict") {
|
|
@@ -607,6 +623,8 @@ export function configureSelectedAgents(workspaceRoot, binaryPath, version, sele
|
|
|
607
623
|
const wrote = configureClaudeCodeGlobal(binaryPath, mcpServerPath, workspaceRoot);
|
|
608
624
|
if (wrote)
|
|
609
625
|
mcpConfigs.push("~/.claude.json");
|
|
626
|
+
installClaudeCodeHintHook(workspaceRoot, binaryPath);
|
|
627
|
+
installClaudeCodeStopGate(workspaceRoot, binaryPath);
|
|
610
628
|
if (guardMode() === "strict")
|
|
611
629
|
installClaudeCodeHook(workspaceRoot);
|
|
612
630
|
else
|
|
@@ -616,6 +634,7 @@ export function configureSelectedAgents(workspaceRoot, binaryPath, version, sele
|
|
|
616
634
|
const wrote = configureCodexGlobal(binaryPath, mcpServerPath, workspaceRoot);
|
|
617
635
|
if (wrote)
|
|
618
636
|
mcpConfigs.push("~/.codex/config.toml");
|
|
637
|
+
installCodexHintHook(workspaceRoot, binaryPath);
|
|
619
638
|
}
|
|
620
639
|
if (detector.agent === "Antigravity") {
|
|
621
640
|
const wrote = configureAntigravityGlobal(binaryPath, mcpServerPath);
|
|
@@ -636,6 +655,7 @@ export function configureSelectedAgents(workspaceRoot, binaryPath, version, sele
|
|
|
636
655
|
const wrote = configureOpencodeMcp(workspaceRoot, binaryPath, mcpServerPath);
|
|
637
656
|
if (wrote)
|
|
638
657
|
mcpConfigs.push(wrote);
|
|
658
|
+
installOpencodeHintPlugin(workspaceRoot, binaryPath, ".opencode/plugin");
|
|
639
659
|
if (guardMode() === "strict")
|
|
640
660
|
installOpencodePlugin(workspaceRoot);
|
|
641
661
|
else
|
|
@@ -655,6 +675,7 @@ export function configureSelectedAgents(workspaceRoot, binaryPath, version, sele
|
|
|
655
675
|
const wrote = configureKiloMcp(workspaceRoot, binaryPath, mcpServerPath);
|
|
656
676
|
if (wrote)
|
|
657
677
|
mcpConfigs.push(wrote);
|
|
678
|
+
installOpencodeHintPlugin(workspaceRoot, binaryPath, ".kilo/plugin");
|
|
658
679
|
if (guardMode() === "strict")
|
|
659
680
|
installKiloPlugin(workspaceRoot);
|
|
660
681
|
else
|
|
@@ -1634,20 +1655,23 @@ export function installClaudeCodeHook(workspaceRoot) {
|
|
|
1634
1655
|
const existingPreToolUse = Array.isArray(hooks.PreToolUse) ? hooks.PreToolUse : [];
|
|
1635
1656
|
// Aggressively remove ALL vexp-related hook entries (old format, stale matchers, malformed)
|
|
1636
1657
|
const filtered = existingPreToolUse.filter((h) => !isVexpGuardHookEntry(h));
|
|
1637
|
-
//
|
|
1638
|
-
//
|
|
1639
|
-
//
|
|
1640
|
-
//
|
|
1641
|
-
//
|
|
1642
|
-
//
|
|
1643
|
-
//
|
|
1658
|
+
// Shell form with an explicit `bash` prefix and a QUOTED path — the one
|
|
1659
|
+
// shape that works on every OS Claude Code supports:
|
|
1660
|
+
// - the quotes fix the historical spaces bug (unquoted
|
|
1661
|
+
// `$CLAUDE_PROJECT_DIR/...` word-split and the guard never ran);
|
|
1662
|
+
// - exec form (`args` present → direct spawn) fixed spaces but broke
|
|
1663
|
+
// Windows entirely: the OS cannot spawn a `.sh` without a shell, the
|
|
1664
|
+
// hook errored, failed open, and enforced nothing (field report) —
|
|
1665
|
+
// Claude Code on Windows runs shell-form hooks through Git Bash, so
|
|
1666
|
+
// `bash "$VAR"` works there and on unix alike.
|
|
1667
|
+
// `timeout` is SECONDS (default 600), not ms: 3000 once configured a
|
|
1668
|
+
// 50-minute hook timeout.
|
|
1644
1669
|
filtered.push({
|
|
1645
1670
|
matcher: "Grep|Glob|Regex",
|
|
1646
1671
|
hooks: [
|
|
1647
1672
|
{
|
|
1648
1673
|
type: "command",
|
|
1649
|
-
command: "$
|
|
1650
|
-
args: [],
|
|
1674
|
+
command: 'bash "$CLAUDE_PROJECT_DIR/.claude/hooks/vexp-guard.sh"',
|
|
1651
1675
|
timeout: 5,
|
|
1652
1676
|
},
|
|
1653
1677
|
],
|
|
@@ -1667,6 +1691,206 @@ export function installClaudeCodeHook(workspaceRoot) {
|
|
|
1667
1691
|
}
|
|
1668
1692
|
return existed ? "updated" : "created";
|
|
1669
1693
|
}
|
|
1694
|
+
function isVexpHookEntry(h, needle) {
|
|
1695
|
+
if (!h || typeof h !== "object")
|
|
1696
|
+
return false;
|
|
1697
|
+
const entry = h;
|
|
1698
|
+
if (entry.command?.includes(needle))
|
|
1699
|
+
return true;
|
|
1700
|
+
const hks = entry.hooks;
|
|
1701
|
+
return Array.isArray(hks) && hks.some((hook) => {
|
|
1702
|
+
if (!hook || typeof hook !== "object")
|
|
1703
|
+
return false;
|
|
1704
|
+
return hook.command?.includes(needle) ?? false;
|
|
1705
|
+
});
|
|
1706
|
+
}
|
|
1707
|
+
/**
|
|
1708
|
+
* Horizon F2a: install the Stop-hook verification gate for Claude Code.
|
|
1709
|
+
* Same fail-open family as the hint hook: the Rust `stop-gate` command
|
|
1710
|
+
* blocks a stop at most once per session and only on mechanically broken
|
|
1711
|
+
* references (parse errors, imports of removed names). No daemon = no
|
|
1712
|
+
* gate = vanilla stop. Additive like the hint hook, so not guardMode-gated.
|
|
1713
|
+
*/
|
|
1714
|
+
export function installClaudeCodeStopGate(workspaceRoot, binaryPath) {
|
|
1715
|
+
const hookDir = path.join(workspaceRoot, ".claude", "hooks");
|
|
1716
|
+
const hookPath = path.join(hookDir, "vexp-verify.sh");
|
|
1717
|
+
const settingsPath = path.join(workspaceRoot, ".claude", "settings.json");
|
|
1718
|
+
fs.mkdirSync(hookDir, { recursive: true });
|
|
1719
|
+
const script = vexpStopGateHookScript(binaryPath);
|
|
1720
|
+
const existed = fs.existsSync(hookPath);
|
|
1721
|
+
const scriptIdentical = existed && fs.readFileSync(hookPath, "utf8") === script;
|
|
1722
|
+
if (!scriptIdentical) {
|
|
1723
|
+
fs.writeFileSync(hookPath, script, { mode: 0o755 });
|
|
1724
|
+
}
|
|
1725
|
+
const read = readJsonConfigSafe(settingsPath);
|
|
1726
|
+
if (!read.ok) {
|
|
1727
|
+
warnUnparseable(settingsPath);
|
|
1728
|
+
return scriptIdentical ? null : existed ? "updated" : "created";
|
|
1729
|
+
}
|
|
1730
|
+
const settings = read.data;
|
|
1731
|
+
const hooks = (settings.hooks ?? {});
|
|
1732
|
+
const existing = Array.isArray(hooks.Stop) ? hooks.Stop : [];
|
|
1733
|
+
const filtered = existing.filter((h) => !isVexpHookEntry(h, "vexp-verify"));
|
|
1734
|
+
// Cross-OS shape as everywhere: bash + QUOTED path; timeout in SECONDS.
|
|
1735
|
+
filtered.push({
|
|
1736
|
+
hooks: [
|
|
1737
|
+
{
|
|
1738
|
+
type: "command",
|
|
1739
|
+
command: 'bash "$CLAUDE_PROJECT_DIR/.claude/hooks/vexp-verify.sh"',
|
|
1740
|
+
timeout: 15,
|
|
1741
|
+
},
|
|
1742
|
+
],
|
|
1743
|
+
});
|
|
1744
|
+
const merged = { ...hooks, Stop: filtered };
|
|
1745
|
+
const settingsIdentical = JSON.stringify(merged) === JSON.stringify(settings.hooks ?? {});
|
|
1746
|
+
if (scriptIdentical && settingsIdentical)
|
|
1747
|
+
return null;
|
|
1748
|
+
settings.hooks = merged;
|
|
1749
|
+
if (!settingsIdentical) {
|
|
1750
|
+
if (read.existed)
|
|
1751
|
+
backupConfig(settingsPath);
|
|
1752
|
+
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
1753
|
+
}
|
|
1754
|
+
return existed ? "updated" : "created";
|
|
1755
|
+
}
|
|
1756
|
+
function isVexpHintHookEntry(h) {
|
|
1757
|
+
if (!h || typeof h !== "object")
|
|
1758
|
+
return false;
|
|
1759
|
+
const entry = h;
|
|
1760
|
+
const own = entry.command?.includes("vexp-hint");
|
|
1761
|
+
if (own)
|
|
1762
|
+
return true;
|
|
1763
|
+
const hks = entry.hooks;
|
|
1764
|
+
return Array.isArray(hks) && hks.some((hook) => {
|
|
1765
|
+
if (!hook || typeof hook !== "object")
|
|
1766
|
+
return false;
|
|
1767
|
+
return hook.command?.includes("vexp-hint") ?? false;
|
|
1768
|
+
});
|
|
1769
|
+
}
|
|
1770
|
+
/**
|
|
1771
|
+
* Install the event-driven hint hook for Claude Code (2.3.3 default).
|
|
1772
|
+
* Replaces the resident CLAUDE.md strategy text: a UserPromptSubmit hook
|
|
1773
|
+
* asks the daemon to classify each prompt against the index and injects a
|
|
1774
|
+
* one-line run_pipeline suggestion ONLY for unfamiliar-area prompts. On
|
|
1775
|
+
* oriented prompts the injection is empty — zero context cost. Fail-open by
|
|
1776
|
+
* construction (see VEXP_HINT_HOOK): any failure = vanilla behavior.
|
|
1777
|
+
* Non-blocking and additive, so it is NOT gated by guardMode (the 2.3 A2
|
|
1778
|
+
* opt-in applies to DENY hooks, which add a failure mode; this one cannot
|
|
1779
|
+
* block anything).
|
|
1780
|
+
*/
|
|
1781
|
+
export function installClaudeCodeHintHook(workspaceRoot, binaryPath) {
|
|
1782
|
+
const hookDir = path.join(workspaceRoot, ".claude", "hooks");
|
|
1783
|
+
const hookPath = path.join(hookDir, "vexp-hint.sh");
|
|
1784
|
+
const settingsPath = path.join(workspaceRoot, ".claude", "settings.json");
|
|
1785
|
+
const script = vexpHintHookScript(binaryPath);
|
|
1786
|
+
fs.mkdirSync(hookDir, { recursive: true });
|
|
1787
|
+
const existed = fs.existsSync(hookPath);
|
|
1788
|
+
const scriptIdentical = existed && fs.readFileSync(hookPath, "utf-8") === script;
|
|
1789
|
+
if (!scriptIdentical) {
|
|
1790
|
+
fs.writeFileSync(hookPath, script, { mode: 0o755 });
|
|
1791
|
+
}
|
|
1792
|
+
const read = readJsonConfigSafe(settingsPath);
|
|
1793
|
+
if (!read.ok) {
|
|
1794
|
+
warnUnparseable(settingsPath);
|
|
1795
|
+
return scriptIdentical ? null : existed ? "updated" : "created";
|
|
1796
|
+
}
|
|
1797
|
+
const settings = read.data;
|
|
1798
|
+
const hooks = (settings.hooks ?? {});
|
|
1799
|
+
const existing = Array.isArray(hooks.UserPromptSubmit)
|
|
1800
|
+
? hooks.UserPromptSubmit
|
|
1801
|
+
: [];
|
|
1802
|
+
const filtered = existing.filter((h) => !isVexpHintHookEntry(h));
|
|
1803
|
+
// Same cross-OS shape as the guard entry: bash prefix + QUOTED path
|
|
1804
|
+
// (spaces-safe on unix, Git Bash on Windows). timeout is SECONDS.
|
|
1805
|
+
filtered.push({
|
|
1806
|
+
hooks: [
|
|
1807
|
+
{
|
|
1808
|
+
type: "command",
|
|
1809
|
+
command: 'bash "$CLAUDE_PROJECT_DIR/.claude/hooks/vexp-hint.sh"',
|
|
1810
|
+
timeout: 5,
|
|
1811
|
+
},
|
|
1812
|
+
],
|
|
1813
|
+
});
|
|
1814
|
+
const merged = { ...hooks, UserPromptSubmit: filtered };
|
|
1815
|
+
const settingsIdentical = JSON.stringify(merged) === JSON.stringify(settings.hooks ?? {});
|
|
1816
|
+
if (scriptIdentical && settingsIdentical)
|
|
1817
|
+
return null;
|
|
1818
|
+
settings.hooks = merged;
|
|
1819
|
+
if (!settingsIdentical) {
|
|
1820
|
+
if (read.existed)
|
|
1821
|
+
backupConfig(settingsPath);
|
|
1822
|
+
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
1823
|
+
}
|
|
1824
|
+
return existed ? "updated" : "created";
|
|
1825
|
+
}
|
|
1826
|
+
/**
|
|
1827
|
+
* opencode/Kilo hint plugin (2.4.0): default-ON, non-blocking, fail-open —
|
|
1828
|
+
* unlike the deny guard (strict-only), this cannot break a session; it only
|
|
1829
|
+
* ever appends an orientation line the daemon judged useful.
|
|
1830
|
+
*/
|
|
1831
|
+
export function installOpencodeHintPlugin(workspaceRoot, binaryPath, pluginDir) {
|
|
1832
|
+
const pluginPath = path.join(workspaceRoot, pluginDir, "vexp-hint.js");
|
|
1833
|
+
const content = vexpOpencodeHintPlugin(binaryPath);
|
|
1834
|
+
fs.mkdirSync(path.dirname(pluginPath), { recursive: true });
|
|
1835
|
+
const existed = fs.existsSync(pluginPath);
|
|
1836
|
+
if (existed && fs.readFileSync(pluginPath, "utf-8") === content)
|
|
1837
|
+
return null;
|
|
1838
|
+
fs.writeFileSync(pluginPath, content);
|
|
1839
|
+
return existed ? "updated" : "created";
|
|
1840
|
+
}
|
|
1841
|
+
/**
|
|
1842
|
+
* Codex hint hook (2.4.0). Codex >=0.129 discovers `<repo>/.codex/hooks.json`
|
|
1843
|
+
* and its UserPromptSubmit stdout envelope is byte-identical to Claude
|
|
1844
|
+
* Code's (hookSpecificOutput.additionalContext), so the same prompt-hint
|
|
1845
|
+
* backend serves both: PROJECT-scope file, no global config touched.
|
|
1846
|
+
* Fail-open: missing bash/binary/daemon => no output => vanilla.
|
|
1847
|
+
*/
|
|
1848
|
+
export function installCodexHintHook(workspaceRoot, binaryPath) {
|
|
1849
|
+
const dir = path.join(workspaceRoot, ".codex");
|
|
1850
|
+
const hookPath = path.join(dir, "vexp-hint.sh");
|
|
1851
|
+
const hooksJsonPath = path.join(dir, "hooks.json");
|
|
1852
|
+
const script = vexpHintHookScript(binaryPath);
|
|
1853
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
1854
|
+
const existed = fs.existsSync(hookPath);
|
|
1855
|
+
const scriptIdentical = existed && fs.readFileSync(hookPath, "utf-8") === script;
|
|
1856
|
+
if (!scriptIdentical) {
|
|
1857
|
+
fs.writeFileSync(hookPath, script, { mode: 0o755 });
|
|
1858
|
+
}
|
|
1859
|
+
let root = {};
|
|
1860
|
+
if (fs.existsSync(hooksJsonPath)) {
|
|
1861
|
+
try {
|
|
1862
|
+
root = JSON.parse(fs.readFileSync(hooksJsonPath, "utf-8"));
|
|
1863
|
+
}
|
|
1864
|
+
catch {
|
|
1865
|
+
warnUnparseable(hooksJsonPath);
|
|
1866
|
+
return scriptIdentical ? null : existed ? "updated" : "created";
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
// Codex hooks.json shape: EVENT NAME at top level (no "hooks" wrapper).
|
|
1870
|
+
const existing = Array.isArray(root.UserPromptSubmit)
|
|
1871
|
+
? root.UserPromptSubmit
|
|
1872
|
+
: [];
|
|
1873
|
+
const filtered = existing.filter((h) => !isVexpHintHookEntry(h));
|
|
1874
|
+
// Absolute path: Codex has no $CLAUDE_PROJECT_DIR substitution.
|
|
1875
|
+
filtered.push({
|
|
1876
|
+
hooks: [
|
|
1877
|
+
{
|
|
1878
|
+
type: "command",
|
|
1879
|
+
command: `bash "${hookPath.replace(/\\/g, "/")}"`,
|
|
1880
|
+
timeout: 5,
|
|
1881
|
+
},
|
|
1882
|
+
],
|
|
1883
|
+
});
|
|
1884
|
+
const before = JSON.stringify(root.UserPromptSubmit ?? null);
|
|
1885
|
+
root.UserPromptSubmit = filtered;
|
|
1886
|
+
const identical = before === JSON.stringify(filtered);
|
|
1887
|
+
if (scriptIdentical && identical)
|
|
1888
|
+
return null;
|
|
1889
|
+
if (!identical) {
|
|
1890
|
+
fs.writeFileSync(hooksJsonPath, JSON.stringify(root, null, 2) + "\n");
|
|
1891
|
+
}
|
|
1892
|
+
return existed ? "updated" : "created";
|
|
1893
|
+
}
|
|
1670
1894
|
// ---------------------------------------------------------------------------
|
|
1671
1895
|
// opencode plugin - blocks grep/glob (and shelled-out search) when the daemon
|
|
1672
1896
|
// is healthy. opencode has no PreToolUse hook, but auto-loads plugins from
|
|
@@ -1929,90 +2153,33 @@ function generateAgentConfig(template, vars) {
|
|
|
1929
2153
|
// ---------------------------------------------------------------------------
|
|
1930
2154
|
function claudeCodeTemplate(vars) {
|
|
1931
2155
|
return `## vexp - Context-Aware AI Coding <!-- vexp v${vars.version} -->
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
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
|
|
2156
|
+
vexp runs entirely on this machine: local daemon, index in \`.vexp/\`.
|
|
2157
|
+
\`run_pipeline\` transmits nothing to any external service.
|
|
2158
|
+
- \`run_pipeline({ "task": "..." })\` - orientation in one call (ranked pivot
|
|
2159
|
+
files with line ranges + blast radius + session notes) when a task does NOT
|
|
2160
|
+
name the files/symbols to touch. If it does, SKIP vexp - use your normal tools.
|
|
2161
|
+
- \`get_skeleton\` - file structure at 70-90% token savings for files you only
|
|
1947
2162
|
need to understand, not edit.
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
### Primary Tool
|
|
1959
|
-
- \`run_pipeline\` - **USE THIS FOR EVERYTHING**. Single call that runs
|
|
1960
|
-
capsule + impact + memory server-side. Returns compressed results.
|
|
1961
|
-
Auto-detects intent (debug/modify/refactor/explore) from your task.
|
|
1962
|
-
Includes full file content for pivots.
|
|
1963
|
-
Examples:
|
|
1964
|
-
- \`run_pipeline({ "task": "fix JWT validation bug" })\` - auto-detect
|
|
1965
|
-
- \`run_pipeline({ "task": "refactor db layer", "preset": "refactor" })\` - explicit
|
|
1966
|
-
- \`run_pipeline({ "task": "add auth", "observation": "using JWT" })\` - save insight in same call
|
|
1967
|
-
|
|
1968
|
-
### Other MCP tools (use only when run_pipeline is insufficient)
|
|
1969
|
-
- \`get_skeleton\` - **preferred over Read** for inspecting files (minimal/standard/detailed detail levels, 70-90% token savings)
|
|
1970
|
-
- \`index_status\` - indexing status and health check
|
|
1971
|
-
- \`expand_vexp_ref\` - expand V-REF hash placeholders in v2 compact output
|
|
1972
|
-
|
|
1973
|
-
${QUERY_SHAPE}
|
|
1974
|
-
|
|
1975
|
-
### Workflow
|
|
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
|
|
1981
|
-
|
|
1982
|
-
### Subagent / Explore / Plan mode
|
|
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
|
|
1987
|
-
|
|
1988
|
-
### Smart Features (automatic - no action needed)
|
|
1989
|
-
- **Intent Detection**: auto-detects from your task keywords. "fix bug" -> Debug, "refactor" -> blast-radius, "add" -> Modify
|
|
1990
|
-
- **Hybrid Search**: keyword + semantic + graph centrality ranking
|
|
1991
|
-
- **Session Memory**: auto-captures observations; memories auto-surfaced in results
|
|
1992
|
-
- **LSP Bridge**: VS Code captures type-resolved call edges
|
|
1993
|
-
- **Change Coupling**: co-changed files included as related context
|
|
1994
|
-
|
|
1995
|
-
### Advanced Parameters
|
|
1996
|
-
- \`preset: "debug"\` - forces debug mode (capsule+tests+impact+memory)
|
|
1997
|
-
- \`preset: "refactor"\` - deep impact analysis (depth 5)
|
|
1998
|
-
- \`max_tokens: 12000\` - increase total budget for complex tasks
|
|
1999
|
-
- \`include_tests: true\` - include test files in results
|
|
2000
|
-
- \`include_file_content: false\` - omit full file content (lighter response)
|
|
2001
|
-
|
|
2002
|
-
### Fallback
|
|
2003
|
-
If \`run_pipeline\` returns \`status: "degraded"\` or 0 pivots with an INDEX EMPTY warning,
|
|
2004
|
-
the index is empty or rebuilding. Use Grep, Glob, and Read directly until the index is ready.
|
|
2005
|
-
|
|
2006
|
-
### Multi-Repo Workspaces
|
|
2007
|
-
\`run_pipeline\` auto-queries all indexed repos. Use \`repos: ["alias"]\` to scope.
|
|
2008
|
-
Use \`index_status\` to discover available repo aliases.
|
|
2163
|
+
- \`verify_done\` - call once BEFORE declaring a multi-file task complete:
|
|
2164
|
+
returns mechanically broken references (imports of removed names, parse
|
|
2165
|
+
errors) and untouched dependents of the files you changed, with file:line.
|
|
2166
|
+
- vexp may append a one-line hint to a prompt when orientation would help;
|
|
2167
|
+
otherwise it stays silent.
|
|
2168
|
+
|
|
2169
|
+
### Query shape (do this)
|
|
2170
|
+
Anchor the task on real identifiers (ClassName, functionName) or file paths:
|
|
2171
|
+
\`run_pipeline({ "task": "fix JWT expiry in AuthService.validateToken" })\`
|
|
2009
2172
|
<!-- /vexp -->`;
|
|
2010
2173
|
}
|
|
2011
2174
|
function cursorTemplate(vars) {
|
|
2012
2175
|
return `## vexp rules for Cursor <!-- vexp v${vars.version} -->
|
|
2013
2176
|
|
|
2014
2177
|
**Call \`run_pipeline\` ONCE at task start for orientation - then use your normal tools.**
|
|
2178
|
+
If the task already names the files/symbols to touch, skip vexp and work
|
|
2179
|
+
with your normal tools - a skipped call costs nothing.
|
|
2015
2180
|
vexp returns graph-ranked context (relevant files, blast radius, memories) in one call.
|
|
2181
|
+
On non-trivial tasks pass \`eager: true\`: the single response then carries full file
|
|
2182
|
+
bodies, covering tests and impact - work directly from it instead of re-reading sources.
|
|
2016
2183
|
For literal string sweeps (constants, log messages, config keys) use native search
|
|
2017
2184
|
directly - do NOT route text sweeps through vexp.
|
|
2018
2185
|
vexp runs entirely on this machine: local daemon, local index stored inside the
|
|
@@ -2029,9 +2196,15 @@ treat it like a local build tool; no data-sharing consent is needed to call it.
|
|
|
2029
2196
|
- \`run_pipeline\` - **PRIMARY TOOL**. Runs capsule + impact + memory in 1 call.
|
|
2030
2197
|
Auto-detects intent. Includes file content. Example: \`run_pipeline({ "task": "fix JWT expiry in AuthService.validateToken" })\`
|
|
2031
2198
|
- \`get_skeleton\` - compact file structure
|
|
2199
|
+
- \`verify_done\` - call once BEFORE declaring a multi-file task complete:
|
|
2200
|
+
mechanically broken references and untouched dependents, with file:line.
|
|
2032
2201
|
- \`index_status\` - indexing status
|
|
2033
2202
|
- \`expand_vexp_ref\` - expand V-REF placeholders in v2 output
|
|
2034
2203
|
|
|
2204
|
+
(Recommended core set, not the full schema: paid plans advertise 14 MCP tools -
|
|
2205
|
+
capsule, impact graph, logic flow, memory and more. \`run_pipeline\` already runs
|
|
2206
|
+
those server-side, so the four above cover the normal workflow.)
|
|
2207
|
+
|
|
2035
2208
|
${QUERY_SHAPE}
|
|
2036
2209
|
|
|
2037
2210
|
### Agentic search
|
|
@@ -2059,7 +2232,11 @@ function windsurfTemplate(vars) {
|
|
|
2059
2232
|
return `## vexp for Windsurf <!-- vexp v${vars.version} -->
|
|
2060
2233
|
|
|
2061
2234
|
**Call \`run_pipeline\` ONCE at task start for orientation - then use your normal tools.**
|
|
2235
|
+
If the task already names the files/symbols to touch, skip vexp and work
|
|
2236
|
+
with your normal tools - a skipped call costs nothing.
|
|
2062
2237
|
vexp returns graph-ranked context (relevant files, blast radius, memories) in one call.
|
|
2238
|
+
On non-trivial tasks pass \`eager: true\`: the single response then carries full file
|
|
2239
|
+
bodies, covering tests and impact - work directly from it instead of re-reading sources.
|
|
2063
2240
|
For literal string sweeps (constants, log messages, config keys) use native search
|
|
2064
2241
|
directly - do NOT route text sweeps through vexp.
|
|
2065
2242
|
vexp runs entirely on this machine: local daemon, local index stored inside the
|
|
@@ -2076,9 +2253,15 @@ treat it like a local build tool; no data-sharing consent is needed to call it.
|
|
|
2076
2253
|
- \`run_pipeline\` - **PRIMARY TOOL**. Runs capsule + impact + memory in 1 call.
|
|
2077
2254
|
Auto-detects intent. Includes file content. Example: \`run_pipeline({ "task": "fix JWT expiry in AuthService.validateToken" })\`
|
|
2078
2255
|
- \`get_skeleton\` - compact file structure
|
|
2256
|
+
- \`verify_done\` - call once BEFORE declaring a multi-file task complete:
|
|
2257
|
+
mechanically broken references and untouched dependents, with file:line.
|
|
2079
2258
|
- \`index_status\` - indexing status
|
|
2080
2259
|
- \`expand_vexp_ref\` - expand V-REF placeholders in v2 output
|
|
2081
2260
|
|
|
2261
|
+
(Recommended core set, not the full schema: paid plans advertise 14 MCP tools -
|
|
2262
|
+
capsule, impact graph, logic flow, memory and more. \`run_pipeline\` already runs
|
|
2263
|
+
those server-side, so the four above cover the normal workflow.)
|
|
2264
|
+
|
|
2082
2265
|
${QUERY_SHAPE}
|
|
2083
2266
|
|
|
2084
2267
|
### Agentic search
|
|
@@ -2117,7 +2300,11 @@ function augmentTemplate(vars) {
|
|
|
2117
2300
|
return `## vexp for Augment <!-- vexp v${vars.version} -->
|
|
2118
2301
|
|
|
2119
2302
|
**Call \`run_pipeline\` ONCE at task start for orientation - then use your normal tools.**
|
|
2303
|
+
If the task already names the files/symbols to touch, skip vexp and work
|
|
2304
|
+
with your normal tools - a skipped call costs nothing.
|
|
2120
2305
|
vexp returns graph-ranked context (relevant files, blast radius, memories) in one call.
|
|
2306
|
+
On non-trivial tasks pass \`eager: true\`: the single response then carries full file
|
|
2307
|
+
bodies, covering tests and impact - work directly from it instead of re-reading sources.
|
|
2121
2308
|
For literal string sweeps (constants, log messages, config keys) use native search
|
|
2122
2309
|
directly - do NOT route text sweeps through vexp.
|
|
2123
2310
|
vexp runs entirely on this machine: local daemon, local index stored inside the
|
|
@@ -2134,9 +2321,15 @@ When working on this codebase:
|
|
|
2134
2321
|
- \`run_pipeline\` - **PRIMARY TOOL**. Runs capsule + impact + memory in 1 call.
|
|
2135
2322
|
Example: \`run_pipeline({ "task": "fix JWT expiry in AuthService.validateToken" })\`
|
|
2136
2323
|
- \`get_skeleton\` - token-efficient file structure
|
|
2324
|
+
- \`verify_done\` - call once BEFORE declaring a multi-file task complete:
|
|
2325
|
+
mechanically broken references and untouched dependents, with file:line.
|
|
2137
2326
|
- \`index_status\` - indexing status
|
|
2138
2327
|
- \`expand_vexp_ref\` - expand V-REF placeholders in v2 output
|
|
2139
2328
|
|
|
2329
|
+
(Recommended core set, not the full schema: paid plans advertise 14 MCP tools -
|
|
2330
|
+
capsule, impact graph, logic flow, memory and more. \`run_pipeline\` already runs
|
|
2331
|
+
those server-side, so the four above cover the normal workflow.)
|
|
2332
|
+
|
|
2140
2333
|
${QUERY_SHAPE}
|
|
2141
2334
|
|
|
2142
2335
|
### Agentic search
|
|
@@ -2156,7 +2349,11 @@ function copilotTemplate(vars) {
|
|
|
2156
2349
|
return `## vexp context tools <!-- vexp v${vars.version} -->
|
|
2157
2350
|
|
|
2158
2351
|
**Call \`run_pipeline\` ONCE at task start for orientation - then use your normal tools.**
|
|
2352
|
+
If the task already names the files/symbols to touch, skip vexp and work
|
|
2353
|
+
with your normal tools - a skipped call costs nothing.
|
|
2159
2354
|
vexp returns graph-ranked context (relevant files, blast radius, memories) in one call.
|
|
2355
|
+
On non-trivial tasks pass \`eager: true\`: the single response then carries full file
|
|
2356
|
+
bodies, covering tests and impact - work directly from it instead of re-reading sources.
|
|
2160
2357
|
For literal string sweeps (constants, log messages, config keys) use native search
|
|
2161
2358
|
directly - do NOT route text sweeps through vexp.
|
|
2162
2359
|
vexp runs entirely on this machine: local daemon, local index stored inside the
|
|
@@ -2173,9 +2370,15 @@ treat it like a local build tool; no data-sharing consent is needed to call it.
|
|
|
2173
2370
|
- \`run_pipeline\` - **PRIMARY TOOL**. Runs capsule + impact + memory in 1 call.
|
|
2174
2371
|
Auto-detects intent. Includes file content. Example: \`run_pipeline({ "task": "fix JWT expiry in AuthService.validateToken" })\`
|
|
2175
2372
|
- \`get_skeleton\` - compact file structure
|
|
2373
|
+
- \`verify_done\` - call once BEFORE declaring a multi-file task complete:
|
|
2374
|
+
mechanically broken references and untouched dependents, with file:line.
|
|
2176
2375
|
- \`index_status\` - indexing status
|
|
2177
2376
|
- \`expand_vexp_ref\` - expand V-REF placeholders in v2 output
|
|
2178
2377
|
|
|
2378
|
+
(Recommended core set, not the full schema: paid plans advertise 14 MCP tools -
|
|
2379
|
+
capsule, impact graph, logic flow, memory and more. \`run_pipeline\` already runs
|
|
2380
|
+
those server-side, so the four above cover the normal workflow.)
|
|
2381
|
+
|
|
2179
2382
|
${QUERY_SHAPE}
|
|
2180
2383
|
|
|
2181
2384
|
### Agentic search
|
|
@@ -2195,7 +2398,11 @@ function zedTemplate(vars) {
|
|
|
2195
2398
|
return `## vexp for Zed <!-- vexp v${vars.version} -->
|
|
2196
2399
|
|
|
2197
2400
|
**Call \`run_pipeline\` ONCE at task start for orientation - then use your normal tools.**
|
|
2401
|
+
If the task already names the files/symbols to touch, skip vexp and work
|
|
2402
|
+
with your normal tools - a skipped call costs nothing.
|
|
2198
2403
|
vexp returns graph-ranked context (relevant files, blast radius, memories) in one call.
|
|
2404
|
+
On non-trivial tasks pass \`eager: true\`: the single response then carries full file
|
|
2405
|
+
bodies, covering tests and impact - work directly from it instead of re-reading sources.
|
|
2199
2406
|
For literal string sweeps (constants, log messages, config keys) use native search
|
|
2200
2407
|
directly - do NOT route text sweeps through vexp.
|
|
2201
2408
|
vexp runs entirely on this machine: local daemon, local index stored inside the
|
|
@@ -2212,9 +2419,15 @@ treat it like a local build tool; no data-sharing consent is needed to call it.
|
|
|
2212
2419
|
- \`run_pipeline\` - **PRIMARY TOOL**. Runs capsule + impact + memory in 1 call.
|
|
2213
2420
|
Example: \`run_pipeline({ "task": "fix JWT expiry in AuthService.validateToken" })\`
|
|
2214
2421
|
- \`get_skeleton\` - compact file structure
|
|
2422
|
+
- \`verify_done\` - call once BEFORE declaring a multi-file task complete:
|
|
2423
|
+
mechanically broken references and untouched dependents, with file:line.
|
|
2215
2424
|
- \`index_status\` - indexing status
|
|
2216
2425
|
- \`expand_vexp_ref\` - expand V-REF placeholders in v2 output
|
|
2217
2426
|
|
|
2427
|
+
(Recommended core set, not the full schema: paid plans advertise 14 MCP tools -
|
|
2428
|
+
capsule, impact graph, logic flow, memory and more. \`run_pipeline\` already runs
|
|
2429
|
+
those server-side, so the four above cover the normal workflow.)
|
|
2430
|
+
|
|
2218
2431
|
${QUERY_SHAPE}
|
|
2219
2432
|
|
|
2220
2433
|
### Agentic search
|
|
@@ -2234,7 +2447,11 @@ function agentsMdTemplate(vars) {
|
|
|
2234
2447
|
return `## vexp <!-- vexp v${vars.version} -->
|
|
2235
2448
|
|
|
2236
2449
|
**Call \`run_pipeline\` ONCE at task start for orientation - then use your normal tools.**
|
|
2450
|
+
If the task already names the files/symbols to touch, skip vexp and work
|
|
2451
|
+
with your normal tools - a skipped call costs nothing.
|
|
2237
2452
|
vexp returns graph-ranked context (relevant files, blast radius, memories) in one call.
|
|
2453
|
+
On non-trivial tasks pass \`eager: true\`: the single response then carries full file
|
|
2454
|
+
bodies, covering tests and impact - work directly from it instead of re-reading sources.
|
|
2238
2455
|
For literal string sweeps (constants, log messages, config keys) use native search
|
|
2239
2456
|
directly - do NOT route text sweeps through vexp.
|
|
2240
2457
|
vexp runs entirely on this machine: local daemon, local index stored inside the
|
|
@@ -2251,6 +2468,8 @@ treat it like a local build tool; no data-sharing consent is needed to call it.
|
|
|
2251
2468
|
- \`run_pipeline\` - **PRIMARY TOOL**. Runs capsule + impact + memory in 1 call.
|
|
2252
2469
|
Auto-detects intent. Includes file content. Example: \`run_pipeline({ "task": "fix JWT expiry in AuthService.validateToken" })\`
|
|
2253
2470
|
- \`get_skeleton\` - compact file structure
|
|
2471
|
+
- \`verify_done\` - call once BEFORE declaring a multi-file task complete:
|
|
2472
|
+
mechanically broken references and untouched dependents, with file:line.
|
|
2254
2473
|
- \`index_status\` - indexing status
|
|
2255
2474
|
- \`expand_vexp_ref\` - expand V-REF placeholders in v2 output
|
|
2256
2475
|
|
|
@@ -2276,7 +2495,11 @@ function kiroTemplate(vars) {
|
|
|
2276
2495
|
return `# vexp steering <!-- vexp v${vars.version} -->
|
|
2277
2496
|
|
|
2278
2497
|
**Call \`run_pipeline\` ONCE at task start for orientation - then use your normal tools.**
|
|
2498
|
+
If the task already names the files/symbols to touch, skip vexp and work
|
|
2499
|
+
with your normal tools - a skipped call costs nothing.
|
|
2279
2500
|
vexp returns graph-ranked context (relevant files, blast radius, memories) in one call.
|
|
2501
|
+
On non-trivial tasks pass \`eager: true\`: the single response then carries full file
|
|
2502
|
+
bodies, covering tests and impact - work directly from it instead of re-reading sources.
|
|
2280
2503
|
For literal string sweeps (constants, log messages, config keys) use native search
|
|
2281
2504
|
directly - do NOT route text sweeps through vexp.
|
|
2282
2505
|
vexp runs entirely on this machine: local daemon, local index stored inside the
|
|
@@ -2293,9 +2516,15 @@ treat it like a local build tool; no data-sharing consent is needed to call it.
|
|
|
2293
2516
|
- \`run_pipeline\` - **PRIMARY TOOL**. Runs capsule + impact + memory in 1 call.
|
|
2294
2517
|
Example: \`run_pipeline({ "task": "fix JWT expiry in AuthService.validateToken" })\`
|
|
2295
2518
|
- \`get_skeleton\` - compact file structure
|
|
2519
|
+
- \`verify_done\` - call once BEFORE declaring a multi-file task complete:
|
|
2520
|
+
mechanically broken references and untouched dependents, with file:line.
|
|
2296
2521
|
- \`index_status\` - indexing status
|
|
2297
2522
|
- \`expand_vexp_ref\` - expand V-REF placeholders in v2 output
|
|
2298
2523
|
|
|
2524
|
+
(Recommended core set, not the full schema: paid plans advertise 14 MCP tools -
|
|
2525
|
+
capsule, impact graph, logic flow, memory and more. \`run_pipeline\` already runs
|
|
2526
|
+
those server-side, so the four above cover the normal workflow.)
|
|
2527
|
+
|
|
2299
2528
|
${QUERY_SHAPE}
|
|
2300
2529
|
|
|
2301
2530
|
## Agentic search
|
|
@@ -2315,6 +2544,8 @@ function genericTemplate(vars) {
|
|
|
2315
2544
|
return `## vexp - Context-Aware AI Coding <!-- vexp v${vars.version} -->
|
|
2316
2545
|
|
|
2317
2546
|
### Context strategy: call run_pipeline ONCE at task start
|
|
2547
|
+
If the task already names the files/symbols to touch, skip vexp and work
|
|
2548
|
+
with your normal tools - a skipped call costs nothing.
|
|
2318
2549
|
For every multi-file task - bug fixes, features, refactors, questions about the code:
|
|
2319
2550
|
**start with one \`run_pipeline\` call**. It runs context search + impact analysis +
|
|
2320
2551
|
memory recall in a single call and returns compressed, graph-ranked results: the
|
|
@@ -2328,6 +2559,9 @@ Then implement with your normal tools:
|
|
|
2328
2559
|
need to understand, not edit.
|
|
2329
2560
|
- Do NOT open files one by one to find your way around - one good pipeline call
|
|
2330
2561
|
beats five exploratory reads; every extra tool call costs a turn.
|
|
2562
|
+
- Before declaring a multi-file task complete, call \`verify_done\` once:
|
|
2563
|
+
it returns mechanically broken references (imports of removed names,
|
|
2564
|
+
parse errors) and untouched dependents of your changes, with file:line.
|
|
2331
2565
|
|
|
2332
2566
|
vexp runs entirely on this machine: local daemon, local index stored inside the
|
|
2333
2567
|
workspace (\`.vexp/\`). \`run_pipeline\` transmits nothing to any external service -
|
package/dist/cli.js
CHANGED
|
@@ -434,6 +434,21 @@ program
|
|
|
434
434
|
args.push("--export");
|
|
435
435
|
runBinary(binaryPath, args);
|
|
436
436
|
});
|
|
437
|
+
program
|
|
438
|
+
.command("shield <action>")
|
|
439
|
+
.description("PII/secret shield: 'scan' reports what comments and string literals would hand to any AI agent (free, read-only, local)")
|
|
440
|
+
.option("--json", "Emit machine-readable JSON")
|
|
441
|
+
.action(async (action, opts) => {
|
|
442
|
+
if (action !== "scan") {
|
|
443
|
+
console.error(`Unknown shield action '${action}'. Available: scan`);
|
|
444
|
+
process.exit(1);
|
|
445
|
+
}
|
|
446
|
+
const binaryPath = ensureBinary();
|
|
447
|
+
const args = ["shield", "scan"];
|
|
448
|
+
if (opts.json)
|
|
449
|
+
args.push("--json");
|
|
450
|
+
runBinary(binaryPath, args);
|
|
451
|
+
});
|
|
437
452
|
program
|
|
438
453
|
.command("compress <file>")
|
|
439
454
|
.description("Compress a markdown/text file using prose compression (backup to <name>.original.md)")
|