diffwiki 0.9.0 → 0.10.0-rc.202607301110.42cca4d
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 +270 -263
- package/package.json +2 -2
package/dist/bin.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
4
|
import { spawn as spawn4 } from "child_process";
|
|
5
|
-
import { Command } from "commander";
|
|
5
|
+
import { Command, Option } from "commander";
|
|
6
6
|
import {
|
|
7
7
|
addExternalCollection,
|
|
8
8
|
addTags,
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
resolveCwdCollection,
|
|
27
27
|
resolveEditorCommand,
|
|
28
28
|
resolveEditTarget,
|
|
29
|
+
setArticleMeta,
|
|
29
30
|
setConfigValue,
|
|
30
31
|
setTags,
|
|
31
32
|
updateArticleBody
|
|
@@ -292,282 +293,288 @@ async function runPreview(opts) {
|
|
|
292
293
|
|
|
293
294
|
// src/cli.ts
|
|
294
295
|
var icon = (level) => level === "ok" ? "\u2713" : level === "warn" ? "!" : "\u2716";
|
|
296
|
+
async function listCollectionsAction() {
|
|
297
|
+
const collections = await listCollections2();
|
|
298
|
+
if (collections.length === 0) {
|
|
299
|
+
console.log("No collections yet. Create one with: diffwiki collection create <name>");
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
for (const c of collections) {
|
|
303
|
+
const status = await collectionGitStatus(c);
|
|
304
|
+
const branch = status?.branch ? ` (${status.branch})` : "";
|
|
305
|
+
console.log(`${c.name} [${c.type}] ${c.path}${branch}`);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
async function addExternalAction(opts) {
|
|
309
|
+
const entry = await addExternalCollection({ cwd: process.cwd(), docsDir: opts.docs });
|
|
310
|
+
const status = await collectionGitStatus(entry);
|
|
311
|
+
const branch = status?.branch ? ` (${status.branch})` : "";
|
|
312
|
+
console.log(`Added "${entry.name}"${branch} \u2192 ${entry.path}`);
|
|
313
|
+
console.log("Registered globally \u2014 discoverable via: diffwiki collection list");
|
|
314
|
+
}
|
|
315
|
+
async function createCollectionAction(name) {
|
|
316
|
+
const entry = await createCollection(name);
|
|
317
|
+
console.log(`Created collection "${entry.name}" at ${entry.path}`);
|
|
318
|
+
}
|
|
319
|
+
async function createArticleAction(target, opts) {
|
|
320
|
+
const article = await createArticle({
|
|
321
|
+
target,
|
|
322
|
+
tags: splitTags(opts.tag),
|
|
323
|
+
body: opts.body,
|
|
324
|
+
audience: opts.audience,
|
|
325
|
+
status: opts.status
|
|
326
|
+
});
|
|
327
|
+
console.log(article.path);
|
|
328
|
+
}
|
|
329
|
+
async function updateArticleAction(target, opts) {
|
|
330
|
+
const tagList = splitTags(opts.tag);
|
|
331
|
+
const hasMeta = opts.audience !== void 0 || opts.status !== void 0;
|
|
332
|
+
if (opts.body === void 0 && tagList.length === 0 && !hasMeta) {
|
|
333
|
+
console.error("Nothing to update \u2014 pass -b/--body, -t/--tag, --audience, and/or --status.");
|
|
334
|
+
process.exitCode = 1;
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
let article;
|
|
338
|
+
if (opts.body !== void 0) article = await updateArticleBody(target, opts.body);
|
|
339
|
+
if (tagList.length > 0) article = await setTags(target, tagList);
|
|
340
|
+
if (hasMeta) article = await setArticleMeta(target, { audience: opts.audience, status: opts.status });
|
|
341
|
+
console.log(`Updated ${article.path}`);
|
|
342
|
+
}
|
|
343
|
+
async function removeArticleAction(target) {
|
|
344
|
+
const removed = await removeArticle(target);
|
|
345
|
+
console.log(`Removed ${removed}`);
|
|
346
|
+
}
|
|
347
|
+
async function tagsAction(fn, target, tags) {
|
|
348
|
+
const article = await fn(target, splitTags([tags]));
|
|
349
|
+
console.log(`${article.path} \u2192 tags: ${article.tags.join(", ") || "(none)"}`);
|
|
350
|
+
}
|
|
351
|
+
async function indexAction(opts) {
|
|
352
|
+
const provider = await loadSearchProvider();
|
|
353
|
+
if (!provider) {
|
|
354
|
+
const installable = (await listSearchModes()).filter((m) => m.plugin !== "native");
|
|
355
|
+
console.log(
|
|
356
|
+
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>"
|
|
357
|
+
);
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
const indexed = await provider.reindex(await listCollections2(), { embed: !!opts.embed });
|
|
361
|
+
console.log(`Indexed ${indexed} document(s).`);
|
|
362
|
+
}
|
|
363
|
+
async function queryAction(term, opts) {
|
|
364
|
+
const queryOpts = { engine: opts.engine, collection: opts.collection, type: opts.type };
|
|
365
|
+
const outcome = await search(term, queryOpts);
|
|
366
|
+
const hits = outcome.hits;
|
|
367
|
+
console.log(`${hits.length} result(s)`);
|
|
368
|
+
for (const hit of hits) {
|
|
369
|
+
const score = typeof hit.score === "number" ? hit.score.toFixed(3) : "-";
|
|
370
|
+
const line = `${score} ${hit.collection} ${hit.title} ${hit.path}`;
|
|
371
|
+
console.log(hit.snippet ? `${line} ${hit.snippet}` : line);
|
|
372
|
+
}
|
|
373
|
+
if (outcome.error) {
|
|
374
|
+
console.error(`\u26A0 engine "${outcome.engine}" failed: ${outcome.error} \u2014 used native (${hits.length} results)`);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
async function pluginInstallAction(spec, opts) {
|
|
378
|
+
const { record, setup, defaulted } = await installPlugin(spec, {
|
|
379
|
+
setup: opts.setup,
|
|
380
|
+
setDefault: opts.default,
|
|
381
|
+
prefetchModels: opts.prefetchModels
|
|
382
|
+
});
|
|
383
|
+
console.log(`Installed "${record.name}"${record.version ? ` v${record.version}` : ""}`);
|
|
384
|
+
if (!opts.setup) {
|
|
385
|
+
console.log("Skipped setup (--no-setup) \u2014 run: diffwiki search index");
|
|
386
|
+
} else if (setup && !setup.ready) {
|
|
387
|
+
const reason = setup.steps.find((s) => !s.ok)?.message ?? "setup did not complete";
|
|
388
|
+
console.log(`\u26A0 Not ready: ${reason}`);
|
|
389
|
+
console.log(" \u2192 run: diffwiki doctor");
|
|
390
|
+
}
|
|
391
|
+
if (defaulted) {
|
|
392
|
+
console.log(
|
|
393
|
+
`Set "${record.name}" as the default search engine (change: diffwiki config set defaultSearch <engine[:type]>)`
|
|
394
|
+
);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
async function pluginRegisterAction(name, opts) {
|
|
398
|
+
const record = await registerPlugin(name, opts.command);
|
|
399
|
+
console.log(`Registered "${record.name}" \u2192 ${record.command.join(" ")}`);
|
|
400
|
+
}
|
|
401
|
+
async function pluginListAction() {
|
|
402
|
+
const installed = await listPlugins();
|
|
403
|
+
const installedNames = new Set(installed.map((p) => p.name));
|
|
404
|
+
console.log("Installed:");
|
|
405
|
+
if (installed.length === 0) {
|
|
406
|
+
console.log(" (none)");
|
|
407
|
+
} else {
|
|
408
|
+
for (const p of installed) {
|
|
409
|
+
console.log(
|
|
410
|
+
` ${p.name} ${p.kind} ${p.version ?? "-"} enabled=${p.enabled} managed=${!!p.managed} spawnable=${p.spawnable}`
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
const available = listAvailablePlugins().filter((k) => !installedNames.has(k.name));
|
|
415
|
+
if (available.length > 0) {
|
|
416
|
+
console.log("\nAvailable (install with: diffwiki plugin install <name>):");
|
|
417
|
+
for (const k of available) {
|
|
418
|
+
console.log(` ${k.name} [${k.kind}] ${k.description}`);
|
|
419
|
+
console.log(` requires ${k.requires ?? "nothing"}`);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
async function pluginRemoveAction(name) {
|
|
424
|
+
await removePlugin(name);
|
|
425
|
+
console.log(`Removed "${name}"`);
|
|
426
|
+
}
|
|
427
|
+
async function initAction(opts) {
|
|
428
|
+
const entry = await initRepoWiki({
|
|
429
|
+
cwd: process.cwd(),
|
|
430
|
+
collection: opts.collection,
|
|
431
|
+
wikiPath: opts.path
|
|
432
|
+
});
|
|
433
|
+
console.log(`Initialized repo wiki "${entry.name}" at ${entry.path}`);
|
|
434
|
+
console.log("Registered globally \u2014 discoverable via: diffwiki collection list");
|
|
435
|
+
}
|
|
436
|
+
async function exportAction(opts) {
|
|
437
|
+
await runExport({
|
|
438
|
+
out: opts.out,
|
|
439
|
+
base: opts.base,
|
|
440
|
+
filter: opts.filter,
|
|
441
|
+
exclude: opts.exclude,
|
|
442
|
+
project: opts.project
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
async function configSetAction(key, value) {
|
|
446
|
+
await setConfigValue(key, value);
|
|
447
|
+
console.log(`Set ${key} = ${value}`);
|
|
448
|
+
}
|
|
449
|
+
async function configGetAction(key) {
|
|
450
|
+
const value = await getConfigValue(key);
|
|
451
|
+
console.log(value ?? "(unset)");
|
|
452
|
+
}
|
|
453
|
+
async function doctorAction() {
|
|
454
|
+
const diagnostics = await doctor();
|
|
455
|
+
for (const d of diagnostics) {
|
|
456
|
+
console.log(`${icon(d.level)} ${d.message}`);
|
|
457
|
+
}
|
|
458
|
+
if (diagnostics.some((d) => d.level === "error")) {
|
|
459
|
+
process.exitCode = 1;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
async function upAction(opts) {
|
|
463
|
+
const existing = await readState();
|
|
464
|
+
if (existing && isAlive(existing.pid)) {
|
|
465
|
+
console.log(`UI already running at ${existing.url} (pid ${existing.pid})`);
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
const state = await startUi(Number(opts.port));
|
|
469
|
+
const ok = await waitForServer(state.url);
|
|
470
|
+
console.log(
|
|
471
|
+
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\``
|
|
472
|
+
);
|
|
473
|
+
}
|
|
474
|
+
async function downAction() {
|
|
475
|
+
const stopped = await stopUi();
|
|
476
|
+
console.log(stopped ? `Stopped UI (pid ${stopped.pid})` : "UI is not running.");
|
|
477
|
+
}
|
|
478
|
+
async function statusAction() {
|
|
479
|
+
const state = await readState();
|
|
480
|
+
if (state && isAlive(state.pid)) {
|
|
481
|
+
console.log(`running \xB7 ${state.url} \xB7 pid ${state.pid} \xB7 since ${state.startedAt}`);
|
|
482
|
+
} else {
|
|
483
|
+
console.log("stopped");
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
async function openAction(opts) {
|
|
487
|
+
let state = await readState();
|
|
488
|
+
if (!state || !isAlive(state.pid)) {
|
|
489
|
+
state = await startUi(Number(opts.port));
|
|
490
|
+
await waitForServer(state.url);
|
|
491
|
+
}
|
|
492
|
+
const coll = await resolveCwdCollection(process.cwd());
|
|
493
|
+
const target = coll ? `${state.url}/${encodeURIComponent(coll.name)}` : state.url;
|
|
494
|
+
openBrowser(target);
|
|
495
|
+
console.log(`Opening ${target}`);
|
|
496
|
+
}
|
|
497
|
+
async function editAction(target, editorFlag) {
|
|
498
|
+
const argv = await resolveEditorCommand(editorFlag);
|
|
499
|
+
if (!argv) {
|
|
500
|
+
console.error(
|
|
501
|
+
'No editor set. Configure one: diffwiki config set editor "<cmd>" \u2014 or set $EDITOR, or pass --editor.'
|
|
502
|
+
);
|
|
503
|
+
process.exitCode = 1;
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
const { path: dest, kind } = await resolveEditTarget(target, process.cwd());
|
|
507
|
+
const [cmd, ...args] = argv;
|
|
508
|
+
console.log(`Opening ${kind} ${dest} with ${argv.join(" ")}`);
|
|
509
|
+
const code = await new Promise((resolve) => {
|
|
510
|
+
const child = spawn4(cmd, [...args, dest], { stdio: "inherit" });
|
|
511
|
+
child.on("error", (err) => {
|
|
512
|
+
console.error(`Could not launch editor "${cmd}": ${err.message}`);
|
|
513
|
+
resolve(127);
|
|
514
|
+
});
|
|
515
|
+
child.on("exit", (c) => resolve(c ?? 0));
|
|
516
|
+
});
|
|
517
|
+
if (code !== 0) process.exitCode = code;
|
|
518
|
+
}
|
|
519
|
+
async function previewAction(opts) {
|
|
520
|
+
await runPreview({ cwd: process.cwd(), port: Number(opts.port) });
|
|
521
|
+
}
|
|
295
522
|
function createProgram() {
|
|
296
523
|
const program = new Command();
|
|
297
524
|
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");
|
|
298
525
|
program.action(() => {
|
|
299
526
|
program.outputHelp();
|
|
300
527
|
});
|
|
301
|
-
program.command("
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
program.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(
|
|
316
|
-
withErrors(async (opts) => {
|
|
317
|
-
const entry = await addExternalCollection({ cwd: process.cwd(), docsDir: opts.docs });
|
|
318
|
-
const status = await collectionGitStatus(entry);
|
|
319
|
-
const branch = status?.branch ? ` (${status.branch})` : "";
|
|
320
|
-
console.log(`Added "${entry.name}"${branch} \u2192 ${entry.path}`);
|
|
321
|
-
console.log("Registered globally \u2014 discoverable via: diffwiki list");
|
|
322
|
-
})
|
|
323
|
-
);
|
|
324
|
-
program.command("create").description("Create a new global collection").argument("<name>", "collection name").action(
|
|
325
|
-
withErrors(async (name) => {
|
|
326
|
-
const entry = await createCollection(name);
|
|
327
|
-
console.log(`Created collection "${entry.name}" at ${entry.path}`);
|
|
328
|
-
})
|
|
329
|
-
);
|
|
330
|
-
program.command("add-article").description("Add an article to a collection").requiredOption("-p, --path <target>", '"[collection:]title"').option("-t, --tag <tag>", "tag (repeatable, comma-separated)", collect, []).argument("[body]", "markdown body").action(
|
|
331
|
-
withErrors(async (body, opts) => {
|
|
332
|
-
const article = await createArticle({
|
|
333
|
-
target: opts.path,
|
|
334
|
-
tags: splitTags(opts.tag),
|
|
335
|
-
body
|
|
336
|
-
});
|
|
337
|
-
console.log(article.path);
|
|
338
|
-
})
|
|
339
|
-
);
|
|
340
|
-
program.command("update-article").description("Replace an article's body").requiredOption("-p, --path <target>", '"collection:path"').argument("<body>", "new markdown body").action(
|
|
341
|
-
withErrors(async (body, opts) => {
|
|
342
|
-
const article = await updateArticleBody(opts.path, body);
|
|
343
|
-
console.log(`Updated ${article.path}`);
|
|
344
|
-
})
|
|
345
|
-
);
|
|
346
|
-
program.command("remove-article").description("Delete an article").requiredOption("-p, --path <target>", '"collection:path"').action(
|
|
347
|
-
withErrors(async (opts) => {
|
|
348
|
-
const removed = await removeArticle(opts.path);
|
|
349
|
-
console.log(`Removed ${removed}`);
|
|
350
|
-
})
|
|
351
|
-
);
|
|
352
|
-
const registerTagCommand = (name, fn) => {
|
|
353
|
-
program.command(name).requiredOption("-p, --path <target>", '"collection:path"').argument("<tags>", "comma-separated tags").action(
|
|
354
|
-
withErrors(async (tags, opts) => {
|
|
355
|
-
const article = await fn(opts.path, splitTags([tags]));
|
|
356
|
-
console.log(`${article.path} \u2192 tags: ${article.tags.join(", ") || "(none)"}`);
|
|
357
|
-
})
|
|
358
|
-
);
|
|
528
|
+
const collection = program.command("collection").description("Manage collections (containers)");
|
|
529
|
+
collection.command("list").description("List all collections").action(withErrors(listCollectionsAction));
|
|
530
|
+
collection.command("create").description("Create a new global collection").argument("<name>", "collection name").action(withErrors((name) => createCollectionAction(name)));
|
|
531
|
+
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)));
|
|
532
|
+
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)));
|
|
533
|
+
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)));
|
|
534
|
+
const article = program.command("article").description("Manage articles (documents)");
|
|
535
|
+
article.command("create").description("Add an article to a collection").argument("<target>", '"[collection:]title"').option("-b, --body <markdown>", 'article body (default: a "# <title>" heading)').option("-t, --tag <tag>", "tag (repeatable, comma-separated)", collect, []).addOption(new Option("--audience <audience>", "visibility (default: public)").choices(["public", "private"])).addOption(new Option("--status <status>", "publish status (default: published)").choices(["draft", "published"])).action(withErrors((target, opts) => createArticleAction(target, opts)));
|
|
536
|
+
article.command("update").description("Update an article's body, tags, audience, and/or status").argument("<target>", '"collection:slug"').option("-b, --body <markdown>", "replace the article body").option("-t, --tag <tag>", "replace all tags (repeatable, comma-separated)", collect, []).addOption(new Option("--audience <audience>", "set visibility").choices(["public", "private"])).addOption(new Option("--status <status>", "set publish status").choices(["draft", "published"])).action(withErrors((target, opts) => updateArticleAction(target, opts)));
|
|
537
|
+
article.command("remove").description("Delete an article").argument("<target>", '"collection:slug"').action(withErrors((target) => removeArticleAction(target)));
|
|
538
|
+
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)));
|
|
539
|
+
const tags = program.command("tags").description("Manage an article's tags");
|
|
540
|
+
const registerTagsSub = (name, description, fn) => {
|
|
541
|
+
tags.command(name).description(description).argument("<target>", '"collection:slug"').argument("<tags>", "comma-separated tags").action(withErrors((target, tagList) => tagsAction(fn, target, tagList)));
|
|
359
542
|
};
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
program.command("
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
console.log(
|
|
369
|
-
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>"
|
|
370
|
-
);
|
|
371
|
-
return;
|
|
372
|
-
}
|
|
373
|
-
const indexed = await provider.reindex(await listCollections2(), { embed: !!opts.embed });
|
|
374
|
-
console.log(`Indexed ${indexed} document(s).`);
|
|
375
|
-
})
|
|
376
|
-
);
|
|
377
|
-
program.command("query").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(
|
|
378
|
-
withErrors(async (term, opts) => {
|
|
379
|
-
const queryOpts = { engine: opts.engine, collection: opts.collection, type: opts.type };
|
|
380
|
-
const outcome = await search(term, queryOpts);
|
|
381
|
-
const hits = outcome.hits;
|
|
382
|
-
console.log(`${hits.length} result(s)`);
|
|
383
|
-
for (const hit of hits) {
|
|
384
|
-
const score = typeof hit.score === "number" ? hit.score.toFixed(3) : "-";
|
|
385
|
-
const line = `${score} ${hit.collection} ${hit.title} ${hit.path}`;
|
|
386
|
-
console.log(hit.snippet ? `${line} ${hit.snippet}` : line);
|
|
387
|
-
}
|
|
388
|
-
if (outcome.error) {
|
|
389
|
-
console.error(`\u26A0 engine "${outcome.engine}" failed: ${outcome.error} \u2014 used native (${hits.length} results)`);
|
|
390
|
-
}
|
|
391
|
-
})
|
|
543
|
+
registerTagsSub("add", "Add to an article's tags", addTags);
|
|
544
|
+
registerTagsSub("remove", "Remove from an article's tags", removeTags);
|
|
545
|
+
registerTagsSub("set", "Replace an article's tags", setTags);
|
|
546
|
+
const searchCmd = program.command("search").description("Search articles and manage the index");
|
|
547
|
+
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(
|
|
548
|
+
withErrors(
|
|
549
|
+
(term, opts) => queryAction(term, opts)
|
|
550
|
+
)
|
|
392
551
|
);
|
|
552
|
+
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)));
|
|
393
553
|
const plugin = program.command("plugin").description("Manage search plugins");
|
|
394
554
|
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(
|
|
395
|
-
withErrors(async (spec, opts) => {
|
|
396
|
-
const { record, setup, defaulted } = await installPlugin(spec, {
|
|
397
|
-
setup: opts.setup,
|
|
398
|
-
setDefault: opts.default,
|
|
399
|
-
prefetchModels: opts.prefetchModels
|
|
400
|
-
});
|
|
401
|
-
console.log(`Installed "${record.name}"${record.version ? ` v${record.version}` : ""}`);
|
|
402
|
-
if (!opts.setup) {
|
|
403
|
-
console.log("Skipped setup (--no-setup) \u2014 run: diffwiki index");
|
|
404
|
-
} else if (setup && !setup.ready) {
|
|
405
|
-
const reason = setup.steps.find((s) => !s.ok)?.message ?? "setup did not complete";
|
|
406
|
-
console.log(`\u26A0 Not ready: ${reason}`);
|
|
407
|
-
console.log(" \u2192 run: diffwiki doctor");
|
|
408
|
-
}
|
|
409
|
-
if (defaulted) {
|
|
410
|
-
console.log(
|
|
411
|
-
`Set "${record.name}" as the default search engine (change: diffwiki config-set defaultSearch <engine[:type]>)`
|
|
412
|
-
);
|
|
413
|
-
}
|
|
414
|
-
})
|
|
415
|
-
);
|
|
416
|
-
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(
|
|
417
|
-
withErrors(async (name, opts) => {
|
|
418
|
-
const record = await registerPlugin(name, opts.command);
|
|
419
|
-
console.log(`Registered "${record.name}" \u2192 ${record.command.join(" ")}`);
|
|
420
|
-
})
|
|
421
|
-
);
|
|
422
|
-
plugin.command("list").description("List installed and available search plugins").action(
|
|
423
|
-
withErrors(async () => {
|
|
424
|
-
const installed = await listPlugins();
|
|
425
|
-
const installedNames = new Set(installed.map((p) => p.name));
|
|
426
|
-
console.log("Installed:");
|
|
427
|
-
if (installed.length === 0) {
|
|
428
|
-
console.log(" (none)");
|
|
429
|
-
} else {
|
|
430
|
-
for (const p of installed) {
|
|
431
|
-
console.log(
|
|
432
|
-
` ${p.name} ${p.kind} ${p.version ?? "-"} enabled=${p.enabled} managed=${!!p.managed} spawnable=${p.spawnable}`
|
|
433
|
-
);
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
const available = listAvailablePlugins().filter((k) => !installedNames.has(k.name));
|
|
437
|
-
if (available.length > 0) {
|
|
438
|
-
console.log("\nAvailable (install with: diffwiki plugin install <name>):");
|
|
439
|
-
for (const k of available) {
|
|
440
|
-
console.log(` ${k.name} [${k.kind}] ${k.description}`);
|
|
441
|
-
console.log(` requires ${k.requires ?? "nothing"}`);
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
})
|
|
445
|
-
);
|
|
446
|
-
plugin.command("remove").description("Remove a search plugin").argument("<name>", "plugin name").action(
|
|
447
|
-
withErrors(async (name) => {
|
|
448
|
-
await removePlugin(name);
|
|
449
|
-
console.log(`Removed "${name}"`);
|
|
450
|
-
})
|
|
451
|
-
);
|
|
452
|
-
program.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(
|
|
453
|
-
withErrors(async (opts) => {
|
|
454
|
-
const entry = await initRepoWiki({
|
|
455
|
-
cwd: process.cwd(),
|
|
456
|
-
collection: opts.collection,
|
|
457
|
-
wikiPath: opts.path
|
|
458
|
-
});
|
|
459
|
-
console.log(`Initialized repo wiki "${entry.name}" at ${entry.path}`);
|
|
460
|
-
console.log("Registered globally \u2014 discoverable via: diffwiki list");
|
|
461
|
-
})
|
|
462
|
-
);
|
|
463
|
-
program.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(
|
|
464
555
|
withErrors(
|
|
465
|
-
|
|
466
|
-
await runExport({
|
|
467
|
-
out: opts.out,
|
|
468
|
-
base: opts.base,
|
|
469
|
-
filter: opts.filter,
|
|
470
|
-
exclude: opts.exclude,
|
|
471
|
-
project: opts.project
|
|
472
|
-
});
|
|
473
|
-
}
|
|
556
|
+
(spec, opts) => pluginInstallAction(spec, opts)
|
|
474
557
|
)
|
|
475
558
|
);
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
const value = await getConfigValue(key);
|
|
485
|
-
console.log(value ?? "(unset)");
|
|
486
|
-
})
|
|
487
|
-
);
|
|
488
|
-
program.command("doctor").description("Diagnose configuration issues").action(
|
|
489
|
-
withErrors(async () => {
|
|
490
|
-
const diagnostics = await doctor();
|
|
491
|
-
for (const d of diagnostics) {
|
|
492
|
-
console.log(`${icon(d.level)} ${d.message}`);
|
|
493
|
-
}
|
|
494
|
-
if (diagnostics.some((d) => d.level === "error")) {
|
|
495
|
-
process.exitCode = 1;
|
|
496
|
-
}
|
|
497
|
-
})
|
|
498
|
-
);
|
|
499
|
-
program.command("up").description("Start the diffwiki UI (latest published diffwiki-app)").option("-p, --port <port>", "port to serve on", "4321").action(
|
|
500
|
-
withErrors(async (opts) => {
|
|
501
|
-
const existing = await readState();
|
|
502
|
-
if (existing && isAlive(existing.pid)) {
|
|
503
|
-
console.log(`UI already running at ${existing.url} (pid ${existing.pid})`);
|
|
504
|
-
return;
|
|
505
|
-
}
|
|
506
|
-
const state = await startUi(Number(opts.port));
|
|
507
|
-
const ok = await waitForServer(state.url);
|
|
508
|
-
console.log(
|
|
509
|
-
ok ? `UI started at ${state.url} (pid ${state.pid})` : `Launched (pid ${state.pid}) but ${state.url} did not respond yet \u2014 check \`diffwiki status\``
|
|
510
|
-
);
|
|
511
|
-
})
|
|
512
|
-
);
|
|
513
|
-
program.command("down").description("Stop the running diffwiki UI").action(
|
|
514
|
-
withErrors(async () => {
|
|
515
|
-
const stopped = await stopUi();
|
|
516
|
-
console.log(stopped ? `Stopped UI (pid ${stopped.pid})` : "UI is not running.");
|
|
517
|
-
})
|
|
518
|
-
);
|
|
519
|
-
program.command("status").description("Show whether the diffwiki UI is running").action(
|
|
520
|
-
withErrors(async () => {
|
|
521
|
-
const state = await readState();
|
|
522
|
-
if (state && isAlive(state.pid)) {
|
|
523
|
-
console.log(`running \xB7 ${state.url} \xB7 pid ${state.pid} \xB7 since ${state.startedAt}`);
|
|
524
|
-
} else {
|
|
525
|
-
console.log("stopped");
|
|
526
|
-
}
|
|
527
|
-
})
|
|
528
|
-
);
|
|
529
|
-
program.command("open").description("Open the diffwiki UI in the browser (starting it if needed)").option("-p, --port <port>", "port to serve on", "4321").action(
|
|
530
|
-
withErrors(async (opts) => {
|
|
531
|
-
let state = await readState();
|
|
532
|
-
if (!state || !isAlive(state.pid)) {
|
|
533
|
-
state = await startUi(Number(opts.port));
|
|
534
|
-
await waitForServer(state.url);
|
|
535
|
-
}
|
|
536
|
-
const coll = await resolveCwdCollection(process.cwd());
|
|
537
|
-
const target = coll ? `${state.url}/${encodeURIComponent(coll.name)}` : state.url;
|
|
538
|
-
openBrowser(target);
|
|
539
|
-
console.log(`Opening ${target}`);
|
|
540
|
-
})
|
|
541
|
-
);
|
|
542
|
-
program.command("edit").description("Open a collection (or article) in your editor \u2014 GUI not required").argument("[target]", 'collection or "collection:slug" (default: the current repo collection)').option("--editor <cmd>", "editor command (overrides config `editor`, $VISUAL, $EDITOR)").action(
|
|
543
|
-
withErrors(async (target, opts) => {
|
|
544
|
-
const argv = await resolveEditorCommand(opts.editor);
|
|
545
|
-
if (!argv) {
|
|
546
|
-
console.error(
|
|
547
|
-
'No editor set. Configure one: diffwiki config-set editor "<cmd>" \u2014 or set $EDITOR, or pass --editor.'
|
|
548
|
-
);
|
|
549
|
-
process.exitCode = 1;
|
|
550
|
-
return;
|
|
551
|
-
}
|
|
552
|
-
const { path: dest, kind } = await resolveEditTarget(target, process.cwd());
|
|
553
|
-
const [cmd, ...args] = argv;
|
|
554
|
-
console.log(`Opening ${kind} ${dest} with ${argv.join(" ")}`);
|
|
555
|
-
const code = await new Promise((resolve) => {
|
|
556
|
-
const child = spawn4(cmd, [...args, dest], { stdio: "inherit" });
|
|
557
|
-
child.on("error", (err) => {
|
|
558
|
-
console.error(`Could not launch editor "${cmd}": ${err.message}`);
|
|
559
|
-
resolve(127);
|
|
560
|
-
});
|
|
561
|
-
child.on("exit", (c) => resolve(c ?? 0));
|
|
562
|
-
});
|
|
563
|
-
if (code !== 0) process.exitCode = code;
|
|
564
|
-
})
|
|
565
|
-
);
|
|
566
|
-
program.command("preview").description("Preview the site from the repo diffwiki.yaml (ignores global collections)").option("-p, --port <port>", "port to serve on", "4321").action(
|
|
567
|
-
withErrors(async (opts) => {
|
|
568
|
-
await runPreview({ cwd: process.cwd(), port: Number(opts.port) });
|
|
569
|
-
})
|
|
559
|
+
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)));
|
|
560
|
+
plugin.command("list").description("List installed and available search plugins").action(withErrors(pluginListAction));
|
|
561
|
+
plugin.command("remove").description("Remove a search plugin").argument("<name>", "plugin name").action(withErrors((name) => pluginRemoveAction(name)));
|
|
562
|
+
const site = program.command("site").description("Build and preview the published site");
|
|
563
|
+
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(
|
|
564
|
+
withErrors(
|
|
565
|
+
(opts) => exportAction(opts)
|
|
566
|
+
)
|
|
570
567
|
);
|
|
568
|
+
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)));
|
|
569
|
+
const ui = program.command("ui").description("Run the local diffwiki app server");
|
|
570
|
+
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)));
|
|
571
|
+
ui.command("down").description("Stop the running diffwiki UI").action(withErrors(downAction));
|
|
572
|
+
ui.command("status").description("Show whether the diffwiki UI is running").action(withErrors(statusAction));
|
|
573
|
+
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)));
|
|
574
|
+
const config = program.command("config").description("Read and write diffwiki config");
|
|
575
|
+
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)));
|
|
576
|
+
config.command("get").description("Read a config value").argument("<key>", "config key").action(withErrors((key) => configGetAction(key)));
|
|
577
|
+
program.command("doctor").description("Diagnose configuration issues").action(withErrors(doctorAction));
|
|
571
578
|
return program;
|
|
572
579
|
}
|
|
573
580
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "diffwiki",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0-rc.202607301110.42cca4d",
|
|
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.10.0-rc.202607301110.42cca4d"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"@types/node": "^22.0.0"
|