@opencoven/coven-code 0.0.1

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 (86) hide show
  1. package/README.md +145 -0
  2. package/bin/coven-code-sdk.mjs +12 -0
  3. package/bin/coven-code.mjs +19 -0
  4. package/docs/CLI.md +192 -0
  5. package/docs/CONFIGURATION.md +107 -0
  6. package/docs/DEVELOPMENT.md +104 -0
  7. package/docs/DOGFOOD-PROTOCOL.md +263 -0
  8. package/docs/MCP-SKILLS-PLUGINS.md +127 -0
  9. package/docs/README.md +38 -0
  10. package/docs/RELEASE.md +33 -0
  11. package/docs/SDK.md +107 -0
  12. package/docs/superpowers/plans/2026-05-25-coven-code-panel-tui.md +904 -0
  13. package/docs/superpowers/plans/2026-05-25-coven-code-rebrand.md +670 -0
  14. package/docs/superpowers/specs/2026-05-25-coven-code-panel-tui-design.md +235 -0
  15. package/docs/superpowers/specs/2026-05-26-slash-first-tui-review.md +63 -0
  16. package/package.json +36 -0
  17. package/src/agent/lane.mjs +136 -0
  18. package/src/agent/local.mjs +95 -0
  19. package/src/cli/dispatch.mjs +66 -0
  20. package/src/cli/execute.mjs +588 -0
  21. package/src/cli/help.mjs +58 -0
  22. package/src/cli/interactive-core.mjs +302 -0
  23. package/src/cli/notifications.mjs +13 -0
  24. package/src/cli/parse.mjs +83 -0
  25. package/src/cli/reasoning.mjs +45 -0
  26. package/src/cli/refs.mjs +162 -0
  27. package/src/cli/repl.mjs +61 -0
  28. package/src/cli/slash-commands.mjs +357 -0
  29. package/src/cli/stream-json.mjs +116 -0
  30. package/src/cli/tui.mjs +757 -0
  31. package/src/commands/agents.mjs +53 -0
  32. package/src/commands/config.mjs +27 -0
  33. package/src/commands/ide.mjs +17 -0
  34. package/src/commands/login.mjs +84 -0
  35. package/src/commands/mcp.mjs +176 -0
  36. package/src/commands/permissions.mjs +328 -0
  37. package/src/commands/plugins.mjs +86 -0
  38. package/src/commands/review.mjs +74 -0
  39. package/src/commands/skill.mjs +23 -0
  40. package/src/commands/threads.mjs +165 -0
  41. package/src/commands/tools.mjs +77 -0
  42. package/src/commands/update.mjs +31 -0
  43. package/src/commands/usage.mjs +34 -0
  44. package/src/constants.mjs +46 -0
  45. package/src/main.mjs +87 -0
  46. package/src/mcp/discover.mjs +154 -0
  47. package/src/mcp/permissions.mjs +52 -0
  48. package/src/mcp/probe.mjs +424 -0
  49. package/src/mcp/registry.mjs +96 -0
  50. package/src/plugins/discover.mjs +880 -0
  51. package/src/sdk-install.mjs +187 -0
  52. package/src/sdk.mjs +314 -0
  53. package/src/settings/load.mjs +134 -0
  54. package/src/settings/paths.mjs +101 -0
  55. package/src/skills/builtin/building-skills/SKILL.md +20 -0
  56. package/src/skills/discover.mjs +95 -0
  57. package/src/threads/store.mjs +176 -0
  58. package/src/tools/builtin/bash.mjs +110 -0
  59. package/src/tools/builtin/create-file.mjs +66 -0
  60. package/src/tools/builtin/edit-file.mjs +76 -0
  61. package/src/tools/builtin/finder.mjs +73 -0
  62. package/src/tools/builtin/glob.mjs +74 -0
  63. package/src/tools/builtin/grep.mjs +82 -0
  64. package/src/tools/builtin/index.mjs +83 -0
  65. package/src/tools/builtin/librarian.mjs +97 -0
  66. package/src/tools/builtin/look-at.mjs +92 -0
  67. package/src/tools/builtin/mcp.mjs +51 -0
  68. package/src/tools/builtin/mermaid.mjs +59 -0
  69. package/src/tools/builtin/oracle.mjs +56 -0
  70. package/src/tools/builtin/painter.mjs +81 -0
  71. package/src/tools/builtin/plugin-tool.mjs +53 -0
  72. package/src/tools/builtin/read-mcp-resource.mjs +63 -0
  73. package/src/tools/builtin/read-web-page.mjs +72 -0
  74. package/src/tools/builtin/read.mjs +59 -0
  75. package/src/tools/builtin/runtime.mjs +215 -0
  76. package/src/tools/builtin/task.mjs +63 -0
  77. package/src/tools/builtin/toolbox-tool.mjs +57 -0
  78. package/src/tools/builtin/undo-edit.mjs +97 -0
  79. package/src/tools/builtin/web-search.mjs +128 -0
  80. package/src/tools/toolbox.mjs +273 -0
  81. package/src/util/fs.mjs +13 -0
  82. package/src/util/glob.mjs +46 -0
  83. package/src/util/html.mjs +21 -0
  84. package/src/util/media.mjs +13 -0
  85. package/src/util/shell.mjs +24 -0
  86. package/src/util/table.mjs +11 -0
