@markjaquith/agency 1.8.3 → 1.8.5
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 +1 -1
- package/src/commands/emit.test.ts +32 -0
- package/src/commands/push.test.ts +3 -1
- package/src/commands/push.ts +62 -19
- package/src/utils/glob.test.ts +7 -0
- package/src/utils/glob.ts +1 -1
- package/src/utils/pr-branch.ts +0 -21
- package/src/utils/spinner.ts +1 -0
package/package.json
CHANGED
|
@@ -367,5 +367,37 @@ describe("emit command", () => {
|
|
|
367
367
|
// This is the key assertion - the symlink target should be filtered too
|
|
368
368
|
expect(lastCall!.args).toContain("AGENTS.md")
|
|
369
369
|
})
|
|
370
|
+
|
|
371
|
+
test("expands injected dot-directory globs into filter paths", async () => {
|
|
372
|
+
await checkoutBranch(tempDir, "main")
|
|
373
|
+
await createBranch(tempDir, "agency--dotdir-test")
|
|
374
|
+
|
|
375
|
+
await Bun.spawn(["mkdir", "-p", join(tempDir, ".agents", "foo")], {
|
|
376
|
+
stdout: "pipe",
|
|
377
|
+
stderr: "pipe",
|
|
378
|
+
}).exited
|
|
379
|
+
|
|
380
|
+
await Bun.write(
|
|
381
|
+
join(tempDir, "agency.json"),
|
|
382
|
+
JSON.stringify({
|
|
383
|
+
version: 1,
|
|
384
|
+
injectedFiles: [".agents/**"],
|
|
385
|
+
template: "test",
|
|
386
|
+
createdAt: new Date().toISOString(),
|
|
387
|
+
}),
|
|
388
|
+
)
|
|
389
|
+
await Bun.write(join(tempDir, ".agents", "foo", "bar.whatever"), "test\n")
|
|
390
|
+
await addAndCommit(
|
|
391
|
+
tempDir,
|
|
392
|
+
["agency.json", ".agents/foo/bar.whatever"],
|
|
393
|
+
"Add template dot-directory file",
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
await runTestEffectWithMockFilterRepo(emit({ silent: true }))
|
|
397
|
+
|
|
398
|
+
const lastCall = getLastCapturedFilterRepoCall()
|
|
399
|
+
expect(lastCall).toBeDefined()
|
|
400
|
+
expect(lastCall!.args).toContain(".agents/foo/bar.whatever")
|
|
401
|
+
})
|
|
370
402
|
})
|
|
371
403
|
})
|
|
@@ -306,7 +306,9 @@ describe("push command", () => {
|
|
|
306
306
|
runTestEffect(
|
|
307
307
|
push({ baseBranch: "main", silent: true, skipFilter: true }),
|
|
308
308
|
),
|
|
309
|
-
).rejects.toThrow(
|
|
309
|
+
).rejects.toThrow(
|
|
310
|
+
/non-fast-forward update.*agency push --force|agency push --force.*non-fast-forward update/s,
|
|
311
|
+
)
|
|
310
312
|
|
|
311
313
|
// Should still be on agency--feature branch (not left in intermediate state)
|
|
312
314
|
expect(await getCurrentBranch(tempDir)).toBe("agency--feature")
|
package/src/commands/push.ts
CHANGED
|
@@ -1,13 +1,8 @@
|
|
|
1
1
|
import { Effect, Either } from "effect"
|
|
2
2
|
import type { BaseCommandOptions } from "../utils/command"
|
|
3
|
-
import { GitService } from "../services/GitService"
|
|
3
|
+
import { GitCommandError, GitService } from "../services/GitService"
|
|
4
4
|
import { ConfigService } from "../services/ConfigService"
|
|
5
|
-
import {
|
|
6
|
-
extractSourceBranch,
|
|
7
|
-
makePrBranchName,
|
|
8
|
-
resolveBranchPairWithAgencyJson,
|
|
9
|
-
} from "../utils/pr-branch"
|
|
10
|
-
import { FileSystemService } from "../services/FileSystemService"
|
|
5
|
+
import { resolveBranchPairWithAgencyJson } from "../utils/pr-branch"
|
|
11
6
|
import { emit } from "./emit"
|
|
12
7
|
import highlight, { done } from "../utils/colors"
|
|
13
8
|
import {
|
|
@@ -28,6 +23,51 @@ interface PushOptions extends BaseCommandOptions {
|
|
|
28
23
|
skipFilter?: boolean
|
|
29
24
|
}
|
|
30
25
|
|
|
26
|
+
const getPushFailureStderr = (error: unknown): string => {
|
|
27
|
+
if (error instanceof GitCommandError) {
|
|
28
|
+
return error.stderr.trim()
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (
|
|
32
|
+
typeof error === "object" &&
|
|
33
|
+
error !== null &&
|
|
34
|
+
"cause" in error &&
|
|
35
|
+
(error as { cause?: unknown }).cause instanceof GitCommandError
|
|
36
|
+
) {
|
|
37
|
+
return (error as { cause: GitCommandError }).cause.stderr.trim()
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (error instanceof Error) {
|
|
41
|
+
return error.message.trim()
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return String(error).trim()
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const pushFailureNeedsForce = (stderr: string): boolean => {
|
|
48
|
+
const normalized = stderr.toLowerCase()
|
|
49
|
+
|
|
50
|
+
return [
|
|
51
|
+
"non-fast-forward",
|
|
52
|
+
"updates were rejected because the tip of your current branch is behind",
|
|
53
|
+
"fetch first",
|
|
54
|
+
"failed to push some refs",
|
|
55
|
+
].some((pattern) => normalized.includes(pattern))
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const formatPushFailure = (error: unknown): Error => {
|
|
59
|
+
const stderr = getPushFailureStderr(error)
|
|
60
|
+
|
|
61
|
+
if (pushFailureNeedsForce(stderr)) {
|
|
62
|
+
return new Error(
|
|
63
|
+
"Failed to push branch to remote: the remote rejected a non-fast-forward update, which usually means the branch was rebased or rewritten.\n" +
|
|
64
|
+
"Run `agency push --force` to replace the remote branch.",
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return new Error(`Failed to push branch to remote: ${stderr}`)
|
|
69
|
+
}
|
|
70
|
+
|
|
31
71
|
export const push = (options: PushOptions = {}) =>
|
|
32
72
|
Effect.gen(function* () {
|
|
33
73
|
const gitRoot = yield* ensureGitRepo()
|
|
@@ -39,7 +79,6 @@ export const push = (options: PushOptions = {}) =>
|
|
|
39
79
|
|
|
40
80
|
const pushCore = (gitRoot: string, options: PushOptions) =>
|
|
41
81
|
Effect.gen(function* () {
|
|
42
|
-
const { verbose = false } = options
|
|
43
82
|
const { log, verboseLog } = createLoggers(options)
|
|
44
83
|
|
|
45
84
|
const git = yield* GitService
|
|
@@ -52,7 +91,6 @@ const pushCore = (gitRoot: string, options: PushOptions) =>
|
|
|
52
91
|
let sourceBranch = yield* git.getCurrentBranch(gitRoot)
|
|
53
92
|
|
|
54
93
|
// Check if we're already on an emit branch using proper branch resolution
|
|
55
|
-
const fs = yield* FileSystemService
|
|
56
94
|
const branchInfo = yield* resolveBranchPairWithAgencyJson(
|
|
57
95
|
gitRoot,
|
|
58
96
|
sourceBranch,
|
|
@@ -153,7 +191,7 @@ const pushCore = (gitRoot: string, options: PushOptions) =>
|
|
|
153
191
|
),
|
|
154
192
|
)
|
|
155
193
|
if (Either.isLeft(pushEither)) {
|
|
156
|
-
const error = pushEither.left
|
|
194
|
+
const error = formatPushFailure(pushEither.left)
|
|
157
195
|
// If push failed, switch back to source branch
|
|
158
196
|
yield* git.checkoutBranch(gitRoot, sourceBranch)
|
|
159
197
|
return yield* Effect.fail(error)
|
|
@@ -226,11 +264,7 @@ const pushBranchToRemoteEffect = (
|
|
|
226
264
|
const stderr = pushResult.stderr
|
|
227
265
|
|
|
228
266
|
// Check if this is a force-push-needed error
|
|
229
|
-
const needsForce =
|
|
230
|
-
stderr.includes("rejected") ||
|
|
231
|
-
stderr.includes("non-fast-forward") ||
|
|
232
|
-
stderr.includes("fetch first") ||
|
|
233
|
-
stderr.includes("Updates were rejected")
|
|
267
|
+
const needsForce = pushFailureNeedsForce(stderr)
|
|
234
268
|
|
|
235
269
|
if (needsForce && force) {
|
|
236
270
|
// User provided --force flag, retry with force
|
|
@@ -258,15 +292,24 @@ const pushBranchToRemoteEffect = (
|
|
|
258
292
|
} else if (needsForce && !force) {
|
|
259
293
|
// User didn't provide --force but it's needed
|
|
260
294
|
return yield* Effect.fail(
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
`
|
|
295
|
+
formatPushFailure(
|
|
296
|
+
new GitCommandError({
|
|
297
|
+
command: `git push -u ${remote} ${branchName}`,
|
|
298
|
+
exitCode: pushResult.exitCode,
|
|
299
|
+
stderr,
|
|
300
|
+
}),
|
|
264
301
|
),
|
|
265
302
|
)
|
|
266
303
|
} else {
|
|
267
304
|
// Some other error
|
|
268
305
|
return yield* Effect.fail(
|
|
269
|
-
|
|
306
|
+
formatPushFailure(
|
|
307
|
+
new GitCommandError({
|
|
308
|
+
command: `git push -u ${remote} ${branchName}`,
|
|
309
|
+
exitCode: pushResult.exitCode,
|
|
310
|
+
stderr,
|
|
311
|
+
}),
|
|
312
|
+
),
|
|
270
313
|
)
|
|
271
314
|
}
|
|
272
315
|
}
|
package/src/utils/glob.test.ts
CHANGED
|
@@ -97,10 +97,12 @@ describe("expandGlobs", () => {
|
|
|
97
97
|
await mkdir(join(tempDir, "plans"))
|
|
98
98
|
await mkdir(join(tempDir, "plans", "sub"))
|
|
99
99
|
await mkdir(join(tempDir, "other"))
|
|
100
|
+
await mkdir(join(tempDir, ".agents", "foo"), { recursive: true })
|
|
100
101
|
|
|
101
102
|
await writeFile(join(tempDir, "plans", "foo.md"), "foo")
|
|
102
103
|
await writeFile(join(tempDir, "plans", "sub", "bar.md"), "bar")
|
|
103
104
|
await writeFile(join(tempDir, "other", "baz.md"), "baz")
|
|
105
|
+
await writeFile(join(tempDir, ".agents", "foo", "bar.whatever"), "dot")
|
|
104
106
|
await writeFile(join(tempDir, "root.txt"), "root")
|
|
105
107
|
})
|
|
106
108
|
|
|
@@ -151,4 +153,9 @@ describe("expandGlobs", () => {
|
|
|
151
153
|
["plans/foo.md", "plans/sub/bar.md", "other/baz.md"].sort(),
|
|
152
154
|
)
|
|
153
155
|
})
|
|
156
|
+
|
|
157
|
+
test("expands glob patterns inside dot-directories", async () => {
|
|
158
|
+
const files = await expandGlobs([".agents/**"], tempDir)
|
|
159
|
+
expect(files).toEqual([".agents/foo/bar.whatever"])
|
|
160
|
+
})
|
|
154
161
|
})
|
package/src/utils/glob.ts
CHANGED
|
@@ -38,7 +38,7 @@ export async function expandGlobs(
|
|
|
38
38
|
if (isGlobPattern(pattern)) {
|
|
39
39
|
// Expand glob pattern
|
|
40
40
|
const glob = new Bun.Glob(pattern)
|
|
41
|
-
for await (const file of glob.scan({ cwd })) {
|
|
41
|
+
for await (const file of glob.scan({ cwd, dot: true })) {
|
|
42
42
|
files.add(file)
|
|
43
43
|
}
|
|
44
44
|
} else {
|
package/src/utils/pr-branch.ts
CHANGED
|
@@ -439,24 +439,3 @@ export const resolveBranchPairWithAgencyJson = (
|
|
|
439
439
|
// Strategy 4: Fall back to pattern-based resolution
|
|
440
440
|
return resolveBranchPair(currentBranch, sourcePattern, emitPattern)
|
|
441
441
|
})
|
|
442
|
-
|
|
443
|
-
// Legacy function names for backward compatibility
|
|
444
|
-
// These will be updated as we migrate the codebase
|
|
445
|
-
|
|
446
|
-
/**
|
|
447
|
-
* @deprecated Use makeEmitBranchName instead. This function now creates emit branches,
|
|
448
|
-
* not PR branches with suffixes.
|
|
449
|
-
*/
|
|
450
|
-
export function makePrBranchName(branchName: string, pattern: string): string {
|
|
451
|
-
return makeEmitBranchName(branchName, pattern)
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
/**
|
|
455
|
-
* @deprecated Use extractCleanFromEmit instead. Extracts clean branch from emit branch.
|
|
456
|
-
*/
|
|
457
|
-
export function extractSourceBranch(
|
|
458
|
-
emitBranchName: string,
|
|
459
|
-
pattern: string,
|
|
460
|
-
): string | null {
|
|
461
|
-
return extractCleanFromEmit(emitBranchName, pattern)
|
|
462
|
-
}
|