@reticlehq/init 2.14.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 (114) hide show
  1. package/LICENSE +105 -0
  2. package/NOTICE +5 -0
  3. package/README.md +29 -0
  4. package/dist/agent-rules.d.ts +116 -0
  5. package/dist/agent-rules.js +279 -0
  6. package/dist/app-choice.d.ts +29 -0
  7. package/dist/app-choice.js +39 -0
  8. package/dist/astro-host.d.ts +21 -0
  9. package/dist/astro-host.js +27 -0
  10. package/dist/astro-patch.d.ts +33 -0
  11. package/dist/astro-patch.js +103 -0
  12. package/dist/brace-scan.d.ts +24 -0
  13. package/dist/brace-scan.js +46 -0
  14. package/dist/capabilities.d.ts +40 -0
  15. package/dist/capabilities.js +167 -0
  16. package/dist/closing-hint.d.ts +35 -0
  17. package/dist/closing-hint.js +122 -0
  18. package/dist/codex-toml.d.ts +10 -0
  19. package/dist/codex-toml.js +144 -0
  20. package/dist/connect-steps.d.ts +55 -0
  21. package/dist/connect-steps.js +92 -0
  22. package/dist/cra.d.ts +82 -0
  23. package/dist/cra.js +183 -0
  24. package/dist/csp-check.d.ts +49 -0
  25. package/dist/csp-check.js +168 -0
  26. package/dist/csp-doctor.d.ts +32 -0
  27. package/dist/csp-doctor.js +62 -0
  28. package/dist/desktop-doctor.d.ts +46 -0
  29. package/dist/desktop-doctor.js +201 -0
  30. package/dist/detect.d.ts +225 -0
  31. package/dist/detect.js +334 -0
  32. package/dist/dev-script.d.ts +51 -0
  33. package/dist/dev-script.js +66 -0
  34. package/dist/dev-server-ports.d.ts +20 -0
  35. package/dist/dev-server-ports.js +45 -0
  36. package/dist/electron-patch.d.ts +11 -0
  37. package/dist/electron-patch.js +70 -0
  38. package/dist/electron-vite-patch.d.ts +12 -0
  39. package/dist/electron-vite-patch.js +100 -0
  40. package/dist/existing-config.d.ts +31 -0
  41. package/dist/existing-config.js +63 -0
  42. package/dist/format-generated.d.ts +18 -0
  43. package/dist/format-generated.js +48 -0
  44. package/dist/framework-adapter.d.ts +60 -0
  45. package/dist/framework-adapter.js +138 -0
  46. package/dist/host.d.ts +41 -0
  47. package/dist/host.js +21 -0
  48. package/dist/index.d.ts +25 -0
  49. package/dist/index.js +25 -0
  50. package/dist/init-failure.d.ts +19 -0
  51. package/dist/init-failure.js +18 -0
  52. package/dist/init-opt-out.d.ts +17 -0
  53. package/dist/init-opt-out.js +30 -0
  54. package/dist/install-hint.d.ts +9 -0
  55. package/dist/install-hint.js +41 -0
  56. package/dist/install-retries.d.ts +22 -0
  57. package/dist/install-retries.js +65 -0
  58. package/dist/install-source-config.d.ts +18 -0
  59. package/dist/install-source-config.js +34 -0
  60. package/dist/lockfiles.d.ts +14 -0
  61. package/dist/lockfiles.js +49 -0
  62. package/dist/mcp-clients.d.ts +94 -0
  63. package/dist/mcp-clients.js +327 -0
  64. package/dist/mcp-registered.d.ts +12 -0
  65. package/dist/mcp-registered.js +14 -0
  66. package/dist/mcp.d.ts +89 -0
  67. package/dist/mcp.js +129 -0
  68. package/dist/next-patch.d.ts +49 -0
  69. package/dist/next-patch.js +237 -0
  70. package/dist/node-io.d.ts +22 -0
  71. package/dist/node-io.js +150 -0
  72. package/dist/non-js-project.d.ts +44 -0
  73. package/dist/non-js-project.js +137 -0
  74. package/dist/nuxt-patch.d.ts +18 -0
  75. package/dist/nuxt-patch.js +33 -0
  76. package/dist/patch-kind.d.ts +20 -0
  77. package/dist/patch-kind.js +10 -0
  78. package/dist/plan-electron-vite.d.ts +16 -0
  79. package/dist/plan-electron-vite.js +72 -0
  80. package/dist/plan-framework.d.ts +122 -0
  81. package/dist/plan-framework.js +773 -0
  82. package/dist/plan.d.ts +312 -0
  83. package/dist/plan.js +650 -0
  84. package/dist/platform.d.ts +16 -0
  85. package/dist/platform.js +16 -0
  86. package/dist/preflight.d.ts +30 -0
  87. package/dist/preflight.js +52 -0
  88. package/dist/project-id.d.ts +12 -0
  89. package/dist/project-id.js +25 -0
  90. package/dist/refresh-rules.d.ts +40 -0
  91. package/dist/refresh-rules.js +51 -0
  92. package/dist/remember-project.d.ts +24 -0
  93. package/dist/remember-project.js +40 -0
  94. package/dist/run.d.ts +122 -0
  95. package/dist/run.js +762 -0
  96. package/dist/slash-command.d.ts +27 -0
  97. package/dist/slash-command.js +132 -0
  98. package/dist/snippets.d.ts +267 -0
  99. package/dist/snippets.js +898 -0
  100. package/dist/tanstack-start.d.ts +18 -0
  101. package/dist/tanstack-start.js +57 -0
  102. package/dist/version.d.ts +18 -0
  103. package/dist/version.js +23 -0
  104. package/dist/vite-config.d.ts +18 -0
  105. package/dist/vite-config.js +111 -0
  106. package/dist/vite-owning-config.d.ts +25 -0
  107. package/dist/vite-owning-config.js +219 -0
  108. package/dist/windows-quote.d.ts +38 -0
  109. package/dist/windows-quote.js +70 -0
  110. package/dist/workspace-apps.d.ts +40 -0
  111. package/dist/workspace-apps.js +188 -0
  112. package/dist/workspace-redirect.d.ts +22 -0
  113. package/dist/workspace-redirect.js +97 -0
  114. package/package.json +53 -0
