pi-para 0.5.2 → 0.5.5
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 +29 -0
- package/CONFIGURATION.md +243 -0
- package/README.md +140 -237
- package/SETUP.md +196 -112
- package/STRUCTURE.md +140 -0
- package/dist/atomic-write.d.ts +2 -0
- package/dist/atomic-write.d.ts.map +1 -0
- package/dist/atomic-write.js +18 -0
- package/dist/atomic-write.js.map +1 -0
- package/dist/capture.d.ts +4 -4
- package/dist/capture.d.ts.map +1 -1
- package/dist/capture.js +1 -1
- package/dist/capture.js.map +1 -1
- package/dist/cli-legacy.d.ts +4 -0
- package/dist/cli-legacy.d.ts.map +1 -0
- package/dist/cli-legacy.js +41 -0
- package/dist/cli-legacy.js.map +1 -0
- package/dist/cli.d.ts +2 -9
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +135 -29
- package/dist/cli.js.map +1 -1
- package/dist/commands.d.ts +3 -3
- package/dist/commands.d.ts.map +1 -1
- package/dist/commands.js +189 -256
- package/dist/commands.js.map +1 -1
- package/dist/config.d.ts +104 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +242 -0
- package/dist/config.js.map +1 -0
- package/dist/context.d.ts +4 -2
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +29 -7
- package/dist/context.js.map +1 -1
- package/dist/credentials.d.ts +27 -0
- package/dist/credentials.d.ts.map +1 -0
- package/dist/credentials.js +85 -0
- package/dist/credentials.js.map +1 -0
- package/dist/daemon.d.ts +1 -1
- package/dist/daemon.d.ts.map +1 -1
- package/dist/doctor.d.ts +20 -0
- package/dist/doctor.d.ts.map +1 -0
- package/dist/doctor.js +171 -0
- package/dist/doctor.js.map +1 -0
- package/dist/gepa/index.js +1 -1
- package/dist/gepa/index.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +102 -37
- package/dist/index.js.map +1 -1
- package/dist/maintainer.d.ts +2 -2
- package/dist/maintainer.d.ts.map +1 -1
- package/dist/maintainer.js +2 -2
- package/dist/maintainer.js.map +1 -1
- package/dist/model-resolver.d.ts +30 -0
- package/dist/model-resolver.d.ts.map +1 -0
- package/dist/model-resolver.js +71 -0
- package/dist/model-resolver.js.map +1 -0
- package/dist/paths.d.ts +20 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +33 -0
- package/dist/paths.js.map +1 -0
- package/dist/processor.d.ts +2 -2
- package/dist/processor.d.ts.map +1 -1
- package/dist/processor.js +2 -2
- package/dist/processor.js.map +1 -1
- package/dist/qmd-providers.d.ts +12 -0
- package/dist/qmd-providers.d.ts.map +1 -0
- package/dist/qmd-providers.js +45 -0
- package/dist/qmd-providers.js.map +1 -0
- package/dist/query.d.ts +1 -1
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +14 -1
- package/dist/query.js.map +1 -1
- package/dist/repair.d.ts +11 -0
- package/dist/repair.d.ts.map +1 -0
- package/dist/repair.js +41 -0
- package/dist/repair.js.map +1 -0
- package/dist/scheduler/controls.d.ts +35 -0
- package/dist/scheduler/controls.d.ts.map +1 -0
- package/dist/scheduler/controls.js +98 -0
- package/dist/scheduler/controls.js.map +1 -0
- package/dist/scheduler/index.d.ts +48 -0
- package/dist/scheduler/index.d.ts.map +1 -0
- package/dist/scheduler/index.js +193 -0
- package/dist/scheduler/index.js.map +1 -0
- package/dist/scheduler/leases.d.ts +22 -0
- package/dist/scheduler/leases.d.ts.map +1 -0
- package/dist/scheduler/leases.js +74 -0
- package/dist/scheduler/leases.js.map +1 -0
- package/dist/scheduler/session-capture.d.ts +33 -0
- package/dist/scheduler/session-capture.d.ts.map +1 -0
- package/dist/scheduler/session-capture.js +108 -0
- package/dist/scheduler/session-capture.js.map +1 -0
- package/dist/scheduler/state.d.ts +56 -0
- package/dist/scheduler/state.d.ts.map +1 -0
- package/dist/scheduler/state.js +210 -0
- package/dist/scheduler/state.js.map +1 -0
- package/dist/session-tools.d.ts +1 -1
- package/dist/session-tools.d.ts.map +1 -1
- package/dist/settings.d.ts +19 -0
- package/dist/settings.d.ts.map +1 -0
- package/dist/settings.js +106 -0
- package/dist/settings.js.map +1 -0
- package/dist/setup.d.ts +29 -0
- package/dist/setup.d.ts.map +1 -0
- package/dist/setup.js +90 -0
- package/dist/setup.js.map +1 -0
- package/dist/status.d.ts +22 -0
- package/dist/status.d.ts.map +1 -0
- package/dist/status.js +93 -0
- package/dist/status.js.map +1 -0
- package/dist/store.d.ts +23 -8
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +89 -34
- package/dist/store.js.map +1 -1
- package/dist/summarize.d.ts +6 -2
- package/dist/summarize.d.ts.map +1 -1
- package/dist/summarize.js +60 -25
- package/dist/summarize.js.map +1 -1
- package/dist/templates/prompts.d.ts +1 -1
- package/dist/templates/prompts.d.ts.map +1 -1
- package/dist/templates/prompts.js +1 -1
- package/dist/tools.d.ts +5 -5
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +243 -78
- package/dist/tools.js.map +1 -1
- package/dist/wiki-tool-guidance.d.ts +6 -0
- package/dist/wiki-tool-guidance.d.ts.map +1 -0
- package/dist/wiki-tool-guidance.js +70 -0
- package/dist/wiki-tool-guidance.js.map +1 -0
- package/dist/wiki.d.ts +6 -1
- package/dist/wiki.d.ts.map +1 -1
- package/dist/wiki.js +88 -19
- package/dist/wiki.js.map +1 -1
- package/package.json +80 -15
- package/scripts/install.ps1 +44 -0
- package/scripts/install.sh +62 -0
- package/scripts/smoke-install.mjs +61 -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 +204 -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/commands.js
CHANGED
|
@@ -3,14 +3,15 @@
|
|
|
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
|
-
import { readFile
|
|
8
|
+
import { readFile } from "node:fs/promises";
|
|
9
9
|
import { join } from "node:path";
|
|
10
10
|
import { lintWiki } from "./lint.js";
|
|
11
11
|
import { listPages, readPage, writePage, movePage, writeIndex, PARA_CATEGORIES } from "./wiki.js";
|
|
12
12
|
import { searchWiki } from "./store.js";
|
|
13
13
|
import { CURRENT_SCHEMA_VERSION, migrateToLatest, parseFrontmatter, validateFrontmatter, } from "./frontmatter.js";
|
|
14
|
+
import { appendCompletedSession } from "./scheduler/session-capture.js";
|
|
14
15
|
// -- Helpers -----------------------------------------------------------------
|
|
15
16
|
/** Convert raw text to a kebab-case slug. */
|
|
16
17
|
function toKebabSlug(raw) {
|
|
@@ -208,21 +209,19 @@ export function registerCommands(pi, wikiDir, store, getScope, setScope) {
|
|
|
208
209
|
});
|
|
209
210
|
// ---- /wiki-capture — capture via session LLM ----------------------------
|
|
210
211
|
pi.registerCommand("wiki-capture", {
|
|
211
|
-
description: "
|
|
212
|
+
description: "Queue this session for scheduler-backed wiki capture",
|
|
212
213
|
handler: async (args, ctx) => {
|
|
213
214
|
const sessionFile = ctx.sessionManager.getSessionFile();
|
|
214
215
|
if (!sessionFile) {
|
|
215
216
|
ctx.ui.notify("No session file found", "warning");
|
|
216
217
|
return;
|
|
217
218
|
}
|
|
218
|
-
// Register the session for
|
|
219
|
+
// Register the session for scheduler capture — runs in background, doesn't block user
|
|
219
220
|
try {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
"Check progress: /wiki-daemon status\n" +
|
|
225
|
-
"Tip: Set \"daemonModel\" in ~/.pi/wiki/config.json for better capture quality.", "info");
|
|
221
|
+
await appendCompletedSession(wikiDir, sessionFile);
|
|
222
|
+
ctx.ui.notify("Session queued for wiki capture (scheduler will process when Pi is open).\n" +
|
|
223
|
+
"Check progress: /wiki-scheduler status\n" +
|
|
224
|
+
"Tip: Set the capture model in /wiki-settings for better capture quality.", "info");
|
|
226
225
|
ctx.ui.setStatus("pi-para", "wiki: capture queued");
|
|
227
226
|
setTimeout(() => ctx.ui.setStatus("pi-para", undefined), 5000);
|
|
228
227
|
}
|
|
@@ -312,85 +311,78 @@ export function registerCommands(pi, wikiDir, store, getScope, setScope) {
|
|
|
312
311
|
pi.sendUserMessage(`Summarize the wiki: target="${target}". Use wiki_summarize tool with target="${target}".`);
|
|
313
312
|
},
|
|
314
313
|
});
|
|
315
|
-
// ---- /wiki-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
314
|
+
// ---- /wiki-scheduler — scheduler/capture queue status ----------------------
|
|
315
|
+
const registerSchedulerStatusCommand = (name, legacy) => {
|
|
316
|
+
pi.registerCommand(name, {
|
|
317
|
+
description: legacy
|
|
318
|
+
? "Compatibility alias for /wiki-scheduler"
|
|
319
|
+
: "Check scheduler queue, task history, and capture status",
|
|
320
|
+
getArgumentCompletions: (prefix) => {
|
|
321
|
+
const cmds = ["status", "queue", "history", "capture-history"];
|
|
322
|
+
const filtered = cmds.filter((c) => c.startsWith(prefix));
|
|
323
|
+
return filtered.length > 0 ? filtered.map((c) => ({ value: c, label: c })) : null;
|
|
324
|
+
},
|
|
325
|
+
handler: async (args, ctx) => {
|
|
326
|
+
const subcmd = args.trim().split(/\s+/)[0] || "status";
|
|
327
|
+
const { listSchedulerTasks, listSchedulerHistory, formatQueueItems, formatSchedulerHistory } = await import("./scheduler/controls.js");
|
|
328
|
+
const { StateDB } = await import("./state.js");
|
|
329
329
|
switch (subcmd) {
|
|
330
330
|
case "status": {
|
|
331
|
-
const
|
|
332
|
-
const
|
|
333
|
-
const
|
|
334
|
-
const
|
|
335
|
-
let running = false;
|
|
336
|
-
if (pid) {
|
|
337
|
-
try {
|
|
338
|
-
process.kill(parseInt(pid), 0);
|
|
339
|
-
running = true;
|
|
340
|
-
}
|
|
341
|
-
catch { }
|
|
342
|
-
}
|
|
331
|
+
const queued = listSchedulerTasks(wikiDir, { status: "queued" }).length;
|
|
332
|
+
const running = listSchedulerTasks(wikiDir, { status: "running" }).length;
|
|
333
|
+
const failed = listSchedulerTasks(wikiDir, { status: "failed" }).length;
|
|
334
|
+
const recent = listSchedulerHistory(wikiDir, { limit: 5 });
|
|
343
335
|
const lines = [
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
`Failed: ${failed.length}`,
|
|
336
|
+
legacy ? "Scheduler: active (via /wiki-daemon compatibility alias)" : "Scheduler: active",
|
|
337
|
+
`Queue: ${queued} queued, ${running} running, ${failed} failed`,
|
|
347
338
|
"",
|
|
348
|
-
"Recent
|
|
339
|
+
"Recent scheduler history:",
|
|
340
|
+
formatSchedulerHistory(recent).split("\n").map((line) => ` ${line}`).join("\n"),
|
|
349
341
|
];
|
|
350
|
-
if (history.length === 0) {
|
|
351
|
-
lines.push(" (none)");
|
|
352
|
-
}
|
|
353
|
-
else {
|
|
354
|
-
for (const h of history) {
|
|
355
|
-
const status = h.error
|
|
356
|
-
? `ERROR: ${h.error.slice(0, 50)}`
|
|
357
|
-
: h.pagesCreated.length > 0
|
|
358
|
-
? `${h.pagesCreated.length} page(s)`
|
|
359
|
-
: "skipped";
|
|
360
|
-
lines.push(` ${h.processedAt.slice(0, 19)} | ${h.scope} | ${status}`);
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
342
|
ctx.ui.notify(lines.filter(Boolean).join("\n"), "info");
|
|
364
343
|
break;
|
|
365
344
|
}
|
|
345
|
+
case "queue": {
|
|
346
|
+
ctx.ui.notify(formatQueueItems(listSchedulerTasks(wikiDir)), "info");
|
|
347
|
+
break;
|
|
348
|
+
}
|
|
366
349
|
case "history": {
|
|
350
|
+
ctx.ui.notify(formatSchedulerHistory(listSchedulerHistory(wikiDir, { limit: 15 })), "info");
|
|
351
|
+
break;
|
|
352
|
+
}
|
|
353
|
+
case "capture-history": {
|
|
367
354
|
const scope = getScope();
|
|
368
|
-
const
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
355
|
+
const state = new StateDB(wikiDir);
|
|
356
|
+
try {
|
|
357
|
+
const history = state.getHistory(scope.name, 15);
|
|
358
|
+
if (history.length === 0) {
|
|
359
|
+
ctx.ui.notify(`No capture history for scope: ${scope.name}`, "info");
|
|
360
|
+
break;
|
|
361
|
+
}
|
|
362
|
+
const lines = [`Capture history (${scope.name}):\n`];
|
|
363
|
+
for (const h of history) {
|
|
364
|
+
const status = h.error
|
|
365
|
+
? `ERROR: ${h.error.slice(0, 60)}`
|
|
366
|
+
: h.pagesCreated.length > 0
|
|
367
|
+
? `${h.pagesCreated.length} page(s): ${h.pagesCreated.join(", ")}`
|
|
368
|
+
: "skipped";
|
|
369
|
+
lines.push(`${h.processedAt.slice(0, 19)} | ${status}`);
|
|
370
|
+
}
|
|
371
|
+
ctx.ui.notify(lines.join("\n"), "info");
|
|
372
372
|
}
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
const status = h.error
|
|
376
|
-
? `ERROR: ${h.error.slice(0, 60)}`
|
|
377
|
-
: h.pagesCreated.length > 0
|
|
378
|
-
? `${h.pagesCreated.length} page(s): ${h.pagesCreated.join(", ")}`
|
|
379
|
-
: "skipped";
|
|
380
|
-
lines.push(`${h.processedAt.slice(0, 19)} | ${status}`);
|
|
373
|
+
finally {
|
|
374
|
+
state.close();
|
|
381
375
|
}
|
|
382
|
-
ctx.ui.notify(lines.join("\n"), "info");
|
|
383
376
|
break;
|
|
384
377
|
}
|
|
385
378
|
default:
|
|
386
|
-
ctx.ui.notify(`Usage:
|
|
379
|
+
ctx.ui.notify(`Usage: /${name} [status|queue|history|capture-history]`, "error");
|
|
387
380
|
}
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
});
|
|
381
|
+
},
|
|
382
|
+
});
|
|
383
|
+
};
|
|
384
|
+
registerSchedulerStatusCommand("wiki-scheduler", false);
|
|
385
|
+
registerSchedulerStatusCommand("wiki-daemon", true);
|
|
394
386
|
// ---- /wiki-migrate — batch schema migration ------------------------------
|
|
395
387
|
pi.registerCommand("wiki-migrate", {
|
|
396
388
|
description: "Migrate all wiki pages to the current schema version",
|
|
@@ -517,266 +509,207 @@ ${goal} — verified and complete.
|
|
|
517
509
|
ctx.ui.notify("/wiki-settings requires interactive mode", "error");
|
|
518
510
|
return;
|
|
519
511
|
}
|
|
520
|
-
const {
|
|
521
|
-
const {
|
|
522
|
-
const {
|
|
523
|
-
const {
|
|
524
|
-
|
|
525
|
-
const
|
|
526
|
-
|
|
527
|
-
try {
|
|
528
|
-
config = JSON.parse(await readFile(configPath, "utf-8"));
|
|
529
|
-
}
|
|
530
|
-
catch { /* defaults */ }
|
|
531
|
-
// Load qmd config
|
|
532
|
-
const qmdPath = join(homedir(), ".config", "qmd", "index.yml");
|
|
533
|
-
let qmdConfig = {};
|
|
534
|
-
try {
|
|
535
|
-
qmdConfig = parse(await readFile(qmdPath, "utf-8")) ?? {};
|
|
536
|
-
}
|
|
537
|
-
catch { /* no qmd config */ }
|
|
538
|
-
const providers = (qmdConfig.providers ?? {});
|
|
539
|
-
// Check statuses
|
|
540
|
-
let qmdVersion = "";
|
|
541
|
-
try {
|
|
542
|
-
qmdVersion = execSync("qmd --version 2>/dev/null", { encoding: "utf-8" }).trim();
|
|
543
|
-
}
|
|
544
|
-
catch { }
|
|
545
|
-
let daemonStatus = "not running";
|
|
546
|
-
try {
|
|
547
|
-
daemonStatus = execSync("systemctl --user is-active pi-para-daemon 2>/dev/null", { encoding: "utf-8" }).trim();
|
|
548
|
-
}
|
|
549
|
-
catch { }
|
|
550
|
-
// Main menu loop
|
|
512
|
+
const { loadParaConfig, saveParaConfig } = await import("./config.js");
|
|
513
|
+
const { modelSelectionLabel, providerProfileLabel, setContextMaxTokens, setSearchLimit, setLintStaleDays, toggleSearchGraphBoost, toggleLintAutoFix, setCaptureModelAuto, setCaptureModel, setWebWikiEnabled, setWebWikiHost, setWebWikiPort, ensureEmbeddingProfile, ensureRerankProfile, disableRerank, setProviderProfileField, setProviderDims, } = await import("./settings.js");
|
|
514
|
+
const { listSchedulerTasks } = await import("./scheduler/controls.js");
|
|
515
|
+
const { setSecret } = await import("./credentials.js");
|
|
516
|
+
const loaded = await loadParaConfig({ migrate: true });
|
|
517
|
+
const config = loaded.config;
|
|
518
|
+
const persist = async () => saveParaConfig(config, { homeDir: loaded.paths.homeDir });
|
|
551
519
|
while (true) {
|
|
552
520
|
const scope = getScope();
|
|
553
521
|
const pageCount = (await listPages(wikiDir).catch(() => [])).length;
|
|
522
|
+
const queuedTasks = listSchedulerTasks(wikiDir, { status: "queued" }).length;
|
|
523
|
+
const failedTasks = listSchedulerTasks(wikiDir, { status: "failed" }).length;
|
|
554
524
|
const choice = await ctx.ui.select("Wiki Settings", [
|
|
555
|
-
`[Context] Max tokens: ${config.
|
|
556
|
-
`[Search] Limit: ${config.searchLimit
|
|
557
|
-
`[Lint] Auto-fix: ${config.
|
|
558
|
-
`[
|
|
559
|
-
`[WebWiki] ${config.webWiki
|
|
560
|
-
`[
|
|
561
|
-
`[
|
|
562
|
-
`[Rerank] ${providers.rerank?.model ?? "not configured"} ${providers.rerank?.url ? "at " + providers.rerank.url : ""}`,
|
|
525
|
+
`[Context] Max tokens: ${config.context.maxTokens}`,
|
|
526
|
+
`[Search] Limit: ${config.context.searchLimit}, Graph boost: ${config.context.searchGraphBoost}`,
|
|
527
|
+
`[Lint] Auto-fix: ${config.lint.autoFix}, Stale days: ${config.lint.staleDays}`,
|
|
528
|
+
`[Capture] Model: ${modelSelectionLabel(config.models.capture)}`,
|
|
529
|
+
`[WebWiki] ${config.webWiki.enabled ? `Enabled at http://${config.webWiki.host}:${config.webWiki.port}` : "Disabled"}`,
|
|
530
|
+
`[Embedding] ${providerProfileLabel(config.qmd.embedding)}`,
|
|
531
|
+
`[Rerank] ${providerProfileLabel(config.qmd.rerank)}`,
|
|
563
532
|
`---`,
|
|
564
|
-
`[Status] Scope: ${scope.name} | Pages: ${pageCount} | qmd:
|
|
533
|
+
`[Status] Scope: ${scope.name} | Pages: ${pageCount} | qmd: SDK | Scheduler: ${queuedTasks} queued, ${failedTasks} failed`,
|
|
565
534
|
]);
|
|
566
535
|
if (!choice)
|
|
567
536
|
break;
|
|
568
537
|
if (choice.startsWith("[Context]")) {
|
|
569
|
-
const val = await ctx.ui.input("Context max tokens:", String(config.
|
|
538
|
+
const val = await ctx.ui.input("Context max tokens:", String(config.context.maxTokens));
|
|
570
539
|
if (val) {
|
|
571
|
-
config
|
|
572
|
-
await
|
|
573
|
-
ctx.ui.notify(`Set
|
|
540
|
+
setContextMaxTokens(config, val);
|
|
541
|
+
await persist();
|
|
542
|
+
ctx.ui.notify(`Set context.maxTokens = ${config.context.maxTokens}`, "info");
|
|
574
543
|
}
|
|
575
544
|
}
|
|
576
545
|
else if (choice.startsWith("[Search]")) {
|
|
577
546
|
const sub = await ctx.ui.select("Search Settings", [
|
|
578
|
-
`Limit: ${config.searchLimit
|
|
579
|
-
`Graph boost: ${config.searchGraphBoost
|
|
547
|
+
`Limit: ${config.context.searchLimit}`,
|
|
548
|
+
`Graph boost: ${config.context.searchGraphBoost}`,
|
|
580
549
|
]);
|
|
581
550
|
if (sub?.startsWith("Limit")) {
|
|
582
|
-
const val = await ctx.ui.input("Search result limit:", String(config.searchLimit
|
|
551
|
+
const val = await ctx.ui.input("Search result limit:", String(config.context.searchLimit));
|
|
583
552
|
if (val) {
|
|
584
|
-
config
|
|
585
|
-
await
|
|
586
|
-
ctx.ui.notify(`Set searchLimit = ${config.searchLimit}`, "info");
|
|
553
|
+
setSearchLimit(config, val);
|
|
554
|
+
await persist();
|
|
555
|
+
ctx.ui.notify(`Set context.searchLimit = ${config.context.searchLimit}`, "info");
|
|
587
556
|
}
|
|
588
557
|
}
|
|
589
558
|
else if (sub?.startsWith("Graph boost")) {
|
|
590
|
-
|
|
591
|
-
await
|
|
592
|
-
ctx.ui.notify(`Set searchGraphBoost = ${config.searchGraphBoost}`, "info");
|
|
559
|
+
toggleSearchGraphBoost(config);
|
|
560
|
+
await persist();
|
|
561
|
+
ctx.ui.notify(`Set context.searchGraphBoost = ${config.context.searchGraphBoost}`, "info");
|
|
593
562
|
}
|
|
594
563
|
}
|
|
595
564
|
else if (choice.startsWith("[Lint]")) {
|
|
596
565
|
const sub = await ctx.ui.select("Lint Settings", [
|
|
597
|
-
`Auto-fix: ${config.
|
|
598
|
-
`Stale days: ${config.
|
|
566
|
+
`Auto-fix: ${config.lint.autoFix}`,
|
|
567
|
+
`Stale days: ${config.lint.staleDays}`,
|
|
599
568
|
]);
|
|
600
569
|
if (sub?.startsWith("Auto-fix")) {
|
|
601
|
-
|
|
602
|
-
await
|
|
603
|
-
ctx.ui.notify(`Set
|
|
570
|
+
toggleLintAutoFix(config);
|
|
571
|
+
await persist();
|
|
572
|
+
ctx.ui.notify(`Set lint.autoFix = ${config.lint.autoFix}`, "info");
|
|
604
573
|
}
|
|
605
574
|
else if (sub?.startsWith("Stale")) {
|
|
606
|
-
const val = await ctx.ui.input("Stale days threshold:", String(config.
|
|
575
|
+
const val = await ctx.ui.input("Stale days threshold:", String(config.lint.staleDays));
|
|
607
576
|
if (val) {
|
|
608
|
-
config
|
|
609
|
-
await
|
|
610
|
-
ctx.ui.notify(`Set
|
|
577
|
+
setLintStaleDays(config, val);
|
|
578
|
+
await persist();
|
|
579
|
+
ctx.ui.notify(`Set lint.staleDays = ${config.lint.staleDays}`, "info");
|
|
611
580
|
}
|
|
612
581
|
}
|
|
613
582
|
}
|
|
614
|
-
else if (choice.startsWith("[
|
|
583
|
+
else if (choice.startsWith("[Capture]")) {
|
|
615
584
|
try {
|
|
616
|
-
const { getProviders, getModels } = await import("@
|
|
617
|
-
const { AuthStorage } = await import("@
|
|
585
|
+
const { getProviders, getModels } = await import("@earendil-works/pi-ai/compat");
|
|
586
|
+
const { AuthStorage } = await import("@earendil-works/pi-coding-agent");
|
|
618
587
|
const authStorage = AuthStorage.create();
|
|
619
|
-
|
|
620
|
-
const allProviders = getProviders();
|
|
621
|
-
const availableProviders = [];
|
|
622
|
-
for (const p of allProviders) {
|
|
623
|
-
if (authStorage.hasAuth(p))
|
|
624
|
-
availableProviders.push(p);
|
|
625
|
-
}
|
|
588
|
+
const availableProviders = getProviders().filter((p) => authStorage.hasAuth(p));
|
|
626
589
|
if (availableProviders.length === 0) {
|
|
627
|
-
ctx.ui.notify("No providers configured.
|
|
590
|
+
ctx.ui.notify("No Pi auth providers configured. Use /login first or configure a pi-para secret for custom providers.", "warning");
|
|
628
591
|
}
|
|
629
592
|
else {
|
|
630
|
-
|
|
631
|
-
const providerChoice = await ctx.ui.select("Select provider", ["auto (best available)", ...availableProviders]);
|
|
593
|
+
const providerChoice = await ctx.ui.select("Select capture provider", ["auto (best available)", ...availableProviders]);
|
|
632
594
|
if (providerChoice === "auto (best available)") {
|
|
633
|
-
config
|
|
634
|
-
await
|
|
635
|
-
|
|
636
|
-
execSync("systemctl --user restart pi-para-daemon 2>/dev/null");
|
|
637
|
-
}
|
|
638
|
-
catch { }
|
|
639
|
-
ctx.ui.notify("Set daemonModel = auto (daemon restarted)", "info");
|
|
595
|
+
setCaptureModelAuto(config);
|
|
596
|
+
await persist();
|
|
597
|
+
ctx.ui.notify("Set capture model = auto. Restart open Pi sessions to apply.", "info");
|
|
640
598
|
}
|
|
641
599
|
else if (providerChoice) {
|
|
642
|
-
// Show models for selected provider
|
|
643
600
|
const models = getModels(providerChoice)
|
|
644
601
|
.sort((a, b) => (b.contextWindow ?? 0) - (a.contextWindow ?? 0));
|
|
645
|
-
const
|
|
646
|
-
const modelChoice = await ctx.ui.select(`Select ${providerChoice} model`, modelOptions);
|
|
602
|
+
const modelChoice = await ctx.ui.select(`Select ${providerChoice} model`, models.map((m) => `${m.id} (${Math.round((m.contextWindow ?? 0) / 1000)}k ctx${m.reasoning ? ", reasoning" : ""})`));
|
|
647
603
|
if (modelChoice) {
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
try {
|
|
652
|
-
execSync("systemctl --user restart pi-para-daemon 2>/dev/null");
|
|
653
|
-
}
|
|
654
|
-
catch { }
|
|
655
|
-
ctx.ui.notify(`Set daemonModel = ${config.daemonModel} (daemon restarted)`, "info");
|
|
604
|
+
setCaptureModel(config, providerChoice, modelChoice.split(" (")[0]);
|
|
605
|
+
await persist();
|
|
606
|
+
ctx.ui.notify(`Set capture model = ${modelSelectionLabel(config.models.capture)}. Restart open Pi sessions to apply.`, "info");
|
|
656
607
|
}
|
|
657
608
|
}
|
|
658
609
|
}
|
|
659
610
|
}
|
|
660
|
-
catch
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
611
|
+
catch {
|
|
612
|
+
const val = await ctx.ui.input("Capture model (provider/model-id, empty for auto):", modelSelectionLabel(config.models.capture));
|
|
613
|
+
if (!val?.trim())
|
|
614
|
+
setCaptureModelAuto(config);
|
|
615
|
+
else {
|
|
616
|
+
const slash = val.indexOf("/");
|
|
617
|
+
if (slash <= 0) {
|
|
618
|
+
ctx.ui.notify("Use provider/model-id format, or leave empty for auto.", "error");
|
|
619
|
+
continue;
|
|
620
|
+
}
|
|
621
|
+
setCaptureModel(config, val.slice(0, slash), val.slice(slash + 1));
|
|
668
622
|
}
|
|
669
|
-
|
|
670
|
-
ctx.ui.notify(`Set
|
|
623
|
+
await persist();
|
|
624
|
+
ctx.ui.notify(`Set capture model = ${modelSelectionLabel(config.models.capture)}. Restart open Pi sessions to apply.`, "info");
|
|
671
625
|
}
|
|
672
626
|
}
|
|
673
627
|
else if (choice.startsWith("[WebWiki]")) {
|
|
674
|
-
const webWiki = config.webWiki ?? { enabled: false, host: "0.0.0.0", port: 10973 };
|
|
675
628
|
const sub = await ctx.ui.select("Web Wiki Settings", [
|
|
676
|
-
`Enabled: ${webWiki.enabled}`,
|
|
677
|
-
`Host: ${webWiki.host}`,
|
|
678
|
-
`Port: ${webWiki.port}`,
|
|
629
|
+
`Enabled: ${config.webWiki.enabled}`,
|
|
630
|
+
`Host: ${config.webWiki.host}`,
|
|
631
|
+
`Port: ${config.webWiki.port}`,
|
|
679
632
|
]);
|
|
680
633
|
if (sub?.startsWith("Enabled")) {
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
if (webWiki.enabled) {
|
|
685
|
-
// Detect LAN IP
|
|
686
|
-
const { networkInterfaces } = await import("node:os");
|
|
687
|
-
const nets = networkInterfaces();
|
|
688
|
-
let lanIp = webWiki.host;
|
|
689
|
-
for (const ifaces of Object.values(nets)) {
|
|
690
|
-
for (const iface of ifaces ?? []) {
|
|
691
|
-
if (iface.family === "IPv4" && !iface.internal) {
|
|
692
|
-
lanIp = iface.address;
|
|
693
|
-
break;
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
}
|
|
697
|
-
ctx.ui.notify(`Web Wiki enabled at http://${lanIp}:${webWiki.port}\nRestart pi to apply.`, "info");
|
|
698
|
-
}
|
|
699
|
-
else {
|
|
700
|
-
ctx.ui.notify("Web Wiki disabled. Restart pi to apply.", "info");
|
|
701
|
-
}
|
|
634
|
+
setWebWikiEnabled(config, !config.webWiki.enabled);
|
|
635
|
+
await persist();
|
|
636
|
+
ctx.ui.notify(`Web Wiki ${config.webWiki.enabled ? "enabled" : "disabled"}. Restart open Pi sessions to apply.`, "info");
|
|
702
637
|
}
|
|
703
638
|
else if (sub?.startsWith("Host")) {
|
|
704
|
-
const val = await ctx.ui.input("Host
|
|
639
|
+
const val = await ctx.ui.input("Host:", config.webWiki.host);
|
|
705
640
|
if (val) {
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
await writeFileAsync(configPath, JSON.stringify(config, null, 2));
|
|
709
|
-
ctx.ui.notify(`Set webWiki.host = ${webWiki.host}`, "info");
|
|
641
|
+
setWebWikiHost(config, val);
|
|
642
|
+
await persist();
|
|
710
643
|
}
|
|
711
644
|
}
|
|
712
645
|
else if (sub?.startsWith("Port")) {
|
|
713
|
-
const val = await ctx.ui.input("Port:", String(webWiki.port));
|
|
646
|
+
const val = await ctx.ui.input("Port:", String(config.webWiki.port));
|
|
714
647
|
if (val) {
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
await writeFileAsync(configPath, JSON.stringify(config, null, 2));
|
|
718
|
-
ctx.ui.notify(`Set webWiki.port = ${webWiki.port}`, "info");
|
|
648
|
+
setWebWikiPort(config, val);
|
|
649
|
+
await persist();
|
|
719
650
|
}
|
|
720
651
|
}
|
|
721
652
|
}
|
|
722
|
-
else if (choice.startsWith("[
|
|
723
|
-
const
|
|
724
|
-
const
|
|
725
|
-
const sub = await ctx.ui.select(
|
|
726
|
-
`
|
|
727
|
-
`
|
|
728
|
-
`Model: ${
|
|
729
|
-
...(
|
|
730
|
-
|
|
731
|
-
`
|
|
653
|
+
else if (choice.startsWith("[Embedding]") || choice.startsWith("[Rerank]")) {
|
|
654
|
+
const isEmbedding = choice.startsWith("[Embedding]");
|
|
655
|
+
const profile = isEmbedding ? ensureEmbeddingProfile(config) : ensureRerankProfile(config);
|
|
656
|
+
const sub = await ctx.ui.select(isEmbedding ? "Embedding provider" : "Rerank provider", [
|
|
657
|
+
`Provider: ${profile.provider}`,
|
|
658
|
+
`Base URL: ${profile.baseUrl ?? "not set"}`,
|
|
659
|
+
`Model: ${profile.model ?? "not set"}`,
|
|
660
|
+
...(isEmbedding ? [`Dims: ${profile.dims ?? "auto"}`] : []),
|
|
661
|
+
`API format: ${profile.apiFormat ?? "openai"}`,
|
|
662
|
+
`Credential ref: ${profile.credentialRef}`,
|
|
663
|
+
`Store/update local secret`,
|
|
664
|
+
...(!isEmbedding ? [`Disable rerank`] : []),
|
|
732
665
|
]);
|
|
733
|
-
if (sub?.startsWith("
|
|
734
|
-
const val = await ctx.ui.input(
|
|
735
|
-
if (val)
|
|
736
|
-
|
|
737
|
-
providers[providerType] = current;
|
|
738
|
-
}
|
|
666
|
+
if (sub?.startsWith("Provider")) {
|
|
667
|
+
const val = await ctx.ui.input("Provider id:", profile.provider);
|
|
668
|
+
if (val)
|
|
669
|
+
setProviderProfileField(profile, "provider", val);
|
|
739
670
|
}
|
|
740
|
-
else if (sub?.startsWith("
|
|
741
|
-
const val = await ctx.ui.input(
|
|
742
|
-
if (val)
|
|
743
|
-
|
|
744
|
-
providers[providerType] = current;
|
|
745
|
-
}
|
|
671
|
+
else if (sub?.startsWith("Base URL")) {
|
|
672
|
+
const val = await ctx.ui.input("Base URL:", profile.baseUrl ?? "");
|
|
673
|
+
if (val)
|
|
674
|
+
setProviderProfileField(profile, "baseUrl", val);
|
|
746
675
|
}
|
|
747
676
|
else if (sub?.startsWith("Model")) {
|
|
748
|
-
const val = await ctx.ui.input(
|
|
749
|
-
if (val)
|
|
750
|
-
|
|
751
|
-
providers[providerType] = current;
|
|
752
|
-
}
|
|
677
|
+
const val = await ctx.ui.input("Model id:", profile.model ?? "");
|
|
678
|
+
if (val)
|
|
679
|
+
setProviderProfileField(profile, "model", val);
|
|
753
680
|
}
|
|
754
681
|
else if (sub?.startsWith("Dims")) {
|
|
755
|
-
const val = await ctx.ui.input("Embedding dimensions:",
|
|
756
|
-
if (val)
|
|
757
|
-
|
|
758
|
-
providers[providerType] = current;
|
|
759
|
-
}
|
|
682
|
+
const val = await ctx.ui.input("Embedding dimensions:", profile.dims ? String(profile.dims) : "");
|
|
683
|
+
if (val)
|
|
684
|
+
setProviderDims(profile, val);
|
|
760
685
|
}
|
|
761
686
|
else if (sub?.startsWith("API format")) {
|
|
762
|
-
const fmt = await ctx.ui.select("API format", ["openai", "anthropic"]);
|
|
763
|
-
if (fmt)
|
|
764
|
-
|
|
765
|
-
|
|
687
|
+
const fmt = await ctx.ui.select("API format", ["openai", "anthropic", "custom"]);
|
|
688
|
+
if (fmt)
|
|
689
|
+
setProviderProfileField(profile, "apiFormat", fmt);
|
|
690
|
+
}
|
|
691
|
+
else if (sub?.startsWith("Credential ref")) {
|
|
692
|
+
const val = await ctx.ui.input("Credential ref (pi-auth:<provider>, secret:<name>, or none):", profile.credentialRef);
|
|
693
|
+
if (val)
|
|
694
|
+
setProviderProfileField(profile, "credentialRef", val);
|
|
695
|
+
}
|
|
696
|
+
else if (sub?.startsWith("Store/update local secret")) {
|
|
697
|
+
const name = await ctx.ui.input("Secret name:", profile.credentialRef.startsWith("secret:") ? profile.credentialRef.slice("secret:".length) : "");
|
|
698
|
+
if (name) {
|
|
699
|
+
const value = await ctx.ui.input("API key (stored in ~/.pi/para/secrets.json, not wiki git):", "");
|
|
700
|
+
if (value) {
|
|
701
|
+
await setSecret(name, value, loaded.paths.secretsPath);
|
|
702
|
+
setProviderProfileField(profile, "credentialRef", `secret:${name}`);
|
|
703
|
+
}
|
|
766
704
|
}
|
|
767
705
|
}
|
|
768
|
-
else if (sub?.startsWith("
|
|
769
|
-
|
|
706
|
+
else if (sub?.startsWith("Disable rerank")) {
|
|
707
|
+
disableRerank(config);
|
|
770
708
|
}
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
const { mkdirSync } = await import("node:fs");
|
|
774
|
-
mkdirSync(join(homedir(), ".config", "qmd"), { recursive: true });
|
|
775
|
-
await writeFileAsync(qmdPath, stringify(qmdConfig));
|
|
776
|
-
ctx.ui.notify(`Updated ${qmdPath}`, "info");
|
|
709
|
+
await persist();
|
|
710
|
+
ctx.ui.notify(`Updated ${isEmbedding ? "embedding" : "rerank"} provider config.`, "info");
|
|
777
711
|
}
|
|
778
712
|
else if (choice.startsWith("---") || choice.startsWith("[Status]")) {
|
|
779
|
-
// Status row — just show, don't edit
|
|
780
713
|
continue;
|
|
781
714
|
}
|
|
782
715
|
}
|