leerness 1.9.238 β 1.9.240
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 +76 -0
- package/README.md +3 -3
- package/bin/harness.js +212 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,81 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.9.240 β 2026-05-24
|
|
4
|
+
|
|
5
|
+
**π UR-0013 2λ¨κ³: handoff/session close/health --json pyFiles (9 νλ) + ν€λλΌμΈ μλ.**
|
|
6
|
+
|
|
7
|
+
### 1. handoff/session close/health --json 9λ²μ§Έ ν΅ν© νλ `pyFiles`
|
|
8
|
+
- Python νμΌ μλ detect (1.9.239 `_collectPyFiles` μ¬μ¬μ©)
|
|
9
|
+
- νμ: `{ total, analyzed, totalLOC, totalImports, totalFuncs, totalClasses }`
|
|
10
|
+
- 3 λͺ
λ Ή μΌκ΄μ± λ§€νΈλ¦μ€ μ§ν: 7 β 8 β **9 νλ** (3 Γ 9 = 27 ν΅ν© ν¬μΈνΈ)
|
|
11
|
+
- AI κ° λ€μ€ μΈμ΄ μν¬μ€νμ΄μ€ (.md + .py) μΈμ§
|
|
12
|
+
|
|
13
|
+
### 2. handoff ν€λλΌμΈ 18λ²μ§Έ μμ
|
|
14
|
+
- `.py` νμΌ β₯ 1 μ: `π py N` μλ λ
ΈμΆ
|
|
15
|
+
- 0κ°μΌ λ λ―Έλ
ΈμΆ (μ‘°μ©ν¨)
|
|
16
|
+
|
|
17
|
+
### 3. UR-0013 2λ¨κ³ μλ£
|
|
18
|
+
- 1.9.239 1λ¨κ³: py-check CLI + agent-mode CLI + MCP 67/68
|
|
19
|
+
- **1.9.240 2λ¨κ³**: JSON 9 νλ + ν€λλΌμΈ μλ
|
|
20
|
+
- λ€μ€ μΈμ΄ νλ©΄ (.md + .py) μλ κ°μν
|
|
21
|
+
|
|
22
|
+
### 4. λμ νκ· (1.9.207~239) β λͺ¨λ μ μ§
|
|
23
|
+
|
|
24
|
+
### 5. stress-v185 β 16/16 PASS
|
|
25
|
+
- 1.9.240 (7): VERSION + handoff/session close/health JSON + 9 νλ + ν€λλΌμΈ (μμ/μμ)
|
|
26
|
+
- μ±λ₯ (1): cold_start avg 386ms
|
|
27
|
+
- λμ νκ· (8): 1.9.207~239
|
|
28
|
+
|
|
29
|
+
### 6. μλ release (102 λΌμ΄λ main-push streak Β· 63 λΌμ΄λ npm publish streak)
|
|
30
|
+
|
|
31
|
+
π **λ€μ€ μΈμ΄ νλ©΄ κ°μν** β .md + .py μλ ν΅ν© (μΈλΆ AI κ° λ νλ©΄ λͺ¨λ νμ)
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 1.9.239 β 2026-05-24
|
|
36
|
+
|
|
37
|
+
**π leerness py-check + π€ agent-mode + MCP 67/68 (μ¬μ©μ λͺ
μ UR-0013).**
|
|
38
|
+
|
|
39
|
+
### μ¬μ©μ λͺ
μ (UR-0013)
|
|
40
|
+
> *"leernessκ° mdλΏλ§ μλλΌ pyλ₯Ό ν¬ν¨ν νμ΄μ¬ μ€ν¬λ¦½νΈ λ±μ΄ ν¨μ¨μ μ΄λΌκ±°λ, μμ΄μ νΈ λͺ¨λλ₯Ό μν μ₯μΉ λ± λ ν¨μ¨μ μΌλ‘ μλν μ μλλ‘ κ΅¬ν"*
|
|
41
|
+
|
|
42
|
+
### 1. `leerness py-check` μ λͺ
λ Ή β Python νμΌ λΆμ
|
|
43
|
+
- μμ‘΄μ± 0, regex fallback (Node λ΄μ₯λ§)
|
|
44
|
+
- λΆμ: νμΌ μ / LOC / imports / functions / classes / TODO/FIXME
|
|
45
|
+
- Top 5 νμΌ (LOC κΈ°μ€)
|
|
46
|
+
- `--json` μ΅μ
(μΈλΆ μλν)
|
|
47
|
+
- `.harness` / `node_modules` / `venv` / `__pycache__` λ± μλ skip
|
|
48
|
+
- leerness κ° .md μΈμλ **.py μΈμ§** β λ€μ€ μΈμ΄ μν¬μ€νμ΄μ€ μ§μ
|
|
49
|
+
|
|
50
|
+
### 2. `leerness agent-mode <start|tick|stop>` β μμ¨ λͺ¨λ μ μ© ν΅ν©
|
|
51
|
+
- **start**: handoff + drift --auto-fix + session-resume --auto-fix (μ§μ
μλν)
|
|
52
|
+
- **tick**: pulse ν μ€ (λ§€ λΌμ΄λ κ°λ²Όμ)
|
|
53
|
+
- **stop**: session close --auto-apply-delivered --auto-cleanup-branches (λ§κ° ν΅ν©)
|
|
54
|
+
- AI μμ¨ λΌμ΄λ μ§μ
/λ§€ λΌμ΄λ/λ§κ°μ **1 λͺ
λ ΉμΌλ‘ μμΆ** β νΈμΆ λΉμ© μ κ°
|
|
55
|
+
|
|
56
|
+
### 3. MCP 67/68λ²μ§Έ λꡬ
|
|
57
|
+
- `leerness_py_check` β μΈλΆ AI κ° Python νλ©΄ λΆμ
|
|
58
|
+
- `leerness_agent_mode` β μΈλΆ AI κ° μμ¨ λͺ¨λ λ¨μΌ νΈμΆ
|
|
59
|
+
- MCP 66 β 68 (+2)
|
|
60
|
+
|
|
61
|
+
### 4. κ²μ¦ β μ€ Python νμΌ (격리)
|
|
62
|
+
- 2 νμΌ β 22 LOC / 4 imports / 4 funcs / 2 classes / 2 TODOs (μ ν νμ)
|
|
63
|
+
- 200 file limit μμ κ°λ + heavy dir skip
|
|
64
|
+
|
|
65
|
+
### 5. λμ νκ· (1.9.207~238) β λͺ¨λ μ μ§
|
|
66
|
+
|
|
67
|
+
### 6. stress-v184 β 18/18 PASS
|
|
68
|
+
- 1.9.239 (8): VERSION + py-check (격리/human/fallback) + agent-mode (help/tick) + MCP 68 + μ€ νΈμΆ
|
|
69
|
+
- μ±λ₯ (2): cold_start avg 372ms / py-check 387ms
|
|
70
|
+
- λμ νκ· (8): 1.9.207~238
|
|
71
|
+
|
|
72
|
+
### 7. μλ release (101 λΌμ΄λ main-push streak Β· 62 λΌμ΄λ npm publish streak)
|
|
73
|
+
|
|
74
|
+
π **leerness λ€μ€ μΈμ΄ μ§μ μ μ€** β .md β .md + .py (νμ₯ κ°λ₯ λ² μ΄μ€)
|
|
75
|
+
π€ **agent-mode ν΅ν©** β μμ¨ λͺ¨λ μ§μ
/λ§€λΌμ΄λ/λ§κ°μ 3 λͺ
λ ΉμΌλ‘ λ¨μΆ
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
3
79
|
## 1.9.238 β 2026-05-24
|
|
4
80
|
|
|
5
81
|
**π R100 main-push streak λ¬μ± + CLAUDE/AGENTS/session-workflow drift μ°¨λ¨ (1.9.228~237 9 λΌμ΄λ λμ ).**
|
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.240 AI Agent Reliability Harness + Sandbox β
|
|
16
16
|
β verify Β· remember Β· orchestrate Β· audit Β· sandbox Β· drift β
|
|
17
|
-
β
|
|
17
|
+
β π JSON 9 νλ (3 λͺ
λ Ή Γ 9 = 27 ν΅ν© β UR-0013 2λ¨κ³) β
|
|
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.240';
|
|
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 μ²λ¦¬).
|
|
@@ -2196,6 +2196,156 @@ function milestonesCmd(root) {
|
|
|
2196
2196
|
}
|
|
2197
2197
|
}
|
|
2198
2198
|
|
|
2199
|
+
// 1.9.239: Python νμΌ λΆμ (μ¬μ©μ λͺ
μ UR-0013) β μμ‘΄μ± 0 regex fallback
|
|
2200
|
+
// leerness κ° .md μΈμ .py νμ΄μ¬ μ€ν¬λ¦½νΈλ ν¨μ¨μ μΌλ‘ μΈμ§νλλ‘.
|
|
2201
|
+
// λΆμ: νμΌ μ / import μ / ν¨μ μ / class μ / 볡μ‘λ(approx LOC) / TODO/FIXME
|
|
2202
|
+
// μλ΅ (--json): { totalFiles, totalLOC, imports[], funcs[], classes[], complexity, todoCount }
|
|
2203
|
+
function _collectPyFiles(root, limit = 100) {
|
|
2204
|
+
const out = [];
|
|
2205
|
+
function walk(dir) {
|
|
2206
|
+
if (out.length >= limit) return;
|
|
2207
|
+
let entries;
|
|
2208
|
+
try { entries = fs.readdirSync(dir, { withFileTypes: true }); }
|
|
2209
|
+
catch { return; }
|
|
2210
|
+
for (const e of entries) {
|
|
2211
|
+
if (out.length >= limit) return;
|
|
2212
|
+
const fp = path.join(dir, e.name);
|
|
2213
|
+
if (e.isDirectory()) {
|
|
2214
|
+
// skip common heavy dirs
|
|
2215
|
+
if (/^(node_modules|\.git|__pycache__|\.harness|venv|\.venv|dist|build|_apps|_reports)$/.test(e.name)) continue;
|
|
2216
|
+
walk(fp);
|
|
2217
|
+
} else if (e.isFile() && /\.py$/i.test(e.name)) {
|
|
2218
|
+
out.push(fp);
|
|
2219
|
+
}
|
|
2220
|
+
}
|
|
2221
|
+
}
|
|
2222
|
+
walk(root);
|
|
2223
|
+
return out;
|
|
2224
|
+
}
|
|
2225
|
+
|
|
2226
|
+
function _analyzePyFile(fp) {
|
|
2227
|
+
let txt = '';
|
|
2228
|
+
try { txt = fs.readFileSync(fp, 'utf8'); } catch { return null; }
|
|
2229
|
+
const lines = txt.split('\n');
|
|
2230
|
+
const importRe = /^\s*(?:from\s+\S+\s+)?import\s+\S+/;
|
|
2231
|
+
const funcRe = /^\s*(?:async\s+)?def\s+(\w+)\s*\(/;
|
|
2232
|
+
const classRe = /^\s*class\s+(\w+)/;
|
|
2233
|
+
const todoRe = /#\s*(TODO|FIXME|XXX|HACK)\b/;
|
|
2234
|
+
let imports = 0, funcs = [], classes = [], todos = 0;
|
|
2235
|
+
for (const line of lines) {
|
|
2236
|
+
if (importRe.test(line)) imports++;
|
|
2237
|
+
const fm = line.match(funcRe); if (fm) funcs.push(fm[1]);
|
|
2238
|
+
const cm = line.match(classRe); if (cm) classes.push(cm[1]);
|
|
2239
|
+
if (todoRe.test(line)) todos++;
|
|
2240
|
+
}
|
|
2241
|
+
return {
|
|
2242
|
+
file: fp,
|
|
2243
|
+
loc: lines.length,
|
|
2244
|
+
imports,
|
|
2245
|
+
funcs: funcs.length,
|
|
2246
|
+
classes: classes.length,
|
|
2247
|
+
todos
|
|
2248
|
+
};
|
|
2249
|
+
}
|
|
2250
|
+
|
|
2251
|
+
function pyCheckCmd(root) {
|
|
2252
|
+
root = absRoot(root);
|
|
2253
|
+
const isTty = process.stdout && process.stdout.isTTY;
|
|
2254
|
+
const cy = s => isTty ? `\x1b[36m${s}\x1b[0m` : s;
|
|
2255
|
+
const gr = s => isTty ? `\x1b[32m${s}\x1b[0m` : s;
|
|
2256
|
+
const yl = s => isTty ? `\x1b[33m${s}\x1b[0m` : s;
|
|
2257
|
+
const dm = s => isTty ? `\x1b[2m${s}\x1b[0m` : s;
|
|
2258
|
+
const files = _collectPyFiles(root, 200);
|
|
2259
|
+
const analyses = files.map(f => _analyzePyFile(f)).filter(Boolean);
|
|
2260
|
+
const totalLOC = analyses.reduce((s, a) => s + a.loc, 0);
|
|
2261
|
+
const totalImports = analyses.reduce((s, a) => s + a.imports, 0);
|
|
2262
|
+
const totalFuncs = analyses.reduce((s, a) => s + a.funcs, 0);
|
|
2263
|
+
const totalClasses = analyses.reduce((s, a) => s + a.classes, 0);
|
|
2264
|
+
const totalTodos = analyses.reduce((s, a) => s + a.todos, 0);
|
|
2265
|
+
const result = {
|
|
2266
|
+
totalFiles: analyses.length,
|
|
2267
|
+
totalLOC, totalImports, totalFuncs, totalClasses, totalTodos,
|
|
2268
|
+
biggest: analyses.slice().sort((a, b) => b.loc - a.loc).slice(0, 5).map(a => ({ file: path.relative(root, a.file), loc: a.loc, funcs: a.funcs, classes: a.classes }))
|
|
2269
|
+
};
|
|
2270
|
+
if (has('--json')) { log(JSON.stringify(result, null, 2)); return; }
|
|
2271
|
+
log(cy(`# leerness py-check (1.9.239) β Python νμΌ λΆμ (μ¬μ©μ λͺ
μ UR-0013)`));
|
|
2272
|
+
log('');
|
|
2273
|
+
if (analyses.length === 0) {
|
|
2274
|
+
log(dm(` Python νμΌ μμ (.py κ²μλ¨: ${files.length}, λΆμ κ°λ₯: 0)`));
|
|
2275
|
+
log(dm(` β leerness λ .md/.py λͺ¨λ λΆμ κ°λ₯ (1.9.239)`));
|
|
2276
|
+
return;
|
|
2277
|
+
}
|
|
2278
|
+
log(` π ν΅κ³:`);
|
|
2279
|
+
log(` νμΌ ${gr(String(analyses.length))} Β· LOC ${gr(String(totalLOC))} Β· νκ· ${gr(String(Math.round(totalLOC / analyses.length)))} loc/file`);
|
|
2280
|
+
log(` imports ${totalImports} Β· functions ${totalFuncs} Β· classes ${totalClasses}`);
|
|
2281
|
+
if (totalTodos > 0) log(yl(` β TODO/FIXME ${totalTodos}건`));
|
|
2282
|
+
log('');
|
|
2283
|
+
if (result.biggest.length > 0) {
|
|
2284
|
+
log(` Top 5 νμΌ (LOC κΈ°μ€):`);
|
|
2285
|
+
result.biggest.forEach(f => log(` β’ ${f.file} β ${f.loc} loc / ${f.funcs} func / ${f.classes} class`));
|
|
2286
|
+
}
|
|
2287
|
+
log('');
|
|
2288
|
+
log(dm(` β JSON μΆλ ₯: leerness py-check --json`));
|
|
2289
|
+
}
|
|
2290
|
+
|
|
2291
|
+
// 1.9.239: agent-mode β μμ¨ λͺ¨λ μ μ© ν΅ν© λͺ
λ Ή (μ¬μ©μ λͺ
μ UR-0013)
|
|
2292
|
+
// handoff + pulse + drift auto-fix + session-resume λ₯Ό 1 λͺ
λ ΉμΌλ‘
|
|
2293
|
+
// start: μ§μ
(handoff + drift auto-fix + λΉμ μμ’
λ£ μλ ν볡)
|
|
2294
|
+
// tick: λ§€ λΌμ΄λ (pulse + 1 line)
|
|
2295
|
+
// stop: λ§κ° (session close + auto-apply-delivered + auto-cleanup-branches)
|
|
2296
|
+
function agentModeCmd(root, sub) {
|
|
2297
|
+
root = absRoot(root);
|
|
2298
|
+
const isTty = process.stdout && process.stdout.isTTY;
|
|
2299
|
+
const cy = s => isTty ? `\x1b[36m${s}\x1b[0m` : s;
|
|
2300
|
+
const gr = s => isTty ? `\x1b[32m${s}\x1b[0m` : s;
|
|
2301
|
+
const yl = s => isTty ? `\x1b[33m${s}\x1b[0m` : s;
|
|
2302
|
+
const dm = s => isTty ? `\x1b[2m${s}\x1b[0m` : s;
|
|
2303
|
+
function spawnChild(args, opts) {
|
|
2304
|
+
return cp.spawnSync(process.execPath, [__filename, ...args], Object.assign({ encoding: 'utf8', cwd: root, env: { ...process.env, LEERNESS_NO_BANNER: '1', LEERNESS_NO_PROMPT: '1' } }, opts || {}));
|
|
2305
|
+
}
|
|
2306
|
+
if (!sub || sub === 'help') {
|
|
2307
|
+
log(cy(`# leerness agent-mode (1.9.239) β μμ¨ λͺ¨λ μ μ© ν΅ν© λͺ
λ Ή`));
|
|
2308
|
+
log('');
|
|
2309
|
+
log(` start μ§μ
(handoff + drift --auto-fix + session-resume --auto-fix)`);
|
|
2310
|
+
log(` tick λ§€ λΌμ΄λ (pulse + ν΅μ¬ μ νΈ)`);
|
|
2311
|
+
log(` stop λ§κ° (session close --auto-apply-delivered --auto-cleanup-branches)`);
|
|
2312
|
+
log('');
|
|
2313
|
+
log(dm(` μ¬μ©μ λͺ
μ UR-0013 (Python + agent-mode ν¨μ¨ν)`));
|
|
2314
|
+
return;
|
|
2315
|
+
}
|
|
2316
|
+
if (sub === 'start') {
|
|
2317
|
+
log(cy(`# leerness agent-mode start (1.9.239)`));
|
|
2318
|
+
log(dm(` β 1. drift check --auto-fix (보μ + delivered + release cleanup)`));
|
|
2319
|
+
const r1 = spawnChild(['drift', 'check', root, '--auto-fix']);
|
|
2320
|
+
if (r1.status === 0) log(gr(` β drift auto-fix μλ£`)); else log(yl(` β drift auto-fix μ’
λ£ ${r1.status}`));
|
|
2321
|
+
log(dm(` β 2. session-resume --auto-fix (λΉμ μμ’
λ£ μλ ν볡)`));
|
|
2322
|
+
const r2 = spawnChild(['session-resume', '--path', root, '--auto-fix']);
|
|
2323
|
+
if (r2.status === 0) log(gr(` β session-resume μλ£`)); else log(yl(` β session-resume μ’
λ£ ${r2.status}`));
|
|
2324
|
+
log(dm(` β 3. handoff (컨ν
μ€νΈ μ μ¬)`));
|
|
2325
|
+
const r3 = spawnChild(['handoff', root]);
|
|
2326
|
+
if (r3.status === 0) log(gr(` β handoff μΆλ ₯ μλ£ (μ μ°Έμ‘°)`)); else log(yl(` β handoff μ’
λ£ ${r3.status}`));
|
|
2327
|
+
log('');
|
|
2328
|
+
log(gr(`β agent-mode start μλ£ β μμ¨ λΌμ΄λ μ§ν μ€λΉ`));
|
|
2329
|
+
return;
|
|
2330
|
+
}
|
|
2331
|
+
if (sub === 'tick') {
|
|
2332
|
+
// κ°λ²Όμ μ°μ β pulse λ§ νΈμΆ
|
|
2333
|
+
log(cy(`# leerness agent-mode tick (1.9.239) β κ°λ²Όμ΄ μν νμΈ`));
|
|
2334
|
+
const r = spawnChild(['pulse', '--path', root]);
|
|
2335
|
+
process.stdout.write(r.stdout || '');
|
|
2336
|
+
return;
|
|
2337
|
+
}
|
|
2338
|
+
if (sub === 'stop') {
|
|
2339
|
+
log(cy(`# leerness agent-mode stop (1.9.239) β ν΅ν© λ§κ°`));
|
|
2340
|
+
log(dm(` β session close --auto-apply-delivered --auto-cleanup-branches`));
|
|
2341
|
+
const r = spawnChild(['session', 'close', root, '--auto-apply-delivered', '--auto-cleanup-branches']);
|
|
2342
|
+
process.stdout.write(r.stdout || '');
|
|
2343
|
+
return;
|
|
2344
|
+
}
|
|
2345
|
+
log(yl(`unknown subcommand: ${sub}`));
|
|
2346
|
+
log(dm(` β leerness agent-mode help`));
|
|
2347
|
+
}
|
|
2348
|
+
|
|
2199
2349
|
// 1.9.234: recentChanges β μ΅κ·Ό N λΌμ΄λμ ν΅μ¬ λ³κ²½ μμ½ (git tag commit subject)
|
|
2200
2350
|
// μλ΅: [{ version, date, subject }] (κ°μ₯ μ΅κ·ΌλΆν°)
|
|
2201
2351
|
function _computeRecentChanges(root, limit = 5) {
|
|
@@ -6240,6 +6390,19 @@ function handoff(root) {
|
|
|
6240
6390
|
try {
|
|
6241
6391
|
result.recentChanges = _computeRecentChanges(root, 5);
|
|
6242
6392
|
} catch {}
|
|
6393
|
+
// 1.9.240: pyFiles ν΅ν© (handoff JSON 9λ²μ§Έ ν΅ν© νλ) β Python λ€μ€ μΈμ΄ κ°μν (UR-0013 2λ¨κ³)
|
|
6394
|
+
try {
|
|
6395
|
+
const pyFiles = _collectPyFiles(root, 200);
|
|
6396
|
+
const analyses = pyFiles.slice(0, 200).map(f => _analyzePyFile(f)).filter(Boolean);
|
|
6397
|
+
result.pyFiles = {
|
|
6398
|
+
total: pyFiles.length,
|
|
6399
|
+
analyzed: analyses.length,
|
|
6400
|
+
totalLOC: analyses.reduce((s, a) => s + a.loc, 0),
|
|
6401
|
+
totalImports: analyses.reduce((s, a) => s + a.imports, 0),
|
|
6402
|
+
totalFuncs: analyses.reduce((s, a) => s + a.funcs, 0),
|
|
6403
|
+
totalClasses: analyses.reduce((s, a) => s + a.classes, 0)
|
|
6404
|
+
};
|
|
6405
|
+
} catch {}
|
|
6243
6406
|
} catch {}
|
|
6244
6407
|
try {
|
|
6245
6408
|
const pwState = _loadPreWakeReport(root);
|
|
@@ -6402,6 +6565,13 @@ function handoff(root) {
|
|
|
6402
6565
|
parts.push(`π official ${m.total}/${m.cacheTotal} (${ageStr}${status})`);
|
|
6403
6566
|
}
|
|
6404
6567
|
} catch {}
|
|
6568
|
+
// 18) 1.9.240: pyFiles ν€λλΌμΈ β Python λ€μ€ μΈμ΄ κ°μν (UR-0013 2λ¨κ³, .py νμΌ β₯ 1)
|
|
6569
|
+
try {
|
|
6570
|
+
const pyFiles = _collectPyFiles(root, 200);
|
|
6571
|
+
if (pyFiles.length > 0) {
|
|
6572
|
+
parts.push(`π py ${pyFiles.length}`);
|
|
6573
|
+
}
|
|
6574
|
+
} catch {}
|
|
6405
6575
|
// 17) 1.9.226: round-history ν€λλΌμΈ β μμ¨ λΌμ΄λ μΉ΄μ΄ν° + λ€μ λ§μΌμ€ν€ (5 λΌμ΄λ μ΄μ μλ§ νμ)
|
|
6406
6576
|
// 1.9.230: λ€μ λ§μΌμ€ν€ ETA (10R μ΄λ΄ + ETA 7μΌ μ΄λ΄μΌ λ κ°μ‘°)
|
|
6407
6577
|
try {
|
|
@@ -10422,6 +10592,19 @@ function sessionClose(root, opts = {}) {
|
|
|
10422
10592
|
try {
|
|
10423
10593
|
jsonResult.recentChanges = _computeRecentChanges(root, 5);
|
|
10424
10594
|
} catch {}
|
|
10595
|
+
// 1.9.240: pyFiles ν΅ν© (session close JSON 9λ²μ§Έ ν΅ν© νλ) β UR-0013 2λ¨κ³
|
|
10596
|
+
try {
|
|
10597
|
+
const pyFiles = _collectPyFiles(root, 200);
|
|
10598
|
+
const analyses = pyFiles.slice(0, 200).map(f => _analyzePyFile(f)).filter(Boolean);
|
|
10599
|
+
jsonResult.pyFiles = {
|
|
10600
|
+
total: pyFiles.length,
|
|
10601
|
+
analyzed: analyses.length,
|
|
10602
|
+
totalLOC: analyses.reduce((s, a) => s + a.loc, 0),
|
|
10603
|
+
totalImports: analyses.reduce((s, a) => s + a.imports, 0),
|
|
10604
|
+
totalFuncs: analyses.reduce((s, a) => s + a.funcs, 0),
|
|
10605
|
+
totalClasses: analyses.reduce((s, a) => s + a.classes, 0)
|
|
10606
|
+
};
|
|
10607
|
+
} catch {}
|
|
10425
10608
|
} catch {}
|
|
10426
10609
|
try {
|
|
10427
10610
|
// 1.9.209: pre-wake-audit μλ μ€ν + μ μ₯ (sleep μ μλ μ κ²)
|
|
@@ -14558,7 +14741,9 @@ function mcpServeCmd(root) {
|
|
|
14558
14741
|
{ name: 'leerness_milestones', description: '1.9.229 (1.9.226 νμ₯) β λλ¬ λ§μΌμ€ν€ + λ€μ ETA. git tag μμ°¨ λΆμμΌλ‘ 25/50/75/100/125/150/175/200/250/300/400/500 λ§μΌμ€ν€ λλ¬ μΌμ + λ€μ λ§μΌμ€ν€ ETA (νμ¬ μλ κΈ°μ€) κ³μ°. μλ΅: { totalRounds, reached: [{milestone, version, reachedAt, daysFromBaseline}], next: {milestone, roundsRemaining, etaDays, etaDate}, baselineAt, avgRoundsPerDay }. μΈλΆ AIκ° "μ§κΈκΉμ§ μ΄λ€ λ§μΌμ€ν€μ μΈμ λ¬μ±νκ³ λ€μ λ§μΌμ€ν€ μμ λλ¬μΌμ?"μ νμ. μΈμ: { path? }', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
14559
14742
|
{ name: 'leerness_pulse', description: '1.9.231 β ν μ€ μ’
ν© μμ½ (10 ν΅μ¬ μ§ν). μλ΅: { version, roundCount, mcpTools, memorySurface, security, health, driftScore, nextMilestone, etaDays, abnormalShutdown }. μΈλΆ AIκ° "leerness μν ν λμ 보기"λ₯Ό κ°λ²Όμ΄ λ¨μΌ νΈμΆλ‘ νμ. handoff λ³΄λ€ 5λ°° λΉ λ¦ (drift/health κ³μ° skip). μΈμ: { path? }', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
14560
14743
|
{ 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' } } } },
|
|
14561
|
-
{ 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' } } } }
|
|
14744
|
+
{ 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' } } } },
|
|
14745
|
+
{ 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' } } } },
|
|
14746
|
+
{ 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'] } }
|
|
14562
14747
|
];
|
|
14563
14748
|
|
|
14564
14749
|
function send(obj) {
|
|
@@ -14735,6 +14920,14 @@ function mcpServeCmd(root) {
|
|
|
14735
14920
|
// 1.9.233: μΉ΄ν
κ³ λ¦¬νλ μ 체 CLI λͺ
λ Ή λͺ©λ‘
|
|
14736
14921
|
cliArgs = ['commands', '--json'];
|
|
14737
14922
|
break;
|
|
14923
|
+
case 'leerness_py_check':
|
|
14924
|
+
// 1.9.239 (UR-0013): Python νμΌ λΆμ
|
|
14925
|
+
cliArgs = ['py-check', '--path', targetPath, '--json'];
|
|
14926
|
+
break;
|
|
14927
|
+
case 'leerness_agent_mode':
|
|
14928
|
+
// 1.9.239 (UR-0013): μμ¨ λͺ¨λ ν΅ν© (start/tick/stop)
|
|
14929
|
+
cliArgs = ['agent-mode', String(args.sub || 'help'), '--path', targetPath];
|
|
14930
|
+
break;
|
|
14738
14931
|
case 'leerness_release_cleanup':
|
|
14739
14932
|
// 1.9.236 (1.9.235): local release/* branches μ 리
|
|
14740
14933
|
cliArgs = ['release', 'cleanup', '--json'];
|
|
@@ -16965,6 +17158,19 @@ function healthCmd(root) {
|
|
|
16965
17158
|
try {
|
|
16966
17159
|
out.recentChanges = _computeRecentChanges(root, 5);
|
|
16967
17160
|
} catch { out.recentChanges = { error: 'recentChanges μ κ² μ€ν¨' }; }
|
|
17161
|
+
// 1.9.240: health --json pyFiles ν΅ν© (3 λͺ
λ Ή 9 νλ β UR-0013 2λ¨κ³)
|
|
17162
|
+
try {
|
|
17163
|
+
const pyFiles = _collectPyFiles(root, 200);
|
|
17164
|
+
const analyses = pyFiles.slice(0, 200).map(f => _analyzePyFile(f)).filter(Boolean);
|
|
17165
|
+
out.pyFiles = {
|
|
17166
|
+
total: pyFiles.length,
|
|
17167
|
+
analyzed: analyses.length,
|
|
17168
|
+
totalLOC: analyses.reduce((s, a) => s + a.loc, 0),
|
|
17169
|
+
totalImports: analyses.reduce((s, a) => s + a.imports, 0),
|
|
17170
|
+
totalFuncs: analyses.reduce((s, a) => s + a.funcs, 0),
|
|
17171
|
+
totalClasses: analyses.reduce((s, a) => s + a.classes, 0)
|
|
17172
|
+
};
|
|
17173
|
+
} catch { out.pyFiles = { error: 'pyFiles μ κ² μ€ν¨' }; }
|
|
16968
17174
|
// 1.9.163: 5λ₯λ ₯ λ§€νΈλ¦μ€ μλ νκ° (1.9.155 sub-agent μ κ² β μ½λ κΈ°λ° μλν)
|
|
16969
17175
|
// κ° λ₯λ ₯μ μ½λ grep μΌλ‘ κ²μΆ β 0~100 μ μ. μ¬μ©μκ° λ§€ health νΈμΆ μ leerness μκΈ° νκ° νμΈ.
|
|
16970
17176
|
try {
|
|
@@ -18406,6 +18612,10 @@ async function main() {
|
|
|
18406
18612
|
if (cmd === 'pulse') return pulseCmd(arg('--path', process.cwd()));
|
|
18407
18613
|
// 1.9.233: leerness commands β μΉ΄ν
κ³ λ¦¬νλ μ 체 CLI λͺ
λ Ή λͺ©λ‘
|
|
18408
18614
|
if (cmd === 'commands') return commandsCmd(arg('--path', process.cwd()));
|
|
18615
|
+
// 1.9.239: leerness py-check β Python νμΌ λΆμ (μ¬μ©μ λͺ
μ UR-0013)
|
|
18616
|
+
if (cmd === 'py-check') return pyCheckCmd(arg('--path', process.cwd()));
|
|
18617
|
+
// 1.9.239: leerness agent-mode <start|tick|stop> β μμ¨ λͺ¨λ μ μ© ν΅ν© λͺ
λ Ή (μ¬μ©μ λͺ
μ UR-0013)
|
|
18618
|
+
if (cmd === 'agent-mode') return agentModeCmd(arg('--path', process.cwd()), args[1]);
|
|
18409
18619
|
// 1.9.208: leerness constraints <list|check|add> β νλ«νΌ/API μ μ½ μ¬μ μ²΄ν¬ (μ¬μ©μ λͺ
μ)
|
|
18410
18620
|
if (cmd === 'constraints') return constraintsCmd(arg('--path', process.cwd()), args[1], ...args.slice(2));
|
|
18411
18621
|
// 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.240",
|
|
4
4
|
"description": "Leerness: λΉνκ΄΄ λ§μ΄κ·Έλ μ΄μ
, μλ λ²μ κ°μ§Β·μ
λ°μ΄νΈ, κ³ν/μ§ν/νΈλμ€ν μλν, κ²μΌλ¦Β·μν¬λ¦ΏΒ·μΈμ½λ© μλ κ°λ, Claude Code μ¬λμ ν΅ν©μ κ°μΆ νκ΅μ΄ μ°μ AI κ°λ° νλ€μ€.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"leerness",
|