leerness 1.24.0 → 1.26.0
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 +96 -0
- package/README.md +4 -4
- package/bin/leerness.js +100 -23
- package/lib/health.js +355 -354
- package/lib/session-close.js +2 -2
- package/package.json +1 -1
- package/scripts/e2e.js +35 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,101 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.26.0 — 2026-06-15 — 🛡️ [안정화/Stable] i18n 행위가드 + health 진단 영어화 안정 minor
|
|
4
|
+
|
|
5
|
+
**🛡️ 안정화(Stable) minor — i18n 레이어 견고성 검증·가드 + health 진단 영어화를 npm 공개.** 직전 minor(1.25.0) 이후 누적된 패치 2건(1.25.1 + 1.25.2)을 검증·통합해 배포. R-0011 정책의 17번째 stable minor. 한국어 우선 기본은 그대로.
|
|
6
|
+
|
|
7
|
+
### 이번 minor 통합 (1.25.1~1.25.2)
|
|
8
|
+
- **🔬 22번째 버그헌트(i18n 레이어) + 행위 e2e 회귀가드 (1.25.1)**: 8 phase 영어화 레이어를 통째 적대 검증 → 런타임 버그 0(맹신 X 양방향). uiLang 크래시안전·`--language` positional 무누출·`--language=en` 문법·`--json` 유효·flag>manifest 우선순위 확인. 소스가드만 있던 공백(1.23.0 과장 통과 원인)을 **행위 e2e 가드**로 보강.
|
|
9
|
+
- **🌐 health 진단 완전 영어화 (1.25.2)**: 고빈도 진단 `health` 를 렌더 라벨 + 능력 매트릭스 evidence(16종) + issues + summary 까지 완전 영어화(반쪽 번역 회피). `--language en` opt-in.
|
|
10
|
+
- **한국어 우선 기본 보존**: 영어는 명시 opt-in. 한국어 출력/매트릭스는 한 글자도 안 바뀜(e2e 무회귀).
|
|
11
|
+
|
|
12
|
+
### 잔여 (UR-0010 Phase 10+, 백로그)
|
|
13
|
+
- capabilities/commands/drift check/install-safety/constraints/doctor + 메모리 CRUD 빈상태 + handoff 본문 — en-leak 우선순위순.
|
|
14
|
+
|
|
15
|
+
### 검증 (회귀 0)
|
|
16
|
+
- **selftest 245/245** · **E2E 366/366** (i18n 행위가드: lens/health en 한글 0 + ko 기본 보존 + positional 무누출 포함).
|
|
17
|
+
- minor(1.26.0) — npm 배포(R-0011 stable) + annotated tag(Stable) + GitHub release(latest) + 게시본 클린룸 재실증.
|
|
18
|
+
|
|
19
|
+
## 1.25.2 — 2026-06-15 — CLI 영어화 Phase 9: health 진단 완전 영어화 (UR-0010)
|
|
20
|
+
|
|
21
|
+
**🌐 health 진단을 통째로 영어로.** en-leak 스캔 우선순위에서 고빈도 진단인 `health` 를 — 렌더 라벨만이 아니라 **능력 매트릭스 evidence·issues·요약까지 완전히** — 영어화. 반쪽 번역(1.23.0 과장)의 재발을 피하려 한 모듈(lib/health.js)의 모든 한국어를 한 번에 처리.
|
|
22
|
+
|
|
23
|
+
### 변경 (UR-0010 Phase 9)
|
|
24
|
+
- **health 완전 영어화 (lib/health.js, DI uiLang 주입)**: 렌더 라벨(`## 보안`→`## Security`, skills/usage/tasks, `6능력 매트릭스`→`6-capability matrix`, `자동 회복`→`auto-recover`) + **능력 매트릭스 evidence 16종**(웹/PC/멀티/REPL/MCP/LSP 각 점수대) + summary + 보안 issues 4종 + path-not-found 에러. `t(ko,en)` 분기, ko 인자 verbatim.
|
|
25
|
+
- **bin DI**: `healthCmd` 호출에 `uiLang: _uiLang(root)` 주입(session-close/health 동일 패턴).
|
|
26
|
+
- **한국어 기본 유지**: 영어는 명시 opt-in. `health`/`health --json` 기본은 한국어 그대로(e2e 무회귀). `--json` 값도 언어 따름(en 시 영어 evidence).
|
|
27
|
+
|
|
28
|
+
### 잔여 (UR-0010 Phase 10+, 백로그)
|
|
29
|
+
- capabilities(24)/commands(90)/drift check/install-safety/constraints/doctor + 메모리 CRUD 빈상태 메시지 + handoff 본문 — en-leak 우선순위순.
|
|
30
|
+
|
|
31
|
+
### 검증 (회귀 0)
|
|
32
|
+
- **selftest 244→245** (health 영어/한국어 보존 + uiLang 주입 소스가드) · 행위(health `--language en` 한글 0 / ko 16줄 보존 / en --json 유효) · **E2E 366/366** (i18n 행위가드에 health en/ko 추가).
|
|
33
|
+
- patch(1.25.2) — npm 미배포(R-0011, GitHub/CHANGELOG 누적).
|
|
34
|
+
|
|
35
|
+
## 1.25.1 — 2026-06-15 — 22번째 버그헌트(i18n 레이어 검증) + i18n 행위 e2e 회귀가드 (UR-0010)
|
|
36
|
+
|
|
37
|
+
**🔬 8 phase 영어화 레이어를 통째로 적대 검증.** uiLang/`_tx`/`_t` 머신을 cross-cutting 레이어로 한 번에 점검 — **런타임 버그 0** (맹신 X 양방향 확인). 다만 그동안 i18n 은 *소스가드(문자열 존재)* 만 있고 *행위 e2e* 가 없었는데, 이 공백이 1.23.0 "완전 영어" 과장(런타임 누출)을 통과시킨 근본 원인이었음 → 행위 회귀가드로 보강.
|
|
38
|
+
|
|
39
|
+
### 검증 결과 (런타임 버그 없음, 레이어 견고 확인)
|
|
40
|
+
- `_uiLang` 크래시 안전(try/catch + String 가드, malformed manifest/빈 값 → ko 폴백).
|
|
41
|
+
- arg 상호작용: `--language en` 값이 positional 로 누출 안 됨(앞/뒤 위치 모두), `task add "텍스트" --language en` 보존.
|
|
42
|
+
- `--language=en` equals 문법 동작, `--json` 출력 en 에서도 유효 JSON, flag > env > manifest 우선순위(en 프로젝트에서 `--language ko` 가 ko 강제).
|
|
43
|
+
|
|
44
|
+
### 변경 (defense-in-depth)
|
|
45
|
+
- **i18n 행위 e2e 회귀가드 1건 추가** (e2e 365→366): ① ko 프로젝트 기본 lens 한글 보존 ② `--language en` 런타임 영어 렌더 + 한글 0 ③ `--language` positional 무누출 ④ status path-not-found 에러 en/ko 분기. 소스가드가 못 잡는 *런타임 누출* 을 행위로 차단.
|
|
46
|
+
|
|
47
|
+
### 검증 (회귀 0)
|
|
48
|
+
- **selftest 244/244** · **E2E 366/366** (신규 i18n 행위가드 포함, ko 기본 무회귀).
|
|
49
|
+
- patch(1.25.1) — npm 미배포(R-0011, GitHub/CHANGELOG 누적).
|
|
50
|
+
|
|
51
|
+
## 1.25.0 — 2026-06-15 — 🛡️ [안정화/Stable] 마감 본문 정직성 + lens 플래그십 영어화 안정 minor
|
|
52
|
+
|
|
53
|
+
**🛡️ 안정화(Stable) minor — 자가 검증으로 잡은 정직성 수정 + 품질 렌즈 영어화를 npm 공개.** 직전 minor(1.24.0) 이후 누적된 패치 2건(1.24.1 + 1.24.2)을 검증·통합해 배포. R-0011 정책의 16번째 stable minor. 한국어 우선 기본은 그대로.
|
|
54
|
+
|
|
55
|
+
### 이번 minor 통합 (1.24.1~1.24.2)
|
|
56
|
+
- **🔎 session close 보고 본문 정직성 수정 (1.24.1)**: 게시된 모든 영어 표면을 `--language en` 으로 실행해 한글 누출을 자동 스캔(맹신 X — 내 'CLOSED' 주장 재실증)한 결과, 1.23.0 "session close 완전 영어" 가 과장임을 발견 → 보고 본문 잔여 영어화(`- 없음`→`- none`, `완료/결정 누적`→`done/decisions accumulated`, roadmap `자동 갱신`→`auto-updated`).
|
|
57
|
+
- **🌐 lens 품질 렌즈 플래그십 영어화 (1.24.2)**: 분야별 자기질문 품질 렌즈 5 도메인(code/design/docs/test/security)에 영어 병렬 카탈로그 추가 + `--language en` 렌더. 영어권 사용자도 완료 선언 전 자가 점검 + 분야간 인과를 활용.
|
|
58
|
+
- **한국어 우선 기본 보존**: 영어는 명시 opt-in. 한국어 원문/렌즈 질문은 한 글자도 안 바뀜(e2e 무회귀).
|
|
59
|
+
|
|
60
|
+
### 잔여 (UR-0010 Phase 9+, 백로그)
|
|
61
|
+
- 진단/정보 명령 본문(health/capabilities/commands/doctor/drift/install-safety/constraints) + 메모리 CRUD 빈상태 메시지 + handoff 본문 + init starter 데이터 — en-leak 스캔 우선순위순 단계적 확대.
|
|
62
|
+
|
|
63
|
+
### 검증 (회귀 0)
|
|
64
|
+
- **selftest 244/244** (정직성 + lens 영어/한국어 보존 소스가드 포함) · 행위(en-leak 스캔: session close 본문·lens en 한글 0 / ko 보존) · **E2E 365/365**.
|
|
65
|
+
- minor(1.25.0) — npm 배포(R-0011 stable) + annotated tag(Stable) + GitHub release(latest) + 게시본 클린룸 재실증.
|
|
66
|
+
|
|
67
|
+
## 1.24.2 — 2026-06-15 — CLI 영어화 Phase 8: lens(품질 렌즈 플래그십) 영어화 (UR-0010)
|
|
68
|
+
|
|
69
|
+
**🌐 품질 렌즈를 영어로.** 남은 한국어 표면을 en-leak 스캔으로 매핑한 뒤, 가장 가치 높은 플래그십 — **분야별 자기질문 품질 렌즈(`leerness lens`)** — 를 영어화. 영어권 사용자도 "완료 선언 전 자가 점검" + 분야간 인과관계를 그대로 활용할 수 있습니다.
|
|
70
|
+
|
|
71
|
+
### 변경 (UR-0010 Phase 8)
|
|
72
|
+
- **lens 영어 병렬 카탈로그**: `LENS_CATALOG` 5 도메인(code/design/docs/test/security)에 `titleEn`/`personaEn`/`questionsEn`/`affectsNoteEn` 추가. 한국어 원문(`questions`/`persona` 등)은 **한 글자도 안 바꿈** — selftest `userVerbatim` 가드 + e2e(ko) 안전.
|
|
73
|
+
- **lensCmd 영어 렌더**: `--language en` 시 헤더/부제/페르소나/질문/↔인과/푸터/unknown 에러 모두 영어. 프로젝트 커스텀 렌즈(.harness/quality-lenses.json)는 영어 필드가 없으면 작성된 텍스트로 graceful fallback, builtin 도메인에 추가한 커스텀 질문은 `questionsEn` 개수 기준으로 분리해 영어 렌더에 append.
|
|
74
|
+
- **한국어 기본 유지**: 영어는 명시 opt-in. 플래그 없으면 한국어 그대로(e2e 무회귀).
|
|
75
|
+
|
|
76
|
+
### 잔여 (UR-0010 Phase 9+, 백로그)
|
|
77
|
+
- 진단/정보 명령 본문(health/capabilities/commands/doctor/drift check/install-safety/constraints) + 메모리 CRUD 빈상태 메시지 + handoff 본문 + init starter 데이터 — en-leak 스캔 우선순위순 단계적 확대.
|
|
78
|
+
|
|
79
|
+
### 검증 (회귀 0)
|
|
80
|
+
- **selftest 243→244** (lens 영어 병렬필드 + ko verbatim + 렌더 영어 분기 소스가드) · 행위(en-leak 재스캔: `lens --language en` 한글 0 / ko 29줄 보존 / unknown 에러 en·ko) · **E2E 365/365**.
|
|
81
|
+
- patch(1.24.2) — npm 미배포(R-0011, GitHub/CHANGELOG 누적).
|
|
82
|
+
|
|
83
|
+
## 1.24.1 — 2026-06-15 — 정직성 수정: session close 보고 본문 잔여 한국어 영어화 (UR-0010)
|
|
84
|
+
|
|
85
|
+
**🔎 자가 검증으로 발견한 과장 주장 교정.** 게시된 모든 영어 표면을 `--language en` 으로 직접 실행해 한국어(가-힣) 누출을 스캔(맹신 X — 내 'CLOSED' 주장 재실증)한 결과: help/status/group help 는 깨끗했지만, **session close 보고 본문**은 1.23.0 에서 "완전 영어"로 배포했음에도 일부 한국어가 남아 있었습니다. 그 잔여를 영어화.
|
|
86
|
+
|
|
87
|
+
### 변경 (UR-0010)
|
|
88
|
+
- **session close 보고 본문 영어화**: 빈 섹션 표시 `- 없음`→`- none`(rowsToList, 헤더는 이미 영어라 정합↑), 진행 요약 한 줄 `완료 N/M · 결정 N건 누적`→`done N/M · decisions N accumulated`(`_retroOneLine` 에 선택적 lang), roadmap 자동 갱신 로그 `자동 갱신`→`auto-updated`(`_autoRoadmap`, `_uiLang(root)` 기반).
|
|
89
|
+
- **한국어 기본 불변**: 모든 변경은 en 분기에만. ko 는 한 글자도 안 바뀜(e2e 무회귀). `_retroOneLine` 의 기존 호출부(retro 명령)는 lang 미전달 → ko 유지.
|
|
90
|
+
- **검증 방법 공개**: en-leak 스캔(Node `[가-힣]` 탐지 — grep `\x{}` 빌드 한계 우회)으로 재실증. 남은 2줄은 seeded T-0001 + task-add 기본 next-action 값(데이터, Phase 8).
|
|
91
|
+
|
|
92
|
+
### 잔여 (UR-0010 Phase 8+, 백로그)
|
|
93
|
+
- handoff 본문 + 메모리 CRUD/진단 명령 본문(task/decision/audit/gate/scan 사람용 메시지) + init starter 콘텐츠/task-add 기본값 — 단계적 확대.
|
|
94
|
+
|
|
95
|
+
### 검증 (회귀 0)
|
|
96
|
+
- **selftest 242→243** (보고 본문 영어/한국어 보존 소스가드) · 행위(en fresh: `- none / done 0/1 · decisions 0 accumulated / roadmap.html auto-updated`, ko fresh: `- 없음 / 완료 0/1 · 결정 0건 누적 / 자동 갱신`) · **E2E 365/365**.
|
|
97
|
+
- patch(1.24.1) — npm 미배포(R-0011, GitHub/CHANGELOG 누적).
|
|
98
|
+
|
|
3
99
|
## 1.24.0 — 2026-06-15 — 🛡️ [안정화/Stable] help 표면 전체 영어화 안정 minor
|
|
4
100
|
|
|
5
101
|
**🛡️ 안정화(Stable) minor — `leerness --help` 와 모든 도움말 표면을 영어로 공개.** 직전 minor(1.23.0) 이후 누적된 패치 2건(1.23.1 Phase 6 + 1.23.2 Phase 7)을 검증·통합해 npm 배포. R-0011 정책의 15번째 stable minor. 이제 영어 사용자가 **메인 도움말 → 명령군 그룹 도움말 → 사용법 힌트**까지 막힘없이 읽을 수 있습니다. 한국어 우선 기본은 그대로.
|
package/README.md
CHANGED
|
@@ -104,7 +104,7 @@ MIT
|
|
|
104
104
|
<!-- leerness:project-readme:start -->
|
|
105
105
|
## Leerness Project Harness
|
|
106
106
|
|
|
107
|
-
이 프로젝트는 Leerness v1.
|
|
107
|
+
이 프로젝트는 Leerness v1.26.0 하네스를 사용합니다. AI 에이전트는 작업 전 `leerness handoff`로 컨텍스트를 적재하고, 작업 후 `leerness check`/`leerness audit`/`leerness session close`를 수행해야 합니다.
|
|
108
108
|
|
|
109
109
|
### 정체성 — AI 에이전트 운영 레이어 (UR-0030)
|
|
110
110
|
|
|
@@ -158,7 +158,7 @@ leerness memory restore decision <date|title>
|
|
|
158
158
|
|
|
159
159
|
### MCP server (외부 AI 통합)
|
|
160
160
|
|
|
161
|
-
Leerness v1.
|
|
161
|
+
Leerness v1.26.0는 stdio JSON-RPC MCP server를 내장합니다 — Claude Code · Cursor · Codex CLI 등 외부 AI에 **85개 도구**를 노출:
|
|
162
162
|
|
|
163
163
|
```jsonc
|
|
164
164
|
// 카테고리별
|
|
@@ -179,7 +179,7 @@ Leerness v1.24.0는 stdio JSON-RPC MCP server를 내장합니다 — Claude Code
|
|
|
179
179
|
`<<autonomous-loop-dynamic>>` 신호만 보내면 AI가:
|
|
180
180
|
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) 다음 라운드 예약.
|
|
181
181
|
|
|
182
|
-
현재 누적: **70 라운드 (1.9.40 → 1.
|
|
182
|
+
현재 누적: **70 라운드 (1.9.40 → 1.26.0)** · 매 라운드 GitHub release/태그 생성 · _reports/는 비공개 보존.
|
|
183
183
|
|
|
184
184
|
### 성능 가이드 (1.9.140 측정)
|
|
185
185
|
|
|
@@ -217,6 +217,6 @@ leerness release pack --close --auto-main-push
|
|
|
217
217
|
- `.harness/session-handoff.md`: 다음 세션 인수인계 (자동 작성)
|
|
218
218
|
- `.harness/lessons.md` / `decisions.md` / `rules.md`: 영구 메모리 (5 surface)
|
|
219
219
|
|
|
220
|
-
Last synced by Leerness v1.
|
|
220
|
+
Last synced by Leerness v1.26.0: 2026-06-15
|
|
221
221
|
<!-- leerness:project-readme:end -->
|
|
222
222
|
|
package/bin/leerness.js
CHANGED
|
@@ -32,7 +32,7 @@ const { _evidenceQuality, _parseEvidenceStats, _shellGuardAnalyze, _claimFileInG
|
|
|
32
32
|
// 1.9.295 (UR-0025 4단계): 정적 데이터 카탈로그 모듈 분리 (비파괴, require-based).
|
|
33
33
|
const { CAPABILITY_SURFACE, POWERFUL_COMMANDS, ADAPTERS, REUSE_CATEGORIES, REUSE_CHECKLIST, _DEFAULT_PLATFORM_CONSTRAINTS, _DEFAULT_DOMAIN_CATALOG, _TOOL_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 분리 · 1.11.4 (UR-0007): _TOOL_CATALOG
|
|
34
34
|
|
|
35
|
-
const VERSION = '1.
|
|
35
|
+
const VERSION = '1.26.0';
|
|
36
36
|
|
|
37
37
|
// 1.9.290 (UR-0037, Codex gpt-5.5 #4 수렴): CLI 전용 부작용은 require 시 실행하지 않는다.
|
|
38
38
|
// 이전: warning listener 제거 / NODE_OPTIONS 변경 / chcp IIFE 가 top-level 즉시 실행 → require('harness') 시 호스트 프로세스 오염.
|
|
@@ -3782,6 +3782,34 @@ function _selfTestCases() {
|
|
|
3782
3782
|
const koUsagePreserved = bin.includes('rule add "<텍스트>" --trigger <트리거>'); // ko 맵 불변
|
|
3783
3783
|
return en && koPreserved && groupUsageEn && koUsagePreserved;
|
|
3784
3784
|
} },
|
|
3785
|
+
{ name: '영어화 정직성 (1.24.1, UR-0010): session close 보고 본문(없음/retro/roadmap) 영어/한국어 보존 (소스 가드)', run: () => {
|
|
3786
|
+
const bin = read(__filename);
|
|
3787
|
+
const sc = read(path.join(path.dirname(__filename), '..', 'lib', 'session-close.js'));
|
|
3788
|
+
const rowsEn = sc.includes("t('- 없음', '- none')") && sc.includes('_retroOneLine(agg, uiLang)');
|
|
3789
|
+
const retroEn = bin.includes('function _retroOneLine(agg, lang)') && bin.includes('`done ${done}/${total}') && bin.includes('decisions ${agg.decisionBlocks} accumulated');
|
|
3790
|
+
const roadmapEn = bin.includes('roadmap.html auto-updated (${trigger})');
|
|
3791
|
+
const koPreserved = bin.includes('완료 ${done}/${total}') && bin.includes('roadmap.html 자동 갱신 (${trigger})') && sc.includes("t('- 없음', '- none')"); // ko 인자 보존
|
|
3792
|
+
return rowsEn && retroEn && roadmapEn && koPreserved;
|
|
3793
|
+
} },
|
|
3794
|
+
{ name: 'CLI 영어화 Phase 9 (1.25.2, UR-0010): health 진단 영어/한국어 보존 + uiLang 주입 (소스 가드)', run: () => {
|
|
3795
|
+
const bin = read(__filename);
|
|
3796
|
+
const h = read(path.join(path.dirname(__filename), '..', 'lib', 'health.js'));
|
|
3797
|
+
const injected = bin.includes('uiLang: _uiLang(root), harnessPath: __filename, listAllSkills');
|
|
3798
|
+
const en = h.includes('## Security') && h.includes('command calls:') && h.includes('6-capability matrix') && h.includes('web bridge present, playwright not installed');
|
|
3799
|
+
const koPreserved = h.includes('## 보안') && h.includes('명령 호출:') && h.includes('6능력 매트릭스') && h.includes('playwright 미설치'); // ko 인자 보존(e2e ko)
|
|
3800
|
+
return injected && en && koPreserved;
|
|
3801
|
+
} },
|
|
3802
|
+
{ name: 'CLI 영어화 Phase 8 (1.24.2, UR-0010): lens 영어 병렬필드 + 렌더 영어/한국어 보존 (소스 가드)', run: () => {
|
|
3803
|
+
// 카탈로그 영어 병렬필드 + ko verbatim 동시 보존 + 렌더 영어 분기
|
|
3804
|
+
const enFields = LENS_CATALOG.code.questionsEn && LENS_CATALOG.code.questionsEn.length === LENS_CATALOG.code.questions.length
|
|
3805
|
+
&& LENS_CATALOG.code.questionsEn.some(q => q.includes('senior engineer') && q.includes('complex'))
|
|
3806
|
+
&& LENS_CATALOG.design.personaEn.includes('senior designer') && LENS_CATALOG.docs.questionsEn.some(q => q.includes('30 seconds'));
|
|
3807
|
+
const koVerbatim = LENS_CATALOG.code.questions.some(q => q.includes('선임 개발자') && q.includes('복잡'))
|
|
3808
|
+
&& LENS_CATALOG.docs.questions.some(q => q.includes('30초')); // ko 원문 유지(e2e/userVerbatim)
|
|
3809
|
+
const bin = read(__filename);
|
|
3810
|
+
const renderEn = bin.includes('quality self-question lenses (v${VERSION})') && bin.includes("t('페르소나', 'persona')");
|
|
3811
|
+
return enFields && koVerbatim && renderEn;
|
|
3812
|
+
} },
|
|
3785
3813
|
{ name: 'VERSION 형식 (x.y.z)', run: () => /^\d+\.\d+\.\d+$/.test(VERSION) }
|
|
3786
3814
|
];
|
|
3787
3815
|
}
|
|
@@ -4313,51 +4341,89 @@ function pulseCmd(root) {
|
|
|
4313
4341
|
// 1.18.3 (UR-0003 사용자 명시): 분야별 자기질문 품질 렌즈 — AI 가 완료 선언 전 스스로 답해보는 질문 + 분야간 인과관계.
|
|
4314
4342
|
// "선임 개발자가 내 코드를 보고 복잡하다고 느끼지 않을까?" / "선임 디자이너와 일반 사용자가 봤을 때 이쁘고 직관적인가?" (사용자 원문).
|
|
4315
4343
|
// 질문에 "그렇다(통과)"라고 답할 수 없으면 아직 완료가 아님. affects = 이 분야를 바꿨을 때 다시 물어야 할 분야(인과관계).
|
|
4344
|
+
// 1.24.2 (UR-0010 Phase 8): 각 도메인에 영어 병렬 필드(titleEn/personaEn/questionsEn/affectsNoteEn) 추가.
|
|
4345
|
+
// 한국어(title/persona/questions/affectsNote)는 verbatim 유지 — selftest userVerbatim 가드 + e2e(ko) 무회귀.
|
|
4346
|
+
// questionsEn 개수는 builtin questions 와 동일하게 유지(렌더가 커스텀 추가질문을 length 기준으로 분리·append).
|
|
4316
4347
|
const LENS_CATALOG = {
|
|
4317
4348
|
code: {
|
|
4318
4349
|
title: '코드', persona: '선임 개발자',
|
|
4350
|
+
titleEn: 'code quality', personaEn: 'senior engineer',
|
|
4319
4351
|
questions: [
|
|
4320
4352
|
'선임 개발자가 이 코드를 보고 "복잡하다"고 느끼지 않을까? — 가볍고 단순해야 함',
|
|
4321
4353
|
'더 단순한 방법이 있는데 추상화/패턴/옵션을 추가하고 있지 않은가?',
|
|
4322
4354
|
'처음 보는 사람이 5분 안에 이 변경을 이해할 수 있는가?'
|
|
4323
4355
|
],
|
|
4324
|
-
|
|
4356
|
+
questionsEn: [
|
|
4357
|
+
'Would a senior engineer call this code "complex"? — it should be light and simple',
|
|
4358
|
+
'Are you adding abstraction/patterns/options when a simpler way exists?',
|
|
4359
|
+
'Can a newcomer understand this change in 5 minutes?'
|
|
4360
|
+
],
|
|
4361
|
+
affects: ['test', 'docs', 'design'], affectsNote: 'UI 를 만지는 코드 변경이면 design 질문 재확인 필수',
|
|
4362
|
+
affectsNoteEn: 'if the change touches UI, re-check the design questions'
|
|
4325
4363
|
},
|
|
4326
4364
|
design: {
|
|
4327
4365
|
title: '디자인/UX', persona: '선임 디자이너 + 일반 사용자',
|
|
4366
|
+
titleEn: 'design / UX', personaEn: 'senior designer + everyday user',
|
|
4328
4367
|
questions: [
|
|
4329
4368
|
'선임 디자이너가 봤을 때 이쁘고 일관적인가?',
|
|
4330
4369
|
'일반 사용자가 처음 봤을 때 편하고 직관적이며 헷갈리지 않는가?',
|
|
4331
4370
|
'꾸미기 위해 복잡해지고 있지 않은가? — 단순함이 곧 직관'
|
|
4332
4371
|
],
|
|
4333
|
-
|
|
4372
|
+
questionsEn: [
|
|
4373
|
+
'Would a senior designer find it pretty and consistent?',
|
|
4374
|
+
'Is it comfortable, intuitive, and unconfusing for a first-time everyday user?',
|
|
4375
|
+
'Are you adding complexity just to decorate? — simplicity is intuition'
|
|
4376
|
+
],
|
|
4377
|
+
affects: ['code', 'docs'], affectsNote: '디자인 단순화는 보통 코드도 단순하게 만든다 (역도 성립)',
|
|
4378
|
+
affectsNoteEn: 'simplifying design usually simplifies the code too (and vice versa)'
|
|
4334
4379
|
},
|
|
4335
4380
|
docs: {
|
|
4336
4381
|
title: '문서/README', persona: '처음 온 사용자 (비개발자 포함)',
|
|
4382
|
+
titleEn: 'docs / README', personaEn: 'first-time user (incl. non-developers)',
|
|
4337
4383
|
questions: [
|
|
4338
4384
|
'그래서 30초 안에 뭘 해보면 되지?',
|
|
4339
4385
|
'비개발자가 터미널 명령 하나 없이 어떻게 사용하지?',
|
|
4340
4386
|
'기존 도구가 이미 있는데 이걸 쓸 이유가 뭐지?'
|
|
4341
4387
|
],
|
|
4342
|
-
|
|
4388
|
+
questionsEn: [
|
|
4389
|
+
'So, what can I actually try in 30 seconds?',
|
|
4390
|
+
'How does a non-developer use this without a single terminal command?',
|
|
4391
|
+
'Tools already exist — why use this one?'
|
|
4392
|
+
],
|
|
4393
|
+
affects: ['design'], affectsNote: '문서가 어렵다면 보통 제품 흐름(UX) 자체가 어렵다는 신호',
|
|
4394
|
+
affectsNoteEn: 'hard docs usually signal the product flow (UX) itself is hard'
|
|
4343
4395
|
},
|
|
4344
4396
|
test: {
|
|
4345
4397
|
title: '테스트', persona: '검증자',
|
|
4398
|
+
titleEn: 'tests', personaEn: 'verifier',
|
|
4346
4399
|
questions: [
|
|
4347
4400
|
'이 테스트는 실패할 수 있는 테스트인가? (assert(true) 아님)',
|
|
4348
4401
|
'주장한 테스트 개수/통과가 실측과 일치하는가?',
|
|
4349
4402
|
'테스트가 구현을 실제로 import/호출하는가?'
|
|
4350
4403
|
],
|
|
4351
|
-
|
|
4404
|
+
questionsEn: [
|
|
4405
|
+
'Can this test actually fail? (not assert(true))',
|
|
4406
|
+
'Do the claimed test count/pass match the real measured numbers?',
|
|
4407
|
+
'Does the test actually import/call the implementation?'
|
|
4408
|
+
],
|
|
4409
|
+
affects: ['code'], affectsNote: '테스트하기 어렵다면 코드가 복잡하다는 신호 — code 질문으로 돌아갈 것',
|
|
4410
|
+
affectsNoteEn: 'hard-to-test signals complex code — go back to the code questions'
|
|
4352
4411
|
},
|
|
4353
4412
|
security: {
|
|
4354
4413
|
title: '보안', persona: '공격자',
|
|
4414
|
+
titleEn: 'security', personaEn: 'attacker',
|
|
4355
4415
|
questions: [
|
|
4356
4416
|
'시크릿이 코드/커밋에 들어가지 않았는가?',
|
|
4357
4417
|
'이 입력을 악의적으로 주면 어떻게 되는가?',
|
|
4358
4418
|
'권한/경계를 한 단어 비틀기로 우회할 수 있는가?'
|
|
4359
4419
|
],
|
|
4360
|
-
|
|
4420
|
+
questionsEn: [
|
|
4421
|
+
'Are there no secrets in the code/commits?',
|
|
4422
|
+
'What happens if this input is given maliciously?',
|
|
4423
|
+
'Can a one-word twist bypass a permission/boundary?'
|
|
4424
|
+
],
|
|
4425
|
+
affects: ['code', 'test'], affectsNote: '보안 가드를 넣었다면 우회/오탐 테스트가 따라와야 함',
|
|
4426
|
+
affectsNoteEn: 'if you added a security guard, bypass/false-positive tests should follow'
|
|
4361
4427
|
}
|
|
4362
4428
|
};
|
|
4363
4429
|
// 1.19.3 (UR-0003 렌즈 완전판 v3): 프로젝트별 커스텀 렌즈 — .harness/quality-lenses.json 읽기-병합(쓰기 명령 없음, AI/사용자가 편집).
|
|
@@ -4410,24 +4476,32 @@ function lensCmd(domain, opts = {}) {
|
|
|
4410
4476
|
if (domain != null) domain = String(domain).trim().toLowerCase();
|
|
4411
4477
|
// 1.19.3: 내장 + 프로젝트 커스텀(.harness/quality-lenses.json) 병합 catalog.
|
|
4412
4478
|
const root = opts.root || arg('--path', process.cwd());
|
|
4479
|
+
const L = _uiLang(root); const t = (ko, en) => (L === 'en' ? en : ko); // 1.24.2 (UR-0010 Phase 8): lens 영어 opt-in
|
|
4413
4480
|
const catalog = _effectiveLensCatalog(root);
|
|
4414
4481
|
if (domain && !catalog[domain]) {
|
|
4415
|
-
return fail(`알 수 없는 렌즈: ${domain} — 유효값: ${Object.keys(catalog).join(', ')}`);
|
|
4482
|
+
return fail(t(`알 수 없는 렌즈: ${domain} — 유효값: ${Object.keys(catalog).join(', ')}`, `unknown lens: ${domain} — valid: ${Object.keys(catalog).join(', ')}`));
|
|
4416
4483
|
}
|
|
4417
4484
|
const picked = domain ? { [domain]: catalog[domain] } : catalog;
|
|
4418
4485
|
if (jsonMode) { log(JSON.stringify({ ok: true, lenses: picked }, null, 2)); return; }
|
|
4419
4486
|
const hasCustom = Object.values(catalog).some(l => l && (l._custom || l._customAdded));
|
|
4420
|
-
log(`# leerness lens — 분야별 자기질문 품질 렌즈 (v${VERSION})${hasCustom ? ' + 프로젝트 커스텀(.harness/quality-lenses.json)' : ''}`);
|
|
4421
|
-
log(`완료 선언 전 해당 분야 질문에 스스로 답해보세요. "그렇다(통과)"라고 답할 수 없으면 아직 완료가
|
|
4487
|
+
log(t(`# leerness lens — 분야별 자기질문 품질 렌즈 (v${VERSION})${hasCustom ? ' + 프로젝트 커스텀(.harness/quality-lenses.json)' : ''}`, `# leerness lens — quality self-question lenses (v${VERSION})${hasCustom ? ' + project custom (.harness/quality-lenses.json)' : ''}`));
|
|
4488
|
+
log(t(`완료 선언 전 해당 분야 질문에 스스로 답해보세요. "그렇다(통과)"라고 답할 수 없으면 아직 완료가 아닙니다.`, `Before declaring done, answer each domain's questions yourself. If you can't answer "yes (pass)", it's not done yet.`));
|
|
4422
4489
|
for (const [key, l] of Object.entries(picked)) {
|
|
4423
4490
|
log('');
|
|
4424
|
-
|
|
4425
|
-
l.
|
|
4426
|
-
|
|
4491
|
+
const title = (L === 'en' && l.titleEn) ? l.titleEn : l.title;
|
|
4492
|
+
const persona = (L === 'en' && l.personaEn) ? l.personaEn : l.persona;
|
|
4493
|
+
const note = (L === 'en' && l.affectsNoteEn) ? l.affectsNoteEn : l.affectsNote;
|
|
4494
|
+
const cflag = l._custom ? t(' [프로젝트]', ' [project]') : (l._customAdded ? t(' [+프로젝트 질문]', ' [+project questions]') : '');
|
|
4495
|
+
// en: builtin 영어 질문 + (커스텀 추가질문은 questions 의 builtin 개수 이후 — questionsEn 과 동일 개수 기준으로 분리)
|
|
4496
|
+
let qs = l.questions;
|
|
4497
|
+
if (L === 'en' && l.questionsEn) qs = l.questions.length > l.questionsEn.length ? [...l.questionsEn, ...l.questions.slice(l.questionsEn.length)] : l.questionsEn;
|
|
4498
|
+
log(`## ${key} (${title}) — ${t('페르소나', 'persona')}: ${persona}${cflag}`);
|
|
4499
|
+
qs.forEach((q, i) => log(` ${i + 1}. ${q}`));
|
|
4500
|
+
log(t(` ↔ 인과: ${key} 를 바꾸면 → ${(l.affects || []).join(', ') || '(없음)'} 질문도 다시 — ${note}`, ` ↔ causality: change ${key} → re-check ${(l.affects || []).join(', ') || '(none)'} too — ${note}`));
|
|
4427
4501
|
}
|
|
4428
4502
|
log('');
|
|
4429
|
-
log(`사용: leerness lens <${Object.keys(catalog).join('|')}> · 완료 검증과 함께: leerness verify-claim T-XXXX`);
|
|
4430
|
-
if (!hasCustom) log(`프로젝트 커스텀 렌즈: .harness/quality-lenses.json 에 { "domains": { "code": { "questions": ["..."] } } }
|
|
4503
|
+
log(t(`사용: leerness lens <${Object.keys(catalog).join('|')}> · 완료 검증과 함께: leerness verify-claim T-XXXX`, `Usage: leerness lens <${Object.keys(catalog).join('|')}> · with completion check: leerness verify-claim T-XXXX`));
|
|
4504
|
+
if (!hasCustom) log(t(`프로젝트 커스텀 렌즈: .harness/quality-lenses.json 에 { "domains": { "code": { "questions": ["..."] } } } 추가`, `Project custom lenses: add { "domains": { "code": { "questions": ["..."] } } } to .harness/quality-lenses.json`));
|
|
4431
4505
|
}
|
|
4432
4506
|
|
|
4433
4507
|
function commandsCmd(root) {
|
|
@@ -12371,13 +12445,15 @@ function _retroAggregate(root) {
|
|
|
12371
12445
|
};
|
|
12372
12446
|
}
|
|
12373
12447
|
|
|
12374
|
-
function _retroOneLine(agg) {
|
|
12448
|
+
function _retroOneLine(agg, lang) {
|
|
12449
|
+
// 1.24.1 (UR-0010): 선택적 lang — en 시 한 줄 요약 영어. 미지정 시 ko(기존 호출부 무영향).
|
|
12450
|
+
const t = (ko, en) => (lang === 'en' ? en : ko);
|
|
12375
12451
|
const parts = [];
|
|
12376
12452
|
const done = agg.statusCounts.done;
|
|
12377
12453
|
const total = agg.totalTasks;
|
|
12378
|
-
if (total) parts.push(`완료 ${done}/${total} (${Math.round(done / total * 100)}%)`);
|
|
12379
|
-
if (agg.totalSkillUsage) parts.push(`스킬 ${agg.skillUsage.length}종 / 사용 ${agg.totalSkillUsage}회 / 최적화 ${agg.totalOptimizations}
|
|
12380
|
-
if (agg.activeRules) parts.push(`룰 ${agg.activeRules}건 활성 (${agg.verifiedRules} 검증됨)`);
|
|
12454
|
+
if (total) parts.push(t(`완료 ${done}/${total} (${Math.round(done / total * 100)}%)`, `done ${done}/${total} (${Math.round(done / total * 100)}%)`));
|
|
12455
|
+
if (agg.totalSkillUsage) parts.push(t(`스킬 ${agg.skillUsage.length}종 / 사용 ${agg.totalSkillUsage}회 / 최적화 ${agg.totalOptimizations}건`, `skills ${agg.skillUsage.length} / uses ${agg.totalSkillUsage} / optimizations ${agg.totalOptimizations}`));
|
|
12456
|
+
if (agg.activeRules) parts.push(t(`룰 ${agg.activeRules}건 활성 (${agg.verifiedRules} 검증됨)`, `rules ${agg.activeRules} active (${agg.verifiedRules} verified)`));
|
|
12381
12457
|
if (agg.durations.length >= 4) {
|
|
12382
12458
|
const mid = Math.floor(agg.durations.length / 2);
|
|
12383
12459
|
const a = agg.durations.slice(0, mid).reduce((x, y) => x + y, 0) / mid;
|
|
@@ -12385,10 +12461,10 @@ function _retroOneLine(agg) {
|
|
|
12385
12461
|
if (a > 0) {
|
|
12386
12462
|
const delta = ((b - a) / a) * 100;
|
|
12387
12463
|
const sign = delta > 0 ? '+' : '';
|
|
12388
|
-
parts.push(`검증 ${Math.round(a)}ms→${Math.round(b)}ms (${sign}${delta.toFixed(1)}%)`);
|
|
12464
|
+
parts.push(t(`검증 ${Math.round(a)}ms→${Math.round(b)}ms (${sign}${delta.toFixed(1)}%)`, `verify ${Math.round(a)}ms→${Math.round(b)}ms (${sign}${delta.toFixed(1)}%)`));
|
|
12389
12465
|
}
|
|
12390
12466
|
}
|
|
12391
|
-
parts.push(`결정 ${agg.decisionBlocks}건
|
|
12467
|
+
parts.push(t(`결정 ${agg.decisionBlocks}건 누적`, `decisions ${agg.decisionBlocks} accumulated`));
|
|
12392
12468
|
return parts.join(' · ');
|
|
12393
12469
|
}
|
|
12394
12470
|
|
|
@@ -13350,10 +13426,11 @@ function _autoRoadmap(root, trigger) {
|
|
|
13350
13426
|
const outFile = path.resolve(cfg.outFile || path.join(root, 'roadmap.html'));
|
|
13351
13427
|
const data = _roadmapData(root);
|
|
13352
13428
|
writeUtf8(outFile, _roadmapHTML(data));
|
|
13353
|
-
|
|
13429
|
+
const _en = _uiLang(root) === 'en'; // 1.24.1 (UR-0010): roadmap 로그 en
|
|
13430
|
+
log(_en ? `✓ roadmap.html auto-updated (${trigger}) — ${rel(root, outFile)}` : `✓ roadmap.html 자동 갱신 (${trigger}) — ${rel(root, outFile)}`);
|
|
13354
13431
|
return true;
|
|
13355
13432
|
} catch (e) {
|
|
13356
|
-
warn('roadmap 자동 갱신 실패: ' + (e && e.message ? e.message : e));
|
|
13433
|
+
warn((_uiLang(root) === 'en' ? 'roadmap auto-update failed: ' : 'roadmap 자동 갱신 실패: ') + (e && e.message ? e.message : e));
|
|
13357
13434
|
return false;
|
|
13358
13435
|
}
|
|
13359
13436
|
}
|
|
@@ -18917,7 +18994,7 @@ async function deployAutoCmd(root, service) {
|
|
|
18917
18994
|
// 1.9.85: leerness health — 종합 헬스 체크 (drift + 보안 + skill + MCP + 누적)
|
|
18918
18995
|
const _health = require('../lib/health');
|
|
18919
18996
|
// 1.9.423 (UR-0025/UR-0125 큰 핸들러 모듈화 8번째): healthCmd → lib/health.js (DI 위임, thin wrapper)
|
|
18920
|
-
function healthCmd(root) { return _health.healthCmd(root, { VERSION, STATUSES, has, arg, harnessPath: __filename, listAllSkills, planPath, readProgressRows, readRules, envDiff, _collectSecretFindings, _readUsageStats, _loadDecisions, _loadLessons, _loadShellFailures, _readFeatureGraph, _scanShellScriptsEncoding, _shellEnvDrift, _computeMilestones, _computeRecentChanges, _computeRoundHistory, _collectPyFiles, _analyzePyFile, _collectRuntimeEnv, _listAPISkills, _matchAPISkills, _mcpToolCount }); }
|
|
18997
|
+
function healthCmd(root) { return _health.healthCmd(root, { VERSION, STATUSES, has, arg, uiLang: _uiLang(root), harnessPath: __filename, listAllSkills, planPath, readProgressRows, readRules, envDiff, _collectSecretFindings, _readUsageStats, _loadDecisions, _loadLessons, _loadShellFailures, _readFeatureGraph, _scanShellScriptsEncoding, _shellEnvDrift, _computeMilestones, _computeRecentChanges, _computeRoundHistory, _collectPyFiles, _analyzePyFile, _collectRuntimeEnv, _listAPISkills, _matchAPISkills, _mcpToolCount }); }
|
|
18921
18998
|
|
|
18922
18999
|
function usageStatsCmd(root) {
|
|
18923
19000
|
root = absRoot(root || process.cwd());
|