@markjaquith/agency 0.5.1 → 0.6.1

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 CHANGED
@@ -22,17 +22,28 @@ Open `TASK.md` in the system editor for editing. Nice if you have to paste in la
22
22
 
23
23
  Launch Opencode to work on the current task defined in `TASK.md`. All your context will be loaded.
24
24
 
25
- ### `agency pr [base-branch]`
25
+ ### `agency emit [base-branch]`
26
26
 
27
27
  Create an emit branch with smuggled files reverted to their merge-base state (removes additions/modifications to those files made on feature branch). Default branch name is current branch with `--PR` suffix.
28
28
 
29
- ### `agency push`
29
+ ### `agency push [base-branch]`
30
30
 
31
- Runs `agency pr`, pushes the branch, and then switches back to the source branch.
31
+ Runs `agency emit`, pushes the branch, and then switches back to the source branch.
32
+
33
+ **Options:**
34
+
35
+ - `--pr` - Open GitHub PR in browser after pushing (requires `gh` CLI)
36
+ - `--force` - Force push to remote if branch has diverged
37
+ - `--branch <name>` - Custom name for emit branch
32
38
 
33
39
  ### `agency merge`
34
40
 
35
- Runs `agency pr`, and then merges the PR back into the base branch locally.
41
+ Runs `agency emit`, and then merges the PR back into the base branch locally.
42
+
43
+ **Options:**
44
+
45
+ - `--squash` - Use squash merge instead of regular merge (stages changes, requires manual commit)
46
+ - `--push` - Push the base branch to origin after merging
36
47
 
37
48
  ## Other Commands
38
49
 
package/cli.ts CHANGED
@@ -4,12 +4,13 @@ import { parseArgs } from "util"
4
4
  import { Effect, Layer } from "effect"
5
5
  import { clean, help as cleanHelp } from "./src/commands/clean"
6
6
  import { init, help as initHelp } from "./src/commands/init"
7
- import { task, taskEdit, help as taskHelp } from "./src/commands/task"
7
+ import { task, taskEdit, help as taskHelp, editHelp } from "./src/commands/task"
8
8
  import { tasks, help as tasksHelp } from "./src/commands/tasks"
9
9
  import { emit, help as emitHelp } from "./src/commands/emit"
10
10
  import { emitted, help as emittedHelp } from "./src/commands/emitted"
11
11
  import { push, help as pushHelp } from "./src/commands/push"
12
12
  import { pull, help as pullHelp } from "./src/commands/pull"
13
+ import { rebase, help as rebaseHelp } from "./src/commands/rebase"
13
14
  import { base, help as baseHelp } from "./src/commands/base"
14
15
  import { switchBranch, help as switchHelp } from "./src/commands/switch"
15
16
  import { source, help as sourceHelp } from "./src/commands/source"
