@markjaquith/agency 3.14.0 → 3.15.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 +9 -3
- package/cli-main.ts +1 -1
- package/package.json +1 -1
- package/src/cli-parser.ts +8 -1
- package/src/commands/act-actions.ts +42 -9
- package/src/commands/review.ts +28 -7
- package/src/services/ReviewService.ts +45 -0
- package/src/services/TaskService.ts +9 -0
- package/src/workbase/AGENTS.md +7 -3
package/README.md
CHANGED
|
@@ -710,7 +710,7 @@ external chooser continues to offer goals and **Browse items** through that choo
|
|
|
710
710
|
| Split work | Add a phase / split this task; name the existing work's first phase | `split` |
|
|
711
711
|
| Work on a task or phase | Work on this item | `work` |
|
|
712
712
|
| Move from investigation to execution | Create implementation follow-up | `handoff` |
|
|
713
|
-
| Review someone else's work | Review a PR, or a remote branch/commit
|
|
713
|
+
| Review someone else's work | Review a PR, or a remote branch/commit; finish a review | `review`, `review-ref`, `finish-review` |
|
|
714
714
|
| Publish or update PR status | Push without a PR, refresh provider state, create a PR, mark a GitHub PR ready, or close it | `push`, `sync`, `pr`, `pr-ready`, `pr-close` |
|
|
715
715
|
| Maintain review work | Fetch and repin a review task to its current source | `review-refresh` |
|
|
716
716
|
| Organize work | Rename an item, move a task into/out of an epic, or add/remove a sibling dependency | `rename`, `move-to-epic`, `remove-from-epic`, `dependency-add`, `dependency-remove` |
|
|
@@ -723,7 +723,9 @@ on the new item now** or **Finish**. Only choosing Work prepares checkouts and
|
|
|
723
723
|
launches the configured runner; creation alone leaves the item for later.
|
|
724
724
|
Investigation handoff creates a distinct implementation item with source and
|
|
725
725
|
revision provenance. Completing a non-PR outcome requires a durable summary.
|
|
726
|
-
|
|
726
|
+
**Finish review** marks a review task done without a summary; review tasks offer
|
|
727
|
+
it instead of non-PR completion. PR-backed completion comes from provider
|
|
728
|
+
reconciliation after merge.
|
|
727
729
|
|
|
728
730
|
**Refresh Agency state** reads the provider and reconciles local records. The
|
|
729
731
|
explicit **GitHub PR ready/close** actions mutate the recorded GitHub URL, then
|
|
@@ -1060,6 +1062,7 @@ Create a pinned, read-only review task from the selected alias's origin:
|
|
|
1060
1062
|
agency task create <id> --review <alias> --pull-request <url-or-number>
|
|
1061
1063
|
agency task create <id> --review <alias> --ref <remote-ref>
|
|
1062
1064
|
agency review refresh <id> [--if-revision <hash>] [--json]
|
|
1065
|
+
agency review finish <id> [--if-revision <hash>] [--json]
|
|
1063
1066
|
```
|
|
1064
1067
|
|
|
1065
1068
|
Review creation fetches the source and records its exact 40-character commit.
|
|
@@ -1069,7 +1072,10 @@ detached checkout and no writable branch. Source movement is observed separately
|
|
|
1069
1072
|
from the pin and applied only by `review refresh`; sync, doctor, work, cleanup,
|
|
1070
1073
|
and archive never move the pin implicitly. Dirty or structurally unexpected
|
|
1071
1074
|
review checkouts block refresh and cleanup. Review tasks support the normal status
|
|
1072
|
-
lifecycle, but reject phase conversion and delivery PR operations.
|
|
1075
|
+
lifecycle, but reject phase conversion and delivery PR operations. Because a
|
|
1076
|
+
review has no delivery pull request, `review finish` marks an open, working, or
|
|
1077
|
+
delegated review done without the `--no-pull-request --summary` requirement
|
|
1078
|
+
that applies to other work.
|
|
1073
1079
|
Each active or archived review task owns one internal task-scoped pin ref. A
|
|
1074
1080
|
refresh advances that ref transactionally; failed creation removes it. Archiving
|
|
1075
1081
|
retains the pin so a deleted source can still be restored and inspected.
|
package/cli-main.ts
CHANGED
|
@@ -726,7 +726,7 @@ Commands:
|
|
|
726
726
|
next List or select ready execution units
|
|
727
727
|
pr create / pr [...] Create an Agency PR or run gh pr with repository focus
|
|
728
728
|
push Validate and publish the current execution unit
|
|
729
|
-
review refresh
|
|
729
|
+
review refresh|finish Refresh or finish a pinned review task
|
|
730
730
|
repo <subcommand> Manage workbase repositories
|
|
731
731
|
status Show status for the current workbase
|
|
732
732
|
doctor Diagnose workbase health and integrations
|
package/package.json
CHANGED
package/src/cli-parser.ts
CHANGED
|
@@ -783,7 +783,8 @@ const commands = {
|
|
|
783
783
|
},
|
|
784
784
|
},
|
|
785
785
|
review: {
|
|
786
|
-
usage:
|
|
786
|
+
usage:
|
|
787
|
+
"agency review <refresh|finish> <task-id> [--if-revision <hash>] [--json]",
|
|
787
788
|
options: {
|
|
788
789
|
...outputOptions,
|
|
789
790
|
"if-revision": { type: "string" },
|
|
@@ -796,6 +797,12 @@ const commands = {
|
|
|
796
797
|
maxArgs: 1,
|
|
797
798
|
options: ["if-revision", "json"],
|
|
798
799
|
},
|
|
800
|
+
finish: {
|
|
801
|
+
usage: "agency review finish <task-id> [--if-revision <hash>] [--json]",
|
|
802
|
+
minArgs: 1,
|
|
803
|
+
maxArgs: 1,
|
|
804
|
+
options: ["if-revision", "json"],
|
|
805
|
+
},
|
|
799
806
|
},
|
|
800
807
|
},
|
|
801
808
|
worktree: {
|
|
@@ -234,6 +234,11 @@ const actionPresentation: Record<
|
|
|
234
234
|
icon: "",
|
|
235
235
|
color: macchiato.sapphire,
|
|
236
236
|
},
|
|
237
|
+
"finish-review": {
|
|
238
|
+
description: "Mark this review done; no completion summary is required.",
|
|
239
|
+
icon: "",
|
|
240
|
+
color: macchiato.green,
|
|
241
|
+
},
|
|
237
242
|
reopen: {
|
|
238
243
|
description: "Return terminal work to open status.",
|
|
239
244
|
icon: "",
|
|
@@ -371,7 +376,7 @@ export const actionGroups = [
|
|
|
371
376
|
id: "review",
|
|
372
377
|
label: "Review someone else's work",
|
|
373
378
|
icon: "",
|
|
374
|
-
actions: ["review", "review-ref"],
|
|
379
|
+
actions: ["review", "review-ref", "finish-review"],
|
|
375
380
|
},
|
|
376
381
|
{
|
|
377
382
|
id: "split",
|
|
@@ -875,6 +880,7 @@ export const actActions = (
|
|
|
875
880
|
const purpose =
|
|
876
881
|
parent && "purpose" in parent.data ? parent.data.purpose : undefined
|
|
877
882
|
const terminal = node.status === "done" || node.status === "dropped"
|
|
883
|
+
const isReview = node.kind === "task" && "review" in node.data
|
|
878
884
|
const blockers = (execution ?? node).readiness.blockers
|
|
879
885
|
const validation = blockers.find(
|
|
880
886
|
(blocker) => blocker.kind === "validation",
|
|
@@ -1044,9 +1050,7 @@ export const actActions = (
|
|
|
1044
1050
|
details(
|
|
1045
1051
|
"review-refresh",
|
|
1046
1052
|
"Refresh review source",
|
|
1047
|
-
|
|
1048
|
-
? null
|
|
1049
|
-
: "Select a review task",
|
|
1053
|
+
isReview ? null : "Select a review task",
|
|
1050
1054
|
),
|
|
1051
1055
|
{
|
|
1052
1056
|
command: [
|
|
@@ -1065,6 +1069,33 @@ export const actActions = (
|
|
|
1065
1069
|
}),
|
|
1066
1070
|
},
|
|
1067
1071
|
),
|
|
1072
|
+
immediate(
|
|
1073
|
+
details(
|
|
1074
|
+
"finish-review",
|
|
1075
|
+
"Finish review",
|
|
1076
|
+
!isReview
|
|
1077
|
+
? "Select a review task"
|
|
1078
|
+
: terminal
|
|
1079
|
+
? "Review is already terminal"
|
|
1080
|
+
: null,
|
|
1081
|
+
),
|
|
1082
|
+
{
|
|
1083
|
+
command: [
|
|
1084
|
+
"agency",
|
|
1085
|
+
"review",
|
|
1086
|
+
"finish",
|
|
1087
|
+
target.taskId,
|
|
1088
|
+
"--if-revision",
|
|
1089
|
+
node.data.sha256,
|
|
1090
|
+
],
|
|
1091
|
+
run: review({
|
|
1092
|
+
...options,
|
|
1093
|
+
subcommand: "finish",
|
|
1094
|
+
taskId: target.taskId,
|
|
1095
|
+
ifRevision: node.data.sha256,
|
|
1096
|
+
}),
|
|
1097
|
+
},
|
|
1098
|
+
),
|
|
1068
1099
|
...(["reopen", "drop"] as const).map((id) => {
|
|
1069
1100
|
const status = id === "reopen" ? "open" : "dropped"
|
|
1070
1101
|
return immediate(
|
|
@@ -1300,11 +1331,13 @@ export const actActions = (
|
|
|
1300
1331
|
"complete",
|
|
1301
1332
|
"Complete without a pull request",
|
|
1302
1333
|
noExecution ??
|
|
1303
|
-
(
|
|
1304
|
-
? "
|
|
1305
|
-
:
|
|
1306
|
-
? "
|
|
1307
|
-
:
|
|
1334
|
+
(isReview
|
|
1335
|
+
? "Use Finish review for review tasks"
|
|
1336
|
+
: terminal
|
|
1337
|
+
? "Item is already terminal"
|
|
1338
|
+
: prUrl
|
|
1339
|
+
? "Reconcile the recorded pull request instead"
|
|
1340
|
+
: null),
|
|
1308
1341
|
),
|
|
1309
1342
|
inputs: [
|
|
1310
1343
|
input("summary", "Completed outcome summary", { multiline: true }),
|
package/src/commands/review.ts
CHANGED
|
@@ -12,17 +12,34 @@ interface ReviewOptions extends BaseCommandOptions {
|
|
|
12
12
|
|
|
13
13
|
export const review = (options: ReviewOptions) =>
|
|
14
14
|
Effect.gen(function* () {
|
|
15
|
-
if (
|
|
15
|
+
if (
|
|
16
|
+
(options.subcommand !== "refresh" && options.subcommand !== "finish") ||
|
|
17
|
+
!options.taskId
|
|
18
|
+
) {
|
|
16
19
|
return yield* Effect.fail(
|
|
17
|
-
new Error("Usage: agency review refresh <task>"),
|
|
20
|
+
new Error("Usage: agency review <refresh|finish> <task>"),
|
|
18
21
|
)
|
|
19
22
|
}
|
|
20
|
-
const
|
|
23
|
+
const service = yield* ReviewService
|
|
24
|
+
const { log } = createLoggers(options)
|
|
25
|
+
if (options.subcommand === "finish") {
|
|
26
|
+
const result = yield* service.finish(
|
|
27
|
+
options.taskId,
|
|
28
|
+
options.cwd,
|
|
29
|
+
options.ifRevision,
|
|
30
|
+
)
|
|
31
|
+
log(
|
|
32
|
+
options.json
|
|
33
|
+
? JSON.stringify(result, null, 2)
|
|
34
|
+
: `Finished review '${options.taskId}'`,
|
|
35
|
+
)
|
|
36
|
+
return
|
|
37
|
+
}
|
|
38
|
+
const result = yield* service.refresh(
|
|
21
39
|
options.taskId,
|
|
22
40
|
options.cwd,
|
|
23
41
|
options.ifRevision,
|
|
24
42
|
)
|
|
25
|
-
const { log } = createLoggers(options)
|
|
26
43
|
log(
|
|
27
44
|
options.json
|
|
28
45
|
? JSON.stringify(result, null, 2)
|
|
@@ -31,8 +48,12 @@ export const review = (options: ReviewOptions) =>
|
|
|
31
48
|
})
|
|
32
49
|
|
|
33
50
|
export const help = `
|
|
34
|
-
Usage: agency review refresh <task-id> [--if-revision <hash>] [--json]
|
|
51
|
+
Usage: agency review <refresh|finish> <task-id> [--if-revision <hash>] [--json]
|
|
35
52
|
|
|
36
|
-
|
|
37
|
-
|
|
53
|
+
Subcommands:
|
|
54
|
+
refresh Fetch the review source explicitly and replace the pinned commit
|
|
55
|
+
and any clean, detached review checkout. Review sources never
|
|
56
|
+
move implicitly.
|
|
57
|
+
finish Mark an open, working, or delegated review task done. Reviews
|
|
58
|
+
have no delivery pull request and need no completion summary.
|
|
38
59
|
`
|
|
@@ -424,6 +424,51 @@ export class ReviewService extends Effect.Service<ReviewService>()(
|
|
|
424
424
|
}),
|
|
425
425
|
)
|
|
426
426
|
}),
|
|
427
|
+
|
|
428
|
+
finish: (
|
|
429
|
+
taskId: string,
|
|
430
|
+
startPath: string = process.cwd(),
|
|
431
|
+
ifRevision?: string,
|
|
432
|
+
) =>
|
|
433
|
+
Effect.gen(function* () {
|
|
434
|
+
const workbase = yield* WorkbaseService
|
|
435
|
+
const tasks = yield* TaskService
|
|
436
|
+
const root = yield* workbase.discover(startPath)
|
|
437
|
+
const task = yield* tasks.show(taskId, root)
|
|
438
|
+
if (!("review" in task.data)) {
|
|
439
|
+
return yield* new ReviewError({
|
|
440
|
+
message: `Task '${taskId}' is not a review task`,
|
|
441
|
+
})
|
|
442
|
+
}
|
|
443
|
+
if (ifRevision && task.revision !== ifRevision) {
|
|
444
|
+
return yield* new RevisionConflictError({
|
|
445
|
+
path: task.path,
|
|
446
|
+
target: `task '${taskId}'`,
|
|
447
|
+
expectedRevision: ifRevision,
|
|
448
|
+
currentRevision: task.revision,
|
|
449
|
+
message: `Revision conflict for task '${taskId}'`,
|
|
450
|
+
})
|
|
451
|
+
}
|
|
452
|
+
if (task.data.status === "done" || task.data.status === "dropped") {
|
|
453
|
+
return yield* new ReviewError({
|
|
454
|
+
message: `Cannot finish review task '${taskId}' from ${task.data.status}; reopen it first`,
|
|
455
|
+
})
|
|
456
|
+
}
|
|
457
|
+
const parsed = yield* parseFrontmatter(task.content, task.path)
|
|
458
|
+
const data = { ...task.data, status: "done" as const }
|
|
459
|
+
const content = formatMarkdownDocument(data, parsed.body)
|
|
460
|
+
yield* runLifecycleTransaction({
|
|
461
|
+
root,
|
|
462
|
+
preconditions: [{ path: task.path, revision: task.revision }],
|
|
463
|
+
steps: [documentWriteStep(root, [{ path: task.path, content }])],
|
|
464
|
+
})
|
|
465
|
+
return {
|
|
466
|
+
id: taskId,
|
|
467
|
+
path: task.path,
|
|
468
|
+
revision: documentRevision(content),
|
|
469
|
+
data,
|
|
470
|
+
}
|
|
471
|
+
}),
|
|
427
472
|
}),
|
|
428
473
|
},
|
|
429
474
|
) {}
|
|
@@ -652,6 +652,15 @@ export class TaskService extends Effect.Service<TaskService>()("TaskService", {
|
|
|
652
652
|
!canTransitionStatus(record.data.status, validStatus) &&
|
|
653
653
|
!completionResult
|
|
654
654
|
) {
|
|
655
|
+
if (
|
|
656
|
+
validStatus === "done" &&
|
|
657
|
+
"review" in record.data &&
|
|
658
|
+
record.data.status !== "dropped"
|
|
659
|
+
) {
|
|
660
|
+
return yield* new TaskError({
|
|
661
|
+
message: `Finish review task '${id}' with 'agency review finish ${id}'; reviews do not require a completion summary`,
|
|
662
|
+
})
|
|
663
|
+
}
|
|
655
664
|
if (validStatus === "done") {
|
|
656
665
|
return yield* new TaskError({
|
|
657
666
|
message:
|
package/src/workbase/AGENTS.md
CHANGED
|
@@ -108,6 +108,8 @@ retain `--if-revision` guards when shown, and do not add flags that are not show
|
|
|
108
108
|
`agency context <new-task> --json`. Do not prepare or start it unless requested.
|
|
109
109
|
15. Refresh a pinned review task with the current revision:
|
|
110
110
|
`agency review refresh <task> --if-revision <revision> --json`.
|
|
111
|
+
16. Finish a review task with the current revision; no summary is required:
|
|
112
|
+
`agency review finish <task> --if-revision <revision> --json`.
|
|
111
113
|
|
|
112
114
|
Never pass `--work` or `--auto` to `agency task create`. Do not run separate
|
|
113
115
|
`agency validate`, `agency worktree prepare`, `agency graph`, `agency task list`,
|
|
@@ -182,8 +184,8 @@ agency validate --json
|
|
|
182
184
|
Require explicit user intent before initializing a workbase; changing repository
|
|
183
185
|
aliases or applying repository setup or workbase sync changes; launching another
|
|
184
186
|
agent from an active agent session; creating a pull request; archiving, restoring,
|
|
185
|
-
dropping, reopening, or completing work without a pull
|
|
186
|
-
to override readiness.
|
|
187
|
+
dropping, reopening, finishing a review, or completing work without a pull
|
|
188
|
+
request; or using `--force` to override readiness.
|
|
187
189
|
|
|
188
190
|
## Investigation Handoffs
|
|
189
191
|
|
|
@@ -272,7 +274,9 @@ its pull request is open. It becomes `done` only after its authoritative pull
|
|
|
272
274
|
request is merged and Agency reconciles that state. Do not mark committed or
|
|
273
275
|
review-ready work `done` manually. A genuine investigation, operational action,
|
|
274
276
|
or no-change result may complete without a pull request only with explicit user
|
|
275
|
-
intent, `--no-pull-request`, and a durable outcome summary.
|
|
277
|
+
intent, `--no-pull-request`, and a durable outcome summary. A review task has no
|
|
278
|
+
delivery pull request; with explicit user intent, finish it with
|
|
279
|
+
`agency review finish` instead, which requires no summary.
|
|
276
280
|
|
|
277
281
|
At each closeout trigger (creating or updating a PR, marking it ready, completing
|
|
278
282
|
a refinement loop, or pausing or handing off completed implementation work):
|