@stjbrown/agent-knowledge 0.1.0-beta.1 → 0.1.0-beta.10
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/NOTICE +5 -4
- package/OBSERVABILITY.md +163 -0
- package/README.md +74 -8
- package/dist/chunk-JZ3GYPHM.js +3993 -0
- package/dist/chunk-JZ3GYPHM.js.map +1 -0
- package/dist/{chunk-J4MELCGD.js → chunk-X6R3XLAQ.js} +19 -6
- package/dist/chunk-X6R3XLAQ.js.map +1 -0
- package/dist/headless.js +2 -2
- package/dist/index.js +2 -2
- package/dist/main.js +7 -18
- package/dist/main.js.map +1 -1
- package/dist/tui-74FJW5Y4.js +1519 -0
- package/dist/tui-74FJW5Y4.js.map +1 -0
- package/package.json +27 -15
- package/skills/kb/SKILL.md +5 -0
- package/skills/kb/example-bundle/spec/types.md +3 -2
- package/skills/kb-ingest/SKILL.md +27 -6
- package/skills/kb-init/SKILL.md +23 -5
- package/skills/kb-lint/SKILL.md +6 -1
- package/dist/chunk-3XSOMUQQ.js +0 -131
- package/dist/chunk-3XSOMUQQ.js.map +0 -1
- package/dist/chunk-J4MELCGD.js.map +0 -1
- package/dist/chunk-YIAVFL7A.js +0 -1732
- package/dist/chunk-YIAVFL7A.js.map +0 -1
- package/dist/tui-VZVO7UHV.js +0 -521
- package/dist/tui-VZVO7UHV.js.map +0 -1
package/dist/tui-VZVO7UHV.js
DELETED
|
@@ -1,521 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { createRequire as __janetCreateRequire } from "node:module";
|
|
3
|
-
const require = __janetCreateRequire(import.meta.url);
|
|
4
|
-
import {
|
|
5
|
-
availableModels,
|
|
6
|
-
completeOnboarding,
|
|
7
|
-
loadSettings,
|
|
8
|
-
normalizeModelSelection,
|
|
9
|
-
rememberModel
|
|
10
|
-
} from "./chunk-3XSOMUQQ.js";
|
|
11
|
-
import {
|
|
12
|
-
GREETING,
|
|
13
|
-
bootJanet,
|
|
14
|
-
getAuthStorage,
|
|
15
|
-
messageText
|
|
16
|
-
} from "./chunk-YIAVFL7A.js";
|
|
17
|
-
|
|
18
|
-
// src/tui/index.ts
|
|
19
|
-
import {
|
|
20
|
-
Container,
|
|
21
|
-
Editor,
|
|
22
|
-
Loader,
|
|
23
|
-
Markdown,
|
|
24
|
-
ProcessTerminal,
|
|
25
|
-
SelectList,
|
|
26
|
-
Spacer,
|
|
27
|
-
TUI,
|
|
28
|
-
Text
|
|
29
|
-
} from "@earendil-works/pi-tui";
|
|
30
|
-
|
|
31
|
-
// src/tui/theme.ts
|
|
32
|
-
import chalk from "chalk";
|
|
33
|
-
var c = {
|
|
34
|
-
accent: chalk.cyan,
|
|
35
|
-
accentBold: chalk.cyan.bold,
|
|
36
|
-
dim: chalk.dim,
|
|
37
|
-
user: chalk.green,
|
|
38
|
-
error: chalk.red,
|
|
39
|
-
warn: chalk.yellow,
|
|
40
|
-
bold: chalk.bold,
|
|
41
|
-
italic: chalk.italic
|
|
42
|
-
};
|
|
43
|
-
var editorTheme = {
|
|
44
|
-
borderColor: (s) => chalk.cyan(s),
|
|
45
|
-
selectList: {
|
|
46
|
-
selectedPrefix: (s) => chalk.cyan(s),
|
|
47
|
-
selectedText: (s) => chalk.cyan.bold(s),
|
|
48
|
-
description: (s) => chalk.dim(s),
|
|
49
|
-
scrollInfo: (s) => chalk.dim(s),
|
|
50
|
-
noMatch: (s) => chalk.dim(s)
|
|
51
|
-
}
|
|
52
|
-
};
|
|
53
|
-
var markdownTheme = {
|
|
54
|
-
heading: (s) => chalk.cyan.bold(s),
|
|
55
|
-
link: (s) => chalk.cyan.underline(s),
|
|
56
|
-
linkUrl: (s) => chalk.dim(s),
|
|
57
|
-
code: (s) => chalk.yellow(s),
|
|
58
|
-
codeBlock: (s) => chalk.yellow(s),
|
|
59
|
-
codeBlockBorder: (s) => chalk.dim(s),
|
|
60
|
-
quote: (s) => chalk.italic(s),
|
|
61
|
-
quoteBorder: (s) => chalk.dim(s),
|
|
62
|
-
hr: (s) => chalk.dim(s),
|
|
63
|
-
listBullet: (s) => chalk.cyan(s),
|
|
64
|
-
bold: (s) => chalk.bold(s),
|
|
65
|
-
italic: (s) => chalk.italic(s),
|
|
66
|
-
strikethrough: (s) => chalk.strikethrough(s),
|
|
67
|
-
underline: (s) => chalk.underline(s)
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
// src/tui/index.ts
|
|
71
|
-
var OAUTH_PROVIDERS = ["anthropic", "openai-codex"];
|
|
72
|
-
var JanetEditor = class extends Editor {
|
|
73
|
-
onCtrlC;
|
|
74
|
-
handleInput(data) {
|
|
75
|
-
if (data === "") {
|
|
76
|
-
this.onCtrlC?.();
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
super.handleInput(data);
|
|
80
|
-
}
|
|
81
|
-
};
|
|
82
|
-
var HELP_TEXT = `Commands:
|
|
83
|
-
/models Pick a model from a list (arrow keys)
|
|
84
|
-
/model [provider/id] Open the picker, or switch directly by id
|
|
85
|
-
/login <provider> [mode]
|
|
86
|
-
Log in; OpenAI mode is browser or device
|
|
87
|
-
/logout <provider> Remove stored credentials for a provider
|
|
88
|
-
/auth Show which providers are authenticated
|
|
89
|
-
/help This help
|
|
90
|
-
/quit Exit (double Ctrl+C also works)
|
|
91
|
-
|
|
92
|
-
Anything else is a message to Janet.`;
|
|
93
|
-
function resolveAnswer(q, text) {
|
|
94
|
-
if (!q.options?.length) return text;
|
|
95
|
-
const opts = q.options;
|
|
96
|
-
const pick = (token) => {
|
|
97
|
-
const t = token.trim();
|
|
98
|
-
if (!t) return void 0;
|
|
99
|
-
const n = Number(t);
|
|
100
|
-
if (Number.isInteger(n) && n >= 1 && n <= opts.length) return opts[n - 1].label;
|
|
101
|
-
const exact = opts.find((o) => o.label.toLowerCase() === t.toLowerCase());
|
|
102
|
-
if (exact) return exact.label;
|
|
103
|
-
return opts.find((o) => o.label.toLowerCase().startsWith(t.toLowerCase()))?.label;
|
|
104
|
-
};
|
|
105
|
-
if (q.multi) {
|
|
106
|
-
const picks = text.split(",").map(pick);
|
|
107
|
-
return picks.some((p) => p === void 0) ? void 0 : picks;
|
|
108
|
-
}
|
|
109
|
-
return pick(text);
|
|
110
|
-
}
|
|
111
|
-
async function runTui(opts) {
|
|
112
|
-
const { controller, session, paths, herdrDetach } = await bootJanet({ ...opts, interactive: true });
|
|
113
|
-
const persistedModel = process.env["JANET_MODEL"] || loadSettings().defaultModelId;
|
|
114
|
-
const presetModel = persistedModel ? normalizeModelSelection(persistedModel, availableModels()) : void 0;
|
|
115
|
-
if (!session.model.hasSelection() && presetModel) {
|
|
116
|
-
await session.model.switch({ modelId: presetModel });
|
|
117
|
-
}
|
|
118
|
-
const terminal = new ProcessTerminal();
|
|
119
|
-
const ui = new TUI(terminal);
|
|
120
|
-
const chat = new Container();
|
|
121
|
-
const status = new Text("", 1, 0);
|
|
122
|
-
const editor = new JanetEditor(ui, editorTheme);
|
|
123
|
-
const loader = new Loader(ui, c.accent, c.dim, "Janet is thinking\u2026");
|
|
124
|
-
ui.addChild(chat);
|
|
125
|
-
ui.addChild(new Spacer(1));
|
|
126
|
-
ui.addChild(editor);
|
|
127
|
-
ui.addChild(status);
|
|
128
|
-
let running = false;
|
|
129
|
-
let loaderMounted = false;
|
|
130
|
-
let pendingApproval = null;
|
|
131
|
-
let pendingQuestion = null;
|
|
132
|
-
let pendingInput = null;
|
|
133
|
-
let activeSelect = null;
|
|
134
|
-
let active = null;
|
|
135
|
-
const updateStatus = () => {
|
|
136
|
-
const model = session.model.hasSelection() ? session.model.get() : "no model \u2014 /model <id>";
|
|
137
|
-
const state = pendingInput ? "enter the requested value" : pendingQuestion || activeSelect ? "answer Janet's question" : pendingApproval ? "awaiting approval" : running ? "working" : "idle";
|
|
138
|
-
status.setText(c.dim(`${paths.projectPath} \xB7 `) + c.accent(model) + c.dim(` \xB7 ${state}`));
|
|
139
|
-
ui.requestRender();
|
|
140
|
-
};
|
|
141
|
-
const appendToChat = (comp) => {
|
|
142
|
-
if (loaderMounted) chat.removeChild(loader);
|
|
143
|
-
if (activeSelect) chat.removeChild(activeSelect);
|
|
144
|
-
chat.addChild(comp);
|
|
145
|
-
if (activeSelect) chat.addChild(activeSelect);
|
|
146
|
-
if (loaderMounted) chat.addChild(loader);
|
|
147
|
-
ui.requestRender();
|
|
148
|
-
};
|
|
149
|
-
const addLine = (text) => appendToChat(new Text(text, 1, 0));
|
|
150
|
-
const setLoader = (on) => {
|
|
151
|
-
if (on && !loaderMounted) {
|
|
152
|
-
chat.addChild(loader);
|
|
153
|
-
loader.start();
|
|
154
|
-
loaderMounted = true;
|
|
155
|
-
} else if (!on && loaderMounted) {
|
|
156
|
-
loader.stop();
|
|
157
|
-
chat.removeChild(loader);
|
|
158
|
-
loaderMounted = false;
|
|
159
|
-
}
|
|
160
|
-
ui.requestRender();
|
|
161
|
-
};
|
|
162
|
-
const closeSegment = () => {
|
|
163
|
-
if (active) {
|
|
164
|
-
active.committedLen = active.lastText.length;
|
|
165
|
-
active.comp = null;
|
|
166
|
-
}
|
|
167
|
-
};
|
|
168
|
-
const answerQuestion = (resumeData, echo) => {
|
|
169
|
-
if (activeSelect) {
|
|
170
|
-
chat.removeChild(activeSelect);
|
|
171
|
-
activeSelect = null;
|
|
172
|
-
}
|
|
173
|
-
const q = pendingQuestion;
|
|
174
|
-
pendingQuestion = null;
|
|
175
|
-
ui.setFocus(editor);
|
|
176
|
-
addLine(c.user(`\u276F ${echo}`));
|
|
177
|
-
setLoader(true);
|
|
178
|
-
updateStatus();
|
|
179
|
-
if (q) void session.respondToToolSuspension({ toolCallId: q.toolCallId, resumeData });
|
|
180
|
-
};
|
|
181
|
-
const onEvent = (event) => {
|
|
182
|
-
switch (event.type) {
|
|
183
|
-
case "agent_start":
|
|
184
|
-
running = true;
|
|
185
|
-
active = null;
|
|
186
|
-
setLoader(true);
|
|
187
|
-
updateStatus();
|
|
188
|
-
break;
|
|
189
|
-
case "message_update":
|
|
190
|
-
case "message_end": {
|
|
191
|
-
if (event.message.role !== "assistant") break;
|
|
192
|
-
const text = messageText(event.message);
|
|
193
|
-
if (!text) break;
|
|
194
|
-
if (!active || active.id !== event.message.id) {
|
|
195
|
-
active = { id: event.message.id, committedLen: 0, comp: null, lastText: "" };
|
|
196
|
-
}
|
|
197
|
-
active.lastText = text;
|
|
198
|
-
const tail = text.slice(active.committedLen);
|
|
199
|
-
if (!tail) break;
|
|
200
|
-
if (!active.comp) {
|
|
201
|
-
active.comp = new Markdown(tail, 1, 0, markdownTheme);
|
|
202
|
-
appendToChat(active.comp);
|
|
203
|
-
} else {
|
|
204
|
-
active.comp.setText(tail);
|
|
205
|
-
ui.requestRender();
|
|
206
|
-
}
|
|
207
|
-
break;
|
|
208
|
-
}
|
|
209
|
-
case "tool_start":
|
|
210
|
-
closeSegment();
|
|
211
|
-
if (event.toolName !== "ask_user") addLine(c.dim(` \u2699 ${event.toolName}`));
|
|
212
|
-
break;
|
|
213
|
-
case "tool_end":
|
|
214
|
-
if (event.isError) {
|
|
215
|
-
closeSegment();
|
|
216
|
-
addLine(c.warn(` \u26A0 tool error: ${String(event.result).slice(0, 140)}`));
|
|
217
|
-
}
|
|
218
|
-
break;
|
|
219
|
-
case "tool_suspended": {
|
|
220
|
-
closeSegment();
|
|
221
|
-
setLoader(false);
|
|
222
|
-
const payload = event.suspendPayload;
|
|
223
|
-
const question = payload?.question ?? `Janet needs input for ${event.toolName}.`;
|
|
224
|
-
const options = payload?.options;
|
|
225
|
-
const multi = payload?.selectionMode === "multi_select";
|
|
226
|
-
addLine(c.accentBold(` \u2753 ${question}`));
|
|
227
|
-
if (options?.length && !multi) {
|
|
228
|
-
const items = options.map((o) => ({
|
|
229
|
-
value: o.label,
|
|
230
|
-
label: o.label,
|
|
231
|
-
...o.description ? { description: o.description } : {}
|
|
232
|
-
}));
|
|
233
|
-
const select = new SelectList(items, Math.min(items.length, 8), editorTheme.selectList);
|
|
234
|
-
select.onSelect = (item) => answerQuestion(item.value, item.label);
|
|
235
|
-
activeSelect = select;
|
|
236
|
-
pendingQuestion = { toolCallId: event.toolCallId, options, multi: false };
|
|
237
|
-
chat.addChild(select);
|
|
238
|
-
addLine(c.dim(" \u2191/\u2193 to move, enter to choose."));
|
|
239
|
-
ui.setFocus(select);
|
|
240
|
-
} else {
|
|
241
|
-
pendingQuestion = { toolCallId: event.toolCallId, options, multi };
|
|
242
|
-
if (options?.length) {
|
|
243
|
-
options.forEach(
|
|
244
|
-
(o, i) => addLine(c.accent(` ${i + 1}. `) + o.label + (o.description ? c.dim(` \u2014 ${o.description}`) : ""))
|
|
245
|
-
);
|
|
246
|
-
addLine(c.dim(" Reply with numbers/labels (comma-separated), then enter."));
|
|
247
|
-
} else {
|
|
248
|
-
addLine(c.dim(" Type your answer and press enter."));
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
updateStatus();
|
|
252
|
-
break;
|
|
253
|
-
}
|
|
254
|
-
case "tool_approval_required":
|
|
255
|
-
closeSegment();
|
|
256
|
-
pendingApproval = { toolCallId: event.toolCallId, toolName: event.toolName };
|
|
257
|
-
addLine(
|
|
258
|
-
c.warn(` Janet wants to run ${c.bold(event.toolName)}.`) + c.dim(" y = yes \xB7 n = no \xB7 a = always allow this kind")
|
|
259
|
-
);
|
|
260
|
-
updateStatus();
|
|
261
|
-
break;
|
|
262
|
-
case "error": {
|
|
263
|
-
closeSegment();
|
|
264
|
-
const err = event.error;
|
|
265
|
-
addLine(
|
|
266
|
-
c.error(` \u2717 ${err?.message || "error"}${err?.responseBody ? ` \u2014 ${err.responseBody.slice(0, 200)}` : ""}`)
|
|
267
|
-
);
|
|
268
|
-
break;
|
|
269
|
-
}
|
|
270
|
-
case "model_changed":
|
|
271
|
-
updateStatus();
|
|
272
|
-
break;
|
|
273
|
-
case "agent_end":
|
|
274
|
-
running = false;
|
|
275
|
-
if (event.reason !== "suspended") pendingQuestion = null;
|
|
276
|
-
setLoader(false);
|
|
277
|
-
updateStatus();
|
|
278
|
-
break;
|
|
279
|
-
}
|
|
280
|
-
};
|
|
281
|
-
const unsubscribe = session.subscribe(onEvent);
|
|
282
|
-
const shutdown = async (code) => {
|
|
283
|
-
unsubscribe();
|
|
284
|
-
herdrDetach();
|
|
285
|
-
ui.stop();
|
|
286
|
-
await controller.destroy().catch(() => {
|
|
287
|
-
});
|
|
288
|
-
process.exit(code);
|
|
289
|
-
};
|
|
290
|
-
const promptInput = (message, placeholder) => {
|
|
291
|
-
addLine(c.accentBold(` ${message}`));
|
|
292
|
-
if (placeholder) addLine(c.dim(` (${placeholder})`));
|
|
293
|
-
updateStatus();
|
|
294
|
-
return new Promise((resolve) => {
|
|
295
|
-
pendingInput = resolve;
|
|
296
|
-
});
|
|
297
|
-
};
|
|
298
|
-
const showModelPicker = (intro) => {
|
|
299
|
-
const choices = availableModels();
|
|
300
|
-
if (intro) addLine(c.accentBold(intro));
|
|
301
|
-
if (!choices.length) {
|
|
302
|
-
addLine(c.dim(" No providers are configured yet. Set one up, then try again:"));
|
|
303
|
-
addLine(c.dim(" \u2022 Vertex AI: gcloud auth application-default login (+ GOOGLE_VERTEX_PROJECT)"));
|
|
304
|
-
addLine(c.dim(" \u2022 Anthropic: set ANTHROPIC_API_KEY, or /login anthropic"));
|
|
305
|
-
addLine(c.dim(" \u2022 OpenAI: set OPENAI_API_KEY, or /login openai-codex"));
|
|
306
|
-
addLine(c.dim(" \u2022 Bedrock: configure AWS credentials"));
|
|
307
|
-
updateStatus();
|
|
308
|
-
return;
|
|
309
|
-
}
|
|
310
|
-
const current = session.model.hasSelection() ? session.model.get() : null;
|
|
311
|
-
addLine(c.dim(" \u2191/\u2193 to move, enter to choose:"));
|
|
312
|
-
const select = new SelectList(
|
|
313
|
-
choices.map((ch) => ({
|
|
314
|
-
value: ch.id,
|
|
315
|
-
label: ch.id === current ? `${ch.label} (current)` : ch.label,
|
|
316
|
-
description: ch.via
|
|
317
|
-
})),
|
|
318
|
-
Math.min(choices.length, 10),
|
|
319
|
-
editorTheme.selectList
|
|
320
|
-
);
|
|
321
|
-
select.onSelect = (item) => {
|
|
322
|
-
chat.removeChild(select);
|
|
323
|
-
activeSelect = null;
|
|
324
|
-
ui.setFocus(editor);
|
|
325
|
-
void session.model.switch({ modelId: item.value });
|
|
326
|
-
completeOnboarding(item.value, (/* @__PURE__ */ new Date()).toISOString());
|
|
327
|
-
addLine(c.accentBold(` \u2713 Using ${item.value}.`) + c.dim(" (saved as your default)"));
|
|
328
|
-
updateStatus();
|
|
329
|
-
};
|
|
330
|
-
activeSelect = select;
|
|
331
|
-
chat.addChild(select);
|
|
332
|
-
ui.setFocus(select);
|
|
333
|
-
updateStatus();
|
|
334
|
-
};
|
|
335
|
-
const handleCommand = async (text) => {
|
|
336
|
-
const [cmd, ...rest] = text.slice(1).split(/\s+/);
|
|
337
|
-
switch (cmd) {
|
|
338
|
-
case "quit":
|
|
339
|
-
case "exit":
|
|
340
|
-
await shutdown(0);
|
|
341
|
-
break;
|
|
342
|
-
case "help":
|
|
343
|
-
addLine(c.dim(HELP_TEXT));
|
|
344
|
-
break;
|
|
345
|
-
case "login": {
|
|
346
|
-
const providerId = (rest[0] || "anthropic").trim();
|
|
347
|
-
if (!OAUTH_PROVIDERS.includes(providerId)) {
|
|
348
|
-
addLine(c.dim(`Usage: /login <${OAUTH_PROVIDERS.join(" | ")}>`));
|
|
349
|
-
break;
|
|
350
|
-
}
|
|
351
|
-
const authMode = rest[1]?.trim();
|
|
352
|
-
if (authMode && (providerId !== "openai-codex" || !["browser", "device"].includes(authMode))) {
|
|
353
|
-
addLine(c.dim("Usage: /login openai-codex [browser | device]"));
|
|
354
|
-
break;
|
|
355
|
-
}
|
|
356
|
-
addLine(c.dim(`Starting ${providerId} login\u2026`));
|
|
357
|
-
try {
|
|
358
|
-
await getAuthStorage().login(providerId, {
|
|
359
|
-
onAuth: (info) => {
|
|
360
|
-
addLine(c.accent(" Open this URL in your browser to authorize:"));
|
|
361
|
-
addLine(" " + info.url);
|
|
362
|
-
if (info.instructions) addLine(c.dim(" " + info.instructions));
|
|
363
|
-
},
|
|
364
|
-
onProgress: (m) => addLine(c.dim(" " + m)),
|
|
365
|
-
onManualCodeInput: () => promptInput("Paste the code shown after you authorize:"),
|
|
366
|
-
onPrompt: (p) => promptInput(p.message, p.placeholder),
|
|
367
|
-
...authMode ? { authMode } : {}
|
|
368
|
-
});
|
|
369
|
-
addLine(c.accentBold(` \u2713 Logged in to ${providerId}.`));
|
|
370
|
-
updateStatus();
|
|
371
|
-
} catch (err) {
|
|
372
|
-
addLine(c.error(` Login failed: ${err.message}`));
|
|
373
|
-
} finally {
|
|
374
|
-
pendingInput = null;
|
|
375
|
-
updateStatus();
|
|
376
|
-
}
|
|
377
|
-
break;
|
|
378
|
-
}
|
|
379
|
-
case "logout": {
|
|
380
|
-
const providerId = rest[0]?.trim();
|
|
381
|
-
if (!providerId) {
|
|
382
|
-
addLine(c.dim(`Usage: /logout <${OAUTH_PROVIDERS.join(" | ")}>`));
|
|
383
|
-
break;
|
|
384
|
-
}
|
|
385
|
-
const storage = getAuthStorage();
|
|
386
|
-
storage.logout(providerId);
|
|
387
|
-
storage.remove(`apikey:${providerId}`);
|
|
388
|
-
addLine(c.dim(`Logged out of ${providerId}.`));
|
|
389
|
-
break;
|
|
390
|
-
}
|
|
391
|
-
case "auth": {
|
|
392
|
-
const storage = getAuthStorage();
|
|
393
|
-
storage.reload();
|
|
394
|
-
const providers = storage.list();
|
|
395
|
-
if (!providers.length) {
|
|
396
|
-
addLine(c.dim("No stored credentials. Use /login <provider>, or set an API key env var"));
|
|
397
|
-
addLine(c.dim("(ANTHROPIC_API_KEY, OPENAI_API_KEY, GOOGLE_VERTEX_PROJECT, AWS_*)."));
|
|
398
|
-
} else {
|
|
399
|
-
for (const p of providers) {
|
|
400
|
-
const cred = storage.get(p);
|
|
401
|
-
addLine(c.dim(` ${p}: `) + (cred?.type === "oauth" ? c.accent("OAuth (subscription)") : "API key"));
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
break;
|
|
405
|
-
}
|
|
406
|
-
case "model": {
|
|
407
|
-
const inputId = rest.join(" ").trim();
|
|
408
|
-
if (!inputId) {
|
|
409
|
-
showModelPicker();
|
|
410
|
-
break;
|
|
411
|
-
}
|
|
412
|
-
const id = normalizeModelSelection(inputId, availableModels());
|
|
413
|
-
await session.model.switch({ modelId: id });
|
|
414
|
-
completeOnboarding(id, (/* @__PURE__ */ new Date()).toISOString());
|
|
415
|
-
rememberModel(id);
|
|
416
|
-
addLine(c.dim(`Model set to ${id}.`));
|
|
417
|
-
updateStatus();
|
|
418
|
-
break;
|
|
419
|
-
}
|
|
420
|
-
case "models":
|
|
421
|
-
showModelPicker();
|
|
422
|
-
break;
|
|
423
|
-
default:
|
|
424
|
-
addLine(c.dim(`Unknown command /${cmd}. Try /help.`));
|
|
425
|
-
}
|
|
426
|
-
};
|
|
427
|
-
editor.onSubmit = (raw) => {
|
|
428
|
-
const text = raw.trim();
|
|
429
|
-
editor.setText("");
|
|
430
|
-
if (!text) return;
|
|
431
|
-
if (!pendingInput && !pendingApproval && !pendingQuestion) {
|
|
432
|
-
editor.addToHistory(text);
|
|
433
|
-
}
|
|
434
|
-
if (pendingInput) {
|
|
435
|
-
const resolve = pendingInput;
|
|
436
|
-
pendingInput = null;
|
|
437
|
-
addLine(c.dim(" \u276F (value entered)"));
|
|
438
|
-
updateStatus();
|
|
439
|
-
resolve(text);
|
|
440
|
-
return;
|
|
441
|
-
}
|
|
442
|
-
if (pendingQuestion && !activeSelect) {
|
|
443
|
-
const resumeData = resolveAnswer(pendingQuestion, text);
|
|
444
|
-
if (resumeData === void 0) {
|
|
445
|
-
addLine(c.dim(" Didn't match an option \u2014 reply with a number or an exact label."));
|
|
446
|
-
return;
|
|
447
|
-
}
|
|
448
|
-
answerQuestion(resumeData, Array.isArray(resumeData) ? resumeData.join(", ") : resumeData);
|
|
449
|
-
return;
|
|
450
|
-
}
|
|
451
|
-
if (pendingApproval) {
|
|
452
|
-
const approve = /^y(es)?$/i.test(text);
|
|
453
|
-
const decline = /^n(o)?$/i.test(text);
|
|
454
|
-
const always = /^a(lways)?$/i.test(text);
|
|
455
|
-
if (approve || decline || always) {
|
|
456
|
-
const { toolCallId } = pendingApproval;
|
|
457
|
-
pendingApproval = null;
|
|
458
|
-
addLine(c.dim(always ? " \u2713 always allowed" : approve ? " \u2713 approved" : " \u2717 declined"));
|
|
459
|
-
updateStatus();
|
|
460
|
-
void session.respondToToolApproval({
|
|
461
|
-
decision: always ? "always_allow_category" : approve ? "approve" : "decline",
|
|
462
|
-
toolCallId
|
|
463
|
-
});
|
|
464
|
-
return;
|
|
465
|
-
}
|
|
466
|
-
addLine(c.dim(" Answer y (yes), n (no), or a (always allow) first."));
|
|
467
|
-
return;
|
|
468
|
-
}
|
|
469
|
-
if (text.startsWith("/")) {
|
|
470
|
-
void handleCommand(text);
|
|
471
|
-
return;
|
|
472
|
-
}
|
|
473
|
-
addLine(c.user(`\u276F ${text}`));
|
|
474
|
-
if (!session.model.hasSelection()) {
|
|
475
|
-
showModelPicker(" Pick a model first:");
|
|
476
|
-
return;
|
|
477
|
-
}
|
|
478
|
-
void session.sendMessage({ content: text }).catch((err) => {
|
|
479
|
-
running = false;
|
|
480
|
-
setLoader(false);
|
|
481
|
-
addLine(c.error(` \u2717 ${err.message}`));
|
|
482
|
-
updateStatus();
|
|
483
|
-
});
|
|
484
|
-
};
|
|
485
|
-
let lastCtrlC = 0;
|
|
486
|
-
editor.onCtrlC = () => {
|
|
487
|
-
const now = Date.now();
|
|
488
|
-
if (now - lastCtrlC < 800) {
|
|
489
|
-
void shutdown(0);
|
|
490
|
-
return;
|
|
491
|
-
}
|
|
492
|
-
lastCtrlC = now;
|
|
493
|
-
if (running) {
|
|
494
|
-
void session.abort();
|
|
495
|
-
addLine(c.dim(" (aborted \u2014 Ctrl+C again to quit)"));
|
|
496
|
-
} else if (editor.getText()) {
|
|
497
|
-
editor.setText("");
|
|
498
|
-
ui.requestRender();
|
|
499
|
-
} else {
|
|
500
|
-
addLine(c.dim(" (Ctrl+C again to quit)"));
|
|
501
|
-
}
|
|
502
|
-
};
|
|
503
|
-
addLine(c.accentBold(GREETING));
|
|
504
|
-
addLine(
|
|
505
|
-
c.dim(
|
|
506
|
-
`Knowledge bundle: ${paths.bundlePath}
|
|
507
|
-
Ask me anything in the bundle, or say what to ingest. /help for commands.`
|
|
508
|
-
)
|
|
509
|
-
);
|
|
510
|
-
updateStatus();
|
|
511
|
-
ui.start();
|
|
512
|
-
ui.setFocus(editor);
|
|
513
|
-
ui.requestRender();
|
|
514
|
-
if (!session.model.hasSelection()) showModelPicker(" Let's pick a model to get you started.");
|
|
515
|
-
return await new Promise(() => {
|
|
516
|
-
});
|
|
517
|
-
}
|
|
518
|
-
export {
|
|
519
|
-
runTui
|
|
520
|
-
};
|
|
521
|
-
//# sourceMappingURL=tui-VZVO7UHV.js.map
|
package/dist/tui-VZVO7UHV.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/tui/index.ts","../src/tui/theme.ts"],"sourcesContent":["/**\n * Janet's interactive TUI — a minimal pi-tui chat.\n *\n * The transcript renders in strict chronological order: each run of assistant\n * text becomes its own markdown block, and a tool line / question / approval\n * \"closes\" the current block so the next text appears BELOW it (rather than the\n * whole answer streaming at the top while tools pile up underneath).\n *\n * Approvals are governed by the controller's tool-category policy: reads,\n * skills, task bookkeeping, ask_user, and bundle edits never prompt. Execution,\n * MCP, and unknown future tools ask — and the prompt offers \"always allow\" for\n * the session. Questions with options render as an arrow-key SelectList.\n */\nimport {\n Container,\n Editor,\n Loader,\n Markdown,\n ProcessTerminal,\n SelectList,\n Spacer,\n TUI,\n Text,\n} from \"@earendil-works/pi-tui\";\nimport type { Component, SelectItem } from \"@earendil-works/pi-tui\";\nimport type { AgentControllerEvent } from \"@mastra/core/agent-controller\";\nimport { bootJanet, type BootOptions } from \"../agent/controller.js\";\nimport { messageText } from \"../headless/format.js\";\nimport { GREETING } from \"../agent/persona.js\";\nimport { getAuthStorage } from \"../gateways/oauth/claude-max.js\";\nimport { loadSettings, completeOnboarding, rememberModel } from \"../onboarding/settings.js\";\nimport { availableModels, normalizeModelSelection } from \"../onboarding/providers.js\";\nimport { c, editorTheme, markdownTheme } from \"./theme.js\";\n\n/** OAuth providers janet can log in to. */\nconst OAUTH_PROVIDERS = [\"anthropic\", \"openai-codex\"] as const;\n\n/** Editor with a Ctrl+C hook (raw-mode terminals deliver it as input \\x03). */\nclass JanetEditor extends Editor {\n onCtrlC?: () => void;\n override handleInput(data: string): void {\n if (data === \"\\x03\") {\n this.onCtrlC?.();\n return;\n }\n super.handleInput(data);\n }\n}\n\nconst HELP_TEXT = `Commands:\n /models Pick a model from a list (arrow keys)\n /model [provider/id] Open the picker, or switch directly by id\n /login <provider> [mode]\n Log in; OpenAI mode is browser or device\n /logout <provider> Remove stored credentials for a provider\n /auth Show which providers are authenticated\n /help This help\n /quit Exit (double Ctrl+C also works)\n\nAnything else is a message to Janet.`;\n\ninterface PendingApproval {\n toolCallId: string;\n toolName: string;\n}\n\ninterface QuestionOption {\n label: string;\n description?: string;\n}\n\ninterface PendingQuestion {\n toolCallId: string;\n options?: QuestionOption[];\n multi: boolean;\n}\n\n/** The assistant text block currently being streamed (one segment between tools). */\ninterface ActiveMessage {\n id: string;\n committedLen: number;\n comp: Markdown | null;\n lastText: string;\n}\n\n/** Map a typed answer to ask_user resume data (free-text or multi-select). */\nfunction resolveAnswer(q: PendingQuestion, text: string): string | string[] | undefined {\n if (!q.options?.length) return text;\n const opts = q.options;\n const pick = (token: string): string | undefined => {\n const t = token.trim();\n if (!t) return undefined;\n const n = Number(t);\n if (Number.isInteger(n) && n >= 1 && n <= opts.length) return opts[n - 1]!.label;\n const exact = opts.find((o) => o.label.toLowerCase() === t.toLowerCase());\n if (exact) return exact.label;\n return opts.find((o) => o.label.toLowerCase().startsWith(t.toLowerCase()))?.label;\n };\n if (q.multi) {\n const picks = text.split(\",\").map(pick);\n return picks.some((p) => p === undefined) ? undefined : (picks as string[]);\n }\n return pick(text);\n}\n\nexport async function runTui(opts: Omit<BootOptions, \"interactive\">): Promise<number> {\n const { controller, session, paths, herdrDetach } = await bootJanet({ ...opts, interactive: true });\n\n // The interactive approval policy is set deterministically in the controller's\n // initialState (reads/edits/meta never prompt; only execute asks, with an\n // \"always allow\" option) — see INTERACTIVE_RULES in controller.ts.\n\n // Model precedence: an already-persisted per-thread selection, else\n // JANET_MODEL, else the global onboarding default. If none, the first-run\n // wizard runs after the UI is up.\n const persistedModel = process.env[\"JANET_MODEL\"] || loadSettings().defaultModelId;\n const presetModel = persistedModel\n ? normalizeModelSelection(persistedModel, availableModels())\n : undefined;\n if (!session.model.hasSelection() && presetModel) {\n await session.model.switch({ modelId: presetModel });\n }\n\n const terminal = new ProcessTerminal();\n const ui = new TUI(terminal);\n const chat = new Container();\n const status = new Text(\"\", 1, 0);\n const editor = new JanetEditor(ui, editorTheme);\n const loader = new Loader(ui, c.accent, c.dim, \"Janet is thinking…\");\n\n ui.addChild(chat);\n ui.addChild(new Spacer(1));\n ui.addChild(editor);\n ui.addChild(status);\n\n let running = false;\n let loaderMounted = false;\n let pendingApproval: PendingApproval | null = null;\n let pendingQuestion: PendingQuestion | null = null;\n let pendingInput: ((text: string) => void) | null = null;\n let activeSelect: SelectList | null = null;\n let active: ActiveMessage | null = null;\n\n const updateStatus = (): void => {\n const model = session.model.hasSelection() ? session.model.get() : \"no model — /model <id>\";\n const state =\n pendingInput\n ? \"enter the requested value\"\n : pendingQuestion || activeSelect\n ? \"answer Janet's question\"\n : pendingApproval\n ? \"awaiting approval\"\n : running\n ? \"working\"\n : \"idle\";\n status.setText(c.dim(`${paths.projectPath} · `) + c.accent(model) + c.dim(` · ${state}`));\n ui.requestRender();\n };\n\n // Keep the spinner (and any focused select) visually last by inserting new\n // content before them.\n const appendToChat = (comp: Component): void => {\n if (loaderMounted) chat.removeChild(loader);\n if (activeSelect) chat.removeChild(activeSelect);\n chat.addChild(comp);\n if (activeSelect) chat.addChild(activeSelect);\n if (loaderMounted) chat.addChild(loader);\n ui.requestRender();\n };\n\n const addLine = (text: string): void => appendToChat(new Text(text, 1, 0));\n\n const setLoader = (on: boolean): void => {\n if (on && !loaderMounted) {\n chat.addChild(loader);\n loader.start();\n loaderMounted = true;\n } else if (!on && loaderMounted) {\n loader.stop();\n chat.removeChild(loader);\n loaderMounted = false;\n }\n ui.requestRender();\n };\n\n // Freeze the current text segment so the next assistant text starts a new\n // block below whatever we're about to insert (a tool line, question, etc.).\n const closeSegment = (): void => {\n if (active) {\n active.committedLen = active.lastText.length;\n active.comp = null;\n }\n };\n\n const answerQuestion = (resumeData: string | string[], echo: string): void => {\n if (activeSelect) {\n chat.removeChild(activeSelect);\n activeSelect = null;\n }\n const q = pendingQuestion;\n pendingQuestion = null;\n ui.setFocus(editor);\n addLine(c.user(`❯ ${echo}`));\n setLoader(true);\n updateStatus();\n if (q) void session.respondToToolSuspension({ toolCallId: q.toolCallId, resumeData });\n };\n\n const onEvent = (event: AgentControllerEvent): void => {\n switch (event.type) {\n case \"agent_start\":\n running = true;\n active = null;\n setLoader(true);\n updateStatus();\n break;\n case \"message_update\":\n case \"message_end\": {\n if (event.message.role !== \"assistant\") break;\n const text = messageText(event.message);\n if (!text) break;\n if (!active || active.id !== event.message.id) {\n active = { id: event.message.id, committedLen: 0, comp: null, lastText: \"\" };\n }\n active.lastText = text;\n const tail = text.slice(active.committedLen);\n if (!tail) break;\n if (!active.comp) {\n active.comp = new Markdown(tail, 1, 0, markdownTheme);\n appendToChat(active.comp);\n } else {\n active.comp.setText(tail);\n ui.requestRender();\n }\n break;\n }\n case \"tool_start\":\n closeSegment();\n if (event.toolName !== \"ask_user\") addLine(c.dim(` ⚙ ${event.toolName}`));\n break;\n case \"tool_end\":\n if (event.isError) {\n closeSegment();\n addLine(c.warn(` ⚠ tool error: ${String(event.result).slice(0, 140)}`));\n }\n break;\n case \"tool_suspended\": {\n closeSegment();\n setLoader(false);\n const payload = event.suspendPayload as {\n question?: string;\n options?: QuestionOption[];\n selectionMode?: string;\n };\n const question = payload?.question ?? `Janet needs input for ${event.toolName}.`;\n const options = payload?.options;\n const multi = payload?.selectionMode === \"multi_select\";\n addLine(c.accentBold(` ❓ ${question}`));\n\n if (options?.length && !multi) {\n // Arrow-key selection (↑/↓, enter), like a native picker.\n const items: SelectItem[] = options.map((o) => ({\n value: o.label,\n label: o.label,\n ...(o.description ? { description: o.description } : {}),\n }));\n const select = new SelectList(items, Math.min(items.length, 8), editorTheme.selectList);\n select.onSelect = (item: SelectItem) => answerQuestion(item.value, item.label);\n activeSelect = select;\n pendingQuestion = { toolCallId: event.toolCallId, options, multi: false };\n chat.addChild(select);\n addLine(c.dim(\" ↑/↓ to move, enter to choose.\"));\n ui.setFocus(select);\n } else {\n pendingQuestion = { toolCallId: event.toolCallId, options, multi };\n if (options?.length) {\n options.forEach((o, i) =>\n addLine(c.accent(` ${i + 1}. `) + o.label + (o.description ? c.dim(` — ${o.description}`) : \"\")),\n );\n addLine(c.dim(\" Reply with numbers/labels (comma-separated), then enter.\"));\n } else {\n addLine(c.dim(\" Type your answer and press enter.\"));\n }\n }\n updateStatus();\n break;\n }\n case \"tool_approval_required\":\n closeSegment();\n pendingApproval = { toolCallId: event.toolCallId, toolName: event.toolName };\n addLine(\n c.warn(` Janet wants to run ${c.bold(event.toolName)}.`) +\n c.dim(\" y = yes · n = no · a = always allow this kind\"),\n );\n updateStatus();\n break;\n case \"error\": {\n closeSegment();\n const err = event.error as Error & { responseBody?: string };\n addLine(\n c.error(` ✗ ${err?.message || \"error\"}${err?.responseBody ? ` — ${err.responseBody.slice(0, 200)}` : \"\"}`),\n );\n break;\n }\n case \"model_changed\":\n updateStatus();\n break;\n case \"agent_end\":\n running = false;\n if (event.reason !== \"suspended\") pendingQuestion = null;\n setLoader(false);\n updateStatus();\n break;\n }\n };\n const unsubscribe = session.subscribe(onEvent);\n\n const shutdown = async (code: number): Promise<never> => {\n unsubscribe();\n herdrDetach();\n ui.stop();\n await controller.destroy().catch(() => {});\n process.exit(code);\n };\n\n // Ask the user for one value; the next editor submit resolves it. Used by the\n // OAuth login flow (paste-code / prompts).\n const promptInput = (message: string, placeholder?: string): Promise<string> => {\n addLine(c.accentBold(` ${message}`));\n if (placeholder) addLine(c.dim(` (${placeholder})`));\n updateStatus();\n return new Promise((resolve) => {\n pendingInput = resolve;\n });\n };\n\n // Interactive model picker: an arrow-key list of models from the providers\n // reachable right now. Selecting one switches the session and persists it as\n // the default. Shared by /models, /model (no arg), and first-run onboarding.\n const showModelPicker = (intro?: string): void => {\n const choices = availableModels();\n if (intro) addLine(c.accentBold(intro));\n if (!choices.length) {\n addLine(c.dim(\" No providers are configured yet. Set one up, then try again:\"));\n addLine(c.dim(\" • Vertex AI: gcloud auth application-default login (+ GOOGLE_VERTEX_PROJECT)\"));\n addLine(c.dim(\" • Anthropic: set ANTHROPIC_API_KEY, or /login anthropic\"));\n addLine(c.dim(\" • OpenAI: set OPENAI_API_KEY, or /login openai-codex\"));\n addLine(c.dim(\" • Bedrock: configure AWS credentials\"));\n updateStatus();\n return;\n }\n const current = session.model.hasSelection() ? session.model.get() : null;\n addLine(c.dim(\" ↑/↓ to move, enter to choose:\"));\n const select = new SelectList(\n choices.map((ch) => ({\n value: ch.id,\n label: ch.id === current ? `${ch.label} (current)` : ch.label,\n description: ch.via,\n })),\n Math.min(choices.length, 10),\n editorTheme.selectList,\n );\n select.onSelect = (item: SelectItem) => {\n chat.removeChild(select);\n activeSelect = null;\n ui.setFocus(editor);\n void session.model.switch({ modelId: item.value });\n completeOnboarding(item.value, new Date().toISOString());\n addLine(c.accentBold(` ✓ Using ${item.value}.`) + c.dim(\" (saved as your default)\"));\n updateStatus();\n };\n activeSelect = select;\n chat.addChild(select);\n ui.setFocus(select);\n updateStatus();\n };\n\n const handleCommand = async (text: string): Promise<void> => {\n const [cmd, ...rest] = text.slice(1).split(/\\s+/);\n switch (cmd) {\n case \"quit\":\n case \"exit\":\n await shutdown(0);\n break;\n case \"help\":\n addLine(c.dim(HELP_TEXT));\n break;\n case \"login\": {\n const providerId = (rest[0] || \"anthropic\").trim();\n if (!(OAUTH_PROVIDERS as readonly string[]).includes(providerId)) {\n addLine(c.dim(`Usage: /login <${OAUTH_PROVIDERS.join(\" | \")}>`));\n break;\n }\n const authMode = rest[1]?.trim();\n if (\n authMode &&\n (providerId !== \"openai-codex\" || ![\"browser\", \"device\"].includes(authMode))\n ) {\n addLine(c.dim(\"Usage: /login openai-codex [browser | device]\"));\n break;\n }\n addLine(c.dim(`Starting ${providerId} login…`));\n try {\n await getAuthStorage().login(providerId, {\n onAuth: (info) => {\n addLine(c.accent(\" Open this URL in your browser to authorize:\"));\n addLine(\" \" + info.url);\n if (info.instructions) addLine(c.dim(\" \" + info.instructions));\n },\n onProgress: (m) => addLine(c.dim(\" \" + m)),\n onManualCodeInput: () => promptInput(\"Paste the code shown after you authorize:\"),\n onPrompt: (p) => promptInput(p.message, p.placeholder),\n ...(authMode ? { authMode } : {}),\n });\n addLine(c.accentBold(` ✓ Logged in to ${providerId}.`));\n updateStatus();\n } catch (err) {\n addLine(c.error(` Login failed: ${(err as Error).message}`));\n } finally {\n // A successful browser callback can win the race with the manual-code\n // prompt. Disarm that abandoned prompt so it cannot consume the next\n // chat message after login completes.\n pendingInput = null;\n updateStatus();\n }\n break;\n }\n case \"logout\": {\n const providerId = rest[0]?.trim();\n if (!providerId) {\n addLine(c.dim(`Usage: /logout <${OAUTH_PROVIDERS.join(\" | \")}>`));\n break;\n }\n const storage = getAuthStorage();\n storage.logout(providerId); // OAuth credential\n storage.remove(`apikey:${providerId}`); // stored API key slot, if any\n addLine(c.dim(`Logged out of ${providerId}.`));\n break;\n }\n case \"auth\": {\n const storage = getAuthStorage();\n storage.reload();\n const providers = storage.list();\n if (!providers.length) {\n addLine(c.dim(\"No stored credentials. Use /login <provider>, or set an API key env var\"));\n addLine(c.dim(\"(ANTHROPIC_API_KEY, OPENAI_API_KEY, GOOGLE_VERTEX_PROJECT, AWS_*).\"));\n } else {\n for (const p of providers) {\n const cred = storage.get(p);\n addLine(c.dim(` ${p}: `) + (cred?.type === \"oauth\" ? c.accent(\"OAuth (subscription)\") : \"API key\"));\n }\n }\n break;\n }\n case \"model\": {\n const inputId = rest.join(\" \").trim();\n // No id → open the picker; an explicit id still works for power users.\n if (!inputId) {\n showModelPicker();\n break;\n }\n const id = normalizeModelSelection(inputId, availableModels());\n await session.model.switch({ modelId: id });\n completeOnboarding(id, new Date().toISOString());\n rememberModel(id); // so a hand-typed model shows up in the picker next time\n addLine(c.dim(`Model set to ${id}.`));\n updateStatus();\n break;\n }\n case \"models\":\n showModelPicker();\n break;\n default:\n addLine(c.dim(`Unknown command /${cmd}. Try /help.`));\n }\n };\n\n editor.onSubmit = (raw: string) => {\n const text = raw.trim();\n editor.setText(\"\");\n if (!text) return;\n\n // Feed normal prompt input (messages + slash commands) into the editor's\n // built-in up/down history. Skip transient responses — approvals, question\n // answers, and paste-codes shouldn't clutter recall.\n if (!pendingInput && !pendingApproval && !pendingQuestion) {\n editor.addToHistory(text);\n }\n\n // A requested value (e.g. an OAuth paste-code) consumes the next submit.\n // Don't echo it verbatim — it may be a credential.\n if (pendingInput) {\n const resolve = pendingInput;\n pendingInput = null;\n addLine(c.dim(\" ❯ (value entered)\"));\n updateStatus();\n resolve(text);\n return;\n }\n\n // A typed question (free-text or multi-select) consumes the next submit.\n if (pendingQuestion && !activeSelect) {\n const resumeData = resolveAnswer(pendingQuestion, text);\n if (resumeData === undefined) {\n addLine(c.dim(\" Didn't match an option — reply with a number or an exact label.\"));\n return;\n }\n answerQuestion(resumeData, Array.isArray(resumeData) ? resumeData.join(\", \") : resumeData);\n return;\n }\n\n // Pending tool approval: y / n / a (always allow this category).\n if (pendingApproval) {\n const approve = /^y(es)?$/i.test(text);\n const decline = /^n(o)?$/i.test(text);\n const always = /^a(lways)?$/i.test(text);\n if (approve || decline || always) {\n const { toolCallId } = pendingApproval;\n pendingApproval = null;\n addLine(c.dim(always ? \" ✓ always allowed\" : approve ? \" ✓ approved\" : \" ✗ declined\"));\n updateStatus();\n void session.respondToToolApproval({\n decision: always ? \"always_allow_category\" : approve ? \"approve\" : \"decline\",\n toolCallId,\n });\n return;\n }\n addLine(c.dim(\" Answer y (yes), n (no), or a (always allow) first.\"));\n return;\n }\n\n if (text.startsWith(\"/\")) {\n void handleCommand(text);\n return;\n }\n\n addLine(c.user(`❯ ${text}`));\n if (!session.model.hasSelection()) {\n showModelPicker(\" Pick a model first:\");\n return;\n }\n void session.sendMessage({ content: text }).catch((err: Error) => {\n running = false;\n setLoader(false);\n addLine(c.error(` ✗ ${err.message}`));\n updateStatus();\n });\n };\n\n // Double Ctrl+C exits; single clears input or aborts a running turn.\n let lastCtrlC = 0;\n editor.onCtrlC = () => {\n const now = Date.now();\n if (now - lastCtrlC < 800) {\n void shutdown(0);\n return;\n }\n lastCtrlC = now;\n if (running) {\n void session.abort();\n addLine(c.dim(\" (aborted — Ctrl+C again to quit)\"));\n } else if (editor.getText()) {\n editor.setText(\"\");\n ui.requestRender();\n } else {\n addLine(c.dim(\" (Ctrl+C again to quit)\"));\n }\n };\n\n addLine(c.accentBold(GREETING));\n addLine(\n c.dim(\n `Knowledge bundle: ${paths.bundlePath}\\n` +\n `Ask me anything in the bundle, or say what to ingest. /help for commands.`,\n ),\n );\n updateStatus();\n ui.start();\n ui.setFocus(editor);\n ui.requestRender();\n\n // First run (no model configured): open the picker to get set up.\n if (!session.model.hasSelection()) showModelPicker(\" Let's pick a model to get you started.\");\n\n // The TUI owns the process from here; exit happens via shutdown().\n return await new Promise<number>(() => {});\n}\n","import chalk from \"chalk\";\nimport type { EditorTheme } from \"@earendil-works/pi-tui\";\n\n/**\n * Janet's minimal terminal theme. Good Place warm: cyan accents, soft dims.\n * Kept tiny on purpose — no gradients, no branding machinery.\n */\nexport const c = {\n accent: chalk.cyan,\n accentBold: chalk.cyan.bold,\n dim: chalk.dim,\n user: chalk.green,\n error: chalk.red,\n warn: chalk.yellow,\n bold: chalk.bold,\n italic: chalk.italic,\n};\n\nexport const editorTheme: EditorTheme = {\n borderColor: (s: string) => chalk.cyan(s),\n selectList: {\n selectedPrefix: (s: string) => chalk.cyan(s),\n selectedText: (s: string) => chalk.cyan.bold(s),\n description: (s: string) => chalk.dim(s),\n scrollInfo: (s: string) => chalk.dim(s),\n noMatch: (s: string) => chalk.dim(s),\n },\n};\n\nexport const markdownTheme = {\n heading: (s: string) => chalk.cyan.bold(s),\n link: (s: string) => chalk.cyan.underline(s),\n linkUrl: (s: string) => chalk.dim(s),\n code: (s: string) => chalk.yellow(s),\n codeBlock: (s: string) => chalk.yellow(s),\n codeBlockBorder: (s: string) => chalk.dim(s),\n quote: (s: string) => chalk.italic(s),\n quoteBorder: (s: string) => chalk.dim(s),\n hr: (s: string) => chalk.dim(s),\n listBullet: (s: string) => chalk.cyan(s),\n bold: (s: string) => chalk.bold(s),\n italic: (s: string) => chalk.italic(s),\n strikethrough: (s: string) => chalk.strikethrough(s),\n underline: (s: string) => chalk.underline(s),\n};\n"],"mappings":"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","names":[]}
|