@@ -93,7 +94,7 @@ const commands: Record<string, Command> = {
93
94
  await runCommand(
94
95
  emit({
95
96
  baseBranch: args[0],
96
- branch: options.branch,
97
+ emit: options.emit || options.branch,
97
98
  silent: options.silent,
98
99
  force: options.verbose,
99
100
  verbose: options.verbose,
@@ -127,7 +128,7 @@ const commands: Record<string, Command> = {
127
128
  await runCommand(
128
129
  push({
129
130
  baseBranch: args[0],
130
- branch: options.branch,
131
+ emit: options.emit || options.branch,
131
132
  silent: options.silent,
132
133
  force: options.force,
133
134
  verbose: options.verbose,
@@ -155,6 +156,25 @@ const commands: Record<string, Command> = {
155
156
  },
156
157
  help: pullHelp,
157
158
  },
159
+ rebase: {
160
+ name: "rebase",
161
+ description: "Rebase source branch onto base branch",
162
+ run: async (args: string[], options: Record<string, any>) => {
163
+ if (options.help) {
164
+ console.log(rebaseHelp)
165
+ return
166
+ }
167
+ await runCommand(
168
+ rebase({
169
+ baseBranch: args[0],
170
+ emit: options.emit || options.branch,
171
+ silent: options.silent,
172
+ verbose: options.verbose,
173
+ }),
174
+ )
175
+ },
176
+ help: rebaseHelp,
177
+ },
158
178
  template: {
159
179
  name: "template",
160
180
  description: "Template management commands",
@@ -251,15 +271,16 @@ const commands: Record<string, Command> = {
251
271
  return
252
272
  }
253
273
  // Initialize with optional branch name
254
- const branch = args[0] || options.branch
274
+ const branch = args[0] || options.emit || options.branch
255
275
  await runCommand(
256
276
  task({
257
- branch,
277
+ emit: branch,
258
278
  silent: options.silent,
259
279
  verbose: options.verbose,
260
280
  task: options.task,
261
281
  from: options.from,
262
282
  fromCurrent: options["from-current"],
283
+ continue: options.continue,
263
284
  }),
264
285
  )
265
286
  },
@@ -288,21 +309,7 @@ const commands: Record<string, Command> = {
288
309
  description: "Open TASK.md in system editor",
289
310
  run: async (_args: string[], options: Record<string, any>) => {
290
311
  if (options.help) {
291
- console.log(`
292
- Usage: agency edit [options]
293
-
294
- Open TASK.md in the system editor for editing.
295
-
296
- Notes:
297
- - Requires TASK.md to exist (run 'agency task' first)
298
- - Respects VISUAL and EDITOR environment variables
299
- - On macOS, defaults to 'open' which uses the default app for .md files
300
- - On other platforms, defaults to 'vim'
301
- - The command waits for the editor to close before returning
302
-
303
- Example:
304
- agency edit # Open TASK.md in default editor
305
- `)
312
+ console.log(editHelp)
306
313
  return
307
314
  }
308
315
  await runCommand(
@@ -312,7 +319,7 @@ Example:
312
319
  }),
313
320
  )
314
321
  },
315
- help: `Open TASK.md in system editor`,
322
+ help: editHelp,
316
323
  },
317
324
  work: {
318
325
  name: "work",
@@ -401,6 +408,7 @@ Commands:
401
408
  emitted Get the name of the emitted branch
402
409
  push [base-branch] Emit, push to remote, return to source
403
410
  pull Pull commits from remote emit branch to source
411
+ rebase [base-branch] Rebase source branch onto base branch
404
412
  base Get or set the base branch
405
413
  set <branch> Set the base branch for the current feature branch
406
414
  get Get the configured base branch
@@ -508,9 +516,11 @@ try {
508
516
  type: "string",
509
517
  short: "t",
510
518
  },
519
+ emit: {
520
+ type: "string",
521
+ },
511
522
  branch: {
512
523
  type: "string",
513
- short: "b",
514
524
  },
515
525
  task: {
516
526
  type: "string",
@@ -521,6 +531,9 @@ try {
521
531
  "from-current": {
522
532
  type: "boolean",
523
533
  },
534
+ continue: {
535
+ type: "boolean",
536
+ },
524
537
  json: {
525
538
  type: "boolean",
526
539
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markjaquith/agency",
3
- "version": "0.5.1",
3
+ "version": "0.6.1",
4
4
  "description": "Manages personal agents files",
5
5
  "license": "MIT",
6
6
  "author": "Mark Jaquith",
@@ -130,7 +130,7 @@ describe("emit command", () => {
130
130
 
131
131
  // Skip filter for speed - we're just testing branch creation
132
132
  await runTestEffect(
133
- emit({ branch: "custom-pr", silent: true, skipFilter: true }),
133
+ emit({ emit: "custom-pr", silent: true, skipFilter: true }),
134
134
  )
135
135
 
136
136
  const branches = await getGitOutput(tempDir, [
@@ -377,6 +377,76 @@ describe("emit command", () => {
377
377
  expect(logOutput).toContain("Add feature file")
378
378
  expect(logOutput).not.toContain("Add AGENTS.md")
379
379
  })
380
+
381
+ test("filters pre-existing CLAUDE.md that gets edited by agency", async () => {
382
+ if (!hasGitFilterRepo) {
383
+ console.log("Skipping test: git-filter-repo not installed")
384
+ return
385
+ }
386
+
387
+ // Start fresh on main branch
388
+ await checkoutBranch(tempDir, "main")
389
+
390
+ // Create CLAUDE.md on main branch (simulating pre-existing file)
391
+ await Bun.write(
392
+ join(tempDir, "CLAUDE.md"),
393
+ "# Original Claude Instructions\n\nSome content here.\n",
394
+ )
395
+ await addAndCommit(tempDir, "CLAUDE.md", "Add CLAUDE.md")
396
+
397
+ // Create a new feature branch
398
+ await createBranch(tempDir, "agency/claude-test")
399
+
400
+ // Initialize agency on this branch (this will modify CLAUDE.md)
401
+ await Bun.write(
402
+ join(tempDir, "agency.json"),
403
+ JSON.stringify({
404
+ version: 1,
405
+ injectedFiles: ["AGENTS.md"],
406
+ template: "test",
407
+ createdAt: new Date().toISOString(),
408
+ }),
409
+ )
410
+ await Bun.write(join(tempDir, "AGENTS.md"), "# Test AGENTS\n")
411
+
412
+ // Simulate what agency task does - inject into CLAUDE.md
413
+ const originalClaude = await Bun.file(join(tempDir, "CLAUDE.md")).text()
414
+ const modifiedClaude = `${originalClaude}\n# Agency References\n@AGENTS.md\n@TASK.md\n`
415
+ await Bun.write(join(tempDir, "CLAUDE.md"), modifiedClaude)
416
+
417
+ await addAndCommit(
418
+ tempDir,
419
+ "agency.json AGENTS.md CLAUDE.md",
420
+ "Initialize agency files",
421
+ )
422
+
423
+ // Add a feature file
424
+ await createCommit(tempDir, "Feature commit")
425
+
426
+ // Create emit branch (this should filter CLAUDE.md)
427
+ await runTestEffect(emit({ silent: true, baseBranch: "main" }))
428
+
429
+ // Should still be on source branch
430
+ const currentBranch = await getCurrentBranch(tempDir)
431
+ expect(currentBranch).toBe("agency/claude-test")
432
+
433
+ // Switch to emit branch to verify CLAUDE.md is reverted to main's version
434
+ await checkoutBranch(tempDir, "claude-test")
435
+
436
+ const files = await getGitOutput(tempDir, ["ls-files"])
437
+ expect(files).toContain("CLAUDE.md") // File should exist (from main)
438
+ expect(files).not.toContain("AGENTS.md") // Should be filtered
439
+ expect(files).not.toContain("TASK.md") // Should be filtered
440
+ expect(files).toContain("test.txt") // Feature file should exist
441
+
442
+ // Verify CLAUDE.md was reverted to original (no agency references)
443
+ const claudeContent = await Bun.file(join(tempDir, "CLAUDE.md")).text()
444
+ expect(claudeContent).toBe(
445
+ "# Original Claude Instructions\n\nSome content here.\n",
446
+ )
447
+ expect(claudeContent).not.toContain("@AGENTS.md")
448
+ expect(claudeContent).not.toContain("@TASK.md")
449
+ })
380
450
  })
381
451
 
382
452
  describe("error handling", () => {
@@ -24,7 +24,8 @@ import {
24
24
  import { withSpinner } from "../utils/spinner"
25
25
 
26
26
  interface EmitOptions extends BaseCommandOptions {
27
- branch?: string
27
+ emit?: string
28
+ branch?: string // Deprecated: use emit instead
28
29
  baseBranch?: string
29
30
  force?: boolean
30
31
  /** Skip the git-filter-repo step (for testing) */
@@ -101,9 +102,12 @@ const emitCore = (gitRoot: string, options: EmitOptions) =>
101
102
 
102
103
  let emitBranchName: string
103
104
 
104
- if (options.branch) {
105
+ // Support both --emit (new) and --branch (deprecated)
106
+ const explicitBranchName = options.emit || options.branch
107
+
108
+ if (explicitBranchName) {
105
109
  // Explicit branch name provided via CLI
106
- emitBranchName = options.branch
110
+ emitBranchName = explicitBranchName
107
111
  } else if (metadata?.emitBranch) {
108
112
  // Use emitBranch from agency.json (source of truth)
109
113
  emitBranchName = metadata.emitBranch
@@ -267,7 +271,13 @@ const ensureEmitBranchInMetadata = (
267
271
 
268
272
  // Stage and commit the change
269
273
  yield* git.gitAdd(["agency.json"], gitRoot)
270
- yield* git.gitCommit("chore: agency emit", gitRoot, { noVerify: true })
274
+ // Note: baseBranch not available in this helper function context
275
+ // This commit only happens when backfilling emitBranch in legacy repos
276
+ yield* git.gitCommit(
277
+ `chore: agency emit ${currentBranch} → ${emitBranchName}`,
278
+ gitRoot,
279
+ { noVerify: true },
280
+ )
271
281
  })
272
282
 
273
283
  /**
@@ -476,7 +486,8 @@ Arguments:
476
486
  If not provided, will use saved config or prompt interactively
477
487
 
478
488
  Options:
479
- -b, --branch Custom name for emit branch (defaults to pattern from config)
489
+ --emit Custom name for emit branch (defaults to pattern from config)
490
+ --branch (Deprecated: use --emit) Custom name for emit branch
480
491
  -f, --force Force emit branch creation even if current branch looks like an emit branch
481
492
 
482
493
  Configuration:
@@ -119,7 +119,7 @@ describe("push command", () => {
119
119
  await runTestEffect(
120
120
  push({
121
121
  baseBranch: "main",
122
- branch: "custom-pr-branch",
122
+ emit: "custom-pr-branch",
123
123
  silent: true,
124
124
  skipFilter: true,
125
125
  }),
@@ -21,7 +21,8 @@ import { spawnProcess } from "../utils/process"
21
21
 
22
22
  interface PushOptions extends BaseCommandOptions {
23
23
  baseBranch?: string
24
- branch?: string
24
+ emit?: string
25
+ branch?: string // Deprecated: use emit instead
25
26
  force?: boolean
26
27
  pr?: boolean
27
28
  skipFilter?: boolean
@@ -77,10 +78,10 @@ const pushCore = (gitRoot: string, options: PushOptions) =>
77
78
 
78
79
  // Step 1: Create emit branch (agency emit)
79
80
  verboseLog("Step 1: Emitting...")
80
- // Use emit command
81
+ // Use emit command - prefer emit option, fallback to branch for backward compatibility
81
82
  const prEffectWithOptions = emit({
82
83
  baseBranch: options.baseBranch,
83
- branch: options.branch,
84
+ emit: options.emit || options.branch,
84
85
  silent: true, // Suppress emit command output, we'll provide our own
85
86
  force: options.force,
86
87
  verbose: options.verbose,
@@ -99,7 +100,8 @@ const pushCore = (gitRoot: string, options: PushOptions) =>
99
100
 
100
101
  // Compute the emit branch name (emit() command now stays on source branch)
101
102
  // Use the branchInfo we already computed earlier
102
- const emitBranchName = options.branch || branchInfo.emitBranch
103
+ const emitBranchName =
104
+ options.emit || options.branch || branchInfo.emitBranch
103
105
  log(done(`Emitted ${highlight.branch(emitBranchName)}`))
104
106
 
105
107
  // Step 2: Push to remote (git push)
@@ -327,7 +329,8 @@ Arguments:
327
329
  If not provided, will use saved config or auto-detect
328
330
 
329
331
  Options:
330
- -b, --branch Custom name for emit branch (defaults to pattern from config)
332
+ --emit Custom name for emit branch (defaults to pattern from config)
333
+ --branch (Deprecated: use --emit) Custom name for emit branch
331
334
  -f, --force Force push to remote if branch has diverged
332
335
  --pr Open GitHub PR in browser after pushing (requires gh CLI)
333
336