@markjaquith/agency 0.5.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/LICENSE +21 -0
- package/README.md +109 -0
- package/cli.ts +569 -0
- package/index.ts +1 -0
- package/package.json +65 -0
- package/src/commands/base.test.ts +198 -0
- package/src/commands/base.ts +198 -0
- package/src/commands/clean.test.ts +299 -0
- package/src/commands/clean.ts +320 -0
- package/src/commands/emit.test.ts +412 -0
- package/src/commands/emit.ts +521 -0
- package/src/commands/emitted.test.ts +226 -0
- package/src/commands/emitted.ts +57 -0
- package/src/commands/init.test.ts +311 -0
- package/src/commands/init.ts +140 -0
- package/src/commands/merge.test.ts +365 -0
- package/src/commands/merge.ts +253 -0
- package/src/commands/pull.test.ts +385 -0
- package/src/commands/pull.ts +205 -0
- package/src/commands/push.test.ts +394 -0
- package/src/commands/push.ts +346 -0
- package/src/commands/save.test.ts +247 -0
- package/src/commands/save.ts +162 -0
- package/src/commands/source.test.ts +195 -0
- package/src/commands/source.ts +72 -0
- package/src/commands/status.test.ts +489 -0
- package/src/commands/status.ts +258 -0
- package/src/commands/switch.test.ts +194 -0
- package/src/commands/switch.ts +84 -0
- package/src/commands/task-branching.test.ts +334 -0
- package/src/commands/task-edit.test.ts +141 -0
- package/src/commands/task-main.test.ts +872 -0
- package/src/commands/task.ts +712 -0
- package/src/commands/tasks.test.ts +335 -0
- package/src/commands/tasks.ts +155 -0
- package/src/commands/template-delete.test.ts +178 -0
- package/src/commands/template-delete.ts +98 -0
- package/src/commands/template-list.test.ts +135 -0
- package/src/commands/template-list.ts +87 -0
- package/src/commands/template-view.test.ts +158 -0
- package/src/commands/template-view.ts +86 -0
- package/src/commands/template.test.ts +32 -0
- package/src/commands/template.ts +96 -0
- package/src/commands/use.test.ts +87 -0
- package/src/commands/use.ts +97 -0
- package/src/commands/work.test.ts +462 -0
- package/src/commands/work.ts +193 -0
- package/src/errors.ts +17 -0
- package/src/schemas.ts +33 -0
- package/src/services/AgencyMetadataService.ts +287 -0
- package/src/services/ClaudeService.test.ts +184 -0
- package/src/services/ClaudeService.ts +91 -0
- package/src/services/ConfigService.ts +115 -0
- package/src/services/FileSystemService.ts +222 -0
- package/src/services/GitService.ts +751 -0
- package/src/services/OpencodeService.ts +263 -0
- package/src/services/PromptService.ts +183 -0
- package/src/services/TemplateService.ts +75 -0
- package/src/test-utils.ts +362 -0
- package/src/types/native-exec.d.ts +8 -0
- package/src/types.ts +216 -0
- package/src/utils/colors.ts +178 -0
- package/src/utils/command.ts +17 -0
- package/src/utils/effect.ts +281 -0
- package/src/utils/exec.ts +48 -0
- package/src/utils/paths.ts +51 -0
- package/src/utils/pr-branch.test.ts +372 -0
- package/src/utils/pr-branch.ts +473 -0
- package/src/utils/process.ts +110 -0
- package/src/utils/spinner.ts +82 -0
- package/templates/AGENCY.md +20 -0
- package/templates/AGENTS.md +11 -0
- package/templates/CLAUDE.md +3 -0
- package/templates/TASK.md +5 -0
- package/templates/opencode.json +4 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { resolve, join, dirname, basename } from "path"
|
|
2
|
+
import { Effect } from "effect"
|
|
3
|
+
import type { BaseCommandOptions } from "../utils/command"
|
|
4
|
+
import { TemplateService } from "../services/TemplateService"
|
|
5
|
+
import { FileSystemService } from "../services/FileSystemService"
|
|
6
|
+
import { RepositoryNotInitializedError } from "../errors"
|
|
7
|
+
import highlight, { done } from "../utils/colors"
|
|
8
|
+
import { createLoggers, ensureGitRepo, getTemplateName } from "../utils/effect"
|
|
9
|
+
|
|
10
|
+
interface SaveOptions extends BaseCommandOptions {
|
|
11
|
+
files?: string[]
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function isDirectory(filePath: string): Effect.Effect<boolean, Error> {
|
|
15
|
+
return Effect.tryPromise({
|
|
16
|
+
try: async () => {
|
|
17
|
+
try {
|
|
18
|
+
const file = Bun.file(filePath)
|
|
19
|
+
const stat = await file.stat()
|
|
20
|
+
return stat?.isDirectory?.() ?? false
|
|
21
|
+
} catch {
|
|
22
|
+
return false
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
catch: (error) =>
|
|
26
|
+
new Error(`Failed to check if path is directory: ${error}`),
|
|
27
|
+
})
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const save = (options: SaveOptions = {}) =>
|
|
31
|
+
Effect.gen(function* () {
|
|
32
|
+
const { files: filesToSave = [] } = options
|
|
33
|
+
const { log, verboseLog } = createLoggers(options)
|
|
34
|
+
|
|
35
|
+
const templateService = yield* TemplateService
|
|
36
|
+
const fs = yield* FileSystemService
|
|
37
|
+
|
|
38
|
+
const gitRoot = yield* ensureGitRepo()
|
|
39
|
+
|
|
40
|
+
// Get template name from git config
|
|
41
|
+
const templateName = yield* getTemplateName(gitRoot)
|
|
42
|
+
if (!templateName) {
|
|
43
|
+
return yield* Effect.fail(new RepositoryNotInitializedError())
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
verboseLog(`Saving to template: ${highlight.template(templateName)}`)
|
|
47
|
+
|
|
48
|
+
// Get template directory
|
|
49
|
+
const templateDir = yield* templateService.getTemplateDir(templateName)
|
|
50
|
+
|
|
51
|
+
// Create template directory if it doesn't exist
|
|
52
|
+
yield* templateService.createTemplateDir(templateName)
|
|
53
|
+
verboseLog(`Ensured template directory exists: ${templateDir}`)
|
|
54
|
+
|
|
55
|
+
// Determine which files to save
|
|
56
|
+
let filesToProcess: string[] = []
|
|
57
|
+
|
|
58
|
+
if (filesToSave.length > 0) {
|
|
59
|
+
// Process provided file/dir names
|
|
60
|
+
for (const fileOrDir of filesToSave) {
|
|
61
|
+
const fullPath = resolve(gitRoot, fileOrDir)
|
|
62
|
+
const isDir = yield* isDirectory(fullPath)
|
|
63
|
+
|
|
64
|
+
if (isDir) {
|
|
65
|
+
// Recursively collect files from directory
|
|
66
|
+
const collected = yield* fs.collectFiles(fullPath, {
|
|
67
|
+
relativeTo: gitRoot,
|
|
68
|
+
})
|
|
69
|
+
filesToProcess.push(...collected)
|
|
70
|
+
} else {
|
|
71
|
+
// Add file path relative to git root
|
|
72
|
+
const relativePath = fileOrDir.startsWith(gitRoot)
|
|
73
|
+
? fileOrDir.replace(gitRoot + "/", "")
|
|
74
|
+
: fileOrDir
|
|
75
|
+
filesToProcess.push(relativePath)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
} else {
|
|
79
|
+
return yield* Effect.fail(
|
|
80
|
+
new Error(
|
|
81
|
+
"No files specified. Usage: agency save <file|dir> [file|dir ...]",
|
|
82
|
+
),
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Save each file
|
|
87
|
+
for (const filePath of filesToProcess) {
|
|
88
|
+
const sourceFilePath = resolve(gitRoot, filePath)
|
|
89
|
+
|
|
90
|
+
// Check if file exists
|
|
91
|
+
const exists = yield* fs.exists(sourceFilePath)
|
|
92
|
+
if (!exists) {
|
|
93
|
+
verboseLog(`Skipping ${filePath} (does not exist)`)
|
|
94
|
+
continue
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Refuse to save TASK.md files - agency itself must control these
|
|
98
|
+
const fileName = basename(filePath)
|
|
99
|
+
if (fileName === "TASK.md") {
|
|
100
|
+
return yield* Effect.fail(
|
|
101
|
+
new Error(
|
|
102
|
+
`Cannot save ${filePath}: TASK.md files cannot be saved to templates. ` +
|
|
103
|
+
`Agency itself must control the creation of TASK.md files.`,
|
|
104
|
+
),
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Read content
|
|
109
|
+
const content = yield* fs.readFile(sourceFilePath)
|
|
110
|
+
|
|
111
|
+
const templateFilePath = join(templateDir, filePath)
|
|
112
|
+
|
|
113
|
+
// Ensure directory exists
|
|
114
|
+
const dir = dirname(templateFilePath)
|
|
115
|
+
yield* fs.createDirectory(dir)
|
|
116
|
+
|
|
117
|
+
// Write to template
|
|
118
|
+
yield* fs.writeFile(templateFilePath, content)
|
|
119
|
+
log(
|
|
120
|
+
done(
|
|
121
|
+
`Saved ${highlight.file(filePath)} to ${highlight.template(templateName)} template`,
|
|
122
|
+
),
|
|
123
|
+
)
|
|
124
|
+
}
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
// Help text for reference (not exported as it's handled by template command)
|
|
128
|
+
const help = `
|
|
129
|
+
Usage: agency save <file|dir> [file|dir ...] [options]
|
|
130
|
+
|
|
131
|
+
Save specified files or directories to the configured template.
|
|
132
|
+
|
|
133
|
+
This command copies files and directories from the current git repository to
|
|
134
|
+
the template directory configured in .git/config (agency.template). Directories
|
|
135
|
+
are saved recursively.
|
|
136
|
+
|
|
137
|
+
Arguments:
|
|
138
|
+
<file|dir> File or directory path to save (relative to git root)
|
|
139
|
+
[file|dir ...] Additional files or directories to save
|
|
140
|
+
|
|
141
|
+
Options:
|
|
142
|
+
-h, --help Show this help message
|
|
143
|
+
-s, --silent Suppress output messages
|
|
144
|
+
-v, --verbose Show verbose output
|
|
145
|
+
|
|
146
|
+
Examples:
|
|
147
|
+
agency save AGENTS.md # Save specific file
|
|
148
|
+
agency save .config # Save entire directory
|
|
149
|
+
agency save src/ # Save src directory to template
|
|
150
|
+
agency save AGENTS.md docs/ # Save file and directory
|
|
151
|
+
agency save --verbose # Save with verbose output
|
|
152
|
+
agency save --help # Show this help message
|
|
153
|
+
|
|
154
|
+
Notes:
|
|
155
|
+
- Requires agency.template to be set (run 'agency init' first)
|
|
156
|
+
- At least one file or directory must be specified
|
|
157
|
+
- Files are saved to ~/.config/agency/templates/{template-name}/
|
|
158
|
+
- Template directory is created automatically if it doesn't exist
|
|
159
|
+
- Existing template files will be overwritten
|
|
160
|
+
- Directory structure is preserved in the template
|
|
161
|
+
- TASK.md files cannot be saved - agency controls their creation
|
|
162
|
+
`
|
|
@@ -0,0 +1,195 @@
|
|
|
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
|
+
})
|
|
@@ -0,0 +1,72 @@
|
|
|
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
|
+
`
|