jinzd-ai-cli 0.4.23 → 0.4.25

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.
@@ -8,7 +8,7 @@ import { platform } from "os";
8
8
  import chalk from "chalk";
9
9
 
10
10
  // src/core/constants.ts
11
- var VERSION = "0.4.23";
11
+ var VERSION = "0.4.25";
12
12
  var APP_NAME = "ai-cli";
13
13
  var CONFIG_DIR_NAME = ".aicli";
14
14
  var CONFIG_FILE_NAME = "config.json";
@@ -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.23";
9
+ var VERSION = "0.4.25";
10
10
  var APP_NAME = "ai-cli";
11
11
  var CONFIG_DIR_NAME = ".aicli";
12
12
  var CONFIG_FILE_NAME = "config.json";
@@ -0,0 +1,96 @@
1
+ // src/tools/file-checkpoint.ts
2
+ import { existsSync, readFileSync, writeFileSync, unlinkSync } from "fs";
3
+ var MAX_SNAPSHOTS = 200;
4
+ var FileCheckpointStore = class {
5
+ snapshots = [];
6
+ /**
7
+ * 在文件写入前调用,保存当前文件内容。
8
+ * 同一 messageIndex 下同一文件只记录首次快照。
9
+ */
10
+ snapshot(filePath, messageIndex) {
11
+ const exists = this.snapshots.some(
12
+ (s) => s.filePath === filePath && s.messageIndex === messageIndex
13
+ );
14
+ if (exists) return;
15
+ let content = null;
16
+ if (existsSync(filePath)) {
17
+ try {
18
+ content = readFileSync(filePath, "utf-8");
19
+ } catch {
20
+ return;
21
+ }
22
+ }
23
+ this.snapshots.push({
24
+ filePath,
25
+ content,
26
+ messageIndex,
27
+ timestamp: Date.now()
28
+ });
29
+ if (this.snapshots.length > MAX_SNAPSHOTS) {
30
+ this.snapshots = this.snapshots.slice(-MAX_SNAPSHOTS);
31
+ }
32
+ }
33
+ /**
34
+ * 恢复所有在指定消息索引之后被修改的文件到该索引时的状态。
35
+ * @returns 恢复和删除的文件数量
36
+ */
37
+ restoreToMessageIndex(targetIndex) {
38
+ const modifiedAfter = this.snapshots.filter((s) => s.messageIndex > targetIndex);
39
+ const uniqueFiles = [...new Set(modifiedAfter.map((s) => s.filePath))];
40
+ let restored = 0;
41
+ let deleted = 0;
42
+ const files = [];
43
+ for (const filePath of uniqueFiles) {
44
+ const beforeSnapshots = this.snapshots.filter((s) => s.filePath === filePath && s.messageIndex <= targetIndex).sort((a, b) => b.messageIndex - a.messageIndex);
45
+ const restoreTo = beforeSnapshots[0];
46
+ try {
47
+ if (restoreTo) {
48
+ if (restoreTo.content === null) {
49
+ if (existsSync(filePath)) {
50
+ unlinkSync(filePath);
51
+ deleted++;
52
+ files.push(filePath);
53
+ }
54
+ } else {
55
+ writeFileSync(filePath, restoreTo.content, "utf-8");
56
+ restored++;
57
+ files.push(filePath);
58
+ }
59
+ } else {
60
+ const earliest = this.snapshots.filter((s) => s.filePath === filePath).sort((a, b) => a.messageIndex - b.messageIndex)[0];
61
+ if (earliest && earliest.content === null) {
62
+ if (existsSync(filePath)) {
63
+ unlinkSync(filePath);
64
+ deleted++;
65
+ files.push(filePath);
66
+ }
67
+ } else if (earliest) {
68
+ writeFileSync(filePath, earliest.content, "utf-8");
69
+ restored++;
70
+ files.push(filePath);
71
+ }
72
+ }
73
+ } catch {
74
+ }
75
+ }
76
+ this.snapshots = this.snapshots.filter((s) => s.messageIndex <= targetIndex);
77
+ return { restored, deleted, files };
78
+ }
79
+ /** 获取所有快照(供调试/显示) */
80
+ getSnapshots() {
81
+ return this.snapshots;
82
+ }
83
+ /** 获取有快照的消息索引列表(去重排序) */
84
+ getMessageIndices() {
85
+ return [...new Set(this.snapshots.map((s) => s.messageIndex))].sort((a, b) => a - b);
86
+ }
87
+ /** 清空所有快照(新会话/清除时调用) */
88
+ clear() {
89
+ this.snapshots = [];
90
+ }
91
+ };
92
+ var fileCheckpoints = new FileCheckpointStore();
93
+
94
+ export {
95
+ fileCheckpoints
96
+ };
@@ -7,7 +7,7 @@ import {
7
7
  ProviderNotFoundError,
8
8
  RateLimitError,
9
9
  schemaToJsonSchema
10
- } from "./chunk-PDVX5QJA.js";
10
+ } from "./chunk-5GZQLJAY.js";
11
11
  import {
12
12
  APP_NAME,
13
13
  CONFIG_DIR_NAME,
@@ -20,7 +20,7 @@ import {
20
20
  MCP_TOOL_PREFIX,
21
21
  PLUGINS_DIR_NAME,
22
22
  VERSION
23
- } from "./chunk-UA4BVWKV.js";
23
+ } from "./chunk-AHH5I2U6.js";
24
24
 
