@opencoven/coven-code 0.0.4 → 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.
- package/README.md +29 -130
- package/bin/coven-code +26 -0
- package/install.js +117 -0
- package/package.json +26 -23
- package/bin/coven-code-sdk.mjs +0 -12
- package/bin/coven-code.mjs +0 -19
- package/docs/CLI.md +0 -256
- package/docs/CONFIGURATION.md +0 -107
- package/docs/DEMO.md +0 -453
- package/docs/DEVELOPMENT.md +0 -104
- package/docs/DOGFOOD-PROTOCOL.md +0 -263
- package/docs/MCP-SKILLS-PLUGINS.md +0 -127
- package/docs/README.md +0 -39
- package/docs/RELEASE.md +0 -33
- package/docs/SDK.md +0 -107
- package/docs/superpowers/plans/2026-05-25-coven-code-panel-tui.md +0 -904
- package/docs/superpowers/plans/2026-05-25-coven-code-rebrand.md +0 -670
- package/docs/superpowers/specs/2026-05-25-coven-code-panel-tui-design.md +0 -235
- package/docs/superpowers/specs/2026-05-26-slash-first-tui-review.md +0 -63
- package/src/agent/fixture.mjs +0 -95
- package/src/agent/lane.mjs +0 -136
- package/src/cli/dispatch.mjs +0 -66
- package/src/cli/execute.mjs +0 -452
- package/src/cli/help.mjs +0 -58
- package/src/cli/interactive-core.mjs +0 -28
- package/src/cli/interactive-io.mjs +0 -101
- package/src/cli/interactive-slash.mjs +0 -184
- package/src/cli/notifications.mjs +0 -13
- package/src/cli/parse.mjs +0 -83
- package/src/cli/reasoning.mjs +0 -45
- package/src/cli/refs.mjs +0 -162
- package/src/cli/repl.mjs +0 -60
- package/src/cli/slash-commands.mjs +0 -375
- package/src/cli/stream-json.mjs +0 -225
- package/src/cli/tui-actions.mjs +0 -72
- package/src/cli/tui-blessed.mjs +0 -198
- package/src/cli/tui-keys.mjs +0 -80
- package/src/cli/tui-lane.mjs +0 -73
- package/src/cli/tui-render.mjs +0 -169
- package/src/cli/tui-submit.mjs +0 -82
- package/src/cli/tui.mjs +0 -174
- package/src/commands/agents.mjs +0 -53
- package/src/commands/config.mjs +0 -27
- package/src/commands/ide.mjs +0 -17
- package/src/commands/login.mjs +0 -84
- package/src/commands/mcp.mjs +0 -176
- package/src/commands/permissions-eval.mjs +0 -122
- package/src/commands/permissions-rules.mjs +0 -53
- package/src/commands/permissions-text.mjs +0 -112
- package/src/commands/permissions.mjs +0 -62
- package/src/commands/plugins.mjs +0 -86
- package/src/commands/review.mjs +0 -74
- package/src/commands/skill.mjs +0 -23
- package/src/commands/threads.mjs +0 -165
- package/src/commands/tools.mjs +0 -77
- package/src/commands/update.mjs +0 -31
- package/src/commands/usage.mjs +0 -34
- package/src/constants.mjs +0 -52
- package/src/main.mjs +0 -87
- package/src/mcp/discover.mjs +0 -154
- package/src/mcp/local.mjs +0 -55
- package/src/mcp/parsers.mjs +0 -46
- package/src/mcp/permissions.mjs +0 -52
- package/src/mcp/probe.mjs +0 -85
- package/src/mcp/registry.mjs +0 -96
- package/src/mcp/remote-oauth.mjs +0 -55
- package/src/mcp/remote-session.mjs +0 -54
- package/src/mcp/remote-sse.mjs +0 -82
- package/src/mcp/remote.mjs +0 -74
- package/src/plugins/api.mjs +0 -187
- package/src/plugins/configuration.mjs +0 -124
- package/src/plugins/discover.mjs +0 -84
- package/src/plugins/helpers.mjs +0 -187
- package/src/plugins/subsystems.mjs +0 -198
- package/src/plugins/validators.mjs +0 -142
- package/src/sdk-execute.mjs +0 -82
- package/src/sdk-install.mjs +0 -187
- package/src/sdk-settings.mjs +0 -88
- package/src/sdk.mjs +0 -163
- package/src/settings/load.mjs +0 -134
- package/src/settings/paths.mjs +0 -101
- package/src/skills/builtin/building-skills/SKILL.md +0 -20
- package/src/skills/discover.mjs +0 -95
- package/src/threads/store.mjs +0 -176
- package/src/tools/builtin/bash.mjs +0 -110
- package/src/tools/builtin/create-file.mjs +0 -66
- package/src/tools/builtin/edit-file.mjs +0 -76
- package/src/tools/builtin/finder.mjs +0 -73
- package/src/tools/builtin/glob.mjs +0 -74
- package/src/tools/builtin/grep.mjs +0 -82
- package/src/tools/builtin/index.mjs +0 -83
- package/src/tools/builtin/librarian.mjs +0 -97
- package/src/tools/builtin/look-at.mjs +0 -92
- package/src/tools/builtin/mcp.mjs +0 -51
- package/src/tools/builtin/mermaid.mjs +0 -59
- package/src/tools/builtin/oracle.mjs +0 -56
- package/src/tools/builtin/painter.mjs +0 -81
- package/src/tools/builtin/plugin-tool.mjs +0 -53
- package/src/tools/builtin/read-mcp-resource.mjs +0 -63
- package/src/tools/builtin/read-web-page.mjs +0 -72
- package/src/tools/builtin/read.mjs +0 -59
- package/src/tools/builtin/runtime-content.mjs +0 -31
- package/src/tools/builtin/runtime-decisions.mjs +0 -115
- package/src/tools/builtin/runtime.mjs +0 -85
- package/src/tools/builtin/task.mjs +0 -63
- package/src/tools/builtin/toolbox-tool.mjs +0 -57
- package/src/tools/builtin/undo-edit.mjs +0 -97
- package/src/tools/builtin/web-search.mjs +0 -128
- package/src/tools/toolbox.mjs +0 -273
- package/src/util/fs.mjs +0 -13
- package/src/util/glob.mjs +0 -46
- package/src/util/html.mjs +0 -21
- package/src/util/media.mjs +0 -13
- package/src/util/shell.mjs +0 -24
- package/src/util/table.mjs +0 -11
|
@@ -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
|
-
}
|
package/src/cli/reasoning.mjs
DELETED
|
@@ -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
|
-
}
|