jinzd-ai-cli 0.4.215 → 0.4.217

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.
Files changed (31) hide show
  1. package/README.md +5 -2
  2. package/dist/{batch-74H5SA7P.js → batch-YCOVKTXD.js} +2 -2
  3. package/dist/{chat-index-R2E27VXN.js → chat-index-PS274XM7.js} +1 -1
  4. package/dist/{chunk-ZOF5NFKW.js → chunk-524WZOKS.js} +1 -1
  5. package/dist/{ci-3ALK2XJN.js → chunk-KOPUCJXM.js} +40 -46
  6. package/dist/{chunk-6C3JYNM6.js → chunk-MGBMNCHG.js} +279 -54
  7. package/dist/{chunk-KL7UBVSQ.js → chunk-OUAZOE5U.js} +2 -2
  8. package/dist/{chunk-RS4WBI73.js → chunk-QAYOI57M.js} +3 -1
  9. package/dist/{chunk-X6OXS7KU.js → chunk-SNJAOXFT.js} +1 -1
  10. package/dist/{chunk-OQGVGPEK.js → chunk-VHY6NVMQ.js} +2 -0
  11. package/dist/{chunk-3TSHNZKI.js → chunk-VTH7BLXK.js} +1 -1
  12. package/dist/{chunk-2224JGA6.js → chunk-WKOQ5CYC.js} +1 -1
  13. package/dist/chunk-WZ3VKLF3.js +105 -0
  14. package/dist/{chunk-7JES2NWR.js → chunk-XJGEQIYS.js} +3 -1
  15. package/dist/ci-52RZIYWB.js +19 -0
  16. package/dist/ci-format-73UXKE65.js +18 -0
  17. package/dist/{constants-IN2HXJK7.js → constants-DIXAD35W.js} +3 -1
  18. package/dist/{doctor-cli-7XL4TCVT.js → doctor-cli-7GOQPULZ.js} +4 -4
  19. package/dist/electron-server.js +562 -318
  20. package/dist/{hub-VAOG5EY6.js → hub-GIGBITZN.js} +1 -1
  21. package/dist/index.js +169 -73
  22. package/dist/pr-KPQ5RPKB.js +267 -0
  23. package/dist/{run-tests-J2JQ57R3.js → run-tests-K7QR5QN4.js} +1 -1
  24. package/dist/{run-tests-QJD43AML.js → run-tests-ZDSA3QES.js} +2 -2
  25. package/dist/{server-WDLV3W4F.js → server-22YF3U34.js} +5 -4
  26. package/dist/{server-VGPZOISQ.js → server-QT3SC2KI.js} +129 -86
  27. package/dist/{task-orchestrator-QFOCO3N7.js → task-orchestrator-BHQQCVTY.js} +5 -4
  28. package/dist/{usage-IEB476NE.js → usage-WZZFSFLM.js} +2 -2
  29. package/dist/web/client/app.js +53 -2
  30. package/dist/web/client/index.html +11 -1
  31. package/package.json +1 -1
