llm-wiki-kit 0.2.1 → 0.2.2
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 +2 -0
- package/docs/integrations/claude-code.md +2 -1
- package/docs/integrations/codex.md +3 -2
- package/docs/manual.md +2 -0
- package/docs/operations.md +2 -0
- package/package.json +1 -1
- package/src/cli.js +2 -0
- package/src/hook.js +15 -2
- package/src/update-notice.js +120 -0
package/README.md
CHANGED
|
@@ -104,6 +104,8 @@ Most users should not need these during daily Claude Code/Codex work. They exist
|
|
|
104
104
|
|
|
105
105
|
`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.
|
|
106
106
|
|
|
107
|
+
Installed npm runtimes also perform a cached update notice check from hooks while the user works. This does not install anything automatically. When a newer npm release is detected, the injected hook context tells the active agent to mention the available update and offer `llm-wiki update --workspace <project-or-search-root>`. Set `LLM_WIKI_KIT_UPDATE_NOTICE=0` only when diagnosing or suppressing that reminder.
|
|
108
|
+
|
|
107
109
|
`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.
|
|
108
110
|
|
|
109
111
|
`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. By default, episodic `wiki/queries/` pages are excluded from search unless they were promoted with `memory_type: semantic` or `procedural` and `importance >= 4`; use `--include-episodic` only when debugging old automatic query records.
|
|
@@ -40,11 +40,12 @@ when no project `CLAUDE.md` exists. Existing `CLAUDE.md` files are not overwritt
|
|
|
40
40
|
|
|
41
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 first attempts a safe managed-template refresh, recovers stale turn state into `outputs/maintenance/queue.md`, then injects `llm-wiki/wiki/memory.md`, `llm-wiki/wiki/index.md`, recent log context, operating rules, a one-item maintenance summary when needed, and any maintenance note for outdated or customized managed rules. At `UserPromptSubmit`, it recovers stale turn state, searches wiki pages with MiniSearch or substring fallback, expands one-hop wikilinks, redacts context fields, and injects the smallest useful context set. Maintenance reminders are shown only when the prompt is wiki/maintenance related or matches a queue topic.
|
|
43
|
+
At `SessionStart`/`InstructionsLoaded`, the hook first attempts a safe managed-template refresh, recovers stale turn state into `outputs/maintenance/queue.md`, performs a cached npm update notice check for npm installs, then injects `llm-wiki/wiki/memory.md`, `llm-wiki/wiki/index.md`, recent log context, operating rules, a one-item maintenance summary when needed, any update notice, and any maintenance note for outdated or customized managed rules. At `UserPromptSubmit`, it recovers stale turn state, searches wiki pages with MiniSearch or substring fallback, expands one-hop wikilinks, redacts context fields, performs the same cached update notice check, and injects the smallest useful context set. Maintenance reminders are shown only when the prompt is wiki/maintenance related or matches a queue topic.
|
|
44
44
|
|
|
45
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. In the default `answer-first` mode, `SubagentStop` does not create live Q&A, query, decision, or maintenance files. `Stop` and `SessionEnd` append live Q&A only for meaningful work turns and do not auto-create `wiki/queries/` or `wiki/decisions/`. If the user explicitly asked to record or document durable knowledge and no durable wiki update is detected, `Stop`/`SessionEnd` queue a pending maintenance item for agent review. `Stop` and `SessionEnd` then clear the per-session turn buffer; `SubagentStop` does not.
|
|
46
46
|
|
|
47
47
|
Set `LLM_WIKI_KIT_AUTO_PROJECT_UPDATE=0` only while diagnosing automatic managed-template refresh behavior.
|
|
48
|
+
Set `LLM_WIKI_KIT_UPDATE_NOTICE=0` only while suppressing the cached npm update reminder.
|
|
48
49
|
Set `LLM_WIKI_KIT_CAPTURE_MODE=legacy-eager` only as deprecated compatibility mode for the old eager query/decision capture behavior.
|
|
49
50
|
|
|
50
51
|
After installation or update, run:
|
|
@@ -29,8 +29,8 @@ Handled events:
|
|
|
29
29
|
|
|
30
30
|
Expected behavior:
|
|
31
31
|
|
|
32
|
-
- `SessionStart` first attempts a safe managed-template refresh, recovers stale turn state into `outputs/maintenance/queue.md`, then injects `llm-wiki/wiki/memory.md`, `llm-wiki/wiki/index.md`, recent log context, operating rules, a one-item maintenance summary when needed, and any maintenance note for outdated or customized managed rules.
|
|
33
|
-
- `UserPromptSubmit` recovers stale turn state, searches project wiki pages with MiniSearch or substring fallback, expands one-hop wikilinks, redacts context fields, and injects the smallest useful context set. Maintenance reminders are shown only when the prompt is wiki/maintenance related or matches a queue topic.
|
|
32
|
+
- `SessionStart` first attempts a safe managed-template refresh, recovers stale turn state into `outputs/maintenance/queue.md`, performs a cached npm update notice check for npm installs, then injects `llm-wiki/wiki/memory.md`, `llm-wiki/wiki/index.md`, recent log context, operating rules, a one-item maintenance summary when needed, any update notice, and any maintenance note for outdated or customized managed rules.
|
|
33
|
+
- `UserPromptSubmit` recovers stale turn state, searches project wiki pages with MiniSearch or substring fallback, expands one-hop wikilinks, redacts context fields, performs the same cached update notice check, and injects the smallest useful context set. Maintenance reminders are shown only when the prompt is wiki/maintenance related or matches a queue topic.
|
|
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.
|
|
@@ -41,6 +41,7 @@ Expected behavior:
|
|
|
41
41
|
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.
|
|
42
42
|
|
|
43
43
|
Set `LLM_WIKI_KIT_AUTO_PROJECT_UPDATE=0` only while diagnosing automatic managed-template refresh behavior.
|
|
44
|
+
Set `LLM_WIKI_KIT_UPDATE_NOTICE=0` only while suppressing the cached npm update reminder.
|
|
44
45
|
Set `LLM_WIKI_KIT_CAPTURE_MODE=legacy-eager` only as deprecated compatibility mode for the old eager query/decision capture behavior.
|
|
45
46
|
|
|
46
47
|
Run these after install:
|
package/docs/manual.md
CHANGED
|
@@ -149,6 +149,8 @@ llm-wiki update --workspace /path/to/project --dry-run
|
|
|
149
149
|
|
|
150
150
|
`update`는 source checkout에서 self-update하지 않는다. source checkout 개발 중에는 npm package나 local tarball로 global install한 뒤 update behavior를 테스트한다.
|
|
151
151
|
|
|
152
|
+
global npm runtime으로 설치된 경우 hook은 사용 흐름 중 cached update notice check를 수행한다. 자동 설치는 하지 않는다. 새 npm release가 감지되면 hook context가 active agent에게 “업데이트 가능”을 짧게 알리고 `llm-wiki update --workspace <project-or-search-root>` 실행을 제안하게 한다. `LLM_WIKI_KIT_UPDATE_NOTICE=0`으로 이 reminder만 끌 수 있다.
|
|
153
|
+
|
|
152
154
|
### `llm-wiki post-update`
|
|
153
155
|
|
|
154
156
|
npm install 없이 현재 runtime으로 hook entries와 managed project templates를 다시 적용한다.
|
package/docs/operations.md
CHANGED
|
@@ -101,6 +101,8 @@ llm-wiki maintenance --workspace /path/to/project
|
|
|
101
101
|
|
|
102
102
|
`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.
|
|
103
103
|
|
|
104
|
+
Installed npm runtimes also run a cached hook-side update notice check while the user works. It never installs automatically. If npm has a newer release, `SessionStart`/`InstructionsLoaded`/`UserPromptSubmit` context tells the active agent to mention the available update and offer `llm-wiki update --workspace <project-or-search-root>`. Set `LLM_WIKI_KIT_UPDATE_NOTICE=0` to suppress this reminder while diagnosing.
|
|
105
|
+
|
|
104
106
|
`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.
|
|
105
107
|
|
|
106
108
|
`update` applies changes explicitly only when the user runs it:
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -279,6 +279,7 @@ function formatUpdate(value) {
|
|
|
279
279
|
`- installed: ${value.installedVersion}`,
|
|
280
280
|
`- latest: ${value.latestVersion}`,
|
|
281
281
|
`- update available: ${value.updateAvailable ? 'yes' : 'no'}`,
|
|
282
|
+
...(value.updateAvailable ? [`- recommended action: ${commandForProject('update', value.workspace)}`] : []),
|
|
282
283
|
`- project applied runtime: ${value.project?.lastRuntimeVersionApplied || 'unknown'}`,
|
|
283
284
|
].join('\n');
|
|
284
285
|
}
|
|
@@ -295,6 +296,7 @@ function formatUpdate(value) {
|
|
|
295
296
|
`- installed: ${value.installedVersion}`,
|
|
296
297
|
`- latest: ${value.latestVersion}`,
|
|
297
298
|
`- update available: ${value.updateAvailable ? 'yes' : 'no'}`,
|
|
299
|
+
...(value.updateAvailable ? [`- recommended action: ${commandForProject('update', value.workspace)}`] : []),
|
|
298
300
|
`- scope: ${value.scope || 'current'}`,
|
|
299
301
|
...(projects.length > 0 ? [`- projects checked: ${projects.length}`] : []),
|
|
300
302
|
`- project template changes: ${changed}`,
|
package/src/hook.js
CHANGED
|
@@ -6,6 +6,7 @@ import { applyProjectTemplateUpdate, inspectProjectState } from './project-state
|
|
|
6
6
|
import { recordProject } from './projects.js';
|
|
7
7
|
import { summarizeForStorage } from './redaction.js';
|
|
8
8
|
import { buildEntryFromState, clearTurnState, rememberQuestion, rememberTool } from './state.js';
|
|
9
|
+
import { updateNoticeContext } from './update-notice.js';
|
|
9
10
|
import { relative } from 'path';
|
|
10
11
|
|
|
11
12
|
async function readStdinJson() {
|
|
@@ -42,6 +43,13 @@ function contextOutput(eventName, context) {
|
|
|
42
43
|
};
|
|
43
44
|
}
|
|
44
45
|
|
|
46
|
+
async function hookContext(projectRoot, eventName, context, payload) {
|
|
47
|
+
const notice = await updateNoticeContext(projectRoot, eventName, {
|
|
48
|
+
session: payload.session_id || payload.sessionId,
|
|
49
|
+
});
|
|
50
|
+
return [context, notice].filter(Boolean).join('\n\n');
|
|
51
|
+
}
|
|
52
|
+
|
|
45
53
|
async function handleLegacyEagerStop(projectRoot, eventName, payload, entry) {
|
|
46
54
|
if (entry.question !== '(not captured)' || entry.result !== '(not captured)') {
|
|
47
55
|
const liveQaPath = await appendLiveQa(projectRoot, entry);
|
|
@@ -111,7 +119,7 @@ export async function handleHook(provider, explicitEvent) {
|
|
|
111
119
|
}
|
|
112
120
|
|
|
113
121
|
if (eventName === 'SessionStart' || eventName === 'InstructionsLoaded') {
|
|
114
|
-
const context = await buildContextBrief(projectRoot, 'SessionStart');
|
|
122
|
+
const context = await hookContext(projectRoot, eventName, await buildContextBrief(projectRoot, 'SessionStart'), payload);
|
|
115
123
|
return contextOutput(eventName, context);
|
|
116
124
|
}
|
|
117
125
|
|
|
@@ -119,7 +127,12 @@ export async function handleHook(provider, explicitEvent) {
|
|
|
119
127
|
const prompt = promptText(payload);
|
|
120
128
|
await rememberQuestion(projectRoot, payload, prompt);
|
|
121
129
|
const guidance = formatDurableCaptureGuidance(prompt);
|
|
122
|
-
const context =
|
|
130
|
+
const context = await hookContext(
|
|
131
|
+
projectRoot,
|
|
132
|
+
eventName,
|
|
133
|
+
[await buildContextBrief(projectRoot, eventName, prompt), guidance].filter(Boolean).join('\n\n'),
|
|
134
|
+
payload
|
|
135
|
+
);
|
|
123
136
|
return contextOutput(eventName, context);
|
|
124
137
|
}
|
|
125
138
|
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { spawnSync } from 'child_process';
|
|
2
|
+
import { join, resolve } from 'path';
|
|
3
|
+
import { cacheHome, readJson, writeJson } from './fs-utils.js';
|
|
4
|
+
import { commandForProject } from './projects.js';
|
|
5
|
+
import { compareVersions, parseRegistryVersion } from './update.js';
|
|
6
|
+
import { detectInstallSource, packageName, runtimeVersion } from './version.js';
|
|
7
|
+
|
|
8
|
+
const NOTICE_SCHEMA_VERSION = 1;
|
|
9
|
+
const DEFAULT_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
10
|
+
const DEFAULT_TIMEOUT_MS = 3000;
|
|
11
|
+
|
|
12
|
+
function noticeCachePath() {
|
|
13
|
+
return join(cacheHome(), 'llm-wiki-kit', 'update-notice.json');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function npmCommand() {
|
|
17
|
+
return process.env.LLM_WIKI_KIT_NPM || 'npm';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function numberEnv(name, fallback) {
|
|
21
|
+
const raw = process.env[name];
|
|
22
|
+
if (!raw) return fallback;
|
|
23
|
+
const parsed = Number(raw);
|
|
24
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : fallback;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function intervalMs() {
|
|
28
|
+
if (process.env.LLM_WIKI_KIT_UPDATE_NOTICE_INTERVAL_MS) {
|
|
29
|
+
return numberEnv('LLM_WIKI_KIT_UPDATE_NOTICE_INTERVAL_MS', DEFAULT_INTERVAL_MS);
|
|
30
|
+
}
|
|
31
|
+
return numberEnv('LLM_WIKI_KIT_UPDATE_NOTICE_INTERVAL_HOURS', 24) * 60 * 60 * 1000;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function timeoutMs() {
|
|
35
|
+
return numberEnv('LLM_WIKI_KIT_UPDATE_NOTICE_TIMEOUT_MS', DEFAULT_TIMEOUT_MS);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function noticeEnabled() {
|
|
39
|
+
if (process.env.LLM_WIKI_KIT_UPDATE_NOTICE === '0') return false;
|
|
40
|
+
if (detectInstallSource() === 'source' && process.env.LLM_WIKI_KIT_UPDATE_NOTICE_ALLOW_SOURCE !== '1') {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function freshEnough(record, now = Date.now()) {
|
|
47
|
+
const checkedAt = Date.parse(record?.checkedAt || '');
|
|
48
|
+
return Number.isFinite(checkedAt) && now - checkedAt < intervalMs();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function cacheMatches(record, target, installedVersion) {
|
|
52
|
+
return record?.schemaVersion === NOTICE_SCHEMA_VERSION &&
|
|
53
|
+
record.target === target &&
|
|
54
|
+
record.installedVersion === installedVersion;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function trimDetail(value) {
|
|
58
|
+
return String(value || '').trim().slice(0, 500);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function checkRegistry(target) {
|
|
62
|
+
const result = spawnSync(npmCommand(), ['view', `${packageName()}@${target}`, 'version'], {
|
|
63
|
+
encoding: 'utf8',
|
|
64
|
+
timeout: timeoutMs(),
|
|
65
|
+
});
|
|
66
|
+
if (result.status !== 0 || result.error) {
|
|
67
|
+
return {
|
|
68
|
+
latestVersion: null,
|
|
69
|
+
updateAvailable: false,
|
|
70
|
+
error: trimDetail(result.stderr) || trimDetail(result.stdout) || result.error?.message || 'unknown npm registry check error',
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
const latestVersion = parseRegistryVersion(result.stdout);
|
|
74
|
+
return {
|
|
75
|
+
latestVersion,
|
|
76
|
+
updateAvailable: compareVersions(latestVersion, runtimeVersion()) > 0,
|
|
77
|
+
error: null,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export async function checkUpdateNotice(options = {}) {
|
|
82
|
+
if (!noticeEnabled()) return null;
|
|
83
|
+
const target = options.to || process.env.LLM_WIKI_KIT_UPDATE_NOTICE_TARGET || 'latest';
|
|
84
|
+
const installedVersion = runtimeVersion();
|
|
85
|
+
const cachePath = noticeCachePath();
|
|
86
|
+
const cached = await readJson(cachePath, null);
|
|
87
|
+
if (!options.force && cacheMatches(cached, target, installedVersion) && freshEnough(cached)) {
|
|
88
|
+
return cached;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const checkedAt = new Date().toISOString();
|
|
92
|
+
const registry = checkRegistry(target);
|
|
93
|
+
const record = {
|
|
94
|
+
schemaVersion: NOTICE_SCHEMA_VERSION,
|
|
95
|
+
checkedAt,
|
|
96
|
+
target,
|
|
97
|
+
installedVersion,
|
|
98
|
+
...registry,
|
|
99
|
+
};
|
|
100
|
+
await writeJson(cachePath, record).catch(() => {});
|
|
101
|
+
return record;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export async function updateNoticeContext(projectRoot, eventName, options = {}) {
|
|
105
|
+
if (!['SessionStart', 'InstructionsLoaded', 'UserPromptSubmit'].includes(eventName)) return '';
|
|
106
|
+
const notice = await checkUpdateNotice(options).catch(() => null);
|
|
107
|
+
if (!notice?.updateAvailable) return '';
|
|
108
|
+
|
|
109
|
+
const workspace = resolve(projectRoot || process.cwd());
|
|
110
|
+
const updateCommand = commandForProject('update', workspace);
|
|
111
|
+
return [
|
|
112
|
+
'LLM Wiki update notice:',
|
|
113
|
+
`- Installed llm-wiki-kit: ${notice.installedVersion}`,
|
|
114
|
+
`- Latest npm release: ${notice.latestVersion}`,
|
|
115
|
+
`- A newer llm-wiki-kit release was detected automatically while the user was using the hooks.`,
|
|
116
|
+
`- Briefly tell the user an update is available and offer to run: \`${updateCommand}\``,
|
|
117
|
+
'- If the user wants every known project root updated, use the broader search root in the same command, for example `--workspace /apps`.',
|
|
118
|
+
'- Do not install automatically unless the user asks for the update.',
|
|
119
|
+
].join('\n');
|
|
120
|
+
}
|