@reticlehq/init 3.3.0 → 3.4.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.
Files changed (81) hide show
  1. package/dist/detect/capabilities.d.ts +4 -1
  2. package/dist/detect/capabilities.js +45 -9
  3. package/dist/detect/detect.d.ts +45 -2
  4. package/dist/detect/detect.js +76 -5
  5. package/dist/detect/dev-script.d.ts +26 -2
  6. package/dist/detect/dev-script.js +80 -7
  7. package/dist/detect/non-js-project.d.ts +1 -1
  8. package/dist/detect/non-js-project.js +6 -1
  9. package/dist/detect/workspace-apps.d.ts +21 -0
  10. package/dist/detect/workspace-apps.js +111 -12
  11. package/dist/detect/workspace-redirect.js +28 -2
  12. package/dist/diagnose/containerised-dev-server.d.ts +12 -4
  13. package/dist/diagnose/containerised-dev-server.js +36 -4
  14. package/dist/diagnose/csp-check.d.ts +12 -6
  15. package/dist/diagnose/csp-check.js +61 -13
  16. package/dist/diagnose/csp-doctor.d.ts +13 -1
  17. package/dist/diagnose/csp-doctor.js +2 -2
  18. package/dist/diagnose/csp-step.d.ts +27 -0
  19. package/dist/diagnose/csp-step.js +86 -0
  20. package/dist/diagnose/desktop-doctor.js +8 -2
  21. package/dist/diagnose/install-hint.d.ts +9 -1
  22. package/dist/diagnose/install-hint.js +17 -7
  23. package/dist/diagnose/install-retries.d.ts +16 -1
  24. package/dist/diagnose/install-retries.js +50 -3
  25. package/dist/diagnose/terminal-wrap.d.ts +18 -0
  26. package/dist/diagnose/terminal-wrap.js +63 -0
  27. package/dist/index.d.ts +6 -2
  28. package/dist/index.js +6 -2
  29. package/dist/no-package-json.d.ts +10 -0
  30. package/dist/no-package-json.js +121 -0
  31. package/dist/node-io.d.ts +9 -5
  32. package/dist/node-io.js +25 -7
  33. package/dist/patch/angular.d.ts +68 -0
  34. package/dist/patch/angular.js +216 -0
  35. package/dist/patch/astro-patch.d.ts +1 -1
  36. package/dist/patch/astro-patch.js +2 -2
  37. package/dist/patch/cra.d.ts +0 -2
  38. package/dist/patch/cra.js +0 -2
  39. package/dist/patch/electron-patch.d.ts +10 -1
  40. package/dist/patch/electron-patch.js +79 -27
  41. package/dist/patch/electron-vite-patch.js +1 -1
  42. package/dist/patch/nuxt-patch.d.ts +1 -1
  43. package/dist/patch/nuxt-patch.js +2 -2
  44. package/dist/patch/nuxt-snippets.js +6 -6
  45. package/dist/patch/remix.d.ts +24 -0
  46. package/dist/patch/remix.js +59 -0
  47. package/dist/patch/snippets.d.ts +57 -9
  48. package/dist/patch/snippets.js +119 -28
  49. package/dist/patch/static-page.d.ts +39 -0
  50. package/dist/patch/static-page.js +102 -0
  51. package/dist/patch/tanstack-start.d.ts +36 -8
  52. package/dist/patch/tanstack-start.js +118 -29
  53. package/dist/patch/vite-config.d.ts +5 -0
  54. package/dist/patch/vite-config.js +8 -0
  55. package/dist/patch/vite-owning-config.d.ts +34 -0
  56. package/dist/patch/vite-owning-config.js +63 -5
  57. package/dist/plan/connect-steps.d.ts +7 -1
  58. package/dist/plan/connect-steps.js +18 -1
  59. package/dist/plan/framework-adapter.d.ts +3 -1
  60. package/dist/plan/framework-adapter.js +99 -6
  61. package/dist/plan/notices.js +4 -3
  62. package/dist/plan/plan-angular.d.ts +6 -0
  63. package/dist/plan/plan-angular.js +89 -0
  64. package/dist/plan/plan-electron-vite.d.ts +6 -3
  65. package/dist/plan/plan-electron-vite.js +39 -8
  66. package/dist/plan/plan-framework.d.ts +8 -46
  67. package/dist/plan/plan-framework.js +81 -312
  68. package/dist/plan/plan-types.d.ts +29 -0
  69. package/dist/plan/plan-vite.d.ts +67 -0
  70. package/dist/plan/plan-vite.js +256 -0
  71. package/dist/plan/plan.js +38 -34
  72. package/dist/plan/port-steps.d.ts +45 -0
  73. package/dist/plan/port-steps.js +119 -0
  74. package/dist/plan/preflight.d.ts +17 -3
  75. package/dist/plan/preflight.js +53 -23
  76. package/dist/register/mcp.d.ts +18 -5
  77. package/dist/register/mcp.js +48 -8
  78. package/dist/run-types.d.ts +8 -0
  79. package/dist/run.js +111 -78
  80. package/package.json +3 -4
  81. package/NOTICE +0 -5
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Angular CLI: the dev-only connect in the browser entry, and the dev-server route that hands it
3
+ * the pairing token. See `patch/angular.ts` for why the token takes that route and no other.
4
+ */
5
+ import { ANGULAR_DEFAULT_ENTRY, ANGULAR_PROXY_PATH, ANGULAR_TOKEN_PATH, angularProxyFile, patchAngularJson, patchAngularMain, } from '../patch/angular.js';
6
+ import { PatchKind } from '../patch/patch-kind.js';
7
+ import { ANGULAR_WORKSPACE_FILE } from '../detect/detect.js';
8
+ import { StepTitle } from './connect-steps.js';
9
+ import { patchStep } from './plan-framework.js';
10
+ import { alreadyOrMovedPort, retargetPort } from './port-steps.js';
11
+ import { StepStatus } from './plan-types.js';
12
+ const ANGULAR_SETUP_GATED_NOTE = 'The install gate scaffolds an Angular app (Angular 21, no SSR) on every release and requires a ' +
13
+ 'session, and this wiring was proven by hand on Angular 22 with and without SSR; nothing yet ' +
14
+ 'drives an Angular app to a verdict on each change. DOM, network, console and state ' +
15
+ 'tools work here; component identity does not (that is the React adapter). If no session ' +
16
+ 'appears, please open an issue.';
17
+ const ENTRY_DETAIL = 'connect from the browser entry, dev-only (`ngDevMode`), through a dynamic import the ' +
18
+ 'production build drops';
19
+ const PROXY_DETAIL = 'the `ng serve`-only module that hands the page this machine’s pairing token, read from ' +
20
+ '~/.reticle at request time — never written into the project';
21
+ const SERVE_DETAIL = `point the serve target's proxyConfig at ${ANGULAR_PROXY_PATH} (\`ng build\` never reads it)`;
22
+ /** The full recipe, for when the entry is not a file `init` can see. */
23
+ function entryManual(input, entryPath) {
24
+ const recipe = patchAngularMain('', input.options.port, input.options.projectId);
25
+ const code = recipe.kind === PatchKind.APPLY ? recipe.code : '';
26
+ return `Add this to your browser entry (${entryPath}):\n\n${code}`;
27
+ }
28
+ function proxyStep(input) {
29
+ const existing = input.angularProxySource ?? null;
30
+ if (null !== existing && existing.includes(ANGULAR_TOKEN_PATH)) {
31
+ return {
32
+ title: StepTitle.ANGULAR_TOKEN_PROXY,
33
+ target: ANGULAR_PROXY_PATH,
34
+ status: StepStatus.ALREADY,
35
+ detail: 'already there',
36
+ };
37
+ }
38
+ return {
39
+ title: StepTitle.ANGULAR_TOKEN_PROXY,
40
+ target: ANGULAR_PROXY_PATH,
41
+ status: StepStatus.APPLY,
42
+ detail: PROXY_DETAIL,
43
+ write: { path: ANGULAR_PROXY_PATH, content: angularProxyFile(), expect: [ANGULAR_TOKEN_PATH] },
44
+ };
45
+ }
46
+ function serveStep(input) {
47
+ const workspace = input.angularWorkspace ?? null;
48
+ const manual = `In ${ANGULAR_WORKSPACE_FILE}, set the serve target's "options.proxyConfig" to "${ANGULAR_PROXY_PATH}".`;
49
+ if (null === workspace) {
50
+ return {
51
+ title: StepTitle.ANGULAR_SERVE_CONFIG,
52
+ target: ANGULAR_WORKSPACE_FILE,
53
+ status: StepStatus.MANUAL,
54
+ detail: manual,
55
+ };
56
+ }
57
+ return patchStep(StepTitle.ANGULAR_SERVE_CONFIG, workspace.path, patchAngularJson(workspace.source), SERVE_DETAIL, manual);
58
+ }
59
+ function entryStep(input) {
60
+ const entry = input.angularEntry ?? null;
61
+ if (null === entry) {
62
+ return {
63
+ title: StepTitle.CONNECT_SNIPPET_ANGULAR,
64
+ target: ANGULAR_DEFAULT_ENTRY,
65
+ status: StepStatus.MANUAL,
66
+ detail: entryManual(input, ANGULAR_DEFAULT_ENTRY),
67
+ };
68
+ }
69
+ // "already wired" read the entry without reading the port baked into its connect, so a re-run
70
+ // with a new `--port` left the page dialling the old daemon.
71
+ const patch = patchAngularMain(entry.source, input.options.port, input.options.projectId);
72
+ // An entry the previous version wired is rewritten in place (its guard), so a re-run that ALSO
73
+ // moves the port has to move it in the rewritten code — the ALREADY path below never sees it.
74
+ const moved = PatchKind.APPLY === patch.kind ? retargetPort(patch.code, input.options.port) : null;
75
+ return alreadyOrMovedPort(patchStep(StepTitle.CONNECT_SNIPPET_ANGULAR, entry.path, null === moved ? patch : { kind: PatchKind.APPLY, code: moved.code }, ENTRY_DETAIL, entryManual(input, entry.path)), entry.source, input.options.port);
76
+ }
77
+ export function angularSteps(input) {
78
+ return [
79
+ {
80
+ title: StepTitle.ANGULAR_SETUP_GATED,
81
+ target: ANGULAR_WORKSPACE_FILE,
82
+ status: StepStatus.NOTICE,
83
+ detail: ANGULAR_SETUP_GATED_NOTE,
84
+ },
85
+ entryStep(input),
86
+ proxyStep(input),
87
+ serveStep(input),
88
+ ];
89
+ }
@@ -1,8 +1,10 @@
1
1
  /**
2
- * The plan's electron-vite half: the renderer plugin, capabilities, and the two Electron steps.
2
+ * The plan's Electron half: the renderer plugin, capabilities, and the two Electron steps — for
3
+ * electron-vite and for Electron Forge's Vite template, which differ only in where the renderer's
4
+ * Vite config lives.
3
5
  *
4
- * Its own module because this is the only framework whose wiring reaches outside the renderer, into
5
- * a preload and a main process the web frameworks have no equivalent of.
6
+ * Its own module because these are the only frameworks whose wiring reaches outside the renderer,
7
+ * into a preload and a main process the web frameworks have no equivalent of.
6
8
  */
