Skip to content
Open
135 changes: 135 additions & 0 deletions .cursor/skills/personal-knowledge/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
---
name: personal-knowledge
description: 対話でユーザーの成り立ち・価値観・考え方・仕事観・目標を引き出し、Obsidian 互換 Markdown として personal-vault/ に蓄積する。
---

# パーソナルナレッジ対話 Skill

## ゴール

> 対話で引き出した「自分のこと」を `personal-vault/` に構造化し、ユーザーがローカル Obsidian に取り込める状態にする。

## 起動

- `@personal-knowledge`
- 「自分のことを話したい」「成り立ちを話したい」等の依頼

## セッション前(必須)

1. `personal-vault/00-MOC-プロフィール.md` を読む
2. 対象テーマのノート(例: `成り立ち.md`)を読む
3. `personal-vault/sessions/` の直近ログを読み、続きか新規か判断する

## 対話ルール

| ルール | 内容 |
|--------|------|
| 質問数 | **1セッション 2〜4 問**に絞る |
| 形式 | 箇条書きで答えやすい質問。長文を求めない |
| 深掘り | 抽象的な回答には具体例を1つだけ追加で聞く |
| 推測 | 回答がない部分は推測で書かない。「未記入」のまま残す |
| 確認 | セッション末尾に「今回追加した内容」サマリを提示 |

## テーマ順序(複数セッション可)

1. **成り立ち** — 幼少期〜現在の転機、きっかけ、影響を受けた人・出来事
2. **価値観** — 譲れないこと、嫌なこと、尊敬する姿
3. **考え方** — 意思決定の癖、迷ったときの判断基準、失敗から学んだこと
4. **仕事の進め方** — ツール、自動化、エージェント委任の境界
5. **目標とビジョン** — 短期・長期、「最強AIエージェントマスター」の具体像

**続きの検出:** 対象ノートの `status: draft` かつ「(対話で追記予定)」が残っていれば、前回の `sessions/` ログの「次回の問い」から再開する。

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Detect unfinished drafts using markers the notes actually contain

When a theme remains unfinished, this continuation condition never matches the supplied template or any of the committed core notes: they use blank list items or 未記入 checklists rather than the exact (対話で追記予定) marker. Consequently even status: draft notes with pending questions are not recognized as continuations, so the next invocation can restart or advance instead of resuming the previous log's 次回の問い; base the check on draft status plus the actual unfinished representation.

Useful? React with 👍 / 👎.


## 書き込みルール

### 更新先

| 種別 | パス |
|------|------|
| 本ノート | `personal-vault/{テーマ}.md` |
| 対話ログ | `personal-vault/sessions/YYYY-MM-DD-{テーマ}.md` |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Add a unique session identifier to log filenames

If the same theme has two sessions on one calendar day—which the Skill explicitly permits—both resolve to this identical path. The later run therefore cannot create a distinct session log and may overwrite or conflate the earlier raw answers, while MOC links and “most recent log” selection cannot distinguish them; include the session number or a timestamp in the filename.

Useful? React with 👍 / 👎.

| 目次 | `personal-vault/00-MOC-プロフィール.md` の状態・ログリンクを更新 |

### ノート構造(本ノート)

```markdown
## 事実
(客観的に言えること、経歴、出来事)

## 解釈
(本人の意味づけ、学び、価値観への接続)

## 具体例
(エピソード、場面、固有名詞は shareable に注意)

## 関連
(wikilink: [[価値観]] 等)
```

### frontmatter(必須キー)

```yaml
title: ...
tags: [...]
created: YYYY-MM-DD
updated: YYYY-MM-DD # 更新のたびに今日の日付
status: draft | stable
shareable: true | false # 記事・コードに流用してよいか
```

### 対話ログ構造

`templates/session-log-template.md` に従う:

- 質問(エージェントが投げた問い)
- 回答(生)— ユーザーの言葉をできるだけそのまま
- 蒸留メモ — 本ノートへ反映した要点
- 次回の問い — 次セッション用

### 業務情報の扱い

- `business-ops/BUSINESS_MAP.md` / `AI_AGENT_MASTERY.md` の内容は **複製しない**
- `仕事の進め方.md` / `目標とビジョン.md` には要約 + ファイルパス参照で十分
- 対話で新しい個人的な文脈が出たら、業務ドキュメントとの差分を本ノートに書く

## セッションフロー

```
1. 既存ノート・ログを読む
2. テーマを宣言(例: 「今日は成り立ちの続きです」)
3. 2〜4 問を投げる
4. ユーザー回答を受け取る
5. sessions/ にログを書く
6. 本ノートを更新(事実 / 解釈 / 具体例)
7. MOC の状態を更新
8. サマリを提示 → 誤りがあれば修正
9. git commit(personal-vault 変更時)
```

## 初回質問バンク(成り立ち)

