@corenel/term 0.2.9 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/ink/index.js CHANGED
@@ -1,12 +1,16 @@
1
1
  import {
2
2
  TermSession,
3
+ ageWord,
4
+ formatContext,
5
+ mcpStatusWord,
3
6
  parseMarkdown
4
- } from "../chunk-QZJTOVZL.js";
5
- import "../chunk-L4KCEKIT.js";
7
+ } from "../chunk-HUVRQX53.js";
8
+ import "../chunk-2MGDL3I4.js";
6
9
  import {
10
+ TERM_COMMANDS,
7
11
  filterCommands,
8
12
  parseCommand
9
- } from "../chunk-VTJEWHM3.js";
13
+ } from "../chunk-5RPHYXND.js";
10
14
  import "../chunk-M44CET5L.js";
11
15
  import {
12
16
  formatEndingChange
@@ -28,7 +32,7 @@ import { STANDARD_POLICY } from "@corenel/harness/guardrail/builtins";
28
32
  // src/ink/App.tsx
29
33
  import { useEffect, useState } from "react";
30
34
  import { homedir } from "node:os";
31
- import { Box as Box5, Static, Text as Text5, useStdout } from "ink";
35
+ import { Box as Box8, Static, Text as Text8, useStdout } from "ink";
32
36
  import TextInput from "ink-text-input";
33
37
 
34
38
  // src/ink/items.tsx
@@ -169,27 +173,132 @@ function LiveToolRow({ tool }) {
169
173
  ] });
170
174
  }
171
175
 
172
- // src/ink/Modal.tsx
176
+ // src/ink/McpOverlay.tsx
173
177
  import { Box as Box2, Text as Text2 } from "ink";
