llm-wiki-kit 0.2.12 → 0.2.14
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 +1 -1
- package/docs/manual.md +4 -1
- package/docs/operations.md +3 -1
- package/docs/troubleshooting.md +2 -0
- package/package.json +1 -1
- package/src/cli.js +15 -2
- package/src/update.js +171 -4
package/README.md
CHANGED
|
@@ -133,7 +133,7 @@ Most users should not need these during daily Claude Code/Codex work. They exist
|
|
|
133
133
|
|
|
134
134
|
`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.
|
|
135
135
|
|
|
136
|
-
`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
|
|
136
|
+
`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. Before installing, it checks whether `npm root -g` points at the active runtime package root; after installing, it verifies the active runtime and `post-update` runtime reached the registry target. If either check fails, it exits nonzero and prints the exact runtime/npm roots plus the manual install/post-update commands to run. If the installed runtime already satisfies the registry target, it prints an already-current result, 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. The command prints step progress to stderr, including registry lookup, npm root/prefix checks, npm install, post-update, and project discovery. Use `--timeout-ms <ms>` to bound external commands and `--max-dirs <n>` to bound project discovery under large or slow roots such as WSL `/mnt/*` trees.
|
|
137
137
|
|
|
138
138
|
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, Codex and Claude receive the same passive runtime update status in hook context: current runtime, npm registry target, and the manual command to use when the user asks for update or maintenance work. It is not an instruction to interrupt the current answer or to sell the update to the user. The cache is scoped to the npm command used for lookup so test/fake npm checks do not leak into normal hook sessions. Set `LLM_WIKI_KIT_UPDATE_NOTICE=0` only when diagnosing or suppressing that status block.
|
|
139
139
|
|
package/docs/manual.md
CHANGED
|
@@ -163,10 +163,13 @@ Most users should not need these during daily coding. They are for install, upda
|
|
|
163
163
|
|
|
164
164
|
- runs `npm install -g llm-wiki-kit@<target>` only when needed;
|
|
165
165
|
- skips npm install when the installed runtime already satisfies the target;
|
|
166
|
-
-
|
|
166
|
+
- checks that `npm root -g` points at the active runtime package root before installing;
|
|
167
|
+
- verifies that the active runtime and `post-update` runtime reached the registry target after installing;
|
|
168
|
+
- exits nonzero instead of printing `updated` when `npm install -g` succeeds but the active runtime is still old;
|
|
167
169
|
- reinstalls hooks without duplicating them;
|
|
168
170
|
- updates only managed project templates and policy blocks;
|
|
169
171
|
- preserves user-edited files and curated wiki contents;
|
|
172
|
+
- prints before/after runtime, registry target, npm global root, active runtime root, and PATH command diagnostics;
|
|
170
173
|
- prints progress to stderr so JSON stdout remains parseable;
|
|
171
174
|
- supports `--timeout-ms` and `--max-dirs` for slow npm, WSL, or large search roots.
|
|
172
175
|
|
package/docs/operations.md
CHANGED
|
@@ -127,7 +127,9 @@ Installed npm runtimes also run a cached hook-side update notice check while the
|
|
|
127
127
|
|
|
128
128
|
- runs `npm install -g llm-wiki-kit@<target>` only when the registry target is newer than the installed runtime
|
|
129
129
|
- skips npm installation when the installed runtime already satisfies the target, then still runs post-update maintenance
|
|
130
|
-
-
|
|
130
|
+
- checks `npm root -g`/`npm prefix -g` before install and refuses to claim success when npm would update a different global prefix than the active runtime
|
|
131
|
+
- verifies that the active runtime and `post-update` child runtime reached the registry target after install
|
|
132
|
+
- prints before/after runtime, registry target, npm global root, active runtime root, and PATH command diagnostics
|
|
131
133
|
- reinstalls hook entries without duplicating them
|
|
132
134
|
- patches only managed project files across known or discovered project roots
|
|
133
135
|
- backs up changed files under `~/.local/share/llm-wiki-kit/backups/`
|
package/docs/troubleshooting.md
CHANGED
|
@@ -113,6 +113,8 @@ If the proxy performs TLS inspection, install the organization's CA and set `caf
|
|
|
113
113
|
|
|
114
114
|
If `npm install -g llm-wiki-kit@latest` succeeds but `llm-wiki --help` does not show newer commands such as `projects`, `context`, `lint`, or `consolidate`, the shell is probably executing an older source checkout or stale shim before the npm global binary.
|
|
115
115
|
|
|
116
|
+
Newer `llm-wiki update` releases diagnose this before claiming success. If the npm global root does not match the active runtime root, or if `npm install -g` exits 0 but the active runtime is still old, `update` exits nonzero with details such as `active runtime root`, `npm global root`, `runtime before`, `runtime after`, and `command matches runtime`. On root-owned Linux prefixes, run the printed `sudo npm install -g llm-wiki-kit@<target>` command, then run the printed `llm-wiki post-update ...` command as the normal Codex/Claude user.
|
|
117
|
+
|
|
116
118
|
Confirm what is actually running:
|
|
117
119
|
|
|
118
120
|
```bash
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -348,13 +348,26 @@ function formatUpdate(value) {
|
|
|
348
348
|
`- project template skipped: ${skipped}`,
|
|
349
349
|
].join('\n');
|
|
350
350
|
}
|
|
351
|
+
const title = value.updateApplied
|
|
352
|
+
? 'llm-wiki-kit updated'
|
|
353
|
+
: 'llm-wiki-kit already current; post-update complete';
|
|
351
354
|
return [
|
|
352
|
-
|
|
355
|
+
title,
|
|
353
356
|
`- workspace: ${value.workspace}`,
|
|
354
|
-
`- before: ${value.before}`,
|
|
357
|
+
`- runtime before: ${value.runtimeBefore || value.before}`,
|
|
358
|
+
`- registry target: ${value.latest || value.target}`,
|
|
359
|
+
`- runtime after: ${value.runtimeAfter || 'unknown'}`,
|
|
360
|
+
...(value.postUpdateRuntime ? [`- post-update runtime: ${value.postUpdateRuntime}`] : []),
|
|
361
|
+
`- runtime verified: ${value.runtimeVerified ? 'yes' : 'no'}`,
|
|
362
|
+
`- npm install: ${value.npmInstall?.skipped ? 'skipped' : 'ran'}`,
|
|
355
363
|
`- target: ${value.target}`,
|
|
364
|
+
`- active runtime root: ${value.activePackageRoot || 'unknown'}`,
|
|
365
|
+
`- npm global root: ${value.npmGlobal?.root || 'unknown'}`,
|
|
366
|
+
`- command: ${value.command?.path || 'not found'}`,
|
|
367
|
+
`- command matches runtime: ${value.command?.matchesRuntime ? 'yes' : 'no'}`,
|
|
356
368
|
`- scope: ${value.scope || 'current'}`,
|
|
357
369
|
...(value.projects ? [`- projects updated: ${value.projects.length}`] : []),
|
|
370
|
+
...((value.warnings || []).map((warning) => `- warning: ${warning}`)),
|
|
358
371
|
].join('\n');
|
|
359
372
|
}
|
|
360
373
|
|
package/src/update.js
CHANGED
|
@@ -3,10 +3,10 @@ import { join, resolve } from 'path';
|
|
|
3
3
|
import { exists } from './fs-utils.js';
|
|
4
4
|
import { appendWikiLog } from './project.js';
|
|
5
5
|
import { install } from './install.js';
|
|
6
|
-
import { isWindows } from './platform.js';
|
|
6
|
+
import { commandMatchesRuntime, commandPaths, isWindows, sameResolvedPath } from './platform.js';
|
|
7
7
|
import { applyProjectTemplateUpdate, inspectProjectState } from './project-state.js';
|
|
8
8
|
import { knownProjectRoots, recordProject } from './projects.js';
|
|
9
|
-
import { binPath, detectInstallSource, packageName, runtimeVersion } from './version.js';
|
|
9
|
+
import { binPath, detectInstallSource, packageName, packageRoot, runtimeVersion } from './version.js';
|
|
10
10
|
|
|
11
11
|
function commandLine(command, args) {
|
|
12
12
|
return [command, ...args].join(' ');
|
|
@@ -26,6 +26,7 @@ async function runCommand(command, args, options = {}) {
|
|
|
26
26
|
const label = options.label || commandLine(command, args);
|
|
27
27
|
const startedAt = Date.now();
|
|
28
28
|
const windows = isWindows(options);
|
|
29
|
+
const useShell = options.shell !== undefined ? options.shell : windows;
|
|
29
30
|
const detached = !windows;
|
|
30
31
|
let stdout = '';
|
|
31
32
|
let stderr = '';
|
|
@@ -69,7 +70,7 @@ async function runCommand(command, args, options = {}) {
|
|
|
69
70
|
child = spawn(command, args, {
|
|
70
71
|
detached,
|
|
71
72
|
env: options.env || process.env,
|
|
72
|
-
shell:
|
|
73
|
+
shell: useShell,
|
|
73
74
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
74
75
|
});
|
|
75
76
|
} catch (error) {
|
|
@@ -115,6 +116,10 @@ function binCommand() {
|
|
|
115
116
|
return process.env.LLM_WIKI_KIT_BIN || binPath;
|
|
116
117
|
}
|
|
117
118
|
|
|
119
|
+
function packageSpec(target) {
|
|
120
|
+
return `${packageName()}@${target}`;
|
|
121
|
+
}
|
|
122
|
+
|
|
118
123
|
function assertCommandOk(result, label) {
|
|
119
124
|
if (result.status === 0 && !result.error) return;
|
|
120
125
|
const detail = result.stderr.trim() ||
|
|
@@ -125,6 +130,45 @@ function assertCommandOk(result, label) {
|
|
|
125
130
|
throw new Error(`${label} failed: ${detail}`);
|
|
126
131
|
}
|
|
127
132
|
|
|
133
|
+
function postUpdateCommand(workspace, options = {}) {
|
|
134
|
+
const command = ['llm-wiki', 'post-update'];
|
|
135
|
+
if (shouldUpdateAllProjects(options)) command.push('--all');
|
|
136
|
+
command.push('--workspace', workspace);
|
|
137
|
+
return command.join(' ');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function formatRemediation(workspace, target, options = {}) {
|
|
141
|
+
const install = isWindows(options)
|
|
142
|
+
? `npm install -g ${packageSpec(target)}`
|
|
143
|
+
: `sudo npm install -g ${packageSpec(target)}`;
|
|
144
|
+
return [
|
|
145
|
+
'Run the npm install command that owns the active runtime, then reapply hooks/templates:',
|
|
146
|
+
` ${install}`,
|
|
147
|
+
` ${postUpdateCommand(workspace, options)}`,
|
|
148
|
+
'Then verify:',
|
|
149
|
+
` llm-wiki status --workspace ${workspace}`,
|
|
150
|
+
].join('\n');
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function createUpdateVerificationError(message, diagnostics = {}, workspace, target, options = {}) {
|
|
154
|
+
const lines = [
|
|
155
|
+
'llm-wiki-kit update did not update the active runtime.',
|
|
156
|
+
message,
|
|
157
|
+
];
|
|
158
|
+
if (diagnostics.registryTarget) lines.push(`- registry target: ${diagnostics.registryTarget}`);
|
|
159
|
+
if (diagnostics.runtimeBefore) lines.push(`- runtime before: ${diagnostics.runtimeBefore}`);
|
|
160
|
+
if (diagnostics.runtimeAfter) lines.push(`- runtime after: ${diagnostics.runtimeAfter}`);
|
|
161
|
+
if (diagnostics.activePackageRoot) lines.push(`- active runtime root: ${diagnostics.activePackageRoot}`);
|
|
162
|
+
if (diagnostics.npmRoot) lines.push(`- npm global root: ${diagnostics.npmRoot}`);
|
|
163
|
+
if (diagnostics.npmPackageRoot) lines.push(`- npm target package root: ${diagnostics.npmPackageRoot}`);
|
|
164
|
+
if (diagnostics.commandPath) lines.push(`- command on PATH: ${diagnostics.commandPath}`);
|
|
165
|
+
if (diagnostics.commandMatchesRuntime !== undefined) {
|
|
166
|
+
lines.push(`- command matches runtime: ${diagnostics.commandMatchesRuntime ? 'yes' : 'no'}`);
|
|
167
|
+
}
|
|
168
|
+
lines.push(formatRemediation(workspace, target, options));
|
|
169
|
+
return Object.assign(new Error(lines.join('\n')), { diagnostics });
|
|
170
|
+
}
|
|
171
|
+
|
|
128
172
|
async function hasProjectWiki(projectRoot) {
|
|
129
173
|
return exists(join(projectRoot, 'llm-wiki', 'wiki'));
|
|
130
174
|
}
|
|
@@ -190,6 +234,74 @@ export function compareVersions(a, b) {
|
|
|
190
234
|
return left.prerelease > right.prerelease ? 1 : -1;
|
|
191
235
|
}
|
|
192
236
|
|
|
237
|
+
async function npmGlobalInfo(options = {}) {
|
|
238
|
+
const rootResult = await runCommand(npmCommand(), ['root', '-g'], {
|
|
239
|
+
...options,
|
|
240
|
+
label: 'npm root -g',
|
|
241
|
+
timeout: options.timeout || 30000,
|
|
242
|
+
});
|
|
243
|
+
assertCommandOk(rootResult, 'npm root -g');
|
|
244
|
+
const prefixResult = await runCommand(npmCommand(), ['prefix', '-g'], {
|
|
245
|
+
...options,
|
|
246
|
+
label: 'npm prefix -g',
|
|
247
|
+
timeout: options.timeout || 30000,
|
|
248
|
+
});
|
|
249
|
+
assertCommandOk(prefixResult, 'npm prefix -g');
|
|
250
|
+
const root = rootResult.stdout.trim().split(/\r?\n/).at(-1) || '';
|
|
251
|
+
const prefix = prefixResult.stdout.trim().split(/\r?\n/).at(-1) || '';
|
|
252
|
+
return {
|
|
253
|
+
root,
|
|
254
|
+
prefix,
|
|
255
|
+
packageRoot: root ? join(root, packageName()) : '',
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
async function commandInfo(options = {}) {
|
|
260
|
+
const paths = await commandPaths('llm-wiki', options);
|
|
261
|
+
const commandPath = paths[0] || null;
|
|
262
|
+
const matchesRuntime = await commandMatchesRuntime(commandPath, binPath, {
|
|
263
|
+
...options,
|
|
264
|
+
installSource: detectInstallSource(),
|
|
265
|
+
});
|
|
266
|
+
return {
|
|
267
|
+
path: commandPath,
|
|
268
|
+
paths,
|
|
269
|
+
matchesRuntime: Boolean(matchesRuntime),
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function runtimeSatisfiesTarget(runtime, targetVersion) {
|
|
274
|
+
return compareVersions(runtime, targetVersion) >= 0;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function rootMatchesActiveRuntime(npmPackageRoot) {
|
|
278
|
+
return sameResolvedPath(npmPackageRoot, packageRoot) ||
|
|
279
|
+
resolve(npmPackageRoot || '') === resolve(packageRoot);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
async function preflightRuntimeUpdate(check, workspace, target, options = {}) {
|
|
283
|
+
const npmGlobal = await npmGlobalInfo(options);
|
|
284
|
+
const command = await commandInfo(options);
|
|
285
|
+
if (check.updateAvailable && detectInstallSource() === 'npm' && !rootMatchesActiveRuntime(npmGlobal.packageRoot)) {
|
|
286
|
+
throw createUpdateVerificationError(
|
|
287
|
+
'The active llm-wiki runtime is not under the npm global root that this update command would modify.',
|
|
288
|
+
{
|
|
289
|
+
registryTarget: check.latestVersion,
|
|
290
|
+
runtimeBefore: check.installedVersion,
|
|
291
|
+
activePackageRoot: packageRoot,
|
|
292
|
+
npmRoot: npmGlobal.root,
|
|
293
|
+
npmPackageRoot: npmGlobal.packageRoot,
|
|
294
|
+
commandPath: command.path,
|
|
295
|
+
commandMatchesRuntime: command.matchesRuntime,
|
|
296
|
+
},
|
|
297
|
+
workspace,
|
|
298
|
+
target,
|
|
299
|
+
options
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
return { npmGlobal, command };
|
|
303
|
+
}
|
|
304
|
+
|
|
193
305
|
export async function checkForUpdate(options = {}) {
|
|
194
306
|
const target = options.to || 'latest';
|
|
195
307
|
const installedVersion = runtimeVersion();
|
|
@@ -297,9 +409,10 @@ export async function update(options = {}) {
|
|
|
297
409
|
}
|
|
298
410
|
|
|
299
411
|
const target = options.to || 'latest';
|
|
412
|
+
const beforeDiagnostics = await preflightRuntimeUpdate(check, workspace, target, options);
|
|
300
413
|
const shouldRunNpmInstall = check.updateAvailable;
|
|
301
414
|
const installResult = shouldRunNpmInstall
|
|
302
|
-
? await runCommand(npmCommand(), ['install', '-g',
|
|
415
|
+
? await runCommand(npmCommand(), ['install', '-g', packageSpec(target)], {
|
|
303
416
|
...options,
|
|
304
417
|
label: 'npm install -g',
|
|
305
418
|
timeout: options.timeout || 120000,
|
|
@@ -310,6 +423,25 @@ export async function update(options = {}) {
|
|
|
310
423
|
stderr: '',
|
|
311
424
|
};
|
|
312
425
|
assertCommandOk(installResult, 'npm install -g');
|
|
426
|
+
const runtimeAfterInstall = runtimeVersion();
|
|
427
|
+
if (shouldRunNpmInstall && !runtimeSatisfiesTarget(runtimeAfterInstall, check.latestVersion)) {
|
|
428
|
+
throw createUpdateVerificationError(
|
|
429
|
+
'npm install -g exited successfully, but the active runtime version did not change to the registry target.',
|
|
430
|
+
{
|
|
431
|
+
registryTarget: check.latestVersion,
|
|
432
|
+
runtimeBefore: check.installedVersion,
|
|
433
|
+
runtimeAfter: runtimeAfterInstall,
|
|
434
|
+
activePackageRoot: packageRoot,
|
|
435
|
+
npmRoot: beforeDiagnostics.npmGlobal.root,
|
|
436
|
+
npmPackageRoot: beforeDiagnostics.npmGlobal.packageRoot,
|
|
437
|
+
commandPath: beforeDiagnostics.command.path,
|
|
438
|
+
commandMatchesRuntime: beforeDiagnostics.command.matchesRuntime,
|
|
439
|
+
},
|
|
440
|
+
workspace,
|
|
441
|
+
target,
|
|
442
|
+
options
|
|
443
|
+
);
|
|
444
|
+
}
|
|
313
445
|
|
|
314
446
|
const postArgs = [binCommand(), 'post-update', '--workspace', workspace, '--json'];
|
|
315
447
|
if (shouldUpdateAllProjects(options)) postArgs.push('--all');
|
|
@@ -324,17 +456,52 @@ export async function update(options = {}) {
|
|
|
324
456
|
LLM_WIKI_KIT_PROGRESS: process.env.LLM_WIKI_KIT_PROGRESS || '1',
|
|
325
457
|
},
|
|
326
458
|
label: 'post-update',
|
|
459
|
+
shell: false,
|
|
327
460
|
timeout: options.timeout || 120000,
|
|
328
461
|
});
|
|
329
462
|
assertCommandOk(postResult, 'post-update');
|
|
330
463
|
const parsedPostUpdate = parseJsonOutput(postResult.stdout);
|
|
464
|
+
const postRuntimeVersion = parsedPostUpdate?.runtimeVersion || null;
|
|
465
|
+
if (shouldRunNpmInstall && postRuntimeVersion && !runtimeSatisfiesTarget(postRuntimeVersion, check.latestVersion)) {
|
|
466
|
+
throw createUpdateVerificationError(
|
|
467
|
+
'post-update ran, but it did not run from the registry target runtime.',
|
|
468
|
+
{
|
|
469
|
+
registryTarget: check.latestVersion,
|
|
470
|
+
runtimeBefore: check.installedVersion,
|
|
471
|
+
runtimeAfter: postRuntimeVersion,
|
|
472
|
+
activePackageRoot: packageRoot,
|
|
473
|
+
npmRoot: beforeDiagnostics.npmGlobal.root,
|
|
474
|
+
npmPackageRoot: beforeDiagnostics.npmGlobal.packageRoot,
|
|
475
|
+
commandPath: beforeDiagnostics.command.path,
|
|
476
|
+
commandMatchesRuntime: beforeDiagnostics.command.matchesRuntime,
|
|
477
|
+
},
|
|
478
|
+
workspace,
|
|
479
|
+
target,
|
|
480
|
+
options
|
|
481
|
+
);
|
|
482
|
+
}
|
|
483
|
+
const afterCommand = await commandInfo(options);
|
|
484
|
+
const warnings = [];
|
|
485
|
+
if (!afterCommand.matchesRuntime) {
|
|
486
|
+
warnings.push(`llm-wiki command on PATH does not resolve to the active runtime: ${afterCommand.path || 'not found'}`);
|
|
487
|
+
}
|
|
331
488
|
|
|
332
489
|
return {
|
|
333
490
|
mode: 'update',
|
|
334
491
|
workspace,
|
|
335
492
|
before: check.installedVersion,
|
|
336
493
|
target,
|
|
494
|
+
latest: check.latestVersion,
|
|
495
|
+
runtimeBefore: check.installedVersion,
|
|
496
|
+
runtimeAfter: runtimeAfterInstall,
|
|
497
|
+
postUpdateRuntime: postRuntimeVersion,
|
|
498
|
+
updateApplied: shouldRunNpmInstall,
|
|
499
|
+
runtimeVerified: runtimeSatisfiesTarget(runtimeAfterInstall, check.latestVersion),
|
|
337
500
|
scope: shouldUpdateAllProjects(options) ? 'all' : 'current',
|
|
501
|
+
activePackageRoot: packageRoot,
|
|
502
|
+
npmGlobal: beforeDiagnostics.npmGlobal,
|
|
503
|
+
command: afterCommand,
|
|
504
|
+
warnings,
|
|
338
505
|
npmInstall: {
|
|
339
506
|
skipped: !shouldRunNpmInstall,
|
|
340
507
|
stdout: installResult.stdout.trim(),
|