@lincoln504/pi-research 1.7.5 → 1.7.6

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.
@@ -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.7.5", "package": "@lincoln504/pi-research", "engine": "pi-research-sdk", "openclaw": { "requires": { "bins": ["node"] } } }
5
+ metadata: { "author": "Lincoln Deen", "version": "1.7.6", "package": "@lincoln504/pi-research", "engine": "pi-research-sdk", "openclaw": { "requires": { "bins": ["node"] } } }
6
6
  allowed-tools: Bash(node:*)
7
7
  ---
8
8
 
package/dist/cli-bin.mjs CHANGED
@@ -34,24 +34,62 @@
34
34
  * the user reached for.
35
35
  */
36
36
 
37
+ import { existsSync } from 'node:fs';
37
38
  import { fileURLToPath } from 'node:url';
38
39
 
39
40
  const REQUIRED_PEERS = ['@earendil-works/pi-ai', '@earendil-works/pi-coding-agent'];
40
41
 
41
- const missing = REQUIRED_PEERS.filter((name) => {
42
+ /**
43
+ * Probe one peer dependency.
44
+ *
45
+ * 'ok' — resolves, and the file it resolves to exists
46
+ * 'missing' — does not resolve at all (the --legacy-peer-deps case)
47
+ * 'broken' — resolves (its package.json and exports map are readable) but the
48
+ * file those point at is not on disk
49
+ *
50
+ * The 'broken' case is checked with existsSync rather than by importing the
51
+ * module. Resolving is not loading: an interrupted install, a pruned
52
+ * node_modules or a bad package-cache restore leaves a package that resolves
53
+ * perfectly and then fails to load, which would pass this preflight and die
54
+ * inside cli.mjs with the same raw ERR_MODULE_NOT_FOUND this shim exists to
55
+ * replace. Importing it here instead would work, but it runs the host
56
+ * package's module side effects before the CLI proper has decided to start, so
57
+ * the cheap existence check is the right trade.
58
+ *
59
+ * @returns {'ok' | 'missing' | 'broken'}
60
+ */
61
+ function probePeer(name) {
62
+ let resolved;
42
63
  try {
43
- import.meta.resolve(name);
44
- return false;
64
+ resolved = import.meta.resolve(name);
45
65
  } catch {
46
- return true;
66
+ return 'missing';
47
67
  }
48
- });
68
+ try {
69
+ return existsSync(fileURLToPath(resolved)) ? 'ok' : 'broken';
70
+ } catch {
71
+ // Resolved to something fileURLToPath cannot turn into a path (not a file:
72
+ // URL). It resolved, so treat it as present rather than inventing a failure.
73
+ return 'ok';
74
+ }
75
+ }
76
+
77
+ const status = new Map(REQUIRED_PEERS.map((name) => [name, probePeer(name)]));
78
+ const missing = REQUIRED_PEERS.filter((name) => status.get(name) === 'missing');
79
+ const broken = REQUIRED_PEERS.filter((name) => status.get(name) === 'broken');
49
80
 