@@ -0,0 +1,30 @@
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, options?: {
29
+ alreadyServed?: boolean;
30
+ }): string | undefined;
@@ -0,0 +1,52 @@
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, options = {}) {
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
+ //
33
+ // `alreadyServed` is `--url`, and skipping the check for it is the message keeping its own promise.
34
+ // This guard exists for the DEV SERVER — to stop `spawn pnpm ENOENT` surfacing inside "the dev
35
+ // server exited" — and `--url` says the app is already up, so init starts nothing and the failure
36
+ // it protects against cannot happen. Reported from the field as a circle: the refusal named `--url`
37
+ // as the way past it while `--url` was being passed, because the flag was parsed and then never
38
+ // handed to init at all.
39
+ //
40
+ // The dependency install can still fail without the package manager. That is one step reporting ⚠,
41
+ // which is what a step that cannot complete is for, and is a better outcome than writing nothing:
42
+ // the config, the connect module and `.reticle.json` all land, and the user is told the one thing
43
+ // left to do rather than being sent to install a package manager they were trying to avoid.
44
+ if (true !== options.alreadyServed &&
45
+ ALWAYS_PRESENT !== packageManager &&
46
+ !io.probe(packageManager, ['--version'])) {
47
+ return (`this project uses ${packageManager} (its lockfile says so) and ${packageManager} is not ` +
48
+ `installed on this machine. Install it (npm i -g ${packageManager}, or corepack enable), or ` +
49
+ 'pass --url with the address the app already serves.');
50
+ }
51
+ return undefined;
52
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Project-identity for `reticle init` — the `.reticle.json` side of the scheme the Vite plugin
3
+ * stamps at build time. The RULE lives in `@reticlehq/core` (projectIdFrom) so both sides spell the
4
+ * id the same way by construction; this file supplies only the Node-only hashing it needs.
5
+ */
6
+ export { slugifyPackageName } from '@reticlehq/core';
7
+ /** Short, stable hex fingerprint of the absolute project root. */
8
+ export declare function shortHash(input: string): string;
9
+ /** Derive the stable projectId from the package name (may be undefined) and the absolute root path. */
10
+ export declare function deriveProjectId(pkgName: string | undefined, rootPath: string): string;
11
+ /** Pull a string `name` out of an already-parsed package.json object, or undefined. */
12
+ export declare function packageName(pkg: unknown): string | undefined;
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Project-identity for `reticle init` — the `.reticle.json` side of the scheme the Vite plugin
3
+ * stamps at build time. The RULE lives in `@reticlehq/core` (projectIdFrom) so both sides spell the
4
+ * id the same way by construction; this file supplies only the Node-only hashing it needs.
5
+ */
6
+ import { createHash } from 'node:crypto';
7
+ import { PROJECT_ID_HASH_LENGTH, projectIdFrom } from '@reticlehq/core';
8
+ export { slugifyPackageName } from '@reticlehq/core';
9
+ /** Short, stable hex fingerprint of the absolute project root. */
10
+ export function shortHash(input) {
11
+ return createHash('sha1').update(input).digest('hex').slice(0, PROJECT_ID_HASH_LENGTH);
12
+ }
13
+ /** Derive the stable projectId from the package name (may be undefined) and the absolute root path. */
14
+ export function deriveProjectId(pkgName, rootPath) {
15
+ return projectIdFrom(pkgName, rootPath, shortHash);
16
+ }
17
+ /** Pull a string `name` out of an already-parsed package.json object, or undefined. */
18
+ export function packageName(pkg) {
19
+ if ('object' === typeof pkg && pkg !== null) {
20
+ const name = pkg['name'];
21
+ if ('string' === typeof name && name.length > 0)
22
+ return name;
23
+ }
24
+ return undefined;
25
+ }
@@ -0,0 +1,40 @@
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
+ interface RuleFileIo {
22
+ read: (path: string) => string | null;
23
+ write: (path: string, content: string) => void;
24
+ }
25
+ 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;
40
+ export {};
@@ -0,0 +1,51 @@
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
+ import { join } from 'node:path';
20
+ import { mergeMarkedInstruction, AgentRuleStatus, hasManagedBlock } from './agent-rules.js';
21
+ /** The instruction files the managed block lives in. Cursor's rule file is regenerated whole. */
22
+ export const RULE_FILES = ['CLAUDE.md', 'AGENTS.md'];
23
+ /**
24
+ * Refresh every instruction file that already carries a Reticle block.
25
+ *
26
+ * Deliberately does NOT create one where none exists. A project with no Reticle block in its
27
+ * CLAUDE.md either never ran `init` here or had the block removed on purpose, and `update` is not
28
+ * the place to make that decision for somebody — it is a version command, and quietly writing
29
+ * instructions into a file a user curates would be a surprise.
30
+ */
31
+ export function refreshAgentRules(cwd, io) {
32
+ const updated = [];
33
+ const unchanged = [];
34
+ for (const name of RULE_FILES) {
35
+ const path = join(cwd, name);
36
+ const existing = io.read(path);
37
+ if (null === existing || !hasManagedBlock(existing)) {
38
+ unchanged.push(name);
39
+ continue;
40
+ }
41
+ const result = mergeMarkedInstruction(existing);
42
+ if (AgentRuleStatus.APPLY === result.status && result.content !== existing) {
43
+ io.write(path, result.content);
44
+ updated.push(name);
45
+ }
46
+ else {
47
+ unchanged.push(name);
48
+ }
49
+ }
50
+ return { updated, unchanged };
51
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Record where this project lives, so a daemon started somewhere else can still find it.
3
+ *
4
+ * Called at the end of `init`, which is the one moment both halves are known for certain: the
5
+ * directory being initialised, and the projectId that was just written into its `.reticle.json`.
6
+ *
7
+ * Deliberately runs on EVERY init, not only the first. Most init runs report "already wired", and a
8
+ * re-run in a re-cloned or moved checkout is exactly when the remembered path has gone stale — so
9
+ * the run that would have been a no-op is the one that repairs the entry.
10
+ *
11
+ * Every failure is swallowed. This is a cache that makes a later resolution better; a read-only home
12
+ * directory, a full disk or a hand-mangled file must not turn a successful init into a failed one.
13
+ * The user's project is wired either way, and `init`'s report is the first thing a new user reads.
14
+ */
15
+ /** The sync filesystem seam `init` already carries. Narrowed to what this needs. */
16
+ export interface RegistryIo {
17
+ readFile(path: string): string | null;
18
+ writeFile(path: string, content: string): void;
19
+ exists(path: string): boolean;
20
+ homeDir(): string;
21
+ }
22
+ /** The `projectId` a `.reticle.json` declares, or undefined when it declares none we can use. */
23
+ export declare function projectIdOf(configSource: string | null): string | undefined;
24
+ export declare function rememberProjectOnDisk(io: RegistryIo, projectId: string, directory: string, now: number): boolean;
@@ -0,0 +1,40 @@
1
+ import { join } from 'node:path';
2
+ import { PROJECT_REGISTRY_FILE, ReticleDir, emptyProjectRegistry, parseProjectRegistry, rememberProject, } from '@reticlehq/core';
3
+ /** The `projectId` a `.reticle.json` declares, or undefined when it declares none we can use. */
4
+ export function projectIdOf(configSource) {
5
+ if (null === configSource || 0 === configSource.length)
6
+ return undefined;
7
+ try {
8
+ const parsed = JSON.parse(configSource);
9
+ if ('object' !== typeof parsed || null === parsed || Array.isArray(parsed))
10
+ return undefined;
11
+ const id = parsed['projectId'];
12
+ return 'string' === typeof id && id.length > 0 ? id : undefined;
13
+ }
14
+ catch {
15
+ return undefined;
16
+ }
17
+ }
18
+ export function rememberProjectOnDisk(io, projectId, directory, now) {
19
+ if (0 === projectId.length || 0 === directory.length)
20
+ return false;
21
+ try {
22
+ const path = join(io.homeDir(), ReticleDir.ROOT, PROJECT_REGISTRY_FILE);
23
+ const existing = io.exists(path) ? io.readFile(path) : null;
24
+ const registry = null === existing ? emptyProjectRegistry() : parseProjectRegistry(safeJson(existing));
25
+ const next = rememberProject(registry, projectId, directory, now);
26
+ io.writeFile(path, `${JSON.stringify(next, null, 2)}\n`);
27
+ return true;
28
+ }
29
+ catch {
30
+ return false;
31
+ }
32
+ }
33
+ function safeJson(raw) {
34
+ try {
35
+ return JSON.parse(raw);
36
+ }
37
+ catch {
38
+ return undefined;
39
+ }
40
+ }
package/dist/run.d.ts ADDED
@@ -0,0 +1,122 @@
1
+ /**
2
+ * The impure shell for `reticle init`: gather project files via an injected IO surface, build the
3
+ * plan (pure), optionally write the apply-steps, and print a human-readable report. All filesystem
4
+ * access goes through `InitIo` so the orchestration is unit-testable with an in-memory IO.
5
+ */
6
+ import type { InitHost } from './host.js';
7
+ import type { InitOutcome } from '@reticlehq/core/telemetry';
8
+ import { resolveLockfiles } from './lockfiles.js';
9
+ export { resolveLockfiles };
10
+ export interface InitOptions {
11
+ /** `--capture-bodies`: write `captureNetworkBodies: true` into the app's config. Off by default (#705). */
12
+ captureBodies?: boolean | undefined;
13
+ cwd: string;
14
+ port: number | undefined;
15
+ mcp: boolean;
16
+ dryRun: boolean;
17
+ install: boolean;
18
+ /**
19
+ * Which app in a monorepo to wire, when several are found. Without it `init` refuses to guess, and
20
+ * "re-run inside the one you want" is not an instruction a script or an agent can follow.
21
+ */
22
+ app?: string;
23
+ /** Set on the recursive call after a workspace redirect, so the search happens at most once. */
24
+ redirected?: boolean;
25
+ /**
26
+ * `--url`: the address the app is ALREADY served on, so init starts nothing.
27
+ *
28
+ * Parsed by the CLI since the flag existed and never handed to `init`, which made the
29
+ * package-manager refusal name it as the escape hatch while being unable to see it. The value
30
+ * itself is not read here — only whether one was given — but it is typed as the URL rather than a
31
+ * boolean so the option means the same thing everywhere the flag does.
32
+ */
33
+ url?: string | undefined;
34
+ /**
35
+ * Where the human ran the command, carried across a workspace redirect.
36
+ *
37
+ * The agent rule and `/reticle` command files are read by the AGENT, whose session runs where the
38
+ * human started it — the repo root. Writing them beside the app instead is how a repo with its app
39
+ * at `src/admin` ended up with no `/reticle` at all (#318).
40
+ */
41
+ agentRoot?: string;
42
+ /**
43
+ * Hand the telemetry outcome back rather than emitting it here.
44
+ *
45
+ * `init` now stays to watch for an app to connect, and whether it saw one belongs on the SAME
46
+ * `init_completed` event — the funnel it exists to measure would be double-counted by a second
47
+ * emit and unjoinable as a second event kind. Only the CLI sets this; every other caller keeps
48
+ * today's fire-and-forget behaviour, so no path loses its event by forgetting to report.
49
+ */
50
+ deferOutcome?: boolean;
51
+ /**
52
+ * Whether the caller carries on into booting the app and driving it.
53
+ *
54
+ * Only affects the closing hint, which otherwise tells the reader to restart their dev server and
55
+ * drive a flow by hand — three lines before this command does both.
56
+ */
57
+ continuesToRuntime?: boolean;
58
+ }
59
+ export interface InitIo {
60
+ /** Returns file content or null if it does not exist. Path is project-relative or absolute. */
61
+ readFile(relPath: string): string | null;
62
+ /** Writes content, creating parent directories. Path is project-relative or absolute. */
63
+ writeFile(relPath: string, content: string): void;
64
+ exists(relPath: string): boolean;
65
+ /** The user's home directory (for global agent config like ~/.cursor/mcp.json). */
66
+ homeDir(): string;
67
+ /** Absolute path of the directory `init` is running in — the project's own root. */
68
+ cwd(): string;
69
+ /** Basenames present in the project root. */
70
+ rootFiles(): readonly string[];
71
+ /** Subdirectory names inside a project-relative directory; empty when it isn't one. */
72
+ listDirs(relPath: string): readonly string[];
73
+ /** File (non-directory) basenames inside a project-relative directory, including dotfiles. */
74
+ listFiles(relPath: string): readonly string[];
75
+ /** The same IO re-rooted at a project-relative subdirectory (used for the workspace redirect). */
76
+ scoped(relPath: string): InitIo;
77
+ /** Runs a subprocess to completion (inherits stdio); returns true on exit code 0. */
78
+ exec(command: string, args: readonly string[]): boolean;
79
+ /** Runs a subprocess quietly (no stdio) for a yes/no check; returns true on exit code 0. */
80
+ probe(command: string, args: readonly string[]): boolean;
81
+ /** Can this process write into the project root — see preflight.ts. */
82
+ canWrite(): boolean;
83
+ print(line: string): void;
84
+ /**
85
+ * The capabilities only the daemon has: the release version's tracer, the outcome reporter, the
86
+ * bridge pairing token and the declared install channel. See host.ts for why they are carried
87
+ * here rather than as a second parameter, and why none of them is optional.
88
+ */
89
+ host: InitHost;
90
+ }
91
+ /**
92
+ * What a run established, for a caller that means to continue where init stopped.
93
+ *
94
+ * Everything here was already computed and then thrown away. That was fine while init only wrote
95
+ * files: nobody downstream existed. A caller that goes on to boot the app needs the same answers,
96
+ * and re-deriving them is how two parts of one command end up disagreeing about which directory
97
+ * they are in — which is not hypothetical, because a monorepo redirect re-enters `runInit` with a
98
+ * different cwd and the outer caller never learns that it happened.
99
+ */
100
+ interface InitContext {
101
+ /** The directory actually wired, AFTER any monorepo redirect. */
102
+ readonly appDir: string;
103
+ readonly framework: string;
104
+ readonly packageManager: string;
105
+ /** The project's own dev command, when its scripts name one. Never composed. */
106
+ readonly devCommand?: string | undefined;
107
+ /** Set when init redirected into a workspace app, naming the one it chose. */
108
+ readonly redirectedTo?: string | undefined;
109
+ }
110
+ export interface InitResult {
111
+ ok: boolean;
112
+ applied: number;
113
+ manual: number;
114
+ /** What this run established. Absent only where init exits before establishing anything. */
115
+ context?: InitContext;
116
+ /**
117
+ * The event body this run would report, handed to the caller instead of emitted, when
118
+ * `deferOutcome` is set. Absent on a dry run and on the exits that report for themselves.
119
+ */
120
+ outcome?: InitOutcome;
121
+ }
122
+ export declare function runInit(options: InitOptions, io: InitIo): InitResult;