leerness 1.9.142 → 1.9.144

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,64 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.9.144 — 2026-05-20
4
+
5
+ **사용자 명시 요청 3종 — 배너 문구 변경 + 1초 홀드 + quickStart UI/UX 전면 개선.**
6
+
7
+ ### Changed — 슬로건/배너 문구 변경 (사용자 요청 #1)
8
+ - 기존: "한국어 우선 AI 개발 하네스" / "Korean-first AI Development Harness"
9
+ - 신규: **"AI 에이전트 검수·기억·드리프트 방지 하네스"** / "AI Agent Reliability Harness"
10
+ - 배너 슬로건 라인: `verify · remember · orchestrate · audit` (실제 leerness 동작 반영)
11
+ - 박스 폭 보존 (padding 재조정)
12
+
13
+ ### Added — 배너 모션 후 1초 홀드 (사용자 요청 #2)
14
+ - ASCII wave 애니메이션 settle 후 **1000ms 홀드** — 사용자 시선이 배너에 자리잡도록
15
+ - `LEERNESS_BANNER_HOLD_MS` 환경변수로 조정 (기본 1000, max 5000, 0 시 스킵)
16
+ - non-TTY / `LEERNESS_NO_ANIMATE` / `--no-animate` 시 홀드 없음
17
+
18
+ ### Added — quickStart 부드러운 cascade 표시
19
+ - 각 라인 사이 8ms cascade (~기본값) — 부드러운 표시
20
+ - `LEERNESS_CASCADE_MS` 로 조정 (0~100ms, 기본 8)
21
+ - non-TTY 시 즉시 출력 (CI/pipe 호환 보장)
22
+
23
+ ### Changed — quickStart UI/UX 전면 개선 (사용자 요청 #3)
24
+ - 카테고리 6개 그룹 (이전: 2개 그룹):
25
+ 1. **✨ 시작하기 (3단계면 끝)** — init / handoff / session close 핵심 트리오
26
+ 2. **🧠 메모리 5종 CRUD (1.9.142 — cascade 방지)** — task/decision/lesson/plan/rule/feature
27
+ 3. **🔗 인과관계 + 영향 추적 (1.9.141~143)** — feature impact/list/audit
28
+ 4. **🛡 보안·드리프트·게으름 가드** — drift/lazy/env/health
29
+ 5. **🤖 외부 AI 통합 (MCP 46 도구)** — mcp serve/memory status/archive
30
+ 6. **🚀 Release 자동화** — release pack/sync-main
31
+ - 명령 컬럼 폭 42 char padEnd 정렬 (가독성 향상)
32
+ - 1️⃣ 2️⃣ 3️⃣ 단계 표시 (3-step quickest path)
33
+ - 설명 짧고 의미 중심 (실제 효과 강조)
34
+ - 끝에 CTA: "자세히: leerness --help · 자율 모드: `<<autonomous-loop-dynamic>>`"
35
+
36
+ ### Validation
37
+ - stress-v89: PASS (phrase 변경 + cascade 환경변수 + UI 카테고리 + 누적 회귀)
38
+ - e2e: 219/219 PASS
39
+
40
+ ## 1.9.143 — 2026-05-20
41
+
42
+ **JSON 4종 featureGraph 통합 완성 + drift check feature 신호** — 1.9.142 session close --json 패턴을 handoff/health에 확장.
43
+
44
+ ### Added — handoff --json featureGraph 통합
45
+ - `result.featureGraph = { total, edges, isolated, summary: "F<n>/E<n>[/iso<n>]" }`
46
+ - 외부 AI가 handoff 한 번에 컨텍스트 + memory surface + featureGraph 동시 회수
47
+
48
+ ### Added — health --json featureGraph 통합
49
+ - `out.featureGraph = { total, edges, isolated, summary }`
50
+ - JSON 4종 (handoff/memory status/session close/health) featureGraph 일관성 완성 (memorySurface 1.9.123 패턴과 동형)
51
+
52
+ ### Added — drift check feature graph 신호 (6번째 신호)
53
+ - 노드 ≥ 3개 + edges == 0 → weight 25
54
+ - 노드 ≥ 3개 + isolated 비율 ≥ 50% → weight 15
55
+ - `drift check --json` `fired` 배열에 노출
56
+ - 사용자 cascade 방지 의지 + 실제 사용 사이 gap 자동 감지
57
+
58
+ ### Validation
59
+ - stress-v88: PASS (handoff/health featureGraph + drift 신호 + 누적 회귀)
60
+ - e2e: 219/219 PASS
61
+
3
62
  ## 1.9.142 — 2026-05-20
