ruvnet-brain 4.7.20 → 4.7.22

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 (260) hide show
  1. package/README.md +2 -2
  2. package/bin/install.mjs +618 -104
  3. package/console/activity.js +122 -38
  4. package/kb/download-retry.mjs +93 -21
  5. package/kb/forge-guard-injection.mjs +277 -0
  6. package/kb/forge-update.mjs +166 -38
  7. package/kb/lifecycle-evidence-retention.mjs +151 -14
  8. package/kb/model-requirements.mjs +4 -1
  9. package/kb/refresh-run.mjs +175 -27
  10. package/kb/verify-citation.mjs +15 -2
  11. package/package.json +3 -1
  12. package/plugin/.claude-plugin/plugin.json +1 -1
  13. package/plugin/.codex-plugin/plugin.json +1 -1
  14. package/plugin/mcp/managed-cli-interface.mjs +16 -8
  15. package/plugin/scripts/adr-currency-gate.mjs +2 -1
  16. package/plugin/scripts/advisory-contract.mjs +21 -0
  17. package/plugin/scripts/advocacy-route.mjs +5 -4
  18. package/plugin/scripts/agentdb-recall-process.mjs +115 -5
  19. package/plugin/scripts/agentdb-recall-reader.mjs +44 -2
  20. package/plugin/scripts/agentdb-recall.mjs +49 -17
  21. package/plugin/scripts/automatic-update.mjs +9 -1
  22. package/plugin/scripts/brain-confirmation.mjs +2 -1
  23. package/plugin/scripts/brain-footprint.mjs +72 -11
  24. package/plugin/scripts/capability-registry.mjs +1 -1
  25. package/plugin/scripts/capacity-aware-parallel-work.mjs +2 -1
  26. package/plugin/scripts/codex-overnight-notice.mjs +33 -0
  27. package/plugin/scripts/completion-claim-evidence.mjs +241 -23
  28. package/plugin/scripts/continuation-gate.mjs +75 -11
  29. package/plugin/scripts/decision-gate.mjs +13 -63
  30. package/plugin/scripts/decision-outcomes.mjs +36 -0
  31. package/plugin/scripts/decision-recall-gate.mjs +40 -5
  32. package/plugin/scripts/decision-record.mjs +8 -3
  33. package/plugin/scripts/degradation-watch.mjs +2 -1
  34. package/plugin/scripts/developer-update-lock.mjs +172 -33
  35. package/plugin/scripts/developer-update-owner.mjs +8 -5
  36. package/plugin/scripts/developer-update-policy.mjs +4 -1
  37. package/plugin/scripts/developer-update.mjs +155 -53
  38. package/plugin/scripts/doc-currency.mjs +2 -16
  39. package/plugin/scripts/duplicate-gate.mjs +2 -1
  40. package/plugin/scripts/entrypoint.mjs +34 -0
  41. package/plugin/scripts/footprint-io.mjs +135 -8
  42. package/plugin/scripts/grok-hook-adapter.mjs +2 -1
  43. package/plugin/scripts/ground-ruvnet.sh +6 -3
  44. package/plugin/scripts/grounding-answer.mjs +2 -1
  45. package/plugin/scripts/grounding-code-projection.mjs +2 -1
  46. package/plugin/scripts/grounding-substance.mjs +2 -1
  47. package/plugin/scripts/grounding-turn-gate.mjs +2 -2
  48. package/plugin/scripts/grounding-turn-mark.mjs +2 -2
  49. package/plugin/scripts/hook-context-budget.mjs +2 -1
  50. package/plugin/scripts/hook-input.mjs +2 -2
  51. package/plugin/scripts/hook-shim.mjs +9 -2
  52. package/plugin/scripts/host-update.mjs +29 -5
  53. package/plugin/scripts/identifier-preflight.mjs +2 -1
  54. package/plugin/scripts/kb-copy-proof.mjs +57 -9
  55. package/plugin/scripts/learn-capture.mjs +2 -1
  56. package/plugin/scripts/learning-store.mjs +62 -16
  57. package/plugin/scripts/lesson-bridge.mjs +2 -2
  58. package/plugin/scripts/lesson-gate.mjs +7 -39
  59. package/plugin/scripts/lesson-promote.mjs +83 -22
  60. package/plugin/scripts/lesson-scope.mjs +51 -0
  61. package/plugin/scripts/lesson-store.mjs +1 -1
  62. package/plugin/scripts/md-stamp.mjs +2 -1
  63. package/plugin/scripts/native-turn-transcript.mjs +98 -6
  64. package/plugin/scripts/nightly-scheduler.mjs +10 -5
  65. package/plugin/scripts/package-recommender.mjs +2 -2
  66. package/plugin/scripts/project-store-resolver.mjs +19 -0
  67. package/plugin/scripts/routing-outcome-capture.mjs +2 -1
  68. package/plugin/scripts/ruflo-bin.mjs +14 -6
  69. package/plugin/scripts/ruflo-memory-hooks.mjs +157 -19
  70. package/plugin/scripts/session-snapshot-hook.mjs +25 -5
  71. package/plugin/scripts/session-start-core.mjs +7 -16
  72. package/plugin/scripts/session-start-health.mjs +98 -10
  73. package/plugin/scripts/session-start-issue-alert.mjs +63 -0
  74. package/plugin/scripts/session-start-update-plane.mjs +15 -1
  75. package/plugin/scripts/signal-watch.mjs +2 -1
  76. package/plugin/scripts/spend-guard.mjs +2 -1
  77. package/plugin/scripts/substitution-wall.mjs +2 -1
  78. package/plugin/scripts/turn-outcome-capture.mjs +7 -3
  79. package/plugin/scripts/unprompted-runtime.mjs +20 -3
  80. package/plugin/scripts/update-apply.mjs +186 -15
  81. package/plugin/skills/release-proof/scripts/release-proof.mjs +2 -1
  82. package/plugin/skills/ruvnet-brain/PLAYBOOK.md +2 -2
  83. package/plugin/skills/ruvnet-brain/SKILL.md +3 -2
  84. package/scripts/adr-072-completion.mjs +2 -1
  85. package/scripts/agentdb-context.mjs +2 -1
  86. package/scripts/agentdb-fleet-doctor.mjs +30 -8
  87. package/scripts/approved-runtime.mjs +2 -1
  88. package/scripts/architecture-review-lock.mjs +2 -1
  89. package/scripts/ascii-drift.mjs +2 -1
  90. package/scripts/brain-latency-50.mjs +2 -1
  91. package/scripts/brain-novice-50.mjs +2 -16
  92. package/scripts/brain-score.mjs +13 -2
  93. package/scripts/build-bundle.mjs +2 -1
  94. package/scripts/build-concepts.mjs +2 -1
  95. package/scripts/candidate-ci-receipt.mjs +2 -1
  96. package/scripts/candidate-host-evidence.mjs +2 -1
  97. package/scripts/card-from-source.mjs +2 -1
  98. package/scripts/ci/warm-brain-models.mjs +2 -1
  99. package/scripts/claims-verify.mjs +7 -3
  100. package/scripts/claude-controlled-terminal.mjs +2 -1
  101. package/scripts/claude-terminal-mod.mjs +2 -1
  102. package/scripts/code-release-corpus.mjs +2 -1
  103. package/scripts/codex-boot-surface-proof.mjs +208 -0
  104. package/scripts/codex-hook-trust-reconcile.mjs +67 -1
  105. package/scripts/codex-hook-trust.mjs +40 -1
  106. package/scripts/codex-hook-wrapper-history.mjs +2 -1
  107. package/scripts/codex-host-execution-proof.mjs +2 -1
  108. package/scripts/completion-claim-replay.mjs +2 -1
  109. package/scripts/console-engine.mjs +8 -3
  110. package/scripts/corpus-canary.mjs +2 -1
  111. package/scripts/corpus-candidate.mjs +2 -2
  112. package/scripts/corpus-coverage-sidecar.mjs +2 -1
  113. package/scripts/corpus-dispatch-decision.mjs +2 -1
  114. package/scripts/corpus-dispatch-receipt.mjs +2 -1
  115. package/scripts/corpus-next-seed.mjs +2 -1
  116. package/scripts/corpus-qa.mjs +2 -1
  117. package/scripts/corpus-reconcile.mjs +2 -2
  118. package/scripts/corpus-watchdog.mjs +2 -1
  119. package/scripts/count-chunks.mjs +2 -1
  120. package/scripts/customer-state-matrix.mjs +2 -1
  121. package/scripts/derive-passage-content-map.mjs +2 -1
  122. package/scripts/development-push-check.mjs +2 -1
  123. package/scripts/dispatch-receipt.mjs +2 -1
  124. package/scripts/doc-currency.mjs +2 -1
  125. package/scripts/dream-issue-gate.mjs +2 -1
  126. package/scripts/dual-host-deliberation.mjs +2 -16
  127. package/scripts/dual-host-suggest.mjs +2 -16
  128. package/scripts/eval-brain.mjs +7 -3
  129. package/scripts/execution-policy.mjs +2 -1
  130. package/scripts/execution-preflight.mjs +2 -1
  131. package/scripts/falsify.mjs +2 -1
  132. package/scripts/fix-metaharness-memretrieve.mjs +2 -1
  133. package/scripts/fix-workstream.mjs +2 -1
  134. package/scripts/full-hints.mjs +2 -1
  135. package/scripts/full-suite-gate.mjs +2 -1
  136. package/scripts/grok-host.mjs +68 -0
  137. package/scripts/hook-qualify-core.mjs +3 -1
  138. package/scripts/hook-qualify.mjs +2 -1
  139. package/scripts/hook-retirement-check.mjs +2 -1
  140. package/scripts/host-registry.mjs +2 -1
  141. package/scripts/independent-review-receipt.mjs +2 -1
  142. package/scripts/ingest-new-repos.mjs +2 -1
  143. package/scripts/ingest-repo.mjs +12 -2
  144. package/scripts/integration-evidence.mjs +2 -1
  145. package/scripts/issue-fix.mjs +7 -24
  146. package/scripts/issue-watch.mjs +116 -40
  147. package/scripts/job-heartbeat.sh +54 -19
  148. package/scripts/key-canary.mjs +9 -3
  149. package/scripts/latency-to-surface.mjs +2 -1
  150. package/scripts/learning-replay-cli.mjs +97 -19
  151. package/scripts/learning-replay-execution.mjs +2 -0
  152. package/scripts/learning-replay-fixture.mjs +3 -0
  153. package/scripts/learning-replay-proof.mjs +14 -13
  154. package/scripts/learning-replay.mjs +3 -3
  155. package/scripts/learnings.mjs +2 -1
  156. package/scripts/lesson-ratify.mjs +88 -40
  157. package/scripts/loop-checkpoint.mjs +2 -1
  158. package/scripts/metaharness-receipts.mjs +2 -1
  159. package/scripts/model-catalog.mjs +2 -1
  160. package/scripts/model-currency-evidence.mjs +6 -4
  161. package/scripts/model-currency.mjs +33 -3
  162. package/scripts/model-native-catalog.mjs +2 -1
  163. package/scripts/model-router-agent-hook.mjs +2 -1
  164. package/scripts/model-router-dispatch.mjs +2 -1
  165. package/scripts/model-router-engine.mjs +2 -1
  166. package/scripts/model-router-outcome.mjs +2 -1
  167. package/scripts/model-router-setup.mjs +2 -1
  168. package/scripts/model-routing-eval.mjs +2 -1
  169. package/scripts/model-routing-gateway.mjs +2 -1
  170. package/scripts/model-routing-launchers.mjs +4 -1
  171. package/scripts/model-terminal-gateway.mjs +2 -1
  172. package/scripts/model-terminal-launchers.mjs +2 -1
  173. package/scripts/model-weekly-analyst.mjs +2 -1
  174. package/scripts/model-weekly-cycle.mjs +2 -1
  175. package/scripts/model-weekly-qualification.mjs +2 -1
  176. package/scripts/nightly-gists.sh +26 -20
  177. package/scripts/nightly-two-run-proof.mjs +2 -1
  178. package/scripts/nightly-watchdog.mjs +158 -47
  179. package/scripts/no-silent-substitution.mjs +2 -1
  180. package/scripts/notify.sh +11 -6
  181. package/scripts/ntfy-inbox.mjs +41 -18
  182. package/scripts/onboarding-console.mjs +123 -23
  183. package/scripts/oracle/abstain-threshold-sweep.mjs +2 -1
  184. package/scripts/oracle/abstain-trace.mjs +2 -1
  185. package/scripts/oracle/doc2query-generate.mjs +2 -1
  186. package/scripts/oracle/doc2query-reach.mjs +2 -1
  187. package/scripts/oracle/judge-train.mjs +2 -1
  188. package/scripts/oracle/need-set-split.mjs +2 -1
  189. package/scripts/oracle/produce-questions.mjs +2 -16
  190. package/scripts/oracle/repo-recall.mjs +2 -2
  191. package/scripts/oracle/retrieval-accuracy.mjs +2 -2
  192. package/scripts/oracle/sona-query-adapter-eval.mjs +2 -1
  193. package/scripts/oracle/source-tree.mjs +2 -15
  194. package/scripts/oracle/source-units.mjs +2 -16
  195. package/scripts/oracle/spike-run.mjs +2 -16
  196. package/scripts/oracle/validate-labels.mjs +2 -16
  197. package/scripts/org-repo-count.mjs +2 -1
  198. package/scripts/package-cards.mjs +2 -1
  199. package/scripts/prepublication-evidence.mjs +2 -1
  200. package/scripts/private-overlay.mjs +2 -1
  201. package/scripts/product-integrity-contract.mjs +2 -1
  202. package/scripts/protected-release-invocation.mjs +2 -1
  203. package/scripts/public-verification-abandon.mjs +2 -1
  204. package/scripts/public-verification-aggregate.mjs +2 -1
  205. package/scripts/public-verification-finalizer.mjs +2 -1
  206. package/scripts/public-verification-inputs.mjs +2 -1
  207. package/scripts/public-verification-lane.mjs +2 -1
  208. package/scripts/publication-receipt.mjs +2 -1
  209. package/scripts/published-surface-probe.mjs +2 -1
  210. package/scripts/qe/card-lane-gate.mjs +2 -16
  211. package/scripts/qe/session-start-gate.mjs +2 -16
  212. package/scripts/qe/ux-suite.mjs +2 -1
  213. package/scripts/qualified-candidate-check.mjs +2 -1
  214. package/scripts/rebuild-gists-from-receipts.mjs +2 -1
  215. package/scripts/recall-latency-probe.mjs +48 -0
  216. package/scripts/recommendation-eval.mjs +2 -1
  217. package/scripts/recommendation-floor.mjs +2 -1
  218. package/scripts/recommendation-judge-score.mjs +2 -1
  219. package/scripts/recommendation-real-host-score.mjs +2 -1
  220. package/scripts/recommendation-real-host.mjs +2 -1
  221. package/scripts/refresh-capability-only-store.mjs +2 -1
  222. package/scripts/refresh-model-catalog.mjs +2 -1
  223. package/scripts/rehearse-corpus-pipeline.mjs +2 -1
  224. package/scripts/release-authority.mjs +2 -1
  225. package/scripts/release-evidence-aggregate.mjs +2 -1
  226. package/scripts/release-payload.mjs +2 -1
  227. package/scripts/release-projection.mjs +2 -1
  228. package/scripts/release-proof.mjs +2 -1
  229. package/scripts/release-qualification-contract.mjs +82 -28
  230. package/scripts/release-qualification.mjs +8 -1
  231. package/scripts/release-vector.mjs +2 -1
  232. package/scripts/restore-local-ingests.mjs +2 -1
  233. package/scripts/retrieval-canary.mjs +2 -1
  234. package/scripts/route-cheap.mjs +2 -1
  235. package/scripts/route-gold-rank.mjs +2 -1
  236. package/scripts/route-index-memory.mjs +2 -1
  237. package/scripts/route-latency-warm.mjs +2 -1
  238. package/scripts/router-utilization.mjs +2 -1
  239. package/scripts/routing-flywheel.mjs +2 -1
  240. package/scripts/run-operational-benchmark.mjs +2 -1
  241. package/scripts/run-operational-benchmark.v3.mjs +2 -1
  242. package/scripts/rvf-index-audit.mjs +2 -16
  243. package/scripts/seal-gist-receipt.mjs +2 -1
  244. package/scripts/security-guidance-codex-compat.mjs +2 -1
  245. package/scripts/selfcheck.mjs +2 -1
  246. package/scripts/signal-watch.mjs +2 -1
  247. package/scripts/source-coverage.mjs +2 -1
  248. package/scripts/stabilization-receipt.mjs +2 -1
  249. package/scripts/stamp-sweep.mjs +2 -1
  250. package/scripts/status-honesty.mjs +2 -1
  251. package/scripts/sync-commands.mjs +2 -2
  252. package/scripts/top100-benchmark.mjs +2 -1
  253. package/scripts/top100-corpus.mjs +2 -1
  254. package/scripts/user-model-hook-install.mjs +2 -1
  255. package/scripts/user-model-prompt-hook.mjs +2 -1
  256. package/scripts/verify-bundle.mjs +3 -1
  257. package/scripts/verify-model-catalog.mjs +2 -1
  258. package/scripts/version.mjs +2 -1
  259. package/scripts/wired-check.mjs +13 -5
  260. package/scripts/worktree-integrity.mjs +2 -2
