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.
Files changed (195) hide show
  1. package/ARCHITECTURE.md +160 -0
  2. package/CHANGELOG.md +51 -0
  3. package/CONFIGURATION.md +243 -0
  4. package/README.md +140 -237
  5. package/SETUP.md +196 -112
  6. package/STRUCTURE.md +140 -0
  7. package/package.json +86 -18
  8. package/scripts/build-assets.mjs +29 -0
  9. package/scripts/install.ps1 +55 -0
  10. package/scripts/install.sh +63 -0
  11. package/scripts/smoke-install.mjs +63 -0
  12. package/skills/para/SKILL.md +1 -1
  13. package/skills/setup/SKILL.md +1 -1
  14. package/src/atomic-write.ts +17 -0
  15. package/src/capture.ts +4 -4
  16. package/src/cli-legacy.ts +43 -0
  17. package/src/cli.ts +133 -28
  18. package/src/commands.ts +203 -243
  19. package/src/config.ts +355 -0
  20. package/src/context.ts +29 -14
  21. package/src/credentials.ts +100 -0
  22. package/src/daemon.ts +1 -1
  23. package/src/doctor.ts +209 -0
  24. package/src/gepa/index.ts +1 -1
  25. package/src/index.ts +119 -40
  26. package/src/maintainer.ts +4 -4
  27. package/src/model-resolver.ts +96 -0
  28. package/src/paths.ts +48 -0
  29. package/src/processor.ts +4 -4
  30. package/src/qmd-providers.ts +63 -0
  31. package/src/query.ts +15 -1
  32. package/src/repair.ts +49 -0
  33. package/src/scheduler/controls.ts +125 -0
  34. package/src/scheduler/index.ts +210 -0
  35. package/src/scheduler/leases.ts +98 -0
  36. package/src/scheduler/session-capture.ts +146 -0
  37. package/src/scheduler/state.ts +304 -0
  38. package/src/session-tools.ts +1 -1
  39. package/src/settings.ts +126 -0
  40. package/src/setup.ts +116 -0
  41. package/src/status.ts +115 -0
  42. package/src/store.ts +116 -31
  43. package/src/summarize.ts +72 -16
  44. package/src/templates/prompts.ts +1 -1
  45. package/src/tools.ts +280 -90
  46. package/src/wiki-tool-guidance.ts +84 -0
  47. package/src/wiki.ts +98 -26
  48. package/dist/capture.d.ts +0 -34
  49. package/dist/capture.d.ts.map +0 -1
  50. package/dist/capture.js +0 -461
  51. package/dist/capture.js.map +0 -1
  52. package/dist/cli.d.ts +0 -15
  53. package/dist/cli.d.ts.map +0 -1
  54. package/dist/cli.js +0 -405
  55. package/dist/cli.js.map +0 -1
  56. package/dist/commands.d.ts +0 -15
  57. package/dist/commands.d.ts.map +0 -1
  58. package/dist/commands.js +0 -786
  59. package/dist/commands.js.map +0 -1
  60. package/dist/context.d.ts +0 -61
  61. package/dist/context.d.ts.map +0 -1
  62. package/dist/context.js +0 -289
  63. package/dist/context.js.map +0 -1
  64. package/dist/daemon.d.ts +0 -49
  65. package/dist/daemon.d.ts.map +0 -1
  66. package/dist/daemon.js +0 -226
  67. package/dist/daemon.js.map +0 -1
  68. package/dist/frontmatter.d.ts +0 -59
  69. package/dist/frontmatter.d.ts.map +0 -1
  70. package/dist/frontmatter.js +0 -170
  71. package/dist/frontmatter.js.map +0 -1
  72. package/dist/gepa/dataset.d.ts +0 -36
  73. package/dist/gepa/dataset.d.ts.map +0 -1
  74. package/dist/gepa/dataset.js +0 -148
  75. package/dist/gepa/dataset.js.map +0 -1
  76. package/dist/gepa/evaluator.d.ts +0 -51
  77. package/dist/gepa/evaluator.d.ts.map +0 -1
  78. package/dist/gepa/evaluator.js +0 -317
  79. package/dist/gepa/evaluator.js.map +0 -1
  80. package/dist/gepa/index.d.ts +0 -66
  81. package/dist/gepa/index.d.ts.map +0 -1
  82. package/dist/gepa/index.js +0 -240
  83. package/dist/gepa/index.js.map +0 -1
  84. package/dist/gepa/llm.d.ts +0 -35
  85. package/dist/gepa/llm.d.ts.map +0 -1
  86. package/dist/gepa/llm.js +0 -268
  87. package/dist/gepa/llm.js.map +0 -1
  88. package/dist/gepa/optimized/capture-prompt.txt +0 -65
  89. package/dist/gepa/optimizer.d.ts +0 -29
  90. package/dist/gepa/optimizer.d.ts.map +0 -1
  91. package/dist/gepa/optimizer.js +0 -249
  92. package/dist/gepa/optimizer.js.map +0 -1
  93. package/dist/gepa/targets.d.ts +0 -39
  94. package/dist/gepa/targets.d.ts.map +0 -1
  95. package/dist/gepa/targets.js +0 -326
  96. package/dist/gepa/targets.js.map +0 -1
  97. package/dist/gepa/types.d.ts +0 -168
  98. package/dist/gepa/types.d.ts.map +0 -1
  99. package/dist/gepa/types.js +0 -124
  100. package/dist/gepa/types.js.map +0 -1
  101. package/dist/index.d.ts +0 -21
  102. package/dist/index.d.ts.map +0 -1
  103. package/dist/index.js +0 -294
  104. package/dist/index.js.map +0 -1
  105. package/dist/ingest.d.ts +0 -88
  106. package/dist/ingest.d.ts.map +0 -1
  107. package/dist/ingest.js +0 -105
  108. package/dist/ingest.js.map +0 -1
  109. package/dist/link-discovery.d.ts +0 -40
  110. package/dist/link-discovery.d.ts.map +0 -1
  111. package/dist/link-discovery.js +0 -122
  112. package/dist/link-discovery.js.map +0 -1
  113. package/dist/link-utils.d.ts +0 -30
  114. package/dist/link-utils.d.ts.map +0 -1
  115. package/dist/link-utils.js +0 -145
  116. package/dist/link-utils.js.map +0 -1
  117. package/dist/lint.d.ts +0 -39
  118. package/dist/lint.d.ts.map +0 -1
  119. package/dist/lint.js +0 -827
  120. package/dist/lint.js.map +0 -1
  121. package/dist/maintainer.d.ts +0 -24
  122. package/dist/maintainer.d.ts.map +0 -1
  123. package/dist/maintainer.js +0 -358
  124. package/dist/maintainer.js.map +0 -1
  125. package/dist/processor.d.ts +0 -14
  126. package/dist/processor.d.ts.map +0 -1
  127. package/dist/processor.js +0 -361
  128. package/dist/processor.js.map +0 -1
  129. package/dist/query.d.ts +0 -69
  130. package/dist/query.d.ts.map +0 -1
  131. package/dist/query.js +0 -107
  132. package/dist/query.js.map +0 -1
  133. package/dist/raw.d.ts +0 -57
  134. package/dist/raw.d.ts.map +0 -1
  135. package/dist/raw.js +0 -219
  136. package/dist/raw.js.map +0 -1
  137. package/dist/redact.d.ts +0 -21
  138. package/dist/redact.d.ts.map +0 -1
  139. package/dist/redact.js +0 -143
  140. package/dist/redact.js.map +0 -1
  141. package/dist/scope.d.ts +0 -54
  142. package/dist/scope.d.ts.map +0 -1
  143. package/dist/scope.js +0 -161
  144. package/dist/scope.js.map +0 -1
  145. package/dist/scripts/bulk-link-fix.d.ts +0 -9
  146. package/dist/scripts/bulk-link-fix.d.ts.map +0 -1
  147. package/dist/scripts/bulk-link-fix.js +0 -106
  148. package/dist/scripts/bulk-link-fix.js.map +0 -1
  149. package/dist/session-tools.d.ts +0 -33
  150. package/dist/session-tools.d.ts.map +0 -1
  151. package/dist/session-tools.js +0 -211
  152. package/dist/session-tools.js.map +0 -1
  153. package/dist/state.d.ts +0 -54
  154. package/dist/state.d.ts.map +0 -1
  155. package/dist/state.js +0 -176
  156. package/dist/state.js.map +0 -1
  157. package/dist/store.d.ts +0 -56
  158. package/dist/store.d.ts.map +0 -1
  159. package/dist/store.js +0 -227
  160. package/dist/store.js.map +0 -1
  161. package/dist/summarize.d.ts +0 -56
  162. package/dist/summarize.d.ts.map +0 -1
  163. package/dist/summarize.js +0 -231
  164. package/dist/summarize.js.map +0 -1
  165. package/dist/tag-registry.d.ts +0 -56
  166. package/dist/tag-registry.d.ts.map +0 -1
  167. package/dist/tag-registry.js +0 -146
  168. package/dist/tag-registry.js.map +0 -1
  169. package/dist/templates/prompts.d.ts +0 -63
  170. package/dist/templates/prompts.d.ts.map +0 -1
  171. package/dist/templates/prompts.js +0 -260
  172. package/dist/templates/prompts.js.map +0 -1
  173. package/dist/tools.d.ts +0 -26
  174. package/dist/tools.d.ts.map +0 -1
  175. package/dist/tools.js +0 -1033
  176. package/dist/tools.js.map +0 -1
  177. package/dist/watcher.d.ts +0 -25
  178. package/dist/watcher.d.ts.map +0 -1
  179. package/dist/watcher.js +0 -84
  180. package/dist/watcher.js.map +0 -1
  181. package/dist/webui/client/dist/index.html +0 -1885
  182. package/dist/webui/server.d.ts +0 -31
  183. package/dist/webui/server.d.ts.map +0 -1
  184. package/dist/webui/server.js +0 -566
  185. package/dist/webui/server.js.map +0 -1
  186. package/dist/wiki.d.ts +0 -103
  187. package/dist/wiki.d.ts.map +0 -1
  188. package/dist/wiki.js +0 -428
  189. package/dist/wiki.js.map +0 -1
  190. package/scripts/gepa/__pycache__/dataset.cpython-313.pyc +0 -0
  191. package/scripts/gepa/__pycache__/lm_providers.cpython-313.pyc +0 -0
  192. package/scripts/gepa/__pycache__/metric.cpython-313.pyc +0 -0
  193. package/scripts/gepa/__pycache__/program.cpython-313.pyc +0 -0
  194. package/scripts/gepa/__pycache__/wiki_reader.cpython-313.pyc +0 -0
  195. package/scripts/gepa/uv.lock +0 -2241
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env node
2
+ /* v8 ignore start */
3
+ // Cross-platform post-tsc asset copy (replaces POSIX mkdir -p/cp in npm build).
4
+ import { cp, mkdir, readdir } from "node:fs/promises";
5
+ import { existsSync } from "node:fs";
6
+ import { fileURLToPath } from "node:url";
7
+ import { join, resolve, dirname } from "node:path";
8
+
9
+ const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
10
+
11
+ // 1. Web UI client bundle
12
+ const webuiSrc = join(root, "src", "webui", "client", "dist", "index.html");
13
+ const webuiDestDir = join(root, "dist", "webui", "client", "dist");
14
+ await mkdir(webuiDestDir, { recursive: true });
15
+ if (existsSync(webuiSrc)) {
16
+ await cp(webuiSrc, join(webuiDestDir, "index.html"));
17
+ }
18
+
19
+ // 2. GEPA optimized prompt text files (optional)
20
+ const gepaSrcDir = join(root, "src", "gepa", "optimized");
21
+ const gepaDestDir = join(root, "dist", "gepa", "optimized");
22
+ await mkdir(gepaDestDir, { recursive: true });
23
+ if (existsSync(gepaSrcDir)) {
24
+ for (const entry of await readdir(gepaSrcDir)) {
25
+ if (entry.endsWith(".txt")) {
26
+ await cp(join(gepaSrcDir, entry), join(gepaDestDir, entry));
27
+ }
28
+ }
29
+ }
@@ -0,0 +1,55 @@
1
+ # pi-para — Interactive Setup (Windows)
2
+ # Usage:
3
+ # irm https://raw.githubusercontent.com/picassio/pi-para/main/scripts/install.ps1 | iex
4
+
5
+ Write-Host ""
6
+ Write-Host " 🗂️ pi-para — Setup" -ForegroundColor Cyan
7
+ Write-Host " ──────────────────"
8
+ Write-Host ""
9
+
10
+ $package = "pi-para"
11
+ $packageLatest = "$package@latest"
12
+ # qmd-engine (embedded search SDK) requires Node >= 22.
13
+ $minMajor = 22
14
+
15
+ $nodeVer = (node -v 2>$null) -replace '^v',''
16
+ if (-not $nodeVer) {
17
+ Write-Host " ✗ Node.js not found on PATH." -ForegroundColor Red
18
+ Write-Host " Install Node.js (>= $minMajor) from https://nodejs.org, then re-run." -ForegroundColor Yellow
19
+ exit 1
20
+ }
21
+
22
+ $parts = $nodeVer.Split('.')
23
+ $major = [int]$parts[0]
24
+ if ($major -lt $minMajor) {
25
+ Write-Host " ✗ Node.js $nodeVer is too old (pi-para requires Node >= $minMajor)" -ForegroundColor Red
26
+ Write-Host " Upgrade Node.js: https://nodejs.org" -ForegroundColor Yellow
27
+ exit 1
28
+ }
29
+
30
+ # Resolve npx.cmd explicitly. Bare `npx` resolves to npx.ps1, which fails
31
+ # under restricted PowerShell execution policies.
32
+ $npx = $null
33
+ $npxCmd = Get-Command npx.cmd -ErrorAction SilentlyContinue
34
+ if ($npxCmd) {
35
+ $npx = $npxCmd.Source
36
+ } else {
37
+ $nodeCmd = Get-Command node -ErrorAction SilentlyContinue
38
+ if ($nodeCmd) {
39
+ $candidate = Join-Path (Split-Path $nodeCmd.Source) "npx.cmd"
40
+ if (Test-Path $candidate) { $npx = $candidate }
41
+ }
42
+ }
43
+ if (-not $npx) {
44
+ Write-Host " ✗ npx.cmd not found." -ForegroundColor Red
45
+ Write-Host " Install Node.js (>= $minMajor) from https://nodejs.org, then re-run." -ForegroundColor Yellow
46
+ exit 1
47
+ }
48
+
49
+ Write-Host " → Using $npx (Node $nodeVer)" -ForegroundColor Gray
50
+ Write-Host ""
51
+
52
+ # Always pin @latest so npx does a registry lookup instead of reusing an older
53
+ # cached package.
54
+ & $npx -y $packageLatest setup @args
55
+ exit $LASTEXITCODE
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ # pi-para — Interactive Setup
5
+ # Usage:
6
+ # curl -fsSL https://raw.githubusercontent.com/picassio/pi-para/main/scripts/install.sh | bash
7
+
8
+ PACKAGE="pi-para"
9
+ # qmd-engine (embedded search SDK) requires Node >= 22.
10
+ MIN_NODE_MAJOR=22
11
+ MIN_NODE_MINOR=0
12
+
13
+ check_node_version() {
14
+ if ! command -v node >/dev/null 2>&1; then
15
+ return 1
16
+ fi
17
+
18
+ local version major minor
19
+ version=$(node -v 2>/dev/null | sed 's/^v//')
20
+ major=$(echo "$version" | cut -d. -f1)
21
+ minor=$(echo "$version" | cut -d. -f2)
22
+
23
+ if [ "${major:-0}" -lt "$MIN_NODE_MAJOR" ]; then
24
+ return 1
25
+ fi
26
+ if [ "$major" -eq "$MIN_NODE_MAJOR" ] && [ "${minor:-0}" -lt "$MIN_NODE_MINOR" ]; then
27
+ return 1
28
+ fi
29
+ return 0
30
+ }
31
+
32
+ main() {
33
+ echo ""
34
+ echo " 🗂️ pi-para — Setup"
35
+ echo " ──────────────────"
36
+ echo ""
37
+
38
+ if check_node_version && command -v npx >/dev/null 2>&1; then
39
+ local node_version
40
+ node_version=$(node -v 2>/dev/null | sed 's/^v//')
41
+ echo " → Using npx (Node $node_version)"
42
+ echo ""
43
+
44
+ # Always pin @latest so npx does a registry lookup instead of reusing an
45
+ # older cached package. Redirect stdin from /dev/tty when available so
46
+ # interactive prompts still work under curl | bash.
47
+ if [ -r /dev/tty ]; then
48
+ npx -y "$PACKAGE@latest" setup "$@" </dev/tty
49
+ else
50
+ npx -y "$PACKAGE@latest" setup "$@"
51
+ fi
52
+ else
53
+ echo " ✗ Node $MIN_NODE_MAJOR.$MIN_NODE_MINOR+ with npx is required."
54
+ echo ""
55
+ echo " Install Node from https://nodejs.org, then run:"
56
+ echo ""
57
+ echo " npx $PACKAGE@latest setup"
58
+ echo ""
59
+ exit 1
60
+ fi
61
+ }
62
+
63
+ main "$@"
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env node
2
+ /* v8 ignore start */
3
+ import { mkdtemp, rm, access } from "node:fs/promises";
4
+ import { constants } from "node:fs";
5
+ import { tmpdir } from "node:os";
6
+ import { join, resolve, dirname } from "node:path";
7
+ import { fileURLToPath } from "node:url";
8
+ import { execFile } from "node:child_process";
9
+ import { promisify } from "node:util";
10
+
11
+ const execFileAsync = promisify(execFile);
12
+ const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
13
+ const cli = join(root, "dist", "cli.js");
14
+
15
+ async function main() {
16
+ await access(cli, constants.R_OK).catch(() => {
17
+ throw new Error("dist/cli.js not found. Run npm run build before smoke-install.");
18
+ });
19
+
20
+ const home = await mkdtemp(join(tmpdir(), "pi-para-smoke-"));
21
+ // os.homedir() reads HOME on POSIX and USERPROFILE on Windows — set both.
22
+ const env = { ...process.env, HOME: home, USERPROFILE: home };
23
+
24
+ try {
25
+ await run([cli, "setup", "--yes", "--local", root], env);
26
+
27
+ const status = JSON.parse((await run([cli, "status", "--json"], env)).stdout);
28
+ assert(status.configPath?.startsWith(home), "status configPath should use smoke HOME");
29
+ assert(status.wikiDir?.startsWith(home), "status wikiDir should use smoke HOME");
30
+ assert(typeof status.pages?.total === "number", "status pages.total should be numeric");
31
+ assert(typeof status.scheduler?.queued === "number", "status scheduler.queued should be numeric");
32
+
33
+ const doctor = JSON.parse((await run([cli, "doctor", "--json"], env)).stdout);
34
+ assert(doctor.ok === true, "doctor should pass in smoke HOME");
35
+
36
+ console.log("pi-para smoke install passed");
37
+ console.log(` HOME: ${home}`);
38
+ console.log(` Config: ${status.configPath}`);
39
+ console.log(` Wiki: ${status.wikiDir}`);
40
+ } finally {
41
+ if (!process.env.PI_PARA_KEEP_SMOKE_HOME) {
42
+ await rm(home, { recursive: true, force: true });
43
+ }
44
+ }
45
+ }
46
+
47
+ async function run(args, env) {
48
+ const [file, ...rest] = args;
49
+ return execFileAsync(process.execPath, [file, ...rest], {
50
+ cwd: root,
51
+ env,
52
+ maxBuffer: 10 * 1024 * 1024,
53
+ });
54
+ }
55
+
56
+ function assert(condition, message) {
57
+ if (!condition) throw new Error(message);
58
+ }
59
+
60
+ main().catch((err) => {
61
+ console.error(err instanceof Error ? err.message : err);
62
+ process.exit(1);
63
+ });
@@ -45,7 +45,7 @@ Write to the wiki immediately when you:
45
45
  - Learn how a system actually works (vs. how you assumed)
