indusagi-coding-agent 0.1.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 +2249 -0
- package/README.md +546 -0
- package/dist/cli/args.js +282 -0
- package/dist/cli/config-selector.js +30 -0
- package/dist/cli/file-processor.js +78 -0
- package/dist/cli/list-models.js +91 -0
- package/dist/cli/session-picker.js +31 -0
- package/dist/cli.js +10 -0
- package/dist/config.js +158 -0
- package/dist/core/agent-session.js +2097 -0
- package/dist/core/auth-storage.js +278 -0
- package/dist/core/bash-executor.js +211 -0
- package/dist/core/compaction/branch-summarization.js +241 -0
- package/dist/core/compaction/compaction.js +606 -0
- package/dist/core/compaction/index.js +6 -0
- package/dist/core/compaction/utils.js +137 -0
- package/dist/core/diagnostics.js +1 -0
- package/dist/core/event-bus.js +24 -0
- package/dist/core/exec.js +70 -0
- package/dist/core/export-html/ansi-to-html.js +248 -0
- package/dist/core/export-html/index.js +221 -0
- package/dist/core/export-html/template.css +905 -0
- package/dist/core/export-html/template.html +54 -0
- package/dist/core/export-html/template.js +1549 -0
- package/dist/core/export-html/tool-renderer.js +56 -0
- package/dist/core/export-html/vendor/highlight.min.js +1213 -0
- package/dist/core/export-html/vendor/marked.min.js +6 -0
- package/dist/core/extensions/index.js +8 -0
- package/dist/core/extensions/loader.js +395 -0
- package/dist/core/extensions/runner.js +499 -0
- package/dist/core/extensions/types.js +31 -0
- package/dist/core/extensions/wrapper.js +101 -0
- package/dist/core/footer-data-provider.js +133 -0
- package/dist/core/index.js +8 -0
- package/dist/core/keybindings.js +140 -0
- package/dist/core/messages.js +122 -0
- package/dist/core/model-registry.js +454 -0
- package/dist/core/model-resolver.js +309 -0
- package/dist/core/package-manager.js +1142 -0
- package/dist/core/prompt-templates.js +250 -0
- package/dist/core/resource-loader.js +569 -0
- package/dist/core/sdk.js +225 -0
- package/dist/core/session-manager.js +1078 -0
- package/dist/core/settings-manager.js +430 -0
- package/dist/core/skills.js +339 -0
- package/dist/core/system-prompt.js +136 -0
- package/dist/core/timings.js +24 -0
- package/dist/core/tools/bash.js +226 -0
- package/dist/core/tools/edit-diff.js +242 -0
- package/dist/core/tools/edit.js +145 -0
- package/dist/core/tools/find.js +205 -0
- package/dist/core/tools/grep.js +238 -0
- package/dist/core/tools/index.js +60 -0
- package/dist/core/tools/ls.js +117 -0
- package/dist/core/tools/path-utils.js +52 -0
- package/dist/core/tools/read.js +165 -0
- package/dist/core/tools/truncate.js +204 -0
- package/dist/core/tools/write.js +77 -0
- package/dist/index.js +41 -0
- package/dist/main.js +565 -0
- package/dist/migrations.js +260 -0
- package/dist/modes/index.js +7 -0
- package/dist/modes/interactive/components/armin.js +328 -0
- package/dist/modes/interactive/components/assistant-message.js +86 -0
- package/dist/modes/interactive/components/bash-execution.js +155 -0
- package/dist/modes/interactive/components/bordered-loader.js +47 -0
- package/dist/modes/interactive/components/branch-summary-message.js +41 -0
- package/dist/modes/interactive/components/compaction-summary-message.js +42 -0
- package/dist/modes/interactive/components/config-selector.js +458 -0
- package/dist/modes/interactive/components/countdown-timer.js +27 -0
- package/dist/modes/interactive/components/custom-editor.js +61 -0
- package/dist/modes/interactive/components/custom-message.js +80 -0
- package/dist/modes/interactive/components/diff.js +132 -0
- package/dist/modes/interactive/components/dynamic-border.js +19 -0
- package/dist/modes/interactive/components/extension-editor.js +96 -0
- package/dist/modes/interactive/components/extension-input.js +54 -0
- package/dist/modes/interactive/components/extension-selector.js +70 -0
- package/dist/modes/interactive/components/footer.js +213 -0
- package/dist/modes/interactive/components/index.js +31 -0
- package/dist/modes/interactive/components/keybinding-hints.js +60 -0
- package/dist/modes/interactive/components/login-dialog.js +138 -0
- package/dist/modes/interactive/components/model-selector.js +253 -0
- package/dist/modes/interactive/components/oauth-selector.js +91 -0
- package/dist/modes/interactive/components/scoped-models-selector.js +262 -0
- package/dist/modes/interactive/components/session-selector-search.js +145 -0
- package/dist/modes/interactive/components/session-selector.js +698 -0
- package/dist/modes/interactive/components/settings-selector.js +250 -0
- package/dist/modes/interactive/components/show-images-selector.js +33 -0
- package/dist/modes/interactive/components/skill-invocation-message.js +44 -0
- package/dist/modes/interactive/components/theme-selector.js +43 -0
- package/dist/modes/interactive/components/thinking-selector.js +45 -0
- package/dist/modes/interactive/components/tool-execution.js +608 -0
- package/dist/modes/interactive/components/tree-selector.js +892 -0
- package/dist/modes/interactive/components/user-message-selector.js +109 -0
- package/dist/modes/interactive/components/user-message.js +15 -0
- package/dist/modes/interactive/components/visual-truncate.js +32 -0
- package/dist/modes/interactive/interactive-mode.js +3576 -0
- package/dist/modes/interactive/theme/dark.json +85 -0
- package/dist/modes/interactive/theme/light.json +84 -0
- package/dist/modes/interactive/theme/theme-schema.json +335 -0
- package/dist/modes/interactive/theme/theme.js +938 -0
- package/dist/modes/print-mode.js +96 -0
- package/dist/modes/rpc/rpc-client.js +390 -0
- package/dist/modes/rpc/rpc-mode.js +448 -0
- package/dist/modes/rpc/rpc-types.js +7 -0
- package/dist/utils/changelog.js +86 -0
- package/dist/utils/clipboard-image.js +116 -0
- package/dist/utils/clipboard.js +58 -0
- package/dist/utils/frontmatter.js +25 -0
- package/dist/utils/git.js +5 -0
- package/dist/utils/image-convert.js +34 -0
- package/dist/utils/image-resize.js +180 -0
- package/dist/utils/mime.js +25 -0
- package/dist/utils/photon.js +120 -0
- package/dist/utils/shell.js +164 -0
- package/dist/utils/sleep.js +16 -0
- package/dist/utils/tools-manager.js +186 -0
- package/docs/compaction.md +390 -0
- package/docs/custom-provider.md +538 -0
- package/docs/development.md +69 -0
- package/docs/extensions.md +1733 -0
- package/docs/images/doom-extension.png +0 -0
- package/docs/images/interactive-mode.png +0 -0
- package/docs/images/tree-view.png +0 -0
- package/docs/json.md +79 -0
- package/docs/keybindings.md +162 -0
- package/docs/models.md +193 -0
- package/docs/packages.md +163 -0
- package/docs/prompt-templates.md +67 -0
- package/docs/providers.md +147 -0
- package/docs/rpc.md +1048 -0
- package/docs/sdk.md +957 -0
- package/docs/session.md +412 -0
- package/docs/settings.md +216 -0
- package/docs/shell-aliases.md +13 -0
- package/docs/skills.md +226 -0
- package/docs/terminal-setup.md +65 -0
- package/docs/themes.md +295 -0
- package/docs/tree.md +219 -0
- package/docs/tui.md +887 -0
- package/docs/windows.md +17 -0
- package/examples/README.md +25 -0
- package/examples/extensions/README.md +192 -0
- package/examples/extensions/antigravity-image-gen.ts +414 -0
- package/examples/extensions/auto-commit-on-exit.ts +49 -0
- package/examples/extensions/bookmark.ts +50 -0
- package/examples/extensions/claude-rules.ts +86 -0
- package/examples/extensions/confirm-destructive.ts +59 -0
- package/examples/extensions/custom-compaction.ts +115 -0
- package/examples/extensions/custom-footer.ts +65 -0
- package/examples/extensions/custom-header.ts +73 -0
- package/examples/extensions/custom-provider-anthropic/index.ts +605 -0
- package/examples/extensions/custom-provider-anthropic/package-lock.json +24 -0
- package/examples/extensions/custom-provider-anthropic/package.json +19 -0
- package/examples/extensions/custom-provider-gitlab-duo/index.ts +350 -0
- package/examples/extensions/custom-provider-gitlab-duo/package.json +16 -0
- package/examples/extensions/custom-provider-gitlab-duo/test.ts +83 -0
- package/examples/extensions/dirty-repo-guard.ts +56 -0
- package/examples/extensions/doom-overlay/README.md +46 -0
- package/examples/extensions/doom-overlay/doom/build/doom.js +21 -0
- package/examples/extensions/doom-overlay/doom/build/doom.wasm +0 -0
- package/examples/extensions/doom-overlay/doom/build.sh +152 -0
- package/examples/extensions/doom-overlay/doom/doomgeneric_pi.c +72 -0
- package/examples/extensions/doom-overlay/doom-component.ts +133 -0
- package/examples/extensions/doom-overlay/doom-engine.ts +173 -0
- package/examples/extensions/doom-overlay/doom-keys.ts +105 -0
- package/examples/extensions/doom-overlay/index.ts +74 -0
- package/examples/extensions/doom-overlay/wad-finder.ts +51 -0
- package/examples/extensions/event-bus.ts +43 -0
- package/examples/extensions/file-trigger.ts +41 -0
- package/examples/extensions/git-checkpoint.ts +53 -0
- package/examples/extensions/handoff.ts +151 -0
- package/examples/extensions/hello.ts +25 -0
- package/examples/extensions/inline-bash.ts +94 -0
- package/examples/extensions/input-transform.ts +43 -0
- package/examples/extensions/interactive-shell.ts +196 -0
- package/examples/extensions/mac-system-theme.ts +47 -0
- package/examples/extensions/message-renderer.ts +60 -0
- package/examples/extensions/modal-editor.ts +86 -0
- package/examples/extensions/model-status.ts +31 -0
- package/examples/extensions/notify.ts +25 -0
- package/examples/extensions/overlay-qa-tests.ts +882 -0
- package/examples/extensions/overlay-test.ts +151 -0
- package/examples/extensions/permission-gate.ts +34 -0
- package/examples/extensions/pirate.ts +47 -0
- package/examples/extensions/plan-mode/README.md +65 -0
- package/examples/extensions/plan-mode/index.ts +341 -0
- package/examples/extensions/plan-mode/utils.ts +168 -0
- package/examples/extensions/preset.ts +399 -0
- package/examples/extensions/protected-paths.ts +30 -0
- package/examples/extensions/qna.ts +120 -0
- package/examples/extensions/question.ts +265 -0
- package/examples/extensions/questionnaire.ts +428 -0
- package/examples/extensions/rainbow-editor.ts +88 -0
- package/examples/extensions/sandbox/index.ts +318 -0
- package/examples/extensions/sandbox/package-lock.json +92 -0
- package/examples/extensions/sandbox/package.json +19 -0
- package/examples/extensions/send-user-message.ts +97 -0
- package/examples/extensions/session-name.ts +27 -0
- package/examples/extensions/shutdown-command.ts +63 -0
- package/examples/extensions/snake.ts +344 -0
- package/examples/extensions/space-invaders.ts +561 -0
- package/examples/extensions/ssh.ts +220 -0
- package/examples/extensions/status-line.ts +40 -0
- package/examples/extensions/subagent/README.md +172 -0
- package/examples/extensions/subagent/agents/planner.md +37 -0
- package/examples/extensions/subagent/agents/reviewer.md +35 -0
- package/examples/extensions/subagent/agents/scout.md +50 -0
- package/examples/extensions/subagent/agents/worker.md +24 -0
- package/examples/extensions/subagent/agents.ts +127 -0
- package/examples/extensions/subagent/index.ts +964 -0
- package/examples/extensions/subagent/prompts/implement-and-review.md +10 -0
- package/examples/extensions/subagent/prompts/implement.md +10 -0
- package/examples/extensions/subagent/prompts/scout-and-plan.md +9 -0
- package/examples/extensions/summarize.ts +196 -0
- package/examples/extensions/timed-confirm.ts +70 -0
- package/examples/extensions/todo.ts +300 -0
- package/examples/extensions/tool-override.ts +144 -0
- package/examples/extensions/tools.ts +147 -0
- package/examples/extensions/trigger-compact.ts +40 -0
- package/examples/extensions/truncated-tool.ts +193 -0
- package/examples/extensions/widget-placement.ts +17 -0
- package/examples/extensions/with-deps/index.ts +36 -0
- package/examples/extensions/with-deps/package-lock.json +31 -0
- package/examples/extensions/with-deps/package.json +22 -0
- package/examples/sdk/01-minimal.ts +22 -0
- package/examples/sdk/02-custom-model.ts +50 -0
- package/examples/sdk/03-custom-prompt.ts +55 -0
- package/examples/sdk/04-skills.ts +46 -0
- package/examples/sdk/05-tools.ts +56 -0
- package/examples/sdk/06-extensions.ts +88 -0
- package/examples/sdk/07-context-files.ts +40 -0
- package/examples/sdk/08-prompt-templates.ts +47 -0
- package/examples/sdk/09-api-keys-and-oauth.ts +48 -0
- package/examples/sdk/10-settings.ts +38 -0
- package/examples/sdk/11-sessions.ts +48 -0
- package/examples/sdk/12-full-control.ts +82 -0
- package/examples/sdk/13-codex-oauth.ts +37 -0
- package/examples/sdk/README.md +144 -0
- package/package.json +85 -0
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One-time migrations that run on startup.
|
|
3
|
+
*/
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "fs";
|
|
6
|
+
import { dirname, join } from "path";
|
|
7
|
+
import { CONFIG_DIR_NAME, getAgentDir, getBinDir } from "./config.js";
|
|
8
|
+
const MIGRATION_GUIDE_URL = "https://github.com/badlogic/indusagi-mono/blob/main/packages/coding-agent/CHANGELOG.md#extensions-migration";
|
|
9
|
+
const EXTENSIONS_DOC_URL = "https://github.com/badlogic/indusagi-mono/blob/main/packages/coding-agent/docs/extensions.md";
|
|
10
|
+
/**
|
|
11
|
+
* Migrate legacy oauth.json and settings.json apiKeys to auth.json.
|
|
12
|
+
*
|
|
13
|
+
* @returns Array of provider names that were migrated
|
|
14
|
+
*/
|
|
15
|
+
export function migrateAuthToAuthJson() {
|
|
16
|
+
const agentDir = getAgentDir();
|
|
17
|
+
const authPath = join(agentDir, "auth.json");
|
|
18
|
+
const oauthPath = join(agentDir, "oauth.json");
|
|
19
|
+
const settingsPath = join(agentDir, "settings.json");
|
|
20
|
+
// Skip if auth.json already exists
|
|
21
|
+
if (existsSync(authPath))
|
|
22
|
+
return [];
|
|
23
|
+
const migrated = {};
|
|
24
|
+
const providers = [];
|
|
25
|
+
// Migrate oauth.json
|
|
26
|
+
if (existsSync(oauthPath)) {
|
|
27
|
+
try {
|
|
28
|
+
const oauth = JSON.parse(readFileSync(oauthPath, "utf-8"));
|
|
29
|
+
for (const [provider, cred] of Object.entries(oauth)) {
|
|
30
|
+
migrated[provider] = { type: "oauth", ...cred };
|
|
31
|
+
providers.push(provider);
|
|
32
|
+
}
|
|
33
|
+
renameSync(oauthPath, `${oauthPath}.migrated`);
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// Skip on error
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
// Migrate settings.json apiKeys
|
|
40
|
+
if (existsSync(settingsPath)) {
|
|
41
|
+
try {
|
|
42
|
+
const content = readFileSync(settingsPath, "utf-8");
|
|
43
|
+
const settings = JSON.parse(content);
|
|
44
|
+
if (settings.apiKeys && typeof settings.apiKeys === "object") {
|
|
45
|
+
for (const [provider, key] of Object.entries(settings.apiKeys)) {
|
|
46
|
+
if (!migrated[provider] && typeof key === "string") {
|
|
47
|
+
migrated[provider] = { type: "api_key", key };
|
|
48
|
+
providers.push(provider);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
delete settings.apiKeys;
|
|
52
|
+
writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
// Skip on error
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (Object.keys(migrated).length > 0) {
|
|
60
|
+
mkdirSync(dirname(authPath), { recursive: true });
|
|
61
|
+
writeFileSync(authPath, JSON.stringify(migrated, null, 2), { mode: 0o600 });
|
|
62
|
+
}
|
|
63
|
+
return providers;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Migrate sessions from ~/.indusagi/agent/*.jsonl to proper session directories.
|
|
67
|
+
*
|
|
68
|
+
* Bug in v0.30.0: Sessions were saved to ~/.indusagi/agent/ instead of
|
|
69
|
+
* ~/.indusagi/agent/sessions/<encoded-cwd>/. This migration moves them
|
|
70
|
+
* to the correct location based on the cwd in their session header.
|
|
71
|
+
*
|
|
72
|
+
* See: https://github.com/badlogic/indusagi-mono/issues/320
|
|
73
|
+
*/
|
|
74
|
+
export function migrateSessionsFromAgentRoot() {
|
|
75
|
+
const agentDir = getAgentDir();
|
|
76
|
+
// Find all .jsonl files directly in agentDir (not in subdirectories)
|
|
77
|
+
let files;
|
|
78
|
+
try {
|
|
79
|
+
files = readdirSync(agentDir)
|
|
80
|
+
.filter((f) => f.endsWith(".jsonl"))
|
|
81
|
+
.map((f) => join(agentDir, f));
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (files.length === 0)
|
|
87
|
+
return;
|
|
88
|
+
for (const file of files) {
|
|
89
|
+
try {
|
|
90
|
+
// Read first line to get session header
|
|
91
|
+
const content = readFileSync(file, "utf8");
|
|
92
|
+
const firstLine = content.split("\n")[0];
|
|
93
|
+
if (!firstLine?.trim())
|
|
94
|
+
continue;
|
|
95
|
+
const header = JSON.parse(firstLine);
|
|
96
|
+
if (header.type !== "session" || !header.cwd)
|
|
97
|
+
continue;
|
|
98
|
+
const cwd = header.cwd;
|
|
99
|
+
// Compute the correct session directory (same encoding as session-manager.ts)
|
|
100
|
+
const safePath = `--${cwd.replace(/^[/\\]/, "").replace(/[/\\:]/g, "-")}--`;
|
|
101
|
+
const correctDir = join(agentDir, "sessions", safePath);
|
|
102
|
+
// Create directory if needed
|
|
103
|
+
if (!existsSync(correctDir)) {
|
|
104
|
+
mkdirSync(correctDir, { recursive: true });
|
|
105
|
+
}
|
|
106
|
+
// Move the file
|
|
107
|
+
const fileName = file.split("/").pop() || file.split("\\").pop();
|
|
108
|
+
const newPath = join(correctDir, fileName);
|
|
109
|
+
if (existsSync(newPath))
|
|
110
|
+
continue; // Skip if target exists
|
|
111
|
+
renameSync(file, newPath);
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
// Skip files that can't be migrated
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Migrate commands/ to prompts/ if needed.
|
|
120
|
+
* Works for both regular directories and symlinks.
|
|
121
|
+
*/
|
|
122
|
+
function migrateCommandsToPrompts(baseDir, label) {
|
|
123
|
+
const commandsDir = join(baseDir, "commands");
|
|
124
|
+
const promptsDir = join(baseDir, "prompts");
|
|
125
|
+
if (existsSync(commandsDir) && !existsSync(promptsDir)) {
|
|
126
|
+
try {
|
|
127
|
+
renameSync(commandsDir, promptsDir);
|
|
128
|
+
console.log(chalk.green(`Migrated ${label} commands/ → prompts/`));
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
catch (err) {
|
|
132
|
+
console.log(chalk.yellow(`Warning: Could not migrate ${label} commands/ to prompts/: ${err instanceof Error ? err.message : err}`));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Move fd/rg binaries from tools/ to bin/ if they exist.
|
|
139
|
+
*/
|
|
140
|
+
function migrateToolsToBin() {
|
|
141
|
+
const agentDir = getAgentDir();
|
|
142
|
+
const toolsDir = join(agentDir, "tools");
|
|
143
|
+
const binDir = getBinDir();
|
|
144
|
+
if (!existsSync(toolsDir))
|
|
145
|
+
return;
|
|
146
|
+
const binaries = ["fd", "rg", "fd.exe", "rg.exe"];
|
|
147
|
+
let movedAny = false;
|
|
148
|
+
for (const bin of binaries) {
|
|
149
|
+
const oldPath = join(toolsDir, bin);
|
|
150
|
+
const newPath = join(binDir, bin);
|
|
151
|
+
if (existsSync(oldPath)) {
|
|
152
|
+
if (!existsSync(binDir)) {
|
|
153
|
+
mkdirSync(binDir, { recursive: true });
|
|
154
|
+
}
|
|
155
|
+
if (!existsSync(newPath)) {
|
|
156
|
+
try {
|
|
157
|
+
renameSync(oldPath, newPath);
|
|
158
|
+
movedAny = true;
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
// Ignore errors
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
// Target exists, just delete the old one
|
|
166
|
+
try {
|
|
167
|
+
rmSync?.(oldPath, { force: true });
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
// Ignore
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
if (movedAny) {
|
|
176
|
+
console.log(chalk.green(`Migrated managed binaries tools/ → bin/`));
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Check for deprecated hooks/ and tools/ directories.
|
|
181
|
+
* Note: tools/ may contain fd/rg binaries extracted by indusagi, so only warn if it has other files.
|
|
182
|
+
*/
|
|
183
|
+
function checkDeprecatedExtensionDirs(baseDir, label) {
|
|
184
|
+
const hooksDir = join(baseDir, "hooks");
|
|
185
|
+
const toolsDir = join(baseDir, "tools");
|
|
186
|
+
const warnings = [];
|
|
187
|
+
if (existsSync(hooksDir)) {
|
|
188
|
+
warnings.push(`${label} hooks/ directory found. Hooks have been renamed to extensions.`);
|
|
189
|
+
}
|
|
190
|
+
if (existsSync(toolsDir)) {
|
|
191
|
+
// Check if tools/ contains anything other than fd/rg (which are auto-extracted binaries)
|
|
192
|
+
try {
|
|
193
|
+
const entries = readdirSync(toolsDir);
|
|
194
|
+
const customTools = entries.filter((e) => {
|
|
195
|
+
const lower = e.toLowerCase();
|
|
196
|
+
return (lower !== "fd" && lower !== "rg" && lower !== "fd.exe" && lower !== "rg.exe" && !e.startsWith(".") // Ignore .DS_Store and other hidden files
|
|
197
|
+
);
|
|
198
|
+
});
|
|
199
|
+
if (customTools.length > 0) {
|
|
200
|
+
warnings.push(`${label} tools/ directory contains custom tools. Custom tools have been merged into extensions.`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
// Ignore read errors
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return warnings;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Run extension system migrations (commands→prompts) and collect warnings about deprecated directories.
|
|
211
|
+
*/
|
|
212
|
+
function migrateExtensionSystem(cwd) {
|
|
213
|
+
const agentDir = getAgentDir();
|
|
214
|
+
const projectDir = join(cwd, CONFIG_DIR_NAME);
|
|
215
|
+
// Migrate commands/ to prompts/
|
|
216
|
+
migrateCommandsToPrompts(agentDir, "Global");
|
|
217
|
+
migrateCommandsToPrompts(projectDir, "Project");
|
|
218
|
+
// Check for deprecated directories
|
|
219
|
+
const warnings = [
|
|
220
|
+
...checkDeprecatedExtensionDirs(agentDir, "Global"),
|
|
221
|
+
...checkDeprecatedExtensionDirs(projectDir, "Project"),
|
|
222
|
+
];
|
|
223
|
+
return warnings;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Print deprecation warnings and wait for keypress.
|
|
227
|
+
*/
|
|
228
|
+
export async function showDeprecationWarnings(warnings) {
|
|
229
|
+
if (warnings.length === 0)
|
|
230
|
+
return;
|
|
231
|
+
for (const warning of warnings) {
|
|
232
|
+
console.log(chalk.yellow(`Warning: ${warning}`));
|
|
233
|
+
}
|
|
234
|
+
console.log(chalk.yellow(`\nMove your extensions to the extensions/ directory.`));
|
|
235
|
+
console.log(chalk.yellow(`Migration guide: ${MIGRATION_GUIDE_URL}`));
|
|
236
|
+
console.log(chalk.yellow(`Documentation: ${EXTENSIONS_DOC_URL}`));
|
|
237
|
+
console.log(chalk.dim(`\nPress any key to continue...`));
|
|
238
|
+
await new Promise((resolve) => {
|
|
239
|
+
process.stdin.setRawMode?.(true);
|
|
240
|
+
process.stdin.resume();
|
|
241
|
+
process.stdin.once("data", () => {
|
|
242
|
+
process.stdin.setRawMode?.(false);
|
|
243
|
+
process.stdin.pause();
|
|
244
|
+
resolve();
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
console.log();
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Run all migrations. Called once on startup.
|
|
251
|
+
*
|
|
252
|
+
* @returns Object with migration results and deprecation warnings
|
|
253
|
+
*/
|
|
254
|
+
export function runMigrations(cwd = process.cwd()) {
|
|
255
|
+
const migratedAuthProviders = migrateAuthToAuthJson();
|
|
256
|
+
migrateSessionsFromAgentRoot();
|
|
257
|
+
migrateToolsToBin();
|
|
258
|
+
const deprecationWarnings = migrateExtensionSystem(cwd);
|
|
259
|
+
return { migratedAuthProviders, deprecationWarnings };
|
|
260
|
+
}
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Armin says hi! A fun easter egg with animated XBM art.
|
|
3
|
+
*/
|
|
4
|
+
import { theme } from "../theme/theme.js";
|
|
5
|
+
// XBM image: 31x36 pixels, LSB first, 1=background, 0=foreground
|
|
6
|
+
const WIDTH = 31;
|
|
7
|
+
const HEIGHT = 36;
|
|
8
|
+
const BITS = [
|
|
9
|
+
0xff, 0xff, 0xff, 0x7f, 0xff, 0xf0, 0xff, 0x7f, 0xff, 0xed, 0xff, 0x7f, 0xff, 0xdb, 0xff, 0x7f, 0xff, 0xb7, 0xff,
|
|
10
|
+
0x7f, 0xff, 0x77, 0xfe, 0x7f, 0x3f, 0xf8, 0xfe, 0x7f, 0xdf, 0xff, 0xfe, 0x7f, 0xdf, 0x3f, 0xfc, 0x7f, 0x9f, 0xc3,
|
|
11
|
+
0xfb, 0x7f, 0x6f, 0xfc, 0xf4, 0x7f, 0xf7, 0x0f, 0xf7, 0x7f, 0xf7, 0xff, 0xf7, 0x7f, 0xf7, 0xff, 0xe3, 0x7f, 0xf7,
|
|
12
|
+
0x07, 0xe8, 0x7f, 0xef, 0xf8, 0x67, 0x70, 0x0f, 0xff, 0xbb, 0x6f, 0xf1, 0x00, 0xd0, 0x5b, 0xfd, 0x3f, 0xec, 0x53,
|
|
13
|
+
0xc1, 0xff, 0xef, 0x57, 0x9f, 0xfd, 0xee, 0x5f, 0x9f, 0xfc, 0xae, 0x5f, 0x1f, 0x78, 0xac, 0x5f, 0x3f, 0x00, 0x50,
|
|
14
|
+
0x6c, 0x7f, 0x00, 0xdc, 0x77, 0xff, 0xc0, 0x3f, 0x78, 0xff, 0x01, 0xf8, 0x7f, 0xff, 0x03, 0x9c, 0x78, 0xff, 0x07,
|
|
15
|
+
0x8c, 0x7c, 0xff, 0x0f, 0xce, 0x78, 0xff, 0xff, 0xcf, 0x7f, 0xff, 0xff, 0xcf, 0x78, 0xff, 0xff, 0xdf, 0x78, 0xff,
|
|
16
|
+
0xff, 0xdf, 0x7d, 0xff, 0xff, 0x3f, 0x7e, 0xff, 0xff, 0xff, 0x7f,
|
|
17
|
+
];
|
|
18
|
+
const BYTES_PER_ROW = Math.ceil(WIDTH / 8);
|
|
19
|
+
const DISPLAY_HEIGHT = Math.ceil(HEIGHT / 2); // Half-block rendering
|
|
20
|
+
const EFFECTS = ["typewriter", "scanline", "rain", "fade", "crt", "glitch", "dissolve"];
|
|
21
|
+
// Get pixel at (x, y): true = foreground, false = background
|
|
22
|
+
function getPixel(x, y) {
|
|
23
|
+
if (y >= HEIGHT)
|
|
24
|
+
return false;
|
|
25
|
+
const byteIndex = y * BYTES_PER_ROW + Math.floor(x / 8);
|
|
26
|
+
const bitIndex = x % 8;
|
|
27
|
+
return ((BITS[byteIndex] >> bitIndex) & 1) === 0;
|
|
28
|
+
}
|
|
29
|
+
// Get the character for a cell (2 vertical pixels packed)
|
|
30
|
+
function getChar(x, row) {
|
|
31
|
+
const upper = getPixel(x, row * 2);
|
|
32
|
+
const lower = getPixel(x, row * 2 + 1);
|
|
33
|
+
if (upper && lower)
|
|
34
|
+
return "█";
|
|
35
|
+
if (upper)
|
|
36
|
+
return "▀";
|
|
37
|
+
if (lower)
|
|
38
|
+
return "▄";
|
|
39
|
+
return " ";
|
|
40
|
+
}
|
|
41
|
+
// Build the final image grid
|
|
42
|
+
function buildFinalGrid() {
|
|
43
|
+
const grid = [];
|
|
44
|
+
for (let row = 0; row < DISPLAY_HEIGHT; row++) {
|
|
45
|
+
const line = [];
|
|
46
|
+
for (let x = 0; x < WIDTH; x++) {
|
|
47
|
+
line.push(getChar(x, row));
|
|
48
|
+
}
|
|
49
|
+
grid.push(line);
|
|
50
|
+
}
|
|
51
|
+
return grid;
|
|
52
|
+
}
|
|
53
|
+
export class ArminComponent {
|
|
54
|
+
constructor(ui) {
|
|
55
|
+
this.interval = null;
|
|
56
|
+
this.effectState = {};
|
|
57
|
+
this.cachedLines = [];
|
|
58
|
+
this.cachedWidth = 0;
|
|
59
|
+
this.gridVersion = 0;
|
|
60
|
+
this.cachedVersion = -1;
|
|
61
|
+
this.ui = ui;
|
|
62
|
+
this.effect = EFFECTS[Math.floor(Math.random() * EFFECTS.length)];
|
|
63
|
+
this.finalGrid = buildFinalGrid();
|
|
64
|
+
this.currentGrid = this.createEmptyGrid();
|
|
65
|
+
this.initEffect();
|
|
66
|
+
this.startAnimation();
|
|
67
|
+
}
|
|
68
|
+
invalidate() {
|
|
69
|
+
this.cachedWidth = 0;
|
|
70
|
+
}
|
|
71
|
+
render(width) {
|
|
72
|
+
if (width === this.cachedWidth && this.cachedVersion === this.gridVersion) {
|
|
73
|
+
return this.cachedLines;
|
|
74
|
+
}
|
|
75
|
+
const padding = 1;
|
|
76
|
+
const availableWidth = width - padding;
|
|
77
|
+
this.cachedLines = this.currentGrid.map((row) => {
|
|
78
|
+
// Clip row to available width before applying color
|
|
79
|
+
const clipped = row.slice(0, availableWidth).join("");
|
|
80
|
+
const padRight = Math.max(0, width - padding - clipped.length);
|
|
81
|
+
return ` ${theme.fg("accent", clipped)}${" ".repeat(padRight)}`;
|
|
82
|
+
});
|
|
83
|
+
// Add "ARMIN SAYS HI" at the end
|
|
84
|
+
const message = "ARMIN SAYS HI";
|
|
85
|
+
const msgPadRight = Math.max(0, width - padding - message.length);
|
|
86
|
+
this.cachedLines.push(` ${theme.fg("accent", message)}${" ".repeat(msgPadRight)}`);
|
|
87
|
+
this.cachedWidth = width;
|
|
88
|
+
this.cachedVersion = this.gridVersion;
|
|
89
|
+
return this.cachedLines;
|
|
90
|
+
}
|
|
91
|
+
createEmptyGrid() {
|
|
92
|
+
return Array.from({ length: DISPLAY_HEIGHT }, () => Array(WIDTH).fill(" "));
|
|
93
|
+
}
|
|
94
|
+
initEffect() {
|
|
95
|
+
switch (this.effect) {
|
|
96
|
+
case "typewriter":
|
|
97
|
+
this.effectState = { pos: 0 };
|
|
98
|
+
break;
|
|
99
|
+
case "scanline":
|
|
100
|
+
this.effectState = { row: 0 };
|
|
101
|
+
break;
|
|
102
|
+
case "rain":
|
|
103
|
+
// Track falling position for each column
|
|
104
|
+
this.effectState = {
|
|
105
|
+
drops: Array.from({ length: WIDTH }, () => ({
|
|
106
|
+
y: -Math.floor(Math.random() * DISPLAY_HEIGHT * 2),
|
|
107
|
+
settled: 0,
|
|
108
|
+
})),
|
|
109
|
+
};
|
|
110
|
+
break;
|
|
111
|
+
case "fade": {
|
|
112
|
+
// Shuffle all pixel positions
|
|
113
|
+
const positions = [];
|
|
114
|
+
for (let row = 0; row < DISPLAY_HEIGHT; row++) {
|
|
115
|
+
for (let x = 0; x < WIDTH; x++) {
|
|
116
|
+
positions.push([row, x]);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// Fisher-Yates shuffle
|
|
120
|
+
for (let i = positions.length - 1; i > 0; i--) {
|
|
121
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
122
|
+
[positions[i], positions[j]] = [positions[j], positions[i]];
|
|
123
|
+
}
|
|
124
|
+
this.effectState = { positions, idx: 0 };
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
case "crt":
|
|
128
|
+
this.effectState = { expansion: 0 };
|
|
129
|
+
break;
|
|
130
|
+
case "glitch":
|
|
131
|
+
this.effectState = { phase: 0, glitchFrames: 8 };
|
|
132
|
+
break;
|
|
133
|
+
case "dissolve": {
|
|
134
|
+
// Start with random noise
|
|
135
|
+
this.currentGrid = Array.from({ length: DISPLAY_HEIGHT }, () => Array.from({ length: WIDTH }, () => {
|
|
136
|
+
const chars = [" ", "░", "▒", "▓", "█", "▀", "▄"];
|
|
137
|
+
return chars[Math.floor(Math.random() * chars.length)];
|
|
138
|
+
}));
|
|
139
|
+
// Shuffle positions for gradual resolve
|
|
140
|
+
const dissolvePositions = [];
|
|
141
|
+
for (let row = 0; row < DISPLAY_HEIGHT; row++) {
|
|
142
|
+
for (let x = 0; x < WIDTH; x++) {
|
|
143
|
+
dissolvePositions.push([row, x]);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
for (let i = dissolvePositions.length - 1; i > 0; i--) {
|
|
147
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
148
|
+
[dissolvePositions[i], dissolvePositions[j]] = [dissolvePositions[j], dissolvePositions[i]];
|
|
149
|
+
}
|
|
150
|
+
this.effectState = { positions: dissolvePositions, idx: 0 };
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
startAnimation() {
|
|
156
|
+
const fps = this.effect === "glitch" ? 60 : 30;
|
|
157
|
+
this.interval = setInterval(() => {
|
|
158
|
+
const done = this.tickEffect();
|
|
159
|
+
this.updateDisplay();
|
|
160
|
+
this.ui.requestRender();
|
|
161
|
+
if (done) {
|
|
162
|
+
this.stopAnimation();
|
|
163
|
+
}
|
|
164
|
+
}, 1000 / fps);
|
|
165
|
+
}
|
|
166
|
+
stopAnimation() {
|
|
167
|
+
if (this.interval) {
|
|
168
|
+
clearInterval(this.interval);
|
|
169
|
+
this.interval = null;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
tickEffect() {
|
|
173
|
+
switch (this.effect) {
|
|
174
|
+
case "typewriter":
|
|
175
|
+
return this.tickTypewriter();
|
|
176
|
+
case "scanline":
|
|
177
|
+
return this.tickScanline();
|
|
178
|
+
case "rain":
|
|
179
|
+
return this.tickRain();
|
|
180
|
+
case "fade":
|
|
181
|
+
return this.tickFade();
|
|
182
|
+
case "crt":
|
|
183
|
+
return this.tickCrt();
|
|
184
|
+
case "glitch":
|
|
185
|
+
return this.tickGlitch();
|
|
186
|
+
case "dissolve":
|
|
187
|
+
return this.tickDissolve();
|
|
188
|
+
default:
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
tickTypewriter() {
|
|
193
|
+
const state = this.effectState;
|
|
194
|
+
const pixelsPerFrame = 3;
|
|
195
|
+
for (let i = 0; i < pixelsPerFrame; i++) {
|
|
196
|
+
const row = Math.floor(state.pos / WIDTH);
|
|
197
|
+
const x = state.pos % WIDTH;
|
|
198
|
+
if (row >= DISPLAY_HEIGHT)
|
|
199
|
+
return true;
|
|
200
|
+
this.currentGrid[row][x] = this.finalGrid[row][x];
|
|
201
|
+
state.pos++;
|
|
202
|
+
}
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
tickScanline() {
|
|
206
|
+
const state = this.effectState;
|
|
207
|
+
if (state.row >= DISPLAY_HEIGHT)
|
|
208
|
+
return true;
|
|
209
|
+
// Copy row
|
|
210
|
+
for (let x = 0; x < WIDTH; x++) {
|
|
211
|
+
this.currentGrid[state.row][x] = this.finalGrid[state.row][x];
|
|
212
|
+
}
|
|
213
|
+
state.row++;
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
tickRain() {
|
|
217
|
+
const state = this.effectState;
|
|
218
|
+
let allSettled = true;
|
|
219
|
+
this.currentGrid = this.createEmptyGrid();
|
|
220
|
+
for (let x = 0; x < WIDTH; x++) {
|
|
221
|
+
const drop = state.drops[x];
|
|
222
|
+
// Draw settled pixels
|
|
223
|
+
for (let row = DISPLAY_HEIGHT - 1; row >= DISPLAY_HEIGHT - drop.settled; row--) {
|
|
224
|
+
if (row >= 0) {
|
|
225
|
+
this.currentGrid[row][x] = this.finalGrid[row][x];
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
// Check if this column is done
|
|
229
|
+
if (drop.settled >= DISPLAY_HEIGHT)
|
|
230
|
+
continue;
|
|
231
|
+
allSettled = false;
|
|
232
|
+
// Find the target row for this column (lowest non-space pixel)
|
|
233
|
+
let targetRow = -1;
|
|
234
|
+
for (let row = DISPLAY_HEIGHT - 1 - drop.settled; row >= 0; row--) {
|
|
235
|
+
if (this.finalGrid[row][x] !== " ") {
|
|
236
|
+
targetRow = row;
|
|
237
|
+
break;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
// Move drop down
|
|
241
|
+
drop.y++;
|
|
242
|
+
// Draw falling drop
|
|
243
|
+
if (drop.y >= 0 && drop.y < DISPLAY_HEIGHT) {
|
|
244
|
+
if (targetRow >= 0 && drop.y >= targetRow) {
|
|
245
|
+
// Settle
|
|
246
|
+
drop.settled = DISPLAY_HEIGHT - targetRow;
|
|
247
|
+
drop.y = -Math.floor(Math.random() * 5) - 1;
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
// Still falling
|
|
251
|
+
this.currentGrid[drop.y][x] = "▓";
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return allSettled;
|
|
256
|
+
}
|
|
257
|
+
tickFade() {
|
|
258
|
+
const state = this.effectState;
|
|
259
|
+
const pixelsPerFrame = 15;
|
|
260
|
+
for (let i = 0; i < pixelsPerFrame; i++) {
|
|
261
|
+
if (state.idx >= state.positions.length)
|
|
262
|
+
return true;
|
|
263
|
+
const [row, x] = state.positions[state.idx];
|
|
264
|
+
this.currentGrid[row][x] = this.finalGrid[row][x];
|
|
265
|
+
state.idx++;
|
|
266
|
+
}
|
|
267
|
+
return false;
|
|
268
|
+
}
|
|
269
|
+
tickCrt() {
|
|
270
|
+
const state = this.effectState;
|
|
271
|
+
const midRow = Math.floor(DISPLAY_HEIGHT / 2);
|
|
272
|
+
this.currentGrid = this.createEmptyGrid();
|
|
273
|
+
// Draw from middle expanding outward
|
|
274
|
+
const top = midRow - state.expansion;
|
|
275
|
+
const bottom = midRow + state.expansion;
|
|
276
|
+
for (let row = Math.max(0, top); row <= Math.min(DISPLAY_HEIGHT - 1, bottom); row++) {
|
|
277
|
+
for (let x = 0; x < WIDTH; x++) {
|
|
278
|
+
this.currentGrid[row][x] = this.finalGrid[row][x];
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
state.expansion++;
|
|
282
|
+
return state.expansion > DISPLAY_HEIGHT;
|
|
283
|
+
}
|
|
284
|
+
tickGlitch() {
|
|
285
|
+
const state = this.effectState;
|
|
286
|
+
if (state.phase < state.glitchFrames) {
|
|
287
|
+
// Glitch phase: show corrupted version
|
|
288
|
+
this.currentGrid = this.finalGrid.map((row) => {
|
|
289
|
+
const offset = Math.floor(Math.random() * 7) - 3;
|
|
290
|
+
const glitchRow = [...row];
|
|
291
|
+
// Random horizontal offset
|
|
292
|
+
if (Math.random() < 0.3) {
|
|
293
|
+
const shifted = glitchRow.slice(offset).concat(glitchRow.slice(0, offset));
|
|
294
|
+
return shifted.slice(0, WIDTH);
|
|
295
|
+
}
|
|
296
|
+
// Random vertical swap
|
|
297
|
+
if (Math.random() < 0.2) {
|
|
298
|
+
const swapRow = Math.floor(Math.random() * DISPLAY_HEIGHT);
|
|
299
|
+
return [...this.finalGrid[swapRow]];
|
|
300
|
+
}
|
|
301
|
+
return glitchRow;
|
|
302
|
+
});
|
|
303
|
+
state.phase++;
|
|
304
|
+
return false;
|
|
305
|
+
}
|
|
306
|
+
// Final frame: show clean image
|
|
307
|
+
this.currentGrid = this.finalGrid.map((row) => [...row]);
|
|
308
|
+
return true;
|
|
309
|
+
}
|
|
310
|
+
tickDissolve() {
|
|
311
|
+
const state = this.effectState;
|
|
312
|
+
const pixelsPerFrame = 20;
|
|
313
|
+
for (let i = 0; i < pixelsPerFrame; i++) {
|
|
314
|
+
if (state.idx >= state.positions.length)
|
|
315
|
+
return true;
|
|
316
|
+
const [row, x] = state.positions[state.idx];
|
|
317
|
+
this.currentGrid[row][x] = this.finalGrid[row][x];
|
|
318
|
+
state.idx++;
|
|
319
|
+
}
|
|
320
|
+
return false;
|
|
321
|
+
}
|
|
322
|
+
updateDisplay() {
|
|
323
|
+
this.gridVersion++;
|
|
324
|
+
}
|
|
325
|
+
dispose() {
|
|
326
|
+
this.stopAnimation();
|
|
327
|
+
}
|
|
328
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { Container, Markdown, Spacer, Text } from "indusagi/tui";
|
|
2
|
+
import { getMarkdownTheme, theme } from "../theme/theme.js";
|
|
3
|
+
/**
|
|
4
|
+
* Component that renders a complete assistant message
|
|
5
|
+
*/
|
|
6
|
+
export class AssistantMessageComponent extends Container {
|
|
7
|
+
constructor(message, hideThinkingBlock = false, markdownTheme = getMarkdownTheme()) {
|
|
8
|
+
super();
|
|
9
|
+
this.hideThinkingBlock = hideThinkingBlock;
|
|
10
|
+
this.markdownTheme = markdownTheme;
|
|
11
|
+
// Container for text/thinking content
|
|
12
|
+
this.contentContainer = new Container();
|
|
13
|
+
this.addChild(this.contentContainer);
|
|
14
|
+
if (message) {
|
|
15
|
+
this.updateContent(message);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
invalidate() {
|
|
19
|
+
super.invalidate();
|
|
20
|
+
if (this.lastMessage) {
|
|
21
|
+
this.updateContent(this.lastMessage);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
setHideThinkingBlock(hide) {
|
|
25
|
+
this.hideThinkingBlock = hide;
|
|
26
|
+
}
|
|
27
|
+
updateContent(message) {
|
|
28
|
+
this.lastMessage = message;
|
|
29
|
+
// Clear content container
|
|
30
|
+
this.contentContainer.clear();
|
|
31
|
+
const hasVisibleContent = message.content.some((c) => (c.type === "text" && c.text.trim()) || (c.type === "thinking" && c.thinking.trim()));
|
|
32
|
+
if (hasVisibleContent) {
|
|
33
|
+
this.contentContainer.addChild(new Spacer(1));
|
|
34
|
+
}
|
|
35
|
+
// Render content in order
|
|
36
|
+
for (let i = 0; i < message.content.length; i++) {
|
|
37
|
+
const content = message.content[i];
|
|
38
|
+
if (content.type === "text" && content.text.trim()) {
|
|
39
|
+
// Assistant text messages with no background - trim the text
|
|
40
|
+
// Set paddingY=0 to avoid extra spacing before tool executions
|
|
41
|
+
this.contentContainer.addChild(new Markdown(content.text.trim(), 1, 0, this.markdownTheme));
|
|
42
|
+
}
|
|
43
|
+
else if (content.type === "thinking" && content.thinking.trim()) {
|
|
44
|
+
// Check if there's text content after this thinking block
|
|
45
|
+
const hasTextAfter = message.content.slice(i + 1).some((c) => c.type === "text" && c.text.trim());
|
|
46
|
+
if (this.hideThinkingBlock) {
|
|
47
|
+
// Show static "Thinking..." label when hidden
|
|
48
|
+
this.contentContainer.addChild(new Text(theme.italic(theme.fg("thinkingText", "Thinking...")), 1, 0));
|
|
49
|
+
if (hasTextAfter) {
|
|
50
|
+
this.contentContainer.addChild(new Spacer(1));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
// Thinking traces in thinkingText color, italic
|
|
55
|
+
this.contentContainer.addChild(new Markdown(content.thinking.trim(), 1, 0, this.markdownTheme, {
|
|
56
|
+
color: (text) => theme.fg("thinkingText", text),
|
|
57
|
+
italic: true,
|
|
58
|
+
}));
|
|
59
|
+
this.contentContainer.addChild(new Spacer(1));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// Check if aborted - show after partial content
|
|
64
|
+
// But only if there are no tool calls (tool execution components will show the error)
|
|
65
|
+
const hasToolCalls = message.content.some((c) => c.type === "toolCall");
|
|
66
|
+
if (!hasToolCalls) {
|
|
67
|
+
if (message.stopReason === "aborted") {
|
|
68
|
+
const abortMessage = message.errorMessage && message.errorMessage !== "Request was aborted"
|
|
69
|
+
? message.errorMessage
|
|
70
|
+
: "Operation aborted";
|
|
71
|
+
if (hasVisibleContent) {
|
|
72
|
+
this.contentContainer.addChild(new Spacer(1));
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
this.contentContainer.addChild(new Spacer(1));
|
|
76
|
+
}
|
|
77
|
+
this.contentContainer.addChild(new Text(theme.fg("error", abortMessage), 1, 0));
|
|
78
|
+
}
|
|
79
|
+
else if (message.stopReason === "error") {
|
|
80
|
+
const errorMsg = message.errorMessage || "Unknown error";
|
|
81
|
+
this.contentContainer.addChild(new Spacer(1));
|
|
82
|
+
this.contentContainer.addChild(new Text(theme.fg("error", `Error: ${errorMsg}`), 1, 0));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|