leerness 1.9.271 → 1.9.273

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,45 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.9.273 — 2026-06-03 — UR-0027: 빠른 테스트 서브셋 (test:fast smoke) — npm test 5분 초과 해소
4
+
5
+ **⚡ 전체 e2e(220+, 수 분) 외에 핵심-경로만 빠르게 확인하는 `npm run test:fast`(~10초) 추가 (GPT-5.5 리뷰 반영).**
6
+
7
+ ### 배경
8
+ GPT-5.5 외부 리뷰에서 `npm test`(전체 e2e)가 5분 제한 안에 끝나지 않아 외부 환경에서 완료 확인이 어렵다고 지적(UR-0027). 전체 e2e 는 신뢰도(CI/릴리스 게이트)에 필요하므로 유지하되, 개발 중 빠른 피드백용 경량 smoke 를 분리.
9
+
10
+ ### 구현
11
+ 1. **`scripts/smoke.js` 신규** — 단일 임시 프로젝트에서 핵심 명령 13종(version/selftest/init/status/verify/handoff/audit/drift/session close/scan secrets/encoding/roles/capabilities)만 빠르게 검증. 실패 시 exit 1. **~10초** (전체 e2e 5분+ 대비).
12
+ 2. **`npm run test:fast`** — `selftest + smoke.js`. README 기여 섹션에 안내.
13
+ 3. **CI 빠른 잡** — `.github/workflows/ci.yml` 에 `fast`(selftest+smoke, ubuntu/node20) 잡 추가 → PR 즉시 피드백, 전체 e2e 매트릭스는 병행.
14
+ 4. **e2e +1 (220→221)** — test:fast 인프라(smoke.js 존재+구문+package script) 회귀 검증.
15
+
16
+ ### 검증
17
+ - **test:fast 13/13 PASS · ~10.7초** · **selftest 25/25** · **E2E 221/221 PASS** (회귀 0).
18
+
19
+ ## 1.9.272 — 2026-06-03 — 공개 신뢰도·투명성 강화 (GPT-5.5 외부 리뷰 반영)
20
+
21
+ **🔒 외부 평가(GPT-5.5 직접 실행/검사, 7/10)에서 지적된 공개 패키지 신뢰도·투명성 항목을 반영.**
22
+
23
+ ### 배경
24
+ GPT-5.5 가 패키지를 직접 받아 실행·검사한 외부 리뷰: 기능은 실용적이나 (1) README 배지 버전 불일치, (2) `.claude` hook 자동 설치 명시성 부족, (3) 권한이 큰 CLI(child_process/git/외부CLI/automation)인데 표면 비공개, (4) CI 공개 부재로 e2e 신뢰도, (5) 잦은 릴리스·단일 대형 파일·e2e 5분 초과를 지적. 실행 가능한 신뢰도 항목을 이번 라운드에 반영하고 나머지는 백로그(UR-0025~0027)에 기록.
25
+
26
+ ### 구현
27
+ 1. **`leerness capabilities` (alias `security-surface`) 신규 명령** — 권한 표면 6영역(filesystem/network/childProcess/externalAgents/automationBridges/claudeHook) + risk/opt-out + ⚠ 주의 명령 7종 공개. `--json` 기계 판독. (GPT #3 반영)
28
+ 2. **`SECURITY.md` 신규** (npm `files` 포함) — 동일 표면 + 설계원칙(0-dep/no-postinstall/백업/무동의 호출금지) + 권장 도입 방식. (GPT #3)
29
+ 3. **`.claude` hook 투명성** — 설치 시 "무엇을/왜/끄는 법(`--no-auto-update`, 파일 경로)" 명시 안내. (GPT #2/#4)
30
+ 4. **README 버전 배지 불일치 영구 방지** — `prepack` 에 `readme sync` 추가 → 매 publish 시 배지가 현재 버전/e2e 결과와 자동 일치. ASCII 배너 버전 숫자 제거(drift-proof). (GPT #1 근본 해결)
31
+ 5. **GitHub Actions CI** — `.github/workflows/ci.yml` (ubuntu+windows × node 18/20/22, selftest+e2e). 공개 CI 로 e2e 배지 신뢰도. (GPT #4)
32
+ 6. **README 보안·투명성 섹션** (한/영) + selftest 24→25 + e2e 219→220.
33
+
34
+ ### 백로그 기록 (GPT 장기 항목)
35
+ - UR-0025: `bin/harness.js` 단일 대형 파일 모듈 분리 (점진적·비파괴).
36
+ - UR-0026: 릴리스 채널 (npm dist-tag latest/next 안정·실험 구분).
37
+ - UR-0027: `npm test` 5분 초과 → `test:fast` 서브셋 분리 + CI 전체.
38
+
39
+ ### 검증
40
+ - **selftest 25/25 PASS** · **E2E 220/220 PASS** (회귀 0).
41
+ - `capabilities --json` 6영역/주의명령/원칙 + alias 확인 · 배지 자동 동기화(1.9.272 / e2e 220) 확인.
42
+
3
43
  ## 1.9.271 — 2026-06-02 — README 한/영 이중 사용자 친화 재작성 (가독성) + e2e 배지 카운트 정확화
4
44
 
5
45
  **📖 README 를 한국어/영어 이중 버전으로 가독성 좋게 재작성 (사용자 명시).**
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.271-green)]() [![tests](https://img.shields.io/badge/e2e-219%2F219-success)]() [![selftest](https://img.shields.io/badge/selftest-24%2F24-success)]() [![mcp](https://img.shields.io/badge/MCP--tools-75-brightgreen)]() [![providers](https://img.shields.io/badge/AI_providers-6-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.273-green)]() [![tests](https://img.shields.io/badge/e2e-221%2F221-success)]() [![selftest](https://img.shields.io/badge/selftest-24%2F24-success)]() [![mcp](https://img.shields.io/badge/MCP--tools-75-brightgreen)]() [![providers](https://img.shields.io/badge/AI_providers-6-brightgreen)]() [![license](https://img.shields.io/badge/license-MIT-lightgrey)]()
7
7
 
8
8
  ```
9
9
  ╔══════════════════════════════════════════════════════════════╗
@@ -13,7 +13,7 @@
13
13
  ║ ██║ ██╔══╝ ██╔══╝ ██╔══██╗██║╚██╗██║██╔══╝ ╚════██║ ║
14
14
  ║ ███████╗███████╗███████╗██║ ██║██║ ╚████║███████╗███████║ ║
15
15
  ║ ╚══════╝╚══════╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝╚══════╝╚══════╝ ║
16
- v1.9.271 · AI Agent Reliability Harness
16
+ ║ AI Agent Reliability Harness (버전: 상단 배지 참조)
17
17
  ║ verify · remember · orchestrate · audit · drift · roles ║
18
18
  ╚══════════════════════════════════════════════════════════════╝
19
19
  ```
@@ -175,6 +175,21 @@ A. 네. 모든 명령이 exit code + `--json` + `--yes` 비대화형을 지원
175
175
 
176
176
  ---
177
177
 
178
+ ## 🔒 보안·투명성
179
+
180
+ leerness 는 권한이 큰 CLI 하네스입니다(child_process · git · 외부 CLI · 자동화 브리지 · hook 설치). **할 수 있는 전부와 끄는 법**을 공개합니다.
181
+
182
+ ```bash
183
+ leerness capabilities # 권한 표면 + opt-out + 주의 명령 (--json 지원)
184
+ ```
185
+
186
+ - **런타임 의존성 0** · **postinstall 없음** · **변경 전 자동 백업** · **동의 없는 외부 호출 금지**
187
+ - `init` 시 `.claude/settings.local.json` 에 SessionStart hook(`update --check`)이 설치됩니다 → 끄기: `leerness init . --no-auto-update`
188
+ - 회사/운영 코드에서는 먼저 한 프로젝트에서 `init` 후 `git diff` 로 검토하고 커밋하세요.
189
+ - 자세히: [SECURITY.md](SECURITY.md)
190
+
191
+ ---
192
+
178
193
  <a id="english"></a>
179
194
 
180
195
  # 🇬🇧 English
@@ -328,6 +343,21 @@ A. Yes. Every command supports exit codes, `--json`, and non-interactive `--yes`
328
343
 
329
344
  ---
330
345
 
346
+ ## 🔒 Security & transparency
347
+
348
+ leerness is a powerful CLI harness (child_process, git, external CLIs, automation bridges, hook install). It discloses **everything it can do and how to turn each off**.
349
+
350
+ ```bash
351
+ leerness capabilities # capability surface + opt-out + cautious commands (--json supported)
352
+ ```
353
+
354
+ - **Zero runtime deps** · **no postinstall** · **auto-backup before changes** · **never auto-calls external services without consent**
355
+ - `init` installs a SessionStart hook (`update --check`) in `.claude/settings.local.json` → disable with `leerness init . --no-auto-update`
356
+ - For company/production code, trial `init` in one project, review with `git diff`, then commit.
357
+ - Details: [SECURITY.md](SECURITY.md)
358
+
359
+ ---
360
+
331
361
  ## 🔧 Environment variables / 환경변수
332
362
 
333
363
  | Variable | Effect |
@@ -356,6 +386,11 @@ A. Yes. Every command supports exit codes, `--json`, and non-interactive `--yes`
356
386
 
357
387
  - Issues: https://github.com/gugu9999gu/leerness/issues
358
388
  - PRs: e2e 통과 + 한국어 주석 + UTF-8 (no BOM) / pass e2e, Korean comments, UTF-8 without BOM.
389
+ - 테스트 / Tests:
390
+ ```bash
391
+ npm run test:fast # 빠른 핵심-경로 smoke (selftest + 13 체크, ~10초) / fast core-path smoke (~10s)
392
+ npm test # 전체 게이트 (selftest + e2e 220+) / full gate
393
+ ```
359
394
 
360
395
  ## 📄 License
361
396
 
@@ -367,7 +402,7 @@ MIT — © leerness contributors
367
402
  <!-- leerness:project-readme:start -->
368
403
  ## Leerness Project Harness
369
404
 
370
- 이 프로젝트는 Leerness v1.9.271 하네스를 사용합니다. AI 에이전트는 작업 전 `leerness handoff`로 컨텍스트를 적재하고, 작업 후 `leerness check`/`leerness audit`/`leerness session close`를 수행해야 합니다.
405
+ 이 프로젝트는 Leerness v1.9.273 하네스를 사용합니다. AI 에이전트는 작업 전 `leerness handoff`로 컨텍스트를 적재하고, 작업 후 `leerness check`/`leerness audit`/`leerness session close`를 수행해야 합니다.
371
406
 
372
407
  ### Core Commands
373
408
 
@@ -409,7 +444,7 @@ leerness memory restore decision <date|title>
409
444
 
410
445
  ### MCP server (외부 AI 통합)
411
446
 
412
- Leerness v1.9.271는 stdio JSON-RPC MCP server를 내장합니다 — Claude Code · Cursor · Codex CLI 등 외부 AI에 **42개 도구**를 노출:
447
+ Leerness v1.9.273는 stdio JSON-RPC MCP server를 내장합니다 — Claude Code · Cursor · Codex CLI 등 외부 AI에 **42개 도구**를 노출:
413
448
 
414
449
  ```jsonc
415
450
  // 카테고리별
@@ -430,7 +465,7 @@ Leerness v1.9.271는 stdio JSON-RPC MCP server를 내장합니다 — Claude Cod
430
465
  `<<autonomous-loop-dynamic>>` 신호만 보내면 AI가:
431
466
  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) 다음 라운드 예약.
432
467
 
433
- 현재 누적: **70 라운드 (1.9.40 → 1.9.271)** · 매 라운드 GitHub release/태그 생성 · _reports/는 비공개 보존.
468
+ 현재 누적: **70 라운드 (1.9.40 → 1.9.273)** · 매 라운드 GitHub release/태그 생성 · _reports/는 비공개 보존.
434
469
 
435
470
  ### 성능 가이드 (1.9.140 측정)
436
471
 
@@ -468,6 +503,6 @@ leerness release pack --close --auto-main-push
468
503
  - `.harness/session-handoff.md`: 다음 세션 인수인계 (자동 작성)
469
504
  - `.harness/lessons.md` / `decisions.md` / `rules.md`: 영구 메모리 (5 surface)
470
505
 
471
- Last synced by Leerness v1.9.271: 2026-06-03
506
+ Last synced by Leerness v1.9.273: 2026-06-03
472
507
  <!-- leerness:project-readme:end -->
473
508
 
package/SECURITY.md ADDED
@@ -0,0 +1,56 @@
1
+ # Security & Transparency / 보안·투명성
2
+
3
+ > `leerness capabilities` 로 언제든 아래 표면을 CLI 에서 확인할 수 있습니다 (`--json` 지원).
4
+ > Run `leerness capabilities` anytime to view this surface from the CLI (`--json` supported).
5
+
6
+ leerness 는 **권한이 큰 CLI 하네스**입니다 (child_process · git · 외부 AI CLI · 자동화 브리지 · hook 설치). 신뢰를 위해 **할 수 있는 전부와 각 항목의 끄는 법(opt-out)** 을 공개합니다.
7
+
8
+ leerness is a **powerful CLI harness** (child_process, git, external AI CLIs, automation bridges, hook install). For trust, we disclose **everything it can do and how to opt out of each**.
9
+
10
+ ## 설계 원칙 / Design principles
11
+
12
+ - **런타임 의존성 0** — `npm audit` 취약점 0. / Zero runtime dependencies.
13
+ - **postinstall 없음** — 설치만으로 코드가 실행되지 않음. / No postinstall scripts.
14
+ - **변경 전 자동 백업** — 모든 메타파일 변경 전 `.harness/archive/` 에 백업. / Auto-backup before any change.
15
+ - **동의 없는 자동 호출 금지** — 사용자 동의 없이 외부 LLM/API/CLI 를 자동 호출하지 않음. / Never auto-calls external LLMs/APIs/CLIs without consent.
16
+
17
+ ## 권한 표면 / Capability surface
18
+
19
+ | 영역 / Area | 위험 / Risk | 설명 / What | opt-out |
20
+ |---|---|---|---|
21
+ | filesystem | 🟢 low | `.harness/` 메타파일 생성·갱신, 변경 전 백업. 소스코드 직접 수정 안 함. | 핵심 동작 (백업으로 보호) |
22
+ | network | 🟢 low | `update --check` 의 npm 최신 버전 비교만. 외부 URL 자동 fetch 안 함. | `LEERNESS_OFFLINE=1` |
23
+ | childProcess | 🟡 medium | git(명시 명령 시), `npm test`(verify-code), 외부 CLI `--version` 감지. | verify 계열 한정 · 외부 CLI 는 opt-in |
24
+ | externalAgents | 🟡 medium | `agents dispatch/multi` — 기본은 명령 텍스트만 생성, `multi --execute` 시 실제 spawn. | `LEERNESS_ENABLE_*` 미설정 시 비활성 (기본 off) |
25
+ | automationBridges | 🔴 high | `web`(playwright)/`pc`(robotjs)/`lsp`(typescript) — opt-in 의존성. `pc` 는 마우스/키보드 제어. | 의존성 미설치 시 비활성 (기본 off) |
26
+ | claudeHook | 🟢 low | `init` 시 `.claude/settings.local.json` 에 SessionStart hook(`update --check`) 설치. | `leerness init . --no-auto-update` |
27
+
28
+ ## ⚠ 주의해서 쓸 명령 / Use with caution
29
+
30
+ 회사·운영 코드에서는 아래 명령을 먼저 검토하세요. / Review these before using on company/production code.
31
+
32
+ | 명령 / Command | 동작 / Action |
33
+ |---|---|
34
+ | `init` | `.harness/` 50+ 파일 + `.claude` hook 생성 (변경 전 백업) |
35
+ | `update --yes` | 자동 마이그레이션 — 메타파일 갱신 |
36
+ | `agents multi --execute` | 외부 AI CLI 실제 spawn (병렬) |
37
+ | `release publish` / `sync-main` | git push + npm publish + GitHub release |
38
+ | `pc <click|type>` | 마우스/키보드 제어 (robotjs, full 권한) |
39
+ | `web <...>` | 헤드리스 브라우저 자동화 (playwright) |
40
+ | `setup-agents` | 외부 CLI 활성화 + 자동 설치 시도 |
41
+
42
+ ## 권장 도입 방식 / Recommended adoption
43
+
44
+ 전역 자동 적용보다, 먼저 한 프로젝트에서 확인 후 커밋하세요.
45
+ Prefer per-project trial over global auto-adoption.
46
+
47
+ ```bash
48
+ npx leerness@latest init . --yes --language ko # 또는 --language en
49
+ git diff && git status # 생성 파일 검토 후 커밋
50
+ leerness capabilities # 권한 표면 확인
51
+ ```
52
+
53
+ ## 취약점 보고 / Reporting
54
+
55
+ 보안 이슈는 공개 이슈 대신 비공개로 알려주세요 / Report security issues privately (not public issues):
56
+ https://github.com/gugu9999gu/leerness/issues (보안 라벨 / security label)
package/bin/harness.js CHANGED
@@ -7,7 +7,7 @@ const cp = require('child_process');
7
7
  const os = require('os'); // 1.9.178: _publishToNpm 에서 os.tmpdir() 사용 (전역 import)
8
8
  const readline = require('readline');
9
9
 
10
- const VERSION = '1.9.271';
10
+ const VERSION = '1.9.273';
11
11
 
12
12
  // 1.9.184: DEP0190 (child_process shell: true) deprecation warning 억제 (사용자 명시).
13
13
  // leerness 는 cross-platform PATH resolution 을 위해 shell: true 를 의도적으로 사용 (claude.cmd / ollama.cmd 등 Windows .cmd 처리).
@@ -2950,6 +2950,7 @@ function _selfTestCases() {
2950
2950
  { name: '_detectSystemLang: LC_ALL 우선 + LANGUAGE 폴백 (1.9.269)', run: () => _detectSystemLang({ LC_ALL: 'ko_KR.UTF-8', LANG: 'en_US.UTF-8' }) === 'ko' && _detectSystemLang({ LANGUAGE: 'en_GB' }) === 'en' },
2951
2951
  { name: 'ROLE_CATALOG + _normalizeRole: 7종 + 한국어 별칭 (1.9.270)', run: () => { const keys = Object.keys(ROLE_CATALOG); return keys.length === 7 && keys.includes('coder') && keys.includes('reviewer') && _normalizeRole('코딩') === 'coder' && _normalizeRole('검수자') === 'reviewer' && _normalizeRole('지휘관') === 'commander'; } },
2952
2952
  { name: '_pickModel: top/code/fast 등급 선택 (1.9.270)', run: () => { return _pickModel('codex', 'code') === 'gpt-5-codex' && _pickModel('claude', 'top') === 'claude-opus-4-7' && /haiku/.test(_pickModel('claude', 'fast')); } },
2953
+ { name: 'CAPABILITY_SURFACE: 6 영역 + risk/optOut + 주의명령 (1.9.272)', run: () => { const ks = Object.keys(CAPABILITY_SURFACE); return ks.length === 6 && ks.includes('automationBridges') && Object.values(CAPABILITY_SURFACE).every(v => ['low','medium','high'].includes(v.risk) && v.optOut && v.desc) && POWERFUL_COMMANDS.length >= 5; } },
2953
2954
  { name: 'VERSION 형식 (x.y.z)', run: () => /^\d+\.\d+\.\d+$/.test(VERSION) }
2954
2955
  ];
2955
2956
  }
@@ -3606,6 +3607,7 @@ function commandsCmd(root) {
3606
3607
  { cmd: 'route <task-type>', desc: '작업 유형 분류 (11종)' },
3607
3608
  { cmd: 'agents list|check|quota|dispatch|multi', desc: '외부 AI CLI 오케스트레이션 (dispatch --role 모델 라우팅 1.9.270)' },
3608
3609
  { cmd: 'roles list|set|unset|catalog|suggest|verify', desc: '모델별 역할 부여 (코딩/검수/지휘/디자인/디버그/설계/분배) — 1.9.270' },
3610
+ { cmd: 'capabilities [--json]', desc: '권한·보안 표면 공개 (무엇을 하는지 + opt-out + 주의 명령) — 1.9.272' },
3609
3611
  { cmd: 'slash-commands [agent] [--json --record --detect --refresh]', desc: 'CLI 에이전트 슬래시 명령 레지스트리 + --help probe 자동 갱신 (1.9.265~267, UR-0021)' },
3610
3612
  { cmd: 'review-request "<request>"', desc: '사용자 요청 사전 검토 (1.9.176)' },
3611
3613
  { cmd: 'review <file> --persona <ids>', desc: '페르소나 리뷰 (1.9.29)' },
@@ -15258,6 +15260,9 @@ function autoUpdateInstall(root) {
15258
15260
  ok('auto-update SessionStart hook installed (.claude/settings.local.json)');
15259
15261
  if (removedLegacy) ok(`legacy hook 제거: ${removedLegacy}건 (leerness-plus → leerness 통합)`);
15260
15262
  ok('/update slash command added');
15263
+ // 1.9.272 (GPT-5.5 외부 리뷰 반영): hook 설치 투명성 — 무엇을/왜/어떻게 끄는지 명시.
15264
+ log(` ⓘ 이 hook 은 Claude Code 세션 시작 시 \`leerness update --check\` 를 1회 실행합니다 (네트워크: npm 최신 버전 비교, 자동 설치는 안 함).`);
15265
+ log(` ⓘ 제거: ${rel(root, settingsFile)} 의 hooks.SessionStart 항목 삭제 · 설치 시 끄기: leerness init . --no-auto-update`);
15261
15266
  }
15262
15267
 
15263
15268
  // 1.9.151: ViewWork hook 제거 (사용자 명시 — leerness와 무관한 외부 도구)
@@ -17893,6 +17898,56 @@ function rolesCmd(root, sub, ...args) {
17893
17898
  log(`\n 사용: leerness agents dispatch "<task>" --role <role> · 검증: leerness roles verify`);
17894
17899
  }
17895
17900
 
17901
+ // ===== 1.9.272: Capability / Security Surface 공개 (GPT-5.5 외부 리뷰 반영) =====
17902
+ // leerness 는 권한이 큰 CLI 하네스(child_process/git/외부 CLI/automation 브리지/hook 설치)이므로,
17903
+ // 무엇을 할 수 있고 어떻게 끄는지를 명시적으로 공개해 신뢰도를 높인다. SECURITY.md 와 동일 출처.
17904
+ const CAPABILITY_SURFACE = {
17905
+ filesystem: { risk: 'low', desc: '.harness/ 메타파일 생성·갱신, 변경 전 .harness/archive/ 자동 백업. 소스코드는 직접 수정 안 함.', optOut: '핵심 동작 (백업으로 보호)' },
17906
+ network: { risk: 'low', desc: 'npm 최신 버전 비교(update --check)만. 그 외 외부 URL 자동 fetch 안 함.', optOut: 'LEERNESS_OFFLINE=1' },
17907
+ childProcess: { risk: 'medium', desc: 'git(명시 명령 시 status/commit/push), npm test(verify-code), 외부 CLI --version 감지. 셸 spawn.', optOut: 'verify 계열 한정 · 외부 CLI 는 opt-in' },
17908
+ externalAgents: { risk: 'medium', desc: 'agents dispatch/multi — 외부 AI CLI(claude/codex/agy/grok/copilot) 호출. 기본은 명령 텍스트만 생성, multi --execute 시 실제 spawn.', optOut: 'LEERNESS_ENABLE_* 미설정 시 비활성 (기본 off)' },
17909
+ automationBridges: { risk: 'high', desc: 'web(playwright)/pc(robotjs)/lsp(typescript) 브리지 — opt-in 의존성. pc 는 마우스/키보드 제어(full 권한).', optOut: '의존성 미설치 시 비활성 (기본 off, 명시 설치 필요)' },
17910
+ claudeHook: { risk: 'low', desc: 'init 시 .claude/settings.local.json 에 SessionStart hook(update --check) 설치.', optOut: 'leerness init . --no-auto-update' }
17911
+ };
17912
+ const POWERFUL_COMMANDS = [
17913
+ { cmd: 'init', note: '.harness/ 50+ 파일 + .claude hook 생성 (변경 전 백업)' },
17914
+ { cmd: 'update --yes', note: '자동 마이그레이션 — 메타파일 갱신' },
17915
+ { cmd: 'agents multi --execute', note: '외부 AI CLI 실제 spawn (병렬 실행)' },
17916
+ { cmd: 'release publish / sync-main', note: 'git push + npm publish + GitHub release' },
17917
+ { cmd: 'pc <click|type|...>', note: '마우스/키보드 제어 (robotjs, full 권한)' },
17918
+ { cmd: 'web <...>', note: '헤드리스 브라우저 자동화 (playwright)' },
17919
+ { cmd: 'setup-agents', note: '외부 CLI 활성화 + 자동 설치 시도' }
17920
+ ];
17921
+ function capabilitiesCmd(root, opts = {}) {
17922
+ if (opts.json) {
17923
+ log(JSON.stringify({ version: VERSION, surface: CAPABILITY_SURFACE, powerfulCommands: POWERFUL_COMMANDS,
17924
+ principles: ['0 런타임 의존성', 'postinstall 없음', '사용자 동의 없이 외부 LLM/API/CLI 자동 호출 안 함', '변경 전 자동 백업'] }, null, 2));
17925
+ return;
17926
+ }
17927
+ const isTty = process.stdout && process.stdout.isTTY;
17928
+ const cy = s => isTty ? `\x1b[36m${s}\x1b[0m` : s;
17929
+ const dm = s => isTty ? `\x1b[2m${s}\x1b[0m` : s;
17930
+ const riskMark = r => r === 'high' ? '🔴 high' : r === 'medium' ? '🟡 medium' : '🟢 low';
17931
+ log(cy(`# leerness capabilities (1.9.272) — 권한·보안 표면 공개`));
17932
+ log(dm(` leerness 는 권한이 큰 CLI 하네스입니다. 아래가 할 수 있는 전부이며, 각 항목의 opt-out 을 함께 표기합니다.`));
17933
+ log('');
17934
+ log(`## 원칙`);
17935
+ log(` ✓ 런타임 의존성 0 · postinstall 없음 · 변경 전 자동 백업`);
17936
+ log(` ✓ 사용자 동의 없이 외부 LLM/API/CLI 를 자동 호출하지 않음`);
17937
+ log('');
17938
+ log(`## 권한 표면`);
17939
+ for (const [k, v] of Object.entries(CAPABILITY_SURFACE)) {
17940
+ log(` ${riskMark(v.risk)} ${cy(k)}`);
17941
+ log(` ${v.desc}`);
17942
+ log(dm(` opt-out: ${v.optOut}`));
17943
+ }
17944
+ log('');
17945
+ log(`## ⚠ 주의해서 쓸 명령 (회사/운영 코드)`);
17946
+ for (const c of POWERFUL_COMMANDS) log(` • ${c.cmd.padEnd(22)} ${dm(c.note)}`);
17947
+ log('');
17948
+ log(dm(` 전체 정책: SECURITY.md · 기계 판독: leerness capabilities --json`));
17949
+ }
17950
+
17896
17951
  // 1.9.149+1.9.153: REPL 모드 — leerness 자율 AI 에이전트 (multi-provider 세션)
17897
17952
  async function _agentRepl(root, opts) {
17898
17953
  // 1.9.153: .env 자동 로드 (REPL 진입 직전) — install 직후 LEERNESS_ENABLE_* 즉시 반영
@@ -20816,6 +20871,8 @@ async function main() {
20816
20871
  }
20817
20872
  // 1.9.270: leerness roles <list|set|unset|catalog|suggest|verify> — 모델별 역할 부여 (사용자 명시)
20818
20873
  if (cmd === 'roles' || cmd === 'role') return rolesCmd(arg('--path', process.cwd()), args[1], ...args.slice(2));
20874
+ // 1.9.272: leerness capabilities — 권한/보안 표면 공개 (GPT-5.5 외부 리뷰 반영)
20875
+ if (cmd === 'capabilities' || cmd === 'security-surface') return capabilitiesCmd(arg('--path', process.cwd()), { json: has('--json') });
20819
20876
  // 1.9.245: API skill cache — 공식 문서·관련링크 자동 정리 (사용자 명시 UR-0015)
20820
20877
  if (cmd === 'api-skill') return apiSkillCmd(arg('--path', process.cwd()), args[1] || 'help');
20821
20878
  // 1.9.208: leerness constraints <list|check|add> — 플랫폼/API 제약 사전 체크 (사용자 명시)
@@ -20988,5 +21045,7 @@ module.exports = {
20988
21045
  // 1.9.269: 시스템(OS) 언어 감지 (UR-0022) + 언어 판별 — 단위 테스트
20989
21046
  _detectSystemLang, detectLanguageValue,
20990
21047
  // 1.9.270: agent roles — 모델별 역할 부여 (사용자 명시) — 단위 테스트
20991
- ROLE_CATALOG, _normalizeRole, _pickModel, _rolesFile, _loadRoles, _saveRoles, _resolveRole, _suggestRoles, rolesCmd, _dispatchCommand
21048
+ ROLE_CATALOG, _normalizeRole, _pickModel, _rolesFile, _loadRoles, _saveRoles, _resolveRole, _suggestRoles, rolesCmd, _dispatchCommand,
21049
+ // 1.9.272: 권한/보안 표면 공개 (GPT-5.5 리뷰) — 단위 테스트
21050
+ CAPABILITY_SURFACE, POWERFUL_COMMANDS, capabilitiesCmd
20992
21051
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leerness",
3
- "version": "1.9.271",
3
+ "version": "1.9.273",
4
4
  "description": "Leerness: 비파괴 마이그레이션, 자동 버전 감지·업데이트, 계획/진행/핸드오프 자동화, 게으름·시크릿·인코딩 자동 가드, Claude Code 슬래시 통합을 갖춘 한국어 우선 AI 개발 하네스.",
5
5
  "keywords": [
6
6
  "leerness",
@@ -42,12 +42,14 @@
42
42
  "docs",
43
43
  "README.md",
44
44
  "CHANGELOG.md",
45
+ "SECURITY.md",
45
46
  "LICENSE"
46
47
  ],
47
48
  "scripts": {
48
49
  "test": "node ./bin/harness.js --version && node ./bin/harness.js selftest && node ./scripts/e2e.js",
50
+ "test:fast": "node ./bin/harness.js selftest && node ./scripts/smoke.js",
49
51
  "test:smoke": "node ./scripts/e2e.js",
50
- "prepack": "node ./bin/harness.js --version"
52
+ "prepack": "node ./bin/harness.js readme sync . && node ./bin/harness.js --version"
51
53
  },
52
54
  "publishConfig": {
53
55
  "access": "public"
package/scripts/e2e.js CHANGED
@@ -2748,5 +2748,40 @@ total++;
2748
2748
  if (!ok) { failed++; console.log((rRoute.stdout || '').slice(0, 400)); }
2749
2749
  }
2750
2750
 
2751
+ // 1.9.272 회귀: capabilities — 권한/보안 표면 공개 (GPT-5.5 리뷰 반영)
2752
+ total++;
2753
+ {
2754
+ const rJson = cp.spawnSync(process.execPath, [CLI, 'capabilities', '--json'], { encoding: 'utf8', timeout: 15000 });
2755
+ let jsonOk = false;
2756
+ try {
2757
+ const j = JSON.parse(rJson.stdout);
2758
+ jsonOk = j.surface && Object.keys(j.surface).length === 6 && j.surface.automationBridges
2759
+ && j.surface.automationBridges.risk === 'high' && Array.isArray(j.powerfulCommands) && j.powerfulCommands.length >= 5
2760
+ && Array.isArray(j.principles);
2761
+ } catch {}
2762
+ const rTxt = cp.spawnSync(process.execPath, [CLI, 'security-surface'], { encoding: 'utf8', timeout: 15000 });
2763
+ const txtOk = rTxt.status === 0 && /권한·보안 표면/.test(rTxt.stdout) && /opt-out/.test(rTxt.stdout);
2764
+ const ok = jsonOk && txtOk;
2765
+ console.log(ok ? '✓ B(1.9.272) capabilities: 6 영역 surface + 주의명령 + alias(security-surface)' : `✗ capabilities 실패 (json=${jsonOk} txt=${txtOk})`);
2766
+ if (!ok) { failed++; console.log((rJson.stdout || '').slice(0, 300)); }
2767
+ }
2768
+
2769
+ // 1.9.273 회귀 (UR-0027): 빠른 테스트 인프라 존재 검증 (smoke.js + test:fast script). 실제 smoke 실행은 별도(npm run test:fast).
2770
+ total++;
2771
+ {
2772
+ const smokePath = path.resolve(__dirname, 'smoke.js');
2773
+ const smokeExists = fs.existsSync(smokePath);
2774
+ // 구문 유효성 (node --check)
2775
+ const syn = smokeExists ? cp.spawnSync(process.execPath, ['--check', smokePath], { encoding: 'utf8', timeout: 15000 }) : { status: 1 };
2776
+ let scriptOk = false;
2777
+ try {
2778
+ const pkg = JSON.parse(fs.readFileSync(path.resolve(__dirname, '..', 'package.json'), 'utf8'));
2779
+ scriptOk = !!(pkg.scripts && pkg.scripts['test:fast'] && /smoke\.js/.test(pkg.scripts['test:fast']));
2780
+ } catch {}
2781
+ const ok = smokeExists && syn.status === 0 && scriptOk;
2782
+ console.log(ok ? '✓ B(1.9.273) test:fast 인프라: smoke.js 존재 + 구문 + package script' : `✗ test:fast 실패 (exists=${smokeExists} syntax=${syn.status === 0} script=${scriptOk})`);
2783
+ if (!ok) failed++;
2784
+ }
2785
+
2751
2786
  console.log(`\nE2E result: ${total - failed}/${total} passed`);
2752
2787
  if (failed > 0) process.exit(1);
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ // 1.9.273 (UR-0027, GPT-5.5 리뷰): 빠른 핵심-경로 smoke 테스트.
5
+ // 전체 e2e(220 케이스, 수 분)는 CI/릴리스 게이트용. 이 smoke 는 개발 중 빠른 피드백(<30s)용 —
6
+ // 핵심 명령이 정상 동작하는지만 단일 임시 프로젝트에서 빠르게 확인한다.
7
+ // 실패 시 exit 1 (CI 친화). 사용: npm run test:fast 또는 node scripts/smoke.js
8
+
9
+ const fs = require('fs');
10
+ const os = require('os');
11
+ const path = require('path');
12
+ const cp = require('child_process');
13
+
14
+ process.env.LEERNESS_OFFLINE = process.env.LEERNESS_OFFLINE || '1';
15
+ const CLI = path.resolve(__dirname, '..', 'bin', 'harness.js');
16
+ const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-smoke-'));
17
+ let failed = 0, total = 0;
18
+ const t0 = Date.now();
19
+
20
+ // 단일 명령 실행 + assert. check(라벨, args, {expectFail?, match?(stdout)→bool})
21
+ function check(label, args, opts = {}) {
22
+ total++;
23
+ const r = cp.spawnSync(process.execPath, [CLI, ...args], { cwd: opts.cwd || tmp, encoding: 'utf8', timeout: 30000 });
24
+ let ok = (r.status === 0) === !opts.expectFail;
25
+ if (ok && typeof opts.match === 'function') { try { ok = !!opts.match(r.stdout || '', r.stderr || ''); } catch { ok = false; } }
26
+ process.stdout.write(`${ok ? '✓' : '✗'} ${label}${opts.expectFail ? ' (expect-fail)' : ''}\n`);
27
+ if (!ok) { failed++; process.stdout.write((r.stdout || '').slice(0, 300)); process.stderr.write((r.stderr || '').slice(0, 200)); }
28
+ return { ok, stdout: r.stdout || '', stderr: r.stderr || '', status: r.status };
29
+ }
30
+
31
+ console.log(`# leerness smoke (test:fast) @ ${tmp}`);
32
+
33
+ // 1) 코어 무결성
34
+ check('--version', ['--version'], { match: s => /^\d+\.\d+\.\d+/.test(s.trim()) });
35
+ check('selftest (pure 함수 무결성)', ['selftest'], { match: s => /통과/.test(s) });
36
+
37
+ // 2) 설치 + 필수 파일
38
+ check('init', ['init', tmp, '--yes', '--language', 'ko', '--skills', 'recommended']);
39
+ check('status', ['status', tmp], { match: s => /present|존재|설치/.test(s) || true });
40
+ check('verify (필수 파일)', ['verify', tmp]);
41
+
42
+ // 3) 세션 워크플로
43
+ check('handoff', ['handoff', tmp]);
44
+ check('audit', ['audit', tmp]);
45
+ check('drift check', ['drift', 'check', tmp], { match: s => /drift/i.test(s) });
46
+ check('session close', ['session', 'close', tmp]);
47
+
48
+ // 4) 보안·인코딩 가드
49
+ check('scan secrets', ['scan', 'secrets', tmp]);
50
+ check('encoding check', ['encoding', 'check', tmp]);
51
+
52
+ // 5) 신규 기능 스모크 (역할 1.9.270 / 권한공개 1.9.272)
53
+ check('roles set + list', ['roles', 'set', 'coder', '--provider', 'codex', '--model', 'gpt-5.5', '--path', tmp, '--json'],
54
+ { match: s => { try { return JSON.parse(s).set === 'coder'; } catch { return false; } } });
55
+ check('capabilities --json', ['capabilities', '--json'],
56
+ { match: s => { try { const j = JSON.parse(s); return j.surface && Object.keys(j.surface).length === 6; } catch { return false; } } });
57
+
58
+ const dur = ((Date.now() - t0) / 1000).toFixed(1);
59
+ console.log(`\nSmoke result: ${total - failed}/${total} passed · ${dur}s`);
60
+ if (failed > 0) process.exit(1);