@tienne/gestalt 0.64.0 → 0.65.1

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/dist/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@tienne/gestalt",
3
- "version": "0.64.0",
3
+ "version": "0.65.1",
4
4
  "description": "TypeScript AI Development Harness - Gestalt psychology-driven requirement clarification",
5
5
  "type": "module",
6
6
  "main": "./dist/src/index.js",
7
7
  "types": "./dist/src/index.d.ts",
8
8
  "engines": {
9
- "node": ">=20.0.0"
9
+ "node": ">=22.0.0"
10
10
  },
11
11
  "bin": {
12
12
  "gestalt": "./dist/bin/gestalt.js"
@@ -43,7 +43,7 @@
43
43
  "@anthropic-ai/sdk": "^0.39.0",
44
44
  "@modelcontextprotocol/sdk": "^1.12.1",
45
45
  "@xenova/transformers": "^2.17.2",
46
- "better-sqlite3": "^11.8.2",
46
+ "better-sqlite3": "^13.0.3",
47
47
  "chokidar": "^4.0.3",
48
48
  "commander": "^13.1.0",
49
49
  "dotenv": "^17.3.1",
@@ -103,9 +103,13 @@ ges_code_graph {
103
103
  |------|------|
104
104
  | `changedFiles` | 변경된 파일 목록 |
105
105
  | `impactedFiles` | 영향받는 파일 목록 (테스트 파일 우선 정렬) |
106
- | `riskScore` | 위험도 점수 0~1 (전체 대비 영향 노드 비율) |
106
+ | `riskScore` | 위험도 점수 0~1 (전체 대비 영향 노드 비율). `depthExhausted`면 하한이다 |
107
+ | `depthExhausted` | `maxDepth`에 걸려 탐색이 멈췄고 갈 곳이 남아 있었다 |
108
+ | `unexploredNodes` | 그때 다음 홉에서 기다리던 노드 수 |
107
109
  | `summary` | 한 줄 요약 |
108
110
 
111
+ **`depthExhausted: true`면 결과는 전부가 아니라 하한이다.** 기본 `maxDepth`가 2라 3홉 이상 떨어진 호출부는 목록에 없다. 이걸 안 알리면 사용자는 "영향받는 파일 12개, 위험도 낮음"을 완전한 답으로 읽고 나머지를 안 읽는다 — 이 스킬을 쓰는 이유가 사이드 이펙트를 놓치지 않으려는 것이므로 그 오해가 가장 비싸다.
112
+
109
113
  ## Skill Instructions
110
114
 
111
115
  1. `repoRoot`가 주어지지 않으면 현재 작업 디렉토리를 절대 경로로 사용합니다.
@@ -131,6 +135,13 @@ ges_code_graph {
131
135
  **요약**: {summary}
132
136
  ```
133
137
 
138
+ `depthExhausted: true`면 위 표시 바로 아래에 한 줄을 덧붙입니다. 빠뜨리지 않습니다.
139
+
140
+ ```
141
+ ⚠️ 깊이 {maxDepthUsed}에서 탐색이 멈췄고 {unexploredNodes}개 노드가 남았습니다.
142
+ 위 목록과 위험도는 하한이며 전부가 아닙니다. 전체를 보려면 maxDepth를 올려 다시 부르세요.
143
+ ```
144
+
134
145
  5. `impactedFiles` 목록을 컨텍스트로 활용합니다:
135
146
  - "아래 파일들이 영향을 받을 수 있습니다. 관련 작업 전 이 파일들을 먼저 읽어보겠습니다:" 형식으로 안내
136
147
  - 파일이 많으면 (10개 이상) 가장 중요한 파일(테스트 파일, 핵심 모듈)을 우선 읽도록 제안
@@ -124,5 +124,12 @@ ges_code_graph {
124
124
  **요약**: {summary}
125
125
  ```
126
126
 
127
+ `depthExhausted: true`면 위 표시 바로 아래에 한 줄을 덧붙입니다. 빠뜨리지 않습니다.
128
+
129
+ ```
130
+ ⚠️ 깊이 {maxDepthUsed}에서 탐색이 멈췄고 {unexploredNodes}개 노드가 남았습니다.
131
+ 위 목록과 위험도는 하한이며 전부가 아닙니다. 전체를 보려면 maxDepth를 올려 다시 부르세요.
132
+ ```
133
+
127
134
  5. `impactedFiles` 목록을 컨텍스트로 활용합니다.
128
135
  6. 변경된 파일이 없으면 "현재 미커밋 변경이 없습니다." 안내합니다.
@@ -81,11 +81,13 @@ gh api user --jq .login
81
81
  REST(`pulls/{n}/comments`)는 resolved 여부를 주지 않으므로 **GraphQL로 조회**한다. 이미 닫힌 스레드에 답글을 다시 붙이지 않으려면 이 단계가 필요하다.
82
82
 
83
83
  ```bash
84
- gh api graphql -F owner='<owner>' -F repo='<repo>' -F number=<number> -f query='
85
- query($owner:String!, $repo:String!, $number:Int!) {
84
+ gh api graphql --paginate \
85
+ -F owner='<owner>' -F repo='<repo>' -F number=<number> -f query='
86
+ query($owner:String!, $repo:String!, $number:Int!, $endCursor:String) {
86
87
  repository(owner:$owner, name:$repo) {
87
88
  pullRequest(number:$number) {
88
- reviewThreads(first:100) {
89
+ reviewThreads(first:100, after:$endCursor) {
90
+ pageInfo { hasNextPage endCursor }
89
91
  nodes {
90
92
  id
91
93
  isResolved
@@ -93,7 +95,8 @@ query($owner:String!, $repo:String!, $number:Int!) {
93
95
  path
94
96
  line
95
97
  originalLine
96
- comments(first:30) {
98
+ comments(last:100) {
99
+ totalCount
97
100
  nodes { databaseId author { login } body createdAt }
98
101
  }
99
102
  }
@@ -103,6 +106,12 @@ query($owner:String!, $repo:String!, $number:Int!) {
103
106
  }'
104
107
  ```
105
108
 
109
+ **`comments`는 `first`가 아니라 `last`다.** 아래 필터가 스레드의 *마지막* 코멘트 작성자를 보는데 `first`는 가장 오래된 것부터 준다. 왕복이 상한을 넘은 스레드에서 중간 코멘트를 마지막으로 착각하면 판정이 양쪽으로 뒤집힌다 — 리뷰어가 기다리는 질문을 닫힌 걸로 보고 건너뛰거나, 이미 답한 스레드에 또 답글을 단다.
110
+
111
+ **스레드는 `--paginate`로 전부 받는다.** AI 리뷰어가 붙으면 라인마다 스레드가 생겨 한 PR에 100개를 넘기는 일이 흔하다. 커서를 안 돌리면 101번째부터 조용히 사라지고 그 리뷰어들은 답을 못 받는다. 잘렸다는 사실조차 안 보이는 게 이 실패의 고약한 점이다.
112
+
113
+ `first`와 `last`는 100이 GitHub 상한이다(101을 넘기면 `EXCESSIVE_PAGINATION`). 얕은 스레드는 있는 만큼만 오므로 100으로 잡아도 손해가 없다. `comments.totalCount`가 100을 넘는 스레드는 앞부분이 안 실려 온 것이니, 답글에 맥락이 필요하면 그 스레드만 `comments(first:100)`으로 다시 받는다.
114
+
106
115
  수집한 스레드를 아래 기준으로 걸러 `openThreads`를 만든다.
107
116
 
108
117
  - `isResolved: true` → 제외 (이미 닫힘)
@@ -113,11 +122,16 @@ query($owner:String!, $repo:String!, $number:Int!) {
113
122
  PR 전반 코멘트(라인에 안 붙은 것)도 함께 모은다. 스레드 개념이 없어 답글 API가 다르다.
114
123
 
115
124
  ```bash
116
- gh api repos/<owner>/<repo>/issues/<number>/comments --jq '.[] | {id, user: .user.login, body}'
125
+ gh api --paginate 'repos/<owner>/<repo>/issues/<number>/comments?per_page=100' \
126
+ --jq '.[] | {id, user: .user.login, body}'
117
127
  ```
118
128
 
129
+ 여기도 `--paginate`가 필요하다. REST 기본 페이지가 30건이라 그냥 부르면 31번째부터 잘린다.
130
+
119
131
  수집 결과를 한 줄로 알린다: **"미해결 스레드 N건, PR 전반 코멘트 M건을 찾았어요."** 0건이면 여기서 끝낸다 ("답할 코멘트가 없네요").
120
132
 
133
+ 세는 값은 페이지를 전부 받은 뒤의 총계여야 한다. 한 페이지만 보고 "100건"이라고 알리면 사용자는 그게 실제 개수인지 잘린 값인지 알 수 없다.
134
+
121
135
  ### 2단계: 코멘트별 컨텍스트 확인
122
136
 
123
137
  각 스레드가 짚은 **현재 코드**를 읽는다. 코멘트의 `diff_hunk`는 리뷰 시점 스냅샷이라 지금 코드와 다를 수 있다.
@@ -1 +1 @@
1
- {"version":3,"file":"blast-radius.d.ts","sourceRoot":"","sources":["../../../src/code-graph/blast-radius.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,KAAK,EAAE,iBAAiB,EAAmB,MAAM,YAAY,CAAC;AAGrE;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,cAAc,EACrB,YAAY,EAAE,MAAM,EAAE,EACtB,QAAQ,GAAE,MAAU,GACnB,iBAAiB,CAoGnB"}
1
+ {"version":3,"file":"blast-radius.d.ts","sourceRoot":"","sources":["../../../src/code-graph/blast-radius.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,KAAK,EAAE,iBAAiB,EAAmB,MAAM,YAAY,CAAC;AAGrE;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,cAAc,EACrB,YAAY,EAAE,MAAM,EAAE,EACtB,QAAQ,GAAE,MAAU,GACnB,iBAAiB,CAsHnB"}
@@ -12,6 +12,8 @@ export function computeBlastRadius(store, changedFiles, maxDepth = 2) {
12
12
  impactedNodes: [],
13
13
  riskScore: 0,
14
14
  maxDepthUsed: maxDepth,
15
+ depthExhausted: false,
16
+ unexploredNodes: 0,
15
17
  summary: 'No changed files provided.',
16
18
  };
17
19
  }
@@ -46,6 +48,11 @@ export function computeBlastRadius(store, changedFiles, maxDepth = 2) {
46
48
  }
47
49
  frontier = nextFrontier;
48
50
  }
51
+ // 루프는 두 이유로 끝난다 — 더 갈 데가 없거나(frontier 비었음),
52
+ // maxDepth에 걸렸거나. 후자면 아직 안 가본 노드가 frontier에 남아 있고
53
+ // impactedFiles와 riskScore는 실제값의 하한이 된다. 그 사실을 결과에 담는다.
54
+ const depthExhausted = frontier.length > 0;
55
+ const unexploredNodes = frontier.length;
49
56
  // 3. Resolve impacted node details and extract file paths
50
57
  const impactedFileSet = new Set(changedFiles);
51
58
  const impactedNodes = [];
@@ -83,13 +90,15 @@ export function computeBlastRadius(store, changedFiles, maxDepth = 2) {
83
90
  const totalNodes = stats.totalNodes;
84
91
  const riskScore = totalNodes > 0 ? Math.min(1, impactedNodeIds.size / totalNodes) : 0;
85
92
  // 7. Build summary
86
- const summary = buildSummary(changedFiles, impactedFiles, impactedNodes, riskScore);
93
+ const summary = buildSummary(changedFiles, impactedFiles, impactedNodes, riskScore, maxDepth, depthExhausted, unexploredNodes);
87
94
  return {
88
95
  changedFiles,
89
96
  impactedFiles,
90
97
  impactedNodes,
91
98
  riskScore,
92
99
  maxDepthUsed: maxDepth,
100
+ depthExhausted,
101
+ unexploredNodes,
93
102
  summary,
94
103
  };
95
104
  }
@@ -128,12 +137,19 @@ function isTestFile(filePath) {
128
137
  filePath.includes('_test.') ||
129
138
  filePath.includes('test_'));
130
139
  }
131
- function buildSummary(changedFiles, impactedFiles, impactedNodes, riskScore) {
140
+ function buildSummary(changedFiles, impactedFiles, impactedNodes, riskScore, maxDepth, depthExhausted, unexploredNodes) {
132
141
  const testFileCount = impactedFiles.filter(isTestFile).length;
133
142
  const riskLabel = riskScore > 0.6 ? 'HIGH' : riskScore > 0.3 ? 'MEDIUM' : 'LOW';
134
143
  const testNodes = impactedNodes.filter((n) => n.kind === NodeKind.Function && n.isTest).length;
135
- return (`Changed ${changedFiles.length} file(s) impact ${impactedFiles.length} file(s) ` +
144
+ const base = `Changed ${changedFiles.length} file(s) impact ${impactedFiles.length} file(s) ` +
136
145
  `(${testFileCount} test files, ${testNodes} test functions). ` +
137
- `Risk: ${riskLabel} (${(riskScore * 100).toFixed(1)}%).`);
146
+ `Risk: ${riskLabel} (${(riskScore * 100).toFixed(1)}%).`;
147
+ // 잘렸으면 요약에서 먼저 말한다. 사람은 summary만 읽고 판단하는 일이 잦은데
148
+ // "영향 12개, 위험 낮음"만 보면 그게 전부인 줄 안다.
149
+ if (!depthExhausted)
150
+ return base;
151
+ return (`${base} INCOMPLETE: search stopped at depth ${maxDepth} with ${unexploredNodes} node(s) ` +
152
+ `still unexplored, so the impact list and risk are lower bounds, not totals. ` +
153
+ `Re-run with a higher maxDepth to see the rest.`);
138
154
  }
139
155
  //# sourceMappingURL=blast-radius.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"blast-radius.js","sourceRoot":"","sources":["../../../src/code-graph/blast-radius.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"blast-radius.js","sourceRoot":"","sources":["../../../src/code-graph/blast-radius.ts"],"names":[],"mappings":"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"}
@@ -44,6 +44,13 @@ export interface BlastRadiusResult {
44
44
  impactedNodes: BlastRadiusNode[];
45
45
  riskScore: number;
46
46
  maxDepthUsed: number;
47
+ /**
48
+ * maxDepth에 걸려 탐색을 멈췄고 아직 갈 곳이 남아 있었다.
49
+ * true면 impactedFiles와 riskScore는 실제값의 하한이지 전부가 아니다.
50
+ */
51
+ depthExhausted: boolean;
52
+ /** depthExhausted일 때 다음 홉에서 기다리던 노드 수. 얼마나 잘렸는지 가늠용 */
53
+ unexploredNodes: number;
47
54
  summary: string;
48
55
  }
49
56
  export interface ParseResult {
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/code-graph/types.ts"],"names":[],"mappings":"AACA,oBAAY,QAAQ;IAClB,IAAI,SAAS;IACb,QAAQ,aAAa;IACrB,KAAK,UAAU;IACf,IAAI,SAAS;CACd;AAED,oBAAY,QAAQ;IAClB,KAAK,UAAU;IACf,YAAY,iBAAiB;IAC7B,QAAQ,aAAa;IACrB,QAAQ,aAAa;IACrB,SAAS,cAAc;CACxB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,QAAQ,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB;AAGD,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,QAAQ,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,aAAa,EAAE,eAAe,EAAE,CAAC;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB;AAGD,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,WAAW,CAAC;CACvD;AAGD,MAAM,MAAM,YAAY,GAAG,YAAY,GAAG,YAAY,GAAG,WAAW,GAAG,YAAY,CAAC;AAEpF,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,KAAK,EAAE,aAAa,EAAE,CAAC;CACxB;AAGD,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;CACrB;AAGD,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,aAAa,CAAC;AAE/C,MAAM,WAAW,YAAY;IAC3B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,IAAI,CAAC,EAAE,SAAS,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,OAAO,CAAC;CACxB;AAGD,MAAM,WAAW,kBAAkB;IACjC,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAGD,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,UAAU,GAAG,KAAK,CAAC;AAErD,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/code-graph/types.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../../src/llm/adapter.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEtE,qBAAa,gBAAiB,YAAW,UAAU;IACjD,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,KAAK,CAAS;gBAEV,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;IAKnC,IAAI,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC;CA6CtD"}
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../../src/llm/adapter.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEtE,qBAAa,gBAAiB,YAAW,UAAU;IACjD,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,KAAK,CAAS;gBAEV,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;IAKnC,IAAI,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC;CAuDtD"}
@@ -22,6 +22,13 @@ export class AnthropicAdapter {
22
22
  content: m.content,
23
23
  })),
24
24
  });
25
+ // 잘린 응답을 성공으로 돌려주면 소비자가 그걸 완전한 답으로 읽는다.
26
+ // JSON을 뽑는 쪽은 닫는 괄호가 없어 파싱에 실패하고 기본값으로 떨어지는데,
27
+ // 그 기본값이 "점수 0점" 같은 그럴듯한 값이라 원인이 영영 안 드러난다.
28
+ if (response.stop_reason === 'max_tokens') {
29
+ throw new LLMError(`Response truncated at max_tokens (${request.maxTokens ?? LLM_MAX_TOKENS}). ` +
30
+ `Raise maxTokens or shorten the prompt.`);
31
+ }
25
32
  const textBlock = response.content.find((b) => b.type === 'text');
26
33
  if (!textBlock || textBlock.type !== 'text') {
27
34
  throw new LLMError('No text content in LLM response');
@@ -1 +1 @@
1
- {"version":3,"file":"adapter.js","sourceRoot":"","sources":["../../../src/llm/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACvE,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAG3C,MAAM,OAAO,gBAAgB;IACnB,MAAM,CAAY;IAClB,KAAK,CAAS;IAEtB,YAAY,MAAc,EAAE,KAAa;QACvC,IAAI,CAAC,MAAM,GAAG,IAAI,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;QACxC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,OAAmB;QAC5B,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACtB,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;gBACjD,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,UAAU,EAAE,OAAO,CAAC,SAAS,IAAI,cAAc;gBAC/C,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,eAAe;gBACnD,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,QAAQ,EAAE,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;oBACrC,IAAI,EAAE,CAAC,CAAC,IAAI;oBACZ,OAAO,EAAE,CAAC,CAAC,OAAO;iBACnB,CAAC,CAAC;aACJ,CAAC,CAAC;YAEH,MAAM,SAAS,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC;YAClE,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;gBAC5C,MAAM,IAAI,QAAQ,CAAC,iCAAiC,CAAC,CAAC;YACxD,CAAC;YAED,MAAM,CAAC,IAAI,CAAC,oBAAoB,EAAE;gBAChC,MAAM,EAAE,aAAa;gBACrB,QAAQ,EAAE,WAAW;gBACrB,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE;aAC5B,CAAC,CAAC;YAEH,OAAO;gBACL,OAAO,EAAE,SAAS,CAAC,IAAI;gBACvB,KAAK,EAAE;oBACL,WAAW,EAAE,QAAQ,CAAC,KAAK,CAAC,YAAY;oBACxC,YAAY,EAAE,QAAQ,CAAC,KAAK,CAAC,aAAa;iBAC3C;aACF,CAAC;QACJ,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,MAAM,CAAC,KAAK,CAAC,iBAAiB,EAAE;gBAC9B,MAAM,EAAE,aAAa;gBACrB,QAAQ,EAAE,WAAW;gBACrB,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE;gBAC3B,KAAK,EAAE,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;aAClD,CAAC,CAAC;YACH,IAAI,CAAC,YAAY,QAAQ;gBAAE,MAAM,CAAC,CAAC;YACnC,MAAM,IAAI,QAAQ,CAAC,wBAAwB,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC3F,CAAC;IACH,CAAC;CACF"}
1
+ {"version":3,"file":"adapter.js","sourceRoot":"","sources":["../../../src/llm/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACvE,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAG3C,MAAM,OAAO,gBAAgB;IACnB,MAAM,CAAY;IAClB,KAAK,CAAS;IAEtB,YAAY,MAAc,EAAE,KAAa;QACvC,IAAI,CAAC,MAAM,GAAG,IAAI,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;QACxC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,OAAmB;QAC5B,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACtB,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;gBACjD,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,UAAU,EAAE,OAAO,CAAC,SAAS,IAAI,cAAc;gBAC/C,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,eAAe;gBACnD,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,QAAQ,EAAE,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;oBACrC,IAAI,EAAE,CAAC,CAAC,IAAI;oBACZ,OAAO,EAAE,CAAC,CAAC,OAAO;iBACnB,CAAC,CAAC;aACJ,CAAC,CAAC;YAEH,wCAAwC;YACxC,8CAA8C;YAC9C,4CAA4C;YAC5C,IAAI,QAAQ,CAAC,WAAW,KAAK,YAAY,EAAE,CAAC;gBAC1C,MAAM,IAAI,QAAQ,CAChB,qCAAqC,OAAO,CAAC,SAAS,IAAI,cAAc,KAAK;oBAC3E,wCAAwC,CAC3C,CAAC;YACJ,CAAC;YAED,MAAM,SAAS,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC;YAClE,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;gBAC5C,MAAM,IAAI,QAAQ,CAAC,iCAAiC,CAAC,CAAC;YACxD,CAAC;YAED,MAAM,CAAC,IAAI,CAAC,oBAAoB,EAAE;gBAChC,MAAM,EAAE,aAAa;gBACrB,QAAQ,EAAE,WAAW;gBACrB,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE;aAC5B,CAAC,CAAC;YAEH,OAAO;gBACL,OAAO,EAAE,SAAS,CAAC,IAAI;gBACvB,KAAK,EAAE;oBACL,WAAW,EAAE,QAAQ,CAAC,KAAK,CAAC,YAAY;oBACxC,YAAY,EAAE,QAAQ,CAAC,KAAK,CAAC,aAAa;iBAC3C;aACF,CAAC;QACJ,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,MAAM,CAAC,KAAK,CAAC,iBAAiB,EAAE;gBAC9B,MAAM,EAAE,aAAa;gBACrB,QAAQ,EAAE,WAAW;gBACrB,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE;gBAC3B,KAAK,EAAE,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;aAClD,CAAC,CAAC;YACH,IAAI,CAAC,YAAY,QAAQ;gBAAE,MAAM,CAAC,CAAC;YACnC,MAAM,IAAI,QAAQ,CAAC,wBAAwB,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC3F,CAAC;IACH,CAAC;CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"openai-adapter.d.ts","sourceRoot":"","sources":["../../../src/llm/openai-adapter.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEtE,qBAAa,aAAc,YAAW,UAAU;IAC9C,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,KAAK,CAAS;gBAEV,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM;IAKrD,IAAI,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC;CAmDtD"}
1
+ {"version":3,"file":"openai-adapter.d.ts","sourceRoot":"","sources":["../../../src/llm/openai-adapter.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEtE,qBAAa,aAAc,YAAW,UAAU;IAC9C,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,KAAK,CAAS;gBAEV,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM;IAKrD,IAAI,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC;CA6DtD"}
@@ -26,6 +26,12 @@ export class OpenAIAdapter {
26
26
  messages,
27
27
  });
28
28
  const choice = response.choices[0];
29
+ // adapter.ts의 stop_reason 검사와 같은 이유다 — 잘린 응답을 성공으로
30
+ // 돌려주면 소비자가 완전한 답으로 읽고 조용히 기본값으로 떨어진다.
31
+ if (choice?.finish_reason === 'length') {
32
+ throw new LLMError(`Response truncated at max_tokens (${request.maxTokens ?? LLM_MAX_TOKENS}). ` +
33
+ `Raise maxTokens or shorten the prompt.`);
34
+ }
29
35
  if (!choice?.message?.content) {
30
36
  throw new LLMError('No content in OpenAI response');
31
37
  }
@@ -1 +1 @@
1
- {"version":3,"file":"openai-adapter.js","sourceRoot":"","sources":["../../../src/llm/openai-adapter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"openai-adapter.js","sourceRoot":"","sources":["../../../src/llm/openai-adapter.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@tienne/gestalt",
3
- "version": "0.64.0",
3
+ "version": "0.65.1",
4
4
  "description": "TypeScript AI Development Harness - Gestalt psychology-driven requirement clarification",
5
5
  "type": "module",
6
6
  "main": "./dist/src/index.js",
7
7
  "types": "./dist/src/index.d.ts",
8
8
  "engines": {
9
- "node": ">=20.0.0"
9
+ "node": ">=22.0.0"
10
10
  },
11
11
  "bin": {
12
12
  "gestalt": "./dist/bin/gestalt.js"
@@ -43,7 +43,7 @@
43
43
  "@anthropic-ai/sdk": "^0.39.0",
44
44
  "@modelcontextprotocol/sdk": "^1.12.1",
45
45
  "@xenova/transformers": "^2.17.2",
46
- "better-sqlite3": "^11.8.2",
46
+ "better-sqlite3": "^13.0.3",
47
47
  "chokidar": "^4.0.3",
48
48
  "commander": "^13.1.0",
49
49
  "dotenv": "^17.3.1",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gestalt",
3
- "version": "0.64.0",
3
+ "version": "0.65.1",
4
4
  "description": "Gestalt psychology-driven AI development harness. Transforms scattered requirements into structured, validated specifications through interactive interviews.",
5
5
  "author": {
6
6
  "name": "tienne"
@@ -103,9 +103,13 @@ ges_code_graph {
103
103
  |------|------|
104
104
  | `changedFiles` | 변경된 파일 목록 |
105
105
  | `impactedFiles` | 영향받는 파일 목록 (테스트 파일 우선 정렬) |
106
- | `riskScore` | 위험도 점수 0~1 (전체 대비 영향 노드 비율) |
106
+ | `riskScore` | 위험도 점수 0~1 (전체 대비 영향 노드 비율). `depthExhausted`면 하한이다 |
107
+ | `depthExhausted` | `maxDepth`에 걸려 탐색이 멈췄고 갈 곳이 남아 있었다 |
108
+ | `unexploredNodes` | 그때 다음 홉에서 기다리던 노드 수 |
107
109
  | `summary` | 한 줄 요약 |
108
110
 
111
+ **`depthExhausted: true`면 결과는 전부가 아니라 하한이다.** 기본 `maxDepth`가 2라 3홉 이상 떨어진 호출부는 목록에 없다. 이걸 안 알리면 사용자는 "영향받는 파일 12개, 위험도 낮음"을 완전한 답으로 읽고 나머지를 안 읽는다 — 이 스킬을 쓰는 이유가 사이드 이펙트를 놓치지 않으려는 것이므로 그 오해가 가장 비싸다.
112
+
109
113
  ## Skill Instructions
110
114
 
111
115
  1. `repoRoot`가 주어지지 않으면 현재 작업 디렉토리를 절대 경로로 사용합니다.
@@ -131,6 +135,13 @@ ges_code_graph {
131
135
  **요약**: {summary}
132
136
  ```
133
137
 
138
+ `depthExhausted: true`면 위 표시 바로 아래에 한 줄을 덧붙입니다. 빠뜨리지 않습니다.
139
+
140
+ ```
141
+ ⚠️ 깊이 {maxDepthUsed}에서 탐색이 멈췄고 {unexploredNodes}개 노드가 남았습니다.
142
+ 위 목록과 위험도는 하한이며 전부가 아닙니다. 전체를 보려면 maxDepth를 올려 다시 부르세요.
143
+ ```
144
+
134
145
  5. `impactedFiles` 목록을 컨텍스트로 활용합니다:
135
146
  - "아래 파일들이 영향을 받을 수 있습니다. 관련 작업 전 이 파일들을 먼저 읽어보겠습니다:" 형식으로 안내
136
147
  - 파일이 많으면 (10개 이상) 가장 중요한 파일(테스트 파일, 핵심 모듈)을 우선 읽도록 제안
@@ -124,5 +124,12 @@ ges_code_graph {
124
124
  **요약**: {summary}
125
125
  ```
126
126
 
127
+ `depthExhausted: true`면 위 표시 바로 아래에 한 줄을 덧붙입니다. 빠뜨리지 않습니다.
128
+
129
+ ```
130
+ ⚠️ 깊이 {maxDepthUsed}에서 탐색이 멈췄고 {unexploredNodes}개 노드가 남았습니다.
131
+ 위 목록과 위험도는 하한이며 전부가 아닙니다. 전체를 보려면 maxDepth를 올려 다시 부르세요.
132
+ ```
133
+
127
134
  5. `impactedFiles` 목록을 컨텍스트로 활용합니다.
128
135
  6. 변경된 파일이 없으면 "현재 미커밋 변경이 없습니다." 안내합니다.
@@ -81,11 +81,13 @@ gh api user --jq .login
81
81
  REST(`pulls/{n}/comments`)는 resolved 여부를 주지 않으므로 **GraphQL로 조회**한다. 이미 닫힌 스레드에 답글을 다시 붙이지 않으려면 이 단계가 필요하다.
82
82
 
83
83
  ```bash
84
- gh api graphql -F owner='<owner>' -F repo='<repo>' -F number=<number> -f query='
85
- query($owner:String!, $repo:String!, $number:Int!) {
84
+ gh api graphql --paginate \
85
+ -F owner='<owner>' -F repo='<repo>' -F number=<number> -f query='
86
+ query($owner:String!, $repo:String!, $number:Int!, $endCursor:String) {
86
87
  repository(owner:$owner, name:$repo) {
87
88
  pullRequest(number:$number) {
88
- reviewThreads(first:100) {
89
+ reviewThreads(first:100, after:$endCursor) {
90
+ pageInfo { hasNextPage endCursor }
89
91
  nodes {
90
92
  id
91
93
  isResolved
@@ -93,7 +95,8 @@ query($owner:String!, $repo:String!, $number:Int!) {
93
95
  path
94
96
  line
95
97
  originalLine
96
- comments(first:30) {
98
+ comments(last:100) {
99
+ totalCount
97
100
  nodes { databaseId author { login } body createdAt }
98
101
  }
99
102
  }
@@ -103,6 +106,12 @@ query($owner:String!, $repo:String!, $number:Int!) {
103
106
  }'
104
107
  ```
105
108
 
109
+ **`comments`는 `first`가 아니라 `last`다.** 아래 필터가 스레드의 *마지막* 코멘트 작성자를 보는데 `first`는 가장 오래된 것부터 준다. 왕복이 상한을 넘은 스레드에서 중간 코멘트를 마지막으로 착각하면 판정이 양쪽으로 뒤집힌다 — 리뷰어가 기다리는 질문을 닫힌 걸로 보고 건너뛰거나, 이미 답한 스레드에 또 답글을 단다.
110
+
111
+ **스레드는 `--paginate`로 전부 받는다.** AI 리뷰어가 붙으면 라인마다 스레드가 생겨 한 PR에 100개를 넘기는 일이 흔하다. 커서를 안 돌리면 101번째부터 조용히 사라지고 그 리뷰어들은 답을 못 받는다. 잘렸다는 사실조차 안 보이는 게 이 실패의 고약한 점이다.
112
+
113
+ `first`와 `last`는 100이 GitHub 상한이다(101을 넘기면 `EXCESSIVE_PAGINATION`). 얕은 스레드는 있는 만큼만 오므로 100으로 잡아도 손해가 없다. `comments.totalCount`가 100을 넘는 스레드는 앞부분이 안 실려 온 것이니, 답글에 맥락이 필요하면 그 스레드만 `comments(first:100)`으로 다시 받는다.
114
+
106
115
  수집한 스레드를 아래 기준으로 걸러 `openThreads`를 만든다.
107
116
 
108
117
  - `isResolved: true` → 제외 (이미 닫힘)
@@ -113,11 +122,16 @@ query($owner:String!, $repo:String!, $number:Int!) {
113
122
  PR 전반 코멘트(라인에 안 붙은 것)도 함께 모은다. 스레드 개념이 없어 답글 API가 다르다.
114
123
 
115
124
  ```bash
116
- gh api repos/<owner>/<repo>/issues/<number>/comments --jq '.[] | {id, user: .user.login, body}'
125
+ gh api --paginate 'repos/<owner>/<repo>/issues/<number>/comments?per_page=100' \
126
+ --jq '.[] | {id, user: .user.login, body}'
117
127
  ```
118
128
 
129
+ 여기도 `--paginate`가 필요하다. REST 기본 페이지가 30건이라 그냥 부르면 31번째부터 잘린다.
130
+
119
131
  수집 결과를 한 줄로 알린다: **"미해결 스레드 N건, PR 전반 코멘트 M건을 찾았어요."** 0건이면 여기서 끝낸다 ("답할 코멘트가 없네요").
120
132
 
133
+ 세는 값은 페이지를 전부 받은 뒤의 총계여야 한다. 한 페이지만 보고 "100건"이라고 알리면 사용자는 그게 실제 개수인지 잘린 값인지 알 수 없다.
134
+
121
135
  ### 2단계: 코멘트별 컨텍스트 확인
122
136
 
123
137
  각 스레드가 짚은 **현재 코드**를 읽는다. 코멘트의 `diff_hunk`는 리뷰 시점 스냅샷이라 지금 코드와 다를 수 있다.