leerness 1.9.172 β†’ 1.9.174

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,124 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.9.174 β€” 2026-05-21
4
+
5
+ **πŸ” μ‚¬μš©μž λͺ…μ‹œ β€” install κΆŒν•œ prompt 제거 + REPL `:permissions` μ¦‰μ‹œ λ³€κ²½.**
6
+
7
+ 자율 λͺ¨λ“œ 104 λΌμš΄λ“œ. μ‚¬μš©μž λͺ…μ‹œ: *"κΆŒν•œ μ„€μ • 문항은 μ œκ±°ν•˜κ³  REPL λͺ¨λ“œμ—μ„œ κ°„νŽΈν•˜κ²Œ κΆŒν•œ λ³€κ²½ν•  수 μžˆλ„λ‘"*.
8
+
9
+ ### 1. Install κΆŒν•œ prompt 제거
10
+ 이전 (1.9.146): install μ‹œ 3-tier κΆŒν•œ λͺ¨λ“œ 선택 prompt (basic/extended/full).
11
+ **문제**: μ‚¬μš©μž κ²½ν—˜ λ³΅μž‘λ„ 증가 + 잘λͺ»λœ 선택 (full) μ‹œ μœ„ν—˜.
12
+
13
+ **1.9.174**:
14
+ - install μ‹œ κΆŒν•œ **항상 `basic` μžλ™ 적용** (μ•ˆμ „ default).
15
+ - prompt μ½”λ“œ (resolveInstallOptions μ•ˆ κΆŒν•œ λͺ¨λ“œ _selectOne 블둝) μ™„μ „ 제거.
16
+ - μ•ˆλ‚΄ 라인: `Agent κΆŒν•œ λͺ¨λ“œ: basic (1.9.174 β€” REPLμ—μ„œ :permissions extended|full 둜 μ¦‰μ‹œ λ³€κ²½ κ°€λŠ₯)`.
17
+
18
+ ### 2. REPL `:permissions` μ¦‰μ‹œ λ³€κ²½ κ°€λŠ₯
19
+ 이전 (1.9.146~1.9.173): `:permissions` 메타 λͺ…령은 list 만 (쑰회).
20
+
21
+ **1.9.174**:
22
+ ```
23
+ agent[claude/actor/β–Ά]> :permissions
24
+ πŸ” ν˜„μž¬ κΆŒν•œ λͺ¨λ“œ: basic
25
+
26
+ λ³€κ²½:
27
+ :permissions basic β€” μ•ˆμ „ (.harness 만 μ“°κΈ°, ꢌμž₯)
28
+ :permissions extended β€” ν”„λ‘œμ νŠΈ 폴더 + shell allowlist
29
+ :permissions full β€” ⚠ 전체 (마우슀/ν‚€λ³΄λ“œ/μ›Ή, IDE 톡합 μ‹œλ§Œ)
30
+
31
+ μ„ΈλΆ€ κΆŒν•œ (mouse/keyboard/browser/admin):
32
+ mouse: βœ— κ±°λΆ€
33
+ keyboard: βœ— κ±°λΆ€
34
+ browser: βœ— κ±°λΆ€
35
+ admin: βœ— κ±°λΆ€
36
+
37
+ agent[claude/actor/β–Ά]> :permissions extended
38
+ βœ“ κΆŒν•œ λͺ¨λ“œ λ³€κ²½: extended (μ¦‰μ‹œ 적용 β€” λ‹€μŒ λͺ…λ ΉλΆ€ν„°)
39
+
40
+ agent[claude/actor/β–Ά]> :permissions full
41
+ βœ“ κΆŒν•œ λͺ¨λ“œ λ³€κ²½: full (μ¦‰μ‹œ 적용 β€” λ‹€μŒ λͺ…λ ΉλΆ€ν„°)
42
+ ⚠ full λͺ¨λ“œ β€” 마우슀/ν‚€λ³΄λ“œ/μ›Ή/κ΄€λ¦¬μž 전체 ν—ˆμš©. IDE 톡합 μ™Έ ν™˜κ²½μ—μ„œλŠ” μœ„ν—˜.
43
+ ```
44
+
45
+ - 인자 μ—†μŒ β†’ ν˜„μž¬ λͺ¨λ“œ + μ„ΈλΆ€ κΆŒν•œ (mouse/keyboard/browser/admin) ν‘œμ‹œ + λ³€κ²½ μ˜΅μ…˜ μ•ˆλ‚΄
46
+ - `:permissions basic|extended|full` β†’ μ¦‰μ‹œ λ³€κ²½ (`permissionsSetCmd` 호좜)
47
+ - `:perm` alias μΆ”κ°€ (단좕)
48
+ - `full` λͺ¨λ“œ λ³€κ²½ μ‹œ ⚠ λͺ…μ‹œμ  κ²½κ³ 
49
+ - 잘λͺ»λœ λͺ¨λ“œ β†’ μΉœμ ˆν•œ μ•ˆλ‚΄ (`잘λͺ»λœ λͺ¨λ“œ: xyz (basic | extended | full)`)
50
+
51
+ ### ν˜Έν™˜μ„±
52
+ - CLI λͺ…λ Ή `leerness permissions list|set` κ·ΈλŒ€λ‘œ μœ μ§€ (1.9.146 ν˜Έν™˜).
53
+ - κΈ°μ‘΄ `.harness/agent-permissions.json` ν˜•μ‹ κ·ΈλŒ€λ‘œ (mode + 4-tier μ„ΈλΆ€).
54
+
55
+ ### Verified
56
+ - e2e 217/217 baseline μœ μ§€
57
+ - stress-v119: **20/20** (install 제거 5 + REPL :permissions 7 + CLI ν˜Έν™˜ 2 + λˆ„μ  νšŒκ·€ 6)
58
+ - VERSION = 1.9.174 / autonomous-rounds = 104 / main μžλ™ push 35 λΌμš΄λ“œ 연속
59
+
60
+ ---
61
+
62
+ ## 1.9.173 β€” 2026-05-21
63
+
64
+ **🌐 LSP μ–΄λŒ‘ν„° λ‹€κ΅­μ–΄ ν™•μž₯ β€” JavaScript + Python + Go + Rust + Java (5개 μ–Έμ–΄ regex fallback).**
65
+
66
+ 자율 λͺ¨λ“œ 103 λΌμš΄λ“œ. 1.9.167 codeIntel (JS/TS only) β†’ 5개 μ–Έμ–΄ ν™•μž₯.
67
+
68
+ ### `_LSP_LANG_PATTERNS` β€” 5개 μ–Έμ–΄ νŒ¨ν„΄
69
+ | μ–Έμ–΄ | μΆ”μΆœ κ°€λŠ₯ |
70
+ |---|---|
71
+ | **javascript** (.ts/.tsx/.js/.jsx/.mjs/.cjs) | function, class, interface, type, enum, arrow function |
72
+ | **python** (.py/.pyw/.pyi) | def, async def, class |
73
+ | **go** (.go) | func (receiver 포함), type struct/interface, type alias |
74
+ | **rust** (.rs) | fn (pub/async), struct, enum, trait, impl, type |
75
+ | **java** (.java/.kt/.scala) | class (public/private/abstract), interface, enum, method |
76
+
77
+ ### `_detectLspLang(file)` β€” ν™•μž₯자 μžλ™ λΌμš°νŒ…
78
+ 파일 ν™•μž₯자 기반 μ–Έμ–΄ μžλ™ 감지. 미지원 ν™•μž₯μžλŠ” javascript κΈ°λ³Έ (1.9.167 ν˜Έν™˜).
79
+
80
+ ### μ‚¬μš© μ˜ˆμ‹œ
81
+ ```bash
82
+ $ leerness lsp symbols src/api.py
83
+ # leerness lsp symbols (1.9.173 λ‹€κ΅­μ–΄)
84
+ file: src/api.py Β· lang: python
85
+ mode: regex-fallback (python) Β· 12 symbols Β· 4ms
86
+ 1:function parse_request
87
+ 8:function fetch_data
88
+ 15:class Handler
89
+ 16:function __init__
90
+ ...
91
+
92
+ $ leerness lsp symbols src/main.rs
93
+ # leerness lsp symbols (1.9.173 λ‹€κ΅­μ–΄)
94
+ file: src/main.rs Β· lang: rust
95
+ mode: regex-fallback (rust) Β· 9 symbols Β· 5ms
96
+ 1:function hello
97
+ 5:struct User
98
+ 9:impl User
99
+ 15:trait Greeter
100
+ ...
101
+ ```
102
+
103
+ ### ν‚€μ›Œλ“œ false-positive 제거
104
+ Java method μ •κ·œμ‹μ΄ `if(`, `for(`, `while(`, `switch(`, `catch(`, `return(`, `throw(`, `new(` λ“± ν‚€μ›Œλ“œμ— λ§€μΉ˜λ˜λŠ” 경우 ν•„ν„°.
105
+
106
+ ### references λ‹€κ΅­μ–΄ 파일 μŠ€μΊ”
107
+ `leerness lsp references <name>` κ°€ `.py/.go/.rs/.java/.kt/.scala` νŒŒμΌλ„ μŠ€μΊ” (κΈ°μ‘΄ `.ts/.js/.md` 에 μΆ”κ°€).
108
+
109
+ ### μ‹€μΈ‘ (regex fallback)
110
+ - Python (5 symbols): 472ms
111
+ - Go (4 symbols): 566ms
112
+ - Rust (6 symbols): 531ms
113
+ - Java (4 symbols): 1229ms
114
+
115
+ ### Verified
116
+ - e2e 217/217 baseline μœ μ§€
117
+ - stress-v118: **15/15** (νŒ¨ν„΄ μ •μ˜ 4 + Python 1 + Go 1 + Rust 1 + Java 1 + JS ν˜Έν™˜ 1 + references 1 + λˆ„μ  νšŒκ·€ 5)
118
+ - VERSION = 1.9.173 / autonomous-rounds = 103 / main μžλ™ push 34 λΌμš΄λ“œ 연속
119
+
120
+ ---
121
+
3
122
  ## 1.9.172 β€” 2026-05-21
