@opencoven/coven-code 0.0.3 → 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 -590
- 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 -116
- 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,590 +0,0 @@
|
|
|
1
|
-
import { randomUUID } from 'node:crypto';
|
|
2
|
-
import { readFileSync } from 'node:fs';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import { BUILTIN_TOOLS } from '../constants.mjs';
|
|
5
|
-
import { estimateUsage, fixtureAgentResponse } from '../agent/fixture.mjs';
|
|
6
|
-
import {
|
|
7
|
-
listActiveMcpServerEntries,
|
|
8
|
-
} from '../mcp/discover.mjs';
|
|
9
|
-
import { discoverMcpToolRows } from '../mcp/probe.mjs';
|
|
10
|
-
import {
|
|
11
|
-
loadPlugins,
|
|
12
|
-
runPluginEventHandlers,
|
|
13
|
-
} from '../plugins/discover.mjs';
|
|
14
|
-
import { discoverAgentFiles, firstGuidanceInDir } from '../commands/agents.mjs';
|
|
15
|
-
import {
|
|
16
|
-
isToolDisabled,
|
|
17
|
-
listToolboxTools,
|
|
18
|
-
toolKindForName,
|
|
19
|
-
} from '../tools/toolbox.mjs';
|
|
20
|
-
import { executePromptToolRequest } from '../tools/builtin/index.mjs';
|
|
21
|
-
import { toolResultContent } from '../tools/builtin/runtime.mjs';
|
|
22
|
-
import { persistThreadMessages, threadContinuationPrompt } from '../threads/store.mjs';
|
|
23
|
-
import { readEffectiveSettings } from '../settings/load.mjs';
|
|
24
|
-
import { expandFileReferences, expandThreadReferences } from './refs.mjs';
|
|
25
|
-
import { UsageError } from './parse.mjs';
|
|
26
|
-
import { reasoningEffortForMode } from './reasoning.mjs';
|
|
27
|
-
import { notifyAgentComplete } from './notifications.mjs';
|
|
28
|
-
import { globToRegex } from '../util/glob.mjs';
|
|
29
|
-
import { displayCwd, emitJson } from '../util/fs.mjs';
|
|
30
|
-
import {
|
|
31
|
-
streamJsonInputMessages,
|
|
32
|
-
streamJsonOutputUserContent,
|
|
33
|
-
streamJsonPermissionDenials,
|
|
34
|
-
streamJsonResultMessage,
|
|
35
|
-
streamJsonTurnCount,
|
|
36
|
-
toolRunParent,
|
|
37
|
-
} from './stream-json.mjs';
|
|
38
|
-
|
|
39
|
-
const MAX_AGENT_CONTINUATIONS = 8;
|
|
40
|
-
|
|
41
|
-
export async function runExecute(parsed, stdin, options = {}) {
|
|
42
|
-
const started = Date.now();
|
|
43
|
-
if (parsed.streamJsonInput && parsed.streamJson) {
|
|
44
|
-
return runStreamJsonInputExecute(parsed, stdin, options, started);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const prompt = combinePrompt(parsed.prompt, stdin, parsed.streamJsonInput);
|
|
48
|
-
const sessionId = options.thread?.id ?? `T-${randomUUID()}`;
|
|
49
|
-
const plugins = await loadPlugins(process.cwd());
|
|
50
|
-
await runSessionStartHandlers(plugins, sessionId);
|
|
51
|
-
const sequence = await runExecuteTurnSequence({
|
|
52
|
-
initialPrompt: prompt,
|
|
53
|
-
stdin,
|
|
54
|
-
sessionId,
|
|
55
|
-
parsed,
|
|
56
|
-
plugins,
|
|
57
|
-
thread: options.thread,
|
|
58
|
-
});
|
|
59
|
-
const { turns, errorSubtype } = sequence;
|
|
60
|
-
const result = turns.at(-1)?.result ?? '';
|
|
61
|
-
|
|
62
|
-
if (parsed.streamJson) {
|
|
63
|
-
const activeMcpServers = listActiveMcpServerEntries(parsed, turns.map((turn) => turn.prompt).join('\n\n'));
|
|
64
|
-
const tools = [
|
|
65
|
-
...BUILTIN_TOOLS.map(([name]) => name),
|
|
66
|
-
...listToolboxTools(parsed).map((tool) => tool.name),
|
|
67
|
-
...plugins.tools.map((tool) => tool.name),
|
|
68
|
-
...(await discoverMcpToolRows(activeMcpServers)).map(([name]) => name),
|
|
69
|
-
].filter((name) => !isToolDisabled(name, toolKindForName(name), parsed));
|
|
70
|
-
emitJson({
|
|
71
|
-
type: 'system',
|
|
72
|
-
subtype: 'init',
|
|
73
|
-
cwd: displayCwd(),
|
|
74
|
-
session_id: sessionId,
|
|
75
|
-
tools,
|
|
76
|
-
mcp_servers: activeMcpServers.map(({ name }) => ({ name, status: 'connected' })),
|
|
77
|
-
agent_mode: parsed.mode,
|
|
78
|
-
reasoning_effort: reasoningEffortForMode(parsed.mode, parsed.reasoningEffort),
|
|
79
|
-
});
|
|
80
|
-
for (const turn of turns) {
|
|
81
|
-
emitJson({
|
|
82
|
-
type: 'user',
|
|
83
|
-
message: { role: 'user', content: [{ type: 'text', text: turn.prompt }] },
|
|
84
|
-
parent_tool_use_id: null,
|
|
85
|
-
session_id: sessionId,
|
|
86
|
-
});
|
|
87
|
-
if (turn.toolRun?.toolUse) {
|
|
88
|
-
emitJson({
|
|
89
|
-
type: 'assistant',
|
|
90
|
-
message: {
|
|
91
|
-
type: 'message',
|
|
92
|
-
role: 'assistant',
|
|
93
|
-
content: [turn.toolRun.toolUse],
|
|
94
|
-
stop_reason: 'tool_use',
|
|
95
|
-
usage: estimateUsage(turn.prompt, JSON.stringify(turn.toolRun.toolUse.input)),
|
|
96
|
-
},
|
|
97
|
-
parent_tool_use_id: null,
|
|
98
|
-
session_id: sessionId,
|
|
99
|
-
});
|
|
100
|
-
for (const subagentMessage of turn.toolRun.subagentMessages ?? []) {
|
|
101
|
-
emitJson({
|
|
102
|
-
type: 'assistant',
|
|
103
|
-
message: {
|
|
104
|
-
type: 'message',
|
|
105
|
-
role: 'assistant',
|
|
106
|
-
content: assistantStreamContent(subagentMessage.text ?? '', parsed),
|
|
107
|
-
stop_reason: 'end_turn',
|
|
108
|
-
usage: estimateUsage(turn.prompt, subagentMessage.text ?? ''),
|
|
109
|
-
},
|
|
110
|
-
parent_tool_use_id: turn.toolRun.toolUse.id,
|
|
111
|
-
session_id: sessionId,
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
emitJson({
|
|
115
|
-
type: 'user',
|
|
116
|
-
message: {
|
|
117
|
-
role: 'user',
|
|
118
|
-
content: [{
|
|
119
|
-
type: 'tool_result',
|
|
120
|
-
tool_use_id: turn.toolRun.toolUse.id,
|
|
121
|
-
content: toolResultContent(turn.toolRun),
|
|
122
|
-
is_error: turn.toolRun.exitCode !== 0,
|
|
123
|
-
}],
|
|
124
|
-
},
|
|
125
|
-
parent_tool_use_id: toolRunParent(turn.toolRun, 'toolResultParentToolUseId', null),
|
|
126
|
-
session_id: sessionId,
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
emitJson({
|
|
130
|
-
type: 'assistant',
|
|
131
|
-
message: {
|
|
132
|
-
type: 'message',
|
|
133
|
-
role: 'assistant',
|
|
134
|
-
content: assistantStreamContent(turn.result, parsed),
|
|
135
|
-
stop_reason: 'end_turn',
|
|
136
|
-
usage: estimateUsage(turn.prompt, turn.result),
|
|
137
|
-
},
|
|
138
|
-
parent_tool_use_id: turn.toolRun ? toolRunParent(turn.toolRun, 'finalParentToolUseId', null) : null,
|
|
139
|
-
session_id: sessionId,
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
emitJson(streamJsonResultMessage({
|
|
143
|
-
started,
|
|
144
|
-
isError: errorSubtype !== undefined || turns.some((turn) => (turn.toolRun?.exitCode ?? 0) !== 0),
|
|
145
|
-
errorSubtype,
|
|
146
|
-
numTurns: streamJsonTurnCount(turns),
|
|
147
|
-
result: errorSubtype === 'error_max_turns' ? 'Maximum agent continuation turns exceeded' : result,
|
|
148
|
-
sessionId,
|
|
149
|
-
usage: estimateUsage(turns.map((turn) => turn.prompt).join('\n'), result),
|
|
150
|
-
permissionDenials: streamJsonPermissionDenials(turns.flatMap((turn) => turn.toolRun?.permissionDenials ?? [])),
|
|
151
|
-
}));
|
|
152
|
-
const thread = await persistThreadMessages(sessionId, turnMessages(turns), parsed.mode, options.thread, parsed);
|
|
153
|
-
notifyAgentComplete(parsed);
|
|
154
|
-
return thread;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
if (!options.silent) {
|
|
158
|
-
process.stdout.write(result.endsWith('\n') ? result : `${result}\n`);
|
|
159
|
-
}
|
|
160
|
-
const thread = await persistThreadMessages(sessionId, turnMessages(turns), parsed.mode, options.thread, parsed);
|
|
161
|
-
notifyAgentComplete(parsed);
|
|
162
|
-
return thread;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
async function runExecuteTurnSequence({ initialPrompt, stdin, sessionId, parsed, plugins, thread }) {
|
|
166
|
-
const turns = [];
|
|
167
|
-
let prompt = initialPrompt;
|
|
168
|
-
let continuationCount = 0;
|
|
169
|
-
let errorSubtype;
|
|
170
|
-
while (prompt !== undefined) {
|
|
171
|
-
const turnStdin = turns.length === 0 ? stdin : '';
|
|
172
|
-
const turn = await runExecuteTurn(prompt, turnStdin, sessionId, parsed, plugins, thread, turns);
|
|
173
|
-
turns.push(turn);
|
|
174
|
-
const nextPrompt = agentContinuationMessage(turn.endDecision);
|
|
175
|
-
if (!nextPrompt) break;
|
|
176
|
-
continuationCount += 1;
|
|
177
|
-
if (continuationCount > MAX_AGENT_CONTINUATIONS) {
|
|
178
|
-
errorSubtype = 'error_max_turns';
|
|
179
|
-
process.exitCode = 1;
|
|
180
|
-
break;
|
|
181
|
-
}
|
|
182
|
-
prompt = nextPrompt;
|
|
183
|
-
}
|
|
184
|
-
return { turns, errorSubtype };
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
async function runExecuteTurn(prompt, stdin, sessionId, parsed, plugins, thread, priorTurns = []) {
|
|
188
|
-
const messageId = createThreadMessageID();
|
|
189
|
-
const startDecision = await runPluginEventHandlers(plugins.handlers['agent.start'], {
|
|
190
|
-
message: prompt,
|
|
191
|
-
id: messageId,
|
|
192
|
-
thread: { id: sessionId },
|
|
193
|
-
});
|
|
194
|
-
const promptWithPluginContext = appendAgentStartMessage(prompt, startDecision);
|
|
195
|
-
const expandedPrompt = expandFileReferences(promptWithPluginContext, { parsed });
|
|
196
|
-
const guidancePrompt = expandAgentGuidanceReferences(expandedPrompt, parsed);
|
|
197
|
-
const turnPrompt = guidancePrompt ? `${guidancePrompt}\n${expandedPrompt}` : expandedPrompt;
|
|
198
|
-
const contextThread = threadContextForTurn(thread, sessionId, priorTurns);
|
|
199
|
-
const modelPrompt = contextThread
|
|
200
|
-
? threadContinuationPrompt(contextThread, turnPrompt)
|
|
201
|
-
: expandThreadReferences(turnPrompt);
|
|
202
|
-
const toolRun = await executePromptToolRequest(prompt, stdin, sessionId, parsed, plugins);
|
|
203
|
-
const result = toolRun?.output ?? fixtureAgentResponse(applySystemPrompt(modelPrompt, parsed), stdin);
|
|
204
|
-
const endDecision = await runPluginEventHandlers(plugins.handlers['agent.end'], {
|
|
205
|
-
message: prompt,
|
|
206
|
-
id: messageId,
|
|
207
|
-
result,
|
|
208
|
-
status: agentTurnStatus(toolRun),
|
|
209
|
-
messages: pluginMessagesForTurn(prompt, result, toolRun, messageId),
|
|
210
|
-
thread: { id: sessionId },
|
|
211
|
-
});
|
|
212
|
-
return { prompt, result, toolRun, endDecision };
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
function agentTurnStatus(toolRun) {
|
|
216
|
-
return (toolRun?.exitCode ?? 0) === 0 ? 'done' : 'error';
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
function createThreadMessageID() {
|
|
220
|
-
return `msg_${randomUUID()}`;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
function appendAgentStartMessage(prompt, decision = {}) {
|
|
224
|
-
validateAgentStartDecision(decision);
|
|
225
|
-
const content = decision.message?.content.trim() ?? '';
|
|
226
|
-
if (!content) return prompt;
|
|
227
|
-
return prompt ? `${prompt}\n\n${content}` : content;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
function validateAgentStartDecision(decision = {}) {
|
|
231
|
-
if (!decision || typeof decision !== 'object' || !Object.hasOwn(decision, 'message')) return;
|
|
232
|
-
if (Object.keys(decision).some((key) => key !== 'message')) {
|
|
233
|
-
throw new Error('plugin agent.start fields must match the documented shape');
|
|
234
|
-
}
|
|
235
|
-
if (!decision.message || typeof decision.message !== 'object' || Array.isArray(decision.message)) {
|
|
236
|
-
throw new Error('plugin agent.start message must be an object');
|
|
237
|
-
}
|
|
238
|
-
if (Object.keys(decision.message).some((key) => key !== 'content' && key !== 'display')) {
|
|
239
|
-
throw new Error('plugin agent.start message fields must be content and display');
|
|
240
|
-
}
|
|
241
|
-
if (typeof decision.message?.content !== 'string') {
|
|
242
|
-
throw new Error('plugin agent.start message content must be a string');
|
|
243
|
-
}
|
|
244
|
-
if (decision.message.display !== true) {
|
|
245
|
-
throw new Error('plugin agent.start message display must be true');
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
function threadContextForTurn(thread, sessionId, priorTurns = []) {
|
|
250
|
-
if (!thread && priorTurns.length === 0) return undefined;
|
|
251
|
-
return {
|
|
252
|
-
id: thread?.id ?? sessionId,
|
|
253
|
-
messages: [
|
|
254
|
-
...(thread?.messages ?? []),
|
|
255
|
-
...turnMessages(priorTurns),
|
|
256
|
-
],
|
|
257
|
-
};
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
function turnMessages(turns) {
|
|
261
|
-
return turns.flatMap((turn) => [
|
|
262
|
-
{ role: 'user', content: turn.prompt },
|
|
263
|
-
{ role: 'assistant', content: turn.result },
|
|
264
|
-
]);
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
function pluginMessagesForTurn(prompt, result, toolRun, messageId = createThreadMessageID()) {
|
|
268
|
-
const messages = [{ role: 'user', id: messageId, content: textContent(prompt) }];
|
|
269
|
-
if (toolRun?.toolUse) {
|
|
270
|
-
messages.push({ role: 'assistant', id: createThreadMessageID(), content: [toolRun.toolUse] });
|
|
271
|
-
messages.push({
|
|
272
|
-
role: 'user',
|
|
273
|
-
id: createThreadMessageID(),
|
|
274
|
-
content: [{
|
|
275
|
-
type: 'tool_result',
|
|
276
|
-
toolUseID: toolRun.toolUse.id,
|
|
277
|
-
output: toolResultContent(toolRun),
|
|
278
|
-
status: toolRun.exitCode !== 0 ? 'error' : 'done',
|
|
279
|
-
}],
|
|
280
|
-
});
|
|
281
|
-
}
|
|
282
|
-
messages.push({ role: 'assistant', id: createThreadMessageID(), content: textContent(result) });
|
|
283
|
-
return messages;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
function textContent(text) {
|
|
287
|
-
return [{ type: 'text', text: String(text ?? '') }];
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
function agentContinuationMessage(decision) {
|
|
291
|
-
validateAgentEndDecision(decision);
|
|
292
|
-
if (decision?.action !== 'continue') return undefined;
|
|
293
|
-
return typeof decision.userMessage === 'string' && decision.userMessage.trim()
|
|
294
|
-
? decision.userMessage
|
|
295
|
-
: undefined;
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
function validateAgentEndDecision(decision) {
|
|
299
|
-
if (decision === undefined) return;
|
|
300
|
-
if (decision?.action === 'allow') return;
|
|
301
|
-
if (!decision || typeof decision !== 'object' || decision.action !== 'continue') {
|
|
302
|
-
throw new Error('plugin agent.end action must be continue');
|
|
303
|
-
}
|
|
304
|
-
if (typeof decision.userMessage !== 'string') {
|
|
305
|
-
throw new Error('plugin agent.end continue userMessage must be a string');
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
async function runStreamJsonInputExecute(parsed, stdin, options = {}, started = Date.now()) {
|
|
310
|
-
const sessionId = options.thread?.id ?? `T-${randomUUID()}`;
|
|
311
|
-
const inputMessages = streamJsonInputMessages(parsed.prompt, stdin);
|
|
312
|
-
const combinedPrompt = inputMessages.map((message) => message.text).filter(Boolean).join('\n\n');
|
|
313
|
-
const plugins = await loadPlugins(process.cwd());
|
|
314
|
-
await runSessionStartHandlers(plugins, sessionId);
|
|
315
|
-
const activeMcpServers = listActiveMcpServerEntries(parsed, combinedPrompt);
|
|
316
|
-
const tools = [
|
|
317
|
-
...BUILTIN_TOOLS.map(([name]) => name),
|
|
318
|
-
...listToolboxTools(parsed).map((tool) => tool.name),
|
|
319
|
-
...plugins.tools.map((tool) => tool.name),
|
|
320
|
-
...(await discoverMcpToolRows(activeMcpServers)).map(([name]) => name),
|
|
321
|
-
].filter((name) => !isToolDisabled(name, toolKindForName(name), parsed));
|
|
322
|
-
emitJson({
|
|
323
|
-
type: 'system',
|
|
324
|
-
subtype: 'init',
|
|
325
|
-
cwd: displayCwd(),
|
|
326
|
-
session_id: sessionId,
|
|
327
|
-
tools,
|
|
328
|
-
mcp_servers: activeMcpServers.map(({ name }) => ({ name, status: 'connected' })),
|
|
329
|
-
agent_mode: parsed.mode,
|
|
330
|
-
reasoning_effort: reasoningEffortForMode(parsed.mode, parsed.reasoningEffort),
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
const transcript = [];
|
|
334
|
-
const persistedMessages = [];
|
|
335
|
-
const permissionDenials = [];
|
|
336
|
-
let result = '';
|
|
337
|
-
let parentToolUseId = null;
|
|
338
|
-
let numTurns = 0;
|
|
339
|
-
let isError = false;
|
|
340
|
-
|
|
341
|
-
for (const input of inputMessages) {
|
|
342
|
-
const messageId = createThreadMessageID();
|
|
343
|
-
const startDecision = await runPluginEventHandlers(plugins.handlers['agent.start'], {
|
|
344
|
-
message: input.text,
|
|
345
|
-
id: messageId,
|
|
346
|
-
thread: { id: sessionId },
|
|
347
|
-
});
|
|
348
|
-
const inputTextWithPluginContext = appendAgentStartMessage(input.text, startDecision);
|
|
349
|
-
const expandedPrompt = expandFileReferences(inputTextWithPluginContext, { parsed });
|
|
350
|
-
const guidancePrompt = expandAgentGuidanceReferences(expandedPrompt, parsed);
|
|
351
|
-
const turnPrompt = guidancePrompt ? `${guidancePrompt}\n${expandedPrompt}` : expandedPrompt;
|
|
352
|
-
const modelPrompt = modelPromptWithTranscript(turnPrompt, transcript, options.thread, parsed);
|
|
353
|
-
const toolRun = await executePromptToolRequest(input.text, '', sessionId, parsed, plugins);
|
|
354
|
-
result = toolRun?.output ?? fixtureAgentResponse(modelPrompt, '');
|
|
355
|
-
numTurns += streamJsonTurnCount([{ toolRun }]);
|
|
356
|
-
if (toolRun?.permissionDenials) permissionDenials.push(...toolRun.permissionDenials);
|
|
357
|
-
if ((toolRun?.exitCode ?? 0) !== 0) isError = true;
|
|
358
|
-
|
|
359
|
-
emitJson({
|
|
360
|
-
type: 'user',
|
|
361
|
-
...(input.steer ? { steer: true } : {}),
|
|
362
|
-
message: { role: 'user', content: streamJsonOutputUserContent(input.content) },
|
|
363
|
-
parent_tool_use_id: null,
|
|
364
|
-
session_id: sessionId,
|
|
365
|
-
});
|
|
366
|
-
if (toolRun?.toolUse) {
|
|
367
|
-
emitJson({
|
|
368
|
-
type: 'assistant',
|
|
369
|
-
message: {
|
|
370
|
-
type: 'message',
|
|
371
|
-
role: 'assistant',
|
|
372
|
-
content: [toolRun.toolUse],
|
|
373
|
-
stop_reason: 'tool_use',
|
|
374
|
-
usage: estimateUsage(input.text, JSON.stringify(toolRun.toolUse.input)),
|
|
375
|
-
},
|
|
376
|
-
parent_tool_use_id: null,
|
|
377
|
-
session_id: sessionId,
|
|
378
|
-
});
|
|
379
|
-
for (const subagentMessage of toolRun.subagentMessages ?? []) {
|
|
380
|
-
emitJson({
|
|
381
|
-
type: 'assistant',
|
|
382
|
-
message: {
|
|
383
|
-
type: 'message',
|
|
384
|
-
role: 'assistant',
|
|
385
|
-
content: assistantStreamContent(subagentMessage.text ?? '', parsed),
|
|
386
|
-
stop_reason: 'end_turn',
|
|
387
|
-
usage: estimateUsage(input.text, subagentMessage.text ?? ''),
|
|
388
|
-
},
|
|
389
|
-
parent_tool_use_id: toolRun.toolUse.id,
|
|
390
|
-
session_id: sessionId,
|
|
391
|
-
});
|
|
392
|
-
}
|
|
393
|
-
emitJson({
|
|
394
|
-
type: 'user',
|
|
395
|
-
message: {
|
|
396
|
-
role: 'user',
|
|
397
|
-
content: [{
|
|
398
|
-
type: 'tool_result',
|
|
399
|
-
tool_use_id: toolRun.toolUse.id,
|
|
400
|
-
content: toolResultContent(toolRun),
|
|
401
|
-
is_error: toolRun.exitCode !== 0,
|
|
402
|
-
}],
|
|
403
|
-
},
|
|
404
|
-
parent_tool_use_id: toolRunParent(toolRun, 'toolResultParentToolUseId', null),
|
|
405
|
-
session_id: sessionId,
|
|
406
|
-
});
|
|
407
|
-
parentToolUseId = toolRunParent(toolRun, 'finalParentToolUseId', null);
|
|
408
|
-
}
|
|
409
|
-
emitJson({
|
|
410
|
-
type: 'assistant',
|
|
411
|
-
message: {
|
|
412
|
-
type: 'message',
|
|
413
|
-
role: 'assistant',
|
|
414
|
-
content: assistantStreamContent(result, parsed),
|
|
415
|
-
stop_reason: 'end_turn',
|
|
416
|
-
usage: estimateUsage(input.text, result),
|
|
417
|
-
},
|
|
418
|
-
parent_tool_use_id: parentToolUseId,
|
|
419
|
-
session_id: sessionId,
|
|
420
|
-
});
|
|
421
|
-
await runPluginEventHandlers(plugins.handlers['agent.end'], {
|
|
422
|
-
message: input.text,
|
|
423
|
-
id: messageId,
|
|
424
|
-
result,
|
|
425
|
-
status: agentTurnStatus(toolRun),
|
|
426
|
-
messages: pluginMessagesForTurn(input.text, result, toolRun, messageId),
|
|
427
|
-
thread: { id: sessionId },
|
|
428
|
-
});
|
|
429
|
-
transcript.push({ user: input.text, assistant: result });
|
|
430
|
-
persistedMessages.push(
|
|
431
|
-
{ role: 'user', content: input.text },
|
|
432
|
-
{ role: 'assistant', content: result },
|
|
433
|
-
);
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
emitJson(streamJsonResultMessage({
|
|
437
|
-
started,
|
|
438
|
-
isError,
|
|
439
|
-
numTurns,
|
|
440
|
-
result,
|
|
441
|
-
sessionId,
|
|
442
|
-
usage: estimateUsage(combinedPrompt, result),
|
|
443
|
-
permissionDenials: streamJsonPermissionDenials(permissionDenials),
|
|
444
|
-
}));
|
|
445
|
-
const thread = await persistThreadMessages(sessionId, persistedMessages, parsed.mode, options.thread, parsed);
|
|
446
|
-
notifyAgentComplete(parsed);
|
|
447
|
-
return thread;
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
async function runSessionStartHandlers(plugins, sessionId) {
|
|
451
|
-
await runPluginEventHandlers(plugins.handlers['session.start'], {
|
|
452
|
-
thread: { id: sessionId },
|
|
453
|
-
});
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
function assistantStreamContent(result, parsed = {}) {
|
|
457
|
-
if (parsed.streamJsonThinking && readEffectiveSettings(parsed)['covenCode.thinking.enabled'] !== false) {
|
|
458
|
-
return [{ type: 'thinking', thinking: 'Using the local deterministic recreation.' }, { type: 'text', text: result }];
|
|
459
|
-
}
|
|
460
|
-
return [{ type: 'text', text: result }];
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
function modelPromptWithTranscript(turnPrompt, transcript, thread, parsed = {}) {
|
|
464
|
-
const context = transcript.length > 0
|
|
465
|
-
? `[conversation:${thread?.id ?? 'stream-json-input'}]\n${transcript
|
|
466
|
-
.map((entry) => `user: ${entry.user}\nassistant: ${entry.assistant}`)
|
|
467
|
-
.join('\n')}\n[/conversation]\n`
|
|
468
|
-
: '';
|
|
469
|
-
const prompt = `${context}${turnPrompt}`;
|
|
470
|
-
const modelPrompt = thread ? threadContinuationPrompt(thread, prompt) : expandThreadReferences(prompt);
|
|
471
|
-
return applySystemPrompt(modelPrompt, parsed);
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
function applySystemPrompt(prompt, parsed = {}) {
|
|
475
|
-
const systemPrompt = readEffectiveSettings(parsed)['covenCode.systemPrompt'];
|
|
476
|
-
if (typeof systemPrompt !== 'string' || systemPrompt.trim() === '') return prompt;
|
|
477
|
-
return `[system]\n${systemPrompt.trim()}\n[/system]\n${prompt}`;
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
function expandAgentGuidanceReferences(expandedPrompt, parsed = {}) {
|
|
481
|
-
const readFiles = filesInPrompt(expandedPrompt);
|
|
482
|
-
const agentFiles = [
|
|
483
|
-
...discoverAgentFiles(process.cwd()),
|
|
484
|
-
...discoverSubtreeAgentFiles(readFiles),
|
|
485
|
-
];
|
|
486
|
-
const blocks = [...new Set(agentFiles)]
|
|
487
|
-
.map((filePath) => guidanceBlock(filePath, readFiles, parsed))
|
|
488
|
-
.filter(Boolean);
|
|
489
|
-
return blocks.join('\n');
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
function discoverSubtreeAgentFiles(readFiles) {
|
|
493
|
-
const cwd = path.resolve(process.cwd());
|
|
494
|
-
const files = [];
|
|
495
|
-
for (const filePath of readFiles) {
|
|
496
|
-
let current = path.dirname(path.resolve(filePath));
|
|
497
|
-
while (current.startsWith(cwd) && current !== cwd && current !== path.dirname(current)) {
|
|
498
|
-
const guidance = firstGuidanceInDir(current);
|
|
499
|
-
if (guidance) files.push(guidance);
|
|
500
|
-
current = path.dirname(current);
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
return files;
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
function guidanceBlock(filePath, readFiles, parsed = {}) {
|
|
507
|
-
try {
|
|
508
|
-
const content = readFileSync(filePath, 'utf8');
|
|
509
|
-
const expandable = stripFencedCodeBlocks(content);
|
|
510
|
-
const expanded = expandFileReferences(expandable, {
|
|
511
|
-
baseDir: path.dirname(filePath),
|
|
512
|
-
parsed,
|
|
513
|
-
includeFile: (mentionedFile, mentionedContent) => guidanceMentionApplies(mentionedFile, mentionedContent, readFiles),
|
|
514
|
-
});
|
|
515
|
-
return `[agent:${filePath}]\n${expanded}\n[/agent]`;
|
|
516
|
-
} catch {
|
|
517
|
-
return undefined;
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
function filesInPrompt(prompt) {
|
|
522
|
-
return [...prompt.matchAll(/\[file:([^\]\r\n]+)\]/g)].map((match) => path.resolve(match[1]));
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
function guidanceMentionApplies(mentionedFile, content, readFiles) {
|
|
526
|
-
const globs = frontmatterGlobs(content);
|
|
527
|
-
if (globs.length === 0) return true;
|
|
528
|
-
return readFiles.some((filePath) => globs.some((glob) => guidanceGlobMatches(glob, filePath, mentionedFile)));
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
function frontmatterGlobs(content) {
|
|
532
|
-
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
533
|
-
if (!match) return [];
|
|
534
|
-
const frontmatter = match[1];
|
|
535
|
-
const inline = frontmatter.match(/^globs:\s*\[(.*)\]\s*$/m);
|
|
536
|
-
if (inline) {
|
|
537
|
-
return inline[1]
|
|
538
|
-
.split(',')
|
|
539
|
-
.map((entry) => entry.trim().replace(/^["']|["']$/g, ''))
|
|
540
|
-
.filter(Boolean);
|
|
541
|
-
}
|
|
542
|
-
const lines = frontmatter.split(/\r?\n/);
|
|
543
|
-
const globs = [];
|
|
544
|
-
let inGlobs = false;
|
|
545
|
-
for (const line of lines) {
|
|
546
|
-
if (/^globs:\s*$/.test(line)) {
|
|
547
|
-
inGlobs = true;
|
|
548
|
-
continue;
|
|
549
|
-
}
|
|
550
|
-
if (inGlobs) {
|
|
551
|
-
const entry = line.match(/^\s*-\s*(.+?)\s*$/);
|
|
552
|
-
if (entry) globs.push(entry[1].replace(/^["']|["']$/g, ''));
|
|
553
|
-
else if (/^\S/.test(line)) break;
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
return globs.filter(Boolean);
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
function guidanceGlobMatches(pattern, readFile, guidanceFile) {
|
|
560
|
-
const normalized = path.normalize(pattern);
|
|
561
|
-
if (normalized.startsWith(`..${path.sep}`) || normalized.startsWith(`.${path.sep}`)) {
|
|
562
|
-
const absolutePattern = path.resolve(path.dirname(guidanceFile), normalized);
|
|
563
|
-
return globToRegex(path.normalize(absolutePattern)).test(path.normalize(readFile));
|
|
564
|
-
}
|
|
565
|
-
const relative = path.relative(process.cwd(), readFile);
|
|
566
|
-
const candidates = normalized.startsWith(`**${path.sep}`)
|
|
567
|
-
? [normalized]
|
|
568
|
-
: [normalized, `**${path.sep}${normalized}`];
|
|
569
|
-
return candidates.some((candidate) => globToRegex(path.normalize(candidate)).test(path.normalize(relative)));
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
function stripFencedCodeBlocks(text) {
|
|
573
|
-
return text.replace(/```[\s\S]*?```/g, '');
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
function combinePrompt(prompt, stdin, streamJsonInput) {
|
|
577
|
-
if (streamJsonInput && stdin.trim()) {
|
|
578
|
-
return streamJsonInputMessages(prompt, stdin)
|
|
579
|
-
.map((message) => message.text)
|
|
580
|
-
.filter(Boolean)
|
|
581
|
-
.join('\n');
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
const processedStdin = stdin;
|
|
585
|
-
if (prompt && processedStdin.trim()) return `${prompt}\n\n${processedStdin.trimEnd()}`;
|
|
586
|
-
if (prompt) return prompt;
|
|
587
|
-
return processedStdin.trimEnd();
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
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
|
-
}
|