@markjaquith/agency 2.61.1 → 2.61.2

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.61.1",
3
+ "version": "2.61.2",
4
4
  "description": "Manage agentic work across repositories with durable workbases",
5
5
  "keywords": [
6
6
  "agents",
@@ -1,6 +1,6 @@
1
1
  import { afterEach, beforeEach, describe, expect, test } from "bun:test"
2
2
  import { Effect } from "effect"
3
- import { mkdir } from "node:fs/promises"
3
+ import { chmod, mkdir } from "node:fs/promises"
4
4
  import { join } from "node:path"
5
5
  import { TaskService } from "../services/TaskService"
6
6
  import {
@@ -99,6 +99,44 @@ describe("sync command", () => {
99
99
  })
100
100
  })
101
101
 
102
+ test("groups repeated human-readable warnings by affected target", async () => {
103
+ await runTestEffect(
104
+ TaskService.pipe(
105
+ Effect.flatMap((service) =>
106
+ service.create(
107
+ {
108
+ id: "second",
109
+ ticketUrl: null,
110
+ repo: "agency",
111
+ branch: "task/second",
112
+ base: "main",
113
+ },
114
+ root,
115
+ ),
116
+ ),
117
+ ),
118
+ )
119
+ const originalPath = process.env.PATH
120
+ const bin = join(root, "bin")
121
+ await mkdir(bin)
122
+ await Bun.write(
123
+ join(bin, "gh"),
124
+ '#!/bin/sh\necho "provider unavailable" >&2\nexit 1\n',
125
+ )
126
+ await chmod(join(bin, "gh"), 0o755)
127
+ process.env.PATH = `${bin}:${originalPath}`
128
+ try {
129
+ const logs = await captureLogs(() =>
130
+ runTestEffect(sync({ cwd: root, dryRun: true })),
131
+ )
132
+ expect(
133
+ logs.filter((line) => line.includes("provider unavailable")),
134
+ ).toEqual(["Warning 'task:example', 'task:second': provider unavailable"])
135
+ } finally {
136
+ process.env.PATH = originalPath
137
+ }
138
+ })
139
+
102
140
  test("reports human-readable progress without polluting JSON output", async () => {
103
141
  const updates: string[] = []
104
142
  const progress: Progress = {
@@ -8,6 +8,27 @@ interface SyncCommandOptions extends BaseCommandOptions {
8
8
  readonly dryRun?: boolean
9
9
  }
10
10
 
11
+ interface Notice {
12
+ readonly kind: string
13
+ readonly target: string
14
+ readonly message: string
15
+ readonly action?: string
16
+ }
17
+
18
+ const groupedNotices = <T extends Notice>(notices: readonly T[]) => {
19
+ const groups = new Map<string, { notice: T; targets: string[] }>()
20
+ for (const notice of notices) {
21
+ const key = JSON.stringify([notice.kind, notice.message, notice.action])
22
+ const group = groups.get(key)
23
+ if (group) group.targets.push(notice.target)
24
+ else groups.set(key, { notice, targets: [notice.target] })
25
+ }
26
+ return groups.values()
27
+ }
28
+
29
+ const formatTargets = (targets: readonly string[]) =>
30
+ targets.map((target) => `'${target}'`).join(", ")
31
+
11
32
  export const sync = (
12
33
  options: SyncCommandOptions = {},
13
34
  progress: Progress = createProgress({
@@ -62,9 +83,11 @@ export const sync = (
62
83
  `${change.status === "applied" ? "Applied" : "Planned"} ${change.kind} '${change.target}': ${change.message}`,
63
84
  )
64
85
  }
65
- for (const warning of result.warnings) {
86
+ for (const { notice: warning, targets } of groupedNotices(
87
+ result.warnings,
88
+ )) {
66
89
  log(
67
- `Warning '${warning.target}': ${warning.message}${warning.action ? `. ${warning.action}` : ""}`,
90
+ `Warning ${formatTargets(targets)}: ${warning.message}${warning.action ? `. ${warning.action}` : ""}`,
68
91
  )
69
92
  }
70
93
  for (const issue of result.repositories.unresolved) {
@@ -72,9 +95,11 @@ export const sync = (
72
95
  `Unresolved repository '${issue.alias}': ${issue.message}. ${issue.action}`,
73
96
  )
74
97
  }
75
- for (const issue of result.unresolved) {
98
+ for (const { notice: issue, targets } of groupedNotices(
99
+ result.unresolved,
100
+ )) {
76
101
  log(
77
- `Unresolved '${issue.target}': ${issue.message}${issue.action ? `. ${issue.action}` : ""}`,
102
+ `Unresolved ${formatTargets(targets)}: ${issue.message}${issue.action ? `. ${issue.action}` : ""}`,
78
103
  )
79
104
  }
80
105
  if (
@@ -64,14 +64,17 @@ case "$*" in
64
64
  *mergeable*) ;;
65
65
  *) echo "mergeable field was not requested" >&2; exit 2 ;;
66
66
  esac
67
+ case "$*" in
68
+ *baseRepository*) echo "unsupported baseRepository field was requested" >&2; exit 3 ;;
69
+ esac
67
70
  if [ "$2" = "view" ]; then
68
71
  cat <<'JSON'
69
- {"number":42,"state":"MERGED","title":"Ship","isDraft":false,"headRefName":"feat/example","baseRefName":"main","headRepository":{"nameWithOwner":"example/agency"},"baseRepository":{"nameWithOwner":"example/agency"},"url":"https://github.com/example/agency/pull/42","mergedAt":"2100-01-01T00:00:00Z","mergeCommit":{"oid":"abc"},"mergeable":"MERGEABLE"}
72
+ {"number":42,"state":"MERGED","title":"Ship","isDraft":false,"headRefName":"feat/example","baseRefName":"main","headRepository":{"nameWithOwner":"example/agency"},"url":"https://github.com/example/agency/pull/42","mergedAt":"2100-01-01T00:00:00Z","mergeCommit":{"oid":"abc"},"mergeable":"MERGEABLE"}
70
73
  JSON
71
74
  exit 0
72
75
  fi
73
76
  cat <<'JSON'
74
- [{"number":42,"state":"MERGED","title":"Ship","isDraft":false,"headRefName":"feat/example","baseRefName":"main","headRepository":{"nameWithOwner":"example/agency"},"baseRepository":{"nameWithOwner":"example/agency"},"url":"https://github.com/example/agency/pull/42","mergedAt":"2100-01-01T00:00:00Z","mergeCommit":{"oid":"abc"},"mergeable":"MERGEABLE"}]
77
+ [{"number":42,"state":"MERGED","title":"Ship","isDraft":false,"headRefName":"feat/example","baseRefName":"main","headRepository":{"nameWithOwner":"example/agency"},"url":"https://github.com/example/agency/pull/42","mergedAt":"2100-01-01T00:00:00Z","mergeCommit":{"oid":"abc"},"mergeable":"MERGEABLE"}]
75
78
  JSON
76
79
  `,
77
80
  )
@@ -172,6 +175,7 @@ pr: null
172
175
  )
173
176
  const invocation = await Bun.file(capture).text()
174
177
  expect(invocation).toContain("--repo\n")
178
+ expect(invocation).not.toContain("baseRepository")
175
179
  expect(invocation).toContain("GIT_DIR=")
176
180
  expect(invocation).toContain(".jj")
177
181
 
@@ -1013,4 +1017,44 @@ exit 9
1013
1017
  ),
1014
1018
  ).toEqual([])
1015
1019
  })
1020
+
1021
+ test("keeps pull request query failures concise", async () => {
1022
+ await runTestEffect(
1023
+ TaskService.pipe(
1024
+ Effect.flatMap((service) =>
1025
+ service.create(
1026
+ {
1027
+ id: "unavailable",
1028
+ ticketUrl: null,
1029
+ repo: "agency",
1030
+ branch: "feat/unavailable",
1031
+ base: "main",
1032
+ },
1033
+ root,
1034
+ ),
1035
+ ),
1036
+ ),
1037
+ )
1038
+ await Bun.write(
1039
+ join(root, "bin", "gh"),
1040
+ `#!/bin/sh
1041
+ echo "Unknown JSON field: unsupported" >&2
1042
+ echo "Available fields:" >&2
1043
+ echo " additions" >&2
1044
+ exit 1
1045
+ `,
1046
+ )
1047
+ await chmod(join(root, "bin", "gh"), 0o755)
1048
+
1049
+ const result = await runTestEffect(
1050
+ SyncService.pipe(
1051
+ Effect.flatMap((service) => service.reconcile({ cwd: root })),
1052
+ ),
1053
+ )
1054
+ expect(result.warnings).toContainEqual({
1055
+ kind: "pr-discovery-unavailable",
1056
+ target: "task:unavailable",
1057
+ message: "Unknown JSON field: unsupported",
1058
+ })
1059
+ })
1016
1060
  })
@@ -153,6 +153,15 @@ const isCommitId = (ref: string) => /^[0-9a-f]{40,64}$/i.test(ref)
153
153
  const originRef = (ref: string) =>
154
154
  ref.replace(/^refs\/remotes\/origin\//, "").replace(/^origin\//, "")
155
155
 
156
+ const GITHUB_PR_FIELDS =
157
+ "number,state,title,isDraft,headRefName,baseRefName,headRepository,url,mergedAt,mergeCommit,mergeable"
158
+
159
+ const commandErrorSummary = (stderr: string, fallback: string) =>
160
+ stderr
161
+ .split("\n")
162
+ .map((line) => line.trim())
163
+ .find(Boolean) ?? fallback
164
+
156
165
  export class SyncService extends Effect.Service<SyncService>()("SyncService", {
157
166
  sync: () => ({
158
167
  reconcile: (
@@ -349,7 +358,7 @@ export class SyncService extends Effect.Service<SyncService>()("SyncService", {
349
358
  "view",
350
359
  existing.url,
351
360
  "--json",
352
- "number,state,title,isDraft,headRefName,baseRefName,headRepository,baseRepository,url,mergedAt,mergeCommit,mergeable",
361
+ GITHUB_PR_FIELDS,
353
362
  ],
354
363
  {
355
364
  cwd: repositoryPath,
@@ -372,7 +381,7 @@ export class SyncService extends Effect.Service<SyncService>()("SyncService", {
372
381
  "--state",
373
382
  "all",
374
383
  "--json",
375
- "number,state,title,isDraft,headRefName,baseRefName,headRepository,baseRepository,url,mergedAt,mergeCommit,mergeable",
384
+ GITHUB_PR_FIELDS,
376
385
  ],
377
386
  {
378
387
  cwd: repositoryPath,
@@ -824,8 +833,10 @@ export class SyncService extends Effect.Service<SyncService>()("SyncService", {
824
833
  warnings.push({
825
834
  kind: existing ? "pr-unavailable" : "pr-discovery-unavailable",
826
835
  target: record.key,
827
- message:
828
- queried.stderr.trim() || "Could not query delivery provider",
836
+ message: commandErrorSummary(
837
+ queried.stderr,
838
+ "Could not query delivery provider",
839
+ ),
829
840
  })
830
841
  }
831
842
  } else if (existing) {
@@ -858,7 +869,7 @@ export class SyncService extends Effect.Service<SyncService>()("SyncService", {
858
869
  warnings.push({
859
870
  kind: "pr-unavailable",
860
871
  target: record.key,
861
- message: `Could not inspect ${existing.url}: ${viewed.stderr.trim()}`,
872
+ message: `Could not inspect ${existing.url}: ${commandErrorSummary(viewed.stderr, "GitHub query failed")}`,
862
873
  })
863
874
  }
864
875
  } else {
@@ -901,8 +912,10 @@ export class SyncService extends Effect.Service<SyncService>()("SyncService", {
901
912
  warnings.push({
902
913
  kind: "pr-discovery-unavailable",
903
914
  target: record.key,
904
- message:
905
- listed.stderr.trim() || "Could not discover pull requests",
915
+ message: commandErrorSummary(
916
+ listed.stderr,
917
+ "Could not discover pull requests",
918
+ ),
906
919
  })
907
920
  }
908
921
  }