7
9
  import { type PlanInput, type Step } from './plan-types.js';
8
10
  /**
@@ -13,3 +15,4 @@ import { type PlanInput, type Step } from './plan-types.js';
13
15
  * manual recipe the way Astro's config+layout pair does.
14
16
  */
15
17
  export declare function electronViteSteps(input: PlanInput): Step[];
18
+ export declare function electronForgeSteps(input: PlanInput): Step[];
@@ -1,12 +1,16 @@
1
1
  /**
2
- * The plan's electron-vite half: the renderer plugin, capabilities, and the two Electron steps.
2
+ * The plan's Electron half: the renderer plugin, capabilities, and the two Electron steps — for
3
+ * electron-vite and for Electron Forge's Vite template, which differ only in where the renderer's
4
+ * Vite config lives.
3
5
  *
4
- * Its own module because this is the only framework whose wiring reaches outside the renderer, into
5
- * a preload and a main process the web frameworks have no equivalent of.
6
+ * Its own module because these are the only frameworks whose wiring reaches outside the renderer,
7
+ * into a preload and a main process the web frameworks have no equivalent of.
6
8
  */
7
9
  import { StepStatus } from './plan-types.js';
8
10
  import { StepTitle } from './connect-steps.js';
9
- import { capabilitiesStep, patchStep } from './plan-framework.js';
11
+ import { patchStep } from './plan-framework.js';
12
+ import { capabilitiesStep, viteSteps } from './plan-vite.js';
13
+ import { alreadyOrMovedPort } from './port-steps.js';
10
14
  import { patchElectronViteConfig } from '../patch/electron-vite-patch.js';
