leerness 1.9.170 β†’ 1.9.172

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,116 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.9.172 β€” 2026-05-21
4
+
5
+ **🎨 슀트리밍 UX κ°•ν™” β€” spinner + Claude tool_use κ°€μ‹œν™” + diff νŒ¨ν„΄ μžλ™ 색깔 (μ‚¬μš©μž λͺ…μ‹œ κ°•ν™”).**
6
+
7
+ 자율 λͺ¨λ“œ 102 λΌμš΄λ“œ. 1.9.170 μ‚¬μš©μž λͺ…μ‹œ ("터미널 ν™”λ©΄ 계속 κ°±μ‹ , 좔둠쀑/diff/μƒκ°ν•˜λŠ” κ³Όμ • μ‹€μ‹œκ°„ ν‘œμ‹œ") 의 μ§„μ§œ μ˜λ„ 보강.
8
+
9
+ ### 1. 좔둠쀑 Spinner β€” Visual Feedback
10
+ - `_cliChatStream` μ•ˆμ— `setInterval(120ms)` spinner β€” stdout idle 800ms+ μ‹œ κΉœλΉ‘μž„
11
+ - `⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏` 10 frames + κ²½κ³Ό μ‹œκ°„ ν‘œμ‹œ ("β ™ 좔둠쀑... (3s)")
12
+ - `\r\x1b[K` ANSI escape β€” ν˜„μž¬ 라인 클리어 + spinner κ°±μ‹ 
13
+ - `stopSpinner()` β€” stdout/stderr data 도착 μ‹œ μ¦‰μ‹œ spinner 라인 클리어
14
+ - μ˜΅μ…˜: `opts.noSpinner: true` λ˜λŠ” λΉ„-TTY ν™˜κ²½μ—μ„  μžλ™ λΉ„ν™œμ„±
15
+
16
+ ### 2. Claude tool_use κ°€μ‹œν™”
17
+ - Claude `--output-format=stream-json` 의 `content_block_start` μ΄λ²€νŠΈμ—μ„œ `type='tool_use'` 감지
18
+ - ν‘œμ‹œ: `πŸ”§ Tool: Read 호좜 쀑...` (cyan 색)
19
+ - AIκ°€ 파일 읽기/μ“°κΈ°/Bash 호좜 μ‹œ μ‚¬μš©μžκ°€ μ¦‰μ‹œ 인지
20
+
21
+ ### 3. diff νŒ¨ν„΄ μžλ™ 색깔 (writeColored)
22
+ ```diff
23
+ diff --git a/file.js b/file.js # yellow (헀더)
24
+ --- a/file.js # yellow
25
+ +++ b/file.js # yellow
26
+ @@ -10,3 +10,3 @@ # cyan (hunk)
27
+ - old line # red
28
+ + new line # green
29
+ context # κΈ°λ³Έ 색
30
+ ```
31
+ - 라인 λ‹¨μœ„ 처리 (`lineBuf` 버퍼링)
32
+ - μ •κ·œμ‹ λ§€μΉ­: `/^\+(?!\+\+)/` β†’ green, `/^-(?!--)/` β†’ red, `/^@@.*@@/` β†’ cyan
33
+ - `flushLineBuf()` β€” μ’…λ£Œ μ‹œ μž”μ—¬ 라인 좜λ ₯
34
+
35
+ ### 톡합 흐름
36
+ ```
37
+ user> 이 ν•¨μˆ˜ λ¦¬νŒ©ν† λ§ + diff λ³΄μ—¬μ€˜
38
+ β†’ claude CLI stream 호좜 쀑... (Ctrl+C 둜 쀑단)
39
+ ── claude stream ──
40
+ β Ή 좔둠쀑... (2s) # 응닡 μ‹œμž‘ μ „ spinner
41
+ [claude-opus-4-7]
42
+ πŸ”§ Tool: Read 호좜 쀑... # tool_use 감지
43
+
44
+ 뢄석 κ²°κ³Ό:
45
+
46
+ diff --git a/utils.js b/utils.js # yellow
47
+ @@ -5,3 +5,3 @@ # cyan
48
+ - function bad() { return null; } # red
49
+ + function good() { return {}; } # green
50
+ ── /stream (4521ms) ──
51
+ ```
52
+
53
+ ### Verified
54
+ - e2e 217/217 baseline μœ μ§€
55
+ - stress-v117: **20/20** (spinner 4 + tool_use 2 + diff 4 + ANSI helpers 2 + 톡합 2 + λˆ„μ  νšŒκ·€ 6)
56
+ - VERSION = 1.9.172 / autonomous-rounds = 102 / main μžλ™ push 33 λΌμš΄λ“œ 연속
57
+
58
+ ---
59
+
60
+ ## 1.9.171 β€” 2026-05-21
61
+
62
+ **AGENTS.md / CLAUDE.md / session-workflow.md 1.9.88~170 λˆ„μ  κ°±μ‹  (drift 차단).**
63
+
64
+ 자율 λͺ¨λ“œ 101 λΌμš΄λ“œ. 1.9.87 λ§ˆμ§€λ§‰ metadata κ°±μ‹  ν›„ 84 λΌμš΄λ“œ 간격 β€” λ‹€μŒ μ„Έμ…˜μ—μ„œ μƒˆ κΈ°λŠ₯ (REPL/Bridge/Tab cycle/53 MCP/6 λŠ₯λ ₯) 인지 λͺ» ν•  μœ„ν—˜ 차단.
65
+
66
+ ### Updated
67
+ - **session-workflow.md**: 1.9.140~170 31 λΌμš΄λ“œ λˆ„μ  변경사항 μΆ”κ°€
68
+ - release sync-main μžλ™ (31 λΌμš΄λ“œ 연속 main μžλ™ push)
69
+ - Feature Causality Graph (1.9.141~143)
70
+ - env detect (1.9.145)
71
+ - CLI μ—μ΄μ „νŠΈ λͺ¨λ“œ + 3-tier κΆŒν•œ μ‹œμŠ€ν…œ (1.9.146)
72
+ - REPL agent + Sandboxing runCommandSafe (1.9.149~150)
73
+ - REPL UX κ°•ν™” (1.9.151~155)
74
+ - agents multi --execute + consensus (1.9.156)
75
+ - Provider Registry CRUD CLI + MCP **50 도ꡬ λ§ˆμΌμŠ€ν†€** (1.9.157~159)
76
+ - 90 λΌμš΄λ“œ λ§ˆμΌμŠ€ν†€ (1.9.160)
77
+ - REPL slash 4μ’… (1.9.161~162)
78
+ - 5λŠ₯λ ₯ 맀트릭슀 health 톡합 (1.9.163)
79
+ - leerness which 진단 (1.9.164)
80
+ - web/pc/lsp bridge 3μ’… (1.9.165~167)
81
+ - MCP **53 도ꡬ λ§ˆμΌμŠ€ν†€** + Bridge μ™ΈλΆ€ λ…ΈμΆœ (1.9.168)
82
+ - --include explicit-only hotfix (1.9.169)
83
+ - **100 λΌμš΄λ“œ + Tab cycle + μ‹€μ‹œκ°„ 슀트리밍** (1.9.170)
84
+
85
+ - **AGENTS.md**:
86
+ - "REPL Agent + Bridge λͺ…λ Ή (1.9.149~170)" μ„Ήμ…˜ μ‹ μ„€ (μžμ—°μ–΄ λ§€ν•‘ 11μ’…)
87
+ - "6 λŠ₯λ ₯ 맀트릭슀 (1.9.167+)" μ„Ήμ…˜ β€” overallScore + production-ready/beta-ready/mvp 라벨
88
+
89
+ - **CLAUDE.md**:
90
+ - REPL Agent 100 λΌμš΄λ“œ 자율 λ§ˆμΌμŠ€ν†€ μ„Ήμ…˜ (Tab/Shift+Tab/슀트리밍)
91
+ - Bridge 3μ’… opt-in μ•ˆλ‚΄
92
+ - 6 λŠ₯λ ₯ 맀트릭슀 72% production-ready
93
+
94
+ ### μžμ—°μ–΄ λ§€ν•‘ μ‹ κ·œ μΆ”κ°€ (AGENTS.md)
95
+ | μ‚¬μš©μž λ°œν™” | μ¦‰μ‹œ μ‹€ν–‰ |
96
+ |---|---|
97
+ | "μ—μ΄μ „νŠΈ 켜쀘 / REPL λͺ¨λ“œ" | `leerness agent .` |
98
+ | "Claude/Codex λŒ€ν™”" | `leerness agent . --provider claude` |
99
+ | "λ‹€λ₯Έ provider / Tab" | REPLμ—μ„œ `Tab` / `Shift+Tab` |
100
+ | "μ›Ή μŠ€ν¬λ¦°μƒ· / URL 캑처" | `leerness web screenshot <url>` |
101
+ | "마우슀 클릭 / μžλ™ν™”" | `leerness pc click <x> <y>` |
102
+ | "ν•¨μˆ˜ μ°Ύμ•„μ€˜ / 심볼 μΆ”μΆœ" | `leerness lsp symbols <file>` |
103
+ | "μ°Έμ‘° 검색" | `leerness lsp references <name> --in <dir>` |
104
+ | "κΆŒν•œ λͺ¨λ“œ 확인" | `leerness permissions list` |
105
+ | "μ΅œμ‹  버전 μž‘λ™ 확인" | `leerness which` |
106
+
107
+ ### Verified
108
+ - e2e 217/217 baseline μœ μ§€
109
+ - stress-v116: **20/20** (session-workflow 6 + AGENTS 4 + CLAUDE 3 + λˆ„μ  νšŒκ·€ 7)
110
+ - VERSION = 1.9.171 / autonomous-rounds = 101 / main μžλ™ push 32 λΌμš΄λ“œ 연속
111
+
112
+ ---
113
+
3
114
  ## 1.9.170 β€” 2026-05-21 β€” πŸŽ‰ 100 λΌμš΄λ“œ 자율 λ§ˆμΌμŠ€ν†€
