leerness 1.15.0 → 1.17.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 +79 -0
- package/README.md +6 -6
- package/bin/leerness.js +55 -17
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,84 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.17.0 — 2026-06-09 — 🛡️ [안정화/Stable] 외부 클린룸 일관성 안정 minor
|
|
4
|
+
|
|
5
|
+
**🛡️ 안정화(Stable) minor.** 외부 클린룸 리뷰(게시본 무README 신규사용자 관점)에서 도출한 --json·CLI 일관성 개선(1.16.1~1.16.2)을 검증·통합해 npm 공개. R-0011 정책의 8번째 minor. 영상은 HyperFrames "문제→해소" 디자인.
|
|
6
|
+
|
|
7
|
+
### 이번 minor 통합 (1.16.1~1.16.2)
|
|
8
|
+
- **gate `--json` 단일 객체화**: 텍스트+단계JSON 혼재로 파싱 불가하던 것 → `{ok,total,failed,checks}` 단일 JSON(CI/에이전트 소비 가능).
|
|
9
|
+
- **memory search `--json`**: 플래그 무시하던 것 → `{query,total,results}` 구조화.
|
|
10
|
+
- **명령그룹 무인자 일관화**: `rule`/`skill`/`feature`/`memory` 를 하위명령 없이 부르면 "알 수 없는 명령" 대신 **사용법 힌트**(decision/lesson 과 일관, `--json` 구조화).
|
|
11
|
+
- **문서 정합**: `about` 의 메모리 경로 `.leerness/` → `.harness/`(기본 워크스페이스) 정정.
|
|
12
|
+
|
|
13
|
+
### 검증 (회귀 0)
|
|
14
|
+
- **selftest 216 PASS** · **E2E 365/365 PASS** · npm gate=minor_bump. gate/memory --json valid JSON + bare-group 사용법 힌트 행위 재현. (맹신 X: scan --json exit·AKIA 는 비-버그로 판정해 제외.)
|
|
15
|
+
|
|
16
|
+
### 안정화 표시 (R-0006)
|
|
17
|
+
CHANGELOG [안정화/Stable] · git tag (Stable) · GitHub release (Stable) · npm dist-tag `stable` 시도.
|
|
18
|
+
|
|
19
|
+
## 1.16.2 — 2026-06-09 — CLI 일관성: 명령그룹 무인자 → 사용법 힌트 (외부클린룸 UR-0042)
|
|
20
|
+
|
|
21
|
+
**🧭 명령그룹을 하위명령 없이 부르면 친절한 사용법 안내.** 외부 클린룸 리뷰가 지적: `rule`/`skill`/`feature`/`memory` 를 하위명령 없이 부르면 "알 수 없는 명령"(유효 그룹인데 혼란)이 떴음 — `decision`/`lesson` 처럼 사용법 힌트로 통일.
|
|
22
|
+
|
|
23
|
+
### 변경 (UR-0042)
|
|
24
|
+
- bare `rule`/`skill`/`feature`/`memory` → `subcommand_required` + 실제 하위명령 사용법 표시(예: `memory search "<키>" | memory status | …`). `--json` 도 구조화 출력.
|
|
25
|
+
- 기존 동작 보존: 하위명령이 있는 호출(`memory search` 등)은 그대로.
|
|
26
|
+
|
|
27
|
+
### 검증 (회귀 0)
|
|
28
|
+
- **selftest 215→216** · **E2E 365/365** · 4개 그룹 bare 호출 → 사용법 힌트, `memory --json` → `{code:'subcommand_required'}` 행위 재현.
|
|
29
|
+
- patch(1.16.2) — npm 미배포(R-0011, GitHub). 잔여: bare `leerness` 자동 init·hook(UR-0041, 설계 재검토), `--language en` 런타임(UR-0042 잔여).
|
|
30
|
+
|
|
31
|
+
## 1.16.1 — 2026-06-09 — 외부 클린룸 리뷰: --json 일관성 + 문서 정합
|
|
32
|
+
|
|
33
|
+
**🔬 외부 클린룸 리뷰(게시본 1.16.0 설치·README/소스 미참조·신규 사용자 관점, 2모델).** 발굴된 지적을 직접 재현해 진짜만 수정(맹신 X — 비-버그/의도된 동작은 제외).
|
|
34
|
+
|
|
35
|
+
### 수정 (전부 재현 검증)
|
|
36
|
+
- **gate `--json` 단일 객체화 (C2)**: 이전엔 텍스트 헤더 + 단계별 JSON 이 섞여 파싱 불가. 이제 `{ok, total, failed, checks:[{name,ok}]}` 단일 객체(하위 출력 억제). CI/에이전트 소비 가능.
|
|
37
|
+
- **memory search `--json` (C3)**: `--json` 을 무시하고 텍스트만 내던 것 → `{query, total, results:[{file,line,text}]}` 구조화(전 명령 일관).
|
|
38
|
+
- **about 문서 정합 (C4)**: `about` 의 메모리 설명이 `.leerness/` 라 했으나 기본 워크스페이스는 `.harness/` → 정정(선택 state substrate 만 .leerness/).
|
|
39
|
+
|
|
40
|
+
### 비-버그 판정 (맹신 X — 재현했으나 수정 안 함)
|
|
41
|
+
- **scan secrets `--json` exit 코드**: 리뷰는 exit 0 라 했으나 재현 결과 committed 발견 시 exit 1 정상(코드도 `if(committed.length) process.exitCode=1`). gitignored-only(안전) → exit 0 은 의도된 동작.
|
|
42
|
+
- **AWS AKIA 미탐지**: `...EXAMPLE` 포함은 placeholder 가드로 스킵(의도) — 실 키(EXAMPLE 없음)는 정상 탐지.
|
|
43
|
+
|
|
44
|
+
### 잔여(백로그): bare `leerness`(무인자) 자동 init·hook / `--language en` 런타임 미적용 / 서브명령그룹 무인자 동작 일관화.
|
|
45
|
+
|
|
46
|
+
### 검증 (회귀 0)
|
|
47
|
+
- **selftest 214→215** · **E2E 365/365** · gate/memory --json valid JSON + about .harness 행위 재현.
|
|
48
|
+
- patch(1.16.1) — npm 미배포(R-0011, GitHub).
|
|
49
|
+
|
|
50
|
+
## 1.16.0 — 2026-06-09 — 🛡️ [안정화/Stable] 16번째 버그헌트 안정 minor
|
|
51
|
+
|
|
52
|
+
**🛡️ 안정화(Stable) minor.** 16번째 멀티에이전트 버그헌트(8건 발굴·전부 직접 재현)의 코어 수정(1.15.1)을 검증·통합해 npm 공개. R-0011 정책의 7번째 minor. **이 릴리스 영상부터 개선된 "문제→해소" 디자인**(짧은 인트로 + 이전/이제 모션 + 의미 보존 하이라이트)으로 제작됩니다.
|
|
53
|
+
|
|
54
|
+
### 이번 minor 통합 (1.15.1 — 전부 재현 검증)
|
|
55
|
+
- **🔴 시크릿 스캐너 FN 차단**: 한 파일에 같은 패턴 시크릿이 2개 이상이면 첫 번째만 보고하고 나머지를 놓치던 문제(루프 break) 수정 → 모두 탐지.
|
|
56
|
+
- **🟠 task/rule list 표 정렬**: 제목에 파이프(`|`)가 있으면 마크다운 표 칼럼이 깨지던 표시 버그 → `_cellSafe` 적용.
|
|
57
|
+
- **📖 문서 정합**: `encoding check` 도움말이 실제 미검사 항목(CRLF)을 광고하던 것 정정.
|
|
58
|
+
- (사이트 영상 파이프라인 4버그 — 큐 키/빈 제목/빈 WAV 통과/check-only 부작용 — 도 동시 수정.)
|
|
59
|
+
|
|
60
|
+
### 검증 (회귀 0)
|
|
61
|
+
- **selftest 214 PASS** · **E2E 365/365 PASS** · npm gate=minor_bump. 시크릿 멀티매치·표 파이프·도움말 정합 행위 재현.
|
|
62
|
+
|
|
63
|
+
### 안정화 표시 (R-0006)
|
|
64
|
+
CHANGELOG [안정화/Stable] · git tag annotation (Stable) · GitHub release (Stable) · npm dist-tag `stable` 시도.
|
|
65
|
+
|
|
66
|
+
## 1.15.1 — 2026-06-09 — 16번째 버그헌트: 시크릿 스캐너 FN + 표 파이프 + 문서정합
|
|
67
|
+
|
|
68
|
+
**🔬 멀티에이전트 버그헌트(신규코드 0건·코어 4건·사이트 4건 발굴) → 8건 전부 직접 재현 후 진짜만 수정.** 맹신 X: 각 지적을 temp 에서 직접 재현하고, 의도된 동작은 수정 제외.
|
|
69
|
+
|
|
70
|
+
### 수정 (코어 3건 — 전부 재현 검증)
|
|
71
|
+
- **🔴 시크릿 스캐너 FN (F1)**: 한 파일에 같은 패턴의 시크릿이 2개 이상(예: `secret:` 와 `api_key:` 둘 다 'Hardcoded password' 패턴)이면 **첫 번째만 잡고 나머지 누락**(루프 `break`). break 제거 + zero-width 가드 → 모두 탐지.
|
|
72
|
+
- **🟠 task/rule list 표 깨짐 (F2)**: 항목 제목에 파이프(`|`)가 있으면 마크다운 표 칼럼이 어긋남(저장은 안전했으나 list **표시**가 raw). 표시도 `_cellSafe` 적용(`\|`).
|
|
73
|
+
- **문서정합 (F3)**: `encoding check` 도움말이 'CRLF 검사'를 광고했으나 실제 미검사(정책은 BOM/NUL/.bat/roundtrip). 도움말을 실제 동작과 일치하게 정정.
|
|
74
|
+
|
|
75
|
+
### 의도된 동작(수정 안 함 — 맹신 X)
|
|
76
|
+
- `status --json` 가 `healthy:false` 여도 exit 0: **설계상 정보성 명령**(`scope:'install'` + `healthyMeaning` 명시, 게이트는 `verify`/`gate`). 버그 아님.
|
|
77
|
+
|
|
78
|
+
### 검증 (회귀 0)
|
|
79
|
+
- **selftest 213→214** · **E2E 365/365** · F1(2건 탐지)·F2(파이프 `\|` 이스케이프)·F3(help CRLF 제거) 행위 재현. (사이트 파이프라인 4건은 leerness-site 에서 별도 수정.)
|
|
80
|
+
- patch(1.15.1) — npm 미배포(R-0011, GitHub).
|
|
81
|
+
|
|
3
82
|
## 1.15.0 — 2026-06-09 — 🛡️ [안정화/Stable] Karpathy 가이드라인 정렬 3부작 안정 minor
|
|
4
83
|
|
|
5
84
|
**🛡️ 안정화(Stable) minor.** Andrej Karpathy 코딩 가이드라인(생각하고 코딩 / 단순성 / 외과적 변경 / 목표 주도) 대비 외부 에이전트 검토에서 도출한 정렬 작업(1.14.1~1.14.3)을 검증·통합해 npm 공개. R-0011 정책의 6번째 minor. 영상은 HyperFrames 파이프라인 제작.
|
package/README.md
CHANGED
|
@@ -186,13 +186,13 @@ MIT
|
|
|
186
186
|
<!-- leerness:project-readme:start -->
|
|
187
187
|
## Leerness Project Harness
|
|
188
188
|
|
|
189
|
-
이 프로젝트는 Leerness v1.
|
|
189
|
+
이 프로젝트는 Leerness v1.17.0 하네스를 사용합니다. AI 에이전트는 작업 전 `leerness handoff`로 컨텍스트를 적재하고, 작업 후 `leerness check`/`leerness audit`/`leerness session close`를 수행해야 합니다.
|
|
190
190
|
|
|
191
191
|
### 정체성 — AI 에이전트 운영 레이어 (UR-0030)
|
|
192
192
|
|
|
193
193
|
Leerness 는 **실행기/코딩 에이전트가 아니라**, 어떤 AI 코딩 에이전트(Claude Code · Codex · Cursor · Goose 등) 위에도 얹는 **범용 운영 레이어**입니다. 5개 공통 계층을 제공합니다:
|
|
194
194
|
|
|
195
|
-
- **기억(Memory)** — 프로젝트 상태/결정/진행을 `.
|
|
195
|
+
- **기억(Memory)** — 프로젝트 상태/결정/진행을 `.harness/` 에 영속화
|
|
196
196
|
- **정책(Policy)** — 8단계 권한 등급 + enforce (read-only→publish), MCP 호출 게이트
|
|
197
197
|
- **인수인계(Handoff)** — 에이전트 간 컨텍스트 표준 전달 + `get_project_context` 1콜 온보딩
|
|
198
198
|
- **검증(Verification)** — 근거 기반 완료 검증으로 허위 완료 차단
|
|
@@ -208,7 +208,7 @@ leerness status . # 설치 상태
|
|
|
208
208
|
leerness verify . # 필수 파일 검증
|
|
209
209
|
leerness audit . # 일관성·계획-진행 정렬 감사
|
|
210
210
|
leerness scan secrets . # 시크릿 패턴 스캔
|
|
211
|
-
leerness encoding check . # UTF-8 / BOM /
|
|
211
|
+
leerness encoding check . # UTF-8 / BOM / NUL / .bat 인코딩 검사
|
|
212
212
|
leerness lazy detect . # 게으름 방지 자동 평가
|
|
213
213
|
leerness memory search "키" # 결정/이력 검색
|
|
214
214
|
leerness session close . # 세션 종료 + handoff 자동 작성
|
|
@@ -240,7 +240,7 @@ leerness memory restore decision <date|title>
|
|
|
240
240
|
|
|
241
241
|
### MCP server (외부 AI 통합)
|
|
242
242
|
|
|
243
|
-
Leerness v1.
|
|
243
|
+
Leerness v1.17.0는 stdio JSON-RPC MCP server를 내장합니다 — Claude Code · Cursor · Codex CLI 등 외부 AI에 **85개 도구**를 노출:
|
|
244
244
|
|
|
245
245
|
```jsonc
|
|
246
246
|
// 카테고리별
|
|
@@ -261,7 +261,7 @@ Leerness v1.15.0는 stdio JSON-RPC MCP server를 내장합니다 — Claude Code
|
|
|
261
261
|
`<<autonomous-loop-dynamic>>` 신호만 보내면 AI가:
|
|
262
262
|
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) 다음 라운드 예약.
|
|
263
263
|
|
|
264
|
-
현재 누적: **70 라운드 (1.9.40 → 1.
|
|
264
|
+
현재 누적: **70 라운드 (1.9.40 → 1.17.0)** · 매 라운드 GitHub release/태그 생성 · _reports/는 비공개 보존.
|
|
265
265
|
|
|
266
266
|
### 성능 가이드 (1.9.140 측정)
|
|
267
267
|
|
|
@@ -299,6 +299,6 @@ leerness release pack --close --auto-main-push
|
|
|
299
299
|
- `.harness/session-handoff.md`: 다음 세션 인수인계 (자동 작성)
|
|
300
300
|
- `.harness/lessons.md` / `decisions.md` / `rules.md`: 영구 메모리 (5 surface)
|
|
301
301
|
|
|
302
|
-
Last synced by Leerness v1.
|
|
302
|
+
Last synced by Leerness v1.17.0: 2026-06-09
|
|
303
303
|
<!-- leerness:project-readme:end -->
|
|
304
304
|
|
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.17.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') 시 호스트 프로세스 오염.
|
|
@@ -281,7 +281,7 @@ function managedReadmeBlock(project) {
|
|
|
281
281
|
'',
|
|
282
282
|
'Leerness 는 **실행기/코딩 에이전트가 아니라**, 어떤 AI 코딩 에이전트(Claude Code · Codex · Cursor · Goose 등) 위에도 얹는 **범용 운영 레이어**입니다. 5개 공통 계층을 제공합니다:',
|
|
283
283
|
'',
|
|
284
|
-
'- **기억(Memory)** — 프로젝트 상태/결정/진행을 `.
|
|
284
|
+
'- **기억(Memory)** — 프로젝트 상태/결정/진행을 `.harness/` 에 영속화',
|
|
285
285
|
'- **정책(Policy)** — 8단계 권한 등급 + enforce (read-only→publish), MCP 호출 게이트',
|
|
286
286
|
'- **인수인계(Handoff)** — 에이전트 간 컨텍스트 표준 전달 + `get_project_context` 1콜 온보딩',
|
|
287
287
|
'- **검증(Verification)** — 근거 기반 완료 검증으로 허위 완료 차단',
|
|
@@ -297,7 +297,7 @@ function managedReadmeBlock(project) {
|
|
|
297
297
|
'leerness verify . # 필수 파일 검증',
|
|
298
298
|
'leerness audit . # 일관성·계획-진행 정렬 감사',
|
|
299
299
|
'leerness scan secrets . # 시크릿 패턴 스캔',
|
|
300
|
-
'leerness encoding check . # UTF-8 / BOM /
|
|
300
|
+
'leerness encoding check . # UTF-8 / BOM / NUL / .bat 인코딩 검사',
|
|
301
301
|
'leerness lazy detect . # 게으름 방지 자동 평가',
|
|
302
302
|
'leerness memory search "키" # 결정/이력 검색',
|
|
303
303
|
'leerness session close . # 세션 종료 + handoff 자동 작성',
|
|
@@ -624,7 +624,7 @@ leerness memory restore <surface> <target> # archive → active 복귀 (DELETE
|
|
|
624
624
|
'.claude/commands/audit.md': `# /audit\n\n계획-진행 정렬, 디자인/재사용 일관성, 시크릿/인코딩을 일괄 점검합니다.\n\n\`\`\`\n!leerness audit .\n!leerness scan secrets .\n!leerness encoding check .\n\`\`\`\n`,
|
|
625
625
|
'.claude/commands/lazy-detect.md': `# /lazy-detect\n\n게으름 방지 자동 평가를 실행합니다.\n\n\`\`\`\n!leerness lazy detect .\n\`\`\`\n`,
|
|
626
626
|
'.claude/commands/update.md': `# /update\n\nleerness 자동 업데이트를 실행합니다 (감지 → 마이그레이션 → 검증).\n\n\`\`\`\n!leerness update --yes\n\`\`\`\n`,
|
|
627
|
-
'.claude/skills/leerness.md': `---\nname: leerness\ndescription: Leerness harness commands - handoff, audit, scan secrets, encoding check, lazy detect, session close, update. Use when the user asks to load project context, verify work quality, scan secrets, check encoding, or end a session.\n---\n\n# leerness skill\n\n## When to use\n- 사용자가 프로젝트 컨텍스트를 로드해달라고 할 때\n- 완료 선언 전 자기 검증을 요청할 때\n- 세션을 종료하거나 인수인계를 요청할 때\n- 시크릿/한글 인코딩 점검을 요청할 때\n- 새 leerness 버전 적용을 요청할 때\n\n## Commands\n\n\`\`\`bash\nleerness handoff . # 컨텍스트 로드\nleerness check . # pre-action 체크\nleerness audit . # 일관성/계획 정렬 감사\nleerness scan secrets . # 시크릿 패턴 스캔\nleerness encoding check . # UTF-8/BOM/
|
|
627
|
+
'.claude/skills/leerness.md': `---\nname: leerness\ndescription: Leerness harness commands - handoff, audit, scan secrets, encoding check, lazy detect, session close, update. Use when the user asks to load project context, verify work quality, scan secrets, check encoding, or end a session.\n---\n\n# leerness skill\n\n## When to use\n- 사용자가 프로젝트 컨텍스트를 로드해달라고 할 때\n- 완료 선언 전 자기 검증을 요청할 때\n- 세션을 종료하거나 인수인계를 요청할 때\n- 시크릿/한글 인코딩 점검을 요청할 때\n- 새 leerness 버전 적용을 요청할 때\n\n## Commands\n\n\`\`\`bash\nleerness handoff . # 컨텍스트 로드\nleerness check . # pre-action 체크\nleerness audit . # 일관성/계획 정렬 감사\nleerness scan secrets . # 시크릿 패턴 스캔\nleerness encoding check . # UTF-8/BOM/NUL\nleerness lazy detect . # 게으름 평가\nleerness memory search "key" # 결정/이력 검색\nleerness session close . # 종료 보고 + handoff 자동 생성\nleerness update --yes # 자동 업데이트\n\`\`\`\n`,
|
|
628
628
|
};
|
|
629
629
|
// 1.9.276: minimal 모드 — 코어가 요구하지 않는 파일 제외 (verify 필수 파일은 유지).
|
|
630
630
|
if (opts.minimal) { for (const k of MINIMAL_SKIP_KEYS) delete _files[k]; }
|
|
@@ -3539,6 +3539,24 @@ function _selfTestCases() {
|
|
|
3539
3539
|
const dw = (b.match(/^Done-When:\s*(.+)$/m) || [])[1];
|
|
3540
3540
|
return wired && dw === '로그인 e2e 테스트 통과';
|
|
3541
3541
|
} },
|
|
3542
|
+
{ name: '16th 버그헌트 F1/F2: scan secrets 패턴당 멀티매치(break 제거) + task/rule list 파이프 셀안전 (1.15.1)', run: () => {
|
|
3543
|
+
const src = read(__filename);
|
|
3544
|
+
const f1 = src.includes('같은 패턴이 한 파일에 여러 번') && src.includes('if (re.lastIndex === m.index) re.lastIndex++;');
|
|
3545
|
+
const f2 = src.includes('_cellSafe(r.request)') && src.includes('_cellSafe(r.rule)');
|
|
3546
|
+
return f1 && f2;
|
|
3547
|
+
} },
|
|
3548
|
+
{ name: '외부클린룸 C2/C3/C4: gate --json 단일객체 + memory search --json + about .harness 정합 (1.16.1)', run: () => {
|
|
3549
|
+
const src = read(__filename);
|
|
3550
|
+
const c2 = src.includes("const jsonMode = has('--json'); // 외부리뷰 C2") && src.includes('ok: bad === 0, total: checks.length, failed: bad, checks');
|
|
3551
|
+
const c3 = src.includes('// 외부리뷰 C3: --json 일관성') && src.includes('JSON.stringify({ version: VERSION, query, total, includeCode');
|
|
3552
|
+
const _badDir = '.leern' + 'ess/ 에 영속화 (state start'; // 자기참조 회피: 분할 — about state 줄이 .leerness 로 남아있으면 감지
|
|
3553
|
+
const c4 = src.includes('상태/결정/진행을 .harness/ 에 영속화 (task/decision') && !src.includes('상태/결정/진행을 ' + _badDir);
|
|
3554
|
+
return c2 && c3 && c4;
|
|
3555
|
+
} },
|
|
3556
|
+
{ name: '외부클린룸 UR-0042: bare 명령그룹(rule/skill/feature/memory) → 사용법 힌트(unknown command 아님) (1.16.2)', run: () => {
|
|
3557
|
+
const src = read(__filename);
|
|
3558
|
+
return src.includes('const _GROUP_USAGE = {') && src.includes("if (_GROUP_USAGE[cmd] && !args[1])") && src.includes("'subcommand_required'");
|
|
3559
|
+
} },
|
|
3542
3560
|
{ name: 'VERSION 형식 (x.y.z)', run: () => /^\d+\.\d+\.\d+$/.test(VERSION) }
|
|
3543
3561
|
];
|
|
3544
3562
|
}
|
|
@@ -6589,7 +6607,7 @@ function taskList(root) {
|
|
|
6589
6607
|
if (!filtered.length) return log('(no tasks)');
|
|
6590
6608
|
log('| ID | Status | Request | Evidence | Next Action | Updated |');
|
|
6591
6609
|
log('|---|---|---|---|---|---|');
|
|
6592
|
-
for (const r of filtered) log(`| ${r.id} | ${r.status} | ${r.request} | ${r.evidence} | ${r.nextAction} | ${r.updated} |`);
|
|
6610
|
+
for (const r of filtered) log(`| ${r.id} | ${r.status} | ${_cellSafe(r.request)} | ${_cellSafe(r.evidence)} | ${_cellSafe(r.nextAction)} | ${r.updated} |`); // 16th 버그헌트 F2: 표시도 _cellSafe — 파이프(|)가 칼럼 깨던 것 차단(저장은 안전했으나 list 표시가 raw)
|
|
6593
6611
|
}
|
|
6594
6612
|
// 1.9.310 (UR-0046, 설치리뷰 3중수렴): CLI/MCP 입력 스키마 검증 — 무효 status/trigger 거부(--force 우회).
|
|
6595
6613
|
// 이전: task --status nonsense / rule --trigger 오타가 그대로 등록돼 상태/정책 신뢰성 훼손.
|
|
@@ -7394,7 +7412,8 @@ function _collectSecretFindings(root) {
|
|
|
7394
7412
|
if (valueGroup != null && requireSecretLike && !_looksSecretLike(val)) { if (re.lastIndex === m.index) re.lastIndex++; continue; }
|
|
7395
7413
|
const line = text.slice(0, m.index).split('\n').length;
|
|
7396
7414
|
findings.push({ file: fileRel, line, name, snippet: m[0].slice(0, 32), gitignored });
|
|
7397
|
-
break
|
|
7415
|
+
// 16th 버그헌트 F1: break 제거 — 같은 패턴이 한 파일에 여러 번(예: secret: + api_key: 둘 다 'Hardcoded password') 나오면 모두 보고(보안 FN 차단). zero-width 매치는 lastIndex 전진으로 무한루프 방지.
|
|
7416
|
+
if (re.lastIndex === m.index) re.lastIndex++;
|
|
7398
7417
|
}
|
|
7399
7418
|
}
|
|
7400
7419
|
}
|
|
@@ -7629,7 +7648,8 @@ function preCheck(root) {
|
|
|
7629
7648
|
|
|
7630
7649
|
function memorySearch(root, query) {
|
|
7631
7650
|
root = absRoot(root);
|
|
7632
|
-
|
|
7651
|
+
const jsonMode = has('--json'); const results = []; // 외부리뷰 C3: --json 일관성(이전엔 --json 무시하고 텍스트만)
|
|
7652
|
+
if (!query) { failJson(jsonMode, 'query_required', 'query required (e.g., memory search "키워드")'); return; }
|
|
7633
7653
|
// 1.13.1 (15th 블라인드 리뷰 P1, Sonnet): lessons.md + rules.md 누락 수정 — memory search 가 5종 메모리 표면을 표방하나 lesson/rule 을 검색 못 해(lesson add/rule add 로 저장한 교훈·룰이 'no matches') 모순감지 핵심 용도가 훼손됐음.
|
|
7634
7654
|
const files = ['.harness/decisions.md','.harness/lessons.md','.harness/rules.md','.harness/task-log.md','.harness/session-handoff.md','.harness/progress-tracker.md','.harness/plan.md','.harness/review-evidence.md','.harness/architecture.md'];
|
|
7635
7655
|
const re = new RegExp(query.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&'), 'i');
|
|
@@ -7639,8 +7659,8 @@ function memorySearch(root, query) {
|
|
|
7639
7659
|
const lines = read(p).split('\n');
|
|
7640
7660
|
const hits = lines.map((line, i) => ({ line, i })).filter(x => re.test(x.line));
|
|
7641
7661
|
if (hits.length) {
|
|
7642
|
-
log(`\n# ${f}`);
|
|
7643
|
-
for (const h of hits.slice(0, _parseLimit(arg('--limit','5'),5))) log(` L${h.i+1}: ${h.line.trim()}`);
|
|
7662
|
+
if (!jsonMode) log(`\n# ${f}`);
|
|
7663
|
+
for (const h of hits.slice(0, _parseLimit(arg('--limit','5'),5))) { if (!jsonMode) log(` L${h.i+1}: ${h.line.trim()}`); results.push({ file: f, line: h.i + 1, text: h.line.trim() }); }
|
|
7644
7664
|
total += hits.length;
|
|
7645
7665
|
}
|
|
7646
7666
|
}
|
|
@@ -7661,8 +7681,8 @@ function memorySearch(root, query) {
|
|
|
7661
7681
|
const lines = txt.split('\n');
|
|
7662
7682
|
const hits = lines.map((line, i) => ({ line, i })).filter(x => re.test(x.line));
|
|
7663
7683
|
if (hits.length) {
|
|
7664
|
-
log(`\n# ${rel(root, p)}`);
|
|
7665
|
-
for (const h of hits.slice(0, _parseLimit(arg('--limit','5'),5))) log(` L${h.i+1}: ${h.line.trim().slice(0, 160)}`);
|
|
7684
|
+
if (!jsonMode) log(`\n# ${rel(root, p)}`);
|
|
7685
|
+
for (const h of hits.slice(0, _parseLimit(arg('--limit','5'),5))) { if (!jsonMode) log(` L${h.i+1}: ${h.line.trim().slice(0, 160)}`); results.push({ file: rel(root, p), line: h.i + 1, text: h.line.trim().slice(0, 160) }); }
|
|
7666
7686
|
total += hits.length;
|
|
7667
7687
|
}
|
|
7668
7688
|
}
|
|
@@ -7670,6 +7690,7 @@ function memorySearch(root, query) {
|
|
|
7670
7690
|
walkCodeDir(dp);
|
|
7671
7691
|
}
|
|
7672
7692
|
}
|
|
7693
|
+
if (jsonMode) { log(JSON.stringify({ version: VERSION, query, total, includeCode: has('--include-code'), results }, null, 2)); return; }
|
|
7673
7694
|
if (total === 0) log('(no matches)');
|
|
7674
7695
|
else log(`\n${total} matches${has('--include-code') ? ' (소스 코드 포함)' : ''}`);
|
|
7675
7696
|
}
|
|
@@ -11702,13 +11723,21 @@ async function selfCheck(root) {
|
|
|
11702
11723
|
// 1.9.2: 게이트 5종 한번에 실행 (verify + audit + scan secrets + encoding check + lazy detect).
|
|
11703
11724
|
function gate(root) {
|
|
11704
11725
|
root = absRoot(root);
|
|
11705
|
-
|
|
11726
|
+
const jsonMode = has('--json'); // 외부리뷰 C2: --json 일관성 — 이전엔 텍스트 헤더+단계별 JSON 혼재로 파싱 불가. 단일 객체로 집계.
|
|
11727
|
+
const checks = [];
|
|
11706
11728
|
let bad = 0;
|
|
11729
|
+
if (!jsonMode) log('# leerness gate (5 checks)');
|
|
11707
11730
|
function step(label, fn) {
|
|
11708
|
-
log(`\n## ${label}`);
|
|
11709
11731
|
const code0 = process.exitCode || 0;
|
|
11710
|
-
|
|
11711
|
-
|
|
11732
|
+
if (!jsonMode) log(`\n## ${label}`);
|
|
11733
|
+
const orig = process.stdout.write;
|
|
11734
|
+
if (jsonMode) process.stdout.write = () => true; // 단계 하위출력 억제(JSON 오염 방지) — fn 은 동기, finally 로 복원
|
|
11735
|
+
let threw = null;
|
|
11736
|
+
try { fn(); } catch (e) { threw = e; } finally { if (jsonMode) process.stdout.write = orig; }
|
|
11737
|
+
const failed = threw != null || !!(process.exitCode && process.exitCode !== code0);
|
|
11738
|
+
if (threw && !jsonMode) fail(`${label} threw: ${threw.message}`);
|
|
11739
|
+
if (failed) bad++;
|
|
11740
|
+
checks.push({ name: label, ok: !failed, ...(threw ? { error: threw.message } : {}) });
|
|
11712
11741
|
process.exitCode = 0;
|
|
11713
11742
|
}
|
|
11714
11743
|
step('verify', () => verify(root));
|
|
@@ -11716,6 +11745,7 @@ function gate(root) {
|
|
|
11716
11745
|
step('scan secrets', () => scanSecrets(root));
|
|
11717
11746
|
step('encoding check', () => encodingCheck(root));
|
|
11718
11747
|
step('lazy detect', () => lazyDetect(root));
|
|
11748
|
+
if (jsonMode) { log(JSON.stringify({ version: VERSION, root, ok: bad === 0, total: checks.length, failed: bad, checks }, null, 2)); if (bad) process.exitCode = 1; return; }
|
|
11719
11749
|
log(`\n# gate summary: ${bad} 단계 실패`);
|
|
11720
11750
|
if (bad) process.exitCode = 1;
|
|
11721
11751
|
else ok('all gates passed');
|
|
@@ -12937,7 +12967,7 @@ function ruleList(root) {
|
|
|
12937
12967
|
if (!rules.length) return ok('등록된 룰 없음');
|
|
12938
12968
|
log('| ID | Trigger | Rule | Status | Last Verified |');
|
|
12939
12969
|
log('|---|---|---|---|---|');
|
|
12940
|
-
for (const r of rules) log(`| ${r.id} | ${r.trigger} | ${r.rule} | ${r.status} | ${r.lastVerified} |`);
|
|
12970
|
+
for (const r of rules) log(`| ${r.id} | ${_cellSafe(r.trigger)} | ${_cellSafe(r.rule)} | ${r.status} | ${r.lastVerified} |`); // 16th 버그헌트 F2: 파이프 칼럼 깨짐 차단(표시 _cellSafe)
|
|
12941
12971
|
}
|
|
12942
12972
|
|
|
12943
12973
|
function ruleRemove(root, id) {
|
|
@@ -16623,7 +16653,7 @@ function _leernessIdentity() {
|
|
|
16623
16653
|
isNot: '실행기/코딩 에이전트가 아님 — 어떤 에이전트 위에도 얹는 공통 운영 계층',
|
|
16624
16654
|
tagline: '어떤 AI 코딩 에이전트에도 적용되는 범용 운영 레이어 — 기억·정책·인수인계·검증·감사',
|
|
16625
16655
|
layers: [
|
|
16626
|
-
{ key: 'memory', ko: '기억', desc: '프로젝트 상태/결정/진행을 .
|
|
16656
|
+
{ key: 'memory', ko: '기억', desc: '프로젝트 상태/결정/진행을 .harness/ 에 영속화 (task/decision/lesson/plan; 선택 state substrate 는 .leerness/)' },
|
|
16627
16657
|
{ key: 'policy', ko: '정책', desc: '8단계 권한 등급 + enforce (read-only→publish), MCP 호출 게이트' },
|
|
16628
16658
|
{ key: 'handoff', ko: '인수인계', desc: '에이전트 간 컨텍스트 표준 전달 (Claude→Codex→Goose), get_project_context 1콜 온보딩' },
|
|
16629
16659
|
{ key: 'verification', ko: '검증', desc: '근거 기반 완료 검증 (verify-claim --require-evidence) — 허위 완료 차단' },
|
|
@@ -19498,6 +19528,14 @@ async function main() {
|
|
|
19498
19528
|
}
|
|
19499
19529
|
// 1.9.306 (UR-0045): 명시적 help 요청은 exit 0, 그 외 미인식 명령은 안내 + exit 1 (실패를 성공으로 오판 방지).
|
|
19500
19530
|
if (cmd === 'help' || cmd === 'commands' || cmd === '--help' || cmd === '-h') { help(); return; }
|
|
19531
|
+
// 1.16.2 (외부클린룸 UR-0042): 유효 명령그룹을 하위명령 없이 부르면 'unknown command'(혼란) 대신 사용법 힌트 — decision/lesson 과 일관.
|
|
19532
|
+
const _GROUP_USAGE = {
|
|
19533
|
+
rule: 'rule add "<텍스트>" --trigger <트리거> | rule list | rule pause/resume/remove <ID> | rule verify',
|
|
19534
|
+
skill: 'skill list | skill add <id> | skill use <id> | skill search "<키>" | skill match "<텍스트>"',
|
|
19535
|
+
feature: 'feature add "<이름>" | feature list | feature show <ID> | feature link <A> <B> | feature impact <ID>',
|
|
19536
|
+
memory: 'memory search "<키>" [--json] | memory status | memory archive | memory restore',
|
|
19537
|
+
};
|
|
19538
|
+
if (_GROUP_USAGE[cmd] && !args[1]) { failJson(has('--json'), 'subcommand_required', `${cmd} 하위명령 필요 — 사용법: leerness ${_GROUP_USAGE[cmd]}`); return; }
|
|
19501
19539
|
// 1.9.437 (11th 외부평가 Codex P2, UR-0138): --json 모드 unknown command 도 순수 JSON.
|
|
19502
19540
|
failJson(has('--json'), 'unknown_command', `알 수 없는 명령: ${cmd} (leerness --help 로 전체 명령 확인)`);
|
|
19503
19541
|
return;
|