@cryer/star-cli 0.3.7 → 0.3.9
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 +10 -2
- package/README.zh-CN.md +9 -2
- package/dist/chunk-4IRY44TG.js +34 -0
- package/dist/chunk-4IRY44TG.js.map +1 -0
- package/dist/chunk-56ZIA57D.js +493 -0
- package/dist/chunk-56ZIA57D.js.map +1 -0
- package/dist/chunk-A5PXPWHB.js +2014 -0
- package/dist/chunk-A5PXPWHB.js.map +1 -0
- package/dist/chunk-MVAMPVTA.js +232 -0
- package/dist/chunk-MVAMPVTA.js.map +1 -0
- package/dist/chunk-OIJYNQFZ.js +1163 -0
- package/dist/chunk-OIJYNQFZ.js.map +1 -0
- package/dist/chunk-RE3NOULF.js +3415 -0
- package/dist/chunk-RE3NOULF.js.map +1 -0
- package/dist/{chunk-4X25DNTF.js → chunk-VJFWTXWN.js} +337 -420
- package/dist/chunk-VJFWTXWN.js.map +1 -0
- package/dist/loop-PVOSF6LR.js +17 -0
- package/dist/main.js +257 -79
- package/dist/main.js.map +1 -1
- package/dist/provider-EWMUPBLH.js +11 -0
- package/dist/provider-EWMUPBLH.js.map +1 -0
- package/dist/{repl-RT6HBM5B.js → repl-OO7C6V7M.js} +444 -271
- package/dist/repl-OO7C6V7M.js.map +1 -0
- package/dist/tools-JDTACJYX.js +18 -0
- package/dist/tools-JDTACJYX.js.map +1 -0
- package/package.json +17 -26
- package/dist/chunk-4X25DNTF.js.map +0 -1
- package/dist/chunk-OOOW7FC4.js +0 -5354
- package/dist/chunk-OOOW7FC4.js.map +0 -1
- package/dist/loop-DVVPIOS7.js +0 -10
- package/dist/repl-RT6HBM5B.js.map +0 -1
- /package/dist/{loop-DVVPIOS7.js.map → loop-PVOSF6LR.js.map} +0 -0
package/dist/main.js
CHANGED
|
@@ -5,38 +5,87 @@ import {
|
|
|
5
5
|
SessionStore,
|
|
6
6
|
VERSION,
|
|
7
7
|
clearSessions,
|
|
8
|
-
createModel,
|
|
9
8
|
findLatestSession,
|
|
10
9
|
formatSessionEntries,
|
|
11
10
|
formatStreamError,
|
|
12
11
|
formatTokens,
|
|
13
12
|
imageMimeType,
|
|
14
13
|
listSessionEntries,
|
|
15
|
-
loadEnvFile,
|
|
16
14
|
loadSessionSnapshots,
|
|
17
15
|
readImageInput,
|
|
18
|
-
reasoningEffortMetadata,
|
|
19
16
|
resolveMentions,
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
} from "./chunk-
|
|
17
|
+
resolveSessionId,
|
|
18
|
+
summarizeArgs
|
|
19
|
+
} from "./chunk-VJFWTXWN.js";
|
|
23
20
|
import {
|
|
24
|
-
AgentLoop,
|
|
25
21
|
ConfigSchema,
|
|
22
|
+
HookConfigSchema,
|
|
23
|
+
ModelConfigSchema,
|
|
24
|
+
PermissionsConfigSchema,
|
|
25
|
+
ProviderConfigSchema,
|
|
26
26
|
contextWindowTokens,
|
|
27
|
-
|
|
27
|
+
reconcileStarMessages,
|
|
28
|
+
toCoreMessages
|
|
29
|
+
} from "./chunk-56ZIA57D.js";
|
|
30
|
+
import {
|
|
31
|
+
resolveModelConfig,
|
|
32
|
+
resolveStartupModel
|
|
33
|
+
} from "./chunk-4IRY44TG.js";
|
|
34
|
+
import {
|
|
28
35
|
defaultAgentTasks,
|
|
29
36
|
defaultTaskManager,
|
|
30
37
|
globalConfigPath,
|
|
31
38
|
hydrateSnapshots,
|
|
39
|
+
loadEnvFile,
|
|
32
40
|
loadTodos,
|
|
33
41
|
projectConfigPath,
|
|
34
|
-
|
|
35
|
-
|
|
42
|
+
registerSensitivePatterns,
|
|
43
|
+
setTodoSession,
|
|
44
|
+
setWebFetchAllowPrivateHosts
|
|
45
|
+
} from "./chunk-A5PXPWHB.js";
|
|
36
46
|
|
|
37
47
|
// src/main.tsx
|
|
38
48
|
import { Command } from "commander";
|
|
39
49
|
|
|
50
|
+
// src/cli/print-human.ts
|
|
51
|
+
function printEventLine(event) {
|
|
52
|
+
switch (event.type) {
|
|
53
|
+
case "text-delta":
|
|
54
|
+
return { stream: "stdout", text: event.text };
|
|
55
|
+
case "tool-call":
|
|
56
|
+
return { stream: "stderr", text: `
|
|
57
|
+
[tool] ${event.name} ${summarizeArgs(event.args)}
|
|
58
|
+
` };
|
|
59
|
+
case "tool-result": {
|
|
60
|
+
const preview = event.content.length > 500 ? `${event.content.slice(0, 500)}... (truncated)` : event.content;
|
|
61
|
+
return {
|
|
62
|
+
stream: "stderr",
|
|
63
|
+
text: `[result] ${event.isError ? "ERROR: " : ""}${preview}
|
|
64
|
+
`
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
case "retry": {
|
|
68
|
+
const wait = event.delayMs !== void 0 && event.delayMs >= 1e3 ? ` in ${Math.round(event.delayMs / 1e3)}s` : "";
|
|
69
|
+
return {
|
|
70
|
+
stream: "stderr",
|
|
71
|
+
text: `
|
|
72
|
+
[retry ${event.attempt}/${event.maxAttempts}${wait}] ${event.reason}
|
|
73
|
+
`
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
case "notice":
|
|
77
|
+
return { stream: "stderr", text: `
|
|
78
|
+
[notice] ${event.message}
|
|
79
|
+
` };
|
|
80
|
+
case "error":
|
|
81
|
+
return { stream: "stderr", text: `
|
|
82
|
+
[error] ${formatStreamError(event.error)}
|
|
83
|
+
` };
|
|
84
|
+
default:
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
40
89
|
// src/cli/print-json.ts
|
|
41
90
|
function eventToJsonLine(event) {
|
|
42
91
|
switch (event.type) {
|
|
@@ -80,11 +129,35 @@ var UsageTracker = class {
|
|
|
80
129
|
}
|
|
81
130
|
};
|
|
82
131
|
|
|
132
|
+
// src/cli/startup.ts
|
|
133
|
+
function imageRequiresPrintError(print, images) {
|
|
134
|
+
if (print !== void 0) return null;
|
|
135
|
+
return images.length > 0 ? "--image requires -p/--print" : null;
|
|
136
|
+
}
|
|
137
|
+
function interactiveRequiresTtyError(streams) {
|
|
138
|
+
return streams.stdinTTY && streams.stdoutTTY ? null : "interactive mode requires a TTY; use -p/--print";
|
|
139
|
+
}
|
|
140
|
+
function installStdoutEpipeGuard(stdout = process.stdout) {
|
|
141
|
+
stdout.on("error", (error) => {
|
|
142
|
+
if (error?.code === "EPIPE") process.exit(0);
|
|
143
|
+
else throw error;
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
async function reportResumeSessions(cwd) {
|
|
147
|
+
const entries = await listSessionEntries(cwd);
|
|
148
|
+
if (entries.length === 0) {
|
|
149
|
+
console.error("No sessions found for this directory.");
|
|
150
|
+
return 1;
|
|
151
|
+
}
|
|
152
|
+
console.log(formatSessionEntries(entries));
|
|
153
|
+
return 0;
|
|
154
|
+
}
|
|
155
|
+
|
|
83
156
|
// src/config/loader.ts
|
|
84
157
|
import fs from "fs";
|
|
85
158
|
import { parse } from "smol-toml";
|
|
86
159
|
var PartialConfigSchema = ConfigSchema.partial();
|
|
87
|
-
function parseTomlFile(content, filePath) {
|
|
160
|
+
function parseTomlFile(content, filePath, scope) {
|
|
88
161
|
let raw;
|
|
89
162
|
try {
|
|
90
163
|
raw = parse(content);
|
|
@@ -94,8 +167,14 @@ function parseTomlFile(content, filePath) {
|
|
|
94
167
|
}
|
|
95
168
|
const result = PartialConfigSchema.safeParse(raw);
|
|
96
169
|
if (!result.success) {
|
|
97
|
-
|
|
170
|
+
const details = result.error.issues.map((issue) => {
|
|
171
|
+
const where = issue.path.length > 0 ? issue.path.join(".") : "(root)";
|
|
172
|
+
return ` ${where}: ${issue.message}`;
|
|
173
|
+
}).join("\n");
|
|
174
|
+
throw new Error(`Invalid config in ${filePath}:
|
|
175
|
+
${details}`);
|
|
98
176
|
}
|
|
177
|
+
warnUnknownKeys(raw, filePath, scope);
|
|
99
178
|
return result.data;
|
|
100
179
|
}
|
|
101
180
|
var PROJECT_ALLOWED_KEYS = /* @__PURE__ */ new Set([
|
|
@@ -121,6 +200,73 @@ var PROJECT_STREAM_RANGES = {
|
|
|
121
200
|
streamMaxRetries: { min: 0, max: 10 }
|
|
122
201
|
};
|
|
123
202
|
var PROJECT_PROTECTIVE_MIN_KEYS = /* @__PURE__ */ new Set(["maxSteps", "doomLoopThreshold"]);
|
|
203
|
+
var TOP_LEVEL_KEYS = new Set(Object.keys(ConfigSchema.shape));
|
|
204
|
+
var SECTION_KEYS = {
|
|
205
|
+
permissions: { keys: new Set(Object.keys(PermissionsConfigSchema.shape)), array: false },
|
|
206
|
+
providers: { keys: new Set(Object.keys(ProviderConfigSchema.shape)), array: true },
|
|
207
|
+
models: { keys: new Set(Object.keys(ModelConfigSchema.shape)), array: true },
|
|
208
|
+
hooks: { keys: new Set(Object.keys(HookConfigSchema.shape)), array: true }
|
|
209
|
+
};
|
|
210
|
+
function editDistance(a, b) {
|
|
211
|
+
let previous = Array.from({ length: b.length + 1 }, (_, i) => i);
|
|
212
|
+
for (let i = 1; i <= a.length; i++) {
|
|
213
|
+
const current = [i];
|
|
214
|
+
for (let j = 1; j <= b.length; j++) {
|
|
215
|
+
current[j] = Math.min(
|
|
216
|
+
(previous[j] ?? 0) + 1,
|
|
217
|
+
(current[j - 1] ?? 0) + 1,
|
|
218
|
+
(previous[j - 1] ?? 0) + (a[i - 1] === b[j - 1] ? 0 : 1)
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
previous = current;
|
|
222
|
+
}
|
|
223
|
+
return previous[b.length] ?? 0;
|
|
224
|
+
}
|
|
225
|
+
function suggestKey(key, known) {
|
|
226
|
+
const needle = key.toLowerCase();
|
|
227
|
+
let best;
|
|
228
|
+
let bestDistance = Number.POSITIVE_INFINITY;
|
|
229
|
+
for (const candidate of known) {
|
|
230
|
+
const distance = editDistance(needle, candidate.toLowerCase());
|
|
231
|
+
if (distance <= Math.max(1, Math.floor(candidate.length / 4)) && distance < bestDistance) {
|
|
232
|
+
best = candidate;
|
|
233
|
+
bestDistance = distance;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return best;
|
|
237
|
+
}
|
|
238
|
+
function warnUnknownKey(keyPath, filePath, known) {
|
|
239
|
+
const leaf = keyPath.slice(keyPath.lastIndexOf(".") + 1);
|
|
240
|
+
const suggestion = suggestKey(leaf, known);
|
|
241
|
+
const hint = suggestion ? `possible typo of "${suggestion}"? the key is ignored` : "possible typo? the key is ignored";
|
|
242
|
+
process.stderr.write(`[star] unknown config key "${keyPath}" in ${filePath}: ${hint}
|
|
243
|
+
`);
|
|
244
|
+
}
|
|
245
|
+
function warnUnknownKeys(raw, filePath, scope) {
|
|
246
|
+
if (typeof raw !== "object" || raw === null) return;
|
|
247
|
+
const table = raw;
|
|
248
|
+
for (const key of Object.keys(table)) {
|
|
249
|
+
if (!TOP_LEVEL_KEYS.has(key)) warnUnknownKey(key, filePath, TOP_LEVEL_KEYS);
|
|
250
|
+
}
|
|
251
|
+
for (const [section, { keys, array }] of Object.entries(SECTION_KEYS)) {
|
|
252
|
+
if (scope === "project" && !PROJECT_ALLOWED_KEYS.has(section)) continue;
|
|
253
|
+
const value = table[section];
|
|
254
|
+
if (array) {
|
|
255
|
+
if (!Array.isArray(value)) continue;
|
|
256
|
+
value.forEach((entry, index) => {
|
|
257
|
+
if (typeof entry !== "object" || entry === null) return;
|
|
258
|
+
for (const key of Object.keys(entry)) {
|
|
259
|
+
if (!keys.has(key)) warnUnknownKey(`${section}[${index}].${key}`, filePath, keys);
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
if (typeof value !== "object" || value === null) continue;
|
|
265
|
+
for (const key of Object.keys(value)) {
|
|
266
|
+
if (!keys.has(key)) warnUnknownKey(`${section}.${key}`, filePath, keys);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
124
270
|
function warnProject(filePath, message) {
|
|
125
271
|
process.stderr.write(`[star] ${message} in ${filePath}
|
|
126
272
|
`);
|
|
@@ -181,19 +327,56 @@ function sanitizeProjectConfig(project, filePath) {
|
|
|
181
327
|
}
|
|
182
328
|
return sanitized;
|
|
183
329
|
}
|
|
184
|
-
function
|
|
330
|
+
function mergeModelsByName(globalModels, projectModels) {
|
|
331
|
+
const projectByName = new Map(projectModels.map((model) => [model.name, model]));
|
|
332
|
+
const merged = globalModels.map((model) => projectByName.get(model.name) ?? model);
|
|
333
|
+
const globalNames = new Set(globalModels.map((model) => model.name));
|
|
334
|
+
for (const model of projectModels) {
|
|
335
|
+
if (!globalNames.has(model.name)) merged.push(model);
|
|
336
|
+
}
|
|
337
|
+
return merged;
|
|
338
|
+
}
|
|
339
|
+
function protectiveMin(a, b) {
|
|
340
|
+
const result = Math.min(
|
|
341
|
+
a === 0 ? Number.POSITIVE_INFINITY : a,
|
|
342
|
+
b === 0 ? Number.POSITIVE_INFINITY : b
|
|
343
|
+
);
|
|
344
|
+
return result === Number.POSITIVE_INFINITY ? 0 : result;
|
|
345
|
+
}
|
|
346
|
+
function mergeProjectConfig(global, project, projectPath) {
|
|
185
347
|
const merged = { ...global, ...project };
|
|
348
|
+
if (project.models !== void 0) {
|
|
349
|
+
merged.models = mergeModelsByName(global.models ?? [], project.models);
|
|
350
|
+
warnProject(projectPath, `project config overrides ${project.models.length} model(s)`);
|
|
351
|
+
}
|
|
186
352
|
if (project.sessionBudgetUsd !== void 0 && global.sessionBudgetUsd !== void 0) {
|
|
187
353
|
merged.sessionBudgetUsd = Math.min(global.sessionBudgetUsd, project.sessionBudgetUsd);
|
|
188
354
|
}
|
|
189
355
|
if (project.maxSteps !== void 0) {
|
|
190
|
-
merged.maxSteps =
|
|
356
|
+
merged.maxSteps = protectiveMin(global.maxSteps ?? 100, project.maxSteps);
|
|
191
357
|
}
|
|
192
358
|
if (project.doomLoopThreshold !== void 0) {
|
|
193
|
-
merged.doomLoopThreshold =
|
|
359
|
+
merged.doomLoopThreshold = protectiveMin(
|
|
360
|
+
global.doomLoopThreshold ?? 3,
|
|
361
|
+
project.doomLoopThreshold
|
|
362
|
+
);
|
|
194
363
|
}
|
|
195
364
|
return merged;
|
|
196
365
|
}
|
|
366
|
+
function warnPlaintextApiKey(config) {
|
|
367
|
+
const offenders = [];
|
|
368
|
+
for (const provider of config.providers ?? []) {
|
|
369
|
+
if (provider.apiKey !== void 0) offenders.push(`provider "${provider.name}"`);
|
|
370
|
+
}
|
|
371
|
+
for (const model of config.models ?? []) {
|
|
372
|
+
if ("apiKey" in model && model.apiKey !== void 0) offenders.push(`model "${model.name}"`);
|
|
373
|
+
}
|
|
374
|
+
if (offenders.length === 0) return;
|
|
375
|
+
process.stderr.write(
|
|
376
|
+
`[star] plaintext apiKey in config (${offenders.join(", ")}) is deprecated: store keys in ~/.star-cli/.env and reference them with apiKeyEnv instead
|
|
377
|
+
`
|
|
378
|
+
);
|
|
379
|
+
}
|
|
197
380
|
function applyOverrides(config, overrides) {
|
|
198
381
|
if (!overrides) return config;
|
|
199
382
|
const merged = { ...config };
|
|
@@ -201,22 +384,29 @@ function applyOverrides(config, overrides) {
|
|
|
201
384
|
if (overrides.permissionMode !== void 0) merged.permissionMode = overrides.permissionMode;
|
|
202
385
|
return merged;
|
|
203
386
|
}
|
|
204
|
-
function readTomlFileSync(filePath) {
|
|
387
|
+
function readTomlFileSync(filePath, scope) {
|
|
205
388
|
if (!fs.existsSync(filePath)) return {};
|
|
206
389
|
const content = fs.readFileSync(filePath, "utf8");
|
|
207
|
-
return parseTomlFile(content, filePath);
|
|
390
|
+
return parseTomlFile(content, filePath, scope);
|
|
208
391
|
}
|
|
209
392
|
function loadConfigSync(cwd, overrides) {
|
|
210
393
|
loadEnvFile();
|
|
211
|
-
const global = readTomlFileSync(globalConfigPath());
|
|
394
|
+
const global = readTomlFileSync(globalConfigPath(), "global");
|
|
212
395
|
const projectPath = projectConfigPath(cwd);
|
|
213
|
-
const project = sanitizeProjectConfig(readTomlFileSync(projectPath), projectPath);
|
|
214
|
-
const merged = applyOverrides(mergeProjectConfig(global, project), overrides);
|
|
215
|
-
|
|
396
|
+
const project = sanitizeProjectConfig(readTomlFileSync(projectPath, "project"), projectPath);
|
|
397
|
+
const merged = applyOverrides(mergeProjectConfig(global, project, projectPath), overrides);
|
|
398
|
+
warnPlaintextApiKey(merged);
|
|
399
|
+
return wireRuntimeSettings(ConfigSchema.parse(merged));
|
|
400
|
+
}
|
|
401
|
+
function wireRuntimeSettings(config) {
|
|
402
|
+
registerSensitivePatterns(config.permissions.sensitive);
|
|
403
|
+
setWebFetchAllowPrivateHosts(config.webFetchAllowPrivateHosts);
|
|
404
|
+
return config;
|
|
216
405
|
}
|
|
217
406
|
|
|
218
407
|
// src/main.tsx
|
|
219
408
|
async function createLoop(config, modelName, cwd, sessionStore) {
|
|
409
|
+
const [{ AgentLoop }, { createModel, reasoningEffortMetadata }, { createDefaultRegistry }] = await Promise.all([import("./loop-PVOSF6LR.js"), import("./provider-EWMUPBLH.js"), import("./tools-JDTACJYX.js")]);
|
|
220
410
|
const model = createModel(config, modelName);
|
|
221
411
|
const registry = createDefaultRegistry();
|
|
222
412
|
const modelConfig = resolveModelConfig(config, modelName);
|
|
@@ -227,11 +417,13 @@ async function createLoop(config, modelName, cwd, sessionStore) {
|
|
|
227
417
|
cwd,
|
|
228
418
|
system: SYSTEM_PROMPT,
|
|
229
419
|
sessionStore,
|
|
420
|
+
modelName,
|
|
230
421
|
contextMaxTokens: contextWindowTokens(config, modelName),
|
|
231
422
|
providerMetadata: reasoningEffortMetadata(config, modelName),
|
|
232
423
|
temperature: modelConfig.temperature,
|
|
233
424
|
streamIdleTimeoutSec: modelConfig.streamIdleTimeoutSec,
|
|
234
|
-
streamFirstChunkTimeoutSec: modelConfig.streamFirstChunkTimeoutSec
|
|
425
|
+
streamFirstChunkTimeoutSec: modelConfig.streamFirstChunkTimeoutSec,
|
|
426
|
+
vision: modelConfig.vision
|
|
235
427
|
});
|
|
236
428
|
}
|
|
237
429
|
async function printMode(loop, prompt, cwd, json, imagePaths, sessionStore) {
|
|
@@ -281,26 +473,13 @@ async function printMode(loop, prompt, cwd, json, imagePaths, sessionStore) {
|
|
|
281
473
|
const line = eventToJsonLine(event);
|
|
282
474
|
if (line) process.stdout.write(`${line}
|
|
283
475
|
`);
|
|
476
|
+
} else {
|
|
477
|
+
const line = printEventLine(event);
|
|
478
|
+
if (line) {
|
|
479
|
+
(line.stream === "stdout" ? process.stdout : process.stderr).write(line.text);
|
|
480
|
+
}
|
|
284
481
|
}
|
|
285
482
|
switch (event.type) {
|
|
286
|
-
case "text-delta":
|
|
287
|
-
if (!json) process.stdout.write(event.text);
|
|
288
|
-
break;
|
|
289
|
-
case "reasoning":
|
|
290
|
-
break;
|
|
291
|
-
case "tool-call":
|
|
292
|
-
if (!json) process.stderr.write(`
|
|
293
|
-
[tool] ${event.name} ${JSON.stringify(event.args)}
|
|
294
|
-
`);
|
|
295
|
-
break;
|
|
296
|
-
case "tool-result": {
|
|
297
|
-
if (!json) {
|
|
298
|
-
const preview = event.content.length > 500 ? `${event.content.slice(0, 500)}... (truncated)` : event.content;
|
|
299
|
-
process.stderr.write(`[result] ${event.isError ? "ERROR: " : ""}${preview}
|
|
300
|
-
`);
|
|
301
|
-
}
|
|
302
|
-
break;
|
|
303
|
-
}
|
|
304
483
|
case "finish":
|
|
305
484
|
usage.add(event.usage);
|
|
306
485
|
if (event.usage) {
|
|
@@ -308,26 +487,7 @@ async function printMode(loop, prompt, cwd, json, imagePaths, sessionStore) {
|
|
|
308
487
|
});
|
|
309
488
|
}
|
|
310
489
|
break;
|
|
311
|
-
case "retry": {
|
|
312
|
-
if (!json) {
|
|
313
|
-
const wait = event.delayMs !== void 0 && event.delayMs >= 1e3 ? ` in ${Math.round(event.delayMs / 1e3)}s` : "";
|
|
314
|
-
process.stderr.write(
|
|
315
|
-
`
|
|
316
|
-
[retry ${event.attempt}/${event.maxAttempts}${wait}] ${event.reason}
|
|
317
|
-
`
|
|
318
|
-
);
|
|
319
|
-
}
|
|
320
|
-
break;
|
|
321
|
-
}
|
|
322
|
-
case "notice":
|
|
323
|
-
if (!json) process.stderr.write(`
|
|
324
|
-
[notice] ${event.message}
|
|
325
|
-
`);
|
|
326
|
-
break;
|
|
327
490
|
case "error":
|
|
328
|
-
if (!json) process.stderr.write(`
|
|
329
|
-
[error] ${formatStreamError(event.error)}
|
|
330
|
-
`);
|
|
331
491
|
exitCode = 1;
|
|
332
492
|
break;
|
|
333
493
|
}
|
|
@@ -377,6 +537,7 @@ program.name("star").description("Star CLI \u2014 an AI agent command-line inter
|
|
|
377
537
|
"--clear-sessions [scope]",
|
|
378
538
|
"delete stored sessions for the current directory ('all' deletes every session) and exit"
|
|
379
539
|
).action(async (opts) => {
|
|
540
|
+
installStdoutEpipeGuard();
|
|
380
541
|
const cwd = process.cwd();
|
|
381
542
|
if (opts.json && opts.print === void 0) {
|
|
382
543
|
console.error(
|
|
@@ -384,18 +545,17 @@ program.name("star").description("Star CLI \u2014 an AI agent command-line inter
|
|
|
384
545
|
);
|
|
385
546
|
process.exit(1);
|
|
386
547
|
}
|
|
548
|
+
const imageError = imageRequiresPrintError(opts.print, opts.image ?? []);
|
|
549
|
+
if (imageError) {
|
|
550
|
+
console.error(imageError);
|
|
551
|
+
process.exit(1);
|
|
552
|
+
}
|
|
387
553
|
if (opts.resume !== void 0 && opts.continue) {
|
|
388
554
|
console.error("Options --resume and --continue are mutually exclusive: pick one.");
|
|
389
555
|
process.exit(1);
|
|
390
556
|
}
|
|
391
557
|
if (opts.resume === true) {
|
|
392
|
-
|
|
393
|
-
if (entries.length === 0) {
|
|
394
|
-
console.error("No sessions found for this directory.");
|
|
395
|
-
} else {
|
|
396
|
-
console.log(formatSessionEntries(entries));
|
|
397
|
-
}
|
|
398
|
-
process.exit(0);
|
|
558
|
+
process.exit(await reportResumeSessions(cwd));
|
|
399
559
|
}
|
|
400
560
|
if (opts.clearSessions !== void 0) {
|
|
401
561
|
const scope = opts.clearSessions;
|
|
@@ -417,13 +577,6 @@ program.name("star").description("Star CLI \u2014 an AI agent command-line inter
|
|
|
417
577
|
console.error(error instanceof Error ? error.message : String(error));
|
|
418
578
|
process.exit(1);
|
|
419
579
|
}
|
|
420
|
-
const modelName = opts.model ?? config.defaultModel;
|
|
421
|
-
if (!modelName) {
|
|
422
|
-
console.error(
|
|
423
|
-
"No model configured. Add one to ~/.star-cli/config.toml or pass --model.\nSee README.md for configuration examples."
|
|
424
|
-
);
|
|
425
|
-
process.exit(1);
|
|
426
|
-
}
|
|
427
580
|
let sessionStore = null;
|
|
428
581
|
let resumed = null;
|
|
429
582
|
let resumeId = null;
|
|
@@ -447,14 +600,31 @@ program.name("star").description("Star CLI \u2014 an AI agent command-line inter
|
|
|
447
600
|
console.error(`Session not found: ${resumeId}`);
|
|
448
601
|
process.exit(1);
|
|
449
602
|
}
|
|
450
|
-
const [meta, loaded] = await Promise.all([sessionStore.meta(), sessionStore.
|
|
451
|
-
const messages =
|
|
603
|
+
const [meta, loaded] = await Promise.all([sessionStore.meta(), sessionStore.starMessages()]);
|
|
604
|
+
const messages = reconcileStarMessages(loaded);
|
|
452
605
|
if (messages.length !== loaded.length) {
|
|
453
606
|
await sessionStore.replaceMessages(messages);
|
|
454
607
|
}
|
|
455
608
|
resumed = { meta, messages };
|
|
456
|
-
|
|
609
|
+
setTodoSession(resumeId);
|
|
610
|
+
}
|
|
611
|
+
const { name: modelName, notice: modelNotice } = resolveStartupModel(
|
|
612
|
+
config,
|
|
613
|
+
opts.model ? void 0 : resumed?.meta.model
|
|
614
|
+
);
|
|
615
|
+
if (modelNotice) {
|
|
616
|
+
process.stderr.write(`${modelNotice}
|
|
617
|
+
`);
|
|
618
|
+
}
|
|
619
|
+
if (!modelName) {
|
|
620
|
+
console.error(
|
|
621
|
+
"No model configured. Add one to ~/.star-cli/config.toml or pass --model.\nSee README.md for configuration examples."
|
|
622
|
+
);
|
|
623
|
+
process.exit(1);
|
|
624
|
+
}
|
|
625
|
+
if (!resumeId && !opts.print) {
|
|
457
626
|
sessionStore = await SessionStore.create(cwd, modelName);
|
|
627
|
+
setTodoSession(sessionStore.id);
|
|
458
628
|
}
|
|
459
629
|
let loop;
|
|
460
630
|
try {
|
|
@@ -465,7 +635,7 @@ program.name("star").description("Star CLI \u2014 an AI agent command-line inter
|
|
|
465
635
|
}
|
|
466
636
|
if (resumed) {
|
|
467
637
|
await loop.loadMessages(resumed.messages);
|
|
468
|
-
await loadTodos(cwd);
|
|
638
|
+
await loadTodos(cwd, resumeId ?? void 0);
|
|
469
639
|
if (sessionStore) {
|
|
470
640
|
hydrateSnapshots(await loadSessionSnapshots(sessionStore.dir));
|
|
471
641
|
}
|
|
@@ -481,14 +651,22 @@ program.name("star").description("Star CLI \u2014 an AI agent command-line inter
|
|
|
481
651
|
);
|
|
482
652
|
return;
|
|
483
653
|
}
|
|
484
|
-
const
|
|
654
|
+
const ttyError = interactiveRequiresTtyError({
|
|
655
|
+
stdinTTY: Boolean(process.stdin.isTTY),
|
|
656
|
+
stdoutTTY: Boolean(process.stdout.isTTY)
|
|
657
|
+
});
|
|
658
|
+
if (ttyError) {
|
|
659
|
+
console.error(ttyError);
|
|
660
|
+
process.exit(1);
|
|
661
|
+
}
|
|
662
|
+
const { renderRepl } = await import("./repl-OO7C6V7M.js");
|
|
485
663
|
renderRepl(loop, {
|
|
486
664
|
model: modelName,
|
|
487
665
|
permissionMode: config.permissionMode,
|
|
488
666
|
config,
|
|
489
667
|
cwd,
|
|
490
668
|
sessionStore,
|
|
491
|
-
initialMessages: resumed
|
|
669
|
+
initialMessages: resumed ? toCoreMessages(resumed.messages) : void 0,
|
|
492
670
|
initialUsage: resumed?.meta.usage
|
|
493
671
|
});
|
|
494
672
|
});
|