leerness 1.9.407 → 1.9.408

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,19 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.9.408 — 2026-06-07 — CRLF/CR SKILL.md frontmatter 파싱 복구 (8번째 버그헌트, UR-0112)
4
+
5
+ **🪟 Windows 호환 — CRLF/CR 줄바꿈의 SKILL.md frontmatter 가 통째로 소실돼 `skill install` 이 "name 필수" 로 실패하던 것 수정.**
6
+
7
+ ### 배경 (2차 버그헌트 encoding 차원 · Windows 직격)
8
+ `_parseSkillMd` 의 frontmatter 값 정규식 `(.+)$` 의 `.` 은 CR(`\r`)을 매칭하지 못함 → `name: x\r` 라인이 통째로 매칭 실패 → CRLF SKILL.md(Windows/Notepad 기본)의 meta 가 `{}` 로 소실 → `skill install` 이 frontmatter 의 name 을 못 찾아 "name 필수" 로 실패. 직접 재현: LF 는 정상, CRLF 는 meta={}.
9
+
10
+ ### 구현
11
+ - `_parseSkillMd` 상단에서 BOM strip 후 `replace(/\r\n?/g, '\n')` 로 CRLF/CR → LF 정규화. 모든 줄바꿈 스타일(LF/CRLF/CR/BOM+CRLF) 동일 파싱.
12
+
13
+ ### 검증 (회귀 0)
14
+ - **selftest 153→154 PASS** (LF/CRLF/CR/BOM+CRLF 4종 meta 파싱).
15
+ - **E2E 346→347 PASS** (CRLF SKILL.md `skill install` exit0 + skill list 에 등록 확인).
16
+
3
17
  ## 1.9.407 — 2026-06-07 — MCP feature_link 권한경계 + NaN --limit 가드 (8번째 버그헌트, UR-0111)
4
18
 
