leerness 1.9.65 → 1.9.67
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 +45 -0
- package/README.md +4 -2
- package/bin/harness.js +86 -15
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,50 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.9.67 — 2026-05-19
|
|
4
|
+
|
|
5
|
+
**handoff 자동 skill 추천 default ON + lessons 인덱스에 task-log 통합**.
|
|
6
|
+
|
|
7
|
+
### Added — handoff 자동 skill match (default ON)
|
|
8
|
+
- 매 `leerness handoff` 시 **현재 in-progress task와 매칭되는 설치된 skill을 자동 추천** (점수 + skill id + description 미리보기).
|
|
9
|
+
- 1.9.45의 `LEERNESS_SKILL_AUTO_DISCOVER=1` opt-in 환경변수 의존성 제거 → default 활성.
|
|
10
|
+
- 끄기: `--no-skill-suggest` 또는 `LEERNESS_NO_SKILL_SUGGEST=1`.
|
|
11
|
+
- 매칭 알고리즘: `_jaccard(task.request_tokens, skill.name+description_tokens)`, top 3.
|
|
12
|
+
- 매칭 점수 0이면 출력 안 함 (잡음 최소화).
|
|
13
|
+
|
|
14
|
+
### Improved — lessons 인덱스 확장
|
|
15
|
+
- `_loadLessonsIndex`에 **task-log.md 실패 라인** 추가 (mtime 기반 invalidation).
|
|
16
|
+
- `_lidx.taskLogFails: [{title, block}]` 새 필드.
|
|
17
|
+
- handoff lessons 자동 재상기에서 task-log fuzzy 매칭도 가능.
|
|
18
|
+
- `leerness lessons` 명령도 같은 인덱스 사용 (split 1회).
|
|
19
|
+
|
|
20
|
+
### Updated
|
|
21
|
+
- `_banner` quickStart: "13 도구 노출 (task_export 포함)" + "매칭 skill 자동 추천" 안내.
|
|
22
|
+
- `.harness/session-workflow.md` 템플릿: 1.9.67 라인 추가.
|
|
23
|
+
|
|
24
|
+
### Verified
|
|
25
|
+
- stress-v13 (1.9.67 검증) — handoff skill match default + --no-skill-suggest + lessons task-log fuzzy.
|
|
26
|
+
- e2e 회귀: 219/219 PASS 유지.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 1.9.66 — 2026-05-19
|
|
31
|
+
|
|
32
|
+
**성능 최적화 2차 + MCP 13번째 도구**.
|
|
33
|
+
|
|
34
|
+
### Performance
|
|
35
|
+
- **`listAllSkills` 메모리 캐시 (`_SKILLS_LIST_CACHE`)** — userSkillsDir mtime 기반 캐시. `skill list/info/match/discover/suggest` 가 같은 인덱스 공유.
|
|
36
|
+
- `saveUserSkill`/`skillRemove`에서 캐시 invalidate — skill 추가/제거 즉시 반영.
|
|
37
|
+
|
|
38
|
+
### MCP server — 13번째 도구
|
|
39
|
+
- **`leerness_task_export`** — 1.9.60 TodoWrite 호환 JSON을 외부 에이전트(Claude Code, Cursor 등)에 노출. `to: <path>` 또는 stdout JSON 모두 지원.
|
|
40
|
+
- MCP server 도구 카운트: 12 → **13**.
|
|
41
|
+
|
|
42
|
+
### Verified
|
|
43
|
+
- stress-v12 (1.9.66 검증) — listAllSkills 캐시 정합성 + MCP 13 도구 + warm-up 1회 시나리오 보강.
|
|
44
|
+
- e2e 회귀: 219/219 PASS 유지.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
3
48
|
## 1.9.65 — 2026-05-19
|
|
4
49
|
|
|
5
50
|
**성능 최적화 1차 — usage-stats 메모리 캐시 + lessons 인덱스 캐시**.
|
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.67 AI Agent Reliability Harness ║
|
|
16
16
|
║ verify · remember · orchestrate · audit · prevent drift ║
|
|
17
17
|
╚══════════════════════════════════════════════════════════════╝
|
|
18
18
|
```
|
|
@@ -433,6 +433,8 @@ npm test # = node ./scripts/e2e.js
|
|
|
433
433
|
|
|
434
434
|
## 변경 이력 (최근)
|
|
435
435
|
|
|
436
|
+
- **1.9.67** — **handoff 자동 skill 추천 default ON** (jaccard 매칭) + lessons 인덱스에 task-log.md 실패 라인 통합 (회수 범위 확장).
|
|
437
|
+
- **1.9.66** — **성능 최적화 2차 + MCP 13번째 도구**. `listAllSkills` 메모리 캐시 (skill list/info/match/discover/suggest 공유) + MCP `leerness_task_export` 추가 (TodoWrite 양방향 sync 외부 노출).
|
|
436
438
|
- **1.9.65** — **성능 최적화 1차** — usage-stats 메모리 캐시 + lessons 인덱스 캐시 (mtime invalidation). handoff -37% · drift -19% · audit -29% · skill list -17% · 100-task handoff -42% · status -48% (vs 1.9.64).
|
|
437
439
|
- **1.9.64** — `leerness install <SKILL.md>` 별칭 (skill install 단축) · **성능 벤치마크 1차 실측** (status/handoff/drift/audit/skill list 평균 1.2~1.5초).
|
|
438
440
|
- **1.9.63** — `leerness audit --strict [--threshold N]` — CI 친화 옵션 (warnings → failures 승격).
|
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.67';
|
|
10
10
|
const MARK = '<!-- leerness:managed -->';
|
|
11
11
|
const README_START = '<!-- leerness:project-readme:start -->';
|
|
12
12
|
const README_END = '<!-- leerness:project-readme:end -->';
|
|
@@ -289,6 +289,8 @@ leerness audit . --fix # 누락 메타 자동 보강
|
|
|
289
289
|
- session close가 누락되면 다음 세션 시작 시 drift critical 발생.
|
|
290
290
|
- 자동 회복 옵션: \`drift check --auto-fix\` (critical 시 session close 자동 실행).
|
|
291
291
|
- 1.9.56+ handoff가 매 세션 시작 시 **과거 lessons 자동 재상기** (현재 task 키워드 기준).
|
|
292
|
+
- 1.9.67+ handoff가 현재 task와 매칭되는 **설치된 skill을 자동 추천** (jaccard 기반, default ON, \`--no-skill-suggest\`로 끄기).
|
|
293
|
+
- 1.9.67+ lessons 인덱스에 \`task-log.md\` 실패 라인까지 포함 → 회수 범위 확장.
|
|
292
294
|
|
|
293
295
|
---
|
|
294
296
|
|
|
@@ -715,13 +717,34 @@ function loadUserSkill(root, id) {
|
|
|
715
717
|
function saveUserSkill(root, id, data) {
|
|
716
718
|
const dir = path.join(userSkillsDir(root), id); mkdirp(dir);
|
|
717
719
|
writeUtf8(path.join(dir, 'skill.json'), JSON.stringify(data, null, 2) + '\n');
|
|
720
|
+
// 1.9.66: 캐시 invalidate (skill 추가/변경 즉시 반영)
|
|
721
|
+
try { _SKILLS_LIST_CACHE.delete(absRoot(root)); } catch {}
|
|
718
722
|
// README mirror
|
|
719
723
|
const usage = data.usage || { count: 0 };
|
|
720
724
|
const readme = `# ${data.displayNameKo || id}\n\n## Capabilities\n${(data.capabilities || []).map(c => '- ' + c).join('\n') || '-'}\n\n## Sources\n${(data.sources || []).map(s => `- ${s.url || s}`).join('\n') || '-'}\n\n## Patterns (성공 명령/접근)\n${(data.patterns || []).map(p => `- \`${p.command}\` — ${p.note || ''}`).join('\n') || '-'}\n\n## Optimization history\n${(data.optimizations || []).map(o => `- ${o.at}: ${o.note || ''}${o.before||o.after?` (${o.before||'?'} → ${o.after||'?'})`:''}`).join('\n') || '-'}\n\n## Usage\n${usage.count || 0}회 사용 / 마지막: ${usage.lastUsed || '-'}\n${usage.lastNote ? '\n마지막 노트: ' + usage.lastNote : ''}\n`;
|
|
721
725
|
writeUtf8(path.join(dir, 'README.md'), readme);
|
|
722
726
|
}
|
|
723
727
|
|
|
728
|
+
// 1.9.66: listAllSkills 메모리 캐시 — skill list/info/match/discover/suggest 가 공유
|
|
729
|
+
// key: root → { mtime(skillsDir), out }
|
|
730
|
+
const _SKILLS_LIST_CACHE = new Map();
|
|
724
731
|
function listAllSkills(root) {
|
|
732
|
+
// 캐시 hit 확인: userSkillsDir mtime 동일 시 재구성 skip
|
|
733
|
+
if (root) {
|
|
734
|
+
try {
|
|
735
|
+
const dir = userSkillsDir(root);
|
|
736
|
+
const dirMtime = exists(dir) ? fs.statSync(dir).mtimeMs : 0;
|
|
737
|
+
const key = absRoot(root);
|
|
738
|
+
const cached = _SKILLS_LIST_CACHE.get(key);
|
|
739
|
+
if (cached && cached.dirMtime === dirMtime) return cached.out;
|
|
740
|
+
const out = _buildAllSkills(root);
|
|
741
|
+
_SKILLS_LIST_CACHE.set(key, { dirMtime, out });
|
|
742
|
+
return out;
|
|
743
|
+
} catch { return _buildAllSkills(root); }
|
|
744
|
+
}
|
|
745
|
+
return _buildAllSkills(root);
|
|
746
|
+
}
|
|
747
|
+
function _buildAllSkills(root) {
|
|
725
748
|
const out = {};
|
|
726
749
|
// 1.9.10: skillCatalog의 _source('skillpack' 또는 'builtin')를 보존
|
|
727
750
|
for (const [k, v] of Object.entries(skillCatalog)) out[k] = { ...v, _source: v._source || 'builtin' };
|
|
@@ -739,6 +762,10 @@ function listAllSkills(root) {
|
|
|
739
762
|
}
|
|
740
763
|
return out;
|
|
741
764
|
}
|
|
765
|
+
// 1.9.66: skill 추가/제거 시 캐시 invalidate (외부 helper)
|
|
766
|
+
function _invalidateSkillsCache(root) {
|
|
767
|
+
try { _SKILLS_LIST_CACHE.delete(absRoot(root)); } catch {}
|
|
768
|
+
}
|
|
742
769
|
|
|
743
770
|
function skillList(root) {
|
|
744
771
|
const all = listAllSkills(root);
|
|
@@ -826,6 +853,8 @@ function skillRemove(root, id) {
|
|
|
826
853
|
if (!id) return fail('id required');
|
|
827
854
|
const dir = path.join(userSkillsDir(root), id);
|
|
828
855
|
if (!exists(dir)) return fail(`skill folder not found: ${id}`);
|
|
856
|
+
// 1.9.66: 캐시 invalidate
|
|
857
|
+
try { _SKILLS_LIST_CACHE.delete(absRoot(root)); } catch {}
|
|
829
858
|
if (skillCatalog[id]) {
|
|
830
859
|
// catalog 스킬은 로컬 메타만 제거 (카탈로그는 패키지 내장이라 영구 제거 불가)
|
|
831
860
|
fs.rmSync(dir, { recursive: true, force: true });
|
|
@@ -1775,6 +1804,12 @@ function handoff(root) {
|
|
|
1775
1804
|
matches.push({ source: 'decisions.md', title: d.title, block: d.block });
|
|
1776
1805
|
}
|
|
1777
1806
|
}
|
|
1807
|
+
// 1.9.67: task-log.md 실패 라인도 fuzzy 매칭 (회수 범위 확장)
|
|
1808
|
+
for (const t of (idx.taskLogFails || [])) {
|
|
1809
|
+
if (fuzzyRe.test(t.block)) {
|
|
1810
|
+
matches.push({ source: 'task-log.md', title: t.title, block: t.block });
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1778
1813
|
if (matches.length) {
|
|
1779
1814
|
const isTty = process.stdout && process.stdout.isTTY;
|
|
1780
1815
|
const yel = s => isTty ? `\x1b[33m${s}\x1b[0m` : s;
|
|
@@ -1788,6 +1823,30 @@ function handoff(root) {
|
|
|
1788
1823
|
log(dim(` → 전체: leerness lessons --auto --path .`));
|
|
1789
1824
|
log('');
|
|
1790
1825
|
}
|
|
1826
|
+
// 1.9.67: 현재 task와 관련된 skill 자동 추천 (default ON, 1.9.45 opt-in → default)
|
|
1827
|
+
// 끄려면: --no-skill-suggest 또는 LEERNESS_NO_SKILL_SUGGEST=1
|
|
1828
|
+
if (!has('--no-skill-suggest') && process.env.LEERNESS_NO_SKILL_SUGGEST !== '1') {
|
|
1829
|
+
try {
|
|
1830
|
+
const installed = _readInstalledSkills(root);
|
|
1831
|
+
if (installed.length) {
|
|
1832
|
+
const qTokens = _tokenize(String(latestRow.request));
|
|
1833
|
+
const ranked = installed.map(s => ({
|
|
1834
|
+
...s, score: _jaccard(qTokens, _tokenize(s.name + ' ' + s.description))
|
|
1835
|
+
})).filter(s => s.score > 0).sort((a, b) => b.score - a.score).slice(0, 3);
|
|
1836
|
+
if (ranked.length) {
|
|
1837
|
+
const isTty = process.stdout && process.stdout.isTTY;
|
|
1838
|
+
const grn = s => isTty ? `\x1b[32m${s}\x1b[0m` : s;
|
|
1839
|
+
const dim = s => isTty ? `\x1b[2m${s}\x1b[0m` : s;
|
|
1840
|
+
log(grn(`## 🎯 현재 task와 매칭되는 skill 자동 추천 (1.9.67) — 키워드 "${keyword}"`));
|
|
1841
|
+
for (const r of ranked) {
|
|
1842
|
+
log(dim(` • [${r.score.toFixed(2)}] ${r.id} — ${(r.description || '').slice(0, 60)}`));
|
|
1843
|
+
}
|
|
1844
|
+
log(dim(` → 전체: leerness skill match "${String(latestRow.request).slice(0, 60)}"`));
|
|
1845
|
+
log('');
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
} catch {}
|
|
1849
|
+
}
|
|
1791
1850
|
}
|
|
1792
1851
|
}
|
|
1793
1852
|
} catch {}
|
|
@@ -3137,14 +3196,14 @@ function _banner(opts = {}) {
|
|
|
3137
3196
|
lines.push('');
|
|
3138
3197
|
for (const ln of lines) log(ln);
|
|
3139
3198
|
if (opts.quickStart) {
|
|
3140
|
-
log(C.bold(C.cyan(' ✨ 빠른 시작 (1.9.
|
|
3199
|
+
log(C.bold(C.cyan(' ✨ 빠른 시작 (1.9.67+ 워크플로)')));
|
|
3141
3200
|
log(' ' + C.green('npx leerness@latest init .') + C.dim(' # 신규 프로젝트 + 외부 AI CLI 설정'));
|
|
3142
|
-
log(' ' + C.green('npx leerness handoff .') + C.dim(' # 컨텍스트
|
|
3201
|
+
log(' ' + C.green('npx leerness handoff .') + C.dim(' # 컨텍스트 + lessons 재상기 + 매칭 skill 자동 추천'));
|
|
3143
3202
|
log(' ' + C.green('npx leerness verify-claim T-0001 --run-tests') + C.dim(' # AI 거짓 완료 자동 검증'));
|
|
3144
3203
|
log(' ' + C.green('npx leerness session close .') + C.dim(' # 마감 + 다음 라운드 추천 (default)'));
|
|
3145
3204
|
log('');
|
|
3146
3205
|
log(C.bold(C.cyan(' 🤖 메인 에이전트 (Claude/Cursor/Copilot)용')));
|
|
3147
|
-
log(' ' + C.green('npx leerness mcp serve') + C.dim(' # MCP 서버 —
|
|
3206
|
+
log(' ' + C.green('npx leerness mcp serve') + C.dim(' # MCP 서버 — 13 도구 노출 (task_export 포함)'));
|
|
3148
3207
|
log(' ' + C.green('npx leerness agents bench "<task>"') + C.dim(' # 3 CLI 동시 비교'));
|
|
3149
3208
|
log('');
|
|
3150
3209
|
}
|
|
@@ -5518,10 +5577,9 @@ function lessonsCmd(root) {
|
|
|
5518
5577
|
}
|
|
5519
5578
|
log(`# Lessons --auto (1.9.54): 추출 키워드 "${query}"`);
|
|
5520
5579
|
}
|
|
5521
|
-
// 1.9.65: 인덱스 캐시 활용 (decisions/evidence split 1회만)
|
|
5580
|
+
// 1.9.65/67: 인덱스 캐시 활용 (decisions/evidence/task-log split 1회만)
|
|
5522
5581
|
const _lidx = _loadLessonsIndex(root);
|
|
5523
5582
|
const decisions = exists(decisionsPath(root)) ? read(decisionsPath(root)) : '';
|
|
5524
|
-
const tlog = exists(taskLogPath(root)) ? read(taskLogPath(root)) : '';
|
|
5525
5583
|
const handoff = exists(handoffPath(root)) ? read(handoffPath(root)) : '';
|
|
5526
5584
|
const lessons = [];
|
|
5527
5585
|
// decisions: ### 블록 전체 (1.9.14: 코드블록/Template 제외)
|
|
@@ -5536,11 +5594,9 @@ function lessonsCmd(root) {
|
|
|
5536
5594
|
lessons.push({ source: 'review-evidence.md', title: e.title, block: e.block });
|
|
5537
5595
|
}
|
|
5538
5596
|
}
|
|
5539
|
-
// task-log: 실패 키워드 라인
|
|
5540
|
-
for (const
|
|
5541
|
-
|
|
5542
|
-
lessons.push({ source: 'task-log.md', title: line.replace(/^[-*]\s*/, '').slice(0, 80), block: line });
|
|
5543
|
-
}
|
|
5597
|
+
// task-log: 실패 키워드 라인 (1.9.67: 인덱스 재활용)
|
|
5598
|
+
for (const t of (_lidx.taskLogFails || [])) {
|
|
5599
|
+
lessons.push({ source: 'task-log.md', title: t.title, block: t.block });
|
|
5544
5600
|
}
|
|
5545
5601
|
// handoff: 미완료/블로커 항목
|
|
5546
5602
|
if (handoff) {
|
|
@@ -6135,16 +6191,19 @@ function driftCheckCmd(root, opts = {}) {
|
|
|
6135
6191
|
}
|
|
6136
6192
|
|
|
6137
6193
|
// 1.9.65: lessons blocks 인덱스 — evidence/decisions 파일 read + split을 1회로
|
|
6138
|
-
//
|
|
6194
|
+
// 1.9.67: task-log.md 실패 라인도 인덱스에 포함 (mtime 기반 invalidation)
|
|
6195
|
+
// key: root → { evidenceMtime, decisionsMtime, taskLogMtime, evidence/decisions/taskLogFails: [{title, block}] }
|
|
6139
6196
|
const _LESSONS_INDEX_CACHE = new Map();
|
|
6140
6197
|
function _loadLessonsIndex(root) {
|
|
6141
6198
|
const ep = evidencePath(root);
|
|
6142
6199
|
const dp = decisionsPath(root);
|
|
6200
|
+
const tp = taskLogPath(root);
|
|
6143
6201
|
const em = exists(ep) ? (() => { try { return fs.statSync(ep).mtimeMs; } catch { return 0; } })() : 0;
|
|
6144
6202
|
const dm = exists(dp) ? (() => { try { return fs.statSync(dp).mtimeMs; } catch { return 0; } })() : 0;
|
|
6203
|
+
const tm = exists(tp) ? (() => { try { return fs.statSync(tp).mtimeMs; } catch { return 0; } })() : 0;
|
|
6145
6204
|
const cacheKey = absRoot(root);
|
|
6146
6205
|
const cached = _LESSONS_INDEX_CACHE.get(cacheKey);
|
|
6147
|
-
if (cached && cached.evidenceMtime === em && cached.decisionsMtime === dm) return cached;
|
|
6206
|
+
if (cached && cached.evidenceMtime === em && cached.decisionsMtime === dm && cached.taskLogMtime === tm) return cached;
|
|
6148
6207
|
const evidence = [];
|
|
6149
6208
|
if (em) {
|
|
6150
6209
|
const txt = read(ep);
|
|
@@ -6163,7 +6222,17 @@ function _loadLessonsIndex(root) {
|
|
|
6163
6222
|
if (t) decisions.push({ title: t[1].trim(), block });
|
|
6164
6223
|
}
|
|
6165
6224
|
}
|
|
6166
|
-
|
|
6225
|
+
// 1.9.67: task-log.md 라인 중 실패/롤백 표지가 있는 라인만 인덱스
|
|
6226
|
+
const taskLogFails = [];
|
|
6227
|
+
if (tm) {
|
|
6228
|
+
const txt = read(tp);
|
|
6229
|
+
for (const line of txt.split('\n')) {
|
|
6230
|
+
if (line.length > 4 && /✗|\bfail|롤백|재발|incomplete|버그/i.test(line)) {
|
|
6231
|
+
taskLogFails.push({ title: line.replace(/^[-*]\s*/, '').slice(0, 100), block: line });
|
|
6232
|
+
}
|
|
6233
|
+
}
|
|
6234
|
+
}
|
|
6235
|
+
const idx = { evidenceMtime: em, decisionsMtime: dm, taskLogMtime: tm, evidence, decisions, taskLogFails };
|
|
6167
6236
|
_LESSONS_INDEX_CACHE.set(cacheKey, idx);
|
|
6168
6237
|
return idx;
|
|
6169
6238
|
}
|
|
@@ -6815,7 +6884,8 @@ function mcpServeCmd(root) {
|
|
|
6815
6884
|
{ name: 'leerness_usage_stats', description: 'leerness 명령별 누적 호출 통계 + drift 통계', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
6816
6885
|
{ name: 'leerness_session_close', description: '세션 마감 — handoff/current-state/task-log 자동 갱신', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
6817
6886
|
{ name: 'leerness_skill_suggest', description: '1.9.53 — 사용 패턴 자동 분석 → 새 skill 후보 제안 (Hermes-style 자동 학습)', inputSchema: { type: 'object', properties: { path: { type: 'string' }, min: { type: 'number' }, days: { type: 'number' } } } },
|
|
6818
|
-
{ name: 'leerness_lessons', description: '1.9.7/54 — 과거 결정·실수 자동 회수 (--auto: 현재 task 키워드 자동 추출)', inputSchema: { type: 'object', properties: { path: { type: 'string' }, query: { type: 'string' }, auto: { type: 'boolean' }, limit: { type: 'number' } } } }
|
|
6887
|
+
{ name: 'leerness_lessons', description: '1.9.7/54 — 과거 결정·실수 자동 회수 (--auto: 현재 task 키워드 자동 추출)', inputSchema: { type: 'object', properties: { path: { type: 'string' }, query: { type: 'string' }, auto: { type: 'boolean' }, limit: { type: 'number' } } } },
|
|
6888
|
+
{ name: 'leerness_task_export', description: '1.9.60/66 — leerness task → Claude Code TodoWrite 호환 JSON (외부 AI 양방향 sync)', inputSchema: { type: 'object', properties: { path: { type: 'string' }, to: { type: 'string' } } } }
|
|
6819
6889
|
];
|
|
6820
6890
|
|
|
6821
6891
|
function send(obj) {
|
|
@@ -6857,6 +6927,7 @@ function mcpServeCmd(root) {
|
|
|
6857
6927
|
case 'leerness_session_close': cliArgs = ['session', 'close', targetPath]; break;
|
|
6858
6928
|
case 'leerness_skill_suggest': cliArgs = ['skill', 'suggest', '--path', targetPath, '--json', ...(args.min ? ['--min', String(args.min)] : []), ...(args.days ? ['--days', String(args.days)] : [])]; break;
|
|
6859
6929
|
case 'leerness_lessons': cliArgs = ['lessons', '--path', targetPath, ...(args.auto ? ['--auto'] : []), ...(args.query ? ['--query', args.query] : []), ...(args.limit ? ['--limit', String(args.limit)] : [])]; break;
|
|
6930
|
+
case 'leerness_task_export': cliArgs = ['task', 'export', '--path', targetPath, ...(args.to ? ['--to', args.to] : ['--json'])]; break;
|
|
6860
6931
|
default:
|
|
6861
6932
|
return send({ jsonrpc: '2.0', id, error: { code: -32601, message: `Unknown tool: ${name}` } });
|
|
6862
6933
|
}
|