@markjaquith/agency 2.71.23 → 2.71.24
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 +5 -0
- package/package.json +1 -1
- package/src/services/PullRequestService.test.ts +2 -2
- package/src/services/SyncService.test.ts +49 -1
- package/src/services/SyncService.ts +8 -10
- package/src/services/WorkbaseService.ts +20 -7
- package/src/services/WorktreeService.test.ts +82 -1
- package/src/services/WorktreeService.ts +46 -0
package/README.md
CHANGED
|
@@ -269,6 +269,11 @@ Custom commands own writable branch creation. Agency checks for conflicting
|
|
|
269
269
|
worktrees first, invokes the command only when the branch is not checked out,
|
|
270
270
|
and verifies that `{worktree}` exists afterward.
|
|
271
271
|
|
|
272
|
+
Agency also reconciles known tool-owned artifacts such as Worktrunk's
|
|
273
|
+
`.worktree.lock` into each managed worktree's local Git exclude file. This keeps
|
|
274
|
+
new and existing managed worktrees clean without changing the repository's
|
|
275
|
+
tracked ignore configuration or overwriting user-maintained local excludes.
|
|
276
|
+
|
|
272
277
|
The configured command applies only to the writable checkout of a Git workbase.
|
|
273
278
|
Supplemental read-only repositories remain detached Git worktrees at their
|
|
274
279
|
declared refs so they do not acquire writable branches. Jj workbases always use
|
package/package.json
CHANGED
|
@@ -605,14 +605,14 @@ process.stdout.write(${JSON.stringify(JSON.stringify(providerRecord))})
|
|
|
605
605
|
expect("pr" in task.data && task.data.pr).toBeNull()
|
|
606
606
|
})
|
|
607
607
|
|
|
608
|
-
test("reports
|
|
608
|
+
test("reports invalid reused Git checkout before push or gh", async () => {
|
|
609
609
|
await createTask()
|
|
610
610
|
const workspace = await materialize()
|
|
611
611
|
await rm(join(workspace.writablePath!, ".git"))
|
|
612
612
|
await writeFakeGh({ stdout: "https://github.com/example/agency/pull/48" })
|
|
613
613
|
|
|
614
614
|
await expect(createPullRequest()).rejects.toThrow(
|
|
615
|
-
"Failed to
|
|
615
|
+
"Failed to locate local excludes",
|
|
616
616
|
)
|
|
617
617
|
|
|
618
618
|
await expectRemoteBranch("task/example", false)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, test } from "bun:test"
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, spyOn, test } from "bun:test"
|
|
2
2
|
import { Effect } from "effect"
|
|
3
3
|
import { chmod, mkdir, rm } from "node:fs/promises"
|
|
4
4
|
import { join } from "node:path"
|
|
@@ -1115,6 +1115,54 @@ exec ${JSON.stringify(realGit)} "$@"
|
|
|
1115
1115
|
)
|
|
1116
1116
|
})
|
|
1117
1117
|
|
|
1118
|
+
test("reuses documents parsed during validation", async () => {
|
|
1119
|
+
await runTestEffect(
|
|
1120
|
+
TaskService.pipe(
|
|
1121
|
+
Effect.flatMap((service) =>
|
|
1122
|
+
service.create(
|
|
1123
|
+
{
|
|
1124
|
+
id: "single-read",
|
|
1125
|
+
ticketUrl: null,
|
|
1126
|
+
repo: "agency",
|
|
1127
|
+
branch: "feat/single-read",
|
|
1128
|
+
base: "main",
|
|
1129
|
+
},
|
|
1130
|
+
root,
|
|
1131
|
+
),
|
|
1132
|
+
),
|
|
1133
|
+
),
|
|
1134
|
+
)
|
|
1135
|
+
await runTestEffect(
|
|
1136
|
+
TaskService.pipe(
|
|
1137
|
+
Effect.flatMap((service) =>
|
|
1138
|
+
service.setStatus("single-read", "done", root, {
|
|
1139
|
+
summary: "Completed without a pull request",
|
|
1140
|
+
}),
|
|
1141
|
+
),
|
|
1142
|
+
),
|
|
1143
|
+
)
|
|
1144
|
+
const taskPath = join(root, "tasks/single-read/TASK.md")
|
|
1145
|
+
const taskContent = await Bun.file(taskPath).text()
|
|
1146
|
+
const originalFile = Bun.file
|
|
1147
|
+
let reads = 0
|
|
1148
|
+
const fileSpy = spyOn(Bun, "file").mockImplementation(((path: string) => {
|
|
1149
|
+
if (path === taskPath) reads += 1
|
|
1150
|
+
return path === taskPath ? new Blob([taskContent]) : originalFile(path)
|
|
1151
|
+
}) as typeof Bun.file)
|
|
1152
|
+
|
|
1153
|
+
try {
|
|
1154
|
+
await runTestEffect(
|
|
1155
|
+
SyncService.pipe(
|
|
1156
|
+
Effect.flatMap((service) => service.reconcile({ cwd: root })),
|
|
1157
|
+
),
|
|
1158
|
+
)
|
|
1159
|
+
} finally {
|
|
1160
|
+
fileSpy.mockRestore()
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
expect(reads).toBe(1)
|
|
1164
|
+
})
|
|
1165
|
+
|
|
1118
1166
|
test("queries pull request providers concurrently", async () => {
|
|
1119
1167
|
for (const id of ["first", "second", "third"]) {
|
|
1120
1168
|
await runTestEffect(
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Data, Effect, Either } from "effect"
|
|
2
2
|
import { dirname, join, resolve } from "node:path"
|
|
3
|
-
import { documentRevision } from "../workbase/document-revision"
|
|
4
3
|
import type {
|
|
5
4
|
ClaimRecord,
|
|
6
5
|
PhaseFrontmatter,
|
|
@@ -17,8 +16,6 @@ import {
|
|
|
17
16
|
} from "../workbase/delivery-command"
|
|
18
17
|
import { ClaimService } from "./ClaimService"
|
|
19
18
|
import { FileSystemService } from "./FileSystemService"
|
|
20
|
-
import { PhaseService } from "./PhaseService"
|
|
21
|
-
import { TaskService } from "./TaskService"
|
|
22
19
|
import { WorkbaseService } from "./WorkbaseService"
|
|
23
20
|
import { WorktreeService } from "./WorktreeService"
|
|
24
21
|
import {
|
|
@@ -217,15 +214,15 @@ export class SyncService extends Effect.Service<SyncService>()("SyncService", {
|
|
|
217
214
|
Effect.gen(function* () {
|
|
218
215
|
const fs = yield* FileSystemService
|
|
219
216
|
const workbase = yield* WorkbaseService
|
|
220
|
-
const tasks = yield* TaskService
|
|
221
|
-
const phases = yield* PhaseService
|
|
222
217
|
const worktrees = yield* WorktreeService
|
|
223
218
|
const claims = yield* ClaimService
|
|
224
219
|
const repositories = yield* RepositoryService
|
|
225
220
|
const versionControl = yield* VersionControlService
|
|
226
221
|
const { root, config } = yield* workbase.loadConfig(options.cwd)
|
|
227
222
|
const backend = yield* versionControl.forWorkbase(root)
|
|
228
|
-
const validation = yield* workbase.validate(root
|
|
223
|
+
const validation = yield* workbase.validate(root, {
|
|
224
|
+
includeDocuments: true,
|
|
225
|
+
})
|
|
229
226
|
if (!validation.valid) {
|
|
230
227
|
return yield* new SyncError({
|
|
231
228
|
message: validation.issues
|
|
@@ -238,7 +235,8 @@ export class SyncService extends Effect.Service<SyncService>()("SyncService", {
|
|
|
238
235
|
message: "A phase sync scope requires a task ID",
|
|
239
236
|
})
|
|
240
237
|
}
|
|
241
|
-
const
|
|
238
|
+
const documents = validation.documents!
|
|
239
|
+
const allTaskRecords = documents.tasks
|
|
242
240
|
const taskRecords = options.taskId
|
|
243
241
|
? allTaskRecords.filter((task) => task.id === options.taskId)
|
|
244
242
|
: allTaskRecords
|
|
@@ -250,14 +248,14 @@ export class SyncService extends Effect.Service<SyncService>()("SyncService", {
|
|
|
250
248
|
const records: ExecutionRecord[] = []
|
|
251
249
|
for (const task of taskRecords) {
|
|
252
250
|
if ("phases" in task.data) {
|
|
253
|
-
for (const phase of
|
|
251
|
+
for (const phase of documents.phasesByTask.get(task.id) ?? []) {
|
|
254
252
|
if (options.phaseId && phase.id !== options.phaseId) continue
|
|
255
253
|
records.push({
|
|
256
254
|
key: `phase:${task.id}/${phase.id}`,
|
|
257
255
|
taskId: task.id,
|
|
258
256
|
phaseId: phase.id,
|
|
259
257
|
path: phase.path,
|
|
260
|
-
revision:
|
|
258
|
+
revision: phase.revision,
|
|
261
259
|
data: phase.data,
|
|
262
260
|
})
|
|
263
261
|
}
|
|
@@ -266,7 +264,7 @@ export class SyncService extends Effect.Service<SyncService>()("SyncService", {
|
|
|
266
264
|
key: `task:${task.id}`,
|
|
267
265
|
taskId: task.id,
|
|
268
266
|
path: task.path,
|
|
269
|
-
revision:
|
|
267
|
+
revision: task.revision,
|
|
270
268
|
data: task.data,
|
|
271
269
|
})
|
|
272
270
|
}
|
|
@@ -27,6 +27,7 @@ import { validateAgents } from "../workbase/agent-command"
|
|
|
27
27
|
import { findDependencyCycles } from "../workbase/dependency-graph"
|
|
28
28
|
import { validateDelivery } from "../workbase/delivery-command"
|
|
29
29
|
import { preferredVersionControl } from "../workbase/version-control"
|
|
30
|
+
import { documentRevision } from "../workbase/document-revision"
|
|
30
31
|
|
|
31
32
|
class WorkbaseNotFoundError extends Data.TaggedError("WorkbaseNotFoundError")<{
|
|
32
33
|
readonly message: string
|
|
@@ -62,6 +63,8 @@ export interface ValidationReport {
|
|
|
62
63
|
interface DocumentRecord<T> {
|
|
63
64
|
readonly id: string
|
|
64
65
|
readonly path: string
|
|
66
|
+
readonly content: string
|
|
67
|
+
readonly revision: string
|
|
65
68
|
readonly data: T
|
|
66
69
|
}
|
|
67
70
|
|
|
@@ -729,8 +732,9 @@ export class WorkbaseService extends Effect.Service<WorkbaseService>()(
|
|
|
729
732
|
issue(path, "Required document is missing")
|
|
730
733
|
return null
|
|
731
734
|
}
|
|
735
|
+
const documentContent = content.value
|
|
732
736
|
const parsed = yield* Effect.either(
|
|
733
|
-
parseFrontmatter(
|
|
737
|
+
parseFrontmatter(documentContent, path),
|
|
734
738
|
)
|
|
735
739
|
if (Either.isLeft(parsed)) {
|
|
736
740
|
issue(path, parsed.left.message)
|
|
@@ -742,7 +746,11 @@ export class WorkbaseService extends Effect.Service<WorkbaseService>()(
|
|
|
742
746
|
issue(path, decoded.error)
|
|
743
747
|
return null
|
|
744
748
|
}
|
|
745
|
-
return
|
|
749
|
+
return {
|
|
750
|
+
content: documentContent,
|
|
751
|
+
revision: documentRevision(documentContent),
|
|
752
|
+
data: decoded.value,
|
|
753
|
+
}
|
|
746
754
|
})
|
|
747
755
|
|
|
748
756
|
const epicIds = yield* readDirectories(join(root, "epics"))
|
|
@@ -750,8 +758,8 @@ export class WorkbaseService extends Effect.Service<WorkbaseService>()(
|
|
|
750
758
|
epicIds.map((id) =>
|
|
751
759
|
Effect.gen(function* () {
|
|
752
760
|
const path = join(root, "epics", id, "EPIC.md")
|
|
753
|
-
const
|
|
754
|
-
return
|
|
761
|
+
const document = yield* readDocument(path, EpicFrontmatter)
|
|
762
|
+
return document ? { id, path, ...document } : null
|
|
755
763
|
}),
|
|
756
764
|
),
|
|
757
765
|
{ concurrency: validationConcurrency },
|
|
@@ -766,7 +774,7 @@ export class WorkbaseService extends Effect.Service<WorkbaseService>()(
|
|
|
766
774
|
Effect.gen(function* () {
|
|
767
775
|
const taskPath = join(root, "tasks", id)
|
|
768
776
|
const path = join(taskPath, "TASK.md")
|
|
769
|
-
const
|
|
777
|
+
const document = yield* readDocument(path, TaskFrontmatter)
|
|
770
778
|
const phaseIds = yield* readDirectories(
|
|
771
779
|
join(taskPath, "phases"),
|
|
772
780
|
)
|
|
@@ -787,7 +795,12 @@ export class WorkbaseService extends Effect.Service<WorkbaseService>()(
|
|
|
787
795
|
PhaseFrontmatter,
|
|
788
796
|
)
|
|
789
797
|
return phase
|
|
790
|
-
? {
|
|
798
|
+
? {
|
|
799
|
+
id: phaseId,
|
|
800
|
+
taskId: id,
|
|
801
|
+
path: phasePath,
|
|
802
|
+
...phase,
|
|
803
|
+
}
|
|
791
804
|
: null
|
|
792
805
|
}),
|
|
793
806
|
),
|
|
@@ -795,7 +808,7 @@ export class WorkbaseService extends Effect.Service<WorkbaseService>()(
|
|
|
795
808
|
)
|
|
796
809
|
return {
|
|
797
810
|
id,
|
|
798
|
-
task:
|
|
811
|
+
task: document ? { id, path, ...document } : null,
|
|
799
812
|
phases: taskPhases,
|
|
800
813
|
}
|
|
801
814
|
}),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { afterEach, beforeEach, describe, expect, test } from "bun:test"
|
|
2
2
|
import { Effect } from "effect"
|
|
3
3
|
import { chmod, mkdir, realpath, rename, rm, stat } from "node:fs/promises"
|
|
4
|
-
import { join } from "node:path"
|
|
4
|
+
import { join, resolve } from "node:path"
|
|
5
5
|
import {
|
|
6
6
|
captureErrors,
|
|
7
7
|
cleanupTempDir,
|
|
@@ -23,6 +23,21 @@ const git = async (args: string[], cwd?: string) => {
|
|
|
23
23
|
throw new Error(await new Response(process.stderr).text())
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
const gitOutput = async (args: string[], cwd?: string) => {
|
|
27
|
+
const process = Bun.spawn(["git", ...args], {
|
|
28
|
+
cwd,
|
|
29
|
+
stdout: "pipe",
|
|
30
|
+
stderr: "pipe",
|
|
31
|
+
})
|
|
32
|
+
const [exitCode, stdout, stderr] = await Promise.all([
|
|
33
|
+
process.exited,
|
|
34
|
+
new Response(process.stdout).text(),
|
|
35
|
+
new Response(process.stderr).text(),
|
|
36
|
+
])
|
|
37
|
+
if (exitCode !== 0) throw new Error(stderr)
|
|
38
|
+
return stdout.trim()
|
|
39
|
+
}
|
|
40
|
+
|
|
26
41
|
const jj = async (args: string[], cwd?: string) => {
|
|
27
42
|
const process = Bun.spawn(["jj", ...args], {
|
|
28
43
|
cwd,
|
|
@@ -117,6 +132,63 @@ describe("WorktreeService", () => {
|
|
|
117
132
|
{ stdout: "pipe" },
|
|
118
133
|
)
|
|
119
134
|
expect(new TextDecoder().decode(branch.stdout).trim()).toBe("task/example")
|
|
135
|
+
for (const checkout of [
|
|
136
|
+
workspace.writablePath!,
|
|
137
|
+
join(workspace.codePath, "effect"),
|
|
138
|
+
]) {
|
|
139
|
+
await Bun.write(join(checkout, ".worktree.lock"), "")
|
|
140
|
+
expect(await gitOutput(["status", "--porcelain"], checkout)).toBe("")
|
|
141
|
+
}
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
test("reconciles local worktree excludes without overwriting user entries", async () => {
|
|
145
|
+
await runTestEffect(
|
|
146
|
+
TaskService.pipe(
|
|
147
|
+
Effect.flatMap((service) =>
|
|
148
|
+
service.create(
|
|
149
|
+
{
|
|
150
|
+
id: "existing-ignore",
|
|
151
|
+
ticketUrl: null,
|
|
152
|
+
repo: "agency",
|
|
153
|
+
branch: "task/existing-ignore",
|
|
154
|
+
base: "main",
|
|
155
|
+
},
|
|
156
|
+
root,
|
|
157
|
+
),
|
|
158
|
+
),
|
|
159
|
+
),
|
|
160
|
+
)
|
|
161
|
+
const first = await runTestEffect(
|
|
162
|
+
WorktreeService.pipe(
|
|
163
|
+
Effect.flatMap((service) =>
|
|
164
|
+
service.materialize("existing-ignore", undefined, root),
|
|
165
|
+
),
|
|
166
|
+
),
|
|
167
|
+
)
|
|
168
|
+
const checkout = first.writablePath!
|
|
169
|
+
const excludePath = resolve(
|
|
170
|
+
checkout,
|
|
171
|
+
await gitOutput(["rev-parse", "--git-path", "info/exclude"], checkout),
|
|
172
|
+
)
|
|
173
|
+
await Bun.write(excludePath, "user-entry")
|
|
174
|
+
|
|
175
|
+
await runTestEffect(
|
|
176
|
+
WorktreeService.pipe(
|
|
177
|
+
Effect.flatMap((service) =>
|
|
178
|
+
service.materialize("existing-ignore", undefined, root),
|
|
179
|
+
),
|
|
180
|
+
),
|
|
181
|
+
)
|
|
182
|
+
await runTestEffect(
|
|
183
|
+
WorktreeService.pipe(
|
|
184
|
+
Effect.flatMap((service) =>
|
|
185
|
+
service.materialize("existing-ignore", undefined, root),
|
|
186
|
+
),
|
|
187
|
+
),
|
|
188
|
+
)
|
|
189
|
+
const excludes = await Bun.file(excludePath).text()
|
|
190
|
+
expect(excludes).toBe("user-entry\n/.worktree.lock\n")
|
|
191
|
+
expect(excludes.match(/^\/\.worktree\.lock$/gm)).toHaveLength(1)
|
|
120
192
|
})
|
|
121
193
|
|
|
122
194
|
test("runs repository hooks for new writable and reference checkouts", async () => {
|
|
@@ -2720,5 +2792,14 @@ pr: null
|
|
|
2720
2792
|
expect(
|
|
2721
2793
|
await Bun.file(join(workspace.writablePath!, "README.md")).text(),
|
|
2722
2794
|
).toBe("example\n")
|
|
2795
|
+
expect(
|
|
2796
|
+
await gitOutput(["status", "--porcelain"], workspace.writablePath!),
|
|
2797
|
+
).toBe("")
|
|
2798
|
+
expect(
|
|
2799
|
+
await gitOutput(
|
|
2800
|
+
["check-ignore", "--no-index", ".worktree.lock"],
|
|
2801
|
+
workspace.writablePath!,
|
|
2802
|
+
),
|
|
2803
|
+
).toBe(".worktree.lock")
|
|
2723
2804
|
})
|
|
2724
2805
|
})
|
|
@@ -80,6 +80,8 @@ interface GitWorktree {
|
|
|
80
80
|
readonly branch?: string
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
const managedWorktreeIgnorePatterns = ["/.worktree.lock"] as const
|
|
84
|
+
|
|
83
85
|
interface WorktreeOwner {
|
|
84
86
|
readonly kind: "task" | "phase"
|
|
85
87
|
readonly taskId: string
|
|
@@ -2325,6 +2327,42 @@ export class WorktreeService extends Effect.Service<WorktreeService>()(
|
|
|
2325
2327
|
const registeredAtPath = worktrees.find(
|
|
2326
2328
|
(worktree) => worktree.path === canonicalCheckoutPath,
|
|
2327
2329
|
)
|
|
2330
|
+
const reconcileManagedWorktreeIgnores = (path: string) =>
|
|
2331
|
+
Effect.gen(function* () {
|
|
2332
|
+
const resolved = yield* fs.runCommand(
|
|
2333
|
+
[
|
|
2334
|
+
"git",
|
|
2335
|
+
"-C",
|
|
2336
|
+
path,
|
|
2337
|
+
"rev-parse",
|
|
2338
|
+
"--git-path",
|
|
2339
|
+
"info/exclude",
|
|
2340
|
+
],
|
|
2341
|
+
{ captureOutput: true },
|
|
2342
|
+
)
|
|
2343
|
+
if (resolved.exitCode !== 0) {
|
|
2344
|
+
return yield* new WorktreeError({
|
|
2345
|
+
message: `Failed to locate local excludes for ${path}: ${resolved.stderr}`,
|
|
2346
|
+
})
|
|
2347
|
+
}
|
|
2348
|
+
const excludePath = resolve(path, resolved.stdout.trim())
|
|
2349
|
+
const existing = (yield* fs.exists(excludePath))
|
|
2350
|
+
? yield* fs.readFile(excludePath)
|
|
2351
|
+
: ""
|
|
2352
|
+
const existingLines = new Set(existing.split(/\r?\n/))
|
|
2353
|
+
const missing = managedWorktreeIgnorePatterns.filter(
|
|
2354
|
+
(pattern) => !existingLines.has(pattern),
|
|
2355
|
+
)
|
|
2356
|
+
if (missing.length === 0) return
|
|
2357
|
+
const prefix =
|
|
2358
|
+
existing.length > 0 && !existing.endsWith("\n")
|
|
2359
|
+
? "\n"
|
|
2360
|
+
: ""
|
|
2361
|
+
yield* fs.writeFile(
|
|
2362
|
+
excludePath,
|
|
2363
|
+
`${existing}${prefix}${missing.join("\n")}\n`,
|
|
2364
|
+
)
|
|
2365
|
+
})
|
|
2328
2366
|
|
|
2329
2367
|
if ("branch" in checkout) {
|
|
2330
2368
|
const branchRef = `refs/heads/${checkout.branch}`
|
|
@@ -2341,6 +2379,9 @@ export class WorktreeService extends Effect.Service<WorktreeService>()(
|
|
|
2341
2379
|
}
|
|
2342
2380
|
if (yield* fs.isDirectory(checkoutPath)) {
|
|
2343
2381
|
if (registeredAtPath?.branch === branchRef) {
|
|
2382
|
+
if (!options.dryRun) {
|
|
2383
|
+
yield* reconcileManagedWorktreeIgnores(checkoutPath)
|
|
2384
|
+
}
|
|
2344
2385
|
checkoutReports.push({
|
|
2345
2386
|
repo: alias,
|
|
2346
2387
|
kind: "writable",
|
|
@@ -2545,6 +2586,7 @@ export class WorktreeService extends Effect.Service<WorktreeService>()(
|
|
|
2545
2586
|
message: `Created worktree for '${alias}' failed validation for branch '${checkout.branch}'`,
|
|
2546
2587
|
})
|
|
2547
2588
|
}
|
|
2589
|
+
yield* reconcileManagedWorktreeIgnores(checkoutPath)
|
|
2548
2590
|
yield* runPostCheckoutHook({
|
|
2549
2591
|
command: config.repositories?.[alias]?.postCheckoutCommand,
|
|
2550
2592
|
variables: {
|
|
@@ -2620,6 +2662,9 @@ export class WorktreeService extends Effect.Service<WorktreeService>()(
|
|
|
2620
2662
|
currentHead.exitCode === 0 &&
|
|
2621
2663
|
currentHead.stdout.trim() === commit
|
|
2622
2664
|
) {
|
|
2665
|
+
if (!options.dryRun) {
|
|
2666
|
+
yield* reconcileManagedWorktreeIgnores(checkoutPath)
|
|
2667
|
+
}
|
|
2623
2668
|
checkoutReports.push({
|
|
2624
2669
|
repo: alias,
|
|
2625
2670
|
kind: "reference",
|
|
@@ -2709,6 +2754,7 @@ export class WorktreeService extends Effect.Service<WorktreeService>()(
|
|
|
2709
2754
|
message: `Created reference checkout for '${alias}' failed validation for '${checkout.ref}'`,
|
|
2710
2755
|
})
|
|
2711
2756
|
}
|
|
2757
|
+
yield* reconcileManagedWorktreeIgnores(checkoutPath)
|
|
2712
2758
|
yield* runPostCheckoutHook({
|
|
2713
2759
|
command: config.repositories?.[alias]?.postCheckoutCommand,
|
|
2714
2760
|
variables: {
|