@layers/amba 1.0.1 → 4.0.2

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.
@@ -14,3 +14,14 @@ export interface ProjectConfig {
14
14
  apiUrl: string;
15
15
  }
16
16
  export declare function loadProjectConfig(cwd?: string): Promise<ProjectConfig>;
17
+ /**
18
+ * Parse a `.env`-style file body into a flat string map.
19
+ *
20
+ * Lines are trimmed; blank lines and `#`-comments are skipped; lines
21
+ * without an `=` are skipped. Values may be wrapped in matching single
22
+ * or double quotes which are stripped on read. Bug fixes should land
23
+ * here once — both `project-config.ts` (resolves `AMBA_PROJECT_ID`)
24
+ * and `commands/functions.ts` (loads `.env.local` for the local dev
25
+ * server) call this.
26
+ */
27
+ export declare function parseEnv(content: string): Record<string, string>;
@@ -0,0 +1,309 @@
1
+ /**
2
+ * Headless agentic sandbox bootstrap.
3
+ *
4
+ * Implements `amba init --sandbox`: the zero-question, no-browser path
5
+ * an AI coding agent runs when a developer pastes the homepage prompt:
6
+ *
7
+ * Run `npx @layers/amba init --sandbox` and follow the
8
+ * instructions it prints.
9
+ *
10
+ * The CLI does everything: synthesize an anonymous email + password,
11
+ * sign the developer up via the public `/v1/auth/developer/signup`
12
+ * endpoint (no Bearer needed), pluck the returned PAT + project
13
+ * credentials, and write them into:
14
+ *
15
+ * - `~/.amba/credentials.json` (chmod 0600)
16
+ * - `<cwd>/.env.local` (.gitignored — SDK reads it)
17
+ * - `<cwd>/AMBA.md` (markdown context for the agent)
18
+ * - every detected MCP client config (`~/.claude.json`,
19
+ * `~/.cursor/mcp.json`, `~/.codeium/windsurf/mcp_config.json`,
20
+ * plus their project-local equivalents WHEN already present —
21
+ * never created from scratch, to avoid cluttering repos)
22
+ *
23
+ * The MCP-config merge is non-destructive: an existing `mcpServers.amba`
24
+ * entry is replaced with the new PAT, but the prior file is copied
25
+ * aside to `<path>.bak-<unix-ms>` first so a developer who had a real
26
+ * production PAT wired in can recover. Every other server entry is
27
+ * preserved. JSON files that already exist but lack an `mcpServers`
28
+ * key gain one; missing files for the global locations get scaffolded
29
+ * with a minimal `{ "mcpServers": { "amba": … }}`.
30
+ *
31
+ * Similarly, a pre-existing `~/.amba/credentials.json` whose `source`
32
+ * is not `'sandbox-init'` is backed up to `credentials.json.bak-<ms>`
33
+ * before the sandbox PAT replaces it. Idempotent re-runs from our own
34
+ * sandbox session do NOT trigger a backup.
35
+ *
36
+ * Provisioning polling: deliberately skipped. The server returns the
37
+ * project row with `provisioning_status: 'provisioning'` immediately and
38
+ * the workflow flips it to `'active'` within ~5s. The agent's next SDK
39
+ * call may briefly retry — fine. Blocking the CLI here would just hide
40
+ * the same wait behind a different progress indicator.
41
+ */
42
+ export interface SandboxSignupRequest {
43
+ email: string;
44
+ password: string;
45
+ name?: string;
46
+ }
47
+ /**
48
+ * Shape returned by `POST /v1/auth/developer/signup`. Only the fields the
49
+ * sandbox flow actually consumes are typed — the response body has more
50
+ * (refresh token, expiry, etc.) but we don't need them here.
51
+ */
52
+ export interface SandboxSignupResponse {
53
+ pat: string;
54
+ project_id: string;
55
+ client_key: string;
56
+ server_key?: string;
57
+ api_url: string;
58
+ /** `'provisioning'` immediately after signup; flips to `'active'` once the workflow finishes. */
59
+ provisioning_status?: string;
60
+ /** Email-verification URL — surfaced by the API so we can echo it for upgrade. */
61
+ verify_url?: string;
62
+ /** The actual email we used (may be the auto-generated one). */
63
+ email: string;
64
+ /**
65
+ * Developer row returned in `data.developer`. Required for callers
66
+ * (e.g. `ensureDeveloperIdentity`) to populate the developer_id on
67
+ * the freshly-minted local credentials without a second round trip
68
+ * to `/developer/me`.
69
+ */
70
+ developer_id: string;
71
+ /** Developer name, if set on the row. */
72
+ developer_name?: string;
73
+ }
74
+ export interface McpClientConfigTarget {
75
+ /** Display name for logging. */
76
+ label: string;
77
+ /** Absolute path on disk. */
78
+ path: string;
79
+ /** Whether to create the file if it doesn't exist. Global configs: yes. Project-local: no. */
80
+ scaffoldIfMissing: boolean;
81
+ }
82
+ export interface SandboxResult {
83
+ email: string;
84
+ projectId: string;
85
+ pat: string;
86
+ patPreview: string;
87
+ clientKey: string;
88
+ /** The exact API URL the signup POST hit. Source of truth for callers. */
89
+ apiUrl: string;
90
+ credentialsPath: string;
91
+ /** Backup of a pre-existing non-sandbox `~/.amba/credentials.json`, or null. */
92
+ credentialsBackedUpTo: string | null;
93
+ envLocalPath: string;
94
+ ambaMdPath: string;
95
+ /** One record per MCP client config we touched; includes per-file backup info. */
96
+ mcpConfigsWritten: McpConfigPathResult[];
97
+ /** Best-guess SDK package for the framework detected in CWD. */
98
+ sdkPackage: string;
99
+ framework: string;
100
+ verifyUrl: string | null;
101
+ provisioningStatus: string;
102
+ /**
103
+ * Absolute path of the `.claude/skills/amba-build/SKILL.md` we wrote
104
+ * during the sandbox init, or `null` if the skill install was skipped
105
+ * (e.g. `--no-skills`).
106
+ */
107
+ skillPath: string | null;
108
+ /**
109
+ * Per-agent setup-guide fan-out result. One entry per writer that
110
+ * succeeded. Empty array when `--no-skills` was passed (the same flag
111
+ * gates both the build-task skill and the cross-agent fan-out — they
112
+ * write into the same project surfaces and there's no use-case for
113
+ * skipping one but not the other).
114
+ */
115
+ setupTargets: Array<{
116
+ target: string;
117
+ path: string;
118
+ mode: 'created' | 'refreshed';
119
+ }>;
120
+ }
121
+ /**
122
+ * Generate a deterministic-looking but globally-unique sandbox email.
123
+ *
124
+ * Pattern: `sandbox-<epoch>-<6char>@layers.com`.
125
+ *
126
+ * The control DB's `developers` table has a UNIQUE(email) constraint and
127
+ * a 5-per-minute / 50-per-day per-IP rate limit on signup. Embedding the
128
+ * epoch + a 6-char nonce keeps the collision probability negligible even
129
+ * across a herd of CI agents all running `amba init --sandbox` from the
130
+ * same VPC.
131
+ *
132
+ * We use `@layers.com` (not the customer's own domain) because the
133
+ * sandbox tier is pre-verification — the developer never receives or
134
+ * actions a verification email for this address. When they want to
135
+ * upgrade, the CLI prints the verify URL the API returned so they can
136
+ * claim a real email in the console.
137
+ */
138
+ export declare function generateSandboxEmail(): string;
139
+ /**
140
+ * Random URL-safe password. 24 raw bytes → 32 base64url chars; well over
141
+ * the 8-char minimum the API enforces, with ~192 bits of entropy.
142
+ *
143
+ * The password is never shown to the developer or written anywhere — the
144
+ * PAT is what gets stored. We generate it solely because `POST /signup`
145
+ * requires it (and demands a non-empty value); a future API change could
146
+ * accept "agent signup" with no password and we'd drop this entirely.
147
+ */
148
+ export declare function generateSandboxPassword(): string;
149
+ /**
150
+ * POST the synthesized credentials at the public signup endpoint.
151
+ *
152
+ * No Bearer auth — this is the bootstrap call that mints one. We use
153
+ * `fetch` directly (not the api-client wrapper) because that wrapper
154
+ * always resolves a bearer token first, which is exactly what we don't
155
+ * have yet.
156
+ *
157
+ * Returns the unwrapped, flattened shape consumed by the rest of the
158
+ * sandbox flow. Throws with a human-readable message on any non-2xx so
159
+ * the CLI's `runAction` wrapper can surface it without crashing on a
160
+ * generic 'fetch failed'.
161
+ */
162
+ export declare function performSandboxSignup(req: SandboxSignupRequest, options?: {
163
+ apiUrl?: string;
164
+ fetchImpl?: typeof fetch;
165
+ }): Promise<SandboxSignupResponse>;
166
+ /**
167
+ * Write or update `<cwd>/.env.local` with the sandbox project's keys.
168
+ *
169
+ * Mirrors the `init` interactive flow exactly so the existing env-read
170
+ * conventions in the SDKs and CLI commands keep working. The merge
171
+ * logic: if the file already exists and contains an `AMBA_PROJECT_ID`
172
+ * line we replace the Amba lines in place; otherwise we append a
173
+ * fresh stanza.
174
+ *
175
+ * `serverKey` is optional — pass it on the new two-scope credential
176
+ * model where init mints both client+server. Pre-existing AMBA_SERVER_KEY
177
+ * lines are refreshed when a new value is provided and removed when
178
+ * serverKey is null AND no prior line existed (no-op on second case).
179
+ */
180
+ export declare function writeSandboxEnvLocal(cwd: string, projectId: string, clientKey: string, apiUrl: string, serverKey?: string | null): Promise<string>;
181
+ /**
182
+ * Write the AMBA.md sandbox-tier guide to `<cwd>/AMBA.md`.
183
+ *
184
+ * Always overwrites — the file is meant to be regenerated, and the
185
+ * interactive `init` flow's longer AMBA.md template is replaced here
186
+ * with a sandbox-specific shorter one (with upgrade instructions).
187
+ */
188
+ export declare function writeSandboxAmbaMd(cwd: string, ctx: {
189
+ projectId: string;
190
+ email: string;
191
+ verifyUrl: string | null;
192
+ sdkPackage: string;
193
+ framework: string;
194
+ apiUrl: string;
195
+ }): Promise<string>;
196
+ /**
197
+ * The canonical Amba MCP entry. Used as the value of
198
+ * `mcpServers.amba` in every detected client config.
199
+ *
200
+ * Shape note: Claude Code, Cursor, and Windsurf all read the same
201
+ * `type` + `url` + `headers` triple. (Windsurf historically also
202
+ * accepted `serverUrl` — we emit `url` to match the modern shape it
203
+ * also accepts, and skip emitting the legacy alias to keep the JSON
204
+ * minimal.)
205
+ */
206
+ export declare function buildAmbaMcpEntry(pat: string): Record<string, unknown>;
207
+ /**
208
+ * Identifier for the MCP client family a config file belongs to.
209
+ * Kept around so callers can reason about which agent runtime a given
210
+ * config path targets — the CLI no longer prints per-client restart
211
+ * bullets (the new flow frames MCP wiring as "active next agent
212
+ * launch" rather than asking the developer to restart), but the
213
+ * classifier is still useful for callers that want to render per-
214
+ * client labels in their own UI.
215
+ */
216
+ export type McpClientKind = 'claude-code' | 'cursor' | 'windsurf';
217
+ /**
218
+ * Map an MCP config file path back to its client family. Returns null
219
+ * for paths that don't match any known config location — defensive
220
+ * against future additions to `mcpClientTargets`.
221
+ *
222
+ * The match is on path tail rather than full equality so the cwd /
223
+ * homedir-injected variants both classify correctly. We deliberately
224
+ * accept both global and project-local Claude Code paths
225
+ * (`.claude.json` and `.mcp.json`) as 'claude-code'.
226
+ */
227
+ export declare function classifyMcpPath(path: string): McpClientKind | null;
228
+ /**
229
+ * Reduce a list of written-config paths to the set of unique client
230
+ * families they belong to. Order: claude-code, cursor, windsurf (so
231
+ * the done-message renders consistently). Skips unclassified paths
232
+ * silently.
233
+ */
234
+ export declare function clientKindsFromPaths(paths: string[]): McpClientKind[];
235
+ /**
236
+ * The list of MCP client config files we probe. Order matters only for
237
+ * the printed report.
238
+ *
239
+ * Project-local entries are listed but only get touched when the file
240
+ * already exists in CWD — we don't want to scatter `.mcp.json` /
241
+ * `.cursor/mcp.json` files into random user repos that have never been
242
+ * MCP-configured.
243
+ */
244
+ export declare function mcpClientTargets(cwd: string, options?: {
245
+ homeDir?: string;
246
+ }): McpClientConfigTarget[];
247
+ /**
248
+ * Per-target result of an MCP config merge.
249
+ *
250
+ * - `path: null` means the target file didn't exist and scaffolding was
251
+ * not allowed (project-local targets in unconfigured repos).
252
+ * - `backedUpTo` is the absolute path of the backup file when an
253
+ * existing `mcpServers.amba` entry was present (so a developer who
254
+ * had a real PAT wired in can recover); `null` when no backup was
255
+ * needed.
256
+ */
257
+ export interface McpConfigWriteResult {
258
+ path: string | null;
259
+ backedUpTo: string | null;
260
+ }
261
+ /**
262
+ * Merge the `amba` entry into a single client config file.
263
+ *
264
+ * Strategy:
265
+ * - If the file exists, load + JSON-parse. If parse fails, throw with
266
+ * a clear "we won't clobber malformed JSON" error.
267
+ * - If the file doesn't exist and scaffolding is allowed, create the
268
+ * parent dir and write `{ "mcpServers": { "amba": ... } }`.
269
+ * - In all cases, `mcpServers.amba` ends up set; every other
270
+ * `mcpServers.*` entry is preserved.
271
+ *
272
+ * Real-credential safety: if the existing file ALREADY has a
273
+ * `mcpServers.amba` entry, we copy the whole file aside to
274
+ * `<path>.bak-<unix-ms>` BEFORE merging. This protects a developer who
275
+ * had a real production PAT wired in and then ran `amba init --sandbox`
276
+ * to "try" the flow. Idempotent re-runs from the same sandbox session
277
+ * still trigger a backup — cheap, and the developer can `rm *.bak-*`
278
+ * any time.
279
+ */
280
+ export declare function mergeMcpConfigFile(target: McpClientConfigTarget, pat: string): Promise<McpConfigWriteResult>;
281
+ /**
282
+ * Per-target result returned by `writeAllMcpConfigs`. Same shape as
283
+ * `McpConfigWriteResult` but always carries a non-null `path` (skipped
284
+ * targets aren't included in the list).
285
+ */
286
+ export interface McpConfigPathResult {
287
+ path: string;
288
+ backedUpTo: string | null;
289
+ }
290
+ /**
291
+ * Probe every known MCP client location and merge our entry into the
292
+ * ones that exist (or that are flagged scaffoldIfMissing). Returns one
293
+ * record per touched file (skipped files are omitted).
294
+ *
295
+ * `warn` is invoked (instead of `console.warn`) for per-target errors
296
+ * so callers using `--json` can route those notices to stderr and keep
297
+ * stdout machine-parseable.
298
+ */
299
+ export declare function writeAllMcpConfigs(cwd: string, pat: string, options?: {
300
+ homeDir?: string;
301
+ warn?: (msg: string) => void;
302
+ }): Promise<McpConfigPathResult[]>;
303
+ /**
304
+ * Render the canonical Amba MCP snippet for clients we can't auto-wire
305
+ * (anything outside the {Claude Code, Cursor, Windsurf} set). Printed by
306
+ * the CLI when no client configs are detected so the developer at least
307
+ * has a paste-ready JSON blob.
308
+ */
309
+ export declare function formatManualMcpSnippet(pat: string): string;
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Amba skill bundle installer.
3
+ *
4
+ * The bundled `skill-bundle/` directory contains `SKILL.md` plus a
5
+ * `references/` folder with one file per Amba surface area. The skill
6
+ * teaches the agent the classify → confirm → wire-up playbook for
7
+ * adding Amba primitives to a developer's codebase. See
8
+ * `packages/cli/skill-bundle/SKILL.md` for the source.
9
+ *
10
+ * Why ship a bundled skill (instead of `npx skills add layers/amba`):
11
+ * the CLI run is the same install step. Bundling avoids a second
12
+ * fetch, keeps the skill version locked to the CLI version, and means
13
+ * `amba init` produces a fully-wired agent on offline networks too.
14
+ *
15
+ * Cross-agent install — we drop the same body into every detected
16
+ * coding agent's skill directory. Agents read their own location:
17
+ *
18
+ * - Claude Code: `.claude/skills/amba/`
19
+ * - Cursor: `.cursor/skills/amba/`
20
+ * - Codex CLI: `.codex/skills/amba/`
21
+ * - Windsurf: `.windsurf/skills/amba/`
22
+ *
23
+ * We also write a project-root copy at `.agents/skills/amba/` which
24
+ * the `npx skills add ...` distribution tool reads from (and which any
25
+ * agent that pre-registers an `.agents/skills/` lookup picks up). Five
26
+ * locations, one body — same fan-out pattern `writeAllSetupTargets`
27
+ * already uses for the legacy setup guide.
28
+ *
29
+ * Idempotency: re-running `amba init` overwrites the bundled
30
+ * `SKILL.md` and `references/*.md` so every developer ends up on the
31
+ * latest playbook. We back up a pre-existing `SKILL.md` to a sibling
32
+ * `.bak-<unix-ms>` ONLY when its first frontmatter key (`name:`) is
33
+ * not `amba` — that's the signal it was hand-authored / unrelated and
34
+ * shouldn't be silently clobbered. Bundled Amba files are refreshed
35
+ * without backup.
36
+ */
37
+ export type SkillInstallTargetKind = 'claude-code' | 'cursor' | 'codex' | 'windsurf' | 'generic-agents';
38
+ export interface SkillInstallTarget {
39
+ kind: SkillInstallTargetKind;
40
+ /** Absolute path to the `amba/` directory inside the agent's skill root. */
41
+ path: string;
42
+ }
43
+ export interface SkillFileWritten {
44
+ /** Absolute path of the file written. */
45
+ path: string;
46
+ /** Backup path when an existing non-Amba SKILL.md was preserved; otherwise null. */
47
+ backedUpTo: string | null;
48
+ }
49
+ export interface SkillInstallResult {
50
+ /** Target directory the skill was installed under. */
51
+ target: SkillInstallTarget;
52
+ /** Files written this run. */
53
+ files: SkillFileWritten[];
54
+ }
55
+ /**
56
+ * Resolve the bundled skill source directory.
57
+ *
58
+ * The bundle lives at `<package-root>/skill-bundle/` in both the
59
+ * source tree and the published tarball (via `files[]` in
60
+ * `package.json`). From a built `dist/commands/init.js` the path is
61
+ * `../../skill-bundle/`. From the source tree
62
+ * (`src/skill-installer.ts`) the path is `../skill-bundle/`. We try
63
+ * both relative to `import.meta.url` and pick the one that exists.
64
+ */
65
+ export declare function resolveBundleDir(): Promise<string>;
66
+ /**
67
+ * List the five install targets the CLI fans out to. Project-local
68
+ * directories (`<cwd>/.claude/skills/amba/`, etc.) — the agent reads
69
+ * project-local skills with priority over global ones, so this is the
70
+ * canonical install location for a tool meant to wire up THIS project.
71
+ */
72
+ export declare function skillInstallTargets(cwd: string): SkillInstallTarget[];
73
+ /**
74
+ * Copy SKILL.md + every file under references/ into the target
75
+ * directory. Creates the directory tree if missing. Returns the list
76
+ * of files touched and any backup paths.
77
+ *
78
+ * Backup rule: a pre-existing `SKILL.md` is backed up to
79
+ * `SKILL.md.bak-<unix-ms>` ONLY when its first `name:` frontmatter
80
+ * line is NOT `name: amba`. That's the signal it was authored by the
81
+ * user for an unrelated purpose and shouldn't be silently overwritten.
82
+ * Amba-owned files get refreshed without backup so developers
83
+ * tracking the latest playbook don't accumulate junk.
84
+ */
85
+ export declare function installSkillBundle(cwd: string, options?: {
86
+ bundleDir?: string;
87
+ }): Promise<SkillInstallResult[]>;
88
+ /**
89
+ * Convenience: returns the count of skill files written and the list
90
+ * of target kinds, for the CLI's done-message summary.
91
+ */
92
+ export declare function summarizeSkillInstall(results: SkillInstallResult[]): {
93
+ totalFiles: number;
94
+ targetKinds: SkillInstallTargetKind[];
95
+ };
@@ -0,0 +1,146 @@
1
+ /**
2
+ * App-kind presets for the "Hey Claude, get amba.dev in my app" onboarding.
3
+ *
4
+ * The classifier picks one of 10 kinds (9 opinionated + `custom`); the
5
+ * wire-up engine reads the matching preset to know:
6
+ *
7
+ * - which Amba surface areas to enable (used to scope the docs the
8
+ * coding agent pulls into context),
9
+ * - which follow-up questions to ask the developer when the preset has
10
+ * genuine ambiguity (e.g. friends-vs-global leaderboard scope),
11
+ * - the exact MCP tool calls to issue against `mcp.amba.dev` to create
12
+ * the starter resources (currencies, achievements, streaks, …),
13
+ * - the literal SDK init snippet to paste into the developer's project
14
+ * for whichever of the six supported stacks they're on.
15
+ *
16
+ * The MCP tool names and argument shapes here MUST match the registered
17
+ * tools in `packages/mcp/src/tools/*`. The validation surface for that
18
+ * is the per-tool zod schema; this file is the single source of truth
19
+ * for "what does Amba install for a fitness app" and lives in the CLI
20
+ * because the CLI's classifier (Task #3) reads it directly. The MCP
21
+ * server doesn't import this — its job is to expose the underlying
22
+ * tools, not to know about preset choices.
23
+ *
24
+ * Naming discipline (see CLAUDE.md): no `cf_*` / `neon_*` / `temporal_*`
25
+ * field names; achievement icons reference customer-replaceable URLs on
26
+ * the Amba CDN; SDK snippets never name an underlying vendor.
27
+ */
28
+ /**
29
+ * The closed set of app kinds the classifier returns. `custom` is the
30
+ * escape hatch — when the classifier can't confidently pick, the CLI
31
+ * presents the full surface-area picker instead of applying a preset.
32
+ */
33
+ export type AppKind = 'fitness' | 'social' | 'marketplace' | 'productivity' | 'education' | 'game' | 'dating' | 'content_creator' | 'ai_chatbot' | 'custom';
34
+ /**
35
+ * The surface areas (Amba primitives) a preset can opt into.
36
+ *
37
+ * `identity` is implicit in every preset — anonymous user provisioning is
38
+ * a precondition of every other surface — but listing it explicitly in
39
+ * `surfaces` makes the wire-up output self-describing.
40
+ */
41
+ export type AmbaSurface = 'identity' | 'xp' | 'streaks' | 'achievements' | 'leaderboards' | 'currencies' | 'catalog' | 'stores' | 'inventory' | 'friendships' | 'groups' | 'feeds' | 'messaging' | 'moderation' | 'reviews' | 'push' | 'content' | 'referrals' | 'deeplinks' | 'segments' | 'onboarding' | 'ai_prompts';
42
+ /** The 6 SDK stacks for which we emit a copy-pasteable init snippet. */
43
+ export type SdkStack = 'expo' | 'reactNative' | 'web' | 'swift' | 'kotlin' | 'flutter';
44
+ export interface FollowUpOption {
45
+ value: string;
46
+ label: string;
47
+ /**
48
+ * Marks the option the CLI selects by default if the developer accepts
49
+ * the recommended answer without interaction. Exactly zero or one
50
+ * option per question carries this flag.
51
+ */
52
+ recommended?: boolean;
53
+ }
54
+ export interface FollowUpQuestion {
55
+ /** The surface the answer mutates. Used by the wire-up engine to route. */
56
+ surface: AmbaSurface;
57
+ /**
58
+ * Stable identifier used to look up the answer in the wire-up engine.
59
+ * Tied to `WireUpAction.dependsOn` references.
60
+ */
61
+ id: string;
62
+ question: string;
63
+ options: FollowUpOption[];
64
+ }
65
+ /**
66
+ * A single MCP tool call the wire-up engine should issue.
67
+ *
68
+ * `args` mirrors the tool's zod input schema MINUS `project_id`, which
69
+ * the engine injects from the active project context — the developer
70
+ * never passes a project id at this layer.
71
+ *
72
+ * `dependsOn` is the id of a follow-up question whose answer mutates
73
+ * the action (e.g. leaderboard scope drives `metric` / `name`). The
74
+ * engine reads the answer, applies `whenAnswer` to transform `args`,
75
+ * and skips the action entirely if `whenAnswer` returns `null`.
76
+ */
77
+ export interface WireUpAction {
78
+ tool: string;
79
+ args: Record<string, unknown>;
80
+ /**
81
+ * Optional follow-up dependency. The engine resolves the developer's
82
+ * answer to `dependsOn.id`, then calls `dependsOn.whenAnswer(value)`
83
+ * to produce the final args. Returning `null` means "skip this
84
+ * action for that answer."
85
+ */
86
+ dependsOn?: {
87
+ id: string;
88
+ whenAnswer: (answer: string) => Record<string, unknown> | null;
89
+ };
90
+ }
91
+ export interface SdkInitSnippets {
92
+ /** Expo (managed) — uses `@layers/amba-expo`. */
93
+ expo: string;
94
+ /** Bare React Native — uses `@layers/amba-react-native`. */
95
+ reactNative: string;
96
+ /** Browser / SSR — uses `@layers/amba-web`. */
97
+ web: string;
98
+ /** iOS / macOS / tvOS / watchOS — Swift Package Manager. */
99
+ swift: string;
100
+ /** Android — Maven Central artifact `com.layers.amba:amba-sdk-android`. */
101
+ kotlin: string;
102
+ /** Flutter / Dart — `amba` on pub.dev. */
103
+ flutter: string;
104
+ }
105
+ export interface AppKindPreset {
106
+ kind: AppKind;
107
+ label: string;
108
+ description: string;
109
+ /**
110
+ * Surfaces to enable. `identity` always appears first — every preset
111
+ * requires user identity. The order in the array is the order the
112
+ * wire-up engine renders to the developer (logical, not arbitrary).
113
+ */
114
+ surfaces: AmbaSurface[];
115
+ /**
116
+ * Genuinely ambiguous choices the developer needs to make. Decisions
117
+ * with a clear default (e.g. "should we install identity?" — yes,
118
+ * always) are NOT follow-ups; they're baked into `wireUp.actions`.
119
+ */
120
+ followUpQuestions: FollowUpQuestion[];
121
+ wireUp: {
122
+ actions: WireUpAction[];
123
+ sdkInit: SdkInitSnippets;
124
+ };
125
+ }
126
+ /**
127
+ * Canonical preset registry. Lookup is by `AppKind`; iteration order is
128
+ * the order the CLI presents to the developer when classification is
129
+ * ambiguous and we fall back to a manual menu (alphabetical-ish by
130
+ * common usage, with `custom` last).
131
+ */
132
+ export declare const PRESETS: Record<AppKind, AppKindPreset>;
133
+ /**
134
+ * Ordered list of the 9 non-custom presets. Useful when the CLI
135
+ * renders the "pick an app type" menu — `custom` is appended by the
136
+ * caller as the final escape-hatch option.
137
+ */
138
+ export declare const PRESET_ORDER: readonly AppKind[];
139
+ /**
140
+ * Type-narrowing helper for the classifier output. Returns the preset
141
+ * verbatim when the kind is known; the caller is expected to surface a
142
+ * helpful error if classification produced a value outside the closed
143
+ * set (the type system blocks that in TypeScript, but the boundary
144
+ * between classifier-JSON and this module is a runtime trust check).
145
+ */
146
+ export declare function getPreset(kind: AppKind): AppKindPreset;