@reticlehq/server 2.3.0 → 2.5.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 +702 -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} +1 -1
- package/dist/{bridge.js → bridge/bridge.js} +105 -15
- package/dist/{bridge.test-harness.d.ts → bridge/bridge.test-harness.d.ts} +6 -11
- package/dist/{bridge.test-harness.js → bridge/bridge.test-harness.js} +21 -9
- 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-doctor.js → cli/cli-doctor.js} +9 -2
- 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.d.ts +143 -0
- package/dist/{cli-parse.js → cli/cli-parse.js} +146 -65
- package/dist/{cli-port.d.ts → cli/cli-port.d.ts} +15 -0
- package/dist/{cli-port.js → cli/cli-port.js} +29 -8
- 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.d.ts +2 -2
- package/dist/cli.js +182 -93
- 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 +49 -0
- package/dist/daemon/daemon-resilience.js +191 -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} +80 -48
- 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} +13 -0
- package/dist/{idle-shutdown.js → daemon/idle-shutdown.js} +27 -4
- 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 +43 -20
- package/dist/events/predicate-eval.js +159 -55
- package/dist/events/predicate.d.ts +2 -2
- package/dist/events/predicate.js +112 -35
- package/dist/events/reconcile.js +5 -5
- package/dist/events/ring-buffer.js +1 -1
- 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.d.ts +0 -6
- package/dist/flows/decision.js +64 -8
- 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 +5 -7
- package/dist/flows/flow-replay.js +114 -47
- 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 +157 -6
- package/dist/flows/flow-success.js +20 -15
- package/dist/flows/flow-tools.js +67 -13
- 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/server-verify.d.ts +1 -1
- package/dist/flows/server-verify.js +5 -5
- 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 +16 -3
- package/dist/honesty/blind-spots.js +24 -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 +22 -3
- package/dist/honesty/verified.js +48 -8
- package/dist/http-server.js +14 -7
- package/dist/hunt/hunt-report.d.ts +2 -1
- package/dist/hunt/hunt-report.js +2 -2
- package/dist/index.d.ts +8 -6
- package/dist/index.js +32 -25
- package/dist/init/agent-rules.d.ts +2 -1
- package/dist/init/agent-rules.js +45 -7
- 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.d.ts +17 -0
- package/dist/init/capabilities.js +54 -0
- package/dist/init/cra.d.ts +37 -0
- package/dist/init/cra.js +90 -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 +26 -0
- package/dist/init/detect.js +78 -5
- 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 +39 -0
- package/dist/init/next-patch.js +196 -0
- package/dist/init/node-io.js +48 -6
- package/dist/init/patch-kind.d.ts +20 -0
- package/dist/init/patch-kind.js +10 -0
- package/dist/init/plan-framework.d.ts +50 -0
- package/dist/init/plan-framework.js +341 -0
- package/dist/init/plan.d.ts +119 -6
- package/dist/init/plan.js +230 -131
- package/dist/init/project-id.d.ts +5 -9
- package/dist/init/project-id.js +10 -22
- package/dist/init/run.d.ts +22 -0
- package/dist/init/run.js +393 -28
- package/dist/init/slash-command.d.ts +27 -0
- package/dist/init/slash-command.js +61 -0
- package/dist/init/snippets.d.ts +41 -3
- package/dist/init/snippets.js +185 -10
- package/dist/init/vite-config.d.ts +4 -10
- package/dist/init/vite-config.js +15 -7
- package/dist/init/workspace-apps.d.ts +23 -0
- package/dist/init/workspace-apps.js +68 -0
- package/dist/input/driven-redaction.d.ts +15 -0
- package/dist/input/driven-redaction.js +76 -0
- package/dist/input/network-detail.d.ts +9 -3
- package/dist/input/network-detail.js +43 -23
- 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 +31 -0
- package/dist/mcp/mcp-outage.js +48 -0
- package/dist/mcp/mcp-proxy.d.ts +124 -0
- package/dist/mcp/mcp-proxy.js +697 -0
- package/dist/{mcp.d.ts → mcp/mcp.d.ts} +13 -10
- package/dist/{mcp.js → mcp/mcp.js} +110 -35
- 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/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/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 +38 -0
- package/dist/session/no-session-diagnosis.js +70 -0
- package/dist/session/no-session-watch.d.ts +19 -0
- package/dist/session/no-session-watch.js +61 -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/session-health.d.ts +8 -1
- package/dist/session/session-health.js +8 -7
- package/dist/session/session-info.d.ts +26 -7
- package/dist/session/session-info.js +42 -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 +3 -0
- package/dist/session/session-manager.js +58 -9
- 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 +20 -2
- package/dist/session/session.js +76 -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/daemon-telemetry.d.ts +19 -0
- package/dist/telemetry/daemon-telemetry.js +12 -2
- package/dist/telemetry/dev-repo.js +4 -4
- package/dist/telemetry/error-fingerprint.js +21 -2
- package/dist/telemetry/feedback-cli.d.ts +1 -1
- package/dist/telemetry/feedback-cli.js +4 -4
- package/dist/telemetry/feedback-context.d.ts +22 -0
- package/dist/telemetry/feedback-context.js +41 -1
- 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.js +1 -1
- package/dist/telemetry/session-metrics.d.ts +2 -0
- package/dist/telemetry/session-metrics.js +68 -6
- package/dist/telemetry/telemetry.d.ts +27 -2
- package/dist/telemetry/telemetry.js +119 -29
- 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 +84 -115
- 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 +3 -1
- package/dist/tools/assert-verdict.js +8 -5
- package/dist/tools/browser-tools.js +30 -11
- package/dist/tools/contract-tools.js +1 -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 +90 -18
- package/dist/tools/error-recovery.d.ts +20 -2
- package/dist/tools/error-recovery.js +179 -4
- 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 -6
- package/dist/tools/invoke-tool.js +57 -32
- 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-result.d.ts +17 -0
- package/dist/tools/navigate-result.js +31 -0
- package/dist/tools/observe-tools.js +54 -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 +85 -20
- package/dist/tools/real-input-attempt.d.ts +28 -0
- package/dist/tools/real-input-attempt.js +88 -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 +8 -0
- package/dist/tools/tool-surface.d.ts +83 -0
- package/dist/tools/tool-surface.js +204 -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/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.md +28 -0
- package/docs/flows.md +166 -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/mint.json +61 -0
- package/docs/multi-agent-testing.md +55 -0
- package/docs/platform-integration.md +122 -0
- package/docs/telemetry-contract.md +214 -0
- package/docs/telemetry.md +184 -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-launch.js +0 -112
- package/dist/cli-parse.d.ts +0 -140
- package/dist/daemon-resilience.d.ts +0 -22
- package/dist/daemon-resilience.js +0 -85
- package/dist/mcp-proxy.d.ts +0 -35
- package/dist/mcp-proxy.js +0 -190
- 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-doctor.d.ts → cli/cli-doctor.d.ts} +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,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the app can be driven by: its `data-testid` values, and which state library holds its truth.
|
|
3
|
+
*
|
|
4
|
+
* `init` wired none of this, so every app came up with `hasCapabilities: false`, empty capabilities,
|
|
5
|
+
* and a `reticle_state` holding nothing but `__reticle_renders` — the state-truth read that SKILL.md
|
|
6
|
+
* calls the highest-value line was unavailable on every app out of the box. Measured across six.
|
|
7
|
+
*
|
|
8
|
+
* Testids are scanned rather than asked for. Stores are NAMED but not wired: detecting that an app
|
|
9
|
+
* depends on zustand is easy, knowing which module exports the store instance is not, and generating
|
|
10
|
+
* a wrong import would break the dev module that everything else now hangs off.
|
|
11
|
+
*/
|
|
12
|
+
/** `data-testid="foo"` / `data-testid='foo'` / `data-testid={"foo"}` — the forms people actually write. */
|
|
13
|
+
const TESTID_ATTR = /data-testid\s*=\s*[{]?\s*["'`]([^"'`]+)["'`]/g;
|
|
14
|
+
/** Cap the generated list. A capabilities block is a hint for an agent, not an inventory. */
|
|
15
|
+
export const MAX_TESTIDS = 60;
|
|
16
|
+
/** Every distinct `data-testid` literal in the given sources, in first-seen order. */
|
|
17
|
+
export function scanTestids(sources) {
|
|
18
|
+
const found = new Set();
|
|
19
|
+
for (const src of sources) {
|
|
20
|
+
for (const m of src.matchAll(TESTID_ATTR)) {
|
|
21
|
+
const id = m[1];
|
|
22
|
+
if (id !== undefined && id.length > 0)
|
|
23
|
+
found.add(id);
|
|
24
|
+
if (found.size >= MAX_TESTIDS)
|
|
25
|
+
return [...found];
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return [...found];
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* State libraries the browser SDK has an adapter for, in the order we would recommend registering
|
|
32
|
+
* them. TanStack Query is first on purpose: a stale cache served as fresh fires NO network request,
|
|
33
|
+
* so the network log shows silence and the DOM shows a plausible number — the cache is the only
|
|
34
|
+
* witness, which makes it the highest-value store in the list and the least obvious one to add.
|
|
35
|
+
*/
|
|
36
|
+
const STORE_LIBRARIES = [
|
|
37
|
+
['@tanstack/react-query', "registerStore('queries', tanstackQueryStore(queryClient))"],
|
|
38
|
+
[
|
|
39
|
+
'zustand',
|
|
40
|
+
"registerStore('app', useStore) // pass the store itself, not () => store.getState()",
|
|
41
|
+
],
|
|
42
|
+
['@reduxjs/toolkit', "registerStore('app', store)"],
|
|
43
|
+
['redux', "registerStore('app', store)"],
|
|
44
|
+
['jotai', "registerStore('app', jotaiStore(getDefaultStore(), { cart, user }))"],
|
|
45
|
+
['valtio', "registerStore('app', valtioStore(state))"],
|
|
46
|
+
['mobx', "registerStore('app', mobxStore(state))"],
|
|
47
|
+
['xstate', "registerStore('machine', xstateStore(actor))"],
|
|
48
|
+
['pinia', "registerStore('cart', piniaStore(useCartStore()))"],
|
|
49
|
+
['svelte', "registerStore('cart', svelteStore(cartStore))"],
|
|
50
|
+
];
|
|
51
|
+
/** The store libraries this app depends on, as ready-to-uncomment registration lines. */
|
|
52
|
+
export function storeHints(deps) {
|
|
53
|
+
return STORE_LIBRARIES.filter(([dep]) => deps.has(dep)).map(([, hint]) => hint);
|
|
54
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
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
|
+
/** Add the import after the last existing one, or null when it is already present. */
|
|
33
|
+
export declare function craImportPatch(source: string): string | null;
|
|
34
|
+
/** Set the token variable, or null when nothing needs to change. */
|
|
35
|
+
export declare function craEnvPatch(existing: string | null, token: string): string | null;
|
|
36
|
+
/** The dev-only connect module imported from `src/index.tsx`. */
|
|
37
|
+
export declare function craDevModuleFile(port: number | undefined, projectId?: string): string;
|
package/dist/init/cra.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
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
|
+
/** Add the import after the last existing one, or null when it is already present. */
|
|
35
|
+
export function craImportPatch(source) {
|
|
36
|
+
if (source.includes(CRA_DEV_MODULE_IMPORT))
|
|
37
|
+
return null;
|
|
38
|
+
const lines = source.split('\n');
|
|
39
|
+
// After the LAST import: React's own imports must still run first, and a side-effect import placed
|
|
40
|
+
// above them would connect before the app exists.
|
|
41
|
+
let lastImport = -1;
|
|
42
|
+
for (const [index, line] of lines.entries()) {
|
|
43
|
+
if (/^\s*import\s/.test(line))
|
|
44
|
+
lastImport = index;
|
|
45
|
+
}
|
|
46
|
+
lines.splice(lastImport + 1, 0, CRA_DEV_MODULE_IMPORT);
|
|
47
|
+
return lines.join('\n');
|
|
48
|
+
}
|
|
49
|
+
/** Set the token variable, or null when nothing needs to change. */
|
|
50
|
+
export function craEnvPatch(existing, token) {
|
|
51
|
+
if ('' === token)
|
|
52
|
+
return null;
|
|
53
|
+
const line = `${TOKEN_VAR}=${token}`;
|
|
54
|
+
if (null === existing || '' === existing.trim())
|
|
55
|
+
return `${line}\n`;
|
|
56
|
+
if (existing.includes(line))
|
|
57
|
+
return null;
|
|
58
|
+
// Replace rather than append: two assignments of one variable is a silent coin flip on which wins.
|
|
59
|
+
if (new RegExp(`^${TOKEN_VAR}=`, 'm').test(existing)) {
|
|
60
|
+
return existing.replace(new RegExp(`^${TOKEN_VAR}=.*$`, 'm'), line);
|
|
61
|
+
}
|
|
62
|
+
return `${existing.endsWith('\n') ? existing : `${existing}\n`}${line}\n`;
|
|
63
|
+
}
|
|
64
|
+
/** The dev-only connect module imported from `src/index.tsx`. */
|
|
65
|
+
export function craDevModuleFile(port, projectId) {
|
|
66
|
+
const fields = [];
|
|
67
|
+
if (port !== undefined)
|
|
68
|
+
fields.push(`url: 'ws://127.0.0.1:${String(port)}/reticle'`);
|
|
69
|
+
if (projectId !== undefined && projectId.length > 0)
|
|
70
|
+
fields.push(`projectId: '${projectId}'`);
|
|
71
|
+
const inline = fields.length > 0 ? `${fields.join(', ')}, ` : '';
|
|
72
|
+
return `// Dev-only: connect Reticle. Imported for its side effect from src/index.tsx.
|
|
73
|
+
//
|
|
74
|
+
// CRA's public/index.html is a static template the bundler never processes for modules, so the
|
|
75
|
+
// connect cannot live there. The pairing token arrives through REACT_APP_RETICLE_TOKEN because
|
|
76
|
+
// REACT_APP_* is the only thing CRA inlines into browser code.
|
|
77
|
+
if (process.env.NODE_ENV === 'development') {
|
|
78
|
+
void import('@reticlehq/react').then(({ reticle, install }) => {
|
|
79
|
+
install();
|
|
80
|
+
const token = process.env.${TOKEN_VAR} ?? '';
|
|
81
|
+
// Loud on purpose: without this the only symptom is the bridge's generic auth failure.
|
|
82
|
+
if (token.length === 0) console.error(${JSON.stringify(`[reticle] ${CRA_TOKEN_MISSING_NOTE}`)});
|
|
83
|
+
// Still attempt it — a bridge running without a token pairs fine.
|
|
84
|
+
reticle.connect({ ${inline}...(token.length > 0 ? { token } : {}) });
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export {};
|
|
89
|
+
`;
|
|
90
|
+
}
|
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
|
@@ -6,6 +6,9 @@ export declare const Framework: {
|
|
|
6
6
|
readonly NEXT: "next";
|
|
7
7
|
readonly VITE: "vite";
|
|
8
8
|
readonly SVELTEKIT: "sveltekit";
|
|
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";
|
|
9
12
|
readonly HTML: "html";
|
|
10
13
|
};
|
|
11
14
|
export type Framework = (typeof Framework)[keyof typeof Framework];
|
|
@@ -27,9 +30,31 @@ export interface DetectInput {
|
|
|
27
30
|
configFiles: ReadonlySet<string>;
|
|
28
31
|
/** Lockfile basenames present in the project root. */
|
|
29
32
|
lockfiles: ReadonlySet<string>;
|
|
33
|
+
/** Marker basenames inside `node_modules` (`.modules.yaml`, `.yarn-state.yml`, …), if installed. */
|
|
34
|
+
nodeModulesMarkers?: ReadonlySet<string>;
|
|
30
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* The UI library the app renders with. Detection used to key on "vite is in package.json" and stop
|
|
38
|
+
* there, so a Vue or Preact app got `@reticlehq/react` installed and an all-green report with no
|
|
39
|
+
* mention that the React adapter has nothing to attach to.
|
|
40
|
+
*/
|
|
41
|
+
export declare const UiLibrary: {
|
|
42
|
+
readonly REACT: "react";
|
|
43
|
+
readonly PREACT: "preact";
|
|
44
|
+
readonly VUE: "vue";
|
|
45
|
+
readonly SVELTE: "svelte";
|
|
46
|
+
readonly UNKNOWN: "unknown";
|
|
47
|
+
};
|
|
48
|
+
export type UiLibrary = (typeof UiLibrary)[keyof typeof UiLibrary];
|
|
31
49
|
export interface Detection {
|
|
32
50
|
framework: Framework;
|
|
51
|
+
uiLibrary: UiLibrary;
|
|
52
|
+
/**
|
|
53
|
+
* Whether the project is TypeScript. Not cosmetic: generating a `.tsx` file into a JavaScript
|
|
54
|
+
* project makes Next auto-install TypeScript on the next `next dev`, which on Next 13 takes its
|
|
55
|
+
* require-hook down with it and the dev server never starts.
|
|
56
|
+
*/
|
|
57
|
+
typescript: boolean;
|
|
33
58
|
reactMajor: number | undefined;
|
|
34
59
|
/** React 19 dropped _debugSource, so it needs the build-time source-map stamp. */
|
|
35
60
|
needsSourceMapping: boolean;
|
|
@@ -37,6 +62,7 @@ export interface Detection {
|
|
|
37
62
|
}
|
|
38
63
|
/** Extract the leading major version from a semver range like "^19.0.0" or "19.1.1". */
|
|
39
64
|
export declare function parseMajor(range: string | undefined): number | undefined;
|
|
65
|
+
export declare function detectPackageManager(lockfiles: ReadonlySet<string>, nodeModulesMarkers: ReadonlySet<string>): PackageManager;
|
|
40
66
|
export declare function detect(input: DetectInput): Detection;
|
|
41
67
|
interface InstallCommand {
|
|
42
68
|
command: string;
|
package/dist/init/detect.js
CHANGED
|
@@ -6,6 +6,9 @@ export const Framework = {
|
|
|
6
6
|
NEXT: 'next',
|
|
7
7
|
VITE: 'vite',
|
|
8
8
|
SVELTEKIT: 'sveltekit',
|
|
9
|
+
ASTRO: 'astro',
|
|
10
|
+
/** Create React App. No config file exists, so `react-scripts` in the dependencies is the signal. */
|
|
11
|
+
CRA: 'cra',
|
|
9
12
|
HTML: 'html',
|
|
10
13
|
};
|
|
11
14
|
export const PackageManager = {
|
|
@@ -14,9 +17,27 @@ export const PackageManager = {
|
|
|
14
17
|
BUN: 'bun',
|
|
15
18
|
NPM: 'npm',
|
|
16
19
|
};
|
|
20
|
+
/**
|
|
21
|
+
* The UI library the app renders with. Detection used to key on "vite is in package.json" and stop
|
|
22
|
+
* there, so a Vue or Preact app got `@reticlehq/react` installed and an all-green report with no
|
|
23
|
+
* mention that the React adapter has nothing to attach to.
|
|
24
|
+
*/
|
|
25
|
+
export const UiLibrary = {
|
|
26
|
+
REACT: 'react',
|
|
27
|
+
PREACT: 'preact',
|
|
28
|
+
VUE: 'vue',
|
|
29
|
+
SVELTE: 'svelte',
|
|
30
|
+
UNKNOWN: 'unknown',
|
|
31
|
+
};
|
|
17
32
|
const NEXT_CONFIGS = ['next.config.js', 'next.config.mjs', 'next.config.ts', 'next.config.cjs'];
|
|
18
33
|
const VITE_CONFIGS = ['vite.config.js', 'vite.config.ts', 'vite.config.mjs', 'vite.config.mts'];
|
|
19
34
|
const SVELTE_CONFIGS = ['svelte.config.js', 'svelte.config.ts', 'svelte.config.mjs'];
|
|
35
|
+
const ASTRO_CONFIGS = [
|
|
36
|
+
'astro.config.mjs',
|
|
37
|
+
'astro.config.js',
|
|
38
|
+
'astro.config.ts',
|
|
39
|
+
'astro.config.cjs',
|
|
40
|
+
];
|
|
20
41
|
function depVersion(pkg, name) {
|
|
21
42
|
return pkg.dependencies?.[name] ?? pkg.devDependencies?.[name] ?? pkg.peerDependencies?.[name];
|
|
22
43
|
}
|
|
@@ -28,19 +49,41 @@ export function parseMajor(range) {
|
|
|
28
49
|
if (range === undefined)
|
|
29
50
|
return undefined;
|
|
30
51
|
const match = range.match(/(\d+)/);
|
|
31
|
-
if (
|
|
52
|
+
if (null === match || match[1] === undefined)
|
|
32
53
|
return undefined;
|
|
33
54
|
const major = parseInt(match[1], 10);
|
|
34
55
|
return isNaN(major) ? undefined : major;
|
|
35
56
|
}
|
|
36
|
-
|
|
57
|
+
/**
|
|
58
|
+
* Markers each package manager leaves INSIDE `node_modules`. An installed tree is the strongest
|
|
59
|
+
* evidence there is — stronger than a lockfile, which may simply not be committed.
|
|
60
|
+
*
|
|
61
|
+
* Without this, a pnpm-installed project with no committed lockfile was read as npm, and `npm i -D`
|
|
62
|
+
* then died on pnpm's symlink layout with `Cannot read properties of null (reading 'matches')`. Worse,
|
|
63
|
+
* it left the package present in `node_modules` but absent from `package.json`, so every later run
|
|
64
|
+
* reported the same failure — a setup that could not be retried into working.
|
|
65
|
+
*/
|
|
66
|
+
const NODE_MODULES_MARKERS = [
|
|
67
|
+
['.modules.yaml', PackageManager.PNPM],
|
|
68
|
+
['.yarn-state.yml', PackageManager.YARN],
|
|
69
|
+
['.package-lock.json', PackageManager.NPM],
|
|
70
|
+
];
|
|
71
|
+
/** Marker basenames present inside the project's `node_modules`, if it has one. */
|
|
72
|
+
function packageManagerFromNodeModules(markers) {
|
|
73
|
+
for (const [name, pm] of NODE_MODULES_MARKERS)
|
|
74
|
+
if (markers.has(name))
|
|
75
|
+
return pm;
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
export function detectPackageManager(lockfiles, nodeModulesMarkers) {
|
|
37
79
|
if (lockfiles.has('pnpm-lock.yaml'))
|
|
38
80
|
return PackageManager.PNPM;
|
|
39
81
|
if (lockfiles.has('yarn.lock'))
|
|
40
82
|
return PackageManager.YARN;
|
|
41
83
|
if (lockfiles.has('bun.lockb') || lockfiles.has('bun.lock'))
|
|
42
84
|
return PackageManager.BUN;
|
|
43
|
-
|
|
85
|
+
// No lockfile is not the same as "npm". An already-installed tree says which manager built it.
|
|
86
|
+
return packageManagerFromNodeModules(nodeModulesMarkers) ?? PackageManager.NPM;
|
|
44
87
|
}
|
|
45
88
|
function detectFramework(input) {
|
|
46
89
|
const { pkg, configFiles } = input;
|
|
@@ -52,18 +95,48 @@ function detectFramework(input) {
|
|
|
52
95
|
if (depVersion(pkg, '@sveltejs/kit') !== undefined || hasAnyConfig(configFiles, SVELTE_CONFIGS)) {
|
|
53
96
|
return Framework.SVELTEKIT;
|
|
54
97
|
}
|
|
98
|
+
// Astro is Vite-based but SSRs its own HTML, so the plugin's index.html injection never fires and
|
|
99
|
+
// `vite` is not a direct dependency — it used to fall all the way through to HTML and be handed
|
|
100
|
+
// connect instructions for a bundler it does not have. Check BEFORE the generic Vite branch.
|
|
101
|
+
if (depVersion(pkg, 'astro') !== undefined || hasAnyConfig(configFiles, ASTRO_CONFIGS)) {
|
|
102
|
+
return Framework.ASTRO;
|
|
103
|
+
}
|
|
55
104
|
if (depVersion(pkg, 'vite') !== undefined || hasAnyConfig(configFiles, VITE_CONFIGS)) {
|
|
56
105
|
return Framework.VITE;
|
|
57
106
|
}
|
|
107
|
+
// Checked after Vite, never before: a project migrating off CRA can carry both, and the Vite path
|
|
108
|
+
// is the one that works. CRA has no config file at all, so the dependency is the only signal.
|
|
109
|
+
if (depVersion(pkg, 'react-scripts') !== undefined)
|
|
110
|
+
return Framework.CRA;
|
|
58
111
|
return Framework.HTML;
|
|
59
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* React first: a Preact app using preact/compat aliases React, and Next/Remix apps list both. The
|
|
115
|
+
* order is the precedence — whichever the app actually renders through is the one it depends on
|
|
116
|
+
* directly.
|
|
117
|
+
*/
|
|
118
|
+
function detectUiLibrary(pkg) {
|
|
119
|
+
if (depVersion(pkg, 'react') !== undefined)
|
|
120
|
+
return UiLibrary.REACT;
|
|
121
|
+
if (depVersion(pkg, 'preact') !== undefined)
|
|
122
|
+
return UiLibrary.PREACT;
|
|
123
|
+
if (depVersion(pkg, 'vue') !== undefined)
|
|
124
|
+
return UiLibrary.VUE;
|
|
125
|
+
if (depVersion(pkg, 'svelte') !== undefined)
|
|
126
|
+
return UiLibrary.SVELTE;
|
|
127
|
+
return UiLibrary.UNKNOWN;
|
|
128
|
+
}
|
|
129
|
+
const TS_CONFIGS = ['tsconfig.json'];
|
|
60
130
|
export function detect(input) {
|
|
61
131
|
const reactMajor = parseMajor(depVersion(input.pkg, 'react'));
|
|
62
132
|
return {
|
|
63
133
|
framework: detectFramework(input),
|
|
134
|
+
uiLibrary: detectUiLibrary(input.pkg),
|
|
135
|
+
typescript: hasAnyConfig(input.configFiles, TS_CONFIGS) ||
|
|
136
|
+
depVersion(input.pkg, 'typescript') !== undefined,
|
|
64
137
|
reactMajor,
|
|
65
138
|
needsSourceMapping: reactMajor !== undefined && reactMajor >= 19,
|
|
66
|
-
packageManager: detectPackageManager(input.lockfiles),
|
|
139
|
+
packageManager: detectPackageManager(input.lockfiles, input.nodeModulesMarkers ?? new Set()),
|
|
67
140
|
};
|
|
68
141
|
}
|
|
69
142
|
const INSTALL_ARGS = {
|
|
@@ -74,7 +147,7 @@ const INSTALL_ARGS = {
|
|
|
74
147
|
};
|
|
75
148
|
/** Build a dev-dependency install command for one or more packages (e.g. the kit + its build plugin). */
|
|
76
149
|
export function installCommandParts(pm, pkgs) {
|
|
77
|
-
const list = typeof pkgs
|
|
150
|
+
const list = 'string' === typeof pkgs ? [pkgs] : pkgs;
|
|
78
151
|
return { command: pm, args: [...INSTALL_ARGS[pm], ...list] };
|
|
79
152
|
}
|
|
80
153
|
export function installCommand(pm, pkgs) {
|
|
@@ -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
|
}
|