@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,81 @@
|
|
|
1
|
+
export declare const McpClient: {
|
|
2
|
+
readonly CLAUDE_CODE: "claude-code";
|
|
3
|
+
readonly CURSOR: "cursor";
|
|
4
|
+
readonly WINDSURF: "windsurf";
|
|
5
|
+
readonly OPENCODE: "opencode";
|
|
6
|
+
readonly CODEX: "codex";
|
|
7
|
+
readonly GEMINI: "gemini";
|
|
8
|
+
readonly VSCODE: "vscode";
|
|
9
|
+
};
|
|
10
|
+
export type McpClient = (typeof McpClient)[keyof typeof McpClient];
|
|
11
|
+
/** Where the config lives. `cli` means the client owns registration and we shell out to it. */
|
|
12
|
+
export declare const ConfigScope: {
|
|
13
|
+
readonly HOME: "home";
|
|
14
|
+
readonly PROJECT: "project";
|
|
15
|
+
readonly CLI: "cli";
|
|
16
|
+
};
|
|
17
|
+
export type ConfigScope = (typeof ConfigScope)[keyof typeof ConfigScope];
|
|
18
|
+
/** How the file is written. `toml` is deliberately never auto-merged — see mergeClientConfig. */
|
|
19
|
+
export declare const ConfigFormat: {
|
|
20
|
+
readonly JSON: "json";
|
|
21
|
+
readonly TOML: "toml";
|
|
22
|
+
readonly CLI: "cli";
|
|
23
|
+
};
|
|
24
|
+
export type ConfigFormat = (typeof ConfigFormat)[keyof typeof ConfigFormat];
|
|
25
|
+
export interface ClientSpec {
|
|
26
|
+
id: McpClient;
|
|
27
|
+
label: string;
|
|
28
|
+
scope: ConfigScope;
|
|
29
|
+
/** Relative to `$HOME` for `home`, to the project root for `project`. Empty for `cli`. */
|
|
30
|
+
relPath: string;
|
|
31
|
+
format: ConfigFormat;
|
|
32
|
+
/** The object key holding the server map — `mcpServers`, `servers`, or `mcp`. */
|
|
33
|
+
serversKey: string;
|
|
34
|
+
/** Our entry, in THIS client's shape. */
|
|
35
|
+
entry: () => unknown;
|
|
36
|
+
/** Where the shape came from, so the next person can re-check it rather than trust this file. */
|
|
37
|
+
docs: string;
|
|
38
|
+
}
|
|
39
|
+
export declare const MCP_CLIENTS: readonly ClientSpec[];
|
|
40
|
+
export declare function clientSpec(id: McpClient): ClientSpec;
|
|
41
|
+
export declare const ClientMergeStatus: {
|
|
42
|
+
readonly APPLY: "apply";
|
|
43
|
+
readonly ALREADY: "already";
|
|
44
|
+
readonly MANUAL: "manual";
|
|
45
|
+
};
|
|
46
|
+
export type ClientMergeStatus = (typeof ClientMergeStatus)[keyof typeof ClientMergeStatus];
|
|
47
|
+
export interface ClientMergeResult {
|
|
48
|
+
status: ClientMergeStatus;
|
|
49
|
+
/** Full file content to write. Byte-identical to `existing` when not `apply`. */
|
|
50
|
+
content: string;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Merge our server into this client's config.
|
|
54
|
+
*
|
|
55
|
+
* TOML is never auto-merged. Editing TOML without a parser is how a config file gets corrupted, and
|
|
56
|
+
* a corrupted `~/.codex/config.toml` costs the user every server they had, not just ours. Printing
|
|
57
|
+
* the exact block is honest; writing a guess is not — so Codex returns MANUAL with a snippet.
|
|
58
|
+
*/
|
|
59
|
+
export declare function mergeClientConfig(spec: ClientSpec, existing: string | null): ClientMergeResult;
|
|
60
|
+
/**
|
|
61
|
+
* A block the user can paste, for a client we could not write.
|
|
62
|
+
*
|
|
63
|
+
* Every client gets one, including the ones we CAN write: when a merge comes back MANUAL because the
|
|
64
|
+
* file could not be parsed, the snippet is the only actionable thing left to give.
|
|
65
|
+
*/
|
|
66
|
+
export declare function clientSnippet(spec: ClientSpec): string;
|
|
67
|
+
/**
|
|
68
|
+
* The path whose existence means "this user has this client".
|
|
69
|
+
*
|
|
70
|
+
* Detection is deliberately conservative and one-directional: we write into a config a client
|
|
71
|
+
* ALREADY has, and never create `~/.gemini/` or `~/.codeium/` for somebody who does not use them.
|
|
72
|
+
* Littering a home directory to register a server nobody asked for is worse than printing a snippet.
|
|
73
|
+
*
|
|
74
|
+
* For a `home` client this is the directory above its config; for a `project` client it is the
|
|
75
|
+
* config file (or its directory) inside the repo, which is unambiguous evidence somebody uses it
|
|
76
|
+
* here. Same reasoning as Cursor's existing rule, which also accepts a project-level `.cursor/`
|
|
77
|
+
* because a fresh Cursor profile has not written `~/.cursor` yet.
|
|
78
|
+
*/
|
|
79
|
+
export declare function clientMarkerRelPath(spec: ClientSpec): string;
|
|
80
|
+
/** Clients `init` can wire by writing a file — everything but the CLI-registered one. */
|
|
81
|
+
export declare function fileBackedClients(): readonly ClientSpec[];
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where each agent keeps its MCP config, and how to write ours into it.
|
|
3
|
+
*
|
|
4
|
+
* `init` knew two clients: Claude Code (a CLI shell-out) and Cursor (a JSON merge). Everything else
|
|
5
|
+
* got a printed snippet. That is the blocker under the whole release matrix — a per-client release
|
|
6
|
+
* artifact for a client `init` cannot wire measures whether somebody pasted JSON correctly, not
|
|
7
|
+
* whether the product works.
|
|
8
|
+
*
|
|
9
|
+
* Every path and shape below was read from the client's own documentation rather than recalled,
|
|
10
|
+
* because they differ in ways that look like they should not:
|
|
11
|
+
*
|
|
12
|
+
* Cursor / Windsurf / Gemini `mcpServers: { name: { command, args } }` — one shape, three paths
|
|
13
|
+
* VS Code `servers:` — a different key entirely
|
|
14
|
+
* OpenCode `mcp: { name: { type, command: [cmd, ...args] } }` — an ARRAY
|
|
15
|
+
* Codex TOML
|
|
16
|
+
*
|
|
17
|
+
* A single "write mcp.json" assuming the Cursor shape would produce a file three of these ignore:
|
|
18
|
+
* an install that reports success and registers nothing, which is the exact failure class this
|
|
19
|
+
* repo's install gate exists to catch.
|
|
20
|
+
*/
|
|
21
|
+
import { NPX, MCP_SERVER_NAME, npxServerArgs } from './mcp.js';
|
|
22
|
+
import { RETICLE_NPM_PACKAGE } from '../version/server-version.js';
|
|
23
|
+
export const McpClient = {
|
|
24
|
+
CLAUDE_CODE: 'claude-code',
|
|
25
|
+
CURSOR: 'cursor',
|
|
26
|
+
WINDSURF: 'windsurf',
|
|
27
|
+
OPENCODE: 'opencode',
|
|
28
|
+
CODEX: 'codex',
|
|
29
|
+
GEMINI: 'gemini',
|
|
30
|
+
VSCODE: 'vscode',
|
|
31
|
+
};
|
|
32
|
+
/** Where the config lives. `cli` means the client owns registration and we shell out to it. */
|
|
33
|
+
export const ConfigScope = {
|
|
34
|
+
HOME: 'home',
|
|
35
|
+
PROJECT: 'project',
|
|
36
|
+
CLI: 'cli',
|
|
37
|
+
};
|
|
38
|
+
/** How the file is written. `toml` is deliberately never auto-merged — see mergeClientConfig. */
|
|
39
|
+
export const ConfigFormat = {
|
|
40
|
+
JSON: 'json',
|
|
41
|
+
TOML: 'toml',
|
|
42
|
+
CLI: 'cli',
|
|
43
|
+
};
|
|
44
|
+
/** The `{ command, args }` entry Cursor, Windsurf, Gemini and VS Code all take. */
|
|
45
|
+
function commandArgsEntry() {
|
|
46
|
+
return { command: NPX, args: npxServerArgs() };
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* OpenCode's entry. `command` is a single ARRAY containing the executable and its arguments, and
|
|
50
|
+
* `enabled` is explicit — a server registered but disabled is the quietest possible failure.
|
|
51
|
+
*/
|
|
52
|
+
function openCodeEntry() {
|
|
53
|
+
return { type: 'local', command: [NPX, ...npxServerArgs()], enabled: true };
|
|
54
|
+
}
|
|
55
|
+
export const MCP_CLIENTS = [
|
|
56
|
+
{
|
|
57
|
+
id: McpClient.CLAUDE_CODE,
|
|
58
|
+
label: 'Claude Code',
|
|
59
|
+
scope: ConfigScope.CLI,
|
|
60
|
+
relPath: '',
|
|
61
|
+
format: ConfigFormat.CLI,
|
|
62
|
+
serversKey: 'mcpServers',
|
|
63
|
+
entry: commandArgsEntry,
|
|
64
|
+
docs: 'registered via `claude mcp add -s user` — see init/mcp.ts',
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
id: McpClient.CURSOR,
|
|
68
|
+
label: 'Cursor',
|
|
69
|
+
scope: ConfigScope.HOME,
|
|
70
|
+
relPath: '.cursor/mcp.json',
|
|
71
|
+
format: ConfigFormat.JSON,
|
|
72
|
+
serversKey: 'mcpServers',
|
|
73
|
+
entry: commandArgsEntry,
|
|
74
|
+
docs: 'https://docs.cursor.com — global ~/.cursor/mcp.json, or ./.cursor/mcp.json per project',
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
id: McpClient.WINDSURF,
|
|
78
|
+
label: 'Windsurf',
|
|
79
|
+
scope: ConfigScope.HOME,
|
|
80
|
+
relPath: '.codeium/windsurf/mcp_config.json',
|
|
81
|
+
format: ConfigFormat.JSON,
|
|
82
|
+
serversKey: 'mcpServers',
|
|
83
|
+
entry: commandArgsEntry,
|
|
84
|
+
docs: 'Windsurf docs — ~/.codeium/windsurf/mcp_config.json (same shape as Cursor, different path)',
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
id: McpClient.GEMINI,
|
|
88
|
+
label: 'Gemini CLI',
|
|
89
|
+
scope: ConfigScope.HOME,
|
|
90
|
+
relPath: '.gemini/settings.json',
|
|
91
|
+
format: ConfigFormat.JSON,
|
|
92
|
+
serversKey: 'mcpServers',
|
|
93
|
+
entry: commandArgsEntry,
|
|
94
|
+
docs: 'https://google-gemini.github.io/gemini-cli/docs/tools/mcp-server.html',
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
id: McpClient.VSCODE,
|
|
98
|
+
label: 'VS Code (Copilot)',
|
|
99
|
+
scope: ConfigScope.PROJECT,
|
|
100
|
+
relPath: '.vscode/mcp.json',
|
|
101
|
+
format: ConfigFormat.JSON,
|
|
102
|
+
// NOT `mcpServers`. Writing that key here registers nothing and reports success.
|
|
103
|
+
serversKey: 'servers',
|
|
104
|
+
entry: commandArgsEntry,
|
|
105
|
+
docs: 'VS Code MCP docs — .vscode/mcp.json, key is `servers`',
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
id: McpClient.OPENCODE,
|
|
109
|
+
label: 'OpenCode',
|
|
110
|
+
scope: ConfigScope.PROJECT,
|
|
111
|
+
relPath: 'opencode.json',
|
|
112
|
+
format: ConfigFormat.JSON,
|
|
113
|
+
serversKey: 'mcp',
|
|
114
|
+
entry: openCodeEntry,
|
|
115
|
+
docs: 'https://opencode.ai/docs/config — `mcp` key, type:"local", command is an array',
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
id: McpClient.CODEX,
|
|
119
|
+
label: 'Codex CLI',
|
|
120
|
+
scope: ConfigScope.HOME,
|
|
121
|
+
relPath: '.codex/config.toml',
|
|
122
|
+
format: ConfigFormat.TOML,
|
|
123
|
+
serversKey: 'mcp_servers',
|
|
124
|
+
entry: commandArgsEntry,
|
|
125
|
+
docs: 'https://developers.openai.com/codex/mcp — ~/.codex/config.toml, [mcp_servers.<name>]',
|
|
126
|
+
},
|
|
127
|
+
];
|
|
128
|
+
export function clientSpec(id) {
|
|
129
|
+
const found = MCP_CLIENTS.find((spec) => spec.id === id);
|
|
130
|
+
if (found === undefined)
|
|
131
|
+
throw new Error(`no MCP client spec for '${id}'`);
|
|
132
|
+
return found;
|
|
133
|
+
}
|
|
134
|
+
export const ClientMergeStatus = {
|
|
135
|
+
APPLY: 'apply',
|
|
136
|
+
ALREADY: 'already',
|
|
137
|
+
MANUAL: 'manual',
|
|
138
|
+
};
|
|
139
|
+
function parseConfig(existing) {
|
|
140
|
+
if (null === existing || 0 === existing.trim().length)
|
|
141
|
+
return { ok: true, config: {} };
|
|
142
|
+
try {
|
|
143
|
+
const parsed = JSON.parse(existing);
|
|
144
|
+
// Valid JSON that is not a PLAIN OBJECT — `[]`, `3`, `"x"`, `null` — must not fall through to an
|
|
145
|
+
// empty config, because the file then gets rewritten wholesale. Unparseable JSON was always
|
|
146
|
+
// handled conservatively; this adjacent case destroyed the file instead.
|
|
147
|
+
if (typeof parsed !== 'object' || null === parsed || Array.isArray(parsed))
|
|
148
|
+
return { ok: false };
|
|
149
|
+
return { ok: true, config: parsed };
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
return { ok: false };
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Should this entry be left exactly as it is?
|
|
157
|
+
*
|
|
158
|
+
* Two different reasons to leave one alone, and only one is "it is correct":
|
|
159
|
+
* - it already matches what we would write; or
|
|
160
|
+
* - it is not an entry WE wrote. Somebody pointing `reticle` at their own local build made a
|
|
161
|
+
* deliberate choice, and replacing it with the published npx command breaks a setup to fix a
|
|
162
|
+
* problem they do not have.
|
|
163
|
+
*
|
|
164
|
+
* What IS repaired is a stale entry of our own shape — an old pin, a moved command — which
|
|
165
|
+
* presence-only idempotency reported as "already registered" and never fixed, so an upgrade could
|
|
166
|
+
* not repair the thing an upgrade exists to repair.
|
|
167
|
+
*/
|
|
168
|
+
function leaveEntryAlone(existing, spec) {
|
|
169
|
+
if (JSON.stringify(existing) === JSON.stringify(spec.entry()))
|
|
170
|
+
return true;
|
|
171
|
+
if (typeof existing !== 'object' || null === existing)
|
|
172
|
+
return true;
|
|
173
|
+
const record = existing;
|
|
174
|
+
// Both shapes carry the package name somewhere in a string; find it without assuming which field.
|
|
175
|
+
const tokens = [record.command, record.args].flat().filter((v) => 'string' === typeof v);
|
|
176
|
+
const ours = tokens.some((t) => t === NPX) && tokens.some((t) => t.includes(RETICLE_NPM_PACKAGE));
|
|
177
|
+
return !ours;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Merge our server into this client's config.
|
|
181
|
+
*
|
|
182
|
+
* TOML is never auto-merged. Editing TOML without a parser is how a config file gets corrupted, and
|
|
183
|
+
* a corrupted `~/.codex/config.toml` costs the user every server they had, not just ours. Printing
|
|
184
|
+
* the exact block is honest; writing a guess is not — so Codex returns MANUAL with a snippet.
|
|
185
|
+
*/
|
|
186
|
+
export function mergeClientConfig(spec, existing) {
|
|
187
|
+
if (spec.format !== ConfigFormat.JSON) {
|
|
188
|
+
return { status: ClientMergeStatus.MANUAL, content: existing ?? '' };
|
|
189
|
+
}
|
|
190
|
+
const parsed = parseConfig(existing);
|
|
191
|
+
if (!parsed.ok)
|
|
192
|
+
return { status: ClientMergeStatus.MANUAL, content: existing ?? '' };
|
|
193
|
+
const config = parsed.config;
|
|
194
|
+
const current = config[spec.serversKey];
|
|
195
|
+
const servers = 'object' === typeof current && null !== current && !Array.isArray(current)
|
|
196
|
+
? current
|
|
197
|
+
: {};
|
|
198
|
+
if (Object.prototype.hasOwnProperty.call(servers, MCP_SERVER_NAME) &&
|
|
199
|
+
leaveEntryAlone(servers[MCP_SERVER_NAME], spec)) {
|
|
200
|
+
return { status: ClientMergeStatus.ALREADY, content: existing ?? '' };
|
|
201
|
+
}
|
|
202
|
+
// Only OUR key is replaced; every other server and top-level key is carried through untouched.
|
|
203
|
+
const merged = {
|
|
204
|
+
...config,
|
|
205
|
+
[spec.serversKey]: { ...servers, [MCP_SERVER_NAME]: spec.entry() },
|
|
206
|
+
};
|
|
207
|
+
return { status: ClientMergeStatus.APPLY, content: `${JSON.stringify(merged, null, 2)}\n` };
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* A block the user can paste, for a client we could not write.
|
|
211
|
+
*
|
|
212
|
+
* Every client gets one, including the ones we CAN write: when a merge comes back MANUAL because the
|
|
213
|
+
* file could not be parsed, the snippet is the only actionable thing left to give.
|
|
214
|
+
*/
|
|
215
|
+
export function clientSnippet(spec) {
|
|
216
|
+
if (spec.format === ConfigFormat.TOML) {
|
|
217
|
+
const args = npxServerArgs()
|
|
218
|
+
.map((a) => `"${a}"`)
|
|
219
|
+
.join(', ');
|
|
220
|
+
return `[mcp_servers.${MCP_SERVER_NAME}]\ncommand = "${NPX}"\nargs = [${args}]\n`;
|
|
221
|
+
}
|
|
222
|
+
if (spec.format === ConfigFormat.CLI) {
|
|
223
|
+
return `claude mcp add -s user ${MCP_SERVER_NAME} -- ${NPX} ${npxServerArgs().join(' ')}\n`;
|
|
224
|
+
}
|
|
225
|
+
return `${JSON.stringify({ [spec.serversKey]: { [MCP_SERVER_NAME]: spec.entry() } }, null, 2)}\n`;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* The path whose existence means "this user has this client".
|
|
229
|
+
*
|
|
230
|
+
* Detection is deliberately conservative and one-directional: we write into a config a client
|
|
231
|
+
* ALREADY has, and never create `~/.gemini/` or `~/.codeium/` for somebody who does not use them.
|
|
232
|
+
* Littering a home directory to register a server nobody asked for is worse than printing a snippet.
|
|
233
|
+
*
|
|
234
|
+
* For a `home` client this is the directory above its config; for a `project` client it is the
|
|
235
|
+
* config file (or its directory) inside the repo, which is unambiguous evidence somebody uses it
|
|
236
|
+
* here. Same reasoning as Cursor's existing rule, which also accepts a project-level `.cursor/`
|
|
237
|
+
* because a fresh Cursor profile has not written `~/.cursor` yet.
|
|
238
|
+
*/
|
|
239
|
+
export function clientMarkerRelPath(spec) {
|
|
240
|
+
if (spec.scope === ConfigScope.CLI)
|
|
241
|
+
return '';
|
|
242
|
+
const parts = spec.relPath.split('/');
|
|
243
|
+
return parts.length > 1 ? parts.slice(0, -1).join('/') : spec.relPath;
|
|
244
|
+
}
|
|
245
|
+
/** Clients `init` can wire by writing a file — everything but the CLI-registered one. */
|
|
246
|
+
export function fileBackedClients() {
|
|
247
|
+
return MCP_CLIENTS.filter((spec) => spec.scope !== ConfigScope.CLI);
|
|
248
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Did `reticle init` actually register the MCP server?
|
|
3
|
+
*
|
|
4
|
+
* This was `!failed.has(MCP_TARGET)` — the negation of one narrow failure set — so a step that was
|
|
5
|
+
* SKIPPED (`--no-mcp`) or left MANUAL (the user has to run a command themselves) reported success,
|
|
6
|
+
* because neither is a failure. Under `--no-mcp` every run claimed registration.
|
|
7
|
+
*
|
|
8
|
+
* It is the onboarding funnel's most important field, and it was wrong in the flattering direction
|
|
9
|
+
* on exactly the runs least likely to have a working install.
|
|
10
|
+
*/
|
|
11
|
+
import { StepStatus } from './plan.js';
|
|
12
|
+
export declare function wasMcpRegistered(status: StepStatus | undefined): boolean;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Did `reticle init` actually register the MCP server?
|
|
3
|
+
*
|
|
4
|
+
* This was `!failed.has(MCP_TARGET)` — the negation of one narrow failure set — so a step that was
|
|
5
|
+
* SKIPPED (`--no-mcp`) or left MANUAL (the user has to run a command themselves) reported success,
|
|
6
|
+
* because neither is a failure. Under `--no-mcp` every run claimed registration.
|
|
7
|
+
*
|
|
8
|
+
* It is the onboarding funnel's most important field, and it was wrong in the flattering direction
|
|
9
|
+
* on exactly the runs least likely to have a working install.
|
|
10
|
+
*/
|
|
11
|
+
import { StepStatus } from './plan.js';
|
|
12
|
+
export function wasMcpRegistered(status) {
|
|
13
|
+
return status === StepStatus.APPLY || status === StepStatus.ALREADY;
|
|
14
|
+
}
|
package/dist/init/mcp.d.ts
CHANGED
|
@@ -12,9 +12,34 @@
|
|
|
12
12
|
* global agent config.
|
|
13
13
|
*/
|
|
14
14
|
export declare const MCP_SERVER_NAME = "reticle";
|
|
15
|
+
/**
|
|
16
|
+
* The registered command is STILL bare `npx` on every platform, deliberately.
|
|
17
|
+
*
|
|
18
|
+
* Windows is 66% of Reticle's users (65 of 99 in a day of telemetry) and has NO CI, no fixture, and
|
|
19
|
+
* nothing in this repo has ever run there. Switching the majority platform to a launch command
|
|
20
|
+
* nobody can test — where `.cmd` also has its own spawn caveats in some hosts — risks breaking the
|
|
21
|
+
* users it is meant to help. So the fallback is DOCUMENTED (see mcpManual) rather than defaulted,
|
|
22
|
+
* until somebody can run it on Windows.
|
|
23
|
+
*/
|
|
15
24
|
export declare const NPX = "npx";
|
|
16
|
-
/**
|
|
17
|
-
*
|
|
25
|
+
/**
|
|
26
|
+
* Args after `npx` that launch the bridge: `@reticlehq/server mcp`. Portless — the port comes from
|
|
27
|
+
* the project's `.reticle.json` at runtime, so one global entry works for every project.
|
|
28
|
+
*
|
|
29
|
+
* DELIBERATELY UNPINNED, and the trade is worth stating. `reticle init` pins the SDK to the CLI's
|
|
30
|
+
* exact version, so on release day the app can be on the new one while npx serves a cached older
|
|
31
|
+
* build — a real skew, reported from the field. Pinning this entry would remove that window.
|
|
32
|
+
*
|
|
33
|
+
* It would also freeze the agent's MCP server at whatever version was installed the day `init` ran,
|
|
34
|
+
* for as long as that entry survives — and `reticle update` upgrades the CLI, not a global agent
|
|
35
|
+
* config. Reticle's biggest measured problem is fixes not reaching people (2.4.0 reached zero users
|
|
36
|
+
* before its nudge existed), and a permanent pin makes that worse for every install, to close a
|
|
37
|
+
* window that lasts until the next npx cache miss.
|
|
38
|
+
*
|
|
39
|
+
* So it stays unpinned, and the skew is handled where it actually shows up: the contract fingerprint
|
|
40
|
+
* makes a real mismatch loud on the next tool result (see version-skew), and a stale entry of our own
|
|
41
|
+
* shape is now repaired on re-run rather than reported "already registered" (see cursor.ts).
|
|
42
|
+
*/
|
|
18
43
|
export declare function npxServerArgs(): string[];
|
|
19
44
|
interface ClaudeAddCommand {
|
|
20
45
|
command: string;
|
package/dist/init/mcp.js
CHANGED
|
@@ -11,16 +11,41 @@
|
|
|
11
11
|
* project's `.reticle.json` in its CWD (see cli-port.ts). The port belongs to the project, not the
|
|
12
12
|
* global agent config.
|
|
13
13
|
*/
|
|
14
|
-
import { RETICLE_NPM_PACKAGE } from '../server-version.js';
|
|
14
|
+
import { RETICLE_NPM_PACKAGE } from '../version/server-version.js';
|
|
15
15
|
export const MCP_SERVER_NAME = 'reticle';
|
|
16
|
+
/**
|
|
17
|
+
* The registered command is STILL bare `npx` on every platform, deliberately.
|
|
18
|
+
*
|
|
19
|
+
* Windows is 66% of Reticle's users (65 of 99 in a day of telemetry) and has NO CI, no fixture, and
|
|
20
|
+
* nothing in this repo has ever run there. Switching the majority platform to a launch command
|
|
21
|
+
* nobody can test — where `.cmd` also has its own spawn caveats in some hosts — risks breaking the
|
|
22
|
+
* users it is meant to help. So the fallback is DOCUMENTED (see mcpManual) rather than defaulted,
|
|
23
|
+
* until somebody can run it on Windows.
|
|
24
|
+
*/
|
|
16
25
|
export const NPX = 'npx';
|
|
17
26
|
// The `reticle` bin lives in the server package, so `npx <pkg> mcp` runs the bridge without the
|
|
18
27
|
// retired `core` umbrella. Sourced from the single derived identity so it can never drift.
|
|
19
28
|
const RETICLE_PACKAGE = RETICLE_NPM_PACKAGE;
|
|
20
29
|
const MCP_SUBCOMMAND = 'mcp';
|
|
21
30
|
const CLAUDE_CLI = 'claude';
|
|
22
|
-
/**
|
|
23
|
-
*
|
|
31
|
+
/**
|
|
32
|
+
* Args after `npx` that launch the bridge: `@reticlehq/server mcp`. Portless — the port comes from
|
|
33
|
+
* the project's `.reticle.json` at runtime, so one global entry works for every project.
|
|
34
|
+
*
|
|
35
|
+
* DELIBERATELY UNPINNED, and the trade is worth stating. `reticle init` pins the SDK to the CLI's
|
|
36
|
+
* exact version, so on release day the app can be on the new one while npx serves a cached older
|
|
37
|
+
* build — a real skew, reported from the field. Pinning this entry would remove that window.
|
|
38
|
+
*
|
|
39
|
+
* It would also freeze the agent's MCP server at whatever version was installed the day `init` ran,
|
|
40
|
+
* for as long as that entry survives — and `reticle update` upgrades the CLI, not a global agent
|
|
41
|
+
* config. Reticle's biggest measured problem is fixes not reaching people (2.4.0 reached zero users
|
|
42
|
+
* before its nudge existed), and a permanent pin makes that worse for every install, to close a
|
|
43
|
+
* window that lasts until the next npx cache miss.
|
|
44
|
+
*
|
|
45
|
+
* So it stays unpinned, and the skew is handled where it actually shows up: the contract fingerprint
|
|
46
|
+
* makes a real mismatch loud on the next tool result (see version-skew), and a stale entry of our own
|
|
47
|
+
* shape is now repaired on re-run rather than reported "already registered" (see cursor.ts).
|
|
48
|
+
*/
|
|
24
49
|
export function npxServerArgs() {
|
|
25
50
|
return [RETICLE_PACKAGE, MCP_SUBCOMMAND];
|
|
26
51
|
}
|
|
@@ -36,6 +61,11 @@ export function claudeAddCommand() {
|
|
|
36
61
|
}
|
|
37
62
|
/** Probe args that tell us whether an `reticle` server already exists in any scope (exit 0 = exists). */
|
|
38
63
|
export function claudeExistsProbe() {
|
|
64
|
+
// NO `-s`: `claude mcp get` takes no options at all, so passing one exits 1 with "unknown option
|
|
65
|
+
// '-s'" — the probe answered "not registered" on EVERY machine, init then ran `claude mcp add`,
|
|
66
|
+
// which exits 1 with "already exists", and a re-run reported `[⚠] step failed` plus a manual
|
|
67
|
+
// command that fails the same way. A false positive from a project-scoped entry costs one skipped
|
|
68
|
+
// registration; the flag cost every re-run a failed step.
|
|
39
69
|
return { command: CLAUDE_CLI, args: [MCP_SUBCOMMAND, 'get', MCP_SERVER_NAME] };
|
|
40
70
|
}
|
|
41
71
|
/** Probe args for whether the `claude` CLI is installed at all. */
|
|
@@ -51,5 +81,11 @@ export function mcpManual() {
|
|
|
51
81
|
|
|
52
82
|
Or, for another agent, add this to its global MCP config (e.g. Cursor's ~/.cursor/mcp.json):
|
|
53
83
|
|
|
54
|
-
"${MCP_SERVER_NAME}": { "command": "${NPX}", "args": ${JSON.stringify(serverInvocation().slice(1))} }
|
|
84
|
+
"${MCP_SERVER_NAME}": { "command": "${NPX}", "args": ${JSON.stringify(serverInvocation().slice(1))} }
|
|
85
|
+
|
|
86
|
+
On Windows, if the agent cannot start Reticle because npx is blocked ("running scripts is disabled
|
|
87
|
+
on this system" — a PowerShell execution policy), register it through cmd instead, which that policy
|
|
88
|
+
does not gate:
|
|
89
|
+
|
|
90
|
+
"${MCP_SERVER_NAME}": { "command": "cmd", "args": ${JSON.stringify(['/c', NPX, ...serverInvocation().slice(1)])} }`;
|
|
55
91
|
}
|
|
@@ -6,11 +6,8 @@
|
|
|
6
6
|
* skipped, and one of them is JSX.
|
|
7
7
|
*/
|
|
8
8
|
import { type SourcePatch } from './patch-kind.js';
|
|
9
|
-
export declare const NEXT_CONFIG_IMPORT = "import { withReticle } from '@reticlehq/next';";
|
|
10
|
-
export declare const NEXT_CONFIG_REQUIRE = "const { withReticle } = require('@reticlehq/next');";
|
|
11
|
-
export declare const RETICLE_DEV_IMPORT: string;
|
|
12
9
|
/** Where the generated connect component goes, and how the mount file should import it. */
|
|
13
|
-
|
|
10
|
+
interface ReticleDevLocation {
|
|
14
11
|
/** Project-relative path to write. */
|
|
15
12
|
path: string;
|
|
16
13
|
/** What the mount file (`app/layout.*` or `pages/_app.*`) should import. */
|
|
@@ -39,3 +36,4 @@ export declare function patchNextConfig(source: string): SourcePatch;
|
|
|
39
36
|
*/
|
|
40
37
|
export declare function patchPagesApp(source: string, importSpecifier?: string): SourcePatch;
|
|
41
38
|
export declare function patchRootLayout(source: string): SourcePatch;
|
|
39
|
+
export {};
|
package/dist/init/next-patch.js
CHANGED
|
@@ -7,14 +7,14 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { PatchKind } from './patch-kind.js';
|
|
9
9
|
const RETICLE_NEXT_PACKAGE = '@reticlehq/next';
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
const NEXT_CONFIG_IMPORT = `import { withReticle } from '${RETICLE_NEXT_PACKAGE}';`;
|
|
11
|
+
const NEXT_CONFIG_REQUIRE = `const { withReticle } = require('${RETICLE_NEXT_PACKAGE}');`;
|
|
12
12
|
const RETICLE_DEV_COMPONENT = 'ReticleDev';
|
|
13
13
|
const RETICLE_DEV_BASENAME = 'reticle-dev';
|
|
14
14
|
/** Default import specifier: the component sits beside the file importing it (App Router). */
|
|
15
15
|
const RETICLE_DEV_SIBLING = `./${RETICLE_DEV_BASENAME}`;
|
|
16
16
|
const reticleDevImport = (specifier) => `import { ${RETICLE_DEV_COMPONENT} } from '${specifier}';`;
|
|
17
|
-
|
|
17
|
+
const RETICLE_DEV_IMPORT = reticleDevImport(RETICLE_DEV_SIBLING);
|
|
18
18
|
/**
|
|
19
19
|
* Decide where the connect component lives, from the file that will mount it.
|
|
20
20
|
*
|
|
@@ -31,11 +31,11 @@ export const RETICLE_DEV_IMPORT = reticleDevImport(RETICLE_DEV_SIBLING);
|
|
|
31
31
|
export function reticleDevLocation(mountPath, typescript) {
|
|
32
32
|
const ext = typescript ? '.tsx' : '.jsx';
|
|
33
33
|
const slash = mountPath.lastIndexOf('/');
|
|
34
|
-
const dir =
|
|
34
|
+
const dir = -1 === slash ? '' : mountPath.slice(0, slash);
|
|
35
35
|
const isPagesRouter = /(^|\/)pages$/.test(dir);
|
|
36
36
|
if (!isPagesRouter) {
|
|
37
37
|
return {
|
|
38
|
-
path: `${
|
|
38
|
+
path: `${'' === dir ? '' : `${dir}/`}${RETICLE_DEV_BASENAME}${ext}`,
|
|
39
39
|
importSpecifier: RETICLE_DEV_SIBLING,
|
|
40
40
|
};
|
|
41
41
|
}
|
|
@@ -48,26 +48,116 @@ export function reticleDevLocation(mountPath, typescript) {
|
|
|
48
48
|
}
|
|
49
49
|
/** The dev-guarded mount. Production strips it — `process.env.NODE_ENV` is inlined at build time. */
|
|
50
50
|
const RETICLE_DEV_MOUNT = `{process.env.NODE_ENV === 'development' ? <${RETICLE_DEV_COMPONENT} /> : null}`;
|
|
51
|
-
/**
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
51
|
+
/**
|
|
52
|
+
* `export default ` / `module.exports = ` at the START of a line — the head of the assignment, not
|
|
53
|
+
* the whole statement. Where the exported expression ENDS is decided by `expressionEnd` below.
|
|
54
|
+
*
|
|
55
|
+
* These used to be `/…([\s\S]+?);?\s*$/`, which reads as "the expression, lazily". It is not: with
|
|
56
|
+
* no `m` flag the `$` is end-of-FILE and the trailing `;?\s*` is satisfiable only there, so the
|
|
57
|
+
* capture always ran to the last non-blank character of the file. Every config whose export was not
|
|
58
|
+
* the final statement got everything after it swallowed into the wrap. On a config exporting
|
|
59
|
+
* conditionally that produced an unbalanced paren — a syntax error, so `next dev` exited 1 while
|
|
60
|
+
* `init` reported the step as ✓ and the only symptom was "dev server never served".
|
|
61
|
+
*/
|
|
62
|
+
const ESM_DEFAULT_HEAD = /^[ \t]*export[ \t]+default[ \t]+/gm;
|
|
63
|
+
const CJS_EXPORT_HEAD = /^[ \t]*module\.exports[ \t]*=[ \t]*/gm;
|
|
55
64
|
/** The opening `<body ...>` tag, whose first child is where the mount goes. */
|
|
56
65
|
const BODY_OPEN_TAG = /<body(\s[^>]*)?>/g;
|
|
57
66
|
const NO_EXPORT_REASON = "couldn't find an `export default` or `module.exports` to wrap";
|
|
58
67
|
const NO_BODY_REASON = "couldn't find a single <body> tag to mount <ReticleDev /> inside";
|
|
68
|
+
/** Bracket pairs that keep an expression open. A `;` at depth 0 is what ends it. */
|
|
69
|
+
const OPENERS = '([{';
|
|
70
|
+
const CLOSERS = ')]}';
|
|
71
|
+
const LINE_COMMENT = '//';
|
|
72
|
+
const BLOCK_COMMENT_OPEN = '/*';
|
|
73
|
+
const BLOCK_COMMENT_CLOSE = '*/';
|
|
74
|
+
const QUOTES = '\'"`';
|
|
75
|
+
/**
|
|
76
|
+
* Index just past the exported expression that starts at `from` — the `;` that closes it, or end of
|
|
77
|
+
* file. Tracks bracket depth so a multi-line object or a nested call ends in the right place, and
|
|
78
|
+
* skips strings and comments so a `;` or a brace inside one cannot end it early.
|
|
79
|
+
*
|
|
80
|
+
* Deliberately a scanner and not a parser: `next.config.*` is arbitrary JS and this file's contract
|
|
81
|
+
* is to recognise the obvious shape or bail. It only has to be right about where ONE expression
|
|
82
|
+
* stops, and wrong is caught by the caller bailing to manual, never by a half-edit.
|
|
83
|
+
*/
|
|
84
|
+
function expressionEnd(source, from) {
|
|
85
|
+
let depth = 0;
|
|
86
|
+
for (let i = from; i < source.length; i++) {
|
|
87
|
+
const ch = source[i] ?? '';
|
|
88
|
+
const pair = source.slice(i, i + 2);
|
|
89
|
+
if (pair === LINE_COMMENT) {
|
|
90
|
+
const nl = source.indexOf('\n', i);
|
|
91
|
+
if (-1 === nl)
|
|
92
|
+
return source.length;
|
|
93
|
+
i = nl;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (pair === BLOCK_COMMENT_OPEN) {
|
|
97
|
+
const close = source.indexOf(BLOCK_COMMENT_CLOSE, i + 2);
|
|
98
|
+
i = -1 === close ? source.length : close + 1;
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
if (QUOTES.includes(ch)) {
|
|
102
|
+
for (i++; i < source.length; i++) {
|
|
103
|
+
if ('\\' === source[i])
|
|
104
|
+
i++;
|
|
105
|
+
else if (source[i] === ch)
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (OPENERS.includes(ch))
|
|
111
|
+
depth++;
|
|
112
|
+
else if (CLOSERS.includes(ch)) {
|
|
113
|
+
// A closer at depth 0 belongs to an enclosing block, so the expression ended before it.
|
|
114
|
+
if (0 === depth)
|
|
115
|
+
return i;
|
|
116
|
+
depth--;
|
|
117
|
+
}
|
|
118
|
+
else if (';' === ch && 0 === depth)
|
|
119
|
+
return i;
|
|
120
|
+
}
|
|
121
|
+
return source.length;
|
|
122
|
+
}
|
|
123
|
+
/** Every top-level export assignment in the file, in source order. */
|
|
124
|
+
function exportSites(source, head) {
|
|
125
|
+
const sites = [];
|
|
126
|
+
for (const match of source.matchAll(head)) {
|
|
127
|
+
if (match.index === undefined)
|
|
128
|
+
continue;
|
|
129
|
+
const start = match.index + match[0].length;
|
|
130
|
+
sites.push({ start, end: expressionEnd(source, start) });
|
|
131
|
+
}
|
|
132
|
+
return sites;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Wrap every listed expression in `withReticle(...)`, leaving every other byte of the file alone.
|
|
136
|
+
*
|
|
137
|
+
* All of them, not just the first: a config can export CONDITIONALLY — Sentry-wrapped in one branch,
|
|
138
|
+
* plain in the other — and which branch runs is an environment variable's business, not ours.
|
|
139
|
+
* Wrapping each assignment is correct whichever one executes, and it is what turns that shape from a
|
|
140
|
+
* manual step (an app that boots and never connects) into a working install.
|
|
141
|
+
*
|
|
142
|
+
* Applied back-to-front so each splice leaves the earlier offsets valid.
|
|
143
|
+
*/
|
|
144
|
+
function wrapAll(source, sites) {
|
|
145
|
+
let out = source;
|
|
146
|
+
for (const site of [...sites].sort((a, b) => b.start - a.start)) {
|
|
147
|
+
out = `${out.slice(0, site.start)}withReticle(${out.slice(site.start, site.end)})${out.slice(site.end)}`;
|
|
148
|
+
}
|
|
149
|
+
return out;
|
|
150
|
+
}
|
|
59
151
|
export function patchNextConfig(source) {
|
|
60
152
|
if (source.includes(RETICLE_NEXT_PACKAGE))
|
|
61
153
|
return { kind: PatchKind.ALREADY };
|
|
62
|
-
const esm =
|
|
63
|
-
if (esm
|
|
64
|
-
|
|
65
|
-
return { kind: PatchKind.APPLY, code: `${NEXT_CONFIG_IMPORT}\n${wrapped}` };
|
|
154
|
+
const esm = exportSites(source, ESM_DEFAULT_HEAD);
|
|
155
|
+
if (esm.length > 0) {
|
|
156
|
+
return { kind: PatchKind.APPLY, code: `${NEXT_CONFIG_IMPORT}\n${wrapAll(source, esm)}` };
|
|
66
157
|
}
|
|
67
|
-
const cjs =
|
|
68
|
-
if (cjs
|
|
69
|
-
|
|
70
|
-
return { kind: PatchKind.APPLY, code: `${NEXT_CONFIG_REQUIRE}\n${wrapped}` };
|
|
158
|
+
const cjs = exportSites(source, CJS_EXPORT_HEAD);
|
|
159
|
+
if (cjs.length > 0) {
|
|
160
|
+
return { kind: PatchKind.APPLY, code: `${NEXT_CONFIG_REQUIRE}\n${wrapAll(source, cjs)}` };
|
|
71
161
|
}
|
|
72
162
|
return { kind: PatchKind.MANUAL, reason: NO_EXPORT_REASON };
|
|
73
163
|
}
|
|
@@ -84,7 +174,7 @@ export function patchPagesApp(source, importSpecifier = RETICLE_DEV_SIBLING) {
|
|
|
84
174
|
if (source.includes(RETICLE_DEV_COMPONENT))
|
|
85
175
|
return { kind: PatchKind.ALREADY };
|
|
86
176
|
const matches = [...source.matchAll(PAGES_APP_COMPONENT)];
|
|
87
|
-
const match = matches.length
|
|
177
|
+
const match = 1 === matches.length ? matches[0] : undefined;
|
|
88
178
|
if (match?.index === undefined)
|
|
89
179
|
return { kind: PatchKind.MANUAL, reason: NO_PAGES_COMPONENT_REASON };
|
|
90
180
|
const wrapped = `<>${RETICLE_DEV_MOUNT}${match[0]}</>`;
|
|
@@ -97,7 +187,7 @@ export function patchRootLayout(source) {
|
|
|
97
187
|
const tags = [...source.matchAll(BODY_OPEN_TAG)];
|
|
98
188
|
// Exactly one <body> or we cannot tell which one actually renders — and mounting into the wrong
|
|
99
189
|
// one is the same silent no-connect as not mounting at all.
|
|
100
|
-
const tag = tags.length
|
|
190
|
+
const tag = 1 === tags.length ? tags[0] : undefined;
|
|
101
191
|
if (tag?.index === undefined)
|
|
102
192
|
return { kind: PatchKind.MANUAL, reason: NO_BODY_REASON };
|
|
103
193
|
const insertAt = tag.index + tag[0].length;
|