leerness 1.9.127 → 1.9.129
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 +62 -0
- package/README.md +2 -2
- package/bin/harness.js +114 -5
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,67 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.9.129 — 2026-05-20
|
|
4
|
+
|
|
5
|
+
**handoff 7번째 자동 회수 — 🗑 최근 24h archive 알림** — DELETE 5종 archive 활동을 매 세션 시작 시 자동 노출.
|
|
6
|
+
|
|
7
|
+
### Added — handoff archive 알림
|
|
8
|
+
- handoff 출력에 archive 활동 라인 추가:
|
|
9
|
+
```
|
|
10
|
+
🗑 최근 24h archive (1.9.129): D2/L1/P0 (3건 archived) — 복원 후보
|
|
11
|
+
→ 회수: leerness memory archive list --json
|
|
12
|
+
→ 복원: leerness memory restore <surface> <target>
|
|
13
|
+
```
|
|
14
|
+
- 3 archive 파일 (`decisions.archive.md`, `lessons.archive.md`, `plan.archive.md`) 의 mtime 24h 내 + entry date 24h 내만 카운트
|
|
15
|
+
- `--no-mem-delta` / `--compact` / `--quiet` / `LEERNESS_NO_MEM_DELTA=1` 로 끄기
|
|
16
|
+
|
|
17
|
+
### 7개 handoff 자동 회수
|
|
18
|
+
| # | 라운드 | 자동 회수 |
|
|
19
|
+
|---|---|---|
|
|
20
|
+
| 1 | (기존) | lessons matching |
|
|
21
|
+
| 2 | 1.9.45 | skill match 추천 |
|
|
22
|
+
| 3 | 1.9.69 | history hit |
|
|
23
|
+
| 4 | 1.9.88 | brainstorm hits |
|
|
24
|
+
| 5 | 1.9.81 | 통합 헤드라인 |
|
|
25
|
+
| 6 | 1.9.121 | 🆕 24h 메모리 변동 |
|
|
26
|
+
| **7** | **1.9.129** | **🗑 24h archive 알림** |
|
|
27
|
+
|
|
28
|
+
### 사용 시나리오
|
|
29
|
+
세션 시작 시 handoff:
|
|
30
|
+
```
|
|
31
|
+
🆕 최근 24h 메모리 변동 (1.9.121): task +3 · decision +1 · plan: 변경됨
|
|
32
|
+
🗑 최근 24h archive (1.9.129): D1/L0/P1 (2건 archived) — 복원 후보
|
|
33
|
+
→ 회수: leerness memory archive list --json
|
|
34
|
+
→ 복원: leerness memory restore <surface> <target>
|
|
35
|
+
```
|
|
36
|
+
AI 가 즉시 "어제 PostgreSQL 결정 취소했었네 — 다시 검토해야 할까?" 판단 가능.
|
|
37
|
+
|
|
38
|
+
## 1.9.128 — 2026-05-20
|
|
39
|
+
|
|
40
|
+
**`leerness memory restore` CLI + MCP 40번째 도구 `leerness_memory_restore`** 🎉 **MCP 40 도구 마일스톤** — DELETE→RESTORE cycle 완성.
|
|
41
|
+
|
|
42
|
+
### Added — `leerness memory restore <surface> <target>` CLI
|
|
43
|
+
- surface: `decisions` | `lessons` | `plan`
|
|
44
|
+
- target: date YYYY-MM-DD 또는 substring 매칭
|
|
45
|
+
- archive 의 매칭 블록을 active 파일 끝에 복귀
|
|
46
|
+
- archive 에서 제거 (또는 다 비면 헤더만 남김)
|
|
47
|
+
- 여러 매칭 동시 복원 가능
|
|
48
|
+
- Invalid surface / 미매칭 → fail
|
|
49
|
+
|
|
50
|
+
### Added — MCP 40번째 도구 `leerness_memory_restore` 🎉
|
|
51
|
+
- 외부 AI 가 직접 archive 복원
|
|
52
|
+
- 인자: `{ surface (required), target (required), path? }`
|
|
53
|
+
|
|
54
|
+
### DELETE → RESTORE cycle 완성
|
|
55
|
+
1. `decision drop "PostgreSQL"` → 제거 + archive
|
|
56
|
+
2. `memory archive list --surface decisions` → 후보 회수
|
|
57
|
+
3. `memory restore decisions "PostgreSQL"` → 복원
|
|
58
|
+
|
|
59
|
+
### 사용 시나리오
|
|
60
|
+
사용자: "어제 잘못 취소한 PostgreSQL 결정 다시 살려줘"
|
|
61
|
+
→ 외부 AI: `leerness_memory_restore({ surface: "decisions", target: "PostgreSQL" })` — archive에서 active로 복귀
|
|
62
|
+
|
|
63
|
+
### MCP 도구 누계: 40 🎉 (1.9.127: 39 + leerness_memory_restore — MCP 40 마일스톤)
|
|
64
|
+
|
|
3
65
|
## 1.9.127 — 2026-05-20
|
|
4
66
|
|
|
5
67
|
**`leerness memory archive list` CLI + MCP 39번째 도구 `leerness_memory_archive_list`** — DELETE 5종 archive 통합 조회 (decisions/lessons/plan).
|
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.129 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.129';
|
|
10
10
|
const MARK = '<!-- leerness:managed -->';
|
|
11
11
|
const README_START = '<!-- leerness:project-readme:start -->';
|
|
12
12
|
const README_END = '<!-- leerness:project-readme:end -->';
|
|
@@ -328,6 +328,8 @@ leerness audit . --fix # 누락 메타 자동 보강
|
|
|
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
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 도구 마일스톤**.
|
|
332
|
+
- 1.9.129+ handoff **7번째 자동 회수** — \`🗑 최근 24h archive\` (D/L/P 카운트 + 복원 후보 안내). DELETE 활동 자동 인지.
|
|
331
333
|
|
|
332
334
|
---
|
|
333
335
|
|
|
@@ -1530,6 +1532,64 @@ function memoryArchiveListCmd(root, opts = {}) {
|
|
|
1530
1532
|
log(`\n📊 Total archived: ${totals.all} entries (D${totals.decisions}/L${totals.lessons}/P${totals.plan})`);
|
|
1531
1533
|
}
|
|
1532
1534
|
|
|
1535
|
+
// 1.9.128: memory restore — archive 의 블록을 active 파일로 복귀 (DELETE→RESTORE cycle 완성)
|
|
1536
|
+
// surface: decisions|lessons|plan
|
|
1537
|
+
// target: date (YYYY-MM-DD) 또는 target substring 매칭
|
|
1538
|
+
// 매칭 archive 블록을 active 파일 끝에 추가 + archive 에서 제거
|
|
1539
|
+
function memoryRestoreCmd(root, surface, target) {
|
|
1540
|
+
root = absRoot(root);
|
|
1541
|
+
if (!surface || !['decisions', 'lessons', 'plan'].includes(surface)) {
|
|
1542
|
+
return fail('memory restore <decisions|lessons|plan> <target> 필요 (target: date YYYY-MM-DD 또는 substring)');
|
|
1543
|
+
}
|
|
1544
|
+
if (!target) return fail('memory restore <surface> <target> — target 누락');
|
|
1545
|
+
const hd = path.join(root, '.harness');
|
|
1546
|
+
const archivePath = path.join(hd, `${surface}.archive.md`);
|
|
1547
|
+
if (!exists(archivePath)) return fail(`${surface}.archive.md 없음 — 복원할 항목 없음`);
|
|
1548
|
+
const text = read(archivePath);
|
|
1549
|
+
// archive 헤더 (# X archive) 와 본문 분리
|
|
1550
|
+
const headerMatch = text.match(/^(# [^\n]*\n+)([\s\S]*)$/);
|
|
1551
|
+
const archiveHeader = headerMatch ? headerMatch[1] : '';
|
|
1552
|
+
const body = headerMatch ? headerMatch[2] : text;
|
|
1553
|
+
// body 를 "## 제거 " 단위로 split
|
|
1554
|
+
const blocks = body.split(/\n(?=## 제거 )/);
|
|
1555
|
+
const kept = [];
|
|
1556
|
+
const restoredBlocks = [];
|
|
1557
|
+
for (const b of blocks) {
|
|
1558
|
+
if (!b.trim()) continue;
|
|
1559
|
+
const m = b.match(/^## 제거 (\d{4}-\d{2}-\d{2})\s*\(target:\s*"([^"]*)"\)/);
|
|
1560
|
+
if (!m) { kept.push(b); continue; }
|
|
1561
|
+
const date = m[1];
|
|
1562
|
+
const blockTarget = m[2];
|
|
1563
|
+
const isDateTarget = date === target;
|
|
1564
|
+
const isSubstring = blockTarget.includes(target);
|
|
1565
|
+
if (isDateTarget || isSubstring) {
|
|
1566
|
+
// archive 블록에서 원래 active 블록만 추출 (## 제거 ... 한 줄 + 다음 line부터)
|
|
1567
|
+
const content = b.replace(/^## 제거 [^\n]*\n+/, '');
|
|
1568
|
+
if (content.trim()) restoredBlocks.push(content.trim());
|
|
1569
|
+
} else {
|
|
1570
|
+
kept.push(b);
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
if (restoredBlocks.length === 0) return fail(`매칭 archive entry 없음: surface=${surface}, target="${target}"`);
|
|
1574
|
+
// active 파일 경로
|
|
1575
|
+
const activePath = surface === 'decisions' ? decisionsPath(root)
|
|
1576
|
+
: surface === 'lessons' ? lessonsPath(root)
|
|
1577
|
+
: planPath(root);
|
|
1578
|
+
// active 파일에 복귀 (헤더 보존)
|
|
1579
|
+
for (const blk of restoredBlocks) {
|
|
1580
|
+
append(activePath, '\n' + blk + '\n');
|
|
1581
|
+
}
|
|
1582
|
+
// archive 재작성 — 모두 제거되면 파일 비움 (헤더만 남김 또는 삭제)
|
|
1583
|
+
if (kept.length === 0) {
|
|
1584
|
+
// archive 헤더만 남겨도 의미 있음 — 향후 다시 사용 가능
|
|
1585
|
+
writeUtf8(archivePath, archiveHeader);
|
|
1586
|
+
} else {
|
|
1587
|
+
writeUtf8(archivePath, archiveHeader + kept.join('\n'));
|
|
1588
|
+
}
|
|
1589
|
+
ok(`${surface} restored: ${restoredBlocks.length}건 (archive에서 active로 복귀)`);
|
|
1590
|
+
_autoRoadmap(absRoot(root), 'data-change');
|
|
1591
|
+
}
|
|
1592
|
+
|
|
1533
1593
|
// 1.9.117: lesson list — lessons.md 의 모든 항목 조회 + --tag 필터 + --json
|
|
1534
1594
|
function lessonListCmd(root, opts = {}) {
|
|
1535
1595
|
root = absRoot(root);
|
|
@@ -2732,6 +2792,48 @@ function handoff(root) {
|
|
|
2732
2792
|
}
|
|
2733
2793
|
} catch {}
|
|
2734
2794
|
}
|
|
2795
|
+
// 1.9.129: handoff 7번째 자동 회수 — 🗑 최근 24h archive 알림
|
|
2796
|
+
// DELETE 5종 archive 파일 (.harness/decisions.archive.md, lessons.archive.md, plan.archive.md)
|
|
2797
|
+
// 에 최근 24h 내 추가된 entry 카운트를 노출. AI가 잘못 제거한 항목을 즉시 인지 + restore 후보 회수.
|
|
2798
|
+
// `leerness memory archive list` / `leerness memory restore <surface> <target>` 안내 포함.
|
|
2799
|
+
if (!has('--no-mem-delta') && !has('--compact') && !has('--quiet') && process.env.LEERNESS_NO_MEM_DELTA !== '1') {
|
|
2800
|
+
try {
|
|
2801
|
+
const isTtyArc = process.stdout && process.stdout.isTTY;
|
|
2802
|
+
const arcCy = s => isTtyArc ? `\x1b[36m${s}\x1b[0m` : s;
|
|
2803
|
+
const arcDim = s => isTtyArc ? `\x1b[2m${s}\x1b[0m` : s;
|
|
2804
|
+
const hd = path.join(root, '.harness');
|
|
2805
|
+
const cutoffArchive = Date.now() - 24 * 60 * 60 * 1000;
|
|
2806
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
2807
|
+
const yest = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString().slice(0, 10);
|
|
2808
|
+
const surfaces = [
|
|
2809
|
+
{ key: 'D', label: 'decisions', file: 'decisions.archive.md' },
|
|
2810
|
+
{ key: 'L', label: 'lessons', file: 'lessons.archive.md' },
|
|
2811
|
+
{ key: 'P', label: 'plan', file: 'plan.archive.md' }
|
|
2812
|
+
];
|
|
2813
|
+
const archiveDeltas = [];
|
|
2814
|
+
let totalRecent = 0;
|
|
2815
|
+
for (const s of surfaces) {
|
|
2816
|
+
const fp = path.join(hd, s.file);
|
|
2817
|
+
if (!exists(fp)) continue;
|
|
2818
|
+
try {
|
|
2819
|
+
const stat = fs.statSync(fp);
|
|
2820
|
+
if (stat.mtimeMs < cutoffArchive) continue; // 파일 자체가 24h 외면 skip
|
|
2821
|
+
const entries = _parseArchiveBlocks(read(fp));
|
|
2822
|
+
const recent = entries.filter(e => e.date === today || e.date === yest).length;
|
|
2823
|
+
if (recent > 0) {
|
|
2824
|
+
archiveDeltas.push(`${s.key}${recent}`);
|
|
2825
|
+
totalRecent += recent;
|
|
2826
|
+
}
|
|
2827
|
+
} catch {}
|
|
2828
|
+
}
|
|
2829
|
+
if (totalRecent > 0) {
|
|
2830
|
+
log(arcCy(`🗑 최근 24h archive (1.9.129): ${archiveDeltas.join('/')} (${totalRecent}건 archived) — 복원 후보`));
|
|
2831
|
+
log(arcDim(` → 회수: leerness memory archive list --json`));
|
|
2832
|
+
log(arcDim(` → 복원: leerness memory restore <surface> <target>`));
|
|
2833
|
+
log('');
|
|
2834
|
+
}
|
|
2835
|
+
} catch {}
|
|
2836
|
+
}
|
|
2735
2837
|
// 1.9.76: handoff 보안 상태 요약 — .env vs .env.example + .gitignore 시크릿 패턴 1줄 요약
|
|
2736
2838
|
// 매 세션 시작 시 AI가 보안 위험을 즉시 인지. --no-security-summary 또는 --compact로 끄기
|
|
2737
2839
|
if (!has('--no-security-summary') && !has('--compact') && !has('--quiet')) {
|
|
@@ -4136,7 +4238,7 @@ function _banner(opts = {}) {
|
|
|
4136
4238
|
lines.push('');
|
|
4137
4239
|
for (const ln of lines) log(ln);
|
|
4138
4240
|
if (opts.quickStart) {
|
|
4139
|
-
log(C.bold(C.cyan(' ✨ 빠른 시작 (1.9.
|
|
4241
|
+
log(C.bold(C.cyan(' ✨ 빠른 시작 (1.9.129+ handoff 7 자동 회수 🗑 archive 알림 — 59 라운드 자율 누적)')));
|
|
4140
4242
|
log(' ' + C.green('npx leerness@latest init .') + C.dim(' # 신규 프로젝트 + 외부 AI CLI 설정'));
|
|
4141
4243
|
log(' ' + C.green('npx leerness handoff .') + C.dim(' # 컨텍스트 + lessons + 매칭 skill + history hit + brainstorm hits + 헤드라인'));
|
|
4142
4244
|
log(' ' + C.green('npx leerness handoff . --quiet') + C.dim(' # 자동화/CI 모드 (1.9.99) — 자동 회수 라인 비활성'));
|
|
@@ -4152,9 +4254,9 @@ function _banner(opts = {}) {
|
|
|
4152
4254
|
log(' ' + C.green('npx leerness session close .') + C.dim(' # 마감 + 다음 라운드 추천 (default)'));
|
|
4153
4255
|
log('');
|
|
4154
4256
|
log(C.bold(C.cyan(' 🤖 메인 에이전트 (Claude/Cursor/Copilot)용')));
|
|
4155
|
-
log(' ' + C.green('npx leerness mcp serve') + C.dim(' # MCP 서버 —
|
|
4156
|
-
log(' ' + C.green('npx leerness plan remove <M-XXXX|title>') + C.dim(' # milestone 영구 제거 (archive 보존, 1.9.126) — Memory DELETE 5종 완성'));
|
|
4257
|
+
log(' ' + C.green('npx leerness mcp serve') + C.dim(' # MCP 서버 — 40 도구 🎉 (memory_restore 추가, 1.9.128)'));
|
|
4157
4258
|
log(' ' + C.green('npx leerness memory archive list --json') + C.dim(' # DELETE 5종 archive 통합 조회 (1.9.127) — D/L/P entries + 복원 후보'));
|
|
4259
|
+
log(' ' + C.green('npx leerness memory restore <surface> <target>') + C.dim(' # archive → active 복원 (1.9.128) — DELETE→RESTORE cycle'));
|
|
4158
4260
|
log(' ' + C.green('npx leerness lesson save "<text>" --tag "..."') + C.dim(' # lessons.md 직접 write (1.9.112 — handoff 자동 회수와 통합)'));
|
|
4159
4261
|
log(' ' + C.green('npx leerness memory status . --json') + C.dim(' # Memory Surface 5종 통합 상태 JSON (1.9.114)'));
|
|
4160
4262
|
log(' ' + C.green('npx leerness decision add "<title>" --reason "..."') + C.dim(' # 설계 결정 영구화 (1.9.108) — handoff lessons 자동 회수와 통합'));
|
|
@@ -8337,7 +8439,8 @@ function mcpServeCmd(root) {
|
|
|
8337
8439
|
{ 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'] } },
|
|
8338
8440
|
{ 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'] } },
|
|
8339
8441
|
{ 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'] } },
|
|
8340
|
-
{ 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' } } } }
|
|
8442
|
+
{ 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' } } } },
|
|
8443
|
+
{ 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'] } }
|
|
8341
8444
|
];
|
|
8342
8445
|
|
|
8343
8446
|
function send(obj) {
|
|
@@ -8408,6 +8511,7 @@ function mcpServeCmd(root) {
|
|
|
8408
8511
|
case 'leerness_decision_drop': cliArgs = ['decision', 'drop', String(args.target || ''), '--path', targetPath]; break;
|
|
8409
8512
|
case 'leerness_plan_remove': cliArgs = ['plan', 'remove', String(args.target || ''), '--path', targetPath]; break;
|
|
8410
8513
|
case 'leerness_memory_archive_list': cliArgs = ['memory', 'archive', 'list', '--path', targetPath, '--json', ...(args.surface ? ['--surface', args.surface] : [])]; break;
|
|
8514
|
+
case 'leerness_memory_restore': cliArgs = ['memory', 'restore', String(args.surface || ''), String(args.target || ''), '--path', targetPath]; break;
|
|
8411
8515
|
default:
|
|
8412
8516
|
return send({ jsonrpc: '2.0', id, error: { code: -32601, message: `Unknown tool: ${name}` } });
|
|
8413
8517
|
}
|
|
@@ -9151,6 +9255,11 @@ async function main() {
|
|
|
9151
9255
|
const root = absRoot(arg('--path', args[3] && !args[3].startsWith('-') ? args[3] : process.cwd()));
|
|
9152
9256
|
return memoryArchiveListCmd(root, { json: has('--json') });
|
|
9153
9257
|
}
|
|
9258
|
+
// 1.9.128: memory restore — archive 블록을 active 파일로 복귀 (DELETE→RESTORE cycle)
|
|
9259
|
+
if (cmd === 'memory' && args[1] === 'restore') {
|
|
9260
|
+
const root = absRoot(arg('--path', process.cwd()));
|
|
9261
|
+
return memoryRestoreCmd(root, args[2], args[3]);
|
|
9262
|
+
}
|
|
9154
9263
|
// 1.9.112: lesson save — lessons.md에 새 lesson 추가
|
|
9155
9264
|
// 1.9.117: lesson list — lessons.md 조회 + --tag 필터 + --json
|
|
9156
9265
|
if (cmd === 'lesson') {
|