@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/plugins/helpers.mjs
DELETED
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
import { spawnSync } from 'node:child_process';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { fileURLToPath } from 'node:url';
|
|
4
|
-
import { shellQuote } from '../util/shell.mjs';
|
|
5
|
-
|
|
6
|
-
export function filesModifiedByToolCall(event = {}) {
|
|
7
|
-
const input = event.input ?? {};
|
|
8
|
-
if ((event.tool === 'edit_file' || event.tool === 'create_file' || event.tool === 'undo_edit') && input.path) {
|
|
9
|
-
return [path.resolve(process.cwd(), String(input.path))];
|
|
10
|
-
}
|
|
11
|
-
if (event.tool === 'apply_patch' && typeof input.patch === 'string') {
|
|
12
|
-
return applyPatchModifiedFiles(input.patch);
|
|
13
|
-
}
|
|
14
|
-
const shellCommand = event.tool === 'Bash' || event.tool === 'shell_command'
|
|
15
|
-
? input.command ?? input.cmd
|
|
16
|
-
: undefined;
|
|
17
|
-
if (typeof shellCommand === 'string') {
|
|
18
|
-
const files = sedInPlaceModifiedFiles(shellCommand);
|
|
19
|
-
return files.length > 0 ? files : null;
|
|
20
|
-
}
|
|
21
|
-
return null;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function applyPatchModifiedFiles(patch) {
|
|
25
|
-
return [...patch.matchAll(/^\*\*\* (?:Add|Update|Delete) File: (.+)$/gm)]
|
|
26
|
-
.map((match) => path.resolve(process.cwd(), match[1].trim()))
|
|
27
|
-
.filter(Boolean);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function sedInPlaceModifiedFiles(command) {
|
|
31
|
-
const tokens = shellWords(command);
|
|
32
|
-
if (tokens[0] !== 'sed') return [];
|
|
33
|
-
const files = [];
|
|
34
|
-
let sawInPlace = false;
|
|
35
|
-
for (let index = 1; index < tokens.length; index += 1) {
|
|
36
|
-
const token = tokens[index];
|
|
37
|
-
if (token === '-i') {
|
|
38
|
-
sawInPlace = true;
|
|
39
|
-
if (tokens[index + 1] === '' || (tokens[index + 1] && !tokens[index + 1].startsWith('-') && !looksLikeSedScript(tokens[index + 1]))) {
|
|
40
|
-
index += 1;
|
|
41
|
-
}
|
|
42
|
-
continue;
|
|
43
|
-
}
|
|
44
|
-
if (token.startsWith('-i')) {
|
|
45
|
-
sawInPlace = true;
|
|
46
|
-
continue;
|
|
47
|
-
}
|
|
48
|
-
if (!sawInPlace || token.startsWith('-') || looksLikeSedScript(token)) continue;
|
|
49
|
-
files.push(path.resolve(process.cwd(), token));
|
|
50
|
-
}
|
|
51
|
-
return files;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function looksLikeSedScript(token = '') {
|
|
55
|
-
return /^[a-zA-Z][^/|,;]*[\/|,;]/.test(token);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export function shellWords(command) {
|
|
59
|
-
const words = [];
|
|
60
|
-
let current = '';
|
|
61
|
-
let quote = '';
|
|
62
|
-
let escaping = false;
|
|
63
|
-
let tokenStarted = false;
|
|
64
|
-
for (const char of command) {
|
|
65
|
-
if (escaping) {
|
|
66
|
-
current += char;
|
|
67
|
-
escaping = false;
|
|
68
|
-
tokenStarted = true;
|
|
69
|
-
continue;
|
|
70
|
-
}
|
|
71
|
-
if (char === '\\' && quote !== "'") {
|
|
72
|
-
escaping = true;
|
|
73
|
-
tokenStarted = true;
|
|
74
|
-
continue;
|
|
75
|
-
}
|
|
76
|
-
if (quote) {
|
|
77
|
-
if (char === quote) quote = '';
|
|
78
|
-
else {
|
|
79
|
-
current += char;
|
|
80
|
-
tokenStarted = true;
|
|
81
|
-
}
|
|
82
|
-
continue;
|
|
83
|
-
}
|
|
84
|
-
if (char === '"' || char === "'") {
|
|
85
|
-
quote = char;
|
|
86
|
-
tokenStarted = true;
|
|
87
|
-
continue;
|
|
88
|
-
}
|
|
89
|
-
if (/\s/.test(char)) {
|
|
90
|
-
if (tokenStarted) {
|
|
91
|
-
words.push(current);
|
|
92
|
-
current = '';
|
|
93
|
-
tokenStarted = false;
|
|
94
|
-
}
|
|
95
|
-
continue;
|
|
96
|
-
}
|
|
97
|
-
current += char;
|
|
98
|
-
tokenStarted = true;
|
|
99
|
-
}
|
|
100
|
-
if (tokenStarted) words.push(current);
|
|
101
|
-
return words;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
export function toolCallsInMessages(messages = []) {
|
|
105
|
-
const calls = [];
|
|
106
|
-
const resultsById = new Map();
|
|
107
|
-
for (const message of messages) {
|
|
108
|
-
const blocks = Array.isArray(message.content) ? message.content : [];
|
|
109
|
-
for (const block of blocks) {
|
|
110
|
-
if (block?.type === 'tool_use') {
|
|
111
|
-
calls.push({
|
|
112
|
-
toolUseID: block.id,
|
|
113
|
-
tool: block.name,
|
|
114
|
-
input: block.input ?? {},
|
|
115
|
-
});
|
|
116
|
-
continue;
|
|
117
|
-
}
|
|
118
|
-
if (block?.type !== 'tool_result') continue;
|
|
119
|
-
const toolUseID = block.toolUseID ?? block.tool_use_id;
|
|
120
|
-
if (typeof toolUseID !== 'string' || !toolUseID) continue;
|
|
121
|
-
const status = normalizeToolResultStatus(block);
|
|
122
|
-
if (!status) continue;
|
|
123
|
-
resultsById.set(toolUseID, {
|
|
124
|
-
toolUseID,
|
|
125
|
-
status,
|
|
126
|
-
output: normalizeToolResultOutput(block),
|
|
127
|
-
...(status === 'error' && typeof block.content === 'string' ? { error: block.content } : {}),
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
return calls.flatMap((call) => {
|
|
133
|
-
const result = resultsById.get(call.toolUseID);
|
|
134
|
-
if (!result) return [];
|
|
135
|
-
return [{
|
|
136
|
-
call,
|
|
137
|
-
result: {
|
|
138
|
-
...result,
|
|
139
|
-
tool: call.tool,
|
|
140
|
-
input: call.input,
|
|
141
|
-
},
|
|
142
|
-
}];
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
function normalizeToolResultStatus(block = {}) {
|
|
147
|
-
if (block.status === 'done' || block.status === 'error' || block.status === 'cancelled') return block.status;
|
|
148
|
-
if (typeof block.is_error === 'boolean') return block.is_error ? 'error' : 'done';
|
|
149
|
-
if (typeof block.isError === 'boolean') return block.isError ? 'error' : 'done';
|
|
150
|
-
return undefined;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
function normalizeToolResultOutput(block = {}) {
|
|
154
|
-
if (Object.hasOwn(block, 'output')) return block.output;
|
|
155
|
-
if (Object.hasOwn(block, 'content')) return block.content;
|
|
156
|
-
return undefined;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
export function filePathFromURI(uri) {
|
|
160
|
-
if (!uri) return undefined;
|
|
161
|
-
try {
|
|
162
|
-
return fileURLToPath(String(uri));
|
|
163
|
-
} catch {
|
|
164
|
-
return undefined;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
export function isPluginUINotAvailableError(error) {
|
|
169
|
-
if (!error || typeof error !== 'object') return false;
|
|
170
|
-
if (error.name === 'PluginUINotAvailableError') return true;
|
|
171
|
-
if (error.code === 'PLUGIN_UI_NOT_AVAILABLE') return true;
|
|
172
|
-
return /\b(?:no|plugin)\s+plugin\s+ui\s+(?:is\s+)?available\b/i.test(String(error.message ?? ''))
|
|
173
|
-
|| /\bplugin\s+ui\s+(?:is\s+)?not\s+available\b/i.test(String(error.message ?? ''));
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
export function runPluginShell(cwd, strings, values) {
|
|
177
|
-
const command = strings.reduce((text, part, index) => {
|
|
178
|
-
return `${text}${part}${index < values.length ? shellQuote(values[index]) : ''}`;
|
|
179
|
-
}, '');
|
|
180
|
-
const result = spawnSync(command, { cwd, shell: true, encoding: 'utf8' });
|
|
181
|
-
return {
|
|
182
|
-
exitCode: result.status ?? 0,
|
|
183
|
-
stdout: result.stdout ?? '',
|
|
184
|
-
stderr: result.stderr ?? '',
|
|
185
|
-
status: result.status ?? 0,
|
|
186
|
-
};
|
|
187
|
-
}
|
|
@@ -1,198 +0,0 @@
|
|
|
1
|
-
import { latestActiveThread, readThread, writeThread } from '../threads/store.mjs';
|
|
2
|
-
import { createSubscription, validateObservableSubscriber } from './configuration.mjs';
|
|
3
|
-
import {
|
|
4
|
-
validatePluginConfirmOptions,
|
|
5
|
-
validatePluginInputOptions,
|
|
6
|
-
validatePluginSelectOptions,
|
|
7
|
-
validatePluginStatusItemValue,
|
|
8
|
-
validatePluginSystemOpenTarget,
|
|
9
|
-
} from './validators.mjs';
|
|
10
|
-
|
|
11
|
-
export function createPluginLogger() {
|
|
12
|
-
return {
|
|
13
|
-
log() {},
|
|
14
|
-
};
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function createPluginSystem(recordOpen = () => {}) {
|
|
18
|
-
const covenCodeURL = pluginCovenCodeURL();
|
|
19
|
-
return {
|
|
20
|
-
open: async (target) => {
|
|
21
|
-
validatePluginSystemOpenTarget(target);
|
|
22
|
-
recordOpen(target);
|
|
23
|
-
},
|
|
24
|
-
covenCodeURL,
|
|
25
|
-
executor: { kind: 'local' },
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function pluginCovenCodeURL() {
|
|
30
|
-
try {
|
|
31
|
-
return new URL(process.env.COVEN_CODE_URL || 'https://coven-code.local');
|
|
32
|
-
} catch {
|
|
33
|
-
return new URL('https://coven-code.local');
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export function createPluginAI() {
|
|
38
|
-
return {
|
|
39
|
-
ask: async (question) => {
|
|
40
|
-
if (typeof question !== 'string') {
|
|
41
|
-
throw new Error('plugin ai.ask question must be a string');
|
|
42
|
-
}
|
|
43
|
-
return classifyPluginQuestion(question);
|
|
44
|
-
},
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function classifyPluginQuestion(question = '') {
|
|
49
|
-
const text = String(question).toLowerCase();
|
|
50
|
-
const matches = [
|
|
51
|
-
['deploy', /\bdeploy(?:ment)?\b/],
|
|
52
|
-
['production', /\b(?:prod|production)\b/],
|
|
53
|
-
['destructive', /\b(?:destroy|delete|remove|reset|force push|drop)\b/],
|
|
54
|
-
['risky', /\b(?:risky|danger|unsafe|secret|password|token)\b/],
|
|
55
|
-
].filter(([, pattern]) => pattern.test(text)).map(([label]) => label);
|
|
56
|
-
|
|
57
|
-
if (matches.length > 0) {
|
|
58
|
-
return {
|
|
59
|
-
result: 'yes',
|
|
60
|
-
probability: 0.9,
|
|
61
|
-
reason: `local keyword classifier matched: ${matches.join(', ')}`,
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
if (/\b(?:maybe|unclear|uncertain|unknown|not sure)\b/.test(text)) {
|
|
66
|
-
return {
|
|
67
|
-
result: 'uncertain',
|
|
68
|
-
probability: 0.5,
|
|
69
|
-
reason: 'local keyword classifier found ambiguity markers',
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
return {
|
|
74
|
-
result: 'no',
|
|
75
|
-
probability: 0.1,
|
|
76
|
-
reason: 'local keyword classifier found no risky keywords',
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export function createPluginExperimentalApi(runtime) {
|
|
81
|
-
return {
|
|
82
|
-
createStatusItem(initial) {
|
|
83
|
-
const item = { value: validatePluginStatusItemValue(initial) };
|
|
84
|
-
runtime.statusItems.push(item);
|
|
85
|
-
return {
|
|
86
|
-
update(value) {
|
|
87
|
-
item.value = validatePluginStatusItemValue(value);
|
|
88
|
-
},
|
|
89
|
-
...createSubscription(() => {
|
|
90
|
-
runtime.statusItems = runtime.statusItems.filter((entry) => entry !== item);
|
|
91
|
-
}),
|
|
92
|
-
};
|
|
93
|
-
},
|
|
94
|
-
activeThread: createActiveThreadObservable(),
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function createActiveThreadObservable() {
|
|
99
|
-
const subscribers = [];
|
|
100
|
-
const observableSymbol = Symbol.observable ?? Symbol.for('observable');
|
|
101
|
-
const observable = {
|
|
102
|
-
get current() {
|
|
103
|
-
const thread = latestActiveThread();
|
|
104
|
-
return thread ? { id: thread.id } : null;
|
|
105
|
-
},
|
|
106
|
-
subscribe(observer) {
|
|
107
|
-
validateObservableSubscriber(observer);
|
|
108
|
-
subscribers.push(observer);
|
|
109
|
-
return createSubscription(() => {
|
|
110
|
-
const index = subscribers.indexOf(observer);
|
|
111
|
-
if (index >= 0) subscribers.splice(index, 1);
|
|
112
|
-
});
|
|
113
|
-
},
|
|
114
|
-
pipe(op) {
|
|
115
|
-
return op(observable);
|
|
116
|
-
},
|
|
117
|
-
[observableSymbol]() {
|
|
118
|
-
return observable;
|
|
119
|
-
},
|
|
120
|
-
};
|
|
121
|
-
return observable;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
export function createPluginThreadContext(threadId) {
|
|
125
|
-
const thread = threadId ? readThread(threadId) ?? createPendingThread(threadId) : latestActiveThread();
|
|
126
|
-
if (!thread) return undefined;
|
|
127
|
-
return {
|
|
128
|
-
id: thread.id,
|
|
129
|
-
append: async (entries = []) => {
|
|
130
|
-
const messages = pluginThreadEntriesToMessages(entries);
|
|
131
|
-
if (messages.length === 0) return;
|
|
132
|
-
thread.messages.push(...messages);
|
|
133
|
-
thread.updatedAt = new Date().toISOString();
|
|
134
|
-
await writeThread(thread);
|
|
135
|
-
},
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
function createPendingThread(threadId) {
|
|
140
|
-
const now = new Date().toISOString();
|
|
141
|
-
return {
|
|
142
|
-
id: threadId,
|
|
143
|
-
title: '(pending thread)',
|
|
144
|
-
cwd: process.cwd(),
|
|
145
|
-
mode: 'smart',
|
|
146
|
-
visibility: 'private',
|
|
147
|
-
labels: [],
|
|
148
|
-
archived: false,
|
|
149
|
-
createdAt: now,
|
|
150
|
-
updatedAt: now,
|
|
151
|
-
messages: [],
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
function pluginThreadEntriesToMessages(entries) {
|
|
156
|
-
if (!Array.isArray(entries)) {
|
|
157
|
-
throw new Error('plugin thread append expects an array of user-message entries');
|
|
158
|
-
}
|
|
159
|
-
return entries.map((entry = {}) => {
|
|
160
|
-
if (entry.type !== 'user-message') {
|
|
161
|
-
throw new Error('plugin thread append only supports user-message entries');
|
|
162
|
-
}
|
|
163
|
-
if (typeof entry.content !== 'string') {
|
|
164
|
-
throw new Error('plugin thread append content must be a string');
|
|
165
|
-
}
|
|
166
|
-
return { role: 'user', content: entry.content };
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
function pluginEnvBoolean(name) {
|
|
171
|
-
const value = process.env[name];
|
|
172
|
-
if (!value) return false;
|
|
173
|
-
return ['1', 'true', 'yes', 'y', 'allow', 'confirm', 'ok'].includes(value.toLowerCase());
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
export function pluginEnvConfirm(options) {
|
|
177
|
-
validatePluginConfirmOptions(options);
|
|
178
|
-
return pluginEnvBoolean('COVEN_CODE_PLUGIN_CONFIRM');
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
export function pluginEnvInput(inputOptions) {
|
|
182
|
-
validatePluginInputOptions(inputOptions);
|
|
183
|
-
const value = process.env.COVEN_CODE_PLUGIN_INPUT;
|
|
184
|
-
if (value !== undefined && value !== '') return value;
|
|
185
|
-
if (inputOptions.initialValue !== undefined) return inputOptions.initialValue;
|
|
186
|
-
return value;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
export function pluginEnvSelection(selectOptions) {
|
|
190
|
-
validatePluginSelectOptions(selectOptions);
|
|
191
|
-
const requested = process.env.COVEN_CODE_PLUGIN_SELECT;
|
|
192
|
-
if (!requested) return undefined;
|
|
193
|
-
const options = selectOptions.options;
|
|
194
|
-
for (const option of options) {
|
|
195
|
-
if (typeof option === 'string' && option === requested) return option;
|
|
196
|
-
}
|
|
197
|
-
return requested;
|
|
198
|
-
}
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
const PLUGIN_TOOL_NAME_PATTERN = /^[a-zA-Z0-9_-]+$/;
|
|
2
|
-
|
|
3
|
-
export function isPlainObject(value) {
|
|
4
|
-
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
export function removeFirst(entries, value) {
|
|
8
|
-
const index = entries.indexOf(value);
|
|
9
|
-
if (index >= 0) entries.splice(index, 1);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function validatePluginToolName(name) {
|
|
13
|
-
if (typeof name !== 'string' || !PLUGIN_TOOL_NAME_PATTERN.test(name)) {
|
|
14
|
-
throw new Error(`plugin tool name must match ^[a-zA-Z0-9_-]+$: ${String(name ?? '')}`);
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function validatePluginToolDefinition(tool) {
|
|
19
|
-
const name = String(tool?.name ?? '');
|
|
20
|
-
if (typeof tool?.description !== 'string' || tool.description.trim() === '') {
|
|
21
|
-
throw new Error(`plugin tool description is required: ${name}`);
|
|
22
|
-
}
|
|
23
|
-
if (tool?.inputSchema?.type !== 'object') {
|
|
24
|
-
throw new Error(`plugin tool inputSchema.type must be object: ${name}`);
|
|
25
|
-
}
|
|
26
|
-
if (
|
|
27
|
-
tool.inputSchema.properties !== undefined &&
|
|
28
|
-
(!isPlainObject(tool.inputSchema.properties) ||
|
|
29
|
-
Object.values(tool.inputSchema.properties).some((property) => !isPlainObject(property)))
|
|
30
|
-
) {
|
|
31
|
-
throw new Error(`plugin tool inputSchema.properties values must be objects: ${name}`);
|
|
32
|
-
}
|
|
33
|
-
if (
|
|
34
|
-
tool.inputSchema.required !== undefined &&
|
|
35
|
-
(!Array.isArray(tool.inputSchema.required) ||
|
|
36
|
-
tool.inputSchema.required.some((propertyName) => typeof propertyName !== 'string'))
|
|
37
|
-
) {
|
|
38
|
-
throw new Error(`plugin tool inputSchema.required must be strings: ${name}`);
|
|
39
|
-
}
|
|
40
|
-
if (typeof tool?.execute !== 'function') {
|
|
41
|
-
throw new Error(`plugin tool execute handler is required: ${name}`);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function validatePluginCommand(name, metadata) {
|
|
46
|
-
if (typeof metadata?.title !== 'string' || metadata.title.trim() === '') {
|
|
47
|
-
throw new Error(`plugin command title is required: ${String(name ?? '')}`);
|
|
48
|
-
}
|
|
49
|
-
if (metadata.category !== undefined && typeof metadata.category !== 'string') {
|
|
50
|
-
throw new Error(`plugin command category must be a string: ${String(name ?? '')}`);
|
|
51
|
-
}
|
|
52
|
-
if (metadata.description !== undefined && typeof metadata.description !== 'string') {
|
|
53
|
-
throw new Error(`plugin command description must be a string: ${String(name ?? '')}`);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export function validateCommandAvailability(commandName, availability) {
|
|
58
|
-
const type = availability?.type;
|
|
59
|
-
if (type !== 'enabled' && type !== 'disabled' && type !== 'hidden') {
|
|
60
|
-
throw new Error(`plugin command availability must be enabled, disabled, or hidden: ${String(commandName ?? '')}`);
|
|
61
|
-
}
|
|
62
|
-
if (type === 'disabled' && typeof availability.reason !== 'string') {
|
|
63
|
-
throw new Error(`plugin command disabled availability reason is required: ${String(commandName ?? '')}`);
|
|
64
|
-
}
|
|
65
|
-
const keys = Object.keys(availability);
|
|
66
|
-
const allowedKeys = type === 'disabled' ? ['reason', 'type'] : ['type'];
|
|
67
|
-
if (keys.some((key) => !allowedKeys.includes(key))) {
|
|
68
|
-
throw new Error(`plugin command availability fields must match the documented union: ${String(commandName ?? '')}`);
|
|
69
|
-
}
|
|
70
|
-
return availability;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export function validatePluginSystemOpenTarget(target) {
|
|
74
|
-
if (typeof target !== 'string' && !(target instanceof URL)) {
|
|
75
|
-
throw new Error('plugin system open target must be a string or URL');
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
export function validatePluginStatusItemValue(value) {
|
|
80
|
-
if (value === undefined) return undefined;
|
|
81
|
-
for (const key of Object.keys(value ?? {})) {
|
|
82
|
-
if (key !== 'text' && key !== 'url') {
|
|
83
|
-
throw new Error('plugin status item fields must be text or url');
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
if (typeof value?.text !== 'string' || value.text.trim() === '') {
|
|
87
|
-
throw new Error('plugin status item text is required');
|
|
88
|
-
}
|
|
89
|
-
if (value.url !== undefined && typeof value.url !== 'string') {
|
|
90
|
-
throw new Error('plugin status item url must be a string');
|
|
91
|
-
}
|
|
92
|
-
return value;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export function validatePluginConfirmOptions(options) {
|
|
96
|
-
if (!options || typeof options !== 'object' || Array.isArray(options)) {
|
|
97
|
-
throw new Error('plugin confirm options must be an object');
|
|
98
|
-
}
|
|
99
|
-
if (typeof options?.title !== 'string' || options.title.trim() === '') {
|
|
100
|
-
throw new Error('plugin confirm title is required');
|
|
101
|
-
}
|
|
102
|
-
for (const key of ['message', 'confirmButtonText']) {
|
|
103
|
-
if (options[key] !== undefined && typeof options[key] !== 'string') {
|
|
104
|
-
throw new Error(`plugin confirm ${key} must be a string`);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
export function validatePluginNotifyMessage(message) {
|
|
110
|
-
if (typeof message !== 'string') {
|
|
111
|
-
throw new Error('plugin notify message must be a string');
|
|
112
|
-
}
|
|
113
|
-
return message;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
export function validatePluginInputOptions(options) {
|
|
117
|
-
if (!options || typeof options !== 'object' || Array.isArray(options)) {
|
|
118
|
-
throw new Error('plugin input options must be an object');
|
|
119
|
-
}
|
|
120
|
-
for (const key of ['title', 'helpText', 'initialValue', 'submitButtonText']) {
|
|
121
|
-
if (options[key] !== undefined && typeof options[key] !== 'string') {
|
|
122
|
-
throw new Error(`plugin input ${key} must be a string`);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
export function validatePluginSelectOptions(options) {
|
|
128
|
-
if (!options || typeof options !== 'object' || Array.isArray(options)) {
|
|
129
|
-
throw new Error('plugin select options must be an object');
|
|
130
|
-
}
|
|
131
|
-
if (typeof options?.title !== 'string' || options.title.trim() === '') {
|
|
132
|
-
throw new Error('plugin select title is required');
|
|
133
|
-
}
|
|
134
|
-
for (const key of ['message', 'initialValue']) {
|
|
135
|
-
if (options[key] !== undefined && typeof options[key] !== 'string') {
|
|
136
|
-
throw new Error(`plugin select ${key} must be a string`);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
if (!Array.isArray(options.options) || options.options.some((option) => typeof option !== 'string')) {
|
|
140
|
-
throw new Error('plugin select options must be strings');
|
|
141
|
-
}
|
|
142
|
-
}
|
package/src/sdk-execute.mjs
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import { normalizeThreadVisibility } from './threads/store.mjs';
|
|
2
|
-
|
|
3
|
-
export function executeArgs(prompt, options, isStreamInput) {
|
|
4
|
-
const args = ['--execute'];
|
|
5
|
-
if (!isStreamInput) args.push(String(prompt));
|
|
6
|
-
args.push(options.thinking ? '--stream-json-thinking' : '--stream-json');
|
|
7
|
-
if (isStreamInput) args.push('--stream-json-input');
|
|
8
|
-
if (options.dangerouslyAllowAll) args.push('--dangerously-allow-all');
|
|
9
|
-
if (options.archive) args.push('--archive');
|
|
10
|
-
if (options.mode) args.push('--mode', options.mode);
|
|
11
|
-
if (options.reasoningEffort) args.push('--reasoning-effort', options.reasoningEffort);
|
|
12
|
-
const visibility = normalizeSdkThreadVisibility(options.visibility) ?? (options.continue ? undefined : 'workspace');
|
|
13
|
-
if (visibility) args.push('--visibility', visibility);
|
|
14
|
-
if (options.settingsFile) args.push('--settings-file', options.settingsFile);
|
|
15
|
-
if (options.continue) args.push('--continue', ...(typeof options.continue === 'string' ? [options.continue] : []));
|
|
16
|
-
if (options.toolbox) args.push('--toolbox', options.toolbox);
|
|
17
|
-
if (options.skills) args.push('--skills', options.skills);
|
|
18
|
-
if (options.mcpConfig) args.push('--mcp-config', typeof options.mcpConfig === 'string' ? options.mcpConfig : JSON.stringify(options.mcpConfig));
|
|
19
|
-
for (const label of options.labels ?? []) args.push('--label', label);
|
|
20
|
-
return args;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function normalizeSdkThreadVisibility(visibility) {
|
|
24
|
-
if (visibility === 'team') return 'workspace';
|
|
25
|
-
return normalizeThreadVisibility(visibility);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export async function writeStreamInput(child, prompt, signal) {
|
|
29
|
-
const iterator = prompt[Symbol.asyncIterator]();
|
|
30
|
-
try {
|
|
31
|
-
while (true) {
|
|
32
|
-
const { value: message, done } = await nextWithAbort(iterator, signal);
|
|
33
|
-
if (done) break;
|
|
34
|
-
if (child.stdin.destroyed) break;
|
|
35
|
-
child.stdin.write(`${JSON.stringify(message)}\n`);
|
|
36
|
-
}
|
|
37
|
-
child.stdin.end();
|
|
38
|
-
} catch (error) {
|
|
39
|
-
if (!isAbortError(error)) throw error;
|
|
40
|
-
child.stdin.destroy();
|
|
41
|
-
try {
|
|
42
|
-
iterator.return?.().catch?.(() => {});
|
|
43
|
-
} catch {
|
|
44
|
-
// Best-effort generator cleanup; abort should not wait on a slow prompt source.
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function nextWithAbort(iterator, signal) {
|
|
50
|
-
if (!signal) return iterator.next();
|
|
51
|
-
if (signal.aborted) return Promise.reject(abortReason(signal));
|
|
52
|
-
return new Promise((resolve, reject) => {
|
|
53
|
-
const onAbort = () => reject(abortReason(signal));
|
|
54
|
-
signal.addEventListener('abort', onAbort, { once: true });
|
|
55
|
-
iterator.next().then(resolve, reject).finally(() => {
|
|
56
|
-
signal.removeEventListener('abort', onAbort);
|
|
57
|
-
});
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function abortReason(signal) {
|
|
62
|
-
return signal.reason instanceof Error ? signal.reason : new Error('aborted');
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function isAbortError(error) {
|
|
66
|
-
return error?.name === 'AbortError' || error?.code === 'ABORT_ERR' || /abort/i.test(error?.message ?? '');
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export async function closeStdin(child) {
|
|
70
|
-
child.stdin.end();
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export function waitForExit(child) {
|
|
74
|
-
return new Promise((resolve, reject) => {
|
|
75
|
-
child.on('error', reject);
|
|
76
|
-
child.on('close', (code) => resolve(code ?? 0));
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export function isAsyncIterable(value) {
|
|
81
|
-
return value && typeof value[Symbol.asyncIterator] === 'function';
|
|
82
|
-
}
|