@@ -154,7 +154,7 @@ ${content}`);
154
154
  }
155
155
  }
156
156
  async function runTaskMode(config, providers, configManager, topic) {
157
- const { TaskOrchestrator } = await import("./task-orchestrator-QFOCO3N7.js");
157
+ const { TaskOrchestrator } = await import("./task-orchestrator-BHQQCVTY.js");
158
158
  const orchestrator = new TaskOrchestrator(config, providers, configManager);
159
159
  let interrupted = false;
160
160
  const onSigint = () => {
package/dist/index.js CHANGED
@@ -15,7 +15,7 @@ import {
15
15
  scanProject,
16
16
  sessionHasMeaningfulContent,
17
17
  setupProxy
18
- } from "./chunk-KL7UBVSQ.js";
18
+ } from "./chunk-OUAZOE5U.js";
19
19
  import {
20
20
  buildReviewPrompt,
21
21
  buildSecurityReviewPrompt
@@ -23,18 +23,27 @@ import {
23
23
  import {
24
24
  ToolExecutor,
25
25
  ToolRegistry,
26
+ addMemoryEntry,
26
27
  askUserContext,
27
28
  cleanupRejectedTeeFile,
28
29
  clearRecentlyDeniedAutoActions,
30
+ deleteMemoryEntry,
29
31
  estimateTokens,
32
+ expireMemoryEntry,
33
+ exportMemoryEntries,
34
+ formatMemoryForPrompt,
30
35
  formatPermissionProfileWarning,
31
36
  getPendingHookTrust,
32
37
  getRecentlyDeniedAutoActions,
33
38
  googleSearchContext,
34
39
  initTheme,
35
40
  isInterrupted,
41
+ isMemoryExpired,
36
42
  lastResponseStore,
37
43
  listHooks,
44
+ listMemoryEntries,
45
+ memoryMarkdownPath,
46
+ memoryStorePath,
38
47
  renderDiff,
39
48
  requestInterrupt,
40
49
  resetInterrupt,
@@ -46,11 +55,12 @@ import {
46
55
  theme,
47
56
  trustHook,
48
57
  undoStack,
49
- untrustHook
50
- } from "./chunk-6C3JYNM6.js";
58
+ untrustHook,
59
+ updateMemoryApproval
60
+ } from "./chunk-MGBMNCHG.js";
51
61
  import "./chunk-T2NL5ZIA.js";
52
62
  import "./chunk-BXP6YZ2P.js";
53
- import "./chunk-3TSHNZKI.js";
63
+ import "./chunk-VTH7BLXK.js";
54
64
  import {
55
65
  SessionManager,
56
66
  getContentText
@@ -67,7 +77,7 @@ import {
67
77
  getConfigDirUsage,
68
78
  listRecentCrashes,
69
79
  writeCrashLog
70
- } from "./chunk-ZOF5NFKW.js";
80
+ } from "./chunk-524WZOKS.js";
71
81
  import {
72
82
  ProviderRegistry
73
83
  } from "./chunk-QMXC327F.js";
@@ -76,7 +86,7 @@ import {
76
86
  getTopFailingTools,
77
87
  getTopUsedTools,
78
88
  installFlushOnExit
79
- } from "./chunk-X6OXS7KU.js";
89
+ } from "./chunk-SNJAOXFT.js";
80
90
  import {
81
91
  CONTENT_ONLY_STREAM_REMINDER,
82
92
  TEE_FINAL_USER_NUDGE,
@@ -98,7 +108,7 @@ import {
98
108
  } from "./chunk-XPBEJB27.js";
99
109
  import {
100
110
  ConfigManager
101
- } from "./chunk-2224JGA6.js";
111
+ } from "./chunk-WKOQ5CYC.js";
102
112
  import {
103
113
  AuthError,
104
114
  ProviderError,
@@ -117,14 +127,13 @@ import {
117
127
  DEV_STATE_FILE_NAME,
118
128
  MCP_PROJECT_CONFIG_NAME,
119
129
  MEMORY_FILE_NAME,
120
- MEMORY_MAX_CHARS,
121
130
  PLAN_MODE_READONLY_TOOLS,
122
131
  PLAN_MODE_SYSTEM_ADDON,
123
132
  REPO_URL,
124
133
  SKILLS_DIR_NAME,
125
134
  VERSION,
126
135
  buildUserIdentityPrompt
127
- } from "./chunk-7JES2NWR.js";
136
+ } from "./chunk-XJGEQIYS.js";
128
137
  import {
129
138
  formatGitContextForPrompt,
130
139
  getGitContext,
@@ -1661,7 +1670,7 @@ No tools match "${filter}".
1661
1670
  const { join: join5 } = await import("path");
1662
1671
  const { existsSync: existsSync5 } = await import("fs");
1663
1672
  const { getGitRoot: getGitRoot2 } = await import("./git-context-EXOEHQSF.js");
1664
- const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-IN2HXJK7.js");
1673
+ const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-DIXAD35W.js");
1665
1674
  const { approveProject, hashMcpFile } = await import("./project-trust-NKYHL3VZ.js");
1666
1675
  const cwd = process.cwd();
1667
1676
  const projectRoot = getGitRoot2(cwd) ?? cwd;
@@ -2720,7 +2729,7 @@ ${hint}` : "")
2720
2729
  usage: "/test [command|filter]",
