leerness 1.36.76 → 1.36.78
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 +24 -0
- package/README.ko.md +1 -1
- package/README.md +6 -6
- package/bin/leerness.js +103 -41
- package/lib/agents.js +14 -1
- package/lib/mcp-tools.js +111 -110
- package/lib/team.js +31 -11
- package/package.json +1 -1
- package/scripts/e2e.js +111 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.36.78 — 2026-07-28 — codex 10차 홀리스틱 헌트: 오케스트레이션/분석 명령군 7건 수정 (병렬 적대검증 8건 중 7 CONFIRMED·1 REFUTED)
|
|
4
|
+
|
|
5
|
+
표면 회전으로 team/agents·constraints/intent·contract·milestones 를 헌트. 8건 보고를 8개 독립 에이전트로 병렬 재현(워크플로) → 7 CONFIRMED 수정, 1 REFUTED(pulse).
|
|
6
|
+
|
|
7
|
+
- **F1 team 스토어 형상 무효 → 데이터 손실**: 객체 루트(`{teams:[...]}`)/문자열 members 를 조용히 `[]`로 coerce → list 는 0건(무경고), 이어지는 add 가 원본을 통째로 덮었다. previews/state 규율 적용 — 로더가 invalid 반환, 변경 거부(원본 보존), 유효 항목 members/personas 배열 정규화(string→[] 크래시 제거).
|
|
8
|
+
- **F2 constraints 무효/중복 저장**: `--constraint` 가 `kind:detail` 형식 위반(콜론 없음/빈 kind/빈 detail)이면 조용히 버려지고 exit 0 + 빈 플랫폼을 쓰던 거짓 성공 → 저장 없이 거부. 동일 {kind,detail} 중복 누적 방지(멱등).
|
|
9
|
+
- **F3 손상 constraints 읽기가 기본값을 권위처럼 노출**: list/check 가 손상 store 를 조용히 defaults 로 폴백 → `corruptStore` 표기 + 경고(커스텀 제약 미반영 명시). add 는 이미 fail-closed.
|
|
10
|
+
- **F4 team --json 계약/exit 불일치**: add/remove/show/preview/deploy 의 --json 이 사람용 텍스트를 뱉고, deploy 는 사람모드 exit 1 vs --json exit 0 이던 것 → 전 서브명령 구조화 JSON + exit 일치.
|
|
11
|
+
- **F5 광고된 `agents recommend` 미구현**: review-request 가 안내하는 명령이 핸들러 부재로 usage(exit 1)만 뱉던 것 → 내부 `_recommendAgent` 를 정식 서브명령으로 노출(구조화 JSON).
|
|
12
|
+
- **F6 빈 contract 공허 통과**: 선언 0건 spec 이 항상 `ok:true`(CI 에서 빈/오탈자 spec 통과 위험) → 검증 불가로 거부, 의도적이면 `--allow-empty`.
|
|
13
|
+
- **F8 1태그 ETA 조작**: 태그 1개면 경과 구간이 없어 속도 관측 불가인데 `days=max(1,0)` 로 avg=1 을 지어내 구체 ETA 를 보였다 → 유효 태그 2+ & 양의 구간일 때만 속도/ETA(아니면 avg=0·eta=null). milestones + round-history 양쪽.
|
|
14
|
+
- **F7 REFUTED**: pulse 는 `.json` 만 손상시켜도 MD 프로젝션 폴백이 정확한 카운트를 복구 — 4파일 모두 지워야 0(진짜 빈 상태). 설계가 이미 옳아 무변경.
|
|
15
|
+
- **(R-0001 검수 20회전이 제 수정의 불완전성 3건 차단)**: ① team add 락 획득 후 재로드가 여전히 무효→[] coerce 라 대기 중 스토어가 무효로 바뀌면 덮어썼다 → 락내 재로드도 checked. ② deploy execute --json 이 gate JSON 후 사람 로그+inherited child stdout 을 뱉어 exact-once JSON 위반 → 자식 출력 캡처 후 단일 JSON. ③ `typeof []==='object'` 라 배열형 platforms(`{"platforms":[]}`)가 손상으로 안 잡혀 add 가 덮어썼다 → 배열 명시 배제 + add 도 checked 로더 fail-closed.
|
|
16
|
+
- 검증: 7건 재현→수정→재확인 실측(F8 은 실 git repo) + 검수 3건 재확인, selftest 334, e2e +1(15 단언).
|
|
17
|
+
|
|
18
|
+
## 1.36.77 — 2026-07-25 — UR-0061/0066 MCP 완결: leerness_clarify + leerness_preview (86→88 도구)
|
|
19
|
+
|
|
20
|
+
clarify/preview 워크플로가 CLI 전용이라 MCP 전용 에이전트(Claude Desktop 등)는 질문·시안 승인 흐름을 쓸 수 없던 갭.
|
|
21
|
+
|
|
22
|
+
- **`leerness_clarify`** (read-only): 요청 텍스트 모호성 신호 → 질문 목록 JSON.
|
|
23
|
+
- **`leerness_preview`** (safe-write): action 파라미터로 add/list/show/approve/revise/mockup 전체 — 시안 스캐폴드 생성·승인·수정요구를 MCP 로. revise 노트 필수 계약 유지(isError 실측).
|
|
24
|
+
- README 도구 수 86→88 정합(selftest 문서-drift 가드가 잡아준 대로).
|
|
25
|
+
- 검증: MCP 왕복 실측(clarify ambiguous·add→mockup→approve·revise 거부), selftest 334, e2e +1.
|
|
26
|
+
|
|
3
27
|
## 1.36.76 — 2026-07-25 — 9차 헌트 이월 3건 종결 (P1 1·P2 2) — 헌트 8/8 전건 처리
|
|
4
28
|
|
|
5
29
|
- **P1 (#3) 손상 아카이브 복원 거부**: 잘린 UTF-8 아카이브가 U+FFFD 를 품은 채 "정상 복원"돼 손상 데이터가 active 로 유입되고 아카이브 원본이 소비되던 것(codex 정확 재현으로 확정 — 지난 라운드 제 절단 변형은 미재현이라 이월했었다). 치환문자 감지 시 복원 중단·아카이브 무변경.
|
package/README.ko.md
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
> **어떤 언어, 어떤 AI 에이전트로 작업하든 — "증거 없이는 끝났다고 말할 수 없게" 만드는 AI 코딩 운영 레이어.** 코드를 대신 쓰는 도구가 아니라, AI 에이전트의 **기억·인수인계·검증·감사·보안 가드**를 프로젝트에 영속화하는 CLI + MCP 서버입니다.
|
|
15
15
|
|
|
16
|
-
[](https://www.npmjs.com/package/leerness) · ](https://www.npmjs.com/package/leerness) ·  · **런타임 의존성 0** · **install-script 0** · offline-first · Node ≥ 18 · MIT
|
|
17
17
|
|
|
18
18
|
---
|
|
19
19
|
|
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
> **The AI-coding operations layer that makes "done" require evidence — for any language, any AI agent.**
|
|
13
13
|
> leerness does not write code. It gives your AI agent persistent memory, verified completion, and clean handoffs — stored inside your repo as plain files, exposed via CLI + MCP.
|
|
14
14
|
|
|
15
|
-
[](https://www.npmjs.com/package/leerness) · ](https://www.npmjs.com/package/leerness) ·  · **0 runtime deps** · **0 install scripts** · offline-first · Node ≥ 18 · MIT
|
|
16
16
|
|
|
17
17
|
**🇰🇷 한국어 전문: [README.ko.md](./README.ko.md)**
|
|
18
18
|
|
|
@@ -122,7 +122,7 @@ MIT
|
|
|
122
122
|
<!-- leerness:project-readme:start -->
|
|
123
123
|
## Leerness Project Harness
|
|
124
124
|
|
|
125
|
-
이 프로젝트는 Leerness v1.36.
|
|
125
|
+
이 프로젝트는 Leerness v1.36.78 하네스를 사용합니다. AI 에이전트는 작업 전 `leerness handoff`로 컨텍스트를 적재하고, 작업 후 `leerness check`/`leerness audit`/`leerness session close`를 수행해야 합니다.
|
|
126
126
|
|
|
127
127
|
### 정체성 — AI 에이전트 운영 레이어 (UR-0030)
|
|
128
128
|
|
|
@@ -176,7 +176,7 @@ leerness memory restore decision <date|title>
|
|
|
176
176
|
|
|
177
177
|
### MCP server (외부 AI 통합)
|
|
178
178
|
|
|
179
|
-
Leerness v1.36.
|
|
179
|
+
Leerness v1.36.78는 stdio JSON-RPC MCP server를 내장합니다 — Claude Code · Cursor · Codex CLI 등 외부 AI에 **88개 도구**를 노출:
|
|
180
180
|
|
|
181
181
|
```jsonc
|
|
182
182
|
// 카테고리별
|
|
@@ -189,7 +189,7 @@ Leerness v1.36.76는 stdio JSON-RPC MCP server를 내장합니다 — Claude Cod
|
|
|
189
189
|
// • Workflow: session_close / agents_list / task_export / env_check / usage_stats / reuse_map / whats_new
|
|
190
190
|
|
|
191
191
|
// MCP server 실행: leerness mcp serve
|
|
192
|
-
// tools/list 응답:
|
|
192
|
+
// tools/list 응답: 88 도구
|
|
193
193
|
```
|
|
194
194
|
|
|
195
195
|
### Autonomous mode (자율 모드)
|
|
@@ -197,7 +197,7 @@ Leerness v1.36.76는 stdio JSON-RPC MCP server를 내장합니다 — Claude Cod
|
|
|
197
197
|
`<<autonomous-loop-dynamic>>` 신호만 보내면 AI가:
|
|
198
198
|
1) 다음 라운드 후보 선정 → 2) 코드 변경 → 3) 회귀 테스트 갱신 → 4) 전체 e2e 스위트 통과 → 5) npm publish + git tag → 6) main push → 7) session close → 8) 다음 라운드 예약.
|
|
199
199
|
|
|
200
|
-
현재 누적: **v1.9.x → 1.36.
|
|
200
|
+
현재 누적: **v1.9.x → 1.36.78 릴리스 태그 이력** (수백 라운드) · _reports/는 비공개 보존.
|
|
201
201
|
|
|
202
202
|
### 성능 가이드
|
|
203
203
|
|
|
@@ -235,5 +235,5 @@ leerness release pack --close --auto-main-push
|
|
|
235
235
|
- `.harness/session-handoff.md`: 다음 세션 인수인계 (자동 작성)
|
|
236
236
|
- `.harness/lessons.md` / `decisions.md` / `rules.md`: 영구 메모리 (5 surface)
|
|
237
237
|
|
|
238
|
-
Last synced by Leerness v1.36.
|
|
238
|
+
Last synced by Leerness v1.36.78: 2026-07-28
|
|
239
239
|
<!-- leerness:project-readme:end -->
|
package/bin/leerness.js
CHANGED
|
@@ -34,7 +34,7 @@ const { CAPABILITY_SURFACE, POWERFUL_COMMANDS, ADAPTERS, REUSE_CATEGORIES, REUSE
|
|
|
34
34
|
const { tokenizeForRank: _tokenizeForRank, expandQuery: _expandQuery, scoreHits: _scoreHits, suggestTerms: _suggestTerms } = require('../lib/search-core'); // 1.36.23: memory search 랭킹 코어(순수·0-deps)
|
|
35
35
|
const { findCorruptedStateJson: _findCorruptedStateJson } = require('../lib/state-integrity'); // 1.36.1 (클린룸 리뷰 FN): .harness/*.json 상태 무결성 (audit/health/check 공유)
|
|
36
36
|
|
|
37
|
-
const VERSION = '1.36.
|
|
37
|
+
const VERSION = '1.36.78';
|
|
38
38
|
|
|
39
39
|
// 1.9.290 (UR-0037, Codex gpt-5.5 #4 수렴): CLI 전용 부작용은 require 시 실행하지 않는다.
|
|
40
40
|
// 이전: warning listener 제거 / NODE_OPTIONS 변경 / chcp IIFE 가 top-level 즉시 실행 → require('harness') 시 호스트 프로세스 오염.
|
|
@@ -2400,12 +2400,17 @@ function _computeRoundHistory(root) {
|
|
|
2400
2400
|
result.nextMilestone = next;
|
|
2401
2401
|
result.roundsToNextMilestone = next - result.roundCount;
|
|
2402
2402
|
}
|
|
2403
|
-
// 일일 평균
|
|
2403
|
+
// 일일 평균 — 1.36.78 (F8): 태그 2+ & 양의 구간일 때만(1개면 속도 관측 불가 → 0 유지)
|
|
2404
2404
|
const first = new Date(result.firstTagAt).getTime();
|
|
2405
2405
|
const last = new Date(result.latestTagAt).getTime();
|
|
2406
|
-
const
|
|
2407
|
-
|
|
2408
|
-
|
|
2406
|
+
const spanDays = (last - first) / 86400000;
|
|
2407
|
+
if (tags.length >= 2 && spanDays > 0) {
|
|
2408
|
+
result.daysActive = Math.max(1, Math.floor(spanDays));
|
|
2409
|
+
result.avgRoundsPerDay = Math.round((result.roundCount / spanDays) * 100) / 100;
|
|
2410
|
+
} else {
|
|
2411
|
+
result.daysActive = 0;
|
|
2412
|
+
result.avgRoundsPerDay = 0;
|
|
2413
|
+
}
|
|
2409
2414
|
} catch {}
|
|
2410
2415
|
return result;
|
|
2411
2416
|
}
|
|
@@ -2451,16 +2456,23 @@ function _computeMilestones(root) {
|
|
|
2451
2456
|
}
|
|
2452
2457
|
}
|
|
2453
2458
|
// 다음 마일스톤 + 평균 속도로 ETA 계산
|
|
2459
|
+
// 1.36.78 (10차 헌트 F8): 태그 1개면 경과 구간이 없어 속도를 관측할 수 없다 — 종전엔 days=max(1,0)=1 로
|
|
2460
|
+
// avgPerDay=1 을 지어내 구체적 ETA 를 보였다. 유효 태그 2+ 이고 양의 구간일 때만 속도/ETA 산출(아니면 null).
|
|
2454
2461
|
const lastTime = new Date(tags[tags.length - 1].date).getTime();
|
|
2455
|
-
const
|
|
2456
|
-
const avgPerDay = (tags.length / days);
|
|
2457
|
-
result.avgRoundsPerDay = Math.round(avgPerDay * 100) / 100;
|
|
2462
|
+
const spanDays = (lastTime - baselineTime) / 86400000;
|
|
2458
2463
|
const nextM = milestones.find(m => m > tags.length);
|
|
2459
|
-
if (
|
|
2460
|
-
const
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
+
if (tags.length >= 2 && spanDays > 0) {
|
|
2465
|
+
const avgPerDay = tags.length / spanDays;
|
|
2466
|
+
result.avgRoundsPerDay = Math.round(avgPerDay * 100) / 100;
|
|
2467
|
+
if (nextM != null) {
|
|
2468
|
+
const roundsRemaining = nextM - tags.length;
|
|
2469
|
+
const etaDays = Math.ceil(roundsRemaining / avgPerDay);
|
|
2470
|
+
const etaDate = new Date(Date.now() + etaDays * 86400000).toISOString().split('T')[0];
|
|
2471
|
+
result.next = { milestone: nextM, roundsRemaining, etaDays, etaDate };
|
|
2472
|
+
}
|
|
2473
|
+
} else if (nextM != null) {
|
|
2474
|
+
// 속도 관측 불가 — 다음 마일스톤은 표시하되 ETA 는 null(추정 없음)
|
|
2475
|
+
result.next = { milestone: nextM, roundsRemaining: nextM - tags.length, etaDays: null, etaDate: null };
|
|
2464
2476
|
}
|
|
2465
2477
|
} catch {}
|
|
2466
2478
|
return result;
|
|
@@ -3375,10 +3387,10 @@ function _selfTestCases() {
|
|
|
3375
3387
|
const stateLock = s.includes("const _mut = ['start', 'record', 'verify', 'handoff'].includes(sub || '')")
|
|
3376
3388
|
&& s.includes("_withLock(path.join(_leernessStateDir(root), 'state.json'), () => stateCmd(root, sub, ...args))")
|
|
3377
3389
|
&& s.includes("!_heldLocks.has(path.join(_leernessStateDir(root), 'state.json') + '.lock')");
|
|
3378
|
-
// #2: team add 가 락 안 재로드→중복검사→저장 + EPERM
|
|
3390
|
+
// #2: team add 가 락 안 재로드→중복검사→저장 + EPERM 재시도. 1.36.78: 락내 재로드가 checked 로더로(형상무효 레이스 거부).
|
|
3379
3391
|
const teamSrc = read(path.join(path.dirname(__filename), '..', 'lib', 'team.js'));
|
|
3380
|
-
const teamLock = teamSrc.includes('
|
|
3381
|
-
const teamWired = s.includes('_detectShellCtx, arg, has, _withLock }');
|
|
3392
|
+
const teamLock = teamSrc.includes('_loadTeamsChecked(root)') && teamSrc.includes("deps._withLock(require('path').join(root, '.harness', 'teams.json'), _doAdd)") && teamSrc.includes("e.code === 'EPERM'") && teamSrc.includes('_rechk.invalid');
|
|
3393
|
+
const teamWired = s.includes('_loadTeamsChecked, _saveTeams, _detectShellCtx, arg, has, _withLock, failJson }');
|
|
3382
3394
|
// #7: pre-wake 매처가 테이블형+YAML형 모두 (행위)
|
|
3383
3395
|
const both = ('| T-0001 | in-progress | x |\nstatus: in-progress\n'.match(/^\|\s*T-\d{4,}\s*\|\s*in-progress\s*\|/gm) || []).length === 1;
|
|
3384
3396
|
const pwWired = /_runPreWakeAudit[\s\S]{0,2200}\^\\\|\\s\*T-\\d\{4,\}\\s\*\\\|\\s\*in-progress/.test(s);
|
|
@@ -6735,7 +6747,7 @@ function commandsCmd(root) {
|
|
|
6735
6747
|
{ cmd: 'session close [path] [--json] [--auto-apply-delivered]', desc: '세션 마감 + 9 카테고리 + 자동 통합' },
|
|
6736
6748
|
{ cmd: 'resume [path]', desc: 'auto-resume-plan 적용 (1.9.203)' },
|
|
6737
6749
|
{ cmd: 'route <task-type>', desc: '작업 유형 분류 (11종)' },
|
|
6738
|
-
{ cmd: 'agents list|check|quota|dispatch|multi', desc: '외부 AI CLI 오케스트레이션 (dispatch --role 모델 라우팅 1.9.270)' },
|
|
6750
|
+
{ cmd: 'agents list|check|quota|dispatch|multi|recommend', desc: '외부 AI CLI 오케스트레이션 (dispatch --role 모델 라우팅 1.9.270)' },
|
|
6739
6751
|
{ cmd: 'roles list|set|unset|catalog|suggest|verify', desc: '모델별 역할 부여 (코딩/검수/지휘/디자인/디버그/설계/분배) — 1.9.270' },
|
|
6740
6752
|
{ cmd: 'capabilities [--json]', desc: '권한·보안 표면 공개 (무엇을 하는지 + opt-out + 주의 명령) — 1.9.272' },
|
|
6741
6753
|
{ cmd: 'state show|start|record|verify|handoff', desc: '.leerness/ JSON 상태 substrate (에이전트 간 인수인계 표준) — 1.9.278' },
|
|
@@ -6870,19 +6882,22 @@ function roundHistoryCmd(root) {
|
|
|
6870
6882
|
// 기본 catalog 6종: Stripe / OpenAI / Anthropic / GitHub / Discord / Twitter
|
|
6871
6883
|
function _platformConstraintsPath(root) { return path.join(root, '.harness', 'platform-constraints.json'); }
|
|
6872
6884
|
// 1.9.333 (UR-0025 심층): _DEFAULT_PLATFORM_CONSTRAINTS → lib/catalogs.js 로 이동 (순수 데이터, require 사용).
|
|
6885
|
+
// 1.36.78 (10차 헌트 F3): 손상 store 를 조용히 defaults 로 폴백해 "권위 있는 정상 목록"처럼 보이던 것 —
|
|
6886
|
+
// 사용자 커스텀 제약이 사라졌는데도 알 수 없었다. 손상 여부를 함께 반환해 list/check 가 표면화(read 는 폴백 유지 + 경고).
|
|
6887
|
+
function _loadPlatformConstraintsChecked(root) {
|
|
6888
|
+
const fp = _platformConstraintsPath(root);
|
|
6889
|
+
if (!exists(fp)) return { catalog: _DEFAULT_PLATFORM_CONSTRAINTS, corrupt: false };
|
|
6890
|
+
let j;
|
|
6891
|
+
try { j = JSON.parse(read(fp)); } catch { return { catalog: _DEFAULT_PLATFORM_CONSTRAINTS, corrupt: true }; }
|
|
6892
|
+
// 1.36.78 (검수 #3): typeof []==='object' 라 배열 루트/배열 platforms 가 통과하던 것 — 명시 배제(비객체 취급).
|
|
6893
|
+
if (!j || typeof j !== 'object' || Array.isArray(j) || (j.platforms != null && (typeof j.platforms !== 'object' || Array.isArray(j.platforms)))) return { catalog: _DEFAULT_PLATFORM_CONSTRAINTS, corrupt: true };
|
|
6894
|
+
const merged = { ...JSON.parse(JSON.stringify(_DEFAULT_PLATFORM_CONSTRAINTS)) };
|
|
6895
|
+
if (j.platforms) for (const k of Object.keys(j.platforms)) merged.platforms[k] = j.platforms[k];
|
|
6896
|
+
return { catalog: merged, corrupt: false };
|
|
6897
|
+
}
|
|
6873
6898
|
function _loadPlatformConstraints(root) {
|
|
6874
6899
|
try {
|
|
6875
|
-
|
|
6876
|
-
if (!exists(fp)) return _DEFAULT_PLATFORM_CONSTRAINTS;
|
|
6877
|
-
const j = JSON.parse(read(fp));
|
|
6878
|
-
// user catalog 와 default merge — user override 우선
|
|
6879
|
-
const merged = { ...JSON.parse(JSON.stringify(_DEFAULT_PLATFORM_CONSTRAINTS)) };
|
|
6880
|
-
if (j.platforms) {
|
|
6881
|
-
for (const k of Object.keys(j.platforms)) {
|
|
6882
|
-
merged.platforms[k] = j.platforms[k];
|
|
6883
|
-
}
|
|
6884
|
-
}
|
|
6885
|
-
return merged;
|
|
6900
|
+
return _loadPlatformConstraintsChecked(root).catalog;
|
|
6886
6901
|
} catch { return _DEFAULT_PLATFORM_CONSTRAINTS; }
|
|
6887
6902
|
}
|
|
6888
6903
|
function _writePlatformConstraints(root, catalog) {
|
|
@@ -7882,8 +7897,10 @@ function constraintsCmd(root, sub, ...rest) {
|
|
|
7882
7897
|
}
|
|
7883
7898
|
|
|
7884
7899
|
if (sub === 'list') {
|
|
7885
|
-
const
|
|
7886
|
-
|
|
7900
|
+
const _chk = _loadPlatformConstraintsChecked(root); // 1.36.78 (F3): 손상 표면화
|
|
7901
|
+
const catalog = _chk.catalog;
|
|
7902
|
+
if (has('--json')) { log(JSON.stringify({ ...catalog, ...(_chk.corrupt ? { corruptStore: true, note: 'platform-constraints.json 손상 — 기본값만 표시(커스텀 제약 미반영)' } : {}) }, null, 2)); return; }
|
|
7903
|
+
if (_chk.corrupt) log(yel(_t(' ⚠ platform-constraints.json 손상 — 기본값만 표시(커스텀 제약 미반영). 복구 후 재시도', ' ⚠ platform-constraints.json corrupt — showing defaults only (custom constraints omitted). Repair and retry')));
|
|
7887
7904
|
log(cyan(`# leerness constraints list (1.9.208)`));
|
|
7888
7905
|
log(` total platforms: ${Object.keys(catalog.platforms).length}`);
|
|
7889
7906
|
log('');
|
|
@@ -7904,8 +7921,10 @@ function constraintsCmd(root, sub, ...rest) {
|
|
|
7904
7921
|
if (sub === 'check') {
|
|
7905
7922
|
const text = rest.filter(x => !x.startsWith('-')).join(' ');
|
|
7906
7923
|
if (!text) return failJson(has('--json'), 'missing_query', 'Usage: leerness constraints check "<request text>"'); // 1.32.1 (15th리뷰 C2): --json 구조화
|
|
7924
|
+
const _corrupt = _loadPlatformConstraintsChecked(root).corrupt; // 1.36.78 (F3): 손상 표면화
|
|
7907
7925
|
const result = _checkRequestConstraints(root, text, _L);
|
|
7908
|
-
if (has('--json')) { log(JSON.stringify({ query: text, ...result }, null, 2)); return; }
|
|
7926
|
+
if (has('--json')) { log(JSON.stringify({ query: text, ...result, ...(_corrupt ? { corruptStore: true } : {}) }, null, 2)); return; }
|
|
7927
|
+
if (_corrupt) log(yel(_t(' ⚠ platform-constraints.json 손상 — 커스텀 제약 미반영(기본값만 대조)', ' ⚠ platform-constraints.json corrupt — custom constraints omitted (defaults only)')));
|
|
7909
7928
|
log(cyan(`# leerness constraints check (1.9.208)`));
|
|
7910
7929
|
log(` query: ${text.slice(0, 80)}${text.length > 80 ? '…' : ''}`);
|
|
7911
7930
|
log('');
|
|
@@ -7936,15 +7955,24 @@ function constraintsCmd(root, sub, ...rest) {
|
|
|
7936
7955
|
if (sub === 'add') {
|
|
7937
7956
|
const id = rest[0];
|
|
7938
7957
|
if (!id) return failJson(has('--json'), 'missing_platform_id', 'Usage: leerness constraints add <platform-id> --constraint "kind:detail"'); // 1.32.1 (15th리뷰 C2)
|
|
7939
|
-
|
|
7958
|
+
// 1.36.78 (검수 #3): add 도 checked 로더로 — 손상/배열형 store 위 변경 거부(기존 커스텀·metadata 보존).
|
|
7959
|
+
const _cchk = _loadPlatformConstraintsChecked(root);
|
|
7960
|
+
if (_cchk.corrupt) return failJson(has('--json'), 'store_corrupt', `platform-constraints.json 손상/형상 무효 — 덮어쓰기 거부: ${_platformConstraintsPath(root)} (복구 후 재시도)`);
|
|
7961
|
+
const catalog = _cchk.catalog;
|
|
7940
7962
|
const aliases = (arg('--alias', '') || '').split(',').map(s => s.trim()).filter(Boolean);
|
|
7941
7963
|
const detailRaw = arg('--constraint', '');
|
|
7942
|
-
if (!detailRaw) return failJson(has('--json'), 'missing_constraint', '--constraint "kind:detail" required'); // 1.32.1 (15th리뷰 C2)
|
|
7943
|
-
const [kind, ...rest2] = detailRaw.split(':');
|
|
7964
|
+
if (!detailRaw || detailRaw === true) return failJson(has('--json'), 'missing_constraint', '--constraint "kind:detail" required'); // 1.32.1 (15th리뷰 C2)
|
|
7965
|
+
const [kind, ...rest2] = String(detailRaw).split(':');
|
|
7966
|
+
const kindTrim = (kind || '').trim();
|
|
7944
7967
|
const detail = rest2.join(':').trim();
|
|
7968
|
+
// 1.36.78 (10차 헌트 F2): 무효 kind:detail(콜론 없음/빈 kind/빈 detail)은 조용히 버려지고 exit 0 + 빈-constraints 플랫폼을
|
|
7969
|
+
// 쓰던 것(거짓 성공). 형식 위반은 저장 없이 거부. 플랫폼 생성도 검증 통과 후로 이동.
|
|
7970
|
+
if (!kindTrim || !detail) return failJson(has('--json'), 'invalid_constraint', '--constraint 는 "kind:detail" 형식(빈 kind/detail 불가) — 예: "auth:token required"');
|
|
7945
7971
|
if (!catalog.platforms[id]) catalog.platforms[id] = { aliases: [], constraints: [] };
|
|
7946
7972
|
if (aliases.length) catalog.platforms[id].aliases = [...new Set([...(catalog.platforms[id].aliases || []), ...aliases])];
|
|
7947
|
-
|
|
7973
|
+
// 1.36.78 (F2): 동일 {kind,detail} 중복 누적 방지(멱등)
|
|
7974
|
+
const _cons = catalog.platforms[id].constraints;
|
|
7975
|
+
if (!_cons.some(c => c.kind === kindTrim && c.detail === detail)) _cons.push({ kind: kindTrim, detail });
|
|
7948
7976
|
_writePlatformConstraints(root, catalog);
|
|
7949
7977
|
if (has('--json')) { log(JSON.stringify(catalog.platforms[id], null, 2)); return; }
|
|
7950
7978
|
log(grn(_t(`✓ platform "${id}" 갱신 — constraints: ${catalog.platforms[id].constraints.length}`, `✓ platform "${id}" updated — constraints: ${catalog.platforms[id].constraints.length}`)));
|
|
@@ -9126,11 +9154,20 @@ function _saveDecisions(root, decisions) {
|
|
|
9126
9154
|
// 1.9.371 (UR-0073 Phase A): agent team 정의 레지스트리 — canonical JSON(teams.json) 주 + teams.md projection. opt-in · 정의 전용(자동 실행 X).
|
|
9127
9155
|
const teamsJsonPath = root => path.join(root, '.harness/teams.json');
|
|
9128
9156
|
const teamsPath = root => path.join(root, '.harness/teams.md');
|
|
9129
|
-
|
|
9157
|
+
// 1.36.78 (10차 헌트 F1): teams.json 이 "파싱은 되지만 형상 무효"(객체 루트 {teams:[...]}/항목 비객체/id 없음)일 때
|
|
9158
|
+
// 종전엔 조용히 []로 coerce → list 는 count:0(무경고), 이어지는 add 가 배열을 써서 원본을 통째로 덮었다(데이터 손실).
|
|
9159
|
+
// previews/state 와 동일 규율: 로더가 invalid 를 함께 반환하고 변경 진입점은 거부. 유효 항목은 members/personas 를 배열로 정규화(string→[]).
|
|
9160
|
+
function _loadTeamsChecked(root) {
|
|
9130
9161
|
const jp = teamsJsonPath(root);
|
|
9131
|
-
if (exists(jp))
|
|
9132
|
-
|
|
9133
|
-
}
|
|
9162
|
+
if (!exists(jp)) return { teams: [], invalid: false };
|
|
9163
|
+
let parsed;
|
|
9164
|
+
try { parsed = JSON.parse(read(jp)); } catch { return { teams: [], invalid: true }; }
|
|
9165
|
+
if (!Array.isArray(parsed) || parsed.some(e => !e || typeof e !== 'object' || typeof e.id !== 'string')) return { teams: [], invalid: true };
|
|
9166
|
+
const _arr = a => Array.isArray(a) ? a : (a == null ? [] : [String(a)]); // 무효 members(string) → 크래시 대신 배열 정규화
|
|
9167
|
+
const teams = parsed.map(t => Object.assign({}, t, { members: _arr(t.members), personas: _arr(t.personas) }));
|
|
9168
|
+
return { teams, invalid: false };
|
|
9169
|
+
}
|
|
9170
|
+
function _loadTeams(root) { return _loadTeamsChecked(root).teams; }
|
|
9134
9171
|
function _saveTeams(root, teams) {
|
|
9135
9172
|
const arr = Array.isArray(teams) ? teams : [];
|
|
9136
9173
|
_assertStoreParsable(teamsJsonPath(root), 'team'); // 1.36.28 (#3): 손상 teams.json 을 덮어써 기존 팀 유실 방지
|
|
@@ -9144,7 +9181,7 @@ const _team = require('../lib/team');
|
|
|
9144
9181
|
const _tgl = require('../lib/toggles'); // 1.36.30: 기능 토글 (그래프 ⚙ 탭 연동 — gate/lens/auto-graph/delegation-brief)
|
|
9145
9182
|
const _clar = require('../lib/clarify'); // 1.36.51 (UR-0061): 모호성 질문 + 미리보기 승인 워크플로
|
|
9146
9183
|
const _tech = require('../lib/tech-profile'); // 1.36.53 (UR-0062): 기술 프로필 (언어·서비스 감지 + 변경 이력)
|
|
9147
|
-
function teamCmd(root, sub, id, opts = {}) { return _team.teamCmd(root, sub, id, opts, { VERSION, _loadTeams, _saveTeams, _detectShellCtx, arg, has, _withLock }); } // 1.36.31: add 경합 락
|
|
9184
|
+
function teamCmd(root, sub, id, opts = {}) { return _team.teamCmd(root, sub, id, opts, { VERSION, _loadTeams, _loadTeamsChecked, _saveTeams, _detectShellCtx, arg, has, _withLock, failJson }); } // 1.36.31: add 경합 락 · 1.36.78: 형상무효 가드 + failJson
|
|
9148
9185
|
|
|
9149
9186
|
// 1.9.112: 전용 lessons.md (Memory Write Surface 5번째)
|
|
9150
9187
|
const lessonsPath = root => path.join(root, '.harness/lessons.md');
|
|
@@ -18975,6 +19012,25 @@ function _mcpToCliArgs(name, args, targetPath) {
|
|
|
18975
19012
|
case 'leerness_state_show':
|
|
18976
19013
|
cliArgs = ['state', 'show', '--path', targetPath, '--json'];
|
|
18977
19014
|
break;
|
|
19015
|
+
// 1.36.77 (UR-0061/0066): clarify/preview 워크플로 MCP 노출 — MCP 전용 에이전트도 질문·시안 승인 흐름 사용
|
|
19016
|
+
case 'leerness_clarify':
|
|
19017
|
+
cliArgs = ['clarify', String(args.text || ''), '--path', targetPath, '--json'];
|
|
19018
|
+
break;
|
|
19019
|
+
case 'leerness_preview': {
|
|
19020
|
+
const _pa = String(args.action || 'list');
|
|
19021
|
+
cliArgs = ['preview', _pa, '--path', targetPath, '--json'];
|
|
19022
|
+
if (_pa === 'add') {
|
|
19023
|
+
cliArgs.splice(2, 0, String(args.title || ''));
|
|
19024
|
+
if (args.design) cliArgs.push('--design', String(args.design));
|
|
19025
|
+
if (args.features) cliArgs.push('--features', String(args.features));
|
|
19026
|
+
if (args.mockup) cliArgs.push('--mockup', String(args.mockup));
|
|
19027
|
+
} else if (['show', 'approve', 'revise', 'mockup'].includes(_pa)) {
|
|
19028
|
+
cliArgs.splice(2, 0, String(args.id || ''));
|
|
19029
|
+
if (_pa === 'revise' && args.note) cliArgs.push('--note', String(args.note));
|
|
19030
|
+
if (_pa === 'mockup' && args.force) cliArgs.push('--force');
|
|
19031
|
+
}
|
|
19032
|
+
break;
|
|
19033
|
+
}
|
|
18978
19034
|
case 'leerness_state_start':
|
|
18979
19035
|
cliArgs = ['state', 'start', String(args.goal || ''), '--path', targetPath, '--json'];
|
|
18980
19036
|
if (args.agent) cliArgs.push('--agent', String(args.agent));
|
|
@@ -22344,6 +22400,12 @@ function contractVerifyCmd(specPath, implPath) {
|
|
|
22344
22400
|
const _fe = f.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
22345
22401
|
if (!new RegExp(`(?<![\\w$])${_fe}(?![\\w$])`).test(implSrc)) fieldMissing.push(f);
|
|
22346
22402
|
}
|
|
22403
|
+
// 1.36.78 (10차 헌트 F6): 선언(강함수+필드)이 0건인 spec 은 검사할 게 없어 항상 ok:true 로 "공허 통과"했다
|
|
22404
|
+
// (CI 에서 빈/오탈자 spec 이 계약을 통과시키는 위험). 검증 불가로 취급 — 명시적 --allow-empty 로만 허용.
|
|
22405
|
+
if (declaredSpec.size === 0 && fieldSpec.size === 0 && !has('--allow-empty')) {
|
|
22406
|
+
failJson(_j, 'empty_contract', 'spec 에 검증할 함수/필드 선언이 없습니다 — 공허 통과 방지(의도된 빈 계약이면 --allow-empty). "- name(args)" 또는 "## Fields\\n- fieldName" 으로 선언하세요');
|
|
22407
|
+
return process.exit(process.exitCode || 1);
|
|
22408
|
+
}
|
|
22347
22409
|
// 출력
|
|
22348
22410
|
if (has('--json')) {
|
|
22349
22411
|
const okJson = missing.length === 0 && fieldMissing.length === 0;
|
|
@@ -22954,7 +23016,7 @@ STATUS & DIAGNOSTICS
|
|
|
22954
23016
|
VERIFICATION (evidence-gated "done")
|
|
22955
23017
|
verify-claim <T-ID> [--run-tests] [--test-cmd "..."] [--json]
|
|
22956
23018
|
Check evidence vs reality (stub / fake-test / inflated-count detection)
|
|
22957
|
-
contract verify <spec.md> <impl.js> [--json]
|
|
23019
|
+
contract verify <spec.md> <impl.js> [--json] [--allow-empty] Spec <-> implementation match (empty spec rejected unless --allow-empty)
|
|
22958
23020
|
verify-code [path] [--build] [--bench] Run tests/lint/typecheck, record evidence
|
|
22959
23021
|
gate [path] One-call CI gate: verify + audit + scan + encoding + lazy
|
|
22960
23022
|
lens [code|design|docs|test|security|database|debug] [--json] Quality self-question lenses
|
package/lib/agents.js
CHANGED
|
@@ -59,6 +59,19 @@ function agentsCmd(root, sub, args = [], deps = {}) {
|
|
|
59
59
|
return;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
// 1.36.78 (10차 헌트 F5): review-request 가 광고하는 `agents recommend <task>` 가 핸들러 부재로 usage(exit 1)만 뱉던 것.
|
|
63
|
+
// 내부 _recommendAgent 로직을 정식 서브명령으로 노출 — 작업 텍스트/유형 → 적합 CLI + 이유.
|
|
64
|
+
if (sub === 'recommend') {
|
|
65
|
+
const task = _taskArg();
|
|
66
|
+
if (!task) { failJson(has('--json'), 'missing_task', 'Usage: leerness agents recommend "<작업 설명 또는 유형>"'); return; }
|
|
67
|
+
const rec = _recommendAgent(task) || { target: null, reason: '' };
|
|
68
|
+
if (has('--json')) { log(JSON.stringify({ ok: true, task, recommended: rec.target, reason: rec.reason }, null, 2)); return; }
|
|
69
|
+
log(`# leerness agents recommend (1.36.78)`);
|
|
70
|
+
log(` 작업: ${task.slice(0, 80)}${task.length > 80 ? '…' : ''}`);
|
|
71
|
+
if (rec.target) { log(` 💡 추천: ${rec.target}${rec.reason ? ` — ${rec.reason}` : ''}`); }
|
|
72
|
+
else { log(` (특정 추천 없음 — 기본 provider 사용. leerness agents list 로 활성 확인)`); }
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
62
75
|
if (sub === 'check') {
|
|
63
76
|
// list의 alias, 단 명시적 재확인 (JSON 출력 기본)
|
|
64
77
|
// 1.9.157: Provider Registry 통합
|
|
@@ -467,7 +480,7 @@ function agentsCmd(root, sub, args = [], deps = {}) {
|
|
|
467
480
|
return;
|
|
468
481
|
}
|
|
469
482
|
|
|
470
|
-
fail('사용법: leerness agents list|check|quota|dispatch|bench [--write] "<task>" [--to <id>]');
|
|
483
|
+
fail('사용법: leerness agents list|check|quota|dispatch|multi|bench|recommend [--write] "<task>" [--to <id>]');
|
|
471
484
|
return process.exit(1);
|
|
472
485
|
}
|
|
473
486
|
|
package/lib/mcp-tools.js
CHANGED
|
@@ -1,110 +1,111 @@
|
|
|
1
|
-
// lib/mcp-tools.js — MCP 도구 정의 단일출처 (순수 데이터, 부작용 0)
|
|
2
|
-
// 1.9.297 (UR-0025 5단계): bin/harness.js 의 const TOOLS 를 분리. tools/list 응답 + _mcpToolCount() 단일 출처.
|
|
3
|
-
// 이전: _mcpToolCount 가 __filename regex 로 self-count(Codex #5 취약) → 이제 module.exports.length 로 정확.
|
|
4
|
-
'use strict';
|
|
5
|
-
|
|
6
|
-
module.exports = [
|
|
7
|
-
{ name: 'leerness_handoff', requiredTier: 'safe-write', description: '워크스페이스 컨텍스트(plan/progress/decisions) 적재', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
8
|
-
{ name: 'leerness_drift_check', requiredTier: 'read-only', description: '1.9.136 — AI 에이전트 leerness 미사용 drift 자동 감지 JSON ({ root, score, level, signals[], healthy }). 5+ 신호 + 4단계 레벨 (🟢 healthy / 🟡 warning / 🟠 caution / 🔴 critical). 보안 신호 통합 (1.9.78)', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
9
|
-
{ name: 'leerness_audit', requiredTier: 'safe-write', description: '1.9.102 — 워크스페이스 일관성 감사 JSON (warnings/failures/fixed/healthy + findings[]. kind 11종: design_dup/design_system_default/reuse_map_empty/milestone_unlinked/handoff_not_generated/current_state_stale/readme_version_mismatch/npm_cve/gitignore_missing_secrets/env_keys_missing/strict_promoted)', inputSchema: { type: 'object', properties: { path: { type: 'string' }, fix: { type: 'boolean' }, strict: { type: 'boolean' } } } },
|
|
10
|
-
{ name: 'leerness_verify_claim', requiredTier: 'read-only', description: 'AI 거짓 완료 자동 검증 (evidence 파일 + 실 테스트 실행). 1.9.309(UR-0048): done/완료 주장은 evidence(수정파일+테스트) 기본 강제 — 증거 없는 done 은 FAIL(exit 1). lenient=true 로 완화, runTests/strictClaims 추가 가능. 응답 verdict.evidenceComplete 포함.', inputSchema: { type: 'object', properties: { taskId: { type: 'string' }, path: { type: 'string' }, runTests: { type: 'boolean' }, strictClaims: { type: 'boolean' }, lenient: { type: 'boolean' } }, required: ['taskId'] } },
|
|
11
|
-
{ name: 'leerness_verify_claim_all', requiredTier: 'read-only', description: '1.33.3 — 모든 done/완료 주장을 한 번에 검증(CI·스케일). progress-tracker 의 done 항목 전부를 verify-claim 정밀 검사(파일 존재·스텁·부풀린 카운트·증거 완전성)로 일괄 점검. 응답 { ok, total, failed, results:[{id,ok,reasons}] }. 세션 마감 전 "내 완료 주장 전부 증거와 맞는가?" 자가 점검용. runTests/strictClaims/lenient 추가 가능.', inputSchema: { type: 'object', properties: { path: { type: 'string' }, runTests: { type: 'boolean' }, strictClaims: { type: 'boolean' }, lenient: { type: 'boolean' } } } },
|
|
12
|
-
{ name: 'leerness_contract_verify', requiredTier: 'read-only', description: '명세 ↔ 구현 함수/필드 일치 자동 검사', inputSchema: { type: 'object', properties: { spec: { type: 'string' }, impl: { type: 'string' } }, required: ['spec', 'impl'] } },
|
|
13
|
-
{ name: 'leerness_agents_list', requiredTier: 'read-only', description: '외부 AI CLI 가용성 표 (claude/codex/agy/copilot 상태 + 환경변수 활성화 여부)', inputSchema: { type: 'object', properties: {} } },
|
|
14
|
-
{ name: 'leerness_reuse_map', requiredTier: 'read-only', description: '워크스페이스 중복 함수/capability 자동 감지 (--all-apps + fuzzy 매칭)', inputSchema: { type: 'object', properties: { path: { type: 'string' }, allApps: { type: 'boolean' }, strictElements: { type: 'boolean' } } } },
|
|
15
|
-
{ name: 'leerness_whats_new', requiredTier: 'read-only', description: 'CHANGELOG 차분 자동 추출 (from → to 사이 신규 명령/플래그/파일). 1.36.69: 기본 최신 30개 버전만 반환(응답 폭주 방지) — 응답의 totalVersions/shown/truncated 로 절단 여부 확인, 더 필요하면 limit(개수) 또는 all=true(전체, 대용량 주의) 지정.', inputSchema: { type: 'object', properties: { from: { type: 'string' }, to: { type: 'string' }, limit: { type: 'number' }, all: { type: 'boolean' } } } },
|
|
16
|
-
{ name: 'leerness_usage_stats', requiredTier: 'read-only', description: 'leerness 명령별 누적 호출 통계 + drift 통계', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
17
|
-
{ name: 'leerness_session_close', requiredTier: 'safe-write', description: '1.9.103 — 세션 마감 JSON (handoff/current-state/task-log 갱신 + taskCounts + rules + skillCandidates + drift + topCommands + mcpStats). 외부 AI가 마감 통계 자동 회수', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
18
|
-
{ name: 'leerness_skill_suggest', requiredTier: 'read-only', description: '1.9.53 — 사용 패턴 자동 분석 → 새 skill 후보 제안 (Hermes-style 자동 학습)', inputSchema: { type: 'object', properties: { path: { type: 'string' }, min: { type: 'number' }, days: { type: 'number' } } } },
|
|
19
|
-
{ name: 'leerness_lessons', requiredTier: 'read-only', description: '1.9.7/54 — 과거 결정·실수 자동 회수 (--auto: 현재 task 키워드 자동 추출)', inputSchema: { type: 'object', properties: { path: { type: 'string' }, query: { type: 'string' }, auto: { type: 'boolean' }, limit: { type: 'number' } } } },
|
|
20
|
-
{ name: 'leerness_task_export', requiredTier: 'project-write', description: '1.9.60/66 — leerness task → Claude Code TodoWrite 호환 JSON (외부 AI 양방향 sync)', inputSchema: { type: 'object', properties: { path: { type: 'string' }, to: { type: 'string' } } } },
|
|
21
|
-
{ name: 'leerness_env_check', requiredTier: 'read-only', description: '1.9.71/73 — .env vs .env.example 동기화 검사 (보안: 키만, 값 미노출). exit 1 if 누락 키 있음', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
22
|
-
{ name: 'leerness_brainstorm', requiredTier: 'read-only', description: '1.9.16/72/77 — 누적 컨텍스트(decisions+skills+tasks+rules+evidence+lessons+skillHistory+taskLogFails) 자원 회수. 외부 AI가 새 작업 시작 전 호출', inputSchema: { type: 'object', properties: { topic: { type: 'string' }, path: { type: 'string' }, allApps: { type: 'boolean' } }, required: ['topic'] } },
|
|
23
|
-
{ name: 'leerness_skill_match', requiredTier: 'safe-write', description: '1.9.45/50/83 — 사용자 task 키워드에 매칭되는 설치된 skill 추천 (jaccard 또는 embedding). 1.9.68 rolling history 자동 누적', inputSchema: { type: 'object', properties: { query: { type: 'string' }, path: { type: 'string' }, useEmbedding: { type: 'boolean' } }, required: ['query'] } },
|
|
24
|
-
{ name: 'leerness_skill_list', requiredTier: 'read-only', description: '1.9.84 — 워크스페이스에 설치된 skill 목록 + 사용 횟수 + 출처 (catalog/user). 외부 AI가 사용 가능한 skill 조회', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
25
|
-
{ name: 'leerness_health', requiredTier: 'read-only', description: '1.9.85/86 — 종합 헬스 체크 (drift + 보안 + skills + MCP + tasks + issues 배열). 외부 AI가 워크스페이스 상태 한 번에 확인', inputSchema: { type: 'object', properties: { path: { type: 'string' }, strict: { type: 'boolean' } } } },
|
|
26
|
-
{ name: 'leerness_skill_search', requiredTier: 'read-only', description: '1.9.90/91 — capability 배열에서 부분 일치하는 skill 검색 (substring + case-insensitive). skill match와 다른 정확 매칭', inputSchema: { type: 'object', properties: { capability: { type: 'string' }, path: { type: 'string' } }, required: ['capability'] } },
|
|
27
|
-
{ name: 'leerness_skill_info', requiredTier: 'read-only', description: '1.9.92 — 개별 skill 상세 조회 (version/capabilities/sources/patterns/usage/optimizations). 외부 AI가 skill 능력 정확히 파악', inputSchema: { type: 'object', properties: { id: { type: 'string' }, path: { type: 'string' } }, required: ['id'] } },
|
|
28
|
-
{ name: 'leerness_benchmark', requiredTier: 'read-only', description: '1.9.46/51/94 — 6 차원 점수 + 검수 시나리오 (--scenario) 결과 JSON. 외부 AI가 워크스페이스 leerness 활용 점수 확인', inputSchema: { type: 'object', properties: { path: { type: 'string' }, scenario: { type: 'string' } } } },
|
|
29
|
-
{ name: 'leerness_lazy_detect', requiredTier: 'read-only', description: '1.9.101 — 게으른 작업 자동 감지 결과 JSON (evidence 없는 done / empty handoff / no test run / TODO 미추적 / blocker no-next-action 등). 외부 AI가 거짓 완료 신호 사전 점검', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
30
|
-
{ name: 'leerness_retro', requiredTier: 'read-only', description: '1.9.104 — 4세션 누적 회고 보고서 JSON (statusCounts/focusNext/skillUsage/recentDecisions/durations/activeRules/fixSignals/passSignals/totalOptimizations). 외부 AI가 누적 패턴 자동 학습. 1.36.71: data.rows 는 기본 최신 30행(rowsTotal/rowsShown/rowsTruncated 로 절단 확인, rowsTotal 은 --days 창이 아니라 전체 task 수), 더 필요하면 limit(개수)/all=true(전체) 지정', inputSchema: { type: 'object', properties: { path: { type: 'string' }, days: { type: 'number' }, allApps: { type: 'boolean' }, limit: { type: 'number' }, all: { type: 'boolean' } } } },
|
|
31
|
-
{ name: 'leerness_task_add', requiredTier: 'safe-write', description: '1.9.105 — progress-tracker.md 에 새 task 추가 (양방향 제어 완성). 외부 AI가 사용자 요청을 task로 즉시 등록. 인자: { text (required), status?, evidence?, nextAction?, path? }', inputSchema: { type: 'object', properties: { text: { type: 'string' }, status: { type: 'string', enum: ['requested', 'planned', 'in-progress', 'waiting', 'on-hold', 'blocked', 'incomplete', 'done', 'dropped'] }, evidence: { type: 'string' }, nextAction: { type: 'string' }, path: { type: 'string' } }, required: ['text'] } },
|
|
32
|
-
{ name: 'leerness_task_update', requiredTier: 'safe-write', description: '1.9.106 — 기존 task 상태/evidence/nextAction 갱신. 외부 AI가 작업 진행에 따라 task를 단계적으로 업데이트 (read+add+update 3종 surface 완성). 인자: { id (required), status?, evidence?, nextAction?, note?, path? }', inputSchema: { type: 'object', properties: { id: { type: 'string' }, status: { type: 'string', enum: ['requested', 'planned', 'in-progress', 'waiting', 'on-hold', 'blocked', 'incomplete', 'done', 'dropped'] }, evidence: { type: 'string' }, nextAction: { type: 'string' }, note: { type: 'string' }, path: { type: 'string' } }, required: ['id'] } },
|
|
33
|
-
{ name: 'leerness_task_drop', requiredTier: 'safe-write', description: '1.9.107 — task를 dropped 상태로 폐기 (CRUD 완성: read/add/update/drop). 외부 AI가 사용자 요청으로 task 취소. 인자: { id (required), reason?, path? }', inputSchema: { type: 'object', properties: { id: { type: 'string' }, reason: { type: 'string' }, path: { type: 'string' } }, required: ['id'] } },
|
|
34
|
-
{ name: 'leerness_decision_add', requiredTier: 'safe-write', description: '1.9.108 — decisions.md 에 새 설계 결정 추가 (메모리 영구화). 1.9.43+ handoff lessons 자동 회수와 통합 — 추후 동일 키워드 작업 시 자동 재상기. 인자: { title (required), reason?, alternatives?, impact?, path? }', inputSchema: { type: 'object', properties: { title: { type: 'string' }, reason: { type: 'string' }, alternatives: { type: 'string' }, impact: { type: 'string' }, path: { type: 'string' } }, required: ['title'] } },
|
|
35
|
-
{ name: 'leerness_rule_add', requiredTier: 'safe-write', description: '1.9.109 — 자연어 영구 룰 등록 (1.9.8). "매 X마다 Y를 해줘" 같은 룰을 등록 — handoff 가 매 세션 자동 출력, session close 가 자동 검증·보고. 인자: { description (required), trigger? (every-session/every-update/every-commit/session-start/session-close/pre-publish), path? }', inputSchema: { type: 'object', properties: { description: { type: 'string' }, trigger: { type: 'string', enum: ['every-session', 'every-update', 'every-commit', 'session-start', 'session-close', 'pre-publish'] }, path: { type: 'string' } }, required: ['description'] } },
|
|
36
|
-
{ name: 'leerness_rule_list', requiredTier: 'read-only', description: '1.9.109 — 등록된 자연어 룰 목록 JSON (id/trigger/rule/status/lastVerified). 외부 AI가 현재 활성 룰 자동 회수', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
37
|
-
{ name: 'leerness_plan_add', requiredTier: 'safe-write', description: '1.9.110 — plan.md 에 새 milestone 추가 + progress-tracker.md에 자동 동기화 task 생성. 외부 AI가 계획 단계를 직접 등록. 인자: { text (required), status?, progress?, nextAction?, path? }', inputSchema: { type: 'object', properties: { text: { type: 'string' }, status: { type: 'string' }, progress: { type: 'string' }, nextAction: { type: 'string' }, path: { type: 'string' } }, required: ['text'] } },
|
|
38
|
-
{ name: 'leerness_lesson_save', requiredTier: 'safe-write', description: '1.9.112 — .harness/lessons.md 에 새 lesson 영구화 (Memory Write Surface 5번째). 외부 AI가 세션 중 얻은 통찰을 즉시 영구 기록 — handoff 자동 회수와 통합. 인자: { text (required), tag?, path? }', inputSchema: { type: 'object', properties: { text: { type: 'string' }, tag: { type: 'string' }, path: { type: 'string' } }, required: ['text'] } },
|
|
39
|
-
{ name: 'leerness_memory_status', requiredTier: 'read-only', description: '1.9.114 — Memory Write Surface 5종 (tasks/decisions/rules/plan/lessons) 통합 상태 JSON. 외부 AI가 한 호출로 영구화 상태 + 카운트 + 최근 항목 회수. summary 필드는 "T2/D3/R1/P5/L7" 형식', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
40
|
-
{ name: 'leerness_lesson_list', requiredTier: 'read-only', description: '1.9.117 — lessons.md 전용 list JSON ({ date, text, tag }[]). --tag 필터 지원. 1.9.139+ --query 키워드 필터 (text/tag case-insensitive). 외부 AI가 영구화된 lesson 전체 회수 (vs leerness_lessons 는 다중 source fuzzy 매칭)', inputSchema: { type: 'object', properties: { path: { type: 'string' }, tag: { type: 'string' }, query: { type: 'string' } } } },
|
|
41
|
-
{ name: 'leerness_decision_list', requiredTier: 'read-only', description: '1.9.118 — decisions.md 전체 조회 JSON ({ date, title, decision, reason, alternatives, impact }[]). 1.9.139+ --query 키워드 필터 (title/decision/reason/alternatives/impact case-insensitive). 외부 AI가 영구화된 설계 결정 전체 회수', inputSchema: { type: 'object', properties: { path: { type: 'string' }, query: { type: 'string' } } } },
|
|
42
|
-
{ name: 'leerness_plan_list', requiredTier: 'read-only', 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' } } } },
|
|
43
|
-
{ name: 'leerness_lesson_drop', requiredTier: 'safe-write', 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'] } },
|
|
44
|
-
{ name: 'leerness_decision_drop', requiredTier: 'safe-write', 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'] } },
|
|
45
|
-
{ name: 'leerness_plan_remove', requiredTier: 'safe-write', 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'] } },
|
|
46
|
-
{ name: 'leerness_memory_archive_list', requiredTier: 'read-only', description: '1.9.127 — DELETE 5종 archive 파일 통합 조회 JSON ({ decisions: [], lessons: [], plan: [], totals: { decisions, lessons, plan, all } }). 외부 AI가 과거에 제거된 항목을 회수/복원 후보로 참조. --surface 필터: decisions|lessons|plan. 1.9.138+ --query 키워드 필터 (target/originalHeader case-insensitive 매칭)', inputSchema: { type: 'object', properties: { surface: { type: 'string' }, query: { type: 'string' }, path: { type: 'string' } } } },
|
|
47
|
-
{ name: 'leerness_memory_restore', requiredTier: 'safe-write', 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'] } },
|
|
48
|
-
{ name: 'leerness_task_list', requiredTier: 'read-only', description: '1.9.134 — progress-tracker.md task 조회 JSON ({ total, shown, truncated, tasks: [...] }). --status 필터 지원 (planned|in-progress|done 등). 1.36.72: 기본 최신 100행(total 은 전량) — 더 필요하면 limit(개수)/all=true(전체) 지정', inputSchema: { type: 'object', properties: { path: { type: 'string' }, status: { type: 'string' }, limit: { type: 'number' }, all: { type: 'boolean' } } } },
|
|
49
|
-
{ name: 'leerness_rule_remove', requiredTier: 'safe-write', description: '1.9.135 — rules.md 에서 특정 rule 제거 (id: R-XXXX). 제거된 rule 은 .harness/rules.archive.md 에 자동 보존 (복구 가능). Rule surface CRUD MCP 완성 (add/list/remove)', inputSchema: { type: 'object', properties: { id: { type: 'string' }, path: { type: 'string' } }, required: ['id'] } },
|
|
50
|
-
{ name: 'leerness_feature_impact', requiredTier: 'read-only', description: '1.9.141 — Feature Causality Graph 인과관계 영향 추적 JSON ({ feature, total, impacted: [{ id, title, depth, via, files, errorModes }] }). 신규 기능 추가/형식 변경 전 호출: id 변경으로 영향받는 다른 feature를 transitive (affects + co-changes + reverse depends-on) 으로 회수. 1+1=20 cascade 방지', inputSchema: { type: 'object', properties: { id: { type: 'string' }, path: { type: 'string' } }, required: ['id'] } },
|
|
51
|
-
{ name: 'leerness_feature_list', requiredTier: 'read-only', description: '1.9.141 — 전체 Feature Graph 노드 + 엣지 JSON. 외부 AI가 시스템 내 기능 의존성을 한 번에 회수', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
52
|
-
{ name: 'leerness_feature_add', requiredTier: 'safe-write', description: '1.9.142 — Feature Graph 에 새 노드 추가 (외부 AI가 코드 작성 중 직접 feature 등록). 인자: { title (required), dependsOn?, affects?, coChangesWith?, files?, path? }. 자동 F-XXXX ID 부여. CRUD 완성에 기여', inputSchema: { type: 'object', properties: { title: { type: 'string' }, dependsOn: { type: 'string' }, affects: { type: 'string' }, coChangesWith: { type: 'string' }, files: { type: 'string' }, path: { type: 'string' } }, required: ['title'] } },
|
|
53
|
-
{ name: 'leerness_feature_link', requiredTier: 'safe-write', description: '1.9.142 — 기존 feature 노드에 의존/영향/공변경 엣지 추가. 인자: { id (required, F-XXXX), dependsOn?, affects?, coChangesWith?, path? }. 외부 AI가 코드 변경 도중 발견한 인과관계를 즉시 그래프에 반영', inputSchema: { type: 'object', properties: { id: { type: 'string' }, dependsOn: { type: 'string' }, affects: { type: 'string' }, coChangesWith: { type: 'string' }, path: { type: 'string' } }, required: ['id'] } }, // 1.9.407 (8번째 버그헌트, UR-0111): 그래프 mutate 인데 read-only 오분류였음 → safe-write (feature_add 와 일관, 권한경계 회복)
|
|
54
|
-
{ name: 'leerness_env_detect', requiredTier: 'read-only', description: '1.9.145 — 실행 환경 자동 감지 + 변동 추적 JSON ({ snapshot: { os, hardware, locale, shell, node, tools, scriptDependencies }, diff: { firstCapture, changes, missing }, persisted }). "X은(는) 내부 또는 외부 명령... 아닙니다" 사전 방지: package.json scripts 의존 도구가 PATH에 있는지 검증 + 머신/Node/도구 변경 감지. 절대경로 마스킹 (보안). 인자: { path? }', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
55
|
-
{ name: 'leerness_provider_list', requiredTier: 'read-only', description: '1.9.157/158 — Provider Registry 조회 JSON ({ total, builtin, user, providers: [{ id, bin, envFlag, source, desc }] }). 빌트인 5종 (claude/codex/agy/copilot/ollama) + .harness/providers.json 사용자 정의 통합. 외부 AI가 sub-agent 분배 가능한 provider 전체 회수 (OpenRouter/Bedrock 등 등록되어 있으면 같이 노출). 🎉 MCP 48 도구 마일스톤', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
56
|
-
{ name: 'leerness_provider_add', requiredTier: 'safe-write', description: '1.9.159 — Provider Registry 에 새 provider 동적 추가. 인자: { id (required), bin?, envFlag?, versionArgs?, desc?, path? }. 외부 AI가 새 CLI 발견 시 자가 확장 (OpenRouter / Bedrock / Groq / Hugging Face 등 등록). 같은 id 두 번 호출 → 갱신. 빌트인 id 호출 → user override. id 는 영문자/숫자/_- 만 허용.', inputSchema: { type: 'object', properties: { id: { type: 'string' }, bin: { type: 'string' }, envFlag: { type: 'string' }, versionArgs: { type: 'string' }, desc: { type: 'string' }, installHint: { type: 'string' }, path: { type: 'string' } }, required: ['id'] } },
|
|
57
|
-
{ name: 'leerness_provider_remove', requiredTier: 'safe-write', description: '1.9.159 — Provider Registry 에서 사용자 정의 provider 제거. 인자: { id (required), path? }. 빌트인 5종 id 는 제거 불가 (override 만 제거 가능). 🎉 MCP 50 도구 마일스톤 — Provider Registry CRUD MCP 완성 (list/add/remove)', inputSchema: { type: 'object', properties: { id: { type: 'string' }, path: { type: 'string' } }, required: ['id'] } },
|
|
58
|
-
{ name: 'leerness_web', requiredTier: 'network', description: '1.9.168 — Web Bridge (1.9.165 playwright opt-in). sub: check (설치 + permissions.browser 확인) | screenshot (URL → PNG) | extract (URL + CSS selector → DOM 텍스트). 외부 AI가 leerness 의 웹 자동화 능력을 직접 호출. playwright 미설치 시 친절 안내 (graceful). 인자: { sub (required), url?, out?, selector?, path? }', inputSchema: { type: 'object', properties: { sub: { type: 'string', enum: ['check', 'screenshot', 'extract'] }, url: { type: 'string' }, out: { type: 'string' }, selector: { type: 'string' }, path: { type: 'string' } }, required: ['sub'] } },
|
|
59
|
-
{ name: 'leerness_pc', requiredTier: 'shell-write', description: '1.9.168 — PC Bridge (1.9.166 robotjs/nut-tree opt-in). sub: check (설치 + permissions.mouse/keyboard) | click (x,y) | type (text) | screenshot (out). ⚠ full permissions 권장 (mouse/keyboard 접근). 외부 AI가 데스크탑 자동화 능력을 직접 호출. 인자: { sub (required), x?, y?, text?, out?, path? }', inputSchema: { type: 'object', properties: { sub: { type: 'string', enum: ['check', 'click', 'type', 'screenshot'] }, x: { type: 'number' }, y: { type: 'number' }, text: { type: 'string' }, out: { type: 'string' }, path: { type: 'string' } }, required: ['sub'] } },
|
|
60
|
-
{ name: 'leerness_lsp', requiredTier: 'safe-write', description: '1.9.168 — LSP Bridge (1.9.167 typescript opt-in + regex fallback). sub: check (설치 여부) | symbols (file → function/class/interface/type/enum 목록) | references (name + in 디렉토리 → 호출 위치). 외부 AI가 코드 인텔리전스를 직접 호출 (의존성 0 fallback 동작). 🎉 MCP 53 도구 마일스톤. 인자: { sub (required), file?, name?, in?, path? }', inputSchema: { type: 'object', properties: { sub: { type: 'string', enum: ['check', 'symbols', 'references'] }, file: { type: 'string' }, name: { type: 'string' }, in: { type: 'string' }, path: { type: 'string' } }, required: ['sub'] } },
|
|
61
|
-
{ name: 'leerness_review_request', requiredTier: 'safe-write', description: '1.9.176 — 사용자 요청 사전 검토 (사용자 명시 요청). AI 에이전트가 사용자 요구를 **무조건 구현 전**에 호출. 분석: 1) estimatedType (route 추정), 2) conflicts (lesson 실패/진행중 task), 3) reuseCandidates (skill/reuse-map 매칭), 4) lessonsRecall (과거 결정), 5) planConflicts (진행중 milestone), 6) featureConflicts (feature graph 영역 겹침), 7) recommendedSteps (작업 유형별 3-5 단계), 8) efficiencyHints, 9) proceed (true/false). 사용자 결정 도움. 인자: { request (required), path? }', inputSchema: { type: 'object', properties: { request: { type: 'string' }, path: { type: 'string' } }, required: ['request'] } },
|
|
62
|
-
{ name: 'leerness_requests_audit', requiredTier: 'read-only', description: '1.9.216 (1.9.207 사용자 명시) — 사용자 명시 요청 누락 확인 절차. .harness/user-requests.json 의 open/in-progress 요청을 task-log/plan/decisions 와 매칭 → missing/tracked/stale 분류. 외부 AI가 "사용자가 했던 요청 중 누락된 게 있나?"를 직접 회수. 응답: { total, open, missing[], tracked[], stale[], completed, dropped }. 인자: { path? }', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
63
|
-
{ name: 'leerness_constraints_check', requiredTier: 'read-only', description: '1.9.216 (1.9.208 사용자 명시) — 플랫폼/API 제약 사전 체크. 사용자 요청 텍스트에서 플랫폼 alias 매칭 (stripe/openai/anthropic/github/discord/twitter 6종 + 사용자 정의) → 각 플랫폼의 rate-limit / idempotency / auth / cost 제약 노출. 외부 AI가 "이 기능 구현 전 어떤 규정을 봐야 하나?"를 직접 회수. 인자: { request (required), path? }', inputSchema: { type: 'object', properties: { request: { type: 'string' }, path: { type: 'string' } }, required: ['request'] } },
|
|
64
|
-
{ name: 'leerness_pre_wake_audit', requiredTier: 'safe-write', description: '1.9.216 (1.9.209 사용자 명시) — sleep 전 sub-agent audit. 6 영역 점검: missing-user-requests / stale-in-progress / drift-handoff-stale / wakeup-missed / next-action-pending / auto-resume-plan. 외부 AI가 "깨어나기 전 점검할 부분"을 회수. 응답: { auditedAt, findings: {critical, warning, info}, summary }. 인자: { path? }', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
65
|
-
{ name: 'leerness_intent_classify', requiredTier: 'read-only', description: '1.9.216 (1.9.213 사용자 명시) — 사용자 의도 파악 + scope expansion 게이트. 응답: { intent: precise|broad|default, signals, domain, explicitMentions, expansionCandidates, mode: dry-run }. 3원칙 안전: (1) Always-Off Opt-In, (2) Dry-run 기본 (실행 X), (3) 명시 vs 추론 분리 라벨링. 5 도메인 (game/web/api/cli/data). 외부 AI가 "이 요청은 정확히 그것만 / 포괄적 / 기본인가?"를 회수. 인자: { request (required), path? }', inputSchema: { type: 'object', properties: { request: { type: 'string' }, path: { type: 'string' } }, required: ['request'] } },
|
|
66
|
-
{ name: 'leerness_idempotency_audit', requiredTier: 'read-only', description: '1.9.216 (1.9.212 사용자 명시) — 멱등성 위반 탐지. 4영역 점검: rule-duplicate (medium) / task-duplicate-request (medium) / user-request-duplicate (low) / wakeup-duplicate (high). 응답: { violations[], verified[], summary: {totalViolations, high/medium/low, overall} }. 외부 AI가 "워크스페이스에 중복/충돌이 있나?"를 회수. 🎉 MCP 58 도구 마일스톤 (50→58). 인자: { path? }', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
67
|
-
{ name: 'leerness_session_resume', requiredTier: 'read-only', description: '1.9.221 (1.9.220 사용자 명시) — 비정상 종료 감지 + 자율 재개. 5신호 분석: last-handoff-stale / wakeup-missed / in-progress-stale / auto-resume-plan-unused / release-branch-pending. 응답: { abnormalShutdown, severity (none/low/medium/high), signals[], resumeGuide[] }. 외부 AI가 "절전/시스템종료/세션종료 후 leerness 상태가 정상인가? 어떻게 재개?"를 회수. 🎉 MCP 60 도구 마일스톤 (53→60, +7 in 1.9.168/216/221). 인자: { path? }', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
68
|
-
{ name: 'leerness_requests_auto_complete', requiredTier: 'safe-write', description: '1.9.224 (1.9.223 자동 회수) — delivered 패턴 자동 감지 + 자동 완료. "Round X.Y.Z — 구현 완료" / "implemented" / "delivered" 패턴이 있는 open 요청 중 현재 버전 이하인 것을 후보로 분류. 응답: { total, candidates: [{id, text, claimedVersion, currentVersion, deliveredKeyword, recordedAt}], currentVersion, applied (apply=true 시), completedIds[] }. 외부 AI가 "운영 누적된 가짜 미답 신호 정리 가능한가?"를 회수. 기본 dry-run, apply: true 명시 시에만 실 적용. 인자: { path?, apply? (default false) }', inputSchema: { type: 'object', properties: { path: { type: 'string' }, apply: { type: 'boolean' } } } },
|
|
69
|
-
{ name: 'leerness_round_history', requiredTier: 'read-only', description: '1.9.226 — 자율 라운드 통계. git tag v1.9.X 기반 누적 라운드 카운트 + 다음 마일스톤 (50/75/100/125/150/175/200/250/300/400/500) + 평균 rounds/day. 응답: { currentVersion, roundCount, baselineVersion, latestTags[], nextMilestone, roundsToNextMilestone, firstTagAt, latestTagAt, daysActive, avgRoundsPerDay }. 외부 AI가 "이 프로젝트는 얼마나 진행됐고 다음 마일스톤까지 몇 라운드 남았나?"를 회수. 인자: { path? }', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
70
|
-
{ name: 'leerness_milestones', requiredTier: 'read-only', description: '1.9.229 (1.9.226 확장) — 도달 마일스톤 + 다음 ETA. git tag 순차 분석으로 25/50/75/100/125/150/175/200/250/300/400/500 마일스톤 도달 일자 + 다음 마일스톤 ETA (현재 속도 기준) 계산. 응답: { totalRounds, reached: [{milestone, version, reachedAt, daysFromBaseline}], next: {milestone, roundsRemaining, etaDays, etaDate}, baselineAt, avgRoundsPerDay }. 외부 AI가 "지금까지 어떤 마일스톤을 언제 달성했고 다음 마일스톤 예상 도달일은?"을 회수. 인자: { path? }', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
71
|
-
{ name: 'leerness_pulse', requiredTier: 'read-only', description: '1.9.231 — 한 줄 종합 요약 (10 핵심 지표). 응답: { version, roundCount, mcpTools, memorySurface, security, health, driftScore, nextMilestone, etaDays, abnormalShutdown }. 외부 AI가 "leerness 상태 한 눈에 보기"를 가벼운 단일 호출로 회수. handoff 보다 5배 빠름 (drift/health 계산 skip). 인자: { path? }', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
72
|
-
{ name: 'leerness_commands', requiredTier: 'read-only', description: '1.9.233 — 카테고리화된 전체 CLI 명령 목록 (9 카테고리). 응답: { version, totalCommands, categories: { status, task, memory, audit, workflow, release, skill, bridge, config } }. 외부 AI가 "leerness 가 제공하는 명령이 뭐가 있나"를 직접 회수. 매뉴얼/도움말 동적 생성. 인자: { path? }', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
73
|
-
{ name: 'leerness_team_list', requiredTier: 'read-only', description: '1.9.378 (UR-0073) — 정의된 에이전트 팀 목록 (페르소나 기반, opt-in · 정의 전용). 응답: { version, count, teams: [{ id, name, purpose, personas[], members[], schedule, deployCommand, status }] }. 외부 AI 가 "이 워크스페이스에 정의된 팀이 뭐가 있나"를 회수. 인자: { path? }', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
74
|
-
{ name: 'leerness_team_preview', requiredTier: 'read-only', description: '1.9.378 (UR-0073 Phase B) — 팀 실행 계획 dry-run 미리보기 (실제 실행/배포 없음). 응답: { dryRun: true, teamId, task, schedule, memberCount, steps: [{ member, personas[], dispatchPrompt, suggestedCommand }] }. 외부 AI 가 "이 팀이 무엇을 할지"를 안전하게 회수. 인자: { id (required), task?, path? }', inputSchema: { type: 'object', properties: { id: { type: 'string' }, task: { type: 'string' }, path: { type: 'string' } }, required: ['id'] } },
|
|
75
|
-
{ name: 'leerness_release_cleanup', requiredTier: 'git-write', description: '1.9.236 (1.9.235 자동 회수) — local release/* branches 정리. main 에 merge된 것만 후보 (unmerged 보호, 현재 branch 보호). 응답: { apply, keep, total, merged, unmerged, deleteCount, toDelete[], recent[], unmergedSample[] }. 외부 AI가 "운영 누적 release branches 정리 가능?"을 회수. 기본 dry-run, apply: true 시에만 실 삭제. 인자: { path?, apply? (default false), keep? (default 5) }', inputSchema: { type: 'object', properties: { path: { type: 'string' }, apply: { type: 'boolean' }, keep: { type: 'number' } } } },
|
|
76
|
-
{ name: 'leerness_py_check', requiredTier: 'read-only', description: '1.9.239 (사용자 명시 UR-0013) — Python 파일 분석. 의존성 0 regex fallback. .md 외 .py 도 leerness 인지. 응답: { totalFiles, totalLOC, totalImports, totalFuncs, totalClasses, totalTodos, biggest: [{ file, loc, funcs, classes }] }. 외부 AI가 "이 프로젝트 Python 표면이 얼마나 되나"를 회수. 인자: { path? }', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
77
|
-
{ name: 'leerness_agent_mode', requiredTier: 'safe-write', description: '1.9.239 (사용자 명시 UR-0013) — 자율 모드 전용 통합 명령. start: handoff + drift --auto-fix + session-resume --auto-fix (진입). tick: pulse 한 줄 (매 라운드). stop: session close --auto-apply-delivered --auto-cleanup-branches (마감). 외부 AI 가 자율 라운드 진입/매 라운드/마감을 단일 호출로 수행. 인자: { path?, sub (required: "start"|"tick"|"stop"|"help") }', inputSchema: { type: 'object', properties: { path: { type: 'string' }, sub: { type: 'string', enum: ['start', 'tick', 'stop', 'help'] } }, required: ['sub'] } },
|
|
78
|
-
{ name: 'leerness_env_info', requiredTier: 'read-only', description: '1.9.241 (사용자 명시 UR-0014) — 환경 종합 정보 회수. OS / 언어 (LANG, 코드페이지) / 한국어 Windows / 하드웨어 / 터미널 (TTY, PowerShell 버전) / 도구 버전 (git, npm, python). 외부 AI 가 환경 호환성을 미리 인지 → 인코딩 오류 예방. 응답: { os, node, locale, hardware, terminal, tools }. 인자: { path?, encodingCheck? }. encodingCheck: true 시 셸 스크립트 (.ps1/.bat/.cmd/.sh) BOM 없는 비-ASCII 위험 감지', inputSchema: { type: 'object', properties: { path: { type: 'string' }, encodingCheck: { type: 'boolean' } } } },
|
|
79
|
-
{ name: 'leerness_api_skill', requiredTier: 'safe-write', description: '1.9.245 (사용자 명시 UR-0015) — API 문서·관련링크 자동 캐시. 공식 API 문서 URL을 fetch 하고 1단계 same-domain 관련 링크까지 정리 → .harness/api-skills/<id>.md 저장. 후속 같은 API 관련 작업 시 자동 참조. 응답: list (skills 배열) / show (전체 본문) / match (task 매칭 결과). 인자: { path?, sub ("list"|"show"|"match"|"add"|"drop"), url? (add), id? (show/drop), query? (match), direction? (add: 구현 방향 텍스트) }. 외부 AI가 "이 프로젝트 어떤 API 문서가 정리되어 있나?" / "내 작업과 매칭되는 API skill 있나?" 회수.', inputSchema: { type: 'object', properties: { path: { type: 'string' }, sub: { type: 'string', enum: ['list', 'show', 'match', 'add', 'drop'] }, url: { type: 'string' }, id: { type: 'string' }, query: { type: 'string' }, direction: { type: 'string' } }, required: ['sub'] } },
|
|
80
|
-
{ name: 'leerness_selftest', requiredTier: 'read-only', description: '1.9.258/259 — 설치된 leerness 바이너리의 코어 함수(보안 _isSecretKey / 버전 compareVer / 인코딩 _classifyCJK 등) 무결성 자가 검증. 응답: { version, total, pass, fail, ok, results[] }. 외부 AI/CI 가 "이 leerness 설치가 정상인가?(npx 캐시 손상·부분 설치 감지)" 를 1초 내 확인. 인자: 없음.', inputSchema: { type: 'object', properties: {} } },
|
|
81
|
-
{ name: 'leerness_shell_guard', requiredTier: 'read-only', description: '1.9.260/261 (사용자 명시 UR-0020) — 터미널 명령 셸 호환성 린터. 외부 AI 가 명령을 실행하기 전에 셸 호환성 문제를 사전 점검. 예: Windows PowerShell 5.1 은 && / || 미지원 → A; if ($?) { B } 제안. 6 규칙(ps5-chain/ps-devnull/ps-inline-env/ps-rm-rf/cmd-semicolon/ps-version-unknown) + 과거 실패 회수(.harness/shell-failures.json). 응답: { shell, psVersion, issues[{rule,severity,detail,suggestion}], pastSame, pastSimilar, ok }. 인자: { command (required), path? }. 현재 셸/PS 버전 자동 감지.', inputSchema: { type: 'object', properties: { command: { type: 'string' }, path: { type: 'string' } }, required: ['command'] } },
|
|
82
|
-
{ name: 'leerness_slash_commands', requiredTier: 'safe-write', description: '1.9.265/266 (사용자 명시 UR-0021) — CLI AI 에이전트별 슬래시 명령어 레지스트리. 외부 AI(메인)가 sub-agent(codex/agy/claude/grok/copilot)를 호출할 때 각 에이전트에 알맞는 슬래시 명령을 참조. 빌트인 + 사용자 .harness/agent-slash-commands.json override 병합. 응답: { agents: { <id>: { label, asOf, invoke(slash|subcommand), note, source, count, commands[{cmd,desc}] } } }. 인자: { path?, agent? (생략 시 전체), refresh? (1.9.267 — 설치된 CLI --help probe 자동 갱신), dryRun? }. agent 지정 시 단일.', inputSchema: { type: 'object', properties: { path: { type: 'string' }, agent: { type: 'string' }, refresh: { type: 'boolean' }, dryRun: { type: 'boolean' } } } },
|
|
83
|
-
{ name: 'leerness_roles', requiredTier: 'safe-write', description: '1.9.270 (사용자 명시) — 모델별 역할 부여. 여러 AI 에이전트 활성 시 역할(commander/reviewer/coder/architect/designer/debugger/dispatcher)을 provider+model 에 매핑하고, agents dispatch --role 로 라우팅. sub=suggest 면 활성 에이전트 기반 최적 배치 + 근거 반환(방향성 판단). sub=set 시 role/provider/model 필요. 응답: list/suggest/verify 별 JSON. 인자: { path?, sub(list|set|unset|catalog|suggest|verify), role?, provider?, model?, apply? }.', inputSchema: { type: 'object', properties: { path: { type: 'string' }, sub: { type: 'string' }, role: { type: 'string' }, provider: { type: 'string' }, model: { type: 'string' }, apply: { type: 'boolean' } } } },
|
|
84
|
-
// 1.9.279 (UR-0031, GPT-5.5 범용 하네스): 상태 substrate(.leerness/, 1.9.278)를 MCP 시맨틱 verb 로 노출 — 모든 에이전트 공통 호출 표면.
|
|
85
|
-
{ name: 'leerness_state_show', requiredTier: 'read-only', description: '1.9.279 (UR-0031) — get_project_context / get_current_task. .leerness/ 구조화 상태(현재 run + 누적)를 회수. 외부 에이전트가 작업 시작 전 "지금 무슨 작업이 진행 중인가 / 무엇을 읽고 무엇이 변경됐나"를 JSON 으로 파악. 응답: { state, currentRun }. 인자: { path? }.', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
86
|
-
{ name: 'leerness_state_start', requiredTier: 'safe-write', description: '1.9.279 (UR-0031) — start_task. 새 작업 run 시작 (.leerness/runs/run-NNNN.json 생성). 인자: { path?, goal (required), agent?, model?, task? }. 응답: { started, run }.', inputSchema: { type: 'object', properties: { path: { type: 'string' }, goal: { type: 'string' }, agent: { type: 'string' }, model: { type: 'string' }, task: { type: 'string' } }, required: ['goal'] } },
|
|
87
|
-
{ name: 'leerness_state_record', requiredTier: 'safe-write', description: '1.9.279 (UR-0031) — record_file_change / record_decision. 진행 중 run 에 변경 파일/명령/테스트/결정을 누적. 인자: { path?, filesChanged? (콤마), filesRead?, commands?, tests?, errors?, decision? }. 응답: { recorded, run }.', inputSchema: { type: 'object', properties: { path: { type: 'string' }, filesChanged: { type: 'string' }, filesRead: { type: 'string' }, commands: { type: 'string' }, tests: { type: 'string' }, errors: { type: 'string' }, decision: { type: 'string' } } } },
|
|
88
|
-
{ name: 'leerness_state_verify', requiredTier: 'safe-write', description: '1.9.279 (UR-0031) — request_verification / verify_done. 현재 run 의 검증 결과 기록. 인자: { path?, result (required: "pass"|"fail"), note? }. 응답: { verified, result, run }.', inputSchema: { type: 'object', properties: { path: { type: 'string' }, result: { type: 'string', enum: ['pass', 'fail'] }, note: { type: 'string' } }, required: ['result'] } },
|
|
89
|
-
{ name: 'leerness_state_handoff', requiredTier: 'safe-write', description: '1.9.279 (UR-0031) — create_handoff / make_handoff. 현재 run 을 마감하고 다음 에이전트용 .leerness/handoff/latest.{md,json} 작성 (Claude→Goose→Codex 인수인계 표준). 인자: { path?, summary (required) }. 응답: { handoff, run }.', inputSchema: { type: 'object', properties: { path: { type: 'string' }, summary: { type: 'string' } }, required: ['summary'] } },
|
|
90
|
-
{ name: 'leerness_get_project_context', requiredTier: 'read-only', description: '1.9.292 (UR-0031) — get_project_context. 외부 에이전트가 작업 시작 전 단 1콜로 "지금 무엇을 알아야 하는가"를 파악하는 집약 컨텍스트. 여러 소스(현재 작업/미답 사용자 요청/최근 결정/활성 룰/next-actions/memory surface/프로젝트 의도)를 한 번에 구조화 회수. read-only. 인자: { path? }. 응답: { version, project, currentTask, openRequests, recentDecisions, activeRules, nextActions, memory }.', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
91
|
-
{ name: 'leerness_about', requiredTier: 'read-only', description: '1.9.296 (UR-0030) — leerness 정체성/포지셔닝 회수. leerness 가 무엇인가: "AI 에이전트 운영 레이어"(실행기 아님) — 기억·정책·인수인계·검증·감사 5계층. read-only. 어떤 에이전트든 "이 도구가 무엇이고 무엇을 보완하는가"를 1콜로 파악. 응답: { identity, isNot, tagline, layers[], complements, entryPoints, surface }.', inputSchema: { type: 'object', properties: {} } },
|
|
92
|
-
{ name: 'leerness_honesty_check', requiredTier: 'read-only', description: '1.9.305 (사용자 명시) — AI 인식론적 정직성 점검. 주장/evidence 텍스트가 (1) 모르는 걸 아는 척(근거 없는 단정), (2) 검증 없는 섣부른 판단(추정+완료 결론), (3) 외부 정보 미수집(API/버전/스펙 언급+수집흔적 없음) 중 무엇에 해당하는지 휴리스틱 탐지. read-only. 에이전트가 자기 주장을 단언 전 self-check. 인자: { path?, taskId?, text? }. 응답: { ok, findings[], dimensions[], highCount }.', inputSchema: { type: 'object', properties: { path: { type: 'string' }, taskId: { type: 'string' }, text: { type: 'string' } } } },
|
|
93
|
-
{ name: 'leerness_brief', requiredTier: 'read-only', description: '1.9.308 (UR-0055) — 프로젝트 청사진 회수. 프로젝트 개요/소개/목적/기능/스택/아키텍처/성공기준/방향이력을 구조화 반환(show) 또는 복사용 blueprint 텍스트(export=true). read-only. 외부 에이전트가 "이 프로젝트가 무엇이고 어디로 가는가"를 1콜로 파악하거나, 신규 프로젝트 계획으로 복사. 인자: { path?, export? }. 응답(show): { project, intro, purpose, features[], stack[], directionHistory[], ... }.', inputSchema: { type: 'object', properties: { path: { type: 'string' }, export: { type: 'boolean' } } } }
|
|
94
|
-
]
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
//
|
|
98
|
-
//
|
|
99
|
-
// (
|
|
100
|
-
Object.
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
'
|
|
105
|
-
'
|
|
106
|
-
'
|
|
107
|
-
'
|
|
108
|
-
'
|
|
109
|
-
|
|
110
|
-
|
|
1
|
+
// lib/mcp-tools.js — MCP 도구 정의 단일출처 (순수 데이터, 부작용 0)
|
|
2
|
+
// 1.9.297 (UR-0025 5단계): bin/harness.js 의 const TOOLS 를 분리. tools/list 응답 + _mcpToolCount() 단일 출처.
|
|
3
|
+
// 이전: _mcpToolCount 가 __filename regex 로 self-count(Codex #5 취약) → 이제 module.exports.length 로 정확.
|
|
4
|
+
'use strict';
|
|
5
|
+
|
|
6
|
+
module.exports = [
|
|
7
|
+
{ name: 'leerness_handoff', requiredTier: 'safe-write', description: '워크스페이스 컨텍스트(plan/progress/decisions) 적재', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
8
|
+
{ name: 'leerness_drift_check', requiredTier: 'read-only', description: '1.9.136 — AI 에이전트 leerness 미사용 drift 자동 감지 JSON ({ root, score, level, signals[], healthy }). 5+ 신호 + 4단계 레벨 (🟢 healthy / 🟡 warning / 🟠 caution / 🔴 critical). 보안 신호 통합 (1.9.78)', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
9
|
+
{ name: 'leerness_audit', requiredTier: 'safe-write', description: '1.9.102 — 워크스페이스 일관성 감사 JSON (warnings/failures/fixed/healthy + findings[]. kind 11종: design_dup/design_system_default/reuse_map_empty/milestone_unlinked/handoff_not_generated/current_state_stale/readme_version_mismatch/npm_cve/gitignore_missing_secrets/env_keys_missing/strict_promoted)', inputSchema: { type: 'object', properties: { path: { type: 'string' }, fix: { type: 'boolean' }, strict: { type: 'boolean' } } } },
|
|
10
|
+
{ name: 'leerness_verify_claim', requiredTier: 'read-only', description: 'AI 거짓 완료 자동 검증 (evidence 파일 + 실 테스트 실행). 1.9.309(UR-0048): done/완료 주장은 evidence(수정파일+테스트) 기본 강제 — 증거 없는 done 은 FAIL(exit 1). lenient=true 로 완화, runTests/strictClaims 추가 가능. 응답 verdict.evidenceComplete 포함.', inputSchema: { type: 'object', properties: { taskId: { type: 'string' }, path: { type: 'string' }, runTests: { type: 'boolean' }, strictClaims: { type: 'boolean' }, lenient: { type: 'boolean' } }, required: ['taskId'] } },
|
|
11
|
+
{ name: 'leerness_verify_claim_all', requiredTier: 'read-only', description: '1.33.3 — 모든 done/완료 주장을 한 번에 검증(CI·스케일). progress-tracker 의 done 항목 전부를 verify-claim 정밀 검사(파일 존재·스텁·부풀린 카운트·증거 완전성)로 일괄 점검. 응답 { ok, total, failed, results:[{id,ok,reasons}] }. 세션 마감 전 "내 완료 주장 전부 증거와 맞는가?" 자가 점검용. runTests/strictClaims/lenient 추가 가능.', inputSchema: { type: 'object', properties: { path: { type: 'string' }, runTests: { type: 'boolean' }, strictClaims: { type: 'boolean' }, lenient: { type: 'boolean' } } } },
|
|
12
|
+
{ name: 'leerness_contract_verify', requiredTier: 'read-only', description: '명세 ↔ 구현 함수/필드 일치 자동 검사', inputSchema: { type: 'object', properties: { spec: { type: 'string' }, impl: { type: 'string' } }, required: ['spec', 'impl'] } },
|
|
13
|
+
{ name: 'leerness_agents_list', requiredTier: 'read-only', description: '외부 AI CLI 가용성 표 (claude/codex/agy/copilot 상태 + 환경변수 활성화 여부)', inputSchema: { type: 'object', properties: {} } },
|
|
14
|
+
{ name: 'leerness_reuse_map', requiredTier: 'read-only', description: '워크스페이스 중복 함수/capability 자동 감지 (--all-apps + fuzzy 매칭)', inputSchema: { type: 'object', properties: { path: { type: 'string' }, allApps: { type: 'boolean' }, strictElements: { type: 'boolean' } } } },
|
|
15
|
+
{ name: 'leerness_whats_new', requiredTier: 'read-only', description: 'CHANGELOG 차분 자동 추출 (from → to 사이 신규 명령/플래그/파일). 1.36.69: 기본 최신 30개 버전만 반환(응답 폭주 방지) — 응답의 totalVersions/shown/truncated 로 절단 여부 확인, 더 필요하면 limit(개수) 또는 all=true(전체, 대용량 주의) 지정.', inputSchema: { type: 'object', properties: { from: { type: 'string' }, to: { type: 'string' }, limit: { type: 'number' }, all: { type: 'boolean' } } } },
|
|
16
|
+
{ name: 'leerness_usage_stats', requiredTier: 'read-only', description: 'leerness 명령별 누적 호출 통계 + drift 통계', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
17
|
+
{ name: 'leerness_session_close', requiredTier: 'safe-write', description: '1.9.103 — 세션 마감 JSON (handoff/current-state/task-log 갱신 + taskCounts + rules + skillCandidates + drift + topCommands + mcpStats). 외부 AI가 마감 통계 자동 회수', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
18
|
+
{ name: 'leerness_skill_suggest', requiredTier: 'read-only', description: '1.9.53 — 사용 패턴 자동 분석 → 새 skill 후보 제안 (Hermes-style 자동 학습)', inputSchema: { type: 'object', properties: { path: { type: 'string' }, min: { type: 'number' }, days: { type: 'number' } } } },
|
|
19
|
+
{ name: 'leerness_lessons', requiredTier: 'read-only', description: '1.9.7/54 — 과거 결정·실수 자동 회수 (--auto: 현재 task 키워드 자동 추출)', inputSchema: { type: 'object', properties: { path: { type: 'string' }, query: { type: 'string' }, auto: { type: 'boolean' }, limit: { type: 'number' } } } },
|
|
20
|
+
{ name: 'leerness_task_export', requiredTier: 'project-write', description: '1.9.60/66 — leerness task → Claude Code TodoWrite 호환 JSON (외부 AI 양방향 sync)', inputSchema: { type: 'object', properties: { path: { type: 'string' }, to: { type: 'string' } } } },
|
|
21
|
+
{ name: 'leerness_env_check', requiredTier: 'read-only', description: '1.9.71/73 — .env vs .env.example 동기화 검사 (보안: 키만, 값 미노출). exit 1 if 누락 키 있음', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
22
|
+
{ name: 'leerness_brainstorm', requiredTier: 'read-only', description: '1.9.16/72/77 — 누적 컨텍스트(decisions+skills+tasks+rules+evidence+lessons+skillHistory+taskLogFails) 자원 회수. 외부 AI가 새 작업 시작 전 호출', inputSchema: { type: 'object', properties: { topic: { type: 'string' }, path: { type: 'string' }, allApps: { type: 'boolean' } }, required: ['topic'] } },
|
|
23
|
+
{ name: 'leerness_skill_match', requiredTier: 'safe-write', description: '1.9.45/50/83 — 사용자 task 키워드에 매칭되는 설치된 skill 추천 (jaccard 또는 embedding). 1.9.68 rolling history 자동 누적', inputSchema: { type: 'object', properties: { query: { type: 'string' }, path: { type: 'string' }, useEmbedding: { type: 'boolean' } }, required: ['query'] } },
|
|
24
|
+
{ name: 'leerness_skill_list', requiredTier: 'read-only', description: '1.9.84 — 워크스페이스에 설치된 skill 목록 + 사용 횟수 + 출처 (catalog/user). 외부 AI가 사용 가능한 skill 조회', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
25
|
+
{ name: 'leerness_health', requiredTier: 'read-only', description: '1.9.85/86 — 종합 헬스 체크 (drift + 보안 + skills + MCP + tasks + issues 배열). 외부 AI가 워크스페이스 상태 한 번에 확인', inputSchema: { type: 'object', properties: { path: { type: 'string' }, strict: { type: 'boolean' } } } },
|
|
26
|
+
{ name: 'leerness_skill_search', requiredTier: 'read-only', description: '1.9.90/91 — capability 배열에서 부분 일치하는 skill 검색 (substring + case-insensitive). skill match와 다른 정확 매칭', inputSchema: { type: 'object', properties: { capability: { type: 'string' }, path: { type: 'string' } }, required: ['capability'] } },
|
|
27
|
+
{ name: 'leerness_skill_info', requiredTier: 'read-only', description: '1.9.92 — 개별 skill 상세 조회 (version/capabilities/sources/patterns/usage/optimizations). 외부 AI가 skill 능력 정확히 파악', inputSchema: { type: 'object', properties: { id: { type: 'string' }, path: { type: 'string' } }, required: ['id'] } },
|
|
28
|
+
{ name: 'leerness_benchmark', requiredTier: 'read-only', description: '1.9.46/51/94 — 6 차원 점수 + 검수 시나리오 (--scenario) 결과 JSON. 외부 AI가 워크스페이스 leerness 활용 점수 확인', inputSchema: { type: 'object', properties: { path: { type: 'string' }, scenario: { type: 'string' } } } },
|
|
29
|
+
{ name: 'leerness_lazy_detect', requiredTier: 'read-only', description: '1.9.101 — 게으른 작업 자동 감지 결과 JSON (evidence 없는 done / empty handoff / no test run / TODO 미추적 / blocker no-next-action 등). 외부 AI가 거짓 완료 신호 사전 점검', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
30
|
+
{ name: 'leerness_retro', requiredTier: 'read-only', description: '1.9.104 — 4세션 누적 회고 보고서 JSON (statusCounts/focusNext/skillUsage/recentDecisions/durations/activeRules/fixSignals/passSignals/totalOptimizations). 외부 AI가 누적 패턴 자동 학습. 1.36.71: data.rows 는 기본 최신 30행(rowsTotal/rowsShown/rowsTruncated 로 절단 확인, rowsTotal 은 --days 창이 아니라 전체 task 수), 더 필요하면 limit(개수)/all=true(전체) 지정', inputSchema: { type: 'object', properties: { path: { type: 'string' }, days: { type: 'number' }, allApps: { type: 'boolean' }, limit: { type: 'number' }, all: { type: 'boolean' } } } },
|
|
31
|
+
{ name: 'leerness_task_add', requiredTier: 'safe-write', description: '1.9.105 — progress-tracker.md 에 새 task 추가 (양방향 제어 완성). 외부 AI가 사용자 요청을 task로 즉시 등록. 인자: { text (required), status?, evidence?, nextAction?, path? }', inputSchema: { type: 'object', properties: { text: { type: 'string' }, status: { type: 'string', enum: ['requested', 'planned', 'in-progress', 'waiting', 'on-hold', 'blocked', 'incomplete', 'done', 'dropped'] }, evidence: { type: 'string' }, nextAction: { type: 'string' }, path: { type: 'string' } }, required: ['text'] } },
|
|
32
|
+
{ name: 'leerness_task_update', requiredTier: 'safe-write', description: '1.9.106 — 기존 task 상태/evidence/nextAction 갱신. 외부 AI가 작업 진행에 따라 task를 단계적으로 업데이트 (read+add+update 3종 surface 완성). 인자: { id (required), status?, evidence?, nextAction?, note?, path? }', inputSchema: { type: 'object', properties: { id: { type: 'string' }, status: { type: 'string', enum: ['requested', 'planned', 'in-progress', 'waiting', 'on-hold', 'blocked', 'incomplete', 'done', 'dropped'] }, evidence: { type: 'string' }, nextAction: { type: 'string' }, note: { type: 'string' }, path: { type: 'string' } }, required: ['id'] } },
|
|
33
|
+
{ name: 'leerness_task_drop', requiredTier: 'safe-write', description: '1.9.107 — task를 dropped 상태로 폐기 (CRUD 완성: read/add/update/drop). 외부 AI가 사용자 요청으로 task 취소. 인자: { id (required), reason?, path? }', inputSchema: { type: 'object', properties: { id: { type: 'string' }, reason: { type: 'string' }, path: { type: 'string' } }, required: ['id'] } },
|
|
34
|
+
{ name: 'leerness_decision_add', requiredTier: 'safe-write', description: '1.9.108 — decisions.md 에 새 설계 결정 추가 (메모리 영구화). 1.9.43+ handoff lessons 자동 회수와 통합 — 추후 동일 키워드 작업 시 자동 재상기. 인자: { title (required), reason?, alternatives?, impact?, path? }', inputSchema: { type: 'object', properties: { title: { type: 'string' }, reason: { type: 'string' }, alternatives: { type: 'string' }, impact: { type: 'string' }, path: { type: 'string' } }, required: ['title'] } },
|
|
35
|
+
{ name: 'leerness_rule_add', requiredTier: 'safe-write', description: '1.9.109 — 자연어 영구 룰 등록 (1.9.8). "매 X마다 Y를 해줘" 같은 룰을 등록 — handoff 가 매 세션 자동 출력, session close 가 자동 검증·보고. 인자: { description (required), trigger? (every-session/every-update/every-commit/session-start/session-close/pre-publish), path? }', inputSchema: { type: 'object', properties: { description: { type: 'string' }, trigger: { type: 'string', enum: ['every-session', 'every-update', 'every-commit', 'session-start', 'session-close', 'pre-publish'] }, path: { type: 'string' } }, required: ['description'] } },
|
|
36
|
+
{ name: 'leerness_rule_list', requiredTier: 'read-only', description: '1.9.109 — 등록된 자연어 룰 목록 JSON (id/trigger/rule/status/lastVerified). 외부 AI가 현재 활성 룰 자동 회수', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
37
|
+
{ name: 'leerness_plan_add', requiredTier: 'safe-write', description: '1.9.110 — plan.md 에 새 milestone 추가 + progress-tracker.md에 자동 동기화 task 생성. 외부 AI가 계획 단계를 직접 등록. 인자: { text (required), status?, progress?, nextAction?, path? }', inputSchema: { type: 'object', properties: { text: { type: 'string' }, status: { type: 'string' }, progress: { type: 'string' }, nextAction: { type: 'string' }, path: { type: 'string' } }, required: ['text'] } },
|
|
38
|
+
{ name: 'leerness_lesson_save', requiredTier: 'safe-write', description: '1.9.112 — .harness/lessons.md 에 새 lesson 영구화 (Memory Write Surface 5번째). 외부 AI가 세션 중 얻은 통찰을 즉시 영구 기록 — handoff 자동 회수와 통합. 인자: { text (required), tag?, path? }', inputSchema: { type: 'object', properties: { text: { type: 'string' }, tag: { type: 'string' }, path: { type: 'string' } }, required: ['text'] } },
|
|
39
|
+
{ name: 'leerness_memory_status', requiredTier: 'read-only', description: '1.9.114 — Memory Write Surface 5종 (tasks/decisions/rules/plan/lessons) 통합 상태 JSON. 외부 AI가 한 호출로 영구화 상태 + 카운트 + 최근 항목 회수. summary 필드는 "T2/D3/R1/P5/L7" 형식', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
40
|
+
{ name: 'leerness_lesson_list', requiredTier: 'read-only', description: '1.9.117 — lessons.md 전용 list JSON ({ date, text, tag }[]). --tag 필터 지원. 1.9.139+ --query 키워드 필터 (text/tag case-insensitive). 외부 AI가 영구화된 lesson 전체 회수 (vs leerness_lessons 는 다중 source fuzzy 매칭)', inputSchema: { type: 'object', properties: { path: { type: 'string' }, tag: { type: 'string' }, query: { type: 'string' } } } },
|
|
41
|
+
{ name: 'leerness_decision_list', requiredTier: 'read-only', description: '1.9.118 — decisions.md 전체 조회 JSON ({ date, title, decision, reason, alternatives, impact }[]). 1.9.139+ --query 키워드 필터 (title/decision/reason/alternatives/impact case-insensitive). 외부 AI가 영구화된 설계 결정 전체 회수', inputSchema: { type: 'object', properties: { path: { type: 'string' }, query: { type: 'string' } } } },
|
|
42
|
+
{ name: 'leerness_plan_list', requiredTier: 'read-only', 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' } } } },
|
|
43
|
+
{ name: 'leerness_lesson_drop', requiredTier: 'safe-write', 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'] } },
|
|
44
|
+
{ name: 'leerness_decision_drop', requiredTier: 'safe-write', 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'] } },
|
|
45
|
+
{ name: 'leerness_plan_remove', requiredTier: 'safe-write', 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'] } },
|
|
46
|
+
{ name: 'leerness_memory_archive_list', requiredTier: 'read-only', description: '1.9.127 — DELETE 5종 archive 파일 통합 조회 JSON ({ decisions: [], lessons: [], plan: [], totals: { decisions, lessons, plan, all } }). 외부 AI가 과거에 제거된 항목을 회수/복원 후보로 참조. --surface 필터: decisions|lessons|plan. 1.9.138+ --query 키워드 필터 (target/originalHeader case-insensitive 매칭)', inputSchema: { type: 'object', properties: { surface: { type: 'string' }, query: { type: 'string' }, path: { type: 'string' } } } },
|
|
47
|
+
{ name: 'leerness_memory_restore', requiredTier: 'safe-write', 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'] } },
|
|
48
|
+
{ name: 'leerness_task_list', requiredTier: 'read-only', description: '1.9.134 — progress-tracker.md task 조회 JSON ({ total, shown, truncated, tasks: [...] }). --status 필터 지원 (planned|in-progress|done 등). 1.36.72: 기본 최신 100행(total 은 전량) — 더 필요하면 limit(개수)/all=true(전체) 지정', inputSchema: { type: 'object', properties: { path: { type: 'string' }, status: { type: 'string' }, limit: { type: 'number' }, all: { type: 'boolean' } } } },
|
|
49
|
+
{ name: 'leerness_rule_remove', requiredTier: 'safe-write', description: '1.9.135 — rules.md 에서 특정 rule 제거 (id: R-XXXX). 제거된 rule 은 .harness/rules.archive.md 에 자동 보존 (복구 가능). Rule surface CRUD MCP 완성 (add/list/remove)', inputSchema: { type: 'object', properties: { id: { type: 'string' }, path: { type: 'string' } }, required: ['id'] } },
|
|
50
|
+
{ name: 'leerness_feature_impact', requiredTier: 'read-only', description: '1.9.141 — Feature Causality Graph 인과관계 영향 추적 JSON ({ feature, total, impacted: [{ id, title, depth, via, files, errorModes }] }). 신규 기능 추가/형식 변경 전 호출: id 변경으로 영향받는 다른 feature를 transitive (affects + co-changes + reverse depends-on) 으로 회수. 1+1=20 cascade 방지', inputSchema: { type: 'object', properties: { id: { type: 'string' }, path: { type: 'string' } }, required: ['id'] } },
|
|
51
|
+
{ name: 'leerness_feature_list', requiredTier: 'read-only', description: '1.9.141 — 전체 Feature Graph 노드 + 엣지 JSON. 외부 AI가 시스템 내 기능 의존성을 한 번에 회수', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
52
|
+
{ name: 'leerness_feature_add', requiredTier: 'safe-write', description: '1.9.142 — Feature Graph 에 새 노드 추가 (외부 AI가 코드 작성 중 직접 feature 등록). 인자: { title (required), dependsOn?, affects?, coChangesWith?, files?, path? }. 자동 F-XXXX ID 부여. CRUD 완성에 기여', inputSchema: { type: 'object', properties: { title: { type: 'string' }, dependsOn: { type: 'string' }, affects: { type: 'string' }, coChangesWith: { type: 'string' }, files: { type: 'string' }, path: { type: 'string' } }, required: ['title'] } },
|
|
53
|
+
{ name: 'leerness_feature_link', requiredTier: 'safe-write', description: '1.9.142 — 기존 feature 노드에 의존/영향/공변경 엣지 추가. 인자: { id (required, F-XXXX), dependsOn?, affects?, coChangesWith?, path? }. 외부 AI가 코드 변경 도중 발견한 인과관계를 즉시 그래프에 반영', inputSchema: { type: 'object', properties: { id: { type: 'string' }, dependsOn: { type: 'string' }, affects: { type: 'string' }, coChangesWith: { type: 'string' }, path: { type: 'string' } }, required: ['id'] } }, // 1.9.407 (8번째 버그헌트, UR-0111): 그래프 mutate 인데 read-only 오분류였음 → safe-write (feature_add 와 일관, 권한경계 회복)
|
|
54
|
+
{ name: 'leerness_env_detect', requiredTier: 'read-only', description: '1.9.145 — 실행 환경 자동 감지 + 변동 추적 JSON ({ snapshot: { os, hardware, locale, shell, node, tools, scriptDependencies }, diff: { firstCapture, changes, missing }, persisted }). "X은(는) 내부 또는 외부 명령... 아닙니다" 사전 방지: package.json scripts 의존 도구가 PATH에 있는지 검증 + 머신/Node/도구 변경 감지. 절대경로 마스킹 (보안). 인자: { path? }', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
55
|
+
{ name: 'leerness_provider_list', requiredTier: 'read-only', description: '1.9.157/158 — Provider Registry 조회 JSON ({ total, builtin, user, providers: [{ id, bin, envFlag, source, desc }] }). 빌트인 5종 (claude/codex/agy/copilot/ollama) + .harness/providers.json 사용자 정의 통합. 외부 AI가 sub-agent 분배 가능한 provider 전체 회수 (OpenRouter/Bedrock 등 등록되어 있으면 같이 노출). 🎉 MCP 48 도구 마일스톤', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
56
|
+
{ name: 'leerness_provider_add', requiredTier: 'safe-write', description: '1.9.159 — Provider Registry 에 새 provider 동적 추가. 인자: { id (required), bin?, envFlag?, versionArgs?, desc?, path? }. 외부 AI가 새 CLI 발견 시 자가 확장 (OpenRouter / Bedrock / Groq / Hugging Face 등 등록). 같은 id 두 번 호출 → 갱신. 빌트인 id 호출 → user override. id 는 영문자/숫자/_- 만 허용.', inputSchema: { type: 'object', properties: { id: { type: 'string' }, bin: { type: 'string' }, envFlag: { type: 'string' }, versionArgs: { type: 'string' }, desc: { type: 'string' }, installHint: { type: 'string' }, path: { type: 'string' } }, required: ['id'] } },
|
|
57
|
+
{ name: 'leerness_provider_remove', requiredTier: 'safe-write', description: '1.9.159 — Provider Registry 에서 사용자 정의 provider 제거. 인자: { id (required), path? }. 빌트인 5종 id 는 제거 불가 (override 만 제거 가능). 🎉 MCP 50 도구 마일스톤 — Provider Registry CRUD MCP 완성 (list/add/remove)', inputSchema: { type: 'object', properties: { id: { type: 'string' }, path: { type: 'string' } }, required: ['id'] } },
|
|
58
|
+
{ name: 'leerness_web', requiredTier: 'network', description: '1.9.168 — Web Bridge (1.9.165 playwright opt-in). sub: check (설치 + permissions.browser 확인) | screenshot (URL → PNG) | extract (URL + CSS selector → DOM 텍스트). 외부 AI가 leerness 의 웹 자동화 능력을 직접 호출. playwright 미설치 시 친절 안내 (graceful). 인자: { sub (required), url?, out?, selector?, path? }', inputSchema: { type: 'object', properties: { sub: { type: 'string', enum: ['check', 'screenshot', 'extract'] }, url: { type: 'string' }, out: { type: 'string' }, selector: { type: 'string' }, path: { type: 'string' } }, required: ['sub'] } },
|
|
59
|
+
{ name: 'leerness_pc', requiredTier: 'shell-write', description: '1.9.168 — PC Bridge (1.9.166 robotjs/nut-tree opt-in). sub: check (설치 + permissions.mouse/keyboard) | click (x,y) | type (text) | screenshot (out). ⚠ full permissions 권장 (mouse/keyboard 접근). 외부 AI가 데스크탑 자동화 능력을 직접 호출. 인자: { sub (required), x?, y?, text?, out?, path? }', inputSchema: { type: 'object', properties: { sub: { type: 'string', enum: ['check', 'click', 'type', 'screenshot'] }, x: { type: 'number' }, y: { type: 'number' }, text: { type: 'string' }, out: { type: 'string' }, path: { type: 'string' } }, required: ['sub'] } },
|
|
60
|
+
{ name: 'leerness_lsp', requiredTier: 'safe-write', description: '1.9.168 — LSP Bridge (1.9.167 typescript opt-in + regex fallback). sub: check (설치 여부) | symbols (file → function/class/interface/type/enum 목록) | references (name + in 디렉토리 → 호출 위치). 외부 AI가 코드 인텔리전스를 직접 호출 (의존성 0 fallback 동작). 🎉 MCP 53 도구 마일스톤. 인자: { sub (required), file?, name?, in?, path? }', inputSchema: { type: 'object', properties: { sub: { type: 'string', enum: ['check', 'symbols', 'references'] }, file: { type: 'string' }, name: { type: 'string' }, in: { type: 'string' }, path: { type: 'string' } }, required: ['sub'] } },
|
|
61
|
+
{ name: 'leerness_review_request', requiredTier: 'safe-write', description: '1.9.176 — 사용자 요청 사전 검토 (사용자 명시 요청). AI 에이전트가 사용자 요구를 **무조건 구현 전**에 호출. 분석: 1) estimatedType (route 추정), 2) conflicts (lesson 실패/진행중 task), 3) reuseCandidates (skill/reuse-map 매칭), 4) lessonsRecall (과거 결정), 5) planConflicts (진행중 milestone), 6) featureConflicts (feature graph 영역 겹침), 7) recommendedSteps (작업 유형별 3-5 단계), 8) efficiencyHints, 9) proceed (true/false). 사용자 결정 도움. 인자: { request (required), path? }', inputSchema: { type: 'object', properties: { request: { type: 'string' }, path: { type: 'string' } }, required: ['request'] } },
|
|
62
|
+
{ name: 'leerness_requests_audit', requiredTier: 'read-only', description: '1.9.216 (1.9.207 사용자 명시) — 사용자 명시 요청 누락 확인 절차. .harness/user-requests.json 의 open/in-progress 요청을 task-log/plan/decisions 와 매칭 → missing/tracked/stale 분류. 외부 AI가 "사용자가 했던 요청 중 누락된 게 있나?"를 직접 회수. 응답: { total, open, missing[], tracked[], stale[], completed, dropped }. 인자: { path? }', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
63
|
+
{ name: 'leerness_constraints_check', requiredTier: 'read-only', description: '1.9.216 (1.9.208 사용자 명시) — 플랫폼/API 제약 사전 체크. 사용자 요청 텍스트에서 플랫폼 alias 매칭 (stripe/openai/anthropic/github/discord/twitter 6종 + 사용자 정의) → 각 플랫폼의 rate-limit / idempotency / auth / cost 제약 노출. 외부 AI가 "이 기능 구현 전 어떤 규정을 봐야 하나?"를 직접 회수. 인자: { request (required), path? }', inputSchema: { type: 'object', properties: { request: { type: 'string' }, path: { type: 'string' } }, required: ['request'] } },
|
|
64
|
+
{ name: 'leerness_pre_wake_audit', requiredTier: 'safe-write', description: '1.9.216 (1.9.209 사용자 명시) — sleep 전 sub-agent audit. 6 영역 점검: missing-user-requests / stale-in-progress / drift-handoff-stale / wakeup-missed / next-action-pending / auto-resume-plan. 외부 AI가 "깨어나기 전 점검할 부분"을 회수. 응답: { auditedAt, findings: {critical, warning, info}, summary }. 인자: { path? }', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
65
|
+
{ name: 'leerness_intent_classify', requiredTier: 'read-only', description: '1.9.216 (1.9.213 사용자 명시) — 사용자 의도 파악 + scope expansion 게이트. 응답: { intent: precise|broad|default, signals, domain, explicitMentions, expansionCandidates, mode: dry-run }. 3원칙 안전: (1) Always-Off Opt-In, (2) Dry-run 기본 (실행 X), (3) 명시 vs 추론 분리 라벨링. 5 도메인 (game/web/api/cli/data). 외부 AI가 "이 요청은 정확히 그것만 / 포괄적 / 기본인가?"를 회수. 인자: { request (required), path? }', inputSchema: { type: 'object', properties: { request: { type: 'string' }, path: { type: 'string' } }, required: ['request'] } },
|
|
66
|
+
{ name: 'leerness_idempotency_audit', requiredTier: 'read-only', description: '1.9.216 (1.9.212 사용자 명시) — 멱등성 위반 탐지. 4영역 점검: rule-duplicate (medium) / task-duplicate-request (medium) / user-request-duplicate (low) / wakeup-duplicate (high). 응답: { violations[], verified[], summary: {totalViolations, high/medium/low, overall} }. 외부 AI가 "워크스페이스에 중복/충돌이 있나?"를 회수. 🎉 MCP 58 도구 마일스톤 (50→58). 인자: { path? }', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
67
|
+
{ name: 'leerness_session_resume', requiredTier: 'read-only', description: '1.9.221 (1.9.220 사용자 명시) — 비정상 종료 감지 + 자율 재개. 5신호 분석: last-handoff-stale / wakeup-missed / in-progress-stale / auto-resume-plan-unused / release-branch-pending. 응답: { abnormalShutdown, severity (none/low/medium/high), signals[], resumeGuide[] }. 외부 AI가 "절전/시스템종료/세션종료 후 leerness 상태가 정상인가? 어떻게 재개?"를 회수. 🎉 MCP 60 도구 마일스톤 (53→60, +7 in 1.9.168/216/221). 인자: { path? }', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
68
|
+
{ name: 'leerness_requests_auto_complete', requiredTier: 'safe-write', description: '1.9.224 (1.9.223 자동 회수) — delivered 패턴 자동 감지 + 자동 완료. "Round X.Y.Z — 구현 완료" / "implemented" / "delivered" 패턴이 있는 open 요청 중 현재 버전 이하인 것을 후보로 분류. 응답: { total, candidates: [{id, text, claimedVersion, currentVersion, deliveredKeyword, recordedAt}], currentVersion, applied (apply=true 시), completedIds[] }. 외부 AI가 "운영 누적된 가짜 미답 신호 정리 가능한가?"를 회수. 기본 dry-run, apply: true 명시 시에만 실 적용. 인자: { path?, apply? (default false) }', inputSchema: { type: 'object', properties: { path: { type: 'string' }, apply: { type: 'boolean' } } } },
|
|
69
|
+
{ name: 'leerness_round_history', requiredTier: 'read-only', description: '1.9.226 — 자율 라운드 통계. git tag v1.9.X 기반 누적 라운드 카운트 + 다음 마일스톤 (50/75/100/125/150/175/200/250/300/400/500) + 평균 rounds/day. 응답: { currentVersion, roundCount, baselineVersion, latestTags[], nextMilestone, roundsToNextMilestone, firstTagAt, latestTagAt, daysActive, avgRoundsPerDay }. 외부 AI가 "이 프로젝트는 얼마나 진행됐고 다음 마일스톤까지 몇 라운드 남았나?"를 회수. 인자: { path? }', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
70
|
+
{ name: 'leerness_milestones', requiredTier: 'read-only', description: '1.9.229 (1.9.226 확장) — 도달 마일스톤 + 다음 ETA. git tag 순차 분석으로 25/50/75/100/125/150/175/200/250/300/400/500 마일스톤 도달 일자 + 다음 마일스톤 ETA (현재 속도 기준) 계산. 응답: { totalRounds, reached: [{milestone, version, reachedAt, daysFromBaseline}], next: {milestone, roundsRemaining, etaDays, etaDate}, baselineAt, avgRoundsPerDay }. 외부 AI가 "지금까지 어떤 마일스톤을 언제 달성했고 다음 마일스톤 예상 도달일은?"을 회수. 인자: { path? }', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
71
|
+
{ name: 'leerness_pulse', requiredTier: 'read-only', description: '1.9.231 — 한 줄 종합 요약 (10 핵심 지표). 응답: { version, roundCount, mcpTools, memorySurface, security, health, driftScore, nextMilestone, etaDays, abnormalShutdown }. 외부 AI가 "leerness 상태 한 눈에 보기"를 가벼운 단일 호출로 회수. handoff 보다 5배 빠름 (drift/health 계산 skip). 인자: { path? }', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
72
|
+
{ name: 'leerness_commands', requiredTier: 'read-only', description: '1.9.233 — 카테고리화된 전체 CLI 명령 목록 (9 카테고리). 응답: { version, totalCommands, categories: { status, task, memory, audit, workflow, release, skill, bridge, config } }. 외부 AI가 "leerness 가 제공하는 명령이 뭐가 있나"를 직접 회수. 매뉴얼/도움말 동적 생성. 인자: { path? }', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
73
|
+
{ name: 'leerness_team_list', requiredTier: 'read-only', description: '1.9.378 (UR-0073) — 정의된 에이전트 팀 목록 (페르소나 기반, opt-in · 정의 전용). 응답: { version, count, teams: [{ id, name, purpose, personas[], members[], schedule, deployCommand, status }] }. 외부 AI 가 "이 워크스페이스에 정의된 팀이 뭐가 있나"를 회수. 인자: { path? }', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
74
|
+
{ name: 'leerness_team_preview', requiredTier: 'read-only', description: '1.9.378 (UR-0073 Phase B) — 팀 실행 계획 dry-run 미리보기 (실제 실행/배포 없음). 응답: { dryRun: true, teamId, task, schedule, memberCount, steps: [{ member, personas[], dispatchPrompt, suggestedCommand }] }. 외부 AI 가 "이 팀이 무엇을 할지"를 안전하게 회수. 인자: { id (required), task?, path? }', inputSchema: { type: 'object', properties: { id: { type: 'string' }, task: { type: 'string' }, path: { type: 'string' } }, required: ['id'] } },
|
|
75
|
+
{ name: 'leerness_release_cleanup', requiredTier: 'git-write', description: '1.9.236 (1.9.235 자동 회수) — local release/* branches 정리. main 에 merge된 것만 후보 (unmerged 보호, 현재 branch 보호). 응답: { apply, keep, total, merged, unmerged, deleteCount, toDelete[], recent[], unmergedSample[] }. 외부 AI가 "운영 누적 release branches 정리 가능?"을 회수. 기본 dry-run, apply: true 시에만 실 삭제. 인자: { path?, apply? (default false), keep? (default 5) }', inputSchema: { type: 'object', properties: { path: { type: 'string' }, apply: { type: 'boolean' }, keep: { type: 'number' } } } },
|
|
76
|
+
{ name: 'leerness_py_check', requiredTier: 'read-only', description: '1.9.239 (사용자 명시 UR-0013) — Python 파일 분석. 의존성 0 regex fallback. .md 외 .py 도 leerness 인지. 응답: { totalFiles, totalLOC, totalImports, totalFuncs, totalClasses, totalTodos, biggest: [{ file, loc, funcs, classes }] }. 외부 AI가 "이 프로젝트 Python 표면이 얼마나 되나"를 회수. 인자: { path? }', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
77
|
+
{ name: 'leerness_agent_mode', requiredTier: 'safe-write', description: '1.9.239 (사용자 명시 UR-0013) — 자율 모드 전용 통합 명령. start: handoff + drift --auto-fix + session-resume --auto-fix (진입). tick: pulse 한 줄 (매 라운드). stop: session close --auto-apply-delivered --auto-cleanup-branches (마감). 외부 AI 가 자율 라운드 진입/매 라운드/마감을 단일 호출로 수행. 인자: { path?, sub (required: "start"|"tick"|"stop"|"help") }', inputSchema: { type: 'object', properties: { path: { type: 'string' }, sub: { type: 'string', enum: ['start', 'tick', 'stop', 'help'] } }, required: ['sub'] } },
|
|
78
|
+
{ name: 'leerness_env_info', requiredTier: 'read-only', description: '1.9.241 (사용자 명시 UR-0014) — 환경 종합 정보 회수. OS / 언어 (LANG, 코드페이지) / 한국어 Windows / 하드웨어 / 터미널 (TTY, PowerShell 버전) / 도구 버전 (git, npm, python). 외부 AI 가 환경 호환성을 미리 인지 → 인코딩 오류 예방. 응답: { os, node, locale, hardware, terminal, tools }. 인자: { path?, encodingCheck? }. encodingCheck: true 시 셸 스크립트 (.ps1/.bat/.cmd/.sh) BOM 없는 비-ASCII 위험 감지', inputSchema: { type: 'object', properties: { path: { type: 'string' }, encodingCheck: { type: 'boolean' } } } },
|
|
79
|
+
{ name: 'leerness_api_skill', requiredTier: 'safe-write', description: '1.9.245 (사용자 명시 UR-0015) — API 문서·관련링크 자동 캐시. 공식 API 문서 URL을 fetch 하고 1단계 same-domain 관련 링크까지 정리 → .harness/api-skills/<id>.md 저장. 후속 같은 API 관련 작업 시 자동 참조. 응답: list (skills 배열) / show (전체 본문) / match (task 매칭 결과). 인자: { path?, sub ("list"|"show"|"match"|"add"|"drop"), url? (add), id? (show/drop), query? (match), direction? (add: 구현 방향 텍스트) }. 외부 AI가 "이 프로젝트 어떤 API 문서가 정리되어 있나?" / "내 작업과 매칭되는 API skill 있나?" 회수.', inputSchema: { type: 'object', properties: { path: { type: 'string' }, sub: { type: 'string', enum: ['list', 'show', 'match', 'add', 'drop'] }, url: { type: 'string' }, id: { type: 'string' }, query: { type: 'string' }, direction: { type: 'string' } }, required: ['sub'] } },
|
|
80
|
+
{ name: 'leerness_selftest', requiredTier: 'read-only', description: '1.9.258/259 — 설치된 leerness 바이너리의 코어 함수(보안 _isSecretKey / 버전 compareVer / 인코딩 _classifyCJK 등) 무결성 자가 검증. 응답: { version, total, pass, fail, ok, results[] }. 외부 AI/CI 가 "이 leerness 설치가 정상인가?(npx 캐시 손상·부분 설치 감지)" 를 1초 내 확인. 인자: 없음.', inputSchema: { type: 'object', properties: {} } },
|
|
81
|
+
{ name: 'leerness_shell_guard', requiredTier: 'read-only', description: '1.9.260/261 (사용자 명시 UR-0020) — 터미널 명령 셸 호환성 린터. 외부 AI 가 명령을 실행하기 전에 셸 호환성 문제를 사전 점검. 예: Windows PowerShell 5.1 은 && / || 미지원 → A; if ($?) { B } 제안. 6 규칙(ps5-chain/ps-devnull/ps-inline-env/ps-rm-rf/cmd-semicolon/ps-version-unknown) + 과거 실패 회수(.harness/shell-failures.json). 응답: { shell, psVersion, issues[{rule,severity,detail,suggestion}], pastSame, pastSimilar, ok }. 인자: { command (required), path? }. 현재 셸/PS 버전 자동 감지.', inputSchema: { type: 'object', properties: { command: { type: 'string' }, path: { type: 'string' } }, required: ['command'] } },
|
|
82
|
+
{ name: 'leerness_slash_commands', requiredTier: 'safe-write', description: '1.9.265/266 (사용자 명시 UR-0021) — CLI AI 에이전트별 슬래시 명령어 레지스트리. 외부 AI(메인)가 sub-agent(codex/agy/claude/grok/copilot)를 호출할 때 각 에이전트에 알맞는 슬래시 명령을 참조. 빌트인 + 사용자 .harness/agent-slash-commands.json override 병합. 응답: { agents: { <id>: { label, asOf, invoke(slash|subcommand), note, source, count, commands[{cmd,desc}] } } }. 인자: { path?, agent? (생략 시 전체), refresh? (1.9.267 — 설치된 CLI --help probe 자동 갱신), dryRun? }. agent 지정 시 단일.', inputSchema: { type: 'object', properties: { path: { type: 'string' }, agent: { type: 'string' }, refresh: { type: 'boolean' }, dryRun: { type: 'boolean' } } } },
|
|
83
|
+
{ name: 'leerness_roles', requiredTier: 'safe-write', description: '1.9.270 (사용자 명시) — 모델별 역할 부여. 여러 AI 에이전트 활성 시 역할(commander/reviewer/coder/architect/designer/debugger/dispatcher)을 provider+model 에 매핑하고, agents dispatch --role 로 라우팅. sub=suggest 면 활성 에이전트 기반 최적 배치 + 근거 반환(방향성 판단). sub=set 시 role/provider/model 필요. 응답: list/suggest/verify 별 JSON. 인자: { path?, sub(list|set|unset|catalog|suggest|verify), role?, provider?, model?, apply? }.', inputSchema: { type: 'object', properties: { path: { type: 'string' }, sub: { type: 'string' }, role: { type: 'string' }, provider: { type: 'string' }, model: { type: 'string' }, apply: { type: 'boolean' } } } },
|
|
84
|
+
// 1.9.279 (UR-0031, GPT-5.5 범용 하네스): 상태 substrate(.leerness/, 1.9.278)를 MCP 시맨틱 verb 로 노출 — 모든 에이전트 공통 호출 표면.
|
|
85
|
+
{ name: 'leerness_state_show', requiredTier: 'read-only', description: '1.9.279 (UR-0031) — get_project_context / get_current_task. .leerness/ 구조화 상태(현재 run + 누적)를 회수. 외부 에이전트가 작업 시작 전 "지금 무슨 작업이 진행 중인가 / 무엇을 읽고 무엇이 변경됐나"를 JSON 으로 파악. 응답: { state, currentRun }. 인자: { path? }.', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
86
|
+
{ name: 'leerness_state_start', requiredTier: 'safe-write', description: '1.9.279 (UR-0031) — start_task. 새 작업 run 시작 (.leerness/runs/run-NNNN.json 생성). 인자: { path?, goal (required), agent?, model?, task? }. 응답: { started, run }.', inputSchema: { type: 'object', properties: { path: { type: 'string' }, goal: { type: 'string' }, agent: { type: 'string' }, model: { type: 'string' }, task: { type: 'string' } }, required: ['goal'] } },
|
|
87
|
+
{ name: 'leerness_state_record', requiredTier: 'safe-write', description: '1.9.279 (UR-0031) — record_file_change / record_decision. 진행 중 run 에 변경 파일/명령/테스트/결정을 누적. 인자: { path?, filesChanged? (콤마), filesRead?, commands?, tests?, errors?, decision? }. 응답: { recorded, run }.', inputSchema: { type: 'object', properties: { path: { type: 'string' }, filesChanged: { type: 'string' }, filesRead: { type: 'string' }, commands: { type: 'string' }, tests: { type: 'string' }, errors: { type: 'string' }, decision: { type: 'string' } } } },
|
|
88
|
+
{ name: 'leerness_state_verify', requiredTier: 'safe-write', description: '1.9.279 (UR-0031) — request_verification / verify_done. 현재 run 의 검증 결과 기록. 인자: { path?, result (required: "pass"|"fail"), note? }. 응답: { verified, result, run }.', inputSchema: { type: 'object', properties: { path: { type: 'string' }, result: { type: 'string', enum: ['pass', 'fail'] }, note: { type: 'string' } }, required: ['result'] } },
|
|
89
|
+
{ name: 'leerness_state_handoff', requiredTier: 'safe-write', description: '1.9.279 (UR-0031) — create_handoff / make_handoff. 현재 run 을 마감하고 다음 에이전트용 .leerness/handoff/latest.{md,json} 작성 (Claude→Goose→Codex 인수인계 표준). 인자: { path?, summary (required) }. 응답: { handoff, run }.', inputSchema: { type: 'object', properties: { path: { type: 'string' }, summary: { type: 'string' } }, required: ['summary'] } },
|
|
90
|
+
{ name: 'leerness_get_project_context', requiredTier: 'read-only', description: '1.9.292 (UR-0031) — get_project_context. 외부 에이전트가 작업 시작 전 단 1콜로 "지금 무엇을 알아야 하는가"를 파악하는 집약 컨텍스트. 여러 소스(현재 작업/미답 사용자 요청/최근 결정/활성 룰/next-actions/memory surface/프로젝트 의도)를 한 번에 구조화 회수. read-only. 인자: { path? }. 응답: { version, project, currentTask, openRequests, recentDecisions, activeRules, nextActions, memory }.', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
|
|
91
|
+
{ name: 'leerness_about', requiredTier: 'read-only', description: '1.9.296 (UR-0030) — leerness 정체성/포지셔닝 회수. leerness 가 무엇인가: "AI 에이전트 운영 레이어"(실행기 아님) — 기억·정책·인수인계·검증·감사 5계층. read-only. 어떤 에이전트든 "이 도구가 무엇이고 무엇을 보완하는가"를 1콜로 파악. 응답: { identity, isNot, tagline, layers[], complements, entryPoints, surface }.', inputSchema: { type: 'object', properties: {} } },
|
|
92
|
+
{ name: 'leerness_honesty_check', requiredTier: 'read-only', description: '1.9.305 (사용자 명시) — AI 인식론적 정직성 점검. 주장/evidence 텍스트가 (1) 모르는 걸 아는 척(근거 없는 단정), (2) 검증 없는 섣부른 판단(추정+완료 결론), (3) 외부 정보 미수집(API/버전/스펙 언급+수집흔적 없음) 중 무엇에 해당하는지 휴리스틱 탐지. read-only. 에이전트가 자기 주장을 단언 전 self-check. 인자: { path?, taskId?, text? }. 응답: { ok, findings[], dimensions[], highCount }.', inputSchema: { type: 'object', properties: { path: { type: 'string' }, taskId: { type: 'string' }, text: { type: 'string' } } } },
|
|
93
|
+
{ name: 'leerness_brief', requiredTier: 'read-only', description: '1.9.308 (UR-0055) — 프로젝트 청사진 회수. 프로젝트 개요/소개/목적/기능/스택/아키텍처/성공기준/방향이력을 구조화 반환(show) 또는 복사용 blueprint 텍스트(export=true). read-only. 외부 에이전트가 "이 프로젝트가 무엇이고 어디로 가는가"를 1콜로 파악하거나, 신규 프로젝트 계획으로 복사. 인자: { path?, export? }. 응답(show): { project, intro, purpose, features[], stack[], directionHistory[], ... }.', inputSchema: { type: 'object', properties: { path: { type: 'string' }, export: { type: 'boolean' } } } },
|
|
94
|
+
{ name: 'leerness_clarify', requiredTier: 'read-only', description: '1.36.77 (UR-0061) — 사용자 요청 텍스트의 판단-모호 신호 감지 → 사용자에게 그대로 물을 질문 목록 JSON ({ ambiguous, signals[], questions[] }). 추측 구현 방지 — 모호하면 질문 먼저.', inputSchema: { type: 'object', properties: { text: { type: 'string' }, path: { type: 'string' } }, required: ['text'] } },
|
|
95
|
+
{ name: 'leerness_preview', requiredTier: 'safe-write', description: '1.36.77 (UR-0061/0066) — 신규 기능/디자인 미리보기 승인 워크플로. action: add(title/design/features/mockup)·list·show·approve·revise(note 필수)·mockup(HTML 시안 스캐폴드 생성, force 로 재생성). 계약: approve 전 해당 기능 코드 작성 금지. list/show 는 읽기지만 단일 도구라 safe-write 로 보수 선언.', inputSchema: { type: 'object', properties: { action: { type: 'string', enum: ['add', 'list', 'show', 'approve', 'revise', 'mockup'] }, path: { type: 'string' }, title: { type: 'string' }, design: { type: 'string' }, features: { type: 'string' }, mockup: { type: 'string' }, id: { type: 'string' }, note: { type: 'string' }, force: { type: 'boolean' } }, required: ['action'] } },];
|
|
96
|
+
|
|
97
|
+
// 1.36.58 (외부 GPT 감사 F-09): core 프로필 — 세션 시작 토큰/도구 선택 비용을 줄이는 핵심 20종.
|
|
98
|
+
// `leerness mcp serve --profile core` (또는 LEERNESS_MCP_PROFILE=core) 에서 tools/list·tools/call 이 이 집합만 노출.
|
|
99
|
+
// 기본은 full(무변경). 선정 기준: 에이전트 1세션의 필수 루프(컨텍스트 적재→기억 CRUD→검증→인수인계) + 상태 substrate.
|
|
100
|
+
// (검수) 비열거 프로퍼티 — Object.keys/for..in 을 쓰는 미래 소비자가 87번째 '요소'로 오인하지 않게.
|
|
101
|
+
Object.defineProperty(module.exports, 'CORE', {
|
|
102
|
+
enumerable: false,
|
|
103
|
+
value: Object.freeze([
|
|
104
|
+
'leerness_handoff', 'leerness_session_close', 'leerness_get_project_context', 'leerness_about',
|
|
105
|
+
'leerness_task_add', 'leerness_task_update', 'leerness_task_list',
|
|
106
|
+
'leerness_decision_add', 'leerness_lesson_save', 'leerness_lessons', 'leerness_rule_list',
|
|
107
|
+
'leerness_verify_claim', 'leerness_lazy_detect', 'leerness_drift_check', 'leerness_health',
|
|
108
|
+
'leerness_state_show', 'leerness_state_start', 'leerness_state_record', 'leerness_state_handoff',
|
|
109
|
+
'leerness_brief',
|
|
110
|
+
]),
|
|
111
|
+
});
|
package/lib/team.js
CHANGED
|
@@ -10,11 +10,16 @@ const { _composeTeamPlan, _teamDeployGate } = require('./pure-utils');
|
|
|
10
10
|
const { _shellGuardAnalyze } = require('./analyzers');
|
|
11
11
|
|
|
12
12
|
function teamCmd(root, sub, id, opts = {}, deps = {}) {
|
|
13
|
-
const { VERSION, _loadTeams, _saveTeams, _detectShellCtx, arg, has } = deps;
|
|
13
|
+
const { VERSION, _loadTeams, _loadTeamsChecked, _saveTeams, _detectShellCtx, arg, has, failJson } = deps;
|
|
14
14
|
root = absRoot(root);
|
|
15
15
|
const json = opts.json || has('--json');
|
|
16
|
-
const teams = _loadTeams(root);
|
|
17
16
|
sub = sub || 'list';
|
|
17
|
+
// 1.36.78 (10차 헌트 F1): 형상 무효 스토어 위 변경 거부 — 읽기는 관용(빈 목록). failJson 이 없으면(구 DI) 기존 경로 유지.
|
|
18
|
+
const _chk = (typeof _loadTeamsChecked === 'function') ? _loadTeamsChecked(root) : { teams: _loadTeams(root), invalid: false };
|
|
19
|
+
const teams = _chk.teams;
|
|
20
|
+
const _fj = failJson || ((j, code, msg) => { if (j) log(JSON.stringify({ ok: false, code, error: msg }, null, 2)); else fail(msg); });
|
|
21
|
+
const _mutating = ['add', 'remove', 'deploy'].includes(sub);
|
|
22
|
+
if (_mutating && _chk.invalid) { _fj(json, 'store_invalid', `teams.json 형상 무효(비배열 루트 또는 null/ID 없는 항목) — 덮어쓰기 거부: ${require('path').join(root, '.harness', 'teams.json')} (수동 복구 후 재시도)`); return; }
|
|
18
23
|
if (sub === 'list') {
|
|
19
24
|
if (json) { log(JSON.stringify({ version: VERSION, root, count: teams.length, teams }, null, 2)); return; }
|
|
20
25
|
log(`# leerness team (1.9.371, UR-0073 Phase A) — 에이전트 팀 정의 (opt-in · 정의 전용)`);
|
|
@@ -25,8 +30,8 @@ function teamCmd(root, sub, id, opts = {}, deps = {}) {
|
|
|
25
30
|
}
|
|
26
31
|
if (sub === 'add') {
|
|
27
32
|
const teamId = String(id || '').toLowerCase().replace(/[^a-z0-9._-]+/g, '-').replace(/^[.\-]+|[.\-]+$/g, '');
|
|
28
|
-
if (!teamId || teamId.includes('..')) {
|
|
29
|
-
if (teams.some(t => t.id === teamId)) {
|
|
33
|
+
if (!teamId || teamId.includes('..')) { _fj(json, 'invalid_id', `무효한 team id: "${id}" (영숫자/._- 만)`); return; } // 1.36.78 (F4): --json 구조화
|
|
34
|
+
if (teams.some(t => t.id === teamId)) { _fj(json, 'already_exists', `이미 존재: ${teamId} (제거 후 재정의: leerness team remove ${teamId})`); return; }
|
|
30
35
|
const splitCsv = v => (v && v !== true) ? String(v).split(',').map(s => s.trim()).filter(Boolean) : [];
|
|
31
36
|
const sched = arg('--schedule', 'manual');
|
|
32
37
|
const validSched = new Set(['manual', 'every-session', 'daily', 'weekly']);
|
|
@@ -44,8 +49,11 @@ function teamCmd(root, sub, id, opts = {}, deps = {}) {
|
|
|
44
49
|
};
|
|
45
50
|
// 1.36.31 (codex 미검토표면 #2): 병렬 team add 가 서로의 등록을 덮어쓰던 lost update — 락 안에서 재로드→중복검사→추가→저장.
|
|
46
51
|
const _doAdd = () => {
|
|
47
|
-
|
|
48
|
-
|
|
52
|
+
// 1.36.78 (검수 #1): 락 획득 후 재로드도 checked 로 — 대기 중 스토어가 형상 무효로 바뀌면 덮어쓰기 거부(레이스 데이터 손실 차단).
|
|
53
|
+
const _rechk = (typeof _loadTeamsChecked === 'function') ? _loadTeamsChecked(root) : { teams: _loadTeams(root), invalid: false };
|
|
54
|
+
if (_rechk.invalid) { _fj(json, 'store_invalid', `teams.json 형상 무효 — 덮어쓰기 거부(락 재확인)`); return false; }
|
|
55
|
+
const cur = _rechk.teams;
|
|
56
|
+
if (cur.some(t => t.id === teamId)) { _fj(json, 'already_exists', `이미 존재: ${teamId} (제거 후 재정의: leerness team remove ${teamId})`); return false; }
|
|
49
57
|
cur.push(team);
|
|
50
58
|
// Windows: 락 밖의 병렬 _loadTeams 읽기가 파일을 잠깐 열어둔 사이 원자 rename 이 EPERM(일시적) — 짧은 재시도.
|
|
51
59
|
let _lastErr = null;
|
|
@@ -60,13 +68,14 @@ function teamCmd(root, sub, id, opts = {}, deps = {}) {
|
|
|
60
68
|
? deps._withLock(require('path').join(root, '.harness', 'teams.json'), _doAdd)
|
|
61
69
|
: _doAdd();
|
|
62
70
|
if (!_added) return;
|
|
71
|
+
if (json) { log(JSON.stringify({ ok: true, id: teamId, personas: team.personas.length, members: team.members.length, schedule: team.schedule, review: team.review }, null, 2)); return; } // 1.36.78 (F4)
|
|
63
72
|
ok(`team 정의: ${teamId} (personas:${team.personas.length} members:${team.members.length} schedule:${team.schedule} review:${team.review ? 'on' : 'off'})`);
|
|
64
73
|
log(` ⓘ 정의 전용 — 자동 실행 없음. 목록: leerness team list`);
|
|
65
74
|
return;
|
|
66
75
|
}
|
|
67
76
|
if (sub === 'show') {
|
|
68
77
|
const t = teams.find(x => x.id === id);
|
|
69
|
-
if (!t) {
|
|
78
|
+
if (!t) { _fj(json, 'not_found', `team 없음: ${id}`); return; } // 1.36.78 (F4): --json 구조화
|
|
70
79
|
if (json) { log(JSON.stringify(t, null, 2)); return; }
|
|
71
80
|
log(`# team ${t.id}`);
|
|
72
81
|
log(` name: ${t.name || ''}`);
|
|
@@ -81,15 +90,16 @@ function teamCmd(root, sub, id, opts = {}, deps = {}) {
|
|
|
81
90
|
if (sub === 'remove') {
|
|
82
91
|
const before = teams.length;
|
|
83
92
|
const next = teams.filter(x => x.id !== id);
|
|
84
|
-
if (next.length === before) {
|
|
93
|
+
if (next.length === before) { _fj(json, 'not_found', `team 없음: ${id}`); return; } // 1.36.78 (F4): --json 구조화 + exit 1
|
|
85
94
|
_saveTeams(root, next);
|
|
95
|
+
if (json) { log(JSON.stringify({ ok: true, removed: id, count: next.length }, null, 2)); return; }
|
|
86
96
|
ok(`team 제거: ${id}`);
|
|
87
97
|
return;
|
|
88
98
|
}
|
|
89
99
|
// 1.9.372 (UR-0073 Phase B): team preview — dry-run 실행 계획 미리보기 (실제 dispatch/spawn/배포 없음).
|
|
90
100
|
if (sub === 'preview') {
|
|
91
101
|
const t = teams.find(x => x.id === id);
|
|
92
|
-
if (!t) {
|
|
102
|
+
if (!t) { _fj(json, 'not_found', `team 없음: ${id}`); return; } // 1.36.78 (F4): --json 구조화
|
|
93
103
|
const plan = _composeTeamPlan(t, arg('--task', null));
|
|
94
104
|
if (json) { log(JSON.stringify({ version: VERSION, dryRun: true, ...plan }, null, 2)); return; }
|
|
95
105
|
log(`# team preview ${t.id} (1.9.372, UR-0073 Phase B) — dry-run (실제 실행 없음)`);
|
|
@@ -113,9 +123,19 @@ function teamCmd(root, sub, id, opts = {}, deps = {}) {
|
|
|
113
123
|
// 1.9.376 (UR-0073 Phase D): team deploy — 사용자 설정 deployCommand 실행. 안전: dry-run 기본 + --yes + LEERNESS_TEAM_DEPLOY=1 이중 게이트 + shell-guard.
|
|
114
124
|
if (sub === 'deploy') {
|
|
115
125
|
const t = teams.find(x => x.id === id);
|
|
116
|
-
if (!t) {
|
|
126
|
+
if (!t) { _fj(json, 'not_found', `team 없음: ${id}`); return; }
|
|
117
127
|
const gate = _teamDeployGate(t, { yes: has('--yes'), envOn: process.env.LEERNESS_TEAM_DEPLOY === '1' });
|
|
118
|
-
|
|
128
|
+
// 1.36.78 (F4): --json 모드도 사람 모드와 exit 일치 — no-command/gated 는 실패(exit 1), dry-run 은 정상(exit 0).
|
|
129
|
+
if (gate.mode === 'no-command' || gate.mode === 'gated') process.exitCode = 1;
|
|
130
|
+
// 1.36.78 (검수 #2): execute 는 exact-once JSON 위반(gate JSON 후 사람 로그 + inherited child stdout) → --json execute 전용 분기.
|
|
131
|
+
// 자식 출력을 캡처하고 실행 후 단일 JSON 하나만 방출한다.
|
|
132
|
+
if (json && gate.mode === 'execute') {
|
|
133
|
+
const r = cp.spawnSync(gate.command, { cwd: root, shell: true, encoding: 'utf8', timeout: 600000 });
|
|
134
|
+
if (r.status !== 0) process.exitCode = 1;
|
|
135
|
+
log(JSON.stringify({ version: VERSION, teamId: t.id, ...gate, executed: true, exitStatus: r.status, stdout: (r.stdout || '').slice(0, 4000), stderr: (r.stderr || '').slice(0, 4000) }, null, 2));
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
if (json) { log(JSON.stringify({ version: VERSION, teamId: t.id, ...gate }, null, 2)); return; } // non-execute --json: 단일 JSON
|
|
119
139
|
if (gate.mode === 'no-command') { fail(`team '${t.id}' deployCommand 미설정 — leerness team add ${t.id} --deploy "<배포 명령>"`); return; }
|
|
120
140
|
if (gate.mode === 'dry-run') {
|
|
121
141
|
log(`# team deploy ${t.id} (1.9.376, UR-0073 Phase D) — dry-run (실행 없음)`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "leerness",
|
|
3
|
-
"version": "1.36.
|
|
3
|
+
"version": "1.36.78",
|
|
4
4
|
"description": "The AI-coding operations layer that makes \"done\" require evidence — persistent memory, evidence-gated completion checks, and clean handoffs for any AI agent (Claude Code, Codex, Cursor). State lives as plain files in your repo. CLI + MCP, 0 runtime dependencies.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"leerness",
|
package/scripts/e2e.js
CHANGED
|
@@ -6103,11 +6103,15 @@ total++;
|
|
|
6103
6103
|
// (4) codex #9: 코드펜스 예제 함수는 계약 아님 → 통과(exit 0) + specFunctions 에 helper 없음
|
|
6104
6104
|
fs.writeFileSync(path.join(d, 'fen.md'), '# S\n```js\nfunction helper(){}\n```\n');
|
|
6105
6105
|
fs.writeFileSync(path.join(d, 'fen.js'), 'module.exports={};\n');
|
|
6106
|
-
|
|
6106
|
+
// 1.36.78 (10차 F6): 펜스-only spec 은 선언 0건이라 이제 empty_contract 로 거부됨 — 이 테스트 의도는 "helper 가 specFunctions 에 없음"(펜스 제외)이므로 --allow-empty 로 유지.
|
|
6107
|
+
const cfen = cp.spawnSync(process.execPath, [CLI, 'contract', 'verify', path.join(d, 'fen.md'), path.join(d, 'fen.js'), '--json', '--allow-empty'], { encoding: 'utf8', timeout: 15000 });
|
|
6107
6108
|
let fenceOk = false; try { const j = JSON.parse(cfen.stdout); fenceOk = cfen.status === 0 && j.ok === true && !(j.specFunctions || []).includes('helper'); } catch {}
|
|
6109
|
+
// 1.36.78 (10차 F6): 펜스-only(--allow-empty 없음)는 empty_contract 거부(exit 1)
|
|
6110
|
+
const cfenReject = cp.spawnSync(process.execPath, [CLI, 'contract', 'verify', path.join(d, 'fen.md'), path.join(d, 'fen.js'), '--json'], { encoding: 'utf8', timeout: 15000 });
|
|
6111
|
+
let fenceRejectOk = false; try { fenceRejectOk = cfenReject.status === 1 && JSON.parse(cfenReject.stdout).code === 'empty_contract'; } catch {}
|
|
6108
6112
|
fs.rmSync(d, { recursive: true, force: true });
|
|
6109
|
-
ok = fpFixed && fnGuard && bracketOk && fenceOk;
|
|
6110
|
-
if (!ok) console.log(` [cvdollar 디버그] fpFixed=${fpFixed} fnGuard=${fnGuard} bracket=${bracketOk} fence=${fenceOk}`);
|
|
6113
|
+
ok = fpFixed && fnGuard && bracketOk && fenceOk && fenceRejectOk;
|
|
6114
|
+
if (!ok) console.log(` [cvdollar 디버그] fpFixed=${fpFixed} fnGuard=${fnGuard} bracket=${bracketOk} fence=${fenceOk} fenceReject=${fenceRejectOk}`);
|
|
6111
6115
|
} catch {}
|
|
6112
6116
|
console.log(ok ? '✓ B(1.35.11) 자체 contract 헌트 + codex 교차: $필드 정규식 안전화 + bracket export(#8) + 코드펜스 예제 제외(#9) FP 3종 + no-FN' : '✗ contract 헌트 FP 3종 수정 실패');
|
|
6113
6117
|
if (!ok) failed++;
|
|
@@ -7511,5 +7515,109 @@ total++;
|
|
|
7511
7515
|
if (!ok) failed++;
|
|
7512
7516
|
}
|
|
7513
7517
|
|
|
7518
|
+
// 1.36.77 (UR-0061/0066 MCP 완결): leerness_clarify + leerness_preview 도구 — MCP 전용 에이전트도 질문·시안 승인 흐름
|
|
7519
|
+
total++;
|
|
7520
|
+
{
|
|
7521
|
+
let ok = false;
|
|
7522
|
+
const _d = [];
|
|
7523
|
+
try {
|
|
7524
|
+
const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-mcp77-')); _d.push(d);
|
|
7525
|
+
cp.spawnSync(process.execPath, [CLI, 'init', d, '--yes', '--no-env', '--no-stale-check'], { encoding: 'utf8', timeout: 40000 });
|
|
7526
|
+
const MCP = (name, args) => {
|
|
7527
|
+
const req = JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'tools/call', params: { name, arguments: args } }) + '\n';
|
|
7528
|
+
const r = cp.spawnSync(process.execPath, [CLI, 'mcp', 'serve'], { encoding: 'utf8', timeout: 25000, input: req, maxBuffer: 8 * 1024 * 1024 });
|
|
7529
|
+
for (const l of (r.stdout || '').trim().split('\n')) { try { const j = JSON.parse(l); if (j.result) return j.result; } catch {} }
|
|
7530
|
+
return null;
|
|
7531
|
+
};
|
|
7532
|
+
const inner = (res) => { try { return JSON.parse(res.content[0].text); } catch { return null; } };
|
|
7533
|
+
const cl = inner(MCP('leerness_clarify', { text: '적당히 이쁘게 그거 고쳐줘', path: d }));
|
|
7534
|
+
const clOk = cl && cl.ambiguous === true && Array.isArray(cl.questions) && cl.questions.length >= 2;
|
|
7535
|
+
const ad = inner(MCP('leerness_preview', { action: 'add', title: '신규 페이지 디자인', design: '밝게', path: d }));
|
|
7536
|
+
const mk = inner(MCP('leerness_preview', { action: 'mockup', id: 'P-0001', path: d }));
|
|
7537
|
+
const flowOk = ad && ad.id === 'P-0001' && mk && mk.mockupPath === '.harness/previews/P-0001-mockup.html'
|
|
7538
|
+
&& fs.existsSync(path.join(d, '.harness', 'previews', 'P-0001-mockup.html'));
|
|
7539
|
+
const ap = inner(MCP('leerness_preview', { action: 'approve', id: 'P-0001', path: d }));
|
|
7540
|
+
const noteErr = MCP('leerness_preview', { action: 'revise', id: 'P-0001', path: d });
|
|
7541
|
+
const contractOk = ap && ap.status === 'approved' && noteErr && noteErr.isError === true;
|
|
7542
|
+
// 도구 수 정합 (README 가드와 별개로 정의 자체)
|
|
7543
|
+
const T = require(path.resolve(path.dirname(CLI), '..', 'lib', 'mcp-tools.js'));
|
|
7544
|
+
const defOk = T.some(t => t.name === 'leerness_clarify' && t.requiredTier === 'read-only')
|
|
7545
|
+
&& T.some(t => t.name === 'leerness_preview' && t.requiredTier === 'safe-write');
|
|
7546
|
+
ok = clOk && flowOk && contractOk && defOk;
|
|
7547
|
+
if (!ok) console.log(` [mcp77 디버그] cl=${clOk} flow=${flowOk} contract=${contractOk} def=${defOk}`);
|
|
7548
|
+
} catch (e) {} finally { _d.forEach(x => { try { fs.rmSync(x, { recursive: true, force: true }); } catch {} }); }
|
|
7549
|
+
console.log(ok ? '✓ B(1.36.77) MCP clarify/preview 노출: 질문 생성 + add→mockup→approve 왕복 + revise 노트 계약 + 티어' : '✗ MCP clarify/preview 실패');
|
|
7550
|
+
if (!ok) failed++;
|
|
7551
|
+
}
|
|
7552
|
+
|
|
7553
|
+
// 1.36.78 (10차 헌트 확정 7건): team 형상/계약 · constraints 검증/손상 · agents recommend · 빈 contract
|
|
7554
|
+
total++;
|
|
7555
|
+
{
|
|
7556
|
+
let ok = false;
|
|
7557
|
+
const _d = [];
|
|
7558
|
+
try {
|
|
7559
|
+
const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-h10-')); _d.push(d);
|
|
7560
|
+
cp.spawnSync(process.execPath, [CLI, 'init', d, '--yes', '--no-env', '--no-stale-check'], { encoding: 'utf8', timeout: 40000 });
|
|
7561
|
+
const T = (a) => cp.spawnSync(process.execPath, [CLI, ...a, '--path', d], { encoding: 'utf8', timeout: 20000 });
|
|
7562
|
+
const J = (r) => { try { return JSON.parse(r.stdout); } catch { return null; } };
|
|
7563
|
+
// F1: 객체 루트 teams.json → 변경 거부 + 원본 보존
|
|
7564
|
+
const tj = path.join(d, '.harness', 'teams.json');
|
|
7565
|
+
fs.writeFileSync(tj, '{"teams":[{"id":"preserved","members":["codex"]}],"metadata":{"revision":7}}');
|
|
7566
|
+
const beforeT = fs.readFileSync(tj, 'utf8');
|
|
7567
|
+
const f1a = T(['team', 'add', 'newteam', '--members', 'claude', '--json']);
|
|
7568
|
+
const f1Ok = f1a.status === 1 && fs.readFileSync(tj, 'utf8') === beforeT;
|
|
7569
|
+
// F1 variant2: string members → 크래시 대신 정규화(list 정상)
|
|
7570
|
+
fs.writeFileSync(tj, '[{"id":"bad","members":"codex","personas":[],"schedule":"manual","status":"active"}]');
|
|
7571
|
+
let f1bOk = false; try { f1bOk = Array.isArray(J(T(['team', 'list', '--json'])).teams[0].members); } catch {}
|
|
7572
|
+
// F4: team --json 순수 + exit 일치
|
|
7573
|
+
fs.writeFileSync(tj, '[]');
|
|
7574
|
+
const addJson = T(['team', 'add', 'alpha', '--members', 'claude,codex', '--json']);
|
|
7575
|
+
const f4addOk = J(addJson) && J(addJson).ok === true;
|
|
7576
|
+
const dupJson = T(['team', 'add', 'alpha', '--members', 'x', '--json']);
|
|
7577
|
+
const f4dupOk = J(dupJson) && J(dupJson).ok === false && dupJson.status === 1;
|
|
7578
|
+
const rmMiss = T(['team', 'remove', 'nope', '--json']);
|
|
7579
|
+
const f4rmOk = J(rmMiss) && rmMiss.status === 1;
|
|
7580
|
+
const depHuman = T(['team', 'deploy', 'alpha']);
|
|
7581
|
+
const depJson = T(['team', 'deploy', 'alpha', '--json']);
|
|
7582
|
+
const f4depOk = depHuman.status === depJson.status && depHuman.status === 1 && J(depJson) != null;
|
|
7583
|
+
// F2: 무효 constraint 거부 + dedup
|
|
7584
|
+
const _pcf = path.join(d, '.harness', 'platform-constraints.json'); // 무효 add 는 파일 자체를 안 만들 수 있음(부재 = 미저장)
|
|
7585
|
+
const f2malOk = T(['constraints', 'add', 'x1', '--alias', 'x1', '--constraint', 'rate-limit', '--json']).status === 1
|
|
7586
|
+
&& T(['constraints', 'add', 'x2', '--alias', 'x2', '--constraint', 'auth:', '--json']).status === 1
|
|
7587
|
+
&& !(fs.existsSync(_pcf) && fs.readFileSync(_pcf, 'utf8').includes('"x1"'));
|
|
7588
|
+
T(['constraints', 'add', 'dp', '--alias', 'dp', '--constraint', 'auth:token required', '--json']);
|
|
7589
|
+
const dup2 = J(T(['constraints', 'add', 'dp', '--alias', 'dp', '--constraint', 'auth:token required', '--json']));
|
|
7590
|
+
const f2dupOk = dup2 && dup2.constraints.length === 1;
|
|
7591
|
+
// F3: 손상 constraints store 표면화
|
|
7592
|
+
fs.writeFileSync(path.join(d, '.harness', 'platform-constraints.json'), '{"platforms":');
|
|
7593
|
+
const f3Ok = J(T(['constraints', 'list', '--json'])).corruptStore === true
|
|
7594
|
+
&& J(T(['constraints', 'check', 'stripe API', '--json'])).corruptStore === true;
|
|
7595
|
+
// F5: agents recommend 존재
|
|
7596
|
+
const rec = J(cp.spawnSync(process.execPath, [CLI, 'agents', 'recommend', 'refactor payment module', '--json'], { encoding: 'utf8', timeout: 20000 }));
|
|
7597
|
+
const f5Ok = rec && rec.ok === true && typeof rec.recommended === 'string';
|
|
7598
|
+
// F6: 빈 contract 거부 + --allow-empty
|
|
7599
|
+
fs.writeFileSync(path.join(d, 'SPEC.md'), '# Spec\nprose'); fs.writeFileSync(path.join(d, 'impl.js'), '// empty');
|
|
7600
|
+
const f6Ok = cp.spawnSync(process.execPath, [CLI, 'contract', 'verify', path.join(d, 'SPEC.md'), path.join(d, 'impl.js'), '--json'], { encoding: 'utf8', timeout: 20000 }).status === 1
|
|
7601
|
+
&& cp.spawnSync(process.execPath, [CLI, 'contract', 'verify', path.join(d, 'SPEC.md'), path.join(d, 'impl.js'), '--json', '--allow-empty'], { encoding: 'utf8', timeout: 20000 }).status === 0;
|
|
7602
|
+
// (검수 #3) 배열형 platforms 도 손상 표면화 + add 거부(metadata 보존)
|
|
7603
|
+
const pc = path.join(d, '.harness', 'platform-constraints.json');
|
|
7604
|
+
fs.writeFileSync(pc, '{"platforms":[],"metadata":{"revision":7}}');
|
|
7605
|
+
const beforePc = fs.readFileSync(pc, 'utf8');
|
|
7606
|
+
const arrOk = J(T(['constraints', 'list', '--json'])).corruptStore === true
|
|
7607
|
+
&& T(['constraints', 'add', 'p', '--alias', 'p', '--constraint', 'auth:x', '--json']).status === 1
|
|
7608
|
+
&& fs.readFileSync(pc, 'utf8') === beforePc;
|
|
7609
|
+
// (검수 #2) deploy execute --json exact-once JSON
|
|
7610
|
+
const d2 = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-h10dep-')); _d.push(d2);
|
|
7611
|
+
cp.spawnSync(process.execPath, [CLI, 'init', d2, '--yes', '--no-env', '--no-stale-check'], { encoding: 'utf8', timeout: 40000 });
|
|
7612
|
+
cp.spawnSync(process.execPath, [CLI, 'team', 'add', 'dep', '--members', 'claude', '--deploy', 'node -e "process.exit(0)"', '--path', d2], { encoding: 'utf8', timeout: 20000 });
|
|
7613
|
+
const depExec = cp.spawnSync(process.execPath, [CLI, 'team', 'deploy', 'dep', '--yes', '--json', '--path', d2], { encoding: 'utf8', timeout: 30000, env: { ...process.env, LEERNESS_TEAM_DEPLOY: '1' } });
|
|
7614
|
+
let depExecOk = false; try { const j = JSON.parse(depExec.stdout); depExecOk = j.executed === true && j.exitStatus === 0 && depExec.status === 0; } catch {}
|
|
7615
|
+
ok = f1Ok && f1bOk && f4addOk && f4dupOk && f4rmOk && f4depOk && f2malOk && f2dupOk && f3Ok && f5Ok && f6Ok && arrOk && depExecOk;
|
|
7616
|
+
if (!ok) console.log(` [h10 디버그] f1=${f1Ok} f1b=${f1bOk} f4add=${f4addOk} f4dup=${f4dupOk} f4rm=${f4rmOk} f4dep=${f4depOk} f2mal=${f2malOk} f2dup=${f2dupOk} f3=${f3Ok} f5=${f5Ok} f6=${f6Ok} arr=${arrOk} depExec=${depExecOk}`);
|
|
7617
|
+
} catch (e) {} finally { _d.forEach(x => { try { fs.rmSync(x, { recursive: true, force: true }); } catch {} }); }
|
|
7618
|
+
console.log(ok ? '✓ B(1.36.78) 10차헌트: team 형상거부/정규화·JSON계약 · constraints 검증/dedup/손상표면 · agents recommend · 빈 contract 거부' : '✗ 10차 헌트 수정 실패');
|
|
7619
|
+
if (!ok) failed++;
|
|
7620
|
+
}
|
|
7621
|
+
|
|
7514
7622
|
console.log(`\nE2E result: ${total - failed}/${total} passed · ${((Date.now() - _e2eStart) / 1000).toFixed(0)}s`);
|
|
7515
7623
|
if (failed > 0) process.exit(1);
|