@tienne/gestalt 0.83.2 → 0.84.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 (142) hide show
  1. package/CLAUDE.md +2 -2
  2. package/README.ko.md +3 -1
  3. package/README.md +3 -1
  4. package/dist/package.json +1 -1
  5. package/dist/plugin/review-agents/harness-reviewer/AGENT.md +211 -0
  6. package/dist/plugin/role-agents/_shared/references/comment-rules.md +1 -1
  7. package/dist/plugin/role-agents/suggestion-verifier/AGENT.md +7 -1
  8. package/dist/plugin/skills/_shared/proactive-routing.md +1 -0
  9. package/dist/plugin/skills/_shared/rule-sources.md +1 -1
  10. package/dist/plugin/skills/agent/SKILL.md +2 -0
  11. package/dist/plugin/skills/review/SKILL.md +343 -7
  12. package/dist/plugin/skills/review-loop/SKILL.md +124 -2
  13. package/dist/plugin/skills/ship/SKILL.md +40 -1
  14. package/dist/schemas/gestalt.schema.json +12 -0
  15. package/dist/src/cli/commands/approve-gate.d.ts +24 -0
  16. package/dist/src/cli/commands/approve-gate.d.ts.map +1 -0
  17. package/dist/src/cli/commands/approve-gate.js +90 -0
  18. package/dist/src/cli/commands/approve-gate.js.map +1 -0
  19. package/dist/src/cli/commands/harness-refs-cross-pr.d.ts +218 -0
  20. package/dist/src/cli/commands/harness-refs-cross-pr.d.ts.map +1 -0
  21. package/dist/src/cli/commands/harness-refs-cross-pr.js +934 -0
  22. package/dist/src/cli/commands/harness-refs-cross-pr.js.map +1 -0
  23. package/dist/src/cli/commands/harness-refs.d.ts +19 -0
  24. package/dist/src/cli/commands/harness-refs.d.ts.map +1 -0
  25. package/dist/src/cli/commands/harness-refs.js +51 -0
  26. package/dist/src/cli/commands/harness-refs.js.map +1 -0
  27. package/dist/src/cli/index.d.ts.map +1 -1
  28. package/dist/src/cli/index.js +97 -0
  29. package/dist/src/cli/index.js.map +1 -1
  30. package/dist/src/core/config.d.ts +3 -0
  31. package/dist/src/core/config.d.ts.map +1 -1
  32. package/dist/src/core/config.js +23 -0
  33. package/dist/src/core/config.js.map +1 -1
  34. package/dist/src/core/types.d.ts +2 -0
  35. package/dist/src/core/types.d.ts.map +1 -1
  36. package/dist/src/harness-review/approve-gate.d.ts +53 -0
  37. package/dist/src/harness-review/approve-gate.d.ts.map +1 -0
  38. package/dist/src/harness-review/approve-gate.js +148 -0
  39. package/dist/src/harness-review/approve-gate.js.map +1 -0
  40. package/dist/src/harness-review/backward-search.d.ts +50 -0
  41. package/dist/src/harness-review/backward-search.d.ts.map +1 -0
  42. package/dist/src/harness-review/backward-search.js +183 -0
  43. package/dist/src/harness-review/backward-search.js.map +1 -0
  44. package/dist/src/harness-review/collect.d.ts +75 -0
  45. package/dist/src/harness-review/collect.d.ts.map +1 -0
  46. package/dist/src/harness-review/collect.js +253 -0
  47. package/dist/src/harness-review/collect.js.map +1 -0
  48. package/dist/src/harness-review/copy-drift.d.ts +83 -0
  49. package/dist/src/harness-review/copy-drift.d.ts.map +1 -0
  50. package/dist/src/harness-review/copy-drift.js +477 -0
  51. package/dist/src/harness-review/copy-drift.js.map +1 -0
  52. package/dist/src/harness-review/follow-up-marker.d.ts +29 -0
  53. package/dist/src/harness-review/follow-up-marker.d.ts.map +1 -0
  54. package/dist/src/harness-review/follow-up-marker.js +143 -0
  55. package/dist/src/harness-review/follow-up-marker.js.map +1 -0
  56. package/dist/src/harness-review/forward-search.d.ts +30 -0
  57. package/dist/src/harness-review/forward-search.d.ts.map +1 -0
  58. package/dist/src/harness-review/forward-search.js +381 -0
  59. package/dist/src/harness-review/forward-search.js.map +1 -0
  60. package/dist/src/harness-review/github-backend.d.ts +42 -0
  61. package/dist/src/harness-review/github-backend.d.ts.map +1 -0
  62. package/dist/src/harness-review/github-backend.js +130 -0
  63. package/dist/src/harness-review/github-backend.js.map +1 -0
  64. package/dist/src/harness-review/github-code-search.d.ts +46 -0
  65. package/dist/src/harness-review/github-code-search.d.ts.map +1 -0
  66. package/dist/src/harness-review/github-code-search.js +94 -0
  67. package/dist/src/harness-review/github-code-search.js.map +1 -0
  68. package/dist/src/harness-review/identifiers.d.ts +75 -0
  69. package/dist/src/harness-review/identifiers.d.ts.map +1 -0
  70. package/dist/src/harness-review/identifiers.js +678 -0
  71. package/dist/src/harness-review/identifiers.js.map +1 -0
  72. package/dist/src/harness-review/placeholders.d.ts +44 -0
  73. package/dist/src/harness-review/placeholders.d.ts.map +1 -0
  74. package/dist/src/harness-review/placeholders.js +138 -0
  75. package/dist/src/harness-review/placeholders.js.map +1 -0
  76. package/dist/src/harness-review/related-pr.d.ts +133 -0
  77. package/dist/src/harness-review/related-pr.d.ts.map +1 -0
  78. package/dist/src/harness-review/related-pr.js +553 -0
  79. package/dist/src/harness-review/related-pr.js.map +1 -0
  80. package/dist/src/harness-review/related-repos.d.ts +69 -0
  81. package/dist/src/harness-review/related-repos.d.ts.map +1 -0
  82. package/dist/src/harness-review/related-repos.js +430 -0
  83. package/dist/src/harness-review/related-repos.js.map +1 -0
  84. package/dist/src/harness-review/remote-url.d.ts +8 -0
  85. package/dist/src/harness-review/remote-url.d.ts.map +1 -0
  86. package/dist/src/harness-review/remote-url.js +35 -0
  87. package/dist/src/harness-review/remote-url.js.map +1 -0
  88. package/dist/src/harness-review/replay.d.ts +163 -0
  89. package/dist/src/harness-review/replay.d.ts.map +1 -0
  90. package/dist/src/harness-review/replay.js +575 -0
  91. package/dist/src/harness-review/replay.js.map +1 -0
  92. package/dist/src/harness-review/rule-id-lists.d.ts +38 -0
  93. package/dist/src/harness-review/rule-id-lists.d.ts.map +1 -0
  94. package/dist/src/harness-review/rule-id-lists.js +189 -0
  95. package/dist/src/harness-review/rule-id-lists.js.map +1 -0
  96. package/dist/src/harness-review/search-backend.d.ts +59 -0
  97. package/dist/src/harness-review/search-backend.d.ts.map +1 -0
  98. package/dist/src/harness-review/search-backend.js +178 -0
  99. package/dist/src/harness-review/search-backend.js.map +1 -0
  100. package/dist/src/harness-review/self-contamination.d.ts +83 -0
  101. package/dist/src/harness-review/self-contamination.d.ts.map +1 -0
  102. package/dist/src/harness-review/self-contamination.js +560 -0
  103. package/dist/src/harness-review/self-contamination.js.map +1 -0
  104. package/dist/src/harness-review/self-references.d.ts +20 -0
  105. package/dist/src/harness-review/self-references.d.ts.map +1 -0
  106. package/dist/src/harness-review/self-references.js +173 -0
  107. package/dist/src/harness-review/self-references.js.map +1 -0
  108. package/dist/src/harness-review/three-state.d.ts +79 -0
  109. package/dist/src/harness-review/three-state.d.ts.map +1 -0
  110. package/dist/src/harness-review/three-state.js +238 -0
  111. package/dist/src/harness-review/three-state.js.map +1 -0
  112. package/dist/src/harness-review/types.d.ts +148 -0
  113. package/dist/src/harness-review/types.d.ts.map +1 -0
  114. package/dist/src/harness-review/types.js +74 -0
  115. package/dist/src/harness-review/types.js.map +1 -0
  116. package/dist/src/index.d.ts +1 -0
  117. package/dist/src/index.d.ts.map +1 -1
  118. package/dist/src/index.js +1 -0
  119. package/dist/src/index.js.map +1 -1
  120. package/dist/src/mcp/tools/review-passthrough.js +7 -1
  121. package/dist/src/mcp/tools/review-passthrough.js.map +1 -1
  122. package/dist/src/review/agent-matcher.d.ts +16 -1
  123. package/dist/src/review/agent-matcher.d.ts.map +1 -1
  124. package/dist/src/review/agent-matcher.js +31 -4
  125. package/dist/src/review/agent-matcher.js.map +1 -1
  126. package/dist/src/review/passthrough-engine.d.ts.map +1 -1
  127. package/dist/src/review/passthrough-engine.js +40 -2
  128. package/dist/src/review/passthrough-engine.js.map +1 -1
  129. package/package.json +1 -1
  130. package/plugin/.codex-plugin/plugin.json +1 -1
  131. package/plugin/.mcp.json +1 -1
  132. package/plugin/mcp.json +1 -1
  133. package/plugin/review-agents/harness-reviewer/AGENT.md +211 -0
  134. package/plugin/role-agents/_shared/references/comment-rules.md +1 -1
  135. package/plugin/role-agents/suggestion-verifier/AGENT.md +7 -1
  136. package/plugin/skills/_shared/proactive-routing.md +1 -0
  137. package/plugin/skills/_shared/rule-sources.md +1 -1
  138. package/plugin/skills/agent/SKILL.md +2 -0
  139. package/plugin/skills/review/SKILL.md +343 -7
  140. package/plugin/skills/review-loop/SKILL.md +124 -2
  141. package/plugin/skills/ship/SKILL.md +40 -1
  142. package/schemas/gestalt.schema.json +12 -0