11
15
  import { patchElectronMain, patchElectronPreload } from '../patch/electron-patch.js';
12
16
  import { ELECTRON_CAPTURE_FIX, ELECTRON_PRELOAD_FIX } from '../diagnose/desktop-doctor.js';
@@ -29,15 +33,19 @@ export function electronViteSteps(input) {
29
33
  status: StepStatus.MANUAL,
30
34
  detail: manual,
31
35
  }
32
- : patchStep(StepTitle.ELECTRON_VITE_PLUGIN, cfg.path, patchElectronViteConfig(cfg.source, port), 'add reticle({ desktop: true }) to the renderer plugins (also injects connect())', manual);
36
+ : alreadyOrMovedPort(patchStep(StepTitle.ELECTRON_VITE_PLUGIN, cfg.path, patchElectronViteConfig(cfg.source, port), 'add reticle({ desktop: true }) to the renderer plugins (also injects connect())', manual), cfg.source, port);
33
37
  return [
34
38
  plugin,
35
39
  ...capabilitiesStep(input, ELECTRON_VITE_DEV_MODULE_PATH),
36
- ...electronPreloadStep(input),
40
+ ...electronPreloadStep(input, false),
37
41
  ...electronCaptureStep(input),
38
42
  ];
39
43
  }
40
- function electronPreloadStep(input) {
44
+ /**
45
+ * `viteTypes` is for Forge, whose tsconfig has no `import.meta.env` types for the dev guard. See
46
+ * `patchElectronPreload`.
47
+ */
48
+ function electronPreloadStep(input, viteTypes) {
41
49
  const file = input.electronPreload ?? null;
42
50
  if (null === file) {
43
51
  return [
@@ -50,7 +58,7 @@ function electronPreloadStep(input) {
50
58
  ];
51
59
  }
52
60
  return [
53
- patchStep(StepTitle.ELECTRON_PRELOAD, file.path, patchElectronPreload(file.source, file.path), 'require the IPC shim as the first line of preload', ELECTRON_PRELOAD_FIX),
61
+ patchStep(StepTitle.ELECTRON_PRELOAD, file.path, patchElectronPreload(file.source, file.path, viteTypes), 'require the IPC shim as the first line of preload, dev only', ELECTRON_PRELOAD_FIX),
54
62
  ];
55
63
  }
56
64
  function electronCaptureStep(input) {
@@ -69,3 +77,26 @@ function electronCaptureStep(input) {
69
77
  patchStep(StepTitle.ELECTRON_CAPTURE, file.path, patchElectronMain(file.source, file.path), 'installReticleCapture on the BrowserWindow so screenshots work', ELECTRON_CAPTURE_FIX),
70
78
  ];
71
79
  }
80
+ /**
81
+ * Electron Forge's Vite template: the renderer config, then the same two Electron halves.
82
+ *
83
+ * Forge's renderer config is an ordinary Vite config (`defineConfig({})`), so it is patched by the
84
+ * ordinary Vite patcher — `run.ts` hands it over as `viteConfig` when the framework is Forge.
85
+ *
86
+ * The plugin goes in WITHOUT `desktop: true`. `electron-forge start` serves the renderer through a
87
+ * real Vite dev server, so the plugin's ordinary serve-time injection reaches the window; the flag
88
+ * exists for a packaged renderer driven with no dev server, which is built in a non-production mode
89
+ * on purpose. Without it a packaged Forge app has no connect() in any mode, which is the web default
90
+ * and the safe direction. Measured on a
91
+ * scaffolded Forge 8 app: `tsc --noEmit` and `electron-forge package` both pass after `init`, and the
92
+ * packaged renderer carries no Reticle code.
93
+ */
94
+ const FORGE_RENDERER_DETAIL = 'add reticle() to the renderer plugins — the only one of Forge’s three Vite builds with a ' +
95
+ 'document (also injects connect() while `electron-forge start` serves it)';
96
+ export function electronForgeSteps(input) {
97
+ return [
98
+ ...viteSteps(input, FORGE_RENDERER_DETAIL),
99
+ ...electronPreloadStep(input, true),
100
+ ...electronCaptureStep(input),
101
+ ];
102
+ }
@@ -1,40 +1,12 @@
1
1
  /**
2
- * How each framework gets wired: the Vite plugin, the three Next files, the SvelteKit client hook.
3
- * `plan.ts` is the plan's SHAPE (statuses, ordering, the agent/MCP steps); this is the per-framework
4
- * detail, and they grow for different reasons.
2
+ * How each framework gets wired: the three Next files, the SvelteKit client hook, CRA, Nuxt, Astro
3
+ * and the rest. `plan.ts` is the plan's SHAPE (statuses, ordering, the agent/MCP steps); this is the
4
+ * per-framework detail, and they grow for different reasons. The Vite plugin and the capabilities
5
+ * module every Vite-based stack shares live in `plan-vite.ts`.
5
6
  */
6
7
  import { type SourcePatch } from '../patch/patch-kind.js';
7
8
  import { type PlanInput, type Step } from './plan-types.js';
8
9
  import { StepTitle } from './connect-steps.js';
9
- /** What adding `reticle()` to a Vite config buys, which differs by framework. */
10
- export declare const VITE_PLUGIN_DETAIL: {
11
- /** A plain Vite app gets both halves from the plugin. */
12
- readonly VITE: "add reticle() to plugins (also injects connect())";
13
- /**
14
- * SvelteKit renders through app.html, so the plugin's HTML injection never fires and connect()
15
- * comes from the client hook instead. The plugin is still required: it is what stamps
16
- * data-reticle-source into .svelte components, and without it every verdict on a SvelteKit app
17
- * comes back with no file:line at all.
18
- */
19
- readonly SVELTEKIT: "add reticle() to plugins (stamps data-reticle-source in .svelte components)";
20
- /**
21
- * React Router framework mode renders through its own request handler, so the plugin's HTML
22
- * injection never fires and connect() comes from the client entry instead. The plugin is still
23
- * required for the same reason it is under SvelteKit: without it every verdict on the app comes
24
- * back with no file:line at all.
25
- */
26
- readonly REACT_ROUTER: "add reticle() to plugins (stamps data-reticle-source in .tsx components)";
27
- /**
28
- * TanStack Start SSRs its own HTML, so the plugin's HTML injection never fires and connect()
29
- * comes from a client effect instead. `inject: false` is load-bearing honesty: leaving the
30
- * default would keep reporting "also injects connect()" for a transform that never runs.
31
- * The plugin is still required for the same reason it is under SvelteKit: without it every
32
- * verdict on the app comes back with no file:line at all.
33
- */
34
- readonly TANSTACK_START: "add reticle({ inject: false }) to plugins (stamps data-reticle-source; Start SSRs its own HTML so connect() cannot come from the plugin)";
35
- };
36
- export declare function capabilitiesStep(input: PlanInput, path?: string): Step[];
37
- export declare function viteSteps(input: PlanInput, detail?: string, inject?: boolean): Step[];
38
10
  /**
39
11
  * Turn a conservative source patch into a step: applied when it patched, already when the wiring is
40
12
  * there, and the hand-edit instructions when the file shape wasn't one we recognise.
@@ -75,12 +47,12 @@ export declare function nuxtSteps(input: PlanInput): Step[];
75
47
  * would replace that default with one that never hydrates. When it is present, one line is added to
76
48
  * it and nothing else is touched.
77
49
  */
78
- export declare function reactRouterSteps(input: PlanInput): Step[];
50
+ export declare function reactRouterSteps(input: PlanInput, title?: StepTitle, newEntry?: () => string): Step[];
79
51
  /**
80
- * TanStack Start: the client-document connect, printed rather than written.
52
+ * TanStack Start: a client connect component, written, and rendered in the root document.
81
53
  *
82
- * `src/routes/__root.tsx` is the document Start SSRs. A static import of the SDK on that module
83
- * 500s, so writing one is worse than a documented manual step. See `tanstackStartManual`.
54
+ * ATOMIC, like Astro: the component on disk with nothing rendering it is a guaranteed
55
+ * non-connection, so when the root cannot be patched BOTH halves go back to the recipe.
84
56
  */
85
57
  export declare function tanstackStartSteps(input: PlanInput): Step[];
86
58
  export declare function svelteKitSteps(input: PlanInput): Step[];
@@ -93,16 +65,6 @@ export declare function svelteKitSteps(input: PlanInput): Step[];
93
65
  * and a half-edited build config is worse than a documented manual step.
94
66
  */
95
67
  export declare function astroSteps(input: PlanInput): Step[];
96
- /**
97
- * A Content-Security-Policy whose `connect-src` excludes the bridge, said out loud at install time.
98
- *
99
- * Both reported cases were Next apps where `init` printed success for every step and the app then
100
- * never connected: the browser blocked the WebSocket and reported it in its own console, which
101
- * nothing on this side reads. A NOTICE rather than a step, because editing someone's security policy
102
- * is theirs to do — but it carries the exact text to paste, which is the difference between a
103
- * warning and a fix.
104
- */
105
- export declare function cspStep(input: PlanInput): Step[];
106
68
  /**
107
69
  * The plain-HTML path: no bundler to hook, so the connect snippet is printed for a hand edit.
108
70
  *