@suwujs/codex-vault 0.1.0

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.
Files changed (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +174 -0
  3. package/README.zh-CN.md +155 -0
  4. package/bin/cli.js +90 -0
  5. package/package.json +29 -0
  6. package/plugin/VERSION +1 -0
  7. package/plugin/hooks/classify-message.py +240 -0
  8. package/plugin/hooks/session-start.sh +176 -0
  9. package/plugin/hooks/validate-write.py +113 -0
  10. package/plugin/install.sh +350 -0
  11. package/plugin/instructions.md +118 -0
  12. package/plugin/skills/dump.md +29 -0
  13. package/plugin/skills/ingest.md +63 -0
  14. package/plugin/skills/recall.md +54 -0
  15. package/plugin/skills/wrap-up.md +35 -0
  16. package/vault/.claude/settings.json +39 -0
  17. package/vault/.claude/skills/dump/SKILL.md +29 -0
  18. package/vault/.claude/skills/ingest/SKILL.md +63 -0
  19. package/vault/.claude/skills/recall/SKILL.md +54 -0
  20. package/vault/.claude/skills/wrap-up/SKILL.md +35 -0
  21. package/vault/.codex/config.toml +2 -0
  22. package/vault/.codex/hooks.json +39 -0
  23. package/vault/.codex/skills/dump/SKILL.md +29 -0
  24. package/vault/.codex/skills/ingest/SKILL.md +63 -0
  25. package/vault/.codex/skills/recall/SKILL.md +54 -0
  26. package/vault/.codex/skills/wrap-up/SKILL.md +35 -0
  27. package/vault/.codex-vault/hooks/classify-message.py +240 -0
  28. package/vault/.codex-vault/hooks/session-start.sh +176 -0
  29. package/vault/.codex-vault/hooks/validate-write.py +113 -0
  30. package/vault/AGENTS.md +118 -0
  31. package/vault/CLAUDE.md +118 -0
  32. package/vault/Home.md +21 -0
  33. package/vault/brain/Key Decisions.md +11 -0
  34. package/vault/brain/Memories.md +19 -0
  35. package/vault/brain/North Star.md +29 -0
  36. package/vault/brain/Patterns.md +11 -0
  37. package/vault/log.md +15 -0
  38. package/vault/reference/.gitkeep +0 -0
  39. package/vault/sources/.gitkeep +0 -0
  40. package/vault/sources/README.md +19 -0
  41. package/vault/templates/Decision Record.md +29 -0
  42. package/vault/templates/Reference Note.md +25 -0
  43. package/vault/templates/Source Summary.md +25 -0
  44. package/vault/templates/Thinking Note.md +26 -0
  45. package/vault/templates/Work Note.md +25 -0
  46. package/vault/work/Index.md +35 -0
  47. package/vault/work/active/.gitkeep +0 -0
  48. package/vault/work/archive/.gitkeep +0 -0
@@ -0,0 +1,29 @@
1
+ ---
2
+ name: dump
3
+ description: "Freeform capture — dump anything (notes, ideas, decisions, links) and it gets classified and routed to the right vault location. Triggers: 'dump this', 'capture', 'save this thought', 'note this down', 'remember this', 'jot down'."
4
+ license: MIT
5
+ metadata:
6
+ author: sukbearai
7
+ version: "1.0.0"
8
+ homepage: "https://github.com/sukbearai/codex-vault"
9
+ ---
10
+
11
+ Process the following freeform dump. For each distinct piece of information:
12
+
13
+ 1. **Classify** it: decision, project update, win/achievement, or general work note.
14
+ 2. **Search first**: Check if a related note already exists. Prefer updating over creating.
15
+ 3. **Create or update** the appropriate note:
16
+ - Correct folder (work/active/, brain/, etc.)
17
+ - Full YAML frontmatter (date, description, tags)
18
+ - All relevant [[wikilinks]]
19
+ 4. **Update indexes**: `work/Index.md`, `brain/` notes as needed.
20
+ 5. **Cross-link**: Every new note links to at least one existing note.
21
+
22
+ After processing, summarize:
23
+ - What was captured and where
24
+ - New notes created (with paths)
25
+ - Existing notes updated
26
+ - Anything unclear (ask the user)
27
+
28
+ Content to process:
29
+ $ARGUMENTS
@@ -0,0 +1,63 @@
1
+ ---
2
+ name: ingest
3
+ description: "Import external content (URLs, files, text) into the vault as structured source notes. Triggers: 'ingest', 'import this', 'save this article', 'add source', 'read and save'."
4
+ license: MIT
5
+ metadata:
6
+ author: sukbearai
7
+ version: "1.0.0"
8
+ homepage: "https://github.com/sukbearai/codex-vault"
9
+ ---
10
+
11
+ Ingest a source into the vault. Follow these steps:
12
+
13
+ ### 1. Locate the Source
14
+
15
+ The user provides either:
16
+ - A filename in `sources/` (e.g., `sources/karpathy-llm-wiki.md`)
17
+ - A URL to fetch (use `defuddle parse <url> --md` or `curl` to save to `sources/` first)
18
+
19
+ If a URL, save the raw content to `sources/` before proceeding — sources are the immutable record.
20
+
21
+ ### 2. Read the Source
22
+
23
+ Read the full source document. Do not skim — ingestion depends on thorough reading.
24
+
25
+ ### 3. Discuss Key Takeaways
26
+
27
+ Present the top 3-5 takeaways to the user. Ask:
28
+ - Which points are most relevant to current work?
29
+ - Any connections to existing vault notes?
30
+ - Anything to skip or emphasize?
31
+
32
+ Wait for user input before proceeding.
33
+
34
+ ### 4. Create a Source Summary
35
+
36
+ Create a note in `work/active/` using the **Source Summary** template:
37
+ - Fill in all YAML frontmatter (date, description, source, tags)
38
+ - Write Key Takeaways, Summary, Connections (with [[wikilinks]]), Quotes/Data Points
39
+ - The `source` field should reference the file in `sources/` or the original URL
40
+
41
+ ### 5. Update Indexes
42
+
43
+ - Add the summary to `work/Index.md` under a "Sources" section (create the section if it doesn't exist)
44
+ - Update relevant `brain/` notes if the source contains decisions (`Key Decisions.md`), patterns (`Patterns.md`), or context worth remembering (`Memories.md`)
45
+
46
+ ### 6. Cross-Link
47
+
48
+ Check existing vault notes for connections:
49
+ - Do any active projects relate to this source?
50
+ - Does the source reinforce or challenge any existing decisions?
51
+ - Add [[wikilinks]] in both directions where relevant
52
+
53
+ ### 7. Report
54
+
55
+ Summarize what was done:
56
+ - Source file location
57
+ - Summary note created (path)
58
+ - Indexes updated
59
+ - Cross-links added
60
+ - Brain notes updated (if any)
61
+
62
+ Source to process:
63
+ $ARGUMENTS
@@ -0,0 +1,54 @@
1
+ ---
2
+ name: recall
3
+ description: "Search vault memory for a topic — finds relevant notes across brain, work, reference, and sources. Triggers: 'recall', 'what do I know about', 'search memory', 'find notes about', 'look up'."
4
+ license: MIT
5
+ metadata:
6
+ author: sukbearai
7
+ version: "1.0.0"
8
+ homepage: "https://github.com/sukbearai/codex-vault"
9
+ ---
10
+
11
+ Search the vault for information about the given topic.
12
+
13
+ ### 1. Parse the Query
14
+
15
+ Extract the key topic or question from the user's input.
16
+
17
+ ### 2. Search the Vault
18
+
19
+ Two-pass search — semantic first, keyword fallback:
20
+
21
+ **Pass 1 — Frontmatter scan (semantic):**
22
+ Read the first 5 lines (YAML frontmatter) of every `.md` file in the vault. Use the `description` and `tags` fields to judge relevance semantically — match by meaning, not just keywords. For example, a query about "caching" should match a note with description "Redis selection for session storage".
23
+
24
+ Scan in priority order:
25
+ 1. `brain/` — persistent memory
26
+ 2. `work/active/` — current projects
27
+ 3. `reference/` — saved analyses
28
+ 4. `work/archive/` — completed work
29
+ 5. `sources/` — raw source documents
30
+
31
+ **Pass 2 — Keyword grep (fallback):**
32
+ If Pass 1 finds fewer than 2 relevant files, supplement with a keyword grep across file contents.
33
+
34
+ ### 3. Read Matches
35
+
36
+ Read the top 3-5 relevant files in full. Prioritize files where the topic appears in:
37
+ - The description or tags (strongest signal)
38
+ - Headings
39
+ - Multiple times in the body
40
+
41
+ ### 4. Synthesize
42
+
43
+ Present what the vault knows about this topic:
44
+ - **Found in**: list the files (as [[wikilinks]])
45
+ - **Summary**: synthesize the relevant information across all matches
46
+ - **Connections**: note any links between the matched notes
47
+ - **Gaps**: flag if the vault has limited or no information on this topic
48
+
49
+ ### 5. Offer Writeback
50
+
51
+ If the synthesis is substantial (combines 3+ sources), offer to save it as a reference note.
52
+
53
+ Topic to recall:
54
+ $ARGUMENTS
@@ -0,0 +1,35 @@
1
+ ---
2
+ name: wrap-up
3
+ description: "End-of-session wrap-up — commits changes, updates indexes, captures decisions made. Triggers: 'wrap up', 'end session', 'save progress', 'commit and close', 'done for today'."
4
+ license: MIT
5
+ metadata:
6
+ author: sukbearai
7
+ version: "1.0.0"
8
+ homepage: "https://github.com/sukbearai/codex-vault"
9
+ ---
10
+
11
+ Session wrap-up. Review what was done and leave the vault clean.
12
+
13
+ ### 1. Review
14
+ Scan the conversation for notes created or modified. List them all.
15
+
16
+ ### 2. Verify Quality
17
+ For each note: frontmatter complete? At least one [[wikilink]]? Correct folder?
18
+
19
+ ### 3. Check Indexes
20
+ - `work/Index.md` — new notes linked? Completed projects moved?
21
+ - `brain/Memories.md` — Recent Context updated?
22
+ - `brain/Key Decisions.md` — new decisions captured?
23
+ - `brain/Patterns.md` — new patterns observed?
24
+
25
+ ### 4. Check for Orphans
26
+ Any new notes not linked from at least one other note?
27
+
28
+ ### 5. Archive Check
29
+ Notes in `work/active/` that should move to `work/archive/`?
30
+
31
+ ### 6. Report
32
+ - **Done**: what was captured
33
+ - **Fixed**: issues resolved
34
+ - **Flagged**: needs user input
35
+ - **Suggested**: improvements for next time
@@ -0,0 +1,2 @@
1
+ [features]
2
+ codex_hooks = true
@@ -0,0 +1,39 @@
1
+ {
2
+ "hooks": {
3
+ "SessionStart": [
4
+ {
5
+ "matcher": "startup|resume",
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": "bash .codex-vault/hooks/session-start.sh",
10
+ "timeout": 30
11
+ }
12
+ ]
13
+ }
14
+ ],
15
+ "UserPromptSubmit": [
16
+ {
17
+ "hooks": [
18
+ {
19
+ "type": "command",
20
+ "command": "python3 .codex-vault/hooks/classify-message.py",
21
+ "timeout": 15
22
+ }
23
+ ]
24
+ }
25
+ ],
26
+ "PostToolUse": [
27
+ {
28
+ "matcher": "Write|Edit",
29
+ "hooks": [
30
+ {
31
+ "type": "command",
32
+ "command": "python3 .codex-vault/hooks/validate-write.py",
33
+ "timeout": 15
34
+ }
35
+ ]
36
+ }
37
+ ]
38
+ }
39
+ }
@@ -0,0 +1,29 @@
1
+ ---
2
+ name: dump
3
+ description: "Freeform capture — dump anything (notes, ideas, decisions, links) and it gets classified and routed to the right vault location. Triggers: 'dump this', 'capture', 'save this thought', 'note this down', 'remember this', 'jot down'."
4
+ license: MIT
5
+ metadata:
6
+ author: sukbearai
7
+ version: "1.0.0"
8
+ homepage: "https://github.com/sukbearai/codex-vault"
9
+ ---
10
+
11
+ Process the following freeform dump. For each distinct piece of information:
12
+
13
+ 1. **Classify** it: decision, project update, win/achievement, or general work note.
14
+ 2. **Search first**: Check if a related note already exists. Prefer updating over creating.
15
+ 3. **Create or update** the appropriate note:
16
+ - Correct folder (work/active/, brain/, etc.)
17
+ - Full YAML frontmatter (date, description, tags)
18
+ - All relevant [[wikilinks]]
19
+ 4. **Update indexes**: `work/Index.md`, `brain/` notes as needed.
20
+ 5. **Cross-link**: Every new note links to at least one existing note.
21
+
22
+ After processing, summarize:
23
+ - What was captured and where
24
+ - New notes created (with paths)
25
+ - Existing notes updated
26
+ - Anything unclear (ask the user)
27
+
28
+ Content to process:
29
+ $ARGUMENTS
@@ -0,0 +1,63 @@
1
+ ---
2
+ name: ingest
3
+ description: "Import external content (URLs, files, text) into the vault as structured source notes. Triggers: 'ingest', 'import this', 'save this article', 'add source', 'read and save'."
4
+ license: MIT
5
+ metadata:
6
+ author: sukbearai
7
+ version: "1.0.0"
8
+ homepage: "https://github.com/sukbearai/codex-vault"
9
+ ---
10
+
11
+ Ingest a source into the vault. Follow these steps:
12
+
13
+ ### 1. Locate the Source
14
+
15
+ The user provides either:
16
+ - A filename in `sources/` (e.g., `sources/karpathy-llm-wiki.md`)
17
+ - A URL to fetch (use `defuddle parse <url> --md` or `curl` to save to `sources/` first)
18
+
19
+ If a URL, save the raw content to `sources/` before proceeding — sources are the immutable record.
20
+
21
+ ### 2. Read the Source
22
+
23
+ Read the full source document. Do not skim — ingestion depends on thorough reading.
24
+
25
+ ### 3. Discuss Key Takeaways
26
+
27
+ Present the top 3-5 takeaways to the user. Ask:
28
+ - Which points are most relevant to current work?
29
+ - Any connections to existing vault notes?
30
+ - Anything to skip or emphasize?
31
+
32
+ Wait for user input before proceeding.
33
+
34
+ ### 4. Create a Source Summary
35
+
36
+ Create a note in `work/active/` using the **Source Summary** template:
37
+ - Fill in all YAML frontmatter (date, description, source, tags)
38
+ - Write Key Takeaways, Summary, Connections (with [[wikilinks]]), Quotes/Data Points
39
+ - The `source` field should reference the file in `sources/` or the original URL
40
+
41
+ ### 5. Update Indexes
42
+
43
+ - Add the summary to `work/Index.md` under a "Sources" section (create the section if it doesn't exist)
44
+ - Update relevant `brain/` notes if the source contains decisions (`Key Decisions.md`), patterns (`Patterns.md`), or context worth remembering (`Memories.md`)
45
+
46
+ ### 6. Cross-Link
47
+
48
+ Check existing vault notes for connections:
49
+ - Do any active projects relate to this source?
50
+ - Does the source reinforce or challenge any existing decisions?
51
+ - Add [[wikilinks]] in both directions where relevant
52
+
53
+ ### 7. Report
54
+
55
+ Summarize what was done:
56
+ - Source file location
57
+ - Summary note created (path)
58
+ - Indexes updated
59
+ - Cross-links added
60
+ - Brain notes updated (if any)
61
+
62
+ Source to process:
63
+ $ARGUMENTS
@@ -0,0 +1,54 @@
1
+ ---
2
+ name: recall
3
+ description: "Search vault memory for a topic — finds relevant notes across brain, work, reference, and sources. Triggers: 'recall', 'what do I know about', 'search memory', 'find notes about', 'look up'."
4
+ license: MIT
5
+ metadata:
6
+ author: sukbearai
7
+ version: "1.0.0"
8
+ homepage: "https://github.com/sukbearai/codex-vault"
9
+ ---
10
+
11
+ Search the vault for information about the given topic.
12
+
13
+ ### 1. Parse the Query
14
+
15
+ Extract the key topic or question from the user's input.
16
+
17
+ ### 2. Search the Vault
18
+
19
+ Two-pass search — semantic first, keyword fallback:
20
+
21
+ **Pass 1 — Frontmatter scan (semantic):**
22
+ Read the first 5 lines (YAML frontmatter) of every `.md` file in the vault. Use the `description` and `tags` fields to judge relevance semantically — match by meaning, not just keywords. For example, a query about "caching" should match a note with description "Redis selection for session storage".
23
+
24
+ Scan in priority order:
25
+ 1. `brain/` — persistent memory
26
+ 2. `work/active/` — current projects
27
+ 3. `reference/` — saved analyses
28
+ 4. `work/archive/` — completed work
29
+ 5. `sources/` — raw source documents
30
+
31
+ **Pass 2 — Keyword grep (fallback):**
32
+ If Pass 1 finds fewer than 2 relevant files, supplement with a keyword grep across file contents.
33
+
34
+ ### 3. Read Matches
35
+
36
+ Read the top 3-5 relevant files in full. Prioritize files where the topic appears in:
37
+ - The description or tags (strongest signal)
38
+ - Headings
39
+ - Multiple times in the body
40
+
41
+ ### 4. Synthesize
42
+
43
+ Present what the vault knows about this topic:
44
+ - **Found in**: list the files (as [[wikilinks]])
45
+ - **Summary**: synthesize the relevant information across all matches
46
+ - **Connections**: note any links between the matched notes
47
+ - **Gaps**: flag if the vault has limited or no information on this topic
48
+
49
+ ### 5. Offer Writeback
50
+
51
+ If the synthesis is substantial (combines 3+ sources), offer to save it as a reference note.
52
+
53
+ Topic to recall:
54
+ $ARGUMENTS
@@ -0,0 +1,35 @@
1
+ ---
2
+ name: wrap-up
3
+ description: "End-of-session wrap-up — commits changes, updates indexes, captures decisions made. Triggers: 'wrap up', 'end session', 'save progress', 'commit and close', 'done for today'."
4
+ license: MIT
5
+ metadata:
6
+ author: sukbearai
7
+ version: "1.0.0"
8
+ homepage: "https://github.com/sukbearai/codex-vault"
9
+ ---
10
+
11
+ Session wrap-up. Review what was done and leave the vault clean.
12
+
13
+ ### 1. Review
14
+ Scan the conversation for notes created or modified. List them all.
15
+
16
+ ### 2. Verify Quality
17
+ For each note: frontmatter complete? At least one [[wikilink]]? Correct folder?
18
+
19
+ ### 3. Check Indexes
20
+ - `work/Index.md` — new notes linked? Completed projects moved?
21
+ - `brain/Memories.md` — Recent Context updated?
22
+ - `brain/Key Decisions.md` — new decisions captured?
23
+ - `brain/Patterns.md` — new patterns observed?
24
+
25
+ ### 4. Check for Orphans
26
+ Any new notes not linked from at least one other note?
27
+
28
+ ### 5. Archive Check
29
+ Notes in `work/active/` that should move to `work/archive/`?
30
+
31
+ ### 6. Report
32
+ - **Done**: what was captured
33
+ - **Fixed**: issues resolved
34
+ - **Flagged**: needs user input
35
+ - **Suggested**: improvements for next time
@@ -0,0 +1,240 @@
1
+ #!/usr/bin/env python3
2
+ """Classify user messages and inject routing hints.
3
+
4
+ Lightweight version: 5 core signals + session-end vault integrity check.
5
+ Agent-agnostic — outputs hookSpecificOutput compatible with both
6
+ Claude Code and Codex CLI.
7
+ """
8
+ import json
9
+ import os
10
+ import subprocess
11
+ import sys
12
+ import re
13
+ from pathlib import Path
14
+
15
+
16
+ SIGNALS = [
17
+ {
18
+ "name": "DECISION",
19
+ "message": "DECISION detected — suggest the user run /dump to capture this decision",
20
+ "patterns": [
21
+ "decided", "deciding", "decision", "we chose", "agreed to",
22
+ "let's go with", "the call is", "we're going with",
23
+ ],
24
+ },
25
+ {
26
+ "name": "WIN",
27
+ "message": "WIN detected — suggest the user run /dump to record this achievement",
28
+ "patterns": [
29
+ "achieved", "won", "praised",
30
+ "kudos", "shoutout", "great feedback", "recognized",
31
+ ],
32
+ },
33
+ {
34
+ "name": "PROJECT UPDATE",
35
+ "message": "PROJECT UPDATE detected — suggest the user run /dump to log this progress",
36
+ "patterns": [
37
+ "project update", "sprint", "milestone",
38
+ "shipped", "shipping", "launched", "launching",
39
+ "completed", "completing", "released", "releasing",
40
+ "deployed", "deploying",
41
+ "went live", "rolled out", "merged", "cut the release",
42
+ ],
43
+ },
44
+ {
45
+ "name": "QUERY",
46
+ "message": "QUERY detected — suggest the user run /recall to check existing knowledge first",
47
+ "patterns": [
48
+ "what is", "how does", "why did", "compare", "analyze",
49
+ "explain the", "what's the difference", "summarize the",
50
+ "relationship between",
51
+ ],
52
+ },
53
+ {
54
+ "name": "INGEST",
55
+ "message": "INGEST detected — suggest the user run /ingest to process the source",
56
+ "patterns": [
57
+ "ingest", "process this", "read this article",
58
+ "summarize this", "new source", "clip this", "web clip",
59
+ ],
60
+ },
61
+ ]
62
+
63
+ SESSION_END_PATTERNS = [
64
+ "wrap up", "wrapping up", "that's all", "that's it",
65
+ "done for now", "done for today", "i'm done", "call it a day",
66
+ "end session", "bye", "goodbye", "good night", "see you",
67
+ "结束", "收工", "今天到这", "就这样",
68
+ ]
69
+
70
+
71
+ def _match(patterns, text):
72
+ for phrase in patterns:
73
+ if re.search(r'(?<![a-zA-Z])' + re.escape(phrase) + r'(?![a-zA-Z])', text):
74
+ return True
75
+ return False
76
+
77
+
78
+ def _find_vault_root():
79
+ """Find vault root from CWD — check for Home.md/brain/, then vault/ subdir."""
80
+ cwd = os.environ.get("CLAUDE_PROJECT_DIR",
81
+ os.environ.get("CODEX_PROJECT_DIR", os.getcwd()))
82
+ if os.path.isfile(os.path.join(cwd, "Home.md")) or os.path.isdir(os.path.join(cwd, "brain")):
83
+ return cwd
84
+ vault_sub = os.path.join(cwd, "vault")
85
+ if os.path.isdir(vault_sub) and (
86
+ os.path.isfile(os.path.join(vault_sub, "Home.md")) or
87
+ os.path.isdir(os.path.join(vault_sub, "brain"))
88
+ ):
89
+ return vault_sub
90
+ return None
91
+
92
+
93
+ def _get_changed_files(vault_root):
94
+ """Get list of changed/new .md files relative to vault root."""
95
+ files = set()
96
+ try:
97
+ # Staged + unstaged changes
98
+ result = subprocess.run(
99
+ ["git", "diff", "--name-only", "HEAD"],
100
+ capture_output=True, text=True, cwd=vault_root, timeout=5,
101
+ )
102
+ for f in result.stdout.strip().splitlines():
103
+ if f.endswith(".md"):
104
+ files.add(f)
105
+
106
+ # Untracked files
107
+ result = subprocess.run(
108
+ ["git", "ls-files", "--others", "--exclude-standard"],
109
+ capture_output=True, text=True, cwd=vault_root, timeout=5,
110
+ )
111
+ for f in result.stdout.strip().splitlines():
112
+ if f.endswith(".md"):
113
+ files.add(f)
114
+ except Exception:
115
+ pass
116
+ return files
117
+
118
+
119
+ def _check_vault_integrity(vault_root):
120
+ """Check for common memory-write omissions."""
121
+ warnings = []
122
+ changed = _get_changed_files(vault_root)
123
+ if not changed:
124
+ return warnings
125
+
126
+ # Check 1: New work notes but Index.md not updated
127
+ new_work = [f for f in changed if f.startswith("work/active/") and f != "work/Index.md"]
128
+ index_updated = "work/Index.md" in changed
129
+ if new_work and not index_updated:
130
+ names = ", ".join(os.path.basename(f).replace(".md", "") for f in new_work)
131
+ warnings.append(f"New work notes ({names}) but work/Index.md not updated")
132
+
133
+ # Check 2: Decision content written but brain/Key Decisions.md not updated
134
+ decision_keywords = ["decided", "decision", "agreed to", "we chose", "the call is"]
135
+ brain_decisions_updated = "brain/Key Decisions.md" in changed
136
+ if not brain_decisions_updated:
137
+ for f in changed:
138
+ if f.endswith(".md") and not f.startswith("brain/"):
139
+ try:
140
+ content = Path(os.path.join(vault_root, f)).read_text(encoding="utf-8").lower()
141
+ if any(kw in content for kw in decision_keywords):
142
+ warnings.append(
143
+ f"'{f}' contains decision content but brain/Key Decisions.md not updated"
144
+ )
145
+ break
146
+ except Exception:
147
+ pass
148
+
149
+ # Check 3: Pattern content written but brain/Patterns.md not updated
150
+ pattern_keywords = ["pattern", "convention", "always do", "never do", "recurring"]
151
+ brain_patterns_updated = "brain/Patterns.md" in changed
152
+ if not brain_patterns_updated:
153
+ for f in changed:
154
+ if f.endswith(".md") and not f.startswith("brain/"):
155
+ try:
156
+ content = Path(os.path.join(vault_root, f)).read_text(encoding="utf-8").lower()
157
+ if any(kw in content for kw in pattern_keywords):
158
+ warnings.append(
159
+ f"'{f}' contains pattern content but brain/Patterns.md not updated"
160
+ )
161
+ break
162
+ except Exception:
163
+ pass
164
+
165
+ # Check 4: operation log not updated after significant changes
166
+ log_updated = "log.md" in changed
167
+ significant_changes = len([f for f in changed
168
+ if f.startswith(("work/", "reference/", "brain/"))]) >= 2
169
+ if significant_changes and not log_updated:
170
+ warnings.append("Multiple vault changes but log.md not updated")
171
+
172
+ return warnings
173
+
174
+
175
+ def classify(prompt):
176
+ p = prompt.lower()
177
+ return [s["message"] for s in SIGNALS if _match(s["patterns"], p)]
178
+
179
+
180
+ def is_session_end(prompt):
181
+ p = prompt.lower()
182
+ return _match(SESSION_END_PATTERNS, p)
183
+
184
+
185
+ def main():
186
+ try:
187
+ input_data = json.load(sys.stdin)
188
+ except (ValueError, EOFError, OSError):
189
+ sys.exit(0)
190
+
191
+ prompt = input_data.get("prompt", "")
192
+ if not isinstance(prompt, str) or not prompt:
193
+ sys.exit(0)
194
+
195
+ messages = []
196
+
197
+ try:
198
+ # Regular signal classification
199
+ signals = classify(prompt)
200
+ messages.extend(signals)
201
+
202
+ # Session-end check
203
+ if is_session_end(prompt):
204
+ vault_root = _find_vault_root()
205
+ if vault_root:
206
+ integrity_warnings = _check_vault_integrity(vault_root)
207
+ if integrity_warnings:
208
+ messages.append(
209
+ "SESSION END — vault integrity check found issues:\n"
210
+ + "\n".join(f" - {w}" for w in integrity_warnings)
211
+ + "\nFix these before wrapping up."
212
+ )
213
+ else:
214
+ messages.append("SESSION END — vault integrity check passed.")
215
+ except Exception:
216
+ sys.exit(0)
217
+
218
+ if messages:
219
+ hints = "\n".join(f"- {s}" for s in messages)
220
+ output = {
221
+ "hookSpecificOutput": {
222
+ "hookEventName": "UserPromptSubmit",
223
+ "additionalContext": (
224
+ "Skill suggestions (do NOT auto-execute — suggest the skill to the user and let them decide):\n"
225
+ + hints
226
+ + "\n\nWait for the user to invoke the skill. Do not create vault notes without explicit user action."
227
+ )
228
+ }
229
+ }
230
+ json.dump(output, sys.stdout)
231
+ sys.stdout.flush()
232
+
233
+ sys.exit(0)
234
+
235
+
236
+ if __name__ == "__main__":
237
+ try:
238
+ main()
239
+ except Exception:
240
+ sys.exit(0)