leerness 1.9.159 → 1.9.161

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,93 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.9.161 — 2026-05-20
4
+
5
+ **REPL Memory Slash 4종 추가 — Memory Surface 즉시 조회 (1.9.150 slash 패턴 확장).**
6
+
7
+ 자율 모드 91 라운드. REPL 안에서 leerness 메모리 (lessons / brainstorm / tasks / plan) 즉시 조회 가능 → 대화 중 컨텍스트 회수 마찰 0.
8
+
9
+ ### Added — REPL `:lessons` / `:brainstorm` / `:tasks` / `:plan`
10
+ - `:lessons [query]` — `leerness lessons --query <q>` 또는 인자 없이 전체 회수
11
+ - `:brainstorm <topic>` — `leerness brainstorm "topic"` (키워드 필수, 누락 시 안내)
12
+ - `:tasks` — `leerness task list` (현재 task 상태)
13
+ - `:plan` — `leerness plan show` (현재 milestone)
14
+
15
+ ### 동작 방식
16
+ - 1.9.150 slash 패턴 재사용 — `runCommandSafe` 경유 (sandbox 자동 적용)
17
+ - `_recordRun` observability — `kind: 'agent_repl_slash'` 자동 기록
18
+ - 출력 30줄로 제한 (REPL 화면 보호)
19
+ - 60초 timeout
20
+
21
+ ### REPL Slash 명령 카탈로그 갱신
22
+ | 1.9.150 (검수) | 1.9.161 (메모리) |
23
+ |---|---|
24
+ | `:verify` | `:lessons` |
25
+ | `:audit` | `:brainstorm` |
26
+ | `:handoff` | `:tasks` |
27
+ | `:health` | `:plan` |
28
+
29
+ ### Use Cases
30
+ - 메인 에이전트가 새 task 시작 전 `:lessons "auth"` 로 과거 실수 회수
31
+ - `:brainstorm "deployment"` 로 관련 task-log / decisions / lessons 통합 검색
32
+ - `:tasks` 로 진행 중 task 즉시 확인 후 :provider claude 로 sub-agent 분배
33
+ - `:plan` 으로 현재 milestone 진행률 확인
34
+
35
+ ### Verified
36
+ - e2e 217/217 ✓
37
+ - stress-v106: 13/13 (handleMeta 분기 4종 + REPL 시작 배너/help 안내 2종 + 누적 회귀 7종)
38
+ - VERSION = 1.9.161 / autonomous-rounds = 91
39
+
40
+ ---
41
+
42
+ ## 1.9.160 — 2026-05-20
43
+
44
+ **🎉 자율 모드 90 라운드 마일스톤 + `provider sync` (외부 catalog 자동 동기화).**
45
+
46
+ ### Added — `leerness provider sync <url>` (1.9.157 Provider Registry 확장)
47
+ - 외부 URL 에서 provider catalog 자동 가져오기 — OpenRouter llms.txt / GitHub raw JSON / 자체 호스트
48
+ - **의존성 0 유지** — Node built-in `https.get` 사용
49
+ - 2가지 형식 자동 인식:
50
+ 1. **JSON array** — `[{ id, bin, envFlag?, desc?, ... }, ...]` 또는 `{ providers: [...] }`
51
+ 2. **llms.txt** — 한 줄당 `"id|bin|desc"` (`#` 주석 무시)
52
+ - `--dry-run` — 파일 쓰기 스킵하고 결과만 미리보기
53
+ - 보안:
54
+ - `LEERNESS_OFFLINE=1` 시 거부
55
+ - http:// 또는 https:// 강제
56
+ - response 1MB 제한
57
+ - timeout 15s
58
+ - 3xx redirect 자동 follow (1단계)
59
+ - id 정규식 검증 (영문자/숫자/_- 만 — sync 도 add 와 동일)
60
+ - 같은 id 두 번 → 갱신 (덮어쓰기) / 잘못된 id → skip
61
+
62
+ ### Use Case — OpenRouter / Bedrock 일괄 흡수
63
+ ```bash
64
+ # 가상의 leerness-providers catalog
65
+ leerness provider sync https://raw.githubusercontent.com/example/leerness-providers/main/openrouter.json
66
+
67
+ # 또는 llms.txt 형식
68
+ leerness provider sync https://example.com/providers.txt --dry-run
69
+ ```
70
+
71
+ ### Verified — 🎉 90 라운드 마일스톤 보고서
72
+ - `_reports/1.9.160-90-rounds-milestone.md` (비공개)
73
+ - 1.9.140 ~ 1.9.160 — 21 라운드 진화 타임라인 (사용자 명시 11 / 자율 후속 10)
74
+ - MCP 도구 진화: 8 → 30 🎉 → 40 🎉 → 47 → 48 → **50 🎉**
75
+ - **21 라운드 연속 `main` 자동 sync** 안정성
76
+ - 5능력 매트릭스 갱신: 55% → **65%**
77
+
78
+ ### Pending — 다음 10 라운드 전망 (1.9.161 ~ 1.9.170)
79
+ 1. LSP 어댑터 MVP (TypeScript) — 5능력 #2
80
+ 2. playwright/computer-use bridge — 5능력 #4
81
+ 3. i18n + 영어 docs 토글
82
+ 4. REPL slash 추가 (`:lessons`, `:brainstorm`)
83
+
84
+ ### Verified
85
+ - e2e 217/217 ✓
86
+ - stress-v105: 20/20 (provider sync 10종 + 마일스톤 보고서 3종 + 누적 회귀 7종) 🎉 **90 라운드 마일스톤**
87
+ - VERSION = 1.9.160 / autonomous-rounds = 90
88
+
89
+ ---
90
+
3
91
  ## 1.9.159 — 2026-05-20
