leerness 1.9.126 → 1.9.127

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 CHANGED
@@ -1,5 +1,28 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.9.127 — 2026-05-20
4
+
5
+ **`leerness memory archive list` CLI + MCP 39번째 도구 `leerness_memory_archive_list`** — DELETE 5종 archive 통합 조회 (decisions/lessons/plan).
6
+
7
+ ### Added — `leerness memory archive list` CLI
8
+ - DELETE 5종 archive 파일 (`.harness/decisions.archive.md`, `lessons.archive.md`, `plan.archive.md`) 통합 조회
9
+ - 각 archive entry 파싱: `{ date, target, originalHeader }`
10
+ - `--surface decisions|lessons|plan` 필터 지원
11
+ - `--json` 옵션 — totals + 각 surface 별 entries
12
+ - archive 파일 없으면 안내 메시지
13
+
14
+ ### Added — MCP 39번째 도구 `leerness_memory_archive_list`
15
+ - 외부 AI 가 과거에 제거된 항목 회수 — 복원 후보 참조 / 의사결정 변경 흐름 추적.
16
+ - 인자: `{ surface?, path? }` (surface optional)
17
+
18
+ ### 사용 시나리오
19
+ 1. **복원 후보 회수**: "이전에 PostgreSQL 채택 결정 취소했었지? 어떤 게 있었나"
20
+ → 외부 AI: `leerness_memory_archive_list({ surface: "decisions" })` — 모든 제거된 결정 회수
21
+ 2. **의사결정 변경 패턴 분석**: 자주 변경되는 surface 의 빈도 추적
22
+ 3. **복구**: archive entry 참조 후 다시 `decision add` 로 재등록
23
+
24
+ ### MCP 도구 누계: 39 (1.9.126: 38 + leerness_memory_archive_list)
25
+
3
26
  ## 1.9.126 — 2026-05-20
4
27
 
5
28
  **`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
- [![npm](https://img.shields.io/badge/npm-leerness-blue)](https://www.npmjs.com/package/leerness) [![version](https://img.shields.io/badge/version-1.9.126-green)]() [![tests](https://img.shields.io/badge/e2e-219%2F219-success)]() [![mcp](https://img.shields.io/badge/MCP--tools-38-blue)]() [![json](https://img.shields.io/badge/--json-18_commands-blueviolet)]() [![rounds](https://img.shields.io/badge/autonomous--rounds-56-blueviolet)]() [![memory-delete](https://img.shields.io/badge/Memory--DELETE-5--complete-success)]() [![license](https://img.shields.io/badge/license-MIT-lightgrey)]()
5
+ [![npm](https://img.shields.io/badge/npm-leerness-blue)](https://www.npmjs.com/package/leerness) [![version](https://img.shields.io/badge/version-1.9.127-green)]() [![tests](https://img.shields.io/badge/e2e-219%2F219-success)]() [![mcp](https://img.shields.io/badge/MCP--tools-39-blue)]() [![json](https://img.shields.io/badge/--json-19_commands-blueviolet)]() [![rounds](https://img.shields.io/badge/autonomous--rounds-57-blueviolet)]() [![memory-delete](https://img.shields.io/badge/Memory--DELETE-5--complete-success)]() [![license](https://img.shields.io/badge/license-MIT-lightgrey)]()
6
6
 
7
7
  ```
8
8
  ╔══════════════════════════════════════════════════════════════╗
@@ -12,7 +12,7 @@
12
12
  ║ ██║ ██╔══╝ ██╔══╝ ██╔══██╗██║╚██╗██║██╔══╝ ╚════██║ ║
13
13
  ║ ███████╗███████╗███████╗██║ ██║██║ ╚████║███████╗███████║ ║
14
14
  ║ ╚══════╝╚══════╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝╚══════╝╚══════╝ ║
15
- ║ v1.9.126 AI Agent Reliability Harness ║
15
+ ║ v1.9.127 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.126';
9
+ const VERSION = '1.9.127';
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,7 @@ 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 통합 조회 (복원 후보 회수).
330
331
 
331
332
  ---
332
333
 
@@ -1450,6 +1451,85 @@ function memoryStatusCmd(root, opts = {}) {
1450
1451
  log(`\n📊 Summary: ${payload.summary}`);
1451
1452
  }
1452
1453
 
