jinzd-ai-cli 0.4.24 → 0.4.26

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,7 +1,7 @@
1
1
  import {
2
2
  executeTests,
3
3
  runTestsTool
4
- } from "./chunk-XMTMCMAP.js";
4
+ } from "./chunk-ETMUP3CY.js";
5
5
  export {
6
6
  executeTests,
7
7
  runTestsTool
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  executeTests,
4
4
  runTestsTool
5
- } from "./chunk-UA4BVWKV.js";
5
+ } from "./chunk-AHH5I2U6.js";
6
6
  export {
7
7
  executeTests,
8
8
  runTestsTool
@@ -7,7 +7,6 @@ import {
7
7
  SessionManager,
8
8
  SkillManager,
9
9
  TOOL_CALL_REMINDER,
10
- checkPermission,
11
10
  detectsHallucinatedFileOp,
12
11
  formatGitContextForPrompt,
13
12
  getContentText,
@@ -15,24 +14,27 @@ import {
15
14
  getGitRoot,
16
15
  hadPreviousWriteToolCalls,
17
16
  loadDevState,
18
- renderDiff,
19
- runHook,
20
17
  setupProxy
21
- } from "./chunk-GBMVHLPA.js";
18
+ } from "./chunk-SS7BQZ5R.js";
22
19
  import {
23
20
  AuthManager
24
21
  } from "./chunk-BYNY5JPB.js";
25
22
  import {
23
+ ToolExecutor,
26
24
  ToolRegistry,
27
25
  askUserContext,
26
+ checkPermission,
28
27
  getDangerLevel,
29
28
  googleSearchContext,
30
29
  isFileWriteTool,
30
+ renderDiff,
31
+ runHook,
31
32
  setContextWindow,
32
33
  spawnAgentContext,
33
34
  truncateOutput,
34
35
  undoStack
35
- } from "./chunk-PDVX5QJA.js";
36
+ } from "./chunk-5GZQLJAY.js";
37
+ import "./chunk-4BKXL7SM.js";
36
38
  import {
37
39
  AGENTIC_BEHAVIOR_GUIDELINE,
38
40
  AUTHOR,
@@ -50,7 +52,7 @@ import {
50
52
  SKILLS_DIR_NAME,
51
53
  VERSION,
52
54
  buildUserIdentityPrompt
53
- } from "./chunk-UA4BVWKV.js";
55
+ } from "./chunk-AHH5I2U6.js";
54
56
 
55
57
  // src/web/server.ts
56
58
  import express from "express";
@@ -815,6 +817,7 @@ You have a maximum of ${MAX_TOOL_ROUNDS} tool call rounds for this task. Plan ef
815
817
  spawnAgentContext.systemPrompt = systemPrompt;
816
818
  spawnAgentContext.modelParams = modelParams;
817
819
  spawnAgentContext.configManager = this.config;
820
+ ToolExecutor.currentMessageIndex = this.sessions.current?.messages.length ?? 0;
818
821
  const toolResults = await this.toolExecutor.executeAll(result.toolCalls);
819
822
  const reasoningContent = result.reasoningContent;
820
823
  const newMsgs = provider.buildToolResultMessages(result.toolCalls, toolResults, reasoningContent);
@@ -1144,6 +1147,8 @@ Tokens: in=${this.sessionTokenUsage.inputTokens} out=${this.sessionTokenUsage.ou
1144
1147
  " /checkpoint [save|restore|delete] <name> \u2014 Session checkpoints",
1145
1148
  " /fork [checkpoint] \u2014 Fork session from checkpoint or current",
1146
1149
  " /review [--staged] \u2014 AI code review from git diff",
1150
+ " /security-review \u2014 Security vulnerability scan on git diff",
1151
+ " /rewind [list|<n>] \u2014 Rewind conversation & restore files to checkpoint",
1147
1152
  " /test [command] \u2014 Run project tests",
1148
1153
  " /init [--force] \u2014 Generate AICLI.md by scanning project",
1149
1154
  " /scaffold <desc> \u2014 Generate project scaffolding with AI",
@@ -1512,11 +1517,96 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
1512
1517
  }
1513
1518
  break;