4
92
 
5
93
  **🎉 MCP 50 도구 마일스톤 — Provider Registry CRUD MCP 완성 (list/add/remove).**
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.159-green)]() [![tests](https://img.shields.io/badge/e2e-217%2F217-success)]() [![stress](https://img.shields.io/badge/stress--v104-16%2F16-success)]() [![mcp](https://img.shields.io/badge/MCP--tools-50%20%F0%9F%8E%89-brightgreen)]() [![rounds](https://img.shields.io/badge/autonomous--rounds-89-blueviolet)]() [![main-push](https://img.shields.io/badge/release--main--push-auto-success)]() [![provider-crud](https://img.shields.io/badge/provider_registry-CRUD_MCP_complete-success)]() [![multi-execute](https://img.shields.io/badge/agents_multi-real_spawn%2Bconsensus-success)]() [![sandbox](https://img.shields.io/badge/runCommandSafe-cwd_jail%2Benv_scrub-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.161-green)]() [![tests](https://img.shields.io/badge/e2e-217%2F217-success)]() [![stress](https://img.shields.io/badge/stress--v106-13%2F13-success)]() [![mcp](https://img.shields.io/badge/MCP--tools-50-brightgreen)]() [![rounds](https://img.shields.io/badge/autonomous--rounds-91-blueviolet)]() [![main-push](https://img.shields.io/badge/release--main--push-22_rounds-success)]() [![repl-slash](https://img.shields.io/badge/REPL_slash-8_commands-success)]() [![provider-crud](https://img.shields.io/badge/provider_registry-CRUD%2Bsync-success)]() [![sandbox](https://img.shields.io/badge/runCommandSafe-cwd_jail%2Benv_scrub-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.159 AI Agent Reliability Harness + Sandbox ║
15
+ ║ v1.9.161 AI Agent Reliability Harness + Sandbox ║
16
16
  ║ verify · remember · orchestrate · audit · sandbox · drift ║
17
17
  ╚══════════════════════════════════════════════════════════════╝
18
18
  ```
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.159';
9
+ const VERSION = '1.9.161';
10
10
  const MARK = '<!-- leerness:managed -->';
11
11
  const README_START = '<!-- leerness:project-readme:start -->';
12
12
  const README_END = '<!-- leerness:project-readme:end -->';
@@ -4694,7 +4694,93 @@ function providerCmd(root, sub, ...args) {
4694
4694
  ok(`provider 제거: ${id}`);
4695
4695
  return;
4696
4696
  }
4697
- fail(`알 없는 sub: ${sub} (list / add / remove)`);
4697
+ if (sub === 'sync') {
4698
+ // 1.9.160: 외부 catalog URL 에서 provider 자동 등록 (의존성 0 — Node built-in https)
4699
+ // 형식 1: llms.txt — 한 줄당 "id|bin|desc" (예: "openrouter|openrouter-cli|OpenRouter 200+ models")
4700
+ // 형식 2: JSON [{ id, bin, envFlag?, desc?, installHint? }, ...]
4701
+ // 보안: LEERNESS_OFFLINE=1 시 거부
4702
+ const url = (args[0] || arg('--url', '')).trim();
4703
+ if (!url) return fail('provider sync <url> 필요 (예: https://raw.githubusercontent.com/.../providers.json 또는 llms.txt)');
4704
+ if (process.env.LEERNESS_OFFLINE === '1') return fail('LEERNESS_OFFLINE=1 — 외부 fetch 거부 (보안 정책)');
4705
+ if (!/^https?:\/\//.test(url)) return fail(`URL 형식 오류: ${url} (http:// 또는 https://)`);
4706
+ const dryRun = has('--dry-run');
4707
+ return (async () => {
4708
+ const lib = url.startsWith('https:') ? require('https') : require('http');
4709
+ const fetchUrl = (u) => new Promise((resolve) => {
4710
+ try {
4711
+ const req = lib.get(u, { timeout: 15000, headers: { 'User-Agent': `leerness/${VERSION}` } }, (res) => {
4712
+ // redirect handling (3xx)
4713
+ if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
4714
+ return resolve(fetchUrl(res.headers.location));
4715
+ }
4716
+ if (res.statusCode !== 200) return resolve({ ok: false, error: `HTTP ${res.statusCode}` });
4717
+ let data = '';
4718
+ res.on('data', c => { data += c; if (data.length > 1024 * 1024) { req.destroy(); resolve({ ok: false, error: 'response > 1MB' }); } });
4719
+ res.on('end', () => resolve({ ok: true, body: data, contentType: res.headers['content-type'] || '' }));
4720
+ });
4721
+ req.on('error', e => resolve({ ok: false, error: e.message }));
4722
+ req.on('timeout', () => { req.destroy(); resolve({ ok: false, error: 'timeout' }); });
4723
+ } catch (e) { resolve({ ok: false, error: e.message }); }
4724
+ });
4725
+ log(`# leerness provider sync (1.9.160)`);
4726
+ log(`URL: ${url}`);
4727
+ const r = await fetchUrl(url);
4728
+ if (!r.ok) { fail(`fetch 실패: ${r.error}`); process.exitCode = 1; return; }
4729
+ // 파싱: JSON 우선 → llms.txt fallback
4730
+ let entries = null;
4731
+ try {
4732
+ const j = JSON.parse(r.body);
4733
+ if (Array.isArray(j)) entries = j;
4734
+ else if (Array.isArray(j.providers)) entries = j.providers;
4735
+ } catch {}
4736
+ if (!entries) {
4737
+ // llms.txt 형식 — "id|bin|desc" 한 줄당
4738
+ entries = r.body.split('\n')
4739
+ .map(l => l.trim())
4740
+ .filter(l => l && !l.startsWith('#'))
4741
+ .map(l => {
4742
+ const parts = l.split('|').map(s => s.trim());
4743
+ if (parts.length < 2) return null;
4744
+ return { id: parts[0], bin: parts[1], desc: parts[2] || `(synced from ${url})` };
4745
+ })
4746
+ .filter(Boolean);
4747
+ }
4748
+ if (!entries.length) { fail(`URL 응답에서 provider 추출 실패 (JSON array 또는 llms.txt "id|bin|desc" 형식 기대)`); process.exitCode = 1; return; }
4749
+ log(`발견 ${entries.length}개 후보`);
4750
+ log('');
4751
+ const userList = _readUserProviders(root);
4752
+ const existingIds = new Set(userList.map(u => u.id));
4753
+ const validIdRegex = /^[a-z][a-z0-9_-]*$/i;
4754
+ let added = 0, updated = 0, skipped = 0;
4755
+ for (const e of entries) {
4756
+ if (!e.id || !e.bin || !validIdRegex.test(e.id)) { skipped++; continue; }
4757
+ const entry = {
4758
+ id: e.id,
4759
+ bin: e.bin,
4760
+ envFlag: e.envFlag || `LEERNESS_ENABLE_${String(e.id).toUpperCase()}`,
4761
+ versionArgs: Array.isArray(e.versionArgs) ? e.versionArgs : (typeof e.versionArgs === 'string' ? e.versionArgs.split(/\s+/) : ['--version']),
4762
+ desc: e.desc || `(synced) ${e.id}`,
4763
+ installHint: e.installHint || ''
4764
+ };
4765
+ if (existingIds.has(e.id)) {
4766
+ const idx = userList.findIndex(u => u.id === e.id);
4767
+ if (!dryRun) userList[idx] = entry;
4768
+ updated++;
4769
+ log(` ↺ ${e.id} (갱신)`);
4770
+ } else {
4771
+ if (!dryRun) userList.push(entry);
4772
+ existingIds.add(e.id);
4773
+ added++;
4774
+ log(` + ${e.id} (신규)`);
4775
+ }
4776
+ }
4777
+ if (!dryRun) _writeUserProviders(root, userList);
4778
+ log('');
4779
+ log(`✓ sync 완료: 신규 ${added} · 갱신 ${updated} · 무시 ${skipped}${dryRun ? ' (--dry-run)' : ''}`);
4780
+ if (!dryRun) log(` .env 에 LEERNESS_ENABLE_<X>=1 설정 후 \`leerness agents list\` 로 확인`);
4781
+ })();
4782
+ }
4783
+ fail(`알 수 없는 sub: ${sub} (list / add / remove / sync)`);
4698
4784
  }
