jinzd-ai-cli 0.4.75 → 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.
@@ -1,15 +1,25 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- fileCheckpoints
4
- } from "./chunk-4BKXL7SM.js";
5
- import {
6
- runTestsTool
7
- } from "./chunk-3BHGEPIT.js";
8
2
  import {
9
3
  EnvLoader,
10
4
  NetworkError,
11
5
  ToolError
12
6
  } from "./chunk-2ZD3YTVM.js";
7
+ import {
8
+ fileCheckpoints
9
+ } from "./chunk-4BKXL7SM.js";
10
+ import {
11
+ indexProject
12
+ } from "./chunk-NHNWUBXB.js";
13
+ import {
14
+ hasSemanticIndex,
15
+ semanticSearch
16
+ } from "./chunk-HPDDAXFY.js";
17
+ import {
18
+ loadIndex
19
+ } from "./chunk-6VRJGH25.js";
20
+ import {
21
+ runTestsTool
22
+ } from "./chunk-QATT4NCL.js";
13
23
  import {
14
24
  CONFIG_DIR_NAME,
15
25
  DEFAULT_MAX_TOOL_OUTPUT_CHARS_CAP,
@@ -17,7 +27,7 @@ import {
17
27
  SUBAGENT_ALLOWED_TOOLS,
18
28
  SUBAGENT_DEFAULT_MAX_ROUNDS,
19
29
  SUBAGENT_MAX_ROUNDS_LIMIT
20
- } from "./chunk-5P4QTZBI.js";
30
+ } from "./chunk-S4WDPHKS.js";
21
31
 
22
32
  // src/tools/builtin/bash.ts
23
33
  import { execSync } from "child_process";
@@ -1028,8 +1038,8 @@ function checkPermission(toolName, args, dangerLevel, rules, defaultAction = "co
1028
1038
  if (rule.when) {
1029
1039
  if (rule.when.dangerLevel && rule.when.dangerLevel !== dangerLevel) continue;
1030
1040
  if (rule.when.pathPattern) {
1031
- const path = String(args["path"] ?? args["command"] ?? "");
1032
- if (!path.includes(rule.when.pathPattern)) continue;
1041
+ const path3 = String(args["path"] ?? args["command"] ?? "");
1042
+ if (!path3.includes(rule.when.pathPattern)) continue;
1033
1043
  }
1034
1044
  }
1035
1045
  return rule.action;
@@ -1729,6 +1739,13 @@ Important: For long content (over 500 lines or 3000 chars), you MUST split into
1729
1739
  }
1730
1740
  const lines = content.split("\n").length;
1731
1741
  const mode = appendMode ? "appended" : "written";
1742
+ void (async () => {
1743
+ try {
1744
+ const { updateFile } = await import("./indexer-C7QYYHSZ.js");
1745
+ await updateFile(process.cwd(), filePath);
1746
+ } catch {
1747
+ }
1748
+ })();
1732
1749
  return `File ${mode}: ${filePath} (${lines} lines, ${content.length} bytes)`;
1733
1750
  }
1734
1751
  };
@@ -4286,6 +4303,281 @@ var notebookEditTool = {
4286
4303
  }
4287
4304
  };
4288
4305
 
