@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
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
import { test, expect, describe, beforeEach, afterEach } from "bun:test"
|
|
2
|
-
import { join } from "path"
|
|
3
|
-
import { source } from "./source"
|
|
4
|
-
import {
|
|
5
|
-
createTempDir,
|
|
6
|
-
cleanupTempDir,
|
|
7
|
-
initGitRepo,
|
|
8
|
-
getCurrentBranch,
|
|
9
|
-
createCommit,
|
|
10
|
-
runTestEffect,
|
|
11
|
-
} from "../test-utils"
|
|
12
|
-
import { writeAgencyMetadata } from "../types"
|
|
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("source 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("switches from emit branch to source branch", async () => {
|
|
57
|
-
// Create source branch with agency.json
|
|
58
|
-
await createBranch(tempDir, "agency--feature")
|
|
59
|
-
await writeAgencyMetadata(tempDir, {
|
|
60
|
-
version: 1,
|
|
61
|
-
injectedFiles: [],
|
|
62
|
-
template: "test-template",
|
|
63
|
-
emitBranch: "feature",
|
|
64
|
-
createdAt: new Date().toISOString(),
|
|
65
|
-
} as any)
|
|
66
|
-
// Stage and commit agency.json
|
|
67
|
-
await Bun.spawn(["git", "add", "agency.json"], {
|
|
68
|
-
cwd: tempDir,
|
|
69
|
-
stdout: "pipe",
|
|
70
|
-
stderr: "pipe",
|
|
71
|
-
}).exited
|
|
72
|
-
await createCommit(tempDir, "Setup")
|
|
73
|
-
|
|
74
|
-
// Create emit branch and remove agency.json
|
|
75
|
-
await createBranch(tempDir, "feature")
|
|
76
|
-
await Bun.spawn(["rm", "agency.json"], {
|
|
77
|
-
cwd: tempDir,
|
|
78
|
-
stdout: "pipe",
|
|
79
|
-
stderr: "pipe",
|
|
80
|
-
}).exited
|
|
81
|
-
|
|
82
|
-
// Run source command (should switch from feature emit branch to agency--feature source)
|
|
83
|
-
await runTestEffect(source({ silent: true }))
|
|
84
|
-
|
|
85
|
-
// Should be on agency--feature now
|
|
86
|
-
const currentBranch = await getCurrentBranch(tempDir)
|
|
87
|
-
expect(currentBranch).toBe("agency--feature")
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
test("works with custom emit branch pattern", async () => {
|
|
91
|
-
// Create custom config with custom patterns
|
|
92
|
-
const configPath = join(tempDir, "custom-config.json")
|
|
93
|
-
await Bun.write(
|
|
94
|
-
configPath,
|
|
95
|
-
JSON.stringify({
|
|
96
|
-
sourceBranchPattern: "WIP--%branch%",
|
|
97
|
-
emitBranch: "PR--%branch%",
|
|
98
|
-
}),
|
|
99
|
-
)
|
|
100
|
-
process.env.AGENCY_CONFIG_PATH = configPath
|
|
101
|
-
|
|
102
|
-
// Create source branch with agency.json
|
|
103
|
-
await createBranch(tempDir, "WIP--feature")
|
|
104
|
-
await writeAgencyMetadata(tempDir, {
|
|
105
|
-
version: 1,
|
|
106
|
-
injectedFiles: [],
|
|
107
|
-
template: "test-template",
|
|
108
|
-
emitBranch: "PR--feature",
|
|
109
|
-
createdAt: new Date().toISOString(),
|
|
110
|
-
} as any)
|
|
111
|
-
// Stage and commit
|
|
112
|
-
await Bun.spawn(["git", "add", "agency.json"], {
|
|
113
|
-
cwd: tempDir,
|
|
114
|
-
stdout: "pipe",
|
|
115
|
-
stderr: "pipe",
|
|
116
|
-
}).exited
|
|
117
|
-
await createCommit(tempDir, "Feature work")
|
|
118
|
-
|
|
119
|
-
// Create emit branch
|
|
120
|
-
await createBranch(tempDir, "PR--feature")
|
|
121
|
-
await Bun.spawn(["rm", "agency.json"], {
|
|
122
|
-
cwd: tempDir,
|
|
123
|
-
stdout: "pipe",
|
|
124
|
-
stderr: "pipe",
|
|
125
|
-
}).exited
|
|
126
|
-
|
|
127
|
-
// Run source command (from PR--feature to WIP--feature)
|
|
128
|
-
await runTestEffect(source({ silent: true }))
|
|
129
|
-
|
|
130
|
-
// Should be on WIP--feature now
|
|
131
|
-
const currentBranch = await getCurrentBranch(tempDir)
|
|
132
|
-
expect(currentBranch).toBe("WIP--feature")
|
|
133
|
-
})
|
|
134
|
-
})
|
|
135
|
-
|
|
136
|
-
describe("error handling", () => {
|
|
137
|
-
test("throws error when not on an emit branch", async () => {
|
|
138
|
-
// We're on main, which is not an emit branch
|
|
139
|
-
await expect(runTestEffect(source({ silent: true }))).rejects.toThrow(
|
|
140
|
-
"Not on an emit branch",
|
|
141
|
-
)
|
|
142
|
-
})
|
|
143
|
-
|
|
144
|
-
test("throws error when not in a git repository", async () => {
|
|
145
|
-
const nonGitDir = await createTempDir()
|
|
146
|
-
process.chdir(nonGitDir)
|
|
147
|
-
|
|
148
|
-
await expect(runTestEffect(source({ silent: true }))).rejects.toThrow(
|
|
149
|
-
"Not in a git repository",
|
|
150
|
-
)
|
|
151
|
-
|
|
152
|
-
await cleanupTempDir(nonGitDir)
|
|
153
|
-
})
|
|
154
|
-
})
|
|
155
|
-
|
|
156
|
-
describe("silent mode", () => {
|
|
157
|
-
test("silent flag suppresses output", async () => {
|
|
158
|
-
// Create source branch with agency.json
|
|
159
|
-
await createBranch(tempDir, "agency--feature")
|
|
160
|
-
await writeAgencyMetadata(tempDir, {
|
|
161
|
-
version: 1,
|
|
162
|
-
injectedFiles: [],
|
|
163
|
-
template: "test-template",
|
|
164
|
-
emitBranch: "feature",
|
|
165
|
-
createdAt: new Date().toISOString(),
|
|
166
|
-
} as any)
|
|
167
|
-
await Bun.spawn(["git", "add", "agency.json"], {
|
|
168
|
-
cwd: tempDir,
|
|
169
|
-
stdout: "pipe",
|
|
170
|
-
stderr: "pipe",
|
|
171
|
-
}).exited
|
|
172
|
-
await createCommit(tempDir, "Setup")
|
|
173
|
-
|
|
174
|
-
// Create emit branch
|
|
175
|
-
await createBranch(tempDir, "feature")
|
|
176
|
-
await Bun.spawn(["rm", "agency.json"], {
|
|
177
|
-
cwd: tempDir,
|
|
178
|
-
stdout: "pipe",
|
|
179
|
-
stderr: "pipe",
|
|
180
|
-
}).exited
|
|
181
|
-
|
|
182
|
-
// Capture output
|
|
183
|
-
const originalLog = console.log
|
|
184
|
-
let logCalled = false
|
|
185
|
-
console.log = () => {
|
|
186
|
-
logCalled = true
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
await runTestEffect(source({ silent: true }))
|
|
190
|
-
|
|
191
|
-
console.log = originalLog
|
|
192
|
-
expect(logCalled).toBe(false)
|
|
193
|
-
})
|
|
194
|
-
})
|
|
195
|
-
})
|
package/src/commands/source.ts
DELETED
|
@@ -1,72 +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 { FileSystemService } from "../services/FileSystemService"
|
|
6
|
-
import { resolveBranchPairWithAgencyJson } from "../utils/pr-branch"
|
|
7
|
-
import highlight, { done } from "../utils/colors"
|
|
8
|
-
import {
|
|
9
|
-
createLoggers,
|
|
10
|
-
ensureGitRepo,
|
|
11
|
-
ensureBranchExists,
|
|
12
|
-
} from "../utils/effect"
|
|
13
|
-
|
|
14
|
-
interface SourceOptions extends BaseCommandOptions {}
|
|
15
|
-
|
|
16
|
-
export const source = (options: SourceOptions = {}) =>
|
|
17
|
-
Effect.gen(function* () {
|
|
18
|
-
const { log } = createLoggers(options)
|
|
19
|
-
|
|
20
|
-
const git = yield* GitService
|
|
21
|
-
const configService = yield* ConfigService
|
|
22
|
-
|
|
23
|
-
const gitRoot = yield* ensureGitRepo()
|
|
24
|
-
|
|
25
|
-
// Load config
|
|
26
|
-
const config = yield* configService.loadConfig()
|
|
27
|
-
|
|
28
|
-
// Get current branch and resolve the branch pair
|
|
29
|
-
const currentBranch = yield* git.getCurrentBranch(gitRoot)
|
|
30
|
-
const { sourceBranch, isOnEmitBranch } =
|
|
31
|
-
yield* resolveBranchPairWithAgencyJson(
|
|
32
|
-
gitRoot,
|
|
33
|
-
currentBranch,
|
|
34
|
-
config.sourceBranchPattern,
|
|
35
|
-
config.emitBranch,
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
if (!isOnEmitBranch) {
|
|
39
|
-
return yield* Effect.fail(
|
|
40
|
-
new Error(`Not on an emit branch. Current branch: ${currentBranch}`),
|
|
41
|
-
)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Check if source branch exists
|
|
45
|
-
yield* ensureBranchExists(
|
|
46
|
-
gitRoot,
|
|
47
|
-
sourceBranch,
|
|
48
|
-
`Source branch ${highlight.branch(sourceBranch)} does not exist`,
|
|
49
|
-
)
|
|
50
|
-
|
|
51
|
-
// Checkout source branch
|
|
52
|
-
yield* git.checkoutBranch(gitRoot, sourceBranch)
|
|
53
|
-
|
|
54
|
-
log(done(`Switched to source branch: ${highlight.branch(sourceBranch)}`))
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
export const help = `
|
|
58
|
-
Usage: agency source [options]
|
|
59
|
-
|
|
60
|
-
Switch back to the source branch from an emit branch.
|
|
61
|
-
|
|
62
|
-
This command extracts the source branch name from your current emit branch name
|
|
63
|
-
using the configured pattern, and switches back to it.
|
|
64
|
-
|
|
65
|
-
Example:
|
|
66
|
-
agency source # From main--PR, switch to main
|
|
67
|
-
|
|
68
|
-
Notes:
|
|
69
|
-
- Must be run from an emit branch
|
|
70
|
-
- Source branch must exist
|
|
71
|
-
- Uses emit branch pattern from ~/.config/agency/agency.json
|
|
72
|
-
`
|
|
@@ -1,194 +0,0 @@
|
|
|
1
|
-
import { test, expect, describe, beforeEach, afterEach } from "bun:test"
|
|
2
|
-
import { join } from "path"
|
|
3
|
-
import { switchBranch } from "./switch"
|
|
4
|
-
import {
|
|
5
|
-
createTempDir,
|
|
6
|
-
cleanupTempDir,
|
|
7
|
-
initGitRepo,
|
|
8
|
-
getGitOutput,
|
|
9
|
-
getCurrentBranch,
|
|
10
|
-
createCommit,
|
|
11
|
-
checkoutBranch,
|
|
12
|
-
runTestEffect,
|
|
13
|
-
} from "../test-utils"
|
|
14
|
-
|
|
15
|
-
async function createBranch(cwd: string, branchName: string): Promise<void> {
|
|
16
|
-
await Bun.spawn(["git", "checkout", "-b", branchName], {
|
|
17
|
-
cwd,
|
|
18
|
-
stdout: "pipe",
|
|
19
|
-
stderr: "pipe",
|
|
20
|
-
}).exited
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
describe("switch command", () => {
|
|
24
|
-
let tempDir: string
|
|
25
|
-
let originalCwd: string
|
|
26
|
-
|
|
27
|
-
beforeEach(async () => {
|
|
28
|
-
tempDir = await createTempDir()
|
|
29
|
-
originalCwd = process.cwd()
|
|
30
|
-
process.chdir(tempDir)
|
|
31
|
-
|
|
32
|
-
// Set config path to non-existent file to use defaults
|
|
33
|
-
process.env.AGENCY_CONFIG_PATH = join(tempDir, "non-existent-config.json")
|
|
34
|
-
|
|
35
|
-
// Initialize git repo
|
|
36
|
-
await initGitRepo(tempDir)
|
|
37
|
-
await createCommit(tempDir, "Initial commit")
|
|
38
|
-
|
|
39
|
-
// Rename to main if needed
|
|
40
|
-
const currentBranch = await getCurrentBranch(tempDir)
|
|
41
|
-
if (currentBranch === "master") {
|
|
42
|
-
await Bun.spawn(["git", "branch", "-m", "main"], {
|
|
43
|
-
cwd: tempDir,
|
|
44
|
-
stdout: "pipe",
|
|
45
|
-
stderr: "pipe",
|
|
46
|
-
}).exited
|
|
47
|
-
}
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
afterEach(async () => {
|
|
51
|
-
process.chdir(originalCwd)
|
|
52
|
-
delete process.env.AGENCY_CONFIG_PATH
|
|
53
|
-
await cleanupTempDir(tempDir)
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
describe("basic functionality", () => {
|
|
57
|
-
test("switches from emit branch to source branch", async () => {
|
|
58
|
-
// Create source and emit branches (source=agency--main, emit=main)
|
|
59
|
-
await createBranch(tempDir, "agency--main")
|
|
60
|
-
await createCommit(tempDir, "Work on source")
|
|
61
|
-
// Emit branch is just "main" (already exists from setup)
|
|
62
|
-
await checkoutBranch(tempDir, "main")
|
|
63
|
-
|
|
64
|
-
// Run switch command
|
|
65
|
-
await runTestEffect(switchBranch({ silent: true }))
|
|
66
|
-
|
|
67
|
-
// Should be on source branch now
|
|
68
|
-
const currentBranch = await getCurrentBranch(tempDir)
|
|
69
|
-
expect(currentBranch).toBe("agency--main")
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
test("switches from source branch to emit branch", async () => {
|
|
73
|
-
// Create source branch (main becomes the emit branch)
|
|
74
|
-
await createBranch(tempDir, "agency--main")
|
|
75
|
-
await createCommit(tempDir, "Work on source")
|
|
76
|
-
|
|
77
|
-
// We're on agency--main (source), switch to main (emit)
|
|
78
|
-
// Run switch command
|
|
79
|
-
await runTestEffect(switchBranch({ silent: true }))
|
|
80
|
-
|
|
81
|
-
// Should be on emit branch now
|
|
82
|
-
const currentBranch = await getCurrentBranch(tempDir)
|
|
83
|
-
expect(currentBranch).toBe("main")
|
|
84
|
-
})
|
|
85
|
-
|
|
86
|
-
test("toggles back and forth", async () => {
|
|
87
|
-
// Create source and emit branches
|
|
88
|
-
await createBranch(tempDir, "agency--main")
|
|
89
|
-
await createCommit(tempDir, "Work on source")
|
|
90
|
-
await checkoutBranch(tempDir, "main") // Go to emit
|
|
91
|
-
|
|
92
|
-
// Switch to source
|
|
93
|
-
await runTestEffect(switchBranch({ silent: true }))
|
|
94
|
-
expect(await getCurrentBranch(tempDir)).toBe("agency--main")
|
|
95
|
-
|
|
96
|
-
// Switch back to emit
|
|
97
|
-
await runTestEffect(switchBranch({ silent: true }))
|
|
98
|
-
expect(await getCurrentBranch(tempDir)).toBe("main")
|
|
99
|
-
|
|
100
|
-
// And back to source
|
|
101
|
-
await runTestEffect(switchBranch({ silent: true }))
|
|
102
|
-
expect(await getCurrentBranch(tempDir)).toBe("agency--main")
|
|
103
|
-
})
|
|
104
|
-
|
|
105
|
-
test("works with custom emit branch pattern", async () => {
|
|
106
|
-
// Create custom config
|
|
107
|
-
const configPath = join(tempDir, "custom-config.json")
|
|
108
|
-
await Bun.write(
|
|
109
|
-
configPath,
|
|
110
|
-
JSON.stringify({
|
|
111
|
-
sourceBranchPattern: "agency--%branch%",
|
|
112
|
-
emitBranch: "PR--%branch%",
|
|
113
|
-
}),
|
|
114
|
-
)
|
|
115
|
-
process.env.AGENCY_CONFIG_PATH = configPath
|
|
116
|
-
|
|
117
|
-
// Create source branch and its emit branch
|
|
118
|
-
await createBranch(tempDir, "agency--feature")
|
|
119
|
-
await createCommit(tempDir, "Feature work")
|
|
120
|
-
await createBranch(tempDir, "PR--feature")
|
|
121
|
-
|
|
122
|
-
// Switch to source
|
|
123
|
-
await runTestEffect(switchBranch({ silent: true }))
|
|
124
|
-
expect(await getCurrentBranch(tempDir)).toBe("agency--feature")
|
|
125
|
-
|
|
126
|
-
// Switch back to emit
|
|
127
|
-
await runTestEffect(switchBranch({ silent: true }))
|
|
128
|
-
expect(await getCurrentBranch(tempDir)).toBe("PR--feature")
|
|
129
|
-
})
|
|
130
|
-
})
|
|
131
|
-
|
|
132
|
-
describe("error handling", () => {
|
|
133
|
-
test("throws error when emit branch doesn't exist", async () => {
|
|
134
|
-
// Use custom emit pattern so emit branch differs from source
|
|
135
|
-
const configPath = join(tempDir, "custom-config.json")
|
|
136
|
-
await Bun.write(
|
|
137
|
-
configPath,
|
|
138
|
-
JSON.stringify({
|
|
139
|
-
sourceBranchPattern: "agency--%branch%",
|
|
140
|
-
emitBranch: "%branch%--PR",
|
|
141
|
-
}),
|
|
142
|
-
)
|
|
143
|
-
process.env.AGENCY_CONFIG_PATH = configPath
|
|
144
|
-
|
|
145
|
-
// Create source branch, but not the emit branch
|
|
146
|
-
await createBranch(tempDir, "agency--feature")
|
|
147
|
-
// feature--PR doesn't exist
|
|
148
|
-
|
|
149
|
-
await expect(
|
|
150
|
-
runTestEffect(switchBranch({ silent: true })),
|
|
151
|
-
).rejects.toThrow(/Emit branch .* does not exist/)
|
|
152
|
-
})
|
|
153
|
-
|
|
154
|
-
test("throws error when emit branch doesn't exist", async () => {
|
|
155
|
-
// Create source branch but no emit branch
|
|
156
|
-
await createBranch(tempDir, "agency--feature")
|
|
157
|
-
// We never created 'feature' (emit), so it doesn't exist
|
|
158
|
-
|
|
159
|
-
await expect(
|
|
160
|
-
runTestEffect(switchBranch({ silent: true })),
|
|
161
|
-
).rejects.toThrow(/Emit branch .* does not exist/)
|
|
162
|
-
})
|
|
163
|
-
|
|
164
|
-
test("throws error when not in a git repository", async () => {
|
|
165
|
-
const nonGitDir = await createTempDir()
|
|
166
|
-
process.chdir(nonGitDir)
|
|
167
|
-
|
|
168
|
-
await expect(
|
|
169
|
-
runTestEffect(switchBranch({ silent: true })),
|
|
170
|
-
).rejects.toThrow("Not in a git repository")
|
|
171
|
-
|
|
172
|
-
await cleanupTempDir(nonGitDir)
|
|
173
|
-
})
|
|
174
|
-
})
|
|
175
|
-
|
|
176
|
-
describe("silent mode", () => {
|
|
177
|
-
test("silent flag suppresses output", async () => {
|
|
178
|
-
await createBranch(tempDir, "agency--main")
|
|
179
|
-
await checkoutBranch(tempDir, "main")
|
|
180
|
-
|
|
181
|
-
// Capture output
|
|
182
|
-
const originalLog = console.log
|
|
183
|
-
let logCalled = false
|
|
184
|
-
console.log = () => {
|
|
185
|
-
logCalled = true
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
await runTestEffect(switchBranch({ silent: true }))
|
|
189
|
-
|
|
190
|
-
console.log = originalLog
|
|
191
|
-
expect(logCalled).toBe(false)
|
|
192
|
-
})
|
|
193
|
-
})
|
|
194
|
-
})
|
package/src/commands/switch.ts
DELETED
|
@@ -1,84 +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 highlight, { done } from "../utils/colors"
|
|
10
|
-
import {
|
|
11
|
-
createLoggers,
|
|
12
|
-
ensureGitRepo,
|
|
13
|
-
ensureBranchExists,
|
|
14
|
-
} from "../utils/effect"
|
|
15
|
-
|
|
16
|
-
interface SwitchOptions extends BaseCommandOptions {}
|
|
17
|
-
|
|
18
|
-
export const switchBranch = (options: SwitchOptions = {}) =>
|
|
19
|
-
Effect.gen(function* () {
|
|
20
|
-
const { log } = createLoggers(options)
|
|
21
|
-
|
|
22
|
-
const git = yield* GitService
|
|
23
|
-
const configService = yield* ConfigService
|
|
24
|
-
|
|
25
|
-
const gitRoot = yield* ensureGitRepo()
|
|
26
|
-
|
|
27
|
-
// Load config
|
|
28
|
-
const config = yield* configService.loadConfig()
|
|
29
|
-
|
|
30
|
-
// Get current branch and resolve the branch pair
|
|
31
|
-
const currentBranch = yield* git.getCurrentBranch(gitRoot)
|
|
32
|
-
const branches: BranchPair = yield* resolveBranchPairWithAgencyJson(
|
|
33
|
-
gitRoot,
|
|
34
|
-
currentBranch,
|
|
35
|
-
config.sourceBranchPattern,
|
|
36
|
-
config.emitBranch,
|
|
37
|
-
)
|
|
38
|
-
const { sourceBranch, emitBranch, isOnEmitBranch } = branches
|
|
39
|
-
|
|
40
|
-
if (isOnEmitBranch) {
|
|
41
|
-
// We're on an emit branch, switch to source
|
|
42
|
-
yield* ensureBranchExists(
|
|
43
|
-
gitRoot,
|
|
44
|
-
sourceBranch,
|
|
45
|
-
`Source branch ${highlight.branch(sourceBranch)} does not exist`,
|
|
46
|
-
)
|
|
47
|
-
|
|
48
|
-
yield* git.checkoutBranch(gitRoot, sourceBranch)
|
|
49
|
-
log(done(`Switched to source branch: ${highlight.branch(sourceBranch)}`))
|
|
50
|
-
} else {
|
|
51
|
-
// We're on a source branch, switch to emit branch
|
|
52
|
-
yield* ensureBranchExists(
|
|
53
|
-
gitRoot,
|
|
54
|
-
emitBranch,
|
|
55
|
-
`Emit branch ${highlight.branch(emitBranch)} does not exist. Run 'agency emit' to create it.`,
|
|
56
|
-
)
|
|
57
|
-
|
|
58
|
-
yield* git.checkoutBranch(gitRoot, emitBranch)
|
|
59
|
-
log(done(`Switched to ${highlight.branch(emitBranch)}`))
|
|
60
|
-
}
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
export const help = `
|
|
64
|
-
Usage: agency switch [options]
|
|
65
|
-
|
|
66
|
-
Toggle between source branch and emit branch.
|
|
67
|
-
|
|
68
|
-
Source and Emit Branches:
|
|
69
|
-
- Source branches: Your working branches with agency-specific files (e.g., agency--main)
|
|
70
|
-
- Emit branches: Clean branches suitable for PRs without agency files (e.g., main)
|
|
71
|
-
|
|
72
|
-
This command intelligently switches between your source branch and its
|
|
73
|
-
corresponding emit branch:
|
|
74
|
-
- If on an emit branch (e.g., main), switches to source (agency--main)
|
|
75
|
-
- If on a source branch (e.g., agency--main), switches to emit branch (main)
|
|
76
|
-
|
|
77
|
-
Example:
|
|
78
|
-
agency switch # Toggle between branches
|
|
79
|
-
|
|
80
|
-
Notes:
|
|
81
|
-
- Target branch must exist
|
|
82
|
-
- Uses source and emit patterns from ~/.config/agency/agency.json
|
|
83
|
-
- If emit branch doesn't exist, run 'agency emit' to create it
|
|
84
|
-
`
|