jinzd-ai-cli 0.4.88 → 0.4.89

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.
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ConfigManager
4
- } from "./chunk-QT2KNL3V.js";
4
+ } from "./chunk-3O3U3L5W.js";
5
5
  import "./chunk-2ZD3YTVM.js";
6
- import "./chunk-VGXNE37B.js";
6
+ import "./chunk-E7YC4GWV.js";
7
7
 
8
8
  // src/cli/batch.ts
9
9
  import Anthropic from "@anthropic-ai/sdk";
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ buildChatIndex,
4
+ chunkSession,
5
+ clearChatIndex,
6
+ getChatIndexStatus,
7
+ loadChatIndex,
8
+ searchChatMemory
9
+ } from "./chunk-ANYYM4CF.js";
10
+ import "./chunk-KHYD3WXE.js";
11
+ export {
12
+ buildChatIndex,
13
+ chunkSession,
14
+ clearChatIndex,
15
+ getChatIndexStatus,
16
+ loadChatIndex,
17
+ searchChatMemory
18
+ };
@@ -1,72 +1,28 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ EMBEDDING_DIM
4
+ } from "./chunk-KHYD3WXE.js";
2
5
 
3
6
  // 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
7
+ import fs from "fs";
10
8
  import path from "path";
11
9
  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
10
+ import crypto from "crypto";
55
11
  var MAGIC = 1094927190;
56
12
  var VERSION = 1;
57
13
  var HEADER_BYTES = 16;
58
14
  function indexDir() {
59
- return path2.join(os2.homedir(), ".aicli", "index");
15
+ return path.join(os.homedir(), ".aicli", "index");
60
16
  }
61
17
  function projectHash(root) {
62
- return crypto.createHash("sha1").update(path2.resolve(root).toLowerCase()).digest("hex").slice(0, 16);
18
+ return crypto.createHash("sha1").update(path.resolve(root).toLowerCase()).digest("hex").slice(0, 16);
63
19
  }
64
20
  function vecPath(root) {
65
- return path2.join(indexDir(), `${projectHash(root)}.vec`);
21
+ return path.join(indexDir(), `${projectHash(root)}.vec`);
66
22
  }
