@lincoln504/pi-research 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/cli.mjs +1404 -600
- package/dist/thread-worker.mjs +40 -11
- package/docs/AGENT-SKILL.md +11 -4
- package/docs/ARCHITECTURE.md +19 -9
- package/docs/CHANGELOG.md +107 -1
- package/docs/CONFIGURATION.md +10 -8
- package/docs/KNOWLEDGE-STORE.md +5 -4
- package/docs/PI-EXTENSION.md +1 -1
- package/docs/SDK.md +63 -5
- package/docs/THIRD-PARTY-NOTICES.md +1 -1
- package/package.json +5 -4
- package/scripts/build.cjs +23 -5
- package/scripts/cleanup.cjs +50 -10
- package/scripts/prepare.cjs +15 -5
- package/scripts/setup.cjs +24 -4
- package/skills/pi-research/README.md +9 -2
- package/skills/pi-research/SKILL.md +8 -5
- package/skills/pi-research/references/configuration.md +7 -3
- package/skills/pi-research/scripts/run.mjs +24 -2
- package/src/cli.ts +161 -21
- package/src/config.ts +56 -15
- package/src/constants.ts +31 -0
- package/src/core/interfaces/knowledge-interfaces.ts +6 -1
- package/src/core/interfaces/state-manager-interfaces.ts +1 -1
- package/src/core/llm/model-registry-factory.ts +40 -3
- package/src/core/llm/research-model-resolver.ts +2 -2
- package/src/core/pi-version.ts +136 -0
- package/src/core/service-registry.ts +117 -8
- package/src/index.ts +11 -18
- package/src/infrastructure/browser/browser-client.ts +16 -4
- package/src/infrastructure/browser/browser-error-utils.ts +12 -1
- package/src/infrastructure/browser/browser-server.ts +17 -5
- package/src/infrastructure/browser/browser-task-scheduler.ts +101 -23
- package/src/infrastructure/browser/cleanup-utils.ts +45 -7
- package/src/infrastructure/browser/client-agent.ts +4 -3
- package/src/infrastructure/browser/priority-task-queue.ts +10 -3
- package/src/infrastructure/browser/scheduler-factory.ts +26 -1
- package/src/infrastructure/browser/task-execution-service.ts +92 -28
- package/src/infrastructure/browser/thread-worker-lifecycle.ts +50 -13
- package/src/infrastructure/browser/thread-worker.mjs +40 -11
- package/src/infrastructure/browser/worker-pool-manager.ts +11 -3
- package/src/infrastructure/embedding/embedding-client.ts +22 -8
- package/src/infrastructure/embedding/embedding-factory.ts +28 -11
- package/src/infrastructure/embedding/embedding-server.ts +65 -4
- package/src/infrastructure/file-lock-service.ts +223 -26
- package/src/infrastructure/process-lifecycle-service.ts +108 -31
- package/src/infrastructure/state/state-backup-manager.ts +5 -7
- package/src/infrastructure/state/state-manager-service.ts +1 -1
- package/src/infrastructure/state/state-manager.ts +19 -20
- package/src/infrastructure/types/state-types.ts +9 -0
- package/src/knowledge/embedder-init.ts +1 -1
- package/src/knowledge/embedder.ts +52 -3
- package/src/knowledge/index.ts +1 -1
- package/src/knowledge/store.ts +35 -9
- package/src/knowledge/webgpu-viability.ts +9 -4
- package/src/knowledge/writer-queue.ts +88 -46
- package/src/orchestration/deep-research-orchestrator.ts +13 -2
- package/src/orchestration/quick-research-orchestrator.ts +2 -2
- package/src/orchestration/research-orchestration-service.ts +2 -4
- package/src/orchestration/research-synthesis-service.ts +34 -92
- package/src/security/cisa-kev.ts +10 -3
- package/src/security/github-advisories.ts +18 -7
- package/src/security/nvd-client.ts +17 -6
- package/src/security/osv-client.ts +14 -4
- package/src/skill-install/skill-installer.ts +135 -9
- package/src/stackexchange/rest-client.ts +6 -2
- package/src/tools/research-knowledge-search.ts +23 -2
- package/src/tools/search.ts +6 -1
- package/src/utils/atomic-replace.ts +101 -0
- package/src/utils/circuit-breaker.ts +20 -0
- package/src/utils/citation-utils.ts +106 -8
- package/src/utils/http-body.ts +176 -0
- package/src/utils/llm-usage.ts +37 -0
- package/src/utils/metrics-summary.ts +25 -2
- package/src/utils/research-export.ts +9 -2
- package/src/web-research/browser-search.ts +50 -4
- package/src/web-research/retry-utils.ts +13 -1
- package/src/web-research/search.ts +20 -5
- package/src/web-research/types.ts +17 -4
- package/src/web-research/web-scraper.ts +7 -50
- package/src/youtube/transcript-client.ts +5 -1
package/scripts/build.cjs
CHANGED
|
@@ -106,13 +106,31 @@ const TARGETS = {
|
|
|
106
106
|
copyWebGpuProbe();
|
|
107
107
|
copyPrompts();
|
|
108
108
|
},
|
|
109
|
-
skill: () =>
|
|
110
|
-
|
|
109
|
+
skill: () => {
|
|
110
|
+
const entry = p('skills', 'pi-research', 'scripts', 'run.ts');
|
|
111
|
+
const outfile = p('skills', 'pi-research', 'scripts', 'run.mjs');
|
|
112
|
+
// The published package ships the BUILT run.mjs and deliberately excludes its
|
|
113
|
+
// run.ts source (package.json "files": "!skills/pi-research/scripts/run.ts"),
|
|
114
|
+
// yet it also ships prepare.cjs + build.cjs — and `prepare` runs `build.cjs all`.
|
|
115
|
+
// On any install that executes prepare against the shipped tree (npm install
|
|
116
|
+
// <dir>, file:, npm link, pi install <dir>) this target had no entry point and
|
|
117
|
+
// aborted the whole install with "Could not resolve run.ts". Registry .tgz and
|
|
118
|
+
// npx installs were unaffected only because prepare does not run there.
|
|
119
|
+
// Nothing to do when the artifact is already present and its source is not.
|
|
120
|
+
if (!fs.existsSync(entry)) {
|
|
121
|
+
if (fs.existsSync(outfile)) {
|
|
122
|
+
console.log('[build] skill: run.ts absent and run.mjs already built (published tree) — skipping');
|
|
123
|
+
return Promise.resolve();
|
|
124
|
+
}
|
|
125
|
+
throw new Error(`[build] skill: neither ${entry} nor a prebuilt ${outfile} exists`);
|
|
126
|
+
}
|
|
127
|
+
return esbuild.build({
|
|
111
128
|
...COMMON,
|
|
112
|
-
entryPoints: [
|
|
113
|
-
outfile
|
|
129
|
+
entryPoints: [entry],
|
|
130
|
+
outfile,
|
|
114
131
|
banner: { js: SHEBANG },
|
|
115
|
-
})
|
|
132
|
+
});
|
|
133
|
+
},
|
|
116
134
|
};
|
|
117
135
|
|
|
118
136
|
// The cli target's copyThreadWorker step depends on the worker output existing,
|
package/scripts/cleanup.cjs
CHANGED
|
@@ -1,6 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Uninstall-time cleanup: skill links, cache tree, state tree.
|
|
5
|
+
*
|
|
6
|
+
* IMPORTANT — this does NOT run on `npm uninstall`. npm removed the
|
|
7
|
+
* `preuninstall`/`postuninstall` lifecycle for the package being removed in
|
|
8
|
+
* npm 7; verified against npm 11, where `postinstall` fires on install but
|
|
9
|
+
* `preuninstall` never fires on uninstall. The `preuninstall` entry in
|
|
10
|
+
* package.json is retained for package managers that still honour it, but it
|
|
11
|
+
* must not be treated as the cleanup path.
|
|
12
|
+
*
|
|
13
|
+
* The supported paths are `pi-research skill uninstall` (removes the skill
|
|
14
|
+
* links via the same owned-only manifest logic) and running this script
|
|
15
|
+
* directly: node <package-root>/scripts/cleanup.cjs
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const { spawnSync } = require('child_process');
|
|
4
19
|
const { rmSync, existsSync, lstatSync, readlinkSync, readFileSync, writeFileSync, unlinkSync } = require('fs');
|
|
5
20
|
const os = require('os');
|
|
6
21
|
const path = require('path');
|
|
@@ -52,7 +67,7 @@ function removeInstalledSkills() {
|
|
|
52
67
|
if (!owned) { kept.push(e); continue; }
|
|
53
68
|
try {
|
|
54
69
|
if (lstatSync(p).isSymbolicLink()) unlinkSync(p);
|
|
55
|
-
else rmSync(p, { recursive: true, force: true });
|
|
70
|
+
else rmSync(p, { recursive: true, force: true, maxRetries: 5, retryDelay: 50 });
|
|
56
71
|
console.log(`pi-research: removed skill install at ${p}`);
|
|
57
72
|
} catch (err) {
|
|
58
73
|
kept.push(e);
|
|
@@ -93,8 +108,17 @@ if (process.env.PI_RESEARCH_PURGE_BROWSERS === '1') {
|
|
|
93
108
|
if (process.env.PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD !== '1') {
|
|
94
109
|
try {
|
|
95
110
|
const bin = resolveCamoufoxBin();
|
|
96
|
-
|
|
97
|
-
|
|
111
|
+
// Argv array, not a shell string — see the matching note in setup.cjs: `bin`
|
|
112
|
+
// is derived from the install directory, and a shell string would let a
|
|
113
|
+
// directory name containing `$(…)` execute at uninstall time.
|
|
114
|
+
const isWin = process.platform === 'win32';
|
|
115
|
+
const res = bin
|
|
116
|
+
? (isWin
|
|
117
|
+
? spawnSync(`"${bin}"`, ['remove'], { stdio: 'inherit', shell: true })
|
|
118
|
+
: spawnSync(bin, ['remove'], { stdio: 'inherit' }))
|
|
119
|
+
: spawnSync('npx', ['camoufox-js', 'remove'], { stdio: 'inherit', shell: isWin });
|
|
120
|
+
if (res.error) throw res.error;
|
|
121
|
+
if (res.status !== 0) throw new Error(`camoufox remove exited with code ${res.status}`);
|
|
98
122
|
console.log('pi-research: camoufox browser binaries removed.');
|
|
99
123
|
} catch (error) {
|
|
100
124
|
console.warn(`pi-research: could not remove camoufox binaries: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -109,7 +133,7 @@ if (process.env.PI_RESEARCH_PURGE_BROWSERS === '1') {
|
|
|
109
133
|
const legacyCacheDir = path.join(projectRoot, '.browser');
|
|
110
134
|
if (existsSync(legacyCacheDir)) {
|
|
111
135
|
try {
|
|
112
|
-
rmSync(legacyCacheDir, { recursive: true, force: true });
|
|
136
|
+
rmSync(legacyCacheDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 50 });
|
|
113
137
|
} catch (error) {
|
|
114
138
|
console.warn(`pi-research: could not remove ${legacyCacheDir}: ${error instanceof Error ? error.message : String(error)}`);
|
|
115
139
|
}
|
|
@@ -126,7 +150,7 @@ if (existsSync(legacyCacheDir)) {
|
|
|
126
150
|
function removeOwnedScratchDir(label, dir) {
|
|
127
151
|
if (!dir || !existsSync(dir)) return;
|
|
128
152
|
try {
|
|
129
|
-
rmSync(dir, { recursive: true, force: true });
|
|
153
|
+
rmSync(dir, { recursive: true, force: true, maxRetries: 5, retryDelay: 50 });
|
|
130
154
|
console.log(`pi-research: removed ${label} ${dir}`);
|
|
131
155
|
} catch (error) {
|
|
132
156
|
console.warn(`pi-research: could not remove ${label} ${dir}: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -136,14 +160,30 @@ function removeOwnedScratchDir(label, dir) {
|
|
|
136
160
|
// Cache tree: browser profiles, webgpu-viability probe, downloaded embedding models.
|
|
137
161
|
// Same resolution as config.ts (XDG_CACHE_HOME || ~/.cache, then pi-research) on
|
|
138
162
|
// every platform. Shared camoufox binaries live in ~/.cache/camoufox (handled above).
|
|
139
|
-
|
|
163
|
+
// A RELATIVE XDG_CACHE_HOME is ignored, exactly as embedder-utils.ts does at runtime.
|
|
164
|
+
// Honouring one here would rmSync a cwd-relative `pi-research` tree — deleting whatever
|
|
165
|
+
// happens to sit there — while leaving the real cache untouched. This script's whole job
|
|
166
|
+
// is deletion, so it must not resolve paths more loosely than the code that created them.
|
|
167
|
+
const xdgCache = process.env.XDG_CACHE_HOME;
|
|
168
|
+
const cacheHome = xdgCache && path.isAbsolute(xdgCache) ? xdgCache : path.join(os.homedir(), '.cache');
|
|
140
169
|
removeOwnedScratchDir('cache dir', path.join(cacheHome, 'pi-research'));
|
|
141
170
|
|
|
142
171
|
// State tree: transient run state, locks, backups, project-settings. Now lives in
|
|
143
172
|
// pi-research's own namespace (~/.pi/research/state), honoring the same overrides
|
|
144
|
-
// as getGlobalConfigDir()/PI_RESEARCH_STATE_DIR
|
|
145
|
-
|
|
146
|
-
|
|
173
|
+
// as getGlobalConfigDir()/PI_RESEARCH_STATE_DIR — with the same strictness the
|
|
174
|
+
// XDG guard above applies. A RELATIVE state dir would rmSync a tree relative to
|
|
175
|
+
// wherever the user happens to invoke this script (generally NOT where the
|
|
176
|
+
// runtime created state), and a config-dir name carrying a path separator could
|
|
177
|
+
// traverse out of HOME entirely. Deletion must never resolve paths more loosely
|
|
178
|
+
// than the code that created them.
|
|
179
|
+
const stateOverride = process.env.PI_RESEARCH_STATE_DIR;
|
|
180
|
+
const rawDirName = process.env.PI_RESEARCH_CONFIG_DIR_NAME;
|
|
181
|
+
const dirName = rawDirName && !rawDirName.includes('/') && !rawDirName.includes('\\') && rawDirName !== '.' && rawDirName !== '..'
|
|
182
|
+
? rawDirName
|
|
183
|
+
: '.pi';
|
|
184
|
+
const stateDir = stateOverride && path.isAbsolute(stateOverride)
|
|
185
|
+
? stateOverride
|
|
186
|
+
: path.join(os.homedir(), dirName, 'research', 'state');
|
|
147
187
|
removeOwnedScratchDir('state dir', stateDir);
|
|
148
188
|
|
|
149
189
|
process.exit(0);
|
package/scripts/prepare.cjs
CHANGED
|
@@ -15,14 +15,24 @@
|
|
|
15
15
|
|
|
16
16
|
'use strict';
|
|
17
17
|
|
|
18
|
-
const {
|
|
18
|
+
const { spawnSync } = require('child_process');
|
|
19
19
|
const path = require('path');
|
|
20
20
|
|
|
21
21
|
const ROOT = path.join(__dirname, '..');
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
// spawnSync with an argv array, not execSync with a quoted string: execSync goes
|
|
24
|
+
// through /bin/sh -c, where $(…), backticks and quotes stay live INSIDE double
|
|
25
|
+
// quotes — so a Node binary path under a directory named with shell metacharacters
|
|
26
|
+
// would execute them during npm ci / a git install. Same fix setup.cjs and
|
|
27
|
+
// cleanup.cjs already carry. process.execPath (not a bare 'node') for the same
|
|
28
|
+
// reason as ensure-native-deps.cjs: a PATH lookup can resolve a different
|
|
29
|
+
// interpreter (nvm-windows, Volta, a portable Node) than the one running npm.
|
|
30
|
+
const r = spawnSync(process.execPath, ['scripts/build.cjs', 'all'], { stdio: 'inherit', cwd: ROOT });
|
|
31
|
+
if (r.error) {
|
|
32
|
+
console.error('[prepare] Build failed:', r.error.message);
|
|
27
33
|
process.exit(1);
|
|
28
34
|
}
|
|
35
|
+
if (r.status !== 0) {
|
|
36
|
+
console.error(`[prepare] Build failed with exit code ${r.status}`);
|
|
37
|
+
process.exit(r.status ?? 1);
|
|
38
|
+
}
|
package/scripts/setup.cjs
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* PLAYWRIGHT_INSTALL_DEPS=true - also install Linux system deps (or pass --system-deps)
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
const { execSync } = require('child_process');
|
|
14
|
+
const { execSync, spawnSync } = require('child_process');
|
|
15
15
|
const { existsSync, readdirSync, statSync } = require('fs');
|
|
16
16
|
const { homedir } = require('os');
|
|
17
17
|
const path = require('path');
|
|
@@ -95,18 +95,38 @@ if (process.env.PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD === '1') {
|
|
|
95
95
|
if (!alreadyInstalled) {
|
|
96
96
|
try {
|
|
97
97
|
const bin = resolveCamoufoxBin();
|
|
98
|
-
|
|
98
|
+
// spawnSync with an ARGV ARRAY, never a shell string. `bin` is a filesystem
|
|
99
|
+
// path derived from the install directory, and execSync runs through
|
|
100
|
+
// `/bin/sh -c` where `$(…)`, backticks and quotes stay live INSIDE double
|
|
101
|
+
// quotes — so a package installed under a directory whose name contains
|
|
102
|
+
// shell metacharacters would execute them at install time. Windows still
|
|
103
|
+
// needs a shell to run the `.cmd` shim, and there the path is quoted; the
|
|
104
|
+
// runtime twin in ensure-browser.ts uses the same shape.
|
|
99
105
|
// Bound the ~100MB download so a stalled/interrupted network fails fast
|
|
100
106
|
// instead of hanging `npm install` forever (the catch below exits 0, and the
|
|
101
107
|
// browser is re-fetchable manually). 15 min is ample even on slow links.
|
|
102
|
-
|
|
108
|
+
const spawnOpts = { stdio: 'inherit', env, timeout: 15 * 60 * 1000 };
|
|
109
|
+
const res = bin
|
|
110
|
+
? (isWindows
|
|
111
|
+
? spawnSync(`"${bin}"`, ['fetch'], { ...spawnOpts, shell: true })
|
|
112
|
+
: spawnSync(bin, ['fetch'], spawnOpts))
|
|
113
|
+
: spawnSync('npx', ['camoufox-js', 'fetch'], { ...spawnOpts, shell: isWindows });
|
|
114
|
+
if (res.error) throw res.error;
|
|
115
|
+
if (res.status !== 0) throw new Error(`camoufox fetch exited with code ${res.status}`);
|
|
103
116
|
browsersInstalled = true;
|
|
104
117
|
} catch (error) {
|
|
105
118
|
const msg = error instanceof Error ? error.message : String(error);
|
|
106
119
|
console.error('ERROR: Camoufox browser install failed — pi-research will not work.');
|
|
107
120
|
console.error('Run manually to fix: npx camoufox-js fetch');
|
|
108
121
|
console.error(`Reason: ${msg}`);
|
|
109
|
-
|
|
122
|
+
// Exit 0 by default: the browser is fetched lazily on first use, so a restrictive
|
|
123
|
+
// network must not fail a user's install. But that also meant nothing here could
|
|
124
|
+
// ever turn CI red — a broken Windows launcher yielded a green `npm ci`. CI sets
|
|
125
|
+
// PI_RESEARCH_STRICT_SETUP=1 so the platform-specific spawn paths above are
|
|
126
|
+
// actually enforced somewhere.
|
|
127
|
+
const strict = process.env.PI_RESEARCH_STRICT_SETUP === '1' ||
|
|
128
|
+
process.env.PI_RESEARCH_STRICT_SETUP === 'true';
|
|
129
|
+
process.exit(strict ? 1 : 0);
|
|
110
130
|
}
|
|
111
131
|
} else {
|
|
112
132
|
browsersInstalled = true;
|
|
@@ -52,7 +52,13 @@ node "<skill_dir>/scripts/run.mjs" status # verify detection
|
|
|
52
52
|
Recommended. From the pi extension, run `/research-config` and choose
|
|
53
53
|
Install in External Agents. It detects Claude, Codex, and OpenClaw and
|
|
54
54
|
symlinks this skill into each one that is installed. Remove from External Agents
|
|
55
|
-
removes those symlinks
|
|
55
|
+
removes those symlinks.
|
|
56
|
+
|
|
57
|
+
Uninstalling the npm package does **not** remove them: npm 7 and newer do not run a
|
|
58
|
+
package's own `preuninstall` script (verified against npm 11). Use Remove from
|
|
59
|
+
External Agents first, or run `node scripts/cleanup.cjs` from the package directory,
|
|
60
|
+
otherwise the symlinks, `~/.pi/research/state` and the `~/.cache/pi-research` model
|
|
61
|
+
cache are all left behind.
|
|
56
62
|
|
|
57
63
|
Cursor is not auto-installed — it has no global skills directory and only reads
|
|
58
64
|
project-level `.cursor/skills/`. Symlink it per-project instead (see the table).
|
|
@@ -82,7 +88,8 @@ node "<skill_dir>/scripts/run.mjs" status
|
|
|
82
88
|
### Files
|
|
83
89
|
|
|
84
90
|
- `SKILL.md` — the skill definition + system prompt (what agents read).
|
|
85
|
-
- `scripts/run.mjs` — the launcher (
|
|
91
|
+
- `scripts/run.mjs` — the launcher (built from `run.ts`, which is a repo-only source
|
|
92
|
+
file and is not shipped in the npm package).
|
|
86
93
|
- `references/configuration.md` — model/key setup and the full variable reference.
|
|
87
94
|
|
|
88
95
|
### License
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: pi-research
|
|
3
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
4
|
license: MIT
|
|
5
|
-
metadata: { "author": "Lincoln Deen", "version": "1.
|
|
5
|
+
metadata: { "author": "Lincoln Deen", "version": "1.2.0", "package": "@lincoln504/pi-research", "engine": "pi-research-sdk", "openclaw": { "requires": { "bins": ["node"] } } }
|
|
6
6
|
allowed-tools: Bash(node:*)
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -55,17 +55,20 @@ node "<SKILL_DIR>/scripts/run.mjs" status [--json]
|
|
|
55
55
|
- **Cite the report's sources.** Carry its URLs/`[n]` references into your answer;
|
|
56
56
|
attribute each non-trivial claim. Never present researched facts as unsourced;
|
|
57
57
|
never invent or substitute sources.
|
|
58
|
-
- **Surface the saved file.**
|
|
59
|
-
|
|
58
|
+
- **Surface the saved file.** Look for a saved-report line: stdout's report footer ends
|
|
59
|
+
with `Research report saved to: <path>`, and stderr carries `[pi-research] report saved
|
|
60
|
+
to: <path>`. Give the user that exact path. If neither appears, no file was written —
|
|
61
|
+
say nothing about one.
|
|
60
62
|
|
|
61
63
|
## Exit codes
|
|
62
64
|
|
|
63
65
|
| Exit | Meaning | Action |
|
|
64
66
|
|------|---------|--------|
|
|
65
67
|
| `0` | Success | Read stdout; cite sources; relay any "saved to: <path>" line. |
|
|
66
|
-
| `78` on `knowledge` |
|
|
68
|
+
| `78` on `knowledge` | Two cases — **read stderr to tell them apart.** | Says the store is **disabled**: silent; proceed to `research`. Mentions **credentials/model/API key**: this is the same hard config failure as `78` on `research` — **stop and relay full stderr**; don't silently continue, `research` will fail the same way. |
|
|
67
69
|
| `78` on `research` | Engine missing or no model/key | **Stop. Relay full stderr** (it shows where to configure). Don't retry until fixed. |
|
|
68
70
|
| `75` | Machine at capacity — other research runs are already using every slot | **Nothing is broken.** Wait for an in-flight run to finish, then retry this query once. Don't reconfigure anything and don't retry in a tight loop. |
|
|
71
|
+
| `130` (and `129`/`131`/`143`) | Cancelled — the run was interrupted. These are the POSIX `128 + signal` codes: `130` Ctrl-C/SIGINT, `143` SIGTERM, `129` SIGHUP, `131` SIGQUIT. Treat **any exit ≥ 128 as a cancellation.** | **Nothing is broken and nothing failed.** Say the research was cancelled. **Do NOT re-run it** unless the user asks; they stopped it deliberately. |
|
|
69
72
|
| `70` | Runtime error | Relay stderr; suggest one retry for transient failures (network, rate limit). |
|
|
70
73
|
| `64` | Bad arguments | Fix args; don't surface unless the query itself is malformed. |
|
|
71
74
|
|
|
@@ -81,7 +84,7 @@ the new setting. Check the current one first if useful:
|
|
|
81
84
|
|
|
82
85
|
```
|
|
83
86
|
node "<SKILL_DIR>/scripts/run.mjs" knowledge-config # current mode + where it's set
|
|
84
|
-
node "<SKILL_DIR>/scripts/run.mjs" knowledge-config set none|project|global
|
|
87
|
+
node "<SKILL_DIR>/scripts/run.mjs" knowledge-config set <none|project|global> # pick ONE value
|
|
85
88
|
```
|
|
86
89
|
|
|
87
90
|
The change applies to the current directory on the next run. Never change it unprompted.
|
|
@@ -52,7 +52,7 @@ shared store — the default), `project` (scoped to the current directory), or `
|
|
|
52
52
|
|
|
53
53
|
```sh
|
|
54
54
|
node "<SKILL_DIR>/scripts/run.mjs" knowledge-config # current mode + its source
|
|
55
|
-
node "<SKILL_DIR>/scripts/run.mjs" knowledge-config set none|project|global
|
|
55
|
+
node "<SKILL_DIR>/scripts/run.mjs" knowledge-config set <none|project|global> # pick ONE value
|
|
56
56
|
```
|
|
57
57
|
|
|
58
58
|
`set` persists to the per-directory project registry (`~/.pi/research/state/
|
|
@@ -62,8 +62,12 @@ outranks both (the command says so when that happens).
|
|
|
62
62
|
|
|
63
63
|
### Exit codes
|
|
64
64
|
|
|
65
|
-
`0` success · `64` bad arguments · `
|
|
66
|
-
|
|
65
|
+
`0` success · `64` bad arguments · `70` runtime error (network/provider/internal) ·
|
|
66
|
+
`75` machine at capacity — every concurrent-run slot is taken; wait and retry once ·
|
|
67
|
+
`78` not configured (engine missing or no model/key; message lists the fix).
|
|
68
|
+
|
|
69
|
+
A run stopped by a signal exits `128 + N`: `130` Ctrl-C (SIGINT), `143` SIGTERM,
|
|
70
|
+
`129` SIGHUP, `131` SIGQUIT. See SKILL.md for the full table.
|
|
67
71
|
|
|
68
72
|
### Troubleshooting
|
|
69
73
|
|
|
@@ -3,13 +3,18 @@
|
|
|
3
3
|
// skills/pi-research/scripts/run.ts
|
|
4
4
|
import { spawn } from "node:child_process";
|
|
5
5
|
import { existsSync, readFileSync } from "node:fs";
|
|
6
|
-
import { homedir } from "node:os";
|
|
6
|
+
import { homedir, constants as osConstants } from "node:os";
|
|
7
7
|
import { dirname, join, resolve } from "node:path";
|
|
8
8
|
import { createRequire } from "node:module";
|
|
9
9
|
import { fileURLToPath } from "node:url";
|
|
10
10
|
var PKG = "@lincoln504/pi-research";
|
|
11
11
|
var REQUIRED_DEP = "@earendil-works/pi-coding-agent";
|
|
12
|
-
var EXIT = { OK: 0, USAGE: 64, CONFIG: 78, SOFTWARE: 70 };
|
|
12
|
+
var EXIT = { OK: 0, USAGE: 64, CONFIG: 78, SOFTWARE: 70, CANCELLED: 130 };
|
|
13
|
+
var CANCELLING_SIGNALS = /* @__PURE__ */ new Set(["SIGINT", "SIGTERM", "SIGHUP", "SIGQUIT"]);
|
|
14
|
+
function exitCodeForSignal(sig) {
|
|
15
|
+
const signum = osConstants.signals[sig];
|
|
16
|
+
return typeof signum === "number" ? 128 + signum : EXIT.CANCELLED;
|
|
17
|
+
}
|
|
13
18
|
var argv = process.argv.slice(2);
|
|
14
19
|
var subcommand = argv[0];
|
|
15
20
|
if (!subcommand || subcommand === "-h" || subcommand === "--help" || subcommand === "help") {
|
|
@@ -199,6 +204,17 @@ Set PI_RESEARCH_BIN to the engine's node CLI (\u2026\\dist\\cli.mjs) or PI_RESEA
|
|
|
199
204
|
...useShell ? { shell: true } : {}
|
|
200
205
|
}
|
|
201
206
|
);
|
|
207
|
+
for (const sig of CANCELLING_SIGNALS) {
|
|
208
|
+
try {
|
|
209
|
+
process.on(sig, () => {
|
|
210
|
+
try {
|
|
211
|
+
child.kill(sig);
|
|
212
|
+
} catch {
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
} catch {
|
|
216
|
+
}
|
|
217
|
+
}
|
|
202
218
|
child.on("error", (err) => {
|
|
203
219
|
process.stderr.write(`
|
|
204
220
|
Error: failed to launch pi-research (${engine2.label}): ${err.message}
|
|
@@ -207,6 +223,12 @@ Error: failed to launch pi-research (${engine2.label}): ${err.message}
|
|
|
207
223
|
});
|
|
208
224
|
child.on("exit", (code, signal) => {
|
|
209
225
|
if (signal) {
|
|
226
|
+
if (CANCELLING_SIGNALS.has(signal)) {
|
|
227
|
+
process.stderr.write(`
|
|
228
|
+
pi-research cancelled (${signal}).
|
|
229
|
+
`);
|
|
230
|
+
process.exit(exitCodeForSignal(signal));
|
|
231
|
+
}
|
|
210
232
|
process.stderr.write(`
|
|
211
233
|
Error: pi-research killed by ${signal}
|
|
212
234
|
`);
|