4
123
 
5
124
  **🎨 슀트리밍 UX κ°•ν™” β€” spinner + Claude tool_use κ°€μ‹œν™” + diff νŒ¨ν„΄ μžλ™ 색깔 (μ‚¬μš©μž λͺ…μ‹œ κ°•ν™”).**
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.172-green)]() [![tests](https://img.shields.io/badge/e2e-217%2F217-success)]() [![stress](https://img.shields.io/badge/stress--v117-20%2F20-success)]() [![mcp](https://img.shields.io/badge/MCP--tools-53-brightgreen)]() [![rounds](https://img.shields.io/badge/autonomous--rounds-102-blueviolet)]() [![main-push](https://img.shields.io/badge/release--main--push-33_rounds-success)]() [![stream-ux](https://img.shields.io/badge/슀트리밍-spinner%2Btool__use%2Bdiff_색깔-success)]() [![repl-tab](https://img.shields.io/badge/REPL-Tab_cycle%2Bμ‹€μ‹œκ°„_슀트리밍-success)]() [![mcp-bridge](https://img.shields.io/badge/MCP_bridge-web%2Fpc%2Flsp_λ…ΈμΆœ-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.174-green)]() [![tests](https://img.shields.io/badge/e2e-217%2F217-success)]() [![stress](https://img.shields.io/badge/stress--v119-20%2F20-success)]() [![mcp](https://img.shields.io/badge/MCP--tools-53-brightgreen)]() [![rounds](https://img.shields.io/badge/autonomous--rounds-104-blueviolet)]() [![main-push](https://img.shields.io/badge/release--main--push-35_rounds-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)]() [![mcp-bridge](https://img.shields.io/badge/MCP_bridge-web%2Fpc%2Flsp_λ…ΈμΆœ-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.172 AI Agent Reliability Harness + Sandbox β•‘
15
+ β•‘ v1.9.174 AI Agent Reliability Harness + Sandbox β•‘
16
16
  β•‘ verify Β· remember Β· orchestrate Β· audit Β· sandbox Β· drift β•‘
17
- β•‘ 🎨 슀트리밍 spinner + tool_use κ°€μ‹œν™” + diff μžλ™ 색깔 β•‘
17
+ β•‘ πŸ” install κΆŒν•œ prompt 제거 Β· REPL :permissions μ¦‰μ‹œ λ³€κ²½ β•‘
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.172';
9
+ const VERSION = '1.9.174';
10
10
  const MARK = '<!-- leerness:managed -->';
11
11
  const README_START = '<!-- leerness:project-readme:start -->';
12
12
  const README_END = '<!-- leerness:project-readme:end -->';
@@ -739,25 +739,11 @@ async function resolveInstallOptions(root, opts = {}) {
739
739
  }
740
740
  }
741
741
  }