4699
4785
 
4700
4786
  // 1.9.36: 작업 키워드 분석으로 최적 CLI 추천
@@ -10671,6 +10757,7 @@ async function _agentRepl(root, opts) {
10671
10757
  log('');
10672
10758
  log(C.dim(' 메타 명령: :help | :model <m> | :role <r> | :provider <p> | :status | :clear | :save | :history | :quit'));
10673
10759
  log(C.dim(' Slash 명령 (1.9.150): :verify | :audit | :handoff | :health'));
10760
+ log(C.dim(' Memory Slash (1.9.161): :lessons | :brainstorm <topic> | :tasks | :plan'));
10674
10761
  log(C.dim(` 현재 — provider=${state.provider} model=${state.model || '(기본)'} role=${state.role} permissions=${_readPermissions(root).mode}`));
10675
10762
  // 1.9.155: REPL 진입 시 handoff 컨텍스트 자동 노출 (UX 개선 — 사용자가 매번 :handoff 안 해도 컨텍스트 인지)
10676
10763
  try {
@@ -10716,6 +10803,11 @@ async function _agentRepl(root, opts) {
10716
10803
  log(' :audit — leerness audit (보안 + drift + lazy)');
10717
10804
  log(' :handoff — leerness handoff --quiet (현재 컨텍스트 요약)');
10718
10805
  log(' :health — leerness health --json (종합 헬스 체크)');
10806
+ log(C.bold('\n Memory Slash (1.9.161) — Memory Surface 즉시 조회:'));
10807
+ log(' :lessons [query] — leerness lessons (과거 결정/실수 회수)');
10808
+ log(' :brainstorm <topic> — leerness brainstorm "topic" (관련 컨텍스트 회수)');
10809
+ log(' :tasks — leerness task list (현재 task 상태)');
10810
+ log(' :plan — leerness plan show (현재 milestone)');
10719
10811
  return false;
10720
10812
  }
10721
10813
  if (op === 'model') {
@@ -10813,13 +10905,26 @@ async function _agentRepl(root, opts) {
10813
10905
  return false;
10814
10906
  }
10815
10907
  // 1.9.150: leerness 내부 명령 slash-commands — :verify / :audit / :handoff / :health
10816
- if (op === 'verify' || op === 'audit' || op === 'handoff' || op === 'health') {
10908
+ // 1.9.161: Memory Surface 조회 slash 추가 :lessons / :brainstorm / :tasks / :plan
10909
+ if (op === 'verify' || op === 'audit' || op === 'handoff' || op === 'health'
10910
+ || op === 'lessons' || op === 'brainstorm' || op === 'tasks' || op === 'plan') {
10911
+ const query = rest.join(' ').trim();
10817
10912
  const subArgs = {
10818
10913
  verify: ['verify-code', root],
10819
10914
  audit: ['audit', root],
10820
10915
  handoff: ['handoff', root, '--quiet', '--no-drift-check'],
10821
- health: ['health', root, '--json']
10916
+ health: ['health', root, '--json'],
10917
+ // 1.9.161 Memory Surface slash 4종
10918
+ lessons: query ? ['lessons', '--query', query, '--path', root] : ['lessons', '--path', root],
10919
+ brainstorm: query ? ['brainstorm', query, '--path', root] : ['brainstorm', '--path', root],
10920
+ tasks: ['task', 'list', '--path', root],
10921
+ plan: ['plan', 'show', '--path', root]
10822
10922
  }[op];
10923
+ // 인자 필요한데 누락 시 안내 (brainstorm 은 인자 필수)
10924
+ if (op === 'brainstorm' && !query) {
10925
+ log(C.yel(` ⚠ :brainstorm 은 키워드 필요 — 예: :brainstorm "auth bug"`));
10926
+ return false;
10927
+ }
10823
10928
  log(C.dim(` → leerness ${subArgs.join(' ')}`));
10824
10929
  const t0 = Date.now();
10825
10930
  const r = runCommandSafe(process.execPath, [__filename, ...subArgs], {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leerness",
3
- "version": "1.9.159",
3
+ "version": "1.9.161",
4
4
  "description": "Leerness: 비파괴 마이그레이션, 자동 버전 감지·업데이트, 계획/진행/핸드오프 자동화, 게으름·시크릿·인코딩 자동 가드, Claude Code 슬래시 통합을 갖춘 한국어 우선 AI 개발 하네스.",
5
5
  "keywords": [
6
6
  "leerness",