leerness 1.9.156 β†’ 1.9.158

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.158 β€” 2026-05-20
4
+
5
+ **πŸŽ‰ MCP 48번째 도ꡬ `leerness_provider_list` β€” Provider Registry μ™ΈλΆ€ AI λ…ΈμΆœ λ§ˆμΌμŠ€ν†€.**
6
+
7
+ 자율 λͺ¨λ“œ 88 λΌμš΄λ“œ. 1.9.157 Provider Registry CLI 의 μžμ—°μŠ€λŸ¬μš΄ 후속 β€” MCP λ…ΈμΆœ.
8
+
9
+ ### Added β€” MCP `leerness_provider_list` (48번째 도ꡬ)
10
+ - μ™ΈλΆ€ AI (Claude / Codex / Gemini λ“±) κ°€ MCP 톡해 λ“±λ‘λœ provider 전체 회수 κ°€λŠ₯
11
+ - 응닡 JSON: `{ total, builtin, user, providers: [{ id, bin, envFlag, source, desc }] }`
12
+ - 빌트인 5μ’… (claude/codex/gemini/copilot/ollama) + `.harness/providers.json` μ‚¬μš©μž μ •μ˜ 톡합
13
+ - μ™ΈλΆ€ AI ν™œμš© 사둀:
14
+ - 메인 μ—μ΄μ „νŠΈκ°€ sub-agent λΆ„λ°° μ „ μ‚¬μš© κ°€λŠ₯ν•œ provider 확인
15
+ - OpenRouter/Bedrock λ“± λ“±λ‘λ˜μ–΄ 있으면 μžλ™ 발견
16
+ - sub-agent κ°€ "λ‚΄κ°€ μ‚¬μš© κ°€λŠ₯ν•œ 도ꡬ가 무엇인지" 자기-점검
17
+
18
+ ### MCP 도ꡬ 카운트 μ§„ν™”
19
+ - 1.9.43 β†’ 1.9.84 (READ 5μ’… μ™„μ„±): 17 도ꡬ
20
+ - 1.9.85 β†’ 1.9.110 (health + Memory CRUD): 30 도ꡬ λ§ˆμΌμŠ€ν†€
21
+ - 1.9.112 β†’ 1.9.119 (Memory READ 5μ’…): 35 도ꡬ
22
+ - 1.9.128 (DELETE/RESTORE 5μ’…): 40 도ꡬ λ§ˆμΌμŠ€ν†€
23
+ - 1.9.142 (Feature Graph): 45 도ꡬ
24
+ - 1.9.145 (env detect): 47 도ꡬ
25
+ - **1.9.158: 48 도ꡬ πŸŽ‰**
26
+
27
+ ### Pending β€” 1.9.158 ꢌ고 λ‹€μŒ 후보
28
+ - **1.9.159** β€” `leerness_provider_add` MCP 도ꡬ (49번째) β€” μ™ΈλΆ€ AI κ°€ μžκ°€ ν™•μž₯ κ°€λŠ₯
29
+ - **1.9.160** β€” `provider sync` (OpenRouter llms.txt μžλ™ 동기화)
30
+ - **1.9.161** β€” LSP μ–΄λŒ‘ν„° MVP
31
+
32
+ ### Verified
33
+ - e2e 217/217 βœ“
34
+ - stress-v103: 12/12 (MCP tools/list 48개 3μ’… + tools/call μ‹€ν˜ΈμΆœ 2μ’… + λˆ„μ  νšŒκ·€ 7μ’…) πŸŽ‰ **MCP 48 도ꡬ λ§ˆμΌμŠ€ν†€**
35
+ - VERSION = 1.9.158 / autonomous-rounds = 88
36
+
37
+ ---
38
+
39
+ ## 1.9.157 β€” 2026-05-20
40
+
41
+ **Provider Registry CLI MVP β€” μ‚¬μš©μž μ •μ˜ provider 동적 μΆ”κ°€ (점검 λ³΄κ³ μ„œ ꢌ고 #3 β€” Provider Registry MCP의 CLI 단계).**
42
+
43
+ 자율 λͺ¨λ“œ 87 λΌμš΄λ“œ. 1.9.155 점검 λ³΄κ³ μ„œκ°€ λ°œκ²¬ν•œ "λͺ¨λΈ/ν”„λ‘œλ°”μ΄λ” 폭 5μ’… vs 200+" gap λ³΄κ°•μ˜ 첫 단계.
44
+
45
+ ### Added β€” `leerness provider list|add|remove` μ‹ κ·œ λͺ…λ Ή
46
+ - 빌트인 5μ’… (claude/codex/gemini/copilot/ollama) + **μ‚¬μš©μž μ •μ˜ provider 동적 μΆ”κ°€**
47
+ - `.harness/providers.json` β€” μ‚¬μš©μž μ •μ˜ μ €μž₯ (schemaVersion 1)
48
+ - `_allProviders(root)` β€” 빌트인 + μ‚¬μš©μž μ •μ˜ merge
49
+ - 같은 id 의 user override 적용 β†’ 빌트인 μ„€μ • λ³€κ²½ κ°€λŠ₯
50
+ - λΉŒνŠΈμΈμ— μ—†λŠ” user-only provider μΆ”κ°€
51
+
52
+ ### CLI
53
+ ```bash
54
+ leerness provider list # 빌트인 + μ‚¬μš©μž μ •μ˜ 톡합 ν‘œμ‹œ
55
+ leerness provider list --json # ꡬ쑰화 좜λ ₯ (total/builtin/user/providers)
56
+ leerness provider add openrouter --bin openrouter-cli --desc "OpenRouter aggregator"
57
+ leerness provider add bedrock --bin aws-bedrock-cli --env-flag LEERNESS_ENABLE_BEDROCK
58
+ leerness provider remove openrouter # μ‚¬μš©μž μ •μ˜λ§Œ 제거 κ°€λŠ₯ (빌트인 κ±°λΆ€)
59
+ ```
60
+
61
+ ### Changed β€” `agents list/check` Provider Registry 톡합
62
+ - ν‘œ 좜λ ₯에 `source` 컬럼 μΆ”κ°€ (builtin / user / user(override))
63
+ - JSON 응닡에 `source` ν•„λ“œ μΆ”κ°€
64
+ - ν™œμ„± 없을 λ•Œ "1.9.157: 빌트인 μ™Έ CLI μΆ”κ°€" hint ν‘œμ‹œ
65
+ - 헀더 `(1.9.30)` μœ μ§€ β€” κΈ°μ‘΄ e2e regex ν˜Έν™˜
66
+
67
+ ### Use Cases
68
+ - **OpenRouter 200+ λͺ¨λΈ 흑수**: `provider add openrouter --bin openrouter-cli` ν›„ `LEERNESS_ENABLE_OPENROUTER=1` μ„€μ •
69
+ - **AWS Bedrock 톡합**: `provider add bedrock --bin aws-bedrock-cli`
70
+ - **Groq / Hugging Face / 자체 LLM CLI**: λͺ¨λ‘ 동일 νŒ¨ν„΄
71
+ - **μžλ™ 발견**: 1.9.158 μ—μ„œ OpenRouter llms.txt μžλ™ 동기화 μ˜ˆμ •
72
+
73
+ ### Verified β€” 5λŠ₯λ ₯ 맀트릭슀 κ°±μ‹ 
74
+ | μ˜μ—­ | 1.9.156 | 1.9.157 |
75
+ |---|---|---|
76
+ | Provider 폭 | 5μ’… κ³ μ • | **λ¬΄μ œν•œ (동적 등둝)** |
77
+ | μ’…ν•© 완성도 | 60% | **62%** |
78
+
79
+ ### Pending β€” λ³΄κ³ μ„œ ꢌ고 남은 후보
80
+ - **1.9.158** β€” `leerness provider sync` (OpenRouter llms.txt μžλ™ 동기화) + MCP `leerness_provider_list` (48번째 도ꡬ)
81
+ - **1.9.159** β€” LSP μ–΄λŒ‘ν„° MVP (TypeScript LSP)
82
+ - **1.9.160** β€” playwright/computer-use bridge (`permissions.browser/mouse` μ‹€ λ™μž‘)
83
+
84
+ ### Verified
85
+ - e2e 217/217 βœ“
86
+ - stress-v102: 19/19 (Provider Registry ν•¨μˆ˜ 3μ’… + list 2μ’… + add/remove 6μ’… + agents 톡합 2μ’… + λˆ„μ  νšŒκ·€ 6μ’…)
87
+ - VERSION = 1.9.157 / autonomous-rounds = 87
88
+
89
+ ---
90
+
3
91
  ## 1.9.156 β€” 2026-05-20
4
92
 
5
93
  **`agents multi --execute` μ‹€μ œ spawn + consensus 톡합 (1.9.155 점검 λ³΄κ³ μ„œ 발견 gap #1 보강).**
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.156-green)]() [![tests](https://img.shields.io/badge/e2e-217%2F217-success)]() [![stress](https://img.shields.io/badge/stress--v101-18%2F18-success)]() [![mcp](https://img.shields.io/badge/MCP--tools-47-blue)]() [![rounds](https://img.shields.io/badge/autonomous--rounds-86-blueviolet)]() [![main-push](https://img.shields.io/badge/release--main--push-auto-success)]() [![multi-execute](https://img.shields.io/badge/agents_multi-real_spawn%2Bconsensus-success)]() [![multi-provider](https://img.shields.io/badge/REPL-5_providers%2Bmodel_catalog-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.158-green)]() [![tests](https://img.shields.io/badge/e2e-217%2F217-success)]() [![stress](https://img.shields.io/badge/stress--v103-12%2F12-success)]() [![mcp](https://img.shields.io/badge/MCP--tools-48-blue)]() [![rounds](https://img.shields.io/badge/autonomous--rounds-88-blueviolet)]() [![main-push](https://img.shields.io/badge/release--main--push-auto-success)]() [![provider-registry](https://img.shields.io/badge/provider_registry-CLI%2BMCP-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)]()
6
6
 
7
7
  ```
8
8
  ╔══════════════════════════════════════════════════════════════╗
@@ -12,7 +12,7 @@
12
12
  β•‘ β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•”β•β•β• β–ˆβ–ˆβ•”β•β•β• β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β• β•šβ•β•β•β•β–ˆβ–ˆβ•‘ β•‘
13
13
  β•‘ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘ β•šβ–ˆβ–ˆβ–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•‘ β•‘
14
14
  β•‘ β•šβ•β•β•β•β•β•β•β•šβ•β•β•β•β•β•β•β•šβ•β•β•β•β•β•β•β•šβ•β• β•šβ•β•β•šβ•β• β•šβ•β•β•β•β•šβ•β•β•β•β•β•β•β•šβ•β•β•β•β•β•β• β•‘
15
- β•‘ v1.9.156 AI Agent Reliability Harness + Sandbox β•‘
15
+ β•‘ v1.9.158 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.156';
9
+ const VERSION = '1.9.158';
10
10
  const MARK = '<!-- leerness:managed -->';
11
11
  const README_START = '<!-- leerness:project-readme:start -->';
12
12
  const README_END = '<!-- leerness:project-readme:end -->';
@@ -4584,6 +4584,119 @@ const EXTERNAL_AGENTS = [
4584
4584
  installCmd: 'curl -fsSL https://ollama.com/install.sh | sh (λ˜λŠ” https://ollama.com/download)', installHint: 'ollama serve μ‹€ν–‰ + ollama pull <model>' }
4585
4585
  ];
4586
4586
 
4587
+ // 1.9.157: Provider Registry β€” μ‚¬μš©μž μ •μ˜ provider 동적 μΆ”κ°€ (.harness/providers.json)
4588
+ // 빌트인 5μ’… (EXTERNAL_AGENTS) + μ‚¬μš©μž μ •μ˜λ₯Ό merge. OpenRouter / Bedrock / Groq λ“± μƒˆ CLI μ¦‰μ‹œ 흑수 κ°€λŠ₯.
4589
+ // 파일 ν˜•μ‹: { "schemaVersion": 1, "providers": [{ id, bin, envFlag, versionArgs, desc, installHint }] }
4590
+ function _providersFile(root) { return path.join(absRoot(root), '.harness', 'providers.json'); }
4591
+ function _readUserProviders(root) {
4592
+ const p = _providersFile(root);
4593
+ if (!exists(p)) return [];
4594
+ try {
4595
+ const j = JSON.parse(read(p));
4596
+ return Array.isArray(j.providers) ? j.providers : [];
4597
+ } catch { return []; }
4598
+ }
4599
+ function _writeUserProviders(root, providers) {
4600
+ const p = _providersFile(root);
4601
+ mkdirp(path.dirname(p));
4602
+ writeUtf8(p, JSON.stringify({ schemaVersion: 1, providers }, null, 2) + '\n');
4603
+ }
4604
+ // 빌트인 + μ‚¬μš©μž μ •μ˜ merge β€” 같은 id κ°€ 있으면 user κ°€ 빌트인 override
4605
+ function _allProviders(root) {
4606
+ try {
4607
+ const userList = _readUserProviders(root);
4608
+ const builtinIds = new Set(EXTERNAL_AGENTS.map(a => a.id));
4609
+ const userOverrides = new Map();
4610
+ for (const u of userList) {
4611
+ if (!u || !u.id) continue;
4612
+ // 정상화 β€” λˆ„λ½ ν•„λ“œλŠ” λΉŒνŠΈμΈμ—μ„œ fallback
4613
+ userOverrides.set(u.id, {
4614
+ id: u.id,
4615
+ bin: u.bin || u.id,
4616
+ envFlag: u.envFlag || `LEERNESS_ENABLE_${String(u.id).toUpperCase()}`,
4617
+ versionArgs: Array.isArray(u.versionArgs) ? u.versionArgs : ['--version'],
4618
+ desc: u.desc || `(user) ${u.id}`,
4619
+ installHint: u.installHint || '',
4620
+ installCmd: u.installCmd || ''
4621
+ });
4622
+ }
4623
+ // 빌트인 λ¨Όμ €, user override 적용
4624
+ const merged = EXTERNAL_AGENTS.map(a => userOverrides.has(a.id) ? userOverrides.get(a.id) : a);
4625
+ // λΉŒνŠΈμΈμ— μ—†λŠ” user-only μΆ”κ°€
4626
+ for (const u of userOverrides.values()) {
4627
+ if (!builtinIds.has(u.id)) merged.push(u);
4628
+ }
4629
+ return merged;
4630
+ } catch { return EXTERNAL_AGENTS.slice(); }
4631
+ }
4632
+ function providerCmd(root, sub, ...args) {
4633
+ root = absRoot(root || process.cwd());
4634
+ _loadEnvFile(root);
4635
+ if (!sub || sub === 'list') {
4636
+ const all = _allProviders(root);
4637
+ const userList = _readUserProviders(root);
4638
+ const userIds = new Set(userList.map(u => u.id));
4639
+ if (has('--json')) {
4640
+ log(JSON.stringify({
4641
+ total: all.length,
4642
+ builtin: EXTERNAL_AGENTS.length,
4643
+ user: userList.length,
4644
+ providers: all.map(p => ({ id: p.id, bin: p.bin, envFlag: p.envFlag, source: userIds.has(p.id) ? 'user' : 'builtin', desc: p.desc }))
4645
+ }, null, 2));
4646
+ return;
4647
+ }
4648
+ log(`# leerness provider list (1.9.157)`);
4649
+ log(`총 ${all.length}개 (빌트인 ${EXTERNAL_AGENTS.length} + μ‚¬μš©μž ${userList.length})`);
4650
+ log('');
4651
+ log(`| id | source | bin | envFlag |`);
4652
+ log(`|---|---|---|---|`);
4653
+ for (const p of all) {
4654
+ const src = userIds.has(p.id) ? (EXTERNAL_AGENTS.some(b => b.id === p.id) ? 'user(override)' : 'user') : 'builtin';
4655
+ log(`| ${p.id} | ${src} | ${p.bin} | ${p.envFlag} |`);
4656
+ }
4657
+ if (!userList.length) {
4658
+ log('');
4659
+ log(`πŸ’‘ μ‚¬μš©μž μ •μ˜ provider μΆ”κ°€: leerness provider add <id> --bin <cmd> [--env-flag F] [--version-args ARGS] [--desc D]`);
4660
+ }
4661
+ return;
4662
+ }
4663
+ if (sub === 'add') {
4664
+ const id = (args[0] || arg('--id', '')).trim();
4665
+ if (!id) return fail('provider add <id> ν•„μš” (예: openrouter)');
4666
+ if (!/^[a-z][a-z0-9_-]*$/i.test(id)) return fail(`잘λͺ»λœ id: ${id} (영문자/숫자/_- 만 ν—ˆμš©)`);
4667
+ const bin = arg('--bin', id);
4668
+ const envFlag = arg('--env-flag', `LEERNESS_ENABLE_${id.toUpperCase()}`);
4669
+ const versionArgs = (arg('--version-args', '--version') || '--version').split(/\s+/).filter(Boolean);
4670
+ const desc = arg('--desc', `(user) ${id}`);
4671
+ const installHint = arg('--install-hint', '');
4672
+ const userList = _readUserProviders(root);
4673
+ // 쀑볡 id 처리: 빌트인 override λ˜λŠ” user κ°±μ‹ 
4674
+ const existingIdx = userList.findIndex(u => u.id === id);
4675
+ const entry = { id, bin, envFlag, versionArgs, desc, installHint };
4676
+ if (existingIdx >= 0) userList[existingIdx] = entry;
4677
+ else userList.push(entry);
4678
+ _writeUserProviders(root, userList);
4679
+ ok(`provider 등둝: ${id} (bin=${bin}, envFlag=${envFlag})`);
4680
+ log(` β†’ ν™œμ„±ν™”: .env에 ${envFlag}=1 μ„€μ • ν›„ \`leerness agents list\` 둜 확인`);
4681
+ return;
4682
+ }
4683
+ if (sub === 'remove') {
4684
+ const id = (args[0] || arg('--id', '')).trim();
4685
+ if (!id) return fail('provider remove <id> ν•„μš”');
4686
+ if (EXTERNAL_AGENTS.some(b => b.id === id) && !_readUserProviders(root).some(u => u.id === id)) {
4687
+ return fail(`${id} λŠ” 빌트인 β€” 제거 λΆˆκ°€ (override 만 제거 κ°€λŠ₯)`);
4688
+ }
4689
+ const userList = _readUserProviders(root);
4690
+ const before = userList.length;
4691
+ const filtered = userList.filter(u => u.id !== id);
4692
+ if (filtered.length === before) return fail(`μ‚¬μš©μž μ •μ˜ provider ${id} μ—†μŒ`);
4693
+ _writeUserProviders(root, filtered);
4694
+ ok(`provider 제거: ${id}`);
4695
+ return;
4696
+ }
4697
+ fail(`μ•Œ 수 μ—†λŠ” sub: ${sub} (list / add / remove)`);
4698
+ }
4699
+
4587
4700
  // 1.9.36: μž‘μ—… ν‚€μ›Œλ“œ λΆ„μ„μœΌλ‘œ 졜적 CLI μΆ”μ²œ