2721
2730
  async execute(args, ctx) {
2722
2731
  try {
2723
- const { executeTests } = await import("./run-tests-QJD43AML.js");
2732
+ const { executeTests } = await import("./run-tests-ZDSA3QES.js");
2724
2733
  const argStr = args.join(" ").trim();
2725
2734
  let testArgs = {};
2726
2735
  if (argStr) {
@@ -2875,10 +2884,10 @@ ${hint}` : "")
2875
2884
  // ── /memory ────────────────────────────────────────────────────────────────
2876
2885
  {
2877
2886
  name: "memory",
2878
- description: "Persistent memory (memory.md) + chat memory recall index (v0.4.89+)",
2879
- usage: "/memory [show|add <text>|clear|path|rebuild|refresh|status|recall <query>|index-clear]",
2887
+ description: "Governed persistent memory + chat memory recall index",
2888
+ usage: "/memory [show|add|approve|reject|delete|expire|export|path|rebuild|refresh|status|recall|index-clear]",
2880
2889
  async execute(args, ctx) {
2881
- const memoryFile = join2(ctx.config.getConfigDir(), MEMORY_FILE_NAME);
2890
+ const configDir = ctx.config.getConfigDir();
2882
2891
  const sub = args[0] ?? "show";
2883
2892
  if (sub === "rebuild" || sub === "refresh") {
2884
2893
  const full = sub === "rebuild";
@@ -2912,6 +2921,13 @@ ${hint}` : "")
2912
2921
  }
2913
2922
  if (sub === "status") {
2914
2923
  const s = getChatIndexStatus();
2924
+ const memories = listMemoryEntries(configDir, { includeExpired: true, includeRejected: true });
2925
+ const active = memories.filter((m) => m.approved && !isMemoryExpired(m)).length;
2926
+ const pending = memories.filter((m) => !m.approved && !isMemoryExpired(m)).length;
2927
+ console.log(theme.heading("\n\u{1F9E0} Governed Memory"));
2928
+ console.log(` active : ${active}`);
2929
+ console.log(` pending : ${pending}`);
2930
+ console.log(` store : ${memoryStorePath(configDir)}`);
2915
2931
  console.log(theme.heading("\n\u{1F9E0} Chat Memory Index"));
2916
2932
  if (!s.exists) {
2917
2933
  console.log(theme.dim(" not built yet \u2014 run /memory rebuild\n"));
@@ -2958,60 +2974,116 @@ ${hint}` : "")
2958
2974
  }
2959
2975
  if (sub === "index-clear") {
2960
2976
  clearChatIndex();
2961
- ctx.renderer.printSuccess("Chat memory index cleared (memory.md untouched).");
2977
+ ctx.renderer.printSuccess("Chat memory index cleared (persistent memory untouched).");
2962
2978
  return;
2963
2979
  }
2964
- if (sub === "show" || sub === "view") {
2965
- if (!existsSync2(memoryFile)) {
2966
- ctx.renderer.printInfo("Memory is empty (memory.md not found)");
2967
- return;
2980
+ const parseFlags = (items) => {
2981
+ const flags = {};
2982
+ const text = [];
2983
+ for (let i = 0; i < items.length; i++) {
2984
+ const item = items[i];
2985
+ if (item.startsWith("--")) {
2986
+ const key = item.slice(2);
2987
+ const next = items[i + 1];
2988
+ if (next && !next.startsWith("--")) {
2989
+ flags[key] = next;
2990
+ i++;
2991
+ } else {
2992
+ flags[key] = true;
2993
+ }
2994
+ } else {
2995
+ text.push(item);
2996
+ }
2968
2997
  }
2969
- const content = readFileSync(memoryFile, "utf-8").trim();
2970
- if (!content) {
2998
+ return { flags, text: text.join(" ").trim() };
2999
+ };
3000
+ const asScope = (value) => value === "personal" || value === "project" || value === "session" || value === "team" ? value : void 0;
3001
+ const asSensitivity = (value) => value === "low" || value === "medium" || value === "high" ? value : void 0;
3002
+ if (sub === "show" || sub === "view" || sub === "list") {
3003
+ const entries = listMemoryEntries(configDir, { includeExpired: true, includeRejected: true });
3004
+ if (entries.length === 0) {
2971
3005
  ctx.renderer.printInfo("Memory is empty");
2972
3006
  return;
2973
3007
  }
2974
- const lines = content.split("\n").length;
2975
- console.log(theme.heading("\n\u{1F4DD} Persistent Memory:\n") + theme.dim("\u2500".repeat(60)));
2976
- console.log(chalk2.white(content));
2977
- console.log(theme.dim("\u2500".repeat(60)));
2978
- console.log(theme.dim(` ${content.length} chars \xB7 ${lines} lines
3008
+ console.log(theme.heading("\n\u{1F4DD} Governed Persistent Memory:\n") + theme.dim("\u2500".repeat(72)));
3009
+ for (const entry of entries) {
3010
+ const status = isMemoryExpired(entry) ? "expired" : entry.approved ? "approved" : "pending";
3011
+ const source = entry.sourceSession ? `session ${entry.sourceSession.slice(0, 8)}` : entry.sourceProject ? `project ${entry.sourceProject}` : "manual/tool";
3012
+ console.log(theme.accent(` ${entry.id.slice(0, 8)} ${status} scope:${entry.scope} sensitivity:${entry.sensitivity}`));
3013
+ console.log(theme.dim(` source: ${source} \xB7 created: ${entry.createdAt.replace("T", " ").slice(0, 19)}${entry.expiresAt ? ` \xB7 expires: ${entry.expiresAt}` : ""}`));
3014
+ console.log(" " + entry.content.replace(/\n/g, "\n "));
3015
+ console.log();
3016
+ }
3017
+ console.log(theme.dim("\u2500".repeat(72)));
3018
+ console.log(theme.dim(` store: ${memoryStorePath(configDir)}
2979
3019
  `));
2980
3020
  } else if (sub === "add") {
2981
- const text = args.slice(1).join(" ").trim();
2982
- if (!text) {
2983
- ctx.renderer.printInfo("Usage: /memory add <text>");
3021
+ const parsed = parseFlags(args.slice(1));
3022
+ if (!parsed.text) {
3023
+ ctx.renderer.printInfo("Usage: /memory add [--scope personal|project|session|team] [--sensitivity low|medium|high] [--expires-at ISO] <text>");
2984
3024
  return;
2985
3025
  }
2986
- const now = /* @__PURE__ */ new Date();
2987
- const dateStr = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}`;
2988
- const entry = `
2989
- ## ${dateStr}
2990
- ${text}
2991
- `;
2992
3026
  try {
2993
- mkdirSync2(dirname2(memoryFile), { recursive: true });
2994
- const previous = existsSync2(memoryFile) ? readFileSync(memoryFile, "utf-8") : "";
2995
- atomicWriteFileSync(memoryFile, previous + entry);
2996
- ctx.renderer.printSuccess(`Added to memory: "${text.slice(0, 60)}${text.length > 60 ? "\u2026" : ""}"`);
3027
+ const entry = addMemoryEntry(configDir, parsed.text, {
3028
+ scope: asScope(parsed.flags.scope) ?? "personal",
3029
+ sensitivity: asSensitivity(parsed.flags.sensitivity),
3030
+ expiresAt: typeof parsed.flags["expires-at"] === "string" ? parsed.flags["expires-at"] : void 0,
3031
+ approved: parsed.flags.pending === true ? false : void 0,
3032
+ cwd: process.cwd(),
3033
+ redact: true
3034
+ });
3035
+ const status = entry.approved ? "approved" : "pending approval";
3036
+ ctx.renderer.printSuccess(`Added memory ${entry.id.slice(0, 8)} (${status}, scope:${entry.scope}, sensitivity:${entry.sensitivity})`);
2997
3037
  } catch (err) {
2998
3038
  ctx.renderer.renderError(`Failed to write memory: ${err instanceof Error ? err.message : String(err)}`);
2999
3039
  }
3000
- } else if (sub === "clear") {
3001
- if (!existsSync2(memoryFile)) {
3002
- ctx.renderer.printInfo("Memory is already empty");
3040
+ } else if (sub === "approve" || sub === "reject") {
3041
+ const id = args[1];
3042
+ if (!id) {
3043
+ ctx.renderer.printInfo(`Usage: /memory ${sub} <id>`);
3003
3044
  return;
3004
3045
  }
3005
3046
  try {
3006
- atomicWriteFileSync(memoryFile, "");
3007
- ctx.renderer.printSuccess("Memory cleared (memory.md emptied)");
3047
+ const entry = updateMemoryApproval(configDir, id, sub === "approve");
3048
+ ctx.renderer.printSuccess(`${sub === "approve" ? "Approved" : "Rejected"} memory ${entry.id.slice(0, 8)}`);
3008
3049
  } catch (err) {
3009
- ctx.renderer.renderError(`Failed to clear memory: ${err instanceof Error ? err.message : String(err)}`);
3050
+ ctx.renderer.renderError(err instanceof Error ? err.message : String(err));
3051
+ }
3052
+ } else if (sub === "delete") {
3053
+ const id = args[1];
3054
+ if (!id) {
3055
+ ctx.renderer.printInfo("Usage: /memory delete <id>");
3056
+ return;
3010
3057
  }
3058
+ try {
3059
+ const entry = deleteMemoryEntry(configDir, id);
3060
+ ctx.renderer.printSuccess(`Deleted memory ${entry.id.slice(0, 8)}`);
3061
+ } catch (err) {
3062
+ ctx.renderer.renderError(err instanceof Error ? err.message : String(err));
3063
+ }
3064
+ } else if (sub === "expire") {
3065
+ const id = args[1];
3066
+ if (!id) {
3067
+ ctx.renderer.printInfo("Usage: /memory expire <id> [ISO timestamp]");
3068
+ return;
3069
+ }
3070
+ try {
3071
+ const entry = expireMemoryEntry(configDir, id, args[2]);
3072
+ ctx.renderer.printSuccess(`Expired memory ${entry.id.slice(0, 8)}`);
3073
+ } catch (err) {
3074
+ ctx.renderer.renderError(err instanceof Error ? err.message : String(err));
3075
+ }
3076
+ } else if (sub === "export") {
3077
+ console.log(exportMemoryEntries(configDir, process.cwd()));
3078
+ } else if (sub === "clear") {
3079
+ atomicWriteFileSync(memoryStorePath(configDir), "");
3080
+ atomicWriteFileSync(memoryMarkdownPath(configDir), "");
3081
+ ctx.renderer.printSuccess("Memory cleared (memory.jsonl and memory.md emptied)");
3011
3082
  } else if (sub === "path") {
3012
- console.log(theme.accent(memoryFile));
3083
+ console.log(theme.accent(memoryStorePath(configDir)));
3084
+ console.log(theme.dim(memoryMarkdownPath(configDir)));
3013
3085
  } else {
3014
- ctx.renderer.printInfo("Usage: /memory [show|add <text>|clear|path]");
3086
+ ctx.renderer.printInfo("Usage: /memory [show|add|approve|reject|delete|expire|export|path|rebuild|refresh|status|recall|index-clear]");
3015
3087
  }
3016
3088
  }
3017
3089
  },
@@ -4945,19 +5017,7 @@ ${content}
4945
5017
  return { layers: result.layers, mergedContent: result.mergedContent };
4946
5018
  }
4947
5019
  loadMemoryContent() {
4948
- const memoryPath = join4(this.config.getConfigDir(), MEMORY_FILE_NAME);
4949
- if (!existsSync4(memoryPath)) return null;
4950
- let content = readFileSync3(memoryPath, "utf-8").trim();
4951
- if (!content) return null;
4952
- if (content.length > MEMORY_MAX_CHARS) {
4953
- content = content.slice(-MEMORY_MAX_CHARS);
4954
- const firstEntry = content.indexOf("\n## ");
4955
- if (firstEntry !== -1) {
4956
- content = content.slice(firstEntry + 1);
4957
- }
4958
- }
4959
- const entryCount = (content.match(/^## \d{4}-\d{2}-\d{2}/gm) ?? []).length;
4960
- return { content, entryCount };
5020
+ return formatMemoryForPrompt(this.config.getConfigDir(), process.cwd());
4961
5021
  }
4962
5022
  /**
4963
5023
  * 将项目上下文内容合并到 system prompt。
@@ -7207,7 +7267,7 @@ program.command("web").description("Start Web UI server with browser-based chat
7207
7267
  console.error("Error: Invalid port number. Must be between 1 and 65535.");
7208
7268
  process.exit(1);
7209
7269
  }
7210
- const { startWebServer } = await import("./server-VGPZOISQ.js");
7270
+ const { startWebServer } = await import("./server-QT3SC2KI.js");
7211
7271
  await startWebServer({ port, host: options.host });
7212
7272
  });
7213
7273
  program.command("user [action] [username]").description("Manage Web UI users (list | create <name> | delete <name> | reset-password <name> | logout-all <name> | migrate <name>)").action(async (action, username) => {
@@ -7374,16 +7434,16 @@ program.command("sessions").description("List recent conversation sessions").opt
7374
7434
  console.log(footer + "\n");
7375
7435
  });
7376
7436
  program.command("usage").description("Show token + cost usage grouped by provider/model (cross-session)").option("--days <n>", "Only the last N days (inclusive of today)").option("--month <ym>", "Only a specific month, format YYYY-MM (e.g. 2026-06)").option("--json", "Output as JSON (for scripting)").action(async (options) => {
7377
- const { runUsageCli } = await import("./usage-IEB476NE.js");
7437
+ const { runUsageCli } = await import("./usage-WZZFSFLM.js");
7378
7438
  await runUsageCli(options);
7379
7439
  });
7380
7440
  program.command("doctor").description("Health check: API keys, config, MCP, recent crashes, tool usage, disk usage").option("--json", "Output as JSON (for scripting)").option("--reset-stats", "Reset accumulated tool usage statistics").action(async (options) => {
7381
- const { runDoctorCli } = await import("./doctor-cli-7XL4TCVT.js");
7441
+ const { runDoctorCli } = await import("./doctor-cli-7GOQPULZ.js");
7382
7442
  await runDoctorCli({ json: !!options.json, resetStats: !!options.resetStats });
7383
7443
  });
7384
7444
  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) => {
7385
7445
  try {
7386
- const batch = await import("./batch-74H5SA7P.js");
7446
+ const batch = await import("./batch-YCOVKTXD.js");
7387
7447
  switch (action) {
7388
7448
  case "submit":
7389
7449
  if (!arg) {
@@ -7426,7 +7486,7 @@ program.command("batch <action> [arg] [arg2]").description("Anthropic Message Ba
7426
7486
  }
7427
7487
  });
7428
7488
  program.command("mcp-serve").description("Start an MCP server over STDIO, exposing aicli's built-in tools to Claude Desktop / Cursor / other MCP clients").option("--allow-destructive", "Allow bash / run_interactive / task_create (always destructive in MCP mode)").option("--allow-outside-cwd", "Allow tool path arguments to escape the sandbox root \u2014 disabled by default").option("--tools <list>", "Comma-separated whitelist of tools to expose (default: all eligible tools)").option("--cwd <path>", "Working directory AND sandbox root (default: current directory)").action(async (options) => {
7429
- const { startMcpServer } = await import("./server-WDLV3W4F.js");
7489
+ const { startMcpServer } = await import("./server-22YF3U34.js");
7430
7490
  await startMcpServer({
7431
7491
  allowDestructive: !!options.allowDestructive,
7432
7492
  allowOutsideCwd: !!options.allowOutsideCwd,
@@ -7434,8 +7494,39 @@ program.command("mcp-serve").description("Start an MCP server over STDIO, exposi
7434
7494
  cwd: options.cwd
7435
7495
  });
7436
7496
  });
7437
- program.command("ci").description("Headless PR review (code + security) \u2014 reads git/gh diff, optionally posts to PR. Designed for GitHub Actions.").option("--pr <num>", "PR number; diff fetched via `gh pr diff <num>`", (v) => parseInt(v, 10)).option("--base <ref>", "Base ref for `git diff <ref>...HEAD` (ignored when --pr set)").option("--post", "Post review as a PR comment (requires gh CLI + GH_TOKEN, needs --pr)").option("--no-update", "Always create a new comment instead of updating the previous aicli review").option("--skip-code", "Skip the code review section").option("--skip-security", "Skip the security review section").option("--detailed", "Use the detailed code-review prompt").option("--max-diff <n>", "Max diff chars sent to the model (default 30000)", (v) => parseInt(v, 10)).option("--provider <id>", "Override provider (default: config.defaultProvider)").option("--model <id>", "Override model").option("--dry-run", "Print result to stdout instead of posting (overrides --post)").action(async (options) => {
7438
- const { runCi } = await import("./ci-3ALK2XJN.js");
7497
+ program.command("pr <action> [target]").description("PR review helpers: review | security-review | summarize. Defaults to git diff main...HEAD.").option("--base <ref>", "Base ref for git diff <ref>...HEAD (ignored when --pr set)").option("--head <ref>", "Head ref for local git diff (default: HEAD)").option("--post", "Post/update a PR comment; only when target is a GitHub PR number or URL").option("--no-update", "Always create a new PR comment instead of updating the previous aicli review").option("--detailed", "Use the detailed code-review prompt").option("--agents <list>", "Comma-separated review lenses: security,bugs,tests,maintainability").option("--max-diff <n>", "Max diff chars sent to the model (default 30000)", (v) => parseInt(v, 10)).option("--provider <id>", "Override provider (default: config.defaultProvider)").option("--model <id>", "Override model").option("--dry-run", "Print result to stdout instead of posting (overrides --post)").action(async (action, target, options) => {
7498
+ if (!["review", "security-review", "summarize"].includes(action)) {
7499
+ console.error(`Unknown pr action: ${action}. Use review | security-review | summarize.`);
7500
+ process.exit(1);
7501
+ }
7502
+ const { runPrCli } = await import("./pr-KPQ5RPKB.js");
7503
+ const result = await runPrCli({
7504
+ action,
7505
+ target,
7506
+ base: options.base,
7507
+ head: options.head,
7508
+ post: !!options.post,
7509
+ update: options.update !== false,
7510
+ detailed: !!options.detailed,
7511
+ agents: options.agents,
7512
+ maxDiffChars: options.maxDiff,
7513
+ provider: options.provider,
7514
+ model: options.model,
7515
+ dryRun: !!options.dryRun
7516
+ });
7517
+ process.stdout.write(result.markdown + "\n");
7518
+ if (result.posted) process.stderr.write("\n\u2705 Posted PR review comment\n");
7519
+ process.exit(result.exitCode);
7520
+ });
7521
+ program.command("ci").description("Headless PR review (code + security) \u2014 reads git/gh diff, optionally posts to PR. Designed for GitHub Actions.").option("--pr <num>", "PR number or URL; diff fetched via gh pr diff <value>", (v) => /^#?\d+$/.test(v) ? parseInt(v.replace(/^#/, ""), 10) : v).option("--base <ref>", "Base ref for git diff <ref>...HEAD (ignored when --pr set)").option("--post", "Post review as a PR comment (requires gh CLI + GH_TOKEN, needs --pr)").option("--no-update", "Always create a new comment instead of updating the previous aicli review").option("--skip-code", "Skip the code review section").option("--skip-security", "Skip the security review section").option("--detailed", "Use the detailed code-review prompt").option("--max-diff <n>", "Max diff chars sent to the model (default 30000)", (v) => parseInt(v, 10)).option("--provider <id>", "Override provider (default: config.defaultProvider)").option("--model <id>", "Override model").option("--dry-run", "Print result to stdout instead of posting (overrides --post)").option("--json", "Output a structured JSON report").option("--markdown", "Output Markdown report (default)").option("--sarif", "Output SARIF 2.1.0 for code scanning artifacts").option("--fail-on-security-high", "Fail when the security review contains HIGH/CRITICAL findings (default)").option("--no-fail-on-security-high", "Do not fail on security HIGH/CRITICAL findings").option("--fail-on-test-failure", "Fail when the report mentions test failures (default)").option("--no-fail-on-test-failure", "Do not fail on test failure findings").option("--fail-on-lint-failure", "Fail when the report mentions lint/typecheck failures (default)").option("--no-fail-on-lint-failure", "Do not fail on lint/typecheck failure findings").action(async (options) => {
7522
+ const { runCi } = await import("./ci-52RZIYWB.js");
7523
+ const { formatCiResult } = await import("./ci-format-73UXKE65.js");
7524
+ const formats = [options.json, options.markdown, options.sarif].filter(Boolean).length;
7525
+ if (formats > 1) {
7526
+ console.error("Choose only one output format: --json, --markdown, or --sarif.");
7527
+ process.exit(2);
7528
+ }
7529
+ const outputFormat = options.json ? "json" : options.sarif ? "sarif" : "markdown";
7439
7530
  const result = await runCi({
7440
7531
  pr: options.pr,
7441
7532
  base: options.base,
@@ -7447,13 +7538,16 @@ program.command("ci").description("Headless PR review (code + security) \u2014 r
7447
7538
  maxDiffChars: options.maxDiff,
7448
7539
  provider: options.provider,
7449
7540
  model: options.model,
7450
- dryRun: !!options.dryRun
7541
+ dryRun: !!options.dryRun,
7542
+ thresholds: {
7543
+ securityHigh: options.failOnSecurityHigh !== false,
7544
+ testFailure: options.failOnTestFailure !== false,
7545
+ lintFailure: options.failOnLintFailure !== false
7546
+ }
7451
7547
  });
7452
- process.stdout.write(result.markdown + "\n");
7548
+ process.stdout.write(formatCiResult({ version: VERSION, ...result }, outputFormat) + "\n");
7453
7549
  if (result.posted) {
7454
- process.stderr.write(`
7455
- \u2705 Posted review to PR #${options.pr}${result.updatedCommentId ? " (updated existing comment)" : ""}
7456
- `);
7550
+ process.stderr.write("\n\u2705 Posted review to PR #" + options.pr + (result.updatedCommentId ? " (updated existing comment)" : "") + "\n");
7457
7551
  }
7458
7552
  process.exit(result.exitCode);
7459
7553
  });
@@ -7484,6 +7578,8 @@ program.command("help").description("Show a comprehensive guide to all aicli fea
7484
7578
  ` ${G}aicli usage${R} Token + cost usage by provider/model (--days/--month/--json)`,
7485
7579
  ` ${G}aicli user <action>${R} User management (list/create/delete)`,
7486
7580
  ` ${G}aicli batch <action>${R} Anthropic Batches API (50% off, 24h): submit/list/status/results/cancel`,
7581
+ ` ${G}aicli pr <action>${R} Local PR review/summarize commands`,
7582
+ ` ${G}aicli ci [options]${R} Headless PR review for GitHub Actions`,
7487
7583
  ` ${G}aicli mcp-serve${R} MCP server over STDIO (expose tools to Claude Desktop / Cursor)`,
7488
7584
  "",
7489
7585
  `${B}${C} \u25A0 STARTUP OPTIONS${R}`,
@@ -7581,7 +7677,7 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
7581
7677
  }),
7582
7678
  config.get("customProviders")
7583
7679
  );
7584
- const { startHub } = await import("./hub-VAOG5EY6.js");
7680
+ const { startHub } = await import("./hub-GIGBITZN.js");
7585
7681
  await startHub(
7586
7682
  {
7587
7683
  topic: topic ?? "",