leerness 1.9.153 → 1.9.154

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,36 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.9.154 — 2026-05-20
4
+
5
+ **agent 1-shot multi-provider + REPL `:provider` 활성 검증 (1.9.153 후속, 일관성 강화).**
6
+
7
+ 자율 모드 84 라운드.
8
+
9
+ ### Added — `leerness agent "<task>" --provider <p>` 1-shot multi-provider
10
+ - 기존: 1-shot 모드는 Ollama 만 호출, 다른 CLI 는 `agents dispatch` 안내만
11
+ - 변경: **claude / codex / gemini / copilot 도 직접 호출** (1.9.153 `_cliChat` 재사용)
12
+ - `_recordRun` observability — provider/model 필드 동적 (`agent_one_shot` kind)
13
+ - task-log 기록도 provider 동적 (`leerness agent (claude:claude, role=actor)` 형식)
14
+ - 실패 시 provider 별 friendly 안내 (Ollama: BASE_URL 확인 / 외부 CLI: `LEERNESS_ENABLE_<X>=1` + 설치)
15
+
16
+ ### Added — REPL `:provider <p>` 전환 시 활성 사전 검증
17
+ - validProviders 화이트리스트 5종 — 알 수 없는 provider 거부
18
+ - **비활성 (`ready` 아님) provider 전환 시 즉시 거부** — 실제 호출 시 실패 방지
19
+ - `_checkAgent` 결과로 status/installed/enabled 종합 판정
20
+ - Ollama 는 `LEERNESS_OLLAMA_BASE_URL` 미설정 시 친절한 안내 (블록 아님 — fallback URL 시도)
21
+ - 전환 성공 시 `rl.setPrompt(prompt())` 으로 프롬프트 즉시 갱신
22
+
23
+ ### Verified — setup-agents `_selectMany` 일관성 (회귀 방지)
24
+ - 1.9.34 이래 setup-agents 이미 `_selectMany` 사용 중 — 1.9.151 install 흐름과 일관
25
+ - stress-v99 회귀 테스트 추가 (사용자 명시 요청 일관성 보장)
26
+
27
+ ### Verified
28
+ - e2e 217/217 ✓
29
+ - stress-v99: 18/18 (1-shot multi-provider 6종 + REPL :provider 검증 4종 + setup-agents 1종 + 누적 회귀 7종)
30
+ - VERSION = 1.9.154 / autonomous-rounds = 84
31
+
32
+ ---
33
+
3
34
  ## 1.9.153 — 2026-05-20
4
35
 
