leerness 1.9.147 → 1.9.148

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,42 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.9.148 — 2026-05-20
4
+
5
+ **사용자 명시 4종 + 3중 LLM 합의 (GPT-5.5 + Codex + Gemini) 우선 라운드 진행.**
6
+
7
+ ### Fixed — 방향키 선택 UI 중첩 출력 버그 (사용자 명시)
8
+ - `_selectOne` / `_selectMany` 의 question + 안내 라인에 `\x1b[2K` (clear entire line) ANSI 추가
9
+ - 이전: 매 render 마다 같은 위치에 question 라인이 누적되어 표시
10
+ - 이후: 화살표 이동 시 라인 깔끔히 덮어쓰기
11
+
12
+ ### Changed — 스킬 prompt 제거 (사용자 명시)
13
+ - "스킬 라이브러리 자동 설치 / 건너뛰기" 2-option prompt 완전 제거
14
+ - leerness가 자동으로 표준 공식 5종 설치 (office / commerce-api / ai-verified-skill-publisher / feature-implementation / project-roadmap-generator)
15
+ - 사용자 추가 설치는 `leerness skill install <id>` 명시 호출
16
+
17
+ ### Removed — CLI 에이전트 prompt 중복 (사용자 명시)
18
+ - 1.9.32 에서 추가된 "외부 AI CLI 활용하시겠습니까?" prompt 제거 (install 끝부분)
19
+ - 1.9.146 의 4지선다 prompt (resolveInstallOptions 안) 만 유지 — 모든 prompt 단일 위치 통합
20
+
21
+ ### Added — verify-code 다중 런타임 자동 감지 (3중 LLM 합의 — top priority)
22
+ - Node: `vitest`/`jest`/`mocha` 의존성 자동 감지 (script 없어도)
23
+ - Python: `pyproject.toml` / `setup.py` / `tests/` → `pytest -q`
24
+ - Go: `go.mod` → `go test ./...`
25
+ - Rust: `Cargo.toml` → `cargo test`
26
+ - TypeScript: `tsconfig.json` → `tsc --noEmit`
27
+ - `--strict` 또는 `LEERNESS_AUTONOMOUS=1`: no-test 감지 시 exit 1 (production 강제)
28
+
29
+ ### Added — agent 모드 고도화 (Gemini 권고)
30
+ - `--role planner|reviewer|actor` — 자기-승인 편향 방지
31
+ - planner: step 분해, 코드 작성 금지
32
+ - reviewer: 비판적 검토 (cascade 가능성 지적)
33
+ - actor: 계획대로 정확한 명령/코드만 실행 (기본값)
34
+ - Ollama 호출 시 role prompt 자동 prepend
35
+
36
+ ### Validation
37
+ - stress-v93: PASS
38
+ - e2e: 219/219 PASS
39
+
3
40
  ## 1.9.147 — 2026-05-20
4
41
 
