@ocis/myagent-cli 0.2.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.
Files changed (91) hide show
  1. package/README.md +357 -0
  2. package/dist/agent/context.d.ts +33 -0
  3. package/dist/agent/context.js +169 -0
  4. package/dist/agent/modes.d.ts +21 -0
  5. package/dist/agent/modes.js +84 -0
  6. package/dist/agent/prompt-builder.d.ts +9 -0
  7. package/dist/agent/prompt-builder.js +31 -0
  8. package/dist/agent/sessions.d.ts +38 -0
  9. package/dist/agent/sessions.js +130 -0
  10. package/dist/agent/todo.d.ts +18 -0
  11. package/dist/agent/todo.js +61 -0
  12. package/dist/agent/turn.d.ts +309 -0
  13. package/dist/agent/turn.js +1253 -0
  14. package/dist/approval/policy.d.ts +81 -0
  15. package/dist/approval/policy.js +157 -0
  16. package/dist/config.d.ts +49 -0
  17. package/dist/config.js +156 -0
  18. package/dist/git/status.d.ts +89 -0
  19. package/dist/git/status.js +226 -0
  20. package/dist/headless.d.ts +72 -0
  21. package/dist/headless.js +330 -0
  22. package/dist/index.d.ts +60 -0
  23. package/dist/index.js +511 -0
  24. package/dist/protocol/client.d.ts +123 -0
  25. package/dist/protocol/client.js +250 -0
  26. package/dist/protocol/sse-frames.d.ts +6 -0
  27. package/dist/protocol/sse-frames.js +75 -0
  28. package/dist/protocol/types.d.ts +200 -0
  29. package/dist/protocol/types.js +8 -0
  30. package/dist/runtime.d.ts +38 -0
  31. package/dist/runtime.js +166 -0
  32. package/dist/sanitize.d.ts +1 -0
  33. package/dist/sanitize.js +21 -0
  34. package/dist/skills/discovery.d.ts +24 -0
  35. package/dist/skills/discovery.js +109 -0
  36. package/dist/tools/binary.d.ts +2 -0
  37. package/dist/tools/binary.js +22 -0
  38. package/dist/tools/diff.d.ts +1 -0
  39. package/dist/tools/diff.js +49 -0
  40. package/dist/tools/find.d.ts +2 -0
  41. package/dist/tools/find.js +61 -0
  42. package/dist/tools/fs.d.ts +2 -0
  43. package/dist/tools/fs.js +276 -0
  44. package/dist/tools/glob.d.ts +6 -0
  45. package/dist/tools/glob.js +131 -0
  46. package/dist/tools/grep.d.ts +3 -0
  47. package/dist/tools/grep.js +228 -0
  48. package/dist/tools/paths.d.ts +27 -0
  49. package/dist/tools/paths.js +124 -0
  50. package/dist/tools/registry.d.ts +13 -0
  51. package/dist/tools/registry.js +38 -0
  52. package/dist/tools/shell.d.ts +2 -0
  53. package/dist/tools/shell.js +136 -0
  54. package/dist/tools/skills.d.ts +2 -0
  55. package/dist/tools/skills.js +36 -0
  56. package/dist/tools/todo.d.ts +2 -0
  57. package/dist/tools/todo.js +43 -0
  58. package/dist/tools/transfer.d.ts +2 -0
  59. package/dist/tools/transfer.js +145 -0
  60. package/dist/tools/truncate.d.ts +12 -0
  61. package/dist/tools/truncate.js +46 -0
  62. package/dist/tools/types.d.ts +85 -0
  63. package/dist/tools/types.js +63 -0
  64. package/dist/ui/app.d.ts +39 -0
  65. package/dist/ui/app.js +1061 -0
  66. package/dist/ui/colors.d.ts +100 -0
  67. package/dist/ui/colors.js +169 -0
  68. package/dist/ui/components.d.ts +267 -0
  69. package/dist/ui/components.js +811 -0
  70. package/dist/ui/diff.d.ts +37 -0
  71. package/dist/ui/diff.js +143 -0
  72. package/dist/ui/format.d.ts +28 -0
  73. package/dist/ui/format.js +76 -0
  74. package/dist/ui/help.d.ts +6 -0
  75. package/dist/ui/help.js +45 -0
  76. package/dist/ui/highlight.d.ts +20 -0
  77. package/dist/ui/highlight.js +210 -0
  78. package/dist/ui/logo.d.ts +24 -0
  79. package/dist/ui/logo.js +106 -0
  80. package/dist/ui/model-list.d.ts +10 -0
  81. package/dist/ui/model-list.js +33 -0
  82. package/dist/ui/quit-confirm.d.ts +8 -0
  83. package/dist/ui/quit-confirm.js +40 -0
  84. package/dist/ui/select-popup.d.ts +30 -0
  85. package/dist/ui/select-popup.js +54 -0
  86. package/dist/ui/theme.d.ts +4 -0
  87. package/dist/ui/theme.js +41 -0
  88. package/dist/ui/tool-view.d.ts +20 -0
  89. package/dist/ui/tool-view.js +326 -0
  90. package/package.json +44 -0
  91. package/skills/git-commit/SKILL.md +27 -0
