leerness 1.9.176 β†’ 1.9.178

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,102 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.9.178 β€” 2026-05-21
4
+
5
+ **πŸ“¦ μ‚¬μš©μž λͺ…μ‹œ: `release sync-main` μžλ™ npm publish β€” .env NPM_TOKEN μ‚¬μš©.**
6
+
7
+ 자율 λͺ¨λ“œ 108 λΌμš΄λ“œ. μ‚¬μš©μž λͺ…μ‹œ: *"NPM μ•‘μ„ΈμŠ€ 토큰 .env에 μž…λ ₯ν•΄λ’€μŒ. μ—…λ°μ΄νŠΈλ  λ•Œλ§ˆλ‹€ κΉƒν—ˆλΈŒμ²˜λŸΌ NPM에도 μžλ™ 배포"*.
8
+
9
+ ### 톡합 흐름
10
+ ```bash
11
+ $ leerness release sync-main .
12
+ # leerness release sync-main (1.9.140)
13
+ from: release/1.9.178 β†’ main
14
+ βœ“ main merged: release/1.9.178
15
+ βœ“ main pushed β†’ origin/main
16
+
17
+ πŸ“¦ npm publish μžλ™ trigger (1.9.178)
18
+ leerness@1.9.178
19
+ npm publish μ‹œλ„ 쀑...
20
+ βœ“ npm publish μ™„λ£Œ: leerness@1.9.178
21
+ ```
22
+
23
+ β†’ git push ν›„ NPM μžλ™ publish β€” git μ›Œν¬ν”Œλ‘œμ™€ NPM μ›Œν¬ν”Œλ‘œ μ™„μ „ 톡합.
24
+
25
+ ### λ³΄μ•ˆ 섀계 (3쀑 μ•ˆμ „λ§)
26
+ 1. **토큰 ν™˜κ²½λ³€μˆ˜λ§Œ μ‚¬μš©**: `process.env.LEERNESS_NPM_TOKEN || process.env.NPM_TOKEN` (κ°’ μ ˆλŒ€ 둜그 X)
27
+ 2. **μž„μ‹œ `.npmrc` 격리**: `mkdtempSync` β†’ `.npmrc` (mode 0o600 μ†Œμœ μžλ§Œ 읽기) β†’ publish β†’ **finally μ¦‰μ‹œ μ‚­μ œ**
28
+ 3. **`.env` μžλ™ 보호**: 1.9.75+ audit이 `.gitignore`에 `.env` 등둝 κ°•μ œ (μ‹œν¬λ¦Ώ λˆ„λ½ 감지)
29
+
30
+ ### 쀑볡 publish 차단
31
+ - `npm view <pkg>@<version> version` 사전 호좜
32
+ - 이미 publish된 버전이면 skip + μ•Œλ¦Ό (race condition도 후속 처리)
33
+
34
+ ### μΉœμ ˆν•œ μ—λŸ¬ μ•ˆλ‚΄
35
+ - `EPUBLISHCONFLICT` β†’ "이미 publish됨 β€” skip"
36
+ - `EAUTH / 401 / 403` β†’ "토큰 κΆŒν•œ λΆ€μ‘± λ˜λŠ” 만료 β€” .env NPM_TOKEN μž¬λ°œκΈ‰ ν•„μš”"
37
+ - `ENEEDAUTH` β†’ "인증 λ―Έμž‘λ™ β€” 토큰 ν˜•μ‹ 확인 (npm_xxxxx)"
38
+ - 기타 β†’ λ§ˆμ§€λ§‰ 3쀄 stderr λ…ΈμΆœ
39
+
40
+ ### Opt-out (3κ°€μ§€)
41
+ 1. `--no-npm` ν”Œλž˜κ·Έ
42
+ 2. `LEERNESS_NO_NPM_PUBLISH=1` ν™˜κ²½λ³€μˆ˜
43
+ 3. `--dry-run-npm` ν”Œλž˜κ·Έ (dry-run mode)
44
+
45
+ ### Observability
46
+ `_recordRun(kind: 'npm_publish')` β€” λͺ¨λ“  publish μ‹œλ„ (성곡/μ‹€νŒ¨) μžλ™ 기둝.
47
+
48
+ ### Verified
49
+ - e2e 217/217 baseline μœ μ§€ (LEERNESS_NO_NPM_PUBLISH=1 ν™˜κ²½)
50
+ - stress-v123: **22/22** (ν•¨μˆ˜ μ •μ˜ 6 + 쀑볡 차단 4 + release 톡합 3 + μΉœμ ˆν•œ μ•ˆλ‚΄ 3 + λˆ„μ  νšŒκ·€ 6)
51
+ - VERSION = 1.9.178 / autonomous-rounds = 108 / main μžλ™ push 39 λΌμš΄λ“œ 연속 + **NPM μžλ™ publish 1 λΌμš΄λ“œ μ‹œμž‘**
52
+
53
+ ---
54
+
55
+ ## 1.9.177 β€” 2026-05-21
56
+
57
+ **πŸ” `task add` μžλ™ review-request trigger β€” μ‚¬μš©μž λͺ…μ‹œ 1.9.176 μžλ™ν™”.**
58
+
59
+ 자율 λͺ¨λ“œ 107 λΌμš΄λ“œ. 1.9.176 (μ‚¬μš©μž λͺ…μ‹œ: 무쑰건 κ΅¬ν˜„ μ „ 사전 κ²€ν† ) 의 μžλ™ν™” β€” μ‚¬μš©μž/AIκ°€ `task add` 호좜 μ‹œ reviewλ₯Ό 직접 μ‹€ν–‰ν•˜μ§€ μ•Šμ•„λ„ μžλ™ trigger.
60
+
61
+ ### 톡합 흐름
62
+ ```bash
63
+ $ leerness task add "OAuth 둜그인 κ΅¬ν˜„ν•΄μ€˜"
64
+ βœ“ task added: T-0001
65
+
66
+ πŸ” review-request (1.9.177 μžλ™): type=feature Β· βœ“ μ§„ν–‰ μ•ˆμ „ (705ms)
67
+ ꢌμž₯ 단계:
68
+ 1) leerness reuse find "<핡심 capability>" β€” 쀑볡 κ΅¬ν˜„ 사전 차단
69
+ 2) leerness plan add "<milestone>" β€” μ§„ν–‰ 좔적
70
+ ... +2건 (leerness review-request "OAuth 둜그인 κ΅¬ν˜„ν•΄μ€˜" 으둜 전체 보기)
71
+ πŸ’‘ πŸ‘₯ leerness agents recommend feature β€” μž‘μ—… μœ ν˜•λ³„ sub-agent λ§€ν•‘ ν™œμš© κ°€λŠ₯
72
+ ```
73
+
74
+ β†’ μ‚¬μš©μž/AIκ°€ λͺ…μ‹œμ μœΌλ‘œ `:review` 호좜 μ•ˆ 해도, `task add` 만으둜 μžλ™ 사전 κ²€ν†  μ™„λ£Œ. **1.9.176 μ‚¬μš©μž λͺ…μ‹œ μ˜λ„κ°€ default λ™μž‘μ— 톡합**.
75
+
76
+ ### Opt-out (3 κ°€μ§€)
77
+ 1. CLI ν”Œλž˜κ·Έ: `leerness task add "..." --no-review`
78
+ 2. ν™˜κ²½λ³€μˆ˜: `LEERNESS_NO_AUTO_REVIEW=1`
79
+ 3. 운영 메타: `--status done|dropped|blocked` (이미 μ’…λ£Œλœ μž‘μ—…μ€ review λΆˆν•„μš”)
80
+
81
+ ### ν‘œμ‹œ μ •μ±… (κ°„κ²°)
82
+ - 헀더 1쀄: `type=X Β· ⚠ N 좩돌 Β· πŸ” N μž¬μ‚¬μš© 후보 Β· βœ“ μ§„ν–‰ μ•ˆμ „ / ⚠ 확인 ν•„μš” (ms)`
83
+ - ꢌμž₯ 단계 첫 2건 (λ‚˜λ¨Έμ§€λŠ” `leerness review-request` 직접 호좜 μ•ˆλ‚΄)
84
+ - 효율 μ œμ•ˆ 1건 (κ°€μž₯ μ€‘μš”ν•œ hint)
85
+ - `proceed=false` μ‹œ ⚠ μ‚¬μœ  λ…ΈμΆœ
86
+
87
+ ### MCP ν˜Έν™˜μ„±
88
+ `leerness_task_add` (MCP) 호좜 μ‹œμ—λ„ μžλ™ review λ™μž‘ β€” μ™ΈλΆ€ AI (Claude/Codex/Gemini)κ°€ task 등둝 μ‹œ μžλ™μœΌλ‘œ 사전 κ²€ν†  κ²°κ³Ό λ°›μŒ.
89
+
90
+ ### μ„±λŠ₯
91
+ μ‹€μΈ‘: ~1.1초 task add (이전 ~30ms + review 1초 μΆ”κ°€). brainstorm/reuse-map 회수 λΉ„μš© β€” opt-out κ°€λŠ₯.
92
+
93
+ ### Verified
94
+ - e2e 217/217 baseline μœ μ§€
95
+ - stress-v122: **18/18** (taskAdd 톡합 4 + μ‹€ λ™μž‘ 7 + MCP ν˜Έν™˜ 1 + λˆ„μ  νšŒκ·€ 6)
96
+ - VERSION = 1.9.177 / autonomous-rounds = 107 / main μžλ™ push 38 λΌμš΄λ“œ 연속
97
+
98
+ ---
99
+
3
100
  ## 1.9.176 β€” 2026-05-21
