@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,235 @@
1
+ # Coven Code Panel TUI Design
2
+
3
+ Date: 2026-05-25
4
+ Status: Approved for implementation planning
5
+ Project: `OpenCoven/coven-code`
6
+
7
+ ## Summary
8
+
9
+ Bare `coven-code` should open a full-screen terminal UI instead of the current plain readline prompt. The TUI should make Coven Code feel like a real interactive coding surface while preserving the existing command engine, noninteractive modes, and slash-command behavior.
10
+
11
+ The selected direction is a panel-based TUI:
12
+
13
+ - Chat transcript as the main pane
14
+ - Composer at the bottom
15
+ - Status and shortcuts rail on the side
16
+ - Tabs for chat, tools, threads, config, and help
17
+ - Command palette for discoverable actions
18
+
19
+ ## Goals
20
+
21
+ - Make interactive `coven-code` feel intentional, keyboard-first, and product-grade.
22
+ - Preserve current CLI compatibility for scripts, tests, and docs.
23
+ - Reuse existing command handlers rather than rewriting tool, thread, config, and skill behavior.
24
+ - Keep the first implementation focused on a strong single-session experience, not a multi-session orchestration cockpit.
25
+ - Leave clear seams for later streaming, tool event inspection, approval prompts, and multi-session control.
26
+
27
+ ## Non-Goals
28
+
29
+ - Do not change `coven-code -x`, `--execute`, `--stream-json`, `--stream-json-input`, stdin piping, or command subcommands.
30
+ - Do not build the full multi-session control console in this pass.
31
+ - Do not introduce remote service dependencies.
32
+ - Do not replace the local deterministic runtime.
33
+ - Do not add decorative UI elements that do not improve operation or comprehension.
34
+
35
+ ## Current State
36
+
37
+ The current interactive mode lives in `src/cli/repl.mjs` and uses Node's `readline/promises`.
38
+
39
+ It already supports:
40
+
41
+ - Plain prompt turns through `runInteractiveTurn`
42
+ - Slash commands
43
+ - `/mode`
44
+ - `/reasoning`
45
+ - `/queue`
46
+ - `/new`
47
+ - `/continue`
48
+ - `/editor`
49
+ - `/edit`
50
+ - Thread archive and visibility commands
51
+ - Skill and plugin command aliases
52
+ - REPL history
53
+
54
+ The TUI should keep those behaviors and adapt presentation/input handling around them.
55
+
56
+ ## Proposed Architecture
57
+
58
+ Add a new TUI layer, likely `src/cli/tui.mjs`, that owns terminal rendering and keyboard interaction. It should call shared REPL/session operations instead of duplicating command logic.
59
+
60
+ Recommended structure:
61
+
62
+ - `src/cli/interactive-core.mjs`
63
+ - Shared state transitions and command execution currently embedded in `repl.mjs`
64
+ - Handles turns, slash commands, queue draining, mode/reasoning updates, and thread selection
65
+ - `src/cli/repl.mjs`
66
+ - Keeps readline behavior as a fallback and for simple terminal environments
67
+ - Delegates shared behavior to `interactive-core.mjs`
68
+ - `src/cli/tui.mjs`
69
+ - Full-screen TUI renderer
70
+ - Owns layout, focus, keybindings, palette, transcript rendering, status rail, and composer behavior
71
+ - `src/main.mjs`
72
+ - Routes bare TTY interactive sessions to the TUI by default
73
+ - Preserves existing execute and command routing
74
+
75
+ The TUI library should be small and terminal-focused. `blessed` is acceptable if it works cleanly with ESM and Node 20. If it creates friction, use a similarly small maintained package rather than hand-rolling terminal control.
76
+
77
+ ## Default Routing
78
+
79
+ Interactive routing should become:
80
+
81
+ - If `parsed.execute` is true: use `runExecute`
82
+ - If stdin is piped and stdout is not a TTY: use `runExecute`
83
+ - If a command is provided: use `runCommand`
84
+ - If stdin/stdout are TTYs: use `runTuiInteractive`
85
+ - If TUI initialization fails or `NO_COLOR`/minimal terminal constraints make full-screen mode unsuitable: fall back to `runInteractive`
86
+
87
+ Add an escape hatch:
88
+
89
+ - `COVEN_CODE_REPL=1 coven-code` starts the existing readline REPL
90
+
91
+ This keeps the new UI default while preserving a debugging path.
92
+
93
+ ## TUI Layout
94
+
95
+ The first implementation should have one primary screen with these areas:
96
+
97
+ ### Header
98
+
99
+ Shows:
100
+
101
+ - Product name and version
102
+ - Mode
103
+ - Reasoning effort
104
+ - Runtime label
105
+ - Current thread status
106
+
107
+ ### Tabs
108
+
109
+ Tabs are visual/focus regions, not separate command modes:
110
+
111
+ - `chat`: transcript and composer
112
+ - `tools`: available tools summary
113
+ - `threads`: active/latest thread controls
114
+ - `config`: current relevant settings
115
+ - `help`: keybindings and slash commands
116
+
117
+ ### Transcript
118
+
119
+ Shows user prompts, assistant responses, command output, and errors in a scrollable main pane.
120
+
121
+ Transcript entries should distinguish:
122
+
123
+ - User messages
124
+ - Assistant messages
125
+ - Slash command results
126
+ - Tool or command output
127
+ - Errors
128
+
129
+ ### Status Rail
130
+
131
+ Shows compact operational state:
132
+
133
+ - Thread id or "new thread"
134
+ - Mode
135
+ - Reasoning effort
136
+ - Queue count
137
+ - Tool count
138
+ - Config path or profile summary when useful
139
+
140
+ ### Composer
141
+
142
+ Supports:
143
+
144
+ - Single-line prompt entry
145
+ - Multi-line entry when users insert newline intentionally
146
+ - Slash commands
147
+ - Submitting queued prompts after the current turn
148
+ - Clear status while a turn is running
149
+
150
+ ## Keybindings
151
+
152
+ Minimum keybindings:
153
+
154
+ - `Enter`: submit composer
155
+ - `Shift-Enter` or `Alt-Enter`: insert newline if supported by the library/terminal
156
+ - `Tab`: cycle tabs or focus regions
157
+ - `Ctrl-P`: open command palette
158
+ - `Ctrl-N`: new thread
159
+ - `Ctrl-R`: cycle reasoning effort
160
+ - `Ctrl-M`: cycle mode
161
+ - `Ctrl-E`: open editor flow
162
+ - `Esc`: close palette or overlay
163
+ - `Ctrl-C`: graceful quit confirmation or immediate quit when idle
164
+
165
+ Slash commands remain available and should behave consistently with the old REPL.
166
+
167
+ ## Command Palette
168
+
169
+ The command palette should expose common actions without forcing users to memorize slash commands:
170
+
171
+ - New thread
172
+ - Continue latest thread
173
+ - Open editor
174
+ - Edit previous prompt
175
+ - Toggle/cycle mode
176
+ - Toggle/cycle reasoning effort
177
+ - List tools
178
+ - List skills
179
+ - List plugins
180
+ - Open help
181
+ - Archive thread and quit
182
+
183
+ Palette commands should call the same shared command handlers used by slash commands.
184
+
185
+ ## Error Handling
186
+
187
+ - Slash command errors should render in the transcript and keep the TUI alive.
188
+ - TUI initialization failure should fall back to readline with a concise warning.
189
+ - Terminal resize should redraw without losing transcript or composer contents.
190
+ - Command execution exceptions should preserve the current thread state when possible.
191
+ - Editor flow cancellation should return to the composer without submitting an empty prompt.
192
+
193
+ ## Accessibility and Terminal Constraints
194
+
195
+ - Avoid emoji and decorative glyphs in the UI.
196
+ - Use text labels and simple ASCII-safe separators by default.
197
+ - Do not rely on color alone; labels must carry meaning.
198
+ - Keep all text readable on narrow terminals by truncating low-priority status fields before prompt or transcript text.
199
+ - Respect `NO_COLOR` by using plain styling if the selected library supports it.
200
+
201
+ ## Testing Strategy
202
+
203
+ Use test-first implementation.
204
+
205
+ Required tests:
206
+
207
+ - Bare TTY routing chooses the TUI path.
208
+ - `COVEN_CODE_REPL=1` chooses the readline REPL path.
209
+ - Execute mode still prints `4` for `what is 2+2?`.
210
+ - `--stream-json` behavior is unchanged.
211
+ - Command subcommands still dispatch before interactive routing.
212
+ - Shared interactive command handling preserves `/mode`, `/reasoning`, `/queue`, `/new`, and `/continue` behavior.
213
+ - TUI command palette actions call the same underlying handlers as slash commands.
214
+
215
+ If the selected TUI library supports stable rendering in tests, add a lightweight layout smoke test. If not, test the TUI controller/state model instead of brittle terminal screenshots.
216
+
217
+ ## Documentation Updates
218
+
219
+ Update:
220
+
221
+ - `README.md` interactive usage section
222
+ - `docs/CLI.md`
223
+ - `docs/DEVELOPMENT.md` if new test or TUI development instructions are needed
224
+
225
+ Docs should mention:
226
+
227
+ - Bare `coven-code` opens the TUI
228
+ - `COVEN_CODE_REPL=1 coven-code` starts the classic readline REPL
229
+ - Noninteractive command behavior is unchanged
230
+
231
+ ## Implementation Plan Entry Point
232
+
233
+ The implementation plan should start by extracting shared interactive behavior from `src/cli/repl.mjs` into a testable module. After that, add TTY routing tests, introduce the TUI layer, and only then wire bare `coven-code` to the panel UI.
234
+
235
+ This avoids building the TUI around duplicated command logic and keeps the old REPL available as a fallback.
@@ -0,0 +1,63 @@
1
+ # Slash-First TUI Implementation Review
2
+
3
+ Date: 2026-05-26
4
+ Project: Coven Code
5
+ Status: Implemented and locally verified
6
+
7
+ ## Bottom Line
8
+
9
+ This is a strong v1, not the best possible solution in the absolute sense. It is the right conservative solution for this branch because it keeps the existing Node/neo-blessed CLI stack, reuses the shared interactive command engine, preserves Coven-only branding, and adds focused coverage around the new slash-first behavior.
10
+
11
+ The main things to reconsider are future maintainability and deeper UX polish, not whether the current patch satisfies the requested interaction model.
12
+
13
+ ## What Changed
14
+
15
+ - Added `src/cli/slash-commands.mjs` as the shared slash catalog source for built-ins, top-level shortcuts, plugin commands, and discovered skills.
16
+ - Extended `src/cli/interactive-core.mjs` so catalog-backed slash commands execute through the existing handler path.
17
+ - Made skill slash entries real: `/<skill>` shows skill details and `/<skill> <prompt>` submits a skill-guided prompt.
18
+ - Reworked `src/cli/tui.mjs` into a chat-first TTY experience with header, transcript, composer, compact status line, slash list, and command details overlay.
19
+ - Added slash menu state for open/close, filtering, selected index, details, completion, acceptance, cursor movement, and multiline composer behavior.
20
+ - Replaced placeholder TUI tab output with real summaries for tools, threads, config, and help.
21
+ - Updated skill discovery to support cwd-specific roots.
22
+ - Normalized early downstream pipe close handling with a SIGPIPE exit path.
23
+ - Adjusted local-agent prompt routing so global guidance containing review language does not override explicit codename/system-prompt tests.
24
+ - Updated README and CLI/development docs for the slash-first TUI behavior and manual smoke flow.
25
+ - Expanded tests for catalog construction, plugin visibility/availability, temp skill roots, TUI slash interaction, details rendering, narrow frames, tab summaries, and interactive-core slash behavior.
26
+
27
+ ## Why This Is A Good Solution
28
+
29
+ - It matches the requested slash-first interaction model without copying outside branding.
30
+ - It avoids a runtime stack rewrite and honors the plan's no-new-dependency assumption.
31
+ - It centralizes slash command metadata so TUI and REPL help do not drift.
32
+ - It keeps command execution behind the existing shared interactive handler instead of creating a parallel command runner.
33
+ - It makes plugin and skill entries discoverable while respecting hidden and disabled command behavior.
34
+ - It is covered by both deterministic tests and a real manual TTY smoke path.
35
+
36
+ ## Where To Reconsider
37
+
38
+ - `src/cli/tui.mjs` is now large and mixes model state, rendering, blessed wiring, summaries, and execution glue. If more TUI work lands, split it into model/controller/render/live-adapter modules.
39
+ - The dynamic slash catalog is rebuilt at launch and through the interactive-core safety path. That is fine now, but plugin-heavy setups may want explicit caching and invalidation.
40
+ - Skill execution currently injects skill guidance into the prompt. That is real and useful, but a future structured skill invocation contract could be more efficient and easier to reason about.
41
+ - Disabled plugin commands are visible and blocked, but the live UI could make disabled availability clearer with stronger styling and status text.
42
+ - Very small PTYs need more graceful fallback behavior. The manual smoke had to set rows and columns because expect defaults to a tiny pseudo-terminal.
43
+ - Long `/help` or detail output can push useful transcript context out of view. A future scroll/collapse affordance would improve dense command discovery.
44
+ - The current render tests are text-based and stable, but they do not replace periodic manual visual smoke tests for terminal layout drift.
45
+ - If Coven wants the richest possible terminal UX later, it may be worth evaluating Ink/react-blessed or a custom renderer. That should be a separate decision, because the current neo-blessed path is intentionally conservative.
46
+
47
+ ## Verification Completed
48
+
49
+ - `git diff --check`
50
+ - `node --check` on touched runtime modules
51
+ - `node ./bin/coven-code.mjs --help`
52
+ - `node ./bin/coven-code.mjs -x "what is 2+2?"`
53
+ - `npm test -- --test-name-pattern "tui|slash|interactive core"`
54
+ - `npm test`
55
+ - Manual TTY smoke for `/`, `/mo`, arrows, Enter, Esc, `/help`, and `/exit`
56
+
57
+ ## Recommended Follow-Ups
58
+
59
+ 1. Keep this implementation as the v1 unless real user testing exposes a workflow miss.
60
+ 2. Split TUI internals before adding another major interactive surface.
61
+ 3. Add catalog caching only if plugin or skill discovery becomes measurably slow.
62
+ 4. Design a structured skill invocation contract before expanding skill slash behavior further.
63
+ 5. Add a tiny-terminal fallback and a lightweight visual smoke checklist for future TUI changes.
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@opencoven/coven-code",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/OpenCoven/coven-code.git"
8
+ },
9
+ "exports": {
10
+ ".": "./src/sdk.mjs"
11
+ },
12
+ "bin": {
13
+ "coven-code": "./bin/coven-code.mjs",
14
+ "coven-code-sdk": "./bin/coven-code-sdk.mjs"
15
+ },
16
+ "files": [
17
+ "bin/",
18
+ "docs/",
19
+ "src/",
20
+ "README.md"
21
+ ],
22
+ "publishConfig": {
23
+ "access": "public"
24
+ },
25
+ "scripts": {
26
+ "test": "node --test 'test/*.test.mjs'",
27
+ "start": "node ./bin/coven-code.mjs",
28
+ "coven-code": "node ./bin/coven-code.mjs"
29
+ },
30
+ "engines": {
31
+ "node": ">=20"
32
+ },
33
+ "dependencies": {
34
+ "neo-blessed": "0.2.0"
35
+ }
36
+ }
@@ -0,0 +1,136 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { existsSync } from 'node:fs';
3
+ import path from 'node:path';
4
+
5
+ export const LANE_HARNESSES = ['smart', 'deep', 'rush', 'large'];
6
+
7
+ export function defaultLaneState(options = {}) {
8
+ return {
9
+ worktree: options.worktree ?? process.cwd(),
10
+ branch: options.branch ?? 'unknown',
11
+ baseBranch: options.baseBranch ?? 'main',
12
+ harness: normalizeLaneHarness(options.harness),
13
+ status: options.status ?? 'unknown',
14
+ changedFiles: options.changedFiles ?? [],
15
+ diffSummary: options.diffSummary ?? '',
16
+ verification: {
17
+ command: options.verification?.command ?? verificationCommandFor(options.worktree ?? process.cwd()),
18
+ status: options.verification?.status ?? 'not run',
19
+ },
20
+ terminalLines: options.terminalLines ?? [],
21
+ pullRequest: options.pullRequest ?? 'not opened',
22
+ merge: options.merge ?? 'not merged',
23
+ cleanup: options.cleanup ?? 'pending',
24
+ };
25
+ }
26
+
27
+ export async function inspectLane(options = {}) {
28
+ const cwd = options.cwd ?? process.cwd();
29
+ const runner = options.gitRunner ?? runGit;
30
+ const root = gitText(runner, cwd, ['rev-parse', '--show-toplevel']) || cwd;
31
+ const branch = gitText(runner, root, ['branch', '--show-current'])
32
+ || detachedBranchLabel(runner, root)
33
+ || 'unknown';
34
+ const baseBranch = gitText(runner, root, ['rev-parse', '--abbrev-ref', '--symbolic-full-name', '@{upstream}'])
35
+ || options.baseBranch
36
+ || 'main';
37
+ const statusResult = runner(root, ['status', '--short']);
38
+ const statusLines = splitLines(statusResult.stdout);
39
+ const changedFiles = statusLines.map(statusFileName).filter(Boolean);
40
+ const diffSummary = gitText(runner, root, ['diff', '--stat', '--', '.']);
41
+ const unavailable = !gitText(runner, cwd, ['rev-parse', '--git-dir']);
42
+
43
+ return defaultLaneState({
44
+ ...options,
45
+ worktree: root,
46
+ branch,
47
+ baseBranch,
48
+ status: unavailable ? 'unavailable' : changedFiles.length > 0 ? 'dirty' : 'ready',
49
+ changedFiles,
50
+ diffSummary,
51
+ verification: {
52
+ command: verificationCommandFor(root),
53
+ status: options.verification?.status ?? 'not run',
54
+ },
55
+ terminalLines: [
56
+ '$ git status --short',
57
+ ...(statusLines.length > 0 ? statusLines : ['clean']),
58
+ ],
59
+ });
60
+ }
61
+
62
+ export async function runLaneVerification(lane, options = {}) {
63
+ const cwd = lane.worktree ?? process.cwd();
64
+ const command = lane.verification?.command ?? verificationCommandFor(cwd);
65
+ const runner = options.runner ?? runShell;
66
+ const result = runner(command, cwd);
67
+ return {
68
+ ...lane,
69
+ verification: {
70
+ command,
71
+ status: result.status === 0 ? 'passed' : 'failed',
72
+ },
73
+ terminalLines: [
74
+ ...(lane.terminalLines ?? []).slice(-20),
75
+ `$ ${command}`,
76
+ ...splitLines(result.stdout),
77
+ ...splitLines(result.stderr),
78
+ `exit: ${result.status}`,
79
+ ].slice(-40),
80
+ };
81
+ }
82
+
83
+ export function normalizeLaneHarness(value) {
84
+ return LANE_HARNESSES.includes(value) ? value : 'smart';
85
+ }
86
+
87
+ export function nextLaneHarness(current) {
88
+ const index = LANE_HARNESSES.indexOf(normalizeLaneHarness(current));
89
+ return LANE_HARNESSES[(index + 1) % LANE_HARNESSES.length];
90
+ }
91
+
92
+ function verificationCommandFor(cwd) {
93
+ if (existsSync(path.join(cwd, 'package.json'))) return 'npm test';
94
+ if (existsSync(path.join(cwd, 'Cargo.toml'))) return 'cargo test';
95
+ return 'git diff --check';
96
+ }
97
+
98
+ function runGit(cwd, args) {
99
+ const result = spawnSync('git', args, { cwd, encoding: 'utf8' });
100
+ return {
101
+ status: result.status ?? 1,
102
+ stdout: result.stdout ?? '',
103
+ stderr: result.stderr ?? '',
104
+ };
105
+ }
106
+
107
+ function runShell(command, cwd) {
108
+ const result = spawnSync(command, { cwd, encoding: 'utf8', shell: true });
109
+ return {
110
+ status: result.status ?? 1,
111
+ stdout: result.stdout ?? '',
112
+ stderr: result.stderr ?? '',
113
+ };
114
+ }
115
+
116
+ function gitText(runner, cwd, args) {
117
+ const result = runner(cwd, args);
118
+ if (result.status !== 0) return '';
119
+ return result.stdout.trim();
120
+ }
121
+
122
+ function detachedBranchLabel(runner, cwd) {
123
+ const sha = gitText(runner, cwd, ['rev-parse', '--short', 'HEAD']);
124
+ return sha ? `detached:${sha}` : '';
125
+ }
126
+
127
+ function statusFileName(line) {
128
+ const file = line.slice(3).trim();
129
+ if (!file) return '';
130
+ const rename = file.split(' -> ');
131
+ return rename.at(-1) ?? file;
132
+ }
133
+
134
+ function splitLines(text = '') {
135
+ return String(text).split(/\r?\n/).filter(Boolean);
136
+ }
@@ -0,0 +1,95 @@
1
+ import { existsSync, readdirSync } from 'node:fs';
2
+ import path from 'node:path';
3
+
4
+ export function localAgentResponse(prompt, stdin) {
5
+ const text = prompt.trim();
6
+ const lower = text.toLowerCase();
7
+
8
+ if (lower.includes('now add') && lower.includes('to that')) {
9
+ const addend = Number([...lower.matchAll(/now add\s+(\d+)\s+to that/g)].at(-1)?.[1] ?? 0);
10
+ const prior = Number([...text.matchAll(/^assistant:\s*(\d+)\s*$/gim)].at(-1)?.[1] ?? 0);
11
+ if (addend && prior) return String(prior + addend);
12
+ }
13
+ if (/\b2\s*\+\s*2\b/.test(lower)) return '4';
14
+ if (lower.includes('markdown') && lower.includes('filename')) {
15
+ return readdirSync(process.cwd())
16
+ .filter((entry) => entry.toLowerCase().endsWith('.md'))
17
+ .sort((a, b) => a.localeCompare(b))
18
+ .join('\n');
19
+ }
20
+ if (lower.includes('colorscheme')) {
21
+ const match = text.match(/^\s*colorscheme\s+([^\s#]+)/mi);
22
+ return match ? `The colorscheme used is ${match[1]}.` : 'No colorscheme declaration found.';
23
+ }
24
+ if (lower.includes('package manager')) {
25
+ return detectPackageManager();
26
+ }
27
+ if (lower.includes('what did the shell command output')) {
28
+ return extractShellOutput(text) || 'No shell output is available from the captured context.';
29
+ }
30
+ if (lower.includes('image') && lower.includes('[image:')) {
31
+ const match = text.match(/\[image:([^\]\r\n]+)\]\nmedia_type:\s*([^\r\n]+)\nbytes:\s*(\d+)/);
32
+ if (match) return `${path.basename(match[1])} ${match[2]} ${match[3]} bytes`;
33
+ return 'No image was found.';
34
+ }
35
+ if (lower.includes('codename') && lower.includes('[thread:')) {
36
+ const match = text.match(/codename is ([a-z0-9_-]+)/i);
37
+ return match ? match[1] : 'No codename was found in the referenced thread.';
38
+ }
39
+ if (lower.includes('codename') && lower.includes('[file:')) {
40
+ const matches = [...text.matchAll(/codename:\s*([a-z0-9_-]+)/gi)].map((m) => m[1]);
41
+ if (lower.includes('codenames') && matches.length > 0) return matches.join('\n');
42
+ return matches[0] || 'No codename was found in the referenced file.';
43
+ }
44
+ if (lower.includes('codename')) {
45
+ const match = text.match(/codename:\s*([a-z0-9_-]+)/i);
46
+ if (match) return match[1];
47
+ }
48
+ if (lower.includes('codename')) {
49
+ return 'No codename was found.';
50
+ }
51
+ if (lower.includes('review')) {
52
+ return 'No automated review findings in the local deterministic recreation.';
53
+ }
54
+ if (stdin.trim()) {
55
+ return `Received ${stdin.trim().split(/\s+/).length} input words and prompt: ${parsedSummary(text)}`;
56
+ }
57
+ return `Coven Code local runtime received: ${parsedSummary(text)}`;
58
+ }
59
+
60
+ export function detectPackageManager() {
61
+ const checks = [
62
+ ['pnpm-lock.yaml', 'pnpm'],
63
+ ['bun.lockb', 'bun'],
64
+ ['bun.lock', 'bun'],
65
+ ['yarn.lock', 'yarn'],
66
+ ['package-lock.json', 'npm'],
67
+ ['Cargo.lock', 'cargo'],
68
+ ];
69
+ for (const [file, manager] of checks) {
70
+ if (existsSync(path.join(process.cwd(), file))) return manager;
71
+ }
72
+ return 'unknown';
73
+ }
74
+
75
+ export function extractShellOutput(text) {
76
+ const match = text.match(/\[shell\]\n\$ .+\n([\s\S]*?)(?:\n[^\n]*\?|$)/);
77
+ return match?.[1]?.trim() ?? '';
78
+ }
79
+
80
+ export function parsedSummary(text) {
81
+ return text.length > 160 ? `${text.slice(0, 157)}...` : text || '(empty message)';
82
+ }
83
+
84
+ export function estimateUsage(input, output) {
85
+ return {
86
+ input_tokens: estimateTokenCount(input),
87
+ output_tokens: estimateTokenCount(output),
88
+ max_tokens: 968000,
89
+ service_tier: 'local-recreation',
90
+ };
91
+ }
92
+
93
+ export function estimateTokenCount(text) {
94
+ return Math.max(1, Math.ceil(String(text).length / 4));
95
+ }
@@ -0,0 +1,66 @@
1
+ import { printHelp } from './help.mjs';
2
+ import { UsageError } from './parse.mjs';
3
+ import { runLogin } from '../commands/login.mjs';
4
+ import { runUpdate } from '../commands/update.mjs';
5
+ import { runReview } from '../commands/review.mjs';
6
+ import { runUsage } from '../commands/usage.mjs';
7
+ import { runTools } from '../commands/tools.mjs';
8
+ import { runPermissions } from '../commands/permissions.mjs';
9
+ import { runConfig } from '../commands/config.mjs';
10
+ import { runAgents } from '../commands/agents.mjs';
11
+ import { runPlugins } from '../commands/plugins.mjs';
12
+ import { runMcp } from '../commands/mcp.mjs';
13
+ import { runSkill } from '../commands/skill.mjs';
14
+ import { runThreads } from '../commands/threads.mjs';
15
+ import { runIde } from '../commands/ide.mjs';
16
+
17
+ export async function runCommand(command, args, parsed, stdin) {
18
+ switch (command) {
19
+ case 'help':
20
+ printHelp();
21
+ break;
22
+ case 'login':
23
+ await runLogin(args);
24
+ break;
25
+ case 'update':
26
+ runUpdate(parsed);
27
+ break;
28
+ case 'review':
29
+ runReview();
30
+ break;
31
+ case 'usage':
32
+ runUsage(parsed);
33
+ break;
34
+ case 'tools':
35
+ await runTools(args, stdin, parsed);
36
+ break;
37
+ case 'permissions':
38
+ await runPermissions(args, stdin, parsed);
39
+ break;
40
+ case 'config':
41
+ await runConfig(args, parsed);
42
+ break;
43
+ case 'agents':
44
+ case 'agents-md':
45
+ await runAgents(args);
46
+ break;
47
+ case 'plugins':
48
+ await runPlugins(args);
49
+ break;
50
+ case 'mcp':
51
+ await runMcp(args, parsed);
52
+ break;
53
+ case 'skill':
54
+ await runSkill(args, parsed);
55
+ break;
56
+ case 'threads':
57
+ await runThreads(args, parsed, stdin);
58
+ break;
59
+ case 'ide':
60
+ runIde(args);
61
+ break;
62
+ default:
63
+ if (parsed.help) printHelp();
64
+ else throw new UsageError(`Unknown command: ${command}`);
65
+ }
66
+ }