4
115
 
5
116
  **μ‚¬μš©μž λͺ…μ‹œ μš”μ²­ 2μ’…: REPL Tab cycle provider/model + μ‹€μ‹œκ°„ 슀트리밍.**
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.170-green)]() [![tests](https://img.shields.io/badge/e2e-217%2F217-success)]() [![stress](https://img.shields.io/badge/stress--v115-23%2F23-success)]() [![mcp](https://img.shields.io/badge/MCP--tools-53-brightgreen)]() [![rounds](https://img.shields.io/badge/autonomous--rounds-100πŸŽ‰-blueviolet)]() [![main-push](https://img.shields.io/badge/release--main--push-31_rounds-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)]() [![lsp-bridge](https://img.shields.io/badge/lsp_bridge-typescript_opt--in%2Bregex_fallback-success)]() [![pc-bridge](https://img.shields.io/badge/pc_bridge-robotjs%2Fnut--tree_opt--in-success)]() [![web-bridge](https://img.shields.io/badge/playwright_bridge-opt--in_MVP-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.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)]()
6
6
 
7
7
  ```
8
8
  ╔══════════════════════════════════════════════════════════════╗
@@ -12,9 +12,9 @@
12
12
  β•‘ β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•”β•β•β• β–ˆβ–ˆβ•”β•β•β• β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β• β•šβ•β•β•β•β–ˆβ–ˆβ•‘ β•‘
13
13
  β•‘ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘ β•šβ–ˆβ–ˆβ–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•‘ β•‘
14
14
  β•‘ β•šβ•β•β•β•β•β•β•β•šβ•β•β•β•β•β•β•β•šβ•β•β•β•β•β•β•β•šβ•β• β•šβ•β•β•šβ•β• β•šβ•β•β•β•β•šβ•β•β•β•β•β•β•β•šβ•β•β•β•β•β•β• β•‘
15
- β•‘ v1.9.170 AI Agent Reliability Harness + Sandbox β•‘
15
+ β•‘ v1.9.172 AI Agent Reliability Harness + Sandbox β•‘
16
16
  β•‘ verify Β· remember Β· orchestrate Β· audit Β· sandbox Β· drift β•‘
17
- β•‘ πŸŽ‰ 100 λΌμš΄λ“œ 자율 λ§ˆμΌμŠ€ν†€ Β· REPL Tab cycle + μ‹€μ‹œκ°„ 슀트림 β•‘
17
+ β•‘ 🎨 슀트리밍 spinner + tool_use κ°€μ‹œν™” + diff μžλ™ 색깔 β•‘
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.170';
9
+ const VERSION = '1.9.172';
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만 ν‘œμ‹œ (thinking/system 은 dim)
10654
+ // assistant λ©”μ‹œμ§€ chunk
10606
10655
  if (ev.type === 'content_block_delta' && ev.delta?.text) {
10607
- process.stdout.write(ev.delta.text);
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
- process.stdout.write(ln + '\n');
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
- process.stdout.write(s);
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`));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leerness",
3
- "version": "1.9.170",
3
+ "version": "1.9.172",
4
4
  "description": "Leerness: λΉ„νŒŒκ΄΄ λ§ˆμ΄κ·Έλ ˆμ΄μ…˜, μžλ™ 버전 κ°μ§€Β·μ—…λ°μ΄νŠΈ, κ³„νš/μ§„ν–‰/ν•Έλ“œμ˜€ν”„ μžλ™ν™”, κ²ŒμœΌλ¦„Β·μ‹œν¬λ¦ΏΒ·μΈμ½”λ”© μžλ™ κ°€λ“œ, Claude Code μŠ¬λž˜μ‹œ 톡합을 κ°–μΆ˜ ν•œκ΅­μ–΄ μš°μ„  AI 개발 ν•˜λ„€μŠ€.",
5
5
  "keywords": [
6
6
  "leerness",