@takagaki/cortex-decisions-viewer 0.4.43 → 0.4.45
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 +17 -2
- package/dist/render.js +73 -10
- package/dist/repo.js +46 -0
- package/package.json +1 -1
package/dist/build.js
CHANGED
|
@@ -42,6 +42,9 @@ const path = __importStar(require("node:path"));
|
|
|
42
42
|
const gray_matter_1 = __importDefault(require("gray-matter"));
|
|
43
43
|
const marked_1 = require("marked");
|
|
44
44
|
const repo_1 = require("./repo");
|
|
45
|
+
// 直近のGold昇格で追加されたレコードのパス集合。レコード生成の各所から参照するのでモジュールスコープに置く
|
|
46
|
+
// (引数で引き回すと呼び出し側の改修が広範囲になる)。build() の冒頭で1回だけ解決する。
|
|
47
|
+
let LATEST_PATHS = new Set();
|
|
45
48
|
const render_1 = require("./render");
|
|
46
49
|
const DEFAULT_EXCLUDE = ["README.md", "template.md"];
|
|
47
50
|
/** frontmatterのrelationsを安全に正規化する */
|
|
@@ -132,6 +135,9 @@ async function readFleetSources(repoRoot) {
|
|
|
132
135
|
ref: s.ref != null ? String(s.ref) : undefined,
|
|
133
136
|
url: s.url != null ? String(s.url) : undefined,
|
|
134
137
|
gold: typeof s.gold === "boolean" ? s.gold : undefined,
|
|
138
|
+
// goldState は gold の理由。`gold: false` だけだと「意図的に外した」「宣言し忘れ」
|
|
139
|
+
// 「exclude による除外」が同じ値になり、**正常な除外にまで警告を出して麻痺する**。
|
|
140
|
+
goldState: s.goldState != null ? String(s.goldState) : undefined,
|
|
135
141
|
notify: typeof s.notify === "boolean" ? s.notify : undefined,
|
|
136
142
|
gate: s.gate != null ? String(s.gate) : undefined,
|
|
137
143
|
}))
|
|
@@ -175,7 +181,8 @@ async function readFleetSources(repoRoot) {
|
|
|
175
181
|
if (!Object.keys(tools).length)
|
|
176
182
|
tools = undefined;
|
|
177
183
|
}
|
|
178
|
-
|
|
184
|
+
const fleetGeneratedAt = typeof obj.generatedAt === "string" ? obj.generatedAt : undefined;
|
|
185
|
+
return { externalSources, internalSources, pipelines, tools, fleetGeneratedAt };
|
|
179
186
|
}
|
|
180
187
|
/** パスがディレクトリとして存在するか */
|
|
181
188
|
async function isDirectory(dirAbs) {
|
|
@@ -245,6 +252,7 @@ async function parseGeneric(raw, fileName, repoRelDir, repoBaseUrl, branch) {
|
|
|
245
252
|
editUrl,
|
|
246
253
|
rtype: data.type != null ? String(data.type) : "",
|
|
247
254
|
rstatus: data.status != null ? String(data.status) : "",
|
|
255
|
+
isLatest: LATEST_PATHS.has(filePath),
|
|
248
256
|
relations: toRelations(data.relations),
|
|
249
257
|
source: data.source != null ? String(data.source) : "",
|
|
250
258
|
bodyLinks: extractBodyLinks(content),
|
|
@@ -663,7 +671,7 @@ function buildGraph(decisions, home, sections, content, targetUrls, repoBaseUrl,
|
|
|
663
671
|
const deduped = edges.filter((e) => {
|
|
664
672
|
if (e.from === e.to)
|
|
665
673
|
return false;
|
|
666
|
-
const key = `${e.from}
|
|
674
|
+
const key = `${e.from}\u0000${e.to}\u0000${e.rel}`;
|
|
667
675
|
if (seen.has(key))
|
|
668
676
|
return false;
|
|
669
677
|
seen.add(key);
|
|
@@ -901,6 +909,11 @@ async function build(opts) {
|
|
|
901
909
|
branch: opts.branch,
|
|
902
910
|
cwd: dirAbs,
|
|
903
911
|
});
|
|
912
|
+
// 直近のGold昇格で追加されたレコードを git から解決する(レコードの date は決定日なので生成時期が分からない)。
|
|
913
|
+
// git が返すのはリポジトリルート相対のパス(`Cortex/...`)なので、判定も**リポジトリルート**で行う
|
|
914
|
+
// (Decisionsディレクトリで実行すると `-- Cortex/` に一致せず常に空になる)。
|
|
915
|
+
// 浅いclone・git無しの環境では空集合が返り、「最新」ラベルが出ないだけで他の表示は壊れない。
|
|
916
|
+
LATEST_PATHS = (0, repo_1.latestGeneratedPaths)(process.cwd());
|
|
904
917
|
const decisions = [];
|
|
905
918
|
for (const fileName of mdFiles) {
|
|
906
919
|
const raw = await node_fs_1.promises.readFile(path.join(dirAbs, fileName), "utf8");
|
|
@@ -970,6 +983,7 @@ async function build(opts) {
|
|
|
970
983
|
}
|
|
971
984
|
: undefined,
|
|
972
985
|
externalSources: fleet.externalSources,
|
|
986
|
+
fleetGeneratedAt: fleet.fleetGeneratedAt,
|
|
973
987
|
internalSources: fleet.internalSources,
|
|
974
988
|
pipelines: fleet.pipelines,
|
|
975
989
|
tools: fleet.tools,
|
|
@@ -1035,6 +1049,7 @@ async function parseDecision(raw, fileName, repoRelDir, repoBaseUrl, branch) {
|
|
|
1035
1049
|
bodyRef: "",
|
|
1036
1050
|
searchText: toSearchText(content),
|
|
1037
1051
|
rstatus: data.status != null ? String(data.status) : "",
|
|
1052
|
+
isLatest: LATEST_PATHS.has(filePath),
|
|
1038
1053
|
fileName,
|
|
1039
1054
|
editUrl,
|
|
1040
1055
|
fm: sanitizeFm(data),
|
package/dist/render.js
CHANGED
|
@@ -91,6 +91,23 @@ const CM_TOOL_LABEL = {
|
|
|
91
91
|
* 状態: 健全=色付き+流れるアニメーション / 問題あり=黄+⚠️ / 未使用=グレー。
|
|
92
92
|
* fleet-status由来の情報が何も無ければ null(マップを出さない・後方互換)。
|
|
93
93
|
*/
|
|
94
|
+
/**
|
|
95
|
+
* このソースが「注意が必要」か。接続マップ(ビルド時生成)とデータソース一覧(クライアント側)で
|
|
96
|
+
* **同じ判定を使う**ためにここに置く。片方だけ直すと、同じ画面でマップが⚠️・直下が「接続OK」になる。
|
|
97
|
+
*
|
|
98
|
+
* 判定の考え方:
|
|
99
|
+
* - 明示的に Gold 対象から外したもの(`gold === false`)は、読めなくて当たり前なので警告しない。
|
|
100
|
+
* ただし通知先(`notify`)に使っているなら、届いていないこと自体が問題なので拾う。
|
|
101
|
+
* - `gold` が無い(旧 fleet-status.json)ときは**警告する側に倒す**。「不明」を「対象外」と扱うと、
|
|
102
|
+
* 本当に壊れているものが黙って隠れる(フェイルオープン)。
|
|
103
|
+
*/
|
|
104
|
+
function sourceNeedsAttention(s) {
|
|
105
|
+
if (s.gate === "ok")
|
|
106
|
+
return false;
|
|
107
|
+
if (s.gold === false && s.notify !== true)
|
|
108
|
+
return false;
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
94
111
|
function renderConnectionMap(site) {
|
|
95
112
|
const internals = site.internalSources ?? [];
|
|
96
113
|
const externals = site.externalSources ?? [];
|
|
@@ -119,7 +136,9 @@ function renderConnectionMap(site) {
|
|
|
119
136
|
// 問題あり: ライブ参照はgate≠okが1件以上、同期ソースは対応パイプラインの直近runがfailure
|
|
120
137
|
const warn = !off &&
|
|
121
138
|
(isLive
|
|
122
|
-
|
|
139
|
+
// カード側の集計(sourceNeedsAttention)と同じ判定にする。ここだけ接続状態だけを見ていると、
|
|
140
|
+
// マップが⚠️なのに直下のグループが「接続OK」という矛盾が同じ画面に出る。
|
|
141
|
+
? liveExts.some((s) => sourceNeedsAttention(s))
|
|
123
142
|
: pipes.some((p) => c.pipe != null && c.pipe.test(p.id) && p.applicable !== false && p.lastConclusion === "failure"));
|
|
124
143
|
const state = off ? "cm-off" : warn ? "cm-warn" : "cm-ok";
|
|
125
144
|
const toolName = toolVal && toolVal !== "none"
|
|
@@ -549,6 +568,8 @@ a:hover { text-decoration: underline; }
|
|
|
549
568
|
.badge.cat-ビジネス { background: #fde9ef; color: #be185d; }
|
|
550
569
|
.badge.cat-デザイン { background: #fce7f3; color: #a21caf; }
|
|
551
570
|
.badge.draft { background: #fef3c7; color: #92400e; border: 1px solid #fcd34d; }
|
|
571
|
+
/* 直近のGold昇格で作られたレコードの印。draft(黄・要対応)と区別できるよう、新着を示す緑系にする */
|
|
572
|
+
.badge.latest { background: #dcfce7; color: #166534; border: 1px solid #86efac; }
|
|
552
573
|
.badge.error { background: #fee2e2; color: #b91c1c; border: 1px solid #fca5a5; }
|
|
553
574
|
.draft-note {
|
|
554
575
|
display: flex; gap: 8px; align-items: flex-start;
|
|
@@ -655,7 +676,8 @@ a:hover { text-decoration: underline; }
|
|
|
655
676
|
/* ---- データソースと自動化 ---- */
|
|
656
677
|
.datasources { margin-top: 34px; }
|
|
657
678
|
.datasources-head { font-size: 18px; font-weight: 700; color: var(--text); margin: 0 0 4px; }
|
|
658
|
-
.datasources-intro { color: var(--muted); font-size: 13.5px; line-height: 1.8; margin: 0 0
|
|
679
|
+
.datasources-intro { color: var(--muted); font-size: 13.5px; line-height: 1.8; margin: 0 0 6px; }
|
|
680
|
+
.datasources-asof { color: var(--muted); font-size: 12.5px; line-height: 1.7; margin: 0 0 14px; }
|
|
659
681
|
.ds-sub { font-size: 13px; font-weight: 700; color: var(--muted); margin: 20px 0 8px; }
|
|
660
682
|
.ds-list { display: flex; flex-direction: column; gap: 10px; }
|
|
661
683
|
.ds-card {
|
|
@@ -720,6 +742,7 @@ a.ds-name:hover { text-decoration: underline; }
|
|
|
720
742
|
.ds-badges { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; flex: 0 0 auto; }
|
|
721
743
|
.badge.ds-gold { background: #fef3c7; color: #92400e; border: 1px solid #fcd34d; }
|
|
722
744
|
.badge.ds-excluded { background: #eceef1; color: #6b7280; }
|
|
745
|
+
.badge.ds-undeclared { background: #fff4e5; color: #9a6700; }
|
|
723
746
|
.badge.ds-notify { background: #eff6ff; color: #1e40af; border: 1px solid #bfdbfe; }
|
|
724
747
|
.ds-gate { display: inline-flex; align-items: center; gap: 5px; font-size: 12.5px; font-weight: 600; flex: 0 0 auto; }
|
|
725
748
|
.ds-gate::before { content: ""; width: 8px; height: 8px; border-radius: 999px; background: currentColor; }
|
|
@@ -922,6 +945,11 @@ const CLIENT_JS = `
|
|
|
922
945
|
function draftBadge(rec) {
|
|
923
946
|
return rec && rec.rstatus === "draft" ? el("span", { class: "badge draft", text: "AI生成・未確認" }) : null;
|
|
924
947
|
}
|
|
948
|
+
// 直近のGold昇格で作られたレコードの印。レコードの date は決定日なので、日付順に並べても
|
|
949
|
+
// 「今回の実行で何が増えたか」は分からない。ビルド時に git から判定した結果(isLatest)を出す
|
|
950
|
+
function latestBadge(rec) {
|
|
951
|
+
return rec && rec.isLatest ? el("span", { class: "badge latest", text: "最新の生成" }) : null;
|
|
952
|
+
}
|
|
925
953
|
// 詳細ページの警告。バッジより強く「そのまま信じないでほしい」と伝える
|
|
926
954
|
function draftNote(rec) {
|
|
927
955
|
if (!rec || rec.rstatus !== "draft") return null;
|
|
@@ -1151,17 +1179,24 @@ const CLIENT_JS = `
|
|
|
1151
1179
|
var base = SITE.repoBaseUrl;
|
|
1152
1180
|
var INFO = {
|
|
1153
1181
|
"decisions": {
|
|
1154
|
-
text: "
|
|
1182
|
+
text: "議事録・課題から確定した決定が毎晩自動で追記されます。",
|
|
1155
1183
|
btnLabel: "実行履歴を確認 →",
|
|
1156
|
-
btnUrl: base ? base + "/actions/workflows/update-
|
|
1184
|
+
btnUrl: base ? base + "/actions/workflows/update-gold.yml" : null,
|
|
1157
1185
|
},
|
|
1158
1186
|
"dir:Glossary": {
|
|
1159
|
-
text: "毎晩の自動走査が新しい用語を draft
|
|
1187
|
+
text: "毎晩の自動走査が新しい用語を draft として直接追加します。AIが生成した定義は仮のものです。「AI生成・未確認」の用語は必ず内容を確認し、適切な定義に置き換えて status: active にしてください。",
|
|
1160
1188
|
btnLabel: "実行履歴を確認 →",
|
|
1161
|
-
btnUrl: base ? base + "/actions/workflows/update-
|
|
1189
|
+
btnUrl: base ? base + "/actions/workflows/update-gold.yml" : null,
|
|
1190
|
+
},
|
|
1191
|
+
"dir:Members": {
|
|
1192
|
+
text: "プロジェクト関係者の名簿です。人名の表記ゆれを解決する根拠になります。毎晩の自動走査が新しい参加者を draft として追加します。",
|
|
1193
|
+
btnLabel: "実行履歴を確認 →",
|
|
1194
|
+
btnUrl: base ? base + "/actions/workflows/update-gold.yml" : null,
|
|
1162
1195
|
},
|
|
1163
1196
|
"dir:Rules": {
|
|
1164
1197
|
text: "案件で継続的に守る制約・ルールの一覧です。AIはここを読んで、違反する提案を避けます。",
|
|
1198
|
+
btnLabel: "実行履歴を確認 →",
|
|
1199
|
+
btnUrl: base ? base + "/actions/workflows/update-gold.yml" : null,
|
|
1165
1200
|
},
|
|
1166
1201
|
"graph": {
|
|
1167
1202
|
text: "決定・用語・レポートと、その根拠になった課題・議事録・資料のつながりを自動でつないだ地図です。点をクリックすると、そのページや実際のシステム(Backlog・GitHub等)に移動できます。",
|
|
@@ -1334,6 +1369,14 @@ const CLIENT_JS = `
|
|
|
1334
1369
|
var sec = el("section", { class: "datasources" });
|
|
1335
1370
|
sec.appendChild(el("h2", { class: "datasources-head", text: "データソースと自動化" }));
|
|
1336
1371
|
sec.appendChild(el("p", { class: "datasources-intro", text: "このCortexが接続している情報源と、毎晩自動でGoldに昇格する処理の一覧です。" }));
|
|
1372
|
+
// **いつ時点の情報かを必ず出す。** この節は夜間バッチが作るスナップショットを読んでいるので、
|
|
1373
|
+
// 設定を変えても次の生成までは古い値のまま。時刻が無いと「変えたのに反映されない」ではなく
|
|
1374
|
+
// 「変えたはずなのに違う値が出ている」と読まれてしまう(実際に設定変更直後にそうなった)。
|
|
1375
|
+
var asOf = SITE.fleetGeneratedAt ? dsFmtTime(SITE.fleetGeneratedAt) : "";
|
|
1376
|
+
if (asOf) {
|
|
1377
|
+
sec.appendChild(el("p", { class: "datasources-asof",
|
|
1378
|
+
text: "接続状況は " + asOf + " 時点の自動チェック結果です。設定を変えた場合、ここに反映されるのは次回の実行後になります。" }));
|
|
1379
|
+
}
|
|
1337
1380
|
|
|
1338
1381
|
if (internals.length) {
|
|
1339
1382
|
sec.appendChild(el("div", { class: "ds-sub", text: "リポジトリ内の情報源(自動同期)" }));
|
|
@@ -1400,14 +1443,27 @@ const CLIENT_JS = `
|
|
|
1400
1443
|
var cards = el("div", { class: "ds-list" });
|
|
1401
1444
|
g.items.forEach(function (s) { cards.appendChild(extSourceCard(s, meta)); });
|
|
1402
1445
|
if (g.items.length >= 4) {
|
|
1403
|
-
// 折りたたみ(既定は閉)。summary
|
|
1446
|
+
// 折りたたみ(既定は閉)。summaryに件数と状態の要約を出す。
|
|
1447
|
+
// 判定は接続マップ側の sourceNeedsAttention と揃える(片方だけ直すと同じ画面で矛盾する)。
|
|
1448
|
+
// 意図的に外したソースが接続できないのは当たり前なので数えない。ただし通知先に
|
|
1449
|
+
// 使っているなら届いていないこと自体が問題なので拾う。gold 不明は警告する側に倒す。
|
|
1404
1450
|
var bad = 0;
|
|
1405
|
-
|
|
1451
|
+
var noToken = 0;
|
|
1452
|
+
g.items.forEach(function (s) {
|
|
1453
|
+
if (s.gate === "ok") return;
|
|
1454
|
+
if (s.gold === false && s.notify !== true) return;
|
|
1455
|
+
bad++;
|
|
1456
|
+
if (s.gate === "no_token") noToken++;
|
|
1457
|
+
});
|
|
1406
1458
|
var sum = el("summary", {}, [
|
|
1407
1459
|
el("span", { text: meta.icon + " " + meta.label + "(" + g.items.length + (UNIT[g.type] || "件") + ")" }),
|
|
1408
1460
|
bad === 0
|
|
1409
1461
|
? el("span", { class: "badge ds-sum-ok", text: "接続OK" })
|
|
1410
|
-
|
|
1462
|
+
// 原因が1つに集約できるときは原因を書く。「19件に問題」より「トークンが未設定」の方が、
|
|
1463
|
+
// 読んだ人が次に何をすればよいか分かる(実際、19件の原因がトークン1本ということがあった)。
|
|
1464
|
+
: el("span", { class: "badge ds-sum-warn", text: noToken === bad
|
|
1465
|
+
? "⚠️ " + bad + "件が読めていません(トークン未設定)"
|
|
1466
|
+
: "⚠️ " + bad + "件に問題" }),
|
|
1411
1467
|
]);
|
|
1412
1468
|
var det = el("details", { class: "ds-group" }, [sum, cards]);
|
|
1413
1469
|
if (anchorId) det.id = anchorId;
|
|
@@ -1425,7 +1481,7 @@ const CLIENT_JS = `
|
|
|
1425
1481
|
// 注記は内部・外部の両グループの後ろに1つ(内部の直下だけに置くと外部ソースが
|
|
1426
1482
|
// Gold昇格の対象外に見えるため)。除外の方法もここで案内する。
|
|
1427
1483
|
if (internals.length || sources.length || chatOff || devOff) {
|
|
1428
|
-
sec.appendChild(el("p", { class: "ds-note", text: "
|
|
1484
|
+
sec.appendChild(el("p", { class: "ds-note", text: "「Gold昇格の読み取り対象」のものだけを毎晩の Gold 昇格が読み取ります。「除外」は意図的に外したもの、「未宣言(対象外)」はまだ判断されていないもので、どちらも読み取りません(チャンネル・リポジトリ単位で切り替えられます)。" }));
|
|
1429
1485
|
}
|
|
1430
1486
|
|
|
1431
1487
|
if (pipes.length) {
|
|
@@ -1471,7 +1527,10 @@ const CLIENT_JS = `
|
|
|
1471
1527
|
// 外部ソース1件のカード(グループ化表示・展開表示の両方から使う)
|
|
1472
1528
|
function extSourceCard(s, meta) {
|
|
1473
1529
|
var badges = el("div", { class: "ds-badges" });
|
|
1530
|
+
// 「意図的に外した」と「宣言し忘れ」を分けて出す。同じ「除外」と書くと、
|
|
1531
|
+
// 判断されていないだけのものが「判断済み」に見えてしまう。
|
|
1474
1532
|
if (s.gold === true) badges.appendChild(el("span", { class: "badge ds-gold", text: "Gold昇格の読み取り対象" }));
|
|
1533
|
+
else if (s.goldState === "undeclared") badges.appendChild(el("span", { class: "badge ds-undeclared", text: "未宣言(対象外)" }));
|
|
1475
1534
|
else if (s.gold === false) badges.appendChild(el("span", { class: "badge ds-excluded", text: "除外" }));
|
|
1476
1535
|
if (s.notify === true) badges.appendChild(el("span", { class: "badge ds-notify", text: "通知先" }));
|
|
1477
1536
|
var gate = DS_GATE[s.gate] || DS_GATE["unknown"];
|
|
@@ -1747,6 +1806,7 @@ const CLIENT_JS = `
|
|
|
1747
1806
|
pg.slice.forEach(function (d) {
|
|
1748
1807
|
var meta = el("div", { class: "card-meta" }, [
|
|
1749
1808
|
draftBadge(d),
|
|
1809
|
+
latestBadge(d),
|
|
1750
1810
|
el("span", { class: "mono", text: d.date || "----" }),
|
|
1751
1811
|
catBadge(d.category),
|
|
1752
1812
|
el("span", { class: "mono", text: d.id }),
|
|
@@ -1814,6 +1874,7 @@ const CLIENT_JS = `
|
|
|
1814
1874
|
pg.slice.forEach(function (r) {
|
|
1815
1875
|
var meta = el("div", { class: "card-meta" }, [
|
|
1816
1876
|
draftBadge(r),
|
|
1877
|
+
latestBadge(r),
|
|
1817
1878
|
r.date ? el("span", { class: "mono", text: r.date }) : null,
|
|
1818
1879
|
el("span", { class: "mono", text: r.id }),
|
|
1819
1880
|
]);
|
|
@@ -1864,6 +1925,7 @@ const CLIENT_JS = `
|
|
|
1864
1925
|
|
|
1865
1926
|
app.appendChild(el("div", { class: "detail-meta" }, [
|
|
1866
1927
|
draftBadge(r),
|
|
1928
|
+
latestBadge(r),
|
|
1867
1929
|
r.date ? el("span", { class: "mono", text: r.date }) : null,
|
|
1868
1930
|
el("span", { class: "mono", text: r.id }),
|
|
1869
1931
|
r.summary ? el("span", { text: r.summary }) : null,
|
|
@@ -2521,6 +2583,7 @@ const CLIENT_JS = `
|
|
|
2521
2583
|
|
|
2522
2584
|
var meta = el("div", { class: "detail-meta" }, [
|
|
2523
2585
|
draftBadge(d),
|
|
2586
|
+
latestBadge(d),
|
|
2524
2587
|
el("span", { class: "mono", text: d.date || "----" }),
|
|
2525
2588
|
catBadge(d.category),
|
|
2526
2589
|
el("span", { class: "mono", text: d.id }),
|
package/dist/repo.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.resolveRepo = resolveRepo;
|
|
4
|
+
exports.latestGeneratedPaths = latestGeneratedPaths;
|
|
4
5
|
exports.normalizeToHttps = normalizeToHttps;
|
|
5
6
|
const node_child_process_1 = require("node:child_process");
|
|
6
7
|
/**
|
|
@@ -15,6 +16,51 @@ function resolveRepo(opts) {
|
|
|
15
16
|
const remote = tryGitRemote(opts.cwd);
|
|
16
17
|
return { baseUrl: remote ? normalizeToHttps(remote) : null, branch };
|
|
17
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* 直近のGold昇格で追加されたレコードのパス集合を返す。
|
|
21
|
+
*
|
|
22
|
+
* 「最新の生成物」をビューアで区別するために使う。レコードの frontmatter の `date` は
|
|
23
|
+
* **決定日**(過去の議事録から起こした決定は古い日付になる)なので、生成のタイミングは
|
|
24
|
+
* git 側からしか分からない。
|
|
25
|
+
*
|
|
26
|
+
* 判定: 夜間パイプラインのコミット(メッセージが「自動追記」を含む)のうち最新の1回を探し、
|
|
27
|
+
* そのコミットで **追加(A)** されたファイルを返す。同じ夜に複数フェーズが別コミットで入るため、
|
|
28
|
+
* 最新コミットと同じ「日」のパイプラインコミットまでをまとめて1回の実行として扱う。
|
|
29
|
+
*
|
|
30
|
+
* clone が浅い(--depth 1 等)・git が無い環境では**空集合を返す**(ラベルが出ないだけで壊れない)。
|
|
31
|
+
*/
|
|
32
|
+
function latestGeneratedPaths(cwd) {
|
|
33
|
+
const empty = new Set();
|
|
34
|
+
try {
|
|
35
|
+
// 夜間パイプラインのコミットを新しい順に数件。--grep はコミットメッセージの規約に依存するため、
|
|
36
|
+
// 変わっても「ラベルが出ない」だけで済むように例外は握る。
|
|
37
|
+
const log = (0, node_child_process_1.execSync)('git log -n 20 --grep="自動追記" --pretty=format:"%H %cI" -- Cortex/', { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
38
|
+
if (!log)
|
|
39
|
+
return empty;
|
|
40
|
+
const rows = log.split("\n").map((l) => {
|
|
41
|
+
const [sha, iso] = l.trim().split(/\s+/);
|
|
42
|
+
return { sha, day: (iso || "").slice(0, 10) };
|
|
43
|
+
});
|
|
44
|
+
const latestDay = rows[0].day;
|
|
45
|
+
if (!latestDay)
|
|
46
|
+
return empty;
|
|
47
|
+
const out = new Set();
|
|
48
|
+
for (const r of rows) {
|
|
49
|
+
if (r.day !== latestDay)
|
|
50
|
+
break; // 直近の実行日ぶんだけ
|
|
51
|
+
const files = (0, node_child_process_1.execSync)(`git show --diff-filter=A --name-only --pretty=format: ${r.sha}`, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
52
|
+
for (const f of files.split("\n")) {
|
|
53
|
+
const p = f.trim();
|
|
54
|
+
if (p)
|
|
55
|
+
out.add(p);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return out;
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return empty; // 浅いclone・git無し・メッセージ規約の変更 → ラベルなしで続行
|
|
62
|
+
}
|
|
63
|
+
}
|
|
18
64
|
function tryGitRemote(cwd) {
|
|
19
65
|
try {
|
|
20
66
|
return (0, node_child_process_1.execSync)("git config --get remote.origin.url", {
|
package/package.json
CHANGED