leerness 1.9.175 β†’ 1.9.177

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/CHANGELOG.md CHANGED
@@ -1,5 +1,111 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.9.177 β€” 2026-05-21
4
+
5
+ **πŸ” `task add` μžλ™ review-request trigger β€” μ‚¬μš©μž λͺ…μ‹œ 1.9.176 μžλ™ν™”.**
6
+
7
+ 자율 λͺ¨λ“œ 107 λΌμš΄λ“œ. 1.9.176 (μ‚¬μš©μž λͺ…μ‹œ: 무쑰건 κ΅¬ν˜„ μ „ 사전 κ²€ν† ) 의 μžλ™ν™” β€” μ‚¬μš©μž/AIκ°€ `task add` 호좜 μ‹œ reviewλ₯Ό 직접 μ‹€ν–‰ν•˜μ§€ μ•Šμ•„λ„ μžλ™ trigger.
8
+
9
+ ### 톡합 흐름
10
+ ```bash
11
+ $ leerness task add "OAuth 둜그인 κ΅¬ν˜„ν•΄μ€˜"
12
+ βœ“ task added: T-0001
13
+
14
+ πŸ” review-request (1.9.177 μžλ™): type=feature Β· βœ“ μ§„ν–‰ μ•ˆμ „ (705ms)
15
+ ꢌμž₯ 단계:
16
+ 1) leerness reuse find "<핡심 capability>" β€” 쀑볡 κ΅¬ν˜„ 사전 차단
17
+ 2) leerness plan add "<milestone>" β€” μ§„ν–‰ 좔적
18
+ ... +2건 (leerness review-request "OAuth 둜그인 κ΅¬ν˜„ν•΄μ€˜" 으둜 전체 보기)
19
+ πŸ’‘ πŸ‘₯ leerness agents recommend feature β€” μž‘μ—… μœ ν˜•λ³„ sub-agent λ§€ν•‘ ν™œμš© κ°€λŠ₯
20
+ ```
21
+
22
+ β†’ μ‚¬μš©μž/AIκ°€ λͺ…μ‹œμ μœΌλ‘œ `:review` 호좜 μ•ˆ 해도, `task add` 만으둜 μžλ™ 사전 κ²€ν†  μ™„λ£Œ. **1.9.176 μ‚¬μš©μž λͺ…μ‹œ μ˜λ„κ°€ default λ™μž‘μ— 톡합**.
23
+
24
+ ### Opt-out (3 κ°€μ§€)
25
+ 1. CLI ν”Œλž˜κ·Έ: `leerness task add "..." --no-review`
26
+ 2. ν™˜κ²½λ³€μˆ˜: `LEERNESS_NO_AUTO_REVIEW=1`
27
+ 3. 운영 메타: `--status done|dropped|blocked` (이미 μ’…λ£Œλœ μž‘μ—…μ€ review λΆˆν•„μš”)
28
+
29
+ ### ν‘œμ‹œ μ •μ±… (κ°„κ²°)
30
+ - 헀더 1쀄: `type=X Β· ⚠ N 좩돌 Β· πŸ” N μž¬μ‚¬μš© 후보 Β· βœ“ μ§„ν–‰ μ•ˆμ „ / ⚠ 확인 ν•„μš” (ms)`
31
+ - ꢌμž₯ 단계 첫 2건 (λ‚˜λ¨Έμ§€λŠ” `leerness review-request` 직접 호좜 μ•ˆλ‚΄)
32
+ - 효율 μ œμ•ˆ 1건 (κ°€μž₯ μ€‘μš”ν•œ hint)
33
+ - `proceed=false` μ‹œ ⚠ μ‚¬μœ  λ…ΈμΆœ
34
+
35
+ ### MCP ν˜Έν™˜μ„±
36
+ `leerness_task_add` (MCP) 호좜 μ‹œμ—λ„ μžλ™ review λ™μž‘ β€” μ™ΈλΆ€ AI (Claude/Codex/Gemini)κ°€ task 등둝 μ‹œ μžλ™μœΌλ‘œ 사전 κ²€ν†  κ²°κ³Ό λ°›μŒ.
37
+
38
+ ### μ„±λŠ₯
39
+ μ‹€μΈ‘: ~1.1초 task add (이전 ~30ms + review 1초 μΆ”κ°€). brainstorm/reuse-map 회수 λΉ„μš© β€” opt-out κ°€λŠ₯.
40
+
41
+ ### Verified
42
+ - e2e 217/217 baseline μœ μ§€
43
+ - stress-v122: **18/18** (taskAdd 톡합 4 + μ‹€ λ™μž‘ 7 + MCP ν˜Έν™˜ 1 + λˆ„μ  νšŒκ·€ 6)
44
+ - VERSION = 1.9.177 / autonomous-rounds = 107 / main μžλ™ push 38 λΌμš΄λ“œ 연속
45
+
46
+ ---
47
+
48
+ ## 1.9.176 β€” 2026-05-21
49
+
50
+ **⚠ μ‚¬μš©μž λͺ…μ‹œ: `leerness review-request` β€” μ‚¬μš©μž μš”κ΅¬λ₯Ό 무쑰건 κ΅¬ν˜„ μ „ 사전 κ²€ν† .**
51
+
52
+ μ‚¬μš©μž λͺ…μ‹œ: *"leernessκ°€ 적용된 ν”„λ‘œμ νŠΈλŠ” μ‚¬μš©μžμ˜ μš”κ΅¬λ₯Ό 무쑰건적으둜 κ΅¬ν˜„ν•˜κΈ° 전에, 좩돌이 λ°œμƒν•  수 μžˆλŠ” λΆ€λΆ„μ΄λ‚˜ μ œμž‘ν•˜κ³ μž ν•˜λŠ” κΈ°λŠ₯ 등을 κ΅¬ν˜„ν•˜κ±°λ‚˜ 섀계할 λ•Œ 더 효율적인 단계가 μžˆλŠ”μ§€ 검토해보고 μ œμ‹œν•  μˆ˜λ„ μžˆλ„λ‘ 섀계"*.
53
+
54
+ ### `leerness review-request "<request>"` β€” 9개 μ‹ ν˜Έ 뢄석
55
+ | μ‹ ν˜Έ | 데이터 μ†ŒμŠ€ |
56
+ |---|---|
57
+ | **estimatedType** | route ν‚€μ›Œλ“œ λ§€ν•‘ (feature/bugfix/refactor/research/planning/release/consistency) |
58
+ | **conflicts** | lessons μ‹€νŒ¨ νŒ¨ν„΄ + μ§„ν–‰ 쀑 task + taskLogFails |
59
+ | **reuseCandidates** | skills λ§€μΉ­ + reuse-map ν‚€μ›Œλ“œ 검색 |
60
+ | **lessonsRecall** | κ³Όκ±° decisions + κ΄€λ ¨ lessons |
61
+ | **planConflicts** | μ§„ν–‰ 쀑 milestone (plan.md) |
62
+ | **featureConflicts** | feature_graph.md μ˜μ—­ κ²ΉμΉ¨ |
63
+ | **recommendedSteps** | μž‘μ—… μœ ν˜•λ³„ 3-4단계 ꢌμž₯ 흐름 |
64
+ | **efficiencyHints** | μž¬μ‚¬μš©/sub-agent/skill ν™œμš© μ œμ•ˆ |
65
+ | **proceed** | true (μ•ˆμ „) / false (μ‚¬μš©μž 확인 ν•„μš”) |
66
+
67
+ ### μ‚¬μš© 예
68
+ ```bash
69
+ $ leerness review-request "OAuth 둜그인 κ΅¬ν˜„ν•΄μ€˜"
70
+ # leerness review-request (1.9.176 사전 κ²€ν† )
71
+ μš”μ²­: "OAuth 둜그인 κ΅¬ν˜„ν•΄μ€˜"
72
+ μΆ”μ • μž‘μ—… μœ ν˜•: feature
73
+
74
+ ## πŸ’‘ 효율 μ œμ•ˆ
75
+ πŸ‘₯ leerness agents recommend feature β€” μž‘μ—… μœ ν˜•λ³„ sub-agent λ§€ν•‘ ν™œμš© κ°€λŠ₯
76
+
77
+ ## πŸ“ ꢌμž₯ 단계 (feature)
78
+ 1) leerness reuse find "<핡심 capability>" β€” 쀑볡 κ΅¬ν˜„ 사전 차단
79
+ 2) leerness plan add "<milestone>" β€” μ§„ν–‰ 좔적
80
+ 3) leerness contract verify SPEC.md src/<mod>.js β€” 사양 ↔ κ΅¬ν˜„ 일치 검증
81
+ 4) verify-claim --run-tests 둜 evidence μ˜λ¬΄ν™”
82
+
83
+ ## β–Ά μ§„ν–‰ ꢌμž₯: βœ“ μ§„ν–‰ μ•ˆμ „
84
+ μ‚¬μœ : μ•ˆμ „ β€” 좩돌 μ‹ ν˜Έ < 3 + plan 좩돌 0
85
+ 뢄석 μ†Œμš”: 938ms
86
+ ```
87
+
88
+ ### 톡합 β€” 3 μ§„μž…μ 
89
+ - **CLI**: `leerness review-request "<request>"` (λ˜λŠ” 단좕 `review-req`)
90
+ - **REPL**: `:review "<request>"` slash (1.9.175 흐름 μ—°μž₯)
91
+ - **MCP**: `leerness_review_request` (μ™ΈλΆ€ AI 직접 호좜 β€” **54번째 도ꡬ**)
92
+
93
+ ### AGENTS.md / CLAUDE.md κ°•μ œ μ•ˆλ‚΄
94
+ ```markdown
95
+ ## ⚠ μ‚¬μš©μž μš”μ²­ 사전 κ²€ν†  의무 (1.9.176 β€” μ‚¬μš©μž λͺ…μ‹œ)
96
+ **μ‚¬μš©μžκ°€ "X κ΅¬ν˜„ν•΄μ€˜ / X λ§Œλ“€μ–΄μ€˜ / X μΆ”κ°€ν•΄μ€˜" 같은 μš”μ²­μ„ 쀬을 λ•Œ 무쑰건 μ¦‰μ‹œ κ΅¬ν˜„ν•˜μ§€ 말 것.**
97
+ λ¨Όμ € `leerness review-request "<μš”μ²­>"` 호좜 β†’ 뢄석 κ²°κ³Ό ν‘œμ‹œ β†’ μ‚¬μš©μž 확인 ν›„ κ΅¬ν˜„.
98
+ "κ·Έλƒ₯ λ°”λ‘œ ν•΄μ€˜ / review κ±΄λ„ˆλ›°μ–΄μ€˜" λͺ…μ‹œ μ˜΅νŠΈμ•„μ›ƒ μ‹œμ—λ§Œ review μƒλž΅.
99
+ ```
100
+
101
+ ### Verified
102
+ - e2e 217/217 baseline μœ μ§€
103
+ - stress-v121: **23/23** (ν•¨μˆ˜ μ •μ˜ 4 + router/help 2 + μ‹€ λ™μž‘ 6 + REPL/MCP 3 + metadata 2 + λˆ„μ  νšŒκ·€ 6)
104
+ - μž‘μ—… μœ ν˜• μΆ”μ • 정확도: feature/bugfix/refactor/research 4μ’… 100% λ§€μΉ­
105
+ - VERSION = 1.9.176 Β· MCP **54 도ꡬ** Β· autonomous-rounds = 106 Β· main μžλ™ push 37 λΌμš΄λ“œ 연속
106
+
107
+ ---
108
+
3
109
  ## 1.9.175 β€” 2026-05-21
