@markjaquith/agency 0.6.1 → 0.7.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markjaquith/agency",
3
- "version": "0.6.1",
3
+ "version": "0.7.0",
4
4
  "description": "Manages personal agents files",
5
5
  "license": "MIT",
6
6
  "author": "Mark Jaquith",
@@ -14,7 +14,7 @@
14
14
  "agency": "cli.ts"
15
15
  },
16
16
  "scripts": {
17
- "test": "bun test",
17
+ "test": "find src -name '*.test.ts' -print0 | xargs -0 -n 1 -P 4 bun test",
18
18
  "format": "prettier --write .",
19
19
  "format:check": "prettier --check .",
20
20
  "knip": "knip --production",
@@ -278,23 +278,41 @@ const openGitHubPR = (
278
278
  const { verbose = false } = options
279
279
 
280
280
  // Run gh pr create --web with --head to specify the emit branch
281
- const ghResult = yield* spawnProcess(
281
+ // Set environment variables to ensure non-interactive mode in CI
282
+ // Add a 3 second timeout to prevent hanging in CI environments
283
+ const ghEffect = spawnProcess(
282
284
  ["gh", "pr", "create", "--web", "--head", branchName],
283
285
  {
284
286
  cwd: gitRoot,
285
287
  stdout: verbose ? "inherit" : "pipe",
286
288
  stderr: "pipe",
289
+ env: {
290
+ GH_PROMPT_DISABLED: "1",
291
+ NO_COLOR: "1",
292
+ },
287
293
  },
288
294
  ).pipe(
289
- Effect.catchAll((error) =>
290
- Effect.succeed({
291
- exitCode: error.exitCode,
295
+ Effect.timeout("3 seconds"),
296
+ Effect.catchAll((error) => {
297
+ // Handle timeout or process errors
298
+ if (error._tag === "TimeoutException") {
299
+ return Effect.succeed({
300
+ exitCode: -1,
301
+ stdout: "",
302
+ stderr: "gh command timed out after 3 seconds",
303
+ })
304
+ }
305
+ // Handle ProcessError
306
+ return Effect.succeed({
307
+ exitCode: (error as any).exitCode ?? -1,
292
308
  stdout: "",
293
- stderr: error.stderr,
294
- }),
295
- ),
309
+ stderr: (error as any).stderr ?? String(error),
310
+ })
311
+ }),
296
312
  )
297
313
 
314
+ const ghResult = yield* ghEffect
315
+
298
316
  if (ghResult.exitCode !== 0) {
299
317
  return yield* Effect.fail(
300
318
  new Error(`gh CLI command failed: ${ghResult.stderr.trim()}`),
@@ -759,6 +759,49 @@ describe("task command", () => {
759
759
 
760
760
  expect(await fileExists(join(tempDir, "AGENTS.md"))).toBe(true)
761
761
  })
762
+
763
+ test("fails when on agency source branch without branch name in silent mode", async () => {
764
+ await initGitRepo(tempDir)
765
+ process.chdir(tempDir)
766
+
767
+ await initAgency(tempDir, "test")
768
+
769
+ // Create a task branch with agency files
770
+ await runTestEffect(task({ silent: true, emit: "original-feature" }))
771
+
772
+ // Verify we're on the source branch with agency.json
773
+ const currentBranch = await getCurrentBranch(tempDir)
774
+ expect(currentBranch).toBe("agency/original-feature")
775
+ expect(await fileExists(join(tempDir, "agency.json"))).toBe(true)
776
+
777
+ // Try to run agency task again without providing a branch name
778
+ // This should fail because we're on an agency source branch
779
+ await expect(runTestEffect(task({ silent: true }))).rejects.toThrow(
780
+ "agency source branch",
781
+ )
782
+ })
783
+
784
+ test("prompts for branch name when on agency source branch", async () => {
785
+ await initGitRepo(tempDir)
786
+ process.chdir(tempDir)
787
+
788
+ await initAgency(tempDir, "test")
789
+
790
+ // Create a task branch with agency files
791
+ await runTestEffect(task({ silent: true, emit: "original-feature" }))
792
+
793
+ // Verify we're on the source branch with agency.json
794
+ const currentBranch = await getCurrentBranch(tempDir)
795
+ expect(currentBranch).toBe("agency/original-feature")
796
+ expect(await fileExists(join(tempDir, "agency.json"))).toBe(true)
797
+
798
+ // With a branch name provided, it should work
799
+ await runTestEffect(task({ silent: true, emit: "new-feature" }))
800
+
801
+ // Verify we're now on the new branch
802
+ const newBranch = await getCurrentBranch(tempDir)
803
+ expect(newBranch).toBe("agency/new-feature")
804
+ })
762
805
  })
763
806
 
764
807
  describe("template-based source files", () => {
@@ -212,11 +212,21 @@ const taskContinue = (options: TaskOptions) =>
212
212
  targetPath,
213
213
  baseBranchToBranchFrom,
214
214
  )
215
+ // Calculate the emit branch name for display
216
+ const cleanBranchForDisplay =
217
+ extractCleanBranch(sourceBranchName, config.sourceBranchPattern) ||
218
+ sourceBranchName
219
+ const emitBranchForDisplay = makeEmitBranchName(
220
+ cleanBranchForDisplay,
221
+ config.emitBranch,
222
+ )
223
+
215
224
  log(
216
- done(
217
- `Created and switched to branch ${highlight.branch(sourceBranchName)} based on ${highlight.branch(baseBranchToBranchFrom)}`,
225
+ info(
226
+ `(${highlight.branch(baseBranchToBranchFrom)}) ${highlight.branch(sourceBranchName)} ${highlight.branch(emitBranchForDisplay)}`,
218
227
  ),
219
228
  )
229
+ log(done(`Created and switched to ${highlight.branch(sourceBranchName)}`))
220
230
 
221
231
  // Calculate the new emit branch name
222
232
  const newEmitBranchName = makeEmitBranchName(branchName, config.emitBranch)
@@ -367,6 +377,11 @@ export const task = (options: TaskOptions = {}) =>
367
377
  const isFeature = yield* git.isFeatureBranch(currentBranch, targetPath)
368
378
  verboseLog(`Is feature branch: ${isFeature}`)
369
379
 
380
+ // Check if we're on an agency source branch (has agency.json with backpacked files)
381
+ const agencyJsonPath = resolve(targetPath, "agency.json")
382
+ const hasAgencyJson = yield* fs.exists(agencyJsonPath)
383
+ verboseLog(`Has agency.json: ${hasAgencyJson}`)
384
+
370
385
  // Determine base branch to branch from
371
386
  let baseBranchToBranchFrom: string | undefined
372
387
 
@@ -458,8 +473,8 @@ export const task = (options: TaskOptions = {}) =>
458
473
  // Determine branch name logic
459
474
  let branchName = options.emit || options.branch
460
475
 
461
- // If on main branch or using --from without a branch name, prompt for it (unless in silent mode)
462
- if ((!isFeature || options.from) && !branchName) {
476
+ // If on main branch, using --from, or on an agency source branch without a branch name, prompt for it (unless in silent mode)
477
+ if ((!isFeature || options.from || hasAgencyJson) && !branchName) {
463
478
  if (silent) {
464
479
  if (options.from) {
465
480
  return yield* Effect.fail(
@@ -469,6 +484,15 @@ export const task = (options: TaskOptions = {}) =>
469
484
  ),
470
485
  )
471
486
  }
487
+ if (hasAgencyJson) {
488
+ return yield* Effect.fail(
489
+ new Error(
490
+ `You're currently on ${highlight.branch(currentBranch)}, which is an agency source branch.\n` +
491
+ `Branch name is required when re-importing backpacked files.\n` +
492
+ `Use: 'agency task <branch-name>' or 'agency task --continue <branch-name>'`,
493
+ ),
494
+ )
495
+ }
472
496
  return yield* Effect.fail(
473
497
  new Error(
474
498
  `You're currently on ${highlight.branch(currentBranch)}, which appears to be your main branch.\n` +
@@ -823,11 +847,27 @@ const createFeatureBranchEffect = (
823
847
  }
824
848
 
825
849
  yield* git.createBranch(branchName, targetPath, baseBranch)
850
+
851
+ // Load config for emit pattern calculation
852
+ const configService = yield* ConfigService
853
+ const config = yield* configService.loadConfig()
854
+
855
+ // Calculate the emit branch name for display
856
+ const cleanBranchForDisplay =
857
+ extractCleanBranch(branchName, config.sourceBranchPattern) || branchName
858
+ const emitBranchForDisplay = makeEmitBranchName(
859
+ cleanBranchForDisplay,
860
+ config.emitBranch,
861
+ )
862
+
826
863
  log(
827
- done(
828
- `Created and switched to branch ${highlight.branch(branchName)}${baseBranch ? ` based on ${highlight.branch(baseBranch)}` : ""}`,
864
+ info(
865
+ baseBranch
866
+ ? `(${highlight.branch(baseBranch)}) ${highlight.branch(branchName)} → ${highlight.branch(emitBranchForDisplay)}`
867
+ : `${highlight.branch(branchName)} → ${highlight.branch(emitBranchForDisplay)}`,
829
868
  ),
830
869
  )
870
+ log(done(`Created and switched to ${highlight.branch(branchName)}`))
831
871
  })
832
872
 
833
873
  // Helper: Discover template files
@@ -1,4 +1,4 @@
1
1
  {
2
- "$schema": "https://opencode.ai/config.json",
3
- "instructions": ["AGENCY.md", "TASK.md"]
2
+ "$schema": "https://opencode.ai/config.json",
3
+ "instructions": ["AGENCY.md", "TASK.md"]
4
4
  }