1514
1519
  }
1520
+ // ── /security-review ──────────────────────────────────────────────
1521
+ case "security-review": {
1522
+ const gitCtx = getGitContext();
1523
+ if (!gitCtx) {
1524
+ this.send({ type: "error", message: "Not a git repository." });
1525
+ break;
1526
+ }
1527
+ const secStaged = args.includes("--staged");
1528
+ let secDiff;
1529
+ try {
1530
+ const cmd = secStaged ? "git diff --staged" : "git diff";
1531
+ secDiff = execSync(cmd, { encoding: "utf-8", timeout: 1e4 }).trim();
1532
+ } catch {
1533
+ this.send({ type: "error", message: "Failed to run git diff." });
1534
+ break;
1535
+ }
1536
+ if (!secDiff) {
1537
+ this.send({ type: "info", message: "No changes to review." + (secStaged ? "" : " Try --staged for staged changes.") });
1538
+ break;
1539
+ }
1540
+ const SEC_MAX_DIFF = 8e3;
1541
+ let secTruncated = false;
1542
+ if (secDiff.length > SEC_MAX_DIFF) {
1543
+ const head = secDiff.slice(0, Math.floor(SEC_MAX_DIFF * 0.7));
1544
+ const tail = secDiff.slice(secDiff.length - Math.floor(SEC_MAX_DIFF * 0.2));
1545
+ secDiff = head + "\n\n... [diff truncated, " + secDiff.length + " chars total] ...\n\n" + tail;
1546
+ secTruncated = true;
1547
+ }
1548
+ const secPrompt = this.buildSecurityReviewPrompt(secDiff, formatGitContextForPrompt(gitCtx));
1549
+ this.send({ type: "info", message: "\u{1F512} Scanning for security vulnerabilities..." });
1550
+ try {
1551
+ const secReview = await this.chatOnce(secPrompt, { temperature: 0.2, maxTokens: 8192 });
1552
+ const secMsg = secTruncated ? secReview + "\n\n\u26A0 Diff was truncated. Consider reviewing smaller changesets." : secReview;
1553
+ this.send({ type: "info", message: secMsg });
1554
+ } catch (err) {
1555
+ this.send({ type: "error", message: `Security review failed: ${err.message}` });
1556
+ }
1557
+ break;
1558
+ }
1559
+ // ── /rewind ────────────────────────────────────────────────────────
1560
+ case "rewind": {
1561
+ const session = this.sessions.current;
1562
+ if (!session || session.messages.length === 0) {
1563
+ this.send({ type: "info", message: "No messages to rewind." });
1564
+ break;
1565
+ }
1566
+ const rewindSub = args[0];
1567
+ if (rewindSub === "list" || !rewindSub) {
1568
+ const lines = [`Conversation messages (${session.messages.length} total):
1569
+ `];
1570
+ const cpIndices = (await import("./file-checkpoint-NKBHGC7L.js")).fileCheckpoints.getMessageIndices();
1571
+ for (let i = 0; i < session.messages.length; i++) {
1572
+ const m = session.messages[i];
1573
+ const text = getContentText(m.content).replace(/\n/g, " ").slice(0, 60);
1574
+ const pin = cpIndices.includes(i) ? " \u{1F4CC}" : "";
1575
+ lines.push(` [${i + 1}] ${m.role.padEnd(10)} ${text}${pin}`);
1576
+ }
1577
+ lines.push("", "Usage: /rewind <n> \u2014 rewind to message N", "\u{1F4CC} = file checkpoint");
1578
+ this.send({ type: "info", message: lines.join("\n") });
1579
+ break;
1580
+ }
1581
+ const rewindN = parseInt(rewindSub, 10);
1582
+ if (isNaN(rewindN) || rewindN < 1 || rewindN > session.messages.length) {
1583
+ this.send({ type: "error", message: `Invalid message number: ${rewindSub}. Range: 1-${session.messages.length}` });
1584
+ break;
1585
+ }
1586
+ const { fileCheckpoints: fc } = await import("./file-checkpoint-NKBHGC7L.js");
1587
+ const rewindRemoved = session.messages.length - rewindN;
1588
+ const rewindResult = fc.restoreToMessageIndex(rewindN);
1589
+ session.messages = session.messages.slice(0, rewindN);
1590
+ session.checkpoints = session.checkpoints.filter((c) => c.messageIndex <= rewindN);
1591
+ session.updated = /* @__PURE__ */ new Date();
1592
+ this.sessions.save();
1593
+ const rewindLines = [`\u2713 Rewound to message ${rewindN}`, ` Messages removed: ${rewindRemoved}`];
1594
+ if (rewindResult.restored > 0 || rewindResult.deleted > 0) {
1595
+ rewindLines.push(` Files restored: ${rewindResult.restored}, deleted: ${rewindResult.deleted}`);
1596
+ for (const f of rewindResult.files) rewindLines.push(` ${f}`);
1597
+ } else {
1598
+ rewindLines.push(" No file changes to revert.");
1599
+ }
1600
+ this.send({ type: "info", message: rewindLines.join("\n") });
1601
+ this.sendSessionMessages();
1602
+ this.sendStatus();
1603
+ break;
1604
+ }
1515
1605
  // ── /test ───────────────────────────────────────────────────────
