leerness 1.9.204 β 1.9.206
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 +78 -0
- package/README.md +7 -7
- package/bin/harness.js +190 -9
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,83 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.9.206 β 2026-05-22
|
|
4
|
+
|
|
5
|
+
**π i18n MVP + UI/UX ν¬νΌ (μ¬μ©μ λͺ
μ 2μ’
).**
|
|
6
|
+
|
|
7
|
+
### μ¬μ©μ λͺ
μ
|
|
8
|
+
> 1. *"μ€μΉ κ°μ΄λμμ μΈμ΄ μ νμ λ°λΌ μ€μΉ κ°μ΄λ λ° REPL agent λͺ¨λ λ± μ€μ λ μΈμ΄λ‘ νμλμ΄μΌ"*
|
|
9
|
+
> 2. *"μ€μΉ κ°μ΄λ λ° REPL agent λͺ¨λ UI UXλ₯Ό κ°μ ν΄μ€, λͺ¨μ
μ΄λ, ν
μ€νΈ μΆλ ₯λ°©μ λ±λ±"*
|
|
10
|
+
|
|
11
|
+
### 1. i18n μμ€ν
MVP (10 ν΅μ¬ keys)
|
|
12
|
+
- `STRINGS` table (ko/en) β install.lang.* / install.agents.* / repl.welcome.* / common.*
|
|
13
|
+
- `_currentLang(root)` β `LEERNESS_LANG` env β `.harness/LANGUAGE` β 'ko' fallback
|
|
14
|
+
- `_t(key, lang)` β i18n lookup helper
|
|
15
|
+
|
|
16
|
+
### 2. μμ΄ μ ν μ νμ prompt μλ μμ΄ νμ
|
|
17
|
+
- μΈμ΄ μ ν μ체λ νκ΅μ΄ (μ¬μ©μκ° μΈμ΄λ₯Ό λͺ¨λ₯΄λ―λ‘)
|
|
18
|
+
- 'en' μ ν μ `process.env.LEERNESS_LANG = 'en'` μ¦μ μ€μ β agents prompt λ± μλ μμ΄ λ³ν
|
|
19
|
+
|
|
20
|
+
### 3. UI/UX ν¬νΌ (`_typewrite`, `_ui`)
|
|
21
|
+
- `_typewrite(text, delayMs)` β opt-in via `LEERNESS_TYPEWRITER=1` (TTY λ§)
|
|
22
|
+
- `_ui.bold/dim/cyan/green/yellow/hr` β μμ + ꡬλΆμ νμ€ν
|
|
23
|
+
- λΉ-TTY νκ²½ μλ fallback (plain text)
|
|
24
|
+
|
|
25
|
+
### 4. λμ νκ· (1.9.200~205) β λͺ¨λ μ μ§
|
|
26
|
+
|
|
27
|
+
### 5. stress-v151 β 16/16 PASS
|
|
28
|
+
- i18n (3) + UI helpers (1) + install λ€κ΅μ΄ μ μ© (3) + μ±λ₯ (2) + λμ (7)
|
|
29
|
+
- μ±λ₯: --version cold start avg **1049 ms** Β· MCP 54 λꡬ **768 ms**
|
|
30
|
+
|
|
31
|
+
### 6. MVP λ
ΈνΈ (νμ₯ κ°λ₯ μ€κ³)
|
|
32
|
+
- νμ¬: 10 ν΅μ¬ keys (μ€μΉ κ°μ΄λ + REPL welcome + common)
|
|
33
|
+
- μΆν: REPL agent λͺ¨λ μ 체 λ©λ΄ / handoff / resume λ± νμ₯ κ°λ₯ (μ μ§μ )
|
|
34
|
+
|
|
35
|
+
### 7. μλ release
|
|
36
|
+
- main μλ push **68 λΌμ΄λ μ°μ** (1.9.140~206)
|
|
37
|
+
- npm publish μλ (1.9.178~)
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 1.9.205 β 2026-05-22
|
|
42
|
+
|
|
43
|
+
**β‘ ScheduleWakeup μ¬μ©μ μμ² μ°μ κ°±μ (μ¬μ©μ λͺ
μ) β wakeup miss μ€μΈ‘ νμ.**
|
|
44
|
+
|
|
45
|
+
### μ¬μ©μ λͺ
μ + 보κ³
|
|
46
|
+
> *"μλλͺ¨λλ‘ μ§νν λ, μμ λ μλμ΄μ μ μ¬μ©μ μμ²μ΄ λ€μ΄μ€λ©΄ λ°±κ·ΈλΌμ΄λμ μλμ μ’
λ£ν λ€μ κ°±μ νλ λ°©μμ μ΄λ€μ§"*
|
|
47
|
+
> *"ScheduleWakeup 10:51 KST κ° μ§λ¬μ΄"* β μ¬μ©μκ° λͺ
μ λ³΄κ³ ν wakeup miss μ¬λ‘
|
|
48
|
+
|
|
49
|
+
### 1. `.harness/active-wakeups.json` λ±λ‘ μΆμ
|
|
50
|
+
- release sync-main λ§λ¬΄λ¦¬ μ λ€μ wakeup μλ λ±λ‘
|
|
51
|
+
- ꡬ쑰: `{ expectedFireAt, intervalMin, source, status: pending/fired/missed/superseded, registeredAt }`
|
|
52
|
+
- μ΅κ·Ό 20κ° μ μ§
|
|
53
|
+
- ν¨μ: `_loadActiveWakeups` / `_writeActiveWakeups` / `_recordWakeup` / `_activeWakeupsPath`
|
|
54
|
+
|
|
55
|
+
### 2. handoff μ§μ
μ wakeup μν λΆμ (`_analyzeWakeupStatus`)
|
|
56
|
+
| kind | 쑰건 | μλ¦Ό |
|
|
57
|
+
|---|---|---|
|
|
58
|
+
| `early` | delta < -5min (λ―Έλ) | `## β‘ μ¬μ©μ μ‘°κΈ° μ§μ
κ°μ§` |
|
|
59
|
+
| `on-time` | -5 ~ +5min | `β ScheduleWakeup μ μ μ§μ
` |
|
|
60
|
+
| `late` | +5 ~ +30min | (μ‘°μ©ν) |
|
|
61
|
+
| `missed` | > +30min | `## β° ScheduleWakeup miss νμ ` + status μλ κ°±μ |
|
|
62
|
+
|
|
63
|
+
### 3. μ‘°κΈ° μ§μ
μ μλ κ°±μ κ°μ΄λ
|
|
64
|
+
μ¬μ©μκ° wakeup μμ μκ° μ΄μ μ§μ
β "λΌμ΄λ λ§λ¬΄λ¦¬ μ μ wakeup λ±λ‘ (μ΄μ μλ superseded)" μλ΄
|
|
65
|
+
|
|
66
|
+
### 4. miss μλ status κ°±μ
|
|
67
|
+
fire μκ° μ§λ¨ + 30λΆ μ΄μ β status `pending` β `missed` μλ μ
λ°μ΄νΈ β ν΅κ³ λμ
|
|
68
|
+
|
|
69
|
+
### 5. λμ νκ· (1.9.199~204) β λͺ¨λ μ μ§
|
|
70
|
+
|
|
71
|
+
### 6. stress-v150 β 16/16 PASS
|
|
72
|
+
- λ±λ‘ μΆμ (3) + μν λΆμ (2) + μμ μν¬μ€νμ΄μ€ ν΅ν© (2) + μ±λ₯ (2) + λμ (7)
|
|
73
|
+
- μ±λ₯: --version cold start avg **464 ms** Β· MCP 54 λꡬ **460 ms**
|
|
74
|
+
|
|
75
|
+
### 7. μλ release
|
|
76
|
+
- main μλ push **67 λΌμ΄λ μ°μ** (1.9.140~205)
|
|
77
|
+
- npm publish μλ (1.9.178~)
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
3
81
|
## 1.9.204 β 2026-05-22
|
|
4
82
|
|
|
5
83
|
**β° timezone λ³΄κ° + π auto-loop νμ± λΌλ²¨ (μ¬μ©μ λͺ
μ 2μ’
).**
|
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) []() []() []() []() []() []() [-brightgreen)]() []() []() []() []() []()
|
|
6
6
|
|
|
7
7
|
```
|
|
8
8
|
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
@@ -12,9 +12,9 @@
|
|
|
12
12
|
β βββ ββββββ ββββββ ββββββββββββββββββββββββ ββββββββ β
|
|
13
13
|
β βββββββββββββββββββββββββββ ββββββ ββββββββββββββββββββββ β
|
|
14
14
|
β βββββββββββββββββββββββββββ ββββββ βββββββββββββββββββββ β
|
|
15
|
-
β v1.9.
|
|
15
|
+
β v1.9.206 AI Agent Reliability Harness + Sandbox β
|
|
16
16
|
β verify Β· remember Β· orchestrate Β· audit Β· sandbox Β· drift β
|
|
17
|
-
β
|
|
17
|
+
β π i18n ko/en MVP Β· _typewrite/_ui UI/UX ν¬νΌ (μ¬μ©μ λͺ
μ) β
|
|
18
18
|
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
19
19
|
```
|
|
20
20
|
|
|
@@ -530,7 +530,7 @@ MIT β Β© leerness contributors
|
|
|
530
530
|
<!-- leerness:project-readme:start -->
|
|
531
531
|
## Leerness Project Harness
|
|
532
532
|
|
|
533
|
-
μ΄ νλ‘μ νΈλ Leerness v1.9.
|
|
533
|
+
μ΄ νλ‘μ νΈλ Leerness v1.9.206 νλ€μ€λ₯Ό μ¬μ©ν©λλ€. AI μμ΄μ νΈλ μμ
μ `leerness handoff`λ‘ μ»¨ν
μ€νΈλ₯Ό μ μ¬νκ³ , μμ
ν `leerness check`/`leerness audit`/`leerness session close`λ₯Ό μνν΄μΌ ν©λλ€.
|
|
534
534
|
|
|
535
535
|
### Core Commands
|
|
536
536
|
|
|
@@ -572,7 +572,7 @@ leerness memory restore decision <date|title>
|
|
|
572
572
|
|
|
573
573
|
### MCP server (μΈλΆ AI ν΅ν©)
|
|
574
574
|
|
|
575
|
-
Leerness v1.9.
|
|
575
|
+
Leerness v1.9.206λ stdio JSON-RPC MCP serverλ₯Ό λ΄μ₯ν©λλ€ β Claude Code Β· Cursor Β· Codex CLI λ± μΈλΆ AIμ **42κ° λꡬ**λ₯Ό λ
ΈμΆ:
|
|
576
576
|
|
|
577
577
|
```jsonc
|
|
578
578
|
// μΉ΄ν
κ³ λ¦¬λ³
|
|
@@ -593,7 +593,7 @@ Leerness v1.9.144λ stdio JSON-RPC MCP serverλ₯Ό λ΄μ₯ν©λλ€ β Claude Cod
|
|
|
593
593
|
`<<autonomous-loop-dynamic>>` μ νΈλ§ 보λ΄λ©΄ AIκ°:
|
|
594
594
|
1) λ€μ λΌμ΄λ ν보 μ μ β 2) μ½λ λ³κ²½ β 3) stress-v* μ κ· μμ± + λμ νκ· β 4) e2e 219/219 β 5) npm pack + git tag + GitHub release β 6) main μλ push (1.9.140+) β 7) session close β 8) λ€μ λΌμ΄λ μμ½.
|
|
595
595
|
|
|
596
|
-
νμ¬ λμ : **70 λΌμ΄λ (1.9.40 β 1.9.
|
|
596
|
+
νμ¬ λμ : **70 λΌμ΄λ (1.9.40 β 1.9.206)** Β· λ§€ λΌμ΄λ GitHub release/νκ·Έ μμ± Β· _reports/λ λΉκ³΅κ° 보쑴.
|
|
597
597
|
|
|
598
598
|
### μ±λ₯ κ°μ΄λ (1.9.140 μΈ‘μ )
|
|
599
599
|
|
|
@@ -631,6 +631,6 @@ leerness release pack --close --auto-main-push
|
|
|
631
631
|
- `.harness/session-handoff.md`: λ€μ μΈμ
μΈμμΈκ³ (μλ μμ±)
|
|
632
632
|
- `.harness/lessons.md` / `decisions.md` / `rules.md`: μꡬ λ©λͺ¨λ¦¬ (5 surface)
|
|
633
633
|
|
|
634
|
-
Last synced by Leerness v1.9.
|
|
634
|
+
Last synced by Leerness v1.9.206: 2026-05-22
|
|
635
635
|
<!-- leerness:project-readme:end -->
|
|
636
636
|
|
package/bin/harness.js
CHANGED
|
@@ -7,7 +7,7 @@ const cp = require('child_process');
|
|
|
7
7
|
const os = require('os'); // 1.9.178: _publishToNpm μμ os.tmpdir() μ¬μ© (μ μ import)
|
|
8
8
|
const readline = require('readline');
|
|
9
9
|
|
|
10
|
-
const VERSION = '1.9.
|
|
10
|
+
const VERSION = '1.9.206';
|
|
11
11
|
|
|
12
12
|
// 1.9.184: DEP0190 (child_process shell: true) deprecation warning μ΅μ (μ¬μ©μ λͺ
μ).
|
|
13
13
|
// leerness λ cross-platform PATH resolution μ μν΄ shell: true λ₯Ό μλμ μΌλ‘ μ¬μ© (claude.cmd / ollama.cmd λ± Windows .cmd μ²λ¦¬).
|
|
@@ -766,19 +766,24 @@ async function resolveInstallOptions(root, opts = {}) {
|
|
|
766
766
|
// --no-interactive-select λλ LEERNESS_NO_INTERACTIVE=1 β ꡬμ μ«μ μ ν
|
|
767
767
|
const useInteractive = shouldAsk && !has('--no-interactive-select') && process.env.LEERNESS_NO_INTERACTIVE !== '1';
|
|
768
768
|
if (shouldAsk && !explicitLang) {
|
|
769
|
+
// 1.9.206: μΈμ΄ μ ν μ체λ νμ ko (μ¬μ©μκ° μΈμ΄λ₯Ό λͺ¨λ₯΄λ―λ‘) β μ ν ν νμμ μ νλ μΈμ΄
|
|
769
770
|
if (useInteractive) {
|
|
770
|
-
const langOpt = await _selectOne('
|
|
771
|
-
{ label: '
|
|
772
|
-
{ label: '
|
|
773
|
-
{ label: '
|
|
771
|
+
const langOpt = await _selectOne(_t('install.lang.title', 'ko'), [
|
|
772
|
+
{ label: _t('install.lang.auto', 'ko'), description: _t('install.lang.auto.desc', 'ko'), id: 'auto' },
|
|
773
|
+
{ label: _t('install.lang.ko', 'ko'), description: _t('install.lang.ko.desc', 'ko'), id: 'ko' },
|
|
774
|
+
{ label: _t('install.lang.en', 'ko'), description: _t('install.lang.en.desc', 'ko'), id: 'en' }
|
|
774
775
|
], { defaultIndex: 0 });
|
|
775
776
|
lang = langOpt && langOpt.id ? detectLanguageValue(root, langOpt.id) : detectLanguageValue(root, 'auto');
|
|
776
777
|
} else {
|
|
777
|
-
log('\n
|
|
778
|
-
log('1)
|
|
779
|
-
|
|
778
|
+
log('\n' + _t('install.lang.title', 'ko') + '.');
|
|
779
|
+
log('1) ' + _t('install.lang.auto', 'ko'));
|
|
780
|
+
log('2) ' + _t('install.lang.ko', 'ko'));
|
|
781
|
+
log('3) ' + _t('install.lang.en', 'ko'));
|
|
782
|
+
const a = await ask('Select / μ ν [1]: ');
|
|
780
783
|
lang = a === '2' ? 'ko' : a === '3' ? 'en' : detectLanguageValue(root, 'auto');
|
|
781
784
|
}
|
|
785
|
+
// 1.9.206: μμ΄ μ ν μ νμ prompt μμ΄ νμ νκ²½λ³μ μ¦μ μ€μ
|
|
786
|
+
if (lang === 'en') process.env.LEERNESS_LANG = 'en';
|
|
782
787
|
}
|
|
783
788
|
// 1.9.148: μ€ν¬ prompt μ κ±° (μ¬μ©μ λͺ
μ μμ²) β leernessκ° μλμΌλ‘ 곡μ νμ€ μ€ν¬ 5μ’
μ€μΉ.
|
|
784
789
|
// νμν λ μ¬μ©μκ° leerness skill install <id> λ‘ μΆκ° κ°λ₯.
|
|
@@ -787,8 +792,10 @@ async function resolveInstallOptions(root, opts = {}) {
|
|
|
787
792
|
// _selectMany λ‘ Space ν κΈ, a μ 체, n ν΄μ , Enter νμ . μ νλ μμ΄μ νΈλ€λ§ .env.exampleμ LEERNESS_ENABLE_* νμ±ν.
|
|
788
793
|
let agentsOptIn = null; // string[] (λ€μ€) λλ 'none' (μ ν μν¨)
|
|
789
794
|
if (shouldAsk && !opts._skipAgentsPrompt) {
|
|
795
|
+
// 1.9.206: μ νλ μΈμ΄λ‘ prompt νμ
|
|
796
|
+
const _agLang = lang === 'en' ? 'en' : 'ko';
|
|
790
797
|
if (useInteractive) {
|
|
791
|
-
const picked = await _selectMany('
|
|
798
|
+
const picked = await _selectMany(_t('install.agents.title', _agLang), [
|
|
792
799
|
{ label: 'Claude (ANTHROPIC_API_KEY λλ claude CLI)', description: 'μΆλ‘ λ ₯ μ΅κ³ β μ½λ μμ±/리뷰 κΈ°λ³Έ', id: 'claude' },
|
|
793
800
|
{ label: 'Codex (OpenAI codex CLI)', description: 'OpenAI μ½λ λͺ¨λΈ', id: 'codex' },
|
|
794
801
|
{ label: 'Gemini (gemini CLI)', description: 'Google λ©ν°λͺ¨λ¬ λͺ¨λΈ', id: 'gemini' },
|
|
@@ -1779,6 +1786,138 @@ async function skillAutoCacheCmd(root, sub) {
|
|
|
1779
1786
|
if (c.expired) log(` β κ°±μ : leerness skill auto-cache refresh`);
|
|
1780
1787
|
}
|
|
1781
1788
|
|
|
1789
|
+
// 1.9.206: i18n μμ€ν
(μ¬μ©μ λͺ
μ) β μ€μΉ κ°μ΄λ / REPL agent λ€κ΅μ΄ + UI/UX κ°μ
|
|
1790
|
+
// μ¬μ©μ λͺ
μ: "μ€μΉ κ°μ΄λμμ μΈμ΄ μ νμ λ°λΌ μ€μΉ κ°μ΄λ λ° REPL agent λͺ¨λ λ± μ€μ λ μΈμ΄λ‘ νμ"
|
|
1791
|
+
// μ€κ³: ν΅μ¬ string table (ko/en) + _t(key, lang) helper. μ€μΉ μ μ ν μΈμ΄ β .harness/LANGUAGE μ μ₯.
|
|
1792
|
+
// lang κ²°μ : explicit > .harness/LANGUAGE > LEERNESS_LANG env > 'ko' (default)
|
|
1793
|
+
const STRINGS = {
|
|
1794
|
+
// μ€μΉ κ°μ΄λ prompt
|
|
1795
|
+
'install.lang.title': { ko: 'μ€μΉ μΈμ΄λ₯Ό μ ννμΈμ', en: 'Select install language' },
|
|
1796
|
+
'install.lang.auto': { ko: 'μλ κ°μ§', en: 'Auto detect' },
|
|
1797
|
+
'install.lang.auto.desc': { ko: 'λλ ν 리/νμΌ λΆμ (νκ΅μ΄/μμ΄ μλ νλ³)', en: 'Analyze dir/files (auto KO/EN)' },
|
|
1798
|
+
'install.lang.ko': { ko: 'νκ΅μ΄', en: 'Korean' },
|
|
1799
|
+
'install.lang.ko.desc': { ko: 'λͺ¨λ μΈμ€νΈλμ
μ νκ΅μ΄λ‘ μμ±', en: 'All instructions in Korean' },
|
|
1800
|
+
'install.lang.en': { ko: 'English', en: 'English' },
|
|
1801
|
+
'install.lang.en.desc': { ko: 'λͺ¨λ μΈμ€νΈλμ
μ μμ΄λ‘ μμ±', en: 'All instructions in English' },
|
|
1802
|
+
'install.agents.title': { ko: 'CLI μμ΄μ νΈ νμ±ν (볡μ μ ν, Space ν κΈ) β sub-agent μμμ©',
|
|
1803
|
+
en: 'Enable CLI agents (multi-select, Space toggle) β for sub-agent dispatch' },
|
|
1804
|
+
'install.agents.none': { ko: 'μ ν μν¨ (λμ€μ setup-agents)', en: 'None (later setup-agents)' },
|
|
1805
|
+
'install.complete': { ko: 'β μ€μΉ μλ£', en: 'β Install complete' },
|
|
1806
|
+
// REPL agent λͺ¨λ
|
|
1807
|
+
'repl.welcome.title': { ko: 'leerness REPL agent', en: 'leerness REPL agent' },
|
|
1808
|
+
'repl.welcome.subtitle': { ko: 'Tab provider Β· Shift+Tab model Β· :help Β· /slash', en: 'Tab provider Β· Shift+Tab model Β· :help Β· /slash' },
|
|
1809
|
+
'repl.welcome.start': { ko: 'μμνλ €λ©΄ λ©μμ§λ₯Ό μ
λ ₯νμΈμ', en: 'Type a message to start' },
|
|
1810
|
+
// 곡ν΅
|
|
1811
|
+
'common.cancel': { ko: 'μ·¨μλ¨', en: 'Cancelled' },
|
|
1812
|
+
'common.confirm': { ko: 'νμΈ', en: 'Confirm' },
|
|
1813
|
+
'common.ready': { ko: 'μ€λΉ μλ£', en: 'Ready' }
|
|
1814
|
+
};
|
|
1815
|
+
// νμ¬ μ¬μ© μΈμ΄ κ²°μ (env > config > 'ko')
|
|
1816
|
+
function _currentLang(root) {
|
|
1817
|
+
if (process.env.LEERNESS_LANG) return process.env.LEERNESS_LANG === 'en' ? 'en' : 'ko';
|
|
1818
|
+
try {
|
|
1819
|
+
if (root) {
|
|
1820
|
+
const fp = path.join(root, '.harness', 'LANGUAGE');
|
|
1821
|
+
if (exists(fp)) {
|
|
1822
|
+
const v = read(fp).trim().toLowerCase();
|
|
1823
|
+
if (v === 'en' || v === 'english') return 'en';
|
|
1824
|
+
if (v === 'ko' || v === 'korean' || v === 'kr') return 'ko';
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
} catch {}
|
|
1828
|
+
return 'ko'; // default
|
|
1829
|
+
}
|
|
1830
|
+
function _t(key, lang) {
|
|
1831
|
+
const entry = STRINGS[key];
|
|
1832
|
+
if (!entry) return key; // fallback: ν€ μ체 λ°ν
|
|
1833
|
+
return entry[lang || 'ko'] || entry.ko || key;
|
|
1834
|
+
}
|
|
1835
|
+
|
|
1836
|
+
// 1.9.206: UI/UX κ°μ β typewriter / fade-in ν¨κ³Ό (opt-in via LEERNESS_TYPEWRITER=1)
|
|
1837
|
+
function _typewrite(text, delayMs) {
|
|
1838
|
+
delayMs = delayMs || 15;
|
|
1839
|
+
if (process.env.LEERNESS_TYPEWRITER !== '1' || !process.stdout.isTTY) {
|
|
1840
|
+
process.stdout.write(text);
|
|
1841
|
+
return Promise.resolve();
|
|
1842
|
+
}
|
|
1843
|
+
return new Promise((resolve) => {
|
|
1844
|
+
let i = 0;
|
|
1845
|
+
const step = () => {
|
|
1846
|
+
if (i >= text.length) return resolve();
|
|
1847
|
+
process.stdout.write(text[i++]);
|
|
1848
|
+
setTimeout(step, delayMs);
|
|
1849
|
+
};
|
|
1850
|
+
step();
|
|
1851
|
+
});
|
|
1852
|
+
}
|
|
1853
|
+
// μμ helper (TTY μ ANSI, λΉ-TTY μ plain)
|
|
1854
|
+
const _ui = {
|
|
1855
|
+
bold: s => process.stdout.isTTY ? `\x1b[1m${s}\x1b[0m` : s,
|
|
1856
|
+
dim: s => process.stdout.isTTY ? `\x1b[2m${s}\x1b[0m` : s,
|
|
1857
|
+
cyan: s => process.stdout.isTTY ? `\x1b[36m${s}\x1b[0m` : s,
|
|
1858
|
+
green: s => process.stdout.isTTY ? `\x1b[32m${s}\x1b[0m` : s,
|
|
1859
|
+
yellow: s => process.stdout.isTTY ? `\x1b[33m${s}\x1b[0m` : s,
|
|
1860
|
+
// ꡬλΆμ μ λ ¬ (ν°λ―Έλ width κ³ λ €)
|
|
1861
|
+
hr: (char) => {
|
|
1862
|
+
const w = process.stdout.columns || 60;
|
|
1863
|
+
return (char || 'β').repeat(Math.min(w - 2, 60));
|
|
1864
|
+
}
|
|
1865
|
+
};
|
|
1866
|
+
|
|
1867
|
+
// 1.9.205: ScheduleWakeup λ±λ‘ μΆμ (μ¬μ©μ λͺ
μ)
|
|
1868
|
+
// "μμ λ μλμ΄μ μ μ¬μ©μ μμ²μ΄ λ€μ΄μ€λ©΄ λ°±κ·ΈλΌμ΄λμ μλμ μ’
λ£ν λ€μ κ°±μ "
|
|
1869
|
+
// λΌμ΄λ λ§λ¬΄λ¦¬ μ λ±λ‘λ wakeup id/expectedFireAt μ .harness/active-wakeups.json μ κΈ°λ‘ β
|
|
1870
|
+
// handoff/resume μ§μ
μ expected fire κ° κ³Όκ±°μ΄λ©΄ "wakeup miss λλ μ¬μ©μ μ‘°κΈ° μ§μ
" κ°μ§ β
|
|
1871
|
+
// λ©μΈ μμ΄μ νΈκ° μ wakeup νΈμΆνλΌλ λͺ
νν μλ΄
|
|
1872
|
+
function _activeWakeupsPath(root) { return path.join(root, '.harness', 'active-wakeups.json'); }
|
|
1873
|
+
function _loadActiveWakeups(root) {
|
|
1874
|
+
try {
|
|
1875
|
+
const fp = _activeWakeupsPath(root);
|
|
1876
|
+
if (!exists(fp)) return { wakeups: [] };
|
|
1877
|
+
const j = JSON.parse(read(fp));
|
|
1878
|
+
return { wakeups: Array.isArray(j.wakeups) ? j.wakeups : [] };
|
|
1879
|
+
} catch { return { wakeups: [] }; }
|
|
1880
|
+
}
|
|
1881
|
+
function _writeActiveWakeups(root, wakeups) {
|
|
1882
|
+
try {
|
|
1883
|
+
mkdirp(path.join(root, '.harness'));
|
|
1884
|
+
writeUtf8(_activeWakeupsPath(root), JSON.stringify({ wakeups, updatedAt: new Date().toISOString() }, null, 2));
|
|
1885
|
+
return true;
|
|
1886
|
+
} catch { return false; }
|
|
1887
|
+
}
|
|
1888
|
+
function _recordWakeup(root, entry) {
|
|
1889
|
+
const state = _loadActiveWakeups(root);
|
|
1890
|
+
// entry: { expectedFireAt, intervalMin, source, status: 'pending'|'fired'|'missed'|'superseded' }
|
|
1891
|
+
const e = { ...entry, registeredAt: new Date().toISOString() };
|
|
1892
|
+
// κ°μ expectedFireAt μ€λ³΅ μ replace
|
|
1893
|
+
state.wakeups = state.wakeups.filter(w => w.expectedFireAt !== e.expectedFireAt);
|
|
1894
|
+
state.wakeups.push(e);
|
|
1895
|
+
// μ΅κ·Ό 20κ°λ§ μ μ§
|
|
1896
|
+
if (state.wakeups.length > 20) state.wakeups = state.wakeups.slice(-20);
|
|
1897
|
+
_writeActiveWakeups(root, state.wakeups);
|
|
1898
|
+
return e;
|
|
1899
|
+
}
|
|
1900
|
+
// wakeup miss / μ¬μ©μ μ‘°κΈ° μ§μ
κ°μ§ β νμ± wakeup μ€ κ°μ₯ κ°κΉμ΄ λ―Έλ/κ³Όκ±° fire λΆμ
|
|
1901
|
+
function _analyzeWakeupStatus(root) {
|
|
1902
|
+
const state = _loadActiveWakeups(root);
|
|
1903
|
+
const pending = state.wakeups.filter(w => w.status === 'pending');
|
|
1904
|
+
if (pending.length === 0) return { hasWakeup: false };
|
|
1905
|
+
// κ°μ₯ μ΅κ·Ό λ±λ‘λ pending wakeup
|
|
1906
|
+
const latest = pending.sort((a, b) => new Date(b.registeredAt).getTime() - new Date(a.registeredAt).getTime())[0];
|
|
1907
|
+
const now = Date.now();
|
|
1908
|
+
const expectedMs = new Date(latest.expectedFireAt).getTime();
|
|
1909
|
+
const deltaMin = Math.floor((now - expectedMs) / 60000);
|
|
1910
|
+
// deltaMin < 0 β μμ§ fire μκ° μ (μ¬μ©μ μ‘°κΈ° μ§μ
)
|
|
1911
|
+
// deltaMin >= 0 β fire μκ° μ§λ¨ (μ μ λλ miss)
|
|
1912
|
+
// |deltaMin| < 5 β μ μ λΆκ·Ό
|
|
1913
|
+
let kind;
|
|
1914
|
+
if (deltaMin < -5) kind = 'early'; // μ¬μ©μ μ‘°κΈ° μ§μ
|
|
1915
|
+
else if (deltaMin > 30) kind = 'missed'; // wakeup miss
|
|
1916
|
+
else if (deltaMin > 5) kind = 'late'; // μ½κ° μ§μ°
|
|
1917
|
+
else kind = 'on-time';
|
|
1918
|
+
return { hasWakeup: true, latest, deltaMin, kind, pending: pending.length };
|
|
1919
|
+
}
|
|
1920
|
+
|
|
1782
1921
|
// 1.9.203: μλ λΌμ΄λ plan μ 리 β μ¬μ©μ λͺ
μ
|
|
1783
1922
|
// "λ°±κ·ΈλΌμ΄λμ λ€μ μμ
μ΄ μμκ°λ₯ν μμ μκ° + μΌμ΄λ¬μλ ν΄μΌνλ μΌμ μ 리"
|
|
1784
1923
|
// λΌμ΄λ λ§λ¬΄λ¦¬ μ .harness/auto-resume-plan.json μλ μ μ₯ β λ€μ wakeup μ μ¦μ μ€ν κ°λ₯
|
|
@@ -4224,6 +4363,40 @@ function handoff(root) {
|
|
|
4224
4363
|
}
|
|
4225
4364
|
} catch {}
|
|
4226
4365
|
}
|
|
4366
|
+
// 1.9.205: ScheduleWakeup μν λΆμ + μ¬μ©μ μ‘°κΈ° μ§μ
/ miss κ°μ§ (μ¬μ©μ λͺ
μ)
|
|
4367
|
+
if (process.env.LEERNESS_NO_WAKEUP_STATUS !== '1' && !has('--quiet')) {
|
|
4368
|
+
try {
|
|
4369
|
+
const wakeup = _analyzeWakeupStatus(root);
|
|
4370
|
+
if (wakeup.hasWakeup) {
|
|
4371
|
+
const isTty = process.stdout && process.stdout.isTTY;
|
|
4372
|
+
const yel = s => isTty ? `\x1b[33m${s}\x1b[0m` : s;
|
|
4373
|
+
const grn = s => isTty ? `\x1b[32m${s}\x1b[0m` : s;
|
|
4374
|
+
const dim = s => isTty ? `\x1b[2m${s}\x1b[0m` : s;
|
|
4375
|
+
const expectedLocal = _formatLocal(wakeup.latest.expectedFireAt);
|
|
4376
|
+
if (wakeup.kind === 'early') {
|
|
4377
|
+
// μ¬μ©μ μ‘°κΈ° μ§μ
β κΈ°μ‘΄ wakeup κ°±μ κΆμ₯
|
|
4378
|
+
log(yel(`## β‘ μ¬μ©μ μ‘°κΈ° μ§μ
κ°μ§ (1.9.205) β λ±λ‘λ wakeup ${Math.abs(wakeup.deltaMin)}λΆ ν fire μμ `));
|
|
4379
|
+
log(dim(` μμ fire: ${expectedLocal} (${wakeup.latest.source})`));
|
|
4380
|
+
log(dim(` β κΆμ₯: λΌμ΄λ λ§λ¬΄λ¦¬ μ μ wakeup λ±λ‘ (μ΄μ μλ superseded)`));
|
|
4381
|
+
log('');
|
|
4382
|
+
} else if (wakeup.kind === 'missed') {
|
|
4383
|
+
// wakeup miss β fire μκ° μ§λ¨
|
|
4384
|
+
log(yel(`## β° ScheduleWakeup miss νμ (1.9.205) β μμ fire ${wakeup.deltaMin}λΆ μ `));
|
|
4385
|
+
log(dim(` μμ fire: ${expectedLocal} (${wakeup.latest.source})`));
|
|
4386
|
+
log(dim(` β ν볡: λΌμ΄λ λ§λ¬΄λ¦¬ μ μ wakeup μλ λ±λ‘`));
|
|
4387
|
+
// μλμΌλ‘ status λ₯Ό 'missed' λ‘ μ
λ°μ΄νΈ
|
|
4388
|
+
try {
|
|
4389
|
+
const s = _loadActiveWakeups(root);
|
|
4390
|
+
const idx = s.wakeups.findIndex(w => w.expectedFireAt === wakeup.latest.expectedFireAt);
|
|
4391
|
+
if (idx >= 0) { s.wakeups[idx].status = 'missed'; _writeActiveWakeups(root, s.wakeups); }
|
|
4392
|
+
} catch {}
|
|
4393
|
+
log('');
|
|
4394
|
+
} else if (wakeup.kind === 'on-time') {
|
|
4395
|
+
log(grn(` β ScheduleWakeup μ μ μ§μ
(1.9.205, delta ${wakeup.deltaMin}λΆ)`));
|
|
4396
|
+
}
|
|
4397
|
+
}
|
|
4398
|
+
} catch {}
|
|
4399
|
+
}
|
|
4227
4400
|
// 1.9.203: auto-resume-plan μλ¦Ό β λΌμ΄λ λ§λ¬΄λ¦¬ μ μ μ₯λ plan μλ λ
ΈμΆ
|
|
4228
4401
|
// μ¬μ©μ λͺ
μ: "λ°±κ·ΈλΌμ΄λμ λ€μ μμ
μ΄ μμκ°λ₯ν μμ μκ° + μΌμ΄λ¬μλ ν΄μΌνλ μΌμ μ 리"
|
|
4229
4402
|
if (_autoResumePlan && !has('--quiet') && process.env.LEERNESS_NO_RESUME_PLAN !== '1') {
|
|
@@ -9334,6 +9507,14 @@ function _publishToNpm(root, opts = {}) {
|
|
|
9334
9507
|
note: `λ€μ wakeup μ: 1) leerness resume . μΌλ‘ plan νμΈ 2) leerness handoff . μ€ν 3) leerness next-action take λ‘ μ¦μ task μ§μ
`
|
|
9335
9508
|
});
|
|
9336
9509
|
_writeAutoResumePlan(root, plan);
|
|
9510
|
+
// 1.9.205: λ€μ wakeup λ±λ‘ μΆμ (μ¬μ©μ λͺ
μ κ°±μ μ©)
|
|
9511
|
+
_recordWakeup(root, {
|
|
9512
|
+
expectedFireAt: plan.expectedFireAt,
|
|
9513
|
+
intervalMin: plan.intervalMin || 25,
|
|
9514
|
+
source: 'release-sync-main',
|
|
9515
|
+
status: 'pending',
|
|
9516
|
+
fromVersion: pkgVersion
|
|
9517
|
+
});
|
|
9337
9518
|
} catch {}
|
|
9338
9519
|
} else {
|
|
9339
9520
|
const errOut = (pubR.stderr || pubR.stdout || '').slice(-400);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "leerness",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.206",
|
|
4
4
|
"description": "Leerness: λΉνκ΄΄ λ§μ΄κ·Έλ μ΄μ
, μλ λ²μ κ°μ§Β·μ
λ°μ΄νΈ, κ³ν/μ§ν/νΈλμ€ν μλν, κ²μΌλ¦Β·μν¬λ¦ΏΒ·μΈμ½λ© μλ κ°λ, Claude Code μ¬λμ ν΅ν©μ κ°μΆ νκ΅μ΄ μ°μ AI κ°λ° νλ€μ€.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"leerness",
|