@takagaki/cortex-decisions-viewer 0.12.119 → 0.12.121
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/dist/build.js +43 -7
- package/dist/change-log.js +187 -0
- package/dist/cli.js +1 -1
- package/dist/hands.js +52 -0
- package/dist/ledger-dir.js +1 -1
- package/dist/render.js +180 -150
- package/dist/repo.js +11 -508
- package/dist/story.js +66 -3
- package/dist/supersedes.js +8 -21
- package/package.json +2 -1
package/dist/story.js
CHANGED
|
@@ -36,6 +36,7 @@ exports.readStoryDigest = readStoryDigest;
|
|
|
36
36
|
* 印の HTML コメント(`<!-- cortex-engine:story-day … -->`・`story-late`・その終わり)は捨てる。本文の行に
|
|
37
37
|
* `urn:`・ファイル名・`cortex-engine`・`sha=` が紛れていたら**行ごと伏せる**(本文は AI が書いた行なので、
|
|
38
38
|
* 語だけ消すと文が壊れる)。課題番号・コミットの ID は engine の門が落とすので重ねない(`MACHINE_WORD_RE`)。
|
|
39
|
+
* 決定の記録への相対リンク(`[文字](…/x.md)`)は、門に掛ける前にリンクの文字だけに剝がす(行ごとは伏せない)。
|
|
39
40
|
* 人の追記には伏せ字を掛けない(落とすのは印の行だけ)。本文の Markdown は他の本文と同じ関所(marked → sanitize)を呼び手が通す。
|
|
40
41
|
*/
|
|
41
42
|
const node_fs_1 = require("node:fs");
|
|
@@ -61,6 +62,26 @@ const LATE_END_RE = /^<!--\s*\/cortex-engine:story-late\s*-->$/;
|
|
|
61
62
|
* 重ねると、門を通った正当な行(`COVID-19`・`ISO-27001`・`A-1 案`)まで画面が黙って消す(実装レビュー 🟠1)
|
|
62
63
|
*/
|
|
63
64
|
const MACHINE_WORD_RE = /urn:|cortex-engine|sha=|<!--|-->|\.md\b|Days\/|story-days\.json|\.cortex\//;
|
|
65
|
+
/**
|
|
66
|
+
* Markdown のリンクのうち、行き先が相対パスで `.md` で終わるもの(決定の記録など)。画面は 1 枚の HTML で行き先を開けないので、
|
|
67
|
+
* 門に掛ける前にリンクの文字だけに剝がす。外の URL(`https://…`・`<https://…>`)は対象外(そのまま残す)。
|
|
68
|
+
* 本文に素で書かれた `foo.md` はここでは剝がさず、`MACHINE_WORD_RE` が行ごと伏せる
|
|
69
|
+
*/
|
|
70
|
+
const RELATIVE_MD_LINK_RE = /\[([^\]\n]*)\]\(\s*<?(?![a-zA-Z][a-zA-Z0-9+.-]*:|\/\/|#)[^)\s<>]*\.md(?:#[^)\s<>]*)?>?\s*\)/g;
|
|
71
|
+
const LINK_OPEN_RE = /\[[^\]\n]*\]\([^)\s]*/g;
|
|
72
|
+
/**
|
|
73
|
+
* 相対 `.md` のリンクを文字に剝がす。剝がした結果、剝がす前の行に無かった `](` のリンクが現れる行(入れ子のリンクなど)は、
|
|
74
|
+
* 剝がさず元のまま返す(`.md` が残るので `MACHINE_WORD_RE` が行ごと伏せる)。後ろの関所に頼らない
|
|
75
|
+
*/
|
|
76
|
+
const unlinkRelativeMd = (line) => {
|
|
77
|
+
const out = line.replace(RELATIVE_MD_LINK_RE, "$1");
|
|
78
|
+
if (out === line)
|
|
79
|
+
return line;
|
|
80
|
+
const before = new Set(line.match(LINK_OPEN_RE) ?? []);
|
|
81
|
+
return (out.match(LINK_OPEN_RE) ?? []).some((m) => !before.has(m)) ? line : out;
|
|
82
|
+
};
|
|
83
|
+
/** 太字だけの 1 行(筋の名前の行) */
|
|
84
|
+
const BOLD_ONLY_RE = /^\*\*[^*]+\*\*$/;
|
|
64
85
|
/** 見出しの行(`##`〜`######`) */
|
|
65
86
|
const SUBHEADING_RE = /^#{2,6}\s/;
|
|
66
87
|
/** 伏せて本文が空になった日の 1 行 */
|
|
@@ -124,6 +145,20 @@ function withoutEmptySections(lines) {
|
|
|
124
145
|
return false;
|
|
125
146
|
});
|
|
126
147
|
}
|
|
148
|
+
/** 下の行が全部伏せられて中身が無くなった太字の筋の行(`**…**` だけの行)を落とす(`withoutEmptySections` と同じ考え) */
|
|
149
|
+
function withoutEmptyBoldHeads(lines) {
|
|
150
|
+
return lines.filter((l, i) => {
|
|
151
|
+
if (!BOLD_ONLY_RE.test(l.trim()))
|
|
152
|
+
return true;
|
|
153
|
+
for (let j = i + 1; j < lines.length; j++) {
|
|
154
|
+
const t = lines[j].trim();
|
|
155
|
+
if (t === "")
|
|
156
|
+
continue;
|
|
157
|
+
return !(BOLD_ONLY_RE.test(t) || SUBHEADING_RE.test(lines[j]));
|
|
158
|
+
}
|
|
159
|
+
return false;
|
|
160
|
+
});
|
|
161
|
+
}
|
|
127
162
|
/**
|
|
128
163
|
* Days 1 枚の本文を、画面に出す Markdown にする(純関数)。
|
|
129
164
|
*
|
|
@@ -163,10 +198,11 @@ function storyDayMarkdown(content, day) {
|
|
|
163
198
|
human.push(l);
|
|
164
199
|
}
|
|
165
200
|
// 機械の行: 印と機械の語の行を伏せる
|
|
166
|
-
|
|
201
|
+
// 決定の記録へのリンクは、門に掛ける前に文字だけに剝がす(行ごと伏せない)
|
|
202
|
+
const clean = (ls) => ls.map(unlinkRelativeMd).filter((l) => !STORY_MARKER_RE.test(l.trim()) && !MACHINE_WORD_RE.test(l));
|
|
167
203
|
// 🔴 人の追記: 落とすのは**印の行だけ**(人が書いた行を画面が黙って消さない・実装レビュー 🟠2)
|
|
168
204
|
const humanKept = human.filter((l) => !STORY_MARKER_RE.test(l.trim()));
|
|
169
|
-
const out = withoutEmptySections([...clean(main), ...clean(lateAfter)]);
|
|
205
|
+
const out = withoutEmptySections(withoutEmptyBoldHeads([...clean(main), ...clean(lateAfter)]));
|
|
170
206
|
if (humanKept.some((l) => l.trim() !== ""))
|
|
171
207
|
out.push("", HUMAN_HEADING, ...humanKept);
|
|
172
208
|
// 空行の続きを 1 行に・前後の空行を落とす
|
|
@@ -363,6 +399,29 @@ function storyWeekHeading(monday) {
|
|
|
363
399
|
function storyMonthHeading(month) {
|
|
364
400
|
return `${month.slice(0, 4)}年${Number(month.slice(5, 7))}月`;
|
|
365
401
|
}
|
|
402
|
+
/**
|
|
403
|
+
* 週・月の(太字だけの行, 直後の段落〈空行まで〉)の組のうち、段落の行が全部機械の語で伏せられるものを、太字の行ごと落とす
|
|
404
|
+
* (伏せる**前**に組を作る。段落の一部だけ伏せられるなら太字は残す。日の `withoutEmptyBoldHeads` とは別物)。
|
|
405
|
+
* 太字の直後が空行・終わりで段落が無い組は触らない(旧い形の答えを変えない)
|
|
406
|
+
*/
|
|
407
|
+
function withoutHiddenBoldGroups(lines) {
|
|
408
|
+
const out = [];
|
|
409
|
+
for (let i = 0; i < lines.length; i++) {
|
|
410
|
+
const l = lines[i];
|
|
411
|
+
if (BOLD_ONLY_RE.test(l.trim())) {
|
|
412
|
+
let j = i + 1;
|
|
413
|
+
while (j < lines.length && lines[j].trim() !== "")
|
|
414
|
+
j++;
|
|
415
|
+
const para = lines.slice(i + 1, j);
|
|
416
|
+
if (para.length > 0 && para.every((x) => MACHINE_WORD_RE.test(x))) {
|
|
417
|
+
i = j - 1; // 太字の行と段落を落とす(段落の後ろの空行は残る)
|
|
418
|
+
continue;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
out.push(l);
|
|
422
|
+
}
|
|
423
|
+
return out;
|
|
424
|
+
}
|
|
366
425
|
/**
|
|
367
426
|
* 週・月の 1 枚の本文を、画面の材料にする(純関数)。
|
|
368
427
|
*
|
|
@@ -379,7 +438,8 @@ function storyDigestParts(content) {
|
|
|
379
438
|
const end = body.findIndex((l) => DIGEST_END_RE.test(l.trim()));
|
|
380
439
|
const at = body.findIndex((l, i) => i > end && l.trim() === HUMAN_HEADING);
|
|
381
440
|
// 機械の部分の `#` の見出しの行は落とす(流れは段落だけの約束。見出しが紛れると後ろの文が別の節に見える)
|
|
382
|
-
const main = (at < 0 ? body : body.slice(0, at)).filter((l) => !STORY_MARKER_RE.test(l.trim()) &&
|
|
441
|
+
const main = withoutHiddenBoldGroups((at < 0 ? body : body.slice(0, at)).map(unlinkRelativeMd).filter((l) => !STORY_MARKER_RE.test(l.trim()) && !/^\s{0,3}#{1,6}(\s|$)/.test(l)))
|
|
442
|
+
.filter((l) => !MACHINE_WORD_RE.test(l));
|
|
383
443
|
const human = (at < 0 ? [] : body.slice(at + 1)).filter((l) => !STORY_MARKER_RE.test(l.trim()));
|
|
384
444
|
let phase = null;
|
|
385
445
|
const flow = [];
|
|
@@ -390,6 +450,9 @@ function storyDigestParts(content) {
|
|
|
390
450
|
continue;
|
|
391
451
|
}
|
|
392
452
|
flow.push(l);
|
|
453
|
+
// 太字だけの行(筋の名前)の直後に空行を足す。marked は空行の無い太字と文を 1 段落につなぐので、筋の名前を独立した段落にする
|
|
454
|
+
if (BOLD_ONLY_RE.test(l.trim()))
|
|
455
|
+
flow.push("");
|
|
393
456
|
}
|
|
394
457
|
const tidy = (ls) => ls.join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
395
458
|
return { phase: phase, flow: tidy(flow), human: tidy(human) };
|
package/dist/supersedes.js
CHANGED
|
@@ -18,28 +18,19 @@ exports.retiredIdsOf = retiredIdsOf;
|
|
|
18
18
|
* (tools は `test/fixtures/`)を置き、`test/supersedes-index-parity.test.mjs` が全ケースを流す。
|
|
19
19
|
* 規則を変えるときは engine の正本・この写し・題材を同じ日に直す(**tools 側で規則を足さない**)。
|
|
20
20
|
*
|
|
21
|
-
* ## 規則(2026-10-04
|
|
21
|
+
* ## 規則(2026-10-04 のオーナー決定を、2026-10-06 に engine #1311 で替えた)
|
|
22
22
|
*
|
|
23
23
|
* 1. 置き換えの正本は**新しい側の `relations[supersedes]` だけ**。古い側の `superseded_by`
|
|
24
24
|
* (engine の夜間の精製が書く写し)は逆引きの材料にしない
|
|
25
|
-
* 2. **確定した置き換え(`confirmed
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* 置き換えが候補に戻る)。🔴 **人の手の材料は 4-1 の間 `status` だけ**(口 `confirmed.ts` の
|
|
30
|
-
* `humanOwned`・決定の区画では `active`)。engine の人の手の表を使うのは、表がビューアに届く段 5
|
|
25
|
+
* 2. **確定した置き換え(`confirmed`)=新しい側が古い側を置き換えると書いている全部**(人の手の有無を問わない・
|
|
26
|
+
* engine #1311)。10/4 の「人が手を入れた古い決定は候補に留める」の例外は外した(候補を確かめる人がいない)。
|
|
27
|
+
* 人の直しを守るのは置き換えを決める書き手の判定で、ここ(逆引き)は人の手を見ない。
|
|
28
|
+
* 出力の形(`by[].confirmed`)は題材の互換のため残す(常に真)
|
|
31
29
|
* 3. **置き換えられた(もう有効でない・retired)=確定した置き換えが 1 本以上**
|
|
32
30
|
* 4. 置き換え先は**全部・新しい順**(決定日の降順 → ID の降順・同じ ID は 1 回)
|
|
33
31
|
* 5. 自分自身を指す・実在しない ID は数えない
|
|
34
32
|
*/
|
|
35
33
|
const node_path_1 = __importDefault(require("node:path"));
|
|
36
|
-
const confirmed_1 = require("./confirmed");
|
|
37
|
-
/**
|
|
38
|
-
* 口に渡す path が無い・決定の区画の外のときの置き場(逆引きに来るのは決定だけなので、決定の区画の答えで聞く)。
|
|
39
|
-
* 🔴 区画の分からない path のまま聞くと、決定の語彙に無い `superseded`・`inactive` まで確認済みに数える
|
|
40
|
-
* (engine の `DECISIONS_ANCHOR` と同じ)
|
|
41
|
-
*/
|
|
42
|
-
const DECISIONS_ANCHOR = "Cortex/Decisions/records/";
|
|
43
34
|
/**
|
|
44
35
|
* 決定を指す文字列を突き合わせの鍵にする(`decision:` の頭・パス・`.md` を落とす)。
|
|
45
36
|
* engine は `path.basename`(Linux で走る=posix)なので、ここも posix で固定する
|
|
@@ -65,8 +56,6 @@ function normalize(d) {
|
|
|
65
56
|
key: decisionKeyOf(id),
|
|
66
57
|
date: decisionDateOf(r.date, id),
|
|
67
58
|
status: typeof r.status === "string" ? r.status.trim() : "",
|
|
68
|
-
// 口(`confirmed.ts`)へ渡すだけ(逆引きの出力には載せない・題材の契約は変わらない)
|
|
69
|
-
path: typeof r.path === "string" && r.path.includes("Cortex/Decisions/") ? r.path : DECISIONS_ANCHOR,
|
|
70
59
|
supersedes: (Array.isArray(r.supersedes) ? r.supersedes : []).map(decisionKeyOf).filter(Boolean),
|
|
71
60
|
};
|
|
72
61
|
}
|
|
@@ -102,11 +91,9 @@ function supersedesIndex(decisions) {
|
|
|
102
91
|
const entry = out.get(old.key) || { id: old.id, by: [], retired: false };
|
|
103
92
|
if (entry.by.some((x) => decisionKeyOf(x.id) === d.key))
|
|
104
93
|
continue;
|
|
105
|
-
//
|
|
106
|
-
|
|
107
|
-
entry.
|
|
108
|
-
if (confirmed)
|
|
109
|
-
entry.retired = true;
|
|
94
|
+
// 規則 2: 人の手の有無を問わず確定(engine #1311)
|
|
95
|
+
entry.by.push({ id: d.id, date: d.date, status: d.status, confirmed: true });
|
|
96
|
+
entry.retired = true;
|
|
110
97
|
out.set(old.key, entry);
|
|
111
98
|
}
|
|
112
99
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@takagaki/cortex-decisions-viewer",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.121",
|
|
4
4
|
"description": "Cortexの意思決定記録(Decisions/*.md)を静的サイトにビルドして閲覧する。各レコードに「Edit on GitHub」リンクを付与する。",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"private": false,
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"build": "tsc -p tsconfig.json",
|
|
22
22
|
"prepublishOnly": "npm run build",
|
|
23
23
|
"demo": "npm run build && node dist/cli.js build ../../aidd-project-cortex/Cortex/Decisions/records --out .demo-site",
|
|
24
|
+
"pretest": "node ../scripts/ci/assemble-engine.mjs",
|
|
24
25
|
"test": "npm run test:syntax && npm run build && node --test --test-concurrency=4 test/*.test.mjs",
|
|
25
26
|
"test:syntax": "node --test test/embedded-js-syntax.test.mjs"
|
|
26
27
|
},
|