cvc-tui 0.4.0 → 0.4.2
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/entry.js +71148 -61
- package/package.json +2 -2
- package/dist/app/completion.js +0 -102
- package/dist/app/createGatewayEventHandler.js +0 -508
- package/dist/app/createSlashHandler.js +0 -101
- package/dist/app/delegationStore.js +0 -51
- package/dist/app/gatewayContext.js +0 -17
- package/dist/app/historyStore.js +0 -123
- package/dist/app/inputBuffer.js +0 -120
- package/dist/app/inputSelectionStore.js +0 -8
- package/dist/app/inputStore.js +0 -28
- package/dist/app/interfaces.js +0 -6
- package/dist/app/overlayStore.js +0 -40
- package/dist/app/promptStore.js +0 -44
- package/dist/app/queueStore.js +0 -25
- package/dist/app/scroll.js +0 -44
- package/dist/app/setupHandoff.js +0 -28
- package/dist/app/slash/commands/core.js +0 -479
- package/dist/app/slash/commands/debug.js +0 -44
- package/dist/app/slash/commands/ops.js +0 -498
- package/dist/app/slash/commands/session.js +0 -431
- package/dist/app/slash/commands/setup.js +0 -20
- package/dist/app/slash/commands/toggles.js +0 -40
- package/dist/app/slash/registry.js +0 -18
- package/dist/app/slash/types.js +0 -1
- package/dist/app/spawnHistoryStore.js +0 -105
- package/dist/app/turnController.js +0 -650
- package/dist/app/turnStore.js +0 -48
- package/dist/app/uiStore.js +0 -36
- package/dist/app/useComposerState.js +0 -265
- package/dist/app/useConfigSync.js +0 -144
- package/dist/app/useInputHandlers.js +0 -403
- package/dist/app/useLongRunToolCharms.js +0 -50
- package/dist/app/useMainApp.js +0 -629
- package/dist/app/useSessionLifecycle.js +0 -175
- package/dist/app/useSubmission.js +0 -287
- package/dist/app.js +0 -15
- package/dist/banner.js +0 -57
- package/dist/components/agentsOverlay.js +0 -474
- package/dist/components/appChrome.js +0 -252
- package/dist/components/appLayout.js +0 -121
- package/dist/components/appOverlays.js +0 -65
- package/dist/components/branding.js +0 -97
- package/dist/components/fpsOverlay.js +0 -22
- package/dist/components/helpHint.js +0 -21
- package/dist/components/markdown.js +0 -501
- package/dist/components/maskedPrompt.js +0 -12
- package/dist/components/messageLine.js +0 -82
- package/dist/components/modelPicker.js +0 -254
- package/dist/components/overlayControls.js +0 -30
- package/dist/components/overlays/confirmPrompt.js +0 -25
- package/dist/components/overlays/helpOverlay.js +0 -76
- package/dist/components/overlays/historySearch.js +0 -49
- package/dist/components/overlays/modelPicker.js +0 -60
- package/dist/components/overlays/overlayUtils.js +0 -19
- package/dist/components/overlays/secretPrompt.js +0 -36
- package/dist/components/overlays/sessionPicker.js +0 -93
- package/dist/components/overlays/skillsHub.js +0 -71
- package/dist/components/prompts.js +0 -95
- package/dist/components/queuedMessages.js +0 -24
- package/dist/components/sessionPicker.js +0 -130
- package/dist/components/skillsHub.js +0 -165
- package/dist/components/streamingAssistant.js +0 -35
- package/dist/components/streamingMarkdown.js +0 -144
- package/dist/components/textInput.js +0 -794
- package/dist/components/themed.js +0 -12
- package/dist/components/thinking.js +0 -496
- package/dist/components/todoPanel.js +0 -40
- package/dist/components/transcript.js +0 -22
- package/dist/config/env.js +0 -18
- package/dist/config/limits.js +0 -22
- package/dist/config/timing.js +0 -18
- package/dist/content/charms.js +0 -5
- package/dist/content/faces.js +0 -21
- package/dist/content/fortunes.js +0 -29
- package/dist/content/hotkeys.js +0 -38
- package/dist/content/placeholders.js +0 -15
- package/dist/content/setup.js +0 -14
- package/dist/content/verbs.js +0 -41
- package/dist/domain/details.js +0 -53
- package/dist/domain/messages.js +0 -63
- package/dist/domain/paths.js +0 -16
- package/dist/domain/providers.js +0 -11
- package/dist/domain/roles.js +0 -6
- package/dist/domain/slash.js +0 -11
- package/dist/domain/usage.js +0 -1
- package/dist/domain/viewport.js +0 -33
- package/dist/gateway/client.js +0 -312
- package/dist/gatewayClient.js +0 -574
- package/dist/gatewayTypes.js +0 -1
- package/dist/hooks/useCompletion.js +0 -86
- package/dist/hooks/useGitBranch.js +0 -58
- package/dist/hooks/useInputHistory.js +0 -12
- package/dist/hooks/useQueue.js +0 -57
- package/dist/hooks/useVirtualHistory.js +0 -401
- package/dist/lib/circularBuffer.js +0 -43
- package/dist/lib/clipboard.js +0 -126
- package/dist/lib/editor.js +0 -41
- package/dist/lib/editor.test.js +0 -58
- package/dist/lib/emoji.js +0 -49
- package/dist/lib/externalCli.js +0 -11
- package/dist/lib/forceTruecolor.js +0 -26
- package/dist/lib/fpsStore.js +0 -36
- package/dist/lib/gracefulExit.js +0 -29
- package/dist/lib/history.js +0 -69
- package/dist/lib/inputMetrics.js +0 -143
- package/dist/lib/liveProgress.js +0 -51
- package/dist/lib/liveProgress.test.js +0 -89
- package/dist/lib/mathUnicode.js +0 -685
- package/dist/lib/memory.js +0 -123
- package/dist/lib/memoryMonitor.js +0 -76
- package/dist/lib/messages.js +0 -3
- package/dist/lib/messages.test.js +0 -25
- package/dist/lib/osc52.js +0 -53
- package/dist/lib/perfPane.js +0 -94
- package/dist/lib/platform.js +0 -312
- package/dist/lib/precisionWheel.js +0 -25
- package/dist/lib/reasoning.js +0 -39
- package/dist/lib/rpc.js +0 -26
- package/dist/lib/subagentTree.js +0 -287
- package/dist/lib/syntax.js +0 -89
- package/dist/lib/terminalModes.js +0 -46
- package/dist/lib/terminalParity.js +0 -48
- package/dist/lib/terminalSetup.js +0 -321
- package/dist/lib/text.js +0 -203
- package/dist/lib/text.test.js +0 -18
- package/dist/lib/todo.js +0 -2
- package/dist/lib/todo.test.js +0 -22
- package/dist/lib/viewportStore.js +0 -82
- package/dist/lib/virtualHeights.js +0 -61
- package/dist/lib/wheelAccel.js +0 -143
- package/dist/theme.js +0 -398
- package/dist/types.js +0 -1
package/dist/lib/clipboard.js
DELETED
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
|
-
// SPDX-License-Identifier: MIT
|
|
3
|
-
// Ported from CVC Agent (https://github.com/NousResearch/cvc)
|
|
4
|
-
// Original Copyright (c) 2025 Nous Research. CVC adaptations (c) 2026 Jai Kumar Meena.
|
|
5
|
-
import { execFile, spawn } from 'node:child_process';
|
|
6
|
-
import { promisify } from 'node:util';
|
|
7
|
-
const execFileAsync = promisify(execFile);
|
|
8
|
-
const CLIPBOARD_MAX_BUFFER = 4 * 1024 * 1024;
|
|
9
|
-
const POWERSHELL_ARGS = ['-NoProfile', '-NonInteractive', '-Command', 'Get-Clipboard -Raw'];
|
|
10
|
-
export function isUsableClipboardText(text) {
|
|
11
|
-
if (!text || !/[^\s]/.test(text)) {
|
|
12
|
-
return false;
|
|
13
|
-
}
|
|
14
|
-
if (text.includes('\u0000')) {
|
|
15
|
-
return false;
|
|
16
|
-
}
|
|
17
|
-
let suspicious = 0;
|
|
18
|
-
for (const ch of text) {
|
|
19
|
-
const code = ch.charCodeAt(0);
|
|
20
|
-
const isControl = code < 0x20 && ch !== '\n' && ch !== '\r' && ch !== '\t';
|
|
21
|
-
if (isControl || ch === '\ufffd') {
|
|
22
|
-
suspicious += 1;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
return suspicious <= Math.max(2, Math.floor(text.length * 0.02));
|
|
26
|
-
}
|
|
27
|
-
function readClipboardCommands(platform, env) {
|
|
28
|
-
if (platform === 'darwin') {
|
|
29
|
-
return [{ cmd: 'pbpaste', args: [] }];
|
|
30
|
-
}
|
|
31
|
-
if (platform === 'win32') {
|
|
32
|
-
return [{ cmd: 'powershell', args: POWERSHELL_ARGS }];
|
|
33
|
-
}
|
|
34
|
-
const attempts = [];
|
|
35
|
-
if (env.WSL_INTEROP || env.WSL_DISTRO_NAME) {
|
|
36
|
-
attempts.push({ cmd: 'powershell.exe', args: POWERSHELL_ARGS });
|
|
37
|
-
}
|
|
38
|
-
if (env.WAYLAND_DISPLAY) {
|
|
39
|
-
attempts.push({ cmd: 'wl-paste', args: ['--type', 'text'] });
|
|
40
|
-
}
|
|
41
|
-
attempts.push({ cmd: 'xclip', args: ['-selection', 'clipboard', '-out'] });
|
|
42
|
-
return attempts;
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Read plain text from the system clipboard.
|
|
46
|
-
*
|
|
47
|
-
* Uses native platform tools in fallback order:
|
|
48
|
-
* - macOS: pbpaste
|
|
49
|
-
* - Windows: PowerShell Get-Clipboard -Raw
|
|
50
|
-
* - WSL: powershell.exe Get-Clipboard -Raw
|
|
51
|
-
* - Linux Wayland: wl-paste --type text
|
|
52
|
-
* - Linux X11: xclip -selection clipboard -out
|
|
53
|
-
*/
|
|
54
|
-
export async function readClipboardText(platform = process.platform, run = execFileAsync, env = process.env) {
|
|
55
|
-
for (const attempt of readClipboardCommands(platform, env)) {
|
|
56
|
-
try {
|
|
57
|
-
const result = await run(attempt.cmd, [...attempt.args], {
|
|
58
|
-
encoding: 'utf8',
|
|
59
|
-
maxBuffer: CLIPBOARD_MAX_BUFFER,
|
|
60
|
-
windowsHide: true
|
|
61
|
-
});
|
|
62
|
-
if (typeof result.stdout === 'string') {
|
|
63
|
-
return result.stdout;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
catch {
|
|
67
|
-
// Fall through to the next clipboard backend.
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
return null;
|
|
71
|
-
}
|
|
72
|
-
function writeClipboardCommands(platform, env) {
|
|
73
|
-
if (platform === 'darwin') {
|
|
74
|
-
return [{ cmd: 'pbcopy', args: [] }];
|
|
75
|
-
}
|
|
76
|
-
if (platform === 'win32') {
|
|
77
|
-
return [{ cmd: 'powershell', args: ['-NoProfile', '-NonInteractive', '-Command', 'Set-Clipboard -Value $input'] }];
|
|
78
|
-
}
|
|
79
|
-
const attempts = [];
|
|
80
|
-
if (env.WSL_INTEROP || env.WSL_DISTRO_NAME) {
|
|
81
|
-
attempts.push({
|
|
82
|
-
cmd: 'powershell.exe',
|
|
83
|
-
args: ['-NoProfile', '-NonInteractive', '-Command', 'Set-Clipboard -Value $input']
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
if (env.WAYLAND_DISPLAY) {
|
|
87
|
-
attempts.push({ cmd: 'wl-copy', args: ['--type', 'text/plain'] });
|
|
88
|
-
}
|
|
89
|
-
attempts.push({ cmd: 'xclip', args: ['-selection', 'clipboard', '-in'] });
|
|
90
|
-
attempts.push({ cmd: 'xsel', args: ['--clipboard', '--input'] });
|
|
91
|
-
return attempts;
|
|
92
|
-
}
|
|
93
|
-
/**
|
|
94
|
-
* Write plain text to the system clipboard.
|
|
95
|
-
*
|
|
96
|
-
* Tries native platform tools in fallback order:
|
|
97
|
-
* - macOS: pbcopy
|
|
98
|
-
* - Windows: PowerShell Set-Clipboard
|
|
99
|
-
* - WSL: powershell.exe Set-Clipboard
|
|
100
|
-
* - Linux Wayland: wl-copy --type text/plain
|
|
101
|
-
* - Linux X11: xclip -selection clipboard -in
|
|
102
|
-
* - Linux X11 alt: xsel --clipboard --input
|
|
103
|
-
*
|
|
104
|
-
* Returns true if at least one backend succeeded, false otherwise
|
|
105
|
-
* (callers should fall back to OSC52 on false).
|
|
106
|
-
*/
|
|
107
|
-
export async function writeClipboardText(text, platform = process.platform, start = spawn, env = process.env) {
|
|
108
|
-
const candidates = writeClipboardCommands(platform, env);
|
|
109
|
-
for (const { cmd, args } of candidates) {
|
|
110
|
-
try {
|
|
111
|
-
const ok = await new Promise(resolve => {
|
|
112
|
-
const child = start(cmd, [...args], { stdio: ['pipe', 'ignore', 'ignore'], windowsHide: true });
|
|
113
|
-
child.once('error', () => resolve(false));
|
|
114
|
-
child.once('close', code => resolve(code === 0));
|
|
115
|
-
child.stdin?.end(text);
|
|
116
|
-
});
|
|
117
|
-
if (ok) {
|
|
118
|
-
return true;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
catch {
|
|
122
|
-
// Fall through to the next clipboard backend.
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
return false;
|
|
126
|
-
}
|
package/dist/lib/editor.js
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
|
-
// SPDX-License-Identifier: MIT
|
|
3
|
-
// Ported from CVC Agent (https://github.com/NousResearch/cvc)
|
|
4
|
-
// Original Copyright (c) 2025 Nous Research. CVC adaptations (c) 2026 Jai Kumar Meena.
|
|
5
|
-
import { accessSync, constants } from 'node:fs';
|
|
6
|
-
import { delimiter, join } from 'node:path';
|
|
7
|
-
/**
|
|
8
|
-
* Editor fallback chain when neither $VISUAL nor $EDITOR is set. Mirrors
|
|
9
|
-
* prompt_toolkit's `Buffer.open_in_editor()` picker so the classic CLI and
|
|
10
|
-
* the TUI launch the same editor on a given box.
|
|
11
|
-
*/
|
|
12
|
-
const FALLBACKS = ['editor', 'nano', 'pico', 'vi', 'emacs'];
|
|
13
|
-
const isExecutable = (path) => {
|
|
14
|
-
try {
|
|
15
|
-
accessSync(path, constants.X_OK);
|
|
16
|
-
return true;
|
|
17
|
-
}
|
|
18
|
-
catch {
|
|
19
|
-
return false;
|
|
20
|
-
}
|
|
21
|
-
};
|
|
22
|
-
/**
|
|
23
|
-
* Resolve the editor invocation argv (without the file argument).
|
|
24
|
-
*
|
|
25
|
-
* 1. $VISUAL / $EDITOR, shell-tokenized so `EDITOR="code --wait"` works
|
|
26
|
-
* 2. on POSIX: first FALLBACKS entry resolvable on $PATH
|
|
27
|
-
* 3. on Windows: `notepad.exe`
|
|
28
|
-
* 4. literal `['vi']` as the last-resort POSIX floor
|
|
29
|
-
*/
|
|
30
|
-
export const resolveEditor = (env = process.env, platform = process.platform) => {
|
|
31
|
-
const explicit = env.VISUAL ?? env.EDITOR;
|
|
32
|
-
if (explicit?.trim()) {
|
|
33
|
-
return explicit.trim().split(/\s+/);
|
|
34
|
-
}
|
|
35
|
-
if (platform === 'win32') {
|
|
36
|
-
return ['notepad.exe'];
|
|
37
|
-
}
|
|
38
|
-
const dirs = (env.PATH ?? '').split(delimiter).filter(Boolean);
|
|
39
|
-
const found = FALLBACKS.flatMap(name => dirs.map(d => join(d, name))).find(isExecutable);
|
|
40
|
-
return [found ?? 'vi'];
|
|
41
|
-
};
|
package/dist/lib/editor.test.js
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
|
-
// SPDX-License-Identifier: MIT
|
|
3
|
-
// Ported from CVC Agent (https://github.com/NousResearch/cvc)
|
|
4
|
-
// Original Copyright (c) 2025 Nous Research. CVC adaptations (c) 2026 Jai Kumar Meena.
|
|
5
|
-
import { chmodSync, mkdtempSync, writeFileSync } from 'node:fs';
|
|
6
|
-
import { tmpdir } from 'node:os';
|
|
7
|
-
import { delimiter, join } from 'node:path';
|
|
8
|
-
import { beforeEach, describe, expect, it } from 'vitest';
|
|
9
|
-
import { resolveEditor } from './editor.js';
|
|
10
|
-
const exe = (dir, name) => {
|
|
11
|
-
const path = join(dir, name);
|
|
12
|
-
writeFileSync(path, '#!/bin/sh\nexit 0\n');
|
|
13
|
-
chmodSync(path, 0o755);
|
|
14
|
-
return path;
|
|
15
|
-
};
|
|
16
|
-
describe('resolveEditor', () => {
|
|
17
|
-
let dir;
|
|
18
|
-
beforeEach(() => {
|
|
19
|
-
dir = mkdtempSync(join(tmpdir(), 'editor-test-'));
|
|
20
|
-
});
|
|
21
|
-
it('honors $VISUAL above all else', () => {
|
|
22
|
-
expect(resolveEditor({ EDITOR: 'vim', PATH: dir, VISUAL: 'helix' })).toEqual(['helix']);
|
|
23
|
-
});
|
|
24
|
-
it('falls back to $EDITOR when $VISUAL is unset', () => {
|
|
25
|
-
expect(resolveEditor({ EDITOR: 'nvim', PATH: dir })).toEqual(['nvim']);
|
|
26
|
-
});
|
|
27
|
-
it('shell-tokenizes editors with arguments', () => {
|
|
28
|
-
expect(resolveEditor({ EDITOR: 'code --wait', PATH: dir })).toEqual(['code', '--wait']);
|
|
29
|
-
expect(resolveEditor({ PATH: dir, VISUAL: 'emacsclient -t' })).toEqual(['emacsclient', '-t']);
|
|
30
|
-
});
|
|
31
|
-
it('ignores whitespace-only env vars', () => {
|
|
32
|
-
const expected = exe(dir, 'editor');
|
|
33
|
-
expect(resolveEditor({ EDITOR: ' ', PATH: dir, VISUAL: '' })).toEqual([expected]);
|
|
34
|
-
});
|
|
35
|
-
it('prefers `editor` over nano over vi on $PATH', () => {
|
|
36
|
-
exe(dir, 'nano');
|
|
37
|
-
exe(dir, 'vi');
|
|
38
|
-
const expected = exe(dir, 'editor');
|
|
39
|
-
expect(resolveEditor({ PATH: dir })).toEqual([expected]);
|
|
40
|
-
});
|
|
41
|
-
it('falls back to nano before vi when both exist', () => {
|
|
42
|
-
exe(dir, 'vi');
|
|
43
|
-
const expected = exe(dir, 'nano');
|
|
44
|
-
expect(resolveEditor({ PATH: dir })).toEqual([expected]);
|
|
45
|
-
});
|
|
46
|
-
it('returns ["vi"] when $PATH is empty', () => {
|
|
47
|
-
expect(resolveEditor({ PATH: '' })).toEqual(['vi']);
|
|
48
|
-
});
|
|
49
|
-
it('walks multi-entry $PATH', () => {
|
|
50
|
-
const a = mkdtempSync(join(tmpdir(), 'editor-a-'));
|
|
51
|
-
const b = mkdtempSync(join(tmpdir(), 'editor-b-'));
|
|
52
|
-
const expected = exe(b, 'editor');
|
|
53
|
-
expect(resolveEditor({ PATH: [a, b].join(delimiter) })).toEqual([expected]);
|
|
54
|
-
});
|
|
55
|
-
it('uses notepad.exe on Windows when no env override', () => {
|
|
56
|
-
expect(resolveEditor({ PATH: dir }, 'win32')).toEqual(['notepad.exe']);
|
|
57
|
-
});
|
|
58
|
-
});
|
package/dist/lib/emoji.js
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
|
-
// SPDX-License-Identifier: MIT
|
|
3
|
-
// Ported from CVC Agent (https://github.com/NousResearch/cvc)
|
|
4
|
-
// Original Copyright (c) 2025 Nous Research. CVC adaptations (c) 2026 Jai Kumar Meena.
|
|
5
|
-
const VS15 = 0xfe0e;
|
|
6
|
-
const VS16 = 0xfe0f;
|
|
7
|
-
const KEYCAP = 0x20e3;
|
|
8
|
-
const TEXT_DEFAULT_EMOJI = new Set([
|
|
9
|
-
0x00a9, 0x00ae, 0x203c, 0x2049, 0x2122, 0x2139, 0x2194, 0x2195, 0x2196, 0x2197, 0x2198, 0x2199, 0x21a9, 0x21aa,
|
|
10
|
-
0x2328, 0x23cf, 0x23ed, 0x23ee, 0x23ef, 0x23f1, 0x23f2, 0x23f8, 0x23f9, 0x23fa, 0x24c2, 0x25aa, 0x25ab, 0x25b6,
|
|
11
|
-
0x25c0, 0x25fb, 0x25fc, 0x2600, 0x2601, 0x2602, 0x2603, 0x2604, 0x260e, 0x2611, 0x2618, 0x261d, 0x2620, 0x2622,
|
|
12
|
-
0x2623, 0x2626, 0x262a, 0x262e, 0x262f, 0x2638, 0x2639, 0x263a, 0x2640, 0x2642, 0x265f, 0x2660, 0x2663, 0x2665,
|
|
13
|
-
0x2666, 0x2668, 0x267b, 0x267e, 0x2692, 0x2694, 0x2695, 0x2696, 0x2697, 0x2699, 0x269b, 0x269c, 0x26a0, 0x26a7,
|
|
14
|
-
0x26b0, 0x26b1, 0x26c8, 0x26cf, 0x26d1, 0x26d3, 0x26d4, 0x26e9, 0x26f0, 0x26f1, 0x26f4, 0x26f7, 0x26f8, 0x26f9,
|
|
15
|
-
0x2702, 0x2708, 0x2709, 0x270c, 0x270d, 0x270f, 0x2712, 0x2714, 0x2716, 0x271d, 0x2721, 0x2733, 0x2734, 0x2744,
|
|
16
|
-
0x2747, 0x2763, 0x2764, 0x27a1, 0x2934, 0x2935, 0x2b05, 0x2b06, 0x2b07, 0x3030, 0x303d, 0x3297, 0x3299
|
|
17
|
-
]);
|
|
18
|
-
const MAYBE_TEXT_EMOJI_RE = /[\u00a9\u00ae\u203c\u2049\u2122\u2139\u2194-\u2199\u21a9\u21aa\u2328\u23cf\u23ed-\u23ef\u23f1\u23f2\u23f8-\u23fa\u24c2\u25aa\u25ab\u25b6\u25c0\u25fb\u25fc\u2600-\u2604\u260e\u2611\u2618\u261d\u2620\u2622\u2623\u2626\u262a\u262e\u262f\u2638-\u263a\u2640\u2642\u265f\u2660\u2663\u2665\u2666\u2668\u267b\u267e\u2692\u2694-\u2697\u2699\u269b\u269c\u26a0\u26a7\u26b0\u26b1\u26c8\u26cf\u26d1\u26d3\u26d4\u26e9\u26f0\u26f1\u26f4\u26f7-\u26f9\u2702\u2708\u2709\u270c\u270d\u270f\u2712\u2714\u2716\u271d\u2721\u2733\u2734\u2744\u2747\u2763\u2764\u27a1\u2934\u2935\u2b05-\u2b07\u3030\u303d\u3297\u3299]/;
|
|
19
|
-
export function ensureEmojiPresentation(text) {
|
|
20
|
-
if (!text || !MAYBE_TEXT_EMOJI_RE.test(text)) {
|
|
21
|
-
return text;
|
|
22
|
-
}
|
|
23
|
-
// Lazy output: only start building when we actually need to insert VS16.
|
|
24
|
-
// Short-circuits the whole walk for strings where every text-default emoji
|
|
25
|
-
// is already followed by VS16/VS15, avoiding per-codepoint string growth.
|
|
26
|
-
let out = null;
|
|
27
|
-
let last = 0;
|
|
28
|
-
let i = 0;
|
|
29
|
-
while (i < text.length) {
|
|
30
|
-
const cp = text.codePointAt(i);
|
|
31
|
-
const size = cp > 0xffff ? 2 : 1;
|
|
32
|
-
if (TEXT_DEFAULT_EMOJI.has(cp)) {
|
|
33
|
-
const next = text.codePointAt(i + size);
|
|
34
|
-
// Skip only when the sequence already carries an explicit presentation
|
|
35
|
-
// selector. VS16 means the user (or a prior pass) already requested
|
|
36
|
-
// emoji presentation; VS15 is an explicit text-presentation request so
|
|
37
|
-
// leave it alone and don't pile VS16 on top of it. Inject before ZWJ
|
|
38
|
-
// and KEYCAP so ZWJ-joined sequences (e.g. ❤️🔥) and digit keycaps
|
|
39
|
-
// both render as emoji rather than text.
|
|
40
|
-
if (next !== VS16 && next !== VS15) {
|
|
41
|
-
out ??= '';
|
|
42
|
-
out += text.slice(last, i + size) + '\uFE0F';
|
|
43
|
-
last = i + size;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
i += size;
|
|
47
|
-
}
|
|
48
|
-
return out === null ? text : out + text.slice(last);
|
|
49
|
-
}
|
package/dist/lib/externalCli.js
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
|
-
// SPDX-License-Identifier: MIT
|
|
3
|
-
// Ported from CVC Agent (https://github.com/NousResearch/cvc)
|
|
4
|
-
// Original Copyright (c) 2025 Nous Research. CVC adaptations (c) 2026 Jai Kumar Meena.
|
|
5
|
-
import { spawn } from 'node:child_process';
|
|
6
|
-
const resolveHermesBin = () => process.env.HERMES_BIN?.trim() || 'hermes';
|
|
7
|
-
export const launchHermesCommand = (args) => new Promise(resolve => {
|
|
8
|
-
const child = spawn(resolveHermesBin(), args, { stdio: 'inherit' });
|
|
9
|
-
child.on('error', err => resolve({ code: null, error: err.message }));
|
|
10
|
-
child.on('exit', code => resolve({ code }));
|
|
11
|
-
});
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
|
-
// SPDX-License-Identifier: MIT
|
|
3
|
-
// Ported from CVC Agent (https://github.com/NousResearch/cvc)
|
|
4
|
-
// Original Copyright (c) 2025 Nous Research. CVC adaptations (c) 2026 Jai Kumar Meena.
|
|
5
|
-
/**
|
|
6
|
-
* Targeted 24-bit truecolor override before chalk / supports-color imports.
|
|
7
|
-
*
|
|
8
|
-
* macOS Terminal.app before Tahoe 26 does not support RGB SGR, so do not
|
|
9
|
-
* infer truecolor from TERM_PROGRAM=Apple_Terminal. Users can still opt in
|
|
10
|
-
* explicitly on terminals that support RGB but do not advertise COLORTERM.
|
|
11
|
-
*/
|
|
12
|
-
const TRUE_RE = /^(?:1|true|yes|on)$/i;
|
|
13
|
-
const FALSE_RE = /^(?:0|false|no|off)$/i;
|
|
14
|
-
export function shouldForceTruecolor(env = process.env) {
|
|
15
|
-
const override = (env.CVC_TUI_TRUECOLOR ?? '').trim();
|
|
16
|
-
if (FALSE_RE.test(override) || 'NO_COLOR' in env) {
|
|
17
|
-
return false;
|
|
18
|
-
}
|
|
19
|
-
return TRUE_RE.test(override);
|
|
20
|
-
}
|
|
21
|
-
if (shouldForceTruecolor()) {
|
|
22
|
-
if (!process.env.COLORTERM) {
|
|
23
|
-
process.env.COLORTERM = 'truecolor';
|
|
24
|
-
}
|
|
25
|
-
process.env.FORCE_COLOR = '3';
|
|
26
|
-
}
|
package/dist/lib/fpsStore.js
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
|
-
// SPDX-License-Identifier: MIT
|
|
3
|
-
// Ported from CVC Agent (https://github.com/NousResearch/cvc)
|
|
4
|
-
// Original Copyright (c) 2025 Nous Research. CVC adaptations (c) 2026 Jai Kumar Meena.
|
|
5
|
-
// Tiny FPS tracker fed by ink's onFrame callback. Each entry is an Ink
|
|
6
|
-
// frame (React commit + drain-only frames) — the right notion for
|
|
7
|
-
// user-perceived motion.
|
|
8
|
-
//
|
|
9
|
-
// Zero-cost when CVC_TUI_FPS is unset: trackFrame is undefined so the
|
|
10
|
-
// onFrame callback short-circuits at the optional chain.
|
|
11
|
-
import { atom } from 'nanostores';
|
|
12
|
-
import { SHOW_FPS } from '../config/env.js';
|
|
13
|
-
const WINDOW_SIZE = 30;
|
|
14
|
-
export const $fpsState = atom({ fps: 0, lastDurationMs: 0, totalFrames: 0 });
|
|
15
|
-
const timestamps = [];
|
|
16
|
-
let totalFrames = 0;
|
|
17
|
-
export const trackFrame = SHOW_FPS
|
|
18
|
-
? (durationMs) => {
|
|
19
|
-
timestamps.push(performance.now());
|
|
20
|
-
if (timestamps.length > WINDOW_SIZE) {
|
|
21
|
-
timestamps.shift();
|
|
22
|
-
}
|
|
23
|
-
totalFrames++;
|
|
24
|
-
if (timestamps.length < 2) {
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
const elapsed = (timestamps[timestamps.length - 1] - timestamps[0]) / 1000;
|
|
28
|
-
if (elapsed > 0) {
|
|
29
|
-
$fpsState.set({
|
|
30
|
-
fps: Math.round(((timestamps.length - 1) / elapsed) * 10) / 10,
|
|
31
|
-
lastDurationMs: Math.round(durationMs * 100) / 100,
|
|
32
|
-
totalFrames
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
: undefined;
|
package/dist/lib/gracefulExit.js
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
const SIGNAL_EXIT_CODE = {
|
|
2
|
-
SIGHUP: 129,
|
|
3
|
-
SIGINT: 130,
|
|
4
|
-
SIGTERM: 143
|
|
5
|
-
};
|
|
6
|
-
let wired = false;
|
|
7
|
-
export function setupGracefulExit({ cleanups = [], failsafeMs = 4000, onError, onSignal } = {}) {
|
|
8
|
-
if (wired) {
|
|
9
|
-
return;
|
|
10
|
-
}
|
|
11
|
-
wired = true;
|
|
12
|
-
let shuttingDown = false;
|
|
13
|
-
const exit = (code, signal) => {
|
|
14
|
-
if (shuttingDown) {
|
|
15
|
-
return;
|
|
16
|
-
}
|
|
17
|
-
shuttingDown = true;
|
|
18
|
-
if (signal) {
|
|
19
|
-
onSignal?.(signal);
|
|
20
|
-
}
|
|
21
|
-
setTimeout(() => process.exit(code), failsafeMs).unref?.();
|
|
22
|
-
void Promise.allSettled(cleanups.map(fn => Promise.resolve().then(fn))).finally(() => process.exit(code));
|
|
23
|
-
};
|
|
24
|
-
for (const sig of ['SIGINT', 'SIGTERM', 'SIGHUP']) {
|
|
25
|
-
process.on(sig, () => exit(SIGNAL_EXIT_CODE[sig], sig));
|
|
26
|
-
}
|
|
27
|
-
process.on('uncaughtException', err => onError?.('uncaughtException', err));
|
|
28
|
-
process.on('unhandledRejection', reason => onError?.('unhandledRejection', reason));
|
|
29
|
-
}
|
package/dist/lib/history.js
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
|
-
// SPDX-License-Identifier: MIT
|
|
3
|
-
// Ported from CVC Agent (https://github.com/NousResearch/cvc)
|
|
4
|
-
// Original Copyright (c) 2025 Nous Research. CVC adaptations (c) 2026 Jai Kumar Meena.
|
|
5
|
-
import { appendFileSync, existsSync, mkdirSync, readFileSync } from 'node:fs';
|
|
6
|
-
import { homedir } from 'node:os';
|
|
7
|
-
import { join } from 'node:path';
|
|
8
|
-
const MAX = 1000;
|
|
9
|
-
const dir = process.env.HERMES_HOME ?? join(homedir(), '.hermes');
|
|
10
|
-
const file = join(dir, '.hermes_history');
|
|
11
|
-
let cache = null;
|
|
12
|
-
export function load() {
|
|
13
|
-
if (cache) {
|
|
14
|
-
return cache;
|
|
15
|
-
}
|
|
16
|
-
try {
|
|
17
|
-
if (!existsSync(file)) {
|
|
18
|
-
cache = [];
|
|
19
|
-
return cache;
|
|
20
|
-
}
|
|
21
|
-
const entries = [];
|
|
22
|
-
let current = [];
|
|
23
|
-
for (const line of readFileSync(file, 'utf8').split('\n')) {
|
|
24
|
-
if (line.startsWith('+')) {
|
|
25
|
-
current.push(line.slice(1));
|
|
26
|
-
}
|
|
27
|
-
else if (current.length) {
|
|
28
|
-
entries.push(current.join('\n'));
|
|
29
|
-
current = [];
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
if (current.length) {
|
|
33
|
-
entries.push(current.join('\n'));
|
|
34
|
-
}
|
|
35
|
-
cache = entries.slice(-MAX);
|
|
36
|
-
}
|
|
37
|
-
catch {
|
|
38
|
-
cache = [];
|
|
39
|
-
}
|
|
40
|
-
return cache;
|
|
41
|
-
}
|
|
42
|
-
export function append(line) {
|
|
43
|
-
const trimmed = line.trim();
|
|
44
|
-
if (!trimmed) {
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
const items = load();
|
|
48
|
-
if (items.at(-1) === trimmed) {
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
items.push(trimmed);
|
|
52
|
-
if (items.length > MAX) {
|
|
53
|
-
items.splice(0, items.length - MAX);
|
|
54
|
-
}
|
|
55
|
-
try {
|
|
56
|
-
if (!existsSync(dir)) {
|
|
57
|
-
mkdirSync(dir, { recursive: true });
|
|
58
|
-
}
|
|
59
|
-
const ts = new Date().toISOString().replace('T', ' ').replace('Z', '');
|
|
60
|
-
const encoded = trimmed
|
|
61
|
-
.split('\n')
|
|
62
|
-
.map(l => `+${l}`)
|
|
63
|
-
.join('\n');
|
|
64
|
-
appendFileSync(file, `\n# ${ts}\n${encoded}\n`);
|
|
65
|
-
}
|
|
66
|
-
catch {
|
|
67
|
-
void 0;
|
|
68
|
-
}
|
|
69
|
-
}
|
package/dist/lib/inputMetrics.js
DELETED
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
|
-
// SPDX-License-Identifier: MIT
|
|
3
|
-
// Ported from CVC Agent (https://github.com/NousResearch/cvc)
|
|
4
|
-
// Original Copyright (c) 2025 Nous Research. CVC adaptations (c) 2026 Jai Kumar Meena.
|
|
5
|
-
import { stringWidth } from '@cvc/ink';
|
|
6
|
-
export const COMPOSER_PROMPT_GAP_WIDTH = 1;
|
|
7
|
-
let _seg = null;
|
|
8
|
-
const seg = () => (_seg ??= new Intl.Segmenter(undefined, { granularity: 'grapheme' }));
|
|
9
|
-
const isWhitespace = (value) => /\s/.test(value);
|
|
10
|
-
const graphemes = (value) => [...seg().segment(value)].map(({ segment, index }) => ({
|
|
11
|
-
end: index + segment.length,
|
|
12
|
-
index,
|
|
13
|
-
segment,
|
|
14
|
-
width: Math.max(1, stringWidth(segment))
|
|
15
|
-
}));
|
|
16
|
-
function visualLines(value, cols) {
|
|
17
|
-
const width = Math.max(1, cols);
|
|
18
|
-
const lines = [];
|
|
19
|
-
let sourceLineStart = 0;
|
|
20
|
-
for (const sourceLine of value.split('\n')) {
|
|
21
|
-
const parts = graphemes(sourceLine);
|
|
22
|
-
if (!parts.length) {
|
|
23
|
-
lines.push({ start: sourceLineStart, end: sourceLineStart });
|
|
24
|
-
sourceLineStart += 1;
|
|
25
|
-
continue;
|
|
26
|
-
}
|
|
27
|
-
let lineStartPart = 0;
|
|
28
|
-
let lineStartOffset = sourceLineStart;
|
|
29
|
-
let column = 0;
|
|
30
|
-
let breakPart = null;
|
|
31
|
-
let i = 0;
|
|
32
|
-
while (i < parts.length) {
|
|
33
|
-
const part = parts[i];
|
|
34
|
-
const partStart = sourceLineStart + part.index;
|
|
35
|
-
if (column + part.width > width && i > lineStartPart) {
|
|
36
|
-
if (breakPart !== null && breakPart > lineStartPart) {
|
|
37
|
-
const breakOffset = sourceLineStart + parts[breakPart - 1].end;
|
|
38
|
-
lines.push({ start: lineStartOffset, end: breakOffset });
|
|
39
|
-
lineStartPart = breakPart;
|
|
40
|
-
lineStartOffset = breakOffset;
|
|
41
|
-
}
|
|
42
|
-
else {
|
|
43
|
-
lines.push({ start: lineStartOffset, end: partStart });
|
|
44
|
-
lineStartPart = i;
|
|
45
|
-
lineStartOffset = partStart;
|
|
46
|
-
}
|
|
47
|
-
column = 0;
|
|
48
|
-
breakPart = null;
|
|
49
|
-
i = lineStartPart;
|
|
50
|
-
continue;
|
|
51
|
-
}
|
|
52
|
-
column += part.width;
|
|
53
|
-
if (isWhitespace(part.segment)) {
|
|
54
|
-
breakPart = i + 1;
|
|
55
|
-
}
|
|
56
|
-
i += 1;
|
|
57
|
-
if (column >= width && i < parts.length) {
|
|
58
|
-
const next = parts[i];
|
|
59
|
-
const nextStartsWord = !isWhitespace(next.segment);
|
|
60
|
-
if (breakPart !== null && breakPart > lineStartPart && nextStartsWord) {
|
|
61
|
-
const breakOffset = sourceLineStart + parts[breakPart - 1].end;
|
|
62
|
-
lines.push({ start: lineStartOffset, end: breakOffset });
|
|
63
|
-
lineStartPart = breakPart;
|
|
64
|
-
lineStartOffset = breakOffset;
|
|
65
|
-
column = 0;
|
|
66
|
-
breakPart = null;
|
|
67
|
-
i = lineStartPart;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
lines.push({ start: lineStartOffset, end: sourceLineStart + sourceLine.length });
|
|
72
|
-
sourceLineStart += sourceLine.length + 1;
|
|
73
|
-
}
|
|
74
|
-
return lines.length ? lines : [{ start: 0, end: 0 }];
|
|
75
|
-
}
|
|
76
|
-
function widthBetween(value, start, end) {
|
|
77
|
-
let width = 0;
|
|
78
|
-
for (const part of graphemes(value.slice(start, end))) {
|
|
79
|
-
width += part.width;
|
|
80
|
-
}
|
|
81
|
-
return width;
|
|
82
|
-
}
|
|
83
|
-
/**
|
|
84
|
-
* Mirrors the word-wrap behavior used by the composer TextInput.
|
|
85
|
-
* Returns the zero-based visual line and column of the cursor cell.
|
|
86
|
-
*/
|
|
87
|
-
export function cursorLayout(value, cursor, cols) {
|
|
88
|
-
const pos = Math.max(0, Math.min(cursor, value.length));
|
|
89
|
-
const w = Math.max(1, cols);
|
|
90
|
-
const lines = visualLines(value, w);
|
|
91
|
-
let lineIndex = 0;
|
|
92
|
-
for (let i = 0; i < lines.length; i += 1) {
|
|
93
|
-
if (lines[i].start <= pos) {
|
|
94
|
-
lineIndex = i;
|
|
95
|
-
}
|
|
96
|
-
else {
|
|
97
|
-
break;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
const line = lines[lineIndex];
|
|
101
|
-
let column = widthBetween(value, line.start, Math.min(pos, line.end));
|
|
102
|
-
// trailing cursor-cell overflows to the next row at the wrap column
|
|
103
|
-
if (column >= w) {
|
|
104
|
-
lineIndex++;
|
|
105
|
-
column = 0;
|
|
106
|
-
}
|
|
107
|
-
return { column, line: lineIndex };
|
|
108
|
-
}
|
|
109
|
-
export function offsetFromPosition(value, row, col, cols) {
|
|
110
|
-
if (!value.length) {
|
|
111
|
-
return 0;
|
|
112
|
-
}
|
|
113
|
-
const lines = visualLines(value, cols);
|
|
114
|
-
const target = lines[Math.max(0, Math.min(lines.length - 1, Math.floor(row)))];
|
|
115
|
-
const targetCol = Math.max(0, Math.floor(col));
|
|
116
|
-
let column = 0;
|
|
117
|
-
for (const part of graphemes(value.slice(target.start, target.end))) {
|
|
118
|
-
if (targetCol <= column + Math.max(0, part.width - 1)) {
|
|
119
|
-
return target.start + part.index;
|
|
120
|
-
}
|
|
121
|
-
column += part.width;
|
|
122
|
-
}
|
|
123
|
-
return target.end;
|
|
124
|
-
}
|
|
125
|
-
export function inputVisualHeight(value, columns) {
|
|
126
|
-
return cursorLayout(value, value.length, columns).line + 1;
|
|
127
|
-
}
|
|
128
|
-
export function composerPromptWidth(promptText) {
|
|
129
|
-
return Math.max(1, stringWidth(promptText)) + COMPOSER_PROMPT_GAP_WIDTH;
|
|
130
|
-
}
|
|
131
|
-
export function transcriptGutterWidth(role, userPrompt) {
|
|
132
|
-
return role === 'user' ? composerPromptWidth(userPrompt) : 3;
|
|
133
|
-
}
|
|
134
|
-
export function transcriptBodyWidth(totalCols, role, userPrompt) {
|
|
135
|
-
return Math.max(20, totalCols - transcriptGutterWidth(role, userPrompt) - 2);
|
|
136
|
-
}
|
|
137
|
-
export function stableComposerColumns(totalCols, promptWidth) {
|
|
138
|
-
// Physical render/wrap width. Always reserve outer composer padding and
|
|
139
|
-
// prompt prefix. Only reserve the transcript scrollbar gutter when the
|
|
140
|
-
// terminal is wide enough; on narrow panes, preserving input columns beats
|
|
141
|
-
// keeping gutters visually aligned.
|
|
142
|
-
return Math.max(1, totalCols - promptWidth - 2 - (totalCols - promptWidth >= 24 ? 2 : 0));
|
|
143
|
-
}
|
package/dist/lib/liveProgress.js
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
export const countPendingTodos = (todos) => todos.filter(todo => todo.status === 'in_progress' || todo.status === 'pending').length;
|
|
2
|
-
export const isTodoDone = (todos) => todos.length > 0 && todos.every(todo => todo.status === 'completed' || todo.status === 'cancelled');
|
|
3
|
-
export const isToolShelfMessage = (msg) => Boolean(msg?.kind === 'trail' && !msg.text && !msg.thinking?.trim() && msg.tools?.length);
|
|
4
|
-
export const canHoldToolShelf = (msg) => Boolean(msg?.kind === 'trail' && !msg.text && (msg.thinking?.trim() || msg.tools?.length));
|
|
5
|
-
export const mergeToolShelfInto = (target, source) => ({
|
|
6
|
-
...target,
|
|
7
|
-
tools: [...(target.tools ?? []), ...(source.tools ?? [])]
|
|
8
|
-
});
|
|
9
|
-
const isBarrierMessage = (msg) => {
|
|
10
|
-
if (!msg) {
|
|
11
|
-
return true;
|
|
12
|
-
}
|
|
13
|
-
// Assistant text, user input, intro/panel rows all terminate the shelf.
|
|
14
|
-
if (msg.kind === 'intro' || msg.kind === 'panel' || msg.kind === 'diff') {
|
|
15
|
-
return true;
|
|
16
|
-
}
|
|
17
|
-
if (msg.role && msg.role !== 'system') {
|
|
18
|
-
return true;
|
|
19
|
-
}
|
|
20
|
-
if (msg.text) {
|
|
21
|
-
return true;
|
|
22
|
-
}
|
|
23
|
-
return false;
|
|
24
|
-
};
|
|
25
|
-
const isToolCarryingTrail = (msg) => Boolean(msg?.kind === 'trail' && !msg.text && msg.tools?.length);
|
|
26
|
-
export const appendToolShelfMessage = (prev, msg) => {
|
|
27
|
-
if (!isToolShelfMessage(msg)) {
|
|
28
|
-
return [...prev, msg];
|
|
29
|
-
}
|
|
30
|
-
let fallbackHolder = null;
|
|
31
|
-
for (let index = prev.length - 1; index >= 0; index--) {
|
|
32
|
-
const candidate = prev[index];
|
|
33
|
-
if (isToolCarryingTrail(candidate)) {
|
|
34
|
-
const next = [...prev];
|
|
35
|
-
next[index] = mergeToolShelfInto(candidate, msg);
|
|
36
|
-
return next;
|
|
37
|
-
}
|
|
38
|
-
if (fallbackHolder === null && canHoldToolShelf(candidate)) {
|
|
39
|
-
fallbackHolder = index;
|
|
40
|
-
}
|
|
41
|
-
if (isBarrierMessage(candidate)) {
|
|
42
|
-
break;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
if (fallbackHolder !== null) {
|
|
46
|
-
const next = [...prev];
|
|
47
|
-
next[fallbackHolder] = mergeToolShelfInto(prev[fallbackHolder], msg);
|
|
48
|
-
return next;
|
|
49
|
-
}
|
|
50
|
-
return [...prev, msg];
|
|
51
|
-
};
|