cvc-tui 0.4.4 → 0.4.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/NOTICES.md +13 -0
  2. package/dist/app/completion.js +102 -0
  3. package/dist/app/createGatewayEventHandler.js +508 -0
  4. package/dist/app/createSlashHandler.js +101 -0
  5. package/dist/app/delegationStore.js +51 -0
  6. package/dist/app/gatewayContext.js +17 -0
  7. package/dist/app/historyStore.js +123 -0
  8. package/dist/app/inputBuffer.js +120 -0
  9. package/dist/app/inputSelectionStore.js +8 -0
  10. package/dist/app/inputStore.js +28 -0
  11. package/dist/app/interfaces.js +6 -0
  12. package/dist/app/overlayStore.js +40 -0
  13. package/dist/app/promptStore.js +44 -0
  14. package/dist/app/queueStore.js +25 -0
  15. package/dist/app/scroll.js +44 -0
  16. package/dist/app/setupHandoff.js +28 -0
  17. package/dist/app/slash/commands/core.js +479 -0
  18. package/dist/app/slash/commands/debug.js +44 -0
  19. package/dist/app/slash/commands/ops.js +498 -0
  20. package/dist/app/slash/commands/session.js +431 -0
  21. package/dist/app/slash/commands/setup.js +20 -0
  22. package/dist/app/slash/commands/toggles.js +40 -0
  23. package/dist/app/slash/registry.js +18 -0
  24. package/dist/app/slash/types.js +1 -0
  25. package/dist/app/spawnHistoryStore.js +105 -0
  26. package/dist/app/turnController.js +650 -0
  27. package/dist/app/turnStore.js +48 -0
  28. package/dist/app/uiStore.js +36 -0
  29. package/dist/app/useComposerState.js +265 -0
  30. package/dist/app/useConfigSync.js +144 -0
  31. package/dist/app/useInputHandlers.js +403 -0
  32. package/dist/app/useLongRunToolCharms.js +50 -0
  33. package/dist/app/useMainApp.js +629 -0
  34. package/dist/app/useSessionLifecycle.js +175 -0
  35. package/dist/app/useSubmission.js +287 -0
  36. package/dist/app.js +15 -0
  37. package/dist/banner.js +63 -0
  38. package/dist/components/agentsOverlay.js +474 -0
  39. package/dist/components/appChrome.js +252 -0
  40. package/dist/components/appLayout.js +121 -0
  41. package/dist/components/appOverlays.js +65 -0
  42. package/dist/components/branding.js +97 -0
  43. package/dist/components/fpsOverlay.js +22 -0
  44. package/dist/components/helpHint.js +21 -0
  45. package/dist/components/markdown.js +501 -0
  46. package/dist/components/maskedPrompt.js +12 -0
  47. package/dist/components/messageLine.js +82 -0
  48. package/dist/components/modelPicker.js +254 -0
  49. package/dist/components/overlayControls.js +30 -0
  50. package/dist/components/overlays/confirmPrompt.js +25 -0
  51. package/dist/components/overlays/helpOverlay.js +76 -0
  52. package/dist/components/overlays/historySearch.js +49 -0
  53. package/dist/components/overlays/modelPicker.js +60 -0
  54. package/dist/components/overlays/overlayUtils.js +19 -0
  55. package/dist/components/overlays/secretPrompt.js +36 -0
  56. package/dist/components/overlays/sessionPicker.js +93 -0
  57. package/dist/components/overlays/skillsHub.js +71 -0
  58. package/dist/components/prompts.js +95 -0
  59. package/dist/components/queuedMessages.js +24 -0
  60. package/dist/components/sessionPicker.js +130 -0
  61. package/dist/components/skillsHub.js +165 -0
  62. package/dist/components/streamingAssistant.js +35 -0
  63. package/dist/components/streamingMarkdown.js +144 -0
  64. package/dist/components/textInput.js +794 -0
  65. package/dist/components/themed.js +12 -0
  66. package/dist/components/thinking.js +496 -0
  67. package/dist/components/todoPanel.js +40 -0
  68. package/dist/components/transcript.js +22 -0
  69. package/dist/config/env.js +18 -0
  70. package/dist/config/limits.js +22 -0
  71. package/dist/config/timing.js +25 -0
  72. package/dist/content/charms.js +5 -0
  73. package/dist/content/faces.js +21 -0
  74. package/dist/content/fortunes.js +29 -0
  75. package/dist/content/hotkeys.js +38 -0
  76. package/dist/content/placeholders.js +15 -0
  77. package/dist/content/setup.js +14 -0
  78. package/dist/content/verbs.js +41 -0
  79. package/dist/domain/details.js +53 -0
  80. package/dist/domain/messages.js +63 -0
  81. package/dist/domain/paths.js +16 -0
  82. package/dist/domain/providers.js +11 -0
  83. package/dist/domain/roles.js +6 -0
  84. package/dist/domain/slash.js +11 -0
  85. package/dist/domain/usage.js +1 -0
  86. package/dist/domain/viewport.js +33 -0
  87. package/dist/entry.js +64 -70236
  88. package/dist/gateway/client.js +312 -0
  89. package/dist/gatewayClient.js +574 -0
  90. package/dist/gatewayTypes.js +1 -0
  91. package/dist/hooks/useCompletion.js +86 -0
  92. package/dist/hooks/useGitBranch.js +58 -0
  93. package/dist/hooks/useInputHistory.js +12 -0
  94. package/dist/hooks/useQueue.js +57 -0
  95. package/dist/hooks/useVirtualHistory.js +401 -0
  96. package/dist/lib/circularBuffer.js +43 -0
  97. package/dist/lib/clipboard.js +126 -0
  98. package/dist/lib/editor.js +41 -0
  99. package/dist/lib/editor.test.js +58 -0
  100. package/dist/lib/emoji.js +49 -0
  101. package/dist/lib/externalCli.js +11 -0
  102. package/dist/lib/forceTruecolor.js +26 -0
  103. package/dist/lib/fpsStore.js +36 -0
  104. package/dist/lib/gracefulExit.js +29 -0
  105. package/dist/lib/history.js +69 -0
  106. package/dist/lib/inputMetrics.js +143 -0
  107. package/dist/lib/liveProgress.js +51 -0
  108. package/dist/lib/liveProgress.test.js +89 -0
  109. package/dist/lib/mathUnicode.js +685 -0
  110. package/dist/lib/memory.js +123 -0
  111. package/dist/lib/memoryMonitor.js +76 -0
  112. package/dist/lib/messages.js +3 -0
  113. package/dist/lib/messages.test.js +25 -0
  114. package/dist/lib/osc52.js +53 -0
  115. package/dist/lib/perfPane.js +94 -0
  116. package/dist/lib/platform.js +312 -0
  117. package/dist/lib/precisionWheel.js +25 -0
  118. package/dist/lib/react-devtools-stub.js +12 -0
  119. package/dist/lib/reasoning.js +39 -0
  120. package/dist/lib/rpc.js +26 -0
  121. package/dist/lib/subagentTree.js +287 -0
  122. package/dist/lib/syntax.js +89 -0
  123. package/dist/lib/terminalModes.js +46 -0
  124. package/dist/lib/terminalParity.js +48 -0
  125. package/dist/lib/terminalSetup.js +321 -0
  126. package/dist/lib/text.js +203 -0
  127. package/dist/lib/text.test.js +18 -0
  128. package/dist/lib/todo.js +2 -0
  129. package/dist/lib/todo.test.js +22 -0
  130. package/dist/lib/viewportStore.js +82 -0
  131. package/dist/lib/virtualHeights.js +61 -0
  132. package/dist/lib/wheelAccel.js +143 -0
  133. package/dist/protocol/interpolation.js +4 -0
  134. package/dist/protocol/paste.js +3 -0
  135. package/dist/theme.js +398 -0
  136. package/dist/types.js +1 -0
  137. package/dist/vendor/cvc-ink/dist/entry-exports.js +52737 -0
  138. package/dist/vendor/cvc-ink/index.js +1 -0
  139. package/dist/vendor/cvc-ink/package.json +9 -0
  140. package/dist/vendor/cvc-ink/text-input.js +1 -0
  141. package/package.json +9 -9
