@tienne/gestalt 0.83.1 → 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 (153) 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/ai-tell-quick-rules.md +4 -1
  7. package/dist/plugin/role-agents/_shared/references/comment-rules.md +3 -3
  8. package/dist/plugin/role-agents/_shared/references/rule-path-walk.md +1 -1
  9. package/dist/plugin/role-agents/_shared/references/style-guide.md +1 -0
  10. package/dist/plugin/role-agents/_shared/references/truncation-rules.md +1 -1
  11. package/dist/plugin/role-agents/suggestion-verifier/AGENT.md +7 -1
  12. package/dist/plugin/skills/_shared/proactive-routing.md +1 -0
  13. package/dist/plugin/skills/_shared/rule-sources.md +1 -1
  14. package/dist/plugin/skills/agent/SKILL.md +2 -0
  15. package/dist/plugin/skills/review/SKILL.md +343 -7
  16. package/dist/plugin/skills/review-loop/SKILL.md +124 -2
  17. package/dist/plugin/skills/ship/SKILL.md +40 -1
  18. package/dist/schemas/gestalt.schema.json +12 -0
  19. package/dist/src/cli/commands/approve-gate.d.ts +24 -0
  20. package/dist/src/cli/commands/approve-gate.d.ts.map +1 -0
  21. package/dist/src/cli/commands/approve-gate.js +90 -0
  22. package/dist/src/cli/commands/approve-gate.js.map +1 -0
  23. package/dist/src/cli/commands/harness-refs-cross-pr.d.ts +218 -0
  24. package/dist/src/cli/commands/harness-refs-cross-pr.d.ts.map +1 -0
  25. package/dist/src/cli/commands/harness-refs-cross-pr.js +934 -0
  26. package/dist/src/cli/commands/harness-refs-cross-pr.js.map +1 -0
  27. package/dist/src/cli/commands/harness-refs.d.ts +19 -0
  28. package/dist/src/cli/commands/harness-refs.d.ts.map +1 -0
  29. package/dist/src/cli/commands/harness-refs.js +51 -0
  30. package/dist/src/cli/commands/harness-refs.js.map +1 -0
  31. package/dist/src/cli/index.d.ts.map +1 -1
  32. package/dist/src/cli/index.js +97 -0
  33. package/dist/src/cli/index.js.map +1 -1
  34. package/dist/src/core/config.d.ts +3 -0
  35. package/dist/src/core/config.d.ts.map +1 -1
  36. package/dist/src/core/config.js +23 -0
  37. package/dist/src/core/config.js.map +1 -1
  38. package/dist/src/core/types.d.ts +2 -0
  39. package/dist/src/core/types.d.ts.map +1 -1
  40. package/dist/src/harness-review/approve-gate.d.ts +53 -0
  41. package/dist/src/harness-review/approve-gate.d.ts.map +1 -0
  42. package/dist/src/harness-review/approve-gate.js +148 -0
  43. package/dist/src/harness-review/approve-gate.js.map +1 -0
  44. package/dist/src/harness-review/backward-search.d.ts +50 -0
  45. package/dist/src/harness-review/backward-search.d.ts.map +1 -0
  46. package/dist/src/harness-review/backward-search.js +183 -0
  47. package/dist/src/harness-review/backward-search.js.map +1 -0
  48. package/dist/src/harness-review/collect.d.ts +75 -0
  49. package/dist/src/harness-review/collect.d.ts.map +1 -0
  50. package/dist/src/harness-review/collect.js +253 -0
  51. package/dist/src/harness-review/collect.js.map +1 -0
  52. package/dist/src/harness-review/copy-drift.d.ts +83 -0
  53. package/dist/src/harness-review/copy-drift.d.ts.map +1 -0
  54. package/dist/src/harness-review/copy-drift.js +477 -0
  55. package/dist/src/harness-review/copy-drift.js.map +1 -0
  56. package/dist/src/harness-review/follow-up-marker.d.ts +29 -0
  57. package/dist/src/harness-review/follow-up-marker.d.ts.map +1 -0
  58. package/dist/src/harness-review/follow-up-marker.js +143 -0
  59. package/dist/src/harness-review/follow-up-marker.js.map +1 -0
  60. package/dist/src/harness-review/forward-search.d.ts +30 -0
  61. package/dist/src/harness-review/forward-search.d.ts.map +1 -0
  62. package/dist/src/harness-review/forward-search.js +381 -0
  63. package/dist/src/harness-review/forward-search.js.map +1 -0
  64. package/dist/src/harness-review/github-backend.d.ts +42 -0
  65. package/dist/src/harness-review/github-backend.d.ts.map +1 -0
  66. package/dist/src/harness-review/github-backend.js +130 -0
  67. package/dist/src/harness-review/github-backend.js.map +1 -0
  68. package/dist/src/harness-review/github-code-search.d.ts +46 -0
  69. package/dist/src/harness-review/github-code-search.d.ts.map +1 -0
  70. package/dist/src/harness-review/github-code-search.js +94 -0
  71. package/dist/src/harness-review/github-code-search.js.map +1 -0
  72. package/dist/src/harness-review/identifiers.d.ts +75 -0
  73. package/dist/src/harness-review/identifiers.d.ts.map +1 -0
  74. package/dist/src/harness-review/identifiers.js +678 -0
  75. package/dist/src/harness-review/identifiers.js.map +1 -0
  76. package/dist/src/harness-review/placeholders.d.ts +44 -0
  77. package/dist/src/harness-review/placeholders.d.ts.map +1 -0
  78. package/dist/src/harness-review/placeholders.js +138 -0
  79. package/dist/src/harness-review/placeholders.js.map +1 -0
  80. package/dist/src/harness-review/related-pr.d.ts +133 -0
  81. package/dist/src/harness-review/related-pr.d.ts.map +1 -0
  82. package/dist/src/harness-review/related-pr.js +553 -0
  83. package/dist/src/harness-review/related-pr.js.map +1 -0
  84. package/dist/src/harness-review/related-repos.d.ts +69 -0
  85. package/dist/src/harness-review/related-repos.d.ts.map +1 -0
  86. package/dist/src/harness-review/related-repos.js +430 -0
  87. package/dist/src/harness-review/related-repos.js.map +1 -0
  88. package/dist/src/harness-review/remote-url.d.ts +8 -0
  89. package/dist/src/harness-review/remote-url.d.ts.map +1 -0
  90. package/dist/src/harness-review/remote-url.js +35 -0
  91. package/dist/src/harness-review/remote-url.js.map +1 -0
  92. package/dist/src/harness-review/replay.d.ts +163 -0
  93. package/dist/src/harness-review/replay.d.ts.map +1 -0
  94. package/dist/src/harness-review/replay.js +575 -0
  95. package/dist/src/harness-review/replay.js.map +1 -0
  96. package/dist/src/harness-review/rule-id-lists.d.ts +38 -0
  97. package/dist/src/harness-review/rule-id-lists.d.ts.map +1 -0
  98. package/dist/src/harness-review/rule-id-lists.js +189 -0
  99. package/dist/src/harness-review/rule-id-lists.js.map +1 -0
  100. package/dist/src/harness-review/search-backend.d.ts +59 -0
  101. package/dist/src/harness-review/search-backend.d.ts.map +1 -0
  102. package/dist/src/harness-review/search-backend.js +178 -0
  103. package/dist/src/harness-review/search-backend.js.map +1 -0
  104. package/dist/src/harness-review/self-contamination.d.ts +83 -0
  105. package/dist/src/harness-review/self-contamination.d.ts.map +1 -0
  106. package/dist/src/harness-review/self-contamination.js +560 -0
  107. package/dist/src/harness-review/self-contamination.js.map +1 -0
  108. package/dist/src/harness-review/self-references.d.ts +20 -0
  109. package/dist/src/harness-review/self-references.d.ts.map +1 -0
  110. package/dist/src/harness-review/self-references.js +173 -0
  111. package/dist/src/harness-review/self-references.js.map +1 -0
  112. package/dist/src/harness-review/three-state.d.ts +79 -0
  113. package/dist/src/harness-review/three-state.d.ts.map +1 -0
  114. package/dist/src/harness-review/three-state.js +238 -0
  115. package/dist/src/harness-review/three-state.js.map +1 -0
  116. package/dist/src/harness-review/types.d.ts +148 -0
  117. package/dist/src/harness-review/types.d.ts.map +1 -0
  118. package/dist/src/harness-review/types.js +74 -0
  119. package/dist/src/harness-review/types.js.map +1 -0
  120. package/dist/src/humanize/detectors.d.ts.map +1 -1
  121. package/dist/src/humanize/detectors.js +3 -1
  122. package/dist/src/humanize/detectors.js.map +1 -1
  123. package/dist/src/index.d.ts +1 -0
  124. package/dist/src/index.d.ts.map +1 -1
  125. package/dist/src/index.js +1 -0
  126. package/dist/src/index.js.map +1 -1
  127. package/dist/src/mcp/tools/review-passthrough.js +7 -1
  128. package/dist/src/mcp/tools/review-passthrough.js.map +1 -1
  129. package/dist/src/review/agent-matcher.d.ts +16 -1
  130. package/dist/src/review/agent-matcher.d.ts.map +1 -1
  131. package/dist/src/review/agent-matcher.js +31 -4
  132. package/dist/src/review/agent-matcher.js.map +1 -1
  133. package/dist/src/review/passthrough-engine.d.ts.map +1 -1
  134. package/dist/src/review/passthrough-engine.js +40 -2
  135. package/dist/src/review/passthrough-engine.js.map +1 -1
  136. package/package.json +1 -1
  137. package/plugin/.codex-plugin/plugin.json +1 -1
  138. package/plugin/.mcp.json +1 -1
  139. package/plugin/mcp.json +1 -1
  140. package/plugin/review-agents/harness-reviewer/AGENT.md +211 -0
  141. package/plugin/role-agents/_shared/references/ai-tell-quick-rules.md +4 -1
  142. package/plugin/role-agents/_shared/references/comment-rules.md +3 -3
  143. package/plugin/role-agents/_shared/references/rule-path-walk.md +1 -1
  144. package/plugin/role-agents/_shared/references/style-guide.md +1 -0
  145. package/plugin/role-agents/_shared/references/truncation-rules.md +1 -1
  146. package/plugin/role-agents/suggestion-verifier/AGENT.md +7 -1
  147. package/plugin/skills/_shared/proactive-routing.md +1 -0
  148. package/plugin/skills/_shared/rule-sources.md +1 -1
  149. package/plugin/skills/agent/SKILL.md +2 -0
  150. package/plugin/skills/review/SKILL.md +343 -7
  151. package/plugin/skills/review-loop/SKILL.md +124 -2
  152. package/plugin/skills/ship/SKILL.md +40 -1
  153. package/schemas/gestalt.schema.json +12 -0
