@markjaquith/agency 1.11.1 → 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 -420
  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 -641
  73. package/src/commands/push.ts +0 -482
  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 -1187
  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,322 +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: "Push the emitted branch or fall through to git push",
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
- gitArgs: args,
191
- emit: options.emit || options.branch,
192
- silent: options.silent,
193
- force: options.force,
194
- forceWithLease: options["force-with-lease"],
195
- noVerify: options["no-verify"],
196
- verbose: options.verbose,
197
- pr: options.pr,
198
- }),
199
- )
200
- },
201
- help: pushHelp,
202
- },
203
- pull: {
204
- name: "pull",
205
- description: "Pull commits from remote emit branch to source",
206
- run: async (_args: string[], options: Record<string, any>) => {
207
- if (options.help) {
208
- console.log(pullHelp)
209
- return
210
- }
211
- await runCommand(
212
- pull({
213
- remote: options.remote,
214
- silent: options.silent,
215
- verbose: options.verbose,
216
- }),
217
- )
218
- },
219
- help: pullHelp,
220
- },
221
- rebase: {
222
- name: "rebase",
223
- description: "Rebase source branch onto base branch",
224
- run: async (args: string[], options: Record<string, any>) => {
225
- if (options.help) {
226
- console.log(rebaseHelp)
227
- return
228
- }
229
- await runCommand(
230
- rebase({
231
- baseBranch: args[0],
232
- 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,
233
129
  silent: options.silent,
234
130
  verbose: options.verbose,
235
131
  }),
236
132
  )
237
133
  },
238
- help: rebaseHelp,
239
134
  },
