craig-cli 0.15.0 → 0.15.2
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 +38 -214
- package/dist/cli.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,249 +10,73 @@
|
|
|
10
10
|
░ ░░ ░ ░ ▒ ▒ ░░ ░ ░
|
|
11
11
|
░ ░ ░ ░ ░ ░ ░
|
|
12
12
|
|
|
13
|
-
crAIg is that you?
|
|
14
|
-
|
|
15
13
|
</pre>
|
|
16
14
|
|
|
17
|
-
**An agnostic
|
|
15
|
+
**An agnostic orchestrator TUI, for the people.**
|
|
18
16
|
|
|
19
17
|
[](https://www.npmjs.com/package/craig-cli)
|
|
20
|
-
[](LICENSE)
|
|
18
|
+
[](https://github.com/hallsamuel90/craig/blob/main/LICENSE)
|
|
21
19
|
[](https://nodejs.org)
|
|
22
20
|
|
|
23
|
-
[
|
|
21
|
+
[Website](https://craig-cli.com) · [Documentation](https://craig-cli.com/docs/) · [CLI reference](https://craig-cli.com/docs/cli/)
|
|
24
22
|
|
|
25
23
|
</div>
|
|
26
24
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
it's like a game, except the PRs are real.
|
|
32
|
-
|
|
33
|
-
## Install
|
|
34
|
-
|
|
35
|
-
```bash
|
|
36
|
-
npm install -g craig-cli
|
|
37
|
-
```
|
|
25
|
+
<a href="https://craig-cli.com">
|
|
26
|
+
<img src="https://craig-cli.com/craig-agent.gif" alt="Craig running parallel coding agents in its terminal UI">
|
|
27
|
+
</a>
|
|
38
28
|
|
|
39
|
-
|
|
29
|
+
Craig gives every task its own Git branch, isolated worktree, and live PTY, then keeps the agents, changes, pull requests, and checks together in one keyboard-driven workspace.
|
|
40
30
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
```bash
|
|
44
|
-
cd path/to/your/workspace
|
|
45
|
-
craig
|
|
46
|
-
```
|
|
31
|
+
Bring the agent CLIs you already use—Claude Code, Codex, or Cursor. Craig is not another coding agent; it is the heads-up display and controls around them.
|
|
47
32
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
Craig writes all local state under `.craig/` — worktrees, logs, task records, PR metadata. Don't commit it.
|
|
51
|
-
|
|
52
|
-
## Workflow
|
|
53
|
-
|
|
54
|
-
1. Press `n` to create a task — type a short task description, pick a runner, press `Enter`.
|
|
55
|
-
2. Craig creates the task record, branch, worktree, and agent tab automatically.
|
|
56
|
-
3. Press `Enter` on the task to attach its live PTY. `Ctrl+]` to return to control mode.
|
|
57
|
-
4. Give the runner its instructions in the agent PTY. The task description is Craig metadata; it is not pasted into the runner automatically.
|
|
58
|
-
5. Use the **Files**, **Changes**, and read-only **Review** inspector panels to check the diff, PR state, checks, and comments.
|
|
59
|
-
|
|
60
|
-
## Polyrepo support
|
|
61
|
-
|
|
62
|
-
Point Craig at a directory containing multiple repos and it treats them as a single workspace. Create a task once — Craig provisions a worktree for each repo and runs the agent across all of them in one pass.
|
|
63
|
-
|
|
64
|
-
```bash
|
|
65
|
-
cd path/to/your/projects # contains repo-a/, repo-b/, repo-c/
|
|
66
|
-
craig
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
## TUI Keys
|
|
70
|
-
|
|
71
|
-
```
|
|
72
|
-
Global
|
|
73
|
-
? help
|
|
74
|
-
Esc pause
|
|
75
|
-
Tab, ] next panel
|
|
76
|
-
Shift+Tab, [ previous panel
|
|
77
|
-
Up/Down, j/k move selection
|
|
78
|
-
PgUp/PgDn, Wheel scroll focused inspection or terminal view
|
|
79
|
-
z zoom center panel
|
|
80
|
-
n new task
|
|
81
|
-
q quit
|
|
33
|
+
_It's like a game, except the PRs are real._
|
|
82
34
|
|
|
83
|
-
|
|
84
|
-
Left/Right, h/l switch tabs or inspector mode
|
|
35
|
+
## Why Craig?
|
|
85
36
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
X on workspace remove selected workspace
|
|
37
|
+
- **Run agents in parallel.** Each task works in an isolated Git worktree, so one agent does not overwrite another.
|
|
38
|
+
- **Keep the native agent experience.** Every runner opens in a real PTY instead of a lowest-common-denominator chat wrapper.
|
|
39
|
+
- **Stay in flow.** Launch agents, move between tasks, inspect changes, and watch PR checks without leaving the keyboard.
|
|
40
|
+
- **Work across repositories.** Treat one repo or a directory of repos as a workspace while preserving real repository boundaries.
|
|
41
|
+
- **Let agents operate the workspace.** The same task model backs the TUI and CLI, so agents and scripts can inspect context and perform Craig actions directly.
|
|
42
|
+
- **Leave and come back.** A local daemon keeps agent sessions and PR synchronization alive when the foreground TUI closes.
|
|
93
43
|
|
|
94
|
-
|
|
95
|
-
Enter attach PTY
|
|
96
|
-
+ create preferred tab kind
|
|
97
|
-
a new agent tab
|
|
98
|
-
t new terminal tab
|
|
99
|
-
r cycle runner for next agent tab
|
|
100
|
-
x close tab
|
|
101
|
-
|
|
102
|
-
Review
|
|
103
|
-
Enter refresh status or close task, depending on selected action
|
|
104
|
-
R refresh read-only PR/check state
|
|
105
|
-
o open tracked PR URL
|
|
106
|
-
X close task record
|
|
107
|
-
|
|
108
|
-
Task prompt
|
|
109
|
-
Ctrl+R cycle runner
|
|
110
|
-
Enter create task
|
|
111
|
-
Esc cancel
|
|
112
|
-
|
|
113
|
-
Workspace browser
|
|
114
|
-
Up/Down, j/k move selection
|
|
115
|
-
Right, l open directory
|
|
116
|
-
Left, h parent directory
|
|
117
|
-
Enter add selected path
|
|
118
|
-
Esc cancel
|
|
119
|
-
|
|
120
|
-
Terminal mode
|
|
121
|
-
Ctrl+] return to control mode
|
|
122
|
-
Wheel, PgUp/PgDn scroll terminal
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
## Command mode
|
|
44
|
+
## Install
|
|
126
45
|
|
|
127
46
|
```bash
|
|
128
|
-
|
|
129
|
-
craig repo list
|
|
130
|
-
craig repo remove <repo-id>
|
|
131
|
-
|
|
132
|
-
craig workspace add <path>
|
|
133
|
-
craig workspace list
|
|
134
|
-
craig workspace list --archived
|
|
135
|
-
craig workspace archive <workspace-id>
|
|
136
|
-
craig workspace restore <workspace-id>
|
|
137
|
-
craig workspace remove <workspace-id>
|
|
138
|
-
|
|
139
|
-
craig task new --repo <repo-id> [--runner codex|cursor|claude] "<task>"
|
|
140
|
-
craig task new --workspace <workspace-id> [--runner codex|cursor|claude] "<task>"
|
|
141
|
-
craig task list
|
|
142
|
-
craig task list --repo <repo-id>
|
|
143
|
-
craig task show <task-id>
|
|
144
|
-
craig task open <task-id>
|
|
145
|
-
craig task logs <task-id>
|
|
146
|
-
craig task diff <task-id>
|
|
147
|
-
craig task check <task-id>
|
|
148
|
-
craig task commit <task-id>
|
|
149
|
-
craig task create-child --parent <task-id> [--repo <repo-id> | --workspace <workspace-id>] [--runner codex|cursor|claude] \
|
|
150
|
-
[--idempotency-key <key>] "<task>" --json
|
|
151
|
-
craig task children [<task-id>] --json
|
|
152
|
-
craig task cancel-tree [<task-id>] --json
|
|
153
|
-
|
|
154
|
-
craig agent list [--task <task-id>] --json
|
|
155
|
-
craig agent status [--task <task-id>] [--tab <tab-id>] --json
|
|
156
|
-
craig agent send --task <task-id> [--tab <tab-id>] (--prompt <text> | --prompt-file <path> | --stdin) \
|
|
157
|
-
[--delivery when-ready|immediate] [--timeout <duration>] [--idempotency-key <key>] --json
|
|
158
|
-
craig task wait [<task-id>] --state <states> [--tab <tab-id>] [--timeout <duration>] --json
|
|
159
|
-
|
|
160
|
-
craig events list [--task <task-id>] [--type <glob>] [--after <cursor>] --json
|
|
161
|
-
craig events watch [--task <task-id>] [--type <glob>] [--after <cursor>] [--format jsonl]
|
|
162
|
-
|
|
163
|
-
craig fury validate <file> --json
|
|
164
|
-
craig fury plan <file> [--root-task <task-id>] [--input key=value] --json
|
|
165
|
-
craig fury approve <plan-id> --json
|
|
166
|
-
craig fury run <plan-id> --json
|
|
167
|
-
craig fury status <run-id> --json
|
|
168
|
-
craig fury watch <run-id> [--after <cursor>] [--format jsonl]
|
|
169
|
-
craig fury cancel <run-id> --json
|
|
170
|
-
craig fury resume <run-id> --json
|
|
171
|
-
craig fury step complete --run <run-id> --step <step-id> [--output <json> | --output-file <path> | --stdin] --json
|
|
172
|
-
craig fury step fail --run <run-id> --step <step-id> --reason <text> --json
|
|
173
|
-
craig fury reviews list [--run <run-id>] [--state <state>] --json
|
|
174
|
-
craig fury review show|approve|reject|request-changes|resubmit <review-id> --json
|
|
175
|
-
|
|
176
|
-
craig command show <command-id> --json
|
|
177
|
-
craig command list [--task <task-id>] --json
|
|
178
|
-
craig command cancel <command-id> --json
|
|
179
|
-
craig command wait <command-id> [--state <states>] [--timeout <duration>] --json
|
|
180
|
-
|
|
181
|
-
craig link add <task-id> <repo-id>
|
|
182
|
-
craig link list <task-id>
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
## Config
|
|
186
|
-
|
|
187
|
-
Optional workspace-local config at `.craig/config.json`:
|
|
188
|
-
|
|
189
|
-
```json
|
|
190
|
-
{
|
|
191
|
-
"runners": {
|
|
192
|
-
"codex": { "enabled": true },
|
|
193
|
-
"cursor": { "enabled": true },
|
|
194
|
-
"claude": { "enabled": false }
|
|
195
|
-
},
|
|
196
|
-
"checks": {
|
|
197
|
-
"commands": ["pnpm test", "pnpm typecheck", "pnpm lint"]
|
|
198
|
-
},
|
|
199
|
-
"github": {
|
|
200
|
-
"mergeMethod": "squash",
|
|
201
|
-
"watchIntervalSeconds": 5
|
|
202
|
-
},
|
|
203
|
-
"previews": {
|
|
204
|
-
"agentOrchestration": false
|
|
205
|
-
}
|
|
206
|
-
}
|
|
47
|
+
npm install -g craig-cli
|
|
207
48
|
```
|
|
208
49
|
|
|
209
|
-
|
|
50
|
+
Craig requires Node.js 22+ and `git`. Install and authenticate at least one supported runner: Claude Code, Codex, or Cursor. Pull request discovery and review status also require an authenticated `gh` CLI.
|
|
210
51
|
|
|
211
|
-
|
|
52
|
+
## Quick start
|
|
212
53
|
|
|
213
|
-
|
|
54
|
+
Run `craig` in a full-screen terminal, then work through the TUI:
|
|
214
55
|
|
|
215
|
-
|
|
56
|
+
1. Select **+ New Workspace** and choose a Git repository. You can also choose a parent directory to work across its direct-child repositories.
|
|
57
|
+
2. Press `n`, name the task, and use `Ctrl+R` to choose Claude Code, Codex, or Cursor. Press `Enter` to create it.
|
|
58
|
+
3. Craig creates an isolated branch and worktree, starts the runner there, and takes you directly into its live PTY. Tell the agent what you want it to do.
|
|
59
|
+
4. Press `Ctrl+]` to return to Craig's control mode. Move between panels with `Tab`, navigate with the arrow keys or `hjkl`, and press `Enter` on a task to reattach to its agent.
|
|
60
|
+
5. Press `n` again to start more tasks while the first agent keeps working. Each task remains isolated in its own worktree.
|
|
61
|
+
6. Use **Files** to inspect the worktree and **Review** to follow diffs, pull requests, checks, and review state. Ask the agent to commit, push, create the PR, or merge from its PTY; Craig keeps the status in sync.
|
|
216
62
|
|
|
217
|
-
|
|
63
|
+
See the [TUI reference](https://craig-cli.com/docs/tui/) for every panel and keyboard shortcut.
|
|
218
64
|
|
|
219
|
-
|
|
65
|
+
## Local by default
|
|
220
66
|
|
|
221
|
-
|
|
67
|
+
Craig stores workspace state, task records, worktrees, and runtime metadata under `.craig/` at the workspace root. Do not commit this directory; ignore `.craig/` when the workspace root is itself a Git repository. Craig does not require an account or hosted control plane. Your agent CLIs still use their own authentication and network services.
|
|
222
68
|
|
|
223
|
-
|
|
224
|
-
version: 1
|
|
225
|
-
name: inspect-and-review
|
|
226
|
-
limits: { max_concurrency: 2, max_tasks: 4, timeout: 2h }
|
|
227
|
-
inputs:
|
|
228
|
-
task_id: { type: string, required: true }
|
|
229
|
-
steps:
|
|
230
|
-
inspect:
|
|
231
|
-
task: "${{ inputs.task_id }}"
|
|
232
|
-
prompt: Inspect the task and submit structured findings.
|
|
233
|
-
output:
|
|
234
|
-
schema: { type: object, required: [findings] }
|
|
235
|
-
review:
|
|
236
|
-
needs: [inspect]
|
|
237
|
-
human_review:
|
|
238
|
-
title: Review findings
|
|
239
|
-
summary: Review the submitted findings before execution continues.
|
|
240
|
-
feedback_target: { task: "${{ steps.inspect.task_id }}" }
|
|
241
|
-
timeout: 1h
|
|
242
|
-
```
|
|
69
|
+
## Go deeper
|
|
243
70
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
reacts to task and agent completion events for faster refreshes.
|
|
71
|
+
- [TUI reference](https://craig-cli.com/docs/tui/) — layout, controls, and task navigation
|
|
72
|
+
- [Runners](https://craig-cli.com/docs/runners/) — Claude Code, Codex, and Cursor setup
|
|
73
|
+
- [CLI reference](https://craig-cli.com/docs/cli/) — commands for humans, scripts, and agents
|
|
74
|
+
- [Configuration](https://craig-cli.com/docs/config/) — runners, checks, GitHub polling, and previews
|
|
75
|
+
- [Agent orchestration](https://craig-cli.com/docs/orchestration/) — delegation, durable prompts, events, and Fury workflows
|
|
250
76
|
|
|
251
77
|
## Contributing
|
|
252
78
|
|
|
253
|
-
Craig is open source under MIT, but not a community-maintained project. Bug reports and focused questions are welcome.
|
|
254
|
-
|
|
255
|
-
See [CONTRIBUTING.md](CONTRIBUTING.md) and [SECURITY.md](SECURITY.md).
|
|
79
|
+
Craig is open source under the [MIT License](https://github.com/hallsamuel90/craig/blob/main/LICENSE), but it is not a community-maintained project. Bug reports and focused questions are welcome. Please read the [contribution guide](https://github.com/hallsamuel90/craig/blob/main/CONTRIBUTING.md) before proposing a change and report security issues through the [security policy](https://github.com/hallsamuel90/craig/blob/main/SECURITY.md).
|
|
256
80
|
|
|
257
81
|
## Development
|
|
258
82
|
|
|
@@ -267,4 +91,4 @@ pnpm package:audit
|
|
|
267
91
|
pnpm package:smoke
|
|
268
92
|
```
|
|
269
93
|
|
|
270
|
-
The npm package is published as `craig-cli` from `packages/cli`.
|
|
94
|
+
The npm package is published as [`craig-cli`](https://www.npmjs.com/package/craig-cli) from `packages/cli`.
|
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
var Yy=Object.defineProperty;var Ds=(e,t)=>{for(var n in t)Yy(e,n,{get:t[n],enumerable:!0})};import{realpath as iE}from"node:fs/promises";import ye from"node:path";function ve(e){let t=ye.join(e,".craig");return{workspaceRoot:e,repoRoot:e,craigDir:t,indexFile:ye.join(t,"index.json"),reposDir:ye.join(t,"repos"),workspacesDir:ye.join(t,"workspaces"),runtimeDir:ye.join(t,"runtime"),uiStateFile:ye.join(t,"runtime","ui-state.json"),tasksDir:ye.join(t,"tasks"),jobsDir:ye.join(t,"jobs"),commandsDir:ye.join(t,"commands"),eventsDir:ye.join(t,"events"),orchestrationDir:ye.join(t,"orchestration"),logsDir:ye.join(t,"logs"),logFile:ye.join(t,"logs","craig.jsonl"),artifactsDir:ye.join(t,"artifacts"),worktreesDir:ye.join(t,"worktrees"),configFile:ye.join(t,"config.json")}}import Uc from"node:path";import{readFile as YP}from"node:fs/promises";import{readFile as nw}from"node:fs/promises";import{randomUUID as zy}from"node:crypto";import{chmod as Jy,open as Xy,readFile as Qy,rename as Zy,rm as ew}from"node:fs/promises";import Yo from"node:path";async function q(e,t,n={}){let r=Yo.dirname(e),s=Yo.join(r,`.${Yo.basename(e)}.${process.pid}.${Date.now()}.${zy()}.tmp`),i=await Xy(s,"w",n.mode);try{let o=`${JSON.stringify(t,null,2)}
|
|
3
3
|
`;await i.writeFile(o,"utf8"),await i.sync(),await i.close(),await Zy(s,e),n.mode!==void 0&&await Jy(e,n.mode)}catch(o){throw await i.close().catch(()=>{}),await ew(s,{force:!0}).catch(()=>{}),o}}async function Ns(e){try{let t=await Qy(e,"utf8");return JSON.parse(t)}catch(t){if(tw(t))return null;throw t}}function tw(e){return typeof e=="object"&&e!==null&&"code"in e&&typeof e.code=="string"&&e.code==="ENOENT"}async function Mn(e){try{let t=await nw(e.uiStateFile,"utf8"),n=JSON.parse(t);return rw(n,e.uiStateFile)}catch(t){if(iw(t))return null;throw t}}async function jt(e,t){await q(e.uiStateFile,{...t,updatedAt:new Date().toISOString()})}function xs(){return{version:1,selectedRepoId:null,selectedWorkspaceId:null,selectedTaskId:null,selectedPtyTabId:null,inputMode:"control",focusedRegion:"tasks",activeTab:"agent",inspectorSection:"task",inspectionMode:"files",openInspectionKind:null,selectedFileTreePath:null,selectedFilePath:null,selectedDiffPath:null,collapsedFileTreePaths:[],selectedActionId:"commit",updatedAt:new Date().toISOString()}}function rw(e,t){if(typeof e!="object"||e===null||e.version!==1||!(e.selectedRepoId===null||typeof e.selectedRepoId=="string")||!(e.selectedWorkspaceId===null||typeof e.selectedWorkspaceId=="string")||!(e.selectedTaskId===null||typeof e.selectedTaskId=="string")||!(e.selectedPtyTabId===void 0||e.selectedPtyTabId===null||typeof e.selectedPtyTabId=="string")||!Fn(e,"inputMode")||!Fn(e,"focusedRegion")||!Fn(e,"activeTab")||!Fn(e,"inspectorSection")||!Fn(e,"inspectionMode")||!_s(e,"openInspectionKind")||!_s(e,"selectedFileTreePath")||!_s(e,"selectedFilePath")||!_s(e,"selectedDiffPath")||!sw(e,"collapsedFileTreePaths")||!Fn(e,"selectedActionId")||typeof e.updatedAt!="string")throw new Error(`Craig UI state at ${t} is invalid. Remove or repair the file before rerunning Craig.`);return e}function Fn(e,t){return typeof e=="object"&&e!==null&&(!(t in e)||typeof e[t]=="string")}function _s(e,t){return typeof e=="object"&&e!==null&&(!(t in e)||e[t]===null||typeof e[t]=="string")}function sw(e,t){return typeof e=="object"&&e!==null&&(!(t in e)||Array.isArray(e[t])&&e[t].every(n=>typeof n=="string"))}function iw(e){return typeof e=="object"&&e!==null&&"code"in e&&typeof e.code=="string"&&e.code==="ENOENT"}var zo={};Ds(zo,{RUNNER_IDS:()=>z,assertEnabled:()=>Zu,buildCommand:()=>nd,getConfiguredProfile:()=>Ls,getDefault:()=>Qu,getDisplayName:()=>td,getEnabled:()=>Fs,getProfile:()=>jn,isRunnerType:()=>Ms,parse:()=>ed,setRunnerEnabled:()=>rd,setRunnerPath:()=>sd});var z=["codex","cursor","claude"];var Xu={codex:{id:"codex",displayName:"Codex",executable:"codex",defaultAgentTitle:"Codex"},cursor:{id:"cursor",displayName:"Cursor",executable:"cursor-agent",defaultAgentTitle:"Cursor"},claude:{id:"claude",displayName:"Claude",executable:"claude",defaultAgentTitle:"Claude"}};var jn=e=>Xu[e];var Ls=(e,t={})=>{let n=jn(e),r=t.runners?.[e]?.path?.trim();return{...n,executable:r&&r.length>0?r:n.executable}};var Fs=(e={})=>z.filter(t=>e.runners?.[t]?.enabled!==!1);var Qu=(e={})=>{let t=Fs(e);if(t.length===0)throw new Error("No runners are enabled. Enable at least one runner in .craig/config.json.");return t.includes("codex")?"codex":t[0]};var Zu=(e,t={})=>{if(t.runners?.[e]?.enabled===!1)throw new Error(`Runner "${e}" is disabled in .craig/config.json.`)};var Ms=e=>z.includes(e);var ed=e=>{if(!e||e.length===0)return"codex";if(!Ms(e))throw new Error(`Unsupported runner "${e}". Expected one of: ${z.join(", ")}.`);return e};var td=e=>jn(e).displayName;var nd=(e,t,n={})=>{let r=[Ls(e,n).executable];return t&&t.length>0&&r.push(t),r};var rd=(e,t,n)=>({...e,runners:{...e.runners??{},[t]:{...e.runners?.[t]??{},enabled:n}}});var sd=(e,t,n)=>{let r=e.runners?.[t]??{},{path:s,...i}=r;return{...e,runners:{...e.runners??{},[t]:n!=null?{...r,path:n}:i}}};var Jo={};Ds(Jo,{PREVIEW_FEATURE_IDS:()=>ze,isEnabled:()=>id,setEnabled:()=>od});var ze=["agentOrchestration"];function id(e,t){return e.previews?.[t]===!0}function od(e,t,n){return{...e,previews:{...e.previews,[t]:n}}}import{mkdir as ow,readFile as aw,writeFile as lw}from"node:fs/promises";import cw from"node:path";var uw=e=>typeof e=="object"&&e!==null&&"code"in e&&typeof e.code=="string"&&e.code==="ENOENT",ad=async e=>{try{return await aw(e,"utf8")}catch(t){if(uw(t))return null;throw t}},ld=async(e,t)=>{await ow(cw.dirname(e),{recursive:!0}),await lw(e,t,"utf8")};var dw=["incrementalCenterPane","agentActivityIndicators"],js=(e,t)=>{if(typeof e!="object"||e===null)throw new Error(`Craig config at ${t} is invalid. Expected a JSON object.`);let n=e;if(n.runners!==void 0){if(typeof n.runners!="object"||n.runners===null||Array.isArray(n.runners))throw new Error(`Craig config at ${t} is invalid. "runners" must be an object.`);for(let[r,s]of Object.entries(n.runners)){if(!z.includes(r))throw new Error(`Craig config at ${t} is invalid. "runners.${r}" is not supported. Expected one of: ${z.join(", ")}.`);if(typeof s!="object"||s===null||Array.isArray(s))throw new Error(`Craig config at ${t} is invalid. "runners.${r}" must be an object.`);let i=s;if(i.enabled!==void 0&&typeof i.enabled!="boolean")throw new Error(`Craig config at ${t} is invalid. "runners.${r}.enabled" must be a boolean.`);if(i.path!==void 0&&(typeof i.path!="string"||i.path.trim().length===0))throw new Error(`Craig config at ${t} is invalid. "runners.${r}.path" must be a non-empty string.`)}}if(n.checks!==void 0&&(typeof n.checks!="object"||n.checks===null||!Array.isArray(n.checks.commands)||!n.checks.commands.every(r=>typeof r=="string")))throw new Error(`Craig config at ${t} is invalid. "checks.commands" must be an array of strings.`);if(n.open!==void 0&&(typeof n.open!="object"||n.open===null||!Array.isArray(n.open.command)||!n.open.command.every(r=>typeof r=="string")))throw new Error(`Craig config at ${t} is invalid. "open.command" must be an array of strings.`);if(n.github!==void 0){if(typeof n.github!="object"||n.github===null)throw new Error(`Craig config at ${t} is invalid. "github" must be an object.`);if(n.github.mergeMethod!==void 0&&n.github.mergeMethod!=="merge"&&n.github.mergeMethod!=="rebase"&&n.github.mergeMethod!=="squash")throw new Error(`Craig config at ${t} is invalid. "github.mergeMethod" must be "merge", "rebase", or "squash".`);if(n.github.watchIntervalSeconds!==void 0&&(!Number.isInteger(n.github.watchIntervalSeconds)||n.github.watchIntervalSeconds<=0))throw new Error(`Craig config at ${t} is invalid. "github.watchIntervalSeconds" must be a positive integer.`)}if(n.previews!==void 0){if(typeof n.previews!="object"||n.previews===null||Array.isArray(n.previews))throw new Error(`Craig config at ${t} is invalid. "previews" must be an object.`);for(let[r,s]of Object.entries(n.previews)){if(!dw.includes(r)&&!ze.includes(r))throw new Error(`Craig config at ${t} is invalid. "previews.${r}" is not supported. Expected one of: ${ze.join(", ")}.`);if(typeof s!="boolean")throw new Error(`Craig config at ${t} is invalid. "previews.${r}" must be a boolean.`)}}return n};var Xo=async(e,t={readConfigFile:ad})=>{let n=await t.readConfigFile(e.configFile);if(!n)return{};if(n.trimStart()[0]!=="{")throw new Error(`Craig config at ${e.configFile} is malformed. Remove or repair the file before rerunning Craig.`);let r;try{r=JSON.parse(n)}catch{throw new Error(`Craig config at ${e.configFile} is malformed. Remove or repair the file before rerunning Craig.`)}return js(r,e.configFile)};var Qo=async(e,t,n={writeConfigFile:ld})=>{js(t,e.configFile),await n.writeConfigFile(e.configFile,`${JSON.stringify(t,null,2)}
|
|
4
|
-
`)};var Zo={};Ds(Zo,{checkForUpdate:()=>cd,getCurrent:()=>qs});var qs=()=>{try{return"0.15.
|
|
4
|
+
`)};var Zo={};Ds(Zo,{checkForUpdate:()=>cd,getCurrent:()=>qs});var qs=()=>{try{return"0.15.2"}catch{return"unknown"}};var pw="https://registry.npmjs.org/craig-cli/latest",fw=3600*1e3,mw={value:null},hw=(e,t)=>{let n=c=>c.split(".").map(Number),[r=0,s=0,i=0]=n(e),[o=0,a=0,l=0]=n(t);return r!==o?r>o:s!==a?s>a:i>l},cd=async(e={fetch:globalThis.fetch,getCurrent:qs,cache:mw})=>{let t=e.getCurrent(),n=Date.now();if(e.cache.value&&n-e.cache.value.cachedAt<fw)return e.cache.value.result;let r=null;try{let i=await e.fetch(pw,{signal:AbortSignal.timeout(5e3)});i.ok&&(r=(await i.json()).version??null)}catch{}let s={current:t,latest:r,updateAvailable:r!==null&&r!==t&&hw(r,t)};return e.cache.value={result:s,cachedAt:n},s};var b={load:Xo,save:Qo,previews:Jo,runners:zo,version:Zo};var ud={CLI_USAGE:2,INPUT_REQUIRED:2,WORKSPACE_CONTEXT_NOT_FOUND:3,WORKSPACE_CONTEXT_INVALID:4,WORKSPACE_NOT_FOUND:3,WORKSPACE_RECORD_INVALID:2,TASK_CONTEXT_NOT_FOUND:3,TASK_CONTEXT_AMBIGUOUS:4,TASK_CONTEXT_CONFLICT:4,TASK_NOT_FOUND:3,TASK_RECORD_INVALID:2,REPO_NOT_FOUND:3,REPO_RECORD_INVALID:2,PR_REPOSITORY_MISMATCH:4,PR_BRANCH_MISMATCH:4,EXTERNAL_DEPENDENCY_FAILED:5,OPERATION_TIMEOUT:6,OPERATION_CANCELLED:6,EVENT_CURSOR_INVALID:2,EVENT_CURSOR_EXPIRED:4,EVENT_JOURNAL_CORRUPT:2,COMMAND_NOT_FOUND:3,COMMAND_RECORD_INVALID:2,COMMAND_STATE_CONFLICT:4,CAPABILITY_NOT_FOUND:3,CAPABILITY_RECORD_INVALID:2,CAPABILITY_DENIED:4,DELEGATION_LIMIT_EXCEEDED:4,FURY_DEFINITION_INVALID:2,FURY_INPUT_INVALID:2,FURY_PLAN_NOT_FOUND:3,FURY_APPROVAL_REQUIRED:4,FURY_RUN_NOT_FOUND:3,FURY_REVIEW_NOT_FOUND:3,FURY_RECORD_INVALID:2,FURY_STATE_CONFLICT:4,FURY_OUTPUT_INVALID:4,PARTIAL_RESULT:7,INTERNAL_ERROR:1},g=class extends Error{code;exitCode;retryable;details;constructor(t,n,r){super(n,r.cause===void 0?void 0:{cause:r.cause}),this.name="CraigError",this.code=t,this.exitCode=ud[t],this.retryable=r.retryable??!1,this.details=r.details??{}}};function ea(e){return e instanceof g?e:new g("INTERNAL_ERROR",e instanceof Error?e.message:"Unknown Craig error",{cause:e})}import{appendFile as gw,mkdir as yw,readFile as ww}from"node:fs/promises";import kw from"node:path";var dd=80,Us=async(e,t)=>{await yw(kw.dirname(e.logFile),{recursive:!0});let n={timestamp:new Date().toISOString(),...t};await gw(e.logFile,`${JSON.stringify(n)}
|
|
5
5
|
`,"utf8")},pd=async(e,t)=>{try{await Us(e,t)}catch{}},Ws=async(e,t=dd)=>{try{let s=(await ww(e.logFile,"utf8")).trimEnd().split(`
|
|
6
6
|
`).filter(i=>i.length>0).map(Tw);return{path:e.logFile,lines:s.slice(Math.max(0,s.length-t)),empty:s.length===0}}catch(n){if(Rw(n))return{path:e.logFile,lines:[],empty:!0};throw n}},ta=async(e,t)=>Us(e,{level:"error",component:"application",event:bw(t.context),message:t.message,...t.details?{details:t.details}:{}}),fd=async(e,t)=>{try{await ta(e,t)}catch{}},md=Ws,hd=async(e,t=dd)=>Ws(e,t);function Tw(e){try{let t=JSON.parse(e);if(!t.timestamp||!t.level||!t.component||!t.event||!t.message)return e;let n=[t.taskId,t.tabId].filter(Boolean).join("/");return`[${t.timestamp}] ${t.level.toUpperCase().padEnd(5)} ${t.component}.${t.event}${n?` ${n}`:""} \u2014 ${t.message}`}catch{return e}}function bw(e){return e.trim().toLowerCase().replace(/[^a-z0-9]+/g,".").replace(/^\.+|\.+$/g,"")||"error"}function Rw(e){return typeof e=="object"&&e!==null&&"code"in e&&typeof e.code=="string"&&e.code==="ENOENT"}var ue={appendErrorLog:ta,appendErrorLogBestEffort:fd,appendLog:Us,appendLogBestEffort:pd,readLog:Ws,readErrorLog:md,readRecentErrorLines:hd};function bd(e){let{commandTokens:t,options:n,help:r}=Pw(e);if(r)return yd({kind:"help"},n);if(t.length===0)return{mode:"interactive",options:n};let[s,i,...o]=t,a;switch(`${s??""}:${i??""}`){case"context:show":Pr(o,"context show"),a={kind:"showContext"};break;case"repo:add":a={kind:"addRepo",path:kd(o,"Repo path")};break;case"repo:list":Pr(o,"repo list"),a={kind:"listRepos"};break;case"repo:remove":a={kind:"removeRepo",repoId:ee(o,"Repo id")};break;case"workspace:add":a={kind:"addWorkspace",path:kd(o,"Workspace path")};break;case"workspace:list":a=Iw(o);break;case"workspace:archive":a={kind:"archiveWorkspace",workspaceId:ee(o,"Workspace id")};break;case"workspace:restore":a={kind:"restoreWorkspace",workspaceId:ee(o,"Workspace id")};break;case"workspace:remove":a={kind:"removeWorkspace",workspaceId:ee(o,"Workspace id")};break;case"task:new":a=Cw(o);break;case"task:create-child":a=Aw(o);break;case"task:children":a={kind:"listTaskChildren",...o.length===0?{}:{taskId:ee(o,"Task id")}};break;case"task:cancel-tree":a={kind:"cancelTaskTree",...o.length===0?{}:{taskId:ee(o,"Task id")}};break;case"task:list":a=vw(o);break;case"task:current":Pr(o,"task current"),a={kind:"currentTask"};break;case"task:show":a=o.length===0?{kind:"showCurrentTask"}:{kind:"showTask",taskId:ee(o,"Task id")};break;case"task:pr":a=Ew(o);break;case"task:wait":a=Lw(o);break;case"agent:list":Pr(o,"agent list"),a={kind:"listAgents"};break;case"agent:status":a=Dw(o);break;case"agent:send":a=Nw(o);break;case"command:show":a={kind:"showPromptCommand",commandId:ee(o,"Command id")};break;case"command:list":Pr(o,"command list"),a={kind:"listPromptCommands"};break;case"command:cancel":a={kind:"cancelPromptCommand",commandId:ee(o,"Command id")};break;case"command:wait":a=_w(o);break;case"events:list":a=gd(o,!1);break;case"events:watch":a=gd(o,!0);break;case"fury:validate":a={kind:"validateFury",file:ee(o,"Fury file")};break;case"fury:plan":a=Fw(o);break;case"fury:approve":a={kind:"approveFury",planId:ee(o,"Fury plan id")};break;case"fury:run":a={kind:"runFury",planId:ee(o,"Fury plan id")};break;case"fury:status":a={kind:"showFury",runId:ee(o,"Fury run id")};break;case"fury:watch":a=Mw(o);break;case"fury:cancel":a={kind:"cancelFury",runId:ee(o,"Fury run id")};break;case"fury:resume":a={kind:"resumeFury",runId:ee(o,"Fury run id")};break;case"fury:step":a=jw(o);break;case"fury:reviews":a=qw(o);break;case"fury:review":a=Uw(o);break;case"task:logs":a={kind:"streamTaskLogs",taskId:ee(o,"Task id")};break;case"task:diff":a={kind:"showTaskDiff",taskId:ee(o,"Task id")};break;case"task:open":a={kind:"openTask",taskId:ee(o,"Task id")};break;case"task:check":a={kind:"runChecks",taskId:ee(o,"Task id")};break;case"task:commit":a={kind:"commitTask",taskId:ee(o,"Task id")};break;case"link:add":a={kind:"addTaskLink",taskId:Td(o,0,"Task id"),repoId:Td(o,1,"Repo id")},oa(o,2,"link add");break;case"link:list":a={kind:"listTaskLinks",taskId:ee(o,"Task id")};break;default:if(t.length===1&&(s==="help"||s==="--help"||s==="-h")){a={kind:"help"};break}throw P(`Unsupported command: ${t.join(" ")}`)}return yd(a,n)}function Rd(e){let t=e[0]==="--"?e.slice(1):e,n=!0;for(let r of t){if(r==="--"){n=!1;continue}if(n&&r==="--json")return!0}return!1}function Sw(e){return e.kind==="actFuryReview"?`fury.review.${e.action==="request_changes"?"request-changes":e.action}`:{addWorkspace:"workspace.add",addRepo:"repo.add",listRepos:"repo.list",removeRepo:"repo.remove",listWorkspaces:"workspace.list",archiveWorkspace:"workspace.archive",restoreWorkspace:"workspace.restore",removeWorkspace:"workspace.remove",createTask:"task.new",createChildTask:"task.create-child",listTaskChildren:"task.children",cancelTaskTree:"task.cancel-tree",validateFury:"fury.validate",planFury:"fury.plan",approveFury:"fury.approve",runFury:"fury.run",showFury:"fury.status",watchFury:"fury.watch",cancelFury:"fury.cancel",resumeFury:"fury.resume",completeFuryStep:"fury.step.complete",failFuryStep:"fury.step.fail",listFuryReviews:"fury.reviews.list",showFuryReview:"fury.review.show",actFuryReview:"fury.review.action",listTasks:"task.list",currentTask:"task.current",showTask:"task.show",showCurrentTask:"task.show",showTaskPr:"task.pr.show",discoverTaskPr:"task.pr.discover",linkTaskPr:"task.pr.link",refreshTaskPr:"task.pr.refresh",unlinkTaskPr:"task.pr.unlink",listAgents:"agent.list",showAgentStatus:"agent.status",sendAgentPrompt:"agent.send",showPromptCommand:"command.show",listPromptCommands:"command.list",cancelPromptCommand:"command.cancel",waitPromptCommand:"command.wait",waitTask:"task.wait",listEvents:"events.list",watchEvents:"events.watch",addTaskLink:"link.add",listTaskLinks:"link.list",refreshInteractiveState:"interactive.refresh",showSelectedTask:"task.show",streamTaskLogs:"task.logs",streamSelectedTaskLogs:"task.logs",showTaskDiff:"task.diff",showSelectedTaskDiff:"task.diff",openTask:"task.open",openSelectedTask:"task.open",runChecks:"task.check",runSelectedTaskChecks:"task.check",commitTask:"task.commit",commitSelectedTask:"task.commit",showContext:"context.show",help:"help",exit:"exit"}[e.kind]}function na(){return["Craig commands:"," craig [--workspace-root <path>] [--task <id>] [--json] [--no-input] <command>"," craig context show Show resolved workspace, task, and agent-tab context"," craig repo add <path> Register a repo in the current Craig workspace"," craig workspace add <path> Register a repo or parent-directory workspace"," craig repo list List registered repos"," craig repo remove <id> Remove a registered repo"," craig workspace list [--archived] List workspaces"," craig workspace archive <id> Archive a workspace"," craig workspace restore <id> Restore an archived workspace"," craig workspace remove <id> Remove an archived workspace"," craig task new --repo <repo-id> [--runner codex|cursor|claude] <prompt>"," craig task new --workspace <workspace-id> [--runner codex|cursor|claude] <prompt>"," craig task create-child [--parent <task-id>] [--repo <repo-id> | --workspace <workspace-id>] [--runner codex|cursor|claude] [--idempotency-key <key>] <prompt>"," craig task children [<task-id>] List direct child tasks"," craig task cancel-tree [<task-id>] Cancel a task and all descendants"," craig task list [--repo <repo-id>] List known Craig tasks"," craig task current Show the task resolved from flags, environment, or cwd"," craig task show [<id>] Show task details; omit id to use resolved context"," craig task pr show [<id>] [--repo <repo-id>]"," craig task pr discover [<id>] [--repo <repo-id>]"," craig task pr link [<id>] --pr <url|number> [--repo <repo-id>]"," craig task pr refresh [<id>] [--repo <repo-id>]"," craig task pr unlink [<id>] --pr <url|number> [--repo <repo-id>]"," craig agent list [--task <task-id>] List agent-tab and task roll-up states"," craig agent status [--task <task-id>] [--tab <tab-id>] Show agent state details"," craig agent send --task <task-id> [--tab <tab-id>] (--prompt <text> | --prompt-file <path> | --stdin)"," [--delivery when-ready|immediate] [--timeout <duration>] [--idempotency-key <key>]"," craig command show <command-id> Show a durable prompt command"," craig command list [--task <task-id>] List durable prompt commands"," craig command cancel <command-id> Cancel a queued prompt command"," craig command wait <command-id> [--state <states>] [--timeout <duration>]"," craig task wait [<id>] --state <states> [--tab <tab-id>] [--timeout <duration>]"," craig events list [--task <task-id>] [--type <glob>] [--after <cursor>] --json"," craig events watch [--task <task-id>] [--type <glob>] [--after <cursor>] [--format jsonl]"," craig fury validate <file> Validate a versioned fury YAML definition"," craig fury plan <file> [--root-task <id>] [--input key=value] Create an immutable approval plan"," craig fury approve <plan-id> Approve an exact validated plan (human only)"," craig fury run <plan-id> Start the single run authorized by an approved plan"," craig fury status|watch|cancel|resume <run-id> Inspect or control a durable run"," craig fury step complete|fail --run <run-id> --step <step-id> Explicitly finish agent work"," craig fury reviews list [--run <run-id>] [--state <state>] List human checkpoints"," craig fury review show|approve|reject|request-changes|resubmit <review-id>"," craig task logs <id> Stream Craig-managed logs for a task"," craig task diff <id> Show the current worktree diff for a task"," craig task open <id> Open the task worktree or print its path"," craig task check <id> Run configured checks for a task"," craig task commit <id> Commit all task worktree changes"," craig link add <task-id> <repo-id> Add a linked repo to a task"," craig link list <task-id> List linked repos for a task"].join(`
|
|
7
7
|
`)}function Pw(e){let t=[...e];t[0]==="--"&&t.shift();let n=[],r={json:!1,noInput:!1},s=new Set,i=!1,o=!0;for(let a=0;a<t.length;a+=1){let l=t[a];if(l==="--"){o=!1,n.push(l);continue}if(!o||!wd(l)){n.push(l);continue}if(s.has(l))throw P(`Global option ${l} may only be provided once.`);if(s.add(l),l==="--json"){r.json=!0;continue}if(l==="--no-input"){r.noInput=!0;continue}if(l==="--help"||l==="-h"){i=!0;continue}let c=t[a+1];if(c===void 0||c==="--"||wd(c))throw P(`Global option ${l} requires a value.`);a+=1,l==="--workspace-root"?r.workspaceRoot=L(c,"Workspace root"):r.taskId=L(c,"Task id")}return{commandTokens:n,options:r,help:i}}function Iw(e){if(e.length===0)return{kind:"listWorkspaces",archived:!1};if(e.length===1&&e[0]==="--archived")return{kind:"listWorkspaces",archived:!0};throw P(`Unsupported command: workspace list ${e.join(" ")}`)}function vw(e){if(e.length===0)return{kind:"listTasks"};if(e.length===2&&e[0]==="--repo")return{kind:"listTasks",repoId:L(e[1],"Repo id")};throw P(`Unsupported command: task list ${e.join(" ")}`)}function Cw(e){let t,n,r,s=[],i=!0;for(let a=0;a<e.length;a+=1){let l=e[a];if(l==="--"){i=!1;continue}if(!i||!["--repo","--workspace","--runner"].includes(l)){if(i&&l.startsWith("--"))throw P(`Unsupported task new option: ${l}`);s.push(l);continue}let c=e[a+1];if(c===void 0||c==="--")throw P(`Task option ${l} requires a value.`);if(a+=1,l==="--repo"){if(t!==void 0)throw P("Task option --repo may only be provided once.");t=L(c,"Repo id")}else if(l==="--workspace"){if(n!==void 0)throw P("Task option --workspace may only be provided once.");n=L(c,"Workspace id")}else{if(r!==void 0)throw P("Task option --runner may only be provided once.");try{r=b.runners.parse(c)}catch(d){throw P(d instanceof Error?d.message:"Unsupported runner.")}}}if(!t&&!n)throw P("Task creation requires '--repo <repo-id>' or '--workspace <workspace-id>'.");if(t&&n)throw P("Task creation accepts either '--repo' or '--workspace', not both.");let o=L(s.join(" "),"Task prompt");return{kind:"createTask",...t?{repoId:t}:{},...n?{workspaceId:n}:{},...r?{runner:r}:{},prompt:o}}function Aw(e){let t,n,r,s,i,o=[],a=!0;for(let l=0;l<e.length;l+=1){let c=e[l];if(c==="--"){a=!1;continue}if(!a||!["--parent","--repo","--workspace","--runner","--idempotency-key"].includes(c)){if(a&&c.startsWith("--"))throw P(`Unsupported task create-child option: ${c}`);o.push(c);continue}let d=e[l+1];if(d===void 0||d==="--")throw P(`Task option ${c} requires a value.`);if(l+=1,c==="--parent"){if(t)throw P("Task option --parent may only be provided once.");t=L(d,"Parent task id")}else if(c==="--repo"){if(n)throw P("Task option --repo may only be provided once.");n=L(d,"Repo id")}else if(c==="--workspace"){if(r)throw P("Task option --workspace may only be provided once.");r=L(d,"Workspace id")}else if(c==="--runner"){if(s)throw P("Task option --runner may only be provided once.");try{s=b.runners.parse(d)}catch(u){throw P(u instanceof Error?u.message:"Unsupported runner.")}}else{if(i)throw P("Task option --idempotency-key may only be provided once.");i=L(d,"Idempotency key")}}if(n&&r)throw P("Child task creation accepts only one of --repo or --workspace.");return{kind:"createChildTask",...t?{parentTaskId:t}:{},...n?{repoId:n}:{},...r?{workspaceId:r}:{},prompt:L(o.join(" "),"Child task prompt"),...s?{runner:s}:{},...i?{idempotencyKey:i}:{}}}function Ew(e){let[t,...n]=e;if(!["show","discover","link","refresh","unlink"].includes(t??""))throw P(`Unsupported command: task pr ${e.join(" ")}`);let r,s,i;for(let a=0;a<n.length;a+=1){let l=n[a];if(l!=="--repo"&&l!=="--pr"){if(l.startsWith("--"))throw P(`Unsupported task pr ${t} option: ${l}`);if(r!==void 0)throw P(`task pr ${t} accepts at most one task id.`);r=L(l,"Task id");continue}let c=n[a+1];if(c===void 0||c==="--")throw P(`Task PR option ${l} requires a value.`);if(a+=1,l==="--repo"){if(s!==void 0)throw P("Task PR option --repo may only be provided once.");s=L(c,"Repo id")}else{if(i!==void 0)throw P("Task PR option --pr may only be provided once.");i=L(c,"Pull request selector")}}if((t==="link"||t==="unlink")&&i===void 0)throw P(`task pr ${t} requires --pr <url|number>.`);if(t!=="link"&&t!=="unlink"&&i!==void 0)throw P(`task pr ${t} does not accept --pr.`);let o={...r?{taskId:r}:{},...s?{repoId:s}:{}};switch(t){case"show":return{kind:"showTaskPr",...o};case"discover":return{kind:"discoverTaskPr",...o};case"link":return{kind:"linkTaskPr",...o,pullRequest:i};case"refresh":return{kind:"refreshTaskPr",...o};case"unlink":return{kind:"unlinkTaskPr",...o,pullRequest:i};default:throw P(`Unsupported command: task pr ${e.join(" ")}`)}}var ra=300*1e3,Ow=new Set(["idle","working","ready","error"]),$w=new Set(["queued","delivering","delivered","failed","cancelled"]);function Dw(e){if(e.length===0)return{kind:"showAgentStatus"};if(e.length===2&&e[0]==="--tab")return{kind:"showAgentStatus",tabId:L(e[1],"Agent tab id")};throw P(`Unsupported command: agent status ${e.join(" ")}`)}function Nw(e){let t,n,r="when-ready",s=ra,i,o=new Set;for(let a=0;a<e.length;a+=1){let l=e[a];if(!["--prompt","--prompt-file","--stdin","--tab","--delivery","--timeout","--idempotency-key"].includes(l))throw P(`Unsupported agent send option: ${l}`);if(o.has(l))throw P(`Agent send option ${l} may only be provided once.`);if(o.add(l),l==="--stdin"){if(t)throw P("Agent send accepts exactly one prompt source.");t={source:"stdin"};continue}let c=e[a+1];if(c===void 0||c.startsWith("--"))throw P(`Agent send option ${l} requires a value.`);if(a+=1,l==="--prompt"||l==="--prompt-file"){if(t)throw P("Agent send accepts exactly one prompt source.");t=l==="--prompt"?{source:"inline",text:L(c,"Prompt")}:{source:"file",path:L(c,"Prompt file")}}else if(l==="--tab")n=L(c,"Agent tab id");else if(l==="--delivery"){if(c!=="when-ready"&&c!=="immediate")throw P('Agent send delivery must be "when-ready" or "immediate".');r=c}else l==="--timeout"?s=ia(c):i=L(c,"Idempotency key")}if(!t)throw P("Agent send requires --prompt, --prompt-file, or --stdin.");return{kind:"sendAgentPrompt",prompt:t,delivery:r,timeoutMs:s,...n?{tabId:n}:{},...i?{idempotencyKey:i}:{}}}function _w(e){let t=L(e[0]??"","Command id"),n,r=ra,s=new Set;for(let i=1;i<e.length;i+=1){let o=e[i];if(o!=="--state"&&o!=="--timeout")throw P(`Unsupported command wait option: ${o}`);if(s.has(o))throw P(`Command wait option ${o} may only be provided once.`);s.add(o);let a=e[i+1];if(a===void 0||a.startsWith("--"))throw P(`Command wait option ${o} requires a value.`);i+=1,o==="--state"?n=xw(a):r=ia(a)}return{kind:"waitPromptCommand",commandId:t,...n?{states:n}:{},timeoutMs:r}}function xw(e){let t=e.split(",").map(n=>n.trim()).filter(Boolean);if(t.length===0||t.some(n=>!$w.has(n)))throw P(`Invalid command state list "${e}". Use queued, delivering, delivered, failed, or cancelled.`);return[...new Set(t)]}function gd(e,t){let n,r,s="human",i=new Set;for(let a=0;a<e.length;a+=1){let l=e[a];if(!["--type","--after","--format"].includes(l))throw P(`Unsupported events ${t?"watch":"list"} option: ${l}`);if(!t&&l==="--format")throw P("--format is supported only by events watch.");if(i.has(l))throw P(`Events option ${l} may only be provided once.`);i.add(l);let c=e[a+1];if(c===void 0||c.startsWith("--"))throw P(`Events option ${l} requires a value.`);if(a+=1,l==="--type")n=L(c,"Event type glob");else if(l==="--after")r=L(c,"Event cursor");else if(c==="jsonl")s="jsonl";else throw P('Events watch format must be "jsonl".')}let o={...n?{typeGlob:n}:{},...r?{after:r}:{}};return t?{kind:"watchEvents",...o,format:s}:{kind:"listEvents",...o}}function Lw(e){let t,n,r,s=ra,i=new Set;for(let o=0;o<e.length;o+=1){let a=e[o];if(!["--state","--tab","--timeout"].includes(a)){if(a.startsWith("--"))throw P(`Unsupported task wait option: ${a}`);if(t!==void 0)throw P("task wait accepts at most one task id.");t=L(a,"Task id");continue}if(i.has(a))throw P(`Task wait option ${a} may only be provided once.`);i.add(a);let l=e[o+1];if(l===void 0||l.startsWith("--"))throw P(`Task wait option ${a} requires a value.`);o+=1,a==="--state"?n=Ww(l):a==="--tab"?r=L(l,"Agent tab id"):s=ia(l)}if(!n)throw P("task wait requires --state <idle|working|ready|error>[,...].");return{kind:"waitTask",...t?{taskId:t}:{},states:n,...r?{tabId:r}:{},timeoutMs:s}}function Fw(e){let t,n,r={};for(let s=0;s<e.length;s+=1){let i=e[s];if(i!=="--input"&&i!=="--root-task"){if(i.startsWith("--"))throw P(`Unsupported fury plan option: ${i}`);if(t!==void 0)throw P("fury plan accepts exactly one definition file.");t=L(i,"Fury file");continue}let o=e[s+1];if(o===void 0||o.startsWith("--"))throw P(`Fury plan option ${i} requires a value.`);if(s+=1,i==="--root-task"){if(n)throw P("Fury plan option --root-task may only be provided once.");n=L(o,"Root task id");continue}let a=o.indexOf("=");if(a<1)throw P(`Invalid fury input "${o}". Use key=value.`);let l=L(o.slice(0,a),"Fury input name");if(Object.hasOwn(r,l))throw P(`Fury input "${l}" may only be provided once.`);r[l]=o.slice(a+1)}if(!t)throw P("fury plan requires a definition file.");return{kind:"planFury",file:t,inputs:r,...n?{rootTaskId:n}:{}}}function Mw(e){let t=L(e[0]??"","Fury run id"),n,r="human";for(let s=1;s<e.length;s+=2){let i=e[s],o=e[s+1];if(!o||!["--after","--format"].includes(i??""))throw P("fury watch supports --after and --format jsonl.");if(i==="--after")n=L(o,"Event cursor");else if(o==="jsonl")r=o;else throw P("fury watch format must be jsonl.")}return{kind:"watchFury",runId:t,...n?{after:n}:{},format:r}}function jw(e){let t=e[0];if(t!=="complete"&&t!=="fail")throw P("fury step requires complete or fail.");let n=sa(e.slice(1),["--run","--step","--output","--output-file","--stdin","--reason"]),r=L(n.get("--run")??"","Fury run id"),s=L(n.get("--step")??"","Fury step id");if(t==="fail"){if(["--output","--output-file","--stdin"].some(a=>n.has(a)))throw P("fury step fail accepts only --reason.");return{kind:"failFuryStep",runId:r,stepId:s,reason:L(n.get("--reason")??"","Failure reason")}}if(n.has("--reason"))throw P("fury step complete does not accept --reason.");let i=["--output","--output-file","--stdin"].filter(a=>n.has(a));if(i.length>1)throw P("fury step complete accepts only one output source.");let o=i[0];return{kind:"completeFuryStep",runId:r,stepId:s,...o==="--output"?{output:{source:"inline",text:n.get(o)}}:o==="--output-file"?{output:{source:"file",path:n.get(o)}}:o==="--stdin"?{output:{source:"stdin"}}:{}}}function qw(e){if(e[0]!=="list")throw P("fury reviews requires list.");let t=sa(e.slice(1),["--run","--state"]),n=t.get("--run"),r=t.get("--state");if(r&&!["waiting_for_review","changes_requested","approved","rejected","timed_out","cancelled"].includes(r))throw P(`Unsupported fury review state: ${r}`);return{kind:"listFuryReviews",...n?{runId:n}:{},...r?{state:r}:{}}}function Uw(e){let t=e[0],n=L(e[1]??"","Fury review id");if(t==="show")return oa(e,2,"fury review show"),{kind:"showFuryReview",reviewId:n};if(!t||!["approve","reject","request-changes","resubmit"].includes(t))throw P("Unsupported fury review action.");let r=sa(e.slice(2),["--note","--reason","--summary"]),s=t==="request-changes"?"request_changes":t,i=r.get("--note")??r.get("--reason")??r.get("--summary"),o=t==="approve"?"--note":t==="resubmit"?"--summary":"--reason";if([...r.keys()].some(a=>a!==o))throw P(`fury review ${t} accepts only ${o}.`);return{kind:"actFuryReview",reviewId:n,action:s,...i?{message:i}:{}}}function sa(e,t){let n=new Map;for(let r=0;r<e.length;r+=1){let s=e[r];if(!t.includes(s)||n.has(s))throw P(`Unsupported or duplicate option: ${s}`);if(s==="--stdin"){n.set(s,"true");continue}let i=e[++r];if(i===void 0||i.startsWith("--"))throw P(`Option ${s} requires a value.`);n.set(s,L(i,s))}return n}function Ww(e){let t=e.split(",").map(n=>n.trim()).filter(Boolean);if(t.length===0||t.some(n=>!Ow.has(n)))throw P(`Invalid agent state list "${e}". Use idle, working, ready, or error.`);return[...new Set(t)]}function ia(e){let t=/^(\d+)(ms|s|m|h)$/.exec(e.trim());if(!t)throw P(`Invalid duration "${e}". Use values such as 500ms, 30s, 5m, or 1h.`);let n=Number(t[1]),r={ms:1,s:1e3,m:6e4,h:36e5}[t[2]],s=n*r;if(!Number.isSafeInteger(s))throw P(`Duration "${e}" is too large.`);return s}function yd(e,t){return{mode:"command",command:e,commandName:Sw(e),options:t}}function wd(e){return["--json","--no-input","--workspace-root","--task","--help","-h"].includes(e)}function Pr(e,t){oa(e,0,t)}function oa(e,t,n){if(e.length!==t)throw P(`Command ${n} received an unexpected number of arguments.`)}function ee(e,t){if(e.length!==1)throw P(`${t} is required and must be provided once.`);return L(e[0],t)}function kd(e,t){return L(e.join(" "),t)}function Td(e,t,n){return L(e[t]??"",n)}function L(e,t){let n=e.trim();if(n.length===0)throw P(`${t} cannot be empty.`);return n}function P(e){return new g("CLI_USAGE",`${e}
|