@cryer/star-cli 0.2.2 → 0.3.0

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.
package/dist/main.js CHANGED
@@ -14,6 +14,7 @@ import {
14
14
  compactMessages,
15
15
  createDefaultRegistry,
16
16
  createIgnorePredicate,
17
+ defaultAgentTasks,
17
18
  defaultTaskManager,
18
19
  discoverSkills,
19
20
  estimateTokens,
@@ -29,6 +30,8 @@ import {
29
30
  isGitRepo,
30
31
  isSensitivePath,
31
32
  listSnapshots,
33
+ loadTodos,
34
+ parseTodoArgs,
32
35
  projectConfigPath,
33
36
  reconcileToolCalls,
34
37
  resolveShell,
@@ -38,7 +41,7 @@ import {
38
41
  summarizeMessages,
39
42
  undoTurnSnapshots,
40
43
  userMemoryPath
41
- } from "./chunk-GN236FD4.js";
44
+ } from "./chunk-DZWTIMJ2.js";
42
45
 
43
46
  // src/main.tsx
44
47
  import { Command } from "commander";
@@ -272,8 +275,8 @@ var UsageTracker = class {
272
275
  };
273
276
 
274
277
  // src/cli/repl.tsx
275
- import { Box as Box10, Text as Text11, render, useApp, useInput as useInput6 } from "ink";
276
- import { useCallback, useEffect as useEffect2, useMemo, useRef as useRef2, useState as useState3 } from "react";
278
+ import { Box as Box11, Text as Text12, render, useApp, useInput as useInput6 } from "ink";
279
+ import { useCallback, useEffect as useEffect2, useMemo, useRef as useRef3, useState as useState3 } from "react";
277
280
 
278
281
  // src/config/save.ts
279
282
  import fs from "fs";
@@ -368,6 +371,20 @@ var ConfigSchema = z.object({
368
371
  // gracefully (some relays never send the terminal chunks). Applies once
369
372
  // streaming has started; the first part gets a longer, fixed allowance.
370
373
  streamIdleTimeoutSec: z.number().positive().default(20),
374
+ // Seconds to wait for the very first stream part before giving up; slow
375
+ // thinking-model relays can buffer for minutes before sending anything.
376
+ streamFirstChunkTimeoutSec: z.number().positive().default(300),
377
+ // Extra attempts per model request when a stream fails transiently
378
+ // (network error, 429/5xx, idle watchdog cutoff) or comes back empty, so a
379
+ // relay hiccup does not silently end a half-finished turn. 0 disables.
380
+ // Retries after a timeout/empty failure scale both stream timeouts up
381
+ // (attempt number ×, capped at 3x) since the relay is likely overloaded.
382
+ streamMaxRetries: z.number().int().min(0).default(3),
383
+ // How many times a turn may be nudged to continue when the model ends it
384
+ // with a text-only reply that announces pending work ("我将…", "I will…")
385
+ // instead of tool calls — a common premature-stop failure of weaker
386
+ // agentic models. Never fires in plan mode. 0 disables.
387
+ maxAutoContinues: z.number().int().min(0).default(2),
371
388
  contextCompaction: z.enum(["summary", "truncate"]).default("summary"),
372
389
  // Optional per-session dollar budget; when set, the REPL stops the session
373
390
  // once the estimated cost for the session reaches this amount.
@@ -2562,6 +2579,16 @@ function InputBox({
2562
2579
  return;
2563
2580
  }
2564
2581
  if (key.return) {
2582
+ if (key.shift) {
2583
+ edit(`${value.slice(0, cursor)}
2584
+ ${value.slice(cursor)}`, cursor + 1);
2585
+ return;
2586
+ }
2587
+ if (value[cursor - 1] === "\\") {
2588
+ edit(`${value.slice(0, cursor - 1)}
2589
+ ${value.slice(cursor)}`, cursor);
2590
+ return;
2591
+ }
2565
2592
  const text = value.trim();
2566
2593
  if (text.length > 0) {
2567
2594
  setHistory((prev) => [...prev, text]);
@@ -2572,6 +2599,11 @@ function InputBox({
2572
2599
  draftRef.current = "";
2573
2600
  return;
2574
2601
  }
2602
+ if (input === "\n" || input === "\r" || /^\[13;\d+u$/.test(input)) {
2603
+ edit(`${value.slice(0, cursor)}
2604
+ ${value.slice(cursor)}`, cursor + 1);
2605
+ return;
2606
+ }
2575
2607
  if (key.tab) {
2576
2608
  if (key.shift) return;
2577
2609
  if (suggestionCount > 0) {
@@ -2648,19 +2680,18 @@ function InputBox({
2648
2680
  return;
2649
2681
  }
2650
2682
  if (input && !key.ctrl && !key.meta) {
2651
- edit(value.slice(0, cursor) + input + value.slice(cursor), cursor + input.length);
2683
+ const text = input.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
2684
+ edit(value.slice(0, cursor) + text + value.slice(cursor), cursor + text.length);
2652
2685
  }
2653
2686
  });
2654
2687
  const before = value.slice(0, cursor);
2655
- const at = value[cursor] ?? " ";
2688
+ const at = value[cursor];
2656
2689
  const after = value.slice(cursor + 1);
2690
+ const cursorChar = at === "\n" || at === void 0 ? " " : at;
2691
+ const tail = (at === "\n" ? "\n" : "") + after;
2692
+ const styled = `\x1B[36m> \x1B[39m${before}\x1B[7m${cursorChar}\x1B[27m${tail}`;
2657
2693
  return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
2658
- /* @__PURE__ */ jsxs2(Box2, { borderStyle: "round", borderColor: "gray", paddingX: 1, children: [
2659
- /* @__PURE__ */ jsx2(Text2, { color: "cyan", children: "> " }),
2660
- /* @__PURE__ */ jsx2(Text2, { children: before }),
2661
- /* @__PURE__ */ jsx2(Text2, { inverse: true, children: at }),
2662
- /* @__PURE__ */ jsx2(Text2, { children: after })
2663
- ] }),
2694
+ /* @__PURE__ */ jsx2(Box2, { borderStyle: "round", borderColor: "gray", paddingX: 1, children: /* @__PURE__ */ jsx2(Text2, { children: styled }) }),
2664
2695
  suggestions.length > 0 && /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", paddingLeft: 2, children: suggestions.map((cmd, index) => {
2665
2696
  const label = cmd.usage ?? `/${cmd.name}`;
2666
2697
  return index === activeIndex ? /* @__PURE__ */ jsx2(Text2, { bold: true, inverse: true, children: `${label} - ${cmd.description}` }, cmd.name) : /* @__PURE__ */ jsxs2(Text2, { children: [
@@ -2777,25 +2808,30 @@ function RewindConfirmPrompt({ summary, onDecision }) {
2777
2808
 
2778
2809
  // src/cli/components/SelectPrompt.tsx
2779
2810
  import { Box as Box6, Text as Text7, useInput as useInput5 } from "ink";
2780
- import { useState as useState2 } from "react";
2811
+ import { useRef as useRef2, useState as useState2 } from "react";
2781
2812
  import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
2782
2813
  var SELECT_PROMPT_MAX_VISIBLE = 9;
2783
2814
  function SelectPrompt({ title, options, onSelect, onCancel }) {
2784
2815
  const [index, setIndex] = useState2(0);
2816
+ const indexRef = useRef2(0);
2817
+ const move = (delta) => {
2818
+ indexRef.current = Math.max(0, Math.min(indexRef.current + delta, options.length - 1));
2819
+ setIndex(indexRef.current);
2820
+ };
2785
2821
  useInput5((input, key) => {
2786
2822
  if (key.escape) {
2787
2823
  onCancel();
2788
2824
  return;
2789
2825
  }
2790
2826
  if (key.return) {
2791
- const option = options[index];
2827
+ const option = options[indexRef.current];
2792
2828
  if (option) onSelect(option.value);
2793
2829
  return;
2794
2830
  }
2795
2831
  if (key.downArrow || input === "j") {
2796
- setIndex((i) => Math.min(i + 1, options.length - 1));
2832
+ move(1);
2797
2833
  } else if (key.upArrow || input === "k") {
2798
- setIndex((i) => Math.max(i - 1, 0));
2834
+ move(-1);
2799
2835
  }
2800
2836
  });
2801
2837
  const maxStart = Math.max(0, options.length - SELECT_PROMPT_MAX_VISIBLE);
@@ -2943,6 +2979,31 @@ function ThinkingIndicator({
2943
2979
  ] });
2944
2980
  }
2945
2981
 
2982
+ // src/cli/components/TodoPanel.tsx
2983
+ import { Box as Box10, Text as Text11 } from "ink";
2984
+ import { jsx as jsx9, jsxs as jsxs11 } from "react/jsx-runtime";
2985
+ var SYMBOLS = {
2986
+ pending: "[ ]",
2987
+ in_progress: "[~]",
2988
+ done: "[x]"
2989
+ };
2990
+ function TodoPanel({ todos }) {
2991
+ if (todos.length === 0) return null;
2992
+ return /* @__PURE__ */ jsx9(Box10, { flexDirection: "column", children: todos.map((todo) => /* @__PURE__ */ jsxs11(
2993
+ Text11,
2994
+ {
2995
+ dimColor: todo.status === "done",
2996
+ color: todo.status === "in_progress" ? "cyan" : void 0,
2997
+ children: [
2998
+ SYMBOLS[todo.status],
2999
+ " ",
3000
+ todo.title
3001
+ ]
3002
+ },
3003
+ todo.id
3004
+ )) });
3005
+ }
3006
+
2946
3007
  // src/cli/components/ToolCallCard.tsx
2947
3008
  function formatToolCard(card) {
2948
3009
  const header = `${card.name} ${card.argsSummary}${card.isError ? " [error]" : ""}`;
@@ -3118,13 +3179,15 @@ async function checkForUpdate(currentVersion, fetcher = defaultFetcher2) {
3118
3179
  }
3119
3180
 
3120
3181
  // src/cli/repl.tsx
3121
- import { jsx as jsx9, jsxs as jsxs11 } from "react/jsx-runtime";
3182
+ import { jsx as jsx10, jsxs as jsxs12 } from "react/jsx-runtime";
3122
3183
  var CLEAR_TERMINAL = process.platform === "win32" ? "\x1B[2J\x1B[0f" : "\x1B[2J\x1B[3J\x1B[H";
3123
3184
  function emptyUsage() {
3124
3185
  return { requests: 0, promptTokens: 0, completionTokens: 0, totalTokens: 0 };
3125
3186
  }
3126
3187
  function runningTaskLabels() {
3127
- return defaultTaskManager.list().filter((t) => t.status === "running").map((t) => t.description ?? t.command);
3188
+ const shells = defaultTaskManager.list().filter((t) => t.status === "running").map((t) => t.description ?? t.command);
3189
+ const agents = defaultAgentTasks.list().filter((t) => t.status === "running").map((t) => t.description ?? t.id);
3190
+ return [...shells, ...agents];
3128
3191
  }
3129
3192
  var SESSION_MODE_CYCLE = ["ask", "auto", "readonly", "plan"];
3130
3193
  function formatUsage(usage) {
@@ -3165,44 +3228,45 @@ function Repl({
3165
3228
  const [spinnerTick, setSpinnerTick] = useState3(0);
3166
3229
  const [activity, setActivity] = useState3(null);
3167
3230
  const [bgLabels, setBgLabels] = useState3(() => runningTaskLabels());
3231
+ const [todos, setTodos] = useState3([]);
3168
3232
  const [permissionModeState, setPermissionModeState] = useState3(permissionMode);
3169
3233
  const [planApproval, setPlanApprovalState] = useState3(false);
3170
3234
  const [pendingRewind, setPendingRewindState] = useState3(null);
3171
3235
  const [picker, setPickerState] = useState3(null);
3172
- const backendRef = useRef2(backend);
3173
- const nextIdRef = useRef2(initialDisplay.length);
3174
- const abortRef = useRef2(null);
3175
- const streamedRef = useRef2("");
3176
- const thinkingRef = useRef2(false);
3177
- const reasoningRef = useRef2("");
3178
- const tickerStopRef = useRef2(null);
3179
- const flushedRef = useRef2({ streamed: "", reasoning: "" });
3180
- const toolCardsRef = useRef2(/* @__PURE__ */ new Map());
3181
- const pendingRef = useRef2(null);
3182
- const alwaysAllowedRef = useRef2(/* @__PURE__ */ new Set());
3183
- const prevModeRef = useRef2(null);
3184
- const planApprovalRef = useRef2(false);
3185
- const pendingRewindRef = useRef2(null);
3186
- const pickerRef = useRef2(null);
3187
- const modelNameRef = useRef2(model);
3188
- const sessionStoreRef = useRef2(sessionStore);
3189
- const usageRef = useRef2(initialUsage ? { ...initialUsage } : emptyUsage());
3190
- const turnChunksRef = useRef2(0);
3191
- const messagesRef = useRef2(initialDisplay);
3236
+ const backendRef = useRef3(backend);
3237
+ const nextIdRef = useRef3(initialDisplay.length);
3238
+ const abortRef = useRef3(null);
3239
+ const streamedRef = useRef3("");
3240
+ const thinkingRef = useRef3(false);
3241
+ const reasoningRef = useRef3("");
3242
+ const tickerStopRef = useRef3(null);
3243
+ const flushedRef = useRef3({ streamed: "", reasoning: "" });
3244
+ const toolCardsRef = useRef3(/* @__PURE__ */ new Map());
3245
+ const pendingRef = useRef3(null);
3246
+ const alwaysAllowedRef = useRef3(/* @__PURE__ */ new Set());
3247
+ const prevModeRef = useRef3(null);
3248
+ const planApprovalRef = useRef3(false);
3249
+ const pendingRewindRef = useRef3(null);
3250
+ const pickerRef = useRef3(null);
3251
+ const modelNameRef = useRef3(model);
3252
+ const sessionStoreRef = useRef3(sessionStore);
3253
+ const usageRef = useRef3(initialUsage ? { ...initialUsage } : emptyUsage());
3254
+ const turnChunksRef = useRef3(0);
3255
+ const messagesRef = useRef3(initialDisplay);
3192
3256
  const [queueItems, setQueueItems] = useState3([]);
3193
- const queueRef = useRef2(new PromptQueue());
3257
+ const queueRef = useRef3(new PromptQueue());
3194
3258
  const [pendingImages, setPendingImages] = useState3([]);
3195
- const pendingImagesRef = useRef2([]);
3196
- const imageSeqRef = useRef2(1);
3259
+ const pendingImagesRef = useRef3([]);
3260
+ const imageSeqRef = useRef3(1);
3197
3261
  const [inputRefill, setInputRefill] = useState3();
3198
- const refillSeqRef = useRef2(0);
3199
- const lastEscRef = useRef2(null);
3262
+ const refillSeqRef = useRef3(0);
3263
+ const lastEscRef = useRef3(null);
3200
3264
  const [gitBranch, setGitBranch] = useState3(null);
3201
3265
  const [contextPercent, setContextPercent] = useState3(null);
3202
3266
  const [initialHistory] = useState3(() => loadHistory());
3203
- const budgetWarnedRef = useRef2(false);
3204
- const budgetExceededRef = useRef2(false);
3205
- const runStreamRef = useRef2(
3267
+ const budgetWarnedRef = useRef3(false);
3268
+ const budgetExceededRef = useRef3(false);
3269
+ const runStreamRef = useRef3(
3206
3270
  null
3207
3271
  );
3208
3272
  const pushMessage = useCallback(
@@ -3351,6 +3415,10 @@ function Repl({
3351
3415
  if (message) pushMessage("system", message);
3352
3416
  });
3353
3417
  }, [pushMessage]);
3418
+ useEffect2(() => {
3419
+ loadTodos(cwd).then(setTodos).catch(() => {
3420
+ });
3421
+ }, [cwd]);
3354
3422
  useEffect2(() => {
3355
3423
  const onUpdate = (task) => {
3356
3424
  setBgLabels(runningTaskLabels());
@@ -3366,10 +3434,28 @@ function Repl({
3366
3434
  });
3367
3435
  }
3368
3436
  };
3437
+ const onAgentUpdate = (task) => {
3438
+ setBgLabels(runningTaskLabels());
3439
+ const label = task.description ? `: ${task.description}` : "";
3440
+ pushMessage(
3441
+ "system",
3442
+ task.status === "running" ? `Background subagent ${task.id} started${label}` : `Background subagent ${task.id} ${task.status}${label}`
3443
+ );
3444
+ if (task.status !== "running") {
3445
+ notifyBell({
3446
+ enabled: config.notifyBell,
3447
+ thresholdSec: config.notifyBellThresholdSec,
3448
+ noNotifyEnv: process.env.STAR_NO_NOTIFY === "1"
3449
+ });
3450
+ }
3451
+ };
3369
3452
  defaultTaskManager.on("update", onUpdate);
3453
+ defaultAgentTasks.on("update", onAgentUpdate);
3370
3454
  return () => {
3371
3455
  defaultTaskManager.off("update", onUpdate);
3456
+ defaultAgentTasks.off("update", onAgentUpdate);
3372
3457
  defaultTaskManager.cleanup();
3458
+ defaultAgentTasks.cleanup();
3373
3459
  };
3374
3460
  }, [pushMessage, config]);
3375
3461
  const interrupt = useCallback(() => {
@@ -3409,11 +3495,10 @@ function Repl({
3409
3495
  }, [pushMessage, redrawMessages]);
3410
3496
  const handleExit = useCallback(() => {
3411
3497
  const killed = defaultTaskManager.cleanup();
3412
- if (killed.length > 0) {
3413
- pushMessage(
3414
- "system",
3415
- `Stopped ${killed.length} background task(s): ${killed.map((t) => t.id).join(", ")}`
3416
- );
3498
+ const killedAgents = defaultAgentTasks.cleanup();
3499
+ const stopped = [...killed.map((t) => t.id), ...killedAgents.map((t) => t.id)];
3500
+ if (stopped.length > 0) {
3501
+ pushMessage("system", `Stopped ${stopped.length} background task(s): ${stopped.join(", ")}`);
3417
3502
  }
3418
3503
  exit();
3419
3504
  }, [exit, pushMessage]);
@@ -3598,6 +3683,10 @@ function Repl({
3598
3683
  name: event.name,
3599
3684
  argsSummary: summarizeArgs(event.args)
3600
3685
  });
3686
+ if (event.name === "todo_write") {
3687
+ const list = parseTodoArgs(event.args);
3688
+ if (list) setTodos(list);
3689
+ }
3601
3690
  setActivity(`running ${event.name}: ${summarizeArgs(event.args, 60)}`);
3602
3691
  } else if (event.type === "tool-result") {
3603
3692
  const card = toolCardsRef.current.get(event.id) ?? {
@@ -3630,6 +3719,13 @@ function Repl({
3630
3719
  sessionStoreRef.current?.addUsage(event.usage).catch(() => {
3631
3720
  });
3632
3721
  }
3722
+ } else if (event.type === "retry") {
3723
+ pushMessage(
3724
+ "system",
3725
+ `Request failed, retrying (${event.attempt}/${event.maxAttempts}): ${event.reason}`
3726
+ );
3727
+ } else if (event.type === "notice") {
3728
+ pushMessage("system", event.message);
3633
3729
  } else if (event.type === "error") {
3634
3730
  pushMessage("system", `Error: ${formatStreamError(event.error)}`);
3635
3731
  }
@@ -4068,6 +4164,9 @@ ${plan.affected.length} file change(s) will be reverted, ${messageCount} message
4068
4164
  `maxSteps: ${config.maxSteps}`,
4069
4165
  `contextMaxTokens: ${config.contextMaxTokens}`,
4070
4166
  `streamIdleTimeoutSec: ${config.streamIdleTimeoutSec}`,
4167
+ `streamFirstChunkTimeoutSec: ${config.streamFirstChunkTimeoutSec}`,
4168
+ `streamMaxRetries: ${config.streamMaxRetries}`,
4169
+ `maxAutoContinues: ${config.maxAutoContinues}`,
4071
4170
  `permissions.allow (${config.permissions.allow.length}): ${config.permissions.allow.join(", ") || "(none)"}`,
4072
4171
  `hooks (${config.hooks.length}): ${config.hooks.map((h) => h.event).join(", ") || "(none)"}`
4073
4172
  ].join("\n")
@@ -4192,9 +4291,9 @@ ${plan.affected.length} file change(s) will be reverted, ${messageCount} message
4192
4291
  }
4193
4292
  }, [cwd, config, isStreaming, epoch]);
4194
4293
  const sessionCost = sessionCostUsd();
4195
- return /* @__PURE__ */ jsxs11(Box10, { flexDirection: "column", children: [
4196
- /* @__PURE__ */ jsx9(MessageList, { messages }, `messages-${epoch}`),
4197
- (thinking || activity !== null) && /* @__PURE__ */ jsx9(
4294
+ return /* @__PURE__ */ jsxs12(Box11, { flexDirection: "column", children: [
4295
+ /* @__PURE__ */ jsx10(MessageList, { messages }, `messages-${epoch}`),
4296
+ (thinking || activity !== null) && /* @__PURE__ */ jsx10(
4198
4297
  ThinkingIndicator,
4199
4298
  {
4200
4299
  reasoning: thinkingText,
@@ -4202,9 +4301,9 @@ ${plan.affected.length} file change(s) will be reverted, ${messageCount} message
4202
4301
  activity: activity ?? void 0
4203
4302
  }
4204
4303
  ),
4205
- !thinking && activity === null && thoughtSummary !== null && /* @__PURE__ */ jsx9(Text11, { dimColor: true, children: thoughtSummary }),
4206
- streamingText !== null && (streamingText !== "" || turnChunksRef.current === 0) && /* @__PURE__ */ jsx9(StreamingMessage, { text: streamingText, continuation: turnChunksRef.current > 0 }),
4207
- pending && /* @__PURE__ */ jsx9(
4304
+ !thinking && activity === null && thoughtSummary !== null && /* @__PURE__ */ jsx10(Text12, { dimColor: true, children: thoughtSummary }),
4305
+ streamingText !== null && (streamingText !== "" || turnChunksRef.current === 0) && /* @__PURE__ */ jsx10(StreamingMessage, { text: streamingText, continuation: turnChunksRef.current > 0 }),
4306
+ pending && /* @__PURE__ */ jsx10(
4208
4307
  PermissionPrompt,
4209
4308
  {
4210
4309
  request: pending.request,
@@ -4212,9 +4311,9 @@ ${plan.affected.length} file change(s) will be reverted, ${messageCount} message
4212
4311
  onDecision: handleDecision
4213
4312
  }
4214
4313
  ),
4215
- planApproval && /* @__PURE__ */ jsx9(ExitPlanPrompt, { onDecision: handlePlanDecision }),
4216
- pendingRewind && /* @__PURE__ */ jsx9(RewindConfirmPrompt, { summary: pendingRewind.summary, onDecision: handleRewindDecision }),
4217
- picker && /* @__PURE__ */ jsx9(
4314
+ planApproval && /* @__PURE__ */ jsx10(ExitPlanPrompt, { onDecision: handlePlanDecision }),
4315
+ pendingRewind && /* @__PURE__ */ jsx10(RewindConfirmPrompt, { summary: pendingRewind.summary, onDecision: handleRewindDecision }),
4316
+ picker && /* @__PURE__ */ jsx10(
4218
4317
  SelectPrompt,
4219
4318
  {
4220
4319
  title: picker.title,
@@ -4223,16 +4322,17 @@ ${plan.affected.length} file change(s) will be reverted, ${messageCount} message
4223
4322
  onCancel: handlePickerCancel
4224
4323
  }
4225
4324
  ),
4226
- queueItems.map((item) => /* @__PURE__ */ jsxs11(Text11, { dimColor: true, children: [
4325
+ queueItems.map((item) => /* @__PURE__ */ jsxs12(Text12, { dimColor: true, children: [
4227
4326
  "queued: ",
4228
4327
  item.text.length > 80 ? `${item.text.slice(0, 80)}\u2026` : item.text
4229
4328
  ] }, item.id)),
4230
- pendingImages.map((staged) => /* @__PURE__ */ jsxs11(Text11, { dimColor: true, children: [
4329
+ pendingImages.map((staged) => /* @__PURE__ */ jsxs12(Text12, { dimColor: true, children: [
4231
4330
  "[image attached: ",
4232
4331
  staged.image.path,
4233
4332
  "]"
4234
4333
  ] }, staged.id)),
4235
- /* @__PURE__ */ jsx9(
4334
+ /* @__PURE__ */ jsx10(TodoPanel, { todos }),
4335
+ /* @__PURE__ */ jsx10(
4236
4336
  InputBox,
4237
4337
  {
4238
4338
  isStreaming,
@@ -4247,7 +4347,7 @@ ${plan.affected.length} file change(s) will be reverted, ${messageCount} message
4247
4347
  onPasteImage: handlePasteImage
4248
4348
  }
4249
4349
  ),
4250
- /* @__PURE__ */ jsx9(
4350
+ /* @__PURE__ */ jsx10(
4251
4351
  StatusBar,
4252
4352
  {
4253
4353
  cwd,
@@ -4267,7 +4367,7 @@ function renderRepl(backend, opts) {
4267
4367
  defaultTaskManager.cleanup();
4268
4368
  });
4269
4369
  return render(
4270
- /* @__PURE__ */ jsx9(
4370
+ /* @__PURE__ */ jsx10(
4271
4371
  Repl,
4272
4372
  {
4273
4373
  backend,
@@ -4327,7 +4427,13 @@ function loadConfigSync(cwd, overrides) {
4327
4427
  var SYSTEM_PROMPT = `You are Star CLI, an AI coding agent running in the user's terminal.
4328
4428
  You help with software engineering tasks: reading, writing and editing code, running shell commands, and managing todos.
4329
4429
  Be concise and direct. Use tools when they help accomplish the task.
4330
- The working directory is the user's project root; never touch files outside it without explicit instruction.`;
4430
+ For any task with two or more steps, start by creating a todo list with todo_write and keep it updated as you progress; only mark an item completed after you have verified its result.
4431
+ The working directory is the user's project root; never touch files outside it without explicit instruction.
4432
+ For conversion, batch-processing, or other scriptable tasks, write a script file into the working directory and run it instead of pasting long inline code into the shell; once the script's output is verified, delete the script unless the user asked to keep it.
4433
+ When a tool call or command fails, read the error output, work out the cause, and try again with a corrected or alternative approach \u2014 never repeat an identical failing call without changing something.
4434
+ Never consider the task finished until you have verified the result yourself: run the tests, the build, or a check command that proves the output is correct, and if verification fails or reveals gaps, keep fixing until it passes.
4435
+ Do not end your turn while the task is still incomplete; keep going until it is done or you are genuinely blocked, and if you are blocked, state exactly what is missing.
4436
+ Never end a reply by announcing what you will do next \u2014 either do it now with tool calls, or do not mention it. Narrating future actions is not progress.`;
4331
4437
  async function createLoop(config, modelName, cwd, sessionStore) {
4332
4438
  const model = createModel(config, modelName);
4333
4439
  const registry = createDefaultRegistry();
@@ -4411,6 +4517,19 @@ async function printMode(loop, prompt, cwd, json, imagePaths) {
4411
4517
  case "finish":
4412
4518
  usage.add(event.usage);
4413
4519
  break;
4520
+ case "retry":
4521
+ if (!json)
4522
+ process.stderr.write(
4523
+ `
4524
+ [retry ${event.attempt}/${event.maxAttempts}] ${event.reason}
4525
+ `
4526
+ );
4527
+ break;
4528
+ case "notice":
4529
+ if (!json) process.stderr.write(`
4530
+ [notice] ${event.message}
4531
+ `);
4532
+ break;
4414
4533
  case "error":
4415
4534
  if (!json) process.stderr.write(`
4416
4535
  [error] ${formatStreamError(event.error)}
@@ -4444,6 +4563,7 @@ async function printMode(loop, prompt, cwd, json, imagePaths) {
4444
4563
  }
4445
4564
  if (!json) process.stdout.write("\n");
4446
4565
  defaultTaskManager.cleanup();
4566
+ defaultAgentTasks.cleanup();
4447
4567
  return exitCode;
4448
4568
  }
4449
4569
  var program = new Command();