jinzd-ai-cli 0.4.3 → 0.4.5

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.
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  EnvLoader,
4
4
  schemaToJsonSchema
5
- } from "./chunk-AQ4O6SVK.js";
5
+ } from "./chunk-BVQJ4BOW.js";
6
6
  import {
7
7
  APP_NAME,
8
8
  CONFIG_DIR_NAME,
@@ -15,7 +15,7 @@ import {
15
15
  MCP_TOOL_PREFIX,
16
16
  PLUGINS_DIR_NAME,
17
17
  VERSION
18
- } from "./chunk-3LKIHGAR.js";
18
+ } from "./chunk-GT2YXIQ4.js";
19
19
 
20
20
  // src/config/config-manager.ts
21
21
  import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
@@ -6,7 +6,7 @@ import {
6
6
  SUBAGENT_DEFAULT_MAX_ROUNDS,
7
7
  SUBAGENT_MAX_ROUNDS_LIMIT,
8
8
  runTestsTool
9
- } from "./chunk-3LKIHGAR.js";
9
+ } from "./chunk-GT2YXIQ4.js";
10
10
 
11
11
  // src/tools/builtin/bash.ts
12
12
  import { execSync } from "child_process";
@@ -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.3";
11
+ var VERSION = "0.4.5";
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.3";
9
+ var VERSION = "0.4.5";
10
10
  var APP_NAME = "ai-cli";
11
11
  var CONFIG_DIR_NAME = ".aicli";
12
12
  var CONFIG_FILE_NAME = "config.json";
@@ -381,7 +381,7 @@ ${content}`);
381
381
  }
382
382
  }
383
383
  async function runTaskMode(config, providers, configManager, topic) {
384
- const { TaskOrchestrator } = await import("./task-orchestrator-7NTCIVCF.js");
384
+ const { TaskOrchestrator } = await import("./task-orchestrator-H2VTYZ5W.js");
385
385
  const orchestrator = new TaskOrchestrator(config, providers, configManager);
386
386
  let interrupted = false;
387
387
  const onSigint = () => {
package/dist/index.js CHANGED
@@ -23,7 +23,7 @@ import {
23
23
  saveDevState,
24
24
  sessionHasMeaningfulContent,
25
25
  setupProxy
26
- } from "./chunk-VC6DFUFP.js";
26
+ } from "./chunk-6X3IYMCS.js";
27
27
  import {
28
28
  ToolRegistry,
29
29
  askUserContext,
@@ -38,7 +38,7 @@ import {
38
38
  theme,
39
39
  truncateOutput,
40
40
  undoStack
41
- } from "./chunk-AQ4O6SVK.js";
41
+ } from "./chunk-BVQJ4BOW.js";
42
42
  import {
43
43
  AGENTIC_BEHAVIOR_GUIDELINE,
44
44
  AUTHOR,
@@ -58,7 +58,7 @@ import {
58
58
  REPO_URL,
59
59
  SKILLS_DIR_NAME,
60
60
  VERSION
61
- } from "./chunk-3LKIHGAR.js";
61
+ } from "./chunk-GT2YXIQ4.js";
62
62
 
63
63
  // src/index.ts
64
64
  import { program } from "commander";
@@ -1914,7 +1914,7 @@ ${hint}` : "")
1914
1914
  description: "Run project tests and show structured report",
1915
1915
  usage: "/test [command|filter]",
1916
1916
  async execute(args, _ctx) {
1917
- const { executeTests } = await import("./run-tests-W2RQN33S.js");
1917
+ const { executeTests } = await import("./run-tests-NJHE6WPG.js");
1918
1918
  const argStr = args.join(" ").trim();
1919
1919
  let testArgs = {};
1920
1920
  if (argStr) {
@@ -5524,7 +5524,7 @@ program.command("web").description("Start Web UI server with browser-based chat
5524
5524
  console.error("Error: Invalid port number. Must be between 1 and 65535.");
5525
5525
  process.exit(1);
5526
5526
  }
5527
- const { startWebServer } = await import("./server-WPLQ2AII.js");
5527
+ const { startWebServer } = await import("./server-TXOTONRK.js");
5528
5528
  await startWebServer({ port, host: options.host });
5529
5529
  });
5530
5530
  program.command("user [action] [username]").description("Manage Web UI users (list | create <name> | delete <name> | reset-password <name> | migrate <name>)").action(async (action, username) => {
@@ -5757,7 +5757,7 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
5757
5757
  }),
