@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.
Files changed (115) hide show
  1. package/README.md +29 -130
  2. package/bin/coven-code +26 -0
  3. package/install.js +117 -0
  4. package/package.json +26 -23
  5. package/bin/coven-code-sdk.mjs +0 -12
  6. package/bin/coven-code.mjs +0 -19
  7. package/docs/CLI.md +0 -256
  8. package/docs/CONFIGURATION.md +0 -107
  9. package/docs/DEMO.md +0 -453
  10. package/docs/DEVELOPMENT.md +0 -104
  11. package/docs/DOGFOOD-PROTOCOL.md +0 -263
  12. package/docs/MCP-SKILLS-PLUGINS.md +0 -127
  13. package/docs/README.md +0 -39
  14. package/docs/RELEASE.md +0 -33
  15. package/docs/SDK.md +0 -107
  16. package/docs/superpowers/plans/2026-05-25-coven-code-panel-tui.md +0 -904
  17. package/docs/superpowers/plans/2026-05-25-coven-code-rebrand.md +0 -670
  18. package/docs/superpowers/specs/2026-05-25-coven-code-panel-tui-design.md +0 -235
  19. package/docs/superpowers/specs/2026-05-26-slash-first-tui-review.md +0 -63
  20. package/src/agent/fixture.mjs +0 -95
  21. package/src/agent/lane.mjs +0 -136
  22. package/src/cli/dispatch.mjs +0 -66
  23. package/src/cli/execute.mjs +0 -590
  24. package/src/cli/help.mjs +0 -58
  25. package/src/cli/interactive-core.mjs +0 -28
  26. package/src/cli/interactive-io.mjs +0 -101
  27. package/src/cli/interactive-slash.mjs +0 -184
  28. package/src/cli/notifications.mjs +0 -13
  29. package/src/cli/parse.mjs +0 -83
  30. package/src/cli/reasoning.mjs +0 -45
  31. package/src/cli/refs.mjs +0 -162
  32. package/src/cli/repl.mjs +0 -60
  33. package/src/cli/slash-commands.mjs +0 -375
  34. package/src/cli/stream-json.mjs +0 -116
  35. package/src/cli/tui-actions.mjs +0 -72
  36. package/src/cli/tui-blessed.mjs +0 -198
  37. package/src/cli/tui-keys.mjs +0 -80
  38. package/src/cli/tui-lane.mjs +0 -73
  39. package/src/cli/tui-render.mjs +0 -169
  40. package/src/cli/tui-submit.mjs +0 -82
  41. package/src/cli/tui.mjs +0 -174
  42. package/src/commands/agents.mjs +0 -53
  43. package/src/commands/config.mjs +0 -27
  44. package/src/commands/ide.mjs +0 -17
  45. package/src/commands/login.mjs +0 -84
  46. package/src/commands/mcp.mjs +0 -176
  47. package/src/commands/permissions-eval.mjs +0 -122
  48. package/src/commands/permissions-rules.mjs +0 -53
  49. package/src/commands/permissions-text.mjs +0 -112
  50. package/src/commands/permissions.mjs +0 -62
  51. package/src/commands/plugins.mjs +0 -86
  52. package/src/commands/review.mjs +0 -74
  53. package/src/commands/skill.mjs +0 -23
  54. package/src/commands/threads.mjs +0 -165
  55. package/src/commands/tools.mjs +0 -77
  56. package/src/commands/update.mjs +0 -31
  57. package/src/commands/usage.mjs +0 -34
  58. package/src/constants.mjs +0 -52
  59. package/src/main.mjs +0 -87
  60. package/src/mcp/discover.mjs +0 -154
  61. package/src/mcp/local.mjs +0 -55
  62. package/src/mcp/parsers.mjs +0 -46
  63. package/src/mcp/permissions.mjs +0 -52
  64. package/src/mcp/probe.mjs +0 -85
  65. package/src/mcp/registry.mjs +0 -96
  66. package/src/mcp/remote-oauth.mjs +0 -55
  67. package/src/mcp/remote-session.mjs +0 -54
  68. package/src/mcp/remote-sse.mjs +0 -82
  69. package/src/mcp/remote.mjs +0 -74
  70. package/src/plugins/api.mjs +0 -187
  71. package/src/plugins/configuration.mjs +0 -124
  72. package/src/plugins/discover.mjs +0 -84
  73. package/src/plugins/helpers.mjs +0 -187
  74. package/src/plugins/subsystems.mjs +0 -198
  75. package/src/plugins/validators.mjs +0 -142
  76. package/src/sdk-execute.mjs +0 -82
  77. package/src/sdk-install.mjs +0 -187
  78. package/src/sdk-settings.mjs +0 -88
  79. package/src/sdk.mjs +0 -163
  80. package/src/settings/load.mjs +0 -134
  81. package/src/settings/paths.mjs +0 -101
  82. package/src/skills/builtin/building-skills/SKILL.md +0 -20
  83. package/src/skills/discover.mjs +0 -95
  84. package/src/threads/store.mjs +0 -176
  85. package/src/tools/builtin/bash.mjs +0 -110
  86. package/src/tools/builtin/create-file.mjs +0 -66
  87. package/src/tools/builtin/edit-file.mjs +0 -76
  88. package/src/tools/builtin/finder.mjs +0 -73
  89. package/src/tools/builtin/glob.mjs +0 -74
  90. package/src/tools/builtin/grep.mjs +0 -82
  91. package/src/tools/builtin/index.mjs +0 -83
  92. package/src/tools/builtin/librarian.mjs +0 -97
  93. package/src/tools/builtin/look-at.mjs +0 -92
  94. package/src/tools/builtin/mcp.mjs +0 -51
  95. package/src/tools/builtin/mermaid.mjs +0 -59
  96. package/src/tools/builtin/oracle.mjs +0 -56
  97. package/src/tools/builtin/painter.mjs +0 -81
  98. package/src/tools/builtin/plugin-tool.mjs +0 -53
  99. package/src/tools/builtin/read-mcp-resource.mjs +0 -63
  100. package/src/tools/builtin/read-web-page.mjs +0 -72
  101. package/src/tools/builtin/read.mjs +0 -59
  102. package/src/tools/builtin/runtime-content.mjs +0 -31
  103. package/src/tools/builtin/runtime-decisions.mjs +0 -115
  104. package/src/tools/builtin/runtime.mjs +0 -85
  105. package/src/tools/builtin/task.mjs +0 -63
  106. package/src/tools/builtin/toolbox-tool.mjs +0 -57
  107. package/src/tools/builtin/undo-edit.mjs +0 -97
  108. package/src/tools/builtin/web-search.mjs +0 -128
  109. package/src/tools/toolbox.mjs +0 -273
  110. package/src/util/fs.mjs +0 -13
  111. package/src/util/glob.mjs +0 -46
  112. package/src/util/html.mjs +0 -21
  113. package/src/util/media.mjs +0 -13
  114. package/src/util/shell.mjs +0 -24
  115. 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
- }
@@ -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
- }
@@ -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
- }
@@ -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
- }
@@ -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
- }
@@ -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
- }
@@ -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
- }