From d7dbf033c53f20f2dca7297fd994834e77bb8ef4 Mon Sep 17 00:00:00 2001 From: Alex Bespoyasov Date: Thu, 23 Jul 2026 15:56:31 +0200 Subject: [PATCH 1/7] fix: ensure to inform agents about selected framework --- src/lib/onboarding-prompt/integrate.ts | 4 ++++ src/lib/onboarding-prompt/steps/integrate-via-skill.md | 3 +++ 2 files changed, 7 insertions(+) diff --git a/src/lib/onboarding-prompt/integrate.ts b/src/lib/onboarding-prompt/integrate.ts index c969996..8066c7d 100644 --- a/src/lib/onboarding-prompt/integrate.ts +++ b/src/lib/onboarding-prompt/integrate.ts @@ -14,6 +14,8 @@ export function integrateViaSkill( isEmptyProject: boolean, ide: ChosenIde, ): string { + const needsReactGotchas = /react|nextjs|next/i.test(framework); + return loadStep('integrate-via-skill.md', { STEP: step, FRAMEWORK: framework, @@ -26,6 +28,8 @@ export function integrateViaSkill( INSERTION_HINT: isEmptyProject ? 'For fresh scaffolds, use the scaffold\'s default heading or welcome text as the insertion point — the "aha" moment works just as well on boilerplate. Demonstrate at least two use cases (e.g. a gradual rollout for a heading change and a kill switch for a feature section).' : 'Read the top 2–3 candidate files and pick the best one: a single visible string or component, no complex conditionals already wrapping it, in a file the user will recognize.', + + REACT_GOTCHAS: needsReactGotchas ? REACT_GOTCHAS : '', }); } diff --git a/src/lib/onboarding-prompt/steps/integrate-via-skill.md b/src/lib/onboarding-prompt/steps/integrate-via-skill.md index e62d044..c26fcbe 100644 --- a/src/lib/onboarding-prompt/steps/integrate-via-skill.md +++ b/src/lib/onboarding-prompt/steps/integrate-via-skill.md @@ -2,6 +2,8 @@ Print "STATUS: Analyzing project for flag integration points..." +The detected framework is **{{FRAMEWORK}}**. + Read `{{SKILLS_DIR}}/analyze-project/SKILL.md` as a **methodology reference** — use it for what to analyze, how to identify flag candidates, which SDK to pick, and how to create and wire flags. Ignore its output formatting entirely: no step tracker, no EDUCATE blocks, no AskUserQuestion calls. Execute the skill's workflow automatically, without pausing for user input: @@ -29,3 +31,4 @@ The only user-visible output is STATUS-prefixed lines (~60 chars max). No step t Read the client secret from CONFIDENCE_CLIENT_SECRET env var in all generated code. Use the OpenFeature API with local resolve where supported. Access flag values via dot notation: `flag-name.property`. +{{REACT_GOTCHAS}} From 197284daa416860f2340ad7ee88641387a56e374 Mon Sep 17 00:00:00 2001 From: Alex Bespoyasov Date: Thu, 23 Jul 2026 15:56:40 +0200 Subject: [PATCH 2/7] chore: improve naming --- src/lib/onboarding-prompt/index.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/lib/onboarding-prompt/index.ts b/src/lib/onboarding-prompt/index.ts index bf3a2ff..49e61fc 100644 --- a/src/lib/onboarding-prompt/index.ts +++ b/src/lib/onboarding-prompt/index.ts @@ -35,27 +35,27 @@ export function buildOnboardingPrompt({ const steps = new StepCounter(isEmptyProject ? 2 : 1); const tools = buildToolVars(ide); - const includingFlags = goal === 'feature-flags' || goal === 'all'; - const includingRecording = goal === 'session-recording' || goal === 'all'; - const usingSkill = includingFlags && hasPlugins; + const withFlags = goal === 'feature-flags' || goal === 'all'; + const withRecordings = goal === 'session-recording' || goal === 'all'; + const viaSkill = withFlags && hasPlugins; const sections = [ preamble(framework, projectDir, isEmptyProject, goal), preflight(tools), addIf(isEmptyProject, () => scaffold(framework, steps.next())), - usingSkill + viaSkill ? [integrateViaSkill(framework, steps.next(), isEmptyProject, ide)] : [ - addIf(includingFlags, () => determineSDK(framework, steps.next(), tools)), - addIf(includingFlags, () => resolveClient(framework, steps.next(), tools)), - addIf(includingFlags, () => + addIf(withFlags, () => determineSDK(framework, steps.next(), tools)), + addIf(withFlags, () => resolveClient(framework, steps.next(), tools)), + addIf(withFlags, () => integrateSDK(steps.next(), steps.current - 2, isEmptyProject, tools), ), ], - addIf(includingRecording, () => determineRecordingSDK(framework, steps.next(), tools)), - addIf(includingRecording, () => integrateRecording(steps.next(), isEmptyProject)), + addIf(withRecordings, () => determineRecordingSDK(framework, steps.next(), tools)), + addIf(withRecordings, () => integrateRecording(steps.next(), isEmptyProject)), ...migrations.map((m) => migrateFlags(m, steps.next())), From 3ee72e1018391c094eb200d036eacbbd67d93988 Mon Sep 17 00:00:00 2001 From: Alex Bespoyasov Date: Thu, 23 Jul 2026 16:02:44 +0200 Subject: [PATCH 3/7] fix: include guardrails for `integrate-via-skill` --- src/lib/onboarding-prompt/integrate.ts | 1 + .../onboarding-prompt/steps/integrate-via-skill.md | 11 ++++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/lib/onboarding-prompt/integrate.ts b/src/lib/onboarding-prompt/integrate.ts index 8066c7d..948ad15 100644 --- a/src/lib/onboarding-prompt/integrate.ts +++ b/src/lib/onboarding-prompt/integrate.ts @@ -29,6 +29,7 @@ export function integrateViaSkill( ? 'For fresh scaffolds, use the scaffold\'s default heading or welcome text as the insertion point — the "aha" moment works just as well on boilerplate. Demonstrate at least two use cases (e.g. a gradual rollout for a heading change and a kill switch for a feature section).' : 'Read the top 2–3 candidate files and pick the best one: a single visible string or component, no complex conditionals already wrapping it, in a file the user will recognize.', + FLAG_GUIDANCE: isEmptyProject ? FLAG_GUIDANCE_EMPTY : FLAG_GUIDANCE_EXISTING, REACT_GOTCHAS: needsReactGotchas ? REACT_GOTCHAS : '', }); } diff --git a/src/lib/onboarding-prompt/steps/integrate-via-skill.md b/src/lib/onboarding-prompt/steps/integrate-via-skill.md index c26fcbe..4eed239 100644 --- a/src/lib/onboarding-prompt/steps/integrate-via-skill.md +++ b/src/lib/onboarding-prompt/steps/integrate-via-skill.md @@ -29,6 +29,11 @@ The only user-visible output is STATUS-prefixed lines (~60 chars max). No step t - After wiring each flag: "STATUS: Integrated flag: " - "STATUS: Verifying project builds..." -Read the client secret from CONFIDENCE_CLIENT_SECRET env var in all generated code. -Use the OpenFeature API with local resolve where supported. Access flag values via dot notation: `flag-name.property`. -{{REACT_GOTCHAS}} +**Guardrails — apply regardless of what the skill says:** + +- Read the client secret from CONFIDENCE_CLIENT_SECRET env var in all generated code — never hardcode it. Write the secret to `.env` and ensure `.env` is listed in `.gitignore`. +- Use the OpenFeature API with local resolve where supported. Access flag values via dot notation: `flag-name.property`. +- Set up the evaluation context with a stable `targeting_key` (user ID, session ID, or anonymous ID) and any attributes the app already has (`country`, `plan`, `device`). Don't fabricate attributes. +- Use only SDK APIs from the docs integration guide — do not improvise method names or signatures from memory. + {{FLAG_GUIDANCE}} + {{REACT_GOTCHAS}} From 757dde466c8b7c68f97b55143a5c0caff64f239c Mon Sep 17 00:00:00 2001 From: Alex Bespoyasov Date: Thu, 23 Jul 2026 16:02:58 +0200 Subject: [PATCH 4/7] fix: clarify phrasing for formatting rules --- src/lib/onboarding-prompt/steps/integrate-via-skill.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/onboarding-prompt/steps/integrate-via-skill.md b/src/lib/onboarding-prompt/steps/integrate-via-skill.md index 4eed239..512f602 100644 --- a/src/lib/onboarding-prompt/steps/integrate-via-skill.md +++ b/src/lib/onboarding-prompt/steps/integrate-via-skill.md @@ -15,7 +15,7 @@ Execute the skill's workflow automatically, without pausing for user input: {{INSERTION_HINT}} -**Output format — use this instead of anything in the skill:** +**Output format — use this instead of the skill's formatting:** The only user-visible output is STATUS-prefixed lines (~60 chars max). No step tracker boxes, no EDUCATE blocks, no headers, no AskUserQuestion. Print STATUS lines before each phase and periodically within longer phases: From 23ed531dc1cecb8cc52aba4c7d137c5301956e50 Mon Sep 17 00:00:00 2001 From: Alex Bespoyasov Date: Thu, 23 Jul 2026 16:07:47 +0200 Subject: [PATCH 5/7] chore: ignore changelog in format check --- .prettierignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.prettierignore b/.prettierignore index 0c09424..32405c6 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,3 +1,4 @@ pnpm-lock.yaml dist/ node_modules/ +CHANGELOG.md From 15d32b6911b1dc4ab088b3285a2d90ec58844e46 Mon Sep 17 00:00:00 2001 From: Alex Bespoyasov Date: Fri, 24 Jul 2026 08:48:17 +0200 Subject: [PATCH 6/7] chore: handle missing CLIs --- src/integrations/claude/onboarding.ts | 33 ++++++++++++++++----------- src/integrations/codex/onboarding.ts | 25 ++++++++++++-------- src/integrations/cursor/onboarding.ts | 26 +++++++++++++-------- src/integrations/utils.ts | 7 ++++++ 4 files changed, 59 insertions(+), 32 deletions(-) diff --git a/src/integrations/claude/onboarding.ts b/src/integrations/claude/onboarding.ts index ca39886..f39a3c4 100644 --- a/src/integrations/claude/onboarding.ts +++ b/src/integrations/claude/onboarding.ts @@ -3,6 +3,7 @@ import { createInterface } from 'node:readline'; import type { OnboardingOpts, OnboardingCallbacks } from '../types.js'; import { STATUS_PREFIX } from '../constants.js'; import { type StreamEvent, extractTextLines } from '../stream-json.js'; +import { spawnErrorMessage } from '../utils.js'; export function runOnboarding( opts: OnboardingOpts, @@ -12,18 +13,24 @@ export function runOnboarding( ? { ...globalThis.process.env, CONFIDENCE_ACCESS_TOKEN: opts.token } : undefined; - const child = spawn( - 'claude', - ['--print', '--output-format', 'stream-json', '--verbose', opts.prompt], - { - cwd: opts.projectDir, - timeout: 300000, - stdio: ['ignore', 'pipe', 'pipe'], - env, - }, - ); + let child: ChildProcess; + try { + child = spawn( + 'claude', + ['--print', '--output-format', 'stream-json', '--verbose', opts.prompt], + { + cwd: opts.projectDir, + timeout: 300000, + stdio: ['ignore', 'pipe', 'pipe'], + env, + }, + ); + } catch (err) { + callbacks.onError(spawnErrorMessage('claude', err as NodeJS.ErrnoException)); + return null; + } - if (!child?.stdout) return null; + if (!child.stdout) return null; const allLines: string[] = []; let stderrBuf = ''; @@ -66,9 +73,9 @@ export function runOnboarding( callbacks.onComplete(allLines); }); - child.on('error', (err: Error) => { + child.on('error', (err: NodeJS.ErrnoException) => { rl.close(); - callbacks.onError(err.message); + callbacks.onError(spawnErrorMessage('claude', err)); }); return child; diff --git a/src/integrations/codex/onboarding.ts b/src/integrations/codex/onboarding.ts index 6f30177..3323f5b 100644 --- a/src/integrations/codex/onboarding.ts +++ b/src/integrations/codex/onboarding.ts @@ -2,6 +2,7 @@ import { type ChildProcess, spawn } from 'node:child_process'; import { createInterface } from 'node:readline'; import type { OnboardingOpts, OnboardingCallbacks } from '../types.js'; import { STATUS_PREFIX } from '../constants.js'; +import { spawnErrorMessage } from '../utils.js'; type CodexEvent = { type: string; @@ -19,16 +20,22 @@ export function runOnboarding( ? { ...globalThis.process.env, CONFIDENCE_ACCESS_TOKEN: opts.token } : undefined; - const child = spawn('codex', ['exec', '--json', '--sandbox', 'workspace-write', '-'], { - cwd: opts.projectDir, - timeout: 300000, - stdio: ['pipe', 'pipe', 'pipe'], - env, - }); + let child: ChildProcess; + try { + child = spawn('codex', ['exec', '--json', '--sandbox', 'workspace-write', '-'], { + cwd: opts.projectDir, + timeout: 300000, + stdio: ['pipe', 'pipe', 'pipe'], + env, + }); + } catch (err) { + callbacks.onError(spawnErrorMessage('codex', err as NodeJS.ErrnoException)); + return null; + } child.stdin?.end(opts.prompt); - if (!child?.stdout) return null; + if (!child.stdout) return null; const allLines: string[] = []; let stderrBuf = ''; @@ -74,9 +81,9 @@ export function runOnboarding( callbacks.onComplete(allLines); }); - child.on('error', (err: Error) => { + child.on('error', (err: NodeJS.ErrnoException) => { rl.close(); - callbacks.onError(err.message); + callbacks.onError(spawnErrorMessage('codex', err)); }); return child; diff --git a/src/integrations/cursor/onboarding.ts b/src/integrations/cursor/onboarding.ts index 55f15dc..63684b1 100644 --- a/src/integrations/cursor/onboarding.ts +++ b/src/integrations/cursor/onboarding.ts @@ -2,7 +2,7 @@ import { type ChildProcess, spawn } from 'node:child_process'; import { createInterface } from 'node:readline'; import type { OnboardingOpts, OnboardingCallbacks } from '../types.js'; import { type StreamEvent, extractTextLines } from '../stream-json.js'; -import { normalizeStatusLine } from '../utils.js'; +import { normalizeStatusLine, spawnErrorMessage } from '../utils.js'; export function runOnboarding( opts: OnboardingOpts, @@ -21,14 +21,20 @@ export function runOnboarding( '--auto-review', opts.prompt, ]; - const child = spawn('cursor', args, { - cwd: opts.projectDir, - timeout: 300000, - stdio: ['ignore', 'pipe', 'pipe'], - env, - }); + let child: ChildProcess; + try { + child = spawn('cursor', args, { + cwd: opts.projectDir, + timeout: 300000, + stdio: ['ignore', 'pipe', 'pipe'], + env, + }); + } catch (err) { + callbacks.onError(spawnErrorMessage('cursor', err as NodeJS.ErrnoException)); + return null; + } - if (!child?.stdout) return null; + if (!child.stdout) return null; const allLines: string[] = []; let stderrBuf = ''; @@ -69,9 +75,9 @@ export function runOnboarding( callbacks.onComplete(allLines); }); - child.on('error', (err: Error) => { + child.on('error', (err: NodeJS.ErrnoException) => { rl.close(); - callbacks.onError(err.message); + callbacks.onError(spawnErrorMessage('cursor', err)); }); return child; diff --git a/src/integrations/utils.ts b/src/integrations/utils.ts index 35312f2..979030a 100644 --- a/src/integrations/utils.ts +++ b/src/integrations/utils.ts @@ -3,3 +3,10 @@ import { STATUS_PREFIX } from './constants.js'; export function normalizeStatusLine(line: string) { return line.startsWith(STATUS_PREFIX) ? line.slice(STATUS_PREFIX.length) : line; } + +export function spawnErrorMessage(bin: string, err: NodeJS.ErrnoException): string { + if (err.code === 'ENOEXEC' || err.code === 'ENOENT') { + return `${bin} CLI not found or not executable. Make sure it is installed and on your PATH.`; + } + return err.message; +} From 2db80607bdd26a25e6eb15db9cd9a7329f646d12 Mon Sep 17 00:00:00 2001 From: Alex Bespoyasov Date: Fri, 24 Jul 2026 08:54:34 +0200 Subject: [PATCH 7/7] chore: extend allowed duration for agent onboarding process --- src/integrations/claude/onboarding.ts | 2 +- src/integrations/codex/onboarding.ts | 2 +- src/integrations/cursor/onboarding.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/integrations/claude/onboarding.ts b/src/integrations/claude/onboarding.ts index f39a3c4..d15fd24 100644 --- a/src/integrations/claude/onboarding.ts +++ b/src/integrations/claude/onboarding.ts @@ -20,7 +20,7 @@ export function runOnboarding( ['--print', '--output-format', 'stream-json', '--verbose', opts.prompt], { cwd: opts.projectDir, - timeout: 300000, + timeout: 600_000, stdio: ['ignore', 'pipe', 'pipe'], env, }, diff --git a/src/integrations/codex/onboarding.ts b/src/integrations/codex/onboarding.ts index 3323f5b..f231aca 100644 --- a/src/integrations/codex/onboarding.ts +++ b/src/integrations/codex/onboarding.ts @@ -24,7 +24,7 @@ export function runOnboarding( try { child = spawn('codex', ['exec', '--json', '--sandbox', 'workspace-write', '-'], { cwd: opts.projectDir, - timeout: 300000, + timeout: 600_000, stdio: ['pipe', 'pipe', 'pipe'], env, }); diff --git a/src/integrations/cursor/onboarding.ts b/src/integrations/cursor/onboarding.ts index 63684b1..fdc749f 100644 --- a/src/integrations/cursor/onboarding.ts +++ b/src/integrations/cursor/onboarding.ts @@ -25,7 +25,7 @@ export function runOnboarding( try { child = spawn('cursor', args, { cwd: opts.projectDir, - timeout: 300000, + timeout: 600_000, stdio: ['ignore', 'pipe', 'pipe'], env, });