llm-wiki-kit 0.1.7 → 0.1.9
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/README.md +13 -9
- package/docs/concepts.md +3 -3
- package/docs/integrations/claude-code.md +7 -3
- package/docs/integrations/codex.md +9 -3
- package/docs/operations.md +28 -16
- package/docs/troubleshooting.md +24 -11
- package/package.json +1 -1
- package/src/cli.js +28 -9
- package/src/doctor.js +49 -19
- package/src/install.js +98 -8
- package/src/projects.js +16 -4
- package/src/templates.js +2 -0
- package/src/update.js +60 -14
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ After the package is published to npm:
|
|
|
11
11
|
```bash
|
|
12
12
|
npm install -g llm-wiki-kit
|
|
13
13
|
llm-wiki install --workspace /apps --profile standard
|
|
14
|
-
llm-wiki doctor
|
|
14
|
+
llm-wiki doctor --workspace /apps
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
Restart Claude Code and Codex sessions after installation.
|
|
@@ -69,7 +69,7 @@ Use Claude Code or Codex normally.
|
|
|
69
69
|
|
|
70
70
|
The installed hooks:
|
|
71
71
|
|
|
72
|
-
- inject `wiki/memory.md`, `wiki/index.md`, and relevant wiki context at session start
|
|
72
|
+
- inject `wiki/memory.md`, `wiki/index.md`, and relevant wiki context at session start, instructions loaded, prompt submit, and post-compact time
|
|
73
73
|
- record redacted turn summaries
|
|
74
74
|
- capture decision points, debugging findings, changed files, and verification notes
|
|
75
75
|
- allow tool calls to proceed without secret/PII-based hook blocking
|
|
@@ -85,16 +85,18 @@ If you need to think about saving every answer manually, the setup has failed.
|
|
|
85
85
|
|
|
86
86
|
Most users should not need these during daily Claude Code/Codex work. They exist for install, update, diagnostics, and agent-side maintenance.
|
|
87
87
|
|
|
88
|
-
- Install/update: `llm-wiki install`, `llm-wiki update`, `llm-wiki projects`
|
|
88
|
+
- Install/update: `llm-wiki install`, `llm-wiki update`, `llm-wiki post-update`, `llm-wiki projects`
|
|
89
89
|
- Diagnostics: `llm-wiki doctor`, `llm-wiki status`, `llm-wiki version`
|
|
90
90
|
- Agent maintenance helpers: `llm-wiki context`, `llm-wiki lint`, `llm-wiki consolidate`
|
|
91
91
|
- Cleanup: `llm-wiki uninstall`
|
|
92
92
|
|
|
93
|
-
`llm-wiki status` is an offline consistency check. It reports the installed runtime version, hook targets,
|
|
93
|
+
`llm-wiki status` is an offline consistency check. It reports the installed runtime version, hook targets, whether the `llm-wiki` command on `PATH` resolves to the current runtime, whether the current workspace has the current managed templates applied, how many rules are auto-updateable, and how many managed-looking rules need agent cleanup.
|
|
94
94
|
|
|
95
|
-
`llm-wiki update --check` is the online update check. It compares the installed package version with the npm registry without changing files.
|
|
95
|
+
`llm-wiki update --check [--to <version-or-tag>]` is the online update check. It compares the installed package version with the npm registry target without changing files, and reports an available update only when the target version is newer than the installed version.
|
|
96
96
|
|
|
97
|
-
`llm-wiki update` upgrades the global npm package, reinstalls the hook entries, and
|
|
97
|
+
`llm-wiki update` upgrades the global npm package when npm has a newer target, reinstalls the hook entries, and reapplies safe managed template updates across known project roots. If the installed runtime already satisfies the registry target, it skips `npm install -g` and still runs post-update maintenance. Use `--current-only` when you intentionally want to update only the supplied workspace. Existing wiki content is not overwritten.
|
|
98
|
+
|
|
99
|
+
`llm-wiki post-update --workspace <project>` reapplies the current runtime's hook entries and safe managed template updates without running `npm install -g`. Use `post-update --all --workspace <search-root>` to reapply templates across discovered project roots.
|
|
98
100
|
|
|
99
101
|
`llm-wiki context "<query>"` prints the same layered context used by hooks. It is mainly for debugging what Codex/Claude Code will see; daily use should rely on hook injection.
|
|
100
102
|
|
|
@@ -102,10 +104,12 @@ Most users should not need these during daily Claude Code/Codex work. They exist
|
|
|
102
104
|
|
|
103
105
|
`llm-wiki consolidate` refreshes only generated marker blocks in `wiki/memory.md` and `wiki/index.md`. It is an agent maintenance helper, not a command users should run after every turn.
|
|
104
106
|
|
|
105
|
-
`llm-wiki projects --workspace /apps` lists project roots that already have `llm-wiki-kit` state and shows the update commands to run. `llm-wiki update --
|
|
107
|
+
`llm-wiki projects --workspace /apps` lists project roots that already have `llm-wiki-kit` state or an older `llm-wiki/wiki/index.md`, and shows the update commands to run. `llm-wiki update --workspace /apps` updates the global runtime once, then reapplies managed templates across every known or discovered project root under `/apps`.
|
|
106
108
|
|
|
107
109
|
After a plain `npm install -g llm-wiki-kit@latest`, existing hooks keep working when they already point at the global npm package path. The next `SessionStart`/`InstructionsLoaded` hook automatically reapplies safe managed template updates for the active project root. Clearly generated older `llm-wiki/AGENTS.md` and procedure files are refreshed even when old state is missing; user-edited files are not overwritten and are surfaced to the active agent as cleanup work. If hooks point at a source checkout or stale shim, run `llm-wiki post-update --workspace <project>` or `llm-wiki install --workspace <project>` once to reconnect them.
|
|
108
110
|
|
|
111
|
+
`llm-wiki install` no longer creates a user-local `~/.local/bin/llm-wiki` shim when an npm/nvm global `llm-wiki` command already resolves to the current runtime. If an older kit-managed local shim is shadowing that npm command, install backs it up and removes it.
|
|
112
|
+
|
|
109
113
|
On PCs that use nvm or user-local npm, prefer the non-sudo global install and make sure the `llm-wiki` command resolves to that npm package:
|
|
110
114
|
|
|
111
115
|
```bash
|
|
@@ -116,10 +120,10 @@ llm-wiki version
|
|
|
116
120
|
llm-wiki status
|
|
117
121
|
```
|
|
118
122
|
|
|
119
|
-
If `which -a llm-wiki` shows a stale `~/.local/bin/llm-wiki` before the nvm/npm global binary, remove
|
|
123
|
+
If `which -a llm-wiki` shows a stale `~/.local/bin/llm-wiki` before the nvm/npm global binary, run install once so the kit can remove a managed shadowing shim or report an unmanaged one:
|
|
120
124
|
|
|
121
125
|
```bash
|
|
122
|
-
|
|
126
|
+
llm-wiki install --workspace /path/to/project --profile standard
|
|
123
127
|
hash -r
|
|
124
128
|
llm-wiki version
|
|
125
129
|
```
|
package/docs/concepts.md
CHANGED
|
@@ -18,9 +18,9 @@ The important behavior is a loop:
|
|
|
18
18
|
2. `memory.md`, `index.md`, and relevant wiki context are injected automatically.
|
|
19
19
|
3. The user works normally; no extra command loop is required.
|
|
20
20
|
4. Hooks gather redacted prompt/tool/result summaries.
|
|
21
|
-
5. At stop/session end,
|
|
22
|
-
6.
|
|
23
|
-
7.
|
|
21
|
+
5. At stop/session end, hooks append redacted live Q&A and create query or decision candidates when the turn has enough captured context.
|
|
22
|
+
6. When reusable knowledge appears, the active Claude Code/Codex agent folds it into existing durable wiki pages instead of leaving everything as one-off Q&A.
|
|
23
|
+
7. Future sessions start from the improved wiki instead of relying on long chat history.
|
|
24
24
|
|
|
25
25
|
The kit is a template/runtime repository. It must not centralize project wiki contents.
|
|
26
26
|
|
|
@@ -38,15 +38,19 @@ Claude Code reads `CLAUDE.md`. For project compatibility, the kit creates a `CLA
|
|
|
38
38
|
|
|
39
39
|
when no project `CLAUDE.md` exists. Existing `CLAUDE.md` files are not overwritten.
|
|
40
40
|
|
|
41
|
-
The hook records redacted turn summaries but does not deny tool calls only because an input looks sensitive.
|
|
41
|
+
The hook records redacted turn summaries but does not deny tool calls only because an input looks sensitive. Hook payloads are stored as small redacted event envelopes rather than full transcripts, and context output is redacted field by field before it is returned to Claude Code.
|
|
42
42
|
|
|
43
|
-
At `SessionStart`/`InstructionsLoaded`, the hook injects `llm-wiki/wiki/memory.md`, `llm-wiki/wiki/index.md`, recent log context, and
|
|
43
|
+
At `SessionStart`/`InstructionsLoaded`, the hook first attempts a safe managed-template refresh, then injects `llm-wiki/wiki/memory.md`, `llm-wiki/wiki/index.md`, recent log context, operating rules, and any maintenance note for outdated or customized managed rules. At `UserPromptSubmit`, it searches wiki pages with MiniSearch or substring fallback, expands one-hop wikilinks, redacts context fields, and injects the smallest useful context set.
|
|
44
|
+
|
|
45
|
+
`PostToolUse` and `PostToolBatch` record redacted tool summaries in the same turn buffer. `PreCompact` records a compaction note, and `PostCompact` records the note and returns fresh wiki context. `SubagentStop`, `Stop`, and `SessionEnd` append live Q&A and create `wiki/queries/` candidates when a captured question exists. A `wiki/decisions/` page is created only when the captured turn looks decision-like. `Stop` and `SessionEnd` clear the per-session turn buffer after recording; `SubagentStop` does not.
|
|
46
|
+
|
|
47
|
+
Set `LLM_WIKI_KIT_AUTO_PROJECT_UPDATE=0` only while diagnosing automatic managed-template refresh behavior.
|
|
44
48
|
|
|
45
49
|
After installation or update, run:
|
|
46
50
|
|
|
47
51
|
```bash
|
|
48
52
|
llm-wiki status --workspace /path/to/project
|
|
49
|
-
llm-wiki doctor
|
|
53
|
+
llm-wiki doctor --workspace /path/to/project
|
|
50
54
|
```
|
|
51
55
|
|
|
52
56
|
Restart Claude Code so it reloads hook settings.
|
|
@@ -29,17 +29,23 @@ Handled events:
|
|
|
29
29
|
|
|
30
30
|
Expected behavior:
|
|
31
31
|
|
|
32
|
-
- `SessionStart` injects `llm-wiki/wiki/memory.md`, `llm-wiki/wiki/index.md`, recent log context, and
|
|
32
|
+
- `SessionStart` first attempts a safe managed-template refresh, then injects `llm-wiki/wiki/memory.md`, `llm-wiki/wiki/index.md`, recent log context, operating rules, and any maintenance note for outdated or customized managed rules.
|
|
33
33
|
- `UserPromptSubmit` searches project wiki pages with MiniSearch or substring fallback, expands one-hop wikilinks, redacts context fields, and injects the smallest useful context set.
|
|
34
34
|
- `PreToolUse` records redacted tool summaries without blocking tool calls.
|
|
35
35
|
- `PostToolUse` records redacted tool summaries in a turn buffer.
|
|
36
|
-
- `
|
|
36
|
+
- `PreCompact` records a compaction note; `PostCompact` records the note and returns fresh wiki context.
|
|
37
|
+
- `SubagentStop` and `Stop` append live Q&A and create `wiki/queries/` candidates when a captured question exists. A `wiki/decisions/` page is created only when the captured turn looks decision-like.
|
|
38
|
+
- `Stop` clears the per-session turn buffer after recording. `SubagentStop` leaves the parent turn buffer available for the final stop event.
|
|
39
|
+
|
|
40
|
+
Hook payloads are stored as small redacted event envelopes rather than full transcripts. Context output is also redacted field by field before it is returned to Codex.
|
|
41
|
+
|
|
42
|
+
Set `LLM_WIKI_KIT_AUTO_PROJECT_UPDATE=0` only while diagnosing automatic managed-template refresh behavior.
|
|
37
43
|
|
|
38
44
|
Run these after install:
|
|
39
45
|
|
|
40
46
|
```bash
|
|
41
47
|
llm-wiki status --workspace /path/to/project
|
|
42
|
-
llm-wiki doctor
|
|
48
|
+
llm-wiki doctor --workspace /path/to/project
|
|
43
49
|
```
|
|
44
50
|
|
|
45
51
|
If Codex reports untrusted hooks, trust the hook through Codex's own trust flow or run a trusted automation profile that explicitly accepts the hook source. Restart Codex after hook installation or update.
|
package/docs/operations.md
CHANGED
|
@@ -41,7 +41,9 @@ Avoid mixing root-owned and user-local installs unless you intentionally choose
|
|
|
41
41
|
|
|
42
42
|
The installer:
|
|
43
43
|
|
|
44
|
-
-
|
|
44
|
+
- uses an npm/nvm global `llm-wiki` command when it already resolves to the current runtime
|
|
45
|
+
- removes an older kit-managed `~/.local/bin/llm-wiki` shim when it shadows that npm/nvm command
|
|
46
|
+
- creates a user-local shim only when no `PATH` command points at the current runtime, such as source checkout development or user-local fallback installs
|
|
45
47
|
- backs up existing Codex/Claude settings before editing
|
|
46
48
|
- merges hook entries without removing existing hooks
|
|
47
49
|
- bootstraps the workspace `llm-wiki/`
|
|
@@ -73,11 +75,14 @@ Daily work should happen through Claude Code/Codex. These commands are for maint
|
|
|
73
75
|
```bash
|
|
74
76
|
llm-wiki version
|
|
75
77
|
llm-wiki status --workspace /path/to/project
|
|
78
|
+
llm-wiki doctor --workspace /path/to/project
|
|
76
79
|
llm-wiki projects --workspace /path/to/search-root
|
|
77
|
-
llm-wiki update --check --workspace /path/to/project
|
|
78
|
-
llm-wiki update --
|
|
80
|
+
llm-wiki update --check --workspace /path/to/project [--to <version-or-tag>]
|
|
81
|
+
llm-wiki update --workspace /path/to/search-root
|
|
82
|
+
llm-wiki update --current-only --workspace /path/to/project
|
|
79
83
|
llm-wiki update --dry-run --workspace /path/to/project
|
|
80
|
-
llm-wiki update --workspace /path/to/project
|
|
84
|
+
llm-wiki post-update --workspace /path/to/project
|
|
85
|
+
llm-wiki post-update --all --workspace /path/to/search-root
|
|
81
86
|
llm-wiki context "search phrase" --workspace /path/to/project
|
|
82
87
|
llm-wiki lint --workspace /path/to/project
|
|
83
88
|
llm-wiki consolidate --workspace /path/to/project
|
|
@@ -86,25 +91,30 @@ llm-wiki consolidate --workspace /path/to/project
|
|
|
86
91
|
`status` is offline and answers whether the local installation is internally consistent:
|
|
87
92
|
|
|
88
93
|
- runtime version and install source
|
|
94
|
+
- whether the `llm-wiki` command on `PATH` resolves to the current runtime
|
|
89
95
|
- Codex/Claude hook entries pointing at the current runtime
|
|
90
96
|
- project template state from `llm-wiki/.kit-state.json`
|
|
91
97
|
- current managed file hashes
|
|
98
|
+
- auto-updateable managed rules and managed-looking rules that need agent cleanup
|
|
92
99
|
|
|
93
|
-
`update --check` is online and asks npm
|
|
100
|
+
`update --check [--to <version-or-tag>]` is online and asks npm for the target version. It reports `update available` only when that registry target is newer than the installed version, so it does not suggest downgrades.
|
|
94
101
|
|
|
95
|
-
`projects --workspace <search-root>` lists discovered project roots that have `llm-wiki/.kit-state.json`, reports whether their managed templates are current, and prints the update commands for the search root.
|
|
102
|
+
`projects --workspace <search-root>` lists discovered project roots that have `llm-wiki/.kit-state.json` or an older `llm-wiki/wiki/index.md`, reports whether their managed templates are current, and prints the update commands for the search root.
|
|
96
103
|
|
|
97
104
|
`update` applies changes explicitly only when the user runs it:
|
|
98
105
|
|
|
99
|
-
- runs `npm install -g llm-wiki-kit@<target>`
|
|
106
|
+
- runs `npm install -g llm-wiki-kit@<target>` only when the registry target is newer than the installed runtime
|
|
107
|
+
- skips npm installation when the installed runtime already satisfies the target, then still runs post-update maintenance
|
|
100
108
|
- restarts into the updated binary for `post-update`
|
|
101
109
|
- reinstalls hook entries without duplicating them
|
|
102
|
-
- patches only managed project files
|
|
110
|
+
- patches only managed project files across known or discovered project roots
|
|
103
111
|
- backs up changed files under `~/.local/share/llm-wiki-kit/backups/`
|
|
104
112
|
|
|
105
|
-
`update --
|
|
113
|
+
By default, `update --workspace <search-root>` performs the npm runtime update once, then reapplies managed templates to every known or discovered project root under the search root. Use `update --current-only --workspace <project>` to limit template reapplication to one project.
|
|
106
114
|
|
|
107
|
-
|
|
115
|
+
`post-update --workspace <project>` skips npm installation and reapplies the current runtime's hook entries plus safe managed template updates. `post-update --all --workspace <search-root>` does the same template reapplication across discovered project roots.
|
|
116
|
+
|
|
117
|
+
After a plain `npm install -g llm-wiki-kit@latest`, existing hooks keep working when they already point at the global npm package path. On the next `SessionStart` or `InstructionsLoaded`, the runtime automatically reapplies safe managed template updates for the active project root. If hooks point at a source checkout or stale shim, run `llm-wiki post-update --workspace <project>` or `llm-wiki install --workspace <project>` once to reconnect them. `install` will avoid recreating a local shim when an npm/nvm command already points at the current runtime.
|
|
108
118
|
|
|
109
119
|
## Context And Wiki Maintenance
|
|
110
120
|
|
|
@@ -153,7 +163,7 @@ Broken links, invalid source IDs, and secret-like content are errors and return
|
|
|
153
163
|
|
|
154
164
|
Agents may run `consolidate` after meaningful wiki growth. Users should not need to run it after every turn.
|
|
155
165
|
|
|
156
|
-
When a new runtime sees an older project, `SessionStart`/`InstructionsLoaded` automatically reapplies safe managed template updates. Files that are clearly generated by older kit versions are refreshed. Files that look user-edited are preserved and surfaced to the active agent as cleanup context instead of being overwritten.
|
|
166
|
+
When a new runtime sees an older project, `SessionStart`/`InstructionsLoaded` automatically reapplies safe managed template updates. Files that are clearly generated by older kit versions are refreshed. Files that look user-edited are preserved and surfaced to the active agent as cleanup context instead of being overwritten. Set `LLM_WIKI_KIT_AUTO_PROJECT_UPDATE=0` only while diagnosing automatic template refresh behavior.
|
|
157
167
|
|
|
158
168
|
## Updating User-Local Or nvm Installs
|
|
159
169
|
|
|
@@ -174,10 +184,10 @@ npm root -g
|
|
|
174
184
|
node "$(npm root -g)/llm-wiki-kit/bin/llm-wiki.js" version
|
|
175
185
|
```
|
|
176
186
|
|
|
177
|
-
When `which -a llm-wiki` shows `~/.local/bin/llm-wiki` before the nvm global binary,
|
|
187
|
+
When `which -a llm-wiki` shows `~/.local/bin/llm-wiki` before the nvm global binary, run install once. The kit removes an older managed shadowing shim when the npm/nvm command already points at the current runtime, and reports unmanaged local commands through `status`/`doctor`.
|
|
178
188
|
|
|
179
189
|
```bash
|
|
180
|
-
|
|
190
|
+
llm-wiki install --workspace /path/to/project --profile standard
|
|
181
191
|
hash -r
|
|
182
192
|
llm-wiki version
|
|
183
193
|
```
|
|
@@ -195,8 +205,10 @@ npm uninstall -g llm-wiki-kit
|
|
|
195
205
|
Managed project files are conservative:
|
|
196
206
|
|
|
197
207
|
- root `AGENTS.md` is patched only inside the `llm-wiki-kit` marker block
|
|
198
|
-
- `llm-wiki/AGENTS.md` is replaced only when its recorded hash still matches
|
|
199
|
-
- generated procedures are replaced only when their recorded hash still matches
|
|
208
|
+
- `llm-wiki/AGENTS.md` is replaced only when its recorded hash still matches, the whole file exactly matches the current generated template, or the whole file exactly matches a known legacy generated template
|
|
209
|
+
- generated procedures are replaced only when their recorded hash still matches, the whole file exactly matches the current generated template, or the whole file exactly matches a known legacy generated template
|
|
210
|
+
- missing generated `llm-wiki/AGENTS.md` and procedure files are restored when the project wiki tree exists
|
|
211
|
+
- customized managed-looking files and malformed root policy markers are not overwritten; `status`, `lint`, and injected maintenance context surface them for agent cleanup
|
|
200
212
|
- `llm-wiki/wiki/memory.md` is created if missing but never overwritten by template update once it exists
|
|
201
213
|
- `llm-wiki/wiki/index.md` and existing wiki pages are not overwritten by runtime update; `llm-wiki consolidate` updates only its generated marker block
|
|
202
214
|
|
|
@@ -220,7 +232,7 @@ npm view llm-wiki-kit version
|
|
|
220
232
|
npm install -g llm-wiki-kit
|
|
221
233
|
llm-wiki install --workspace /apps --profile standard
|
|
222
234
|
llm-wiki status --workspace /apps
|
|
223
|
-
llm-wiki doctor
|
|
235
|
+
llm-wiki doctor --workspace /apps
|
|
224
236
|
llm-wiki update --check --workspace /apps
|
|
225
237
|
```
|
|
226
238
|
|
package/docs/troubleshooting.md
CHANGED
|
@@ -5,12 +5,21 @@
|
|
|
5
5
|
Run:
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
llm-wiki doctor
|
|
9
|
-
llm-wiki status
|
|
8
|
+
llm-wiki doctor --workspace /path/to/project
|
|
9
|
+
llm-wiki status --workspace /path/to/project
|
|
10
10
|
```
|
|
11
11
|
|
|
12
12
|
Then restart Claude Code or Codex.
|
|
13
13
|
|
|
14
|
+
`doctor` and `status` report whether hooks point at the current runtime, whether the `llm-wiki` command on `PATH` resolves to that runtime, and whether project managed templates are current. If hooks or shims point at a source checkout or stale path, run one of these once:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
llm-wiki post-update --workspace /path/to/project
|
|
18
|
+
llm-wiki install --workspace /path/to/project --profile standard
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Use `LLM_WIKI_KIT_AUTO_PROJECT_UPDATE=0` only while diagnosing automatic managed-template refresh behavior.
|
|
22
|
+
|
|
14
23
|
## Is An Update Available?
|
|
15
24
|
|
|
16
25
|
Use the offline check first:
|
|
@@ -85,30 +94,32 @@ npm root -g
|
|
|
85
94
|
node "$(npm root -g)/llm-wiki-kit/bin/llm-wiki.js" version
|
|
86
95
|
```
|
|
87
96
|
|
|
88
|
-
If the direct `node "$(npm root -g)/llm-wiki-kit/bin/llm-wiki.js" version` command prints the latest version but plain `llm-wiki` is old,
|
|
97
|
+
If the direct `node "$(npm root -g)/llm-wiki-kit/bin/llm-wiki.js" version` command prints the latest version but plain `llm-wiki` is old, reconnect through the installer:
|
|
89
98
|
|
|
90
99
|
```bash
|
|
91
|
-
|
|
92
|
-
ln -sfn "$(npm root -g)/llm-wiki-kit/bin/llm-wiki.js" "$HOME/.local/bin/llm-wiki"
|
|
93
|
-
export PATH="$HOME/.local/bin:$PATH"
|
|
100
|
+
llm-wiki install --workspace /path/to/project --profile standard
|
|
94
101
|
hash -r
|
|
95
|
-
llm-wiki status
|
|
102
|
+
llm-wiki status --workspace /path/to/project
|
|
96
103
|
```
|
|
97
104
|
|
|
105
|
+
After a manual `sudo npm install -g llm-wiki-kit@latest`, a normal-user `llm-wiki update --workspace <search-root>` can be used to reapply hooks and managed templates. When the installed runtime already matches the registry target, `update` skips the npm install step and runs only post-update maintenance.
|
|
106
|
+
|
|
98
107
|
If `readlink -f "$(command -v llm-wiki)"` points at a repository checkout such as `/mnt/d/dev_proj/llm-wiki-kit/bin/llm-wiki.js`, `npm install -g` is not updating that checkout. Either switch the shim to the npm package as above, or update the checkout itself with `git pull` and run it intentionally as source.
|
|
99
108
|
|
|
100
|
-
|
|
109
|
+
Running `llm-wiki post-update --workspace /path/to/project` or `llm-wiki install --workspace /path/to/project --profile standard` also reconnects hook entries to the current runtime. `install` uses an npm/nvm global command when it already resolves to the current runtime, removes an older kit-managed local shim when it shadows that command, and creates a local shim only when no `PATH` command points at the current runtime.
|
|
110
|
+
|
|
111
|
+
If `which -a llm-wiki` shows both `~/.local/bin/llm-wiki` and an nvm path such as `~/.nvm/versions/node/v20.20.2/bin/llm-wiki`, the `~/.local/bin` entry usually wins because it appears earlier on `PATH`. Let install handle managed shims first:
|
|
101
112
|
|
|
102
113
|
```bash
|
|
103
|
-
|
|
114
|
+
llm-wiki install --workspace /path/to/project --profile standard
|
|
104
115
|
hash -r
|
|
105
116
|
which -a llm-wiki
|
|
106
117
|
readlink -f "$(command -v llm-wiki)"
|
|
107
118
|
llm-wiki version
|
|
108
|
-
llm-wiki status
|
|
119
|
+
llm-wiki status --workspace /path/to/project
|
|
109
120
|
```
|
|
110
121
|
|
|
111
|
-
Do not remove the entire `~/.local/bin` directory; it may contain unrelated commands. If
|
|
122
|
+
Do not remove the entire `~/.local/bin` directory; it may contain unrelated commands. If `status` or `doctor` reports an unmanaged local command that still shadows npm, inspect that one file before removing it. If the nvm command is missing, reinstall the package in the active nvm Node version:
|
|
112
123
|
|
|
113
124
|
```bash
|
|
114
125
|
npm uninstall -g llm-wiki-kit
|
|
@@ -170,6 +181,8 @@ Check:
|
|
|
170
181
|
|
|
171
182
|
The hook does not block tool calls only because inputs look sensitive. Durable summaries redact authentication values before writing, while ordinary work context such as dates, phone numbers, emails, and business identifiers is preserved by default.
|
|
172
183
|
|
|
184
|
+
Hook payloads are stored as small redacted event envelopes rather than full transcripts. Manual and hook context output is redacted before wiki excerpts or search hits are returned.
|
|
185
|
+
|
|
173
186
|
## Duplicate Pages Appear
|
|
174
187
|
|
|
175
188
|
Run a manual review:
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -4,7 +4,7 @@ import { handleHook } from './hook.js';
|
|
|
4
4
|
import { install, status, uninstall } from './install.js';
|
|
5
5
|
import { bootstrapProject } from './project.js';
|
|
6
6
|
import { inspectProjectState } from './project-state.js';
|
|
7
|
-
import { commandForProject, knownProjectRoots } from './projects.js';
|
|
7
|
+
import { commandForProject, knownProjectRoots, recordProject } from './projects.js';
|
|
8
8
|
import { formatDoctor, runDoctor } from './doctor.js';
|
|
9
9
|
import { migrate } from './migrate.js';
|
|
10
10
|
import { postUpdate, update } from './update.js';
|
|
@@ -54,6 +54,8 @@ function parseOptions(args) {
|
|
|
54
54
|
options.replaceHooks = true;
|
|
55
55
|
} else if (arg === '--all') {
|
|
56
56
|
options.all = true;
|
|
57
|
+
} else if (arg === '--current-only') {
|
|
58
|
+
options.currentOnly = true;
|
|
57
59
|
} else if (arg === '--json') {
|
|
58
60
|
options.json = true;
|
|
59
61
|
} else {
|
|
@@ -82,8 +84,9 @@ export async function runCli(args) {
|
|
|
82
84
|
|
|
83
85
|
Usage:
|
|
84
86
|
llm-wiki install --workspace /apps [--profile standard]
|
|
85
|
-
llm-wiki update --workspace <project> [--check|--dry-run|--to <version-or-tag>]
|
|
86
|
-
llm-wiki
|
|
87
|
+
llm-wiki update --workspace <project-or-search-root> [--check|--dry-run|--current-only|--to <version-or-tag>]
|
|
88
|
+
llm-wiki post-update --workspace <project> [--all]
|
|
89
|
+
llm-wiki doctor --workspace <project>
|
|
87
90
|
llm-wiki projects --workspace /apps
|
|
88
91
|
llm-wiki status
|
|
89
92
|
llm-wiki version
|
|
@@ -104,7 +107,9 @@ Usage:
|
|
|
104
107
|
printJsonOrText(result, options, (value) => [
|
|
105
108
|
'llm-wiki-kit installed',
|
|
106
109
|
`- workspace: ${value.workspace}`,
|
|
107
|
-
`- bin: ${value.
|
|
110
|
+
`- runtime bin: ${value.binPath}`,
|
|
111
|
+
`- command: ${value.commandPath || 'not found on PATH'}`,
|
|
112
|
+
`- local shim: ${value.localBin} (${value.localBinAction})`,
|
|
108
113
|
`- changed hooks: ${value.changed.length ? value.changed.join(', ') : 'none (already installed)'}`,
|
|
109
114
|
'Restart Codex/Claude Code sessions so new hooks are loaded.',
|
|
110
115
|
].join('\n'));
|
|
@@ -149,7 +154,7 @@ Usage:
|
|
|
149
154
|
}
|
|
150
155
|
|
|
151
156
|
if (command === 'doctor') {
|
|
152
|
-
const result = await runDoctor();
|
|
157
|
+
const result = await runDoctor(options);
|
|
153
158
|
printJsonOrText(result, options, formatDoctor);
|
|
154
159
|
if (!result.ok) process.exitCode = 1;
|
|
155
160
|
return;
|
|
@@ -157,7 +162,9 @@ Usage:
|
|
|
157
162
|
|
|
158
163
|
if (command === 'bootstrap') {
|
|
159
164
|
const projectRoot = resolve(options.workspace || process.cwd());
|
|
160
|
-
|
|
165
|
+
const result = await bootstrapProject(projectRoot, options);
|
|
166
|
+
await recordProject(projectRoot, 'bootstrap').catch(() => {});
|
|
167
|
+
printJsonOrText(result, options);
|
|
161
168
|
return;
|
|
162
169
|
}
|
|
163
170
|
|
|
@@ -232,6 +239,7 @@ function formatStatus(value) {
|
|
|
232
239
|
`- bin: ${value.binPath}`,
|
|
233
240
|
`- command: ${value.commandPath || 'not found'}`,
|
|
234
241
|
`- command matches runtime: ${value.commandMatchesRuntime ? 'yes' : 'no'}`,
|
|
242
|
+
`- local shim: ${value.localBin?.path || 'unknown'} (${value.localBin?.exists ? (value.localBin.managed ? 'managed' : 'unmanaged') : 'absent'}${value.localBin?.matchesRuntime ? ', current' : ''})`,
|
|
235
243
|
`- hooks current: ${value.hooksCurrent ? 'yes' : 'no'}`,
|
|
236
244
|
`- codex hook: ${value.codexInstalled ? 'current' : 'missing/outdated'}`,
|
|
237
245
|
`- claude hook: ${value.claudeInstalled ? 'current' : 'missing/outdated'}`,
|
|
@@ -253,13 +261,22 @@ function formatUpdate(value) {
|
|
|
253
261
|
].join('\n');
|
|
254
262
|
}
|
|
255
263
|
if (value.mode === 'dry-run') {
|
|
264
|
+
const projects = Array.isArray(value.projects) ? value.projects : [];
|
|
265
|
+
const changed = projects.length > 0
|
|
266
|
+
? projects.reduce((sum, item) => sum + (item.project?.changed?.length || 0), 0)
|
|
267
|
+
: (value.project?.changed?.length || 0);
|
|
268
|
+
const skipped = projects.length > 0
|
|
269
|
+
? projects.reduce((sum, item) => sum + (item.project?.skipped?.length || 0), 0)
|
|
270
|
+
: (value.project?.skipped?.length || 0);
|
|
256
271
|
return [
|
|
257
272
|
'llm-wiki-kit update dry-run',
|
|
258
273
|
`- installed: ${value.installedVersion}`,
|
|
259
274
|
`- latest: ${value.latestVersion}`,
|
|
260
275
|
`- update available: ${value.updateAvailable ? 'yes' : 'no'}`,
|
|
261
|
-
`-
|
|
262
|
-
`-
|
|
276
|
+
`- scope: ${value.scope || 'current'}`,
|
|
277
|
+
...(projects.length > 0 ? [`- projects checked: ${projects.length}`] : []),
|
|
278
|
+
`- project template changes: ${changed}`,
|
|
279
|
+
`- project template skipped: ${skipped}`,
|
|
263
280
|
].join('\n');
|
|
264
281
|
}
|
|
265
282
|
return [
|
|
@@ -267,6 +284,7 @@ function formatUpdate(value) {
|
|
|
267
284
|
`- workspace: ${value.workspace}`,
|
|
268
285
|
`- before: ${value.before}`,
|
|
269
286
|
`- target: ${value.target}`,
|
|
287
|
+
`- scope: ${value.scope || 'current'}`,
|
|
270
288
|
...(value.projects ? [`- projects updated: ${value.projects.length}`] : []),
|
|
271
289
|
].join('\n');
|
|
272
290
|
}
|
|
@@ -304,7 +322,8 @@ function formatProjects(value) {
|
|
|
304
322
|
lines.push(`- ${project.workspace}: ${status}`);
|
|
305
323
|
}
|
|
306
324
|
}
|
|
307
|
-
lines.push('', 'To update every listed project root:', commandForProject('update
|
|
325
|
+
lines.push('', 'To update every listed project root:', commandForProject('update', value.workspace));
|
|
326
|
+
lines.push('To update only the current project root:', commandForProject('update --current-only', value.workspace));
|
|
308
327
|
lines.push('To reapply templates without npm install:', commandForProject('post-update --all', value.workspace));
|
|
309
328
|
return lines.join('\n');
|
|
310
329
|
}
|
package/src/doctor.js
CHANGED
|
@@ -9,27 +9,27 @@ function nodeMajor() {
|
|
|
9
9
|
return Number.parseInt(process.versions.node.split('.')[0], 10);
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
export async function runDoctor() {
|
|
12
|
+
export async function runDoctor(options = {}) {
|
|
13
13
|
const checks = [];
|
|
14
|
-
const add = (name, ok, detail = '') => checks.push({ name, ok, detail });
|
|
15
|
-
const stat = await status();
|
|
14
|
+
const add = (id, name, ok, detail = '') => checks.push({ id, name, ok, detail });
|
|
15
|
+
const stat = await status(options);
|
|
16
16
|
|
|
17
|
-
add('node >= 20', nodeMajor() >= 20, process.version);
|
|
18
|
-
add('runtime version detected', Boolean(stat.runtimeVersion), stat.runtimeVersion || 'unknown');
|
|
19
|
-
add('llm-wiki bin exists', await exists(stat.binPath), stat.binPath);
|
|
20
|
-
add('runtime installed from npm', stat.installSource === 'npm', `${stat.installSource}; npm install -g does not update source checkouts`);
|
|
21
|
-
add('llm-wiki command resolves to current runtime', stat.commandMatchesRuntime, stat.commandPath ? `command=${stat.commandPath}; runtime=${stat.binPath}` : 'command not found on PATH');
|
|
22
|
-
add('Codex hook installed', stat.codexInstalled, stat.codexHooksPath);
|
|
23
|
-
add('Claude hook installed', stat.claudeInstalled, stat.claudeSettingsPath);
|
|
24
|
-
add('project templates current', stat.project.managedFilesCurrent, stat.project.statePath);
|
|
25
|
-
add('codex command available', spawnSync('codex', ['--version'], { encoding: 'utf8' }).status === 0, 'codex --version');
|
|
26
|
-
add('claude command available', spawnSync('claude', ['--version'], { encoding: 'utf8' }).status === 0, 'claude --version');
|
|
27
|
-
add('state directory writable', await canWrite(join(kitDataDir(), '.doctor')), kitDataDir());
|
|
28
|
-
add('docs present', await docsPresent(), 'README.md and docs/');
|
|
29
|
-
add('sample hook roundtrip', await sampleHookRoundtrip(stat.binPath), 'UserPromptSubmit fixture');
|
|
17
|
+
add('node', 'node >= 20', nodeMajor() >= 20, process.version);
|
|
18
|
+
add('runtime-version', 'runtime version detected', Boolean(stat.runtimeVersion), stat.runtimeVersion || 'unknown');
|
|
19
|
+
add('runtime-bin', 'llm-wiki bin exists', await exists(stat.binPath), stat.binPath);
|
|
20
|
+
add('install-source', 'runtime installed from npm', stat.installSource === 'npm', `${stat.installSource}; npm install -g does not update source checkouts`);
|
|
21
|
+
add('command-path', 'llm-wiki command resolves to current runtime', stat.commandMatchesRuntime, stat.commandPath ? `command=${stat.commandPath}; runtime=${stat.binPath}` : 'command not found on PATH');
|
|
22
|
+
add('codex-hook', 'Codex hook installed', stat.codexInstalled, stat.codexHooksPath);
|
|
23
|
+
add('claude-hook', 'Claude hook installed', stat.claudeInstalled, stat.claudeSettingsPath);
|
|
24
|
+
add('project-templates', 'project templates current', stat.project.managedFilesCurrent, stat.project.statePath);
|
|
25
|
+
add('codex-command', 'codex command available', spawnSync('codex', ['--version'], { encoding: 'utf8' }).status === 0, 'codex --version');
|
|
26
|
+
add('claude-command', 'claude command available', spawnSync('claude', ['--version'], { encoding: 'utf8' }).status === 0, 'claude --version');
|
|
27
|
+
add('state-writable', 'state directory writable', await canWrite(join(kitDataDir(), '.doctor')), kitDataDir());
|
|
28
|
+
add('docs', 'docs present', await docsPresent(), 'README.md and docs/');
|
|
29
|
+
add('sample-hook', 'sample hook roundtrip', await sampleHookRoundtrip(stat.binPath), 'UserPromptSubmit fixture');
|
|
30
30
|
|
|
31
31
|
const allOk = checks.every((check) => check.ok);
|
|
32
|
-
return { ok: allOk, checks };
|
|
32
|
+
return { ok: allOk, checks, workspace: stat.workspace, status: stat };
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
async function docsPresent() {
|
|
@@ -84,8 +84,38 @@ export function formatDoctor(result) {
|
|
|
84
84
|
lines.push(`- ${check.ok ? 'PASS' : 'WARN'} ${check.name}${check.detail ? ` (${check.detail})` : ''}`);
|
|
85
85
|
}
|
|
86
86
|
if (!result.ok) {
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
const remediation = doctorRemediation(result);
|
|
88
|
+
if (remediation.length > 0) {
|
|
89
|
+
lines.push('', 'Suggested fix:');
|
|
90
|
+
for (const item of remediation) lines.push(`- ${item}`);
|
|
91
|
+
}
|
|
89
92
|
}
|
|
90
93
|
return lines.join('\n');
|
|
91
94
|
}
|
|
95
|
+
|
|
96
|
+
function failed(result, id) {
|
|
97
|
+
return (result.checks || []).some((check) => check.id === id && !check.ok);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function doctorRemediation(result) {
|
|
101
|
+
const workspace = result.workspace || process.cwd();
|
|
102
|
+
const suggestions = [];
|
|
103
|
+
if (failed(result, 'install-source')) {
|
|
104
|
+
suggestions.push(`normal install: npm install -g llm-wiki-kit@latest && llm-wiki install --workspace ${workspace} --profile standard`);
|
|
105
|
+
suggestions.push(`source checkout development: ./install.sh --workspace ${workspace} --profile standard`);
|
|
106
|
+
}
|
|
107
|
+
if (failed(result, 'command-path')) {
|
|
108
|
+
const local = result.status?.localBin;
|
|
109
|
+
if (result.status?.installSource === 'npm' && local?.exists && local.managed) {
|
|
110
|
+
suggestions.push(`remove stale managed local shim if it still shadows npm: rm -f ${local.path} && hash -r`);
|
|
111
|
+
}
|
|
112
|
+
suggestions.push(`reconnect command and hooks: llm-wiki install --workspace ${workspace} --profile standard`);
|
|
113
|
+
}
|
|
114
|
+
if (failed(result, 'codex-hook') || failed(result, 'claude-hook')) {
|
|
115
|
+
suggestions.push(`install hooks: llm-wiki install --workspace ${workspace} --profile standard, then restart Codex/Claude Code sessions`);
|
|
116
|
+
}
|
|
117
|
+
if (failed(result, 'project-templates')) {
|
|
118
|
+
suggestions.push(`refresh managed project templates: llm-wiki post-update --workspace ${workspace}`);
|
|
119
|
+
}
|
|
120
|
+
return [...new Set(suggestions)];
|
|
121
|
+
}
|
package/src/install.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { realpathSync } from 'fs';
|
|
2
|
-
import { chmod } from 'fs/promises';
|
|
2
|
+
import { chmod, lstat, readlink, unlink } from 'fs/promises';
|
|
3
3
|
import { spawnSync } from 'child_process';
|
|
4
4
|
import { join, resolve } from 'path';
|
|
5
5
|
import { CLAUDE_EVENTS, CODEX_EVENTS, KIT_NAME } from './constants.js';
|
|
6
|
-
import { backupFile,
|
|
6
|
+
import { backupFile, exists, homeDir, readJson, safeSymlink, writeJson } from './fs-utils.js';
|
|
7
7
|
import { inspectProjectState } from './project-state.js';
|
|
8
8
|
import { bootstrapProject } from './project.js';
|
|
9
9
|
import { recordProject } from './projects.js';
|
|
@@ -34,6 +34,91 @@ function realpathOrOriginal(path) {
|
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
function sameResolvedPath(left, right) {
|
|
38
|
+
const resolvedLeft = realpathOrOriginal(left);
|
|
39
|
+
const resolvedRight = realpathOrOriginal(right);
|
|
40
|
+
return Boolean(resolvedLeft && resolvedRight && resolvedLeft === resolvedRight);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function samePath(left, right) {
|
|
44
|
+
if (!left || !right) return false;
|
|
45
|
+
return resolve(left) === resolve(right);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function isKitPath(path) {
|
|
49
|
+
return String(path || '').replace(/\\/g, '/').includes('/llm-wiki-kit/');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function inspectLocalBin(localBinPath) {
|
|
53
|
+
try {
|
|
54
|
+
const stat = await lstat(localBinPath);
|
|
55
|
+
const symlink = stat.isSymbolicLink();
|
|
56
|
+
const target = symlink ? await readlink(localBinPath).catch(() => null) : null;
|
|
57
|
+
const resolved = realpathOrOriginal(localBinPath);
|
|
58
|
+
return {
|
|
59
|
+
path: localBinPath,
|
|
60
|
+
exists: true,
|
|
61
|
+
symlink,
|
|
62
|
+
target,
|
|
63
|
+
resolved,
|
|
64
|
+
managed: symlink && (isKitPath(target) || isKitPath(resolved)),
|
|
65
|
+
matchesRuntime: sameResolvedPath(localBinPath, binPath),
|
|
66
|
+
};
|
|
67
|
+
} catch {
|
|
68
|
+
return {
|
|
69
|
+
path: localBinPath,
|
|
70
|
+
exists: false,
|
|
71
|
+
symlink: false,
|
|
72
|
+
target: null,
|
|
73
|
+
resolved: null,
|
|
74
|
+
managed: false,
|
|
75
|
+
matchesRuntime: false,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async function reconcileLocalBin(localBinPath) {
|
|
81
|
+
const commandPathsBefore = llmWikiCommandPaths();
|
|
82
|
+
const localBefore = await inspectLocalBin(localBinPath);
|
|
83
|
+
const alternateRuntimeCommand = commandPathsBefore.some((path) => (
|
|
84
|
+
!samePath(path, localBinPath) && sameResolvedPath(path, binPath)
|
|
85
|
+
));
|
|
86
|
+
let action = 'kept';
|
|
87
|
+
|
|
88
|
+
if (alternateRuntimeCommand) {
|
|
89
|
+
if (localBefore.exists && localBefore.managed) {
|
|
90
|
+
await backupFile(localBinPath, 'local-bin-llm-wiki');
|
|
91
|
+
await unlink(localBinPath).catch(() => {});
|
|
92
|
+
action = 'removed-shadowing-shim';
|
|
93
|
+
} else if (localBefore.exists) {
|
|
94
|
+
action = 'left-unmanaged-local-bin';
|
|
95
|
+
} else {
|
|
96
|
+
action = 'skipped-npm-command-available';
|
|
97
|
+
}
|
|
98
|
+
} else if (localBefore.matchesRuntime) {
|
|
99
|
+
action = 'kept-current-shim';
|
|
100
|
+
} else {
|
|
101
|
+
if (localBefore.exists) {
|
|
102
|
+
await backupFile(localBinPath, 'local-bin-llm-wiki');
|
|
103
|
+
action = 'replaced-local-shim';
|
|
104
|
+
} else {
|
|
105
|
+
action = 'created-local-shim';
|
|
106
|
+
}
|
|
107
|
+
await safeSymlink(binPath, localBinPath);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const commandPathsAfter = llmWikiCommandPaths();
|
|
111
|
+
const localAfter = await inspectLocalBin(localBinPath);
|
|
112
|
+
return {
|
|
113
|
+
...localAfter,
|
|
114
|
+
action,
|
|
115
|
+
alternateRuntimeCommand,
|
|
116
|
+
commandPathsBefore,
|
|
117
|
+
commandPathsAfter,
|
|
118
|
+
commandPath: commandPathsAfter[0] || null,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
37
122
|
function addHook(hooks, eventName, command, options = {}) {
|
|
38
123
|
hooks[eventName] = Array.isArray(hooks[eventName]) ? hooks[eventName] : [];
|
|
39
124
|
const already = JSON.stringify(hooks[eventName]).includes(command);
|
|
@@ -72,12 +157,8 @@ export async function install(options = {}) {
|
|
|
72
157
|
const workspace = resolve(options.workspace || process.cwd());
|
|
73
158
|
await chmod(binPath, 0o755).catch(() => {});
|
|
74
159
|
const localBin = join(homeDir(), '.local', 'bin');
|
|
75
|
-
await ensureDir(localBin);
|
|
76
160
|
const localBinPath = join(localBin, 'llm-wiki');
|
|
77
|
-
|
|
78
|
-
await backupFile(localBinPath, 'local-bin-llm-wiki');
|
|
79
|
-
}
|
|
80
|
-
await safeSymlink(binPath, localBinPath);
|
|
161
|
+
const localBinResult = await reconcileLocalBin(localBinPath);
|
|
81
162
|
if (!options.noProject) {
|
|
82
163
|
await bootstrapProject(workspace, { profile: options.profile || 'standard', recordState: true });
|
|
83
164
|
await recordProject(workspace, 'install');
|
|
@@ -133,7 +214,12 @@ export async function install(options = {}) {
|
|
|
133
214
|
workspace,
|
|
134
215
|
runtimeVersion: runtimeVersion(),
|
|
135
216
|
binPath,
|
|
136
|
-
localBin:
|
|
217
|
+
localBin: localBinPath,
|
|
218
|
+
localBinAction: localBinResult.action,
|
|
219
|
+
localBinManaged: localBinResult.managed,
|
|
220
|
+
localBinMatchesRuntime: localBinResult.matchesRuntime,
|
|
221
|
+
commandPath: localBinResult.commandPath,
|
|
222
|
+
commandPaths: localBinResult.commandPathsAfter,
|
|
137
223
|
changed,
|
|
138
224
|
};
|
|
139
225
|
}
|
|
@@ -176,7 +262,10 @@ export async function status(options = {}) {
|
|
|
176
262
|
const commandPath = commandPaths[0] || null;
|
|
177
263
|
const resolvedCommandPath = realpathOrOriginal(commandPath);
|
|
178
264
|
const resolvedBinPath = realpathOrOriginal(binPath);
|
|
265
|
+
const localBinPath = join(homeDir(), '.local', 'bin', 'llm-wiki');
|
|
266
|
+
const localBin = await inspectLocalBin(localBinPath);
|
|
179
267
|
return {
|
|
268
|
+
workspace,
|
|
180
269
|
runtimeVersion: runtimeVersion(),
|
|
181
270
|
packageRoot,
|
|
182
271
|
installSource: detectInstallSource(),
|
|
@@ -184,6 +273,7 @@ export async function status(options = {}) {
|
|
|
184
273
|
commandPath,
|
|
185
274
|
commandPaths,
|
|
186
275
|
commandMatchesRuntime: Boolean(resolvedCommandPath && resolvedBinPath && resolvedCommandPath === resolvedBinPath),
|
|
276
|
+
localBin,
|
|
187
277
|
codexInstalled,
|
|
188
278
|
claudeInstalled,
|
|
189
279
|
hooksCurrent: codexInstalled && claudeInstalled,
|
package/src/projects.js
CHANGED
|
@@ -50,6 +50,11 @@ export async function recordProject(projectRoot, source = 'unknown') {
|
|
|
50
50
|
return registry.projects[workspace];
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
async function looksLikeProjectRoot(root) {
|
|
54
|
+
return (await exists(join(root, 'llm-wiki', '.kit-state.json'))) ||
|
|
55
|
+
(await exists(join(root, 'llm-wiki', 'wiki', 'index.md')));
|
|
56
|
+
}
|
|
57
|
+
|
|
53
58
|
export async function discoverProjectRoots(searchRoot, options = {}) {
|
|
54
59
|
const root = resolve(searchRoot || process.cwd());
|
|
55
60
|
const maxDirs = options.maxDirs || 5000;
|
|
@@ -60,7 +65,7 @@ export async function discoverProjectRoots(searchRoot, options = {}) {
|
|
|
60
65
|
if (seen >= maxDirs) return;
|
|
61
66
|
seen += 1;
|
|
62
67
|
|
|
63
|
-
if (await
|
|
68
|
+
if (await looksLikeProjectRoot(dir)) {
|
|
64
69
|
roots.add(dir);
|
|
65
70
|
}
|
|
66
71
|
|
|
@@ -73,8 +78,15 @@ export async function discoverProjectRoots(searchRoot, options = {}) {
|
|
|
73
78
|
|
|
74
79
|
for (const entry of entries) {
|
|
75
80
|
if (seen >= maxDirs) return;
|
|
76
|
-
if (!entry.isDirectory()
|
|
77
|
-
|
|
81
|
+
if (!entry.isDirectory()) continue;
|
|
82
|
+
const child = join(dir, entry.name);
|
|
83
|
+
if (SKIP_DIRS.has(entry.name)) {
|
|
84
|
+
if (entry.name === 'llm-wiki' && await looksLikeProjectRoot(child)) {
|
|
85
|
+
await walk(child);
|
|
86
|
+
}
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
await walk(child);
|
|
78
90
|
}
|
|
79
91
|
}
|
|
80
92
|
|
|
@@ -92,7 +104,7 @@ export async function knownProjectRoots(options = {}) {
|
|
|
92
104
|
|
|
93
105
|
const existing = [];
|
|
94
106
|
for (const root of [...roots].sort()) {
|
|
95
|
-
if (await isDirectory(root) && await
|
|
107
|
+
if (await isDirectory(root) && await looksLikeProjectRoot(root)) {
|
|
96
108
|
existing.push(root);
|
|
97
109
|
}
|
|
98
110
|
}
|
package/src/templates.js
CHANGED
|
@@ -13,6 +13,8 @@ This repository uses llm-wiki-kit as a hook-first living Markdown wiki for Codex
|
|
|
13
13
|
- \`llm-wiki/raw/\`는 원본 또는 redacted 근거 저장소다. hook envelope append 외에는 원본 capture를 수정하지 않는다.
|
|
14
14
|
- \`llm-wiki/wiki/\`는 agent가 관리하는 지식층이다. 결정, 구조, 디버깅, 개념, 절차, 맥락을 여기에 정리한다.
|
|
15
15
|
- \`llm-wiki/wiki/memory.md\`는 짧은 핵심 기억이다. 긴 설명 대신 현재 상태와 중요한 문서 링크만 유지한다.
|
|
16
|
+
- hook이 주입한 context를 우선 사용한다. 수동 확인이나 정리에는 \`llm-wiki context\`, \`llm-wiki lint\`, \`llm-wiki consolidate\`를 agent 보조 도구로 사용한다.
|
|
17
|
+
- hook은 redacted live Q&A와 query/decision 후보를 기록한다. 재사용 가능한 지식은 agent가 기존 정식 wiki 문서에 합친다.
|
|
16
18
|
- 새 문서를 만들기 전에 기존 wiki 문서를 먼저 찾아 갱신한다. 반복해서 쓸 사실은 \`outputs/questions/\`에만 두지 말고 적절한 wiki 문서에 합친다.
|
|
17
19
|
- 일회성 작업 기록은 \`llm-wiki/outputs/questions/\`에 보존하고, 재사용 가능한 결론은 \`wiki/architecture/\`, \`wiki/debugging/\`, \`wiki/decisions/\`, \`wiki/concepts/\`, \`procedures/\`에 반영한다.
|
|
18
20
|
- 검증 명령, 근거 파일, 불확실한 점을 함께 남긴다. 추론은 추론이라고 표시하고, 모순은 지우지 말고 Open Questions 또는 Contradictions에 남긴다.
|
package/src/update.js
CHANGED
|
@@ -4,7 +4,7 @@ import { exists } from './fs-utils.js';
|
|
|
4
4
|
import { appendWikiLog } from './project.js';
|
|
5
5
|
import { install } from './install.js';
|
|
6
6
|
import { applyProjectTemplateUpdate, inspectProjectState } from './project-state.js';
|
|
7
|
-
import { knownProjectRoots } from './projects.js';
|
|
7
|
+
import { knownProjectRoots, recordProject } from './projects.js';
|
|
8
8
|
import { binPath, detectInstallSource, packageName, runtimeVersion } from './version.js';
|
|
9
9
|
|
|
10
10
|
function runCommand(command, args, options = {}) {
|
|
@@ -39,6 +39,34 @@ async function hasProjectWiki(projectRoot) {
|
|
|
39
39
|
return exists(join(projectRoot, 'llm-wiki', 'wiki'));
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
function shouldUpdateAllProjects(options = {}) {
|
|
43
|
+
return options.all || !options.currentOnly;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function projectRootsForUpdate(workspace, options = {}) {
|
|
47
|
+
if (!shouldUpdateAllProjects(options)) return [workspace];
|
|
48
|
+
const roots = await knownProjectRoots({ workspace });
|
|
49
|
+
return roots.length > 0 ? roots : [workspace];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function applyTemplatesToProject(projectRoot, options = {}) {
|
|
53
|
+
const projectResult = options.noProject
|
|
54
|
+
? null
|
|
55
|
+
: await applyProjectTemplateUpdate(projectRoot, { dryRun: options.dryRun });
|
|
56
|
+
if (!options.noProject && !options.dryRun && await hasProjectWiki(projectRoot)) {
|
|
57
|
+
await recordProject(projectRoot, options.source || 'post-update').catch(() => {});
|
|
58
|
+
}
|
|
59
|
+
return projectResult;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function parseJsonOutput(output) {
|
|
63
|
+
try {
|
|
64
|
+
return JSON.parse(output);
|
|
65
|
+
} catch {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
42
70
|
export function parseRegistryVersion(output) {
|
|
43
71
|
return String(output || '').trim().split(/\s+/).at(-1) || '';
|
|
44
72
|
}
|
|
@@ -95,9 +123,7 @@ export async function postUpdate(options = {}) {
|
|
|
95
123
|
const workspaces = await knownProjectRoots({ workspace });
|
|
96
124
|
const projects = [];
|
|
97
125
|
for (const projectRoot of workspaces) {
|
|
98
|
-
const projectResult = options
|
|
99
|
-
? null
|
|
100
|
-
: await applyProjectTemplateUpdate(projectRoot);
|
|
126
|
+
const projectResult = await applyTemplatesToProject(projectRoot, options);
|
|
101
127
|
if (!options.noProject && await hasProjectWiki(projectRoot)) {
|
|
102
128
|
await appendWikiLog(projectRoot, `llm-wiki-kit post-update applied runtime ${runtimeVersion()}; changed templates: ${projectResult.changed.length}`);
|
|
103
129
|
}
|
|
@@ -114,9 +140,7 @@ export async function postUpdate(options = {}) {
|
|
|
114
140
|
};
|
|
115
141
|
}
|
|
116
142
|
|
|
117
|
-
const projectResult = options
|
|
118
|
-
? null
|
|
119
|
-
: await applyProjectTemplateUpdate(workspace);
|
|
143
|
+
const projectResult = await applyTemplatesToProject(workspace, options);
|
|
120
144
|
|
|
121
145
|
if (!options.noProject && await hasProjectWiki(workspace)) {
|
|
122
146
|
await appendWikiLog(workspace, `llm-wiki-kit post-update applied runtime ${runtimeVersion()}; changed templates: ${projectResult.changed.length}`);
|
|
@@ -148,23 +172,40 @@ export async function update(options = {}) {
|
|
|
148
172
|
}
|
|
149
173
|
|
|
150
174
|
if (options.dryRun) {
|
|
175
|
+
const workspaces = await projectRootsForUpdate(workspace, options);
|
|
176
|
+
const projects = [];
|
|
177
|
+
for (const projectRoot of workspaces) {
|
|
178
|
+
projects.push({
|
|
179
|
+
workspace: projectRoot,
|
|
180
|
+
project: options.noProject ? null : await applyProjectTemplateUpdate(projectRoot, { dryRun: true }),
|
|
181
|
+
});
|
|
182
|
+
}
|
|
151
183
|
return {
|
|
152
184
|
mode: 'dry-run',
|
|
153
185
|
workspace,
|
|
154
186
|
...check,
|
|
155
187
|
installSource: detectInstallSource(),
|
|
156
|
-
|
|
188
|
+
scope: shouldUpdateAllProjects(options) ? 'all' : 'current',
|
|
189
|
+
project: projects.find((item) => item.workspace === workspace)?.project || projects[0]?.project || null,
|
|
190
|
+
projects: shouldUpdateAllProjects(options) ? projects : undefined,
|
|
157
191
|
};
|
|
158
192
|
}
|
|
159
193
|
|
|
160
194
|
const target = options.to || 'latest';
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
|
|
195
|
+
const shouldRunNpmInstall = check.updateAvailable;
|
|
196
|
+
const installResult = shouldRunNpmInstall
|
|
197
|
+
? runCommand(npmCommand(), ['install', '-g', `${packageName()}@${target}`], {
|
|
198
|
+
timeout: options.timeout || 120000,
|
|
199
|
+
})
|
|
200
|
+
: {
|
|
201
|
+
status: 0,
|
|
202
|
+
stdout: `skipped; installed ${check.installedVersion} is already >= target ${check.latestVersion}`,
|
|
203
|
+
stderr: '',
|
|
204
|
+
};
|
|
164
205
|
assertCommandOk(installResult, 'npm install -g');
|
|
165
206
|
|
|
166
|
-
const postArgs = [binCommand(), 'post-update', '--workspace', workspace];
|
|
167
|
-
if (options
|
|
207
|
+
const postArgs = [binCommand(), 'post-update', '--workspace', workspace, '--json'];
|
|
208
|
+
if (shouldUpdateAllProjects(options)) postArgs.push('--all');
|
|
168
209
|
if (options.profile) postArgs.push('--profile', options.profile);
|
|
169
210
|
if (options.noProject) postArgs.push('--no-project');
|
|
170
211
|
if (options.codex === false) postArgs.push('--no-codex');
|
|
@@ -173,16 +214,21 @@ export async function update(options = {}) {
|
|
|
173
214
|
timeout: options.timeout || 120000,
|
|
174
215
|
});
|
|
175
216
|
assertCommandOk(postResult, 'post-update');
|
|
217
|
+
const parsedPostUpdate = parseJsonOutput(postResult.stdout);
|
|
176
218
|
|
|
177
219
|
return {
|
|
178
220
|
mode: 'update',
|
|
179
221
|
workspace,
|
|
180
222
|
before: check.installedVersion,
|
|
181
223
|
target,
|
|
224
|
+
scope: shouldUpdateAllProjects(options) ? 'all' : 'current',
|
|
182
225
|
npmInstall: {
|
|
226
|
+
skipped: !shouldRunNpmInstall,
|
|
183
227
|
stdout: installResult.stdout.trim(),
|
|
184
228
|
stderr: installResult.stderr.trim(),
|
|
185
229
|
},
|
|
186
|
-
postUpdate: postResult.stdout.trim(),
|
|
230
|
+
postUpdate: parsedPostUpdate || postResult.stdout.trim(),
|
|
231
|
+
project: parsedPostUpdate?.project,
|
|
232
|
+
projects: parsedPostUpdate?.projects,
|
|
187
233
|
};
|
|
188
234
|
}
|