leerness 1.9.398 β 1.9.400
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 +37 -0
- package/README.md +5 -5
- package/bin/harness.js +19 -11
- package/lib/pure-utils.js +8 -1
- package/package.json +1 -1
- package/scripts/e2e.js +44 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,42 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.9.400 β 2026-06-07 π β anti-laziness λͺ
λ Ή --json μλ¬ κ΅¬μ‘°ν (7λ²μ§Έ λ²κ·ΈννΈ P1-B, UR-0105)
|
|
4
|
+
|
|
5
|
+
**π verify-claim / optimism-check / honesty-check μ `--json` μλ¬λ ꡬ쑰ν JSON β AI μμ΄μ νΈ self-gate κ²μ¦ λͺ
λ Ήμ μλν μ λ’°μ±.**
|
|
6
|
+
|
|
7
|
+
### λ°°κ²½ (7λ²μ§Έ λ²κ·ΈννΈ P1-B Β· failJson ν¨ν΄ νλ)
|
|
8
|
+
λ²κ·ΈννΈ: μ 3κ° anti-laziness κ²μ¦ λͺ
λ Ή(AI μμ΄μ νΈκ° μκΈ° κ²μ¦μ μ¬μ©)μ΄ μλ T-ID μ `--json` μΌλ‘ νΈμΆ μ `β ... μμ` ν
μ€νΈλ₯Ό stdout μ μΆλ ₯ β JSON.parse ν¬λμ. 1.9.398 μ failJson ν¬νΌλ₯Ό μ΄λ€μ νλ μ μ©.
|
|
9
|
+
|
|
10
|
+
### ꡬν
|
|
11
|
+
- verify-claim / optimism-check: missing_args / not_found λ₯Ό `failJson(_j, ...)` λ‘.
|
|
12
|
+
- honesty-check: T-ID not_found λ₯Ό failJson λ‘.
|
|
13
|
+
- μ¬λμ© μΆλ ₯(--json μμ λ) 무λ³κ²½.
|
|
14
|
+
|
|
15
|
+
### κ²μ¦ (νκ· 0)
|
|
16
|
+
- **selftest 145β146 PASS** (3κ° λͺ
λ Ή failJson μμ΄μ΄ μμ€ νμΈ).
|
|
17
|
+
- **E2E 338β339 PASS** (3κ° --json μλ¬ β {ok:false,code:not_found} exit1 + μ¬λμ© β ν
μ€νΈ 보쑴).
|
|
18
|
+
|
|
19
|
+
## 1.9.399 β 2026-06-07 β ν
μ΄λΈμ
injection μ°¨λ¨: task/rule νμ΄νΒ·κ°ν (7λ²μ§Έ λ²κ·ΈννΈ P1-A, UR-0104)
|
|
20
|
+
|
|
21
|
+
**π‘ λ°μ΄ν° λ¬΄κ²°μ± β task/rule ν
μ€νΈμ νμ΄ν(|)Β·κ°ν(\\n)μ΄ progress-tracker/rules.md νλ₯Ό μμΒ·κ°μ§ν μ£Όμ
Β·λ©±λ±μ± 무λ ₯ννλ κ²μ μ°¨λ¨(μ
μ΄μ€μΌμ΄ν).**
|
|
22
|
+
|
|
23
|
+
### λ°°κ²½ (7λ²μ§Έ β λ΄λΆ λ©ν°μμ΄μ νΈ λ²κ·ΈννΈ, μΈλΆλ¦¬λ·° μ΄μ)
|
|
24
|
+
38 μμ΄μ νΈ ν¬κ΄ λ²κ·ΈννΈ(31ν보βconfirmed 30/refuted 1)κ° μΈλΆ 리뷰(codex/Opus)κ° λͺ» λ³Έ **ν
μ΄λΈμ
injection ν΄λμ€**λ₯Ό λ°κ²¬. μ§μ μ¬ν κ²μ¦:
|
|
25
|
+
- `task add 'fix login | bypass'` β progress-tracker ν μ»¬λΌ μννΈ, update μ μꡬ μ λ¨(JSON λ°±μ
μμ).
|
|
26
|
+
- `task add '...\\n| T-9999 | done | ...'` β κ°μ§ done ν μ£Όμ
+ μ€μ task λ¬΄μ± μμ€.
|
|
27
|
+
- `rule add 'a | b'` β rules.md μ»¬λΌ λ°λ¦Ό + dedup(1.9.212) 무λ ₯νλ‘ μ€λ³΅ λ£° 무ν μμ±.
|
|
28
|
+
|
|
29
|
+
### ꡬν
|
|
30
|
+
- **`_cellSafe`/`_cellUnescape`** μμ ν¬νΌ(lib/pure-utils.js): μ°κΈ° μ κ°νβ곡백 + `|`β`\\|`, μ½κΈ° μ λΉμ΄μ€μΌμ΄ν νμ΄ν(`/(?<!\\)\\|/`)μμλ§ λΆλ¦¬ ν `\\|`β`|` 볡μ.
|
|
31
|
+
- μ μ©: `writeProgressRows`/`readProgressRows`(task) + `writeRules`/`readRules`(rule). μ¬μ©μ ν
μ€νΈμ νμ΄νλ **보쑴**(볡μ), κ°νμ 곡백ν(ν μ£Όμ
μ°¨λ¨).
|
|
32
|
+
|
|
33
|
+
### κ²μ¦ (νκ· 0)
|
|
34
|
+
- **selftest 144β145 PASS** (cellSafe/Unescape reference-equality + νμ΄ν round-trip + κ°ν μ κ±° + μμ΄μ΄).
|
|
35
|
+
- **E2E 337β338 PASS** (task νμ΄ν 보쑴/κ°ν κ°μ§ν μ°¨λ¨ + rule νμ΄ν 보쑴/λ©±λ± ν볡/status λΉμ€μΌ).
|
|
36
|
+
- μ€μΈ‘: `task add 'fix login | bypass'` μλ³Έ 보쑴, κ°ν μ£Όμ
T-9999 μ°¨λ¨, rule μ€λ³΅ add β skip(λ©±λ±).
|
|
37
|
+
|
|
38
|
+
### λ€μ(κ°μ ν΄λ¬μ€ν°): decisions/lessons MD projection μ
μμ ν(UR-0104 μμ¬, JSON canonical μ μ΄λ―Έ μμ ).
|
|
39
|
+
|
|
3
40
|
## 1.9.398 β 2026-06-07 β --json μλ¬ κ²½λ‘ κ΅¬μ‘°ν (6λ²μ§Έ μΈλΆνκ° P1-C, UR-0099)
|
|
4
41
|
|
|
5
42
|
**π `--json` λͺ¨λμμ μλ¬λ ꡬ쑰ν JSON(`{ok:false,error,code}`) β AI μμ΄μ νΈκ° μλ¬ κ²½λ‘μμ JSON.parse μ€ν¨νμ§ μλλ‘.**
|
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
> **AI μ½λ© μμ΄μ νΈμ κ±°μ§ μλ£Β·μ€λ³΅Β·λ§κ°Β·μΆ©λμ λ§μμ£Όλ κ²μΒ·κΈ°μ΅Β·νμ
CLI νλ€μ€.**
|
|
4
4
|
> **A CLI harness that stops AI coding agents from faking completion, duplicating work, forgetting context, and colliding.**
|
|
5
5
|
|
|
6
|
-
[](https://www.npmjs.com/package/leerness) [](https://www.npmjs.com/package/leerness) []() []() []() []() []() []()
|
|
7
7
|
|
|
8
8
|
```
|
|
9
9
|
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
@@ -471,7 +471,7 @@ MIT β Β© leerness contributors
|
|
|
471
471
|
<!-- leerness:project-readme:start -->
|
|
472
472
|
## Leerness Project Harness
|
|
473
473
|
|
|
474
|
-
μ΄ νλ‘μ νΈλ Leerness v1.9.
|
|
474
|
+
μ΄ νλ‘μ νΈλ Leerness v1.9.400 νλ€μ€λ₯Ό μ¬μ©ν©λλ€. AI μμ΄μ νΈλ μμ
μ `leerness handoff`λ‘ μ»¨ν
μ€νΈλ₯Ό μ μ¬νκ³ , μμ
ν `leerness check`/`leerness audit`/`leerness session close`λ₯Ό μνν΄μΌ ν©λλ€.
|
|
475
475
|
|
|
476
476
|
### μ μ²΄μ± β AI μμ΄μ νΈ μ΄μ λ μ΄μ΄ (UR-0030)
|
|
477
477
|
|
|
@@ -525,7 +525,7 @@ leerness memory restore decision <date|title>
|
|
|
525
525
|
|
|
526
526
|
### MCP server (μΈλΆ AI ν΅ν©)
|
|
527
527
|
|
|
528
|
-
Leerness v1.9.
|
|
528
|
+
Leerness v1.9.400λ stdio JSON-RPC MCP serverλ₯Ό λ΄μ₯ν©λλ€ β Claude Code Β· Cursor Β· Codex CLI λ± μΈλΆ AIμ **85κ° λꡬ**λ₯Ό λ
ΈμΆ:
|
|
529
529
|
|
|
530
530
|
```jsonc
|
|
531
531
|
// μΉ΄ν
κ³ λ¦¬λ³
|
|
@@ -546,7 +546,7 @@ Leerness v1.9.398λ stdio JSON-RPC MCP serverλ₯Ό λ΄μ₯ν©λλ€ β Claude Cod
|
|
|
546
546
|
`<<autonomous-loop-dynamic>>` μ νΈλ§ 보λ΄λ©΄ AIκ°:
|
|
547
547
|
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) λ€μ λΌμ΄λ μμ½.
|
|
548
548
|
|
|
549
|
-
νμ¬ λμ : **70 λΌμ΄λ (1.9.40 β 1.9.
|
|
549
|
+
νμ¬ λμ : **70 λΌμ΄λ (1.9.40 β 1.9.400)** Β· λ§€ λΌμ΄λ GitHub release/νκ·Έ μμ± Β· _reports/λ λΉκ³΅κ° 보쑴.
|
|
550
550
|
|
|
551
551
|
### μ±λ₯ κ°μ΄λ (1.9.140 μΈ‘μ )
|
|
552
552
|
|
|
@@ -584,6 +584,6 @@ leerness release pack --close --auto-main-push
|
|
|
584
584
|
- `.harness/session-handoff.md`: λ€μ μΈμ
μΈμμΈκ³ (μλ μμ±)
|
|
585
585
|
- `.harness/lessons.md` / `decisions.md` / `rules.md`: μꡬ λ©λͺ¨λ¦¬ (5 surface)
|
|
586
586
|
|
|
587
|
-
Last synced by Leerness v1.9.
|
|
587
|
+
Last synced by Leerness v1.9.400: 2026-06-06
|
|
588
588
|
<!-- leerness:project-readme:end -->
|
|
589
589
|
|
package/bin/harness.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 } = require('../lib/pure-utils'); // 1.9.318~
|
|
28
|
+
_parseChangelogBetween, _cellSafe, _cellUnescape } = require('../lib/pure-utils'); // 1.9.318~399 (UR-0025/0053/0075/0086/0087/0104): μμ μ νΈ λͺ¨λ λΆλ¦¬
|
|
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.400';
|
|
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') μ νΈμ€νΈ νλ‘μΈμ€ μ€μΌ.
|
|
@@ -3003,6 +3003,8 @@ function _selfTestCases() {
|
|
|
3003
3003
|
{ name: '6λ²μ§Έ μΈλΆνκ°/codex P1-B: task drop μ‘΄μ¬νμΈ κ°λ β μλ ID κ°μ§ row λ°©μ§ (1.9.396)', run: () => { const src = read(__filename); const i = src.indexOf('function taskDrop(root, id)'); if (i < 0) return false; const body = src.slice(i, i + 700); return body.includes('not found in progress-tracker.md') && body.includes('rows.find(r => r.id === id)') && body.includes('_requireInit'); } },
|
|
3004
3004
|
{ name: '6λ²μ§Έ μΈλΆνκ°/codex P1-A (UR-0098): install-safety λ μνΌ μ
Έ-λ¬΄κ΄ + hardeningNote (1.9.397)', run: () => { if (typeof installSafetyCmd !== 'function') return false; const save = process.argv; const _w = process.stdout.write; let out = ''; try { process.argv = ['node', 'h', 'install-safety', '--json']; process.stdout.write = s => { out += s; return true; }; installSafetyCmd({ json: true }); } catch {} finally { process.stdout.write = _w; process.argv = save; } let j; try { j = JSON.parse(out); } catch {} const noPosixPrefix = !!j && Array.isArray(j.safeInstall) && !j.safeInstall.some(x => /^npm_config_\w+=/.test(String(x).trim())); const crossShell = !!j && j.safeInstall.filter(x => String(x).includes('npx --yes')).length >= 2; const noteOk = !!j && typeof j.hardeningNote === 'string' && j.hardeningNote.includes('PowerShell'); return noPosixPrefix && crossShell && noteOk; } },
|
|
3005
3005
|
{ name: '6λ²μ§Έ μΈλΆνκ°/codex P1-C (UR-0099): --json μλ¬ κ²½λ‘ κ΅¬μ‘°ν failJson + μμ΄μ΄ (1.9.398)', run: () => { const io = require('../lib/io'); if (io.failJson !== failJson) return false; const _w = process.stdout.write; const saved = process.exitCode; let jOut = '', hOut = ''; let jExit = 0; try { process.stdout.write = s => { jOut += s; return true; }; process.exitCode = 0; failJson(true, 'tc', 'm'); jExit = process.exitCode; process.stdout.write = s => { hOut += s; return true; }; process.exitCode = 0; failJson(false, 'c', 'humanmsg'); } catch {} finally { process.stdout.write = _w; process.exitCode = saved; } let pj; try { pj = JSON.parse(jOut); } catch {} const jsonOk = !!pj && pj.ok === false && pj.code === 'tc' && pj.error === 'm' && jExit === 1; const humanOk = hOut.includes('β') && hOut.includes('humanmsg') && !hOut.includes('{'); const src = read(__filename); const wired = src.includes("failJson(_j, 'missing_args'") && src.includes("failJson(_j, 'spec_not_found'"); return jsonOk && humanOk && wired; } },
|
|
3006
|
+
{ name: '7λ²μ§Έ λ²κ·ΈννΈ P1-A (UR-0104): ν
μ΄λΈμ
μμ ν _cellSafe/_cellUnescape (νμ΄ν/κ°ν injection μ°¨λ¨) (1.9.399)', run: () => { const m = require('../lib/pure-utils'); if (m._cellSafe !== _cellSafe || m._cellUnescape !== _cellUnescape) return false; const safe = _cellSafe('fix | bug\nrow2'); const noRaw = !/(?<!\\)\|/.test(safe) && !/[\r\n]/.test(safe); const pipeRt = _cellUnescape(_cellSafe('a | b | c')) === 'a | b | c'; const nlGone = _cellSafe('a\nb') === 'a b'; const src = read(__filename); const wired = src.includes('_cellSafe(r.request)') && src.includes('_cellSafe(r.rule)'); return noRaw && pipeRt && nlGone && wired; } },
|
|
3007
|
+
{ name: '7λ²μ§Έ λ²κ·ΈννΈ P1-B (UR-0105): verify-claim/optimism-check/honesty-check --json μλ¬ κ΅¬μ‘°ν (1.9.400)', run: () => { const src = read(__filename); const vc = /function verifyClaimCmd[\s\S]{0,400}?failJson\(_j, 'not_found'/.test(src); const oc = /function optimismCheckCmd[\s\S]{0,400}?failJson\(_j, 'not_found'/.test(src); const hc = /function honestyCheckCmd[\s\S]{0,900}?failJson\(has\('--json'\), 'not_found'/.test(src); return vc && oc && hc; } },
|
|
3006
3008
|
{ name: 'VERSION νμ (x.y.z)', run: () => /^\d+\.\d+\.\d+$/.test(VERSION) }
|
|
3007
3009
|
];
|
|
3008
3010
|
}
|
|
@@ -5832,7 +5834,8 @@ function readProgressRows(root) {
|
|
|
5832
5834
|
const rows = [];
|
|
5833
5835
|
for (const line of text.split('\n')) {
|
|
5834
5836
|
if (!/^\| (?:T|M|D)-\d{4} \|/.test(line)) continue;
|
|
5835
|
-
|
|
5837
|
+
// 1.9.399 (7λ²μ§Έ λ²κ·ΈννΈ P1-A, UR-0104): λΉμ΄μ€μΌμ΄ν νμ΄νμμλ§ λΆλ¦¬ + μ
볡μ β μ¬μ©μ ν
μ€νΈμ '|'(μ΄μ€μΌμ΄νλ¨)μ΄ μ»¬λΌμ κΉ¨μ§ μμ.
|
|
5838
|
+
const cells = line.split(/(?<!\\)\|/).slice(1, -1).map(s => _cellUnescape(s).trim());
|
|
5836
5839
|
if (cells.length < 6) continue;
|
|
5837
5840
|
const [id, status, request, evidence, nextAction, updated] = cells;
|
|
5838
5841
|
rows.push({ id, status, request, evidence, nextAction, updated });
|
|
@@ -5853,8 +5856,9 @@ function progressHeader(root) {
|
|
|
5853
5856
|
return text.slice(0, text.indexOf('\n', idx)).trimEnd();
|
|
5854
5857
|
}
|
|
5855
5858
|
function writeProgressRows(root, header, rows) {
|
|
5859
|
+
// 1.9.399 (7λ²μ§Έ λ²κ·ΈννΈ P1-A, UR-0104): μ
μ½ν
μΈ μμ ν β κ°νβ곡백, '|'β'\|'. μ¬μ©μ ν
μ€νΈμ νμ΄ν/κ°νμ΄ ν νμ μμ/μ£Όμ
λͺ» ν¨.
|
|
5856
5860
|
const composed = header + '\n' +
|
|
5857
|
-
rows.map(r => `| ${r.id} | ${r.status} | ${r.request} | ${r.evidence} | ${r.nextAction} | ${r.updated} |`).join('\n') +
|
|
5861
|
+
rows.map(r => `| ${_cellSafe(r.id)} | ${_cellSafe(r.status)} | ${_cellSafe(r.request)} | ${_cellSafe(r.evidence)} | ${_cellSafe(r.nextAction)} | ${_cellSafe(r.updated)} |`).join('\n') +
|
|
5858
5862
|
(rows.length ? '\n' : '');
|
|
5859
5863
|
writeUtf8(progressPath(root), composed);
|
|
5860
5864
|
}
|
|
@@ -9232,10 +9236,11 @@ function _gitChangedFiles(root) {
|
|
|
9232
9236
|
// _claimFileInGit β lib/analyzers.js (1.9.304 UR-0025)
|
|
9233
9237
|
function verifyClaimCmd(root, taskId) {
|
|
9234
9238
|
root = absRoot(root);
|
|
9235
|
-
|
|
9239
|
+
const _j = has('--json'); // 1.9.400 (7λ²μ§Έ λ²κ·ΈννΈ P1-B, UR-0105): --json μλ¬λ ꡬ쑰ν
|
|
9240
|
+
if (!taskId) return failJson(_j, 'missing_args', 'verify-claim <T-ID> νμ. μ: leerness verify-claim T-0008');
|
|
9236
9241
|
const rows = readProgressRows(root);
|
|
9237
9242
|
const row = rows.find(r => r.id === taskId);
|
|
9238
|
-
if (!row) return
|
|
9243
|
+
if (!row) return failJson(_j, 'not_found', `progress-tracker.mdμ ${taskId} μμ.`);
|
|
9239
9244
|
|
|
9240
9245
|
const evidence = row.evidence || '';
|
|
9241
9246
|
// 1.9.20: νμΌ κ²½λ‘ μΆμΆ β λλ©μΈ ν΄λ μλ μΈμ + λ£¨νΈ λ©ννμΌ
|
|
@@ -9991,7 +9996,7 @@ function honestyCheckCmd(root, arg1) {
|
|
|
9991
9996
|
if (textArg) { subject = String(textArg); sourceLabel = '--text'; }
|
|
9992
9997
|
else if (arg1 && !arg1.startsWith('-')) {
|
|
9993
9998
|
const row = readProgressRows(root).find(r => r.id === arg1);
|
|
9994
|
-
if (!row) {
|
|
9999
|
+
if (!row) { failJson(has('--json'), 'not_found', `progress-tracker.mdμ ${arg1} μμ.`); return; } // 1.9.400 (UR-0105): --json μλ¬ κ΅¬μ‘°ν
|
|
9995
10000
|
subject = row.evidence || ''; sourceLabel = `${arg1} evidence`;
|
|
9996
10001
|
} else { fail('μ¬μ©λ²: leerness honesty-check <T-ID> λλ leerness honesty-check --text "<μ£Όμ₯>"'); process.exitCode = 1; return; }
|
|
9997
10002
|
const r = _epistemicHonestyCheck(subject);
|
|
@@ -10009,10 +10014,11 @@ function honestyCheckCmd(root, arg1) {
|
|
|
10009
10014
|
}
|
|
10010
10015
|
function optimismCheckCmd(root, taskId) {
|
|
10011
10016
|
root = absRoot(root || process.cwd());
|
|
10012
|
-
|
|
10017
|
+
const _j = has('--json'); // 1.9.400 (7λ²μ§Έ λ²κ·ΈννΈ P1-B, UR-0105): --json μλ¬λ ꡬ쑰ν
|
|
10018
|
+
if (!taskId) return failJson(_j, 'missing_args', 'optimism-check <T-ID> νμ. μ: leerness optimism-check T-0001');
|
|
10013
10019
|
const rows = readProgressRows(root);
|
|
10014
10020
|
const row = rows.find(r => r.id === taskId);
|
|
10015
|
-
if (!row) return
|
|
10021
|
+
if (!row) return failJson(_j, 'not_found', `progress-tracker.mdμ ${taskId} μμ.`);
|
|
10016
10022
|
|
|
10017
10023
|
const codeText = _scanCodeForPatterns(root);
|
|
10018
10024
|
const suspects = _detectOptimism(row.evidence || '', codeText);
|
|
@@ -13565,7 +13571,8 @@ function readRules(root) {
|
|
|
13565
13571
|
const rules = [];
|
|
13566
13572
|
for (const line of read(f).split('\n')) {
|
|
13567
13573
|
if (!/^\| R-\d{4} \|/.test(line)) continue;
|
|
13568
|
-
|
|
13574
|
+
// 1.9.399 (7λ²μ§Έ λ²κ·ΈννΈ P1-A, UR-0104): λΉμ΄μ€μΌμ΄ν νμ΄ν λΆλ¦¬ + 볡μ β rule ν
μ€νΈμ '|'κ° μ»¬λΌ λ°λ¦Ό/λ©±λ±μ± 무λ ₯νλ₯Ό λͺ» μΌμΌν΄.
|
|
13575
|
+
const cells = line.split(/(?<!\\)\|/).slice(1, -1).map(s => _cellUnescape(s).trim());
|
|
13569
13576
|
if (cells.length < 6) continue;
|
|
13570
13577
|
rules.push({ id: cells[0], trigger: cells[1], rule: cells[2], added: cells[3], status: cells[4], lastVerified: cells[5] });
|
|
13571
13578
|
}
|
|
@@ -13573,7 +13580,8 @@ function readRules(root) {
|
|
|
13573
13580
|
}
|
|
13574
13581
|
|
|
13575
13582
|
function writeRules(root, rules) {
|
|
13576
|
-
|
|
13583
|
+
// 1.9.399 (7λ²μ§Έ λ²κ·ΈννΈ P1-A, UR-0104): μ
μμ ν β rule ν
μ€νΈμ νμ΄ν/κ°ν μ°¨λ¨(μ»¬λΌ λ°λ¦ΌΒ·μ€λ³΅ λ£° 무νμμ± λ°©μ§).
|
|
13584
|
+
const body = rules.map(r => `| ${_cellSafe(r.id)} | ${_cellSafe(r.trigger)} | ${_cellSafe(r.rule)} | ${_cellSafe(r.added)} | ${_cellSafe(r.status)} | ${_cellSafe(r.lastVerified || '-')} |`).join('\n');
|
|
13577
13585
|
writeUtf8(rulesPath(root), _rulesHeader() + '\n' + body + (body ? '\n' : ''));
|
|
13578
13586
|
}
|
|
13579
13587
|
|
package/lib/pure-utils.js
CHANGED
|
@@ -935,7 +935,9 @@ module.exports = {
|
|
|
935
935
|
// 1.9.391 (UR-0025): feature μν₯ BFS (μμ, 곡μ )
|
|
936
936
|
_featureImpactBfs,
|
|
937
937
|
// 1.9.393 (UR-0025): CHANGELOG λ²μ κ΅¬κ° μ°¨λΆ νμ (μμ, 곡μ )
|
|
938
|
-
_parseChangelogBetween
|
|
938
|
+
_parseChangelogBetween,
|
|
939
|
+
// 1.9.399 (7λ²μ§Έ λ²κ·ΈννΈ P1-A, UR-0104): markdown ν
μ΄λΈ μ
μμ ν(νμ΄ν/κ°ν injection μ°¨λ¨)
|
|
940
|
+
_cellSafe, _cellUnescape
|
|
939
941
|
};
|
|
940
942
|
|
|
941
943
|
// 1.9.355 (UR-0075 Phase A): AI μμ΄μ νΈμ© ν¬λ‘μ€λ²μ λ§μ΄κ·Έλ μ΄μ
μμ μν¬νλ‘ κ°μ΄λ (μμ ν
μ€νΈ). μμμ€μΉ + --path + λ°±μ
+ diff κ²μ¦.
|
|
@@ -1167,3 +1169,8 @@ function _parseChangelogBetween(changelogText, fromV, toV) {
|
|
|
1167
1169
|
}
|
|
1168
1170
|
return ranged;
|
|
1169
1171
|
}
|
|
1172
|
+
// 1.9.399 (7λ²μ§Έ λ²κ·ΈννΈ P1-A, UR-0104): markdown ν
μ΄λΈ μ
μμ ν β κ°ν(ν μ£Όμ
)Β·νμ΄ν(μ»¬λΌ μννΈ) μ°¨λ¨.
|
|
1173
|
+
// _cellSafe: μ°κΈ° μ κ°νβ곡백, '|'β'\|'(μ΄μ€μΌμ΄ν). _cellUnescape: μ½κΈ° μ '\|'β'|' 볡μ.
|
|
1174
|
+
// table νμλ split(/(?<!\\)\|/) λ‘ λΉμ΄μ€μΌμ΄ν νμ΄νμμλ§ λΆλ¦¬ β μ¬μ©μ ν
μ€νΈμ νμ΄ν/κ°νμ΄ λ°μ΄ν° μμΒ·κ°μ§ν μ£Όμ
μ λͺ» μΌμΌν΄.
|
|
1175
|
+
function _cellSafe(s) { return String(s == null ? '' : s).replace(/\r\n|\r|\n/g, ' ').replace(/\|/g, '\\|'); }
|
|
1176
|
+
function _cellUnescape(s) { return String(s == null ? '' : s).replace(/\\\|/g, '|'); }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "leerness",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.400",
|
|
4
4
|
"description": "Leerness: λΉνκ΄΄ λ§μ΄κ·Έλ μ΄μ
, μλ λ²μ κ°μ§Β·μ
λ°μ΄νΈ, κ³ν/μ§ν/νΈλμ€ν μλν, κ²μΌλ¦Β·μν¬λ¦ΏΒ·μΈμ½λ© μλ κ°λ, Claude Code μ¬λμ ν΅ν©μ κ°μΆ νκ΅μ΄ μ°μ AI κ°λ° νλ€μ€.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"leerness",
|
package/scripts/e2e.js
CHANGED
|
@@ -5558,5 +5558,49 @@ total++;
|
|
|
5558
5558
|
if (!ok) failed++;
|
|
5559
5559
|
}
|
|
5560
5560
|
|
|
5561
|
+
// 1.9.399 νκ· (7λ²μ§Έ λ²κ·ΈννΈ P1-A, UR-0104): ν
μ΄λΈμ
injection μ°¨λ¨ β task/rule ν
μ€νΈμ νμ΄ν(|) 보쑴 + κ°ν κ°μ§ν μ£Όμ
μ°¨λ¨ + rule λ©±λ±μ±
|
|
5562
|
+
total++;
|
|
5563
|
+
{
|
|
5564
|
+
let ok = false;
|
|
5565
|
+
try {
|
|
5566
|
+
const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-cellinj-'));
|
|
5567
|
+
cp.spawnSync(process.execPath, [CLI, 'init', d, '--yes', '--language', 'ko'], { encoding: 'utf8', timeout: 30000 });
|
|
5568
|
+
cp.spawnSync(process.execPath, [CLI, 'task', 'add', 'fix login | bypass', '--path', d, '--no-review'], { encoding: 'utf8', timeout: 15000 });
|
|
5569
|
+
cp.spawnSync(process.execPath, [CLI, 'task', 'add', 'real\n| T-9999 | done | x | y | z | w |', '--path', d, '--no-review'], { encoding: 'utf8', timeout: 15000 });
|
|
5570
|
+
const tl = JSON.parse(cp.spawnSync(process.execPath, [CLI, 'task', 'list', '--path', d, '--json'], { encoding: 'utf8', timeout: 15000 }).stdout);
|
|
5571
|
+
const ts = tl.tasks || tl;
|
|
5572
|
+
const pipeOk = ts.some(t => t.request === 'fix login | bypass'); // νμ΄ν μλ³Έ 보쑴
|
|
5573
|
+
const noInject = !ts.some(t => t.id === 'T-9999' || t.status === 'done'); // κ°ν κ°μ§ν μ£Όμ
μ°¨λ¨
|
|
5574
|
+
// rule νμ΄ν + λ©±λ±μ±(μ€λ³΅ add β skip)
|
|
5575
|
+
cp.spawnSync(process.execPath, [CLI, 'rule', 'add', 'lint | typecheck', '--trigger', 'every-commit', '--path', d], { encoding: 'utf8', timeout: 15000 });
|
|
5576
|
+
cp.spawnSync(process.execPath, [CLI, 'rule', 'add', 'lint | typecheck', '--trigger', 'every-commit', '--path', d], { encoding: 'utf8', timeout: 15000 });
|
|
5577
|
+
const rl = JSON.parse(cp.spawnSync(process.execPath, [CLI, 'rule', 'list', '--path', d, '--json'], { encoding: 'utf8', timeout: 15000 }).stdout);
|
|
5578
|
+
const rs = rl.rules || rl;
|
|
5579
|
+
const ruleOk = rs.length === 1 && rs[0].rule === 'lint | typecheck' && rs[0].status === 'active'; // νμ΄ν 보쑴 + λ©±λ± + status λΉμ€μΌ
|
|
5580
|
+
fs.rmSync(d, { recursive: true, force: true });
|
|
5581
|
+
ok = pipeOk && noInject && ruleOk;
|
|
5582
|
+
} catch {}
|
|
5583
|
+
console.log(ok ? 'β B(1.9.399) 7thλ²κ·ΈννΈ P1-A: ν
μ΄λΈμ
injection μ°¨λ¨(task/rule νμ΄ν 보쑴+κ°ν κ°μ§ν μ°¨λ¨+rule λ©±λ±) (UR-0104)' : 'β ν
μ΄λΈμ
injection μ°¨λ¨ μ€ν¨');
|
|
5584
|
+
if (!ok) failed++;
|
|
5585
|
+
}
|
|
5586
|
+
|
|
5587
|
+
// 1.9.400 νκ· (7λ²μ§Έ λ²κ·ΈννΈ P1-B, UR-0105): verify-claim/optimism-check/honesty-check --json μλ¬κ° ꡬ쑰ν JSON + μ¬λμ© λ³΄μ‘΄
|
|
5588
|
+
total++;
|
|
5589
|
+
{
|
|
5590
|
+
let ok = false;
|
|
5591
|
+
try {
|
|
5592
|
+
const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-antilazyjson-'));
|
|
5593
|
+
cp.spawnSync(process.execPath, [CLI, 'init', d, '--yes', '--language', 'ko'], { encoding: 'utf8', timeout: 30000 });
|
|
5594
|
+
const jsonErr = (cmd) => { const r = cp.spawnSync(process.execPath, [CLI, cmd, 'T-9999', '--path', d, '--json'], { encoding: 'utf8', timeout: 15000 }); try { const j = JSON.parse(r.stdout); return j.ok === false && j.code === 'not_found' && r.status === 1; } catch { return false; } };
|
|
5595
|
+
const allJson = jsonErr('verify-claim') && jsonErr('optimism-check') && jsonErr('honesty-check');
|
|
5596
|
+
const hr = cp.spawnSync(process.execPath, [CLI, 'verify-claim', 'T-9999', '--path', d], { encoding: 'utf8', timeout: 15000 });
|
|
5597
|
+
const humanOk = hr.status === 1 && /β/.test(hr.stdout || '') && !/^\s*\{/.test(hr.stdout || '');
|
|
5598
|
+
fs.rmSync(d, { recursive: true, force: true });
|
|
5599
|
+
ok = allJson && humanOk;
|
|
5600
|
+
} catch {}
|
|
5601
|
+
console.log(ok ? 'β B(1.9.400) 7thλ²κ·ΈννΈ P1-B: anti-laziness(verify-claim/optimism/honesty) --json μλ¬ κ΅¬μ‘°ν + μ¬λμ© λ³΄μ‘΄ (UR-0105)' : 'β anti-laziness --json μλ¬ μ€ν¨');
|
|
5602
|
+
if (!ok) failed++;
|
|
5603
|
+
}
|
|
5604
|
+
|
|
5561
5605
|
console.log(`\nE2E result: ${total - failed}/${total} passed Β· ${((Date.now() - _e2eStart) / 1000).toFixed(0)}s`);
|
|
5562
5606
|
if (failed > 0) process.exit(1);
|