vexp-cli 1.3.11 → 2.0.3
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 +50 -8
- package/dist/agent-config.js +66 -70
- package/dist/binary.js +18 -1
- package/dist/cli.js +171 -11
- package/dist/hook-template.js +0 -1
- package/dist/license.js +263 -16
- package/dist/update-check.js +0 -1
- package/dist/version.js +0 -1
- package/mcp/mcp-server.cjs +7 -43269
- package/package.json +10 -9
- package/dist/agent-config.d.ts +0 -37
- package/dist/agent-config.js.map +0 -1
- package/dist/binary.d.ts +0 -8
- package/dist/binary.js.map +0 -1
- package/dist/cli.d.ts +0 -2
- package/dist/cli.js.map +0 -1
- package/dist/hook-template.d.ts +0 -6
- package/dist/hook-template.js.map +0 -1
- package/dist/license.d.ts +0 -24
- package/dist/license.js.map +0 -1
- package/dist/update-check.d.ts +0 -1
- package/dist/update-check.js.map +0 -1
- package/dist/version.d.ts +0 -1
- package/dist/version.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
# vexp — Context Engine for AI Coding Agents
|
|
2
2
|
|
|
3
|
-
**
|
|
3
|
+
**58% Less Cost. 90% Fewer Tool Calls. 100% On Your Machine.**
|
|
4
4
|
|
|
5
5
|
vexp is a local-first context engine that gives AI coding agents the right code context — automatically. One install, and every agent gets smarter answers with fewer tokens.
|
|
6
6
|
|
|
7
|
-
Works with **
|
|
7
|
+
Works with **14+ AI coding agents** and **30 programming languages** out of the box.
|
|
8
|
+
|
|
9
|
+
→ [SWE-bench Verified: 73% pass@1 at $0.67/task](https://vexp.dev/benchmark)
|
|
8
10
|
|
|
9
11
|
## Why vexp?
|
|
10
12
|
|
|
11
|
-
AI coding agents
|
|
13
|
+
AI coding agents waste more than half their budget exploring codebases before writing a single line of code — 23 tool calls on average just to orient themselves. vexp pre-indexes your project into a dependency graph and delivers only the code that matters, ranked by relevance.
|
|
12
14
|
|
|
15
|
+
- **23 tool calls → 2** — the agent stops guessing and starts coding
|
|
16
|
+
- **$0.78 → $0.33 per task** — measured on SWE-bench Verified
|
|
17
|
+
- **117s → 74s per task** — less context to process = faster answers
|
|
13
18
|
- **Local-first** — your code never leaves your machine
|
|
14
19
|
- **Zero config** — auto-detects your agents and configures MCP
|
|
15
20
|
- **Universal** — works with any agent that supports MCP or custom rules
|
|
21
|
+
- **Optional local AI** — unlock +30% extra savings with `vexp-devmind`, a ~3.5 GB model that runs 100% on your machine. GPU-accelerated on NVIDIA (CUDA) and Apple Silicon (Metal). Opt in with one command.
|
|
16
22
|
|
|
17
23
|
## Supported AI Coding Agents
|
|
18
24
|
|
|
@@ -26,11 +32,15 @@ AI coding agents dump entire files into their context window, burning tokens and
|
|
|
26
32
|
| Augment | ✓ |
|
|
27
33
|
| Zed | ✓ |
|
|
28
34
|
| Codex (OpenAI) | ✓ |
|
|
35
|
+
| Cline | ✓ |
|
|
36
|
+
| Aider | ✓ |
|
|
29
37
|
| Kilo Code | ✓ |
|
|
30
38
|
| Kiro | ✓ |
|
|
31
39
|
| Antigravity | ✓ |
|
|
32
40
|
| Opencode | ✓ |
|
|
33
41
|
|
|
42
|
+
Your agents don't compete with each other — they all share the same context layer.
|
|
43
|
+
|
|
34
44
|
## Supported Languages (30)
|
|
35
45
|
|
|
36
46
|
TypeScript, JavaScript, Python, Go, Rust, Java, C#, C, C++, Ruby, Kotlin, Scala, Swift, Dart, Elixir, Haskell, OCaml, Lua, R, PHP, Zig, HCL/Terraform, Objective-C, Bash/Shell, Dockerfile, Clojure, F#, SQL, HTML/CSS, YAML/JSON
|
|
@@ -44,18 +54,50 @@ vexp
|
|
|
44
54
|
|
|
45
55
|
That's it. The interactive CLI guides you through setup, indexing, and agent configuration.
|
|
46
56
|
|
|
57
|
+
## What You Get
|
|
58
|
+
|
|
59
|
+
- **Semantic + graph-ranked context** — finds the right symbols even when keywords don't match
|
|
60
|
+
- **Intent detection in 15 languages** — English, Italian, French, German, Spanish, Portuguese, Russian, Chinese, Japanese, Korean, Arabic, Hindi, Turkish, Dutch, Polish
|
|
61
|
+
- **Cross-session memory** — your agent picks up exactly where it left off, even days later
|
|
62
|
+
- **Stale-knowledge alerts** — when code changes, outdated memory is automatically flagged
|
|
63
|
+
- **Pattern & anti-pattern detection** — recurring changes surface as guidelines; dead-end explorations get flagged early
|
|
64
|
+
- **Change awareness** — files frequently modified together are automatically linked
|
|
65
|
+
- **Security filter** — `.env`, credentials, and SSH keys are excluded automatically
|
|
66
|
+
|
|
47
67
|
## Platforms
|
|
48
68
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
|
52
|
-
|
|
53
|
-
|
|
|
69
|
+
Native binaries — no runtime dependencies:
|
|
70
|
+
|
|
71
|
+
| Platform | Architecture | GPU Acceleration |
|
|
72
|
+
|----------|:-:|:-:|
|
|
73
|
+
| Linux | x64, ARM64 | CUDA (NVIDIA) |
|
|
74
|
+
| macOS | x64, ARM64 (Apple Silicon) | Metal (built-in) |
|
|
75
|
+
| Windows | x64 | CUDA (NVIDIA) |
|
|
76
|
+
|
|
77
|
+
GPU acceleration applies to the optional local AI layer. Vexp's core context engine runs on any modern CPU.
|
|
54
78
|
|
|
55
79
|
## VS Code Extension
|
|
56
80
|
|
|
57
81
|
For the best experience, install the [vexp VS Code extension](https://marketplace.visualstudio.com/items?itemName=Vexp.vexp-vscode) — it manages the daemon automatically and adds a sidebar dashboard with token savings tracking.
|
|
58
82
|
|
|
83
|
+
## Local AI — Optional +30% Savings
|
|
84
|
+
|
|
85
|
+
Turn on `vexp-devmind` — a code-specialised AI model that runs entirely on your machine — for an extra +30% token savings on top of the baseline.
|
|
86
|
+
|
|
87
|
+
- **100% local** — the model never calls out. Your code stays on your hardware.
|
|
88
|
+
- **GPU-accelerated** — Metal on Apple Silicon, CUDA on NVIDIA. Auto-detected.
|
|
89
|
+
- **One-command install** — ~3.5 GB one-time download. Minimum 6 GB RAM.
|
|
90
|
+
- **Always up to date** — `--check-updates` compares your local version to the published release without downloading anything.
|
|
91
|
+
|
|
92
|
+
Sharper context ranking, higher-quality session memory, smarter prompt preprocessing. All without sending a single byte to any server.
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
vexp setup-llm --install # ~3.5 GB one-time download
|
|
96
|
+
vexp setup-llm --check-updates # no-op if already up to date
|
|
97
|
+
vexp setup-llm --status # show current config + hardware
|
|
98
|
+
vexp setup-llm --disable # turn it off any time
|
|
99
|
+
```
|
|
100
|
+
|
|
59
101
|
## Links
|
|
60
102
|
|
|
61
103
|
- [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=Vexp.vexp-vscode)
|
package/dist/agent-config.js
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import * as fs from "fs";
|
|
10
10
|
import * as path from "path";
|
|
11
11
|
import * as os from "os";
|
|
12
|
+
import * as crypto from "crypto";
|
|
12
13
|
import { VEXP_GUARD_HOOK } from "./hook-template.js";
|
|
13
14
|
// ---------------------------------------------------------------------------
|
|
14
15
|
// Constants
|
|
@@ -206,9 +207,9 @@ export function configureAgents(workspaceRoot, binaryPath, version, agentFilter,
|
|
|
206
207
|
});
|
|
207
208
|
}
|
|
208
209
|
}
|
|
209
|
-
// Codex: configure MCP in ~/.codex/config.toml (global,
|
|
210
|
+
// Codex: configure MCP in ~/.codex/config.toml (global, HTTP + bearer token)
|
|
210
211
|
if (detector.agent === "Codex") {
|
|
211
|
-
const wrote = configureCodexGlobal(binaryPath, mcpServerPath);
|
|
212
|
+
const wrote = configureCodexGlobal(binaryPath, mcpServerPath, workspaceRoot);
|
|
212
213
|
if (wrote)
|
|
213
214
|
mcpConfigs.push("~/.codex/config.toml");
|
|
214
215
|
}
|
|
@@ -315,7 +316,7 @@ export function configureSelectedAgents(workspaceRoot, binaryPath, version, sele
|
|
|
315
316
|
installClaudeCodeHook(workspaceRoot);
|
|
316
317
|
}
|
|
317
318
|
if (detector.agent === "Codex") {
|
|
318
|
-
const wrote = configureCodexGlobal(binaryPath, mcpServerPath);
|
|
319
|
+
const wrote = configureCodexGlobal(binaryPath, mcpServerPath, workspaceRoot);
|
|
319
320
|
if (wrote)
|
|
320
321
|
mcpConfigs.push("~/.codex/config.toml");
|
|
321
322
|
}
|
|
@@ -449,33 +450,74 @@ function writeMcpConfig(mcpConfigPath, binaryPath, alwaysAllow, mcpServerPath) {
|
|
|
449
450
|
fs.writeFileSync(mcpConfigPath, JSON.stringify(existing, null, 2), "utf-8");
|
|
450
451
|
return true;
|
|
451
452
|
}
|
|
453
|
+
/**
|
|
454
|
+
* Read or generate a bearer token for MCP HTTP auth.
|
|
455
|
+
* Persists to ~/.vexp/mcp.token so the MCP server and Codex config share it.
|
|
456
|
+
*/
|
|
457
|
+
function resolveCodexToken() {
|
|
458
|
+
const home = os.homedir();
|
|
459
|
+
const tokenPath = home ? path.join(home, ".vexp", "mcp.token") : "";
|
|
460
|
+
if (tokenPath) {
|
|
461
|
+
try {
|
|
462
|
+
const existing = fs.readFileSync(tokenPath, "utf-8").trim();
|
|
463
|
+
if (existing)
|
|
464
|
+
return existing;
|
|
465
|
+
}
|
|
466
|
+
catch { /* not yet created */ }
|
|
467
|
+
}
|
|
468
|
+
const token = crypto.randomUUID();
|
|
469
|
+
if (tokenPath) {
|
|
470
|
+
try {
|
|
471
|
+
fs.mkdirSync(path.dirname(tokenPath), { recursive: true });
|
|
472
|
+
fs.writeFileSync(tokenPath, token, { mode: 0o600 });
|
|
473
|
+
}
|
|
474
|
+
catch { /* non-fatal */ }
|
|
475
|
+
}
|
|
476
|
+
return token;
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* Read the MCP HTTP port from .vexp/mcp.port, or return the default.
|
|
480
|
+
*/
|
|
481
|
+
function readMcpPort(workspaceRoot) {
|
|
482
|
+
const DEFAULT_PORT = 7821;
|
|
483
|
+
if (workspaceRoot) {
|
|
484
|
+
try {
|
|
485
|
+
const portStr = fs.readFileSync(path.join(workspaceRoot, ".vexp", "mcp.port"), "utf-8").trim();
|
|
486
|
+
const port = parseInt(portStr, 10);
|
|
487
|
+
if (port > 0)
|
|
488
|
+
return port;
|
|
489
|
+
}
|
|
490
|
+
catch { /* file doesn't exist */ }
|
|
491
|
+
}
|
|
492
|
+
return DEFAULT_PORT;
|
|
493
|
+
}
|
|
452
494
|
/**
|
|
453
495
|
* Configure MCP in ~/.codex/config.toml (global).
|
|
454
496
|
* Codex only reads the global config, not project-level .codex/config.toml.
|
|
497
|
+
* Uses HTTP transport with bearer token so Codex shows "Auth: BearerToken"
|
|
498
|
+
* instead of "Auth: Unsupported" (which is hardcoded for stdio servers).
|
|
455
499
|
* Uses TOML format with [mcp_servers.<name>] sections.
|
|
456
500
|
*/
|
|
457
|
-
function configureCodexGlobal(binaryPath, mcpServerPath) {
|
|
501
|
+
function configureCodexGlobal(binaryPath, mcpServerPath, workspaceRoot) {
|
|
458
502
|
const home = os.homedir();
|
|
459
503
|
if (!home)
|
|
460
504
|
return false;
|
|
461
505
|
const configPath = path.join(home, ".codex", "config.toml");
|
|
506
|
+
const mcpPort = readMcpPort(workspaceRoot);
|
|
507
|
+
const token = resolveCodexToken();
|
|
508
|
+
const newSection = `\n[mcp_servers.vexp]\nurl = "http://127.0.0.1:${mcpPort}/mcp"\ntool_timeout_sec = 120\n\n[mcp_servers.vexp.http_headers]\nAuthorization = "Bearer ${token}"\n`;
|
|
462
509
|
let content = "";
|
|
463
510
|
if (fs.existsSync(configPath)) {
|
|
464
511
|
content = fs.readFileSync(configPath, "utf-8");
|
|
465
512
|
}
|
|
466
513
|
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
467
|
-
const useNode = mcpServerPath && fs.existsSync(mcpServerPath);
|
|
468
|
-
const cmd = useNode ? "node" : binaryPath;
|
|
469
|
-
const arg = useNode ? mcpServerPath : "mcp";
|
|
470
|
-
const escapedCmd = cmd.replace(/\\/g, "\\\\");
|
|
471
|
-
const escapedArg = arg.replace(/\\/g, "\\\\");
|
|
472
|
-
const newSection = `\n[mcp_servers.vexp]\ncommand = "${escapedCmd}"\nargs = ["${escapedArg}"]\nstartup_timeout_sec = 30\ntool_timeout_sec = 120\n`;
|
|
473
514
|
if (content.includes("[mcp_servers.vexp]")) {
|
|
474
|
-
//
|
|
475
|
-
if (content.includes(`
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
515
|
+
// Check if already up to date (same URL and token)
|
|
516
|
+
if (content.includes(`url = "http://127.0.0.1:${mcpPort}/mcp"`) &&
|
|
517
|
+
content.includes(`Authorization = "Bearer ${token}"`))
|
|
518
|
+
return false;
|
|
519
|
+
// Remove old section(s) — handles both stdio (command=) and http (url=) formats
|
|
520
|
+
content = content.replace(/\n?\[mcp_servers\.vexp(?:\.http_headers)?\][^\[]*/g, "");
|
|
479
521
|
}
|
|
480
522
|
content += newSection;
|
|
481
523
|
fs.writeFileSync(configPath, content, "utf-8");
|
|
@@ -716,14 +758,9 @@ Only use Read when you need exact raw content to edit a specific line.
|
|
|
716
758
|
- \`run_pipeline({ "task": "add auth", "observation": "using JWT" })\` — save insight in same call
|
|
717
759
|
|
|
718
760
|
### Other MCP tools (use only when run_pipeline is insufficient)
|
|
719
|
-
- \`get_context_capsule\` — lightweight alternative for simple questions only
|
|
720
|
-
- \`get_impact_graph\` — standalone deep impact analysis of a specific symbol
|
|
721
|
-
- \`search_logic_flow\` — trace execution paths between two specific symbols
|
|
722
761
|
- \`get_skeleton\` — **preferred over Read** for inspecting files (minimal/standard/detailed detail levels, 70-90% token savings)
|
|
723
762
|
- \`index_status\` — indexing status and health check
|
|
724
|
-
- \`
|
|
725
|
-
- \`search_memory\` — cross-session search for past decisions
|
|
726
|
-
- \`save_observation\` — persist insights (prefer using run_pipeline's observation param instead)
|
|
763
|
+
- \`expand_vexp_ref\` — expand V-REF hash placeholders in v2 compact output
|
|
727
764
|
|
|
728
765
|
### Workflow
|
|
729
766
|
1. \`run_pipeline("your task")\` — ALWAYS FIRST. Returns pivots + impact + memories in 1 call
|
|
@@ -776,14 +813,9 @@ vexp returns pre-indexed, graph-ranked context in a single call.
|
|
|
776
813
|
### Available MCP tools
|
|
777
814
|
- \`run_pipeline\` — **PRIMARY TOOL**. Runs capsule + impact + memory in 1 call.
|
|
778
815
|
Auto-detects intent. Includes file content. Example: \`run_pipeline({ "task": "fix auth bug" })\`
|
|
779
|
-
- \`get_context_capsule\` — lightweight, for simple questions only
|
|
780
|
-
- \`get_impact_graph\` — impact analysis of a specific symbol
|
|
781
|
-
- \`search_logic_flow\` — execution paths between functions
|
|
782
816
|
- \`get_skeleton\` — compact file structure
|
|
783
817
|
- \`index_status\` — indexing status
|
|
784
|
-
- \`
|
|
785
|
-
- \`search_memory\` — cross-session search
|
|
786
|
-
- \`save_observation\` — persist insights (prefer run_pipeline's observation param)
|
|
818
|
+
- \`expand_vexp_ref\` — expand V-REF placeholders in v2 output
|
|
787
819
|
|
|
788
820
|
### Agentic search
|
|
789
821
|
- Do NOT use built-in file search, grep, or codebase indexing — always call \`run_pipeline\` first
|
|
@@ -820,14 +852,9 @@ vexp returns pre-indexed, graph-ranked context in a single call.
|
|
|
820
852
|
### Available MCP tools
|
|
821
853
|
- \`run_pipeline\` — **PRIMARY TOOL**. Runs capsule + impact + memory in 1 call.
|
|
822
854
|
Auto-detects intent. Includes file content. Example: \`run_pipeline({ "task": "fix auth bug" })\`
|
|
823
|
-
- \`get_context_capsule\` — lightweight, for simple questions only
|
|
824
|
-
- \`get_impact_graph\` — impact analysis of a specific symbol
|
|
825
|
-
- \`search_logic_flow\` — execution paths between functions
|
|
826
855
|
- \`get_skeleton\` — compact file structure
|
|
827
856
|
- \`index_status\` — indexing status
|
|
828
|
-
- \`
|
|
829
|
-
- \`search_memory\` — cross-session search
|
|
830
|
-
- \`save_observation\` — persist insights (prefer run_pipeline's observation param)
|
|
857
|
+
- \`expand_vexp_ref\` — expand V-REF placeholders in v2 output
|
|
831
858
|
|
|
832
859
|
### Agentic search
|
|
833
860
|
- Do NOT use built-in file search, grep, or codebase indexing — always call \`run_pipeline\` first
|
|
@@ -874,14 +901,9 @@ When working on this codebase:
|
|
|
874
901
|
### Available MCP tools
|
|
875
902
|
- \`run_pipeline\` — **PRIMARY TOOL**. Runs capsule + impact + memory in 1 call.
|
|
876
903
|
Example: \`run_pipeline({ "task": "fix auth bug" })\`
|
|
877
|
-
- \`get_context_capsule\` — lightweight, for simple questions only
|
|
878
|
-
- \`get_impact_graph\` — standalone impact analysis
|
|
879
|
-
- \`search_logic_flow\` — execution paths between functions
|
|
880
904
|
- \`get_skeleton\` — token-efficient file structure
|
|
881
905
|
- \`index_status\` — indexing status
|
|
882
|
-
- \`
|
|
883
|
-
- \`search_memory\` — cross-session search
|
|
884
|
-
- \`save_observation\` — persist insights (prefer run_pipeline's observation param)
|
|
906
|
+
- \`expand_vexp_ref\` — expand V-REF placeholders in v2 output
|
|
885
907
|
|
|
886
908
|
### Agentic search
|
|
887
909
|
- Do NOT use built-in file search, grep, or codebase indexing — always call \`run_pipeline\` first
|
|
@@ -909,14 +931,9 @@ vexp returns pre-indexed, graph-ranked context in a single call.
|
|
|
909
931
|
### Available MCP tools
|
|
910
932
|
- \`run_pipeline\` — **PRIMARY TOOL**. Runs capsule + impact + memory in 1 call.
|
|
911
933
|
Auto-detects intent. Includes file content. Example: \`run_pipeline({ "task": "fix auth bug" })\`
|
|
912
|
-
- \`get_context_capsule\` — lightweight, for simple questions only
|
|
913
|
-
- \`get_impact_graph\` — impact analysis of a specific symbol
|
|
914
|
-
- \`search_logic_flow\` — execution paths between functions
|
|
915
934
|
- \`get_skeleton\` — compact file structure
|
|
916
935
|
- \`index_status\` — indexing status
|
|
917
|
-
- \`
|
|
918
|
-
- \`search_memory\` — cross-session search
|
|
919
|
-
- \`save_observation\` — persist insights (prefer run_pipeline's observation param)
|
|
936
|
+
- \`expand_vexp_ref\` — expand V-REF placeholders in v2 output
|
|
920
937
|
|
|
921
938
|
### Agentic search
|
|
922
939
|
- Do NOT use built-in file search, grep, or codebase indexing — always call \`run_pipeline\` first
|
|
@@ -944,14 +961,9 @@ vexp returns pre-indexed, graph-ranked context in a single call.
|
|
|
944
961
|
### Available MCP tools
|
|
945
962
|
- \`run_pipeline\` — **PRIMARY TOOL**. Runs capsule + impact + memory in 1 call.
|
|
946
963
|
Example: \`run_pipeline({ "task": "fix auth bug" })\`
|
|
947
|
-
- \`get_context_capsule\` — lightweight, for simple questions only
|
|
948
|
-
- \`get_impact_graph\` — impact analysis of a specific symbol
|
|
949
|
-
- \`search_logic_flow\` — execution paths between functions
|
|
950
964
|
- \`get_skeleton\` — compact file structure
|
|
951
965
|
- \`index_status\` — indexing status
|
|
952
|
-
- \`
|
|
953
|
-
- \`search_memory\` — cross-session search
|
|
954
|
-
- \`save_observation\` — persist insights (prefer run_pipeline's observation param)
|
|
966
|
+
- \`expand_vexp_ref\` — expand V-REF placeholders in v2 output
|
|
955
967
|
|
|
956
968
|
### Agentic search
|
|
957
969
|
- Do NOT use built-in file search, grep, or codebase indexing — always call \`run_pipeline\` first
|
|
@@ -979,14 +991,9 @@ vexp returns pre-indexed, graph-ranked context in a single call.
|
|
|
979
991
|
### Available MCP tools
|
|
980
992
|
- \`run_pipeline\` — **PRIMARY TOOL**. Runs capsule + impact + memory in 1 call.
|
|
981
993
|
Auto-detects intent. Includes file content. Example: \`run_pipeline({ "task": "fix auth bug" })\`
|
|
982
|
-
- \`get_context_capsule\` — lightweight, for simple questions only
|
|
983
|
-
- \`get_impact_graph\` — impact analysis of a specific symbol
|
|
984
|
-
- \`search_logic_flow\` — execution paths between functions
|
|
985
994
|
- \`get_skeleton\` — compact file structure
|
|
986
995
|
- \`index_status\` — indexing status
|
|
987
|
-
- \`
|
|
988
|
-
- \`search_memory\` — cross-session search
|
|
989
|
-
- \`save_observation\` — persist insights (prefer run_pipeline's observation param)
|
|
996
|
+
- \`expand_vexp_ref\` — expand V-REF placeholders in v2 output
|
|
990
997
|
|
|
991
998
|
### Agentic search
|
|
992
999
|
- Do NOT use built-in file search, grep, or codebase indexing — always call \`run_pipeline\` first
|
|
@@ -1014,14 +1021,9 @@ vexp returns pre-indexed, graph-ranked context in a single call.
|
|
|
1014
1021
|
## Available vexp tools
|
|
1015
1022
|
- \`run_pipeline\` — **PRIMARY TOOL**. Runs capsule + impact + memory in 1 call.
|
|
1016
1023
|
Example: \`run_pipeline({ "task": "fix auth bug" })\`
|
|
1017
|
-
- \`get_context_capsule\` — lightweight, for simple questions only
|
|
1018
|
-
- \`get_impact_graph\` — impact analysis of a specific symbol
|
|
1019
|
-
- \`search_logic_flow\` — trace execution paths
|
|
1020
1024
|
- \`get_skeleton\` — compact file structure
|
|
1021
1025
|
- \`index_status\` — indexing status
|
|
1022
|
-
- \`
|
|
1023
|
-
- \`search_memory\` — cross-session search
|
|
1024
|
-
- \`save_observation\` — persist insights (prefer run_pipeline's observation param)
|
|
1026
|
+
- \`expand_vexp_ref\` — expand V-REF placeholders in v2 output
|
|
1025
1027
|
|
|
1026
1028
|
## Agentic search
|
|
1027
1029
|
- Do NOT use built-in file search, grep, or codebase indexing — always call \`run_pipeline\` first
|
|
@@ -1049,14 +1051,9 @@ vexp returns pre-indexed, graph-ranked context in a single call.
|
|
|
1049
1051
|
### Available MCP tools
|
|
1050
1052
|
- \`run_pipeline\` — **PRIMARY TOOL**. Runs capsule + impact + memory in 1 call.
|
|
1051
1053
|
Example: \`run_pipeline({ "task": "fix auth bug" })\`
|
|
1052
|
-
- \`get_context_capsule\` — lightweight, for simple questions only
|
|
1053
|
-
- \`get_impact_graph\` — impact analysis of a specific symbol
|
|
1054
|
-
- \`search_logic_flow\` — execution paths between functions
|
|
1055
1054
|
- \`get_skeleton\` — compact file structure
|
|
1056
1055
|
- \`index_status\` — indexing status
|
|
1057
|
-
- \`
|
|
1058
|
-
- \`search_memory\` — cross-session search
|
|
1059
|
-
- \`save_observation\` — persist insights (prefer run_pipeline's observation param)
|
|
1056
|
+
- \`expand_vexp_ref\` — expand V-REF placeholders in v2 output
|
|
1060
1057
|
|
|
1061
1058
|
### Agentic search
|
|
1062
1059
|
- Do NOT use built-in file search, grep, or codebase indexing — always call \`run_pipeline\` first
|
|
@@ -1070,4 +1067,3 @@ Intent auto-detection, hybrid ranking, session memory, auto-expanding budget.
|
|
|
1070
1067
|
\`run_pipeline\` auto-queries all indexed repos. Use \`repos: ["alias"]\` to scope. Run \`index_status\` to see aliases.
|
|
1071
1068
|
<!-- /vexp -->`;
|
|
1072
1069
|
}
|
|
1073
|
-
//# sourceMappingURL=agent-config.js.map
|
package/dist/binary.js
CHANGED
|
@@ -10,6 +10,24 @@ const PLATFORM_PACKAGES = {
|
|
|
10
10
|
"win32-x64": "@vexp/core-win32-x64",
|
|
11
11
|
"win32-arm64": "@vexp/core-win32-x64", // ARM64 Windows uses x64 binary via emulation
|
|
12
12
|
};
|
|
13
|
+
// A single core binary per OS/arch decides at RUNTIME which backend to
|
|
14
|
+
// use (CPU, Metal, CUDA) via ggml's dynamic plugin loader. The CUDA
|
|
15
|
+
// plugin is downloaded on-demand by `vexp setup-llm` when an NVIDIA
|
|
16
|
+
// driver is detected — no separate GPU binary package.
|
|
17
|
+
//
|
|
18
|
+
// The `--gpu` / `--cuda` flags and VEXP_GPU / VEXP_CUDA env vars are
|
|
19
|
+
// forwarded to the Rust core as VEXP_PREFER_GPU=1, which flips
|
|
20
|
+
// prefer_gpu=true on the llama.cpp load. If no GPU plugin is present,
|
|
21
|
+
// llama.cpp silently falls back to CPU.
|
|
22
|
+
function gpuRequested() {
|
|
23
|
+
return (process.env.VEXP_GPU === "1" ||
|
|
24
|
+
process.env.VEXP_CUDA === "1" ||
|
|
25
|
+
process.argv.includes("--gpu") ||
|
|
26
|
+
process.argv.includes("--cuda"));
|
|
27
|
+
}
|
|
28
|
+
export function preferGpuEnv() {
|
|
29
|
+
return gpuRequested() ? { VEXP_PREFER_GPU: "1" } : {};
|
|
30
|
+
}
|
|
13
31
|
export function getBinaryPath() {
|
|
14
32
|
// Dev override: use VEXP_CORE_PATH env var to point to a local binary
|
|
15
33
|
const envPath = process.env.VEXP_CORE_PATH;
|
|
@@ -93,4 +111,3 @@ export async function getInstalledVersion() {
|
|
|
93
111
|
return null;
|
|
94
112
|
}
|
|
95
113
|
}
|
|
96
|
-
//# sourceMappingURL=binary.js.map
|
package/dist/cli.js
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
import { Command } from "commander";
|
|
3
3
|
import chalk from "chalk";
|
|
4
4
|
import ora from "ora";
|
|
5
|
-
import { spawn, spawnSync } from "child_process";
|
|
5
|
+
import { spawn, spawnSync, execFileSync } from "child_process";
|
|
6
6
|
import * as path from "path";
|
|
7
7
|
import * as fs from "fs";
|
|
8
8
|
import { checkbox } from "@inquirer/prompts";
|
|
9
9
|
import { getBinaryPath, getInstalledVersion, getMcpServerPath } from "./binary.js";
|
|
10
10
|
import { detectAgents, getAgentList, configureSelectedAgents } from "./agent-config.js";
|
|
11
11
|
import { CLI_VERSION } from "./version.js";
|
|
12
|
-
import { activateLicense, deactivateLicense, readLicenseLimits, } from "./license.js";
|
|
12
|
+
import { activateLicense, deactivateLicense, readLicenseLimits, readDeviceBlocked, } from "./license.js";
|
|
13
13
|
import { checkForUpdate } from "./update-check.js";
|
|
14
14
|
const program = new Command();
|
|
15
15
|
program
|
|
@@ -163,14 +163,57 @@ program
|
|
|
163
163
|
.command("savings")
|
|
164
164
|
.description("Show token savings statistics")
|
|
165
165
|
.option("--days <n>", "Number of days to analyze", "30")
|
|
166
|
+
.option("--today", "Show savings for today only (since local midnight)")
|
|
166
167
|
.option("--export", "Export report to .vexp/token-savings-report.md")
|
|
167
168
|
.action(async (opts) => {
|
|
168
169
|
const binaryPath = ensureBinary();
|
|
169
|
-
const args = ["savings"
|
|
170
|
+
const args = ["savings"];
|
|
171
|
+
if (opts.today) {
|
|
172
|
+
// Compute local midnight in unix seconds so the Rust core aggregates
|
|
173
|
+
// only events from today in the caller's timezone.
|
|
174
|
+
const midnight = new Date();
|
|
175
|
+
midnight.setHours(0, 0, 0, 0);
|
|
176
|
+
const sinceTs = Math.floor(midnight.getTime() / 1000);
|
|
177
|
+
args.push("--since-ts", String(sinceTs), "--label", "today");
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
args.push("--days", opts.days);
|
|
181
|
+
}
|
|
170
182
|
if (opts.export)
|
|
171
183
|
args.push("--export");
|
|
172
184
|
runBinary(binaryPath, args);
|
|
173
185
|
});
|
|
186
|
+
program
|
|
187
|
+
.command("compress <file>")
|
|
188
|
+
.description("Compress a markdown/text file using caveman-style prose compression (backup to <name>.original.md)")
|
|
189
|
+
.option("--level <lvl>", "Compression level: off | lite | full | ultra", "full")
|
|
190
|
+
.action(async (file, opts) => {
|
|
191
|
+
const binaryPath = ensureBinary();
|
|
192
|
+
runBinary(binaryPath, ["compress", file, "--level", opts.level]);
|
|
193
|
+
});
|
|
194
|
+
program
|
|
195
|
+
.command("setup-llm")
|
|
196
|
+
.description("Configure optional local LLM compression (vexp-devmind). Runs interactive wizard by default.")
|
|
197
|
+
.option("--status", "Print current LLM config and readiness")
|
|
198
|
+
.option("--disable", "Disable LLM compression without removing model files")
|
|
199
|
+
.option("--install", "Non-interactive install (for CI / first-run)")
|
|
200
|
+
.option("--check-updates", "Check for model updates without downloading")
|
|
201
|
+
.option("--enable", "Enable a previously-installed LLM without re-downloading anything")
|
|
202
|
+
.action(async (opts) => {
|
|
203
|
+
const binaryPath = ensureBinary();
|
|
204
|
+
const args = ["setup-llm"];
|
|
205
|
+
if (opts.status)
|
|
206
|
+
args.push("--status");
|
|
207
|
+
if (opts.disable)
|
|
208
|
+
args.push("--disable");
|
|
209
|
+
if (opts.install)
|
|
210
|
+
args.push("--install");
|
|
211
|
+
if (opts.checkUpdates)
|
|
212
|
+
args.push("--check-updates");
|
|
213
|
+
if (opts.enable)
|
|
214
|
+
args.push("--enable");
|
|
215
|
+
runBinary(binaryPath, args);
|
|
216
|
+
});
|
|
174
217
|
program
|
|
175
218
|
.command("hooks <action>")
|
|
176
219
|
.description("Manage git hooks (install, check, remove)")
|
|
@@ -273,6 +316,46 @@ program
|
|
|
273
316
|
catch {
|
|
274
317
|
spinnerDaemon.warn("Daemon already running or failed to start (non-fatal)");
|
|
275
318
|
}
|
|
319
|
+
// Step 2.6: Start MCP HTTP server (needed for Codex and HTTP-based agents)
|
|
320
|
+
const mcpPath = getMcpServerPath();
|
|
321
|
+
if (mcpPath) {
|
|
322
|
+
const spinnerMcp = ora("Starting MCP HTTP server...").start();
|
|
323
|
+
try {
|
|
324
|
+
const { randomUUID } = await import("crypto");
|
|
325
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
326
|
+
let mcpToken = "";
|
|
327
|
+
const tokenPath = home ? path.join(home, ".vexp", "mcp.token") : "";
|
|
328
|
+
if (tokenPath) {
|
|
329
|
+
try {
|
|
330
|
+
mcpToken = fs.readFileSync(tokenPath, "utf-8").trim();
|
|
331
|
+
}
|
|
332
|
+
catch { }
|
|
333
|
+
}
|
|
334
|
+
if (!mcpToken) {
|
|
335
|
+
mcpToken = randomUUID();
|
|
336
|
+
if (tokenPath) {
|
|
337
|
+
fs.mkdirSync(path.dirname(tokenPath), { recursive: true });
|
|
338
|
+
fs.writeFileSync(tokenPath, mcpToken, { mode: 0o600 });
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
const mcpChild = spawn("node", [mcpPath, "--http", "--port=7821"], {
|
|
342
|
+
detached: true,
|
|
343
|
+
stdio: "ignore",
|
|
344
|
+
env: {
|
|
345
|
+
...process.env,
|
|
346
|
+
VEXP_WORKSPACE: workspaceRoot,
|
|
347
|
+
VEXP_HTTP: "1",
|
|
348
|
+
VEXP_PORT: "7821",
|
|
349
|
+
VEXP_MCP_TOKEN: mcpToken,
|
|
350
|
+
},
|
|
351
|
+
});
|
|
352
|
+
mcpChild.unref();
|
|
353
|
+
spinnerMcp.succeed("MCP HTTP server started (port 7821)");
|
|
354
|
+
}
|
|
355
|
+
catch {
|
|
356
|
+
spinnerMcp.warn("MCP HTTP server failed to start (non-fatal)");
|
|
357
|
+
}
|
|
358
|
+
}
|
|
276
359
|
// In personal mode, make .vexp/ fully gitignored
|
|
277
360
|
if (isPersonal) {
|
|
278
361
|
const vexpGitignore = path.join(workspaceRoot, ".vexp", ".gitignore");
|
|
@@ -541,6 +624,12 @@ program
|
|
|
541
624
|
console.log(` All tools: ${limits.allTools ? "yes" : "no (7/10)"}`);
|
|
542
625
|
if (limits.renewsAt)
|
|
543
626
|
console.log(` Renews: ${limits.renewsAt.toLocaleDateString()}`);
|
|
627
|
+
const blocked = readDeviceBlocked();
|
|
628
|
+
if (blocked) {
|
|
629
|
+
console.log("");
|
|
630
|
+
console.log(chalk.yellow(` ⚠ This machine is not registered (${blocked.maxDevices}/${blocked.maxDevices} slots used).`));
|
|
631
|
+
console.log(chalk.dim(` Free a slot at ${blocked.manageUrl} and run a vexp command\n to re-register this device.`));
|
|
632
|
+
}
|
|
544
633
|
console.log("");
|
|
545
634
|
});
|
|
546
635
|
// ────────────────────────────────────────────────────
|
|
@@ -562,6 +651,7 @@ const MENU_CONFIG = [
|
|
|
562
651
|
{ key: "3", label: "setup", hint: "Full setup (index + agents + hooks)", description: "One-command bootstrap: indexes the codebase, detects AI agents,\n writes MCP configs, installs git hooks." },
|
|
563
652
|
{ key: "4", label: "setup-agents", hint: "Choose which AI agents to configure", description: "Select agents (Claude, Cursor, Windsurf, Copilot, …) to configure.\n Creates config directories and writes MCP settings for each." },
|
|
564
653
|
{ key: "5", label: "daemon-cmd", hint: "Manage daemon (start/stop/status/logs)", description: "Controls the background daemon:\n start → launches the daemon (indexes + watches for changes)\n stop → stops the running daemon\n status → shows node/edge/file counts\n logs → prints .vexp/vexp.log" },
|
|
654
|
+
{ key: "6", label: "setup-llm", hint: "Configure local LLM compression (vexp-devmind)", description: "Install, update, or disable the optional local LLM (vexp-devmind).\n Runs entirely on your machine (~3.5 GB download).\n Enables smart prompt preprocessing and higher-quality memory compression." },
|
|
565
655
|
];
|
|
566
656
|
// ── Sub-menu: Explore & Analyze ──
|
|
567
657
|
const MENU_EXPLORE = [
|
|
@@ -592,10 +682,30 @@ function printBanner() {
|
|
|
592
682
|
console.log(chalk.dim(" ╚═══╝ ╚══════╝╚═╝ ╚═╝╚═╝") + chalk.dim(` v${version}`));
|
|
593
683
|
console.log("");
|
|
594
684
|
console.log(chalk.dim(" Context Engine for AI Coding Agents"));
|
|
685
|
+
// One-liner LLM status
|
|
686
|
+
try {
|
|
687
|
+
const llmBin = ensureBinary();
|
|
688
|
+
const stdout = execFileSync(llmBin, ["setup-llm", "--status"], {
|
|
689
|
+
timeout: 5000,
|
|
690
|
+
encoding: "utf-8",
|
|
691
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
692
|
+
});
|
|
693
|
+
const operational = /Operational:\s*true/.test(stdout);
|
|
694
|
+
const model = stdout.match(/Model:\s*(.+)/)?.[1]?.trim() ?? "";
|
|
695
|
+
if (operational) {
|
|
696
|
+
console.log(chalk.green(` ● LLM: ${model} (active)`));
|
|
697
|
+
}
|
|
698
|
+
else {
|
|
699
|
+
console.log(chalk.dim(" ○ LLM: not configured — type 'setup-llm' or press 1 > 6"));
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
catch {
|
|
703
|
+
// Binary not available or no local_llm feature — skip silently
|
|
704
|
+
}
|
|
595
705
|
}
|
|
596
706
|
function printMainMenu() {
|
|
597
707
|
console.log("");
|
|
598
|
-
console.log(` ${chalk.cyan.bold("1")} ${chalk.green("Setup & Config".padEnd(20))} ${chalk.dim("Initialize, index,
|
|
708
|
+
console.log(` ${chalk.cyan.bold("1")} ${chalk.green("Setup & Config".padEnd(20))} ${chalk.dim("Initialize, index, configure agents & LLM")}`);
|
|
599
709
|
console.log(` ${chalk.cyan.bold("2")} ${chalk.green("Explore & Analyze".padEnd(20))} ${chalk.dim("Query context, skeletons, impact, flow")}`);
|
|
600
710
|
console.log(` ${chalk.cyan.bold("3")} ${chalk.green("Token Savings".padEnd(20))} ${chalk.dim("View savings stats, export report")}`);
|
|
601
711
|
console.log(` ${chalk.cyan.bold("4")} ${chalk.green("License".padEnd(20))} ${chalk.dim("View or activate your license")}`);
|
|
@@ -732,6 +842,41 @@ async function executeCommand(label, rl) {
|
|
|
732
842
|
}
|
|
733
843
|
break;
|
|
734
844
|
}
|
|
845
|
+
case "setup-llm": {
|
|
846
|
+
console.log("");
|
|
847
|
+
console.log(chalk.cyan(" Local LLM compression (vexp-devmind)"));
|
|
848
|
+
console.log(chalk.dim(" All processing runs locally — no data leaves your machine.\n"));
|
|
849
|
+
console.log(` ${chalk.bold("1")} Install / Reinstall (~3.5 GB download)`);
|
|
850
|
+
console.log(` ${chalk.bold("2")} Start (enable without re-downloading)`);
|
|
851
|
+
console.log(` ${chalk.bold("3")} Check for updates (no download if up to date)`);
|
|
852
|
+
console.log(` ${chalk.bold("4")} Status (show current config + hardware)`);
|
|
853
|
+
console.log(` ${chalk.bold("5")} Disable (keep files, stop using LLM)`);
|
|
854
|
+
console.log(` ${chalk.dim("b")} ${chalk.dim("Cancel")}`);
|
|
855
|
+
console.log("");
|
|
856
|
+
const llmChoice = await ask(rl, chalk.cyan(" Choose: "));
|
|
857
|
+
const llmBinary = ensureBinary();
|
|
858
|
+
switch (llmChoice.trim()) {
|
|
859
|
+
case "1":
|
|
860
|
+
console.log(chalk.dim(" Starting LLM setup (this may take several minutes)…\n"));
|
|
861
|
+
runBinary(llmBinary, ["setup-llm", "--install"]);
|
|
862
|
+
break;
|
|
863
|
+
case "2":
|
|
864
|
+
runBinary(llmBinary, ["setup-llm", "--enable"]);
|
|
865
|
+
break;
|
|
866
|
+
case "3":
|
|
867
|
+
runBinary(llmBinary, ["setup-llm", "--check-updates"]);
|
|
868
|
+
break;
|
|
869
|
+
case "4":
|
|
870
|
+
runBinary(llmBinary, ["setup-llm", "--status"]);
|
|
871
|
+
break;
|
|
872
|
+
case "5":
|
|
873
|
+
runBinary(llmBinary, ["setup-llm", "--disable"]);
|
|
874
|
+
break;
|
|
875
|
+
default:
|
|
876
|
+
console.log(chalk.dim(" Cancelled."));
|
|
877
|
+
}
|
|
878
|
+
break;
|
|
879
|
+
}
|
|
735
880
|
// ── Explore commands ──
|
|
736
881
|
case "capsule": {
|
|
737
882
|
const query = await ask(rl, chalk.cyan(" Describe your task: "));
|
|
@@ -772,17 +917,33 @@ async function executeCommand(label, rl) {
|
|
|
772
917
|
}
|
|
773
918
|
case "savings": // fallthrough from direct command
|
|
774
919
|
case "summary": {
|
|
775
|
-
const
|
|
776
|
-
const days = parseInt(daysInput.trim(), 10) || 30;
|
|
920
|
+
const input = await ask(rl, chalk.cyan(" Period (today / 7 / 14 / 30 / 60 / 90): "));
|
|
777
921
|
const binaryPath = ensureBinary();
|
|
778
|
-
|
|
922
|
+
const trimmed = input.trim().toLowerCase();
|
|
923
|
+
if (trimmed === "today") {
|
|
924
|
+
const midnight = new Date();
|
|
925
|
+
midnight.setHours(0, 0, 0, 0);
|
|
926
|
+
runBinary(binaryPath, ["savings", "--since-ts", String(Math.floor(midnight.getTime() / 1000)), "--label", "today"]);
|
|
927
|
+
}
|
|
928
|
+
else {
|
|
929
|
+
const days = parseInt(trimmed, 10) || 30;
|
|
930
|
+
runBinary(binaryPath, ["savings", "--days", String(days)]);
|
|
931
|
+
}
|
|
779
932
|
break;
|
|
780
933
|
}
|
|
781
934
|
case "export": {
|
|
782
|
-
const
|
|
783
|
-
const days = parseInt(daysInput.trim(), 10) || 30;
|
|
935
|
+
const input = await ask(rl, chalk.cyan(" Period (today / 7 / 14 / 30 / 60 / 90): "));
|
|
784
936
|
const binaryPath = ensureBinary();
|
|
785
|
-
|
|
937
|
+
const trimmed = input.trim().toLowerCase();
|
|
938
|
+
if (trimmed === "today") {
|
|
939
|
+
const midnight = new Date();
|
|
940
|
+
midnight.setHours(0, 0, 0, 0);
|
|
941
|
+
runBinary(binaryPath, ["savings", "--since-ts", String(Math.floor(midnight.getTime() / 1000)), "--label", "today", "--export"]);
|
|
942
|
+
}
|
|
943
|
+
else {
|
|
944
|
+
const days = parseInt(trimmed, 10) || 30;
|
|
945
|
+
runBinary(binaryPath, ["savings", "--days", String(days), "--export"]);
|
|
946
|
+
}
|
|
786
947
|
break;
|
|
787
948
|
}
|
|
788
949
|
// ── License commands ──
|
|
@@ -838,4 +999,3 @@ if (process.argv.length <= 2) {
|
|
|
838
999
|
else {
|
|
839
1000
|
program.parse();
|
|
840
1001
|
}
|
|
841
|
-
//# sourceMappingURL=cli.js.map
|
package/dist/hook-template.js
CHANGED