leerness 1.9.440 → 1.9.442
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 +23 -0
- package/README.md +14 -4
- package/bin/leerness.js +24 -3
- package/lib/pure-utils.js +18 -1
- package/package.json +1 -1
- package/scripts/e2e.js +20 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.9.442 — 2026-06-08 — task 계열 positional path 지원 (12th 외부평가 Sonnet P1, UR-0141)
|
|
4
|
+
|
|
5
|
+
**🐛 P1 데이터 오염 수정: `task add "제목" ./경로` / `task list /경로` 가 무시되고 cwd 에 저장되던 문제.**
|
|
6
|
+
|
|
7
|
+
### 변경
|
|
8
|
+
- `task list/add/update/drop/...` 가 positional path 를 인식 — `--path` > path-like positional > cwd 우선순위. 기존엔 `arg('--path', cwd)` 만 사용해 positional 경로를 무시 → 프로젝트 루트가 아닌 cwd 에서 실행 시 엉뚱한 곳에 메모리 생성(멀티프로젝트 오염).
|
|
9
|
+
- 순수 `_taskPositionalPath(args, startIdx)` (pure-utils): `_parseAddTitle` 과 동일한 path-like 판정(선행 구분자 `/ ./ ../ C:\`)으로 **제목/ID/맨이름은 경로로 오인하지 않음**(`task add "fix src/auth bug"` 의 내부 슬래시 제목 보호). **값-취하는 플래그의 값**(`--evidence /abs/log`)도 경로 후보에서 제외.
|
|
10
|
+
|
|
11
|
+
### 검증 (회귀 0)
|
|
12
|
+
- **selftest 186→187** (순수 7케이스 + 와이어), **E2E 신규 B(1.9.442)**: 다른 cwd 에서 `task add "x" <ws>` → ws 에 저장 + cwd 오염 0.
|
|
13
|
+
- 행위 재현으로 확인: positional 경로 저장 + cwd `.harness` 미생성. `--path` 최우선·내부슬래시 제목·Windows 경로(`C:\`,`C:/`)·boolean 플래그 뒤 경로 전부 정상.
|
|
14
|
+
|
|
15
|
+
## 1.9.441 — 2026-06-08 — README ASCII 배너 추가
|
|
16
|
+
|
|
17
|
+
**🎨 README 최상단에 LEERNESS ASCII 아트 배너 추가(CLI `_banner` 와 동일 아트).**
|
|
18
|
+
|
|
19
|
+
### 변경
|
|
20
|
+
- `README.md` 상단에 LEERNESS 블록레터 ASCII 배너를 코드블록으로 표시 — CLI init 배너(`_banner`)와 동일한 아트로 브랜드 일관성.
|
|
21
|
+
- selftest: README ↔ CLI `_banner` 동일 배너 라인 검증.
|
|
22
|
+
|
|
23
|
+
### 검증 (회귀 0)
|
|
24
|
+
- **selftest 185→186 PASS** · **E2E 무회귀**.
|
|
25
|
+
|
|
3
26
|
## 1.9.440 — 2026-06-08 — 🛡️ [안정화/Stable] 시크릿 스캐너 prefix 패턴 placeholder 가드 (12th 외부평가 Opus P2, UR-0140)
|
|
4
27
|
|
|
5
28
|
**🛡️ 안정화 릴리스(Stable) — 보안 스캐너 False-Positive 가 CI 를 깨던 문제 수정.**
|
package/README.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# leerness
|
|
2
2
|
|
|
3
|
+
```
|
|
4
|
+
██╗ ███████╗███████╗██████╗ ███╗ ██╗███████╗███████╗
|
|
5
|
+
██║ ██╔════╝██╔════╝██╔══██╗████╗ ██║██╔════╝██╔════╝
|
|
6
|
+
██║ █████╗ █████╗ ██████╔╝██╔██╗ ██║█████╗ ███████╗
|
|
7
|
+
██║ ██╔══╝ ██╔══╝ ██╔══██╗██║╚██╗██║██╔══╝ ╚════██║
|
|
8
|
+
███████╗███████╗███████╗██║ ██║██║ ╚████║███████╗███████║
|
|
9
|
+
╚══════╝╚══════╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝╚══════╝╚══════╝
|
|
10
|
+
```
|
|
11
|
+
|
|
3
12
|
> **AI 코딩 에이전트를 위한 운영 레이어(operating layer).** 코드를 대신 쓰는 도구가 아니라, AI 에이전트의 **기억·인수인계·검증·감사·보안 가드**를 프로젝트에 영속화하는 CLI + MCP 서버입니다.
|
|
4
13
|
|
|
5
14
|
[](https://www.npmjs.com/package/leerness) ·  · **런타임 의존성 0** · **install-script 0** · offline-first · Node ≥ 18 · MIT
|
|
@@ -159,7 +168,7 @@ MIT
|
|
|
159
168
|
<!-- leerness:project-readme:start -->
|
|
160
169
|
## Leerness Project Harness
|
|
161
170
|
|
|
162
|
-
이 프로젝트는 Leerness v1.9.
|
|
171
|
+
이 프로젝트는 Leerness v1.9.442 하네스를 사용합니다. AI 에이전트는 작업 전 `leerness handoff`로 컨텍스트를 적재하고, 작업 후 `leerness check`/`leerness audit`/`leerness session close`를 수행해야 합니다.
|
|
163
172
|
|
|
164
173
|
### 정체성 — AI 에이전트 운영 레이어 (UR-0030)
|
|
165
174
|
|
|
@@ -213,7 +222,7 @@ leerness memory restore decision <date|title>
|
|
|
213
222
|
|
|
214
223
|
### MCP server (외부 AI 통합)
|
|
215
224
|
|
|
216
|
-
Leerness v1.9.
|
|
225
|
+
Leerness v1.9.442는 stdio JSON-RPC MCP server를 내장합니다 — Claude Code · Cursor · Codex CLI 등 외부 AI에 **85개 도구**를 노출:
|
|
217
226
|
|
|
218
227
|
```jsonc
|
|
219
228
|
// 카테고리별
|
|
@@ -234,7 +243,7 @@ Leerness v1.9.440는 stdio JSON-RPC MCP server를 내장합니다 — Claude Cod
|
|
|
234
243
|
`<<autonomous-loop-dynamic>>` 신호만 보내면 AI가:
|
|
235
244
|
1) 다음 라운드 후보 선정 → 2) 코드 변경 → 3) stress-v* 신규 작성 + 누적 회귀 → 4) e2e 219/219 → 5) npm pack + git tag + GitHub release → 6) main 자동 push (1.9.140+) → 7) session close → 8) 다음 라운드 예약.
|
|
236
245
|
|
|
237
|
-
현재 누적: **70 라운드 (1.9.40 → 1.9.
|
|
246
|
+
현재 누적: **70 라운드 (1.9.40 → 1.9.442)** · 매 라운드 GitHub release/태그 생성 · _reports/는 비공개 보존.
|
|
238
247
|
|
|
239
248
|
### 성능 가이드 (1.9.140 측정)
|
|
240
249
|
|
|
@@ -272,5 +281,6 @@ leerness release pack --close --auto-main-push
|
|
|
272
281
|
- `.harness/session-handoff.md`: 다음 세션 인수인계 (자동 작성)
|
|
273
282
|
- `.harness/lessons.md` / `decisions.md` / `rules.md`: 영구 메모리 (5 surface)
|
|
274
283
|
|
|
275
|
-
Last synced by Leerness v1.9.
|
|
284
|
+
Last synced by Leerness v1.9.442: 2026-06-08
|
|
276
285
|
<!-- leerness:project-readme:end -->
|
|
286
|
+
|
package/bin/leerness.js
CHANGED
|
@@ -25,13 +25,13 @@ const { _isSecretKey, _isPlaceholderSecret, _looksSecretLike, _mergeLines, _merg
|
|
|
25
25
|
_withBuiltinSource, _esc, _roadmapTokenStyles, _parseSkillMd,
|
|
26
26
|
_migrationGuideText, _parseContractSpec, _gitignoreMatch,
|
|
27
27
|
_featureGraphTemplate, _parseFeatureGraph, _nextFeatureId, _featureBlock, _featureImpactBfs,
|
|
28
|
-
_parseChangelogBetween, _cellSafe, _cellUnescape, _lineSafe, _parseLimit, _parseAddTitle, _parseImplExports } = require('../lib/pure-utils'); // 1.9.318~
|
|
28
|
+
_parseChangelogBetween, _cellSafe, _cellUnescape, _lineSafe, _parseLimit, _parseAddTitle, _parseImplExports, _taskPositionalPath } = require('../lib/pure-utils'); // 1.9.318~442 (UR-0025/0053/0075/0086/0087/0104/0122/0141): 순수 유틸 모듈 분리
|
|
29
29
|
// 1.9.304 (UR-0025): 순수 분석/검증 함수 모듈 분리.
|
|
30
30
|
const { _evidenceQuality, _parseEvidenceStats, _shellGuardAnalyze, _claimFileInGit, _epistemicHonestyCheck } = require('../lib/analyzers');
|
|
31
31
|
// 1.9.295 (UR-0025 4단계): 정적 데이터 카탈로그 모듈 분리 (비파괴, require-based).
|
|
32
32
|
const { CAPABILITY_SURFACE, POWERFUL_COMMANDS, ADAPTERS, REUSE_CATEGORIES, REUSE_CHECKLIST, _DEFAULT_PLATFORM_CONSTRAINTS, _DEFAULT_DOMAIN_CATALOG, _LSP_LANG_PATTERNS, OPTIMISM_PATTERNS, BUILT_IN_PERSONAS, STRINGS, BUILTIN_CATALOG, ROADMAP_STATUS_LABEL, ROADMAP_STATUS_COLOR, SECRET_PATTERNS, MERGE_OVERWRITE_FILES, MINIMAL_SKIP_KEYS, REQUIRED_WORKSPACE_FILES, KEYWORD_STOPWORDS, SKILL_CATALOG_PRESETS } = require('../lib/catalogs'); // 1.9.344/368/369 (UR-0025): catalog 분리 (MERGE_OVERWRITE_FILES/MINIMAL_SKIP_KEYS 포함)
|
|
33
33
|
|
|
34
|
-
const VERSION = '1.9.
|
|
34
|
+
const VERSION = '1.9.442';
|
|
35
35
|
|
|
36
36
|
// 1.9.290 (UR-0037, Codex gpt-5.5 #4 수렴): CLI 전용 부작용은 require 시 실행하지 않는다.
|
|
37
37
|
// 이전: warning listener 제거 / NODE_OPTIONS 변경 / chcp IIFE 가 top-level 즉시 실행 → require('harness') 시 호스트 프로세스 오염.
|
|
@@ -3307,6 +3307,25 @@ function _selfTestCases() {
|
|
|
3307
3307
|
const behav = m._isPlaceholderSecret('AKIA' + 'X'.repeat(16)) === true && m._isPlaceholderSecret('AKIAJQXMP7RZ2KL9WXYZ') === false && m._isPlaceholderSecret('sk-EXAMPLEab12cd34ef56gh78ij90kl') === false;
|
|
3308
3308
|
return wired && behav;
|
|
3309
3309
|
} },
|
|
3310
|
+
{ name: 'README ASCII 배너 표시 + CLI _banner 와 동일 아트 (1.9.441)', run: () => {
|
|
3311
|
+
const readme = read(path.join(path.dirname(__filename), '..', 'README.md'));
|
|
3312
|
+
const bannerLine = '███████╗███████╗██╗ ██╗'.slice(0, 0) + '██║ █████╗ █████╗ ██████╔╝'; // LEERNESS 배너 고유 라인(자기참조 회피 split)
|
|
3313
|
+
return readme.includes(bannerLine) && read(__filename).includes(bannerLine); // README ↔ CLI _banner 동일 아트
|
|
3314
|
+
} },
|
|
3315
|
+
{ name: '12th 외부평가 Sonnet P1 (UR-0141): task 계열 positional path 지원 (cwd 오염 차단) (1.9.442)', run: () => {
|
|
3316
|
+
const m = require('../lib/pure-utils');
|
|
3317
|
+
if (typeof m._taskPositionalPath !== 'function' || m._taskPositionalPath !== _taskPositionalPath) return false;
|
|
3318
|
+
const p = (a) => m._taskPositionalPath(a, 2);
|
|
3319
|
+
const pure = p(['task', 'add', '제목', '/abs/p']) === '/abs/p' // add 뒤 절대경로
|
|
3320
|
+
&& p(['task', 'add', '제목']) === null // 경로 없으면 null(→cwd)
|
|
3321
|
+
&& p(['task', 'add', 'fix src/auth bug']) === null // 내부 슬래시 제목은 경로 아님(보호)
|
|
3322
|
+
&& p(['task', 'list', './ws']) === './ws' // list positional
|
|
3323
|
+
&& p(['task', 'update', 'T-0001', '/abs']) === '/abs' // id 뒤 경로
|
|
3324
|
+
&& p(['task', 'update', 'T-0001', '--evidence', '/abs/log']) === null // 값-플래그 값은 경로 아님
|
|
3325
|
+
&& p(['task', 'list', '--json', '/abs']) === '/abs'; // boolean 플래그 뒤 경로 OK
|
|
3326
|
+
const wired = read(__filename).includes("arg('--path', null) || _taskPositionalPath(args, 2) || process.cwd()");
|
|
3327
|
+
return pure && wired;
|
|
3328
|
+
} },
|
|
3310
3329
|
{ name: 'VERSION 형식 (x.y.z)', run: () => /^\d+\.\d+\.\d+$/.test(VERSION) }
|
|
3311
3330
|
];
|
|
3312
3331
|
}
|
|
@@ -18997,7 +19016,9 @@ async function main() {
|
|
|
18997
19016
|
if (sub==='list') return planListCmd(absRoot(_resolveRoot(args[2])), { json: has('--json') }); // 1.9.412 (UR-0100): positional path 지원
|
|
18998
19017
|
}
|
|
18999
19018
|
if (cmd === 'task') {
|
|
19000
|
-
|
|
19019
|
+
// 1.9.442 (UR-0141): positional path 지원 — --path > path-like positional(_taskPositionalPath) > cwd.
|
|
19020
|
+
// 기존엔 arg('--path',cwd) 만 사용해 `task add "제목" ./경로` / `task list /경로` 가 무시되고 cwd 오염. 제목/ID/값-플래그값은 경로로 오인 안 함.
|
|
19021
|
+
const root = absRoot(arg('--path', null) || _taskPositionalPath(args, 2) || process.cwd()); const sub = args[1] || 'list';
|
|
19001
19022
|
if (sub==='list') return taskList(root);
|
|
19002
19023
|
if (sub==='add') {
|
|
19003
19024
|
// 1.9.416 (UR-0122): flag/경로 break + 빈 입력 거부 (기존: args.slice(2).join(' ') 가 경로 흡수 + 빈 task 생성)
|
package/lib/pure-utils.js
CHANGED
|
@@ -1005,7 +1005,9 @@ module.exports = {
|
|
|
1005
1005
|
// 1.9.407 (8번째 버그헌트, UR-0111): --limit 안전 파싱(NaN/음수/0 → 기본값)
|
|
1006
1006
|
_parseLimit,
|
|
1007
1007
|
// 1.9.416 (9th 외부평가, UR-0122): add 류 제목 파싱(flag/경로 break) 단일 출처
|
|
1008
|
-
_parseAddTitle
|
|
1008
|
+
_parseAddTitle,
|
|
1009
|
+
// 1.9.442 (12th 외부평가, UR-0141): task 계열 positional path 안전 추출
|
|
1010
|
+
_taskPositionalPath
|
|
1009
1011
|
};
|
|
1010
1012
|
|
|
1011
1013
|
// 1.9.355 (UR-0075 Phase A): AI 에이전트용 크로스버전 마이그레이션 안전 워크플로 가이드 (순수 텍스트). 임시설치 + --path + 백업 + diff 검증.
|
|
@@ -1281,3 +1283,18 @@ function _parseAddTitle(args, startIdx = 0) {
|
|
|
1281
1283
|
}
|
|
1282
1284
|
return parts.join(' ').trim();
|
|
1283
1285
|
}
|
|
1286
|
+
|
|
1287
|
+
// 1.9.442 (12th 외부평가 Sonnet UR-0141): task 계열 positional path 안전 추출.
|
|
1288
|
+
// _parseAddTitle 과 동일한 path-like 판정(선행 구분자 / ./ ../ C:\)으로 제목/ID/맨이름은 경로로 오인 안 함(src/auth 같은 내부 슬래시 제목 보호).
|
|
1289
|
+
// 값-취하는 플래그(--evidence /abs/log 등)의 값은 root 후보에서 제외(직전 토큰이 값-플래그면 skip) → 오탐 차단. 첫 path-like positional 만 반환, 없으면 null.
|
|
1290
|
+
const _TASK_VALUE_FLAGS = new Set(['--status', '--evidence', '--priority', '--note', '--reason', '--title', '--desc', '--summary', '--id', '--limit', '--from', '--to']);
|
|
1291
|
+
function _taskPositionalPath(args, startIdx = 2) {
|
|
1292
|
+
const a = args || [];
|
|
1293
|
+
for (let i = startIdx; i < a.length; i++) {
|
|
1294
|
+
if (typeof a[i] !== 'string') continue;
|
|
1295
|
+
if (_TASK_VALUE_FLAGS.has(a[i - 1])) continue; // 값-플래그의 값(예: --evidence /abs) 은 경로 아님
|
|
1296
|
+
if (a[i].startsWith('-')) continue; // 플래그 자체 제외
|
|
1297
|
+
if (/^([A-Za-z]:[\\/]|\/|\.\.?[\\/])/.test(a[i])) return a[i]; // 선행 구분자 path-like 만
|
|
1298
|
+
}
|
|
1299
|
+
return null;
|
|
1300
|
+
}
|
package/package.json
CHANGED
package/scripts/e2e.js
CHANGED
|
@@ -6111,5 +6111,25 @@ total++;
|
|
|
6111
6111
|
if (!ok) failed++;
|
|
6112
6112
|
}
|
|
6113
6113
|
|
|
6114
|
+
// 1.9.442 (12th 외부평가 Sonnet P1, UR-0141): task 계열 positional path — 다른 cwd 에서 실행해도 positional 경로에 저장(cwd 오염 차단).
|
|
6115
|
+
total++;
|
|
6116
|
+
{
|
|
6117
|
+
let ok = false;
|
|
6118
|
+
try {
|
|
6119
|
+
const ws = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-taskpos-'));
|
|
6120
|
+
const cwd2 = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-cwd-'));
|
|
6121
|
+
cp.spawnSync(process.execPath, [CLI, 'init', ws, '--yes', '--language', 'ko'], { encoding: 'utf8', timeout: 30000 });
|
|
6122
|
+
cp.spawnSync(process.execPath, [CLI, 'task', 'add', '포지셔널e2e', ws, '--no-review'], { encoding: 'utf8', timeout: 15000, cwd: cwd2 });
|
|
6123
|
+
const tracker = path.join(ws, '.harness', 'progress-tracker.md');
|
|
6124
|
+
const savedToWs = fs.existsSync(tracker) && fs.readFileSync(tracker, 'utf8').includes('포지셔널e2e');
|
|
6125
|
+
const cwdClean = !fs.existsSync(path.join(cwd2, '.harness'));
|
|
6126
|
+
fs.rmSync(ws, { recursive: true, force: true });
|
|
6127
|
+
fs.rmSync(cwd2, { recursive: true, force: true });
|
|
6128
|
+
ok = savedToWs && cwdClean;
|
|
6129
|
+
} catch {}
|
|
6130
|
+
console.log(ok ? '✓ B(1.9.442) UR-0141: task positional path 저장 + cwd 오염 차단' : '✗ task positional path 실패');
|
|
6131
|
+
if (!ok) failed++;
|
|
6132
|
+
}
|
|
6133
|
+
|
|
6114
6134
|
console.log(`\nE2E result: ${total - failed}/${total} passed · ${((Date.now() - _e2eStart) / 1000).toFixed(0)}s`);
|
|
6115
6135
|
if (failed > 0) process.exit(1);
|