diffwiki 0.8.0 → 0.9.0-rc.202607291532.69c042c
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/dist/bin.js +461 -200
- package/package.json +2 -2
package/dist/bin.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
|
+
import { spawn as spawn4 } from "child_process";
|
|
4
5
|
import { Command } from "commander";
|
|
5
6
|
import {
|
|
6
7
|
addExternalCollection,
|
|
@@ -23,6 +24,8 @@ import {
|
|
|
23
24
|
removePlugin,
|
|
24
25
|
removeTags,
|
|
25
26
|
resolveCwdCollection,
|
|
27
|
+
resolveEditorCommand,
|
|
28
|
+
resolveEditTarget,
|
|
26
29
|
setConfigValue,
|
|
27
30
|
setTags,
|
|
28
31
|
updateArticleBody
|
|
@@ -54,6 +57,9 @@ function withErrors(fn) {
|
|
|
54
57
|
}
|
|
55
58
|
var collect = (value, previous) => previous.concat([value]);
|
|
56
59
|
var splitTags = (raw) => raw.flatMap((t) => t.split(",")).map((t) => t.trim()).filter((t) => t.length > 0);
|
|
60
|
+
var deprecated = (oldForm, newForm) => {
|
|
61
|
+
console.error(`\u26A0 "diffwiki ${oldForm}" is deprecated; use "diffwiki ${newForm}".`);
|
|
62
|
+
};
|
|
57
63
|
|
|
58
64
|
// src/ui-server.ts
|
|
59
65
|
import { spawn } from "child_process";
|
|
@@ -171,7 +177,7 @@ async function runExport(opts) {
|
|
|
171
177
|
const out = path2.resolve(opts.out);
|
|
172
178
|
const filter = splitGlobs(opts.filter);
|
|
173
179
|
const exclude = splitGlobs(opts.exclude);
|
|
174
|
-
if (filter.length > 0 || exclude.length > 0) {
|
|
180
|
+
if (!opts.project && (filter.length > 0 || exclude.length > 0)) {
|
|
175
181
|
const names = (await listCollections()).map((c) => c.name);
|
|
176
182
|
const { errors } = resolveCollectionSelection(names, { filter, exclude });
|
|
177
183
|
if (errors.length > 0) throw new InvalidExportSelectionError(errors);
|
|
@@ -182,6 +188,10 @@ async function runExport(opts) {
|
|
|
182
188
|
const args = [...prefix, "export", "--out", out, "--base", opts.base];
|
|
183
189
|
if (opts.filter) args.push("--filter", opts.filter);
|
|
184
190
|
if (opts.exclude) args.push("--exclude", opts.exclude);
|
|
191
|
+
if (opts.project) {
|
|
192
|
+
args.push("--project");
|
|
193
|
+
if (typeof opts.project === "string") args.push(opts.project);
|
|
194
|
+
}
|
|
185
195
|
if (override) console.log(`Exporting via ${override}`);
|
|
186
196
|
await new Promise((resolve, reject) => {
|
|
187
197
|
const child = spawn2(cmd, args, { stdio: "inherit", env: process.env });
|
|
@@ -191,261 +201,512 @@ async function runExport(opts) {
|
|
|
191
201
|
console.log(`Static site written to ${out}`);
|
|
192
202
|
}
|
|
193
203
|
|
|
204
|
+
// src/preview.ts
|
|
205
|
+
import { existsSync } from "fs";
|
|
206
|
+
import fs3 from "fs/promises";
|
|
207
|
+
import net from "net";
|
|
208
|
+
import path3 from "path";
|
|
209
|
+
import { spawn as spawn3 } from "child_process";
|
|
210
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
211
|
+
import { seedProjectHome } from "diffwiki-core";
|
|
212
|
+
async function findFreePort(start, host = "127.0.0.1") {
|
|
213
|
+
const isFree = (port) => new Promise((resolve) => {
|
|
214
|
+
const srv = net.createServer();
|
|
215
|
+
srv.once("error", () => resolve(false));
|
|
216
|
+
srv.once("listening", () => srv.close(() => resolve(true)));
|
|
217
|
+
srv.listen(port, host);
|
|
218
|
+
});
|
|
219
|
+
for (let port = start; port < start + 100; port += 1) {
|
|
220
|
+
if (await isFree(port)) return port;
|
|
221
|
+
}
|
|
222
|
+
throw new Error(`No free port available in ${start}..${start + 99}`);
|
|
223
|
+
}
|
|
224
|
+
function launchCommand() {
|
|
225
|
+
const override = process.env.DIFFWIKI_APP_CMD;
|
|
226
|
+
if (override) {
|
|
227
|
+
const [cmd, ...prefix] = override.split(" ");
|
|
228
|
+
return { cmd, prefix };
|
|
229
|
+
}
|
|
230
|
+
const candidates = [
|
|
231
|
+
path3.resolve(path3.dirname(fileURLToPath3(import.meta.url)), "../../../apps/wiki/bin/wiki-server.mjs"),
|
|
232
|
+
path3.resolve(process.cwd(), "apps/wiki/bin/wiki-server.mjs")
|
|
233
|
+
];
|
|
234
|
+
const local = candidates.find((p) => existsSync(p));
|
|
235
|
+
return local ? { cmd: "node", prefix: [local] } : { cmd: "npx", prefix: ["-y", "diffwiki-app@latest"] };
|
|
236
|
+
}
|
|
237
|
+
async function runPreview(opts) {
|
|
238
|
+
const root = path3.resolve(opts.cwd);
|
|
239
|
+
const { home, entries } = await seedProjectHome(root);
|
|
240
|
+
const { cmd, prefix } = launchCommand();
|
|
241
|
+
const port = await findFreePort(opts.port);
|
|
242
|
+
const url = `http://127.0.0.1:${port}`;
|
|
243
|
+
console.log(`Previewing ${entries.length} collection(s) from ${path3.join(root, "diffwiki.yaml")}`);
|
|
244
|
+
if (port !== opts.port) console.log(`Port ${opts.port} is in use \u2014 using ${port} instead.`);
|
|
245
|
+
console.log(`Starting UI at ${url} \u2026 (Ctrl-C to stop)`);
|
|
246
|
+
const child = spawn3(cmd, [...prefix, "--port", String(port)], {
|
|
247
|
+
env: {
|
|
248
|
+
...process.env,
|
|
249
|
+
DIFFWIKI_HOME: home,
|
|
250
|
+
DIFFWIKI_PROJECT_DIR: root,
|
|
251
|
+
PORT: String(port),
|
|
252
|
+
HOST: "127.0.0.1"
|
|
253
|
+
},
|
|
254
|
+
stdio: "inherit",
|
|
255
|
+
// Own process group so a terminal Ctrl-C doesn't double-signal the child (which
|
|
256
|
+
// triggers its slow graceful-shutdown countdown). We drive shutdown ourselves.
|
|
257
|
+
detached: true
|
|
258
|
+
});
|
|
259
|
+
const signalTree = (sig) => {
|
|
260
|
+
if (child.pid === void 0) return;
|
|
261
|
+
try {
|
|
262
|
+
process.kill(-child.pid, sig);
|
|
263
|
+
} catch {
|
|
264
|
+
try {
|
|
265
|
+
child.kill(sig);
|
|
266
|
+
} catch {
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
let forcing = false;
|
|
271
|
+
const stop = () => {
|
|
272
|
+
if (forcing) return signalTree("SIGKILL");
|
|
273
|
+
forcing = true;
|
|
274
|
+
signalTree("SIGTERM");
|
|
275
|
+
setTimeout(() => signalTree("SIGKILL"), 1e3).unref();
|
|
276
|
+
};
|
|
277
|
+
process.on("SIGINT", stop);
|
|
278
|
+
process.on("SIGTERM", stop);
|
|
279
|
+
const ready = await waitForServer(url);
|
|
280
|
+
if (ready) openBrowser(url);
|
|
281
|
+
await new Promise((resolve) => {
|
|
282
|
+
const finish = () => {
|
|
283
|
+
process.off("SIGINT", stop);
|
|
284
|
+
process.off("SIGTERM", stop);
|
|
285
|
+
void fs3.rm(home, { recursive: true, force: true });
|
|
286
|
+
resolve();
|
|
287
|
+
};
|
|
288
|
+
child.on("close", finish);
|
|
289
|
+
child.on("error", (err) => {
|
|
290
|
+
console.error(`Failed to start preview: ${err.message}`);
|
|
291
|
+
finish();
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
|
|
194
296
|
// src/cli.ts
|
|
195
297
|
var icon = (level) => level === "ok" ? "\u2713" : level === "warn" ? "!" : "\u2716";
|
|
298
|
+
async function listCollectionsAction() {
|
|
299
|
+
const collections = await listCollections2();
|
|
300
|
+
if (collections.length === 0) {
|
|
301
|
+
console.log("No collections yet. Create one with: diffwiki collection create <name>");
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
for (const c of collections) {
|
|
305
|
+
const status = await collectionGitStatus(c);
|
|
306
|
+
const branch = status?.branch ? ` (${status.branch})` : "";
|
|
307
|
+
console.log(`${c.name} [${c.type}] ${c.path}${branch}`);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
async function addExternalAction(opts) {
|
|
311
|
+
const entry = await addExternalCollection({ cwd: process.cwd(), docsDir: opts.docs });
|
|
312
|
+
const status = await collectionGitStatus(entry);
|
|
313
|
+
const branch = status?.branch ? ` (${status.branch})` : "";
|
|
314
|
+
console.log(`Added "${entry.name}"${branch} \u2192 ${entry.path}`);
|
|
315
|
+
console.log("Registered globally \u2014 discoverable via: diffwiki collection list");
|
|
316
|
+
}
|
|
317
|
+
async function createCollectionAction(name) {
|
|
318
|
+
const entry = await createCollection(name);
|
|
319
|
+
console.log(`Created collection "${entry.name}" at ${entry.path}`);
|
|
320
|
+
}
|
|
321
|
+
async function createArticleAction(target, body, tags) {
|
|
322
|
+
const article = await createArticle({
|
|
323
|
+
target,
|
|
324
|
+
tags: splitTags(tags),
|
|
325
|
+
body
|
|
326
|
+
});
|
|
327
|
+
console.log(article.path);
|
|
328
|
+
}
|
|
329
|
+
async function updateArticleAction(target, body) {
|
|
330
|
+
const article = await updateArticleBody(target, body);
|
|
331
|
+
console.log(`Updated ${article.path}`);
|
|
332
|
+
}
|
|
333
|
+
async function removeArticleAction(target) {
|
|
334
|
+
const removed = await removeArticle(target);
|
|
335
|
+
console.log(`Removed ${removed}`);
|
|
336
|
+
}
|
|
337
|
+
async function tagsAction(fn, target, tags) {
|
|
338
|
+
const article = await fn(target, splitTags([tags]));
|
|
339
|
+
console.log(`${article.path} \u2192 tags: ${article.tags.join(", ") || "(none)"}`);
|
|
340
|
+
}
|
|
341
|
+
async function indexAction(opts) {
|
|
342
|
+
const provider = await loadSearchProvider();
|
|
343
|
+
if (!provider) {
|
|
344
|
+
const installable = (await listSearchModes()).filter((m) => m.plugin !== "native");
|
|
345
|
+
console.log(
|
|
346
|
+
installable.length > 0 ? `No index-backed plugin active. Available modes: ${installable.map((m) => `${m.plugin}\xB7${m.type}`).join(", ")}` : "No search plugin installed \u2014 using native search (nothing to index). Install one with: diffwiki plugin install <spec>"
|
|
347
|
+
);
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
const indexed = await provider.reindex(await listCollections2(), { embed: !!opts.embed });
|
|
351
|
+
console.log(`Indexed ${indexed} document(s).`);
|
|
352
|
+
}
|
|
353
|
+
async function queryAction(term, opts) {
|
|
354
|
+
const queryOpts = { engine: opts.engine, collection: opts.collection, type: opts.type };
|
|
355
|
+
const outcome = await search(term, queryOpts);
|
|
356
|
+
const hits = outcome.hits;
|
|
357
|
+
console.log(`${hits.length} result(s)`);
|
|
358
|
+
for (const hit of hits) {
|
|
359
|
+
const score = typeof hit.score === "number" ? hit.score.toFixed(3) : "-";
|
|
360
|
+
const line = `${score} ${hit.collection} ${hit.title} ${hit.path}`;
|
|
361
|
+
console.log(hit.snippet ? `${line} ${hit.snippet}` : line);
|
|
362
|
+
}
|
|
363
|
+
if (outcome.error) {
|
|
364
|
+
console.error(`\u26A0 engine "${outcome.engine}" failed: ${outcome.error} \u2014 used native (${hits.length} results)`);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
async function pluginInstallAction(spec, opts) {
|
|
368
|
+
const { record, setup, defaulted } = await installPlugin(spec, {
|
|
369
|
+
setup: opts.setup,
|
|
370
|
+
setDefault: opts.default,
|
|
371
|
+
prefetchModels: opts.prefetchModels
|
|
372
|
+
});
|
|
373
|
+
console.log(`Installed "${record.name}"${record.version ? ` v${record.version}` : ""}`);
|
|
374
|
+
if (!opts.setup) {
|
|
375
|
+
console.log("Skipped setup (--no-setup) \u2014 run: diffwiki search index");
|
|
376
|
+
} else if (setup && !setup.ready) {
|
|
377
|
+
const reason = setup.steps.find((s) => !s.ok)?.message ?? "setup did not complete";
|
|
378
|
+
console.log(`\u26A0 Not ready: ${reason}`);
|
|
379
|
+
console.log(" \u2192 run: diffwiki doctor");
|
|
380
|
+
}
|
|
381
|
+
if (defaulted) {
|
|
382
|
+
console.log(
|
|
383
|
+
`Set "${record.name}" as the default search engine (change: diffwiki config set defaultSearch <engine[:type]>)`
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
async function pluginRegisterAction(name, opts) {
|
|
388
|
+
const record = await registerPlugin(name, opts.command);
|
|
389
|
+
console.log(`Registered "${record.name}" \u2192 ${record.command.join(" ")}`);
|
|
390
|
+
}
|
|
391
|
+
async function pluginListAction() {
|
|
392
|
+
const installed = await listPlugins();
|
|
393
|
+
const installedNames = new Set(installed.map((p) => p.name));
|
|
394
|
+
console.log("Installed:");
|
|
395
|
+
if (installed.length === 0) {
|
|
396
|
+
console.log(" (none)");
|
|
397
|
+
} else {
|
|
398
|
+
for (const p of installed) {
|
|
399
|
+
console.log(
|
|
400
|
+
` ${p.name} ${p.kind} ${p.version ?? "-"} enabled=${p.enabled} managed=${!!p.managed} spawnable=${p.spawnable}`
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
const available = listAvailablePlugins().filter((k) => !installedNames.has(k.name));
|
|
405
|
+
if (available.length > 0) {
|
|
406
|
+
console.log("\nAvailable (install with: diffwiki plugin install <name>):");
|
|
407
|
+
for (const k of available) {
|
|
408
|
+
console.log(` ${k.name} [${k.kind}] ${k.description}`);
|
|
409
|
+
console.log(` requires ${k.requires ?? "nothing"}`);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
async function pluginRemoveAction(name) {
|
|
414
|
+
await removePlugin(name);
|
|
415
|
+
console.log(`Removed "${name}"`);
|
|
416
|
+
}
|
|
417
|
+
async function initAction(opts) {
|
|
418
|
+
const entry = await initRepoWiki({
|
|
419
|
+
cwd: process.cwd(),
|
|
420
|
+
collection: opts.collection,
|
|
421
|
+
wikiPath: opts.path
|
|
422
|
+
});
|
|
423
|
+
console.log(`Initialized repo wiki "${entry.name}" at ${entry.path}`);
|
|
424
|
+
console.log("Registered globally \u2014 discoverable via: diffwiki collection list");
|
|
425
|
+
}
|
|
426
|
+
async function exportAction(opts) {
|
|
427
|
+
await runExport({
|
|
428
|
+
out: opts.out,
|
|
429
|
+
base: opts.base,
|
|
430
|
+
filter: opts.filter,
|
|
431
|
+
exclude: opts.exclude,
|
|
432
|
+
project: opts.project
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
async function configSetAction(key, value) {
|
|
436
|
+
await setConfigValue(key, value);
|
|
437
|
+
console.log(`Set ${key} = ${value}`);
|
|
438
|
+
}
|
|
439
|
+
async function configGetAction(key) {
|
|
440
|
+
const value = await getConfigValue(key);
|
|
441
|
+
console.log(value ?? "(unset)");
|
|
442
|
+
}
|
|
443
|
+
async function doctorAction() {
|
|
444
|
+
const diagnostics = await doctor();
|
|
445
|
+
for (const d of diagnostics) {
|
|
446
|
+
console.log(`${icon(d.level)} ${d.message}`);
|
|
447
|
+
}
|
|
448
|
+
if (diagnostics.some((d) => d.level === "error")) {
|
|
449
|
+
process.exitCode = 1;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
async function upAction(opts) {
|
|
453
|
+
const existing = await readState();
|
|
454
|
+
if (existing && isAlive(existing.pid)) {
|
|
455
|
+
console.log(`UI already running at ${existing.url} (pid ${existing.pid})`);
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
const state = await startUi(Number(opts.port));
|
|
459
|
+
const ok = await waitForServer(state.url);
|
|
460
|
+
console.log(
|
|
461
|
+
ok ? `UI started at ${state.url} (pid ${state.pid})` : `Launched (pid ${state.pid}) but ${state.url} did not respond yet \u2014 check \`diffwiki ui status\``
|
|
462
|
+
);
|
|
463
|
+
}
|
|
464
|
+
async function downAction() {
|
|
465
|
+
const stopped = await stopUi();
|
|
466
|
+
console.log(stopped ? `Stopped UI (pid ${stopped.pid})` : "UI is not running.");
|
|
467
|
+
}
|
|
468
|
+
async function statusAction() {
|
|
469
|
+
const state = await readState();
|
|
470
|
+
if (state && isAlive(state.pid)) {
|
|
471
|
+
console.log(`running \xB7 ${state.url} \xB7 pid ${state.pid} \xB7 since ${state.startedAt}`);
|
|
472
|
+
} else {
|
|
473
|
+
console.log("stopped");
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
async function openAction(opts) {
|
|
477
|
+
let state = await readState();
|
|
478
|
+
if (!state || !isAlive(state.pid)) {
|
|
479
|
+
state = await startUi(Number(opts.port));
|
|
480
|
+
await waitForServer(state.url);
|
|
481
|
+
}
|
|
482
|
+
const coll = await resolveCwdCollection(process.cwd());
|
|
483
|
+
const target = coll ? `${state.url}/${encodeURIComponent(coll.name)}` : state.url;
|
|
484
|
+
openBrowser(target);
|
|
485
|
+
console.log(`Opening ${target}`);
|
|
486
|
+
}
|
|
487
|
+
async function editAction(target, editorFlag) {
|
|
488
|
+
const argv = await resolveEditorCommand(editorFlag);
|
|
489
|
+
if (!argv) {
|
|
490
|
+
console.error(
|
|
491
|
+
'No editor set. Configure one: diffwiki config set editor "<cmd>" \u2014 or set $EDITOR, or pass --editor.'
|
|
492
|
+
);
|
|
493
|
+
process.exitCode = 1;
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
const { path: dest, kind } = await resolveEditTarget(target, process.cwd());
|
|
497
|
+
const [cmd, ...args] = argv;
|
|
498
|
+
console.log(`Opening ${kind} ${dest} with ${argv.join(" ")}`);
|
|
499
|
+
const code = await new Promise((resolve) => {
|
|
500
|
+
const child = spawn4(cmd, [...args, dest], { stdio: "inherit" });
|
|
501
|
+
child.on("error", (err) => {
|
|
502
|
+
console.error(`Could not launch editor "${cmd}": ${err.message}`);
|
|
503
|
+
resolve(127);
|
|
504
|
+
});
|
|
505
|
+
child.on("exit", (c) => resolve(c ?? 0));
|
|
506
|
+
});
|
|
507
|
+
if (code !== 0) process.exitCode = code;
|
|
508
|
+
}
|
|
509
|
+
async function previewAction(opts) {
|
|
510
|
+
await runPreview({ cwd: process.cwd(), port: Number(opts.port) });
|
|
511
|
+
}
|
|
196
512
|
function createProgram() {
|
|
197
513
|
const program = new Command();
|
|
198
514
|
program.name("diffwiki").description("A git-based knowledge base").version(version).showHelpAfterError().option("-v, --verbose", "verbose logging (sets the log level to debug)").option("--log <level>", "log level: trace|debug|info|warning|error|fatal");
|
|
199
515
|
program.action(() => {
|
|
200
516
|
program.outputHelp();
|
|
201
517
|
});
|
|
202
|
-
program.command("
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
518
|
+
const collection = program.command("collection").description("Manage collections (containers)");
|
|
519
|
+
collection.command("list").description("List all collections").action(withErrors(listCollectionsAction));
|
|
520
|
+
collection.command("create").description("Create a new global collection").argument("<name>", "collection name").action(withErrors((name) => createCollectionAction(name)));
|
|
521
|
+
collection.command("add").description("Add the current git repo as an external collection (docs dir, no diffwiki.yaml)").option("-d, --docs <dir>", "content dir relative to repo root", "docs").action(withErrors((opts) => addExternalAction(opts)));
|
|
522
|
+
collection.command("init").description("Initialize an in-repo wiki and register it globally").option("-c, --collection <name>", "collection name (default: repo name)").option("-p, --path <path>", "wiki path within the repo (default: wiki)").action(withErrors((opts) => initAction(opts)));
|
|
523
|
+
collection.command("edit").description("Open a collection in your editor \u2014 GUI not required").argument("[name]", "collection (default: the current repo collection)").option("--editor <cmd>", "editor command (overrides config `editor`, $VISUAL, $EDITOR)").action(withErrors((name, opts) => editAction(name, opts.editor)));
|
|
524
|
+
const article = program.command("article").description("Manage articles (documents)");
|
|
525
|
+
article.command("create").description("Add an article to a collection").argument("<target>", '"[collection:]title"').argument("[body]", "markdown body").option("-t, --tag <tag>", "tag (repeatable, comma-separated)", collect, []).action(
|
|
526
|
+
withErrors(
|
|
527
|
+
(target, body, opts) => createArticleAction(target, body, opts.tag)
|
|
528
|
+
)
|
|
529
|
+
);
|
|
530
|
+
article.command("update").description("Replace an article's body").argument("<target>", '"collection:slug"').argument("<body>", "new markdown body").action(withErrors((target, body) => updateArticleAction(target, body)));
|
|
531
|
+
article.command("remove").description("Delete an article").argument("<target>", '"collection:slug"').action(withErrors((target) => removeArticleAction(target)));
|
|
532
|
+
article.command("edit").description("Open an article in your editor \u2014 GUI not required").argument("<target>", '"collection:slug"').option("--editor <cmd>", "editor command (overrides config `editor`, $VISUAL, $EDITOR)").action(withErrors((target, opts) => editAction(target, opts.editor)));
|
|
533
|
+
const tags = program.command("tags").description("Manage an article's tags");
|
|
534
|
+
const registerTagsSub = (name, description, fn) => {
|
|
535
|
+
tags.command(name).description(description).argument("<target>", '"collection:slug"').argument("<tags>", "comma-separated tags").action(withErrors((target, tagList) => tagsAction(fn, target, tagList)));
|
|
536
|
+
};
|
|
537
|
+
registerTagsSub("add", "Add to an article's tags", addTags);
|
|
538
|
+
registerTagsSub("remove", "Remove from an article's tags", removeTags);
|
|
539
|
+
registerTagsSub("set", "Replace an article's tags", setTags);
|
|
540
|
+
const searchCmd = program.command("search").description("Search articles and manage the index");
|
|
541
|
+
searchCmd.command("query").alias("q").description("Search articles via the resolved search engine (native fallback if none)").option("--engine <name>", "search engine/plugin (default: config defaultSearch, else native)").option("-c, --collection <name>", "limit to a collection").option("-t, --type <type>", "search type (engine-defined, e.g. keyword|semantic|hybrid)").argument("[term]", "search term", "").action(
|
|
542
|
+
withErrors(
|
|
543
|
+
(term, opts) => queryAction(term, opts)
|
|
544
|
+
)
|
|
545
|
+
);
|
|
546
|
+
searchCmd.command("index").description("Rebuild the search index via the active plugin").option("--embed", "also build vector embeddings (may download models on first run)").action(withErrors((opts) => indexAction(opts)));
|
|
547
|
+
const plugin = program.command("plugin").description("Manage search plugins");
|
|
548
|
+
plugin.command("install").description("Install a search plugin from an npm spec and provision it (setup)").argument("<spec>", "npm package spec (name, name@version, or path)").option("--no-setup", "skip the end-to-end setup after install").option("--no-default", "do not make this the default search engine").option("--prefetch-models", "pre-download models during setup (heavy)").action(
|
|
549
|
+
withErrors(
|
|
550
|
+
(spec, opts) => pluginInstallAction(spec, opts)
|
|
551
|
+
)
|
|
552
|
+
);
|
|
553
|
+
plugin.command("register").description("Register an existing executable as a search plugin").argument("<name>", "plugin name").requiredOption("--command <cmd...>", "command argv to spawn the plugin (e.g. node /abs/adapter.js)").action(withErrors((name, opts) => pluginRegisterAction(name, opts)));
|
|
554
|
+
plugin.command("list").description("List installed and available search plugins").action(withErrors(pluginListAction));
|
|
555
|
+
plugin.command("remove").description("Remove a search plugin").argument("<name>", "plugin name").action(withErrors((name) => pluginRemoveAction(name)));
|
|
556
|
+
const site = program.command("site").description("Build and preview the published site");
|
|
557
|
+
site.command("export").description("Generate a static site (deployable to GitHub Pages)").requiredOption("-o, --out <dir>", "output directory").option("-b, --base <path>", "base URL path (e.g. /my-repo/ for GitHub Pages)", "/").option("-f, --filter <globs>", "only export collections matching these globs (comma-separated)").option("-e, --exclude <globs>", "exclude collections matching these globs (comma-separated)").option("--project [dir]", "build only the repo project from its diffwiki.yaml (default: cwd)").action(
|
|
558
|
+
withErrors(
|
|
559
|
+
(opts) => exportAction(opts)
|
|
560
|
+
)
|
|
561
|
+
);
|
|
562
|
+
site.command("preview").description("Preview the site from the repo diffwiki.yaml (ignores global collections)").option("-p, --port <port>", "port to serve on", "4321").action(withErrors((opts) => previewAction(opts)));
|
|
563
|
+
const ui = program.command("ui").description("Run the local diffwiki app server");
|
|
564
|
+
ui.command("up").description("Start the diffwiki UI (latest published diffwiki-app)").option("-p, --port <port>", "port to serve on", "4321").action(withErrors((opts) => upAction(opts)));
|
|
565
|
+
ui.command("down").description("Stop the running diffwiki UI").action(withErrors(downAction));
|
|
566
|
+
ui.command("status").description("Show whether the diffwiki UI is running").action(withErrors(statusAction));
|
|
567
|
+
ui.command("open").description("Open the diffwiki UI in the browser (starting it if needed)").option("-p, --port <port>", "port to serve on", "4321").action(withErrors((opts) => openAction(opts)));
|
|
568
|
+
const config = program.command("config").description("Read and write diffwiki config");
|
|
569
|
+
config.command("set").description("Set a config value (e.g. defaultCollection, or defaultSearch <engine[:type]>)").argument("<key>", "config key").argument("<value>", "config value").action(withErrors((key, value) => configSetAction(key, value)));
|
|
570
|
+
config.command("get").description("Read a config value").argument("<key>", "config key").action(withErrors((key) => configGetAction(key)));
|
|
571
|
+
program.command("doctor").description("Diagnose configuration issues").action(withErrors(doctorAction));
|
|
572
|
+
registerDeprecatedAliases(program);
|
|
573
|
+
return program;
|
|
574
|
+
}
|
|
575
|
+
function registerDeprecatedAliases(program) {
|
|
576
|
+
program.command("list", { hidden: true }).action(
|
|
577
|
+
withErrors(() => {
|
|
578
|
+
deprecated("list", "collection list");
|
|
579
|
+
return listCollectionsAction();
|
|
214
580
|
})
|
|
215
581
|
);
|
|
216
|
-
program.command("add"
|
|
217
|
-
withErrors(
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
const branch = status?.branch ? ` (${status.branch})` : "";
|
|
221
|
-
console.log(`Added "${entry.name}"${branch} \u2192 ${entry.path}`);
|
|
222
|
-
console.log("Registered globally \u2014 discoverable via: diffwiki list");
|
|
582
|
+
program.command("add", { hidden: true }).option("-d, --docs <dir>", "content dir relative to repo root", "docs").action(
|
|
583
|
+
withErrors((opts) => {
|
|
584
|
+
deprecated("add", "collection add");
|
|
585
|
+
return addExternalAction(opts);
|
|
223
586
|
})
|
|
224
587
|
);
|
|
225
|
-
program.command("create"
|
|
226
|
-
withErrors(
|
|
227
|
-
|
|
228
|
-
|
|
588
|
+
program.command("create", { hidden: true }).argument("<name>", "collection name").action(
|
|
589
|
+
withErrors((name) => {
|
|
590
|
+
deprecated("create", "collection create");
|
|
591
|
+
return createCollectionAction(name);
|
|
229
592
|
})
|
|
230
593
|
);
|
|
231
|
-
program.command("add-article"
|
|
232
|
-
withErrors(
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
tags: splitTags(opts.tag),
|
|
236
|
-
body
|
|
237
|
-
});
|
|
238
|
-
console.log(article.path);
|
|
594
|
+
program.command("add-article", { hidden: true }).requiredOption("-p, --path <target>", '"[collection:]title"').option("-t, --tag <tag>", "tag (repeatable, comma-separated)", collect, []).argument("[body]", "markdown body").action(
|
|
595
|
+
withErrors((body, opts) => {
|
|
596
|
+
deprecated("add-article", "article create");
|
|
597
|
+
return createArticleAction(opts.path, body, opts.tag);
|
|
239
598
|
})
|
|
240
599
|
);
|
|
241
|
-
program.command("update-article"
|
|
242
|
-
withErrors(
|
|
243
|
-
|
|
244
|
-
|
|
600
|
+
program.command("update-article", { hidden: true }).requiredOption("-p, --path <target>", '"collection:path"').argument("<body>", "new markdown body").action(
|
|
601
|
+
withErrors((body, opts) => {
|
|
602
|
+
deprecated("update-article", "article update");
|
|
603
|
+
return updateArticleAction(opts.path, body);
|
|
245
604
|
})
|
|
246
605
|
);
|
|
247
|
-
program.command("remove-article"
|
|
248
|
-
withErrors(
|
|
249
|
-
|
|
250
|
-
|
|
606
|
+
program.command("remove-article", { hidden: true }).requiredOption("-p, --path <target>", '"collection:path"').action(
|
|
607
|
+
withErrors((opts) => {
|
|
608
|
+
deprecated("remove-article", "article remove");
|
|
609
|
+
return removeArticleAction(opts.path);
|
|
251
610
|
})
|
|
252
611
|
);
|
|
253
|
-
const
|
|
254
|
-
program.command(name).requiredOption("-p, --path <target>", '"collection:path"').argument("<tags>", "comma-separated tags").action(
|
|
255
|
-
withErrors(
|
|
256
|
-
|
|
257
|
-
|
|
612
|
+
const registerDeprecatedTag = (name, newForm, fn) => {
|
|
613
|
+
program.command(name, { hidden: true }).requiredOption("-p, --path <target>", '"collection:path"').argument("<tags>", "comma-separated tags").action(
|
|
614
|
+
withErrors((tagList, opts) => {
|
|
615
|
+
deprecated(name, newForm);
|
|
616
|
+
return tagsAction(fn, opts.path, tagList);
|
|
258
617
|
})
|
|
259
618
|
);
|
|
260
619
|
};
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
program.command("index"
|
|
265
|
-
withErrors(
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
const installable = (await listSearchModes()).filter((m) => m.plugin !== "native");
|
|
269
|
-
console.log(
|
|
270
|
-
installable.length > 0 ? `No index-backed plugin active. Available modes: ${installable.map((m) => `${m.plugin}\xB7${m.type}`).join(", ")}` : "No search plugin installed \u2014 using native search (nothing to index). Install one with: diffwiki plugin install <spec>"
|
|
271
|
-
);
|
|
272
|
-
return;
|
|
273
|
-
}
|
|
274
|
-
const indexed = await provider.reindex(await listCollections2(), { embed: !!opts.embed });
|
|
275
|
-
console.log(`Indexed ${indexed} document(s).`);
|
|
620
|
+
registerDeprecatedTag("add-tags", "tags add", addTags);
|
|
621
|
+
registerDeprecatedTag("remove-tags", "tags remove", removeTags);
|
|
622
|
+
registerDeprecatedTag("update-tags", "tags set", setTags);
|
|
623
|
+
program.command("index", { hidden: true }).option("--embed", "also build vector embeddings (may download models on first run)").action(
|
|
624
|
+
withErrors((opts) => {
|
|
625
|
+
deprecated("index", "search index");
|
|
626
|
+
return indexAction(opts);
|
|
276
627
|
})
|
|
277
628
|
);
|
|
278
|
-
program.command("query"
|
|
279
|
-
withErrors(
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
const hits = outcome.hits;
|
|
283
|
-
console.log(`${hits.length} result(s)`);
|
|
284
|
-
for (const hit of hits) {
|
|
285
|
-
const score = typeof hit.score === "number" ? hit.score.toFixed(3) : "-";
|
|
286
|
-
const line = `${score} ${hit.collection} ${hit.title} ${hit.path}`;
|
|
287
|
-
console.log(hit.snippet ? `${line} ${hit.snippet}` : line);
|
|
288
|
-
}
|
|
289
|
-
if (outcome.error) {
|
|
290
|
-
console.error(`\u26A0 engine "${outcome.engine}" failed: ${outcome.error} \u2014 used native (${hits.length} results)`);
|
|
291
|
-
}
|
|
292
|
-
})
|
|
293
|
-
);
|
|
294
|
-
const plugin = program.command("plugin").description("Manage search plugins");
|
|
295
|
-
plugin.command("install").description("Install a search plugin from an npm spec and provision it (setup)").argument("<spec>", "npm package spec (name, name@version, or path)").option("--no-setup", "skip the end-to-end setup after install").option("--no-default", "do not make this the default search engine").option("--prefetch-models", "pre-download models during setup (heavy)").action(
|
|
296
|
-
withErrors(async (spec, opts) => {
|
|
297
|
-
const { record, setup, defaulted } = await installPlugin(spec, {
|
|
298
|
-
setup: opts.setup,
|
|
299
|
-
setDefault: opts.default,
|
|
300
|
-
prefetchModels: opts.prefetchModels
|
|
301
|
-
});
|
|
302
|
-
console.log(`Installed "${record.name}"${record.version ? ` v${record.version}` : ""}`);
|
|
303
|
-
if (!opts.setup) {
|
|
304
|
-
console.log("Skipped setup (--no-setup) \u2014 run: diffwiki index");
|
|
305
|
-
} else if (setup && !setup.ready) {
|
|
306
|
-
const reason = setup.steps.find((s) => !s.ok)?.message ?? "setup did not complete";
|
|
307
|
-
console.log(`\u26A0 Not ready: ${reason}`);
|
|
308
|
-
console.log(" \u2192 run: diffwiki doctor");
|
|
309
|
-
}
|
|
310
|
-
if (defaulted) {
|
|
311
|
-
console.log(
|
|
312
|
-
`Set "${record.name}" as the default search engine (change: diffwiki config-set defaultSearch <engine[:type]>)`
|
|
313
|
-
);
|
|
314
|
-
}
|
|
629
|
+
program.command("query", { hidden: true }).option("--engine <name>", "search engine/plugin (default: config defaultSearch, else native)").option("-c, --collection <name>", "limit to a collection").option("-t, --type <type>", "search type (engine-defined, e.g. keyword|semantic|hybrid)").argument("[term]", "search term", "").action(
|
|
630
|
+
withErrors((term, opts) => {
|
|
631
|
+
deprecated("query", "search query");
|
|
632
|
+
return queryAction(term, opts);
|
|
315
633
|
})
|
|
316
634
|
);
|
|
317
|
-
|
|
318
|
-
withErrors(
|
|
319
|
-
|
|
320
|
-
|
|
635
|
+
program.command("init", { hidden: true }).option("-c, --collection <name>", "collection name (default: repo name)").option("-p, --path <path>", "wiki path within the repo (default: wiki)").action(
|
|
636
|
+
withErrors((opts) => {
|
|
637
|
+
deprecated("init", "collection init");
|
|
638
|
+
return initAction(opts);
|
|
321
639
|
})
|
|
322
640
|
);
|
|
323
|
-
|
|
324
|
-
withErrors(
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
if (installed.length === 0) {
|
|
329
|
-
console.log(" (none)");
|
|
330
|
-
} else {
|
|
331
|
-
for (const p of installed) {
|
|
332
|
-
console.log(
|
|
333
|
-
` ${p.name} ${p.kind} ${p.version ?? "-"} enabled=${p.enabled} managed=${!!p.managed} spawnable=${p.spawnable}`
|
|
334
|
-
);
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
const available = listAvailablePlugins().filter((k) => !installedNames.has(k.name));
|
|
338
|
-
if (available.length > 0) {
|
|
339
|
-
console.log("\nAvailable (install with: diffwiki plugin install <name>):");
|
|
340
|
-
for (const k of available) {
|
|
341
|
-
console.log(` ${k.name} [${k.kind}] ${k.description}`);
|
|
342
|
-
console.log(` requires ${k.requires ?? "nothing"}`);
|
|
343
|
-
}
|
|
641
|
+
program.command("export", { hidden: true }).requiredOption("-o, --out <dir>", "output directory").option("-b, --base <path>", "base URL path (e.g. /my-repo/ for GitHub Pages)", "/").option("-f, --filter <globs>", "only export collections matching these globs (comma-separated)").option("-e, --exclude <globs>", "exclude collections matching these globs (comma-separated)").option("--project [dir]", "build only the repo project from its diffwiki.yaml (default: cwd)").action(
|
|
642
|
+
withErrors(
|
|
643
|
+
(opts) => {
|
|
644
|
+
deprecated("export", "site export");
|
|
645
|
+
return exportAction(opts);
|
|
344
646
|
}
|
|
345
|
-
|
|
346
|
-
);
|
|
347
|
-
plugin.command("remove").description("Remove a search plugin").argument("<name>", "plugin name").action(
|
|
348
|
-
withErrors(async (name) => {
|
|
349
|
-
await removePlugin(name);
|
|
350
|
-
console.log(`Removed "${name}"`);
|
|
351
|
-
})
|
|
647
|
+
)
|
|
352
648
|
);
|
|
353
|
-
program.command("
|
|
354
|
-
withErrors(
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
collection: opts.collection,
|
|
358
|
-
wikiPath: opts.path
|
|
359
|
-
});
|
|
360
|
-
console.log(`Initialized repo wiki "${entry.name}" at ${entry.path}`);
|
|
361
|
-
console.log("Registered globally \u2014 discoverable via: diffwiki list");
|
|
649
|
+
program.command("config-set", { hidden: true }).argument("<key>", "config key").argument("<value>", "config value").action(
|
|
650
|
+
withErrors((key, value) => {
|
|
651
|
+
deprecated("config-set", "config set");
|
|
652
|
+
return configSetAction(key, value);
|
|
362
653
|
})
|
|
363
654
|
);
|
|
364
|
-
program.command("
|
|
365
|
-
withErrors(
|
|
366
|
-
|
|
655
|
+
program.command("config-get", { hidden: true }).argument("<key>", "config key").action(
|
|
656
|
+
withErrors((key) => {
|
|
657
|
+
deprecated("config-get", "config get");
|
|
658
|
+
return configGetAction(key);
|
|
367
659
|
})
|
|
368
660
|
);
|
|
369
|
-
program.command("
|
|
370
|
-
withErrors(
|
|
371
|
-
|
|
372
|
-
|
|
661
|
+
program.command("up", { hidden: true }).option("-p, --port <port>", "port to serve on", "4321").action(
|
|
662
|
+
withErrors((opts) => {
|
|
663
|
+
deprecated("up", "ui up");
|
|
664
|
+
return upAction(opts);
|
|
373
665
|
})
|
|
374
666
|
);
|
|
375
|
-
program.command("
|
|
376
|
-
withErrors(
|
|
377
|
-
|
|
378
|
-
|
|
667
|
+
program.command("down", { hidden: true }).action(
|
|
668
|
+
withErrors(() => {
|
|
669
|
+
deprecated("down", "ui down");
|
|
670
|
+
return downAction();
|
|
379
671
|
})
|
|
380
672
|
);
|
|
381
|
-
program.command("
|
|
382
|
-
withErrors(
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
console.log(`${icon(d.level)} ${d.message}`);
|
|
386
|
-
}
|
|
387
|
-
if (diagnostics.some((d) => d.level === "error")) {
|
|
388
|
-
process.exitCode = 1;
|
|
389
|
-
}
|
|
390
|
-
})
|
|
391
|
-
);
|
|
392
|
-
program.command("up").description("Start the diffwiki UI (latest published diffwiki-app)").option("-p, --port <port>", "port to serve on", "4321").action(
|
|
393
|
-
withErrors(async (opts) => {
|
|
394
|
-
const existing = await readState();
|
|
395
|
-
if (existing && isAlive(existing.pid)) {
|
|
396
|
-
console.log(`UI already running at ${existing.url} (pid ${existing.pid})`);
|
|
397
|
-
return;
|
|
398
|
-
}
|
|
399
|
-
const state = await startUi(Number(opts.port));
|
|
400
|
-
const ok = await waitForServer(state.url);
|
|
401
|
-
console.log(
|
|
402
|
-
ok ? `UI started at ${state.url} (pid ${state.pid})` : `Launched (pid ${state.pid}) but ${state.url} did not respond yet \u2014 check \`diffwiki status\``
|
|
403
|
-
);
|
|
673
|
+
program.command("status", { hidden: true }).action(
|
|
674
|
+
withErrors(() => {
|
|
675
|
+
deprecated("status", "ui status");
|
|
676
|
+
return statusAction();
|
|
404
677
|
})
|
|
405
678
|
);
|
|
406
|
-
program.command("
|
|
407
|
-
withErrors(
|
|
408
|
-
|
|
409
|
-
|
|
679
|
+
program.command("open", { hidden: true }).option("-p, --port <port>", "port to serve on", "4321").action(
|
|
680
|
+
withErrors((opts) => {
|
|
681
|
+
deprecated("open", "ui open");
|
|
682
|
+
return openAction(opts);
|
|
410
683
|
})
|
|
411
684
|
);
|
|
412
|
-
program.command("
|
|
413
|
-
withErrors(
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
console.log(`running \xB7 ${state.url} \xB7 pid ${state.pid} \xB7 since ${state.startedAt}`);
|
|
417
|
-
} else {
|
|
418
|
-
console.log("stopped");
|
|
419
|
-
}
|
|
685
|
+
program.command("edit", { hidden: true }).argument("[target]", 'collection or "collection:slug" (default: the current repo collection)').option("--editor <cmd>", "editor command (overrides config `editor`, $VISUAL, $EDITOR)").action(
|
|
686
|
+
withErrors((target, opts) => {
|
|
687
|
+
deprecated("edit", "collection edit / article edit");
|
|
688
|
+
return editAction(target, opts.editor);
|
|
420
689
|
})
|
|
421
690
|
);
|
|
422
|
-
program.command("
|
|
423
|
-
withErrors(
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
state = await startUi(Number(opts.port));
|
|
427
|
-
await waitForServer(state.url);
|
|
428
|
-
}
|
|
429
|
-
const coll = await resolveCwdCollection(process.cwd());
|
|
430
|
-
const target = coll ? `${state.url}/${encodeURIComponent(coll.name)}` : state.url;
|
|
431
|
-
openBrowser(target);
|
|
432
|
-
console.log(`Opening ${target}`);
|
|
691
|
+
program.command("preview", { hidden: true }).option("-p, --port <port>", "port to serve on", "4321").action(
|
|
692
|
+
withErrors((opts) => {
|
|
693
|
+
deprecated("preview", "site preview");
|
|
694
|
+
return previewAction(opts);
|
|
433
695
|
})
|
|
434
696
|
);
|
|
435
|
-
return program;
|
|
436
697
|
}
|
|
437
698
|
|
|
438
699
|
// src/logging.ts
|
|
439
700
|
import { mkdirSync } from "fs";
|
|
440
701
|
import os from "os";
|
|
441
|
-
import
|
|
702
|
+
import path4 from "path";
|
|
442
703
|
import { configureSync, getConfig } from "@logtape/logtape";
|
|
443
704
|
import { getRotatingFileSink } from "@logtape/file";
|
|
444
705
|
import { resolveLogLevel } from "diffwiki-core";
|
|
445
706
|
var configured = false;
|
|
446
707
|
function diffwikiHome() {
|
|
447
708
|
const override = process.env.DIFFWIKI_HOME;
|
|
448
|
-
return override && override.length > 0 ? override :
|
|
709
|
+
return override && override.length > 0 ? override : path4.join(os.homedir(), ".diffwiki");
|
|
449
710
|
}
|
|
450
711
|
function resolveCliLevel(argv) {
|
|
451
712
|
if (argv.includes("-v") || argv.includes("--verbose")) return "debug";
|
|
@@ -462,9 +723,9 @@ function configureCliLogging(argv = process.argv) {
|
|
|
462
723
|
if (configured || getConfig() != null) return;
|
|
463
724
|
configured = true;
|
|
464
725
|
const level = resolveCliLevel(argv);
|
|
465
|
-
const logsDir =
|
|
726
|
+
const logsDir = path4.join(diffwikiHome(), "logs");
|
|
466
727
|
mkdirSync(logsDir, { recursive: true });
|
|
467
|
-
const logFile =
|
|
728
|
+
const logFile = path4.join(logsDir, "diffwiki.log");
|
|
468
729
|
const stderrSink = (record) => {
|
|
469
730
|
process.stderr.write(formatLine(record) + "\n");
|
|
470
731
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "diffwiki",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0-rc.202607291532.69c042c",
|
|
4
4
|
"description": "diffwiki command-line interface",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"@logtape/logtape": "^2.2.4",
|
|
18
18
|
"@logtape/file": "^2.2.4",
|
|
19
19
|
"commander": "^13.0.0",
|
|
20
|
-
"diffwiki-core": "0.
|
|
20
|
+
"diffwiki-core": "0.9.0-rc.202607291532.69c042c"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"@types/node": "^22.0.0"
|