@@ -0,0 +1,71 @@
1
+ import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
+ // @ts-nocheck
3
+ // CVC skillsHub overlay — list skills (via gateway or supplied prop), search/filter,
4
+ // arrow-key navigable, Enter triggers an onActivate callback (e.g. to inject the
5
+ // skill into the next user message).
6
+ import { useEffect, useMemo, useState } from 'react';
7
+ import { Box, Text, useInput } from 'ink';
8
+ import { CVC_THEME } from '../../types.js';
9
+ import { filterRows, windowItems } from './overlayUtils.js';
10
+ const VISIBLE = 12;
11
+ export const SkillsHub = ({ skills, loadSkills, onActivate, onCancel, }) => {
12
+ const [all, setAll] = useState(skills ?? []);
13
+ const [loading, setLoading] = useState(!skills && Boolean(loadSkills));
14
+ const [query, setQuery] = useState('');
15
+ const [idx, setIdx] = useState(0);
16
+ useEffect(() => {
17
+ if (skills || !loadSkills)
18
+ return;
19
+ let cancelled = false;
20
+ loadSkills()
21
+ .then(list => {
22
+ if (!cancelled) {
23
+ setAll(list);
24
+ setLoading(false);
25
+ }
26
+ })
27
+ .catch(() => {
28
+ if (!cancelled)
29
+ setLoading(false);
30
+ });
31
+ return () => {
32
+ cancelled = true;
33
+ };
34
+ }, [skills, loadSkills]);
35
+ const filtered = useMemo(() => filterRows(all, query, s => `${s.name} ${s.description ?? ''} ${s.category ?? ''}`), [all, query]);
36
+ const safeIdx = Math.min(idx, Math.max(0, filtered.length - 1));
37
+ useInput((input, key) => {
38
+ if (key.escape)
39
+ return onCancel();
40
+ if (key.upArrow && safeIdx > 0)
41
+ return setIdx(safeIdx - 1);
42
+ if (key.downArrow && safeIdx < filtered.length - 1)
43
+ return setIdx(safeIdx + 1);
44
+ if (key.return) {
45
+ const s = filtered[safeIdx];
46
+ if (s)
47
+ onActivate(s);
48
+ return;
49
+ }
50
+ if (key.backspace || key.delete) {
51
+ setQuery(q => q.slice(0, -1));
52
+ setIdx(0);
53
+ return;
54
+ }
55
+ if (input && !key.ctrl && !key.meta && !key.tab) {
56
+ setQuery(q => q + input);
57
+ setIdx(0);
58
+ }
59
+ });
60
+ const rows = filtered.map(s => {
61
+ const cat = s.category ? `[${s.category}] ` : '';
62
+ const desc = s.description ? ` — ${s.description}` : '';
63
+ return `${cat}${s.name}${desc}`;
64
+ });
65
+ const { items, offset } = windowItems(rows, safeIdx, VISIBLE);
66
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: CVC_THEME.primary, bold: true, children: ["Skills Hub (", filtered.length, "/", all.length, ")"] }), _jsxs(Text, { dimColor: true, children: ["filter: ", query || '(none)'] }), loading ? (_jsx(Text, { dimColor: true, children: "loading skills\u2026" })) : items.length === 0 ? (_jsx(Text, { dimColor: true, children: "no skills match" })) : (_jsxs(_Fragment, { children: [offset > 0 ? _jsx(Text, { dimColor: true, children: ` ↑ ${offset} more` }) : _jsx(Text, { children: " " }), items.map((row, i) => {
67
+ const k = offset + i;
68
+ const active = safeIdx === k;
69
+ return (_jsxs(Text, { color: active ? CVC_THEME.primary : undefined, inverse: active, bold: active, children: [active ? '▸ ' : ' ', row] }, filtered[k]?.name ?? `sk-${k}`));
70
+ }), offset + VISIBLE < rows.length ? (_jsx(Text, { dimColor: true, children: ` ↓ ${rows.length - offset - VISIBLE} more` })) : (_jsx(Text, { children: " " }))] })), _jsx(Text, { dimColor: true, children: "type to filter \u00B7 \u2191/\u2193 select \u00B7 Enter activate \u00B7 Esc cancel" })] }));
71
+ };
@@ -0,0 +1,95 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ // @ts-nocheck
3
+ // SPDX-License-Identifier: MIT
4
+ // Ported from CVC Agent (https://github.com/NousResearch/cvc)
5
+ // Original Copyright (c) 2025 Nous Research. CVC adaptations (c) 2026 Jai Kumar Meena.
6
+ import { Box, Text, useInput } from '../vendor/cvc-ink/index.js';
7
+ import { useState } from 'react';
8
+ import { isMac } from '../lib/platform.js';
9
+ import { TextInput } from './textInput.js';
10
+ const OPTS = ['once', 'session', 'always', 'deny'];
11
+ const LABELS = { always: 'Always allow', deny: 'Deny', once: 'Allow once', session: 'Allow this session' };
12
+ const CMD_PREVIEW_LINES = 10;
13
+ export function ApprovalPrompt({ onChoice, req, t }) {
14
+ const [sel, setSel] = useState(0);
15
+ useInput((ch, key) => {
16
+ if (key.upArrow && sel > 0) {
17
+ setSel(s => s - 1);
18
+ }
19
+ if (key.downArrow && sel < OPTS.length - 1) {
20
+ setSel(s => s + 1);
21
+ }
22
+ const n = parseInt(ch, 10);
23
+ if (n >= 1 && n <= OPTS.length) {
24
+ onChoice(OPTS[n - 1]);
25
+ return;
26
+ }
27
+ if (key.return) {
28
+ onChoice(OPTS[sel]);
29
+ }
30
+ });
31
+ const rawLines = req.command.split('\n');
32
+ const shown = rawLines.slice(0, CMD_PREVIEW_LINES);
33
+ const overflow = rawLines.length - shown.length;
34
+ return (_jsxs(Box, { borderColor: t.color.warn, borderStyle: "double", flexDirection: "column", paddingX: 1, children: [_jsxs(Text, { bold: true, color: t.color.warn, children: ["\u26A0 approval required \u00B7 ", req.description] }), _jsxs(Box, { flexDirection: "column", paddingLeft: 1, children: [shown.map((line, i) => (_jsx(Text, { color: t.color.text, wrap: "truncate-end", children: line || ' ' }, i))), overflow > 0 ? (_jsxs(Text, { color: t.color.muted, children: ["\u2026 +", overflow, " more line", overflow === 1 ? '' : 's', " (full text above)"] })) : null] }), _jsx(Text, {}), OPTS.map((o, i) => (_jsx(Text, { children: _jsxs(Text, { bold: sel === i, color: sel === i ? t.color.warn : t.color.muted, inverse: sel === i, children: [sel === i ? '▸ ' : ' ', i + 1, ". ", LABELS[o]] }) }, o))), _jsx(Text, { color: t.color.muted, children: "\u2191/\u2193 select \u00B7 Enter confirm \u00B7 1-4 quick pick \u00B7 Ctrl+C deny" })] }));
35
+ }
36
+ export function ClarifyPrompt({ cols = 80, onAnswer, onCancel, req, t }) {
37
+ const [sel, setSel] = useState(0);
38
+ const [custom, setCustom] = useState('');
39
+ const [typing, setTyping] = useState(false);
40
+ const choices = req.choices ?? [];
41
+ const heading = (_jsxs(Text, { bold: true, children: [_jsx(Text, { color: t.color.accent, children: "ask" }), _jsxs(Text, { color: t.color.text, children: [" ", req.question] })] }));
42
+ useInput((ch, key) => {
43
+ if (key.escape) {
44
+ typing && choices.length ? setTyping(false) : onCancel();
45
+ return;
46
+ }
47
+ if (typing || !choices.length) {
48
+ return;
49
+ }
50
+ if (key.upArrow && sel > 0) {
51
+ setSel(s => s - 1);
52
+ }
53
+ if (key.downArrow && sel < choices.length) {
54
+ setSel(s => s + 1);
55
+ }
56
+ if (key.return) {
57
+ sel === choices.length ? setTyping(true) : choices[sel] && onAnswer(choices[sel]);
58
+ }
59
+ const n = parseInt(ch);
60
+ if (n >= 1 && n <= choices.length) {
61
+ onAnswer(choices[n - 1]);
62
+ }
63
+ });
64
+ if (typing || !choices.length) {
65
+ return (_jsxs(Box, { flexDirection: "column", children: [heading, _jsxs(Box, { children: [_jsx(Text, { color: t.color.label, children: '> ' }), _jsx(TextInput, { columns: Math.max(20, cols - 6), onChange: setCustom, onSubmit: onAnswer, value: custom })] }), _jsxs(Text, { color: t.color.muted, children: ["Enter send \u00B7 Esc ", choices.length ? 'back' : 'cancel', " \u00B7", ' ', isMac ? 'Cmd+C copy · Cmd+V paste · Ctrl+C cancel' : 'Ctrl+C cancel'] })] }));
66
+ }
67
+ return (_jsxs(Box, { flexDirection: "column", children: [heading, [...choices, 'Other (type your answer)'].map((c, i) => (_jsx(Text, { children: _jsxs(Text, { bold: sel === i, color: sel === i ? t.color.label : t.color.muted, inverse: sel === i, children: [sel === i ? '▸ ' : ' ', i + 1, ". ", c] }) }, i))), _jsxs(Text, { color: t.color.muted, children: ["\u2191/\u2193 select \u00B7 Enter confirm \u00B7 1-", choices.length, " quick pick \u00B7 Esc/Ctrl+C cancel"] })] }));
68
+ }
69
+ export function ConfirmPrompt({ onCancel, onConfirm, req, t }) {
70
+ const [sel, setSel] = useState(0);
71
+ useInput((ch, key) => {
72
+ const lower = ch.toLowerCase();
73
+ if (key.escape || (key.ctrl && lower === 'c') || lower === 'n') {
74
+ return onCancel();
75
+ }
76
+ if (lower === 'y') {
77
+ return onConfirm();
78
+ }
79
+ if (key.upArrow) {
80
+ setSel(0);
81
+ }
82
+ if (key.downArrow) {
83
+ setSel(1);
84
+ }
85
+ if (key.return) {
86
+ sel === 0 ? onCancel() : onConfirm();
87
+ }
88
+ });
89
+ const accent = req.danger ? t.color.error : t.color.warn;
90
+ const rows = [
91
+ { color: t.color.text, label: req.cancelLabel ?? 'No' },
92
+ { color: req.danger ? t.color.error : t.color.text, label: req.confirmLabel ?? 'Yes' }
93
+ ];
94
+ return (_jsxs(Box, { borderColor: accent, borderStyle: "double", flexDirection: "column", paddingX: 1, children: [_jsxs(Text, { bold: true, color: accent, children: [req.danger ? '⚠' : '?', " ", req.title] }), req.detail ? (_jsx(Box, { paddingLeft: 1, children: _jsx(Text, { color: t.color.text, wrap: "truncate-end", children: req.detail }) })) : null, _jsx(Text, {}), rows.map((row, i) => (_jsxs(Text, { children: [_jsx(Text, { color: sel === i ? accent : t.color.muted, children: sel === i ? '▸ ' : ' ' }), _jsx(Text, { color: sel === i ? row.color : t.color.muted, children: row.label })] }, row.label))), _jsx(Text, { color: t.color.muted, children: "\u2191/\u2193 select \u00B7 Enter confirm \u00B7 Y/N quick \u00B7 Esc cancel" })] }));
95
+ }
@@ -0,0 +1,24 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // @ts-nocheck
3
+ // SPDX-License-Identifier: MIT
4
+ // Ported from CVC Agent (https://github.com/NousResearch/cvc)
5
+ // Original Copyright (c) 2025 Nous Research. CVC adaptations (c) 2026 Jai Kumar Meena.
6
+ import { Box, Text } from '../vendor/cvc-ink/index.js';
7
+ import { compactPreview } from '../lib/text.js';
8
+ export const QUEUE_WINDOW = 3;
9
+ export function getQueueWindow(queueLen, queueEditIdx) {
10
+ const start = queueEditIdx === null ? 0 : Math.max(0, Math.min(queueEditIdx - 1, Math.max(0, queueLen - QUEUE_WINDOW)));
11
+ const end = Math.min(queueLen, start + QUEUE_WINDOW);
12
+ return { end, showLead: start > 0, showTail: end < queueLen, start };
13
+ }
14
+ export function QueuedMessages({ cols, queueEditIdx, queued, t }) {
15
+ if (!queued.length) {
16
+ return null;
17
+ }
18
+ const q = getQueueWindow(queued.length, queueEditIdx);
19
+ return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { color: t.color.muted, dimColor: true, children: `queued (${queued.length})${queueEditIdx !== null ? ` · editing ${queueEditIdx + 1} · Ctrl+X delete · Esc cancel` : ''}` }), q.showLead && (_jsxs(Text, { color: t.color.muted, dimColor: true, children: [' ', "\u2026"] })), queued.slice(q.start, q.end).map((item, i) => {
20
+ const idx = q.start + i;
21
+ const active = queueEditIdx === idx;
22
+ return (_jsxs(Text, { color: active ? t.color.accent : t.color.muted, dimColor: true, children: [active ? '▸' : ' ', " ", idx + 1, ". ", compactPreview(item, Math.max(16, cols - 10))] }, `${idx}-${item.slice(0, 16)}`));
23
+ }), q.showTail && (_jsxs(Text, { color: t.color.muted, dimColor: true, children: [' ', "\u2026and ", queued.length - q.end, " more"] }))] }));
24
+ }
@@ -0,0 +1,130 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // @ts-nocheck
3
+ // SPDX-License-Identifier: MIT
4
+ // Ported from CVC Agent (https://github.com/NousResearch/cvc)
5
+ // Original Copyright (c) 2025 Nous Research. CVC adaptations (c) 2026 Jai Kumar Meena.
6
+ import { Box, Text, useInput, useStdout } from '../vendor/cvc-ink/index.js';
7
+ import { useEffect, useState } from 'react';
8
+ import { asRpcResult, rpcErrorMessage } from '../lib/rpc.js';
9
+ import { OverlayHint, useOverlayKeys, windowOffset } from './overlayControls.js';
10
+ const VISIBLE = 15;
11
+ const MIN_WIDTH = 60;
12
+ const MAX_WIDTH = 120;
13
+ const age = (ts) => {
14
+ const d = (Date.now() / 1000 - ts) / 86400;
15
+ if (d < 1) {
16
+ return 'today';
17
+ }
18
+ if (d < 2) {
19
+ return 'yesterday';
20
+ }
21
+ return `${Math.floor(d)}d ago`;
22
+ };
23
+ export function SessionPicker({ gw, onCancel, onSelect, t }) {
24
+ const [items, setItems] = useState([]);
25
+ const [err, setErr] = useState('');
26
+ const [sel, setSel] = useState(0);
27
+ const [loading, setLoading] = useState(true);
28
+ // When non-null, the user pressed `d` on this index and we're waiting for
29
+ // a second `d`/`D` to confirm deletion. Any other key cancels the prompt.
30
+ const [confirmDelete, setConfirmDelete] = useState(null);
31
+ const [deleting, setDeleting] = useState(false);
32
+ const { stdout } = useStdout();
33
+ const width = Math.max(MIN_WIDTH, Math.min(MAX_WIDTH, (stdout?.columns ?? 80) - 6));
34
+ useOverlayKeys({ onClose: onCancel });
35
+ useEffect(() => {
36
+ gw.request('session.list', { limit: 200 })
37
+ .then(raw => {
38
+ const r = asRpcResult(raw);
39
+ if (!r) {
40
+ setErr('invalid response: session.list');
41
+ setLoading(false);
42
+ return;
43
+ }
44
+ setItems(r.sessions ?? []);
45
+ setErr('');
46
+ setLoading(false);
47
+ })
48
+ .catch((e) => {
49
+ setErr(rpcErrorMessage(e));
50
+ setLoading(false);
51
+ });
52
+ }, [gw]);
53
+ const performDelete = (index) => {
54
+ const target = items[index];
55
+ if (!target || deleting) {
56
+ return;
57
+ }
58
+ setDeleting(true);
59
+ gw.request('session.delete', { session_id: target.id })
60
+ .then(raw => {
61
+ const r = asRpcResult(raw);
62
+ if (!r || r.deleted !== target.id) {
63
+ setErr('invalid response: session.delete');
64
+ setDeleting(false);
65
+ return;
66
+ }
67
+ setItems(prev => {
68
+ const next = prev.filter((_, i) => i !== index);
69
+ setSel(s => Math.max(0, Math.min(s, next.length - 1)));
70
+ return next;
71
+ });
72
+ setErr('');
73
+ setDeleting(false);
74
+ })
75
+ .catch((e) => {
76
+ setErr(rpcErrorMessage(e));
77
+ setDeleting(false);
78
+ });
79
+ };
80
+ useInput((ch, key) => {
81
+ if (deleting) {
82
+ return;
83
+ }
84
+ if (confirmDelete !== null) {
85
+ if (ch?.toLowerCase() === 'd') {
86
+ const idx = confirmDelete;
87
+ setConfirmDelete(null);
88
+ performDelete(idx);
89
+ }
90
+ else {
91
+ setConfirmDelete(null);
92
+ }
93
+ return;
94
+ }
95
+ if (key.upArrow && sel > 0) {
96
+ setSel(s => s - 1);
97
+ }
98
+ if (key.downArrow && sel < items.length - 1) {
99
+ setSel(s => s + 1);
100
+ }
101
+ if (key.return && items[sel]) {
102
+ onSelect(items[sel].id);
103
+ return;
104
+ }
105
+ if (ch?.toLowerCase() === 'd' && items[sel]) {
106
+ setConfirmDelete(sel);
107
+ return;
108
+ }
109
+ const n = parseInt(ch);
110
+ if (n >= 1 && n <= Math.min(9, items.length)) {
111
+ onSelect(items[n - 1].id);
112
+ }
113
+ });
114
+ if (loading) {
115
+ return _jsx(Text, { color: t.color.muted, children: "loading sessions\u2026" });
116
+ }
117
+ if (err && !items.length) {
118
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: t.color.label, children: ["error: ", err] }), _jsx(OverlayHint, { t: t, children: "Esc/q cancel" })] }));
119
+ }
120
+ if (!items.length) {
121
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: t.color.muted, children: "no previous sessions" }), _jsx(OverlayHint, { t: t, children: "Esc/q cancel" })] }));
122
+ }
123
+ const offset = windowOffset(items.length, sel, VISIBLE);
124
+ return (_jsxs(Box, { flexDirection: "column", width: width, children: [_jsx(Text, { bold: true, color: t.color.accent, children: "Resume Session" }), offset > 0 && _jsxs(Text, { color: t.color.muted, children: [" \u2191 ", offset, " more"] }), items.slice(offset, offset + VISIBLE).map((s, vi) => {
125
+ const i = offset + vi;
126
+ const selected = sel === i;
127
+ const pendingDelete = confirmDelete === i;
128
+ return (_jsxs(Box, { children: [_jsx(Text, { bold: selected, color: selected ? t.color.accent : t.color.muted, inverse: selected, children: selected ? '▸ ' : ' ' }), _jsx(Box, { width: 30, children: _jsxs(Text, { bold: selected, color: selected ? t.color.accent : t.color.muted, inverse: selected, children: [String(i + 1).padStart(2), ". [", s.id, "]"] }) }), _jsx(Box, { width: 30, children: _jsxs(Text, { bold: selected, color: selected ? t.color.accent : t.color.muted, inverse: selected, children: ["(", s.message_count, " msgs, ", age(s.started_at), ", ", s.source || 'tui', ")"] }) }), _jsx(Text, { bold: selected, color: pendingDelete ? t.color.label : selected ? t.color.accent : t.color.muted, inverse: selected, wrap: "truncate-end", children: pendingDelete ? 'press d again to delete' : s.title || s.preview || '(untitled)' })] }, s.id));
129
+ }), offset + VISIBLE < items.length && _jsxs(Text, { color: t.color.muted, children: [" \u2193 ", items.length - offset - VISIBLE, " more"] }), err && _jsxs(Text, { color: t.color.label, children: ["error: ", err] }), deleting ? (_jsx(OverlayHint, { t: t, children: "deleting\u2026" })) : (_jsx(OverlayHint, { t: t, children: "\u2191/\u2193 select \u00B7 Enter resume \u00B7 1-9 quick \u00B7 d delete \u00B7 Esc/q cancel" }))] }));
130
+ }
@@ -0,0 +1,165 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // @ts-nocheck
3
+ // SPDX-License-Identifier: MIT
4
+ // Ported from CVC Agent (https://github.com/NousResearch/cvc)
5
+ // Original Copyright (c) 2025 Nous Research. CVC adaptations (c) 2026 Jai Kumar Meena.
6
+ import { Box, Text, useInput, useStdout } from '../vendor/cvc-ink/index.js';
7
+ import { useEffect, useState } from 'react';
8
+ import { rpcErrorMessage } from '../lib/rpc.js';
9
+ import { OverlayHint, useOverlayKeys, windowItems, windowOffset } from './overlayControls.js';
10
+ const VISIBLE = 12;
11
+ const MIN_WIDTH = 40;
12
+ const MAX_WIDTH = 90;
13
+ export function SkillsHub({ gw, onClose, t }) {
14
+ const [skillsByCat, setSkillsByCat] = useState({});
15
+ const [selectedCat, setSelectedCat] = useState('');
16
+ const [catIdx, setCatIdx] = useState(0);
17
+ const [skillIdx, setSkillIdx] = useState(0);
18
+ const [stage, setStage] = useState('category');
19
+ const [info, setInfo] = useState(null);
20
+ const [installing, setInstalling] = useState(false);
21
+ const [err, setErr] = useState('');
22
+ const [loading, setLoading] = useState(true);
23
+ const { stdout } = useStdout();
24
+ const width = Math.max(MIN_WIDTH, Math.min(MAX_WIDTH, (stdout?.columns ?? 80) - 6));
25
+ useEffect(() => {
26
+ gw.request('skills.manage', { action: 'list' })
27
+ .then(r => {
28
+ setSkillsByCat(r?.skills ?? {});
29
+ setErr('');
30
+ setLoading(false);
31
+ })
32
+ .catch((e) => {
33
+ setErr(rpcErrorMessage(e));
34
+ setLoading(false);
35
+ });
36
+ }, [gw]);
37
+ const cats = Object.keys(skillsByCat).sort();
38
+ const skills = selectedCat ? (skillsByCat[selectedCat] ?? []) : [];
39
+ const skillName = skills[skillIdx] ?? '';
40
+ const back = () => {
41
+ if (stage === 'actions') {
42
+ setStage('skill');
43
+ setInfo(null);
44
+ setErr('');
45
+ return;
46
+ }
47
+ if (stage === 'skill') {
48
+ setStage('category');
49
+ setSkillIdx(0);
50
+ return;
51
+ }
52
+ onClose();
53
+ };
54
+ useOverlayKeys({ disabled: installing, onBack: back, onClose });
55
+ const inspect = (name) => {
56
+ setInfo(null);
57
+ setErr('');
58
+ gw.request('skills.manage', { action: 'inspect', query: name })
59
+ .then(r => setInfo(r?.info ?? { name }))
60
+ .catch((e) => setErr(rpcErrorMessage(e)));
61
+ };
62
+ const install = (name) => {
63
+ setInstalling(true);
64
+ setErr('');
65
+ gw.request('skills.manage', { action: 'install', query: name })
66
+ .then(() => onClose())
67
+ .catch((e) => setErr(rpcErrorMessage(e)))
68
+ .finally(() => setInstalling(false));
69
+ };
70
+ useInput((ch, key) => {
71
+ if (installing) {
72
+ return;
73
+ }
74
+ if (stage === 'actions') {
75
+ if (key.return) {
76
+ setStage('skill');
77
+ setInfo(null);
78
+ setErr('');
79
+ return;
80
+ }
81
+ if (ch.toLowerCase() === 'x' && skillName) {
82
+ install(skillName);
83
+ return;
84
+ }
85
+ if (ch.toLowerCase() === 'i' && skillName) {
86
+ inspect(skillName);
87
+ }
88
+ return;
89
+ }
90
+ const count = stage === 'category' ? cats.length : skills.length;
91
+ const sel = stage === 'category' ? catIdx : skillIdx;
92
+ const setSel = stage === 'category' ? setCatIdx : setSkillIdx;
93
+ if (key.upArrow && sel > 0) {
94
+ setSel(v => v - 1);
95
+ return;
96
+ }
97
+ if (key.downArrow && sel < count - 1) {
98
+ setSel(v => v + 1);
99
+ return;
100
+ }
101
+ if (key.return) {
102
+ if (stage === 'category') {
103
+ const cat = cats[catIdx];
104
+ if (!cat) {
105
+ return;
106
+ }
107
+ setSelectedCat(cat);
108
+ setSkillIdx(0);
109
+ setStage('skill');
110
+ return;
111
+ }
112
+ const name = skills[skillIdx];
113
+ if (name) {
114
+ setStage('actions');
115
+ inspect(name);
116
+ }
117
+ return;
118
+ }
119
+ const n = ch === '0' ? 10 : parseInt(ch, 10);
120
+ if (!Number.isNaN(n) && n >= 1 && n <= Math.min(10, count)) {
121
+ const next = windowOffset(count, sel, VISIBLE) + n - 1;
122
+ if (stage === 'category') {
123
+ const cat = cats[next];
124
+ if (cat) {
125
+ setSelectedCat(cat);
126
+ setCatIdx(next);
127
+ setSkillIdx(0);
128
+ setStage('skill');
129
+ }
130
+ return;
131
+ }
132
+ const name = skills[next];
133
+ if (name) {
134
+ setSkillIdx(next);
135
+ setStage('actions');
136
+ inspect(name);
137
+ }
138
+ }
139
+ });
140
+ if (loading) {
141
+ return _jsx(Text, { color: t.color.muted, children: "loading skills\u2026" });
142
+ }
143
+ if (err && stage === 'category') {
144
+ return (_jsxs(Box, { flexDirection: "column", width: width, children: [_jsxs(Text, { color: t.color.label, children: ["error: ", err] }), _jsx(OverlayHint, { t: t, children: "Esc/q cancel" })] }));
145
+ }
146
+ if (!cats.length) {
147
+ return (_jsxs(Box, { flexDirection: "column", width: width, children: [_jsx(Text, { color: t.color.muted, children: "no skills available" }), _jsx(OverlayHint, { t: t, children: "Esc/q cancel" })] }));
148
+ }
149
+ if (stage === 'category') {
150
+ const rows = cats.map(c => `${c} · ${skillsByCat[c]?.length ?? 0} skills`);
151
+ const { items, offset } = windowItems(rows, catIdx, VISIBLE);
152
+ return (_jsxs(Box, { flexDirection: "column", width: width, children: [_jsx(Text, { bold: true, color: t.color.accent, children: "Skills Hub" }), _jsx(Text, { color: t.color.muted, children: "select a category" }), offset > 0 && _jsxs(Text, { color: t.color.muted, children: [" \u2191 ", offset, " more"] }), items.map((row, i) => {
153
+ const idx = offset + i;
154
+ return (_jsxs(Text, { bold: catIdx === idx, color: catIdx === idx ? t.color.accent : t.color.muted, inverse: catIdx === idx, wrap: "truncate-end", children: [catIdx === idx ? '▸ ' : ' ', i + 1, ". ", row] }, row));
155
+ }), offset + VISIBLE < rows.length && _jsxs(Text, { color: t.color.muted, children: [" \u2193 ", rows.length - offset - VISIBLE, " more"] }), _jsx(OverlayHint, { t: t, children: "\u2191/\u2193 select \u00B7 Enter open \u00B7 1-9,0 quick \u00B7 Esc/q cancel" })] }));
156
+ }
157
+ if (stage === 'skill') {
158
+ const { items, offset } = windowItems(skills, skillIdx, VISIBLE);
159
+ return (_jsxs(Box, { flexDirection: "column", width: width, children: [_jsx(Text, { bold: true, color: t.color.accent, children: selectedCat }), _jsxs(Text, { color: t.color.muted, children: [skills.length, " skill(s)"] }), !skills.length ? _jsx(Text, { color: t.color.muted, children: "no skills in this category" }) : null, offset > 0 && _jsxs(Text, { color: t.color.muted, children: [" \u2191 ", offset, " more"] }), items.map((row, i) => {
160
+ const idx = offset + i;
161
+ return (_jsxs(Text, { bold: skillIdx === idx, color: skillIdx === idx ? t.color.accent : t.color.muted, inverse: skillIdx === idx, wrap: "truncate-end", children: [skillIdx === idx ? '▸ ' : ' ', i + 1, ". ", row] }, row));
162
+ }), offset + VISIBLE < skills.length && (_jsxs(Text, { color: t.color.muted, children: [" \u2193 ", skills.length - offset - VISIBLE, " more"] })), _jsx(OverlayHint, { t: t, children: skills.length ? '↑/↓ select · Enter open · 1-9,0 quick · Esc back · q close' : 'Esc back · q close' })] }));
163
+ }
164
+ return (_jsxs(Box, { flexDirection: "column", width: width, children: [_jsx(Text, { bold: true, color: t.color.accent, children: info?.name ?? skillName }), _jsx(Text, { color: t.color.muted, children: info?.category ?? selectedCat }), info?.description ? _jsx(Text, { color: t.color.text, children: info.description }) : null, info?.path ? _jsxs(Text, { color: t.color.muted, children: ["path: ", info.path] }) : null, !info && !err ? _jsx(Text, { color: t.color.muted, children: "loading\u2026" }) : null, err ? _jsxs(Text, { color: t.color.label, children: ["error: ", err] }) : null, installing ? _jsx(Text, { color: t.color.accent, children: "installing\u2026" }) : null, _jsx(OverlayHint, { t: t, children: "i reinspect \u00B7 x reinstall \u00B7 Enter/Esc back \u00B7 q close" })] }));
165
+ }
@@ -0,0 +1,35 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // @ts-nocheck
3
+ // SPDX-License-Identifier: MIT
4
+ // Ported from CVC Agent (https://github.com/NousResearch/cvc)
5
+ // Original Copyright (c) 2025 Nous Research. CVC adaptations (c) 2026 Jai Kumar Meena.
6
+ import { useStore } from '@nanostores/react';
7
+ import { memo } from 'react';
8
+ import { toggleTodoCollapsed, useTurnSelector } from '../app/turnStore.js';
9
+ import { $uiState } from '../app/uiStore.js';
10
+ import { appendToolShelfMessage } from '../lib/liveProgress.js';
11
+ import { MessageLine } from './messageLine.js';
12
+ import { TodoPanel } from './todoPanel.js';
13
+ const groupedSegments = (segments) => segments.reduce((acc, msg) => appendToolShelfMessage(acc, msg), []);
14
+ export const StreamingAssistant = memo(function StreamingAssistant({ cols, compact, detailsMode, detailsModeCommandOverride, progress, sections }) {
15
+ const ui = useStore($uiState);
16
+ const streamSegments = useTurnSelector(state => state.streamSegments);
17
+ const streamPendingTools = useTurnSelector(state => state.streamPendingTools);
18
+ const streaming = useTurnSelector(state => state.streaming);
19
+ const activeTools = useTurnSelector(state => state.tools);
20
+ const showStreamingArea = Boolean(streaming);
21
+ if (!progress.showProgressArea && !showStreamingArea && !activeTools.length) {
22
+ return null;
23
+ }
24
+ return (_jsxs(_Fragment, { children: [groupedSegments(streamSegments).map((msg, i) => (_jsx(MessageLine, { cols: cols, compact: compact, detailsMode: detailsMode, detailsModeCommandOverride: detailsModeCommandOverride, msg: msg, sections: sections, t: ui.theme }, `seg:${i}`))), !!activeTools.length && (_jsx(MessageLine, { cols: cols, compact: compact, detailsMode: detailsMode, detailsModeCommandOverride: detailsModeCommandOverride, msg: { kind: 'trail', role: 'system', text: '' }, sections: sections, t: ui.theme, tools: activeTools })), showStreamingArea && (_jsx(MessageLine, { cols: cols, compact: compact, detailsMode: detailsMode, detailsModeCommandOverride: detailsModeCommandOverride, isStreaming: true, msg: {
25
+ role: 'assistant',
26
+ text: streaming,
27
+ ...(streamPendingTools.length && { tools: streamPendingTools })
28
+ }, sections: sections, t: ui.theme })), !showStreamingArea && !!streamPendingTools.length && (_jsx(MessageLine, { cols: cols, compact: compact, detailsMode: detailsMode, detailsModeCommandOverride: detailsModeCommandOverride, msg: { kind: 'trail', role: 'system', text: '', tools: streamPendingTools }, sections: sections, t: ui.theme }))] }));
29
+ });
30
+ export const LiveTodoPanel = memo(function LiveTodoPanel() {
31
+ const ui = useStore($uiState);
32
+ const todos = useTurnSelector(state => state.todos);
33
+ const collapsed = useTurnSelector(state => state.todoCollapsed);
34
+ return _jsx(TodoPanel, { collapsed: collapsed, onToggle: toggleTodoCollapsed, t: ui.theme, todos: todos });
35
+ });