@opencoven/coven-code 0.0.3 → 0.0.6

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 (115) hide show
  1. package/README.md +29 -130
  2. package/bin/coven-code +26 -0
  3. package/install.js +117 -0
  4. package/package.json +26 -23
  5. package/bin/coven-code-sdk.mjs +0 -12
  6. package/bin/coven-code.mjs +0 -19
  7. package/docs/CLI.md +0 -256
  8. package/docs/CONFIGURATION.md +0 -107
  9. package/docs/DEMO.md +0 -453
  10. package/docs/DEVELOPMENT.md +0 -104
  11. package/docs/DOGFOOD-PROTOCOL.md +0 -263
  12. package/docs/MCP-SKILLS-PLUGINS.md +0 -127
  13. package/docs/README.md +0 -39
  14. package/docs/RELEASE.md +0 -33
  15. package/docs/SDK.md +0 -107
  16. package/docs/superpowers/plans/2026-05-25-coven-code-panel-tui.md +0 -904
  17. package/docs/superpowers/plans/2026-05-25-coven-code-rebrand.md +0 -670
  18. package/docs/superpowers/specs/2026-05-25-coven-code-panel-tui-design.md +0 -235
  19. package/docs/superpowers/specs/2026-05-26-slash-first-tui-review.md +0 -63
  20. package/src/agent/fixture.mjs +0 -95
  21. package/src/agent/lane.mjs +0 -136
  22. package/src/cli/dispatch.mjs +0 -66
  23. package/src/cli/execute.mjs +0 -590
  24. package/src/cli/help.mjs +0 -58
  25. package/src/cli/interactive-core.mjs +0 -28
  26. package/src/cli/interactive-io.mjs +0 -101
  27. package/src/cli/interactive-slash.mjs +0 -184
  28. package/src/cli/notifications.mjs +0 -13
  29. package/src/cli/parse.mjs +0 -83
  30. package/src/cli/reasoning.mjs +0 -45
  31. package/src/cli/refs.mjs +0 -162
  32. package/src/cli/repl.mjs +0 -60
  33. package/src/cli/slash-commands.mjs +0 -375
  34. package/src/cli/stream-json.mjs +0 -116
  35. package/src/cli/tui-actions.mjs +0 -72
  36. package/src/cli/tui-blessed.mjs +0 -198
  37. package/src/cli/tui-keys.mjs +0 -80
  38. package/src/cli/tui-lane.mjs +0 -73
  39. package/src/cli/tui-render.mjs +0 -169
  40. package/src/cli/tui-submit.mjs +0 -82
  41. package/src/cli/tui.mjs +0 -174
  42. package/src/commands/agents.mjs +0 -53
  43. package/src/commands/config.mjs +0 -27
  44. package/src/commands/ide.mjs +0 -17
  45. package/src/commands/login.mjs +0 -84
  46. package/src/commands/mcp.mjs +0 -176
  47. package/src/commands/permissions-eval.mjs +0 -122
  48. package/src/commands/permissions-rules.mjs +0 -53
  49. package/src/commands/permissions-text.mjs +0 -112
  50. package/src/commands/permissions.mjs +0 -62
  51. package/src/commands/plugins.mjs +0 -86
  52. package/src/commands/review.mjs +0 -74
  53. package/src/commands/skill.mjs +0 -23
  54. package/src/commands/threads.mjs +0 -165
  55. package/src/commands/tools.mjs +0 -77
  56. package/src/commands/update.mjs +0 -31
  57. package/src/commands/usage.mjs +0 -34
  58. package/src/constants.mjs +0 -52
  59. package/src/main.mjs +0 -87
  60. package/src/mcp/discover.mjs +0 -154
  61. package/src/mcp/local.mjs +0 -55
  62. package/src/mcp/parsers.mjs +0 -46
  63. package/src/mcp/permissions.mjs +0 -52
  64. package/src/mcp/probe.mjs +0 -85
  65. package/src/mcp/registry.mjs +0 -96
  66. package/src/mcp/remote-oauth.mjs +0 -55
  67. package/src/mcp/remote-session.mjs +0 -54
  68. package/src/mcp/remote-sse.mjs +0 -82
  69. package/src/mcp/remote.mjs +0 -74
  70. package/src/plugins/api.mjs +0 -187
  71. package/src/plugins/configuration.mjs +0 -124
  72. package/src/plugins/discover.mjs +0 -84
  73. package/src/plugins/helpers.mjs +0 -187
  74. package/src/plugins/subsystems.mjs +0 -198
  75. package/src/plugins/validators.mjs +0 -142
  76. package/src/sdk-execute.mjs +0 -82
  77. package/src/sdk-install.mjs +0 -187
  78. package/src/sdk-settings.mjs +0 -88
  79. package/src/sdk.mjs +0 -163
  80. package/src/settings/load.mjs +0 -134
  81. package/src/settings/paths.mjs +0 -101
  82. package/src/skills/builtin/building-skills/SKILL.md +0 -20
  83. package/src/skills/discover.mjs +0 -95
  84. package/src/threads/store.mjs +0 -176
  85. package/src/tools/builtin/bash.mjs +0 -110
  86. package/src/tools/builtin/create-file.mjs +0 -66
  87. package/src/tools/builtin/edit-file.mjs +0 -76
  88. package/src/tools/builtin/finder.mjs +0 -73
  89. package/src/tools/builtin/glob.mjs +0 -74
  90. package/src/tools/builtin/grep.mjs +0 -82
  91. package/src/tools/builtin/index.mjs +0 -83
  92. package/src/tools/builtin/librarian.mjs +0 -97
  93. package/src/tools/builtin/look-at.mjs +0 -92
  94. package/src/tools/builtin/mcp.mjs +0 -51
  95. package/src/tools/builtin/mermaid.mjs +0 -59
  96. package/src/tools/builtin/oracle.mjs +0 -56
  97. package/src/tools/builtin/painter.mjs +0 -81
  98. package/src/tools/builtin/plugin-tool.mjs +0 -53
  99. package/src/tools/builtin/read-mcp-resource.mjs +0 -63
  100. package/src/tools/builtin/read-web-page.mjs +0 -72
  101. package/src/tools/builtin/read.mjs +0 -59
  102. package/src/tools/builtin/runtime-content.mjs +0 -31
  103. package/src/tools/builtin/runtime-decisions.mjs +0 -115
  104. package/src/tools/builtin/runtime.mjs +0 -85
  105. package/src/tools/builtin/task.mjs +0 -63
  106. package/src/tools/builtin/toolbox-tool.mjs +0 -57
  107. package/src/tools/builtin/undo-edit.mjs +0 -97
  108. package/src/tools/builtin/web-search.mjs +0 -128
  109. package/src/tools/toolbox.mjs +0 -273
  110. package/src/util/fs.mjs +0 -13
  111. package/src/util/glob.mjs +0 -46
  112. package/src/util/html.mjs +0 -21
  113. package/src/util/media.mjs +0 -13
  114. package/src/util/shell.mjs +0 -24
  115. package/src/util/table.mjs +0 -11
