@reticlehq/init 3.4.0 → 3.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -13,6 +13,13 @@ export declare const InitFailure: {
|
|
|
13
13
|
readonly DEPENDENCY_INSTALL: "dependency_install";
|
|
14
14
|
/** The `claude mcp add` step failed — the CLI is missing or refused. Reticle installs but is unreachable. */
|
|
15
15
|
readonly MCP_REGISTRATION: "mcp_registration";
|
|
16
|
+
/** The checkout or required tooling failed preflight. */
|
|
17
|
+
readonly PREFLIGHT: "preflight";
|
|
18
|
+
/** Runtime continuation could not start the app or connect its SDK. */
|
|
19
|
+
readonly DEV_SERVER: "dev_server";
|
|
20
|
+
readonly APP_CONNECTION: "app_connection";
|
|
21
|
+
readonly BRIDGE_OCCUPIED: "bridge_occupied";
|
|
22
|
+
readonly RUNTIME_ERROR: "runtime_error";
|
|
16
23
|
readonly OTHER: "other";
|
|
17
24
|
};
|
|
18
25
|
export type InitFailure = (typeof InitFailure)[keyof typeof InitFailure];
|
|
@@ -13,5 +13,12 @@ export const InitFailure = {
|
|
|
13
13
|
DEPENDENCY_INSTALL: 'dependency_install',
|
|
14
14
|
/** The `claude mcp add` step failed — the CLI is missing or refused. Reticle installs but is unreachable. */
|
|
15
15
|
MCP_REGISTRATION: 'mcp_registration',
|
|
16
|
+
/** The checkout or required tooling failed preflight. */
|
|
17
|
+
PREFLIGHT: 'preflight',
|
|
18
|
+
/** Runtime continuation could not start the app or connect its SDK. */
|
|
19
|
+
DEV_SERVER: 'dev_server',
|
|
20
|
+
APP_CONNECTION: 'app_connection',
|
|
21
|
+
BRIDGE_OCCUPIED: 'bridge_occupied',
|
|
22
|
+
RUNTIME_ERROR: 'runtime_error',
|
|
16
23
|
OTHER: 'other',
|
|
17
24
|
};
|
package/dist/no-package-json.js
CHANGED
|
@@ -21,6 +21,7 @@ const SNIPPET_WRITTEN = `Wrote the dev-only connect snippet into ${HTML_INDEX_PA
|
|
|
21
21
|
'gitignored — keep it out of anything you publish, and let each teammate run `reticle init` ' +
|
|
22
22
|
'for their own.';
|
|
23
23
|
const SNIPPET_ALREADY = `${HTML_INDEX_PATH} already loads the Reticle SDK — nothing to change.`;
|
|
24
|
+
const DRY_RUN_PAIRING_TOKEN = 'DRY_RUN_TOKEN_NOT_FOR_USE';
|
|
24
25
|
/**
|
|
25
26
|
* Write the snippet into `index.html`, and hand over to setup as a success.
|
|
26
27
|
*
|
|
@@ -74,6 +75,10 @@ export function initWithoutPackageJson(options, io) {
|
|
|
74
75
|
// Written only when absent. A config a user or an earlier run already placed here is theirs.
|
|
75
76
|
const nonJsProjectId = deriveProjectId(undefined, io.cwd());
|
|
76
77
|
const writeConfig = () => {
|
|
78
|
+
if (options.dryRun) {
|
|
79
|
+
io.print('Dry run: no files written; the snippet uses a placeholder pairing token.');
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
77
82
|
// Except its port. A re-run with a new `--port` starts the daemon there, and a config still
|
|
78
83
|
// naming the old one sends the agent to a daemon that is no longer running.
|
|
79
84
|
const existingConfig = io.readFile(RETICLE_CONFIG_FILE);
|
|
@@ -88,10 +93,14 @@ export function initWithoutPackageJson(options, io) {
|
|
|
88
93
|
rememberProjectOnDisk(io, nonJsProjectId, io.cwd(), Date.now());
|
|
89
94
|
io.print(`Wrote ${RETICLE_CONFIG_FILE} (project "${nonJsProjectId}").`);
|
|
90
95
|
};
|
|
91
|
-
const connect = connectArgWithToken(options.port, nonJsProjectId, io.host.pairingToken());
|
|
96
|
+
const connect = connectArgWithToken(options.port, nonJsProjectId, options.dryRun ? DRY_RUN_PAIRING_TOKEN : io.host.pairingToken());
|
|
92
97
|
// One page at the root is a page we can see whole. Several, or none, is a choice that is not ours.
|
|
93
98
|
if (!streamlit && !django && io.exists(HTML_INDEX_PATH)) {
|
|
94
99
|
writeConfig();
|
|
100
|
+
if (options.dryRun) {
|
|
101
|
+
io.print(staticPageSnippet(connect));
|
|
102
|
+
return { ok: true, applied: 0, manual: 0 };
|
|
103
|
+
}
|
|
95
104
|
// Without the token: the page is the deployable artifact, so the token goes beside it instead.
|
|
96
105
|
return wireStaticPage(options, io, connectArg(options.port, nonJsProjectId));
|
|
97
106
|
}
|
|
@@ -116,6 +125,7 @@ export function initWithoutPackageJson(options, io) {
|
|
|
116
125
|
: staticPageSnippet(connect));
|
|
117
126
|
// The onboarding funnel had NO instrumentation, so a setup that died here was indistinguishable
|
|
118
127
|
// from someone who never ran the command — the two failure modes with the most different fixes.
|
|
119
|
-
|
|
128
|
+
if (!options.dryRun)
|
|
129
|
+
io.host.reportOutcome({ ok: false, reason: InitFailure.NO_PACKAGE_JSON });
|
|
120
130
|
return { ok: false, applied: 0, manual: 0 };
|
|
121
131
|
}
|
package/dist/node-io.d.ts
CHANGED
|
@@ -34,4 +34,6 @@ export declare const RUNNABLE_COMMANDS: readonly string[];
|
|
|
34
34
|
* reason a second, broken copy existed.
|
|
35
35
|
*/
|
|
36
36
|
export declare function probeCli(command: string, args: readonly string[]): boolean;
|
|
37
|
-
export declare function buildNodeIo(cwd: string, host: InitHost
|
|
37
|
+
export declare function buildNodeIo(cwd: string, host: InitHost, output?: {
|
|
38
|
+
stderr?: boolean;
|
|
39
|
+
}): InitIo;
|
package/dist/node-io.js
CHANGED
|
@@ -102,7 +102,7 @@ export function probeCli(command, args) {
|
|
|
102
102
|
return false;
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
|
-
export function buildNodeIo(cwd, host) {
|
|
105
|
+
export function buildNodeIo(cwd, host, output = {}) {
|
|
106
106
|
// Project-relative by default; absolute paths (e.g. ~/.cursor/mcp.json) pass through unchanged.
|
|
107
107
|
const abs = (rel) => (isAbsolute(rel) ? rel : join(cwd, rel));
|
|
108
108
|
return {
|
|
@@ -161,11 +161,14 @@ export function buildNodeIo(cwd, host) {
|
|
|
161
161
|
scoped(rel) {
|
|
162
162
|
// The host travels with the re-rooted IO: a monorepo redirect re-enters `runInit` through
|
|
163
163
|
// this, and an untraced, unreported inner run is the half of init that actually did the work.
|
|
164
|
-
return buildNodeIo(abs(rel), host);
|
|
164
|
+
return buildNodeIo(abs(rel), host, output);
|
|
165
165
|
},
|
|
166
166
|
exec(command, args) {
|
|
167
|
-
//
|
|
168
|
-
const result = spawnAllowed(command, args, {
|
|
167
|
+
// JSON callers reserve stdout for the result, including across package-manager subprocesses.
|
|
168
|
+
const result = spawnAllowed(command, args, {
|
|
169
|
+
cwd,
|
|
170
|
+
stdio: true === output.stderr ? ['inherit', 2, 2] : 'inherit',
|
|
171
|
+
});
|
|
169
172
|
return 0 === result.status;
|
|
170
173
|
},
|
|
171
174
|
capture(command, args) {
|
|
@@ -189,7 +192,8 @@ export function buildNodeIo(cwd, host) {
|
|
|
189
192
|
return 0 === result.status;
|
|
190
193
|
},
|
|
191
194
|
print(line) {
|
|
192
|
-
process.stdout
|
|
195
|
+
const stream = true === output.stderr ? process.stderr : process.stdout;
|
|
196
|
+
stream.write(`${wrapForTerminal(line, terminalWidth())}\n`);
|
|
193
197
|
},
|
|
194
198
|
host,
|
|
195
199
|
};
|
|
@@ -162,9 +162,8 @@ export const MCP_CLIENTS = [
|
|
|
162
162
|
* `~/.gemini/config/mcp_config.json`, and one file serves the 2.0 IDE, the CLI and the SDK. Same
|
|
163
163
|
* `command`/`args` object Cursor, Windsurf and Gemini CLI already use.
|
|
164
164
|
*
|
|
165
|
-
* It shares the `~/.gemini` tree with Gemini CLI
|
|
166
|
-
*
|
|
167
|
-
* so having one installed cannot make `init` write a config for the other.
|
|
165
|
+
* It shares the `~/.gemini` tree with Gemini CLI. Gemini detection requires its settings file:
|
|
166
|
+
* checking only the parent directory also matches an Antigravity-only installation.
|
|
168
167
|
*
|
|
169
168
|
* NOT verified against a running install. The path is documented, the merge is the shared JSON
|
|
170
169
|
* path that every other client here uses, and an unparseable config still degrades to a printed
|
|
@@ -329,6 +328,9 @@ export function clientSnippet(spec) {
|
|
|
329
328
|
export function clientMarkerRelPath(spec) {
|
|
330
329
|
if (spec.scope === ConfigScope.CLI)
|
|
331
330
|
return '';
|
|
331
|
+
// Antigravity also creates ~/.gemini. The shared parent is not evidence of Gemini CLI.
|
|
332
|
+
if (spec.id === McpClient.GEMINI)
|
|
333
|
+
return spec.relPath;
|
|
332
334
|
const parts = spec.relPath.split('/');
|
|
333
335
|
return parts.length > 1 ? parts.slice(0, -1).join('/') : spec.relPath;
|
|
334
336
|
}
|
package/dist/run.js
CHANGED
|
@@ -32,6 +32,8 @@ import { tanstackStartConnectPath } from './patch/tanstack-start.js';
|
|
|
32
32
|
import { installedViteMajor } from './patch/vite-owning-config.js';
|
|
33
33
|
/** CRA's bundled entry, in the order create-react-app itself generates them. */
|
|
34
34
|
const CRA_ENTRY_CANDIDATES = ['src/index.tsx', 'src/index.jsx', 'src/index.ts', 'src/index.js'];
|
|
35
|
+
/** Preview snippets must not mint a real machine credential or imply this token is usable. */
|
|
36
|
+
const DRY_RUN_PAIRING_TOKEN = 'DRY_RUN_TOKEN_NOT_FOR_USE';
|
|
35
37
|
function craEntryOf(io) {
|
|
36
38
|
for (const path of CRA_ENTRY_CANDIDATES) {
|
|
37
39
|
const source = io.readFile(path);
|
|
@@ -395,7 +397,7 @@ function gatherPlanInput(options, io, pkg) {
|
|
|
395
397
|
: {}),
|
|
396
398
|
craEntry: craEntryOf(io),
|
|
397
399
|
craEnv: io.readFile(CRA_ENV_PATH),
|
|
398
|
-
pairingToken: io.host.pairingToken(),
|
|
400
|
+
pairingToken: options.dryRun ? DRY_RUN_PAIRING_TOKEN : io.host.pairingToken(),
|
|
399
401
|
installSource: io.host.installSource(),
|
|
400
402
|
reticleConfigExists: io.exists(RETICLE_CONFIG_FILE),
|
|
401
403
|
// The CONTENT, so a config that exists can be checked rather than trusted — a `"port"` set to
|
|
@@ -737,7 +739,8 @@ function runInitSteps(options, io) {
|
|
|
737
739
|
io.print(`${PACKAGE_JSON} is not valid JSON (${manifest.error}). Fix it and re-run — init reads the ` +
|
|
738
740
|
'framework, the dev script and the package manager from it, and will not guess at any of ' +
|
|
739
741
|
'them from a file it cannot read.');
|
|
740
|
-
|
|
742
|
+
if (!options.dryRun)
|
|
743
|
+
io.host.reportOutcome({ ok: false, reason: InitFailure.MALFORMED_PACKAGE_JSON });
|
|
741
744
|
return { ok: false, applied: 0, manual: 0 };
|
|
742
745
|
}
|
|
743
746
|
const pkgRaw = manifest.pkg;
|
|
@@ -777,6 +780,8 @@ function runInitSteps(options, io) {
|
|
|
777
780
|
});
|
|
778
781
|
if (resolved.refusal !== undefined) {
|
|
779
782
|
io.print(resolved.refusal);
|
|
783
|
+
if (!options.dryRun)
|
|
784
|
+
io.host.reportOutcome({ ok: false, reason: InitFailure.PREFLIGHT });
|
|
780
785
|
return { ok: false, applied: 0, manual: 1 };
|
|
781
786
|
}
|
|
782
787
|
// The prefix that can actually run it on THIS machine — `pnpm`, or `corepack pnpm` where preflight
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reticlehq/init",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.0",
|
|
4
4
|
"description": "Reticle project scaffolder — detects the framework, writes the config, patches the build and registers the MCP server. The build-time half of `reticle init`.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"./package.json": "./package.json"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@reticlehq/core": "3.
|
|
16
|
+
"@reticlehq/core": "3.5.0"
|
|
17
17
|
},
|
|
18
18
|
"license": "FSL-1.1-ALv2",
|
|
19
19
|
"author": "Reticle contributors",
|