@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
package/src/cli/execute.mjs
DELETED
|
@@ -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
|
-
}
|