@@ -1,101 +0,0 @@
1
- import { readFileSync } from 'node:fs';
2
- import { spawnSync } from 'node:child_process';
3
- import { appendFile, mkdir, readFile, unlink, writeFile } from 'node:fs/promises';
4
- import { tmpdir } from 'node:os';
5
- import path from 'node:path';
6
- import { CLI_NAME, CONFIG_SUBDIR, REPL_HISTORY_LIMIT } from '../constants.mjs';
7
- import { configDir } from '../settings/paths.mjs';
8
- import { shellQuote } from '../util/shell.mjs';
9
- import { latestActiveThread, requireThread } from '../threads/store.mjs';
10
- import { runExecute } from './execute.mjs';
11
-
12
- export async function runInteractiveTurn(parsed, text, thread, executeRunner = runExecute, options = {}) {
13
- try {
14
- return await executeRunner(
15
- { ...parsed, execute: true, prompt: text, streamJson: false, streamJsonThinking: false, streamJsonInput: false },
16
- '',
17
- { thread, ...options },
18
- );
19
- } catch (error) {
20
- console.error(`${CLI_NAME}: ${error?.message ?? error}`);
21
- return thread;
22
- }
23
- }
24
-
25
- export async function submitPromptAndQueue(session, text) {
26
- session.thread = await runInteractiveTurn(session.parsed, text, session.thread, session.executeRunner, { silent: session.silent });
27
- while (session.queuedMessages.length > 0) {
28
- session.thread = await runInteractiveTurn(session.parsed, session.queuedMessages.shift(), session.thread, session.executeRunner, { silent: session.silent });
29
- }
30
- }
31
-
32
- export async function readEditorPrompt(initialText = '') {
33
- const editor = process.env.EDITOR || process.env.VISUAL;
34
- if (!editor) {
35
- console.error(`${CLI_NAME}: /editor requires $EDITOR or $VISUAL`);
36
- return '';
37
- }
38
- const file = path.join(tmpdir(), `${CONFIG_SUBDIR}-prompt-${process.pid}-${Date.now()}.md`);
39
- try {
40
- await writeFile(file, initialText);
41
- const result = spawnSync(`${editor} ${shellQuote(file)}`, {
42
- stdio: 'inherit',
43
- shell: true,
44
- });
45
- if (result.error) {
46
- console.error(`${CLI_NAME}: Unable to run editor: ${result.error.message}`);
47
- return '';
48
- }
49
- if ((result.status ?? 0) !== 0) {
50
- console.error(`${CLI_NAME}: Editor exited with status ${result.status}`);
51
- return '';
52
- }
53
- return (await readFile(file, 'utf8')).trim();
54
- } finally {
55
- await unlink(file).catch(() => {});
56
- }
57
- }
58
-
59
- export function editablePreviousPrompt(thread) {
60
- const index = (thread.messages ?? []).findLastIndex((message) => (
61
- message.role === 'user' && typeof message.content === 'string'
62
- ));
63
- if (index === -1) throw new Error('No previous user prompt to edit');
64
- return { index, prompt: thread.messages[index].content };
65
- }
66
-
67
- export function interactiveContinuationThread(threadId) {
68
- if (threadId) return requireThread(threadId);
69
- const thread = latestActiveThread();
70
- if (!thread) throw new Error('No active thread to continue');
71
- return thread;
72
- }
73
-
74
- export function replHistoryFile() {
75
- return process.env.COVEN_CODE_REPL_HISTORY_FILE
76
- || path.join(configDir(), CONFIG_SUBDIR, 'repl_history');
77
- }
78
-
79
- export function loadReplHistory() {
80
- if (process.env.COVEN_CODE_REPL_HISTORY === '0') return [];
81
- try {
82
- return readFileSync(replHistoryFile(), 'utf8')
83
- .split(/\r?\n/)
84
- .filter(Boolean)
85
- .slice(-REPL_HISTORY_LIMIT)
86
- .reverse();
87
- } catch {
88
- return [];
89
- }
90
- }
91
-
92
- export async function appendReplHistory(line) {
93
- if (process.env.COVEN_CODE_REPL_HISTORY === '0') return;
94
- try {
95
- const file = replHistoryFile();
96
- await mkdir(path.dirname(file), { recursive: true });
97
- await appendFile(file, `${line}\n`);
98
- } catch {
99
- // history must never break the REPL
100
- }
101
- }
@@ -1,184 +0,0 @@
1
- import { AGENT_MODES, CLI_NAME } from '../constants.mjs';
2
- import { splitShellWords } from '../util/shell.mjs';
3
- import {
4
- coerceReasoningEffortForMode,
5
- nextReasoningEffortForMode,
6
- reasoningEffortForMode,
7
- } from './reasoning.mjs';
8
- import {
9
- buildSlashCommandCatalog,
10
- findSlashCommand,
11
- formatSlashCommandDetails,
12
- formatSlashHelpLines,
13
- skillPromptFromSlashCommand,
14
- } from './slash-commands.mjs';
15
- import {
16
- editablePreviousPrompt,
17
- interactiveContinuationThread,
18
- submitPromptAndQueue,
19
- } from './interactive-io.mjs';
20
-
21
- export async function handleSlashCommand(session, text) {
22
- const tokens = splitShellWords(text.slice(1));
23
- const [cmd, ...rest] = tokens;
24
- const catalog = await safeSlashCommandCatalog(session);
25
- if (!cmd) return { kind: 'help', lines: formatCatalogHelpLines(catalog) };
26
- const catalogEntry = findSlashCommand(catalog, cmd);
27
-
28
- if (catalogEntry?.source === 'skill') {
29
- if (rest.length === 0) {
30
- return { kind: 'command', lines: formatSlashCommandDetails(catalogEntry) };
31
- }
32
- await submitPromptAndQueue(session, skillPromptFromSlashCommand(catalogEntry, rest.join(' ')));
33
- return { kind: 'turn', lines: [] };
34
- }
35
-
36
- if (catalogEntry?.source === 'plugin' && catalogEntry.availability?.type === 'disabled') {
37
- return {
38
- kind: 'error',
39
- lines: [`${CLI_NAME}: ${catalogEntry.availability.reason ?? `Plugin command disabled: ${catalogEntry.name}`}`],
40
- };
41
- }
42
-
43
- if (cmd === 'mode') {
44
- const nextMode = rest[0];
45
- if (!nextMode) return { kind: 'command', lines: [`mode: ${session.parsed.mode}`] };
46
- if (!AGENT_MODES.includes(nextMode)) return { kind: 'error', lines: [`${CLI_NAME}: Unknown mode: ${nextMode}`] };
47
- session.parsed.mode = nextMode;
48
- session.parsed.reasoningEffort = coerceReasoningEffortForMode(session.parsed.mode, session.parsed.reasoningEffort);
49
- return { kind: 'command', lines: [`mode: ${session.parsed.mode}`, `reasoning effort: ${session.parsed.reasoningEffort}`] };
50
- }
51
-
52
- if (cmd === 'reasoning') {
53
- try {
54
- const nextEffort = rest[0] === 'next'
55
- ? nextReasoningEffortForMode(session.parsed.mode, session.parsed.reasoningEffort)
56
- : rest[0];
57
- session.parsed.reasoningEffort = reasoningEffortForMode(session.parsed.mode, nextEffort ?? session.parsed.reasoningEffort);
58
- return { kind: 'command', lines: [`reasoning effort: ${session.parsed.reasoningEffort}`] };
59
- } catch (error) {
60
- return { kind: 'error', lines: [`${CLI_NAME}: ${error?.message ?? error}`] };
61
- }
62
- }
63
-
64
- if (cmd === 'queue') {
65
- const queued = rest.join(' ').trim();
66
- if (!queued) return { kind: 'error', lines: [`${CLI_NAME}: /queue requires a prompt`] };
67
- session.queuedMessages.push(queued);
68
- return { kind: 'command', lines: [`queued: ${queued}`] };
69
- }
70
-
71
- if (cmd === 'new') {
72
- session.thread = undefined;
73
- return { kind: 'command', lines: ['new thread'] };
74
- }
75
-
76
- if (cmd === 'continue') {
77
- try {
78
- session.thread = interactiveContinuationThread(rest[0]);
79
- return { kind: 'command', lines: [`continued: ${session.thread.id}`] };
80
- } catch (error) {
81
- return { kind: 'error', lines: [`${CLI_NAME}: ${error?.message ?? error}`] };
82
- }
83
- }
84
-
85
- if (cmd === `${CLI_NAME}:` && rest.join(' ') === 'help') return { kind: 'help', lines: await sessionSlashHelpLines(session) };
86
-
87
- if (cmd === 'editor') {
88
- const edited = await session.editorReader();
89
- if (edited) await submitPromptAndQueue(session, edited);
90
- return { kind: 'command', lines: [] };
91
- }
92
-
93
- if (cmd === 'edit') {
94
- try {
95
- if (!session.thread) throw new Error('No current thread to edit');
96
- const editTarget = editablePreviousPrompt(session.thread);
97
- const edited = await session.editorReader(editTarget.prompt);
98
- if (edited) {
99
- session.thread.messages = session.thread.messages.slice(0, editTarget.index);
100
- if (session.thread.messages.length === 0) {
101
- session.thread.title = edited.split(/\r?\n/).find(Boolean)?.slice(0, 120) || '(empty prompt)';
102
- }
103
- await submitPromptAndQueue(session, edited);
104
- }
105
- return { kind: 'command', lines: [] };
106
- } catch (error) {
107
- return { kind: 'error', lines: [`${CLI_NAME}: ${error?.message ?? error}`] };
108
- }
109
- }
110
-
111
- if (cmd === 'thread:' && rest.join(' ') === 'archive and quit') {
112
- try {
113
- if (!session.thread) throw new Error('No current thread to archive');
114
- await session.commandRunner('threads', ['archive', session.thread.id], session.parsed, '');
115
- return { kind: 'exit', lines: [] };
116
- } catch (error) {
117
- return { kind: 'error', lines: [`${CLI_NAME}: ${error?.message ?? error}`] };
118
- }
119
- }
120
-
121
- if (cmd === 'thread:' && rest[0] === 'set' && rest[1] === 'visibility') {
122
- try {
123
- if (!session.thread) throw new Error('No current thread to update');
124
- await session.commandRunner('threads', ['visibility', session.thread.id, ...rest.slice(2)], session.parsed, '');
125
- return { kind: 'command', lines: [] };
126
- } catch (error) {
127
- return { kind: 'error', lines: [`${CLI_NAME}: ${error?.message ?? error}`] };
128
- }
129
- }
130
-
131
- if (cmd === 'feedback:' && rest.join(' ') === 'send report with diagnostics') {
132
- try {
133
- if (!session.thread) throw new Error('No current thread to report');
134
- await session.commandRunner('threads', ['report', session.thread.id], session.parsed, '');
135
- return { kind: 'command', lines: [] };
136
- } catch (error) {
137
- return { kind: 'error', lines: [`${CLI_NAME}: ${error?.message ?? error}`] };
138
- }
139
- }
140
-
141
- try {
142
- if (cmd === 'skill:') await session.commandRunner('skill', rest, session.parsed, '');
143
- else if (cmd === 'plugins:') await session.commandRunner('plugins', rest, session.parsed, '');
144
- else await session.commandRunner(cmd, rest, session.parsed, '');
145
- return { kind: 'command', lines: [] };
146
- } catch (error) {
147
- if (String(error?.message ?? '').startsWith('Unknown command:')) {
148
- try {
149
- await session.commandRunner('plugins', ['run', cmd], session.parsed, '');
150
- return { kind: 'command', lines: [] };
151
- } catch (pluginError) {
152
- if (!String(pluginError?.message ?? '').startsWith('Unknown plugin command:')) throw pluginError;
153
- }
154
- }
155
- return { kind: 'error', lines: [`${CLI_NAME}: ${error?.message ?? error}`] };
156
- }
157
- }
158
-
159
- export function slashHelpLines() {
160
- return formatSlashHelpLines();
161
- }
162
-
163
- export function printSlashHelp() {
164
- for (const line of slashHelpLines()) console.log(line);
165
- }
166
-
167
- export async function safeSlashCommandCatalog(session) {
168
- try {
169
- return await buildSlashCommandCatalog({
170
- parsed: session.parsed,
171
- cwd: session.cwd ?? process.cwd(),
172
- });
173
- } catch {
174
- return [];
175
- }
176
- }
177
-
178
- export async function sessionSlashHelpLines(session) {
179
- return formatCatalogHelpLines(await safeSlashCommandCatalog(session));
180
- }
181
-
182
- function formatCatalogHelpLines(catalog) {
183
- return catalog.length > 0 ? formatSlashHelpLines(catalog) : slashHelpLines();
184
- }
@@ -1,13 +0,0 @@
1
- import { readEffectiveSettings } from '../settings/load.mjs';
2
-
3
- export function notifyAgentComplete(parsed = {}) {
4
- if (readEffectiveSettings(parsed)['covenCode.notifications.enabled'] === false) return;
5
- if (!shouldUseTerminalBell()) return;
6
- process.stderr.write('\x07');
7
- }
8
-
9
- function shouldUseTerminalBell() {
10
- return process.env.COVEN_CODE_FORCE_BEL === '1'
11
- || process.env.COVEN_CODE_FORCE_BEL === 'true'
12
- || Boolean(process.env.SSH_TTY || process.env.SSH_CONNECTION);
13
- }
package/src/cli/parse.mjs DELETED
@@ -1,83 +0,0 @@
1
- export class UsageError extends Error {}
2
-
3
- export function parseGlobalArgs(args) {
4
- const parsed = {
5
- execute: false,
6
- prompt: '',
7
- streamJson: false,
8
- streamJsonThinking: false,
9
- streamJsonInput: false,
10
- dangerouslyAllowAll: false,
11
- help: false,
12
- version: false,
13
- mode: 'smart',
14
- reasoningEffort: undefined,
15
- mcpConfig: undefined,
16
- settingsFile: undefined,
17
- labels: [],
18
- visibility: undefined,
19
- archive: false,
20
- continueThread: false,
21
- toolbox: undefined,
22
- skills: undefined,
23
- ide: undefined,
24
- positionals: [],
25
- };
26
-
27
- for (let index = 0; index < args.length; index += 1) {
28
- const arg = args[index];
29
- if (arg === '--help' || arg === '-h') parsed.help = true;
30
- else if (arg === '--version' || arg === '-v') parsed.version = true;
31
- else if (arg === '--execute' || arg === '-x') {
32
- parsed.execute = true;
33
- const next = args[index + 1];
34
- if (next && !next.startsWith('-')) {
35
- parsed.prompt = next;
36
- index += 1;
37
- }
38
- } else if (arg === '--stream-json') parsed.streamJson = true;
39
- else if (arg === '--stream-json-thinking') {
40
- parsed.streamJson = true;
41
- parsed.streamJsonThinking = true;
42
- } else if (arg === '--stream-json-input') parsed.streamJsonInput = true;
43
- else if (arg === '--dangerously-allow-all') parsed.dangerouslyAllowAll = true;
44
- else if (arg === '--mcp-config') {
45
- parsed.mcpConfig = args[index + 1] ?? '';
46
- index += 1;
47
- } else if (arg === '--settings-file') {
48
- parsed.settingsFile = args[index + 1] ?? '';
49
- index += 1;
50
- } else if (arg === '--label') {
51
- parsed.labels.push(args[index + 1] ?? '');
52
- index += 1;
53
- } else if (arg === '--visibility') {
54
- parsed.visibility = args[index + 1] ?? '';
55
- index += 1;
56
- } else if (arg === '--archive') {
57
- parsed.archive = true;
58
- } else if (arg === '--continue') {
59
- const next = args[index + 1];
60
- if (next && /^T-[A-Za-z0-9-]+$/.test(next)) {
61
- parsed.continueThread = next;
62
- index += 1;
63
- } else {
64
- parsed.continueThread = true;
65
- }
66
- } else if (arg === '--toolbox') {
67
- parsed.toolbox = args[index + 1] ?? '';
68
- index += 1;
69
- } else if (arg === '--skills') {
70
- parsed.skills = args[index + 1] ?? '';
71
- index += 1;
72
- } else if (arg === '--mode') {
73
- parsed.mode = args[index + 1] ?? parsed.mode;
74
- index += 1;
75
- } else if (arg === '--reasoning-effort') {
76
- parsed.reasoningEffort = args[index + 1] ?? '';
77
- index += 1;
78
- } else if (arg === '--jetbrains') parsed.ide = 'jetbrains';
79
- else parsed.positionals.push(arg);
80
- }
81
-
82
- return parsed;
83
- }
@@ -1,45 +0,0 @@
1
- import { UsageError } from './parse.mjs';
2
-
3
- const DEFAULT_REASONING_EFFORT_BY_MODE = {
4
- smart: 'high',
5
- deep: 'high',
6
- rush: 'minimal',
7
- large: 'high',
8
- };
9
-
10
- const REASONING_EFFORTS_BY_MODE = {
11
- smart: ['high', 'xhigh', 'max'],
12
- deep: ['high', 'low', 'medium', 'xhigh'],
13
- rush: ['minimal'],
14
- large: ['high', 'xhigh', 'max'],
15
- };
16
-
17
- export function reasoningEffortForMode(mode, requested) {
18
- const efforts = reasoningEffortsForMode(mode);
19
- if (requested === undefined || requested === null || requested === '') return defaultReasoningEffortForMode(mode);
20
- if (efforts.includes(requested)) return requested;
21
- throw new UsageError(`reasoning effort for ${mode} must be one of: ${efforts.join(', ')}`);
22
- }
23
-
24
- export function coerceReasoningEffortForMode(mode, requested) {
25
- try {
26
- return reasoningEffortForMode(mode, requested);
27
- } catch {
28
- return defaultReasoningEffortForMode(mode);
29
- }
30
- }
31
-
32
- export function nextReasoningEffortForMode(mode, requested) {
33
- const efforts = reasoningEffortsForMode(mode);
34
- const current = coerceReasoningEffortForMode(mode, requested);
35
- const index = efforts.indexOf(current);
36
- return efforts[(index + 1) % efforts.length];
37
- }
38
-
39
- function defaultReasoningEffortForMode(mode) {
40
- return DEFAULT_REASONING_EFFORT_BY_MODE[mode] ?? DEFAULT_REASONING_EFFORT_BY_MODE.smart;
41
- }
42
-
43
- function reasoningEffortsForMode(mode) {
44
- return REASONING_EFFORTS_BY_MODE[mode] ?? REASONING_EFFORTS_BY_MODE.smart;
45
- }
package/src/cli/refs.mjs DELETED
@@ -1,162 +0,0 @@
1
- import { existsSync, readFileSync, statSync } from 'node:fs';
2
- import os from 'node:os';
3
- import path from 'node:path';
4
- import { FILE_MENTION_MAX_LINES, FILE_MENTION_MAX_LINE_LENGTH, THREAD_URL_BASE } from '../constants.mjs';
5
- import { globToRegex, hasGlob, walkFiles } from '../util/glob.mjs';
6
- import { listThreads, readThread, threadSearchText } from '../threads/store.mjs';
7
- import { readEffectiveSettings } from '../settings/load.mjs';
8
-
9
- export function expandFileReferences(prompt, options = {}) {
10
- return prompt.replace(/@(?!(?:T-|@))((?:~|\/|\.{1,2}\/)?[A-Za-z0-9_./*?-]*[A-Za-z0-9_*-])/g, (match, rawPath) => {
11
- const blocks = mentionedFiles(rawPath, options)
12
- .map((filePath) => fileMentionBlock(filePath, options))
13
- .filter(Boolean);
14
- if (blocks.length === 0) return match;
15
- return blocks.join('\n');
16
- });
17
- }
18
-
19
- function fileMentionBlock(filePath, options = {}) {
20
- const image = imageMentionBlock(filePath);
21
- if (image) return image;
22
- const content = readMentionedTextFile(filePath);
23
- if (content === undefined) return undefined;
24
- if (options.includeFile && !options.includeFile(filePath, content)) return undefined;
25
- return `[file:${filePath}]\n${content}\n[/file]`;
26
- }
27
-
28
- export function imageMentionBlock(filePath) {
29
- const mediaType = imageMediaType(filePath);
30
- if (!mediaType) return undefined;
31
- const bytes = readFileSync(filePath).byteLength;
32
- return `[image:${filePath}]\nmedia_type: ${mediaType}\nbytes: ${bytes}\n[/image]`;
33
- }
34
-
35
- function imageMediaType(filePath) {
36
- const ext = path.extname(filePath).toLowerCase();
37
- if (ext === '.png') return 'image/png';
38
- if (ext === '.jpg' || ext === '.jpeg') return 'image/jpeg';
39
- if (ext === '.gif') return 'image/gif';
40
- if (ext === '.webp') return 'image/webp';
41
- return undefined;
42
- }
43
-
44
- function readMentionedTextFile(filePath) {
45
- const raw = readFileSync(filePath);
46
- if (raw.includes(0)) return undefined;
47
- const text = raw.toString('utf8');
48
- return text
49
- .split(/\r?\n/)
50
- .slice(0, FILE_MENTION_MAX_LINES)
51
- .map((line) => line.slice(0, FILE_MENTION_MAX_LINE_LENGTH))
52
- .join('\n');
53
- }
54
-
55
- function mentionedFiles(rawPath, options = {}) {
56
- if (!hasGlob(rawPath)) {
57
- const filePath = resolveMentionedPath(rawPath, options);
58
- return existsSync(filePath) && statSync(filePath).isFile() ? [filePath] : [];
59
- }
60
- const root = globSearchRoot(rawPath, options);
61
- const pattern = rawPath.startsWith('~/')
62
- ? path.join(os.homedir(), rawPath.slice(2))
63
- : path.resolve(options.baseDir ?? process.cwd(), rawPath);
64
- const re = globToRegex(path.normalize(pattern));
65
- const ignored = gitignoredFileMatcher(options.baseDir ?? process.cwd());
66
- const alwaysIncluded = alwaysIncludedFileMatcher(options.baseDir ?? process.cwd(), options.parsed);
67
- return walkFiles(root)
68
- .filter((filePath) => re.test(path.normalize(filePath)))
69
- .filter((filePath) => !ignored(filePath) || alwaysIncluded(filePath))
70
- .sort((a, b) => a.localeCompare(b));
71
- }
72
-
73
- function resolveMentionedPath(rawPath, options = {}) {
74
- if (rawPath.startsWith('~/')) return path.join(os.homedir(), rawPath.slice(2));
75
- if (path.isAbsolute(rawPath)) return rawPath;
76
- return path.resolve(options.baseDir ?? process.cwd(), rawPath);
77
- }
78
-
79
- function globSearchRoot(rawPath, options = {}) {
80
- const pattern = resolveMentionedPath(rawPath, options);
81
- const parts = pattern.split(path.sep);
82
- const stableParts = [];
83
- for (const part of parts) {
84
- if (hasGlob(part)) break;
85
- stableParts.push(part);
86
- }
87
- const root = stableParts.join(path.sep) || path.sep;
88
- return existsSync(root) && statSync(root).isDirectory() ? root : path.dirname(root);
89
- }
90
-
91
- function gitignoredFileMatcher(baseDir) {
92
- const gitignorePath = findUp('.gitignore', baseDir);
93
- if (!gitignorePath) return () => false;
94
- const root = path.dirname(gitignorePath);
95
- const patterns = readFileSync(gitignorePath, 'utf8')
96
- .split(/\r?\n/)
97
- .map((line) => line.trim())
98
- .filter((line) => line && !line.startsWith('#') && !line.startsWith('!'))
99
- .map((line) => gitignorePatternRegex(root, line));
100
- return (filePath) => patterns.some((pattern) => pattern.test(path.normalize(filePath)));
101
- }
102
-
103
- function alwaysIncludedFileMatcher(baseDir, parsed = {}) {
104
- const settings = readEffectiveSettings(parsed);
105
- const patterns = Array.isArray(settings['covenCode.fuzzy.alwaysIncludePaths'])
106
- ? settings['covenCode.fuzzy.alwaysIncludePaths']
107
- : [];
108
- const root = path.dirname(findUp('.gitignore', baseDir) ?? path.join(path.resolve(baseDir), '.gitignore'));
109
- const regexes = patterns
110
- .filter((pattern) => typeof pattern === 'string' && pattern.trim())
111
- .map((pattern) => globToRegex(path.normalize(path.resolve(root, pattern))));
112
- return (filePath) => regexes.some((pattern) => pattern.test(path.normalize(filePath)));
113
- }
114
-
115
- function gitignorePatternRegex(root, pattern) {
116
- const directoryOnly = pattern.endsWith('/');
117
- const cleanPattern = pattern.replace(/\/+$/, '');
118
- const absolutePattern = path.resolve(root, cleanPattern);
119
- const source = directoryOnly
120
- ? `${globToRegex(path.normalize(absolutePattern)).source.slice(1, -1)}(?:${escapePathSep()}.*)?`
121
- : globToRegex(path.normalize(absolutePattern)).source.slice(1, -1);
122
- return new RegExp(`^${source}$`);
123
- }
124
-
125
- function escapePathSep() {
126
- return path.sep === '\\' ? '\\\\' : '/';
127
- }
128
-
129
- function findUp(name, cwd) {
130
- let current = path.resolve(cwd);
131
- while (true) {
132
- const candidate = path.join(current, name);
133
- if (existsSync(candidate)) return candidate;
134
- const parent = path.dirname(current);
135
- if (parent === current || current === os.homedir()) return undefined;
136
- current = parent;
137
- }
138
- }
139
-
140
- export function expandThreadReferences(prompt) {
141
- const threadUrlPattern = THREAD_URL_BASE.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
142
- const explicitReferences = prompt.replace(new RegExp(`(?:@|${threadUrlPattern}/)(T-[A-Za-z0-9-]+)`, 'g'), (match, threadId) => {
143
- const thread = readThread(threadId);
144
- if (!thread) return match;
145
- return threadMentionBlock(thread);
146
- });
147
- return explicitReferences.replace(/@@([A-Za-z0-9_.:/-]+)/g, (match, rawQuery) => {
148
- const thread = findMentionedThread(rawQuery);
149
- return thread ? threadMentionBlock(thread) : match;
150
- });
151
- }
152
-
153
- function findMentionedThread(rawQuery) {
154
- const query = String(rawQuery ?? '').trim().toLowerCase();
155
- if (!query) return undefined;
156
- return listThreads()
157
- .filter((thread) => threadSearchText(thread).toLowerCase().includes(query))[0];
158
- }
159
-
160
- function threadMentionBlock(thread) {
161
- return `[thread:${thread.id}]\n${thread.messages.map((message) => `${message.role}: ${message.content}`).join('\n')}\n[/thread]`;
162
- }
package/src/cli/repl.mjs DELETED
@@ -1,60 +0,0 @@
1
- import * as readline from 'node:readline/promises';
2
- import { CLI_NAME, REPL_HISTORY_LIMIT, VERSION } from '../constants.mjs';
3
- import {
4
- createInteractiveSession,
5
- handleInteractiveInput,
6
- } from './interactive-core.mjs';
7
- import { appendReplHistory, loadReplHistory } from './interactive-io.mjs';
8
-
9
- export async function runInteractive(parsed, initialInput = '') {
10
- const rl = readline.createInterface({
11
- input: process.stdin,
12
- output: process.stdout,
13
- terminal: true,
14
- historySize: REPL_HISTORY_LIMIT,
15
- removeHistoryDuplicates: true,
16
- history: loadReplHistory(),
17
- });
18
- const session = createInteractiveSession(parsed);
19
- let buffer = [];
20
- console.log(`${CLI_NAME} ${VERSION} — interactive mode. Type /exit or press Ctrl-D to quit, /help for slash commands.`);
21
- if (initialInput.trim()) {
22
- await handleInteractiveInput(session, initialInput.trim());
23
- if (!process.stdin.isTTY) {
24
- rl.close();
25
- return;
26
- }
27
- }
28
- rl.setPrompt('> ');
29
- rl.prompt();
30
- try {
31
- for await (const line of rl) {
32
- if (line.trim()) await appendReplHistory(line);
33
- if (line.endsWith('\\')) {
34
- buffer.push(line.slice(0, -1));
35
- rl.setPrompt('… ');
36
- rl.prompt();
37
- continue;
38
- }
39
- buffer.push(line);
40
- const text = buffer.join('\n').trim();
41
- buffer = [];
42
- rl.setPrompt('> ');
43
- if (!text) {
44
- rl.prompt();
45
- continue;
46
- }
47
- const result = await handleInteractiveInput(session, text);
48
- if (result.lines.length > 0) {
49
- for (const outputLine of result.lines) {
50
- if (result.kind === 'error') console.error(outputLine);
51
- else console.log(outputLine);
52
- }
53
- }
54
- if (result.kind === 'exit') break;
55
- rl.prompt();
56
- }
57
- } finally {
58
- rl.close();
59
- }
60
- }