diffwiki 0.4.0 → 0.5.0-rc.202607230329.fb70592
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 +189 -13
- package/package.json +4 -2
package/dist/bin.js
CHANGED
|
@@ -3,16 +3,26 @@
|
|
|
3
3
|
// src/cli.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
import {
|
|
6
|
+
addExternalCollection,
|
|
6
7
|
addTags,
|
|
8
|
+
collectionGitStatus,
|
|
7
9
|
createArticle,
|
|
8
10
|
createCollection,
|
|
9
11
|
doctor,
|
|
10
12
|
getConfigValue,
|
|
11
13
|
initRepoWiki,
|
|
14
|
+
installPlugin,
|
|
12
15
|
listCollections,
|
|
13
|
-
|
|
16
|
+
listAvailablePlugins,
|
|
17
|
+
listPlugins,
|
|
18
|
+
listSearchModes,
|
|
19
|
+
loadSearchProvider,
|
|
20
|
+
search,
|
|
21
|
+
registerPlugin,
|
|
14
22
|
removeArticle,
|
|
23
|
+
removePlugin,
|
|
15
24
|
removeTags,
|
|
25
|
+
resolveCwdCollection,
|
|
16
26
|
setConfigValue,
|
|
17
27
|
setTags,
|
|
18
28
|
updateArticleBody
|
|
@@ -143,11 +153,38 @@ async function stopUi() {
|
|
|
143
153
|
return state;
|
|
144
154
|
}
|
|
145
155
|
|
|
156
|
+
// src/exporter.ts
|
|
157
|
+
import { spawn as spawn2 } from "child_process";
|
|
158
|
+
import fs2 from "fs";
|
|
159
|
+
import path2 from "path";
|
|
160
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
161
|
+
function localAppExporter() {
|
|
162
|
+
const candidates = [
|
|
163
|
+
path2.resolve(path2.dirname(fileURLToPath2(import.meta.url)), "../../../apps/wiki/bin/export.mjs"),
|
|
164
|
+
path2.resolve(process.cwd(), "apps/wiki/bin/export.mjs")
|
|
165
|
+
];
|
|
166
|
+
return candidates.find((p) => fs2.existsSync(p));
|
|
167
|
+
}
|
|
168
|
+
async function runExport(opts) {
|
|
169
|
+
const out = path2.resolve(opts.out);
|
|
170
|
+
const local = localAppExporter();
|
|
171
|
+
const override = process.env.DIFFWIKI_APP_CMD ?? (local ? `node ${local}` : void 0);
|
|
172
|
+
const [cmd, ...prefix] = override ? override.split(" ") : ["npx", "-y", "diffwiki-app@latest"];
|
|
173
|
+
const args = [...prefix, "export", "--out", out, "--base", opts.base];
|
|
174
|
+
if (override) console.log(`Exporting via ${override}`);
|
|
175
|
+
await new Promise((resolve, reject) => {
|
|
176
|
+
const child = spawn2(cmd, args, { stdio: "inherit", env: process.env });
|
|
177
|
+
child.on("error", reject);
|
|
178
|
+
child.on("close", (code) => code === 0 ? resolve() : reject(new Error(`export failed (exit ${code})`)));
|
|
179
|
+
});
|
|
180
|
+
console.log(`Static site written to ${out}`);
|
|
181
|
+
}
|
|
182
|
+
|
|
146
183
|
// src/cli.ts
|
|
147
184
|
var icon = (level) => level === "ok" ? "\u2713" : level === "warn" ? "!" : "\u2716";
|
|
148
185
|
function createProgram() {
|
|
149
186
|
const program = new Command();
|
|
150
|
-
program.name("diffwiki").description("A git-based knowledge base").version(version).showHelpAfterError();
|
|
187
|
+
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");
|
|
151
188
|
program.action(() => {
|
|
152
189
|
program.outputHelp();
|
|
153
190
|
});
|
|
@@ -159,17 +196,28 @@ function createProgram() {
|
|
|
159
196
|
return;
|
|
160
197
|
}
|
|
161
198
|
for (const c of collections) {
|
|
162
|
-
|
|
199
|
+
const status = await collectionGitStatus(c);
|
|
200
|
+
const branch = status?.branch ? ` (${status.branch})` : "";
|
|
201
|
+
console.log(`${c.name} [${c.type}] ${c.path}${branch}`);
|
|
163
202
|
}
|
|
164
203
|
})
|
|
165
204
|
);
|
|
205
|
+
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(
|
|
206
|
+
withErrors(async (opts) => {
|
|
207
|
+
const entry = await addExternalCollection({ cwd: process.cwd(), docsDir: opts.docs });
|
|
208
|
+
const status = await collectionGitStatus(entry);
|
|
209
|
+
const branch = status?.branch ? ` (${status.branch})` : "";
|
|
210
|
+
console.log(`Added "${entry.name}"${branch} \u2192 ${entry.path}`);
|
|
211
|
+
console.log("Registered globally \u2014 discoverable via: diffwiki list");
|
|
212
|
+
})
|
|
213
|
+
);
|
|
166
214
|
program.command("create").description("Create a new global collection").argument("<name>", "collection name").action(
|
|
167
215
|
withErrors(async (name) => {
|
|
168
216
|
const entry = await createCollection(name);
|
|
169
217
|
console.log(`Created collection "${entry.name}" at ${entry.path}`);
|
|
170
218
|
})
|
|
171
219
|
);
|
|
172
|
-
program.command("add").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(
|
|
220
|
+
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(
|
|
173
221
|
withErrors(async (body, opts) => {
|
|
174
222
|
const article = await createArticle({
|
|
175
223
|
target: opts.path,
|
|
@@ -179,13 +227,13 @@ function createProgram() {
|
|
|
179
227
|
console.log(article.path);
|
|
180
228
|
})
|
|
181
229
|
);
|
|
182
|
-
program.command("update").description("Replace an article's body").requiredOption("-p, --path <target>", '"collection:path"').argument("<body>", "new markdown body").action(
|
|
230
|
+
program.command("update-article").description("Replace an article's body").requiredOption("-p, --path <target>", '"collection:path"').argument("<body>", "new markdown body").action(
|
|
183
231
|
withErrors(async (body, opts) => {
|
|
184
232
|
const article = await updateArticleBody(opts.path, body);
|
|
185
233
|
console.log(`Updated ${article.path}`);
|
|
186
234
|
})
|
|
187
235
|
);
|
|
188
|
-
program.command("remove").description("Delete an article").requiredOption("-p, --path <target>", '"collection:path"').action(
|
|
236
|
+
program.command("remove-article").description("Delete an article").requiredOption("-p, --path <target>", '"collection:path"').action(
|
|
189
237
|
withErrors(async (opts) => {
|
|
190
238
|
const removed = await removeArticle(opts.path);
|
|
191
239
|
console.log(`Removed ${removed}`);
|
|
@@ -202,15 +250,89 @@ function createProgram() {
|
|
|
202
250
|
registerTagCommand("add-tags", addTags);
|
|
203
251
|
registerTagCommand("remove-tags", removeTags);
|
|
204
252
|
registerTagCommand("update-tags", setTags);
|
|
205
|
-
program.command("
|
|
253
|
+
program.command("index").description("Rebuild the search index via the active plugin").option("--embed", "also build vector embeddings (may download models on first run)").action(
|
|
254
|
+
withErrors(async (opts) => {
|
|
255
|
+
const provider = await loadSearchProvider();
|
|
256
|
+
if (!provider) {
|
|
257
|
+
const installable = (await listSearchModes()).filter((m) => m.plugin !== "native");
|
|
258
|
+
console.log(
|
|
259
|
+
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>"
|
|
260
|
+
);
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
const indexed = await provider.reindex(await listCollections(), { embed: !!opts.embed });
|
|
264
|
+
console.log(`Indexed ${indexed} document(s).`);
|
|
265
|
+
})
|
|
266
|
+
);
|
|
267
|
+
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(
|
|
206
268
|
withErrors(async (term, opts) => {
|
|
207
|
-
const
|
|
208
|
-
|
|
269
|
+
const queryOpts = { engine: opts.engine, collection: opts.collection, type: opts.type };
|
|
270
|
+
const outcome = await search(term, queryOpts);
|
|
271
|
+
const hits = outcome.hits;
|
|
272
|
+
console.log(`${hits.length} result(s)`);
|
|
209
273
|
for (const hit of hits) {
|
|
210
|
-
|
|
274
|
+
const score = typeof hit.score === "number" ? hit.score.toFixed(3) : "-";
|
|
275
|
+
const line = `${score} ${hit.collection} ${hit.title} ${hit.path}`;
|
|
276
|
+
console.log(hit.snippet ? `${line} ${hit.snippet}` : line);
|
|
277
|
+
}
|
|
278
|
+
if (outcome.error) {
|
|
279
|
+
console.error(`\u26A0 engine "${outcome.engine}" failed: ${outcome.error} \u2014 used native (${hits.length} results)`);
|
|
280
|
+
}
|
|
281
|
+
})
|
|
282
|
+
);
|
|
283
|
+
const plugin = program.command("plugin").description("Manage search plugins");
|
|
284
|
+
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("--prefetch-models", "pre-download models during setup (heavy)").action(
|
|
285
|
+
withErrors(async (spec, opts) => {
|
|
286
|
+
const { record, setup } = await installPlugin(spec, {
|
|
287
|
+
setup: opts.setup,
|
|
288
|
+
prefetchModels: opts.prefetchModels
|
|
289
|
+
});
|
|
290
|
+
console.log(`Installed "${record.name}"${record.version ? ` v${record.version}` : ""}`);
|
|
291
|
+
if (!opts.setup) {
|
|
292
|
+
console.log("Skipped setup (--no-setup) \u2014 run: diffwiki index");
|
|
293
|
+
} else if (setup && !setup.ready) {
|
|
294
|
+
const reason = setup.steps.find((s) => !s.ok)?.message ?? "setup did not complete";
|
|
295
|
+
console.log(`\u26A0 Not ready: ${reason}`);
|
|
296
|
+
console.log(" \u2192 run: diffwiki doctor");
|
|
211
297
|
}
|
|
212
298
|
})
|
|
213
299
|
);
|
|
300
|
+
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(
|
|
301
|
+
withErrors(async (name, opts) => {
|
|
302
|
+
const record = await registerPlugin(name, opts.command);
|
|
303
|
+
console.log(`Registered "${record.name}" \u2192 ${record.command.join(" ")}`);
|
|
304
|
+
})
|
|
305
|
+
);
|
|
306
|
+
plugin.command("list").description("List installed and available search plugins").action(
|
|
307
|
+
withErrors(async () => {
|
|
308
|
+
const installed = await listPlugins();
|
|
309
|
+
const installedNames = new Set(installed.map((p) => p.name));
|
|
310
|
+
console.log("Installed:");
|
|
311
|
+
if (installed.length === 0) {
|
|
312
|
+
console.log(" (none)");
|
|
313
|
+
} else {
|
|
314
|
+
for (const p of installed) {
|
|
315
|
+
console.log(
|
|
316
|
+
` ${p.name} ${p.kind} ${p.version ?? "-"} enabled=${p.enabled} managed=${!!p.managed} spawnable=${p.spawnable}`
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
const available = listAvailablePlugins().filter((k) => !installedNames.has(k.name));
|
|
321
|
+
if (available.length > 0) {
|
|
322
|
+
console.log("\nAvailable (install with: diffwiki plugin install <name>):");
|
|
323
|
+
for (const k of available) {
|
|
324
|
+
console.log(` ${k.name} [${k.kind}] ${k.description}`);
|
|
325
|
+
console.log(` requires ${k.requires ?? "nothing"}`);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
})
|
|
329
|
+
);
|
|
330
|
+
plugin.command("remove").description("Remove a search plugin").argument("<name>", "plugin name").action(
|
|
331
|
+
withErrors(async (name) => {
|
|
332
|
+
await removePlugin(name);
|
|
333
|
+
console.log(`Removed "${name}"`);
|
|
334
|
+
})
|
|
335
|
+
);
|
|
214
336
|
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(
|
|
215
337
|
withErrors(async (opts) => {
|
|
216
338
|
const entry = await initRepoWiki({
|
|
@@ -222,7 +344,12 @@ function createProgram() {
|
|
|
222
344
|
console.log("Registered globally \u2014 discoverable via: diffwiki list");
|
|
223
345
|
})
|
|
224
346
|
);
|
|
225
|
-
program.command("
|
|
347
|
+
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)", "/").action(
|
|
348
|
+
withErrors(async (opts) => {
|
|
349
|
+
await runExport({ out: opts.out, base: opts.base });
|
|
350
|
+
})
|
|
351
|
+
);
|
|
352
|
+
program.command("config-set").description("Set a config value (e.g. defaultCollection, or defaultSearch <engine[:type]>)").argument("<key>", "config key").argument("<value>", "config value").action(
|
|
226
353
|
withErrors(async (key, value) => {
|
|
227
354
|
await setConfigValue(key, value);
|
|
228
355
|
console.log(`Set ${key} = ${value}`);
|
|
@@ -282,12 +409,61 @@ function createProgram() {
|
|
|
282
409
|
state = await startUi(Number(opts.port));
|
|
283
410
|
await waitForServer(state.url);
|
|
284
411
|
}
|
|
285
|
-
|
|
286
|
-
|
|
412
|
+
const coll = await resolveCwdCollection(process.cwd());
|
|
413
|
+
const target = coll ? `${state.url}/${encodeURIComponent(coll.name)}` : state.url;
|
|
414
|
+
openBrowser(target);
|
|
415
|
+
console.log(`Opening ${target}`);
|
|
287
416
|
})
|
|
288
417
|
);
|
|
289
418
|
return program;
|
|
290
419
|
}
|
|
291
420
|
|
|
421
|
+
// src/logging.ts
|
|
422
|
+
import { mkdirSync } from "fs";
|
|
423
|
+
import os from "os";
|
|
424
|
+
import path3 from "path";
|
|
425
|
+
import { configureSync, getConfig } from "@logtape/logtape";
|
|
426
|
+
import { getRotatingFileSink } from "@logtape/file";
|
|
427
|
+
import { resolveLogLevel } from "diffwiki-core";
|
|
428
|
+
var configured = false;
|
|
429
|
+
function diffwikiHome() {
|
|
430
|
+
const override = process.env.DIFFWIKI_HOME;
|
|
431
|
+
return override && override.length > 0 ? override : path3.join(os.homedir(), ".diffwiki");
|
|
432
|
+
}
|
|
433
|
+
function resolveCliLevel(argv) {
|
|
434
|
+
if (argv.includes("-v") || argv.includes("--verbose")) return "debug";
|
|
435
|
+
const flagIdx = argv.indexOf("--log");
|
|
436
|
+
const flagValue = flagIdx !== -1 ? argv[flagIdx + 1] : void 0;
|
|
437
|
+
return resolveLogLevel(flagValue ? { DIFFWIKI_LOG: flagValue } : process.env);
|
|
438
|
+
}
|
|
439
|
+
function formatLine(record) {
|
|
440
|
+
const category = record.category.join(".");
|
|
441
|
+
const message = record.message.map((part) => typeof part === "string" ? part : String(part)).join("");
|
|
442
|
+
return `${record.level.toUpperCase()} [${category}] ${message}`;
|
|
443
|
+
}
|
|
444
|
+
function configureCliLogging(argv = process.argv) {
|
|
445
|
+
if (configured || getConfig() != null) return;
|
|
446
|
+
configured = true;
|
|
447
|
+
const level = resolveCliLevel(argv);
|
|
448
|
+
const logsDir = path3.join(diffwikiHome(), "logs");
|
|
449
|
+
mkdirSync(logsDir, { recursive: true });
|
|
450
|
+
const logFile = path3.join(logsDir, "diffwiki.log");
|
|
451
|
+
const stderrSink = (record) => {
|
|
452
|
+
process.stderr.write(formatLine(record) + "\n");
|
|
453
|
+
};
|
|
454
|
+
configureSync({
|
|
455
|
+
sinks: {
|
|
456
|
+
stderr: stderrSink,
|
|
457
|
+
file: getRotatingFileSink(logFile)
|
|
458
|
+
},
|
|
459
|
+
loggers: [
|
|
460
|
+
{ category: ["diffwiki"], lowestLevel: level, sinks: ["stderr", "file"] },
|
|
461
|
+
// Silence everything else (incl. LogTape meta logs).
|
|
462
|
+
{ category: [], lowestLevel: "fatal", sinks: [] }
|
|
463
|
+
]
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
|
|
292
467
|
// src/bin.ts
|
|
468
|
+
configureCliLogging(process.argv);
|
|
293
469
|
createProgram().parseAsync(process.argv);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "diffwiki",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0-rc.202607230329.fb70592",
|
|
4
4
|
"description": "diffwiki command-line interface",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -14,8 +14,10 @@
|
|
|
14
14
|
"access": "public"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
+
"@logtape/logtape": "^2.2.4",
|
|
18
|
+
"@logtape/file": "^2.2.4",
|
|
17
19
|
"commander": "^13.0.0",
|
|
18
|
-
"diffwiki-core": "0.
|
|
20
|
+
"diffwiki-core": "0.5.0-rc.202607230329.fb70592"
|
|
19
21
|
},
|
|
20
22
|
"devDependencies": {
|
|
21
23
|
"@types/node": "^22.0.0"
|