@@ -0,0 +1,189 @@
1
+ /**
2
+ * 코드 속 룰 ID 목록에서 빠진 ID를 찾는다.
3
+ *
4
+ * 룰북에서 룰을 새로 정의하거나 정의 줄을 고치면, 그 룰을 골라 담는 코드 목록(금지 목록 펼칠
5
+ * 룰, 자가점검 항목 같은 것)도 함께 봐야 할 때가 있다. 이런 누락은 남아 있는 문자열이 아니라
6
+ * 빠진 문자열이라 금지어 grep으로는 원리상 안 잡힌다. 그래서 같은 룰북의 ID를 여럿 나열한
7
+ * 목록을 찾고 바뀐 ID가 그 목록에 없으면 후보로 낸다.
8
+ *
9
+ * 목록은 대개 룰 일부만 일부러 고른 것이라 빠진 게 곧 결함은 아니다. 후보는 전부
10
+ * needsLlmJudgment로 넘기고 harness-reviewer가 목록 위 설명을 읽고 판정한다.
11
+ */
12
+ import { execFileSync } from 'node:child_process';
13
+ import { ruleIdOfDefinitionLine, parseUnifiedDiff } from './identifiers.js';
14
+ /** 이보다 적게 나열한 자리는 목록이 아니라 설명 속 언급으로 본다 */
15
+ export const MIN_LIST_IDS = 3;
16
+ /** 룰 ID 사이가 이 줄 수보다 벌어지면 다른 목록으로 나눈다 */
17
+ export const MAX_LIST_GAP_LINES = 2;
18
+ export const MAX_LIST_CONTEXT_LINES = 8;
19
+ const CODE_EXT_RE = /\.(?:[cm]?[jt]sx?|json|ya?ml|py)$/;
20
+ // 테스트와 fixture는 룰 일부만 골라 검사하는 게 정상이라 빠진 ID가 결함이 아니다
21
+ const TEST_PATH_RE = /(?:^|\/)(?:tests?|__tests__|fixtures?)\/|\.(?:test|spec)\.[^/]+$/;
22
+ const QUOTED_RULE_ID_RE = /(['"`])([A-Z]{1,2}-\d{1,3})\1/g;
23
+ function isMarkdown(path) {
24
+ return /\.mdx?$/i.test(path);
25
+ }
26
+ /** diff의 추가 줄에서 정의된 룰 ID를 룰북 경로별로 모은다 */
27
+ export function changedRuleIds(diff) {
28
+ const out = new Map();
29
+ for (const f of parseUnifiedDiff(diff)) {
30
+ const path = f.newPath;
31
+ if (!path || !isMarkdown(path))
32
+ continue;
33
+ for (const h of f.hunks) {
34
+ for (const l of h.lines) {
35
+ if (l.type !== 'added')
36
+ continue;
37
+ const id = ruleIdOfDefinitionLine(l.text);
38
+ if (!id)
39
+ continue;
40
+ if (!out.has(path))
41
+ out.set(path, new Set());
42
+ out.get(path).add(id);
43
+ }
44
+ }
45
+ }
46
+ return out;
47
+ }
48
+ function definedRuleIds(markdown) {
49
+ const ids = new Set();
50
+ let inFence = false;
51
+ for (const line of markdown.split('\n')) {
52
+ if (/^\s*(```|~~~)/.test(line)) {
53
+ inFence = !inFence;
54
+ continue;
55
+ }
56
+ if (inFence)
57
+ continue;
58
+ const id = ruleIdOfDefinitionLine(line);
59
+ if (id)
60
+ ids.add(id);
61
+ }
62
+ return ids;
63
+ }
64
+ /** 따옴표로 감싼 룰 ID가 줄 간격 MAX_LIST_GAP_LINES 안에서 이어지는 구간을 목록 하나로 묶는다 */
65
+ export function findRuleIdLists(file, content) {
66
+ const lists = [];
67
+ let current = null;
68
+ const lines = content.split('\n');
69
+ for (let i = 0; i < lines.length; i++) {
70
+ const ids = [...lines[i].matchAll(QUOTED_RULE_ID_RE)].map((m) => m[2]);
71
+ if (ids.length === 0)
72
+ continue;
73
+ const lineNo = i + 1;
74
+ if (!current || lineNo - current.endLine > MAX_LIST_GAP_LINES) {
75
+ current = { file, startLine: lineNo, endLine: lineNo, ids: new Set(), entries: [] };
76
+ lists.push(current);
77
+ }
78
+ current.endLine = lineNo;
79
+ for (const id of ids) {
80
+ current.ids.add(id);
81
+ current.entries.push({ id, line: lineNo });
82
+ }
83
+ }
84
+ return lists.filter((l) => l.ids.size >= MIN_LIST_IDS);
85
+ }
86
+ function compareRuleIds(a, b) {
87
+ const [pa, na] = a.split('-');
88
+ const [pb, nb] = b.split('-');
89
+ return pa === pb ? Number(na) - Number(nb) : pa < pb ? -1 : 1;
90
+ }
91
+ /**
92
+ * 빠진 ID가 들어갈 줄. ID 순으로 정렬된 목록이면 순서상 자리를, 아니면 목록 끝 다음 줄을 준다.
93
+ * 리뷰어가 코멘트를 달 자리이고 목록 첫 줄을 주면 긴 목록에서 어디를 보라는 건지 흐려진다.
94
+ */
95
+ export function insertionLine(list, id) {
96
+ const { entries } = list;
97
+ const sorted = entries.every((e, i) => i === 0 || compareRuleIds(entries[i - 1].id, e.id) <= 0);
98
+ if (!sorted)
99
+ return list.endLine + 1;
100
+ const before = entries.filter((e) => compareRuleIds(e.id, id) < 0);
101
+ return before.length === 0 ? list.startLine : before[before.length - 1].line + 1;
102
+ }
103
+ function contextOf(content, list) {
104
+ const lines = content.split('\n').slice(list.startLine - 1, list.endLine);
105
+ if (lines.length <= MAX_LIST_CONTEXT_LINES)
106
+ return lines;
107
+ return [...lines.slice(0, MAX_LIST_CONTEXT_LINES - 1), `… (${list.endLine}줄까지)`];
108
+ }
109
+ export function findRuleIdListGaps(input) {
110
+ const changed = changedRuleIds(input.diff);
111
+ if (changed.size === 0)
112
+ return [];
113
+ const rulebooks = [...changed.entries()].flatMap(([path, ids]) => {
114
+ const content = input.readFile(path);
115
+ return content === undefined ? [] : [{ path, ids, defined: definedRuleIds(content) }];
116
+ });
117
+ const candidates = [];
118
+ for (const file of input.files) {
119
+ if (!CODE_EXT_RE.test(file) || TEST_PATH_RE.test(file))
120
+ continue;
121
+ const content = input.readFile(file);
122
+ if (content === undefined)
123
+ continue;
124
+ const quotedHere = new Set([...content.matchAll(QUOTED_RULE_ID_RE)].map((m) => m[2]));
125
+ for (const list of findRuleIdLists(file, content)) {
126
+ for (const book of rulebooks) {
127
+ // 다른 룰북의 ID 목록은 같은 접두 글자를 써도 이 룰과 무관하다
128
+ const shared = [...list.ids].filter((id) => book.defined.has(id)).length;
129
+ if (shared < MIN_LIST_IDS)
130
+ continue;
131
+ for (const id of book.ids) {
132
+ // 한 파일에서 여러 줄로 흩어진 목록(탐지기 등록부 같은 것)은 이미 그 ID를 다른 구간에 들고 있다
133
+ if (quotedHere.has(id))
134
+ continue;
135
+ candidates.push({
136
+ kind: 'ruleIdListGap',
137
+ sourceFile: file,
138
+ sourceLine: insertionLine(list, id),
139
+ targetRepo: input.repoName ?? '.',
140
+ targetPath: book.path,
141
+ matchedText: id,
142
+ contextLines: contextOf(content, list),
143
+ needsLlmJudgment: true,
144
+ });
145
+ }
146
+ }
147
+ }
148
+ }
149
+ return candidates;
150
+ }
151
+ /** base와 head 사이 diff로 검사한다. 파일은 head 트리에서 읽는다 */
152
+ export function findRuleIdListGapsFromGit(repoRoot, base, head, repoName) {
153
+ const git = (...args) => execFileSync('git', ['-c', 'core.quotepath=false', ...args], {
154
+ cwd: repoRoot,
155
+ encoding: 'utf-8',
156
+ maxBuffer: 64 * 1024 * 1024,
157
+ stdio: ['ignore', 'pipe', 'ignore'],
158
+ });
159
+ const diff = git('diff', '--no-color', base, head);
160
+ if (changedRuleIds(diff).size === 0)
161
+ return [];
162
+ // 파일마다 git show를 부르면 느려서 따옴표 친 룰 ID가 있는 파일만 먼저 추린다
163
+ let grepped = '';
164
+ try {
165
+ grepped = git('grep', '-l', '-E', `['"\`][A-Z]{1,2}-[0-9]{1,3}['"\`]`, head);
166
+ }
167
+ catch {
168
+ // 걸린 파일이 없으면 git grep이 1로 끝난다
169
+ }
170
+ const prefix = `${head}:`;
171
+ const files = grepped
172
+ .split('\n')
173
+ .filter(Boolean)
174
+ .map((l) => (l.startsWith(prefix) ? l.slice(prefix.length) : l));
175
+ return findRuleIdListGaps({
176
+ diff,
177
+ files,
178
+ readFile: (p) => {
179
+ try {
180
+ return git('show', `${head}:${p}`);
181
+ }
182
+ catch {
183
+ return undefined;
184
+ }
185
+ },
186
+ repoName,
187
+ });
188
+ }
189
+ //# sourceMappingURL=rule-id-lists.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rule-id-lists.js","sourceRoot":"","sources":["../../../src/harness-review/rule-id-lists.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ import type { SearchBackend } from './types.js';
2
+ export interface SearchHit {
3
+ /** owner/name 표기 */
4
+ repo: string;
5
+ /** 레포 루트 기준, 항상 슬래시 구분자 */
6
+ path: string;
7
+ /** 1부터 시작 */
8
+ line: number;
9
+ /** 매치된 줄 전체. MAX_HIT_TEXT_LENGTH에서 자른다 */
10
+ text: string;
11
+ }
12
+ export interface SkippedRepo {
13
+ repo: string;
14
+ reason: string;
15
+ }
16
+ export interface SearchResult {
17
+ hits: SearchHit[];
18
+ /** 검색하지 못한 레포. 결과가 비었다고 "참조 없음"으로 읽지 않게 따로 싣는다 */
19
+ skipped: SkippedRepo[];
20
+ }
21
+ export interface SearchOptions {
22
+ /** 레포당 상한. 넘으면 앞쪽만 남긴다 */
23
+ maxHitsPerRepo?: number;
24
+ }
25
+ /** 식별자 문자열을 관련 레포에서 찾는 역방향 검색. 구현체를 바꿔 끼워도 호출부는 그대로다 */
26
+ export interface CodeSearchBackend extends SearchBackend {
27
+ /** identifier는 정규식이 아닌 고정 문자열로 찾는다. repos는 owner/name 목록 */
28
+ search(identifier: string, repos: string[], opts?: SearchOptions): Promise<SearchResult>;
29
+ /**
30
+ * 그 레포에 이 경로의 파일이 있는지. `/`가 없으면 같은 이름의 파일이 어디든 있는지 본다.
31
+ * 모르면 undefined다. 파일 목록을 싸게 못 얻는 백엔드는 구현하지 않는다
32
+ */
33
+ hasFile?(repo: string, path: string): boolean | undefined;
34
+ }
35
+ export declare const DEFAULT_MAX_HITS_PER_REPO = 100;
36
+ export declare const MAX_HIT_TEXT_LENGTH = 300;
37
+ export interface LocalRepoSource {
38
+ /** owner/name */
39
+ repo: string;
40
+ /** clone이나 워크트리 디렉토리 */
41
+ dir: string;
42
+ /** 지정하면 워킹트리 대신 이 ref(브랜치, SHA)의 트리를 본다 */
43
+ ref?: string;
44
+ }
45
+ export declare class LocalCloneBackend implements CodeSearchBackend {
46
+ readonly kind: "localClone";
47
+ readonly capabilities: {
48
+ fixedStringSearch: boolean;
49
+ refSnapshot: boolean;
50
+ rateLimited: boolean;
51
+ };
52
+ private readonly sources;
53
+ private readonly fileLists;
54
+ constructor(sources: LocalRepoSource[]);
55
+ hasFile(repo: string, path: string): boolean | undefined;
56
+ private fileList;
57
+ search(identifier: string, repos: string[], opts?: SearchOptions): Promise<SearchResult>;
58
+ }
59
+ //# sourceMappingURL=search-backend.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-backend.d.ts","sourceRoot":"","sources":["../../../src/harness-review/search-backend.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,MAAM,WAAW,SAAS;IACxB,oBAAoB;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,2BAA2B;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,aAAa;IACb,IAAI,EAAE,MAAM,CAAC;IACb,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,SAAS,EAAE,CAAC;IAClB,kDAAkD;IAClD,OAAO,EAAE,WAAW,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAC5B,0BAA0B;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,wDAAwD;AACxD,MAAM,WAAW,iBAAkB,SAAQ,aAAa;IACtD,4DAA4D;IAC5D,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,IAAI,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IACzF;;;OAGG;IACH,OAAO,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;CAC3D;AAED,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAC7C,eAAO,MAAM,mBAAmB,MAAM,CAAC;AAKvC,MAAM,WAAW,eAAe;IAC9B,iBAAiB;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,wBAAwB;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,2CAA2C;IAC3C,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,qBAAa,iBAAkB,YAAW,iBAAiB;IACzD,QAAQ,CAAC,IAAI,EAAG,YAAY,CAAU;IACtC,QAAQ,CAAC,YAAY;;;;MAInB;IAEF,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsC;IAE9D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAwE;gBAEtF,OAAO,EAAE,eAAe,EAAE;IAItC,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS;IAYxD,OAAO,CAAC,QAAQ;IAqBV,MAAM,CACV,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,MAAM,EAAE,EACf,IAAI,GAAE,aAAkB,GACvB,OAAO,CAAC,YAAY,CAAC;CAsBzB"}
@@ -0,0 +1,178 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { readFileSync, readdirSync, realpathSync } from 'node:fs';
3
+ import { join, relative, sep } from 'node:path';
4
+ export const DEFAULT_MAX_HITS_PER_REPO = 100;
5
+ export const MAX_HIT_TEXT_LENGTH = 300;
6
+ const MAX_FILE_BYTES = 1_000_000;
7
+ const SKIPPED_DIRS = new Set(['.git', 'node_modules']);
8
+ export class LocalCloneBackend {
9
+ kind = 'localClone';
10
+ capabilities = {
11
+ fixedStringSearch: true,
12
+ refSnapshot: true,
13
+ rateLimited: false,
14
+ };
15
+ sources = new Map();
16
+ fileLists = new Map();
17
+ constructor(sources) {
18
+ for (const s of sources)
19
+ this.sources.set(s.repo, s);
20
+ }
21
+ hasFile(repo, path) {
22
+ const list = this.fileList(repo);
23
+ if (!list)
24
+ return undefined;
25
+ const clean = path.replace(/^\.\//, '').replace(/\/$/, '');
26
+ if (!clean.includes('/'))
27
+ return list.names.has(clean);
28
+ // 모노레포면 같은 경로가 한 단계 안쪽(plugins/*/.claude-plugin/plugin.json)에 있다
29
+ return (list.paths.has(clean) ||
30
+ [...list.paths].some((p) => p.startsWith(`${clean}/`) || p.endsWith(`/${clean}`)));
31
+ }
32
+ fileList(repo) {
33
+ if (this.fileLists.has(repo))
34
+ return this.fileLists.get(repo);
35
+ const source = this.sources.get(repo);
36
+ let list = null;
37
+ if (source && (source.ref || isGitRoot(source.dir))) {
38
+ const args = source.ref
39
+ ? ['-C', source.dir, 'ls-tree', '-r', '--name-only', source.ref]
40
+ : ['-C', source.dir, 'ls-files'];
41
+ const r = spawnSync('git', ['-c', 'core.quotepath=false', ...args], {
42
+ encoding: 'utf8',
43
+ maxBuffer: 256 * 1024 * 1024,
44
+ });
45
+ if (r.status === 0) {
46
+ const paths = new Set(r.stdout.split('\n').filter(Boolean));
47
+ list = { paths, names: new Set([...paths].map((p) => p.slice(p.lastIndexOf('/') + 1))) };
48
+ }
49
+ }
50
+ this.fileLists.set(repo, list);
51
+ return list;
52
+ }
53
+ async search(identifier, repos, opts = {}) {
54
+ const max = opts.maxHitsPerRepo ?? DEFAULT_MAX_HITS_PER_REPO;
55
+ const result = { hits: [], skipped: [] };
56
+ if (identifier === '')
57
+ return result;
58
+ for (const repo of repos) {
59
+ const source = this.sources.get(repo);
60
+ if (!source) {
61
+ result.skipped.push({ repo, reason: '로컬 clone 경로가 없다' });
62
+ continue;
63
+ }
64
+ try {
65
+ const hits = source.ref
66
+ ? grepRef(source, identifier)
67
+ : (grepGitWorktree(source, identifier) ?? grepWorktree(source, identifier));
68
+ result.hits.push(...hits.slice(0, max));
69
+ }
70
+ catch (e) {
71
+ result.skipped.push({ repo, reason: e instanceof Error ? e.message : String(e) });
72
+ }
73
+ }
74
+ return result;
75
+ }
76
+ }
77
+ function toHit(repo, path, line, text) {
78
+ return { repo, path, line, text: text.slice(0, MAX_HIT_TEXT_LENGTH) };
79
+ }
80
+ function isSkippedPath(relPath) {
81
+ return relPath.split('/').some((seg) => SKIPPED_DIRS.has(seg));
82
+ }
83
+ /**
84
+ * git 작업 트리면 git grep으로 찾는다. 파일을 직접 훑으면 식별자마다 레포 전체를 다시 읽고
85
+ * node_modules 같은 무시 디렉토리까지 내려갔다가 걸러내서, 큰 레포에서는 식별자 수십 개에 몇 분이 걸린다.
86
+ * git 레포가 아니면 null을 돌려 직접 훑기로 넘긴다
87
+ */
88
+ const gitRootCache = new Map();
89
+ // 바깥 레포 안에 든 일반 디렉토리면 git이 바깥 레포를 잡는다. 디렉토리가 곧 작업 트리 루트일 때만 쓴다
90
+ function isGitRoot(dir) {
91
+ const cached = gitRootCache.get(dir);
92
+ if (cached !== undefined)
93
+ return cached;
94
+ const r = spawnSync('git', ['-C', dir, 'rev-parse', '--show-toplevel'], { encoding: 'utf8' });
95
+ const ok = r.status === 0 && realpathSync(r.stdout.trim()) === realpathSync(dir);
96
+ gitRootCache.set(dir, ok);
97
+ return ok;
98
+ }
99
+ function grepGitWorktree(source, identifier) {
100
+ if (!isGitRoot(source.dir))
101
+ return null;
102
+ const r = spawnSync('git', ['-C', source.dir, 'grep', '-n', '-F', '-I', '--null', '--no-color', '-e', identifier, '--'], { encoding: 'utf8', maxBuffer: 64 * 1024 * 1024 });
103
+ if (r.error)
104
+ return null;
105
+ if (r.status === 1)
106
+ return [];
107
+ if (r.status !== 0)
108
+ return null;
109
+ return parseGrepLines(source, r.stdout, '');
110
+ }
111
+ function parseGrepLines(source, stdout, prefix) {
112
+ const hits = [];
113
+ for (const raw of stdout.split('\n')) {
114
+ if (!raw.startsWith(prefix))
115
+ continue;
116
+ // --null이면 경로와 줄 번호 뒤에 각각 NUL이 붙는다
117
+ const m = /^([^\0]*)\0(\d+)\0(.*)$/.exec(raw.slice(prefix.length));
118
+ if (!m)
119
+ continue;
120
+ const path = m[1];
121
+ if (isSkippedPath(path))
122
+ continue;
123
+ hits.push(toHit(source.repo, path, Number(m[2]), m[3].trimEnd()));
124
+ }
125
+ return hits.sort((a, b) => a.path.localeCompare(b.path) || a.line - b.line);
126
+ }
127
+ function grepWorktree(source, identifier) {
128
+ const entries = readdirSync(source.dir, { recursive: true, withFileTypes: true });
129
+ const files = entries
130
+ .filter((d) => d.isFile())
131
+ .map((d) => relative(source.dir, join(d.parentPath, d.name)).split(sep).join('/'))
132
+ .filter((p) => !isSkippedPath(p))
133
+ .sort();
134
+ const hits = [];
135
+ for (const path of files) {
136
+ let buf;
137
+ try {
138
+ buf = readFileSync(join(source.dir, path));
139
+ }
140
+ catch {
141
+ continue;
142
+ }
143
+ if (buf.length > MAX_FILE_BYTES || buf.includes(0))
144
+ continue;
145
+ const lines = buf.toString('utf8').split('\n');
146
+ lines.forEach((text, i) => {
147
+ if (text.includes(identifier))
148
+ hits.push(toHit(source.repo, path, i + 1, text.trimEnd()));
149
+ });
150
+ }
151
+ return hits;
152
+ }
153
+ function grepRef(source, identifier) {
154
+ const ref = source.ref;
155
+ const r = spawnSync('git', [
156
+ '-C',
157
+ source.dir,
158
+ 'grep',
159
+ '-n',
160
+ '-F',
161
+ '-I',
162
+ '--null',
163
+ '--no-color',
164
+ '-e',
165
+ identifier,
166
+ ref,
167
+ '--',
168
+ ], { encoding: 'utf8', maxBuffer: 64 * 1024 * 1024 });
169
+ if (r.error)
170
+ throw r.error;
171
+ // status 1은 매치 없음이다
172
+ if (r.status === 1)
173
+ return [];
174
+ if (r.status !== 0)
175
+ throw new Error(`git grep 실패 (${ref}): ${r.stderr.trim()}`);
176
+ return parseGrepLines(source, r.stdout, `${ref}:`);
177
+ }
178
+ //# sourceMappingURL=search-backend.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-backend.js","sourceRoot":"","sources":["../../../src/harness-review/search-backend.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,83 @@
1
+ import type { ReferenceCandidate } from './types.js';
2
+ export type TermKind = 'ko' | 'en';
3
+ /**
4
+ * literal은 diff에 실제로 적힌 금지어, synonym은 룰북 표에서 넓힌 말이다.
5
+ * phraseRoot는 자리표시자가 든 금지 예 구절("A가 원조다")에서 조사를 떼고 남긴 어근이다.
6
+ */
7
+ export type TermOrigin = 'literal' | 'synonym' | 'phraseRoot';
8
+ export interface SearchTerm {
9
+ text: string;
10
+ kind: TermKind;
11
+ origin: TermOrigin;
12
+ /** 룰의 뜻이 조건부거나 정의뿐이라 문맥을 읽어야 판정되는 검색어 */
13
+ needsLlmJudgment: boolean;
14
+ }
15
+ export interface AddedRule {
16
+ file: string;
17
+ line: number;
18
+ ruleId?: string;
19
+ text: string;
20
+ terms: SearchTerm[];
21
+ /** 검색어가 하나도 없는 룰. grep으로는 확정할 수 없다 */
22
+ definitionOnly: boolean;
23
+ }
24
+ export interface SelfContaminationCandidate extends ReferenceCandidate {
25
+ kind: 'selfContamination';
26
+ targetLine: number;
27
+ searchTerm: string;
28
+ termOrigin: TermOrigin | 'definition';
29
+ }
30
+ export interface SelfContaminationInput {
31
+ repoRoot: string;
32
+ diff: string;
33
+ repoName?: string;
34
+ /** 후보 줄 앞뒤로 함께 돌려줄 줄 수 */
35
+ contextLines?: number;
36
+ /** 동의어를 읽을 룰북 경로(repoRoot 기준). 없으면 이름으로 찾는다 */
37
+ rulebookPaths?: string[];
38
+ }
39
+ export declare function parseAddedLines(diff: string): Map<string, Map<number, string>>;
40
+ /** 파일별로 diff에서 지워진 줄. 고친 표 행의 옛 모습을 찾는 데만 쓴다 */
41
+ export declare function parseRemovedLines(diff: string): Map<string, string[]>;
42
+ /** 따옴표, 백틱, 괄호 안은 인용이다. verify-rule-refs의 bareProse와 같은 기준 */
43
+ export declare function stripQuoted(line: string): string;
44
+ /**
45
+ * 자리표시자가 든 금지 예 구절에서 한국어 금지어 어근을 뽑는다.
46
+ * "A가 원조다"는 통째로는 어디에도 안 나오지만 "원조"는 다른 문서 산문에 그대로 남는다.
47
+ * 자리표시자 없는 구절은 extractCellTerms가 통째로 쓰므로 여기서는 빈 배열이다.
48
+ */
49
+ export declare function extractPhraseRoots(cell: string): string[];
50
+ /** 셀 하나에서 금지어 후보를 뽑는다. 따옴표와 백틱, 괄호 안 나열, 화살표 앞 영어 원어 */
51
+ export declare function extractCellTerms(cell: string, plainFallback: boolean): string[];
52
+ interface TableRow {
53
+ line: number;
54
+ ruleId?: string;
55
+ /** 검색어를 뽑는 칸만 이은 글. 정의 줄 판별에 쓴다 */
56
+ searchText: string;
57
+ fullText: string;
58
+ terms: string[];
59
+ /** 금지 예 구절에서 뽑은 어근. terms와 겹치는 말은 뺀다 */
60
+ roots: string[];
61
+ }
62
+ export declare function parseRuleTables(markdown: string): TableRow[];
63
+ export declare function extractAddedRules(repoRoot: string, added: Map<string, Map<number, string>>, removed?: Map<string, string[]>): AddedRule[];
64
+ /**
65
+ * 룰북 표에서 같은 행에 놓인 다른 언어의 말을 동의어로 넓힌다.
66
+ * 한글 금지어면 영어 원어를, 영어 원어면 그 한글 직역형을 더한다.
67
+ * 같은 행 안의 형제 금지어는 넣지 않는다. 다른 룰의 말까지 끌려온다.
68
+ */
69
+ export declare function expandSearchTerms(rules: AddedRule[], rulebookFiles: string[]): SearchTerm[];
70
+ /** 행의 화살표 짝 중 key가 든 짝의 상대편. key가 어느 짝에도 없으면 null */
71
+ export declare function arrowPartners(text: string, key: string): string[] | null;
72
+ /** 띄어쓰기 변형과 영어의 구분자 변형을 잡는 정규식. 한글 어간은 별도 변형으로 만든다 */
73
+ export declare function termPatterns(term: SearchTerm): Array<{
74
+ regex: RegExp;
75
+ exact: boolean;
76
+ }>;
77
+ /**
78
+ * 추가된 금지어와 룰 문장으로 레포 전체 md를 grep해 후보를 낸다.
79
+ * 뜻으로만 정의된 룰과 조건부 룰, 동의어 일치는 needsLlmJudgment=true로 두고 확정하지 않는다.
80
+ */
81
+ export declare function findSelfContamination(input: SelfContaminationInput): SelfContaminationCandidate[];
82
+ export {};
83
+ //# sourceMappingURL=self-contamination.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"self-contamination.d.ts","sourceRoot":"","sources":["../../../src/harness-review/self-contamination.ts"],"names":[],"mappings":"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"}