@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
@@ -1,226 +0,0 @@
1
- import { test, expect, describe, beforeEach, afterEach } from "bun:test"
2
- import { join } from "path"
3
- import { emitted } from "./emitted"
4
- import {
5
- createTempDir,
6
- cleanupTempDir,
7
- initGitRepo,
8
- getCurrentBranch,
9
- createCommit,
10
- checkoutBranch,
11
- runTestEffect,
12
- } from "../test-utils"
13
-
14
- async function createBranch(cwd: string, branchName: string): Promise<void> {
15
- await Bun.spawn(["git", "checkout", "-b", branchName], {
16
- cwd,
17
- stdout: "pipe",
18
- stderr: "pipe",
19
- }).exited
20
- }
21
-
22
- describe("emitted command", () => {
23
- let tempDir: string
24
- let originalCwd: string
25
-
26
- beforeEach(async () => {
27
- tempDir = await createTempDir()
28
- originalCwd = process.cwd()
29
- process.chdir(tempDir)
30
-
31
- // Set config path to non-existent file to use defaults
32
- process.env.AGENCY_CONFIG_PATH = join(tempDir, "non-existent-config.json")
33
-
34
- // Initialize git repo
35
- await initGitRepo(tempDir)
36
- await createCommit(tempDir, "Initial commit")
37
-
38
- // Rename to main if needed
39
- const currentBranch = await getCurrentBranch(tempDir)
40
- if (currentBranch === "master") {
41
- await Bun.spawn(["git", "branch", "-m", "main"], {
42
- cwd: tempDir,
43
- stdout: "pipe",
44
- stderr: "pipe",
45
- }).exited
46
- }
47
- })
48
-
49
- afterEach(async () => {
50
- process.chdir(originalCwd)
51
- delete process.env.AGENCY_CONFIG_PATH
52
- await cleanupTempDir(tempDir)
53
- })
54
-
55
- describe("basic functionality", () => {
56
- test("returns emit branch name when on source branch", async () => {
57
- // Create source branch (agency--main)
58
- await createBranch(tempDir, "agency--main")
59
- await createCommit(tempDir, "Work on source")
60
-
61
- // Capture output
62
- const originalLog = console.log
63
- let capturedOutput = ""
64
- console.log = (msg: string) => {
65
- capturedOutput = msg
66
- }
67
-
68
- await runTestEffect(emitted({ silent: false }))
69
-
70
- console.log = originalLog
71
- expect(capturedOutput).toBe("main")
72
- })
73
-
74
- test("returns emit branch name when on emit branch", async () => {
75
- // Create source branch
76
- await createBranch(tempDir, "agency--main")
77
- await createCommit(tempDir, "Work on source")
78
-
79
- // Switch to emit branch (main)
80
- await checkoutBranch(tempDir, "main")
81
-
82
- // Capture output
83
- const originalLog = console.log
84
- let capturedOutput = ""
85
- console.log = (msg: string) => {
86
- capturedOutput = msg
87
- }
88
-
89
- await runTestEffect(emitted({ silent: false }))
90
-
91
- console.log = originalLog
92
- expect(capturedOutput).toBe("main")
93
- })
94
-
95
- test("returns emit branch with custom pattern", async () => {
96
- // Create custom config
97
- const configPath = join(tempDir, "custom-config.json")
98
- await Bun.write(
99
- configPath,
100
- JSON.stringify({
101
- sourceBranchPattern: "agency--%branch%",
102
- emitBranch: "%branch%--PR",
103
- }),
104
- )
105
- process.env.AGENCY_CONFIG_PATH = configPath
106
-
107
- // Create source branch
108
- await createBranch(tempDir, "agency--feature")
109
- await createCommit(tempDir, "Feature work")
110
-
111
- // Capture output
112
- const originalLog = console.log
113
- let capturedOutput = ""
114
- console.log = (msg: string) => {
115
- capturedOutput = msg
116
- }
117
-
118
- await runTestEffect(emitted({ silent: false }))
119
-
120
- console.log = originalLog
121
- expect(capturedOutput).toBe("feature--PR")
122
- })
123
-
124
- test("returns emit branch from agency.json when present", async () => {
125
- // Create source branch
126
- await createBranch(tempDir, "agency--feature")
127
-
128
- // Create agency.json with custom emitBranch
129
- const agencyJsonPath = join(tempDir, "agency.json")
130
- await Bun.write(
131
- agencyJsonPath,
132
- JSON.stringify({
133
- version: 1,
134
- injectedFiles: [],
135
- template: "test-template",
136
- createdAt: new Date().toISOString(),
137
- emitBranch: "custom-emit-name",
138
- }),
139
- )
140
-
141
- // Commit the agency.json file so it's available on the branch
142
- await Bun.spawn(["git", "add", "agency.json"], {
143
- cwd: tempDir,
144
- stdout: "pipe",
145
- stderr: "pipe",
146
- }).exited
147
- await createCommit(tempDir, "Add agency.json")
148
-
149
- // Capture output
150
- const originalLog = console.log
151
- let capturedOutput = ""
152
- console.log = (msg: string) => {
153
- capturedOutput = msg
154
- }
155
-
156
- await runTestEffect(emitted({ silent: false }))
157
-
158
- console.log = originalLog
159
- expect(capturedOutput).toBe("custom-emit-name")
160
- })
161
-
162
- test("works with legacy branch names", async () => {
163
- // On a branch that doesn't match the source pattern
164
- // Should treat it as a legacy branch and apply emit pattern
165
- await createBranch(tempDir, "feature-foo")
166
-
167
- // Capture output
168
- const originalLog = console.log
169
- let capturedOutput = ""
170
- console.log = (msg: string) => {
171
- capturedOutput = msg
172
- }
173
-
174
- await runTestEffect(emitted({ silent: false }))
175
-
176
- console.log = originalLog
177
- // With default pattern "%branch%", emit should be the same as clean branch
178
- expect(capturedOutput).toBe("feature-foo")
179
- })
180
- })
181
-
182
- describe("silent mode", () => {
183
- test("still outputs when not in silent mode", async () => {
184
- await createBranch(tempDir, "agency--main")
185
-
186
- const originalLog = console.log
187
- let logCalled = false
188
- console.log = () => {
189
- logCalled = true
190
- }
191
-
192
- await runTestEffect(emitted({ silent: false }))
193
-
194
- console.log = originalLog
195
- expect(logCalled).toBe(true)
196
- })
197
-
198
- test("silent flag suppresses output", async () => {
199
- await createBranch(tempDir, "agency--main")
200
-
201
- const originalLog = console.log
202
- let logCalled = false
203
- console.log = () => {
204
- logCalled = true
205
- }
206
-
207
- await runTestEffect(emitted({ silent: true }))
208
-
209
- console.log = originalLog
210
- expect(logCalled).toBe(false)
211
- })
212
- })
213
-
214
- describe("error handling", () => {
215
- test("throws error when not in a git repository", async () => {
216
- const nonGitDir = await createTempDir()
217
- process.chdir(nonGitDir)
218
-
219
- await expect(runTestEffect(emitted({ silent: true }))).rejects.toThrow(
220
- "Not in a git repository",
221
- )
222
-
223
- await cleanupTempDir(nonGitDir)
224
- })
225
- })
226
- })
@@ -1,57 +0,0 @@
1
- import { Effect } from "effect"
2
- import type { BaseCommandOptions } from "../utils/command"
3
- import { GitService } from "../services/GitService"
4
- import { ConfigService } from "../services/ConfigService"
5
- import {
6
- resolveBranchPairWithAgencyJson,
7
- type BranchPair,
8
- } from "../utils/pr-branch"
9
- import { createLoggers, ensureGitRepo } from "../utils/effect"
10
-
11
- interface EmittedOptions extends BaseCommandOptions {}
12
-
13
- export const emitted = (options: EmittedOptions = {}) =>
14
- Effect.gen(function* () {
15
- const { log } = createLoggers(options)
16
-
17
- const git = yield* GitService
18
- const configService = yield* ConfigService
19
-
20
- const gitRoot = yield* ensureGitRepo()
21
-
22
- // Load config
23
- const config = yield* configService.loadConfig()
24
-
25
- // Get current branch and resolve the branch pair
26
- const currentBranch = yield* git.getCurrentBranch(gitRoot)
27
- const branches: BranchPair = yield* resolveBranchPairWithAgencyJson(
28
- gitRoot,
29
- currentBranch,
30
- config.sourceBranchPattern,
31
- config.emitBranch,
32
- )
33
-
34
- // Return the emit branch name (whether we're on it or not)
35
- log(branches.emitBranch)
36
- })
37
-
38
- export const help = `
39
- Usage: agency emitted [options]
40
-
41
- Get the name of the emitted branch (or what it would be).
42
-
43
- This command shows the emit branch name corresponding to your current branch:
44
- - If on a source branch (e.g., agency--main), shows the emit branch (e.g., main)
45
- - If on an emit branch (e.g., main), shows the current branch name
46
-
47
- This is useful for scripting and automation where you need to know
48
- the emit branch name without actually creating or switching to it.
49
-
50
- Example:
51
- agency emitted # Show the emit branch name
52
-
53
- Notes:
54
- - Does not require the emit branch to exist
55
- - Uses source and emit patterns from ~/.config/agency/agency.json
56
- - Respects emitBranch field in agency.json when present
57
- `