1516
1606
  case "test": {
1517
1607
  this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
1518
1608
  try {
1519
- const { executeTests } = await import("./run-tests-7ZBI4ZTU.js");
1609
+ const { executeTests } = await import("./run-tests-L3JNRB6X.js");
1520
1610
  const argStr = args.join(" ").trim();
1521
1611
  let testArgs = {};
1522
1612
  if (argStr) {
@@ -2320,6 +2410,37 @@ ${diff}
2320
2410
  4. **Highlights** (if any)
2321
2411
 
2322
2412
  Severity: \u{1F534} Critical / \u{1F7E1} Warning / \u{1F535} Info`;
2413
+ }
2414
+ buildSecurityReviewPrompt(diff, gitContextStr) {
2415
+ return `# Security Vulnerability Review
2416
+
2417
+ Analyze the following code changes **exclusively for security vulnerabilities**.
2418
+
2419
+ ## Categories
2420
+ 1. Injection (SQL, command, path traversal, XSS)
2421
+ 2. Auth & Authorization (hardcoded creds, missing checks)
2422
+ 3. Secrets & Sensitive Data (API keys, tokens in code)
2423
+ 4. Input Validation (missing validation, unsafe deserialization)
2424
+ 5. Cryptography (weak algorithms, hardcoded IVs)
2425
+ 6. File System (path traversal, symlink attacks)
2426
+ 7. Network (SSRF, insecure protocols)
2427
+
2428
+ ## Git Status
2429
+ ${gitContextStr}
2430
+
2431
+ ## Code Changes
2432
+ \`\`\`diff
2433
+ ${diff}
2434
+ \`\`\`
2435
+
2436
+ ## Output Format
2437
+ For each finding:
2438
+ - **Severity**: \u{1F534} CRITICAL / \u{1F7E0} HIGH / \u{1F7E1} MEDIUM / \u{1F535} LOW
2439
+ - **Category** + **File:line**
2440
+ - **Description** + exploitation scenario
2441
+ - **Recommended fix**
2442
+
2443
+ If none found: "\u2705 No security vulnerabilities detected"`;
2323
2444
  }
2324
2445
  loadContextFiles() {
2325
2446
  const parts = [];
@@ -4,10 +4,11 @@ import {
4
4
  getDangerLevel,
5
5
  googleSearchContext,
6
6
  truncateOutput
7
- } from "./chunk-PDVX5QJA.js";
7
+ } from "./chunk-5GZQLJAY.js";
8
+ import "./chunk-4BKXL7SM.js";
8
9
  import {
9
10
  SUBAGENT_ALLOWED_TOOLS
10
- } from "./chunk-UA4BVWKV.js";
11
+ } from "./chunk-AHH5I2U6.js";
11
12
 
12
13
  // src/hub/task-orchestrator.ts
13
14
  import { createInterface } from "readline";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jinzd-ai-cli",
3
- "version": "0.4.24",
3
+ "version": "0.4.26",
4
4
  "description": "Cross-platform REPL-style AI CLI with multi-provider support",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",