@opencoven/coven-code 0.0.4 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -130
- package/bin/coven-code +26 -0
- package/install.js +117 -0
- package/package.json +25 -22
- 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/sdk-install.mjs
DELETED
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
import { spawnSync } from 'node:child_process';
|
|
2
|
-
import { chmod, mkdir, writeFile } from 'node:fs/promises';
|
|
3
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
4
|
-
import os from 'node:os';
|
|
5
|
-
import path from 'node:path';
|
|
6
|
-
import { fileURLToPath } from 'node:url';
|
|
7
|
-
import { VERSION } from './constants.mjs';
|
|
8
|
-
|
|
9
|
-
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
10
|
-
const covenCodeBin = path.join(repoRoot, 'bin', 'coven-code.mjs');
|
|
11
|
-
|
|
12
|
-
export async function runInstallCommand(args = []) {
|
|
13
|
-
const command = args[0];
|
|
14
|
-
if (!command || command === '--help' || command === '-h' || command === 'help') {
|
|
15
|
-
printUsage();
|
|
16
|
-
return 0;
|
|
17
|
-
}
|
|
18
|
-
if (command !== 'install') {
|
|
19
|
-
console.error(`Unknown command: ${command}`);
|
|
20
|
-
printUsage();
|
|
21
|
-
return 1;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const parsed = parseInstallOptions(args.slice(1));
|
|
25
|
-
if (parsed.errorMessage) {
|
|
26
|
-
console.error(parsed.errorMessage);
|
|
27
|
-
printUsage();
|
|
28
|
-
return 1;
|
|
29
|
-
}
|
|
30
|
-
if (parsed.showHelp) {
|
|
31
|
-
printUsage();
|
|
32
|
-
return 0;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
await installLocalCovenCode(parsed.options);
|
|
36
|
-
return 0;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function printUsage() {
|
|
40
|
-
console.log('Usage: coven-code-sdk install [--force]');
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function parseInstallOptions(args = []) {
|
|
44
|
-
const options = { forceInstall: false };
|
|
45
|
-
for (const arg of args) {
|
|
46
|
-
if (arg === '--help' || arg === '-h') return { options, showHelp: true, errorMessage: null };
|
|
47
|
-
if (arg === '--force') {
|
|
48
|
-
options.forceInstall = true;
|
|
49
|
-
continue;
|
|
50
|
-
}
|
|
51
|
-
return { options, showHelp: false, errorMessage: `Unknown option for install: ${arg}` };
|
|
52
|
-
}
|
|
53
|
-
return { options, showHelp: false, errorMessage: null };
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
async function installLocalCovenCode(options = {}) {
|
|
57
|
-
if (!options.forceInstall) {
|
|
58
|
-
const installedCli = findPreManagedInstalledCli();
|
|
59
|
-
if (installedCli) {
|
|
60
|
-
console.log(`Coven Code CLI ${installedCli.version} already satisfies minimum ${VERSION} (${installedCli.source}).`);
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
} else {
|
|
64
|
-
console.log('Forcing SDK-managed install; skipping existing CLI detection.');
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const target = sdkManagedCovenCodePath();
|
|
68
|
-
if (!options.forceInstall && existsSync(target)) {
|
|
69
|
-
const version = installedCovenCodeVersion(target);
|
|
70
|
-
if (version) {
|
|
71
|
-
console.log(`Coven Code CLI ${version} already satisfies minimum ${VERSION} (SDK_MANAGED).`);
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
if (!options.forceInstall) {
|
|
77
|
-
const installedCli = findPostManagedInstalledCli();
|
|
78
|
-
if (installedCli) {
|
|
79
|
-
console.log(`Coven Code CLI ${installedCli.version} already satisfies minimum ${VERSION} (${installedCli.source}).`);
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
await mkdir(path.dirname(target), { recursive: true, mode: 0o700 });
|
|
85
|
-
await writeFile(target, covenCodeWrapperSource(), 'utf8');
|
|
86
|
-
if (process.platform !== 'win32') await chmod(target, 0o755);
|
|
87
|
-
console.log(`Coven Code CLI ${VERSION} installed at ${target}.`);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
function findPreManagedInstalledCli() {
|
|
91
|
-
const cliPath = process.env.COVEN_CODE_CLI_PATH;
|
|
92
|
-
const envVersion = cliPath && existsSync(cliPath) ? installedCovenCodeVersion(cliPath) : undefined;
|
|
93
|
-
if (envVersion) return { source: 'COVEN_CODE_CLI_PATH', version: envVersion };
|
|
94
|
-
|
|
95
|
-
const localPackage = findLocalCovenCodePackage();
|
|
96
|
-
if (localPackage) return localPackage;
|
|
97
|
-
|
|
98
|
-
return undefined;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function findPostManagedInstalledCli() {
|
|
102
|
-
const covenCodeHomePath = path.join(resolveCovenCodeHome(), 'bin', process.platform === 'win32' ? 'coven-code.cmd' : 'coven-code');
|
|
103
|
-
const covenCodeHomeVersion = existsSync(covenCodeHomePath) ? installedCovenCodeVersion(covenCodeHomePath) : undefined;
|
|
104
|
-
if (covenCodeHomeVersion) return { source: process.env.COVEN_CODE_HOME ? 'COVEN_CODE_HOME' : 'COVEN_CODE_HOME', version: covenCodeHomeVersion };
|
|
105
|
-
|
|
106
|
-
const pathCli = findCovenCodeOnPath();
|
|
107
|
-
if (pathCli) return pathCli;
|
|
108
|
-
|
|
109
|
-
return undefined;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function findLocalCovenCodePackage() {
|
|
113
|
-
for (const packageName of ['@opencoven/coven-code']) {
|
|
114
|
-
const packageJsonPath = findNodeModulePackageJson(packageName);
|
|
115
|
-
if (!packageJsonPath) continue;
|
|
116
|
-
try {
|
|
117
|
-
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
|
|
118
|
-
const bin = typeof packageJson.bin?.['coven-code'] === 'string' ? packageJson.bin['coven-code'] : undefined;
|
|
119
|
-
if (!bin) continue;
|
|
120
|
-
const cliPath = path.resolve(path.dirname(packageJsonPath), bin);
|
|
121
|
-
const version = existsSync(cliPath) ? installedCovenCodeVersion(cliPath) : undefined;
|
|
122
|
-
if (version) return { source: 'LOCAL_NPM', version };
|
|
123
|
-
} catch {
|
|
124
|
-
// Try the next package candidate.
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
return undefined;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
function findNodeModulePackageJson(packageName) {
|
|
131
|
-
let current = process.cwd();
|
|
132
|
-
while (true) {
|
|
133
|
-
const candidate = path.join(current, 'node_modules', ...packageName.split('/'), 'package.json');
|
|
134
|
-
if (existsSync(candidate)) return candidate;
|
|
135
|
-
const parent = path.dirname(current);
|
|
136
|
-
if (parent === current) return undefined;
|
|
137
|
-
current = parent;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
function findCovenCodeOnPath() {
|
|
142
|
-
for (const dir of process.env.PATH?.split(path.delimiter) ?? []) {
|
|
143
|
-
if (!dir) continue;
|
|
144
|
-
for (const name of covenCodeCommandNames()) {
|
|
145
|
-
const candidate = path.join(dir, name);
|
|
146
|
-
const version = existsSync(candidate) ? installedCovenCodeVersion(candidate) : undefined;
|
|
147
|
-
if (version) return { source: 'PATH', version };
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
return undefined;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
function covenCodeCommandNames() {
|
|
154
|
-
return process.platform === 'win32' ? ['coven-code.exe', 'coven-code.cmd', 'coven-code'] : ['coven-code'];
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
function sdkManagedCovenCodePath() {
|
|
158
|
-
return path.join(resolveCovenCodeHome(), 'sdk', 'bin', process.platform === 'win32' ? 'coven-code.cmd' : 'coven-code');
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
function resolveCovenCodeHome() {
|
|
162
|
-
return process.env.COVEN_CODE_HOME || path.join(os.homedir(), '.coven-code');
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
function covenCodeWrapperSource() {
|
|
166
|
-
if (process.platform === 'win32') {
|
|
167
|
-
return `@echo off\r\n"${process.execPath}" "${covenCodeBin}" %*\r\n`;
|
|
168
|
-
}
|
|
169
|
-
return `#!/usr/bin/env node
|
|
170
|
-
import { spawnSync } from 'node:child_process';
|
|
171
|
-
|
|
172
|
-
const result = spawnSync(${JSON.stringify(process.execPath)}, [${JSON.stringify(covenCodeBin)}, ...process.argv.slice(2)], {
|
|
173
|
-
stdio: 'inherit',
|
|
174
|
-
});
|
|
175
|
-
if (result.error) {
|
|
176
|
-
console.error(result.error.stack || String(result.error));
|
|
177
|
-
process.exit(1);
|
|
178
|
-
}
|
|
179
|
-
process.exit(result.status ?? 0);
|
|
180
|
-
`;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
function installedCovenCodeVersion(command) {
|
|
184
|
-
const result = spawnSync(command, ['--version'], { encoding: 'utf8' });
|
|
185
|
-
if (result.status !== 0) return undefined;
|
|
186
|
-
return result.stdout.trim() || undefined;
|
|
187
|
-
}
|
package/src/sdk-settings.mjs
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import { existsSync } from 'node:fs';
|
|
2
|
-
import { appendFile, mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
|
|
3
|
-
import os from 'node:os';
|
|
4
|
-
import path from 'node:path';
|
|
5
|
-
import { fileURLToPath } from 'node:url';
|
|
6
|
-
import { parseJsonc } from './settings/load.mjs';
|
|
7
|
-
|
|
8
|
-
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
9
|
-
const covenCodeBin = path.join(repoRoot, 'bin', 'coven-code.mjs');
|
|
10
|
-
|
|
11
|
-
export async function withEnv(env = {}, fn) {
|
|
12
|
-
const previous = new Map();
|
|
13
|
-
for (const [key, value] of Object.entries(env ?? {})) {
|
|
14
|
-
previous.set(key, Object.hasOwn(process.env, key) ? process.env[key] : undefined);
|
|
15
|
-
process.env[key] = value;
|
|
16
|
-
}
|
|
17
|
-
try {
|
|
18
|
-
return await fn();
|
|
19
|
-
} finally {
|
|
20
|
-
for (const [key, value] of previous) {
|
|
21
|
-
if (value === undefined) delete process.env[key];
|
|
22
|
-
else process.env[key] = value;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export async function prepareRunSettings(options = {}) {
|
|
28
|
-
if (!shouldWriteRunSettings(options)) return { settingsFile: options.settingsFile, cleanup: async () => {} };
|
|
29
|
-
const dir = await mkdtemp(path.join(os.tmpdir(), 'coven-code-sdk-settings-'));
|
|
30
|
-
const settingsFile = path.join(dir, 'settings.json');
|
|
31
|
-
const baseSettings = options.settingsFile ? await readJsonSettings(sdkOptionsPath(options.settingsFile, options.cwd)) : {};
|
|
32
|
-
const settings = { ...baseSettings };
|
|
33
|
-
if (Array.isArray(options.permissions)) settings['covenCode.permissions'] = options.permissions;
|
|
34
|
-
if (Array.isArray(options.enabledTools)) settings['covenCode.tools.enable'] = options.enabledTools;
|
|
35
|
-
if (typeof options.systemPrompt === 'string') settings['covenCode.systemPrompt'] = options.systemPrompt;
|
|
36
|
-
if (typeof options.skills === 'string') settings['covenCode.skills.path'] = options.skills;
|
|
37
|
-
await writeFile(settingsFile, `${JSON.stringify(settings, null, 2)}\n`, 'utf8');
|
|
38
|
-
return {
|
|
39
|
-
settingsFile,
|
|
40
|
-
cleanup: async () => {
|
|
41
|
-
await rm(dir, { recursive: true, force: true });
|
|
42
|
-
},
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function shouldWriteRunSettings(options = {}) {
|
|
47
|
-
return Array.isArray(options.permissions)
|
|
48
|
-
|| Array.isArray(options.enabledTools)
|
|
49
|
-
|| typeof options.systemPrompt === 'string'
|
|
50
|
-
|| typeof options.skills === 'string';
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
async function readJsonSettings(filePath) {
|
|
54
|
-
try {
|
|
55
|
-
return parseJsonc(await readFile(filePath, 'utf8'));
|
|
56
|
-
} catch {
|
|
57
|
-
return {};
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export async function writeDebugLog(options = {}, covenCodeCommand = resolveCovenCodeCommand(), args = []) {
|
|
62
|
-
if (options.logLevel !== 'debug') return;
|
|
63
|
-
const line = `level=debug cwd=${options.cwd ?? process.cwd()} argv=${[covenCodeCommand.command, ...covenCodeCommand.args, ...args].map(JSON.stringify).join(' ')}\n`;
|
|
64
|
-
process.stderr.write(line);
|
|
65
|
-
if (!options.logFile) return;
|
|
66
|
-
const logFile = sdkOptionsPath(options.logFile, options.cwd);
|
|
67
|
-
await mkdir(path.dirname(logFile), { recursive: true });
|
|
68
|
-
await appendFile(logFile, line, 'utf8');
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function sdkOptionsPath(filePath, cwd = process.cwd()) {
|
|
72
|
-
if (!filePath || path.isAbsolute(filePath)) return filePath;
|
|
73
|
-
return path.resolve(cwd, filePath);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export function resolveCovenCodeCommand() {
|
|
77
|
-
const cliPath = process.env.COVEN_CODE_CLI_PATH;
|
|
78
|
-
if (cliPath && existsSync(cliPath)) {
|
|
79
|
-
return isNodeScriptPath(cliPath)
|
|
80
|
-
? { command: process.execPath, args: [cliPath] }
|
|
81
|
-
: { command: cliPath, args: [] };
|
|
82
|
-
}
|
|
83
|
-
return { command: process.execPath, args: [covenCodeBin] };
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function isNodeScriptPath(filePath) {
|
|
87
|
-
return filePath.endsWith('.js') || filePath.endsWith('.mjs') || filePath.endsWith('.cjs');
|
|
88
|
-
}
|
package/src/sdk.mjs
DELETED
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
import { spawn } from 'node:child_process';
|
|
2
|
-
import { randomUUID } from 'node:crypto';
|
|
3
|
-
import { createInterface } from 'node:readline';
|
|
4
|
-
import { normalizeThreadVisibility, readThread, writeThread } from './threads/store.mjs';
|
|
5
|
-
import {
|
|
6
|
-
closeStdin,
|
|
7
|
-
executeArgs,
|
|
8
|
-
isAsyncIterable,
|
|
9
|
-
waitForExit,
|
|
10
|
-
writeStreamInput,
|
|
11
|
-
} from './sdk-execute.mjs';
|
|
12
|
-
import {
|
|
13
|
-
prepareRunSettings,
|
|
14
|
-
resolveCovenCodeCommand,
|
|
15
|
-
withEnv,
|
|
16
|
-
writeDebugLog,
|
|
17
|
-
} from './sdk-settings.mjs';
|
|
18
|
-
|
|
19
|
-
export const threads = {
|
|
20
|
-
async new(options = {}) {
|
|
21
|
-
return withEnv(options.env, async () => {
|
|
22
|
-
const now = new Date().toISOString();
|
|
23
|
-
const thread = {
|
|
24
|
-
id: `T-${randomUUID()}`,
|
|
25
|
-
title: '(empty thread)',
|
|
26
|
-
cwd: options.cwd ?? process.cwd(),
|
|
27
|
-
mode: options.mode ?? 'smart',
|
|
28
|
-
visibility: normalizeSdkThreadVisibility(options.visibility) ?? 'private',
|
|
29
|
-
labels: [],
|
|
30
|
-
archived: false,
|
|
31
|
-
createdAt: now,
|
|
32
|
-
updatedAt: now,
|
|
33
|
-
messages: [],
|
|
34
|
-
};
|
|
35
|
-
await writeThread(thread);
|
|
36
|
-
return thread.id;
|
|
37
|
-
});
|
|
38
|
-
},
|
|
39
|
-
|
|
40
|
-
async markdown({ threadId, env } = {}) {
|
|
41
|
-
if (!threadId) throw new Error('threads.markdown requires threadId');
|
|
42
|
-
return withEnv(env, async () => threadMarkdown(requireSdkThread(threadId)));
|
|
43
|
-
},
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
export async function* execute({ prompt, options = {}, signal } = {}) {
|
|
47
|
-
if (prompt === undefined) throw new Error('execute requires a prompt');
|
|
48
|
-
const isStreamInput = isAsyncIterable(prompt);
|
|
49
|
-
const runSettings = await prepareRunSettings(options);
|
|
50
|
-
const args = executeArgs(prompt, { ...options, settingsFile: runSettings.settingsFile }, isStreamInput);
|
|
51
|
-
const covenCodeCommand = resolveCovenCodeCommand();
|
|
52
|
-
const started = Date.now();
|
|
53
|
-
await writeDebugLog(options, covenCodeCommand, args);
|
|
54
|
-
const child = spawn(covenCodeCommand.command, [...covenCodeCommand.args, ...args], {
|
|
55
|
-
cwd: options.cwd ?? process.cwd(),
|
|
56
|
-
env: { ...process.env, ...(options.env ?? {}) },
|
|
57
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
58
|
-
signal,
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
let exitError;
|
|
62
|
-
const exitTask = waitForExit(child).catch((error) => {
|
|
63
|
-
exitError = error;
|
|
64
|
-
});
|
|
65
|
-
const stderr = [];
|
|
66
|
-
child.stderr.on('data', (chunk) => stderr.push(chunk));
|
|
67
|
-
let inputError;
|
|
68
|
-
const inputTask = (isStreamInput ? writeStreamInput(child, prompt, signal) : closeStdin(child))
|
|
69
|
-
.catch((error) => {
|
|
70
|
-
inputError = error;
|
|
71
|
-
});
|
|
72
|
-
const rl = createInterface({ input: child.stdout, crlfDelay: Infinity });
|
|
73
|
-
const closeChildStreams = () => {
|
|
74
|
-
rl.close();
|
|
75
|
-
child.stdout.destroy();
|
|
76
|
-
child.stderr.destroy();
|
|
77
|
-
};
|
|
78
|
-
signal?.addEventListener('abort', closeChildStreams, { once: true });
|
|
79
|
-
let sessionId = `T-${randomUUID()}`;
|
|
80
|
-
let emittedResult = false;
|
|
81
|
-
try {
|
|
82
|
-
for await (const line of rl) {
|
|
83
|
-
if (!line.trim()) continue;
|
|
84
|
-
const message = JSON.parse(line);
|
|
85
|
-
if (message.session_id) sessionId = message.session_id;
|
|
86
|
-
if (message.type === 'result') emittedResult = true;
|
|
87
|
-
yield message;
|
|
88
|
-
}
|
|
89
|
-
await inputTask;
|
|
90
|
-
if (inputError) throw inputError;
|
|
91
|
-
const status = await exitTask;
|
|
92
|
-
if (exitError) throw exitError;
|
|
93
|
-
if (status !== 0 && !emittedResult) {
|
|
94
|
-
yield {
|
|
95
|
-
type: 'result',
|
|
96
|
-
subtype: 'error_during_execution',
|
|
97
|
-
duration_ms: Date.now() - started,
|
|
98
|
-
is_error: true,
|
|
99
|
-
num_turns: 0,
|
|
100
|
-
error: Buffer.concat(stderr).toString('utf8').trim() || `coven-code exited with status ${status}`,
|
|
101
|
-
session_id: sessionId,
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
} finally {
|
|
105
|
-
signal?.removeEventListener('abort', closeChildStreams);
|
|
106
|
-
rl.close();
|
|
107
|
-
await runSettings.cleanup();
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export function createUserMessage(text) {
|
|
112
|
-
return {
|
|
113
|
-
type: 'user',
|
|
114
|
-
message: {
|
|
115
|
-
role: 'user',
|
|
116
|
-
content: [{ type: 'text', text: String(text) }],
|
|
117
|
-
},
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
export function createPermission(tool, action, options = {}) {
|
|
122
|
-
if (action === 'delegate' && !options.to) {
|
|
123
|
-
throw new Error('delegate action requires "to" option');
|
|
124
|
-
}
|
|
125
|
-
return {
|
|
126
|
-
tool,
|
|
127
|
-
action,
|
|
128
|
-
...(options.matches ? { matches: options.matches } : {}),
|
|
129
|
-
...(options.context ? { context: options.context } : {}),
|
|
130
|
-
...(options.to ? { to: options.to } : {}),
|
|
131
|
-
...(options.message ? { message: options.message } : {}),
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function normalizeSdkThreadVisibility(visibility) {
|
|
136
|
-
if (visibility === 'team') return 'workspace';
|
|
137
|
-
return normalizeThreadVisibility(visibility);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function requireSdkThread(threadId) {
|
|
141
|
-
const thread = readThread(threadId);
|
|
142
|
-
if (!thread) throw new Error(`Unknown thread: ${threadId}`);
|
|
143
|
-
return thread;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
function threadMarkdown(thread) {
|
|
147
|
-
const lines = [
|
|
148
|
-
`# Thread ${thread.id}`,
|
|
149
|
-
'',
|
|
150
|
-
`Visibility: ${thread.visibility ?? 'private'}`,
|
|
151
|
-
`Status: ${thread.archived ? 'archived' : 'active'}`,
|
|
152
|
-
`CWD: ${thread.cwd}`,
|
|
153
|
-
'',
|
|
154
|
-
];
|
|
155
|
-
for (const message of thread.messages ?? []) {
|
|
156
|
-
lines.push(`## ${titleCaseRole(message.role)}`, '', String(message.content), '');
|
|
157
|
-
}
|
|
158
|
-
return `${lines.join('\n').trimEnd()}\n`;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
function titleCaseRole(role = '') {
|
|
162
|
-
return role ? `${role.slice(0, 1).toUpperCase()}${role.slice(1)}` : 'Message';
|
|
163
|
-
}
|
package/src/settings/load.mjs
DELETED
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
-
import { mkdir, writeFile } from 'node:fs/promises';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import { findManagedSettingsFile, findWorkspaceSettingsFile, settingsFile } from './paths.mjs';
|
|
5
|
-
|
|
6
|
-
export const SETTINGS_PREFIX = 'covenCode.';
|
|
7
|
-
|
|
8
|
-
export function readSettings(parsed = {}) {
|
|
9
|
-
return readSettingsFile(settingsFile(parsed));
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function readEffectiveSettings(parsed = {}, options = {}) {
|
|
13
|
-
const userSettings = readSettings(parsed);
|
|
14
|
-
const workspacePath = findWorkspaceSettingsFile(options.cwd ?? process.cwd());
|
|
15
|
-
const workspaceSettings = workspacePath ? readSettingsFile(workspacePath) : {};
|
|
16
|
-
return { ...userSettings, ...workspaceSettings, ...readManagedSettings() };
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function readManagedSettings() {
|
|
20
|
-
return readSettingsFile(findManagedSettingsFile());
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export async function writeSettings(settings, parsed = {}) {
|
|
24
|
-
await writeSettingsFile(settingsFile(parsed), settings);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function readSettingsFile(filePath) {
|
|
28
|
-
if (!filePath || !existsSync(filePath)) return {};
|
|
29
|
-
try {
|
|
30
|
-
return parseJsonc(readFileSync(filePath, 'utf8'));
|
|
31
|
-
} catch {
|
|
32
|
-
return {};
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export async function writeSettingsFile(filePath, settings) {
|
|
37
|
-
await mkdir(path.dirname(filePath), { recursive: true });
|
|
38
|
-
await writeFile(filePath, `${JSON.stringify(settings, null, 2)}\n`, 'utf8');
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export function parseJsonc(text) {
|
|
42
|
-
return JSON.parse(removeJsonTrailingCommas(stripJsonComments(text)));
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function stripJsonComments(text) {
|
|
46
|
-
let output = '';
|
|
47
|
-
let inString = false;
|
|
48
|
-
let escaped = false;
|
|
49
|
-
for (let index = 0; index < text.length; index += 1) {
|
|
50
|
-
const char = text[index];
|
|
51
|
-
const next = text[index + 1];
|
|
52
|
-
|
|
53
|
-
if (inString) {
|
|
54
|
-
output += char;
|
|
55
|
-
if (escaped) escaped = false;
|
|
56
|
-
else if (char === '\\') escaped = true;
|
|
57
|
-
else if (char === '"') inString = false;
|
|
58
|
-
continue;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
if (char === '"') {
|
|
62
|
-
inString = true;
|
|
63
|
-
output += char;
|
|
64
|
-
continue;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (char === '/' && next === '/') {
|
|
68
|
-
while (index < text.length && text[index] !== '\n') index += 1;
|
|
69
|
-
if (index < text.length) output += text[index];
|
|
70
|
-
continue;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
if (char === '/' && next === '*') {
|
|
74
|
-
index += 2;
|
|
75
|
-
while (index < text.length && !(text[index] === '*' && text[index + 1] === '/')) index += 1;
|
|
76
|
-
index += 1;
|
|
77
|
-
continue;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
output += char;
|
|
81
|
-
}
|
|
82
|
-
return output;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function removeJsonTrailingCommas(text) {
|
|
86
|
-
let output = '';
|
|
87
|
-
let inString = false;
|
|
88
|
-
let escaped = false;
|
|
89
|
-
for (let index = 0; index < text.length; index += 1) {
|
|
90
|
-
const char = text[index];
|
|
91
|
-
|
|
92
|
-
if (inString) {
|
|
93
|
-
output += char;
|
|
94
|
-
if (escaped) escaped = false;
|
|
95
|
-
else if (char === '\\') escaped = true;
|
|
96
|
-
else if (char === '"') inString = false;
|
|
97
|
-
continue;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
if (char === '"') {
|
|
101
|
-
inString = true;
|
|
102
|
-
output += char;
|
|
103
|
-
continue;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
if (char === ',') {
|
|
107
|
-
let lookahead = index + 1;
|
|
108
|
-
while (/\s/.test(text[lookahead] ?? '')) lookahead += 1;
|
|
109
|
-
if (text[lookahead] === '}' || text[lookahead] === ']') continue;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
output += char;
|
|
113
|
-
}
|
|
114
|
-
return output;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
export function readFrontmatter(text) {
|
|
118
|
-
const metadata = {};
|
|
119
|
-
const match = text.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
120
|
-
if (!match) return metadata;
|
|
121
|
-
for (const line of match[1].split(/\r?\n/)) {
|
|
122
|
-
const entry = line.match(/^([A-Za-z0-9_.-]+):\s*(.*)$/);
|
|
123
|
-
if (entry) metadata[entry[1]] = entry[2].replace(/^["']|["']$/g, '');
|
|
124
|
-
}
|
|
125
|
-
return metadata;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
export function splitListValue(value) {
|
|
129
|
-
const text = String(value).trim();
|
|
130
|
-
if (text.startsWith('[') && text.endsWith(']')) {
|
|
131
|
-
return text.slice(1, -1).split(',').map((entry) => entry.trim().replace(/^["']|["']$/g, '')).filter(Boolean);
|
|
132
|
-
}
|
|
133
|
-
return text.split(/[,\s]+/).map((entry) => entry.trim()).filter(Boolean);
|
|
134
|
-
}
|
package/src/settings/paths.mjs
DELETED
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import { existsSync } from 'node:fs';
|
|
2
|
-
import os from 'node:os';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import { CONFIG_SUBDIR, PROJECT_SUBDIR } from '../constants.mjs';
|
|
5
|
-
|
|
6
|
-
const TOOLBOX_ENV = 'COVEN_CODE_TOOLBOX';
|
|
7
|
-
const SETTINGS_FILE_ENV = 'COVEN_CODE_SETTINGS_FILE';
|
|
8
|
-
const MANAGED_SETTINGS_FILE_ENV = 'COVEN_CODE_MANAGED_SETTINGS_FILE';
|
|
9
|
-
|
|
10
|
-
export function configDir() {
|
|
11
|
-
return process.env.XDG_CONFIG_HOME || path.join(os.homedir(), '.config');
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export function toolsDirs(parsed = {}) {
|
|
15
|
-
if (parsed.toolbox) return parsed.toolbox.split(path.delimiter).filter(Boolean).map(expandHomePath);
|
|
16
|
-
if (Object.hasOwn(process.env, TOOLBOX_ENV)) {
|
|
17
|
-
if (process.env[TOOLBOX_ENV] === '') return [];
|
|
18
|
-
return process.env[TOOLBOX_ENV].split(path.delimiter).filter(Boolean);
|
|
19
|
-
}
|
|
20
|
-
return [path.join(configDir(), CONFIG_SUBDIR, 'tools')];
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function writableToolsDir() {
|
|
24
|
-
return toolsDirs()[0] || path.join(configDir(), CONFIG_SUBDIR, 'tools');
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function workspaceSettingsFile(cwd) {
|
|
28
|
-
return path.join(cwd, PROJECT_SUBDIR, 'settings.json');
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export function findWorkspaceSettingsFile(cwd) {
|
|
32
|
-
let current = path.resolve(cwd);
|
|
33
|
-
const boundary = findGitRoot(current) ?? current;
|
|
34
|
-
while (true) {
|
|
35
|
-
for (const name of ['settings.json', 'settings.jsonc']) {
|
|
36
|
-
const candidate = path.join(current, PROJECT_SUBDIR, name);
|
|
37
|
-
if (existsSync(candidate)) return candidate;
|
|
38
|
-
}
|
|
39
|
-
if (current === boundary) return undefined;
|
|
40
|
-
const parent = path.dirname(current);
|
|
41
|
-
if (parent === current) return undefined;
|
|
42
|
-
current = parent;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export function findUserSettingsFile() {
|
|
47
|
-
const dir = path.join(configDir(), CONFIG_SUBDIR);
|
|
48
|
-
for (const name of ['settings.json', 'settings.jsonc']) {
|
|
49
|
-
const candidate = path.join(dir, name);
|
|
50
|
-
if (existsSync(candidate)) return candidate;
|
|
51
|
-
}
|
|
52
|
-
return undefined;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export function findManagedSettingsFile() {
|
|
56
|
-
if (process.env[MANAGED_SETTINGS_FILE_ENV]) return process.env[MANAGED_SETTINGS_FILE_ENV];
|
|
57
|
-
const candidates = [];
|
|
58
|
-
if (process.platform === 'win32') {
|
|
59
|
-
if (process.env.ProgramData) {
|
|
60
|
-
candidates.push(path.join(process.env.ProgramData, CONFIG_SUBDIR, 'managed-settings.json'));
|
|
61
|
-
}
|
|
62
|
-
} else if (process.platform === 'darwin') {
|
|
63
|
-
candidates.push('/Library/Application Support/coven-code/managed-settings.json');
|
|
64
|
-
} else {
|
|
65
|
-
candidates.push('/etc/coven-code/managed-settings.json');
|
|
66
|
-
}
|
|
67
|
-
return candidates.find((candidate) => existsSync(candidate));
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export function settingsFile(parsed = {}) {
|
|
71
|
-
if (parsed.settingsFile || process.env[SETTINGS_FILE_ENV]) {
|
|
72
|
-
return parsed.settingsFile || process.env[SETTINGS_FILE_ENV];
|
|
73
|
-
}
|
|
74
|
-
return findUserSettingsFile() || path.join(configDir(), CONFIG_SUBDIR, 'settings.json');
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export function findProjectRoot(cwd) {
|
|
78
|
-
let current = cwd;
|
|
79
|
-
while (true) {
|
|
80
|
-
if (existsSync(path.join(current, 'package.json')) || existsSync(path.join(current, '.git'))) return current;
|
|
81
|
-
const parent = path.dirname(current);
|
|
82
|
-
if (parent === current || current === os.homedir()) return cwd;
|
|
83
|
-
current = parent;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function findGitRoot(cwd) {
|
|
88
|
-
let current = path.resolve(cwd);
|
|
89
|
-
while (true) {
|
|
90
|
-
if (existsSync(path.join(current, '.git'))) return current;
|
|
91
|
-
const parent = path.dirname(current);
|
|
92
|
-
if (parent === current || current === os.homedir()) return undefined;
|
|
93
|
-
current = parent;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export function expandHomePath(value) {
|
|
98
|
-
if (value === '~') return os.homedir();
|
|
99
|
-
if (value.startsWith(`~${path.sep}`)) return path.join(os.homedir(), value.slice(2));
|
|
100
|
-
return value;
|
|
101
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: building-skills
|
|
3
|
-
description: Create Coven Code skills for a codebase or workflow
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Building Skills
|
|
7
|
-
|
|
8
|
-
Use this skill when the user asks to create a new Coven Code skill for a codebase,
|
|
9
|
-
workflow, service, or repeated task.
|
|
10
|
-
|
|
11
|
-
## Workflow
|
|
12
|
-
|
|
13
|
-
1. Identify the task the skill should help with and where it should live.
|
|
14
|
-
2. Create a skill directory with a `SKILL.md` file.
|
|
15
|
-
3. Add YAML frontmatter with a unique `name` and concise `description`.
|
|
16
|
-
4. Write clear instructions, examples, and any resource paths the agent should use.
|
|
17
|
-
5. Keep the skill focused so it is loaded only when its description matches.
|
|
18
|
-
|
|
19
|
-
Project skills live in `.agents/skills/<name>/`. User-wide skills live in
|
|
20
|
-
`~/.config/agents/skills/<name>/`.
|