@reticlehq/server 2.4.0 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SKILL.md +719 -0
- package/dist/bridge/auth-failure-reason.d.ts +19 -0
- package/dist/bridge/auth-failure-reason.js +29 -0
- package/dist/{bridge-security.d.ts → bridge/bridge-security.d.ts} +1 -1
- package/dist/{bridge-security.js → bridge/bridge-security.js} +1 -1
- package/dist/{bridge.d.ts → bridge/bridge.d.ts} +13 -1
- package/dist/{bridge.js → bridge/bridge.js} +95 -15
- package/dist/{bridge.test-harness.d.ts → bridge/bridge.test-harness.d.ts} +9 -11
- package/dist/{bridge.test-harness.js → bridge/bridge.test-harness.js} +18 -10
- package/dist/{pairing-token.js → bridge/pairing-token.js} +1 -1
- package/dist/capsule/capsule-store.d.ts +8 -3
- package/dist/capsule/capsule-store.js +7 -2
- package/dist/capsule/capsule.js +3 -3
- package/dist/capsule/causal-summary.d.ts +8 -0
- package/dist/capsule/causal-summary.js +22 -12
- package/dist/capsule/divergence.d.ts +9 -4
- package/dist/capsule/divergence.js +8 -8
- package/dist/capsule/predicate-to-links.js +11 -8
- package/dist/cli/cli-doctor.d.ts +1 -0
- package/dist/cli/cli-doctor.js +135 -0
- package/dist/{cli-flow-commands.d.ts → cli/cli-flow-commands.d.ts} +2 -2
- package/dist/{cli-flow-commands.js → cli/cli-flow-commands.js} +15 -15
- package/dist/{cli-launch.d.ts → cli/cli-launch.d.ts} +32 -3
- package/dist/cli/cli-launch.js +171 -0
- package/dist/cli/cli-parse-feedback.d.ts +31 -0
- package/dist/cli/cli-parse-feedback.js +56 -0
- package/dist/{cli-parse.d.ts → cli/cli-parse.d.ts} +6 -19
- package/dist/{cli-parse.js → cli/cli-parse.js} +118 -81
- package/dist/{cli-port.d.ts → cli/cli-port.d.ts} +12 -0
- package/dist/{cli-port.js → cli/cli-port.js} +9 -9
- package/dist/cli/cli-update-commands.d.ts +4 -0
- package/dist/cli/cli-update-commands.js +93 -0
- package/dist/{cli-verify.js → cli/cli-verify.js} +14 -14
- package/dist/{cloud-cli.js → cli/cloud-cli.js} +29 -24
- package/dist/cli/doctor-daemon-line.d.ts +37 -0
- package/dist/cli/doctor-daemon-line.js +35 -0
- package/dist/cli/port-holder.d.ts +43 -0
- package/dist/cli/port-holder.js +99 -0
- package/dist/cli.d.ts +2 -2
- package/dist/cli.js +302 -100
- package/dist/cloud/cloud-config.d.ts +0 -1
- package/dist/cloud/cloud-config.js +9 -9
- package/dist/cloud/cloud-sync.d.ts +1 -7
- package/dist/cloud/cloud-sync.js +12 -12
- package/dist/crawl/crawl-empty.d.ts +16 -0
- package/dist/crawl/crawl-empty.js +24 -0
- package/dist/crawl/crawl-tools.js +4 -1
- package/dist/crawl/crawl.js +26 -7
- package/dist/daemon/daemon-resilience.d.ts +73 -0
- package/dist/daemon/daemon-resilience.js +279 -0
- package/dist/daemon/daemon-usefulness.d.ts +61 -0
- package/dist/daemon/daemon-usefulness.js +62 -0
- package/dist/{daemon.d.ts → daemon/daemon.d.ts} +33 -8
- package/dist/{daemon.js → daemon/daemon.js} +105 -48
- package/dist/daemon/heartbeat.d.ts +115 -0
- package/dist/daemon/heartbeat.js +141 -0
- package/dist/daemon/idle-grace.d.ts +32 -0
- package/dist/daemon/idle-grace.js +41 -0
- package/dist/{idle-shutdown.d.ts → daemon/idle-shutdown.d.ts} +23 -2
- package/dist/{idle-shutdown.js → daemon/idle-shutdown.js} +37 -6
- package/dist/daemon/port-presence.d.ts +76 -0
- package/dist/daemon/port-presence.js +93 -0
- package/dist/daemon/state-dir.d.ts +7 -0
- package/dist/daemon/state-dir.js +59 -0
- package/dist/daemon/unclean-predecessor.d.ts +23 -0
- package/dist/daemon/unclean-predecessor.js +27 -0
- package/dist/domain/domain-model.js +2 -2
- package/dist/events/body-failures.js +8 -8
- package/dist/events/contradictions.d.ts +1 -1
- package/dist/events/contradictions.js +60 -10
- package/dist/events/echo-mismatch.js +10 -10
- package/dist/events/event-filters.js +6 -6
- package/dist/events/json-salvage.d.ts +2 -1
- package/dist/events/json-salvage.js +6 -6
- package/dist/events/observed-in-window.d.ts +12 -0
- package/dist/events/observed-in-window.js +21 -0
- package/dist/events/predicate-eval.d.ts +53 -20
- package/dist/events/predicate-eval.js +179 -55
- package/dist/events/predicate-parse.d.ts +23 -0
- package/dist/events/predicate-parse.js +54 -0
- package/dist/events/predicate.d.ts +7 -2
- package/dist/events/predicate.js +126 -35
- package/dist/events/reconcile.js +5 -5
- package/dist/events/ring-buffer.js +2 -17
- package/dist/events/stale-response.js +6 -6
- package/dist/events/testid-near-miss.d.ts +22 -0
- package/dist/events/testid-near-miss.js +33 -0
- package/dist/events/unit-mismatch.js +8 -8
- package/dist/flows/affected-tools.js +19 -4
- package/dist/flows/affected.js +1 -1
- package/dist/flows/annotate-target.d.ts +1 -0
- package/dist/flows/annotate-target.js +20 -0
- package/dist/flows/annotate-tools.js +25 -3
- package/dist/flows/annotate.js +17 -4
- package/dist/flows/assertion-integrity.d.ts +2 -1
- package/dist/flows/assertion-tiers-store.d.ts +3 -66
- package/dist/flows/assertion-tiers-store.js +2 -2
- package/dist/flows/attributed-failure.d.ts +17 -0
- package/dist/flows/attributed-failure.js +20 -0
- package/dist/flows/buddy-status.d.ts +2 -1
- package/dist/flows/buddy-status.js +2 -2
- package/dist/flows/coverage.d.ts +3 -2
- package/dist/flows/coverage.js +2 -2
- package/dist/flows/decision.js +24 -9
- package/dist/flows/empty-flow.d.ts +6 -0
- package/dist/flows/empty-flow.js +24 -0
- package/dist/flows/flake-store.d.ts +0 -1
- package/dist/flows/flake-store.js +1 -1
- package/dist/flows/flake.d.ts +2 -1
- package/dist/flows/flake.js +2 -2
- package/dist/flows/flow-classify.js +12 -0
- package/dist/flows/flow-replay-run.js +4 -4
- package/dist/flows/flow-replay.d.ts +6 -8
- package/dist/flows/flow-replay.js +132 -48
- package/dist/flows/flow-report.js +1 -1
- package/dist/flows/flow-scope.d.ts +1 -6
- package/dist/flows/flow-step-runners.d.ts +26 -6
- package/dist/flows/flow-step-runners.js +156 -6
- package/dist/flows/flow-success.js +20 -15
- package/dist/flows/flow-tools.js +63 -21
- package/dist/flows/flows.js +15 -11
- package/dist/flows/gate.d.ts +4 -3
- package/dist/flows/gate.js +1 -1
- package/dist/flows/heal-run.d.ts +0 -8
- package/dist/flows/heal-run.js +5 -5
- package/dist/flows/heal.js +3 -3
- package/dist/flows/predicate-to-expect.d.ts +38 -0
- package/dist/flows/predicate-to-expect.js +95 -0
- package/dist/flows/replay.d.ts +16 -1
- package/dist/flows/replay.js +58 -22
- package/dist/flows/role-anchor-nearest.d.ts +6 -0
- package/dist/flows/role-anchor-nearest.js +45 -0
- package/dist/flows/role-drift-reason.d.ts +20 -0
- package/dist/flows/role-drift-reason.js +39 -0
- package/dist/flows/server-verify.d.ts +1 -1
- package/dist/flows/server-verify.js +5 -5
- package/dist/flows/suite-flakes.d.ts +33 -0
- package/dist/flows/suite-flakes.js +32 -0
- package/dist/flows/verify-change-tools.js +32 -10
- package/dist/flows/verify-change-verdict.d.ts +15 -0
- package/dist/flows/verify-change-verdict.js +25 -0
- package/dist/flows/watch-batcher.d.ts +3 -2
- package/dist/honesty/already-true.d.ts +2 -0
- package/dist/honesty/already-true.js +31 -0
- package/dist/honesty/blind-spots.d.ts +35 -3
- package/dist/honesty/blind-spots.js +57 -12
- package/dist/honesty/honesty.d.ts +3 -2
- package/dist/honesty/honesty.js +4 -4
- package/dist/honesty/uncaptured-bodies.js +1 -1
- package/dist/honesty/unread-outcome.js +2 -2
- package/dist/honesty/verified.d.ts +31 -3
- package/dist/honesty/verified.js +64 -8
- package/dist/http-server.js +23 -7
- package/dist/hunt/hunt-report.d.ts +2 -1
- package/dist/hunt/hunt-report.js +2 -2
- package/dist/index.d.ts +9 -6
- package/dist/index.js +41 -25
- package/dist/init/agent-rules.d.ts +20 -1
- package/dist/init/agent-rules.js +64 -8
- package/dist/init/app-choice.d.ts +20 -0
- package/dist/init/app-choice.js +25 -0
- package/dist/init/astro-host.d.ts +21 -0
- package/dist/init/astro-host.js +27 -0
- package/dist/init/astro-patch.d.ts +16 -0
- package/dist/init/astro-patch.js +240 -0
- package/dist/init/capabilities.js +4 -1
- package/dist/init/cra.d.ts +50 -0
- package/dist/init/cra.js +106 -0
- package/dist/init/cursor.js +39 -4
- package/dist/init/desktop-doctor.d.ts +3 -2
- package/dist/init/desktop-doctor.js +4 -4
- package/dist/init/detect.d.ts +9 -2
- package/dist/init/detect.js +24 -5
- package/dist/init/mcp-clients.d.ts +81 -0
- package/dist/init/mcp-clients.js +248 -0
- package/dist/init/mcp-registered.d.ts +12 -0
- package/dist/init/mcp-registered.js +14 -0
- package/dist/init/mcp.d.ts +27 -2
- package/dist/init/mcp.js +40 -4
- package/dist/init/next-patch.d.ts +2 -4
- package/dist/init/next-patch.js +109 -19
- package/dist/init/node-io.js +29 -6
- package/dist/init/plan-framework.d.ts +16 -1
- package/dist/init/plan-framework.js +166 -8
- package/dist/init/plan.d.ts +85 -6
- package/dist/init/plan.js +196 -33
- package/dist/init/project-id.d.ts +5 -9
- package/dist/init/project-id.js +10 -22
- package/dist/init/run.d.ts +23 -1
- package/dist/init/run.js +338 -47
- package/dist/init/slash-command.d.ts +9 -0
- package/dist/init/slash-command.js +10 -1
- package/dist/init/snippets.d.ts +7 -10
- package/dist/init/snippets.js +44 -15
- package/dist/init/vite-config.js +1 -1
- package/dist/init/windows-quote.d.ts +38 -0
- package/dist/init/windows-quote.js +70 -0
- package/dist/init/workspace-apps.d.ts +23 -0
- package/dist/init/workspace-apps.js +68 -0
- package/dist/input/driven-redaction.js +1 -1
- package/dist/input/network-detail.js +8 -8
- package/dist/input/network-mock-tools.js +2 -2
- package/dist/input/network-mock.js +4 -4
- package/dist/input/real-input.js +8 -3
- package/dist/input/scroll-find.js +2 -2
- package/dist/input/scroll-tools.d.ts +0 -5
- package/dist/input/scroll-tools.js +11 -4
- package/dist/input/viewport-tools.js +2 -2
- package/dist/journal/ambient-store.d.ts +0 -2
- package/dist/journal/ambient-store.js +1 -1
- package/dist/journal/ambient.js +1 -1
- package/dist/journal/attach-journal.d.ts +2 -1
- package/dist/journal/deviation-report.js +6 -6
- package/dist/journal/deviation-service.js +1 -1
- package/dist/journal/envelope-store.d.ts +0 -2
- package/dist/journal/envelope-store.js +1 -1
- package/dist/journal/envelope.d.ts +2 -3
- package/dist/journal/envelope.js +3 -3
- package/dist/journal/journal-query.js +1 -1
- package/dist/journal/journal-recorder.d.ts +2 -1
- package/dist/journal/journal-recorder.js +2 -2
- package/dist/journal/rollups.js +9 -9
- package/dist/journal/session-end.d.ts +2 -1
- package/dist/journal/session-journal.js +4 -4
- package/dist/license/license.d.ts +0 -14
- package/dist/license/license.js +8 -8
- package/dist/log.d.ts +10 -0
- package/dist/log.js +11 -1
- package/dist/mcp/mcp-is-error.d.ts +13 -0
- package/dist/mcp/mcp-is-error.js +18 -0
- package/dist/mcp/mcp-outage.d.ts +32 -0
- package/dist/mcp/mcp-outage.js +55 -0
- package/dist/mcp/mcp-proxy.d.ts +134 -0
- package/dist/mcp/mcp-proxy.js +720 -0
- package/dist/{mcp.d.ts → mcp/mcp.d.ts} +13 -10
- package/dist/{mcp.js → mcp/mcp.js} +139 -37
- package/dist/mcp/proxy-handshake.d.ts +3 -0
- package/dist/mcp/proxy-handshake.js +54 -0
- package/dist/mcp/proxy-lifecycle.d.ts +64 -0
- package/dist/mcp/proxy-lifecycle.js +70 -0
- package/dist/mcp/tool-catalog-cache.d.ts +29 -0
- package/dist/mcp/tool-catalog-cache.js +55 -0
- package/dist/oracles/propose-consequences.d.ts +2 -1
- package/dist/oracles/propose-consequences.js +16 -11
- package/dist/oracles/self-instrument.d.ts +2 -1
- package/dist/oracles/self-instrument.js +1 -1
- package/dist/phenomena/phenomena.js +3 -3
- package/dist/platform.d.ts +16 -0
- package/dist/platform.js +16 -0
- package/dist/pool/browser-pool.d.ts +9 -0
- package/dist/pool/browser-pool.js +38 -9
- package/dist/pool/playwright-launcher.d.ts +15 -2
- package/dist/pool/playwright-launcher.js +21 -2
- package/dist/project/fs-port.js +1 -1
- package/dist/project/project-tools.js +2 -2
- package/dist/runs/build-verification-run.js +1 -1
- package/dist/runs/repair-prompt.d.ts +0 -6
- package/dist/runs/repair-prompt.js +3 -3
- package/dist/runs/risk-classify.js +1 -1
- package/dist/runs/run-diff.d.ts +0 -2
- package/dist/runs/run-diff.js +3 -3
- package/dist/runs/run-tools.js +2 -2
- package/dist/runs/verification-sync.js +2 -2
- package/dist/runs/verify-http.js +1 -1
- package/dist/runs/verify-server.js +1 -1
- package/dist/session/attachment-history.d.ts +46 -0
- package/dist/session/attachment-history.js +65 -0
- package/dist/session/command-timeout.d.ts +11 -3
- package/dist/session/command-timeout.js +2 -2
- package/dist/session/control-envelope.d.ts +6 -0
- package/dist/session/control-envelope.js +7 -1
- package/dist/session/dev-server-probe.d.ts +13 -0
- package/dist/session/dev-server-probe.js +52 -0
- package/dist/session/last-act.d.ts +17 -3
- package/dist/session/last-act.js +16 -4
- package/dist/session/live-control-tools.js +7 -2
- package/dist/session/live-control.js +1 -1
- package/dist/session/looks-like-dev-server.d.ts +14 -0
- package/dist/session/looks-like-dev-server.js +25 -0
- package/dist/session/no-session-diagnosis.d.ts +45 -0
- package/dist/session/no-session-diagnosis.js +120 -0
- package/dist/session/no-session-watch.d.ts +21 -0
- package/dist/session/no-session-watch.js +69 -0
- package/dist/session/observed-state.d.ts +4 -0
- package/dist/session/observed-state.js +19 -1
- package/dist/session/output-budget.js +1 -1
- package/dist/session/pending-commands.js +1 -0
- package/dist/session/session-health.d.ts +8 -1
- package/dist/session/session-health.js +8 -7
- package/dist/session/session-info.d.ts +43 -8
- package/dist/session/session-info.js +46 -1
- package/dist/session/session-lease.d.ts +0 -11
- package/dist/session/session-lease.js +1 -1
- package/dist/session/session-manager.d.ts +6 -0
- package/dist/session/session-manager.js +124 -12
- package/dist/session/session-readiness.d.ts +1 -1
- package/dist/session/session-readiness.js +6 -1
- package/dist/session/session-reaper.js +1 -1
- package/dist/session/session-reconnect.d.ts +32 -0
- package/dist/session/session-reconnect.js +42 -0
- package/dist/session/session.d.ts +15 -2
- package/dist/session/session.js +77 -26
- package/dist/session/state-select.d.ts +1 -1
- package/dist/session/state-select.js +1 -1
- package/dist/status-payload.d.ts +19 -0
- package/dist/status-payload.js +11 -0
- package/dist/telemetry/argument-shape.js +10 -1
- package/dist/telemetry/browser-mode.d.ts +27 -0
- package/dist/telemetry/browser-mode.js +33 -0
- package/dist/telemetry/bug-found.d.ts +17 -21
- package/dist/telemetry/bug-found.js +120 -8
- package/dist/telemetry/cli-telemetry.d.ts +2 -12
- package/dist/telemetry/cli-telemetry.js +27 -4
- package/dist/telemetry/crash-cause.d.ts +47 -0
- package/dist/telemetry/crash-cause.js +104 -0
- package/dist/telemetry/daemon-telemetry.d.ts +38 -1
- package/dist/telemetry/daemon-telemetry.js +37 -12
- package/dist/telemetry/dev-repo.js +4 -4
- package/dist/telemetry/error-class.d.ts +26 -0
- package/dist/telemetry/error-class.js +52 -0
- package/dist/telemetry/error-fingerprint.js +21 -2
- package/dist/telemetry/feedback-cli.d.ts +8 -3
- package/dist/telemetry/feedback-cli.js +52 -8
- package/dist/telemetry/feedback-context.d.ts +23 -6
- package/dist/telemetry/feedback-context.js +105 -5
- package/dist/telemetry/feedback-delivery.d.ts +20 -0
- package/dist/telemetry/feedback-delivery.js +30 -0
- package/dist/telemetry/feedback-outbox.d.ts +22 -0
- package/dist/telemetry/feedback-outbox.js +87 -0
- package/dist/telemetry/feedback.d.ts +22 -1
- package/dist/telemetry/feedback.js +67 -8
- package/dist/telemetry/git-facts.d.ts +7 -0
- package/dist/telemetry/git-facts.js +5 -5
- package/dist/telemetry/project-profile.js +2 -1
- package/dist/telemetry/session-metrics.d.ts +47 -3
- package/dist/telemetry/session-metrics.js +315 -15
- package/dist/telemetry/telemetry.d.ts +45 -2
- package/dist/telemetry/telemetry.js +150 -37
- package/dist/telemetry/verification-of.d.ts +22 -0
- package/dist/telemetry/verification-of.js +84 -0
- package/dist/tools/act-capsule.js +2 -2
- package/dist/tools/act-danger.d.ts +12 -0
- package/dist/tools/act-danger.js +20 -3
- package/dist/tools/act-helpers.js +4 -4
- package/dist/tools/act-tools.d.ts +0 -8
- package/dist/tools/act-tools.js +114 -118
- package/dist/tools/act-view.js +7 -7
- package/dist/tools/alias-args.d.ts +17 -0
- package/dist/tools/alias-args.js +25 -0
- package/dist/tools/assert-grade.js +9 -9
- package/dist/tools/assert-verdict.d.ts +9 -1
- package/dist/tools/assert-verdict.js +21 -6
- package/dist/tools/browser-tools.js +36 -11
- package/dist/tools/contract-tools.d.ts +0 -5
- package/dist/tools/contract-tools.js +27 -1
- package/dist/tools/coverage-identity.d.ts +34 -0
- package/dist/tools/coverage-identity.js +46 -0
- package/dist/tools/coverage-tools.js +10 -14
- package/dist/tools/dynamic-tools.d.ts +2 -1
- package/dist/tools/dynamic-tools.js +96 -19
- package/dist/tools/error-recovery.d.ts +34 -2
- package/dist/tools/error-recovery.js +255 -4
- package/dist/tools/feedback-invite.d.ts +48 -0
- package/dist/tools/feedback-invite.js +71 -0
- package/dist/tools/feedback-tools.d.ts +12 -1
- package/dist/tools/feedback-tools.js +28 -5
- package/dist/tools/invoke-tool.d.ts +0 -12
- package/dist/tools/invoke-tool.js +126 -33
- package/dist/tools/lease-hint.d.ts +17 -0
- package/dist/tools/lease-hint.js +24 -0
- package/dist/tools/lease-tools.js +6 -5
- package/dist/tools/merge-tools.d.ts +10 -1
- package/dist/tools/merge-tools.js +9 -3
- package/dist/tools/merged-name-redirect.d.ts +13 -0
- package/dist/tools/merged-name-redirect.js +55 -0
- package/dist/tools/navigate-arrival.d.ts +26 -0
- package/dist/tools/navigate-arrival.js +60 -0
- package/dist/tools/navigate-result.d.ts +31 -0
- package/dist/tools/navigate-result.js +38 -0
- package/dist/tools/observe-tools.js +72 -20
- package/dist/tools/observed-nothing.d.ts +25 -0
- package/dist/tools/observed-nothing.js +33 -0
- package/dist/tools/query-paginate.js +2 -2
- package/dist/tools/query-shape.d.ts +18 -0
- package/dist/tools/query-shape.js +39 -0
- package/dist/tools/read-tools.js +81 -20
- package/dist/tools/real-input-attempt.d.ts +28 -0
- package/dist/tools/real-input-attempt.js +93 -0
- package/dist/tools/reconcile-tools.js +7 -7
- package/dist/tools/reload-result.d.ts +14 -0
- package/dist/tools/reload-result.js +29 -0
- package/dist/tools/snapshot-delta.d.ts +4 -5
- package/dist/tools/snapshot-delta.js +0 -0
- package/dist/tools/tool-kit.d.ts +16 -0
- package/dist/tools/tool-surface.d.ts +83 -0
- package/dist/tools/tool-surface.js +217 -0
- package/dist/tools/tools-helpers.js +6 -6
- package/dist/tools/tools.d.ts +15 -0
- package/dist/tools/tools.js +53 -17
- package/dist/tools/unadvertised-help.d.ts +5 -0
- package/dist/tools/unadvertised-help.js +39 -0
- package/dist/trace.d.ts +69 -0
- package/dist/trace.js +150 -0
- package/dist/update/nudge-credit.d.ts +4 -0
- package/dist/update/nudge-credit.js +53 -0
- package/dist/update/sdk-sync.d.ts +21 -0
- package/dist/update/sdk-sync.js +42 -0
- package/dist/update/update-checker.js +3 -3
- package/dist/update/update-nudge.d.ts +63 -6
- package/dist/update/update-nudge.js +128 -15
- package/dist/update/updater.d.ts +1 -9
- package/dist/update/updater.js +13 -8
- package/dist/version/peer-announce.d.ts +4 -0
- package/dist/version/peer-announce.js +30 -0
- package/dist/{server-version.js → version/server-version.js} +5 -1
- package/dist/version/version-nudge.d.ts +28 -0
- package/dist/version/version-nudge.js +40 -0
- package/dist/version/version-skew.d.ts +66 -0
- package/dist/version/version-skew.js +71 -0
- package/dist/visual/visual-diff.js +2 -2
- package/dist/visual/visual-tools.js +4 -4
- package/docs/README.md +55 -0
- package/docs/agent-cheatsheet.md +85 -0
- package/docs/architecture.md +113 -0
- package/docs/benchmarks.md +255 -0
- package/docs/debugging.md +102 -0
- package/docs/deploy-checks.md +69 -0
- package/docs/desktop-apps.md +256 -0
- package/docs/enterprise.md +62 -0
- package/docs/fixtures-dispatch-receiver.yml +76 -0
- package/docs/fixtures.md +45 -0
- package/docs/flows.md +166 -0
- package/docs/gate-plan.md +98 -0
- package/docs/gates.md +117 -0
- package/docs/getting-started.md +469 -0
- package/docs/human-control.md +63 -0
- package/docs/integration-patterns.md +171 -0
- package/docs/local-registry.md +126 -0
- package/docs/matrix/2.5.0/claude-code-machine.json +19 -0
- package/docs/matrix/2.5.0/codex-machine.json +20 -0
- package/docs/matrix/2.5.0/cursor-machine.json +22 -0
- package/docs/matrix/2.5.0/gemini-machine.json +22 -0
- package/docs/matrix/2.5.0/opencode-machine.json +22 -0
- package/docs/matrix/2.5.0/vscode-machine.json +22 -0
- package/docs/matrix/2.5.0/windsurf-machine.json +22 -0
- package/docs/matrix/MATRIX.md +26 -0
- package/docs/matrix/README.md +59 -0
- package/docs/mint.json +61 -0
- package/docs/multi-agent-testing.md +55 -0
- package/docs/platform-integration.md +122 -0
- package/docs/system-map.md +152 -0
- package/docs/telemetry-contract.md +214 -0
- package/docs/telemetry.md +191 -0
- package/docs/testing.md +82 -0
- package/docs/token-efficiency.md +80 -0
- package/docs/usage.md +1046 -0
- package/package.json +8 -5
- package/dist/cli-doctor.d.ts +0 -6
- package/dist/cli-doctor.js +0 -60
- package/dist/cli-launch.js +0 -112
- package/dist/daemon-resilience.d.ts +0 -22
- package/dist/daemon-resilience.js +0 -85
- package/dist/mcp-proxy.d.ts +0 -67
- package/dist/mcp-proxy.js +0 -337
- package/dist/tools/profiles.d.ts +0 -37
- package/dist/tools/profiles.js +0 -139
- /package/dist/{pairing-token.d.ts → bridge/pairing-token.d.ts} +0 -0
- /package/dist/{token-auth.d.ts → bridge/token-auth.d.ts} +0 -0
- /package/dist/{token-auth.js → bridge/token-auth.js} +0 -0
- /package/dist/{cli-verify.d.ts → cli/cli-verify.d.ts} +0 -0
- /package/dist/{cloud-cli.d.ts → cli/cloud-cli.d.ts} +0 -0
- /package/dist/{server-version.d.ts → version/server-version.d.ts} +0 -0
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure, conservative patchers for an Astro app: the config gets the token/root `define` and the
|
|
3
|
+
* raised build target, one layout gets the dev-only connect `<script>`.
|
|
4
|
+
*
|
|
5
|
+
* Astro was the last gated framework where `init` printed a correct recipe and applied none of it —
|
|
6
|
+
* the only ⚠ left on a supported stack, and the user's first session was two hand-copied snippets
|
|
7
|
+
* away. Astro is Vite-based but renders its own HTML, so the Vite plugin's injection never fires and
|
|
8
|
+
* there is no entry module to wire: the connect has to live in a page or layout `<script>`, and the
|
|
9
|
+
* token has to be inlined by the config because nothing else is in the page's path to inject it.
|
|
10
|
+
*
|
|
11
|
+
* Both patchers bail to `manual` (the printed recipe) on any shape they do not fully recognise.
|
|
12
|
+
* Half-editing a build config is worse than a documented manual step.
|
|
13
|
+
*/
|
|
14
|
+
import { RETICLE_DEFAULT_PORT, bridgeWsUrl } from '@reticlehq/core';
|
|
15
|
+
import { PatchKind } from './patch-kind.js';
|
|
16
|
+
/** Present in a patched config AND in a hand-followed recipe — so both count as already wired. */
|
|
17
|
+
const CONFIG_MARKER = '__RETICLE_TOKEN__';
|
|
18
|
+
/** Present in a patched layout and in the printed recipe's script. */
|
|
19
|
+
const LAYOUT_MARKER = 'reticle.connect';
|
|
20
|
+
/**
|
|
21
|
+
* The SDK, declared so Vite pre-bundles it BEFORE the first page load.
|
|
22
|
+
*
|
|
23
|
+
* The connect script does `await import('@reticlehq/react')`. Undeclared, Vite meets that import
|
|
24
|
+
* mid-load, pre-bundles it, and the hashed `/node_modules/.vite/deps/@reticlehq_react.js?v=…` URL
|
|
25
|
+
* the browser already requested stops existing — the import rejects with "Failed to fetch
|
|
26
|
+
* dynamically imported module", connect() never runs, and the page looks entirely normal. Measured
|
|
27
|
+
* on astro-nanostores, intermittent on whether the dep cache was warm. The Vite plugin has declared
|
|
28
|
+
* the SDK for this exact reason since the bug was first found on React; Astro is hand-patched and
|
|
29
|
+
* never got it.
|
|
30
|
+
*/
|
|
31
|
+
const SDK_INCLUDE_LITERAL = `'@reticlehq/react'`;
|
|
32
|
+
/**
|
|
33
|
+
* An `include:` the app already declared — ours joins that array instead of adding a second key.
|
|
34
|
+
* Deliberately not anchored to a newline: `optimizeDeps: { include: ['x'] }` on one line is the
|
|
35
|
+
* common way to write it, and requiring a line break made the merge miss it and duplicate the key.
|
|
36
|
+
*/
|
|
37
|
+
const EXISTING_INCLUDE = /(\s*include\s*:\s*\[)/;
|
|
38
|
+
const DEFINE_CONFIG = /defineConfig\s*\(\s*\{/;
|
|
39
|
+
/** A `vite:` key whose value is an object LITERAL — the one shape we can merge into safely. */
|
|
40
|
+
const VITE_OBJECT_KEY = /(^\s*vite\s*:\s*\{)/m;
|
|
41
|
+
/** Any `vite:` key at all. One that is not an object literal (a spread, an imported config) is ours to refuse. */
|
|
42
|
+
const ANY_VITE_KEY = /^\s*vite\s*:/m;
|
|
43
|
+
const BODY_CLOSE = '</body>';
|
|
44
|
+
const HELPER = `
|
|
45
|
+
function reticleToken() {
|
|
46
|
+
const dir = process.env['RETICLE_PAIRING_TOKEN_DIR'] || join(homedir(), '.reticle');
|
|
47
|
+
try { return readFileSync(join(dir, 'pairing-token'), 'utf8').trim(); } catch { return ''; }
|
|
48
|
+
}
|
|
49
|
+
`;
|
|
50
|
+
const HELPER_IMPORTS = `import { readFileSync } from 'node:fs';
|
|
51
|
+
import { homedir } from 'node:os';
|
|
52
|
+
import { join } from 'node:path';
|
|
53
|
+
`;
|
|
54
|
+
/**
|
|
55
|
+
* The keys we add inside `vite:`, each with what it must contain.
|
|
56
|
+
*
|
|
57
|
+
* Kept as a list rather than one blob because a config that already sets one of these must be merged
|
|
58
|
+
* INTO, not duplicated: the real astro-nanostores config has `build: { chunkSizeWarningLimit }`, and
|
|
59
|
+
* inserting our own `build` beside it gave the object two — where the last one wins, so
|
|
60
|
+
* `target: 'es2022'` was silently discarded while init reported success. Losing that target is not
|
|
61
|
+
* cosmetic: Astro's default down-levels the modern SDK bundle and dies on a destructuring transform.
|
|
62
|
+
*/
|
|
63
|
+
const VITE_KEYS = [
|
|
64
|
+
{ key: 'build', inner: `\n target: 'es2022',` },
|
|
65
|
+
{
|
|
66
|
+
key: 'optimizeDeps',
|
|
67
|
+
inner: `\n include: [${SDK_INCLUDE_LITERAL}],\n esbuildOptions: { target: 'es2022' },`,
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
key: 'define',
|
|
71
|
+
inner: `\n __RETICLE_TOKEN__: JSON.stringify(reticleToken()),\n __RETICLE_ROOT__: JSON.stringify(process.cwd()),`,
|
|
72
|
+
},
|
|
73
|
+
];
|
|
74
|
+
/** Whole-key form, for a `vite:` block that does not have the key at all. */
|
|
75
|
+
const WHOLE = {
|
|
76
|
+
build: `\n build: { target: 'es2022' },`,
|
|
77
|
+
optimizeDeps: `\n optimizeDeps: { include: [${SDK_INCLUDE_LITERAL}], esbuildOptions: { target: 'es2022' } },`,
|
|
78
|
+
define: `\n define: {\n __RETICLE_TOKEN__: JSON.stringify(reticleToken()),\n __RETICLE_ROOT__: JSON.stringify(process.cwd()),\n },`,
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* Add our keys to an existing `vite: { ... }` block, merging into any the app already set.
|
|
82
|
+
*
|
|
83
|
+
* Returns null when a colliding key is not an object literal (`build: sharedConfig`) — there is no
|
|
84
|
+
* brace to merge into, and duplicating or replacing it would corrupt someone's build.
|
|
85
|
+
*/
|
|
86
|
+
function mergeIntoViteBlock(source, braceAt) {
|
|
87
|
+
let out = source;
|
|
88
|
+
for (const { key, inner } of VITE_KEYS) {
|
|
89
|
+
// Scoped to the vite block by searching from its opening brace: a `build:` under `markdown:` or
|
|
90
|
+
// at the top level is somebody else's key and must not be touched.
|
|
91
|
+
const existing = new RegExp(`(\\n\\s*${key}\\s*:\\s*)([\\{A-Za-z_])`).exec(out.slice(braceAt));
|
|
92
|
+
if (existing?.index === undefined) {
|
|
93
|
+
out = `${out.slice(0, braceAt)}${WHOLE[key] ?? ''}${out.slice(braceAt)}`;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
// The character after the colon decides: `{` is a literal we can open; anything else is a
|
|
97
|
+
// reference we must not touch.
|
|
98
|
+
if (existing[2] !== '{')
|
|
99
|
+
return null;
|
|
100
|
+
const at = braceAt + existing.index + existing[0].length; // just past the `{`
|
|
101
|
+
// An `include:` the app already wrote is an ARRAY, and a second `include:` key in the same
|
|
102
|
+
// object literal is not a merge — the last one wins and one of the two silently disappears.
|
|
103
|
+
// That is how `build.target` was lost while init still reported success. Join their array.
|
|
104
|
+
// Scoped to THIS block's braces: an `include:` under a later key is somebody else's array, and
|
|
105
|
+
// appending the SDK to it would both miss the target and edit a config we were not asked to.
|
|
106
|
+
const own = 'optimizeDeps' === key ? EXISTING_INCLUDE.exec(blockAfter(out, at)) : null;
|
|
107
|
+
if (own?.index === undefined) {
|
|
108
|
+
out = insertAt(out, at, inner);
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
// Their array first, then the rest of our keys at the block's start — inserting at the LOWER
|
|
112
|
+
// offset second keeps the first offset valid.
|
|
113
|
+
out = insertAt(out, at + own.index + own[0].length, `${SDK_INCLUDE_LITERAL}, `);
|
|
114
|
+
out = insertAt(out, at, withoutInclude(inner));
|
|
115
|
+
}
|
|
116
|
+
return out;
|
|
117
|
+
}
|
|
118
|
+
function insertAt(source, at, text) {
|
|
119
|
+
return `${source.slice(0, at)}${text}${source.slice(at)}`;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* The text from just inside an opening brace to its match — a brace count, not a parser.
|
|
123
|
+
*
|
|
124
|
+
* Enough for the one question asked of it (does THIS object literal already declare `include`), and
|
|
125
|
+
* honest about its limit: a `{` or `}` inside a string literal in someone's Vite config would fool
|
|
126
|
+
* it. That only ever shortens or lengthens the window we search for `include:` in, so the worst case
|
|
127
|
+
* is the same duplicate-key merge we would have done before, never a corrupted file.
|
|
128
|
+
*/
|
|
129
|
+
function blockAfter(source, at) {
|
|
130
|
+
let depth = 1;
|
|
131
|
+
for (let i = at; i < source.length; i++) {
|
|
132
|
+
if ('{' === source[i])
|
|
133
|
+
depth += 1;
|
|
134
|
+
else if ('}' === source[i]) {
|
|
135
|
+
depth -= 1;
|
|
136
|
+
if (0 === depth)
|
|
137
|
+
return source.slice(at, i);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return source.slice(at);
|
|
141
|
+
}
|
|
142
|
+
/** The optimizeDeps inner minus our `include:` line, for when the app already has one to join. */
|
|
143
|
+
function withoutInclude(inner) {
|
|
144
|
+
return inner.replace(`\n include: [${SDK_INCLUDE_LITERAL}],`, '');
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* The `vite:` block Astro needs. `build.target` is raised because Astro's default down-levels the
|
|
148
|
+
* modern SDK bundle and dies on a destructuring transform; `__RETICLE_ROOT__` is defined because
|
|
149
|
+
* without it every source pointer comes back as an absolute path from the machine that ran `init`.
|
|
150
|
+
*/
|
|
151
|
+
const VITE_BLOCK = ` vite: {
|
|
152
|
+
build: { target: 'es2022' },
|
|
153
|
+
optimizeDeps: { include: [${SDK_INCLUDE_LITERAL}], esbuildOptions: { target: 'es2022' } },
|
|
154
|
+
define: {
|
|
155
|
+
__RETICLE_TOKEN__: JSON.stringify(reticleToken()),
|
|
156
|
+
__RETICLE_ROOT__: JSON.stringify(process.cwd()),
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
`;
|
|
160
|
+
export function patchAstroConfig(source) {
|
|
161
|
+
if (source.includes(CONFIG_MARKER))
|
|
162
|
+
return { kind: PatchKind.ALREADY };
|
|
163
|
+
// A `vite: { ... }` block is an object literal, so our keys can go straight after the brace and
|
|
164
|
+
// whatever is already in there is untouched. Refusing outright was the only genuine install defect
|
|
165
|
+
// left in the gate: the config bailed while the LAYOUT patch applied anyway, leaving an app with a
|
|
166
|
+
// connect snippet, no inlined token and no raised build target — which cannot connect, reported as
|
|
167
|
+
// one OK step and one warning.
|
|
168
|
+
const viteObject = VITE_OBJECT_KEY.exec(source);
|
|
169
|
+
if (viteObject?.index !== undefined) {
|
|
170
|
+
const at = viteObject.index + viteObject[0].length;
|
|
171
|
+
const merged = mergeIntoViteBlock(source, at);
|
|
172
|
+
if (merged !== null) {
|
|
173
|
+
return {
|
|
174
|
+
kind: PatchKind.APPLY,
|
|
175
|
+
code: `${HELPER_IMPORTS}${merged.trimStart()}\n${HELPER}`.trimEnd() + '\n',
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
return {
|
|
179
|
+
kind: PatchKind.MANUAL,
|
|
180
|
+
reason: 'this config sets a `vite` key Reticle needs (build / optimizeDeps / define) to something ' +
|
|
181
|
+
'other than an object literal — merging into it is your call, not a text edit Reticle should make',
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
if (ANY_VITE_KEY.test(source)) {
|
|
185
|
+
// `vite: sharedConfig` — not an object literal, so there is no brace to merge into and guessing
|
|
186
|
+
// would corrupt a build config. The recipe is the honest answer here.
|
|
187
|
+
return {
|
|
188
|
+
kind: PatchKind.MANUAL,
|
|
189
|
+
reason: 'this config sets `vite:` to something other than an object literal — merging into it is your call, not a text edit Reticle should make',
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
const opening = DEFINE_CONFIG.exec(source);
|
|
193
|
+
if (opening?.index === undefined) {
|
|
194
|
+
return {
|
|
195
|
+
kind: PatchKind.MANUAL,
|
|
196
|
+
reason: "couldn't find a `defineConfig({ ... })` call to extend",
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
const at = opening.index + opening[0].length;
|
|
200
|
+
const withBlock = `${source.slice(0, at)}\n${VITE_BLOCK}${source.slice(at)}`;
|
|
201
|
+
return {
|
|
202
|
+
kind: PatchKind.APPLY,
|
|
203
|
+
code: `${HELPER_IMPORTS}${withBlock.trimStart()}\n${HELPER}`.trimEnd() + '\n',
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
/** The dev-only connect that goes inside the layout's `<body>`. */
|
|
207
|
+
function astroConnectScript(port, projectId) {
|
|
208
|
+
const url = port !== undefined && port !== RETICLE_DEFAULT_PORT
|
|
209
|
+
? `\n url: '${bridgeWsUrl(port)}',`
|
|
210
|
+
: '';
|
|
211
|
+
const id = projectId !== undefined && projectId.length > 0 ? `\n projectId: '${projectId}',` : '';
|
|
212
|
+
return ` <script>
|
|
213
|
+
if (import.meta.env.DEV) {
|
|
214
|
+
const token = typeof __RETICLE_TOKEN__ !== 'undefined' ? __RETICLE_TOKEN__ : '';
|
|
215
|
+
const root = typeof __RETICLE_ROOT__ !== 'undefined' ? __RETICLE_ROOT__ : '';
|
|
216
|
+
const { reticle, install } = await import('@reticlehq/react');
|
|
217
|
+
install();
|
|
218
|
+
reticle.connect({${id}${url}
|
|
219
|
+
...(token.length > 0 ? { token } : {}),
|
|
220
|
+
...(root.length > 0 ? { root } : {}),
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
</script>
|
|
224
|
+
`;
|
|
225
|
+
}
|
|
226
|
+
export function patchAstroLayout(source, port, projectId) {
|
|
227
|
+
if (source.includes(LAYOUT_MARKER))
|
|
228
|
+
return { kind: PatchKind.ALREADY };
|
|
229
|
+
const at = source.lastIndexOf(BODY_CLOSE);
|
|
230
|
+
if (at < 0) {
|
|
231
|
+
return {
|
|
232
|
+
kind: PatchKind.MANUAL,
|
|
233
|
+
reason: "couldn't find a `</body>` to place the connect script before",
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
return {
|
|
237
|
+
kind: PatchKind.APPLY,
|
|
238
|
+
code: `${source.slice(0, at)}${astroConnectScript(port, projectId)}${source.slice(at)}`,
|
|
239
|
+
};
|
|
240
|
+
}
|
|
@@ -35,7 +35,10 @@ export function scanTestids(sources) {
|
|
|
35
35
|
*/
|
|
36
36
|
const STORE_LIBRARIES = [
|
|
37
37
|
['@tanstack/react-query', "registerStore('queries', tanstackQueryStore(queryClient))"],
|
|
38
|
-
[
|
|
38
|
+
[
|
|
39
|
+
'zustand',
|
|
40
|
+
"registerStore('app', useStore) // pass the store itself, not () => store.getState()",
|
|
41
|
+
],
|
|
39
42
|
['@reduxjs/toolkit', "registerStore('app', store)"],
|
|
40
43
|
['redux', "registerStore('app', store)"],
|
|
41
44
|
['jotai', "registerStore('app', jotaiStore(getDefaultStore(), { cart, user }))"],
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wiring Reticle into a Create React App project.
|
|
3
|
+
*
|
|
4
|
+
* CRA had no automated path: init reported `⚠ Connect snippet → index.html`, a target that cannot
|
|
5
|
+
* work. CRA's `public/index.html` is a static template the bundler never processes for modules, so a
|
|
6
|
+
* bare import in it resolves to nothing. `src/index.tsx` is what gets bundled, and that is where the
|
|
7
|
+
* connect has to arrive.
|
|
8
|
+
*
|
|
9
|
+
* The token is the second half of the problem. Every other stack has somewhere to inline it —
|
|
10
|
+
* Vite defines `__RETICLE_TOKEN__`, the Astro and HTML snippets read the file in Node. `src/index.tsx`
|
|
11
|
+
* is browser code inside a bundler that inlines exactly one thing: environment variables prefixed
|
|
12
|
+
* `REACT_APP_`. So it travels through `.env.development.local`, which is CRA's own documented
|
|
13
|
+
* mechanism and is gitignored by CRA's own template.
|
|
14
|
+
*/
|
|
15
|
+
/** The line added to `src/index.tsx`. Side-effect import: the module guards itself on NODE_ENV. */
|
|
16
|
+
export declare const CRA_DEV_MODULE_IMPORT = "import './reticle-dev';";
|
|
17
|
+
export declare const CRA_DEV_MODULE_PATH = "src/reticle-dev.ts";
|
|
18
|
+
export declare const CRA_ENV_PATH = ".env.development.local";
|
|
19
|
+
export declare const TOKEN_VAR = "REACT_APP_RETICLE_TOKEN";
|
|
20
|
+
/**
|
|
21
|
+
* What the app itself says when the token is not there.
|
|
22
|
+
*
|
|
23
|
+
* The token is a per-machine secret and CRA's own template gitignores the file it lives in, so it
|
|
24
|
+
* CANNOT travel with the repo — that part is correct and must stay. What was wrong is that it failed
|
|
25
|
+
* silently: a teammate clones, runs `npm start`, and the app connects with an empty token, so the
|
|
26
|
+
* only signal is the bridge's generic `authentication failed` in a console nobody had open. Naming
|
|
27
|
+
* the variable, the gitignored file and the one command that fixes it turns a dead app into a
|
|
28
|
+
* one-line repair. Every other stack reads the token in Node at dev-server start (the Vite plugin,
|
|
29
|
+
* withReticle); CRA has no such hook without ejecting, so this message is the fix.
|
|
30
|
+
*/
|
|
31
|
+
export declare const CRA_TOKEN_MISSING_NOTE: string;
|
|
32
|
+
/**
|
|
33
|
+
* Said as its own NOTICE line, beside the write — not inside it.
|
|
34
|
+
*
|
|
35
|
+
* The caveat has always been in the write step's `detail`, and the write step renders `[✓]`. But
|
|
36
|
+
* `SKILL.md` tells whoever reads the report: *"If every line is `✓`, `·` or `–`, skip to Step 4 and
|
|
37
|
+
* validate."* So the one fact that makes this install conditional sat in the one place the reading
|
|
38
|
+
* protocol says to ignore — which is exactly how it was reported to us as "4 OK marks and no
|
|
39
|
+
* warning". The words were on screen; the reader was following instructions.
|
|
40
|
+
*
|
|
41
|
+
* It cannot simply be promoted: `run.ts` only writes steps whose status is APPLY, so demoting the
|
|
42
|
+
* token step to NOTICE would stop it writing the token and break the install outright.
|
|
43
|
+
*/
|
|
44
|
+
export declare const CRA_TOKEN_PER_MACHINE_NOTICE: string;
|
|
45
|
+
/** Add the import after the last existing one, or null when it is already present. */
|
|
46
|
+
export declare function craImportPatch(source: string): string | null;
|
|
47
|
+
/** Set the token variable, or null when nothing needs to change. */
|
|
48
|
+
export declare function craEnvPatch(existing: string | null, token: string): string | null;
|
|
49
|
+
/** The dev-only connect module imported from `src/index.tsx`. */
|
|
50
|
+
export declare function craDevModuleFile(port: number | undefined, projectId?: string): string;
|
package/dist/init/cra.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wiring Reticle into a Create React App project.
|
|
3
|
+
*
|
|
4
|
+
* CRA had no automated path: init reported `⚠ Connect snippet → index.html`, a target that cannot
|
|
5
|
+
* work. CRA's `public/index.html` is a static template the bundler never processes for modules, so a
|
|
6
|
+
* bare import in it resolves to nothing. `src/index.tsx` is what gets bundled, and that is where the
|
|
7
|
+
* connect has to arrive.
|
|
8
|
+
*
|
|
9
|
+
* The token is the second half of the problem. Every other stack has somewhere to inline it —
|
|
10
|
+
* Vite defines `__RETICLE_TOKEN__`, the Astro and HTML snippets read the file in Node. `src/index.tsx`
|
|
11
|
+
* is browser code inside a bundler that inlines exactly one thing: environment variables prefixed
|
|
12
|
+
* `REACT_APP_`. So it travels through `.env.development.local`, which is CRA's own documented
|
|
13
|
+
* mechanism and is gitignored by CRA's own template.
|
|
14
|
+
*/
|
|
15
|
+
/** The line added to `src/index.tsx`. Side-effect import: the module guards itself on NODE_ENV. */
|
|
16
|
+
export const CRA_DEV_MODULE_IMPORT = "import './reticle-dev';";
|
|
17
|
+
export const CRA_DEV_MODULE_PATH = 'src/reticle-dev.ts';
|
|
18
|
+
export const CRA_ENV_PATH = '.env.development.local';
|
|
19
|
+
export const TOKEN_VAR = 'REACT_APP_RETICLE_TOKEN';
|
|
20
|
+
/**
|
|
21
|
+
* What the app itself says when the token is not there.
|
|
22
|
+
*
|
|
23
|
+
* The token is a per-machine secret and CRA's own template gitignores the file it lives in, so it
|
|
24
|
+
* CANNOT travel with the repo — that part is correct and must stay. What was wrong is that it failed
|
|
25
|
+
* silently: a teammate clones, runs `npm start`, and the app connects with an empty token, so the
|
|
26
|
+
* only signal is the bridge's generic `authentication failed` in a console nobody had open. Naming
|
|
27
|
+
* the variable, the gitignored file and the one command that fixes it turns a dead app into a
|
|
28
|
+
* one-line repair. Every other stack reads the token in Node at dev-server start (the Vite plugin,
|
|
29
|
+
* withReticle); CRA has no such hook without ejecting, so this message is the fix.
|
|
30
|
+
*/
|
|
31
|
+
export const CRA_TOKEN_MISSING_NOTE = `${TOKEN_VAR} is not set, so Reticle cannot pair with the daemon. ` +
|
|
32
|
+
`The pairing token is per-machine and ${CRA_ENV_PATH} is gitignored by CRA's template, ` +
|
|
33
|
+
'so it does not survive a clone. Run `npx reticle init` in this project to write it for this machine.';
|
|
34
|
+
/**
|
|
35
|
+
* Said as its own NOTICE line, beside the write — not inside it.
|
|
36
|
+
*
|
|
37
|
+
* The caveat has always been in the write step's `detail`, and the write step renders `[✓]`. But
|
|
38
|
+
* `SKILL.md` tells whoever reads the report: *"If every line is `✓`, `·` or `–`, skip to Step 4 and
|
|
39
|
+
* validate."* So the one fact that makes this install conditional sat in the one place the reading
|
|
40
|
+
* protocol says to ignore — which is exactly how it was reported to us as "4 OK marks and no
|
|
41
|
+
* warning". The words were on screen; the reader was following instructions.
|
|
42
|
+
*
|
|
43
|
+
* It cannot simply be promoted: `run.ts` only writes steps whose status is APPLY, so demoting the
|
|
44
|
+
* token step to NOTICE would stop it writing the token and break the install outright.
|
|
45
|
+
*/
|
|
46
|
+
export const CRA_TOKEN_PER_MACHINE_NOTICE = `${CRA_ENV_PATH} is in CRA's own .gitignore, so the token just written does not travel. ` +
|
|
47
|
+
'It works on THIS machine and nowhere else: a teammate cloning the repo, CI, or a container ' +
|
|
48
|
+
'gets an app that boots, never pairs, and reports it only in the browser console. ' +
|
|
49
|
+
'Each developer runs `npx reticle init` once locally.';
|
|
50
|
+
/** Add the import after the last existing one, or null when it is already present. */
|
|
51
|
+
export function craImportPatch(source) {
|
|
52
|
+
if (source.includes(CRA_DEV_MODULE_IMPORT))
|
|
53
|
+
return null;
|
|
54
|
+
const lines = source.split('\n');
|
|
55
|
+
// After the LAST import: React's own imports must still run first, and a side-effect import placed
|
|
56
|
+
// above them would connect before the app exists.
|
|
57
|
+
let lastImport = -1;
|
|
58
|
+
for (const [index, line] of lines.entries()) {
|
|
59
|
+
if (/^\s*import\s/.test(line))
|
|
60
|
+
lastImport = index;
|
|
61
|
+
}
|
|
62
|
+
lines.splice(lastImport + 1, 0, CRA_DEV_MODULE_IMPORT);
|
|
63
|
+
return lines.join('\n');
|
|
64
|
+
}
|
|
65
|
+
/** Set the token variable, or null when nothing needs to change. */
|
|
66
|
+
export function craEnvPatch(existing, token) {
|
|
67
|
+
if ('' === token)
|
|
68
|
+
return null;
|
|
69
|
+
const line = `${TOKEN_VAR}=${token}`;
|
|
70
|
+
if (null === existing || '' === existing.trim())
|
|
71
|
+
return `${line}\n`;
|
|
72
|
+
if (existing.includes(line))
|
|
73
|
+
return null;
|
|
74
|
+
// Replace rather than append: two assignments of one variable is a silent coin flip on which wins.
|
|
75
|
+
if (new RegExp(`^${TOKEN_VAR}=`, 'm').test(existing)) {
|
|
76
|
+
return existing.replace(new RegExp(`^${TOKEN_VAR}=.*$`, 'm'), line);
|
|
77
|
+
}
|
|
78
|
+
return `${existing.endsWith('\n') ? existing : `${existing}\n`}${line}\n`;
|
|
79
|
+
}
|
|
80
|
+
/** The dev-only connect module imported from `src/index.tsx`. */
|
|
81
|
+
export function craDevModuleFile(port, projectId) {
|
|
82
|
+
const fields = [];
|
|
83
|
+
if (port !== undefined)
|
|
84
|
+
fields.push(`url: 'ws://127.0.0.1:${String(port)}/reticle'`);
|
|
85
|
+
if (projectId !== undefined && projectId.length > 0)
|
|
86
|
+
fields.push(`projectId: '${projectId}'`);
|
|
87
|
+
const inline = fields.length > 0 ? `${fields.join(', ')}, ` : '';
|
|
88
|
+
return `// Dev-only: connect Reticle. Imported for its side effect from src/index.tsx.
|
|
89
|
+
//
|
|
90
|
+
// CRA's public/index.html is a static template the bundler never processes for modules, so the
|
|
91
|
+
// connect cannot live there. The pairing token arrives through REACT_APP_RETICLE_TOKEN because
|
|
92
|
+
// REACT_APP_* is the only thing CRA inlines into browser code.
|
|
93
|
+
if (process.env.NODE_ENV === 'development') {
|
|
94
|
+
void import('@reticlehq/react').then(({ reticle, install }) => {
|
|
95
|
+
install();
|
|
96
|
+
const token = process.env.${TOKEN_VAR} ?? '';
|
|
97
|
+
// Loud on purpose: without this the only symptom is the bridge's generic auth failure.
|
|
98
|
+
if (token.length === 0) console.error(${JSON.stringify(`[reticle] ${CRA_TOKEN_MISSING_NOTE}`)});
|
|
99
|
+
// Still attempt it — a bridge running without a token pairs fine.
|
|
100
|
+
reticle.connect({ ${inline}...(token.length > 0 ? { token } : {}) });
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export {};
|
|
105
|
+
`;
|
|
106
|
+
}
|
package/dist/init/cursor.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* (jsonc/comment) files bail to manual rather than being rewritten.
|
|
6
6
|
*/
|
|
7
7
|
import { NPX, MCP_SERVER_NAME, npxServerArgs } from './mcp.js';
|
|
8
|
+
import { RETICLE_NPM_PACKAGE } from '../version/server-version.js';
|
|
8
9
|
/** Path of Cursor's global MCP config, relative to the user's home directory. */
|
|
9
10
|
export const CURSOR_MCP_RELPATH = '.cursor/mcp.json';
|
|
10
11
|
/** The directory whose presence signals Cursor is installed for this user. */
|
|
@@ -18,18 +19,47 @@ export function cursorServerEntry() {
|
|
|
18
19
|
return { command: NPX, args: npxServerArgs() };
|
|
19
20
|
}
|
|
20
21
|
function parseConfig(existing) {
|
|
21
|
-
if (
|
|
22
|
+
if (null === existing || 0 === existing.trim().length)
|
|
22
23
|
return { ok: true, config: {} };
|
|
23
24
|
try {
|
|
24
25
|
const parsed = JSON.parse(existing);
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
// Valid JSON that is not a PLAIN OBJECT — `[]`, `3`, `"x"`, `null` — used to fall through to an
|
|
27
|
+
// empty config, and the file was then rewritten wholesale. Unparseable JSON was already handled
|
|
28
|
+
// conservatively; this adjacent case destroyed the file instead. Whatever is in there is the
|
|
29
|
+
// user's, and we do not understand it, so we do not touch it.
|
|
30
|
+
if (typeof parsed !== 'object' || null === parsed || Array.isArray(parsed))
|
|
31
|
+
return { ok: false };
|
|
27
32
|
return { ok: true, config: parsed };
|
|
28
33
|
}
|
|
29
34
|
catch {
|
|
30
35
|
return { ok: false };
|
|
31
36
|
}
|
|
32
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* Should this entry be left exactly as it is?
|
|
40
|
+
*
|
|
41
|
+
* Two different reasons to leave one alone, and only one of them is "it is correct":
|
|
42
|
+
* - it already matches what we would write; or
|
|
43
|
+
* - it is not an entry WE wrote. Somebody pointing `reticle` at their own local build is a
|
|
44
|
+
* deliberate choice, and silently replacing it with the published npx command would break their
|
|
45
|
+
* setup to fix a problem they do not have.
|
|
46
|
+
*
|
|
47
|
+
* What IS repaired is a stale entry of our own shape — an old pin, a command that has moved — which
|
|
48
|
+
* presence-only idempotency reported as "already registered" and never fixed, so an upgrade could
|
|
49
|
+
* not repair the thing an upgrade exists to repair.
|
|
50
|
+
*/
|
|
51
|
+
function leaveEntryAlone(existing) {
|
|
52
|
+
if (JSON.stringify(existing) === JSON.stringify(cursorServerEntry()))
|
|
53
|
+
return true;
|
|
54
|
+
if (typeof existing !== 'object' || null === existing)
|
|
55
|
+
return true;
|
|
56
|
+
const command = existing.command;
|
|
57
|
+
const args = existing.args;
|
|
58
|
+
const ours = command === NPX &&
|
|
59
|
+
Array.isArray(args) &&
|
|
60
|
+
args.some((arg) => 'string' === typeof arg && arg.includes(RETICLE_NPM_PACKAGE));
|
|
61
|
+
return !ours;
|
|
62
|
+
}
|
|
33
63
|
export function mergeCursorConfig(existing) {
|
|
34
64
|
const parsed = parseConfig(existing);
|
|
35
65
|
if (!parsed.ok) {
|
|
@@ -37,7 +67,12 @@ export function mergeCursorConfig(existing) {
|
|
|
37
67
|
}
|
|
38
68
|
const config = parsed.config;
|
|
39
69
|
const servers = config.mcpServers ?? {};
|
|
40
|
-
|
|
70
|
+
// Presence alone used to mean "already registered", so a `reticle` entry left by an older release —
|
|
71
|
+
// a command that no longer exists, or a stale pin — was reported done and never repaired. An
|
|
72
|
+
// upgrade could not fix a bad entry, which is exactly when it needs to. Only OUR key is replaced;
|
|
73
|
+
// every other server and top-level key is carried through untouched by the spread below.
|
|
74
|
+
if (Object.prototype.hasOwnProperty.call(servers, MCP_SERVER_NAME) &&
|
|
75
|
+
leaveEntryAlone(servers[MCP_SERVER_NAME])) {
|
|
41
76
|
return { status: CursorMergeStatus.ALREADY, content: existing ?? '' };
|
|
42
77
|
}
|
|
43
78
|
const merged = {
|
|
@@ -23,7 +23,7 @@ export declare const DesktopFinding: {
|
|
|
23
23
|
readonly ELECTRON_CAPTURE_MISSING: "electron-capture-missing";
|
|
24
24
|
};
|
|
25
25
|
export type DesktopFinding = (typeof DesktopFinding)[keyof typeof DesktopFinding];
|
|
26
|
-
|
|
26
|
+
interface DesktopDiagnosis {
|
|
27
27
|
code: DesktopFinding;
|
|
28
28
|
/** Which file to look at. */
|
|
29
29
|
file: string;
|
|
@@ -33,8 +33,9 @@ export interface DesktopDiagnosis {
|
|
|
33
33
|
fix: string;
|
|
34
34
|
}
|
|
35
35
|
/** Reads a project-relative path. Undefined means the file does not exist. */
|
|
36
|
-
|
|
36
|
+
type ReadFile = (path: string) => string | undefined;
|
|
37
37
|
/** Is this project a desktop shell at all? Used to print a positive result instead of silence. */
|
|
38
38
|
export declare function isDesktopProject(read: ReadFile): boolean;
|
|
39
39
|
/** Which desktop shell (if any) this project is, and what is missing from its Reticle wiring. */
|
|
40
40
|
export declare function diagnoseDesktop(read: ReadFile, port: number): DesktopDiagnosis[];
|
|
41
|
+
export {};
|
|
@@ -31,7 +31,7 @@ function parseJson(text) {
|
|
|
31
31
|
return undefined;
|
|
32
32
|
try {
|
|
33
33
|
const parsed = JSON.parse(text);
|
|
34
|
-
return typeof parsed
|
|
34
|
+
return 'object' === typeof parsed && parsed !== null
|
|
35
35
|
? parsed
|
|
36
36
|
: undefined;
|
|
37
37
|
}
|
|
@@ -40,7 +40,7 @@ function parseJson(text) {
|
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
function record(value) {
|
|
43
|
-
return typeof value
|
|
43
|
+
return 'object' === typeof value && value !== null ? value : {};
|
|
44
44
|
}
|
|
45
45
|
/**
|
|
46
46
|
* Does this CSP permit the bridge WebSocket on `port`?
|
|
@@ -72,7 +72,7 @@ function diagnoseTauri(read, port) {
|
|
|
72
72
|
if (conf === undefined)
|
|
73
73
|
return [];
|
|
74
74
|
const security = record(record(conf['app'])['security']);
|
|
75
|
-
const csp = typeof security['csp']
|
|
75
|
+
const csp = 'string' === typeof security['csp'] ? security['csp'] : undefined;
|
|
76
76
|
const findings = [];
|
|
77
77
|
if (!cspAllowsBridge(csp, port)) {
|
|
78
78
|
findings.push({
|
|
@@ -193,6 +193,6 @@ export function diagnoseDesktop(read, port) {
|
|
|
193
193
|
const deps = { ...record(pkg['dependencies']), ...record(pkg['devDependencies']) };
|
|
194
194
|
if (deps['electron'] === undefined)
|
|
195
195
|
return [];
|
|
196
|
-
const main = typeof pkg['main']
|
|
196
|
+
const main = 'string' === typeof pkg['main'] ? pkg['main'] : undefined;
|
|
197
197
|
return main === undefined ? [] : diagnoseElectron(read, main);
|
|
198
198
|
}
|
package/dist/init/detect.d.ts
CHANGED
|
@@ -7,6 +7,8 @@ export declare const Framework: {
|
|
|
7
7
|
readonly VITE: "vite";
|
|
8
8
|
readonly SVELTEKIT: "sveltekit";
|
|
9
9
|
readonly ASTRO: "astro";
|
|
10
|
+
/** Create React App. No config file exists, so `react-scripts` in the dependencies is the signal. */
|
|
11
|
+
readonly CRA: "cra";
|
|
10
12
|
readonly HTML: "html";
|
|
11
13
|
};
|
|
12
14
|
export type Framework = (typeof Framework)[keyof typeof Framework];
|
|
@@ -60,8 +62,13 @@ export interface Detection {
|
|
|
60
62
|
}
|
|
61
63
|
/** Extract the leading major version from a semver range like "^19.0.0" or "19.1.1". */
|
|
62
64
|
export declare function parseMajor(range: string | undefined): number | undefined;
|
|
63
|
-
/**
|
|
64
|
-
|
|
65
|
+
/**
|
|
66
|
+
* Whether an installed tree identifies the manager that built it. Callers use this to decide whether
|
|
67
|
+
* they still need weaker evidence — see `resolveLockfiles`, which stops inheriting an ancestor
|
|
68
|
+
* lockfile once the project's own tree can answer the question.
|
|
69
|
+
*/
|
|
70
|
+
export declare function namesAPackageManager(markers: ReadonlySet<string>): boolean;
|
|
71
|
+
export declare function detectPackageManager(lockfiles: ReadonlySet<string>, nodeModulesMarkers: ReadonlySet<string>): PackageManager;
|
|
65
72
|
export declare function detect(input: DetectInput): Detection;
|
|
66
73
|
interface InstallCommand {
|
|
67
74
|
command: string;
|
package/dist/init/detect.js
CHANGED
|
@@ -7,6 +7,8 @@ export const Framework = {
|
|
|
7
7
|
VITE: 'vite',
|
|
8
8
|
SVELTEKIT: 'sveltekit',
|
|
9
9
|
ASTRO: 'astro',
|
|
10
|
+
/** Create React App. No config file exists, so `react-scripts` in the dependencies is the signal. */
|
|
11
|
+
CRA: 'cra',
|
|
10
12
|
HTML: 'html',
|
|
11
13
|
};
|
|
12
14
|
export const PackageManager = {
|
|
@@ -30,7 +32,12 @@ export const UiLibrary = {
|
|
|
30
32
|
const NEXT_CONFIGS = ['next.config.js', 'next.config.mjs', 'next.config.ts', 'next.config.cjs'];
|
|
31
33
|
const VITE_CONFIGS = ['vite.config.js', 'vite.config.ts', 'vite.config.mjs', 'vite.config.mts'];
|
|
32
34
|
const SVELTE_CONFIGS = ['svelte.config.js', 'svelte.config.ts', 'svelte.config.mjs'];
|
|
33
|
-
const ASTRO_CONFIGS = [
|
|
35
|
+
const ASTRO_CONFIGS = [
|
|
36
|
+
'astro.config.mjs',
|
|
37
|
+
'astro.config.js',
|
|
38
|
+
'astro.config.ts',
|
|
39
|
+
'astro.config.cjs',
|
|
40
|
+
];
|
|
34
41
|
function depVersion(pkg, name) {
|
|
35
42
|
return pkg.dependencies?.[name] ?? pkg.devDependencies?.[name] ?? pkg.peerDependencies?.[name];
|
|
36
43
|
}
|
|
@@ -42,7 +49,7 @@ export function parseMajor(range) {
|
|
|
42
49
|
if (range === undefined)
|
|
43
50
|
return undefined;
|
|
44
51
|
const match = range.match(/(\d+)/);
|
|
45
|
-
if (
|
|
52
|
+
if (null === match || match[1] === undefined)
|
|
46
53
|
return undefined;
|
|
47
54
|
const major = parseInt(match[1], 10);
|
|
48
55
|
return isNaN(major) ? undefined : major;
|
|
@@ -62,13 +69,21 @@ const NODE_MODULES_MARKERS = [
|
|
|
62
69
|
['.package-lock.json', PackageManager.NPM],
|
|
63
70
|
];
|
|
64
71
|
/** Marker basenames present inside the project's `node_modules`, if it has one. */
|
|
65
|
-
|
|
72
|
+
function packageManagerFromNodeModules(markers) {
|
|
66
73
|
for (const [name, pm] of NODE_MODULES_MARKERS)
|
|
67
74
|
if (markers.has(name))
|
|
68
75
|
return pm;
|
|
69
76
|
return undefined;
|
|
70
77
|
}
|
|
71
|
-
|
|
78
|
+
/**
|
|
79
|
+
* Whether an installed tree identifies the manager that built it. Callers use this to decide whether
|
|
80
|
+
* they still need weaker evidence — see `resolveLockfiles`, which stops inheriting an ancestor
|
|
81
|
+
* lockfile once the project's own tree can answer the question.
|
|
82
|
+
*/
|
|
83
|
+
export function namesAPackageManager(markers) {
|
|
84
|
+
return packageManagerFromNodeModules(markers) !== undefined;
|
|
85
|
+
}
|
|
86
|
+
export function detectPackageManager(lockfiles, nodeModulesMarkers) {
|
|
72
87
|
if (lockfiles.has('pnpm-lock.yaml'))
|
|
73
88
|
return PackageManager.PNPM;
|
|
74
89
|
if (lockfiles.has('yarn.lock'))
|
|
@@ -97,6 +112,10 @@ function detectFramework(input) {
|
|
|
97
112
|
if (depVersion(pkg, 'vite') !== undefined || hasAnyConfig(configFiles, VITE_CONFIGS)) {
|
|
98
113
|
return Framework.VITE;
|
|
99
114
|
}
|
|
115
|
+
// Checked after Vite, never before: a project migrating off CRA can carry both, and the Vite path
|
|
116
|
+
// is the one that works. CRA has no config file at all, so the dependency is the only signal.
|
|
117
|
+
if (depVersion(pkg, 'react-scripts') !== undefined)
|
|
118
|
+
return Framework.CRA;
|
|
100
119
|
return Framework.HTML;
|
|
101
120
|
}
|
|
102
121
|
/**
|
|
@@ -136,7 +155,7 @@ const INSTALL_ARGS = {
|
|
|
136
155
|
};
|
|
137
156
|
/** Build a dev-dependency install command for one or more packages (e.g. the kit + its build plugin). */
|
|
138
157
|
export function installCommandParts(pm, pkgs) {
|
|
139
|
-
const list = typeof pkgs
|
|
158
|
+
const list = 'string' === typeof pkgs ? [pkgs] : pkgs;
|
|
140
159
|
return { command: pm, args: [...INSTALL_ARGS[pm], ...list] };
|
|
141
160
|
}
|
|
142
161
|
export function installCommand(pm, pkgs) {
|