25
25
  // src/config/config-manager.ts
26
26
  import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
@@ -3239,159 +3239,6 @@ async function setupProxy(configProxy) {
3239
3239
  }
3240
3240
  }
3241
3241
 
3242
- // src/tools/diff-utils.ts
3243
- import chalk from "chalk";
3244
- function renderDiff(oldText, newText, opts = {}) {
3245
- const contextLines = opts.contextLines ?? 3;
3246
- const maxLines = opts.maxLines ?? 120;
3247
- const filePath = opts.filePath ?? "";
3248
- const oldLines = oldText.split("\n");
3249
- const newLines = newText.split("\n");
3250
- const hunks = computeHunks(oldLines, newLines, contextLines);
3251
- if (hunks.length === 0) {
3252
- return chalk.dim(" (no changes)");
3253
- }
3254
- const output = [];
3255
- if (filePath) {
3256
- output.push(chalk.bold.white(`--- ${filePath} (before)`));
3257
- output.push(chalk.bold.white(`+++ ${filePath} (after)`));
3258
- }
3259
- let totalDisplayed = 0;
3260
- for (const hunk of hunks) {
3261
- if (totalDisplayed >= maxLines) {
3262
- output.push(chalk.dim(` ... (diff truncated, too many changes)`));
3263
- break;
3264
- }
3265
- output.push(
3266
- chalk.cyan(
3267
- `@@ -${hunk.oldStart + 1},${hunk.oldCount} +${hunk.newStart + 1},${hunk.newCount} @@`
3268
- )
3269
- );
3270
- for (const line of hunk.lines) {
3271
- if (totalDisplayed >= maxLines) break;
3272
- totalDisplayed++;
3273
- if (line.type === "context") {
3274
- output.push(chalk.dim(` ${line.text}`));
3275
- } else if (line.type === "remove") {
3276
- output.push(chalk.red(`- ${line.text}`));
3277
- } else {
3278
- output.push(chalk.green(`+ ${line.text}`));
3279
- }
3280
- }
3281
- }
3282
- return output.join("\n");
3283
- }
3284
- function computeHunks(oldLines, newLines, contextLines) {
3285
- const edits = diffLines(oldLines, newLines);
3286
- if (edits.every((e) => e.type === "context")) return [];
3287
- const hunks = [];
3288
- let i = 0;
3289
- while (i < edits.length) {
3290
- if (edits[i].type === "context") {
3291
- i++;
3292
- continue;
3293
- }
3294
- const start = Math.max(0, i - contextLines);
3295
- let end = i;
3296
- while (end < edits.length) {
3297
- if (edits[end].type !== "context") {
3298
- end++;
3299
- } else {
3300
- let hasMoreChange = false;
3301
- for (let j = end + 1; j < Math.min(edits.length, end + contextLines * 2 + 1); j++) {
3302
- if (edits[j].type !== "context") {
3303
- hasMoreChange = true;
3304
- break;
3305
- }
3306
- }
3307
- if (hasMoreChange) {
3308
- end++;
3309
- } else {
3310
- break;
3311
- }
3312
- }
3313
- }
3314
- end = Math.min(edits.length, end + contextLines);
3315
- const hunkEdits = edits.slice(start, end);
3316
- let oldStart = 0;
3317
- let newStart = 0;
3318
- for (let k = 0; k < start; k++) {
3319
- if (edits[k].type !== "add") oldStart++;
3320
- if (edits[k].type !== "remove") newStart++;
3321
- }
3322
- let oldCount = 0;
3323
- let newCount = 0;
3324
- for (const e of hunkEdits) {
3325
- if (e.type !== "add") oldCount++;
3326
- if (e.type !== "remove") newCount++;
3327
- }
3328
- hunks.push({
3329
- oldStart,
3330
- oldCount,
3331
- newStart,
3332
- newCount,
3333
- lines: hunkEdits.map((e) => ({ type: e.type, text: e.text }))
3334
- });
3335
- i = end;
3336
- }
3337
- return hunks;
3338
- }
3339
- function diffLines(oldLines, newLines) {
3340
- const n = oldLines.length;
3341
- const m = newLines.length;
3342
- if (n * m > 25e4) {
3343
- return simpleDiff(oldLines, newLines);
3344
- }
3345
- const dp = Array.from({ length: n + 1 }, () => new Array(m + 1).fill(0));
3346
- for (let i2 = 1; i2 <= n; i2++) {
3347
- for (let j2 = 1; j2 <= m; j2++) {
3348
- if (oldLines[i2 - 1] === newLines[j2 - 1]) {
3349
- dp[i2][j2] = dp[i2 - 1][j2 - 1] + 1;
3350
- } else {
3351
- dp[i2][j2] = Math.max(dp[i2 - 1][j2], dp[i2][j2 - 1]);
3352
- }
3353
- }
3354
- }
3355
- const result = [];
3356
- let i = n;
3357
- let j = m;
3358
- while (i > 0 || j > 0) {
3359
- if (i > 0 && j > 0 && oldLines[i - 1] === newLines[j - 1]) {
3360
- result.unshift({ type: "context", text: oldLines[i - 1] });
3361
- i--;
3362
- j--;
3363
- } else if (j > 0 && (i === 0 || dp[i][j - 1] >= dp[i - 1][j])) {
3364
- result.unshift({ type: "add", text: newLines[j - 1] });
3365
- j--;
3366
- } else {
3367
- result.unshift({ type: "remove", text: oldLines[i - 1] });
3368
- i--;
3369
- }
3370
- }
3371
- return result;
3372
- }
3373
- function simpleDiff(oldLines, newLines) {
3374
- const result = [];
3375
- const maxLen = Math.max(oldLines.length, newLines.length);
3376
- for (let i = 0; i < maxLen; i++) {
3377
- const o = oldLines[i];
3378
- const n = newLines[i];
3379
- if (o !== void 0 && n !== void 0) {
3380
- if (o === n) {
3381
- result.push({ type: "context", text: o });
3382
- } else {
3383
- result.push({ type: "remove", text: o });
3384
- result.push({ type: "add", text: n });
3385
- }
3386
- } else if (o !== void 0) {
3387
- result.push({ type: "remove", text: o });
3388
- } else if (n !== void 0) {
3389
- result.push({ type: "add", text: n });
3390
- }
3391
- }
3392
- return result;
3393
- }
3394
-
3395
3242
  // src/repl/dev-state.ts
