jinzd-ai-cli 0.4.76 → 0.4.77
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 +3 -1
- package/dist/{chunk-XTH7S3AM.js → chunk-5PZMIBEN.js} +2 -2
- package/dist/chunk-HPDDAXFY.js +84 -0
- package/dist/{chunk-34NJTPWZ.js → chunk-IR5EW57V.js} +6 -3
- package/dist/{chunk-KR4FTJWB.js → chunk-LKDX2GOW.js} +73 -7
- package/dist/{chunk-H4DQNZZ6.js → chunk-LQ76WLB6.js} +1 -1
- package/dist/chunk-PFYAAX2S.js +169 -0
- package/dist/{chunk-K3JJX2Z5.js → chunk-QATT4NCL.js} +1 -1
- package/dist/{chunk-2Q77FT3F.js → chunk-S4WDPHKS.js} +6 -3
- package/dist/chunk-UTCC3UMT.js +83 -0
- package/dist/chunk-XMA222FQ.js +167 -0
- package/dist/{hub-YPNEYO3Z.js → hub-CZSXSOIH.js} +1 -1
- package/dist/index.js +64 -19
- package/dist/{run-tests-MKKCDUUV.js → run-tests-GEZ4NPWJ.js} +2 -2
- package/dist/{run-tests-2I5S24IH.js → run-tests-RHAXFEOL.js} +1 -1
- package/dist/{server-6MPBAH4K.js → server-CMSF65WV.js} +51 -16
- package/dist/{task-orchestrator-I5HPXTJY.js → task-orchestrator-VC7LCN5J.js} +6 -4
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
- **Web UI Session Replay** *(v0.4.71+)* — 🎬 button on every saved session opens a timeline replay: every message, tool call, reasoning, and cache-aware token usage at a glance
|
|
31
31
|
- **Conversation Branching** *(v0.4.74+)* — `/branch list/new/switch/delete/rename` inside the REPL, plus a 🌿 "fork here" button on every replay step — explore alternate directions without losing the original thread
|
|
32
32
|
- **Symbol Index** *(v0.4.76+)* — persistent tree-sitter index for TS/JS/TSX/Python powers three new AI tools: `find_symbol`, `get_outline`, `find_references`. Orders of magnitude faster than grep for definition lookups; background refresh on REPL startup, `/index status|rebuild|clear` to manage
|
|
33
|
+
- **Semantic Code Search** *(v0.4.77+)* — `search_code` tool finds code by meaning, not name. Local sentence embeddings (multilingual MiniLM, 117 MB one-time download) score symbols by cosine similarity against natural-language queries in English or Chinese ("where are users authenticated", "哪里做了速率限制"). No API key, runs on CPU. Manage with `/index semantic-rebuild|semantic-clear`
|
|
33
34
|
- **Streaming Tool Use** — Real-time streaming of AI reasoning and tool calls as they happen
|
|
34
35
|
- **Sub-Agents** — Delegate complex subtasks to isolated child agents with independent tool loops
|
|
35
36
|
- **Extended Thinking** — Claude deep reasoning mode with `/think` toggle
|
|
@@ -177,6 +178,7 @@ AI autonomously invokes these 27 tools during conversations:
|
|
|
177
178
|
| `find_symbol` | safe | Locate symbol definitions via persistent tree-sitter index (TS/JS/TSX/Python) |
|
|
178
179
|
| `get_outline` | safe | Enumerate all top-level declarations in one source file |
|
|
179
180
|
| `find_references` | safe | Search indexed files for references to a symbol name |
|
|
181
|
+
| `search_code` | safe | Semantic (meaning-based) code search via local sentence embeddings — bilingual, "grep by meaning" |
|
|
180
182
|
|
|
181
183
|
**Safety levels**: `safe` = auto-execute, `write` = diff preview + confirmation, `destructive` = prominent warning + confirmation.
|
|
182
184
|
|
|
@@ -199,7 +201,7 @@ AI autonomously invokes these 27 tools during conversations:
|
|
|
199
201
|
| `/checkpoint` | Save/restore conversation checkpoints |
|
|
200
202
|
| `/fork` | Fork the current session into a new session file |
|
|
201
203
|
| `/branch` | Create/switch/delete branches *within* the current session (B2) |
|
|
202
|
-
| `/index` | Manage
|
|
204
|
+
| `/index` | Manage symbol + semantic index (status/rebuild/clear/semantic-rebuild/semantic-clear) — powers `find_symbol` / `get_outline` / `find_references` / `search_code` (C1+C2) |
|
|
203
205
|
| `/search <keyword>` | Full-text search across all sessions |
|
|
204
206
|
| `/skill` | Manage agent skill packs |
|
|
205
207
|
| `/mcp` | View MCP server status and tools |
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
schemaToJsonSchema,
|
|
4
4
|
truncateForPersist
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-LKDX2GOW.js";
|
|
6
6
|
import {
|
|
7
7
|
AuthError,
|
|
8
8
|
ProviderError,
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
MCP_PROTOCOL_VERSION,
|
|
19
19
|
MCP_TOOL_PREFIX,
|
|
20
20
|
VERSION
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-S4WDPHKS.js";
|
|
22
22
|
|
|
23
23
|
// src/providers/claude.ts
|
|
24
24
|
import Anthropic from "@anthropic-ai/sdk";
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
loadIndex
|
|
4
|
+
} from "./chunk-6VRJGH25.js";
|
|
5
|
+
import {
|
|
6
|
+
EMBEDDING_DIM,
|
|
7
|
+
embed,
|
|
8
|
+
embedOne,
|
|
9
|
+
loadVectorStore,
|
|
10
|
+
saveVectorStore,
|
|
11
|
+
searchVectorStore
|
|
12
|
+
} from "./chunk-PFYAAX2S.js";
|
|
13
|
+
|
|
14
|
+
// src/symbols/semantic.ts
|
|
15
|
+
function buildEmbeddingText(s) {
|
|
16
|
+
const parts = [s.kind, s.name];
|
|
17
|
+
if (s.container) parts.push(`in ${s.container}`);
|
|
18
|
+
if (s.signature) parts.push(s.signature);
|
|
19
|
+
if (s.doc) parts.push(s.doc);
|
|
20
|
+
return parts.join(" ").slice(0, 512);
|
|
21
|
+
}
|
|
22
|
+
async function rebuildSemanticIndex(root, opts = {}) {
|
|
23
|
+
const start = Date.now();
|
|
24
|
+
const index = loadIndex(root);
|
|
25
|
+
if (!index) {
|
|
26
|
+
throw new Error(
|
|
27
|
+
`No symbol index for ${root}. Run /index rebuild first so find_symbol has data to embed.`
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
const maxSymbols = opts.maxSymbols ?? 2e4;
|
|
31
|
+
const batchSize = opts.batchSize ?? 32;
|
|
32
|
+
const total = Math.min(index.symbols.length, maxSymbols);
|
|
33
|
+
if (total === 0) {
|
|
34
|
+
saveVectorStore(root, new Uint32Array(0), new Float32Array(0));
|
|
35
|
+
return { symbolsEmbedded: 0, durationMs: Date.now() - start };
|
|
36
|
+
}
|
|
37
|
+
const symbolIdx = new Uint32Array(total);
|
|
38
|
+
const vectors = new Float32Array(total * EMBEDDING_DIM);
|
|
39
|
+
let modelFirstLoadMs;
|
|
40
|
+
for (let i = 0; i < total; i += batchSize) {
|
|
41
|
+
const end = Math.min(i + batchSize, total);
|
|
42
|
+
const batch = [];
|
|
43
|
+
for (let j = i; j < end; j++) {
|
|
44
|
+
symbolIdx[j] = j;
|
|
45
|
+
batch.push(buildEmbeddingText(index.symbols[j]));
|
|
46
|
+
}
|
|
47
|
+
const batchStart = Date.now();
|
|
48
|
+
const rows = await embed(batch);
|
|
49
|
+
if (i === 0) modelFirstLoadMs = Date.now() - batchStart;
|
|
50
|
+
for (let r = 0; r < rows.length; r++) {
|
|
51
|
+
vectors.set(rows[r], (i + r) * EMBEDDING_DIM);
|
|
52
|
+
}
|
|
53
|
+
if (opts.onProgress) opts.onProgress(end, total);
|
|
54
|
+
}
|
|
55
|
+
saveVectorStore(root, symbolIdx, vectors);
|
|
56
|
+
return {
|
|
57
|
+
symbolsEmbedded: total,
|
|
58
|
+
durationMs: Date.now() - start,
|
|
59
|
+
modelFirstLoadMs
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
async function semanticSearch(root, query, k = 10) {
|
|
63
|
+
const index = loadIndex(root);
|
|
64
|
+
if (!index) return [];
|
|
65
|
+
const store = loadVectorStore(root);
|
|
66
|
+
if (!store || store.count === 0) return [];
|
|
67
|
+
const queryVec = await embedOne(query);
|
|
68
|
+
const hits = searchVectorStore(store, queryVec, k);
|
|
69
|
+
return hits.map((h) => ({
|
|
70
|
+
...h,
|
|
71
|
+
symbol: index.symbols[h.symbolIdx]
|
|
72
|
+
})).filter((h) => h.symbol !== void 0);
|
|
73
|
+
}
|
|
74
|
+
function hasSemanticIndex(root) {
|
|
75
|
+
const s = loadVectorStore(root);
|
|
76
|
+
return s !== null && s.count > 0;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export {
|
|
80
|
+
buildEmbeddingText,
|
|
81
|
+
rebuildSemanticIndex,
|
|
82
|
+
semanticSearch,
|
|
83
|
+
hasSemanticIndex
|
|
84
|
+
};
|
|
@@ -6,7 +6,7 @@ import { platform } from "os";
|
|
|
6
6
|
import chalk from "chalk";
|
|
7
7
|
|
|
8
8
|
// src/core/constants.ts
|
|
9
|
-
var VERSION = "0.4.
|
|
9
|
+
var VERSION = "0.4.77";
|
|
10
10
|
var APP_NAME = "ai-cli";
|
|
11
11
|
var CONFIG_DIR_NAME = ".aicli";
|
|
12
12
|
var CONFIG_FILE_NAME = "config.json";
|
|
@@ -41,8 +41,10 @@ var PLAN_MODE_READONLY_TOOLS = /* @__PURE__ */ new Set([
|
|
|
41
41
|
// C1 symbol index (read-only)
|
|
42
42
|
"get_outline",
|
|
43
43
|
// C1 symbol index (read-only)
|
|
44
|
-
"find_references"
|
|
44
|
+
"find_references",
|
|
45
45
|
// C1 symbol index (read-only)
|
|
46
|
+
"search_code"
|
|
47
|
+
// C2 semantic search (read-only)
|
|
46
48
|
]);
|
|
47
49
|
var PLAN_MODE_SYSTEM_ADDON = `# \u{1F50D} Plan Mode \u2014 Read-Only Planning Mode
|
|
48
50
|
|
|
@@ -85,7 +87,8 @@ var SUBAGENT_ALLOWED_TOOLS = /* @__PURE__ */ new Set([
|
|
|
85
87
|
"run_tests",
|
|
86
88
|
"find_symbol",
|
|
87
89
|
"get_outline",
|
|
88
|
-
"find_references"
|
|
90
|
+
"find_references",
|
|
91
|
+
"search_code"
|
|
89
92
|
]);
|
|
90
93
|
var TEST_TIMEOUT = 3e5;
|
|
91
94
|
var AGENTIC_BEHAVIOR_GUIDELINE = `# Important Behavioral Guidelines
|
|
@@ -1,21 +1,25 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
EnvLoader,
|
|
4
|
+
NetworkError,
|
|
5
|
+
ToolError
|
|
6
|
+
} from "./chunk-2ZD3YTVM.js";
|
|
2
7
|
import {
|
|
3
8
|
fileCheckpoints
|
|
4
9
|
} from "./chunk-4BKXL7SM.js";
|
|
5
10
|
import {
|
|
6
11
|
indexProject
|
|
7
12
|
} from "./chunk-NHNWUBXB.js";
|
|
13
|
+
import {
|
|
14
|
+
hasSemanticIndex,
|
|
15
|
+
semanticSearch
|
|
16
|
+
} from "./chunk-HPDDAXFY.js";
|
|
8
17
|
import {
|
|
9
18
|
loadIndex
|
|
10
19
|
} from "./chunk-6VRJGH25.js";
|
|
11
20
|
import {
|
|
12
21
|
runTestsTool
|
|
13
|
-
} from "./chunk-
|
|
14
|
-
import {
|
|
15
|
-
EnvLoader,
|
|
16
|
-
NetworkError,
|
|
17
|
-
ToolError
|
|
18
|
-
} from "./chunk-2ZD3YTVM.js";
|
|
22
|
+
} from "./chunk-QATT4NCL.js";
|
|
19
23
|
import {
|
|
20
24
|
CONFIG_DIR_NAME,
|
|
21
25
|
DEFAULT_MAX_TOOL_OUTPUT_CHARS_CAP,
|
|
@@ -23,7 +27,7 @@ import {
|
|
|
23
27
|
SUBAGENT_ALLOWED_TOOLS,
|
|
24
28
|
SUBAGENT_DEFAULT_MAX_ROUNDS,
|
|
25
29
|
SUBAGENT_MAX_ROUNDS_LIMIT
|
|
26
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-S4WDPHKS.js";
|
|
27
31
|
|
|
28
32
|
// src/tools/builtin/bash.ts
|
|
29
33
|
import { execSync } from "child_process";
|
|
@@ -4512,6 +4516,67 @@ var findReferencesTool = {
|
|
|
4512
4516
|
${lines.join("\n")}`;
|
|
4513
4517
|
}
|
|
4514
4518
|
};
|
|
4519
|
+
var searchCodeTool = {
|
|
4520
|
+
definition: {
|
|
4521
|
+
name: "search_code",
|
|
4522
|
+
description: 'Semantic (meaning-based) search across indexed code symbols using local sentence embeddings. Use this when you do NOT know the exact symbol name \u2014 it finds code by purpose, e.g. "rate limiting logic", "where users are authenticated", "error recovery retry loops". Returns ranked matches with similarity scores. Requires a prior `/index semantic-rebuild` to build embeddings (one-time 117 MB model download + a few seconds per 1K symbols). For exact name lookups use find_symbol instead.',
|
|
4523
|
+
parameters: {
|
|
4524
|
+
query: {
|
|
4525
|
+
type: "string",
|
|
4526
|
+
description: "Natural-language description of the code you're looking for. English or Chinese both work.",
|
|
4527
|
+
required: true
|
|
4528
|
+
},
|
|
4529
|
+
k: {
|
|
4530
|
+
type: "number",
|
|
4531
|
+
description: "Max results to return (default 10, max 50).",
|
|
4532
|
+
required: false
|
|
4533
|
+
},
|
|
4534
|
+
kind: {
|
|
4535
|
+
type: "string",
|
|
4536
|
+
description: "Restrict to one symbol kind: function, method, class, interface, type, enum, variable, property. Omit to search all.",
|
|
4537
|
+
required: false
|
|
4538
|
+
},
|
|
4539
|
+
path: {
|
|
4540
|
+
type: "string",
|
|
4541
|
+
description: "Project root (defaults to current working directory).",
|
|
4542
|
+
required: false
|
|
4543
|
+
}
|
|
4544
|
+
}
|
|
4545
|
+
},
|
|
4546
|
+
async execute(args) {
|
|
4547
|
+
const query = String(args.query ?? "").trim();
|
|
4548
|
+
if (!query) return "Error: `query` is required.";
|
|
4549
|
+
const root = path2.resolve(String(args.path ?? process.cwd()));
|
|
4550
|
+
const k = Math.min(50, Math.max(1, typeof args.k === "number" ? args.k : 10));
|
|
4551
|
+
const kindFilter = args.kind ? String(args.kind) : void 0;
|
|
4552
|
+
if (!hasSemanticIndex(root)) {
|
|
4553
|
+
return "No semantic index exists for this project yet. Ask the user to run `/index semantic-rebuild` (first run downloads a ~117 MB embedding model; subsequent rebuilds are fast). As a fallback, use grep_files or find_symbol for literal-name lookups.";
|
|
4554
|
+
}
|
|
4555
|
+
let hits;
|
|
4556
|
+
try {
|
|
4557
|
+
const oversampled = await semanticSearch(root, query, kindFilter ? k * 4 : k);
|
|
4558
|
+
hits = kindFilter ? oversampled.filter((h) => h.symbol.kind === kindFilter).slice(0, k) : oversampled;
|
|
4559
|
+
} catch (err) {
|
|
4560
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
4561
|
+
return `Semantic search failed: ${msg}`;
|
|
4562
|
+
}
|
|
4563
|
+
if (hits.length === 0) {
|
|
4564
|
+
return `No semantic matches found for "${query}". Try a different phrasing, or run \`/index semantic-rebuild\` if the codebase has changed significantly.`;
|
|
4565
|
+
}
|
|
4566
|
+
const lines = hits.map((h) => {
|
|
4567
|
+
const s = h.symbol;
|
|
4568
|
+
const rel = path2.relative(root, s.location.file) || s.location.file;
|
|
4569
|
+
const container = s.container ? ` (in ${s.container})` : "";
|
|
4570
|
+
const exp = s.exported ? " [exported]" : "";
|
|
4571
|
+
const score = h.score.toFixed(3);
|
|
4572
|
+
const sig = s.signature ? `
|
|
4573
|
+
${s.signature}` : "";
|
|
4574
|
+
return `[score=${score}] ${s.kind} ${s.name}${container}${exp} \u2014 ${rel}:${s.location.line}${sig}`;
|
|
4575
|
+
});
|
|
4576
|
+
return `Top ${hits.length} semantic match(es) for "${query}":
|
|
4577
|
+
${lines.join("\n")}`;
|
|
4578
|
+
}
|
|
4579
|
+
};
|
|
4515
4580
|
|
|
4516
4581
|
// src/core/token-estimator.ts
|
|
4517
4582
|
var CJK_REGEX = /[\u2E80-\u9FFF\uA000-\uA4FF\uAC00-\uD7FF\uF900-\uFAFF\uFE30-\uFE4F\uFF00-\uFFEF]/g;
|
|
@@ -4571,6 +4636,7 @@ var ToolRegistry = class {
|
|
|
4571
4636
|
this.register(findSymbolTool);
|
|
4572
4637
|
this.register(getOutlineTool);
|
|
4573
4638
|
this.register(findReferencesTool);
|
|
4639
|
+
this.register(searchCodeTool);
|
|
4574
4640
|
}
|
|
4575
4641
|
register(tool) {
|
|
4576
4642
|
this.tools.set(tool.definition.name, tool);
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/symbols/vector-store.ts
|
|
4
|
+
import fs2 from "fs";
|
|
5
|
+
import path2 from "path";
|
|
6
|
+
import os2 from "os";
|
|
7
|
+
import crypto from "crypto";
|
|
8
|
+
|
|
9
|
+
// src/symbols/embedder.ts
|
|
10
|
+
import path from "path";
|
|
11
|
+
import os from "os";
|
|
12
|
+
import fs from "fs";
|
|
13
|
+
var EMBEDDING_MODEL_ID = "Xenova/paraphrase-multilingual-MiniLM-L12-v2";
|
|
14
|
+
var EMBEDDING_DIM = 384;
|
|
15
|
+
var pipelinePromise = null;
|
|
16
|
+
function cacheDir() {
|
|
17
|
+
return path.join(os.homedir(), ".aicli", "models");
|
|
18
|
+
}
|
|
19
|
+
async function getEmbedder() {
|
|
20
|
+
if (pipelinePromise) return pipelinePromise;
|
|
21
|
+
pipelinePromise = (async () => {
|
|
22
|
+
const mod = await import("@huggingface/transformers");
|
|
23
|
+
const dir = cacheDir();
|
|
24
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
25
|
+
mod.env.cacheDir = dir;
|
|
26
|
+
mod.env.allowRemoteModels = true;
|
|
27
|
+
mod.env.allowLocalModels = true;
|
|
28
|
+
const pipe = await mod.pipeline("feature-extraction", EMBEDDING_MODEL_ID, {
|
|
29
|
+
// Keep the ONNX session in float32; int8 quantization exists but the
|
|
30
|
+
// quality drop on short code identifiers is noticeable.
|
|
31
|
+
dtype: "fp32"
|
|
32
|
+
});
|
|
33
|
+
return pipe;
|
|
34
|
+
})();
|
|
35
|
+
return pipelinePromise;
|
|
36
|
+
}
|
|
37
|
+
async function embed(texts) {
|
|
38
|
+
if (texts.length === 0) return [];
|
|
39
|
+
const pipe = await getEmbedder();
|
|
40
|
+
const out = await pipe(texts, { pooling: "mean", normalize: true });
|
|
41
|
+
const batch = texts.length;
|
|
42
|
+
const dim = EMBEDDING_DIM;
|
|
43
|
+
const rows = new Array(batch);
|
|
44
|
+
for (let i = 0; i < batch; i++) {
|
|
45
|
+
rows[i] = new Float32Array(out.data.buffer, out.data.byteOffset + i * dim * 4, dim).slice();
|
|
46
|
+
}
|
|
47
|
+
return rows;
|
|
48
|
+
}
|
|
49
|
+
async function embedOne(text) {
|
|
50
|
+
const [vec] = await embed([text]);
|
|
51
|
+
return vec;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// src/symbols/vector-store.ts
|
|
55
|
+
var MAGIC = 1094927190;
|
|
56
|
+
var VERSION = 1;
|
|
57
|
+
var HEADER_BYTES = 16;
|
|
58
|
+
function indexDir() {
|
|
59
|
+
return path2.join(os2.homedir(), ".aicli", "index");
|
|
60
|
+
}
|
|
61
|
+
function projectHash(root) {
|
|
62
|
+
return crypto.createHash("sha1").update(path2.resolve(root).toLowerCase()).digest("hex").slice(0, 16);
|
|
63
|
+
}
|
|
64
|
+
function vecPath(root) {
|
|
65
|
+
return path2.join(indexDir(), `${projectHash(root)}.vec`);
|
|
66
|
+
}
|
|
67
|
+
function emptyVectorStore(root) {
|
|
68
|
+
return {
|
|
69
|
+
root: path2.resolve(root),
|
|
70
|
+
count: 0,
|
|
71
|
+
dim: EMBEDDING_DIM,
|
|
72
|
+
vectors: new Float32Array(0),
|
|
73
|
+
symbolIdx: new Uint32Array(0)
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function saveVectorStore(root, indices, vectors) {
|
|
77
|
+
if (indices.length * EMBEDDING_DIM !== vectors.length) {
|
|
78
|
+
throw new Error(
|
|
79
|
+
`saveVectorStore: length mismatch \u2014 ${indices.length} indices vs ${vectors.length / EMBEDDING_DIM} vectors`
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
const count = indices.length;
|
|
83
|
+
const dir = indexDir();
|
|
84
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
85
|
+
const totalBytes = HEADER_BYTES + count * 4 + count * EMBEDDING_DIM * 4;
|
|
86
|
+
const buf = Buffer.alloc(totalBytes);
|
|
87
|
+
buf.writeUInt32LE(MAGIC, 0);
|
|
88
|
+
buf.writeUInt32LE(VERSION, 4);
|
|
89
|
+
buf.writeUInt32LE(count, 8);
|
|
90
|
+
buf.writeUInt32LE(EMBEDDING_DIM, 12);
|
|
91
|
+
Buffer.from(indices.buffer, indices.byteOffset, indices.byteLength).copy(buf, HEADER_BYTES);
|
|
92
|
+
Buffer.from(vectors.buffer, vectors.byteOffset, vectors.byteLength).copy(buf, HEADER_BYTES + count * 4);
|
|
93
|
+
const target = vecPath(root);
|
|
94
|
+
const tmp = `${target}.tmp`;
|
|
95
|
+
fs2.writeFileSync(tmp, buf);
|
|
96
|
+
fs2.renameSync(tmp, target);
|
|
97
|
+
}
|
|
98
|
+
function loadVectorStore(root) {
|
|
99
|
+
const p = vecPath(root);
|
|
100
|
+
if (!fs2.existsSync(p)) return null;
|
|
101
|
+
let buf;
|
|
102
|
+
try {
|
|
103
|
+
buf = fs2.readFileSync(p);
|
|
104
|
+
} catch {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
if (buf.length < HEADER_BYTES) return null;
|
|
108
|
+
const magic = buf.readUInt32LE(0);
|
|
109
|
+
const version = buf.readUInt32LE(4);
|
|
110
|
+
const count = buf.readUInt32LE(8);
|
|
111
|
+
const dim = buf.readUInt32LE(12);
|
|
112
|
+
if (magic !== MAGIC || version !== VERSION || dim !== EMBEDDING_DIM) return null;
|
|
113
|
+
const expected = HEADER_BYTES + count * 4 + count * dim * 4;
|
|
114
|
+
if (buf.length !== expected) return null;
|
|
115
|
+
const symbolIdx = new Uint32Array(
|
|
116
|
+
buf.buffer.slice(buf.byteOffset + HEADER_BYTES, buf.byteOffset + HEADER_BYTES + count * 4)
|
|
117
|
+
);
|
|
118
|
+
const vectors = new Float32Array(
|
|
119
|
+
buf.buffer.slice(
|
|
120
|
+
buf.byteOffset + HEADER_BYTES + count * 4,
|
|
121
|
+
buf.byteOffset + HEADER_BYTES + count * 4 + count * dim * 4
|
|
122
|
+
)
|
|
123
|
+
);
|
|
124
|
+
return { root: path2.resolve(root), count, dim, vectors, symbolIdx };
|
|
125
|
+
}
|
|
126
|
+
function clearVectorStore(root) {
|
|
127
|
+
const p = vecPath(root);
|
|
128
|
+
try {
|
|
129
|
+
if (fs2.existsSync(p)) fs2.unlinkSync(p);
|
|
130
|
+
} catch {
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
function searchVectorStore(store, queryVec, k) {
|
|
134
|
+
if (store.count === 0) return [];
|
|
135
|
+
if (queryVec.length !== store.dim) {
|
|
136
|
+
throw new Error(`searchVectorStore: dim mismatch (query=${queryVec.length}, store=${store.dim})`);
|
|
137
|
+
}
|
|
138
|
+
const { count, dim, vectors, symbolIdx } = store;
|
|
139
|
+
const heap = [];
|
|
140
|
+
const push = (hit) => {
|
|
141
|
+
if (heap.length < k) {
|
|
142
|
+
heap.push(hit);
|
|
143
|
+
heap.sort((a, b) => a.score - b.score);
|
|
144
|
+
} else if (hit.score > heap[0].score) {
|
|
145
|
+
heap[0] = hit;
|
|
146
|
+
heap.sort((a, b) => a.score - b.score);
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
for (let row = 0; row < count; row++) {
|
|
150
|
+
const base = row * dim;
|
|
151
|
+
let score = 0;
|
|
152
|
+
for (let d = 0; d < dim; d++) {
|
|
153
|
+
score += vectors[base + d] * queryVec[d];
|
|
154
|
+
}
|
|
155
|
+
push({ row, symbolIdx: symbolIdx[row], score });
|
|
156
|
+
}
|
|
157
|
+
return heap.sort((a, b) => b.score - a.score);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export {
|
|
161
|
+
EMBEDDING_DIM,
|
|
162
|
+
embed,
|
|
163
|
+
embedOne,
|
|
164
|
+
emptyVectorStore,
|
|
165
|
+
saveVectorStore,
|
|
166
|
+
loadVectorStore,
|
|
167
|
+
clearVectorStore,
|
|
168
|
+
searchVectorStore
|
|
169
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/core/constants.ts
|
|
4
|
-
var VERSION = "0.4.
|
|
4
|
+
var VERSION = "0.4.77";
|
|
5
5
|
var APP_NAME = "ai-cli";
|
|
6
6
|
var CONFIG_DIR_NAME = ".aicli";
|
|
7
7
|
var CONFIG_FILE_NAME = "config.json";
|
|
@@ -36,8 +36,10 @@ var PLAN_MODE_READONLY_TOOLS = /* @__PURE__ */ new Set([
|
|
|
36
36
|
// C1 symbol index (read-only)
|
|
37
37
|
"get_outline",
|
|
38
38
|
// C1 symbol index (read-only)
|
|
39
|
-
"find_references"
|
|
39
|
+
"find_references",
|
|
40
40
|
// C1 symbol index (read-only)
|
|
41
|
+
"search_code"
|
|
42
|
+
// C2 semantic search (read-only)
|
|
41
43
|
]);
|
|
42
44
|
var PLAN_MODE_SYSTEM_ADDON = `# \u{1F50D} Plan Mode \u2014 Read-Only Planning Mode
|
|
43
45
|
|
|
@@ -80,7 +82,8 @@ var SUBAGENT_ALLOWED_TOOLS = /* @__PURE__ */ new Set([
|
|
|
80
82
|
"run_tests",
|
|
81
83
|
"find_symbol",
|
|
82
84
|
"get_outline",
|
|
83
|
-
"find_references"
|
|
85
|
+
"find_references",
|
|
86
|
+
"search_code"
|
|
84
87
|
]);
|
|
85
88
|
var CONTEXT_PRESSURE_THRESHOLD = 0.8;
|
|
86
89
|
var TEST_TIMEOUT = 3e5;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import {
|
|
2
|
+
loadIndex
|
|
3
|
+
} from "./chunk-BJAT4GNC.js";
|
|
4
|
+
import {
|
|
5
|
+
EMBEDDING_DIM,
|
|
6
|
+
embed,
|
|
7
|
+
embedOne,
|
|
8
|
+
loadVectorStore,
|
|
9
|
+
saveVectorStore,
|
|
10
|
+
searchVectorStore
|
|
11
|
+
} from "./chunk-XMA222FQ.js";
|
|
12
|
+
|
|
13
|
+
// src/symbols/semantic.ts
|
|
14
|
+
function buildEmbeddingText(s) {
|
|
15
|
+
const parts = [s.kind, s.name];
|
|
16
|
+
if (s.container) parts.push(`in ${s.container}`);
|
|
17
|
+
if (s.signature) parts.push(s.signature);
|
|
18
|
+
if (s.doc) parts.push(s.doc);
|
|
19
|
+
return parts.join(" ").slice(0, 512);
|
|
20
|
+
}
|
|
21
|
+
async function rebuildSemanticIndex(root, opts = {}) {
|
|
22
|
+
const start = Date.now();
|
|
23
|
+
const index = loadIndex(root);
|
|
24
|
+
if (!index) {
|
|
25
|
+
throw new Error(
|
|
26
|
+
`No symbol index for ${root}. Run /index rebuild first so find_symbol has data to embed.`
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
const maxSymbols = opts.maxSymbols ?? 2e4;
|
|
30
|
+
const batchSize = opts.batchSize ?? 32;
|
|
31
|
+
const total = Math.min(index.symbols.length, maxSymbols);
|
|
32
|
+
if (total === 0) {
|
|
33
|
+
saveVectorStore(root, new Uint32Array(0), new Float32Array(0));
|
|
34
|
+
return { symbolsEmbedded: 0, durationMs: Date.now() - start };
|
|
35
|
+
}
|
|
36
|
+
const symbolIdx = new Uint32Array(total);
|
|
37
|
+
const vectors = new Float32Array(total * EMBEDDING_DIM);
|
|
38
|
+
let modelFirstLoadMs;
|
|
39
|
+
for (let i = 0; i < total; i += batchSize) {
|
|
40
|
+
const end = Math.min(i + batchSize, total);
|
|
41
|
+
const batch = [];
|
|
42
|
+
for (let j = i; j < end; j++) {
|
|
43
|
+
symbolIdx[j] = j;
|
|
44
|
+
batch.push(buildEmbeddingText(index.symbols[j]));
|
|
45
|
+
}
|
|
46
|
+
const batchStart = Date.now();
|
|
47
|
+
const rows = await embed(batch);
|
|
48
|
+
if (i === 0) modelFirstLoadMs = Date.now() - batchStart;
|
|
49
|
+
for (let r = 0; r < rows.length; r++) {
|
|
50
|
+
vectors.set(rows[r], (i + r) * EMBEDDING_DIM);
|
|
51
|
+
}
|
|
52
|
+
if (opts.onProgress) opts.onProgress(end, total);
|
|
53
|
+
}
|
|
54
|
+
saveVectorStore(root, symbolIdx, vectors);
|
|
55
|
+
return {
|
|
56
|
+
symbolsEmbedded: total,
|
|
57
|
+
durationMs: Date.now() - start,
|
|
58
|
+
modelFirstLoadMs
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
async function semanticSearch(root, query, k = 10) {
|
|
62
|
+
const index = loadIndex(root);
|
|
63
|
+
if (!index) return [];
|
|
64
|
+
const store = loadVectorStore(root);
|
|
65
|
+
if (!store || store.count === 0) return [];
|
|
66
|
+
const queryVec = await embedOne(query);
|
|
67
|
+
const hits = searchVectorStore(store, queryVec, k);
|
|
68
|
+
return hits.map((h) => ({
|
|
69
|
+
...h,
|
|
70
|
+
symbol: index.symbols[h.symbolIdx]
|
|
71
|
+
})).filter((h) => h.symbol !== void 0);
|
|
72
|
+
}
|
|
73
|
+
function hasSemanticIndex(root) {
|
|
74
|
+
const s = loadVectorStore(root);
|
|
75
|
+
return s !== null && s.count > 0;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export {
|
|
79
|
+
buildEmbeddingText,
|
|
80
|
+
rebuildSemanticIndex,
|
|
81
|
+
semanticSearch,
|
|
82
|
+
hasSemanticIndex
|
|
83
|
+
};
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
// src/symbols/vector-store.ts
|
|
2
|
+
import fs2 from "fs";
|
|
3
|
+
import path2 from "path";
|
|
4
|
+
import os2 from "os";
|
|
5
|
+
import crypto from "crypto";
|
|
6
|
+
|
|
7
|
+
// src/symbols/embedder.ts
|
|
8
|
+
import path from "path";
|
|
9
|
+
import os from "os";
|
|
10
|
+
import fs from "fs";
|
|
11
|
+
var EMBEDDING_MODEL_ID = "Xenova/paraphrase-multilingual-MiniLM-L12-v2";
|
|
12
|
+
var EMBEDDING_DIM = 384;
|
|
13
|
+
var pipelinePromise = null;
|
|
14
|
+
function cacheDir() {
|
|
15
|
+
return path.join(os.homedir(), ".aicli", "models");
|
|
16
|
+
}
|
|
17
|
+
async function getEmbedder() {
|
|
18
|
+
if (pipelinePromise) return pipelinePromise;
|
|
19
|
+
pipelinePromise = (async () => {
|
|
20
|
+
const mod = await import("@huggingface/transformers");
|
|
21
|
+
const dir = cacheDir();
|
|
22
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
23
|
+
mod.env.cacheDir = dir;
|
|
24
|
+
mod.env.allowRemoteModels = true;
|
|
25
|
+
mod.env.allowLocalModels = true;
|
|
26
|
+
const pipe = await mod.pipeline("feature-extraction", EMBEDDING_MODEL_ID, {
|
|
27
|
+
// Keep the ONNX session in float32; int8 quantization exists but the
|
|
28
|
+
// quality drop on short code identifiers is noticeable.
|
|
29
|
+
dtype: "fp32"
|
|
30
|
+
});
|
|
31
|
+
return pipe;
|
|
32
|
+
})();
|
|
33
|
+
return pipelinePromise;
|
|
34
|
+
}
|
|
35
|
+
async function embed(texts) {
|
|
36
|
+
if (texts.length === 0) return [];
|
|
37
|
+
const pipe = await getEmbedder();
|
|
38
|
+
const out = await pipe(texts, { pooling: "mean", normalize: true });
|
|
39
|
+
const batch = texts.length;
|
|
40
|
+
const dim = EMBEDDING_DIM;
|
|
41
|
+
const rows = new Array(batch);
|
|
42
|
+
for (let i = 0; i < batch; i++) {
|
|
43
|
+
rows[i] = new Float32Array(out.data.buffer, out.data.byteOffset + i * dim * 4, dim).slice();
|
|
44
|
+
}
|
|
45
|
+
return rows;
|
|
46
|
+
}
|
|
47
|
+
async function embedOne(text) {
|
|
48
|
+
const [vec] = await embed([text]);
|
|
49
|
+
return vec;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// src/symbols/vector-store.ts
|
|
53
|
+
var MAGIC = 1094927190;
|
|
54
|
+
var VERSION = 1;
|
|
55
|
+
var HEADER_BYTES = 16;
|
|
56
|
+
function indexDir() {
|
|
57
|
+
return path2.join(os2.homedir(), ".aicli", "index");
|
|
58
|
+
}
|
|
59
|
+
function projectHash(root) {
|
|
60
|
+
return crypto.createHash("sha1").update(path2.resolve(root).toLowerCase()).digest("hex").slice(0, 16);
|
|
61
|
+
}
|
|
62
|
+
function vecPath(root) {
|
|
63
|
+
return path2.join(indexDir(), `${projectHash(root)}.vec`);
|
|
64
|
+
}
|
|
65
|
+
function emptyVectorStore(root) {
|
|
66
|
+
return {
|
|
67
|
+
root: path2.resolve(root),
|
|
68
|
+
count: 0,
|
|
69
|
+
dim: EMBEDDING_DIM,
|
|
70
|
+
vectors: new Float32Array(0),
|
|
71
|
+
symbolIdx: new Uint32Array(0)
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function saveVectorStore(root, indices, vectors) {
|
|
75
|
+
if (indices.length * EMBEDDING_DIM !== vectors.length) {
|
|
76
|
+
throw new Error(
|
|
77
|
+
`saveVectorStore: length mismatch \u2014 ${indices.length} indices vs ${vectors.length / EMBEDDING_DIM} vectors`
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
const count = indices.length;
|
|
81
|
+
const dir = indexDir();
|
|
82
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
83
|
+
const totalBytes = HEADER_BYTES + count * 4 + count * EMBEDDING_DIM * 4;
|
|
84
|
+
const buf = Buffer.alloc(totalBytes);
|
|
85
|
+
buf.writeUInt32LE(MAGIC, 0);
|
|
86
|
+
buf.writeUInt32LE(VERSION, 4);
|
|
87
|
+
buf.writeUInt32LE(count, 8);
|
|
88
|
+
buf.writeUInt32LE(EMBEDDING_DIM, 12);
|
|
89
|
+
Buffer.from(indices.buffer, indices.byteOffset, indices.byteLength).copy(buf, HEADER_BYTES);
|
|
90
|
+
Buffer.from(vectors.buffer, vectors.byteOffset, vectors.byteLength).copy(buf, HEADER_BYTES + count * 4);
|
|
91
|
+
const target = vecPath(root);
|
|
92
|
+
const tmp = `${target}.tmp`;
|
|
93
|
+
fs2.writeFileSync(tmp, buf);
|
|
94
|
+
fs2.renameSync(tmp, target);
|
|
95
|
+
}
|
|
96
|
+
function loadVectorStore(root) {
|
|
97
|
+
const p = vecPath(root);
|
|
98
|
+
if (!fs2.existsSync(p)) return null;
|
|
99
|
+
let buf;
|
|
100
|
+
try {
|
|
101
|
+
buf = fs2.readFileSync(p);
|
|
102
|
+
} catch {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
if (buf.length < HEADER_BYTES) return null;
|
|
106
|
+
const magic = buf.readUInt32LE(0);
|
|
107
|
+
const version = buf.readUInt32LE(4);
|
|
108
|
+
const count = buf.readUInt32LE(8);
|
|
109
|
+
const dim = buf.readUInt32LE(12);
|
|
110
|
+
if (magic !== MAGIC || version !== VERSION || dim !== EMBEDDING_DIM) return null;
|
|
111
|
+
const expected = HEADER_BYTES + count * 4 + count * dim * 4;
|
|
112
|
+
if (buf.length !== expected) return null;
|
|
113
|
+
const symbolIdx = new Uint32Array(
|
|
114
|
+
buf.buffer.slice(buf.byteOffset + HEADER_BYTES, buf.byteOffset + HEADER_BYTES + count * 4)
|
|
115
|
+
);
|
|
116
|
+
const vectors = new Float32Array(
|
|
117
|
+
buf.buffer.slice(
|
|
118
|
+
buf.byteOffset + HEADER_BYTES + count * 4,
|
|
119
|
+
buf.byteOffset + HEADER_BYTES + count * 4 + count * dim * 4
|
|
120
|
+
)
|
|
121
|
+
);
|
|
122
|
+
return { root: path2.resolve(root), count, dim, vectors, symbolIdx };
|
|
123
|
+
}
|
|
124
|
+
function clearVectorStore(root) {
|
|
125
|
+
const p = vecPath(root);
|
|
126
|
+
try {
|
|
127
|
+
if (fs2.existsSync(p)) fs2.unlinkSync(p);
|
|
128
|
+
} catch {
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
function searchVectorStore(store, queryVec, k) {
|
|
132
|
+
if (store.count === 0) return [];
|
|
133
|
+
if (queryVec.length !== store.dim) {
|
|
134
|
+
throw new Error(`searchVectorStore: dim mismatch (query=${queryVec.length}, store=${store.dim})`);
|
|
135
|
+
}
|
|
136
|
+
const { count, dim, vectors, symbolIdx } = store;
|
|
137
|
+
const heap = [];
|
|
138
|
+
const push = (hit) => {
|
|
139
|
+
if (heap.length < k) {
|
|
140
|
+
heap.push(hit);
|
|
141
|
+
heap.sort((a, b) => a.score - b.score);
|
|
142
|
+
} else if (hit.score > heap[0].score) {
|
|
143
|
+
heap[0] = hit;
|
|
144
|
+
heap.sort((a, b) => a.score - b.score);
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
for (let row = 0; row < count; row++) {
|
|
148
|
+
const base = row * dim;
|
|
149
|
+
let score = 0;
|
|
150
|
+
for (let d = 0; d < dim; d++) {
|
|
151
|
+
score += vectors[base + d] * queryVec[d];
|
|
152
|
+
}
|
|
153
|
+
push({ row, symbolIdx: symbolIdx[row], score });
|
|
154
|
+
}
|
|
155
|
+
return heap.sort((a, b) => b.score - a.score);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export {
|
|
159
|
+
EMBEDDING_DIM,
|
|
160
|
+
embed,
|
|
161
|
+
embedOne,
|
|
162
|
+
emptyVectorStore,
|
|
163
|
+
saveVectorStore,
|
|
164
|
+
loadVectorStore,
|
|
165
|
+
clearVectorStore,
|
|
166
|
+
searchVectorStore
|
|
167
|
+
};
|
|
@@ -385,7 +385,7 @@ ${content}`);
|
|
|
385
385
|
}
|
|
386
386
|
}
|
|
387
387
|
async function runTaskMode(config, providers, configManager, topic) {
|
|
388
|
-
const { TaskOrchestrator } = await import("./task-orchestrator-
|
|
388
|
+
const { TaskOrchestrator } = await import("./task-orchestrator-VC7LCN5J.js");
|
|
389
389
|
const orchestrator = new TaskOrchestrator(config, providers, configManager);
|
|
390
390
|
let interrupted = false;
|
|
391
391
|
const onSigint = () => {
|
package/dist/index.js
CHANGED
|
@@ -30,7 +30,10 @@ import {
|
|
|
30
30
|
saveDevState,
|
|
31
31
|
sessionHasMeaningfulContent,
|
|
32
32
|
setupProxy
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-5PZMIBEN.js";
|
|
34
|
+
import {
|
|
35
|
+
ConfigManager
|
|
36
|
+
} from "./chunk-LQ76WLB6.js";
|
|
34
37
|
import {
|
|
35
38
|
ToolExecutor,
|
|
36
39
|
ToolRegistry,
|
|
@@ -46,17 +49,16 @@ import {
|
|
|
46
49
|
spawnAgentContext,
|
|
47
50
|
theme,
|
|
48
51
|
undoStack
|
|
49
|
-
} from "./chunk-
|
|
52
|
+
} from "./chunk-LKDX2GOW.js";
|
|
53
|
+
import "./chunk-2ZD3YTVM.js";
|
|
50
54
|
import {
|
|
51
55
|
fileCheckpoints
|
|
52
56
|
} from "./chunk-4BKXL7SM.js";
|
|
53
57
|
import "./chunk-NHNWUBXB.js";
|
|
58
|
+
import "./chunk-HPDDAXFY.js";
|
|
54
59
|
import "./chunk-6VRJGH25.js";
|
|
55
|
-
import "./chunk-
|
|
56
|
-
import
|
|
57
|
-
ConfigManager
|
|
58
|
-
} from "./chunk-H4DQNZZ6.js";
|
|
59
|
-
import "./chunk-2ZD3YTVM.js";
|
|
60
|
+
import "./chunk-PFYAAX2S.js";
|
|
61
|
+
import "./chunk-QATT4NCL.js";
|
|
60
62
|
import {
|
|
61
63
|
AGENTIC_BEHAVIOR_GUIDELINE,
|
|
62
64
|
AUTHOR,
|
|
@@ -78,7 +80,7 @@ import {
|
|
|
78
80
|
SKILLS_DIR_NAME,
|
|
79
81
|
VERSION,
|
|
80
82
|
buildUserIdentityPrompt
|
|
81
|
-
} from "./chunk-
|
|
83
|
+
} from "./chunk-S4WDPHKS.js";
|
|
82
84
|
|
|
83
85
|
// src/index.ts
|
|
84
86
|
import { program } from "commander";
|
|
@@ -216,7 +218,7 @@ var Renderer = class {
|
|
|
216
218
|
console.log(theme.dim(" Gemini (Google) \xB7 Zhipu (GLM) \xB7 OpenRouter \xB7 Ollama (Local, no API key)"));
|
|
217
219
|
console.log(HR);
|
|
218
220
|
const mcpToolCount = mcpInfo?.tools ?? 0;
|
|
219
|
-
const toolTotal =
|
|
221
|
+
const toolTotal = 28 + pluginCount + mcpToolCount;
|
|
220
222
|
const extras = [];
|
|
221
223
|
if (pluginCount > 0) extras.push(`${pluginCount} plugin(s)`);
|
|
222
224
|
if (mcpToolCount > 0) extras.push(`${mcpToolCount} MCP`);
|
|
@@ -249,6 +251,7 @@ var Renderer = class {
|
|
|
249
251
|
console.log(tool("find_symbol", "Locate symbol definitions via persistent tree-sitter index (TS/JS/TSX/Python)"));
|
|
250
252
|
console.log(tool("get_outline", "Enumerate all top-level declarations in one source file"));
|
|
251
253
|
console.log(tool("find_references", "Search indexed files for references to a symbol name"));
|
|
254
|
+
console.log(tool("search_code", 'Semantic (meaning-based) code search via local embeddings \u2014 "grep by meaning", bilingual'));
|
|
252
255
|
console.log(HR);
|
|
253
256
|
console.log(theme.dim(" REPL Commands (42):"));
|
|
254
257
|
console.log(theme.dim(" /help /about /provider /model /clear /compact /plan /session"));
|
|
@@ -976,7 +979,7 @@ function createDefaultCommands() {
|
|
|
976
979
|
" /diff [--stats] - Show all file modifications in this session",
|
|
977
980
|
" /fork [checkpoint] - Fork session from checkpoint or current position",
|
|
978
981
|
" /branch [list|new|switch|delete|rename] - Manage conversation branches (fork tree)",
|
|
979
|
-
" /index [status|rebuild|clear]
|
|
982
|
+
" /index [status|rebuild|clear|semantic-rebuild|semantic-clear] - Symbol + semantic index (find_symbol / search_code)",
|
|
980
983
|
" /yolo [on|off] - Toggle session auto-approve (skip confirmations)",
|
|
981
984
|
" /exit - Exit"
|
|
982
985
|
] : [];
|
|
@@ -2366,15 +2369,17 @@ ${hint}` : "")
|
|
|
2366
2369
|
// ── /index ────────────────────────────────────────────────────
|
|
2367
2370
|
{
|
|
2368
2371
|
name: "index",
|
|
2369
|
-
description: "Manage symbol index (status/rebuild/clear
|
|
2370
|
-
usage: "/index [status | rebuild | clear]",
|
|
2372
|
+
description: "Manage symbol + semantic index (status/rebuild/clear/semantic-rebuild/semantic-clear)",
|
|
2373
|
+
usage: "/index [status | rebuild | clear | semantic-rebuild | semantic-clear]",
|
|
2371
2374
|
async execute(args, ctx) {
|
|
2372
2375
|
const sub = (args[0] ?? "status").toLowerCase();
|
|
2373
2376
|
const root = process.cwd();
|
|
2374
2377
|
const { loadIndex, clearIndex } = await import("./store-S24SPPDZ.js");
|
|
2375
2378
|
const { indexProject } = await import("./indexer-C7QYYHSZ.js");
|
|
2379
|
+
const { loadVectorStore, clearVectorStore } = await import("./vector-store-YTVHACBV.js");
|
|
2376
2380
|
if (sub === "status") {
|
|
2377
2381
|
const idx = loadIndex(root);
|
|
2382
|
+
const vec = loadVectorStore(root);
|
|
2378
2383
|
if (!idx) {
|
|
2379
2384
|
console.log(theme.dim(` No index for ${root}.`));
|
|
2380
2385
|
console.log(theme.dim(" Run `/index rebuild` to build one."));
|
|
@@ -2385,12 +2390,16 @@ ${hint}` : "")
|
|
|
2385
2390
|
console.log(` Generated: ${idx.generated}`);
|
|
2386
2391
|
console.log(` Files: ${idx.fileCount}`);
|
|
2387
2392
|
console.log(` Symbols: ${idx.symbolCount}`);
|
|
2393
|
+
if (vec) {
|
|
2394
|
+
console.log(` Semantic: ${vec.count} vectors \xD7 ${vec.dim}-dim (run /index semantic-rebuild after major changes)`);
|
|
2395
|
+
} else {
|
|
2396
|
+
console.log(` Semantic: ${theme.dim("not built")} \u2014 run /index semantic-rebuild to enable search_code`);
|
|
2397
|
+
}
|
|
2388
2398
|
console.log();
|
|
2389
2399
|
return;
|
|
2390
2400
|
}
|
|
2391
2401
|
if (sub === "rebuild") {
|
|
2392
2402
|
console.log(theme.dim(` Indexing ${root}\u2026`));
|
|
2393
|
-
const start = Date.now();
|
|
2394
2403
|
const { stats } = await indexProject(root, {
|
|
2395
2404
|
force: true,
|
|
2396
2405
|
onProgress: (done, total) => {
|
|
@@ -2408,10 +2417,46 @@ ${hint}` : "")
|
|
|
2408
2417
|
}
|
|
2409
2418
|
if (sub === "clear") {
|
|
2410
2419
|
clearIndex(root);
|
|
2411
|
-
|
|
2420
|
+
clearVectorStore(root);
|
|
2421
|
+
console.log(theme.success(` \u2713 Cleared symbol + semantic index for ${root}`));
|
|
2422
|
+
return;
|
|
2423
|
+
}
|
|
2424
|
+
if (sub === "semantic-rebuild") {
|
|
2425
|
+
const idx = loadIndex(root);
|
|
2426
|
+
if (!idx) {
|
|
2427
|
+
ctx.renderer.renderError("No symbol index yet. Run `/index rebuild` first, then semantic-rebuild.");
|
|
2428
|
+
return;
|
|
2429
|
+
}
|
|
2430
|
+
console.log(theme.dim(` Building semantic index for ${idx.symbolCount} symbols\u2026`));
|
|
2431
|
+
console.log(theme.dim(" (First run downloads ~117 MB embedding model to ~/.aicli/models/)"));
|
|
2432
|
+
const { rebuildSemanticIndex } = await import("./semantic-RBWU76MD.js");
|
|
2433
|
+
try {
|
|
2434
|
+
const stats = await rebuildSemanticIndex(root, {
|
|
2435
|
+
onProgress: (done, total) => {
|
|
2436
|
+
const pct = total > 0 ? Math.floor(done / total * 100) : 0;
|
|
2437
|
+
process.stdout.write(theme.dim(`\r ${done}/${total} (${pct}%)\u2026`));
|
|
2438
|
+
}
|
|
2439
|
+
});
|
|
2440
|
+
process.stdout.write("\r\x1B[K");
|
|
2441
|
+
const first = stats.modelFirstLoadMs ? ` (model load+first batch ${stats.modelFirstLoadMs}ms)` : "";
|
|
2442
|
+
console.log(theme.success(
|
|
2443
|
+
` \u2713 Embedded ${stats.symbolsEmbedded} symbols in ${stats.durationMs}ms${first}`
|
|
2444
|
+
));
|
|
2445
|
+
} catch (err) {
|
|
2446
|
+
process.stdout.write("\r\x1B[K");
|
|
2447
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2448
|
+
ctx.renderer.renderError(`Semantic rebuild failed: ${msg}`);
|
|
2449
|
+
}
|
|
2412
2450
|
return;
|
|
2413
2451
|
}
|
|
2414
|
-
|
|
2452
|
+
if (sub === "semantic-clear") {
|
|
2453
|
+
clearVectorStore(root);
|
|
2454
|
+
console.log(theme.success(` \u2713 Cleared semantic index (symbol index preserved)`));
|
|
2455
|
+
return;
|
|
2456
|
+
}
|
|
2457
|
+
ctx.renderer.renderError(
|
|
2458
|
+
`Unknown subcommand: ${sub}. Use status/rebuild/clear/semantic-rebuild/semantic-clear.`
|
|
2459
|
+
);
|
|
2415
2460
|
}
|
|
2416
2461
|
},
|
|
2417
2462
|
// ── /commands ─────────────────────────────────────────────────
|
|
@@ -2450,7 +2495,7 @@ ${hint}` : "")
|
|
|
2450
2495
|
usage: "/test [command|filter]",
|
|
2451
2496
|
async execute(args, ctx) {
|
|
2452
2497
|
try {
|
|
2453
|
-
const { executeTests } = await import("./run-tests-
|
|
2498
|
+
const { executeTests } = await import("./run-tests-GEZ4NPWJ.js");
|
|
2454
2499
|
const argStr = args.join(" ").trim();
|
|
2455
2500
|
let testArgs = {};
|
|
2456
2501
|
if (argStr) {
|
|
@@ -6343,7 +6388,7 @@ program.command("web").description("Start Web UI server with browser-based chat
|
|
|
6343
6388
|
console.error("Error: Invalid port number. Must be between 1 and 65535.");
|
|
6344
6389
|
process.exit(1);
|
|
6345
6390
|
}
|
|
6346
|
-
const { startWebServer } = await import("./server-
|
|
6391
|
+
const { startWebServer } = await import("./server-CMSF65WV.js");
|
|
6347
6392
|
await startWebServer({ port, host: options.host });
|
|
6348
6393
|
});
|
|
6349
6394
|
program.command("user [action] [username]").description("Manage Web UI users (list | create <name> | delete <name> | reset-password <name> | migrate <name>)").action(async (action, username) => {
|
|
@@ -6466,7 +6511,7 @@ program.command("sessions").description("List recent conversation sessions").act
|
|
|
6466
6511
|
});
|
|
6467
6512
|
program.command("batch <action> [arg] [arg2]").description("Anthropic Message Batches: submit | list | status <id> | results <id> [out] | cancel <id>").option("--dry-run", "Parse and validate input without submitting (submit only)").action(async (action, arg, arg2, options) => {
|
|
6468
6513
|
try {
|
|
6469
|
-
const batch = await import("./batch-
|
|
6514
|
+
const batch = await import("./batch-RRC4RXZF.js");
|
|
6470
6515
|
switch (action) {
|
|
6471
6516
|
case "submit":
|
|
6472
6517
|
if (!arg) {
|
|
@@ -6626,7 +6671,7 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
|
|
|
6626
6671
|
}),
|
|
6627
6672
|
config.get("customProviders")
|
|
6628
6673
|
);
|
|
6629
|
-
const { startHub } = await import("./hub-
|
|
6674
|
+
const { startHub } = await import("./hub-CZSXSOIH.js");
|
|
6630
6675
|
await startHub(
|
|
6631
6676
|
{
|
|
6632
6677
|
topic: topic ?? "",
|
|
@@ -20,7 +20,10 @@ import {
|
|
|
20
20
|
persistToolRound,
|
|
21
21
|
rebuildExtraMessages,
|
|
22
22
|
setupProxy
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-5PZMIBEN.js";
|
|
24
|
+
import {
|
|
25
|
+
ConfigManager
|
|
26
|
+
} from "./chunk-LQ76WLB6.js";
|
|
24
27
|
import {
|
|
25
28
|
ToolExecutor,
|
|
26
29
|
ToolRegistry,
|
|
@@ -38,18 +41,14 @@ import {
|
|
|
38
41
|
spawnAgentContext,
|
|
39
42
|
truncateOutput,
|
|
40
43
|
undoStack
|
|
41
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-LKDX2GOW.js";
|
|
45
|
+
import "./chunk-2ZD3YTVM.js";
|
|
42
46
|
import "./chunk-4BKXL7SM.js";
|
|
43
47
|
import "./chunk-NHNWUBXB.js";
|
|
48
|
+
import "./chunk-HPDDAXFY.js";
|
|
44
49
|
import "./chunk-6VRJGH25.js";
|
|
45
|
-
import "./chunk-
|
|
46
|
-
import
|
|
47
|
-
AuthManager
|
|
48
|
-
} from "./chunk-BYNY5JPB.js";
|
|
49
|
-
import {
|
|
50
|
-
ConfigManager
|
|
51
|
-
} from "./chunk-H4DQNZZ6.js";
|
|
52
|
-
import "./chunk-2ZD3YTVM.js";
|
|
50
|
+
import "./chunk-PFYAAX2S.js";
|
|
51
|
+
import "./chunk-QATT4NCL.js";
|
|
53
52
|
import {
|
|
54
53
|
AGENTIC_BEHAVIOR_GUIDELINE,
|
|
55
54
|
AUTHOR,
|
|
@@ -68,7 +67,10 @@ import {
|
|
|
68
67
|
SKILLS_DIR_NAME,
|
|
69
68
|
VERSION,
|
|
70
69
|
buildUserIdentityPrompt
|
|
71
|
-
} from "./chunk-
|
|
70
|
+
} from "./chunk-S4WDPHKS.js";
|
|
71
|
+
import {
|
|
72
|
+
AuthManager
|
|
73
|
+
} from "./chunk-BYNY5JPB.js";
|
|
72
74
|
|
|
73
75
|
// src/web/server.ts
|
|
74
76
|
import express from "express";
|
|
@@ -1481,7 +1483,7 @@ Tokens: in=${this.sessionTokenUsage.inputTokens} out=${this.sessionTokenUsage.ou
|
|
|
1481
1483
|
" /checkpoint [save|restore|delete] <name> \u2014 Session checkpoints",
|
|
1482
1484
|
" /fork [checkpoint] \u2014 Fork session from checkpoint or current",
|
|
1483
1485
|
" /branch [list|new|switch|delete|rename] \u2014 Manage conversation branches",
|
|
1484
|
-
" /index [status|rebuild|clear] \u2014 Symbol index
|
|
1486
|
+
" /index [status|rebuild|clear|semantic-rebuild|semantic-clear] \u2014 Symbol + semantic index (find_symbol / search_code)",
|
|
1485
1487
|
" /review [--staged] \u2014 AI code review from git diff",
|
|
1486
1488
|
" /security-review \u2014 Security vulnerability scan on git diff",
|
|
1487
1489
|
" /rewind [list|<n>] \u2014 Rewind conversation & restore files to checkpoint",
|
|
@@ -1907,11 +1909,14 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
1907
1909
|
const root = process.cwd();
|
|
1908
1910
|
const { loadIndex, clearIndex } = await import("./store-S24SPPDZ.js");
|
|
1909
1911
|
const { indexProject } = await import("./indexer-C7QYYHSZ.js");
|
|
1912
|
+
const { loadVectorStore, clearVectorStore } = await import("./vector-store-YTVHACBV.js");
|
|
1910
1913
|
if (sub === "status") {
|
|
1911
1914
|
const idx = loadIndex(root);
|
|
1915
|
+
const vec = loadVectorStore(root);
|
|
1912
1916
|
if (!idx) {
|
|
1913
1917
|
this.send({ type: "info", message: `No symbol index for ${root}. Run /index rebuild.` });
|
|
1914
1918
|
} else {
|
|
1919
|
+
const semantic = vec ? `${vec.count} vectors \xD7 ${vec.dim}-dim` : "not built \u2014 run /index semantic-rebuild to enable search_code";
|
|
1915
1920
|
this.send({
|
|
1916
1921
|
type: "info",
|
|
1917
1922
|
message: [
|
|
@@ -1919,7 +1924,8 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
1919
1924
|
` Root: ${idx.root}`,
|
|
1920
1925
|
` Generated: ${idx.generated}`,
|
|
1921
1926
|
` Files: ${idx.fileCount}`,
|
|
1922
|
-
` Symbols: ${idx.symbolCount}
|
|
1927
|
+
` Symbols: ${idx.symbolCount}`,
|
|
1928
|
+
` Semantic: ${semantic}`
|
|
1923
1929
|
].join("\n")
|
|
1924
1930
|
});
|
|
1925
1931
|
}
|
|
@@ -1932,9 +1938,38 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
1932
1938
|
});
|
|
1933
1939
|
} else if (sub === "clear") {
|
|
1934
1940
|
clearIndex(root);
|
|
1935
|
-
|
|
1941
|
+
clearVectorStore(root);
|
|
1942
|
+
this.send({ type: "info", message: `\u2713 Cleared symbol + semantic index for ${root}` });
|
|
1943
|
+
} else if (sub === "semantic-rebuild") {
|
|
1944
|
+
const idx = loadIndex(root);
|
|
1945
|
+
if (!idx) {
|
|
1946
|
+
this.send({ type: "error", message: "No symbol index yet. Run /index rebuild first." });
|
|
1947
|
+
break;
|
|
1948
|
+
}
|
|
1949
|
+
this.send({
|
|
1950
|
+
type: "info",
|
|
1951
|
+
message: `Building semantic index for ${idx.symbolCount} symbols\u2026 (first run downloads ~117 MB model)`
|
|
1952
|
+
});
|
|
1953
|
+
try {
|
|
1954
|
+
const { rebuildSemanticIndex } = await import("./semantic-RBWU76MD.js");
|
|
1955
|
+
const stats = await rebuildSemanticIndex(root);
|
|
1956
|
+
const first = stats.modelFirstLoadMs ? ` (model load+first batch ${stats.modelFirstLoadMs}ms)` : "";
|
|
1957
|
+
this.send({
|
|
1958
|
+
type: "info",
|
|
1959
|
+
message: `\u2713 Embedded ${stats.symbolsEmbedded} symbols in ${stats.durationMs}ms${first}`
|
|
1960
|
+
});
|
|
1961
|
+
} catch (err) {
|
|
1962
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1963
|
+
this.send({ type: "error", message: `Semantic rebuild failed: ${msg}` });
|
|
1964
|
+
}
|
|
1965
|
+
} else if (sub === "semantic-clear") {
|
|
1966
|
+
clearVectorStore(root);
|
|
1967
|
+
this.send({ type: "info", message: "\u2713 Cleared semantic index (symbol index preserved)" });
|
|
1936
1968
|
} else {
|
|
1937
|
-
this.send({
|
|
1969
|
+
this.send({
|
|
1970
|
+
type: "error",
|
|
1971
|
+
message: `Unknown subcommand: ${sub}. Use status/rebuild/clear/semantic-rebuild/semantic-clear.`
|
|
1972
|
+
});
|
|
1938
1973
|
}
|
|
1939
1974
|
break;
|
|
1940
1975
|
}
|
|
@@ -2103,7 +2138,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
2103
2138
|
case "test": {
|
|
2104
2139
|
this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
|
|
2105
2140
|
try {
|
|
2106
|
-
const { executeTests } = await import("./run-tests-
|
|
2141
|
+
const { executeTests } = await import("./run-tests-GEZ4NPWJ.js");
|
|
2107
2142
|
const argStr = args.join(" ").trim();
|
|
2108
2143
|
let testArgs = {};
|
|
2109
2144
|
if (argStr) {
|
|
@@ -4,15 +4,17 @@ import {
|
|
|
4
4
|
getDangerLevel,
|
|
5
5
|
googleSearchContext,
|
|
6
6
|
truncateOutput
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-LKDX2GOW.js";
|
|
8
|
+
import "./chunk-2ZD3YTVM.js";
|
|
8
9
|
import "./chunk-4BKXL7SM.js";
|
|
9
10
|
import "./chunk-NHNWUBXB.js";
|
|
11
|
+
import "./chunk-HPDDAXFY.js";
|
|
10
12
|
import "./chunk-6VRJGH25.js";
|
|
11
|
-
import "./chunk-
|
|
12
|
-
import "./chunk-
|
|
13
|
+
import "./chunk-PFYAAX2S.js";
|
|
14
|
+
import "./chunk-QATT4NCL.js";
|
|
13
15
|
import {
|
|
14
16
|
SUBAGENT_ALLOWED_TOOLS
|
|
15
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-S4WDPHKS.js";
|
|
16
18
|
|
|
17
19
|
// src/hub/task-orchestrator.ts
|
|
18
20
|
import { createInterface } from "readline";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jinzd-ai-cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.77",
|
|
4
4
|
"description": "Cross-platform REPL-style AI CLI with multi-provider support",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -88,6 +88,7 @@
|
|
|
88
88
|
"dependencies": {
|
|
89
89
|
"@anthropic-ai/sdk": "^0.39.0",
|
|
90
90
|
"@google/generative-ai": "^0.24.0",
|
|
91
|
+
"@huggingface/transformers": "^4.1.0",
|
|
91
92
|
"@inquirer/prompts": "^7.0.0",
|
|
92
93
|
"chalk": "^5.4.1",
|
|
93
94
|
"commander": "^13.0.0",
|