742
- // 1.9.146: κΆŒν•œ λͺ¨λ“œ (μ‚¬μš©μž λͺ…μ‹œ μš”μ²­ #5 β€” agent IDE λͺ¨λ“œ 사전 prompt)
743
- let permissionMode = null;
744
- if (shouldAsk && !opts._skipPermissionsPrompt) {
745
- if (useInteractive) {
746
- const pOpt = await _selectOne('agent κΆŒν•œ λͺ¨λ“œ (leerness agent μ‚¬μš© μ‹œ 적용)', [
747
- { label: 'basic (μ•ˆμ „) β€” 읽기/μ“°κΈ° .harness/ 만', description: 'ꢌμž₯ β€” νŒŒμΌμ‹œμŠ€ν…œ/λ„€νŠΈμ›Œν¬ κ±°λΆ€, .harness μ•ˆλ§Œ μ“°κΈ°', id: 'basic' },
748
- { label: 'extended β€” ν”„λ‘œμ νŠΈ 폴더 + shell allowlist', description: 'ν”„λ‘œμ νŠΈ 폴더 read/write, 사전 μ •μ˜λœ λͺ…λ Ήλ§Œ exec', id: 'extended' },
749
- { label: 'full β€” 전체 (마우슀/ν‚€λ³΄λ“œ/μ›Ή/κ΄€λ¦¬μž) ⚠ μœ„ν—˜', description: '⚠ IDE 톡합 μ‹œμ—λ§Œ ꢌμž₯ β€” λͺ¨λ“  PC μž‘μ—… κ°€λŠ₯', id: 'full' }
750
- ], { defaultIndex: 0 });
751
- permissionMode = pOpt ? pOpt.id : 'basic';
752
- } else {
753
- log('\nagent κΆŒν•œ λͺ¨λ“œ (leerness agent λͺ…λ Ή μ‚¬μš© μ‹œ):');
754
- log('1) basic (μ•ˆμ „) β€” .harness/ 만');
755
- log('2) extended β€” ν”„λ‘œμ νŠΈ 폴더 + shell allowlist');
756
- log('3) full ⚠ β€” 마우슀/ν‚€λ³΄λ“œ/μ›Ή/κ΄€λ¦¬μž 전체 (IDE 톡합 μ‹œμ—λ§Œ)');
757
- const a = await ask('선택 [1]: ');
758
- permissionMode = a === '2' ? 'extended' : a === '3' ? 'full' : 'basic';
759
- }
760
- }
742
+ // 1.9.174: κΆŒν•œ prompt 제거 (μ‚¬μš©μž λͺ…μ‹œ) β€” install μ‹œ 항상 basic μžλ™ 적용.
743
+ // REPL μ•ˆμ—μ„œ `:permissions <basic|extended|full>` 둜 μ¦‰μ‹œ λ³€κ²½ κ°€λŠ₯ (REPL UX κ°•ν™”).
744
+ // 이전 1.9.146 의 3-tier 선택 prompt λŠ” μ‚¬μš©μž κ²½ν—˜ λ³΅μž‘λ„ 증가 + 잘λͺ»λœ 선택 (full) μ‹œ μœ„ν—˜ β†’
745
+ // μ•ˆμ „ν•œ κΈ°λ³Έ (basic) μžλ™ μ‹œμž‘ + REPL μ§„μž… μ‹œμ μ— ν•„μš” μ‹œ λ³€κ²½ν•˜λŠ” 흐름이 더 μ•ˆμ „ν•˜κ³  κ°„νŽΈ.
746
+ const permissionMode = 'basic';
761
747
  // 1.9.151: λͺ¨λ“  λ¬Έν•­ μ’…λ£Œ ν›„ β€” REPL λͺ¨λ“œ μ¦‰μ‹œ ν™œμ„±ν™” μ—¬λΆ€ (μ‚¬μš©μž λͺ…μ‹œ μš”μ²­)
