@markjaquith/agency 1.11.0 → 2.0.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.
Files changed (130) hide show
  1. package/README.md +302 -113
  2. package/cli.ts +123 -415
  3. package/index.ts +1 -1
  4. package/package.json +12 -17
  5. package/skills/agency/SKILL.md +258 -0
  6. package/src/cli.test.ts +290 -0
  7. package/src/commands/description.test.ts +103 -0
  8. package/src/commands/epic.test.ts +103 -0
  9. package/src/commands/epic.ts +101 -0
  10. package/src/commands/init.test.ts +42 -290
  11. package/src/commands/init.ts +15 -123
  12. package/src/commands/phase.ts +120 -0
  13. package/src/commands/pr.test.ts +19 -170
  14. package/src/commands/pr.ts +23 -179
  15. package/src/commands/repo.test.ts +80 -0
  16. package/src/commands/repo.ts +83 -0
  17. package/src/commands/status.test.ts +35 -477
  18. package/src/commands/status.ts +24 -213
  19. package/src/commands/task-phase.test.ts +206 -0
  20. package/src/commands/task.ts +83 -1285
  21. package/src/commands/validate.test.ts +91 -0
  22. package/src/commands/validate.ts +50 -0
  23. package/src/commands/work.test.ts +172 -419
  24. package/src/commands/work.ts +47 -166
  25. package/src/services/EpicService.test.ts +71 -0
  26. package/src/services/EpicService.ts +135 -0
  27. package/src/services/FileSystemService.ts +61 -92
  28. package/src/services/PhaseService.ts +323 -0
  29. package/src/services/PullRequestService.test.ts +386 -0
  30. package/src/services/PullRequestService.ts +143 -0
  31. package/src/services/RepositoryService.test.ts +106 -0
  32. package/src/services/RepositoryService.ts +159 -0
  33. package/src/services/TaskPhaseService.test.ts +217 -0
  34. package/src/services/TaskService.ts +192 -0
  35. package/src/services/WorkbaseService.test.ts +446 -0
  36. package/src/services/WorkbaseService.ts +493 -0
  37. package/src/services/WorktreeService.test.ts +599 -0
  38. package/src/services/WorktreeService.ts +357 -0
  39. package/src/test-utils.ts +41 -438
  40. package/src/types.ts +1 -215
  41. package/src/utils/command.ts +1 -5
  42. package/src/utils/effect.test.ts +24 -0
  43. package/src/utils/effect.ts +3 -273
  44. package/src/utils/process.test.ts +0 -15
  45. package/src/utils/process.ts +1 -60
  46. package/src/workbase/frontmatter.test.ts +50 -0
  47. package/src/workbase/frontmatter.ts +77 -0
  48. package/src/workbase/repository-reference.test.ts +25 -0
  49. package/src/workbase/repository-reference.ts +18 -0
  50. package/src/workbase/schemas.test.ts +146 -0
  51. package/src/workbase/schemas.ts +78 -0
  52. package/src/workbase/worktree-command.test.ts +61 -0
  53. package/src/workbase/worktree-command.ts +52 -0
  54. package/src/commands/base.test.ts +0 -198
  55. package/src/commands/base.ts +0 -198
  56. package/src/commands/clean.test.ts +0 -299
  57. package/src/commands/clean.ts +0 -274
  58. package/src/commands/completions.test.ts +0 -51
  59. package/src/commands/completions.ts +0 -308
  60. package/src/commands/emit.integration.test.ts +0 -549
  61. package/src/commands/emit.test.ts +0 -576
  62. package/src/commands/emit.ts +0 -700
  63. package/src/commands/emitted.test.ts +0 -226
  64. package/src/commands/emitted.ts +0 -57
  65. package/src/commands/loop.test.ts +0 -621
  66. package/src/commands/loop.ts +0 -406
  67. package/src/commands/merge.integration.test.ts +0 -195
  68. package/src/commands/merge.test.ts +0 -246
  69. package/src/commands/merge.ts +0 -244
  70. package/src/commands/pull.test.ts +0 -395
  71. package/src/commands/pull.ts +0 -205
  72. package/src/commands/push.test.ts +0 -518
  73. package/src/commands/push.ts +0 -434
  74. package/src/commands/rebase.test.ts +0 -521
  75. package/src/commands/rebase.ts +0 -232
  76. package/src/commands/save.test.ts +0 -247
  77. package/src/commands/save.ts +0 -162
  78. package/src/commands/source.test.ts +0 -195
  79. package/src/commands/source.ts +0 -72
  80. package/src/commands/switch.test.ts +0 -194
  81. package/src/commands/switch.ts +0 -84
  82. package/src/commands/task-branching.test.ts +0 -633
  83. package/src/commands/task-continue.test.ts +0 -311
  84. package/src/commands/task-edit.test.ts +0 -235
  85. package/src/commands/task-main.test.ts +0 -1066
  86. package/src/commands/task-squash.test.ts +0 -266
  87. package/src/commands/tasks.test.ts +0 -391
  88. package/src/commands/tasks.ts +0 -161
  89. package/src/commands/template-delete.test.ts +0 -178
  90. package/src/commands/template-delete.ts +0 -98
  91. package/src/commands/template-list.test.ts +0 -135
  92. package/src/commands/template-list.ts +0 -87
  93. package/src/commands/template-view.test.ts +0 -158
  94. package/src/commands/template-view.ts +0 -86
  95. package/src/commands/template.test.ts +0 -32
  96. package/src/commands/template.ts +0 -96
  97. package/src/commands/use.test.ts +0 -87
  98. package/src/commands/use.ts +0 -97
  99. package/src/constants.ts +0 -10
  100. package/src/errors.ts +0 -17
  101. package/src/schemas.ts +0 -33
  102. package/src/services/AgencyMetadataService.ts +0 -311
  103. package/src/services/ClaudeService.test.ts +0 -184
  104. package/src/services/ClaudeService.ts +0 -91
  105. package/src/services/ConfigService.ts +0 -115
  106. package/src/services/FilterRepoService.ts +0 -164
  107. package/src/services/FormatterService.test.ts +0 -432
  108. package/src/services/FormatterService.ts +0 -219
  109. package/src/services/GitService.ts +0 -1183
  110. package/src/services/MockFilterRepoService.ts +0 -140
  111. package/src/services/OpencodeService.ts +0 -263
  112. package/src/services/PromptService.ts +0 -183
  113. package/src/services/TemplateService.ts +0 -81
  114. package/src/types/native-exec.d.ts +0 -8
  115. package/src/utils/colors.ts +0 -178
  116. package/src/utils/git-path.ts +0 -6
  117. package/src/utils/glob.test.ts +0 -161
  118. package/src/utils/glob.ts +0 -78
  119. package/src/utils/paths.ts +0 -51
  120. package/src/utils/pr-branch.test.ts +0 -376
  121. package/src/utils/pr-branch.ts +0 -508
  122. package/src/utils/spinner.test.ts +0 -45
  123. package/src/utils/spinner.ts +0 -96
  124. package/src/utils/task-parser.test.ts +0 -172
  125. package/src/utils/task-parser.ts +0 -93
  126. package/templates/AGENCY.md +0 -22
  127. package/templates/AGENTS.md +0 -11
  128. package/templates/CLAUDE.md +0 -3
  129. package/templates/TASK.md +0 -5
  130. package/templates/opencode.json +0 -4
