@cryer/star-cli 0.3.4 → 0.3.6

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/README.md CHANGED
@@ -52,6 +52,11 @@ Config file: `~/.star-cli/config.toml` (project-level override: `.star/config.to
52
52
  defaultModel = "gpt"
53
53
  permissionMode = "ask" # ask | auto | readonly | yolo | plan
54
54
  contextMaxTokens = 100000
55
+ # Max model round-trips ("steps") per turn between progress checkpoints — one
56
+ # step is a reply plus the tool calls it asked for; retries and auto-continue
57
+ # nudges don't count. Reaching the cap while tools still execute resets it
58
+ # with a notice instead of stopping the turn; 0 disables the cap entirely.
59
+ maxSteps = 100
55
60
  # compactThresholdTokens = 80000 # optional auto-compaction trigger in tokens; defaults to the
56
61
  # context window. /compact always compacts, regardless of this.
57
62
  contextCompaction = "summary" # summary | truncate — how over-budget history is compacted
@@ -83,6 +88,11 @@ model = "gpt-4o"
83
88
  # optional per-model context window — overrides the top-level contextMaxTokens
84
89
  # for compaction and the ctx % in the status bar
85
90
  # contextMaxTokens = 272000
91
+ # optional reasoning intensity for thinking models — forwarded verbatim as
92
+ # reasoning_effort on every request. Level naming varies by provider
93
+ # (low/medium/high are common; some add minimal/max...). Unset = server
94
+ # default. The /model picker also offers these levels after a model pick.
95
+ # reasoningEffort = "high"
86
96
  # optional per-model pricing in USD per 1M tokens — enables the $ estimate in
87
97
  # /cost and /usage. promptPrice prices input tokens (system prompt, history,
88
98
  # @file contents, tool results — resent every turn, so the bulk of usage);
@@ -166,9 +176,7 @@ star --clear-sessions all delete every stored session
166
176
 
167
177
  Keys: `ESC` / `Ctrl+C` interrupts the current stream — the partial reply generated so far stays on screen (and in the session history) with a dim `[interrupted]` marker; on a permission prompt: `y` allow, `n` deny, `a` always allow — the generated allow-rule (e.g. `bash(npm test)`) is saved to `permissions.allow` in the config file and survives restarts. Write/edit prompts include a colored diff preview of the pending change. `Shift+Tab` cycles the permission mode for the session (`ask` → `auto` → `readonly` → `plan`; not saved to config). Input editing: arrow keys move the cursor, `Ctrl+A`/`Ctrl+E` jump to start/end, `Ctrl+U`/`Ctrl+K` delete before/after the cursor, `Ctrl+W` deletes the previous word, up/down recall history (persisted to `~/.star-cli/history` across sessions, capped at 50 entries, consecutive duplicates collapsed, slash commands and key-repeat garbage never recorded); while a recalled entry is unmodified up/down keep switching entries, but once you edit the text — or are typing your own multi-line input — up/down move the cursor between lines instead. `Ctrl+R` starts a reverse history search: type to filter (case-insensitive, newest match first), `Ctrl+R`/`↑` for older matches, `↓` for newer, `Enter` to accept the match into the input, `ESC`/`Ctrl+C`/`Ctrl+G` to cancel and restore what you had. Multi-line input: use `Ctrl+J` (or `Alt+Enter`), or end the line with `\` and press `Enter` — pasted multi-line text is kept intact. Pasting a large block (10+ lines or 500+ characters in one go) collapses it into a dim `[pasted #N: L lines]` placeholder so the input box stays compact; the full text is restored into what gets submitted, and `Backspace` removes the placeholder as a unit. A huge paste that the terminal delivers in several chunks is reassembled (via bracketed paste where the terminal supports it) and still collapses into a single placeholder. Typing `/` shows slash-command suggestions with their usage — prefix matches first, then fuzzy subsequence matches; `↑`/`↓` to highlight, `Tab` (or `→` at end of input) to complete, `ESC` to dismiss, and an unknown command name gets a `Did you mean: …` hint. Typing `@` completes file paths relative to the working directory with the same keys; directories end in `/` so `Tab` descends, and `node_modules`, `.git`, `dist` and `.starignore` entries are skipped. `Alt+V` pastes an image from the clipboard as an attachment (each one shows as `[image attached: clipboard.png]` above the input; uses PowerShell on Windows, pngpaste/osascript on macOS, xclip on Linux). Images larger than 2000px on the longest side are automatically downsampled to fit (keeping the aspect ratio, via platform-native tools — no extra installs on Windows/macOS; ImageMagick or ffmpeg on Linux); when resizing isn't possible the original is kept and a warning is shown. `Ctrl+V` also works where the terminal passes it through — Windows Terminal binds Ctrl+V to its own paste, so use `Alt+V` there. While a turn is streaming you can keep typing: submitted prompts (and `!` bangs) queue up as dimmed entries and auto-send in order once the turn finishes; `ESC` during a turn aborts it and clears the queue (the partial reply is kept, marked `[interrupted]`). Pressing `ESC` twice within half a second while idle retracts your last prompt and restores its text into the input for editing.
168
178
 
169
- While the model is working, a spinner (`- \ | /`) shows `star is thinking…`; reasoning models also stream a dimmed tail of their thinking (last ~200 chars), which collapses to a one-line summary once the answer starts. Streamed replies, reasoning, tool output and todo titles are sanitized for display (tabs expanded to two spaces, control bytes and raw escape sequences stripped) so relayed model output cannot corrupt the terminal or leave stale frames piling up; long todo lists collapse to a 10-row window around the active task and long typeahead queues to their first 5 entries, keeping the live area well below terminal height.
170
-
171
- The status bar shows the working directory (full path on wide terminals), the git branch, the model, the permission mode, context usage as a percentage of the model's context window (its own `contextMaxTokens` when set, else the top-level one), the session prompt-cache hit rate when the provider reports cache usage (`cache: 42%`, or `cache: Not provided` when it never does), the session cost when the model has pricing configured, and total tokens.
179
+ The status bar shows the working directory (full path on wide terminals), the git branch, the model, the permission mode, context usage as a percentage of the model's context window (its own `contextMaxTokens` when set, else the top-level one), the session prompt-cache hit rate when the provider reports cache usage (`cache: 42%`, or `cache: Not provided` when it never does), the session cost when the model has pricing configured, and total tokens. Model replies render as markdown (headings, emphasis, code blocks, tables) directly in the terminal. Decorative icons use emoji on terminals known to render them double-width (Windows Terminal, VS Code, Kitty, …) and fall back to plain symbols elsewhere so the display never desyncs; force a style with `STAR_ICONS=emoji|plain`.
172
180
 
173
181
  ## !shell passthrough
174
182
 
package/README.zh-CN.md CHANGED
@@ -52,6 +52,10 @@ star -p "hi" # non-interactive print mode
52
52
  defaultModel = "gpt"
53
53
  permissionMode = "ask" # ask | auto | readonly | yolo | plan
54
54
  contextMaxTokens = 100000
55
+ # 每回合在进度检查点之间允许的最大模型往返("step")数——一个 step 是
56
+ # 一次回复加上它请求的所有工具调用;重试和 auto-continue nudge 不占 step。
57
+ # 到达上限时若工具仍在执行,会发提示并重置额度而不是终止回合;0 表示完全不限。
58
+ maxSteps = 100
55
59
  # compactThresholdTokens = 80000 # optional auto-compaction trigger in tokens; defaults to the
56
60
  # context window. /compact always compacts, regardless of this.
57
61
  contextCompaction = "summary" # summary | truncate — how over-budget history is compacted
@@ -83,6 +87,11 @@ model = "gpt-4o"
83
87
  # optional per-model context window — overrides the top-level contextMaxTokens
84
88
  # for compaction and the ctx % in the status bar
85
89
  # contextMaxTokens = 272000
90
+ # 可选的推理强度(思考模型用)——每次请求原样以 reasoning_effort 发送。
91
+ # 档位命名因服务商/模型而异(常见 low/medium/high,部分还有 minimal/max 等),
92
+ # 因此接受任意字符串,由服务端校验。不设置 = 服务端默认。
93
+ # /model 选完模型后也会接着让你选择推理强度。
94
+ # reasoningEffort = "high"
86
95
  # optional per-model pricing in USD per 1M tokens — enables the $ estimate in
87
96
  # /cost and /usage. promptPrice prices input tokens (system prompt, history,
88
97
  # @file contents, tool results — resent every turn, so the bulk of usage);
@@ -166,9 +175,7 @@ star --clear-sessions all delete every stored session
166
175
 
167
176
  按键:`ESC` / `Ctrl+C` 中断当前流——已生成的部分回复保留在屏幕上(以及会话历史中),并带暗色 `[interrupted]` 标记;在权限提示上:`y` 允许,`n` 拒绝,`a` 始终允许——生成的 allow 规则(如 `bash(npm test)`)会保存到配置文件的 `permissions.allow` 中,重启后依然有效。写入/编辑提示包含待变更内容的彩色 diff 预览。`Shift+Tab` 在会话内循环切换权限模式(`ask` → `auto` → `readonly` → `plan`;不保存到配置)。输入编辑:方向键移动光标,`Ctrl+A`/`Ctrl+E` 跳到行首/行尾,`Ctrl+U`/`Ctrl+K` 删除光标前/后的内容,`Ctrl+W` 删除前一个单词,上/下键回忆历史(跨会话持久化到 `~/.star-cli/history`,上限 50 条,连续重复自动折叠,斜杠命令与按键重复的垃圾输入从不记录);当回忆出的条目未被修改时,上/下键继续在条目间切换,而一旦你编辑了文本——或正在输入自己的多行内容——上/下键改为在行之间移动光标。`Ctrl+R` 启动反向历史搜索:输入以过滤(不区分大小写,最新匹配优先),`Ctrl+R`/`↑` 找更早的匹配,`↓` 找更新的,`Enter` 将匹配接进输入框,`ESC`/`Ctrl+C`/`Ctrl+G` 取消并恢复你原有的内容。多行输入:使用 `Ctrl+J`(或 `Alt+Enter`),或以 `\` 结尾再按 `Enter`——粘贴的多行文本会原样保留。一次性粘贴大块内容(10+ 行或 500+ 字符)会折叠成暗色占位符 `[pasted #N: L lines]`,让输入框保持紧凑;完整文本会在提交时还原,`Backspace` 可将占位符整体删除。终端分多个块送达的超长粘贴会被重新组装(在支持的终端上通过 bracketed paste),并仍然折叠为单个占位符。输入 `/` 会显示斜杠命令建议及其用法——前缀匹配优先,然后是模糊子序列匹配;`↑`/`↓` 高亮,`Tab`(或在输入末尾按 `→`)补全,`ESC` 关闭,未知命令名会给出 `Did you mean: …` 提示。输入 `@` 会以同样的按键补全相对工作目录的文件路径;目录以 `/` 结尾以便用 `Tab` 下钻,`node_modules`、`.git`、`dist` 和 `.starignore` 条目会被跳过。`Alt+V` 把剪贴板中的图片作为附件粘贴(每张图片在输入框上方显示为 `[image attached: clipboard.png]`;Windows 上使用 PowerShell,macOS 上使用 pngpaste/osascript,Linux 上使用 xclip)。最长边超过 2000px 的图片会自动缩放以符合限制(保持宽高比,使用平台原生工具——Windows/macOS 无需额外安装;Linux 上使用 ImageMagick 或 ffmpeg);无法缩放时保留原图并显示警告。在终端透传的地方 `Ctrl+V` 也可用——Windows Terminal 把 Ctrl+V 绑定为自己的粘贴,请在那里使用 `Alt+V`。回合流式输出期间你可以继续输入:提交的提示(和 `!` bang)会以暗色条目排队,回合结束后按序自动发送;回合中按 `ESC` 会中止该回合并清空队列(部分回复保留,标记为 `[interrupted]`)。空闲时半秒内连按两次 `ESC` 会撤回你上一条提示,并把原文恢复进输入框供编辑。
168
177
 
169
- 模型工作时,加载动画(`- \ | /`)显示 `star is thinking…`;推理模型还会以暗色流式展示其思维尾部(最后约 200 字符),回答开始后折叠为一行摘要。流式回复、推理内容、工具输出和 todo 标题在显示前都会做净化处理(制表符展开为两个空格,剥离控制字节和原始转义序列),因此经中转的模型输出无法破坏终端或留下堆积的残留帧;长 todo 列表折叠为围绕活动任务的 10 行窗口,长预输入队列只显示前 5 条,让实时区域远低于终端高度。
170
-
171
- 状态栏显示工作目录(宽终端上显示完整路径)、git 分支、模型、权限模式、上下文用量占模型上下文窗口的百分比(模型自身设置了 `contextMaxTokens` 时用它,否则用顶层值)、服务商上报缓存用量时的会话提示词缓存命中率(`cache: 42%`,从不上报时显示 `cache: Not provided`)、模型配置了定价时的会话成本,以及 token 总数。
178
+ 状态栏显示工作目录(宽终端上显示完整路径)、git 分支、模型、权限模式、上下文用量占模型上下文窗口的百分比(模型自身设置了 `contextMaxTokens` 时用它,否则用顶层值)、服务商上报缓存用量时的会话提示词缓存命中率(`cache: 42%`,从不上报时显示 `cache: Not provided`)、模型配置了定价时的会话成本,以及 token 总数。模型回复会以 Markdown 形式直接在终端渲染(标题、强调、代码块、表格等)。装饰图标在已知能正确双宽渲染 emoji 的终端(Windows Terminal、VS Code、Kitty 等)上使用 emoji,其他终端回退为纯符号以避免显示错位;也可用 `STAR_ICONS=emoji|plain` 强制指定。
172
179
 
173
180
  ## !shell 直通
174
181
 
@@ -34,6 +34,14 @@ var ModelConfigSchema = z.object({
34
34
  // Per-model context window; overrides the top-level contextMaxTokens for
35
35
  // compaction and the status bar when set.
36
36
  contextMaxTokens: z.number().int().positive().optional(),
37
+ // Reasoning intensity for thinking models, forwarded verbatim as
38
+ // OpenAI-style reasoning_effort on every request (llm/provider.ts
39
+ // reasoningEffortMetadata) — level naming varies by model/provider
40
+ // (low|medium|high are common; some add minimal/max/xhigh/none), so any
41
+ // non-empty string is accepted and the server validates it. Unset = the
42
+ // request carries no effort field and the server-side default applies.
43
+ // Anthropic maps effort to a thinking budget instead — ignored there.
44
+ reasoningEffort: z.string().min(1).optional(),
37
45
  // Cost estimation needs BOTH prices (USD per 1M tokens); with only one set
38
46
  // the model is treated as unpriced.
39
47
  promptPrice: z.number().nonnegative().optional(),
@@ -68,7 +76,13 @@ var ConfigSchema = z.object({
68
76
  permissionMode: z.enum(["auto", "ask", "readonly", "yolo", "plan"]).default("ask"),
69
77
  providers: z.array(ProviderConfigSchema).default([]),
70
78
  models: z.array(ModelConfigSchema).default([]),
71
- maxSteps: z.number().int().positive().default(50),
79
+ // Cap on model round-trips ("steps") per turn between progress checkpoints.
80
+ // One step is a streamed reply plus every tool call it asked for — stream
81
+ // retries and auto-continue nudges do not consume steps. Reaching the cap
82
+ // while tools still execute resets the budget with a notice; the turn stops
83
+ // only when no tool ran since the last checkpoint (the doom-loop
84
+ // signature). 0 disables the cap entirely (opencode/codex have none).
85
+ maxSteps: z.number().int().min(0).default(100),
72
86
  contextMaxTokens: z.number().int().positive().default(1e5),
73
87
  // Token threshold at which the agent loop auto-compacts the history.
74
88
  // Defaults to the effective context window (compact only when full); set a
@@ -2924,7 +2938,7 @@ var MAX_SUBAGENT_DEPTH = 1;
2924
2938
  var MAX_REPORT_CHARS = 2e4;
2925
2939
  var SUBAGENT_PROMPT = "You are a subagent spawned to handle a focused subtask on behalf of the main agent. Work autonomously with the tools available to you, then finish with a concise report of what you found or did. Your final text is returned to the main agent as the tool result \u2014 make it self-contained.";
2926
2940
  async function runSubagent(deps, args, cwd, signal) {
2927
- const { AgentLoop: AgentLoop2 } = await import("./loop-SDZVEQND.js");
2941
+ const { AgentLoop: AgentLoop2 } = await import("./loop-XY3TE5NN.js");
2928
2942
  const child = new AgentLoop2({
2929
2943
  model: deps.model,
2930
2944
  registry: createDefaultRegistry(),
@@ -2933,6 +2947,7 @@ async function runSubagent(deps, args, cwd, signal) {
2933
2947
  system: deps.system ? `${deps.system}
2934
2948
 
2935
2949
  ${SUBAGENT_PROMPT}` : SUBAGENT_PROMPT,
2950
+ providerMetadata: deps.providerMetadata,
2936
2951
  subagentDepth: deps.depth + 1
2937
2952
  });
2938
2953
  const confirmHandler = deps.getConfirmHandler?.();
@@ -3210,6 +3225,7 @@ var AgentLoop = class {
3210
3225
  config: opts.config,
3211
3226
  cwd: opts.cwd,
3212
3227
  system: opts.system,
3228
+ providerMetadata: opts.providerMetadata,
3213
3229
  depth,
3214
3230
  getConfirmHandler: () => this.confirmHandler
3215
3231
  })
@@ -3219,6 +3235,11 @@ var AgentLoop = class {
3219
3235
  getMessages() {
3220
3236
  return this.messages;
3221
3237
  }
3238
+ // Rebinds the per-call provider metadata (e.g. after the reasoning effort
3239
+ // for the current model changed via /model) without recreating the loop.
3240
+ setProviderMetadata(metadata) {
3241
+ this.opts.providerMetadata = metadata;
3242
+ }
3222
3243
  // Swaps the persistence target (/new starts a fresh session mid-REPL).
3223
3244
  // Title generation is re-armed so the new session gets one after its first
3224
3245
  // turn, and snapshot checkpoints now flow to the new store. The redo stack
@@ -3475,7 +3496,27 @@ var AgentLoop = class {
3475
3496
  let lastWasNudge = false;
3476
3497
  let usedToolsThisTurn = false;
3477
3498
  let openTodos = [];
3478
- for (let step = 0; step < config.maxSteps; step++) {
3499
+ const maxSteps = config.maxSteps;
3500
+ let step = 0;
3501
+ let toolExecutedSinceCheckpoint = false;
3502
+ for (; ; ) {
3503
+ if (maxSteps > 0 && step >= maxSteps) {
3504
+ if (!toolExecutedSinceCheckpoint) {
3505
+ yield {
3506
+ type: "error",
3507
+ error: new Error(
3508
+ `Max steps (${maxSteps}) reached with no tool execution since the last checkpoint, stopping.`
3509
+ )
3510
+ };
3511
+ return;
3512
+ }
3513
+ toolExecutedSinceCheckpoint = false;
3514
+ step = 0;
3515
+ yield {
3516
+ type: "notice",
3517
+ message: `maxSteps (${maxSteps}) reached and the turn is still making progress; continuing. Press Esc to interrupt.`
3518
+ };
3519
+ }
3479
3520
  if ((this.opts.subagentDepth ?? 0) === 0) {
3480
3521
  for (const finished of defaultAgentTasks.drainNotifications()) {
3481
3522
  const label = finished.description ? ` "${finished.description}"` : "";
@@ -3663,6 +3704,8 @@ ${finished.result}`
3663
3704
  const refusal = this.doomLoopRefusal(call);
3664
3705
  if (refusal !== null) {
3665
3706
  yield { type: "notice", message: refusal };
3707
+ } else {
3708
+ toolExecutedSinceCheckpoint = true;
3666
3709
  }
3667
3710
  const result = refusal !== null ? { content: refusal, isError: true } : await this.executeTool(call, signal);
3668
3711
  const toolMessage = {
@@ -3712,11 +3755,8 @@ ${finished.result}`
3712
3755
  }
3713
3756
  }
3714
3757
  if (signal.aborted) return;
3758
+ step++;
3715
3759
  }
3716
- yield {
3717
- type: "error",
3718
- error: new Error(`Max steps (${config.maxSteps}) reached, stopping.`)
3719
- };
3720
3760
  }
3721
3761
  // The permission mode can change at runtime, so the system message is
3722
3762
  // recomputed at the start of every turn instead of being frozen by the
@@ -3773,12 +3813,13 @@ ${summary}`
3773
3813
  }
3774
3814
  }
3775
3815
  async *streamOnce(aiTools, signal, timeoutScale = 1) {
3776
- const { streamChat } = await import("./stream-H4BYBVAZ.js");
3816
+ const { streamChat } = await import("./stream-EFQPULQQ.js");
3777
3817
  for await (const event of streamChat({
3778
3818
  model: this.opts.model,
3779
3819
  messages: this.messages,
3780
3820
  tools: aiTools,
3781
3821
  abortSignal: signal,
3822
+ providerMetadata: this.opts.providerMetadata,
3782
3823
  idleTimeoutMs: this.opts.config.streamIdleTimeoutSec * 1e3 * timeoutScale,
3783
3824
  firstPartTimeoutMs: this.opts.config.streamFirstChunkTimeoutSec * 1e3 * timeoutScale
3784
3825
  })) {
@@ -3952,4 +3993,4 @@ export {
3952
3993
  PLAN_MODE_PROMPT,
3953
3994
  AgentLoop
3954
3995
  };
3955
- //# sourceMappingURL=chunk-K3R42LOX.js.map
3996
+ //# sourceMappingURL=chunk-I6FDXAWX.js.map