@opencoven/coven-code 0.0.4 → 0.0.7

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 +25 -22
  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 -452
  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 -225
  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,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
- }
@@ -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
- }
@@ -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
- }