@@ -17,6 +17,7 @@ import { fileURLToPath } from 'node:url';
17
17
  import { buildInventory, resolveCommit } from './source-units.mjs';
18
18
  import { produceQuestions, DEFAULT_BATCH } from './produce-questions.mjs';
19
19
  import { loadBgeEmbedder, validateLabels } from './validate-labels.mjs';
20
+ import { isDirectInvocation } from '../../plugin/scripts/entrypoint.mjs';
20
21
 
21
22
  function arg(argv, flag) { const i = argv.indexOf(flag); return i >= 0 ? argv[i + 1] : undefined; }
22
23
  const writeJson = (file, value) => fs.writeFileSync(file, `${JSON.stringify(value, null, 2)}\n`);
@@ -80,19 +81,4 @@ export async function main(argv = process.argv.slice(2)) {
80
81
  return 0;
81
82
  }
82
83
 
83
- // Entry-point guard. Compares REALPATHS on both sides: path.resolve() normalizes a path but does
84
- // NOT follow symlinks, while import.meta.url IS symlink-resolved by Node. Through a symlink (npm bin
85
- // shims, wrapper scripts, and every os.tmpdir() path on macOS) the two sides disagree, so main()
86
- // never runs -- and because nothing throws, the process exits 0. A silent exit 0 is indistinguishable
87
- // from "ran, found nothing", which is how prepareCorpusCandidate once reported SUCCESS with no
88
- // archive on disk. Reproduced live 2026-07-27; pinned by tests/unit/entrypoint-symlink.test.mjs.
89
- function isDirectInvocation() {
90
- try {
91
- if (!process.argv[1]) return false;
92
- return fs.realpathSync(process.argv[1]) === fs.realpathSync(fileURLToPath(import.meta.url));
93
- } catch {
94
- return false;
95
- }
96
- }
97
-
98
- if (isDirectInvocation()) process.exitCode = await main();
84
+ if (isDirectInvocation(import.meta.url)) process.exitCode = await main();
@@ -23,6 +23,7 @@ import fs from 'node:fs';
23
23
  import path from 'node:path';
