devkit-debugging-mcp 0.1.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.
- package/AGENTS.md +52 -0
- package/PRD.md +86 -0
- package/README.md +21 -0
- package/TRD.md +120 -0
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +7 -0
- package/dist/bin.js.map +1 -0
- package/dist/core/errors.d.ts +12 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +23 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/fs/readText.d.ts +2 -0
- package/dist/core/fs/readText.d.ts.map +1 -0
- package/dist/core/fs/readText.js +9 -0
- package/dist/core/fs/readText.js.map +1 -0
- package/dist/core/fs/walk.d.ts +7 -0
- package/dist/core/fs/walk.d.ts.map +1 -0
- package/dist/core/fs/walk.js +48 -0
- package/dist/core/fs/walk.js.map +1 -0
- package/dist/core/jsonc.d.ts +2 -0
- package/dist/core/jsonc.d.ts.map +1 -0
- package/dist/core/jsonc.js +6 -0
- package/dist/core/jsonc.js.map +1 -0
- package/dist/core/mcp/mcpResult.d.ts +9 -0
- package/dist/core/mcp/mcpResult.d.ts.map +1 -0
- package/dist/core/mcp/mcpResult.js +7 -0
- package/dist/core/mcp/mcpResult.js.map +1 -0
- package/dist/core/patch/suggestPatch.d.ts +26 -0
- package/dist/core/patch/suggestPatch.d.ts.map +1 -0
- package/dist/core/patch/suggestPatch.js +139 -0
- package/dist/core/patch/suggestPatch.js.map +1 -0
- package/dist/core/readiness/checkReadiness.d.ts +19 -0
- package/dist/core/readiness/checkReadiness.d.ts.map +1 -0
- package/dist/core/readiness/checkReadiness.js +105 -0
- package/dist/core/readiness/checkReadiness.js.map +1 -0
- package/dist/core/redact.d.ts +2 -0
- package/dist/core/redact.d.ts.map +1 -0
- package/dist/core/redact.js +29 -0
- package/dist/core/redact.js.map +1 -0
- package/dist/core/toolRunner.d.ts +3 -0
- package/dist/core/toolRunner.d.ts.map +1 -0
- package/dist/core/toolRunner.js +19 -0
- package/dist/core/toolRunner.js.map +1 -0
- package/dist/core/triage/triageIssue.d.ts +22 -0
- package/dist/core/triage/triageIssue.d.ts.map +1 -0
- package/dist/core/triage/triageIssue.js +234 -0
- package/dist/core/triage/triageIssue.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/server/createDebuggingServer.d.ts +3 -0
- package/dist/server/createDebuggingServer.d.ts.map +1 -0
- package/dist/server/createDebuggingServer.js +17 -0
- package/dist/server/createDebuggingServer.js.map +1 -0
- package/dist/tools/check_debugging_readiness.d.ts +14 -0
- package/dist/tools/check_debugging_readiness.d.ts.map +1 -0
- package/dist/tools/check_debugging_readiness.js +38 -0
- package/dist/tools/check_debugging_readiness.js.map +1 -0
- package/dist/tools/refactor_guarded.d.ts +50 -0
- package/dist/tools/refactor_guarded.d.ts.map +1 -0
- package/dist/tools/refactor_guarded.js +64 -0
- package/dist/tools/refactor_guarded.js.map +1 -0
- package/dist/tools/schemas.d.ts +103 -0
- package/dist/tools/schemas.d.ts.map +1 -0
- package/dist/tools/schemas.js +25 -0
- package/dist/tools/schemas.js.map +1 -0
- package/dist/tools/suggest_patch.d.ts +23 -0
- package/dist/tools/suggest_patch.d.ts.map +1 -0
- package/dist/tools/suggest_patch.js +41 -0
- package/dist/tools/suggest_patch.js.map +1 -0
- package/dist/tools/triage_issue.d.ts +17 -0
- package/dist/tools/triage_issue.d.ts.map +1 -0
- package/dist/tools/triage_issue.js +33 -0
- package/dist/tools/triage_issue.js.map +1 -0
- package/dist/types/contracts.d.ts +36 -0
- package/dist/types/contracts.d.ts.map +1 -0
- package/dist/types/contracts.js +2 -0
- package/dist/types/contracts.js.map +1 -0
- package/package.json +47 -0
package/AGENTS.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# AGENTS.md (devkit-debugging-mcp)
|
|
2
|
+
|
|
3
|
+
코드 및 주석은 영어로 한다.
|
|
4
|
+
이 레포는 npm에 배포할 **Custom MCP 서버**이며, Codex/Claude 등 다양한 MCP 클라이언트 설정에 결합될 수 있어야 한다.
|
|
5
|
+
|
|
6
|
+
## 0) 목적 (Mission)
|
|
7
|
+
|
|
8
|
+
이 MCP는 **Next.js + Supabase** 프로젝트에서의 **테스팅/디버깅/리팩토링(안전한 범위)**을 표준화한다.
|
|
9
|
+
|
|
10
|
+
핵심은:
|
|
11
|
+
|
|
12
|
+
- 에러/테스트 실패를 **빠르게 분류(triage)**
|
|
13
|
+
- 원인을 **최소 컨텍스트로 좁히고**
|
|
14
|
+
- **dry-run 변경 계획(패치)**으로 수정안을 제안
|
|
15
|
+
- 검증 절차(테스트/빌드/재현)를 함께 제공
|
|
16
|
+
|
|
17
|
+
## 1) 기본 원칙 (Non‑Negotiables)
|
|
18
|
+
|
|
19
|
+
1. **Safety first**: 민감정보/권한/RLS/서버경계를 훼손하는 제안 금지.
|
|
20
|
+
2. **Small diffs**: “큰 리팩토링”보다 “최소 변경”을 우선.
|
|
21
|
+
3. **Dry-run 우선**: 파일 수정은 변경 계획을 먼저 제시.
|
|
22
|
+
4. **Verifiable**: 항상 “확인 방법(명령/기대결과)”을 포함.
|
|
23
|
+
5. **Idempotent**: 같은 입력/상태에서 결과가 흔들리지 않게.
|
|
24
|
+
|
|
25
|
+
## 2) 타겟 범위
|
|
26
|
+
|
|
27
|
+
- Next.js App Router 기반 프로젝트
|
|
28
|
+
- Supabase(Postgres, migrations, RLS) 기반 프로젝트
|
|
29
|
+
- TypeScript strict, ESLint, 테스트 러너(jest/vitest/playwright 등) 혼재 가능
|
|
30
|
+
|
|
31
|
+
## 3) 레포 문서의 역할
|
|
32
|
+
|
|
33
|
+
- `PRD.md`: 제품 요구사항/범위/성공 기준
|
|
34
|
+
- `TRD.md`: 기술 설계/계약/툴 스펙
|
|
35
|
+
- 각 폴더의 `AGENTS.md`: 해당 디렉토리 트리에서 지켜야 할 작업 규칙
|
|
36
|
+
|
|
37
|
+
## 4) 작업 방식(Agent Workflow)
|
|
38
|
+
|
|
39
|
+
새 tool 추가 시 순서:
|
|
40
|
+
|
|
41
|
+
1. `src/tools/<tool>.ts`에 1 tool 1 file로 구현
|
|
42
|
+
2. 입력/출력 Zod 스키마를 정의하고 계약을 문서화
|
|
43
|
+
3. dry-run change plan을 기본 경로로 지원
|
|
44
|
+
4. 스냅샷 테스트 추가(`tests/`)
|
|
45
|
+
5. 가드레일(민감정보/권한/서버경계) 점검 로직을 통과
|
|
46
|
+
|
|
47
|
+
금지:
|
|
48
|
+
|
|
49
|
+
- 사용자의 프로젝트를 임의로 “대규모 리팩토링”하는 변경 제안
|
|
50
|
+
- RLS 약화/서비스키 노출을 유도하는 변경
|
|
51
|
+
- `.env`/키/토큰을 출력하거나 로그로 남기는 행위
|
|
52
|
+
|
package/PRD.md
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# PRD (devkit-debugging-mcp)
|
|
2
|
+
|
|
3
|
+
## 0) 한 줄 소개
|
|
4
|
+
|
|
5
|
+
`devkit-debugging-mcp`는 **Next.js(App Router) + Supabase** 프로젝트에서 발생하는 **테스트 실패/런타임 에러/빌드 에러**를 빠르게 **분류(triage)하고**, 안전한 범위에서 **원인 분석 + 수정 제안(패치)** + **리팩토링 가이드**를 제공하는 **Custom MCP 서버(npm 패키지)**다.
|
|
6
|
+
|
|
7
|
+
핵심 의도는 “문서로 부탁”이 아니라, **도구(tool)로 디버깅/테스팅 워크플로우를 표준화**하는 것이다.
|
|
8
|
+
|
|
9
|
+
## 1) 사용자(Who) & 사용 시나리오(When)
|
|
10
|
+
|
|
11
|
+
- 대상 사용자
|
|
12
|
+
- Next.js + Supabase 기반 제품을 개발/운영하는 팀
|
|
13
|
+
- 코드 에이전트(Codex/Claude 등)로 디버깅을 자동화하고 싶은 개발자
|
|
14
|
+
- 주 사용 상황
|
|
15
|
+
- CI에서 테스트가 깨짐(단위/통합/E2E 포함)
|
|
16
|
+
- `next dev`에서 런타임 오류/서버 오류/클라이언트 오류 발생
|
|
17
|
+
- `next build`/`next lint`/`tsc` 에러 발생
|
|
18
|
+
- Supabase migration/RLS 정책/권한 관련 이슈 발생
|
|
19
|
+
- 기능 리팩토링 중 회귀(regression) 의심
|
|
20
|
+
|
|
21
|
+
## 2) 문제 정의(Problem)
|
|
22
|
+
|
|
23
|
+
Next.js + Supabase 프로젝트에서 에러가 나면, 에이전트가 다음을 반복적으로 수행한다:
|
|
24
|
+
|
|
25
|
+
- 로그/스택트레이스를 해석하고 원인을 추적
|
|
26
|
+
- 관련 파일을 찾아 컨텍스트를 구성
|
|
27
|
+
- 재현/검증 절차를 제안하고, 수정안을 작성
|
|
28
|
+
- 수정 후 회귀를 막기 위해 테스트를 보강/정리
|
|
29
|
+
|
|
30
|
+
하지만 프로젝트마다 폴더 구조/컨벤션/테스트 런너/환경변수/DB 정책이 달라 **반복 비용이 크고** 실수로 **위험한 수정(과도한 리팩토링, 보안 취약점, RLS 붕괴)**이 발생하기 쉽다.
|
|
31
|
+
|
|
32
|
+
## 3) 목표(Goals)
|
|
33
|
+
|
|
34
|
+
1. **빠른 분류(Triage)**: 에러/테스트 실패를 “무엇이 깨졌는지”로 빠르게 구조화한다.
|
|
35
|
+
2. **원인 추적(Root cause)**: 최소 컨텍스트로 실제 원인 후보를 좁힌다.
|
|
36
|
+
3. **안전한 수정 제안(Safe fixes)**: 큰 리팩토링 대신, 작은 변경으로 해결하는 패치 제안을 우선한다.
|
|
37
|
+
4. **검증 가능(Verifiable)**: 제안은 항상 “어떻게 확인할지(명령/절차/기대결과)”를 포함한다.
|
|
38
|
+
5. **리팩토링 지원(Refactor assist)**: 기능 변경이 아닌, 안정성/가독성/테스트 용이성 중심 리팩토링을 돕는다.
|
|
39
|
+
6. **Next/Supabase 표준 준수**: env 분리, 서버/클라이언트 경계, RLS 최소권한 등 안전장치를 유지한다.
|
|
40
|
+
|
|
41
|
+
## 4) 비목표(Non-goals)
|
|
42
|
+
|
|
43
|
+
- 프로젝트 도메인 로직을 추측해 기능을 “대신 구현”하지 않는다.
|
|
44
|
+
- 과도한 자동 리팩토링(대규모 파일 이동/아키텍처 재구성)을 목표로 하지 않는다.
|
|
45
|
+
- 민감정보(키/토큰/서비스 롤 키)를 수집/로그/출력하지 않는다.
|
|
46
|
+
- 네트워크가 필요한 외부 서비스 의존(원격 LLM 호출, 원격 API 탐색)을 기본 기능으로 요구하지 않는다.
|
|
47
|
+
|
|
48
|
+
## 5) MVP 범위(What we ship first)
|
|
49
|
+
|
|
50
|
+
MVP는 “디버깅의 80%를 가장 안전하게” 처리하는 데 집중한다.
|
|
51
|
+
|
|
52
|
+
### 5.1 제공할 도구(초기 제안)
|
|
53
|
+
|
|
54
|
+
- `triage_issue`
|
|
55
|
+
- 입력: 에러 메시지/스택트레이스/테스트 실패 로그(텍스트)
|
|
56
|
+
- 출력: 문제 유형 분류, 우선순위, 즉시 확인 체크리스트, 의심 파일/키워드
|
|
57
|
+
- `suggest_patch`
|
|
58
|
+
- 입력: 문제 요약 + 관련 파일 경로(선택) + 원하는 변경 수준(“minimal/medium”)
|
|
59
|
+
- 출력: **dry-run 패치(변경 계획)** + 리스크/검증 절차
|
|
60
|
+
- `refactor_guarded`
|
|
61
|
+
- 입력: 리팩토링 목표(가독성/테스트 용이성/중복 제거) + 대상 파일/심볼
|
|
62
|
+
- 출력: **행동 제한(guardrails)** 하에서의 변경 계획 + 회귀 방지 체크
|
|
63
|
+
- `check_debugging_readiness`
|
|
64
|
+
- 입력: 프로젝트 루트 경로
|
|
65
|
+
- 출력: 디버깅/테스트 관점에서의 “준비도 체크”(예: 스크립트 존재, 테스트 러너 감지, env 접근 패턴 등)
|
|
66
|
+
|
|
67
|
+
> 주의: “실제 테스트 실행/커맨드 실행”은 기본 MVP에서 필수가 아니다. 대신 **실행할 명령과 기대결과를 제안**하는 형태를 우선한다.
|
|
68
|
+
|
|
69
|
+
### 5.2 성공 기준(Success metrics)
|
|
70
|
+
|
|
71
|
+
- 에러/테스트 실패 사례에서 “1~2회 반복”으로 원인 후보를 좁히는 비율 증가
|
|
72
|
+
- 패치 제안이 “최소 변경 + 검증 절차 포함” 형태로 일관되게 생성
|
|
73
|
+
- 위험한 변경(보안/권한/RLS 붕괴, client 번들로 키 노출) 제안 빈도 0에 가깝게 유지
|
|
74
|
+
|
|
75
|
+
## 6) 제약/원칙(Constraints & Principles)
|
|
76
|
+
|
|
77
|
+
- **멱등성**: 같은 입력으로 같은 제안을 내는 방향(비결정성 최소화)
|
|
78
|
+
- **dry-run 우선**: 파일 쓰기/수정은 항상 dry-run 변경 계획을 먼저 제공
|
|
79
|
+
- **친절한 실패**: 입력이 부족하면 “무엇이 더 필요한지”를 구체적으로 요청
|
|
80
|
+
- **안전 가드레일**: env/키/RLS/서버경계 관련 규칙 위반을 유도하지 않는다
|
|
81
|
+
|
|
82
|
+
## 7) 배포/통합(Distribution)
|
|
83
|
+
|
|
84
|
+
- 형태: npm 패키지(ESM) + MCP 서버 실행 엔트리(`bin`)
|
|
85
|
+
- 통합 대상: Codex/Claude 등 MCP 클라이언트 구성에 연결 가능
|
|
86
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# devkit-debugging-mcp
|
|
2
|
+
|
|
3
|
+
Custom MCP server for **debugging, testing, and guarded refactoring** in **Next.js + Supabase** projects.
|
|
4
|
+
|
|
5
|
+
- Focus: triage errors, propose minimal patches (dry-run), provide verification steps.
|
|
6
|
+
- Safety: prevent secrets leakage, respect server/client boundary, keep Supabase RLS secure.
|
|
7
|
+
|
|
8
|
+
## Status
|
|
9
|
+
|
|
10
|
+
First release includes:
|
|
11
|
+
|
|
12
|
+
- `triage_issue`: classify logs/test failures and return an actionable checklist (secrets redacted)
|
|
13
|
+
- `check_debugging_readiness`: inspect a project root for debugging/testing readiness signals
|
|
14
|
+
- `suggest_patch`: guarded, deterministic patch planning for a small set of common issues (dry-run by default)
|
|
15
|
+
- `refactor_guarded`: no-behavior-change refactor planning with verification steps
|
|
16
|
+
|
|
17
|
+
## Usage (MCP)
|
|
18
|
+
|
|
19
|
+
Run via stdio transport (typical MCP client setup).
|
|
20
|
+
|
|
21
|
+
- Command: `npx -y devkit-debugging-mcp`
|
package/TRD.md
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# TRD (devkit-debugging-mcp)
|
|
2
|
+
|
|
3
|
+
## 0) 기술 목표
|
|
4
|
+
|
|
5
|
+
- MCP 서버로서 **도구(tool) 기반 디버깅 워크플로우**를 제공한다.
|
|
6
|
+
- 결과는 “텍스트 조언”이 아니라 **구조화된 출력(스키마) + 변경 계획(dry-run diff)** 중심으로 제공한다.
|
|
7
|
+
- Next.js(App Router) + Supabase 프로젝트의 안전 규칙을 깨지 않도록 **가드레일을 1급 요구사항**으로 둔다.
|
|
8
|
+
|
|
9
|
+
## 1) 런타임/스택
|
|
10
|
+
|
|
11
|
+
- Node.js `>=18.18`
|
|
12
|
+
- TypeScript (strict)
|
|
13
|
+
- ESM 패키지(`"type": "module"`)
|
|
14
|
+
- `@modelcontextprotocol/sdk`
|
|
15
|
+
- `zod`
|
|
16
|
+
|
|
17
|
+
네트워크 의존은 기본 TRD에서 제외(로컬 파일/로그 기반).
|
|
18
|
+
|
|
19
|
+
## 2) 레포 구조(권장)
|
|
20
|
+
|
|
21
|
+
- `src/`
|
|
22
|
+
- `bin.ts` : CLI 엔트리(MCP 서버 실행)
|
|
23
|
+
- `server/` : 서버 생성/등록
|
|
24
|
+
- `tools/` : MCP tool 구현(1 tool = 1 file)
|
|
25
|
+
- `core/`
|
|
26
|
+
- `errors.ts` : 에러 코드/에러 래핑
|
|
27
|
+
- `contracts.ts` : 공통 응답/변경계획 타입
|
|
28
|
+
- `diff/` : 변경 계획 preview 생성
|
|
29
|
+
- `fs/` : 안전한 파일 읽기/쓰기(옵션), walk 유틸
|
|
30
|
+
- `guards/` : Next/Supabase 안전 규칙(키 노출, server/client 경계 등)
|
|
31
|
+
- `triage/` : 로그/스택트레이스 파서
|
|
32
|
+
- `tests/` : tool 단위 테스트(스냅샷 포함)
|
|
33
|
+
- `docs/` : 추가 문서(필요 시)
|
|
34
|
+
|
|
35
|
+
## 3) 공통 계약(Contracts)
|
|
36
|
+
|
|
37
|
+
### 3.1 표준 응답 envelope
|
|
38
|
+
|
|
39
|
+
모든 tool 응답은 아래 중 하나를 따른다.
|
|
40
|
+
|
|
41
|
+
- 성공
|
|
42
|
+
- `{ ok: true, data: T, message?: string, nextSteps?: string[] }`
|
|
43
|
+
- 실패
|
|
44
|
+
- `{ ok: false, error: { code: string, message: string, details?: unknown } }`
|
|
45
|
+
|
|
46
|
+
### 3.2 변경 계획(Change Plan)
|
|
47
|
+
|
|
48
|
+
파일을 수정/생성/삭제하는 tool은 **반드시** dry-run 변경 계획을 지원한다.
|
|
49
|
+
|
|
50
|
+
- `changes: Array<{ type: "create"|"update"|"delete", path: string, preview: string }>`
|
|
51
|
+
- 실제 write 모드(옵션)에서는 `filesWritten: string[]`를 함께 반환한다.
|
|
52
|
+
|
|
53
|
+
## 4) Tool 설계(Design)
|
|
54
|
+
|
|
55
|
+
### 4.1 입력 검증
|
|
56
|
+
|
|
57
|
+
- 모든 입력은 Zod로 검증한다.
|
|
58
|
+
- 입력이 부족하면 `INVALID_INPUT`으로 실패시키되, `details`에 “추가로 필요한 정보”를 포함한다.
|
|
59
|
+
|
|
60
|
+
### 4.2 결정성(Determinism)
|
|
61
|
+
|
|
62
|
+
- 동일 입력에 대해 출력 구조가 크게 흔들리지 않도록 한다.
|
|
63
|
+
- 로그 파싱 결과는 “규칙 기반” 우선으로 구성한다(LLM 의존 최소화).
|
|
64
|
+
|
|
65
|
+
### 4.3 안전 가드레일
|
|
66
|
+
|
|
67
|
+
다음은 tool 레벨에서 금지/차단/경고한다.
|
|
68
|
+
|
|
69
|
+
- `.env*` 내용, API 키, Supabase service role key 등 민감값을 출력/로그로 내보내는 행위
|
|
70
|
+
- `NEXT_PUBLIC_*`로 서버 비밀을 노출하도록 유도하는 제안
|
|
71
|
+
- Supabase RLS를 무력화(`using (true)` 등의 광범위 정책)하는 제안
|
|
72
|
+
- 서버 전용 코드를 클라이언트 번들로 끌어오는 import/사용 패턴 제안
|
|
73
|
+
|
|
74
|
+
## 5) MVP Tool 스펙(초안)
|
|
75
|
+
|
|
76
|
+
### Tool A: `triage_issue`
|
|
77
|
+
|
|
78
|
+
- 목적: 에러/테스트 실패 로그를 구조화하여 다음 액션을 명확히 한다.
|
|
79
|
+
- 입력(예): `{ "text": "....stacktrace....", "contextHint"?: "next|supabase|jest|vitest|playwright|tsc|eslint" }`
|
|
80
|
+
- 출력(예):
|
|
81
|
+
- `{ ok: true, data: { category, severity, suspectedFiles, checklist, questions } }`
|
|
82
|
+
|
|
83
|
+
### Tool B: `suggest_patch`
|
|
84
|
+
|
|
85
|
+
- 목적: 최소 변경으로 문제 해결 패치를 제안한다(dry-run 우선).
|
|
86
|
+
- 입력(예): `{ "summary": "...", "files"?: ["..."], "changeLevel": "minimal|medium", "dryRun": true }`
|
|
87
|
+
- 출력(예):
|
|
88
|
+
- `{ ok: true, data: { rationale, risks, verification }, changes: [...] }`
|
|
89
|
+
|
|
90
|
+
### Tool C: `refactor_guarded`
|
|
91
|
+
|
|
92
|
+
- 목적: 안전한 리팩토링(동작 변경 금지 원칙) 제안.
|
|
93
|
+
- 입력(예): `{ "goal": "readability|testability|dedupe", "targets": [{ "file": "...", "symbol"?: "..." }], "dryRun": true }`
|
|
94
|
+
- 출력(예): `{ ok: true, data: { plan, verification }, changes: [...] }`
|
|
95
|
+
|
|
96
|
+
### Tool D: `check_debugging_readiness`
|
|
97
|
+
|
|
98
|
+
- 목적: 프로젝트의 디버깅/테스트 준비도 진단.
|
|
99
|
+
- 입력(예): `{ "path": ".", "strict": true }`
|
|
100
|
+
- 출력(예): `{ ok: true, data: { detected, warnings, suggestions } }`
|
|
101
|
+
|
|
102
|
+
## 6) 에러 코드(표준)
|
|
103
|
+
|
|
104
|
+
- `INVALID_INPUT`
|
|
105
|
+
- `NOT_FOUND`
|
|
106
|
+
- `CONVENTION_VIOLATION`
|
|
107
|
+
- `FILE_CONFLICT`
|
|
108
|
+
- `INTERNAL_ERROR`
|
|
109
|
+
|
|
110
|
+
## 7) 테스트 정책
|
|
111
|
+
|
|
112
|
+
- 로그 파서/triage 결과는 스냅샷 테스트로 고정한다.
|
|
113
|
+
- dry-run 변경 계획은 스냅샷 테스트로 고정한다.
|
|
114
|
+
- “멱등성”: 동일 입력/동일 상태에서 change plan이 변하지 않는지 테스트한다.
|
|
115
|
+
|
|
116
|
+
## 8) 로깅 정책
|
|
117
|
+
|
|
118
|
+
- 기본 로그는 최소화한다(디버깅 정보가 필요하면 opt-in).
|
|
119
|
+
- 민감정보 마스킹을 공통 유틸로 제공한다.
|
|
120
|
+
|
package/dist/bin.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bin.d.ts","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":""}
|
package/dist/bin.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { createDebuggingServer } from "./server/createDebuggingServer.js";
|
|
4
|
+
const server = createDebuggingServer();
|
|
5
|
+
const transport = new StdioServerTransport();
|
|
6
|
+
await server.connect(transport);
|
|
7
|
+
//# sourceMappingURL=bin.js.map
|
package/dist/bin.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,qBAAqB,EAAE,MAAM,mCAAmC,CAAC;AAE1E,MAAM,MAAM,GAAG,qBAAqB,EAAE,CAAC;AACvC,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;AAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare class ToolErrorBase extends Error {
|
|
2
|
+
readonly code: string;
|
|
3
|
+
readonly details?: unknown;
|
|
4
|
+
constructor(code: string, message: string, details?: unknown);
|
|
5
|
+
}
|
|
6
|
+
export declare class InvalidInputError extends ToolErrorBase {
|
|
7
|
+
constructor(message: string, details?: unknown);
|
|
8
|
+
}
|
|
9
|
+
export declare class NotFoundError extends ToolErrorBase {
|
|
10
|
+
constructor(message: string, details?: unknown);
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,aAAc,SAAQ,KAAK;IACtC,SAAgB,IAAI,EAAE,MAAM,CAAC;IAC7B,SAAgB,OAAO,CAAC,EAAE,OAAO,CAAC;gBAEtB,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO;CAM7D;AAED,qBAAa,iBAAkB,SAAQ,aAAa;gBACtC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO;CAI/C;AAED,qBAAa,aAAc,SAAQ,aAAa;gBAClC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO;CAI/C"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export class ToolErrorBase extends Error {
|
|
2
|
+
code;
|
|
3
|
+
details;
|
|
4
|
+
constructor(code, message, details) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.name = "ToolError";
|
|
7
|
+
this.code = code;
|
|
8
|
+
this.details = details;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export class InvalidInputError extends ToolErrorBase {
|
|
12
|
+
constructor(message, details) {
|
|
13
|
+
super("INVALID_INPUT", message, details);
|
|
14
|
+
this.name = "InvalidInputError";
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export class NotFoundError extends ToolErrorBase {
|
|
18
|
+
constructor(message, details) {
|
|
19
|
+
super("NOT_FOUND", message, details);
|
|
20
|
+
this.name = "NotFoundError";
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,aAAc,SAAQ,KAAK;IACtB,IAAI,CAAS;IACb,OAAO,CAAW;IAElC,YAAY,IAAY,EAAE,OAAe,EAAE,OAAiB;QAC1D,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;CACF;AAED,MAAM,OAAO,iBAAkB,SAAQ,aAAa;IAClD,YAAY,OAAe,EAAE,OAAiB;QAC5C,KAAK,CAAC,eAAe,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QACzC,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;IAClC,CAAC;CACF;AAED,MAAM,OAAO,aAAc,SAAQ,aAAa;IAC9C,YAAY,OAAe,EAAE,OAAiB;QAC5C,KAAK,CAAC,WAAW,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QACrC,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;IAC9B,CAAC;CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"readText.d.ts","sourceRoot":"","sources":["../../../src/core/fs/readText.ts"],"names":[],"mappings":"AAEA,wBAAsB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,SAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAMxF"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
export async function readTextFile(filePath, maxBytes = 512_000) {
|
|
3
|
+
const buf = await fs.readFile(filePath);
|
|
4
|
+
if (buf.byteLength > maxBytes) {
|
|
5
|
+
return buf.subarray(0, maxBytes).toString("utf8") + "\n<trimmed>";
|
|
6
|
+
}
|
|
7
|
+
return buf.toString("utf8");
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=readText.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"readText.js","sourceRoot":"","sources":["../../../src/core/fs/readText.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAElC,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,QAAgB,EAAE,QAAQ,GAAG,OAAO;IACrE,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACxC,IAAI,GAAG,CAAC,UAAU,GAAG,QAAQ,EAAE,CAAC;QAC9B,OAAO,GAAG,CAAC,QAAQ,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,aAAa,CAAC;IACpE,CAAC;IACD,OAAO,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;AAC9B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"walk.d.ts","sourceRoot":"","sources":["../../../src/core/fs/walk.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,WAAW,GAAG;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B,CAAC;AAcF,wBAAsB,SAAS,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAkCvE"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
const DEFAULT_IGNORES = [
|
|
4
|
+
"node_modules",
|
|
5
|
+
".next",
|
|
6
|
+
"dist",
|
|
7
|
+
"build",
|
|
8
|
+
"coverage",
|
|
9
|
+
".turbo",
|
|
10
|
+
".git",
|
|
11
|
+
".vercel",
|
|
12
|
+
".output",
|
|
13
|
+
];
|
|
14
|
+
export async function walkFiles(options) {
|
|
15
|
+
const rootDir = path.resolve(options.rootDir);
|
|
16
|
+
const ignore = new Set(options.ignoreDirNames ?? DEFAULT_IGNORES);
|
|
17
|
+
const maxFiles = options.maxFiles ?? 5000;
|
|
18
|
+
const results = [];
|
|
19
|
+
const queue = [rootDir];
|
|
20
|
+
while (queue.length > 0) {
|
|
21
|
+
const current = queue.pop();
|
|
22
|
+
if (!current)
|
|
23
|
+
break;
|
|
24
|
+
if (results.length >= maxFiles)
|
|
25
|
+
break;
|
|
26
|
+
let entries;
|
|
27
|
+
try {
|
|
28
|
+
entries = await fs.readdir(current, { withFileTypes: true });
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
for (const entry of entries) {
|
|
34
|
+
if (results.length >= maxFiles)
|
|
35
|
+
break;
|
|
36
|
+
const full = path.join(current, entry.name);
|
|
37
|
+
if (entry.isDirectory()) {
|
|
38
|
+
if (!ignore.has(entry.name))
|
|
39
|
+
queue.push(full);
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (entry.isFile())
|
|
43
|
+
results.push(full);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return results;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=walk.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"walk.js","sourceRoot":"","sources":["../../../src/core/fs/walk.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAQ7B,MAAM,eAAe,GAAG;IACtB,cAAc;IACd,OAAO;IACP,MAAM;IACN,OAAO;IACP,UAAU;IACV,QAAQ;IACR,MAAM;IACN,SAAS;IACT,SAAS;CACV,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,OAAoB;IAClD,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9C,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,cAAc,IAAI,eAAe,CAAC,CAAC;IAClE,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,IAAI,CAAC;IAE1C,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,KAAK,GAAa,CAAC,OAAO,CAAC,CAAC;IAElC,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC;QAC5B,IAAI,CAAC,OAAO;YAAE,MAAM;QAEpB,IAAI,OAAO,CAAC,MAAM,IAAI,QAAQ;YAAE,MAAM;QAEtC,IAAI,OAAwC,CAAC;QAC7C,IAAI,CAAC;YACH,OAAO,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/D,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QAED,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,IAAI,OAAO,CAAC,MAAM,IAAI,QAAQ;gBAAE,MAAM;YAEtC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YAC5C,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;gBACxB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC;oBAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBAC9C,SAAS;YACX,CAAC;YACD,IAAI,KAAK,CAAC,MAAM,EAAE;gBAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACzC,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsonc.d.ts","sourceRoot":"","sources":["../../src/core/jsonc.ts"],"names":[],"mappings":"AAAA,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAIhD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsonc.js","sourceRoot":"","sources":["../../src/core/jsonc.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,mBAAmB,EAAE,EAAE,CAAC,CAAC;IAC3D,MAAM,WAAW,GAAG,YAAY,CAAC,OAAO,CAAC,iBAAiB,EAAE,IAAI,CAAC,CAAC;IAClE,OAAO,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;AACjC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcpResult.d.ts","sourceRoot":"","sources":["../../../src/core/mcp/mcpResult.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAE3D,wBAAgB,WAAW,CAAC,OAAO,EAAE,UAAU;;;;;;EAK9C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcpResult.js","sourceRoot":"","sources":["../../../src/core/mcp/mcpResult.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,WAAW,CAAC,OAAmB;IAC7C,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;QAC5E,iBAAiB,EAAE,OAAO;KAC3B,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ChangePreview } from "../../types/contracts.js";
|
|
2
|
+
import { triageIssue } from "../triage/triageIssue.js";
|
|
3
|
+
export type SuggestPatchInput = {
|
|
4
|
+
projectRoot: string;
|
|
5
|
+
text: string;
|
|
6
|
+
changeLevel: "minimal" | "medium";
|
|
7
|
+
dryRun: boolean;
|
|
8
|
+
};
|
|
9
|
+
export type SuggestPatchOutput = {
|
|
10
|
+
triage: ReturnType<typeof triageIssue>;
|
|
11
|
+
rationale: string;
|
|
12
|
+
risks: string[];
|
|
13
|
+
verification: string[];
|
|
14
|
+
};
|
|
15
|
+
export declare function suggestPatchPlan(input: SuggestPatchInput): Promise<{
|
|
16
|
+
data: SuggestPatchOutput;
|
|
17
|
+
changes: ChangePreview[];
|
|
18
|
+
filesWritten: string[];
|
|
19
|
+
nextSteps: string[];
|
|
20
|
+
warnings: Array<{
|
|
21
|
+
code: string;
|
|
22
|
+
message: string;
|
|
23
|
+
path?: string;
|
|
24
|
+
}>;
|
|
25
|
+
}>;
|
|
26
|
+
//# sourceMappingURL=suggestPatch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"suggestPatch.d.ts","sourceRoot":"","sources":["../../../src/core/patch/suggestPatch.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAEvD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,SAAS,GAAG,QAAQ,CAAC;IAClC,MAAM,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,CAAC;AAgFF,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC;IACxE,IAAI,EAAE,kBAAkB,CAAC;IACzB,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,QAAQ,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACnE,CAAC,CAoED"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import { readTextFile } from "../fs/readText.js";
|
|
4
|
+
import { walkFiles } from "../fs/walk.js";
|
|
5
|
+
import { parseJsonc } from "../jsonc.js";
|
|
6
|
+
import { triageIssue } from "../triage/triageIssue.js";
|
|
7
|
+
async function exists(p) {
|
|
8
|
+
try {
|
|
9
|
+
await fs.access(p);
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function toPrettyJson(value) {
|
|
17
|
+
return JSON.stringify(value, null, 2) + "\n";
|
|
18
|
+
}
|
|
19
|
+
function toChangePreview(update) {
|
|
20
|
+
return {
|
|
21
|
+
type: "update",
|
|
22
|
+
path: update.relativePath,
|
|
23
|
+
preview: `# ${update.relativePath}\n# ${update.note}\n\n${update.nextContent}`,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
async function planTsconfigPathAliasFix(projectRoot, text) {
|
|
27
|
+
if (!/@\//.test(text))
|
|
28
|
+
return null;
|
|
29
|
+
const tsconfigPath = path.join(projectRoot, "tsconfig.json");
|
|
30
|
+
if (!(await exists(tsconfigPath)))
|
|
31
|
+
return null;
|
|
32
|
+
const raw = await readTextFile(tsconfigPath, 512_000);
|
|
33
|
+
let json;
|
|
34
|
+
try {
|
|
35
|
+
json = parseJsonc(raw);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
if (!json || typeof json !== "object")
|
|
41
|
+
return null;
|
|
42
|
+
const hasSrcDir = await exists(path.join(projectRoot, "src"));
|
|
43
|
+
const baseUrl = ".";
|
|
44
|
+
const target = hasSrcDir ? "./src/*" : "./*";
|
|
45
|
+
const next = structuredClone(json);
|
|
46
|
+
next.compilerOptions = next.compilerOptions ?? {};
|
|
47
|
+
next.compilerOptions.baseUrl = next.compilerOptions.baseUrl ?? baseUrl;
|
|
48
|
+
next.compilerOptions.paths = next.compilerOptions.paths ?? {};
|
|
49
|
+
const existing = next.compilerOptions.paths["@/*"];
|
|
50
|
+
const desired = [target];
|
|
51
|
+
const shouldUpdate = !Array.isArray(existing) || JSON.stringify(existing) !== JSON.stringify(desired);
|
|
52
|
+
if (!shouldUpdate)
|
|
53
|
+
return null;
|
|
54
|
+
next.compilerOptions.paths["@/*"] = desired;
|
|
55
|
+
return [
|
|
56
|
+
{
|
|
57
|
+
relativePath: "tsconfig.json",
|
|
58
|
+
note: "Add/adjust `@/*` path alias for `@/` imports.",
|
|
59
|
+
nextContent: toPrettyJson(next),
|
|
60
|
+
},
|
|
61
|
+
];
|
|
62
|
+
}
|
|
63
|
+
async function detectNextSupabase(projectRoot) {
|
|
64
|
+
const pkgPath = path.join(projectRoot, "package.json");
|
|
65
|
+
if (!(await exists(pkgPath)))
|
|
66
|
+
return { hasNext: false, hasSupabase: false };
|
|
67
|
+
const pkg = JSON.parse(await readTextFile(pkgPath, 256_000));
|
|
68
|
+
const deps = { ...(pkg.dependencies ?? {}), ...(pkg.devDependencies ?? {}) };
|
|
69
|
+
return {
|
|
70
|
+
hasNext: Boolean(deps.next),
|
|
71
|
+
hasSupabase: Boolean(deps.supabase || deps["@supabase/supabase-js"] || deps["@supabase/ssr"]),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
export async function suggestPatchPlan(input) {
|
|
75
|
+
const projectRoot = path.resolve(input.projectRoot);
|
|
76
|
+
const triage = triageIssue(input.text);
|
|
77
|
+
const warnings = [];
|
|
78
|
+
const { hasNext, hasSupabase } = await detectNextSupabase(projectRoot);
|
|
79
|
+
if (!hasNext)
|
|
80
|
+
warnings.push({ code: "NEXT_NOT_DETECTED", message: "Next.js dependency not detected in package.json." });
|
|
81
|
+
if (!hasSupabase)
|
|
82
|
+
warnings.push({ code: "SUPABASE_NOT_DETECTED", message: "Supabase dependency not detected in package.json." });
|
|
83
|
+
const plannedUpdates = [];
|
|
84
|
+
const filesWritten = [];
|
|
85
|
+
const aliasFix = await planTsconfigPathAliasFix(projectRoot, input.text);
|
|
86
|
+
if (aliasFix)
|
|
87
|
+
plannedUpdates.push(...aliasFix);
|
|
88
|
+
// Heuristic: encourage an env example if none exists (no file write by default).
|
|
89
|
+
const files = await walkFiles({ rootDir: projectRoot, maxFiles: 2000 });
|
|
90
|
+
const hasEnvExample = files.some((f) => /(?:^|\/)\.env\.example$/.test(f) || /(?:^|\/)\.env\.local\.example$/.test(f));
|
|
91
|
+
if (!hasEnvExample) {
|
|
92
|
+
warnings.push({
|
|
93
|
+
code: "NO_ENV_EXAMPLE",
|
|
94
|
+
message: "No `.env.example` (or `.env.local.example`) found; consider adding one (without secret values).",
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
if (!input.dryRun && plannedUpdates.length > 0) {
|
|
98
|
+
for (const update of plannedUpdates) {
|
|
99
|
+
const targetPath = path.join(projectRoot, update.relativePath);
|
|
100
|
+
await fs.writeFile(targetPath, update.nextContent, "utf8");
|
|
101
|
+
filesWritten.push(update.relativePath);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const changes = plannedUpdates.map(toChangePreview);
|
|
105
|
+
const rationale = changes.length > 0
|
|
106
|
+
? "Generated a minimal, deterministic patch plan based on common Next.js/TS failure patterns."
|
|
107
|
+
: "No safe, deterministic patch recipe matched the provided input; returning triage + verification guidance only.";
|
|
108
|
+
const risks = [];
|
|
109
|
+
if (changes.some((c) => c.path === "tsconfig.json")) {
|
|
110
|
+
risks.push("Changing `tsconfig.json` may affect IDE/TS resolution and may reveal additional type errors.");
|
|
111
|
+
}
|
|
112
|
+
if (triage.category === "supabase_rls") {
|
|
113
|
+
risks.push("RLS fixes are security-sensitive; avoid broad allow-all policies and validate with the exact role/claims.");
|
|
114
|
+
}
|
|
115
|
+
const verification = [];
|
|
116
|
+
if (triage.category === "typescript" || changes.some((c) => c.path === "tsconfig.json"))
|
|
117
|
+
verification.push("npm run typecheck");
|
|
118
|
+
if (triage.category === "test_failure")
|
|
119
|
+
verification.push("npm test");
|
|
120
|
+
if (triage.category === "next_build")
|
|
121
|
+
verification.push("npm run build");
|
|
122
|
+
if (verification.length === 0)
|
|
123
|
+
verification.push("Re-run the exact command that produced the error and confirm the first error block changed.");
|
|
124
|
+
const nextSteps = [];
|
|
125
|
+
if (changes.length > 0) {
|
|
126
|
+
nextSteps.push(input.dryRun ? "Review the proposed patch preview, then re-run with `dryRun: false` to apply." : "Re-run verification commands.");
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
nextSteps.push("If you want patch generation, provide project root + the exact failing command output and the relevant file contents.");
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
data: { triage, rationale, risks, verification },
|
|
133
|
+
changes,
|
|
134
|
+
filesWritten,
|
|
135
|
+
nextSteps,
|
|
136
|
+
warnings,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=suggestPatch.js.map
|