@markjaquith/agency 2.12.0 → 2.14.0
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 +46 -7
- package/cli.ts +72 -3
- package/package.json +1 -1
- package/schemas/agency-graph-v1.schema.json +28 -1
- package/skills/agency/SKILL.md +14 -3
- package/src/cli-parser.test.ts +97 -0
- package/src/cli-parser.ts +100 -4
- package/src/cli.test.ts +155 -0
- package/src/commands/claim.ts +97 -0
- package/src/commands/phase.ts +1 -1
- package/src/commands/task-phase.test.ts +4 -4
- package/src/commands/task.ts +1 -1
- package/src/commands/work.test.ts +65 -1
- package/src/commands/work.ts +94 -5
- package/src/protocol.test.ts +25 -0
- package/src/protocol.ts +16 -0
- package/src/services/ClaimService.test.ts +270 -0
- package/src/services/ClaimService.ts +446 -0
- package/src/services/PhaseService.ts +13 -0
- package/src/services/TaskPhaseService.test.ts +10 -9
- package/src/services/TaskService.ts +11 -0
- package/src/services/WorktreeService.test.ts +82 -7
- package/src/services/WorktreeService.ts +199 -41
- package/src/test-utils.ts +2 -0
- package/src/utils/command.ts +1 -0
- package/src/workbase/AGENTS.md +2 -2
- package/src/workbase/document-revision.ts +2 -0
- package/src/workbase/frontmatter.ts +59 -53
- package/src/workbase/schemas.test.ts +27 -0
- package/src/workbase/schemas.ts +21 -0
package/README.md
CHANGED
|
@@ -357,7 +357,7 @@ Inspect tasks:
|
|
|
357
357
|
```text
|
|
358
358
|
agency task list [--json]
|
|
359
359
|
agency task show <id> [--json]
|
|
360
|
-
agency task status <id> <open|
|
|
360
|
+
agency task status <id> <open|done|dropped> [--json]
|
|
361
361
|
```
|
|
362
362
|
|
|
363
363
|
To add a phase to an existing single-phase task, name the phase that will own
|
|
@@ -384,16 +384,48 @@ agency phase create <task-id> <phase-id>
|
|
|
384
384
|
|
|
385
385
|
agency phase list <task-id> [--json]
|
|
386
386
|
agency phase show <task-id> <phase-id> [--json]
|
|
387
|
-
agency phase status <task-id> <phase-id> <open|
|
|
387
|
+
agency phase status <task-id> <phase-id> <open|done|dropped> [--json]
|
|
388
388
|
```
|
|
389
389
|
|
|
390
390
|
Single-phase tasks and phases store status in YAML. New execution units start
|
|
391
391
|
`open`, and `agency work` marks the selected execution unit `working` immediately
|
|
392
|
-
before launch. Use
|
|
393
|
-
|
|
394
|
-
markers before execution units.
|
|
395
|
-
to
|
|
396
|
-
or transition to open; reopen terminal
|
|
392
|
+
before launch. Use claims for coordinated ownership and the status subcommands
|
|
393
|
+
for manual lifecycle overrides. The interactive work selector displays status
|
|
394
|
+
markers before execution units. Existing working and delegated work may be
|
|
395
|
+
released to `open` or assigned a terminal outcome. Done and dropped work are
|
|
396
|
+
terminal and may only remain unchanged or transition to open; reopen terminal
|
|
397
|
+
work before changing its outcome.
|
|
398
|
+
|
|
399
|
+
`delegated` remains readable for existing workbases but cannot be newly assigned.
|
|
400
|
+
Delegation is now explicit: the claimant identifies the orchestrator and the
|
|
401
|
+
runner identifies the assigned agent.
|
|
402
|
+
|
|
403
|
+
### Claims
|
|
404
|
+
|
|
405
|
+
Claim mutations require the SHA-256 revision exposed by `agency context` or
|
|
406
|
+
`agency graph`. Every operation compares that revision while holding an exclusive
|
|
407
|
+
document lock and atomically replaces the execution document.
|
|
408
|
+
|
|
409
|
+
```text
|
|
410
|
+
agency claim <task-id> [phase-id] --claimant <id> --runner <id>
|
|
411
|
+
--session-id <id> --revision <sha256> [--expires-at <timestamp>] [--json]
|
|
412
|
+
agency release <task-id> [phase-id] --session-id <id>
|
|
413
|
+
--revision <sha256> [--json]
|
|
414
|
+
agency finish <task-id> [phase-id] --session-id <id>
|
|
415
|
+
--revision <sha256> --outcome <done|dropped> [--json]
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
An active claim sets status to `working`. Release returns it to `open`; finish
|
|
419
|
+
sets the terminal outcome. Released and finished ownership metadata remains in
|
|
420
|
+
frontmatter. Conflicts return the current revision and complete ownership record
|
|
421
|
+
in the machine error envelope rather than overwriting it. Expired claims may be
|
|
422
|
+
replaced with a revision-guarded claim.
|
|
423
|
+
|
|
424
|
+
`agency work` claims an execution unit before launching its agent. Set
|
|
425
|
+
`AGENCY_CLAIMANT`, `AGENCY_RUNNER`, or `AGENCY_SESSION_ID` to supply orchestrator
|
|
426
|
+
identities; otherwise Agency derives them from the user, selected agent, and
|
|
427
|
+
process. The launched agent receives `AGENCY_SESSION_ID` and
|
|
428
|
+
`AGENCY_CLAIM_REVISION` for a later release or finish operation.
|
|
397
429
|
|
|
398
430
|
### Archive
|
|
399
431
|
|
|
@@ -412,6 +444,7 @@ before moving files, refuses dirty worktrees, and preserves branches.
|
|
|
412
444
|
|
|
413
445
|
```text
|
|
414
446
|
agency work [<directory> | --epic <epic-id>] [--opencode | --claude]
|
|
447
|
+
agency work prepare [target] [--dry-run] [--json]
|
|
415
448
|
agency pr create <task-id> [phase-id] [--draft] [--json]
|
|
416
449
|
```
|
|
417
450
|
|
|
@@ -421,6 +454,12 @@ workbase, Agency first presents the registered workbases, then the selected
|
|
|
421
454
|
workbase's hierarchy. If `fzf` is not installed, Agency prints the available
|
|
422
455
|
choices and asks for an explicit directory.
|
|
423
456
|
|
|
457
|
+
`agency work prepare` resolves an execution unit and creates or reuses its
|
|
458
|
+
writable and reference worktrees without launching an agent or changing status.
|
|
459
|
+
Its JSON result includes document and checkout paths, resolved commits, actions,
|
|
460
|
+
and Git operations. Use `--dry-run` to report planned fetch, branch, and worktree
|
|
461
|
+
changes without applying them.
|
|
462
|
+
|
|
424
463
|
Epic and multi-phase task targets launch orchestration agents beside their
|
|
425
464
|
documents. Single-phase tasks and phases fetch repositories, create or reuse
|
|
426
465
|
worktrees under `code/`, and launch an execution agent in the writable checkout
|
package/cli.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { parseCli } from "./src/cli-parser"
|
|
|
5
5
|
import { init, help as initHelp } from "./src/commands/init"
|
|
6
6
|
import { task, help as taskHelp } from "./src/commands/task"
|
|
7
7
|
import { pr, help as prHelp } from "./src/commands/pr"
|
|
8
|
-
import { work, help as workHelp } from "./src/commands/work"
|
|
8
|
+
import { work, workPrepare, help as workHelp } from "./src/commands/work"
|
|
9
9
|
import { status, help as statusHelp } from "./src/commands/status"
|
|
10
10
|
import { validate, help as validateHelp } from "./src/commands/validate"
|
|
11
11
|
import { context, help as contextHelp } from "./src/commands/context"
|
|
@@ -32,6 +32,13 @@ import { ArchiveService } from "./src/services/ArchiveService"
|
|
|
32
32
|
import { IntegrationService } from "./src/services/IntegrationService"
|
|
33
33
|
import { ContextService } from "./src/services/ContextService"
|
|
34
34
|
import { GraphService } from "./src/services/GraphService"
|
|
35
|
+
import { ClaimService } from "./src/services/ClaimService"
|
|
36
|
+
import {
|
|
37
|
+
claimCommand,
|
|
38
|
+
claimHelp,
|
|
39
|
+
releaseHelp,
|
|
40
|
+
finishHelp,
|
|
41
|
+
} from "./src/commands/claim"
|
|
35
42
|
import {
|
|
36
43
|
collectCommandResult,
|
|
37
44
|
errorEnvelope,
|
|
@@ -53,6 +60,7 @@ const CliLayer = Layer.mergeAll(
|
|
|
53
60
|
IntegrationService.Default,
|
|
54
61
|
ContextService.Default,
|
|
55
62
|
GraphService.Default,
|
|
63
|
+
ClaimService.Default,
|
|
56
64
|
)
|
|
57
65
|
|
|
58
66
|
/**
|
|
@@ -99,6 +107,61 @@ const VERSION = packageJson.version
|
|
|
99
107
|
|
|
100
108
|
// Define commands
|
|
101
109
|
const commands: Record<string, Command> = {
|
|
110
|
+
claim: {
|
|
111
|
+
run: async (args: string[], options: Record<string, any>) => {
|
|
112
|
+
if (options.help) return console.log(claimHelp)
|
|
113
|
+
await runCommand(
|
|
114
|
+
claimCommand({
|
|
115
|
+
operation: "claim",
|
|
116
|
+
taskId: args[0],
|
|
117
|
+
phaseId: args[1],
|
|
118
|
+
claimant: options.claimant,
|
|
119
|
+
runner: options.runner,
|
|
120
|
+
sessionId: options["session-id"],
|
|
121
|
+
revision: options.revision,
|
|
122
|
+
expiresAt: options["expires-at"],
|
|
123
|
+
json: options.json,
|
|
124
|
+
silent: options.silent,
|
|
125
|
+
verbose: options.verbose,
|
|
126
|
+
}),
|
|
127
|
+
)
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
release: {
|
|
131
|
+
run: async (args: string[], options: Record<string, any>) => {
|
|
132
|
+
if (options.help) return console.log(releaseHelp)
|
|
133
|
+
await runCommand(
|
|
134
|
+
claimCommand({
|
|
135
|
+
operation: "release",
|
|
136
|
+
taskId: args[0],
|
|
137
|
+
phaseId: args[1],
|
|
138
|
+
sessionId: options["session-id"],
|
|
139
|
+
revision: options.revision,
|
|
140
|
+
json: options.json,
|
|
141
|
+
silent: options.silent,
|
|
142
|
+
verbose: options.verbose,
|
|
143
|
+
}),
|
|
144
|
+
)
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
finish: {
|
|
148
|
+
run: async (args: string[], options: Record<string, any>) => {
|
|
149
|
+
if (options.help) return console.log(finishHelp)
|
|
150
|
+
await runCommand(
|
|
151
|
+
claimCommand({
|
|
152
|
+
operation: "finish",
|
|
153
|
+
taskId: args[0],
|
|
154
|
+
phaseId: args[1],
|
|
155
|
+
sessionId: options["session-id"],
|
|
156
|
+
revision: options.revision,
|
|
157
|
+
outcome: options.outcome,
|
|
158
|
+
json: options.json,
|
|
159
|
+
silent: options.silent,
|
|
160
|
+
verbose: options.verbose,
|
|
161
|
+
}),
|
|
162
|
+
)
|
|
163
|
+
},
|
|
164
|
+
},
|
|
102
165
|
init: {
|
|
103
166
|
run: async (args: string[], options: Record<string, any>) => {
|
|
104
167
|
if (options.help) {
|
|
@@ -274,10 +337,13 @@ const commands: Record<string, Command> = {
|
|
|
274
337
|
console.log(workHelp)
|
|
275
338
|
return
|
|
276
339
|
}
|
|
340
|
+
const preparing = args[0] === "prepare"
|
|
277
341
|
await runCommand(
|
|
278
|
-
work({
|
|
279
|
-
directory: args[0],
|
|
342
|
+
(preparing ? workPrepare : work)({
|
|
343
|
+
directory: args[preparing ? 1 : 0],
|
|
280
344
|
epicId: options.epic,
|
|
345
|
+
json: options.json,
|
|
346
|
+
dryRun: options["dry-run"],
|
|
281
347
|
silent: options.silent,
|
|
282
348
|
verbose: options.verbose,
|
|
283
349
|
opencode: options.opencode,
|
|
@@ -372,6 +438,9 @@ Commands:
|
|
|
372
438
|
integration <command> Inspect or sync managed integration files
|
|
373
439
|
epic <subcommand> Manage epics
|
|
374
440
|
phase <subcommand> Manage task phases
|
|
441
|
+
claim <task> [phase] Claim an execution unit
|
|
442
|
+
release <task> [phase] Release an execution unit
|
|
443
|
+
finish <task> [phase] Finish an execution unit
|
|
375
444
|
archive <type> Archive a work item
|
|
376
445
|
task <subcommand> Manage tasks
|
|
377
446
|
work [directory|task] Work on an epic, task, or phase
|
package/package.json
CHANGED
|
@@ -339,6 +339,7 @@
|
|
|
339
339
|
"base": { "type": "string" },
|
|
340
340
|
"pr": { "type": ["string", "null"] },
|
|
341
341
|
"status": { "$ref": "#/$defs/status" },
|
|
342
|
+
"claim": { "$ref": "#/$defs/claim" },
|
|
342
343
|
"sha256": { "type": "string" }
|
|
343
344
|
}
|
|
344
345
|
},
|
|
@@ -372,6 +373,7 @@
|
|
|
372
373
|
"base": { "type": "string" },
|
|
373
374
|
"pr": { "type": ["string", "null"] },
|
|
374
375
|
"status": { "$ref": "#/$defs/status" },
|
|
376
|
+
"claim": { "$ref": "#/$defs/claim" },
|
|
375
377
|
"sha256": { "type": "string" }
|
|
376
378
|
}
|
|
377
379
|
},
|
|
@@ -393,7 +395,32 @@
|
|
|
393
395
|
"branch": { "type": "string" },
|
|
394
396
|
"base": { "type": "string" },
|
|
395
397
|
"pr": { "type": ["string", "null"] },
|
|
396
|
-
"status": { "$ref": "#/$defs/status" }
|
|
398
|
+
"status": { "$ref": "#/$defs/status" },
|
|
399
|
+
"claim": { "$ref": "#/$defs/claim" }
|
|
400
|
+
}
|
|
401
|
+
},
|
|
402
|
+
"claim": {
|
|
403
|
+
"type": "object",
|
|
404
|
+
"additionalProperties": false,
|
|
405
|
+
"required": [
|
|
406
|
+
"claimant",
|
|
407
|
+
"runner",
|
|
408
|
+
"sessionId",
|
|
409
|
+
"startedAt",
|
|
410
|
+
"targetRevision",
|
|
411
|
+
"state"
|
|
412
|
+
],
|
|
413
|
+
"properties": {
|
|
414
|
+
"claimant": { "type": "string", "minLength": 1 },
|
|
415
|
+
"runner": { "type": "string", "minLength": 1 },
|
|
416
|
+
"sessionId": { "type": "string", "minLength": 1 },
|
|
417
|
+
"startedAt": { "type": "string", "format": "date-time" },
|
|
418
|
+
"targetRevision": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
|
|
419
|
+
"expiresAt": { "type": "string", "format": "date-time" },
|
|
420
|
+
"state": { "enum": ["active", "released", "finished"] },
|
|
421
|
+
"releasedAt": { "type": "string", "format": "date-time" },
|
|
422
|
+
"finishedAt": { "type": "string", "format": "date-time" },
|
|
423
|
+
"outcome": { "enum": ["done", "dropped"] }
|
|
397
424
|
}
|
|
398
425
|
},
|
|
399
426
|
"repositoryData": {
|
package/skills/agency/SKILL.md
CHANGED
|
@@ -213,7 +213,9 @@ details explicitly with `--include bodies|workspace|git|pr`. For large workbases
|
|
|
213
213
|
- Epic frontmatter owns the child-task dependency graph.
|
|
214
214
|
- `pr` is either a GitHub PR URL string or `null`.
|
|
215
215
|
- Execution-unit `status` is `open`, `working`, `delegated`, `done`, or `dropped`.
|
|
216
|
-
New work starts open, and
|
|
216
|
+
New work starts open, and an active claim sets it working before agent launch.
|
|
217
|
+
`delegated` is readable legacy state; distinct claimant and runner identities
|
|
218
|
+
now represent delegation.
|
|
217
219
|
- Keep directory IDs stable; encode sequencing with `dependsOn`, not numeric
|
|
218
220
|
directory prefixes.
|
|
219
221
|
- Do not use YAML duplicate keys, anchors, aliases, or custom tags.
|
|
@@ -224,8 +226,17 @@ prose, preserve backlinks and run validation immediately afterward.
|
|
|
224
226
|
Update execution status with:
|
|
225
227
|
|
|
226
228
|
```bash
|
|
227
|
-
agency task status <task-id> <open|
|
|
228
|
-
agency phase status <task-id> <phase-id> <open|
|
|
229
|
+
agency task status <task-id> <open|done|dropped>
|
|
230
|
+
agency phase status <task-id> <phase-id> <open|done|dropped>
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Coordinate execution ownership with a document revision from `agency context`
|
|
234
|
+
or `agency graph`:
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
agency claim <task-id> [phase-id] --claimant <id> --runner <id> --session-id <id> --revision <sha256>
|
|
238
|
+
agency release <task-id> [phase-id] --session-id <id> --revision <sha256>
|
|
239
|
+
agency finish <task-id> [phase-id] --session-id <id> --revision <sha256> --outcome <done|dropped>
|
|
229
240
|
```
|
|
230
241
|
|
|
231
242
|
## Archive Completed Work
|
package/src/cli-parser.test.ts
CHANGED
|
@@ -141,11 +141,92 @@ describe("strict CLI parsing", () => {
|
|
|
141
141
|
[["validate", "one", "two"], "agency validate"],
|
|
142
142
|
[["context", "one", "two"], "agency context"],
|
|
143
143
|
[["graph", "extra"], "agency graph"],
|
|
144
|
+
[
|
|
145
|
+
[
|
|
146
|
+
"claim",
|
|
147
|
+
"one",
|
|
148
|
+
"two",
|
|
149
|
+
"three",
|
|
150
|
+
"--claimant",
|
|
151
|
+
"a",
|
|
152
|
+
"--runner",
|
|
153
|
+
"r",
|
|
154
|
+
"--session-id",
|
|
155
|
+
"s",
|
|
156
|
+
"--revision",
|
|
157
|
+
"0".repeat(64),
|
|
158
|
+
],
|
|
159
|
+
"agency claim",
|
|
160
|
+
],
|
|
161
|
+
[
|
|
162
|
+
[
|
|
163
|
+
"release",
|
|
164
|
+
"one",
|
|
165
|
+
"two",
|
|
166
|
+
"three",
|
|
167
|
+
"--session-id",
|
|
168
|
+
"s",
|
|
169
|
+
"--revision",
|
|
170
|
+
"0".repeat(64),
|
|
171
|
+
],
|
|
172
|
+
"agency release",
|
|
173
|
+
],
|
|
174
|
+
[
|
|
175
|
+
[
|
|
176
|
+
"finish",
|
|
177
|
+
"one",
|
|
178
|
+
"two",
|
|
179
|
+
"three",
|
|
180
|
+
"--session-id",
|
|
181
|
+
"s",
|
|
182
|
+
"--revision",
|
|
183
|
+
"0".repeat(64),
|
|
184
|
+
"--outcome",
|
|
185
|
+
"done",
|
|
186
|
+
],
|
|
187
|
+
"agency finish",
|
|
188
|
+
],
|
|
144
189
|
] as const) {
|
|
145
190
|
expectUsageError([...args], usage)
|
|
146
191
|
}
|
|
147
192
|
})
|
|
148
193
|
|
|
194
|
+
test("validates revision-guarded claim operations", () => {
|
|
195
|
+
const revision = "0".repeat(64)
|
|
196
|
+
expect(
|
|
197
|
+
parseCli([
|
|
198
|
+
"claim",
|
|
199
|
+
"task",
|
|
200
|
+
"phase",
|
|
201
|
+
"--claimant",
|
|
202
|
+
"orchestrator",
|
|
203
|
+
"--runner",
|
|
204
|
+
"agent",
|
|
205
|
+
"--session-id",
|
|
206
|
+
"job-1",
|
|
207
|
+
"--revision",
|
|
208
|
+
revision,
|
|
209
|
+
"--expires-at",
|
|
210
|
+
"2026-07-17T13:00:00.000Z",
|
|
211
|
+
]),
|
|
212
|
+
).toMatchObject({ commandName: "claim", args: ["task", "phase"] })
|
|
213
|
+
expect(() =>
|
|
214
|
+
parseCli(["release", "task", "--session-id", "job-1"]),
|
|
215
|
+
).toThrow("--revision' is required")
|
|
216
|
+
expect(() =>
|
|
217
|
+
parseCli([
|
|
218
|
+
"finish",
|
|
219
|
+
"task",
|
|
220
|
+
"--session-id",
|
|
221
|
+
"job-1",
|
|
222
|
+
"--revision",
|
|
223
|
+
revision,
|
|
224
|
+
"--outcome",
|
|
225
|
+
"working",
|
|
226
|
+
]),
|
|
227
|
+
).toThrow("must be 'done' or 'dropped'")
|
|
228
|
+
})
|
|
229
|
+
|
|
149
230
|
test("accepts context projections and keeps compact command-local", () => {
|
|
150
231
|
expect(parseCli(["context", ".", "--json", "--compact"])).toMatchObject({
|
|
151
232
|
commandName: "context",
|
|
@@ -155,6 +236,22 @@ describe("strict CLI parsing", () => {
|
|
|
155
236
|
expectUsageError(["status", "--compact"], "agency status")
|
|
156
237
|
})
|
|
157
238
|
|
|
239
|
+
test("accepts work preparation options only for the prepare subcommand", () => {
|
|
240
|
+
expect(
|
|
241
|
+
parseCli(["work", "prepare", "tasks/example", "--dry-run", "--json"]),
|
|
242
|
+
).toMatchObject({
|
|
243
|
+
commandName: "work",
|
|
244
|
+
args: ["prepare", "tasks/example"],
|
|
245
|
+
values: { "dry-run": true, json: true },
|
|
246
|
+
})
|
|
247
|
+
expect(() => parseCli(["work", "example", "--dry-run"])).toThrow(
|
|
248
|
+
"only valid with 'agency work prepare'",
|
|
249
|
+
)
|
|
250
|
+
expect(() => parseCli(["work", "prepare", "--opencode"])).toThrow(
|
|
251
|
+
"cannot be combined",
|
|
252
|
+
)
|
|
253
|
+
})
|
|
254
|
+
|
|
158
255
|
test("accepts repeatable graph filters and rejects output conflicts", () => {
|
|
159
256
|
expect(
|
|
160
257
|
parseCli([
|
package/src/cli-parser.ts
CHANGED
|
@@ -59,6 +59,21 @@ const phaseCreateOptions = {
|
|
|
59
59
|
"first-phase": { type: "string" },
|
|
60
60
|
} satisfies OptionConfig
|
|
61
61
|
|
|
62
|
+
const claimOptions = {
|
|
63
|
+
...outputOptions,
|
|
64
|
+
claimant: { type: "string" },
|
|
65
|
+
runner: { type: "string" },
|
|
66
|
+
"session-id": { type: "string" },
|
|
67
|
+
revision: { type: "string" },
|
|
68
|
+
"expires-at": { type: "string" },
|
|
69
|
+
} satisfies OptionConfig
|
|
70
|
+
|
|
71
|
+
const ownedClaimOptions = {
|
|
72
|
+
...outputOptions,
|
|
73
|
+
"session-id": { type: "string" },
|
|
74
|
+
revision: { type: "string" },
|
|
75
|
+
} satisfies OptionConfig
|
|
76
|
+
|
|
62
77
|
const commands = {
|
|
63
78
|
init: {
|
|
64
79
|
usage: "agency init [path] [--json]",
|
|
@@ -258,6 +273,52 @@ const commands = {
|
|
|
258
273
|
},
|
|
259
274
|
},
|
|
260
275
|
},
|
|
276
|
+
claim: {
|
|
277
|
+
usage: "agency claim <task-id> [phase-id] [options]",
|
|
278
|
+
options: claimOptions,
|
|
279
|
+
command: {
|
|
280
|
+
usage:
|
|
281
|
+
"agency claim <task-id> [phase-id] --claimant <id> --runner <id> --session-id <id> --revision <sha256> [--expires-at <timestamp>] [--json]",
|
|
282
|
+
minArgs: 1,
|
|
283
|
+
maxArgs: 2,
|
|
284
|
+
options: [
|
|
285
|
+
"claimant",
|
|
286
|
+
"runner",
|
|
287
|
+
"session-id",
|
|
288
|
+
"revision",
|
|
289
|
+
"expires-at",
|
|
290
|
+
"json",
|
|
291
|
+
],
|
|
292
|
+
required: ["claimant", "runner", "session-id", "revision"],
|
|
293
|
+
},
|
|
294
|
+
},
|
|
295
|
+
release: {
|
|
296
|
+
usage: "agency release <task-id> [phase-id] [options]",
|
|
297
|
+
options: ownedClaimOptions,
|
|
298
|
+
command: {
|
|
299
|
+
usage:
|
|
300
|
+
"agency release <task-id> [phase-id] --session-id <id> --revision <sha256> [--json]",
|
|
301
|
+
minArgs: 1,
|
|
302
|
+
maxArgs: 2,
|
|
303
|
+
options: ["session-id", "revision", "json"],
|
|
304
|
+
required: ["session-id", "revision"],
|
|
305
|
+
},
|
|
306
|
+
},
|
|
307
|
+
finish: {
|
|
308
|
+
usage: "agency finish <task-id> [phase-id] [options]",
|
|
309
|
+
options: {
|
|
310
|
+
...ownedClaimOptions,
|
|
311
|
+
outcome: { type: "string" },
|
|
312
|
+
},
|
|
313
|
+
command: {
|
|
314
|
+
usage:
|
|
315
|
+
"agency finish <task-id> [phase-id] --session-id <id> --revision <sha256> --outcome <done|dropped> [--json]",
|
|
316
|
+
minArgs: 1,
|
|
317
|
+
maxArgs: 2,
|
|
318
|
+
options: ["session-id", "revision", "outcome", "json"],
|
|
319
|
+
required: ["session-id", "revision", "outcome"],
|
|
320
|
+
},
|
|
321
|
+
},
|
|
261
322
|
archive: {
|
|
262
323
|
usage: "agency archive <epic|task|phase>",
|
|
263
324
|
options: outputOptions,
|
|
@@ -283,18 +344,22 @@ const commands = {
|
|
|
283
344
|
},
|
|
284
345
|
},
|
|
285
346
|
work: {
|
|
286
|
-
usage:
|
|
347
|
+
usage:
|
|
348
|
+
"agency work [<directory-or-task-id> | --epic <epic-id>] | agency work prepare [target] [--dry-run] [--json]",
|
|
287
349
|
options: {
|
|
288
350
|
...commonOptions,
|
|
351
|
+
json: { type: "boolean" },
|
|
352
|
+
"dry-run": { type: "boolean" },
|
|
289
353
|
epic: { type: "string" },
|
|
290
354
|
opencode: { type: "boolean" },
|
|
291
355
|
claude: { type: "boolean" },
|
|
292
356
|
},
|
|
293
357
|
command: {
|
|
294
|
-
usage:
|
|
358
|
+
usage:
|
|
359
|
+
"agency work [<directory-or-task-id> | --epic <epic-id>] | agency work prepare [target] [--dry-run] [--json]",
|
|
295
360
|
minArgs: 0,
|
|
296
|
-
maxArgs:
|
|
297
|
-
options: ["epic", "opencode", "claude"],
|
|
361
|
+
maxArgs: 2,
|
|
362
|
+
options: ["json", "dry-run", "epic", "opencode", "claude"],
|
|
298
363
|
conflicts: [
|
|
299
364
|
["opencode", "claude"],
|
|
300
365
|
["epic", "$positional"],
|
|
@@ -649,6 +714,37 @@ export function parseCli(args: readonly string[]): ParsedCli {
|
|
|
649
714
|
if (commandName === "graph") {
|
|
650
715
|
validateGraphOptions(parsed.values, spec)
|
|
651
716
|
}
|
|
717
|
+
if (
|
|
718
|
+
commandName === "finish" &&
|
|
719
|
+
parsed.values.outcome !== "done" &&
|
|
720
|
+
parsed.values.outcome !== "dropped"
|
|
721
|
+
) {
|
|
722
|
+
throw usageError(
|
|
723
|
+
"Option '--outcome' must be 'done' or 'dropped'.",
|
|
724
|
+
spec.usage,
|
|
725
|
+
)
|
|
726
|
+
}
|
|
727
|
+
if (commandName === "work") {
|
|
728
|
+
const preparing = commandPositionals[0] === "prepare"
|
|
729
|
+
if (
|
|
730
|
+
(!preparing && commandPositionals.length > 1) ||
|
|
731
|
+
(preparing &&
|
|
732
|
+
(parsed.values.epic || parsed.values.opencode || parsed.values.claude))
|
|
733
|
+
) {
|
|
734
|
+
throw usageError(
|
|
735
|
+
preparing
|
|
736
|
+
? "Work preparation cannot be combined with agent or epic options."
|
|
737
|
+
: "The work command accepts at most one target.",
|
|
738
|
+
spec.usage,
|
|
739
|
+
)
|
|
740
|
+
}
|
|
741
|
+
if (!preparing && (parsed.values.json || parsed.values["dry-run"])) {
|
|
742
|
+
throw usageError(
|
|
743
|
+
"Options '--json' and '--dry-run' are only valid with 'agency work prepare'.",
|
|
744
|
+
spec.usage,
|
|
745
|
+
)
|
|
746
|
+
}
|
|
747
|
+
}
|
|
652
748
|
|
|
653
749
|
return {
|
|
654
750
|
commandName: commandName as keyof typeof commands,
|