174
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
178
+ import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
179
+ function statusColor(word) {
180
+ if (word === "connected") return palette.good;
181
+ if (word === "unreachable") return palette.bad;
182
+ return palette.warn;
183
+ }
184
+ function McpOverlay({ modal }) {
185
+ return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", borderStyle: "round", borderColor: palette.tool, paddingX: 1, children: [
186
+ /* @__PURE__ */ jsx2(Text2, { color: palette.tool, bold: true, children: "MCP servers" }),
187
+ modal.servers.length === 0 ? /* @__PURE__ */ jsx2(Box2, { marginTop: 1, children: /* @__PURE__ */ jsx2(Text2, { color: palette.dim, children: "none configured -- press a to add one" }) }) : /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", marginTop: 1, children: modal.servers.map((s, i) => {
188
+ const word = mcpStatusWord(s);
189
+ const isSelected = i === modal.selected;
190
+ return /* @__PURE__ */ jsxs2(Box2, { children: [
191
+ /* @__PURE__ */ jsx2(Text2, { color: palette.ember, children: isSelected ? `${glyph.active} ` : " " }),
192
+ /* @__PURE__ */ jsx2(Text2, { bold: isSelected, children: s.name }),
193
+ /* @__PURE__ */ jsx2(Text2, { color: palette.dim, children: " " }),
194
+ /* @__PURE__ */ jsx2(Text2, { color: statusColor(word), children: word }),
195
+ /* @__PURE__ */ jsxs2(Text2, { color: palette.dim, children: [
196
+ " ",
197
+ s.toolCount,
198
+ " tool",
199
+ s.toolCount === 1 ? "" : "s",
200
+ ", ",
201
+ ageWord(s.fetchedAt, Date.now())
202
+ ] })
203
+ ] }, s.name);
204
+ }) }),
205
+ modal.login ? /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", marginTop: 1, children: modal.login.url ? /* @__PURE__ */ jsxs2(Fragment, { children: [
206
+ /* @__PURE__ */ jsxs2(Text2, { color: palette.amber, children: [
207
+ "signing in to ",
208
+ modal.login.server,
209
+ " -- open this URL:"
210
+ ] }),
211
+ /* @__PURE__ */ jsx2(Text2, { children: modal.login.url }),
212
+ /* @__PURE__ */ jsx2(Text2, { color: palette.dim, children: "esc cancels" })
213
+ ] }) : /* @__PURE__ */ jsxs2(Text2, { color: palette.amber, children: [
214
+ "signing in to ",
215
+ modal.login.server,
216
+ " -- waiting for a login URL..."
217
+ ] }) }) : null,
218
+ /* @__PURE__ */ jsx2(Box2, { marginTop: 1, children: /* @__PURE__ */ jsxs2(Text2, { color: palette.dim, children: [
219
+ glyph.active,
220
+ " select ",
221
+ glyph.sep,
222
+ " a add ",
223
+ glyph.sep,
224
+ " x remove ",
225
+ glyph.sep,
226
+ " l login ",
227
+ glyph.sep,
228
+ " r refresh ",
229
+ glyph.sep,
230
+ " esc close"
231
+ ] }) })
232
+ ] });
233
+ }
234
+
235
+ // src/ink/HelpOverlay.tsx
236
+ import { Box as Box3, Text as Text3 } from "ink";
237
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
238
+ function HelpOverlay() {
239
+ const width = Math.max(...TERM_COMMANDS.map((c) => `${c.name} ${c.args ?? ""}`.trim().length));
240
+ return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", borderStyle: "round", borderColor: palette.tool, paddingX: 1, children: [
241
+ /* @__PURE__ */ jsx3(Text3, { color: palette.tool, bold: true, children: "Commands" }),
242
+ /* @__PURE__ */ jsx3(Box3, { flexDirection: "column", marginTop: 1, children: TERM_COMMANDS.map((c) => {
243
+ const usage = `${c.name} ${c.args ?? ""}`.trim();
244
+ return /* @__PURE__ */ jsxs3(Box3, { children: [
245
+ /* @__PURE__ */ jsxs3(Text3, { color: palette.ember, children: [
246
+ "/",
247
+ usage.padEnd(width)
248
+ ] }),
249
+ /* @__PURE__ */ jsxs3(Text3, { color: palette.dim, children: [
250
+ " ",
251
+ c.summary
252
+ ] })
253
+ ] }, c.name);
254
+ }) }),
255
+ /* @__PURE__ */ jsx3(Box3, { marginTop: 1, children: /* @__PURE__ */ jsx3(Text3, { color: palette.dim, children: "esc close" }) })
256
+ ] });
257
+ }
258
+
259
+ // src/ink/ContextOverlay.tsx
260
+ import { Box as Box4, Text as Text4 } from "ink";
261
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
262
+ function ContextOverlay({ snapshot }) {
263
+ const text = formatContext(snapshot);
264
+ return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", borderStyle: "round", borderColor: palette.tool, paddingX: 1, children: [
265
+ /* @__PURE__ */ jsx4(Text4, { color: palette.tool, bold: true, children: "Context window" }),
266
+ /* @__PURE__ */ jsx4(Box4, { flexDirection: "column", marginTop: 1, children: /* @__PURE__ */ jsx4(Text4, { children: text }) }),
267
+ /* @__PURE__ */ jsx4(Box4, { marginTop: 1, children: /* @__PURE__ */ jsx4(Text4, { color: palette.dim, children: "esc close" }) })
268
+ ] });
269
+ }
270
+ function contextSnapshotFromState(state, contextWindow, toolCount) {
271
+ return {
272
+ sentTokens: state.sentTokens,
273
+ contextWindow,
274
+ offloaded: state.offloaded,
275
+ checkpoints: state.checkpoints,
276
+ toolCount,
277
+ loadedToolNames: state.toolNames
278
+ };
279
+ }
280
+
281
+ // src/ink/Modal.tsx
282
+ import { Box as Box5, Text as Text5 } from "ink";
283
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
175
284
  function DiffPreview({ diff }) {
176
285
  const contentUnchanged = diff.added === 0 && diff.removed === 0;
177
- const endingRow = diff.endingChange && /* @__PURE__ */ jsx2(Text2, { color: palette.amber, bold: true, children: formatEndingChange(diff.endingChange, contentUnchanged) });
178
- const unexplainedRow = diff.unexplainedChange && /* @__PURE__ */ jsx2(Text2, { color: palette.amber, bold: true, children: "the file changes in a way not shown above (e.g. trailing whitespace or encoding)" });
286
+ const endingRow = diff.endingChange && /* @__PURE__ */ jsx5(Text5, { color: palette.amber, bold: true, children: formatEndingChange(diff.endingChange, contentUnchanged) });
287
+ const unexplainedRow = diff.unexplainedChange && /* @__PURE__ */ jsx5(Text5, { color: palette.amber, bold: true, children: "the file changes in a way not shown above (e.g. trailing whitespace or encoding)" });
179
288
  if (diff.noop) {
180
- return /* @__PURE__ */ jsx2(Text2, { color: palette.dim, children: "(no change -- new content is identical to what's on disk)" });
289
+ return /* @__PURE__ */ jsx5(Text5, { color: palette.dim, children: "(no change -- new content is identical to what's on disk)" });
181
290
  }
182
- return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
291
+ return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
183
292
  endingRow,
184
293
  unexplainedRow,
185
- diff.isNewFile && /* @__PURE__ */ jsx2(Text2, { color: palette.dim, children: "(new file)" }),
294
+ diff.isNewFile && /* @__PURE__ */ jsx5(Text5, { color: palette.dim, children: "(new file)" }),
186
295
  diff.rows.map((r, i) => {
187
296
  if (r.kind === "skip") {
188
297
  const label = r.reason === "changes" ? `\u2026 ${r.count} more changed line${r.count === 1 ? "" : "s"}` : `\u2026 ${r.count} unchanged line${r.count === 1 ? "" : "s"}`;
189
- return /* @__PURE__ */ jsx2(Text2, { color: palette.dim, children: label }, i);
298
+ return /* @__PURE__ */ jsx5(Text5, { color: palette.dim, children: label }, i);
190
299
  }
191
- return /* @__PURE__ */ jsxs2(
192
- Text2,
300
+ return /* @__PURE__ */ jsxs5(
301
+ Text5,
193
302
  {
194
303
  color: r.kind === "add" ? palette.good : r.kind === "del" ? palette.bad : palette.dim,
195
304
  children: [
@@ -200,7 +309,7 @@ function DiffPreview({ diff }) {
200
309
  i
201
310
  );
202
311
  }),
203
- /* @__PURE__ */ jsxs2(Text2, { color: palette.dim, children: [
312
+ /* @__PURE__ */ jsxs5(Text5, { color: palette.dim, children: [
204
313
  "+",
205
314
  diff.added,
206
315
  " \u2212",
@@ -210,37 +319,37 @@ function DiffPreview({ diff }) {
210
319
  }
211
320
  function ModalView({ modal }) {
212
321
  if (modal.kind === "permission") {
213
- return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", borderStyle: "round", borderColor: palette.warn, paddingX: 1, children: [
214
- /* @__PURE__ */ jsxs2(Text2, { color: palette.warn, children: [
322
+ return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", borderStyle: "round", borderColor: palette.warn, paddingX: 1, children: [
323
+ /* @__PURE__ */ jsxs5(Text5, { color: palette.warn, children: [
215
324
  modal.tool,
216
325
  " wants to run"
217
326
  ] }),
218
- /* @__PURE__ */ jsx2(Box2, { marginTop: 1, children: /* @__PURE__ */ jsx2(Text2, { bold: true, children: modal.detail }) }),
219
- modal.diff && /* @__PURE__ */ jsx2(Box2, { marginTop: 1, children: /* @__PURE__ */ jsx2(DiffPreview, { diff: modal.diff }) }),
220
- /* @__PURE__ */ jsxs2(Box2, { marginTop: 1, children: [
221
- /* @__PURE__ */ jsx2(Text2, { color: palette.good, children: "[y]" }),
222
- /* @__PURE__ */ jsx2(Text2, { color: palette.dim, children: " run it " }),
223
- /* @__PURE__ */ jsx2(Text2, { color: palette.bad, children: "[n]" }),
224
- /* @__PURE__ */ jsx2(Text2, { color: palette.dim, children: " deny esc denies" })
327
+ /* @__PURE__ */ jsx5(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text5, { bold: true, children: modal.detail }) }),
328
+ modal.diff && /* @__PURE__ */ jsx5(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx5(DiffPreview, { diff: modal.diff }) }),
329
+ /* @__PURE__ */ jsxs5(Box5, { marginTop: 1, children: [
330
+ /* @__PURE__ */ jsx5(Text5, { color: palette.good, children: "[y]" }),
331
+ /* @__PURE__ */ jsx5(Text5, { color: palette.dim, children: " run it " }),
332
+ /* @__PURE__ */ jsx5(Text5, { color: palette.bad, children: "[n]" }),
333
+ /* @__PURE__ */ jsx5(Text5, { color: palette.dim, children: " deny esc denies" })
225
334
  ] })
226
335
  ] });
227
336
  }
228
337
  const q = modal.questions[0];
229
- return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", borderStyle: "round", borderColor: palette.ember, paddingX: 1, children: [
230
- /* @__PURE__ */ jsx2(Text2, { color: palette.dim, children: q?.header }),
231
- /* @__PURE__ */ jsx2(Text2, { bold: true, children: q?.question }),
232
- /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", marginTop: 1, children: (q?.options ?? []).map((o, i) => /* @__PURE__ */ jsxs2(Box2, { children: [
233
- /* @__PURE__ */ jsxs2(Text2, { color: palette.amber, children: [
338
+ return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", borderStyle: "round", borderColor: palette.ember, paddingX: 1, children: [
339
+ /* @__PURE__ */ jsx5(Text5, { color: palette.dim, children: q?.header }),
340
+ /* @__PURE__ */ jsx5(Text5, { bold: true, children: q?.question }),
341
+ /* @__PURE__ */ jsx5(Box5, { flexDirection: "column", marginTop: 1, children: (q?.options ?? []).map((o, i) => /* @__PURE__ */ jsxs5(Box5, { children: [
342
+ /* @__PURE__ */ jsxs5(Text5, { color: palette.amber, children: [
234
343
  i + 1,
235
344
  " "
236
345
  ] }),
237
- /* @__PURE__ */ jsx2(Text2, { children: o.label }),
238
- o.description ? /* @__PURE__ */ jsxs2(Text2, { color: palette.dim, children: [
346
+ /* @__PURE__ */ jsx5(Text5, { children: o.label }),
347
+ o.description ? /* @__PURE__ */ jsxs5(Text5, { color: palette.dim, children: [
239
348
  " ",
240
349
  o.description
241
350
  ] }) : null
242
351
  ] }, o.label)) }),
243
- /* @__PURE__ */ jsx2(Box2, { marginTop: 1, children: /* @__PURE__ */ jsxs2(Text2, { color: palette.dim, children: [
352
+ /* @__PURE__ */ jsx5(Box5, { marginTop: 1, children: /* @__PURE__ */ jsxs5(Text5, { color: palette.dim, children: [
244
353
  "1-",
245
354
  (q?.options ?? []).length,
246
355
  " choose \xB7 \u21B5 confirm \xB7 or type your own answer \xB7 esc dismisses"
@@ -249,28 +358,28 @@ function ModalView({ modal }) {
249
358
  }
250
359
 
251
360
  // src/ink/Palette.tsx
252
- import { Box as Box3, Text as Text3 } from "ink";
253
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
361
+ import { Box as Box6, Text as Text6 } from "ink";
362
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
254
363
  function PaletteView({ items, selected }) {
255
- return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
256
- items.map((c, i) => /* @__PURE__ */ jsxs3(Box3, { children: [
257
- /* @__PURE__ */ jsxs3(Text3, { color: i === selected ? palette.ember : palette.dim, children: [
364
+ return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
365
+ items.map((c, i) => /* @__PURE__ */ jsxs6(Box6, { children: [
366
+ /* @__PURE__ */ jsxs6(Text6, { color: i === selected ? palette.ember : palette.dim, children: [
258
367
  i === selected ? "\u203A " : " ",
259
368
  "/",
260
369
  c.name,
261
370
  c.args ? ` ${c.args}` : ""
262
371
  ] }),
263
- /* @__PURE__ */ jsxs3(Text3, { color: palette.dim, children: [
372
+ /* @__PURE__ */ jsxs6(Text6, { color: palette.dim, children: [
264
373
  " ",
265
374
  c.summary
266
375
  ] })
267
376
  ] }, c.name)),
268
- /* @__PURE__ */ jsx3(Text3, { color: palette.dim, children: " \u2191\u2193 choose \xB7 tab completes \xB7 esc closes" })
377
+ /* @__PURE__ */ jsx6(Text6, { color: palette.dim, children: " \u2191\u2193 choose \xB7 tab completes \xB7 esc closes" })
269
378
  ] });
270
379
  }
271
380
 
272
381
  // src/ink/TodoBlock.tsx
273
- import { Box as Box4, Text as Text4 } from "ink";
382
+ import { Box as Box7, Text as Text7 } from "ink";
274
383
 
275
384
  // src/ink/todos.ts
276
385
  var DEFAULT_TODO_ROW_CAP = 4;
@@ -300,7 +409,7 @@ function toRows(items) {
300
409
  }
301
410
 
302
411
  // src/ink/TodoBlock.tsx
303
- import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
412
+ import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
304
413
  function marker(status) {
305
414
  switch (status) {
306
415
  case "in_progress":
@@ -316,21 +425,21 @@ function marker(status) {
316
425
  }
317
426
  function TodoRowView({ row }) {
318
427
  const { char, color } = marker(row.status);
319
- return /* @__PURE__ */ jsxs4(Box4, { children: [
320
- /* @__PURE__ */ jsxs4(Text4, { color, children: [
428
+ return /* @__PURE__ */ jsxs7(Box7, { children: [
429
+ /* @__PURE__ */ jsxs7(Text7, { color, children: [
321
430
  char,
322
431
  " "
323
432
  ] }),
324
- /* @__PURE__ */ jsx4(Text4, { color: row.status === "in_progress" ? palette.ink : palette.soft, children: row.title })
433
+ /* @__PURE__ */ jsx7(Text7, { color: row.status === "in_progress" ? palette.ink : palette.soft, children: row.title })
325
434
  ] });
326
435
  }
327
436
  function TodoBlock({ todos }) {
328
437
  const view = visibleTodos(todos);
329
438
  if (!view) return null;
330
- return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", marginTop: 1, children: [
331
- /* @__PURE__ */ jsx4(Text4, { color: palette.dim, children: view.header }),
332
- view.rows.map((row) => /* @__PURE__ */ jsx4(TodoRowView, { row }, row.id)),
333
- view.overflow ? /* @__PURE__ */ jsxs4(Text4, { color: palette.dim, children: [
439
+ return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", marginTop: 1, children: [
440
+ /* @__PURE__ */ jsx7(Text7, { color: palette.dim, children: view.header }),
441
+ view.rows.map((row) => /* @__PURE__ */ jsx7(TodoRowView, { row }, row.id)),
442
+ view.overflow ? /* @__PURE__ */ jsxs7(Text7, { color: palette.dim, children: [
334
443
  " ",
335
444
  view.overflow
336
445
  ] }) : null
@@ -380,7 +489,7 @@ function fitStatus(left, identity, columns) {
380
489
  }
381
490
 
382
491
  // src/ink/App.tsx
383
- import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
492
+ import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
384
493
  function useColumns() {
385
494
  const { stdout } = useStdout();
386
495
  const [columns, setColumns] = useState(stdout?.columns ?? DEFAULT_COLUMNS);
@@ -412,31 +521,31 @@ function App(props) {
412
521
  const costSeg = state.costUsd > 0 ? ` ${glyph.sep} ${usd(state.costUsd)}` : "";
413
522
  const escSeg = busy ? ` ${glyph.sep} esc stops` : "";
414
523
  const queuedSeg = state.queued.length ? ` ${glyph.sep} ${state.queued.length} queued` : "";
415
- const shellSeg = !busy ? ` ${glyph.sep} shell ${props.approveAll ? "auto-approved" : "asks first"}` : "";
524
+ const shellSeg = ` ${glyph.sep} shell ${props.approveAll ? "auto-approved" : "asks first"}`;
416
525
  const statusLeft = ` ${statusWord}${roundSeg}${tokenSeg}${costSeg}${escSeg}${queuedSeg}${shellSeg}`;
417
- const { right: statusRight } = fitStatus(statusLeft, { model: props.model, cwd: props.cwd, home: homedir() }, columns);
418
- return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
419
- /* @__PURE__ */ jsx5(Static, { items: state.committed, children: (item) => /* @__PURE__ */ jsx5(CommittedRow, { item }, item.id) }, `transcript-${state.transcriptGen}`),
420
- state.live?.tools.map((t) => /* @__PURE__ */ jsx5(LiveToolRow, { tool: t }, t.callId)),
421
- state.live?.streaming ? /* @__PURE__ */ jsx5(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text5, { color: palette.soft, children: state.live.streaming }) }) : null,
422
- /* @__PURE__ */ jsx5(TodoBlock, { todos: state.todos }),
423
- state.modal ? /* @__PURE__ */ jsx5(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx5(ModalView, { modal: state.modal }) }) : null,
424
- paletteItems.length && !state.modal ? /* @__PURE__ */ jsx5(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx5(PaletteView, { items: paletteItems, selected: paletteSelected }) }) : null,
425
- state.pendingSkill ? /* @__PURE__ */ jsxs5(Box5, { marginTop: 1, children: [
426
- /* @__PURE__ */ jsxs5(Text5, { color: palette.ember, children: [
526
+ const { right: statusRight } = fitStatus(statusLeft, { model: state.model, cwd: props.cwd, home: homedir() }, columns);
527
+ return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
528
+ /* @__PURE__ */ jsx8(Static, { items: state.committed, children: (item) => /* @__PURE__ */ jsx8(CommittedRow, { item }, item.id) }, `transcript-${state.transcriptGen}`),
529
+ state.live?.tools.map((t) => /* @__PURE__ */ jsx8(LiveToolRow, { tool: t }, t.callId)),
530
+ state.live?.streaming ? /* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx8(Text8, { color: palette.soft, children: state.live.streaming }) }) : null,
531
+ /* @__PURE__ */ jsx8(TodoBlock, { todos: state.todos }),
532
+ state.modal ? /* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: state.modal.kind === "mcp" ? /* @__PURE__ */ jsx8(McpOverlay, { modal: state.modal }) : state.modal.kind === "help" ? /* @__PURE__ */ jsx8(HelpOverlay, {}) : state.modal.kind === "context" ? /* @__PURE__ */ jsx8(ContextOverlay, { snapshot: contextSnapshotFromState(state, props.contextWindow ?? null, props.toolCount) }) : state.modal.kind === "permission" || state.modal.kind === "user" ? /* @__PURE__ */ jsx8(ModalView, { modal: state.modal }) : null }) : null,
533
+ paletteItems.length && !state.modal ? /* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx8(PaletteView, { items: paletteItems, selected: paletteSelected }) }) : null,
534
+ state.pendingSkill ? /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, children: [
535
+ /* @__PURE__ */ jsxs8(Text8, { color: palette.ember, children: [
427
536
  glyph.sep,
428
537
  " skill armed: ",
429
538
  state.pendingSkill,
430
539
  " "
431
540
  ] }),
432
- /* @__PURE__ */ jsx5(Text5, { color: palette.dim, children: "-- applies to your next message, then clears" })
541
+ /* @__PURE__ */ jsx8(Text8, { color: palette.dim, children: "-- applies to your next message, then clears" })
433
542
  ] }) : null,
434
- /* @__PURE__ */ jsxs5(Box5, { borderStyle: "round", borderColor: palette.dim, paddingX: 1, marginTop: 1, children: [
435
- /* @__PURE__ */ jsxs5(Text5, { color: palette.ember, children: [
543
+ /* @__PURE__ */ jsxs8(Box8, { borderStyle: "round", borderColor: palette.dim, paddingX: 1, marginTop: 1, children: [
544
+ /* @__PURE__ */ jsxs8(Text8, { color: palette.ember, children: [
436
545
  glyph.prompt,
437
546
  " "
438
547
  ] }),
439
- /* @__PURE__ */ jsx5(
548
+ /* @__PURE__ */ jsx8(
440
549
  TextInput,
441
550
  {
442
551
  value: props.value,
@@ -448,9 +557,9 @@ function App(props) {
448
557
  props.inputKey
449
558
  )
450
559
  ] }),
451
- state.committed.length === 0 ? /* @__PURE__ */ jsx5(Box5, { marginBottom: 1, children: /* @__PURE__ */ jsxs5(Text5, { color: palette.soft, children: [
560
+ state.committed.length === 0 ? /* @__PURE__ */ jsx8(Box8, { marginBottom: 1, children: /* @__PURE__ */ jsxs8(Text8, { color: palette.soft, children: [
452
561
  "Type a request. ",
453
- /* @__PURE__ */ jsxs5(Text5, { color: palette.dim, children: [
562
+ /* @__PURE__ */ jsxs8(Text8, { color: palette.dim, children: [
454
563
  "/help for commands ",
455
564
  glyph.sep,
456
565
  " Ctrl-C stops a turn ",
@@ -458,22 +567,22 @@ function App(props) {
458
567
  " Ctrl-D leaves"
459
568
  ] })
460
569
  ] }) }) : null,
461
- /* @__PURE__ */ jsxs5(Box5, { justifyContent: "space-between", children: [
462
- /* @__PURE__ */ jsxs5(Box5, { children: [
463
- /* @__PURE__ */ jsxs5(Text5, { color: busy ? palette.amber : palette.dim, children: [
570
+ /* @__PURE__ */ jsxs8(Box8, { justifyContent: "space-between", children: [
571
+ /* @__PURE__ */ jsxs8(Box8, { children: [
572
+ /* @__PURE__ */ jsxs8(Text8, { color: busy ? palette.amber : palette.dim, children: [
464
573
  " ",
465
574
  statusWord
466
575
  ] }),
467
- /* @__PURE__ */ jsxs5(Text5, { color: palette.dim, children: [
576
+ /* @__PURE__ */ jsxs8(Text8, { color: palette.dim, children: [
468
577
  roundSeg,
469
578
  tokenSeg,
470
579
  costSeg,
471
580
  escSeg,
472
581
  queuedSeg
473
582
  ] }),
474
- !busy ? /* @__PURE__ */ jsx5(Text5, { color: props.approveAll ? palette.ember : palette.warn, children: shellSeg }) : null
583
+ /* @__PURE__ */ jsx8(Text8, { color: props.approveAll ? palette.ember : palette.warn, children: shellSeg })
475
584
  ] }),
476
- statusRight ? /* @__PURE__ */ jsx5(Text5, { color: palette.dim, children: statusRight }) : null
585
+ statusRight ? /* @__PURE__ */ jsx8(Text8, { color: palette.dim, children: statusRight }) : null
477
586
  ] })
478
587
  ] });
479
588
  }
@@ -561,7 +670,7 @@ function writeBanner(out, opts) {
561
670
  }
562
671
 
563
672
  // src/ink/index.tsx
564
- import { jsx as jsx6 } from "react/jsx-runtime";
673
+ import { jsx as jsx9 } from "react/jsx-runtime";
565
674
  function Root({ session, deps }) {
566
675
  const state = useSyncExternalStore(
567
676
  useCallback((fn) => session.subscribe(fn), [session]),
@@ -592,6 +701,53 @@ function Root({ session, deps }) {
592
701
  };
593
702
  useInput((input, key) => {
594
703
  if (state.modal) {
704
+ if (state.modal.kind === "mcp") {
705
+ if (key.escape) {
706
+ session.closeMcpManager();
707
+ return;
708
+ }
709
+ if (key.upArrow) {
710
+ session.moveMcpSelection(-1);
711
+ return;
712
+ }
713
+ if (key.downArrow) {
714
+ session.moveMcpSelection(1);
715
+ return;
716
+ }
717
+ const lower = input.toLowerCase();
718
+ if (lower === "x") {
719
+ void session.removeMcpSelection();
720
+ return;
721
+ }
722
+ if (lower === "l") {
723
+ void session.loginMcpSelection();
724
+ return;
725
+ }
726
+ if (lower === "r") {
727
+ void session.refreshMcpSelection();
728
+ return;
729
+ }
730
+ if (lower === "a") {
731
+ session.closeMcpManager();
732
+ completeTo("/mcp add ");
733
+ return;
734
+ }
735
+ return;
736
+ }
737
+ if (state.modal.kind === "help") {
738
+ if (key.escape) {
739
+ session.closeHelp();
740
+ return;
741
+ }
742
+ return;
743
+ }
744
+ if (state.modal.kind === "context") {
745
+ if (key.escape) {
746
+ session.closeContext();
747
+ return;
748
+ }
749
+ return;
750
+ }
595
751
  if (key.escape) {
596
752
  session.answer(state.modal.kind === "permission" ? "deny" : null);
597
753
  return;
@@ -649,7 +805,7 @@ function Root({ session, deps }) {
649
805
  }
650
806
  if (sawSigint) setSawSigint(false);
651
807
  });
652
- return /* @__PURE__ */ jsx6(
808
+ return /* @__PURE__ */ jsx9(
653
809
  App,
654
810
  {
655
811
  state,
@@ -657,10 +813,26 @@ function Root({ session, deps }) {
657
813
  cwd: deps.cwd,
658
814
  version: deps.version,
659
815
  toolCount: deps.tools.length,
816
+ contextWindow: deps.contextWindow?.() ?? null,
660
817
  approveAll: deps.approveAll === true,
661
818
  value,
662
819
  onChange: handleChange,
663
820
  onSubmit: (v) => {
821
+ if (v.trim() === "/help") {
822
+ setValue("");
823
+ session.openHelp();
824
+ return;
825
+ }
826
+ if (v.trim() === "/mcp") {
827
+ setValue("");
828
+ void session.openMcpManager();
829
+ return;
830
+ }
831
+ if (v.trim() === "/context") {
832
+ setValue("");
833
+ session.openContext();
834
+ return;
835
+ }
664
836
  const items = v.startsWith("/") ? filterCommands(v) : [];
665
837
  if (items.length > 0 && !parseCommand(v)) {
666
838
  const chosen = items[Math.min(paletteIndex, items.length - 1)];
@@ -677,6 +849,10 @@ function Root({ session, deps }) {
677
849
  return;
678
850
  }
679
851
  }
852
+ if (session.willReject(v)) {
853
+ void session.submit(v);
854
+ return;
855
+ }
680
856
  setValue("");
681
857
  void session.submit(v).then(() => {
682
858
  if (session.wantsExit) exit();
@@ -687,9 +863,9 @@ function Root({ session, deps }) {
687
863
  const modal = session.getSnapshot().modal;
688
864
  if (!modal) return;
689
865
  if (modal.kind === "permission") session.answer(/^y(es)?$/i.test(v.trim()) ? "allow" : "deny");
690
- else session.answer(v.trim() ? { [modal.questions[0].header]: [v.trim()] } : null);
866
+ else if (modal.kind === "user") session.answer(v.trim() ? { [modal.questions[0].header]: [v.trim()] } : null);
691
867
  },
692
- focus: state.modal?.kind !== "permission",
868
+ focus: state.modal == null || state.modal.kind === "user",
693
869
  paletteSelected: paletteIndex,
694
870
  inputKey: fieldGen
695
871
  }
@@ -714,7 +890,7 @@ async function runTui(deps) {
714
890
  reserve: bannerHeight(process.stdout, bannerOpts)
715
891
  });
716
892
  writeBanner(process.stdout, bannerOpts);
717
- const app = render(/* @__PURE__ */ jsx6(Root, { session, deps }), {
893
+ const app = render(/* @__PURE__ */ jsx9(Root, { session, deps }), {
718
894
  /* LOAD-BEARING — do not "clean up" or default this away. Ink's own App
719
895
  * component (build/components/App.js, handleInput) intercepts the raw
720
896
  * \x03 byte and calls handleExit() BEFORE it ever emits the 'input'
@@ -1 +1 @@
1
- {"version":3,"file":"scroll.d.ts","sourceRoot":"","sources":["../../src/ink/scroll.ts"],"names":[],"mappings":"AAmCA,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,kBAAkB;IACjC;;;;;;;;;wBASoB;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;uCAEmC;IACnC,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;;;6CAI6C;AAC7C,wBAAgB,WAAW,CAAC,GAAG,EAAE,aAAa,EAAE,IAAI,EAAE,kBAAkB,GAAG,MAAM,CAehF"}
1
+ {"version":3,"file":"scroll.d.ts","sourceRoot":"","sources":["../../src/ink/scroll.ts"],"names":[],"mappings":"AAqCA,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,kBAAkB;IACjC;;;;;;;;;wBASoB;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;uCAEmC;IACnC,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;;;6CAI6C;AAC7C,wBAAgB,WAAW,CAAC,GAAG,EAAE,aAAa,EAAE,IAAI,EAAE,kBAAkB,GAAG,MAAM,CAehF"}
@@ -0,0 +1,20 @@
1
+ import type { McpServerView } from './state';
2
+ /** Status word, in priority order: a server can be simultaneously signed out
3
+ * AND unreachable, but `error` is the more actionable fact -- there is
4
+ * nothing to sign into until the connection itself works. `cached` is what is
5
+ * left once neither applies: a primed-from-disk tool list nothing has
6
+ * refreshed yet this run. */
7
+ /** Exported for the Ink manager overlay (McpOverlay.tsx), which presents the
8
+ * SAME status computed here rather than a second copy of it -- see this
9
+ * module's own comment. */
10
+ export declare function mcpStatusWord(v: McpServerView): string;
11
+ /** The age phrase ALREADY READS AS A COMPLETE CLAUSE -- the caller does not
12
+ * prefix it with "fetched". Returning "never fetched" while the caller also
13
+ * wrote "fetched " produced "fetched never fetched" on every server nobody had
14
+ * refreshed yet, which is every server on a cold machine.
15
+ *
16
+ * Exported so the Ink manager overlay (McpOverlay.tsx) can reuse this same
17
+ * logic instead of writing a second age formatter -- two would drift. */
18
+ export declare function ageWord(fetchedAt: number | null, now: number): string;
19
+ export declare function formatMcpServers(views: McpServerView[], now: number): string;
20
+ //# sourceMappingURL=mcpText.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcpText.d.ts","sourceRoot":"","sources":["../src/mcpText.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE7C;;;;6BAI6B;AAC7B;;2BAE2B;AAC3B,wBAAgB,aAAa,CAAC,CAAC,EAAE,aAAa,GAAG,MAAM,CAKtD;AAED;;;;;;0EAM0E;AAC1E,wBAAgB,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAUrE;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,aAAa,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAgB5E"}