@markjaquith/agency 2.74.0 → 3.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.
Files changed (57) hide show
  1. package/README.md +23 -121
  2. package/cli-main.ts +0 -26
  3. package/cli.ts +1 -17
  4. package/package.json +1 -2
  5. package/schemas/agency-graph-v1.schema.json +1 -1
  6. package/src/cli-parser.test.ts +0 -19
  7. package/src/cli-parser.ts +0 -23
  8. package/src/cli.test.ts +3 -3
  9. package/src/commands/context.test.ts +0 -67
  10. package/src/commands/init.test.ts +1 -2
  11. package/src/commands/pr.test.ts +2 -87
  12. package/src/commands/pr.ts +2 -79
  13. package/src/commands/push.test.ts +1 -2
  14. package/src/graph-schema.ts +1 -1
  15. package/src/services/ArchiveBulkService.test.ts +1 -142
  16. package/src/services/ArchiveService.test.ts +1 -172
  17. package/src/services/ArchiveService.ts +0 -26
  18. package/src/services/ContextService.ts +3 -47
  19. package/src/services/DoctorService.ts +8 -24
  20. package/src/services/GraphService.test.ts +1 -0
  21. package/src/services/GraphService.ts +2 -5
  22. package/src/services/PhaseService.ts +70 -191
  23. package/src/services/PullRequestService.test.ts +1 -45
  24. package/src/services/PullRequestService.ts +1 -22
  25. package/src/services/PushService.test.ts +29 -295
  26. package/src/services/PushService.ts +10 -239
  27. package/src/services/RepositoryService.test.ts +1 -61
  28. package/src/services/RepositoryService.ts +2 -13
  29. package/src/services/ReviewService.test.ts +1 -84
  30. package/src/services/ReviewService.ts +9 -48
  31. package/src/services/SyncService.test.ts +0 -81
  32. package/src/services/SyncService.ts +12 -39
  33. package/src/services/TaskPhaseService.test.ts +0 -80
  34. package/src/services/TaskService.ts +1 -9
  35. package/src/services/VersionControlService.test.ts +4 -117
  36. package/src/services/VersionControlService.ts +3 -426
  37. package/src/services/WorkbaseService.test.ts +0 -20
  38. package/src/services/WorkbaseService.ts +1 -18
  39. package/src/services/WorktreeService.test.ts +1 -705
  40. package/src/services/WorktreeService.ts +395 -1604
  41. package/src/services/push-validation.ts +0 -7
  42. package/src/test-utils.ts +0 -4
  43. package/src/workbase/AGENTS.md +4 -5
  44. package/src/workbase/checkout-command.test.ts +1 -1
  45. package/src/workbase/checkout-command.ts +1 -1
  46. package/src/workbase/delivery-command.test.ts +4 -35
  47. package/src/workbase/delivery-command.ts +2 -15
  48. package/src/workbase/schemas.test.ts +0 -19
  49. package/src/workbase/schemas.ts +1 -2
  50. package/src/commands/vcs.test.ts +0 -75
  51. package/src/commands/vcs.ts +0 -72
  52. package/src/services/VcsMigrationService.test.ts +0 -348
  53. package/src/services/VcsMigrationService.ts +0 -857
  54. package/src/vcs-status-fast.ts +0 -310
  55. package/src/workbase/version-control.ts +0 -5
  56. package/src/workbase/workspace-command.test.ts +0 -70
  57. package/src/workbase/workspace-command.ts +0 -63
package/README.md CHANGED
@@ -9,7 +9,6 @@ read or write.
9
9
 