package/cli.ts CHANGED
@@ -1,52 +1,36 @@
1
1
  #!/usr/bin/env bun
2
2
 
3
3
  import { parseArgs } from "util"
4
- import { Effect, Layer } from "effect"
5
- import { clean, help as cleanHelp } from "./src/commands/clean"
4
+ import { Effect, Either, Layer } from "effect"
6
5
  import { init, help as initHelp } from "./src/commands/init"
7
- import { task, taskEdit, help as taskHelp, editHelp } from "./src/commands/task"
8
- import { tasks, help as tasksHelp } from "./src/commands/tasks"
9
- import { emit, help as emitHelp } from "./src/commands/emit"
10
- import { emitted, help as emittedHelp } from "./src/commands/emitted"
6
+ import { task, help as taskHelp } from "./src/commands/task"
11
7
  import { pr, help as prHelp } from "./src/commands/pr"
12
- import { push, help as pushHelp } from "./src/commands/push"
13
- import { pull, help as pullHelp } from "./src/commands/pull"
14
- import { rebase, help as rebaseHelp } from "./src/commands/rebase"
15
- import { base, help as baseHelp } from "./src/commands/base"
16
- import { switchBranch, help as switchHelp } from "./src/commands/switch"
17
- import { source, help as sourceHelp } from "./src/commands/source"
18
- import { merge, help as mergeHelp } from "./src/commands/merge"
19
- import { template, help as templateHelp } from "./src/commands/template"
20
8
  import { work, help as workHelp } from "./src/commands/work"