5
36
  **`.env` 직접 생성/마이그레이션 + REPL 배너 leerness 고유 문구 + multi-provider REPL (사용자 명시 3종).**
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.153-green)]() [![tests](https://img.shields.io/badge/e2e-217%2F217-success)]() [![stress](https://img.shields.io/badge/stress--v98-23%2F23-success)]() [![mcp](https://img.shields.io/badge/MCP--tools-47-blue)]() [![json](https://img.shields.io/badge/--json-20_commands-blueviolet)]() [![rounds](https://img.shields.io/badge/autonomous--rounds-83-blueviolet)]() [![main-push](https://img.shields.io/badge/release--main--push-auto-success)]() [![multi-provider-repl](https://img.shields.io/badge/REPL-5_providers_session-success)]() [![env](https://img.shields.io/badge/.env-auto_generate%2Bmigrate-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.154-green)]() [![tests](https://img.shields.io/badge/e2e-217%2F217-success)]() [![stress](https://img.shields.io/badge/stress--v99-18%2F18-success)]() [![mcp](https://img.shields.io/badge/MCP--tools-47-blue)]() [![json](https://img.shields.io/badge/--json-20_commands-blueviolet)]() [![rounds](https://img.shields.io/badge/autonomous--rounds-84-blueviolet)]() [![main-push](https://img.shields.io/badge/release--main--push-auto-success)]() [![multi-provider](https://img.shields.io/badge/agent-5_provider_1shot%2BREPL-success)]() [![env](https://img.shields.io/badge/.env-auto_generate%2Bmigrate-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.153 AI Agent Reliability Harness + Sandbox ║
15
+ ║ v1.9.154 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.153';
9
+ const VERSION = '1.9.154';
10
10
  const MARK = '<!-- leerness:managed -->';
11
11
  const README_START = '<!-- leerness:project-readme:start -->';
12
12
  const README_END = '<!-- leerness:project-readme:end -->';
@@ -10425,7 +10425,36 @@ async function _agentRepl(root, opts) {
10425
10425
  if (!['planner', 'reviewer', 'actor'].includes(r)) { log(C.yel(` ⚠ role 은 planner/reviewer/actor`)); return false; }
10426
10426
  state.role = r; rl.setPrompt(prompt()); log(C.green(` role = ${r}`)); return false;
10427
10427
  }
10428
- if (op === 'provider') { state.provider = rest[0] || state.provider; log(C.green(` provider = ${state.provider}`)); return false; }
10428
+ if (op === 'provider') {
10429
+ const newProv = rest[0] || state.provider;
10430
+ const validProviders = ['ollama', 'claude', 'codex', 'gemini', 'copilot'];
10431
+ if (!validProviders.includes(newProv)) {
10432
+ log(C.yel(` ⚠ provider 는 ${validProviders.join(' / ')} (받음: ${newProv})`));
10433
+ return false;
10434
+ }
10435
+ // 1.9.154: provider 전환 시 활성 ready 사전 검증 — 비활성/미설치이면 친절한 안내 후 거부 (실제 호출 시 실패 방지)
10436
+ if (newProv === 'ollama') {
10437
+ // Ollama 는 HTTP 기반 — 단순히 LEERNESS_OLLAMA_BASE_URL 확인
10438
+ const url = process.env.LEERNESS_OLLAMA_BASE_URL || '';
10439
+ if (!url) {
10440
+ log(C.yel(` ⚠ ollama base URL 미설정 (LEERNESS_OLLAMA_BASE_URL) — 기본 http://localhost:11434 시도`));
10441
+ }
10442
+ } else {
10443
+ const agent = EXTERNAL_AGENTS.find(a => a.id === newProv);
10444
+ if (agent) {
10445
+ const st = _checkAgent(agent);
10446
+ if (st.status !== 'ready') {
10447
+ log(C.yel(` ⚠ ${newProv} 비활성 (${st.status}) — .env 에서 LEERNESS_ENABLE_${newProv.toUpperCase()}=1 + CLI 설치 필요`));
10448
+ log(C.dim(` (leerness agents list 로 상태 확인) — provider 전환 취소`));
10449
+ return false;
10450
+ }
10451
+ }
10452
+ }
10453
+ state.provider = newProv;
10454
+ rl.setPrompt(prompt());
10455
+ log(C.green(` provider = ${state.provider}`));
10456
+ return false;
10457
+ }
10429
10458
  if (op === 'clear') { process.stdout.write('\x1b[2J\x1b[H'); return false; }
10430
10459
  if (op === 'reset') { state.history = []; log(C.dim(' history 초기화됨')); return false; }
10431
10460
  if (op === 'history') {
@@ -10558,32 +10587,39 @@ async function agentCmd(root, taskArg) {
10558
10587
  } catch {}
10559
10588
  if (dryRun) { log('\n(dry-run) LLM 호출 스킵 — provider/권한/컨텍스트만 출력'); return; }
10560
10589
  if (!provider) { fail('활성 provider 없음 — .env 에서 LEERNESS_ENABLE_OLLAMA=1 또는 LEERNESS_ENABLE_CLAUDE=1 활성화'); process.exitCode = 1; return; }
10561
- // MVP: Ollama 지원 (로컬). 다른 CLI 는 사용자가 직접 호출 (leerness agents dispatch 이미 존재).
10590
+ // 1.9.148: role prompt 자동 prepend (모든 provider 공통)
10591
+ const finalPrompt = `${rolePrompt}\n\nTask: ${task}`;
10592
+ const t0 = Date.now();
10593
+ // 1.9.154: 1-shot 모드도 multi-provider — Ollama 외 claude/codex/gemini/copilot 직접 호출 (1.9.153 _cliChat 재사용)
10594
+ let r;
10562
10595
  if (provider === 'ollama') {
10563
10596
  log('\n[ollama 호출 중...]');
10564
- // 1.9.148: role prompt 자동 prepend
10565
- const finalPrompt = `${rolePrompt}\n\nTask: ${task}`;
10566
- const t0 = Date.now();
10567
- const r = await _ollamaChat(finalPrompt);
10568
- const dt = Date.now() - t0;
10569
- // 1.9.149: observability 기록
10570
- _recordRun(root, { kind: 'agent_one_shot', provider: 'ollama', model: r.model, role, durationMs: dt, ok: r.ok, error: r.error, task: task.slice(0, 200), responseChars: (r.response || '').length });
10571
- if (r.ok) {
10572
- log('\n[response (model=' + r.model + ', role=' + role + ', ' + dt + 'ms)]\n' + r.response);
10573
- try {
10574
- const tlp = taskLogPath(root);
10575
- const block = `\n## ${today()} leerness agent (ollama:${r.model}, role=${role})\n- task: ${task.slice(0, 200)}\n- response (preview): ${r.response.slice(0, 240).replace(/\n+/g, ' ')}\n`;
10576
- append(tlp, block);
10577
- } catch {}
10578
- } else {
10579
- fail(`ollama 호출 실패: ${r.error || 'unknown'}`);
10580
- log(` → ollama serve 실행 + LEERNESS_OLLAMA_BASE_URL 확인`);
10581
- process.exitCode = 1;
10582
- }
10597
+ r = await _ollamaChat(finalPrompt);
10598
+ } else if (['claude', 'codex', 'gemini', 'copilot'].includes(provider)) {
10599
+ log(`\n[${provider} CLI 호출 중...]`);
10600
+ r = await _cliChat(root, provider, finalPrompt, { timeout: 90000 });
10601
+ if (r.ok && !r.model) r.model = provider; // _cliChat 결과 보강
10602
+ } else {
10603
+ fail(`알 없는 provider: ${provider} (ollama/claude/codex/gemini/copilot)`);
10604
+ process.exitCode = 1;
10583
10605
  return;
10584
10606
  }
10585
- // provider: 사용자에게 직접 dispatch 안내
10586
- log(`\n💡 ${provider} provider 는 \`leerness agents dispatch "<task>" --to ${provider}\` 또는 외부 CLI 직접 호출 권장`);
10607
+ const dt = Date.now() - t0;
10608
+ // 1.9.149: observability 기록
10609
+ _recordRun(root, { kind: 'agent_one_shot', provider, model: r.model || provider, role, durationMs: dt, ok: r.ok, error: r.error, task: task.slice(0, 200), responseChars: (r.response || '').length });
10610
+ if (r.ok) {
10611
+ log(`\n[response (provider=${provider}, model=${r.model || provider}, role=${role}, ${dt}ms)]\n${r.response}`);
10612
+ try {
10613
+ const tlp = taskLogPath(root);
10614
+ const block = `\n## ${today()} leerness agent (${provider}:${r.model || provider}, role=${role})\n- task: ${task.slice(0, 200)}\n- response (preview): ${(r.response || '').slice(0, 240).replace(/\n+/g, ' ')}\n`;
10615
+ append(tlp, block);
10616
+ } catch {}
10617
+ } else {
10618
+ fail(`${provider} 호출 실패: ${r.error || 'unknown'}`);
10619
+ if (provider === 'ollama') log(` → ollama serve 실행 + LEERNESS_OLLAMA_BASE_URL 확인`);
10620
+ else log(` → .env 에서 LEERNESS_ENABLE_${provider.toUpperCase()}=1 + CLI 설치 확인 (leerness agents list)`);
10621
+ process.exitCode = 1;
10622
+ }
10587
10623
  }
10588
10624
 
10589
10625
  // ===== 1.9.147: 자동 유지보수 시스템 (사용자 명시 요청) =====
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leerness",
3
- "version": "1.9.153",
3
+ "version": "1.9.154",
4
4
  "description": "Leerness: 비파괴 마이그레이션, 자동 버전 감지·업데이트, 계획/진행/핸드오프 자동화, 게으름·시크릿·인코딩 자동 가드, Claude Code 슬래시 통합을 갖춘 한국어 우선 AI 개발 하네스.",
5
5
  "keywords": [
6
6
  "leerness",