762
748
  // μ„ νƒλœ μ—μ΄μ „νŠΈκ°€ μžˆμ„ λ•Œλ§Œ ν‘œμ‹œ. μ„€μΉ˜ μ™„λ£Œ ν›„ install() κ°€ 처리.
763
749
  let startRepl = false;
@@ -807,7 +793,7 @@ async function install(root, opts = {}) {
807
793
  log(`Agents ν™œμ„±ν™”: ${list}`);
808
794
  }
809
795
  if (resolved.startRepl) log(`REPL μžλ™ μ‹œμž‘: 예 (μ„€μΉ˜ μ™„λ£Œ ν›„ \`leerness agent\` μ§„μž…)`);
810
- if (resolved.permissionMode) log(`Agent κΆŒν•œ λͺ¨λ“œ: ${resolved.permissionMode}`);
796
+ if (resolved.permissionMode) log(`Agent κΆŒν•œ λͺ¨λ“œ: ${resolved.permissionMode} (1.9.174 β€” REPLμ—μ„œ \`:permissions extended|full\` 둜 μ¦‰μ‹œ λ³€κ²½ κ°€λŠ₯)`);
811
797
  // 1.9.10: μŠ€ν‚¬ μΉ΄νƒˆλ‘œκ·Έ 좜처 μ•ˆλ‚΄
812
798
  if (SKILLPACK_SOURCE === 'builtin') log(`Skill catalog source: builtin (leerness-skillpack λ―Έμ„€μΉ˜ β€” \`npm i leerness-skillpack\`둜 ν™•μž₯ κ°€λŠ₯)`);
813
799
  else log(`Skill catalog source: ${SKILLPACK_SOURCE} (leerness-skillpack${SKILLPACK_META ? ` v${SKILLPACK_META.version}` : ''})`);