1. 今の仕事(Web開発)に至った流れを、時系列で3〜5つの節に分けるとしたら何ですか?
2. その中で「人生の分岐点」になった出来事はどれですか?当時どう感じましたか?
3. 今の自分を形づくった人物・環境・体験を、思いつく限り挙げてください。

## 安全・プライバシー

- `shareable: false` の内容を記事・公開コードに流用しない
- 固有名詞(会社名、人名)はユーザーが明示的に OK するまで匿名化(例: 「前職の会社」)
- セッションログは原則 `shareable: false`

## QA

セッション後、変更があれば:

```bash
node business-ops/scripts/knowledge-vault-qa.mjs
```

## 他 Skill との関係

| Skill | 関係 |
|-------|------|
| `@ai-agent-mastery` | 仕事の進め方・目標と整合。委任境界はそちらを正とする |
| `@article-production` | shareable: true の persona 情報のみ記事に転用可 |
| `knowledge-capture`(Notion) | 今回は Obsidian 向け。Notion には書かない |
208 changes: 208 additions & 0 deletions business-ops/scripts/knowledge-vault-qa.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
#!/usr/bin/env node
/**
* personal-vault の機械QA — frontmatter・MOCリンク・空ノートを検証
*
* Usage:
* node business-ops/scripts/knowledge-vault-qa.mjs
* node business-ops/scripts/knowledge-vault-qa.mjs personal-vault/成り立ち.md
*/

import { readFileSync, readdirSync, existsSync, statSync } from "fs";
import { join, dirname, basename } from "path";
import { fileURLToPath } from "url";

const __dirname = dirname(fileURLToPath(import.meta.url));
const ROOT = join(__dirname, "../..");
const VAULT_DIR = join(ROOT, "personal-vault");

const REQUIRED_FRONTMATTER = ["title", "tags", "created", "updated", "status", "shareable"];
const CORE_NOTES = ["成り立ち", "価値観", "考え方", "仕事の進め方", "目標とビジョン"];
const PLACEHOLDER = "(対話で追記予定)";

function parseFrontmatter(content) {
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
if (!match) return { error: "フロントマター(---)がありません", fields: {}, body: "" };

const fields = {};
const lines = match[1].split("\n");
let currentKey = null;

for (const line of lines) {
const keyMatch = line.match(/^(\w+):\s*(.*)$/);
if (keyMatch) {
currentKey = keyMatch[1];
let val = keyMatch[2].trim();
if ((val.startsWith('"') && val.endsWith('"')) || (val.startsWith("'") && val.endsWith("'"))) {
val = val.slice(1, -1);
}
fields[currentKey] = val;
} else if (currentKey === "tags" && line.trim().startsWith("- ")) {
if (!Array.isArray(fields.tags)) fields.tags = [];
fields.tags.push(line.trim().slice(2));
}
}

return { error: null, fields, body: content.slice(match[0].length) };
}

function collectMarkdownFiles(dir, base = dir) {
const results = [];
if (!existsSync(dir)) return results;

for (const entry of readdirSync(dir)) {
const full = join(dir, entry);
const rel = full.replace(base + "/", "");
if (statSync(full).isDirectory()) {
if (entry === "templates") continue;
results.push(...collectMarkdownFiles(full, base));
} else if (entry.endsWith(".md")) {
results.push(full);
}
}
return results;
}

function qaFile(filepath) {
const rel = filepath.replace(ROOT + "/", "");
const issues = [];
const warnings = [];

if (!existsSync(filepath)) {
return { rel, ok: false, issues: ["ファイルが存在しません"], warnings: [] };
}

if (rel === "personal-vault/README.md") {
return { rel, ok: true, issues, warnings };
}

const content = readFileSync(filepath, "utf-8");
const { error, fields, body } = parseFrontmatter(content);

if (error) {
issues.push(error);
return { rel, ok: false, issues, warnings };
}

for (const key of REQUIRED_FRONTMATTER) {
if (!(key in fields)) issues.push(`フロントマター欠落: ${key}`);
}

if (fields.status && !["draft", "stable"].includes(fields.status)) {
warnings.push(`status が draft/stable 以外: ${fields.status}`);
}

if (fields.shareable && !["true", "false"].includes(fields.shareable)) {
warnings.push(`shareable が true/false 以外: ${fields.shareable}`);
}

const bodyWithoutFrontmatter = body.trim();
const sections = ["## 事実", "## 解釈", "## 具体例"];
const isCoreNote = CORE_NOTES.some((n) => rel === `personal-vault/${n}.md`);
const isMoc = rel === "personal-vault/00-MOC-プロフィール.md";
const isSession = rel.startsWith("personal-vault/sessions/");

if (isCoreNote) {
for (const section of sections) {
if (!content.includes(section)) {
issues.push(`必須セクション欠落: ${section}`);
}
}

const placeholderCount = (body.match(new RegExp(PLACEHOLDER, "g")) || []).length;
if (placeholderCount >= 3) {
warnings.push(`プレースホルダーが多い(${placeholderCount}箇所)— 対話未実施の可能性`);
}
}

if (isSession && !content.includes("## 質問")) {
issues.push("セッションログに「## 質問」セクションがありません");
}

if (!isMoc && !rel.includes("/templates/") && bodyWithoutFrontmatter.length < 50) {
warnings.push("本文が極端に短いです");
}

return { rel, ok: issues.length === 0, issues, warnings };
}