67
23
  function emptyVectorStore(root) {
68
24
  return {
69
- root: path2.resolve(root),
25
+ root: path.resolve(root),
70
26
  count: 0,
71
27
  dim: EMBEDDING_DIM,
72
28
  vectors: new Float32Array(0),
@@ -81,7 +37,7 @@ function saveVectorStore(root, indices, vectors) {
81
37
  }
82
38
  const count = indices.length;
83
39
  const dir = indexDir();
84
- fs2.mkdirSync(dir, { recursive: true });
40
+ fs.mkdirSync(dir, { recursive: true });
85
41
  const totalBytes = HEADER_BYTES + count * 4 + count * EMBEDDING_DIM * 4;
86
42
  const buf = Buffer.alloc(totalBytes);
87
43
  buf.writeUInt32LE(MAGIC, 0);
@@ -92,15 +48,15 @@ function saveVectorStore(root, indices, vectors) {
92
48
  Buffer.from(vectors.buffer, vectors.byteOffset, vectors.byteLength).copy(buf, HEADER_BYTES + count * 4);
93
49
  const target = vecPath(root);
94
50
  const tmp = `${target}.tmp`;
95
- fs2.writeFileSync(tmp, buf);
96
- fs2.renameSync(tmp, target);
51
+ fs.writeFileSync(tmp, buf);
52
+ fs.renameSync(tmp, target);
97
53
  }
98
54
  function loadVectorStore(root) {
99
55
  const p = vecPath(root);
100
- if (!fs2.existsSync(p)) return null;
56
+ if (!fs.existsSync(p)) return null;
101
57
  let buf;
102
58
  try {
103
- buf = fs2.readFileSync(p);
59
+ buf = fs.readFileSync(p);
104
60
  } catch {
105
61
  return null;
106
62
  }
@@ -121,12 +77,12 @@ function loadVectorStore(root) {
121
77
  buf.byteOffset + HEADER_BYTES + count * 4 + count * dim * 4
122
78
  )
123
79
  );
124
- return { root: path2.resolve(root), count, dim, vectors, symbolIdx };
80
+ return { root: path.resolve(root), count, dim, vectors, symbolIdx };
125
81
  }
126
82
  function clearVectorStore(root) {
127
83
  const p = vecPath(root);
128
84
  try {
129
- if (fs2.existsSync(p)) fs2.unlinkSync(p);
85
+ if (fs.existsSync(p)) fs.unlinkSync(p);
130
86
  } catch {
131
87
  }
132
88
  }
@@ -158,9 +114,6 @@ function searchVectorStore(store, queryVec, k) {
158
114
  }
159
115
 
160
116
  export {
161
- EMBEDDING_DIM,
162
- embed,
163
- embedOne,
164
117
  emptyVectorStore,
165
118
  saveVectorStore,
166
119
  loadVectorStore,
@@ -8,7 +8,7 @@ import {
8
8
  CONFIG_FILE_NAME,
9
9
  HISTORY_DIR_NAME,
10
10
  PLUGINS_DIR_NAME
11
- } from "./chunk-VGXNE37B.js";
11
+ } from "./chunk-E7YC4GWV.js";
12
12
 
13
13
  // src/config/config-manager.ts
14
14
  import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
@@ -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.88";
9
+ var VERSION = "0.4.89";
10
10
  var APP_NAME = "ai-cli";
11
11
  var CONFIG_DIR_NAME = ".aicli";
12
12
  var CONFIG_FILE_NAME = "config.json";
@@ -7,19 +7,23 @@ import {
7
7
  import {
8
8
  fileCheckpoints
9
9
  } from "./chunk-4BKXL7SM.js";
10
+ import {
11
+ loadChatIndex,
12
+ searchChatMemory
13
+ } from "./chunk-ANYYM4CF.js";
10
14
  import {
11
15
  indexProject
12
16
  } from "./chunk-NHNWUBXB.js";
13
17
  import {
14
18
  hasSemanticIndex,
15
19
  semanticSearch
16
- } from "./chunk-CQQQFNND.js";
20
+ } from "./chunk-KJLJPUY2.js";
17
21
  import {
18
22
  loadIndex
19
23
  } from "./chunk-6VRJGH25.js";
20
24
  import {
21
25
  runTestsTool
22
- } from "./chunk-V3NMERIB.js";
26
+ } from "./chunk-TKYNTXKB.js";
23
27
  import {
24
28
  CONFIG_DIR_NAME,
25
29
  DEFAULT_MAX_TOOL_OUTPUT_CHARS_CAP,
@@ -27,7 +31,7 @@ import {
27
31
  SUBAGENT_ALLOWED_TOOLS,
28
32
  SUBAGENT_DEFAULT_MAX_ROUNDS,
29
33
  SUBAGENT_MAX_ROUNDS_LIMIT
30
- } from "./chunk-VGXNE37B.js";
34
+ } from "./chunk-E7YC4GWV.js";
31
35
 
32
36
  // src/tools/types.ts