24
24
  import { fileURLToPath } from 'node:url';
25
25
  import { gitBlobSha, sha256Hex, unitText } from './source-units.mjs';
26
+ import { isDirectInvocation } from '../../plugin/scripts/entrypoint.mjs';
26
27
 
27
28
  export const THRESHOLDS = Object.freeze({
28
29
  minSpanChars: 40, minSpanTokens: 6, maxSpanFractionOfUnit: 0.9,
@@ -232,19 +233,4 @@ export async function main(argv = process.argv.slice(2)) {
232
233
  return 0;
233
234
  }
234
235
 
235
- // Entry-point guard. Compares REALPATHS on both sides: path.resolve() normalizes a path but does
236
- // NOT follow symlinks, while import.meta.url IS symlink-resolved by Node. Through a symlink (npm bin
237
- // shims, wrapper scripts, and every os.tmpdir() path on macOS) the two sides disagree, so main()
238
- // never runs -- and because nothing throws, the process exits 0. A silent exit 0 is indistinguishable
239
- // from "ran, found nothing", which is how prepareCorpusCandidate once reported SUCCESS with no
240
- // archive on disk. Reproduced live 2026-07-27; pinned by tests/unit/entrypoint-symlink.test.mjs.
241
- function isDirectInvocation() {
242
- try {
243
- if (!process.argv[1]) return false;
244
- return fs.realpathSync(process.argv[1]) === fs.realpathSync(fileURLToPath(import.meta.url));
245
- } catch {
246
- return false;
247
- }
248
- }
249
-
250
- if (isDirectInvocation()) process.exitCode = await main();
236
+ if (isDirectInvocation(import.meta.url)) process.exitCode = await main();
@@ -28,6 +28,7 @@ import fs from 'node:fs';
28
28
  import path from 'node:path';
29
29
  import { spawnSync } from 'node:child_process';
30
30
  import { fileURLToPath } from 'node:url';
31
+ import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
31
32
 
32
33
  const ROOT = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
33
34
  /** The last LIVE reading, so an offline build reuses a real measurement instead of a literal. */
@@ -103,7 +104,7 @@ export function orgRepoCount({ owner = OWNER, file = RECORD_PATH, now = new Date
103
104
  * working tree clean for `release-qe`'s stabilization seal.
104
105
  */
105
106
  const isMain = (() => {
106
- try { return process.argv[1] && fs.realpathSync(process.argv[1]) === fileURLToPath(import.meta.url); }
107
+ try { return isDirectInvocation(import.meta.url); }
107
108
  catch { return false; }
108
109
  })();
109
110
 
@@ -40,6 +40,7 @@ import fs from 'node:fs';
40
40
  import path from 'node:path';
41
41
  import readline from 'node:readline';
42
42
  import { fileURLToPath } from 'node:url';
43
+ import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
43
44
 
44
45
  const ROOT = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
45
46
  export const SCHEMA = 'ruvnet-brain.package-cards/1';
@@ -321,7 +322,7 @@ export function snapshotAgeDays(doc, now = Date.now()) {
321
322
  }
322
323
 
323
324
  const isMain = (() => {
324
- try { return process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url); }
325
+ try { return isDirectInvocation(import.meta.url); }
325
326
  catch { return false; }
326
327
  })();
327
328
 
@@ -10,6 +10,7 @@ import { readCandidateRetrieval } from './staged-host-verifier.mjs';
10
10
  import { validateRetrievalCanaryReceipt } from './retrieval-canary.mjs';
11
11
  import { payloadIdFor } from './release-payload.mjs';
12
12
  import { HOST_WARMUP_TIMEOUT_MS, RELEASE_SEARCH_DEADLINE_MS } from './host-install-matrix.mjs';
13
+ import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
13
14
 
14
15
  const sha256 = (bytes) => crypto.createHash('sha256').update(bytes).digest('hex');
15
16
 
@@ -191,7 +192,7 @@ export function buildPrepublicationEvidence({
191
192
  return { leaves, envelope: aggregateEvidence({ sha, payloadId: payload.payloadId, leaves }) };
192
193
  }
193
194
 
194
- const isCli = process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
195
+ const isCli = isDirectInvocation(import.meta.url);
195
196
  if (isCli) {
196
197
  const arg = (name) => {
197
198
  const index = process.argv.indexOf(name);
@@ -31,6 +31,7 @@ import fs from 'node:fs';
31
31
  import path from 'node:path';
32
32
  import { pathToFileURL } from 'node:url';
33
33
  import { sha256File, projectSourceStore } from './rvf-generation.mjs';
34
+ import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
34
35
 
35
36
  export const REQUIRED_SIDECARS = ['.big.rvf', '.big.rvf.embed.json', '.big.rvf.idmap.json', '.meta.json', '.passages.jsonl'];
36
37
  export const OPTIONAL_SIDECARS = ['.symbols.json', '-primer.md'];
@@ -267,4 +268,4 @@ function main() {
267
268
  console.log(JSON.stringify(receipt, null, 2));
268
269
  }
269
270
 
270
- if (process.argv[1] && import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href) main();
271
+ if (isDirectInvocation(import.meta.url)) main();
@@ -8,6 +8,7 @@ import {
8
8
  digest,
9
9
  validateSourceScopeReceipt,
10
10
  } from './source-scope-receipt.mjs';
11
+ import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
11
12
 
12
13
  export const PRODUCT_INTEGRITY_SCHEMA_VERSION = 1;
13
14
  export const PRODUCT_INTEGRITY_TEST_CLASSES = Object.freeze(['essential', 'supporting', 'obsolete']);
@@ -180,4 +181,4 @@ export function runProductIntegrityCli(argv = process.argv.slice(2), io = { stdo
180
181
  semanticReviewVerified: false, behaviorVerified: false, traceSha256: trace.traceSha256 })}\n`); return 0; }
181
182
  throw new Error('invalid product integrity argument combination');
182
183
  }
183
- if (((() => { try { return process.argv[1] && fs.realpathSync(process.argv[1]) === fs.realpathSync(fileURLToPath(import.meta.url)); } catch { return false; } })())) { try { process.exitCode = runProductIntegrityCli(); } catch (error) { console.error(`[product-integrity-contract] ${error.message}`); process.exitCode = 1; } }
184
+ if (isDirectInvocation(import.meta.url)) { try { process.exitCode = runProductIntegrityCli(); } catch (error) { console.error(`[product-integrity-contract] ${error.message}`); process.exitCode = 1; } }
@@ -7,6 +7,7 @@ import path from 'node:path';
7
7
  import { pathToFileURL } from 'node:url';
8
8
  import { evaluateCandidateReceipt } from './release-proof.mjs';
9
9
  import { getVersion } from './version.mjs';
10
+ import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
10
11
 
11
12
  export const PROTECTED_WORKFLOW = 'protected-release';
12
13
  export const PROTECTED_VERSION = getVersion();
@@ -92,7 +93,7 @@ export function validateProtectedPublishInvocation({ root = process.cwd(), env =
92
93
  };
93
94
  }
94
95
 
95
- if (process.argv[1] && pathToFileURL(path.resolve(process.argv[1])).href === import.meta.url) {
96
+ if (isDirectInvocation(import.meta.url)) {
96
97
  const result = validateProtectedPublishInvocation();
97
98
  if (result.verdict !== 'PASS') {
98
99
  console.error('[protected-release] publication refused:');
@@ -8,6 +8,7 @@ import { fileURLToPath } from 'node:url';
8
8
  import { extractZip } from '../kb/zip-extract.mjs';
9
9
  import { abandonPublicVerificationTransaction } from './release-transaction.mjs';
10
10
  import { liveReleaseProvider } from './release-transaction-provider.mjs';
11
+ import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
11
12
 
12
13
  function regularJson(file, max = 131072) {
13
14
  const stat = fs.lstatSync(file);
@@ -90,7 +91,7 @@ export async function abandonPublicVerification({ identityFile, outputFile, env
90
91
  return receipt;
91
92
  }
92
93
 
93
- const direct = process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
94
+ const direct = isDirectInvocation(import.meta.url);
94
95
  if (direct) {
95
96
  const arg = (name) => { const index = process.argv.indexOf(name); return index < 0 ? null : process.argv[index + 1]; };
96
97
  try {
@@ -8,6 +8,7 @@ import { retrievalOracleExpectationFromPlan, validateRetrievalOracleReview } fro
8
8
  import { validateRetrievalCanaryPlan, validateRetrievalCanaryReceipt } from './retrieval-canary.mjs';
9
9
  import { validateNightlyProofReceipt, validateNativeSchedulerSmoke } from './nightly-two-run-proof.mjs';
10
10
  import { CLAUDE_FABLE_5_ID, GPT_5_6_SOL_ID, LEGACY_REVIEW_MODEL_IDS } from './review-model-defaults.mjs';
11
+ import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
11
12
 
12
13
  export const PUBLIC_VERIFICATION_OS = Object.freeze(['linux', 'macos', 'windows']);
13
14
  export const PUBLIC_VERIFICATION_MODES = Object.freeze(['claude', 'codex', 'dual']);
@@ -359,7 +360,7 @@ async function main() {
359
360
  leaves: aggregate.metrics.leaves })}\n`);
360
361
  }
361
362
 
362
- if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
363
+ if (isDirectInvocation(import.meta.url)) {
363
364
  main().catch((error) => {
364
365
  process.stderr.write(`${error.message}\n`);
365
366
  process.exitCode = 1;
@@ -5,6 +5,7 @@ import path from 'node:path';
5
5
  import { fileURLToPath } from 'node:url';
6
6
  import { finalizeReleaseTransaction } from './release-transaction.mjs';
7
7
  import { liveReleaseProvider } from './release-transaction-provider.mjs';
8
+ import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
8
9
 
9
10
  const argument = (args, name) => {
10
11
  const index = args.indexOf(name);
@@ -61,5 +62,5 @@ export async function main(args = process.argv.slice(2)) {
61
62
  }
62
63
  }
63
64
 
64
- const invokedDirectly = process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
65
+ const invokedDirectly = isDirectInvocation(import.meta.url);
65
66
  if (invokedDirectly) process.exitCode = await main();
@@ -17,6 +17,7 @@ import {
17
17
  validateRetrievalQueryEvidence,
18
18
  verifyQueryOracleSource,
19
19
  } from './retrieval-canary.mjs';
20
+ import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
20
21
 
21
22
  const HEX40 = /^[a-f0-9]{40}$/;
22
23
  const HEX64 = /^[a-f0-9]{64}$/;
@@ -602,6 +603,6 @@ export async function main(argv = process.argv.slice(2)) {
602
603
  return result;
603
604
  }
604
605
 
605
- if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
606
+ if (isDirectInvocation(import.meta.url)) {
606
607
  main().catch((error) => { console.error(error.message); process.exitCode = 1; });
607
608
  }
@@ -20,6 +20,7 @@ import {
20
20
  validatePlanAgainstCoverage,
21
21
  validateRetrievalCanaryReceipt,
22
22
  } from './retrieval-canary.mjs';
23
+ import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
23
24
 
24
25
  const HEX40 = /^[a-f0-9]{40}$/;
25
26
  const HEX64 = /^[a-f0-9]{64}$/;
@@ -353,4 +354,4 @@ export async function main(args = process.argv.slice(2)) {
353
354
  }
354
355
  }
355
356
 
356
- if (((() => { try { return process.argv[1] && fs.realpathSync(process.argv[1]) === fs.realpathSync(fileURLToPath(import.meta.url)); } catch { return false; } })())) process.exitCode = await main();
357
+ if (isDirectInvocation(import.meta.url)) process.exitCode = await main();
@@ -29,6 +29,7 @@ import {
29
29
  validateNightlyProofReceipt,
30
30
  } from './nightly-two-run-proof.mjs';
31
31
  import { parseRetrievalResult } from '../kb/retrieval-result.mjs';
32
+ import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
32
33
 
33
34
  const REPO = 'stuinfla/ruvnet-brain';
34
35
  const PACKAGE = 'ruvnet-brain';
@@ -825,4 +826,4 @@ export async function main(args = process.argv.slice(2)) {
825
826
  }
826
827
  }
827
828
 
828
- if (process.argv[1] && pathToFileURL(path.resolve(process.argv[1])).href === import.meta.url) process.exitCode = await main();
829
+ if (isDirectInvocation(import.meta.url)) process.exitCode = await main();
@@ -54,6 +54,7 @@ import path from 'node:path';
54
54
  import { spawnSync } from 'node:child_process';
55
55
  import { fileURLToPath } from 'node:url';
56
56
  import { describeLatestPointer, isCodeReleaseTag, latestCodeReleaseTag } from './release-channel-kind.mjs';
57
+ import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
57
58
 
58
59
  export const EXIT = Object.freeze({ PASS: 0, FAIL: 1, UNKNOWN: 4 });
59
60
 
@@ -275,5 +276,5 @@ export async function main() {
275
276
  return EXIT[verdict];
276
277
  }
277
278
 
278
- const invokedDirectly = process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
279
+ const invokedDirectly = isDirectInvocation(import.meta.url);
279
280
  if (invokedDirectly) process.exit(await main());
@@ -28,6 +28,7 @@ import fs from 'node:fs';
28
28
  import path from 'node:path';
29
29
  import { fileURLToPath } from 'node:url';
30
30
  import { answerFromCards } from '../../kb/card-lane.mjs';
31
+ import { isDirectInvocation } from '../../plugin/scripts/entrypoint.mjs';
31
32
 
32
33
  const HERE = path.dirname(fileURLToPath(import.meta.url));
33
34
  export const REPO_ROOT = path.resolve(HERE, '../..');
@@ -158,20 +159,5 @@ async function main() {
158
159
  process.exit(1);
159
160
  }
160
161
 
161
- // Entry-point guard. Compares REALPATHS on both sides: path.resolve() normalizes a path but does
162
- // NOT follow symlinks, while import.meta.url IS symlink-resolved by Node. Through a symlink (npm bin
163
- // shims, wrapper scripts, and every os.tmpdir() path on macOS) the two sides disagree, so main()
164
- // never runs -- and because nothing throws, the process exits 0. A silent exit 0 is indistinguishable
165
- // from "ran, found nothing", which is how prepareCorpusCandidate once reported SUCCESS with no
166
- // archive on disk. Reproduced live 2026-07-27; pinned by tests/unit/entrypoint-symlink.test.mjs.
167
- function isDirectInvocation() {
168
- try {
169
- if (!process.argv[1]) return false;
170
- return fs.realpathSync(process.argv[1]) === fs.realpathSync(fileURLToPath(import.meta.url));
171
- } catch {
172
- return false;
173
- }
174
- }
175
-
176
- const invokedDirectly = isDirectInvocation();
162
+ const invokedDirectly = isDirectInvocation(import.meta.url);
177
163
  if (invokedDirectly) main();
@@ -52,6 +52,7 @@ import os from 'node:os';
52
52
  import path from 'node:path';
53
53
  import { fileURLToPath } from 'node:url';
54
54
  import { fireHook, resolveInstalledSurface, readInstalledRegistrations } from '../selfcheck.mjs';
55
+ import { isDirectInvocation } from '../../plugin/scripts/entrypoint.mjs';
55
56
 
56
57
  const HERE = path.dirname(fileURLToPath(import.meta.url));
57
58
  export const REPO_ROOT = path.resolve(HERE, '../..');
@@ -225,20 +226,5 @@ async function main() {
225
226
  process.exit(1);
226
227
  }
227
228
 
228
- // Entry-point guard. Compares REALPATHS on both sides: path.resolve() normalizes a path but does
229
- // NOT follow symlinks, while import.meta.url IS symlink-resolved by Node. Through a symlink (npm bin
230
- // shims, wrapper scripts, and every os.tmpdir() path on macOS) the two sides disagree, so main()
231
- // never runs -- and because nothing throws, the process exits 0. A silent exit 0 is indistinguishable
232
- // from "ran, found nothing", which is how prepareCorpusCandidate once reported SUCCESS with no
233
- // archive on disk. Reproduced live 2026-07-27; pinned by tests/unit/entrypoint-symlink.test.mjs.
234
- function isDirectInvocation() {
235
- try {
236
- if (!process.argv[1]) return false;
237
- return fs.realpathSync(process.argv[1]) === fs.realpathSync(fileURLToPath(import.meta.url));
238
- } catch {
239
- return false;
240
- }
241
- }
242
-
243
- const invokedDirectly = isDirectInvocation();
229
+ const invokedDirectly = isDirectInvocation(import.meta.url);
244
230
  if (invokedDirectly) main();
@@ -33,6 +33,7 @@ import { runCommandProbe } from '../../tests/ux/command-probe.mjs';
33
33
  import { runCardLaneGate } from './card-lane-gate.mjs';
34
34
  import { runSessionStartGate } from './session-start-gate.mjs';
35
35
  import { automaticHookRetirementStatus } from '../../bin/install.mjs';
36
+ import { isDirectInvocation } from '../../plugin/scripts/entrypoint.mjs';
36
37
 
37
38
  const HERE = path.dirname(fileURLToPath(import.meta.url));
38
39
  const RENDER_PROBE = path.resolve(HERE, '../../tests/ux/render-probe.mjs');
@@ -394,7 +395,7 @@ export async function runUxSuite() {
394
395
  throw error;
395
396
  }
396
397
 
397
- if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
398
+ if (isDirectInvocation(import.meta.url)) {
398
399
  runUxSuite().catch((e) => {
399
400
  if (!e.receipt) console.error(' ux-suite crashed:', e.message);
400
401
  process.exit(e.receipt ? 1 : 2);
@@ -10,6 +10,7 @@ import { evaluateCandidateReceipt } from './release-proof.mjs';
10
10
  import { aggregateEvidence } from './release-evidence-aggregate.mjs';
11
11
  import { payloadIdFor } from './release-payload.mjs';
12
12
  import { canonicalJson } from './release-transaction.mjs';
13
+ import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
13
14
 
14
15
  const REPO = 'stuinfla/ruvnet-brain';
15
16
  const WORKFLOW = '.github/workflows/release-candidate-preflight.yml';
@@ -109,7 +110,7 @@ export async function checkQualifiedCandidate({ sha, requiredCheck, timeoutMs =
109
110
  await pause(Math.min(pollMs, remaining));
110
111
  }
111
112
  }
112
- if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
113
+ if (isDirectInvocation(import.meta.url)) {
113
114
  try {
114
115
  if (process.env.GITHUB_REPOSITORY !== REPO) throw new Error('unexpected qualification repository');
115
116
  const args = process.argv.slice(2), options = {};
@@ -25,6 +25,7 @@ import {
25
25
  sealGistReceipt,
26
26
  sealGistReceiptSet,
27
27
  } from './gist-receipts.mjs';
28
+ import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
28
29
 
29
30
  export { buildGistAggregate, paragraphChunks, provenanceBanner, rawUrlFor };
30
31
 
@@ -263,7 +264,7 @@ export async function main(argv = process.argv.slice(2), {
263
264
  return 0;
264
265
  }
265
266
 
266
- if (((() => { try { return process.argv[1] && fs.realpathSync(process.argv[1]) === fs.realpathSync(fileURLToPath(import.meta.url)); } catch { return false; } })())) {
267
+ if (isDirectInvocation(import.meta.url)) {
267
268
  main().then((code) => { process.exitCode = code; }).catch((error) => {
268
269
  console.error(error.message);
269
270
  process.exitCode = 1;
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Measures the real hook paths against a real store (#429): prompt-time recall() and SessionStart restore().
4
+ * node scripts/recall-latency-probe.mjs --project <repo> [--runs N]
5
+ * Prints per-run milliseconds, outcome and `uptime` (machine load). Exit 1 unless EVERY prompt recall is
6
+ * ok-with-results / ok-empty within 1900 ms and EVERY restore is 'restored' within 3500 ms. The thresholds are
7
+ * the product's budgets, not tuned to the machine.
8
+ */
9
+ import { execFileSync } from 'node:child_process';
10
+ import path from 'node:path';
11
+ import { recall } from '../plugin/scripts/agentdb-recall.mjs';
12
+ import { restore } from '../plugin/scripts/ruflo-memory-hooks.mjs';
13
+
14
+ export const RECALL_LIMIT_MS = 1900;
15
+ export const RESTORE_LIMIT_MS = 3500;
16
+ const PROMPT = 'Where are we on the recall latency work? Catch me up on the last decision.';
17
+
18
+ const argValue = (name) => { const at = process.argv.indexOf(name); return at >= 0 ? process.argv[at + 1] : undefined; };
19
+ const uptime = () => { try { return execFileSync('uptime', { encoding: 'utf8' }).trim(); } catch { return 'uptime unavailable'; } };
20
+
21
+ async function main() {
22
+ const project = argValue('--project');
23
+ const runs = Number(argValue('--runs') ?? 5);
24
+ if (!project || !Number.isInteger(runs) || runs < 1) {
25
+ console.error('usage: recall-latency-probe.mjs --project <repo> [--runs N]');
26
+ process.exit(2);
27
+ }
28
+ const dir = path.resolve(project);
29
+ let failures = 0;
30
+ for (let run = 1; run <= runs; run += 1) {
31
+ const t0 = Date.now();
32
+ const r = await recall({ prompt: PROMPT, projectDir: dir });
33
+ const recallMs = Date.now() - t0;
34
+ const recallOk = ['ok-with-results', 'ok-empty'].includes(r.outcome) && recallMs <= RECALL_LIMIT_MS;
35
+ const t1 = Date.now();
36
+ const s = await restore({ cwd: dir });
37
+ const restoreMs = Date.now() - t1;
38
+ const restoreOk = s.status === 'restored' && restoreMs <= RESTORE_LIMIT_MS;
39
+ if (!recallOk) failures += 1;
40
+ if (!restoreOk) failures += 1;
41
+ console.log(`run ${run}: recall ${recallMs} ms ${r.outcome}${r.status?.['memory.db'] && r.outcome !== 'ok-with-results' ? ` [${r.status['memory.db']}]` : ''}${r.reason ? ` (${r.reason})` : ''} ${recallOk ? 'PASS' : 'FAIL'}`
42
+ + ` | restore ${restoreMs} ms ${s.status} ${restoreOk ? 'PASS' : 'FAIL'} | ${uptime()}`);
43
+ }
44
+ console.log(failures ? `FAIL: ${failures} of ${runs * 2} measurements outside budget (recall <= ${RECALL_LIMIT_MS} ms, restore <= ${RESTORE_LIMIT_MS} ms)`
45
+ : `PASS: all ${runs * 2} measurements within budget`);
46
+ process.exit(failures ? 1 : 0);
47
+ }
48
+ await main();
@@ -13,6 +13,7 @@ import fs from 'node:fs';
13
13
  import path from 'node:path';
14
14
  import { fileURLToPath } from 'node:url';
15
15
  import { indexCards, rank, SNAPSHOT_FILE } from '../plugin/scripts/package-recommender.mjs';
16
+ import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
16
17
 
17
18
  const ROOT = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
18
19
 
@@ -76,7 +77,7 @@ export function evaluate(items, index) {
76
77
  };
77
78
  }
78
79
 
79
- const isMain = process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
80
+ const isMain = isDirectInvocation(import.meta.url);
80
81
  if (isMain) {
81
82
  const sets = [];
82
83
  let cards = SNAPSHOT_FILE;
@@ -14,6 +14,7 @@ import path from 'node:path';
14
14
  import { fileURLToPath } from 'node:url';
15
15
  import { judgeScore } from './recommendation-judge-score.mjs';
16
16
  import { wilson } from './recommendation-eval.mjs';
17
+ import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
17
18
 
18
19
  const ROOT = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
19
20
  const POS = new Set(['design', 'diagnosis']);
@@ -31,7 +32,7 @@ export function applyFloor(key, picks, floor) {
31
32
  return Object.fromEntries(key.map((k) => [k.qid, k.lane === 'semantic' && !(k.topSimilarity >= floor) ? null : picks[k.qid] ?? null]));
32
33
  }
33
34
 
34
- const isMain = process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
35
+ const isMain = isDirectInvocation(import.meta.url);
35
36
  if (isMain) {
36
37
  const arg = (n, d) => { const i = process.argv.indexOf(n); return i >= 0 && process.argv[i + 1] ? process.argv[i + 1] : d; };
37
38
  const dir = arg('--dir', null);
@@ -13,6 +13,7 @@ import fs from 'node:fs';
13
13
  import path from 'node:path';
14
14
  import { fileURLToPath } from 'node:url';
15
15
  import { wilson } from './recommendation-eval.mjs';
16
+ import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
16
17
 
17
18
  const ROOT = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
18
19
  const POS = new Set(['design', 'diagnosis']);
@@ -57,7 +58,7 @@ export function judgeScore(key, picks, cards, { familyAware = false } = {}) {
57
58
  };
58
59
  }
59
60
 
60
- const isMain = process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
61
+ const isMain = isDirectInvocation(import.meta.url);
61
62
  if (isMain) {
62
63
  const arg = (n, d) => { const i = process.argv.indexOf(n); return i >= 0 && process.argv[i + 1] ? process.argv[i + 1] : d; };
63
64
  const dir = arg('--dir', null);
@@ -15,6 +15,7 @@ import path from 'node:path';
15
15
  import { fileURLToPath } from 'node:url';
16
16
  import { wilson } from './recommendation-eval.mjs';
17
17
  import { applyFloor } from './recommendation-floor.mjs';
18
+ import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
18
19
 
19
20
  const ROOT = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
20
21
  const POS = new Set(['design', 'diagnosis']);
@@ -55,7 +56,7 @@ export function scoreRealHost(real, key, simPicks, cards) {
55
56
  };
56
57
  }
57
58
 
58
- const isMain = process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
59
+ const isMain = isDirectInvocation(import.meta.url);
59
60
  if (isMain) {
60
61
  const arg = (n, d) => { const i = process.argv.indexOf(n); return i >= 0 && process.argv[i + 1] ? process.argv[i + 1] : d; };
61
62
  const real = JSON.parse(fs.readFileSync(arg('--real'), 'utf8'));
@@ -21,6 +21,7 @@ import os from 'node:os';
21
21
  import path from 'node:path';
22
22
  import readline from 'node:readline';
23
23
  import { fileURLToPath } from 'node:url';
24
+ import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
24
25
 
25
26
  const ROOT = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
26
27
  const arg = (n, d) => { const i = process.argv.indexOf(n); return i >= 0 && process.argv[i + 1] ? process.argv[i + 1] : d; };
@@ -53,7 +54,7 @@ export function mentioned(answer, offered) {
53
54
  return null;
54
55
  }
55
56
 
56
- const isMain = process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
57
+ const isMain = isDirectInvocation(import.meta.url);
57
58
  if (isMain) {
58
59
  const outDir = arg('--out', null);
59
60
  if (!outDir || !arg('--key', null)) { console.error('--key and --out are required'); process.exit(2); }
@@ -8,6 +8,7 @@ import { fileURLToPath, pathToFileURL } from 'node:url';
8
8
  import { buildCorpus } from '../kb/forge-corpus.mjs';
9
9
  import { loadRvf } from '../kb/resolve-deps.mjs';
10
10
  import { CAPABILITY_RETIRED_SUFFIXES, isCapabilityOnly } from '../kb/capability-only.mjs';
11
+ import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
11
12
 
12
13
  const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
13
14
  const NAME = 'cognitum-ruos';
@@ -138,6 +139,6 @@ async function main(argv) {
138
139
  console.log(JSON.stringify({ store: NAME, ...result }));
139
140
  }
140
141
 
141
- if (process.argv[1] && pathToFileURL(path.resolve(process.argv[1])).href === import.meta.url) {
142
+ if (isDirectInvocation(import.meta.url)) {
142
143
  try { await main(process.argv.slice(2)); } catch (error) { console.error(`[capability-store] ${error.message}`); process.exitCode = 1; }
143
144
  }
@@ -15,6 +15,7 @@
15
15
  import fs from 'node:fs';
16
16
  import path from 'node:path';
17
17
  import { fileURLToPath } from 'node:url';
18
+ import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
18
19
 
19
20
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
20
21
  const ROOT = path.resolve(__dirname, '..');
@@ -123,6 +124,6 @@ async function main() {
123
124
  }
124
125
  }
125
126
 
126
- if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
127
+ if (isDirectInvocation(import.meta.url)) {
127
128
  main().catch((e) => { console.error('refresh-model-catalog FAILED:', e.message); process.exit(1); });
128
129
  }
@@ -68,6 +68,7 @@ import os from 'node:os';
68
68
  import path from 'node:path';
69
69
  import { spawnSync } from 'node:child_process';
70
70
  import { fileURLToPath, pathToFileURL } from 'node:url';
71
+ import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
71
72
 
72
73
  const HERE = path.dirname(fileURLToPath(import.meta.url));
73
74
  export const REAL_ROOT = path.resolve(HERE, '..');
@@ -1591,7 +1592,7 @@ export async function main(argv = process.argv.slice(2)) {
1591
1592
  return receipt.verdict === 'PASS' ? 0 : 1;
1592
1593
  }
1593
1594
 
1594
- if (((() => { try { return process.argv[1] && fs.realpathSync(process.argv[1]) === fs.realpathSync(fileURLToPath(import.meta.url)); } catch { return false; } })())) {
1595
+ if (isDirectInvocation(import.meta.url)) {
1595
1596
  main().then((code) => { process.exitCode = code; }).catch((error) => {
1596
1597
  console.error(error?.message || error);
1597
1598
  process.exitCode = 1;
@@ -5,6 +5,7 @@
5
5
  import fs from 'node:fs';
6
6
  import path from 'node:path';
7
7
  import { fileURLToPath } from 'node:url';
8
+ import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
8
9
 
9
10
  const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
10
11
  const CANONICAL_PUBLISHERS = new Set([
@@ -203,7 +204,7 @@ export function main(root = ROOT) {
203
204
  // spellings of one file, so the guard silently reads false and the CLI exits 0 having done nothing —
204
205
  // the same defect 6eead7bc fixed in release-projection.mjs, which is a gate this repo relies on.
205
206
  if (((() => {
206
- try { return process.argv[1] && fs.realpathSync(process.argv[1]) === fs.realpathSync(fileURLToPath(import.meta.url)); } catch { return false; }
207
+ try { return isDirectInvocation(import.meta.url); } catch { return false; }
207
208
  })())) {
208
209
  process.exitCode = main();
209
210
  }
@@ -4,6 +4,7 @@ import fs from 'node:fs';
4
4
  import path from 'node:path';
5
5
  import { fileURLToPath } from 'node:url';
6
6
  import { canonicalJson } from './release-transaction.mjs';
7
+ import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
7
8
 
8
9
  export const REQUIRED_RELEASE_LEAVES = Object.freeze([
9
10
  'source-quality',
@@ -38,7 +39,7 @@ export function aggregateEvidence({ sha, payloadId, leaves }) {
38
39
  return { ...envelope, evidenceDigest: crypto.createHash('sha256').update(canonicalJson(envelope)).digest('hex') };
39
40
  }
40
41
 
41
- const isCli = process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
42
+ const isCli = isDirectInvocation(import.meta.url);
42
43
  if (isCli) {
43
44
  const arg = (name) => {
44
45
  const index = process.argv.indexOf(name);
@@ -4,6 +4,7 @@ import fs from 'node:fs';
4
4
  import path from 'node:path';
5
5
  import { fileURLToPath } from 'node:url';
6
6
  import { canonicalJson } from './release-transaction.mjs';
7
+ import { isDirectInvocation } from '../plugin/scripts/entrypoint.mjs';
7
8
 
8
9
  const sha256 = (bytes) => crypto.createHash('sha256').update(bytes).digest('hex');
9
10
  const digestFile = (file) => sha256(fs.readFileSync(file));
@@ -51,7 +52,7 @@ const arg = (name) => {
51
52
  const index = process.argv.indexOf(name);
52
53
  return index >= 0 ? process.argv[index + 1] : null;
53
54
  };
54
- const command = process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)
55
+ const command = isDirectInvocation(import.meta.url)
55
56
  ? process.argv[2]
56
57
  : null;
57
58