@reticlehq/server 2.11.0 → 2.12.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 +26 -2
- package/dist/bridge/bridge.d.ts +0 -13
- package/dist/bridge/bridge.js +1 -1
- package/dist/bridge/bridge.test-harness.d.ts +2 -0
- package/dist/bridge/bridge.test-harness.js +8 -1
- package/dist/capsule/capsule-store.d.ts +2 -1
- package/dist/capsule/capsule-store.js +1 -1
- package/dist/capsule/causal-summary.d.ts +2 -2
- package/dist/chromium-launch-options.d.ts +28 -0
- package/dist/chromium-launch-options.js +29 -0
- package/dist/cli/cli-doctor.js +8 -0
- package/dist/cli/cli-flow-commands.d.ts +3 -1
- package/dist/cli/cli-flow-commands.js +34 -4
- package/dist/cli/cli-kill.d.ts +2 -1
- package/dist/cli/cli-parse.d.ts +3 -1
- package/dist/cli/cli-parse.js +29 -7
- package/dist/cli/cli-update-commands.js +20 -1
- package/dist/cli/cli-verify.d.ts +1 -1
- package/dist/cli/cli-verify.js +2 -2
- package/dist/cli/cloud-cli.js +12 -6
- package/dist/cli/config-discovery.d.ts +2 -1
- package/dist/cli/doctor-rows.d.ts +3 -1
- package/dist/cli/doctor-rows.js +3 -1
- package/dist/cli/gate-exit.d.ts +21 -0
- package/dist/cli/gate-exit.js +21 -0
- package/dist/cli/gate-hook-message.d.ts +33 -0
- package/dist/cli/gate-hook-message.js +46 -0
- package/dist/cli/mcp-command.d.ts +25 -0
- package/dist/cli/mcp-command.js +143 -0
- package/dist/cli/sibling-ports.d.ts +37 -0
- package/dist/cli/sibling-ports.js +56 -0
- package/dist/cli.js +37 -110
- package/dist/cloud/cloud-config.d.ts +2 -1
- package/dist/cloud/cloud-sync.d.ts +11 -0
- package/dist/cloud/cloud-sync.js +32 -0
- package/dist/crawl/crawl.d.ts +9 -13
- package/dist/crawl/crawl.js +20 -3
- package/dist/daemon/daemon-resolve.d.ts +79 -0
- package/dist/daemon/daemon-resolve.js +118 -0
- package/dist/daemon/free-port.d.ts +29 -0
- package/dist/daemon/free-port.js +55 -0
- package/dist/daemon/heartbeat.d.ts +0 -4
- package/dist/daemon/heartbeat.js +2 -2
- package/dist/dev/stale-issue-guard.d.ts +0 -2
- package/dist/dev/stale-issue-guard.js +1 -1
- package/dist/events/contradictions.d.ts +41 -0
- package/dist/events/contradictions.js +125 -12
- package/dist/events/declared.d.ts +14 -0
- package/dist/events/declared.js +33 -0
- package/dist/events/echo-mismatch.js +14 -0
- package/dist/events/observed-in-window.d.ts +12 -0
- package/dist/events/observed-in-window.js +17 -0
- package/dist/events/predicate-asks.d.ts +13 -0
- package/dist/events/predicate-asks.js +22 -0
- package/dist/events/predicate-eval.js +64 -22
- package/dist/events/predicate-parse.d.ts +0 -6
- package/dist/events/predicate-parse.js +43 -3
- package/dist/events/predicate-schema.d.ts +44 -0
- package/dist/events/predicate-schema.js +126 -23
- package/dist/events/predicate.d.ts +15 -0
- package/dist/events/predicate.js +41 -2
- package/dist/flows/coverage.d.ts +0 -8
- package/dist/flows/coverage.js +1 -1
- package/dist/flows/decision.d.ts +5 -1
- package/dist/flows/decision.js +33 -7
- package/dist/flows/flake.d.ts +0 -2
- package/dist/flows/flake.js +1 -1
- package/dist/flows/flow-intent.d.ts +52 -0
- package/dist/flows/flow-intent.js +123 -0
- package/dist/flows/flow-replay-run.js +27 -4
- package/dist/flows/flow-success.js +15 -3
- package/dist/flows/flow-tools.js +32 -4
- package/dist/flows/flows.d.ts +6 -1
- package/dist/flows/flows.js +34 -22
- package/dist/flows/predicate-to-expect.js +6 -3
- package/dist/flows/server-verify.js +16 -2
- package/dist/flows/verify-change-tools.js +4 -1
- package/dist/honesty/blind-spots.d.ts +0 -17
- package/dist/honesty/blind-spots.js +1 -1
- package/dist/honesty/feature-capture.d.ts +150 -0
- package/dist/honesty/feature-capture.js +233 -0
- package/dist/honesty/instrumentation-gaps.d.ts +30 -6
- package/dist/honesty/instrumentation-gaps.js +61 -5
- package/dist/honesty/tool-hit-rate.d.ts +86 -0
- package/dist/honesty/tool-hit-rate.js +90 -0
- package/dist/honesty/undeclared-change.d.ts +51 -0
- package/dist/honesty/undeclared-change.js +56 -0
- package/dist/honesty/verified.d.ts +15 -0
- package/dist/honesty/verified.js +68 -4
- package/dist/http-server.d.ts +0 -1
- package/dist/http-server.js +1 -1
- package/dist/hunt/hunt-report.d.ts +0 -2
- package/dist/hunt/hunt-report.js +1 -1
- package/dist/impact/impact-store.d.ts +0 -5
- package/dist/impact/impact-store.js +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/init/agent-rules.d.ts +17 -0
- package/dist/init/agent-rules.js +31 -1
- package/dist/init/capabilities.d.ts +0 -2
- package/dist/init/capabilities.js +1 -1
- package/dist/init/closing-hint.d.ts +36 -0
- package/dist/init/closing-hint.js +120 -0
- package/dist/init/confirm.d.ts +0 -25
- package/dist/init/confirm.js +3 -3
- package/dist/init/cra.d.ts +14 -1
- package/dist/init/cra.js +43 -12
- package/dist/init/dev-script.d.ts +48 -0
- package/dist/init/dev-script.js +63 -0
- package/dist/init/mcp.d.ts +16 -5
- package/dist/init/mcp.js +26 -10
- package/dist/init/plan-framework.d.ts +0 -5
- package/dist/init/plan-framework.js +83 -9
- package/dist/init/plan.d.ts +10 -30
- package/dist/init/plan.js +30 -15
- package/dist/init/refresh-rules.d.ts +39 -0
- package/dist/init/refresh-rules.js +51 -0
- package/dist/init/run.d.ts +0 -26
- package/dist/init/run.js +14 -87
- package/dist/init/snippets.js +53 -10
- package/dist/input/real-input.d.ts +2 -0
- package/dist/input/real-input.js +3 -2
- package/dist/input/scroll-find.d.ts +7 -4
- package/dist/input/scroll-find.js +63 -25
- package/dist/intent/inline-intent.d.ts +46 -0
- package/dist/intent/inline-intent.js +103 -0
- package/dist/intent/intent-store.d.ts +37 -0
- package/dist/intent/intent-store.js +112 -0
- package/dist/intent/intent-tools.d.ts +2 -0
- package/dist/intent/intent-tools.js +80 -0
- package/dist/intent/open-intents.d.ts +21 -0
- package/dist/intent/open-intents.js +25 -0
- package/dist/journal/envelope.d.ts +26 -26
- package/dist/journal/journal-recorder.d.ts +23 -0
- package/dist/journal/journal-recorder.js +35 -0
- package/dist/mcp/attach-memory.d.ts +67 -0
- package/dist/mcp/attach-memory.js +142 -0
- package/dist/mcp/mcp-proxy.d.ts +0 -7
- package/dist/mcp/mcp-proxy.js +14 -2
- package/dist/mcp/mcp.d.ts +2 -0
- package/dist/mcp/mcp.js +45 -23
- package/dist/mcp/server-instructions.js +45 -3
- package/dist/mcp/tool-catalog-cache.d.ts +8 -0
- package/dist/mcp/tool-catalog-cache.js +10 -0
- package/dist/oracles/flow-instrument-gaps.d.ts +4 -4
- package/dist/oracles/flow-instrument-gaps.js +4 -5
- package/dist/oracles/self-instrument.d.ts +9 -9
- package/dist/oracles/self-instrument.js +19 -13
- package/dist/pool/browser-pool.d.ts +12 -1
- package/dist/pool/browser-pool.js +31 -1
- package/dist/pool/playwright-launcher.js +3 -1
- package/dist/project/fs-port.d.ts +3 -0
- package/dist/project/fs-port.js +3 -2
- package/dist/project/memory-fs.js +6 -1
- package/dist/project/project-tools.js +3 -32
- package/dist/project/reticle-dir.d.ts +2 -0
- package/dist/project/reticle-dir.js +1 -0
- package/dist/runs/context-tools.d.ts +2 -0
- package/dist/runs/context-tools.js +89 -0
- package/dist/runs/run-context.d.ts +40 -0
- package/dist/runs/run-context.js +158 -0
- package/dist/runs/run-diff.d.ts +2 -1
- package/dist/runs/verification-sync.js +2 -2
- package/dist/session/dev-server-probe.d.ts +0 -8
- package/dist/session/dev-server-probe.js +1 -1
- package/dist/session/last-act.d.ts +7 -1
- package/dist/session/last-act.js +2 -2
- package/dist/session/live-control-tools.js +1 -42
- package/dist/session/no-session-diagnosis.d.ts +10 -0
- package/dist/session/no-session-diagnosis.js +24 -7
- package/dist/session/no-session-next-action.d.ts +5 -0
- package/dist/session/no-session-next-action.js +35 -3
- package/dist/session/no-session-watch.d.ts +8 -0
- package/dist/session/no-session-watch.js +82 -64
- package/dist/session/output-budget.js +4 -1
- package/dist/session/session-health.d.ts +9 -0
- package/dist/session/session-health.js +23 -0
- package/dist/session/session-manager.js +50 -1
- package/dist/session/session-successor.d.ts +37 -0
- package/dist/session/session-successor.js +78 -0
- package/dist/session/session.d.ts +31 -1
- package/dist/session/session.js +75 -2
- package/dist/telemetry/automation-hint.d.ts +2 -1
- package/dist/telemetry/feedback-outbox.d.ts +2 -1
- package/dist/telemetry/project-profile.d.ts +0 -8
- package/dist/telemetry/project-profile.js +1 -1
- package/dist/telemetry/run-telemetry.d.ts +0 -2
- package/dist/telemetry/run-telemetry.js +1 -1
- package/dist/telemetry/verification-of.js +2 -2
- package/dist/tools/act-tools.js +126 -21
- package/dist/tools/assert-source.d.ts +22 -0
- package/dist/tools/assert-source.js +53 -0
- package/dist/tools/assert-verdict.d.ts +20 -3
- package/dist/tools/assert-verdict.js +36 -4
- package/dist/tools/browser-tools.js +0 -20
- package/dist/tools/complete-snapshot.d.ts +48 -0
- package/dist/tools/complete-snapshot.js +75 -0
- package/dist/tools/coverage-tools.d.ts +10 -1
- package/dist/tools/coverage-tools.js +123 -69
- package/dist/tools/dynamic-tools.js +29 -8
- package/dist/tools/error-recovery.d.ts +24 -14
- package/dist/tools/error-recovery.js +70 -9
- package/dist/tools/feedback-tools.d.ts +1 -1
- package/dist/tools/feedback-tools.js +2 -2
- package/dist/tools/invoke-tool.js +62 -2
- package/dist/tools/lease-hint.d.ts +10 -0
- package/dist/tools/lease-hint.js +53 -4
- package/dist/tools/lease-tools.d.ts +5 -1
- package/dist/tools/lease-tools.js +22 -8
- package/dist/tools/navigate-arrival.d.ts +0 -2
- package/dist/tools/navigate-arrival.js +1 -1
- package/dist/tools/observe-tools.js +63 -26
- package/dist/tools/read-tools.js +5 -3
- package/dist/tools/real-input-attempt.d.ts +22 -0
- package/dist/tools/real-input-attempt.js +153 -1
- package/dist/tools/reconcile-tools.js +18 -4
- package/dist/tools/resolve-target.d.ts +0 -7
- package/dist/tools/sdk-marker-probe.d.ts +29 -0
- package/dist/tools/sdk-marker-probe.js +57 -0
- package/dist/tools/snapshot-delta.d.ts +1 -1
- package/dist/tools/tool-kit.d.ts +12 -0
- package/dist/tools/tool-kit.js +16 -0
- package/dist/tools/tool-names.d.ts +3 -0
- package/dist/tools/tool-names.js +3 -0
- package/dist/tools/tool-surface.d.ts +85 -6
- package/dist/tools/tool-surface.js +122 -2
- package/dist/tools/tools.js +22 -4
- package/dist/tools/unadvertised-help.js +4 -4
- package/dist/tools/verify-next-baton.d.ts +53 -0
- package/dist/tools/verify-next-baton.js +52 -0
- package/dist/update/update-nudge.d.ts +7 -10
- package/dist/update/update-nudge.js +12 -3
- package/dist/update/updater.d.ts +1 -1
- package/docs/README.md +2 -1
- package/docs/cli/cloud.mdx +1 -1
- package/docs/cli/doctor.mdx +1 -0
- package/docs/cli/verify.mdx +4 -5
- package/docs/desktop.mdx +1 -1
- package/docs/docs.json +2 -2
- package/docs/gates.md +1 -1
- package/docs/install-manual.mdx +1 -1
- package/docs/packages/browser.mdx +2 -2
- package/docs/packages/server.mdx +1 -1
- package/docs/packages.mdx +8 -6
- package/docs/platform-integration.md +17 -0
- package/docs/predicates.mdx +8 -0
- package/docs/reticle-manual.md +106 -0
- package/docs/tools-tools-and-run.mdx +2 -4
- package/docs/troubleshooting.mdx +9 -0
- package/docs/usage.md +3 -2
- package/package.json +2 -2
- package/dist/session/session-readiness.d.ts +0 -34
- package/dist/session/session-readiness.js +0 -36
package/dist/init/cra.js
CHANGED
|
@@ -12,12 +12,26 @@
|
|
|
12
12
|
* `REACT_APP_`. So it travels through `.env.development.local`, which is CRA's own documented
|
|
13
13
|
* mechanism and is gitignored by CRA's own template.
|
|
14
14
|
*/
|
|
15
|
+
import { bridgeWsUrl } from '@reticlehq/core';
|
|
15
16
|
import { CLI } from './agent-rules.js';
|
|
16
17
|
/** The line added to `src/index.tsx`. Side-effect import: the module guards itself on NODE_ENV. */
|
|
17
18
|
export const CRA_DEV_MODULE_IMPORT = "import './reticle-dev';";
|
|
18
19
|
export const CRA_DEV_MODULE_PATH = 'src/reticle-dev.ts';
|
|
19
20
|
export const CRA_ENV_PATH = '.env.development.local';
|
|
20
21
|
export const TOKEN_VAR = 'REACT_APP_RETICLE_TOKEN';
|
|
22
|
+
/**
|
|
23
|
+
* The bridge URL, carried through the same channel as the token.
|
|
24
|
+
*
|
|
25
|
+
* CRA is the one supported stack with no build-time hook of ours: no plugin, no config wrapper, no
|
|
26
|
+
* `define`. Vite and Astro re-resolve the daemon every dev-server start and Next does it through
|
|
27
|
+
* `withReticle`; CRA has nowhere to run that code. So the URL goes where the token already goes, and
|
|
28
|
+
* is refreshed by the same `init` step that refreshes the token.
|
|
29
|
+
*
|
|
30
|
+
* That makes `reticle init` the heal rather than the dev server, which is weaker and is stated
|
|
31
|
+
* plainly in the step's own detail. It is still strictly better than a URL frozen into a source file
|
|
32
|
+
* the owner has to hand-edit, and `.env.development.local` is already the per-machine file.
|
|
33
|
+
*/
|
|
34
|
+
export const URL_VAR = 'REACT_APP_RETICLE_URL';
|
|
21
35
|
/**
|
|
22
36
|
* What the app itself says when the token is not there.
|
|
23
37
|
*
|
|
@@ -64,25 +78,38 @@ export function craImportPatch(source) {
|
|
|
64
78
|
return lines.join('\n');
|
|
65
79
|
}
|
|
66
80
|
/** Set the token variable, or null when nothing needs to change. */
|
|
67
|
-
export function craEnvPatch(existing, token) {
|
|
81
|
+
export function craEnvPatch(existing, token, url) {
|
|
68
82
|
if ('' === token)
|
|
69
83
|
return null;
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
84
|
+
const pairs = [
|
|
85
|
+
[TOKEN_VAR, token],
|
|
86
|
+
...(url !== undefined && url.length > 0 ? [[URL_VAR, url]] : []),
|
|
87
|
+
];
|
|
88
|
+
let next = null === existing || '' === existing.trim() ? '' : existing;
|
|
89
|
+
for (const [name, value] of pairs) {
|
|
90
|
+
const line = `${name}=${value}`;
|
|
91
|
+
if (next.includes(line))
|
|
92
|
+
continue;
|
|
93
|
+
// Replace rather than append: two assignments of one variable is a silent coin flip on which
|
|
94
|
+
// wins, and for the URL that coin decides which daemon the app talks to.
|
|
95
|
+
if (new RegExp(`^${name}=`, 'm').test(next)) {
|
|
96
|
+
next = next.replace(new RegExp(`^${name}=.*$`, 'm'), line);
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
next = `${'' === next || next.endsWith('\n') ? next : `${next}\n`}${line}\n`;
|
|
100
|
+
}
|
|
78
101
|
}
|
|
79
|
-
return
|
|
102
|
+
return next === (existing ?? '') ? null : next;
|
|
80
103
|
}
|
|
81
104
|
/** The dev-only connect module imported from `src/index.tsx`. */
|
|
82
105
|
export function craDevModuleFile(port, projectId) {
|
|
83
106
|
const fields = [];
|
|
107
|
+
// `bridgeWsUrl`, not a hand-written string. This was the only client URL in the product spelling
|
|
108
|
+
// the host as `127.0.0.1` while every other generator said `localhost` — same endpoint, but a
|
|
109
|
+
// difference with no reason behind it is the kind that becomes a real one after somebody edits half
|
|
110
|
+
// of it.
|
|
84
111
|
if (port !== undefined)
|
|
85
|
-
fields.push(`url: '
|
|
112
|
+
fields.push(`url: '${bridgeWsUrl(port)}'`);
|
|
86
113
|
if (projectId !== undefined && projectId.length > 0)
|
|
87
114
|
fields.push(`projectId: '${projectId}'`);
|
|
88
115
|
const inline = fields.length > 0 ? `${fields.join(', ')}, ` : '';
|
|
@@ -95,10 +122,14 @@ if (process.env.NODE_ENV === 'development') {
|
|
|
95
122
|
void import('@reticlehq/react').then(({ reticle, install }) => {
|
|
96
123
|
install();
|
|
97
124
|
const token = process.env.${TOKEN_VAR} ?? '';
|
|
125
|
+
// Written by \`reticle init\` from the daemon that was live when it ran, and refreshed by
|
|
126
|
+
// re-running it. CRA gives us no hook to resolve this at dev-server start, so if the daemon
|
|
127
|
+
// moves, re-run \`reticle init\` rather than editing the url below.
|
|
128
|
+
const url = process.env.${URL_VAR} ?? '';
|
|
98
129
|
// Loud on purpose: without this the only symptom is the bridge's generic auth failure.
|
|
99
130
|
if (token.length === 0) console.error(${JSON.stringify(`[reticle] ${CRA_TOKEN_MISSING_NOTE}`)});
|
|
100
131
|
// Still attempt it — a bridge running without a token pairs fine.
|
|
101
|
-
reticle.connect({ ${inline}...(token.length > 0 ? { token } : {}) });
|
|
132
|
+
reticle.connect({ ${inline}...(url.length > 0 ? { url } : {}), ...(token.length > 0 ? { token } : {}) });
|
|
102
133
|
});
|
|
103
134
|
}
|
|
104
135
|
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which command starts this project, and should we start it ourselves?
|
|
3
|
+
*
|
|
4
|
+
* `init` finishes by telling the human to start or restart their dev server, in a closing
|
|
5
|
+
* paragraph, in a terminal that is about to be cleared by the client restart it also asks for.
|
|
6
|
+
* That is the second unmarked hand-off in the install, after the capabilities file, and it lands
|
|
7
|
+
* on the same surface: prose nobody re-reads.
|
|
8
|
+
*
|
|
9
|
+
* Every fact needed to remove it is already in the project. `package.json` names the script; the
|
|
10
|
+
* framework tells us the conventional port; a probe tells us whether something already answers
|
|
11
|
+
* there. Nothing here needs a human.
|
|
12
|
+
*
|
|
13
|
+
* Kept as a PURE decision, separate from spawning, because the interesting part is the judgement —
|
|
14
|
+
* which script, and whether to touch anything at all — and that is what wants testing. Spawning a
|
|
15
|
+
* process is not.
|
|
16
|
+
*/
|
|
17
|
+
export declare const DevScriptChoice: {
|
|
18
|
+
/** Something already answers on the port. Use it; never replace a server the user is running. */
|
|
19
|
+
readonly ALREADY_SERVING: "already-serving";
|
|
20
|
+
/** We know the command and nothing is listening. Start it. */
|
|
21
|
+
readonly START: "start";
|
|
22
|
+
/** No script we recognise. Say so rather than guess a command. */
|
|
23
|
+
readonly NO_SCRIPT: "no-script";
|
|
24
|
+
};
|
|
25
|
+
export type DevScriptChoice = (typeof DevScriptChoice)[keyof typeof DevScriptChoice];
|
|
26
|
+
export interface DevScriptPlan {
|
|
27
|
+
choice: DevScriptChoice;
|
|
28
|
+
/** The npm script name, when there is one. */
|
|
29
|
+
script?: string;
|
|
30
|
+
/** What a human would type, for the line we print. */
|
|
31
|
+
command?: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Pick the script and decide whether to run it.
|
|
35
|
+
*
|
|
36
|
+
* `serving` is the probe result, passed in rather than measured here so the decision stays pure.
|
|
37
|
+
* When something is already answering we do not care which script exists: the rule is never to
|
|
38
|
+
* start a second server, because the one that is running may be the user's, with their state in it.
|
|
39
|
+
*/
|
|
40
|
+
export declare function planDevScript(scripts: Readonly<Record<string, string>>, packageManager: string, serving: boolean): DevScriptPlan;
|
|
41
|
+
/**
|
|
42
|
+
* The command to print for this project, straight from its `package.json`.
|
|
43
|
+
*
|
|
44
|
+
* Lives here rather than in `run.ts` because it is the same judgement as `planDevScript` with the
|
|
45
|
+
* parsing attached, and `run.ts` is at its cohesion limit. Returns undefined for anything it cannot
|
|
46
|
+
* read: this phrasing decides nothing, so it must never be able to break an install.
|
|
47
|
+
*/
|
|
48
|
+
export declare function devCommandFrom(pkgRaw: string, packageManager: string): string | undefined;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which command starts this project, and should we start it ourselves?
|
|
3
|
+
*
|
|
4
|
+
* `init` finishes by telling the human to start or restart their dev server, in a closing
|
|
5
|
+
* paragraph, in a terminal that is about to be cleared by the client restart it also asks for.
|
|
6
|
+
* That is the second unmarked hand-off in the install, after the capabilities file, and it lands
|
|
7
|
+
* on the same surface: prose nobody re-reads.
|
|
8
|
+
*
|
|
9
|
+
* Every fact needed to remove it is already in the project. `package.json` names the script; the
|
|
10
|
+
* framework tells us the conventional port; a probe tells us whether something already answers
|
|
11
|
+
* there. Nothing here needs a human.
|
|
12
|
+
*
|
|
13
|
+
* Kept as a PURE decision, separate from spawning, because the interesting part is the judgement —
|
|
14
|
+
* which script, and whether to touch anything at all — and that is what wants testing. Spawning a
|
|
15
|
+
* process is not.
|
|
16
|
+
*/
|
|
17
|
+
/** Script names a JS project uses to run itself, in the order we would pick them. */
|
|
18
|
+
const CANDIDATES = ['dev', 'start', 'serve'];
|
|
19
|
+
export const DevScriptChoice = {
|
|
20
|
+
/** Something already answers on the port. Use it; never replace a server the user is running. */
|
|
21
|
+
ALREADY_SERVING: 'already-serving',
|
|
22
|
+
/** We know the command and nothing is listening. Start it. */
|
|
23
|
+
START: 'start',
|
|
24
|
+
/** No script we recognise. Say so rather than guess a command. */
|
|
25
|
+
NO_SCRIPT: 'no-script',
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Pick the script and decide whether to run it.
|
|
29
|
+
*
|
|
30
|
+
* `serving` is the probe result, passed in rather than measured here so the decision stays pure.
|
|
31
|
+
* When something is already answering we do not care which script exists: the rule is never to
|
|
32
|
+
* start a second server, because the one that is running may be the user's, with their state in it.
|
|
33
|
+
*/
|
|
34
|
+
export function planDevScript(scripts, packageManager, serving) {
|
|
35
|
+
if (serving)
|
|
36
|
+
return { choice: DevScriptChoice.ALREADY_SERVING };
|
|
37
|
+
const name = CANDIDATES.find((c) => 'string' === typeof scripts[c] && scripts[c] !== '');
|
|
38
|
+
if (name === undefined)
|
|
39
|
+
return { choice: DevScriptChoice.NO_SCRIPT };
|
|
40
|
+
// `npm` needs `run`; the others take the script name directly. Getting this wrong prints a
|
|
41
|
+
// command that fails, which is worse than printing nothing.
|
|
42
|
+
const command = 'npm' === packageManager ? `npm run ${name}` : `${packageManager} ${name}`;
|
|
43
|
+
return { choice: DevScriptChoice.START, script: name, command };
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The command to print for this project, straight from its `package.json`.
|
|
47
|
+
*
|
|
48
|
+
* Lives here rather than in `run.ts` because it is the same judgement as `planDevScript` with the
|
|
49
|
+
* parsing attached, and `run.ts` is at its cohesion limit. Returns undefined for anything it cannot
|
|
50
|
+
* read: this phrasing decides nothing, so it must never be able to break an install.
|
|
51
|
+
*/
|
|
52
|
+
export function devCommandFrom(pkgRaw, packageManager) {
|
|
53
|
+
try {
|
|
54
|
+
const parsed = JSON.parse(pkgRaw);
|
|
55
|
+
const scripts = 'object' === typeof parsed && null !== parsed
|
|
56
|
+
? (parsed.scripts ?? {})
|
|
57
|
+
: {};
|
|
58
|
+
return planDevScript(scripts, packageManager, false).command;
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
}
|
package/dist/init/mcp.d.ts
CHANGED
|
@@ -15,11 +15,16 @@ export { MCP_SERVER_NAME } from '@reticlehq/core';
|
|
|
15
15
|
/**
|
|
16
16
|
* The registered command is STILL bare `npx` on every platform, deliberately.
|
|
17
17
|
*
|
|
18
|
-
* Windows is
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
18
|
+
* Windows is most of Reticle's users and, when this was written, had no CI. There is Windows CI
|
|
19
|
+
* now, so the `cmd /c` fallback is testable. Defaulting the majority platform to that launch
|
|
20
|
+
* command is still the riskier change: `.cmd` has spawn caveats in some hosts, and a registration
|
|
21
|
+
* that works on the machines we can test can still fail in the hosts we cannot. So the fallback
|
|
22
|
+
* stays documented rather than defaulted.
|
|
23
|
+
*
|
|
24
|
+
* What DID change is when it is printed. `mcpManual` used to run only when the `claude` CLI was
|
|
25
|
+
* absent. The reported Windows install had `claude mcp add` succeed, so the one paragraph that
|
|
26
|
+
* would have unblocked zero tools never appeared. `mcpWindowsNote` is that paragraph, and init
|
|
27
|
+
* prints it on every Windows run — including the ones that look like a clean registration.
|
|
23
28
|
*/
|
|
24
29
|
export declare const NPX = "npx";
|
|
25
30
|
/**
|
|
@@ -74,5 +79,11 @@ export declare function claudeAvailableProbe(): {
|
|
|
74
79
|
command: string;
|
|
75
80
|
args: string[];
|
|
76
81
|
};
|
|
82
|
+
/**
|
|
83
|
+
* The Windows `cmd /c npx` registration. Printed on every Windows init, not only when the `claude`
|
|
84
|
+
* CLI is missing — a successful `claude mcp add` of bare `npx` is exactly the failure that reported
|
|
85
|
+
* as a clean install with zero tools.
|
|
86
|
+
*/
|
|
87
|
+
export declare function mcpWindowsNote(): string;
|
|
77
88
|
/** Printed when the `claude` CLI isn't available — register Reticle globally once, by hand. */
|
|
78
89
|
export declare function mcpManual(): string;
|
package/dist/init/mcp.js
CHANGED
|
@@ -19,11 +19,16 @@ import { MCP_SERVER_NAME } from '@reticlehq/core';
|
|
|
19
19
|
/**
|
|
20
20
|
* The registered command is STILL bare `npx` on every platform, deliberately.
|
|
21
21
|
*
|
|
22
|
-
* Windows is
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
22
|
+
* Windows is most of Reticle's users and, when this was written, had no CI. There is Windows CI
|
|
23
|
+
* now, so the `cmd /c` fallback is testable. Defaulting the majority platform to that launch
|
|
24
|
+
* command is still the riskier change: `.cmd` has spawn caveats in some hosts, and a registration
|
|
25
|
+
* that works on the machines we can test can still fail in the hosts we cannot. So the fallback
|
|
26
|
+
* stays documented rather than defaulted.
|
|
27
|
+
*
|
|
28
|
+
* What DID change is when it is printed. `mcpManual` used to run only when the `claude` CLI was
|
|
29
|
+
* absent. The reported Windows install had `claude mcp add` succeed, so the one paragraph that
|
|
30
|
+
* would have unblocked zero tools never appeared. `mcpWindowsNote` is that paragraph, and init
|
|
31
|
+
* prints it on every Windows run — including the ones that look like a clean registration.
|
|
27
32
|
*/
|
|
28
33
|
export const NPX = 'npx';
|
|
29
34
|
// The `reticle` bin lives in the server package, so `npx <pkg> mcp` runs the bridge without the
|
|
@@ -31,6 +36,9 @@ export const NPX = 'npx';
|
|
|
31
36
|
const RETICLE_PACKAGE = RETICLE_NPM_PACKAGE;
|
|
32
37
|
const MCP_SUBCOMMAND = 'mcp';
|
|
33
38
|
const CLAUDE_CLI = 'claude';
|
|
39
|
+
/** Windows `cmd /c` — the spawn that PowerShell's execution policy does not gate. */
|
|
40
|
+
const WINDOWS_SHELL = 'cmd';
|
|
41
|
+
const WINDOWS_SHELL_C = '/c';
|
|
34
42
|
/**
|
|
35
43
|
* Args after `npx` that launch the bridge: `@reticlehq/server mcp`. Portless — the port comes from
|
|
36
44
|
* the project's `.reticle.json` at runtime, so one global entry works for every project.
|
|
@@ -94,6 +102,18 @@ export function claudeExistsProbe() {
|
|
|
94
102
|
export function claudeAvailableProbe() {
|
|
95
103
|
return { command: CLAUDE_CLI, args: ['--version'] };
|
|
96
104
|
}
|
|
105
|
+
/**
|
|
106
|
+
* The Windows `cmd /c npx` registration. Printed on every Windows init, not only when the `claude`
|
|
107
|
+
* CLI is missing — a successful `claude mcp add` of bare `npx` is exactly the failure that reported
|
|
108
|
+
* as a clean install with zero tools.
|
|
109
|
+
*/
|
|
110
|
+
export function mcpWindowsNote() {
|
|
111
|
+
return `On Windows, if the agent cannot start Reticle because npx is blocked ("running scripts is disabled
|
|
112
|
+
on this system" — a PowerShell execution policy), register it through cmd instead, which that policy
|
|
113
|
+
does not gate:
|
|
114
|
+
|
|
115
|
+
"${MCP_SERVER_NAME}": { "command": "${WINDOWS_SHELL}", "args": ${JSON.stringify([WINDOWS_SHELL_C, NPX, ...serverInvocation().slice(1)])} }`;
|
|
116
|
+
}
|
|
97
117
|
/** Printed when the `claude` CLI isn't available — register Reticle globally once, by hand. */
|
|
98
118
|
export function mcpManual() {
|
|
99
119
|
const tail = serverInvocation().join(' ');
|
|
@@ -105,9 +125,5 @@ Or, for another agent, add this to its global MCP config (e.g. Cursor's ~/.curso
|
|
|
105
125
|
|
|
106
126
|
"${MCP_SERVER_NAME}": { "command": "${NPX}", "args": ${JSON.stringify(serverInvocation().slice(1))} }
|
|
107
127
|
|
|
108
|
-
|
|
109
|
-
on this system" — a PowerShell execution policy), register it through cmd instead, which that policy
|
|
110
|
-
does not gate:
|
|
111
|
-
|
|
112
|
-
"${MCP_SERVER_NAME}": { "command": "cmd", "args": ${JSON.stringify(['/c', NPX, ...serverInvocation().slice(1)])} }`;
|
|
128
|
+
${mcpWindowsNote()}`;
|
|
113
129
|
}
|
|
@@ -17,11 +17,6 @@ export declare const VITE_PLUGIN_DETAIL: {
|
|
|
17
17
|
readonly SVELTEKIT: "add reticle() to plugins (stamps data-reticle-source in .svelte components)";
|
|
18
18
|
};
|
|
19
19
|
export declare function viteSteps(input: PlanInput, detail?: string): Step[];
|
|
20
|
-
/**
|
|
21
|
-
* Next used to be the ONLY stack with hand edits left over — and both of them fail silently when
|
|
22
|
-
* skipped, so a Next user's app booted, connected to nothing, and said nothing about why. Both are
|
|
23
|
-
* now patched by the same conservative rules the Vite config gets.
|
|
24
|
-
*/
|
|
25
20
|
export declare function nextSteps(input: PlanInput): Step[];
|
|
26
21
|
/**
|
|
27
22
|
* SvelteKit is WIRED but not SUPPORTED, and the plan says so out loud.
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Split out of `plan.ts` — that file is the plan's SHAPE (statuses, ordering, the agent/MCP steps),
|
|
4
4
|
* this one is the per-framework detail, and they grow for different reasons.
|
|
5
5
|
*/
|
|
6
|
+
import { bridgeWsUrl } from '@reticlehq/core';
|
|
6
7
|
import { patchViteConfig, VitePatchKind } from './vite-config.js';
|
|
7
8
|
import { patchNextConfig, patchRootLayout, patchPagesApp } from './next-patch.js';
|
|
8
9
|
import { patchAstroConfig, patchAstroLayout } from './astro-patch.js';
|
|
@@ -62,6 +63,22 @@ function capabilitiesTodo(path, stores) {
|
|
|
62
63
|
* `__reticle_renders` — the state-truth read was unavailable on every app out of the box. Written
|
|
63
64
|
* only when absent, because it is the one generated file a user is expected to EDIT.
|
|
64
65
|
*/
|
|
66
|
+
/**
|
|
67
|
+
* Does the generated dev module actually REGISTER anything?
|
|
68
|
+
*
|
|
69
|
+
* Shared by the Vite and Next paths because it is one question about one generated call, and the
|
|
70
|
+
* two answered it differently for as long as only Vite asked: `capabilitiesStep` was called from
|
|
71
|
+
* `viteSteps` and nowhere else, so a Next app whose store can only be SUGGESTED wrote
|
|
72
|
+
* `registerCapabilities({ testids: [], stores: [] })`, registered nothing, and said nothing.
|
|
73
|
+
* Measured on a real Next + mobx app: five steps, every one a checkmark, no capabilities line.
|
|
74
|
+
*
|
|
75
|
+
* `wired` are stores init resolved and wrote a live `registerStore` call for. Hints are not
|
|
76
|
+
* registrations: they land in the file as a commented line, and counting one as a registration
|
|
77
|
+
* silences the notice whose whole purpose is to say "act on the hint".
|
|
78
|
+
*/
|
|
79
|
+
function registersNothing(testids, wired) {
|
|
80
|
+
return 0 === testids.length && 0 === wired.length;
|
|
81
|
+
}
|
|
65
82
|
function capabilitiesStep(input) {
|
|
66
83
|
if (true === input.viteDevModuleExists) {
|
|
67
84
|
return [
|
|
@@ -79,8 +96,21 @@ function capabilitiesStep(input) {
|
|
|
79
96
|
const found = testids.length > 0
|
|
80
97
|
? `${String(testids.length)} data-testid values`
|
|
81
98
|
: 'no data-testid values yet';
|
|
82
|
-
// A store we found and
|
|
83
|
-
|
|
99
|
+
// A store we found and WIRED is a registration, so the "registers nothing" notice must not fire.
|
|
100
|
+
// A store HINT is not: `stores` holds suggestions, written into the file as a commented line of
|
|
101
|
+
// the form `// import your store, then: registerStore(...)`. Counting a suggestion as a
|
|
102
|
+
// registration let the hint silence the notice whose entire job is to say "act on the hint".
|
|
103
|
+
//
|
|
104
|
+
// Measured against a real product UI (rowy — 70+ deps, jotai, a whole src/atoms tree): init
|
|
105
|
+
// detected jotai, offered one commented line, emitted NO notice, and wrote
|
|
106
|
+
// `registerCapabilities({ testids: [], signals: [], stores: [] })`. So `hasCapabilities` stayed
|
|
107
|
+
// false, `reticle_state` was empty forever, and the install gate reported "connected: 1,
|
|
108
|
+
// manual ⚠: none". Every check green, state observability zero.
|
|
109
|
+
//
|
|
110
|
+
// This is the common case rather than an edge one: a library whose registration needs an argument
|
|
111
|
+
// only reading the source supplies — jotai atoms, an XState actor, a TanStack queryClient — is
|
|
112
|
+
// exactly what init leaves commented.
|
|
113
|
+
const nothingToRegister = registersNothing(testids, wired);
|
|
84
114
|
return [
|
|
85
115
|
{
|
|
86
116
|
title: CAPABILITIES_TITLE,
|
|
@@ -197,16 +227,44 @@ function patchStep(title, path, patch, applyDetail, manualDetail) {
|
|
|
197
227
|
* skipped, so a Next user's app booted, connected to nothing, and said nothing about why. Both are
|
|
198
228
|
* now patched by the same conservative rules the Vite config gets.
|
|
199
229
|
*/
|
|
230
|
+
/** The env var withReticle sets from the discovered daemon, and the component must read. */
|
|
231
|
+
const NEXT_DAEMON_URL_ENV = 'NEXT_PUBLIC_RETICLE_URL';
|
|
232
|
+
/**
|
|
233
|
+
* The two-line edit that unfreezes an existing install's port.
|
|
234
|
+
*
|
|
235
|
+
* Stated as the edit rather than as a problem, because the reader is an agent that can apply it in
|
|
236
|
+
* one write and is otherwise going to ask what to do.
|
|
237
|
+
*/
|
|
238
|
+
const NEXT_DEV_FILE_STALE_DETAIL = 'predates daemon discovery, so it dials the port init saw when it ran. In this file add ' +
|
|
239
|
+
'`const url = process.env.NEXT_PUBLIC_RETICLE_URL;` and spread `...(url ? { url } : {})` into ' +
|
|
240
|
+
'reticle.connect(), after any url already there. Nothing else needs to change.';
|
|
200
241
|
export function nextSteps(input) {
|
|
201
242
|
const configFile = input.nextConfigFile ?? 'next.config.mjs';
|
|
202
243
|
const devPath = input.nextReticleDevPath ?? NEXT_RETICLE_DEV_PATH;
|
|
244
|
+
// An install that predates daemon discovery has a component that never reads the discovered URL,
|
|
245
|
+
// so it keeps dialling whatever port `init` saw on the day it ran. Re-running `init` used to call
|
|
246
|
+
// that "file exists" and move on, which is why the defect survives an upgrade. Named as work.
|
|
247
|
+
//
|
|
248
|
+
// Never overwritten: this file is the one an app owner edits — registered stores, capabilities,
|
|
249
|
+
// their own signals. Rewriting it to fix two lines would take the rest with it. Undefined source
|
|
250
|
+
// means it was not read, and an unread file stays ALREADY rather than becoming invented work.
|
|
251
|
+
const devStale = true === input.nextReticleDevExists &&
|
|
252
|
+
'string' === typeof input.nextReticleDevSource &&
|
|
253
|
+
!input.nextReticleDevSource.includes(NEXT_DAEMON_URL_ENV);
|
|
203
254
|
const devFile = input.nextReticleDevExists
|
|
204
|
-
?
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
255
|
+
? devStale
|
|
256
|
+
? {
|
|
257
|
+
title: 'ReticleDev component',
|
|
258
|
+
target: devPath,
|
|
259
|
+
status: StepStatus.MANUAL,
|
|
260
|
+
detail: NEXT_DEV_FILE_STALE_DETAIL,
|
|
261
|
+
}
|
|
262
|
+
: {
|
|
263
|
+
title: 'ReticleDev component',
|
|
264
|
+
target: devPath,
|
|
265
|
+
status: StepStatus.ALREADY,
|
|
266
|
+
detail: 'file exists',
|
|
267
|
+
}
|
|
210
268
|
: {
|
|
211
269
|
title: 'ReticleDev component',
|
|
212
270
|
target: devPath,
|
|
@@ -233,8 +291,22 @@ export function nextSteps(input) {
|
|
|
233
291
|
: isPagesRouter
|
|
234
292
|
? patchPagesApp(layout.source, input.nextReticleDevImport)
|
|
235
293
|
: patchRootLayout(layout.source);
|
|
294
|
+
// The same question the Vite path asks. Skipped when the module already exists: its contents are
|
|
295
|
+
// the user's, and re-nagging about a file we did not write is noise.
|
|
296
|
+
const nextTodo = true !== input.nextReticleDevExists &&
|
|
297
|
+
registersNothing(input.testids ?? [], input.nextFoundStores ?? [])
|
|
298
|
+
? [
|
|
299
|
+
{
|
|
300
|
+
title: CAPABILITIES_TODO_TITLE,
|
|
301
|
+
target: devPath,
|
|
302
|
+
status: StepStatus.NOTICE,
|
|
303
|
+
detail: capabilitiesTodo(devPath, input.storeHints ?? []),
|
|
304
|
+
},
|
|
305
|
+
]
|
|
306
|
+
: [];
|
|
236
307
|
return [
|
|
237
308
|
devFile,
|
|
309
|
+
...nextTodo,
|
|
238
310
|
patchStep('Next config (withReticle)', configFile, configPatch, 'wrap the export in withReticle (source mapping, dev-only)', nextConfigManual(configFile)),
|
|
239
311
|
patchStep('Mount ReticleDev', layout?.path ?? NEXT_LAYOUT_PATH, layoutPatch, 'mount <ReticleDev /> in the root layout (dev-only)', NEXT_LAYOUT_MANUAL),
|
|
240
312
|
];
|
|
@@ -269,7 +341,9 @@ export function craSteps(input) {
|
|
|
269
341
|
},
|
|
270
342
|
];
|
|
271
343
|
const token = input.pairingToken ?? '';
|
|
272
|
-
|
|
344
|
+
// The daemon that is live NOW, not the one baked into the module at first install. CRA has no
|
|
345
|
+
// build hook, so this is refreshed by re-running `init` rather than by starting the dev server.
|
|
346
|
+
const env = craEnvPatch(input.craEnv ?? null, token, input.options.port === undefined ? undefined : bridgeWsUrl(input.options.port));
|
|
273
347
|
if (env !== null) {
|
|
274
348
|
steps.push({
|
|
275
349
|
title: 'Pairing token',
|
package/dist/init/plan.d.ts
CHANGED
|
@@ -6,16 +6,6 @@
|
|
|
6
6
|
import { Framework, UiLibrary, type Detection } from './detect.js';
|
|
7
7
|
import type { FoundStore } from './capabilities.js';
|
|
8
8
|
import { type McpClient } from './mcp-clients.js';
|
|
9
|
-
/**
|
|
10
|
-
* Pin the SDK to the CLI's own version.
|
|
11
|
-
*
|
|
12
|
-
* `pnpm add -D @reticlehq/react` installed **2.2.1** in one project while npm and yarn took 2.3.0 in
|
|
13
|
-
* the next — a stale registry metadata cache, invisible to the user and to us. A version-skewed SDK
|
|
14
|
-
* talking to a newer daemon is the `-32000` failure path: the app connects, the protocol disagrees,
|
|
15
|
-
* and nothing on either side names a version. Asking for the CLI's exact version makes the cache
|
|
16
|
-
* irrelevant, and a skewed pair impossible to install by accident.
|
|
17
|
-
*/
|
|
18
|
-
export declare function pinnedPackages(packages: readonly string[], version: string | undefined): readonly string[];
|
|
19
9
|
/**
|
|
20
10
|
* The dev-dependencies `reticle init` installs — kit (or sensor) first, build plugin next.
|
|
21
11
|
*
|
|
@@ -101,6 +91,8 @@ export interface PlanInput {
|
|
|
101
91
|
mcpExists: boolean;
|
|
102
92
|
/** Whether Cursor is installed for this user (its global config dir exists). */
|
|
103
93
|
cursorPresent: boolean;
|
|
94
|
+
/** `process.platform`. Injected so this module stays pure. Windows is the only branch. */
|
|
95
|
+
platform?: string;
|
|
104
96
|
/** Whether THIS project has a .cursor/ directory — the signal that Cursor works on this repo. */
|
|
105
97
|
cursorProjectPresent?: boolean | undefined;
|
|
106
98
|
/** Current ~/.cursor/mcp.json content, or null if absent. */
|
|
@@ -154,6 +146,14 @@ export interface PlanInput {
|
|
|
154
146
|
nextReticleDevImport?: string | undefined;
|
|
155
147
|
/** Whether the ReticleDev component file already exists. */
|
|
156
148
|
nextReticleDevExists: boolean;
|
|
149
|
+
/**
|
|
150
|
+
* The existing ReticleDev component's source, when there is one.
|
|
151
|
+
*
|
|
152
|
+
* Read so an install predating daemon discovery can be told apart from a current one. Undefined
|
|
153
|
+
* means NOT READ, and an unread file is reported as already-wired rather than as stale: inventing
|
|
154
|
+
* work from missing information is how a plan grows steps that can never be completed.
|
|
155
|
+
*/
|
|
156
|
+
nextReticleDevSource?: string | null | undefined;
|
|
157
157
|
/** `data-testid` values scanned from the app's source, for the generated capabilities block. */
|
|
158
158
|
testids?: readonly string[] | undefined;
|
|
159
159
|
/** Ready-to-uncomment `registerStore` lines for the state libraries the app actually depends on. */
|
|
@@ -226,24 +226,4 @@ export interface PlanInput {
|
|
|
226
226
|
sdkVersion?: string;
|
|
227
227
|
};
|
|
228
228
|
}
|
|
229
|
-
/**
|
|
230
|
-
* Every coding agent `reticle init` knows how to wire itself into, and the three surfaces it wires:
|
|
231
|
-
* the global MCP registration, the project rule file that makes the agent USE Reticle, and the
|
|
232
|
-
* `/reticle` command.
|
|
233
|
-
*
|
|
234
|
-
* One list, because those three surfaces used to enumerate Claude and Cursor separately in three
|
|
235
|
-
* places — so supporting a fourth agent meant finding all three and getting each right. Adding one
|
|
236
|
-
* is now an entry here plus its builders; each builder answers null when that agent is not present
|
|
237
|
-
* for this user or this project, which is also how "no agent detected" falls through to the
|
|
238
|
-
* cross-agent AGENTS.md.
|
|
239
|
-
*
|
|
240
|
-
* Deliberately functions rather than data: registering with Claude runs its CLI while Cursor merges
|
|
241
|
-
* a JSON file, and pretending those are the same shape would cost more than it saves.
|
|
242
|
-
*/
|
|
243
|
-
declare const AgentId: {
|
|
244
|
-
readonly CLAUDE: "claude";
|
|
245
|
-
readonly CURSOR: "cursor";
|
|
246
|
-
};
|
|
247
|
-
export type AgentId = (typeof AgentId)[keyof typeof AgentId];
|
|
248
229
|
export declare function buildPlan(input: PlanInput): Plan;
|
|
249
|
-
export {};
|
package/dist/init/plan.js
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
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 { claudeAddCommand, mcpManual } from './mcp.js';
|
|
7
|
+
import { claudeAddCommand, mcpManual, mcpWindowsNote } from './mcp.js';
|
|
8
|
+
import { NodePlatform } from '../platform.js';
|
|
8
9
|
import { mergeCursorConfig, CursorMergeStatus, cursorServerEntry } from './cursor.js';
|
|
9
10
|
import { mergeClientConfig, ClientMergeStatus, clientSnippet, clientSpec, } from './mcp-clients.js';
|
|
10
11
|
import { CLAUDE_COMMAND_PATH, CURSOR_COMMAND_PATH, SLASH_COMMAND_BODY, SLASH_COMMAND_SIGNATURE, } from './slash-command.js';
|
|
@@ -32,7 +33,7 @@ const RETICLE_NEXT_PLUGIN = '@reticlehq/next';
|
|
|
32
33
|
* and nothing on either side names a version. Asking for the CLI's exact version makes the cache
|
|
33
34
|
* irrelevant, and a skewed pair impossible to install by accident.
|
|
34
35
|
*/
|
|
35
|
-
|
|
36
|
+
function pinnedPackages(packages, version) {
|
|
36
37
|
if (version === undefined || 0 === version.length)
|
|
37
38
|
return packages;
|
|
38
39
|
return packages.map((p) => `${p}@${version}`);
|
|
@@ -232,19 +233,33 @@ function mcpSteps(input) {
|
|
|
232
233
|
// client. A machine with Cursor AND Windsurf gets both — registering only the first one found is
|
|
233
234
|
// how a user ends up with Reticle in the editor they were not using.
|
|
234
235
|
const steps = [...stepsForAgents(input, (a) => a.mcpStep), ...otherClientSteps(input)];
|
|
235
|
-
if (steps.length
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
236
|
+
if (0 === steps.length) {
|
|
237
|
+
// No agent detected. mcpManual already carries the Windows cmd fallback — do not append it again.
|
|
238
|
+
return [
|
|
239
|
+
{
|
|
240
|
+
title: 'MCP server (global)',
|
|
241
|
+
target: MCP_TARGET,
|
|
242
|
+
status: StepStatus.MANUAL,
|
|
243
|
+
detail: mcpManual(),
|
|
244
|
+
},
|
|
245
|
+
];
|
|
246
|
+
}
|
|
247
|
+
const windowsNote = windowsMcpNoteStep(input);
|
|
248
|
+
if (windowsNote !== null)
|
|
249
|
+
steps.push(windowsNote);
|
|
250
|
+
return steps;
|
|
251
|
+
}
|
|
252
|
+
const WINDOWS_MCP_TITLE = 'Windows MCP spawn';
|
|
253
|
+
/** Windows only. The reported install never reached mcpManual because `claude mcp add` succeeded. */
|
|
254
|
+
function windowsMcpNoteStep(input) {
|
|
255
|
+
if (input.platform !== NodePlatform.WINDOWS)
|
|
256
|
+
return null;
|
|
257
|
+
return {
|
|
258
|
+
title: WINDOWS_MCP_TITLE,
|
|
259
|
+
target: MCP_TARGET,
|
|
260
|
+
status: StepStatus.NOTICE,
|
|
261
|
+
detail: mcpWindowsNote(),
|
|
262
|
+
};
|
|
248
263
|
}
|
|
249
264
|
const SLASH_COMMAND_TITLE = 'The /reticle command';
|
|
250
265
|
/**
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bring an already-installed project's agent rules up to the version that is running.
|
|
3
|
+
*
|
|
4
|
+
* `mergeMarkedInstruction` has always been able to do this: the managed block is marker-delimited,
|
|
5
|
+
* idempotence is decided by comparing CONTENT rather than by seeing a marker, and everything
|
|
6
|
+
* outside the markers is left exactly as the human wrote it. What was missing is anything that
|
|
7
|
+
* CALLS it after the first install.
|
|
8
|
+
*
|
|
9
|
+
* It is reachable only from `buildPlan`, so the rules refresh when somebody re-runs `init` and
|
|
10
|
+
* never otherwise. A project set up on an older release keeps that release's instructions forever,
|
|
11
|
+
* which means every improvement to them — what to do when a capabilities file registers nothing,
|
|
12
|
+
* that an install is not finished until a verdict exists, that intent is captured while building —
|
|
13
|
+
* reaches new projects only.
|
|
14
|
+
*
|
|
15
|
+
* That is the wrong way round. The people who most need better instructions are the ones already
|
|
16
|
+
* installed and not getting value, and `reticle update` is the moment they are already touching the
|
|
17
|
+
* install.
|
|
18
|
+
*/
|
|
19
|
+
/** The instruction files the managed block lives in. Cursor's rule file is regenerated whole. */
|
|
20
|
+
export declare const RULE_FILES: readonly ["CLAUDE.md", "AGENTS.md"];
|
|
21
|
+
export interface RuleFileIo {
|
|
22
|
+
read: (path: string) => string | null;
|
|
23
|
+
write: (path: string, content: string) => void;
|
|
24
|
+
}
|
|
25
|
+
export interface RefreshResult {
|
|
26
|
+
/** Files whose managed block was replaced with the current one. */
|
|
27
|
+
updated: string[];
|
|
28
|
+
/** Files that were already current, or that carry no Reticle block to update. */
|
|
29
|
+
unchanged: string[];
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Refresh every instruction file that already carries a Reticle block.
|
|
33
|
+
*
|
|
34
|
+
* Deliberately does NOT create one where none exists. A project with no Reticle block in its
|
|
35
|
+
* CLAUDE.md either never ran `init` here or had the block removed on purpose, and `update` is not
|
|
36
|
+
* the place to make that decision for somebody — it is a version command, and quietly writing
|
|
37
|
+
* instructions into a file a user curates would be a surprise.
|
|
38
|
+
*/
|
|
39
|
+
export declare function refreshAgentRules(cwd: string, io: RuleFileIo): RefreshResult;
|