5758
5758
  config.get("customProviders")
5759
5759
  );
5760
- const { startHub } = await import("./hub-UQ2IWPJK.js");
5760
+ const { startHub } = await import("./hub-FK6ECMQQ.js");
5761
5761
  await startHub(
5762
5762
  {
5763
5763
  topic: topic ?? "",
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  executeTests,
3
3
  runTestsTool
4
- } from "./chunk-ROFUA4O3.js";
4
+ } from "./chunk-HPWNNTFJ.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-3LKIHGAR.js";
5
+ } from "./chunk-GT2YXIQ4.js";
6
6
  export {
7
7
  executeTests,
8
8
  runTestsTool
@@ -18,7 +18,7 @@ import {
18
18
  renderDiff,
19
19
  runHook,
20
20
  setupProxy
21
- } from "./chunk-VC6DFUFP.js";
21
+ } from "./chunk-6X3IYMCS.js";
22
22
  import {
23
23
  AuthManager
24
24
  } from "./chunk-CPLT6CD3.js";
@@ -32,7 +32,7 @@ import {
32
32
  spawnAgentContext,
33
33
  truncateOutput,
34
34
  undoStack
35
- } from "./chunk-AQ4O6SVK.js";
35
+ } from "./chunk-BVQJ4BOW.js";
36
36
  import {
37
37
  AGENTIC_BEHAVIOR_GUIDELINE,
38
38
  CONTEXT_FILE_CANDIDATES,
@@ -44,7 +44,7 @@ import {
44
44
  PLAN_MODE_SYSTEM_ADDON,
45
45
  SKILLS_DIR_NAME,
46
46
  VERSION
47
- } from "./chunk-3LKIHGAR.js";
47
+ } from "./chunk-GT2YXIQ4.js";
48
48
 
49
49
  // src/web/server.ts
50
50
  import express from "express";
