craig-cli 0.15.0 → 0.15.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 (3) hide show
  1. package/README.md +38 -214
  2. package/dist/cli.js +1 -1
  3. 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 agent orchestrator. For the people.**
15
+ **An agnostic orchestrator TUI, for the people.**
18
16
 
19
17
  [![npm](https://img.shields.io/npm/v/craig-cli)](https://www.npmjs.com/package/craig-cli)
20
- [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
18
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://github.com/hallsamuel90/craig/blob/main/LICENSE)
21
19
  [![Node 22+](https://img.shields.io/badge/node-22%2B-brightgreen)](https://nodejs.org)
22
20
 
23
- [craig-cli.com](https://craig-cli.com) · [Docs](https://craig-cli.com/docs/) · [Quickstart](https://craig-cli.com/docs/quickstart/)
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
- Craig is a full-screen TUI that lets you run multiple AI agents across multiple repos at the same time — each in its own isolated git worktree, each with a live PTY, all from one terminal. Spin up Claude, Codex, and Cursor simultaneously. Watch them cook. Inspect diffs and review state while the agent handles repo actions from its PTY.
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
- Requires Node 22+, `git`, and `gh` (authenticated). Bring your own agent CLI — Claude Code, Codex, or Cursor.
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
- ## Start
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
- The TUI opens with a boot overlay, then a three-panel workspace: task navigation on the left, live agent PTY in the center, file/diff/review inspector on the right.
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
- Navigation
84
- Left/Right, h/l switch tabs or inspector mode
35
+ ## Why Craig?
85
36
 
86
- Tasks
87
- Enter on + New Task open task prompt
88
- Enter on + New Workspace open workspace browser
89
- Enter on task attach selected task PTY
90
- r on + New Task cycle runner for next task
91
- X on task close selected task
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
- Center panel
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
- craig repo add <path>
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
- Agent activity indicators are enabled by default. Feature previews are experimental, workspace-local, and off by default. They can also be toggled from Options > Feature Previews in the TUI.
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
- ### Cursor workspace trust
52
+ ## Quick start
212
53
 
213
- Cursor may ask separately whether to trust each Craig-managed worktree or project task bundle. To establish trust once at their common parent, open `~/.craig` in Cursor and approve its workspace trust prompt. Trust only that directory if you are comfortable allowing Cursor agents to work throughout Craig's local task state. Craig does not pass Cursor's `--trust` flag or otherwise weaken the runner's safety settings. For narrower project-only testing, opening `~/.craig/task_bundles` limits the trusted parent to project bundles but does not cover repo worktrees under `~/.craig/worktrees`.
54
+ Run `craig` in a full-screen terminal, then work through the TUI:
214
55
 
215
- An interactive Craig launched from inside an agent session will not implicitly attach to that agent's live workspace. Pass `--workspace-root <live-root>` to confirm that attachment, or create and pass a different directory for an isolated blank workspace. A client also refuses to replace a live PTY daemon that uses an incompatible protocol; exit the other Craig instance before restarting explicitly.
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
- Event commands, new prompt dispatch creation, child-task creation, and fury validation/planning require the `agentOrchestration` preview. Preview-enabled agent PTYs receive an opaque workspace-local capability token scoped to their own task, the stable delegation command families, an expiry, and bounded child/depth/concurrency/prompt limits; only a non-authorizing reference is stored on the task. Fury operations reuse that scoped token according to each command's preview and recovery policy without persisting preview-only command families into the base capability record, so stable Craig can still read the workspace after preview use. `task create-child` inherits the parent target when neither `--repo` nor `--workspace` is supplied: project parents create project children, while repo parents create children in the same repo. An explicit repo child of a project task stays owned by that project workspace, so it does not require a separate standalone repo workspace. `task children` and idempotent `task cancel-tree` remain available when the preview is disabled so existing lineage can be inspected and safely stopped. Craig may still maintain durable semantic events internally for stable daemon services such as pull request synchronization. Disabling the preview stops new dispatch and child creation but leaves command inspection, waiting, cancellation, and safe reconciliation available for existing durable work. Prompt delivery defaults to `when-ready`; `immediate` explicitly interrupts a busy target. Craig sends a dispatched prompt and its submit key as separate terminal input events so the submit key cannot be consumed as part of the bracketed paste. A delegated child starts in a daemon-owned agent session immediately and selecting that child in the TUI attaches to the same process.
63
+ See the [TUI reference](https://craig-cli.com/docs/tui/) for every panel and keyboard shortcut.
218
64
 
219
- Fury definitions are strict, versioned YAML DAGs stored under `.craig/fury/`. A `create_child` step can specify `repo`, specify `workspace`, or use an empty mapping (`create_child: {}`) to inherit its planning parent's target. `fury validate` checks the schema, limits, templates, references, dependencies, output schemas, and human-review checkpoints without mutating runtime state. `fury plan` resolves declared inputs, binds the graph to a planning task, persists an immutable content-hashed plan, and requests human sign-off.
65
+ ## Local by default
220
66
 
221
- Only a human can `fury approve` the exact plan hash. `fury run` accepts only an approved plan id, creates at most one run for that approval, and schedules every new task as a direct child of the planning task; graph dependencies never create visual grandchildren. Every agent step must explicitly call `fury step complete` or `fury step fail`; terminal silence is never success. Declared outputs are JSON-schema validated before dependents can run. Human-review steps remain blocked until an attributable human approval, while request-changes routes durable feedback to the declared task and requires an explicit resubmission. The sidebar shows one flat Fury attention row when plan or review approvals are waiting. The heartbeat reconciles interrupted dispatch, deadlines, missing agent sessions, and pending work. Retries, conditions, fan-out, and recursive Fury runs remain deferred.
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
- ```yaml
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
- The TUI sidebar groups descendants beneath their root task while enforcing a single visual child level. Grandchildren and deeper descendants use the same indentation as direct children, and every task row continues to show only that task's agent-tab activity roll-up.
245
-
246
- `github.watchIntervalSeconds` is the minimum GitHub polling interval. Craig polls selected PRs with pending checks
247
- most frequently, backs stable PRs and PR discovery off automatically, and pauses after rate-limit responses. The
248
- workspace daemon keeps PR discovery and check synchronization running while the TUI is detached or closed, and
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. PRs should be discussed with the maintainer first — unsolicited PRs may be closed without review.
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.0"}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)}
4
+ `)};var Zo={};Ds(Zo,{checkForUpdate:()=>cd,getCurrent:()=>qs});var qs=()=>{try{return"0.15.1"}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}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "craig-cli",
3
- "version": "0.15.0",
3
+ "version": "0.15.1",
4
4
  "description": "Craig local control plane CLI",
5
5
  "license": "MIT",
6
6
  "keywords": [