4
63
 
5
64
  **Feature Graph 통합 라운드** — 1.9.141 인과관계 시스템을 audit / MCP CRUD / session close 에 통합.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  > **AI 코딩 에이전트의 거짓 완료·중복·망각·충돌을 막아주는 검수·기억·협업 CLI 하네스.**
4
4
 
5
- [![npm](https://img.shields.io/badge/npm-leerness-blue)](https://www.npmjs.com/package/leerness) [![version](https://img.shields.io/badge/version-1.9.142-green)]() [![tests](https://img.shields.io/badge/e2e-219%2F219-success)]() [![mcp](https://img.shields.io/badge/MCP--tools-46-blue)]() [![json](https://img.shields.io/badge/--json-20_commands-blueviolet)]() [![rounds](https://img.shields.io/badge/autonomous--rounds-72-blueviolet)]() [![main-push](https://img.shields.io/badge/release--main--push-auto-success)]() [![feature-graph](https://img.shields.io/badge/feature--graph--integrated-1.9.142-success)]() [![license](https://img.shields.io/badge/license-MIT-lightgrey)]()
5
+ [![npm](https://img.shields.io/badge/npm-leerness-blue)](https://www.npmjs.com/package/leerness) [![version](https://img.shields.io/badge/version-1.9.144-green)]() [![tests](https://img.shields.io/badge/e2e-219%2F219-success)]() [![mcp](https://img.shields.io/badge/MCP--tools-46-blue)]() [![json](https://img.shields.io/badge/--json-20_commands-blueviolet)]() [![rounds](https://img.shields.io/badge/autonomous--rounds-74-blueviolet)]() [![main-push](https://img.shields.io/badge/release--main--push-auto-success)]() [![banner-ux](https://img.shields.io/badge/installer--UX-1.9.144_polished-success)]() [![license](https://img.shields.io/badge/license-MIT-lightgrey)]()
6
6
 
7
7
  ```
8
8
  ╔══════════════════════════════════════════════════════════════╗
@@ -12,7 +12,7 @@
12
12
  ║ ██║ ██╔══╝ ██╔══╝ ██╔══██╗██║╚██╗██║██╔══╝ ╚════██║ ║
13
13
  ║ ███████╗███████╗███████╗██║ ██║██║ ╚████║███████╗███████║ ║
14
14
  ║ ╚══════╝╚══════╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝╚══════╝╚══════╝ ║
15
- ║ v1.9.142 AI Agent Reliability Harness ║
15
+ ║ v1.9.144 AI Agent Reliability Harness ║
16
16
  ║ verify · remember · orchestrate · audit · prevent drift ║
17
17
  ╚══════════════════════════════════════════════════════════════╝
18
18
  ```
@@ -529,7 +529,7 @@ MIT — © leerness contributors
529
529
  <!-- leerness:project-readme:start -->
530
530
  ## Leerness Project Harness
531
531
 
532
- 이 프로젝트는 Leerness v1.9.40 하네스를 사용합니다. AI 에이전트는 작업 전 `leerness handoff`로 컨텍스트를 적재하고, 작업 후 `leerness check`/`leerness audit`/`leerness session close`를 수행해야 합니다.
532
+ 이 프로젝트는 Leerness v1.9.144 하네스를 사용합니다. AI 에이전트는 작업 전 `leerness handoff`로 컨텍스트를 적재하고, 작업 후 `leerness check`/`leerness audit`/`leerness session close`를 수행해야 합니다.
533
533
 
534
534
  ### Core Commands
535
535
 
@@ -546,13 +546,90 @@ leerness session close . # 세션 종료 + handoff 자동 작성
546
546
  leerness update . # 자동 버전 감지 + 마이그레이션
547
547
  ```
548
548
 
549
+ ### Memory Surface CRUD (5 surfaces × add/list/drop)
550
+
551
+ ```bash
552
+ # Tasks
553
+ leerness task add "T-9999 작업 제목"
554
+ leerness task list --json
555
+ # Decisions
556
+ leerness decision add "결정 제목" --reason "이유"
557
+ leerness decision list --query "키워드" # 1.9.139
558
+ # Rules (영구 자연어 룰)
559
+ leerness rule add "매 commit마다 changelog 갱신" --trigger every-commit
560
+ leerness rule list
561
+ # Plan (milestones)
562
+ leerness plan add "M-XXXX 계획" --next-action "다음 단계"
563
+ leerness plan list
564
+ # Lessons (영구 교훈)
565
+ leerness lesson save "교훈 본문" --tag perf
566
+ leerness lesson list --query "키워드" # 1.9.139
567
+ # DELETE → RESTORE (1.9.126~128)
568
+ leerness memory archive list . --query "키워드" # 1.9.138
569
+ leerness memory restore decision <date|title>
570
+ ```
571
+
572
+ ### MCP server (외부 AI 통합)
573
+
574
+ Leerness v1.9.144는 stdio JSON-RPC MCP server를 내장합니다 — Claude Code · Cursor · Codex CLI 등 외부 AI에 **42개 도구**를 노출:
575
+
576
+ ```jsonc
577
+ // 카테고리별
578
+ // • Core: handoff / drift_check / audit / health / verify_claim / contract_verify
579
+ // • Memory READ: task_list / decision_list / lesson_list / plan_list / rule_list / memory_status
580
+ // • Memory WRITE: task_add / decision_add / lesson_save / plan_add / rule_add
581
+ // • Memory DELETE: task_drop / decision_drop / lesson_drop / plan_remove / rule_remove
582
+ // • Skill: skill_match / skill_list / skill_search / skill_info / skill_suggest
583
+ // • Insight: lessons / lessons_auto / brainstorm / retro / benchmark / lazy_detect
584
+ // • Workflow: session_close / agents_list / task_export / env_check / usage_stats / reuse_map / whats_new
585
+
586
+ // MCP server 실행: leerness mcp serve
587
+ // tools/list 응답: 42 도구
588
+ ```
589
+
590
+ ### Autonomous mode (자율 모드)
591
+
592
+ `<<autonomous-loop-dynamic>>` 신호만 보내면 AI가:
593
+ 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) 다음 라운드 예약.
594
+
595
+ 현재 누적: **70 라운드 (1.9.40 → 1.9.144)** · 매 라운드 GitHub release/태그 생성 · _reports/는 비공개 보존.
596
+
597
+ ### 성능 가이드 (1.9.140 측정)
598
+
599
+ - `leerness handoff .` — 평균 ~1.5s (캐시 워밍업 후 ~0.6s)
600
+ - `leerness memory status --json` — 평균 ~250ms
601
+ - `leerness task list --json` — 평균 ~200ms
602
+ - `leerness drift check --json` — 평균 ~400ms
603
+ - MCP `tools/list` 응답 — 평균 ~150ms
604
+ - usage-stats / lessons / listAllSkills 모두 메모리 캐싱 (1.9.65/66)
605
+
606
+ ### 빠른 시작
607
+
608
+ ```bash
609
+ # 1. 설치 (글로벌)
610
+ npm install -g leerness
611
+
612
+ # 2. 프로젝트에 하네스 설치
613
+ cd my-project && leerness init . --yes --skills recommended
614
+
615
+ # 3. AI 세션 시작 시
616
+ leerness handoff . # 컨텍스트 자동 로드
617
+
618
+ # 4. 세션 종료 시
619
+ leerness session close . # 9 카테고리 + 룰 검증 + 다음 라운드 추천
620
+
621
+ # 5. release 자동화 (1.9.140 main 자동 push 포함)
622
+ leerness release pack --close --auto-main-push
623
+ ```
624
+
549
625
  ### Planning Files
550
626
 
551
627
  - `.harness/plan.md`: 전체 목표, milestone, 제외/드랍 범위
552
628
  - `.harness/progress-tracker.md`: 요청 단위 상태와 증거
553
629
  - `.harness/current-state.md`: 지금 이어서 할 작업
554
630
  - `.harness/session-handoff.md`: 다음 세션 인수인계 (자동 작성)
631
+ - `.harness/lessons.md` / `decisions.md` / `rules.md`: 영구 메모리 (5 surface)
555
632
 
556
- Last synced by Leerness v1.9.40: 2026-05-19
633
+ Last synced by Leerness v1.9.144: 2026-05-20
557
634
  <!-- leerness:project-readme:end -->
558
635
 
package/bin/harness.js CHANGED
@@ -6,7 +6,7 @@ const path = require('path');
6
6
  const cp = require('child_process');
7
7
  const readline = require('readline');
8
8
 
9
- const VERSION = '1.9.142';
9
+ const VERSION = '1.9.144';
10
10
  const MARK = '<!-- leerness:managed -->';
11
11
  const README_START = '<!-- leerness:project-readme:start -->';
12
12
  const README_END = '<!-- leerness:project-readme:end -->';
@@ -2748,6 +2748,22 @@ function handoff(root) {
2748
2748
  archive: archiveCountsH, // 1.9.130
2749
2749
  summary: `T${tasksInProgress}/D${decisionsCount}/R${rulesActive}/P${milestones}/L${lessonsCount}`,
2750
2750
  };
2751
+ // 1.9.143: handoff --json featureGraph 통합 (session close 1.9.142 와 동일 패턴)
2752
+ try {
2753
+ const { nodes: fNodesH } = _readFeatureGraph(root);
2754
+ const edgeCount = fNodesH.reduce((s, n) => s + (n.dependsOn?.length || 0) + (n.affects?.length || 0) + (n.coChangesWith?.length || 0), 0);
2755
+ const linkedSet = new Set();
2756
+ for (const n of fNodesH) {
2757
+ for (const x of [...(n.dependsOn||[]), ...(n.affects||[]), ...(n.coChangesWith||[])]) { linkedSet.add(n.id); linkedSet.add(x); }
2758
+ }
2759
+ const isolated = fNodesH.length ? (fNodesH.length - linkedSet.size) : 0;
2760
+ result.featureGraph = {
2761
+ total: fNodesH.length,
2762
+ edges: edgeCount,
2763
+ isolated: Math.max(0, isolated),
2764
+ summary: `F${fNodesH.length}/E${edgeCount}${isolated > 0 ? `/iso${isolated}` : ''}`
2765
+ };
2766
+ } catch {}
2751
2767
  } catch {}
2752
2768
  log(JSON.stringify(result, null, 2));
2753
2769
  return;
@@ -4485,7 +4501,7 @@ function _banner(opts = {}) {
4485
4501
  const cols = process.stdout && process.stdout.columns ? process.stdout.columns : 80;
4486
4502
  if (process.env.LEERNESS_NO_BANNER === '1') return;
4487
4503
  if (cols < 70) {
4488
- log(`Leerness ${v} — 한국어 우선 AI 개발 하네스`);
4504
+ log(`Leerness ${v} — AI 에이전트 검수·기억·드리프트 방지 하네스`);
4489
4505
  return;
4490
4506
  }
4491
4507
  const isTty = process.stdout && process.stdout.isTTY;
@@ -4524,11 +4540,11 @@ function _banner(opts = {}) {
4524
4540
  lines.push(border(' ║ ') + C.g(asciiLines[i], grad[i]) + border(' ║'));
4525
4541
  }
4526
4542
  lines.push(border(' ║ ║'));
4527
- lines.push(border(' ║ ') + C.green(`${v.padEnd(10)}`) + C.dim('Korean-first AI Development Harness') + border(' ║'));
4543
+ lines.push(border(' ║ ') + C.green(`${v.padEnd(10)}`) + C.dim('AI Agent Reliability Harness') + border(' ║'));
4528
4544
  lines.push(border(' ║ ') + C.yel('★ ') + C.dim('verify · reuse-map · handoff · agents · orchestrate') + border(' ║'));
4529
4545
  lines.push(border(' ║ ║'));
4530
4546
  lines.push(border(' ╚══════════════════════════════════════════════════════════════╝'));
4531
- lines.push(' ' + C.dim('한국어 우선 AI 개발 하네스 — ') + C.mag('verify') + C.dim(' · ') + C.mag('reuse-map') + C.dim(' · ') + C.mag('handoff') + C.dim(' · ') + C.mag('agents'));
4547
+ lines.push(' ' + C.dim('AI 에이전트 검수·기억·드리프트 방지 하네스 — ') + C.mag('verify') + C.dim(' · ') + C.mag('remember') + C.dim(' · ') + C.mag('orchestrate') + C.dim(' · ') + C.mag('audit'));
4532
4548
  lines.push('');
4533
4549
 
4534
4550
  // 1.9.141: ASCII 배너 모션 — 그라데이션이 LEERNESS 글자 위로 흐르는 wave 효과 (TTY only, opt-out via LEERNESS_NO_ANIMATE)
@@ -4578,36 +4594,70 @@ function _banner(opts = {}) {
4578
4594
  for (const ln of lines) {
4579
4595
  process.stdout.write('\x1b[2K\r' + ln + '\n');
4580
4596
  }
4597
+ // 1.9.144: 사용자 명시 요청 — 모션 완료 후 1초 홀드 (배너가 시선에 자리잡도록)
4598
+ // 환경변수 LEERNESS_BANNER_HOLD_MS 로 조정 가능 (기본 1000ms, 0 또는 음수면 스킵)
4599
+ const holdMs = (() => {
4600
+ const env = parseInt(process.env.LEERNESS_BANNER_HOLD_MS || '1000', 10);
4601
+ return Number.isFinite(env) && env > 0 ? Math.min(env, 5000) : 0;
4602
+ })();
4603
+ if (holdMs > 0) sleepSync(holdMs);
4581
4604
  } else {
4582
4605
  for (const ln of lines) log(ln);
4583
4606
  }
4584
4607
  if (opts.quickStart) {
4585
- log(C.bold(C.cyan(' ✨ 빠른 시작 (1.9.142+ Feature Graph 통합: MCP CRUD + audit 검증 + session close 72 라운드 자율 누적)')));
4586
- log(' ' + C.green('npx leerness@latest init .') + C.dim(' # 신규 프로젝트 + 외부 AI CLI 설정'));
4587
- log(' ' + C.green('npx leerness handoff .') + C.dim(' # 컨텍스트 + lessons + 매칭 skill + history hit + brainstorm hits + 헤드라인'));
4588
- log(' ' + C.green('npx leerness handoff . --quiet') + C.dim(' # 자동화/CI 모드 (1.9.99) 자동 회수 라인 비활성'));
4589
- log(' ' + C.green('npx leerness handoff . --json') + C.dim(' # 외부 AI/MCP 통합 JSON 출력 (1.9.96)'));
4590
- log(' ' + C.green('npx leerness skill match "<query>"') + C.dim(' # 매칭 skill + rolling history 자동 누적'));
4591
- log(' ' + C.green('npx leerness skill search "<capability>"') + C.dim(' # capability 부분 일치 (1.9.90)'));
4592
- log(' ' + C.green('npx leerness env sync .') + C.dim(' # .env .env.example 동기화 (1.9.71 보안)'));
4593
- log(' ' + C.green('npx leerness health . --json') + C.dim(' # 종합 헬스 체크 — drift + 보안 + skill + MCP (1.9.85)'));
4594
- log(' ' + C.green('npx leerness drift check . --auto-fix') + C.dim(' # drift + 보안 자동 회복 (1.9.82)'));
4595
- log(' ' + C.green('npx leerness lazy detect . --json') + C.dim(' # 거짓 완료/no test run 신호 JSON (1.9.101)'));
4596
- log(' ' + C.green('npx leerness audit . --json') + C.dim(' # 일관성 감사 JSON — 11 kind findings (1.9.102)'));
4597
- log(' ' + C.green('npx leerness session close . --json') + C.dim(' # 마감 통계 JSON — taskCounts + drift + rules + suggestions (1.9.103)'));
4598
- log(' ' + C.green('npx leerness session close .') + C.dim(' # 마감 + 다음 라운드 추천 (default)'));
4599
- log('');
4600
- log(C.bold(C.cyan(' 🤖 메인 에이전트 (Claude/Cursor/Copilot)용')));
4601
- log(' ' + C.green('npx leerness mcp serve') + C.dim(' # MCP 서버 — 40 도구 🎉 (memory_restore 추가, 1.9.128)'));
4602
- log(' ' + C.green('npx leerness memory archive list --json') + C.dim(' # DELETE 5종 archive 통합 조회 (1.9.127) D/L/P entries + 복원 후보'));
4603
- log(' ' + C.green('npx leerness memory restore <surface> <target>') + C.dim(' # archive active 복원 (1.9.128) — DELETE→RESTORE cycle'));
4604
- log(' ' + C.green('npx leerness lesson save "<text>" --tag "..."') + C.dim(' # lessons.md 직접 write (1.9.112 handoff 자동 회수와 통합)'));
4605
- log(' ' + C.green('npx leerness memory status . --json') + C.dim(' # Memory Surface 5종 통합 상태 JSON (1.9.114)'));
4606
- log(' ' + C.green('npx leerness decision add "<title>" --reason "..."') + C.dim(' # 설계 결정 영구화 (1.9.108)handoff lessons 자동 회수와 통합'));
4607
- log(' ' + C.green('npx leerness rule add "매 X마다 Y" --trigger every-X') + C.dim(' # 자연어 영구 룰 (1.9.8) — handoff 매 세션 자동 출력'));
4608
- log(' ' + C.green('npx leerness agents bench "<task>"') + C.dim(' # 3 CLI 동시 비교'));
4609
- log(' ' + C.green('npx leerness skill publish --bundle-only') + C.dim(' # 보안 사전 점검 통합 publish (1.9.98)'));
4610
- log('');
4608
+ // 1.9.144: quickStart UI/UX 개선 카테고리 그룹 + 부드러운 cascade 표시 + 핵심 3단계 우선
4609
+ // • TTY 모션 라인 사이 짧은 fade (8ms) 부드러운 cascade
4610
+ // • non-TTY 즉시 출력 (CI/pipe 호환)
4611
+ // • 카테고리: 신규 프로젝트 / 세션 / 메모리 5종 / MCP·외부 AI / 보안·회복
4612
+ const cascade = animate;
4613
+ const cascadeMs = parseInt(process.env.LEERNESS_CASCADE_MS || '8', 10);
4614
+ const cascadeStep = Number.isFinite(cascadeMs) && cascadeMs >= 0 ? Math.min(cascadeMs, 100) : 8;
4615
+ const cprint = (s) => { log(s); if (cascade && cascadeStep > 0) sleepSync(cascadeStep); };
4616
+ const section = (title) => { cprint(''); cprint(C.bold(C.cyan(` ${title}`))); };
4617
+ const cmd = (run, desc) => {
4618
+ // 명령 컬럼 = 42 (정렬을 위해 padEnd) 가독성 향상
4619
+ const padded = run.length >= 42 ? run + ' ' : run.padEnd(42);
4620
+ cprint(' ' + C.green(padded) + C.dim('# ' + desc));
4621
+ };
4622
+
4623
+ cprint('');
4624
+ cprint(C.bold(C.cyan(' 시작하기 (3단계면 )')));
4625
+ cmd('npx leerness init .', '1️⃣ 하네스 설치 + AI 도구 자동 연결');
4626
+ cmd('npx leerness handoff .', '2️⃣ 세션 시작 컨텍스트·기억·feature impact 자동 회수');
4627
+ cmd('npx leerness session close .', '3️⃣ 세션 종료 마감 통계 + 다음 라운드 추천');
4628
+
4629
+ section('🧠 메모리 5종 CRUD (1.9.142cascade 방지)');
4630
+ cmd('leerness task add "<제목>"', 'progress-tracker 등록');
4631
+ cmd('leerness decision add "<제목>" --reason "..."', '되돌리기 어려운 결정 영구화');
4632
+ cmd('leerness lesson save "<교훈>" --tag "..."', '재발견 가능한 통찰 저장');
4633
+ cmd('leerness plan add "<milestone>"', '계획 단계 등록');
4634
+ cmd('leerness rule add "<룰>" --trigger every-X', '자연어 영구 룰');
4635
+ cmd('leerness feature add "<기능>" --files "..."', 'Feature Graph 노드 (1.9.141)');
4636
+
4637
+ section('🔗 인과관계 + 영향 추적 (1.9.141~143)');
4638
+ cmd('leerness feature impact <F-XXXX>', '코드 변경 전 영향받는 feature 자동 회수');
4639
+ cmd('leerness feature list --json', '전체 그래프 + 엣지');
4640
+ cmd('leerness audit . --json', 'orphan/cycle 무결성 검증');
4641
+
4642
+ section('🛡 보안·드리프트·게으름 가드');
4643
+ cmd('leerness drift check . --auto-fix', 'drift + 보안 자동 회복');
4644
+ cmd('leerness lazy detect . --json', '거짓 완료/no test run 감지');
4645
+ cmd('leerness env sync .', '.env ↔ .env.example 동기화');
4646
+ cmd('leerness health . --json', '종합 헬스 (drift+보안+skill+feature)');
4647
+
4648
+ section('🤖 외부 AI 통합 (MCP 46 도구)');
4649
+ cmd('npx leerness mcp serve', 'stdio JSON-RPC server');
4650
+ cmd('leerness memory status . --json', '5 surface + featureGraph 한 호출');
4651
+ cmd('leerness memory archive list --query "kw"', 'DELETE 5종 archive 검색');
4652
+ cmd('leerness memory restore <surface> <target>', 'archive → active 복원');
4653
+
4654
+ section('🚀 Release 자동화');
4655
+ cmd('leerness release pack --close --auto-main-push', '한 줄 release (1.9.140 main push 통합)');
4656
+ cmd('leerness release sync-main .', 'release branch → main 자동 fast-forward');
4657
+
4658
+ cprint('');
4659
+ cprint(C.dim(' 📚 자세히: `leerness --help` · 자율 모드: `<<autonomous-loop-dynamic>>` 신호로 진행'));
4660
+ cprint('');
4611
4661
  }
4612
4662
  }
4613
4663
 
@@ -8200,6 +8250,24 @@ function driftCheckCmd(root, opts = {}) {
8200
8250
  }
8201
8251
  }
8202
8252
  } catch {}
8253
+ // 1.9.143: Feature Graph 미사용 신호 — 노드는 있는데 edges 비율 낮으면 인과관계 정리 미진
8254
+ try {
8255
+ const { nodes: fGraphNodes } = _readFeatureGraph(root);
8256
+ if (fGraphNodes.length >= 3) {
8257
+ const edgeCount = fGraphNodes.reduce((s, n) => s + (n.dependsOn?.length || 0) + (n.affects?.length || 0) + (n.coChangesWith?.length || 0), 0);
8258
+ const linkedSet = new Set();
8259
+ for (const n of fGraphNodes) {
8260
+ for (const x of [...(n.dependsOn||[]), ...(n.affects||[]), ...(n.coChangesWith||[])]) { linkedSet.add(n.id); linkedSet.add(x); }
8261
+ }
8262
+ const isolatedCount = Math.max(0, fGraphNodes.length - linkedSet.size);
8263
+ const isolatedRatio = isolatedCount / fGraphNodes.length;
8264
+ if (edgeCount === 0 || isolatedRatio >= 0.5) {
8265
+ const fgScore = edgeCount === 0 ? 25 : 15;
8266
+ totalScore += fgScore;
8267
+ fired.push({ file: '.harness/feature-graph.md', ageDays: null, threshold: 0, weight: fgScore, label: `Feature Graph 미정리 (1.9.143): ${fGraphNodes.length} 노드, edges=${edgeCount}, isolated=${isolatedCount}` });
8268
+ }
8269
+ }
8270
+ } catch {}
8203
8271
  // 신규 _apps/* 에서 task 0건도 신호로
8204
8272
  const appsDir = path.join(root, '_apps');
8205
8273
  let appsZeroTask = [];
@@ -9600,6 +9668,22 @@ function healthCmd(root) {
9600
9668
  summary: `T${tasksInProgress}/D${decisionsCount}/R${rulesActive}/P${milestones}/L${lessonsCount}`,
9601
9669
  };
9602
9670
  } catch { out.memorySurface = { error: 'memorySurface 점검 실패' }; }
9671
+ // 1.9.143: health --json featureGraph 통합 (handoff/session close 와 동일 패턴 — JSON 4종 완성)
9672
+ try {
9673
+ const { nodes: fNodesHe } = _readFeatureGraph(root);
9674
+ const edgeCount = fNodesHe.reduce((s, n) => s + (n.dependsOn?.length || 0) + (n.affects?.length || 0) + (n.coChangesWith?.length || 0), 0);
9675
+ const linkedSet = new Set();
9676
+ for (const n of fNodesHe) {
9677
+ for (const x of [...(n.dependsOn||[]), ...(n.affects||[]), ...(n.coChangesWith||[])]) { linkedSet.add(n.id); linkedSet.add(x); }
9678
+ }
9679
+ const isolated = fNodesHe.length ? (fNodesHe.length - linkedSet.size) : 0;
9680
+ out.featureGraph = {
9681
+ total: fNodesHe.length,
9682
+ edges: edgeCount,
9683
+ isolated: Math.max(0, isolated),
9684
+ summary: `F${fNodesHe.length}/E${edgeCount}${isolated > 0 ? `/iso${isolated}` : ''}`
9685
+ };
9686
+ } catch { out.featureGraph = { error: 'featureGraph 점검 실패' }; }
9603
9687
  // 6) issues 요약 (사용자 글로벌 룰 가시화)
9604
9688
  const issues = [];
9605
9689
  if (out.checks.drift?.level && !/healthy/.test(out.checks.drift.level)) issues.push(`drift ${out.checks.drift.level}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leerness",
3
- "version": "1.9.142",
3
+ "version": "1.9.144",
4
4
  "description": "Leerness: 비파괴 마이그레이션, 자동 버전 감지·업데이트, 계획/진행/핸드오프 자동화, 게으름·시크릿·인코딩 자동 가드, Claude Code 슬래시 통합을 갖춘 한국어 우선 AI 개발 하네스.",
5
5
  "keywords": [
6
6
  "leerness",
package/scripts/e2e.js CHANGED
@@ -870,13 +870,12 @@ total++;
870
870
  // 1.9.32 회귀: 배너 + setup-agents (비대화형 모드 안전)
871
871
  total++;
872
872
  {
873
- // --version --banner: LEERNESS ASCII + 한국어 표시
873
+ // --version --banner: LEERNESS ASCII + 신규 슬로건 (1.9.144+ "AI 에이전트 검수·기억·드리프트 방지 하네스")
874
874
  const r = cp.spawnSync(process.execPath, [CLI, '--version', '--banner'], { encoding: 'utf8', timeout: 10000, env: { ...process.env, TERM: 'dumb' } });
875
- // ASCII art 일부 문자 + 한국어 우선 + 1.9.32 + 박스
876
875
  const ok = r.status === 0
877
876
  && /╔═+╗/.test(r.stdout)
878
877
  && /███████╗/.test(r.stdout)
879
- && /한국어 우선/.test(r.stdout)
878
+ && /AI 에이전트 검수.기억.드리프트 방지 하네스/.test(r.stdout)
880
879
  && new RegExp(`v${require('../package.json').version}`).test(r.stdout);
881
880
  console.log(ok ? '✓ B(1.9.32) --version --banner: LEERNESS ASCII 배너' : `✗ 배너 실패`);
882
881
  if (!ok) { failed++; console.log(r.stdout.slice(0, 800)); }
@@ -1891,13 +1890,12 @@ total++;
1891
1890
 
1892
1891
  total++;
1893
1892
  {
1894
- // 1.9.34 배너 256색 그라데이션 — TTY 강제 + --banner
1895
- // 비-TTY일 때는 ANSI 코드 없이 순수 텍스트만 출력
1893
+ // 1.9.34 배너 256색 그라데이션 — TTY 강제 + --banner (1.9.144+ 신규 슬로건)
1896
1894
  const r = cp.spawnSync(process.execPath, [CLI, '--version', '--banner'], { encoding: 'utf8', timeout: 10000 });
1897
1895
  const ok = r.status === 0
1898
1896
  && /███████╗/.test(r.stdout)
1899
- && /verify · reuse-map/.test(r.stdout)
1900
- && /한국어 우선 AI 개발 하네스/.test(r.stdout)
1897
+ && /verify · remember/.test(r.stdout)
1898
+ && /AI 에이전트 검수.기억.드리프트 방지 하네스/.test(r.stdout)
1901
1899
  && /v1\.9\.\d+/.test(r.stdout);
1902
1900
  console.log(ok ? '✓ B(1.9.34) 배너 색상 + ASCII + 한국어' : `✗ 배너 색상 실패`);
1903
1901
  if (!ok) { failed++; console.log(r.stdout.slice(0, 500)); }