codemaxxing 1.0.16 → 1.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.
@@ -0,0 +1,21 @@
1
+ import { EventEmitter } from "events";
2
+ export interface PasteEvent {
3
+ content: string;
4
+ lines: number;
5
+ }
6
+ export type PasteEventBus = EventEmitter;
7
+ /**
8
+ * Sets up the full paste interception pipeline on process.stdin:
9
+ *
10
+ * - Enables bracketed paste mode on the terminal
11
+ * - Patches stdin emit('data') to intercept all incoming data
12
+ * - Detects multiline pastes via bracketed paste escape sequences
13
+ * - Falls back to burst buffering for terminals without bracketed paste
14
+ * - Strips paste marker artifacts from all chunks
15
+ * - Emits "paste" events on the returned EventEmitter for multiline content
16
+ * - Forwards short pastes (1-2 lines) as normal stdin data
17
+ * - Registers an exit handler to disable bracketed paste mode
18
+ *
19
+ * Returns the EventEmitter that fires "paste" events with { content, lines }.
20
+ */
21
+ export declare function setupPasteInterceptor(): PasteEventBus;
@@ -0,0 +1,179 @@
1
+ import { EventEmitter } from "events";
2
+ import { appendFileSync } from "node:fs";
3
+ import { consumePendingPasteEndMarkerChunk, shouldSwallowPostPasteDebris } from "../utils/paste.js";
4
+ /**
5
+ * Sets up the full paste interception pipeline on process.stdin:
6
+ *
7
+ * - Enables bracketed paste mode on the terminal
8
+ * - Patches stdin emit('data') to intercept all incoming data
9
+ * - Detects multiline pastes via bracketed paste escape sequences
10
+ * - Falls back to burst buffering for terminals without bracketed paste
11
+ * - Strips paste marker artifacts from all chunks
12
+ * - Emits "paste" events on the returned EventEmitter for multiline content
13
+ * - Forwards short pastes (1-2 lines) as normal stdin data
14
+ * - Registers an exit handler to disable bracketed paste mode
15
+ *
16
+ * Returns the EventEmitter that fires "paste" events with { content, lines }.
17
+ */
18
+ export function setupPasteInterceptor() {
19
+ const pasteEvents = new EventEmitter();
20
+ // Enable bracketed paste mode — terminal wraps pastes in escape sequences
21
+ process.stdout.write("\x1b[?2004h");
22
+ // ── Internal state ──
23
+ let bracketedBuffer = "";
24
+ let inBracketedPaste = false;
25
+ let burstBuffer = "";
26
+ let burstTimer = null;
27
+ let pendingPasteEndMarker = { active: false, buffer: "" };
28
+ let swallowPostPasteDebrisUntil = 0;
29
+ const BURST_WINDOW_MS = 50; // Long enough for slow terminals to finish delivering paste
30
+ const POST_PASTE_DEBRIS_WINDOW_MS = 1200;
31
+ // Debug paste: set CODEMAXXING_DEBUG_PASTE=1 to log all stdin chunks to /tmp/codemaxxing-paste-debug.log
32
+ const PASTE_DEBUG = process.env.CODEMAXXING_DEBUG_PASTE === "1";
33
+ function pasteLog(msg) {
34
+ if (!PASTE_DEBUG)
35
+ return;
36
+ const escaped = msg.replace(/\x1b/g, "\\x1b").replace(/\r/g, "\\r").replace(/\n/g, "\\n");
37
+ try {
38
+ appendFileSync("/tmp/codemaxxing-paste-debug.log", `[${Date.now()}] ${escaped}\n`);
39
+ }
40
+ catch { }
41
+ }
42
+ const origEmit = process.stdin.emit.bind(process.stdin);
43
+ function handlePasteContent(content) {
44
+ const normalized = content.replace(/\r\n/g, "\n").replace(/\r/g, "\n").trim();
45
+ if (!normalized)
46
+ return;
47
+ const lineCount = normalized.split("\n").length;
48
+ if (lineCount > 2) {
49
+ // Real multiline paste → badge it
50
+ // Some terminals dribble the closing bracketed-paste marker (`[201~`)
51
+ // one character at a time *after* the paste payload. Arm a tiny
52
+ // swallow-state so those trailing fragments never leak into the input.
53
+ pendingPasteEndMarker = { active: true, buffer: "" };
54
+ swallowPostPasteDebrisUntil = Date.now() + POST_PASTE_DEBRIS_WINDOW_MS;
55
+ pasteEvents.emit("paste", { content: normalized, lines: lineCount });
56
+ return;
57
+ }
58
+ // Short paste (1-2 lines) → collapse to single line and forward as normal input
59
+ const sanitized = normalized.replace(/\n/g, " ");
60
+ if (sanitized) {
61
+ origEmit("data", sanitized);
62
+ }
63
+ }
64
+ function looksLikeMultilinePaste(data) {
65
+ const clean = data.replace(/\x1b\[[0-9;]*[A-Za-z]/g, ""); // Strip all ANSI escapes
66
+ // Count \r\n, \n, and bare \r as line breaks (macOS terminals often use bare \r)
67
+ const normalized = clean.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
68
+ const newlines = (normalized.match(/\n/g) ?? []).length;
69
+ const printable = normalized.replace(/\n/g, "").trim().length;
70
+ return newlines >= 2 || (newlines >= 1 && printable >= 40);
71
+ }
72
+ function flushBurst() {
73
+ if (!burstBuffer)
74
+ return;
75
+ let buffered = burstBuffer;
76
+ burstBuffer = "";
77
+ // Strip any bracketed paste marker fragments that accumulated across
78
+ // individual character chunks (terminal sends [, 2, 0, 1, ~ separately)
79
+ buffered = buffered.replace(/\x1b?\[?20[01]~/g, "");
80
+ buffered = buffered.replace(/20[01]~/g, "");
81
+ if (!buffered || !buffered.trim()) {
82
+ pasteLog("BURST FLUSH stripped to empty — swallowed marker");
83
+ return;
84
+ }
85
+ const isMultiline = looksLikeMultilinePaste(buffered);
86
+ pasteLog(`BURST FLUSH len=${buffered.length} multiline=${isMultiline}`);
87
+ if (isMultiline) {
88
+ handlePasteContent(buffered);
89
+ }
90
+ else {
91
+ // Normal typing — forward to Ink
92
+ origEmit("data", buffered);
93
+ }
94
+ }
95
+ // Patch emit('data') — the ONE path all data must travel through to reach
96
+ // Ink's listeners, regardless of how the TTY/stream delivers it internally.
97
+ //
98
+ // Two detection layers:
99
+ // 1. Bracketed paste escape sequences (\x1b[200~ ... \x1b[201~)
100
+ // 2. Burst buffering — accumulate rapid-fire chunks over a short window and check
101
+ // whether the combined content looks like a multiline paste.
102
+ process.stdin.emit = function (event, ...args) {
103
+ // Pass through non-data events untouched
104
+ if (event !== "data") {
105
+ return origEmit(event, ...args);
106
+ }
107
+ const chunk = args[0];
108
+ let data = typeof chunk === "string" ? chunk : Buffer.isBuffer(chunk) ? chunk.toString("utf-8") : String(chunk);
109
+ pasteLog(`CHUNK len=${data.length} raw=${data.substring(0, 200)}`);
110
+ const pendingResult = consumePendingPasteEndMarkerChunk(data, pendingPasteEndMarker);
111
+ pendingPasteEndMarker = pendingResult.nextState;
112
+ data = pendingResult.remaining;
113
+ if (!data) {
114
+ pasteLog("PENDING END MARKER swallowed chunk");
115
+ return true;
116
+ }
117
+ if (Date.now() < swallowPostPasteDebrisUntil && shouldSwallowPostPasteDebris(data)) {
118
+ pasteLog(`POST-PASTE DEBRIS swallowed raw=${data}`);
119
+ return true;
120
+ }
121
+ // Aggressively strip ALL bracketed paste escape sequences from every chunk,
122
+ // regardless of context. Some terminals split markers across chunks or send
123
+ // them in unexpected positions. We never want \x1b[200~ or \x1b[201~ (or
124
+ // partial fragments like [200~ / [201~) to reach the input component.
125
+ const hadStart = data.includes("\x1b[200~") || data.includes("[200~") || data.includes("200~");
126
+ const hadEnd = data.includes("\x1b[201~") || data.includes("[201~") || data.includes("201~");
127
+ pasteLog(`MARKERS start=${hadStart} end=${hadEnd} inBracketed=${inBracketedPaste}`);
128
+ // Strip full and partial bracketed paste markers — catch every possible fragment
129
+ // Full: \x1b[200~ / \x1b[201~ Partial: [200~ / [201~ Bare: 200~ / 201~
130
+ data = data.replace(/\x1b?\[?20[01]~/g, "");
131
+ // Belt-and-suspenders: catch any residual marker fragments with multiple passes
132
+ data = data.replace(/\[20[01]~/g, ""); // [200~ or [201~
133
+ data = data.replace(/20[01]~/g, ""); // 200~ or 201~
134
+ data = data.replace(/\[\d01~/g, ""); // any [Xdigit01~
135
+ // Final paranoia pass: remove anything that looks like a closing bracket-tilde
136
+ if (data.includes("[201") || data.includes("[200")) {
137
+ data = data.replace(/\[[0-9]*0?[01]~?/g, "");
138
+ }
139
+ // ── Bracketed paste handling ──
140
+ if (hadStart) {
141
+ // Flush any pending burst before entering bracketed mode
142
+ if (burstTimer) {
143
+ clearTimeout(burstTimer);
144
+ burstTimer = null;
145
+ }
146
+ flushBurst();
147
+ inBracketedPaste = true;
148
+ pasteLog("ENTERED bracketed paste mode");
149
+ }
150
+ if (hadEnd) {
151
+ bracketedBuffer += data;
152
+ inBracketedPaste = false;
153
+ const content = bracketedBuffer;
154
+ bracketedBuffer = "";
155
+ pasteLog(`BRACKETED COMPLETE len=${content.length} lines=${content.split("\\n").length}`);
156
+ handlePasteContent(content);
157
+ return true;
158
+ }
159
+ if (inBracketedPaste) {
160
+ bracketedBuffer += data;
161
+ pasteLog(`BRACKETED BUFFERING total=${bracketedBuffer.length}`);
162
+ return true;
163
+ }
164
+ // ── Burst buffering for non-bracketed paste ──
165
+ burstBuffer += data;
166
+ if (burstTimer)
167
+ clearTimeout(burstTimer);
168
+ burstTimer = setTimeout(() => {
169
+ burstTimer = null;
170
+ flushBurst();
171
+ }, BURST_WINDOW_MS);
172
+ return true;
173
+ };
174
+ // Disable bracketed paste on exit
175
+ process.on("exit", () => {
176
+ process.stdout.write("\x1b[?2004l");
177
+ });
178
+ return pasteEvents;
179
+ }
@@ -0,0 +1,171 @@
1
+ import type { Theme } from "../themes.js";
2
+ import type { PullProgress } from "../utils/ollama.js";
3
+ import type { HardwareInfo } from "../utils/hardware.js";
4
+ import type { ScoredModel } from "../utils/models.js";
5
+ interface CommandSuggestionsProps {
6
+ cmdMatches: Array<{
7
+ cmd: string;
8
+ desc: string;
9
+ }>;
10
+ cmdIndex: number;
11
+ colors: Theme["colors"];
12
+ }
13
+ export declare function CommandSuggestions({ cmdMatches, cmdIndex, colors }: CommandSuggestionsProps): import("react/jsx-runtime").JSX.Element;
14
+ interface LoginPickerProps {
15
+ loginPickerIndex: number;
16
+ colors: Theme["colors"];
17
+ }
18
+ export declare function LoginPicker({ loginPickerIndex, colors }: LoginPickerProps): import("react/jsx-runtime").JSX.Element;
19
+ interface LoginMethodPickerProps {
20
+ loginMethodPicker: {
21
+ provider: string;
22
+ methods: string[];
23
+ };
24
+ loginMethodIndex: number;
25
+ colors: Theme["colors"];
26
+ }
27
+ export declare function LoginMethodPickerUI({ loginMethodPicker, loginMethodIndex, colors }: LoginMethodPickerProps): import("react/jsx-runtime").JSX.Element;
28
+ interface SkillsMenuProps {
29
+ skillsPickerIndex: number;
30
+ colors: Theme["colors"];
31
+ }
32
+ export declare function SkillsMenu({ skillsPickerIndex, colors }: SkillsMenuProps): import("react/jsx-runtime").JSX.Element;
33
+ interface SkillsBrowseProps {
34
+ skillsPickerIndex: number;
35
+ colors: Theme["colors"];
36
+ }
37
+ export declare function SkillsBrowse({ skillsPickerIndex, colors }: SkillsBrowseProps): import("react/jsx-runtime").JSX.Element;
38
+ interface SkillsInstalledProps {
39
+ skillsPickerIndex: number;
40
+ sessionDisabledSkills: Set<string>;
41
+ colors: Theme["colors"];
42
+ }
43
+ export declare function SkillsInstalled({ skillsPickerIndex, sessionDisabledSkills, colors }: SkillsInstalledProps): import("react/jsx-runtime").JSX.Element;
44
+ interface SkillsRemoveProps {
45
+ skillsPickerIndex: number;
46
+ colors: Theme["colors"];
47
+ }
48
+ export declare function SkillsRemove({ skillsPickerIndex, colors }: SkillsRemoveProps): import("react/jsx-runtime").JSX.Element;
49
+ interface ThemePickerProps {
50
+ themePickerIndex: number;
51
+ theme: Theme;
52
+ }
53
+ export declare function ThemePickerUI({ themePickerIndex, theme }: ThemePickerProps): import("react/jsx-runtime").JSX.Element;
54
+ interface SessionPickerProps {
55
+ sessions: Array<{
56
+ id: string;
57
+ display: string;
58
+ }>;
59
+ selectedIndex: number;
60
+ colors: Theme["colors"];
61
+ }
62
+ export declare function SessionPicker({ sessions, selectedIndex, colors }: SessionPickerProps): import("react/jsx-runtime").JSX.Element;
63
+ interface DeleteSessionPickerProps {
64
+ sessions: Array<{
65
+ id: string;
66
+ display: string;
67
+ }>;
68
+ selectedIndex: number;
69
+ colors: Theme["colors"];
70
+ }
71
+ export declare function DeleteSessionPicker({ sessions, selectedIndex, colors }: DeleteSessionPickerProps): import("react/jsx-runtime").JSX.Element;
72
+ interface DeleteSessionConfirmProps {
73
+ session: {
74
+ id: string;
75
+ display: string;
76
+ };
77
+ colors: Theme["colors"];
78
+ }
79
+ export declare function DeleteSessionConfirm({ session, colors }: DeleteSessionConfirmProps): import("react/jsx-runtime").JSX.Element;
80
+ export interface ModelEntry {
81
+ name: string;
82
+ baseUrl: string;
83
+ apiKey: string;
84
+ providerType: "openai" | "anthropic";
85
+ }
86
+ export interface GroupedModels {
87
+ [providerName: string]: ModelEntry[];
88
+ }
89
+ export interface ProviderPickerEntry {
90
+ name: string;
91
+ description: string;
92
+ authed: boolean;
93
+ }
94
+ interface ProviderPickerProps {
95
+ providers: ProviderPickerEntry[];
96
+ selectedIndex: number;
97
+ colors: Theme["colors"];
98
+ }
99
+ export declare function ProviderPicker({ providers, selectedIndex, colors }: ProviderPickerProps): import("react/jsx-runtime").JSX.Element;
100
+ interface ModelPickerProps {
101
+ providerName: string;
102
+ models: ModelEntry[];
103
+ selectedIndex: number;
104
+ activeModel: string;
105
+ colors: Theme["colors"];
106
+ }
107
+ export declare function ModelPicker({ providerName, models, selectedIndex, activeModel, colors }: ModelPickerProps): import("react/jsx-runtime").JSX.Element;
108
+ interface OllamaDeletePickerProps {
109
+ models: Array<{
110
+ name: string;
111
+ size: number;
112
+ }>;
113
+ selectedIndex: number;
114
+ colors: Theme["colors"];
115
+ }
116
+ export declare function OllamaDeletePicker({ models, selectedIndex, colors }: OllamaDeletePickerProps): import("react/jsx-runtime").JSX.Element;
117
+ interface OllamaPullPickerProps {
118
+ selectedIndex: number;
119
+ colors: Theme["colors"];
120
+ }
121
+ export declare function OllamaPullPicker({ selectedIndex, colors }: OllamaPullPickerProps): import("react/jsx-runtime").JSX.Element;
122
+ interface OllamaDeleteConfirmProps {
123
+ model: string;
124
+ size: number;
125
+ colors: Theme["colors"];
126
+ }
127
+ export declare function OllamaDeleteConfirm({ model, size, colors }: OllamaDeleteConfirmProps): import("react/jsx-runtime").JSX.Element;
128
+ interface OllamaPullProgressProps {
129
+ model: string;
130
+ progress: PullProgress;
131
+ colors: Theme["colors"];
132
+ }
133
+ export declare function OllamaPullProgress({ model, progress, colors }: OllamaPullProgressProps): import("react/jsx-runtime").JSX.Element;
134
+ interface OllamaExitPromptProps {
135
+ colors: Theme["colors"];
136
+ }
137
+ export declare function OllamaExitPrompt({ colors }: OllamaExitPromptProps): import("react/jsx-runtime").JSX.Element;
138
+ interface ApprovalPromptProps {
139
+ approval: {
140
+ tool: string;
141
+ args: Record<string, unknown>;
142
+ diff?: string;
143
+ };
144
+ colors: Theme["colors"];
145
+ }
146
+ export declare function ApprovalPrompt({ approval, colors }: ApprovalPromptProps): import("react/jsx-runtime").JSX.Element;
147
+ interface WizardConnectionProps {
148
+ wizardIndex: number;
149
+ colors: Theme["colors"];
150
+ }
151
+ export declare function WizardConnection({ wizardIndex, colors }: WizardConnectionProps): import("react/jsx-runtime").JSX.Element;
152
+ interface WizardModelsProps {
153
+ wizardIndex: number;
154
+ wizardHardware: HardwareInfo;
155
+ wizardModels: ScoredModel[];
156
+ colors: Theme["colors"];
157
+ }
158
+ export declare function WizardModels({ wizardIndex, wizardHardware, wizardModels, colors }: WizardModelsProps): import("react/jsx-runtime").JSX.Element;
159
+ interface WizardInstallOllamaProps {
160
+ wizardHardware: HardwareInfo | null;
161
+ colors: Theme["colors"];
162
+ }
163
+ export declare function WizardInstallOllama({ wizardHardware, colors }: WizardInstallOllamaProps): import("react/jsx-runtime").JSX.Element;
164
+ interface WizardPullingProps {
165
+ wizardSelectedModel: ScoredModel | null;
166
+ wizardPullProgress: PullProgress | null;
167
+ wizardPullError: string | null;
168
+ colors: Theme["colors"];
169
+ }
170
+ export declare function WizardPulling({ wizardSelectedModel, wizardPullProgress, wizardPullError, colors }: WizardPullingProps): import("react/jsx-runtime").JSX.Element;
171
+ export {};
@@ -0,0 +1,120 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { Box, Text } from "ink";
3
+ import { THEMES, listThemes } from "../themes.js";
4
+ import { PROVIDERS, getCredentials } from "../utils/auth.js";
5
+ import { listInstalledSkills, getRegistrySkills, getActiveSkills } from "../utils/skills.js";
6
+ import { formatBytes } from "../utils/hardware.js";
7
+ import { getFitIcon, isLlmfitAvailable } from "../utils/models.js";
8
+ import { getOllamaInstallCommand } from "../utils/ollama.js";
9
+ export function CommandSuggestions({ cmdMatches, cmdIndex, colors }) {
10
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: colors.muted, paddingX: 1, marginBottom: 0, children: [cmdMatches.slice(0, 6).map((c, i) => (_jsxs(Text, { children: [i === cmdIndex ? _jsx(Text, { color: colors.suggestion, bold: true, children: "▸ " }) : _jsx(Text, { children: " " }), _jsx(Text, { color: i === cmdIndex ? colors.suggestion : colors.primary, bold: true, children: c.cmd }), _jsxs(Text, { color: colors.muted, children: [" — ", c.desc] })] }, i))), _jsx(Text, { dimColor: true, children: " ↑↓ navigate · Tab select" })] }));
11
+ }
12
+ export function LoginPicker({ loginPickerIndex, colors }) {
13
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: colors.border, paddingX: 1, marginBottom: 0, children: [_jsx(Text, { bold: true, color: colors.secondary, children: "\uD83D\uDCAA Choose a provider:" }), PROVIDERS.filter((p) => p.id !== "local").map((p, i) => (_jsxs(Text, { children: [i === loginPickerIndex ? _jsx(Text, { color: colors.suggestion, bold: true, children: "▸ " }) : _jsx(Text, { children: " " }), _jsx(Text, { color: i === loginPickerIndex ? colors.suggestion : colors.primary, bold: true, children: p.name }), _jsxs(Text, { color: colors.muted, children: [" — ", p.description] }), getCredentials().some((c) => c.provider === p.id) ? _jsx(Text, { color: colors.success, children: " \u2713" }) : null] }, p.id))), _jsx(Text, { dimColor: true, children: " ↑↓ navigate · Enter select · Esc cancel" })] }));
14
+ }
15
+ export function LoginMethodPickerUI({ loginMethodPicker, loginMethodIndex, colors }) {
16
+ // Provider-specific label overrides
17
+ const providerLabels = {
18
+ anthropic: { "oauth": "🔐 Login with Claude Pro/Max (browser)" },
19
+ openai: { "oauth": "🔐 Login with ChatGPT (browser)" },
20
+ };
21
+ const labels = {
22
+ "oauth": "🌐 Browser login (OAuth)",
23
+ "setup-token": "🔑 Link subscription (via Claude Code CLI)",
24
+ "cached-token": "📦 Import from existing CLI",
25
+ "api-key": "🔒 Enter API key manually",
26
+ "device-flow": "📱 Device flow (GitHub)",
27
+ };
28
+ const getLabel = (method) => providerLabels[loginMethodPicker.provider]?.[method] ?? labels[method] ?? method;
29
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: colors.border, paddingX: 1, marginBottom: 0, children: [_jsx(Text, { bold: true, color: colors.secondary, children: "How do you want to authenticate?" }), loginMethodPicker.methods.map((method, i) => (_jsxs(Text, { children: [i === loginMethodIndex ? _jsx(Text, { color: colors.suggestion, bold: true, children: "▸ " }) : _jsx(Text, { children: " " }), _jsx(Text, { color: i === loginMethodIndex ? colors.suggestion : colors.primary, bold: true, children: getLabel(method) })] }, method))), _jsx(Text, { dimColor: true, children: " ↑↓ navigate · Enter select · Esc back" })] }));
30
+ }
31
+ export function SkillsMenu({ skillsPickerIndex, colors }) {
32
+ const items = [
33
+ { key: "browse", label: "Browse & Install", icon: "📦" },
34
+ { key: "installed", label: "Installed Skills", icon: "📋" },
35
+ { key: "create", label: "Create Custom Skill", icon: "➕" },
36
+ { key: "remove", label: "Remove Skill", icon: "🗑️" },
37
+ ];
38
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: colors.border, paddingX: 1, marginBottom: 0, children: [_jsx(Text, { bold: true, color: colors.secondary, children: "Skills:" }), items.map((item, i) => (_jsxs(Text, { children: [i === skillsPickerIndex ? _jsx(Text, { color: colors.suggestion, bold: true, children: "▸ " }) : _jsx(Text, { children: " " }), _jsxs(Text, { color: i === skillsPickerIndex ? colors.suggestion : colors.primary, bold: true, children: [item.icon, " ", item.label] })] }, item.key))), _jsx(Text, { dimColor: true, children: " ↑↓ navigate · Enter select · Esc cancel" })] }));
39
+ }
40
+ export function SkillsBrowse({ skillsPickerIndex, colors }) {
41
+ const registry = getRegistrySkills();
42
+ const installed = listInstalledSkills().map((s) => s.name);
43
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: colors.border, paddingX: 1, marginBottom: 0, children: [_jsx(Text, { bold: true, color: colors.secondary, children: "Browse Skills Registry:" }), registry.map((s, i) => (_jsxs(Text, { children: [i === skillsPickerIndex ? _jsx(Text, { color: colors.suggestion, bold: true, children: "▸ " }) : _jsx(Text, { children: " " }), _jsx(Text, { color: i === skillsPickerIndex ? colors.suggestion : colors.primary, bold: true, children: s.name }), _jsxs(Text, { color: colors.muted, children: [" — ", s.description] }), installed.includes(s.name) ? _jsx(Text, { color: colors.success, children: " \u2713" }) : null] }, s.name))), _jsx(Text, { dimColor: true, children: " ↑↓ navigate · Enter install · Esc back" })] }));
44
+ }
45
+ export function SkillsInstalled({ skillsPickerIndex, sessionDisabledSkills, colors }) {
46
+ const installed = listInstalledSkills();
47
+ const active = getActiveSkills(process.cwd(), sessionDisabledSkills);
48
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: colors.border, paddingX: 1, marginBottom: 0, children: [_jsx(Text, { bold: true, color: colors.secondary, children: "Installed Skills:" }), installed.length === 0 ? (_jsx(Text, { color: colors.muted, children: " No skills installed. Use Browse & Install." })) : installed.map((s, i) => (_jsxs(Text, { children: [i === skillsPickerIndex ? _jsx(Text, { color: colors.suggestion, bold: true, children: "▸ " }) : _jsx(Text, { children: " " }), _jsx(Text, { color: i === skillsPickerIndex ? colors.suggestion : colors.primary, bold: true, children: s.name }), _jsxs(Text, { color: colors.muted, children: [" — ", s.description] }), active.includes(s.name) ? _jsx(Text, { color: colors.success, children: " (on)" }) : _jsx(Text, { color: colors.muted, children: " (off)" })] }, s.name))), _jsx(Text, { dimColor: true, children: " ↑↓ navigate · Enter toggle · Esc back" })] }));
49
+ }
50
+ export function SkillsRemove({ skillsPickerIndex, colors }) {
51
+ const installed = listInstalledSkills();
52
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: colors.error, paddingX: 1, marginBottom: 0, children: [_jsx(Text, { bold: true, color: colors.error, children: "Remove a skill:" }), installed.map((s, i) => (_jsxs(Text, { children: [i === skillsPickerIndex ? _jsx(Text, { color: colors.suggestion, bold: true, children: "▸ " }) : _jsx(Text, { children: " " }), _jsxs(Text, { color: i === skillsPickerIndex ? colors.suggestion : colors.muted, children: [s.name, " \u2014 ", s.description] })] }, s.name))), _jsx(Text, { dimColor: true, children: " ↑↓ navigate · Enter remove · Esc back" })] }));
53
+ }
54
+ export function ThemePickerUI({ themePickerIndex, theme }) {
55
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: theme.colors.border, paddingX: 1, marginBottom: 0, children: [_jsx(Text, { bold: true, color: theme.colors.secondary, children: "Choose a theme:" }), listThemes().map((key, i) => (_jsxs(Text, { children: [i === themePickerIndex ? _jsx(Text, { color: theme.colors.suggestion, bold: true, children: "▸ " }) : _jsx(Text, { children: " " }), _jsx(Text, { color: i === themePickerIndex ? theme.colors.suggestion : theme.colors.primary, bold: true, children: key }), _jsxs(Text, { color: theme.colors.muted, children: [" — ", THEMES[key].description] }), key === theme.name.toLowerCase() ? _jsx(Text, { color: theme.colors.muted, children: " (current)" }) : null] }, key))), _jsx(Text, { dimColor: true, children: " ↑↓ navigate · Enter select · Esc cancel" })] }));
56
+ }
57
+ export function SessionPicker({ sessions, selectedIndex, colors }) {
58
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: colors.secondary, paddingX: 1, marginBottom: 0, children: [_jsx(Text, { bold: true, color: colors.secondary, children: "Resume a session:" }), sessions.map((s, i) => (_jsxs(Text, { children: [i === selectedIndex ? _jsx(Text, { color: colors.suggestion, bold: true, children: "▸ " }) : _jsx(Text, { children: " " }), _jsx(Text, { color: i === selectedIndex ? colors.suggestion : colors.muted, children: s.display })] }, s.id))), _jsx(Text, { dimColor: true, children: " ↑↓ navigate · Enter select · Esc cancel" })] }));
59
+ }
60
+ export function DeleteSessionPicker({ sessions, selectedIndex, colors }) {
61
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: colors.error, paddingX: 1, marginBottom: 0, children: [_jsx(Text, { bold: true, color: colors.error, children: "Delete a session:" }), sessions.map((s, i) => (_jsxs(Text, { children: [i === selectedIndex ? _jsx(Text, { color: colors.suggestion, bold: true, children: "▸ " }) : _jsx(Text, { children: " " }), _jsx(Text, { color: i === selectedIndex ? colors.suggestion : colors.muted, children: s.display })] }, s.id))), _jsx(Text, { dimColor: true, children: " ↑↓ navigate · Enter select · Esc cancel" })] }));
62
+ }
63
+ export function DeleteSessionConfirm({ session, colors }) {
64
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: colors.warning, paddingX: 1, marginBottom: 0, children: [_jsxs(Text, { bold: true, color: colors.warning, children: ["Delete session ", session.id, "?"] }), _jsxs(Text, { color: colors.muted, children: [" ", session.display] }), _jsxs(Text, { children: [_jsx(Text, { color: colors.error, bold: true, children: " [y]" }), _jsx(Text, { children: "es " }), _jsx(Text, { color: colors.success, bold: true, children: "[n]" }), _jsx(Text, { children: "o" })] })] }));
65
+ }
66
+ export function ProviderPicker({ providers, selectedIndex, colors }) {
67
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: colors.border, paddingX: 1, marginBottom: 0, children: [_jsx(Text, { bold: true, color: colors.secondary, children: "Choose a provider:" }), _jsx(Text, { children: "" }), providers.map((p, i) => (_jsxs(Text, { children: [i === selectedIndex ? _jsx(Text, { color: colors.primary, bold: true, children: "▸ " }) : " ", _jsx(Text, { color: i === selectedIndex ? colors.primary : undefined, bold: true, children: p.name }), _jsxs(Text, { dimColor: true, children: [" — ", p.description] }), p.authed ? _jsx(Text, { color: colors.success, children: " ✓" }) : null] }, p.name))), _jsx(Text, { children: "" }), _jsx(Text, { dimColor: true, children: " ↑↓ navigate · Enter select · Esc cancel" })] }));
68
+ }
69
+ export function ModelPicker({ providerName, models, selectedIndex, activeModel, colors }) {
70
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: colors.border, paddingX: 1, marginBottom: 0, children: [_jsxs(Text, { bold: true, color: colors.secondary, children: ["── ", providerName, " ──"] }), _jsx(Text, { children: "" }), models.map((entry, i) => (_jsxs(Text, { children: [" ", i === selectedIndex ? _jsx(Text, { color: colors.primary, bold: true, children: "▸ " }) : " ", _jsx(Text, { color: i === selectedIndex ? colors.primary : undefined, children: entry.name }), entry.name === activeModel ? _jsx(Text, { color: colors.success, children: " (active)" }) : null] }, entry.name))), _jsx(Text, { children: "" }), _jsx(Text, { dimColor: true, children: " ↑↓ navigate · Enter switch · Esc back" })] }));
71
+ }
72
+ export function OllamaDeletePicker({ models, selectedIndex, colors }) {
73
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: colors.border, paddingX: 1, marginBottom: 0, children: [_jsx(Text, { bold: true, color: colors.secondary, children: "Delete which model?" }), _jsx(Text, { children: "" }), models.map((m, i) => (_jsxs(Text, { children: [" ", i === selectedIndex ? _jsx(Text, { color: colors.primary, bold: true, children: "▸ " }) : " ", _jsx(Text, { color: i === selectedIndex ? colors.primary : undefined, children: m.name }), _jsxs(Text, { color: colors.muted, children: [" (", (m.size / (1024 * 1024 * 1024)).toFixed(1), " GB)"] })] }, m.name))), _jsx(Text, { children: "" }), _jsx(Text, { dimColor: true, children: " ↑↓ navigate · Enter to delete · Esc cancel" })] }));
74
+ }
75
+ // ── Ollama Pull Picker ──
76
+ const PULL_MODELS = [
77
+ { id: "qwen2.5-coder:7b", name: "Qwen 2.5 Coder 7B", size: "5 GB", desc: "Best balance of speed & quality" },
78
+ { id: "qwen2.5-coder:14b", name: "Qwen 2.5 Coder 14B", size: "9 GB", desc: "Higher quality, needs 16GB+ RAM" },
79
+ { id: "qwen2.5-coder:3b", name: "Qwen 2.5 Coder 3B", size: "2 GB", desc: "\u26A0\uFE0F Basic \u2014 may struggle with tool calls" },
80
+ { id: "qwen2.5-coder:32b", name: "Qwen 2.5 Coder 32B", size: "20 GB", desc: "Premium, needs 48GB+" },
81
+ { id: "deepseek-coder-v2:16b", name: "DeepSeek Coder V2", size: "9 GB", desc: "Strong alternative" },
82
+ { id: "codellama:7b", name: "CodeLlama 7B", size: "4 GB", desc: "Meta's coding model" },
83
+ { id: "starcoder2:7b", name: "StarCoder2 7B", size: "4 GB", desc: "Code completion focused" },
84
+ ];
85
+ export function OllamaPullPicker({ selectedIndex, colors }) {
86
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: colors.border, paddingX: 1, marginBottom: 0, children: [_jsx(Text, { bold: true, color: colors.secondary, children: "Download which model?" }), _jsx(Text, { children: "" }), PULL_MODELS.map((m, i) => (_jsxs(Text, { children: [" ", i === selectedIndex ? _jsx(Text, { color: colors.primary, bold: true, children: "▸ " }) : " ", _jsx(Text, { color: i === selectedIndex ? colors.primary : undefined, bold: true, children: m.name }), _jsxs(Text, { color: colors.muted, children: [" · ", m.size, " · ", m.desc] })] }, m.id))), _jsx(Text, { children: "" }), _jsx(Text, { dimColor: true, children: " ↑↓ navigate · Enter to download · Esc cancel" })] }));
87
+ }
88
+ export function OllamaDeleteConfirm({ model, size, colors }) {
89
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: colors.warning, paddingX: 1, marginBottom: 0, children: [_jsxs(Text, { bold: true, color: colors.warning, children: ["Delete ", model, " (", (size / (1024 * 1024 * 1024)).toFixed(1), " GB)?"] }), _jsxs(Text, { children: [_jsx(Text, { color: colors.error, bold: true, children: " [y]" }), _jsx(Text, { children: "es " }), _jsx(Text, { color: colors.success, bold: true, children: "[n]" }), _jsx(Text, { children: "o" })] })] }));
90
+ }
91
+ export function OllamaPullProgress({ model, progress, colors }) {
92
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: colors.border, paddingX: 1, marginBottom: 0, children: [_jsxs(Text, { bold: true, color: colors.secondary, children: [" Downloading ", model, "..."] }), progress.status === "downloading" || progress.percent > 0 ? (_jsxs(Text, { children: [" ", _jsxs(Text, { color: colors.primary, children: ["\u2588".repeat(Math.floor(progress.percent / 5)), "\u2591".repeat(20 - Math.floor(progress.percent / 5))] }), " ", _jsxs(Text, { bold: true, children: [progress.percent, "%"] }), progress.completed != null && progress.total != null ? (_jsxs(Text, { color: colors.muted, children: [" \u00B7 ", formatBytes(progress.completed), " / ", formatBytes(progress.total)] })) : null] })) : (_jsxs(Text, { color: colors.muted, children: [" ", progress.status, "..."] }))] }));
93
+ }
94
+ export function OllamaExitPrompt({ colors }) {
95
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: colors.warning, paddingX: 1, marginBottom: 0, children: [_jsx(Text, { bold: true, color: colors.warning, children: "Ollama is still running." }), _jsx(Text, { color: colors.muted, children: "Stop it to free GPU memory?" }), _jsxs(Text, { children: [_jsx(Text, { color: colors.success, bold: true, children: " [y]" }), _jsx(Text, { children: "es " }), _jsx(Text, { color: colors.error, bold: true, children: "[n]" }), _jsx(Text, { children: "o " }), _jsx(Text, { color: colors.primary, bold: true, children: "[a]" }), _jsx(Text, { children: "lways" })] })] }));
96
+ }
97
+ export function ApprovalPrompt({ approval, colors }) {
98
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: colors.warning, paddingX: 1, marginTop: 1, children: [_jsxs(Text, { bold: true, color: colors.warning, children: ["\u26A0 Approve ", approval.tool, "?"] }), approval.tool === "write_file" && approval.args.path ? (_jsxs(Text, { color: colors.muted, children: [" 📄 ", String(approval.args.path)] })) : null, approval.tool === "write_file" && approval.args.content ? (_jsxs(Text, { color: colors.muted, children: [" ", String(approval.args.content).split("\n").length, " lines, ", String(approval.args.content).length, "B"] })) : null, approval.diff ? (_jsxs(Box, { flexDirection: "column", marginTop: 0, marginLeft: 2, children: [approval.diff.split("\n").slice(0, 40).map((line, i) => (_jsx(Text, { color: line.startsWith("+") ? colors.success :
99
+ line.startsWith("-") ? colors.error :
100
+ line.startsWith("@@") ? colors.primary :
101
+ colors.muted, children: line }, i))), approval.diff.split("\n").length > 40 ? (_jsxs(Text, { color: colors.muted, children: ["... (", approval.diff.split("\n").length - 40, " more lines)"] })) : null] })) : null, approval.tool === "run_command" && approval.args.command ? (_jsxs(Text, { color: colors.muted, children: [" $ ", String(approval.args.command)] })) : null, _jsxs(Text, { children: [_jsx(Text, { color: colors.success, bold: true, children: " [y]" }), _jsx(Text, { children: "es " }), _jsx(Text, { color: colors.error, bold: true, children: "[n]" }), _jsx(Text, { children: "o " }), _jsx(Text, { color: colors.primary, bold: true, children: "[a]" }), _jsx(Text, { children: "lways" })] })] }));
102
+ }
103
+ export function WizardConnection({ wizardIndex, colors }) {
104
+ const items = [
105
+ { key: "local", icon: "\uD83D\uDDA5\uFE0F", label: "Set up a local model", desc: "free, runs on your machine" },
106
+ { key: "openrouter", icon: "\uD83C\uDF10", label: "OpenRouter", desc: "200+ cloud models, browser login" },
107
+ { key: "apikey", icon: "\uD83D\uDD11", label: "Enter API key manually", desc: "" },
108
+ { key: "existing", icon: "\u2699\uFE0F", label: "I already have a server running", desc: "" },
109
+ ];
110
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: colors.border, paddingX: 1, marginBottom: 0, children: [_jsx(Text, { bold: true, color: colors.secondary, children: "No LLM detected. How do you want to connect?" }), _jsx(Text, { children: "" }), items.map((item, i) => (_jsxs(Text, { children: [i === wizardIndex ? _jsx(Text, { color: colors.suggestion, bold: true, children: " \u25B8 " }) : _jsx(Text, { children: " " }), _jsxs(Text, { color: i === wizardIndex ? colors.suggestion : colors.primary, bold: true, children: [item.icon, " ", item.label] }), item.desc ? _jsxs(Text, { color: colors.muted, children: [" (", item.desc, ")"] }) : null] }, item.key))), _jsx(Text, { children: "" }), _jsx(Text, { dimColor: true, children: " \u2191\u2193 navigate \u00B7 Enter to select" })] }));
111
+ }
112
+ export function WizardModels({ wizardIndex, wizardHardware, wizardModels, colors }) {
113
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: colors.border, paddingX: 1, marginBottom: 0, children: [_jsx(Text, { bold: true, color: colors.secondary, children: "Your hardware:" }), _jsxs(Text, { color: colors.muted, children: [" CPU: ", wizardHardware.cpu.name, " (", wizardHardware.cpu.cores, " cores)"] }), _jsxs(Text, { color: colors.muted, children: [" RAM: ", formatBytes(wizardHardware.ram)] }), wizardHardware.gpu ? (_jsxs(Text, { color: colors.muted, children: [" GPU: ", wizardHardware.gpu.name, wizardHardware.gpu.vram > 0 ? ` (${formatBytes(wizardHardware.gpu.vram)})` : ""] })) : (_jsx(Text, { color: colors.muted, children: " GPU: none detected" })), !isLlmfitAvailable() && (_jsx(Text, { dimColor: true, children: " Tip: Install llmfit for smarter recommendations: brew install llmfit" })), _jsx(Text, { children: "" }), _jsx(Text, { bold: true, color: colors.secondary, children: "Recommended models:" }), _jsx(Text, { children: "" }), wizardModels.map((m, i) => (_jsxs(Text, { children: [i === wizardIndex ? _jsx(Text, { color: colors.suggestion, bold: true, children: " \u25B8 " }) : _jsx(Text, { children: " " }), _jsxs(Text, { children: [getFitIcon(m.fit), " "] }), _jsx(Text, { color: i === wizardIndex ? colors.suggestion : colors.primary, bold: true, children: m.name }), _jsxs(Text, { color: colors.muted, children: [" ~", m.size, " GB \u00B7 ", m.quality === "best" ? "Best" : m.quality === "great" ? "Great" : "Good", " quality \u00B7 ", m.speed] })] }, m.ollamaId))), wizardModels.length === 0 && (_jsx(Text, { color: colors.error, children: " No suitable models found for your hardware." })), _jsx(Text, { children: "" }), _jsx(Text, { dimColor: true, children: " \u2191\u2193 navigate \u00B7 Enter to install \u00B7 Esc back" })] }));
114
+ }
115
+ export function WizardInstallOllama({ wizardHardware, colors }) {
116
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: colors.warning, paddingX: 1, marginBottom: 0, children: [_jsx(Text, { bold: true, color: colors.warning, children: "Ollama is required for local models." }), _jsx(Text, { children: "" }), _jsx(Text, { color: colors.primary, children: " Press Enter to install Ollama automatically" }), _jsxs(Text, { dimColor: true, children: [" Or install manually: ", _jsx(Text, { children: getOllamaInstallCommand(wizardHardware?.os ?? "linux") })] }), _jsx(Text, { children: "" }), _jsx(Text, { dimColor: true, children: " Enter to install · Esc to go back" })] }));
117
+ }
118
+ export function WizardPulling({ wizardSelectedModel, wizardPullProgress, wizardPullError, colors }) {
119
+ return (_jsx(Box, { flexDirection: "column", borderStyle: "single", borderColor: colors.border, paddingX: 1, marginBottom: 0, children: wizardPullError ? (_jsxs(_Fragment, { children: [_jsxs(Text, { color: colors.error, bold: true, children: [" \u274C Error: ", wizardPullError] }), _jsx(Text, { children: "" }), _jsx(Text, { dimColor: true, children: " Press Enter to retry \u00B7 Esc to go back" })] })) : wizardPullProgress ? (_jsxs(_Fragment, { children: [_jsxs(Text, { bold: true, color: colors.secondary, children: [" ", wizardSelectedModel ? `Downloading ${wizardSelectedModel.name}...` : wizardPullProgress?.status || "Working..."] }), wizardPullProgress.status === "downloading" || wizardPullProgress.percent > 0 ? (_jsx(_Fragment, { children: _jsxs(Text, { children: [" ", _jsxs(Text, { color: colors.primary, children: ["\u2588".repeat(Math.floor(wizardPullProgress.percent / 5)), "\u2591".repeat(20 - Math.floor(wizardPullProgress.percent / 5))] }), " ", _jsxs(Text, { bold: true, children: [wizardPullProgress.percent, "%"] }), wizardPullProgress.completed != null && wizardPullProgress.total != null ? (_jsxs(Text, { color: colors.muted, children: [" \u00B7 ", formatBytes(wizardPullProgress.completed), " / ", formatBytes(wizardPullProgress.total)] })) : null] }) })) : (_jsxs(Text, { color: colors.muted, children: [" ", wizardPullProgress.status, "..."] }))] })) : null }));
120
+ }
@@ -0,0 +1,8 @@
1
+ import type { CodingAgent } from "../agent.js";
2
+ interface StatusBarProps {
3
+ agent: CodingAgent;
4
+ modelName: string;
5
+ sessionDisabledSkills: Set<string>;
6
+ }
7
+ export declare function StatusBar({ agent, modelName, sessionDisabledSkills }: StatusBarProps): import("react/jsx-runtime").JSX.Element;
8
+ export {};
@@ -0,0 +1,15 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ import { Box, Text } from "ink";
3
+ import { getActiveSkillCount } from "../utils/skills.js";
4
+ export function StatusBar({ agent, modelName, sessionDisabledSkills }) {
5
+ const tokens = agent.estimateTokens();
6
+ const tokenStr = tokens >= 1000 ? `${(tokens / 1000).toFixed(1)}k` : String(tokens);
7
+ const { totalCost } = agent.getCostInfo();
8
+ const costStr = totalCost > 0
9
+ ? ` · 💰 $${totalCost < 0.01 ? totalCost.toFixed(4) : totalCost.toFixed(2)}`
10
+ : "";
11
+ const count = getActiveSkillCount(process.cwd(), sessionDisabledSkills);
12
+ const skillsStr = count > 0 ? ` · 🧠 ${count} skill${count !== 1 ? "s" : ""}` : "";
13
+ const architectStr = agent.getArchitectModel() ? " · 🏗️ architect" : "";
14
+ return (_jsx(Box, { paddingX: 2, children: _jsxs(Text, { dimColor: true, children: ["💬 ", agent.getContextLength(), " messages · ~", tokenStr, " tokens", costStr, modelName ? ` · 🤖 ${modelName}` : "", skillsStr, architectStr] }) }));
15
+ }
@@ -0,0 +1,27 @@
1
+ import type { HardwareInfo } from "../utils/hardware.js";
2
+ import type { ScoredModel } from "../utils/models.js";
3
+ import type { PullProgress } from "../utils/ollama.js";
4
+ export type WizardScreen = "connection" | "models" | "install-ollama" | "pulling" | null;
5
+ export interface WizardContext {
6
+ wizardScreen: WizardScreen;
7
+ wizardIndex: number;
8
+ wizardModels: ScoredModel[];
9
+ wizardHardware: HardwareInfo | null;
10
+ wizardPullProgress: PullProgress | null;
11
+ wizardPullError: string | null;
12
+ wizardSelectedModel: ScoredModel | null;
13
+ setWizardScreen: (val: WizardScreen) => void;
14
+ setWizardIndex: (fn: (prev: number) => number) => void;
15
+ setWizardHardware: (val: HardwareInfo | null) => void;
16
+ setWizardModels: (val: ScoredModel[]) => void;
17
+ setWizardPullProgress: (val: PullProgress | null) => void;
18
+ setWizardPullError: (val: string | null) => void;
19
+ setWizardSelectedModel: (val: ScoredModel | null) => void;
20
+ setLoginPicker: (val: boolean) => void;
21
+ setLoginPickerIndex: (fn: (prev: number) => number) => void;
22
+ setLoading: (val: boolean) => void;
23
+ setSpinnerMsg: (val: string) => void;
24
+ addMsg: (type: "user" | "response" | "tool" | "tool-result" | "error" | "info", text: string) => void;
25
+ connectToProvider: (isRetry: boolean) => Promise<void>;
26
+ _require: NodeRequire;
27
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ import type { Key } from "ink";
2
+ import type { WizardContext } from "./wizard-types.js";
3
+ export declare function handleWizardScreen(_inputChar: string, key: Key, ctx: WizardContext): boolean;