leerness 1.9.129 → 1.9.131
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 +49 -0
- package/README.md +2 -2
- package/bin/harness.js +138 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,54 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.9.131 — 2026-05-20
|
|
4
|
+
|
|
5
|
+
**brainstorm 회수 범위에 3 archive 파일 통합** — 과거에 제거됐던 ideas 가 새 brainstorm 시 다시 후보로 노출.
|
|
6
|
+
|
|
7
|
+
### Added — brainstorm + archive 통합
|
|
8
|
+
- `hits.archive: { decisions: [], lessons: [], plan: [] }` 추가
|
|
9
|
+
- 3 archive 파일 (`.harness/decisions.archive.md`, `lessons.archive.md`, `plan.archive.md`) 본문 키워드 매칭
|
|
10
|
+
- entry 구조: `{ date, target, originalHeader, preview, line }`
|
|
11
|
+
- 텍스트 모드: `🗑 archive 후보 (N)` 섹션 + 복원 안내 라인
|
|
12
|
+
- `_brainstormFor` (helper) + `brainstormCmd` (CLI) 양쪽 동일 구현
|
|
13
|
+
|
|
14
|
+
### 사용 시나리오
|
|
15
|
+
사용자: `leerness brainstorm "PostgreSQL"`
|
|
16
|
+
→ 응답에 과거 archive 후보 포함:
|
|
17
|
+
```
|
|
18
|
+
🗑 archive 후보 (2) — 과거에 제거됐던 ideas; 복원 검토 가능 (1.9.131)
|
|
19
|
+
- 🧠 .harness/decisions.archive.md:4 — 2026-05-20 "PostgreSQL"
|
|
20
|
+
- 💡 .harness/lessons.archive.md:4 — 2026-05-20 "PostgreSQL"
|
|
21
|
+
→ 복원: leerness memory restore <decisions|lessons|plan> <target>
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### brainstorm 누적 source 진화
|
|
25
|
+
| 라운드 | source |
|
|
26
|
+
|---|---|
|
|
27
|
+
| (기존) | decisions / skills / tasks / rules / evidence / lessons / code |
|
|
28
|
+
| 1.9.72 | skillHistory / taskLogFails |
|
|
29
|
+
| 1.9.116 | lessonsExplicit / planMilestones |
|
|
30
|
+
| **1.9.131** | **archive (decisions/lessons/plan)** |
|
|
31
|
+
|
|
32
|
+
## 1.9.130 — 2026-05-20 🎉 **60 라운드 자율 모드 마일스톤**
|
|
33
|
+
|
|
34
|
+
**JSON 4종 통합에 `memorySurface.archive` 필드 추가** + 60 라운드 자율 누적 보고서.
|
|
35
|
+
|
|
36
|
+
### Added — archive 필드 (JSON 4종)
|
|
37
|
+
- `handoff --json` / `memory status --json` / `session close --json` / `health --json` 모두 `memorySurface.archive: { decisions, lessons, plan, total }` 노출
|
|
38
|
+
- `memory status` 텍스트 모드: `🗑 Archive: D1/L1/P0 (2건)` 라인 추가
|
|
39
|
+
- 외부 AI 가 한 JSON 호출로 모든 메모리 상태 (active + archive) 동시 회수
|
|
40
|
+
|
|
41
|
+
### 60 라운드 마일스톤
|
|
42
|
+
- 라운드: 60 (1.9.70 → 1.9.130)
|
|
43
|
+
- MCP 도구: 11 → **40 🎉** (1.9.128 마일스톤)
|
|
44
|
+
- --json 명령: 6 → **19**
|
|
45
|
+
- handoff 자동 회수: 1 → **7**
|
|
46
|
+
- Memory Surface: WRITE 5 + READ 5 + DELETE 5 + RESTORE 3 + Archive 3
|
|
47
|
+
- stress 시나리오: v16 → v74 (58 추가)
|
|
48
|
+
- e2e: 안정 219/219
|
|
49
|
+
|
|
50
|
+
상세: `_reports/AUTONOMOUS_ROUNDS_60_MILESTONE.md` (비공개)
|
|
51
|
+
|
|
3
52
|
## 1.9.129 — 2026-05-20
|
|
4
53
|
|
|
5
54
|
**handoff 7번째 자동 회수 — 🗑 최근 24h archive 알림** — DELETE 5종 archive 활동을 매 세션 시작 시 자동 노출.
|
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,7 +12,7 @@
|
|
|
12
12
|
║ ██║ ██╔══╝ ██╔══╝ ██╔══██╗██║╚██╗██║██╔══╝ ╚════██║ ║
|
|
13
13
|
║ ███████╗███████╗███████╗██║ ██║██║ ╚████║███████╗███████║ ║
|
|
14
14
|
║ ╚══════╝╚══════╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝╚══════╝╚══════╝ ║
|
|
15
|
-
║ v1.9.
|
|
15
|
+
║ v1.9.131 AI Agent Reliability Harness ║
|
|
16
16
|
║ verify · remember · orchestrate · audit · prevent drift ║
|
|
17
17
|
╚══════════════════════════════════════════════════════════════╝
|
|
18
18
|
```
|
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.
|
|
9
|
+
const VERSION = '1.9.131';
|
|
10
10
|
const MARK = '<!-- leerness:managed -->';
|
|
11
11
|
const README_START = '<!-- leerness:project-readme:start -->';
|
|
12
12
|
const README_END = '<!-- leerness:project-readme:end -->';
|
|
@@ -330,6 +330,8 @@ leerness audit . --fix # 누락 메타 자동 보강
|
|
|
330
330
|
- 1.9.127+ \`leerness memory archive list [--surface decisions|lessons|plan] [--json]\` + MCP **39 도구** (\`leerness_memory_archive_list\`) — DELETE 5종 archive 통합 조회 (복원 후보 회수).
|
|
331
331
|
- 1.9.128+ \`leerness memory restore <surface> <target>\` + MCP **40 도구 🎉** (\`leerness_memory_restore\`) — archive → active 복귀 (DELETE→RESTORE cycle 완성). **MCP 40 도구 마일스톤**.
|
|
332
332
|
- 1.9.129+ handoff **7번째 자동 회수** — \`🗑 최근 24h archive\` (D/L/P 카운트 + 복원 후보 안내). DELETE 활동 자동 인지.
|
|
333
|
+
- 1.9.130+ 🎉 **60 라운드 자율 모드 마일스톤** — JSON 4종 (handoff/memory status/session close/health) \`memorySurface.archive\` 필드 통합. MCP 40 / handoff auto-recovery 7 / DELETE-RESTORE cycle 완성.
|
|
334
|
+
- 1.9.131+ \`brainstorm\` 회수 범위에 3 archive 파일 (decisions/lessons/plan archive) 통합 — 과거 제거된 ideas 가 새 brainstorm 시 다시 후보로 노출. \`hits.archive\` 필드 + 복원 안내 라인.
|
|
333
335
|
|
|
334
336
|
---
|
|
335
337
|
|
|
@@ -1425,6 +1427,19 @@ function memoryStatusCmd(root, opts = {}) {
|
|
|
1425
1427
|
const lm = exists(lessonsPath(root)) ? read(lessonsPath(root)) : '';
|
|
1426
1428
|
const lessonHeaders = lm.match(/^### \d{4}-\d{2}-\d{2}[^\n]*/gm) || [];
|
|
1427
1429
|
const lessonsLatest = lessonHeaders.length ? (lm.split('\n').filter(l => /- Lesson:/.test(l)).pop() || '').replace(/^- Lesson:\s*/, '').slice(0, 100) : null;
|
|
1430
|
+
// 1.9.130: DELETE 5종 archive entry counts
|
|
1431
|
+
const archiveCounts = { decisions: 0, lessons: 0, plan: 0, total: 0 };
|
|
1432
|
+
try {
|
|
1433
|
+
const hd = path.join(root, '.harness');
|
|
1434
|
+
for (const [key, file] of [['decisions', 'decisions.archive.md'], ['lessons', 'lessons.archive.md'], ['plan', 'plan.archive.md']]) {
|
|
1435
|
+
const fp = path.join(hd, file);
|
|
1436
|
+
if (exists(fp)) {
|
|
1437
|
+
const entries = _parseArchiveBlocks(read(fp));
|
|
1438
|
+
archiveCounts[key] = entries.length;
|
|
1439
|
+
archiveCounts.total += entries.length;
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
} catch {}
|
|
1428
1443
|
|
|
1429
1444
|
const payload = {
|
|
1430
1445
|
version: VERSION,
|
|
@@ -1434,6 +1449,7 @@ function memoryStatusCmd(root, opts = {}) {
|
|
|
1434
1449
|
rules: { active: rulesActive, paused: rulesPaused, total: rules.length },
|
|
1435
1450
|
plan: { milestones, inProgress: planInProgress },
|
|
1436
1451
|
lessons: { count: lessonHeaders.length, latest: lessonsLatest },
|
|
1452
|
+
archive: archiveCounts, // 1.9.130
|
|
1437
1453
|
summary: `T${tasksInProgress}/D${decisionHeaders.length}/R${rulesActive}/P${milestones}/L${lessonHeaders.length}`,
|
|
1438
1454
|
};
|
|
1439
1455
|
if (jsonMode) {
|
|
@@ -1450,6 +1466,7 @@ function memoryStatusCmd(root, opts = {}) {
|
|
|
1450
1466
|
log(`🗺 Plan: ${milestones} milestones (${planInProgress} in-progress)`);
|
|
1451
1467
|
log(`💡 Lessons: ${lessonHeaders.length} entries`);
|
|
1452
1468
|
if (lessonsLatest) log(` - 최근: ${lessonsLatest}`);
|
|
1469
|
+
if (archiveCounts.total > 0) log(`🗑 Archive: D${archiveCounts.decisions}/L${archiveCounts.lessons}/P${archiveCounts.plan} (${archiveCounts.total}건)`);
|
|
1453
1470
|
log(`\n📊 Summary: ${payload.summary}`);
|
|
1454
1471
|
}
|
|
1455
1472
|
|
|
@@ -2484,12 +2501,26 @@ function handoff(root) {
|
|
|
2484
2501
|
const milestones = (planText.match(/^### M-\d{4}\./gm) || []).length;
|
|
2485
2502
|
const lm = exists(lessonsPath(root)) ? read(lessonsPath(root)) : '';
|
|
2486
2503
|
const lessonsCount = (lm.match(/^### \d{4}-\d{2}-\d{2}[^\n]*/gm) || []).length;
|
|
2504
|
+
// 1.9.130: archive 카운트 통합
|
|
2505
|
+
const archiveCountsH = { decisions: 0, lessons: 0, plan: 0, total: 0 };
|
|
2506
|
+
try {
|
|
2507
|
+
const hdH = path.join(root, '.harness');
|
|
2508
|
+
for (const [key, file] of [['decisions', 'decisions.archive.md'], ['lessons', 'lessons.archive.md'], ['plan', 'plan.archive.md']]) {
|
|
2509
|
+
const fpH = path.join(hdH, file);
|
|
2510
|
+
if (exists(fpH)) {
|
|
2511
|
+
const entries = _parseArchiveBlocks(read(fpH));
|
|
2512
|
+
archiveCountsH[key] = entries.length;
|
|
2513
|
+
archiveCountsH.total += entries.length;
|
|
2514
|
+
}
|
|
2515
|
+
}
|
|
2516
|
+
} catch {}
|
|
2487
2517
|
result.memorySurface = {
|
|
2488
2518
|
tasks: { inProgress: tasksInProgress, total: rows.length, byStatus: tasksByStatus },
|
|
2489
2519
|
decisions: { count: decisionsCount },
|
|
2490
2520
|
rules: { active: rulesActive, total: rules.length },
|
|
2491
2521
|
plan: { milestones },
|
|
2492
2522
|
lessons: { count: lessonsCount },
|
|
2523
|
+
archive: archiveCountsH, // 1.9.130
|
|
2493
2524
|
summary: `T${tasksInProgress}/D${decisionsCount}/R${rulesActive}/P${milestones}/L${lessonsCount}`,
|
|
2494
2525
|
};
|
|
2495
2526
|
} catch {}
|
|
@@ -4238,7 +4269,7 @@ function _banner(opts = {}) {
|
|
|
4238
4269
|
lines.push('');
|
|
4239
4270
|
for (const ln of lines) log(ln);
|
|
4240
4271
|
if (opts.quickStart) {
|
|
4241
|
-
log(C.bold(C.cyan(' ✨ 빠른 시작 (1.9.
|
|
4272
|
+
log(C.bold(C.cyan(' ✨ 빠른 시작 (1.9.131+ brainstorm + archive 통합 — 61 라운드 자율 누적)')));
|
|
4242
4273
|
log(' ' + C.green('npx leerness@latest init .') + C.dim(' # 신규 프로젝트 + 외부 AI CLI 설정'));
|
|
4243
4274
|
log(' ' + C.green('npx leerness handoff .') + C.dim(' # 컨텍스트 + lessons + 매칭 skill + history hit + brainstorm hits + 헤드라인'));
|
|
4244
4275
|
log(' ' + C.green('npx leerness handoff . --quiet') + C.dim(' # 자동화/CI 모드 (1.9.99) — 자동 회수 라인 비활성'));
|
|
@@ -5160,12 +5191,26 @@ function sessionClose(root, opts = {}) {
|
|
|
5160
5191
|
const milestones0 = (planText0.match(/^### M-\d{4}\./gm) || []).length;
|
|
5161
5192
|
const lm0 = exists(lessonsPath(root)) ? read(lessonsPath(root)) : '';
|
|
5162
5193
|
const lessonsCount0 = (lm0.match(/^### \d{4}-\d{2}-\d{2}[^\n]*/gm) || []).length;
|
|
5194
|
+
// 1.9.130: archive 카운트 통합
|
|
5195
|
+
const archiveCountsS = { decisions: 0, lessons: 0, plan: 0, total: 0 };
|
|
5196
|
+
try {
|
|
5197
|
+
const hdS = path.join(root, '.harness');
|
|
5198
|
+
for (const [key, file] of [['decisions', 'decisions.archive.md'], ['lessons', 'lessons.archive.md'], ['plan', 'plan.archive.md']]) {
|
|
5199
|
+
const fpS = path.join(hdS, file);
|
|
5200
|
+
if (exists(fpS)) {
|
|
5201
|
+
const entries = _parseArchiveBlocks(read(fpS));
|
|
5202
|
+
archiveCountsS[key] = entries.length;
|
|
5203
|
+
archiveCountsS.total += entries.length;
|
|
5204
|
+
}
|
|
5205
|
+
}
|
|
5206
|
+
} catch {}
|
|
5163
5207
|
jsonResult.memorySurface = {
|
|
5164
5208
|
tasks: { inProgress: tasksInProgress0, total: rows0.length, byStatus: tasksByStatus0 },
|
|
5165
5209
|
decisions: { count: decisionsCount0 },
|
|
5166
5210
|
rules: { active: rulesActive0, total: rules0.length },
|
|
5167
5211
|
plan: { milestones: milestones0 },
|
|
5168
5212
|
lessons: { count: lessonsCount0 },
|
|
5213
|
+
archive: archiveCountsS, // 1.9.130
|
|
5169
5214
|
summary: `T${tasksInProgress0}/D${decisionsCount0}/R${rulesActive0}/P${milestones0}/L${lessonsCount0}`,
|
|
5170
5215
|
};
|
|
5171
5216
|
} catch {}
|
|
@@ -5669,6 +5714,36 @@ function _brainstormFor(root, topic) {
|
|
|
5669
5714
|
}
|
|
5670
5715
|
}
|
|
5671
5716
|
}
|
|
5717
|
+
// 1.9.131: 3 archive 파일 (decisions/lessons/plan) hits — DELETE 5종 archive 도 brainstorm 후보로
|
|
5718
|
+
// archived ideas 가 새 brainstorm 시점에 다시 후보로 노출 → "이전에 검토한 건데 다시 볼까?"
|
|
5719
|
+
hits.archive = { decisions: [], lessons: [], plan: [] };
|
|
5720
|
+
const archiveSources_bsFor = [
|
|
5721
|
+
{ key: 'decisions', file: 'decisions.archive.md' },
|
|
5722
|
+
{ key: 'lessons', file: 'lessons.archive.md' },
|
|
5723
|
+
{ key: 'plan', file: 'plan.archive.md' }
|
|
5724
|
+
];
|
|
5725
|
+
for (const src of archiveSources_bsFor) {
|
|
5726
|
+
const fp = path.join(root, '.harness', src.file);
|
|
5727
|
+
if (!exists(fp)) continue;
|
|
5728
|
+
const txt = read(fp);
|
|
5729
|
+
const blocks = txt.split(/\n(?=## 제거 )/);
|
|
5730
|
+
for (const b of blocks) {
|
|
5731
|
+
const m = b.match(/^## 제거 (\d{4}-\d{2}-\d{2})\s*\(target:\s*"([^"]*)"\)/);
|
|
5732
|
+
if (!m) continue;
|
|
5733
|
+
if (matches(b)) {
|
|
5734
|
+
const headerMatch = b.match(/^### (.+)$/m);
|
|
5735
|
+
const idx = txt.indexOf(b);
|
|
5736
|
+
const lineNo = idx >= 0 ? txt.slice(0, idx).split('\n').length : 0;
|
|
5737
|
+
hits.archive[src.key].push({
|
|
5738
|
+
date: m[1],
|
|
5739
|
+
target: m[2],
|
|
5740
|
+
originalHeader: headerMatch ? headerMatch[1].trim() : null,
|
|
5741
|
+
preview: b.slice(0, 220).replace(/\n+/g, ' '),
|
|
5742
|
+
line: lineNo
|
|
5743
|
+
});
|
|
5744
|
+
}
|
|
5745
|
+
}
|
|
5746
|
+
}
|
|
5672
5747
|
// 1.9.25: --include-code 옵션 — 소스 본문 검색 추가 (모순 감지 핵심)
|
|
5673
5748
|
if (has('--include-code')) {
|
|
5674
5749
|
const codeDirs = ['src', 'tests', 'bin', 'lib'];
|
|
@@ -5864,6 +5939,35 @@ function brainstormCmd(root, topic) {
|
|
|
5864
5939
|
}
|
|
5865
5940
|
}
|
|
5866
5941
|
}
|
|
5942
|
+
// 1.9.131: 3 archive 파일 hits (brainstormCmd 변종) — DELETE 5종 archive 도 brainstorm 후보로
|
|
5943
|
+
if (!hits.archive) hits.archive = { decisions: [], lessons: [], plan: [] };
|
|
5944
|
+
const archiveSources_bsCmd = [
|
|
5945
|
+
{ key: 'decisions', file: 'decisions.archive.md' },
|
|
5946
|
+
{ key: 'lessons', file: 'lessons.archive.md' },
|
|
5947
|
+
{ key: 'plan', file: 'plan.archive.md' }
|
|
5948
|
+
];
|
|
5949
|
+
for (const src of archiveSources_bsCmd) {
|
|
5950
|
+
const fp = path.join(root, '.harness', src.file);
|
|
5951
|
+
if (!exists(fp)) continue;
|
|
5952
|
+
const txt = read(fp);
|
|
5953
|
+
const blocks = txt.split(/\n(?=## 제거 )/);
|
|
5954
|
+
for (const b of blocks) {
|
|
5955
|
+
const m = b.match(/^## 제거 (\d{4}-\d{2}-\d{2})\s*\(target:\s*"([^"]*)"\)/);
|
|
5956
|
+
if (!m) continue;
|
|
5957
|
+
if (matches(b)) {
|
|
5958
|
+
const headerMatch = b.match(/^### (.+)$/m);
|
|
5959
|
+
const idx = txt.indexOf(b);
|
|
5960
|
+
const lineNo = idx >= 0 ? txt.slice(0, idx).split('\n').length : 0;
|
|
5961
|
+
hits.archive[src.key].push({
|
|
5962
|
+
date: m[1],
|
|
5963
|
+
target: m[2],
|
|
5964
|
+
originalHeader: headerMatch ? headerMatch[1].trim() : null,
|
|
5965
|
+
preview: b.slice(0, 220).replace(/\n+/g, ' '),
|
|
5966
|
+
line: lineNo
|
|
5967
|
+
});
|
|
5968
|
+
}
|
|
5969
|
+
}
|
|
5970
|
+
}
|
|
5867
5971
|
// 1.9.116: lessons.md + plan.md milestone hits (Memory Surface 5종 완전 통합)
|
|
5868
5972
|
if (!hits.lessonsExplicit) hits.lessonsExplicit = [];
|
|
5869
5973
|
if (!hits.planMilestones) hits.planMilestones = [];
|
|
@@ -5932,6 +6036,22 @@ function brainstormCmd(root, topic) {
|
|
|
5932
6036
|
log(`\n## 📜 task-log 실패 라인 (${hits.taskLogFails.length}) — 1.9.67 인덱스 + brainstorm`);
|
|
5933
6037
|
hits.taskLogFails.slice(0, 5).forEach(t => log(` - .harness/task-log.md:${t.line || '?'} — ${t.title}`));
|
|
5934
6038
|
}
|
|
6039
|
+
// 1.9.131: 3 archive 파일 hits — DELETE 5종 archive 도 brainstorm 후보
|
|
6040
|
+
if (hits.archive) {
|
|
6041
|
+
const archiveTotal = (hits.archive.decisions?.length || 0) + (hits.archive.lessons?.length || 0) + (hits.archive.plan?.length || 0);
|
|
6042
|
+
if (archiveTotal > 0) {
|
|
6043
|
+
log(`\n## 🗑 archive 후보 (${archiveTotal}) — 과거에 제거됐던 ideas; 복원 검토 가능 (1.9.131)`);
|
|
6044
|
+
for (const [key, label, emoji] of [['decisions', 'decisions.archive.md', '🧠'], ['lessons', 'lessons.archive.md', '💡'], ['plan', 'plan.archive.md', '🗺']]) {
|
|
6045
|
+
const items = hits.archive[key] || [];
|
|
6046
|
+
if (items.length) {
|
|
6047
|
+
for (const a of items.slice(0, 3)) {
|
|
6048
|
+
log(` - ${emoji} .harness/${label}:${a.line || '?'} — ${a.date} "${a.target}"${a.originalHeader ? ' (orig: ' + a.originalHeader.slice(0, 80) + ')' : ''}`);
|
|
6049
|
+
}
|
|
6050
|
+
}
|
|
6051
|
+
}
|
|
6052
|
+
log(` → 복원: leerness memory restore <decisions|lessons|plan> <target>`);
|
|
6053
|
+
}
|
|
6054
|
+
}
|
|
5935
6055
|
|
|
5936
6056
|
log(`\n## 💡 시작 전 권장 액션`);
|
|
5937
6057
|
log(` 1. 위 자원을 모두 검토 후 plan add 또는 task add로 새 작업 등록`);
|
|
@@ -8777,6 +8897,22 @@ function healthCmd(root) {
|
|
|
8777
8897
|
rules: { active: rulesActive, total: rules.length },
|
|
8778
8898
|
plan: { milestones },
|
|
8779
8899
|
lessons: { count: lessonsCount },
|
|
8900
|
+
archive: (() => {
|
|
8901
|
+
// 1.9.130: archive 카운트 통합
|
|
8902
|
+
const a = { decisions: 0, lessons: 0, plan: 0, total: 0 };
|
|
8903
|
+
try {
|
|
8904
|
+
const hdHe = path.join(root, '.harness');
|
|
8905
|
+
for (const [key, file] of [['decisions', 'decisions.archive.md'], ['lessons', 'lessons.archive.md'], ['plan', 'plan.archive.md']]) {
|
|
8906
|
+
const fpHe = path.join(hdHe, file);
|
|
8907
|
+
if (exists(fpHe)) {
|
|
8908
|
+
const entries = _parseArchiveBlocks(read(fpHe));
|
|
8909
|
+
a[key] = entries.length;
|
|
8910
|
+
a.total += entries.length;
|
|
8911
|
+
}
|
|
8912
|
+
}
|
|
8913
|
+
} catch {}
|
|
8914
|
+
return a;
|
|
8915
|
+
})(),
|
|
8780
8916
|
summary: `T${tasksInProgress}/D${decisionsCount}/R${rulesActive}/P${milestones}/L${lessonsCount}`,
|
|
8781
8917
|
};
|
|
8782
8918
|
} catch { out.memorySurface = { error: 'memorySurface 점검 실패' }; }
|