@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,375 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from 'node:fs';
|
|
2
|
-
import { BUILTIN_TOOLS, CLI_NAME } from '../constants.mjs';
|
|
3
|
-
import { listSkills } from '../skills/discover.mjs';
|
|
4
|
-
import { loadPlugins } from '../plugins/discover.mjs';
|
|
5
|
-
|
|
6
|
-
const STATIC_COMMANDS = [
|
|
7
|
-
{
|
|
8
|
-
name: 'help',
|
|
9
|
-
command: '/help',
|
|
10
|
-
title: 'Help',
|
|
11
|
-
category: 'session',
|
|
12
|
-
source: 'built-in',
|
|
13
|
-
usage: '/help',
|
|
14
|
-
description: 'Show slash commands and keybindings',
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
name: 'mode',
|
|
18
|
-
command: '/mode',
|
|
19
|
-
title: 'Mode',
|
|
20
|
-
category: 'session',
|
|
21
|
-
source: 'built-in',
|
|
22
|
-
usage: '/mode [name]',
|
|
23
|
-
description: 'show or set mode: smart, deep, rush, large',
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
name: 'reasoning',
|
|
27
|
-
command: '/reasoning',
|
|
28
|
-
title: 'Reasoning effort',
|
|
29
|
-
category: 'session',
|
|
30
|
-
source: 'built-in',
|
|
31
|
-
usage: '/reasoning [level|next]',
|
|
32
|
-
description: 'Show, set, or cycle reasoning effort',
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
name: 'new',
|
|
36
|
-
command: '/new',
|
|
37
|
-
title: 'New thread',
|
|
38
|
-
category: 'thread',
|
|
39
|
-
source: 'built-in',
|
|
40
|
-
usage: '/new',
|
|
41
|
-
description: 'Start a fresh thread',
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
name: 'continue',
|
|
45
|
-
command: '/continue',
|
|
46
|
-
title: 'Continue thread',
|
|
47
|
-
category: 'thread',
|
|
48
|
-
source: 'built-in',
|
|
49
|
-
usage: '/continue [thread-id]',
|
|
50
|
-
description: 'Continue the latest active thread or a specific thread',
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
name: 'queue',
|
|
54
|
-
command: '/queue',
|
|
55
|
-
title: 'Queue prompt',
|
|
56
|
-
category: 'thread',
|
|
57
|
-
source: 'built-in',
|
|
58
|
-
usage: '/queue <prompt>',
|
|
59
|
-
description: 'Send a follow-up prompt after the next turn',
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
name: 'lane',
|
|
63
|
-
command: '/lane',
|
|
64
|
-
title: 'Lane panel',
|
|
65
|
-
category: 'lane',
|
|
66
|
-
source: 'built-in',
|
|
67
|
-
usage: '/lane refresh | /lane verify | /lane status | /lane diff | /lane harness <name|next>',
|
|
68
|
-
description: 'TUI lane actions for worktree, harness, diff, and verification state',
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
name: 'editor',
|
|
72
|
-
command: '/editor',
|
|
73
|
-
title: 'Open editor',
|
|
74
|
-
category: 'composer',
|
|
75
|
-
source: 'built-in',
|
|
76
|
-
usage: '/editor',
|
|
77
|
-
aliases: ['Ctrl+G'],
|
|
78
|
-
description: 'compose the next prompt in $EDITOR',
|
|
79
|
-
},
|
|
80
|
-
{
|
|
81
|
-
name: 'edit',
|
|
82
|
-
command: '/edit',
|
|
83
|
-
title: 'Edit previous prompt',
|
|
84
|
-
category: 'composer',
|
|
85
|
-
source: 'built-in',
|
|
86
|
-
usage: '/edit',
|
|
87
|
-
description: 'Edit the previous prompt in $EDITOR',
|
|
88
|
-
},
|
|
89
|
-
{
|
|
90
|
-
name: 'exit',
|
|
91
|
-
command: '/exit',
|
|
92
|
-
title: 'Exit',
|
|
93
|
-
category: 'session',
|
|
94
|
-
source: 'built-in',
|
|
95
|
-
usage: '/exit',
|
|
96
|
-
aliases: ['/quit'],
|
|
97
|
-
description: 'Leave the interactive session',
|
|
98
|
-
},
|
|
99
|
-
{
|
|
100
|
-
name: `${CLI_NAME}:`,
|
|
101
|
-
command: `/${CLI_NAME}: help`,
|
|
102
|
-
title: 'Command palette help',
|
|
103
|
-
category: 'session',
|
|
104
|
-
source: 'built-in',
|
|
105
|
-
usage: `/${CLI_NAME}: help`,
|
|
106
|
-
description: 'Show command-palette help',
|
|
107
|
-
},
|
|
108
|
-
{
|
|
109
|
-
name: 'thread:',
|
|
110
|
-
command: '/thread:',
|
|
111
|
-
title: 'Thread actions',
|
|
112
|
-
category: 'thread',
|
|
113
|
-
source: 'built-in',
|
|
114
|
-
usage: '/thread: archive and quit | /thread: set visibility <level>',
|
|
115
|
-
description: 'Archive or update the current thread',
|
|
116
|
-
},
|
|
117
|
-
{
|
|
118
|
-
name: 'feedback:',
|
|
119
|
-
command: '/feedback:',
|
|
120
|
-
title: 'Feedback report',
|
|
121
|
-
category: 'diagnostics',
|
|
122
|
-
source: 'built-in',
|
|
123
|
-
usage: '/feedback: send report with diagnostics',
|
|
124
|
-
description: 'Create a diagnostic report for the current thread',
|
|
125
|
-
},
|
|
126
|
-
];
|
|
127
|
-
|
|
128
|
-
const TOP_LEVEL_COMMANDS = [
|
|
129
|
-
['tools', 'Tools', 'Inspect and manage built-in and toolbox tools', '/tools [list|show|make|use]'],
|
|
130
|
-
['skill:', 'Skills', 'List or inspect installed skills', '/skill: list | /skill: show <name>'],
|
|
131
|
-
['plugins:', 'Plugins', 'List, reload, or inspect plugin commands', '/plugins: list | /plugins: reload | /plugins: commands'],
|
|
132
|
-
['threads', 'Threads', 'List, show, search, archive, or map local threads', '/threads [list|show|search|archive|visibility|map|report]'],
|
|
133
|
-
['permissions', 'Permissions', 'Inspect or test permission policy rules', '/permissions list'],
|
|
134
|
-
['config', 'Config', 'Open user or workspace settings', '/config edit [--workspace]'],
|
|
135
|
-
['mcp', 'MCP', 'Manage MCP server settings', '/mcp [add|list|doctor|approve|oauth]'],
|
|
136
|
-
['ide', 'IDE', 'Connect or inspect local IDE integration', '/ide connect'],
|
|
137
|
-
['agents', 'Agents guidance', 'Show AGENTS.md guidance files used for this cwd', '/agents list'],
|
|
138
|
-
['review', 'Review', 'Run configured local review checks', '/review'],
|
|
139
|
-
['usage', 'Usage', 'Show local usage estimates', '/usage'],
|
|
140
|
-
['login', 'Login', 'Print local login instructions', '/login'],
|
|
141
|
-
['update', 'Update', 'Check for updates', '/update'],
|
|
142
|
-
];
|
|
143
|
-
|
|
144
|
-
export function buildStaticSlashCommandCatalog() {
|
|
145
|
-
return normalizeCatalog([
|
|
146
|
-
...STATIC_COMMANDS,
|
|
147
|
-
...TOP_LEVEL_COMMANDS.map(([name, title, description, usage]) => ({
|
|
148
|
-
name,
|
|
149
|
-
command: `/${name}`,
|
|
150
|
-
title,
|
|
151
|
-
category: 'command',
|
|
152
|
-
source: 'top-level',
|
|
153
|
-
usage,
|
|
154
|
-
description,
|
|
155
|
-
})),
|
|
156
|
-
]);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
export async function buildSlashCommandCatalog(options = {}) {
|
|
160
|
-
const restoreEnv = applyTemporaryEnv(options.env);
|
|
161
|
-
try {
|
|
162
|
-
const parsed = options.parsed ?? {};
|
|
163
|
-
const cwd = options.cwd ?? process.cwd();
|
|
164
|
-
const entries = [
|
|
165
|
-
...buildStaticSlashCommandCatalog(),
|
|
166
|
-
...safeSkillSlashCommands(parsed, cwd),
|
|
167
|
-
...await safePluginSlashCommands(cwd),
|
|
168
|
-
];
|
|
169
|
-
return normalizeCatalog(entries);
|
|
170
|
-
} finally {
|
|
171
|
-
restoreEnv();
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
export function filterSlashCommands(catalog = [], input = '') {
|
|
176
|
-
const query = slashQuery(input);
|
|
177
|
-
const entries = query
|
|
178
|
-
? catalog.filter((entry) => slashSearchText(entry).includes(query))
|
|
179
|
-
: catalog;
|
|
180
|
-
return entries
|
|
181
|
-
.map((entry, index) => ({ entry, score: slashScore(entry, query, index) }))
|
|
182
|
-
.sort((a, b) => a.score - b.score)
|
|
183
|
-
.map(({ entry }) => entry);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
export function formatSlashCommandDetails(entry) {
|
|
187
|
-
if (!entry) return ['No command selected.'];
|
|
188
|
-
const lines = [
|
|
189
|
-
`${entry.command} ${entry.title}`,
|
|
190
|
-
`Category: ${entry.category}`,
|
|
191
|
-
`Source: ${entry.source}`,
|
|
192
|
-
`Usage: ${entry.usage ?? entry.command}`,
|
|
193
|
-
`Status: ${formatAvailability(entry.availability)}`,
|
|
194
|
-
];
|
|
195
|
-
if (entry.aliases?.length) lines.push(`Aliases: ${entry.aliases.join(', ')}`);
|
|
196
|
-
if (entry.filePath) lines.push(`File: ${entry.filePath}`);
|
|
197
|
-
if (entry.description) lines.push('', entry.description);
|
|
198
|
-
return lines;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
export function formatSlashHelpLines(catalog = buildStaticSlashCommandCatalog()) {
|
|
202
|
-
const visible = catalog.filter((entry) => entry.availability?.type !== 'hidden');
|
|
203
|
-
const commandLines = visible.map((entry) => {
|
|
204
|
-
const usage = entry.usage ?? entry.command;
|
|
205
|
-
const status = entry.availability?.type === 'disabled' ? ` (${entry.availability.reason})` : '';
|
|
206
|
-
return ` ${usage.padEnd(28)} ${entry.description}${status}`;
|
|
207
|
-
});
|
|
208
|
-
return [
|
|
209
|
-
'Slash commands:',
|
|
210
|
-
...commandLines,
|
|
211
|
-
'End a line with `\\` to continue the prompt onto the next line.',
|
|
212
|
-
'Anything else is sent as a one-turn prompt.',
|
|
213
|
-
'',
|
|
214
|
-
'Keybindings:',
|
|
215
|
-
' / open slash commands',
|
|
216
|
-
' Tab complete the selected slash command',
|
|
217
|
-
' Up/Down move through slash commands or previous messages',
|
|
218
|
-
' Esc close overlays',
|
|
219
|
-
' Ctrl+P open the command palette',
|
|
220
|
-
' Ctrl+N start a fresh thread',
|
|
221
|
-
' Ctrl+M switch agent modes',
|
|
222
|
-
' Ctrl+R search prompt history',
|
|
223
|
-
' Ctrl+G open the current prompt in $EDITOR',
|
|
224
|
-
' Alt+D cycle reasoning effort for the active mode',
|
|
225
|
-
' @ mention files',
|
|
226
|
-
];
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
export function findSlashCommand(catalog = [], name) {
|
|
230
|
-
const normalized = slashQuery(name);
|
|
231
|
-
return catalog.find((entry) => slashQuery(entry.name) === normalized);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
function skillSlashCommands(parsed, cwd) {
|
|
235
|
-
return listSkills({ parsed, cwd }).map((skill) => ({
|
|
236
|
-
name: skill.name,
|
|
237
|
-
command: `/${skill.name}`,
|
|
238
|
-
title: skill.name,
|
|
239
|
-
category: 'skill',
|
|
240
|
-
source: 'skill',
|
|
241
|
-
usage: `/${skill.name} [prompt]`,
|
|
242
|
-
description: skill.description || 'Skill command',
|
|
243
|
-
filePath: skill.filePath,
|
|
244
|
-
skill,
|
|
245
|
-
}));
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
async function pluginSlashCommands(cwd) {
|
|
249
|
-
const runtime = await loadPlugins(cwd);
|
|
250
|
-
return runtime.commands
|
|
251
|
-
.filter((command) => command.availability.type !== 'hidden')
|
|
252
|
-
.map((command) => ({
|
|
253
|
-
name: command.name,
|
|
254
|
-
command: `/${command.name}`,
|
|
255
|
-
title: command.metadata.title ?? command.name,
|
|
256
|
-
category: command.metadata.category ?? 'plugin',
|
|
257
|
-
source: 'plugin',
|
|
258
|
-
usage: `/${command.name}`,
|
|
259
|
-
description: command.metadata.description ?? command.metadata.title ?? command.name,
|
|
260
|
-
availability: command.availability,
|
|
261
|
-
pluginCommand: command,
|
|
262
|
-
}));
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
function safeSkillSlashCommands(parsed, cwd) {
|
|
266
|
-
try {
|
|
267
|
-
return skillSlashCommands(parsed, cwd);
|
|
268
|
-
} catch (error) {
|
|
269
|
-
console.error(`${CLI_NAME}: skill catalog unavailable: ${error?.message ?? error}`);
|
|
270
|
-
return [];
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
async function safePluginSlashCommands(cwd) {
|
|
275
|
-
try {
|
|
276
|
-
return await pluginSlashCommands(cwd);
|
|
277
|
-
} catch (error) {
|
|
278
|
-
console.error(`${CLI_NAME}: plugin catalog unavailable: ${error?.message ?? error}`);
|
|
279
|
-
return [];
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
function normalizeCatalog(entries) {
|
|
284
|
-
const seen = new Set();
|
|
285
|
-
const catalog = [];
|
|
286
|
-
for (const entry of entries) {
|
|
287
|
-
const normalized = {
|
|
288
|
-
availability: { type: 'enabled' },
|
|
289
|
-
aliases: [],
|
|
290
|
-
...entry,
|
|
291
|
-
};
|
|
292
|
-
const key = slashQuery(normalized.name);
|
|
293
|
-
if (!key || seen.has(key)) continue;
|
|
294
|
-
seen.add(key);
|
|
295
|
-
catalog.push(normalized);
|
|
296
|
-
}
|
|
297
|
-
return catalog;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
function slashQuery(input = '') {
|
|
301
|
-
return String(input)
|
|
302
|
-
.trim()
|
|
303
|
-
.replace(/^\/+/, '')
|
|
304
|
-
.toLowerCase();
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
function slashSearchText(entry) {
|
|
308
|
-
return [
|
|
309
|
-
entry.name,
|
|
310
|
-
entry.command,
|
|
311
|
-
entry.title,
|
|
312
|
-
entry.category,
|
|
313
|
-
entry.source,
|
|
314
|
-
entry.description,
|
|
315
|
-
...(entry.aliases ?? []),
|
|
316
|
-
].join(' ').toLowerCase();
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
function slashScore(entry, query, index) {
|
|
320
|
-
if (!query) return index;
|
|
321
|
-
const name = slashQuery(entry.name);
|
|
322
|
-
if (name === query) return index - 10_000;
|
|
323
|
-
if (name.startsWith(query)) return index - 1_000;
|
|
324
|
-
if (slashQuery(entry.command).startsWith(query)) return index - 500;
|
|
325
|
-
return index;
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
function formatAvailability(availability = { type: 'enabled' }) {
|
|
329
|
-
if (availability.type === 'disabled') return `disabled - ${availability.reason}`;
|
|
330
|
-
return availability.type ?? 'enabled';
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
function applyTemporaryEnv(env) {
|
|
334
|
-
if (!env) return () => {};
|
|
335
|
-
const previous = new Map();
|
|
336
|
-
for (const [key, value] of Object.entries(env)) {
|
|
337
|
-
previous.set(key, Object.hasOwn(process.env, key) ? process.env[key] : undefined);
|
|
338
|
-
if (value === undefined) delete process.env[key];
|
|
339
|
-
else process.env[key] = String(value);
|
|
340
|
-
}
|
|
341
|
-
return () => {
|
|
342
|
-
for (const [key, value] of previous) {
|
|
343
|
-
if (value === undefined) delete process.env[key];
|
|
344
|
-
else process.env[key] = value;
|
|
345
|
-
}
|
|
346
|
-
};
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
export function skillPromptFromSlashCommand(entry, prompt) {
|
|
350
|
-
const skillText = entry?.filePath ? readSkillText(entry.filePath) : '';
|
|
351
|
-
return [
|
|
352
|
-
`[skill:${entry.name}]`,
|
|
353
|
-
skillText.trim(),
|
|
354
|
-
'[/skill]',
|
|
355
|
-
'',
|
|
356
|
-
'[prompt]',
|
|
357
|
-
prompt.trim(),
|
|
358
|
-
'[/prompt]',
|
|
359
|
-
].join('\n');
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
function readSkillText(filePath) {
|
|
363
|
-
try {
|
|
364
|
-
return readFileSync(filePath, 'utf8');
|
|
365
|
-
} catch {
|
|
366
|
-
return '';
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
export function builtinToolSummaryLines(limit = 12) {
|
|
371
|
-
return [
|
|
372
|
-
`Built-in tools: ${BUILTIN_TOOLS.length}`,
|
|
373
|
-
...BUILTIN_TOOLS.slice(0, limit).map(([name, kind, description]) => `${name} (${kind}) - ${description}`),
|
|
374
|
-
];
|
|
375
|
-
}
|
package/src/cli/stream-json.mjs
DELETED
|
@@ -1,225 +0,0 @@
|
|
|
1
|
-
import { fileURLToPath } from 'node:url';
|
|
2
|
-
import { BUILTIN_TOOLS } from '../constants.mjs';
|
|
3
|
-
import { estimateUsage } from '../agent/fixture.mjs';
|
|
4
|
-
import { listActiveMcpServerEntries } from '../mcp/discover.mjs';
|
|
5
|
-
import { discoverMcpToolRows } from '../mcp/probe.mjs';
|
|
6
|
-
import { readEffectiveSettings } from '../settings/load.mjs';
|
|
7
|
-
import { isToolDisabled, listToolboxTools, toolKindForName } from '../tools/toolbox.mjs';
|
|
8
|
-
import { toolResultContent } from '../tools/builtin/runtime.mjs';
|
|
9
|
-
import { detectImageMediaType, imageMediaTypeExtension } from '../util/media.mjs';
|
|
10
|
-
import { displayCwd, emitJson } from '../util/fs.mjs';
|
|
11
|
-
import { UsageError } from './parse.mjs';
|
|
12
|
-
import { reasoningEffortForMode } from './reasoning.mjs';
|
|
13
|
-
import { imageMentionBlock } from './refs.mjs';
|
|
14
|
-
|
|
15
|
-
export function streamJsonInputMessages(prompt, stdin) {
|
|
16
|
-
const messages = [];
|
|
17
|
-
let generatedImageIndex = 0;
|
|
18
|
-
if (prompt) {
|
|
19
|
-
messages.push({
|
|
20
|
-
content: [{ type: 'text', text: prompt }],
|
|
21
|
-
text: prompt,
|
|
22
|
-
steer: false,
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
for (const [index, line] of stdin.split(/\r?\n/).entries()) {
|
|
26
|
-
if (!line.trim()) continue;
|
|
27
|
-
let event;
|
|
28
|
-
try {
|
|
29
|
-
event = JSON.parse(line);
|
|
30
|
-
} catch {
|
|
31
|
-
throw new UsageError(`stream-json-input line ${index + 1} is not valid JSON`);
|
|
32
|
-
}
|
|
33
|
-
if (event.type !== 'user') continue;
|
|
34
|
-
const content = Array.isArray(event.message?.content) ? event.message.content : [];
|
|
35
|
-
messages.push({
|
|
36
|
-
content,
|
|
37
|
-
text: content
|
|
38
|
-
.map((block) => streamJsonContentBlockText(block, () => {
|
|
39
|
-
generatedImageIndex += 1;
|
|
40
|
-
return generatedImageIndex;
|
|
41
|
-
}))
|
|
42
|
-
.filter(Boolean)
|
|
43
|
-
.join('\n'),
|
|
44
|
-
steer: event.steer === true,
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
if (messages.length === 0) {
|
|
48
|
-
messages.push({ content: [{ type: 'text', text: '' }], text: '', steer: false });
|
|
49
|
-
}
|
|
50
|
-
return messages;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function streamJsonContentBlockText(block, nextGeneratedImageIndex = () => 1) {
|
|
54
|
-
if (block.type === 'text') return block.text;
|
|
55
|
-
if (block.type !== 'image') return undefined;
|
|
56
|
-
const base64Image = validateStreamJsonBase64Image(block.source);
|
|
57
|
-
if (block.source_path) {
|
|
58
|
-
const filePath = block.source_path.startsWith('file://')
|
|
59
|
-
? fileURLToPath(block.source_path)
|
|
60
|
-
: block.source_path;
|
|
61
|
-
return imageMentionBlock(filePath);
|
|
62
|
-
}
|
|
63
|
-
if (base64Image) {
|
|
64
|
-
const sourcePath = `stream-json-input-image-${nextGeneratedImageIndex()}.${imageMediaTypeExtension(base64Image.mediaType)}`;
|
|
65
|
-
return `[image:${sourcePath}]\nmedia_type: ${base64Image.mediaType}\nbytes: ${base64Image.bytes}\n[/image]`;
|
|
66
|
-
}
|
|
67
|
-
return undefined;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function validateStreamJsonBase64Image(source) {
|
|
71
|
-
if (source?.type !== 'base64' || !source.media_type || !source.data) return undefined;
|
|
72
|
-
const raw = Buffer.from(source.data, 'base64');
|
|
73
|
-
const detected = detectImageMediaType(raw);
|
|
74
|
-
if (detected && detected !== source.media_type) {
|
|
75
|
-
throw new UsageError(`stream-json-input image media_type ${source.media_type} does not match decoded ${detected}`);
|
|
76
|
-
}
|
|
77
|
-
if (!detected) throw new UsageError(`stream-json-input image media_type ${source.media_type} is not supported by decoded image data`);
|
|
78
|
-
return { mediaType: source.media_type, bytes: raw.byteLength };
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
export function streamJsonOutputUserContent(content = []) {
|
|
82
|
-
const textBlocks = content.filter((block) => block.type === 'text');
|
|
83
|
-
return textBlocks.length > 0 ? textBlocks : [{ type: 'text', text: '' }];
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export function toolRunParent(toolRun, field, fallback) {
|
|
87
|
-
return Object.hasOwn(toolRun, field) ? toolRun[field] : fallback;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export function streamJsonTurnCount(turns = []) {
|
|
91
|
-
return turns.reduce((count, turn) => count + (turn.toolRun?.toolUse ? 2 : 1), 0);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export function streamJsonPermissionDenials(denials = []) {
|
|
95
|
-
return denials.map((denial) => {
|
|
96
|
-
if (typeof denial === 'string') return denial;
|
|
97
|
-
const reason = denial.reason ? ` (${denial.reason})` : '';
|
|
98
|
-
return `${denial.tool}: ${denial.action}${reason}`;
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
export function assistantStreamContent(result, parsed = {}) {
|
|
103
|
-
if (parsed.streamJsonThinking && readEffectiveSettings(parsed)['covenCode.thinking.enabled'] !== false) {
|
|
104
|
-
return [{ type: 'thinking', thinking: 'Using the local deterministic recreation.' }, { type: 'text', text: result }];
|
|
105
|
-
}
|
|
106
|
-
return [{ type: 'text', text: result }];
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
export async function emitStreamJsonInit({ parsed, plugins, sessionId, promptForMcpDiscovery }) {
|
|
110
|
-
const activeMcpServers = listActiveMcpServerEntries(parsed, promptForMcpDiscovery);
|
|
111
|
-
const tools = [
|
|
112
|
-
...BUILTIN_TOOLS.map(([name]) => name),
|
|
113
|
-
...listToolboxTools(parsed).map((tool) => tool.name),
|
|
114
|
-
...plugins.tools.map((tool) => tool.name),
|
|
115
|
-
...(await discoverMcpToolRows(activeMcpServers)).map(([name]) => name),
|
|
116
|
-
].filter((name) => !isToolDisabled(name, toolKindForName(name), parsed));
|
|
117
|
-
emitJson({
|
|
118
|
-
type: 'system',
|
|
119
|
-
subtype: 'init',
|
|
120
|
-
cwd: displayCwd(),
|
|
121
|
-
session_id: sessionId,
|
|
122
|
-
tools,
|
|
123
|
-
mcp_servers: activeMcpServers.map(({ name }) => ({ name, status: 'connected' })),
|
|
124
|
-
agent_mode: parsed.mode,
|
|
125
|
-
reasoning_effort: reasoningEffortForMode(parsed.mode, parsed.reasoningEffort),
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
export function emitStreamJsonTurn({
|
|
130
|
-
userContent,
|
|
131
|
-
steer = false,
|
|
132
|
-
promptText,
|
|
133
|
-
toolRun,
|
|
134
|
-
result,
|
|
135
|
-
parsed,
|
|
136
|
-
sessionId,
|
|
137
|
-
assistantParentToolUseId,
|
|
138
|
-
}) {
|
|
139
|
-
emitJson({
|
|
140
|
-
type: 'user',
|
|
141
|
-
...(steer ? { steer: true } : {}),
|
|
142
|
-
message: { role: 'user', content: userContent },
|
|
143
|
-
parent_tool_use_id: null,
|
|
144
|
-
session_id: sessionId,
|
|
145
|
-
});
|
|
146
|
-
if (toolRun?.toolUse) {
|
|
147
|
-
emitJson({
|
|
148
|
-
type: 'assistant',
|
|
149
|
-
message: {
|
|
150
|
-
type: 'message',
|
|
151
|
-
role: 'assistant',
|
|
152
|
-
content: [toolRun.toolUse],
|
|
153
|
-
stop_reason: 'tool_use',
|
|
154
|
-
usage: estimateUsage(promptText, JSON.stringify(toolRun.toolUse.input)),
|
|
155
|
-
},
|
|
156
|
-
parent_tool_use_id: null,
|
|
157
|
-
session_id: sessionId,
|
|
158
|
-
});
|
|
159
|
-
for (const subagentMessage of toolRun.subagentMessages ?? []) {
|
|
160
|
-
emitJson({
|
|
161
|
-
type: 'assistant',
|
|
162
|
-
message: {
|
|
163
|
-
type: 'message',
|
|
164
|
-
role: 'assistant',
|
|
165
|
-
content: assistantStreamContent(subagentMessage.text ?? '', parsed),
|
|
166
|
-
stop_reason: 'end_turn',
|
|
167
|
-
usage: estimateUsage(promptText, subagentMessage.text ?? ''),
|
|
168
|
-
},
|
|
169
|
-
parent_tool_use_id: toolRun.toolUse.id,
|
|
170
|
-
session_id: sessionId,
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
emitJson({
|
|
174
|
-
type: 'user',
|
|
175
|
-
message: {
|
|
176
|
-
role: 'user',
|
|
177
|
-
content: [{
|
|
178
|
-
type: 'tool_result',
|
|
179
|
-
tool_use_id: toolRun.toolUse.id,
|
|
180
|
-
content: toolResultContent(toolRun),
|
|
181
|
-
is_error: toolRun.exitCode !== 0,
|
|
182
|
-
}],
|
|
183
|
-
},
|
|
184
|
-
parent_tool_use_id: toolRunParent(toolRun, 'toolResultParentToolUseId', null),
|
|
185
|
-
session_id: sessionId,
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
emitJson({
|
|
189
|
-
type: 'assistant',
|
|
190
|
-
message: {
|
|
191
|
-
type: 'message',
|
|
192
|
-
role: 'assistant',
|
|
193
|
-
content: assistantStreamContent(result, parsed),
|
|
194
|
-
stop_reason: 'end_turn',
|
|
195
|
-
usage: estimateUsage(promptText, result),
|
|
196
|
-
},
|
|
197
|
-
parent_tool_use_id: assistantParentToolUseId,
|
|
198
|
-
session_id: sessionId,
|
|
199
|
-
});
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
export function streamJsonResultMessage({ started, isError, errorSubtype, numTurns, result, sessionId, usage, permissionDenials }) {
|
|
203
|
-
const common = {
|
|
204
|
-
type: 'result',
|
|
205
|
-
duration_ms: Date.now() - started,
|
|
206
|
-
num_turns: numTurns,
|
|
207
|
-
session_id: sessionId,
|
|
208
|
-
usage,
|
|
209
|
-
permission_denials: permissionDenials,
|
|
210
|
-
};
|
|
211
|
-
if (isError) {
|
|
212
|
-
return {
|
|
213
|
-
...common,
|
|
214
|
-
subtype: errorSubtype ?? 'error_during_execution',
|
|
215
|
-
is_error: true,
|
|
216
|
-
error: result,
|
|
217
|
-
};
|
|
218
|
-
}
|
|
219
|
-
return {
|
|
220
|
-
...common,
|
|
221
|
-
subtype: 'success',
|
|
222
|
-
is_error: false,
|
|
223
|
-
result,
|
|
224
|
-
};
|
|
225
|
-
}
|
package/src/cli/tui-actions.mjs
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
buildSlashCommandCatalog,
|
|
3
|
-
buildStaticSlashCommandCatalog,
|
|
4
|
-
filterSlashCommands,
|
|
5
|
-
} from './slash-commands.mjs';
|
|
6
|
-
import { currentSlashMatches } from './tui-render.mjs';
|
|
7
|
-
import { submitTuiText } from './tui-submit.mjs';
|
|
8
|
-
|
|
9
|
-
export function insertComposerText(model, text) {
|
|
10
|
-
model.composer = `${model.composer.slice(0, model.composerCursor)}${text}${model.composer.slice(model.composerCursor)}`;
|
|
11
|
-
model.composerCursor += text.length;
|
|
12
|
-
updateSlashState(model);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function deleteComposerText(model, kind) {
|
|
16
|
-
if (kind === 'delete') {
|
|
17
|
-
if (model.composerCursor >= model.composer.length) return;
|
|
18
|
-
model.composer = `${model.composer.slice(0, model.composerCursor)}${model.composer.slice(model.composerCursor + 1)}`;
|
|
19
|
-
} else {
|
|
20
|
-
if (model.composerCursor <= 0) return;
|
|
21
|
-
model.composer = `${model.composer.slice(0, model.composerCursor - 1)}${model.composer.slice(model.composerCursor)}`;
|
|
22
|
-
model.composerCursor -= 1;
|
|
23
|
-
}
|
|
24
|
-
updateSlashState(model);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function updateSlashState(model) {
|
|
28
|
-
const beforeCursor = model.composer.slice(0, model.composerCursor);
|
|
29
|
-
const active = beforeCursor.startsWith('/') && !/\s/.test(beforeCursor.slice(1));
|
|
30
|
-
if (!active) {
|
|
31
|
-
closeSlashMenu(model);
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
model.slashOpen = true;
|
|
35
|
-
model.slashQuery = beforeCursor.replace(/^\/+/, '');
|
|
36
|
-
model.slashMatches = filterSlashCommands(model.slashCatalog, beforeCursor);
|
|
37
|
-
if (model.slashMatches.length === 0) model.slashIndex = 0;
|
|
38
|
-
else model.slashIndex = Math.min(model.slashIndex, model.slashMatches.length - 1);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export function closeSlashMenu(model) {
|
|
42
|
-
model.slashOpen = false;
|
|
43
|
-
model.slashQuery = '';
|
|
44
|
-
model.slashMatches = filterSlashCommands(model.slashCatalog, '');
|
|
45
|
-
model.slashIndex = 0;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export function completeSlashSelection(model) {
|
|
49
|
-
const selected = currentSlashMatches(model)[model.slashIndex];
|
|
50
|
-
if (!selected) return;
|
|
51
|
-
model.composer = `${selected.command} `;
|
|
52
|
-
model.composerCursor = model.composer.length;
|
|
53
|
-
closeSlashMenu(model);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export async function acceptSlashSelection(model, session) {
|
|
57
|
-
const selected = currentSlashMatches(model)[model.slashIndex];
|
|
58
|
-
if (!selected) return;
|
|
59
|
-
const command = selected.command;
|
|
60
|
-
model.composer = '';
|
|
61
|
-
model.composerCursor = 0;
|
|
62
|
-
closeSlashMenu(model);
|
|
63
|
-
await submitTuiText(model, session, command);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export async function safeBuildSlashCommandCatalog(parsed) {
|
|
67
|
-
try {
|
|
68
|
-
return await buildSlashCommandCatalog({ parsed, cwd: process.cwd() });
|
|
69
|
-
} catch {
|
|
70
|
-
return buildStaticSlashCommandCatalog();
|
|
71
|
-
}
|
|
72
|
-
}
|