@dhananjay_kaushik/claude-orchestrator 0.1.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.
- package/LICENSE +21 -0
- package/README.md +370 -0
- package/bin/claude-orchestrator.js +2 -0
- package/dist/cli.js +79 -0
- package/dist/commands/doctor.js +43 -0
- package/dist/commands/init.js +36 -0
- package/dist/commands/logs.js +43 -0
- package/dist/commands/plan.js +125 -0
- package/dist/commands/run.js +349 -0
- package/dist/commands/status.js +60 -0
- package/dist/commands/validate.js +58 -0
- package/dist/config/defaults.js +28 -0
- package/dist/config/index.js +3 -0
- package/dist/config/loader.js +53 -0
- package/dist/config/schema.js +50 -0
- package/dist/executor/command.js +18 -0
- package/dist/executor/execution.js +191 -0
- package/dist/executor/parser.js +33 -0
- package/dist/executor/policy.js +64 -0
- package/dist/executor/state.js +36 -0
- package/dist/executor/verification.js +101 -0
- package/dist/git/branch.js +82 -0
- package/dist/git/commit.js +47 -0
- package/dist/git/repo.js +55 -0
- package/dist/logging/format.js +7 -0
- package/dist/logging/redact.js +11 -0
- package/dist/models.js +5 -0
- package/dist/plans/discovery.js +54 -0
- package/dist/plans/parser.js +136 -0
- package/dist/prompts/execution.js +41 -0
- package/dist/prompts/plan.js +42 -0
- package/dist/types/index.js +7 -0
- package/dist/worktrees/index.js +82 -0
- package/package.json +63 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Dhananjay Kaushik
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
# Claude Code Orchestrator
|
|
2
|
+
|
|
3
|
+
Claude Code Orchestrator is a TypeScript CLI for running Claude Code through a test-driven, task-by-task workflow with verification, logs, resumability, and safe Git commits.
|
|
4
|
+
|
|
5
|
+
The core rule is:
|
|
6
|
+
|
|
7
|
+
**Claude implements one selected task. The orchestrator owns lifecycle state.**
|
|
8
|
+
|
|
9
|
+
Claude task sessions do not mark tasks `DONE`, do not commit, and do not decide whether work is complete. The orchestrator validates the plan, runs Claude headlessly, checks structured results, runs verification commands, updates task state, and commits only after verification passes.
|
|
10
|
+
|
|
11
|
+
## Status
|
|
12
|
+
|
|
13
|
+
The MVP described below is implemented and tested. The original implementation contract is documented in [PLAN.md](./PLAN.md), [SKILLS.md](./SKILLS.md), and [AGENTS.md](./AGENTS.md).
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install -g @dhananjay_kaushik/claude-orchestrator
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Requires Node.js >= 18 and the `claude` CLI on your `PATH`.
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
cd your-project
|
|
27
|
+
claude-orchestrator init # create .claude-orchestrator.json
|
|
28
|
+
claude-orchestrator plan # generate a Markdown task plan
|
|
29
|
+
claude-orchestrator run # execute the next task
|
|
30
|
+
claude-orchestrator run --loop # execute tasks until one needs attention
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Workflow
|
|
34
|
+
|
|
35
|
+
The intended workflow is:
|
|
36
|
+
|
|
37
|
+
1. Create or validate config.
|
|
38
|
+
2. Generate a Markdown implementation plan.
|
|
39
|
+
3. Validate the plan.
|
|
40
|
+
4. Select one task.
|
|
41
|
+
5. Create or reuse an isolated per-task Git worktree.
|
|
42
|
+
6. Run Claude Code headlessly for that task.
|
|
43
|
+
7. Parse Claude's structured JSON result.
|
|
44
|
+
8. Run verification commands.
|
|
45
|
+
9. Mark the task `DONE` only if verification passes.
|
|
46
|
+
10. Commit verified changes.
|
|
47
|
+
11. Stop, resume later, or explicitly continue in loop mode.
|
|
48
|
+
|
|
49
|
+
## Plan Format
|
|
50
|
+
|
|
51
|
+
Plans live in `planDir`, defaulting to `workflow_generated_plans`.
|
|
52
|
+
|
|
53
|
+
Tasks use a strict 5-state Markdown checkbox format:
|
|
54
|
+
|
|
55
|
+
- `- [ ]` or `* [ ]`: `NOT_DONE`
|
|
56
|
+
- `- [-]` or `* [-]`: `IN_PROGRESS`
|
|
57
|
+
- `- [x]`, `- [X]`, `* [x]`, or `* [X]`: `DONE`
|
|
58
|
+
- `- [f]`, `- [F]`, `* [f]`, or `* [F]`: `FAILED`
|
|
59
|
+
- `- [b]`, `- [B]`, `* [b]`, or `* [B]`: `BLOCKED`
|
|
60
|
+
|
|
61
|
+
Task identity is based on a stable slug/hash of normalized task text and heading context. Line numbers are not used as persistent task IDs because plan files can change as handoff notes or content are added.
|
|
62
|
+
|
|
63
|
+
Task text must be unique across the whole plan, not just within a section — the parser rejects a plan with two tasks that read the same, even under different headings.
|
|
64
|
+
|
|
65
|
+
Add a Status Tracker section near the top with running counts (`Total`, `NOT_DONE`, `IN_PROGRESS`, `DONE`, `FAILED`, `BLOCKED`) — the orchestrator's planning prompt generates one, and it's the fastest way for a human or a resumed session to see plan progress at a glance.
|
|
66
|
+
|
|
67
|
+
## Claude Execution Contract
|
|
68
|
+
|
|
69
|
+
Planning and execution use different Claude modes:
|
|
70
|
+
|
|
71
|
+
- `plan` is interactive.
|
|
72
|
+
- `run` is headless.
|
|
73
|
+
|
|
74
|
+
Execution must use Claude Code's non-interactive JSON mode:
|
|
75
|
+
|
|
76
|
+
```text
|
|
77
|
+
claude -p "<prompt>" --output-format json
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
The orchestrator expects structured JSON fields such as:
|
|
81
|
+
|
|
82
|
+
- `result`
|
|
83
|
+
- `total_cost_usd`
|
|
84
|
+
- `usage`
|
|
85
|
+
- `session_id`
|
|
86
|
+
- `is_error`
|
|
87
|
+
|
|
88
|
+
The orchestrator stores the raw JSON response in the task log directory. Token usage and cost are read from structured JSON fields, not scraped from terminal text.
|
|
89
|
+
|
|
90
|
+
The `result` field must include an orchestrator sentinel:
|
|
91
|
+
|
|
92
|
+
- `ORCHESTRATOR_RESULT: SUCCESS`
|
|
93
|
+
- `ORCHESTRATOR_RESULT: BLOCKED`
|
|
94
|
+
- `ORCHESTRATOR_RESULT: NEEDS_RETRY_CONTEXT`
|
|
95
|
+
|
|
96
|
+
`DONE` is never inferred from Claude output. `DONE` requires successful verification.
|
|
97
|
+
|
|
98
|
+
## Commands
|
|
99
|
+
|
|
100
|
+
### `claude-orchestrator init`
|
|
101
|
+
|
|
102
|
+
Create or update `.claude-orchestrator.json` interactively.
|
|
103
|
+
|
|
104
|
+
Expected behavior:
|
|
105
|
+
|
|
106
|
+
- Prompt for plan directory, base branch, verification commands, timeouts, and Claude permission settings.
|
|
107
|
+
- Write a validated config file.
|
|
108
|
+
- Avoid adding post-MVP features such as notifications unless explicitly supported later.
|
|
109
|
+
|
|
110
|
+
### `claude-orchestrator doctor`
|
|
111
|
+
|
|
112
|
+
Check whether the current project can safely run orchestration.
|
|
113
|
+
|
|
114
|
+
Checks include:
|
|
115
|
+
|
|
116
|
+
- Claude binary exists.
|
|
117
|
+
- Claude authentication can be verified if Claude exposes a stable check.
|
|
118
|
+
- Current directory is a Git repository.
|
|
119
|
+
- Config is valid.
|
|
120
|
+
- Plan directory is readable.
|
|
121
|
+
- State, log, and worktree directories are writable.
|
|
122
|
+
- Verification commands satisfy command policy.
|
|
123
|
+
- Claude permission settings are safe.
|
|
124
|
+
|
|
125
|
+
No task state is mutated by `doctor`.
|
|
126
|
+
|
|
127
|
+
### `claude-orchestrator validate`
|
|
128
|
+
|
|
129
|
+
Validate config and plan files without executing Claude.
|
|
130
|
+
|
|
131
|
+
Checks include:
|
|
132
|
+
|
|
133
|
+
- `.claude-orchestrator.json` schema and defaults.
|
|
134
|
+
- Structured verification commands.
|
|
135
|
+
- Protected paths.
|
|
136
|
+
- Plan checkbox states.
|
|
137
|
+
- Duplicate or unstable task identity.
|
|
138
|
+
- Unsupported or ambiguous task markers.
|
|
139
|
+
|
|
140
|
+
### `claude-orchestrator plan`
|
|
141
|
+
|
|
142
|
+
Start an interactive planning session with Claude Code.
|
|
143
|
+
|
|
144
|
+
Expected behavior:
|
|
145
|
+
|
|
146
|
+
- Prompt for planning model if needed.
|
|
147
|
+
- Ensure the plan directory exists.
|
|
148
|
+
- Inject the planning prompt.
|
|
149
|
+
- Ask Claude to create a machine-parseable Markdown plan using the 5-state checkbox system.
|
|
150
|
+
|
|
151
|
+
### `claude-orchestrator run`
|
|
152
|
+
|
|
153
|
+
Execute one selected task from a plan.
|
|
154
|
+
|
|
155
|
+
Default behavior:
|
|
156
|
+
|
|
157
|
+
- Validate config and plan.
|
|
158
|
+
- Run Claude binary/auth preflight.
|
|
159
|
+
- Select the next executable task.
|
|
160
|
+
- Create or reuse a per-task worktree.
|
|
161
|
+
- Run Claude headlessly in JSON mode.
|
|
162
|
+
- Enforce `taskTimeoutMs`.
|
|
163
|
+
- Parse the sentinel from Claude's JSON `result`.
|
|
164
|
+
- Run verification commands only after a `SUCCESS` sentinel.
|
|
165
|
+
- Mark `DONE` and commit only after verification passes.
|
|
166
|
+
- Stop after one task.
|
|
167
|
+
|
|
168
|
+
### `claude-orchestrator run --loop`
|
|
169
|
+
|
|
170
|
+
Run multiple tasks in sequence.
|
|
171
|
+
|
|
172
|
+
Loop mode must be explicit. It should still stop on:
|
|
173
|
+
|
|
174
|
+
- verification failure after retry cap
|
|
175
|
+
- `BLOCKED`
|
|
176
|
+
- session-limit pause
|
|
177
|
+
- timeout
|
|
178
|
+
- dirty worktree requiring user choice
|
|
179
|
+
- invalid plan/config
|
|
180
|
+
|
|
181
|
+
### `claude-orchestrator run --dry-run`
|
|
182
|
+
|
|
183
|
+
Preview the next execution without mutating files.
|
|
184
|
+
|
|
185
|
+
Shows:
|
|
186
|
+
|
|
187
|
+
- selected plan
|
|
188
|
+
- selected task
|
|
189
|
+
- stable task ID
|
|
190
|
+
- intended worktree
|
|
191
|
+
- Claude command shape
|
|
192
|
+
- verification commands
|
|
193
|
+
- timeout settings
|
|
194
|
+
- log and state paths
|
|
195
|
+
|
|
196
|
+
Does not:
|
|
197
|
+
|
|
198
|
+
- spawn Claude
|
|
199
|
+
- edit Markdown
|
|
200
|
+
- create commits
|
|
201
|
+
- create branches
|
|
202
|
+
- run verification commands
|
|
203
|
+
|
|
204
|
+
### `claude-orchestrator status`
|
|
205
|
+
|
|
206
|
+
Show current orchestration state.
|
|
207
|
+
|
|
208
|
+
Expected output:
|
|
209
|
+
|
|
210
|
+
- selected/current plan
|
|
211
|
+
- task counts by status
|
|
212
|
+
- active or next task
|
|
213
|
+
- current worktree state
|
|
214
|
+
- last Claude `session_id`
|
|
215
|
+
- last result
|
|
216
|
+
- retry count
|
|
217
|
+
- session-limit pause details if present
|
|
218
|
+
- resume command
|
|
219
|
+
|
|
220
|
+
### `claude-orchestrator logs`
|
|
221
|
+
|
|
222
|
+
Show paths to relevant logs without dumping full verbose output by default.
|
|
223
|
+
|
|
224
|
+
Logs include:
|
|
225
|
+
|
|
226
|
+
- raw Claude JSON response
|
|
227
|
+
- Claude stderr
|
|
228
|
+
- verification stdout/stderr
|
|
229
|
+
- task state snapshots
|
|
230
|
+
- command policy decisions
|
|
231
|
+
- summary reports
|
|
232
|
+
|
|
233
|
+
### Resuming interrupted work
|
|
234
|
+
|
|
235
|
+
There is no separate `resume` command. An interrupted, `IN_PROGRESS`, or `BLOCKED: SESSION_LIMIT` task is just state on disk, so re-running `claude-orchestrator run --plan <path>` picks it back up automatically:
|
|
236
|
+
|
|
237
|
+
- Re-validates config and plan.
|
|
238
|
+
- Detects dirty task worktree state and prompts whether to continue in the existing worktree, retry from a clean base, or halt.
|
|
239
|
+
- Preserves previous logs and retry context.
|
|
240
|
+
- Does not consume retry budget for session-limit pauses.
|
|
241
|
+
|
|
242
|
+
## Configuration
|
|
243
|
+
|
|
244
|
+
Configuration lives in `.claude-orchestrator.json` at the project root.
|
|
245
|
+
|
|
246
|
+
Top-level config areas:
|
|
247
|
+
|
|
248
|
+
- `version`: config schema version.
|
|
249
|
+
- `planDir`: project-relative plan directory.
|
|
250
|
+
- `baseBranch`: base branch for task worktrees.
|
|
251
|
+
- `branchPrefix`: branch name prefix for orchestrator work.
|
|
252
|
+
- `models`: planning/execution model defaults.
|
|
253
|
+
- `claude`: Claude binary, permission mode, allowed tools, and safe extra args.
|
|
254
|
+
- `taskTimeoutMs`: per-task Claude wall-clock timeout.
|
|
255
|
+
- `verificationCommands`: ordered structured verification commands.
|
|
256
|
+
- `maxRetries`: bounded retry cap.
|
|
257
|
+
- `logsDir`: log directory.
|
|
258
|
+
- `stateDir`: state directory.
|
|
259
|
+
- `worktreeDir`: per-task worktree directory.
|
|
260
|
+
- `commitMessageTemplate`: verified task commit template.
|
|
261
|
+
- `sessionLimits`: Claude usage-limit behavior.
|
|
262
|
+
- `security`: command allowlist, denylist, protected paths, and network policy.
|
|
263
|
+
|
|
264
|
+
### Verification Commands
|
|
265
|
+
|
|
266
|
+
Verification commands must be structured objects, not shell strings.
|
|
267
|
+
|
|
268
|
+
Required fields:
|
|
269
|
+
|
|
270
|
+
- `command`: executable name or absolute path.
|
|
271
|
+
- `args`: literal argument array.
|
|
272
|
+
- `timeoutMs`: finite timeout.
|
|
273
|
+
|
|
274
|
+
Optional fields:
|
|
275
|
+
|
|
276
|
+
- `name`: display name.
|
|
277
|
+
- `cwd`: project-relative working directory that must resolve inside the task worktree at execution time.
|
|
278
|
+
- `env`: explicit environment key/value overrides.
|
|
279
|
+
- `allowFailure`: advisory command flag, default false.
|
|
280
|
+
|
|
281
|
+
Commands run with `shell: false`. Shell chains, redirection, command substitution, and interpolated environment expressions are not part of the MVP command model.
|
|
282
|
+
|
|
283
|
+
## Safety Model
|
|
284
|
+
|
|
285
|
+
The MVP safety model relies on real execution boundaries, not only prompt instructions.
|
|
286
|
+
|
|
287
|
+
- Each task runs in an isolated Git worktree or equivalent sandbox.
|
|
288
|
+
- Claude execution uses validated permission settings.
|
|
289
|
+
- `--dangerously-skip-permissions` is not allowed for normal execution.
|
|
290
|
+
- Protected path patterns are checked before and after execution.
|
|
291
|
+
- Destructive Git operations are blocked.
|
|
292
|
+
- Claude task sessions do not commit, amend, push, force-push, reset, clean, rebase, delete branches, or rewrite history.
|
|
293
|
+
- The orchestrator does not auto-push in the MVP.
|
|
294
|
+
- Verification commands are structured and policy-checked.
|
|
295
|
+
- Secret-looking values are redacted from logs.
|
|
296
|
+
|
|
297
|
+
Prompt instructions still tell Claude not to touch secrets or run destructive Git, but prompt text is treated as advisory unless backed by a technical boundary.
|
|
298
|
+
|
|
299
|
+
## Timeouts, Retries & Resume
|
|
300
|
+
|
|
301
|
+
Every Claude task execution has a finite wall-clock timeout. Timeout behavior:
|
|
302
|
+
|
|
303
|
+
- Kill the Claude child process.
|
|
304
|
+
- Preserve logs.
|
|
305
|
+
- Preserve the task worktree for inspection.
|
|
306
|
+
- Mark or report the task according to tested executor rules.
|
|
307
|
+
- Do not commit.
|
|
308
|
+
|
|
309
|
+
Retry behavior:
|
|
310
|
+
|
|
311
|
+
- Verification or implementation failures increment retry count.
|
|
312
|
+
- Retry prompts include concise, redacted prior failure context.
|
|
313
|
+
- Session-limit pauses do not consume retry budget.
|
|
314
|
+
- Retries stop at the configured cap and hard maximum.
|
|
315
|
+
|
|
316
|
+
Resume behavior:
|
|
317
|
+
|
|
318
|
+
- Interrupted work remains inspectable in its task worktree.
|
|
319
|
+
- On resume, dirty worktree state is detected.
|
|
320
|
+
- The user chooses whether to continue, retry cleanly, or halt.
|
|
321
|
+
|
|
322
|
+
## Session Limits
|
|
323
|
+
|
|
324
|
+
The orchestrator should show Claude session-limit information when Claude exposes it through stable output/status.
|
|
325
|
+
|
|
326
|
+
If remaining usage or reset time is available, it appears in pre-run and summary output. If unavailable, the CLI says `unknown` rather than guessing.
|
|
327
|
+
|
|
328
|
+
If Claude stops because of a session limit:
|
|
329
|
+
|
|
330
|
+
- the task is paused as resumable work
|
|
331
|
+
- normal retry count is not incremented
|
|
332
|
+
- reset time is saved when known
|
|
333
|
+
- the terminal shows the resume command
|
|
334
|
+
- no commit is created
|
|
335
|
+
|
|
336
|
+
Automatic resume after reset is post-MVP unless an explicit automation feature is added later.
|
|
337
|
+
|
|
338
|
+
## Logs & Completion Overview
|
|
339
|
+
|
|
340
|
+
Terminal output should stay concise. Full details go to files under `logsDir`.
|
|
341
|
+
|
|
342
|
+
On task completion, the terminal should show:
|
|
343
|
+
|
|
344
|
+
- task result
|
|
345
|
+
- verification result
|
|
346
|
+
- retry count
|
|
347
|
+
- commit hash when created
|
|
348
|
+
- log path
|
|
349
|
+
- next action
|
|
350
|
+
|
|
351
|
+
When all tasks are `DONE`, the orchestrator should close cleanly and show:
|
|
352
|
+
|
|
353
|
+
- plan path
|
|
354
|
+
- final status
|
|
355
|
+
- completed, failed, and blocked counts
|
|
356
|
+
- commits created
|
|
357
|
+
- verification status
|
|
358
|
+
- retry summary
|
|
359
|
+
- estimated cost/usage from Claude JSON when available
|
|
360
|
+
- detailed log directory
|
|
361
|
+
|
|
362
|
+
## Post-MVP
|
|
363
|
+
|
|
364
|
+
These features are explicitly post-MVP:
|
|
365
|
+
|
|
366
|
+
- desktop notifications
|
|
367
|
+
- webhooks
|
|
368
|
+
- automatic resume after session reset
|
|
369
|
+
- network-aware verification policy
|
|
370
|
+
- dangerous override modes
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
export const program = new Command();
|
|
3
|
+
program
|
|
4
|
+
.name('claude-orchestrator')
|
|
5
|
+
.description('Stateful Workflow Engine on top of Claude Code')
|
|
6
|
+
.version('0.1.0')
|
|
7
|
+
.option('-c, --config <path>', 'path to config file')
|
|
8
|
+
.option('-v, --verbose', 'enable verbose logging');
|
|
9
|
+
import { runInitCommand } from './commands/init.js';
|
|
10
|
+
program
|
|
11
|
+
.command('init')
|
|
12
|
+
.description('create or update .claude-orchestrator.json interactively')
|
|
13
|
+
.action(async () => {
|
|
14
|
+
const parentOpts = program.opts();
|
|
15
|
+
await runInitCommand({ config: parentOpts.config });
|
|
16
|
+
});
|
|
17
|
+
import { runDoctorCommand } from './commands/doctor.js';
|
|
18
|
+
import { runValidateCommand } from './commands/validate.js';
|
|
19
|
+
program
|
|
20
|
+
.command('doctor')
|
|
21
|
+
.description('check environment, config, Git, and Claude binary')
|
|
22
|
+
.action(async () => {
|
|
23
|
+
const parentOpts = program.opts();
|
|
24
|
+
await runDoctorCommand({ config: parentOpts.config });
|
|
25
|
+
});
|
|
26
|
+
program
|
|
27
|
+
.command('validate')
|
|
28
|
+
.description('validate config and plan files without executing Claude')
|
|
29
|
+
.option('--plan <path>', 'path to a specific plan file')
|
|
30
|
+
.action(async (options) => {
|
|
31
|
+
const parentOpts = program.opts();
|
|
32
|
+
await runValidateCommand({ ...options, config: parentOpts.config });
|
|
33
|
+
});
|
|
34
|
+
import { runPlanCommand } from './commands/plan.js';
|
|
35
|
+
import { runCommand } from './commands/run.js';
|
|
36
|
+
program
|
|
37
|
+
.command('plan')
|
|
38
|
+
.description('run interactive planning')
|
|
39
|
+
.option('--plan <path>', 'path to a specific plan file')
|
|
40
|
+
.action(async (options) => {
|
|
41
|
+
const parentOpts = program.opts();
|
|
42
|
+
await runPlanCommand({ ...options, config: parentOpts.config });
|
|
43
|
+
});
|
|
44
|
+
program
|
|
45
|
+
.command('run')
|
|
46
|
+
.description('execute one selected task by default')
|
|
47
|
+
.option('--plan <path>', 'path to a specific plan file')
|
|
48
|
+
.option('--task <id>', 'stable ID of a specific task to run')
|
|
49
|
+
.option('--loop', 'optional explicit loop mode; never implicit')
|
|
50
|
+
.option('--yes', 'auto-confirm prompts (e.g. commit with no verification configured) for unattended runs')
|
|
51
|
+
.option('--dry-run', 'show intended execution without mutating state')
|
|
52
|
+
.action(async (options) => {
|
|
53
|
+
const parentOpts = program.opts();
|
|
54
|
+
await runCommand({ ...options, config: parentOpts.config });
|
|
55
|
+
});
|
|
56
|
+
import { runStatusCommand } from './commands/status.js';
|
|
57
|
+
import { runLogsCommand } from './commands/logs.js';
|
|
58
|
+
program
|
|
59
|
+
.command('status')
|
|
60
|
+
.description('show selected plan state, active task, and resume command')
|
|
61
|
+
.option('--plan <path>', 'path to a specific plan file')
|
|
62
|
+
.option('--task <id>', 'stable ID of a specific task')
|
|
63
|
+
.action(async (options) => {
|
|
64
|
+
const parentOpts = program.opts();
|
|
65
|
+
await runStatusCommand({ ...options, config: parentOpts.config });
|
|
66
|
+
});
|
|
67
|
+
program
|
|
68
|
+
.command('logs')
|
|
69
|
+
.description('show paths to relevant logs')
|
|
70
|
+
.option('--plan <path>', 'path to a specific plan file')
|
|
71
|
+
.option('--task <id>', 'stable ID of a specific task')
|
|
72
|
+
.action(async (options) => {
|
|
73
|
+
const parentOpts = program.opts();
|
|
74
|
+
await runLogsCommand({ ...options, config: parentOpts.config });
|
|
75
|
+
});
|
|
76
|
+
const isTest = process.env.NODE_ENV === 'test' || process.env.VITEST;
|
|
77
|
+
if (!isTest) {
|
|
78
|
+
program.parse(process.argv);
|
|
79
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { execa } from 'execa';
|
|
2
|
+
import * as p from '@clack/prompts';
|
|
3
|
+
import pc from 'picocolors';
|
|
4
|
+
import { loadConfig } from '../config/loader.js';
|
|
5
|
+
import { isGitRepository } from '../git/repo.js';
|
|
6
|
+
export async function runDoctorCommand(options) {
|
|
7
|
+
p.intro(pc.bgBlue(pc.white(' Claude Orchestrator: Doctor ')));
|
|
8
|
+
let ok = true;
|
|
9
|
+
const isGit = await isGitRepository();
|
|
10
|
+
if (isGit) {
|
|
11
|
+
p.log.success(pc.green('Git repository detected.'));
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
ok = false;
|
|
15
|
+
p.log.error(pc.red('Not a Git repository. Run "git init" or "claude-orchestrator run" to initialize one.'));
|
|
16
|
+
}
|
|
17
|
+
let config;
|
|
18
|
+
try {
|
|
19
|
+
config = await loadConfig(options.config);
|
|
20
|
+
p.log.success(pc.green('Config loaded and valid.'));
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
ok = false;
|
|
24
|
+
p.log.error(pc.red(`Config invalid: ${error instanceof Error ? error.message : String(error)}`));
|
|
25
|
+
}
|
|
26
|
+
if (config) {
|
|
27
|
+
try {
|
|
28
|
+
await execa(config.claude.binary, ['--version'], { timeout: 5000 });
|
|
29
|
+
p.log.success(pc.green(`Claude binary "${config.claude.binary}" is executable.`));
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
ok = false;
|
|
33
|
+
p.log.error(pc.red(`Claude binary "${config.claude.binary}" is not runnable: ${error instanceof Error ? error.message : String(error)}`));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (ok) {
|
|
37
|
+
p.outro(pc.green('All checks passed.'));
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
p.outro(pc.red('One or more checks failed.'));
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as fs from 'fs/promises';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as p from '@clack/prompts';
|
|
4
|
+
import pc from 'picocolors';
|
|
5
|
+
import { defaultConfig } from '../config/defaults.js';
|
|
6
|
+
export async function runInitCommand(options) {
|
|
7
|
+
p.intro(pc.bgBlue(pc.white(' Claude Orchestrator: Init ')));
|
|
8
|
+
const configPath = path.resolve(process.cwd(), options.config || '.claude-orchestrator.json');
|
|
9
|
+
const configExists = await fs
|
|
10
|
+
.access(configPath)
|
|
11
|
+
.then(() => true)
|
|
12
|
+
.catch(() => false);
|
|
13
|
+
if (configExists) {
|
|
14
|
+
p.log.warn(`Config already exists at ${pc.cyan(configPath)}. Leaving it untouched.`);
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
const planDirResult = await p.text({
|
|
18
|
+
message: 'Where should plan files be stored?',
|
|
19
|
+
initialValue: defaultConfig.planDir,
|
|
20
|
+
});
|
|
21
|
+
if (p.isCancel(planDirResult)) {
|
|
22
|
+
p.cancel('Init cancelled.');
|
|
23
|
+
process.exit(0);
|
|
24
|
+
}
|
|
25
|
+
const config = { ...defaultConfig, planDir: planDirResult };
|
|
26
|
+
await fs.writeFile(configPath, JSON.stringify(config, null, 2) + '\n', 'utf8');
|
|
27
|
+
p.log.success(`Created config at ${pc.cyan(configPath)}.`);
|
|
28
|
+
}
|
|
29
|
+
const config = configExists
|
|
30
|
+
? JSON.parse(await fs.readFile(configPath, 'utf8'))
|
|
31
|
+
: { planDir: defaultConfig.planDir };
|
|
32
|
+
const resolvedPlanDir = path.resolve(process.cwd(), config.planDir || defaultConfig.planDir);
|
|
33
|
+
await fs.mkdir(resolvedPlanDir, { recursive: true });
|
|
34
|
+
p.log.success(`Plan directory ready at ${pc.cyan(resolvedPlanDir)}.`);
|
|
35
|
+
p.outro(pc.green(`Run ${pc.cyan('claude-orchestrator plan')} to create your first plan.`));
|
|
36
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as p from '@clack/prompts';
|
|
4
|
+
import pc from 'picocolors';
|
|
5
|
+
import { loadConfig } from '../config/loader.js';
|
|
6
|
+
import { discoverPlan } from '../plans/discovery.js';
|
|
7
|
+
export async function runLogsCommand(options) {
|
|
8
|
+
p.intro(pc.bgBlue(pc.white(' Claude Orchestrator: Logs ')));
|
|
9
|
+
const config = await loadConfig(options.config);
|
|
10
|
+
let planPath = options.plan;
|
|
11
|
+
if (!planPath) {
|
|
12
|
+
const defaultPlanDir = config.planDir || '.claude-orchestrator/plans';
|
|
13
|
+
planPath = (await discoverPlan({ planDir: defaultPlanDir })) || undefined;
|
|
14
|
+
if (!planPath) {
|
|
15
|
+
process.exit(0);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
const planId = path.basename(planPath, path.extname(planPath));
|
|
19
|
+
const logsDir = config.logsDir || '.claude-orchestrator/logs';
|
|
20
|
+
const stateDir = config.stateDir || '.claude-orchestrator/state';
|
|
21
|
+
const planLogDir = path.join(logsDir, planId);
|
|
22
|
+
const stateFile = path.join(stateDir, `${planId}.json`);
|
|
23
|
+
p.log.info(pc.blue('--- Log Locations ---'));
|
|
24
|
+
p.log.info(`State File: ${stateFile}${fs.existsSync(stateFile) ? '' : pc.yellow(' (not found)')}`);
|
|
25
|
+
if (options.task) {
|
|
26
|
+
const taskLogDir = path.join(planLogDir, options.task);
|
|
27
|
+
p.log.info(`Task Log Dir: ${taskLogDir}${fs.existsSync(taskLogDir) ? '' : pc.yellow(' (not found)')}`);
|
|
28
|
+
if (fs.existsSync(taskLogDir)) {
|
|
29
|
+
for (const file of fs.readdirSync(taskLogDir)) {
|
|
30
|
+
p.log.info(` - ${path.join(taskLogDir, file)}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
p.log.info(`Plan Log Dir: ${planLogDir}${fs.existsSync(planLogDir) ? '' : pc.yellow(' (not found)')}`);
|
|
36
|
+
if (fs.existsSync(planLogDir)) {
|
|
37
|
+
for (const taskId of fs.readdirSync(planLogDir)) {
|
|
38
|
+
p.log.info(` - ${path.join(planLogDir, taskId)}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
p.outro(pc.green('Done.'));
|
|
43
|
+
}
|