@markjaquith/agency 2.48.0 → 2.49.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 CHANGED
@@ -53,7 +53,6 @@ workbase/
53
53
  .opencode/
54
54
  opencode.jsonc # managed @agency subagent, instructions, and reference
55
55
  tui.jsonc # managed TUI plugin registration
56
- command/agency.md # managed /agency workflow command
57
56
  plugin/agency-repository-skills.ts # managed workbase access and checkout skills
58
57
  tui/agency-debug.ts # managed /agency-debug TUI diagnostic
59
58
  agency.json # tracked config and portable repository declarations
@@ -84,10 +83,11 @@ workbase/
84
83
 
85
84
  Agency keeps discovery and other observational commands read-only. Run
86
85
  `agency integration status` to inspect `.agency/AGENTS.md` and
87
- `.opencode/opencode.jsonc`, `.opencode/tui.jsonc`, and their managed command and
88
- plugin files, then `agency integration sync` to create missing files or refresh
86
+ `.opencode/opencode.jsonc`, `.opencode/tui.jsonc`, and their managed plugin
87
+ files, then `agency integration sync` to create missing files or refresh
89
88
  checksum-safe managed files. Customized files are reported but never
90
- overwritten. The root
89
+ overwritten. Sync also removes checksum-valid retired managed artifacts while
90
+ preserving customized files at their former paths. The root
91
91
  `AGENTS.md` is user-owned and is not inspected or modified by Agency.
92
92
 
93
93
  The managed `.agency/AGENTS.md` is the complete in-workbase operating contract
@@ -122,13 +122,6 @@ The plugin grants whole-workbase access dynamically, while the portable
122
122
  reference advertises that context to agents. Bash and Agency operations must
123
123
  still follow the write authority reported by `agency context`.
124
124
 
125
- OpenCode also discovers a managed `/agency` command. Use `/agency status` for a
126
- read-only current-work summary, `/agency start [target]` to begin or resume work
127
- in the active session, `/agency next` to inspect ready work, `/agency validate`
128
- to check the workbase, and `/agency finish [target]` for verified closeout. The
129
- command uses OpenCode positional arguments internally and defaults to the safe
130
- `status` workflow when no subcommand is supplied.
131
-
132
125
  Repository aliases and canonical fetch remotes are declared in tracked
133
126
  `agency.json`; local bare clones and symlinks remain ignored under
134
127
  `repos/{alias}`. A declaration contains no local path, symlink target, checkout,
@@ -617,7 +610,8 @@ Inspect tasks:
617
610
  ```text
618
611
  agency task list [filters] [--json]
619
612
  agency task show <id> [--json]
620
- agency task status <id> <open|working|dropped> [--json]
613
+ agency task status <id> <open|working|done|dropped>
614
+ [--no-pull-request --summary <text> [--evidence-url <url>]] [--json]
621
615
  agency task update <id> [metadata options] [--json]
622
616
  agency task rename <id> <new-id> [--json]
623
617
  agency task move <id> (--epic <epic-id> | --no-epic) [--json]
@@ -656,7 +650,8 @@ agency phase create <task-id> <phase-id>
656
650
 
657
651
  agency phase list <task-id> [filters] [--json]
658
652
  agency phase show <task-id> <phase-id> [--json]
659
- agency phase status <task-id> <phase-id> <open|working|dropped> [--json]
653
+ agency phase status <task-id> <phase-id> <open|working|done|dropped>
654
+ [--no-pull-request --summary <text> [--evidence-url <url>]] [--json]
660
655
  agency phase update <task-id> <phase-id> [metadata options] [--json]
661
656
  agency phase rename <task-id> <phase-id> <new-id> [--json]
662
657
  agency phase dependency <add|remove> <task-id> <phase-id> <dependency-id>
@@ -679,8 +674,12 @@ writing anything.
679
674
  Single-phase tasks and phases store status in YAML. New execution units start
680
675
  `open`, and `agency work` marks the selected execution unit `working` immediately
681
676
  before launch. Running `agency work` again can relaunch unclaimed `working` work.
682
- The `done` status is reserved for an authoritative merged pull request and is
683
- applied by `agency sync --apply`, not by task or phase status commands.
677
+ By default, `done` requires an authoritative merged pull request and is applied
678
+ by `agency sync --apply`. Work whose intended outcome genuinely requires no pull
679
+ request may instead use an explicit `--no-pull-request --summary <text>` status
680
+ transition. Agency records the summary, completion time, and optional evidence
681
+ URL durably; reopening removes that evidence. This exceptional path refuses work
682
+ that already has a recorded pull request.
684
683
  Use explicit claims only when an external orchestrator needs coordinated
685
684
  ownership. The interactive work selector displays status markers before
686
685
  execution units. Existing working and delegated work may be released to `open`
@@ -711,15 +710,19 @@ agency claim <task-id> [phase-id] --claimant <id> --runner <id>
711
710
  agency release <task-id> [phase-id] --session-id <id>
712
711
  --revision <sha256> [--json]
713
712
  agency finish <task-id> [phase-id] --session-id <id>
714
- --revision <sha256> --outcome <done|dropped> [--json]
713
+ --revision <sha256> --outcome <done|dropped>
714
+ [--no-pull-request --summary <text> [--evidence-url <url>]] [--json]
715
715
  ```
