atom-lite 0.6.2 → 0.7.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/CHANGELOG.md +23 -0
- package/README.md +27 -16
- package/dist/agent/loop.d.ts +10 -0
- package/dist/agent/loop.js +114 -1
- package/dist/agent/loop.js.map +1 -1
- package/dist/agent/turn-recorder.d.ts +4 -0
- package/dist/agent/turn-recorder.js +3 -0
- package/dist/agent/turn-recorder.js.map +1 -1
- package/dist/agent/types.d.ts +22 -0
- package/dist/cli/app.js +267 -45
- package/dist/cli/app.js.map +1 -1
- package/dist/cli/commands.js +183 -2
- package/dist/cli/commands.js.map +1 -1
- package/dist/cli/main.js +2 -1
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/tool-box.js +3 -2
- package/dist/cli/tool-box.js.map +1 -1
- package/dist/config/defaults.js +1 -1
- package/dist/config/load.js +2 -2
- package/dist/config/load.js.map +1 -1
- package/dist/config/schema.d.ts +4 -0
- package/dist/config/schema.js +5 -1
- package/dist/config/schema.js.map +1 -1
- package/dist/context/budget.js +19 -1
- package/dist/context/budget.js.map +1 -1
- package/dist/eval/runner.d.ts +51 -0
- package/dist/eval/runner.js +230 -0
- package/dist/eval/runner.js.map +1 -0
- package/dist/mcp/client.js +2 -1
- package/dist/mcp/client.js.map +1 -1
- package/dist/providers/context-window.d.ts +4 -0
- package/dist/providers/context-window.js +54 -0
- package/dist/providers/context-window.js.map +1 -1
- package/dist/providers/factory.js +3 -0
- package/dist/providers/factory.js.map +1 -1
- package/dist/providers/gemini.d.ts +2 -0
- package/dist/providers/gemini.js +18 -11
- package/dist/providers/gemini.js.map +1 -1
- package/dist/providers/ollama.d.ts +3 -0
- package/dist/providers/ollama.js +9 -0
- package/dist/providers/ollama.js.map +1 -1
- package/dist/providers/openai-compatible.d.ts +2 -0
- package/dist/providers/openai-compatible.js +24 -3
- package/dist/providers/openai-compatible.js.map +1 -1
- package/dist/runtime/background-tasks.d.ts +23 -0
- package/dist/runtime/background-tasks.js +141 -0
- package/dist/runtime/background-tasks.js.map +1 -0
- package/dist/runtime/checkpoint-store.d.ts +64 -0
- package/dist/runtime/checkpoint-store.js +419 -0
- package/dist/runtime/checkpoint-store.js.map +1 -0
- package/dist/sessions/events.js +4 -5
- package/dist/sessions/events.js.map +1 -1
- package/dist/sessions/store.js +0 -16
- package/dist/sessions/store.js.map +1 -1
- package/dist/tools/bash.js +45 -9
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/edit.js +28 -8
- package/dist/tools/edit.js.map +1 -1
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.js +1 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/read.js +70 -9
- package/dist/tools/read.js.map +1 -1
- package/dist/tools/task.d.ts +2 -0
- package/dist/tools/task.js +55 -0
- package/dist/tools/task.js.map +1 -0
- package/dist/tools/types.d.ts +8 -1
- package/dist/tools/write.js +29 -8
- package/dist/tools/write.js.map +1 -1
- package/dist/types.d.ts +11 -0
- package/dist/types.js.map +1 -1
- package/dist/version.d.ts +1 -0
- package/dist/version.js +14 -0
- package/dist/version.js.map +1 -0
- package/package.json +3 -2
- package/skills/code-review/SKILL.md +75 -32
- package/skills/code-review/agents/openai.yaml +3 -0
- package/skills/pr/CREDITS.md +3 -0
- package/skills/pr/SKILL.md +170 -0
- package/skills/pr/agents/openai.yaml +3 -0
package/dist/cli/app.js
CHANGED
|
@@ -9,14 +9,15 @@ import { execSync, spawn } from "node:child_process";
|
|
|
9
9
|
import { defaultShell, terminateProcessTree } from "../security/process.js";
|
|
10
10
|
import { openSearchablePicker } from "./picker.js";
|
|
11
11
|
import { openCommandPalette } from "./palette.js";
|
|
12
|
-
import { bareCommandName, createCommandRegistry } from "./commands.js";
|
|
12
|
+
import { bareCommandName, createCommandRegistry, } from "./commands.js";
|
|
13
13
|
import { resolveTheme, themeLabel, themeNames, markdownThemeFor, } from "./theme.js";
|
|
14
14
|
import { SecretInput } from "./secret-input.js";
|
|
15
15
|
import { StatusLine } from "./status-line.js";
|
|
16
16
|
import { Activity } from "./activity.js";
|
|
17
17
|
import { ToolBox } from "./tool-box.js";
|
|
18
|
+
import { ApprovalPrompt } from "./approval-prompt.js";
|
|
18
19
|
import { Banner } from "./banner.js";
|
|
19
|
-
import { formatContextUsage, knownContextWindow, } from "../providers/context-window.js";
|
|
20
|
+
import { formatContextUsage, knownContextWindow, knownVisionModel, } from "../providers/context-window.js";
|
|
20
21
|
import { StreamText } from "./stream-text.js";
|
|
21
22
|
import { Transcript } from "./transcript.js";
|
|
22
23
|
import { AgentRuntime } from "../agent/loop.js";
|
|
@@ -26,10 +27,10 @@ import { collectExtensionTools } from "../extensions/tool-adapter.js";
|
|
|
26
27
|
import { denyProject, trustProject } from "../extensions/trust.js";
|
|
27
28
|
import { ProjectTrustPrompt } from "./trust-prompt.js";
|
|
28
29
|
import { formatSessionList, formatSessionSearch, searchSessions, sessionTitleFromEvents, } from "../sessions/search.js";
|
|
29
|
-
import { providerSwitchNotice,
|
|
30
|
+
import { providerSwitchNotice, sessionSelectionFromEvents, } from "../sessions/history.js";
|
|
30
31
|
import { blockAtLine, createMouseCapture, handleMouseEvent, parseSgrMouse, shouldEnableMouse, } from "./mouse.js";
|
|
31
|
-
import { readTool, writeTool, editTool, bashTool, webfetchTool, websearchTool, grepTool, globTool } from "../tools/index.js";
|
|
32
|
-
import { McpManager, formatMcpStatus, formatMcpDoctor, createMcpTools } from "../mcp/index.js";
|
|
32
|
+
import { readTool, writeTool, editTool, bashTool, webfetchTool, websearchTool, grepTool, globTool, taskTool, } from "../tools/index.js";
|
|
33
|
+
import { McpManager, formatMcpStatus, formatMcpDoctor, createMcpTools, } from "../mcp/index.js";
|
|
33
34
|
import { createProvider } from "../providers/factory.js";
|
|
34
35
|
import { availableProviderNames, getPreset, listModelsForProvider, materializeProviderConfig, probeApiKey, providerLabel, PROVIDER_PRESETS, } from "../providers/catalog.js";
|
|
35
36
|
import { ContextManager } from "../context/manager.js";
|
|
@@ -90,7 +91,8 @@ export async function run(args) {
|
|
|
90
91
|
}
|
|
91
92
|
}
|
|
92
93
|
if (showVersion) {
|
|
93
|
-
|
|
94
|
+
const { VERSION } = await import("../version.js");
|
|
95
|
+
process.stdout.write(VERSION + "\n");
|
|
94
96
|
return;
|
|
95
97
|
}
|
|
96
98
|
if (listSessions || searchSessionsQuery !== undefined) {
|
|
@@ -125,12 +127,23 @@ export async function run(args) {
|
|
|
125
127
|
modelByProvider.set(provider.id, modelOverride);
|
|
126
128
|
const useTools = !positional.includes("__notools__");
|
|
127
129
|
const builtInTools = useTools
|
|
128
|
-
? [
|
|
130
|
+
? [
|
|
131
|
+
readTool,
|
|
132
|
+
writeTool,
|
|
133
|
+
editTool,
|
|
134
|
+
bashTool,
|
|
135
|
+
webfetchTool,
|
|
136
|
+
websearchTool,
|
|
137
|
+
grepTool,
|
|
138
|
+
globTool,
|
|
139
|
+
taskTool,
|
|
140
|
+
]
|
|
129
141
|
: [];
|
|
130
142
|
const interactive = process.stdin.isTTY === true && !jsonMode;
|
|
131
143
|
// one-shot task from positional or stdin
|
|
132
144
|
let stdinTask = "";
|
|
133
|
-
if (!process.stdin.isTTY &&
|
|
145
|
+
if (!process.stdin.isTTY &&
|
|
146
|
+
positional.filter((p) => !p.startsWith("__")).length === 0) {
|
|
134
147
|
const chunks = [];
|
|
135
148
|
for await (const chunk of process.stdin)
|
|
136
149
|
chunks.push(chunk);
|
|
@@ -140,9 +153,11 @@ export async function run(args) {
|
|
|
140
153
|
const oneShot = (taskArgs.join(" ").trim() || stdinTask).trim();
|
|
141
154
|
const store = new JsonlSessionStore();
|
|
142
155
|
let session = sessionOverride
|
|
143
|
-
? store.resume(sessionOverride) ??
|
|
156
|
+
? (store.resume(sessionOverride) ??
|
|
157
|
+
store.create(process.cwd(), provider.id))
|
|
144
158
|
: positional.includes("__continue__")
|
|
145
|
-
? store.resumeLatest(process.cwd()) ??
|
|
159
|
+
? (store.resumeLatest(process.cwd()) ??
|
|
160
|
+
store.create(process.cwd(), provider.id))
|
|
146
161
|
: store.create(process.cwd(), provider.id);
|
|
147
162
|
const mcpServers = (config.mcpServers ?? {});
|
|
148
163
|
const mcpManager = new McpManager({
|
|
@@ -165,7 +180,9 @@ export async function run(args) {
|
|
|
165
180
|
try {
|
|
166
181
|
const { execFileSync } = require("node:child_process");
|
|
167
182
|
const parts = value.slice(1).trim().split(/\s+/);
|
|
168
|
-
return execFileSync(parts[0], parts.slice(1), {
|
|
183
|
+
return execFileSync(parts[0], parts.slice(1), {
|
|
184
|
+
encoding: "utf8",
|
|
185
|
+
}).trim();
|
|
169
186
|
}
|
|
170
187
|
catch {
|
|
171
188
|
return undefined;
|
|
@@ -203,7 +220,7 @@ export async function run(args) {
|
|
|
203
220
|
// Bridge Pi-style extension providers into Atom-lite's config so they
|
|
204
221
|
// appear in /provider, /model, and the command palette.
|
|
205
222
|
for (const loaded of extensionLoader.list()) {
|
|
206
|
-
for (const [name, provider] of
|
|
223
|
+
for (const [name, provider] of loaded.registries?.providers ?? new Map()) {
|
|
207
224
|
const cfg = provider?.config ?? {};
|
|
208
225
|
const firstModel = Array.isArray(cfg.models) && cfg.models.length > 0
|
|
209
226
|
? (cfg.models[0].id ?? cfg.models[0].name ?? "default")
|
|
@@ -278,9 +295,14 @@ export async function run(args) {
|
|
|
278
295
|
contextManager,
|
|
279
296
|
extensionDispatch,
|
|
280
297
|
bashConcurrency: config.runtime.bashConcurrency,
|
|
298
|
+
checkpointingEnabled: interactive || Boolean(config.runtime.checkpointing),
|
|
281
299
|
});
|
|
282
300
|
mcpManager.onToolsChanged = () => {
|
|
283
|
-
runtime.setTools([
|
|
301
|
+
runtime.setTools([
|
|
302
|
+
...builtInTools,
|
|
303
|
+
...extensionTools,
|
|
304
|
+
...createMcpTools(mcpManager),
|
|
305
|
+
]);
|
|
284
306
|
};
|
|
285
307
|
if (oneShot || !interactive) {
|
|
286
308
|
// non-interactive / one-shot path
|
|
@@ -290,7 +312,11 @@ export async function run(args) {
|
|
|
290
312
|
try {
|
|
291
313
|
if (useTools) {
|
|
292
314
|
await mcpManager.syncTools();
|
|
293
|
-
runtime.setTools([
|
|
315
|
+
runtime.setTools([
|
|
316
|
+
...builtInTools,
|
|
317
|
+
...extensionTools,
|
|
318
|
+
...createMcpTools(mcpManager),
|
|
319
|
+
]);
|
|
294
320
|
}
|
|
295
321
|
const result = await runtime.runAgentTurn(oneShot || " ", session, controller.signal);
|
|
296
322
|
if (jsonMode) {
|
|
@@ -302,7 +328,9 @@ export async function run(args) {
|
|
|
302
328
|
}
|
|
303
329
|
catch (err) {
|
|
304
330
|
if (jsonMode)
|
|
305
|
-
process.stderr.write(JSON.stringify({
|
|
331
|
+
process.stderr.write(JSON.stringify({
|
|
332
|
+
error: err instanceof Error ? err.message : String(err),
|
|
333
|
+
}) + "\n");
|
|
306
334
|
else
|
|
307
335
|
process.stderr.write(`atom-lite: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
308
336
|
process.exit(1);
|
|
@@ -314,14 +342,16 @@ export async function run(args) {
|
|
|
314
342
|
return;
|
|
315
343
|
}
|
|
316
344
|
// ---- Interactive TUI (first-class) ----
|
|
317
|
-
const { TuiAltScreen, Text, Input, Editor, Container, Spacer, ScrollView, VStack, Loader, Markdown, Box } = await import("@earendil-works/pi-tui");
|
|
345
|
+
const { TuiAltScreen, Text, Input, Editor, Container, Spacer, ScrollView, VStack, Loader, Markdown, Box, visibleWidth, } = await import("@earendil-works/pi-tui");
|
|
318
346
|
const mouseEnabled = shouldEnableMouse({
|
|
319
347
|
interactive,
|
|
320
348
|
configured: config.ui.mouse,
|
|
321
349
|
stdoutIsTTY: process.stdout.isTTY === true,
|
|
322
350
|
});
|
|
323
351
|
const terminal = new ProcessTerminal();
|
|
324
|
-
const tui = new TuiAltScreen(terminal, false, undefined, {
|
|
352
|
+
const tui = new TuiAltScreen(terminal, false, undefined, {
|
|
353
|
+
mouse: mouseEnabled,
|
|
354
|
+
});
|
|
325
355
|
const mouseCapture = createMouseCapture((data) => terminal.write(data));
|
|
326
356
|
let effortLevel = "medium";
|
|
327
357
|
runtime.setEffort(effortLevel);
|
|
@@ -349,9 +379,17 @@ export async function run(args) {
|
|
|
349
379
|
displayPath = "~" + cwd.slice(home.length);
|
|
350
380
|
let branch = "";
|
|
351
381
|
try {
|
|
352
|
-
branch = execSync("git rev-parse --abbrev-ref HEAD", {
|
|
382
|
+
branch = execSync("git rev-parse --abbrev-ref HEAD", {
|
|
383
|
+
cwd,
|
|
384
|
+
encoding: "utf8",
|
|
385
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
386
|
+
}).trim();
|
|
353
387
|
if (branch === "HEAD") {
|
|
354
|
-
branch = execSync("git rev-parse --short HEAD", {
|
|
388
|
+
branch = execSync("git rev-parse --short HEAD", {
|
|
389
|
+
cwd,
|
|
390
|
+
encoding: "utf8",
|
|
391
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
392
|
+
}).trim();
|
|
355
393
|
}
|
|
356
394
|
}
|
|
357
395
|
catch {
|
|
@@ -401,7 +439,9 @@ export async function run(args) {
|
|
|
401
439
|
const sig = skillCacheSignature(process.cwd());
|
|
402
440
|
if (sig === registrySkillSignature)
|
|
403
441
|
return;
|
|
404
|
-
commandRegistry = createCommandRegistry({
|
|
442
|
+
commandRegistry = createCommandRegistry({
|
|
443
|
+
skills: cachedSkillRegistry().list(),
|
|
444
|
+
});
|
|
405
445
|
registrySkillSignature = sig;
|
|
406
446
|
input.setAutocompleteProvider(new CombinedAutocompleteProvider(commandRegistry.autocomplete(), process.cwd(), fdPath));
|
|
407
447
|
}
|
|
@@ -433,7 +473,9 @@ export async function run(args) {
|
|
|
433
473
|
input.autocompleteList) {
|
|
434
474
|
const selected = input.autocompleteList.getSelectedItem?.();
|
|
435
475
|
const name = selected?.value ?? selected?.name ?? selected?.label;
|
|
436
|
-
const cmd = name
|
|
476
|
+
const cmd = name
|
|
477
|
+
? commandRegistry.get(String(name).replace(/^\//, ""))
|
|
478
|
+
: undefined;
|
|
437
479
|
if (cmd?.bareInvokable) {
|
|
438
480
|
originalHandleInput("\t");
|
|
439
481
|
return;
|
|
@@ -445,18 +487,37 @@ export async function run(args) {
|
|
|
445
487
|
}
|
|
446
488
|
originalHandleInput(data);
|
|
447
489
|
};
|
|
490
|
+
const jumpToBottomLabel = "↓ bottom";
|
|
491
|
+
const jumpToBottomView = {
|
|
492
|
+
render(width) {
|
|
493
|
+
const label = output.isFollowingEnd
|
|
494
|
+
? theme.tokens.dim(jumpToBottomLabel)
|
|
495
|
+
: theme.tokens.accent(jumpToBottomLabel);
|
|
496
|
+
const pad = Math.max(0, width - visibleWidth(jumpToBottomLabel));
|
|
497
|
+
return [" ".repeat(pad) + label];
|
|
498
|
+
},
|
|
499
|
+
invalidate() { },
|
|
500
|
+
};
|
|
448
501
|
const root = new VStack([]);
|
|
449
|
-
// Keep fixed chrome (spacer, input, status, header, dropdown)
|
|
450
|
-
// collapsed when the transcript grows; only the output region may
|
|
502
|
+
// Keep fixed chrome (spacer, input, jump-to-bottom, status, header, dropdown)
|
|
503
|
+
// from being collapsed when the transcript grows; only the output region may
|
|
504
|
+
// shrink.
|
|
451
505
|
root.addChild(output, { grow: 1, shrink: 1, minSize: 0 });
|
|
452
506
|
root.addChild(new Spacer(1), { grow: 0, shrink: 0, minSize: 1 });
|
|
453
507
|
root.addChild(input, { grow: 0, shrink: 0 });
|
|
508
|
+
root.addChild(jumpToBottomView, { grow: 0, shrink: 0, minSize: 1 });
|
|
454
509
|
root.addChild(statusBar, { grow: 0, shrink: 0 });
|
|
455
510
|
// folder:branch info below the status line, dimmed
|
|
456
511
|
root.addChild(header, { grow: 0, shrink: 0 });
|
|
457
512
|
const dropdownContainer = new Container();
|
|
458
513
|
root.addChild(dropdownContainer, { grow: 0, shrink: 0 });
|
|
459
514
|
tui.setLayoutRoot(root);
|
|
515
|
+
function jumpToBottomRow() {
|
|
516
|
+
const width = terminal.columns;
|
|
517
|
+
const outputHeight = Math.max(1, output.viewportHeight || output.render(width).length);
|
|
518
|
+
const inputHeight = Math.max(1, input.render(width).length);
|
|
519
|
+
return outputHeight + 1 + inputHeight;
|
|
520
|
+
}
|
|
460
521
|
tui.setFocus(input);
|
|
461
522
|
if (!noColor) {
|
|
462
523
|
try {
|
|
@@ -595,7 +656,11 @@ export async function run(args) {
|
|
|
595
656
|
try {
|
|
596
657
|
if (useTools) {
|
|
597
658
|
await mcpManager.syncTools();
|
|
598
|
-
runtime.setTools([
|
|
659
|
+
runtime.setTools([
|
|
660
|
+
...builtInTools,
|
|
661
|
+
...extensionTools,
|
|
662
|
+
...createMcpTools(mcpManager),
|
|
663
|
+
]);
|
|
599
664
|
}
|
|
600
665
|
const turnResult = await runtime.runAgentTurn(prompt, session, controller.signal);
|
|
601
666
|
if (turnResult === "TOKEN_BUDGET_REACHED") {
|
|
@@ -746,11 +811,21 @@ export async function run(args) {
|
|
|
746
811
|
function refreshContextWindow() {
|
|
747
812
|
const profile = provider.id;
|
|
748
813
|
const model = currentModelLabel();
|
|
749
|
-
const
|
|
750
|
-
const
|
|
751
|
-
|
|
814
|
+
const pcfg = profileConfig(profile);
|
|
815
|
+
const configuredWindow = pcfg?.contextWindow;
|
|
816
|
+
const window = typeof configuredWindow === "number" && configuredWindow > 0
|
|
817
|
+
? configuredWindow
|
|
752
818
|
: knownContextWindow(model);
|
|
753
819
|
runtime.setContextWindow(window);
|
|
820
|
+
// Config override for vision takes precedence over provider metadata.
|
|
821
|
+
if (typeof pcfg?.supportsVision === "boolean") {
|
|
822
|
+
runtime.setSupportsVision(pcfg.supportsVision);
|
|
823
|
+
}
|
|
824
|
+
else {
|
|
825
|
+
const guessed = knownVisionModel(model);
|
|
826
|
+
if (typeof guessed === "boolean")
|
|
827
|
+
runtime.setSupportsVision(guessed);
|
|
828
|
+
}
|
|
754
829
|
updateStatus();
|
|
755
830
|
const getModelInfo = provider.getModelInfo;
|
|
756
831
|
if (typeof getModelInfo !== "function")
|
|
@@ -761,10 +836,16 @@ export async function run(args) {
|
|
|
761
836
|
if (provider.id !== profile || currentModelLabel() !== model)
|
|
762
837
|
return;
|
|
763
838
|
const resolved = info?.contextWindow;
|
|
764
|
-
if (typeof resolved === "number" &&
|
|
839
|
+
if (typeof resolved === "number" &&
|
|
840
|
+
resolved > 0 &&
|
|
841
|
+
typeof configuredWindow !== "number") {
|
|
765
842
|
runtime.setContextWindow(resolved);
|
|
766
843
|
updateStatus();
|
|
767
844
|
}
|
|
845
|
+
if (typeof pcfg?.supportsVision !== "boolean" &&
|
|
846
|
+
typeof info?.supportsVision === "boolean") {
|
|
847
|
+
runtime.setSupportsVision(info.supportsVision);
|
|
848
|
+
}
|
|
768
849
|
})
|
|
769
850
|
.catch(() => {
|
|
770
851
|
// Keep the static/config fallback.
|
|
@@ -775,7 +856,7 @@ export async function run(args) {
|
|
|
775
856
|
const MODEL_CACHE_TTL_MS = 5 * 60 * 1000;
|
|
776
857
|
const modelCache = new Map();
|
|
777
858
|
for (const loaded of extensionLoader.list()) {
|
|
778
|
-
for (const [name, provider] of
|
|
859
|
+
for (const [name, provider] of loaded.registries?.providers ?? new Map()) {
|
|
779
860
|
const cfg = provider?.config ?? {};
|
|
780
861
|
const models = Array.isArray(cfg.models)
|
|
781
862
|
? cfg.models.map((m) => m.id ?? m.name).filter(Boolean)
|
|
@@ -1011,7 +1092,11 @@ export async function run(args) {
|
|
|
1011
1092
|
provider = createProvider(config, preset.id, chosen);
|
|
1012
1093
|
runtime.switchModel(provider, chosen);
|
|
1013
1094
|
if (typeof session.appendEvent === "function") {
|
|
1014
|
-
session.appendEvent({
|
|
1095
|
+
session.appendEvent({
|
|
1096
|
+
type: "provider_selected",
|
|
1097
|
+
provider: preset.id,
|
|
1098
|
+
model: chosen,
|
|
1099
|
+
});
|
|
1015
1100
|
}
|
|
1016
1101
|
refreshContextWindow();
|
|
1017
1102
|
appendLine(`provider: ${preset.label} (${preset.id}) · model: ${chosen}`);
|
|
@@ -1262,7 +1347,9 @@ export async function run(args) {
|
|
|
1262
1347
|
return "No extensions discovered.";
|
|
1263
1348
|
return report
|
|
1264
1349
|
.map((ext) => {
|
|
1265
|
-
const origin = ext.origin
|
|
1350
|
+
const origin = ext.origin
|
|
1351
|
+
? `${ext.source}:${ext.origin}`
|
|
1352
|
+
: ext.source;
|
|
1266
1353
|
const version = ext.version ? `v${ext.version}` : "-";
|
|
1267
1354
|
const counts = `${ext.toolCount} tools / ${ext.commandCount} commands`;
|
|
1268
1355
|
const error = ext.error ? `\t${ext.error}` : "";
|
|
@@ -1280,7 +1367,9 @@ export async function run(args) {
|
|
|
1280
1367
|
return showThinking;
|
|
1281
1368
|
},
|
|
1282
1369
|
setTheme: setThemeTarget,
|
|
1283
|
-
listSkills: () => cachedSkillRegistry()
|
|
1370
|
+
listSkills: () => cachedSkillRegistry()
|
|
1371
|
+
.list()
|
|
1372
|
+
.map((skill) => ({
|
|
1284
1373
|
name: skill.name,
|
|
1285
1374
|
description: skill.description,
|
|
1286
1375
|
})),
|
|
@@ -1296,11 +1385,77 @@ export async function run(args) {
|
|
|
1296
1385
|
function restoreTranscript(next) {
|
|
1297
1386
|
streamText.finalize();
|
|
1298
1387
|
transcript.reset();
|
|
1388
|
+
// Drop stale references so toggles only affect the resumed transcript.
|
|
1389
|
+
toolBoxes.length = 0;
|
|
1299
1390
|
banner.setTheme(theme);
|
|
1300
1391
|
transcript.add(banner);
|
|
1301
1392
|
transcript.add(new Spacer(1));
|
|
1302
|
-
|
|
1303
|
-
|
|
1393
|
+
const messages = Array.isArray(next?.messages) ? next.messages : [];
|
|
1394
|
+
// Index tool results by their call id so each restored ToolBox can carry
|
|
1395
|
+
// its output and render as the same collapsible block used live.
|
|
1396
|
+
const resultsByCallId = new Map();
|
|
1397
|
+
for (const message of messages) {
|
|
1398
|
+
if (message?.role === "tool" && message.toolCallId) {
|
|
1399
|
+
resultsByCallId.set(String(message.toolCallId), message);
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
const renderedToolCallIds = new Set();
|
|
1403
|
+
const textOf = (content) => {
|
|
1404
|
+
if (typeof content === "string")
|
|
1405
|
+
return content;
|
|
1406
|
+
if (Array.isArray(content)) {
|
|
1407
|
+
return content
|
|
1408
|
+
.map((part) => part?.type === "text" ? String(part.text ?? "") : "")
|
|
1409
|
+
.join("");
|
|
1410
|
+
}
|
|
1411
|
+
return "";
|
|
1412
|
+
};
|
|
1413
|
+
for (const message of messages) {
|
|
1414
|
+
if (!message || typeof message !== "object")
|
|
1415
|
+
continue;
|
|
1416
|
+
if (message.role === "system")
|
|
1417
|
+
continue;
|
|
1418
|
+
if (message.role === "user") {
|
|
1419
|
+
const text = textOf(message.content).trim();
|
|
1420
|
+
if (text) {
|
|
1421
|
+
transcript.add(new Text(theme.tokens.user(`> ${text}`)));
|
|
1422
|
+
}
|
|
1423
|
+
continue;
|
|
1424
|
+
}
|
|
1425
|
+
if (message.role === "assistant") {
|
|
1426
|
+
const text = textOf(message.content).trim();
|
|
1427
|
+
if (text) {
|
|
1428
|
+
transcript.add(new Markdown(text, 0, 0, markdownThemeFor(theme)));
|
|
1429
|
+
}
|
|
1430
|
+
for (const call of message.toolCalls ?? []) {
|
|
1431
|
+
const box = new ToolBox({
|
|
1432
|
+
id: String(call?.id ?? ""),
|
|
1433
|
+
name: String(call?.name ?? ""),
|
|
1434
|
+
args: call?.arguments ?? {},
|
|
1435
|
+
});
|
|
1436
|
+
box.setExpanded(expandTools);
|
|
1437
|
+
const result = resultsByCallId.get(String(call?.id ?? ""));
|
|
1438
|
+
if (result) {
|
|
1439
|
+
box.setResult(String(result.content ?? ""), {
|
|
1440
|
+
isError: result.isError === true,
|
|
1441
|
+
diff: Array.isArray(result.diff) ? result.diff : undefined,
|
|
1442
|
+
});
|
|
1443
|
+
renderedToolCallIds.add(String(call?.id ?? ""));
|
|
1444
|
+
}
|
|
1445
|
+
transcript.add(box);
|
|
1446
|
+
toolBoxes.push(box);
|
|
1447
|
+
}
|
|
1448
|
+
continue;
|
|
1449
|
+
}
|
|
1450
|
+
if (message.role === "tool") {
|
|
1451
|
+
if (renderedToolCallIds.has(String(message.toolCallId ?? "")))
|
|
1452
|
+
continue;
|
|
1453
|
+
const text = String(message.content ?? "").trim();
|
|
1454
|
+
if (text) {
|
|
1455
|
+
transcript.add(new Text(theme.tokens.text(`[${message.name}] ${text}`)));
|
|
1456
|
+
}
|
|
1457
|
+
continue;
|
|
1458
|
+
}
|
|
1304
1459
|
}
|
|
1305
1460
|
tui.requestRender();
|
|
1306
1461
|
}
|
|
@@ -1347,9 +1502,7 @@ export async function run(args) {
|
|
|
1347
1502
|
const summaries = scope === "all" ? store.list() : store.list(process.cwd());
|
|
1348
1503
|
const toggle = {
|
|
1349
1504
|
value: "__scope__",
|
|
1350
|
-
label: scope === "all"
|
|
1351
|
-
? "Show this workspace only"
|
|
1352
|
-
: "Show all workspaces",
|
|
1505
|
+
label: scope === "all" ? "Show this workspace only" : "Show all workspaces",
|
|
1353
1506
|
description: `scope: ${scope === "all" ? "all workspaces" : process.cwd()}`,
|
|
1354
1507
|
};
|
|
1355
1508
|
return [
|
|
@@ -1373,7 +1526,9 @@ export async function run(args) {
|
|
|
1373
1526
|
if (item.value === "__scope__") {
|
|
1374
1527
|
scope = scope === "all" ? "workspace" : "all";
|
|
1375
1528
|
picker.updateItems(buildItems());
|
|
1376
|
-
picker.setStatus(scope === "all"
|
|
1529
|
+
picker.setStatus(scope === "all"
|
|
1530
|
+
? "showing all workspaces"
|
|
1531
|
+
: "showing this workspace");
|
|
1377
1532
|
return;
|
|
1378
1533
|
}
|
|
1379
1534
|
picker.close();
|
|
@@ -1530,7 +1685,8 @@ export async function run(args) {
|
|
|
1530
1685
|
}
|
|
1531
1686
|
extensionProviderNames.clear();
|
|
1532
1687
|
for (const loaded of extensionLoader.list()) {
|
|
1533
|
-
for (const [name, provider] of
|
|
1688
|
+
for (const [name, provider] of loaded.registries?.providers ??
|
|
1689
|
+
new Map()) {
|
|
1534
1690
|
const cfg = provider?.config ?? {};
|
|
1535
1691
|
const firstModel = Array.isArray(cfg.models) && cfg.models.length > 0
|
|
1536
1692
|
? (cfg.models[0].id ?? cfg.models[0].name ?? "default")
|
|
@@ -1631,6 +1787,13 @@ export async function run(args) {
|
|
|
1631
1787
|
return undefined;
|
|
1632
1788
|
if (!mouseEnabled)
|
|
1633
1789
|
return { consume: true };
|
|
1790
|
+
if (event.type === "press" &&
|
|
1791
|
+
event.button === "left" &&
|
|
1792
|
+
event.y === jumpToBottomRow()) {
|
|
1793
|
+
output.scrollToEnd();
|
|
1794
|
+
tui.requestRender();
|
|
1795
|
+
return { consume: true };
|
|
1796
|
+
}
|
|
1634
1797
|
handleMouseEvent(event, {
|
|
1635
1798
|
isModalOpen: () => uiModalOpen,
|
|
1636
1799
|
scrollBy: (lines) => output.scrollBy(lines),
|
|
@@ -1715,6 +1878,22 @@ export async function run(args) {
|
|
|
1715
1878
|
else if (e.type === "context_usage") {
|
|
1716
1879
|
updateStatus();
|
|
1717
1880
|
}
|
|
1881
|
+
else if (e.type === "background_task_complete") {
|
|
1882
|
+
appendLine(`background task ${e.id}: ${e.status} (exit ${e.exitCode ?? "null"})`, theme.tokens.dim);
|
|
1883
|
+
tui.requestRender();
|
|
1884
|
+
}
|
|
1885
|
+
else if (e.type === "checkpoint_created") {
|
|
1886
|
+
appendLine(`checkpoint ${e.id} created`, theme.tokens.dim);
|
|
1887
|
+
tui.requestRender();
|
|
1888
|
+
}
|
|
1889
|
+
else if (e.type === "checkpoint_failed") {
|
|
1890
|
+
appendLine(`checkpoint failed: ${e.reason}`, theme.tokens.warning);
|
|
1891
|
+
tui.requestRender();
|
|
1892
|
+
}
|
|
1893
|
+
else if (e.type === "checkpoint_unavailable") {
|
|
1894
|
+
appendLine(`checkpoint unavailable: ${e.reason ?? "not a git repository"}`, theme.tokens.warning);
|
|
1895
|
+
tui.requestRender();
|
|
1896
|
+
}
|
|
1718
1897
|
else if (e.type === "budget_warning") {
|
|
1719
1898
|
appendLine(`budget warning (${e.scope}): ${e.used}/${e.limit} tokens`, theme.tokens.warning);
|
|
1720
1899
|
tui.requestRender();
|
|
@@ -1722,11 +1901,20 @@ export async function run(args) {
|
|
|
1722
1901
|
else if (e.type === "model_switched") {
|
|
1723
1902
|
appendLine(`switched model to ${e.provider}/${e.model}`);
|
|
1724
1903
|
if (typeof session.appendEvent === "function") {
|
|
1725
|
-
session.appendEvent({
|
|
1904
|
+
session.appendEvent({
|
|
1905
|
+
type: "model_switched",
|
|
1906
|
+
provider: e.provider,
|
|
1907
|
+
model: e.model,
|
|
1908
|
+
});
|
|
1726
1909
|
}
|
|
1727
1910
|
updateStatus();
|
|
1728
1911
|
}
|
|
1729
1912
|
else if (e.type === "tool_call") {
|
|
1913
|
+
if (thinkingText) {
|
|
1914
|
+
transcript.invalidate();
|
|
1915
|
+
thinkingText = null;
|
|
1916
|
+
thinkingBuffer = "";
|
|
1917
|
+
}
|
|
1730
1918
|
finalizeText();
|
|
1731
1919
|
activity.setPhase("working", e.name);
|
|
1732
1920
|
const box = new ToolBox({ id: e.id, name: e.name, args: e.args });
|
|
@@ -1754,6 +1942,11 @@ export async function run(args) {
|
|
|
1754
1942
|
}
|
|
1755
1943
|
}
|
|
1756
1944
|
else if (e.type === "final") {
|
|
1945
|
+
if (thinkingText) {
|
|
1946
|
+
transcript.invalidate();
|
|
1947
|
+
thinkingText = null;
|
|
1948
|
+
thinkingBuffer = "";
|
|
1949
|
+
}
|
|
1757
1950
|
finalizeText();
|
|
1758
1951
|
activity.stop();
|
|
1759
1952
|
}
|
|
@@ -1768,7 +1961,11 @@ export async function run(args) {
|
|
|
1768
1961
|
lines.push(` config: FAIL ${e instanceof Error ? e.message : String(e)}`);
|
|
1769
1962
|
}
|
|
1770
1963
|
try {
|
|
1771
|
-
const r = await provider.generate({
|
|
1964
|
+
const r = await provider.generate({
|
|
1965
|
+
model: modelOverride ?? "default",
|
|
1966
|
+
messages: [{ role: "user", content: [{ type: "text", text: "ping" }] }],
|
|
1967
|
+
tools: [],
|
|
1968
|
+
});
|
|
1772
1969
|
lines.push(` provider generate: ok (${r.finishReason})`);
|
|
1773
1970
|
}
|
|
1774
1971
|
catch (e) {
|
|
@@ -1793,7 +1990,11 @@ export async function run(args) {
|
|
|
1793
1990
|
}
|
|
1794
1991
|
if (useTools) {
|
|
1795
1992
|
await mcpManager.syncTools();
|
|
1796
|
-
runtime.setTools([
|
|
1993
|
+
runtime.setTools([
|
|
1994
|
+
...builtInTools,
|
|
1995
|
+
...extensionTools,
|
|
1996
|
+
...createMcpTools(mcpManager),
|
|
1997
|
+
]);
|
|
1797
1998
|
}
|
|
1798
1999
|
appendLine(formatMcpStatus(mcpManager.status()));
|
|
1799
2000
|
return;
|
|
@@ -1901,7 +2102,9 @@ export async function run(args) {
|
|
|
1901
2102
|
for (const message of session.messages) {
|
|
1902
2103
|
if (message.role === "user") {
|
|
1903
2104
|
turn++;
|
|
1904
|
-
const snippet = redactText(messageText(message))
|
|
2105
|
+
const snippet = redactText(messageText(message))
|
|
2106
|
+
.split("\\n")[0]
|
|
2107
|
+
.slice(0, 80);
|
|
1905
2108
|
lines.push(`${turn}. ${snippet || "(empty)"}`);
|
|
1906
2109
|
}
|
|
1907
2110
|
}
|
|
@@ -2030,8 +2233,27 @@ export async function run(args) {
|
|
|
2030
2233
|
appendLine(`> ${trimmed}`, theme.tokens.user);
|
|
2031
2234
|
await runTurn(trimmed);
|
|
2032
2235
|
};
|
|
2033
|
-
|
|
2034
|
-
|
|
2236
|
+
approvals.handler = interactive
|
|
2237
|
+
? (req) => new Promise((resolve) => {
|
|
2238
|
+
uiModalOpen = true;
|
|
2239
|
+
closeOverlay();
|
|
2240
|
+
dropdownContainer.clear();
|
|
2241
|
+
activity.setPhase("waiting");
|
|
2242
|
+
const prompt = new ApprovalPrompt(req, (decision) => {
|
|
2243
|
+
uiModalOpen = false;
|
|
2244
|
+
dropdownContainer.clear();
|
|
2245
|
+
tui.setFocus(input);
|
|
2246
|
+
activity.setPhase("working");
|
|
2247
|
+
if (decision === "trust-session") {
|
|
2248
|
+
appendLine("trusted this session: outside-workspace operations will run without approval", theme.tokens.dim);
|
|
2249
|
+
}
|
|
2250
|
+
resolve(decision);
|
|
2251
|
+
});
|
|
2252
|
+
dropdownContainer.addChild(prompt);
|
|
2253
|
+
tui.setFocus(prompt);
|
|
2254
|
+
tui.requestRender();
|
|
2255
|
+
})
|
|
2256
|
+
: undefined;
|
|
2035
2257
|
process.on("SIGTERM", () => {
|
|
2036
2258
|
void mcpManager.close().finally(() => {
|
|
2037
2259
|
tui.stop();
|