21
- import { loop, help as loopHelp } from "./src/commands/loop"
22
9
  import { status, help as statusHelp } from "./src/commands/status"
23
- import {
24
- completions,
25
- help as completionsHelp,
26
- } from "./src/commands/completions"
10
+ import { validate, help as validateHelp } from "./src/commands/validate"
11
+ import { repo, help as repoHelp } from "./src/commands/repo"
12
+ import { epic, help as epicHelp } from "./src/commands/epic"
13
+ import { phase, help as phaseHelp } from "./src/commands/phase"
27
14
  import type { Command } from "./src/types"
28
- import { setColorsEnabled } from "./src/utils/colors"
29
- import { GitService } from "./src/services/GitService"
30
- import { ConfigService } from "./src/services/ConfigService"
31
15
  import { FileSystemService } from "./src/services/FileSystemService"
32
- import { PromptService } from "./src/services/PromptService"
33
- import { TemplateService } from "./src/services/TemplateService"
34
- import { OpencodeService } from "./src/services/OpencodeService"
35
- import { ClaudeService } from "./src/services/ClaudeService"
36
- import { FilterRepoService } from "./src/services/FilterRepoService"
37
- import { FormatterService } from "./src/services/FormatterService"
16
+ import { WorkbaseService } from "./src/services/WorkbaseService"
17
+ import { RepositoryService } from "./src/services/RepositoryService"
18
+ import { EpicService } from "./src/services/EpicService"
19
+ import { TaskService } from "./src/services/TaskService"
20
+ import { PhaseService } from "./src/services/PhaseService"
21
+ import { WorktreeService } from "./src/services/WorktreeService"
22
+ import { PullRequestService } from "./src/services/PullRequestService"
38
23
 
39
24
  // Create CLI layer with all services
40
25
  const CliLayer = Layer.mergeAll(
41
- GitService.Default,
42
- ConfigService.Default,
43
26
  FileSystemService.Default,
44
- PromptService.Default,
45
- TemplateService.Default,
46
- OpencodeService.Default,
47
- ClaudeService.Default,
48
- FilterRepoService.Default,
49
- FormatterService.Default,
27
+ WorkbaseService.Default,
28
+ RepositoryService.Default,
29
+ EpicService.Default,
30
+ TaskService.Default,
31
+ PhaseService.Default,
32
+ WorktreeService.Default,
33
+ PullRequestService.Default,
50
34
  )
51
35
 
