leerness 1.36.41 → 1.36.42
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 +11 -0
- package/README.md +4 -4
- package/bin/leerness.js +74 -18
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.36.42 — 2026-07-16 — 판정 정직화 배치 C — codex 5차 헌트 10건 전건 처분 완결 (#4~#10)
|
|
4
|
+
|
|
5
|
+
- **#4 agent-mode**: 3개 자식 전부 실패해도 "자율 라운드 진행 준비" 선언 + exit 0 — 전부 실패=exit 1, 부분 실패=⚠ 명시, tick/stop 자식 실패 전파, 미지 하위명령 exit 1.
|
|
6
|
+
- **#5 orchestrate**: `--agents NaN/0/-1` 이 조용히 1로 화하고 전원 실패에도 exit 0 → 정수 1~256 검증(`invalid_agents`) + 성공 0=exit 1.
|
|
7
|
+
- **#6 release bump/note**: "banana" 를 0.0.1 로 덮어쓰기 → semver 검증 후 거부(`invalid_version`, 원본 보존) · prerelease 는 npm 의미론(1.2.3-beta.2→1.2.3 안정화) · 멀티라인 노트가 CHANGELOG 에 가짜 버전 헤더를 만들던 것 → 한 리스트 항목으로 정규화.
|
|
8
|
+
- **#7 consistency merge-design-guide**: `--dry-run` 실기록 + 재실행마다 중복 append → dry-run 무기록 + 소스별 멱등(이미 병합 skip 표시).
|
|
9
|
+
- **#8 whats-new `--to`**: value-flag 미등록으로 값이 positional 경로로 흡수돼 **엉뚱한(패키지 내장) CHANGELOG 를 읽던 것** → 등록. 실측: --to 지정 시에도 로컬 CHANGELOG.
|
|
10
|
+
- **#9 lens 커스텀**: 손상 quality-lenses.json 무언 무시 → 경고 노출(advisory 유지) · `constructor` 도메인명이 프로토타입 상속 키로 오인돼 크래시 → 자기 소유 키 검사.
|
|
11
|
+
- **#10 register-pending `--json`**: 사람용 3줄이 JSON 앞에 섞여 파서 실패 → 단일 JSON 문서. (review/persona/guide 의 JSON 계약은 별도 라운드 — 출력 구조 재설계 필요라 이연 명기.)
|
|
12
|
+
- **검증**: selftest 312/312, 13케이스 일괄 실증 스크립트 전부 ✓, exit 전파형 게이트 e2e, 클린룸. **codex 5차 10건 전건 처분(수정 10, guide-json 만 부분 이연).**
|
|
13
|
+
|
|
3
14
|
## 1.36.41 — 2026-07-16 — 데이터 보존 3종 2차 — .mcp.json 손상 중단 · register-pending 락 · glossary 마커 보존 (codex 5차 헌트 #1/#2/#3 High)
|
|
4
15
|
|
|
5
16
|
5차 헌트(adapter/ci/orchestrate/guide/doctor/glossary/consistency/release/lens 표면)의 High 3건 — 전부 기존 결함 클래스의 새 발화점:
|
package/README.md
CHANGED
|
@@ -121,7 +121,7 @@ MIT
|
|
|
121
121
|
<!-- leerness:project-readme:start -->
|
|
122
122
|
## Leerness Project Harness
|
|
123
123
|
|
|
124
|
-
이 프로젝트는 Leerness v1.36.
|
|
124
|
+
이 프로젝트는 Leerness v1.36.42 하네스를 사용합니다. AI 에이전트는 작업 전 `leerness handoff`로 컨텍스트를 적재하고, 작업 후 `leerness check`/`leerness audit`/`leerness session close`를 수행해야 합니다.
|
|
125
125
|
|
|
126
126
|
### 정체성 — AI 에이전트 운영 레이어 (UR-0030)
|
|
127
127
|
|
|
@@ -175,7 +175,7 @@ leerness memory restore decision <date|title>
|
|
|
175
175
|
|
|
176
176
|
### MCP server (외부 AI 통합)
|
|
177
177
|
|
|
178
|
-
Leerness v1.36.
|
|
178
|
+
Leerness v1.36.42는 stdio JSON-RPC MCP server를 내장합니다 — Claude Code · Cursor · Codex CLI 등 외부 AI에 **86개 도구**를 노출:
|
|
179
179
|
|
|
180
180
|
```jsonc
|
|
181
181
|
// 카테고리별
|
|
@@ -196,7 +196,7 @@ Leerness v1.36.41는 stdio JSON-RPC MCP server를 내장합니다 — Claude Cod
|
|
|
196
196
|
`<<autonomous-loop-dynamic>>` 신호만 보내면 AI가:
|
|
197
197
|
1) 다음 라운드 후보 선정 → 2) 코드 변경 → 3) 회귀 테스트 갱신 → 4) 전체 e2e 스위트 통과 → 5) npm publish + git tag → 6) main push → 7) session close → 8) 다음 라운드 예약.
|
|
198
198
|
|
|
199
|
-
현재 누적: **v1.9.x → 1.36.
|
|
199
|
+
현재 누적: **v1.9.x → 1.36.42 릴리스 태그 이력** (수백 라운드) · _reports/는 비공개 보존.
|
|
200
200
|
|
|
201
201
|
### 성능 가이드
|
|
202
202
|
|
|
@@ -234,6 +234,6 @@ leerness release pack --close --auto-main-push
|
|
|
234
234
|
- `.harness/session-handoff.md`: 다음 세션 인수인계 (자동 작성)
|
|
235
235
|
- `.harness/lessons.md` / `decisions.md` / `rules.md`: 영구 메모리 (5 surface)
|
|
236
236
|
|
|
237
|
-
Last synced by Leerness v1.36.
|
|
237
|
+
Last synced by Leerness v1.36.42: 2026-07-16
|
|
238
238
|
<!-- leerness:project-readme:end -->
|
|
239
239
|
|
package/bin/leerness.js
CHANGED
|
@@ -34,7 +34,7 @@ const { CAPABILITY_SURFACE, POWERFUL_COMMANDS, ADAPTERS, REUSE_CATEGORIES, REUSE
|
|
|
34
34
|
const { tokenizeForRank: _tokenizeForRank, expandQuery: _expandQuery, scoreHits: _scoreHits, suggestTerms: _suggestTerms } = require('../lib/search-core'); // 1.36.23: memory search 랭킹 코어(순수·0-deps)
|
|
35
35
|
const { findCorruptedStateJson: _findCorruptedStateJson } = require('../lib/state-integrity'); // 1.36.1 (클린룸 리뷰 FN): .harness/*.json 상태 무결성 (audit/health/check 공유)
|
|
36
36
|
|
|
37
|
-
const VERSION = '1.36.
|
|
37
|
+
const VERSION = '1.36.42';
|
|
38
38
|
|
|
39
39
|
// 1.9.290 (UR-0037, Codex gpt-5.5 #4 수렴): CLI 전용 부작용은 require 시 실행하지 않는다.
|
|
40
40
|
// 이전: warning listener 제거 / NODE_OPTIONS 변경 / chcp IIFE 가 top-level 즉시 실행 → require('harness') 시 호스트 프로세스 오염.
|
|
@@ -218,7 +218,7 @@ function _resolveRoot(positional) {
|
|
|
218
218
|
}
|
|
219
219
|
function nonFlagArgs() {
|
|
220
220
|
const out = [];
|
|
221
|
-
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','--keep','--shell','--ps-version','--done-when','--test-cmd']); // 1.14.2 (UR-0032): --done-when 값이 positional 로 누출돼 milestone 제목에 흡수되던 것 차단. 1.17.2 (UR-0045): --test-cmd 동일 원칙(신규 value-flag 는 반드시 여기 등록)
|
|
221
|
+
const withValue = new Set(['--language','--skills','--path','--status','--progress','--goal','--reason','--next','--target','--token-env','--package','--out','--from','--to','--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','--keep','--shell','--ps-version','--done-when','--test-cmd']); // 1.14.2 (UR-0032): --done-when 값이 positional 로 누출돼 milestone 제목에 흡수되던 것 차단. 1.17.2 (UR-0045): --test-cmd 동일 원칙(신규 value-flag 는 반드시 여기 등록)
|
|
222
222
|
const a = process.argv.slice(2);
|
|
223
223
|
for (let i = 0; i < a.length; i++) {
|
|
224
224
|
const x = a[i];
|
|
@@ -2396,7 +2396,11 @@ function agentModeCmd(root, sub) {
|
|
|
2396
2396
|
const r3 = spawnChild(['handoff', root]);
|
|
2397
2397
|
if (r3.status === 0) log(gr(` ✓ handoff 출력 완료 (위 참조)`)); else log(yl(` ⚠ handoff 종료 ${r3.status}`));
|
|
2398
2398
|
log('');
|
|
2399
|
-
|
|
2399
|
+
// 1.36.42 (codex 5차 #4): 세 자식 전부 실패(status null 포함)여도 "준비" 를 선언하던 허위 성공 — 실패 수 기준 정직 판정.
|
|
2400
|
+
const _fails = [r1, r2, r3].filter(r => r.status !== 0).length;
|
|
2401
|
+
if (_fails === 3) { fail(`agent-mode start 실패 — 3개 단계 전부 실패 (경로/설치 확인: ${root})`); process.exitCode = 1; return; }
|
|
2402
|
+
if (_fails > 0) { log(yl(`⚠ agent-mode start 부분 완료 — ${_fails}개 단계 실패 (위 ⚠ 참조)`)); }
|
|
2403
|
+
else log(gr(`✓ agent-mode start 완료 — 자율 라운드 진행 준비`));
|
|
2400
2404
|
return;
|
|
2401
2405
|
}
|
|
2402
2406
|
if (sub === 'tick') {
|
|
@@ -2404,6 +2408,7 @@ function agentModeCmd(root, sub) {
|
|
|
2404
2408
|
log(cy(`# leerness agent-mode tick (1.9.239) — 가벼운 상태 확인`));
|
|
2405
2409
|
const r = spawnChild(['pulse', '--path', root]);
|
|
2406
2410
|
process.stdout.write(r.stdout || '');
|
|
2411
|
+
if (r.status !== 0) process.exitCode = 1; // 1.36.42 (#4): 자식 실패 전파
|
|
2407
2412
|
return;
|
|
2408
2413
|
}
|
|
2409
2414
|
if (sub === 'stop') {
|
|
@@ -2411,10 +2416,12 @@ function agentModeCmd(root, sub) {
|
|
|
2411
2416
|
log(dm(` → session close --auto-apply-delivered --auto-cleanup-branches --auto-fix-encoding`));
|
|
2412
2417
|
const r = spawnChild(['session', 'close', root, '--auto-apply-delivered', '--auto-cleanup-branches', '--auto-fix-encoding']);
|
|
2413
2418
|
process.stdout.write(r.stdout || '');
|
|
2419
|
+
if (r.status !== 0) process.exitCode = 1; // 1.36.42 (#4): 자식 실패 전파
|
|
2414
2420
|
return;
|
|
2415
2421
|
}
|
|
2416
|
-
|
|
2422
|
+
fail(`unknown subcommand: ${sub}`); // 1.36.42 (#4): 미지 하위명령 exit 1 (종전 exit 0)
|
|
2417
2423
|
log(dm(` → leerness agent-mode help`));
|
|
2424
|
+
process.exitCode = 1;
|
|
2418
2425
|
}
|
|
2419
2426
|
|
|
2420
2427
|
// 1.9.234: recentChanges — 최근 N 라운드의 핵심 변경 요약 (git tag commit subject)
|
|
@@ -3291,6 +3298,22 @@ function _selfTestCases() {
|
|
|
3291
3298
|
} catch {} finally { try { fs.rmSync(tmp, { recursive: true, force: true }); } catch {} }
|
|
3292
3299
|
return mcpGuard && rpLock && gOk;
|
|
3293
3300
|
} },
|
|
3301
|
+
{ name: '판정 정직화 배치C (1.36.42, codex 5차 #4~#10): agent-mode/orchestrate exit + release semver/note + consistency dry-run·멱등 + whats-new --to + lens 커스텀 + JSON 순수 (소스가드+행위)', run: () => {
|
|
3302
|
+
const s = read(__filename);
|
|
3303
|
+
const amOk = s.includes('if (_fails === 3) { fail(`agent-mode start 실패') && s.includes('미지 하위명령 exit 1');
|
|
3304
|
+
const orOk = s.includes("'invalid_agents'") && s.includes('if (!ok.length) process.exitCode = 1;');
|
|
3305
|
+
const relOk = s.includes("'invalid_version'") && s.includes('멀티라인은 한 리스트 항목의 연속 들여쓰기로 정규화');
|
|
3306
|
+
const toOk = s.includes("'--from','--to','--repo'");
|
|
3307
|
+
const conOk = s.includes('이미 병합됨(skip)') && /mergeDesign[\s\S]{0,600}has\('--dry-run'\)/.test(s);
|
|
3308
|
+
const lensOk = s.includes('function _loadProjectLensesEx') && s.includes('Object.prototype.hasOwnProperty.call(out, k)');
|
|
3309
|
+
const rpJsonOk = /registerPendingCmd[\s\S]{0,1600}if \(has\('--json'\)\) \{ log\(JSON\.stringify\(\{ ok: true, id, \.\.\.row \}, null, 2\)\); return; \}/.test(s);
|
|
3310
|
+
// 행위: prerelease 안정화 + 프로토타입 키 무크래시
|
|
3311
|
+
const p = require('../lib/pure-utils');
|
|
3312
|
+
const preOk = p.compareVer('1.2.3', '1.2.3-beta.2') === 1; // bump 결과가 상위인지 간접 확인
|
|
3313
|
+
let protoOk = false;
|
|
3314
|
+
try { const m = _mergeLensCatalog(LENS_CATALOG, { constructor: { title: 'C', persona: 'r', questions: ['q1'] } }); protoOk = !!(m.constructor && Array.isArray(m.constructor.questions) && m.constructor.questions[0] === 'q1'); } catch {}
|
|
3315
|
+
return amOk && orOk && relOk && toOk && conOk && lensOk && rpJsonOk && preOk && protoOk;
|
|
3316
|
+
} },
|
|
3294
3317
|
{ name: 'debug 렌즈 (1.36.27, obra/superpowers systematic-debugging): 자기질문 6문항 ko/en 락스텝 + affects 유효 + route bugfix 힌트 + 파일매핑 미확장 — 행위검사', run: () => {
|
|
3295
3318
|
const d = LENS_CATALOG.debug;
|
|
3296
3319
|
if (!d) return false;
|
|
@@ -5579,12 +5602,18 @@ const LENS_CATALOG = {
|
|
|
5579
5602
|
};
|
|
5580
5603
|
// 1.19.3 (UR-0003 렌즈 완전판 v3): 프로젝트별 커스텀 렌즈 — .harness/quality-lenses.json 읽기-병합(쓰기 명령 없음, AI/사용자가 편집).
|
|
5581
5604
|
// 포맷: { "domains": { "code": { "questions": ["추가 질문"] }, "a11y": { "title":"접근성", "persona":"스크린리더 사용자", "questions":[...], "affects":["design"] } } }
|
|
5582
|
-
|
|
5605
|
+
// 1.36.42 (codex 5차 #9): 손상 파일을 무언 무시하면 사용자는 커스텀 렌즈가 적용된 줄 안다 — 에러를 함께 반환(Ex), 기존 시그니처는 래퍼.
|
|
5606
|
+
function _loadProjectLensesEx(root) {
|
|
5607
|
+
const p = path.join(absRoot(root || process.cwd()), '.harness', 'quality-lenses.json');
|
|
5608
|
+
if (!exists(p)) return { domains: {}, error: null };
|
|
5583
5609
|
try {
|
|
5584
|
-
const p = path.join(absRoot(root || process.cwd()), '.harness', 'quality-lenses.json');
|
|
5585
|
-
if (!exists(p)) return {};
|
|
5586
5610
|
const j = JSON.parse(read(p));
|
|
5587
|
-
return (j && typeof j === 'object' && j.domains && typeof j.domains === 'object') ? j.domains : {};
|
|
5611
|
+
return { domains: (j && typeof j === 'object' && j.domains && typeof j.domains === 'object') ? j.domains : {}, error: null };
|
|
5612
|
+
} catch (e) { return { domains: {}, error: `quality-lenses.json 파싱 실패(커스텀 렌즈 미적용): ${e.message}` }; }
|
|
5613
|
+
}
|
|
5614
|
+
function _loadProjectLenses(root) {
|
|
5615
|
+
try {
|
|
5616
|
+
return _loadProjectLensesEx(root).domains;
|
|
5588
5617
|
} catch { return {}; }
|
|
5589
5618
|
}
|
|
5590
5619
|
// 내장 + 프로젝트 커스텀 병합. 기존 도메인 → 질문 추가(dedup, 최대 16 — 내장 최대치(database 12) + 커스텀 여유; 낮으면 내장이 잘림); 신규 도메인 → 기본값으로 추가. _custom 플래그로 표시.
|
|
@@ -5594,7 +5623,9 @@ function _mergeLensCatalog(builtin, custom) {
|
|
|
5594
5623
|
for (const [k, c] of Object.entries(custom || {})) {
|
|
5595
5624
|
if (!c || typeof c !== 'object') continue;
|
|
5596
5625
|
const cq = Array.isArray(c.questions) ? c.questions.filter(q => typeof q === 'string' && q.trim()) : [];
|
|
5597
|
-
|
|
5626
|
+
// 1.36.42 (codex 5차 #9b): truthy 검사는 'constructor' 같은 프로토타입 키에서 상속 함수를 기존 도메인으로
|
|
5627
|
+
// 오인해 .questions.push 크래시 — 자기 소유 키만 병합 대상으로.
|
|
5628
|
+
if (Object.prototype.hasOwnProperty.call(out, k)) {
|
|
5598
5629
|
const seen = new Set(out[k].questions);
|
|
5599
5630
|
for (const q of cq) if (!seen.has(q)) { out[k].questions.push(q); seen.add(q); }
|
|
5600
5631
|
const before = out[k].questions.length;
|
|
@@ -5678,6 +5709,8 @@ function lensCmd(domain, opts = {}) {
|
|
|
5678
5709
|
if (domain != null) domain = String(domain).trim().toLowerCase();
|
|
5679
5710
|
// 1.19.3: 내장 + 프로젝트 커스텀(.harness/quality-lenses.json) 병합 catalog.
|
|
5680
5711
|
const root = opts.root || arg('--path', process.cwd());
|
|
5712
|
+
// 1.36.42 (#9): 손상 커스텀 렌즈 파일은 무언 무시 대신 경고 (advisory — lens 자체는 계속 동작)
|
|
5713
|
+
try { const _lx = _loadProjectLensesEx(root); if (_lx.error && !jsonMode) warn(_lx.error); } catch {}
|
|
5681
5714
|
// 1.36.30: 토글 준수 (⚙ 탭 / leerness toggle set lens off)
|
|
5682
5715
|
if (!_tgl.toggleOn(root, 'lens')) {
|
|
5683
5716
|
if (jsonMode) { log(JSON.stringify({ ok: true, skipped: true, reason: 'toggle_off', hint: 'leerness toggle set lens on' })); return; }
|
|
@@ -12393,7 +12426,11 @@ async function orchestrateCmd(root, goalParts) {
|
|
|
12393
12426
|
return process.exit(1);
|
|
12394
12427
|
}
|
|
12395
12428
|
|
|
12396
|
-
|
|
12429
|
+
// 1.36.42 (codex 5차 #5): NaN/0/-1 이 조용히 1(또는 NaN 루프)로 화하던 것 — 정수 1..256 검증.
|
|
12430
|
+
const _agRaw = arg('--agents', '4');
|
|
12431
|
+
const _agN = Number(_agRaw);
|
|
12432
|
+
if (!Number.isInteger(_agN) || _agN < 1 || _agN > 256) { failJson(has('--json'), 'invalid_agents', `--agents 는 1~256 정수여야 합니다 (받음: ${_agRaw})`); return; }
|
|
12433
|
+
const agentCount = _agN;
|
|
12397
12434
|
const model = arg('--model', process.env.LEERNESS_OLLAMA_MODEL || 'qwen2.5:7b-instruct');
|
|
12398
12435
|
const timeoutMs = parseInt(arg('--timeout', '300000'), 10);
|
|
12399
12436
|
const retryOnFail = parseInt(arg('--retry-on-fail', '0'), 10); // 1.9.22 후보 2 통합
|
|
@@ -12428,6 +12465,7 @@ async function orchestrateCmd(root, goalParts) {
|
|
|
12428
12465
|
if (failures.length) {
|
|
12429
12466
|
for (const f of failures.slice(0, 3)) log(` · agent ${f.agent}: ${f.error}`);
|
|
12430
12467
|
}
|
|
12468
|
+
if (!ok.length) process.exitCode = 1; // 1.36.42 (#5): 전원 실패인데 exit 0 이던 허위 성공 — 성공 0 = exit 1
|
|
12431
12469
|
|
|
12432
12470
|
if (ok.length) {
|
|
12433
12471
|
const totalPromptTokens = ok.reduce((a, b) => a + b.promptTokens, 0);
|
|
@@ -14014,10 +14052,11 @@ function registerPendingCmd(root, requestParts) {
|
|
|
14014
14052
|
return rec;
|
|
14015
14053
|
});
|
|
14016
14054
|
const id = row.id;
|
|
14055
|
+
// 1.36.42 (codex 5차 #10): --json 인데 사람용 3줄이 앞에 섞여 파서가 깨짐 — JSON 모드는 단일 JSON 문서만.
|
|
14056
|
+
if (has('--json')) { log(JSON.stringify({ ok: true, id, ...row }, null, 2)); return; }
|
|
14017
14057
|
log(`✓ ${id} 등록됨 (in-progress) by ${agent}`);
|
|
14018
14058
|
log(` request: ${row.request}`);
|
|
14019
14059
|
log(` 💡 작업 완료 후: leerness task update ${id} --status done --evidence "..."`);
|
|
14020
|
-
if (has('--json')) log(JSON.stringify({ ok: true, id, ...row }, null, 2));
|
|
14021
14060
|
}
|
|
14022
14061
|
|
|
14023
14062
|
// 1.9.22 후보 4: llm-bench record + retro 통합
|
|
@@ -14060,10 +14099,20 @@ function mergeDesign(root) {
|
|
|
14060
14099
|
root = absRoot(root);
|
|
14061
14100
|
const canonical = path.join(root,'.harness/design-system.md');
|
|
14062
14101
|
const cands = ['designguide.md','design-guide.md','.harness/designguide.md','docs/designguide.md','docs/design-guide.md'];
|
|
14102
|
+
// 1.36.42 (codex 5차 #7): (a) --dry-run 인데 실기록, (b) 재실행마다 같은 내용 중복 append(비멱등) — 둘 다 교정.
|
|
14103
|
+
const dry = has('--dry-run');
|
|
14104
|
+
const already = exists(canonical) ? read(canonical) : '';
|
|
14063
14105
|
let merged = '';
|
|
14064
|
-
|
|
14106
|
+
const skipped = [];
|
|
14107
|
+
for (const f of cands) {
|
|
14108
|
+
const p = path.join(root,f);
|
|
14109
|
+
if (!exists(p)) continue;
|
|
14110
|
+
if (already.includes(`## Merged from ${f}`)) { skipped.push(f); continue; } // 멱등: 같은 소스 재병합 방지
|
|
14111
|
+
merged += `\n\n## Merged from ${f}\n\n` + read(p);
|
|
14112
|
+
}
|
|
14113
|
+
if (dry) { ok(`[dry-run] ${merged ? '병합 예정' : '병합 대상 없음'}${skipped.length ? ` · 이미 병합됨(skip): ${skipped.join(', ')}` : ''} — 실제 변경 0`); return; }
|
|
14065
14114
|
if (merged) append(canonical, merged);
|
|
14066
|
-
ok(merged ?
|
|
14115
|
+
ok(merged ? `design guides merged into .harness/design-system.md${skipped.length ? ` (이미 병합 skip: ${skipped.join(', ')})` : ''}` : (skipped.length ? `nothing to merge (이미 전부 병합됨: ${skipped.join(', ')})` : 'nothing to merge'));
|
|
14067
14116
|
}
|
|
14068
14117
|
|
|
14069
14118
|
// 1.9.2: self check를 update --check의 thin wrapper로 통합 (단일 출처).
|
|
@@ -15526,12 +15575,16 @@ function releaseBump(root) {
|
|
|
15526
15575
|
if (!exists(pkgFile)) return fail('package.json 없음');
|
|
15527
15576
|
let pkg; try { pkg = JSON.parse(read(pkgFile)); } catch (e) { return fail('package.json 파싱 실패: ' + e.message); }
|
|
15528
15577
|
const cur = String(pkg.version || '0.0.0');
|
|
15529
|
-
|
|
15530
|
-
|
|
15578
|
+
// 1.36.42 (codex 5차 #6): (a) 비 semver("banana")를 0.0.0 으로 오인해 0.0.1 로 덮어쓰던 것 → 거부.
|
|
15579
|
+
// (b) prerelease patch bump 는 npm 의미론(1.2.3-beta.2 → 1.2.3: pre 태그 제거=안정화)을 따름.
|
|
15580
|
+
const _vm = cur.match(/^(\d+)\.(\d+)\.(\d+)(-[0-9A-Za-z.-]+)?$/);
|
|
15581
|
+
if (!_vm) { failJson(has('--json'), 'invalid_version', `package.json version 이 semver 가 아님: "${cur}" — bump 거부(수동 교정 필요)`); return; }
|
|
15582
|
+
const [maj, min, pat] = [parseInt(_vm[1], 10), parseInt(_vm[2], 10), parseInt(_vm[3], 10)];
|
|
15583
|
+
const isPre = !!_vm[4];
|
|
15531
15584
|
let next;
|
|
15532
|
-
if (kind === 'major') next = `${maj + 1}.0.0`;
|
|
15533
|
-
else if (kind === 'minor') next = `${maj}.${min + 1}.0`;
|
|
15534
|
-
else next = `${maj}.${min}.${pat + 1}`;
|
|
15585
|
+
if (kind === 'major') next = isPre && min === 0 && pat === 0 ? `${maj}.0.0` : `${maj + 1}.0.0`;
|
|
15586
|
+
else if (kind === 'minor') next = isPre && pat === 0 ? `${maj}.${min}.0` : `${maj}.${min + 1}.0`;
|
|
15587
|
+
else next = isPre ? `${maj}.${min}.${pat}` : `${maj}.${min}.${pat + 1}`;
|
|
15535
15588
|
pkg.version = next;
|
|
15536
15589
|
writeUtf8(pkgFile, JSON.stringify(pkg, null, 2) + '\n');
|
|
15537
15590
|
const hv = path.join(root, '.harness/HARNESS_VERSION');
|
|
@@ -15542,6 +15595,9 @@ function releaseBump(root) {
|
|
|
15542
15595
|
function releaseNote(root, text) {
|
|
15543
15596
|
root = absRoot(root);
|
|
15544
15597
|
if (!text) return fail('note text required (e.g., release note "내용")');
|
|
15598
|
+
// 1.36.42 (codex 5차 #6b): 개행 포함 노트가 CHANGELOG 에 가짜 버전 헤더("## 9.9.9 …")를 만들 수 있었음 —
|
|
15599
|
+
// 멀티라인은 한 리스트 항목의 연속 들여쓰기로 정규화(헤더 문자로 시작하는 줄도 텍스트로 유지).
|
|
15600
|
+
text = String(text).replace(/\r\n?/g, '\n').split('\n').map((l, i) => i === 0 ? l : ' ' + l.replace(/^#+\s*/, '')).join('\n');
|
|
15545
15601
|
const pkgFile = path.join(root, 'package.json');
|
|
15546
15602
|
let version = 'unknown';
|
|
15547
15603
|
if (exists(pkgFile)) { try { version = JSON.parse(read(pkgFile)).version || 'unknown'; } catch {} }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "leerness",
|
|
3
|
-
"version": "1.36.
|
|
3
|
+
"version": "1.36.42",
|
|
4
4
|
"description": "The AI-coding operations layer that makes \"done\" require evidence — persistent memory, evidence-gated completion checks, and clean handoffs for any AI agent (Claude Code, Codex, Cursor). State lives as plain files in your repo. CLI + MCP, 0 runtime dependencies.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"leerness",
|