@@ -0,0 +1,100 @@
1
+ import type { RgbColor } from "@earendil-works/pi-tui";
2
+ export declare function isColorEnabled(): boolean;
3
+ export declare function rgb(r: number, g: number, b: number, text: string): string;
4
+ export declare const style: {
5
+ bold: (s: string) => string;
6
+ dim: (s: string) => string;
7
+ italic: (s: string) => string;
8
+ underline: (s: string) => string;
9
+ red: (s: string) => string;
10
+ green: (s: string) => string;
11
+ yellow: (s: string) => string;
12
+ cyan: (s: string) => string;
13
+ };
14
+ /** Plan mode — orange (clearly separate hue from coding's green). */
15
+ export declare const PLAN_COLOR: (s: string) => string;
16
+ /** Coding mode — green, muted so the prompt frame doesn't glare. */
17
+ export declare const CODING_COLOR: (s: string) => string;
18
+ /** Cowork mode (general tasks) — purple. */
19
+ export declare const COWORK_COLOR: (s: string) => string;
20
+ /** Muted gray for panel content — kept low-contrast on the tinted panel. */
21
+ export declare const GRAY_COLOR: (s: string) => string;
22
+ /** Panel title gray — just a step brighter than the content gray. */
23
+ export declare const TITLE_COLOR: (s: string) => string;
24
+ /** Session title (the sidebar's headline) — the panel's brightest text, a
25
+ * step above the section-title gray so the current session reads at a glance. */
26
+ export declare const SESSION_TITLE_COLOR: (s: string) => string;
27
+ /** Markdown headings — bright light-blue (the CONTEXT section hue), clearly
28
+ * brighter than bold prose so headings read as headings. */
29
+ export declare const headingColor: (s: string) => string;
30
+ /** Dimmer gray for secondary/status text. */
31
+ export declare const GRAY_DIM_COLOR: (s: string) => string;
32
+ /** Border color for the editor, by mode. */
33
+ export declare function modeBorder(mode: "plan" | "coding" | "cowork"): (s: string) => string;
34
+ /** AUTO badge — amber so "running unattended" reads at a glance (ASK stays gray). */
35
+ export declare const AUTO_COLOR: (s: string) => string;
36
+ /** Diff line colors (rgb() no-ops when color is disabled). */
37
+ export declare const diffAdd: (s: string) => string;
38
+ export declare const diffDel: (s: string) => string;
39
+ /** Section hues — title text + the tint of the full-width header band. */
40
+ declare const SECTION_HUES: {
41
+ readonly context: {
42
+ readonly r: 125;
43
+ readonly g: 211;
44
+ readonly b: 252;
45
+ };
46
+ readonly todo: {
47
+ readonly r: 196;
48
+ readonly g: 181;
49
+ readonly b: 253;
50
+ };
51
+ readonly changes: {
52
+ readonly r: 253;
53
+ readonly g: 186;
54
+ readonly b: 116;
55
+ };
56
+ };
57
+ export type SectionName = keyof typeof SECTION_HUES;
58
+ /** Panel tint used when the terminal background cannot be detected. */
59
+ export declare const DEFAULT_PANEL_BG: RgbColor;
60
+ /**
61
+ * Panel background: a few percent lighter than a dark terminal background,
62
+ * a few percent darker than a light one (so it stays visible either way).
63
+ */
64
+ export declare function panelBackgroundRgb(terminalBg?: RgbColor): RgbColor;
65
+ /**
66
+ * Popup background: a stronger tint than the panel so a floating card reads
67
+ * above the transcript. Undefined when the terminal background is unknown —
68
+ * the card then falls back to its border alone (never assumes dark or light).
69
+ */
70
+ export declare function overlayBackgroundRgb(terminalBg?: RgbColor): RgbColor | undefined;
71
+ /**
72
+ * User-message band: a subtle tint so the prompt reads as its own block.
73
+ * Undefined without a terminal background — the message then renders untinted.
74
+ */
75
+ export declare function userMessageBackgroundRgb(terminalBg?: RgbColor): RgbColor | undefined;
76
+ /** User-message text — softer than the terminal default so prompts don't glare. */
77
+ export declare const userMessageColor: (s: string) => string;
78
+ /** User-message accent bar (violet, matching the logo gradient). */
79
+ export declare const userMessageAccent: (s: string) => string;
80
+ /** Tool output content — one step darker than the shared content gray. */
81
+ export declare const toolOutputColor: (s: string) => string;
82
+ /** Apply a background color to `text` (no-op when color is disabled). */
83
+ export declare function bgRgb(color: RgbColor, text: string): string;
84
+ export declare function paintLine(text: string, width: number, bg?: RgbColor): string;
85
+ /** Assistant prose tone — one step grayer than the terminal's default foreground. */
86
+ export declare const assistantColor: (s: string) => string;
87
+ /** Keyword hue (declarations, control flow). */
88
+ export declare const syntaxKeyword: (s: string) => string;
89
+ /** String / template literal hue. */
90
+ export declare const syntaxString: (s: string) => string;
91
+ /** Numeric literal hue. */
92
+ export declare const syntaxNumber: (s: string) => string;
93
+ /** Comment hue — muted and italic so it recedes. */
94
+ export declare const syntaxComment: (s: string) => string;
95
+ /**
96
+ * Foreground style for the section's accent bar — the hue muted toward the
97
+ * panel tint so it reads as an accent, not a highlight.
98
+ */
99
+ export declare function sectionFg(name: SectionName, panel?: RgbColor): (s: string) => string;
100
+ export {};
@@ -0,0 +1,169 @@
1
+ // -------------------------------------------------------------------
2
+ // ANSI styling for the TUI.
3
+ //
4
+ // pi-tui components take style callbacks, so this module is the single place
5
+ // that decides whether color is available (TTY + NO_COLOR + TERM) and how the
6
+ // mode/approval palettes map to SGR sequences.
7
+ // -------------------------------------------------------------------
8
+ import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
9
+ const colorEnabled = Boolean(process.stdout.isTTY) &&
10
+ !process.env.NO_COLOR &&
11
+ process.env.TERM !== "dumb";
12
+ export function isColorEnabled() {
13
+ return colorEnabled;
14
+ }
15
+ function sgr(open, close, text) {
16
+ if (!colorEnabled)
17
+ return text;
18
+ return `\u001b[${open}m${text}\u001b[${close}m`;
19
+ }
20
+ export function rgb(r, g, b, text) {
21
+ return sgr(`38;2;${r};${g};${b}`, "39", text);
22
+ }
23
+ export const style = {
24
+ bold: (s) => sgr("1", "22", s),
25
+ dim: (s) => sgr("2", "22", s),
26
+ italic: (s) => sgr("3", "23", s),
27
+ underline: (s) => sgr("4", "24", s),
28
+ red: (s) => sgr("31", "39", s),
29
+ green: (s) => sgr("32", "39", s),
30
+ yellow: (s) => sgr("33", "39", s),
31
+ cyan: (s) => sgr("36", "39", s),
32
+ };
33
+ // ---------------------------------------------------------------------------
34
+ // Semantic palettes
35
+ // ---------------------------------------------------------------------------
36
+ /** Plan mode — orange (clearly separate hue from coding's green). */
37
+ export const PLAN_COLOR = (s) => rgb(0xf9, 0x73, 0x16, s);
38
+ /** Coding mode — green, muted so the prompt frame doesn't glare. */
39
+ export const CODING_COLOR = (s) => rgb(0x5f, 0xbe, 0x82, s);
40
+ /** Cowork mode (general tasks) — purple. */
41
+ export const COWORK_COLOR = (s) => rgb(0xa7, 0x8b, 0xfa, s);
42
+ /** Muted gray for panel content — kept low-contrast on the tinted panel. */
43
+ export const GRAY_COLOR = (s) => rgb(0x7f, 0x87, 0x94, s);
44
+ /** Panel title gray — just a step brighter than the content gray. */
45
+ export const TITLE_COLOR = (s) => rgb(0x9a, 0xa2, 0xae, s);
46
+ /** Session title (the sidebar's headline) — the panel's brightest text, a
47
+ * step above the section-title gray so the current session reads at a glance. */
48
+ export const SESSION_TITLE_COLOR = (s) => rgb(0xc2, 0xc8, 0xd2, s);
49
+ /** Markdown headings — bright light-blue (the CONTEXT section hue), clearly
50
+ * brighter than bold prose so headings read as headings. */
51
+ export const headingColor = (s) => rgb(0x7d, 0xd3, 0xfc, s);
52
+ /** Dimmer gray for secondary/status text. */
53
+ export const GRAY_DIM_COLOR = (s) => rgb(0x59, 0x61, 0x6e, s);
54
+ /** Border color for the editor, by mode. */
55
+ export function modeBorder(mode) {
56
+ if (mode === "plan")
57
+ return PLAN_COLOR;
58
+ return mode === "coding" ? CODING_COLOR : COWORK_COLOR;
59
+ }
60
+ /** AUTO badge — amber so "running unattended" reads at a glance (ASK stays gray). */
61
+ export const AUTO_COLOR = (s) => rgb(0xf9, 0xc7, 0x4a, s);
62
+ /** Diff line colors (rgb() no-ops when color is disabled). */
63
+ export const diffAdd = (s) => rgb(0x4a, 0xde, 0x80, s);
64
+ export const diffDel = (s) => rgb(0xf8, 0x71, 0x71, s);
65
+ // ---------------------------------------------------------------------------
66
+ // Panel + sidebar sections
67
+ // ---------------------------------------------------------------------------
68
+ /** Section hues — title text + the tint of the full-width header band. */
69
+ const SECTION_HUES = {
70
+ context: { r: 0x7d, g: 0xd3, b: 0xfc },
71
+ todo: { r: 0xc4, g: 0xb5, b: 0xfd },
72
+ changes: { r: 0xfd, g: 0xba, b: 0x74 },
73
+ };
74
+ /** Panel tint used when the terminal background cannot be detected. */
75
+ export const DEFAULT_PANEL_BG = { r: 0x22, g: 0x26, b: 0x2e };
76
+ function clamp255(n) {
77
+ return Math.max(0, Math.min(255, Math.round(n)));
78
+ }
79
+ function mix(a, b, weight) {
80
+ return {
81
+ r: clamp255(a.r + (b.r - a.r) * weight),
82
+ g: clamp255(a.g + (b.g - a.g) * weight),
83
+ b: clamp255(a.b + (b.b - a.b) * weight),
84
+ };
85
+ }
86
+ function luminance(color) {
87
+ return (0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b) / 255;
88
+ }
89
+ /** Whichever end of the scale a terminal background tints toward. */
90
+ function contrastTarget(terminalBg) {
91
+ return luminance(terminalBg) < 0.5
92
+ ? { r: 0xff, g: 0xff, b: 0xff }
93
+ : { r: 0x00, g: 0x00, b: 0x00 };
94
+ }
95
+ /** Mix the terminal background toward its contrast target by `ratio`. */
96
+ function tint(terminalBg, ratio) {
97
+ return terminalBg ? mix(terminalBg, contrastTarget(terminalBg), ratio) : undefined;
98
+ }
99
+ /**
100
+ * Panel background: a few percent lighter than a dark terminal background,
101
+ * a few percent darker than a light one (so it stays visible either way).
102
+ */
103
+ export function panelBackgroundRgb(terminalBg) {
104
+ return tint(terminalBg, 0.06) ?? DEFAULT_PANEL_BG;
105
+ }
106
+ /**
107
+ * Popup background: a stronger tint than the panel so a floating card reads
108
+ * above the transcript. Undefined when the terminal background is unknown —
109
+ * the card then falls back to its border alone (never assumes dark or light).
110
+ */
111
+ export function overlayBackgroundRgb(terminalBg) {
112
+ return tint(terminalBg, 0.12);
113
+ }
114
+ /**
115
+ * User-message band: a subtle tint so the prompt reads as its own block.
116
+ * Undefined without a terminal background — the message then renders untinted.
117
+ */
118
+ export function userMessageBackgroundRgb(terminalBg) {
119
+ return tint(terminalBg, 0.06);
120
+ }
121
+ /** User-message text — softer than the terminal default so prompts don't glare. */
122
+ export const userMessageColor = (s) => rgb(0xc2, 0xc8, 0xd2, s);
123
+ /** User-message accent bar (violet, matching the logo gradient). */
124
+ export const userMessageAccent = (s) => rgb(0xc0, 0x84, 0xfc, s);
125
+ /** Tool output content — one step darker than the shared content gray. */
126
+ export const toolOutputColor = (s) => rgb(0x6b, 0x73, 0x80, s);
127
+ /** Apply a background color to `text` (no-op when color is disabled). */
128
+ export function bgRgb(color, text) {
129
+ return sgr(`48;2;${color.r};${color.g};${color.b}`, "49", text);
130
+ }
131
+ /**
132
+ * Clamp `text` to `width` and, when `bg` is set, tint the full line —
133
+ * re-opening the tint after any inner reset so the band stays solid.
134
+ *
135
+ * Inner resets come in two forms: the generic `\u001b[0m` (pasted content)
136
+ * and the paired `\u001b[49m` this module's own bgRgb emits as its close
137
+ * code (nested paintLine output). Splitting on both keeps the band solid
138
+ * either way; the join normalizes to the generic reset.
139
+ */
140
+ const INNER_RESET = /\u001b\[(?:0|49)m/g;
141
+ export function paintLine(text, width, bg) {
142
+ const safeWidth = Math.max(1, width);
143
+ const clipped = truncateToWidth(text, safeWidth, "…", true);
144
+ const padded = clipped + " ".repeat(Math.max(0, safeWidth - visibleWidth(clipped)));
145
+ if (!bg)
146
+ return padded;
147
+ return padded.split(INNER_RESET).map((segment) => bgRgb(bg, segment)).join("\u001b[0m");
148
+ }
149
+ /** Assistant prose tone — one step grayer than the terminal's default foreground. */
150
+ export const assistantColor = (s) => rgb(0xb1, 0xb8, 0xc4, s);
151
+ // ---------------------------------------------------------------------------
152
+ // Syntax highlighting (tool output)
153
+ // ---------------------------------------------------------------------------
154
+ /** Keyword hue (declarations, control flow). */
155
+ export const syntaxKeyword = (s) => rgb(0xc6, 0x78, 0xdd, s);
156
+ /** String / template literal hue. */
157
+ export const syntaxString = (s) => rgb(0x98, 0xc3, 0x79, s);
158
+ /** Numeric literal hue. */
159
+ export const syntaxNumber = (s) => rgb(0xd1, 0x9a, 0x66, s);
160
+ /** Comment hue — muted and italic so it recedes. */
161
+ export const syntaxComment = (s) => style.italic(rgb(0x5c, 0x63, 0x70, s));
162
+ /**
163
+ * Foreground style for the section's accent bar — the hue muted toward the
164
+ * panel tint so it reads as an accent, not a highlight.
165
+ */
166
+ export function sectionFg(name, panel = DEFAULT_PANEL_BG) {
167
+ const color = mix(SECTION_HUES[name], panel, 0.25);
168
+ return (s) => rgb(color.r, color.g, color.b, s);
169
+ }
@@ -0,0 +1,267 @@
1
+ import type { Component, EditorTheme, RgbColor, SelectList, TUI, TuiMouseEvent, TuiMouseEventResult } from "@earendil-works/pi-tui";
2
+ import { Editor } from "@earendil-works/pi-tui";
3
+ import type { AgentMode } from "../agent/modes.js";
4
+ import type { ApprovalMode } from "../approval/policy.js";
5
+ import type { TodoStore } from "../agent/todo.js";
6
+ import type { GitFileChange, GitStatus } from "../git/status.js";
7
+ import type { AgentUsage } from "../protocol/types.js";
8
+ import type { ApprovalRequest } from "../tools/types.js";
9
+ /**
10
+ * Messages submitted while a run is active, floating between the transcript
11
+ * and the prompt editor. A steered message is NOT part of the conversation
12
+ * until the server takes it — until then it stays here, one dim preview line
13
+ * per pending message, and is released (into the transcript) on acceptance
14
+ * or when a re-queued follow-up turn actually starts.
15
+ */
16
+ export declare class QueuedMessagesComponent implements Component {
17
+ private entries;
18
+ invalidate(): void;
19
+ add(text: string): void;
20
+ /** Release the oldest entry with exactly this text (FIFO — steering the
21
+ * same text twice releases one float per acceptance). */
22
+ release(text: string): void;
23
+ render(width: number): string[];
24
+ }
25
+ /**
26
+ * A submitted prompt: a leading blank row (margin from the previous message),
27
+ * a violet block bar down the left edge, and a content-sized tinted bubble
28
+ * (with one row of vertical padding) when the terminal background is known.
29
+ */
30
+ export declare class UserMessageComponent implements Component {
31
+ private readonly text;
32
+ private readonly queued;
33
+ private readonly options;
34
+ constructor(text: string, queued: boolean, options?: {
35
+ bg?: () => RgbColor | undefined;
36
+ });
37
+ invalidate(): void;
38
+ render(width: number): string[];
39
+ }
40
+ export declare class AssistantMessageComponent implements Component {
41
+ private readonly md;
42
+ /** Raw stream (unsanitized) — the sanitizing pass needs the whole text. */
43
+ private raw;
44
+ private text;
45
+ constructor();
46
+ append(delta: string): void;
47
+ get content(): string;
48
+ render(width: number): string[];
49
+ invalidate(): void;
50
+ }
51
+ export declare class ReasoningBlockComponent implements Component {
52
+ private raw;
53
+ private text;
54
+ expanded: boolean;
55
+ append(delta: string): void;
56
+ toggle(): void;
57
+ get hasContent(): boolean;
58
+ invalidate(): void;
59
+ render(width: number): string[];
60
+ }
61
+ export declare class ToolCallComponent implements Component {
62
+ readonly name: string;
63
+ private readonly args;
64
+ status: "running" | "done" | "error";
65
+ expanded: boolean;
66
+ private output;
67
+ private bodyCache?;
68
+ constructor(name: string, args: Record<string, unknown>);
69
+ setResult(output: string, isError: boolean): void;
70
+ toggle(): void;
71
+ /** The whole row is the button: press is consumed, the release click toggles. */
72
+ handleMouse(event: TuiMouseEvent): TuiMouseEventResult | undefined;
73
+ invalidate(): void;
74
+ /** Tool-specific expanded body, cached per width/result (renders on spinner ticks). */
75
+ private body;
76
+ render(width: number): string[];
77
+ }
78
+ export declare class NoticeComponent implements Component {
79
+ private readonly text;
80
+ private readonly level;
81
+ constructor(text: string, level: "info" | "error" | "warn");
82
+ invalidate(): void;
83
+ render(width: number): string[];
84
+ }
85
+ export interface SidebarState {
86
+ title: string | null;
87
+ usage: AgentUsage | null;
88
+ git: GitStatus;
89
+ }
90
+ /** A clickable row in the panel (content-space line index). */
91
+ export type SidebarTarget = {
92
+ type: "file";
93
+ file: GitFileChange;
94
+ } | {
95
+ type: "toggle-changes";
96
+ };
97
+ export interface SidebarOptions {
98
+ todos: TodoStore;
99
+ /**
100
+ * Viewport height of the enclosing ScrollView. Short content is padded to it
101
+ * so the panel background reaches the bottom of the column (never padded
102
+ * beyond it, which would break scrolling for long content).
103
+ */
104
+ viewportHeight?: () => number;
105
+ /**
106
+ * Panel background, resolved asynchronously from the terminal's OSC 11
107
+ * reply. Until it exists (or when color is unavailable) no background is
108
+ * painted.
109
+ */
110
+ panelBg?: () => RgbColor | undefined;
111
+ }
112
+ export declare class SidebarComponent implements Component {
113
+ state: SidebarState;
114
+ private readonly opts;
115
+ private expanded;
116
+ private readonly targets;
117
+ constructor(state: SidebarState, opts: SidebarOptions);
118
+ invalidate(): void;
119
+ toggleChanges(): void;
120
+ /** What a click at content line `y` hits (coordinates include scrollTop). */
121
+ targetAt(y: number): SidebarTarget | undefined;
122
+ render(width: number): string[];
123
+ }
124
+ /**
125
+ * Editor wrapped in a rounded four-sided frame whose bottom edge carries the
126
+ * mode badge (bold, same color as the frame) followed by the model and
127
+ * thinking level. The content is inset by `paddingX`; the frame has no
128
+ * background of its own.
129
+ */
130
+ /** Clickable status chips on the prompt frame's bottom edge. */
131
+ export type EditorChip = "mode" | "model" | "thinking";
132
+ export declare class PromptEditor extends Editor {
133
+ mode: AgentMode;
134
+ model: string | null;
135
+ thinking: string | undefined;
136
+ /** Fired when a bottom-edge chip (mode / model / thinking) is clicked. */
137
+ onChipClick?: (chip: EditorChip) => void;
138
+ /** Chip column extents from the last bottom-edge render, for hit-testing. */
139
+ private chipRanges;
140
+ /** Bottom-edge row in this component's coordinates (-1 = no bottom edge). */
141
+ private bottomBorderRow;
142
+ /**
143
+ * The exact bottom-edge line rendered this frame. render() locates the
144
+ * edge by identity against it — scanning for a "╰" substring would misfire
145
+ * on user content that happens to contain the border glyph.
146
+ */
147
+ private lastBottomBorder;
148
+ constructor(tui: TUI, theme: EditorTheme, mode: AgentMode, thinking?: string);
149
+ protected renderTopBorder(width: number, _hiddenLineCount: number): string;
150
+ /**
151
+ * Alt+Enter inserts a newline exactly like Shift+Enter — the base editor
152
+ * only knows Shift+Enter/Ctrl+J, but terminals send Alt+Enter as ESC+CR
153
+ * (legacy) or a Kitty/modifyOtherKeys sequence.
154
+ */
155
+ handleInput(data: string): void;
156
+ protected renderBottomBorder(width: number, hiddenLineCount: number): string;
157
+ /**
158
+ * Mouse dispatch for the framed editor. The bottom edge carries the
159
+ * mode/model/thinking chips; every other event is translated into the base
160
+ * editor's coordinate system — our frame inserts a padding row below the
161
+ * top edge and above the bottom edge, and the side borders shift text rows
162
+ * one column right, so without the shift the base's cursor/autocomplete
163
+ * hit-testing lands one row and one column off.
164
+ */
165
+ handleMouse(event: TuiMouseEvent): TuiMouseEventResult | undefined;
166
+ render(width: number): string[];
167
+ }
168
+ export interface FooterState {
169
+ approval: ApprovalMode;
170
+ workspace: string;
171
+ branch: string | null;
172
+ usage: AgentUsage | null;
173
+ runState: "idle" | "running" | "stopping";
174
+ spinnerFrame: number;
175
+ /** Set after the first idle Ctrl+C: the next one inside the window exits. */
176
+ pendingQuit?: boolean;
177
+ }
178
+ export declare class FooterComponent implements Component {
179
+ state: FooterState;
180
+ private readonly onApprovalClick?;
181
+ /** Fired when the ASK/AUTO badge is clicked. */
182
+ constructor(state: FooterState, onApprovalClick?: (() => void) | undefined);
183
+ invalidate(): void;
184
+ handleMouse(event: TuiMouseEvent): TuiMouseEventResult | undefined;
185
+ render(width: number): string[];
186
+ private compactPath;
187
+ }
188
+ /** Chrome shared by the popup cards: background tint + bottom-edge hint. */
189
+ export interface PopupCardOptions {
190
+ /** Live background tint; undefined leaves the card unfilled. */
191
+ bg?: () => RgbColor | undefined;
192
+ /** Dim hint rendered in the bottom edge. */
193
+ hint?: string;
194
+ }
195
+ /**
196
+ * A pending approval, floating between the transcript and the prompt (NOT a
197
+ * modal overlay — the editor keeps focus, so typing stays possible while the
198
+ * user decides). Answered with ctrl+y / ctrl+n from the app's global key
199
+ * listener; the card itself is render-only.
200
+ */
201
+ export declare class ApprovalFloatComponent implements Component {
202
+ private request;
203
+ invalidate(): void;
204
+ /** Show (or clear) the approval being decided. */
205
+ set(request: ApprovalRequest | null): void;
206
+ render(width: number): string[];
207
+ }
208
+ export declare class InfoOverlay implements Component {
209
+ private readonly title;
210
+ private readonly body;
211
+ private readonly close;
212
+ private readonly options;
213
+ constructor(title: string, body: string[], close: () => void, options?: PopupCardOptions);
214
+ handleInput(data: string): void;
215
+ invalidate(): void;
216
+ render(width: number): string[];
217
+ }
218
+ /**
219
+ * Scrollable popup for a single file's unified diff. Overlays are clipped, not
220
+ * scrolled by the framework, so the window is managed here: the popup renders
221
+ * exactly `viewportHeight() + 2` lines (top edge, body, bottom edge) and moves the
222
+ * window with the arrow/page keys.
223
+ */
224
+ export declare class DiffOverlay implements Component {
225
+ private readonly title;
226
+ private readonly options;
227
+ private scrollTop;
228
+ private mode;
229
+ private rowCount;
230
+ /** Content rows shown in the viewer (file headers and hunk headers hidden). */
231
+ private readonly visible;
232
+ private cache?;
233
+ constructor(title: string, diff: string, options: {
234
+ /** Live body-row budget, re-read every render so the card refits on resize. */
235
+ viewportHeight: () => number;
236
+ truncated: boolean;
237
+ close: () => void;
238
+ });
239
+ private viewHeight;
240
+ private get maxTop();
241
+ private scrollBy;
242
+ /** Paint one diff line, using the same palette as the transcript. */
243
+ private paint;
244
+ private buildRows;
245
+ private rows;
246
+ handleInput(data: string): void;
247
+ /** The mouse wheel scrolls the diff like the arrow keys do. */
248
+ handleMouse(event: TuiMouseEvent): TuiMouseEventResult | undefined;
249
+ invalidate(): void;
250
+ render(width: number): string[];
251
+ }
252
+ /**
253
+ * Select list inside a popup card, usable as a focused overlay: input and
254
+ * mouse events are forwarded to the inner SelectList (a bare Container would
255
+ * swallow them), with the card's top edge and padding row skipped in the
256
+ * mouse coordinates (renderCard: edge, blank, then list rows).
257
+ */
258
+ export declare class SelectOverlay implements Component {
259
+ private readonly title;
260
+ private readonly list;
261
+ private readonly options;
262
+ constructor(title: string, list: SelectList, options?: PopupCardOptions);
263
+ handleInput(data: string): void;
264
+ handleMouse(event: TuiMouseEvent): TuiMouseEventResult | undefined;
265
+ invalidate(): void;
266
+ render(width: number): string[];
267
+ }