jinzd-ai-cli 0.4.183 → 0.4.185

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.
@@ -36,7 +36,7 @@ import {
36
36
  VERSION,
37
37
  buildUserIdentityPrompt,
38
38
  runTestsTool
39
- } from "./chunk-JWT54N7I.js";
39
+ } from "./chunk-A5VODFAK.js";
40
40
  import {
41
41
  hasSemanticIndex,
42
42
  semanticSearch
@@ -11003,6 +11003,8 @@ function loadMemoryContent(configDir) {
11003
11003
  // src/core/pricing.ts
11004
11004
  var PRICING_TABLE = {
11005
11005
  // ── Anthropic Claude ──────────────────────────────────────────
11006
+ "claude-opus-4-8": { input: 15, output: 75, cacheWrite: 18.75, cacheRead: 1.5 },
11007
+ "claude-opus-4-7": { input: 15, output: 75, cacheWrite: 18.75, cacheRead: 1.5 },
11006
11008
  "claude-opus-4-6": { input: 15, output: 75, cacheWrite: 18.75, cacheRead: 1.5 },
11007
11009
  "claude-opus-4-5": { input: 15, output: 75, cacheWrite: 18.75, cacheRead: 1.5 },
11008
11010
  "claude-sonnet-4-6": { input: 3, output: 15, cacheWrite: 3.75, cacheRead: 0.3 },
@@ -11014,6 +11016,10 @@ var PRICING_TABLE = {
11014
11016
  "claude-3-5-haiku": { input: 0.8, output: 4, cacheWrite: 1, cacheRead: 0.08 },
11015
11017
  "claude-3-opus": { input: 15, output: 75, cacheWrite: 18.75, cacheRead: 1.5 },
11016
11018
  // ── OpenAI ────────────────────────────────────────────────────
11019
+ // GAP (v0.4.184): the GPT-5 family (gpt-5, gpt-5-mini/nano, gpt-5.4, gpt-5.4-pro)
11020
+ // and o4-mini are in the provider's model list but have NO verified pricing yet —
11021
+ // left unpriced on purpose so `aicli usage` shows "—" (unknown) rather than a
11022
+ // deceptive $0.00. Add real rates when confirmed from the official pricing page.
11017
11023
  "gpt-4o": { input: 2.5, output: 10, cacheRead: 1.25 },
11018
11024
  "gpt-4o-mini": { input: 0.15, output: 0.6, cacheRead: 0.075 },
11019
11025
  "gpt-4-turbo": { input: 10, output: 30 },
@@ -11059,10 +11065,15 @@ var PRICING_TABLE = {
11059
11065
  "glm-z1": { input: 0.5, output: 1.5 },
11060
11066
  "glm-z1-air": { input: 0.2, output: 0.6 },
11061
11067
  "glm-z1-flash": { input: 0, output: 0 }
11068
+ // ── MiniMax (海螺) ────────────────────────────────────────────
11069
+ // GAP (v0.4.184): MiniMax-M3 / M2.x are unpriced — no verified rate anchor yet.
11070
+ // Unpriced → `aicli usage` shows "—" (unknown), not a false $0.00. Add when confirmed.
11062
11071
  // ── OpenRouter (pass-through — actual cost depends on underlying model) ──
11063
- // Left empty; callers should resolve via underlying model ID.
11072
+ // Left empty by design: model IDs are "vendor/model" (e.g. anthropic/claude-sonnet-4)
11073
+ // and the real price is set by the underlying model + OpenRouter markup, not statically
11074
+ // resolvable here. Shows "—" (unknown) in `aicli usage`.
11064
11075
  // ── Ollama (local, zero cost) ─────────────────────────────────
11065
- // Handled via provider check below.
11076
+ // Handled via provider check below (FREE_PROVIDERS → genuine $0.00, not "unknown").
11066
11077
  };
11067
11078
  var FREE_PROVIDERS = /* @__PURE__ */ new Set(["ollama"]);
11068
11079
  function getPricing(provider, model) {
@@ -13850,7 +13861,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
13850
13861
  case "test": {
13851
13862
  this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
13852
13863
  try {
13853
- const { executeTests } = await import("./run-tests-UM2YUDTP.js");
13864
+ const { executeTests } = await import("./run-tests-YOP7AKRA.js");
13854
13865
  const argStr = args.join(" ").trim();
13855
13866
  let testArgs = {};
13856
13867
  if (argStr) {
@@ -155,7 +155,7 @@ ${content}`);
155
155
  }
156
156
  }
157
157
  async function runTaskMode(config, providers, configManager, topic) {
158
- const { TaskOrchestrator } = await import("./task-orchestrator-2S5WM7GH.js");
158
+ const { TaskOrchestrator } = await import("./task-orchestrator-2FOUTUSP.js");
159
159
  const orchestrator = new TaskOrchestrator(config, providers, configManager);
160
160
  let interrupted = false;
161
161
  const onSigint = () => {
package/dist/index.js CHANGED
@@ -9,16 +9,13 @@ import {
9
9
  SkillManager,
10
10
  autoTrimSessionIfNeeded,
11
11
  clearDevState,
12
- computeCost,
13
- formatCost,
14
- getPricing,
15
12
  loadDevState,
16
13
  parseSimpleYaml,
17
14
  persistToolRound,
18
15
  saveDevState,
19
16
  sessionHasMeaningfulContent,
20
17
  setupProxy
21
- } from "./chunk-UCPHXAHM.js";
18
+ } from "./chunk-U7P5A3MJ.js";
22
19
  import {
23
20
  ToolExecutor,
24
21
  ToolRegistry,
@@ -37,19 +34,27 @@ import {
37
34
  spawnAgentContext,
38
35
  theme,
39
36
  undoStack
40
- } from "./chunk-G44XZPNZ.js";
37
+ } from "./chunk-GOS4DWW5.js";
41
38
  import "./chunk-HDSKW7Q3.js";
42
39
  import "./chunk-ZWVIDFGY.js";
43
- import "./chunk-2AZX76LV.js";
40
+ import "./chunk-GSRXKHZ7.js";
44
41
  import {
45
42
  SessionManager,
46
43
  getContentText
47
44
  } from "./chunk-RIVZNS3K.js";
45
+ import {
46
+ CostTracker
47
+ } from "./chunk-A3I5WP5L.js";
48
+ import {
49
+ computeCost,
50
+ formatCost,
51
+ getPricing
52
+ } from "./chunk-V37XOYOE.js";
48
53
  import {
49
54
  getConfigDirUsage,
50
55
  listRecentCrashes,
51
56
  writeCrashLog
52
- } from "./chunk-M5JHEB35.js";
57
+ } from "./chunk-ZO4LKUDM.js";
53
58
  import {
54
59
  BudgetWarner,
55
60
  CONTENT_ONLY_STREAM_REMINDER,
@@ -85,11 +90,11 @@ import {
85
90
  getTopFailingTools,
86
91
  getTopUsedTools,
87
92
  installFlushOnExit
88
- } from "./chunk-ETGCCZBT.js";
93
+ } from "./chunk-GH32XE5K.js";
89
94
  import "./chunk-HIU2SH4V.js";
90
95
  import {
91
96
  ConfigManager
92
- } from "./chunk-3GZ7RYAP.js";
97
+ } from "./chunk-FHZ2LKM5.js";
93
98
  import {
94
99
  AuthError,
95
100
  ProviderError,
@@ -116,7 +121,7 @@ import {
116
121
  SKILLS_DIR_NAME,
117
122
  VERSION,
118
123
  buildUserIdentityPrompt
119
- } from "./chunk-A56C3L4P.js";
124
+ } from "./chunk-PMZCQAJL.js";
120
125
  import {
121
126
  formatGitContextForPrompt,
122
127
  getGitContext,
@@ -145,8 +150,8 @@ import { program } from "commander";
145
150
 
146
151
  // src/repl/repl.ts
147
152
  import * as readline from "readline";
148
- import { existsSync as existsSync5, readFileSync as readFileSync4, readdirSync as readdirSync3, statSync as statSync3, unlinkSync as unlinkSync2, writeFileSync as writeFileSync3 } from "fs";
149
- import { join as join5, resolve as resolve2, extname as extname2, dirname as dirname3, basename as basename2 } from "path";
153
+ import { existsSync as existsSync4, readFileSync as readFileSync3, readdirSync as readdirSync3, statSync as statSync3, unlinkSync as unlinkSync2, writeFileSync as writeFileSync2 } from "fs";
154
+ import { join as join4, resolve as resolve2, extname as extname2, dirname as dirname3, basename as basename2 } from "path";
150
155
  import chalk4 from "chalk";
151
156
 
152
157
  // src/session/title-generator.ts
@@ -278,7 +283,7 @@ var ABOUT_FEATURES = [
278
283
  "Session tooling: /checkpoint, /fork, /branch (diff/cherry-pick), /rewind, /session show|page|remove",
279
284
  "Session Replay \u{1F3AC} + Web UI: timeline of every message, tool call, reasoning, token usage",
280
285
  "Cross-session search (/search) + sensitive-data redaction before anything hits disk",
281
- "Cost dashboard: /cost history \u2014 daily/weekly/monthly spend with budget progress",
286
+ "Cost dashboard: /cost (session) + aicli usage (cross-session by provider/model) + monthly budget",
282
287
  "File operation undo (/undo) for write_file / edit_file / bash-created files",
283
288
  "Multi-Agent Hub: aicli hub \u2014 mixed-model debate, --steer, --vote, Web UI room",
284
289
  "aicli ci: headless PR review for GitHub Actions (posts/updates one comment, CI gate)",
@@ -1828,16 +1833,16 @@ No tools match "${filter}".
1828
1833
  usage: "/mcp [reconnect [serverId] | trust-project]",
1829
1834
  async execute(args, ctx) {
1830
1835
  if (args[0] === "trust-project") {
1831
- const { join: join6 } = await import("path");
1832
- const { existsSync: existsSync6 } = await import("fs");
1836
+ const { join: join5 } = await import("path");
1837
+ const { existsSync: existsSync5 } = await import("fs");
1833
1838
  const { getGitRoot: getGitRoot2 } = await import("./git-context-7KIP4X2V.js");
1834
- const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-EQ7ULMVF.js");
1839
+ const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-RJDN7GOH.js");
1835
1840
  const { approveProject, hashMcpFile } = await import("./project-trust-IFM7FXEV.js");
1836
1841
  const cwd = process.cwd();
1837
1842
  const projectRoot = getGitRoot2(cwd) ?? cwd;
1838
- const mcpPath = join6(projectRoot, MCP_PROJECT_CONFIG_NAME2);
1843
+ const mcpPath = join5(projectRoot, MCP_PROJECT_CONFIG_NAME2);
1839
1844
  console.log();
1840
- if (!existsSync6(mcpPath)) {
1845
+ if (!existsSync5(mcpPath)) {
1841
1846
  console.log(theme.dim(` No .mcp.json in ${projectRoot}.`));
1842
1847
  console.log();
1843
1848
  return;
@@ -2892,7 +2897,7 @@ ${hint}` : "")
2892
2897
  usage: "/test [command|filter]",
2893
2898
  async execute(args, ctx) {
2894
2899
  try {
2895
- const { executeTests } = await import("./run-tests-3FJMYYIU.js");
2900
+ const { executeTests } = await import("./run-tests-37RHYYD4.js");
2896
2901
  const argStr = args.join(" ").trim();
2897
2902
  let testArgs = {};
2898
2903
  if (argStr) {
@@ -3510,12 +3515,12 @@ Summary: ${fileMap.size} file(s) \u2014 ${newFiles} new, ${modifiedFiles} modifi
3510
3515
  if (scanAll) {
3511
3516
  const metas = ctx.sessions.listSessions();
3512
3517
  console.log(theme.info(` Scanning ${metas.length} session(s)\u2026`));
3513
- const { readFileSync: readFileSync5 } = await import("fs");
3514
- const { join: join6 } = await import("path");
3518
+ const { readFileSync: readFileSync4 } = await import("fs");
3519
+ const { join: join5 } = await import("path");
3515
3520
  const historyDir = ctx.config.getHistoryDir();
3516
3521
  for (const m of metas) {
3517
3522
  try {
3518
- const content = readFileSync5(join6(historyDir, `${m.id}.json`), "utf-8");
3523
+ const content = readFileSync4(join5(historyDir, `${m.id}.json`), "utf-8");
3519
3524
  const hits2 = scanString(content, opts);
3520
3525
  if (hits2.length) {
3521
3526
  filesWithHits++;
@@ -4278,142 +4283,6 @@ var CustomCommandManager = class {
4278
4283
  }
4279
4284
  };
4280
4285
 
4281
- // src/core/cost-tracker.ts
4282
- import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2, renameSync } from "fs";
4283
- import { join as join4 } from "path";
4284
- var CostTracker = class {
4285
- filePath;
4286
- records = [];
4287
- dirty = false;
4288
- constructor(configDir) {
4289
- this.filePath = join4(configDir, "cost-history.json");
4290
- this.load();
4291
- }
4292
- load() {
4293
- try {
4294
- if (existsSync4(this.filePath)) {
4295
- const data = JSON.parse(readFileSync3(this.filePath, "utf-8"));
4296
- if (data.version === 1 && Array.isArray(data.records)) {
4297
- this.records = [...data.records].sort((a, b) => a.date.localeCompare(b.date));
4298
- }
4299
- }
4300
- } catch {
4301
- this.records = [];
4302
- }
4303
- }
4304
- /**
4305
- * Save to disk (atomic write).
4306
- *
4307
- * H2: Snapshot records before writing and only clear the dirty flag after
4308
- * the write succeeds. If writeFileSync/renameSync throws, dirty remains
4309
- * true so the next save() retries — prevents silent data loss on transient
4310
- * disk errors. writeFileSync is sync in Node.js so there's no interleaving
4311
- * risk within a single save() call; the snapshot mainly protects against
4312
- * future refactors to async I/O.
4313
- */
4314
- save() {
4315
- if (!this.dirty) return;
4316
- const snapshot = {
4317
- version: 1,
4318
- records: [...this.records]
4319
- // shallow copy — records are plain data
4320
- };
4321
- try {
4322
- const tmp = this.filePath + ".tmp";
4323
- writeFileSync2(tmp, JSON.stringify(snapshot, null, 2), "utf-8");
4324
- renameSync(tmp, this.filePath);
4325
- this.dirty = false;
4326
- } catch (err) {
4327
- console.error("[cost-tracker] Failed to persist cost history:", err);
4328
- }
4329
- }
4330
- /**
4331
- * Record cost from a completed session/interaction.
4332
- */
4333
- addCost(provider, model, usage) {
4334
- const cost = computeCost(provider, model, usage);
4335
- if (cost === null || cost === 0) return;
4336
- const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
4337
- let record = this.records.find((r) => r.date === today);
4338
- if (!record) {
4339
- record = { date: today, cost: 0, sessions: 0, inputTokens: 0, outputTokens: 0 };
4340
- this.records.push(record);
4341
- }
4342
- record.cost += cost;
4343
- record.sessions += 1;
4344
- record.inputTokens += usage.inputTokens;
4345
- record.outputTokens += usage.outputTokens;
4346
- this.dirty = true;
4347
- if (this.records.length > 90) {
4348
- this.records = this.records.slice(-90);
4349
- }
4350
- }
4351
- /** Get total cost for a given month ("2026-04"). */
4352
- getMonthlyCost(yearMonth) {
4353
- const prefix = yearMonth ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 7);
4354
- return this.records.filter((r) => r.date.startsWith(prefix)).reduce((sum, r) => sum + r.cost, 0);
4355
- }
4356
- /** Get today's cost. */
4357
- getTodayCost() {
4358
- const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
4359
- return this.records.find((r) => r.date === today)?.cost ?? 0;
4360
- }
4361
- /** Get total cost for last N days. */
4362
- getRecentCost(days) {
4363
- const cutoff = /* @__PURE__ */ new Date();
4364
- cutoff.setDate(cutoff.getDate() - days);
4365
- const cutoffStr = cutoff.toISOString().slice(0, 10);
4366
- return this.records.filter((r) => r.date >= cutoffStr).reduce((sum, r) => sum + r.cost, 0);
4367
- }
4368
- /** Get all records for display. */
4369
- getRecords() {
4370
- return [...this.records];
4371
- }
4372
- /**
4373
- * Check if monthly cost exceeds budget, return warning message or null.
4374
- */
4375
- checkBudget(monthlyBudget) {
4376
- if (!monthlyBudget || monthlyBudget <= 0) return null;
4377
- const monthlyCost = this.getMonthlyCost();
4378
- const ratio = monthlyCost / monthlyBudget;
4379
- if (ratio >= 1) {
4380
- return `\u{1F6A8} Monthly budget exceeded: ${formatCost(monthlyCost)} / ${formatCost(monthlyBudget)} (${Math.round(ratio * 100)}%)`;
4381
- }
4382
- if (ratio >= 0.8) {
4383
- return `\u26A0 Monthly budget warning: ${formatCost(monthlyCost)} / ${formatCost(monthlyBudget)} (${Math.round(ratio * 100)}%)`;
4384
- }
4385
- return null;
4386
- }
4387
- /**
4388
- * Format a cost summary for display.
4389
- */
4390
- formatSummary(monthlyBudget) {
4391
- const today = this.getTodayCost();
4392
- const monthly = this.getMonthlyCost();
4393
- const yearMonth = (/* @__PURE__ */ new Date()).toISOString().slice(0, 7);
4394
- const lines = [
4395
- `Today: ${formatCost(today)}`,
4396
- `This month: ${formatCost(monthly)} (${yearMonth})`
4397
- ];
4398
- if (monthlyBudget && monthlyBudget > 0) {
4399
- const ratio = monthly / monthlyBudget;
4400
- const bar = "\u2588".repeat(Math.min(20, Math.round(ratio * 20))) + "\u2591".repeat(Math.max(0, 20 - Math.round(ratio * 20)));
4401
- lines.push(`Budget: ${formatCost(monthlyBudget)} [${bar}] ${Math.round(ratio * 100)}%`);
4402
- }
4403
- const last7 = [];
4404
- for (let i = 6; i >= 0; i--) {
4405
- const d = /* @__PURE__ */ new Date();
4406
- d.setDate(d.getDate() - i);
4407
- const dateStr = d.toISOString().slice(0, 10);
4408
- const record = this.records.find((r) => r.date === dateStr);
4409
- const dayLabel = dateStr.slice(5);
4410
- last7.push(`${dayLabel}: ${record ? formatCost(record.cost) : "$0.00"}`);
4411
- }
4412
- lines.push(`Last 7 days: ${last7.join(" ")}`);
4413
- return lines.join("\n");
4414
- }
4415
- };
4416
-
4417
4286
  // src/core/model-router.ts
4418
4287
  var TAG_REGEX = /(?:^|\s)#([a-zA-Z][\w-]{0,31})\b/g;
4419
4288
  function extractTags(message) {
@@ -4703,7 +4572,7 @@ function parseAtReferences(input2, cwd) {
4703
4572
  const absPath = resolve2(cwd, rawPath);
4704
4573
  const ext = extname2(rawPath).toLowerCase();
4705
4574
  const mime = IMAGE_MIME[ext];
4706
- if (!existsSync5(absPath)) {
4575
+ if (!existsSync4(absPath)) {
4707
4576
  refs.push({ path: rawPath, type: "notfound" });
4708
4577
  continue;
4709
4578
  }
@@ -4713,7 +4582,7 @@ function parseAtReferences(input2, cwd) {
4713
4582
  refs.push({ path: rawPath, type: "toolarge" });
4714
4583
  continue;
4715
4584
  }
4716
- const data = readFileSync4(absPath).toString("base64");
4585
+ const data = readFileSync3(absPath).toString("base64");
4717
4586
  imageParts.push({
4718
4587
  type: "image_url",
4719
4588
  image_url: { url: `data:${mime};base64,${data}` }
@@ -4721,7 +4590,7 @@ function parseAtReferences(input2, cwd) {
4721
4590
  refs.push({ path: rawPath, type: "image" });
4722
4591
  textBody = textBody.replace(match[0], "").trim();
4723
4592
  } else {
4724
- const content = readFileSync4(absPath, "utf-8");
4593
+ const content = readFileSync3(absPath, "utf-8");
4725
4594
  const inlined = `
4726
4595
 
4727
4596
  [File: ${rawPath}]
@@ -4946,7 +4815,7 @@ var Repl = class {
4946
4815
  const filtered = entries.filter((e) => !SKIP_DIRS_SET.has(e));
4947
4816
  for (let i = 0; i < filtered.length && entryCount < MAX_TREE_ENTRIES; i++) {
4948
4817
  const name = filtered[i];
4949
- const fullPath = join5(dir, name);
4818
+ const fullPath = join4(dir, name);
4950
4819
  const isLast = i === filtered.length - 1;
4951
4820
  const connector = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
4952
4821
  let isDir;
@@ -4980,7 +4849,7 @@ ${treeLines.join("\n")}`
4980
4849
  for (const name of entries) {
4981
4850
  if (totalChars >= MAX_TOTAL_CHARS) break;
4982
4851
  if (SKIP_DIRS_SET.has(name)) continue;
4983
- const fullPath = join5(dir, name);
4852
+ const fullPath = join4(dir, name);
4984
4853
  let st;
4985
4854
  try {
4986
4855
  st = statSync3(fullPath);
@@ -4995,7 +4864,7 @@ ${treeLines.join("\n")}`
4995
4864
  if (!TEXT_EXTS.has(ext) && !isSpecial) continue;
4996
4865
  if (st.size > MAX_FILE_CHARS * 3) continue;
4997
4866
  try {
4998
- let content = readFileSync4(fullPath, "utf-8");
4867
+ let content = readFileSync3(fullPath, "utf-8");
4999
4868
  if (content.length > MAX_FILE_CHARS) {
5000
4869
  content = content.slice(0, MAX_FILE_CHARS) + `
5001
4870
  ... (truncated, ${content.length} chars total)`;
@@ -5025,7 +4894,7 @@ ${content}
5025
4894
  */
5026
4895
  addExtraContextDir(dirPath) {
5027
4896
  const absPath = resolve2(dirPath);
5028
- if (!existsSync5(absPath)) {
4897
+ if (!existsSync4(absPath)) {
5029
4898
  return { success: false, charCount: 0, added: false, error: `Directory not found: ${dirPath}` };
5030
4899
  }
5031
4900
  let isDir;
@@ -5059,9 +4928,9 @@ ${content}
5059
4928
  */
5060
4929
  findContextFile(dir, candidates = CONTEXT_FILE_CANDIDATES) {
5061
4930
  for (const candidate of candidates) {
5062
- const fullPath = join5(dir, candidate);
5063
- if (existsSync5(fullPath)) {
5064
- const content = readFileSync4(fullPath, "utf-8").trim();
4931
+ const fullPath = join4(dir, candidate);
4932
+ if (existsSync4(fullPath)) {
4933
+ const content = readFileSync3(fullPath, "utf-8").trim();
5065
4934
  if (content) return { filePath: fullPath, content };
5066
4935
  }
5067
4936
  }
@@ -5089,10 +4958,10 @@ ${content}
5089
4958
  const cwd = process.cwd();
5090
4959
  const gitRoot = getGitRoot(cwd);
5091
4960
  const projectRoot = gitRoot ?? cwd;
5092
- const mcpPath = join5(projectRoot, MCP_PROJECT_CONFIG_NAME);
5093
- if (!existsSync5(mcpPath)) return null;
4961
+ const mcpPath = join4(projectRoot, MCP_PROJECT_CONFIG_NAME);
4962
+ if (!existsSync4(mcpPath)) return null;
5094
4963
  try {
5095
- const raw = JSON.parse(readFileSync4(mcpPath, "utf-8"));
4964
+ const raw = JSON.parse(readFileSync3(mcpPath, "utf-8"));
5096
4965
  const servers = raw?.mcpServers;
5097
4966
  if (!servers || typeof servers !== "object") {
5098
4967
  process.stderr.write(
@@ -5138,8 +5007,8 @@ ${content}
5138
5007
  );
5139
5008
  return { layers: [], mergedContent: "" };
5140
5009
  }
5141
- if (existsSync5(fullPath)) {
5142
- const content = readFileSync4(fullPath, "utf-8").trim();
5010
+ if (existsSync4(fullPath)) {
5011
+ const content = readFileSync3(fullPath, "utf-8").trim();
5143
5012
  if (content) {
5144
5013
  const layer = {
5145
5014
  level: "project",
@@ -5196,9 +5065,9 @@ ${content}
5196
5065
  * 超过 MEMORY_MAX_CHARS 时只取末尾最新部分。
5197
5066
  */
5198
5067
  loadMemoryContent() {
5199
- const memoryPath = join5(this.config.getConfigDir(), MEMORY_FILE_NAME);
5200
- if (!existsSync5(memoryPath)) return null;
5201
- let content = readFileSync4(memoryPath, "utf-8").trim();
5068
+ const memoryPath = join4(this.config.getConfigDir(), MEMORY_FILE_NAME);
5069
+ if (!existsSync4(memoryPath)) return null;
5070
+ let content = readFileSync3(memoryPath, "utf-8").trim();
5202
5071
  if (!content) return null;
5203
5072
  if (content.length > MEMORY_MAX_CHARS) {
5204
5073
  content = content.slice(-MEMORY_MAX_CHARS);
@@ -5586,14 +5455,14 @@ Session '${this.resumeSessionId}' not found.
5586
5455
  process.stdout.write(theme.dim(` \u{1F50C} Plugins loaded: ${pluginCount} tool(s) from plugins/
5587
5456
  `));
5588
5457
  }
5589
- const skillsDir = join5(this.config.getConfigDir(), SKILLS_DIR_NAME);
5458
+ const skillsDir = join4(this.config.getConfigDir(), SKILLS_DIR_NAME);
5590
5459
  this.skillManager = new SkillManager(skillsDir, this.config.get("ui").skillSizeWarn);
5591
5460
  const skillCount = this.skillManager.loadSkills();
5592
5461
  if (skillCount > 0) {
5593
5462
  process.stdout.write(theme.dim(` \u{1F3AF} Skills: ${skillCount} available (use /skill to manage)
5594
5463
  `));
5595
5464
  }
5596
- const commandsDir = join5(this.config.getConfigDir(), CUSTOM_COMMANDS_DIR_NAME);
5465
+ const commandsDir = join4(this.config.getConfigDir(), CUSTOM_COMMANDS_DIR_NAME);
5597
5466
  this.customCommandManager = new CustomCommandManager(commandsDir);
5598
5467
  const customCmdCount = this.customCommandManager.loadCommands();
5599
5468
  if (customCmdCount > 0) {
@@ -6228,14 +6097,14 @@ Session '${this.resumeSessionId}' not found.
6228
6097
  const dir = normalized.includes("/") ? dirname3(normalized) : ".";
6229
6098
  const prefix = normalized.includes("/") ? basename2(normalized) : normalized;
6230
6099
  const absDir = resolve2(process.cwd(), dir);
6231
- if (!existsSync5(absDir)) return [];
6100
+ if (!existsSync4(absDir)) return [];
6232
6101
  const entries = readdirSync3(absDir);
6233
6102
  const results = [];
6234
6103
  for (const entry of entries) {
6235
6104
  if (entry.startsWith(".")) continue;
6236
6105
  if (!entry.toLowerCase().startsWith(prefix.toLowerCase())) continue;
6237
6106
  try {
6238
- const fullPath = join5(absDir, entry);
6107
+ const fullPath = join4(absDir, entry);
6239
6108
  const stat = statSync3(fullPath);
6240
6109
  const rel = dir === "." ? entry : `${dir}/${entry}`;
6241
6110
  results.push(stat.isDirectory() ? `${rel}/` : rel);
@@ -6881,7 +6750,7 @@ This fresh stream has NO tools. Produce ONLY the document body: start with a mar
6881
6750
  const cleaned = stripPseudoToolCalls(genContent);
6882
6751
  if (looksLikeDocumentBody(cleaned)) {
6883
6752
  try {
6884
- writeFileSync3(saveToFile, cleaned, "utf-8");
6753
+ writeFileSync2(saveToFile, cleaned, "utf-8");
6885
6754
  process.stdout.write(theme.warning(
6886
6755
  `
6887
6756
  \u26A0 Salvaged save: stripped pseudo-tool-call markup (matched: ${pseudoMatch})
@@ -7491,7 +7360,7 @@ program.command("web").description("Start Web UI server with browser-based chat
7491
7360
  console.error("Error: Invalid port number. Must be between 1 and 65535.");
7492
7361
  process.exit(1);
7493
7362
  }
7494
- const { startWebServer } = await import("./server-3KIQK257.js");
7363
+ const { startWebServer } = await import("./server-UT6PLLZC.js");
7495
7364
  await startWebServer({ port, host: options.host });
7496
7365
  });
7497
7366
  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) => {
@@ -7657,13 +7526,17 @@ program.command("sessions").description("List recent conversation sessions").opt
7657
7526
  ${total} session${total !== 1 ? "s" : ""} total`);
7658
7527
  console.log(footer + "\n");
7659
7528
  });
7529
+ 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) => {
7530
+ const { runUsageCli } = await import("./usage-5KBD4UBB.js");
7531
+ await runUsageCli(options);
7532
+ });
7660
7533
  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) => {
7661
- const { runDoctorCli } = await import("./doctor-cli-VWYAVULH.js");
7534
+ const { runDoctorCli } = await import("./doctor-cli-X6MOE3YE.js");
7662
7535
  await runDoctorCli({ json: !!options.json, resetStats: !!options.resetStats });
7663
7536
  });
7664
7537
  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) => {
7665
7538
  try {
7666
- const batch = await import("./batch-V4KR6ENR.js");
7539
+ const batch = await import("./batch-ILD2EPEO.js");
7667
7540
  switch (action) {
7668
7541
  case "submit":
7669
7542
  if (!arg) {
@@ -7706,7 +7579,7 @@ program.command("batch <action> [arg] [arg2]").description("Anthropic Message Ba
7706
7579
  }
7707
7580
  });
7708
7581
  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) => {
7709
- const { startMcpServer } = await import("./server-CESWZS3I.js");
7582
+ const { startMcpServer } = await import("./server-H3KIFOLK.js");
7710
7583
  await startMcpServer({
7711
7584
  allowDestructive: !!options.allowDestructive,
7712
7585
  allowOutsideCwd: !!options.allowOutsideCwd,
@@ -7715,7 +7588,7 @@ program.command("mcp-serve").description("Start an MCP server over STDIO, exposi
7715
7588
  });
7716
7589
  });
7717
7590
  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) => {
7718
- const { runCi } = await import("./ci-NS7PR27C.js");
7591
+ const { runCi } = await import("./ci-7YWXFKGE.js");
7719
7592
  const result = await runCi({
7720
7593
  pr: options.pr,
7721
7594
  base: options.base,
@@ -7761,6 +7634,7 @@ program.command("help").description("Show a comprehensive guide to all aicli fea
7761
7634
  ` ${G}aicli config${R} Setup wizard (API keys, preferences)`,
7762
7635
  ` ${G}aicli providers${R} List all providers and status`,
7763
7636
  ` ${G}aicli sessions${R} List recent conversation sessions`,
7637
+ ` ${G}aicli usage${R} Token + cost usage by provider/model (--days/--month/--json)`,
7764
7638
  ` ${G}aicli user <action>${R} User management (list/create/delete)`,
7765
7639
  ` ${G}aicli batch <action>${R} Anthropic Batches API (50% off, 24h): submit/list/status/results/cancel`,
7766
7640
  ` ${G}aicli mcp-serve${R} MCP server over STDIO (expose tools to Claude Desktop / Cursor)`,
@@ -7860,7 +7734,7 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
7860
7734
  }),
7861
7735
  config.get("customProviders")
7862
7736
  );
7863
- const { startHub } = await import("./hub-DBGEPDIE.js");
7737
+ const { startHub } = await import("./hub-SFMWUEUW.js");
7864
7738
  await startHub(
7865
7739
  {
7866
7740
  topic: topic ?? "",
@@ -2,8 +2,8 @@
2
2
  import {
3
3
  executeTests,
4
4
  runTestsTool
5
- } from "./chunk-2AZX76LV.js";
6
- import "./chunk-A56C3L4P.js";
5
+ } from "./chunk-GSRXKHZ7.js";
6
+ import "./chunk-PMZCQAJL.js";
7
7
  import "./chunk-PDX44BCA.js";
8
8
  export {
9
9
  executeTests,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  executeTests,
3
3
  runTestsTool
4
- } from "./chunk-JWT54N7I.js";
4
+ } from "./chunk-A5VODFAK.js";
5
5
  import "./chunk-3RG5ZIWI.js";
6
6
  export {
7
7
  executeTests,
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ToolRegistry
4
- } from "./chunk-G44XZPNZ.js";
4
+ } from "./chunk-GOS4DWW5.js";
5
5
  import "./chunk-HDSKW7Q3.js";
6
6
  import "./chunk-ZWVIDFGY.js";
7
- import "./chunk-2AZX76LV.js";
7
+ import "./chunk-GSRXKHZ7.js";
8
8
  import {
9
9
  runTool
10
- } from "./chunk-ETGCCZBT.js";
10
+ } from "./chunk-GH32XE5K.js";
11
11
  import {
12
12
  getDangerLevel,
13
13
  schemaToJsonSchema
@@ -15,7 +15,7 @@ import {
15
15
  import "./chunk-TZQHYZKT.js";
16
16
  import {
17
17
  VERSION
18
- } from "./chunk-A56C3L4P.js";
18
+ } from "./chunk-PMZCQAJL.js";
19
19
  import "./chunk-4BKXL7SM.js";
20
20
  import "./chunk-MM3F43H6.js";
21
21
  import "./chunk-KHYD3WXE.js";