@@ -0,0 +1,86 @@
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
+ }
@@ -0,0 +1,74 @@
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
+ }
@@ -0,0 +1,23 @@
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
+ }
@@ -0,0 +1,165 @@
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
+ }
@@ -0,0 +1,77 @@
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
+ }
@@ -0,0 +1,31 @@
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
+ }
@@ -0,0 +1,34 @@
1
+ import { listThreads } from '../threads/store.mjs';
2
+ import { estimateTokenCount } from '../agent/local.mjs';
3
+ import { readEffectiveSettings } from '../settings/load.mjs';
4
+
5
+ export function runUsage(parsed = {}) {
6
+ const summary = localUsageSummary();
7
+ const showCosts = readEffectiveSettings(parsed)['covenCode.showCosts'] !== false;
8
+ if (showCosts) console.log('remote_balance: unavailable (local recreation)');
9
+ console.log(`threads: ${summary.threads}`);
10
+ console.log(`turns: ${summary.turns}`);
11
+ if (showCosts) {
12
+ console.log(`input_tokens_estimate: ${summary.inputTokens}`);
13
+ console.log(`output_tokens_estimate: ${summary.outputTokens}`);
14
+ }
15
+ }
16
+
17
+ function localUsageSummary() {
18
+ const threads = listThreads();
19
+ let turns = 0;
20
+ let inputTokens = 0;
21
+ let outputTokens = 0;
22
+ for (const thread of threads) {
23
+ const messages = Array.isArray(thread.messages) ? thread.messages : [];
24
+ for (const message of messages) {
25
+ if (message.role === 'user') {
26
+ turns += 1;
27
+ inputTokens += estimateTokenCount(message.content ?? '');
28
+ } else if (message.role === 'assistant') {
29
+ outputTokens += estimateTokenCount(message.content ?? '');
30
+ }
31
+ }
32
+ }
33
+ return { threads: threads.length, turns, inputTokens, outputTokens };
34
+ }
@@ -0,0 +1,46 @@
1
+ export const VERSION = '0.0.0-recreate';
2
+ export const PRODUCT_NAME = 'Coven Code';
3
+ export const CLI_NAME = 'coven-code';
4
+ export const PACKAGE_NAME = '@opencoven/coven-code';
5
+ export const CONFIG_SUBDIR = 'coven-code';
6
+ export const PROJECT_SUBDIR = '.coven-code';
7
+ export const THREAD_URL_BASE = 'https://coven-code.local/threads';
8
+
9
+ export const FILE_MENTION_MAX_LINES = 500;
10
+ export const FILE_MENTION_MAX_LINE_LENGTH = 2048;
11
+ export const REPL_HISTORY_LIMIT = 500;
12
+
13
+ export const AGENT_MODES = ['smart', 'deep', 'rush', 'large'];
14
+
15
+ export const BUILTIN_TOOLS = [
16
+ ['Bash', 'built-in', "Executes the given shell command in the user's default shell"],
17
+ ['Read', 'built-in', 'Reads a UTF-8 text file from disk'],
18
+ ['Grep', 'built-in', 'Searches files by regular expression'],
19
+ ['glob', 'built-in', 'Finds files by glob pattern'],
20
+ ['create_file', 'built-in', 'Creates a new file'],
21
+ ['edit_file', 'built-in', 'Edits an existing file'],
22
+ ['oracle', 'built-in', 'Asks for second-opinion review or reasoning'],
23
+ ['librarian', 'built-in', 'Researches code across repositories or the current workspace'],
24
+ ['Task', 'built-in', 'Runs an isolated subagent for delegated work'],
25
+ ['painter', 'built-in', 'Generates or edits local image artifacts'],
26
+ ['mermaid', 'built-in', 'Renders a Mermaid diagram from the provided code'],
27
+ ['look_at', 'built-in', 'Inspects images, PDFs, or media files with a goal'],
28
+ ['web_search', 'built-in', 'Searches the web for information'],
29
+ ['read_web_page', 'built-in', 'Reads and extracts text from a web page'],
30
+ ['undo_edit', 'built-in', 'Undoes the latest edit_file change'],
31
+ ['find_thread', 'built-in', 'Finds prior threads by keyword, file, or task context'],
32
+ ['finder', 'built-in', 'Finds prior threads by keyword, file, or task context'],
33
+ ['read_mcp_resource', 'built-in', 'Reads a resource from a configured MCP server'],
34
+ ];
35
+
36
+ export const BUILTIN_PERMISSIONS = [
37
+ ['allow', 'Bash', 'ls'],
38
+ ['allow', 'Bash', 'git status'],
39
+ ['allow', 'Bash', 'git diff'],
40
+ ['allow', 'Bash', 'git log'],
41
+ ['allow', 'Bash', 'npm test'],
42
+ ['allow', 'Bash', 'cargo build'],
43
+ ['ask', 'Bash', 'git push'],
44
+ ['ask', 'Bash', 'rm -rf'],
45
+ ['ask', 'Bash', 'sudo'],
46
+ ];
package/src/main.mjs ADDED
@@ -0,0 +1,87 @@
1
+ import { parseGlobalArgs, UsageError } from './cli/parse.mjs';
2
+ import { printHelp } from './cli/help.mjs';
3
+ import { runCommand } from './cli/dispatch.mjs';
4
+ import { runExecute } from './cli/execute.mjs';
5
+ import { runInteractive } from './cli/repl.mjs';
6
+ import { runTuiInteractive } from './cli/tui.mjs';
7
+ import { runIdeConnect } from './commands/ide.mjs';
8
+ import { readStdin } from './util/shell.mjs';
9
+ import { AGENT_MODES, VERSION } from './constants.mjs';
10
+ import { latestActiveThread, requireThread } from './threads/store.mjs';
11
+ import { reasoningEffortForMode } from './cli/reasoning.mjs';
12
+
13
+ export { UsageError };
14
+
15
+ export async function main() {
16
+ const args = process.argv.slice(2);
17
+ const parsed = parseGlobalArgs(args);
18
+ const stdin = readStdin();
19
+
20
+ if (parsed.help) {
21
+ printHelp();
22
+ return;
23
+ }
24
+
25
+ if (parsed.version) {
26
+ console.log(VERSION);
27
+ return;
28
+ }
29
+
30
+ if (parsed.ide) {
31
+ runIdeConnect(parsed.ide);
32
+ return;
33
+ }
34
+
35
+ const command = parsed.positionals[0];
36
+ if (command) {
37
+ await runCommand(command, parsed.positionals.slice(1), parsed, stdin);
38
+ return;
39
+ }
40
+
41
+ if (parsed.streamJson && !parsed.execute) {
42
+ throw new UsageError('--stream-json requires --execute');
43
+ }
44
+ if (parsed.streamJsonInput && !parsed.streamJson) {
45
+ throw new UsageError('--stream-json-input requires --stream-json');
46
+ }
47
+ if (parsed.continueThread && !parsed.execute) {
48
+ throw new UsageError('--continue requires --execute');
49
+ }
50
+ if (!AGENT_MODES.includes(parsed.mode)) {
51
+ throw new UsageError(`mode must be one of: ${AGENT_MODES.join(', ')}`);
52
+ }
53
+ parsed.reasoningEffort = reasoningEffortForMode(parsed.mode, parsed.reasoningEffort);
54
+
55
+ if (parsed.execute || (stdin.length > 0 && !process.stdout.isTTY && process.env.COVEN_CODE_TUI_SCRIPTED !== '1')) {
56
+ await runExecute(parsed, stdin, { thread: continuationThread(parsed) });
57
+ return;
58
+ }
59
+
60
+ if (process.env.COVEN_CODE_TUI_SCRIPTED === '1' || (process.stdout.isTTY && (process.stdin.isTTY || stdin.length > 0))) {
61
+ const runner = selectInteractiveRunner({
62
+ stdinIsTTY: Boolean(process.stdin.isTTY),
63
+ stdoutIsTTY: Boolean(process.stdout.isTTY),
64
+ env: process.env,
65
+ });
66
+ if (runner === 'tui') await runTuiInteractive(parsed, stdin);
67
+ else await runInteractive(parsed, stdin);
68
+ return;
69
+ }
70
+
71
+ printHelp();
72
+ }
73
+
74
+ export function selectInteractiveRunner({ stdinIsTTY, stdoutIsTTY, env }) {
75
+ if (env.COVEN_CODE_TUI_SCRIPTED === '1') return 'tui';
76
+ if (env.COVEN_CODE_REPL === '1') return 'repl';
77
+ if (stdinIsTTY && stdoutIsTTY) return 'tui';
78
+ return 'repl';
79
+ }
80
+
81
+ function continuationThread(parsed) {
82
+ if (!parsed.continueThread) return undefined;
83
+ if (typeof parsed.continueThread === 'string') return requireThread(parsed.continueThread);
84
+ const thread = latestActiveThread();
85
+ if (!thread) throw new UsageError('No active thread to continue');
86
+ return thread;
87
+ }