1454
+ // 1.9.127: memory archive list — DELETE 5종 archive 파일 통합 조회
1455
+ // .harness/decisions.archive.md / lessons.archive.md / plan.archive.md 의 "## 제거 YYYY-MM-DD" 블록 파싱
1456
+ // --surface decisions|lessons|plan 필터, --json 옵션
1457
+ function _parseArchiveBlocks(text) {
1458
+ // archive 형식: "## 제거 YYYY-MM-DD (target: \"...\")\n<원래 블록>"
1459
+ // 첫 번째 "## 제거" 이전의 헤더(# Plan archive 등)는 skip
1460
+ const entries = [];
1461
+ if (!text) return entries;
1462
+ const blocks = text.split(/\n(?=## 제거 )/);
1463
+ for (const b of blocks) {
1464
+ const m = b.match(/^## 제거 (\d{4}-\d{2}-\d{2})\s*\(target:\s*"([^"]*)"\)/);
1465
+ if (!m) continue;
1466
+ const date = m[1];
1467
+ const target = m[2];
1468
+ // 원래 헤더 추출 (### …)
1469
+ const headerMatch = b.match(/^### (.+)$/m);
1470
+ const originalHeader = headerMatch ? headerMatch[1].trim() : null;
1471
+ entries.push({ date, target, originalHeader });
1472
+ }
1473
+ return entries;
1474
+ }
1475
+ function memoryArchiveListCmd(root, opts = {}) {
1476
+ root = absRoot(root);
1477
+ const jsonMode = !!opts.json || has('--json');
1478
+ const surfaceFilter = arg('--surface', '');
1479
+ const hd = path.join(root, '.harness');
1480
+ const archives = {
1481
+ decisions: { path: path.join(hd, 'decisions.archive.md'), entries: [] },
1482
+ lessons: { path: path.join(hd, 'lessons.archive.md'), entries: [] },
1483
+ plan: { path: path.join(hd, 'plan.archive.md'), entries: [] }
1484
+ };
1485
+ for (const k of Object.keys(archives)) {
1486
+ if (surfaceFilter && surfaceFilter !== k) continue;
1487
+ const a = archives[k];
1488
+ if (exists(a.path)) a.entries = _parseArchiveBlocks(read(a.path));
1489
+ }
1490
+ const totals = {
1491
+ decisions: archives.decisions.entries.length,
1492
+ lessons: archives.lessons.entries.length,
1493
+ plan: archives.plan.entries.length
1494
+ };
1495
+ totals.all = totals.decisions + totals.lessons + totals.plan;
1496
+ if (jsonMode) {
1497
+ const payload = {
1498
+ version: VERSION, root,
1499
+ decisions: archives.decisions.entries,
1500
+ lessons: archives.lessons.entries,
1501
+ plan: archives.plan.entries,
1502
+ totals
1503
+ };
1504
+ process.stdout.write(JSON.stringify(payload, null, 2) + '\n');
1505
+ return;
1506
+ }
1507
+ log('# 🗑 Memory Archive List (1.9.127)\n');
1508
+ if (totals.all === 0) {
1509
+ log('(archive 파일 없음 — 아직 제거된 항목 없음)');
1510
+ return;
1511
+ }
1512
+ if ((!surfaceFilter || surfaceFilter === 'decisions') && archives.decisions.entries.length) {
1513
+ log(`🧠 Decisions archive: ${archives.decisions.entries.length} entries`);
1514
+ for (const e of archives.decisions.entries) {
1515
+ log(` - ${e.date} — target: "${e.target}"${e.originalHeader ? ' — ' + e.originalHeader : ''}`);
1516
+ }
1517
+ }
1518
+ if ((!surfaceFilter || surfaceFilter === 'lessons') && archives.lessons.entries.length) {
1519
+ log(`💡 Lessons archive: ${archives.lessons.entries.length} entries`);
1520
+ for (const e of archives.lessons.entries) {
1521
+ log(` - ${e.date} — target: "${e.target}"${e.originalHeader ? ' — ' + e.originalHeader : ''}`);
1522
+ }
1523
+ }
1524
+ if ((!surfaceFilter || surfaceFilter === 'plan') && archives.plan.entries.length) {
1525
+ log(`🗺 Plan archive: ${archives.plan.entries.length} entries`);
1526
+ for (const e of archives.plan.entries) {
1527
+ log(` - ${e.date} — target: "${e.target}"${e.originalHeader ? ' — ' + e.originalHeader : ''}`);
1528
+ }
1529
+ }
1530
+ log(`\n📊 Total archived: ${totals.all} entries (D${totals.decisions}/L${totals.lessons}/P${totals.plan})`);
1531
+ }
1532
+
1453
1533
  // 1.9.117: lesson list — lessons.md 의 모든 항목 조회 + --tag 필터 + --json
1454
1534
  function lessonListCmd(root, opts = {}) {
1455
1535
  root = absRoot(root);
@@ -4056,7 +4136,7 @@ function _banner(opts = {}) {
4056
4136
  lines.push('');
4057
4137
  for (const ln of lines) log(ln);
4058
4138
  if (opts.quickStart) {
4059
- log(C.bold(C.cyan(' ✨ 빠른 시작 (1.9.126+ Memory Surface DELETE 5종 완성 56 라운드 자율 누적)')));
4139
+ log(C.bold(C.cyan(' ✨ 빠른 시작 (1.9.127+ memory archive list57 라운드 자율 누적)')));
4060
4140
  log(' ' + C.green('npx leerness@latest init .') + C.dim(' # 신규 프로젝트 + 외부 AI CLI 설정'));
4061
4141
  log(' ' + C.green('npx leerness handoff .') + C.dim(' # 컨텍스트 + lessons + 매칭 skill + history hit + brainstorm hits + 헤드라인'));
4062
4142
  log(' ' + C.green('npx leerness handoff . --quiet') + C.dim(' # 자동화/CI 모드 (1.9.99) — 자동 회수 라인 비활성'));
@@ -4072,8 +4152,9 @@ function _banner(opts = {}) {
4072
4152
  log(' ' + C.green('npx leerness session close .') + C.dim(' # 마감 + 다음 라운드 추천 (default)'));
4073
4153
  log('');
4074
4154
  log(C.bold(C.cyan(' 🤖 메인 에이전트 (Claude/Cursor/Copilot)용')));
4075
- log(' ' + C.green('npx leerness mcp serve') + C.dim(' # MCP 서버 — 38 도구 (plan_remove 추가, 1.9.126)'));
4155
+ log(' ' + C.green('npx leerness mcp serve') + C.dim(' # MCP 서버 — 39 도구 (memory_archive_list 추가, 1.9.127)'));
4076
4156
  log(' ' + C.green('npx leerness plan remove <M-XXXX|title>') + C.dim(' # milestone 영구 제거 (archive 보존, 1.9.126) — Memory DELETE 5종 완성'));
4157
+ log(' ' + C.green('npx leerness memory archive list --json') + C.dim(' # DELETE 5종 archive 통합 조회 (1.9.127) — D/L/P entries + 복원 후보'));
4077
4158
  log(' ' + C.green('npx leerness lesson save "<text>" --tag "..."') + C.dim(' # lessons.md 직접 write (1.9.112 — handoff 자동 회수와 통합)'));
4078
4159
  log(' ' + C.green('npx leerness memory status . --json') + C.dim(' # Memory Surface 5종 통합 상태 JSON (1.9.114)'));
4079
4160
  log(' ' + C.green('npx leerness decision add "<title>" --reason "..."') + C.dim(' # 설계 결정 영구화 (1.9.108) — handoff lessons 자동 회수와 통합'));
@@ -8255,7 +8336,8 @@ function mcpServeCmd(root) {
8255
8336
  { 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
8337
  { 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
8338
  { 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'] } }
8339
+ { 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' } } } }
8259
8341
  ];
8260
8342
 
8261
8343
  function send(obj) {
@@ -8325,6 +8407,7 @@ function mcpServeCmd(root) {
8325
8407
  case 'leerness_lesson_drop': cliArgs = ['lesson', 'drop', String(args.target || ''), '--path', targetPath]; break;
8326
8408
  case 'leerness_decision_drop': cliArgs = ['decision', 'drop', String(args.target || ''), '--path', targetPath]; break;
8327
8409
  case 'leerness_plan_remove': cliArgs = ['plan', 'remove', String(args.target || ''), '--path', targetPath]; break;
8410
+ case 'leerness_memory_archive_list': cliArgs = ['memory', 'archive', 'list', '--path', targetPath, '--json', ...(args.surface ? ['--surface', args.surface] : [])]; break;
8328
8411
  default:
8329
8412
  return send({ jsonrpc: '2.0', id, error: { code: -32601, message: `Unknown tool: ${name}` } });
8330
8413
  }
@@ -9063,6 +9146,11 @@ async function main() {
9063
9146
  const root = absRoot(arg('--path', args[2] && !args[2].startsWith('-') ? args[2] : process.cwd()));
9064
9147
  return memoryStatusCmd(root, { json: has('--json') });
9065
9148
  }
9149
+ // 1.9.127: memory archive list — DELETE 5종 archive 통합 조회
9150
+ if (cmd === 'memory' && args[1] === 'archive' && args[2] === 'list') {
9151
+ const root = absRoot(arg('--path', args[3] && !args[3].startsWith('-') ? args[3] : process.cwd()));
9152
+ return memoryArchiveListCmd(root, { json: has('--json') });
9153
+ }
9066
9154
  // 1.9.112: lesson save — lessons.md에 새 lesson 추가
9067
9155
  // 1.9.117: lesson list — lessons.md 조회 + --tag 필터 + --json
9068
9156
  if (cmd === 'lesson') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leerness",
3
- "version": "1.9.126",
3
+ "version": "1.9.127",
4
4
  "description": "Leerness: 비파괴 마이그레이션, 자동 버전 감지·업데이트, 계획/진행/핸드오프 자동화, 게으름·시크릿·인코딩 자동 가드, Claude Code 슬래시 통합을 갖춘 한국어 우선 AI 개발 하네스.",
5
5
  "keywords": [
6
6
  "leerness",