@@ -1440,7 +1440,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
1440
1440
  case "test": {
1441
1441
  this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
1442
1442
  try {
1443
- const { executeTests } = await import("./run-tests-W2RQN33S.js");
1443
+ const { executeTests } = await import("./run-tests-NJHE6WPG.js");
1444
1444
  const argStr = args.join(" ").trim();
1445
1445
  let testArgs = {};
1446
1446
  if (argStr) {
@@ -4,13 +4,15 @@ import {
4
4
  getDangerLevel,
5
5
  googleSearchContext,
6
6
  truncateOutput
7
- } from "./chunk-AQ4O6SVK.js";
7
+ } from "./chunk-BVQJ4BOW.js";
8
8
  import {
9
9
  SUBAGENT_ALLOWED_TOOLS
10
- } from "./chunk-3LKIHGAR.js";
10
+ } from "./chunk-GT2YXIQ4.js";
11
11
 
12
12
  // src/hub/task-orchestrator.ts
13
13
  import { createInterface } from "readline";
14
+ import { existsSync, readFileSync, writeFileSync, unlinkSync } from "fs";
15
+ import { join } from "path";
14
16
  import chalk from "chalk";
15
17
 
16
18
  // src/hub/task-executor.ts
@@ -27,9 +29,10 @@ async function executeTask(options) {
27
29
  onToolResult,
28
30
  onRound
29
31
  } = options;
32
+ const TASK_EXCLUDED_TOOLS = /* @__PURE__ */ new Set(["write_todos", "save_memory", "ask_user", "spawn_agent"]);
30
33
  const registry = new ToolRegistry();
31
34
  for (const tool of registry.listAll()) {
32
- if (!SUBAGENT_ALLOWED_TOOLS.has(tool.definition.name)) {
35
+ if (!SUBAGENT_ALLOWED_TOOLS.has(tool.definition.name) || TASK_EXCLUDED_TOOLS.has(tool.definition.name)) {
33
36
  registry.unregister(tool.definition.name);
34
37
  }
35
38
  }
@@ -48,11 +51,17 @@ ${task}
48
51
  ## Rules
49
52
  1. Focus exclusively on completing the task described above.
50
53
  2. Use tools to read, create, and modify files as needed.
51
- 3. When done, provide a clear summary: what was done, which files were created/modified, and the result.
52
- 4. Be efficient \u2014 minimize unnecessary tool calls.
53
- 5. Destructive operations (rm -rf, etc.) will be automatically blocked.
54
- 6. You cannot interact with users \u2014 work independently based on the task description.
55
- 7. Use the same language as the task description.${contextSection}`;
54
+ 3. Be efficient \u2014 minimize unnecessary tool calls. Do NOT use write_todos.
55
+ 4. Destructive operations (rm -rf, etc.) will be automatically blocked.
56
+ 5. You cannot interact with users \u2014 work independently based on the task description.
57
+ 6. Use the same language as the task description.
58
+
59
+ ## CRITICAL \u2014 When to Stop
60
+ Once you have completed the task (files created/modified, code written, tests passing), you MUST immediately respond with a plain text summary. Do NOT call any more tools after the work is done. Your text summary should include:
61
+ - What was accomplished
62
+ - Files created or modified
63
+ - Current state / any issues
64
+ If you keep calling tools after the task is done, you are wasting rounds. STOP and write your summary.${contextSection}`;
56
65
  const messages = [
57
66
  { role: "user", content: `Please execute the following task:
58
67
 
@@ -73,9 +82,12 @@ ${task}`, timestamp: /* @__PURE__ */ new Date() }
73
82
  googleSearchContext.configManager = configManager;
74
83
  }
75
84
  let activeSystemPrompt = systemPrompt;
76
- if (round === maxRounds - 2) {
77
- activeSystemPrompt += "\n\n\u26A0\uFE0F You have 2 rounds remaining. Start wrapping up \u2014 finish current work and prepare your summary.";
78
- } else if (round === maxRounds - 1) {
85
+ const remaining = maxRounds - round;
86
+ if (remaining <= 3 && remaining > 1) {
87
+ activeSystemPrompt += `
88
+
89
+ \u26A0\uFE0F You have ${remaining} rounds remaining. Finish your current work NOW and respond with a text summary on the next turn. Do NOT start new sub-tasks.`;
90
+ } else if (remaining === 1) {
79
91
  activeSystemPrompt += "\n\n\u{1F6D1} LAST ROUND. Do NOT call any more tools. You MUST respond with a text summary of what you accomplished. List files created/modified and the current state.";
80
92
  }
81
93
  const result = await provider.chatWithTools(
@@ -172,6 +184,7 @@ var TaskToolExecutor = class {
172
184
  };
173
185
 
174
186
  // src/hub/task-orchestrator.ts
187
+ var TASK_STATE_FILE = ".aicli-task.json";
175
188
  var TaskOrchestrator = class {
176
189
  constructor(config, providers, configManager) {
177
190
  this.config = config;
@@ -183,7 +196,7 @@ var TaskOrchestrator = class {
183
196
  this.aborted = true;
184
197
  }
185
198
  /**
186
- * Run the full task mode workflow.
199
+ * Run the full task mode workflow, with resume support.
187
200
  */
188
201
  async run(goal) {
189
202
  const provider = this.providers.get(this.config.defaultProvider);
@@ -191,23 +204,85 @@ var TaskOrchestrator = class {
191
204
  console.error(chalk.red(` \u2717 Provider "${this.config.defaultProvider}" not available.`));
192
205
  return;
193
206
  }
194
- this.renderPhaseHeader("PLAN", "Architect is decomposing the goal into tasks...");
195
- const plan = await this.generatePlan(goal, provider);
196
- if (!plan || plan.tasks.length === 0) {
197
- console.log(chalk.red(" \u2717 Failed to generate a task plan."));
198
- return;
207
+ const saved = this.loadState();
208
+ let plan = null;
209
+ if (saved && saved.tasks.some((t) => t.status === "pending" || t.status === "failed")) {
210
+ const doneCount = saved.tasks.filter((t) => t.status === "done").length;
211
+ const totalCount = saved.tasks.length;
212
+ console.log();
213
+ console.log(chalk.bold.yellow(` \u26A1 Found saved task plan (${doneCount}/${totalCount} completed)`));
214
+ console.log(chalk.dim(` Goal: ${saved.goal}`));
215
+ console.log();
216
+ const resume = await this.askResume();
217
+ if (resume) {
218
+ plan = saved;
219
+ for (const t of plan.tasks) {
220
+ if (t.status === "failed") t.status = "pending";
221
+ }
222
+ this.renderPlan(plan);
223
+ }
199
224
  }
200
- this.renderPlan(plan);
201
- this.renderPhaseHeader("APPROVE", "Review the task plan");
202
- const approved = await this.waitForApproval(plan);
203
- if (!approved) {
204
- console.log(chalk.yellow(" \u26A0 Task plan rejected. Exiting."));
205
- return;
225
+ if (!plan) {
226
+ this.renderPhaseHeader("PLAN", "Architect is decomposing the goal into tasks...");
227
+ plan = await this.generatePlan(goal, provider);
228
+ if (!plan || plan.tasks.length === 0) {
229
+ console.log(chalk.red(" \u2717 Failed to generate a task plan."));
230
+ return;
231
+ }
232
+ this.renderPlan(plan);
233
+ this.renderPhaseHeader("APPROVE", "Review the task plan");
234
+ const approved = await this.waitForApproval(plan);
235
+ if (!approved) {
236
+ console.log(chalk.yellow(" \u26A0 Task plan rejected. Exiting."));
237
+ return;
238
+ }
206
239
  }
207
240
  this.renderPhaseHeader("EXECUTE", "Agents are executing tasks...");
208
241
  await this.executePlan(plan, provider);
209
242
  this.renderPhaseHeader("REVIEW", "Generating project summary...");
210
243
  await this.generateReview(plan, provider);
244
+ const allDone = plan.tasks.every((t) => t.status === "done");
245
+ if (allDone) {
246
+ this.deleteState();
247
+ }
248
+ }
249
+ // ── State Persistence ────────────────────────────────────────────
250
+ get stateFilePath() {
251
+ return join(process.cwd(), TASK_STATE_FILE);
252
+ }
253
+ saveState(plan) {
254
+ try {
255
+ writeFileSync(this.stateFilePath, JSON.stringify(plan, null, 2), "utf-8");
256
+ } catch {
257
+ }
258
+ }
259
+ loadState() {
260
+ try {
261
+ if (!existsSync(this.stateFilePath)) return null;
262
+ const raw = readFileSync(this.stateFilePath, "utf-8");
263
+ const parsed = JSON.parse(raw);
264
+ if (!parsed.goal || !Array.isArray(parsed.tasks)) return null;
265
+ return parsed;
266
+ } catch {
267
+ return null;
268
+ }
269
+ }
270
+ deleteState() {
271
+ try {
272
+ if (existsSync(this.stateFilePath)) unlinkSync(this.stateFilePath);
273
+ } catch {
274
+ }
275
+ }
276
+ async askResume() {
277
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
278
+ try {
279
+ const answer = await new Promise((resolve) => {
280
+ rl.question(chalk.green(" Resume from saved state? (y/n) > "), resolve);
281
+ });
282
+ return answer.trim().toLowerCase() === "y" || answer.trim().toLowerCase() === "yes";
283
+ } finally {
284
+ rl.close();
285
+ }
211
286
  }
212
287
  // ── Phase 1: Plan Generation ──────────────────────────────────────
213
288
  async generatePlan(goal, provider) {
@@ -319,9 +394,12 @@ Example:
319
394
  }
320
395
  // ── Phase 3: Task Execution ───────────────────────────────────────
321
396
  async executePlan(plan, provider) {
322
- const maxRoundsPerTask = this.config.maxToolRoundsPerTurn ?? 15;
397
+ const maxRoundsPerTask = this.config.maxToolRoundsPerTurn ?? 30;
323
398
  const roleMap = new Map(this.config.roles.map((r) => [r.id, r]));
324
- const completedIds = /* @__PURE__ */ new Set();
399
+ const completedIds = new Set(
400
+ plan.tasks.filter((t) => t.status === "done").map((t) => t.id)
401
+ );
402
+ this.saveState(plan);
325
403
  while (!this.aborted) {
326
404
  const nextTask = plan.tasks.find(
327
405
  (t) => t.status === "pending" && t.dependencies.every((d) => completedIds.has(d))
@@ -368,6 +446,7 @@ Example:
368
446
  if (result.success) {
369
447
  completedIds.add(nextTask.id);
370
448
  }
449
+ this.saveState(plan);
371
450
  this.renderTaskEnd(nextTask, result);
372
451
  if (this.aborted) {
373
452
  console.log(chalk.yellow("\n \u26A0 Execution interrupted by user."));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jinzd-ai-cli",
3
- "version": "0.4.3",
3
+ "version": "0.4.5",
4
4
  "description": "Cross-platform REPL-style AI CLI with multi-provider support",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",