pi-para 0.5.2 → 0.6.0
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/ARCHITECTURE.md +160 -0
- package/CHANGELOG.md +51 -0
- package/CONFIGURATION.md +243 -0
- package/README.md +140 -237
- package/SETUP.md +196 -112
- package/STRUCTURE.md +140 -0
- package/package.json +86 -18
- package/scripts/build-assets.mjs +29 -0
- package/scripts/install.ps1 +55 -0
- package/scripts/install.sh +63 -0
- package/scripts/smoke-install.mjs +63 -0
- package/skills/para/SKILL.md +1 -1
- package/skills/setup/SKILL.md +1 -1
- package/src/atomic-write.ts +17 -0
- package/src/capture.ts +4 -4
- package/src/cli-legacy.ts +43 -0
- package/src/cli.ts +133 -28
- package/src/commands.ts +203 -243
- package/src/config.ts +355 -0
- package/src/context.ts +29 -14
- package/src/credentials.ts +100 -0
- package/src/daemon.ts +1 -1
- package/src/doctor.ts +209 -0
- package/src/gepa/index.ts +1 -1
- package/src/index.ts +119 -40
- package/src/maintainer.ts +4 -4
- package/src/model-resolver.ts +96 -0
- package/src/paths.ts +48 -0
- package/src/processor.ts +4 -4
- package/src/qmd-providers.ts +63 -0
- package/src/query.ts +15 -1
- package/src/repair.ts +49 -0
- package/src/scheduler/controls.ts +125 -0
- package/src/scheduler/index.ts +210 -0
- package/src/scheduler/leases.ts +98 -0
- package/src/scheduler/session-capture.ts +146 -0
- package/src/scheduler/state.ts +304 -0
- package/src/session-tools.ts +1 -1
- package/src/settings.ts +126 -0
- package/src/setup.ts +116 -0
- package/src/status.ts +115 -0
- package/src/store.ts +116 -31
- package/src/summarize.ts +72 -16
- package/src/templates/prompts.ts +1 -1
- package/src/tools.ts +280 -90
- package/src/wiki-tool-guidance.ts +84 -0
- package/src/wiki.ts +98 -26
- package/dist/capture.d.ts +0 -34
- package/dist/capture.d.ts.map +0 -1
- package/dist/capture.js +0 -461
- package/dist/capture.js.map +0 -1
- package/dist/cli.d.ts +0 -15
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js +0 -405
- package/dist/cli.js.map +0 -1
- package/dist/commands.d.ts +0 -15
- package/dist/commands.d.ts.map +0 -1
- package/dist/commands.js +0 -786
- package/dist/commands.js.map +0 -1
- package/dist/context.d.ts +0 -61
- package/dist/context.d.ts.map +0 -1
- package/dist/context.js +0 -289
- package/dist/context.js.map +0 -1
- package/dist/daemon.d.ts +0 -49
- package/dist/daemon.d.ts.map +0 -1
- package/dist/daemon.js +0 -226
- package/dist/daemon.js.map +0 -1
- package/dist/frontmatter.d.ts +0 -59
- package/dist/frontmatter.d.ts.map +0 -1
- package/dist/frontmatter.js +0 -170
- package/dist/frontmatter.js.map +0 -1
- package/dist/gepa/dataset.d.ts +0 -36
- package/dist/gepa/dataset.d.ts.map +0 -1
- package/dist/gepa/dataset.js +0 -148
- package/dist/gepa/dataset.js.map +0 -1
- package/dist/gepa/evaluator.d.ts +0 -51
- package/dist/gepa/evaluator.d.ts.map +0 -1
- package/dist/gepa/evaluator.js +0 -317
- package/dist/gepa/evaluator.js.map +0 -1
- package/dist/gepa/index.d.ts +0 -66
- package/dist/gepa/index.d.ts.map +0 -1
- package/dist/gepa/index.js +0 -240
- package/dist/gepa/index.js.map +0 -1
- package/dist/gepa/llm.d.ts +0 -35
- package/dist/gepa/llm.d.ts.map +0 -1
- package/dist/gepa/llm.js +0 -268
- package/dist/gepa/llm.js.map +0 -1
- package/dist/gepa/optimized/capture-prompt.txt +0 -65
- package/dist/gepa/optimizer.d.ts +0 -29
- package/dist/gepa/optimizer.d.ts.map +0 -1
- package/dist/gepa/optimizer.js +0 -249
- package/dist/gepa/optimizer.js.map +0 -1
- package/dist/gepa/targets.d.ts +0 -39
- package/dist/gepa/targets.d.ts.map +0 -1
- package/dist/gepa/targets.js +0 -326
- package/dist/gepa/targets.js.map +0 -1
- package/dist/gepa/types.d.ts +0 -168
- package/dist/gepa/types.d.ts.map +0 -1
- package/dist/gepa/types.js +0 -124
- package/dist/gepa/types.js.map +0 -1
- package/dist/index.d.ts +0 -21
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -294
- package/dist/index.js.map +0 -1
- package/dist/ingest.d.ts +0 -88
- package/dist/ingest.d.ts.map +0 -1
- package/dist/ingest.js +0 -105
- package/dist/ingest.js.map +0 -1
- package/dist/link-discovery.d.ts +0 -40
- package/dist/link-discovery.d.ts.map +0 -1
- package/dist/link-discovery.js +0 -122
- package/dist/link-discovery.js.map +0 -1
- package/dist/link-utils.d.ts +0 -30
- package/dist/link-utils.d.ts.map +0 -1
- package/dist/link-utils.js +0 -145
- package/dist/link-utils.js.map +0 -1
- package/dist/lint.d.ts +0 -39
- package/dist/lint.d.ts.map +0 -1
- package/dist/lint.js +0 -827
- package/dist/lint.js.map +0 -1
- package/dist/maintainer.d.ts +0 -24
- package/dist/maintainer.d.ts.map +0 -1
- package/dist/maintainer.js +0 -358
- package/dist/maintainer.js.map +0 -1
- package/dist/processor.d.ts +0 -14
- package/dist/processor.d.ts.map +0 -1
- package/dist/processor.js +0 -361
- package/dist/processor.js.map +0 -1
- package/dist/query.d.ts +0 -69
- package/dist/query.d.ts.map +0 -1
- package/dist/query.js +0 -107
- package/dist/query.js.map +0 -1
- package/dist/raw.d.ts +0 -57
- package/dist/raw.d.ts.map +0 -1
- package/dist/raw.js +0 -219
- package/dist/raw.js.map +0 -1
- package/dist/redact.d.ts +0 -21
- package/dist/redact.d.ts.map +0 -1
- package/dist/redact.js +0 -143
- package/dist/redact.js.map +0 -1
- package/dist/scope.d.ts +0 -54
- package/dist/scope.d.ts.map +0 -1
- package/dist/scope.js +0 -161
- package/dist/scope.js.map +0 -1
- package/dist/scripts/bulk-link-fix.d.ts +0 -9
- package/dist/scripts/bulk-link-fix.d.ts.map +0 -1
- package/dist/scripts/bulk-link-fix.js +0 -106
- package/dist/scripts/bulk-link-fix.js.map +0 -1
- package/dist/session-tools.d.ts +0 -33
- package/dist/session-tools.d.ts.map +0 -1
- package/dist/session-tools.js +0 -211
- package/dist/session-tools.js.map +0 -1
- package/dist/state.d.ts +0 -54
- package/dist/state.d.ts.map +0 -1
- package/dist/state.js +0 -176
- package/dist/state.js.map +0 -1
- package/dist/store.d.ts +0 -56
- package/dist/store.d.ts.map +0 -1
- package/dist/store.js +0 -227
- package/dist/store.js.map +0 -1
- package/dist/summarize.d.ts +0 -56
- package/dist/summarize.d.ts.map +0 -1
- package/dist/summarize.js +0 -231
- package/dist/summarize.js.map +0 -1
- package/dist/tag-registry.d.ts +0 -56
- package/dist/tag-registry.d.ts.map +0 -1
- package/dist/tag-registry.js +0 -146
- package/dist/tag-registry.js.map +0 -1
- package/dist/templates/prompts.d.ts +0 -63
- package/dist/templates/prompts.d.ts.map +0 -1
- package/dist/templates/prompts.js +0 -260
- package/dist/templates/prompts.js.map +0 -1
- package/dist/tools.d.ts +0 -26
- package/dist/tools.d.ts.map +0 -1
- package/dist/tools.js +0 -1033
- package/dist/tools.js.map +0 -1
- package/dist/watcher.d.ts +0 -25
- package/dist/watcher.d.ts.map +0 -1
- package/dist/watcher.js +0 -84
- package/dist/watcher.js.map +0 -1
- package/dist/webui/client/dist/index.html +0 -1885
- package/dist/webui/server.d.ts +0 -31
- package/dist/webui/server.d.ts.map +0 -1
- package/dist/webui/server.js +0 -566
- package/dist/webui/server.js.map +0 -1
- package/dist/wiki.d.ts +0 -103
- package/dist/wiki.d.ts.map +0 -1
- package/dist/wiki.js +0 -428
- package/dist/wiki.js.map +0 -1
- package/scripts/gepa/__pycache__/dataset.cpython-313.pyc +0 -0
- package/scripts/gepa/__pycache__/lm_providers.cpython-313.pyc +0 -0
- package/scripts/gepa/__pycache__/metric.cpython-313.pyc +0 -0
- package/scripts/gepa/__pycache__/program.cpython-313.pyc +0 -0
- package/scripts/gepa/__pycache__/wiki_reader.cpython-313.pyc +0 -0
- package/scripts/gepa/uv.lock +0 -2241
package/src/commands.ts
CHANGED
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Commands: /wiki, /wiki-ingest, /wiki-lint, /wiki-capture,
|
|
5
5
|
* /wiki-scope, /wiki-search, /wiki-summarize,
|
|
6
|
-
* /wiki-daemon, /wiki-settings
|
|
6
|
+
* /wiki-scheduler, /wiki-daemon compatibility alias, /wiki-settings
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { readFile
|
|
9
|
+
import { readFile } from "node:fs/promises";
|
|
10
10
|
import { join } from "node:path";
|
|
11
11
|
|
|
12
12
|
import type { QMDStore } from "qmd-engine";
|
|
13
|
-
import type { ExtensionAPI } from "@
|
|
13
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
14
14
|
|
|
15
15
|
import type { ProjectScope } from "./scope.js";
|
|
16
16
|
import type { LintReport } from "./lint.js";
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
parseFrontmatter,
|
|
25
25
|
validateFrontmatter,
|
|
26
26
|
} from "./frontmatter.js";
|
|
27
|
+
import { appendCompletedSession } from "./scheduler/session-capture.js";
|
|
27
28
|
|
|
28
29
|
// -- Helpers -----------------------------------------------------------------
|
|
29
30
|
|
|
@@ -261,7 +262,7 @@ export function registerCommands(
|
|
|
261
262
|
// ---- /wiki-capture — capture via session LLM ----------------------------
|
|
262
263
|
|
|
263
264
|
pi.registerCommand("wiki-capture", {
|
|
264
|
-
description: "
|
|
265
|
+
description: "Queue this session for scheduler-backed wiki capture",
|
|
265
266
|
handler: async (args, ctx) => {
|
|
266
267
|
const sessionFile = ctx.sessionManager.getSessionFile();
|
|
267
268
|
if (!sessionFile) {
|
|
@@ -269,16 +270,14 @@ export function registerCommands(
|
|
|
269
270
|
return;
|
|
270
271
|
}
|
|
271
272
|
|
|
272
|
-
// Register the session for
|
|
273
|
+
// Register the session for scheduler capture — runs in background, doesn't block user
|
|
273
274
|
try {
|
|
274
|
-
|
|
275
|
-
const entry = `${new Date().toISOString()}|${sessionFile}\n`;
|
|
276
|
-
await appendFile(registry, entry);
|
|
275
|
+
await appendCompletedSession(wikiDir, sessionFile);
|
|
277
276
|
|
|
278
277
|
ctx.ui.notify(
|
|
279
|
-
"Session queued for wiki capture (
|
|
280
|
-
"Check progress: /wiki-
|
|
281
|
-
"Tip: Set
|
|
278
|
+
"Session queued for wiki capture (scheduler will process when Pi is open).\n" +
|
|
279
|
+
"Check progress: /wiki-scheduler status\n" +
|
|
280
|
+
"Tip: Set the capture model in /wiki-settings for better capture quality.",
|
|
282
281
|
"info",
|
|
283
282
|
);
|
|
284
283
|
ctx.ui.setStatus("pi-para", "wiki: capture queued");
|
|
@@ -386,86 +385,80 @@ export function registerCommands(
|
|
|
386
385
|
},
|
|
387
386
|
});
|
|
388
387
|
|
|
389
|
-
// ---- /wiki-
|
|
388
|
+
// ---- /wiki-scheduler — scheduler/capture queue status ----------------------
|
|
389
|
+
|
|
390
|
+
const registerSchedulerStatusCommand = (name: "wiki-scheduler" | "wiki-daemon", legacy: boolean) => {
|
|
391
|
+
pi.registerCommand(name, {
|
|
392
|
+
description: legacy
|
|
393
|
+
? "Compatibility alias for /wiki-scheduler"
|
|
394
|
+
: "Check scheduler queue, task history, and capture status",
|
|
395
|
+
getArgumentCompletions: (prefix) => {
|
|
396
|
+
const cmds = ["status", "queue", "history", "capture-history"];
|
|
397
|
+
const filtered = cmds.filter((c) => c.startsWith(prefix));
|
|
398
|
+
return filtered.length > 0 ? filtered.map((c) => ({ value: c, label: c })) : null;
|
|
399
|
+
},
|
|
400
|
+
handler: async (args, ctx) => {
|
|
401
|
+
const subcmd = args.trim().split(/\s+/)[0] || "status";
|
|
402
|
+
const { listSchedulerTasks, listSchedulerHistory, formatQueueItems, formatSchedulerHistory } = await import("./scheduler/controls.js");
|
|
403
|
+
const { StateDB } = await import("./state.js");
|
|
390
404
|
|
|
391
|
-
pi.registerCommand("wiki-daemon", {
|
|
392
|
-
description: "Check daemon status and capture history",
|
|
393
|
-
getArgumentCompletions: (prefix) => {
|
|
394
|
-
const cmds = ["status", "history"];
|
|
395
|
-
const filtered = cmds.filter((c) => c.startsWith(prefix));
|
|
396
|
-
return filtered.length > 0 ? filtered.map((c) => ({ value: c, label: c })) : null;
|
|
397
|
-
},
|
|
398
|
-
handler: async (args, ctx) => {
|
|
399
|
-
const subcmd = args.trim().split(/\s+/)[0] || "status";
|
|
400
|
-
|
|
401
|
-
// Read state directly from the state DB — fast, no subprocess needed
|
|
402
|
-
const { StateDB } = await import("./state.js");
|
|
403
|
-
const state = new StateDB(wikiDir);
|
|
404
|
-
|
|
405
|
-
try {
|
|
406
405
|
switch (subcmd) {
|
|
407
406
|
case "status": {
|
|
408
|
-
const
|
|
409
|
-
const
|
|
410
|
-
const
|
|
411
|
-
const
|
|
412
|
-
|
|
413
|
-
let running = false;
|
|
414
|
-
if (pid) {
|
|
415
|
-
try { process.kill(parseInt(pid), 0); running = true; } catch {}
|
|
416
|
-
}
|
|
417
|
-
|
|
407
|
+
const queued = listSchedulerTasks(wikiDir, { status: "queued" }).length;
|
|
408
|
+
const running = listSchedulerTasks(wikiDir, { status: "running" }).length;
|
|
409
|
+
const failed = listSchedulerTasks(wikiDir, { status: "failed" }).length;
|
|
410
|
+
const recent = listSchedulerHistory(wikiDir, { limit: 5 });
|
|
418
411
|
const lines = [
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
`Failed: ${failed.length}`,
|
|
412
|
+
legacy ? "Scheduler: active (via /wiki-daemon compatibility alias)" : "Scheduler: active",
|
|
413
|
+
`Queue: ${queued} queued, ${running} running, ${failed} failed`,
|
|
422
414
|
"",
|
|
423
|
-
"Recent
|
|
415
|
+
"Recent scheduler history:",
|
|
416
|
+
formatSchedulerHistory(recent).split("\n").map((line) => ` ${line}`).join("\n"),
|
|
424
417
|
];
|
|
425
|
-
if (history.length === 0) {
|
|
426
|
-
lines.push(" (none)");
|
|
427
|
-
} else {
|
|
428
|
-
for (const h of history) {
|
|
429
|
-
const status = h.error
|
|
430
|
-
? `ERROR: ${h.error.slice(0, 50)}`
|
|
431
|
-
: h.pagesCreated.length > 0
|
|
432
|
-
? `${h.pagesCreated.length} page(s)`
|
|
433
|
-
: "skipped";
|
|
434
|
-
lines.push(` ${h.processedAt.slice(0, 19)} | ${h.scope} | ${status}`);
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
418
|
ctx.ui.notify(lines.filter(Boolean).join("\n"), "info");
|
|
438
419
|
break;
|
|
439
420
|
}
|
|
421
|
+
case "queue": {
|
|
422
|
+
ctx.ui.notify(formatQueueItems(listSchedulerTasks(wikiDir)), "info");
|
|
423
|
+
break;
|
|
424
|
+
}
|
|
440
425
|
case "history": {
|
|
426
|
+
ctx.ui.notify(formatSchedulerHistory(listSchedulerHistory(wikiDir, { limit: 15 })), "info");
|
|
427
|
+
break;
|
|
428
|
+
}
|
|
429
|
+
case "capture-history": {
|
|
441
430
|
const scope = getScope();
|
|
442
|
-
const
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
431
|
+
const state = new StateDB(wikiDir);
|
|
432
|
+
try {
|
|
433
|
+
const history = state.getHistory(scope.name, 15);
|
|
434
|
+
if (history.length === 0) {
|
|
435
|
+
ctx.ui.notify(`No capture history for scope: ${scope.name}`, "info");
|
|
436
|
+
break;
|
|
437
|
+
}
|
|
438
|
+
const lines = [`Capture history (${scope.name}):\n`];
|
|
439
|
+
for (const h of history) {
|
|
440
|
+
const status = h.error
|
|
441
|
+
? `ERROR: ${h.error.slice(0, 60)}`
|
|
442
|
+
: h.pagesCreated.length > 0
|
|
443
|
+
? `${h.pagesCreated.length} page(s): ${h.pagesCreated.join(", ")}`
|
|
444
|
+
: "skipped";
|
|
445
|
+
lines.push(`${h.processedAt.slice(0, 19)} | ${status}`);
|
|
446
|
+
}
|
|
447
|
+
ctx.ui.notify(lines.join("\n"), "info");
|
|
448
|
+
} finally {
|
|
449
|
+
state.close();
|
|
457
450
|
}
|
|
458
|
-
ctx.ui.notify(lines.join("\n"), "info");
|
|
459
451
|
break;
|
|
460
452
|
}
|
|
461
453
|
default:
|
|
462
|
-
ctx.ui.notify(`Usage:
|
|
454
|
+
ctx.ui.notify(`Usage: /${name} [status|queue|history|capture-history]`, "error");
|
|
463
455
|
}
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
456
|
+
},
|
|
457
|
+
});
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
registerSchedulerStatusCommand("wiki-scheduler", false);
|
|
461
|
+
registerSchedulerStatusCommand("wiki-daemon", true);
|
|
469
462
|
|
|
470
463
|
// ---- /wiki-migrate — batch schema migration ------------------------------
|
|
471
464
|
|
|
@@ -640,239 +633,206 @@ ${goal} — verified and complete.
|
|
|
640
633
|
return;
|
|
641
634
|
}
|
|
642
635
|
|
|
643
|
-
const {
|
|
644
|
-
const {
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
636
|
+
const { loadParaConfig, saveParaConfig } = await import("./config.js");
|
|
637
|
+
const {
|
|
638
|
+
modelSelectionLabel,
|
|
639
|
+
providerProfileLabel,
|
|
640
|
+
setContextMaxTokens,
|
|
641
|
+
setSearchLimit,
|
|
642
|
+
setLintStaleDays,
|
|
643
|
+
toggleSearchGraphBoost,
|
|
644
|
+
toggleLintAutoFix,
|
|
645
|
+
setCaptureModelAuto,
|
|
646
|
+
setCaptureModel,
|
|
647
|
+
setWebWikiEnabled,
|
|
648
|
+
setWebWikiHost,
|
|
649
|
+
setWebWikiPort,
|
|
650
|
+
ensureEmbeddingProfile,
|
|
651
|
+
ensureRerankProfile,
|
|
652
|
+
disableRerank,
|
|
653
|
+
setProviderProfileField,
|
|
654
|
+
setProviderDims,
|
|
655
|
+
} = await import("./settings.js");
|
|
656
|
+
const { listSchedulerTasks } = await import("./scheduler/controls.js");
|
|
657
|
+
const { setSecret } = await import("./credentials.js");
|
|
658
|
+
|
|
659
|
+
const loaded = await loadParaConfig({ migrate: true });
|
|
660
|
+
const config = loaded.config;
|
|
661
|
+
const persist = async () => saveParaConfig(config, { homeDir: loaded.paths.homeDir });
|
|
668
662
|
|
|
669
|
-
// Main menu loop
|
|
670
663
|
while (true) {
|
|
671
664
|
const scope = getScope();
|
|
672
665
|
const pageCount = (await listPages(wikiDir).catch(() => [])).length;
|
|
666
|
+
const queuedTasks = listSchedulerTasks(wikiDir, { status: "queued" }).length;
|
|
667
|
+
const failedTasks = listSchedulerTasks(wikiDir, { status: "failed" }).length;
|
|
673
668
|
|
|
674
669
|
const choice = await ctx.ui.select("Wiki Settings", [
|
|
675
|
-
`[Context] Max tokens: ${config.
|
|
676
|
-
`[Search] Limit: ${config.searchLimit
|
|
677
|
-
`[Lint] Auto-fix: ${config.
|
|
678
|
-
`[
|
|
679
|
-
`[WebWiki] ${
|
|
680
|
-
`[
|
|
681
|
-
`[
|
|
682
|
-
`[Rerank] ${providers.rerank?.model ?? "not configured"} ${providers.rerank?.url ? "at " + providers.rerank.url : ""}`,
|
|
670
|
+
`[Context] Max tokens: ${config.context.maxTokens}`,
|
|
671
|
+
`[Search] Limit: ${config.context.searchLimit}, Graph boost: ${config.context.searchGraphBoost}`,
|
|
672
|
+
`[Lint] Auto-fix: ${config.lint.autoFix}, Stale days: ${config.lint.staleDays}`,
|
|
673
|
+
`[Capture] Model: ${modelSelectionLabel(config.models.capture)}`,
|
|
674
|
+
`[WebWiki] ${config.webWiki.enabled ? `Enabled at http://${config.webWiki.host}:${config.webWiki.port}` : "Disabled"}`,
|
|
675
|
+
`[Embedding] ${providerProfileLabel(config.qmd.embedding)}`,
|
|
676
|
+
`[Rerank] ${providerProfileLabel(config.qmd.rerank)}`,
|
|
683
677
|
`---`,
|
|
684
|
-
`[Status] Scope: ${scope.name} | Pages: ${pageCount} | qmd:
|
|
678
|
+
`[Status] Scope: ${scope.name} | Pages: ${pageCount} | qmd: SDK | Scheduler: ${queuedTasks} queued, ${failedTasks} failed`,
|
|
685
679
|
]);
|
|
686
680
|
|
|
687
681
|
if (!choice) break;
|
|
688
682
|
|
|
689
683
|
if (choice.startsWith("[Context]")) {
|
|
690
|
-
const val = await ctx.ui.input("Context max tokens:", String(config.
|
|
684
|
+
const val = await ctx.ui.input("Context max tokens:", String(config.context.maxTokens));
|
|
691
685
|
if (val) {
|
|
692
|
-
config
|
|
693
|
-
await
|
|
694
|
-
ctx.ui.notify(`Set
|
|
686
|
+
setContextMaxTokens(config, val);
|
|
687
|
+
await persist();
|
|
688
|
+
ctx.ui.notify(`Set context.maxTokens = ${config.context.maxTokens}`, "info");
|
|
695
689
|
}
|
|
696
690
|
} else if (choice.startsWith("[Search]")) {
|
|
697
691
|
const sub = await ctx.ui.select("Search Settings", [
|
|
698
|
-
`Limit: ${config.searchLimit
|
|
699
|
-
`Graph boost: ${config.searchGraphBoost
|
|
692
|
+
`Limit: ${config.context.searchLimit}`,
|
|
693
|
+
`Graph boost: ${config.context.searchGraphBoost}`,
|
|
700
694
|
]);
|
|
701
695
|
if (sub?.startsWith("Limit")) {
|
|
702
|
-
const val = await ctx.ui.input("Search result limit:", String(config.searchLimit
|
|
696
|
+
const val = await ctx.ui.input("Search result limit:", String(config.context.searchLimit));
|
|
703
697
|
if (val) {
|
|
704
|
-
config
|
|
705
|
-
await
|
|
706
|
-
ctx.ui.notify(`Set searchLimit = ${config.searchLimit}`, "info");
|
|
698
|
+
setSearchLimit(config, val);
|
|
699
|
+
await persist();
|
|
700
|
+
ctx.ui.notify(`Set context.searchLimit = ${config.context.searchLimit}`, "info");
|
|
707
701
|
}
|
|
708
702
|
} else if (sub?.startsWith("Graph boost")) {
|
|
709
|
-
|
|
710
|
-
await
|
|
711
|
-
ctx.ui.notify(`Set searchGraphBoost = ${config.searchGraphBoost}`, "info");
|
|
703
|
+
toggleSearchGraphBoost(config);
|
|
704
|
+
await persist();
|
|
705
|
+
ctx.ui.notify(`Set context.searchGraphBoost = ${config.context.searchGraphBoost}`, "info");
|
|
712
706
|
}
|
|
713
707
|
} else if (choice.startsWith("[Lint]")) {
|
|
714
708
|
const sub = await ctx.ui.select("Lint Settings", [
|
|
715
|
-
`Auto-fix: ${config.
|
|
716
|
-
`Stale days: ${config.
|
|
709
|
+
`Auto-fix: ${config.lint.autoFix}`,
|
|
710
|
+
`Stale days: ${config.lint.staleDays}`,
|
|
717
711
|
]);
|
|
718
712
|
if (sub?.startsWith("Auto-fix")) {
|
|
719
|
-
|
|
720
|
-
await
|
|
721
|
-
ctx.ui.notify(`Set
|
|
713
|
+
toggleLintAutoFix(config);
|
|
714
|
+
await persist();
|
|
715
|
+
ctx.ui.notify(`Set lint.autoFix = ${config.lint.autoFix}`, "info");
|
|
722
716
|
} else if (sub?.startsWith("Stale")) {
|
|
723
|
-
const val = await ctx.ui.input("Stale days threshold:", String(config.
|
|
717
|
+
const val = await ctx.ui.input("Stale days threshold:", String(config.lint.staleDays));
|
|
724
718
|
if (val) {
|
|
725
|
-
config
|
|
726
|
-
await
|
|
727
|
-
ctx.ui.notify(`Set
|
|
719
|
+
setLintStaleDays(config, val);
|
|
720
|
+
await persist();
|
|
721
|
+
ctx.ui.notify(`Set lint.staleDays = ${config.lint.staleDays}`, "info");
|
|
728
722
|
}
|
|
729
723
|
}
|
|
730
|
-
} else if (choice.startsWith("[
|
|
724
|
+
} else if (choice.startsWith("[Capture]")) {
|
|
731
725
|
try {
|
|
732
|
-
const { getProviders, getModels } = await import("@
|
|
733
|
-
const { AuthStorage } = await import("@
|
|
726
|
+
const { getProviders, getModels } = await import("@earendil-works/pi-ai/compat");
|
|
727
|
+
const { AuthStorage } = await import("@earendil-works/pi-coding-agent");
|
|
734
728
|
const authStorage = AuthStorage.create();
|
|
735
|
-
|
|
736
|
-
// Find providers that have auth configured
|
|
737
|
-
const allProviders = getProviders();
|
|
738
|
-
const availableProviders: string[] = [];
|
|
739
|
-
for (const p of allProviders) {
|
|
740
|
-
if (authStorage.hasAuth(p)) availableProviders.push(p);
|
|
741
|
-
}
|
|
729
|
+
const availableProviders = getProviders().filter((p: string) => authStorage.hasAuth(p));
|
|
742
730
|
|
|
743
731
|
if (availableProviders.length === 0) {
|
|
744
|
-
ctx.ui.notify("No providers configured.
|
|
732
|
+
ctx.ui.notify("No Pi auth providers configured. Use /login first or configure a pi-para secret for custom providers.", "warning");
|
|
745
733
|
} else {
|
|
746
|
-
|
|
747
|
-
const providerChoice = await ctx.ui.select(
|
|
748
|
-
"Select provider",
|
|
749
|
-
["auto (best available)", ...availableProviders],
|
|
750
|
-
);
|
|
751
|
-
|
|
734
|
+
const providerChoice = await ctx.ui.select("Select capture provider", ["auto (best available)", ...availableProviders]);
|
|
752
735
|
if (providerChoice === "auto (best available)") {
|
|
753
|
-
config
|
|
754
|
-
await
|
|
755
|
-
|
|
756
|
-
ctx.ui.notify("Set daemonModel = auto (daemon restarted)", "info");
|
|
736
|
+
setCaptureModelAuto(config);
|
|
737
|
+
await persist();
|
|
738
|
+
ctx.ui.notify("Set capture model = auto. Restart open Pi sessions to apply.", "info");
|
|
757
739
|
} else if (providerChoice) {
|
|
758
|
-
// Show models for selected provider
|
|
759
740
|
const models = getModels(providerChoice as any)
|
|
760
741
|
.sort((a: any, b: any) => (b.contextWindow ?? 0) - (a.contextWindow ?? 0));
|
|
761
|
-
const modelOptions = models.map((m: any) =>
|
|
762
|
-
`${m.id} (${Math.round((m.contextWindow ?? 0) / 1000)}k ctx${m.reasoning ? ", reasoning" : ""})`
|
|
763
|
-
);
|
|
764
742
|
const modelChoice = await ctx.ui.select(
|
|
765
743
|
`Select ${providerChoice} model`,
|
|
766
|
-
|
|
744
|
+
models.map((m: any) => `${m.id} (${Math.round((m.contextWindow ?? 0) / 1000)}k ctx${m.reasoning ? ", reasoning" : ""})`),
|
|
767
745
|
);
|
|
768
746
|
if (modelChoice) {
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
try { execSync("systemctl --user restart pi-para-daemon 2>/dev/null"); } catch {}
|
|
773
|
-
ctx.ui.notify(`Set daemonModel = ${config.daemonModel} (daemon restarted)`, "info");
|
|
747
|
+
setCaptureModel(config, providerChoice, modelChoice.split(" (")[0]);
|
|
748
|
+
await persist();
|
|
749
|
+
ctx.ui.notify(`Set capture model = ${modelSelectionLabel(config.models.capture)}. Restart open Pi sessions to apply.`, "info");
|
|
774
750
|
}
|
|
775
751
|
}
|
|
776
752
|
}
|
|
777
|
-
} catch
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
753
|
+
} catch {
|
|
754
|
+
const val = await ctx.ui.input("Capture model (provider/model-id, empty for auto):", modelSelectionLabel(config.models.capture));
|
|
755
|
+
if (!val?.trim()) setCaptureModelAuto(config);
|
|
756
|
+
else {
|
|
757
|
+
const slash = val.indexOf("/");
|
|
758
|
+
if (slash <= 0) {
|
|
759
|
+
ctx.ui.notify("Use provider/model-id format, or leave empty for auto.", "error");
|
|
760
|
+
continue;
|
|
761
|
+
}
|
|
762
|
+
setCaptureModel(config, val.slice(0, slash), val.slice(slash + 1));
|
|
763
|
+
}
|
|
764
|
+
await persist();
|
|
765
|
+
ctx.ui.notify(`Set capture model = ${modelSelectionLabel(config.models.capture)}. Restart open Pi sessions to apply.`, "info");
|
|
787
766
|
}
|
|
788
767
|
} else if (choice.startsWith("[WebWiki]")) {
|
|
789
|
-
const webWiki = (config as any).webWiki ?? { enabled: false, host: "0.0.0.0", port: 10973 };
|
|
790
768
|
const sub = await ctx.ui.select("Web Wiki Settings", [
|
|
791
|
-
`Enabled: ${webWiki.enabled}`,
|
|
792
|
-
`Host: ${webWiki.host}`,
|
|
793
|
-
`Port: ${webWiki.port}`,
|
|
769
|
+
`Enabled: ${config.webWiki.enabled}`,
|
|
770
|
+
`Host: ${config.webWiki.host}`,
|
|
771
|
+
`Port: ${config.webWiki.port}`,
|
|
794
772
|
]);
|
|
795
773
|
if (sub?.startsWith("Enabled")) {
|
|
796
|
-
|
|
797
|
-
(
|
|
798
|
-
|
|
799
|
-
if (webWiki.enabled) {
|
|
800
|
-
// Detect LAN IP
|
|
801
|
-
const { networkInterfaces } = await import("node:os");
|
|
802
|
-
const nets = networkInterfaces();
|
|
803
|
-
let lanIp = webWiki.host;
|
|
804
|
-
for (const ifaces of Object.values(nets)) {
|
|
805
|
-
for (const iface of ifaces ?? []) {
|
|
806
|
-
if (iface.family === "IPv4" && !iface.internal) {
|
|
807
|
-
lanIp = iface.address;
|
|
808
|
-
break;
|
|
809
|
-
}
|
|
810
|
-
}
|
|
811
|
-
}
|
|
812
|
-
ctx.ui.notify(`Web Wiki enabled at http://${lanIp}:${webWiki.port}\nRestart pi to apply.`, "info");
|
|
813
|
-
} else {
|
|
814
|
-
ctx.ui.notify("Web Wiki disabled. Restart pi to apply.", "info");
|
|
815
|
-
}
|
|
774
|
+
setWebWikiEnabled(config, !config.webWiki.enabled);
|
|
775
|
+
await persist();
|
|
776
|
+
ctx.ui.notify(`Web Wiki ${config.webWiki.enabled ? "enabled" : "disabled"}. Restart open Pi sessions to apply.`, "info");
|
|
816
777
|
} else if (sub?.startsWith("Host")) {
|
|
817
|
-
const val = await ctx.ui.input("Host
|
|
818
|
-
if (val) {
|
|
819
|
-
webWiki.host = val;
|
|
820
|
-
(config as any).webWiki = webWiki;
|
|
821
|
-
await writeFileAsync(configPath, JSON.stringify(config, null, 2));
|
|
822
|
-
ctx.ui.notify(`Set webWiki.host = ${webWiki.host}`, "info");
|
|
823
|
-
}
|
|
778
|
+
const val = await ctx.ui.input("Host:", config.webWiki.host);
|
|
779
|
+
if (val) { setWebWikiHost(config, val); await persist(); }
|
|
824
780
|
} else if (sub?.startsWith("Port")) {
|
|
825
|
-
const val = await ctx.ui.input("Port:", String(webWiki.port));
|
|
826
|
-
if (val) {
|
|
827
|
-
webWiki.port = parseInt(val) || 10973;
|
|
828
|
-
(config as any).webWiki = webWiki;
|
|
829
|
-
await writeFileAsync(configPath, JSON.stringify(config, null, 2));
|
|
830
|
-
ctx.ui.notify(`Set webWiki.port = ${webWiki.port}`, "info");
|
|
831
|
-
}
|
|
781
|
+
const val = await ctx.ui.input("Port:", String(config.webWiki.port));
|
|
782
|
+
if (val) { setWebWikiPort(config, val); await persist(); }
|
|
832
783
|
}
|
|
833
|
-
} else if (choice.startsWith("[
|
|
834
|
-
const
|
|
835
|
-
const
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
`URL: ${
|
|
839
|
-
`
|
|
840
|
-
`
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
`
|
|
784
|
+
} else if (choice.startsWith("[Embedding]") || choice.startsWith("[Rerank]")) {
|
|
785
|
+
const isEmbedding = choice.startsWith("[Embedding]");
|
|
786
|
+
const profile = isEmbedding ? ensureEmbeddingProfile(config) : ensureRerankProfile(config);
|
|
787
|
+
const sub = await ctx.ui.select(isEmbedding ? "Embedding provider" : "Rerank provider", [
|
|
788
|
+
`Provider: ${profile.provider}`,
|
|
789
|
+
`Base URL: ${profile.baseUrl ?? "not set"}`,
|
|
790
|
+
`Model: ${profile.model ?? "not set"}`,
|
|
791
|
+
...(isEmbedding ? [`Dims: ${profile.dims ?? "auto"}`] : []),
|
|
792
|
+
`API format: ${profile.apiFormat ?? "openai"}`,
|
|
793
|
+
`Credential ref: ${profile.credentialRef}`,
|
|
794
|
+
`Store/update local secret`,
|
|
795
|
+
...(!isEmbedding ? [`Disable rerank`] : []),
|
|
844
796
|
]);
|
|
845
797
|
|
|
846
|
-
if (sub?.startsWith("
|
|
847
|
-
const val = await ctx.ui.input(
|
|
848
|
-
if (val)
|
|
849
|
-
} else if (sub?.startsWith("
|
|
850
|
-
const val = await ctx.ui.input(
|
|
851
|
-
if (val)
|
|
798
|
+
if (sub?.startsWith("Provider")) {
|
|
799
|
+
const val = await ctx.ui.input("Provider id:", profile.provider);
|
|
800
|
+
if (val) setProviderProfileField(profile, "provider", val);
|
|
801
|
+
} else if (sub?.startsWith("Base URL")) {
|
|
802
|
+
const val = await ctx.ui.input("Base URL:", profile.baseUrl ?? "");
|
|
803
|
+
if (val) setProviderProfileField(profile, "baseUrl", val);
|
|
852
804
|
} else if (sub?.startsWith("Model")) {
|
|
853
|
-
const val = await ctx.ui.input(
|
|
854
|
-
if (val)
|
|
805
|
+
const val = await ctx.ui.input("Model id:", profile.model ?? "");
|
|
806
|
+
if (val) setProviderProfileField(profile, "model", val);
|
|
855
807
|
} else if (sub?.startsWith("Dims")) {
|
|
856
|
-
const val = await ctx.ui.input("Embedding dimensions:",
|
|
857
|
-
if (val)
|
|
808
|
+
const val = await ctx.ui.input("Embedding dimensions:", profile.dims ? String(profile.dims) : "");
|
|
809
|
+
if (val) setProviderDims(profile, val);
|
|
858
810
|
} else if (sub?.startsWith("API format")) {
|
|
859
|
-
const fmt = await ctx.ui.select("API format", ["openai", "anthropic"]);
|
|
860
|
-
if (fmt)
|
|
861
|
-
} else if (sub?.startsWith("
|
|
862
|
-
|
|
811
|
+
const fmt = await ctx.ui.select("API format", ["openai", "anthropic", "custom"]);
|
|
812
|
+
if (fmt) setProviderProfileField(profile, "apiFormat", fmt);
|
|
813
|
+
} else if (sub?.startsWith("Credential ref")) {
|
|
814
|
+
const val = await ctx.ui.input("Credential ref (pi-auth:<provider>, secret:<name>, or none):", profile.credentialRef);
|
|
815
|
+
if (val) setProviderProfileField(profile, "credentialRef", val);
|
|
816
|
+
} else if (sub?.startsWith("Store/update local secret")) {
|
|
817
|
+
const name = await ctx.ui.input("Secret name:", profile.credentialRef.startsWith("secret:") ? profile.credentialRef.slice("secret:".length) : "");
|
|
818
|
+
if (name) {
|
|
819
|
+
const value = await ctx.ui.input("API key (stored in ~/.pi/para/secrets.json, not wiki git):", "");
|
|
820
|
+
if (value) {
|
|
821
|
+
await setSecret(name, value, loaded.paths.secretsPath);
|
|
822
|
+
setProviderProfileField(profile, "credentialRef", `secret:${name}`);
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
} else if (sub?.startsWith("Disable rerank")) {
|
|
826
|
+
disableRerank(config);
|
|
863
827
|
}
|
|
864
828
|
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
const { mkdirSync } = await import("node:fs");
|
|
868
|
-
mkdirSync(join(homedir(), ".config", "qmd"), { recursive: true });
|
|
869
|
-
await writeFileAsync(qmdPath, stringify(qmdConfig));
|
|
870
|
-
ctx.ui.notify(`Updated ${qmdPath}`, "info");
|
|
829
|
+
await persist();
|
|
830
|
+
ctx.ui.notify(`Updated ${isEmbedding ? "embedding" : "rerank"} provider config.`, "info");
|
|
871
831
|
} else if (choice.startsWith("---") || choice.startsWith("[Status]")) {
|
|
872
|
-
// Status row — just show, don't edit
|
|
873
832
|
continue;
|
|
874
833
|
}
|
|
875
834
|
}
|
|
876
835
|
},
|
|
877
836
|
});
|
|
837
|
+
|
|
878
838
|
}
|