240
- template: {
241
- name: "template",
242
- description: "Template management commands",
135
+ phase: {
243
136
  run: async (args: string[], options: Record<string, any>) => {
244
- if (options.help) {
245
- console.log(templateHelp)
246
- return
247
- }
137
+ if (options.help) return console.log(phaseHelp)
248
138
  await runCommand(
249
- template({
139
+ phase({
250
140
  subcommand: args[0],
251
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,
252
150
  silent: options.silent,
253
151
  verbose: options.verbose,
254
- template: options.template,
255
152
  }),
256
153
  )
257
154
  },
258
- help: templateHelp,
259
155
  },
260
- base: {
261
- name: "base",
262
- description: "Get or set the base branch",
156
+ repo: {
263
157
  run: async (args: string[], options: Record<string, any>) => {
264
158
  if (options.help) {
265
- console.log(baseHelp)
159
+ console.log(repoHelp)
266
160
  return
267
161
  }
268
162
  await runCommand(
269
- base({
163
+ repo({
270
164
  subcommand: args[0],
271
165
  args: args.slice(1),
272
- repo: options.repo,
273
- silent: options.silent,
274
- verbose: options.verbose,
275
- }),
276
- )
277
- },
278
- help: baseHelp,
279
- },
280
- switch: {
281
- name: "switch",
282
- description: "Toggle between source and emitted branch",
283
- run: async (_args: string[], options: Record<string, any>) => {
284
- if (options.help) {
285
- console.log(switchHelp)
286
- return
287
- }
288
- await runCommand(
289
- switchBranch({ silent: options.silent, verbose: options.verbose }),
290
- )
291
- },
292
- help: switchHelp,
293
- },
294
- source: {
295
- name: "source",
296
- description: "Switch to source branch from emitted branch",
297
- run: async (_args: string[], options: Record<string, any>) => {
298
- if (options.help) {
299
- console.log(sourceHelp)
300
- return
301
- }
302
- await runCommand(
303
- source({ silent: options.silent, verbose: options.verbose }),
304
- )
305
- },
306
- help: sourceHelp,
307
- },
308
- merge: {
309
- name: "merge",
310
- description: "Merge emitted branch into base branch",
311
- run: async (_args: string[], options: Record<string, any>) => {
312
- if (options.help) {
313
- console.log(mergeHelp)
314
- return
315
- }
316
- await runCommand(
317
- merge({
318
166
  silent: options.silent,
319
167
  verbose: options.verbose,
320
- squash: options.squash,
321
- push: options.push,
168
+ json: options.json,
322
169
  }),
323
170
  )
324
171
  },
325
- help: mergeHelp,
326
172
  },
327
173
  task: {
328
- name: "task",
329
- description: "Task management commands",
330
174
  run: async (args: string[], options: Record<string, any>) => {
331
175
  if (options.help) {
332
176
  console.log(taskHelp)
333
177
  return
334
178
  }
335
- // Initialize with optional branch name
336
- const branch = args[0] || options.emit || options.branch
337
179
  await runCommand(
338
180
  task({
339
- emit: branch,
340
- silent: options.silent,
341
- verbose: options.verbose,
342
- task: options.task,
343
- from: options.from,
344
- fromCurrent: options["from-current"],
345
- continue: options.continue,
346
- squash: options.squash,
347
- }),
348
- )
349
- },
350
- help: taskHelp,
351
- },
352
- tasks: {
353
- name: "tasks",
354
- description: "List all task branches",
355
- run: async (_args: string[], options: Record<string, any>) => {
356
- if (options.help) {
357
- console.log(tasksHelp)
358
- return
359
- }
360
- await runCommand(
361
- tasks({
362
- silent: options.silent,
363
- 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"],
364
191
  json: options.json,
365
- }),
366
- )
367
- },
368
- help: tasksHelp,
369
- },
370
- edit: {
371
- name: "edit",
372
- description: "Open TASK.md in system editor",
373
- run: async (_args: string[], options: Record<string, any>) => {
374
- if (options.help) {
375
- console.log(editHelp)
376
- return
377
- }
378
- await runCommand(
379
- taskEdit({
380
192
  silent: options.silent,
381
193
  verbose: options.verbose,
382
194
  }),
383
195
  )
384
196
  },
385
- help: editHelp,
386
197
  },
387
198
  work: {
388
- name: "work",
389
- description: "Start working on TASK.md with OpenCode",
390
199
  run: async (args: string[], options: Record<string, any>) => {
391
200
  if (options.help) {
392
201
  console.log(workHelp)
393
202
  return
394
203
  }
395
204
 
396
- // Extract extra args (anything after --)
397
- // Note: parseArgs with strict:false and allowPositionals:true will put
398
- // args after -- in the positionals array
399
- const extraArgs = args.length > 0 ? args : undefined
400
-
401
205
  await runCommand(
402
206
  work({
207
+ taskId: args[0],
208
+ phaseId: args[1],
403
209
  silent: options.silent,
404
210
  verbose: options.verbose,
405
211
  opencode: options.opencode,
406
212
  claude: options.claude,
407
- extraArgs,
408
213
  }),
409
214
  )
410
215
  },
411
- help: workHelp,
412
216
  },
413
217
  status: {
414
- name: "status",
415
- description: "Show agency status for this repository",
416
218
  run: async (_args: string[], options: Record<string, any>) => {
417
219
  if (options.help) {
418
220
  console.log(statusHelp)
@@ -426,66 +228,21 @@ const commands: Record<string, Command> = {
426
228
  }),
427
229
  )
428
230
  },
429
- help: statusHelp,
430
231
  },
431
- clean: {
432
- name: "clean",
433
- description: "Delete branches merged into a specified branch",
232
+ validate: {
434
233
  run: async (_args: string[], options: Record<string, any>) => {
435
234
  if (options.help) {
436
- console.log(cleanHelp)
235
+ console.log(validateHelp)
437
236
  return
438
237
  }
439
238
  await runCommand(
440
- clean({
239
+ validate({
441
240
  silent: options.silent,
442
241
  verbose: options.verbose,
443
- dryRun: options["dry-run"],
444
- mergedInto: options["merged-into"],
445
- }),
446
- )
447
- },
448
- help: cleanHelp,
449
- },
450
- loop: {
451
- name: "loop",
452
- description: "Run a Ralph Wiggum loop to complete all tasks",
453
- run: async (args: string[], options: Record<string, any>) => {
454
- if (options.help) {
455
- console.log(loopHelp)
456
- return
457
- }
458
-
459
- // Extract extra args (anything after --)
460
- // Note: parseArgs with strict:false and allowPositionals:true will put
461
- // args after -- in the positionals array
462
- const extraArgs = args.length > 0 ? args : undefined
463
-
464
- await runCommand(
465
- loop({
466
- silent: options.silent,
467
- verbose: options.verbose,
468
- maxLoops: options["max-loops"],
469
- minLoops: options["min-loops"],
470
- opencode: options.opencode,
471
- claude: options.claude,
472
- extraArgs,
242
+ json: options.json,
473
243
  }),
474
244
  )
475
245
  },
476
- help: loopHelp,
477
- },
478
- completions: {
479
- name: "completions",
480
- description: "Generate shell completion scripts",
481
- run: async (args: string[], options: Record<string, any>) => {
482
- if (options.help) {
483
- console.log(completionsHelp)
484
- return
485
- }
486
- await runCommand(completions(args[0]))
487
- },
488
- help: completionsHelp,
489
246
  },
490
247
  }
491
248
 
@@ -496,55 +253,36 @@ agency v${VERSION}
496
253
  Usage: agency <command> [options]
497
254
 
498
255
  Commands:
499
- init Initialize agency with template selection (run first)
500
- task [branch] Initialize template files on a feature branch
501
- tasks List all task branches
502
- edit Open TASK.md in system editor
503
- work Start working on TASK.md with OpenCode
504
- template Template management commands
505
- use [template] Set template for this repository
506
- save <file|dir> ... Save files/dirs to configured template
507
- list List all files in configured template
508
- view <file> View contents of a file in template
509
- delete <file> ... Delete files from configured template
510
- emit [base-branch] Emit a branch with backpack files reverted
511
- emitted Get the name of the emitted branch
512
- pr <subcommand> Run gh pr with the emitted branch name
513
- push [arguments] Push the emitted branch or fall through to git push
514
- pull Pull commits from remote emit branch to source
515
- rebase [base-branch] Rebase source branch onto base branch
516
- base Get or set the base branch
517
- set <branch> Set the base branch for the current feature branch
518
- get Get the configured base branch
519
- switch Toggle between source and emitted branch
520
- source Switch to source branch from emitted branch
521
- merge Merge emitted branch into base branch
522
- status Show agency status for this repository
523
- 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
524
265
 
525
266
  Global Options:
526
267
  -h, --help Show help for a command
527
- -v, --version Show version number
528
- --no-color Disable color output
268
+ -V, --version Show version number
529
269
  -s, --silent Suppress output messages
530
270
  -v, --verbose Show verbose output including detailed debugging info
531
271
 
532
272
  Examples:
533
- agency init # Initialize with template (run first)
534
- agency task my-feature # Create 'my-feature' branch from origin/main
535
- agency task --from-current # Initialize on current feature branch
536
- agency emit # Emit a branch (prompts for base branch)
537
- 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
538
276
 
539
277
  For more information about a command, run:
540
278
  agency <command> --help
541
279
  `)
542
280
  }
543
281
 
544
- // Parse global arguments
545
282
  try {
283
+ const args = process.argv.slice(2)
546
284
  const { values, positionals } = parseArgs({
547
- args: process.argv.slice(2),
285
+ args,
548
286
  options: {
549
287
  help: {
550
288
  type: "boolean",
@@ -552,21 +290,21 @@ try {
552
290
  },
553
291
  version: {
554
292
  type: "boolean",
555
- short: "v",
293
+ short: "V",
556
294
  },
557
- "no-color": {
295
+ silent: {
558
296
  type: "boolean",
297
+ short: "s",
298
+ },
299
+ verbose: {
300
+ type: "boolean",
301
+ short: "v",
559
302
  },
560
303
  },
561
304
  strict: false,
562
305
  allowPositionals: true,
563
306
  })
564
307
 
565
- // Handle --no-color flag
566
- if (values["no-color"]) {
567
- setColorsEnabled(false)
568
- }
569
-
570
308
  // Handle global flags
571
309
  if (values.version) {
572
310
  console.log(`v${VERSION}`)
@@ -579,13 +317,7 @@ try {
579
317
  // Show help if no command
580
318
  if (!commandName) {
581
319
  showMainHelp()
582
- process.exit(1)
583
- }
584
-
585
- // Show main help if --help with no command
586
- if (values.help && !commandName) {
587
- showMainHelp()
588
- process.exit(0)
320
+ process.exit(values.help ? 0 : 1)
589
321
  }
590
322
 
591
323
  // Check if command exists
@@ -596,8 +328,11 @@ try {
596
328
  process.exit(1)
597
329
  }
598
330
 
599
- // Parse command-specific arguments
600
- 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
+ ]
601
336
  const { values: cmdValues, positionals: cmdPositionals } = parseArgs({
602
337
  args: commandArgs,
603
338
  options: {
@@ -609,67 +344,36 @@ try {
609
344
  type: "boolean",
610
345
  short: "s",
611
346
  },
612
- force: {
613
- type: "boolean",
614
- short: "f",
615
- },
616
- "force-with-lease": {
617
- type: "boolean",
618
- },
619
- "no-verify": {
620
- type: "boolean",
621
- },
622
347
  verbose: {
623
348
  type: "boolean",
624
349
  short: "v",
625
350
  },
626
- template: {
627
- type: "string",
628
- short: "t",
629
- },
630
- emit: {
631
- type: "string",
632
- },
633
351
  branch: {
634
352
  type: "string",
635
353
  },
636
- task: {
637
- type: "string",
638
- },
639
- from: {
640
- type: "string",
641
- },
642
- "from-current": {
643
- type: "boolean",
644
- },
645
- continue: {
646
- type: "boolean",
647
- },
648
354
  json: {
649
355
  type: "boolean",
650
356
  },
651
- repo: {
652
- type: "boolean",
653
- },
654
- pr: {
655
- type: "boolean",
656
- },
657
- squash: {
658
- type: "boolean",
659
- },
660
- push: {
661
- type: "boolean",
357
+ "ticket-url": {
358
+ type: "string",
662
359
  },
663
- remote: {
360
+ description: {
664
361
  type: "string",
665
- short: "r",
666
362
  },
667
- "merged-into": {
363
+ repo: {
668
364
  type: "string",
365
+ multiple: true,
669
366
  },
670
- "dry-run": {
671
- type: "boolean",
367
+ reference: {
368
+ type: "string",
369
+ multiple: true,
672
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" },
673
377
  opencode: {
674
378
  type: "boolean",
675
379
  },
@@ -681,8 +385,7 @@ try {
681
385
  allowPositionals: true,
682
386
  })
683
387
 
684
- // Run the command, passing raw args for commands that need them (like pr)
685
- await command.run(cmdPositionals, cmdValues, commandArgs)
388
+ await command.run(cmdPositionals, cmdValues)
686
389
  } catch (error) {
687
390
  if (error instanceof Error) {
688
391
  let message = error.message