@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,452 +0,0 @@
1
- import { randomUUID } from 'node:crypto';
2
- import { readFileSync } from 'node:fs';
3
- import path from 'node:path';
4
- import { estimateUsage, fixtureAgentResponse } from '../agent/fixture.mjs';
5
- import {
6
- loadPlugins,
7
- runPluginEventHandlers,
8
- } from '../plugins/discover.mjs';
9
- import { discoverAgentFiles, firstGuidanceInDir } from '../commands/agents.mjs';
10
- import { executePromptToolRequest } from '../tools/builtin/index.mjs';
11
- import { toolResultContent } from '../tools/builtin/runtime.mjs';
12
- import { persistThreadMessages, threadContinuationPrompt } from '../threads/store.mjs';
13
- import { readEffectiveSettings } from '../settings/load.mjs';
14
- import { expandFileReferences, expandThreadReferences } from './refs.mjs';
15
- import { UsageError } from './parse.mjs';
16
- import { notifyAgentComplete } from './notifications.mjs';
17
- import { globToRegex } from '../util/glob.mjs';
18
- import { emitJson } from '../util/fs.mjs';
19
- import {
20
- emitStreamJsonInit,
21
- emitStreamJsonTurn,
22
- streamJsonInputMessages,
23
- streamJsonOutputUserContent,
24
- streamJsonPermissionDenials,
25
- streamJsonResultMessage,
26
- streamJsonTurnCount,
27
- toolRunParent,
28
- } from './stream-json.mjs';
29
-
30
- const MAX_AGENT_CONTINUATIONS = 8;
31
-
32
- export async function runExecute(parsed, stdin, options = {}) {
33
- const started = Date.now();
34
- if (parsed.streamJsonInput && parsed.streamJson) {
35
- return runStreamJsonInputExecute(parsed, stdin, options, started);
36
- }
37
-
38
- const prompt = combinePrompt(parsed.prompt, stdin, parsed.streamJsonInput);
39
- const sessionId = options.thread?.id ?? `T-${randomUUID()}`;
40
- const plugins = await loadPlugins(process.cwd());
41
- await runSessionStartHandlers(plugins, sessionId);
42
- const sequence = await runExecuteTurnSequence({
43
- initialPrompt: prompt,
44
- stdin,
45
- sessionId,
46
- parsed,
47
- plugins,
48
- thread: options.thread,
49
- });
50
- const { turns, errorSubtype } = sequence;
51
- const result = turns.at(-1)?.result ?? '';
52
-
53
- if (parsed.streamJson) {
54
- await emitStreamJsonInit({
55
- parsed,
56
- plugins,
57
- sessionId,
58
- promptForMcpDiscovery: turns.map((turn) => turn.prompt).join('\n\n'),
59
- });
60
- for (const turn of turns) {
61
- emitStreamJsonTurn({
62
- userContent: [{ type: 'text', text: turn.prompt }],
63
- promptText: turn.prompt,
64
- toolRun: turn.toolRun,
65
- result: turn.result,
66
- parsed,
67
- sessionId,
68
- assistantParentToolUseId: turn.toolRun ? toolRunParent(turn.toolRun, 'finalParentToolUseId', null) : null,
69
- });
70
- }
71
- emitJson(streamJsonResultMessage({
72
- started,
73
- isError: errorSubtype !== undefined || turns.some((turn) => (turn.toolRun?.exitCode ?? 0) !== 0),
74
- errorSubtype,
75
- numTurns: streamJsonTurnCount(turns),
76
- result: errorSubtype === 'error_max_turns' ? 'Maximum agent continuation turns exceeded' : result,
77
- sessionId,
78
- usage: estimateUsage(turns.map((turn) => turn.prompt).join('\n'), result),
79
- permissionDenials: streamJsonPermissionDenials(turns.flatMap((turn) => turn.toolRun?.permissionDenials ?? [])),
80
- }));
81
- const thread = await persistThreadMessages(sessionId, turnMessages(turns), parsed.mode, options.thread, parsed);
82
- notifyAgentComplete(parsed);
83
- return thread;
84
- }
85
-
86
- if (!options.silent) {
87
- process.stdout.write(result.endsWith('\n') ? result : `${result}\n`);
88
- }
89
- const thread = await persistThreadMessages(sessionId, turnMessages(turns), parsed.mode, options.thread, parsed);
90
- notifyAgentComplete(parsed);
91
- return thread;
92
- }
93
-
94
- async function runExecuteTurnSequence({ initialPrompt, stdin, sessionId, parsed, plugins, thread }) {
95
- const turns = [];
96
- let prompt = initialPrompt;
97
- let continuationCount = 0;
98
- let errorSubtype;
99
- while (prompt !== undefined) {
100
- const turnStdin = turns.length === 0 ? stdin : '';
101
- const turn = await runExecuteTurn(prompt, turnStdin, sessionId, parsed, plugins, thread, turns);
102
- turns.push(turn);
103
- const nextPrompt = agentContinuationMessage(turn.endDecision);
104
- if (!nextPrompt) break;
105
- continuationCount += 1;
106
- if (continuationCount > MAX_AGENT_CONTINUATIONS) {
107
- errorSubtype = 'error_max_turns';
108
- process.exitCode = 1;
109
- break;
110
- }
111
- prompt = nextPrompt;
112
- }
113
- return { turns, errorSubtype };
114
- }
115
-
116
- async function runExecuteTurn(prompt, stdin, sessionId, parsed, plugins, thread, priorTurns = []) {
117
- const messageId = createThreadMessageID();
118
- const startDecision = await runPluginEventHandlers(plugins.handlers['agent.start'], {
119
- message: prompt,
120
- id: messageId,
121
- thread: { id: sessionId },
122
- });
123
- const promptWithPluginContext = appendAgentStartMessage(prompt, startDecision);
124
- const expandedPrompt = expandFileReferences(promptWithPluginContext, { parsed });
125
- const guidancePrompt = expandAgentGuidanceReferences(expandedPrompt, parsed);
126
- const turnPrompt = guidancePrompt ? `${guidancePrompt}\n${expandedPrompt}` : expandedPrompt;
127
- const contextThread = threadContextForTurn(thread, sessionId, priorTurns);
128
- const modelPrompt = contextThread
129
- ? threadContinuationPrompt(contextThread, turnPrompt)
130
- : expandThreadReferences(turnPrompt);
131
- const toolRun = await executePromptToolRequest(prompt, stdin, sessionId, parsed, plugins);
132
- const result = toolRun?.output ?? fixtureAgentResponse(applySystemPrompt(modelPrompt, parsed), stdin);
133
- const endDecision = await runPluginEventHandlers(plugins.handlers['agent.end'], {
134
- message: prompt,
135
- id: messageId,
136
- result,
137
- status: agentTurnStatus(toolRun),
138
- messages: pluginMessagesForTurn(prompt, result, toolRun, messageId),
139
- thread: { id: sessionId },
140
- });
141
- return { prompt, result, toolRun, endDecision };
142
- }
143
-
144
- function agentTurnStatus(toolRun) {
145
- return (toolRun?.exitCode ?? 0) === 0 ? 'done' : 'error';
146
- }
147
-
148
- function createThreadMessageID() {
149
- return `msg_${randomUUID()}`;
150
- }
151
-
152
- function appendAgentStartMessage(prompt, decision = {}) {
153
- validateAgentStartDecision(decision);
154
- const content = decision.message?.content.trim() ?? '';
155
- if (!content) return prompt;
156
- return prompt ? `${prompt}\n\n${content}` : content;
157
- }
158
-
159
- function validateAgentStartDecision(decision = {}) {
160
- if (!decision || typeof decision !== 'object' || !Object.hasOwn(decision, 'message')) return;
161
- if (Object.keys(decision).some((key) => key !== 'message')) {
162
- throw new Error('plugin agent.start fields must match the documented shape');
163
- }
164
- if (!decision.message || typeof decision.message !== 'object' || Array.isArray(decision.message)) {
165
- throw new Error('plugin agent.start message must be an object');
166
- }
167
- if (Object.keys(decision.message).some((key) => key !== 'content' && key !== 'display')) {
168
- throw new Error('plugin agent.start message fields must be content and display');
169
- }
170
- if (typeof decision.message?.content !== 'string') {
171
- throw new Error('plugin agent.start message content must be a string');
172
- }
173
- if (decision.message.display !== true) {
174
- throw new Error('plugin agent.start message display must be true');
175
- }
176
- }
177
-
178
- function threadContextForTurn(thread, sessionId, priorTurns = []) {
179
- if (!thread && priorTurns.length === 0) return undefined;
180
- return {
181
- id: thread?.id ?? sessionId,
182
- messages: [
183
- ...(thread?.messages ?? []),
184
- ...turnMessages(priorTurns),
185
- ],
186
- };
187
- }
188
-
189
- function turnMessages(turns) {
190
- return turns.flatMap((turn) => [
191
- { role: 'user', content: turn.prompt },
192
- { role: 'assistant', content: turn.result },
193
- ]);
194
- }
195
-
196
- function pluginMessagesForTurn(prompt, result, toolRun, messageId = createThreadMessageID()) {
197
- const messages = [{ role: 'user', id: messageId, content: textContent(prompt) }];
198
- if (toolRun?.toolUse) {
199
- messages.push({ role: 'assistant', id: createThreadMessageID(), content: [toolRun.toolUse] });
200
- messages.push({
201
- role: 'user',
202
- id: createThreadMessageID(),
203
- content: [{
204
- type: 'tool_result',
205
- toolUseID: toolRun.toolUse.id,
206
- output: toolResultContent(toolRun),
207
- status: toolRun.exitCode !== 0 ? 'error' : 'done',
208
- }],
209
- });
210
- }
211
- messages.push({ role: 'assistant', id: createThreadMessageID(), content: textContent(result) });
212
- return messages;
213
- }
214
-
215
- function textContent(text) {
216
- return [{ type: 'text', text: String(text ?? '') }];
217
- }
218
-
219
- function agentContinuationMessage(decision) {
220
- validateAgentEndDecision(decision);
221
- if (decision?.action !== 'continue') return undefined;
222
- return typeof decision.userMessage === 'string' && decision.userMessage.trim()
223
- ? decision.userMessage
224
- : undefined;
225
- }
226
-
227
- function validateAgentEndDecision(decision) {
228
- if (decision === undefined) return;
229
- if (decision?.action === 'allow') return;
230
- if (!decision || typeof decision !== 'object' || decision.action !== 'continue') {
231
- throw new Error('plugin agent.end action must be continue');
232
- }
233
- if (typeof decision.userMessage !== 'string') {
234
- throw new Error('plugin agent.end continue userMessage must be a string');
235
- }
236
- }
237
-
238
- async function runStreamJsonInputExecute(parsed, stdin, options = {}, started = Date.now()) {
239
- const sessionId = options.thread?.id ?? `T-${randomUUID()}`;
240
- const inputMessages = streamJsonInputMessages(parsed.prompt, stdin);
241
- const combinedPrompt = inputMessages.map((message) => message.text).filter(Boolean).join('\n\n');
242
- const plugins = await loadPlugins(process.cwd());
243
- await runSessionStartHandlers(plugins, sessionId);
244
- await emitStreamJsonInit({
245
- parsed,
246
- plugins,
247
- sessionId,
248
- promptForMcpDiscovery: combinedPrompt,
249
- });
250
-
251
- const transcript = [];
252
- const persistedMessages = [];
253
- const permissionDenials = [];
254
- let result = '';
255
- let parentToolUseId = null;
256
- let numTurns = 0;
257
- let isError = false;
258
-
259
- for (const input of inputMessages) {
260
- const messageId = createThreadMessageID();
261
- const startDecision = await runPluginEventHandlers(plugins.handlers['agent.start'], {
262
- message: input.text,
263
- id: messageId,
264
- thread: { id: sessionId },
265
- });
266
- const inputTextWithPluginContext = appendAgentStartMessage(input.text, startDecision);
267
- const expandedPrompt = expandFileReferences(inputTextWithPluginContext, { parsed });
268
- const guidancePrompt = expandAgentGuidanceReferences(expandedPrompt, parsed);
269
- const turnPrompt = guidancePrompt ? `${guidancePrompt}\n${expandedPrompt}` : expandedPrompt;
270
- const modelPrompt = modelPromptWithTranscript(turnPrompt, transcript, options.thread, parsed);
271
- const toolRun = await executePromptToolRequest(input.text, '', sessionId, parsed, plugins);
272
- result = toolRun?.output ?? fixtureAgentResponse(modelPrompt, '');
273
- numTurns += streamJsonTurnCount([{ toolRun }]);
274
- if (toolRun?.permissionDenials) permissionDenials.push(...toolRun.permissionDenials);
275
- if ((toolRun?.exitCode ?? 0) !== 0) isError = true;
276
-
277
- if (toolRun?.toolUse) {
278
- parentToolUseId = toolRunParent(toolRun, 'finalParentToolUseId', null);
279
- }
280
- emitStreamJsonTurn({
281
- userContent: streamJsonOutputUserContent(input.content),
282
- steer: input.steer,
283
- promptText: input.text,
284
- toolRun,
285
- result,
286
- parsed,
287
- sessionId,
288
- assistantParentToolUseId: parentToolUseId,
289
- });
290
- await runPluginEventHandlers(plugins.handlers['agent.end'], {
291
- message: input.text,
292
- id: messageId,
293
- result,
294
- status: agentTurnStatus(toolRun),
295
- messages: pluginMessagesForTurn(input.text, result, toolRun, messageId),
296
- thread: { id: sessionId },
297
- });
298
- transcript.push({ user: input.text, assistant: result });
299
- persistedMessages.push(
300
- { role: 'user', content: input.text },
301
- { role: 'assistant', content: result },
302
- );
303
- }
304
-
305
- emitJson(streamJsonResultMessage({
306
- started,
307
- isError,
308
- numTurns,
309
- result,
310
- sessionId,
311
- usage: estimateUsage(combinedPrompt, result),
312
- permissionDenials: streamJsonPermissionDenials(permissionDenials),
313
- }));
314
- const thread = await persistThreadMessages(sessionId, persistedMessages, parsed.mode, options.thread, parsed);
315
- notifyAgentComplete(parsed);
316
- return thread;
317
- }
318
-
319
- async function runSessionStartHandlers(plugins, sessionId) {
320
- await runPluginEventHandlers(plugins.handlers['session.start'], {
321
- thread: { id: sessionId },
322
- });
323
- }
324
-
325
- function modelPromptWithTranscript(turnPrompt, transcript, thread, parsed = {}) {
326
- const context = transcript.length > 0
327
- ? `[conversation:${thread?.id ?? 'stream-json-input'}]\n${transcript
328
- .map((entry) => `user: ${entry.user}\nassistant: ${entry.assistant}`)
329
- .join('\n')}\n[/conversation]\n`
330
- : '';
331
- const prompt = `${context}${turnPrompt}`;
332
- const modelPrompt = thread ? threadContinuationPrompt(thread, prompt) : expandThreadReferences(prompt);
333
- return applySystemPrompt(modelPrompt, parsed);
334
- }
335
-
336
- function applySystemPrompt(prompt, parsed = {}) {
337
- const systemPrompt = readEffectiveSettings(parsed)['covenCode.systemPrompt'];
338
- if (typeof systemPrompt !== 'string' || systemPrompt.trim() === '') return prompt;
339
- return `[system]\n${systemPrompt.trim()}\n[/system]\n${prompt}`;
340
- }
341
-
342
- function expandAgentGuidanceReferences(expandedPrompt, parsed = {}) {
343
- const readFiles = filesInPrompt(expandedPrompt);
344
- const agentFiles = [
345
- ...discoverAgentFiles(process.cwd()),
346
- ...discoverSubtreeAgentFiles(readFiles),
347
- ];
348
- const blocks = [...new Set(agentFiles)]
349
- .map((filePath) => guidanceBlock(filePath, readFiles, parsed))
350
- .filter(Boolean);
351
- return blocks.join('\n');
352
- }
353
-
354
- function discoverSubtreeAgentFiles(readFiles) {
355
- const cwd = path.resolve(process.cwd());
356
- const files = [];
357
- for (const filePath of readFiles) {
358
- let current = path.dirname(path.resolve(filePath));
359
- while (current.startsWith(cwd) && current !== cwd && current !== path.dirname(current)) {
360
- const guidance = firstGuidanceInDir(current);
361
- if (guidance) files.push(guidance);
362
- current = path.dirname(current);
363
- }
364
- }
365
- return files;
366
- }
367
-
368
- function guidanceBlock(filePath, readFiles, parsed = {}) {
369
- try {
370
- const content = readFileSync(filePath, 'utf8');
371
- const expandable = stripFencedCodeBlocks(content);
372
- const expanded = expandFileReferences(expandable, {
373
- baseDir: path.dirname(filePath),
374
- parsed,
375
- includeFile: (mentionedFile, mentionedContent) => guidanceMentionApplies(mentionedFile, mentionedContent, readFiles),
376
- });
377
- return `[agent:${filePath}]\n${expanded}\n[/agent]`;
378
- } catch {
379
- return undefined;
380
- }
381
- }
382
-
383
- function filesInPrompt(prompt) {
384
- return [...prompt.matchAll(/\[file:([^\]\r\n]+)\]/g)].map((match) => path.resolve(match[1]));
385
- }
386
-
387
- function guidanceMentionApplies(mentionedFile, content, readFiles) {
388
- const globs = frontmatterGlobs(content);
389
- if (globs.length === 0) return true;
390
- return readFiles.some((filePath) => globs.some((glob) => guidanceGlobMatches(glob, filePath, mentionedFile)));
391
- }
392
-
393
- function frontmatterGlobs(content) {
394
- const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
395
- if (!match) return [];
396
- const frontmatter = match[1];
397
- const inline = frontmatter.match(/^globs:\s*\[(.*)\]\s*$/m);
398
- if (inline) {
399
- return inline[1]
400
- .split(',')
401
- .map((entry) => entry.trim().replace(/^["']|["']$/g, ''))
402
- .filter(Boolean);
403
- }
404
- const lines = frontmatter.split(/\r?\n/);
405
- const globs = [];
406
- let inGlobs = false;
407
- for (const line of lines) {
408
- if (/^globs:\s*$/.test(line)) {
409
- inGlobs = true;
410
- continue;
411
- }
412
- if (inGlobs) {
413
- const entry = line.match(/^\s*-\s*(.+?)\s*$/);
414
- if (entry) globs.push(entry[1].replace(/^["']|["']$/g, ''));
415
- else if (/^\S/.test(line)) break;
416
- }
417
- }
418
- return globs.filter(Boolean);
419
- }
420
-
421
- function guidanceGlobMatches(pattern, readFile, guidanceFile) {
422
- const normalized = path.normalize(pattern);
423
- if (normalized.startsWith(`..${path.sep}`) || normalized.startsWith(`.${path.sep}`)) {
424
- const absolutePattern = path.resolve(path.dirname(guidanceFile), normalized);
425
- return globToRegex(path.normalize(absolutePattern)).test(path.normalize(readFile));
426
- }
427
- const relative = path.relative(process.cwd(), readFile);
428
- const candidates = normalized.startsWith(`**${path.sep}`)
429
- ? [normalized]
430
- : [normalized, `**${path.sep}${normalized}`];
431
- return candidates.some((candidate) => globToRegex(path.normalize(candidate)).test(path.normalize(relative)));
432
- }
433
-
434
- function stripFencedCodeBlocks(text) {
435
- return text.replace(/```[\s\S]*?```/g, '');
436
- }
437
-
438
- function combinePrompt(prompt, stdin, streamJsonInput) {
439
- if (streamJsonInput && stdin.trim()) {
440
- return streamJsonInputMessages(prompt, stdin)
441
- .map((message) => message.text)
442
- .filter(Boolean)
443
- .join('\n');
444
- }
445
-
446
- const processedStdin = stdin;
447
- if (prompt && processedStdin.trim()) return `${prompt}\n\n${processedStdin.trimEnd()}`;
448
- if (prompt) return prompt;
449
- return processedStdin.trimEnd();
450
- }
451
-
452
- export { UsageError };
package/src/cli/help.mjs DELETED
@@ -1,58 +0,0 @@
1
- import { CLI_NAME, PRODUCT_NAME } from '../constants.mjs';
2
-
3
- export function printHelp() {
4
- console.log(`${PRODUCT_NAME}
5
-
6
- Usage: ${CLI_NAME} [options] [command]
7
-
8
- Run with no arguments in a terminal to enter the panel TUI. Set
9
- COVEN_CODE_REPL=1 to use the classic readline REPL. Piped stdin becomes the
10
- first interactive message when stdout is a TTY. Pass --execute or redirect
11
- stdout to run a single turn and exit.
12
-
13
- Options:
14
- --execute, -x [prompt] Run one agent turn, print the final answer, and exit
15
- --stream-json Emit structured JSONL in execute mode
16
- --stream-json-thinking Include thinking blocks in stream JSON output
17
- --stream-json-input Read one or more user messages as JSONL from stdin
18
- --dangerously-allow-all Allow tool calls that would otherwise require approval
19
- --mcp-config <json> Add an inline MCP server config for this run
20
- --settings-file <path> Read user settings from a specific file
21
- --mode <name> Agent mode: smart, deep, rush, or large
22
- --reasoning-effort <level>
23
- Set reasoning effort for the active mode
24
- --label <name> Add a label to the created or continued thread
25
- --visibility <level> Thread visibility: private, public, workspace, group, or unlisted
26
- --archive Archive the thread after the execute turn
27
- --continue [thread-id] Continue the latest active thread or the specified thread
28
- --toolbox <path> Use a PATH-like toolbox root for this run
29
- --skills <path> Use a PATH-like skill root for this run
30
- --jetbrains Connect to a JetBrains IDE
31
- -h, --help Show help
32
- -v, --version Show version
33
-
34
- Commands:
35
- login Print local login instructions
36
- update Check for updates
37
- usage Show local usage estimates
38
- review Run configured local review checks
39
- tools list|make|show|use Manage built-in and toolbox tools
40
- permissions list Show permission policy rules
41
- config edit [--workspace] Open settings in $EDITOR
42
- ide connect Connect or inspect local IDE integration
43
- mcp add|list|doctor|approve|oauth
44
- Manage MCP server settings
45
- skill list|show Inspect discovered Coven Code agent skills
46
- plugins list|reload Show and reload project and user plugin files
47
- threads list|show|search|archive|visibility|continue|map|report
48
- Manage local thread records
49
- agents-md list Show AGENTS.md guidance files used for this cwd
50
- agents list Alias for agents-md list
51
-
52
- TUI lane commands:
53
- /lane refresh Refresh worktree, branch, changed files, and diff summary
54
- /lane harness <name|next> Select smart, deep, rush, or large for the lane
55
- /lane verify Run the detected verification command for the lane
56
- /lane status|diff Show lane status or diff summary
57
- `);
58
- }
@@ -1,28 +0,0 @@
1
- import { runCommand } from './dispatch.mjs';
2
- import { runExecute } from './execute.mjs';
3
- import { readEditorPrompt, submitPromptAndQueue } from './interactive-io.mjs';
4
- import { handleSlashCommand, sessionSlashHelpLines } from './interactive-slash.mjs';
5
-
6
- export function createInteractiveSession(parsed, options = {}) {
7
- return {
8
- parsed,
9
- thread: options.thread,
10
- cwd: options.cwd ?? process.cwd(),
11
- queuedMessages: [],
12
- silent: options.silent ?? false,
13
- commandRunner: options.commandRunner ?? runCommand,
14
- executeRunner: options.executeRunner ?? runExecute,
15
- editorReader: options.editorReader ?? readEditorPrompt,
16
- };
17
- }
18
-
19
- export async function handleInteractiveInput(session, text) {
20
- if (!text) return { kind: 'empty', lines: [] };
21
- if (text === '/exit' || text === '/quit') return { kind: 'exit', lines: [] };
22
- if (text === '/help') return { kind: 'help', lines: await sessionSlashHelpLines(session) };
23
- if (!text.startsWith('/')) {
24
- await submitPromptAndQueue(session, text);
25
- return { kind: 'turn', lines: [] };
26
- }
27
- return handleSlashCommand(session, text);
28
- }
@@ -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
- }