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 +88 -0
- package/README.md +2 -2
- package/bin/harness.js +109 -4
- package/package.json +1 -1
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
|
-
[](https://www.npmjs.com/package/leerness) [](https://www.npmjs.com/package/leerness) []() []() []() []() []() []() []() []() []() []()
|
|
6
6
|
|
|
7
7
|
```
|
|
8
8
|
╔══════════════════════════════════════════════════════════════╗
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
║ ██║ ██╔══╝ ██╔══╝ ██╔══██╗██║╚██╗██║██╔══╝ ╚════██║ ║
|
|
13
13
|
║ ███████╗███████╗███████╗██║ ██║██║ ╚████║███████╗███████║ ║
|
|
14
14
|
║ ╚══════╝╚══════╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝╚══════╝╚══════╝ ║
|
|
15
|
-
║ v1.9.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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], {
|