@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,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
- })
@@ -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
- })
@@ -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
- `