claude-prism 0.3.1 → 0.4.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/README.ko.md +26 -11
- package/README.md +44 -28
- package/bin/cli.mjs +29 -0
- package/hooks/alignment.mjs +94 -0
- package/hooks/commit-guard.mjs +5 -3
- package/hooks/debug-loop.mjs +20 -5
- package/hooks/scope-guard.mjs +11 -7
- package/hooks/test-tracker.mjs +65 -10
- package/hooks/turn-reporter.mjs +70 -0
- package/lib/adapter.mjs +1 -0
- package/lib/config.mjs +21 -2
- package/lib/installer.mjs +119 -9
- package/lib/messages.mjs +60 -0
- package/lib/pipeline.mjs +188 -0
- package/lib/session.mjs +108 -0
- package/package.json +9 -1
- package/templates/runners/post-tool.mjs +13 -0
- package/templates/runners/pre-tool.mjs +9 -0
- package/templates/runners/user-prompt.mjs +7 -0
- package/templates/settings.json +10 -19
package/README.ko.md
CHANGED
|
@@ -11,13 +11,19 @@ AI 코딩 문제 분해 도구 — Understand, Decompose, Execute, Checkpoint (U
|
|
|
11
11
|
스펙트럼
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
+
[](https://www.npmjs.com/package/claude-prism)
|
|
15
|
+
[](https://github.com/lazysaturday91/claude-prism/blob/main/LICENSE)
|
|
16
|
+
[](https://nodejs.org)
|
|
17
|
+
|
|
18
|
+
> `ai-coding` · `problem-decomposition` · `claude-code-hooks` · `claude-code-plugin` · `udec` · `scope-guard`
|
|
19
|
+
|
|
14
20
|
## 무엇인가
|
|
15
21
|
|
|
16
22
|
`claude-prism`은 UDEC 방법론 프레임워크를 Claude Code 프로젝트에 설치합니다.
|
|
17
23
|
|
|
18
24
|
- **U**nderstand (이해) — 행동하기 전에 정보 충분성을 판별. 구조화된 질문 (한 번에 하나씩, 객관식, 최대 3라운드)
|
|
19
25
|
- **D**ecompose (분해) — 복잡한 문제를 2-5분 크기의 검증 가능한 단위로 분해, TDD 기반
|
|
20
|
-
- **E**xecute (실행) — 배치
|
|
26
|
+
- **E**xecute (실행) — 적응형 배치 실행, 파일 경로별 컨텍스트 인식 검증 (TDD / 빌드 / lint-only)
|
|
21
27
|
- **C**heckpoint (확인) — 매 배치 후 보고, 다음 진행 전 사용자 확인
|
|
22
28
|
|
|
23
29
|
## 핵심 철학
|
|
@@ -37,6 +43,9 @@ npx claude-prism init --lang=ja # 일본어
|
|
|
37
43
|
npx claude-prism init --lang=zh # 중국어
|
|
38
44
|
npx claude-prism init --no-hooks # 규칙만, hook 없이
|
|
39
45
|
prism check # 설치 확인
|
|
46
|
+
npx claude-prism init --global # 글로벌 스킬로 설치 (모든 프로젝트에서 사용)
|
|
47
|
+
npx claude-prism update # 규칙과 커맨드를 최신으로 업데이트
|
|
48
|
+
npx claude-prism update --global # 글로벌 스킬도 업데이트
|
|
40
49
|
```
|
|
41
50
|
|
|
42
51
|
## 설치 후 구조
|
|
@@ -53,7 +62,8 @@ prism check # 설치 확인
|
|
|
53
62
|
│ │ ├── plan.md # /claude-prism:plan
|
|
54
63
|
│ │ ├── doctor.md # /claude-prism:doctor
|
|
55
64
|
│ │ ├── stats.md # /claude-prism:stats
|
|
56
|
-
│ │
|
|
65
|
+
│ │ ├── help.md # /claude-prism:help
|
|
66
|
+
│ │ └── update.md # /claude-prism:update
|
|
57
67
|
│ ├── hooks/ # (선택, --no-hooks 시 생략)
|
|
58
68
|
│ │ ├── commit-guard.mjs
|
|
59
69
|
│ │ ├── debug-loop.mjs
|
|
@@ -86,6 +96,7 @@ prism check # 설치 확인
|
|
|
86
96
|
| `/claude-prism:doctor` | 설치 문제 | 건강 진단, 수정 제안 |
|
|
87
97
|
| `/claude-prism:stats` | 현재 상태 | 버전, hooks, 언어, 플랜 진행률 |
|
|
88
98
|
| `/claude-prism:help` | 커맨드 확인 | 커맨드 레퍼런스 |
|
|
99
|
+
| `/claude-prism:update` | 업데이트 후 | 규칙과 커맨드를 최신 버전으로 업데이트 |
|
|
89
100
|
|
|
90
101
|
### 워크플로우
|
|
91
102
|
|
|
@@ -224,7 +235,7 @@ prism stats
|
|
|
224
235
|
|
|
225
236
|
출력:
|
|
226
237
|
```
|
|
227
|
-
Version: v0.1
|
|
238
|
+
Version: v0.3.1
|
|
228
239
|
Language: ko
|
|
229
240
|
Plans: 2 file(s)
|
|
230
241
|
OMC: ✅ v4.1.1
|
|
@@ -276,7 +287,7 @@ Hook은 선택 사항인 CLI 가드로, 개발 중 규율을 강제합니다. `p
|
|
|
276
287
|
|
|
277
288
|
### debug-loop
|
|
278
289
|
|
|
279
|
-
같은
|
|
290
|
+
같은 파일의 편집 패턴을 분석합니다. **발산 편집** (같은 코드 영역 반복 — 삽질 가능성)과 **수렴 편집** (다른 영역: import, 로직, JSX — 정상적인 점진적 작업)을 구별합니다.
|
|
280
291
|
|
|
281
292
|
```json
|
|
282
293
|
{
|
|
@@ -291,9 +302,10 @@ Hook은 선택 사항인 CLI 가드로, 개발 중 규율을 강제합니다. `p
|
|
|
291
302
|
```
|
|
292
303
|
|
|
293
304
|
**동작:**
|
|
294
|
-
-
|
|
295
|
-
-
|
|
296
|
-
-
|
|
305
|
+
- 스니펫 분석으로 편집 패턴 추적
|
|
306
|
+
- **발산 패턴** (같은 영역): 3회에서 경고, 5회에서 차단
|
|
307
|
+
- **수렴 패턴** (다른 영역): 조용히 통과, 차단도 경고로 다운그레이드
|
|
308
|
+
- 무한 디버깅 루프를 감지하면서 정상적인 다영역 편집은 허용
|
|
297
309
|
|
|
298
310
|
### test-tracker
|
|
299
311
|
|
|
@@ -322,7 +334,7 @@ Hook은 선택 사항인 CLI 가드로, 개발 중 규율을 강제합니다. `p
|
|
|
322
334
|
|
|
323
335
|
**동작:**
|
|
324
336
|
- 모든 Bash 커맨드에서 실행
|
|
325
|
-
- 커맨드가 테스트 패턴과 일치하면
|
|
337
|
+
- 커맨드가 테스트 패턴과 일치하면 타임스탬프 기록
|
|
326
338
|
- 결과 기록 (종료 코드 기반 성공/실패)
|
|
327
339
|
- `commit-guard`가 이 상태를 읽어 커밋 허용/차단
|
|
328
340
|
|
|
@@ -351,6 +363,9 @@ Hook은 선택 사항인 CLI 가드로, 개발 중 규율을 강제합니다. `p
|
|
|
351
363
|
- Agent 임계값 (OMC sub-agent 실행 중): 8개에서 경고, 12개에서 차단
|
|
352
364
|
- 경고: "/claude-prism:prism을 실행하여 작업을 분해하는 것을 고려하세요"
|
|
353
365
|
- 차단: "/claude-prism:prism을 실행하여 계속 진행하기 전에 분해하세요"
|
|
366
|
+
- **플랜 인식**: 플랜 파일 생성 시 (`docs/plans/*.md`) 임계값이 자동으로 2배
|
|
367
|
+
- 표준 + 플랜: 8개에서 경고, 14개에서 차단
|
|
368
|
+
- Agent + 플랜: 16개에서 경고, 24개에서 차단
|
|
354
369
|
|
|
355
370
|
## 설정
|
|
356
371
|
|
|
@@ -396,7 +411,7 @@ prism doctor # 진단 정보에 OMC 감지 표시
|
|
|
396
411
|
## 기술 사양
|
|
397
412
|
|
|
398
413
|
- **패키지명**: `claude-prism`
|
|
399
|
-
- **버전**: 0.1
|
|
414
|
+
- **버전**: 0.3.1
|
|
400
415
|
- **CLI 커맨드**: `prism`
|
|
401
416
|
- **Node 버전**: >= 18
|
|
402
417
|
- **의존성**: 0 (순수 ESM 모듈)
|
|
@@ -416,8 +431,8 @@ prism doctor # 진단 정보에 OMC 감지 표시
|
|
|
416
431
|
1. **정보 충분성 판별** — 요청이 명확한가? 질문이 필요한가?
|
|
417
432
|
2. **최대 3라운드 질문** — 한 번에 하나씩, 객관식 우선
|
|
418
433
|
3. **분해 5원칙** — 단위 크기, 테스트 선행, 독립 검증, 파일 명시, 의존성 명시
|
|
419
|
-
4.
|
|
420
|
-
5.
|
|
434
|
+
4. **적응형 배치 + 체크포인트** — 복잡도별 배치 크기 조절 (단순 5-8 / 일반 3-4 / 복잡 1-2), 배치마다 보고
|
|
435
|
+
5. **컨텍스트 인식 검증** — 파일 경로별 검증 전략 (lib→TDD, components→빌드, config→lint)
|
|
421
436
|
6. **자기 교정** — 같은 파일 3회 편집 시 멈추고 재검토
|
|
422
437
|
|
|
423
438
|
## 라이선스
|
package/README.md
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
```
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
2
|
+
╱╲
|
|
3
|
+
━━━━━━━━━▶ ╱ ╲ ──── U Understand
|
|
4
|
+
complex ╱ ╲ ──── D Decompose
|
|
5
|
+
problem ╱ PRISM╲──── E Execute
|
|
6
|
+
╱________╲─── C Checkpoint
|
|
7
|
+
spectrum
|
|
8
8
|
```
|
|
9
9
|
|
|
10
|
+
[](https://www.npmjs.com/package/claude-prism)
|
|
11
|
+
[](https://github.com/lazysaturday91/claude-prism/blob/main/LICENSE)
|
|
12
|
+
[](https://nodejs.org)
|
|
13
|
+
|
|
14
|
+
> `ai-coding` · `problem-decomposition` · `claude-code-hooks` · `claude-code-plugin` · `udec` · `scope-guard`
|
|
15
|
+
|
|
10
16
|
# claude-prism
|
|
11
17
|
|
|
12
18
|
An AI coding problem decomposition tool for Claude Code. Installs the **UDEC** methodology — Understand, Decompose, Execute, Checkpoint — directly into your project's Claude Code environment.
|
|
@@ -15,11 +21,6 @@ An AI coding problem decomposition tool for Claude Code. Installs the **UDEC** m
|
|
|
15
21
|
|
|
16
22
|
**Core philosophy:** Never implement what you haven't understood. Never execute what you haven't decomposed.
|
|
17
23
|
|
|
18
|
-
- Zero dependencies
|
|
19
|
-
- Node >= 18
|
|
20
|
-
- MIT License
|
|
21
|
-
- Repository: https://github.com/lazysaturday91/claude-prism
|
|
22
|
-
|
|
23
24
|
## The Problem
|
|
24
25
|
|
|
25
26
|
Without structure, Claude does this:
|
|
@@ -29,7 +30,7 @@ Without structure, Claude does this:
|
|
|
29
30
|
| Reads request → assumes understanding | Reads request → assesses sufficiency |
|
|
30
31
|
| Starts coding immediately | Asks 1-2 clarifying questions first |
|
|
31
32
|
| Builds one 30-minute mega-feature | Decomposes into 2-5 minute verifiable units |
|
|
32
|
-
| Runs autonomously (no checkpoints) |
|
|
33
|
+
| Runs autonomously (no checkpoints) | Adaptive batches (1-8 tasks by complexity) → checkpoint → ask permission |
|
|
33
34
|
| Produces working code that's wrong | Produces code that's correct *and* wanted |
|
|
34
35
|
|
|
35
36
|
## Installation
|
|
@@ -40,6 +41,9 @@ npx claude-prism init --lang=ko # Korean
|
|
|
40
41
|
npx claude-prism init --lang=ja # Japanese
|
|
41
42
|
npx claude-prism init --lang=zh # Chinese
|
|
42
43
|
npx claude-prism init --no-hooks # Rules only, no hooks
|
|
44
|
+
npx claude-prism init --global # Install as global skill (available in all projects)
|
|
45
|
+
npx claude-prism update # Update rules and commands to latest
|
|
46
|
+
npx claude-prism update --global # Update global skill too
|
|
43
47
|
prism check # Verify installation
|
|
44
48
|
```
|
|
45
49
|
|
|
@@ -50,7 +54,7 @@ After running `prism init`, your project gains:
|
|
|
50
54
|
**UDEC Rules** — Injected into `CLAUDE.md` between `PRISM:START` and `PRISM:END` markers. Explains the four-phase methodology:
|
|
51
55
|
- **U** — Assess information sufficiency before acting. Ask one question at a time, multiple choice, max 3 rounds.
|
|
52
56
|
- **D** — Decompose complex problems into 2-5 minute units with TDD. Create a plan file for 6+ file changes.
|
|
53
|
-
- **E** — Execute in batches
|
|
57
|
+
- **E** — Execute in adaptive batches. Apply context-aware verification by file path (TDD / build / lint-only).
|
|
54
58
|
- **C** — Checkpoint after each batch. Report progress, show next batch preview, get confirmation before continuing.
|
|
55
59
|
|
|
56
60
|
**Slash Commands** — Added to `.claude/commands/claude-prism/`:
|
|
@@ -59,11 +63,12 @@ After running `prism init`, your project gains:
|
|
|
59
63
|
- `/claude-prism:plan` — List, create, or view plan files
|
|
60
64
|
- `/claude-prism:doctor` — Diagnose installation health via Claude
|
|
61
65
|
- `/claude-prism:stats` — Show project statistics, hook status, and plan progress
|
|
66
|
+
- `/claude-prism:update` — Update rules and commands to latest version
|
|
62
67
|
- `/claude-prism:help` — Command reference
|
|
63
68
|
|
|
64
69
|
**Hooks** (optional, unless `--no-hooks` is set) — Four CLI guards that enforce discipline:
|
|
65
70
|
- `commit-guard` — Prevents commits when tests haven't run recently
|
|
66
|
-
- `debug-loop` —
|
|
71
|
+
- `debug-loop` — Detects divergent editing patterns on the same file (catches infinite debugging loops)
|
|
67
72
|
- `test-tracker` — Detects test command execution (npm test, jest, vitest, pytest, etc.) and records pass/fail state
|
|
68
73
|
- `scope-guard` — Warns at 4 unique files modified, blocks at 7 (agent-aware: warns at 8, blocks at 12)
|
|
69
74
|
|
|
@@ -83,6 +88,7 @@ your-project/
|
|
|
83
88
|
│ │ ├── plan.md # /claude-prism:plan
|
|
84
89
|
│ │ ├── doctor.md # /claude-prism:doctor
|
|
85
90
|
│ │ ├── stats.md # /claude-prism:stats
|
|
91
|
+
│ │ ├── update.md # /claude-prism:update
|
|
86
92
|
│ │ └── help.md # /claude-prism:help
|
|
87
93
|
│ ├── hooks/ # (optional, if --no-hooks not set)
|
|
88
94
|
│ │ ├── commit-guard.mjs
|
|
@@ -116,7 +122,7 @@ your-project/
|
|
|
116
122
|
[ DECOMPOSE ] ← Break into 2-5 min units, create plan file
|
|
117
123
|
|
|
|
118
124
|
v
|
|
119
|
-
[ EXECUTE ] ← Run batch
|
|
125
|
+
[ EXECUTE ] ← Run adaptive batch, verify each unit
|
|
120
126
|
|
|
|
121
127
|
v
|
|
122
128
|
[ CHECKPOINT ] ← Report, show next batch, ask to continue
|
|
@@ -135,6 +141,7 @@ your-project/
|
|
|
135
141
|
| `/claude-prism:checkpoint` | Mid-project | Check batch progress, preview next batch |
|
|
136
142
|
| `/claude-prism:doctor` | Installation issues | Diagnose health, suggest fixes |
|
|
137
143
|
| `/claude-prism:stats` | Check current state | Version, hooks, language, plan progress |
|
|
144
|
+
| `/claude-prism:update` | After `npm update` | Update rules and commands to latest |
|
|
138
145
|
| `/claude-prism:help` | Forgot commands | Quick reference |
|
|
139
146
|
|
|
140
147
|
### Workflow
|
|
@@ -237,7 +244,7 @@ Blocks commits if tests haven't been run in the last 5 minutes (configurable via
|
|
|
237
244
|
|
|
238
245
|
### debug-loop
|
|
239
246
|
|
|
240
|
-
|
|
247
|
+
Detects editing patterns on the same file. Distinguishes between **divergent** edits (same code area repeatedly — likely thrashing) and **convergent** edits (different areas like imports, logic, JSX — normal progressive work).
|
|
241
248
|
|
|
242
249
|
```json
|
|
243
250
|
{
|
|
@@ -252,9 +259,10 @@ Warns when the same file is edited 3 times in a row, blocks at 5 edits. Catches
|
|
|
252
259
|
```
|
|
253
260
|
|
|
254
261
|
**Behavior:**
|
|
255
|
-
- Tracks
|
|
256
|
-
-
|
|
257
|
-
-
|
|
262
|
+
- Tracks edit patterns using snippet analysis
|
|
263
|
+
- **Divergent pattern** (same area): warns at 3 edits, blocks at 5
|
|
264
|
+
- **Convergent pattern** (different areas): passes silently, downgrades block to warn
|
|
265
|
+
- Catches infinite debugging loops while allowing normal multi-area edits
|
|
258
266
|
|
|
259
267
|
### test-tracker
|
|
260
268
|
|
|
@@ -312,6 +320,9 @@ Tracks unique source files modified per session. Warns when scope grows without
|
|
|
312
320
|
- Agent thresholds (when OMC sub-agents running): warns at 8, blocks at 12
|
|
313
321
|
- Warning: "Consider running /claude-prism:prism to decompose the task"
|
|
314
322
|
- Block: "Run /claude-prism:prism to decompose before continuing"
|
|
323
|
+
- **Plan-aware**: When a plan file is created (`docs/plans/*.md`), thresholds are automatically doubled
|
|
324
|
+
- Standard with plan: warns at 8, blocks at 14
|
|
325
|
+
- Agent with plan: warns at 16, blocks at 24
|
|
315
326
|
|
|
316
327
|
## Configuration
|
|
317
328
|
|
|
@@ -401,7 +412,7 @@ prism stats
|
|
|
401
412
|
|
|
402
413
|
Output:
|
|
403
414
|
```
|
|
404
|
-
Version: v0.1
|
|
415
|
+
Version: v0.3.1
|
|
405
416
|
Language: en
|
|
406
417
|
Plans: 2 file(s)
|
|
407
418
|
OMC: ✅ v4.1.1
|
|
@@ -469,16 +480,21 @@ prism doctor # Shows OMC detection in diagnostics
|
|
|
469
480
|
|
|
470
481
|
This allows OMC agents (executor, architect, etc.) to modify more files per task without triggering scope warnings, recognizing that coordinated multi-agent work has different constraints than single-agent development.
|
|
471
482
|
|
|
472
|
-
##
|
|
483
|
+
## Verification Strategy
|
|
473
484
|
|
|
474
|
-
|
|
485
|
+
Prism uses context-aware verification — the right level of rigor for each file type:
|
|
475
486
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
487
|
+
| Path Pattern | Strategy | Escalation |
|
|
488
|
+
|---|---|---|
|
|
489
|
+
| `lib/`, `utils/`, `store/`, `hooks/`, `services/` | **TDD required** — failing test → implement → verify | Always TDD |
|
|
490
|
+
| `components/`, `pages/`, `views/` | **Build verification** — build passes + visual check | Escalate to TDD if complex logic |
|
|
491
|
+
| `config/`, `styles/`, `types/`, `*.json` | **Build/lint only** | Never |
|
|
480
492
|
|
|
481
|
-
|
|
493
|
+
**Core rules (all paths):**
|
|
494
|
+
1. Never claim completion without fresh verification evidence
|
|
495
|
+
2. Never commit code that doesn't build
|
|
496
|
+
3. TDD paths: write failing test → minimal code → verify
|
|
497
|
+
4. Build paths: run build/lint → confirm no regressions
|
|
482
498
|
|
|
483
499
|
## Design Philosophy
|
|
484
500
|
|
|
@@ -487,7 +503,7 @@ Prism is built on the insight that **AI needs structure more than humans do.** H
|
|
|
487
503
|
- Explicit understanding phase (no assumptions)
|
|
488
504
|
- Enforced decomposition (no mega-tasks)
|
|
489
505
|
- Batched execution with checkpoints (human in the loop)
|
|
490
|
-
- TDD
|
|
506
|
+
- Context-aware verification (TDD, build, or lint — matched to file type)
|
|
491
507
|
|
|
492
508
|
The prism metaphor: white light (complex problem) enters from one side and decomposes into a spectrum of colors (manageable units). Each color (unit) is individually verified, then recombined into a working whole.
|
|
493
509
|
|
package/bin/cli.mjs
CHANGED
|
@@ -35,6 +35,7 @@ if (hasFlag('version') || hasFlag('v')) {
|
|
|
35
35
|
|
|
36
36
|
const cwd = process.cwd();
|
|
37
37
|
|
|
38
|
+
try {
|
|
38
39
|
switch (command) {
|
|
39
40
|
case 'init': {
|
|
40
41
|
if (hasFlag('global')) {
|
|
@@ -49,6 +50,19 @@ switch (command) {
|
|
|
49
50
|
const language = getFlag('lang') || 'en';
|
|
50
51
|
const hooks = !hasFlag('no-hooks');
|
|
51
52
|
|
|
53
|
+
if (hasFlag('dry-run')) {
|
|
54
|
+
const { dryRun } = await import('../lib/installer.mjs');
|
|
55
|
+
const result = dryRun(cwd, { language, hooks });
|
|
56
|
+
console.log('🌈 claude-prism init --dry-run\n');
|
|
57
|
+
console.log(' Files that would be created/updated:\n');
|
|
58
|
+
for (const action of result.actions) {
|
|
59
|
+
const icon = action.status === 'create' ? '🆕' : '🔄';
|
|
60
|
+
console.log(` ${icon} [${action.status}] ${action.path}`);
|
|
61
|
+
}
|
|
62
|
+
console.log(`\n Total: ${result.actions.length} files`);
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
|
|
52
66
|
console.log('🌈 claude-prism init\n');
|
|
53
67
|
await init(cwd, { language, hooks });
|
|
54
68
|
|
|
@@ -182,8 +196,23 @@ Usage:
|
|
|
182
196
|
Options:
|
|
183
197
|
--lang=XX Language: en (default), ko, ja, zh
|
|
184
198
|
--no-hooks Skip enforcement hooks
|
|
199
|
+
--dry-run Show what init would do without making changes
|
|
185
200
|
--global Install/uninstall globally (all projects)
|
|
186
201
|
--ci Output JSON for CI integration
|
|
187
202
|
--version Show version`);
|
|
188
203
|
}
|
|
189
204
|
}
|
|
205
|
+
} catch (err) {
|
|
206
|
+
const msg = err.message || String(err);
|
|
207
|
+
process.stderr.write(`🌈 Prism Error: ${msg}\n`);
|
|
208
|
+
|
|
209
|
+
if (/EACCES|permission/i.test(msg)) {
|
|
210
|
+
process.stderr.write('💡 Check directory permissions\n');
|
|
211
|
+
} else if (/JSON|parse/i.test(msg)) {
|
|
212
|
+
process.stderr.write('💡 Config file may be corrupted. Try `prism reset` or delete .claude-prism.json\n');
|
|
213
|
+
} else if (/ENOENT.*package\.json/i.test(msg)) {
|
|
214
|
+
process.stderr.write('💡 Not in a project directory?\n');
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
process.exit(1);
|
|
218
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* claude-prism — Alignment Detection
|
|
3
|
+
* Detects scope drift and major unconfirmed decisions
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { readJsonState, writeJsonState } from '../lib/state.mjs';
|
|
7
|
+
import { getMessage } from '../lib/messages.mjs';
|
|
8
|
+
|
|
9
|
+
// Major decision patterns — commands that represent significant choices
|
|
10
|
+
const MAJOR_DECISION_PATTERNS = [
|
|
11
|
+
{ pattern: /\bnpm\s+install\b|\bpnpm\s+add\b|\byarn\s+add\b|\bbun\s+add\b/, label: 'package-install' },
|
|
12
|
+
{ pattern: /\bprisma\s+migrate\b|\bsequelize\b|\bknex\s+migrate\b/, label: 'db-migration' },
|
|
13
|
+
{ pattern: /\brm\s+-rf?\b|\brmdir\b/, label: 'destructive-delete' },
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
// Config files that represent major changes when modified
|
|
17
|
+
const MAJOR_CONFIG_FILES = [
|
|
18
|
+
'tsconfig.json', 'package.json', '.env', 'docker-compose.yml',
|
|
19
|
+
'Dockerfile', '.github/workflows', 'webpack.config', 'vite.config',
|
|
20
|
+
'next.config', 'tailwind.config',
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
export const alignment = {
|
|
24
|
+
name: 'alignment',
|
|
25
|
+
|
|
26
|
+
evaluate(ctx, config, stateDir) {
|
|
27
|
+
if (!config.enabled) return { type: 'pass' };
|
|
28
|
+
|
|
29
|
+
const messages = [];
|
|
30
|
+
|
|
31
|
+
// 1. Directory scope tracking
|
|
32
|
+
if (ctx.filePath) {
|
|
33
|
+
const dir = ctx.filePath.split('/').slice(0, -1).join('/') || '.';
|
|
34
|
+
let scopeDirs = readJsonState(stateDir, 'scope-directories') || [];
|
|
35
|
+
|
|
36
|
+
if (!scopeDirs.includes(dir)) {
|
|
37
|
+
// First 3 unique directories establish the "base scope"
|
|
38
|
+
if (scopeDirs.length < 3) {
|
|
39
|
+
scopeDirs.push(dir);
|
|
40
|
+
writeJsonState(stateDir, 'scope-directories', scopeDirs);
|
|
41
|
+
} else {
|
|
42
|
+
// New directory outside base scope — potential drift
|
|
43
|
+
const driftCount = parseInt(
|
|
44
|
+
(readJsonState(stateDir, 'drift-count') || 0).toString(), 10
|
|
45
|
+
) || 0;
|
|
46
|
+
const newDriftCount = driftCount + 1;
|
|
47
|
+
writeJsonState(stateDir, 'drift-count', newDriftCount);
|
|
48
|
+
|
|
49
|
+
if (newDriftCount >= (config.driftThreshold || 2)) {
|
|
50
|
+
return {
|
|
51
|
+
type: 'warn',
|
|
52
|
+
message: `🌈 Prism 🧭 Scope drift: editing ${dir} (outside base scope: ${scopeDirs.slice(0, 3).join(', ')}). Verify this is intended.`
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// 2. Major config file detection
|
|
59
|
+
const fileName = ctx.filePath.split('/').pop();
|
|
60
|
+
const isMajorConfig = MAJOR_CONFIG_FILES.some(f =>
|
|
61
|
+
ctx.filePath.includes(f) || fileName === f
|
|
62
|
+
);
|
|
63
|
+
if (isMajorConfig) {
|
|
64
|
+
messages.push(`🌈 Prism 🔧 Config change: ${fileName}. Ensure this was discussed with user.`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// 3. Major command detection
|
|
69
|
+
if (ctx.command) {
|
|
70
|
+
for (const { pattern, label } of MAJOR_DECISION_PATTERNS) {
|
|
71
|
+
if (pattern.test(ctx.command)) {
|
|
72
|
+
if (label === 'destructive-delete') {
|
|
73
|
+
return {
|
|
74
|
+
type: 'warn',
|
|
75
|
+
message: `🌈 Prism ⚠️ Destructive command detected: ${ctx.command.slice(0, 60)}. Confirm with user before proceeding.`
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
if (label === 'package-install') {
|
|
79
|
+
messages.push(`🌈 Prism 📦 New dependency being installed. Verify this was agreed upon.`);
|
|
80
|
+
}
|
|
81
|
+
if (label === 'db-migration') {
|
|
82
|
+
messages.push(`🌈 Prism 🗄️ Database migration detected. This is a major decision — confirm with user.`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (messages.length > 0) {
|
|
89
|
+
return { type: 'pass', message: messages.join('\n') };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return { type: 'pass' };
|
|
93
|
+
}
|
|
94
|
+
};
|
package/hooks/commit-guard.mjs
CHANGED
|
@@ -4,12 +4,14 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { readState } from '../lib/state.mjs';
|
|
7
|
+
import { getMessage } from '../lib/messages.mjs';
|
|
7
8
|
|
|
8
9
|
export const commitGuard = {
|
|
9
10
|
name: 'commit-guard',
|
|
10
11
|
|
|
11
12
|
evaluate(ctx, config, stateDir) {
|
|
12
13
|
const command = ctx.command || '';
|
|
14
|
+
const lang = config.language || 'en';
|
|
13
15
|
|
|
14
16
|
if (!command.includes('git commit')) return { type: 'pass' };
|
|
15
17
|
if (command.includes('--allow-empty')) return { type: 'pass' };
|
|
@@ -19,7 +21,7 @@ export const commitGuard = {
|
|
|
19
21
|
if (testResult !== null && testResult.trim() === 'fail') {
|
|
20
22
|
return {
|
|
21
23
|
type: 'block',
|
|
22
|
-
message: '
|
|
24
|
+
message: getMessage(lang, 'commit-guard.block.failed')
|
|
23
25
|
};
|
|
24
26
|
}
|
|
25
27
|
|
|
@@ -28,7 +30,7 @@ export const commitGuard = {
|
|
|
28
30
|
if (lastTestRaw === null) {
|
|
29
31
|
return {
|
|
30
32
|
type: 'warn',
|
|
31
|
-
message: '
|
|
33
|
+
message: getMessage(lang, 'commit-guard.warn.no-test')
|
|
32
34
|
};
|
|
33
35
|
}
|
|
34
36
|
|
|
@@ -39,7 +41,7 @@ export const commitGuard = {
|
|
|
39
41
|
if (diff > (config.maxTestAge || 300)) {
|
|
40
42
|
return {
|
|
41
43
|
type: 'warn',
|
|
42
|
-
message:
|
|
44
|
+
message: getMessage(lang, 'commit-guard.warn.stale', { minutes: Math.floor(diff / 60) })
|
|
43
45
|
};
|
|
44
46
|
}
|
|
45
47
|
|
package/hooks/debug-loop.mjs
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
|
|
6
6
|
import { createHash } from 'crypto';
|
|
7
7
|
import { readState, writeState, readJsonState, writeJsonState } from '../lib/state.mjs';
|
|
8
|
+
import { DEFAULTS, buildSourcePattern } from '../lib/config.mjs';
|
|
9
|
+
import { getMessage } from '../lib/messages.mjs';
|
|
8
10
|
|
|
9
11
|
export const debugLoop = {
|
|
10
12
|
name: 'debug-loop',
|
|
@@ -14,7 +16,8 @@ export const debugLoop = {
|
|
|
14
16
|
if (!filePath) return { type: 'pass' };
|
|
15
17
|
|
|
16
18
|
// Skip non-source files
|
|
17
|
-
|
|
19
|
+
const sourcePattern = buildSourcePattern(config.sourceExtensions || DEFAULTS.sourceExtensions);
|
|
20
|
+
if (!sourcePattern.test(filePath)) return { type: 'pass' };
|
|
18
21
|
|
|
19
22
|
const hash = createHash('md5').update(filePath).digest('hex').slice(0, 8);
|
|
20
23
|
const countKey = `edit-count-${hash}`;
|
|
@@ -34,6 +37,7 @@ export const debugLoop = {
|
|
|
34
37
|
writeJsonState(stateDir, logKey, editLog);
|
|
35
38
|
|
|
36
39
|
const name = filePath.split('/').pop();
|
|
40
|
+
const lang = config.language || 'en';
|
|
37
41
|
const pattern = count >= config.warnAt ? analyzePattern(editLog) : null;
|
|
38
42
|
|
|
39
43
|
if (count >= config.blockAt) {
|
|
@@ -41,12 +45,12 @@ export const debugLoop = {
|
|
|
41
45
|
if (pattern === 'divergent') {
|
|
42
46
|
return {
|
|
43
47
|
type: 'block',
|
|
44
|
-
message:
|
|
48
|
+
message: getMessage(lang, 'debug-loop.block.divergent', { name, count })
|
|
45
49
|
};
|
|
46
50
|
}
|
|
47
51
|
return {
|
|
48
52
|
type: 'warn',
|
|
49
|
-
message:
|
|
53
|
+
message: getMessage(lang, 'debug-loop.warn.convergent', { name, count })
|
|
50
54
|
};
|
|
51
55
|
}
|
|
52
56
|
|
|
@@ -55,7 +59,7 @@ export const debugLoop = {
|
|
|
55
59
|
if (pattern === 'divergent') {
|
|
56
60
|
return {
|
|
57
61
|
type: 'warn',
|
|
58
|
-
message:
|
|
62
|
+
message: getMessage(lang, 'debug-loop.warn.divergent', { name, count })
|
|
59
63
|
};
|
|
60
64
|
}
|
|
61
65
|
// Convergent edits (different areas) = normal progressive work → pass
|
|
@@ -68,8 +72,19 @@ export const debugLoop = {
|
|
|
68
72
|
function analyzePattern(log) {
|
|
69
73
|
if (log.length < 3) return null;
|
|
70
74
|
const recent = log.slice(-3).map(e => e.snippet);
|
|
75
|
+
|
|
76
|
+
// Filter out empty/whitespace snippets — can't determine pattern
|
|
77
|
+
if (recent.some(s => !s || !s.trim())) return null;
|
|
78
|
+
|
|
79
|
+
// Exact duplicates = clearly divergent (same code edited repeatedly)
|
|
71
80
|
const uniqueSnippets = new Set(recent).size;
|
|
72
81
|
if (uniqueSnippets === 1) return 'divergent';
|
|
73
|
-
|
|
82
|
+
|
|
83
|
+
// Check for meaningful overlap using longer window
|
|
84
|
+
const baseSnippet = recent[0].slice(0, 40);
|
|
85
|
+
// Skip overlap check if base is too short for meaningful comparison
|
|
86
|
+
if (baseSnippet.length < 10) return uniqueSnippets <= 2 ? 'divergent' : 'convergent';
|
|
87
|
+
|
|
88
|
+
const overlap = recent.filter(s => s.includes(baseSnippet)).length;
|
|
74
89
|
return overlap >= 2 ? 'divergent' : 'convergent';
|
|
75
90
|
}
|
package/hooks/scope-guard.mjs
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { readJsonState, writeJsonState } from '../lib/state.mjs';
|
|
7
|
+
import { DEFAULTS, buildSourcePattern, buildTestPattern } from '../lib/config.mjs';
|
|
8
|
+
import { getMessage } from '../lib/messages.mjs';
|
|
7
9
|
|
|
8
|
-
const SOURCE_PATTERN = /\.(ts|tsx|js|jsx|py|go|rs|java|c|cpp|h|svelte|vue)$/;
|
|
9
|
-
const TEST_PATTERN = /\.(test|spec|_test)\./;
|
|
10
10
|
const PLAN_PATTERN = /(?:^|\/)docs\/plans\/.*\.md$|(?:^|\/).*plan.*\.md$/i;
|
|
11
11
|
|
|
12
12
|
export const scopeGuard = {
|
|
@@ -17,16 +17,20 @@ export const scopeGuard = {
|
|
|
17
17
|
if (!filePath) return { type: 'pass' };
|
|
18
18
|
|
|
19
19
|
// Plan file created → mark plan as active (thresholds will be doubled)
|
|
20
|
+
const lang = config.language || 'en';
|
|
20
21
|
if (PLAN_PATTERN.test(filePath)) {
|
|
21
22
|
writeJsonState(stateDir, 'scope-has-plan', true);
|
|
22
|
-
return { type: 'pass', message: '
|
|
23
|
+
return { type: 'pass', message: getMessage(lang, 'scope-guard.plan-detected') };
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
// Only track source files
|
|
26
|
-
|
|
27
|
+
const sourcePattern = buildSourcePattern(config.sourceExtensions || DEFAULTS.sourceExtensions);
|
|
28
|
+
const testPattern = buildTestPattern(config.testPatterns || DEFAULTS.testPatterns);
|
|
29
|
+
|
|
30
|
+
if (!sourcePattern.test(filePath)) return { type: 'pass' };
|
|
27
31
|
|
|
28
32
|
// Don't count test files toward scope
|
|
29
|
-
if (
|
|
33
|
+
if (testPattern.test(filePath)) return { type: 'pass' };
|
|
30
34
|
|
|
31
35
|
// Track unique files
|
|
32
36
|
let files = readJsonState(stateDir, 'scope-files') || [];
|
|
@@ -52,14 +56,14 @@ export const scopeGuard = {
|
|
|
52
56
|
if (count >= blockAt) {
|
|
53
57
|
return {
|
|
54
58
|
type: 'block',
|
|
55
|
-
message:
|
|
59
|
+
message: getMessage(lang, 'scope-guard.block', { count })
|
|
56
60
|
};
|
|
57
61
|
}
|
|
58
62
|
|
|
59
63
|
if (count >= warnAt) {
|
|
60
64
|
return {
|
|
61
65
|
type: 'warn',
|
|
62
|
-
message:
|
|
66
|
+
message: getMessage(lang, 'scope-guard.warn', { count })
|
|
63
67
|
};
|
|
64
68
|
}
|
|
65
69
|
|