@markjaquith/agency 1.11.1 → 2.1.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 (132) hide show
  1. package/README.md +313 -117
  2. package/cli.ts +124 -420
  3. package/index.ts +1 -1
  4. package/package.json +12 -17
  5. package/skills/agency/SKILL.md +266 -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 +326 -399
  24. package/src/commands/work.ts +153 -153
  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/work-target.test.ts +77 -0
  53. package/src/workbase/work-target.ts +180 -0
  54. package/src/workbase/worktree-command.test.ts +61 -0
  55. package/src/workbase/worktree-command.ts +52 -0
  56. package/src/commands/base.test.ts +0 -198
  57. package/src/commands/base.ts +0 -198
  58. package/src/commands/clean.test.ts +0 -299
  59. package/src/commands/clean.ts +0 -274
  60. package/src/commands/completions.test.ts +0 -51
  61. package/src/commands/completions.ts +0 -308
  62. package/src/commands/emit.integration.test.ts +0 -549
  63. package/src/commands/emit.test.ts +0 -576
  64. package/src/commands/emit.ts +0 -700
  65. package/src/commands/emitted.test.ts +0 -226
  66. package/src/commands/emitted.ts +0 -57
  67. package/src/commands/loop.test.ts +0 -621
  68. package/src/commands/loop.ts +0 -406
  69. package/src/commands/merge.integration.test.ts +0 -195
  70. package/src/commands/merge.test.ts +0 -246
  71. package/src/commands/merge.ts +0 -244
  72. package/src/commands/pull.test.ts +0 -395
  73. package/src/commands/pull.ts +0 -205
  74. package/src/commands/push.test.ts +0 -641
  75. package/src/commands/push.ts +0 -482
  76. package/src/commands/rebase.test.ts +0 -521
  77. package/src/commands/rebase.ts +0 -232
  78. package/src/commands/save.test.ts +0 -247
  79. package/src/commands/save.ts +0 -162
  80. package/src/commands/source.test.ts +0 -195
  81. package/src/commands/source.ts +0 -72
  82. package/src/commands/switch.test.ts +0 -194
  83. package/src/commands/switch.ts +0 -84
  84. package/src/commands/task-branching.test.ts +0 -633
  85. package/src/commands/task-continue.test.ts +0 -311
  86. package/src/commands/task-edit.test.ts +0 -235
  87. package/src/commands/task-main.test.ts +0 -1066
  88. package/src/commands/task-squash.test.ts +0 -266
  89. package/src/commands/tasks.test.ts +0 -391
  90. package/src/commands/tasks.ts +0 -161
  91. package/src/commands/template-delete.test.ts +0 -178
  92. package/src/commands/template-delete.ts +0 -98
  93. package/src/commands/template-list.test.ts +0 -135
  94. package/src/commands/template-list.ts +0 -87
  95. package/src/commands/template-view.test.ts +0 -158
  96. package/src/commands/template-view.ts +0 -86
  97. package/src/commands/template.test.ts +0 -32
  98. package/src/commands/template.ts +0 -96
  99. package/src/commands/use.test.ts +0 -87
  100. package/src/commands/use.ts +0 -97
  101. package/src/constants.ts +0 -10
  102. package/src/errors.ts +0 -17
  103. package/src/schemas.ts +0 -33
  104. package/src/services/AgencyMetadataService.ts +0 -311
  105. package/src/services/ClaudeService.test.ts +0 -184
  106. package/src/services/ClaudeService.ts +0 -91
  107. package/src/services/ConfigService.ts +0 -115
  108. package/src/services/FilterRepoService.ts +0 -164
  109. package/src/services/FormatterService.test.ts +0 -432
  110. package/src/services/FormatterService.ts +0 -219
  111. package/src/services/GitService.ts +0 -1187
  112. package/src/services/MockFilterRepoService.ts +0 -140
  113. package/src/services/OpencodeService.ts +0 -263
  114. package/src/services/PromptService.ts +0 -183
  115. package/src/services/TemplateService.ts +0 -81
  116. package/src/types/native-exec.d.ts +0 -8
  117. package/src/utils/colors.ts +0 -178
  118. package/src/utils/git-path.ts +0 -6
  119. package/src/utils/glob.test.ts +0 -161
  120. package/src/utils/glob.ts +0 -78
  121. package/src/utils/paths.ts +0 -51
  122. package/src/utils/pr-branch.test.ts +0 -376
  123. package/src/utils/pr-branch.ts +0 -508
  124. package/src/utils/spinner.test.ts +0 -45
  125. package/src/utils/spinner.ts +0 -96
  126. package/src/utils/task-parser.test.ts +0 -172
  127. package/src/utils/task-parser.ts +0 -93
  128. package/templates/AGENCY.md +0 -22
  129. package/templates/AGENTS.md +0 -11
  130. package/templates/CLAUDE.md +0 -3
  131. package/templates/TASK.md +0 -5
  132. 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
- `