@reticlehq/server 2.12.0 → 2.13.1
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 +67 -109
- package/dist/bridge/auth-failure-reason.d.ts +6 -2
- package/dist/bridge/auth-failure-reason.js +15 -8
- package/dist/bridge/bridge.d.ts +18 -1
- package/dist/bridge/bridge.js +247 -164
- package/dist/bridge/pairing-token.d.ts +5 -0
- package/dist/bridge/pairing-token.js +28 -0
- package/dist/bridge/protocol-skew.d.ts +37 -0
- package/dist/bridge/protocol-skew.js +45 -0
- package/dist/capsule/capsule-store.d.ts +6 -6
- package/dist/capsule/causal-summary.js +7 -2
- package/dist/cli/cli-doctor.js +31 -1
- package/dist/cli/cli-launch.d.ts +11 -1
- package/dist/cli/cli-launch.js +59 -10
- package/dist/cli/cli-parse.d.ts +15 -1
- package/dist/cli/cli-parse.js +212 -8
- package/dist/cli/cli-verify.d.ts +1 -0
- package/dist/cli/cli-verify.js +3 -2
- package/dist/cli/cloud-cli.js +428 -259
- package/dist/cli/cloud-keystore.d.ts +37 -0
- package/dist/cli/cloud-keystore.js +97 -0
- package/dist/cli/cloud-kit.d.ts +37 -0
- package/dist/cli/cloud-kit.js +121 -0
- package/dist/cli/cloud-login.d.ts +17 -0
- package/dist/cli/cloud-login.js +183 -0
- package/dist/cli/cloud-session.d.ts +48 -0
- package/dist/cli/cloud-session.js +53 -0
- package/dist/cli/config-discovery.js +1 -0
- package/dist/cli/daemon-start-options.d.ts +32 -0
- package/dist/cli/daemon-start-options.js +38 -0
- package/dist/cli/device-flow.d.ts +76 -0
- package/dist/cli/device-flow.js +56 -0
- package/dist/cli/doctor-rows.d.ts +9 -0
- package/dist/cli/doctor-rows.js +9 -0
- package/dist/cli/drive-attach.d.ts +13 -0
- package/dist/cli/drive-attach.js +18 -0
- package/dist/cli/drive-command.js +7 -3
- package/dist/cli/mcp-command.js +51 -20
- package/dist/cli/project-name.d.ts +30 -0
- package/dist/cli/project-name.js +44 -0
- package/dist/cli/status-next-action.d.ts +14 -0
- package/dist/cli/status-next-action.js +4 -4
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +134 -80
- package/dist/cloud/cloud-config.d.ts +22 -1
- package/dist/cloud/cloud-config.js +64 -5
- package/dist/cloud/cloud-sync.d.ts +5 -5
- package/dist/cloud/cloud-sync.js +33 -2
- package/dist/cloud/sync-cycle.d.ts +110 -0
- package/dist/cloud/sync-cycle.js +270 -0
- package/dist/cloud/sync-daemon.d.ts +69 -0
- package/dist/cloud/sync-daemon.js +260 -0
- package/dist/cloud/sync-disk.d.ts +9 -0
- package/dist/cloud/sync-disk.js +191 -0
- package/dist/cloud/sync-hash.d.ts +2 -0
- package/dist/cloud/sync-hash.js +35 -0
- package/dist/crawl/crawl-tools.js +5 -3
- package/dist/crawl/crawl.d.ts +3 -0
- package/dist/crawl/crawl.js +5 -0
- package/dist/daemon/daemon-resolve.d.ts +43 -0
- package/dist/daemon/daemon-resolve.js +69 -0
- package/dist/daemon/dev-servers.d.ts +15 -0
- package/dist/daemon/dev-servers.js +68 -0
- package/dist/daemon/wake-decision.d.ts +32 -0
- package/dist/daemon/wake-decision.js +38 -0
- package/dist/domain/domain-tools.js +2 -2
- package/dist/events/contradictions.d.ts +18 -0
- package/dist/events/contradictions.js +134 -21
- package/dist/events/declared.d.ts +7 -4
- package/dist/events/declared.js +15 -6
- package/dist/events/echo-mismatch.js +64 -14
- package/dist/events/event-filters.d.ts +8 -0
- package/dist/events/event-filters.js +19 -2
- package/dist/events/predicate-eval.js +53 -5
- package/dist/events/predicate-precheck.d.ts +8 -0
- package/dist/events/predicate-precheck.js +41 -0
- package/dist/events/predicate-route.d.ts +14 -0
- package/dist/events/predicate-route.js +36 -8
- package/dist/events/predicate.d.ts +5 -0
- package/dist/events/predicate.js +37 -5
- package/dist/events/split-text-miss.d.ts +30 -0
- package/dist/events/split-text-miss.js +44 -0
- package/dist/flows/flake.d.ts +6 -6
- package/dist/flows/flow-memory-consult.d.ts +55 -0
- package/dist/flows/flow-memory-consult.js +38 -0
- package/dist/flows/flow-replay-run.d.ts +18 -7
- package/dist/flows/flow-replay-run.js +239 -22
- package/dist/flows/flow-replay.js +11 -2
- package/dist/flows/flow-step-runners.d.ts +8 -0
- package/dist/flows/flow-step-runners.js +8 -2
- package/dist/flows/flow-store-for-session.d.ts +31 -0
- package/dist/flows/flow-store-for-session.js +33 -0
- package/dist/flows/flow-tools.js +58 -32
- package/dist/flows/flows.d.ts +8 -0
- package/dist/flows/flows.js +468 -2
- package/dist/flows/heal-run.js +5 -2
- package/dist/flows/predicate-to-expect.d.ts +17 -12
- package/dist/flows/predicate-to-expect.js +30 -12
- package/dist/flows/recordings.d.ts +13 -1
- package/dist/flows/recordings.js +17 -2
- package/dist/flows/replay.d.ts +22 -2
- package/dist/flows/replay.js +28 -2
- package/dist/flows/verify-change-tools.js +5 -2
- package/dist/honesty/blind-spots.d.ts +55 -2
- package/dist/honesty/blind-spots.js +63 -3
- package/dist/honesty/honesty.d.ts +24 -0
- package/dist/honesty/honesty.js +25 -1
- package/dist/honesty/instrumentation-gaps.d.ts +9 -0
- package/dist/honesty/instrumentation-gaps.js +29 -2
- package/dist/honesty/verified.d.ts +2 -1
- package/dist/honesty/verified.js +16 -1
- package/dist/http-server.js +12 -0
- package/dist/impact/impact-recorder.d.ts +31 -8
- package/dist/impact/impact-recorder.js +104 -21
- package/dist/impact/impact-store.d.ts +15 -1
- package/dist/impact/impact-store.js +43 -2
- package/dist/index.js +113 -7
- package/dist/init/agent-rules.d.ts +13 -6
- package/dist/init/agent-rules.js +23 -12
- package/dist/init/astro-patch.d.ts +16 -0
- package/dist/init/astro-patch.js +28 -0
- package/dist/init/capabilities.d.ts +4 -1
- package/dist/init/capabilities.js +22 -2
- package/dist/init/confirm.d.ts +31 -7
- package/dist/init/confirm.js +48 -8
- package/dist/init/cra.d.ts +20 -4
- package/dist/init/cra.js +57 -17
- package/dist/init/csp-check.js +17 -2
- package/dist/init/csp-doctor.d.ts +8 -1
- package/dist/init/csp-doctor.js +8 -3
- package/dist/init/dev-script.d.ts +3 -1
- package/dist/init/dev-script.js +8 -5
- package/dist/init/format-generated.d.ts +18 -0
- package/dist/init/format-generated.js +39 -0
- package/dist/init/install-hint.d.ts +9 -0
- package/dist/init/install-hint.js +29 -0
- package/dist/init/mcp-clients.d.ts +7 -0
- package/dist/init/mcp-clients.js +7 -0
- package/dist/init/mcp.d.ts +1 -1
- package/dist/init/mcp.js +1 -1
- package/dist/init/node-io.js +11 -1
- package/dist/init/plan-framework.js +81 -45
- package/dist/init/plan.d.ts +20 -7
- package/dist/init/plan.js +20 -45
- package/dist/init/preflight.d.ts +28 -0
- package/dist/init/preflight.js +38 -0
- package/dist/init/run.d.ts +32 -9
- package/dist/init/run.js +141 -119
- package/dist/init/slash-command.d.ts +1 -1
- package/dist/init/snippets.js +41 -25
- package/dist/init/vite-config.d.ts +1 -1
- package/dist/init/vite-config.js +39 -25
- package/dist/init/workspace-apps.d.ts +17 -10
- package/dist/init/workspace-apps.js +110 -0
- package/dist/input/network-mock-tools.js +3 -6
- package/dist/input/real-input.js +25 -5
- package/dist/input/scroll-tools.js +4 -6
- package/dist/input/viewport-tools.js +5 -4
- package/dist/intent/inline-intent.d.ts +10 -19
- package/dist/intent/inline-intent.js +91 -1
- package/dist/intent/intent-shard-store.d.ts +72 -0
- package/dist/intent/intent-shard-store.js +203 -0
- package/dist/intent/intent-shard.d.ts +411 -0
- package/dist/intent/intent-shard.js +176 -0
- package/dist/intent/intent-store.d.ts +12 -0
- package/dist/intent/intent-store.js +21 -0
- package/dist/intent/intent-subject.d.ts +45 -0
- package/dist/intent/intent-subject.js +177 -0
- package/dist/intent/intent-tools.js +65 -2
- package/dist/intent/open-intents.d.ts +21 -0
- package/dist/intent/open-intents.js +15 -0
- package/dist/license/license-env.d.ts +29 -0
- package/dist/license/license-env.js +148 -0
- package/dist/loopback-agent.d.ts +31 -0
- package/dist/loopback-agent.js +57 -0
- package/dist/mcp/attach-memory.d.ts +18 -2
- package/dist/mcp/attach-memory.js +50 -7
- package/dist/mcp/client-identity.d.ts +31 -0
- package/dist/mcp/client-identity.js +51 -0
- package/dist/mcp/mcp-post-transport.d.ts +26 -0
- package/dist/mcp/mcp-post-transport.js +109 -0
- package/dist/mcp/mcp-proxy.d.ts +2 -1
- package/dist/mcp/mcp-proxy.js +52 -62
- package/dist/mcp/mcp.js +7 -1
- package/dist/mcp/proxy-backoff.d.ts +11 -0
- package/dist/mcp/proxy-backoff.js +13 -0
- package/dist/mcp/proxy-daemon-probe.d.ts +1 -0
- package/dist/mcp/proxy-daemon-probe.js +7 -1
- package/dist/mcp/proxy-handshake.d.ts +23 -0
- package/dist/mcp/proxy-handshake.js +31 -0
- package/dist/mcp/tool-catalog-cache.d.ts +8 -0
- package/dist/mcp/tool-catalog-cache.js +12 -0
- package/dist/memory/memory-tools.d.ts +2 -0
- package/dist/memory/memory-tools.js +82 -0
- package/dist/memory/project-memory.d.ts +34 -0
- package/dist/memory/project-memory.js +96 -0
- package/dist/oracles/propose-consequences.js +12 -3
- package/dist/pool/browser-pool.d.ts +63 -4
- package/dist/pool/browser-pool.js +113 -4
- package/dist/pool/playwright-launcher.js +9 -0
- package/dist/project/project-tools.js +3 -3
- package/dist/project/reticle-dir.d.ts +15 -2
- package/dist/project/reticle-dir.js +21 -4
- package/dist/runs/run-store.d.ts +10 -0
- package/dist/runs/run-store.js +10 -0
- package/dist/runs/verification-sync.d.ts +0 -11
- package/dist/runs/verification-sync.js +22 -1
- package/dist/runs/verify-server.d.ts +8 -1
- package/dist/runs/verify-server.js +14 -2
- package/dist/session/command-timeout.d.ts +5 -2
- package/dist/session/command-timeout.js +5 -4
- package/dist/session/lease-visibility.d.ts +67 -0
- package/dist/session/lease-visibility.js +76 -0
- package/dist/session/no-session-diagnosis.d.ts +23 -0
- package/dist/session/no-session-diagnosis.js +58 -17
- package/dist/session/no-session-next-action.d.ts +7 -0
- package/dist/session/no-session-next-action.js +42 -3
- package/dist/session/no-session-watch.d.ts +10 -6
- package/dist/session/no-session-watch.js +51 -19
- package/dist/session/pending-commands.d.ts +11 -1
- package/dist/session/pending-commands.js +12 -2
- package/dist/session/resolve-within.d.ts +34 -0
- package/dist/session/resolve-within.js +53 -0
- package/dist/session/session-info.d.ts +18 -0
- package/dist/session/session-info.js +11 -0
- package/dist/session/session-manager.d.ts +14 -0
- package/dist/session/session-manager.js +61 -3
- package/dist/session/session-mirror.d.ts +16 -0
- package/dist/session/session-mirror.js +22 -0
- package/dist/session/session-recommendation.d.ts +19 -5
- package/dist/session/session-recommendation.js +25 -9
- package/dist/session/session-successor.js +36 -1
- package/dist/session/session-tools.js +2 -2
- package/dist/session/session.d.ts +22 -2
- package/dist/session/session.js +87 -7
- package/dist/session/wall-clock.d.ts +12 -0
- package/dist/session/wall-clock.js +14 -0
- package/dist/setup/agent-configs.d.ts +96 -0
- package/dist/setup/agent-configs.js +333 -0
- package/dist/setup/agent-io.d.ts +9 -0
- package/dist/setup/agent-io.js +16 -0
- package/dist/setup/agent-writer.d.ts +51 -0
- package/dist/setup/agent-writer.js +115 -0
- package/dist/setup/approval-migration.d.ts +47 -0
- package/dist/setup/approval-migration.js +68 -0
- package/dist/setup/auto-approve.d.ts +85 -0
- package/dist/setup/auto-approve.js +150 -0
- package/dist/setup/bridge-port.d.ts +15 -0
- package/dist/setup/bridge-port.js +24 -0
- package/dist/setup/desktop-shape.d.ts +57 -0
- package/dist/setup/desktop-shape.js +63 -0
- package/dist/setup/dev-server-wait.d.ts +68 -0
- package/dist/setup/dev-server-wait.js +67 -0
- package/dist/setup/drive-agent.d.ts +71 -0
- package/dist/setup/drive-agent.js +171 -0
- package/dist/setup/drive-plan.d.ts +68 -0
- package/dist/setup/drive-plan.js +130 -0
- package/dist/setup/ensure-daemon.d.ts +29 -0
- package/dist/setup/ensure-daemon.js +58 -0
- package/dist/setup/init-runtime.d.ts +47 -0
- package/dist/setup/init-runtime.js +160 -0
- package/dist/setup/interrupt.d.ts +25 -0
- package/dist/setup/interrupt.js +40 -0
- package/dist/setup/license-key.d.ts +39 -0
- package/dist/setup/license-key.js +72 -0
- package/dist/setup/listeners.d.ts +37 -0
- package/dist/setup/listeners.js +85 -0
- package/dist/setup/node-effects.d.ts +60 -0
- package/dist/setup/node-effects.js +210 -0
- package/dist/setup/page-probe.d.ts +39 -0
- package/dist/setup/page-probe.js +49 -0
- package/dist/setup/relaunch.d.ts +36 -0
- package/dist/setup/relaunch.js +57 -0
- package/dist/setup/remaining-steps.d.ts +19 -0
- package/dist/setup/remaining-steps.js +31 -0
- package/dist/setup/run-setup.d.ts +99 -0
- package/dist/setup/run-setup.js +203 -0
- package/dist/setup/session-pick.d.ts +47 -0
- package/dist/setup/session-pick.js +51 -0
- package/dist/setup/setup-command.d.ts +45 -0
- package/dist/setup/setup-command.js +199 -0
- package/dist/setup/setup-options.d.ts +74 -0
- package/dist/setup/setup-options.js +67 -0
- package/dist/setup/transcripts.d.ts +16 -0
- package/dist/setup/transcripts.js +43 -0
- package/dist/status-payload.d.ts +14 -1
- package/dist/status-payload.js +23 -1
- package/dist/telemetry/daemon-telemetry.d.ts +8 -1
- package/dist/telemetry/daemon-telemetry.js +52 -5
- package/dist/telemetry/error-class.js +1 -1
- package/dist/telemetry/feedback-context.d.ts +9 -1
- package/dist/telemetry/feedback-context.js +60 -29
- package/dist/telemetry/feedback-local.d.ts +15 -0
- package/dist/telemetry/feedback-local.js +56 -0
- package/dist/telemetry/feedback.d.ts +11 -0
- package/dist/telemetry/feedback.js +19 -6
- package/dist/telemetry/init-telemetry.d.ts +2 -0
- package/dist/telemetry/init-telemetry.js +2 -0
- package/dist/telemetry/install-source.d.ts +14 -0
- package/dist/telemetry/install-source.js +30 -0
- package/dist/telemetry/license-activation.d.ts +7 -1
- package/dist/telemetry/license-activation.js +9 -5
- package/dist/telemetry/mcp-connection.js +7 -0
- package/dist/telemetry/project-profile.d.ts +12 -1
- package/dist/telemetry/project-profile.js +11 -6
- package/dist/telemetry/proxy-telemetry.d.ts +3 -0
- package/dist/telemetry/proxy-telemetry.js +21 -0
- package/dist/telemetry/session-metrics.d.ts +12 -0
- package/dist/telemetry/session-metrics.js +35 -1
- package/dist/telemetry/telemetry.js +17 -2
- package/dist/telemetry/tool-refused.d.ts +3 -1
- package/dist/telemetry/tool-refused.js +34 -2
- package/dist/tools/act-capsule.d.ts +10 -0
- package/dist/tools/act-capsule.js +8 -2
- package/dist/tools/act-danger.d.ts +2 -1
- package/dist/tools/act-danger.js +11 -3
- package/dist/tools/act-observation.d.ts +41 -0
- package/dist/tools/act-observation.js +36 -0
- package/dist/tools/act-preflight.d.ts +20 -0
- package/dist/tools/act-preflight.js +98 -0
- package/dist/tools/act-sequence-retry.d.ts +45 -0
- package/dist/tools/act-sequence-retry.js +58 -0
- package/dist/tools/act-sequence-tool.d.ts +13 -0
- package/dist/tools/act-sequence-tool.js +135 -0
- package/dist/tools/act-target.d.ts +14 -0
- package/dist/tools/act-target.js +47 -0
- package/dist/tools/act-tools.d.ts +21 -1
- package/dist/tools/act-tools.js +100 -213
- package/dist/tools/assert-verdict.js +22 -16
- package/dist/tools/browser-tools.js +6 -2
- package/dist/tools/dynamic-tools.js +24 -5
- package/dist/tools/error-recovery.d.ts +1 -0
- package/dist/tools/error-recovery.js +8 -0
- package/dist/tools/invoke-tool.js +59 -9
- package/dist/tools/lease-tools.d.ts +30 -4
- package/dist/tools/lease-tools.js +169 -5
- package/dist/tools/merged-name-redirect.d.ts +13 -0
- package/dist/tools/merged-name-redirect.js +22 -0
- package/dist/tools/numeric-bounds.d.ts +100 -0
- package/dist/tools/numeric-bounds.js +125 -0
- package/dist/tools/observe-tools.js +47 -40
- package/dist/tools/read-tools.js +28 -7
- package/dist/tools/real-input-attempt.d.ts +10 -0
- package/dist/tools/real-input-attempt.js +59 -1
- package/dist/tools/reconcile-tools.js +4 -4
- package/dist/tools/tool-kit.d.ts +12 -0
- package/dist/tools/tool-kit.js +6 -1
- package/dist/tools/tool-names.d.ts +2 -0
- package/dist/tools/tool-names.js +2 -0
- package/dist/tools/tools-helpers.d.ts +19 -0
- package/dist/tools/tools-helpers.js +46 -0
- package/dist/tools/tools.d.ts +2 -0
- package/dist/tools/tools.js +56 -8
- package/dist/update/reticle-deps.d.ts +9 -0
- package/dist/update/reticle-deps.js +27 -0
- package/dist/update/sdk-sync.d.ts +1 -2
- package/dist/update/sdk-sync.js +1 -21
- package/dist/update/update-nudge.d.ts +18 -0
- package/dist/update/update-nudge.js +20 -0
- package/dist/version/peer-announce.js +4 -2
- package/dist/version/sdk-fix.d.ts +47 -0
- package/dist/version/sdk-fix.js +127 -0
- package/dist/version/version-nudge.d.ts +14 -0
- package/dist/version/version-nudge.js +21 -0
- package/dist/version/version-skew.d.ts +38 -3
- package/dist/version/version-skew.js +83 -7
- package/dist/visual/visual-store.d.ts +6 -6
- package/dist/visual/visual-store.js +18 -16
- package/dist/visual/visual-tools.js +57 -12
- package/docs/cli/doctor.mdx +1 -0
- package/docs/cli/serve.mdx +4 -2
- package/docs/cli.mdx +7 -3
- package/docs/desktop.mdx +1 -1
- package/docs/docs.json +1 -1
- package/docs/getting-started.md +19 -3
- package/docs/index.mdx +9 -6
- package/docs/install-agentic.mdx +38 -12
- package/docs/install-manual.mdx +1 -1
- package/docs/packages.mdx +1 -1
- package/docs/skill-file.mdx +3 -3
- package/docs/telemetry-contract.md +108 -4
- package/docs/tools-act-sequence.mdx +16 -0
- package/docs/usage.md +1 -1
- package/package.json +4 -3
- package/dist/init/cursor.d.ts +0 -24
- package/dist/init/cursor.js +0 -80
package/dist/init/mcp.js
CHANGED
|
@@ -55,7 +55,7 @@ const WINDOWS_SHELL_C = '/c';
|
|
|
55
55
|
*
|
|
56
56
|
* So it stays unpinned, and the skew is handled where it actually shows up: the contract fingerprint
|
|
57
57
|
* makes a real mismatch loud on the next tool result (see version-skew), and a stale entry of our own
|
|
58
|
-
* shape is now repaired on re-run rather than reported "already registered" (see
|
|
58
|
+
* shape is now repaired on re-run rather than reported "already registered" (see mcp-clients.ts).
|
|
59
59
|
*/
|
|
60
60
|
export function npxServerArgs() {
|
|
61
61
|
return [RETICLE_PACKAGE, MCP_SUBCOMMAND];
|
package/dist/init/node-io.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* `runInit` stays testable with an in-memory IO. Prints to stdout — `init` is a one-shot CLI
|
|
4
4
|
* command, not the MCP stdio transport.
|
|
5
5
|
*/
|
|
6
|
-
import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync, statSync } from 'node:fs';
|
|
6
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync, statSync, accessSync, constants, } from 'node:fs';
|
|
7
7
|
import { NodePlatform } from '../platform.js';
|
|
8
8
|
import { join, dirname, isAbsolute } from 'node:path';
|
|
9
9
|
import { homedir } from 'node:os';
|
|
@@ -95,6 +95,16 @@ export function buildNodeIo(cwd) {
|
|
|
95
95
|
const result = spawnSync(command, shellSafe(args), { cwd, stdio: 'inherit', ...shellOpt() });
|
|
96
96
|
return 0 === result.status;
|
|
97
97
|
},
|
|
98
|
+
/** One access check, rather than discovering it as an EACCES stack four phases later. */
|
|
99
|
+
canWrite() {
|
|
100
|
+
try {
|
|
101
|
+
accessSync(cwd, constants.W_OK);
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
},
|
|
98
108
|
probe(command, args) {
|
|
99
109
|
// Quiet yes/no check (CLI availability, existing registration). Never throws.
|
|
100
110
|
const result = spawnSync(command, shellSafe(args), { cwd, stdio: 'ignore', ...shellOpt() });
|
|
@@ -6,13 +6,14 @@
|
|
|
6
6
|
import { bridgeWsUrl } from '@reticlehq/core';
|
|
7
7
|
import { patchViteConfig, VitePatchKind } from './vite-config.js';
|
|
8
8
|
import { patchNextConfig, patchRootLayout, patchPagesApp } from './next-patch.js';
|
|
9
|
-
import { patchAstroConfig, patchAstroLayout } from './astro-patch.js';
|
|
10
|
-
import { CRA_DEV_MODULE_IMPORT,
|
|
9
|
+
import { ASTRO_ENV_DTS_PATH, patchAstroConfig, patchAstroEnvDts, patchAstroLayout, } from './astro-patch.js';
|
|
10
|
+
import { CRA_DEV_MODULE_IMPORT, CRA_ENV_PATH, CRA_TOKEN_PER_MACHINE_NOTICE, TOKEN_VAR, craDevModuleFile, craDevModulePath, craEnvPatch, craImportPatch, } from './cra.js';
|
|
11
11
|
import { PatchKind } from './patch-kind.js';
|
|
12
12
|
import { viteManual, NEXT_LAYOUT_MANUAL, NEXT_LAYOUT_PATH, viteDevModuleFile, VITE_DEV_MODULE_PATH, nextReticleDevFile, NEXT_RETICLE_DEV_PATH, nextConfigManual, svelteKitHooksFile, SVELTEKIT_HOOKS_PATH, UNVERIFIED_FRAMEWORK_NOTE, astroManual, nuxtManual, NUXT_PLUGIN_PATH, } from './snippets.js';
|
|
13
|
-
import {
|
|
13
|
+
import { StepStatus } from './plan.js';
|
|
14
14
|
import { RETICLE_DEFAULT_PORT } from '@reticlehq/core';
|
|
15
|
-
import { CSP_STEP_TITLE
|
|
15
|
+
import { CSP_STEP_TITLE } from './csp-check.js';
|
|
16
|
+
import { diagnoseWebCsp } from './csp-doctor.js';
|
|
16
17
|
/** What adding `reticle()` to a Vite config buys, which differs by framework. */
|
|
17
18
|
export const VITE_PLUGIN_DETAIL = {
|
|
18
19
|
/** A plain Vite app gets both halves from the plugin. */
|
|
@@ -41,20 +42,13 @@ const CAPABILITIES_TITLE = 'Capabilities + store';
|
|
|
41
42
|
*/
|
|
42
43
|
const CAPABILITIES_TODO_TITLE = 'AGENT: finish the capabilities file';
|
|
43
44
|
function capabilitiesTodo(path, stores) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
`${stores.join('; ')}.`
|
|
48
|
-
: `No state library was detected. If the app holds state somewhere init cannot see, register ` +
|
|
49
|
-
`it — see node_modules/@reticlehq/server/docs/usage.md.`;
|
|
50
|
-
return (`${path} was written but registers nothing, so \`hasCapabilities\` stays false and reticle_state ` +
|
|
51
|
-
`has nothing to read.\n` +
|
|
45
|
+
return (`This app's state lives in a library nothing in the running app points at, so it is the one ` +
|
|
46
|
+
`thing Reticle cannot find for itself — reticle_state will not see it until you say where it ` +
|
|
47
|
+
`is.\n` +
|
|
52
48
|
`Do this now, before driving anything:\n` +
|
|
53
|
-
` 1. ${
|
|
54
|
-
` 2.
|
|
55
|
-
`
|
|
56
|
-
` 3. Prove it: drive one flow and check reticle_state returns your keys. An empty state read ` +
|
|
57
|
-
`means this file is still not doing anything, whatever else went green.`);
|
|
49
|
+
` 1. In ${path}, uncomment the suggested line and complete it: ${stores.join('; ')}.\n` +
|
|
50
|
+
` 2. Prove it: drive one flow and check reticle_state returns your key. An empty state read ` +
|
|
51
|
+
`means the line is still not doing anything, whatever else went green.`);
|
|
58
52
|
}
|
|
59
53
|
/**
|
|
60
54
|
* The dev module carrying `registerCapabilities` / `registerStore`.
|
|
@@ -64,20 +58,26 @@ function capabilitiesTodo(path, stores) {
|
|
|
64
58
|
* only when absent, because it is the one generated file a user is expected to EDIT.
|
|
65
59
|
*/
|
|
66
60
|
/**
|
|
67
|
-
*
|
|
61
|
+
* Is there state left that ONLY the app can hand over?
|
|
68
62
|
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
63
|
+
* This used to ask a much bigger question — "does the generated file register anything at all?" —
|
|
64
|
+
* and fired on almost every install, because almost every install generated a file whose testids
|
|
65
|
+
* and stores were both empty. The answer was homework: go read the source, uncomment a line, add
|
|
66
|
+
* data-testid attributes, then drive to prove it. Several turns, on every onboarding.
|
|
67
|
+
*
|
|
68
|
+
* Two of those three are no longer anyone's homework. Testids are read from the live DOM, and a
|
|
69
|
+
* store passed through a React context provider (Redux, TanStack Query) is discovered and
|
|
70
|
+
* registered on the first commit. What is left is the genuinely unreachable case: a module-scope
|
|
71
|
+
* store, or one needing an argument only the source supplies — Zustand, Jotai atoms, an XState
|
|
72
|
+
* actor. Nothing in the running app points at those, so the notice still has to fire, and it now
|
|
73
|
+
* fires ONLY there.
|
|
74
74
|
*
|
|
75
75
|
* `wired` are stores init resolved and wrote a live `registerStore` call for. Hints are not
|
|
76
76
|
* registrations: they land in the file as a commented line, and counting one as a registration
|
|
77
77
|
* silences the notice whose whole purpose is to say "act on the hint".
|
|
78
78
|
*/
|
|
79
|
-
function
|
|
80
|
-
return
|
|
79
|
+
function needsManualStore(hints, wired) {
|
|
80
|
+
return hints.length > 0 && 0 === wired.length;
|
|
81
81
|
}
|
|
82
82
|
function capabilitiesStep(input) {
|
|
83
83
|
if (true === input.viteDevModuleExists) {
|
|
@@ -93,13 +93,15 @@ function capabilitiesStep(input) {
|
|
|
93
93
|
const testids = input.testids ?? [];
|
|
94
94
|
const stores = input.storeHints ?? [];
|
|
95
95
|
const wired = input.foundStores ?? [];
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
//
|
|
100
|
-
|
|
101
|
-
//
|
|
102
|
-
//
|
|
96
|
+
// Testids no longer need counting here. They are read from the live DOM at announce time, so a
|
|
97
|
+
// number printed at install time would be a stale claim about a codebase that is about to change —
|
|
98
|
+
// which is exactly what "no data-testid values yet" used to be on an app whose testids arrive with
|
|
99
|
+
// a lazy route.
|
|
100
|
+
const found = 'testids read from the live DOM';
|
|
101
|
+
// A store we found and WIRED is a registration, so the notice must not fire. A store HINT is not:
|
|
102
|
+
// `stores` holds suggestions, written into the file as a commented line of the form
|
|
103
|
+
// `// import your store, then: registerStore(...)`. Counting a suggestion as a registration let
|
|
104
|
+
// the hint silence the notice whose entire job is to say "act on the hint".
|
|
103
105
|
//
|
|
104
106
|
// Measured against a real product UI (rowy — 70+ deps, jotai, a whole src/atoms tree): init
|
|
105
107
|
// detected jotai, offered one commented line, emitted NO notice, and wrote
|
|
@@ -107,10 +109,10 @@ function capabilitiesStep(input) {
|
|
|
107
109
|
// false, `reticle_state` was empty forever, and the install gate reported "connected: 1,
|
|
108
110
|
// manual ⚠: none". Every check green, state observability zero.
|
|
109
111
|
//
|
|
110
|
-
//
|
|
111
|
-
//
|
|
112
|
-
//
|
|
113
|
-
const nothingToRegister =
|
|
112
|
+
// jotai is still exactly that case. A Redux or TanStack app is not, any more: its store rides a
|
|
113
|
+
// context provider and the React adapter registers it on the first commit, so `storeHints` no
|
|
114
|
+
// longer names either one and this notice no longer fires for them.
|
|
115
|
+
const nothingToRegister = needsManualStore(stores, wired);
|
|
114
116
|
return [
|
|
115
117
|
{
|
|
116
118
|
title: CAPABILITIES_TITLE,
|
|
@@ -165,7 +167,7 @@ function viteConfigSteps(input, detail) {
|
|
|
165
167
|
},
|
|
166
168
|
];
|
|
167
169
|
}
|
|
168
|
-
const patch = patchViteConfig(cfg.source, port);
|
|
170
|
+
const patch = patchViteConfig(cfg.source, port, true === input.captureBodies);
|
|
169
171
|
if (patch.kind === VitePatchKind.ALREADY) {
|
|
170
172
|
return [
|
|
171
173
|
{
|
|
@@ -294,7 +296,7 @@ export function nextSteps(input) {
|
|
|
294
296
|
// The same question the Vite path asks. Skipped when the module already exists: its contents are
|
|
295
297
|
// the user's, and re-nagging about a file we did not write is noise.
|
|
296
298
|
const nextTodo = true !== input.nextReticleDevExists &&
|
|
297
|
-
|
|
299
|
+
needsManualStore(input.storeHints ?? [], input.nextFoundStores ?? [])
|
|
298
300
|
? [
|
|
299
301
|
{
|
|
300
302
|
title: CAPABILITIES_TODO_TITLE,
|
|
@@ -327,15 +329,19 @@ export function nextSteps(input) {
|
|
|
327
329
|
*/
|
|
328
330
|
export function craSteps(input) {
|
|
329
331
|
const entry = input.craEntry ?? null;
|
|
332
|
+
// Match the project language the same way Next does (#675): a JS CRA app cannot resolve `.ts`.
|
|
333
|
+
const modulePath = craDevModulePath(input.detection.typescript);
|
|
330
334
|
const steps = [
|
|
331
335
|
{
|
|
332
336
|
title: 'Reticle connect module',
|
|
333
|
-
target:
|
|
337
|
+
target: modulePath,
|
|
334
338
|
status: StepStatus.APPLY,
|
|
335
339
|
detail: 'create the dev-only connect (CRA cannot inject through public/index.html)',
|
|
336
340
|
write: {
|
|
337
|
-
path:
|
|
338
|
-
content: craDevModuleFile(input.options.port, input.options.projectId
|
|
341
|
+
path: modulePath,
|
|
342
|
+
content: craDevModuleFile(input.options.port, input.options.projectId, {
|
|
343
|
+
typescript: input.detection.typescript,
|
|
344
|
+
}),
|
|
339
345
|
},
|
|
340
346
|
dependsOnInstall: true,
|
|
341
347
|
},
|
|
@@ -500,9 +506,14 @@ export function astroSteps(input) {
|
|
|
500
506
|
},
|
|
501
507
|
];
|
|
502
508
|
}
|
|
509
|
+
const envPatch = patchAstroEnvDts(input.astroEnvDts ?? null);
|
|
503
510
|
return [
|
|
504
511
|
patchStep('Astro config (token + build target)', config.path, configPatch, 'inline the pairing token and raise build.target to es2022', manualWithLayout),
|
|
505
512
|
patchStep('Connect snippet (Astro)', layout.path, layoutPatch, 'add the dev-only connect <script> before </body>', manualWithLayout),
|
|
513
|
+
// Declares the Vite define names so `astro check` (create-astro's default build) can see them
|
|
514
|
+
// (#677). Independent of the two halves above: even an ALREADY config/layout still needs this
|
|
515
|
+
// when the env file was never written.
|
|
516
|
+
patchStep('Astro env types (Vite defines)', ASTRO_ENV_DTS_PATH, envPatch, 'declare __RETICLE_TOKEN__ / __RETICLE_ROOT__ for astro check', manualWithLayout),
|
|
506
517
|
];
|
|
507
518
|
}
|
|
508
519
|
/**
|
|
@@ -515,15 +526,40 @@ export function astroSteps(input) {
|
|
|
515
526
|
* warning and a fix.
|
|
516
527
|
*/
|
|
517
528
|
export function cspStep(input) {
|
|
518
|
-
|
|
519
|
-
|
|
529
|
+
// Reads the SAME list `reticle doctor` reads. It used to read a hand-written pair —
|
|
530
|
+
// `[nextConfigSource, nextLayout?.source]` — while csp-doctor.ts already carried the full set
|
|
531
|
+
// including `index.html`, which is where every Vite and Electron app declares its policy. So the
|
|
532
|
+
// command you run BEFORE anything works checked less than the one you run after it has failed.
|
|
533
|
+
//
|
|
534
|
+
// Measured on MarkText, a production Electron editor: its renderer sets `default-src 'self'` with
|
|
535
|
+
// no `connect-src`, the browser blocked the bridge WebSocket, and `init` printed a clean plan. The
|
|
536
|
+
// daemon cannot see a dial that never left the page, so nothing anywhere said why — and the check
|
|
537
|
+
// that would have said it was sitting one import away.
|
|
538
|
+
// The pre-read Next sources are folded in ON TOP of the shared list, not replaced by it: init
|
|
539
|
+
// resolves `nextConfigFile` across more spellings than CSP_FILES names (`.mts`, for one), and a
|
|
540
|
+
// layout is found by search rather than by a fixed path. Two sources of the same truth is the
|
|
541
|
+
// problem being fixed here — one of them being a SUPERSET is not.
|
|
542
|
+
const extra = {
|
|
543
|
+
...(input.nextConfigFile !== null && input.nextConfigFile !== undefined
|
|
544
|
+
? { [input.nextConfigFile]: input.nextConfigSource ?? undefined }
|
|
545
|
+
: {}),
|
|
546
|
+
...(input.nextLayout ? { [input.nextLayout.path]: input.nextLayout.source } : {}),
|
|
547
|
+
};
|
|
548
|
+
const read = (file) => extra[file] ?? input.cspSources?.[file];
|
|
549
|
+
const findings = diagnoseWebCsp(read, input.options.port ?? RETICLE_DEFAULT_PORT, [
|
|
550
|
+
...Object.keys(extra),
|
|
551
|
+
]);
|
|
552
|
+
const first = findings[0];
|
|
553
|
+
if (first === undefined)
|
|
520
554
|
return [];
|
|
521
555
|
return [
|
|
522
556
|
{
|
|
523
557
|
title: CSP_STEP_TITLE,
|
|
524
|
-
target:
|
|
558
|
+
target: first.file,
|
|
525
559
|
status: StepStatus.NOTICE,
|
|
526
|
-
|
|
560
|
+
// `problem` already ends with the text to paste; `fix` is the same sentence for callers that
|
|
561
|
+
// want it on its own (doctor renders them separately). Printing both said it twice.
|
|
562
|
+
detail: first.problem,
|
|
527
563
|
},
|
|
528
564
|
];
|
|
529
565
|
}
|
package/dist/init/plan.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { Framework, UiLibrary, type Detection } from './detect.js';
|
|
7
7
|
import type { FoundStore } from './capabilities.js';
|
|
8
|
-
import {
|
|
8
|
+
import { McpClient } from './mcp-clients.js';
|
|
9
9
|
/**
|
|
10
10
|
* The dev-dependencies `reticle init` installs — kit (or sensor) first, build plugin next.
|
|
11
11
|
*
|
|
@@ -85,20 +85,28 @@ export interface Plan {
|
|
|
85
85
|
}
|
|
86
86
|
export interface PlanInput {
|
|
87
87
|
detection: Detection;
|
|
88
|
+
/**
|
|
89
|
+
* Write `captureNetworkBodies: true` into the app's config. Off unless the caller asked (#705).
|
|
90
|
+
*
|
|
91
|
+
* Optional so every existing caller and test keeps the safe default without naming it — the one
|
|
92
|
+
* direction a default about somebody else's data should be wrong in.
|
|
93
|
+
*/
|
|
94
|
+
captureBodies?: boolean | undefined;
|
|
95
|
+
/**
|
|
96
|
+
* The CSP-bearing files this project actually has, keyed by path — read once in `gatherPlanInput`.
|
|
97
|
+
*
|
|
98
|
+
* Pre-read rather than given a reader, because `PlanInput` is the pure input to a pure planner and
|
|
99
|
+
* handing it an io would let any later step reach the disk from inside `buildPlan`.
|
|
100
|
+
*/
|
|
101
|
+
cspSources?: Readonly<Record<string, string | undefined>> | undefined;
|
|
88
102
|
/** Whether the `claude` CLI is installed (so we can register the MCP server globally). */
|
|
89
103
|
claudeCli: boolean;
|
|
90
104
|
/** Whether an `reticle` MCP server is already registered with Claude (any scope) — idempotency. */
|
|
91
105
|
mcpExists: boolean;
|
|
92
|
-
/** Whether Cursor is installed for this user (its global config dir exists). */
|
|
93
|
-
cursorPresent: boolean;
|
|
94
106
|
/** `process.platform`. Injected so this module stays pure. Windows is the only branch. */
|
|
95
107
|
platform?: string;
|
|
96
108
|
/** Whether THIS project has a .cursor/ directory — the signal that Cursor works on this repo. */
|
|
97
109
|
cursorProjectPresent?: boolean | undefined;
|
|
98
|
-
/** Current ~/.cursor/mcp.json content, or null if absent. */
|
|
99
|
-
cursorConfig: string | null;
|
|
100
|
-
/** Absolute path of ~/.cursor/mcp.json (the write target). */
|
|
101
|
-
cursorConfigPath: string;
|
|
102
110
|
/**
|
|
103
111
|
* Every OTHER MCP client detected on this machine, with its config path and current content.
|
|
104
112
|
*
|
|
@@ -131,6 +139,11 @@ export interface PlanInput {
|
|
|
131
139
|
path: string;
|
|
132
140
|
source: string;
|
|
133
141
|
} | null | undefined;
|
|
142
|
+
/**
|
|
143
|
+
* Existing `src/env.d.ts` content, when present — where the Vite-define ambient declarations go
|
|
144
|
+
* so `astro check` can see `__RETICLE_TOKEN__` / `__RETICLE_ROOT__` (#677).
|
|
145
|
+
*/
|
|
146
|
+
astroEnvDts?: string | null | undefined;
|
|
134
147
|
/** Discovered Next config filename (e.g. 'next.config.mjs'), or null. */
|
|
135
148
|
nextConfigFile: string | null;
|
|
136
149
|
/** Source of that Next config, so the export can be wrapped in withReticle. */
|
package/dist/init/plan.js
CHANGED
|
@@ -4,16 +4,16 @@
|
|
|
4
4
|
* runner performs the `write` side-effects; this module decides *what* should happen.
|
|
5
5
|
*/
|
|
6
6
|
import { Framework, PackageManager, UiLibrary, installCommand, installCommandParts, } from './detect.js';
|
|
7
|
+
import { installFailureHint } from './install-hint.js';
|
|
7
8
|
import { claudeAddCommand, mcpManual, mcpWindowsNote } from './mcp.js';
|
|
8
9
|
import { NodePlatform } from '../platform.js';
|
|
9
|
-
import {
|
|
10
|
-
import { mergeClientConfig, ClientMergeStatus, clientSnippet, clientSpec, } from './mcp-clients.js';
|
|
10
|
+
import { mergeClientConfig, ClientMergeStatus, clientSnippet, clientSpec, McpClient, } from './mcp-clients.js';
|
|
11
11
|
import { CLAUDE_COMMAND_PATH, CURSOR_COMMAND_PATH, SLASH_COMMAND_BODY, SLASH_COMMAND_SIGNATURE, } from './slash-command.js';
|
|
12
12
|
import { mergeMarkedInstruction, reticleMdFile, cursorRuleFile, AgentRuleStatus, CLAUDE_MD_PATH, AGENTS_MD_PATH, RETICLE_MD_PATH, CURSOR_RULE_PATH, } from './agent-rules.js';
|
|
13
13
|
import { viteSteps, nextSteps, craSteps, svelteKitSteps, nuxtSteps, astroSteps, cspStep, VITE_PLUGIN_DETAIL, } from './plan-framework.js';
|
|
14
14
|
import { join } from 'node:path';
|
|
15
15
|
import { htmlManual, reticleConfigContent, unverifiedUiLibraryNote } from './snippets.js';
|
|
16
|
-
import { declaredInstallSource } from '../telemetry/install-source.js';
|
|
16
|
+
import { configWithInstallSource, declaredInstallSource } from '../telemetry/install-source.js';
|
|
17
17
|
import { devServerPortWarning, isLikelyDevServerPort } from '../cli/cli-port.js';
|
|
18
18
|
// An app dev installs exactly the audience-scoped browser-side dependencies — never the retired
|
|
19
19
|
// `@reticlehq/core` umbrella (which dragged the Node MCP server + ws into every app). The kit is the
|
|
@@ -122,7 +122,6 @@ function agentFile(input, relPath) {
|
|
|
122
122
|
return root === undefined || 0 === root.length ? relPath : join(root, relPath);
|
|
123
123
|
}
|
|
124
124
|
const CLAUDE_MCP_TITLE = 'MCP server (Claude, global)';
|
|
125
|
-
const CURSOR_MCP_TITLE = 'MCP server (Cursor, global)';
|
|
126
125
|
function claudeMcpStep(input) {
|
|
127
126
|
if (!input.claudeCli)
|
|
128
127
|
return null;
|
|
@@ -143,34 +142,6 @@ function claudeMcpStep(input) {
|
|
|
143
142
|
exec: { command: cmd.command, args: cmd.args, fallback: cmd.display },
|
|
144
143
|
};
|
|
145
144
|
}
|
|
146
|
-
function cursorMcpStep(input) {
|
|
147
|
-
if (!input.cursorPresent)
|
|
148
|
-
return null;
|
|
149
|
-
const r = mergeCursorConfig(input.cursorConfig);
|
|
150
|
-
if (r.status === CursorMergeStatus.ALREADY) {
|
|
151
|
-
return {
|
|
152
|
-
title: CURSOR_MCP_TITLE,
|
|
153
|
-
target: input.cursorConfigPath,
|
|
154
|
-
status: StepStatus.ALREADY,
|
|
155
|
-
detail: 'reticle already in Cursor global config',
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
if (r.status === CursorMergeStatus.MANUAL) {
|
|
159
|
-
return {
|
|
160
|
-
title: CURSOR_MCP_TITLE,
|
|
161
|
-
target: input.cursorConfigPath,
|
|
162
|
-
status: StepStatus.MANUAL,
|
|
163
|
-
detail: `couldn't parse ${input.cursorConfigPath} — add this server by hand:\n "reticle": ${JSON.stringify(cursorServerEntry())}`,
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
return {
|
|
167
|
-
title: CURSOR_MCP_TITLE,
|
|
168
|
-
target: input.cursorConfigPath,
|
|
169
|
-
status: StepStatus.APPLY,
|
|
170
|
-
detail: 'register reticle in Cursor global config',
|
|
171
|
-
write: { path: input.cursorConfigPath, content: r.content },
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
145
|
/**
|
|
175
146
|
* One step per OTHER detected client.
|
|
176
147
|
*
|
|
@@ -304,7 +275,8 @@ function claudeCommandStep(input) {
|
|
|
304
275
|
return commandStepFor(agentFile(input, CLAUDE_COMMAND_PATH), input.claudeCli, input.claudeCommandContent);
|
|
305
276
|
}
|
|
306
277
|
function cursorCommandStep(input) {
|
|
307
|
-
const
|
|
278
|
+
const cursorGlobal = (input.detectedClients ?? []).some((c) => c.id === McpClient.CURSOR);
|
|
279
|
+
const present = true === input.cursorProjectPresent || (cursorGlobal && !input.claudeCli);
|
|
308
280
|
return commandStepFor(agentFile(input, CURSOR_COMMAND_PATH), present, input.cursorCommandContent);
|
|
309
281
|
}
|
|
310
282
|
const AGENT_RULE_TITLE = 'Agent verification rule';
|
|
@@ -339,7 +311,8 @@ function claudeRuleStep(input) {
|
|
|
339
311
|
* committed into their repo. (Global MCP registration is different: it is global, and stays.)
|
|
340
312
|
*/
|
|
341
313
|
function cursorRuleStep(input) {
|
|
342
|
-
|
|
314
|
+
const cursorGlobal = (input.detectedClients ?? []).some((c) => c.id === McpClient.CURSOR);
|
|
315
|
+
if (!cursorGlobal && true !== input.cursorProjectPresent)
|
|
343
316
|
return null;
|
|
344
317
|
if (input.cursorProjectPresent !== true && input.claudeCli)
|
|
345
318
|
return null;
|
|
@@ -389,14 +362,13 @@ const AGENT_INTEGRATIONS = [
|
|
|
389
362
|
},
|
|
390
363
|
{
|
|
391
364
|
id: AgentId.CURSOR,
|
|
392
|
-
mcpStep: cursorMcpStep,
|
|
393
365
|
ruleStep: cursorRuleStep,
|
|
394
366
|
commandStep: cursorCommandStep,
|
|
395
367
|
},
|
|
396
368
|
];
|
|
397
369
|
/** The steps one surface contributes across every known agent, in registry order. */
|
|
398
370
|
function stepsForAgents(input, surface) {
|
|
399
|
-
return AGENT_INTEGRATIONS.map((a) => surface(a)(input)).filter((s) => s !== null);
|
|
371
|
+
return AGENT_INTEGRATIONS.map((a) => surface(a)?.(input) ?? null).filter((s) => s !== null);
|
|
400
372
|
}
|
|
401
373
|
/** The `/reticle` command file for every agent that has a command surface and is present here. */
|
|
402
374
|
function slashCommandSteps(input) {
|
|
@@ -503,15 +475,6 @@ function unpinnedRetryNote(version, pm) {
|
|
|
503
475
|
`installed instead. That may not match the daemon — if the agent reports protocol errors, check ` +
|
|
504
476
|
`\`versionSkew\` in reticle_sessions.${remedy}`);
|
|
505
477
|
}
|
|
506
|
-
function installFailureHint(pm) {
|
|
507
|
-
if (pm !== PackageManager.PNPM)
|
|
508
|
-
return 'If the version was refused, install the SDK yourself.';
|
|
509
|
-
return ('If pnpm reported ERR_PNPM_NO_MATURE_MATCHING_VERSION, its minimumReleaseAge setting is holding ' +
|
|
510
|
-
'this release back. Either wait out the window, or allow these packages explicitly:\n' +
|
|
511
|
-
' pnpm config set minimumReleaseAgeExclude "@reticlehq/*"\n' +
|
|
512
|
-
'Do NOT drop the version pin — unpinned, pnpm installs an older SDK against a newer daemon, and ' +
|
|
513
|
-
'that mismatch surfaces as a -32000 with nothing naming a version.');
|
|
514
|
-
}
|
|
515
478
|
function installStep(input) {
|
|
516
479
|
const pm = input.detection.packageManager;
|
|
517
480
|
const packages = pinnedPackages(frameworkPackages(input.detection.framework, input.detection.uiLibrary), input.options.sdkVersion);
|
|
@@ -629,6 +592,18 @@ function reticleConfigStep(input, content) {
|
|
|
629
592
|
detail: problem,
|
|
630
593
|
};
|
|
631
594
|
}
|
|
595
|
+
// The one thing a re-run can still learn: which channel the user actually arrived through.
|
|
596
|
+
// See configWithInstallSource — it only ever ADDS a field that is absent.
|
|
597
|
+
const backfilled = configWithInstallSource(input.reticleConfigSource, declaredInstallSource());
|
|
598
|
+
if (backfilled !== undefined) {
|
|
599
|
+
return {
|
|
600
|
+
title: RETICLE_CONFIG_TITLE,
|
|
601
|
+
target: RETICLE_CONFIG_FILE,
|
|
602
|
+
status: StepStatus.APPLY,
|
|
603
|
+
detail: 'record which install route this project came through',
|
|
604
|
+
write: { path: RETICLE_CONFIG_FILE, content: backfilled },
|
|
605
|
+
};
|
|
606
|
+
}
|
|
632
607
|
return {
|
|
633
608
|
title: RETICLE_CONFIG_TITLE,
|
|
634
609
|
target: RETICLE_CONFIG_FILE,
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The two conditions that make every later phase fail, checked before anything is written.
|
|
3
|
+
*
|
|
4
|
+
* Both lived in setup/reticle.mjs and neither survived the port into `init`. What they buy is the
|
|
5
|
+
* distance between a failure and its cause: without them, an unwritable checkout arrives as EACCES
|
|
6
|
+
* in a stack trace at phase four, and a missing package manager arrives as `spawn pnpm ENOENT`
|
|
7
|
+
* inside "the dev server exited" — which sends the reader into their own dev script hunting a bug
|
|
8
|
+
* that is not there.
|
|
9
|
+
*/
|
|
10
|
+
/** The parts of the environment preflight reads, so the rules are testable without a filesystem. */
|
|
11
|
+
export interface PreflightIo {
|
|
12
|
+
/** Absolute path of the directory init is running in. */
|
|
13
|
+
cwd(): string;
|
|
14
|
+
/** Can this process write into the project root. */
|
|
15
|
+
canWrite(): boolean;
|
|
16
|
+
/** Runs a command quietly for a yes/no check; true on exit code 0. */
|
|
17
|
+
probe(command: string, args: readonly string[]): boolean;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* The refusal to print, or undefined when this machine can run the install.
|
|
21
|
+
*
|
|
22
|
+
* `packageManager` is the one init RESOLVED, never a raw lockfile check. An inherited
|
|
23
|
+
* `pnpm-lock.yaml` at a monorepo root does not mean the app in `frontend/` uses pnpm — that app's own
|
|
24
|
+
* installed tree outranks an ancestor lockfile, and detect.ts already works this out. Re-deriving it
|
|
25
|
+
* here from `exists('pnpm-lock.yaml')` refused an npm app sitting under a pnpm monorepo on a machine
|
|
26
|
+
* with no pnpm, which the install gate proves must succeed.
|
|
27
|
+
*/
|
|
28
|
+
export declare function preflightRefusal(io: PreflightIo, packageManager: string): string | undefined;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The two conditions that make every later phase fail, checked before anything is written.
|
|
3
|
+
*
|
|
4
|
+
* Both lived in setup/reticle.mjs and neither survived the port into `init`. What they buy is the
|
|
5
|
+
* distance between a failure and its cause: without them, an unwritable checkout arrives as EACCES
|
|
6
|
+
* in a stack trace at phase four, and a missing package manager arrives as `spawn pnpm ENOENT`
|
|
7
|
+
* inside "the dev server exited" — which sends the reader into their own dev script hunting a bug
|
|
8
|
+
* that is not there.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* npm ships with node, so refusing for its absence would refuse on a machine that is fine.
|
|
12
|
+
*/
|
|
13
|
+
const ALWAYS_PRESENT = 'npm';
|
|
14
|
+
/**
|
|
15
|
+
* The refusal to print, or undefined when this machine can run the install.
|
|
16
|
+
*
|
|
17
|
+
* `packageManager` is the one init RESOLVED, never a raw lockfile check. An inherited
|
|
18
|
+
* `pnpm-lock.yaml` at a monorepo root does not mean the app in `frontend/` uses pnpm — that app's own
|
|
19
|
+
* installed tree outranks an ancestor lockfile, and detect.ts already works this out. Re-deriving it
|
|
20
|
+
* here from `exists('pnpm-lock.yaml')` refused an npm app sitting under a pnpm monorepo on a machine
|
|
21
|
+
* with no pnpm, which the install gate proves must succeed.
|
|
22
|
+
*/
|
|
23
|
+
export function preflightRefusal(io, packageManager) {
|
|
24
|
+
// First: on a read-only checkout nothing else matters, and one access check is cheaper and
|
|
25
|
+
// quieter than spawning a subprocess to discover the same thing.
|
|
26
|
+
if (!io.canWrite()) {
|
|
27
|
+
return (`${io.cwd()} is not writable, and init has to write into it (.reticle.json, the build config, ` +
|
|
28
|
+
'a capabilities file). Fix the permissions, or run init from a checkout you own.');
|
|
29
|
+
}
|
|
30
|
+
// What the project resolves to says nothing about what the machine HAS, and a project committed to
|
|
31
|
+
// pnpm on an npm-only box is an ordinary Monday.
|
|
32
|
+
if (ALWAYS_PRESENT !== packageManager && !io.probe(packageManager, ['--version'])) {
|
|
33
|
+
return (`this project uses ${packageManager} (its lockfile says so) and ${packageManager} is not ` +
|
|
34
|
+
`installed on this machine. Install it (npm i -g ${packageManager}, or corepack enable), or ` +
|
|
35
|
+
'pass --url with the address the app already serves.');
|
|
36
|
+
}
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
package/dist/init/run.d.ts
CHANGED
|
@@ -18,6 +18,8 @@ import type { InitOutcome } from '@reticlehq/core';
|
|
|
18
18
|
*/
|
|
19
19
|
export declare function resolveLockfiles(rootFiles: ReadonlySet<string>, cwd: string, io: Pick<InitIo, 'exists'>, nodeModulesMarkers?: ReadonlySet<string>): Set<string>;
|
|
20
20
|
export interface InitOptions {
|
|
21
|
+
/** `--capture-bodies`: write `captureNetworkBodies: true` into the app's config. Off by default (#705). */
|
|
22
|
+
captureBodies?: boolean | undefined;
|
|
21
23
|
cwd: string;
|
|
22
24
|
port: number | undefined;
|
|
23
25
|
mcp: boolean;
|
|
@@ -47,6 +49,13 @@ export interface InitOptions {
|
|
|
47
49
|
* today's fire-and-forget behaviour, so no path loses its event by forgetting to report.
|
|
48
50
|
*/
|
|
49
51
|
deferOutcome?: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Whether the caller carries on into booting the app and driving it.
|
|
54
|
+
*
|
|
55
|
+
* Only affects the closing hint, which otherwise tells the reader to restart their dev server and
|
|
56
|
+
* drive a flow by hand — three lines before this command does both.
|
|
57
|
+
*/
|
|
58
|
+
continuesToRuntime?: boolean;
|
|
50
59
|
}
|
|
51
60
|
export interface InitIo {
|
|
52
61
|
/** Returns file content or null if it does not exist. Path is project-relative or absolute. */
|
|
@@ -70,25 +79,39 @@ export interface InitIo {
|
|
|
70
79
|
exec(command: string, args: readonly string[]): boolean;
|
|
71
80
|
/** Runs a subprocess quietly (no stdio) for a yes/no check; returns true on exit code 0. */
|
|
72
81
|
probe(command: string, args: readonly string[]): boolean;
|
|
82
|
+
/** Can this process write into the project root — see preflight.ts. */
|
|
83
|
+
canWrite(): boolean;
|
|
73
84
|
print(line: string): void;
|
|
74
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* What a run established, for a caller that means to continue where init stopped.
|
|
88
|
+
*
|
|
89
|
+
* Everything here was already computed and then thrown away. That was fine while init only wrote
|
|
90
|
+
* files: nobody downstream existed. A caller that goes on to boot the app needs the same answers,
|
|
91
|
+
* and re-deriving them is how two parts of one command end up disagreeing about which directory
|
|
92
|
+
* they are in — which is not hypothetical, because a monorepo redirect re-enters `runInit` with a
|
|
93
|
+
* different cwd and the outer caller never learns that it happened.
|
|
94
|
+
*/
|
|
95
|
+
export interface InitContext {
|
|
96
|
+
/** The directory actually wired, AFTER any monorepo redirect. */
|
|
97
|
+
readonly appDir: string;
|
|
98
|
+
readonly framework: string;
|
|
99
|
+
readonly packageManager: string;
|
|
100
|
+
/** The project's own dev command, when its scripts name one. Never composed. */
|
|
101
|
+
readonly devCommand?: string | undefined;
|
|
102
|
+
/** Set when init redirected into a workspace app, naming the one it chose. */
|
|
103
|
+
readonly redirectedTo?: string | undefined;
|
|
104
|
+
}
|
|
75
105
|
export interface InitResult {
|
|
76
106
|
ok: boolean;
|
|
77
107
|
applied: number;
|
|
78
108
|
manual: number;
|
|
109
|
+
/** What this run established. Absent only where init exits before establishing anything. */
|
|
110
|
+
context?: InitContext;
|
|
79
111
|
/**
|
|
80
112
|
* The event body this run would report, handed to the caller instead of emitted, when
|
|
81
113
|
* `deferOutcome` is set. Absent on a dry run and on the exits that report for themselves.
|
|
82
114
|
*/
|
|
83
115
|
outcome?: InitOutcome;
|
|
84
116
|
}
|
|
85
|
-
/**
|
|
86
|
-
* App directories under a workspace root.
|
|
87
|
-
*
|
|
88
|
-
* Running `reticle init` at the repo root is what people actually do, and in a monorepo the app is a
|
|
89
|
-
* directory down — so init detected "no framework", printed a wall of manual HTML instructions, and
|
|
90
|
-
* would have installed the SDK into the ROOT package.json. It already walks UP for the lockfile, so
|
|
91
|
-
* it knows it is in a workspace; this is the matching walk DOWN.
|
|
92
|
-
*/
|
|
93
|
-
export declare function findWorkspaceApps(io: Pick<InitIo, 'exists' | 'readFile' | 'listDirs'>): string[];
|
|
94
117
|
export declare function runInit(options: InitOptions, io: InitIo): InitResult;
|