4
110
 
5
111
  **πŸŒ‰ REPL Bridge Slash 3μ’… β€” `:web` / `:pc` / `:lsp` μ¦‰μ‹œ 호좜.**
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  > **AI μ½”λ”© μ—μ΄μ „νŠΈμ˜ κ±°μ§“ μ™„λ£ŒΒ·μ€‘λ³΅Β·λ§κ°Β·μΆ©λŒμ„ λ§‰μ•„μ£ΌλŠ” κ²€μˆ˜Β·κΈ°μ–΅Β·ν˜‘μ—… CLI ν•˜λ„€μŠ€.**
4
4
 
5
- [![npm](https://img.shields.io/badge/npm-leerness-blue)](https://www.npmjs.com/package/leerness) [![version](https://img.shields.io/badge/version-1.9.175-green)]() [![tests](https://img.shields.io/badge/e2e-217%2F217-success)]() [![stress](https://img.shields.io/badge/stress--v120-17%2F17-success)]() [![mcp](https://img.shields.io/badge/MCP--tools-53-brightgreen)]() [![rounds](https://img.shields.io/badge/autonomous--rounds-105-blueviolet)]() [![main-push](https://img.shields.io/badge/release--main--push-36_rounds-success)]() [![repl-slash](https://img.shields.io/badge/REPL_slash-:web%2F:pc%2F:lsp_μ¦‰μ‹œ_호좜-success)]() [![repl-perm](https://img.shields.io/badge/REPL_:permissions-μ¦‰μ‹œ_λ³€κ²½-success)]() [![lsp-multi](https://img.shields.io/badge/LSP_λ‹€κ΅­μ–΄-JS%2FPython%2FGo%2FRust%2FJava-success)]() [![capability](https://img.shields.io/badge/6_capability-72%25_production--ready-brightgreen)]() [![sandbox](https://img.shields.io/badge/runCommandSafe-cwd_jail%2Benv_scrub-success)]() [![license](https://img.shields.io/badge/license-MIT-lightgrey)]()
5
+ [![npm](https://img.shields.io/badge/npm-leerness-blue)](https://www.npmjs.com/package/leerness) [![version](https://img.shields.io/badge/version-1.9.177-green)]() [![tests](https://img.shields.io/badge/e2e-217%2F217-success)]() [![stress](https://img.shields.io/badge/stress--v122-18%2F18-success)]() [![mcp](https://img.shields.io/badge/MCP--tools-54-brightgreen)]() [![rounds](https://img.shields.io/badge/autonomous--rounds-107-blueviolet)]() [![main-push](https://img.shields.io/badge/release--main--push-38_rounds-success)]() [![auto-review](https://img.shields.io/badge/task_add-μžλ™_review_trigger-success)]() [![review-request](https://img.shields.io/badge/review--request-사전_κ²€ν† _9_μ‹ ν˜Έ-success)]() [![repl-slash](https://img.shields.io/badge/REPL_slash-:web%2F:pc%2F:lsp%2F:review-success)]() [![capability](https://img.shields.io/badge/6_capability-72%25_production--ready-brightgreen)]() [![sandbox](https://img.shields.io/badge/runCommandSafe-cwd_jail%2Benv_scrub-success)]() [![license](https://img.shields.io/badge/license-MIT-lightgrey)]()
6
6
 
7
7
  ```
8
8
  ╔══════════════════════════════════════════════════════════════╗
@@ -12,9 +12,9 @@
12
12
  β•‘ β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•”β•β•β• β–ˆβ–ˆβ•”β•β•β• β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β• β•šβ•β•β•β•β–ˆβ–ˆβ•‘ β•‘
13
13
  β•‘ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘ β•šβ–ˆβ–ˆβ–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•‘ β•‘
14
14
  β•‘ β•šβ•β•β•β•β•β•β•β•šβ•β•β•β•β•β•β•β•šβ•β•β•β•β•β•β•β•šβ•β• β•šβ•β•β•šβ•β• β•šβ•β•β•β•β•šβ•β•β•β•β•β•β•β•šβ•β•β•β•β•β•β• β•‘
15
- β•‘ v1.9.175 AI Agent Reliability Harness + Sandbox β•‘
15
+ β•‘ v1.9.177 AI Agent Reliability Harness + Sandbox β•‘
16
16
  β•‘ verify Β· remember Β· orchestrate Β· audit Β· sandbox Β· drift β•‘
17
- β•‘ πŸŒ‰ REPL :web/:pc/:lsp slash β€” Bridge 3μ’… μ¦‰μ‹œ 호좜 β•‘
17
+ β•‘ πŸ” task add μžλ™ review trigger β€” default 사전 κ²€ν†  β•‘
18
18
  β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
19
19
  ```
20
20
 
package/bin/harness.js CHANGED
@@ -6,7 +6,7 @@ const path = require('path');
6
6
  const cp = require('child_process');
7
7
  const readline = require('readline');
8
8
 
9
- const VERSION = '1.9.175';
9
+ const VERSION = '1.9.177';
10
10
  const MARK = '<!-- leerness:managed -->';
11
11
  const README_START = '<!-- leerness:project-readme:start -->';
12
12
  const README_END = '<!-- leerness:project-readme:end -->';
@@ -1600,6 +1600,42 @@ function taskAdd(root, text) {
1600
1600
  upsertProgress(root, { id, status: arg('--status','requested'), request: text, evidence: arg('--evidence','user-request'), nextAction: arg('--next','λ‹€μŒ μ•‘μ…˜ μž‘μ„±') });
1601
1601
  ok(`task added: ${id}`);
1602
1602
  _autoRoadmap(absRoot(root), 'data-change');
1603
+ // 1.9.177: task add μžλ™ review-request trigger (μ‚¬μš©μž λͺ…μ‹œ 1.9.176 μžλ™ν™”).
1604
+ // μ‚¬μš©μžκ°€ task μΆ”κ°€ μ‹œ μžλ™μœΌλ‘œ 사전 κ²€ν†  β€” 좩돌/μž¬μ‚¬μš©/효율/ꢌμž₯ 단계 κ²°κ³Όλ₯Ό 등둝 직후 ν‘œμ‹œ.
1605
+ // opt-out: --no-review λ˜λŠ” env LEERNESS_NO_AUTO_REVIEW=1, λ˜λŠ” status=in-progress/done 같은 운영 메타.
1606
+ if (!has('--no-review')
1607
+ && process.env.LEERNESS_NO_AUTO_REVIEW !== '1'
1608
+ && !/^(done|dropped|blocked)$/i.test(arg('--status', 'requested'))
1609
+ && text && text.trim()) {
1610
+ try {
1611
+ const t0 = Date.now();
1612
+ const r = cp.spawnSync(process.execPath, [__filename, 'review-request', text, '--path', absRoot(root), '--json'], {
1613
+ encoding: 'utf8', timeout: 15000,
1614
+ env: { ...process.env, LEERNESS_NO_BANNER: '1', LEERNESS_NO_PROMPT: '1', LEERNESS_NO_DRIFT_CHECK: '1' }
1615
+ });
1616
+ if (r.status === 0 && r.stdout) {
1617
+ const j = JSON.parse(r.stdout);
1618
+ log('');
1619
+ log(`πŸ” review-request (1.9.177 μžλ™): type=${j.estimatedType}` +
1620
+ (j.conflicts.length ? ` · ⚠ ${j.conflicts.length} 좩돌` : '') +
1621
+ (j.reuseCandidates.length ? ` Β· πŸ” ${j.reuseCandidates.length} μž¬μ‚¬μš© 후보` : '') +
1622
+ ` Β· ${j.proceed ? 'βœ“ μ§„ν–‰ μ•ˆμ „' : '⚠ 확인 ν•„μš”'} (${Date.now() - t0}ms)`);
1623
+ // ꢌμž₯ 단계 첫 2건 λ…ΈμΆœ
1624
+ if (j.recommendedSteps && j.recommendedSteps.length) {
1625
+ log(' ꢌμž₯ 단계:');
1626
+ j.recommendedSteps.slice(0, 2).forEach(s => log(' ' + s));
1627
+ if (j.recommendedSteps.length > 2) log(` ... +${j.recommendedSteps.length - 2}건 (leerness review-request "${text.slice(0, 30)}" 으둜 전체 보기)`);
1628
+ }
1629
+ // 효율 μ œμ•ˆ 1건만
1630
+ if (j.efficiencyHints && j.efficiencyHints.length) {
1631
+ log(` πŸ’‘ ${j.efficiencyHints[0]}`);
1632
+ }
1633
+ if (!j.proceed) {
1634
+ log(` ⚠ ${j.proceedReason}`);
1635
+ }
1636
+ }
1637
+ } catch {} // review μ‹€νŒ¨λŠ” task add μžμ²΄μ— 영ν–₯ X
1638
+ }
1603
1639
  }
1604
1640
  function taskUpdate(root, id) {
1605
1641
  if (!id) return fail('id required (e.g., task update T-0001 --status in-progress)');
@@ -9845,7 +9881,8 @@ function mcpServeCmd(root) {
9845
9881
  { name: 'leerness_provider_remove', description: '1.9.159 β€” Provider Registry μ—μ„œ μ‚¬μš©μž μ •μ˜ provider 제거. 인자: { id (required), path? }. 빌트인 5μ’… id λŠ” 제거 λΆˆκ°€ (override 만 제거 κ°€λŠ₯). πŸŽ‰ MCP 50 도ꡬ λ§ˆμΌμŠ€ν†€ β€” Provider Registry CRUD MCP μ™„μ„± (list/add/remove)', inputSchema: { type: 'object', properties: { id: { type: 'string' }, path: { type: 'string' } }, required: ['id'] } },
9846
9882
  { name: 'leerness_web', description: '1.9.168 β€” Web Bridge (1.9.165 playwright opt-in). sub: check (μ„€μΉ˜ + permissions.browser 확인) | screenshot (URL β†’ PNG) | extract (URL + CSS selector β†’ DOM ν…μŠ€νŠΈ). μ™ΈλΆ€ AIκ°€ leerness 의 μ›Ή μžλ™ν™” λŠ₯λ ₯을 직접 호좜. playwright λ―Έμ„€μΉ˜ μ‹œ 친절 μ•ˆλ‚΄ (graceful). 인자: { sub (required), url?, out?, selector?, path? }', inputSchema: { type: 'object', properties: { sub: { type: 'string', enum: ['check', 'screenshot', 'extract'] }, url: { type: 'string' }, out: { type: 'string' }, selector: { type: 'string' }, path: { type: 'string' } }, required: ['sub'] } },
9847
9883
  { name: 'leerness_pc', description: '1.9.168 β€” PC Bridge (1.9.166 robotjs/nut-tree opt-in). sub: check (μ„€μΉ˜ + permissions.mouse/keyboard) | click (x,y) | type (text) | screenshot (out). ⚠ full permissions ꢌμž₯ (mouse/keyboard μ ‘κ·Ό). μ™ΈλΆ€ AIκ°€ λ°μŠ€ν¬νƒ‘ μžλ™ν™” λŠ₯λ ₯을 직접 호좜. 인자: { sub (required), x?, y?, text?, out?, path? }', inputSchema: { type: 'object', properties: { sub: { type: 'string', enum: ['check', 'click', 'type', 'screenshot'] }, x: { type: 'number' }, y: { type: 'number' }, text: { type: 'string' }, out: { type: 'string' }, path: { type: 'string' } }, required: ['sub'] } },
9848
- { name: 'leerness_lsp', description: '1.9.168 β€” LSP Bridge (1.9.167 typescript opt-in + regex fallback). sub: check (μ„€μΉ˜ μ—¬λΆ€) | symbols (file β†’ function/class/interface/type/enum λͺ©λ‘) | references (name + in 디렉토리 β†’ 호좜 μœ„μΉ˜). μ™ΈλΆ€ AIκ°€ μ½”λ“œ μΈν…”λ¦¬μ „μŠ€λ₯Ό 직접 호좜 (μ˜μ‘΄μ„± 0 fallback λ™μž‘). πŸŽ‰ MCP 53 도ꡬ λ§ˆμΌμŠ€ν†€. 인자: { sub (required), file?, name?, in?, path? }', inputSchema: { type: 'object', properties: { sub: { type: 'string', enum: ['check', 'symbols', 'references'] }, file: { type: 'string' }, name: { type: 'string' }, in: { type: 'string' }, path: { type: 'string' } }, required: ['sub'] } }
9884
+ { name: 'leerness_lsp', description: '1.9.168 β€” LSP Bridge (1.9.167 typescript opt-in + regex fallback). sub: check (μ„€μΉ˜ μ—¬λΆ€) | symbols (file β†’ function/class/interface/type/enum λͺ©λ‘) | references (name + in 디렉토리 β†’ 호좜 μœ„μΉ˜). μ™ΈλΆ€ AIκ°€ μ½”λ“œ μΈν…”λ¦¬μ „μŠ€λ₯Ό 직접 호좜 (μ˜μ‘΄μ„± 0 fallback λ™μž‘). πŸŽ‰ MCP 53 도ꡬ λ§ˆμΌμŠ€ν†€. 인자: { sub (required), file?, name?, in?, path? }', inputSchema: { type: 'object', properties: { sub: { type: 'string', enum: ['check', 'symbols', 'references'] }, file: { type: 'string' }, name: { type: 'string' }, in: { type: 'string' }, path: { type: 'string' } }, required: ['sub'] } },
9885
+ { name: 'leerness_review_request', description: '1.9.176 β€” μ‚¬μš©μž μš”μ²­ 사전 κ²€ν†  (μ‚¬μš©μž λͺ…μ‹œ μš”μ²­). AI μ—μ΄μ „νŠΈκ°€ μ‚¬μš©μž μš”κ΅¬λ₯Ό **무쑰건 κ΅¬ν˜„ μ „**에 호좜. 뢄석: 1) estimatedType (route μΆ”μ •), 2) conflicts (lesson μ‹€νŒ¨/진행쀑 task), 3) reuseCandidates (skill/reuse-map λ§€μΉ­), 4) lessonsRecall (κ³Όκ±° κ²°μ •), 5) planConflicts (진행쀑 milestone), 6) featureConflicts (feature graph μ˜μ—­ κ²ΉμΉ¨), 7) recommendedSteps (μž‘μ—… μœ ν˜•λ³„ 3-5 단계), 8) efficiencyHints, 9) proceed (true/false). μ‚¬μš©μž κ²°μ • 도움. 인자: { request (required), path? }', inputSchema: { type: 'object', properties: { request: { type: 'string' }, path: { type: 'string' } }, required: ['request'] } }
9849
9886
  ];
9850
9887
 
9851
9888
  function send(obj) {
@@ -9973,6 +10010,10 @@ function mcpServeCmd(root) {
9973
10010
  if (args.name) cliArgs.splice(2, 0, String(args.name));
9974
10011
  if (args.in) cliArgs.push('--in', String(args.in));
9975
10012
  break;
10013
+ case 'leerness_review_request':
10014
+ // 1.9.176: μ‚¬μš©μž μš”μ²­ 사전 κ²€ν†  (μ‚¬μš©μž λͺ…μ‹œ μš”μ²­)
10015
+ cliArgs = ['review-request', String(args.request || ''), '--path', targetPath, '--json'];
10016
+ break;
9976
10017
  default:
9977
10018
  return send({ jsonrpc: '2.0', id, error: { code: -32601, message: `Unknown tool: ${name}` } });
9978
10019
  }
@@ -11007,6 +11048,7 @@ async function _agentRepl(root, opts) {
11007
11048
  log(C.dim(' πŸ†• 1.9.170 β€” Tab=provider cycle, Shift+Tab=model cycle, :stream on|off (μ‹€μ‹œκ°„ 좜λ ₯)'));
11008
11049
  log(C.dim(' πŸ†• 1.9.174 β€” :permissions [basic|extended|full] 둜 μ¦‰μ‹œ κΆŒν•œ λ³€κ²½ (default: basic μ•ˆμ „)'));
11009
11050
  log(C.dim(' πŸ†• 1.9.175 β€” :web / :pc / :lsp 으둜 Bridge 3μ’… REPL μ•ˆμ—μ„œ μ¦‰μ‹œ 호좜 (μ½”λ“œ 뢄석/μ›Ή/PC)'));
11051
+ log(C.dim(' πŸ†• 1.9.176 β€” :review "<μš”μ²­>" 으둜 μ‚¬μš©μž μš”μ²­ 사전 κ²€ν†  (좩돌/μž¬μ‚¬μš©/효율/ꢌμž₯ 단계)'));
11010
11052
  log(C.dim(` ν˜„μž¬ β€” provider=${state.provider} model=${state.model || '(κΈ°λ³Έ)'} role=${state.role} permissions=${_readPermissions(root).mode}`));
11011
11053
  // 1.9.155: REPL μ§„μž… μ‹œ handoff μ»¨ν…μŠ€νŠΈ μžλ™ λ…ΈμΆœ (UX κ°œμ„  β€” μ‚¬μš©μžκ°€ 맀번 :handoff μ•ˆ 해도 μ»¨ν…μŠ€νŠΈ 인지)
11012
11054
  try {
@@ -11127,6 +11169,8 @@ async function _agentRepl(root, opts) {
11127
11169
  log(' :brainstorm <topic> β€” leerness brainstorm "topic" (κ΄€λ ¨ μ»¨ν…μŠ€νŠΈ 회수)');
11128
11170
  log(' :tasks β€” leerness task list (ν˜„μž¬ task μƒνƒœ)');
11129
11171
  log(' :plan β€” leerness plan show (ν˜„μž¬ milestone)');
11172
+ log(C.bold('\n πŸ†• Review Slash (1.9.176) β€” μ‚¬μš©μž μš”μ²­ 사전 κ²€ν† :'));
11173
+ log(' :review "<request>" β€” 좩돌/μž¬μ‚¬μš©/효율/ꢌμž₯ 단계 뢄석 ν›„ κ΅¬ν˜„ ꢌμž₯');
11130
11174
  log(C.bold('\n πŸ†• Bridge Slash (1.9.175) β€” REPL μ•ˆμ—μ„œ μ¦‰μ‹œ Bridge 호좜:'));
11131
11175
  log(' :web check β€” playwright μ„€μΉ˜ 확인 (opt-in)');
11132
11176
  log(' :web screenshot <url> [--out f.png] β€” URL β†’ PNG');
@@ -11317,6 +11361,27 @@ async function _agentRepl(root, opts) {
11317
11361
  return false;
11318
11362
  }
11319
11363
 
11364
+ // 1.9.176: :review <request> β€” μ‚¬μš©μž μš”μ²­ 사전 κ²€ν†  slash (μ‚¬μš©μž λͺ…μ‹œ)
11365
+ // "이거 κ΅¬ν˜„ν•΄μ€˜" 같은 μš”μ²­μ„ λ°›μœΌλ©΄ AI κ°€ λ¨Όμ € :review 호좜 β†’ 좩돌/μž¬μ‚¬μš©/효율 뢄석.
11366
+ if (op === 'review') {
11367
+ const reqText = rest.join(' ').trim();
11368
+ if (!reqText) {
11369
+ log(C.yel(` ⚠ :review λŠ” μš”μ²­ ν…μŠ€νŠΈ ν•„μš” β€” 예: :review "OAuth 둜그인 μΆ”κ°€"`));
11370
+ return false;
11371
+ }
11372
+ log(C.dim(` β†’ leerness review-request "${reqText.slice(0, 60)}${reqText.length > 60 ? '…' : ''}"`));
11373
+ const t0 = Date.now();
11374
+ const r = runCommandSafe(process.execPath, [__filename, 'review-request', reqText, '--path', root], {
11375
+ cwd: root, root, timeout: 30000, kind: 'agent_repl_slash', label: 'repl-review',
11376
+ env: { LEERNESS_NO_BANNER: '1', LEERNESS_NO_PROMPT: '1', LEERNESS_NO_DRIFT_CHECK: '1' }
11377
+ });
11378
+ const dt = Date.now() - t0;
11379
+ if (r.stdout) log(r.stdout.trim().split('\n').slice(0, 60).join('\n'));
11380
+ if (r.status === 0) log(C.green(` βœ“ :review μ™„λ£Œ (${dt}ms)`));
11381
+ else log(C.yel(` ⚠ :review μ‹€νŒ¨ (exit ${r.status}, ${dt}ms)`));
11382
+ return false;
11383
+ }
11384
+
11320
11385
  // 1.9.150: leerness λ‚΄λΆ€ λͺ…λ Ή slash-commands β€” :verify / :audit / :handoff / :health
11321
11386
  // 1.9.161: Memory Surface 쑰회 slash μΆ”κ°€ β€” :lessons / :brainstorm / :tasks / :plan
11322
11387
  if (op === 'verify' || op === 'audit' || op === 'handoff' || op === 'health'
@@ -12825,6 +12890,274 @@ function lspCmd(root, sub, ...args) {
12825
12890
  fail(`μ•Œ 수 μ—†λŠ” sub: ${sub} (check / symbols / references)`);
12826
12891
  }
12827
12892
 
12893
+ // 1.9.176: μ‚¬μš©μž μš”μ²­ 사전 κ²€ν†  (μ‚¬μš©μž λͺ…μ‹œ μš”μ²­)
12894
+ // AI μ—μ΄μ „νŠΈκ°€ μ‚¬μš©μž μš”κ΅¬λ₯Ό **무쑰건 κ΅¬ν˜„ν•˜κΈ° μ „**에 λ¨Όμ €:
12895
+ // 1) 좩돌 μœ„ν—˜ (같은 ν‚€μ›Œλ“œμ˜ κ³Όκ±° μ‹€νŒ¨/lessons)
12896
+ // 2) κΈ°μ‘΄ μžμ› μž¬μ‚¬μš© 후보 (reuse-map / skills)
12897
+ // 3) 더 효율적인 단계 μ œμ•ˆ (route + plan)
12898
+ // 4) ꢌμž₯ 단계 리슀트 (μž‘μ—… μœ ν˜•λ³„)
12899
+ // λ₯Ό λΆ„μ„ν•˜μ—¬ μ‚¬μš©μžμ—κ²Œ μ œμ‹œ. μ‚¬μš©μž κ²°μ • ν›„ κ΅¬ν˜„ μ§„ν–‰.
12900
+ //
12901
+ // μ‚¬μš© 예:
12902
+ // leerness review-request "OAuth 둜그인 κ΅¬ν˜„ν•΄μ€˜"
12903
+ // leerness review-request "..." --json # MCP/μ™ΈλΆ€ AI 톡합
12904
+ //
12905
+ // REPL: :review <request> (1.9.175 slash νŒ¨ν„΄)
12906
+ // MCP : leerness_review_request (μ™ΈλΆ€ AI 직접 호좜)
12907
+ function reviewRequestCmd(root, request) {
12908
+ root = absRoot(root || process.cwd());
12909
+ if (!request || !String(request).trim()) {
12910
+ return fail('leerness review-request "<request>" β€” μ‚¬μš©μž μš”μ²­ ν…μŠ€νŠΈ ν•„μš”');
12911
+ }
12912
+ const t0 = Date.now();
12913
+ const text = String(request).trim();
12914
+
12915
+ // 1) μž‘μ—… μœ ν˜• μΆ”μ • (route 기반 ν‚€μ›Œλ“œ λ§€ν•‘)
12916
+ const lower = text.toLowerCase();
12917
+ const routeKw = {
12918
+ bugfix: ['버그', '였λ₯˜', 'μ—λŸ¬', 'μˆ˜μ •', '고쳐', 'μ‹€νŒ¨', 'fix', 'bug', 'error'],
12919
+ refactor: ['λ¦¬νŒ©ν† ', 'μž¬κ΅¬μ„±', '정리', 'κ°œμ„ ', 'refactor', 'cleanup'],
12920
+ feature: ['μΆ”κ°€', 'κ΅¬ν˜„', 'λ§Œλ“€', 'μƒˆ', 'κΈ°λŠ₯', 'add', 'implement', 'feature', 'create', 'new'],
12921
+ research: ['쑰사', '뢄석', '비ꡐ', 'κ²€ν† ', '연ꡬ', 'research', 'analyze', 'compare', 'investigate'],
12922
+ planning: ['κ³„νš', '섀계', 'λ‘œλ“œλ§΅', 'plan', 'design', 'architecture', 'roadmap'],
12923
+ release: ['배포', '릴리즈', '버전', 'release', 'deploy', 'publish'],
12924
+ consistency: ['일관성', '톡합', '동기화', '맞좰', 'consistency', 'sync', 'align']
12925
+ };
12926
+ let estimatedType = 'feature'; // default
12927
+ let maxScore = 0;
12928
+ for (const [type, kws] of Object.entries(routeKw)) {
12929
+ const score = kws.filter(k => lower.includes(k)).length;
12930
+ if (score > maxScore) { maxScore = score; estimatedType = type; }
12931
+ }
12932
+
12933
+ // 2) κΈ°μ‘΄ μžμ› 회수 β€” brainstorm spawn (λͺ¨λ“  surface 톡합 회수)
12934
+ const conflictHints = []; // ⚠ 같은 ν‚€μ›Œλ“œ + μ‹€νŒ¨/였λ₯˜ νŒ¨ν„΄
12935
+ const reuseCandidates = []; // πŸ” κΈ°μ‘΄ skill / reuse-map / decision 후보
12936
+ const lessonsRecall = []; // 🧠 과거 lesson
12937
+ const planConflicts = []; // πŸ“‹ μ§„ν–‰ 쀑 milestoneκ³Ό 좩돌 κ°€λŠ₯
12938
+
12939
+ // brainstorm 호좜 (1.9.13~) β€” JSON κ²°κ³Ό 회수
12940
+ try {
12941
+ const r = cp.spawnSync(process.execPath, [__filename, 'brainstorm', text, '--path', root, '--json'], {
12942
+ encoding: 'utf8', timeout: 12000,
12943
+ env: { ...process.env, LEERNESS_NO_BANNER: '1', LEERNESS_NO_PROMPT: '1', LEERNESS_NO_DRIFT_CHECK: '1' }
12944
+ });
12945
+ if (r.stdout) {
12946
+ const j = JSON.parse(r.stdout);
12947
+ const hits = j.hits || {};
12948
+ // decisions β€” κ³Όκ±° κ²°μ • 후보
12949
+ (hits.decisions || []).slice(0, 5).forEach(d => {
12950
+ lessonsRecall.push({ kind: 'decision', title: d.title, line: d.line, preview: (d.preview || '').slice(0, 100) });
12951
+ });
12952
+ // lessons β€” κ³Όκ±° κ΅ν›ˆ (특히 μ‹€νŒ¨ ν‚€μ›Œλ“œ)
12953
+ (hits.lessons || []).slice(0, 5).forEach(l => {
12954
+ const preview = (l.text || l.preview || '').slice(0, 100);
12955
+ const isFailure = /μ‹€νŒ¨|였λ₯˜|μ—λŸ¬|fail|error|bug|문제|warning/i.test(preview);
12956
+ if (isFailure) {
12957
+ conflictHints.push({ kind: 'lesson-failure', preview, tags: l.tags });
12958
+ } else {
12959
+ lessonsRecall.push({ kind: 'lesson', preview, tags: l.tags });
12960
+ }
12961
+ });
12962
+ // skills β€” κΈ°μ‘΄ skill 후보
12963
+ (hits.skills || []).slice(0, 3).forEach(s => {
12964
+ reuseCandidates.push({ kind: 'skill', id: s.id, displayNameKo: s.displayNameKo, capabilities: s.capabilities });
12965
+ });
12966
+ // tasks β€” μ§„ν–‰ 쀑 task 좩돌
12967
+ (hits.tasks || []).slice(0, 3).forEach(tsk => {
12968
+ if (tsk.status && /in-progress|μ§„ν–‰/.test(String(tsk.status))) {
12969
+ conflictHints.push({ kind: 'task-in-progress', id: tsk.id, title: tsk.title });
12970
+ }
12971
+ });
12972
+ // plan milestones β€” μ§„ν–‰ 쀑 milestone
12973
+ (hits.planMilestones || []).slice(0, 3).forEach(m => {
12974
+ if (m.status && /in-progress|μ§„ν–‰/.test(String(m.status))) {
12975
+ planConflicts.push({ kind: 'milestone-in-progress', id: m.id, title: m.title });
12976
+ }
12977
+ });
12978
+ // taskLogFails β€” κ³Όκ±° 같은 ν‚€μ›Œλ“œ μ‹€νŒ¨ 흔적
12979
+ (hits.taskLogFails || []).slice(0, 3).forEach(f => {
12980
+ conflictHints.push({ kind: 'task-log-failure', preview: (f.preview || f.text || '').slice(0, 100) });
12981
+ });
12982
+ }
12983
+ } catch {}
12984
+
12985
+ // 3) reuse-map λ§€μΉ­ β€” κΈ°μ‘΄ capability 등둝 후보
12986
+ try {
12987
+ const reusePath = path.join(root, '.harness/reuse-map.md');
12988
+ if (exists(reusePath)) {
12989
+ const reuseLines = read(reusePath).split('\n');
12990
+ const tokens = lower.split(/\s+/).filter(t => t.length >= 3);
12991
+ for (const line of reuseLines) {
12992
+ if (!/^\| /.test(line)) continue; // ν…Œμ΄λΈ” row만
12993
+ const ll = line.toLowerCase();
12994
+ const matched = tokens.filter(t => ll.includes(t)).length;
12995
+ if (matched > 0) {
12996
+ const cols = line.split('|').map(s => s.trim());
12997
+ if (cols[1]) {
12998
+ reuseCandidates.push({ kind: 'reuse-map', capability: cols[1], where: cols[2] || '', note: cols[3] || '' });
12999
+ }
13000
+ }
13001
+ }
13002
+ }
13003
+ } catch {}
13004
+
13005
+ // 4) feature_graph β€” 같은 μ˜μ—­ λ³€κ²½ κ°€λŠ₯μ„±
13006
+ const featureConflicts = [];
13007
+ try {
13008
+ const fgPath = path.join(root, '.harness/feature_graph.md');
13009
+ if (exists(fgPath)) {
13010
+ const fg = read(fgPath);
13011
+ const tokens = lower.split(/\s+/).filter(t => t.length >= 4);
13012
+ // F-XXXX λ…Έλ“œ 라인 μΆ”μΆœ
13013
+ const nodeBlocks = fg.split(/\n### /);
13014
+ for (const blk of nodeBlocks.slice(1)) {
13015
+ const bl = blk.toLowerCase();
13016
+ const matched = tokens.filter(t => bl.includes(t)).length;
13017
+ if (matched > 0) {
13018
+ const titleMatch = blk.match(/^([^\n]+)/);
13019
+ const idMatch = blk.match(/F-\d+/);
13020
+ if (titleMatch && idMatch) {
13021
+ featureConflicts.push({ kind: 'feature', id: idMatch[0], title: titleMatch[1].trim() });
13022
+ }
13023
+ }
13024
+ }
13025
+ }
13026
+ } catch {}
13027
+
13028
+ // 5) ꢌμž₯ 단계 (μž‘μ—… μœ ν˜•λ³„)
13029
+ const recommendedSteps = {
13030
+ feature: [
13031
+ '1) leerness reuse find "<핡심 capability>" β€” 쀑볡 κ΅¬ν˜„ 사전 차단',
13032
+ '2) leerness plan add "<milestone>" β€” μ§„ν–‰ 좔적',
13033
+ '3) leerness contract verify SPEC.md src/<mod>.js β€” 사양 ↔ κ΅¬ν˜„ 일치 검증',
13034
+ '4) verify-claim --run-tests 둜 evidence μ˜λ¬΄ν™”'
13035
+ ],
13036
+ bugfix: [
13037
+ '1) leerness brainstorm "<버그 ν‚€μ›Œλ“œ>" β€” κ³Όκ±° 같은 μ˜μ—­ lesson 회수',
13038
+ '2) leerness verify-claim T-XXX --strict-claims β€” 낙관적 ν‘œμ‹œ 사전 감지',
13039
+ '3) verify-code --run-tests β€” μž¬ν˜„ + fix 검증',
13040
+ '4) leerness lesson save "<root cause>" β€” 같은 μ‹€μˆ˜ 재발 차단'
13041
+ ],
13042
+ refactor: [
13043
+ '1) leerness reuse-map β€” 영ν–₯ λ²”μœ„ νŒŒμ•…',
13044
+ '2) leerness impact <file> β€” κ°•ν•œ/μ•½ν•œ μ°Έμ‘° 뢄리',
13045
+ '3) leerness contract verify β€” μ™ΈλΆ€ μΈν„°νŽ˜μ΄μŠ€ 보쑴 확인',
13046
+ '4) verify-code --run-tests + νšŒκ·€ ν…ŒμŠ€νŠΈ'
13047
+ ],
13048
+ research: [
13049
+ '1) leerness brainstorm "<주제>" β€” λˆ„μ  μ»¨ν…μŠ€νŠΈ 회수',
13050
+ '2) leerness lessons --query "<주제>" β€” κ³Όκ±° 같은 μ˜μ—­ κ²°μ •',
13051
+ '3) leerness review <file> --persona research β€” 깊이 κ²€ν† ',
13052
+ '4) leerness decision add "<κ²°λ‘ >" β€” 회수 κ°€λŠ₯ν•˜κ²Œ μ˜κ΅¬ν™”'
13053
+ ],
13054
+ planning: [
13055
+ '1) leerness plan add "<milestone>" β€” λΆ„ν•΄ μ‹œμž‘',
13056
+ '2) leerness reuse-map β€” κΈ°μ‘΄ μžμ› 인벀토리',
13057
+ '3) leerness agents recommend planning β€” sub-agent λΆ„λ°°',
13058
+ '4) leerness session close β€” κ²°μ • μ˜κ΅¬ν™”'
13059
+ ],
13060
+ release: [
13061
+ '1) leerness health β€” production-ready 확인',
13062
+ '2) leerness audit + verify-code β€” λ³΄μ•ˆ + κ²€μˆ˜',
13063
+ '3) leerness release bump + note + publish'
13064
+ ],
13065
+ consistency: [
13066
+ '1) leerness audit β€” design/reuse/handoff 일관성 검사',
13067
+ '2) leerness consistency check β€” 잠재 일관성 μœ„λ°˜',
13068
+ '3) leerness drift check --auto-fix β€” μžλ™ 회볡'
13069
+ ]
13070
+ }[estimatedType] || [];
13071
+
13072
+ // 6) 효율 μ œμ•ˆ (적용 κ°€λŠ₯ν•œ sub-agent + skill)
13073
+ const efficiencyHints = [];
13074
+ if (reuseCandidates.length > 0) {
13075
+ efficiencyHints.push(`πŸ” κΈ°μ‘΄ μžμ› ${reuseCandidates.length}건 발견 β€” μ‹ κ·œ κ΅¬ν˜„ μ „ μž¬μ‚¬μš© κ²€ν†  ꢌμž₯`);
13076
+ }
13077
+ if (conflictHints.length > 0) {
13078
+ efficiencyHints.push(`⚠ 좩돌 μ‹ ν˜Έ ${conflictHints.length}건 β€” κ³Όκ±° μ‹€νŒ¨ lesson / μ§„ν–‰ 쀑 task 확인 ν•„μš”`);
13079
+ }
13080
+ if (planConflicts.length > 0) {
13081
+ efficiencyHints.push(`πŸ“‹ μ§„ν–‰ 쀑 milestone ${planConflicts.length}건과 μ˜μ—­ κ²ΉμΉ¨ κ°€λŠ₯ β€” plan μ •λ ¬ ꢌμž₯`);
13082
+ }
13083
+ if (featureConflicts.length > 0) {
13084
+ efficiencyHints.push(`πŸ•Έ Feature Graph ${featureConflicts.length}건 μ˜μ—­ κ²ΉμΉ¨ β€” μ˜μ‘΄μ„± 사전 확인`);
13085
+ }
13086
+ // 닀쀑 μ—μ΄μ „νŠΈ λΆ„λ°° μΆ”μ²œ
13087
+ if (estimatedType === 'feature' || estimatedType === 'planning') {
13088
+ efficiencyHints.push(`πŸ‘₯ leerness agents recommend ${estimatedType} β€” μž‘μ—… μœ ν˜•λ³„ sub-agent λ§€ν•‘ ν™œμš© κ°€λŠ₯`);
13089
+ }
13090
+ if (efficiencyHints.length === 0) {
13091
+ efficiencyHints.push('✨ 좩돌 μ‹ ν˜Έ μ—†μŒ β€” μ¦‰μ‹œ μ§„ν–‰ μ•ˆμ „');
13092
+ }
13093
+
13094
+ // 7) proceed ꢌμž₯ (좩돌 critical μ‹œ false)
13095
+ const proceed = conflictHints.length < 3 && planConflicts.length === 0;
13096
+
13097
+ const dt = Date.now() - t0;
13098
+ const out = {
13099
+ request: text,
13100
+ estimatedType,
13101
+ conflicts: conflictHints,
13102
+ reuseCandidates,
13103
+ lessonsRecall,
13104
+ planConflicts,
13105
+ featureConflicts,
13106
+ recommendedSteps,
13107
+ efficiencyHints,
13108
+ proceed,
13109
+ proceedReason: proceed ? 'μ•ˆμ „ β€” 좩돌 μ‹ ν˜Έ < 3 + plan 좩돌 0' : '⚠ 좩돌 critical β€” μ‚¬μš©μž 확인 ν›„ μ§„ν–‰',
13110
+ durationMs: dt
13111
+ };
13112
+
13113
+ try { _recordRun(root, { kind: 'review_request', estimatedType, conflicts: conflictHints.length, reuse: reuseCandidates.length, durationMs: dt, ok: true }); } catch {}
13114
+
13115
+ if (has('--json')) {
13116
+ log(JSON.stringify(out, null, 2));
13117
+ return;
13118
+ }
13119
+
13120
+ log(`# leerness review-request (1.9.176 사전 κ²€ν† )`);
13121
+ log(`μš”μ²­: "${text.slice(0, 200)}${text.length > 200 ? '…' : ''}"`);
13122
+ log(`μΆ”μ • μž‘μ—… μœ ν˜•: ${estimatedType}`);
13123
+ log('');
13124
+ if (conflictHints.length) {
13125
+ log(`## ⚠ 좩돌 μ‹ ν˜Έ (${conflictHints.length})`);
13126
+ conflictHints.slice(0, 5).forEach(c => log(` - [${c.kind}] ${c.title || c.id || ''} ${c.preview || ''}`.trim()));
13127
+ log('');
13128
+ }
13129
+ if (reuseCandidates.length) {
13130
+ log(`## πŸ” μž¬μ‚¬μš© 후보 (${reuseCandidates.length}) β€” μ‹ κ·œ κ΅¬ν˜„ μ „ κ²€ν† `);
13131
+ reuseCandidates.slice(0, 5).forEach(r => {
13132
+ if (r.kind === 'skill') log(` - [skill] ${r.id}${r.displayNameKo ? ' Β· ' + r.displayNameKo : ''}`);
13133
+ else if (r.kind === 'reuse-map') log(` - [reuse] ${r.capability} @ ${r.where}`);
13134
+ });
13135
+ log('');
13136
+ }
13137
+ if (lessonsRecall.length) {
13138
+ log(`## 🧠 κ³Όκ±° μ»¨ν…μŠ€νŠΈ (${lessonsRecall.length}) β€” κ΄€λ ¨ κ²°μ •/κ΅ν›ˆ`);
13139
+ lessonsRecall.slice(0, 3).forEach(l => log(` - [${l.kind}] ${l.title || l.preview}`));
13140
+ log('');
13141
+ }
13142
+ if (planConflicts.length || featureConflicts.length) {
13143
+ log(`## πŸ“‹ μ§„ν–‰ 쀑 μ˜μ—­ (${planConflicts.length + featureConflicts.length})`);
13144
+ planConflicts.forEach(m => log(` - [milestone] ${m.id}: ${m.title}`));
13145
+ featureConflicts.slice(0, 5).forEach(f => log(` - [feature] ${f.id}: ${f.title}`));
13146
+ log('');
13147
+ }
13148
+ log(`## πŸ’‘ 효율 μ œμ•ˆ`);
13149
+ efficiencyHints.forEach(h => log(` ${h}`));
13150
+ log('');
13151
+ if (recommendedSteps.length) {
13152
+ log(`## πŸ“ ꢌμž₯ 단계 (${estimatedType})`);
13153
+ recommendedSteps.forEach(s => log(` ${s}`));
13154
+ log('');
13155
+ }
13156
+ log(`## β–Ά μ§„ν–‰ ꢌμž₯: ${proceed ? 'βœ“ μ§„ν–‰ μ•ˆμ „' : '⚠ μ‚¬μš©μž 확인 ν•„μš”'}`);
13157
+ log(` μ‚¬μœ : ${out.proceedReason}`);
13158
+ log(` 뢄석 μ†Œμš”: ${dt}ms`);
13159
+ }
13160
+
12828
13161
  // 1.9.164: leerness which β€” 진단 도ꡬ (ꡬ버전 좩돌 / npx μΊμ‹œ / PATH 좩돌 ν•΄κ²°)
12829
13162
  // μ‚¬μš©μžκ°€ "μ΅œμ‹  버전 μž‘λ™ μ•ˆ 함" μ˜μ‹¬ μ‹œ: μ‹€μ œ μ‹€ν–‰ 쀑인 leerness 의 경둜 / 버전 / npm μΊμ‹œ / PATH 후보 ν‘œμ‹œ.
12830
13163
  function whichCmd() {
@@ -12906,7 +13239,7 @@ function whichCmd() {
12906
13239
  }
12907
13240
 
12908
13241
  function help() {
12909
- log(`Leerness v${VERSION}\n\nUsage:\n leerness init [path] [--language auto|ko|en] [--skills recommended|all|a,b]\n leerness migrate [path] [--dry-run] [--force]\n leerness update [path] [--check|--yes|--force|--from <tarball>]\n leerness auto-update install [path]\n leerness status [path]\n leerness verify [path]\n leerness debug [path]\n leerness audit [path]\n leerness check [path]\n leerness scan secrets [path]\n leerness encoding check [path]\n leerness lazy detect [path]\n leerness memory search "query" [--limit 5]\n leerness handoff [path] [--all-apps] [--include p1,p2] [--since 24h|3d] [--compact] [--json] # 1.9.17-22 μ›Œν¬μŠ€νŽ˜μ΄μŠ€ (--compact: LLM μ‹œμŠ€ν…œ ν”„λ‘¬ν”„νŠΈμš© 1쀄 μš”μ•½)\n leerness orchestrate "<λͺ©ν‘œ>" [--agents N] [--model qwen2.5:7b-instruct] [--retry-on-fail K] # 1.9.22 Ollama opt-in (LEERNESS_OLLAMA_BASE_URL ν•„μš”)\n leerness llm-bench record --score N --model X [--label L] [--tokens T] # 1.9.22 LLM 벀치 νžˆμŠ€ν† λ¦¬ λˆ„μ \n leerness deps <capability> [--run-tests] [--json] # 1.9.24 depends-on μ—­λ°©ν–₯ 좔적 + μžλ™ νšŒκ·€ sweep\n leerness memory search "ν‚€" [--include-code] # 1.9.25 μ†ŒμŠ€ μ½”λ“œ 본문도 검색 (λͺ¨μˆœ 감지 핡심)\n leerness brainstorm "주제" [--include-code] # 1.9.25 μ½”λ“œ λ³Έλ¬Έ hits 포함\n leerness register-pending "<μš”μ²­>" [--agent X] [--note Y] # 1.9.25 닀쀑 μ„Έμ…˜ in-progress μ¦‰μ‹œ 등둝\n leerness optimism-check <T-ID> [--json] # 1.9.26/27 낙관적 ν‘œμ‹œ 감지 (1.9.27: 10 μΉ΄ν…Œκ³ λ¦¬ + URL/λ©”μ„œλ“œ λ§€ν•‘ + 신뒰도 점수)\n leerness persona list|show <id>|add <id> # 1.9.29 페λ₯΄μ†Œλ‚˜ μΉ΄νƒˆλ‘œκ·Έ (λ³΄μ•ˆ/μ„±λŠ₯/UX/testing/docs 5μ’… λ‚΄μž₯)\n leerness review <file> --persona <id1,id2,...> # 1.9.29 도메인 페λ₯΄μ†Œλ‚˜ 리뷰 ν”„λ‘¬ν”„νŠΈ μžλ™ 생성\n leerness agents list|check|quota # 1.9.30/31 μ™ΈλΆ€ AI CLI κ°€μš©μ„± + quota μΆ”μ • (claude/codex/gemini/copilot)\n leerness agents dispatch "<task>" --to <id> # 1.9.30 ν™œμ„± CLI λŒ€μƒ μ‹€ν–‰ λͺ…λ Ή 생성 (μ‹€ 호좜 X, μ‚¬μš©μž μ‹€ν–‰)\n leerness agents multi "<task>" [--only c1,c2] [--write] [--execute] [--timeout 60] # 1.9.152/156 ν™œμ„± N개 일괄 dispatch (--execute: μ‹€ spawn + consensus)\n leerness provider list|add|remove [args] # 1.9.157 Provider Registry β€” μ‚¬μš©μž μ •μ˜ CLI provider 동적 μΆ”κ°€ (OpenRouter/Bedrock 흑수)\n leerness agents dispatch "<task>" --multi # 1.9.152 multi λͺ¨λ“œ alias (λ˜λŠ” --to all)\n leerness setup-agents [path] [--yes|--no-setup-agents] # 1.9.32 sub-agent CLI μΈν„°λž™ν‹°λΈŒ μ„€μ • (.env + λ―Έμ„€μΉ˜ μžλ™ μ„€μΉ˜)\n leerness init [path] [--no-stale-check] # 1.9.33 npx μΊμ‹œ 함정 β€” μ˜› 버전 μžλ™ κ²½κ³  (끄렀면 --no-stale-check)\n leerness which [--json] # 1.9.164 진단: ν˜„μž¬ μ‹€ν–‰ 경둜/버전 + npm μΊμ‹œ + PATH 후보 (ꡬ버전 좩돌 ν•΄κ²°)\n leerness web check|screenshot|extract <url> [--out file.png] [--selector "css"] # 1.9.165 playwright bridge (opt-in: npm i -g playwright + permissions.browser)\n leerness pc check|click|type|screenshot [--x N --y N] [--text "s"] [--out f.png] # 1.9.166 robotjs/nut-tree bridge (opt-in: npm i -g robotjs + permissions.mouse/keyboard, ⚠ full λͺ¨λ“œ ꢌμž₯)\n leerness lsp check|symbols|references <file/name> [--in dir] [--json] # 1.9.167 LSP μ–΄λŒ‘ν„° MVP (typescript opt-in + regex fallback, μ½”λ“œ μΈν…”λ¦¬μ „μŠ€)\n leerness contract verify <spec.md> <impl.js> [--json] # 1.9.35 λͺ…μ„Έ ↔ κ΅¬ν˜„ 일치 검사 (ν•¨μˆ˜/ν•„λ“œ)\n leerness reuse autodetect [path] [--apply] [--json] # 1.9.35 src/*.js의 module.exports β†’ reuse-map 후보 등둝\n leerness audit [path] [--fix] # 1.9.35 --fix: session-handoff/current-state μžλ™ κ°±μ‹ \n leerness verify-claim <T-ID> ... [--strict-claims] # 1.9.26 verify-claim에 낙관적 ν‘œμ‹œ μžλ™ 검사 톡합\n leerness reuse-map [path] [--all-apps] [--include p1,p2] [--strict-elements] [--json] # 1.9.18 쀑볡/μž μž¬μ€‘λ³΅/depends-on\n leerness verify-claim <T-ID> [--path .] [--run-tests] [--json] # 1.9.18-20 evidence μžλ™ 검증 (1.9.20: scenes/scripts λ“± 도메인 폴더 + jest/mocha νŒŒμ‹±)\n leerness verify-code [path] [--build] [--bench] # 1.9.20 --bench: scripts.bench μΆ”κ°€ μ‹€ν–‰ + evidence λˆ„μ \n leerness session close [path]\n leerness route <task-type>\n leerness self check [path]\n leerness readme sync [path]\n leerness consistency check [path]\n leerness consistency merge-design-guide [path]\n leerness plan show|init|add|drop|progress|sync [args]\n leerness task list|add|update|drop|fix-evidence|relink [args]\n leerness skill list|info <name>\n leerness skill learn <id> --doc <url> --command "..." --capability "..." [--note ...]\n leerness skill use <id> [--note ...]\n leerness skill optimize <id> --before "..." --after "..." [--note ...]\n leerness skill remove <id>\n leerness skill consolidate [--threshold 0.3]\n leerness gate [path] # verify+audit+scan+encoding+lazy
13242
+ log(`Leerness v${VERSION}\n\nUsage:\n leerness init [path] [--language auto|ko|en] [--skills recommended|all|a,b]\n leerness migrate [path] [--dry-run] [--force]\n leerness update [path] [--check|--yes|--force|--from <tarball>]\n leerness auto-update install [path]\n leerness status [path]\n leerness verify [path]\n leerness debug [path]\n leerness audit [path]\n leerness check [path]\n leerness scan secrets [path]\n leerness encoding check [path]\n leerness lazy detect [path]\n leerness memory search "query" [--limit 5]\n leerness handoff [path] [--all-apps] [--include p1,p2] [--since 24h|3d] [--compact] [--json] # 1.9.17-22 μ›Œν¬μŠ€νŽ˜μ΄μŠ€ (--compact: LLM μ‹œμŠ€ν…œ ν”„λ‘¬ν”„νŠΈμš© 1쀄 μš”μ•½)\n leerness orchestrate "<λͺ©ν‘œ>" [--agents N] [--model qwen2.5:7b-instruct] [--retry-on-fail K] # 1.9.22 Ollama opt-in (LEERNESS_OLLAMA_BASE_URL ν•„μš”)\n leerness llm-bench record --score N --model X [--label L] [--tokens T] # 1.9.22 LLM 벀치 νžˆμŠ€ν† λ¦¬ λˆ„μ \n leerness deps <capability> [--run-tests] [--json] # 1.9.24 depends-on μ—­λ°©ν–₯ 좔적 + μžλ™ νšŒκ·€ sweep\n leerness memory search "ν‚€" [--include-code] # 1.9.25 μ†ŒμŠ€ μ½”λ“œ 본문도 검색 (λͺ¨μˆœ 감지 핡심)\n leerness brainstorm "주제" [--include-code] # 1.9.25 μ½”λ“œ λ³Έλ¬Έ hits 포함\n leerness register-pending "<μš”μ²­>" [--agent X] [--note Y] # 1.9.25 닀쀑 μ„Έμ…˜ in-progress μ¦‰μ‹œ 등둝\n leerness optimism-check <T-ID> [--json] # 1.9.26/27 낙관적 ν‘œμ‹œ 감지 (1.9.27: 10 μΉ΄ν…Œκ³ λ¦¬ + URL/λ©”μ„œλ“œ λ§€ν•‘ + 신뒰도 점수)\n leerness persona list|show <id>|add <id> # 1.9.29 페λ₯΄μ†Œλ‚˜ μΉ΄νƒˆλ‘œκ·Έ (λ³΄μ•ˆ/μ„±λŠ₯/UX/testing/docs 5μ’… λ‚΄μž₯)\n leerness review <file> --persona <id1,id2,...> # 1.9.29 도메인 페λ₯΄μ†Œλ‚˜ 리뷰 ν”„λ‘¬ν”„νŠΈ μžλ™ 생성\n leerness agents list|check|quota # 1.9.30/31 μ™ΈλΆ€ AI CLI κ°€μš©μ„± + quota μΆ”μ • (claude/codex/gemini/copilot)\n leerness agents dispatch "<task>" --to <id> # 1.9.30 ν™œμ„± CLI λŒ€μƒ μ‹€ν–‰ λͺ…λ Ή 생성 (μ‹€ 호좜 X, μ‚¬μš©μž μ‹€ν–‰)\n leerness agents multi "<task>" [--only c1,c2] [--write] [--execute] [--timeout 60] # 1.9.152/156 ν™œμ„± N개 일괄 dispatch (--execute: μ‹€ spawn + consensus)\n leerness provider list|add|remove [args] # 1.9.157 Provider Registry β€” μ‚¬μš©μž μ •μ˜ CLI provider 동적 μΆ”κ°€ (OpenRouter/Bedrock 흑수)\n leerness agents dispatch "<task>" --multi # 1.9.152 multi λͺ¨λ“œ alias (λ˜λŠ” --to all)\n leerness setup-agents [path] [--yes|--no-setup-agents] # 1.9.32 sub-agent CLI μΈν„°λž™ν‹°λΈŒ μ„€μ • (.env + λ―Έμ„€μΉ˜ μžλ™ μ„€μΉ˜)\n leerness init [path] [--no-stale-check] # 1.9.33 npx μΊμ‹œ 함정 β€” μ˜› 버전 μžλ™ κ²½κ³  (끄렀면 --no-stale-check)\n leerness which [--json] # 1.9.164 진단: ν˜„μž¬ μ‹€ν–‰ 경둜/버전 + npm μΊμ‹œ + PATH 후보 (ꡬ버전 좩돌 ν•΄κ²°)\n leerness web check|screenshot|extract <url> [--out file.png] [--selector "css"] # 1.9.165 playwright bridge (opt-in: npm i -g playwright + permissions.browser)\n leerness pc check|click|type|screenshot [--x N --y N] [--text "s"] [--out f.png] # 1.9.166 robotjs/nut-tree bridge (opt-in: npm i -g robotjs + permissions.mouse/keyboard, ⚠ full λͺ¨λ“œ ꢌμž₯)\n leerness lsp check|symbols|references <file/name> [--in dir] [--json] # 1.9.167 LSP μ–΄λŒ‘ν„° MVP (typescript opt-in + regex fallback, μ½”λ“œ μΈν…”λ¦¬μ „μŠ€)\n leerness review-request "<request>" [--json] # 1.9.176 μ‚¬μš©μž μš”μ²­ 사전 κ²€ν†  (좩돌/μž¬μ‚¬μš©/효율/ꢌμž₯ 단계 β€” μ‚¬μš©μž λͺ…μ‹œ)\n leerness contract verify <spec.md> <impl.js> [--json] # 1.9.35 λͺ…μ„Έ ↔ κ΅¬ν˜„ 일치 검사 (ν•¨μˆ˜/ν•„λ“œ)\n leerness reuse autodetect [path] [--apply] [--json] # 1.9.35 src/*.js의 module.exports β†’ reuse-map 후보 등둝\n leerness audit [path] [--fix] # 1.9.35 --fix: session-handoff/current-state μžλ™ κ°±μ‹ \n leerness verify-claim <T-ID> ... [--strict-claims] # 1.9.26 verify-claim에 낙관적 ν‘œμ‹œ μžλ™ 검사 톡합\n leerness reuse-map [path] [--all-apps] [--include p1,p2] [--strict-elements] [--json] # 1.9.18 쀑볡/μž μž¬μ€‘λ³΅/depends-on\n leerness verify-claim <T-ID> [--path .] [--run-tests] [--json] # 1.9.18-20 evidence μžλ™ 검증 (1.9.20: scenes/scripts λ“± 도메인 폴더 + jest/mocha νŒŒμ‹±)\n leerness verify-code [path] [--build] [--bench] # 1.9.20 --bench: scripts.bench μΆ”κ°€ μ‹€ν–‰ + evidence λˆ„μ \n leerness session close [path]\n leerness route <task-type>\n leerness self check [path]\n leerness readme sync [path]\n leerness consistency check [path]\n leerness consistency merge-design-guide [path]\n leerness plan show|init|add|drop|progress|sync [args]\n leerness task list|add|update|drop|fix-evidence|relink [args]\n leerness skill list|info <name>\n leerness skill learn <id> --doc <url> --command "..." --capability "..." [--note ...]\n leerness skill use <id> [--note ...]\n leerness skill optimize <id> --before "..." --after "..." [--note ...]\n leerness skill remove <id>\n leerness skill consolidate [--threshold 0.3]\n leerness gate [path] # verify+audit+scan+encoding+lazy
12910
13243
  leerness retro [path] [--days 7] [--all-apps] [--include p1,p2] [--json] # 회고 (1.9.13~1.9.16)
12911
13244
  leerness insights [path] [--all-apps] [--include p1,p2] [--json] # λˆ„μ  톡계 (1.9.13~1.9.16)
12912
13245
  leerness brainstorm "<주제>" [--all-apps] [--include p1,p2] [--json] # λΈŒλ ˆμΈμŠ€ν† λ° (1.9.13~1.9.16)
@@ -12988,6 +13321,13 @@ async function main() {
12988
13321
  if (cmd === 'pc') return pcCmd(arg('--path', process.cwd()), args[1], ...args.slice(2));
12989
13322
 
12990
13323
  if (cmd === 'lsp') return lspCmd(arg('--path', process.cwd()), args[1], ...args.slice(2));
13324
+
13325
+ // 1.9.176: leerness review-request "<request>" β€” μ‚¬μš©μž μš”μ²­ 사전 κ²€ν†  (μ‚¬μš©μž λͺ…μ‹œ)
13326
+ // AI μ—μ΄μ „νŠΈκ°€ 무쑰건 κ΅¬ν˜„ 전에 좩돌/μž¬μ‚¬μš©/효율/ꢌμž₯ 단계 뢄석.
13327
+ if (cmd === 'review-request' || cmd === 'review-req') {
13328
+ const reqText = args.slice(1).filter(x => !x.startsWith('-')).join(' ');
13329
+ return reviewRequestCmd(arg('--path', process.cwd()), reqText);
13330
+ }
12991
13331
  if (cmd === 'contract' && args[1] === 'verify') return contractVerifyCmd(args[2], args[3]);
12992
13332
  if (cmd === 'drift' && (args[1] === 'check' || !args[1])) return driftCheckCmd(args[2] || arg('--path', process.cwd()));
12993
13333
  if (cmd === 'usage' && (args[1] === 'stats' || !args[1])) return usageStatsCmd(args[2] || arg('--path', process.cwd()));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leerness",
3
- "version": "1.9.175",
3
+ "version": "1.9.177",
4
4
  "description": "Leerness: λΉ„νŒŒκ΄΄ λ§ˆμ΄κ·Έλ ˆμ΄μ…˜, μžλ™ 버전 κ°μ§€Β·μ—…λ°μ΄νŠΈ, κ³„νš/μ§„ν–‰/ν•Έλ“œμ˜€ν”„ μžλ™ν™”, κ²ŒμœΌλ¦„Β·μ‹œν¬λ¦ΏΒ·μΈμ½”λ”© μžλ™ κ°€λ“œ, Claude Code μŠ¬λž˜μ‹œ 톡합을 κ°–μΆ˜ ν•œκ΅­μ–΄ μš°μ„  AI 개발 ν•˜λ„€μŠ€.",
5
5
  "keywords": [
6
6
  "leerness",