codebase-wiki 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 (41) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/LICENSE +17 -0
  3. package/README.md +130 -0
  4. package/dist/CHANGELOG.md +34 -0
  5. package/dist/LICENSE +17 -0
  6. package/dist/README.md +130 -0
  7. package/dist/chunks/build.mjs +1976 -0
  8. package/dist/chunks/config.mjs +174 -0
  9. package/dist/chunks/llm.mjs +214 -0
  10. package/dist/code-wiki-mcp.mjs +367 -0
  11. package/dist/code-wiki.mjs +11 -0
  12. package/dist/codewiki.mjs +2002 -0
  13. package/dist/grammars/c.wasm +0 -0
  14. package/dist/grammars/cpp.wasm +0 -0
  15. package/dist/grammars/go.wasm +0 -0
  16. package/dist/grammars/java.wasm +0 -0
  17. package/dist/grammars/javascript.wasm +0 -0
  18. package/dist/grammars/python.wasm +0 -0
  19. package/dist/grammars/ruby.wasm +0 -0
  20. package/dist/grammars/rust.wasm +0 -0
  21. package/dist/grammars/tsx.wasm +0 -0
  22. package/dist/grammars/typescript.wasm +0 -0
  23. package/dist/plugin/.mcp.json +11 -0
  24. package/dist/plugin/CLAUDE.md +28 -0
  25. package/dist/plugin/commands/wiki-architecture.md +10 -0
  26. package/dist/plugin/commands/wiki-build.md +23 -0
  27. package/dist/plugin/commands/wiki-enrich.md +22 -0
  28. package/dist/plugin/commands/wiki-graph.md +16 -0
  29. package/dist/plugin/commands/wiki-help.md +49 -0
  30. package/dist/plugin/commands/wiki-refresh.md +13 -0
  31. package/dist/plugin/commands/wiki-search.md +18 -0
  32. package/dist/plugin/commands/wiki-symbol.md +21 -0
  33. package/dist/plugin/hooks/hooks.json +74 -0
  34. package/dist/plugin/hooks/nudge.mjs +197 -0
  35. package/dist/plugin/hooks/post-read-reminder.mjs +56 -0
  36. package/dist/plugin/hooks/post-tool-use.mjs +60 -0
  37. package/dist/plugin/hooks/pre-compact.mjs +25 -0
  38. package/dist/plugin/hooks/session-start.mjs +38 -0
  39. package/dist/plugin/hooks/user-prompt-submit.mjs +28 -0
  40. package/dist/plugin/manifest.json +54 -0
  41. package/package.json +100 -0
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,11 @@
1
+ {
2
+ "mcpServers": {
3
+ "code-wiki": {
4
+ "command": "node",
5
+ "args": ["${CLAUDE_PLUGIN_ROOT}/dist/code-wiki-mcp.mjs"],
6
+ "env": {
7
+ "CODEWIKI_CWD": "${workspaceFolder}"
8
+ }
9
+ }
10
+ }
11
+ }
@@ -0,0 +1,28 @@
1
+ # Working with `.codewiki/`
2
+
3
+ If you see a `.codewiki/` directory in this project, the `code-wiki` Claude Code
4
+ plugin is enabled. **Prefer reading `.codewiki/` pages over reading source files
5
+ directly.** The wiki pages are short, structured, and the model is auto-nudged
6
+ toward them whenever you call `Read | Grep | Glob | cat | head | tail`.
7
+
8
+ ## Workflow
9
+
10
+ 1. On a new project or after a major refactor, suggest `/wiki-build` first.
11
+ 2. To find a symbol: `/wiki-search <query>` or the `wiki_search` MCP tool.
12
+ 3. To drill into a single symbol or file: `/wiki-symbol <path>:<name>` or `wiki_drill`.
13
+ 4. To understand callers/callees: `/wiki-graph <symbol>`.
14
+ 5. After making non-trivial edits, suggest `/wiki-refresh` (the PostToolUse hook
15
+ already marks changed files; refresh re-renders them).
16
+
17
+ ## Hard rules
18
+
19
+ - **Never** write into `.codewiki/` directly. Always run `codewiki build` /
20
+ `codewiki refresh`. The plugin permissions deny those writes for a reason.
21
+ - **Never** cat/head/tail a source file when its `.codewiki/files/...md` exists.
22
+ - After a long session, the `PreCompact` hook may have marked `.codewiki/` as
23
+ possibly stale. Run `/wiki-refresh` proactively.
24
+
25
+ ## Disabling
26
+
27
+ Set `nudge.enabled: false` in `.codewikirc` (or `package.json#code-wiki.nudge.enabled`)
28
+ to silence auto-nudges. The wiki itself stays usable.
@@ -0,0 +1,10 @@
1
+ ---
2
+ description: Show the project's architecture.md wiki page (system overview + Mermaid diagram).
3
+ allowed-tools: Read
4
+ ---
5
+
6
+ # /wiki-architecture
7
+
8
+ Read `.codewiki/architecture.md` in `${workspaceFolder}` and summarise the
9
+ project in 4-6 sentences. Include the Mermaid diagram verbatim (don't reformat it).
10
+ Then list the modules in a compact table.
@@ -0,0 +1,23 @@
1
+ ---
2
+ description: Build or rebuild the codewiki for this repo. Run once on first use, or after a major refactor.
3
+ allowed-tools: Bash(node ${CLAUDE_PLUGIN_ROOT}/dist/codewiki.mjs build:*), Bash(node ${CLAUDE_PLUGIN_ROOT}/dist/codewiki.mjs build), Read
4
+ ---
5
+
6
+ # /wiki-build
7
+
8
+ Run `node ${CLAUDE_PLUGIN_ROOT}/dist/codewiki.mjs build` in `${workspaceFolder}`.
9
+
10
+ The plugin ships the CLI binary at `${CLAUDE_PLUGIN_ROOT}/dist/codewiki.mjs`, so
11
+ no global npm install is required. If `.codewiki/` already exists, this runs
12
+ a smart rebuild (only re-parses files changed in git diff). Pass `--full` to
13
+ force a complete rebuild.
14
+
15
+ After the build finishes, report:
16
+
17
+ 1. The number of modules, files, and symbols indexed (from the CLI output).
18
+ 2. The path `.codewiki/INDEX.md`.
19
+ 3. A one-line summary of the largest module.
20
+
21
+ If `.codewiki/` already existed before this command, also note: "To refresh
22
+ later, run `/wiki-refresh` or simply edit files — the PostToolUse hook
23
+ invalidates changed files automatically."
@@ -0,0 +1,22 @@
1
+ ---
2
+ description: Queue LLM enrichment of the wiki using Claude Haiku + Sonnet. Requires ANTHROPIC_API_KEY.
3
+ allowed-tools: Bash(node ${CLAUDE_PLUGIN_ROOT}/dist/codewiki.mjs enrich:*), Bash(node ${CLAUDE_PLUGIN_ROOT}/dist/codewiki.mjs enrich)
4
+ ---
5
+
6
+ # /wiki-enrich
7
+
8
+ Runs `node ${CLAUDE_PLUGIN_ROOT}/dist/codewiki.mjs enrich`. Requires
9
+ `ANTHROPIC_API_KEY` to be set in the environment.
10
+
11
+ Behaviour:
12
+
13
+ - File-level pages: Haiku generates "Notes" for files with cyclomatic > 15 OR
14
+ no docstring OR tokens > 800. Cached by content hash.
15
+ - Module-level pages: Sonnet regenerates the public surface summary if any of
16
+ its files changed. Cached.
17
+
18
+ This command runs in the background by default (the queue is concurrency-bounded
19
+ to 4). Progress is in `.codewiki/.summary-cache/`.
20
+
21
+ If `ANTHROPIC_API_KEY` is not set, the CLI prints a one-line warning and
22
+ exits cleanly — nothing to do.
@@ -0,0 +1,16 @@
1
+ ---
2
+ description: Show callers and callees of a symbol.
3
+ allowed-tools: Bash(node ${CLAUDE_PLUGIN_ROOT}/dist/codewiki.mjs show:*), Bash(node ${CLAUDE_PLUGIN_ROOT}/dist/codewiki.mjs show), Read
4
+ ---
5
+
6
+ # /wiki-graph <symbol>
7
+
8
+ Arguments: $ARGUMENTS
9
+
10
+ Look up the symbol in `.codewiki/.graph.json` (or call the `wiki_callers` and
11
+ `wiki_callees` MCP tools with the symbol id). Print:
12
+
13
+ - Incoming edges (callers), grouped by file.
14
+ - Outgoing edges (callees), grouped by file.
15
+
16
+ Limit to 20 edges each direction. If more exist, say so and offer to expand.
@@ -0,0 +1,49 @@
1
+ ---
2
+ description: Show available wiki commands.
3
+ allowed-tools:
4
+ ---
5
+
6
+ # /wiki-help
7
+
8
+ Print a one-screen summary:
9
+
10
+ ## Slash commands
11
+
12
+ - `/wiki-build` — Build or rebuild the wiki
13
+ - `/wiki-refresh` — Incremental refresh
14
+ - `/wiki-search <query>` — Search symbols/files/modules
15
+ - `/wiki-symbol <path>:<name>` — Read one symbol page
16
+ - `/wiki-architecture` — System overview + diagram
17
+ - `/wiki-graph <symbol>` — Callers and callees
18
+ - `/wiki-enrich` — Optional LLM enrichment (API key required)
19
+ - `/wiki-help` — This help
20
+
21
+ ## MCP tools (auto-discovered)
22
+
23
+ - `wiki_search`, `wiki_drill`, `wiki_callers`, `wiki_callees`, `wiki_changed_since`
24
+
25
+ ## MCP resources
26
+
27
+ - `wiki://tree` — Top-level module index
28
+ - `wiki://architecture` — Architecture overview
29
+ - `wiki://module/<id>` — A module page
30
+ - `wiki://file/<path>` — A file page
31
+ - `wiki://symbol/<id>` — A symbol page
32
+
33
+ ## Onboarding
34
+
35
+ 1. Run `/wiki-build` once.
36
+ 2. Edit files normally — the PostToolUse hook invalidates changed wiki pages.
37
+ 3. Run `/wiki-refresh` after significant edits, or just keep working — the watcher auto-rebuilds.
38
+
39
+ ## Note
40
+
41
+ The CLI is bundled inside the plugin at
42
+ `${CLAUDE_PLUGIN_ROOT}/dist/codewiki.mjs`. No global `npm install` is
43
+ required — the slash commands invoke it via `node` directly.
44
+
45
+ ## Auto-nudge
46
+
47
+ After `/wiki-build`, every `Read | Grep | Glob | Bash cat/head/tail` call you
48
+ make will receive a one-line reminder pointing at the matching `.codewiki/`
49
+ page. This is the source of the token savings — lean into it.
@@ -0,0 +1,13 @@
1
+ ---
2
+ description: Incrementally refresh the wiki based on git diff since the last index.
3
+ allowed-tools: Bash(node ${CLAUDE_PLUGIN_ROOT}/dist/codewiki.mjs refresh:*), Bash(node ${CLAUDE_PLUGIN_ROOT}/dist/codewiki.mjs refresh), Read
4
+ ---
5
+
6
+ # /wiki-refresh
7
+
8
+ Run `node ${CLAUDE_PLUGIN_ROOT}/dist/codewiki.mjs refresh` in `${workspaceFolder}`.
9
+ Report:
10
+
11
+ 1. Which files were re-indexed (git diff since last build, listed by the CLI).
12
+ 2. Which modules were marked stale.
13
+ 3. The new token totals if `.codewiki/.meta.json` changed.
@@ -0,0 +1,18 @@
1
+ ---
2
+ description: Search the wiki for symbols, files, or modules matching a query.
3
+ allowed-tools: Bash(node ${CLAUDE_PLUGIN_ROOT}/dist/codewiki.mjs search:*), Bash(node ${CLAUDE_PLUGIN_ROOT}/dist/codewiki.mjs search)
4
+ ---
5
+
6
+ # /wiki-search <query>
7
+
8
+ Arguments: $ARGUMENTS
9
+
10
+ Run `node ${CLAUDE_PLUGIN_ROOT}/dist/codewiki.mjs search "$ARGUMENTS" --limit 20`
11
+ in `${workspaceFolder}` and present the top results as a table with columns:
12
+ Kind, Name, File, Module.
13
+
14
+ After the table, suggest specific drill-in commands. For example, if a symbol
15
+ hit appears:
16
+
17
+ - `/wiki-symbol src/auth/login.ts:function.loginUser` to read the symbol page
18
+ - or call the MCP tool `wiki_drill` with the symbol id
@@ -0,0 +1,21 @@
1
+ ---
2
+ description: Show the wiki page for a specific symbol or file (function, class, etc.).
3
+ allowed-tools: Bash(node ${CLAUDE_PLUGIN_ROOT}/dist/codewiki.mjs show:*), Bash(node ${CLAUDE_PLUGIN_ROOT}/dist/codewiki.mjs show), Read
4
+ ---
5
+
6
+ # /wiki-symbol <path>:<name>
7
+
8
+ Arguments: $ARGUMENTS
9
+
10
+ Examples:
11
+
12
+ - `/wiki-symbol src/auth/login.ts:function.loginUser`
13
+ - `/wiki-symbol src/auth/login.ts:class.AuthService`
14
+ - `/wiki-symbol src/auth/login.ts` (the whole file page)
15
+
16
+ Run `node ${CLAUDE_PLUGIN_ROOT}/dist/codewiki.mjs show "$ARGUMENTS"` and read
17
+ the result. Summarize:
18
+
19
+ 1. The symbol's purpose (paraphrase the Description).
20
+ 2. Its signature.
21
+ 3. Its callers and callees (from `wiki_callers` and `wiki_callees` MCP tools).
@@ -0,0 +1,74 @@
1
+ {
2
+ "hooks": {
3
+ "SessionStart": [
4
+ {
5
+ "matcher": "*",
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/session-start.mjs",
10
+ "timeout": 5000
11
+ }
12
+ ]
13
+ }
14
+ ],
15
+ "UserPromptSubmit": [
16
+ {
17
+ "matcher": "*",
18
+ "hooks": [
19
+ {
20
+ "type": "command",
21
+ "command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/user-prompt-submit.mjs",
22
+ "timeout": 3000
23
+ }
24
+ ]
25
+ }
26
+ ],
27
+ "PreToolUse": [
28
+ {
29
+ "matcher": "Read|Grep|Glob|Bash",
30
+ "hooks": [
31
+ {
32
+ "type": "command",
33
+ "command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/nudge.mjs",
34
+ "timeout": 3000
35
+ }
36
+ ]
37
+ }
38
+ ],
39
+ "PostToolUse": [
40
+ {
41
+ "matcher": "Write|Edit|MultiEdit",
42
+ "hooks": [
43
+ {
44
+ "type": "command",
45
+ "command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/post-tool-use.mjs",
46
+ "timeout": 3000
47
+ }
48
+ ]
49
+ },
50
+ {
51
+ "matcher": "Read",
52
+ "hooks": [
53
+ {
54
+ "type": "command",
55
+ "command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/post-read-reminder.mjs",
56
+ "timeout": 3000
57
+ }
58
+ ]
59
+ }
60
+ ],
61
+ "PreCompact": [
62
+ {
63
+ "matcher": "*",
64
+ "hooks": [
65
+ {
66
+ "type": "command",
67
+ "command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/pre-compact.mjs",
68
+ "timeout": 2000
69
+ }
70
+ ]
71
+ }
72
+ ]
73
+ }
74
+ }
@@ -0,0 +1,197 @@
1
+ #!/usr/bin/env node
2
+ // PreToolUse nudge hook for Read | Grep | Glob | Bash.
3
+ //
4
+ // Reads stdin JSON: { tool_name, tool_input, cwd }
5
+ // Decides whether the call could be replaced (or preceded) with a wiki read.
6
+ // Emits a permissionDecisionReason the model sees as a reminder.
7
+ //
8
+ // M5 additions:
9
+ // - Honors .codewikirc.json: nudge.minFileLines (skip tiny files),
10
+ // nudge.minLinesForGlob (skip tiny globs)
11
+ // - Adds freshness check: if file mtime > .codewiki/.meta.json updatedAt,
12
+ // the wiki page is stale and we tell the model to run /wiki-refresh
13
+ // - Adds `wiki_changed_since` MCP tool hint on Grep (lets the model
14
+ // catch "is this in the changed set" without paying a Bash cost)
15
+
16
+ import { promises as fs } from 'node:fs'
17
+ import path from 'node:path'
18
+
19
+ const SOURCE_EXTS = new Set([
20
+ '.ts', '.tsx', '.mts', '.cts',
21
+ '.js', '.jsx', '.mjs', '.cjs',
22
+ '.py', '.pyi',
23
+ '.go', '.rs', '.java',
24
+ '.c', '.h', '.cpp', '.cc', '.cxx', '.hpp', '.hh',
25
+ '.rb',
26
+ ])
27
+
28
+ const DEFAULT_MIN_FILE_LINES = 30
29
+
30
+ async function readStdinJson() {
31
+ try {
32
+ const buf = await new Promise((res) => {
33
+ let chunks = ''
34
+ process.stdin.setEncoding('utf8')
35
+ process.stdin.on('data', (c) => (chunks += c))
36
+ process.stdin.on('end', () => res(chunks))
37
+ })
38
+ return JSON.parse(buf || '{}')
39
+ } catch {
40
+ return {}
41
+ }
42
+ }
43
+
44
+ async function loadConfig(cwd) {
45
+ // Tolerant config reader — keeps this script self-contained (no TS imports).
46
+ try {
47
+ const cfg = JSON.parse(await fs.readFile(path.join(cwd, '.codewikirc.json'), 'utf8'))
48
+ return cfg
49
+ } catch {
50
+ try {
51
+ const pkg = JSON.parse(await fs.readFile(path.join(cwd, 'package.json'), 'utf8'))
52
+ if (pkg && pkg['code-wiki'] && pkg['code-wiki'].nudge) {
53
+ return { nudge: pkg['code-wiki'].nudge }
54
+ }
55
+ } catch {
56
+ /* ignore */
57
+ }
58
+ return null
59
+ }
60
+ }
61
+
62
+ async function readMeta(cwd) {
63
+ try {
64
+ return JSON.parse(await fs.readFile(path.join(cwd, '.codewiki', '.meta.json'), 'utf8'))
65
+ } catch {
66
+ return null
67
+ }
68
+ }
69
+
70
+ function repoRel(absOrRel, cwd) {
71
+ const abs = path.isAbsolute(absOrRel) ? absOrRel : path.join(cwd, absOrRel)
72
+ let rel = path.relative(cwd, abs)
73
+ rel = rel.split(path.sep).join('/')
74
+ if (rel.startsWith('./')) rel = rel.slice(2)
75
+ return rel
76
+ }
77
+
78
+ function isSourceFile(repoPath) {
79
+ const ext = path.extname(repoPath).toLowerCase()
80
+ return SOURCE_EXTS.has(ext)
81
+ }
82
+
83
+ function isReadOfWiki(repoPath) {
84
+ return repoPath.startsWith('.codewiki/') || repoPath.includes('/.codewiki/')
85
+ }
86
+
87
+ async function fileMTimeMs(absPath) {
88
+ try {
89
+ const s = await fs.stat(absPath)
90
+ return s.mtimeMs
91
+ } catch {
92
+ return 0
93
+ }
94
+ }
95
+
96
+ async function fileLineCount(absPath) {
97
+ try {
98
+ const buf = await fs.readFile(absPath, 'utf8')
99
+ return buf.split(/\r?\n/).length
100
+ } catch {
101
+ return 0
102
+ }
103
+ }
104
+
105
+ async function isStale(meta, absFilePath) {
106
+ if (!meta || !meta.updatedAt) return false
107
+ const mtime = await fileMTimeMs(absFilePath)
108
+ if (!mtime) return false
109
+ const metaMs = Date.parse(meta.updatedAt)
110
+ return Number.isFinite(metaMs) && mtime > metaMs
111
+ }
112
+
113
+ async function buildReadReason(repoPath, cwd, index, meta, cfg) {
114
+ if (isReadOfWiki(repoPath)) return null
115
+ if (!isSourceFile(repoPath)) return null
116
+
117
+ const filePage = `.codewiki/files/${repoPath}.md`
118
+ try {
119
+ await fs.stat(path.join(cwd, filePage))
120
+ } catch {
121
+ return null
122
+ }
123
+
124
+ const minLines = cfg?.nudge?.minFileLines ?? DEFAULT_MIN_FILE_LINES
125
+ const abs = path.join(cwd, repoPath)
126
+ const lines = await fileLineCount(abs)
127
+ if (lines < minLines) return null
128
+
129
+ const parts = [
130
+ `Tip: ${filePage} is a wiki summary for this file (~${budgetHint(index, repoPath)} tokens).`,
131
+ ]
132
+ if (await isStale(meta, abs)) {
133
+ parts.push('This file is newer than the wiki — run /wiki-refresh first.')
134
+ }
135
+ parts.push('Read the wiki page first; only Read the source if you need details beyond it.')
136
+ return parts.join(' ')
137
+ }
138
+
139
+ function budgetHint(index, repoPath) {
140
+ const f = index?.files?.find((x) => x.path === repoPath)
141
+ return f?.tokens ?? 600
142
+ }
143
+
144
+ async function main() {
145
+ const payload = await readStdinJson()
146
+ const cwd = payload.cwd || process.env.CODEWIKI_CWD || process.cwd()
147
+ const meta = await readMeta(cwd)
148
+ if (!meta) return // no wiki → no nudge
149
+
150
+ const cfg = await loadConfig(cwd)
151
+ if (cfg?.nudge?.enabled === false) return
152
+
153
+ // Soft-delete: respect TTL on identical nudges? M5 keeps it simple — no TTL.
154
+ // Future: timestamp in .codewiki/.state.json.
155
+
156
+ const tool = payload.tool_name
157
+ const ti = payload.tool_input || {}
158
+ let reason = null
159
+
160
+ if (tool === 'Read' && typeof ti.file_path === 'string') {
161
+ const repoPath = repoRel(ti.file_path, cwd)
162
+ const index = payload._index || null // hook can't reach the index from here
163
+ reason = await buildReadReason(repoPath, cwd, index, meta, cfg)
164
+ } else if (tool === 'Grep') {
165
+ const pattern = String(ti.pattern ?? '')
166
+ const pathHint = ti.path ? ` (under ${ti.path})` : ''
167
+ reason = pattern
168
+ ? `Tip: \`wiki_search\` MCP tool covers most symbol/file lookups across this repo. ` +
169
+ `Run \`use_mcp_tool('code-wiki', 'wiki_search', { query: "${pattern.replace(/"/g, '\\"')}" })\` ` +
170
+ `before Grep${pathHint}.`
171
+ : 'Tip: prefer the `wiki_search` MCP tool over Grep when looking for symbols or files.'
172
+ } else if (tool === 'Glob') {
173
+ reason =
174
+ `Tip: \`.codewiki/INDEX.md\` lists every module + file in the wiki. ` +
175
+ `Read it before globbing the source tree.`
176
+ } else if (
177
+ tool === 'Bash' &&
178
+ typeof ti.command === 'string' &&
179
+ /^(cat|head|tail|sed\s+-n|less|more|bat)\s/.test(ti.command.trim())
180
+ ) {
181
+ reason =
182
+ `Tip: prefer \`.codewiki/files/...md\` over cat/head/tail of source files.`
183
+ }
184
+
185
+ if (!reason) return
186
+
187
+ const out = {
188
+ hookSpecificOutput: {
189
+ hookEventName: 'PreToolUse',
190
+ permissionDecision: 'allow',
191
+ permissionDecisionReason: reason,
192
+ },
193
+ }
194
+ process.stdout.write(JSON.stringify(out))
195
+ }
196
+
197
+ main().catch(() => process.exit(0))
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env node
2
+ // PostToolUse hook on Read: emit a one-line note with the wiki page path so
3
+ // the model remembers the wiki location and avoids re-reading the source next
4
+ // time.
5
+
6
+ import { promises as fs } from 'node:fs'
7
+ import path from 'node:path'
8
+
9
+ async function main() {
10
+ let payload
11
+ try {
12
+ const buf = await new Promise((res) => {
13
+ let chunks = ''
14
+ process.stdin.setEncoding('utf8')
15
+ process.stdin.on('data', (c) => (chunks += c))
16
+ process.stdin.on('end', () => res(chunks))
17
+ })
18
+ payload = JSON.parse(buf || '{}')
19
+ } catch {
20
+ payload = {}
21
+ }
22
+ const cwd = payload.cwd || process.env.CODEWIKI_CWD || process.cwd()
23
+ const ti = payload.tool_input || {}
24
+ const filePath = ti.file_path
25
+ if (!filePath) return
26
+
27
+ let rel = path.isAbsolute(filePath) ? path.relative(cwd, filePath) : filePath
28
+ rel = rel.split(path.sep).join('/')
29
+ if (rel.startsWith('./')) rel = rel.slice(2)
30
+ if (rel.startsWith('.codewiki/')) return
31
+
32
+ const wikiPage = `.codewiki/files/${rel}.md`
33
+ try {
34
+ await fs.stat(path.join(cwd, wikiPage))
35
+ } catch {
36
+ return
37
+ }
38
+
39
+ // M5 will pull symbol hints from .index.json. For now, just the file page.
40
+ let hint = ''
41
+ try {
42
+ const idx = JSON.parse(await fs.readFile(path.join(cwd, '.codewiki', '.index.json'), 'utf8'))
43
+ const fileEntry = idx.files?.find((x) => x.path === rel)
44
+ if (fileEntry?.tokens) {
45
+ hint = ` (~${fileEntry.tokens} tokens vs the source file's many more)`
46
+ }
47
+ } catch {
48
+ /* ignore */
49
+ }
50
+
51
+ process.stdout.write(
52
+ `<system-reminder>\nFor future reference, the wiki summary for \`${rel}\` is at \`${wikiPage}\`${hint}.\n</system-reminder>\n`,
53
+ )
54
+ }
55
+
56
+ main().catch(() => process.exit(0))
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env node
2
+ // PostToolUse hook on Write | Edit | MultiEdit:
3
+ // Mark the affected file for incremental re-render.
4
+ // Plus a small PostToolUse reminder: log the wiki page for the touched file
5
+ // so the model sees an inline pointer in subsequent requests.
6
+
7
+ import { promises as fs } from 'node:fs'
8
+ import path from 'node:path'
9
+ import { spawnSync } from 'node:child_process'
10
+
11
+ async function main() {
12
+ let payload
13
+ try {
14
+ const buf = await new Promise((res) => {
15
+ let chunks = ''
16
+ process.stdin.setEncoding('utf8')
17
+ process.stdin.on('data', (c) => (chunks += c))
18
+ process.stdin.on('end', () => res(chunks))
19
+ })
20
+ payload = JSON.parse(buf || '{}')
21
+ } catch {
22
+ payload = {}
23
+ }
24
+ const cwd = payload.cwd || process.env.CODEWIKI_CWD || process.cwd()
25
+
26
+ const ti = payload.tool_input || {}
27
+ const filePath = ti.file_path || ti.path
28
+ if (!filePath) return
29
+
30
+ let rel = path.isAbsolute(filePath) ? path.relative(cwd, filePath) : filePath
31
+ rel = rel.split(path.sep).join('/')
32
+ if (rel.startsWith('./')) rel = rel.slice(2)
33
+ if (!rel || rel.startsWith('.codewiki/')) return
34
+
35
+ // Mark in .state.json for incremental rebuild.
36
+ // Path: this script lives at hooks/post-tool-use.mjs inside the plugin root.
37
+ // The bundled CLI is at dist/codewiki.mjs.
38
+ const here = new URL(import.meta.url).pathname.replace(/^\//, '')
39
+ const hooksDir = path.dirname(here)
40
+ const pluginRoot = path.dirname(hooksDir)
41
+ spawnSync(
42
+ 'node',
43
+ [path.join(pluginRoot, 'dist', 'codewiki.mjs'), 'invalidate', rel],
44
+ { cwd, stdio: 'ignore' },
45
+ )
46
+
47
+ // The inline reminder (Claude Code shows it inline after the tool result).
48
+ const wikiPage = `.codewiki/files/${rel}.md`
49
+ // Verify the wiki page actually exists; otherwise skip.
50
+ try {
51
+ await fs.stat(path.join(cwd, wikiPage))
52
+ } catch {
53
+ return
54
+ }
55
+ process.stdout.write(
56
+ `<system-reminder>\nFile \`${rel}\` was just edited. Its wiki page is at \`${wikiPage}\` and may now be stale. Run /wiki-refresh (or just continue — the next refresh will pick it up).\n</system-reminder>\n`,
57
+ )
58
+ }
59
+
60
+ main().catch(() => process.exit(0))
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env node
2
+ // PreCompact hook: write a marker file so SessionStart can detect that the
3
+ // session was compacted and trigger a smart refresh in the background.
4
+
5
+ import { promises as fs } from 'node:fs'
6
+ import path from 'node:path'
7
+
8
+ async function main() {
9
+ const cwd = process.env.CODEWIKI_CWD || process.cwd()
10
+ const marker = path.join(cwd, '.codewiki', '.last-compact')
11
+ try {
12
+ await fs.mkdir(path.dirname(marker), { recursive: true })
13
+ await fs.writeFile(
14
+ marker,
15
+ JSON.stringify({
16
+ at: new Date().toISOString(),
17
+ head: process.env.CODEWIKI_GIT_HEAD || 'uncommitted',
18
+ }),
19
+ )
20
+ } catch {
21
+ /* never block compact */
22
+ }
23
+ }
24
+
25
+ main().catch(() => process.exit(0))