4588
4701
  // \bλŠ” ASCII word boundary만 인식 β†’ ν•œκΈ€ ν‚€μ›Œλ“œλŠ” λ‹¨μˆœ substring 검사 μ‚¬μš©.
4589
4702
  function _recommendAgent(task) {
@@ -5119,17 +5232,20 @@ function agentsCmd(root, sub, ...args) {
5119
5232
  _loadEnvFile(path.join(root, '..'));
5120
5233
 
5121
5234
  if (!sub || sub === 'list') {
5122
- const checks = EXTERNAL_AGENTS.map(a => _checkAgent(a));
5235
+ // 1.9.157: Provider Registry 톡합 β€” 빌트인 5μ’… + μ‚¬μš©μž μ •μ˜ provider 포함
5236
+ const providers = _allProviders(root);
5237
+ const userIds = new Set(_readUserProviders(root).map(u => u.id));
5238
+ const checks = providers.map(a => ({ ...(_checkAgent(a)), source: userIds.has(a.id) ? 'user' : 'builtin' }));
5123
5239
  if (has('--json')) { log(JSON.stringify({ agents: checks }, null, 2)); return; }
5124
5240
  log(`# μ™ΈλΆ€ AI CLI μ˜€μΌ€μŠ€νŠΈλ ˆμ΄μ…˜ (1.9.30)`);
5125
5241
  log('');
5126
- log(`| Agent | env (${'env=1 ν™œμ„±'}) | μ„€μΉ˜ | 버전 | μƒνƒœ |`);
5127
- log(`|---|---|---|---|---|`);
5242
+ log(`| Agent | source | env (${'env=1 ν™œμ„±'}) | μ„€μΉ˜ | 버전 | μƒνƒœ |`);
5243
+ log(`|---|---|---|---|---|---|`);
5128
5244
  for (const c of checks) {
5129
5245
  const envMark = c.enabled ? 'βœ“' : 'βœ—';
5130
5246
  const instMark = c.installed ? 'βœ“' : 'βœ—';
5131
5247
  const statusEmoji = c.status === 'ready' ? '🟒 ready' : c.status === 'not-installed' ? 'βšͺ λ―Έμ„€μΉ˜' : c.status === 'disabled' ? '🟑 λΉ„ν™œμ„±' : '❓';
5132
- log(`| ${c.id} | ${envMark} ${c.envFlag} | ${instMark} | ${c.version || '-'} | ${statusEmoji} |`);
5248
+ log(`| ${c.id} | ${c.source} | ${envMark} ${c.envFlag} | ${instMark} | ${c.version || '-'} | ${statusEmoji} |`);
5133
5249
  }
5134
5250
  const ready = checks.filter(c => c.status === 'ready');
5135
5251
  log('');
@@ -5140,6 +5256,7 @@ function agentsCmd(root, sub, ...args) {
5140
5256
  log(` 1) CLI μ„€μΉ˜ (예: \`npm i -g @openai/codex-cli\`, \`npm i -g @google/gemini-cli\`)`);
5141
5257
  log(` 2) .env λ˜λŠ” ν™˜κ²½λ³€μˆ˜: LEERNESS_ENABLE_CODEX=1, LEERNESS_ENABLE_GEMINI=1`);
5142
5258
  log(` 3) \`leerness agents check\`둜 μž¬ν™•μΈ`);
5259
+ log(` πŸ’‘ 1.9.157: 빌트인 μ™Έ CLI μΆ”κ°€: \`leerness provider add <id> --bin <cmd>\``);
5143
5260
  } else {
5144
5261
  log('');
5145
5262
  log(`πŸ’‘ 메인 μ—μ΄μ „νŠΈκ°€ sub-agent λΆ„λ°° μ‹œ μœ„ ${ready.length}개 CLI ν™œμš© κ°€λŠ₯:`);
@@ -5150,7 +5267,10 @@ function agentsCmd(root, sub, ...args) {
5150
5267
 
5151
5268
  if (sub === 'check') {
5152
5269
  // list의 alias, 단 λͺ…μ‹œμ  μž¬ν™•μΈ (JSON 좜λ ₯ κΈ°λ³Έ)
5153
- const checks = EXTERNAL_AGENTS.map(a => _checkAgent(a));
5270
+ // 1.9.157: Provider Registry 톡합
5271
+ const providers = _allProviders(root);
5272
+ const userIds = new Set(_readUserProviders(root).map(u => u.id));
5273
+ const checks = providers.map(a => ({ ...(_checkAgent(a)), source: userIds.has(a.id) ? 'user' : 'builtin' }));
5154
5274
  if (has('--json')) { log(JSON.stringify({ agents: checks, ready: checks.filter(c => c.status === 'ready').map(c => c.id) }, null, 2)); return; }
5155
5275
  return agentsCmd(root, 'list'); // λΉ„-JSON은 list와 동일
5156
5276
  }
@@ -9621,7 +9741,8 @@ function mcpServeCmd(root) {
9621
9741
  { name: 'leerness_feature_list', description: '1.9.141 β€” 전체 Feature Graph λ…Έλ“œ + μ—£μ§€ JSON. μ™ΈλΆ€ AIκ°€ μ‹œμŠ€ν…œ λ‚΄ κΈ°λŠ₯ μ˜μ‘΄μ„±μ„ ν•œ λ²ˆμ— 회수', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
9622
9742
  { name: 'leerness_feature_add', description: '1.9.142 β€” Feature Graph 에 μƒˆ λ…Έλ“œ μΆ”κ°€ (μ™ΈλΆ€ AIκ°€ μ½”λ“œ μž‘μ„± 쀑 직접 feature 등둝). 인자: { title (required), dependsOn?, affects?, coChangesWith?, files?, path? }. μžλ™ F-XXXX ID λΆ€μ—¬. CRUD 완성에 κΈ°μ—¬', inputSchema: { type: 'object', properties: { title: { type: 'string' }, dependsOn: { type: 'string' }, affects: { type: 'string' }, coChangesWith: { type: 'string' }, files: { type: 'string' }, path: { type: 'string' } }, required: ['title'] } },
9623
9743
  { name: 'leerness_feature_link', description: '1.9.142 β€” κΈ°μ‘΄ feature λ…Έλ“œμ— 의쑴/영ν–₯/곡변경 μ—£μ§€ μΆ”κ°€. 인자: { id (required, F-XXXX), dependsOn?, affects?, coChangesWith?, path? }. μ™ΈλΆ€ AIκ°€ μ½”λ“œ λ³€κ²½ 도쀑 λ°œκ²¬ν•œ 인과관계λ₯Ό μ¦‰μ‹œ κ·Έλž˜ν”„μ— 반영', inputSchema: { type: 'object', properties: { id: { type: 'string' }, dependsOn: { type: 'string' }, affects: { type: 'string' }, coChangesWith: { type: 'string' }, path: { type: 'string' } }, required: ['id'] } },
9624
- { name: 'leerness_env_detect', description: '1.9.145 β€” μ‹€ν–‰ ν™˜κ²½ μžλ™ 감지 + 변동 좔적 JSON ({ snapshot: { os, hardware, locale, shell, node, tools, scriptDependencies }, diff: { firstCapture, changes, missing }, persisted }). "X은(λŠ”) λ‚΄λΆ€ λ˜λŠ” μ™ΈλΆ€ λͺ…λ Ή... μ•„λ‹™λ‹ˆλ‹€" 사전 λ°©μ§€: package.json scripts 의쑴 도ꡬ가 PATH에 μžˆλŠ”μ§€ 검증 + λ¨Έμ‹ /Node/도ꡬ λ³€κ²½ 감지. μ ˆλŒ€κ²½λ‘œ λ§ˆμŠ€ν‚Ή (λ³΄μ•ˆ). 인자: { path? }', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } }
9744
+ { name: 'leerness_env_detect', description: '1.9.145 β€” μ‹€ν–‰ ν™˜κ²½ μžλ™ 감지 + 변동 좔적 JSON ({ snapshot: { os, hardware, locale, shell, node, tools, scriptDependencies }, diff: { firstCapture, changes, missing }, persisted }). "X은(λŠ”) λ‚΄λΆ€ λ˜λŠ” μ™ΈλΆ€ λͺ…λ Ή... μ•„λ‹™λ‹ˆλ‹€" 사전 λ°©μ§€: package.json scripts 의쑴 도ꡬ가 PATH에 μžˆλŠ”μ§€ 검증 + λ¨Έμ‹ /Node/도ꡬ λ³€κ²½ 감지. μ ˆλŒ€κ²½λ‘œ λ§ˆμŠ€ν‚Ή (λ³΄μ•ˆ). 인자: { path? }', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
9745
+ { name: 'leerness_provider_list', description: '1.9.157/158 β€” Provider Registry 쑰회 JSON ({ total, builtin, user, providers: [{ id, bin, envFlag, source, desc }] }). 빌트인 5μ’… (claude/codex/gemini/copilot/ollama) + .harness/providers.json μ‚¬μš©μž μ •μ˜ 톡합. μ™ΈλΆ€ AIκ°€ sub-agent λΆ„λ°° κ°€λŠ₯ν•œ provider 전체 회수 (OpenRouter/Bedrock λ“± λ“±λ‘λ˜μ–΄ 있으면 같이 λ…ΈμΆœ). πŸŽ‰ MCP 48 도ꡬ λ§ˆμΌμŠ€ν†€', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } }
9625
9746
  ];
9626
9747
 
9627
9748
  function send(obj) {
@@ -9714,6 +9835,8 @@ function mcpServeCmd(root) {
9714
9835
  break;
9715
9836
  // 1.9.145: μ‹€ν–‰ ν™˜κ²½ μžλ™ 감지
9716
9837
  case 'leerness_env_detect': cliArgs = ['env', 'detect', targetPath, '--json']; break;
9838
+ // 1.9.158: Provider Registry β€” μ™ΈλΆ€ AI κ°€ λ“±λ‘λœ provider 회수
9839
+ case 'leerness_provider_list': cliArgs = ['provider', 'list', '--path', targetPath, '--json']; break;
9717
9840
  default:
9718
9841
  return send({ jsonrpc: '2.0', id, error: { code: -32601, message: `Unknown tool: ${name}` } });
9719
9842
  }
@@ -11572,7 +11695,7 @@ function reuseAutodetectCmd(root) {
11572
11695
  }
11573
11696
 
11574
11697
  function help() {
11575
- log(`Leerness v${VERSION}\n\nUsage:\n leerness init [path] [--language auto|ko|en] [--skills recommended|all|a,b]\n leerness migrate [path] [--dry-run] [--force]\n leerness update [path] [--check|--yes|--force|--from <tarball>]\n leerness auto-update install [path]\n leerness status [path]\n leerness verify [path]\n leerness debug [path]\n leerness audit [path]\n leerness check [path]\n leerness scan secrets [path]\n leerness encoding check [path]\n leerness lazy detect [path]\n leerness memory search "query" [--limit 5]\n leerness handoff [path] [--all-apps] [--include p1,p2] [--since 24h|3d] [--compact] [--json] # 1.9.17-22 μ›Œν¬μŠ€νŽ˜μ΄μŠ€ (--compact: LLM μ‹œμŠ€ν…œ ν”„λ‘¬ν”„νŠΈμš© 1쀄 μš”μ•½)\n leerness orchestrate "<λͺ©ν‘œ>" [--agents N] [--model qwen2.5:7b-instruct] [--retry-on-fail K] # 1.9.22 Ollama opt-in (LEERNESS_OLLAMA_BASE_URL ν•„μš”)\n leerness llm-bench record --score N --model X [--label L] [--tokens T] # 1.9.22 LLM 벀치 νžˆμŠ€ν† λ¦¬ λˆ„μ \n leerness deps <capability> [--run-tests] [--json] # 1.9.24 depends-on μ—­λ°©ν–₯ 좔적 + μžλ™ νšŒκ·€ sweep\n leerness memory search "ν‚€" [--include-code] # 1.9.25 μ†ŒμŠ€ μ½”λ“œ 본문도 검색 (λͺ¨μˆœ 감지 핡심)\n leerness brainstorm "주제" [--include-code] # 1.9.25 μ½”λ“œ λ³Έλ¬Έ hits 포함\n leerness register-pending "<μš”μ²­>" [--agent X] [--note Y] # 1.9.25 닀쀑 μ„Έμ…˜ in-progress μ¦‰μ‹œ 등둝\n leerness optimism-check <T-ID> [--json] # 1.9.26/27 낙관적 ν‘œμ‹œ 감지 (1.9.27: 10 μΉ΄ν…Œκ³ λ¦¬ + URL/λ©”μ„œλ“œ λ§€ν•‘ + 신뒰도 점수)\n leerness persona list|show <id>|add <id> # 1.9.29 페λ₯΄μ†Œλ‚˜ μΉ΄νƒˆλ‘œκ·Έ (λ³΄μ•ˆ/μ„±λŠ₯/UX/testing/docs 5μ’… λ‚΄μž₯)\n leerness review <file> --persona <id1,id2,...> # 1.9.29 도메인 페λ₯΄μ†Œλ‚˜ 리뷰 ν”„λ‘¬ν”„νŠΈ μžλ™ 생성\n leerness agents list|check|quota # 1.9.30/31 μ™ΈλΆ€ AI CLI κ°€μš©μ„± + quota μΆ”μ • (claude/codex/gemini/copilot)\n leerness agents dispatch "<task>" --to <id> # 1.9.30 ν™œμ„± CLI λŒ€μƒ μ‹€ν–‰ λͺ…λ Ή 생성 (μ‹€ 호좜 X, μ‚¬μš©μž μ‹€ν–‰)\n leerness agents multi "<task>" [--only c1,c2] [--write] [--execute] [--timeout 60] # 1.9.152/156 ν™œμ„± N개 일괄 dispatch (--execute: μ‹€ spawn + consensus)\n leerness agents dispatch "<task>" --multi # 1.9.152 multi λͺ¨λ“œ alias (λ˜λŠ” --to all)\n leerness setup-agents [path] [--yes|--no-setup-agents] # 1.9.32 sub-agent CLI μΈν„°λž™ν‹°λΈŒ μ„€μ • (.env + λ―Έμ„€μΉ˜ μžλ™ μ„€μΉ˜)\n leerness init [path] [--no-stale-check] # 1.9.33 npx μΊμ‹œ 함정 β€” μ˜› 버전 μžλ™ κ²½κ³  (끄렀면 --no-stale-check)\n leerness contract verify <spec.md> <impl.js> [--json] # 1.9.35 λͺ…μ„Έ ↔ κ΅¬ν˜„ 일치 검사 (ν•¨μˆ˜/ν•„λ“œ)\n leerness reuse autodetect [path] [--apply] [--json] # 1.9.35 src/*.js의 module.exports β†’ reuse-map 후보 등둝\n leerness audit [path] [--fix] # 1.9.35 --fix: session-handoff/current-state μžλ™ κ°±μ‹ \n leerness verify-claim <T-ID> ... [--strict-claims] # 1.9.26 verify-claim에 낙관적 ν‘œμ‹œ μžλ™ 검사 톡합\n leerness reuse-map [path] [--all-apps] [--include p1,p2] [--strict-elements] [--json] # 1.9.18 쀑볡/μž μž¬μ€‘λ³΅/depends-on\n leerness verify-claim <T-ID> [--path .] [--run-tests] [--json] # 1.9.18-20 evidence μžλ™ 검증 (1.9.20: scenes/scripts λ“± 도메인 폴더 + jest/mocha νŒŒμ‹±)\n leerness verify-code [path] [--build] [--bench] # 1.9.20 --bench: scripts.bench μΆ”κ°€ μ‹€ν–‰ + evidence λˆ„μ \n leerness session close [path]\n leerness route <task-type>\n leerness self check [path]\n leerness readme sync [path]\n leerness consistency check [path]\n leerness consistency merge-design-guide [path]\n leerness plan show|init|add|drop|progress|sync [args]\n leerness task list|add|update|drop|fix-evidence|relink [args]\n leerness skill list|info <name>\n leerness skill learn <id> --doc <url> --command "..." --capability "..." [--note ...]\n leerness skill use <id> [--note ...]\n leerness skill optimize <id> --before "..." --after "..." [--note ...]\n leerness skill remove <id>\n leerness skill consolidate [--threshold 0.3]\n leerness gate [path] # verify+audit+scan+encoding+lazy
11698
+ log(`Leerness v${VERSION}\n\nUsage:\n leerness init [path] [--language auto|ko|en] [--skills recommended|all|a,b]\n leerness migrate [path] [--dry-run] [--force]\n leerness update [path] [--check|--yes|--force|--from <tarball>]\n leerness auto-update install [path]\n leerness status [path]\n leerness verify [path]\n leerness debug [path]\n leerness audit [path]\n leerness check [path]\n leerness scan secrets [path]\n leerness encoding check [path]\n leerness lazy detect [path]\n leerness memory search "query" [--limit 5]\n leerness handoff [path] [--all-apps] [--include p1,p2] [--since 24h|3d] [--compact] [--json] # 1.9.17-22 μ›Œν¬μŠ€νŽ˜μ΄μŠ€ (--compact: LLM μ‹œμŠ€ν…œ ν”„λ‘¬ν”„νŠΈμš© 1쀄 μš”μ•½)\n leerness orchestrate "<λͺ©ν‘œ>" [--agents N] [--model qwen2.5:7b-instruct] [--retry-on-fail K] # 1.9.22 Ollama opt-in (LEERNESS_OLLAMA_BASE_URL ν•„μš”)\n leerness llm-bench record --score N --model X [--label L] [--tokens T] # 1.9.22 LLM 벀치 νžˆμŠ€ν† λ¦¬ λˆ„μ \n leerness deps <capability> [--run-tests] [--json] # 1.9.24 depends-on μ—­λ°©ν–₯ 좔적 + μžλ™ νšŒκ·€ sweep\n leerness memory search "ν‚€" [--include-code] # 1.9.25 μ†ŒμŠ€ μ½”λ“œ 본문도 검색 (λͺ¨μˆœ 감지 핡심)\n leerness brainstorm "주제" [--include-code] # 1.9.25 μ½”λ“œ λ³Έλ¬Έ hits 포함\n leerness register-pending "<μš”μ²­>" [--agent X] [--note Y] # 1.9.25 닀쀑 μ„Έμ…˜ in-progress μ¦‰μ‹œ 등둝\n leerness optimism-check <T-ID> [--json] # 1.9.26/27 낙관적 ν‘œμ‹œ 감지 (1.9.27: 10 μΉ΄ν…Œκ³ λ¦¬ + URL/λ©”μ„œλ“œ λ§€ν•‘ + 신뒰도 점수)\n leerness persona list|show <id>|add <id> # 1.9.29 페λ₯΄μ†Œλ‚˜ μΉ΄νƒˆλ‘œκ·Έ (λ³΄μ•ˆ/μ„±λŠ₯/UX/testing/docs 5μ’… λ‚΄μž₯)\n leerness review <file> --persona <id1,id2,...> # 1.9.29 도메인 페λ₯΄μ†Œλ‚˜ 리뷰 ν”„λ‘¬ν”„νŠΈ μžλ™ 생성\n leerness agents list|check|quota # 1.9.30/31 μ™ΈλΆ€ AI CLI κ°€μš©μ„± + quota μΆ”μ • (claude/codex/gemini/copilot)\n leerness agents dispatch "<task>" --to <id> # 1.9.30 ν™œμ„± CLI λŒ€μƒ μ‹€ν–‰ λͺ…λ Ή 생성 (μ‹€ 호좜 X, μ‚¬μš©μž μ‹€ν–‰)\n leerness agents multi "<task>" [--only c1,c2] [--write] [--execute] [--timeout 60] # 1.9.152/156 ν™œμ„± N개 일괄 dispatch (--execute: μ‹€ spawn + consensus)\n leerness provider list|add|remove [args] # 1.9.157 Provider Registry β€” μ‚¬μš©μž μ •μ˜ CLI provider 동적 μΆ”κ°€ (OpenRouter/Bedrock 흑수)\n leerness agents dispatch "<task>" --multi # 1.9.152 multi λͺ¨λ“œ alias (λ˜λŠ” --to all)\n leerness setup-agents [path] [--yes|--no-setup-agents] # 1.9.32 sub-agent CLI μΈν„°λž™ν‹°λΈŒ μ„€μ • (.env + λ―Έμ„€μΉ˜ μžλ™ μ„€μΉ˜)\n leerness init [path] [--no-stale-check] # 1.9.33 npx μΊμ‹œ 함정 β€” μ˜› 버전 μžλ™ κ²½κ³  (끄렀면 --no-stale-check)\n leerness contract verify <spec.md> <impl.js> [--json] # 1.9.35 λͺ…μ„Έ ↔ κ΅¬ν˜„ 일치 검사 (ν•¨μˆ˜/ν•„λ“œ)\n leerness reuse autodetect [path] [--apply] [--json] # 1.9.35 src/*.js의 module.exports β†’ reuse-map 후보 등둝\n leerness audit [path] [--fix] # 1.9.35 --fix: session-handoff/current-state μžλ™ κ°±μ‹ \n leerness verify-claim <T-ID> ... [--strict-claims] # 1.9.26 verify-claim에 낙관적 ν‘œμ‹œ μžλ™ 검사 톡합\n leerness reuse-map [path] [--all-apps] [--include p1,p2] [--strict-elements] [--json] # 1.9.18 쀑볡/μž μž¬μ€‘λ³΅/depends-on\n leerness verify-claim <T-ID> [--path .] [--run-tests] [--json] # 1.9.18-20 evidence μžλ™ 검증 (1.9.20: scenes/scripts λ“± 도메인 폴더 + jest/mocha νŒŒμ‹±)\n leerness verify-code [path] [--build] [--bench] # 1.9.20 --bench: scripts.bench μΆ”κ°€ μ‹€ν–‰ + evidence λˆ„μ \n leerness session close [path]\n leerness route <task-type>\n leerness self check [path]\n leerness readme sync [path]\n leerness consistency check [path]\n leerness consistency merge-design-guide [path]\n leerness plan show|init|add|drop|progress|sync [args]\n leerness task list|add|update|drop|fix-evidence|relink [args]\n leerness skill list|info <name>\n leerness skill learn <id> --doc <url> --command "..." --capability "..." [--note ...]\n leerness skill use <id> [--note ...]\n leerness skill optimize <id> --before "..." --after "..." [--note ...]\n leerness skill remove <id>\n leerness skill consolidate [--threshold 0.3]\n leerness gate [path] # verify+audit+scan+encoding+lazy
11576
11699
  leerness retro [path] [--days 7] [--all-apps] [--include p1,p2] [--json] # 회고 (1.9.13~1.9.16)
11577
11700
  leerness insights [path] [--all-apps] [--include p1,p2] [--json] # λˆ„μ  톡계 (1.9.13~1.9.16)
11578
11701
  leerness brainstorm "<주제>" [--all-apps] [--include p1,p2] [--json] # λΈŒλ ˆμΈμŠ€ν† λ° (1.9.13~1.9.16)
@@ -11644,6 +11767,8 @@ async function main() {
11644
11767
  if (cmd === 'persona') return personaCmd(arg('--path', process.cwd()), args[1], args[2]);
11645
11768
  if (cmd === 'review') return reviewCmd(arg('--path', process.cwd()), args[1]);
11646
11769
  if (cmd === 'agents') return agentsCmd(arg('--path', process.cwd()), args[1], ...args.slice(2));
11770
+ // 1.9.157: Provider Registry β€” μ‚¬μš©μž μ •μ˜ provider 동적 μΆ”κ°€
11771
+ if (cmd === 'provider') return providerCmd(arg('--path', process.cwd()), args[1], ...args.slice(2));
11647
11772
  if (cmd === 'contract' && args[1] === 'verify') return contractVerifyCmd(args[2], args[3]);
11648
11773
  if (cmd === 'drift' && (args[1] === 'check' || !args[1])) return driftCheckCmd(args[2] || arg('--path', process.cwd()));
11649
11774
  if (cmd === 'usage' && (args[1] === 'stats' || !args[1])) return usageStatsCmd(args[2] || arg('--path', process.cwd()));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leerness",
3
- "version": "1.9.156",
3
+ "version": "1.9.158",
4
4
  "description": "Leerness: λΉ„νŒŒκ΄΄ λ§ˆμ΄κ·Έλ ˆμ΄μ…˜, μžλ™ 버전 κ°μ§€Β·μ—…λ°μ΄νŠΈ, κ³„νš/μ§„ν–‰/ν•Έλ“œμ˜€ν”„ μžλ™ν™”, κ²ŒμœΌλ¦„Β·μ‹œν¬λ¦ΏΒ·μΈμ½”λ”© μžλ™ κ°€λ“œ, Claude Code μŠ¬λž˜μ‹œ 톡합을 κ°–μΆ˜ ν•œκ΅­μ–΄ μš°μ„  AI 개발 ν•˜λ„€μŠ€.",
5
5
  "keywords": [
6
6
  "leerness",