4
101
 
5
102
  **⚠ μ‚¬μš©μž λͺ…μ‹œ: `leerness review-request` β€” μ‚¬μš©μž μš”κ΅¬λ₯Ό 무쑰건 κ΅¬ν˜„ μ „ 사전 κ²€ν† .**
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.176-green)]() [![tests](https://img.shields.io/badge/e2e-217%2F217-success)]() [![stress](https://img.shields.io/badge/stress--v121-23%2F23-success)]() [![mcp](https://img.shields.io/badge/MCP--tools-54-brightgreen)]() [![rounds](https://img.shields.io/badge/autonomous--rounds-106-blueviolet)]() [![main-push](https://img.shields.io/badge/release--main--push-37_rounds-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)]() [![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.178-green)]() [![tests](https://img.shields.io/badge/e2e-217%2F217-success)]() [![stress](https://img.shields.io/badge/stress--v123-22%2F22-success)]() [![mcp](https://img.shields.io/badge/MCP--tools-54-brightgreen)]() [![rounds](https://img.shields.io/badge/autonomous--rounds-108-blueviolet)]() [![main-push](https://img.shields.io/badge/release--main--push-39_rounds-success)]() [![npm-auto](https://img.shields.io/badge/npm_auto--publish-NPM__TOKEN_톡합-success)]() [![auto-review](https://img.shields.io/badge/task_add-μžλ™_review_trigger-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.176 AI Agent Reliability Harness + Sandbox β•‘
15
+ β•‘ v1.9.178 AI Agent Reliability Harness + Sandbox β•‘
16
16
  β•‘ verify Β· remember Β· orchestrate Β· audit Β· sandbox Β· drift β•‘
17
- β•‘ ⚠ review-request β€” 무쑰건 κ΅¬ν˜„ μ „ 좩돌/효율 사전 κ²€ν†  β•‘
17
+ β•‘ πŸ“¦ release sync-main μžλ™ npm publish (NPM_TOKEN .env 톡합) β•‘
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.176';
9
+ const VERSION = '1.9.178';
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)');
@@ -7770,6 +7806,94 @@ function releaseSyncMainCmd(root) {
7770
7806
  else ok(`main pushed β†’ ${remoteName}/main`);
7771
7807
  // 5) return to source branch (release μž‘μ—… 흐름 보쑴)
7772
7808
  cp.spawnSync('git', ['checkout', fromBranch], { cwd: root, encoding: 'utf8' });
7809
+
7810
+ // 1.9.178: μžλ™ npm publish (μ‚¬μš©μž λͺ…μ‹œ β€” .env NPM_TOKEN 있으면 μžλ™ 배포).
7811
+ // opt-out: --no-npm λ˜λŠ” LEERNESS_NO_NPM_PUBLISH=1
7812
+ // 토큰 λ―Έμ„€μ • μ‹œ μΉœμ ˆν•œ μ•ˆλ‚΄ ν›„ skip (μ‹€νŒ¨ X).
7813
+ if (!has('--no-npm') && process.env.LEERNESS_NO_NPM_PUBLISH !== '1') {
7814
+ try { _publishToNpm(root, { dryRun: has('--dry-run-npm') }); } catch (e) { warn('npm publish μ‹œλ„ μ‹€νŒ¨ (계속): ' + e.message); }
7815
+ }
7816
+ }
7817
+
7818
+ // 1.9.178: NPM μžλ™ 배포 (μ‚¬μš©μž λͺ…μ‹œ β€” release sync-main ν›„ μžλ™ trigger).
7819
+ // λ³΄μ•ˆ:
7820
+ // - NPM_TOKEN λ˜λŠ” LEERNESS_NPM_TOKEN ν™˜κ²½λ³€μˆ˜μ—μ„œλ§Œ 읽음 (κ°’ μ ˆλŒ€ 둜그 X)
7821
+ // - μž„μ‹œ .npmrc 파일 (mkdtempSync) 생성 β†’ publish β†’ μ¦‰μ‹œ μ‚­μ œ (finally)
7822
+ // - 이미 publish된 버전 μžλ™ detect β†’ skip (쀑볡 publish 차단)
7823
+ function _publishToNpm(root, opts = {}) {
7824
+ root = absRoot(root || process.cwd());
7825
+ // .env μžλ™ λ‘œλ“œ (μ‚¬μš©μž λͺ…μ‹œ νŒ¨ν„΄ β€” 1.9.153)
7826
+ try { _loadEnvFile(root); } catch {}
7827
+ const token = process.env.LEERNESS_NPM_TOKEN || process.env.NPM_TOKEN;
7828
+ if (!token) {
7829
+ log(` ⚠ npm μžλ™ 배포 μŠ€ν‚΅: NPM_TOKEN λ―Έμ„€μ • (.env 에 NPM_TOKEN=npm_xxxxx μΆ”κ°€)`);
7830
+ return;
7831
+ }
7832
+ // package.json 확인
7833
+ const pkgPath = path.join(root, 'package.json');
7834
+ if (!exists(pkgPath)) {
7835
+ log(` ⚠ npm μžλ™ 배포 μŠ€ν‚΅: package.json μ—†μŒ (${rel(root, pkgPath)})`);
7836
+ return;
7837
+ }
7838
+ let pkg;
7839
+ try { pkg = JSON.parse(read(pkgPath)); } catch (e) { warn(`package.json νŒŒμ‹± μ‹€νŒ¨: ${e.message}`); return; }
7840
+ if (!pkg.name || !pkg.version) {
7841
+ log(` ⚠ npm μžλ™ 배포 μŠ€ν‚΅: name/version λˆ„λ½`);
7842
+ return;
7843
+ }
7844
+ const pkgName = pkg.name;
7845
+ const pkgVersion = pkg.version;
7846
+ log('');
7847
+ log(`πŸ“¦ npm publish μžλ™ trigger (1.9.178)`);
7848
+ log(` ${pkgName}@${pkgVersion}`);
7849
+
7850
+ // 1) 이미 publish된 버전인지 확인 (npm view <pkg>@<version> version)
7851
+ try {
7852
+ const viewR = cp.spawnSync('npm', ['view', `${pkgName}@${pkgVersion}`, 'version'], {
7853
+ cwd: root, encoding: 'utf8', shell: true, timeout: 15000
7854
+ });
7855
+ if (viewR.status === 0 && (viewR.stdout || '').trim() === pkgVersion) {
7856
+ log(` βœ“ 이미 npm registry에 publish됨 β€” skip`);
7857
+ return;
7858
+ }
7859
+ } catch {} // λ„€νŠΈμ›Œν¬ μ‹€νŒ¨ μ‹œ κ·Έλƒ₯ publish μ‹œλ„
7860
+
7861
+ // 2) μž„μ‹œ .npmrc 생성 (토큰 λ…ΈμΆœ λ°©μ§€)
7862
+ let tmpDir;
7863
+ try {
7864
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-npmrc-'));
7865
+ const tmpNpmrc = path.join(tmpDir, '.npmrc');
7866
+ fs.writeFileSync(tmpNpmrc, `//registry.npmjs.org/:_authToken=${token}\n`, { mode: 0o600 });
7867
+ // 3) npm publish (--userconfig 둜 μž„μ‹œ .npmrc μ‚¬μš©, --access public)
7868
+ const args = opts.dryRun ? ['publish', '--userconfig', tmpNpmrc, '--access', 'public', '--dry-run'] :
7869
+ ['publish', '--userconfig', tmpNpmrc, '--access', 'public'];
7870
+ log(` ${opts.dryRun ? '(dry-run) ' : ''}npm publish μ‹œλ„ 쀑...`);
7871
+ const pubR = cp.spawnSync('npm', args, {
7872
+ cwd: root, encoding: 'utf8', shell: true, timeout: 60000,
7873
+ env: { ...process.env, npm_config_loglevel: 'warn' }
7874
+ });
7875
+ if (pubR.status === 0) {
7876
+ ok(`npm publish μ™„λ£Œ: ${pkgName}@${pkgVersion}`);
7877
+ try { _recordRun(root, { kind: 'npm_publish', package: pkgName, version: pkgVersion, dryRun: !!opts.dryRun, ok: true }); } catch {}
7878
+ } else {
7879
+ const errOut = (pubR.stderr || pubR.stdout || '').slice(-400);
7880
+ if (/EPUBLISHCONFLICT|already exists|cannot publish over/i.test(errOut)) {
7881
+ log(` βœ“ 이미 publish됨 (race condition) β€” skip`);
7882
+ } else if (/EAUTH|forbidden|401|403/i.test(errOut)) {
7883
+ warn(`npm publish μ‹€νŒ¨: 토큰 κΆŒν•œ λΆ€μ‘± λ˜λŠ” 만료 β€” .env NPM_TOKEN μž¬λ°œκΈ‰ ν•„μš”`);
7884
+ } else if (/ENEEDAUTH/i.test(errOut)) {
7885
+ warn(`npm publish μ‹€νŒ¨: 인증 λ―Έμž‘λ™ β€” 토큰 ν˜•μ‹ 확인 (npm_xxxxx)`);
7886
+ } else {
7887
+ warn(`npm publish μ‹€νŒ¨ (exit ${pubR.status}): ${errOut.split('\n').slice(0, 3).join(' ')}`);
7888
+ }
7889
+ try { _recordRun(root, { kind: 'npm_publish', package: pkgName, version: pkgVersion, ok: false, error: errOut.slice(0, 200) }); } catch {}
7890
+ }
7891
+ } finally {
7892
+ // 4) μž„μ‹œ .npmrc μ¦‰μ‹œ μ‚­μ œ (토큰 μž”μ‘΄ λ°©μ§€)
7893
+ if (tmpDir) {
7894
+ try { fs.rmSync(tmpDir, { recursive: true, force: true }); } catch {}
7895
+ }
7896
+ }
7773
7897
  }
7774
7898
 
7775
7899
  // 1.9.40: release pack β€” κ°€λ²Όμš΄ 톡합 λͺ…λ Ή (npm pack + self-host migrate + auto task + close + readme sync)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leerness",
3
- "version": "1.9.176",
3
+ "version": "1.9.178",
4
4
  "description": "Leerness: λΉ„νŒŒκ΄΄ λ§ˆμ΄κ·Έλ ˆμ΄μ…˜, μžλ™ 버전 κ°μ§€Β·μ—…λ°μ΄νŠΈ, κ³„νš/μ§„ν–‰/ν•Έλ“œμ˜€ν”„ μžλ™ν™”, κ²ŒμœΌλ¦„Β·μ‹œν¬λ¦ΏΒ·μΈμ½”λ”© μžλ™ κ°€λ“œ, Claude Code μŠ¬λž˜μ‹œ 톡합을 κ°–μΆ˜ ν•œκ΅­μ–΄ μš°μ„  AI 개발 ν•˜λ„€μŠ€.",
5
5
  "keywords": [
6
6
  "leerness",