@@ -11019,6 +11005,7 @@ async function _agentRepl(root, opts) {
11019
11005
  log(C.dim(' Slash λͺ…λ Ή (1.9.150): :verify | :audit | :handoff | :health'));
11020
11006
  log(C.dim(' Memory Slash (1.9.161): :lessons | :brainstorm <topic> | :tasks | :plan'));
11021
11007
  log(C.dim(' πŸ†• 1.9.170 β€” Tab=provider cycle, Shift+Tab=model cycle, :stream on|off (μ‹€μ‹œκ°„ 좜λ ₯)'));
11008
+ log(C.dim(' πŸ†• 1.9.174 β€” :permissions [basic|extended|full] 둜 μ¦‰μ‹œ κΆŒν•œ λ³€κ²½ (default: basic μ•ˆμ „)'));
11022
11009
  log(C.dim(` ν˜„μž¬ β€” provider=${state.provider} model=${state.model || '(κΈ°λ³Έ)'} role=${state.role} permissions=${_readPermissions(root).mode}`));
11023
11010
  // 1.9.155: REPL μ§„μž… μ‹œ handoff μ»¨ν…μŠ€νŠΈ μžλ™ λ…ΈμΆœ (UX κ°œμ„  β€” μ‚¬μš©μžκ°€ 맀번 :handoff μ•ˆ 해도 μ»¨ν…μŠ€νŠΈ 인지)
11024
11011
  try {
@@ -11123,7 +11110,8 @@ async function _agentRepl(root, opts) {
11123
11110
  log(' :reset β€” history μ΄ˆκΈ°ν™”');
11124
11111
  log(' :history β€” λŒ€ν™” history ν‘œμ‹œ');
11125
11112
  log(' :save β€” μ„Έμ…˜ μ¦‰μ‹œ μ €μž₯');
11126
- log(' :permissions β€” ν˜„μž¬ κΆŒν•œ λͺ¨λ“œ ν‘œμ‹œ');
11113
+ log(' :permissions [mode] β€” πŸ†• 1.9.174 ν˜„μž¬ λͺ¨λ“œ ν‘œμ‹œ / λ˜λŠ” μ¦‰μ‹œ λ³€κ²½ (basic/extended/full)');
11114
+ log(' :perm [mode] β€” :permissions 단좕 alias');
11127
11115
  log(' :quit / :exit / :q β€” μ’…λ£Œ (μžλ™ μ €μž₯)');
11128
11116
  log(C.bold('\n πŸ†• 1.9.170 ν‚€λ³΄λ“œ 단좕킀:'));
11129
11117
  log(' Tab β€” λ‹€μŒ provider 둜 cycle (ollama β†’ claude β†’ codex β†’ gemini β†’ copilot)');
@@ -11231,7 +11219,42 @@ async function _agentRepl(root, opts) {
11231
11219
  return false;
11232
11220
  }
11233
11221
  if (op === 'save') { saveSession(); log(C.dim(` β†’ ${rel(root, sessionPath())}`)); return false; }
11234
- if (op === 'permissions') { permissionsListCmd(root); return false; }
11222
+ if (op === 'permissions' || op === 'perm') {
11223
+ // 1.9.174: REPLμ—μ„œ κΆŒν•œ λ³€κ²½ κ°€λŠ₯ (μ‚¬μš©μž λͺ…μ‹œ μš”μ²­).
11224
+ // :permissions β€” ν˜„μž¬ λͺ¨λ“œ ν‘œμ‹œ + λ³€κ²½ μ˜΅μ…˜ μ•ˆλ‚΄
11225
+ // :permissions basic β€” κΆŒν•œ λͺ¨λ“œ μ¦‰μ‹œ λ³€κ²½
11226
+ const target = (rest[0] || '').toLowerCase();
11227
+ if (!target) {
11228
+ const p = _readPermissions(root);
11229
+ log('');
11230
+ log(C.bold(` πŸ” ν˜„μž¬ κΆŒν•œ λͺ¨λ“œ: ${C.cy(p.mode || 'basic')}`));
11231
+ log('');
11232
+ log(C.dim(' λ³€κ²½:'));
11233
+ log(` ${C.green(':permissions basic')} β€” μ•ˆμ „ (.harness 만 μ“°κΈ°, ꢌμž₯)`);
11234
+ log(` ${C.yel(':permissions extended')} β€” ν”„λ‘œμ νŠΈ 폴더 + shell allowlist`);
11235
+ log(` ${C.mag(':permissions full')} β€” ⚠ 전체 (마우슀/ν‚€λ³΄λ“œ/μ›Ή, IDE 톡합 μ‹œλ§Œ)`);
11236
+ log('');
11237
+ log(C.dim(' μ„ΈλΆ€ κΆŒν•œ (mouse/keyboard/browser/admin):'));
11238
+ Object.entries(p).filter(([k]) => k !== 'mode' && typeof p[k] === 'boolean').forEach(([k, v]) => {
11239
+ log(` ${k}: ${v ? C.green('βœ“ ν—ˆμš©') : C.dim('βœ— κ±°λΆ€')}`);
11240
+ });
11241
+ return false;
11242
+ }
11243
+ if (!['basic', 'extended', 'full'].includes(target)) {
11244
+ log(C.yel(` ⚠ 잘λͺ»λœ λͺ¨λ“œ: ${target} (basic | extended | full)`));
11245
+ return false;
11246
+ }
11247
+ try {
11248
+ permissionsSetCmd(root, target);
11249
+ log(C.green(` βœ“ κΆŒν•œ λͺ¨λ“œ λ³€κ²½: ${target} (μ¦‰μ‹œ 적용 β€” λ‹€μŒ λͺ…λ ΉλΆ€ν„°)`));
11250
+ if (target === 'full') {
11251
+ log(C.yel(' ⚠ full λͺ¨λ“œ β€” 마우슀/ν‚€λ³΄λ“œ/μ›Ή/κ΄€λ¦¬μž 전체 ν—ˆμš©. IDE 톡합 μ™Έ ν™˜κ²½μ—μ„œλŠ” μœ„ν—˜.'));
11252
+ }
11253
+ } catch (e) {
11254
+ log(C.yel(` ⚠ κΆŒν•œ λ³€κ²½ μ‹€νŒ¨: ${e.message}`));
11255
+ }
11256
+ return false;
11257
+ }
11235
11258
  if (op === 'status') {
11236
11259
  // 1.9.155: REPL μ•ˆμ—μ„œ ν˜„μž¬ μ„Έμ…˜ μƒνƒœ μžμ„Ένžˆ (provider/model/role/permissions/history/runs)
11237
11260
  log(C.bold('\n πŸ“Š REPL μ„Έμ…˜ μƒνƒœ (1.9.155)'));
@@ -12560,22 +12583,69 @@ function _tryLoadLSP() {
12560
12583
  return { ok: false, error: 'typescript λ―Έμ„€μΉ˜ β€” `npm i -g typescript` ν›„ λ‹€μ‹œ μ‹œλ„ (λ˜λŠ” μ •κ·œμ‹ fallback μ‚¬μš©)' };
12561
12584
  }
12562
12585
 
12563
- // μ •κ·œμ‹ fallback β€” TypeScript/JavaScript symbol μΆ”μΆœ (LSP 없이도 λ™μž‘)
12564
- function _lspRegexSymbols(content) {
12565
- const symbols = [];
12566
- const lines = content.split(/\r?\n/);
12567
- const patterns = [
12586
+ // 1.9.173: λ‹€κ΅­μ–΄ ν™•μž₯ β€” Python/Go/Rust/Java νŒ¨ν„΄ μΆ”κ°€ (regex fallback)
12587
+ // 파일 ν™•μž₯자 기반 μžλ™ λΌμš°νŒ…. TypeScript Compiler API λ―Έμ„€μΉ˜ μ‹œμ—λ„ 5개 μ–Έμ–΄ 지원.
12588
+ const _LSP_LANG_PATTERNS = {
12589
+ javascript: [
12568
12590
  { re: /^\s*(?:export\s+)?(?:async\s+)?function\s+([A-Za-z_$][\w$]*)\s*\(/, kind: 'function' },
12569
12591
  { re: /^\s*(?:export\s+)?class\s+([A-Za-z_$][\w$]*)/, kind: 'class' },
12570
12592
  { re: /^\s*(?:export\s+)?interface\s+([A-Za-z_$][\w$]*)/, kind: 'interface' },
12571
12593
  { re: /^\s*(?:export\s+)?(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*(?:async\s+)?(?:function|\()/, kind: 'function' },
12572
12594
  { re: /^\s*(?:export\s+)?type\s+([A-Za-z_$][\w$]*)\s*=/, kind: 'type' },
12573
- { re: /^\s*(?:export\s+)?enum\s+([A-Za-z_$][\w$]*)/, kind: 'enum' },
12574
- ];
12595
+ { re: /^\s*(?:export\s+)?enum\s+([A-Za-z_$][\w$]*)/, kind: 'enum' }
12596
+ ],
12597
+ python: [
12598
+ { re: /^\s*async\s+def\s+([A-Za-z_][\w]*)\s*\(/, kind: 'function' },
12599
+ { re: /^\s*def\s+([A-Za-z_][\w]*)\s*\(/, kind: 'function' },
12600
+ { re: /^\s*class\s+([A-Za-z_][\w]*)\s*[(:]/, kind: 'class' }
12601
+ ],
12602
+ go: [
12603
+ { re: /^\s*func\s+(?:\([^)]*\)\s+)?([A-Za-z_][\w]*)\s*\(/, kind: 'function' },
12604
+ { re: /^\s*type\s+([A-Za-z_][\w]*)\s+struct\b/, kind: 'struct' },
12605
+ { re: /^\s*type\s+([A-Za-z_][\w]*)\s+interface\b/, kind: 'interface' },
12606
+ { re: /^\s*type\s+([A-Za-z_][\w]*)\s+[A-Za-z]/, kind: 'type' }
12607
+ ],
12608
+ rust: [
12609
+ { re: /^\s*(?:pub(?:\([^)]+\))?\s+)?(?:async\s+)?fn\s+([A-Za-z_][\w]*)/, kind: 'function' },
12610
+ { re: /^\s*(?:pub(?:\([^)]+\))?\s+)?struct\s+([A-Za-z_][\w]*)/, kind: 'struct' },
12611
+ { re: /^\s*(?:pub(?:\([^)]+\))?\s+)?enum\s+([A-Za-z_][\w]*)/, kind: 'enum' },
12612
+ { re: /^\s*(?:pub(?:\([^)]+\))?\s+)?trait\s+([A-Za-z_][\w]*)/, kind: 'trait' },
12613
+ { re: /^\s*impl\s+(?:[^{]+\s+for\s+)?([A-Za-z_][\w]*)/, kind: 'impl' },
12614
+ { re: /^\s*(?:pub(?:\([^)]+\))?\s+)?type\s+([A-Za-z_][\w]*)\s*=/, kind: 'type' }
12615
+ ],
12616
+ java: [
12617
+ { re: /^\s*(?:public|private|protected)?\s*(?:final\s+)?(?:abstract\s+)?class\s+([A-Za-z_][\w]*)/, kind: 'class' },
12618
+ { re: /^\s*(?:public|private|protected)?\s*(?:abstract\s+)?interface\s+([A-Za-z_][\w]*)/, kind: 'interface' },
12619
+ { re: /^\s*(?:public|private|protected)?\s*(?:static\s+)?(?:final\s+)?enum\s+([A-Za-z_][\w]*)/, kind: 'enum' },
12620
+ // method: visibility + return type + name( (heuristic β€” 첫 번째 ( λ§€μΉ­, ν‚€μ›Œλ“œ ν•„ν„°)
12621
+ { re: /^\s*(?:public|private|protected)\s+(?:static\s+)?(?:final\s+)?(?:[A-Za-z_<>,\s\[\]]+\s+)?([A-Za-z_][\w]*)\s*\(/, kind: 'method' }
12622
+ ]
12623
+ };
12624
+
12625
+ function _detectLspLang(file) {
12626
+ const ext = (file.match(/\.[a-zA-Z0-9]+$/) || [''])[0].toLowerCase();
12627
+ if (/^\.(py|pyw|pyi)$/.test(ext)) return 'python';
12628
+ if (ext === '.go') return 'go';
12629
+ if (ext === '.rs') return 'rust';
12630
+ if (/^\.(java|kt|scala)$/.test(ext)) return 'java';
12631
+ if (/^\.(ts|tsx|js|jsx|mjs|cjs)$/.test(ext)) return 'javascript';
12632
+ return 'javascript'; // default β€” κΈ°λ³Έ JS νŒ¨ν„΄ (.txt/.md λ“± 미지원 ν™•μž₯자)
12633
+ }
12634
+
12635
+ // μ •κ·œμ‹ fallback β€” 5개 μ–Έμ–΄ (JS/TS/Python/Go/Rust/Java) symbol μΆ”μΆœ (LSP 없이도 λ™μž‘)
12636
+ // 1.9.173: lang 인자 μΆ”κ°€ β€” λ―Έμ§€μ • μ‹œ javascript νŒ¨ν„΄ (1.9.167 ν˜Έν™˜).
12637
+ function _lspRegexSymbols(content, lang) {
12638
+ const symbols = [];
12639
+ const lines = content.split(/\r?\n/);
12640
+ const patterns = _LSP_LANG_PATTERNS[lang || 'javascript'] || _LSP_LANG_PATTERNS.javascript;
12575
12641
  lines.forEach((line, idx) => {
12576
12642
  for (const p of patterns) {
12577
12643
  const m = line.match(p.re);
12578
- if (m) { symbols.push({ name: m[1], kind: p.kind, line: idx + 1 }); break; }
12644
+ // ν‚€μ›Œλ“œ false-positive 제거 (예: java method μ •κ·œμ‹μ΄ `if(`, `for(` 등에 λ§€μΉ˜λ˜λŠ” 경우)
12645
+ if (m && !/^(if|for|while|switch|catch|return|throw|new)$/.test(m[1])) {
12646
+ symbols.push({ name: m[1], kind: p.kind, line: idx + 1 });
12647
+ break;
12648
+ }
12579
12649
  }
12580
12650
  });
12581
12651
  return symbols;
@@ -12641,31 +12711,32 @@ function lspCmd(root, sub, ...args) {
12641
12711
  if (!fs.existsSync(file)) return fail(`파일 μ—†μŒ: ${file}`);
12642
12712
  const content = fs.readFileSync(file, 'utf8');
12643
12713
  const t0 = Date.now();
12714
+ const lang = _detectLspLang(file); // 1.9.173: μ–Έμ–΄ μžλ™ 감지
12644
12715
  const r = _tryLoadLSP();
12645
12716
  let symbols, mode;
12646
12717
  try {
12647
- if (r.ok && /\.(ts|tsx|js|jsx|mjs|cjs)$/.test(file)) {
12718
+ if (r.ok && lang === 'javascript' && /\.(ts|tsx|js|jsx|mjs|cjs)$/.test(file)) {
12648
12719
  symbols = _lspTsSymbols(r.lib, content, file);
12649
12720
  mode = 'typescript-compiler';
12650
12721
  } else {
12651
- symbols = _lspRegexSymbols(content);
12652
- mode = 'regex-fallback';
12722
+ symbols = _lspRegexSymbols(content, lang); // 1.9.173: lang 전달
12723
+ mode = `regex-fallback (${lang})`;
12653
12724
  }
12654
12725
  } catch (e) {
12655
- symbols = _lspRegexSymbols(content);
12656
- mode = 'regex-fallback (after error: ' + e.message + ')';
12726
+ symbols = _lspRegexSymbols(content, lang);
12727
+ mode = `regex-fallback (${lang}, after error: ${e.message})`;
12657
12728
  }
12658
12729
  const dt = Date.now() - t0;
12659
12730
  if (has('--json')) {
12660
- log(JSON.stringify({ file, symbols, count: symbols.length, mode, durationMs: dt }, null, 2));
12731
+ log(JSON.stringify({ file, lang, symbols, count: symbols.length, mode, durationMs: dt }, null, 2));
12661
12732
  } else {
12662
- log(`# leerness lsp symbols (1.9.167)`);
12663
- log(`file: ${file}`);
12733
+ log(`# leerness lsp symbols (1.9.173 λ‹€κ΅­μ–΄)`);
12734
+ log(`file: ${file} Β· lang: ${lang}`);
12664
12735
  log(`mode: ${mode} Β· ${symbols.length} symbols Β· ${dt}ms`);
12665
12736
  symbols.slice(0, 50).forEach(s => log(` ${String(s.line).padStart(5)}:${s.kind.padEnd(10)} ${s.name}`));
12666
12737
  if (symbols.length > 50) log(` ... ${symbols.length - 50} more`);
12667
12738
  }
12668
- try { _recordRun(root, { kind: 'lsp_symbols', file, count: symbols.length, mode, durationMs: dt, ok: true }); } catch {}
12739
+ try { _recordRun(root, { kind: 'lsp_symbols', file, lang, count: symbols.length, mode, durationMs: dt, ok: true }); } catch {}
12669
12740
  return;
12670
12741
  }
12671
12742
  if (sub === 'references') {
@@ -12683,7 +12754,7 @@ function lspCmd(root, sub, ...args) {
12683
12754
  if (e.name.startsWith('.') || e.name === 'node_modules' || e.name === 'dist' || e.name === 'build') continue;
12684
12755
  const p = path.join(d, e.name);
12685
12756
  if (e.isDirectory()) walk(p);
12686
- else if (/\.(ts|tsx|js|jsx|mjs|cjs|md)$/.test(e.name)) {
12757
+ else if (/\.(ts|tsx|js|jsx|mjs|cjs|md|py|pyw|pyi|go|rs|java|kt|scala)$/.test(e.name)) {
12687
12758
  try {
12688
12759
  const lines = fs.readFileSync(p, 'utf8').split(/\r?\n/);
12689
12760
  lines.forEach((ln, idx) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leerness",
3
- "version": "1.9.172",
3
+ "version": "1.9.174",
4
4
  "description": "Leerness: λΉ„νŒŒκ΄΄ λ§ˆμ΄κ·Έλ ˆμ΄μ…˜, μžλ™ 버전 κ°μ§€Β·μ—…λ°μ΄νŠΈ, κ³„νš/μ§„ν–‰/ν•Έλ“œμ˜€ν”„ μžλ™ν™”, κ²ŒμœΌλ¦„Β·μ‹œν¬λ¦ΏΒ·μΈμ½”λ”© μžλ™ κ°€λ“œ, Claude Code μŠ¬λž˜μ‹œ 톡합을 κ°–μΆ˜ ν•œκ΅­μ–΄ μš°μ„  AI 개발 ν•˜λ„€μŠ€.",
5
5
  "keywords": [
6
6
  "leerness",