52
36
  /**
@@ -61,31 +45,28 @@ async function runCommand<E>(
61
45
  never
62
46
  >
63
47
 
64
- // Catch typed errors and convert to standard Error objects
65
- const programWithErrorHandling = Effect.catchAll(providedEffect, (error) => {
66
- // Convert typed errors to standard Error objects with clear messages
48
+ const toError = (error: unknown) => {
67
49
  if (error instanceof Error) {
68
- return Effect.fail(error)
50
+ return error
69
51
  }
70
- // Handle objects with message property (common pattern for tagged errors)
71
52
  if (
72
53
  typeof error === "object" &&
73
54
  error !== null &&
74
55
  "message" in error &&
75
56
  typeof error.message === "string"
76
57
  ) {
77
- return Effect.fail(new Error(error.message))
58
+ return new Error(error.message)
78
59
  }
79
- // Fallback: convert to string
80
- return Effect.fail(new Error(String(error)))
81
- })
60
+ return new Error(String(error))
61
+ }
82
62
 
83
- // Catch defects (unexpected crashes) and convert to errors
84
- const program = Effect.catchAllDefect(programWithErrorHandling, (defect) =>
85
- Effect.fail(defect instanceof Error ? defect : new Error(String(defect))),
63
+ const result = await Effect.runPromise(
64
+ providedEffect.pipe(
65
+ Effect.catchAllDefect((defect) => Effect.fail(toError(defect))),
66
+ Effect.either,
67
+ ),
86
68
  )
87
-
88
- await Effect.runPromise(program)
69
+ if (Either.isLeft(result)) throw toError(result.left)
89
70
  }
90
71
 
91
72
  // Read version from package.json
@@ -97,320 +78,143 @@ const VERSION = packageJson.version
97
78
  // Define commands
98
79
  const commands: Record<string, Command> = {
99
80
  init: {
100
- name: "init",
101
- description: "Initialize agency with template selection",
102
- run: async (_args: string[], options: Record<string, any>) => {
81
+ run: async (args: string[], options: Record<string, any>) => {
103
82
  if (options.help) {
104
83
  console.log(initHelp)
105
84
  return
106
85
  }
107
86
  await runCommand(
108
87
  init({
109
- template: options.template,
88
+ path: args[0],
89
+ json: options.json,
110
90
  silent: options.silent,
111
91
  verbose: options.verbose,
112
92
  }),
113
93
  )
114
94
  },
115
- help: initHelp,
116
95
  },
117
- emit: {
118
- name: "emit",
119
- description: "Emit a branch without backpack files",
96
+ epic: {
120
97
  run: async (args: string[], options: Record<string, any>) => {
121
98
  if (options.help) {
122
- console.log(emitHelp)
99
+ console.log(epicHelp)
123
100
  return
124
101
  }
125
102
  await runCommand(
126
- emit({
127
- baseBranch: args[0],
128
- emit: options.emit || options.branch,
103
+ epic({
104
+ subcommand: args[0],
105
+ args: args.slice(1),
106
+ ticketUrl: options["ticket-url"],
107
+ description: options.description,
108
+ repos: options.repo,
109
+ json: options.json,
129
110
  silent: options.silent,
130
- force: options.verbose,
131
111
  verbose: options.verbose,
132
112
  }),
133
113
  )
134
114
  },
135
- help: emitHelp,
136
- },
137
- emitted: {
138
- name: "emitted",
139
- description: "Get the name of the emitted branch",
140
- run: async (_args: string[], options: Record<string, any>) => {
141
- if (options.help) {
142
- console.log(emittedHelp)
143
- return
144
- }
145
- await runCommand(
146
- emitted({ silent: options.silent, verbose: options.verbose }),
147
- )
148
- },
149
- help: emittedHelp,
150
115
  },
151
116
  pr: {
152
- name: "pr",
153
- description: "Run gh pr with the emitted branch name",
154
- run: async (
155
- _args: string[],
156
- options: Record<string, any>,
157
- rawArgs?: string[],
158
- ) => {
117
+ run: async (args: string[], options: Record<string, any>) => {
159
118
  if (options.help) {
160
119
  console.log(prHelp)
161
120
  return
162
121
  }
163
- // Pass raw args (after filtering agency flags) directly to gh pr
164
- // This allows flags like --web to pass through without needing --
165
- const agencyFlags = ["--help", "-h", "--silent", "-s", "--verbose", "-v"]
166
- const filteredArgs = (rawArgs ?? []).filter(
167
- (arg) => !agencyFlags.includes(arg),
168
- )
169
122
  await runCommand(
170
123
  pr({
171
- args: filteredArgs,
172
- silent: options.silent,
173
- verbose: options.verbose,
174
- }),
175
- )
176
- },
177
- help: prHelp,
178
- },
179
- push: {
180
- name: "push",
181
- description: "Emit, push to remote, return to source",
182
- run: async (args: string[], options: Record<string, any>) => {
183
- if (options.help) {
184
- console.log(pushHelp)
185
- return
186
- }
187
- await runCommand(
188
- push({
189
- baseBranch: args[0],
190
- emit: options.emit || options.branch,
191
- silent: options.silent,
192
- force: options.force,
193
- noVerify: options["no-verify"],
194
- verbose: options.verbose,
195
- pr: options.pr,
196
- }),
197
- )
198
- },
199
- help: pushHelp,
200
- },
201
- pull: {
202
- name: "pull",
203
- description: "Pull commits from remote emit branch to source",
204
- run: async (_args: string[], options: Record<string, any>) => {
205
- if (options.help) {
206
- console.log(pullHelp)
207
- return
208
- }
209
- await runCommand(
210
- pull({
211
- remote: options.remote,
212
- silent: options.silent,
213
- verbose: options.verbose,
214
- }),
215
- )
216
- },
217
- help: pullHelp,
218
- },
219
- rebase: {
220
- name: "rebase",
221
- description: "Rebase source branch onto base branch",
222
- run: async (args: string[], options: Record<string, any>) => {
223
- if (options.help) {
224
- console.log(rebaseHelp)
225
- return
226
- }
227
- await runCommand(
228
- rebase({
229
- baseBranch: args[0],
230
- emit: options.emit || options.branch,
124
+ subcommand: args[0],
125
+ taskId: args[1],
126
+ phaseId: args[2],
127
+ draft: options.draft,
128
+ json: options.json,
231
129
  silent: options.silent,
232
130
  verbose: options.verbose,
233
131
  }),
234
132
  )
235
133
  },
236
- help: rebaseHelp,
237
134
  },
238
- template: {
239
- name: "template",
240
- description: "Template management commands",
135
+ phase: {
241
136
  run: async (args: string[], options: Record<string, any>) => {
242
- if (options.help) {
243
- console.log(templateHelp)
244
- return
245
- }
137
+ if (options.help) return console.log(phaseHelp)
246
138
  await runCommand(
247
- template({
139
+ phase({
248
140
  subcommand: args[0],
249
141
  args: args.slice(1),
142
+ description: options.description,
143
+ repo: options.repo?.[0],
144
+ references: options.reference,
145
+ branch: options.branch,
146
+ base: options.base,
147
+ dependsOn: options["depends-on"],
148
+ firstPhase: options["first-phase"],
149
+ json: options.json,
250
150
  silent: options.silent,
251
151
  verbose: options.verbose,
252
- template: options.template,
253
152
  }),
254
153
  )
255
154
  },
256
- help: templateHelp,
257
155
  },
258
- base: {
259
- name: "base",
260
- description: "Get or set the base branch",
156
+ repo: {
261
157
  run: async (args: string[], options: Record<string, any>) => {
262
158
  if (options.help) {
263
- console.log(baseHelp)
159
+ console.log(repoHelp)
264
160
  return
265
161
  }
266
162
  await runCommand(
267
- base({
163
+ repo({
268
164
  subcommand: args[0],
269
165
  args: args.slice(1),
270
- repo: options.repo,
271
- silent: options.silent,
272
- verbose: options.verbose,
273
- }),
274
- )
275
- },
276
- help: baseHelp,
277
- },
278
- switch: {
279
- name: "switch",
280
- description: "Toggle between source and emitted branch",
281
- run: async (_args: string[], options: Record<string, any>) => {
282
- if (options.help) {
283
- console.log(switchHelp)
284
- return
285
- }
286
- await runCommand(
287
- switchBranch({ silent: options.silent, verbose: options.verbose }),
288
- )
289
- },
290
- help: switchHelp,
291
- },
292
- source: {
293
- name: "source",
294
- description: "Switch to source branch from emitted branch",
295
- run: async (_args: string[], options: Record<string, any>) => {
296
- if (options.help) {
297
- console.log(sourceHelp)
298
- return
299
- }
300
- await runCommand(
301
- source({ silent: options.silent, verbose: options.verbose }),
302
- )
303
- },
304
- help: sourceHelp,
305
- },
306
- merge: {
307
- name: "merge",
308
- description: "Merge emitted branch into base branch",
309
- run: async (_args: string[], options: Record<string, any>) => {
310
- if (options.help) {
311
- console.log(mergeHelp)
312
- return
313
- }
314
- await runCommand(
315
- merge({
316
166
  silent: options.silent,
317
167
  verbose: options.verbose,
318
- squash: options.squash,
319
- push: options.push,
168
+ json: options.json,
320
169
  }),
321
170
  )
322
171
  },
323
- help: mergeHelp,
324
172
  },
325
173
  task: {
326
- name: "task",
327
- description: "Task management commands",
328
174
  run: async (args: string[], options: Record<string, any>) => {
329
175
  if (options.help) {
330
176
  console.log(taskHelp)
331
177
  return
332
178
  }
333
- // Initialize with optional branch name
334
- const branch = args[0] || options.emit || options.branch
335
179
  await runCommand(
336
180
  task({
337
- emit: branch,
338
- silent: options.silent,
339
- verbose: options.verbose,
340
- task: options.task,
341
- from: options.from,
342
- fromCurrent: options["from-current"],
343
- continue: options.continue,
344
- squash: options.squash,
345
- }),
346
- )
347
- },
348
- help: taskHelp,
349
- },
350
- tasks: {
351
- name: "tasks",
352
- description: "List all task branches",
353
- run: async (_args: string[], options: Record<string, any>) => {
354
- if (options.help) {
355
- console.log(tasksHelp)
356
- return
357
- }
358
- await runCommand(
359
- tasks({
360
- silent: options.silent,
361
- verbose: options.verbose,
181
+ subcommand: args[0],
182
+ args: args.slice(1),
183
+ ticketUrl: options["ticket-url"],
184
+ description: options.description,
185
+ epic: options.epic,
186
+ repo: options.repo?.[0],
187
+ references: options.reference,
188
+ branch: options.branch,
189
+ base: options.base,
190
+ multiPhase: options["multi-phase"],
362
191
  json: options.json,
363
- }),
364
- )
365
- },
366
- help: tasksHelp,
367
- },
368
- edit: {
369
- name: "edit",
370
- description: "Open TASK.md in system editor",
371
- run: async (_args: string[], options: Record<string, any>) => {
372
- if (options.help) {
373
- console.log(editHelp)
374
- return
375
- }
376
- await runCommand(
377
- taskEdit({
378
192
  silent: options.silent,
379
193
  verbose: options.verbose,
380
194
  }),
381
195
  )
382
196
  },
383
- help: editHelp,
384
197
  },
385
198
  work: {
386
- name: "work",
387
- description: "Start working on TASK.md with OpenCode",
388
199
  run: async (args: string[], options: Record<string, any>) => {
389
200
  if (options.help) {
390
201
  console.log(workHelp)
391
202
  return
392
203
  }
393
204
 
394
- // Extract extra args (anything after --)
395
- // Note: parseArgs with strict:false and allowPositionals:true will put
396
- // args after -- in the positionals array
397
- const extraArgs = args.length > 0 ? args : undefined
398
-
399
205
  await runCommand(
400
206
  work({
207
+ taskId: args[0],
208
+ phaseId: args[1],
401
209
  silent: options.silent,
402
210
  verbose: options.verbose,
403
211
  opencode: options.opencode,
404
212
  claude: options.claude,
405
- extraArgs,
406
213
  }),
407
214
  )
408
215
  },
409
- help: workHelp,
410
216
  },
411
217
  status: {
412
- name: "status",
413
- description: "Show agency status for this repository",
414
218
  run: async (_args: string[], options: Record<string, any>) => {
415
219
  if (options.help) {
416
220
  console.log(statusHelp)
@@ -424,66 +228,21 @@ const commands: Record<string, Command> = {
424
228
  }),
425
229
  )
426
230
  },
427
- help: statusHelp,
428
231
  },
429
- clean: {
430
- name: "clean",
431
- description: "Delete branches merged into a specified branch",
232
+ validate: {
432
233
  run: async (_args: string[], options: Record<string, any>) => {
433
234
  if (options.help) {
434
- console.log(cleanHelp)
435
- return
436
- }
437
- await runCommand(
438
- clean({
439
- silent: options.silent,
440
- verbose: options.verbose,
441
- dryRun: options["dry-run"],
442
- mergedInto: options["merged-into"],
443
- }),
444
- )
445
- },
446
- help: cleanHelp,
447
- },
448
- loop: {
449
- name: "loop",
450
- description: "Run a Ralph Wiggum loop to complete all tasks",
451
- run: async (args: string[], options: Record<string, any>) => {
452
- if (options.help) {
453
- console.log(loopHelp)
235
+ console.log(validateHelp)
454
236
  return
455
237
  }
456
-
457
- // Extract extra args (anything after --)
458
- // Note: parseArgs with strict:false and allowPositionals:true will put
459
- // args after -- in the positionals array
460
- const extraArgs = args.length > 0 ? args : undefined
461
-
462
238
  await runCommand(
463
- loop({
239
+ validate({
464
240
  silent: options.silent,
465
241
  verbose: options.verbose,
466
- maxLoops: options["max-loops"],
467
- minLoops: options["min-loops"],
468
- opencode: options.opencode,
469
- claude: options.claude,
470
- extraArgs,
242
+ json: options.json,
471
243
  }),
472
244
  )
473
245
  },
474
- help: loopHelp,
475
- },
476
- completions: {
477
- name: "completions",
478
- description: "Generate shell completion scripts",
479
- run: async (args: string[], options: Record<string, any>) => {
480
- if (options.help) {
481
- console.log(completionsHelp)
482
- return
483
- }
484
- await runCommand(completions(args[0]))
485
- },
486
- help: completionsHelp,
487
246
  },
488
247
  }
489
248
 
@@ -494,55 +253,36 @@ agency v${VERSION}
494
253
  Usage: agency <command> [options]
495
254
 
496
255
  Commands:
497
- init Initialize agency with template selection (run first)
498
- task [branch] Initialize template files on a feature branch
499
- tasks List all task branches
500
- edit Open TASK.md in system editor
501
- work Start working on TASK.md with OpenCode
502
- template Template management commands
503
- use [template] Set template for this repository
504
- save <file|dir> ... Save files/dirs to configured template
505
- list List all files in configured template
506
- view <file> View contents of a file in template
507
- delete <file> ... Delete files from configured template
508
- emit [base-branch] Emit a branch with backpack files reverted
509
- emitted Get the name of the emitted branch
510
- pr <subcommand> Run gh pr with the emitted branch name
511
- push [base-branch] Emit, push to remote, return to source
512
- pull Pull commits from remote emit branch to source
513
- rebase [base-branch] Rebase source branch onto base branch
514
- base Get or set the base branch
515
- set <branch> Set the base branch for the current feature branch
516
- get Get the configured base branch
517
- switch Toggle between source and emitted branch
518
- source Switch to source branch from emitted branch
519
- merge Merge emitted branch into base branch
520
- status Show agency status for this repository
521
- completions <shell> Generate bash or zsh completion scripts
256
+ init [path] Initialize an Agency workbase
257
+ epic <subcommand> Manage epics
258
+ phase <subcommand> Manage task phases
259
+ task <subcommand> Manage tasks
260
+ work <task> [phase] Materialize worktrees and launch an agent
261
+ pr create Create a pull request for an execution unit
262
+ repo <subcommand> Manage workbase repositories
263
+ status Show status for the current workbase
264
+ validate Validate the current workbase
522
265
 
523
266
  Global Options:
524
267
  -h, --help Show help for a command
525
- -v, --version Show version number
526
- --no-color Disable color output
268
+ -V, --version Show version number
527
269
  -s, --silent Suppress output messages
528
270
  -v, --verbose Show verbose output including detailed debugging info
529
271
 
530
272
  Examples:
531
- agency init # Initialize with template (run first)
532
- agency task my-feature # Create 'my-feature' branch from origin/main
533
- agency task --from-current # Initialize on current feature branch
534
- agency emit # Emit a branch (prompts for base branch)
535
- agency switch # Toggle between source and emitted branch
273
+ agency init # Initialize the current directory
274
+ agency task list # List tasks
275
+ agency work refresh-cli-copy # Start working on a task
536
276
 
537
277
  For more information about a command, run:
538
278
  agency <command> --help
539
279
  `)
540
280
  }
541
281
 
542
- // Parse global arguments
543
282
  try {
283
+ const args = process.argv.slice(2)
544
284
  const { values, positionals } = parseArgs({
545
- args: process.argv.slice(2),
285
+ args,
546
286
  options: {
547
287
  help: {
548
288
  type: "boolean",
@@ -550,21 +290,21 @@ try {
550
290
  },
551
291
  version: {
552
292
  type: "boolean",
553
- short: "v",
293
+ short: "V",
554
294
  },
555
- "no-color": {
295
+ silent: {
556
296
  type: "boolean",
297
+ short: "s",
298
+ },
299
+ verbose: {
300
+ type: "boolean",
301
+ short: "v",
557
302
  },
558
303
  },
559
304
  strict: false,
560
305
  allowPositionals: true,
561
306
  })
562
307
 
563
- // Handle --no-color flag
564
- if (values["no-color"]) {
565
- setColorsEnabled(false)
566
- }
567
-
568
308
  // Handle global flags
569
309
  if (values.version) {
570
310
  console.log(`v${VERSION}`)
@@ -577,13 +317,7 @@ try {
577
317
  // Show help if no command
578
318
  if (!commandName) {
579
319
  showMainHelp()
580
- process.exit(1)
581
- }
582
-
583
- // Show main help if --help with no command
584
- if (values.help && !commandName) {
585
- showMainHelp()
586
- process.exit(0)
320
+ process.exit(values.help ? 0 : 1)
587
321
  }
588
322
 
589
323
  // Check if command exists
@@ -594,8 +328,11 @@ try {
594
328
  process.exit(1)
595
329
  }
596
330
 
597
- // Parse command-specific arguments
598
- const commandArgs = process.argv.slice(3)
331
+ const commandIndex = args.indexOf(commandName)
332
+ const commandArgs = [
333
+ ...args.slice(0, commandIndex),
334
+ ...args.slice(commandIndex + 1),
335
+ ]
599
336
  const { values: cmdValues, positionals: cmdPositionals } = parseArgs({
600
337
  args: commandArgs,
601
338
  options: {
@@ -607,64 +344,36 @@ try {
607
344
  type: "boolean",
608
345
  short: "s",
609
346
  },
610
- force: {
611
- type: "boolean",
612
- short: "f",
613
- },
614
- "no-verify": {
615
- type: "boolean",
616
- },
617
347
  verbose: {
618
348
  type: "boolean",
619
349
  short: "v",
620
350
  },
621
- template: {
622
- type: "string",
623
- short: "t",
624
- },
625
- emit: {
626
- type: "string",
627
- },
628
351
  branch: {
629
352
  type: "string",
630
353
  },
631
- task: {
632
- type: "string",
633
- },
634
- from: {
635
- type: "string",
636
- },
637
- "from-current": {
638
- type: "boolean",
639
- },
640
- continue: {
641
- type: "boolean",
642
- },
643
354
  json: {
644
355
  type: "boolean",
645
356
  },
646
- repo: {
647
- type: "boolean",
648
- },
649
- pr: {
650
- type: "boolean",
651
- },
652
- squash: {
653
- type: "boolean",
654
- },
655
- push: {
656
- type: "boolean",
357
+ "ticket-url": {
358
+ type: "string",
657
359
  },
658
- remote: {
360
+ description: {
659
361
  type: "string",
660
- short: "r",
661
362
  },
662
- "merged-into": {
363
+ repo: {
663
364
  type: "string",
365
+ multiple: true,
664
366
  },
665
- "dry-run": {
666
- type: "boolean",
367
+ reference: {
368
+ type: "string",
369
+ multiple: true,
667
370
  },
371
+ epic: { type: "string" },
372
+ base: { type: "string" },
373
+ "multi-phase": { type: "boolean" },
374
+ "depends-on": { type: "string", multiple: true },
375
+ "first-phase": { type: "string" },
376
+ draft: { type: "boolean" },
668
377
  opencode: {
669
378
  type: "boolean",
670
379
  },
@@ -676,8 +385,7 @@ try {
676
385
  allowPositionals: true,
677
386
  })
678
387
 
679
- // Run the command, passing raw args for commands that need them (like pr)
680
- await command.run(cmdPositionals, cmdValues, commandArgs)
388
+ await command.run(cmdPositionals, cmdValues)
681
389
  } catch (error) {
682
390
  if (error instanceof Error) {
683
391
  let message = error.message