50
- if (missing.length > 0) {
81
+ if (missing.length > 0 || broken.length > 0) {
82
+ // The remedy names the packages that are ACTUALLY absent. It used to hardcode
83
+ // `npm install -g @earendil-works/pi-coding-agent` with the comment "add just
84
+ // the missing host package", which was wrong whenever pi-ai was the missing
85
+ // one: the instruction named a package the user already had, and following it
86
+ // changed nothing. Reported by review 2026-10-01.
87
+ const needsInstall = [...missing, ...broken];
51
88
  const lines = [
52
89
  '',
53
90
  'Error: pi-research cannot start — it is missing required runtime dependencies:',
54
91
  ...missing.map((name) => ` ${name}`),
92
+ ...broken.map((name) => ` ${name} (installed, but its files are incomplete)`),
55
93
  '',
56
94
  'These are PEER dependencies. pi provides its own copies when it loads this',
57
95
  'package as an extension, and npm >= 7 installs them automatically for a',
@@ -59,10 +97,11 @@ if (missing.length > 0) {
59
97
  'what --legacy-peer-deps, yarn classic, and a pnpm config with',
60
98
  'auto-install-peers=false do.',
61
99
  '',
62
- 'Either of these fixes it:',
100
+ 'Install the missing package(s), or reinstall this package, which resolves',
101
+ 'them as peers:',
63
102
  '',
64
- ' npm install -g @earendil-works/pi-coding-agent # add just the missing host package',
65
- ' npm install -g @lincoln504/pi-research # or reinstall, resolving peers',
103
+ ` npm install -g ${needsInstall.join(' ')}`,
104
+ ' npm install -g @lincoln504/pi-research',
66
105
  '',
67
106
  ];
68
107
  process.stderr.write(lines.join('\n'));
package/docs/CHANGELOG.md CHANGED
@@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.7.6] - 2026-10-01
9
+
10
+ ### Fixed
11
+
12
+ - **The missing-peer remedy names the package that is actually missing.** The 1.7.5 shim's bullet list correctly enumerated every absent `@earendil-works/pi-*` package, but the command underneath it was hardcoded to `npm install -g @earendil-works/pi-coding-agent` with the comment "add just the missing host package". When `pi-ai` (and not `pi-coding-agent`) was the absent one, the instruction named a package the user already had and following it changed nothing. The remedy is now built from the detected set, so both peers are named when both are missing and only the absent one is named otherwise. Caught in review; pinned by a test that asserts the present package is never the one the remedy suggests installing.
13
+ - **The bin preflight detects an install whose files are incomplete, not just an absent package.** It resolved each peer and treated a successful resolve as healthy. Resolving is not loading: an interrupted install, a pruned `node_modules` or a bad package-cache restore leaves `package.json` and its `exports` map intact with the file they point at gone, so the preflight passed it through and the bundle died on that import with the raw `ERR_MODULE_NOT_FOUND` the shim exists to replace. The preflight now also checks that the resolved file exists and reports `installed, but its files are incomplete` with the same exit 78. Checked with `existsSync` rather than by importing the module, which would run the host packages' side effects before the CLI has decided to start. Verified by reverting each half in isolation: without the `argv[1]` rewrite the stub engine exits 97, with a resolve-only preflight a deliberately truncated peer produces `ERR_MODULE_NOT_FOUND`, and the fixed shim produces exit 78.
14
+ - **`dist/cli-bin.mjs` ships executable.** It was published at mode 644 against 755 for `dist/cli.mjs`, because the build copies it with `copyFileSync`, which preserves the committed source mode. Every package manager chmods the declared `bin` target itself, so no install was broken, but the shipped artifact should not depend on that. The build now chmods 755 and the source is committed executable.
15
+
8
16
  ## [1.7.5] - 2026-10-01
9
17
 
10
18
  ### Added
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lincoln504/pi-research",
3
- "version": "1.7.5",
3
+ "version": "1.7.6",
4
4
  "description": "Web research for your agents with smart and safe tooling + knowledge store",
5
5
  "type": "module",
6
6
  "private": false,
package/scripts/build.cjs CHANGED
@@ -57,6 +57,14 @@ function copyWebGpuProbe() {
57
57
  function copyCliBin() {
58
58
  fs.mkdirSync(p('dist'), { recursive: true });
59
59
  fs.copyFileSync(p('src', 'cli-bin.mjs'), p('dist', 'cli-bin.mjs'));
60
+ // copyFileSync preserves the SOURCE mode. The source is committed executable
61
+ // (100755) so this is belt-and-braces, but nothing about the shipped bin should
62
+ // depend on the source file's mode or on the umask of whatever builds it. The
63
+ // bin is what the OS executes, so ship it executable like dist/cli.mjs: every
64
+ // package manager does its own chmod on the declared bin target, but nothing
65
+ // should depend on that. (Flagged in review 2026-10-01: the packed 1.7.5 tarball
66
+ // had dist/cli.mjs at 755 and dist/cli-bin.mjs at 644.)
67
+ fs.chmodSync(p('dist', 'cli-bin.mjs'), 0o755);
60
68
  }
61
69
 
62
70
  // Build the browser worker bundle. Factored out so the cli target can guarantee
package/src/cli-bin.mjs CHANGED
@@ -34,24 +34,62 @@
34
34
  * the user reached for.
35
35
  */
36
36
 
37
+ import { existsSync } from 'node:fs';
37
38
  import { fileURLToPath } from 'node:url';
38
39
 
39
40
  const REQUIRED_PEERS = ['@earendil-works/pi-ai', '@earendil-works/pi-coding-agent'];
40
41
 
41
- const missing = REQUIRED_PEERS.filter((name) => {
42
+ /**
43
+ * Probe one peer dependency.
44
+ *
45
+ * 'ok' — resolves, and the file it resolves to exists
46
+ * 'missing' — does not resolve at all (the --legacy-peer-deps case)
47
+ * 'broken' — resolves (its package.json and exports map are readable) but the
48
+ * file those point at is not on disk
49
+ *
50
+ * The 'broken' case is checked with existsSync rather than by importing the
51
+ * module. Resolving is not loading: an interrupted install, a pruned
52
+ * node_modules or a bad package-cache restore leaves a package that resolves
53
+ * perfectly and then fails to load, which would pass this preflight and die
54
+ * inside cli.mjs with the same raw ERR_MODULE_NOT_FOUND this shim exists to
55
+ * replace. Importing it here instead would work, but it runs the host
56
+ * package's module side effects before the CLI proper has decided to start, so
57
+ * the cheap existence check is the right trade.
58
+ *
59
+ * @returns {'ok' | 'missing' | 'broken'}
60
+ */
61
+ function probePeer(name) {
62
+ let resolved;
42
63
  try {
43
- import.meta.resolve(name);
44
- return false;
64
+ resolved = import.meta.resolve(name);
45
65
  } catch {
46
- return true;
66
+ return 'missing';
47
67
  }
48
- });
68
+ try {
69
+ return existsSync(fileURLToPath(resolved)) ? 'ok' : 'broken';
70
+ } catch {
71
+ // Resolved to something fileURLToPath cannot turn into a path (not a file:
72
+ // URL). It resolved, so treat it as present rather than inventing a failure.
73
+ return 'ok';
74
+ }
75
+ }
76
+
77
+ const status = new Map(REQUIRED_PEERS.map((name) => [name, probePeer(name)]));
78
+ const missing = REQUIRED_PEERS.filter((name) => status.get(name) === 'missing');
79
+ const broken = REQUIRED_PEERS.filter((name) => status.get(name) === 'broken');
49
80
 
50
- if (missing.length > 0) {
81
+ if (missing.length > 0 || broken.length > 0) {
82
+ // The remedy names the packages that are ACTUALLY absent. It used to hardcode
83
+ // `npm install -g @earendil-works/pi-coding-agent` with the comment "add just
84
+ // the missing host package", which was wrong whenever pi-ai was the missing
85
+ // one: the instruction named a package the user already had, and following it
86
+ // changed nothing. Reported by review 2026-10-01.
87
+ const needsInstall = [...missing, ...broken];
51
88
  const lines = [
52
89
  '',
53
90
  'Error: pi-research cannot start — it is missing required runtime dependencies:',
54
91
  ...missing.map((name) => ` ${name}`),
92
+ ...broken.map((name) => ` ${name} (installed, but its files are incomplete)`),
55
93
  '',
56
94
  'These are PEER dependencies. pi provides its own copies when it loads this',
57
95
  'package as an extension, and npm >= 7 installs them automatically for a',
@@ -59,10 +97,11 @@ if (missing.length > 0) {
59
97
  'what --legacy-peer-deps, yarn classic, and a pnpm config with',
60
98
  'auto-install-peers=false do.',
61
99
  '',
62
- 'Either of these fixes it:',
100
+ 'Install the missing package(s), or reinstall this package, which resolves',
101
+ 'them as peers:',
63
102
  '',
64
- ' npm install -g @earendil-works/pi-coding-agent # add just the missing host package',
65
- ' npm install -g @lincoln504/pi-research # or reinstall, resolving peers',
103
+ ` npm install -g ${needsInstall.join(' ')}`,
104
+ ' npm install -g @lincoln504/pi-research',
66
105
  '',
67
106
  ];
68
107
  process.stderr.write(lines.join('\n'));