function qaMocLinks() {
const issues = [];
const warnings = [];
const mocPath = join(VAULT_DIR, "00-MOC-プロフィール.md");

if (!existsSync(mocPath)) {
return { issues: ["00-MOC-プロフィール.md が存在しません"], warnings: [] };
}

const mocContent = readFileSync(mocPath, "utf-8");

for (const note of CORE_NOTES) {
const wikilink = `[[${note}]]`;
if (!mocContent.includes(wikilink)) {
issues.push(`MOC に wikilink がありません: ${wikilink}`);
}
const notePath = join(VAULT_DIR, `${note}.md`);
if (!existsSync(notePath)) {
issues.push(`MOC が参照するノートが存在しません: ${note}.md`);
}
}

return { issues, warnings };
}

function main() {
const args = process.argv.slice(2);
let files = [];

if (!existsSync(VAULT_DIR)) {
console.error("personal-vault/ が見つかりません");
process.exit(1);
}

if (args.length === 0) {
files = collectMarkdownFiles(VAULT_DIR);
} else {
files = args.map((f) => (f.startsWith("/") ? f : join(ROOT, f)));
}

let totalIssues = 0;
let totalWarnings = 0;

for (const f of files) {
const result = qaFile(f);
const icon = result.ok ? "✅" : "❌";
console.log(`\n${icon} ${result.rel}`);

for (const i of result.issues) {
console.log(` ERROR: ${i}`);
totalIssues++;
}
for (const w of result.warnings) {
console.log(` WARN: ${w}`);
totalWarnings++;
}
if (result.ok && result.warnings.length === 0) {
console.log(" OK");
}
}

const mocResult = qaMocLinks();
if (mocResult.issues.length > 0 || mocResult.warnings.length > 0) {
console.log("\n📋 MOC リンクチェック");
for (const i of mocResult.issues) {
console.log(` ERROR: ${i}`);
totalIssues++;
}
for (const w of mocResult.warnings) {
console.log(` WARN: ${w}`);
totalWarnings++;
}
} else {
console.log("\n📋 MOC リンクチェック: OK");
}

console.log(`\n--- Summary: ${files.length} files, ${totalIssues} errors, ${totalWarnings} warnings ---`);
process.exit(totalIssues > 0 ? 1 : 0);
}

main();
48 changes: 48 additions & 0 deletions personal-vault/00-MOC-プロフィール.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
title: プロフィール MOC
tags:
- moc
- profile
created: 2026-08-29
updated: 2026-08-29
status: draft
shareable: false
---

# プロフィール MOC

> 対話で蓄積する「自分のこと」の目次。`@personal-knowledge` で更新される。

## コアノート

| テーマ | ノート | 状態 |
|--------|--------|------|
| 成り立ち | [[成り立ち]] | 一通り記録済み |
| 価値観 | [[価値観]] | 一通り記録済み |
| 考え方 | [[考え方]] | 一通り記録済み |
| 仕事の進め方 | [[仕事の進め方]] | 一通り記録済み |
| 目標とビジョン | [[目標とビジョン]] | 一通り記録済み |

## 対話ログ

- [[sessions/2026-08-29-成り立ち]] — セッション1完了: 経緯・感情・環境・業務フロー記録済み
- [[sessions/2026-08-29-価値観]] — セッション1: 見返す・誠実さ・おじいちゃん・ハッカソン
- [[sessions/2026-08-29-価値観-2]] — セッション2: おじいちゃんの3つの教え
- [[sessions/2026-08-29-考え方]] — セッション1完了: 誠実さ基準・取り繕い・社会課題/SNS
- [[sessions/2026-08-29-仕事と目標]] — セッション1完了: 集中/疲労・SNSプロダクト・エージェント完遂

## 業務コンテキスト(リポジトリ参照)

業務の詳細はリポジトリ内ドキュメントを正とする。ここでは要約のみ。

- 主業務: Web開発
- くり返し作業: GitHub投稿(commit / push / PR)
- ボトルネック: 新しい成果物のゼロからの立ち上げ
- 最終ゴール: 最強AIエージェントマスター

詳細は `business-ops/BUSINESS_MAP.md` および `business-ops/AI_AGENT_MASTERY.md` を参照。

## 関連 Skill

- `@personal-knowledge` — 対話型ナレッジ収集
- `@ai-agent-mastery` — エージェント委任の設計
Loading
Loading