jeo-code 0.1.0 → 0.4.5
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.ja.md +160 -0
- package/README.ko.md +160 -0
- package/README.md +115 -297
- package/README.zh.md +160 -0
- package/package.json +11 -6
- package/scripts/install.sh +28 -28
- package/scripts/uninstall.sh +17 -15
- package/src/AGENTS.md +50 -0
- package/src/agent/AGENTS.md +49 -0
- package/src/agent/bash-fixups.ts +103 -0
- package/src/agent/compaction.ts +410 -19
- package/src/agent/config-schema.ts +119 -5
- package/src/agent/context-files.ts +314 -17
- package/src/agent/dev/AGENTS.md +36 -0
- package/src/agent/dev/advanced-analyzer.ts +12 -0
- package/src/agent/dev/evolution-bridge.ts +82 -0
- package/src/agent/dev/evolution-logger.ts +41 -0
- package/src/agent/dev/self-analysis.ts +64 -0
- package/src/agent/dev/self-improve.ts +24 -0
- package/src/agent/dev/spec-automation.ts +49 -0
- package/src/agent/engine.ts +808 -54
- package/src/agent/hooks.ts +273 -0
- package/src/agent/loop.ts +21 -1
- package/src/agent/memory.ts +201 -0
- package/src/agent/model-recency.ts +32 -0
- package/src/agent/output-minimizer.ts +108 -0
- package/src/agent/output-util.ts +64 -0
- package/src/agent/plan.ts +187 -0
- package/src/agent/seed.ts +52 -0
- package/src/agent/session.ts +235 -21
- package/src/agent/state.ts +286 -39
- package/src/agent/step-budget.ts +232 -0
- package/src/agent/subagents.ts +223 -26
- package/src/agent/task-tool.ts +272 -0
- package/src/agent/todo-tool.ts +87 -0
- package/src/agent/tokenizer.ts +117 -0
- package/src/agent/tool-registry.ts +54 -0
- package/src/agent/tools.ts +624 -103
- package/src/agent/web-search.ts +538 -0
- package/src/ai/AGENTS.md +44 -0
- package/src/ai/index.ts +1 -0
- package/src/ai/model-catalog-compat.ts +3 -1
- package/src/ai/model-catalog.ts +74 -9
- package/src/ai/model-discovery.ts +215 -17
- package/src/ai/model-manager.ts +346 -32
- package/src/ai/model-picker.ts +1 -1
- package/src/ai/model-registry.ts +4 -2
- package/src/ai/pricing.ts +84 -0
- package/src/ai/provider-registry.ts +23 -0
- package/src/ai/provider-status.ts +60 -16
- package/src/ai/providers/AGENTS.md +42 -0
- package/src/ai/providers/anthropic.ts +250 -31
- package/src/ai/providers/antigravity.ts +219 -0
- package/src/ai/providers/errors.ts +15 -1
- package/src/ai/providers/gemini.ts +196 -13
- package/src/ai/providers/ollama.ts +37 -7
- package/src/ai/providers/openai-responses.ts +173 -0
- package/src/ai/providers/openai.ts +64 -12
- package/src/ai/sse.ts +4 -1
- package/src/ai/types.ts +18 -1
- package/src/auth/AGENTS.md +41 -0
- package/src/auth/callback-server.ts +6 -1
- package/src/auth/flows/AGENTS.md +32 -0
- package/src/auth/flows/antigravity.ts +151 -0
- package/src/auth/flows/google-project.ts +190 -0
- package/src/auth/flows/google.ts +39 -18
- package/src/auth/flows/index.ts +15 -5
- package/src/auth/flows/openai.ts +2 -2
- package/src/auth/oauth.ts +8 -0
- package/src/auth/refresh.ts +44 -27
- package/src/auth/storage.ts +149 -26
- package/src/auth/types.ts +1 -1
- package/src/autopilot.ts +362 -0
- package/src/bun-imports.d.ts +4 -0
- package/src/cli/AGENTS.md +39 -0
- package/src/cli/runner.ts +148 -14
- package/src/cli.ts +13 -4
- package/src/commands/AGENTS.md +40 -0
- package/src/commands/approve.ts +62 -3
- package/src/commands/auth.ts +167 -25
- package/src/commands/chat.ts +37 -8
- package/src/commands/deep-interview.ts +633 -175
- package/src/commands/doctor.ts +84 -37
- package/src/commands/evolve-core.ts +18 -0
- package/src/commands/evolve.ts +2 -1
- package/src/commands/export.ts +176 -0
- package/src/commands/gjc.ts +52 -0
- package/src/commands/launch.ts +3549 -240
- package/src/commands/mcp.ts +3 -3
- package/src/commands/ooo-seed.ts +19 -0
- package/src/commands/ralplan.ts +253 -35
- package/src/commands/resume.ts +1 -1
- package/src/commands/session.ts +183 -0
- package/src/commands/setup-helpers.ts +10 -3
- package/src/commands/setup.ts +57 -16
- package/src/commands/skills.ts +78 -18
- package/src/commands/state.ts +198 -0
- package/src/commands/status.ts +84 -0
- package/src/commands/team.ts +340 -212
- package/src/commands/ultragoal.ts +122 -61
- package/src/commands/update.ts +244 -0
- package/src/ledger.ts +270 -0
- package/src/mcp/AGENTS.md +38 -0
- package/src/mcp/server.ts +115 -14
- package/src/mcp/tools.ts +42 -22
- package/src/md-modules.d.ts +4 -0
- package/src/prompts/AGENTS.md +41 -0
- package/src/prompts/agents/AGENTS.md +35 -0
- package/src/prompts/agents/architect.md +35 -0
- package/src/prompts/agents/critic.md +37 -0
- package/src/prompts/agents/executor.md +36 -0
- package/src/prompts/agents/planner.md +37 -0
- package/src/prompts/skills/AGENTS.md +36 -0
- package/src/prompts/skills/deep-dive/AGENTS.md +31 -0
- package/src/prompts/skills/deep-dive/SKILL.md +13 -0
- package/src/prompts/skills/deep-interview/AGENTS.md +31 -0
- package/src/prompts/skills/deep-interview/SKILL.md +12 -0
- package/src/prompts/skills/gjc/AGENTS.md +31 -0
- package/src/prompts/skills/gjc/SKILL.md +15 -0
- package/src/prompts/skills/ralplan/AGENTS.md +31 -0
- package/src/prompts/skills/ralplan/SKILL.md +11 -0
- package/src/prompts/skills/team/AGENTS.md +31 -0
- package/src/prompts/skills/team/SKILL.md +11 -0
- package/src/prompts/skills/ultragoal/AGENTS.md +31 -0
- package/src/prompts/skills/ultragoal/SKILL.md +11 -0
- package/src/skills/AGENTS.md +38 -0
- package/src/skills/catalog.ts +565 -31
- package/src/tui/AGENTS.md +43 -0
- package/src/tui/app.ts +1181 -92
- package/src/tui/components/AGENTS.md +42 -0
- package/src/tui/components/ascii-art.ts +257 -15
- package/src/tui/components/autocomplete.ts +98 -16
- package/src/tui/components/autopilot-status.ts +65 -0
- package/src/tui/components/category-index.ts +49 -0
- package/src/tui/components/code-view.ts +54 -11
- package/src/tui/components/color.ts +171 -2
- package/src/tui/components/config-panel.ts +82 -15
- package/src/tui/components/duration.ts +38 -0
- package/src/tui/components/evolution.ts +3 -3
- package/src/tui/components/footer.ts +91 -42
- package/src/tui/components/forge.ts +426 -31
- package/src/tui/components/hints.ts +54 -0
- package/src/tui/components/hud.ts +73 -0
- package/src/tui/components/index.ts +4 -0
- package/src/tui/components/input-box.ts +150 -0
- package/src/tui/components/layout.ts +11 -3
- package/src/tui/components/live-model-picker.ts +108 -0
- package/src/tui/components/markdown-table.ts +140 -0
- package/src/tui/components/markdown-text.ts +97 -0
- package/src/tui/components/meter.ts +4 -1
- package/src/tui/components/model-picker.ts +3 -2
- package/src/tui/components/provider-picker.ts +3 -2
- package/src/tui/components/section.ts +70 -0
- package/src/tui/components/select-list.ts +40 -10
- package/src/tui/components/skill-picker.ts +25 -0
- package/src/tui/components/slash.ts +244 -21
- package/src/tui/components/status.ts +272 -11
- package/src/tui/components/step-timeline.ts +218 -0
- package/src/tui/components/stream.ts +26 -9
- package/src/tui/components/themes.ts +212 -6
- package/src/tui/components/todo-card.ts +47 -0
- package/src/tui/components/tool-list.ts +58 -12
- package/src/tui/components/transcript.ts +120 -0
- package/src/tui/components/update-box.ts +31 -0
- package/src/tui/components/welcome.ts +162 -0
- package/src/tui/components/width.ts +163 -0
- package/src/tui/monitoring/AGENTS.md +31 -0
- package/src/tui/monitoring/hud-view.ts +55 -0
- package/src/tui/renderer.ts +112 -3
- package/src/tui/terminal.ts +40 -33
- package/src/util/AGENTS.md +39 -0
- package/src/util/clipboard-image.ts +118 -0
- package/src/util/env.ts +12 -0
- package/src/util/provider-error.ts +78 -0
- package/src/util/retry.ts +91 -6
- package/src/util/update-check.ts +64 -0
- package/src/commands/models.ts +0 -104
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<!-- Parent: ../../AGENTS.md -->
|
|
2
|
+
<!-- Generated: 2026-06-11 | Updated: 2026-06-11 -->
|
|
3
|
+
|
|
4
|
+
# components
|
|
5
|
+
|
|
6
|
+
## Purpose
|
|
7
|
+
Reusable UI widgets and layout primitives for the terminal interface.
|
|
8
|
+
|
|
9
|
+
## Key Files
|
|
10
|
+
| File | Description |
|
|
11
|
+
|------|-------------|
|
|
12
|
+
| `forge.ts` | Formats the boxed tool execution outputs |
|
|
13
|
+
| `status.ts` | The `[STEP]` / `[STATUS]` / `[TOOL]` HUD lines |
|
|
14
|
+
| `section.ts` | Shadcn-inspired card layout and spacing tokens |
|
|
15
|
+
| `layout.ts` | Low-level padding, boxing, and alignment math |
|
|
16
|
+
| `ascii-art.ts` | Cellular evolution graphics |
|
|
17
|
+
|
|
18
|
+
## Subdirectories
|
|
19
|
+
*(None)*
|
|
20
|
+
|
|
21
|
+
## For AI Agents
|
|
22
|
+
|
|
23
|
+
### Working In This Directory
|
|
24
|
+
- Prioritize deterministic width/height calculations.
|
|
25
|
+
- Always strip ANSI codes (`\x1b[...m`) before measuring string lengths.
|
|
26
|
+
- Components should return string arrays (`string[]`) representing lines, not perform direct stdout writes.
|
|
27
|
+
|
|
28
|
+
### Testing Requirements
|
|
29
|
+
- Snapshot or exact string matching in unit tests.
|
|
30
|
+
|
|
31
|
+
### Common Patterns
|
|
32
|
+
- Theming via `themes.ts` and `chalk`.
|
|
33
|
+
|
|
34
|
+
## Dependencies
|
|
35
|
+
|
|
36
|
+
### Internal
|
|
37
|
+
- Consumed by `src/tui/app.ts`.
|
|
38
|
+
|
|
39
|
+
### External
|
|
40
|
+
*(None)*
|
|
41
|
+
|
|
42
|
+
<!-- MANUAL: -->
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import { stageIndexForStep, clampStageIndex, type StageGradient } from "./evolution";
|
|
3
|
-
import { applyGradient, hexToRgb, ColorLevel } from "./color";
|
|
3
|
+
import { applyGradient, hexToRgb, ColorLevel, animatedGradientText } from "./color";
|
|
4
4
|
|
|
5
5
|
export interface AsciiStage {
|
|
6
6
|
name: string;
|
|
@@ -13,6 +13,14 @@ export interface AsciiStage {
|
|
|
13
13
|
* the fallback when `frames` is absent. Frames should match `art`'s line count.
|
|
14
14
|
*/
|
|
15
15
|
frames?: string[][];
|
|
16
|
+
/**
|
|
17
|
+
* Optional ASCII-only `art`/`frames` used when the caller passes `unicode: false`
|
|
18
|
+
* (terminals that cannot render box-drawing / geometric glyphs). When absent the
|
|
19
|
+
* stage's normal `art`/`frames` are used as-is (the other stages are already
|
|
20
|
+
* ASCII-clean). Row counts should match so per-line colors stay aligned.
|
|
21
|
+
*/
|
|
22
|
+
asciiArt?: string[];
|
|
23
|
+
asciiFrames?: string[][];
|
|
16
24
|
}
|
|
17
25
|
|
|
18
26
|
export const EVOLUTION_STAGES: AsciiStage[] = [
|
|
@@ -20,33 +28,82 @@ export const EVOLUTION_STAGES: AsciiStage[] = [
|
|
|
20
28
|
name: "Primordial Cell",
|
|
21
29
|
color: s => chalk.cyan(s),
|
|
22
30
|
art: [
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
31
|
+
" ○ ○ ",
|
|
32
|
+
" ╲ ╱ ",
|
|
33
|
+
" ╭───────╮ ",
|
|
34
|
+
" │ ◕ § ◕ │ ",
|
|
35
|
+
" │ · ‿ · │ ",
|
|
36
|
+
" ╰───────╯ ",
|
|
37
|
+
" ╱ ╲ ╱ ╲ ",
|
|
27
38
|
" [Primordial Cell]"
|
|
28
39
|
],
|
|
29
40
|
lineColors: [
|
|
30
41
|
chalk.cyan,
|
|
31
42
|
chalk.cyan,
|
|
32
43
|
chalk.cyan,
|
|
44
|
+
s => chalk.bold.cyan(s),
|
|
45
|
+
chalk.cyan,
|
|
46
|
+
chalk.cyan,
|
|
33
47
|
chalk.cyan,
|
|
34
48
|
s => chalk.bold.cyan(s)
|
|
35
49
|
],
|
|
36
|
-
//
|
|
50
|
+
// A glowing primordial cell after assets/character.png: antennae with pulsing
|
|
51
|
+
// tips, a round membrane, big kawaii eyes flanking an inner DNA helix (§/≋), a
|
|
52
|
+
// smile, and crab-like legs. Two frames "breathe": blink + helix pulse + leg swing.
|
|
53
|
+
// Every glyph is display-width 1 (box-drawing + ambiguous-width geometrics the
|
|
54
|
+
// rest of the TUI already uses) so renderAsciiArt's length-based padding and
|
|
55
|
+
// app.ts/welcome.ts's visibleWidth centering stay in lockstep.
|
|
37
56
|
frames: [
|
|
38
57
|
[
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
58
|
+
" ○ ○ ",
|
|
59
|
+
" ╲ ╱ ",
|
|
60
|
+
" ╭───────╮ ",
|
|
61
|
+
" │ ◕ § ◕ │ ",
|
|
62
|
+
" │ · ‿ · │ ",
|
|
63
|
+
" ╰───────╯ ",
|
|
64
|
+
" ╱ ╲ ╱ ╲ ",
|
|
65
|
+
" [Primordial Cell]"
|
|
66
|
+
],
|
|
67
|
+
[
|
|
68
|
+
" ◉ ◉ ",
|
|
69
|
+
" ╲ ╱ ",
|
|
70
|
+
" ╭───────╮ ",
|
|
71
|
+
" │ ◔ ≋ ◔ │ ",
|
|
72
|
+
" │ ° ‿ ° │ ",
|
|
73
|
+
" ╰───────╯ ",
|
|
74
|
+
" ╲ ╱ ╲ ╱ ",
|
|
75
|
+
" [Primordial Cell]"
|
|
76
|
+
]
|
|
77
|
+
],
|
|
78
|
+
asciiArt: [
|
|
79
|
+
" o o ",
|
|
80
|
+
" \\ / ",
|
|
81
|
+
" .-------. ",
|
|
82
|
+
" | o 8 o | ",
|
|
83
|
+
" | \\_/ | ",
|
|
84
|
+
" '-------' ",
|
|
85
|
+
" / \\ / \\ ",
|
|
86
|
+
" [Primordial Cell]"
|
|
87
|
+
],
|
|
88
|
+
asciiFrames: [
|
|
89
|
+
[
|
|
90
|
+
" o o ",
|
|
91
|
+
" \\ / ",
|
|
92
|
+
" .-------. ",
|
|
93
|
+
" | o 8 o | ",
|
|
94
|
+
" | \\_/ | ",
|
|
95
|
+
" '-------' ",
|
|
96
|
+
" / \\ / \\ ",
|
|
43
97
|
" [Primordial Cell]"
|
|
44
98
|
],
|
|
45
99
|
[
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
100
|
+
" O O ",
|
|
101
|
+
" \\ / ",
|
|
102
|
+
" .-------. ",
|
|
103
|
+
" | - % - | ",
|
|
104
|
+
" | \\_/ | ",
|
|
105
|
+
" '-------' ",
|
|
106
|
+
" \\ / \\ / ",
|
|
50
107
|
" [Primordial Cell]"
|
|
51
108
|
]
|
|
52
109
|
]
|
|
@@ -242,6 +299,9 @@ export interface RenderAsciiOptions {
|
|
|
242
299
|
gradient?: StageGradient;
|
|
243
300
|
/** Color tier for gradient rendering (default TrueColor). */
|
|
244
301
|
colorLevel?: ColorLevel;
|
|
302
|
+
/** Use the stage's ASCII-only `asciiArt`/`asciiFrames` fallback when false
|
|
303
|
+
* (terminals without box-drawing/geometric glyph support). Default true. */
|
|
304
|
+
unicode?: boolean;
|
|
245
305
|
}
|
|
246
306
|
|
|
247
307
|
/**
|
|
@@ -252,7 +312,21 @@ export interface RenderAsciiOptions {
|
|
|
252
312
|
*/
|
|
253
313
|
export function renderAsciiArt(stage: AsciiStage, opts: RenderAsciiOptions = {}): string[] {
|
|
254
314
|
const useColor = opts.color !== false;
|
|
255
|
-
const
|
|
315
|
+
const useUnicode = opts.unicode !== false;
|
|
316
|
+
// ASCII-fallback source set: a non-unicode terminal gets the stage's plain-ASCII
|
|
317
|
+
// art/frames (when defined) instead of box-drawing/geometric glyphs that would
|
|
318
|
+
// render as tofu boxes. stageBlocks/stageFrame keep using the unicode frames so
|
|
319
|
+
// stageWidth()/stageHeight() invariants are unaffected.
|
|
320
|
+
const frameSet =
|
|
321
|
+
!useUnicode && stage.asciiFrames && stage.asciiFrames.length > 0 ? stage.asciiFrames : stageBlocks(stage);
|
|
322
|
+
const baseArt = !useUnicode && stage.asciiArt ? stage.asciiArt : stage.art;
|
|
323
|
+
let source: string[];
|
|
324
|
+
if (opts.frame !== undefined) {
|
|
325
|
+
const t = Number.isFinite(opts.frame) ? Math.trunc(opts.frame) : 0;
|
|
326
|
+
source = frameSet[((t % frameSet.length) + frameSet.length) % frameSet.length]!;
|
|
327
|
+
} else {
|
|
328
|
+
source = baseArt;
|
|
329
|
+
}
|
|
256
330
|
const width = opts.width ?? Math.max(0, ...source.map(l => l.length));
|
|
257
331
|
if (opts.cols !== undefined && opts.cols < width) {
|
|
258
332
|
return [];
|
|
@@ -338,3 +412,171 @@ export async function animateFrames(stage: AsciiStage, opts: AnimateFramesOption
|
|
|
338
412
|
}
|
|
339
413
|
return total;
|
|
340
414
|
}
|
|
415
|
+
export const DNA_CLAW_ART: string[] = [
|
|
416
|
+
" ╭╯ ◆ ◆ ╰╮ ",
|
|
417
|
+
" ╭╯ ╱╲ ╱╲ ╰╮ ",
|
|
418
|
+
" ║ ╲ ╳ ╱ ║ ",
|
|
419
|
+
" ╰╮ ╳ ╳ ╭╯ ",
|
|
420
|
+
" ╰╮ ╱ ╳ ╲ ╭╯ ",
|
|
421
|
+
" ╚══○ ○══╝ ",
|
|
422
|
+
" ║ ║ ",
|
|
423
|
+
" [ DNA Claw ] "
|
|
424
|
+
];
|
|
425
|
+
|
|
426
|
+
export const DNA_CLAW_ART_ASCII: string[] = [
|
|
427
|
+
" /{ * * }\\ ",
|
|
428
|
+
" /{ / \\ / \\ }\\ ",
|
|
429
|
+
" | \\ X / | ",
|
|
430
|
+
" \\{ X X }/ ",
|
|
431
|
+
" \\{ / X \\ }/ ",
|
|
432
|
+
" \\==o o==/ ",
|
|
433
|
+
" | | ",
|
|
434
|
+
" [ DNA Claw ] "
|
|
435
|
+
];
|
|
436
|
+
|
|
437
|
+
/** Twist animation frames for the compact DNA Claw: the claw silhouette stays
|
|
438
|
+
* fixed while the inner helix lattice rotates. Frame 0 === DNA_CLAW_ART, so a
|
|
439
|
+
* frameless render is byte-identical to the static symbol. All lines are the
|
|
440
|
+
* same width (18) and every glyph is display-width 1. */
|
|
441
|
+
export const DNA_CLAW_FRAMES: string[][] = [
|
|
442
|
+
DNA_CLAW_ART,
|
|
443
|
+
[
|
|
444
|
+
" ╭╯ ◆ ◆ ╰╮ ",
|
|
445
|
+
" ╭╯ ╲╱ ╲╱ ╰╮ ",
|
|
446
|
+
" ║ ╳ ╳ ║ ",
|
|
447
|
+
" ╰╮ ╱ ╳ ╲ ╭╯ ",
|
|
448
|
+
" ╰╮ ╳ ╳ ╭╯ ",
|
|
449
|
+
" ╚══○ ○══╝ ",
|
|
450
|
+
" ║ ║ ",
|
|
451
|
+
" [ DNA Claw ] "
|
|
452
|
+
],
|
|
453
|
+
[
|
|
454
|
+
" ╭╯ ◆ ◆ ╰╮ ",
|
|
455
|
+
" ╭╯ ╱╲ ╱╲ ╰╮ ",
|
|
456
|
+
" ║ ╳ ╳ ║ ",
|
|
457
|
+
" ╰╮ ╲ ╳ ╱ ╭╯ ",
|
|
458
|
+
" ╰╮ ╳ ╳ ╭╯ ",
|
|
459
|
+
" ╚══○ ○══╝ ",
|
|
460
|
+
" ║ ║ ",
|
|
461
|
+
" [ DNA Claw ] "
|
|
462
|
+
]
|
|
463
|
+
];
|
|
464
|
+
|
|
465
|
+
export const DNA_CLAW_FRAMES_ASCII: string[][] = [
|
|
466
|
+
DNA_CLAW_ART_ASCII,
|
|
467
|
+
[
|
|
468
|
+
" /{ * * }\\ ",
|
|
469
|
+
" /{ \\ / \\ / }\\ ",
|
|
470
|
+
" | X X | ",
|
|
471
|
+
" \\{ / X \\ }/ ",
|
|
472
|
+
" \\{ X X }/ ",
|
|
473
|
+
" \\==o o==/ ",
|
|
474
|
+
" | | ",
|
|
475
|
+
" [ DNA Claw ] "
|
|
476
|
+
],
|
|
477
|
+
[
|
|
478
|
+
" /{ * * }\\ ",
|
|
479
|
+
" /{ / \\ / \\ }\\ ",
|
|
480
|
+
" | X X | ",
|
|
481
|
+
" \\{ \\ X / }/ ",
|
|
482
|
+
" \\{ X X }/ ",
|
|
483
|
+
" \\==o o==/ ",
|
|
484
|
+
" | | ",
|
|
485
|
+
" [ DNA Claw ] "
|
|
486
|
+
]
|
|
487
|
+
];
|
|
488
|
+
|
|
489
|
+
/** Number of twist frames in the compact DNA Claw animation cycle. */
|
|
490
|
+
export function dnaClawFrameCount(): number {
|
|
491
|
+
return DNA_CLAW_FRAMES.length;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/** Grand hero variant for the welcome forge box (gjc-style spacious banner):
|
|
495
|
+
* a wide claw whose pincers frame a twisting DNA helix. Width-1 glyphs only
|
|
496
|
+
* (box drawing + diagonals + geometrics) so padding/centering math stays exact. */
|
|
497
|
+
export const DNA_CLAW_ART_GRAND: string[] = [
|
|
498
|
+
" ◆◆ ◆◆ ",
|
|
499
|
+
" ╭──╯╰──╮ ╭──╯╰──╮ ",
|
|
500
|
+
" ╭╯ ╰╮ ╲╲ ╱╱ ╭╯ ╰╮ ",
|
|
501
|
+
" ╭╯ ║ ╲╳╳╱ ║ ╰╮ ",
|
|
502
|
+
" ║ ║ ╳╳ ║ ║ ",
|
|
503
|
+
" ║ ║ ╱╳╳╲ ║ ║ ",
|
|
504
|
+
" ╰╮ ║ ╱╱ ╲╲ ║ ╭╯ ",
|
|
505
|
+
" ╰╮ ║ ╲╲ ╱╱ ║ ╭╯ ",
|
|
506
|
+
" ╰──╮ ║ ╲╳╳╱ ║ ╭──╯ ",
|
|
507
|
+
" ╰════○ ╳╳ ○════╯ ",
|
|
508
|
+
" ║ ╱╳╳╲ ║ ",
|
|
509
|
+
" [ D N A · C L A W ] "
|
|
510
|
+
];
|
|
511
|
+
|
|
512
|
+
export const DNA_CLAW_ART_GRAND_ASCII: string[] = [
|
|
513
|
+
" ** ** ",
|
|
514
|
+
" /--'`--\\ /--'`--\\ ",
|
|
515
|
+
" /' `\\ \\\\ // /' `\\ ",
|
|
516
|
+
" /' | \\XX/ | `\\ ",
|
|
517
|
+
" | | XX | | ",
|
|
518
|
+
" | | /XX\\ | | ",
|
|
519
|
+
" \\, | // \\\\ | ,/ ",
|
|
520
|
+
" \\, | \\\\ // | ,/ ",
|
|
521
|
+
" \\--, | \\XX/ | ,--/ ",
|
|
522
|
+
" \\====o XX o====/ ",
|
|
523
|
+
" | /XX\\ | ",
|
|
524
|
+
" [ D N A . C L A W ] "
|
|
525
|
+
];
|
|
526
|
+
|
|
527
|
+
export function renderDnaClaw(opts: {
|
|
528
|
+
cols?: number;
|
|
529
|
+
phase?: number;
|
|
530
|
+
unicode?: boolean;
|
|
531
|
+
color?: boolean;
|
|
532
|
+
colorLevel?: ColorLevel;
|
|
533
|
+
/** Grand hero variant (welcome forge box); default is the compact in-turn symbol. */
|
|
534
|
+
grand?: boolean;
|
|
535
|
+
/** Twist-animation frame (compact symbol only; wraps). The helix lattice rotates
|
|
536
|
+
* while the claw silhouette stays fixed — combined with the flowing gradient
|
|
537
|
+
* `phase` this animates the forge identity without any frame-count growth. */
|
|
538
|
+
frame?: number;
|
|
539
|
+
}): string[] {
|
|
540
|
+
const useUnicode = opts.unicode !== false;
|
|
541
|
+
let source: string[];
|
|
542
|
+
if (opts.grand) {
|
|
543
|
+
source = useUnicode ? DNA_CLAW_ART_GRAND : DNA_CLAW_ART_GRAND_ASCII;
|
|
544
|
+
} else {
|
|
545
|
+
const frames = useUnicode ? DNA_CLAW_FRAMES : DNA_CLAW_FRAMES_ASCII;
|
|
546
|
+
const f = Math.abs(Math.trunc(opts.frame ?? 0)) % frames.length;
|
|
547
|
+
source = frames[f]!;
|
|
548
|
+
}
|
|
549
|
+
const width = Math.max(0, ...source.map(l => l.length));
|
|
550
|
+
|
|
551
|
+
if (opts.cols !== undefined && opts.cols < width) {
|
|
552
|
+
return [];
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
const phase = opts.phase ?? 0;
|
|
556
|
+
const useColor = opts.color !== false;
|
|
557
|
+
const colorLevel = opts.colorLevel ?? ColorLevel.TrueColor;
|
|
558
|
+
const palette = DNA_FLOW_PALETTE;
|
|
559
|
+
|
|
560
|
+
return source.map((line, idx) => {
|
|
561
|
+
const padded = line.length < width ? line + " ".repeat(width - line.length) : line;
|
|
562
|
+
if (!useColor || colorLevel < ColorLevel.TrueColor) {
|
|
563
|
+
return padded;
|
|
564
|
+
}
|
|
565
|
+
return animatedGradientText(padded, palette, phase + idx * 0.07, { colorLevel });
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/** The DNA Claw identity palette (emerald → cyan → violet helix flow). Shared by
|
|
570
|
+
* the claw art and the forge-card border flow so the brand gradient is uniform. */
|
|
571
|
+
export const DNA_FLOW_PALETTE: readonly string[] = ["#10ac84", "#48dbfb", "#8e44ad"];
|
|
572
|
+
|
|
573
|
+
/** Width-1 "claw beat" glyph for an animation tick — the ◆/╳/○ motifs of the
|
|
574
|
+
* claw art cycling in place. Used as the live forge-card title mark. */
|
|
575
|
+
export function dnaClawBeat(frame: number, unicode = true): string {
|
|
576
|
+
const beats = unicode ? ["◆", "╳", "○"] : ["*", "X", "o"];
|
|
577
|
+
return beats[Math.abs(Math.trunc(frame)) % beats.length]!;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
export function dnaClawHeight(): number {
|
|
581
|
+
return DNA_CLAW_ART.length;
|
|
582
|
+
}
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
* Interactive autocomplete engine for the REPL.
|
|
3
3
|
*
|
|
4
4
|
* Completes slash-command *names* and their *arguments*:
|
|
5
|
-
* - `/mod` → `/model
|
|
5
|
+
* - `/mod` → `/model`
|
|
6
6
|
* - `/model gpt` → live (logged-in) model ids + aliases + catalog ids
|
|
7
7
|
* - `/provider an` → provider names; second arg → that provider's live models
|
|
8
8
|
* - `/agents exec` → subagent role ids; second arg → live model ids
|
|
9
|
-
* - `/thinking h` → low/medium/high
|
|
9
|
+
* - `/thinking h` → minimal/low/medium/high/xhigh
|
|
10
10
|
*
|
|
11
11
|
* Pure + synchronous: the dynamic data (live models from the OAuth-authenticated
|
|
12
12
|
* accounts, alias snapshot) is passed in via `CompletionContext`, so the readline
|
|
@@ -17,6 +17,8 @@ import { SLASH_COMMANDS } from "./slash";
|
|
|
17
17
|
import { catalogIds } from "../../ai/model-catalog-compat";
|
|
18
18
|
import { PROVIDER_NAMES } from "../../ai/provider-status";
|
|
19
19
|
import { SUBAGENT_ROLES } from "../../agent/subagents";
|
|
20
|
+
import { skillNames } from "../../skills/catalog";
|
|
21
|
+
import { listThemes } from "./themes";
|
|
20
22
|
|
|
21
23
|
export interface CompletionContext {
|
|
22
24
|
slashCommands: string[];
|
|
@@ -29,8 +31,12 @@ export interface CompletionContext {
|
|
|
29
31
|
providers: string[];
|
|
30
32
|
roleIds: string[];
|
|
31
33
|
thinkingLevels: string[];
|
|
34
|
+
/** Resolved skill names (bundled + user/project). Falls back to bundled when omitted. */
|
|
35
|
+
skillNames?: string[];
|
|
32
36
|
/** Live model ids for a given provider (for `/provider <p> <model>`). */
|
|
33
37
|
modelsForProvider: (provider: string) => string[];
|
|
38
|
+
/** Sync path suggestions for free-text `@path` mentions (relative to cwd). */
|
|
39
|
+
mentionPaths?: (prefix: string) => string[];
|
|
34
40
|
}
|
|
35
41
|
|
|
36
42
|
export interface CompletionResult {
|
|
@@ -42,8 +48,17 @@ export interface CompletionResult {
|
|
|
42
48
|
kind: string;
|
|
43
49
|
}
|
|
44
50
|
|
|
51
|
+
const PREVIEW_LABEL: Record<string, string> = {
|
|
52
|
+
command: "Commands",
|
|
53
|
+
model: "Models",
|
|
54
|
+
provider: "Providers",
|
|
55
|
+
role: "Subagent roles",
|
|
56
|
+
thinking: "Thinking levels",
|
|
57
|
+
subcommand: "Subcommands",
|
|
58
|
+
path: "Paths",
|
|
59
|
+
};
|
|
45
60
|
const MAX_COMPLETIONS = 50;
|
|
46
|
-
const THINKING_LEVELS = ["low", "medium", "high"];
|
|
61
|
+
const THINKING_LEVELS = ["minimal", "low", "medium", "high", "xhigh"];
|
|
47
62
|
|
|
48
63
|
/** Static half of a completion context (no network/config needed). */
|
|
49
64
|
export function staticCompletionContext(): Omit<CompletionContext, "liveModels" | "aliases" | "modelsForProvider"> {
|
|
@@ -88,16 +103,41 @@ function rankedModelPool(ctx: CompletionContext): string[] {
|
|
|
88
103
|
}
|
|
89
104
|
|
|
90
105
|
/**
|
|
91
|
-
* Compute completions for the current input line.
|
|
92
|
-
*
|
|
106
|
+
* Compute completions for the current input line. Slash commands are completed as
|
|
107
|
+
* before; free-text input stays untouched except for `@path` mentions, which can
|
|
108
|
+
* surface local relative paths.
|
|
93
109
|
*/
|
|
94
110
|
export function complete(line: string, ctx: CompletionContext): CompletionResult {
|
|
95
|
-
if (!line.startsWith("/")) return { completions: [], token: line, kind: "none" };
|
|
96
|
-
|
|
97
111
|
const { tokens, trailingSpace } = tokenize(line);
|
|
112
|
+
if (!line.startsWith("/")) {
|
|
113
|
+
const token = trailingSpace ? "" : tokens[tokens.length - 1] ?? "";
|
|
114
|
+
if (token.startsWith("@")) {
|
|
115
|
+
const prefix = token.slice(1);
|
|
116
|
+
const pool = (ctx.mentionPaths?.(prefix) ?? []).map(p => (p.startsWith("@") ? p : `@${p}`));
|
|
117
|
+
return { completions: dedupeCap(prefixHits(pool, token)), token, kind: "path" };
|
|
118
|
+
}
|
|
119
|
+
// `$skill` mention completion at ANY position in the line (mention-style;
|
|
120
|
+
// a leading `$name` is additionally the direct-invocation entrypoint).
|
|
121
|
+
if (token.startsWith("$")) {
|
|
122
|
+
const names = ctx.skillNames ?? skillNames();
|
|
123
|
+
return { completions: dedupeCap(prefixHits(names.map(n => `$${n}`), token)), token, kind: "skill" };
|
|
124
|
+
}
|
|
125
|
+
// `/command` mention completion mid-line (the leading-token case is the
|
|
126
|
+
// dedicated command branch below, which also completes arguments).
|
|
127
|
+
if (token.startsWith("/")) {
|
|
128
|
+
return { completions: dedupeCap(prefixHits(ctx.slashCommands, token)), token, kind: "command" };
|
|
129
|
+
}
|
|
130
|
+
return { completions: [], token: line, kind: "none" };
|
|
131
|
+
}
|
|
132
|
+
|
|
98
133
|
// Completing the command name itself (single token, still typing it).
|
|
99
134
|
if (tokens.length <= 1 && !trailingSpace) {
|
|
100
135
|
const token = tokens[0] ?? "/";
|
|
136
|
+
if (token.toLowerCase().startsWith("/skill:")) {
|
|
137
|
+
const prefix = token.slice("/skill:".length);
|
|
138
|
+
const names = ctx.skillNames ?? skillNames();
|
|
139
|
+
return { completions: dedupeCap(prefixHits(names.map(n => `/skill:${n}`), `/skill:${prefix}`)), token, kind: "command" };
|
|
140
|
+
}
|
|
101
141
|
return { completions: dedupeCap(prefixHits(ctx.slashCommands, token)), token, kind: "command" };
|
|
102
142
|
}
|
|
103
143
|
|
|
@@ -116,31 +156,73 @@ export function complete(line: string, ctx: CompletionContext): CompletionResult
|
|
|
116
156
|
switch (cmd) {
|
|
117
157
|
case "/model": {
|
|
118
158
|
if (token.startsWith("#")) return { completions: [], token, kind: "none" }; // numbered pick
|
|
119
|
-
if (argIndex === 0) return finish(["save", ...rankedModelPool(ctx)], "model");
|
|
120
|
-
|
|
121
|
-
if (argIndex === 1 && tokens[1]?.toLowerCase() === "
|
|
159
|
+
if (argIndex === 0) return finish(["save", "subagent", "role", "thinking", ...rankedModelPool(ctx)], "model");
|
|
160
|
+
if (argIndex === 1 && (tokens[1]?.toLowerCase() === "save")) return finish(rankedModelPool(ctx), "model");
|
|
161
|
+
if (argIndex === 1 && (tokens[1]?.toLowerCase() === "thinking" || tokens[1]?.toLowerCase() === "think")) return finish(ctx.thinkingLevels, "thinking");
|
|
162
|
+
if (argIndex === 1 && (tokens[1]?.toLowerCase() === "subagent" || tokens[1]?.toLowerCase() === "role")) return finish(ctx.roleIds, "role");
|
|
163
|
+
if (argIndex === 2 && (tokens[1]?.toLowerCase() === "subagent" || tokens[1]?.toLowerCase() === "role")) return finish(["thinking", ...rankedModelPool(ctx)], "model");
|
|
164
|
+
if (argIndex === 3 && (tokens[1]?.toLowerCase() === "subagent" || tokens[1]?.toLowerCase() === "role") && (tokens[3]?.toLowerCase() === "thinking" || tokens[3]?.toLowerCase() === "think")) return finish(["inherit", ...ctx.thinkingLevels], "thinking");
|
|
122
165
|
return { completions: [], token, kind: "none" };
|
|
123
166
|
}
|
|
124
|
-
case "/
|
|
125
|
-
return argIndex === 0 ? finish(["
|
|
167
|
+
case "/fast":
|
|
168
|
+
return argIndex === 0 ? finish(["on", "off", "status"], "subcommand") : { completions: [], token, kind: "none" };
|
|
126
169
|
case "/provider": {
|
|
127
|
-
|
|
170
|
+
const cloud = ["anthropic", "openai", "gemini", "antigravity"];
|
|
171
|
+
if (argIndex === 0) return finish(["login", "auth", ...ctx.providers], "provider");
|
|
172
|
+
// `/provider login|auth <name>` → cloud provider names (OAuth-capable).
|
|
173
|
+
if (argIndex === 1 && (tokens[1]?.toLowerCase() === "login" || tokens[1]?.toLowerCase() === "auth")) return finish(cloud, "provider");
|
|
128
174
|
if (argIndex === 1) return finish(ctx.modelsForProvider(tokens[1] ?? ""), "model");
|
|
129
175
|
return { completions: [], token, kind: "none" };
|
|
130
176
|
}
|
|
177
|
+
case "/logout":
|
|
178
|
+
return argIndex === 0 ? finish(["anthropic", "openai", "gemini", "antigravity"], "provider") : { completions: [], token, kind: "none" };
|
|
131
179
|
case "/agents": {
|
|
132
|
-
if (argIndex === 0) return finish(ctx.roleIds, "role");
|
|
133
|
-
if (argIndex === 1) return finish(["maxSteps", ...rankedModelPool(ctx)], "model");
|
|
134
|
-
if (argIndex === 2 && tokens[2]?.toLowerCase() === "
|
|
180
|
+
if (argIndex === 0) return finish(["edit", ...ctx.roleIds], "role");
|
|
181
|
+
if (argIndex === 1) return finish(["reset", "thinking", "maxSteps", ...rankedModelPool(ctx)], "model");
|
|
182
|
+
if (argIndex === 2 && (tokens[2]?.toLowerCase() === "thinking" || tokens[2]?.toLowerCase() === "think")) return finish(["inherit", ...ctx.thinkingLevels], "thinking");
|
|
183
|
+
if (argIndex === 2 && (tokens[2]?.toLowerCase() === "maxsteps" || tokens[2]?.toLowerCase() === "steps")) return { completions: [], token, kind: "none" };
|
|
184
|
+
return { completions: [], token, kind: "none" };
|
|
185
|
+
}
|
|
186
|
+
case "/skill":
|
|
187
|
+
return argIndex === 0 ? finish(ctx.skillNames ?? skillNames(), "subcommand") : { completions: [], token, kind: "none" };
|
|
188
|
+
case "/roles": {
|
|
189
|
+
const tiers = ["smol", "slow", "plan"];
|
|
190
|
+
if (argIndex === 0) return finish(tiers, "role");
|
|
191
|
+
if (argIndex === 1 && tiers.includes(tokens[1]?.toLowerCase() ?? "")) return finish(rankedModelPool(ctx), "model");
|
|
135
192
|
return { completions: [], token, kind: "none" };
|
|
136
193
|
}
|
|
137
194
|
case "/thinking":
|
|
138
195
|
return argIndex === 0 ? finish(ctx.thinkingLevels, "thinking") : { completions: [], token, kind: "none" };
|
|
196
|
+
case "/session":
|
|
197
|
+
return argIndex === 0 ? finish(["info", "delete"], "subcommand") : { completions: [], token, kind: "none" };
|
|
198
|
+
case "/theme":
|
|
199
|
+
return argIndex === 0 ? finish(listThemes().map(t => t.name), "subcommand") : { completions: [], token, kind: "none" };
|
|
200
|
+
case "/login":
|
|
201
|
+
return argIndex === 0 ? finish(["anthropic", "openai", "gemini", "antigravity"], "provider") : { completions: [], token, kind: "none" };
|
|
202
|
+
case "/export":
|
|
203
|
+
return argIndex <= 1 ? finish(["json", "markdown"], "subcommand") : { completions: [], token, kind: "none" };
|
|
139
204
|
default:
|
|
140
205
|
return { completions: [], token, kind: "none" };
|
|
141
206
|
}
|
|
142
207
|
}
|
|
143
208
|
|
|
209
|
+
/** Compact live preview for slash-command arguments (`/subagent `, `/provider login `, ...). */
|
|
210
|
+
export function formatCompletionPreview(line: string, ctx: CompletionContext, max = 6): string[] {
|
|
211
|
+
if (max <= 0) return [];
|
|
212
|
+
const result = complete(line, ctx);
|
|
213
|
+
if (result.kind === "none" || result.kind === "command" || result.completions.length === 0) return [];
|
|
214
|
+
const label = PREVIEW_LABEL[result.kind] ?? "Matches";
|
|
215
|
+
const budget = Math.max(1, max - 1);
|
|
216
|
+
const shown = result.completions.slice(0, budget);
|
|
217
|
+
const lines = [`${label}:`, ...shown.map(c => ` ${c}`)];
|
|
218
|
+
const hidden = result.completions.length - shown.length;
|
|
219
|
+
if (hidden > 0) {
|
|
220
|
+
if (lines.length >= max) lines[lines.length - 1] = ` …(+${hidden + 1} more)`;
|
|
221
|
+
else lines.push(` …(+${hidden} more)`);
|
|
222
|
+
}
|
|
223
|
+
return lines;
|
|
224
|
+
}
|
|
225
|
+
|
|
144
226
|
/** Longest common prefix of a list (for tab "fill to ambiguity"). */
|
|
145
227
|
export function commonPrefix(items: string[]): string {
|
|
146
228
|
if (items.length === 0) return "";
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { padLineTo } from "./layout";
|
|
3
|
+
import { truncate } from "../terminal";
|
|
4
|
+
|
|
5
|
+
export interface AutopilotStatusPanelData {
|
|
6
|
+
task: string;
|
|
7
|
+
goal: string;
|
|
8
|
+
eval: string;
|
|
9
|
+
baseline: string;
|
|
10
|
+
best: string;
|
|
11
|
+
attempts: number;
|
|
12
|
+
kept: number;
|
|
13
|
+
reverted: number;
|
|
14
|
+
sinceImprove: number;
|
|
15
|
+
converged: boolean;
|
|
16
|
+
recommendation: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function renderAutopilotStatusPanel(
|
|
20
|
+
data: AutopilotStatusPanelData,
|
|
21
|
+
opts: { cols?: number; unicode?: boolean; color?: boolean } = {},
|
|
22
|
+
): string[] {
|
|
23
|
+
const width = Math.max(40, Math.min(120, opts.cols ?? 88));
|
|
24
|
+
const useColor = opts.color !== false;
|
|
25
|
+
const unicode = opts.unicode !== false;
|
|
26
|
+
const ruleChar = unicode ? "─" : "-";
|
|
27
|
+
const arrow = unicode ? "→" : "->";
|
|
28
|
+
const keptMark = unicode ? "✓" : "v";
|
|
29
|
+
const revertedMark = unicode ? "↶" : "r";
|
|
30
|
+
const status = data.converged
|
|
31
|
+
? "CONVERGED"
|
|
32
|
+
: data.recommendation.startsWith("continue")
|
|
33
|
+
? "CONTINUE"
|
|
34
|
+
: "STOP";
|
|
35
|
+
|
|
36
|
+
const yellow = useColor ? chalk.hex("#f2b84b") : (s: string) => s;
|
|
37
|
+
const title = useColor ? chalk.hex("#f2b84b").bold : (s: string) => s;
|
|
38
|
+
const green = useColor ? chalk.green : (s: string) => s;
|
|
39
|
+
const red = useColor ? chalk.red : (s: string) => s;
|
|
40
|
+
const cyan = useColor ? chalk.cyan : (s: string) => s;
|
|
41
|
+
const dim = useColor ? chalk.dim : (s: string) => s;
|
|
42
|
+
const statusPaint = useColor
|
|
43
|
+
? status === "CONTINUE"
|
|
44
|
+
? chalk.green.bold
|
|
45
|
+
: status === "CONVERGED"
|
|
46
|
+
? chalk.yellow.bold
|
|
47
|
+
: chalk.red.bold
|
|
48
|
+
: (s: string) => s;
|
|
49
|
+
|
|
50
|
+
const fit = (line: string) => padLineTo(truncate(line, width), width, "left");
|
|
51
|
+
const rule = yellow(ruleChar.repeat(width));
|
|
52
|
+
const score = `${dim("score")} ${cyan(data.baseline)} ${dim(arrow)} ${cyan(data.best)}`;
|
|
53
|
+
const attempts = `${dim("attempts")} ${data.attempts} · ${green(`${keptMark} ${data.kept} kept`)} · ${red(`${revertedMark} ${data.reverted} reverted`)} · patience ${data.sinceImprove}`;
|
|
54
|
+
|
|
55
|
+
return [
|
|
56
|
+
rule,
|
|
57
|
+
fit(`${title("Autopilot Ratchet")} ${statusPaint(status)}`),
|
|
58
|
+
fit(`${dim("task")} ${data.task}`),
|
|
59
|
+
fit(`${dim("eval")} ${data.goal} · ${data.eval}`),
|
|
60
|
+
fit(score),
|
|
61
|
+
fit(attempts),
|
|
62
|
+
fit(`${dim("next")} ${data.recommendation}`),
|
|
63
|
+
rule,
|
|
64
|
+
];
|
|
65
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
|
|
3
|
+
export type UiCategory = "progress" | "status" | "done" | "diff" | "subagent" | "code" | "file" | "cmd" | "tool" | "error" | "search";
|
|
4
|
+
|
|
5
|
+
interface CategoryMeta {
|
|
6
|
+
token: string;
|
|
7
|
+
label: string;
|
|
8
|
+
paint: (s: string) => string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const META: Record<UiCategory, CategoryMeta> = {
|
|
12
|
+
progress: { token: "STEP", label: "progress", paint: chalk.cyan.bold },
|
|
13
|
+
status: { token: "STATUS", label: "status", paint: chalk.cyan.bold },
|
|
14
|
+
done: { token: "DONE", label: "completed", paint: chalk.green.bold },
|
|
15
|
+
diff: { token: "DIFF", label: "diff", paint: chalk.magenta.bold },
|
|
16
|
+
subagent: { token: "AGENT", label: "subagent", paint: chalk.blue.bold },
|
|
17
|
+
code: { token: "CODE", label: "code block", paint: chalk.cyan.bold },
|
|
18
|
+
file: { token: "FILE", label: "file path", paint: chalk.yellow.bold },
|
|
19
|
+
cmd: { token: "CMD", label: "command", paint: chalk.yellow.bold },
|
|
20
|
+
tool: { token: "TOOL", label: "tool", paint: chalk.magenta.bold },
|
|
21
|
+
error: { token: "ERR", label: "error", paint: chalk.red.bold },
|
|
22
|
+
search: { token: "SRCH", label: "search", paint: chalk.green.bold },
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export function categoryMeta(category: UiCategory): { token: string; label: string } {
|
|
26
|
+
const m = META[category];
|
|
27
|
+
return { token: m.token, label: m.label };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function categoryBadge(category: UiCategory, opts: { index?: number; color?: boolean } = {}): string {
|
|
31
|
+
const m = META[category];
|
|
32
|
+
const n = typeof opts.index === "number" ? `${String(Math.max(1, Math.trunc(opts.index))).padStart(2, "0")}:` : "";
|
|
33
|
+
const raw = `[${n}${m.token}]`;
|
|
34
|
+
return opts.color === false ? raw : m.paint(raw);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function prefixCategory(category: UiCategory, text: string, opts: { index?: number; color?: boolean } = {}): string {
|
|
38
|
+
return `${categoryBadge(category, opts)} ${text}`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function categoryForTool(tool: string): UiCategory {
|
|
42
|
+
const normalized = (tool || "").toLowerCase();
|
|
43
|
+
if (normalized === "bash") return "cmd";
|
|
44
|
+
if (normalized === "read" || normalized === "write") return "file";
|
|
45
|
+
if (normalized === "edit") return "diff";
|
|
46
|
+
if (normalized === "search" || normalized === "find") return "search";
|
|
47
|
+
if (normalized === "task" || normalized.includes("agent")) return "subagent";
|
|
48
|
+
return "tool";
|
|
49
|
+
}
|