leerness 1.9.295 → 1.9.296

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.9.296 — 2026-06-04 — UR-0030: 정체성 = AI 에이전트 운영 레이어 (leerness about verb)
4
+
5
+ **🧭 GPT-5.5 범용 하네스 전략의 정체성 정립 — leerness 는 "실행기"가 아니라 어떤 AI 코딩 에이전트 위에도 얹는 "운영 레이어"임을 조회 가능·테스트 가능한 형태로 명문화.**
6
+
7
+ ### 배경
8
+ GPT-5.5 범용 하네스 리뷰: leerness 의 정체성을 "AI 에이전트 운영 레이어(Memory+Policy+Handoff+Verification+Audit 공통 계층)"로 포지셔닝 권고(UR-0030). 하위 구현(MCP-first/state schema/adapters/tiers)은 이미 완료됐으나, **정체성 자체가 코드/문서에서 명시적으로 조회 가능하지 않았음**.
9
+
10
+ ### 구현 (UR-0030)
11
+ 1. **`leerness about [--json]` (alias `identity`) CLI 신설** — 정체성을 구조화 노출: identity("AI 에이전트 운영 레이어") / isNot(실행기 아님) / 5계층(기억·정책·인수인계·검증·감사) / complements(AGENTS.md 보완) / entryPoints / surface(동적: MCP 도구수·어댑터·provider·런타임의존성 0).
12
+ 2. **MCP `leerness_about` (81번째 도구)** — read-only. 어떤 에이전트든 "이 도구가 무엇이고 무엇을 보완하는가"를 1콜로 파악.
13
+ 3. **`_leernessIdentity()` 단일 출처** — about CLI / MCP / README 정체성 섹션이 공유(동적 표면 데이터는 ADAPTERS/_mcpToolCount/EXTERNAL_AGENTS 에서 합성).
14
+ 4. **README 정체성 섹션** — managedReadmeBlock 에 "정체성 — AI 에이전트 운영 레이어" 섹션 추가(5계층 + AGENTS.md 보완 관계 명시). readme sync 로 자동 propagate.
15
+ 5. selftest 43→44 · e2e 240→241. MCP 배지 80→81 자동 동기화.
16
+
17
+ ### 검증
18
+ - **selftest 44/44 PASS** · **E2E 241/241 PASS** (회귀 0).
19
+ - 실측: `about --json` → identity/5계층/surface(MCP 81·어댑터 9·provider 10·deps 0). MCP tools/list 81개에 `leerness_about` 노출. README 정체성 섹션 생성.
20
+ - B(1.9.288) 도구수 정합 가드가 배지 stale(80 vs 81) 즉시 검출 → sync 정합(Codex #5 가드 정상).
21
+
3
22
  ## 1.9.295 — 2026-06-04 — UR-0025 (4단계): 잔여 정적 카탈로그 → lib/catalogs.js — 데이터 추출 단계 완료 ✅
4
23
 
5
24
  **🧩 모놀리스 분리 4단계 — 잔여 정적 데이터 카탈로그 5종을 비파괴 분리. 이로써 leerness 의 모든 정적 데이터 카탈로그가 lib/ 모듈로 외부화됨(데이터 추출 단계 완료).**
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  > **AI 코딩 에이전트의 거짓 완료·중복·망각·충돌을 막아주는 검수·기억·협업 CLI 하네스.**
4
4
  > **A CLI harness that stops AI coding agents from faking completion, duplicating work, forgetting context, and colliding.**
5
5
 
6
- [![npm](https://img.shields.io/badge/npm-leerness-blue)](https://www.npmjs.com/package/leerness) [![version](https://img.shields.io/badge/version-1.9.295-green)]() [![tests](https://img.shields.io/badge/e2e-240%2F240-success)]() [![selftest](https://img.shields.io/badge/selftest-43-success)]() [![mcp](https://img.shields.io/badge/MCP--tools-80-brightgreen)]() [![providers](https://img.shields.io/badge/AI_providers-10-brightgreen)]() [![license](https://img.shields.io/badge/license-MIT-lightgrey)]()
6
+ [![npm](https://img.shields.io/badge/npm-leerness-blue)](https://www.npmjs.com/package/leerness) [![version](https://img.shields.io/badge/version-1.9.296-green)]() [![tests](https://img.shields.io/badge/e2e-241%2F241-success)]() [![selftest](https://img.shields.io/badge/selftest-44-success)]() [![mcp](https://img.shields.io/badge/MCP--tools-81-brightgreen)]() [![providers](https://img.shields.io/badge/AI_providers-10-brightgreen)]() [![license](https://img.shields.io/badge/license-MIT-lightgrey)]()
7
7
 
8
8
  ```
9
9
  ╔══════════════════════════════════════════════════════════════╗
@@ -471,7 +471,19 @@ MIT — © leerness contributors
471
471
  <!-- leerness:project-readme:start -->
472
472
  ## Leerness Project Harness
473
473
 
474
- 이 프로젝트는 Leerness v1.9.295 하네스를 사용합니다. AI 에이전트는 작업 전 `leerness handoff`로 컨텍스트를 적재하고, 작업 후 `leerness check`/`leerness audit`/`leerness session close`를 수행해야 합니다.
474
+ 이 프로젝트는 Leerness v1.9.296 하네스를 사용합니다. AI 에이전트는 작업 전 `leerness handoff`로 컨텍스트를 적재하고, 작업 후 `leerness check`/`leerness audit`/`leerness session close`를 수행해야 합니다.
475
+
476
+ ### 정체성 — AI 에이전트 운영 레이어 (UR-0030)
477
+
478
+ Leerness 는 **실행기/코딩 에이전트가 아니라**, 어떤 AI 코딩 에이전트(Claude Code · Codex · Cursor · Goose 등) 위에도 얹는 **범용 운영 레이어**입니다. 5개 공통 계층을 제공합니다:
479
+
480
+ - **기억(Memory)** — 프로젝트 상태/결정/진행을 `.leerness/` 에 영속화
481
+ - **정책(Policy)** — 8단계 권한 등급 + enforce (read-only→publish), MCP 호출 게이트
482
+ - **인수인계(Handoff)** — 에이전트 간 컨텍스트 표준 전달 + `get_project_context` 1콜 온보딩
483
+ - **검증(Verification)** — 근거 기반 완료 검증으로 허위 완료 차단
484
+ - **감사(Audit)** — drift/idempotency/secret/encoding 자동 감사 + self-heal
485
+
486
+ AGENTS.md(정적 지침)을 **대체하지 않고 보완**합니다 — 정적 규칙은 AGENTS.md, 동적 상태·검증·인수인계는 leerness. 정체성 조회: `leerness about` (MCP `leerness_about`).
475
487
 
476
488
  ### Core Commands
477
489
 
@@ -513,7 +525,7 @@ leerness memory restore decision <date|title>
513
525
 
514
526
  ### MCP server (외부 AI 통합)
515
527
 
516
- Leerness v1.9.295는 stdio JSON-RPC MCP server를 내장합니다 — Claude Code · Cursor · Codex CLI 등 외부 AI에 **80개 도구**를 노출:
528
+ Leerness v1.9.296는 stdio JSON-RPC MCP server를 내장합니다 — Claude Code · Cursor · Codex CLI 등 외부 AI에 **81개 도구**를 노출:
517
529
 
518
530
  ```jsonc
519
531
  // 카테고리별
@@ -526,7 +538,7 @@ Leerness v1.9.295는 stdio JSON-RPC MCP server를 내장합니다 — Claude Cod
526
538
  // • Workflow: session_close / agents_list / task_export / env_check / usage_stats / reuse_map / whats_new
527
539
 
528
540
  // MCP server 실행: leerness mcp serve
529
- // tools/list 응답: 80 도구
541
+ // tools/list 응답: 81 도구
530
542
  ```
531
543
 
532
544
  ### Autonomous mode (자율 모드)
@@ -534,7 +546,7 @@ Leerness v1.9.295는 stdio JSON-RPC MCP server를 내장합니다 — Claude Cod
534
546
  `<<autonomous-loop-dynamic>>` 신호만 보내면 AI가:
535
547
  1) 다음 라운드 후보 선정 → 2) 코드 변경 → 3) stress-v* 신규 작성 + 누적 회귀 → 4) e2e 219/219 → 5) npm pack + git tag + GitHub release → 6) main 자동 push (1.9.140+) → 7) session close → 8) 다음 라운드 예약.
536
548
 
537
- 현재 누적: **70 라운드 (1.9.40 → 1.9.295)** · 매 라운드 GitHub release/태그 생성 · _reports/는 비공개 보존.
549
+ 현재 누적: **70 라운드 (1.9.40 → 1.9.296)** · 매 라운드 GitHub release/태그 생성 · _reports/는 비공개 보존.
538
550
 
539
551
  ### 성능 가이드 (1.9.140 측정)
540
552
 
@@ -572,6 +584,6 @@ leerness release pack --close --auto-main-push
572
584
  - `.harness/session-handoff.md`: 다음 세션 인수인계 (자동 작성)
573
585
  - `.harness/lessons.md` / `decisions.md` / `rules.md`: 영구 메모리 (5 surface)
574
586
 
575
- Last synced by Leerness v1.9.295: 2026-06-04
587
+ Last synced by Leerness v1.9.296: 2026-06-04
576
588
  <!-- leerness:project-readme:end -->
577
589
 
package/bin/harness.js CHANGED
@@ -12,7 +12,7 @@ const { _isSecretKey, compareVer, parseHarnessVersion, _classifyCJK, _riskLabel,
12
12
  // 1.9.295 (UR-0025 4단계): 정적 데이터 카탈로그 모듈 분리 (비파괴, require-based).
13
13
  const { CAPABILITY_SURFACE, POWERFUL_COMMANDS, ADAPTERS, REUSE_CATEGORIES, REUSE_CHECKLIST } = require('../lib/catalogs');
14
14
 
15
- const VERSION = '1.9.295';
15
+ const VERSION = '1.9.296';
16
16
 
17
17
  // 1.9.290 (UR-0037, Codex gpt-5.5 #4 수렴): CLI 전용 부작용은 require 시 실행하지 않는다.
18
18
  // 이전: warning listener 제거 / NODE_OPTIONS 변경 / chcp IIFE 가 top-level 즉시 실행 → require('harness') 시 호스트 프로세스 오염.
@@ -288,6 +288,18 @@ function managedReadmeBlock(project) {
288
288
  '',
289
289
  `이 프로젝트는 Leerness v${VERSION} 하네스를 사용합니다. AI 에이전트는 작업 전 \`leerness handoff\`로 컨텍스트를 적재하고, 작업 후 \`leerness check\`/\`leerness audit\`/\`leerness session close\`를 수행해야 합니다.`,
290
290
  '',
291
+ '### 정체성 — AI 에이전트 운영 레이어 (UR-0030)',
292
+ '',
293
+ 'Leerness 는 **실행기/코딩 에이전트가 아니라**, 어떤 AI 코딩 에이전트(Claude Code · Codex · Cursor · Goose 등) 위에도 얹는 **범용 운영 레이어**입니다. 5개 공통 계층을 제공합니다:',
294
+ '',
295
+ '- **기억(Memory)** — 프로젝트 상태/결정/진행을 `.leerness/` 에 영속화',
296
+ '- **정책(Policy)** — 8단계 권한 등급 + enforce (read-only→publish), MCP 호출 게이트',
297
+ '- **인수인계(Handoff)** — 에이전트 간 컨텍스트 표준 전달 + `get_project_context` 1콜 온보딩',
298
+ '- **검증(Verification)** — 근거 기반 완료 검증으로 허위 완료 차단',
299
+ '- **감사(Audit)** — drift/idempotency/secret/encoding 자동 감사 + self-heal',
300
+ '',
301
+ 'AGENTS.md(정적 지침)을 **대체하지 않고 보완**합니다 — 정적 규칙은 AGENTS.md, 동적 상태·검증·인수인계는 leerness. 정체성 조회: `leerness about` (MCP `leerness_about`).',
302
+ '',
291
303
  '### Core Commands',
292
304
  '',
293
305
  '```bash',
@@ -3004,6 +3016,7 @@ function _selfTestCases() {
3004
3016
  { name: '_canonicalProgressHeader + idempotency auto-fix (근본 복제버그 fix 1.9.293)', run: () => { const h = _canonicalProgressHeader(); const headerOk = /leernessRole: progress-tracker/.test(h) && /\| ID \| Status \| Request \|/.test(h) && /\|---\|/.test(h); const src = read(__filename); const fnOk = typeof _autoFixIdempotency === 'function'; const noWholeTextFallback = /if \(idx < 0\) return _canonicalProgressHeader\(\);/.test(src); const driftWired = /_autoFixIdempotency\(root\)/.test(src) && /idempotency 중복/.test(src); return headerOk && fnOk && noWholeTextFallback && driftWired; } },
3005
3017
  { name: 'lib/role-catalog: ROLE/PROVIDER/ALIASES/PROMPTS 모듈 단일출처 분리 (UR-0025 1.9.294)', run: () => { const m = require('../lib/role-catalog'); return m.ROLE_CATALOG === ROLE_CATALOG && m._PROVIDER_MODEL_CATALOG === _PROVIDER_MODEL_CATALOG && m._ROLE_ALIASES === _ROLE_ALIASES && m._AGENT_ROLE_PROMPTS === _AGENT_ROLE_PROMPTS && Object.keys(m.ROLE_CATALOG).length === 7 && Object.keys(m._PROVIDER_MODEL_CATALOG).length === 10 && !/const ROLE_CATALOG = \{/.test(read(__filename)); } },
3006
3018
  { name: 'lib/catalogs: CAPABILITY/ADAPTERS/REUSE 모듈 단일출처 분리 (UR-0025 1.9.295)', run: () => { const m = require('../lib/catalogs'); return m.CAPABILITY_SURFACE === CAPABILITY_SURFACE && m.ADAPTERS === ADAPTERS && m.REUSE_CATEGORIES === REUSE_CATEGORIES && m.REUSE_CHECKLIST === REUSE_CHECKLIST && m.POWERFUL_COMMANDS === POWERFUL_COMMANDS && Object.keys(m.CAPABILITY_SURFACE).length === 6 && !/const CAPABILITY_SURFACE = \{/.test(read(__filename)); } },
3019
+ { name: 'about: 정체성 verb(AI 운영 레이어) + MCP leerness_about 등록 (UR-0030 1.9.296)', run: () => { const id = _leernessIdentity(); const src = read(__filename); return typeof aboutCmd === 'function' && /운영 레이어/.test(id.identity) && id.layers.length === 5 && id.surface.mcpTools >= 81 && /name: 'leerness_about'/.test(src) && /case 'leerness_about':/.test(src) && /cmd === 'about' \|\| cmd === 'identity'/.test(src); } },
3007
3020
  { name: 'VERSION 형식 (x.y.z)', run: () => /^\d+\.\d+\.\d+$/.test(VERSION) }
3008
3021
  ];
3009
3022
  }
@@ -16634,7 +16647,8 @@ function mcpServeCmd(root) {
16634
16647
  { name: 'leerness_state_record', 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' } } } },
16635
16648
  { name: 'leerness_state_verify', 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'] } },
16636
16649
  { name: 'leerness_state_handoff', 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'] } },
16637
- { name: 'leerness_get_project_context', 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' } } } }
16650
+ { name: 'leerness_get_project_context', 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' } } } },
16651
+ { name: 'leerness_about', description: '1.9.296 (UR-0030) — leerness 정체성/포지셔닝 회수. leerness 가 무엇인가: "AI 에이전트 운영 레이어"(실행기 아님) — 기억·정책·인수인계·검증·감사 5계층. read-only. 어떤 에이전트든 "이 도구가 무엇이고 무엇을 보완하는가"를 1콜로 파악. 응답: { identity, isNot, tagline, layers[], complements, entryPoints, surface }.', inputSchema: { type: 'object', properties: {} } }
16638
16652
  ];
16639
16653
 
16640
16654
  function send(obj) {
@@ -16887,6 +16901,9 @@ function mcpServeCmd(root) {
16887
16901
  case 'leerness_get_project_context':
16888
16902
  cliArgs = ['context', '--path', targetPath, '--json'];
16889
16903
  break;
16904
+ case 'leerness_about':
16905
+ cliArgs = ['about', '--json'];
16906
+ break;
16890
16907
  default:
16891
16908
  return send({ jsonrpc: '2.0', id, error: { code: -32601, message: `Unknown tool: ${name}` } });
16892
16909
  }
@@ -18104,6 +18121,56 @@ function _splitList(v) { return String(v || '').split(',').map(s => s.trim()).fi
18104
18121
  // 1.9.292 (UR-0031): get_project_context — 외부 에이전트 온보딩용 단일 집약 컨텍스트.
18105
18122
  // 1콜로 현재 작업/미답 요청/최근 결정/활성 룰/next-actions/memory/프로젝트 의도를 구조화 회수.
18106
18123
  // MCP leerness_get_project_context 가 이 명령(--json)을 호출. handoff(인간용 장문)와 달리 기계 친화 lean payload.
18124
+ // 1.9.296 (UR-0030, GPT-5.5 범용 하네스 전략): leerness 정체성 = 'AI 에이전트 운영 레이어'.
18125
+ // 실행기가 아니라 기억·정책·인수인계·검증·감사의 공통 계층. 어떤 AI 코딩 에이전트 위에도 얹힌다.
18126
+ // _leernessIdentity() 단일 출처 — about 명령 / MCP leerness_about / README 정체성 섹션이 공유.
18127
+ function _leernessIdentity() {
18128
+ let adapterIds = [];
18129
+ try { adapterIds = Object.keys(ADAPTERS); } catch {}
18130
+ let providerCount = 0;
18131
+ try { providerCount = EXTERNAL_AGENTS.length; } catch {}
18132
+ return {
18133
+ name: 'leerness',
18134
+ version: VERSION,
18135
+ identity: 'AI 에이전트 운영 레이어 (AI agent operating layer)',
18136
+ isNot: '실행기/코딩 에이전트가 아님 — 어떤 에이전트 위에도 얹는 공통 운영 계층',
18137
+ tagline: '어떤 AI 코딩 에이전트에도 적용되는 범용 운영 레이어 — 기억·정책·인수인계·검증·감사',
18138
+ layers: [
18139
+ { key: 'memory', ko: '기억', desc: '프로젝트 상태/결정/진행을 .leerness/ 에 영속화 (state start|record|verify|handoff)' },
18140
+ { key: 'policy', ko: '정책', desc: '8단계 권한 등급 + enforce (read-only→publish), MCP 호출 게이트' },
18141
+ { key: 'handoff', ko: '인수인계', desc: '에이전트 간 컨텍스트 표준 전달 (Claude→Codex→Goose), get_project_context 1콜 온보딩' },
18142
+ { key: 'verification', ko: '검증', desc: '근거 기반 완료 검증 (verify-claim --require-evidence) — 허위 완료 차단' },
18143
+ { key: 'audit', ko: '감사', desc: 'drift/idempotency/secret/encoding 자동 감사 + self-heal (--auto-fix)' }
18144
+ ],
18145
+ complements: 'AGENTS.md(정적 지침)을 대체하지 않고 보완 — 정적 규칙/명령/금지는 AGENTS.md, 동적 상태·기억·검증·인수인계는 leerness',
18146
+ entryPoints: { context: 'leerness context (MCP get_project_context)', mcp: 'leerness mcp serve', adapter: 'leerness adapter <tool>' },
18147
+ surface: { mcpTools: _mcpToolCount(), adapters: adapterIds, providers: providerCount, runtimeDeps: 0, offlineFirst: true }
18148
+ };
18149
+ }
18150
+ function aboutCmd(opts = {}) {
18151
+ const id = _leernessIdentity();
18152
+ if (opts.json || has('--json')) { process.stdout.write(JSON.stringify(id, null, 2) + '\n'); return id; }
18153
+ const isTty = process.stdout && process.stdout.isTTY;
18154
+ const cy = s => isTty ? `\x1b[36m${s}\x1b[0m` : s;
18155
+ const gr = s => isTty ? `\x1b[32m${s}\x1b[0m` : s;
18156
+ const dm = s => isTty ? `\x1b[2m${s}\x1b[0m` : s;
18157
+ log(cy(`# ${id.name} v${id.version} — ${id.identity}`));
18158
+ log('');
18159
+ log(` ${id.tagline}`);
18160
+ log(dm(` ✗ ${id.isNot}`));
18161
+ log('');
18162
+ log(gr('## 5 운영 계층 (Memory · Policy · Handoff · Verification · Audit)'));
18163
+ id.layers.forEach(l => log(` • ${l.ko} (${l.key}) — ${l.desc}`));
18164
+ log('');
18165
+ log(gr('## 보완 관계'));
18166
+ log(` ${id.complements}`);
18167
+ log('');
18168
+ log(gr('## 진입점'));
18169
+ Object.values(id.entryPoints).forEach(e => log(dm(` • ${e}`)));
18170
+ log('');
18171
+ log(`표면: MCP ${id.surface.mcpTools} 도구 · 어댑터 ${id.surface.adapters.length} · provider ${id.surface.providers} · 런타임 의존성 ${id.surface.runtimeDeps} · offline-first ${id.surface.offlineFirst ? '✓' : '✗'}`);
18172
+ return id;
18173
+ }
18107
18174
  function contextCmd(root, opts = {}) {
18108
18175
  root = absRoot(root);
18109
18176
  const rows = readProgressRows(root);
@@ -21350,6 +21417,7 @@ async function main() {
21350
21417
  // 1.9.278 (UR-0032): leerness state <show|start|record|verify|handoff> — .leerness/ 구조화 상태 substrate
21351
21418
  if (cmd === 'state') return stateCmd(arg('--path', process.cwd()), args[1], ...args.slice(2));
21352
21419
  if (cmd === 'context') return contextCmd(arg('--path', process.cwd()), { json: has('--json') });
21420
+ if (cmd === 'about' || cmd === 'identity') return aboutCmd({ json: has('--json') });
21353
21421
  // 1.9.281 (UR-0034): leerness policy <show|set|check> — 권한 등급 (opt-in enforced)
21354
21422
  if (cmd === 'policy') return policyCmd(arg('--path', process.cwd()), args[1], ...args.slice(2));
21355
21423
  // 1.9.285 (UR-0023): leerness reuse-check "<기능>" — 외부 OSS 빌드 vs 재사용 결정 게이트 (오프라인)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leerness",
3
- "version": "1.9.295",
3
+ "version": "1.9.296",
4
4
  "description": "Leerness: 비파괴 마이그레이션, 자동 버전 감지·업데이트, 계획/진행/핸드오프 자동화, 게으름·시크릿·인코딩 자동 가드, Claude Code 슬래시 통합을 갖춘 한국어 우선 AI 개발 하네스.",
5
5
  "keywords": [
6
6
  "leerness",
package/scripts/e2e.js CHANGED
@@ -3225,5 +3225,29 @@ total++;
3225
3225
  if (!ok) failed++;
3226
3226
  }
3227
3227
 
3228
+ // 1.9.296 회귀 (UR-0030): leerness about — 정체성(AI 운영 레이어) verb + MCP + README 섹션
3229
+ total++;
3230
+ {
3231
+ let ok = false;
3232
+ try {
3233
+ const r = cp.spawnSync(process.execPath, [CLI, 'about', '--json'], { cwd: tmp, encoding: 'utf8', timeout: 20000 });
3234
+ const j = JSON.parse(r.stdout);
3235
+ const structOk = r.status === 0 && /운영 레이어/.test(j.identity) && Array.isArray(j.layers) && j.layers.length === 5 &&
3236
+ j.layers.every(l => l.key && l.ko && l.desc) && j.complements && j.surface && typeof j.surface.mcpTools === 'number' &&
3237
+ j.surface.runtimeDeps === 0 && /AGENTS\.md/.test(j.complements);
3238
+ // MCP tools/list 에 leerness_about 노출 (81 도구)
3239
+ const ml = cp.spawnSync(process.execPath, [CLI, 'mcp', 'serve'], { cwd: tmp, encoding: 'utf8', timeout: 15000, input: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'tools/list' }) + '\n' });
3240
+ const names = JSON.parse(ml.stdout.split('\n').filter(Boolean)[0]).result.tools.map(t => t.name);
3241
+ const mcpOk = names.includes('leerness_about') && names.length >= 81;
3242
+ // README 정체성 섹션: readme sync 후 tmp/README.md 에 섹션 생성 확인
3243
+ cp.spawnSync(process.execPath, [CLI, 'readme', 'sync', tmp], { cwd: tmp, encoding: 'utf8', timeout: 20000 });
3244
+ const rd = fs.existsSync(path.join(tmp, 'README.md')) ? fs.readFileSync(path.join(tmp, 'README.md'), 'utf8') : '';
3245
+ const readmeOk = /정체성 — AI 에이전트 운영 레이어/.test(rd);
3246
+ ok = structOk && mcpOk && readmeOk;
3247
+ } catch {}
3248
+ console.log(ok ? '✓ B(1.9.296) leerness about: 정체성 verb + MCP leerness_about(81) + README 섹션 (UR-0030)' : '✗ about 정체성 verb 실패');
3249
+ if (!ok) failed++;
3250
+ }
3251
+
3228
3252
  console.log(`\nE2E result: ${total - failed}/${total} passed · ${((Date.now() - _e2eStart) / 1000).toFixed(0)}s`);
3229
3253
  if (failed > 0) process.exit(1);