46
46
  - Establish a convention or pattern others should follow
47
47
 
48
- Use `wiki_write(mode: 'edit')` for surgical updates to existing pages. Use `wiki_write(mode: 'append')` to add new sections. Don't defer — context and reasoning degrade with time.
48
+ Use `wiki_edit` for surgical updates to existing pages. Use `wiki_write(mode: 'append')` to add new sections and `wiki_write(mode: 'replace')` only for intentional full-page rewrites. Don't defer — context and reasoning degrade with time.
49
49
 
50
50
  ## 4. When Debugging — Search First
51
51
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: setup
3
- description: Set up pi-para PARA knowledge base extension. Installs qmd search engine, configures providers, starts background capture daemon. Use when user asks to install, set up, or configure pi-para wiki.
3
+ description: Set up pi-para PARA knowledge base extension. Configures no-daemon scheduler runtime, embedded QMD SDK, provider profiles, and diagnostics. Use when user asks to install, set up, or configure pi-para wiki.
4
4
  ---
5
5
 
6
6
  # pi-para Setup
@@ -0,0 +1,17 @@
1
+ import { closeSync, fsyncSync, openSync, renameSync, writeFileSync } from "node:fs";
2
+ import { mkdir } from "node:fs/promises";
3
+ import { dirname, join } from "node:path";
4
+ import { randomUUID } from "node:crypto";
5
+
6
+ export async function atomicWriteFile(path: string, content: string, mode?: number): Promise<void> {
7
+ await mkdir(dirname(path), { recursive: true });
8
+ const tempPath = join(dirname(path), `.${randomUUID()}.tmp`);
9
+ const fd = openSync(tempPath, "w", mode);
10
+ try {
11
+ writeFileSync(fd, content, "utf-8");
12
+ fsyncSync(fd);
13
+ } finally {
14
+ closeSync(fd);
15
+ }
16
+ renameSync(tempPath, path);
17
+ }
package/src/capture.ts CHANGED
@@ -8,10 +8,10 @@
8
8
  * Both are fully automatic — no user confirmation prompts.
