@markjaquith/agency 2.71.20 → 2.71.21
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": "2.71.
|
|
3
|
+
"version": "2.71.21",
|
|
4
4
|
"description": "Manage agentic work across repositories with durable workbases",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agents",
|
|
@@ -21,8 +21,9 @@
|
|
|
21
21
|
"index.ts",
|
|
22
22
|
"cli.ts",
|
|
23
23
|
"cli-main.ts",
|
|
24
|
-
"pi-extensions",
|
|
25
24
|
"src",
|
|
25
|
+
"pi-extensions",
|
|
26
|
+
"scripts/install-pi-extension.ts",
|
|
26
27
|
"schemas",
|
|
27
28
|
"fixtures/protocol",
|
|
28
29
|
"README.md",
|
|
@@ -67,6 +68,7 @@
|
|
|
67
68
|
"benchmark:act": "bun scripts/benchmark-act.ts",
|
|
68
69
|
"benchmark:pr": "bun scripts/benchmark-pr.ts",
|
|
69
70
|
"benchmark:release": "bun scripts/benchmark-release.ts",
|
|
71
|
+
"benchmark:repositories": "bun scripts/benchmark-repositories.ts",
|
|
70
72
|
"benchmark:status": "bun scripts/benchmark-status.ts",
|
|
71
73
|
"benchmark:next": "bun scripts/benchmark-next.ts",
|
|
72
74
|
"benchmark:init": "bun scripts/benchmark-init.ts",
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { cp, mkdir, rm } from "node:fs/promises"
|
|
2
|
+
import { homedir } from "node:os"
|
|
3
|
+
import { dirname, join } from "node:path"
|
|
4
|
+
|
|
5
|
+
export const piExtensionPath = (home = homedir()) =>
|
|
6
|
+
join(home, ".pi", "agent", "extensions", "agency.ts")
|
|
7
|
+
|
|
8
|
+
export const installPiExtension = async (
|
|
9
|
+
source = join(import.meta.dir, "..", "pi-extensions", "agency.ts"),
|
|
10
|
+
destination = piExtensionPath(),
|
|
11
|
+
) => {
|
|
12
|
+
await mkdir(dirname(destination), { recursive: true })
|
|
13
|
+
await cp(source, destination)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const uninstallPiExtension = async (destination = piExtensionPath()) => {
|
|
17
|
+
await rm(destination, { force: true })
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (import.meta.main) {
|
|
21
|
+
const command = process.argv[2] ?? "install"
|
|
22
|
+
if (command === "install") await installPiExtension()
|
|
23
|
+
else if (command === "uninstall") await uninstallPiExtension()
|
|
24
|
+
else throw new Error(`Unknown Pi extension lifecycle command: ${command}`)
|
|
25
|
+
}
|
|
@@ -520,8 +520,9 @@ export class RepositoryService extends Effect.Service<RepositoryService>()(
|
|
|
520
520
|
...Object.keys(config.repositories ?? {}),
|
|
521
521
|
...local.keys(),
|
|
522
522
|
])
|
|
523
|
-
|
|
524
|
-
[...aliases].sort()
|
|
523
|
+
return yield* Effect.forEach(
|
|
524
|
+
[...aliases].sort(),
|
|
525
|
+
(alias) =>
|
|
525
526
|
Effect.gen(function* () {
|
|
526
527
|
const path = join(reposPath, alias)
|
|
527
528
|
const entry = local.get(alias)
|
|
@@ -575,10 +576,8 @@ export class RepositoryService extends Effect.Service<RepositoryService>()(
|
|
|
575
576
|
states,
|
|
576
577
|
} satisfies RepositoryInfo
|
|
577
578
|
}),
|
|
578
|
-
|
|
579
|
-
{ concurrency: 4 },
|
|
579
|
+
{ concurrency: 8 },
|
|
580
580
|
)
|
|
581
|
-
return repositories
|
|
582
581
|
}),
|
|
583
582
|
|
|
584
583
|
show: (alias: string, startPath: string = process.cwd()) =>
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { afterEach, describe, expect, test } from "bun:test"
|
|
2
|
-
import { Effect } from "effect"
|
|
2
|
+
import { Effect, Layer } from "effect"
|
|
3
3
|
import { mkdir, realpath, stat } from "node:fs/promises"
|
|
4
4
|
import { join } from "node:path"
|
|
5
5
|
import { cleanupTempDir, createTempDir, runTestEffect } from "../test-utils"
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
GitVersionControlService,
|
|
8
|
+
VersionControlService,
|
|
9
|
+
} from "./VersionControlService"
|
|
10
|
+
import { FileSystemService } from "./FileSystemService"
|
|
7
11
|
import { preferredVersionControl } from "../workbase/version-control"
|
|
8
12
|
|
|
9
13
|
const run = async (args: string[], cwd?: string) => {
|
|
@@ -24,6 +28,39 @@ describe("VersionControlService", () => {
|
|
|
24
28
|
expect(preferredVersionControl(() => null)).toBe("git")
|
|
25
29
|
})
|
|
26
30
|
|
|
31
|
+
test("inspects a Git repository with one subprocess", async () => {
|
|
32
|
+
const commands: readonly string[][] = []
|
|
33
|
+
const fileSystem = {
|
|
34
|
+
...FileSystemService.Default,
|
|
35
|
+
runCommand: (command: readonly string[]) => {
|
|
36
|
+
;(commands as string[][]).push([...command])
|
|
37
|
+
return Effect.succeed({
|
|
38
|
+
exitCode: 0,
|
|
39
|
+
stdout:
|
|
40
|
+
"local\tcore.bare true\nlocal\tremote.origin.url agency:agency.git\nglobal\turl.https://example.com/.insteadof agency:\n",
|
|
41
|
+
stderr: "",
|
|
42
|
+
})
|
|
43
|
+
},
|
|
44
|
+
} as unknown as Effect.Effect.Success<typeof FileSystemService>
|
|
45
|
+
const backend = await Effect.runPromise(
|
|
46
|
+
GitVersionControlService.pipe(
|
|
47
|
+
Effect.provide(GitVersionControlService.Default),
|
|
48
|
+
),
|
|
49
|
+
)
|
|
50
|
+
const inspection = await Effect.runPromise(
|
|
51
|
+
backend
|
|
52
|
+
.inspectRepository("/repository")
|
|
53
|
+
.pipe(Effect.provide(Layer.succeed(FileSystemService, fileSystem))),
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
expect(commands).toHaveLength(1)
|
|
57
|
+
expect(commands[0]).toContain("--get-regexp")
|
|
58
|
+
expect(inspection).toEqual({
|
|
59
|
+
kind: "bare",
|
|
60
|
+
remote: "https://example.com/agency.git",
|
|
61
|
+
})
|
|
62
|
+
})
|
|
63
|
+
|
|
27
64
|
test("selects the backend persisted by the workbase", async () => {
|
|
28
65
|
for (const kind of ["git", "jj"] as const) {
|
|
29
66
|
const root = await createTempDir()
|
|
@@ -118,6 +118,52 @@ const requireSuccess = (
|
|
|
118
118
|
),
|
|
119
119
|
)
|
|
120
120
|
|
|
121
|
+
interface GitConfigEntry {
|
|
122
|
+
readonly scope: string
|
|
123
|
+
readonly key: string
|
|
124
|
+
readonly value: string
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const parseGitConfig = (output: string): readonly GitConfigEntry[] =>
|
|
128
|
+
output
|
|
129
|
+
.trim()
|
|
130
|
+
.split("\n")
|
|
131
|
+
.filter(Boolean)
|
|
132
|
+
.flatMap((line) => {
|
|
133
|
+
const scopeSeparator = line.indexOf("\t")
|
|
134
|
+
const valueSeparator = line.indexOf(" ", scopeSeparator + 1)
|
|
135
|
+
return scopeSeparator >= 0 && valueSeparator >= 0
|
|
136
|
+
? [
|
|
137
|
+
{
|
|
138
|
+
scope: line.slice(0, scopeSeparator),
|
|
139
|
+
key: line.slice(scopeSeparator + 1, valueSeparator),
|
|
140
|
+
value: line.slice(valueSeparator + 1),
|
|
141
|
+
},
|
|
142
|
+
]
|
|
143
|
+
: []
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
const expandGitUrl = (
|
|
147
|
+
url: string,
|
|
148
|
+
entries: readonly GitConfigEntry[],
|
|
149
|
+
): string => {
|
|
150
|
+
let replacement: { readonly prefix: string; readonly base: string } | null =
|
|
151
|
+
null
|
|
152
|
+
for (const entry of entries) {
|
|
153
|
+
const match = /^url\.(.*)\.insteadof$/i.exec(entry.key)
|
|
154
|
+
if (
|
|
155
|
+
match?.[1] !== undefined &&
|
|
156
|
+
url.startsWith(entry.value) &&
|
|
157
|
+
(replacement === null || entry.value.length > replacement.prefix.length)
|
|
158
|
+
) {
|
|
159
|
+
replacement = { prefix: entry.value, base: match[1] }
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return replacement === null
|
|
163
|
+
? url
|
|
164
|
+
: replacement.base + url.slice(replacement.prefix.length)
|
|
165
|
+
}
|
|
166
|
+
|
|
121
167
|
const parseGitWorktrees = (output: string): readonly RegisteredWorkspace[] => {
|
|
122
168
|
const workspaces: RegisteredWorkspace[] = []
|
|
123
169
|
let current: RegisteredWorkspace | null = null
|
|
@@ -168,22 +214,33 @@ export class GitVersionControlService extends Effect.Service<GitVersionControlSe
|
|
|
168
214
|
inspectRepository: (path) =>
|
|
169
215
|
Effect.gen(function* () {
|
|
170
216
|
const fs = yield* FileSystemService
|
|
171
|
-
const
|
|
172
|
-
[
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
["git", "-C", path, "remote", "get-url", "origin"],
|
|
217
|
+
const inspection = yield* fs.runCommand(
|
|
218
|
+
[
|
|
219
|
+
"git",
|
|
220
|
+
"-C",
|
|
221
|
+
path,
|
|
222
|
+
"config",
|
|
223
|
+
"--show-scope",
|
|
224
|
+
"--get-regexp",
|
|
225
|
+
"^(core\\.bare|remote\\.origin\\.url|url\\..*\\.insteadof)$",
|
|
226
|
+
],
|
|
182
227
|
{ captureOutput: true },
|
|
183
228
|
)
|
|
229
|
+
if (inspection.exitCode !== 0) return null
|
|
230
|
+
const entries = parseGitConfig(inspection.stdout)
|
|
231
|
+
const bare = entries.find(
|
|
232
|
+
(entry) =>
|
|
233
|
+
(entry.scope === "local" || entry.scope === "worktree") &&
|
|
234
|
+
entry.key === "core.bare",
|
|
235
|
+
)
|
|
236
|
+
if (!bare) return null
|
|
237
|
+
const remote = entries.find(
|
|
238
|
+
(entry) => entry.key === "remote.origin.url",
|
|
239
|
+
)?.value
|
|
184
240
|
return {
|
|
185
|
-
kind: bare.
|
|
186
|
-
remote:
|
|
241
|
+
kind: bare.value === "true" ? "bare" : "repository",
|
|
242
|
+
remote:
|
|
243
|
+
remote === undefined ? null : expandGitUrl(remote, entries),
|
|
187
244
|
} as const
|
|
188
245
|
}),
|
|
189
246
|
gitEnvironment: () => Effect.succeed({}),
|