4306
+ // src/tools/builtin/symbol-tools.ts
4307
+ import path2 from "path";
4308
+
4309
+ // src/symbols/queries.ts
4310
+ import path from "path";
4311
+ import fs from "fs";
4312
+ function findSymbol(index, opts) {
4313
+ const name = opts.name.toLowerCase();
4314
+ const exact = opts.exact !== false;
4315
+ const kinds = opts.kind ? new Set(Array.isArray(opts.kind) ? opts.kind : [opts.kind]) : null;
4316
+ const limit = opts.limit ?? 50;
4317
+ const out = [];
4318
+ for (const s of index.symbols) {
4319
+ if (kinds && !kinds.has(s.kind)) continue;
4320
+ const lower = s.name.toLowerCase();
4321
+ if (exact ? lower === name : lower.includes(name)) {
4322
+ out.push(s);
4323
+ if (out.length >= limit) break;
4324
+ }
4325
+ }
4326
+ return out;
4327
+ }
4328
+ function getOutline(index, file) {
4329
+ const abs = path.resolve(file);
4330
+ return index.symbols.filter((s) => s.location.file === abs).sort((a, b) => a.location.line - b.location.line).map((s) => ({
4331
+ name: s.name,
4332
+ kind: s.kind,
4333
+ line: s.location.line,
4334
+ signature: s.signature,
4335
+ container: s.container,
4336
+ exported: s.exported
4337
+ }));
4338
+ }
4339
+ function findReferences(index, name, opts = {}) {
4340
+ const maxFiles = opts.maxFiles ?? 2e3;
4341
+ const maxHits = opts.maxHits ?? 500;
4342
+ const re = new RegExp(`\\b${name.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\$&")}\\b`);
4343
+ const hits = [];
4344
+ const files = Object.keys(index.files).slice(0, maxFiles);
4345
+ for (const file of files) {
4346
+ let content;
4347
+ try {
4348
+ content = fs.readFileSync(file, "utf-8");
4349
+ } catch {
4350
+ continue;
4351
+ }
4352
+ if (!re.test(content)) continue;
4353
+ const lines = content.split(/\r?\n/);
4354
+ for (let i = 0; i < lines.length; i++) {
4355
+ const m = re.exec(lines[i]);
4356
+ if (m) {
4357
+ hits.push({
4358
+ file,
4359
+ line: i + 1,
4360
+ column: m.index,
4361
+ text: lines[i].trim().slice(0, 200)
4362
+ });
4363
+ if (hits.length >= maxHits) return hits;
4364
+ }
4365
+ }
4366
+ }
4367
+ return hits;
4368
+ }
4369
+
4370
+ // src/tools/builtin/symbol-tools.ts
4371
+ var NO_INDEX_HINT = "No symbol index exists for this project yet. Ask the user to run `/index rebuild`, or call indexProject() first. Alternatively, use grep_files for a slower fallback.";
4372
+ async function ensureIndex(root, autoBuild) {
4373
+ let index = loadIndex(root);
4374
+ if (!index && autoBuild) {
4375
+ const result = await indexProject(root);
4376
+ index = result.index;
4377
+ }
4378
+ return index;
4379
+ }
4380
+ var findSymbolTool = {
4381
+ definition: {
4382
+ name: "find_symbol",
4383
+ description: "Locate symbol definitions (functions, classes, methods, interfaces, types, variables) across the project using the pre-built symbol index. Orders of magnitude faster than grep_files for definition lookups. Returns file:line locations plus a one-line signature.",
4384
+ parameters: {
4385
+ name: {
4386
+ type: "string",
4387
+ description: 'Symbol name to find (e.g. "handleChat", "ProviderRegistry")',
4388
+ required: true
4389
+ },
4390
+ exact: {
4391
+ type: "boolean",
4392
+ description: "Exact match (default true). When false, substring match (case-insensitive).",
4393
+ required: false
4394
+ },
4395
+ kind: {
4396
+ type: "string",
4397
+ description: "Restrict to one kind: function, method, class, interface, type, enum, variable, property. Omit to search all.",
4398
+ required: false
4399
+ },
4400
+ path: {
4401
+ type: "string",
4402
+ description: "Project root (defaults to current working directory).",
4403
+ required: false
4404
+ },
4405
+ limit: {
4406
+ type: "number",
4407
+ description: "Max results (default 50).",
4408
+ required: false
4409
+ }
4410
+ }
4411
+ },
4412
+ async execute(args) {
4413
+ const name = String(args.name ?? "").trim();
4414
+ if (!name) return "Error: `name` is required.";
4415
+ const root = path2.resolve(String(args.path ?? process.cwd()));
4416
+ const index = await ensureIndex(root, true);
4417
+ if (!index) return NO_INDEX_HINT;
4418
+ const kindArg = args.kind ? String(args.kind) : void 0;
4419
+ const hits = findSymbol(index, {
4420
+ name,
4421
+ exact: args.exact !== false,
4422
+ kind: kindArg,
4423
+ limit: typeof args.limit === "number" ? args.limit : 50
4424
+ });
4425
+ if (hits.length === 0) {
4426
+ return `No symbol matching "${name}" found (index has ${index.symbolCount} symbols across ${index.fileCount} files).`;
4427
+ }
4428
+ const lines = hits.map((s) => {
4429
+ const rel = path2.relative(index.root, s.location.file) || s.location.file;
4430
+ const container = s.container ? ` (in ${s.container})` : "";
4431
+ const exp = s.exported ? " [exported]" : "";
4432
+ const sig = s.signature ? `
4433
+ ${s.signature}` : "";
4434
+ return `${s.kind} ${s.name}${container}${exp} \u2014 ${rel}:${s.location.line}${sig}`;
4435
+ });
4436
+ return `Found ${hits.length} symbol(s):
4437
+ ${lines.join("\n")}`;
4438
+ }
4439
+ };
4440
+ var getOutlineTool = {
4441
+ definition: {
4442
+ name: "get_outline",
4443
+ description: "Return the complete list of top-level declarations in a single source file (classes, functions, methods, types), sorted by line number. Use this to understand file structure without reading its full contents.",
4444
+ parameters: {
4445
+ file: {
4446
+ type: "string",
4447
+ description: "Path to the source file (absolute, or relative to project root).",
4448
+ required: true
4449
+ },
4450
+ path: {
4451
+ type: "string",
4452
+ description: "Project root (defaults to current working directory).",
4453
+ required: false
4454
+ }
4455
+ }
4456
+ },
4457
+ async execute(args) {
4458
+ const file = String(args.file ?? "").trim();
4459
+ if (!file) return "Error: `file` is required.";
4460
+ const root = path2.resolve(String(args.path ?? process.cwd()));
4461
+ const absFile = path2.isAbsolute(file) ? file : path2.resolve(root, file);
4462
+ const index = await ensureIndex(root, true);
4463
+ if (!index) return NO_INDEX_HINT;
4464
+ const outline = getOutline(index, absFile);
4465
+ if (outline.length === 0) {
4466
+ return `No indexed symbols found for ${path2.relative(root, absFile) || absFile}. The file may be of an unsupported language (supported: TS/JS/TSX/JSX/Python), unreadable, or not yet re-indexed.`;
4467
+ }
4468
+ const rel = path2.relative(root, absFile) || absFile;
4469
+ const lines = outline.map((o) => {
4470
+ const container = o.container ? ` (in ${o.container})` : "";
4471
+ const exp = o.exported ? " [exported]" : "";
4472
+ return ` ${String(o.line).padStart(5)}: ${o.kind} ${o.name}${container}${exp}`;
4473
+ });
4474
+ return `Outline of ${rel} (${outline.length} symbols):
4475
+ ${lines.join("\n")}`;
4476
+ }
4477
+ };
4478
+ var findReferencesTool = {
4479
+ definition: {
4480
+ name: "find_references",
4481
+ description: "Find all textual references to a symbol name across indexed files (whole-word match). MVP: regex-based \u2014 may include unrelated identifiers with the same name. Pair with find_symbol to locate the definition.",
4482
+ parameters: {
4483
+ name: {
4484
+ type: "string",
4485
+ description: "Symbol name to find references for.",
4486
+ required: true
4487
+ },
4488
+ path: {
4489
+ type: "string",
4490
+ description: "Project root (defaults to current working directory).",
4491
+ required: false
4492
+ },
4493
+ max_hits: {
4494
+ type: "number",
4495
+ description: "Max reference hits to return (default 200).",
4496
+ required: false
4497
+ }
4498
+ }
4499
+ },
4500
+ async execute(args) {
4501
+ const name = String(args.name ?? "").trim();
4502
+ if (!name) return "Error: `name` is required.";
4503
+ const root = path2.resolve(String(args.path ?? process.cwd()));
4504
+ const index = await ensureIndex(root, true);
4505
+ if (!index) return NO_INDEX_HINT;
4506
+ const maxHits = typeof args.max_hits === "number" ? args.max_hits : 200;
4507
+ const hits = findReferences(index, name, { maxHits });
4508
+ if (hits.length === 0) {
4509
+ return `No references to "${name}" found in ${index.fileCount} indexed files.`;
4510
+ }
4511
+ const lines = hits.map((h) => {
4512
+ const rel = path2.relative(index.root, h.file) || h.file;
4513
+ return `${rel}:${h.line}:${h.column} ${h.text}`;
4514
+ });
4515
+ return `Found ${hits.length} reference(s) to "${name}":
4516
+ ${lines.join("\n")}`;
4517
+ }
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
+ };
4580
+
4289
4581
  // src/core/token-estimator.ts
4290
4582
  var CJK_REGEX = /[\u2E80-\u9FFF\uA000-\uA4FF\uAC00-\uD7FF\uF900-\uFAFF\uFE30-\uFE4F\uFF00-\uFFEF]/g;
4291
4583
  function estimateTokens(text) {
@@ -4341,6 +4633,10 @@ var ToolRegistry = class {
4341
4633
  this.register(gitLogTool);
4342
4634
  this.register(gitCommitTool);
4343
4635
  this.register(notebookEditTool);
4636
+ this.register(findSymbolTool);
4637
+ this.register(getOutlineTool);
4638
+ this.register(findReferencesTool);
4639
+ this.register(searchCodeTool);
4344
4640
  }
4345
4641
  register(tool) {
4346
4642
  this.tools.set(tool.definition.name, tool);
@@ -8,7 +8,7 @@ import {
8
8
  CONFIG_FILE_NAME,
9
9
  HISTORY_DIR_NAME,
10
10
  PLUGINS_DIR_NAME
11
- } from "./chunk-5P4QTZBI.js";
11
+ } from "./chunk-S4WDPHKS.js";
12
12
 
13
13
  // src/config/config-manager.ts
14
14
  import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";