leerness 1.9.126 → 1.9.128
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 +50 -0
- package/README.md +2 -2
- package/bin/harness.js +160 -6
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,55 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.9.128 — 2026-05-20
|
|
4
|
+
|
|
5
|
+
**`leerness memory restore` CLI + MCP 40번째 도구 `leerness_memory_restore`** 🎉 **MCP 40 도구 마일스톤** — DELETE→RESTORE cycle 완성.
|
|
6
|
+
|
|
7
|
+
### Added — `leerness memory restore <surface> <target>` CLI
|
|
8
|
+
- surface: `decisions` | `lessons` | `plan`
|
|
9
|
+
- target: date YYYY-MM-DD 또는 substring 매칭
|
|
10
|
+
- archive 의 매칭 블록을 active 파일 끝에 복귀
|
|
11
|
+
- archive 에서 제거 (또는 다 비면 헤더만 남김)
|
|
12
|
+
- 여러 매칭 동시 복원 가능
|
|
13
|
+
- Invalid surface / 미매칭 → fail
|
|
14
|
+
|
|
15
|
+
### Added — MCP 40번째 도구 `leerness_memory_restore` 🎉
|
|
16
|
+
- 외부 AI 가 직접 archive 복원
|
|
17
|
+
- 인자: `{ surface (required), target (required), path? }`
|
|
18
|
+
|
|
19
|
+
### DELETE → RESTORE cycle 완성
|
|
20
|
+
1. `decision drop "PostgreSQL"` → 제거 + archive
|
|
21
|
+
2. `memory archive list --surface decisions` → 후보 회수
|
|
22
|
+
3. `memory restore decisions "PostgreSQL"` → 복원
|
|
23
|
+
|
|
24
|
+
### 사용 시나리오
|
|
25
|
+
사용자: "어제 잘못 취소한 PostgreSQL 결정 다시 살려줘"
|
|
26
|
+
→ 외부 AI: `leerness_memory_restore({ surface: "decisions", target: "PostgreSQL" })` — archive에서 active로 복귀
|
|
27
|
+
|
|
28
|
+
### MCP 도구 누계: 40 🎉 (1.9.127: 39 + leerness_memory_restore — MCP 40 마일스톤)
|
|
29
|
+
|
|
30
|
+
## 1.9.127 — 2026-05-20
|
|
31
|
+
|
|
32
|
+
**`leerness memory archive list` CLI + MCP 39번째 도구 `leerness_memory_archive_list`** — DELETE 5종 archive 통합 조회 (decisions/lessons/plan).
|
|
33
|
+
|
|
34
|
+
### Added — `leerness memory archive list` CLI
|
|
35
|
+
- DELETE 5종 archive 파일 (`.harness/decisions.archive.md`, `lessons.archive.md`, `plan.archive.md`) 통합 조회
|
|
36
|
+
- 각 archive entry 파싱: `{ date, target, originalHeader }`
|
|
37
|
+
- `--surface decisions|lessons|plan` 필터 지원
|
|
38
|
+
- `--json` 옵션 — totals + 각 surface 별 entries
|
|
39
|
+
- archive 파일 없으면 안내 메시지
|
|
40
|
+
|
|
41
|
+
### Added — MCP 39번째 도구 `leerness_memory_archive_list`
|
|
42
|
+
- 외부 AI 가 과거에 제거된 항목 회수 — 복원 후보 참조 / 의사결정 변경 흐름 추적.
|
|
43
|
+
- 인자: `{ surface?, path? }` (surface optional)
|
|
44
|
+
|
|
45
|
+
### 사용 시나리오
|
|
46
|
+
1. **복원 후보 회수**: "이전에 PostgreSQL 채택 결정 취소했었지? 어떤 게 있었나"
|
|
47
|
+
→ 외부 AI: `leerness_memory_archive_list({ surface: "decisions" })` — 모든 제거된 결정 회수
|
|
48
|
+
2. **의사결정 변경 패턴 분석**: 자주 변경되는 surface 의 빈도 추적
|
|
49
|
+
3. **복구**: archive entry 참조 후 다시 `decision add` 로 재등록
|
|
50
|
+
|
|
51
|
+
### MCP 도구 누계: 39 (1.9.126: 38 + leerness_memory_archive_list)
|
|
52
|
+
|
|
3
53
|
## 1.9.126 — 2026-05-20
|
|
4
54
|
|
|
5
55
|
**`leerness plan remove <target>` CLI + MCP 38번째 도구 `leerness_plan_remove`** — milestone 블록 영구 제거 (archive 자동 보존). **Memory Surface DELETE 5종 완전 완성** 🎉
|
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.128 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.128';
|
|
10
10
|
const MARK = '<!-- leerness:managed -->';
|
|
11
11
|
const README_START = '<!-- leerness:project-readme:start -->';
|
|
12
12
|
const README_END = '<!-- leerness:project-readme:end -->';
|
|
@@ -113,7 +113,7 @@ function arg(name, def = null) { const i = process.argv.indexOf(name); return i
|
|
|
113
113
|
function has(name) { return process.argv.includes(name); }
|
|
114
114
|
function nonFlagArgs() {
|
|
115
115
|
const out = [];
|
|
116
|
-
const withValue = new Set(['--language','--skills','--path','--status','--progress','--goal','--reason','--next','--target','--token-env','--package','--out','--from','--repo','--id','--note','--evidence','--query','--limit','--action','--agent','--tool','--doc','--command','--capability','--before','--after','--display','--threshold','--trigger','--check','--set','--min-score','--include','--days','--gh-pages-src','--roadmap','--since','--agents','--model','--timeout','--retry-on-fail','--label','--score','--tokens','--alternatives','--impact','--tag']);
|
|
116
|
+
const withValue = new Set(['--language','--skills','--path','--status','--progress','--goal','--reason','--next','--target','--token-env','--package','--out','--from','--repo','--id','--note','--evidence','--query','--limit','--action','--agent','--tool','--doc','--command','--capability','--before','--after','--display','--threshold','--trigger','--check','--set','--min-score','--include','--days','--gh-pages-src','--roadmap','--since','--agents','--model','--timeout','--retry-on-fail','--label','--score','--tokens','--alternatives','--impact','--tag','--surface']);
|
|
117
117
|
const a = process.argv.slice(2);
|
|
118
118
|
for (let i = 0; i < a.length; i++) {
|
|
119
119
|
const x = a[i];
|
|
@@ -327,6 +327,8 @@ leerness audit . --fix # 누락 메타 자동 보강
|
|
|
327
327
|
- 1.9.124+ \`leerness lesson drop <target>\` + MCP **36 도구** (\`leerness_lesson_drop\`) — 잘못 저장한 lesson 제거 (archive 자동 보존).
|
|
328
328
|
- 1.9.125+ \`leerness decision drop <target>\` + MCP **37 도구** (\`leerness_decision_drop\`) — 잘못 저장한 결정 제거 (archive 보존).
|
|
329
329
|
- 1.9.126+ \`leerness plan remove <M-XXXX|title>\` + MCP **38 도구** (\`leerness_plan_remove\`) — milestone 영구 제거 (archive 보존). **Memory Surface DELETE 5종 완전 완성** 🎉.
|
|
330
|
+
- 1.9.127+ \`leerness memory archive list [--surface decisions|lessons|plan] [--json]\` + MCP **39 도구** (\`leerness_memory_archive_list\`) — DELETE 5종 archive 통합 조회 (복원 후보 회수).
|
|
331
|
+
- 1.9.128+ \`leerness memory restore <surface> <target>\` + MCP **40 도구 🎉** (\`leerness_memory_restore\`) — archive → active 복귀 (DELETE→RESTORE cycle 완성). **MCP 40 도구 마일스톤**.
|
|
330
332
|
|
|
331
333
|
---
|
|
332
334
|
|
|
@@ -1450,6 +1452,143 @@ function memoryStatusCmd(root, opts = {}) {
|
|
|
1450
1452
|
log(`\n📊 Summary: ${payload.summary}`);
|
|
1451
1453
|
}
|
|
1452
1454
|
|
|
1455
|
+
// 1.9.127: memory archive list — DELETE 5종 archive 파일 통합 조회
|
|
1456
|
+
// .harness/decisions.archive.md / lessons.archive.md / plan.archive.md 의 "## 제거 YYYY-MM-DD" 블록 파싱
|
|
1457
|
+
// --surface decisions|lessons|plan 필터, --json 옵션
|
|
1458
|
+
function _parseArchiveBlocks(text) {
|
|
1459
|
+
// archive 형식: "## 제거 YYYY-MM-DD (target: \"...\")\n<원래 블록>"
|
|
1460
|
+
// 첫 번째 "## 제거" 이전의 헤더(# Plan archive 등)는 skip
|
|
1461
|
+
const entries = [];
|
|
1462
|
+
if (!text) return entries;
|
|
1463
|
+
const blocks = text.split(/\n(?=## 제거 )/);
|
|
1464
|
+
for (const b of blocks) {
|
|
1465
|
+
const m = b.match(/^## 제거 (\d{4}-\d{2}-\d{2})\s*\(target:\s*"([^"]*)"\)/);
|
|
1466
|
+
if (!m) continue;
|
|
1467
|
+
const date = m[1];
|
|
1468
|
+
const target = m[2];
|
|
1469
|
+
// 원래 헤더 추출 (### …)
|
|
1470
|
+
const headerMatch = b.match(/^### (.+)$/m);
|
|
1471
|
+
const originalHeader = headerMatch ? headerMatch[1].trim() : null;
|
|
1472
|
+
entries.push({ date, target, originalHeader });
|
|
1473
|
+
}
|
|
1474
|
+
return entries;
|
|
1475
|
+
}
|
|
1476
|
+
function memoryArchiveListCmd(root, opts = {}) {
|
|
1477
|
+
root = absRoot(root);
|
|
1478
|
+
const jsonMode = !!opts.json || has('--json');
|
|
1479
|
+
const surfaceFilter = arg('--surface', '');
|
|
1480
|
+
const hd = path.join(root, '.harness');
|
|
1481
|
+
const archives = {
|
|
1482
|
+
decisions: { path: path.join(hd, 'decisions.archive.md'), entries: [] },
|
|
1483
|
+
lessons: { path: path.join(hd, 'lessons.archive.md'), entries: [] },
|
|
1484
|
+
plan: { path: path.join(hd, 'plan.archive.md'), entries: [] }
|
|
1485
|
+
};
|
|
1486
|
+
for (const k of Object.keys(archives)) {
|
|
1487
|
+
if (surfaceFilter && surfaceFilter !== k) continue;
|
|
1488
|
+
const a = archives[k];
|
|
1489
|
+
if (exists(a.path)) a.entries = _parseArchiveBlocks(read(a.path));
|
|
1490
|
+
}
|
|
1491
|
+
const totals = {
|
|
1492
|
+
decisions: archives.decisions.entries.length,
|
|
1493
|
+
lessons: archives.lessons.entries.length,
|
|
1494
|
+
plan: archives.plan.entries.length
|
|
1495
|
+
};
|
|
1496
|
+
totals.all = totals.decisions + totals.lessons + totals.plan;
|
|
1497
|
+
if (jsonMode) {
|
|
1498
|
+
const payload = {
|
|
1499
|
+
version: VERSION, root,
|
|
1500
|
+
decisions: archives.decisions.entries,
|
|
1501
|
+
lessons: archives.lessons.entries,
|
|
1502
|
+
plan: archives.plan.entries,
|
|
1503
|
+
totals
|
|
1504
|
+
};
|
|
1505
|
+
process.stdout.write(JSON.stringify(payload, null, 2) + '\n');
|
|
1506
|
+
return;
|
|
1507
|
+
}
|
|
1508
|
+
log('# 🗑 Memory Archive List (1.9.127)\n');
|
|
1509
|
+
if (totals.all === 0) {
|
|
1510
|
+
log('(archive 파일 없음 — 아직 제거된 항목 없음)');
|
|
1511
|
+
return;
|
|
1512
|
+
}
|
|
1513
|
+
if ((!surfaceFilter || surfaceFilter === 'decisions') && archives.decisions.entries.length) {
|
|
1514
|
+
log(`🧠 Decisions archive: ${archives.decisions.entries.length} entries`);
|
|
1515
|
+
for (const e of archives.decisions.entries) {
|
|
1516
|
+
log(` - ${e.date} — target: "${e.target}"${e.originalHeader ? ' — ' + e.originalHeader : ''}`);
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
if ((!surfaceFilter || surfaceFilter === 'lessons') && archives.lessons.entries.length) {
|
|
1520
|
+
log(`💡 Lessons archive: ${archives.lessons.entries.length} entries`);
|
|
1521
|
+
for (const e of archives.lessons.entries) {
|
|
1522
|
+
log(` - ${e.date} — target: "${e.target}"${e.originalHeader ? ' — ' + e.originalHeader : ''}`);
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
if ((!surfaceFilter || surfaceFilter === 'plan') && archives.plan.entries.length) {
|
|
1526
|
+
log(`🗺 Plan archive: ${archives.plan.entries.length} entries`);
|
|
1527
|
+
for (const e of archives.plan.entries) {
|
|
1528
|
+
log(` - ${e.date} — target: "${e.target}"${e.originalHeader ? ' — ' + e.originalHeader : ''}`);
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
log(`\n📊 Total archived: ${totals.all} entries (D${totals.decisions}/L${totals.lessons}/P${totals.plan})`);
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
// 1.9.128: memory restore — archive 의 블록을 active 파일로 복귀 (DELETE→RESTORE cycle 완성)
|
|
1535
|
+
// surface: decisions|lessons|plan
|
|
1536
|
+
// target: date (YYYY-MM-DD) 또는 target substring 매칭
|
|
1537
|
+
// 매칭 archive 블록을 active 파일 끝에 추가 + archive 에서 제거
|
|
1538
|
+
function memoryRestoreCmd(root, surface, target) {
|
|
1539
|
+
root = absRoot(root);
|
|
1540
|
+
if (!surface || !['decisions', 'lessons', 'plan'].includes(surface)) {
|
|
1541
|
+
return fail('memory restore <decisions|lessons|plan> <target> 필요 (target: date YYYY-MM-DD 또는 substring)');
|
|
1542
|
+
}
|
|
1543
|
+
if (!target) return fail('memory restore <surface> <target> — target 누락');
|
|
1544
|
+
const hd = path.join(root, '.harness');
|
|
1545
|
+
const archivePath = path.join(hd, `${surface}.archive.md`);
|
|
1546
|
+
if (!exists(archivePath)) return fail(`${surface}.archive.md 없음 — 복원할 항목 없음`);
|
|
1547
|
+
const text = read(archivePath);
|
|
1548
|
+
// archive 헤더 (# X archive) 와 본문 분리
|
|
1549
|
+
const headerMatch = text.match(/^(# [^\n]*\n+)([\s\S]*)$/);
|
|
1550
|
+
const archiveHeader = headerMatch ? headerMatch[1] : '';
|
|
1551
|
+
const body = headerMatch ? headerMatch[2] : text;
|
|
1552
|
+
// body 를 "## 제거 " 단위로 split
|
|
1553
|
+
const blocks = body.split(/\n(?=## 제거 )/);
|
|
1554
|
+
const kept = [];
|
|
1555
|
+
const restoredBlocks = [];
|
|
1556
|
+
for (const b of blocks) {
|
|
1557
|
+
if (!b.trim()) continue;
|
|
1558
|
+
const m = b.match(/^## 제거 (\d{4}-\d{2}-\d{2})\s*\(target:\s*"([^"]*)"\)/);
|
|
1559
|
+
if (!m) { kept.push(b); continue; }
|
|
1560
|
+
const date = m[1];
|
|
1561
|
+
const blockTarget = m[2];
|
|
1562
|
+
const isDateTarget = date === target;
|
|
1563
|
+
const isSubstring = blockTarget.includes(target);
|
|
1564
|
+
if (isDateTarget || isSubstring) {
|
|
1565
|
+
// archive 블록에서 원래 active 블록만 추출 (## 제거 ... 한 줄 + 다음 line부터)
|
|
1566
|
+
const content = b.replace(/^## 제거 [^\n]*\n+/, '');
|
|
1567
|
+
if (content.trim()) restoredBlocks.push(content.trim());
|
|
1568
|
+
} else {
|
|
1569
|
+
kept.push(b);
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
if (restoredBlocks.length === 0) return fail(`매칭 archive entry 없음: surface=${surface}, target="${target}"`);
|
|
1573
|
+
// active 파일 경로
|
|
1574
|
+
const activePath = surface === 'decisions' ? decisionsPath(root)
|
|
1575
|
+
: surface === 'lessons' ? lessonsPath(root)
|
|
1576
|
+
: planPath(root);
|
|
1577
|
+
// active 파일에 복귀 (헤더 보존)
|
|
1578
|
+
for (const blk of restoredBlocks) {
|
|
1579
|
+
append(activePath, '\n' + blk + '\n');
|
|
1580
|
+
}
|
|
1581
|
+
// archive 재작성 — 모두 제거되면 파일 비움 (헤더만 남김 또는 삭제)
|
|
1582
|
+
if (kept.length === 0) {
|
|
1583
|
+
// archive 헤더만 남겨도 의미 있음 — 향후 다시 사용 가능
|
|
1584
|
+
writeUtf8(archivePath, archiveHeader);
|
|
1585
|
+
} else {
|
|
1586
|
+
writeUtf8(archivePath, archiveHeader + kept.join('\n'));
|
|
1587
|
+
}
|
|
1588
|
+
ok(`${surface} restored: ${restoredBlocks.length}건 (archive에서 active로 복귀)`);
|
|
1589
|
+
_autoRoadmap(absRoot(root), 'data-change');
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1453
1592
|
// 1.9.117: lesson list — lessons.md 의 모든 항목 조회 + --tag 필터 + --json
|
|
1454
1593
|
function lessonListCmd(root, opts = {}) {
|
|
1455
1594
|
root = absRoot(root);
|
|
@@ -4056,7 +4195,7 @@ function _banner(opts = {}) {
|
|
|
4056
4195
|
lines.push('');
|
|
4057
4196
|
for (const ln of lines) log(ln);
|
|
4058
4197
|
if (opts.quickStart) {
|
|
4059
|
-
log(C.bold(C.cyan(' ✨ 빠른 시작 (1.9.
|
|
4198
|
+
log(C.bold(C.cyan(' ✨ 빠른 시작 (1.9.128+ MCP 40 도구 🎉 DELETE→RESTORE cycle — 58 라운드 자율 누적)')));
|
|
4060
4199
|
log(' ' + C.green('npx leerness@latest init .') + C.dim(' # 신규 프로젝트 + 외부 AI CLI 설정'));
|
|
4061
4200
|
log(' ' + C.green('npx leerness handoff .') + C.dim(' # 컨텍스트 + lessons + 매칭 skill + history hit + brainstorm hits + 헤드라인'));
|
|
4062
4201
|
log(' ' + C.green('npx leerness handoff . --quiet') + C.dim(' # 자동화/CI 모드 (1.9.99) — 자동 회수 라인 비활성'));
|
|
@@ -4072,8 +4211,9 @@ function _banner(opts = {}) {
|
|
|
4072
4211
|
log(' ' + C.green('npx leerness session close .') + C.dim(' # 마감 + 다음 라운드 추천 (default)'));
|
|
4073
4212
|
log('');
|
|
4074
4213
|
log(C.bold(C.cyan(' 🤖 메인 에이전트 (Claude/Cursor/Copilot)용')));
|
|
4075
|
-
log(' ' + C.green('npx leerness mcp serve') + C.dim(' # MCP 서버 —
|
|
4076
|
-
log(' ' + C.green('npx leerness
|
|
4214
|
+
log(' ' + C.green('npx leerness mcp serve') + C.dim(' # MCP 서버 — 40 도구 🎉 (memory_restore 추가, 1.9.128)'));
|
|
4215
|
+
log(' ' + C.green('npx leerness memory archive list --json') + C.dim(' # DELETE 5종 archive 통합 조회 (1.9.127) — D/L/P entries + 복원 후보'));
|
|
4216
|
+
log(' ' + C.green('npx leerness memory restore <surface> <target>') + C.dim(' # archive → active 복원 (1.9.128) — DELETE→RESTORE cycle'));
|
|
4077
4217
|
log(' ' + C.green('npx leerness lesson save "<text>" --tag "..."') + C.dim(' # lessons.md 직접 write (1.9.112 — handoff 자동 회수와 통합)'));
|
|
4078
4218
|
log(' ' + C.green('npx leerness memory status . --json') + C.dim(' # Memory Surface 5종 통합 상태 JSON (1.9.114)'));
|
|
4079
4219
|
log(' ' + C.green('npx leerness decision add "<title>" --reason "..."') + C.dim(' # 설계 결정 영구화 (1.9.108) — handoff lessons 자동 회수와 통합'));
|
|
@@ -8255,7 +8395,9 @@ function mcpServeCmd(root) {
|
|
|
8255
8395
|
{ name: 'leerness_plan_list', description: '1.9.119 — plan.md 의 모든 milestone (M-XXXX) 조회 JSON ({ id, title, status, progress, tasks: [{ done, text }] }[]). 외부 AI가 영구화된 계획 + 진행률 + tasks checkbox 전체 회수', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
8256
8396
|
{ name: 'leerness_lesson_drop', description: '1.9.124 — lessons.md 에서 특정 lesson 제거 (target: date YYYY-MM-DD 또는 text substring). 잘못 저장한 lesson 제거. 제거된 블록은 .harness/lessons.archive.md 에 자동 보존 (복구 가능)', inputSchema: { type: 'object', properties: { target: { type: 'string' }, path: { type: 'string' } }, required: ['target'] } },
|
|
8257
8397
|
{ name: 'leerness_decision_drop', description: '1.9.125 — decisions.md 에서 특정 결정 제거 (target: date YYYY-MM-DD 또는 title substring). 제거된 블록은 .harness/decisions.archive.md 에 자동 보존', inputSchema: { type: 'object', properties: { target: { type: 'string' }, path: { type: 'string' } }, required: ['target'] } },
|
|
8258
|
-
{ name: 'leerness_plan_remove', description: '1.9.126 — plan.md 에서 특정 milestone 블록 (### M-XXXX) 제거 (target: M-XXXX 또는 title substring). 제거된 블록은 .harness/plan.archive.md 에 자동 보존. Memory Surface DELETE 5종 완전 완성', inputSchema: { type: 'object', properties: { target: { type: 'string' }, path: { type: 'string' } }, required: ['target'] } }
|
|
8398
|
+
{ name: 'leerness_plan_remove', description: '1.9.126 — plan.md 에서 특정 milestone 블록 (### M-XXXX) 제거 (target: M-XXXX 또는 title substring). 제거된 블록은 .harness/plan.archive.md 에 자동 보존. Memory Surface DELETE 5종 완전 완성', inputSchema: { type: 'object', properties: { target: { type: 'string' }, path: { type: 'string' } }, required: ['target'] } },
|
|
8399
|
+
{ name: 'leerness_memory_archive_list', description: '1.9.127 — DELETE 5종 archive 파일 통합 조회 JSON ({ decisions: [], lessons: [], plan: [], totals: { decisions, lessons, plan, all } }). 외부 AI가 과거에 제거된 항목을 회수/복원 후보로 참조. --surface 필터: decisions|lessons|plan', inputSchema: { type: 'object', properties: { surface: { type: 'string' }, path: { type: 'string' } } } },
|
|
8400
|
+
{ name: 'leerness_memory_restore', description: '1.9.128 — archive 의 항목을 active 파일로 복귀 (DELETE→RESTORE cycle). surface: decisions|lessons|plan. target: date YYYY-MM-DD 또는 target substring 매칭. 복원된 블록은 archive 에서 제거됨. 🎉 MCP 40 도구 마일스톤', inputSchema: { type: 'object', properties: { surface: { type: 'string', enum: ['decisions', 'lessons', 'plan'] }, target: { type: 'string' }, path: { type: 'string' } }, required: ['surface', 'target'] } }
|
|
8259
8401
|
];
|
|
8260
8402
|
|
|
8261
8403
|
function send(obj) {
|
|
@@ -8325,6 +8467,8 @@ function mcpServeCmd(root) {
|
|
|
8325
8467
|
case 'leerness_lesson_drop': cliArgs = ['lesson', 'drop', String(args.target || ''), '--path', targetPath]; break;
|
|
8326
8468
|
case 'leerness_decision_drop': cliArgs = ['decision', 'drop', String(args.target || ''), '--path', targetPath]; break;
|
|
8327
8469
|
case 'leerness_plan_remove': cliArgs = ['plan', 'remove', String(args.target || ''), '--path', targetPath]; break;
|
|
8470
|
+
case 'leerness_memory_archive_list': cliArgs = ['memory', 'archive', 'list', '--path', targetPath, '--json', ...(args.surface ? ['--surface', args.surface] : [])]; break;
|
|
8471
|
+
case 'leerness_memory_restore': cliArgs = ['memory', 'restore', String(args.surface || ''), String(args.target || ''), '--path', targetPath]; break;
|
|
8328
8472
|
default:
|
|
8329
8473
|
return send({ jsonrpc: '2.0', id, error: { code: -32601, message: `Unknown tool: ${name}` } });
|
|
8330
8474
|
}
|
|
@@ -9063,6 +9207,16 @@ async function main() {
|
|
|
9063
9207
|
const root = absRoot(arg('--path', args[2] && !args[2].startsWith('-') ? args[2] : process.cwd()));
|
|
9064
9208
|
return memoryStatusCmd(root, { json: has('--json') });
|
|
9065
9209
|
}
|
|
9210
|
+
// 1.9.127: memory archive list — DELETE 5종 archive 통합 조회
|
|
9211
|
+
if (cmd === 'memory' && args[1] === 'archive' && args[2] === 'list') {
|
|
9212
|
+
const root = absRoot(arg('--path', args[3] && !args[3].startsWith('-') ? args[3] : process.cwd()));
|
|
9213
|
+
return memoryArchiveListCmd(root, { json: has('--json') });
|
|
9214
|
+
}
|
|
9215
|
+
// 1.9.128: memory restore — archive 블록을 active 파일로 복귀 (DELETE→RESTORE cycle)
|
|
9216
|
+
if (cmd === 'memory' && args[1] === 'restore') {
|
|
9217
|
+
const root = absRoot(arg('--path', process.cwd()));
|
|
9218
|
+
return memoryRestoreCmd(root, args[2], args[3]);
|
|
9219
|
+
}
|
|
9066
9220
|
// 1.9.112: lesson save — lessons.md에 새 lesson 추가
|
|
9067
9221
|
// 1.9.117: lesson list — lessons.md 조회 + --tag 필터 + --json
|
|
9068
9222
|
if (cmd === 'lesson') {
|