leerness 1.9.171 β 1.9.173
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 +117 -0
- package/README.md +3 -3
- package/bin/harness.js +139 -23
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,122 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.9.173 β 2026-05-21
|
|
4
|
+
|
|
5
|
+
**π LSP μ΄λν° λ€κ΅μ΄ νμ₯ β JavaScript + Python + Go + Rust + Java (5κ° μΈμ΄ regex fallback).**
|
|
6
|
+
|
|
7
|
+
μμ¨ λͺ¨λ 103 λΌμ΄λ. 1.9.167 codeIntel (JS/TS only) β 5κ° μΈμ΄ νμ₯.
|
|
8
|
+
|
|
9
|
+
### `_LSP_LANG_PATTERNS` β 5κ° μΈμ΄ ν¨ν΄
|
|
10
|
+
| μΈμ΄ | μΆμΆ κ°λ₯ |
|
|
11
|
+
|---|---|
|
|
12
|
+
| **javascript** (.ts/.tsx/.js/.jsx/.mjs/.cjs) | function, class, interface, type, enum, arrow function |
|
|
13
|
+
| **python** (.py/.pyw/.pyi) | def, async def, class |
|
|
14
|
+
| **go** (.go) | func (receiver ν¬ν¨), type struct/interface, type alias |
|
|
15
|
+
| **rust** (.rs) | fn (pub/async), struct, enum, trait, impl, type |
|
|
16
|
+
| **java** (.java/.kt/.scala) | class (public/private/abstract), interface, enum, method |
|
|
17
|
+
|
|
18
|
+
### `_detectLspLang(file)` β νμ₯μ μλ λΌμ°ν
|
|
19
|
+
νμΌ νμ₯μ κΈ°λ° μΈμ΄ μλ κ°μ§. λ―Έμ§μ νμ₯μλ javascript κΈ°λ³Έ (1.9.167 νΈν).
|
|
20
|
+
|
|
21
|
+
### μ¬μ© μμ
|
|
22
|
+
```bash
|
|
23
|
+
$ leerness lsp symbols src/api.py
|
|
24
|
+
# leerness lsp symbols (1.9.173 λ€κ΅μ΄)
|
|
25
|
+
file: src/api.py Β· lang: python
|
|
26
|
+
mode: regex-fallback (python) Β· 12 symbols Β· 4ms
|
|
27
|
+
1:function parse_request
|
|
28
|
+
8:function fetch_data
|
|
29
|
+
15:class Handler
|
|
30
|
+
16:function __init__
|
|
31
|
+
...
|
|
32
|
+
|
|
33
|
+
$ leerness lsp symbols src/main.rs
|
|
34
|
+
# leerness lsp symbols (1.9.173 λ€κ΅μ΄)
|
|
35
|
+
file: src/main.rs Β· lang: rust
|
|
36
|
+
mode: regex-fallback (rust) Β· 9 symbols Β· 5ms
|
|
37
|
+
1:function hello
|
|
38
|
+
5:struct User
|
|
39
|
+
9:impl User
|
|
40
|
+
15:trait Greeter
|
|
41
|
+
...
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### ν€μλ false-positive μ κ±°
|
|
45
|
+
Java method μ κ·μμ΄ `if(`, `for(`, `while(`, `switch(`, `catch(`, `return(`, `throw(`, `new(` λ± ν€μλμ λ§€μΉλλ κ²½μ° νν°.
|
|
46
|
+
|
|
47
|
+
### references λ€κ΅μ΄ νμΌ μ€μΊ
|
|
48
|
+
`leerness lsp references <name>` κ° `.py/.go/.rs/.java/.kt/.scala` νμΌλ μ€μΊ (κΈ°μ‘΄ `.ts/.js/.md` μ μΆκ°).
|
|
49
|
+
|
|
50
|
+
### μ€μΈ‘ (regex fallback)
|
|
51
|
+
- Python (5 symbols): 472ms
|
|
52
|
+
- Go (4 symbols): 566ms
|
|
53
|
+
- Rust (6 symbols): 531ms
|
|
54
|
+
- Java (4 symbols): 1229ms
|
|
55
|
+
|
|
56
|
+
### Verified
|
|
57
|
+
- e2e 217/217 baseline μ μ§
|
|
58
|
+
- stress-v118: **15/15** (ν¨ν΄ μ μ 4 + Python 1 + Go 1 + Rust 1 + Java 1 + JS νΈν 1 + references 1 + λμ νκ· 5)
|
|
59
|
+
- VERSION = 1.9.173 / autonomous-rounds = 103 / main μλ push 34 λΌμ΄λ μ°μ
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## 1.9.172 β 2026-05-21
|
|
64
|
+
|
|
65
|
+
**π¨ μ€νΈλ¦¬λ° UX κ°ν β spinner + Claude tool_use κ°μν + diff ν¨ν΄ μλ μκΉ (μ¬μ©μ λͺ
μ κ°ν).**
|
|
66
|
+
|
|
67
|
+
μμ¨ λͺ¨λ 102 λΌμ΄λ. 1.9.170 μ¬μ©μ λͺ
μ ("ν°λ―Έλ νλ©΄ κ³μ κ°±μ , μΆλ‘ μ€/diff/μκ°νλ κ³Όμ μ€μκ° νμ") μ μ§μ§ μλ 보κ°.
|
|
68
|
+
|
|
69
|
+
### 1. μΆλ‘ μ€ Spinner β Visual Feedback
|
|
70
|
+
- `_cliChatStream` μμ `setInterval(120ms)` spinner β stdout idle 800ms+ μ κΉλΉ‘μ
|
|
71
|
+
- `β β β Ήβ Έβ Όβ ΄β ¦β §β β ` 10 frames + κ²½κ³Ό μκ° νμ ("β μΆλ‘ μ€... (3s)")
|
|
72
|
+
- `\r\x1b[K` ANSI escape β νμ¬ λΌμΈ ν΄λ¦¬μ΄ + spinner κ°±μ
|
|
73
|
+
- `stopSpinner()` β stdout/stderr data λμ°© μ μ¦μ spinner λΌμΈ ν΄λ¦¬μ΄
|
|
74
|
+
- μ΅μ
: `opts.noSpinner: true` λλ λΉ-TTY νκ²½μμ μλ λΉνμ±
|
|
75
|
+
|
|
76
|
+
### 2. Claude tool_use κ°μν
|
|
77
|
+
- Claude `--output-format=stream-json` μ `content_block_start` μ΄λ²€νΈμμ `type='tool_use'` κ°μ§
|
|
78
|
+
- νμ: `π§ Tool: Read νΈμΆ μ€...` (cyan μ)
|
|
79
|
+
- AIκ° νμΌ μ½κΈ°/μ°κΈ°/Bash νΈμΆ μ μ¬μ©μκ° μ¦μ μΈμ§
|
|
80
|
+
|
|
81
|
+
### 3. diff ν¨ν΄ μλ μκΉ (writeColored)
|
|
82
|
+
```diff
|
|
83
|
+
diff --git a/file.js b/file.js # yellow (ν€λ)
|
|
84
|
+
--- a/file.js # yellow
|
|
85
|
+
+++ b/file.js # yellow
|
|
86
|
+
@@ -10,3 +10,3 @@ # cyan (hunk)
|
|
87
|
+
- old line # red
|
|
88
|
+
+ new line # green
|
|
89
|
+
context # κΈ°λ³Έ μ
|
|
90
|
+
```
|
|
91
|
+
- λΌμΈ λ¨μ μ²λ¦¬ (`lineBuf` λ²νΌλ§)
|
|
92
|
+
- μ κ·μ λ§€μΉ: `/^\+(?!\+\+)/` β green, `/^-(?!--)/` β red, `/^@@.*@@/` β cyan
|
|
93
|
+
- `flushLineBuf()` β μ’
λ£ μ μμ¬ λΌμΈ μΆλ ₯
|
|
94
|
+
|
|
95
|
+
### ν΅ν© νλ¦
|
|
96
|
+
```
|
|
97
|
+
user> μ΄ ν¨μ 리ν©ν λ§ + diff 보μ¬μ€
|
|
98
|
+
β claude CLI stream νΈμΆ μ€... (Ctrl+C λ‘ μ€λ¨)
|
|
99
|
+
ββ claude stream ββ
|
|
100
|
+
β Ή μΆλ‘ μ€... (2s) # μλ΅ μμ μ spinner
|
|
101
|
+
[claude-opus-4-7]
|
|
102
|
+
π§ Tool: Read νΈμΆ μ€... # tool_use κ°μ§
|
|
103
|
+
|
|
104
|
+
λΆμ κ²°κ³Ό:
|
|
105
|
+
|
|
106
|
+
diff --git a/utils.js b/utils.js # yellow
|
|
107
|
+
@@ -5,3 +5,3 @@ # cyan
|
|
108
|
+
- function bad() { return null; } # red
|
|
109
|
+
+ function good() { return {}; } # green
|
|
110
|
+
ββ /stream (4521ms) ββ
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Verified
|
|
114
|
+
- e2e 217/217 baseline μ μ§
|
|
115
|
+
- stress-v117: **20/20** (spinner 4 + tool_use 2 + diff 4 + ANSI helpers 2 + ν΅ν© 2 + λμ νκ· 6)
|
|
116
|
+
- VERSION = 1.9.172 / autonomous-rounds = 102 / main μλ push 33 λΌμ΄λ μ°μ
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
3
120
|
## 1.9.171 β 2026-05-21
|
|
4
121
|
|
|
5
122
|
**AGENTS.md / CLAUDE.md / session-workflow.md 1.9.88~170 λμ κ°±μ (drift μ°¨λ¨).**
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> **AI μ½λ© μμ΄μ νΈμ κ±°μ§ μλ£Β·μ€λ³΅Β·λ§κ°Β·μΆ©λμ λ§μμ£Όλ κ²μΒ·κΈ°μ΅Β·νμ
CLI νλ€μ€.**
|
|
4
4
|
|
|
5
|
-
[](https://www.npmjs.com/package/leerness) [](https://www.npmjs.com/package/leerness) []() []() []() []() []() []() []() []() []() []() []() []()
|
|
6
6
|
|
|
7
7
|
```
|
|
8
8
|
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
@@ -12,9 +12,9 @@
|
|
|
12
12
|
β βββ ββββββ ββββββ ββββββββββββββββββββββββ ββββββββ β
|
|
13
13
|
β βββββββββββββββββββββββββββ ββββββ ββββββββββββββββββββββ β
|
|
14
14
|
β βββββββββββββββββββββββββββ ββββββ βββββββββββββββββββββ β
|
|
15
|
-
β v1.9.
|
|
15
|
+
β v1.9.173 AI Agent Reliability Harness + Sandbox β
|
|
16
16
|
β verify Β· remember Β· orchestrate Β· audit Β· sandbox Β· drift β
|
|
17
|
-
β
|
|
17
|
+
β π LSP λ€κ΅μ΄ 5μ’
(JS/Python/Go/Rust/Java regex fallback) β
|
|
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.
|
|
9
|
+
const VERSION = '1.9.173';
|
|
10
10
|
const MARK = '<!-- leerness:managed -->';
|
|
11
11
|
const README_START = '<!-- leerness:project-readme:start -->';
|
|
12
12
|
const README_END = '<!-- leerness:project-readme:end -->';
|
|
@@ -10552,6 +10552,7 @@ async function _cliChat(root, provider, prompt, opts) {
|
|
|
10552
10552
|
}
|
|
10553
10553
|
|
|
10554
10554
|
// 1.9.170: REPL μ€μκ° μ€νΈλ¦¬λ° β CLI stdoutμ μ¦μ ν°λ―Έλμ νμ (μ¬μ©μ λͺ
μ μμ²)
|
|
10555
|
+
// 1.9.172: μΆλ‘ μ€ spinner + Claude tool_use κ°μν + diff ν¨ν΄ μλ μκΉ (μ¬μ©μ λͺ
μ κ°ν)
|
|
10555
10556
|
// μΆλ‘ μ€/diff/thinking κ³Όμ μ΄ stdout μΌλ‘ νλ₯΄λ λμ μ€μκ° λ λλ§.
|
|
10556
10557
|
// runCommandSafe μ λ°°μΉ κ²°κ³Όμ λ¬λ¦¬ cp.spawn μ child.stdout μ μ§μ pipe.
|
|
10557
10558
|
// env scrub + cwd jail μ λμΌνκ² μ μ©.
|
|
@@ -10574,9 +10575,55 @@ async function _cliChatStream(root, provider, promptText, opts) {
|
|
|
10574
10575
|
let out = '', err = '';
|
|
10575
10576
|
let buf = ''; // claude stream-json λΌμΈ λ²νΌ
|
|
10576
10577
|
let firstChunk = true;
|
|
10578
|
+
let lineBuf = ''; // 1.9.172: diff ν¨ν΄ μκΉ μν λΌμΈ λ²νΌ
|
|
10577
10579
|
const isTty = process.stdout.isTTY;
|
|
10578
10580
|
const dim = isTty ? (s) => `\x1b[2m${s}\x1b[0m` : s => s;
|
|
10579
10581
|
const cy = isTty ? (s) => `\x1b[36m${s}\x1b[0m` : s => s;
|
|
10582
|
+
const green = isTty ? (s) => `\x1b[32m${s}\x1b[0m` : s => s;
|
|
10583
|
+
const red = isTty ? (s) => `\x1b[31m${s}\x1b[0m` : s => s;
|
|
10584
|
+
const yel = isTty ? (s) => `\x1b[33m${s}\x1b[0m` : s => s;
|
|
10585
|
+
|
|
10586
|
+
// 1.9.172: μΆλ‘ μ€ spinner β stdoutμ΄ μΌμ μκ° idle νλ©΄ κΉλΉ‘μ΄λ visual feedback
|
|
10587
|
+
// AIκ° μλ΅ μμνκΈ° μ (preprocessing/thinking) λμ μ¬μ©μκ° "λμ μ€" μΈμ§ νμΈ.
|
|
10588
|
+
const spinnerFrames = ['β ', 'β ', 'β Ή', 'β Έ', 'β Ό', 'β ΄', 'β ¦', 'β §', 'β ', 'β '];
|
|
10589
|
+
let spinnerIdx = 0;
|
|
10590
|
+
let spinnerActive = false;
|
|
10591
|
+
let lastActivity = Date.now();
|
|
10592
|
+
const spinnerInterval = isTty && !opts.noSpinner ? setInterval(() => {
|
|
10593
|
+
const idle = Date.now() - lastActivity;
|
|
10594
|
+
if (idle > 800) {
|
|
10595
|
+
if (!spinnerActive) { spinnerActive = true; }
|
|
10596
|
+
// νμ¬ λΌμΈμ ν΄λ¦¬μ΄νκ³ spinner νμ
|
|
10597
|
+
process.stdout.write(`\r\x1b[K${dim(spinnerFrames[spinnerIdx % spinnerFrames.length] + ' μΆλ‘ μ€... (' + Math.floor(idle / 1000) + 's)')}`);
|
|
10598
|
+
spinnerIdx++;
|
|
10599
|
+
}
|
|
10600
|
+
}, 120) : null;
|
|
10601
|
+
const stopSpinner = () => {
|
|
10602
|
+
if (spinnerActive) {
|
|
10603
|
+
process.stdout.write('\r\x1b[K'); // spinner λΌμΈ ν΄λ¦¬μ΄
|
|
10604
|
+
spinnerActive = false;
|
|
10605
|
+
}
|
|
10606
|
+
lastActivity = Date.now();
|
|
10607
|
+
};
|
|
10608
|
+
|
|
10609
|
+
// 1.9.172: diff ν¨ν΄ μλ μκΉ β `+ ` / `- ` / `@@` λΌμΈμ green/red/cyan
|
|
10610
|
+
function writeColored(s) {
|
|
10611
|
+
// λΌμΈ λ¨μλ‘ μ²λ¦¬ (λΌμΈ λκΉμ§ λͺ¨μ ν μκΉ μ
νκΈ°)
|
|
10612
|
+
lineBuf += s;
|
|
10613
|
+
const lines = lineBuf.split('\n');
|
|
10614
|
+
lineBuf = lines.pop() || '';
|
|
10615
|
+
for (const ln of lines) {
|
|
10616
|
+
if (/^\+(?!\+\+)/.test(ln)) process.stdout.write(green(ln) + '\n');
|
|
10617
|
+
else if (/^-(?!--)/.test(ln)) process.stdout.write(red(ln) + '\n');
|
|
10618
|
+
else if (/^@@.*@@/.test(ln)) process.stdout.write(cy(ln) + '\n');
|
|
10619
|
+
else if (/^(diff --git|---|\+\+\+|index )/.test(ln)) process.stdout.write(yel(ln) + '\n');
|
|
10620
|
+
else process.stdout.write(ln + '\n');
|
|
10621
|
+
}
|
|
10622
|
+
}
|
|
10623
|
+
function flushLineBuf() {
|
|
10624
|
+
if (lineBuf) { process.stdout.write(lineBuf); lineBuf = ''; }
|
|
10625
|
+
}
|
|
10626
|
+
|
|
10580
10627
|
process.stdout.write(dim(`\n ββ ${provider} stream ββ\n`));
|
|
10581
10628
|
let child;
|
|
10582
10629
|
try {
|
|
@@ -10587,6 +10634,7 @@ async function _cliChatStream(root, provider, promptText, opts) {
|
|
|
10587
10634
|
stdio: ['ignore', 'pipe', 'pipe']
|
|
10588
10635
|
});
|
|
10589
10636
|
} catch (e) {
|
|
10637
|
+
if (spinnerInterval) clearInterval(spinnerInterval);
|
|
10590
10638
|
return resolve({ ok: false, error: 'spawn μ€ν¨: ' + e.message, provider });
|
|
10591
10639
|
}
|
|
10592
10640
|
const timer = setTimeout(() => {
|
|
@@ -10594,6 +10642,7 @@ async function _cliChatStream(root, provider, promptText, opts) {
|
|
|
10594
10642
|
}, opts.timeout || 120000);
|
|
10595
10643
|
|
|
10596
10644
|
// Claude stream-json λͺ¨λ: κ° λΌμΈμ΄ JSON event. type=assistant_message_delta.text λ§ μΆλ ₯
|
|
10645
|
+
// 1.9.172: tool_use κ°μν μΆκ° (Read/Write/Bash νΈμΆ μ "π§ Tool: <name>" νμ)
|
|
10597
10646
|
function handleClaudeStream(chunk) {
|
|
10598
10647
|
buf += chunk.toString();
|
|
10599
10648
|
const lines = buf.split('\n');
|
|
@@ -10602,44 +10651,63 @@ async function _cliChatStream(root, provider, promptText, opts) {
|
|
|
10602
10651
|
if (!ln.trim()) continue;
|
|
10603
10652
|
try {
|
|
10604
10653
|
const ev = JSON.parse(ln);
|
|
10605
|
-
// assistant λ©μμ§ chunk
|
|
10654
|
+
// assistant λ©μμ§ chunk
|
|
10606
10655
|
if (ev.type === 'content_block_delta' && ev.delta?.text) {
|
|
10607
|
-
|
|
10656
|
+
stopSpinner();
|
|
10657
|
+
writeColored(ev.delta.text);
|
|
10608
10658
|
out += ev.delta.text;
|
|
10609
10659
|
} else if (ev.type === 'message_start' && firstChunk) {
|
|
10610
10660
|
firstChunk = false;
|
|
10661
|
+
stopSpinner();
|
|
10611
10662
|
process.stdout.write(dim(` [${ev.message?.model || provider}] `));
|
|
10612
10663
|
} else if (ev.type === 'thinking_delta' && ev.delta?.thinking) {
|
|
10664
|
+
stopSpinner();
|
|
10613
10665
|
process.stdout.write(dim(ev.delta.thinking));
|
|
10666
|
+
} else if (ev.type === 'content_block_start' && ev.content_block?.type === 'tool_use') {
|
|
10667
|
+
// 1.9.172: tool_use μμ μ "π§ Tool: Read" κ°μ μλ΄
|
|
10668
|
+
stopSpinner();
|
|
10669
|
+
const toolName = ev.content_block.name || 'tool';
|
|
10670
|
+
process.stdout.write(cy(`\n π§ Tool: ${toolName} νΈμΆ μ€...\n`));
|
|
10671
|
+
} else if (ev.type === 'tool_use' || ev.type === 'content_block_stop') {
|
|
10672
|
+
// λꡬ μλ£ μκ·Έλ (νμμ νλ νμ)
|
|
10614
10673
|
}
|
|
10615
10674
|
} catch {
|
|
10616
10675
|
// JSON μλλ©΄ κ·Έλλ‘ μΆλ ₯ (fallback)
|
|
10617
|
-
|
|
10676
|
+
stopSpinner();
|
|
10677
|
+
writeColored(ln + '\n');
|
|
10618
10678
|
out += ln + '\n';
|
|
10619
10679
|
}
|
|
10620
10680
|
}
|
|
10621
10681
|
}
|
|
10622
10682
|
|
|
10623
10683
|
child.stdout.on('data', chunk => {
|
|
10684
|
+
lastActivity = Date.now();
|
|
10624
10685
|
if (provider === 'claude') {
|
|
10625
10686
|
handleClaudeStream(chunk);
|
|
10626
10687
|
} else {
|
|
10688
|
+
stopSpinner();
|
|
10627
10689
|
const s = chunk.toString();
|
|
10628
10690
|
out += s;
|
|
10629
|
-
|
|
10691
|
+
writeColored(s);
|
|
10630
10692
|
}
|
|
10631
10693
|
});
|
|
10632
10694
|
child.stderr.on('data', chunk => {
|
|
10695
|
+
lastActivity = Date.now();
|
|
10696
|
+
stopSpinner();
|
|
10633
10697
|
const s = chunk.toString();
|
|
10634
10698
|
err += s;
|
|
10635
10699
|
// μΆλ‘ μ€/μ§ν λ©μμ§λ dim μΌλ‘ (μ€μ μλ¬λ μΆλ ₯ νμ μ’
ν©)
|
|
10636
10700
|
process.stdout.write(dim(s));
|
|
10637
10701
|
});
|
|
10638
10702
|
child.on('error', e => {
|
|
10703
|
+
if (spinnerInterval) clearInterval(spinnerInterval);
|
|
10639
10704
|
clearTimeout(timer);
|
|
10640
10705
|
resolve({ ok: false, error: 'child error: ' + e.message, provider });
|
|
10641
10706
|
});
|
|
10642
10707
|
child.on('close', code => {
|
|
10708
|
+
if (spinnerInterval) clearInterval(spinnerInterval);
|
|
10709
|
+
stopSpinner();
|
|
10710
|
+
flushLineBuf(); // 1.9.172: lineBuf μμ¬ flush
|
|
10643
10711
|
clearTimeout(timer);
|
|
10644
10712
|
const dt = Date.now() - t0;
|
|
10645
10713
|
process.stdout.write(dim(`\n ββ /stream (${dt}ms) ββ\n`));
|
|
@@ -12492,22 +12560,69 @@ function _tryLoadLSP() {
|
|
|
12492
12560
|
return { ok: false, error: 'typescript λ―Έμ€μΉ β `npm i -g typescript` ν λ€μ μλ (λλ μ κ·μ fallback μ¬μ©)' };
|
|
12493
12561
|
}
|
|
12494
12562
|
|
|
12495
|
-
//
|
|
12496
|
-
|
|
12497
|
-
|
|
12498
|
-
|
|
12499
|
-
const patterns = [
|
|
12563
|
+
// 1.9.173: λ€κ΅μ΄ νμ₯ β Python/Go/Rust/Java ν¨ν΄ μΆκ° (regex fallback)
|
|
12564
|
+
// νμΌ νμ₯μ κΈ°λ° μλ λΌμ°ν
. TypeScript Compiler API λ―Έμ€μΉ μμλ 5κ° μΈμ΄ μ§μ.
|
|
12565
|
+
const _LSP_LANG_PATTERNS = {
|
|
12566
|
+
javascript: [
|
|
12500
12567
|
{ re: /^\s*(?:export\s+)?(?:async\s+)?function\s+([A-Za-z_$][\w$]*)\s*\(/, kind: 'function' },
|
|
12501
12568
|
{ re: /^\s*(?:export\s+)?class\s+([A-Za-z_$][\w$]*)/, kind: 'class' },
|
|
12502
12569
|
{ re: /^\s*(?:export\s+)?interface\s+([A-Za-z_$][\w$]*)/, kind: 'interface' },
|
|
12503
12570
|
{ re: /^\s*(?:export\s+)?(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*(?:async\s+)?(?:function|\()/, kind: 'function' },
|
|
12504
12571
|
{ re: /^\s*(?:export\s+)?type\s+([A-Za-z_$][\w$]*)\s*=/, kind: 'type' },
|
|
12505
|
-
{ re: /^\s*(?:export\s+)?enum\s+([A-Za-z_$][\w$]*)/, kind: 'enum' }
|
|
12506
|
-
]
|
|
12572
|
+
{ re: /^\s*(?:export\s+)?enum\s+([A-Za-z_$][\w$]*)/, kind: 'enum' }
|
|
12573
|
+
],
|
|
12574
|
+
python: [
|
|
12575
|
+
{ re: /^\s*async\s+def\s+([A-Za-z_][\w]*)\s*\(/, kind: 'function' },
|
|
12576
|
+
{ re: /^\s*def\s+([A-Za-z_][\w]*)\s*\(/, kind: 'function' },
|
|
12577
|
+
{ re: /^\s*class\s+([A-Za-z_][\w]*)\s*[(:]/, kind: 'class' }
|
|
12578
|
+
],
|
|
12579
|
+
go: [
|
|
12580
|
+
{ re: /^\s*func\s+(?:\([^)]*\)\s+)?([A-Za-z_][\w]*)\s*\(/, kind: 'function' },
|
|
12581
|
+
{ re: /^\s*type\s+([A-Za-z_][\w]*)\s+struct\b/, kind: 'struct' },
|
|
12582
|
+
{ re: /^\s*type\s+([A-Za-z_][\w]*)\s+interface\b/, kind: 'interface' },
|
|
12583
|
+
{ re: /^\s*type\s+([A-Za-z_][\w]*)\s+[A-Za-z]/, kind: 'type' }
|
|
12584
|
+
],
|
|
12585
|
+
rust: [
|
|
12586
|
+
{ re: /^\s*(?:pub(?:\([^)]+\))?\s+)?(?:async\s+)?fn\s+([A-Za-z_][\w]*)/, kind: 'function' },
|
|
12587
|
+
{ re: /^\s*(?:pub(?:\([^)]+\))?\s+)?struct\s+([A-Za-z_][\w]*)/, kind: 'struct' },
|
|
12588
|
+
{ re: /^\s*(?:pub(?:\([^)]+\))?\s+)?enum\s+([A-Za-z_][\w]*)/, kind: 'enum' },
|
|
12589
|
+
{ re: /^\s*(?:pub(?:\([^)]+\))?\s+)?trait\s+([A-Za-z_][\w]*)/, kind: 'trait' },
|
|
12590
|
+
{ re: /^\s*impl\s+(?:[^{]+\s+for\s+)?([A-Za-z_][\w]*)/, kind: 'impl' },
|
|
12591
|
+
{ re: /^\s*(?:pub(?:\([^)]+\))?\s+)?type\s+([A-Za-z_][\w]*)\s*=/, kind: 'type' }
|
|
12592
|
+
],
|
|
12593
|
+
java: [
|
|
12594
|
+
{ re: /^\s*(?:public|private|protected)?\s*(?:final\s+)?(?:abstract\s+)?class\s+([A-Za-z_][\w]*)/, kind: 'class' },
|
|
12595
|
+
{ re: /^\s*(?:public|private|protected)?\s*(?:abstract\s+)?interface\s+([A-Za-z_][\w]*)/, kind: 'interface' },
|
|
12596
|
+
{ re: /^\s*(?:public|private|protected)?\s*(?:static\s+)?(?:final\s+)?enum\s+([A-Za-z_][\w]*)/, kind: 'enum' },
|
|
12597
|
+
// method: visibility + return type + name( (heuristic β 첫 λ²μ§Έ ( λ§€μΉ, ν€μλ νν°)
|
|
12598
|
+
{ re: /^\s*(?:public|private|protected)\s+(?:static\s+)?(?:final\s+)?(?:[A-Za-z_<>,\s\[\]]+\s+)?([A-Za-z_][\w]*)\s*\(/, kind: 'method' }
|
|
12599
|
+
]
|
|
12600
|
+
};
|
|
12601
|
+
|
|
12602
|
+
function _detectLspLang(file) {
|
|
12603
|
+
const ext = (file.match(/\.[a-zA-Z0-9]+$/) || [''])[0].toLowerCase();
|
|
12604
|
+
if (/^\.(py|pyw|pyi)$/.test(ext)) return 'python';
|
|
12605
|
+
if (ext === '.go') return 'go';
|
|
12606
|
+
if (ext === '.rs') return 'rust';
|
|
12607
|
+
if (/^\.(java|kt|scala)$/.test(ext)) return 'java';
|
|
12608
|
+
if (/^\.(ts|tsx|js|jsx|mjs|cjs)$/.test(ext)) return 'javascript';
|
|
12609
|
+
return 'javascript'; // default β κΈ°λ³Έ JS ν¨ν΄ (.txt/.md λ± λ―Έμ§μ νμ₯μ)
|
|
12610
|
+
}
|
|
12611
|
+
|
|
12612
|
+
// μ κ·μ fallback β 5κ° μΈμ΄ (JS/TS/Python/Go/Rust/Java) symbol μΆμΆ (LSP μμ΄λ λμ)
|
|
12613
|
+
// 1.9.173: lang μΈμ μΆκ° β λ―Έμ§μ μ javascript ν¨ν΄ (1.9.167 νΈν).
|
|
12614
|
+
function _lspRegexSymbols(content, lang) {
|
|
12615
|
+
const symbols = [];
|
|
12616
|
+
const lines = content.split(/\r?\n/);
|
|
12617
|
+
const patterns = _LSP_LANG_PATTERNS[lang || 'javascript'] || _LSP_LANG_PATTERNS.javascript;
|
|
12507
12618
|
lines.forEach((line, idx) => {
|
|
12508
12619
|
for (const p of patterns) {
|
|
12509
12620
|
const m = line.match(p.re);
|
|
12510
|
-
|
|
12621
|
+
// ν€μλ false-positive μ κ±° (μ: java method μ κ·μμ΄ `if(`, `for(` λ±μ λ§€μΉλλ κ²½μ°)
|
|
12622
|
+
if (m && !/^(if|for|while|switch|catch|return|throw|new)$/.test(m[1])) {
|
|
12623
|
+
symbols.push({ name: m[1], kind: p.kind, line: idx + 1 });
|
|
12624
|
+
break;
|
|
12625
|
+
}
|
|
12511
12626
|
}
|
|
12512
12627
|
});
|
|
12513
12628
|
return symbols;
|
|
@@ -12573,31 +12688,32 @@ function lspCmd(root, sub, ...args) {
|
|
|
12573
12688
|
if (!fs.existsSync(file)) return fail(`νμΌ μμ: ${file}`);
|
|
12574
12689
|
const content = fs.readFileSync(file, 'utf8');
|
|
12575
12690
|
const t0 = Date.now();
|
|
12691
|
+
const lang = _detectLspLang(file); // 1.9.173: μΈμ΄ μλ κ°μ§
|
|
12576
12692
|
const r = _tryLoadLSP();
|
|
12577
12693
|
let symbols, mode;
|
|
12578
12694
|
try {
|
|
12579
|
-
if (r.ok && /\.(ts|tsx|js|jsx|mjs|cjs)$/.test(file)) {
|
|
12695
|
+
if (r.ok && lang === 'javascript' && /\.(ts|tsx|js|jsx|mjs|cjs)$/.test(file)) {
|
|
12580
12696
|
symbols = _lspTsSymbols(r.lib, content, file);
|
|
12581
12697
|
mode = 'typescript-compiler';
|
|
12582
12698
|
} else {
|
|
12583
|
-
symbols = _lspRegexSymbols(content);
|
|
12584
|
-
mode =
|
|
12699
|
+
symbols = _lspRegexSymbols(content, lang); // 1.9.173: lang μ λ¬
|
|
12700
|
+
mode = `regex-fallback (${lang})`;
|
|
12585
12701
|
}
|
|
12586
12702
|
} catch (e) {
|
|
12587
|
-
symbols = _lspRegexSymbols(content);
|
|
12588
|
-
mode =
|
|
12703
|
+
symbols = _lspRegexSymbols(content, lang);
|
|
12704
|
+
mode = `regex-fallback (${lang}, after error: ${e.message})`;
|
|
12589
12705
|
}
|
|
12590
12706
|
const dt = Date.now() - t0;
|
|
12591
12707
|
if (has('--json')) {
|
|
12592
|
-
log(JSON.stringify({ file, symbols, count: symbols.length, mode, durationMs: dt }, null, 2));
|
|
12708
|
+
log(JSON.stringify({ file, lang, symbols, count: symbols.length, mode, durationMs: dt }, null, 2));
|
|
12593
12709
|
} else {
|
|
12594
|
-
log(`# leerness lsp symbols (1.9.
|
|
12595
|
-
log(`file: ${file}`);
|
|
12710
|
+
log(`# leerness lsp symbols (1.9.173 λ€κ΅μ΄)`);
|
|
12711
|
+
log(`file: ${file} Β· lang: ${lang}`);
|
|
12596
12712
|
log(`mode: ${mode} Β· ${symbols.length} symbols Β· ${dt}ms`);
|
|
12597
12713
|
symbols.slice(0, 50).forEach(s => log(` ${String(s.line).padStart(5)}:${s.kind.padEnd(10)} ${s.name}`));
|
|
12598
12714
|
if (symbols.length > 50) log(` ... ${symbols.length - 50} more`);
|
|
12599
12715
|
}
|
|
12600
|
-
try { _recordRun(root, { kind: 'lsp_symbols', file, count: symbols.length, mode, durationMs: dt, ok: true }); } catch {}
|
|
12716
|
+
try { _recordRun(root, { kind: 'lsp_symbols', file, lang, count: symbols.length, mode, durationMs: dt, ok: true }); } catch {}
|
|
12601
12717
|
return;
|
|
12602
12718
|
}
|
|
12603
12719
|
if (sub === 'references') {
|
|
@@ -12615,7 +12731,7 @@ function lspCmd(root, sub, ...args) {
|
|
|
12615
12731
|
if (e.name.startsWith('.') || e.name === 'node_modules' || e.name === 'dist' || e.name === 'build') continue;
|
|
12616
12732
|
const p = path.join(d, e.name);
|
|
12617
12733
|
if (e.isDirectory()) walk(p);
|
|
12618
|
-
else if (/\.(ts|tsx|js|jsx|mjs|cjs|md)$/.test(e.name)) {
|
|
12734
|
+
else if (/\.(ts|tsx|js|jsx|mjs|cjs|md|py|pyw|pyi|go|rs|java|kt|scala)$/.test(e.name)) {
|
|
12619
12735
|
try {
|
|
12620
12736
|
const lines = fs.readFileSync(p, 'utf8').split(/\r?\n/);
|
|
12621
12737
|
lines.forEach((ln, idx) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "leerness",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.173",
|
|
4
4
|
"description": "Leerness: λΉνκ΄΄ λ§μ΄κ·Έλ μ΄μ
, μλ λ²μ κ°μ§Β·μ
λ°μ΄νΈ, κ³ν/μ§ν/νΈλμ€ν μλν, κ²μΌλ¦Β·μν¬λ¦ΏΒ·μΈμ½λ© μλ κ°λ, Claude Code μ¬λμ ν΅ν©μ κ°μΆ νκ΅μ΄ μ°μ AI κ°λ° νλ€μ€.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"leerness",
|