5
19
  **🛡 MCP 권한경계 — read-only로 잘못 선언된 그래프-변경 도구 수정 + NaN --limit 가 결과를 조용히 숨기던 것 차단.**
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.407-green)]() [![tests](https://img.shields.io/badge/e2e-346%2F346-success)]() [![selftest](https://img.shields.io/badge/selftest-153-success)]() [![mcp](https://img.shields.io/badge/MCP--tools-85-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.408-green)]() [![tests](https://img.shields.io/badge/e2e-347%2F347-success)]() [![selftest](https://img.shields.io/badge/selftest-154-success)]() [![mcp](https://img.shields.io/badge/MCP--tools-85-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,7 @@ MIT — © leerness contributors
471
471
  <!-- leerness:project-readme:start -->
472
472
  ## Leerness Project Harness
473
473
 
474
- 이 프로젝트는 Leerness v1.9.407 하네스를 사용합니다. AI 에이전트는 작업 전 `leerness handoff`로 컨텍스트를 적재하고, 작업 후 `leerness check`/`leerness audit`/`leerness session close`를 수행해야 합니다.
474
+ 이 프로젝트는 Leerness v1.9.408 하네스를 사용합니다. AI 에이전트는 작업 전 `leerness handoff`로 컨텍스트를 적재하고, 작업 후 `leerness check`/`leerness audit`/`leerness session close`를 수행해야 합니다.
475
475
 
476
476
  ### 정체성 — AI 에이전트 운영 레이어 (UR-0030)
477
477
 
@@ -525,7 +525,7 @@ leerness memory restore decision <date|title>
525
525
 
526
526
  ### MCP server (외부 AI 통합)
527
527
 
528
- Leerness v1.9.407는 stdio JSON-RPC MCP server를 내장합니다 — Claude Code · Cursor · Codex CLI 등 외부 AI에 **85개 도구**를 노출:
528
+ Leerness v1.9.408는 stdio JSON-RPC MCP server를 내장합니다 — Claude Code · Cursor · Codex CLI 등 외부 AI에 **85개 도구**를 노출:
529
529
 
530
530
  ```jsonc
531
531
  // 카테고리별
@@ -546,7 +546,7 @@ Leerness v1.9.407는 stdio JSON-RPC MCP server를 내장합니다 — Claude Cod
546
546
  `<<autonomous-loop-dynamic>>` 신호만 보내면 AI가:
547
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) 다음 라운드 예약.
548
548
 
549
- 현재 누적: **70 라운드 (1.9.40 → 1.9.407)** · 매 라운드 GitHub release/태그 생성 · _reports/는 비공개 보존.
549
+ 현재 누적: **70 라운드 (1.9.40 → 1.9.408)** · 매 라운드 GitHub release/태그 생성 · _reports/는 비공개 보존.
550
550
 
551
551
  ### 성능 가이드 (1.9.140 측정)
552
552
 
@@ -584,6 +584,6 @@ leerness release pack --close --auto-main-push
584
584
  - `.harness/session-handoff.md`: 다음 세션 인수인계 (자동 작성)
585
585
  - `.harness/lessons.md` / `decisions.md` / `rules.md`: 영구 메모리 (5 surface)
586
586
 
587
- Last synced by Leerness v1.9.407: 2026-06-06
587
+ Last synced by Leerness v1.9.408: 2026-06-06
588
588
  <!-- leerness:project-readme:end -->
589
589
 
package/bin/harness.js CHANGED
@@ -31,7 +31,7 @@ const { _evidenceQuality, _parseEvidenceStats, _shellGuardAnalyze, _claimFileInG
31
31
  // 1.9.295 (UR-0025 4단계): 정적 데이터 카탈로그 모듈 분리 (비파괴, require-based).
32
32
  const { CAPABILITY_SURFACE, POWERFUL_COMMANDS, ADAPTERS, REUSE_CATEGORIES, REUSE_CHECKLIST, _DEFAULT_PLATFORM_CONSTRAINTS, _DEFAULT_DOMAIN_CATALOG, _LSP_LANG_PATTERNS, OPTIMISM_PATTERNS, BUILT_IN_PERSONAS, STRINGS, BUILTIN_CATALOG, ROADMAP_STATUS_LABEL, ROADMAP_STATUS_COLOR, SECRET_PATTERNS, MERGE_OVERWRITE_FILES, MINIMAL_SKIP_KEYS, REQUIRED_WORKSPACE_FILES, KEYWORD_STOPWORDS, SKILL_CATALOG_PRESETS } = require('../lib/catalogs'); // 1.9.344/368/369 (UR-0025): catalog 분리 (MERGE_OVERWRITE_FILES/MINIMAL_SKIP_KEYS 포함)
33
33
 
34
- const VERSION = '1.9.407';
34
+ const VERSION = '1.9.408';
35
35
 
36
36
  // 1.9.290 (UR-0037, Codex gpt-5.5 #4 수렴): CLI 전용 부작용은 require 시 실행하지 않는다.
37
37
  // 이전: warning listener 제거 / NODE_OPTIONS 변경 / chcp IIFE 가 top-level 즉시 실행 → require('harness') 시 호스트 프로세스 오염.
@@ -3012,6 +3012,7 @@ function _selfTestCases() {
3012
3012
  { name: '8번째 버그헌트 회귀수정 (UR-0109): 긴 서술형 placeholder FP 차단(마커 우선) + 실키 FN 유지 (1.9.405)', run: () => { const m = require('../lib/pure-utils'); const ph = m._isPlaceholderSecret; const fpFixed = ph('your-super-secret-api-key-example-value') === true && ph('this-is-just-an-example-placeholder-value') === true && ph('example-api-key-do-not-use-1234567890') === true; const fnKept = ph('sk-EXAMPLEab12cd34ef56gh78ij90kl') === false && ph('sk-proj-realKEYexample9988776655') === false; const realKept = ph('a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6') === false; const shortPh = ph('your-api-key-here') === true && ph('changeme') === true; return fpFixed && fnKept && realKept && shortPh; } },
3013
3013
  { name: '8번째 버그헌트 (UR-0110): rule/decision/lesson add 동시쓰기 _withLock 직렬화 (UR-0043 갭 메움) (1.9.406)', run: () => { const src = read(__filename); const L = '_withLock('; const ruleLock = src.includes(L + 'rulesPath' + '(root), () =>'); const decLock = src.includes(L + 'decisionsJsonPath' + '(root), () =>'); const lesLock = src.includes(L + 'lessonsJsonPath' + '(root), () =>'); return ruleLock && decLock && lesLock; } },
3014
3014
  { name: '8번째 버그헌트 (UR-0111): MCP feature_link safe-write tier(권한경계) + _parseLimit NaN/음수 가드 (1.9.407)', run: () => { const m = require('../lib/pure-utils'); const pl = m._parseLimit('abc', 10) === 10 && m._parseLimit('-5', 10) === 10 && m._parseLimit('0', 10) === 10 && m._parseLimit('3', 10) === 3 && m._parseLimit('7.9', 10) === 7; const tools = require('../lib/mcp-tools'); const arr = Array.isArray(tools) ? tools : (tools.MCP_TOOLS || []); const fl = arr.find(x => x.name === 'leerness_feature_link'); const tierOk = !!fl && fl.requiredTier === 'safe-write'; return pl && tierOk; } },
3015
+ { name: '8번째 버그헌트 (UR-0112): _parseSkillMd CRLF/CR 줄바꿈 정규화 (Windows SKILL.md meta 소실 차단) (1.9.408)', run: () => { const m = require('../lib/pure-utils'); const lf = m._parseSkillMd('---\nname: s\ndescription: d\n---\nbody'); const crlf = m._parseSkillMd('---\r\nname: s\r\ndescription: d\r\n---\r\nbody'); const cr = m._parseSkillMd('---\rname: s\rdescription: d\r---\rbody'); const bom = m._parseSkillMd('---\r\nname: s\r\n---\r\nbody'); return lf.meta.name === 's' && crlf.meta.name === 's' && crlf.meta.description === 'd' && cr.meta.name === 's' && bom.meta.name === 's'; } },
3015
3016
  { name: 'VERSION 형식 (x.y.z)', run: () => /^\d+\.\d+\.\d+$/.test(VERSION) }
3016
3017
  ];
3017
3018
  }
package/lib/pure-utils.js CHANGED
@@ -432,7 +432,9 @@ function _roadmapTokenStyles(designTokens, cssVariables) {
432
432
 
433
433
  // 1.9.347 (UR-0025 심층): SKILL.md frontmatter 파서 — { meta, body }, BOM-aware (Windows Notepad 호환). 순수.
434
434
  function _parseSkillMd(text) {
435
- const cleaned = String(text || '').replace(/^/, ''); // BOM strip (U+FEFF)
435
+ // 1.9.408 (8번째 버그헌트, UR-0112): BOM strip + CRLF/CR→LF 정규화.
436
+ // 기존 버그: frontmatter 값 정규식 (.+)$ 의 '.'은 CR(\r)을 매칭 못 해 'name: x\r' 라인이 통째로 실패 → CRLF SKILL.md(Windows/Notepad)의 meta 전체 소실 → skill install "name 필수" 실패.
437
+ const cleaned = String(text || '').replace(/^/, '').replace(/\r\n?/g, '\n'); // BOM strip (U+FEFF) + 줄바꿈 정규화
436
438
  const m = cleaned.match(/^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/);
437
439
  if (!m) return { meta: {}, body: cleaned };
438
440
  const meta = {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leerness",
3
- "version": "1.9.407",
3
+ "version": "1.9.408",
4
4
  "description": "Leerness: 비파괴 마이그레이션, 자동 버전 감지·업데이트, 계획/진행/핸드오프 자동화, 게으름·시크릿·인코딩 자동 가드, Claude Code 슬래시 통합을 갖춘 한국어 우선 AI 개발 하네스.",
5
5
  "keywords": [
6
6
  "leerness",
package/scripts/e2e.js CHANGED
@@ -5767,5 +5767,28 @@ total++;
5767
5767
  if (!ok) failed++;
5768
5768
  }
5769
5769
 
5770
+ // 1.9.408 회귀 (8번째 버그헌트, UR-0112): CRLF/CR SKILL.md frontmatter 파싱 — Windows/Notepad 줄바꿈으로 meta 소실되던 skill install 복구
5771
+ total++;
5772
+ {
5773
+ let ok = false;
5774
+ try {
5775
+ const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-crlfskill-'));
5776
+ cp.spawnSync(process.execPath, [CLI, 'init', d, '--yes', '--language', 'ko'], { encoding: 'utf8', timeout: 30000 });
5777
+ const skillDir = path.join(d, 'crlfSkillSrc');
5778
+ fs.mkdirSync(skillDir, { recursive: true });
5779
+ // CRLF 줄바꿈 SKILL.md (Windows/Notepad)
5780
+ fs.writeFileSync(path.join(skillDir, 'SKILL.md'), '---\r\nname: crlf-skill\r\ndescription: a windows skill\r\n---\r\nbody content\r\n');
5781
+ const r = cp.spawnSync(process.execPath, [CLI, 'skill', 'install', skillDir, '--path', d], { encoding: 'utf8', timeout: 20000 });
5782
+ const installOk = r.status === 0 && !/name.{0,4}필수/.test((r.stdout || '') + (r.stderr || ''));
5783
+ // 설치 확인
5784
+ const ls = cp.spawnSync(process.execPath, [CLI, 'skill', 'list', '--path', d], { encoding: 'utf8', timeout: 15000 });
5785
+ const found = /crlf-skill/.test(ls.stdout || '');
5786
+ fs.rmSync(d, { recursive: true, force: true });
5787
+ ok = installOk && found;
5788
+ } catch {}
5789
+ console.log(ok ? '✓ B(1.9.408) 8th버그헌트: CRLF SKILL.md frontmatter 파싱 복구(Windows skill install) (UR-0112)' : '✗ CRLF SKILL.md 파싱 실패');
5790
+ if (!ok) failed++;
5791
+ }
5792
+
5770
5793
  console.log(`\nE2E result: ${total - failed}/${total} passed · ${((Date.now() - _e2eStart) / 1000).toFixed(0)}s`);
5771
5794
  if (failed > 0) process.exit(1);