@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.
- package/LICENSE +21 -0
- package/README.md +174 -0
- package/README.zh-CN.md +155 -0
- package/bin/cli.js +90 -0
- package/package.json +29 -0
- package/plugin/VERSION +1 -0
- package/plugin/hooks/classify-message.py +240 -0
- package/plugin/hooks/session-start.sh +176 -0
- package/plugin/hooks/validate-write.py +113 -0
- package/plugin/install.sh +350 -0
- package/plugin/instructions.md +118 -0
- package/plugin/skills/dump.md +29 -0
- package/plugin/skills/ingest.md +63 -0
- package/plugin/skills/recall.md +54 -0
- package/plugin/skills/wrap-up.md +35 -0
- package/vault/.claude/settings.json +39 -0
- package/vault/.claude/skills/dump/SKILL.md +29 -0
- package/vault/.claude/skills/ingest/SKILL.md +63 -0
- package/vault/.claude/skills/recall/SKILL.md +54 -0
- package/vault/.claude/skills/wrap-up/SKILL.md +35 -0
- package/vault/.codex/config.toml +2 -0
- package/vault/.codex/hooks.json +39 -0
- package/vault/.codex/skills/dump/SKILL.md +29 -0
- package/vault/.codex/skills/ingest/SKILL.md +63 -0
- package/vault/.codex/skills/recall/SKILL.md +54 -0
- package/vault/.codex/skills/wrap-up/SKILL.md +35 -0
- package/vault/.codex-vault/hooks/classify-message.py +240 -0
- package/vault/.codex-vault/hooks/session-start.sh +176 -0
- package/vault/.codex-vault/hooks/validate-write.py +113 -0
- package/vault/AGENTS.md +118 -0
- package/vault/CLAUDE.md +118 -0
- package/vault/Home.md +21 -0
- package/vault/brain/Key Decisions.md +11 -0
- package/vault/brain/Memories.md +19 -0
- package/vault/brain/North Star.md +29 -0
- package/vault/brain/Patterns.md +11 -0
- package/vault/log.md +15 -0
- package/vault/reference/.gitkeep +0 -0
- package/vault/sources/.gitkeep +0 -0
- package/vault/sources/README.md +19 -0
- package/vault/templates/Decision Record.md +29 -0
- package/vault/templates/Reference Note.md +25 -0
- package/vault/templates/Source Summary.md +25 -0
- package/vault/templates/Thinking Note.md +26 -0
- package/vault/templates/Work Note.md +25 -0
- package/vault/work/Index.md +35 -0
- package/vault/work/active/.gitkeep +0 -0
- 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,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)
|