5
42
  **자동 유지보수 시스템 — 사용자 명시 요청.**
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.147-green)]() [![tests](https://img.shields.io/badge/e2e-219%2F219-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-77-blueviolet)]() [![main-push](https://img.shields.io/badge/release--main--push-auto-success)]() [![auto-maintenance](https://img.shields.io/badge/auto--maintenance-webhook%2Bincident%2Bcreds%2Bdeploy-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.148-green)]() [![tests](https://img.shields.io/badge/e2e-219%2F219-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-78-blueviolet)]() [![main-push](https://img.shields.io/badge/release--main--push-auto-success)]() [![multi-runtime](https://img.shields.io/badge/verify--code-node%2Fpython%2Fgo%2Frust-success)]() [![agent-roles](https://img.shields.io/badge/agent--roles-planner%2Freviewer%2Factor-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.147 AI Agent Reliability Harness ║
15
+ ║ v1.9.148 AI Agent Reliability Harness ║
16
16
  ║ verify · remember · orchestrate · audit · prevent 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.147';
9
+ const VERSION = '1.9.148';
10
10
  const MARK = '<!-- leerness:managed -->';
11
11
  const README_START = '<!-- leerness:project-readme:start -->';
12
12
  const README_END = '<!-- leerness:project-readme:end -->';
@@ -117,7 +117,7 @@ function arg(name, def = null) { const i = process.argv.indexOf(name); return i
117
117
  function has(name) { return process.argv.includes(name); }
118
118
  function nonFlagArgs() {
119
119
  const out = [];
120
- const withValue = new Set(['--language','--skills','--path','--status','--progress','--goal','--reason','--next','--target','--token-env','--package','--out','--from','--repo','--id','--note','--evidence','--query','--limit','--action','--agent','--tool','--doc','--command','--capability','--before','--after','--display','--threshold','--trigger','--check','--set','--min-score','--include','--days','--gh-pages-src','--roadmap','--since','--agents','--model','--timeout','--retry-on-fail','--label','--score','--tokens','--alternatives','--impact','--tag','--surface','--depends-on','--affects','--co-changes-with','--files','--branch','--remote','--task-add','--next-action']);
120
+ const withValue = new Set(['--language','--skills','--path','--status','--progress','--goal','--reason','--next','--target','--token-env','--package','--out','--from','--repo','--id','--note','--evidence','--query','--limit','--action','--agent','--tool','--doc','--command','--capability','--before','--after','--display','--threshold','--trigger','--check','--set','--min-score','--include','--days','--gh-pages-src','--roadmap','--since','--agents','--model','--timeout','--retry-on-fail','--label','--score','--tokens','--alternatives','--impact','--tag','--surface','--depends-on','--affects','--co-changes-with','--files','--branch','--remote','--task-add','--next-action','--role','--provider','--env-var','--deploy','--token-lifetime-hours','--port','--secret']);
121
121
  const a = process.argv.slice(2);
122
122
  for (let i = 0; i < a.length; i++) {
123
123
  const x = a[i];
@@ -685,22 +685,9 @@ async function resolveInstallOptions(root, opts = {}) {
685
685
  lang = a === '2' ? 'ko' : a === '3' ? 'en' : detectLanguageValue(root, 'auto');
686
686
  }
687
687
  }
688
- // 1.9.146: 스킬 라이브러리 표준 공식 추천 자동 설치 / 건너뛰기 2-option 단순화 (사용자 명시 요청 #1)
689
- if (shouldAsk && !explicitSkills) {
690
- if (useInteractive) {
691
- const opt = await _selectOne('스킬 라이브러리 설치 (표준 공식 5종)', [
692
- { label: '표준 공식 5종 자동 설치 (추천)', description: 'office · commerce-api · ai-verified-skill-publisher · feature-implementation · project-roadmap-generator', id: 'recommended' },
693
- { label: '건너뛰기 (필요할 때 leerness skill install 로 추가)', description: '하네스만 설치, 스킬은 없음', id: 'none' }
694
- ], { defaultIndex: 0 });
695
- skills = (opt && opt.id === 'recommended') ? parseSkillsValue('recommended') : [];
696
- } else {
697
- log('\n스킬 라이브러리 설치를 선택하세요.');
698
- log('1) 표준 공식 5종 자동 설치 (추천)');
699
- log('2) 건너뛰기 (leerness skill install 로 추가 가능)');
700
- const a = await ask('선택 [1]: ');
701
- skills = (a === '2') ? [] : parseSkillsValue('recommended');
702
- }
703
- }
688
+ // 1.9.148: 스킬 prompt 제거 (사용자 명시 요청) leerness가 자동으로 공식 표준 스킬 5종 설치.
689
+ // 필요할 사용자가 leerness skill install <id> 로 추가 가능.
690
+ if (!explicitSkills) skills = parseSkillsValue('recommended');
704
691
  // 1.9.146: CLI 에이전트 활성화 선택 (사용자 명시 요청 #3 — Ollama 추가)
705
692
  // 설치 마지막에 .env.example 에 활성화 옵트인 키만 기록 (실제 토큰 입력은 사용자가 직접).
706
693
  let agentsOptIn = null;
@@ -877,21 +864,9 @@ async function install(root, opts = {}) {
877
864
  if (!has('--no-auto-roadmap')) {
878
865
  try { _autoRoadmap(root, 'install'); } catch (e) { warn('auto-roadmap 실패: ' + (e && e.message)); }
879
866
  }
880
- // 1.9.32: init 외부 AI CLI 설정 prompt (TTY + 신규 init + --no-setup-agents 미지정)
881
- const isFreshInit = !opts.migration && !opts.force;
882
- const skipSetup = has('--no-setup-agents') || has('--yes') || has('-y');
883
- if (isFreshInit && process.stdin.isTTY && !skipSetup) {
884
- try {
885
- log('');
886
- log('💡 외부 AI CLI(claude/codex/gemini/copilot)를 sub-agent로 활용하시겠습니까?');
887
- const wantSetup = await _confirm(' 지금 설정할까요? (나중에 `leerness setup-agents`로도 가능)', true);
888
- if (wantSetup) {
889
- await setupAgentsCmd(root);
890
- } else {
891
- log(' → 나중에 `leerness setup-agents .` 명령으로 설정 가능');
892
- }
893
- } catch (e) { warn('setup-agents skipped: ' + (e && e.message)); }
894
- }
867
+ // 1.9.148: 1.9.32 중복 prompt 제거 (사용자 명시 CLI 에이전트 prompt 중복).
868
+ // resolveInstallOptions (1.9.146) 이미 모든 prompt 모은 위치에 통합된 4지선다 prompt 있음.
869
+ // 별도 setupAgents 명령은 사용자가 명시적으로 `leerness setup-agents` 호출 시에만.
895
870
  }
896
871
  }
897
872
 
@@ -4784,8 +4759,9 @@ async function _selectOne(question, options, opts = {}) {
4784
4759
  // 이전 출력 지우기: options.length + 2줄 (제목 + 안내)
4785
4760
  stdout.write(`\x1b[${options.length + 2}A`);
4786
4761
  }
4787
- stdout.write(`\r${C.bold(question)}\n`);
4788
- stdout.write(`${C.dim(' ↑↓ 이동, Enter 확정, q 취소')}\n`);
4762
+ // 1.9.148 fix: question + 안내 라인에도 \x1b[2K (clear entire line) — 중첩 출력 방지 (사용자 명시 버그)
4763
+ stdout.write(`\x1b[2K\r${C.bold(question)}\n`);
4764
+ stdout.write(`\x1b[2K\r${C.dim(' ↑↓ 이동, Enter 확정, q 취소')}\n`);
4789
4765
  for (let i = 0; i < options.length; i++) {
4790
4766
  const label = typeof options[i] === 'string' ? options[i] : (options[i].label || String(options[i]));
4791
4767
  const desc = typeof options[i] === 'object' && options[i].description ? C.dim(' — ' + options[i].description) : '';
@@ -4841,8 +4817,9 @@ async function _selectMany(question, options, opts = {}) {
4841
4817
  const selected = new Set((opts.defaults || []).map(d => typeof d === 'number' ? d : options.findIndex(o => o === d || (o && o.id === d))).filter(i => i >= 0));
4842
4818
  const render = (first) => {
4843
4819
  if (!first) stdout.write(`\x1b[${options.length + 2}A`);
4844
- stdout.write(`\r${C.bold(question)}\n`);
4845
- stdout.write(`${C.dim(' ↑↓ 이동, Space 토글, a 전체, n 해제, Enter 확정, q 취소')}\n`);
4820
+ // 1.9.148 fix: question + 안내 라인에도 \x1b[2K — 중첩 출력 방지
4821
+ stdout.write(`\x1b[2K\r${C.bold(question)}\n`);
4822
+ stdout.write(`\x1b[2K\r${C.dim(' ↑↓ 이동, Space 토글, a 전체, n 해제, Enter 확정, q 취소')}\n`);
4846
4823
  for (let i = 0; i < options.length; i++) {
4847
4824
  const opt = options[i];
4848
4825
  const label = typeof opt === 'string' ? opt : (opt.label || String(opt));
@@ -7440,25 +7417,49 @@ function releasePublish(root) {
7440
7417
  }
7441
7418
 
7442
7419
  // ===== 1.9.7 A: verify-code — npm scripts 자동 감지 + evidence 자동 기록 =====
7420
+ // 1.9.148: 다중 런타임 자동 감지 강화 (3중 LLM 합의 — Codex+Gemini+GPT-5.5)
7421
+ // Node (vitest/jest/mocha), Python (pytest), Go (go test), Rust (cargo test), TypeScript (tsc)
7443
7422
  function verifyCodeCmd(root) {
7444
7423
  root = absRoot(root);
7445
- const pkgFile = path.join(root, 'package.json');
7446
- if (!exists(pkgFile)) return fail('package.json 없음 — Node 프로젝트 위치에서 실행하세요.');
7447
- let pkg;
7448
- try { pkg = JSON.parse(read(pkgFile)); } catch (e) { return fail('package.json 파싱 실패: ' + e.message); }
7449
- const scripts = pkg.scripts || {};
7450
7424
  const tasks = [];
7451
- if (scripts.test) tasks.push({ name: 'test', cmd: 'npm test' });
7452
- else if (scripts['test:smoke']) tasks.push({ name: 'test', cmd: 'npm run test:smoke' });
7453
- if (scripts.lint) tasks.push({ name: 'lint', cmd: 'npm run lint' });
7454
- if (scripts.typecheck) tasks.push({ name: 'typecheck', cmd: 'npm run typecheck' });
7455
- else if (scripts.tsc) tasks.push({ name: 'typecheck', cmd: 'npm run tsc' });
7456
- else if (exists(path.join(root, 'tsconfig.json'))) tasks.push({ name: 'typecheck', cmd: 'npx --yes tsc --noEmit', optional: true });
7457
- if (has('--build') && scripts.build) tasks.push({ name: 'build', cmd: 'npm run build' });
7458
- // 1.9.20: --bench scripts.bench 자동 실행 (성능 metric을 evidence에 누적)
7459
- if (has('--bench') && scripts.bench) tasks.push({ name: 'bench', cmd: 'npm run bench', optional: true });
7425
+ // (1) Node: package.json 우선
7426
+ const pkgFile = path.join(root, 'package.json');
7427
+ if (exists(pkgFile)) {
7428
+ let pkg = {};
7429
+ try { pkg = JSON.parse(read(pkgFile)); } catch (e) { return fail('package.json 파싱 실패: ' + e.message); }
7430
+ const scripts = pkg.scripts || {};
7431
+ const deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
7432
+ if (scripts.test) tasks.push({ name: 'test', cmd: 'npm test', runtime: 'node' });
7433
+ else if (scripts['test:smoke']) tasks.push({ name: 'test', cmd: 'npm run test:smoke', runtime: 'node' });
7434
+ // 1.9.148: 명시 script 없어도 인기 러너 의존성 발견 시 시도
7435
+ else if (deps.vitest) tasks.push({ name: 'test', cmd: 'npx --yes vitest run', runtime: 'node' });
7436
+ else if (deps.jest) tasks.push({ name: 'test', cmd: 'npx --yes jest --ci', runtime: 'node' });
7437
+ else if (deps.mocha) tasks.push({ name: 'test', cmd: 'npx --yes mocha', runtime: 'node' });
7438
+ if (scripts.lint) tasks.push({ name: 'lint', cmd: 'npm run lint', runtime: 'node' });
7439
+ if (scripts.typecheck) tasks.push({ name: 'typecheck', cmd: 'npm run typecheck', runtime: 'node' });
7440
+ else if (scripts.tsc) tasks.push({ name: 'typecheck', cmd: 'npm run tsc', runtime: 'node' });
7441
+ else if (exists(path.join(root, 'tsconfig.json'))) tasks.push({ name: 'typecheck', cmd: 'npx --yes tsc --noEmit', runtime: 'node', optional: true });
7442
+ if (has('--build') && scripts.build) tasks.push({ name: 'build', cmd: 'npm run build', runtime: 'node' });
7443
+ if (has('--bench') && scripts.bench) tasks.push({ name: 'bench', cmd: 'npm run bench', runtime: 'node', optional: true });
7444
+ }
7445
+ // (2) Python: pyproject.toml / setup.py / tests/ 존재 시 pytest 시도
7446
+ if (exists(path.join(root, 'pyproject.toml')) || exists(path.join(root, 'setup.py')) || exists(path.join(root, 'tests'))) {
7447
+ if (!tasks.find(t => t.name === 'test')) tasks.push({ name: 'test', cmd: 'pytest -q', runtime: 'python', optional: true });
7448
+ }
7449
+ // (3) Go: go.mod 존재 시 go test ./...
7450
+ if (exists(path.join(root, 'go.mod'))) {
7451
+ tasks.push({ name: 'test:go', cmd: 'go test ./...', runtime: 'go' });
7452
+ }
7453
+ // (4) Rust: Cargo.toml 존재 시 cargo test
7454
+ if (exists(path.join(root, 'Cargo.toml'))) {
7455
+ tasks.push({ name: 'test:rust', cmd: 'cargo test', runtime: 'rust' });
7456
+ }
7460
7457
  if (!tasks.length) {
7461
- warn('실행할 검증 task 없음 (package.json#scripts에 test/lint/typecheck 추가하세요)');
7458
+ // 1.9.148: --strict 또는 LEERNESS_AUTONOMOUS=1 시 no-test 도 실패로 (3중 LLM 합의: production-grade test 강제)
7459
+ const strict = has('--strict') || process.env.LEERNESS_AUTONOMOUS === '1';
7460
+ const msg = '검증 task 없음 (package.json#scripts test/lint/typecheck, pytest, go test, cargo test 중 하나도 미발견)';
7461
+ if (strict) { fail(msg + ' — --strict/autonomous 모드: 실패 처리 (exit 1)'); process.exitCode = 1; return; }
7462
+ warn(msg);
7462
7463
  return;
7463
7464
  }
7464
7465
  log(`# verify-code (${tasks.length}개)`);
@@ -9972,16 +9973,25 @@ async function _ollamaChat(prompt, model) {
9972
9973
  } catch (e) { resolve({ ok: false, error: e.message, model: mdl }); }
9973
9974
  });
9974
9975
  }
9976
+ // 1.9.148: planner/reviewer/actor 역할 시스템 프롬프트 (Gemini 권고 — 자기-승인 편향 방지)
9977
+ const _AGENT_ROLE_PROMPTS = {
9978
+ planner: '역할: planner. task를 step 3-6개로 분해, 각 step의 입출력/검증 방법 명시. 코드 작성 금지, 계획만.',
9979
+ reviewer: '역할: reviewer. planner 의 계획 또는 actor 의 결과를 비판적으로 검토. 누락된 검증, 잠재 cascade, 오류 가능성 지적. 동의/수정 결론 명시.',
9980
+ actor: '역할: actor. 계획에 따라 정확한 명령/코드만 실행. evidence(파일 경로 + 테스트 결과) 함께 기록. 새 계획 생성 금지.'
9981
+ };
9975
9982
  async function agentCmd(root, taskArg) {
9976
9983
  root = absRoot(root || process.cwd());
9977
9984
  const task = (taskArg || arg('--task', '') || '').trim();
9978
9985
  if (!task) {
9979
- log('# leerness agent (1.9.146) — 오픈소스 CLI 에이전트 모드');
9986
+ log('# leerness agent (1.9.146/148) — 오픈소스 CLI 에이전트 모드');
9980
9987
  log('');
9981
9988
  log('사용법:');
9982
- log(' leerness agent "<task 설명>" # 1회 위임');
9983
- log(' leerness agent --provider ollama # 명시적 provider 선택');
9984
- log(' leerness agent --dry-run # LLM 호출 없이 흐름만 확인');
9989
+ log(' leerness agent "<task>" # 1회 위임 (actor 역할 기본)');
9990
+ log(' leerness agent "<task>" --role planner # 계획만, 코드 작성 없음 (1.9.148)');
9991
+ log(' leerness agent "<task>" --role reviewer # 비판적 검토 (1.9.148)');
9992
+ log(' leerness agent "<task>" --role actor # 계획대로 실행');
9993
+ log(' leerness agent "<task>" --provider ollama # provider 선택');
9994
+ log(' leerness agent "<task>" --dry-run # LLM 호출 없이 흐름만');
9985
9995
  log('');
9986
9996
  log('현재 활성 provider: ' + (_activeCliAgents().join(', ') || '(없음) — .env에서 LEERNESS_ENABLE_* 활성화'));
9987
9997
  log('권한 모드: ' + (_readPermissions(root).mode || 'basic'));
@@ -9989,10 +9999,13 @@ async function agentCmd(root, taskArg) {
9989
9999
  }
9990
10000
  const dryRun = has('--dry-run');
9991
10001
  const providerArg = arg('--provider', null);
10002
+ const role = arg('--role', 'actor'); // 1.9.148
10003
+ const rolePrompt = _AGENT_ROLE_PROMPTS[role] || _AGENT_ROLE_PROMPTS.actor;
9992
10004
  const active = _activeCliAgents();
9993
10005
  const provider = providerArg || active[0] || null;
9994
- log(`# leerness agent (1.9.146)`);
10006
+ log(`# leerness agent (1.9.146/148)`);
9995
10007
  log(`task: ${task.slice(0, 120)}${task.length > 120 ? '…' : ''}`);
10008
+ log(`role: ${role} (${rolePrompt.split('. ')[1] || rolePrompt.slice(0, 60)})`);
9996
10009
  log(`provider: ${provider || '(없음 — .env 에서 LEERNESS_ENABLE_* 활성화 필요)'}`);
9997
10010
  const perms = _readPermissions(root);
9998
10011
  log(`permission mode: ${perms.mode || 'basic'}`);
@@ -10009,13 +10022,15 @@ async function agentCmd(root, taskArg) {
10009
10022
  // MVP: Ollama 지원 (로컬). 다른 CLI 는 사용자가 직접 호출 (leerness agents dispatch 이미 존재).
10010
10023
  if (provider === 'ollama') {
10011
10024
  log('\n[ollama 호출 중...]');
10012
- const r = await _ollamaChat(task);
10025
+ // 1.9.148: role prompt 자동 prepend
10026
+ const finalPrompt = `${rolePrompt}\n\nTask: ${task}`;
10027
+ const r = await _ollamaChat(finalPrompt);
10013
10028
  if (r.ok) {
10014
- log('\n[response (model=' + r.model + ')]\n' + r.response);
10029
+ log('\n[response (model=' + r.model + ', role=' + role + ')]\n' + r.response);
10015
10030
  // task-log 자동 기록
10016
10031
  try {
10017
10032
  const tlp = taskLogPath(root);
10018
- const block = `\n## ${today()} leerness agent (ollama:${r.model})\n- task: ${task.slice(0, 200)}\n- response (preview): ${r.response.slice(0, 240).replace(/\n+/g, ' ')}\n`;
10033
+ 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`;
10019
10034
  append(tlp, block);
10020
10035
  } catch {}
10021
10036
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leerness",
3
- "version": "1.9.147",
3
+ "version": "1.9.148",
4
4
  "description": "Leerness: 비파괴 마이그레이션, 자동 버전 감지·업데이트, 계획/진행/핸드오프 자동화, 게으름·시크릿·인코딩 자동 가드, Claude Code 슬래시 통합을 갖춘 한국어 우선 AI 개발 하네스.",
5
5
  "keywords": [
6
6
  "leerness",