@lincoln504/pi-research 0.1.13 → 1.0.1

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 (248) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +55 -138
  3. package/dist/cli.mjs +23375 -0
  4. package/dist/prompts/research-knowledge-relevance-triage.md +35 -0
  5. package/dist/prompts/research-knowledge-search-extractor.md +41 -0
  6. package/dist/prompts/research-tool-usage.md +92 -0
  7. package/dist/prompts/researcher.md +89 -0
  8. package/dist/prompts/system-coordinator.md +75 -0
  9. package/dist/prompts/system-lead-evaluator.md +118 -0
  10. package/dist/thread-worker.mjs +2611 -0
  11. package/dist/webgpu-probe.mjs +80 -0
  12. package/docs/AGENT-SKILL.md +130 -0
  13. package/docs/ARCHITECTURE.md +306 -80
  14. package/docs/CONFIGURATION.md +249 -0
  15. package/docs/KNOWLEDGE-STORE.md +211 -0
  16. package/docs/PI-EXTENSION.md +91 -0
  17. package/docs/SDK.md +122 -0
  18. package/docs/THIRD-PARTY-NOTICES.md +66 -0
  19. package/package.json +102 -36
  20. package/scripts/build.cjs +140 -0
  21. package/scripts/cleanup.cjs +149 -0
  22. package/scripts/prepare.cjs +28 -0
  23. package/scripts/setup.cjs +134 -0
  24. package/skills/pi-research/README.md +90 -0
  25. package/skills/pi-research/SKILL.md +102 -0
  26. package/skills/pi-research/references/configuration.md +77 -0
  27. package/skills/pi-research/scripts/run.mjs +237 -0
  28. package/src/cleanup/research-cleanup.ts +140 -0
  29. package/src/cli.ts +1195 -0
  30. package/src/config.ts +1032 -155
  31. package/src/constants.ts +115 -84
  32. package/src/core/health-check-service.ts +265 -0
  33. package/src/core/interfaces/health-check-interfaces.ts +50 -0
  34. package/src/core/interfaces/knowledge-interfaces.ts +190 -0
  35. package/src/core/interfaces/observer-interfaces.ts +67 -0
  36. package/src/core/interfaces/orchestration-interfaces.ts +86 -0
  37. package/src/core/interfaces/planning-interfaces.ts +100 -0
  38. package/src/core/interfaces/process-interfaces.ts +37 -0
  39. package/src/core/interfaces/research-plan-types.ts +52 -0
  40. package/src/core/interfaces/scheduler-interfaces.ts +91 -0
  41. package/src/core/interfaces/service-names.ts +39 -0
  42. package/src/core/interfaces/state-manager-interfaces.ts +29 -0
  43. package/src/core/interfaces/steering-interfaces.ts +31 -0
  44. package/src/core/llm/agentic-repair.ts +169 -0
  45. package/src/core/llm/inject-date.ts +52 -0
  46. package/src/core/llm/llm-timeout.ts +64 -0
  47. package/src/core/llm/llm-utils.ts +86 -0
  48. package/src/core/llm/model-registry-factory.ts +287 -0
  49. package/src/core/llm/pi-ai-completion.ts +29 -0
  50. package/src/core/llm/prompts.ts +50 -0
  51. package/src/core/llm/research-model-resolver.ts +91 -0
  52. package/src/core/planning-constants.ts +13 -0
  53. package/src/core/planning-service.ts +649 -0
  54. package/src/core/planning-utils.ts +299 -0
  55. package/src/core/scheduler-factory.ts +48 -0
  56. package/src/core/scheduler-service.ts +123 -0
  57. package/src/core/service-initialization.ts +194 -0
  58. package/src/core/service-interfaces.ts +48 -0
  59. package/src/core/service-registry.ts +659 -0
  60. package/src/healthcheck/index.ts +271 -265
  61. package/src/healthcheck/registry.ts +130 -0
  62. package/src/index.ts +639 -50
  63. package/src/infrastructure/browser/browser-cleanup.ts +309 -0
  64. package/src/infrastructure/browser/browser-client.ts +199 -0
  65. package/src/infrastructure/browser/browser-error-utils.ts +160 -0
  66. package/src/infrastructure/browser/browser-lifecycle.ts +120 -0
  67. package/src/infrastructure/browser/browser-server.ts +174 -0
  68. package/src/infrastructure/browser/browser-task-scheduler.ts +461 -0
  69. package/src/infrastructure/browser/cleanup-utils.ts +155 -0
  70. package/src/infrastructure/browser/client-agent.ts +31 -0
  71. package/src/infrastructure/browser/config.ts +288 -0
  72. package/src/infrastructure/browser/ensure-browser.ts +205 -0
  73. package/src/infrastructure/browser/index.ts +37 -0
  74. package/src/infrastructure/browser/priority-task-queue.ts +226 -0
  75. package/src/infrastructure/browser/scheduler-factory.ts +354 -0
  76. package/src/infrastructure/browser/task-execution-service.ts +209 -0
  77. package/src/infrastructure/browser/thread-worker-browser.ts +282 -0
  78. package/src/infrastructure/browser/thread-worker-lifecycle.ts +323 -0
  79. package/src/infrastructure/browser/thread-worker-messaging.ts +662 -0
  80. package/src/infrastructure/browser/thread-worker-types.ts +25 -0
  81. package/src/infrastructure/browser/thread-worker.mjs +2611 -0
  82. package/src/infrastructure/browser/thread-worker.ts +168 -0
  83. package/src/infrastructure/browser/worker-pool-manager.ts +449 -0
  84. package/src/infrastructure/embedding/embedding-client.ts +205 -0
  85. package/src/infrastructure/embedding/embedding-factory.ts +324 -0
  86. package/src/infrastructure/embedding/embedding-server.ts +535 -0
  87. package/src/infrastructure/file-lock-service.ts +592 -0
  88. package/src/infrastructure/gpu-resource-service.ts +191 -0
  89. package/src/infrastructure/knowledge-store-service.ts +475 -0
  90. package/src/infrastructure/metrics-service.ts +129 -0
  91. package/src/infrastructure/process-lifecycle-service.ts +253 -0
  92. package/src/infrastructure/scheduler-factory-service.ts +58 -0
  93. package/src/infrastructure/service-initialization.ts +292 -0
  94. package/src/infrastructure/state/state-backup-manager.ts +290 -0
  95. package/src/infrastructure/state/state-browser-api.ts +62 -0
  96. package/src/infrastructure/state/state-browser-manager.ts +70 -0
  97. package/src/infrastructure/state/state-manager-service.ts +264 -0
  98. package/src/infrastructure/state/state-manager.ts +547 -0
  99. package/src/infrastructure/state/state-metrics.ts +92 -0
  100. package/src/infrastructure/state/state-migration.ts +103 -0
  101. package/src/infrastructure/state/state-path-configuration.ts +119 -0
  102. package/src/infrastructure/state/state-session-api.ts +94 -0
  103. package/src/infrastructure/state/state-session-manager.ts +127 -0
  104. package/src/infrastructure/state/state-validator.ts +93 -0
  105. package/src/infrastructure/types/state-types.ts +85 -0
  106. package/src/knowledge/chunker.ts +157 -0
  107. package/src/knowledge/embedder-init.ts +413 -0
  108. package/src/knowledge/embedder-utils.ts +296 -0
  109. package/src/knowledge/embedder.ts +592 -0
  110. package/src/knowledge/index.ts +126 -0
  111. package/src/knowledge/lancedb-loader.ts +35 -0
  112. package/src/knowledge/migration.ts +17 -0
  113. package/src/knowledge/model-config.ts +132 -0
  114. package/src/knowledge/onnx-env.ts +48 -0
  115. package/src/knowledge/store-operations.ts +291 -0
  116. package/src/knowledge/store-schema.ts +79 -0
  117. package/src/knowledge/store.ts +1251 -0
  118. package/src/knowledge/transformers-loader.ts +31 -0
  119. package/src/knowledge/webgpu-probe.mjs +80 -0
  120. package/src/knowledge/webgpu-viability.ts +255 -0
  121. package/src/knowledge/writer-queue.ts +252 -0
  122. package/src/logger.ts +316 -122
  123. package/src/observers/research-observer-impl.ts +459 -0
  124. package/src/orchestration/deep-research-orchestrator.ts +524 -773
  125. package/src/orchestration/deep-research-types.ts +47 -35
  126. package/src/orchestration/headless-observer.ts +114 -0
  127. package/src/orchestration/orchestration-types.ts +33 -0
  128. package/src/orchestration/quick-research-orchestrator.ts +463 -0
  129. package/src/orchestration/research-orchestration-service.ts +549 -0
  130. package/src/orchestration/research-session-service.ts +178 -0
  131. package/src/orchestration/research-synthesis-service.ts +348 -0
  132. package/src/orchestration/researcher-executor.ts +394 -0
  133. package/src/orchestration/researcher.ts +115 -42
  134. package/src/orchestration/service-initialization.ts +109 -0
  135. package/src/orchestration/session-context.ts +7 -3
  136. package/src/orchestration/session-state.ts +672 -0
  137. package/src/prompts/research-knowledge-relevance-triage.md +35 -0
  138. package/src/prompts/research-knowledge-search-extractor.md +41 -0
  139. package/src/prompts/research-tool-usage.md +63 -16
  140. package/src/prompts/researcher.md +59 -136
  141. package/src/prompts/system-coordinator.md +64 -13
  142. package/src/prompts/system-lead-evaluator.md +82 -189
  143. package/src/research-config.ts +872 -0
  144. package/src/sdk.ts +847 -0
  145. package/src/security/cisa-kev.ts +72 -9
  146. package/src/security/cvss.ts +92 -0
  147. package/src/security/ecosystem.ts +45 -0
  148. package/src/security/github-advisories.ts +168 -428
  149. package/src/security/github-advisory-types.ts +282 -0
  150. package/src/security/index.ts +63 -177
  151. package/src/security/nvd-client.ts +550 -0
  152. package/src/security/nvd-types.ts +179 -0
  153. package/src/security/osv-client.ts +246 -0
  154. package/src/security/osv-types.ts +285 -0
  155. package/src/security/types.ts +67 -53
  156. package/src/skill-install/skill-installer.ts +519 -0
  157. package/src/stackexchange/index.ts +81 -27
  158. package/src/stackexchange/output/compact.ts +7 -6
  159. package/src/stackexchange/output/table.ts +11 -7
  160. package/src/stackexchange/queries.ts +0 -10
  161. package/src/stackexchange/rest-client.ts +141 -66
  162. package/src/stackexchange/types.ts +0 -17
  163. package/src/tool.ts +15 -423
  164. package/src/tools/grep.ts +32 -310
  165. package/src/tools/health-tool-definition.ts +101 -0
  166. package/src/tools/index.ts +38 -9
  167. package/src/tools/research-knowledge-search.ts +974 -0
  168. package/src/tools/research-knowledge-types.ts +77 -0
  169. package/src/tools/research-tool-definition.ts +494 -0
  170. package/src/tools/scrape.ts +286 -388
  171. package/src/tools/search.ts +98 -153
  172. package/src/tools/security.ts +107 -80
  173. package/src/tools/stackexchange.ts +58 -35
  174. package/src/tools/youtube-transcript.ts +163 -0
  175. package/src/tui/knowledge-search-panel.ts +86 -0
  176. package/src/tui/pulse.ts +86 -0
  177. package/src/tui/research-health.ts +124 -0
  178. package/src/tui/research-panel-color-utils.ts +340 -0
  179. package/src/tui/research-panel-format.ts +54 -0
  180. package/src/tui/research-panel-state.ts +404 -0
  181. package/src/tui/research-panel-wave.ts +280 -0
  182. package/src/tui/research-panel.ts +339 -435
  183. package/src/tui/research-tui-manager.ts +233 -0
  184. package/src/tui/tui-controller.ts +137 -0
  185. package/src/types/extension-context.ts +18 -49
  186. package/src/types/index.ts +61 -0
  187. package/src/types/llm.ts +68 -29
  188. package/src/types/research-panel-types.ts +61 -0
  189. package/src/utils/circuit-breaker.ts +146 -0
  190. package/src/utils/citation-utils.ts +97 -0
  191. package/src/utils/disk-space-checker.ts +55 -0
  192. package/src/utils/error-tracker.ts +265 -0
  193. package/src/utils/host-config.ts +45 -0
  194. package/src/utils/input-validation.ts +8 -10
  195. package/src/utils/json-utils.ts +150 -16
  196. package/src/utils/llm-usage.ts +54 -0
  197. package/src/utils/log-rotation.ts +125 -0
  198. package/src/utils/log-utils.ts +159 -0
  199. package/src/utils/make-resource-loader.ts +3 -1
  200. package/src/utils/metrics-summary.ts +459 -0
  201. package/src/utils/metrics.ts +302 -0
  202. package/src/utils/research-export.ts +28 -12
  203. package/src/utils/safe-unref.ts +31 -0
  204. package/src/utils/shared-links.ts +195 -90
  205. package/src/utils/shutdown-manager.ts +65 -0
  206. package/src/utils/stdio-capture.ts +407 -0
  207. package/src/utils/text-utils.ts +258 -11
  208. package/src/utils/tool-usage-tracker.ts +83 -16
  209. package/src/utils/url-utils.ts +255 -0
  210. package/src/utils/user-agent.ts +28 -0
  211. package/src/web-research/browser-search.ts +179 -0
  212. package/src/web-research/retry-utils.ts +173 -77
  213. package/src/web-research/scraper-types.ts +72 -0
  214. package/src/web-research/scraper-utils.ts +434 -0
  215. package/src/web-research/search.ts +78 -218
  216. package/src/web-research/types.ts +9 -24
  217. package/src/web-research/utils.ts +2 -143
  218. package/src/web-research/web-scraper.ts +477 -0
  219. package/src/youtube/index.ts +154 -0
  220. package/src/youtube/potoken.ts +259 -0
  221. package/src/youtube/transcript-client.ts +390 -0
  222. package/src/youtube/video-id.ts +118 -0
  223. package/config/searxng/default-settings.yml +0 -189
  224. package/config/searxng/limiter.toml +0 -34
  225. package/config/tooling/eslint.config.js +0 -73
  226. package/config/tooling/vitest.config.integration.ts +0 -17
  227. package/config/tooling/vitest.config.ts +0 -40
  228. package/config/tooling/vitest.config.unit.ts +0 -12
  229. package/docs/assets/README-banner.jpg +0 -0
  230. package/scripts/setup.js +0 -212
  231. package/src/infrastructure/searxng-lifecycle.ts +0 -644
  232. package/src/infrastructure/searxng-manager.ts +0 -1183
  233. package/src/infrastructure/state-manager.ts +0 -918
  234. package/src/orchestration/deep-research-reducer.ts +0 -129
  235. package/src/orchestration/id-utils.ts +0 -63
  236. package/src/security/nvd.ts +0 -532
  237. package/src/security/osv.ts +0 -487
  238. package/src/stackexchange/cache.ts +0 -111
  239. package/src/stackexchange/output/index.ts +0 -7
  240. package/src/stackexchange/output/json.ts +0 -21
  241. package/src/tui/searxng-status.ts +0 -80
  242. package/src/utils/inject-date.ts +0 -29
  243. package/src/utils/research-error.ts +0 -205
  244. package/src/utils/searxng-config.ts +0 -94
  245. package/src/utils/session-state.ts +0 -295
  246. package/src/web-research/dependencies.ts +0 -20
  247. package/src/web-research/scrapers.ts +0 -935
  248. package/tsconfig.json +0 -40
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Build all distribution artifacts.
5
+ *
6
+ * esbuild is a regular dependency, so it is always available on any install
7
+ * (including git installs and --omit=dev). dist/ is not tracked in git;
8
+ * this script always builds the artifacts from source.
9
+ *
10
+ * Artifacts built:
11
+ * - dist/thread-worker.mjs (browser worker, via build:worker)
12
+ * - dist/cli.mjs (+ dist/prompts, dist/webgpu-probe.mjs) (pi-research CLI binary, via build:cli)
13
+ * - skills/pi-research/scripts/run.mjs (skill launcher, via build:skill)
14
+ */
15
+
16
+ 'use strict';
17
+
18
+ const { execSync } = require('child_process');
19
+ const path = require('path');
20
+
21
+ const ROOT = path.join(__dirname, '..');
22
+
23
+ try {
24
+ execSync('node scripts/build.cjs all', { stdio: 'inherit', cwd: ROOT });
25
+ } catch (err) {
26
+ console.error('[prepare] Build failed:', err.message);
27
+ process.exit(1);
28
+ }
@@ -0,0 +1,134 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * pi-research postinstall setup.
5
+ * Installs Camoufox browser binaries.
6
+ * Never exits with code 1 — that would break npm install.
7
+ *
8
+ * Environment:
9
+ * PLAYWRIGHT_BROWSERS_PATH - override browser install location
10
+ * PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 - skip download entirely
11
+ * PLAYWRIGHT_INSTALL_DEPS=true - also install Linux system deps (or pass --system-deps)
12
+ */
13
+
14
+ const { execSync } = require('child_process');
15
+ const { existsSync, readdirSync, statSync } = require('fs');
16
+ const { homedir } = require('os');
17
+ const path = require('path');
18
+
19
+ // __dirname / __filename are built-in globals in CommonJS (.cjs) modules
20
+ const projectRoot = path.join(__dirname, '..');
21
+
22
+ const isLinux = process.platform === 'linux';
23
+ const isDarwin = process.platform === 'darwin';
24
+ const isWindows = process.platform === 'win32';
25
+
26
+ const [nodeMajor, nodeMinor] = process.version.replace('v', '').split('.').map((n) => parseInt(n, 10));
27
+ if (nodeMajor < 22 || (nodeMajor === 22 && nodeMinor < 19)) {
28
+ console.warn(`WARNING: Node.js ${process.version} is below the minimum (>=22.19.0). Upgrade to 22.19.0+.`);
29
+ }
30
+
31
+ /**
32
+ * Resolve the camoufox-js binary path.
33
+ *
34
+ * In a typical npm install, dependencies are hoisted to the root node_modules,
35
+ * so the package's own node_modules/ may be empty. We use require.resolve to
36
+ * find the real location of camoufox-js and derive the bin from there.
37
+ */
38
+ function resolveCamoufoxBin() {
39
+ try {
40
+ const pkgDir = path.dirname(require.resolve('camoufox-js/package.json', { paths: [projectRoot] }));
41
+ const binDir = path.join(pkgDir, '..', '.bin');
42
+ // On Windows the executable is the .cmd shim; the extensionless file is a sh
43
+ // script that only runs via PATHEXT luck. Mirror src ensure-browser.ts.
44
+ const bin = path.join(binDir, isWindows ? 'camoufox-js.cmd' : 'camoufox-js');
45
+ if (existsSync(bin)) return bin;
46
+ } catch (_) { /* fall through */ }
47
+ return null;
48
+ }
49
+
50
+ function camoufoxCachePath() {
51
+ const customPath = process.env.PLAYWRIGHT_BROWSERS_PATH;
52
+ if (customPath) return customPath;
53
+
54
+ if (isWindows) {
55
+ // Mirror camoufox-js userCacheDir("camoufox") exactly: homedir-based with a
56
+ // DOUBLED "camoufox" segment. Must match src getWindowsCamoufoxDir().
57
+ return path.join(homedir(), 'AppData', 'Local', 'camoufox', 'camoufox', 'Cache');
58
+ }
59
+ if (isDarwin) return path.join(homedir(), 'Library', 'Caches', 'camoufox');
60
+
61
+ const cacheHome = process.env.XDG_CACHE_HOME || path.join(homedir(), '.cache');
62
+ return path.join(cacheHome, 'camoufox');
63
+ }
64
+
65
+ let browsersInstalled = false;
66
+
67
+ if (process.env.PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD === '1') {
68
+ console.log('pi-research: skipping browser download (PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1)');
69
+ } else {
70
+ const env = { ...process.env };
71
+
72
+ const installDeps = process.argv.includes('--system-deps') || process.env.PLAYWRIGHT_INSTALL_DEPS === 'true';
73
+ if (installDeps && isLinux) {
74
+ try {
75
+ execSync('npx playwright install-deps', { stdio: 'inherit', env: { ...env, PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: '0' } });
76
+ } catch (e) {
77
+ console.warn(`WARNING: could not install system dependencies. Run: sudo apt-get install -y libgbm1 libnss3 libatk1.0-0 libatk-bridge2.0-0 libcups2 libxkbcommon0 libxcomposite1\nReason: ${e instanceof Error ? e.message : String(e)}\n(These are the libraries headless camoufox needs. Xvfb is NOT required for the default headless mode — only add it if you opt into virtual-display mode with PI_RESEARCH_USE_XVFB=true.)`);
78
+ }
79
+ }
80
+
81
+ const cachePath = camoufoxCachePath();
82
+ let alreadyInstalled = false;
83
+ if (existsSync(cachePath)) {
84
+ try {
85
+ const versions = readdirSync(cachePath).filter(f => statSync(path.join(cachePath, f)).isDirectory());
86
+ if (versions.length > 0) {
87
+ alreadyInstalled = true;
88
+ console.log(`pi-research: Camoufox already installed at ${cachePath}. Skipping fetch.`);
89
+ }
90
+ } catch (e) {
91
+ console.warn(`pi-research: error checking camoufox path ${cachePath}: ${e instanceof Error ? e.message : String(e)}`);
92
+ }
93
+ }
94
+
95
+ if (!alreadyInstalled) {
96
+ try {
97
+ const bin = resolveCamoufoxBin();
98
+ const cmd = bin ? `"${bin}" fetch` : 'npx camoufox-js fetch';
99
+ // Bound the ~100MB download so a stalled/interrupted network fails fast
100
+ // instead of hanging `npm install` forever (the catch below exits 0, and the
101
+ // browser is re-fetchable manually). 15 min is ample even on slow links.
102
+ execSync(cmd, { stdio: 'inherit', env, timeout: 15 * 60 * 1000 });
103
+ browsersInstalled = true;
104
+ } catch (error) {
105
+ const msg = error instanceof Error ? error.message : String(error);
106
+ console.error('ERROR: Camoufox browser install failed — pi-research will not work.');
107
+ console.error('Run manually to fix: npx camoufox-js fetch');
108
+ console.error(`Reason: ${msg}`);
109
+ process.exit(0);
110
+ }
111
+ } else {
112
+ browsersInstalled = true;
113
+ }
114
+ }
115
+
116
+ // Verify
117
+ const cachePath = camoufoxCachePath();
118
+ if (existsSync(cachePath)) {
119
+ try {
120
+ const versions = readdirSync(cachePath).filter(f => statSync(path.join(cachePath, f)).isDirectory());
121
+ console.log(`pi-research: camoufox ready (${versions.join(', ') || 'installed'})`);
122
+ } catch (e) {
123
+ console.log(`pi-research: camoufox ready (path check error: ${e instanceof Error ? e.message : String(e)})`);
124
+ }
125
+ } else if (browsersInstalled) {
126
+ console.warn(`pi-research: camoufox binary not found at expected path ${cachePath}`);
127
+ }
128
+
129
+ // On Linux without a display server the browser runs true-headless (renders
130
+ // offscreen, no Xvfb required) — nothing to install. Xvfb is only needed if you
131
+ // opt into the virtual-framebuffer mode with PI_RESEARCH_USE_XVFB=true.
132
+ if (isLinux && !process.env.DISPLAY && !process.env.WAYLAND_DISPLAY) {
133
+ console.log('pi-research: No display server detected — the browser will run headless (no Xvfb needed). To opt into Xvfb virtual-display mode, set PI_RESEARCH_USE_XVFB=true and install it: sudo apt install xvfb');
134
+ }
@@ -0,0 +1,90 @@
1
+ ## research — an Agent Skill for pi-research
2
+
3
+ A portable [Agent Skill](https://agentskills.io/specification) that gives any
4
+ coding agent multi-agent web research by driving the
5
+ [pi-research](https://github.com/Lincoln504/pi-research) engine. It is the same
6
+ engine the `pi` extension uses, packaged as a skill so Claude, Cursor,
7
+ Codex, OpenClaw, and other Agent-Skills-compatible tools can use it.
8
+
9
+ ### What it exposes
10
+
11
+ Two capabilities, invoked as shell subcommands:
12
+
13
+ - `research "<query>"` — live multi-agent web research (search + stealth
14
+ scraping + security databases + Stack Exchange). Returns a cited Markdown report.
15
+ - `knowledge "<query>"` — search the local knowledge store of previously researched
16
+ findings before doing live research (instant, free; the store is on by
17
+ default in `global` mode and can be scoped or disabled via `knowledge-config`).
18
+
19
+ Plus `knowledge-config [set <none|project|global>]` to show or set the per-directory
20
+ knowledge-store mode, and `status` to inspect detection/config without running research.
21
+
22
+ ### How it works
23
+
24
+ ```
25
+ agent ──Bash──▶ scripts/run.mjs ──spawns──▶ pi-research engine (dist/cli.mjs)
26
+ (finds the engine, (uses the SDK: init → run → shutdown)
27
+ clean errors if missing) │
28
+ ▼
29
+ Markdown report → stdout
30
+ ```
31
+
32
+ The launcher (`scripts/run.mjs`) is zero-dependency and locates the engine
33
+ wherever it's installed (`PI_RESEARCH_BIN` / `PI_RESEARCH_PATH` / PATH /
34
+ `node_modules` / `~/.pi/bin`).
35
+ The engine wraps the [pi-research SDK](https://www.npmjs.com/package/@lincoln504/pi-research)
36
+ and fails fast with actionable messages + config locations when the package or a
37
+ model/key is missing.
38
+
39
+ ### Prerequisites
40
+
41
+ - Node.js >= 22.19.0
42
+ - `pi-research` installed somewhere the launcher can find it, and a model +
43
+ API key configured. See [`references/configuration.md`](references/configuration.md).
44
+
45
+ ```bash
46
+ npm install -g @lincoln504/pi-research
47
+ node "<skill_dir>/scripts/run.mjs" status # verify detection
48
+ ```
49
+
50
+ ### Install the skill
51
+
52
+ Recommended. From the pi extension, run `/research-config` and choose
53
+ Install in External Agents. It detects Claude, Codex, and OpenClaw and
54
+ symlinks this skill into each one that is installed. Remove from External Agents
55
+ removes those symlinks (cleanup also runs automatically on `npm uninstall`).
56
+
57
+ Cursor is not auto-installed — it has no global skills directory and only reads
58
+ project-level `.cursor/skills/`. Symlink it per-project instead (see the table).
59
+
60
+ Manual. Or symlink this `pi-research/` directory into your agent's skills folder:
61
+
62
+ | Agent | Location |
63
+ |-------|----------|
64
+ | Claude (personal) | `~/.claude/skills/pi-research/` |
65
+ | Claude (project) | `<project>/.claude/skills/pi-research/` |
66
+ | Codex CLI (personal) | `~/.codex/skills/pi-research/` |
67
+ | Codex CLI (project) | `<project>/.codex/skills/pi-research/` |
68
+ | Cursor (project-only) | `<project>/.cursor/skills/pi-research/` |
69
+ | OpenClaw (personal) | `~/.openclaw/skills/pi-research/` |
70
+
71
+ Then ask your agent to pi-research something — its skill system matches the
72
+ `description` in `SKILL.md` and activates this skill automatically.
73
+
74
+ ### Usage from an agent
75
+
76
+ ```bash
77
+ node "<skill_dir>/scripts/run.mjs" research "solid-state battery progress 2026" --depth 2
78
+ node "<skill_dir>/scripts/run.mjs" knowledge "openai gpt-5 release date"
79
+ node "<skill_dir>/scripts/run.mjs" status
80
+ ```
81
+
82
+ ### Files
83
+
84
+ - `SKILL.md` — the skill definition + system prompt (what agents read).
85
+ - `scripts/run.mjs` — the launcher (compiled from `run.ts`).
86
+ - `references/configuration.md` — model/key setup and the full variable reference.
87
+
88
+ ### License
89
+
90
+ MIT.
@@ -0,0 +1,102 @@
1
+ ---
2
+ name: pi-research
3
+ description: "Multi-agent web research for any coding agent. Use for anything requiring current information from the internet — news, trends, product/service/company facts, statistics, people, places, events, APIs, library behavior, or 'what is X / how does X work' questions. Also use to search a local knowledge store of previously researched findings before doing live research. Powered by the pi-research engine (search, stealth scraping, security databases, Stack Exchange). Not for reading local files, running project commands, or analyzing this repository's own source."
4
+ license: MIT
5
+ metadata: { "author": "Lincoln Deen", "version": "1.0.1", "package": "@lincoln504/pi-research", "engine": "pi-research-sdk", "openclaw": { "requires": { "bins": ["node"] } } }
6
+ allowed-tools: Bash(node:*)
7
+ ---
8
+
9
+ # pi-research skill
10
+
11
+ Run via your shell tool — the **Bash** tool in Claude Code, the **`exec`** tool in
12
+ OpenClaw. `<SKILL_DIR>` is this skill's own directory: substitute the absolute path
13
+ your harness reports for this skill (in OpenClaw, `{baseDir}` resolves to it).
14
+
15
+ ```
16
+ node "<SKILL_DIR>/scripts/run.mjs" research "<query>" --depth 1
17
+ node "<SKILL_DIR>/scripts/run.mjs" knowledge "<query>" ["<q2>" ...]
18
+ node "<SKILL_DIR>/scripts/run.mjs" status [--json]
19
+ ```
20
+
21
+ - **stdout** = Markdown report — read and cite it.
22
+ - **stderr** = progress + errors — relay errors, suppress routine progress.
23
+ - **Always set a generous command timeout** so a stalled run can't block forever, but
24
+ leave plenty of headroom — a real run should finish well inside it:
25
+ `knowledge` 600000ms · `research --depth 1` 900000ms · `--depth 2` 1200000ms · `--depth 3` 1800000ms.
26
+ - **Background**: append `&` and keep the PID to work while a run completes; otherwise it runs foreground (blocks). Timeouts apply either way.
27
+
28
+ ## Workflow
29
+
30
+ 1. **Knowledge store first (always).** Run one `knowledge` check with the same
31
+ intent before any live research — it's instant and free.
32
+ - Complete answer → use it, skip live research.
33
+ - Partial → note it, fill gaps with `research`.
34
+ - Exit 78 → store is off in this directory; proceed with `research`. Don't mention it
35
+ unless the user asks — then see *Knowledge store scope* below to turn it on.
36
+ 2. **Live research — always depth `1`.** It's the default and correct for
37
+ effectively every request; depth 1 already decomposes and parallelises
38
+ internally, so it covers big/broad/complex/important topics too. Do NOT raise
39
+ depth on your own judgment.
40
+ - **Depth `2` — super rare:** only a genuinely ultra-broad or unusually detailed
41
+ topic where the user signalled they want deeper/thorough work. Unsure → depth 1.
42
+ - **Depth `3` — never** unless the user explicitly asks for ludicrously deep /
43
+ exhaustive research ("ultra", "exhaustive", "comprehensive deep-dive", "maximum").
44
+
45
+ ## Reporting back to the user (always)
46
+
47
+ - **Cite the report's sources.** Carry its URLs/`[n]` references into your answer;
48
+ attribute each non-trivial claim. Never present researched facts as unsourced;
49
+ never invent or substitute sources.
50
+ - **Surface the saved file.** If stderr/stdout ends with `Research report saved to: <path>`,
51
+ give the user that exact path. If absent, no file was written — say nothing about one.
52
+
53
+ ## Exit codes
54
+
55
+ | Exit | Meaning | Action |
56
+ |------|---------|--------|
57
+ | `0` | Success | Read stdout; cite sources; relay any "saved to: <path>" line. |
58
+ | `78` on `knowledge` | Store disabled/unconfigured | Silent; proceed to `research`. |
59
+ | `78` on `research` | Engine missing or no model/key | **Stop. Relay full stderr** (it shows where to configure). Don't retry until fixed. |
60
+ | `70` | Runtime error | Relay stderr; suggest one retry for transient failures (network, rate limit). |
61
+ | `64` | Bad arguments | Fix args; don't surface unless the query itself is malformed. |
62
+
63
+ ## Knowledge store scope (act on the user's request)
64
+
65
+ The knowledge store — what `knowledge` searches — is **on by default** (mode `global`) and
66
+ scoped per directory: `global` (one shared store), `project` (this directory only), or `none`
67
+ (off here). The user manages it by **asking you in chat** — they don't have to run anything themselves.
68
+
69
+ When the user asks you to turn the store off, make it global, or scope it to this project for
70
+ the current directory (or clearly implies a preference), do it on their behalf and tell them
71
+ the new setting. Check the current one first if useful:
72
+
73
+ ```
74
+ node "<SKILL_DIR>/scripts/run.mjs" knowledge-config # current mode + where it's set
75
+ node "<SKILL_DIR>/scripts/run.mjs" knowledge-config set none|project|global
76
+ ```
77
+
78
+ The change applies to the current directory on the next run. Never change it unprompted.
79
+
80
+ ## When NOT to use this
81
+
82
+ WEB / internet research ONLY. Never use it to investigate *this* project (source,
83
+ config, architecture, tests, logs, anything on this machine) — use your own
84
+ Read/Grep/Bash for that. Reach for `pi-research` only when the answer lives on the
85
+ public internet or in the knowledge store.
86
+
87
+ ## Configuration (relay only on exit 78 from `research`)
88
+
89
+ On exit 78 the engine prints exact configure locations to stderr — relay verbatim:
90
+
91
+ ```
92
+ config file: ~/.pi/research/config.env
93
+ env vars: PI_RESEARCH_API_KEY PI_RESEARCH_PROVIDER PI_RESEARCH_MODEL
94
+ pi auth: ~/.pi/agent/auth.json
95
+ pi models: ~/.pi/agent/models.json
96
+ ```
97
+
98
+ Run `status` for the resolved paths on this machine. Full reference:
99
+ [`references/configuration.md`](references/configuration.md).
100
+
101
+ The engine shuts down cleanly on every exit (browser pool, LanceDB, embedding
102
+ model all released) — nothing is left behind between runs.
@@ -0,0 +1,77 @@
1
+ ## Configuration
2
+
3
+ The skill drives the **pi-research** engine, which needs a model + API key.
4
+ Run `node "<SKILL_DIR>/scripts/run.mjs" status` to print what's detected and where
5
+ to configure (no research runs).
6
+
7
+ ### Credentials — resolution order
8
+
9
+ 1. **Env vars** (most portable):
10
+ ```sh
11
+ export PI_RESEARCH_API_KEY=sk-...
12
+ export PI_RESEARCH_PROVIDER=openai # openai, anthropic, google, openrouter, …
13
+ export PI_RESEARCH_MODEL=openai/gpt-4o # provider/model-id
14
+ ```
15
+ 2. **Global config** `~/.pi/research/config.env` (same KEY=VALUE keys; read at startup).
16
+ 3. **pi auth** (if you use `pi`): `~/.pi/agent/auth.json` + `~/.pi/agent/models.json`.
17
+
18
+ Real env vars always win. Optional CLI-only overlay `~/.pi/research/cli.env` layers
19
+ over `config.env` for the CLI only. Precedence:
20
+ `defaults < config.env < cli.env < project registry < real env`.
21
+
22
+ ### Common settings (optional; env or `config.env`)
23
+
24
+ | Variable | Default | Description |
25
+ |----------|---------|-------------|
26
+ | `PI_RESEARCH_MODEL` | session model | Model override for all research sub-tasks. |
27
+ | `PI_RESEARCH_DEFAULT_RESEARCH_DEPTH` | `1` | Default depth (1–3) when `--depth` omitted. |
28
+ | `PI_RESEARCH_MAX_RESEARCHERS` | `3` | Parallel researchers (1–5). |
29
+ | `PI_RESEARCH_TIMEOUT_MS` | `300000` | Per-researcher timeout (180000–1800000). |
30
+ | `PI_RESEARCH_MAX_SCRAPE_BATCHES` | `2` | Scrape batches per researcher (0 = unlimited). |
31
+ | `PI_RESEARCH_YOUTUBE_TRANSCRIPT_MAX_VIDEOS` | `3` | Videos per `youtube_transcript` call (1–5). |
32
+ | `PI_RESEARCH_YOUTUBE_QUERY_EVERY_N` | `5` | Append `youtube` to ~one-in-N queries (1 = every). |
33
+ | `PI_RESEARCH_KNOWLEDGE_STORE_MODE` | `global` | Knowledge DB: `none`, `project`, `global`. |
34
+ | `PI_RESEARCH_EMBEDDING_DEVICE` | `auto` | Embedding backend: `auto`, `webgpu`, or `cpu` (`auto` probes the GPU and falls back to CPU; raw `webgpu` is forced and can hard-crash on a software GPU). |
35
+ | `PI_RESEARCH_REPORT_EXPORT_ENABLED` | `false` | Write report to `.md` and print its path. |
36
+ | `STACKEXCHANGE_API_KEY` | — | Raises Stack Exchange rate limits (optional). |
37
+ | `GITHUB_TOKEN` | — | Raises GitHub Advisory limit (optional). |
38
+ | `NVD_API_KEY` | — | Raises NVD limit (optional). |
39
+
40
+ `knowledge` needs a store mode other than `none`. With
41
+ `PI_RESEARCH_REPORT_EXPORT_ENABLED=true`, the report ends with
42
+ `Research report saved to: <path>` — surface that path.
43
+
44
+ ### Knowledge-store scope (per-directory)
45
+
46
+ `PI_RESEARCH_KNOWLEDGE_STORE_MODE` is **per-directory** (project-scoped): `global` (one
47
+ shared store — the default), `project` (scoped to the current directory), or `none`
48
+ (disabled here). The user changes it by asking you in chat; when they do, run it for them:
49
+
50
+ ```sh
51
+ node "<SKILL_DIR>/scripts/run.mjs" knowledge-config # current mode + its source
52
+ node "<SKILL_DIR>/scripts/run.mjs" knowledge-config set none|project|global
53
+ ```
54
+
55
+ `set` persists to the per-directory project registry (`~/.pi/research/state/
56
+ project-settings.json`, keyed by the directory) and applies on the next run. It overrides a
57
+ machine-wide `config.env` default; a real `PI_RESEARCH_KNOWLEDGE_STORE_MODE` env var still
58
+ outranks both (the command says so when that happens).
59
+
60
+ ### Exit codes
61
+
62
+ `0` success · `64` bad arguments · `78` not configured (engine missing or no
63
+ model/key; message lists the fix) · `70` runtime error (network/provider/internal).
64
+
65
+ ### Troubleshooting
66
+
67
+ - **"engine not found" (78 from launcher):** not installed —
68
+ `npm i -g @lincoln504/pi-research`, or `pi install npm:@lincoln504/pi-research`,
69
+ or `export PI_RESEARCH_PATH=/path/to/pi-research` (package dir), or
70
+ `export PI_RESEARCH_BIN=/path/to/pi-research/dist/cli.mjs` (exact CLI entry;
71
+ checked first — also the escape hatch for unusual Windows shim layouts).
72
+ - **"No model or API key" (78 from engine):** set the three env vars (or config /
73
+ pi auth), then re-run `status`.
74
+ - **Rate limit (70):** provider returned 429 — wait and retry.
75
+ - **GPU/embedding errors:** set `PI_RESEARCH_EMBEDDING_DEVICE=cpu` (headless/no-GPU).
76
+
77
+ Full variable list + precedence model: `docs/CONFIGURATION.md` in the pi-research repo.
@@ -0,0 +1,237 @@
1
+ #!/usr/bin/env node
2
+
3
+ // skills/pi-research/scripts/run.ts
4
+ import { spawn } from "node:child_process";
5
+ import { existsSync, readFileSync } from "node:fs";
6
+ import { homedir } from "node:os";
7
+ import { dirname, join, resolve } from "node:path";
8
+ import { createRequire } from "node:module";
9
+ import { fileURLToPath } from "node:url";
10
+ var PKG = "@lincoln504/pi-research";
11
+ var REQUIRED_DEP = "@earendil-works/pi-coding-agent";
12
+ var EXIT = { OK: 0, USAGE: 64, CONFIG: 78, SOFTWARE: 70 };
13
+ var argv = process.argv.slice(2);
14
+ var subcommand = argv[0];
15
+ if (!subcommand || subcommand === "-h" || subcommand === "--help" || subcommand === "help") {
16
+ printUsage();
17
+ process.exit(EXIT.OK);
18
+ }
19
+ var require2 = createRequire(import.meta.url);
20
+ function resolvePackageDir(specifier, roots) {
21
+ const candidates = [
22
+ ...process.cwd() !== roots[0] ? [process.cwd()] : [],
23
+ ...roots
24
+ ];
25
+ for (const root of candidates) {
26
+ try {
27
+ const pkgJson = require2.resolve(`${specifier}/package.json`, { paths: [root] });
28
+ return dirname(pkgJson);
29
+ } catch {
30
+ }
31
+ }
32
+ try {
33
+ const pkgJson = require2.resolve(`${specifier}/package.json`);
34
+ return dirname(pkgJson);
35
+ } catch {
36
+ return null;
37
+ }
38
+ }
39
+ function findOnPath(bin) {
40
+ const pathVar = process.env["PATH"] ?? "";
41
+ const exts = process.platform === "win32" ? (process.env["PATHEXT"] ?? ".EXE;.CMD").split(";") : [""];
42
+ for (const dir of pathVar.split(process.platform === "win32" ? ";" : ":")) {
43
+ if (!dir) continue;
44
+ for (const ext of exts) {
45
+ const candidate = join(dir, bin + ext);
46
+ try {
47
+ if (existsSync(candidate)) return candidate;
48
+ } catch {
49
+ }
50
+ }
51
+ }
52
+ return null;
53
+ }
54
+ function resolveEngine(skillDir) {
55
+ const home = homedir();
56
+ if (process.env["PI_RESEARCH_BIN"] && existsSync(process.env["PI_RESEARCH_BIN"])) {
57
+ return { argv: [process.env["PI_RESEARCH_BIN"]], label: process.env["PI_RESEARCH_BIN"] };
58
+ }
59
+ const explicitPath = process.env["PI_RESEARCH_PATH"];
60
+ if (explicitPath) {
61
+ const fromDir = engineFromPackageDir(explicitPath);
62
+ if (fromDir) {
63
+ if (!depResolvableFrom(explicitPath)) depMissing(explicitPath);
64
+ return fromDir;
65
+ }
66
+ }
67
+ const onPath = findOnPath("pi-research");
68
+ if (onPath) return { argv: [onPath], label: onPath };
69
+ const pkgDir = resolvePackageDir(PKG, [skillDir, process.cwd(), home, join(home, ".pi")]);
70
+ if (pkgDir) {
71
+ const fromPkg = engineFromPackageDir(pkgDir);
72
+ if (fromPkg) {
73
+ if (!depResolvableFrom(pkgDir)) depMissing(pkgDir);
74
+ return fromPkg;
75
+ }
76
+ }
77
+ const piBin = join(home, ".pi", "bin", "pi-research");
78
+ if (existsSync(piBin)) return { argv: [piBin], label: piBin };
79
+ return null;
80
+ }
81
+ function engineFromPackageDir(pkgDir) {
82
+ try {
83
+ const pkg = JSON.parse(readFileSync(join(pkgDir, "package.json"), "utf-8"));
84
+ const binField = typeof pkg?.bin === "string" ? pkg.bin : pkg?.bin?.["pi-research"];
85
+ if (binField) {
86
+ const binPath = join(pkgDir, binField);
87
+ if (existsSync(binPath)) {
88
+ const isJsModule = /\.(m|c)?js$/i.test(binField);
89
+ return isJsModule ? { argv: [process.execPath, binPath], label: binPath } : { argv: [binPath], label: binPath };
90
+ }
91
+ }
92
+ } catch {
93
+ }
94
+ const cliPath = join(pkgDir, "dist", "cli.mjs");
95
+ if (existsSync(cliPath)) {
96
+ return { argv: [process.execPath, cliPath], label: cliPath };
97
+ }
98
+ return null;
99
+ }
100
+ function depResolvableFrom(anchorDir) {
101
+ const depSegs = REQUIRED_DEP.split("/");
102
+ let dir = resolve(anchorDir);
103
+ for (; ; ) {
104
+ if (existsSync(join(dir, "node_modules", ...depSegs, "package.json"))) return true;
105
+ const parent = dirname(dir);
106
+ if (parent === dir) return false;
107
+ dir = parent;
108
+ }
109
+ }
110
+ function depMissing(pkgDir) {
111
+ const lines = [
112
+ "",
113
+ `Error: pi-research engine found, but its required runtime dependency`,
114
+ `'${REQUIRED_DEP}' is not installed alongside it.`,
115
+ "",
116
+ "The standalone engine imports this package at startup, so it cannot run",
117
+ "without it. Reinstall so all dependencies are included:",
118
+ "",
119
+ " npm install -g @lincoln504/pi-research # global \u2014 reinstalls the full dependency tree",
120
+ ` # or, in the package dir: cd "${pkgDir}" && npm install ${REQUIRED_DEP}`,
121
+ "",
122
+ `(engine located at: ${pkgDir})`,
123
+ ""
124
+ ];
125
+ process.stderr.write(lines.join("\n"));
126
+ process.exit(EXIT.CONFIG);
127
+ }
128
+ function notInstalled() {
129
+ const home = homedir();
130
+ const lines = [
131
+ "",
132
+ "Error: pi-research engine not found.",
133
+ "",
134
+ "This research skill drives the pi-research engine, but it is not installed in",
135
+ "any of the locations this launcher checks (PATH, node_modules, ~/.pi/bin,",
136
+ "PI_RESEARCH_PATH). Install it, then re-run:",
137
+ "",
138
+ " npm install -g @lincoln504/pi-research # global (exposes the `pi-research` bin)",
139
+ " # or, with pi: pi install npm:@lincoln504/pi-research",
140
+ " # or point at a copy: export PI_RESEARCH_PATH=/path/to/pi-research",
141
+ "",
142
+ "After installing, configure a model + API key. Locations:",
143
+ ` \u2022 env vars: PI_RESEARCH_API_KEY / PI_RESEARCH_PROVIDER / PI_RESEARCH_MODEL`,
144
+ ` \u2022 global config file: ${join(home, ".pi", "research", "config.env")}`,
145
+ ` \u2022 pi auth storage: ${join(home, ".pi", "agent", "auth.json")}`,
146
+ "",
147
+ "Run `status` once installed to verify detection.",
148
+ ""
149
+ ];
150
+ process.stderr.write(lines.join("\n"));
151
+ process.exit(EXIT.CONFIG);
152
+ }
153
+ function hasCmdMetachar(s) {
154
+ return /[&|<>^"%()!\r\n]/.test(s);
155
+ }
156
+ function resolveWinShimToNode(shimPath) {
157
+ const fromLayout = engineFromPackageDir(join(dirname(shimPath), "node_modules", ...PKG.split("/")));
158
+ if (fromLayout && fromLayout.argv[0] === process.execPath) return fromLayout;
159
+ try {
160
+ const content = readFileSync(shimPath, "utf-8");
161
+ const m = content.match(/node_modules[\\/][^"\r\n]*?\.(?:mjs|cjs|js)\b/i);
162
+ if (m) {
163
+ const abs = resolve(dirname(shimPath), m[0]);
164
+ if (existsSync(abs)) return { argv: [process.execPath, abs], label: abs };
165
+ }
166
+ } catch {
167
+ }
168
+ return null;
169
+ }
170
+ function launch(engine2) {
171
+ let target = engine2.argv[0];
172
+ let runArgv = engine2.argv.slice(1).concat(argv);
173
+ let useShell = false;
174
+ if (process.platform === "win32" && /\.(cmd|bat)$/i.test(target)) {
175
+ const direct = resolveWinShimToNode(target);
176
+ if (direct) {
177
+ target = direct.argv[0];
178
+ runArgv = direct.argv.slice(1).concat(argv);
179
+ } else if (argv.some(hasCmdMetachar)) {
180
+ process.stderr.write(
181
+ `
182
+ Error: cannot safely pass this query to the Windows pi-research.cmd shim \u2014 it contains shell metacharacters (& | < > ^ " % ( ) !).
183
+ Set PI_RESEARCH_BIN to the engine's node CLI (\u2026\\dist\\cli.mjs) or PI_RESEARCH_PATH to its package dir, then re-run.
184
+ `
185
+ );
186
+ process.exit(EXIT.CONFIG);
187
+ } else {
188
+ useShell = true;
189
+ }
190
+ }
191
+ const child = spawn(
192
+ useShell ? `"${target}"` : target,
193
+ useShell ? runArgv.map((a) => `"${a}"`) : runArgv,
194
+ {
195
+ stdio: "inherit",
196
+ env: process.env,
197
+ windowsHide: true,
198
+ ...useShell ? { shell: true } : {}
199
+ }
200
+ );
201
+ child.on("error", (err) => {
202
+ process.stderr.write(`
203
+ Error: failed to launch pi-research (${engine2.label}): ${err.message}
204
+ `);
205
+ process.exit(EXIT.SOFTWARE);
206
+ });
207
+ child.on("exit", (code, signal) => {
208
+ if (signal) {
209
+ process.stderr.write(`
210
+ Error: pi-research killed by ${signal}
211
+ `);
212
+ process.exit(EXIT.SOFTWARE);
213
+ }
214
+ process.exit(code ?? EXIT.SOFTWARE);
215
+ });
216
+ }
217
+ function printUsage() {
218
+ process.stdout.write(
219
+ [
220
+ "research skill \u2014 pi-research launcher",
221
+ "",
222
+ "USAGE",
223
+ ' node run.mjs research "<query>" [--depth <0|1|2|3>] [--model provider/id]',
224
+ ' node run.mjs knowledge "<query>" ["<q2>" ...]',
225
+ " node run.mjs knowledge-config [set <none|project|global>]",
226
+ " node run.mjs status [--json]",
227
+ "",
228
+ "This locates the installed pi-research engine and forwards the subcommand to it.",
229
+ "If the engine is missing, it prints install/config instructions and exits 78.",
230
+ ""
231
+ ].join("\n")
232
+ );
233
+ }
234
+ var here = dirname(fileURLToPath(import.meta.url));
235
+ var engine = resolveEngine(here);
236
+ if (!engine) notInstalled();
237
+ launch(engine);