716
716
 
717
717
  An active claim sets status to `working`. Release returns it to `open`. Finish
718
718
  records the claim outcome and ownership history; a `done` claim outcome leaves
719
719
  the execution unit `working` until its pull request is merged, while `dropped`
720
- remains terminal. Conflicts return the current revision and complete ownership
721
- record in the machine error envelope rather than overwriting it. Expired claims
722
- may be replaced with a revision-guarded claim.
720
+ remains terminal. For a genuine non-PR outcome, `--no-pull-request` atomically
721
+ records completion evidence, finishes the claim, and sets the execution unit to
722
+ `done`; `--summary` is required and `--evidence-url` is optional. Conflicts return
723
+ the current revision and complete ownership record in the machine error envelope
724
+ rather than overwriting it. Expired claims may be replaced with a
725
+ revision-guarded claim.
723
726
 
724
727
  `agency work` does not claim execution units. It refuses active explicit claims,
725
728
  marks open execution work `working`, and launches the runner. External
package/cli.ts CHANGED
@@ -205,6 +205,9 @@ const commands: Record<string, Command> = {
205
205
  sessionId: options["session-id"],
206
206
  revision: options.revision,
207
207
  outcome: options.outcome,
208
+ noPullRequest: options["no-pull-request"],
209
+ summary: options.summary,
210
+ evidenceUrl: options["evidence-url"],
208
211
  json: options.json,
209
212
  silent: options.silent,
210
213
  verbose: options.verbose,
@@ -298,6 +301,9 @@ const commands: Record<string, Command> = {
298
301
  clearReferences: options["clear-references"],
299
302
  prUrl: options["pr-url"],
300
303
  clearPr: options["clear-pr"],
304
+ noPullRequest: options["no-pull-request"],
305
+ summary: options.summary,
306
+ evidenceUrl: options["evidence-url"],
301
307
  ifRevision: options["if-revision"],
302
308
  dependsOn: options["depends-on"],
303
309
  firstPhase: options["first-phase"],
@@ -449,6 +455,9 @@ const commands: Record<string, Command> = {
449
455
  clearReferences: options["clear-references"],
450
456
  prUrl: options["pr-url"],
451
457
  clearPr: options["clear-pr"],
458
+ noPullRequest: options["no-pull-request"],
459
+ summary: options.summary,
460
+ evidenceUrl: options["evidence-url"],
452
461
  ifRevision: options["if-revision"],
453
462
  noEpic: options["no-epic"],
454
463
  multiPhase: options["multi-phase"],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markjaquith/agency",
3
- "version": "2.48.0",
3
+ "version": "2.49.0",
4
4
  "description": "Manage agentic work across repositories with durable workbases",
5
5
  "keywords": [
6
6
  "agents",
@@ -362,6 +362,64 @@ describe("strict CLI parsing", () => {
362
362
  "working",
363
363
  ]),
364
364
  ).toThrow("must be 'done' or 'dropped'")
365
+ expect(
366
+ parseCli([
367
+ "finish",
368
+ "task",
369
+ "--session-id",
370
+ "job-1",
371
+ "--revision",
372
+ revision,
373
+ "--outcome",
374
+ "done",
375
+ "--no-pull-request",
376
+ "--summary",
377
+ "Investigation completed",
378
+ "--evidence-url",
379
+ "https://example.com/result",
380
+ ]),
381
+ ).toMatchObject({
382
+ values: {
383
+ "no-pull-request": true,
384
+ summary: "Investigation completed",
385
+ "evidence-url": "https://example.com/result",
386
+ },
387
+ })
388
+ expect(() =>
389
+ parseCli([
390
+ "finish",
391
+ "task",
392
+ "--session-id",
393
+ "job-1",
394
+ "--revision",
395
+ revision,
396
+ "--outcome",
397
+ "done",
398
+ "--no-pull-request",
399
+ ]),
400
+ ).toThrow("--summary' is required")
401
+ expect(() =>
402
+ parseCli([
403
+ "task",
404
+ "status",
405
+ "example",
406
+ "working",
407
+ "--no-pull-request",
408
+ "--summary",
409
+ "Not done",
410
+ ]),
411
+ ).toThrow("valid only with a done status")
412
+ expect(() =>
413
+ parseCli([
414
+ "phase",
415
+ "status",
416
+ "task",
417
+ "phase",
418
+ "done",
419
+ "--summary",
420
+ "Missing explicit flag",
421
+ ]),
422
+ ).toThrow("require '--no-pull-request'")
365
423
  })
366
424
 
367
425
  test("accepts valid mutation revisions and rejects malformed hashes", () => {
package/src/cli-parser.ts CHANGED
@@ -120,6 +120,12 @@ const ownedClaimOptions = {
120
120
  revision: { type: "string" },
121
121
  } satisfies OptionConfig
122
122
 
123
+ const nonPrCompletionOptions = {
124
+ "no-pull-request": { type: "boolean" },
125
+ summary: { type: "string" },
126
+ "evidence-url": { type: "string" },
127
+ } satisfies OptionConfig
128
+
123
129
  const commands = {
124
130
  init: {
125
131
  usage: "agency init [path] [--json]",
@@ -360,6 +366,7 @@ const commands = {
360
366
  ...newWorkOptions,
361
367
  ...viewOptions,
362
368
  ...mutationOptions,
369
+ ...nonPrCompletionOptions,
363
370
  "pr-url": { type: "string" },
364
371
  task: { type: "string" },
365
372
  },
@@ -417,10 +424,10 @@ const commands = {
417
424
  options: ["json", "task"],
418
425
  },
419
426
  status: {
420
- usage: "agency task status <id> <status> [--json]",
427
+ usage: "agency task status <id> <status> [options] [--json]",
421
428
  minArgs: 2,
422
429
  maxArgs: 2,
423
- options: ["json", "task"],
430
+ options: ["json", "task", "no-pull-request", "summary", "evidence-url"],
424
431
  },
425
432
  update: {
426
433
  usage: "agency task update <id> [options] [--json]",
@@ -479,6 +486,7 @@ const commands = {
479
486
  ...newWorkOptions,
480
487
  ...viewOptions,
481
488
  ...mutationOptions,
489
+ ...nonPrCompletionOptions,
482
490
  "pr-url": { type: "string" },
483
491
  task: { type: "string" },
484
492
  phase: { type: "string" },
@@ -542,10 +550,18 @@ const commands = {
542
550
  options: ["json", "task", "phase"],
543
551
  },
544
552
  status: {
545
- usage: "agency phase status <task-id> <phase-id> <status> [--json]",
553
+ usage:
554
+ "agency phase status <task-id> <phase-id> <status> [options] [--json]",
546
555
  minArgs: 3,
547
556
  maxArgs: 3,
548
- options: ["json", "task", "phase"],
557
+ options: [
558
+ "json",
559
+ "task",
560
+ "phase",
561
+ "no-pull-request",
562
+ "summary",
563
+ "evidence-url",
564
+ ],
549
565
  },
550
566
  update: {
551
567
  usage: "agency phase update <task-id> <phase-id> [options] [--json]",
@@ -631,16 +647,27 @@ const commands = {
631
647
  usage: "agency finish <task-id> [phase-id] [options]",
632
648
  options: {
633
649
  ...ownedClaimOptions,
650
+ ...nonPrCompletionOptions,
634
651
  outcome: { type: "string" },
635
652
  task: { type: "string" },
636
653
  phase: { type: "string" },
637
654
  },
638
655
  command: {
639
656
  usage:
640
- "agency finish <task-id> [phase-id] --session-id <id> --revision <sha256> --outcome <done|dropped> [--json]",
657
+ "agency finish <task-id> [phase-id] --session-id <id> --revision <sha256> --outcome <done|dropped> [options] [--json]",
641
658
  minArgs: 1,
642
659
  maxArgs: 2,
643
- options: ["session-id", "revision", "outcome", "json", "task", "phase"],
660
+ options: [
661
+ "session-id",
662
+ "revision",
663
+ "outcome",
664
+ "no-pull-request",
665
+ "summary",
666
+ "evidence-url",
667
+ "json",
668
+ "task",
669
+ "phase",
670
+ ],
644
671
  required: ["session-id", "revision", "outcome"],
645
672
  },
646
673
  },
@@ -1409,6 +1436,40 @@ export function parseCli(args: readonly string[]): ParsedCli {
1409
1436
  spec.usage,
1410
1437
  )
1411
1438
  }
1439
+ const nonPrCompletion = parsed.values["no-pull-request"] === true
1440
+ const completionSummary = parsed.values.summary
1441
+ const completionEvidenceUrl = parsed.values["evidence-url"]
1442
+ if (
1443
+ (completionSummary !== undefined || completionEvidenceUrl !== undefined) &&
1444
+ !nonPrCompletion
1445
+ ) {
1446
+ throw usageError(
1447
+ "Options '--summary' and '--evidence-url' require '--no-pull-request'.",
1448
+ spec.usage,
1449
+ )
1450
+ }
1451
+ if (nonPrCompletion) {
1452
+ if (
1453
+ typeof completionSummary !== "string" ||
1454
+ completionSummary.trim().length === 0
1455
+ ) {
1456
+ throw usageError(
1457
+ "Option '--summary' is required with '--no-pull-request'.",
1458
+ spec.usage,
1459
+ )
1460
+ }
1461
+ const completesDone =
1462
+ (commandName === "finish" && parsed.values.outcome === "done") ||
1463
+ (["task", "phase"].includes(commandName) &&
1464
+ subcommand === "status" &&
1465
+ commandPositionals.at(-1) === "done")
1466
+ if (!completesDone) {
1467
+ throw usageError(
1468
+ "Option '--no-pull-request' is valid only with a done status or outcome.",
1469
+ spec.usage,
1470
+ )
1471
+ }
1472
+ }
1412
1473
  if (commandName === "work") {
1413
1474
  const preparing = commandPositionals[0] === "prepare"
1414
1475
  if (
package/src/cli.test.ts CHANGED
@@ -248,6 +248,77 @@ describe("CLI", () => {
248
248
  parseJson(await runCli(["task", "show", "claimed", "--json"], root)).data
249
249
  .status,
250
250
  ).toBe("working")
251
+
252
+ parseJson(
253
+ await runCli(
254
+ ["task", "create", "non-pr", "--repo", "agency", "--json"],
255
+ root,
256
+ ),
257
+ )
258
+ const nonPrContext = parseJson(
259
+ await runCli(["context", "tasks/non-pr", "--json"], root),
260
+ )
261
+ const nonPrClaim = parseJson(
262
+ await runCli(
263
+ [
264
+ "claim",
265
+ "non-pr",
266
+ "--claimant",
267
+ "orchestrator",
268
+ "--runner",
269
+ "agent",
270
+ "--session-id",
271
+ "job-2",
272
+ "--revision",
273
+ nonPrContext.documents.task.sha256,
274
+ "--json",
275
+ ],
276
+ root,
277
+ ),
278
+ )
279
+ parseJson(
280
+ await runCli(
281
+ [
282
+ "finish",
283
+ "non-pr",
284
+ "--session-id",
285
+ "job-2",
286
+ "--revision",
287
+ nonPrClaim.revision,
288
+ "--outcome",
289
+ "done",
290
+ "--no-pull-request",
291
+ "--summary",
292
+ "Investigation completed without changes.",
293
+ "--evidence-url",
294
+ "https://example.com/result",
295
+ "--json",
296
+ ],
297
+ root,
298
+ ),
299
+ )
300
+ expect(
301
+ parseJson(await runCli(["task", "show", "non-pr", "--json"], root)).data,
302
+ ).toMatchObject({
303
+ status: "done",
304
+ completion: {
305
+ mode: "non-pr",
306
+ summary: "Investigation completed without changes.",
307
+ evidenceUrl: "https://example.com/result",
308
+ },
309
+ })
310
+ const prUpdate = await runCli(
311
+ [
312
+ "task",
313
+ "update",
314
+ "non-pr",
315
+ "--pr-url",
316
+ "https://github.com/example/agency/pull/1",
317
+ ],
318
+ root,
319
+ )
320
+ expect(prUpdate.exitCode).toBe(1)
321
+ expect(prUpdate.stderr).toContain("Reopen non-PR completed work")
251
322
  })
252
323
 
253
324
  test("routes graph mutations through structured output", async () => {
@@ -526,7 +597,6 @@ describe("CLI", () => {
526
597
  expect(before.files).toMatchObject([
527
598
  { name: "agents", state: "managed" },
528
599
  { name: "opencode", state: "managed" },
529
- { name: "opencode-command", state: "managed" },
530
600
  { name: "opencode-plugin", state: "managed" },
531
601
  { name: "opencode-tui", state: "managed" },
532
602
  { name: "opencode-tui-plugin", state: "managed" },
@@ -538,7 +608,6 @@ describe("CLI", () => {
538
608
  expect(synced.files).toMatchObject([
539
609
  { name: "agents", state: "managed", changed: false },
540
610
  { name: "opencode", state: "managed", changed: false },
541
- { name: "opencode-command", state: "managed", changed: false },
542
611
  { name: "opencode-plugin", state: "managed", changed: false },
543
612
  { name: "opencode-tui", state: "managed", changed: false },
544
613
  { name: "opencode-tui-plugin", state: "managed", changed: false },
@@ -1022,7 +1091,7 @@ status: open
1022
1091
  },
1023
1092
  {
1024
1093
  args: ["--task", "pipeline", "--phase", "build"],
1025
- cwd: join(workbaseRoot, "tasks/pipeline"),
1094
+ cwd: join(workbaseRoot, "tasks/pipeline/phases/build"),
1026
1095
  checkoutPath: join(
1027
1096
  workbaseRoot,
1028
1097
  "tasks/pipeline/phases/build/code/agency",
@@ -1060,6 +1129,16 @@ status: open
1060
1129
  launch.checkoutPath,
1061
1130
  )
1062
1131
  expect(contract.environment.OPENCODE_CONFIG_CONTENT).toBeUndefined()
1132
+ if (launch === launches[1]) {
1133
+ const context = parseJson(
1134
+ await runCli(["context", ".", "--json"], contract.cwd),
1135
+ )
1136
+ expect(context.target).toMatchObject({
1137
+ kind: "phase",
1138
+ taskId: "pipeline",
1139
+ phaseId: "build",
1140
+ })
1141
+ }
1063
1142
  const environment = {
1064
1143
  ...process.env,
1065
1144
  ...contract.environment,
@@ -13,6 +13,9 @@ interface ClaimCommandOptions extends BaseCommandOptions {
13
13
  readonly revision?: string
14
14
  readonly expiresAt?: string
15
15
  readonly outcome?: string
16
+ readonly noPullRequest?: boolean
17
+ readonly summary?: string
18
+ readonly evidenceUrl?: string
16
19
  readonly json?: boolean
17
20
  }
18
21
 
@@ -32,6 +35,16 @@ export const claimCommand = (options: ClaimCommandOptions) =>
32
35
  new Error("Claimant and runner identities are required"),
33
36
  )
34
37
  }
38
+ if (options.noPullRequest && !options.summary?.trim()) {
39
+ return yield* Effect.fail(
40
+ new Error("Non-PR completion requires a non-empty summary"),
41
+ )
42
+ }
43
+ if (options.noPullRequest && options.outcome !== "done") {
44
+ return yield* Effect.fail(
45
+ new Error("Non-PR completion is valid only with a done outcome"),
46
+ )
47
+ }
35
48
  if (
36
49
  options.operation === "finish" &&
37
50
  options.outcome !== "done" &&
@@ -65,6 +78,16 @@ export const claimCommand = (options: ClaimCommandOptions) =>
65
78
  {
66
79
  ...common,
67
80
  outcome: options.outcome as "done" | "dropped",
81
+ ...(options.noPullRequest
82
+ ? {
83
+ nonPrCompletion: {
84
+ summary: options.summary!,
85
+ ...(options.evidenceUrl
86
+ ? { evidenceUrl: options.evidenceUrl }
87
+ : {}),
88
+ },
89
+ }
90
+ : {}),
68
91
  },
69
92
  cwd,
70
93
  )
@@ -91,8 +114,9 @@ Release an execution unit owned by the session.
91
114
  `
92
115
 
93
116
  export const finishHelp = `
94
- Usage: agency finish <task-id> [phase-id] --session-id <id> --revision <sha256> --outcome <done|dropped>
117
+ Usage: agency finish <task-id> [phase-id] --session-id <id> --revision <sha256> --outcome <done|dropped> [--no-pull-request --summary <text> [--evidence-url <url>]]
95
118
 
96
119
  Finish a claim owned by the session. A done claim outcome leaves unmerged work
97
- working; agency sync --apply marks the execution unit done after merge.
120
+ working; agency sync --apply marks the execution unit done after merge. Use
121
+ --no-pull-request with a summary for an explicit non-PR completion.
98
122
  `
@@ -68,11 +68,9 @@ describe("init command", () => {
68
68
  workbase: expect.objectContaining({ path: ".." }),
69
69
  })
70
70
  expect(config.permission).toBeUndefined()
71
- const command = await Bun.file(
72
- join(root, ".opencode/command/agency.md"),
73
- ).text()
74
- expect(command).toContain("Workflow: `$1`")
75
- expect(command).toContain("Optional target: `$2`")
71
+ expect(
72
+ await Bun.file(join(root, ".opencode/command/agency.md")).exists(),
73
+ ).toBe(false)
76
74
  const plugin = await Bun.file(
77
75
  join(root, ".opencode/plugin/agency-repository-skills.ts"),
78
76
  ).text()
@@ -36,11 +36,6 @@ describe("integration command", () => {
36
36
  diagnostic: expect.stringContaining("cannot load"),
37
37
  remediation: expect.stringContaining("integration sync"),
38
38
  },
39
- {
40
- name: "opencode-command",
41
- state: "missing",
42
- remediation: expect.stringContaining("integration sync"),
43
- },
44
39
  {
45
40
  name: "opencode-plugin",
46
41
  state: "missing",
@@ -91,11 +86,6 @@ OpenCode config: missing
91
86
  Agency OpenCode launches cannot load current Agency instructions or whole-workbase access.
92
87
  Action: Run 'agency integration sync' to install Agency instructions and whole-workbase OpenCode access.
93
88
 
94
- OpenCode /agency command: missing
95
- Path: .opencode/command/agency.md
96
- The managed OpenCode /agency command needs synchronization.
97
- Action: Run 'agency integration sync' to install the managed /agency command.
98
-
99
89
  OpenCode workbase plugin: missing
100
90
  Path: .opencode/plugin/agency-repository-skills.ts
101
91
  The managed OpenCode workbase plugin needs synchronization.
@@ -120,7 +110,6 @@ OpenCode /agency-debug: missing
120
110
  expect(JSON.parse(logs[0]!).files).toMatchObject([
121
111
  { name: "agents", state: "managed", changed: true },
122
112
  { name: "opencode", state: "managed", changed: true },
123
- { name: "opencode-command", state: "managed", changed: true },
124
113
  { name: "opencode-plugin", state: "managed", changed: true },
125
114
  { name: "opencode-tui", state: "managed", changed: true },
126
115
  { name: "opencode-tui-plugin", state: "managed", changed: true },
@@ -132,7 +121,7 @@ OpenCode /agency-debug: missing
132
121
  ).toBe(true)
133
122
  expect(
134
123
  await Bun.file(join(root, ".opencode/command/agency.md")).exists(),
135
- ).toBe(true)
124
+ ).toBe(false)
136
125
  expect(
137
126
  await Bun.file(
138
127
  join(root, ".opencode/plugin/agency-repository-skills.ts"),
@@ -161,10 +150,6 @@ OpenCode config: synced
161
150
  Path: .opencode/opencode.jsonc
162
151
  Agency's managed OpenCode launch config is ready to load Agency instructions and provide whole-workbase read access.
163
152
 
164
- OpenCode /agency command: synced
165
- Path: .opencode/command/agency.md
166
- Agency's managed OpenCode /agency command is current.
167
-
168
153
  OpenCode workbase plugin: synced
169
154
  Path: .opencode/plugin/agency-repository-skills.ts
170
155
  Agency's managed OpenCode plugin provides whole-workbase access and exposes writable-checkout skills.
@@ -15,7 +15,6 @@ interface IntegrationResult {
15
15
  readonly name:
16
16
  | "agents"
17
17
  | "opencode"
18
- | "opencode-command"
19
18
  | "opencode-plugin"
20
19
  | "opencode-tui"
21
20
  | "opencode-tui-plugin"
@@ -30,7 +29,6 @@ interface IntegrationResult {
30
29
  const integrationNames = {
31
30
  agents: "Agent instructions",
32
31
  opencode: "OpenCode config",
33
- "opencode-command": "OpenCode /agency command",
34
32
  "opencode-plugin": "OpenCode workbase plugin",
35
33
  "opencode-tui": "OpenCode TUI config",
36
34
  "opencode-tui-plugin": "OpenCode /agency-debug",
@@ -31,6 +31,9 @@ interface PhaseOptions extends BaseCommandOptions {
31
31
  readonly pr?: boolean
32
32
  readonly work?: boolean
33
33
  readonly auto?: boolean
34
+ readonly noPullRequest?: boolean
35
+ readonly summary?: string
36
+ readonly evidenceUrl?: string
34
37
  }
35
38
 
36
39
  export const phase = (options: PhaseOptions, work: StartWork = startWork) =>
@@ -166,7 +169,20 @@ export const phase = (options: PhaseOptions, work: StartWork = startWork) =>
166
169
  ),
167
170
  )
168
171
  }
169
- const record = yield* phases.setStatus(taskId, phaseId, status, cwd)
172
+ const record = yield* phases.setStatus(
173
+ taskId,
174
+ phaseId,
175
+ status,
176
+ cwd,
177
+ options.noPullRequest
178
+ ? {
179
+ summary: options.summary ?? "",
180
+ ...(options.evidenceUrl
181
+ ? { evidenceUrl: options.evidenceUrl }
182
+ : {}),
183
+ }
184
+ : undefined,
185
+ )
170
186
  const { content: _, ...output } = record
171
187
  log(
172
188
  options.json
@@ -275,7 +291,7 @@ Subcommands:
275
291
  list <task> List task phases
276
292
  show <task> <phase> Show a phase
277
293
  status <task> <phase> <status>
278
- Set open, working, or dropped
294
+ Set open, working, dropped, or explicit non-PR done
279
295
  update <task> <phase> Update phase metadata
280
296
  rename <task> <phase> <new-id>
281
297
  Rename a phase and update dependencies
@@ -285,6 +301,11 @@ Subcommands:
285
301
  Mutation option:
286
302
  --if-revision <hash> Require the target's current revision
287
303
 
304
+ Non-PR completion options (status done only):
305
+ --no-pull-request Complete without a pull request
306
+ --summary <text> Required durable outcome summary
307
+ --evidence-url <url> Optional supporting record
308
+
288
309
  Create options:
289
310
  --description <text> Short description of the phase
290
311
  --repo <alias> Writable repository