3396
3243
  import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync3, unlinkSync as unlinkSync2, mkdirSync as mkdirSync4 } from "fs";
3397
3244
  import { join as join5 } from "path";
@@ -3482,46 +3329,6 @@ function clearDevState() {
3482
3329
  }
3483
3330
  }
3484
3331
 
3485
- // src/tools/hooks.ts
3486
- import { execSync as execSync2 } from "child_process";
3487
- function shellEscape(value) {
3488
- return "'" + value.replace(/'/g, "'\\''") + "'";
3489
- }
3490
- function runHook(template, vars) {
3491
- if (!template) return;
3492
- let cmd = template;
3493
- cmd = cmd.replace(/\{tool\}/g, shellEscape(vars.tool));
3494
- cmd = cmd.replace(/\{dangerLevel\}/g, shellEscape(vars.dangerLevel ?? ""));
3495
- cmd = cmd.replace(/\{args\}/g, shellEscape(vars.args ?? ""));
3496
- cmd = cmd.replace(/\{status\}/g, shellEscape(vars.status ?? ""));
3497
- try {
3498
- execSync2(cmd, {
3499
- timeout: 5e3,
3500
- stdio: ["pipe", "pipe", "pipe"],
3501
- encoding: "utf-8"
3502
- });
3503
- } catch {
3504
- process.stderr.write(`\u26A0 Hook failed: ${cmd.slice(0, 100)}
3505
- `);
3506
- }
3507
- }
3508
-
3509
- // src/tools/permissions.ts
3510
- function checkPermission(toolName, args, dangerLevel, rules, defaultAction = "confirm") {
3511
- for (const rule of rules) {
3512
- if (rule.tool !== "*" && rule.tool !== toolName) continue;
3513
- if (rule.when) {
3514
- if (rule.when.dangerLevel && rule.when.dangerLevel !== dangerLevel) continue;
3515
- if (rule.when.pathPattern) {
3516
- const path = String(args["path"] ?? args["command"] ?? "");
3517
- if (!path.includes(rule.when.pathPattern)) continue;
3518
- }
3519
- }
3520
- return rule.action;
3521
- }
3522
- return defaultAction;
3523
- }
3524
-
3525
3332
  export {
3526
3333
  ConfigManager,
3527
3334
  detectsHallucinatedFileOp,
@@ -3534,9 +3341,6 @@ export {
3534
3341
  getGitRoot,
3535
3342
  getGitContext,
3536
3343
  formatGitContextForPrompt,
3537
- renderDiff,
3538
- runHook,
3539
- checkPermission,
3540
3344
  parseSimpleYaml,
3541
3345
  SNAPSHOT_PROMPT,
3542
3346
  sessionHasMeaningfulContent,
@@ -387,7 +387,7 @@ ${content}`);
387
387
  }
388
388
  }
389
389
  async function runTaskMode(config, providers, configManager, topic) {
390
- const { TaskOrchestrator } = await import("./task-orchestrator-C472QXTJ.js");
390
+ const { TaskOrchestrator } = await import("./task-orchestrator-4N5UUA6L.js");
391
391
  const orchestrator = new TaskOrchestrator(config, providers, configManager);
392
392
  let interrupted = false;
393
393
  const onSigint = () => {