33
37
  function isFileWriteTool(name) {
@@ -4578,6 +4582,74 @@ ${lines.join("\n")}`;
4578
4582
  }
4579
4583
  };
4580
4584
 
4585
+ // src/tools/builtin/recall-memory.ts
4586
+ function formatHit(h, i) {
4587
+ const ts = h.chunk.timestamp.slice(0, 16).replace("T", " ");
4588
+ const title = h.chunk.sessionTitle ? ` \xB7 ${h.chunk.sessionTitle}` : "";
4589
+ const sid = h.chunk.sessionId.slice(0, 8);
4590
+ const score = h.score.toFixed(3);
4591
+ const body = h.chunk.text.length > 600 ? h.chunk.text.slice(0, 600) + "\u2026" : h.chunk.text;
4592
+ return `\u2500\u2500\u2500 Hit ${i + 1} (score ${score}, session ${sid}${title}, ${ts}) \u2500\u2500\u2500
4593
+ ` + body;
4594
+ }
4595
+ var recallMemoryTool = {
4596
+ definition: {
4597
+ name: "recall_memory",
4598
+ description: 'Semantic search over past chat sessions. Call this whenever the user references something that may have been discussed before ("last time", "remember", "\u4E4B\u524D", "\u4E0A\u6B21"), when context is ambiguous and continuity matters, or when you want to check what decisions or preferences have been established across prior conversations. Returns up to `topK` relevant snippets with session id, timestamp, and cosine similarity score. Prefer this over asking the user "can you remind me".',
4599
+ parameters: {
4600
+ query: {
4601
+ type: "string",
4602
+ description: "Natural-language description of what to recall. Chinese or English both work.",
4603
+ required: true
4604
+ },
4605
+ topK: {
4606
+ type: "number",
4607
+ description: "Max number of snippets to return (default 5, max 20).",
4608
+ required: false
4609
+ },
4610
+ excludeCurrentSession: {
4611
+ type: "boolean",
4612
+ description: "If true, exclude the current session from results (avoid echoing what you just said). Default false.",
4613
+ required: false
4614
+ },
4615
+ currentSessionId: {
4616
+ type: "string",
4617
+ description: "Session ID to exclude when excludeCurrentSession=true. Usually the active session.",
4618
+ required: false
4619
+ },
4620
+ minScore: {
4621
+ type: "number",
4622
+ description: "Drop hits below this cosine score. Default 0.25. Raise to 0.35+ for stricter matches.",
4623
+ required: false
4624
+ }
4625
+ },
4626
+ dangerous: false
4627
+ },
4628
+ async execute(args) {
4629
+ const query = String(args["query"] ?? "").trim();
4630
+ if (!query) throw new ToolError("recall_memory", "query is required");
4631
+ const topK = Math.max(1, Math.min(20, Number(args["topK"] ?? 5)));
4632
+ const excludeCurrent = Boolean(args["excludeCurrentSession"]);
4633
+ const currentId = args["currentSessionId"] ? String(args["currentSessionId"]) : void 0;
4634
+ const minScore = args["minScore"] !== void 0 ? Number(args["minScore"]) : 0.25;
4635
+ const status = loadChatIndex();
4636
+ if (!status) {
4637
+ return "No chat memory index found. The index is built on REPL startup, or run `/memory rebuild` manually. If you have no past sessions yet, this is expected.";
4638
+ }
4639
+ const hits = await searchChatMemory(query, {
4640
+ topK,
4641
+ minScore,
4642
+ excludeSessionId: excludeCurrent ? currentId : void 0
4643
+ });
4644
+ if (hits.length === 0) {
4645
+ return `No memories matched "${query}" above score ${minScore}. Index has ${status.idx.chunks.length} chunks across ${Object.keys(status.idx.sessionMtimes).length} sessions. Consider lowering minScore to 0.15 or rephrasing the query.`;
4646
+ }
4647
+ const header = `Found ${hits.length} memory hit(s) for "${query}" (min-score ${minScore}):
4648
+ `;
4649
+ return header + "\n" + hits.map(formatHit).join("\n\n");
4650
+ }
4651
+ };
4652
+
4581
4653
  // src/core/token-estimator.ts
4582
4654
  var CJK_REGEX = /[\u2E80-\u9FFF\uA000-\uA4FF\uAC00-\uD7FF\uF900-\uFAFF\uFE30-\uFE4F\uFF00-\uFFEF]/g;
4583
4655
  function estimateTokens(text) {
@@ -4637,6 +4709,7 @@ var ToolRegistry = class {
4637
4709
  this.register(getOutlineTool);
4638
4710
  this.register(findReferencesTool);
4639
4711
  this.register(searchCodeTool);
4712
+ this.register(recallMemoryTool);
4640
4713
  }
4641
4714
  register(tool) {
4642
4715
  this.tools.set(tool.definition.name, tool);