@opencoven/coven-code 0.0.4 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -130
- package/bin/coven-code +26 -0
- package/install.js +117 -0
- package/package.json +26 -23
- package/bin/coven-code-sdk.mjs +0 -12
- package/bin/coven-code.mjs +0 -19
- package/docs/CLI.md +0 -256
- package/docs/CONFIGURATION.md +0 -107
- package/docs/DEMO.md +0 -453
- package/docs/DEVELOPMENT.md +0 -104
- package/docs/DOGFOOD-PROTOCOL.md +0 -263
- package/docs/MCP-SKILLS-PLUGINS.md +0 -127
- package/docs/README.md +0 -39
- package/docs/RELEASE.md +0 -33
- package/docs/SDK.md +0 -107
- package/docs/superpowers/plans/2026-05-25-coven-code-panel-tui.md +0 -904
- package/docs/superpowers/plans/2026-05-25-coven-code-rebrand.md +0 -670
- package/docs/superpowers/specs/2026-05-25-coven-code-panel-tui-design.md +0 -235
- package/docs/superpowers/specs/2026-05-26-slash-first-tui-review.md +0 -63
- package/src/agent/fixture.mjs +0 -95
- package/src/agent/lane.mjs +0 -136
- package/src/cli/dispatch.mjs +0 -66
- package/src/cli/execute.mjs +0 -452
- package/src/cli/help.mjs +0 -58
- package/src/cli/interactive-core.mjs +0 -28
- package/src/cli/interactive-io.mjs +0 -101
- package/src/cli/interactive-slash.mjs +0 -184
- package/src/cli/notifications.mjs +0 -13
- package/src/cli/parse.mjs +0 -83
- package/src/cli/reasoning.mjs +0 -45
- package/src/cli/refs.mjs +0 -162
- package/src/cli/repl.mjs +0 -60
- package/src/cli/slash-commands.mjs +0 -375
- package/src/cli/stream-json.mjs +0 -225
- package/src/cli/tui-actions.mjs +0 -72
- package/src/cli/tui-blessed.mjs +0 -198
- package/src/cli/tui-keys.mjs +0 -80
- package/src/cli/tui-lane.mjs +0 -73
- package/src/cli/tui-render.mjs +0 -169
- package/src/cli/tui-submit.mjs +0 -82
- package/src/cli/tui.mjs +0 -174
- package/src/commands/agents.mjs +0 -53
- package/src/commands/config.mjs +0 -27
- package/src/commands/ide.mjs +0 -17
- package/src/commands/login.mjs +0 -84
- package/src/commands/mcp.mjs +0 -176
- package/src/commands/permissions-eval.mjs +0 -122
- package/src/commands/permissions-rules.mjs +0 -53
- package/src/commands/permissions-text.mjs +0 -112
- package/src/commands/permissions.mjs +0 -62
- package/src/commands/plugins.mjs +0 -86
- package/src/commands/review.mjs +0 -74
- package/src/commands/skill.mjs +0 -23
- package/src/commands/threads.mjs +0 -165
- package/src/commands/tools.mjs +0 -77
- package/src/commands/update.mjs +0 -31
- package/src/commands/usage.mjs +0 -34
- package/src/constants.mjs +0 -52
- package/src/main.mjs +0 -87
- package/src/mcp/discover.mjs +0 -154
- package/src/mcp/local.mjs +0 -55
- package/src/mcp/parsers.mjs +0 -46
- package/src/mcp/permissions.mjs +0 -52
- package/src/mcp/probe.mjs +0 -85
- package/src/mcp/registry.mjs +0 -96
- package/src/mcp/remote-oauth.mjs +0 -55
- package/src/mcp/remote-session.mjs +0 -54
- package/src/mcp/remote-sse.mjs +0 -82
- package/src/mcp/remote.mjs +0 -74
- package/src/plugins/api.mjs +0 -187
- package/src/plugins/configuration.mjs +0 -124
- package/src/plugins/discover.mjs +0 -84
- package/src/plugins/helpers.mjs +0 -187
- package/src/plugins/subsystems.mjs +0 -198
- package/src/plugins/validators.mjs +0 -142
- package/src/sdk-execute.mjs +0 -82
- package/src/sdk-install.mjs +0 -187
- package/src/sdk-settings.mjs +0 -88
- package/src/sdk.mjs +0 -163
- package/src/settings/load.mjs +0 -134
- package/src/settings/paths.mjs +0 -101
- package/src/skills/builtin/building-skills/SKILL.md +0 -20
- package/src/skills/discover.mjs +0 -95
- package/src/threads/store.mjs +0 -176
- package/src/tools/builtin/bash.mjs +0 -110
- package/src/tools/builtin/create-file.mjs +0 -66
- package/src/tools/builtin/edit-file.mjs +0 -76
- package/src/tools/builtin/finder.mjs +0 -73
- package/src/tools/builtin/glob.mjs +0 -74
- package/src/tools/builtin/grep.mjs +0 -82
- package/src/tools/builtin/index.mjs +0 -83
- package/src/tools/builtin/librarian.mjs +0 -97
- package/src/tools/builtin/look-at.mjs +0 -92
- package/src/tools/builtin/mcp.mjs +0 -51
- package/src/tools/builtin/mermaid.mjs +0 -59
- package/src/tools/builtin/oracle.mjs +0 -56
- package/src/tools/builtin/painter.mjs +0 -81
- package/src/tools/builtin/plugin-tool.mjs +0 -53
- package/src/tools/builtin/read-mcp-resource.mjs +0 -63
- package/src/tools/builtin/read-web-page.mjs +0 -72
- package/src/tools/builtin/read.mjs +0 -59
- package/src/tools/builtin/runtime-content.mjs +0 -31
- package/src/tools/builtin/runtime-decisions.mjs +0 -115
- package/src/tools/builtin/runtime.mjs +0 -85
- package/src/tools/builtin/task.mjs +0 -63
- package/src/tools/builtin/toolbox-tool.mjs +0 -57
- package/src/tools/builtin/undo-edit.mjs +0 -97
- package/src/tools/builtin/web-search.mjs +0 -128
- package/src/tools/toolbox.mjs +0 -273
- package/src/util/fs.mjs +0 -13
- package/src/util/glob.mjs +0 -46
- package/src/util/html.mjs +0 -21
- package/src/util/media.mjs +0 -13
- package/src/util/shell.mjs +0 -24
- package/src/util/table.mjs +0 -11
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { BUILTIN_PERMISSIONS } from '../constants.mjs';
|
|
2
|
-
import { readEffectiveSettings } from '../settings/load.mjs';
|
|
3
|
-
|
|
4
|
-
export const PERMISSIONS_SETTING = 'covenCode.permissions';
|
|
5
|
-
export const COMMAND_ALLOWLIST_SETTING = 'covenCode.commands.allowlist';
|
|
6
|
-
export const DANGEROUSLY_ALLOW_ALL_SETTING = 'covenCode.dangerouslyAllowAll';
|
|
7
|
-
export const GUARDED_FILES_ALLOWLIST_SETTING = 'covenCode.guardedFiles.allowlist';
|
|
8
|
-
|
|
9
|
-
export const UNDEFINED_MATCH_VALUE = Object.freeze({ __covenCodeLiteral: 'undefined' });
|
|
10
|
-
|
|
11
|
-
export function isUndefinedMatchValue(value) {
|
|
12
|
-
return Boolean(
|
|
13
|
-
value
|
|
14
|
-
&& typeof value === 'object'
|
|
15
|
-
&& !Array.isArray(value)
|
|
16
|
-
&& value.__covenCodeLiteral === 'undefined'
|
|
17
|
-
&& Object.keys(value).length === 1,
|
|
18
|
-
);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function loadUserPermissionRules(parsed = {}) {
|
|
22
|
-
const settings = readEffectiveSettings(parsed);
|
|
23
|
-
return Array.isArray(settings[PERMISSIONS_SETTING]) ? settings[PERMISSIONS_SETTING] : [];
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function builtinPermissionRules() {
|
|
27
|
-
return BUILTIN_PERMISSIONS.map(([action, builtinTool, cmd]) => ({
|
|
28
|
-
action,
|
|
29
|
-
tool: builtinTool,
|
|
30
|
-
matches: builtinTool === 'Bash' ? { cmd: `${cmd}*` } : undefined,
|
|
31
|
-
}));
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export function commandAllowlistPermissionRules(parsed = {}) {
|
|
35
|
-
const allowlist = readEffectiveSettings(parsed)[COMMAND_ALLOWLIST_SETTING];
|
|
36
|
-
if (!Array.isArray(allowlist)) return [];
|
|
37
|
-
return allowlist
|
|
38
|
-
.map((command) => String(command).trim())
|
|
39
|
-
.filter(Boolean)
|
|
40
|
-
.map((command) => ({
|
|
41
|
-
action: 'allow',
|
|
42
|
-
tool: 'Bash',
|
|
43
|
-
matches: { cmd: commandAllowlistPattern(command) },
|
|
44
|
-
}));
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function commandAllowlistPattern(command) {
|
|
48
|
-
return `/^${escapeRegex(command)}(?:\\s|$).*/`;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function escapeRegex(value) {
|
|
52
|
-
return String(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
53
|
-
}
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
import { UsageError } from '../cli/parse.mjs';
|
|
2
|
-
import { shellQuote, splitShellWords } from '../util/shell.mjs';
|
|
3
|
-
import { UNDEFINED_MATCH_VALUE, isUndefinedMatchValue } from './permissions-rules.mjs';
|
|
4
|
-
|
|
5
|
-
export function formatPermissionRule(rule) {
|
|
6
|
-
const parts = [rule.action];
|
|
7
|
-
for (const key of ['context', 'to', 'message']) {
|
|
8
|
-
if (rule[key] !== undefined) parts.push(`--${key}`, formatPermissionToken(rule[key]));
|
|
9
|
-
}
|
|
10
|
-
parts.push(formatPermissionToken(rule.tool));
|
|
11
|
-
for (const [key, value] of flattenMatches(rule.matches)) {
|
|
12
|
-
parts.push(`--${key}`, formatPermissionToken(value));
|
|
13
|
-
}
|
|
14
|
-
return parts.join(' ');
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function flattenMatches(matches, prefix = '') {
|
|
18
|
-
if (!matches) return [];
|
|
19
|
-
const entries = [];
|
|
20
|
-
for (const [key, value] of Object.entries(matches)) {
|
|
21
|
-
const fullKey = prefix ? `${prefix}.${key}` : key;
|
|
22
|
-
if (Array.isArray(value)) {
|
|
23
|
-
for (const entry of value) entries.push(...flattenMatchValue(fullKey, entry));
|
|
24
|
-
} else {
|
|
25
|
-
entries.push(...flattenMatchValue(fullKey, value));
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
return entries;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function flattenMatchValue(key, value) {
|
|
32
|
-
if (isUndefinedMatchValue(value)) return [[key, value]];
|
|
33
|
-
if (value && typeof value === 'object' && !Array.isArray(value)) return flattenMatches(value, key);
|
|
34
|
-
return [[key, value]];
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function formatPermissionToken(value) {
|
|
38
|
-
if (isUndefinedMatchValue(value)) return 'undefined';
|
|
39
|
-
if (value === null) return 'null';
|
|
40
|
-
if (value === undefined) return 'undefined';
|
|
41
|
-
if (typeof value === 'boolean' || typeof value === 'number') return String(value);
|
|
42
|
-
const text = String(value);
|
|
43
|
-
return /^[A-Za-z0-9_./:@=-]+$/.test(text) ? text : shellQuote(text);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export function parsePermissionText(input) {
|
|
47
|
-
return input
|
|
48
|
-
.split(/\r?\n/)
|
|
49
|
-
.map((line) => line.trim())
|
|
50
|
-
.filter((line) => line && !line.startsWith('#'))
|
|
51
|
-
.map((line) => parsePermissionRule(splitShellWords(line)));
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export function parsePermissionRule(tokens) {
|
|
55
|
-
const [action] = tokens;
|
|
56
|
-
const { flags: actionArgs, index: toolIndex } = parseActionArgs(tokens, 1);
|
|
57
|
-
const tool = tokens[toolIndex];
|
|
58
|
-
const rest = tokens.slice(toolIndex + 1);
|
|
59
|
-
if (!action || !tool) throw new UsageError('permission rules require: <action> <tool>');
|
|
60
|
-
const rule = { action, tool, ...actionArgs };
|
|
61
|
-
const matches = parseFlagMatches(rest, { undefinedPattern: true });
|
|
62
|
-
if (Object.keys(matches).length > 0) rule.matches = matches;
|
|
63
|
-
if (matches.to) {
|
|
64
|
-
rule.to = matches.to;
|
|
65
|
-
delete rule.matches.to;
|
|
66
|
-
if (Object.keys(rule.matches).length === 0) delete rule.matches;
|
|
67
|
-
}
|
|
68
|
-
return rule;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function parseActionArgs(tokens, startIndex) {
|
|
72
|
-
const flags = {};
|
|
73
|
-
let index = startIndex;
|
|
74
|
-
while (tokens[index]?.startsWith('--')) {
|
|
75
|
-
const key = tokens[index].slice(2);
|
|
76
|
-
flags[key] = parsePermissionValue(tokens[index + 1] ?? '');
|
|
77
|
-
index += 2;
|
|
78
|
-
}
|
|
79
|
-
return { flags, index };
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
export function parseFlagMatches(tokens, options = {}) {
|
|
83
|
-
const matches = {};
|
|
84
|
-
for (let index = 0; index < tokens.length; index += 1) {
|
|
85
|
-
const token = tokens[index];
|
|
86
|
-
if (!token.startsWith('--')) continue;
|
|
87
|
-
const key = token.slice(2);
|
|
88
|
-
const value = parsePermissionValue(tokens[index + 1] ?? '', options);
|
|
89
|
-
index += 1;
|
|
90
|
-
setMatchValue(matches, key, value);
|
|
91
|
-
}
|
|
92
|
-
return matches;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
function parsePermissionValue(value, options = {}) {
|
|
96
|
-
if (value === 'undefined') return options.undefinedPattern ? { ...UNDEFINED_MATCH_VALUE } : undefined;
|
|
97
|
-
if (/^(?:true|false|null|-?\d+(?:\.\d+)?)$/.test(value)) return JSON.parse(value);
|
|
98
|
-
return value;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function setMatchValue(target, key, value) {
|
|
102
|
-
const parts = key.split('.').filter(Boolean);
|
|
103
|
-
let current = target;
|
|
104
|
-
for (const part of parts.slice(0, -1)) {
|
|
105
|
-
if (!current[part] || typeof current[part] !== 'object' || Array.isArray(current[part])) current[part] = {};
|
|
106
|
-
current = current[part];
|
|
107
|
-
}
|
|
108
|
-
const finalKey = parts.at(-1) ?? key;
|
|
109
|
-
if (!Object.hasOwn(current, finalKey)) current[finalKey] = value;
|
|
110
|
-
else if (Array.isArray(current[finalKey])) current[finalKey].push(value);
|
|
111
|
-
else current[finalKey] = [current[finalKey], value];
|
|
112
|
-
}
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { UsageError } from '../cli/parse.mjs';
|
|
2
|
-
import { readSettings, writeSettings } from '../settings/load.mjs';
|
|
3
|
-
import { evaluatePermission, resolvePermissionDecision } from './permissions-eval.mjs';
|
|
4
|
-
import {
|
|
5
|
-
PERMISSIONS_SETTING,
|
|
6
|
-
builtinPermissionRules,
|
|
7
|
-
loadUserPermissionRules,
|
|
8
|
-
} from './permissions-rules.mjs';
|
|
9
|
-
import {
|
|
10
|
-
formatPermissionRule,
|
|
11
|
-
parseFlagMatches,
|
|
12
|
-
parsePermissionRule,
|
|
13
|
-
parsePermissionText,
|
|
14
|
-
} from './permissions-text.mjs';
|
|
15
|
-
|
|
16
|
-
export { resolvePermissionDecision };
|
|
17
|
-
|
|
18
|
-
export async function runPermissions(args, stdin = '', parsed = {}) {
|
|
19
|
-
const subcommand = args[0] ?? 'list';
|
|
20
|
-
if (subcommand === 'list') {
|
|
21
|
-
const rules = args.includes('--builtin')
|
|
22
|
-
? builtinPermissionRules()
|
|
23
|
-
: [...loadUserPermissionRules(parsed), ...builtinPermissionRules()];
|
|
24
|
-
for (const rule of rules) console.log(formatPermissionRule(rule));
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (subcommand === 'add') {
|
|
29
|
-
const settings = readSettings(parsed);
|
|
30
|
-
const rule = parsePermissionRule(args.slice(1));
|
|
31
|
-
settings[PERMISSIONS_SETTING] = [rule, ...(settings[PERMISSIONS_SETTING] ?? [])];
|
|
32
|
-
await writeSettings(settings, parsed);
|
|
33
|
-
console.log(`Added permission rule: ${rule.action} ${rule.tool}`);
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if (subcommand === 'edit') {
|
|
38
|
-
const settings = readSettings(parsed);
|
|
39
|
-
settings[PERMISSIONS_SETTING] = parsePermissionText(stdin);
|
|
40
|
-
await writeSettings(settings, parsed);
|
|
41
|
-
console.log(`Wrote ${settings[PERMISSIONS_SETTING].length} permission rule(s).`);
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
if (subcommand === 'test') {
|
|
46
|
-
const tool = args[1];
|
|
47
|
-
if (!tool) throw new UsageError('permissions test requires a tool name');
|
|
48
|
-
const toolArgs = parseFlagMatches(args.slice(2));
|
|
49
|
-
const context = typeof toolArgs.context === 'string' ? toolArgs.context : 'thread';
|
|
50
|
-
delete toolArgs.context;
|
|
51
|
-
const decision = evaluatePermission(tool, toolArgs, parsed, { context });
|
|
52
|
-
console.log(`tool: ${tool}`);
|
|
53
|
-
console.log(`arguments: ${JSON.stringify(toolArgs)}`);
|
|
54
|
-
console.log(`action: ${decision.action}`);
|
|
55
|
-
if (decision.to) console.log(`to: ${decision.to}`);
|
|
56
|
-
if (decision.matchedRule !== undefined) console.log(`matched-rule: ${decision.matchedRule}`);
|
|
57
|
-
console.log(`source: ${decision.source}`);
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
throw new UsageError(`Unknown permissions command: ${subcommand}`);
|
|
62
|
-
}
|
package/src/commands/plugins.mjs
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createPluginCommandContext,
|
|
3
|
-
loadPlugins,
|
|
4
|
-
} from '../plugins/discover.mjs';
|
|
5
|
-
import { UsageError } from '../cli/parse.mjs';
|
|
6
|
-
import { printRows } from '../util/table.mjs';
|
|
7
|
-
|
|
8
|
-
export async function runPlugins(args) {
|
|
9
|
-
const subcommand = args[0] ?? 'list';
|
|
10
|
-
|
|
11
|
-
if (subcommand === 'list') {
|
|
12
|
-
const runtime = await loadPlugins(process.cwd());
|
|
13
|
-
if (runtime.pluginSummaries.length === 0) {
|
|
14
|
-
console.log('No plugins found.');
|
|
15
|
-
return;
|
|
16
|
-
}
|
|
17
|
-
for (const plugin of runtime.pluginSummaries) {
|
|
18
|
-
console.log(formatPluginSummary(plugin));
|
|
19
|
-
}
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
if (subcommand === 'reload') {
|
|
24
|
-
const runtime = await loadPlugins(process.cwd());
|
|
25
|
-
console.log(`Reloaded ${runtime.pluginSummaries.length} plugin(s).`);
|
|
26
|
-
for (const plugin of runtime.pluginSummaries) {
|
|
27
|
-
console.log(formatPluginSummary(plugin));
|
|
28
|
-
}
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
if (subcommand === 'commands') {
|
|
33
|
-
const commands = (await loadPlugins(process.cwd())).commands;
|
|
34
|
-
if (commands.length === 0) {
|
|
35
|
-
console.log('No plugin commands found.');
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
printRows(commands
|
|
39
|
-
.filter((command) => command.availability.type !== 'hidden')
|
|
40
|
-
.map((command) => [
|
|
41
|
-
command.name,
|
|
42
|
-
command.availability.type,
|
|
43
|
-
command.metadata.category ?? '',
|
|
44
|
-
command.metadata.title ?? command.name,
|
|
45
|
-
command.availability.type === 'disabled'
|
|
46
|
-
? command.availability.reason ?? command.metadata.description ?? ''
|
|
47
|
-
: command.metadata.description ?? '',
|
|
48
|
-
]));
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if (subcommand === 'run') {
|
|
53
|
-
const commandName = args[1];
|
|
54
|
-
if (!commandName) throw new UsageError('plugins run requires a command name');
|
|
55
|
-
const plugins = await loadPlugins(process.cwd());
|
|
56
|
-
const command = plugins.commands.find((entry) => entry.name === commandName);
|
|
57
|
-
if (!command) throw new UsageError(`Unknown plugin command: ${commandName}`);
|
|
58
|
-
if (command.availability.type === 'hidden') throw new UsageError(`Unknown plugin command: ${commandName}`);
|
|
59
|
-
if (command.availability.type === 'disabled') {
|
|
60
|
-
throw new UsageError(command.availability.reason ?? `Plugin command disabled: ${commandName}`);
|
|
61
|
-
}
|
|
62
|
-
const ctx = createPluginCommandContext(process.cwd());
|
|
63
|
-
const result = await command.handler(ctx);
|
|
64
|
-
if (result !== undefined) console.log(String(result));
|
|
65
|
-
for (const message of plugins.notifications) console.log(message);
|
|
66
|
-
for (const message of ctx.notifications) console.log(message);
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
throw new UsageError(`Unknown plugins command: ${subcommand}`);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function formatPluginSummary(plugin) {
|
|
74
|
-
return [
|
|
75
|
-
plugin.name,
|
|
76
|
-
plugin.source,
|
|
77
|
-
`tools=${formatRegistrations(plugin.tools)}`,
|
|
78
|
-
`commands=${formatRegistrations(plugin.commands)}`,
|
|
79
|
-
`events=${formatRegistrations(plugin.events)}`,
|
|
80
|
-
plugin.path,
|
|
81
|
-
].join('\t');
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function formatRegistrations(values = []) {
|
|
85
|
-
return values.length > 0 ? values.join(',') : '-';
|
|
86
|
-
}
|
package/src/commands/review.mjs
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { CONFIG_SUBDIR } from '../constants.mjs';
|
|
4
|
-
import { configDir, findProjectRoot } from '../settings/paths.mjs';
|
|
5
|
-
import { readFrontmatter, splitListValue } from '../settings/load.mjs';
|
|
6
|
-
import { printRows } from '../util/table.mjs';
|
|
7
|
-
|
|
8
|
-
export function runReview() {
|
|
9
|
-
const checks = discoverReviewChecks(process.cwd());
|
|
10
|
-
console.log('Review jobs:');
|
|
11
|
-
if (checks.length > 0) {
|
|
12
|
-
printRows(checks.map((check) => [
|
|
13
|
-
check.name,
|
|
14
|
-
'passed',
|
|
15
|
-
check.severity,
|
|
16
|
-
check.tools.length ? check.tools.join(', ') : '-',
|
|
17
|
-
check.displayPath,
|
|
18
|
-
check.description || '-',
|
|
19
|
-
]));
|
|
20
|
-
} else {
|
|
21
|
-
console.log('(no configured checks)');
|
|
22
|
-
}
|
|
23
|
-
console.log('No review findings from configured local checks.');
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function discoverReviewChecks(cwd) {
|
|
27
|
-
const root = findProjectRoot(cwd);
|
|
28
|
-
const byName = new Map();
|
|
29
|
-
for (const dir of reviewCheckDirs(cwd, root)) {
|
|
30
|
-
for (const check of readReviewChecksFromDir(dir, root)) {
|
|
31
|
-
byName.set(check.name, check);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
return [...byName.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function reviewCheckDirs(cwd, root) {
|
|
38
|
-
const dirs = [
|
|
39
|
-
path.join(configDir(), CONFIG_SUBDIR, 'checks'),
|
|
40
|
-
path.join(configDir(), 'agents', 'checks'),
|
|
41
|
-
];
|
|
42
|
-
const ancestors = [];
|
|
43
|
-
let current = cwd;
|
|
44
|
-
while (true) {
|
|
45
|
-
ancestors.push(current);
|
|
46
|
-
if (current === root) break;
|
|
47
|
-
const parent = path.dirname(current);
|
|
48
|
-
if (parent === current) break;
|
|
49
|
-
current = parent;
|
|
50
|
-
}
|
|
51
|
-
for (const dir of ancestors.reverse()) {
|
|
52
|
-
dirs.push(path.join(dir, '.agents', 'checks'));
|
|
53
|
-
}
|
|
54
|
-
return dirs;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function readReviewChecksFromDir(dir, root) {
|
|
58
|
-
if (!existsSync(dir)) return [];
|
|
59
|
-
return readdirSync(dir)
|
|
60
|
-
.filter((entry) => entry.toLowerCase().endsWith('.md'))
|
|
61
|
-
.sort((a, b) => a.localeCompare(b))
|
|
62
|
-
.map((entry) => readReviewCheck(path.join(dir, entry), root));
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function readReviewCheck(filePath, root) {
|
|
66
|
-
const metadata = readFrontmatter(readFileSync(filePath, 'utf8'));
|
|
67
|
-
return {
|
|
68
|
-
name: metadata.name || path.basename(filePath, path.extname(filePath)),
|
|
69
|
-
description: metadata.description || '',
|
|
70
|
-
severity: metadata['severity-default'] || 'medium',
|
|
71
|
-
tools: metadata.tools ? splitListValue(metadata.tools) : [],
|
|
72
|
-
displayPath: path.relative(root, filePath) || path.basename(filePath),
|
|
73
|
-
};
|
|
74
|
-
}
|
package/src/commands/skill.mjs
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from 'node:fs';
|
|
2
|
-
import { listSkills } from '../skills/discover.mjs';
|
|
3
|
-
import { UsageError } from '../cli/parse.mjs';
|
|
4
|
-
import { printRows } from '../util/table.mjs';
|
|
5
|
-
|
|
6
|
-
export async function runSkill(args, parsed = {}) {
|
|
7
|
-
const subcommand = args[0] ?? 'list';
|
|
8
|
-
|
|
9
|
-
if (subcommand === 'list') {
|
|
10
|
-
const rows = listSkills({ parsed }).map((skill) => [skill.name, skill.source, skill.description]);
|
|
11
|
-
printRows(rows.length ? rows : [['(none)', '-', '-']]);
|
|
12
|
-
return;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
if (subcommand === 'show') {
|
|
16
|
-
const skill = listSkills({ parsed }).find((candidate) => candidate.name === args[1]);
|
|
17
|
-
if (!skill) throw new UsageError(`Unknown skill: ${args[1] ?? ''}`);
|
|
18
|
-
process.stdout.write(readFileSync(skill.filePath, 'utf8'));
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
throw new UsageError(`Unknown skill command: ${subcommand}`);
|
|
23
|
-
}
|
package/src/commands/threads.mjs
DELETED
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
import { randomUUID } from 'node:crypto';
|
|
2
|
-
import {
|
|
3
|
-
latestActiveThread,
|
|
4
|
-
listThreads,
|
|
5
|
-
normalizeThreadVisibility,
|
|
6
|
-
requireThread,
|
|
7
|
-
THREAD_VISIBILITY_INPUTS,
|
|
8
|
-
threadSearchText,
|
|
9
|
-
writeThread,
|
|
10
|
-
} from '../threads/store.mjs';
|
|
11
|
-
import { runExecute } from '../cli/execute.mjs';
|
|
12
|
-
import { THREAD_URL_BASE } from '../constants.mjs';
|
|
13
|
-
import { UsageError } from '../cli/parse.mjs';
|
|
14
|
-
import { printRows } from '../util/table.mjs';
|
|
15
|
-
|
|
16
|
-
export async function runThreads(args, parsed = {}, stdin = '') {
|
|
17
|
-
const subcommand = args[0] ?? 'list';
|
|
18
|
-
|
|
19
|
-
if (subcommand === 'continue') {
|
|
20
|
-
if (!parsed.execute) throw new UsageError('threads continue requires --execute');
|
|
21
|
-
const threadId = args.slice(1).find((arg) => !arg.startsWith('-'));
|
|
22
|
-
const thread = threadId ? requireThread(threadId) : latestActiveThread();
|
|
23
|
-
if (!thread) throw new UsageError('No active thread to continue');
|
|
24
|
-
await runExecute(parsed, stdin, { thread });
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (subcommand === 'list') {
|
|
29
|
-
const includeArchived = args.includes('--archived');
|
|
30
|
-
const rows = listThreads()
|
|
31
|
-
.filter((thread) => includeArchived || !thread.archived)
|
|
32
|
-
.map((thread) => [
|
|
33
|
-
thread.id,
|
|
34
|
-
thread.archived ? 'archived' : 'active',
|
|
35
|
-
thread.visibility ?? 'private',
|
|
36
|
-
formatLabels(thread.labels),
|
|
37
|
-
thread.title,
|
|
38
|
-
]);
|
|
39
|
-
printRows(rows.length ? rows : [['(none)', '-', '-', '-', '-']]);
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
if (subcommand === 'show') {
|
|
44
|
-
const thread = requireThread(args[1]);
|
|
45
|
-
console.log(`thread_id: ${thread.id}`);
|
|
46
|
-
console.log(`url: ${THREAD_URL_BASE}/${thread.id}`);
|
|
47
|
-
console.log(`status: ${thread.archived ? 'archived' : 'active'}`);
|
|
48
|
-
console.log(`visibility: ${thread.visibility ?? 'private'}`);
|
|
49
|
-
console.log(`labels: ${formatLabels(thread.labels, ', ')}`);
|
|
50
|
-
console.log(`cwd: ${thread.cwd}`);
|
|
51
|
-
for (const message of thread.messages) {
|
|
52
|
-
console.log(`${message.role}: ${message.content}`);
|
|
53
|
-
}
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (subcommand === 'search') {
|
|
58
|
-
const query = args.slice(1).join(' ').toLowerCase();
|
|
59
|
-
const rows = listThreads()
|
|
60
|
-
.filter((thread) => threadSearchText(thread).toLowerCase().includes(query))
|
|
61
|
-
.map((thread) => [thread.id, thread.archived ? 'archived' : 'active', thread.visibility ?? 'private', formatLabels(thread.labels), thread.title]);
|
|
62
|
-
printRows(rows.length ? rows : [['(none)', '-', '-', '-', '-']]);
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (subcommand === 'map') {
|
|
67
|
-
const root = args[1] ? requireThread(args[1]) : latestActiveThread();
|
|
68
|
-
if (!root) throw new UsageError('No active thread to map');
|
|
69
|
-
console.log(`Thread map for ${root.id}`);
|
|
70
|
-
for (const row of threadMapRows(root.id)) {
|
|
71
|
-
console.log(row);
|
|
72
|
-
}
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
if (subcommand === 'archive') {
|
|
77
|
-
const thread = requireThread(args[1]);
|
|
78
|
-
thread.archived = true;
|
|
79
|
-
thread.updatedAt = new Date().toISOString();
|
|
80
|
-
await writeThread(thread);
|
|
81
|
-
console.log(`Archived thread ${thread.id}`);
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
if (subcommand === 'visibility' || subcommand === 'set-visibility') {
|
|
86
|
-
const thread = requireThread(args[1]);
|
|
87
|
-
const visibility = normalizeThreadVisibility(args[2]);
|
|
88
|
-
if (!visibility) {
|
|
89
|
-
throw new UsageError(`visibility must be one of: ${THREAD_VISIBILITY_INPUTS.join(', ')}`);
|
|
90
|
-
}
|
|
91
|
-
thread.visibility = visibility;
|
|
92
|
-
thread.updatedAt = new Date().toISOString();
|
|
93
|
-
await writeThread(thread);
|
|
94
|
-
console.log(`Set ${thread.id} visibility to ${visibility}`);
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if (subcommand === 'report') {
|
|
99
|
-
const thread = requireThread(args[1]);
|
|
100
|
-
const reportId = `R-${randomUUID()}`;
|
|
101
|
-
console.log(`diagnostic_report_id: ${reportId}`);
|
|
102
|
-
console.log(`thread_id: ${thread.id}`);
|
|
103
|
-
console.log('retention: 7 days');
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
throw new UsageError(`Unknown threads command: ${subcommand}`);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
function formatLabels(labels = [], separator = ',') {
|
|
111
|
-
return labels.length > 0 ? labels.join(separator) : '-';
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
function threadMapRows(rootId) {
|
|
115
|
-
const threads = listThreads();
|
|
116
|
-
const byId = new Map(threads.map((thread) => [thread.id, thread]));
|
|
117
|
-
const edges = threadReferenceEdges(threads);
|
|
118
|
-
const seen = new Set();
|
|
119
|
-
const queue = [rootId];
|
|
120
|
-
const rows = [];
|
|
121
|
-
|
|
122
|
-
while (queue.length > 0) {
|
|
123
|
-
const threadId = queue.shift();
|
|
124
|
-
if (seen.has(threadId)) continue;
|
|
125
|
-
seen.add(threadId);
|
|
126
|
-
const thread = byId.get(threadId);
|
|
127
|
-
if (!thread) continue;
|
|
128
|
-
rows.push(`${thread.id} ${thread.title}`);
|
|
129
|
-
|
|
130
|
-
for (const edge of edges.filter((entry) => entry.from === threadId || entry.to === threadId)) {
|
|
131
|
-
const neighborId = edge.from === threadId ? edge.to : edge.from;
|
|
132
|
-
const neighbor = byId.get(neighborId);
|
|
133
|
-
if (!neighbor) continue;
|
|
134
|
-
const label = edge.from === threadId ? 'mentions ->' : '<- mentioned by';
|
|
135
|
-
rows.push(` ${label} ${neighbor.id} ${neighbor.title}`);
|
|
136
|
-
if (!seen.has(neighborId)) queue.push(neighborId);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
return rows;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
function threadReferenceEdges(threads) {
|
|
144
|
-
const knownIds = new Set(threads.map((thread) => thread.id));
|
|
145
|
-
const edges = [];
|
|
146
|
-
const seen = new Set();
|
|
147
|
-
for (const thread of threads) {
|
|
148
|
-
for (const message of thread.messages ?? []) {
|
|
149
|
-
for (const ref of threadReferences(message.content)) {
|
|
150
|
-
if (ref === thread.id || !knownIds.has(ref)) continue;
|
|
151
|
-
const key = `${thread.id}->${ref}`;
|
|
152
|
-
if (seen.has(key)) continue;
|
|
153
|
-
seen.add(key);
|
|
154
|
-
edges.push({ from: thread.id, to: ref });
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
return edges;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
function threadReferences(text = '') {
|
|
162
|
-
const threadUrlPattern = THREAD_URL_BASE.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
163
|
-
return [...String(text).matchAll(new RegExp(`(?:@|${threadUrlPattern}/)(T-[A-Za-z0-9-]+)`, 'g'))]
|
|
164
|
-
.map((match) => match[1]);
|
|
165
|
-
}
|
package/src/commands/tools.mjs
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import { chmod, mkdir, writeFile } from 'node:fs/promises';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { BUILTIN_TOOLS, CLI_NAME } from '../constants.mjs';
|
|
4
|
-
import { writableToolsDir } from '../settings/paths.mjs';
|
|
5
|
-
import { listConfiguredMcpServers } from '../mcp/discover.mjs';
|
|
6
|
-
import { discoverMcpToolRows } from '../mcp/probe.mjs';
|
|
7
|
-
import { listPluginTools } from '../plugins/discover.mjs';
|
|
8
|
-
import {
|
|
9
|
-
executeToolboxTool,
|
|
10
|
-
isToolDisabled,
|
|
11
|
-
listToolboxTools,
|
|
12
|
-
normalizeToolName,
|
|
13
|
-
parseToolUseArgs,
|
|
14
|
-
printToolboxSchema,
|
|
15
|
-
toolboxTemplate,
|
|
16
|
-
} from '../tools/toolbox.mjs';
|
|
17
|
-
import { UsageError } from '../cli/parse.mjs';
|
|
18
|
-
import { printRows } from '../util/table.mjs';
|
|
19
|
-
|
|
20
|
-
export async function runTools(args, stdin, parsed = {}) {
|
|
21
|
-
const subcommand = args[0] ?? 'list';
|
|
22
|
-
if (subcommand === 'list') {
|
|
23
|
-
const mcpToolRows = await discoverMcpToolRows(
|
|
24
|
-
listConfiguredMcpServers(parsed).filter((server) => server.status === 'approved'),
|
|
25
|
-
);
|
|
26
|
-
const pluginToolRows = (await listPluginTools(process.cwd()))
|
|
27
|
-
.map((tool) => [tool.name, 'plugin', tool.description]);
|
|
28
|
-
const rows = [
|
|
29
|
-
...BUILTIN_TOOLS,
|
|
30
|
-
...mcpToolRows,
|
|
31
|
-
...pluginToolRows,
|
|
32
|
-
...listToolboxTools(parsed).map((tool) => [tool.name, 'toolbox', tool.description]),
|
|
33
|
-
].filter(([name, kind]) => !isToolDisabled(name, kind, parsed));
|
|
34
|
-
printRows(rows);
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (subcommand === 'make') {
|
|
39
|
-
const shell = args.includes('--zsh') ? 'zsh' : args.includes('--bash') ? 'bash' : 'js';
|
|
40
|
-
const rawName = args.find((arg) => !arg.startsWith('-') && arg !== 'make') ?? 'tool';
|
|
41
|
-
const toolName = rawName.replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
42
|
-
const filePath = path.join(writableToolsDir(), toolName);
|
|
43
|
-
await mkdir(path.dirname(filePath), { recursive: true });
|
|
44
|
-
await writeFile(filePath, toolboxTemplate(shell, toolName), 'utf8');
|
|
45
|
-
await chmod(filePath, 0o755);
|
|
46
|
-
console.log(`Tool created at: ${filePath}`);
|
|
47
|
-
console.log(`Inspect with: ${CLI_NAME} tools show tb__${toolName}`);
|
|
48
|
-
console.log(`Execute with: ${CLI_NAME} tools use tb__${toolName}`);
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if (subcommand === 'show') {
|
|
53
|
-
const toolName = normalizeToolName(args[1]);
|
|
54
|
-
const tool = listToolboxTools(parsed).find((entry) => entry.name === toolName);
|
|
55
|
-
if (!tool) throw new UsageError(`Unknown tool: ${args[1] ?? ''}`);
|
|
56
|
-
printToolboxSchema(tool);
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
if (subcommand === 'use') {
|
|
61
|
-
const useArgs = parseToolUseArgs(args.slice(1));
|
|
62
|
-
const toolName = normalizeToolName(useArgs.toolName);
|
|
63
|
-
const tool = listToolboxTools(parsed).find((entry) => entry.name === toolName);
|
|
64
|
-
if (!tool) throw new UsageError(`Unknown tool: ${useArgs.toolName ?? ''}`);
|
|
65
|
-
const result = executeToolboxTool(tool, useArgs.flags, stdin, useArgs.threadId);
|
|
66
|
-
if (useArgs.onlyOutput) {
|
|
67
|
-
process.stdout.write(result.stdout);
|
|
68
|
-
} else {
|
|
69
|
-
if (result.stderr) process.stderr.write(result.stderr);
|
|
70
|
-
process.stdout.write(`${JSON.stringify({ output: result.stdout, exitCode: result.status ?? 0 }, null, 2)}\n`);
|
|
71
|
-
}
|
|
72
|
-
process.exitCode = result.status ?? 0;
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
throw new UsageError(`Unknown tools command: ${subcommand}`);
|
|
77
|
-
}
|
package/src/commands/update.mjs
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { CLI_NAME, VERSION } from '../constants.mjs';
|
|
2
|
-
import { readEffectiveSettings } from '../settings/load.mjs';
|
|
3
|
-
|
|
4
|
-
export function runUpdate(parsed = {}) {
|
|
5
|
-
if (process.env.COVEN_CODE_SKIP_UPDATE_CHECK === '1') {
|
|
6
|
-
console.log('update_check: skipped');
|
|
7
|
-
console.log('reason: COVEN_CODE_SKIP_UPDATE_CHECK=1');
|
|
8
|
-
console.log('update_action: none');
|
|
9
|
-
console.log(`version: ${VERSION}`);
|
|
10
|
-
return;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const mode = updateMode(readEffectiveSettings(parsed)['covenCode.updates.mode']);
|
|
14
|
-
if (mode === 'disabled') {
|
|
15
|
-
console.log('update_check: disabled');
|
|
16
|
-
console.log('mode: disabled');
|
|
17
|
-
console.log('update_action: none');
|
|
18
|
-
console.log(`version: ${VERSION}`);
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
console.log('update_check: checked');
|
|
23
|
-
console.log(`mode: ${mode}`);
|
|
24
|
-
console.log(`update_action: ${mode === 'warn' ? 'notify' : 'auto'}`);
|
|
25
|
-
console.log(`version: ${VERSION}`);
|
|
26
|
-
console.log(`status: ${CLI_NAME} ${VERSION} is current in this local recreation.`);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function updateMode(value) {
|
|
30
|
-
return ['auto', 'warn', 'disabled'].includes(value) ? value : 'auto';
|
|
31
|
-
}
|