@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.
- package/README.md +302 -113
- package/cli.ts +123 -415
- package/index.ts +1 -1
- package/package.json +12 -17
- package/skills/agency/SKILL.md +258 -0
- package/src/cli.test.ts +290 -0
- package/src/commands/description.test.ts +103 -0
- package/src/commands/epic.test.ts +103 -0
- package/src/commands/epic.ts +101 -0
- package/src/commands/init.test.ts +42 -290
- package/src/commands/init.ts +15 -123
- package/src/commands/phase.ts +120 -0
- package/src/commands/pr.test.ts +19 -170
- package/src/commands/pr.ts +23 -179
- package/src/commands/repo.test.ts +80 -0
- package/src/commands/repo.ts +83 -0
- package/src/commands/status.test.ts +35 -477
- package/src/commands/status.ts +24 -213
- package/src/commands/task-phase.test.ts +206 -0
- package/src/commands/task.ts +83 -1285
- package/src/commands/validate.test.ts +91 -0
- package/src/commands/validate.ts +50 -0
- package/src/commands/work.test.ts +172 -419
- package/src/commands/work.ts +47 -166
- package/src/services/EpicService.test.ts +71 -0
- package/src/services/EpicService.ts +135 -0
- package/src/services/FileSystemService.ts +61 -92
- package/src/services/PhaseService.ts +323 -0
- package/src/services/PullRequestService.test.ts +386 -0
- package/src/services/PullRequestService.ts +143 -0
- package/src/services/RepositoryService.test.ts +106 -0
- package/src/services/RepositoryService.ts +159 -0
- package/src/services/TaskPhaseService.test.ts +217 -0
- package/src/services/TaskService.ts +192 -0
- package/src/services/WorkbaseService.test.ts +446 -0
- package/src/services/WorkbaseService.ts +493 -0
- package/src/services/WorktreeService.test.ts +599 -0
- package/src/services/WorktreeService.ts +357 -0
- package/src/test-utils.ts +41 -438
- package/src/types.ts +1 -215
- package/src/utils/command.ts +1 -5
- package/src/utils/effect.test.ts +24 -0
- package/src/utils/effect.ts +3 -273
- package/src/utils/process.test.ts +0 -15
- package/src/utils/process.ts +1 -60
- package/src/workbase/frontmatter.test.ts +50 -0
- package/src/workbase/frontmatter.ts +77 -0
- package/src/workbase/repository-reference.test.ts +25 -0
- package/src/workbase/repository-reference.ts +18 -0
- package/src/workbase/schemas.test.ts +146 -0
- package/src/workbase/schemas.ts +78 -0
- package/src/workbase/worktree-command.test.ts +61 -0
- package/src/workbase/worktree-command.ts +52 -0
- package/src/commands/base.test.ts +0 -198
- package/src/commands/base.ts +0 -198
- package/src/commands/clean.test.ts +0 -299
- package/src/commands/clean.ts +0 -274
- package/src/commands/completions.test.ts +0 -51
- package/src/commands/completions.ts +0 -308
- package/src/commands/emit.integration.test.ts +0 -549
- package/src/commands/emit.test.ts +0 -576
- package/src/commands/emit.ts +0 -700
- package/src/commands/emitted.test.ts +0 -226
- package/src/commands/emitted.ts +0 -57
- package/src/commands/loop.test.ts +0 -621
- package/src/commands/loop.ts +0 -406
- package/src/commands/merge.integration.test.ts +0 -195
- package/src/commands/merge.test.ts +0 -246
- package/src/commands/merge.ts +0 -244
- package/src/commands/pull.test.ts +0 -395
- package/src/commands/pull.ts +0 -205
- package/src/commands/push.test.ts +0 -518
- package/src/commands/push.ts +0 -434
- package/src/commands/rebase.test.ts +0 -521
- package/src/commands/rebase.ts +0 -232
- package/src/commands/save.test.ts +0 -247
- package/src/commands/save.ts +0 -162
- package/src/commands/source.test.ts +0 -195
- package/src/commands/source.ts +0 -72
- package/src/commands/switch.test.ts +0 -194
- package/src/commands/switch.ts +0 -84
- package/src/commands/task-branching.test.ts +0 -633
- package/src/commands/task-continue.test.ts +0 -311
- package/src/commands/task-edit.test.ts +0 -235
- package/src/commands/task-main.test.ts +0 -1066
- package/src/commands/task-squash.test.ts +0 -266
- package/src/commands/tasks.test.ts +0 -391
- package/src/commands/tasks.ts +0 -161
- package/src/commands/template-delete.test.ts +0 -178
- package/src/commands/template-delete.ts +0 -98
- package/src/commands/template-list.test.ts +0 -135
- package/src/commands/template-list.ts +0 -87
- package/src/commands/template-view.test.ts +0 -158
- package/src/commands/template-view.ts +0 -86
- package/src/commands/template.test.ts +0 -32
- package/src/commands/template.ts +0 -96
- package/src/commands/use.test.ts +0 -87
- package/src/commands/use.ts +0 -97
- package/src/constants.ts +0 -10
- package/src/errors.ts +0 -17
- package/src/schemas.ts +0 -33
- package/src/services/AgencyMetadataService.ts +0 -311
- package/src/services/ClaudeService.test.ts +0 -184
- package/src/services/ClaudeService.ts +0 -91
- package/src/services/ConfigService.ts +0 -115
- package/src/services/FilterRepoService.ts +0 -164
- package/src/services/FormatterService.test.ts +0 -432
- package/src/services/FormatterService.ts +0 -219
- package/src/services/GitService.ts +0 -1183
- package/src/services/MockFilterRepoService.ts +0 -140
- package/src/services/OpencodeService.ts +0 -263
- package/src/services/PromptService.ts +0 -183
- package/src/services/TemplateService.ts +0 -81
- package/src/types/native-exec.d.ts +0 -8
- package/src/utils/colors.ts +0 -178
- package/src/utils/git-path.ts +0 -6
- package/src/utils/glob.test.ts +0 -161
- package/src/utils/glob.ts +0 -78
- package/src/utils/paths.ts +0 -51
- package/src/utils/pr-branch.test.ts +0 -376
- package/src/utils/pr-branch.ts +0 -508
- package/src/utils/spinner.test.ts +0 -45
- package/src/utils/spinner.ts +0 -96
- package/src/utils/task-parser.test.ts +0 -172
- package/src/utils/task-parser.ts +0 -93
- package/templates/AGENCY.md +0 -22
- package/templates/AGENTS.md +0 -11
- package/templates/CLAUDE.md +0 -3
- package/templates/TASK.md +0 -5
- package/templates/opencode.json +0 -4
package/src/commands/clean.ts
DELETED
|
@@ -1,274 +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 { parseAgencyMetadata } from "../services/AgencyMetadataService"
|
|
6
|
-
import highlight, { done } from "../utils/colors"
|
|
7
|
-
import { createLoggers, ensureGitRepo } from "../utils/effect"
|
|
8
|
-
import { extractCleanBranch, makeSourceBranchName } from "../utils/pr-branch"
|
|
9
|
-
|
|
10
|
-
interface CleanOptions extends BaseCommandOptions {
|
|
11
|
-
dryRun?: boolean
|
|
12
|
-
mergedInto?: string
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Read agency.json metadata from a specific branch without checking it out.
|
|
17
|
-
* Uses `git show` to read the file contents directly.
|
|
18
|
-
*/
|
|
19
|
-
const readAgencyMetadata = (gitRoot: string, branch: string) =>
|
|
20
|
-
Effect.gen(function* () {
|
|
21
|
-
const git = yield* GitService
|
|
22
|
-
|
|
23
|
-
// Use git show to read agency.json from the branch without checking out
|
|
24
|
-
const content = yield* git.getFileAtRef(gitRoot, branch, "agency.json")
|
|
25
|
-
|
|
26
|
-
if (!content) {
|
|
27
|
-
return null
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
return yield* parseAgencyMetadata(content)
|
|
31
|
-
}).pipe(Effect.catchAll(() => Effect.succeed(null)))
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Get all local branches
|
|
35
|
-
*/
|
|
36
|
-
const getAllLocalBranches = (gitRoot: string) =>
|
|
37
|
-
Effect.gen(function* () {
|
|
38
|
-
const git = yield* GitService
|
|
39
|
-
return yield* git.getAllLocalBranches(gitRoot)
|
|
40
|
-
}).pipe(Effect.catchAll(() => Effect.succeed([] as readonly string[])))
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Get all branches that have been fully merged into the specified branch
|
|
44
|
-
*/
|
|
45
|
-
const getBranchesMergedInto = (
|
|
46
|
-
gitRoot: string,
|
|
47
|
-
targetBranch: string,
|
|
48
|
-
options: BaseCommandOptions,
|
|
49
|
-
) =>
|
|
50
|
-
Effect.gen(function* () {
|
|
51
|
-
const git = yield* GitService
|
|
52
|
-
const { verboseLog } = createLoggers(options)
|
|
53
|
-
|
|
54
|
-
verboseLog(
|
|
55
|
-
`Finding branches merged into ${highlight.branch(targetBranch)}...`,
|
|
56
|
-
)
|
|
57
|
-
|
|
58
|
-
const mergedBranches = yield* git.getMergedBranches(gitRoot, targetBranch)
|
|
59
|
-
|
|
60
|
-
verboseLog(`Found ${mergedBranches.length} merged branches`)
|
|
61
|
-
return mergedBranches
|
|
62
|
-
}).pipe(Effect.catchAll(() => Effect.succeed([] as readonly string[])))
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Find source branches for the given branches (emit branches).
|
|
66
|
-
* For each branch, check if there's a corresponding source branch by:
|
|
67
|
-
* 1. Looking for branches with agency.json that has this branch as emitBranch
|
|
68
|
-
* 2. Using the source branch pattern from config
|
|
69
|
-
*/
|
|
70
|
-
const findSourceBranches = (
|
|
71
|
-
gitRoot: string,
|
|
72
|
-
branches: readonly string[],
|
|
73
|
-
sourcePattern: string,
|
|
74
|
-
emitPattern: string,
|
|
75
|
-
options: BaseCommandOptions,
|
|
76
|
-
) =>
|
|
77
|
-
Effect.gen(function* () {
|
|
78
|
-
const git = yield* GitService
|
|
79
|
-
const { verboseLog } = createLoggers(options)
|
|
80
|
-
|
|
81
|
-
const allBranches = yield* getAllLocalBranches(gitRoot)
|
|
82
|
-
const sourceBranches: string[] = []
|
|
83
|
-
|
|
84
|
-
verboseLog(
|
|
85
|
-
`Looking for source branches for ${branches.length} emit branches...`,
|
|
86
|
-
)
|
|
87
|
-
|
|
88
|
-
// For each branch in our list, check if it's an emit branch and find its source
|
|
89
|
-
for (const branch of branches) {
|
|
90
|
-
// Strategy 1: Check all branches for agency.json with matching emitBranch
|
|
91
|
-
for (const candidateBranch of allBranches) {
|
|
92
|
-
if (candidateBranch === branch) continue
|
|
93
|
-
|
|
94
|
-
const metadata = yield* readAgencyMetadata(gitRoot, candidateBranch)
|
|
95
|
-
|
|
96
|
-
if (metadata?.emitBranch === branch) {
|
|
97
|
-
verboseLog(
|
|
98
|
-
`Found source branch ${highlight.branch(candidateBranch)} for emit branch ${highlight.branch(branch)}`,
|
|
99
|
-
)
|
|
100
|
-
if (!sourceBranches.includes(candidateBranch)) {
|
|
101
|
-
sourceBranches.push(candidateBranch)
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// Strategy 2: Try using the source pattern
|
|
107
|
-
// If emitPattern is "%branch%", the branch name itself is the clean name
|
|
108
|
-
const cleanBranch =
|
|
109
|
-
emitPattern === "%branch%"
|
|
110
|
-
? branch
|
|
111
|
-
: extractCleanBranch(branch, emitPattern)
|
|
112
|
-
|
|
113
|
-
if (cleanBranch) {
|
|
114
|
-
const possibleSourceBranch = makeSourceBranchName(
|
|
115
|
-
cleanBranch,
|
|
116
|
-
sourcePattern,
|
|
117
|
-
)
|
|
118
|
-
const sourceExists = yield* git
|
|
119
|
-
.branchExists(gitRoot, possibleSourceBranch)
|
|
120
|
-
.pipe(Effect.catchAll(() => Effect.succeed(false)))
|
|
121
|
-
|
|
122
|
-
if (sourceExists && !sourceBranches.includes(possibleSourceBranch)) {
|
|
123
|
-
verboseLog(
|
|
124
|
-
`Found source branch ${highlight.branch(possibleSourceBranch)} via pattern matching`,
|
|
125
|
-
)
|
|
126
|
-
sourceBranches.push(possibleSourceBranch)
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
verboseLog(`Found ${sourceBranches.length} source branches`)
|
|
132
|
-
return sourceBranches
|
|
133
|
-
})
|
|
134
|
-
|
|
135
|
-
export const clean = (options: CleanOptions = {}) =>
|
|
136
|
-
Effect.gen(function* () {
|
|
137
|
-
const { dryRun = false, mergedInto } = options
|
|
138
|
-
const { log, verboseLog } = createLoggers(options)
|
|
139
|
-
|
|
140
|
-
const git = yield* GitService
|
|
141
|
-
const configService = yield* ConfigService
|
|
142
|
-
const gitRoot = yield* ensureGitRepo()
|
|
143
|
-
|
|
144
|
-
// Require --merged-into flag
|
|
145
|
-
if (!mergedInto) {
|
|
146
|
-
return yield* Effect.fail(
|
|
147
|
-
new Error(
|
|
148
|
-
"The --merged-into flag is required. Specify which branch to check for merged branches (e.g., --merged-into main)",
|
|
149
|
-
),
|
|
150
|
-
)
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// Verify the target branch exists
|
|
154
|
-
const targetExists = yield* git.branchExists(gitRoot, mergedInto)
|
|
155
|
-
if (!targetExists) {
|
|
156
|
-
return yield* Effect.fail(
|
|
157
|
-
new Error(
|
|
158
|
-
`Branch ${highlight.branch(mergedInto)} does not exist. Please specify a valid branch.`,
|
|
159
|
-
),
|
|
160
|
-
)
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// Load config to get source and emit patterns
|
|
164
|
-
const config = yield* configService.loadConfig()
|
|
165
|
-
const sourcePattern = config.sourceBranchPattern || "agency--%branch%"
|
|
166
|
-
const emitPattern = config.emitBranch || "%branch%"
|
|
167
|
-
|
|
168
|
-
verboseLog(
|
|
169
|
-
`Using source pattern: ${sourcePattern}, emit pattern: ${emitPattern}`,
|
|
170
|
-
)
|
|
171
|
-
|
|
172
|
-
// Get all branches merged into target
|
|
173
|
-
const mergedBranches = yield* getBranchesMergedInto(
|
|
174
|
-
gitRoot,
|
|
175
|
-
mergedInto,
|
|
176
|
-
options,
|
|
177
|
-
)
|
|
178
|
-
verboseLog(
|
|
179
|
-
`Found ${mergedBranches.length} branches merged into ${highlight.branch(mergedInto)}`,
|
|
180
|
-
)
|
|
181
|
-
|
|
182
|
-
// Find source branches for the merged branches
|
|
183
|
-
const sourceBranches = yield* findSourceBranches(
|
|
184
|
-
gitRoot,
|
|
185
|
-
mergedBranches,
|
|
186
|
-
sourcePattern,
|
|
187
|
-
emitPattern,
|
|
188
|
-
options,
|
|
189
|
-
)
|
|
190
|
-
|
|
191
|
-
// Combine merged branches and their source branches
|
|
192
|
-
const branchesToDelete = [
|
|
193
|
-
...new Set([...mergedBranches, ...sourceBranches]),
|
|
194
|
-
]
|
|
195
|
-
|
|
196
|
-
// Filter out the target branch itself (we don't want to delete it)
|
|
197
|
-
const filteredBranches = branchesToDelete.filter(
|
|
198
|
-
(branch) => branch !== mergedInto,
|
|
199
|
-
)
|
|
200
|
-
|
|
201
|
-
if (filteredBranches.length === 0) {
|
|
202
|
-
log(
|
|
203
|
-
`No branches found that are merged into ${highlight.branch(mergedInto)}`,
|
|
204
|
-
)
|
|
205
|
-
return
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// Show what will be deleted
|
|
209
|
-
const branchWord = filteredBranches.length === 1 ? "branch" : "branches"
|
|
210
|
-
log(
|
|
211
|
-
`Found ${filteredBranches.length} ${branchWord} to delete (merged into ${highlight.branch(mergedInto)}):`,
|
|
212
|
-
)
|
|
213
|
-
for (const branch of filteredBranches) {
|
|
214
|
-
log(` ${highlight.branch(branch)}`)
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
if (dryRun) {
|
|
218
|
-
log("")
|
|
219
|
-
log("Dry-run mode: no branches were deleted")
|
|
220
|
-
return
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// Get current branch to avoid deleting it
|
|
224
|
-
const currentBranch = yield* git.getCurrentBranch(gitRoot)
|
|
225
|
-
|
|
226
|
-
// Delete all branches
|
|
227
|
-
log("")
|
|
228
|
-
for (const branch of filteredBranches) {
|
|
229
|
-
// If we're currently on the branch, switch away first
|
|
230
|
-
if (currentBranch === branch) {
|
|
231
|
-
verboseLog(
|
|
232
|
-
`Currently on ${highlight.branch(branch)}, switching to ${highlight.branch(mergedInto)}`,
|
|
233
|
-
)
|
|
234
|
-
yield* git.checkoutBranch(gitRoot, mergedInto)
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
verboseLog(`Deleting ${highlight.branch(branch)}...`)
|
|
238
|
-
yield* git.deleteBranch(gitRoot, branch, true)
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
const deletedBranchWord =
|
|
242
|
-
filteredBranches.length === 1 ? "branch" : "branches"
|
|
243
|
-
log(done(`Deleted ${filteredBranches.length} ${deletedBranchWord}`))
|
|
244
|
-
})
|
|
245
|
-
|
|
246
|
-
export const help = `
|
|
247
|
-
Usage: agency clean --merged-into <branch> [options]
|
|
248
|
-
|
|
249
|
-
Delete all branches that have been fully merged into a specified branch,
|
|
250
|
-
along with their corresponding source branches.
|
|
251
|
-
|
|
252
|
-
This command is useful for cleaning up branches after they've been merged.
|
|
253
|
-
It will:
|
|
254
|
-
1. Find all branches fully merged into the specified branch
|
|
255
|
-
2. Find the corresponding source branches (which won't show as merged due to emit filtering)
|
|
256
|
-
3. Delete both the merged branches and their source branches
|
|
257
|
-
|
|
258
|
-
The --merged-into flag is REQUIRED to prevent accidental deletion of branches.
|
|
259
|
-
|
|
260
|
-
Options:
|
|
261
|
-
--merged-into <branch> Branch to check against (e.g., main, origin/main) [REQUIRED]
|
|
262
|
-
--dry-run Show what would be deleted without actually deleting
|
|
263
|
-
|
|
264
|
-
Safety:
|
|
265
|
-
- By default, this command deletes branches
|
|
266
|
-
- Use --dry-run to preview what would be deleted without making changes
|
|
267
|
-
- If currently on a branch that will be deleted, switches to the target branch first
|
|
268
|
-
- Uses force delete (git branch -D) to ensure branches are deleted
|
|
269
|
-
|
|
270
|
-
Examples:
|
|
271
|
-
agency clean --merged-into main # Delete branches merged into main
|
|
272
|
-
agency clean --merged-into main --dry-run # Preview what would be deleted
|
|
273
|
-
agency clean --merged-into origin/main # Delete branches merged into origin/main
|
|
274
|
-
`
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test"
|
|
2
|
-
import { Effect } from "effect"
|
|
3
|
-
import { completions } from "./completions"
|
|
4
|
-
|
|
5
|
-
const captureCompletions = async (shell: string) => {
|
|
6
|
-
const originalLog = console.log
|
|
7
|
-
let output = ""
|
|
8
|
-
console.log = (message: string) => {
|
|
9
|
-
output += message
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
try {
|
|
13
|
-
await Effect.runPromise(completions(shell))
|
|
14
|
-
} finally {
|
|
15
|
-
console.log = originalLog
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
return output
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
describe("completions command", () => {
|
|
22
|
-
test("generates bash completions", async () => {
|
|
23
|
-
const output = await captureCompletions("bash")
|
|
24
|
-
|
|
25
|
-
expect(output).toContain("_agency_completions()")
|
|
26
|
-
expect(output).toContain("complete -F _agency_completions agency")
|
|
27
|
-
expect(output).toContain("completions")
|
|
28
|
-
expect(output).toContain("bash zsh")
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
test("generates zsh completions", async () => {
|
|
32
|
-
const output = await captureCompletions("zsh")
|
|
33
|
-
|
|
34
|
-
expect(output).toContain("#compdef agency")
|
|
35
|
-
expect(output).toContain("_agency()")
|
|
36
|
-
expect(output).toContain("agency_commands=(")
|
|
37
|
-
expect(output).toContain("bash\\:Generate\\ bash\\ completions")
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
test("prints the requested completion script", async () => {
|
|
41
|
-
const output = await captureCompletions("zsh")
|
|
42
|
-
|
|
43
|
-
expect(output).toContain("#compdef agency")
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
test("rejects unsupported shells", async () => {
|
|
47
|
-
await expect(Effect.runPromise(completions("fish"))).rejects.toThrow(
|
|
48
|
-
"Usage: agency completions <bash|zsh>",
|
|
49
|
-
)
|
|
50
|
-
})
|
|
51
|
-
})
|
|
@@ -1,308 +0,0 @@
|
|
|
1
|
-
import { Effect } from "effect"
|
|
2
|
-
|
|
3
|
-
const shells = ["bash", "zsh"] as const
|
|
4
|
-
type Shell = (typeof shells)[number]
|
|
5
|
-
|
|
6
|
-
const commands = [
|
|
7
|
-
"init",
|
|
8
|
-
"task",
|
|
9
|
-
"tasks",
|
|
10
|
-
"edit",
|
|
11
|
-
"work",
|
|
12
|
-
"template",
|
|
13
|
-
"emit",
|
|
14
|
-
"emitted",
|
|
15
|
-
"pr",
|
|
16
|
-
"push",
|
|
17
|
-
"pull",
|
|
18
|
-
"rebase",
|
|
19
|
-
"base",
|
|
20
|
-
"switch",
|
|
21
|
-
"source",
|
|
22
|
-
"merge",
|
|
23
|
-
"status",
|
|
24
|
-
"clean",
|
|
25
|
-
"loop",
|
|
26
|
-
"completions",
|
|
27
|
-
] as const
|
|
28
|
-
|
|
29
|
-
const templateSubcommands = ["use", "save", "list", "view", "delete"] as const
|
|
30
|
-
const baseSubcommands = ["get", "set"] as const
|
|
31
|
-
const completionSubcommands = shells
|
|
32
|
-
|
|
33
|
-
const globalOptions = [
|
|
34
|
-
"--help",
|
|
35
|
-
"-h",
|
|
36
|
-
"--version",
|
|
37
|
-
"-v",
|
|
38
|
-
"--no-color",
|
|
39
|
-
"--silent",
|
|
40
|
-
"-s",
|
|
41
|
-
"--verbose",
|
|
42
|
-
] as const
|
|
43
|
-
|
|
44
|
-
const commandOptions: Record<string, readonly string[]> = {
|
|
45
|
-
init: [
|
|
46
|
-
"--help",
|
|
47
|
-
"-h",
|
|
48
|
-
"--template",
|
|
49
|
-
"-t",
|
|
50
|
-
"--silent",
|
|
51
|
-
"-s",
|
|
52
|
-
"--verbose",
|
|
53
|
-
"-v",
|
|
54
|
-
],
|
|
55
|
-
emit: [
|
|
56
|
-
"--help",
|
|
57
|
-
"-h",
|
|
58
|
-
"--emit",
|
|
59
|
-
"--branch",
|
|
60
|
-
"--silent",
|
|
61
|
-
"-s",
|
|
62
|
-
"--verbose",
|
|
63
|
-
"-v",
|
|
64
|
-
],
|
|
65
|
-
push: [
|
|
66
|
-
"--help",
|
|
67
|
-
"-h",
|
|
68
|
-
"--emit",
|
|
69
|
-
"--branch",
|
|
70
|
-
"--force",
|
|
71
|
-
"-f",
|
|
72
|
-
"--no-verify",
|
|
73
|
-
"--pr",
|
|
74
|
-
"--silent",
|
|
75
|
-
"-s",
|
|
76
|
-
"--verbose",
|
|
77
|
-
"-v",
|
|
78
|
-
],
|
|
79
|
-
pull: ["--help", "-h", "--remote", "-r", "--silent", "-s", "--verbose", "-v"],
|
|
80
|
-
rebase: [
|
|
81
|
-
"--help",
|
|
82
|
-
"-h",
|
|
83
|
-
"--emit",
|
|
84
|
-
"--branch",
|
|
85
|
-
"--silent",
|
|
86
|
-
"-s",
|
|
87
|
-
"--verbose",
|
|
88
|
-
"-v",
|
|
89
|
-
],
|
|
90
|
-
task: [
|
|
91
|
-
"--help",
|
|
92
|
-
"-h",
|
|
93
|
-
"--emit",
|
|
94
|
-
"--branch",
|
|
95
|
-
"--task",
|
|
96
|
-
"--from",
|
|
97
|
-
"--from-current",
|
|
98
|
-
"--continue",
|
|
99
|
-
"--squash",
|
|
100
|
-
"--silent",
|
|
101
|
-
"-s",
|
|
102
|
-
"--verbose",
|
|
103
|
-
"-v",
|
|
104
|
-
],
|
|
105
|
-
tasks: ["--help", "-h", "--json", "--silent", "-s", "--verbose", "-v"],
|
|
106
|
-
base: ["--help", "-h", "--repo", "--silent", "-s", "--verbose", "-v"],
|
|
107
|
-
template: [
|
|
108
|
-
"--help",
|
|
109
|
-
"-h",
|
|
110
|
-
"--template",
|
|
111
|
-
"-t",
|
|
112
|
-
"--silent",
|
|
113
|
-
"-s",
|
|
114
|
-
"--verbose",
|
|
115
|
-
"-v",
|
|
116
|
-
],
|
|
117
|
-
work: [
|
|
118
|
-
"--help",
|
|
119
|
-
"-h",
|
|
120
|
-
"--opencode",
|
|
121
|
-
"--claude",
|
|
122
|
-
"--silent",
|
|
123
|
-
"-s",
|
|
124
|
-
"--verbose",
|
|
125
|
-
"-v",
|
|
126
|
-
],
|
|
127
|
-
loop: [
|
|
128
|
-
"--help",
|
|
129
|
-
"-h",
|
|
130
|
-
"--min-loops",
|
|
131
|
-
"--max-loops",
|
|
132
|
-
"--opencode",
|
|
133
|
-
"--claude",
|
|
134
|
-
"--silent",
|
|
135
|
-
"-s",
|
|
136
|
-
"--verbose",
|
|
137
|
-
"-v",
|
|
138
|
-
],
|
|
139
|
-
status: ["--help", "-h", "--json", "--silent", "-s", "--verbose", "-v"],
|
|
140
|
-
clean: [
|
|
141
|
-
"--help",
|
|
142
|
-
"-h",
|
|
143
|
-
"--dry-run",
|
|
144
|
-
"--merged-into",
|
|
145
|
-
"--silent",
|
|
146
|
-
"-s",
|
|
147
|
-
"--verbose",
|
|
148
|
-
"-v",
|
|
149
|
-
],
|
|
150
|
-
completions: ["--help", "-h"],
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
const words = (values: readonly string[]) => values.join(" ")
|
|
154
|
-
|
|
155
|
-
const bashCase = (command: string, subcommands?: readonly string[]) => {
|
|
156
|
-
const options = commandOptions[command] ?? [
|
|
157
|
-
"--help",
|
|
158
|
-
"-h",
|
|
159
|
-
"--silent",
|
|
160
|
-
"-s",
|
|
161
|
-
"--verbose",
|
|
162
|
-
"-v",
|
|
163
|
-
]
|
|
164
|
-
const completions = subcommands ? [...subcommands, ...options] : options
|
|
165
|
-
return `\t\t${command})\n\t\t\tCOMPREPLY=( $(compgen -W "${words(completions)}" -- "$cur") )\n\t\t\treturn 0\n\t\t\t;;`
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
const zshCommandSpec = (command: string, description: string) =>
|
|
169
|
-
` '${command}:${description}'`
|
|
170
|
-
|
|
171
|
-
const generateBashCompletions = () => `# bash completion for agency
|
|
172
|
-
_agency_completions() {
|
|
173
|
-
local cur prev command
|
|
174
|
-
COMPREPLY=()
|
|
175
|
-
cur="\${COMP_WORDS[COMP_CWORD]}"
|
|
176
|
-
prev="\${COMP_WORDS[COMP_CWORD-1]}"
|
|
177
|
-
|
|
178
|
-
if [[ $COMP_CWORD -eq 1 ]]; then
|
|
179
|
-
COMPREPLY=( $(compgen -W "${words([...commands, ...globalOptions])}" -- "$cur") )
|
|
180
|
-
return 0
|
|
181
|
-
fi
|
|
182
|
-
|
|
183
|
-
command="\${COMP_WORDS[1]}"
|
|
184
|
-
case "$command" in
|
|
185
|
-
${bashCase("template", templateSubcommands)}
|
|
186
|
-
${bashCase("base", baseSubcommands)}
|
|
187
|
-
${bashCase("completions", completionSubcommands)}
|
|
188
|
-
${commands
|
|
189
|
-
.filter((command) => !["template", "base", "completions"].includes(command))
|
|
190
|
-
.map((command) => bashCase(command))
|
|
191
|
-
.join("\n")}
|
|
192
|
-
esac
|
|
193
|
-
|
|
194
|
-
case "$prev" in
|
|
195
|
-
--template|-t|--emit|--branch|--task|--from|--remote|-r|--merged-into)
|
|
196
|
-
return 0
|
|
197
|
-
;;
|
|
198
|
-
esac
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
complete -F _agency_completions agency
|
|
202
|
-
`
|
|
203
|
-
|
|
204
|
-
const generateZshCompletions = () => `#compdef agency
|
|
205
|
-
# zsh completion for agency
|
|
206
|
-
|
|
207
|
-
_agency() {
|
|
208
|
-
local context state line
|
|
209
|
-
local -a agency_commands
|
|
210
|
-
typeset -A opt_args
|
|
211
|
-
|
|
212
|
-
agency_commands=(
|
|
213
|
-
${zshCommandSpec("init", "Initialize agency with template selection")}
|
|
214
|
-
${zshCommandSpec("task", "Initialize template files on a feature branch")}
|
|
215
|
-
${zshCommandSpec("tasks", "List all task branches")}
|
|
216
|
-
${zshCommandSpec("edit", "Open TASK.md in system editor")}
|
|
217
|
-
${zshCommandSpec("work", "Start working on TASK.md with OpenCode")}
|
|
218
|
-
${zshCommandSpec("template", "Template management commands")}
|
|
219
|
-
${zshCommandSpec("emit", "Emit a branch with backpack files reverted")}
|
|
220
|
-
${zshCommandSpec("emitted", "Get the name of the emitted branch")}
|
|
221
|
-
${zshCommandSpec("pr", "Run gh pr with the emitted branch name")}
|
|
222
|
-
${zshCommandSpec("push", "Emit, push to remote, return to source")}
|
|
223
|
-
${zshCommandSpec("pull", "Pull commits from remote emit branch to source")}
|
|
224
|
-
${zshCommandSpec("rebase", "Rebase source branch onto base branch")}
|
|
225
|
-
${zshCommandSpec("base", "Get or set the base branch")}
|
|
226
|
-
${zshCommandSpec("switch", "Toggle between source and emitted branch")}
|
|
227
|
-
${zshCommandSpec("source", "Switch to source branch from emitted branch")}
|
|
228
|
-
${zshCommandSpec("merge", "Merge emitted branch into base branch")}
|
|
229
|
-
${zshCommandSpec("status", "Show agency status for this repository")}
|
|
230
|
-
${zshCommandSpec("clean", "Delete branches merged into a specified branch")}
|
|
231
|
-
${zshCommandSpec("loop", "Run a Ralph Wiggum loop to complete all tasks")}
|
|
232
|
-
${zshCommandSpec("completions", "Generate shell completion scripts")}
|
|
233
|
-
)
|
|
234
|
-
|
|
235
|
-
_arguments -C \\
|
|
236
|
-
'(-h --help)'{-h,--help}'[show help]' \\
|
|
237
|
-
'(-v --version)'{-v,--version}'[show version]' \\
|
|
238
|
-
'--no-color[disable color output]' \\
|
|
239
|
-
'(-s --silent)'{-s,--silent}'[suppress output messages]' \\
|
|
240
|
-
'--verbose[show verbose output]' \\
|
|
241
|
-
'1:command:->command' \\
|
|
242
|
-
'*::arg:->args'
|
|
243
|
-
|
|
244
|
-
case $state in
|
|
245
|
-
command)
|
|
246
|
-
_describe 'agency command' agency_commands
|
|
247
|
-
;;
|
|
248
|
-
args)
|
|
249
|
-
case $line[1] in
|
|
250
|
-
template)
|
|
251
|
-
_arguments \\
|
|
252
|
-
'(-h --help)'{-h,--help}'[show help]' \\
|
|
253
|
-
'(-t --template)'{-t,--template}'[template name]:template:' \\
|
|
254
|
-
'(-s --silent)'{-s,--silent}'[suppress output messages]' \\
|
|
255
|
-
'--verbose[show verbose output]' \\
|
|
256
|
-
'1:subcommand:((use\\:Set\\ template save\\:Save\\ files list\\:List\\ files view\\:View\\ file delete\\:Delete\\ files))' \\
|
|
257
|
-
'*:file:_files'
|
|
258
|
-
;;
|
|
259
|
-
base)
|
|
260
|
-
_arguments \\
|
|
261
|
-
'(-h --help)'{-h,--help}'[show help]' \\
|
|
262
|
-
'--repo[use repository-local configuration]' \\
|
|
263
|
-
'(-s --silent)'{-s,--silent}'[suppress output messages]' \\
|
|
264
|
-
'--verbose[show verbose output]' \\
|
|
265
|
-
'1:subcommand:((get\\:Get\\ base\\ branch set\\:Set\\ base\\ branch))' \\
|
|
266
|
-
'*:branch:'
|
|
267
|
-
;;
|
|
268
|
-
completions)
|
|
269
|
-
_arguments \\
|
|
270
|
-
'(-h --help)'{-h,--help}'[show help]' \\
|
|
271
|
-
'1:shell:((bash\\:Generate\\ bash\\ completions zsh\\:Generate\\ zsh\\ completions))'
|
|
272
|
-
;;
|
|
273
|
-
*)
|
|
274
|
-
_arguments \\
|
|
275
|
-
'(-h --help)'{-h,--help}'[show help]' \\
|
|
276
|
-
'(-s --silent)'{-s,--silent}'[suppress output messages]' \\
|
|
277
|
-
'--verbose[show verbose output]'
|
|
278
|
-
;;
|
|
279
|
-
esac
|
|
280
|
-
;;
|
|
281
|
-
esac
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
_agency "$@"
|
|
285
|
-
`
|
|
286
|
-
|
|
287
|
-
export const completions = (shell: string | undefined) =>
|
|
288
|
-
Effect.gen(function* () {
|
|
289
|
-
if (!shell || !shells.includes(shell as Shell)) {
|
|
290
|
-
return yield* Effect.fail(
|
|
291
|
-
new Error("Usage: agency completions <bash|zsh>"),
|
|
292
|
-
)
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
console.log(
|
|
296
|
-
shell === "bash" ? generateBashCompletions() : generateZshCompletions(),
|
|
297
|
-
)
|
|
298
|
-
})
|
|
299
|
-
|
|
300
|
-
export const help = `
|
|
301
|
-
Usage: agency completions <bash|zsh>
|
|
302
|
-
|
|
303
|
-
Generate shell completion scripts.
|
|
304
|
-
|
|
305
|
-
Examples:
|
|
306
|
-
agency completions zsh > ~/.zsh/completions/_agency
|
|
307
|
-
agency completions bash > ~/.local/share/bash-completion/completions/agency
|
|
308
|
-
`
|