leerness 1.36.32 → 1.36.33
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 +10 -0
- package/README.md +4 -4
- package/bin/leerness.js +54 -14
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.36.33 — 2026-07-15 — 데이터 보존 5종 — migrate --force 상태 보존 · positional path 클래스 소진 · symlink 미추종 · skill 충돌 가드 · settings 손상 중단 (codex 3차 헌트 + 자체 발견)
|
|
4
|
+
|
|
5
|
+
- **P1 — `migrate --force` 가 사용자 상태를 삭제** (codex 3차 #1): --force 가 progress-tracker/plan/task-log/decisions 등 상태 파일까지 새 템플릿으로 덮어써 활성 task 가 archive 에만 남았다(마커 실측 소실). → force 는 가이드/템플릿 문서에만 적용, 상태 파일 14종은 force 여도 보존(`_USER_STATE`). 실측: --force 후 task 잔존 1 · guideline 은 갱신. **1.36.28 의 마이그레이션 유실 전수 검증을 --force 경로까지 확장 완결.**
|
|
6
|
+
- **P2 — positional path 무시 클래스 8종 소진** (자체 발견 — `migrate-workspace-dir <path>` 가 positional 을 무시하고 cwd 를 마이그레이션해 **검증 중 소스 저장소가 실제로 오염**된 사고로 표면화): migrate-workspace-dir/lessons/resume/session-resume/commands/py-check/path-setup/capabilities 에 1.36.21/24 와 동일한 `_taskPositionalPath` 배선. 오염은 사본 제거로 정리(원본 .harness 불변).
|
|
7
|
+
- **P2 — 재귀 복사 symlink 폭주** (1.36.21 이연 #6): 자기참조 junction 이 있으면 copyRec 이 MAX_PATH 에러로 자기종결할 때까지 실복사(63중첩 실측). → `copyRec`/`copyRecursiveSafe` lstat 미추종(+skip 기록). 실측: 중첩 0 · 일반 파일 정상 복사.
|
|
8
|
+
- **P1 — skill install 무경고 덮어쓰기 + raw-name 우회** (codex 3차 #2): 정규화가 `../x` 의 ../ 를 벗겨 traversal 흔적을 지우고 기존 스킬을 조용히 교체. → raw name 의 경로 문자 선검증 + 내용 다른 충돌은 `--force` 요구(교체 전 .bak 백업, 동일 내용 재설치는 멱등).
|
|
9
|
+
- **P2 — auto-update install 이 손상 settings 를 hooks-only 로 교체** (codex 3차 #3, 1.36.28 손상-스토어 클래스): permissions 등 무관 설정 유실. → 파싱 실패 시 원본 보존 + 중단(exit 1). 정상 파일은 종전대로 병합.
|
|
10
|
+
- **검증**: selftest 303/303, 5건 전부 원 재현 before/after + 무회귀(--force 멱등/정상 병합/skill 멱등), exit 전파형 게이트 e2e, 게시본 클린룸.
|
|
11
|
+
- 이연(codex 3차 잔여): #4 tarball 공백경로 · #5 update 캐시 false-fresh + prerelease 비교 · #6 deps --run-tests false-green · #7 주석 트레이스 오인 · #8 policy exit · #9 creds null 스키마 · #10 session close 상태 별칭 — 다음 라운드.
|
|
12
|
+
|
|
3
13
|
## 1.36.32 — 2026-07-15 — e2e 그린 복원 + 게이트 절차 정직 교정 (자기 감사)
|
|
4
14
|
|
|
5
15
|
**정정(정직 기록)**: 1.36.30/1.36.31 의 CHANGELOG 는 "게이트 e2e" 통과를 주장했지만 **사실이 아니었다** — 1.36.30 의 자동생성 전환(roadmap.html→leerness.html)으로 e2e 의 구식 단언 4건이 실패·크래시했는데, 게이트 러너 래퍼(`npm test > log; echo $?`)의 echo 가 exit code 를 0 으로 가려 두 릴리스가 e2e 실패 상태로 게시됐다(28/29 는 진짜 386/386). selftest 와 라이브 클린룸 실증은 두 릴리스 모두 실제로 통과했으므로 게시된 기능 자체는 검증돼 있었으나, e2e 전체 그린 주장은 과잉이었다.
|
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.33 하네스를 사용합니다. 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.33는 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.32는 stdio JSON-RPC MCP server를 내장합니다 — Claude Cod
|
|
|
196
196
|
`<<autonomous-loop-dynamic>>` 신호만 보내면 AI가:
|
|
197
197
|
1) 다음 라운드 후보 선정 → 2) 코드 변경 → 3) stress-v* 신규 작성 + 누적 회귀 → 4) e2e 219/219 → 5) npm pack + git tag + GitHub release → 6) main 자동 push → 7) session close → 8) 다음 라운드 예약.
|
|
198
198
|
|
|
199
|
-
현재 누적: **70 라운드 (1.9.40 → 1.36.
|
|
199
|
+
현재 누적: **70 라운드 (1.9.40 → 1.36.33)** · 매 라운드 GitHub release/태그 생성 · _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.33: 2026-07-15
|
|
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.33';
|
|
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') 시 호스트 프로세스 오염.
|
|
@@ -652,7 +652,8 @@ leerness memory restore <surface> <target> # archive → active 복귀 (DELETE
|
|
|
652
652
|
function copyRecursiveSafe(src, dst) {
|
|
653
653
|
if (!exists(src)) return;
|
|
654
654
|
if (src.includes(path.sep + '.harness' + path.sep + 'archive')) return;
|
|
655
|
-
const st = fs.
|
|
655
|
+
const st = fs.lstatSync(src); // 1.36.33 (#6): symlink 미추종 — 자기참조 링크 시 MAX_PATH 까지 복제 폭주 방지
|
|
656
|
+
if (st.isSymbolicLink()) return;
|
|
656
657
|
if (st.isDirectory()) {
|
|
657
658
|
mkdirp(dst);
|
|
658
659
|
for (const e of fs.readdirSync(src)) {
|
|
@@ -986,16 +987,20 @@ async function install(root, opts = {}) {
|
|
|
986
987
|
};
|
|
987
988
|
drawProgress(0, '');
|
|
988
989
|
let _done = 0;
|
|
990
|
+
// 1.36.33 (codex 3차 #1, P1 데이터 유실): --force 가 사용자 상태 파일(진행 task/plan/결정/로그/인계 등)까지 새 템플릿으로
|
|
991
|
+
// 덮어써 활성 기록이 archive 에만 남았다. force 는 가이드/템플릿 문서에만 적용 — 상태 파일은 force 여도 보존.
|
|
992
|
+
const _USER_STATE = new Set(['.harness/progress-tracker.md', '.harness/plan.md', '.harness/task-log.md', '.harness/decisions.md', '.harness/current-state.md', '.harness/session-handoff.md', '.harness/rules.md', '.harness/feature-graph.md', '.harness/reuse-map.md', '.harness/feature-contracts.md', '.harness/project-brief.md', '.harness/architecture.md', '.harness/context-map.md', '.harness/design-system.md']);
|
|
989
993
|
for (const [f, c] of Object.entries(files)) {
|
|
990
994
|
const existsNow = exists(path.join(root, f));
|
|
991
995
|
const mergeManaged = managedOverwrite.has(f);
|
|
996
|
+
const effForce = opts.force && !_USER_STATE.has(f);
|
|
992
997
|
if (opts.dry) {
|
|
993
|
-
const action = existsNow ? (mergeManaged ||
|
|
998
|
+
const action = existsNow ? (mergeManaged || effForce ? 'merge/update' : 'preserve') : 'create';
|
|
994
999
|
log(`[dry-run] ${action}: ${f}`);
|
|
995
1000
|
actions.push({ file:f, action });
|
|
996
1001
|
continue;
|
|
997
1002
|
}
|
|
998
|
-
const r = writeIfSafe(root, f, c, { force:
|
|
1003
|
+
const r = writeIfSafe(root, f, c, { force: effForce, mergeManaged, archiveDir: backup.archiveDir });
|
|
999
1004
|
actions.push(r);
|
|
1000
1005
|
_done++;
|
|
1001
1006
|
drawProgress(_done, f);
|
|
@@ -1514,11 +1519,19 @@ async function skillInstallCmd(root, source) {
|
|
|
1514
1519
|
if (!name) { fail('SKILL.md frontmatter에 `name` 필수'); return process.exit(1); }
|
|
1515
1520
|
// .harness/skills/<id>/SKILL.md 저장
|
|
1516
1521
|
// 1.9.350 (UR-0062 외부리뷰): skill id 정규화 — 선행/후행 ./- 제거 + .. 거부 + jail (path traversal 차단)
|
|
1522
|
+
// 1.36.33 (codex 3차 #2): 원문(raw) name 부터 검증 — 정규화가 '../x' 의 ../ 를 벗겨 'x' 로 만들어 traversal 흔적을 지웠다.
|
|
1523
|
+
if (/(^|[\\/])\.\.([\\/]|$)|[\\/]/.test(String(name))) { fail(`유효하지 않은 skill name (path traversal/경로 문자 차단): ${name}`); return process.exit(1); }
|
|
1517
1524
|
const skillId = String(name).toLowerCase().replace(/[^a-z0-9._-]+/g, '-').replace(/^[.\-]+|[.\-]+$/g, '');
|
|
1518
1525
|
if (!skillId || skillId.includes('..')) { fail(`유효하지 않은 skill id (path traversal 차단): ${name}`); return process.exit(1); }
|
|
1519
1526
|
const _skillsRoot = path.join(root, '.harness', 'skills');
|
|
1520
1527
|
const dir = path.join(_skillsRoot, skillId);
|
|
1521
1528
|
if (!path.resolve(dir).startsWith(path.resolve(_skillsRoot) + path.sep)) { fail(`skill id jail 위반: ${name}`); return process.exit(1); }
|
|
1529
|
+
// 1.36.33 (codex 3차 #2): 같은 id 가 이미 설치돼 있고 내용이 다르면 무경고 덮어쓰기 금지 — --force 로만 교체(교체 전 .bak 백업).
|
|
1530
|
+
const _prevMd = path.join(dir, 'SKILL.md');
|
|
1531
|
+
if (exists(_prevMd) && read(_prevMd) !== body) {
|
|
1532
|
+
if (!has('--force')) { fail(`이미 설치된 skill: ${skillId} — 내용이 다릅니다. 교체하려면 --force (기존본은 SKILL.md.bak 백업)`); return process.exit(1); }
|
|
1533
|
+
try { fs.copyFileSync(_prevMd, _prevMd + '.bak'); } catch {}
|
|
1534
|
+
}
|
|
1522
1535
|
mkdirp(dir);
|
|
1523
1536
|
writeUtf8(path.join(dir, 'SKILL.md'), body);
|
|
1524
1537
|
// skill.json도 함께 (자체 catalog 호환)
|
|
@@ -3118,6 +3131,25 @@ function _selfTestCases() {
|
|
|
3118
3131
|
const pwWired = /_runPreWakeAudit[\s\S]{0,2200}\^\\\|\\s\*T-\\d\{4,\}\\s\*\\\|\\s\*in-progress/.test(s);
|
|
3119
3132
|
return stateLock && teamLock && teamWired && both && pwWired;
|
|
3120
3133
|
} },
|
|
3134
|
+
{ name: 'positional path 잔존 클래스 소진 (1.36.33): migrate-workspace-dir 외 8종 — cwd 오염 차단 (소스가드, 실제 자기 저장소 오염 사고로 발견)', run: () => {
|
|
3135
|
+
const s = read(__filename);
|
|
3136
|
+
const P = "arg('--path', null) || _taskPositionalPath(args, 1) || process.cwd()";
|
|
3137
|
+
const cmds = ["'migrate-workspace-dir'", "'lessons'", "'resume'", "'session-resume'", "'commands'", "'py-check'", "'path-setup' || cmd === 'path'", "'capabilities' || cmd === 'security-surface'"];
|
|
3138
|
+
return cmds.every(c => {
|
|
3139
|
+
const i = s.indexOf('if (cmd === ' + c + ')');
|
|
3140
|
+
if (i < 0) return false;
|
|
3141
|
+
return s.slice(i, s.indexOf('\n', i)).includes(P);
|
|
3142
|
+
});
|
|
3143
|
+
} },
|
|
3144
|
+
{ name: '데이터 보존 3종 (1.36.33, codex 3차 #1/#2/#3): migrate --force 상태파일 보존 + skill 충돌/raw-name 가드 + settings 손상 중단 + symlink 미추종 (소스가드)', run: () => {
|
|
3145
|
+
const s = read(__filename);
|
|
3146
|
+
const forceGuard = s.includes("const effForce = opts.force && !_USER_STATE.has(f);") && s.includes("'.harness/progress-tracker.md', '.harness/plan.md', '.harness/task-log.md'");
|
|
3147
|
+
const skillGuard = s.includes('유효하지 않은 skill name (path traversal/경로 문자 차단)') && s.includes('이미 설치된 skill: ${skillId} — 내용이 다릅니다');
|
|
3148
|
+
const settingsGuard = s.includes('settings.local.json 이 손상돼(JSON 파싱 실패) hook 설치를 중단');
|
|
3149
|
+
const symlinkGuard = s.includes("if (stat.isSymbolicLink()) { report.skippedFiles.push(rel + ' (symlink — 따라가지 않음')") || s.includes('symlink — 따라가지 않음');
|
|
3150
|
+
const copySafe = /function copyRecursiveSafe[\s\S]{0,400}lstatSync/.test(s);
|
|
3151
|
+
return forceGuard && skillGuard && settingsGuard && symlinkGuard && copySafe;
|
|
3152
|
+
} },
|
|
3121
3153
|
{ name: 'debug 렌즈 (1.36.27, obra/superpowers systematic-debugging): 자기질문 6문항 ko/en 락스텝 + affects 유효 + route bugfix 힌트 + 파일매핑 미확장 — 행위검사', run: () => {
|
|
3122
3154
|
const d = LENS_CATALOG.debug;
|
|
3123
3155
|
if (!d) return false;
|
|
@@ -6165,7 +6197,10 @@ function _migrateWorkspaceDir(root, opts = {}) {
|
|
|
6165
6197
|
const srcAbs = path.join(srcDir, rel);
|
|
6166
6198
|
const dstAbs = path.join(dstDir, rel);
|
|
6167
6199
|
let stat;
|
|
6168
|
-
|
|
6200
|
+
// 1.36.33 (#6): lstat 로 symlink/junction 을 따라가지 않음 — 자기참조 링크(.harness 안 →.harness)가 있으면
|
|
6201
|
+
// 종전엔 MAX_PATH 에러로 자기종결할 때까지 수십 단계를 실제 복사(63중첩 실측)했다. 링크는 skip 기록.
|
|
6202
|
+
try { stat = fs.lstatSync(srcAbs); } catch (e) { report.errors.push(`stat ${rel}: ${e.message}`); return; }
|
|
6203
|
+
if (stat.isSymbolicLink()) { report.skippedFiles.push(rel + ' (symlink — 따라가지 않음)'); return; }
|
|
6169
6204
|
if (stat.isDirectory()) {
|
|
6170
6205
|
if (!dryRun) mkdirp(dstAbs);
|
|
6171
6206
|
let entries;
|
|
@@ -16389,7 +16424,12 @@ function autoUpdateInstall(root) {
|
|
|
16389
16424
|
mkdirp(settingsDir);
|
|
16390
16425
|
const settingsFile = path.join(settingsDir, 'settings.local.json');
|
|
16391
16426
|
let settings = {};
|
|
16392
|
-
|
|
16427
|
+
// 1.36.33 (codex 3차 #3, 1.36.28 손상-스토어 클래스): 손상 settings 를 빈 객체로 오인해 hooks-only 로 덮어쓰면
|
|
16428
|
+
// 사용자의 permissions 등 무관 설정이 유실된다. 파싱 실패 시 원본 보존 + 중단.
|
|
16429
|
+
if (exists(settingsFile)) {
|
|
16430
|
+
try { settings = JSON.parse(read(settingsFile)); }
|
|
16431
|
+
catch { fail(`.claude/settings.local.json 이 손상돼(JSON 파싱 실패) hook 설치를 중단합니다 — 파일을 복구한 뒤 재시도: ${settingsFile}`); process.exitCode = 1; return; }
|
|
16432
|
+
}
|
|
16393
16433
|
settings.hooks = settings.hooks || {};
|
|
16394
16434
|
// 1.9.1 P1: legacy 'leerness-plus update' hook 자동 제거 (이전 fork 시절 잔재).
|
|
16395
16435
|
let removedLegacy = 0, upgradedQuiet = 0;
|
|
@@ -21603,7 +21643,7 @@ async function main() {
|
|
|
21603
21643
|
if (cmd === 'mcp' && args[1] === 'serve') return mcpServeCmd(absRoot(arg('--path', process.cwd())));
|
|
21604
21644
|
if (cmd === 'gate') return gate(arg('--path', args[1] || process.cwd()));
|
|
21605
21645
|
if (cmd === 'verify-code') return verifyCodeCmd(arg('--path', args[1] || process.cwd()));
|
|
21606
|
-
if (cmd === 'lessons') return lessonsCmd(arg('--path', process.cwd())
|
|
21646
|
+
if (cmd === 'lessons') return lessonsCmd(arg('--path', null) || _taskPositionalPath(args, 1) || process.cwd());
|
|
21607
21647
|
if (cmd === 'retro') return retroCmd(arg('--path', args[1] || process.cwd()));
|
|
21608
21648
|
if (cmd === 'insights') return insightsCmd(arg('--path', args[1] || process.cwd()));
|
|
21609
21649
|
if (cmd === 'brainstorm') return brainstormCmd(arg('--path', process.cwd()), args.slice(1).filter(x => !x.startsWith('-')).join(' '));
|
|
@@ -21612,14 +21652,14 @@ async function main() {
|
|
|
21612
21652
|
// 1.9.201: next-action queue CLI (E축 9.5→10)
|
|
21613
21653
|
if (cmd === 'next-action') return nextActionCmd(arg('--path', process.cwd()), args[1], ...args.slice(2));
|
|
21614
21654
|
// 1.9.203: leerness resume — auto-resume-plan 읽고 다음 라운드 즉시 안내 (사용자 명시)
|
|
21615
|
-
if (cmd === 'resume') return resumeCmd(arg('--path', process.cwd())
|
|
21655
|
+
if (cmd === 'resume') return resumeCmd(arg('--path', null) || _taskPositionalPath(args, 1) || process.cwd());
|
|
21616
21656
|
// 1.9.207: leerness requests <audit|add|list|complete|drop> — 사용자 요청 누락 확인 절차 (사용자 명시)
|
|
21617
21657
|
// 1.36.24 (UR-0027 확장, 실피해 재현): requests 계열만 positional 경로 미지원이라 `requests add "text" C:/other/proj` 가
|
|
21618
21658
|
// 조용히 cwd 프로젝트에 기록하고 성공 보고했다(task/rule 은 _taskPositionalPath 지원 → 불일치가 실수를 유도, 본인 피해 확인).
|
|
21619
21659
|
// rule 선례와 동일 배선 — path-like(선행 구분자) 만 인정하는 기존 계약이라 텍스트/ID 오인 FP 없음(_parseAddTitle 이 add 텍스트를 path-like 에서 끊는 것도 기존과 동일).
|
|
21620
21660
|
if (cmd === 'requests') return requestsCmd(arg('--path', null) || _taskPositionalPath(args, 2) || process.cwd(), args[1], ...args.slice(2));
|
|
21621
21661
|
// 1.9.220: leerness session-resume — 비정상 종료 감지 + 자율 재개 가이드 (사용자 명시)
|
|
21622
|
-
if (cmd === 'session-resume') return sessionResumeCmd(arg('--path', process.cwd())
|
|
21662
|
+
if (cmd === 'session-resume') return sessionResumeCmd(arg('--path', null) || _taskPositionalPath(args, 1) || process.cwd());
|
|
21623
21663
|
// 1.9.226: leerness round-history — 자율 라운드 통계 + 다음 마일스톤
|
|
21624
21664
|
if (cmd === 'round-history') return roundHistoryCmd(_resolveRoot(args[1]));
|
|
21625
21665
|
// 1.9.229: leerness milestones — 도달 마일스톤 + 다음 ETA (1.9.226 확장)
|
|
@@ -21629,9 +21669,9 @@ async function main() {
|
|
|
21629
21669
|
if (cmd === 'toggle') return _tgl.toggleCmd(arg('--path', process.cwd()), args[1], args[2], args[3], { has, VERSION }); // 1.36.30: 기능 토글 (그래프 ⚙ 탭 연동)
|
|
21630
21670
|
if (cmd === 'lens') return lensCmd(args[1]); // 1.18.3 (UR-0003): 분야별 자기질문 품질 렌즈
|
|
21631
21671
|
// 1.9.233: leerness commands — 카테고리화된 전체 CLI 명령 목록
|
|
21632
|
-
if (cmd === 'commands') return commandsCmd(arg('--path', process.cwd())
|
|
21672
|
+
if (cmd === 'commands') return commandsCmd(arg('--path', null) || _taskPositionalPath(args, 1) || process.cwd());
|
|
21633
21673
|
// 1.9.239: leerness py-check — Python 파일 분석 (사용자 명시 UR-0013)
|
|
21634
|
-
if (cmd === 'py-check') return pyCheckCmd(arg('--path', process.cwd())
|
|
21674
|
+
if (cmd === 'py-check') return pyCheckCmd(arg('--path', null) || _taskPositionalPath(args, 1) || process.cwd());
|
|
21635
21675
|
// 1.9.239: leerness agent-mode <start|tick|stop> — 자율 모드 전용 통합 명령 (사용자 명시 UR-0013)
|
|
21636
21676
|
if (cmd === 'agent-mode') return agentModeCmd(arg('--path', process.cwd()), args[1]);
|
|
21637
21677
|
// 1.9.241: leerness env summary|encoding — 환경 종합 + 셸 스크립트 인코딩 위험 (사용자 명시 UR-0014)
|
|
@@ -21641,7 +21681,7 @@ async function main() {
|
|
|
21641
21681
|
return envCmd((args[2] && !args[2].startsWith('-')) ? args[2] : arg('--path', process.cwd()), args[1] === 'summary' ? null : 'encoding-check');
|
|
21642
21682
|
// 1.9.254: leerness path-setup [--apply] — CLI PATH 자동 등록 (사용자 명시 UR-0019)
|
|
21643
21683
|
// npm global bin 경로 감지 + leerness 가 PATH 에서 찾아지는지 확인 → 미등록 시 플랫폼별 등록
|
|
21644
|
-
if (cmd === 'path-setup' || cmd === 'path') return pathSetupCmd(arg('--path', process.cwd()
|
|
21684
|
+
if (cmd === 'path-setup' || cmd === 'path') return pathSetupCmd(arg('--path', null) || _taskPositionalPath(args, 1) || process.cwd(), { apply: has('--apply'), json: has('--json') });
|
|
21645
21685
|
// 1.9.258: leerness selftest — 설치된 바이너리 코어 함수 무결성 자가 검증 (CI/사용자 진단)
|
|
21646
21686
|
if (cmd === 'selftest' || cmd === 'self-test') return selfTestCmd({ json: has('--json') });
|
|
21647
21687
|
// 1.9.260: leerness shell-guard "<cmd>" — 터미널 명령 셸 호환성 린터 + 실패 메모리 (사용자 명시 UR-0020)
|
|
@@ -21658,7 +21698,7 @@ async function main() {
|
|
|
21658
21698
|
// 1.9.270: leerness roles <list|set|unset|catalog|suggest|verify> — 모델별 역할 부여 (사용자 명시)
|
|
21659
21699
|
if (cmd === 'roles' || cmd === 'role') return rolesCmd(arg('--path', process.cwd()), args[1], ...args.slice(2));
|
|
21660
21700
|
// 1.9.272: leerness capabilities — 권한/보안 표면 공개 (GPT-5.5 외부 리뷰 반영)
|
|
21661
|
-
if (cmd === 'capabilities' || cmd === 'security-surface') return capabilitiesCmd(arg('--path', process.cwd()
|
|
21701
|
+
if (cmd === 'capabilities' || cmd === 'security-surface') return capabilitiesCmd(arg('--path', null) || _taskPositionalPath(args, 1) || process.cwd(), { json: has('--json') });
|
|
21662
21702
|
// 1.9.278 (UR-0032): leerness state <show|start|record|verify|handoff> — .leerness/ 구조화 상태 substrate
|
|
21663
21703
|
if (cmd === 'state') return _guardStore(has('--json'), () => stateCmd(arg('--path', process.cwd()), args[1], ...args.slice(2))); // 1.36.28 (#3): 손상 스토어 클로버 차단
|
|
21664
21704
|
if (cmd === 'context') return contextCmd(_resolveRoot(args[1]), { json: has('--json') });
|
|
@@ -21700,7 +21740,7 @@ async function main() {
|
|
|
21700
21740
|
if (cmd === 'workspace-dir') return workspaceDirCmd(arg('--path', process.cwd()), args[1]);
|
|
21701
21741
|
if (cmd === 'parent') return parentCmd(arg('--path', process.cwd()), args[1]);
|
|
21702
21742
|
// 1.9.211: leerness migrate-workspace-dir — .harness → .leerness 마이그레이션 (사용자 명시)
|
|
21703
|
-
if (cmd === 'migrate-workspace-dir') return migrateWorkspaceDirCmd(arg('--path', process.cwd()))
|
|
21743
|
+
if (cmd === 'migrate-workspace-dir') return migrateWorkspaceDirCmd(arg('--path', null) || _taskPositionalPath(args, 1) || process.cwd()); // 1.36.33: positional path 무시 → cwd 오염 (1.36.21/24 계열 잔존 — 실제로 이 버그로 소스 저장소가 오염될 뻔)
|
|
21704
21744
|
// 1.9.212: leerness idempotency audit — 멱등성 위반 탐지 (사용자 명시)
|
|
21705
21745
|
if (cmd === 'idempotency') return idempotencyCmd(arg('--path', process.cwd()), args[1]);
|
|
21706
21746
|
// 1.9.213: leerness intent <classify|expand|domains> — intent inference + scope expansion (사용자 명시)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "leerness",
|
|
3
|
-
"version": "1.36.
|
|
3
|
+
"version": "1.36.33",
|
|
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",
|