10
10
  - [Bun](https://bun.sh) 1.0 or newer
11
11
  - Git
12
- - [Jujutsu](https://jj-vcs.github.io/jj/) is preferred when available
13
12
  - [GitHub CLI](https://cli.github.com/) for `agency pr`
14
13
  - OpenCode, Claude Code, or a configured agent for `agency work`
15
14
 
@@ -151,15 +150,16 @@ the writable checkout's `.claude/skills`, `.agents/skills`,
151
150
  `.opencode/{skill,skills}`, and `.pi/skills` directories. `agency context`
152
151
  remains the authority for writes; reference checkouts remain read-only.
153
152
 
154
- Repository aliases, the version-control backend, and canonical fetch remotes are
155
- declared in tracked `agency.json`; local clones and symlinks remain ignored under
156
- `repos/{alias}`. A declaration contains no local path, symlink target, checkout,
157
- or credential:
153
+ Repository aliases and canonical fetch remotes are declared in tracked
154
+ `agency.json`; local Git clones and symlinks remain ignored under
155
+ `repos/{alias}`. A declaration contains no local path, symlink target, worktree,
156
+ or credential. The optional `vcs` field accepts only `"git"`; omitting it also
157
+ selects Git:
158
158
 
159
159
  ```json
160
160
  {
161
161
  "version": 2,
162
- "vcs": "jj",
162
+ "vcs": "git",
163
163
  "repositories": {
164
164
  "frontend": {
165
165
  "remote": "git@example.com:team/frontend.git"
@@ -168,39 +168,9 @@ or credential:
168
168
  }
169
169
  ```
170
170
 
171
- New workbases select `"jj"` when the `jj` executable is available and otherwise
172
- select `"git"`. Existing version 2 workbases without `vcs` remain Git workbases.
173
- The selected backend is a workbase-level invariant: jj workbases create
174
- non-colocated managed clones and jj workspaces, while Git workbases continue to
175
- use bare repositories and Git worktrees. Existing colocated jj repositories are
176
- also supported.
177
-
178
- Agency uses jj-native commands for repository validity, workspace registration,
179
- working-copy state, revisions, bookmarks, ancestry, fetch, and push. Raw Git is
180
- limited to backing-store plumbing such as durable review refs and integrations
181
- that require Git, including GitHub CLI commit discovery. For those operations,
182
- Agency obtains the backing repository with `jj git root` and supplies it as
183
- `GIT_DIR`; it does not use raw Git to infer jj workspace state.
184
-
185
- Inspect the current backend and migration readiness with:
186
-
187
- ```bash
188
- agency vcs status
189
- agency vcs migrate jj # dry-run
190
- agency vcs migrate jj --apply
191
- agency vcs migrate git --apply
192
- ```
193
-
194
- Migration is workbase-wide and transactional. Agency locks every execution
195
- unit, requires clean registered workspaces, blocks active claims and working or
196
- delegated units, converts repository metadata, recreates checkout paths with the
197
- target backend, and updates `agency.json` last. Migration from jj to Git also
198
- blocks jj-only heads that are not preserved by a bookmark or workspace. Failed
199
- migrations restore the source repositories and workspaces when rollback is
200
- possible and report explicit manual recovery paths otherwise.
201
- Converting a non-colocated jj workbase to Git is currently blocked before any
202
- mutation because its backing Git object store is inside `.jj`; first convert the
203
- repositories to colocated jj so the Git store survives metadata removal.
171
+ Agency materializes declared remotes as bare Git repositories and creates
172
+ managed Git worktrees for execution units. Linked aliases remain symlinks to
173
+ existing Git repositories.
204
174
 
205
175
  Existing version 2 workbases without `repositories` remain valid. Run
206
176
  `agency repo setup` to preview deterministic adoption of legacy local aliases;
@@ -274,63 +244,13 @@ Agency also reconciles known tool-owned artifacts such as Worktrunk's
274
244
  new and existing managed worktrees clean without changing the repository's
275
245
  tracked ignore configuration or overwriting user-maintained local excludes.
276
246
 
277
- The configured command applies only to the writable checkout of a Git workbase.
247
+ The configured command applies only to the writable checkout.
278
248
  Supplemental read-only repositories remain detached Git worktrees at their
279
- declared refs so they do not acquire writable branches. Jj workbases always use
280
- jj workspaces and ignore this Git-specific customization.
281
-
282
- ### Custom Jj Workspace Command
283
-
284
- Jj workbases normally create managed workspaces with `jj workspace add`. Set
285
- `workspaceCreateCommand` to an argv template when another tool should create or
286
- adopt a prepared workspace directly at Agency's destination:
287
-
288
- ```json
289
- {
290
- "version": 2,
291
- "vcs": "jj",
292
- "workspaceCreateCommand": [
293
- "my-prewarm-tool",
294
- "adopt",
295
- "--repo",
296
- "{repo}",
297
- "--destination",
298
- "{workspace}",
299
- "--name",
300
- "{name}",
301
- "--revision",
302
- "{revision}"
303
- ]
304
- }
305
- ```
306
-
307
- Available placeholders are:
308
-
309
- - `{repo}`: absolute repository alias path under `repos/`
310
- - `{workspace}`: absolute managed workspace path Agency requires
311
- - `{name}`: unique jj workspace name Agency requires
312
- - `{revision}`: exact commit the new working copy must be based on
313
- - `{kind}`: `writable` or `reference`
314
- - `{requestedRef}`: configured branch, reference, or review commit
315
-
316
- `{repo}`, `{workspace}`, `{name}`, and `{revision}` are required. Agency invokes
317
- the command directly without a shell and sets matching `AGENCY_REPO`,
318
- `AGENCY_WORKSPACE`, `AGENCY_WORKSPACE_NAME`, `AGENCY_REVISION`,
319
- `AGENCY_CHECKOUT_KIND`, and `AGENCY_REQUESTED_REF` environment variables. The
320
- command applies to each new jj checkout and must leave `{workspace}` registered
321
- under `{name}` with its working-copy parent at `{revision}`. This lets a prewarm
322
- tool move or adopt a prepared workspace without first paying for Agency's normal
323
- full checkout.
324
-
325
- Agency validates the registration, name, path, and revision before running any
326
- `postCheckoutCommand`. A failed command or validation removes a partially
327
- created workspace when possible and otherwise reports manual recovery. Resume
328
- restoration continues to use Agency's built-in exact-target recovery path.
329
- Git workbases ignore this jj-specific customization.
249
+ declared refs so they do not acquire writable branches.
330
250
 
331
251
  ### Post-checkout Commands
332
252
 
333
- Each repository declaration may provide a VCS-neutral `postCheckoutCommand` argv
253
+ Each repository declaration may provide a `postCheckoutCommand` argv
334
254
  template for repository-specific setup. Agency invokes it directly, without a
335
255
  shell, with the new checkout as its working directory:
336
256
 
@@ -346,11 +266,10 @@ shell, with the new checkout as its working directory:
346
266
  }
347
267
  ```
348
268
 
349
- The hook runs for each newly created managed checkout, including writable and
350
- reference checkouts, after Git worktree or jj workspace creation has completed
351
- and Agency has validated the checkout. It does not run for a reused checkout or
352
- for inspection-only commands. A custom `worktreeCreateCommand` or
353
- `workspaceCreateCommand` completes and is validated before this hook runs.
269
+ The hook runs for each newly created managed Git worktree, including writable
270
+ and reference worktrees, after Agency has validated it. It does not run for a
271
+ reused worktree or for inspection-only commands. A custom
272
+ `worktreeCreateCommand` completes and is validated before this hook runs.
354
273
 
355
274
  Available placeholders and matching environment variables are:
356
275
 
@@ -362,7 +281,7 @@ Available placeholders and matching environment variables are:
362
281
  | `{checkoutKind}` | `AGENCY_CHECKOUT_KIND` | `writable` or `reference` |
363
282
  | `{requestedRef}` | `AGENCY_REQUESTED_REF` | Requested branch, reference, or review commit |
364
283
  | `{base}` | `AGENCY_BASE` | Configured execution base |
365
- | `{vcs}` | `AGENCY_VCS` | `git` or `jj` |
284
+ | `{vcs}` | `AGENCY_VCS` | `git` |
366
285
  | `{workbaseRoot}` | `AGENCY_WORKBASE_ROOT` | Absolute workbase root |
367
286
  | `{taskId}` | `AGENCY_TASK_ID` | Task ID |
368
287
  | `{phaseId}` | `AGENCY_PHASE_ID` | Phase ID |
@@ -1162,15 +1081,6 @@ conflicting worktrees. Repair is deliberately conservative: it repairs safe Git
1162
1081
  registration issues and materializes missing checkouts, but never switches a
1163
1082
  branch, resets a commit, or discards uncommitted work.
1164
1083
 
1165
- For jj, explicit removal is a suspend operation. Agency records each workspace's
1166
- exact `@` commit and stable change ID in `.agency-jj-resume.json`, roots that
1167
- commit with an internal local bookmark, and only then forgets the workspace.
1168
- The next `work prepare` validates all three identities, restores the workspace by
1169
- editing the recorded commit, and consumes the resume metadata and bookmarks.
1170
- Missing, ambiguous, or conflicting resume state stops instead of falling back to
1171
- the declared delivery bookmark, `@-`, or the base. Unknown checkout cleanliness
1172
- also stops removal. Git worktree removal and preparation are unchanged.
1173
-
1174
1084
  Agency launches every agent beside its epic or task document. Single-phase tasks
1175
1085
  and phases first fetch repositories and create or reuse worktrees under `code/`,
1176
1086
  then launch the execution agent from the task directory with absolute context
@@ -1195,18 +1105,11 @@ that the declared base is in the publication history, validates every outgoing
1195
1105
  commit's description, author, and conflict state, and refuses non-fast-forward
1196
1106
  updates.
1197
1107
 
1198
- For Git, YAML `branch` must exactly match the checked-out local branch, the
1199
- worktree must be clean, and `HEAD` is pushed with upstream tracking. For jj, YAML
1200
- `branch` is the authoritative delivery bookmark and need not exist before
1201
- publication. Agency publishes `@` unless it is the canonical empty, undescribed
1202
- post-commit working copy, in which case it publishes `@-`; described empty
1203
- changes remain intentional publication tips. Missing descriptions or authors
1204
- stop with exact change IDs and remediation commands. Agency creates or safely
1205
- advances only the declared bookmark and never invents a `push-*` bookmark.
1206
- If the fetched remote base advanced outside the local stack, Agency prints the
1207
- exact `jj rebase` command needed to move the stack onto `<base>@<remote>`. Push
1208
- reports deterministic fetch, inspection, validation, and publication progress
1209
- on stderr, including while `--json` reserves stdout for one machine result.
1108
+ YAML `branch` must exactly match the checked-out local branch, the worktree must
1109
+ be clean, and `HEAD` is pushed with upstream tracking. Missing commit descriptions
1110
+ or authors stop publication with exact commits and remediation commands. Push
1111
+ reports deterministic fetch, inspection, validation, and publication progress on
1112
+ stderr, including while `--json` reserves stdout for one machine result.
1210
1113
 
1211
1114
  Task-aware `agency pr create <task-id> [phase-id]` uses Agency's delivery flow,
1212
1115
  including readiness checks and durable PR recording. It accepts draft, title,
@@ -1216,8 +1119,7 @@ Other `agency pr`
1216
1119
  invocations forward every argument to `gh pr`. From an execution task or phase
1217
1120
  directory, including descendants, passthrough runs in that execution unit's
1218
1121
  authoritative writable checkout. Otherwise it runs in the caller's current
1219
- directory. In jj workbases, Agency also supplies the repository and the work
1220
- item's declared branch to subcommands that would otherwise infer Git context.
1122
+ directory.
1221
1123
 
1222
1124
  ### Status and Validation
1223
1125
 
package/cli-main.ts CHANGED
@@ -48,12 +48,9 @@ import { DoctorService } from "./src/services/DoctorService"
48
48
  import { ReviewService } from "./src/services/ReviewService"
49
49
  import {
50
50
  GitVersionControlService,
51
- JjVersionControlService,
52
51
  VersionControlService,
53
52
  } from "./src/services/VersionControlService"
54
53
  import { review, help as reviewHelp } from "./src/commands/review"
55
- import { vcs, help as vcsHelp } from "./src/commands/vcs"
56
- import { VcsMigrationService } from "./src/services/VcsMigrationService"
57
54
  import { act, help as actHelp } from "./src/commands/act"
58
55
  import {
59
56
  claimCommand,
@@ -74,9 +71,7 @@ const CliLayer = Layer.mergeAll(
74
71
  FileSystemService.Default,
75
72
  WorkbaseService.Default,
76
73
  GitVersionControlService.Default,
77
- JjVersionControlService.Default,
78
74
  VersionControlService.Default,
79
- VcsMigrationService.Default,
80
75
  RepositoryService.Default,
81
76
  EpicService.Default,
82
77
  TaskService.Default,
@@ -623,26 +618,6 @@ const commands: Record<string, Command> = {
623
618
  )
624
619
  },
625
620
  },
626
- vcs: {
627
- run: async (args: string[], options: Record<string, any>) => {
628
- if (options.help) {
629
- console.log(vcsHelp)
630
- return
631
- }
632
- await runCommand(
633
- vcs({
634
- subcommand: args[0],
635
- target: args[1],
636
- apply: options.apply,
637
- dryRun: options["dry-run"],
638
- json: options.json,
639
- silent: options.silent,
640
- verbose: options.verbose,
641
- cwd: options.cwd,
642
- }),
643
- )
644
- },
645
- },
646
621
  next: {
647
622
  run: async (_args: string[], options: Record<string, any>) => {
648
623
  if (options.help) return console.log(nextHelp)
@@ -802,7 +777,6 @@ Commands:
802
777
  task <subcommand> Manage tasks
803
778
  work [directory|task] Work on an epic, task, or phase
804
779
  worktree <subcommand> Inspect and maintain managed workspaces
805
- vcs <subcommand> Inspect or migrate the version-control backend
806
780
  next List or select ready execution units
807
781
  pr create / pr [...] Create an Agency PR or run gh pr with repository focus
808
782
  push Validate and publish the current execution unit
package/cli.ts CHANGED
@@ -1,19 +1,3 @@
1
1
  #!/usr/bin/env bun
2
2
 
3
- const args = Bun.argv.slice(2)
4
- const fastVcsStatus =
5
- args[0] === "vcs" &&
6
- args[1] === "status" &&
7
- (args.length === 2 || (args.length === 3 && args[2] === "--json"))
8
-
9
- if (fastVcsStatus) {
10
- try {
11
- const { runVcsStatusFast } = await import("./src/vcs-status-fast")
12
- if (!(await runVcsStatusFast(args[2] === "--json")))
13
- await import("./cli-main")
14
- } catch {
15
- await import("./cli-main")
16
- }
17
- } else {
18
- await import("./cli-main")
19
- }
3
+ await import("./cli-main")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markjaquith/agency",
3
- "version": "2.74.0",
3
+ "version": "3.0.0",
4
4
  "description": "Manage agentic work across repositories with durable workbases",
5
5
  "keywords": [
6
6
  "agents",
@@ -83,7 +83,6 @@
83
83
  "benchmark:graph": "bun scripts/benchmark-graph.ts",
84
84
  "benchmark:push": "bun scripts/benchmark-push.ts",
85
85
  "benchmark:archive": "bun scripts/benchmark-archive.ts",
86
- "benchmark:vcs": "bun scripts/benchmark-vcs.ts",
87
86
  "benchmark:sync": "bun scripts/benchmark-sync.ts",
88
87
  "benchmark:task": "bun scripts/benchmark-task.ts",
89
88
  "benchmark:validate": "bun scripts/benchmark-validate.ts",
@@ -22,7 +22,7 @@
22
22
  "required": ["version"],
23
23
  "properties": {
24
24
  "version": { "const": 2 },
25
- "vcs": { "enum": ["git", "jj"] },
25
+ "vcs": { "enum": ["git"] },
26
26
  "root": { "type": "string" }
27
27
  }
28
28
  },
@@ -674,25 +674,6 @@ describe("strict CLI parsing", () => {
674
674
  )
675
675
  })
676
676
 
677
- test("parses VCS status and migration commands", () => {
678
- expect(parseCli(["vcs", "status", "--json"])).toMatchObject({
679
- commandName: "vcs",
680
- args: ["status"],
681
- values: { json: true },
682
- })
683
- expect(
684
- parseCli(["vcs", "migrate", "jj", "--apply", "--json"]),
685
- ).toMatchObject({
686
- commandName: "vcs",
687
- args: ["migrate", "jj"],
688
- values: { apply: true, json: true },
689
- })
690
- expectUsageError(["vcs", "migrate"], "agency vcs migrate")
691
- expect(() =>
692
- parseCli(["vcs", "migrate", "jj", "--dry-run", "--apply"]),
693
- ).toThrow("cannot be combined")
694
- })
695
-
696
677
  test("parses push without accepting an alternate target", () => {
697
678
  expect(parseCli(["push", "--json"])).toMatchObject({
698
679
  commandName: "push",
package/src/cli-parser.ts CHANGED
@@ -908,29 +908,6 @@ const commands = {
908
908
  },
909
909
  },
910
910
  },
911
- vcs: {
912
- usage: "agency vcs <status|migrate>",
913
- options: {
914
- ...outputOptions,
915
- apply: { type: "boolean" },
916
- "dry-run": { type: "boolean" },
917
- },
918
- subcommands: {
919
- status: {
920
- usage: "agency vcs status [--json]",
921
- minArgs: 0,
922
- maxArgs: 0,
923
- options: ["json"],
924
- },
925
- migrate: {
926
- usage: "agency vcs migrate <git|jj> [--dry-run | --apply] [--json]",
927
- minArgs: 1,
928
- maxArgs: 1,
929
- options: ["apply", "dry-run", "json"],
930
- conflicts: [["apply", "dry-run"]],
931
- },
932
- },
933
- },
934
911
  work: {
935
912
  usage:
936
913
  "agency work [<directory-or-task-id> | --epic <epic-id>] [--agent <name>] [--auto] | agency work prepare [target] [--dry-run] [--json]",
package/src/cli.test.ts CHANGED
@@ -1929,9 +1929,9 @@ status: open
1929
1929
  alias: "agency",
1930
1930
  status: "applied",
1931
1931
  })
1932
- expect(
1933
- (await stat(join(restored, "repos/agency/.jj"))).isDirectory(),
1934
- ).toBe(true)
1932
+ expect((await stat(join(restored, "repos/agency/HEAD"))).isFile()).toBe(
1933
+ true,
1934
+ )
1935
1935
 
1936
1936
  const prepared = parseJson(
1937
1937
  await runCli(["work", "prepare", "portable", "--json"], restored),
@@ -219,73 +219,6 @@ Phase prose.
219
219
  )
220
220
  })
221
221
 
222
- test("reports the current jj change without inventing a missing bookmark", async () => {
223
- if (!Bun.which("jj")) return
224
- await cleanupTempDir(root)
225
- root = await createTempDir()
226
- const remote = join(root, "remote.git")
227
- const seed = join(root, "seed")
228
- const repository = join(root, "repos/agency")
229
- const checkout = join(root, "tasks/example/code/agency")
230
- await mkdir(join(root, "repos"), { recursive: true })
231
- await write(root, "agency.json", '{"version":2,"vcs":"jj"}\n')
232
- await run(root, ["git", "init", "--bare", "--initial-branch=main", remote])
233
- await run(root, ["git", "init", "--initial-branch=main", seed])
234
- await run(seed, ["git", "config", "user.email", "test@example.com"])
235
- await run(seed, ["git", "config", "user.name", "Test"])
236
- await Bun.write(join(seed, "README.md"), "example\n")
237
- await run(seed, ["git", "add", "README.md"])
238
- await run(seed, ["git", "commit", "-m", "initial"])
239
- await run(seed, ["git", "remote", "add", "origin", remote])
240
- await run(seed, ["git", "push", "origin", "main"])
241
- await run(root, ["jj", "git", "clone", "--no-colocate", remote, repository])
242
- await write(
243
- root,
244
- "tasks/example/TASK.md",
245
- `---
246
- ticketUrl: null
247
- repo: agency
248
- branch: task/example
249
- base: main
250
- pr: null
251
- status: working
252
- ---
253
-
254
- # Example
255
- `,
256
- )
257
- await mkdir(dirname(checkout), { recursive: true })
258
- await run(repository, [
259
- "jj",
260
- "workspace",
261
- "add",
262
- "--name",
263
- "task-example",
264
- "-r",
265
- "main",
266
- checkout,
267
- ])
268
- await Bun.write(join(checkout, "feature.txt"), "current change\n")
269
- const current = Bun.spawn(
270
- ["jj", "log", "--no-graph", "-r", "@", "-T", "commit_id"],
271
- { cwd: checkout, stdout: "pipe", stderr: "pipe" },
272
- )
273
- const currentCommit = (await new Response(current.stdout).text()).trim()
274
- expect(await current.exited).toBe(0)
275
-
276
- const result = await readContext(root, "tasks/example", false, true)
277
- expect(result.workspace.writable).toMatchObject({
278
- branchCommit: null,
279
- checkoutCommit: currentCommit,
280
- checkoutBranch: null,
281
- detached: true,
282
- dirty: false,
283
- })
284
- expect(result.workspace.warnings).toContain(
285
- `Unable to resolve branch 'task/example' in ${repository}`,
286
- )
287
- })
288
-
289
222
  test("defaults to compact while preserving explicit compact compatibility", async () => {
290
223
  const result = await readContext(
291
224
  root,
@@ -8,7 +8,6 @@ import {
8
8
  runTestEffect,
9
9
  } from "../test-utils"
10
10
  import { init } from "./init"
11
- import { preferredVersionControl } from "../workbase/version-control"
12
11
 
13
12
  describe("init command", () => {
14
13
  let parent: string
@@ -27,7 +26,7 @@ describe("init command", () => {
27
26
 
28
27
  expect(await Bun.file(join(root, "agency.json")).json()).toEqual({
29
28
  version: 2,
30
- vcs: preferredVersionControl(),
29
+ vcs: "git",
31
30
  })
32
31
  for (const directory of ["repos", "epics", "tasks"]) {
33
32
  expect((await stat(join(root, directory))).isDirectory()).toBe(true)
@@ -1,6 +1,6 @@
1
1
  import { afterEach, beforeEach, describe, expect, test } from "bun:test"
2
2
  import { Effect } from "effect"
3
- import { chmod, mkdir, realpath, rm } from "node:fs/promises"
3
+ import { chmod, mkdir, realpath } from "node:fs/promises"
4
4
  import { dirname, join } from "node:path"
5
5
  import { PullRequestService } from "../services/PullRequestService"
6
6
  import {
@@ -31,7 +31,6 @@ describe("pr command", () => {
31
31
  join(bin, "gh"),
32
32
  `#!/bin/sh
33
33
  printf '%s\n' "$PWD" "$@" > "$GH_CAPTURE"
34
- if [ -n "$GIT_DIR" ]; then printf 'GIT_DIR=%s\n' "$GIT_DIR" >> "$GH_CAPTURE"; fi
35
34
  exit "\${GH_EXIT:-0}"
36
35
  `,
37
36
  )
@@ -51,14 +50,13 @@ exit "\${GH_EXIT:-0}"
51
50
  const captured = async () =>
52
51
  (await Bun.file(capturePath).text()).trim().split("\n")
53
52
 
54
- const createExecutionWorkbase = async (vcs: "git" | "jj" = "git") => {
53
+ const createExecutionWorkbase = async () => {
55
54
  const root = join(tempRoot, "workbase")
56
55
  await write(
57
56
  root,
58
57
  "agency.json",
59
58
  `${JSON.stringify({
60
59
  version: 2,
61
- vcs,
62
60
  repositories: {
63
61
  agency: { remote: "https://github.com/example/agency.git" },
64
62
  },
@@ -110,41 +108,6 @@ status: open
110
108
  # Build
111
109
  `,
112
110
  )
113
- if (vcs === "jj") {
114
- const repository = join(root, "repos/agency")
115
- await rm(join(root, "tasks/single/code/agency"), { recursive: true })
116
- await rm(join(root, "tasks/multi/phases/build/code/agency"), {
117
- recursive: true,
118
- })
119
- const initialized = Bun.spawn(
120
- ["jj", "git", "init", "--no-colocate", repository],
121
- { stdout: "pipe", stderr: "pipe" },
122
- )
123
- if ((await initialized.exited) !== 0)
124
- throw new Error(await new Response(initialized.stderr).text())
125
- for (const [name, path] of [
126
- ["single", join(root, "tasks/single/code/agency")],
127
- ["build", join(root, "tasks/multi/phases/build/code/agency")],
128
- ] as const) {
129
- const workspace = Bun.spawn(
130
- [
131
- "jj",
132
- "-R",
133
- repository,
134
- "workspace",
135
- "add",
136
- "--name",
137
- name,
138
- "-r",
139
- "root()",
140
- path,
141
- ],
142
- { stdout: "pipe", stderr: "pipe" },
143
- )
144
- if ((await workspace.exited) !== 0)
145
- throw new Error(await new Response(workspace.stderr).text())
146
- }
147
- }
148
111
  return root
149
112
  }
150
113
 
@@ -261,54 +224,6 @@ status: working
261
224
  ])
262
225
  })
263
226
 
264
- test("injects the execution branch and repository for jj targets", async () => {
265
- const root = await createExecutionWorkbase("jj")
266
- const task = join(root, "tasks/single")
267
- const phase = join(root, "tasks/multi/phases/build")
268
-
269
- expect(await runTestEffect(pr(["view", "--web"], task))).toBe(0)
270
- expect(await captured()).toEqual([
271
- await realpath(join(task, "code/agency")),
272
- "pr",
273
- "view",
274
- "feat/single",
275
- "--repo",
276
- "example/agency",
277
- "--web",
278
- expect.stringMatching(/^GIT_DIR=.*\.jj/),
279
- ])
280
-
281
- expect(
282
- await runTestEffect(pr(["create", "--title", "Example"], phase)),
283
- ).toBe(0)
284
- expect(await captured()).toEqual([
285
- await realpath(join(phase, "code/agency")),
286
- "pr",
287
- "create",
288
- "--head",
289
- "feat/build",
290
- "--repo",
291
- "example/agency",
292
- "--title",
293
- "Example",
294
- expect.stringMatching(/^GIT_DIR=.*\.jj/),
295
- ])
296
- })
297
-
298
- test("preserves explicit jj PR and repository targets", async () => {
299
- const root = await createExecutionWorkbase("jj")
300
- const task = join(root, "tasks/single")
301
- const args = ["view", "123", "--repo", "other/repository"]
302
-
303
- expect(await runTestEffect(pr(args, task))).toBe(0)
304
- expect(await captured()).toEqual([
305
- await realpath(join(task, "code/agency")),
306
- "pr",
307
- ...args,
308
- expect.stringMatching(/^GIT_DIR=.*\.jj/),
309
- ])
310
- })
311
-
312
227
  test("falls back to the invocation directory without execution authority", async () => {
313
228
  const root = await createExecutionWorkbase()
314
229
  const orchestration = join(root, "tasks/multi")