leerness 1.9.239 β 1.9.241
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 +77 -0
- package/README.md +3 -3
- package/bin/harness.js +229 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,82 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.9.241 β 2026-05-24
|
|
4
|
+
|
|
5
|
+
**π leerness env + νκ΅μ΄ PowerShell μΈμ½λ© μν μ¬μ κ°μ§ (μ¬μ©μ λͺ
μ UR-0014).**
|
|
6
|
+
|
|
7
|
+
### μ¬μ©μ λͺ
μ (UR-0014)
|
|
8
|
+
> *"leerness μ€μΉ νλ‘μ νΈμμ PowerShell/ν°λ―Έλ νκ΅μ΄ μΈμ½λ© μ€λ₯ μ¬μ κ°μ§ β OS/μΈμ΄μ€μ /νλμ¨μ΄/ν°λ―Έλ/SW λ²μ λ°μ΄ν° μμ§λ‘ κ°λ°/ν
μ€νΈ/λλ²κΉ
μ νκ²½ νΈνμ± λ³΄μ₯"*
|
|
9
|
+
|
|
10
|
+
λ°°κ²½: μ¬μ©μκ° `3d-map-maker` νλ‘μ νΈμμ `npm run setup` β `setup.ps1` νμ± μ€λ₯ (νκ΅μ΄ + BOM μλ UTF-8 β PowerShell CP949 μ€μΈμ)
|
|
11
|
+
|
|
12
|
+
### 1. `leerness env summary` μ λͺ
λ Ή (1.9.145 envCheck μ λ³κ°)
|
|
13
|
+
- νκ²½ μ’
ν© μΆλ ₯: OS / Node / Locale / νκ΅μ΄ Windows κ°μ§ / Hardware / Terminal (PowerShell λ²μ ) / Tools (git/npm/python)
|
|
14
|
+
- `chcp` μΆλ ₯μΌλ‘ μ½λνμ΄μ§ μλ κ°μ§ (949 = νκ΅μ΄)
|
|
15
|
+
- `--json` μ΅μ
|
|
16
|
+
- μ κ· helper: `_collectRuntimeEnv()` (κΈ°μ‘΄ `_detectEnvironment` μ λͺ
λͺ
λΆλ¦¬)
|
|
17
|
+
|
|
18
|
+
### 2. `leerness env encoding` μ λͺ
λ Ή β μ
Έ μ€ν¬λ¦½νΈ μΈμ½λ© μν κ°μ§
|
|
19
|
+
- `.ps1` / `.bat` / `.cmd` / `.sh` μλ μ€μΊ
|
|
20
|
+
- BOM μλ λΉ-ASCII λ°μ΄νΈ κ²μΆ (μ¬κ·, 4096 byte μ΄λ΄)
|
|
21
|
+
- μν μ: `Windows νκ΅μ΄ PowerShell μμ CP949 λ‘ μ€μΈμ κ°λ₯ (BOM μΆκ° κΆμ₯)`
|
|
22
|
+
- ν΄κ²°μ±
μλ μ μ (UTF-8 BOM μΆκ° λλ `$OutputEncoding` μ€μ )
|
|
23
|
+
|
|
24
|
+
### 3. handoff body μλ λ
ΈμΆ
|
|
25
|
+
- `## β μ
Έ μ€ν¬λ¦½νΈ μΈμ½λ© μν N건 (1.9.241, UR-0014)` λ³Έλ¬Έ μΉμ
|
|
26
|
+
- top 3 μν νμΌ + ν΄κ²° μλ΄ (BOM μΆκ°)
|
|
27
|
+
- `leerness env encoding` κ°μ΄λ μλ
|
|
28
|
+
|
|
29
|
+
### 4. MCP 69λ²μ§Έ λꡬ β `leerness_env_info`
|
|
30
|
+
- μΈλΆ AI κ° νκ²½ νΈνμ± μ¬μ μΈμ§ β μΈμ½λ© μ€λ₯ μλ°©
|
|
31
|
+
- `encodingCheck: true` μ΅μ
μ μ
Έ μ€ν¬λ¦½νΈ μ€μΊ
|
|
32
|
+
- MCP 68 β **69** (+1)
|
|
33
|
+
|
|
34
|
+
### 5. λμ νκ· (1.9.207~240) β λͺ¨λ μ μ§
|
|
35
|
+
- κΈ°μ‘΄ `_detectEnvironment(root)` (1.9.145) μ μΆ©λ μμ΄ κ³΅μ‘΄
|
|
36
|
+
|
|
37
|
+
### 6. stress-v186 β 16/16 PASS
|
|
38
|
+
- 1.9.241 (7): VERSION + env summary + JSON + encoding μν κ°μ§ + μμ μ μν 0 + handoff body + MCP 69
|
|
39
|
+
- μ±λ₯ (1): cold_start avg 598ms
|
|
40
|
+
- λμ νκ· (8): 1.9.207~240
|
|
41
|
+
|
|
42
|
+
### 7. μλ release (103 λΌμ΄λ main-push streak Β· 64 λΌμ΄λ npm publish streak)
|
|
43
|
+
|
|
44
|
+
π **νκ΅μ΄ Windows νκ²½ νΈνμ± λ³΄κ°** β μ¬μ©μ λ³΄κ³ .ps1 νμ± μ€λ₯ κ°μ μΌμ΄μ€ μ¬μ κ°μ§
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## 1.9.240 β 2026-05-24
|
|
49
|
+
|
|
50
|
+
**π UR-0013 2λ¨κ³: handoff/session close/health --json pyFiles (9 νλ) + ν€λλΌμΈ μλ.**
|
|
51
|
+
|
|
52
|
+
### 1. handoff/session close/health --json 9λ²μ§Έ ν΅ν© νλ `pyFiles`
|
|
53
|
+
- Python νμΌ μλ detect (1.9.239 `_collectPyFiles` μ¬μ¬μ©)
|
|
54
|
+
- νμ: `{ total, analyzed, totalLOC, totalImports, totalFuncs, totalClasses }`
|
|
55
|
+
- 3 λͺ
λ Ή μΌκ΄μ± λ§€νΈλ¦μ€ μ§ν: 7 β 8 β **9 νλ** (3 Γ 9 = 27 ν΅ν© ν¬μΈνΈ)
|
|
56
|
+
- AI κ° λ€μ€ μΈμ΄ μν¬μ€νμ΄μ€ (.md + .py) μΈμ§
|
|
57
|
+
|
|
58
|
+
### 2. handoff ν€λλΌμΈ 18λ²μ§Έ μμ
|
|
59
|
+
- `.py` νμΌ β₯ 1 μ: `π py N` μλ λ
ΈμΆ
|
|
60
|
+
- 0κ°μΌ λ λ―Έλ
ΈμΆ (μ‘°μ©ν¨)
|
|
61
|
+
|
|
62
|
+
### 3. UR-0013 2λ¨κ³ μλ£
|
|
63
|
+
- 1.9.239 1λ¨κ³: py-check CLI + agent-mode CLI + MCP 67/68
|
|
64
|
+
- **1.9.240 2λ¨κ³**: JSON 9 νλ + ν€λλΌμΈ μλ
|
|
65
|
+
- λ€μ€ μΈμ΄ νλ©΄ (.md + .py) μλ κ°μν
|
|
66
|
+
|
|
67
|
+
### 4. λμ νκ· (1.9.207~239) β λͺ¨λ μ μ§
|
|
68
|
+
|
|
69
|
+
### 5. stress-v185 β 16/16 PASS
|
|
70
|
+
- 1.9.240 (7): VERSION + handoff/session close/health JSON + 9 νλ + ν€λλΌμΈ (μμ/μμ)
|
|
71
|
+
- μ±λ₯ (1): cold_start avg 386ms
|
|
72
|
+
- λμ νκ· (8): 1.9.207~239
|
|
73
|
+
|
|
74
|
+
### 6. μλ release (102 λΌμ΄λ main-push streak Β· 63 λΌμ΄λ npm publish streak)
|
|
75
|
+
|
|
76
|
+
π **λ€μ€ μΈμ΄ νλ©΄ κ°μν** β .md + .py μλ ν΅ν© (μΈλΆ AI κ° λ νλ©΄ λͺ¨λ νμ)
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
3
80
|
## 1.9.239 β 2026-05-24
|
|
4
81
|
|
|
5
82
|
**π leerness py-check + π€ agent-mode + MCP 67/68 (μ¬μ©μ λͺ
μ UR-0013).**
|
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.241 AI Agent Reliability Harness + Sandbox β
|
|
16
16
|
β verify Β· remember Β· orchestrate Β· audit Β· sandbox Β· drift β
|
|
17
|
-
β
|
|
17
|
+
β π env encoding μν μ¬μ κ°μ§ (μ¬μ©μ λͺ
μ UR-0014) β
|
|
18
18
|
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
19
19
|
```
|
|
20
20
|
|
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.241';
|
|
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 μ²λ¦¬).
|
|
@@ -2376,6 +2376,157 @@ function _computeRecentChanges(root, limit = 5) {
|
|
|
2376
2376
|
return items;
|
|
2377
2377
|
}
|
|
2378
2378
|
|
|
2379
|
+
// 1.9.241: νκ²½ μ 보 μ’
ν© μμ§ + μ
Έ μ€ν¬λ¦½νΈ μΈμ½λ© μν κ°μ§ (μ¬μ©μ λͺ
μ UR-0014)
|
|
2380
|
+
// μ¬μ©μ λ³΄κ³ : Windows + νκ΅μ΄ PowerShell μμ .ps1 UTF-8 (no BOM) νμΌμ΄ CP949 λ‘ μλͺ» μΈμλμ΄ νμ± μ€λ₯
|
|
2381
|
+
// ν΄κ²°: leerness κ° νκ²½ μ 보 + μ
Έ μ€ν¬λ¦½νΈ μΈμ½λ©μ μ¬μ κ°μ§ β handoff body κ²½κ³
|
|
2382
|
+
// μ£Όμ: κΈ°μ‘΄ _detectEnvironment (1.9.145) μ λͺ
λͺ
μΆ©λ β _collectRuntimeEnv λ‘ λΆλ¦¬
|
|
2383
|
+
function _collectRuntimeEnv() {
|
|
2384
|
+
const env = {
|
|
2385
|
+
os: { platform: os.platform(), release: os.release(), arch: os.arch() },
|
|
2386
|
+
node: { version: process.version, exe: process.execPath },
|
|
2387
|
+
locale: {
|
|
2388
|
+
LANG: process.env.LANG || null,
|
|
2389
|
+
LC_ALL: process.env.LC_ALL || null,
|
|
2390
|
+
LC_CTYPE: process.env.LC_CTYPE || null,
|
|
2391
|
+
// Windows νκ΅μ΄ νκ²½ detect
|
|
2392
|
+
OEMCP: process.env.OEMCP || null, // CP949 = 949
|
|
2393
|
+
isKoreanWindows: false
|
|
2394
|
+
},
|
|
2395
|
+
hardware: {
|
|
2396
|
+
cpus: os.cpus().length,
|
|
2397
|
+
memGB: Math.round(os.totalmem() / 1024 / 1024 / 1024),
|
|
2398
|
+
hostname: os.hostname()
|
|
2399
|
+
},
|
|
2400
|
+
terminal: {
|
|
2401
|
+
isTTY: !!(process.stdout && process.stdout.isTTY),
|
|
2402
|
+
term: process.env.TERM || null,
|
|
2403
|
+
shell: process.env.SHELL || process.env.ComSpec || null,
|
|
2404
|
+
powershell: null
|
|
2405
|
+
},
|
|
2406
|
+
tools: { git: null, npm: null, python: null, python3: null }
|
|
2407
|
+
};
|
|
2408
|
+
// νκ΅μ΄ Windows κ°μ§
|
|
2409
|
+
try {
|
|
2410
|
+
if (env.os.platform === 'win32') {
|
|
2411
|
+
// chcp μΆλ ₯μΌλ‘ μ½λνμ΄μ§ νμΈ
|
|
2412
|
+
const r = cp.spawnSync('chcp', [], { encoding: 'utf8', shell: true, timeout: 3000 });
|
|
2413
|
+
const m = (r.stdout || '').match(/(\d{3,4})/);
|
|
2414
|
+
if (m) {
|
|
2415
|
+
const cp_num = parseInt(m[1], 10);
|
|
2416
|
+
env.locale.codepage = cp_num;
|
|
2417
|
+
if (cp_num === 949 || cp_num === 51949) env.locale.isKoreanWindows = true;
|
|
2418
|
+
}
|
|
2419
|
+
}
|
|
2420
|
+
} catch {}
|
|
2421
|
+
// PowerShell κ°μ§
|
|
2422
|
+
try {
|
|
2423
|
+
if (env.os.platform === 'win32') {
|
|
2424
|
+
const r = cp.spawnSync('powershell', ['-NoProfile', '-Command', '$PSVersionTable.PSVersion.Major'], { encoding: 'utf8', timeout: 5000 });
|
|
2425
|
+
if (r.status === 0) env.terminal.powershell = (r.stdout || '').trim().split('\n')[0];
|
|
2426
|
+
}
|
|
2427
|
+
} catch {}
|
|
2428
|
+
// λꡬ λ²μ κ°μ§
|
|
2429
|
+
for (const [tool, cmd] of [['git', 'git --version'], ['npm', 'npm --version'], ['python', 'python --version'], ['python3', 'python3 --version']]) {
|
|
2430
|
+
try {
|
|
2431
|
+
const r = cp.spawnSync(cmd.split(' ')[0], cmd.split(' ').slice(1), { encoding: 'utf8', shell: true, timeout: 3000 });
|
|
2432
|
+
if (r.status === 0) {
|
|
2433
|
+
const v = (r.stdout || r.stderr || '').trim().split('\n')[0];
|
|
2434
|
+
env.tools[tool] = v;
|
|
2435
|
+
}
|
|
2436
|
+
} catch {}
|
|
2437
|
+
}
|
|
2438
|
+
return env;
|
|
2439
|
+
}
|
|
2440
|
+
|
|
2441
|
+
// μ
Έ μ€ν¬λ¦½νΈ (.ps1/.bat/.cmd/.sh) μΈμ½λ© μν κ°μ§
|
|
2442
|
+
// μν μκ·Έλ: νκ΅μ΄ (λλ λΉ-ASCII) λ¬Έμ + BOM μμ + Windows νκ²½
|
|
2443
|
+
function _scanShellScriptsEncoding(root) {
|
|
2444
|
+
const result = { scanned: 0, atRisk: [], notes: [] };
|
|
2445
|
+
function walk(dir, depth = 0) {
|
|
2446
|
+
if (depth > 3) return; // λ무 κΉμ΄ λ€μ΄κ°μ§ μμ
|
|
2447
|
+
let entries;
|
|
2448
|
+
try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { return; }
|
|
2449
|
+
for (const e of entries) {
|
|
2450
|
+
const fp = path.join(dir, e.name);
|
|
2451
|
+
if (e.isDirectory()) {
|
|
2452
|
+
if (/^(node_modules|\.git|__pycache__|venv|\.venv|dist|build)$/.test(e.name)) continue;
|
|
2453
|
+
walk(fp, depth + 1);
|
|
2454
|
+
} else if (e.isFile() && /\.(ps1|bat|cmd|sh)$/i.test(e.name)) {
|
|
2455
|
+
result.scanned++;
|
|
2456
|
+
try {
|
|
2457
|
+
const buf = fs.readFileSync(fp);
|
|
2458
|
+
const hasBOM = buf.length >= 3 && buf[0] === 0xEF && buf[1] === 0xBB && buf[2] === 0xBF;
|
|
2459
|
+
// λΉ-ASCII (β₯ 0x80) λ°μ΄νΈ μ‘΄μ¬ μ¬λΆ κ²μ¬
|
|
2460
|
+
let nonAscii = false;
|
|
2461
|
+
for (let i = 0; i < Math.min(buf.length, 4096); i++) {
|
|
2462
|
+
if (buf[i] >= 0x80) { nonAscii = true; break; }
|
|
2463
|
+
}
|
|
2464
|
+
if (nonAscii && !hasBOM) {
|
|
2465
|
+
result.atRisk.push({
|
|
2466
|
+
file: path.relative(root, fp),
|
|
2467
|
+
ext: path.extname(fp),
|
|
2468
|
+
hasBOM: false,
|
|
2469
|
+
nonAscii: true,
|
|
2470
|
+
risk: 'Windows νκ΅μ΄ PowerShell μμ CP949 λ‘ μ€μΈμ κ°λ₯ (BOM μΆκ° κΆμ₯)'
|
|
2471
|
+
});
|
|
2472
|
+
}
|
|
2473
|
+
} catch {}
|
|
2474
|
+
}
|
|
2475
|
+
}
|
|
2476
|
+
}
|
|
2477
|
+
walk(root);
|
|
2478
|
+
if (result.atRisk.length > 0) {
|
|
2479
|
+
result.notes.push('ν΄κ²°: νμΌ μμμ UTF-8 BOM μΆκ° (EF BB BF) λλ .ps1 첫 μ€μ $OutputEncoding/[Console]::OutputEncoding μ€μ ');
|
|
2480
|
+
}
|
|
2481
|
+
return result;
|
|
2482
|
+
}
|
|
2483
|
+
|
|
2484
|
+
function envCmd(root, sub) {
|
|
2485
|
+
root = absRoot(root);
|
|
2486
|
+
const isTty = process.stdout && process.stdout.isTTY;
|
|
2487
|
+
const cy = s => isTty ? `\x1b[36m${s}\x1b[0m` : s;
|
|
2488
|
+
const gr = s => isTty ? `\x1b[32m${s}\x1b[0m` : s;
|
|
2489
|
+
const yl = s => isTty ? `\x1b[33m${s}\x1b[0m` : s;
|
|
2490
|
+
const rd = s => isTty ? `\x1b[31m${s}\x1b[0m` : s;
|
|
2491
|
+
const dm = s => isTty ? `\x1b[2m${s}\x1b[0m` : s;
|
|
2492
|
+
if (sub === 'encoding-check') {
|
|
2493
|
+
const result = _scanShellScriptsEncoding(root);
|
|
2494
|
+
if (has('--json')) { log(JSON.stringify(result, null, 2)); return; }
|
|
2495
|
+
log(cy(`# leerness env encoding-check (1.9.241, UR-0014) β μ
Έ μ€ν¬λ¦½νΈ μΈμ½λ© μν κ°μ§`));
|
|
2496
|
+
log('');
|
|
2497
|
+
log(` μ€μΊ: ${gr(String(result.scanned))} νμΌ (.ps1/.bat/.cmd/.sh)`);
|
|
2498
|
+
if (result.atRisk.length === 0) {
|
|
2499
|
+
log(gr(` β μΈμ½λ© μν μμ`));
|
|
2500
|
+
} else {
|
|
2501
|
+
log(rd(` β μν ${result.atRisk.length}건 (BOM μλ λΉ-ASCII):`));
|
|
2502
|
+
result.atRisk.slice(0, 10).forEach(r => {
|
|
2503
|
+
log(` β’ ${r.file}`);
|
|
2504
|
+
log(dm(` ${r.risk}`));
|
|
2505
|
+
});
|
|
2506
|
+
log('');
|
|
2507
|
+
result.notes.forEach(n => log(yl(` π‘ ${n}`)));
|
|
2508
|
+
}
|
|
2509
|
+
return;
|
|
2510
|
+
}
|
|
2511
|
+
// default: env summary
|
|
2512
|
+
const env = _collectRuntimeEnv();
|
|
2513
|
+
if (has('--json')) { log(JSON.stringify(env, null, 2)); return; }
|
|
2514
|
+
log(cy(`# leerness env (1.9.241, UR-0014) β νκ²½ μ’
ν©`));
|
|
2515
|
+
log('');
|
|
2516
|
+
log(` π» OS: ${gr(env.os.platform)} ${env.os.release} (${env.os.arch})`);
|
|
2517
|
+
log(` π¦ Node: ${gr(env.node.version)}`);
|
|
2518
|
+
log(` π Locale: LANG=${env.locale.LANG || 'unset'} Β· CP=${env.locale.codepage || 'unknown'}${env.locale.isKoreanWindows ? yl(' (νκ΅μ΄ Windows)') : ''}`);
|
|
2519
|
+
log(` π₯ Hardware: ${env.hardware.cpus} CPUs Β· ${env.hardware.memGB} GB RAM`);
|
|
2520
|
+
log(` π± Terminal: TTY=${env.terminal.isTTY}${env.terminal.powershell ? ` Β· PowerShell v${env.terminal.powershell}` : ''}`);
|
|
2521
|
+
log(` π§ Tools:`);
|
|
2522
|
+
for (const [k, v] of Object.entries(env.tools)) {
|
|
2523
|
+
log(` ${v ? gr('β') : dm('β')} ${k}: ${v || dm('not found')}`);
|
|
2524
|
+
}
|
|
2525
|
+
log('');
|
|
2526
|
+
log(dm(` β μΈμ½λ© κ²μ¬: leerness env encoding-check (μ
Έ μ€ν¬λ¦½νΈ .ps1/.bat μν κ°μ§)`));
|
|
2527
|
+
log(dm(` β JSON μΆλ ₯: leerness env --json`));
|
|
2528
|
+
}
|
|
2529
|
+
|
|
2379
2530
|
// 1.9.231: leerness pulse β ν μ€ μ’
ν© μμ½ (10 ν΅μ¬ μ§ν)
|
|
2380
2531
|
// handoff ν€λλΌμΈμ μΆμ½ λ²μ β μ¬μ©μκ° λΉ λ₯΄κ² μν νμ
|
|
2381
2532
|
// μλ΅ (--json): { version, roundCount, mcpTools, memorySurface, security, health, driftScore, nextMilestone, etaDays, abnormal }
|
|
@@ -6390,6 +6541,19 @@ function handoff(root) {
|
|
|
6390
6541
|
try {
|
|
6391
6542
|
result.recentChanges = _computeRecentChanges(root, 5);
|
|
6392
6543
|
} catch {}
|
|
6544
|
+
// 1.9.240: pyFiles ν΅ν© (handoff JSON 9λ²μ§Έ ν΅ν© νλ) β Python λ€μ€ μΈμ΄ κ°μν (UR-0013 2λ¨κ³)
|
|
6545
|
+
try {
|
|
6546
|
+
const pyFiles = _collectPyFiles(root, 200);
|
|
6547
|
+
const analyses = pyFiles.slice(0, 200).map(f => _analyzePyFile(f)).filter(Boolean);
|
|
6548
|
+
result.pyFiles = {
|
|
6549
|
+
total: pyFiles.length,
|
|
6550
|
+
analyzed: analyses.length,
|
|
6551
|
+
totalLOC: analyses.reduce((s, a) => s + a.loc, 0),
|
|
6552
|
+
totalImports: analyses.reduce((s, a) => s + a.imports, 0),
|
|
6553
|
+
totalFuncs: analyses.reduce((s, a) => s + a.funcs, 0),
|
|
6554
|
+
totalClasses: analyses.reduce((s, a) => s + a.classes, 0)
|
|
6555
|
+
};
|
|
6556
|
+
} catch {}
|
|
6393
6557
|
} catch {}
|
|
6394
6558
|
try {
|
|
6395
6559
|
const pwState = _loadPreWakeReport(root);
|
|
@@ -6552,6 +6716,13 @@ function handoff(root) {
|
|
|
6552
6716
|
parts.push(`π official ${m.total}/${m.cacheTotal} (${ageStr}${status})`);
|
|
6553
6717
|
}
|
|
6554
6718
|
} catch {}
|
|
6719
|
+
// 18) 1.9.240: pyFiles ν€λλΌμΈ β Python λ€μ€ μΈμ΄ κ°μν (UR-0013 2λ¨κ³, .py νμΌ β₯ 1)
|
|
6720
|
+
try {
|
|
6721
|
+
const pyFiles = _collectPyFiles(root, 200);
|
|
6722
|
+
if (pyFiles.length > 0) {
|
|
6723
|
+
parts.push(`π py ${pyFiles.length}`);
|
|
6724
|
+
}
|
|
6725
|
+
} catch {}
|
|
6555
6726
|
// 17) 1.9.226: round-history ν€λλΌμΈ β μμ¨ λΌμ΄λ μΉ΄μ΄ν° + λ€μ λ§μΌμ€ν€ (5 λΌμ΄λ μ΄μ μλ§ νμ)
|
|
6556
6727
|
// 1.9.230: λ€μ λ§μΌμ€ν€ ETA (10R μ΄λ΄ + ETA 7μΌ μ΄λ΄μΌ λ κ°μ‘°)
|
|
6557
6728
|
try {
|
|
@@ -6723,6 +6894,27 @@ function handoff(root) {
|
|
|
6723
6894
|
}
|
|
6724
6895
|
} catch {}
|
|
6725
6896
|
|
|
6897
|
+
// 1.9.241: handoff body μ
Έ μ€ν¬λ¦½νΈ μΈμ½λ© μν κ°μ§ (μ¬μ©μ λͺ
μ UR-0014)
|
|
6898
|
+
// νκ΅μ΄ Windows + .ps1/.bat λΉ-ASCII + BOM μμ β CP949 μ€μΈμ μν
|
|
6899
|
+
try {
|
|
6900
|
+
const scan = _scanShellScriptsEncoding(root);
|
|
6901
|
+
if (scan.atRisk && scan.atRisk.length > 0) {
|
|
6902
|
+
const isTty = process.stdout && process.stdout.isTTY;
|
|
6903
|
+
const rd5 = s => isTty ? `\x1b[31m${s}\x1b[0m` : s;
|
|
6904
|
+
const yl5 = s => isTty ? `\x1b[33m${s}\x1b[0m` : s;
|
|
6905
|
+
const dm5 = s => isTty ? `\x1b[2m${s}\x1b[0m` : s;
|
|
6906
|
+
log('');
|
|
6907
|
+
log(rd5(`## β μ
Έ μ€ν¬λ¦½νΈ μΈμ½λ© μν ${scan.atRisk.length}건 (1.9.241, UR-0014)`));
|
|
6908
|
+
scan.atRisk.slice(0, 3).forEach(r => {
|
|
6909
|
+
log(yl5(` β’ ${r.file}`));
|
|
6910
|
+
log(dm5(` ${r.risk}`));
|
|
6911
|
+
});
|
|
6912
|
+
if (scan.atRisk.length > 3) log(dm5(` ... +${scan.atRisk.length - 3}건 λ`));
|
|
6913
|
+
log(dm5(` β μμΈ: leerness env encoding`));
|
|
6914
|
+
log(dm5(` β ν΄κ²°: νμΌ μμμ UTF-8 BOM (EF BB BF) μΆκ°`));
|
|
6915
|
+
}
|
|
6916
|
+
} catch {}
|
|
6917
|
+
|
|
6726
6918
|
// 1.9.237: handoff λ³Έλ¬Έ release/* λμ κ²½κ³ β 50+ branches merged μ μλ μλ΄
|
|
6727
6919
|
// 1.9.220 λΉμ μμ’
λ£ release-branch-pending μ νΈμ λ³Έλ¬Έ λ³΄κ° (handoff ν€λλΌμΈμ βπ λΉμ μμ’
λ£β λ§ νμ)
|
|
6728
6920
|
try {
|
|
@@ -10572,6 +10764,19 @@ function sessionClose(root, opts = {}) {
|
|
|
10572
10764
|
try {
|
|
10573
10765
|
jsonResult.recentChanges = _computeRecentChanges(root, 5);
|
|
10574
10766
|
} catch {}
|
|
10767
|
+
// 1.9.240: pyFiles ν΅ν© (session close JSON 9λ²μ§Έ ν΅ν© νλ) β UR-0013 2λ¨κ³
|
|
10768
|
+
try {
|
|
10769
|
+
const pyFiles = _collectPyFiles(root, 200);
|
|
10770
|
+
const analyses = pyFiles.slice(0, 200).map(f => _analyzePyFile(f)).filter(Boolean);
|
|
10771
|
+
jsonResult.pyFiles = {
|
|
10772
|
+
total: pyFiles.length,
|
|
10773
|
+
analyzed: analyses.length,
|
|
10774
|
+
totalLOC: analyses.reduce((s, a) => s + a.loc, 0),
|
|
10775
|
+
totalImports: analyses.reduce((s, a) => s + a.imports, 0),
|
|
10776
|
+
totalFuncs: analyses.reduce((s, a) => s + a.funcs, 0),
|
|
10777
|
+
totalClasses: analyses.reduce((s, a) => s + a.classes, 0)
|
|
10778
|
+
};
|
|
10779
|
+
} catch {}
|
|
10575
10780
|
} catch {}
|
|
10576
10781
|
try {
|
|
10577
10782
|
// 1.9.209: pre-wake-audit μλ μ€ν + μ μ₯ (sleep μ μλ μ κ²)
|
|
@@ -14710,7 +14915,8 @@ function mcpServeCmd(root) {
|
|
|
14710
14915
|
{ name: 'leerness_commands', description: '1.9.233 β μΉ΄ν
κ³ λ¦¬νλ μ 체 CLI λͺ
λ Ή λͺ©λ‘ (9 μΉ΄ν
κ³ λ¦¬). μλ΅: { version, totalCommands, categories: { status, task, memory, audit, workflow, release, skill, bridge, config } }. μΈλΆ AIκ° "leerness κ° μ 곡νλ λͺ
λ Ήμ΄ λκ° μλ"λ₯Ό μ§μ νμ. λ§€λ΄μΌ/λμλ§ λμ μμ±. μΈμ: { path? }', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
14711
14916
|
{ name: 'leerness_release_cleanup', description: '1.9.236 (1.9.235 μλ νμ) β local release/* branches μ 리. main μ mergeλ κ²λ§ ν보 (unmerged 보νΈ, νμ¬ branch 보νΈ). μλ΅: { apply, keep, total, merged, unmerged, deleteCount, toDelete[], recent[], unmergedSample[] }. μΈλΆ AIκ° "μ΄μ λμ release branches μ 리 κ°λ₯?"μ νμ. κΈ°λ³Έ dry-run, apply: true μμλ§ μ€ μμ . μΈμ: { path?, apply? (default false), keep? (default 5) }', inputSchema: { type: 'object', properties: { path: { type: 'string' }, apply: { type: 'boolean' }, keep: { type: 'number' } } } },
|
|
14712
14917
|
{ name: 'leerness_py_check', description: '1.9.239 (μ¬μ©μ λͺ
μ UR-0013) β Python νμΌ λΆμ. μμ‘΄μ± 0 regex fallback. .md μΈ .py λ leerness μΈμ§. μλ΅: { totalFiles, totalLOC, totalImports, totalFuncs, totalClasses, totalTodos, biggest: [{ file, loc, funcs, classes }] }. μΈλΆ AIκ° "μ΄ νλ‘μ νΈ Python νλ©΄μ΄ μΌλ§λ λλ"λ₯Ό νμ. μΈμ: { path? }', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
14713
|
-
{ name: 'leerness_agent_mode', description: '1.9.239 (μ¬μ©μ λͺ
μ UR-0013) β μμ¨ λͺ¨λ μ μ© ν΅ν© λͺ
λ Ή. start: handoff + drift --auto-fix + session-resume --auto-fix (μ§μ
). tick: pulse ν μ€ (λ§€ λΌμ΄λ). stop: session close --auto-apply-delivered --auto-cleanup-branches (λ§κ°). μΈλΆ AI κ° μμ¨ λΌμ΄λ μ§μ
/λ§€ λΌμ΄λ/λ§κ°μ λ¨μΌ νΈμΆλ‘ μν. μΈμ: { path?, sub (required: "start"|"tick"|"stop"|"help") }', inputSchema: { type: 'object', properties: { path: { type: 'string' }, sub: { type: 'string', enum: ['start', 'tick', 'stop', 'help'] } }, required: ['sub'] } }
|
|
14918
|
+
{ name: 'leerness_agent_mode', description: '1.9.239 (μ¬μ©μ λͺ
μ UR-0013) β μμ¨ λͺ¨λ μ μ© ν΅ν© λͺ
λ Ή. start: handoff + drift --auto-fix + session-resume --auto-fix (μ§μ
). tick: pulse ν μ€ (λ§€ λΌμ΄λ). stop: session close --auto-apply-delivered --auto-cleanup-branches (λ§κ°). μΈλΆ AI κ° μμ¨ λΌμ΄λ μ§μ
/λ§€ λΌμ΄λ/λ§κ°μ λ¨μΌ νΈμΆλ‘ μν. μΈμ: { path?, sub (required: "start"|"tick"|"stop"|"help") }', inputSchema: { type: 'object', properties: { path: { type: 'string' }, sub: { type: 'string', enum: ['start', 'tick', 'stop', 'help'] } }, required: ['sub'] } },
|
|
14919
|
+
{ name: 'leerness_env_info', description: '1.9.241 (μ¬μ©μ λͺ
μ UR-0014) β νκ²½ μ’
ν© μ 보 νμ. OS / μΈμ΄ (LANG, μ½λνμ΄μ§) / νκ΅μ΄ Windows / νλμ¨μ΄ / ν°λ―Έλ (TTY, PowerShell λ²μ ) / λꡬ λ²μ (git, npm, python). μΈλΆ AI κ° νκ²½ νΈνμ±μ 미리 μΈμ§ β μΈμ½λ© μ€λ₯ μλ°©. μλ΅: { os, node, locale, hardware, terminal, tools }. μΈμ: { path?, encodingCheck? }. encodingCheck: true μ μ
Έ μ€ν¬λ¦½νΈ (.ps1/.bat/.cmd/.sh) BOM μλ λΉ-ASCII μν κ°μ§', inputSchema: { type: 'object', properties: { path: { type: 'string' }, encodingCheck: { type: 'boolean' } } } }
|
|
14714
14920
|
];
|
|
14715
14921
|
|
|
14716
14922
|
function send(obj) {
|
|
@@ -14895,6 +15101,10 @@ function mcpServeCmd(root) {
|
|
|
14895
15101
|
// 1.9.239 (UR-0013): μμ¨ λͺ¨λ ν΅ν© (start/tick/stop)
|
|
14896
15102
|
cliArgs = ['agent-mode', String(args.sub || 'help'), '--path', targetPath];
|
|
14897
15103
|
break;
|
|
15104
|
+
case 'leerness_env_info':
|
|
15105
|
+
// 1.9.241 (UR-0014): νκ²½ μ’
ν© + μΈμ½λ© μν κ°μ§
|
|
15106
|
+
cliArgs = ['env', args.encodingCheck === true ? 'encoding' : 'summary', '--path', targetPath, '--json'];
|
|
15107
|
+
break;
|
|
14898
15108
|
case 'leerness_release_cleanup':
|
|
14899
15109
|
// 1.9.236 (1.9.235): local release/* branches μ 리
|
|
14900
15110
|
cliArgs = ['release', 'cleanup', '--json'];
|
|
@@ -17125,6 +17335,19 @@ function healthCmd(root) {
|
|
|
17125
17335
|
try {
|
|
17126
17336
|
out.recentChanges = _computeRecentChanges(root, 5);
|
|
17127
17337
|
} catch { out.recentChanges = { error: 'recentChanges μ κ² μ€ν¨' }; }
|
|
17338
|
+
// 1.9.240: health --json pyFiles ν΅ν© (3 λͺ
λ Ή 9 νλ β UR-0013 2λ¨κ³)
|
|
17339
|
+
try {
|
|
17340
|
+
const pyFiles = _collectPyFiles(root, 200);
|
|
17341
|
+
const analyses = pyFiles.slice(0, 200).map(f => _analyzePyFile(f)).filter(Boolean);
|
|
17342
|
+
out.pyFiles = {
|
|
17343
|
+
total: pyFiles.length,
|
|
17344
|
+
analyzed: analyses.length,
|
|
17345
|
+
totalLOC: analyses.reduce((s, a) => s + a.loc, 0),
|
|
17346
|
+
totalImports: analyses.reduce((s, a) => s + a.imports, 0),
|
|
17347
|
+
totalFuncs: analyses.reduce((s, a) => s + a.funcs, 0),
|
|
17348
|
+
totalClasses: analyses.reduce((s, a) => s + a.classes, 0)
|
|
17349
|
+
};
|
|
17350
|
+
} catch { out.pyFiles = { error: 'pyFiles μ κ² μ€ν¨' }; }
|
|
17128
17351
|
// 1.9.163: 5λ₯λ ₯ λ§€νΈλ¦μ€ μλ νκ° (1.9.155 sub-agent μ κ² β μ½λ κΈ°λ° μλν)
|
|
17129
17352
|
// κ° λ₯λ ₯μ μ½λ grep μΌλ‘ κ²μΆ β 0~100 μ μ. μ¬μ©μκ° λ§€ health νΈμΆ μ leerness μκΈ° νκ° νμΈ.
|
|
17130
17353
|
try {
|
|
@@ -18570,6 +18793,10 @@ async function main() {
|
|
|
18570
18793
|
if (cmd === 'py-check') return pyCheckCmd(arg('--path', process.cwd()));
|
|
18571
18794
|
// 1.9.239: leerness agent-mode <start|tick|stop> β μμ¨ λͺ¨λ μ μ© ν΅ν© λͺ
λ Ή (μ¬μ©μ λͺ
μ UR-0013)
|
|
18572
18795
|
if (cmd === 'agent-mode') return agentModeCmd(arg('--path', process.cwd()), args[1]);
|
|
18796
|
+
// 1.9.241: leerness env summary|encoding β νκ²½ μ’
ν© + μ
Έ μ€ν¬λ¦½νΈ μΈμ½λ© μν (μ¬μ©μ λͺ
μ UR-0014)
|
|
18797
|
+
// κΈ°μ‘΄ env check/sync/detect (1.9.71) μ μΆ©λνμ§ μμ β summary/encoding μ κ· μλΈ
|
|
18798
|
+
if (cmd === 'env' && (args[1] === 'summary' || args[1] === 'encoding' || args[1] === 'encoding-check'))
|
|
18799
|
+
return envCmd(arg('--path', process.cwd()), args[1] === 'summary' ? null : 'encoding-check');
|
|
18573
18800
|
// 1.9.208: leerness constraints <list|check|add> β νλ«νΌ/API μ μ½ μ¬μ μ²΄ν¬ (μ¬μ©μ λͺ
μ)
|
|
18574
18801
|
if (cmd === 'constraints') return constraintsCmd(arg('--path', process.cwd()), args[1], ...args.slice(2));
|
|
18575
18802
|
// 1.9.209: leerness pre-wake-audit β sleep μ sub-agent audit (μ¬μ©μ λͺ
μ)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "leerness",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.241",
|
|
4
4
|
"description": "Leerness: λΉνκ΄΄ λ§μ΄κ·Έλ μ΄μ
, μλ λ²μ κ°μ§Β·μ
λ°μ΄νΈ, κ³ν/μ§ν/νΈλμ€ν μλν, κ²μΌλ¦Β·μν¬λ¦ΏΒ·μΈμ½λ© μλ κ°λ, Claude Code μ¬λμ ν΅ν©μ κ°μΆ νκ΅μ΄ μ°μ AI κ°λ° νλ€μ€.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"leerness",
|