9
9
  */
10
10
 
11
- import type { Model } from "@mariozechner/pi-ai";
12
- import { Agent } from "@mariozechner/pi-agent-core";
13
- import type { AgentMessage, AgentEvent } from "@mariozechner/pi-agent-core";
14
- import type { ModelRegistry } from "@mariozechner/pi-coding-agent";
11
+ import type { Model } from "@earendil-works/pi-ai";
12
+ import { Agent } from "@earendil-works/pi-agent-core";
13
+ import type { AgentMessage, AgentEvent } from "@earendil-works/pi-agent-core";
14
+ import type { ModelRegistry } from "@earendil-works/pi-coding-agent";
15
15
  import type { QMDStore } from "qmd-engine";
16
16
 
17
17
  import type { ProjectScope } from "./scope.js";
@@ -0,0 +1,43 @@
1
+ export const LEGACY_DAEMON_COMMANDS = new Set([
2
+ "start",
3
+ "stop",
4
+ "legacy-status",
5
+ "process",
6
+ "process-recent",
7
+ "retry-failed",
8
+ "history",
9
+ ]);
10
+
11
+ export function isLegacyDaemonCommand(command: string | undefined): boolean {
12
+ return command !== undefined && LEGACY_DAEMON_COMMANDS.has(command);
13
+ }
14
+
15
+ export function formatLegacyDaemonWarning(command: string): string {
16
+ const replacement = replacementForLegacyCommand(command);
17
+ return [
18
+ `[pi-para] Warning: '${command}' is a deprecated legacy daemon command.`,
19
+ "pi-para now uses an in-process scheduler while Pi is open; no system daemon is required.",
20
+ replacement ? `Use ${replacement} for the current workflow.` : "Use 'pi-para tasks' or '/wiki-scheduler status' for the current workflow.",
21
+ ].join("\n");
22
+ }
23
+
24
+ function replacementForLegacyCommand(command: string): string | null {
25
+ switch (command) {
26
+ case "start":
27
+ return "'pi-para setup' and restart Pi";
28
+ case "stop":
29
+ return "'/wiki-scheduler status' or close/restart Pi sessions";
30
+ case "legacy-status":
31
+ return "'pi-para tasks' or '/wiki-scheduler status'";
32
+ case "process":
33
+ return "'pi-para capture-recent --hours N' or '/wiki-capture'";
34
+ case "process-recent":
35
+ return "'pi-para capture-recent --hours N'";
36
+ case "retry-failed":
37
+ return "'pi-para tasks retry'";
38
+ case "history":
39
+ return "'pi-para tasks history'";
40
+ default:
41
+ return null;
42
+ }
43
+ }
package/src/cli.ts CHANGED
@@ -1,15 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * CLI for pi-para-daemon.
3
+ * CLI for pi-para.
4
4
  *