@@ -0,0 +1,553 @@
1
+ import { runGh } from '../review-loop/fetch.js';
2
+ import { classifyGhFailure } from './github-backend.js';
3
+ /**
4
+ * 머지된 연관 PR 후보를 거슬러 찾는 기간. 이번 PR 생성 시각에서 센다. 실행 시각에서 세면
5
+ * 오래 열린 PR이나 재현 실행에서 같은 시기에 머지된 PR이 전부 빠진다.
6
+ * 사용자 결정으로 30일에 고정했고 설정으로 열지 않는다.
7
+ * 0이나 끄기 값을 받을 자리를 두면 그게 탐색을 끄는 스위치가 된다.
8
+ */
9
+ export const MERGED_LOOKBACK_DAYS = 30;
10
+ /** "비슷한 시기"의 폭. 이번 PR 생성 시각 앞뒤로 이만큼 안에 만들어진 PR만 본다 */
11
+ export const SAME_AUTHOR_NEARBY_DAYS = 14;
12
+ /**
13
+ * 참조 대상을 건드리는 열린 PR 중 이번 PR보다 이만큼 먼저 열린 건 연관 후보로 안 본다.
14
+ * 이번 작업이 시작되기 전부터 열려 있던 PR이라, 파일이 겹쳐도 함께 진행한 작업이 아니다.
15
+ * 몇 달째 열린 PR 하나가 그 파일을 건드리는 모든 PR에 후보로 붙는 걸 막는다.
16
+ * 머지된 PR은 이번 PR 앞뒤로 이만큼 안에 열린 것만 본다. 뒤쪽을 막지 않으면 오래 열린 PR이나
17
+ * 재현 실행에서 최근 머지된 PR이 조회 결과를 다 채워 같은 시기 PR이 밀려난다.
18
+ */
19
+ export const TOUCHES_TARGET_MAX_AGE_DAYS = 30;
20
+ /**
21
+ * 한 단계에서 상세 조회까지 가는 후보 상한. 검색은 열린 PR과 머지된 PR로 나눠 두 번 하고
22
+ * 이 상한도 각각에 건다. 합쳐서 자르면 열린 PR이 먼저 차서 머지된 PR이 밀려난다
23
+ */
24
+ export const MAX_CANDIDATES_PER_STEP = 30;
25
+ /**
26
+ * 검색 한 번에 받아오는 결과 수. 상세 조회 상한보다 넉넉히 받아 생성일 거리로 줄 세운 뒤 자른다.
27
+ * GitHub 검색 기본 순서(best-match)대로 자르면 가까운 PR이 뒤로 밀려 빠진다
28
+ */
29
+ export const SEARCH_FETCH_LIMIT = 100;
30
+ /** 본문에서 따라가는 링크 상한 */
31
+ export const MAX_BODY_LINKS = 20;
32
+ const DAY_MS = 24 * 60 * 60 * 1000;
33
+ /**
34
+ * 확정 기준 표의 열. ship은 내 PR이라 ⓐ 승인 단계에서 한 줄로 확인받고
35
+ * review-loop는 남의 PR이라 작성자에게 질문으로 넘긴다.
36
+ */
37
+ export const RELATED_PR_MODES = ['ship', 'reviewLoop'];
38
+ const VIEW_FIELDS = 'number,state,headRefOid,headRefName,baseRefName,author,title,body,url,createdAt,mergedAt,files';
39
+ /**
40
+ * 이번 PR의 연관 PR을 찾아 확정 수준을 매긴다.
41
+ *
42
+ * 순서는 본문 링크, 같은 티켓 키, 참조 대상을 건드리는 열린 PR, 같은 작성자의 비슷한 시기 PR이다.
43
+ * 같은 PR이 여러 단계에 걸리면 먼저 찾은 단계가 foundBy가 되고 신호는 합친다.
44
+ *
45
+ * PR 본문과 제목은 링크와 티켓 키를 정규식으로 뽑는 데만 쓴다. 거기 적힌 문장은
46
+ * 탐색 범위도 확정 수준도 바꾸지 못한다 (untrusted-input.md).
47
+ */
48
+ export function findRelatedPrs(opts) {
49
+ const gh = opts.gh ?? runGh;
50
+ const now = opts.now ?? new Date();
51
+ const { current, mode } = opts;
52
+ const allowed = new Set(opts.relatedRepos.map(repoKey));
53
+ const selfKey = current.repo.toLowerCase();
54
+ const found = new Map();
55
+ const skipped = [];
56
+ const notices = [];
57
+ const ticketKeys = extractTicketKeys([current.title, current.body, current.branch].join('\n'));
58
+ const center = current.createdAt ? new Date(current.createdAt) : now;
59
+ const mergedSinceMs = center.getTime() - MERGED_LOOKBACK_DAYS * DAY_MS;
60
+ const mergedSince = isoDate(new Date(mergedSinceMs));
61
+ const distanceMs = (createdAt) => {
62
+ const t = createdAt ? Date.parse(createdAt) : NaN;
63
+ return Number.isFinite(t) ? Math.abs(t - center.getTime()) : Number.POSITIVE_INFINITY;
64
+ };
65
+ // ship은 PR 번호가 아직 없을 수 있어 같은 레포의 같은 브랜치도 자기 자신으로 본다
66
+ const isSelf = (repo, number, branch) => repo.toLowerCase() === selfKey &&
67
+ (number === current.number || (current.number === undefined && branch === current.branch));
68
+ const targetsByRepo = new Map();
69
+ for (const t of opts.referenceTargets ?? []) {
70
+ const repo = t.repo.toLowerCase();
71
+ if (!allowed.has(repo)) {
72
+ skipped.push({
73
+ step: 'touchesTarget',
74
+ repo: t.repo,
75
+ reason: 'outsideRelatedRepos',
76
+ detail: '관련 레포 목록 밖이라 따라가지 않는다',
77
+ });
78
+ continue;
79
+ }
80
+ const set = targetsByRepo.get(repo) ?? new Set();
81
+ set.add(t.path);
82
+ targetsByRepo.set(repo, set);
83
+ }
84
+ const record = (step, detail, patch) => {
85
+ const key = `${detail.repo.toLowerCase()}#${detail.number}`;
86
+ let entry = found.get(key);
87
+ if (!entry) {
88
+ entry = { detail, foundBy: step, signals: baseSignals(current, detail, ticketKeys) };
89
+ // 머지된 PR도 참조 대상을 건드렸는지 본다. 확정 수준은 안 바꾸고 순서에만 쓴다
90
+ const targets = targetsByRepo.get(detail.repo.toLowerCase());
91
+ if (targets)
92
+ entry.signals.touchedTargets = detail.files.filter((f) => targets.has(f));
93
+ found.set(key, entry);
94
+ const notice = scanInstructionLikeText(detail);
95
+ if (notice)
96
+ notices.push(notice);
97
+ }
98
+ if (patch.bodyLink)
99
+ entry.signals.bodyLink = true;
100
+ for (const t of patch.touchedTargets ?? []) {
101
+ if (!entry.signals.touchedTargets.includes(t))
102
+ entry.signals.touchedTargets.push(t);
103
+ }
104
+ };
105
+ const view = (step, repo, number) => {
106
+ const run = runJson(gh, ['pr', 'view', String(number), '--repo', repo, '--json', VIEW_FIELDS]);
107
+ if (!run.ok) {
108
+ skipped.push({ step, repo, number, reason: run.reason, detail: run.detail });
109
+ return null;
110
+ }
111
+ const detail = toPrDetail(repo, run.value);
112
+ if (!detail)
113
+ skipped.push({ step, repo, number, reason: 'unparsable', detail: 'PR 응답 형식이 다르다' });
114
+ return detail;
115
+ };
116
+ // 1. 본문 링크
117
+ const links = extractPrLinks(current.body).slice(0, MAX_BODY_LINKS);
118
+ let hasBodyLink = false;
119
+ for (const link of links) {
120
+ if (isSelf(link.repo, link.number))
121
+ continue;
122
+ if (!allowed.has(link.repo.toLowerCase())) {
123
+ skipped.push({
124
+ step: 'bodyLink',
125
+ repo: link.repo,
126
+ number: link.number,
127
+ reason: 'outsideRelatedRepos',
128
+ detail: '관련 레포 목록 밖이라 따라가지 않는다',
129
+ });
130
+ continue;
131
+ }
132
+ const detail = view('bodyLink', link.repo, link.number);
133
+ if (!detail)
134
+ continue;
135
+ hasBodyLink = true;
136
+ record('bodyLink', detail, { bodyLink: true });
137
+ }
138
+ const repos = [...allowed];
139
+ const searchStep = (step, baseArgs, accept) => {
140
+ if (repos.length === 0)
141
+ return;
142
+ const repoArgs = repos.flatMap((r) => ['--repo', r]);
143
+ const variants = [
144
+ ['--state', 'open'],
145
+ ['--merged', `--merged-at=>=${mergedSince}`],
146
+ ];
147
+ const hits = [];
148
+ for (const variant of variants) {
149
+ const run = runJson(gh, [
150
+ 'search',
151
+ 'prs',
152
+ ...baseArgs,
153
+ ...repoArgs,
154
+ ...variant,
155
+ '--json',
156
+ 'number,repository,createdAt',
157
+ '--limit',
158
+ String(SEARCH_FETCH_LIMIT),
159
+ ]);
160
+ if (!run.ok) {
161
+ skipped.push({ step, reason: run.reason, detail: run.detail });
162
+ continue;
163
+ }
164
+ const nearest = parseSearchHits(run.value)
165
+ .sort((a, b) => distanceMs(a.createdAt) - distanceMs(b.createdAt))
166
+ .slice(0, MAX_CANDIDATES_PER_STEP);
167
+ hits.push(...nearest);
168
+ }
169
+ for (const hit of dedupeHits(hits)) {
170
+ if (!allowed.has(hit.repo.toLowerCase()) || isSelf(hit.repo, hit.number))
171
+ continue;
172
+ const existing = found.get(`${hit.repo.toLowerCase()}#${hit.number}`);
173
+ const detail = existing?.detail ?? view(step, hit.repo, hit.number);
174
+ if (!detail || isSelf(detail.repo, detail.number, detail.branch))
175
+ continue;
176
+ if (!isWithinWindow(detail, mergedSinceMs) || !accept(detail))
177
+ continue;
178
+ record(step, detail, {});
179
+ }
180
+ };
181
+ // 2. 같은 티켓 키. 검색은 넓게 걸리므로 키가 제목, 본문, 브랜치에 실제로 있는지 다시 본다
182
+ for (const key of ticketKeys) {
183
+ searchStep('ticketKey', [key], (d) => extractTicketKeys([d.title, d.body, d.branch].join('\n')).includes(key));
184
+ }
185
+ // 3. 참조 대상을 건드리는 PR. 목록 조회가 files를 함께 주므로 상세 조회 없이 거른다
186
+ const openedSince = center.getTime() - TOUCHES_TARGET_MAX_AGE_DAYS * DAY_MS;
187
+ const openedUntil = center.getTime() + TOUCHES_TARGET_MAX_AGE_DAYS * DAY_MS;
188
+ const listVariants = [
189
+ { state: 'open', args: ['--state', 'open', '--limit', String(MAX_CANDIDATES_PER_STEP)] },
190
+ {
191
+ state: 'merged',
192
+ args: [
193
+ '--state',
194
+ 'merged',
195
+ '--search',
196
+ `merged:>=${mergedSince} created:${isoDate(new Date(openedSince))}..${isoDate(new Date(openedUntil))}`,
197
+ '--limit',
198
+ String(SEARCH_FETCH_LIMIT),
199
+ ],
200
+ },
201
+ ];
202
+ for (const [repo, paths] of targetsByRepo) {
203
+ for (const variant of listVariants) {
204
+ const run = runJson(gh, [
205
+ 'pr',
206
+ 'list',
207
+ '--repo',
208
+ repo,
209
+ ...variant.args,
210
+ '--json',
211
+ VIEW_FIELDS,
212
+ ]);
213
+ if (!run.ok) {
214
+ skipped.push({ step: 'touchesTarget', repo, reason: run.reason, detail: run.detail });
215
+ continue;
216
+ }
217
+ if (!Array.isArray(run.value)) {
218
+ skipped.push({
219
+ step: 'touchesTarget',
220
+ repo,
221
+ reason: 'unparsable',
222
+ detail: '배열이 아니다',
223
+ });
224
+ continue;
225
+ }
226
+ const hits = [];
227
+ for (const raw of run.value) {
228
+ const detail = toPrDetail(repo, raw);
229
+ if (!detail || detail.state !== variant.state)
230
+ continue;
231
+ if (isSelf(detail.repo, detail.number, detail.branch))
232
+ continue;
233
+ const opened = detail.createdAt ? Date.parse(detail.createdAt) : NaN;
234
+ if (Number.isFinite(opened) && opened < openedSince)
235
+ continue;
236
+ if (variant.state === 'merged') {
237
+ if (Number.isFinite(opened) && opened > openedUntil)
238
+ continue;
239
+ if (!isWithinWindow(detail, mergedSinceMs))
240
+ continue;
241
+ }
242
+ const touched = detail.files.filter((f) => paths.has(f));
243
+ if (touched.length > 0)
244
+ hits.push({ detail, touched });
245
+ }
246
+ hits
247
+ .sort((a, b) => distanceMs(a.detail.createdAt) - distanceMs(b.detail.createdAt))
248
+ .slice(0, MAX_CANDIDATES_PER_STEP)
249
+ .forEach(({ detail, touched }) => record('touchesTarget', detail, { touchedTargets: touched }));
250
+ }
251
+ }
252
+ const distance = (d) => distanceMs(d.createdAt);
253
+ // 4. 같은 작성자의 비슷한 시기 PR
254
+ if (current.author) {
255
+ const from = isoDate(new Date(center.getTime() - SAME_AUTHOR_NEARBY_DAYS * DAY_MS));
256
+ const to = isoDate(new Date(center.getTime() + SAME_AUTHOR_NEARBY_DAYS * DAY_MS));
257
+ searchStep('sameAuthorNearby', [`--author=${current.author}`, `--created=${from}..${to}`], (d) => sameLogin(d.author, current.author));
258
+ }
259
+ // 기본 브랜치는 머지된 후보가 있는 레포만 한 번씩 묻는다. 못 알아내면 묻는 쪽으로 남긴다
260
+ const defaultBranches = new Map();
261
+ const defaultBranchOf = (repo, step) => {
262
+ const key = repo.toLowerCase();
263
+ if (defaultBranches.has(key))
264
+ return defaultBranches.get(key);
265
+ const run = runJson(gh, ['repo', 'view', repo, '--json', 'defaultBranchRef']);
266
+ let branch = null;
267
+ if (!run.ok)
268
+ skipped.push({ step, repo, reason: run.reason, detail: run.detail });
269
+ else if (isRecord(run.value) && isRecord(run.value.defaultBranchRef)) {
270
+ const name = run.value.defaultBranchRef.name;
271
+ if (typeof name === 'string' && name)
272
+ branch = name;
273
+ }
274
+ if (run.ok && branch === null)
275
+ skipped.push({ step, repo, reason: 'unparsable', detail: '기본 브랜치를 못 읽었다' });
276
+ defaultBranches.set(key, branch);
277
+ return branch;
278
+ };
279
+ // 열린 PR과 머지된 PR을 나눠 세운다. 머지된 PR이 열린 PR을 뒤로 밀지 않게 한다
280
+ const candidates = [...found.values()]
281
+ .sort((a, b) => stateGroup(a.detail.state) - stateGroup(b.detail.state) ||
282
+ rankOf(a.signals) - rankOf(b.signals) ||
283
+ distance(a.detail) - distance(b.detail))
284
+ .map(({ detail, foundBy, signals }) => {
285
+ const confirmation = decideConfirmation(signals, mode);
286
+ const inDefault = confirmation !== 'confirmed' &&
287
+ detail.state === 'merged' &&
288
+ detail.baseBranch === defaultBranchOf(detail.repo, foundBy);
289
+ return toCandidate(detail, foundBy, signals, inDefault ? 'inDefaultBranch' : confirmation);
290
+ });
291
+ return {
292
+ mode,
293
+ candidates,
294
+ skipped,
295
+ notices,
296
+ suggestBodyLink: !hasBodyLink && candidates.length > 0,
297
+ ticketKeys,
298
+ };
299
+ }
300
+ /**
301
+ * 확정 기준 표. ship과 review-loop가 갈리는 자리가 여기 하나다.
302
+ * - 본문 링크: 양쪽 다 확정
303
+ * - 같은 작성자 + 같은 티켓: 양쪽 다 확정. review-loop는 근거를 리포트에 싣는다
304
+ * - 같은 작성자 + 같은 브랜치명: ship만 확정
305
+ * - 그 밖(참조 대상만 건드림, 같은 작성자만 등): ship은 ⓐ에서 확인, review-loop는 작성자 질문
306
+ * 확정 못 한 후보가 이미 기본 브랜치에 머지됐으면 findRelatedPrs가 inDefaultBranch로 바꾼다.
307
+ * 기본 브랜치 조회가 gh를 타야 해서 이 함수 밖에 둔다
308
+ */
309
+ export function decideConfirmation(signals, mode) {
310
+ if (signals.bodyLink)
311
+ return 'confirmed';
312
+ if (signals.sameAuthor && signals.sharedTicketKeys.length > 0)
313
+ return 'confirmed';
314
+ if (mode === 'ship' && signals.sameAuthor && signals.sameBranch)
315
+ return 'confirmed';
316
+ return mode === 'ship' ? 'needsShipConfirm' : 'needsAuthorAnswer';
317
+ }
318
+ /** 답 전까지 판정 근거로 쓰면 안 되는 후보를 거른다. confirmed만 세 상태 판정으로 넘긴다 */
319
+ export function confirmedRelatedPrs(candidates) {
320
+ return candidates.filter((c) => c.confirmation === 'confirmed');
321
+ }
322
+ const PR_URL_RE = /https?:\/\/github\.com\/([A-Za-z0-9-]+)\/([A-Za-z0-9._-]+)\/pull\/(\d+)/g;
323
+ const PR_SHORT_RE = /(?<![\w/.-])([A-Za-z0-9-]+)\/([A-Za-z0-9._-]+)#(\d+)\b/g;
324
+ /** 본문에서 PR 링크를 뽑는다. 전체 URL과 owner/name#N 꼴만 본다 */
325
+ export function extractPrLinks(text) {
326
+ const out = [];
327
+ const seen = new Set();
328
+ const push = (owner, name, num) => {
329
+ const repo = `${owner}/${name.replace(/\.git$/, '')}`;
330
+ const number = Number(num);
331
+ const key = `${repo.toLowerCase()}#${number}`;
332
+ if (!Number.isSafeInteger(number) || number <= 0 || seen.has(key))
333
+ return;
334
+ seen.add(key);
335
+ out.push({ repo, number });
336
+ };
337
+ for (const m of text.matchAll(PR_URL_RE))
338
+ push(m[1], m[2], m[3]);
339
+ for (const m of text.matchAll(PR_SHORT_RE))
340
+ push(m[1], m[2], m[3]);
341
+ return out;
342
+ }
343
+ const TICKET_RE = /(?<![A-Za-z0-9])([A-Z][A-Z0-9]{1,9})-(\d{1,6})(?![A-Za-z0-9])/g;
344
+ // 티켓 키 꼴이지만 규격 이름인 것. 같은 규격을 언급했다고 연관 PR이 되지 않는다
345
+ const NON_TICKET_PREFIXES = new Set([
346
+ 'UTF',
347
+ 'ISO',
348
+ 'SHA',
349
+ 'AES',
350
+ 'RFC',
351
+ 'CVE',
352
+ 'HTTP',
353
+ 'TLS',
354
+ 'ES',
355
+ 'MD',
356
+ ]);
357
+ /** 텍스트에서 티켓 키(PROJ-123 꼴)를 나온 순서대로 뽑는다 */
358
+ export function extractTicketKeys(text) {
359
+ const out = [];
360
+ for (const m of text.matchAll(TICKET_RE)) {
361
+ if (NON_TICKET_PREFIXES.has(m[1]))
362
+ continue;
363
+ const key = `${m[1]}-${m[2]}`;
364
+ if (!out.includes(key))
365
+ out.push(key);
366
+ }
367
+ return out;
368
+ }
369
+ // 연관 PR 본문이 리뷰 절차를 바꾸려 드는 흔한 꼴. 걸리면 알리기만 하고 내용은 따르지 않는다
370
+ const INSTRUCTION_LIKE_RE = [
371
+ /ignore (all |the )?(previous|prior|above) (instructions|rules)/i,
372
+ /disregard (the )?(previous|prior|above|system)/i,
373
+ /(approve|merge) (this|the) pr (without|immediately)/i,
374
+ /system prompt/i,
375
+ /(이전|앞의|위의) (지시|규칙|명령)(을|를)? ?무시/,
376
+ /(리뷰|검사|규칙)(을|를)? ?(건너뛰|생략|끄)/,
377
+ /바로 (승인|머지)/,
378
+ ];
379
+ function scanInstructionLikeText(detail) {
380
+ const text = `${detail.title}\n${detail.body}`;
381
+ if (!INSTRUCTION_LIKE_RE.some((re) => re.test(text)))
382
+ return null;
383
+ return {
384
+ repo: detail.repo,
385
+ number: detail.number,
386
+ kind: 'instructionLikeText',
387
+ detail: '연관 PR 본문에 지시로 보이는 문구가 있다. 자료로만 읽고 따르지 않았다',
388
+ };
389
+ }
390
+ /**
391
+ * 후보를 세우는 순서. 거르지 않고 순서만 바꾼다 — 신호가 약해도 진짜 연관인 PR이 있어서다.
392
+ * 여러 레포에 같은 작업을 퍼뜨린 PR은 티켓도 경로도 다를 수 있다
393
+ */
394
+ function stateGroup(state) {
395
+ return state === 'open' ? 0 : state === 'merged' ? 1 : 2;
396
+ }
397
+ function rankOf(signals) {
398
+ if (signals.bodyLink)
399
+ return 0;
400
+ if (signals.sharedTicketKeys.length > 0)
401
+ return 1;
402
+ if (signals.touchedTargets.length > 0)
403
+ return 2;
404
+ return 3;
405
+ }
406
+ function baseSignals(current, detail, ticketKeys) {
407
+ const theirKeys = extractTicketKeys([detail.title, detail.body, detail.branch].join('\n'));
408
+ return {
409
+ bodyLink: false,
410
+ sharedTicketKeys: ticketKeys.filter((k) => theirKeys.includes(k)),
411
+ sameAuthor: sameLogin(current.author, detail.author),
412
+ sameBranch: current.branch !== '' && current.branch === detail.branch,
413
+ touchedTargets: [],
414
+ };
415
+ }
416
+ function toCandidate(detail, foundBy, signals, confirmation) {
417
+ const evidence = [];
418
+ if (signals.bodyLink)
419
+ evidence.push('이번 PR 본문이 링크한다');
420
+ if (signals.sharedTicketKeys.length > 0)
421
+ evidence.push(`같은 티켓 키 ${signals.sharedTicketKeys.join(', ')}`);
422
+ if (signals.sameAuthor)
423
+ evidence.push('같은 작성자');
424
+ if (signals.sameBranch)
425
+ evidence.push(`같은 브랜치명 ${detail.branch}`);
426
+ if (signals.touchedTargets.length > 0)
427
+ evidence.push(`참조 대상을 건드린다: ${signals.touchedTargets.join(', ')}`);
428
+ if (detail.state === 'merged')
429
+ evidence.push(`${detail.baseBranch}에 머지됨`);
430
+ return {
431
+ repo: detail.repo,
432
+ number: detail.number,
433
+ headSha: detail.headSha,
434
+ state: detail.state,
435
+ ...(detail.state === 'merged' ? { mergedBranch: detail.baseBranch } : {}),
436
+ foundBy,
437
+ confirmation,
438
+ url: detail.url,
439
+ author: detail.author,
440
+ branch: detail.branch,
441
+ evidence,
442
+ signals,
443
+ };
444
+ }
445
+ // 머지된 PR만 기간을 본다. 열린 PR은 기간 제한이 없고 닫힌 PR은 본문 링크로만 들어온다
446
+ function isWithinWindow(detail, mergedSinceMs) {
447
+ if (detail.state === 'open')
448
+ return true;
449
+ if (detail.state !== 'merged' || !detail.mergedAt)
450
+ return false;
451
+ const merged = Date.parse(detail.mergedAt);
452
+ return Number.isFinite(merged) && merged >= mergedSinceMs;
453
+ }
454
+ function runJson(gh, args) {
455
+ let raw;
456
+ try {
457
+ raw = gh(args);
458
+ }
459
+ catch (e) {
460
+ const detail = failureText(e);
461
+ return { ok: false, reason: classifyGhFailure(detail) ?? 'unknown', detail };
462
+ }
463
+ try {
464
+ return { ok: true, value: JSON.parse(raw) };
465
+ }
466
+ catch {
467
+ return { ok: false, reason: 'unparsable', detail: 'gh 응답이 JSON이 아니다' };
468
+ }
469
+ }
470
+ function failureText(e) {
471
+ if (typeof e !== 'object' || e === null)
472
+ return String(e);
473
+ const { stderr, message } = e;
474
+ return [stderr, message]
475
+ .filter((v) => v !== undefined && v !== null)
476
+ .map(String)
477
+ .join('\n');
478
+ }
479
+ function toPrDetail(fallbackRepo, raw) {
480
+ if (!isRecord(raw))
481
+ return null;
482
+ const number = raw.number;
483
+ const headSha = raw.headRefOid;
484
+ if (typeof number !== 'number' || typeof headSha !== 'string')
485
+ return null;
486
+ const state = normalizeState(raw.state);
487
+ if (!state)
488
+ return null;
489
+ const author = isRecord(raw.author) && typeof raw.author.login === 'string' ? raw.author.login : '';
490
+ return {
491
+ repo: fallbackRepo,
492
+ number,
493
+ state,
494
+ headSha,
495
+ branch: str(raw.headRefName),
496
+ baseBranch: str(raw.baseRefName),
497
+ author,
498
+ title: str(raw.title),
499
+ body: str(raw.body),
500
+ url: str(raw.url),
501
+ createdAt: typeof raw.createdAt === 'string' ? raw.createdAt : null,
502
+ mergedAt: typeof raw.mergedAt === 'string' && raw.mergedAt !== '' ? raw.mergedAt : null,
503
+ files: fileList(raw),
504
+ };
505
+ }
506
+ function normalizeState(v) {
507
+ if (typeof v !== 'string')
508
+ return null;
509
+ const s = v.toLowerCase();
510
+ return s === 'open' || s === 'merged' || s === 'closed' ? s : null;
511
+ }
512
+ function parseSearchHits(value) {
513
+ if (!Array.isArray(value))
514
+ return [];
515
+ return value.flatMap((h) => {
516
+ if (!isRecord(h) || typeof h.number !== 'number' || !isRecord(h.repository))
517
+ return [];
518
+ const repo = h.repository.nameWithOwner;
519
+ const createdAt = typeof h.createdAt === 'string' ? h.createdAt : null;
520
+ return typeof repo === 'string' ? [{ repo, number: h.number, createdAt }] : [];
521
+ });
522
+ }
523
+ function dedupeHits(hits) {
524
+ const seen = new Set();
525
+ return hits.filter((h) => {
526
+ const key = `${h.repo.toLowerCase()}#${h.number}`;
527
+ if (seen.has(key))
528
+ return false;
529
+ seen.add(key);
530
+ return true;
531
+ });
532
+ }
533
+ function fileList(raw) {
534
+ if (!isRecord(raw) || !Array.isArray(raw.files))
535
+ return [];
536
+ return raw.files.flatMap((f) => (isRecord(f) && typeof f.path === 'string' ? [f.path] : []));
537
+ }
538
+ function repoKey(r) {
539
+ return (typeof r === 'string' ? r : `${r.owner}/${r.name}`).toLowerCase();
540
+ }
541
+ function sameLogin(a, b) {
542
+ return a !== '' && a.toLowerCase() === b.toLowerCase();
543
+ }
544
+ function isoDate(d) {
545
+ return d.toISOString().slice(0, 10);
546
+ }
547
+ function str(v) {
548
+ return typeof v === 'string' ? v : '';
549
+ }
550
+ function isRecord(v) {
551
+ return typeof v === 'object' && v !== null && !Array.isArray(v);
552
+ }
553
+ //# sourceMappingURL=related-pr.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"related-pr.js","sourceRoot":"","sources":["../../../src/harness-review/related-pr.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,69 @@
1
+ import { isHarnessPath } from './identifiers.js';
2
+ import type { DetectionCache, Identifier, RelatedRepo } from './types.js';
3
+ export { isHarnessPath };
4
+ export declare const DETECTION_CACHE_PATH: string;
5
+ export declare const DEFAULT_CACHE_TTL_MS: number;
6
+ export interface RepoRef {
7
+ owner: string;
8
+ name: string;
9
+ }
10
+ /** gh CLI 실행부. 테스트에서 가짜로 바꿔 네트워크를 타지 않게 한다. */
11
+ export type GhRunner = (args: string[]) => string;
12
+ export declare const defaultGhRunner: GhRunner;
13
+ /** MCP 패키지 이름 → 그 package.json의 repository 필드 값. 못 찾으면 null. */
14
+ export type PackageRepositoryResolver = (packageName: string, repoRoot: string) => unknown;
15
+ export declare const defaultPackageRepositoryResolver: PackageRepositoryResolver;
16
+ export interface DetectRelatedReposOptions {
17
+ /** gestalt.json에서 온 추가 레포. "owner/name" 또는 GitHub URL. 추가만 받는다. */
18
+ extraRepos?: ReadonlyArray<string | RepoRef>;
19
+ /** 캐시가 없거나 낡았을 때만 부른다. 결과는 캐시에 함께 저장된다. */
20
+ extractIdentifiers?: (ctx: {
21
+ repoRoot: string;
22
+ harnessDocs: string[];
23
+ }) => Identifier[];
24
+ gh?: GhRunner;
25
+ resolvePackageRepository?: PackageRepositoryResolver;
26
+ now?: Date;
27
+ ttlMs?: number;
28
+ /** true면 캐시를 무시하고 다시 탐지한다. */
29
+ refresh?: boolean;
30
+ }
31
+ export type RelatedRepoDetection = {
32
+ noGitHubRemote: true;
33
+ reason: string;
34
+ } | {
35
+ noGitHubRemote: false;
36
+ org: string;
37
+ self: RepoRef;
38
+ relatedRepos: RelatedRepo[];
39
+ identifiers: Identifier[];
40
+ fromCache: boolean;
41
+ cache: DetectionCache;
42
+ };
43
+ /**
44
+ * 현재 레포의 관련 레포를 찾는다. origin의 owner를 조직으로 보고 하네스 문서의
45
+ * 순방향 언급에서 같은 조직 레포를 뽑는다. 결과는 .gestalt/harness-refs-cache.json에
46
+ * 저장하고 하네스 문서 해시가 바뀌거나 expiresAt이 지나면 다시 탐지한다.
47
+ */
48
+ export declare function detectRelatedRepos(repoRoot: string, opts?: DetectRelatedReposOptions): RelatedRepoDetection;
49
+ export declare function parseGitHubRepo(input: string): RepoRef | null;
50
+ export declare function readOriginRepo(repoRoot: string): RepoRef | null;
51
+ interface DetectionInputs {
52
+ harnessDocs: string[];
53
+ mcpPackageManifests: string[];
54
+ }
55
+ export declare function computeHarnessDocsHash(repoRoot: string, inputs?: DetectionInputs): string;
56
+ /** 본문에서 조직 레포 언급을 뽑는다. owner/repo 표기, GitHub URL, gh repo clone 대상. */
57
+ export declare function extractRepoMentions(text: string, org: string): RepoRef[];
58
+ /** .mcp.json 서버 정의에서 npx류로 띄우는 패키지 이름을 뽑는다. 버전 핀은 뗀다. */
59
+ export declare function extractMcpPackages(mcpJson: unknown): string[];
60
+ export declare function findForwardMentions(repoRoot: string, org: string, inputs?: DetectionInputs, opts?: {
61
+ resolvePackageRepository?: PackageRepositoryResolver;
62
+ }): RepoRef[];
63
+ export interface StoredDetectionCache extends DetectionCache {
64
+ selfRepo: string;
65
+ }
66
+ export declare function isCacheFresh(cache: DetectionCache, harnessDocsHash: string, now: Date): boolean;
67
+ export declare function readDetectionCache(repoRoot: string): StoredDetectionCache | null;
68
+ export declare function writeDetectionCache(repoRoot: string, cache: DetectionCache, self: RepoRef): void;
69
+ //# sourceMappingURL=related-repos.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"related-repos.d.ts","sourceRoot":"","sources":["../../../src/harness-review/related-repos.ts"],"names":[],"mappings":"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"}