@takagaki/cortex-decisions-viewer 0.4.34 → 0.4.37
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 +15 -2
- package/dist/render.js +314 -22
- package/package.json +1 -1
package/dist/build.js
CHANGED
|
@@ -177,6 +177,15 @@ async function readFleetSources(repoRoot) {
|
|
|
177
177
|
}
|
|
178
178
|
return { externalSources, internalSources, pipelines, tools };
|
|
179
179
|
}
|
|
180
|
+
/** パスがディレクトリとして存在するか */
|
|
181
|
+
async function isDirectory(dirAbs) {
|
|
182
|
+
try {
|
|
183
|
+
return (await node_fs_1.promises.stat(dirAbs)).isDirectory();
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
180
189
|
/** ディレクトリ内の .md を汎用レコードとして読み込む(存在しなければ空配列) */
|
|
181
190
|
async function loadGenericRecords(dirAbs, exclude, repoBaseUrl, branch) {
|
|
182
191
|
let entries;
|
|
@@ -915,8 +924,11 @@ async function build(opts) {
|
|
|
915
924
|
for (const e of rootEntries) {
|
|
916
925
|
if (!e.isDirectory() || e.name === "Decisions")
|
|
917
926
|
continue;
|
|
918
|
-
const
|
|
919
|
-
|
|
927
|
+
const recordsDir = path.join(knowledgeRoot, e.name, "records");
|
|
928
|
+
const records = await loadGenericRecords(recordsDir, exclude, repo.baseUrl, repo.branch);
|
|
929
|
+
// レコード0件でもタブは出す(空状態から「+AIで登録」できる導線になる)。
|
|
930
|
+
// records/ 自体が無いディレクトリ(その区画が未導入の案件)はタブにしない
|
|
931
|
+
if (records.length || (await isDirectory(recordsDir)))
|
|
920
932
|
sections.push({ name: e.name, records });
|
|
921
933
|
}
|
|
922
934
|
sections.sort((a, b) => a.name.localeCompare(b.name, "ja"));
|
|
@@ -1022,6 +1034,7 @@ async function parseDecision(raw, fileName, repoRelDir, repoBaseUrl, branch) {
|
|
|
1022
1034
|
bodyHtml,
|
|
1023
1035
|
bodyRef: "",
|
|
1024
1036
|
searchText: toSearchText(content),
|
|
1037
|
+
rstatus: data.status != null ? String(data.status) : "",
|
|
1025
1038
|
fileName,
|
|
1026
1039
|
editUrl,
|
|
1027
1040
|
fm: sanitizeFm(data),
|
package/dist/render.js
CHANGED
|
@@ -197,12 +197,45 @@ function buildTutorialData(site, codeUrl, repoSlug) {
|
|
|
197
197
|
prompt: "直近2週間の主要な動き・論点・未解決事項を、議事録や決定記録を横断してまとめてください。",
|
|
198
198
|
},
|
|
199
199
|
];
|
|
200
|
-
//
|
|
201
|
-
|
|
200
|
+
// ここから先は「書き込み・外部連携」の体験。案件の構成に無い能力のチャレンジは出さない(表示条件つき)
|
|
201
|
+
const tools = site.tools ?? {};
|
|
202
|
+
const internals = site.internalSources ?? [];
|
|
203
|
+
const externals = site.externalSources ?? [];
|
|
204
|
+
// メンバーの自分のレコードを直す体験(Members セクションにレコードがある案件のみ出す)
|
|
205
|
+
if ((site.sections ?? []).some((s) => (s.name === "Members" || s.name === "メンバー") && s.records.length)) {
|
|
202
206
|
specs.push({
|
|
203
207
|
title: "自分の名前を変更してみよう",
|
|
204
208
|
desc: "メンバー一覧の自分のレコードをAIに直してもらう",
|
|
205
|
-
prompt:
|
|
209
|
+
prompt: TUT_MEMBER_PROMPT,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
// Backlogに記票する体験(課題管理にBacklogを使う案件のみ出す)
|
|
213
|
+
const usesBacklog = tools["課題管理"] === "backlog" ||
|
|
214
|
+
internals.some((s) => s.kind === "課題管理" && s.tool === "backlog" && s.enabled !== false);
|
|
215
|
+
if (usesBacklog) {
|
|
216
|
+
specs.push({
|
|
217
|
+
title: "Backlogに記票してみよう",
|
|
218
|
+
desc: "AIから自分の名義で課題にコメントする",
|
|
219
|
+
prompt: TUT_BACKLOG_PROMPT,
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
// 画面インベントリを読む体験(デザインを同期している案件のみ出す)
|
|
223
|
+
const usesDesign = (tools["デザイン"] != null && tools["デザイン"] !== "none") ||
|
|
224
|
+
internals.some((s) => s.kind === "デザイン" && s.enabled !== false);
|
|
225
|
+
if (usesDesign) {
|
|
226
|
+
specs.push({
|
|
227
|
+
title: "デザインを読んでみよう",
|
|
228
|
+
desc: "画面インベントリから1画面を選んで理解する",
|
|
229
|
+
prompt: TUT_DESIGN_PROMPT,
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
// 完了をSlackに知らせる体験(チャットにSlackを宣言している案件のみ出す。最終チャレンジ)
|
|
233
|
+
const usesSlack = tools["チャット"] === "slack" || externals.some((s) => s.type === "slack");
|
|
234
|
+
if (usesSlack) {
|
|
235
|
+
specs.push({
|
|
236
|
+
title: "Slackに完了を知らせよう",
|
|
237
|
+
desc: "チュートリアルの完了を案件のチャンネルに投稿する",
|
|
238
|
+
prompt: TUT_SLACK_PROMPT,
|
|
206
239
|
});
|
|
207
240
|
}
|
|
208
241
|
return {
|
|
@@ -234,6 +267,92 @@ function tutorialChallenge4Prompt(decisions) {
|
|
|
234
267
|
return (`このプロジェクトの決定「${picked.title}」(${picked.id})について、なぜこの結論に至ったのか、` +
|
|
235
268
|
"根拠になった会議や課題まで遡って経緯を説明してください。関連する未解決の論点があれば併せて教えてください。");
|
|
236
269
|
}
|
|
270
|
+
/**
|
|
271
|
+
* 書き込み・外部連携をともなうチャレンジのプロンプト。
|
|
272
|
+
* リンク先はクラウド実行のセッションでエンジンのスキルがロードされないため、規約・手順を埋め込んで自己完結させる
|
|
273
|
+
* (AI導線ボタンのプロンプトと同じ方針)。スキルが使える環境ではそちらを優先させる旨を冒頭に添える。
|
|
274
|
+
*/
|
|
275
|
+
const TUT_MEMBER_PROMPT = [
|
|
276
|
+
"(update-member スキルが使える環境なら、そのスキルの手順を優先してください。使えない場合は以下に従ってください。)",
|
|
277
|
+
"",
|
|
278
|
+
"Cortex/Members/records/ にある、わたし自身のメンバーレコードの表示名(title)を変更してください。Gold層へ書き込む練習です。名簿にわたしのレコードが無ければ、新規登録として1ファイル作ってかまいません。",
|
|
279
|
+
"",
|
|
280
|
+
"前提:",
|
|
281
|
+
"- 名簿は更新型です。氏名・所属・役割が変わったら frontmatter・本文を直接更新してかまいません(変更履歴は git が持ちます)",
|
|
282
|
+
"- 新しい表記揺れ(文字起こしの誤変換・呼称)は新規ファイルを作らず aliases に追記します",
|
|
283
|
+
"- 本人やPMに確認が取れたら status: draft を active に上げます。確証が無い項目は推測で埋めず空のままにします",
|
|
284
|
+
"- 離任しても削除せず status: inactive にします(過去の議事録・決定の人名正規化の根拠として残すため)",
|
|
285
|
+
"",
|
|
286
|
+
"手順:",
|
|
287
|
+
"1. Cortex/Members/README.md(スキーマ)と、対象になるレコードを読む",
|
|
288
|
+
"2. わたしのレコードが無ければ、ファイル名 records/{氏名(スペース無し)}.md・id member:{氏名(スペース無し)} で新規に作る",
|
|
289
|
+
"3. frontmatter の必須は type: member / id(member:{氏名(スペース無し)}) / title / description / status。任意は yomi・aliases・org・side(cm | client | vendor)・role・email。README 定義以外のフィールドは追加しない",
|
|
290
|
+
"4. 名簿に書くのは氏名・所属・役割・表記揺れだけ。人の評価・人事・単価・工数は書かない",
|
|
291
|
+
"",
|
|
292
|
+
"共通ルール:",
|
|
293
|
+
"- 書き込む前に、作成・更新する内容を提示して承認を得てください",
|
|
294
|
+
"- 同じ人に複数ファイルを作らないよう、既存レコードとの重複を必ず確認してください",
|
|
295
|
+
"- relations の target にファイルパスは書かず、安定ID(member:{氏名} / 決定ID YYYYMMDD-NNN / 課題キー 等)で張ってください",
|
|
296
|
+
"- 売上・単価・工数・人事評価などの内部限定情報は書かないでください(このリポジトリは顧客も閲覧します)",
|
|
297
|
+
].join("\n");
|
|
298
|
+
const TUT_BACKLOG_PROMPT = [
|
|
299
|
+
"(backlog-push スキルが使える環境なら、そのスキルの手順を優先してください。使えない場合は以下に従ってください。)",
|
|
300
|
+
"",
|
|
301
|
+
"Backlogの課題に、わたしの名義でコメントを1件投稿してください。Backlogの画面を開かずにAIから記票する練習です。",
|
|
302
|
+
"",
|
|
303
|
+
"Backlogのつなぎ方(本人名義で投稿するには Backlog MCP の接続が必要です):",
|
|
304
|
+
"- 手元の Claude Code(CLI・デスクトップ)なら /mcp コマンドから backlog を選んで認証する",
|
|
305
|
+
"- クラウドのセッション(claude.ai/code)なら、セッションのコネクタ設定で Backlog を有効にする",
|
|
306
|
+
"",
|
|
307
|
+
"手順:",
|
|
308
|
+
"1. どの経路で書き込むかを次の優先順で判定し、判定結果とBacklog上の名義を最初に伝える",
|
|
309
|
+
" - mcp__backlog__ で始まるツールが使えるなら、それを使う(あなた本人の名義。手元にAPIキーは不要)",
|
|
310
|
+
" - 使えない場合、環境変数 DOMAIN / PROJECT_KEY / BACKLOG_API_KEY が参照できるなら Backlog REST API を直接呼ぶ(キーの持ち主の名義)",
|
|
311
|
+
" - どちらも無ければ 課題管理/backlog-proxy.json(url / projectKey / token)を読み、その中央プロキシ経由で投稿する(共有bot名義になるため、本文の冒頭に自分の名前を記名する)",
|
|
312
|
+
" - 3つとも無ければ、上記のつなぎ方を案内して止まる",
|
|
313
|
+
"2. 課題管理/issues/ からコメントする課題を1件選ぶ(候補を挙げて確認する)。課題キーは各ファイルの「基本情報 > 課題キー」にある",
|
|
314
|
+
"3. 投稿する課題キー・本文・使う経路・Backlog上の名義を提示して、承認を得る",
|
|
315
|
+
"4. 承認後に投稿し、投稿先のURLを報告する",
|
|
316
|
+
"",
|
|
317
|
+
"注意:",
|
|
318
|
+
"- 今回はコメントの追加だけです。課題の削除・クローズ・属性変更はしないでください",
|
|
319
|
+
"- 売上・単価・工数・人事評価などの内部限定情報は書かないでください(顧客も閲覧します)",
|
|
320
|
+
].join("\n");
|
|
321
|
+
const TUT_DESIGN_PROMPT = [
|
|
322
|
+
"デザイン/inventory/ の画面インベントリから画面を1つ選び、それがどんな画面なのかを説明してください。",
|
|
323
|
+
"",
|
|
324
|
+
"手順:",
|
|
325
|
+
"1. デザイン/inventory/ を一覧し、主要そうな画面を1つ選ぶ(1画面1ファイル。frontmatter に安定ID design:{fileKey}:{nodeId} とFigmaへのディープリンクがあります)",
|
|
326
|
+
"2. その画面のファイルを読み、画面の役割・主な要素・前後の画面とのつながりを、読み取れる範囲で説明する",
|
|
327
|
+
"3. 関連する決定(Cortex/Decisions/)・用語(Cortex/Glossary/)・課題(課題管理/issues/)があれば、安定IDをたどって補足する",
|
|
328
|
+
"4. 最後に、Figmaで実物を開くリンクを提示する",
|
|
329
|
+
"",
|
|
330
|
+
"注意:",
|
|
331
|
+
"- 読むだけです。ファイルの作成・更新はしないでください",
|
|
332
|
+
"- インベントリはFigmaからの同期で上書きされる生成物です。手で編集しないでください",
|
|
333
|
+
].join("\n");
|
|
334
|
+
const TUT_SLACK_PROMPT = [
|
|
335
|
+
"Cortex のチュートリアルを完了したことを、この案件の Slack チャンネルに投稿してください。最後のステップです。",
|
|
336
|
+
"",
|
|
337
|
+
"Slackのつなぎ方:",
|
|
338
|
+
"- 手元の Claude Code(CLI・デスクトップ)なら /mcp コマンドから slack を選んで認可する",
|
|
339
|
+
"- クラウドのセッション(claude.ai/code)なら、セッションのコネクタ設定で Slack を有効にする",
|
|
340
|
+
"- どちらもできない場合は、その旨とつなぎ方を案内して止まってください",
|
|
341
|
+
"",
|
|
342
|
+
"手順:",
|
|
343
|
+
"1. チャット/channels.json を読み、登録されているチャンネル(name / url / description)から投稿先を選ぶ。複数あれば description を見て判断し、迷ったら確認する",
|
|
344
|
+
"2. 次の雛形で本文を作る({名前} は Cortex/Members/records/ にあるわたしのレコードの表記に合わせる)",
|
|
345
|
+
"",
|
|
346
|
+
"🎉 {名前} さんが Cortex のチュートリアルを完了しました",
|
|
347
|
+
"できるようになったこと: Gold層から案件を把握する・自分の情報を更新する・Backlogに記票する・資料を投入する",
|
|
348
|
+
"使い方の質問はこのチャンネルへどうぞ",
|
|
349
|
+
"",
|
|
350
|
+
"3. 投稿先のチャンネルと本文をプレビューして、承認を得る",
|
|
351
|
+
"4. 承認後に投稿し、投稿したメッセージのリンクを報告する",
|
|
352
|
+
"",
|
|
353
|
+
"注意:",
|
|
354
|
+
"- 売上・単価・工数・人事評価などの内部限定情報は書かないでください",
|
|
355
|
+
].join("\n");
|
|
237
356
|
const CSS = `
|
|
238
357
|
:root {
|
|
239
358
|
--bg: #f6f7f9;
|
|
@@ -818,6 +937,24 @@ const CLIENT_JS = `
|
|
|
818
937
|
function catBadge(cat) {
|
|
819
938
|
return el("span", { class: "badge cat-" + cat, text: cat || "未分類" });
|
|
820
939
|
}
|
|
940
|
+
// 未確認(status: draft = AI生成・人間未確認)の印。Gold層の4型(決定事項・用語集・メンバー・ルール)共通で、active・status無しには何も出さない
|
|
941
|
+
function draftBadge(rec) {
|
|
942
|
+
return rec && rec.rstatus === "draft" ? el("span", { class: "badge draft", text: "AI生成・未確認" }) : null;
|
|
943
|
+
}
|
|
944
|
+
// 詳細ページの警告。バッジより強く「そのまま信じないでほしい」と伝える
|
|
945
|
+
function draftNote(rec) {
|
|
946
|
+
if (!rec || rec.rstatus !== "draft") return null;
|
|
947
|
+
return el("div", { class: "draft-note" }, [
|
|
948
|
+
el("span", { text: "⚠️" }),
|
|
949
|
+
el("span", { text: "この内容はAIが自動生成したものです(人間未確認)。必ず内容を確認し、適切な内容に直したうえで frontmatter の status を active に変更してください。" }),
|
|
950
|
+
]);
|
|
951
|
+
}
|
|
952
|
+
// 未確認の絞り込み(フィルタのセレクトの選択肢は draft の1つだけ)
|
|
953
|
+
function statusLabel(v) { return v === "draft" ? "未確認のみ" : v; }
|
|
954
|
+
// 未確認が1件も無い一覧では絞り込みを出さない
|
|
955
|
+
function hasUnverified(items) {
|
|
956
|
+
return (items || []).some(function (r) { return r.rstatus === "draft"; });
|
|
957
|
+
}
|
|
821
958
|
|
|
822
959
|
// ---------- AI導線(Gold層への人間の書き込み口。claude.ai/code へのディープリンク) ----------
|
|
823
960
|
// ヘッダー「AIに聞く」と同じ方式。repoBaseUrl から slug を導出し、プロンプトを入力済みで Claude Code Web を開く。
|
|
@@ -835,13 +972,155 @@ const CLIENT_JS = `
|
|
|
835
972
|
var m = rec.editUrl.match(/\\/edit\\/[^/]+\\/(.+)$/);
|
|
836
973
|
return m ? decodeURIComponent(m[1]) : "";
|
|
837
974
|
}
|
|
838
|
-
// Gold型(ディレクトリ名)→
|
|
839
|
-
//
|
|
975
|
+
// Gold型(ディレクトリ名)→ 書き込みプロンプトの対応。決定事項/用語集/メンバー/ルールの4型のみ。
|
|
976
|
+
// レポート等スキルの無いディレクトリは対象外(ボタンを出さない)。
|
|
977
|
+
// リンク先はクラウド実行のセッションでエンジンのスキルがロードされないため、プロンプトには規約を埋め込んで自己完結させる。
|
|
978
|
+
// スキルが使える環境(手元のCLI・デスクトップ等)ではそちらを優先させる旨を冒頭に添える。
|
|
979
|
+
function aiHead(skill) {
|
|
980
|
+
return "(" + skill + " スキルが使える環境なら、そのスキルの手順を優先してください。使えない場合は以下に従ってください。)\\n\\n";
|
|
981
|
+
}
|
|
982
|
+
// 4型すべてに付ける共通の規律(承認・重複・安定ID・公開範囲)
|
|
983
|
+
var AI_COMMON = [
|
|
984
|
+
"",
|
|
985
|
+
"",
|
|
986
|
+
"共通ルール:",
|
|
987
|
+
"- 書き込む前に、作成・更新する内容を提示して承認を得てください",
|
|
988
|
+
"- 同じ対象に複数ファイルを作らないよう、既存レコードとの重複を必ず確認してください",
|
|
989
|
+
"- relations の target にファイルパスは書かず、安定ID(決定ID YYYYMMDD-NNN / term:{代表表記} / rule:{slug} / member:{氏名} / 課題キー / minute:{定例名}:{YYYYMMDD})で張ってください",
|
|
990
|
+
"- 売上・単価・工数・人事評価などの内部限定情報は書かないでください(このリポジトリは顧客も閲覧します)",
|
|
991
|
+
].join("\\n");
|
|
840
992
|
var GOLD_SKILL = {
|
|
841
|
-
"Decisions": {
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
993
|
+
"Decisions": {
|
|
994
|
+
skill: "update-decision",
|
|
995
|
+
noun: "決定",
|
|
996
|
+
add: [
|
|
997
|
+
"これから伝える決定事項を Cortex/Decisions/records/ に1決定1ファイルで記録してください。",
|
|
998
|
+
"",
|
|
999
|
+
"手順:",
|
|
1000
|
+
"1. Cortex/Decisions/README.md(フィールド定義・カテゴリー一覧)と Cortex/Decisions/template.md を読む",
|
|
1001
|
+
"2. 既存の records/ を一覧し、同じ決定が記録済みでないかと、決定日の連番の続きを確認する",
|
|
1002
|
+
"3. ファイル名は YYYYMMDD-NNN-決定内容の要約.md(YYYYMMDD=決定日、NNN=同日内の連番)",
|
|
1003
|
+
"4. frontmatter の必須は type: decision / id(YYYYMMDD-NNN) / title / date / sprint(スプリント運用が無ければフェーズ名) / category(ビジネス・技術選定・設計方針・運用ルール・インフラ・デザインから選ぶ) / deciders(Cortex/Members/records/ の正式表記に揃える。名簿に無い人名は「(要確認)」を付ける) / description(1文要約) / references(出典への人間向けリンク)。README 定義以外のフィールドは追加しない",
|
|
1004
|
+
"5. 根拠がある場合は relations に rel: based_on で安定IDを張る(議事録は minute:{定例名}:{YYYYMMDD}、課題は課題キー)",
|
|
1005
|
+
"6. 本文は「# タイトル」「## 背景」(なぜ必要になったか)「## 理由」(なぜその選択をしたか・他案との比較)",
|
|
1006
|
+
"7. 過去の決定を変える内容なら、その記録は書き換えず新しい記録を作り、relations に rel: supersedes で元の決定IDを張る",
|
|
1007
|
+
"8. 確定した決定だけを記録する(検討中・未確定の議論は記録しない)",
|
|
1008
|
+
].join("\\n"),
|
|
1009
|
+
edit: function (target) {
|
|
1010
|
+
return [
|
|
1011
|
+
target + "について、これから伝える変更を反映してください。",
|
|
1012
|
+
"",
|
|
1013
|
+
"重要: 決定の内容そのものが変わる場合、この記録は書き換えません。Cortex/Decisions/records/ に新しいファイル(YYYYMMDD-NNN-決定内容の要約.md。YYYYMMDD=新しい決定日、NNN=同日内の連番)を作り、frontmatter の relations に rel: supersedes / target: 元の決定ID を張って、背景・理由に変更の経緯を書いてください。誤字・リンク切れなど決定の中身が変わらない訂正だけ、その場で直してかまいません。",
|
|
1014
|
+
"",
|
|
1015
|
+
"手順:",
|
|
1016
|
+
"1. Cortex/Decisions/README.md(フィールド定義・カテゴリー一覧)と対象ファイルを読む",
|
|
1017
|
+
"2. 新しい記録を作る場合は、既存の records/ で連番の続きと重複を確認する",
|
|
1018
|
+
"3. frontmatter の必須は type: decision / id / title / date / sprint / category / deciders(Cortex/Members/records/ の正式表記に揃える) / description / references。README 定義以外のフィールドは追加しない",
|
|
1019
|
+
"4. 本文は「# タイトル」「## 背景」「## 理由」の構成を保つ",
|
|
1020
|
+
"5. あなた(人間)が内容を確認して編集したので、status が draft(AI生成・人間未確認)なら active に上げる。内容を確認せず表記・形式だけ直した場合は draft のままにする",
|
|
1021
|
+
].join("\\n");
|
|
1022
|
+
},
|
|
1023
|
+
},
|
|
1024
|
+
"Glossary": {
|
|
1025
|
+
skill: "update-glossary",
|
|
1026
|
+
noun: "用語",
|
|
1027
|
+
add: [
|
|
1028
|
+
"これから伝える用語を Cortex/Glossary/records/ に1用語1ファイルで登録してください。",
|
|
1029
|
+
"",
|
|
1030
|
+
"手順:",
|
|
1031
|
+
"1. Cortex/Glossary/README.md(スキーマ・除外用語リスト)と Cortex/Glossary/template.md を読む",
|
|
1032
|
+
"2. 既存 records/ の title と synonyms を確認する。既存用語の表記揺れなら新規ファイルを作らず、その用語の synonyms に追記する。除外用語リストに載っている語は登録しない",
|
|
1033
|
+
"3. ファイル名は records/{代表表記}.md、id は term:{代表表記}",
|
|
1034
|
+
"4. frontmatter の必須は type: term / id / title / description(1文要約) / scope(project=案件固有 | organization=全社共通) / status / date(登録日)。任意は synonyms(表記揺れ)・source(この案件での出典)・references。README 定義以外のフィールドは追加しない",
|
|
1035
|
+
"5. 案件固有語は社内の情報源(議事録・課題・資料)の記述だけを根拠に定義する(Web検索せず references も付けない)。Figma・OIDC のような一般公開のサービス・技術・規格は、Web で公式ドキュメント等の一次情報を確認してから定義し、そのURLを references に入れる。推測で書かない",
|
|
1036
|
+
"6. 定義を人間と確認できた場合のみ status: active。確証が持てなければ status: draft(AI生成・人間未確認の印)にする",
|
|
1037
|
+
"7. 本文にはこの案件における意味を1〜数段落で具体的に書く(一般的な意味に加え、この案件で特殊な使われ方があれば補足する)",
|
|
1038
|
+
].join("\\n"),
|
|
1039
|
+
edit: function (target) {
|
|
1040
|
+
return [
|
|
1041
|
+
target + "について、これから伝える修正を反映してください。",
|
|
1042
|
+
"",
|
|
1043
|
+
"前提:",
|
|
1044
|
+
"- 用語集は更新型です。定義が変わったら本文・frontmatter を直接更新してかまいません(変更履歴は git が持ちます)",
|
|
1045
|
+
"- 新しい表記揺れは新規ファイルを作らず synonyms に追記します",
|
|
1046
|
+
"- 定義を人間と確認できたら status: draft を active に上げます",
|
|
1047
|
+
"- 使われなくなった用語は削除せず status: superseded にします",
|
|
1048
|
+
"",
|
|
1049
|
+
"手順:",
|
|
1050
|
+
"1. Cortex/Glossary/README.md(スキーマ)と対象ファイルを読む",
|
|
1051
|
+
"2. frontmatter の必須は type: term / id(term:{代表表記}) / title / description / scope / status / date。README 定義以外のフィールドは追加しない",
|
|
1052
|
+
"3. 一般公開のサービス・技術・規格は Web で公式ドキュメント等の一次情報を確認してから定義を直し、references にそのURLを入れる。案件固有語は社内の情報源だけを根拠にし、推測で書かない",
|
|
1053
|
+
"4. あなた(人間)が内容を確認して編集したので、status が draft(AI生成・人間未確認)なら active に上げる。内容を確認せず表記・形式だけ直した場合は draft のままにする",
|
|
1054
|
+
].join("\\n");
|
|
1055
|
+
},
|
|
1056
|
+
},
|
|
1057
|
+
"Members": {
|
|
1058
|
+
skill: "update-member",
|
|
1059
|
+
noun: "メンバー",
|
|
1060
|
+
add: [
|
|
1061
|
+
"これから伝えるメンバーを Cortex/Members/records/ に1人1ファイルで登録してください。",
|
|
1062
|
+
"",
|
|
1063
|
+
"手順:",
|
|
1064
|
+
"1. Cortex/Members/README.md(スキーマ)と Cortex/Members/template.md を読む",
|
|
1065
|
+
"2. 既存 records/ の title・aliases・side を確認する。既存メンバーの表記揺れなら新規ファイルを作らず、その人の aliases に追記する(同一人物に複数ファイルを作らない)",
|
|
1066
|
+
"3. ファイル名は records/{氏名(スペース無し)}.md、id は member:{氏名(スペース無し)}",
|
|
1067
|
+
"4. frontmatter の必須は type: member / id / title(氏名の正式表記。スペースを含んでよい) / description(何者かの1文) / status(active=在籍 | inactive=離任 | draft=AI生成・人間未確認)。任意は yomi・aliases・org・side(cm=開発側 | client=顧客 | vendor=ベンダー)・role・email。README 定義以外のフィールドは追加しない",
|
|
1068
|
+
"5. 氏名・所属・side を推測で確定させない。確証が無い項目は空にし、確認が取れていないレコードは status: draft のままにする",
|
|
1069
|
+
"6. 本文にも何者かを1〜2文書く(Viewer の個別ページに表示されるため空にしない)",
|
|
1070
|
+
"7. 名簿に書くのは氏名・所属・役割・表記揺れだけ。人の評価・人事・単価・工数は書かない",
|
|
1071
|
+
].join("\\n"),
|
|
1072
|
+
edit: function (target) {
|
|
1073
|
+
return [
|
|
1074
|
+
target + "について、これから伝える修正を反映してください。",
|
|
1075
|
+
"",
|
|
1076
|
+
"前提:",
|
|
1077
|
+
"- 名簿は更新型です。氏名・所属・役割が変わったら frontmatter・本文を直接更新してかまいません(変更履歴は git が持ちます)",
|
|
1078
|
+
"- 新しい表記揺れ(文字起こしの誤変換・呼称)は新規ファイルを作らず aliases に追記します",
|
|
1079
|
+
"- 本人やPMに確認が取れたら status: draft を active に上げます。確証が無い項目は推測で埋めず空のままにします",
|
|
1080
|
+
"- 離任しても削除せず status: inactive にします(過去の議事録・決定の人名正規化の根拠として残すため)",
|
|
1081
|
+
"",
|
|
1082
|
+
"手順:",
|
|
1083
|
+
"1. Cortex/Members/README.md(スキーマ)と対象ファイルを読む",
|
|
1084
|
+
"2. frontmatter の必須は type: member / id(member:{氏名(スペース無し)}) / title / description / status。任意は yomi・aliases・org・side(cm | client | vendor)・role・email。README 定義以外のフィールドは追加しない",
|
|
1085
|
+
"3. 名簿に書くのは氏名・所属・役割・表記揺れだけ。人の評価・人事・単価・工数は書かない",
|
|
1086
|
+
"4. あなた(人間)が内容を確認して編集したので、status が draft(AI生成・人間未確認)なら active に上げる。内容を確認せず表記・形式だけ直した場合は draft のままにする",
|
|
1087
|
+
].join("\\n");
|
|
1088
|
+
},
|
|
1089
|
+
},
|
|
1090
|
+
"Rules": {
|
|
1091
|
+
skill: "update-rule",
|
|
1092
|
+
noun: "ルール",
|
|
1093
|
+
add: [
|
|
1094
|
+
"これから伝えるルール(案件で継続的に守る制約)を Cortex/Rules/records/ に1ルール1ファイルで登録してください。",
|
|
1095
|
+
"",
|
|
1096
|
+
"手順:",
|
|
1097
|
+
"1. Cortex/Rules/README.md(スキーマ・除外ルールリスト)と Cortex/Rules/template.md を読む",
|
|
1098
|
+
"2. Rule と Decision を判別する。Rule は「本番リリースは金曜禁止」のように今も効き続ける約束事だけ。一回きりの決定・単発のTODO・期限付きの依頼は Rule にしない(それは Cortex/Decisions/ の担当)",
|
|
1099
|
+
"3. 既存 records/ の title を確認して重複を避ける。README の除外ルールに載っている制約は起票しない(除外リストは読むだけで編集しない)",
|
|
1100
|
+
"4. ファイル名は records/{slug}.md、id は rule:{slug}(例: rule:本番リリース-金曜禁止)",
|
|
1101
|
+
"5. frontmatter の必須は type: rule / id / title(制約の1行表現) / description(1文要約) / status(draft=AI生成・人間未確認 | active=確認済み)。README 定義以外のフィールドは追加しない",
|
|
1102
|
+
"6. status は原則 draft。対話でユーザー本人が制約の内容に合意している場合だけ active にしてよい",
|
|
1103
|
+
"7. 元になった決定があれば relations に rel: derived_from / target: 決定ID(YYYYMMDD-NNN)を、議事録・課題が根拠なら rel: based_on で安定IDを張る",
|
|
1104
|
+
"8. 本文は「## 制約」「## 適用範囲」「## 違反するとどうなるか」「## 出典」で構成する",
|
|
1105
|
+
].join("\\n"),
|
|
1106
|
+
edit: function (target) {
|
|
1107
|
+
return [
|
|
1108
|
+
target + "について、これから伝える修正を反映してください。",
|
|
1109
|
+
"",
|
|
1110
|
+
"前提:",
|
|
1111
|
+
"- ルールは更新型です。制約の表現・適用範囲が変わったら frontmatter・本文を直接更新してかまいません(変更履歴は git が持ちます)",
|
|
1112
|
+
"- 内容を人間と確認できたら status: draft を active に上げます(本人の合意が無いものを勝手に active にしない)",
|
|
1113
|
+
"- 効かなくなったルールは削除せず、本文に失効を追記して元の決定から履歴をたどれるようにします",
|
|
1114
|
+
"- 継続的な制約でなくなる内容(一回きりの決定)は Rule ではなく Cortex/Decisions/ の担当です",
|
|
1115
|
+
"",
|
|
1116
|
+
"手順:",
|
|
1117
|
+
"1. Cortex/Rules/README.md(スキーマ・除外ルール)と対象ファイルを読む",
|
|
1118
|
+
"2. frontmatter の必須は type: rule / id(rule:{slug}) / title / description / status。README 定義以外のフィールドは追加しない",
|
|
1119
|
+
"3. 本文は「## 制約」「## 適用範囲」「## 違反するとどうなるか」「## 出典」の構成を保つ",
|
|
1120
|
+
"4. あなた(人間)が内容を確認して編集したので、status が draft(AI生成・人間未確認)なら active に上げる。内容を確認せず表記・形式だけ直した場合は draft のままにする",
|
|
1121
|
+
].join("\\n");
|
|
1122
|
+
},
|
|
1123
|
+
},
|
|
845
1124
|
};
|
|
846
1125
|
// ディレクトリ名が日本語の案件(用語集 等)でも同じ4型に解決できるよう、表示名(DIR_LABELS)→英語キーの別名も引く
|
|
847
1126
|
var GOLD_ALIAS = { "決定事項": "Decisions", "用語集": "Glossary", "メンバー": "Members", "ルール": "Rules" };
|
|
@@ -852,7 +1131,7 @@ const CLIENT_JS = `
|
|
|
852
1131
|
function aiAddBar(sectionName) {
|
|
853
1132
|
var g = goldSkill(sectionName);
|
|
854
1133
|
if (!g) return null;
|
|
855
|
-
var prompt = g.skill +
|
|
1134
|
+
var prompt = aiHead(g.skill) + g.add + AI_COMMON;
|
|
856
1135
|
return el("div", { class: "ai-add-bar" }, [
|
|
857
1136
|
el("a", { class: "ai-add-btn", href: codeUrl(prompt), target: "_blank", rel: "noopener", text: "+ AIで登録" }),
|
|
858
1137
|
]);
|
|
@@ -865,8 +1144,8 @@ const CLIENT_JS = `
|
|
|
865
1144
|
var target = pathRef
|
|
866
1145
|
? pathRef + " の" + g.noun + "「" + title + "」"
|
|
867
1146
|
: "この" + g.noun + "「" + title + "」(" + rec.id + ")";
|
|
868
|
-
var prompt = g.skill +
|
|
869
|
-
return el("a", { class: "ai-edit-btn", href: codeUrl(prompt), target: "_blank", rel: "noopener", text: "
|
|
1147
|
+
var prompt = aiHead(g.skill) + g.edit(target) + AI_COMMON;
|
|
1148
|
+
return el("a", { class: "ai-edit-btn", href: codeUrl(prompt), target: "_blank", rel: "noopener", text: "AIで編集" });
|
|
870
1149
|
}
|
|
871
1150
|
// 本文HTMLは bodies/<i>.html に外出しされている。詳細表示時にfetchして埋める(キャッシュ・失敗時フォールバック付き)
|
|
872
1151
|
var bodyCache = {};
|
|
@@ -894,7 +1173,7 @@ const CLIENT_JS = `
|
|
|
894
1173
|
window.scrollTo(0, 0);
|
|
895
1174
|
}
|
|
896
1175
|
|
|
897
|
-
var listState = { tab: DEFAULT_TAB, q: "", category: "", month: "", page: 1 };
|
|
1176
|
+
var listState = { tab: DEFAULT_TAB, q: "", category: "", month: "", status: "", page: 1 };
|
|
898
1177
|
|
|
899
1178
|
// 一覧のページネーション(描画コスト対策。1ページあたりの件数)
|
|
900
1179
|
var PAGE_SIZE = 30;
|
|
@@ -1484,6 +1763,9 @@ const CLIENT_JS = `
|
|
|
1484
1763
|
filters.appendChild(buildSelect("すべての期間", SITE.months, listState.month, function (v) { listState.month = v; listState.page = 1; drawCards(); }, monthLabel));
|
|
1485
1764
|
}
|
|
1486
1765
|
filters.appendChild(buildSelect("すべてのカテゴリー", SITE.categories, listState.category, function (v) { listState.category = v; listState.page = 1; drawCards(); }));
|
|
1766
|
+
if (hasUnverified(SITE.decisions)) {
|
|
1767
|
+
filters.appendChild(buildSelect("すべての状態", ["draft"], listState.status, function (v) { listState.status = v; listState.page = 1; drawCards(); }, statusLabel));
|
|
1768
|
+
}
|
|
1487
1769
|
app.appendChild(filters);
|
|
1488
1770
|
|
|
1489
1771
|
var addBarD = aiAddBar("Decisions");
|
|
@@ -1511,6 +1793,7 @@ const CLIENT_JS = `
|
|
|
1511
1793
|
var items = SITE.decisions.filter(function (d) {
|
|
1512
1794
|
if (listState.month && d.date.slice(0, 7) !== listState.month) return false;
|
|
1513
1795
|
if (listState.category && d.category !== listState.category) return false;
|
|
1796
|
+
if (listState.status && d.rstatus !== listState.status) return false;
|
|
1514
1797
|
if (q) {
|
|
1515
1798
|
var hay = (d.title + " " + d.summary + " " + d.searchText + " " + d.deciders.join(" ")).toLowerCase();
|
|
1516
1799
|
if (hay.indexOf(q) === -1) return false;
|
|
@@ -1534,6 +1817,7 @@ const CLIENT_JS = `
|
|
|
1534
1817
|
|
|
1535
1818
|
pg.slice.forEach(function (d) {
|
|
1536
1819
|
var meta = el("div", { class: "card-meta" }, [
|
|
1820
|
+
draftBadge(d),
|
|
1537
1821
|
el("span", { class: "mono", text: d.date || "----" }),
|
|
1538
1822
|
catBadge(d.category),
|
|
1539
1823
|
el("span", { class: "mono", text: d.id }),
|
|
@@ -1566,6 +1850,9 @@ const CLIENT_JS = `
|
|
|
1566
1850
|
var search = el("input", { type: "search", placeholder: "キーワード検索" });
|
|
1567
1851
|
search.value = listState.q;
|
|
1568
1852
|
filters.appendChild(search);
|
|
1853
|
+
if (hasUnverified(items)) {
|
|
1854
|
+
filters.appendChild(buildSelect("すべての状態", ["draft"], listState.status, function (v) { listState.status = v; listState.page = 1; draw(); }, statusLabel));
|
|
1855
|
+
}
|
|
1569
1856
|
app.appendChild(filters);
|
|
1570
1857
|
|
|
1571
1858
|
var addBarG = aiAddBar(listState.tab.replace(/^dir:/, ""));
|
|
@@ -1577,13 +1864,18 @@ const CLIENT_JS = `
|
|
|
1577
1864
|
function draw() {
|
|
1578
1865
|
var q = listState.q.trim().toLowerCase();
|
|
1579
1866
|
var shown = items.filter(function (r) {
|
|
1867
|
+
if (listState.status && r.rstatus !== listState.status) return false;
|
|
1580
1868
|
if (!q) return true;
|
|
1581
1869
|
return (r.title + " " + r.summary + " " + r.searchText).toLowerCase().indexOf(q) !== -1;
|
|
1582
1870
|
});
|
|
1583
1871
|
listWrap.innerHTML = "";
|
|
1584
1872
|
if (!shown.length) {
|
|
1585
1873
|
countEl.textContent = "0 / " + items.length + " 件";
|
|
1586
|
-
|
|
1874
|
+
// レコード0件のセクション(区画はあるがまだ書かれていない)と、絞り込みの結果0件とを言い分ける
|
|
1875
|
+
listWrap.appendChild(el("div", {
|
|
1876
|
+
class: "empty",
|
|
1877
|
+
text: items.length ? "該当するレコードはありません" : "まだ登録されていません",
|
|
1878
|
+
}));
|
|
1587
1879
|
return;
|
|
1588
1880
|
}
|
|
1589
1881
|
var pg = paginate(shown);
|
|
@@ -1592,7 +1884,7 @@ const CLIENT_JS = `
|
|
|
1592
1884
|
(pg.pages > 1 ? "(" + pg.page + "/" + pg.pages + "ページ)" : "");
|
|
1593
1885
|
pg.slice.forEach(function (r) {
|
|
1594
1886
|
var meta = el("div", { class: "card-meta" }, [
|
|
1595
|
-
r
|
|
1887
|
+
draftBadge(r),
|
|
1596
1888
|
r.date ? el("span", { class: "mono", text: r.date }) : null,
|
|
1597
1889
|
el("span", { class: "mono", text: r.id }),
|
|
1598
1890
|
]);
|
|
@@ -1638,7 +1930,7 @@ const CLIENT_JS = `
|
|
|
1638
1930
|
app.appendChild(head);
|
|
1639
1931
|
|
|
1640
1932
|
app.appendChild(el("div", { class: "detail-meta" }, [
|
|
1641
|
-
r
|
|
1933
|
+
draftBadge(r),
|
|
1642
1934
|
r.date ? el("span", { class: "mono", text: r.date }) : null,
|
|
1643
1935
|
el("span", { class: "mono", text: r.id }),
|
|
1644
1936
|
r.summary ? el("span", { text: r.summary }) : null,
|
|
@@ -1647,12 +1939,8 @@ const CLIENT_JS = `
|
|
|
1647
1939
|
var en2 = fmErrorNote(r);
|
|
1648
1940
|
if (en2) app.appendChild(en2);
|
|
1649
1941
|
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
el("span", { text: "⚠️" }),
|
|
1653
|
-
el("span", { text: "この定義はAIが自動生成したものです(人間未確認)。必ず内容を確認し、適切な定義に置き換えたうえで frontmatter の status を active に変更してください。" }),
|
|
1654
|
-
]));
|
|
1655
|
-
}
|
|
1942
|
+
var dn2 = draftNote(r);
|
|
1943
|
+
if (dn2) app.appendChild(dn2);
|
|
1656
1944
|
|
|
1657
1945
|
var genBody = el("div", { class: "body" });
|
|
1658
1946
|
app.appendChild(genBody);
|
|
@@ -2295,6 +2583,7 @@ const CLIENT_JS = `
|
|
|
2295
2583
|
app.appendChild(head);
|
|
2296
2584
|
|
|
2297
2585
|
var meta = el("div", { class: "detail-meta" }, [
|
|
2586
|
+
draftBadge(d),
|
|
2298
2587
|
el("span", { class: "mono", text: d.date || "----" }),
|
|
2299
2588
|
catBadge(d.category),
|
|
2300
2589
|
el("span", { class: "mono", text: d.id }),
|
|
@@ -2304,6 +2593,9 @@ const CLIENT_JS = `
|
|
|
2304
2593
|
var en = fmErrorNote(d);
|
|
2305
2594
|
if (en) app.appendChild(en);
|
|
2306
2595
|
|
|
2596
|
+
var dn = draftNote(d);
|
|
2597
|
+
if (dn) app.appendChild(dn);
|
|
2598
|
+
|
|
2307
2599
|
if (d.summary) {
|
|
2308
2600
|
app.appendChild(el("div", { class: "section-label", text: "要約" }));
|
|
2309
2601
|
app.appendChild(el("div", { class: "summary-box", text: d.summary }));
|
package/package.json
CHANGED