5
- * Usage:
6
- * pi-para-daemon start [--foreground]
7
- * pi-para-daemon stop
8
- * pi-para-daemon status
9
- * pi-para-daemon process <session_file>
10
- * pi-para-daemon process-recent [--hours N]
11
- * pi-para-daemon retry-failed
12
- * pi-para-daemon history [--scope NAME]
5
+ * The old pi-para-daemon commands remain as deprecated compatibility aliases.
13
6
  */
14
7
 
15
8
  import { join } from "node:path";
@@ -19,6 +12,7 @@ import { existsSync, readdirSync, statSync } from "node:fs";
19
12
  import { Daemon } from "./daemon.js";
20
13
  import { StateDB } from "./state.js";
21
14
  import { RegistryWatcher } from "./watcher.js";
15
+ import { formatLegacyDaemonWarning, isLegacyDaemonCommand } from "./cli-legacy.js";
22
16
 
23
17
  // -- Model setup -------------------------------------------------------------
24
18
  // Use MiniMax via the same config as qmd-engine
@@ -26,7 +20,7 @@ import { RegistryWatcher } from "./watcher.js";
26
20
  async function createModel(modelArg?: string) {
27
21
  const { parse } = await import("yaml");
28
22
  const { readFileSync } = await import("node:fs");
29
- const { getModel, getProviders, getEnvApiKey } = await import("@mariozechner/pi-ai");
23
+ const { getModel, getProviders, getEnvApiKey } = await import("@earendil-works/pi-ai/compat");
30
24
 
31
25
  // 0. Try config.json daemonModel
32
26
  if (!modelArg) {
@@ -53,7 +47,7 @@ async function createModel(modelArg?: string) {
53
47
  // Resolve API key: try AuthStorage first (OAuth), then env vars
54
48
  let authStore: any = null;
55
49
  try {
56
- const { AuthStorage } = await import("@mariozechner/pi-coding-agent");
50
+ const { AuthStorage } = await import("@earendil-works/pi-coding-agent");
57
51
  authStore = AuthStorage.create();
58
52
  } catch {}
59
53
  const getApiKey = async (p: string) => {
@@ -71,9 +65,9 @@ async function createModel(modelArg?: string) {
71
65
 
72
66
  // 2. Try pi's auth storage (auth.json) — supports OAuth (Anthropic, GitHub Copilot, etc.)
73
67
  try {
74
- const { AuthStorage } = await import("@mariozechner/pi-coding-agent");
68
+ const { AuthStorage } = await import("@earendil-works/pi-coding-agent");
75
69
  const authStorage = AuthStorage.create();
76
- const { getModels } = await import("@mariozechner/pi-ai");
70
+ const { getModels } = await import("@earendil-works/pi-ai/compat");
77
71
 
78
72
  // Check providers in preference order: anthropic first (best quality)
79
73
  const preferredProviders = ["anthropic", "openai", "openrouter", "google-antigravity", "github-copilot"];
@@ -103,7 +97,7 @@ async function createModel(modelArg?: string) {
103
97
  for (const provider of providers) {
104
98
  const key = getEnvApiKey(provider);
105
99
  if (key) {
106
- const { getModels } = await import("@mariozechner/pi-ai");
100
+ const { getModels } = await import("@earendil-works/pi-ai/compat");
107
101
  const models = getModels(provider as any);
108
102
  const sorted = [...models].sort((a, b) => (b.contextWindow ?? 0) - (a.contextWindow ?? 0));
109
103
  const picked = sorted.find(m => !m.reasoning) ?? sorted[0];
@@ -150,7 +144,102 @@ async function main() {
150
144
  const command = args[0];
151
145
  const wikiDir = join(homedir(), ".pi", "wiki");
152
146
 
147
+ if (isLegacyDaemonCommand(command)) {
148
+ console.warn(formatLegacyDaemonWarning(command));
149
+ console.warn("");
150
+ }
151
+
153
152
  switch (command) {
153
+ case "setup": {
154
+ const dryRun = args.includes("--dry-run");
155
+ const localIdx = args.indexOf("--local");
156
+ const localPath = localIdx >= 0 ? args[localIdx + 1] : undefined;
157
+ const { runSetup, formatSetupResult } = await import("./setup.js");
158
+ const result = await runSetup({ yes: args.includes("--yes"), dryRun, localPath });
159
+ console.log(formatSetupResult(result));
160
+ break;
161
+ }
162
+
163
+ case "doctor": {
164
+ const json = args.includes("--json");
165
+ const { runDoctor, formatDoctorResult } = await import("./doctor.js");
166
+ const result = await runDoctor({
167
+ fix: args.includes("--fix"),
168
+ testCaptureModel: args.includes("--test-capture-model"),
169
+ });
170
+ console.log(json ? JSON.stringify(result, null, 2) : formatDoctorResult(result));
171
+ break;
172
+ }
173
+
174
+ case "tasks": {
175
+ const sub = args[1] ?? "list";
176
+ const {
177
+ listSchedulerTasks,
178
+ retryFailedSchedulerTasks,
179
+ getSchedulerTask,
180
+ listSchedulerHistory,
181
+ formatQueueItems,
182
+ formatQueueItem,
183
+ formatSchedulerHistory,
184
+ } = await import("./scheduler/controls.js");
185
+ if (sub === "retry") {
186
+ const taskIdx = args.indexOf("--task");
187
+ const taskName = taskIdx >= 0 ? args[taskIdx + 1] : undefined;
188
+ const count = retryFailedSchedulerTasks(wikiDir, { taskName });
189
+ console.log(`Requeued ${count} failed task${count === 1 ? "" : "s"}.`);
190
+ } else if (sub === "show") {
191
+ const id = parseInt(args[2] ?? "", 10);
192
+ if (!Number.isFinite(id)) { console.error("Usage: pi-para tasks show <id>"); process.exit(1); }
193
+ console.log(formatQueueItem(getSchedulerTask(wikiDir, id)));
194
+ } else if (sub === "history") {
195
+ const taskIdx = args.indexOf("--task");
196
+ const limitIdx = args.indexOf("--limit");
197
+ const taskName = taskIdx >= 0 ? args[taskIdx + 1] : undefined;
198
+ const limit = limitIdx >= 0 ? parseInt(args[limitIdx + 1] ?? "20", 10) : 20;
199
+ console.log(formatSchedulerHistory(listSchedulerHistory(wikiDir, { taskName, limit })));
200
+ } else {
201
+ const statusIdx = args.indexOf("--status");
202
+ const status = statusIdx >= 0 ? args[statusIdx + 1] as any : undefined;
203
+ console.log(formatQueueItems(listSchedulerTasks(wikiDir, { status })));
204
+ }
205
+ break;
206
+ }
207
+
208
+ case "capture-recent": {
209
+ const hoursIdx = args.indexOf("--hours");
210
+ const hours = hoursIdx >= 0 ? parseInt(args[hoursIdx + 1] ?? "24") : 24;
211
+ const { queueCaptureRecent, formatQueueCaptureRecentResult } = await import("./scheduler/controls.js");
212
+ const result = await queueCaptureRecent({ wikiDir, hours });
213
+ console.log(formatQueueCaptureRecentResult(result));
214
+ console.log("Queued captures run when a Pi session with pi-para is open.");
215
+ break;
216
+ }
217
+
218
+ case "providers": {
219
+ const sub = args[1] ?? "list";
220
+ const { readSecretStore, setSecret, removeSecret, redactCredential } = await import("./credentials.js");
221
+ if (sub === "set-secret") {
222
+ const name = args[2];
223
+ const value = args[3];
224
+ if (!name || !value) { console.error("Usage: pi-para providers set-secret <name> <value>"); process.exit(1); }
225
+ await setSecret(name, value);
226
+ console.log(`Stored secret:${name}`);
227
+ } else if (sub === "remove-secret") {
228
+ const name = args[2];
229
+ if (!name) { console.error("Usage: pi-para providers remove-secret <name>"); process.exit(1); }
230
+ await removeSecret(name);
231
+ console.log(`Removed secret:${name}`);
232
+ } else {
233
+ const secrets = await readSecretStore();
234
+ const names = Object.keys(secrets.secrets);
235
+ console.log("pi-para providers");
236
+ console.log(" Credential policy: Pi AuthStorage preferred; pi-para secrets fallback; env vars not used by setup.");
237
+ if (names.length === 0) console.log(" No pi-para local secrets configured.");
238
+ for (const name of names) console.log(` secret:${name} = ${redactCredential(secrets.secrets[name])}`);
239
+ }
240
+ break;
241
+ }
242
+
154
243
  case "start": {
155
244
  const modelIdx = args.indexOf("--model");
156
245
  const modelArg = modelIdx >= 0 ? args[modelIdx + 1] : undefined;
@@ -210,6 +299,14 @@ async function main() {
210
299
  }
211
300
 
212
301
  case "status": {
302
+ const json = args.includes("--json");
303
+ const { getPiParaStatus, formatPiParaStatus } = await import("./status.js");
304
+ const result = await getPiParaStatus();
305
+ console.log(json ? JSON.stringify(result, null, 2) : formatPiParaStatus(result));
306
+ break;
307
+ }
308
+
309
+ case "legacy-status": {
213
310
  const state = new StateDB(wikiDir);
214
311
  const pid = state.getState("daemon_pid");
215
312
  const startedAt = state.getState("daemon_started_at");
@@ -358,7 +455,7 @@ async function main() {
358
455
  break;
359
456
  }
360
457
  default:
361
- console.log(`pi-para-daemon gepa — GEPA prompt optimizer (DSPy GEPA via uv)
458
+ console.log(`pi-para gepa — GEPA prompt optimizer (DSPy GEPA via uv)
362
459
 
363
460
  Subcommands:
364
461
  optimize Run DSPy GEPA optimization
@@ -387,25 +484,33 @@ Config (persistent defaults in ~/.pi/wiki/config.json):
387
484
  }
388
485
 
389
486
  default:
390
- console.log(`pi-para-daemon — background knowledge capture
487
+ console.log(`pi-para — PARA wiki extension CLI
488
+
489
+ Primary commands:
490
+ setup Configure pi-para for this machine (--yes, --dry-run, --local PATH)
491
+ doctor Validate install/config/wiki/QMD health (--fix, --json, --test-capture-model)
492
+ tasks Show scheduler queue (--status queued|running|done|failed)
493
+ tasks show <id> Show one queued/running/done/failed task payload
494
+ tasks history Show scheduler history [--task NAME] [--limit N]
495
+ tasks retry Requeue failed scheduler tasks [--task NAME]
496
+ status Show config/wiki/scheduler status (--json)
497
+ capture-recent Queue recent completed sessions for capture (--hours N)
498
+ providers List credential refs and local secrets
499
+ providers set-secret <name> <value>
500
+ providers remove-secret <name>
501
+ gepa <subcommand> GEPA prompt optimizer (optimize, list, targets, compare)
391
502
 
392
- Commands:
393
- start Start the daemon (foreground)
394
- stop Stop the running daemon
395
- status Show daemon status and recent history
503
+ Legacy compatibility commands:
504
+ start Deprecated daemon foreground mode
505
+ stop Deprecated daemon stop
506
+ legacy-status Legacy daemon status and recent history
396
507
  process <file> Process a single session file
397
508
  process-recent Process unprocessed sessions (--hours N, default 24)
398
509
  retry-failed Retry all failed sessions
399
510
  history Show processing history (--scope NAME to filter)
400
- gepa <subcommand> GEPA prompt optimizer (optimize, list, targets, compare)
401
-
402
- Options:
403
- --model <provider/id> Use specific model (e.g. anthropic/claude-sonnet-4)
404
511
 
405
- Model resolution order:
406
- 1. --model flag
407
- 2. Pi's model registry (ANTHROPIC_API_KEY, OPENAI_API_KEY, etc.)
408
- 3. ~/.config/qmd/index.yml chat provider (MiniMax, OpenRouter, etc.)`);
512
+ Credential policy:
513
+ Use Pi AuthStorage or ~/.pi/para/secrets.json. Setup does not use env vars for API keys.`);
409
514
  break;
410
515
  }
411
516
  }