@stackwright-pro/raft 1.0.0-alpha.122 → 1.0.0-alpha.124
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.
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -74,6 +74,7 @@ function parseArgs(argv) {
|
|
|
74
74
|
qaOnly: false,
|
|
75
75
|
qaDevServerTimeoutMs: 6e4,
|
|
76
76
|
parallelPhases: false,
|
|
77
|
+
dataflowScheduling: false,
|
|
77
78
|
subagentResponses: false
|
|
78
79
|
};
|
|
79
80
|
const raw = argv.slice(2);
|
|
@@ -111,6 +112,9 @@ function parseArgs(argv) {
|
|
|
111
112
|
case "--parallel-phases":
|
|
112
113
|
args.parallelPhases = true;
|
|
113
114
|
break;
|
|
115
|
+
case "--dataflow-scheduling":
|
|
116
|
+
args.dataflowScheduling = true;
|
|
117
|
+
break;
|
|
114
118
|
case "--subagent-responses":
|
|
115
119
|
args.subagentResponses = true;
|
|
116
120
|
break;
|
|
@@ -147,6 +151,7 @@ Options:
|
|
|
147
151
|
--qa-only Skip the otter pipeline; spawn dev server and run QA directly
|
|
148
152
|
--qa-dev-server-timeout-ms=<n> Dev server startup timeout in ms for QA gate (default: 60000)
|
|
149
153
|
--parallel-phases Enable parallel specialist invocation within dependency waves (~40% wall-time reduction). Default: off (serial).
|
|
154
|
+
--dataflow-scheduling Dissolve wave barriers: fire phases as deps satisfy, not when the whole wave clears. Default: off (wave-parallel mode). (swp-ioc7)
|
|
150
155
|
--subagent-responses Capture full sub-agent response text in raft-puppy NDJSON telemetry (agent_response events). Verbose \u2014 can produce large logs. Default: off.
|
|
151
156
|
--help, -h Show this help
|
|
152
157
|
|
|
@@ -913,6 +918,7 @@ async function main() {
|
|
|
913
918
|
if (args.nonInteractive) initCtx["nonInteractive"] = true;
|
|
914
919
|
if (args.devOnly) initCtx["devOnly"] = true;
|
|
915
920
|
if (args.parallelPhases) initCtx["parallelPhases"] = true;
|
|
921
|
+
if (args.dataflowScheduling) initCtx["dataflowScheduling"] = true;
|
|
916
922
|
initCtx["skipQa"] = args.skipQa;
|
|
917
923
|
initCtx["qaOnly"] = args.qaOnly;
|
|
918
924
|
initCtx["devServer"] = {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/lib.ts","../src/dev-server.ts"],"sourcesContent":["/**\n * @stackwright-pro/raft — Launch the Pro Otter Raft\n *\n * Writes init-context, verifies otter integrity, and spawns code-puppy\n * in foreman mode. The MCP tools handle everything after that.\n *\n * Replaces the deprecated Python cli_adapter.py → ForemanSession → os.execvpe() chain.\n */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync, closeSync } from 'fs';\nimport { join, resolve } from 'path';\nimport { spawn } from 'child_process';\nimport { verifyAllOtters } from '@stackwright-pro/mcp/integrity';\nimport { buildTypeSchemaSummary } from '@stackwright-pro/mcp/type-schemas';\nimport {\n parseArgs,\n printHelp,\n writeInitContext,\n findCodePuppy,\n validateBinaryVersion,\n migrateWorkspaceDir,\n ensureWorkspaceConfig,\n ensureMcpConfig,\n syncAgents,\n printResumeStatus,\n isPipelineResumable,\n resolveOtterDir,\n buildCodePuppyEnv,\n buildLauncherEnvOverrides,\n buildSpawnConfig,\n die,\n log,\n verbose,\n} from './lib.js';\nimport { withDevServer } from './dev-server.js';\n\n// ─── Retry / Backoff Constants ───────────────────────────────────────────────\n\n/** Max automatic re-spawn attempts after an unexpected non-zero exit. */\nconst MAX_RETRIES = 3;\n/** Initial backoff delay in ms. Doubles on each attempt, capped at MAX_BACKOFF_MS. */\nconst BACKOFF_INITIAL_MS = 5_000;\n/** Hard ceiling on backoff delay. */\nconst BACKOFF_MAX_MS = 60_000;\n\n/** Exponential backoff: 5s → 10s → 20s → (cap) 60s. */\nfunction backoffMs(attempt: number): number {\n return Math.min(BACKOFF_INITIAL_MS * Math.pow(2, attempt), BACKOFF_MAX_MS);\n}\n\n// ─── Main ────────────────────────────────────────────────────────────────────\n\nasync function main(): Promise<void> {\n const args = parseArgs(process.argv);\n\n if (args.help) {\n printHelp();\n process.exit(0);\n }\n\n const projectRoot = resolve(args.projectRoot);\n if (!existsSync(projectRoot)) {\n die(`Project root does not exist: ${projectRoot}`);\n }\n\n // Sanity check: is this a Stackwright project?\n if (!existsSync(join(projectRoot, 'package.json'))) {\n die('No package.json found. Run npx @stackwright-pro/launch-stackwright-pro first.');\n }\n\n log('Launching Pro Otter Raft...');\n\n // 1. Write/enrich init context\n writeInitContext(projectRoot);\n verbose('Init context written', args.verbose);\n\n // 1a. Write type schemas sink for foreman routing\n try {\n const stackwrightDir = join(projectRoot, '.stackwright');\n mkdirSync(stackwrightDir, { recursive: true });\n const schemaSummary = buildTypeSchemaSummary();\n writeFileSync(\n join(stackwrightDir, 'type-schemas.json'),\n JSON.stringify(schemaSummary, null, 2) + '\\n'\n );\n verbose('Type schemas sink written', args.verbose);\n } catch (err) {\n // Non-blocking — foreman can still operate without type-schemas.json\n console.warn('⚠️ Could not write type-schemas.json:', String(err));\n }\n\n // 1b. Write raft flags to init-context for foreman consumption\n {\n const stackwrightDir = join(projectRoot, '.stackwright');\n const initContextPath = join(stackwrightDir, 'init-context.json');\n\n // Merge flags into existing init-context (writeInitContext already created it)\n const initRaw = readFileSync(initContextPath, 'utf-8');\n const initCtx = JSON.parse(initRaw) as Record<string, unknown>;\n\n if (args.nonInteractive) initCtx['nonInteractive'] = true;\n if (args.devOnly) initCtx['devOnly'] = true;\n if (args.parallelPhases) initCtx['parallelPhases'] = true;\n\n // QA gate flags (swp-sc4x / Bead F)\n initCtx['skipQa'] = args.skipQa;\n initCtx['qaOnly'] = args.qaOnly;\n initCtx['devServer'] = {\n managed: true,\n baseUrl: 'http://localhost:3000',\n timeoutMs: args.qaDevServerTimeoutMs,\n };\n\n writeFileSync(initContextPath, JSON.stringify(initCtx, null, 2) + '\\n', 'utf-8');\n verbose('Raft flags written to init-context', args.verbose);\n }\n\n // 1c. Seed build context from --use-case file or generic fallback\n {\n const buildContextPath = join(projectRoot, '.stackwright', 'build-context.json');\n const MAX_USE_CASE_BYTES = 1 * 1024 * 1024; // 1MB\n const WARN_USE_CASE_BYTES = 200 * 1024; // 200KB\n\n if (args.useCasePath) {\n const resolvedPath = resolve(args.useCasePath);\n if (!existsSync(resolvedPath)) {\n die(`Use-case file not found: ${resolvedPath}`);\n }\n\n const content = readFileSync(resolvedPath, 'utf-8');\n if (content.length > MAX_USE_CASE_BYTES) {\n die(\n `Use-case file exceeds ${(MAX_USE_CASE_BYTES / 1024 / 1024).toFixed(0)}MB ` +\n `(got ${(content.length / 1024).toFixed(0)}KB). Trim the file or split into sections.`\n );\n }\n if (content.length > WARN_USE_CASE_BYTES) {\n console.warn(\n `⚠️ Large use-case file (${(content.length / 1024).toFixed(0)}KB) — ` +\n `specialist otters will receive the full context via build prompts.`\n );\n }\n\n const payload = {\n version: '1.0',\n savedAt: new Date().toISOString(),\n buildContext: content,\n };\n writeFileSync(buildContextPath, JSON.stringify(payload, null, 2) + '\\n', 'utf-8');\n log(`Build context seeded from ${args.useCasePath}`);\n } else if (args.nonInteractive && !existsSync(buildContextPath)) {\n // Generic fallback for --non-interactive without --use-case\n const payload = {\n version: '1.0',\n savedAt: new Date().toISOString(),\n buildContext:\n 'Build a sample application from the available API specs and data sources ' +\n 'with default settings. Use all available endpoints and standard page layouts.',\n };\n writeFileSync(buildContextPath, JSON.stringify(payload, null, 2) + '\\n', 'utf-8');\n log('Build context seeded with generic defaults (--non-interactive, no --use-case)');\n }\n }\n\n // 1d. Migrate legacy .code-puppy/ to .code_puppy/ if needed\n migrateWorkspaceDir(projectRoot);\n\n // 1e. Ensure workspace config — write .code_puppy/config.json (profile: strict-local)\n // Must come before ensureMcpConfig so the workspace dir exists.\n ensureWorkspaceConfig(projectRoot);\n\n // 1f. Register MCP server — do this BEFORE any die() calls so clean installs\n // get MCP config even if otter install is incomplete\n ensureMcpConfig(projectRoot);\n\n // 1g. Sync agent files from installed @stackwright-pro/otters — always reflects\n // the installed version without relying on postinstall hooks\n syncAgents(projectRoot, args.verbose);\n\n // 2. Verify otter integrity\n const otterDir = resolveOtterDir(projectRoot);\n if (!otterDir) {\n die(\n 'Could not find otter directory. Is @stackwright-pro/otters installed?\\n' +\n ' Run: pnpm add @stackwright-pro/otters'\n );\n }\n\n const result = verifyAllOtters(otterDir);\n if (result.failed.length > 0) {\n console.warn('⚠️ Otter integrity check warnings (non-blocking):');\n for (const f of result.failed) {\n console.warn(` ${f.filename}: ${f.error}`);\n }\n console.warn(' Note: SHA-256 pinning will be replaced by PKI-signed deployment manifests.');\n console.warn(\n ' See: https://github.com/Per-Aspera-LLC/stackwright-pro/issues (signing model issue)'\n );\n } else {\n log(`✅ All ${result.verified.length} otters verified`);\n }\n\n // 3. Print resume status\n printResumeStatus(projectRoot);\n\n // 4. Resolve raft-puppy / code-puppy and validate version (once, before retry loop)\n const { path: executable, fd: initialFd } = findCodePuppy();\n verbose(`Resolved raft-puppy / code-puppy: ${executable}`, args.verbose);\n try {\n closeSync(initialFd);\n } catch {\n /* best-effort */\n }\n\n // 4a. Pre-flight: validate binary version before spawning\n validateBinaryVersion(executable);\n\n // 5. Spawn raft-puppy / code-puppy — with automatic retry on unexpected exits.\n //\n // If code-puppy exits with a non-zero code (e.g. Cloudflare 400 mid-session,\n // transient API error) AND the pipeline state is resumable (status = 'questions'\n // or 'execution'), we wait with exponential backoff then re-spawn with a \"Resume\"\n // prompt. The Foreman reads pipeline-state.json and picks up where it left off.\n //\n // SIGINT/SIGTERM from the user are NOT retried — they abort cleanly.\n // ── QA-only short-circuit (swp-sc4x / Bead F) ────────────────────────────\n // --qa-only: skip the full otter pipeline and jump straight to the visual QA gate.\n // The withDevServer bracket is live; the QA otter invocation lands in swp-wde8.\n if (args.qaOnly) {\n log('--qa-only mode: skipping otter pipeline, spawning dev server for visual QA...');\n await withDevServer(projectRoot, { timeoutMs: args.qaDevServerTimeoutMs }, async (info) => {\n // TODO(swp-wde8): invoke QA otter directly via code-puppy.\n // For now, log info and exit — the bracket is wired and tested.\n log('--qa-only: dev server ready ✅');\n log(` pid: ${info.pid}`);\n log(` baseUrl: ${info.baseUrl}`);\n if (info.prismUrl) log(` prismUrl: ${info.prismUrl}`);\n log(` logPath: ${info.logPath}`);\n log('⏸ QA otter invocation not yet implemented (blocked on swp-wde8 + swp-kjkg)');\n });\n process.exit(0);\n }\n\n log('Spawning raft-puppy / code-puppy raft session...');\n\n let retryCount = 0;\n let userInterrupted = false;\n\n function spawnSession(isResume: boolean): void {\n // Re-open the binary on each spawn attempt so we get a fresh inode lock.\n // On Linux: spawn via /proc/self/fd/N — the fd was opened (and inode-checked) by\n // findCodePuppy(), so an attacker cannot swap the binary between validation and exec.\n //\n // Shebang-aware: raft-puppy is a Python script, not an ELF binary. The kernel's\n // shebang handler re-execs the interpreter (python) with the fd path as a string\n // argument. O_CLOEXEC (set by Node's openSync) would close the fd during that exec,\n // causing Python to get ENOENT on /proc/self/fd/N.\n //\n // Fix: pass the fd through the stdio array at index 3. dup2() clears O_CLOEXEC,\n // so fd 3 survives the interpreter exec. We then spawn via /proc/self/fd/3.\n //\n // On macOS: /proc does not exist — we fall back to the validated path.\n // Residual TOCTOU on macOS is documented and accepted for the current threat model.\n // See: CWE-367, GH#128, beads issue stackwright-pro-s1g.\n const { path: resolvedBinary, fd: binaryFd } = findCodePuppy();\n\n if (process.platform !== 'linux') {\n verbose(\n `macOS/other: spawning via path (residual TOCTOU — /proc unavailable). ` +\n `See GH#128 for threat model documentation.`,\n args.verbose\n );\n }\n\n // First run: \"Begin\". Retry runs: \"Resume\" so the Foreman reads pipeline-state.json.\n const prompt = isResume ? 'Resume' : 'Begin';\n const spawnArgs = [prompt, '--interactive', '--agent', 'stackwright-pro-foreman-otter'];\n\n const { spawnTarget, stdioConfig } = buildSpawnConfig(\n process.platform,\n resolvedBinary,\n binaryFd\n );\n\n verbose(\n `cmd: ${spawnTarget} ${spawnArgs.join(' ')}` +\n `${spawnTarget !== resolvedBinary ? ` (real: ${resolvedBinary})` : ''}` +\n `${isResume ? ` [retry ${retryCount}/${MAX_RETRIES}]` : ''}`,\n args.verbose\n );\n\n const child = spawn(spawnTarget, spawnArgs, {\n stdio: stdioConfig,\n cwd: projectRoot,\n env: buildCodePuppyEnv(\n buildLauncherEnvOverrides(projectRoot, { subagentResponses: args.subagentResponses }),\n args.passEnvKeys\n ),\n });\n\n // Close the parent's copy of the fd. On Linux the child inherited it via dup2\n // to fd 3 (without O_CLOEXEC) so the child's copy survives exec independently.\n try {\n closeSync(binaryFd);\n } catch {\n /* best-effort */\n }\n\n // Forward signals to child — let it clean up gracefully.\n // Track interrupts so we DON'T retry on user-initiated termination.\n const forward = (signal: NodeJS.Signals): void => {\n if (child.pid) child.kill(signal);\n };\n const onSigint = (): void => {\n userInterrupted = true;\n forward('SIGINT');\n };\n const onSigterm = (): void => {\n userInterrupted = true;\n forward('SIGTERM');\n };\n process.on('SIGINT', onSigint);\n process.on('SIGTERM', onSigterm);\n\n child.on('error', (err) => die(`Failed to spawn raft-puppy / code-puppy: ${err.message}`));\n child.on('close', (code, signal) => {\n process.off('SIGINT', onSigint);\n process.off('SIGTERM', onSigterm);\n\n // Signal exit (e.g. SIGINT from user) — re-raise and exit cleanly\n if (signal) {\n process.kill(process.pid, signal);\n return;\n }\n\n // Clean exit or user-interrupted — done\n if (code === 0 || userInterrupted) {\n process.exit(code ?? 0);\n return;\n }\n\n // Non-zero exit — check if retry is warranted\n if (retryCount < MAX_RETRIES && isPipelineResumable(projectRoot)) {\n retryCount++;\n const delay = backoffMs(retryCount - 1);\n log(\n `⚠️ Raft session exited unexpectedly (code ${code ?? '?'}). ` +\n `Pipeline is mid-run — retrying in ${delay / 1_000}s… ` +\n `(attempt ${retryCount}/${MAX_RETRIES})`\n );\n setTimeout(() => spawnSession(true), delay);\n } else {\n if (retryCount >= MAX_RETRIES) {\n log(`❌ Max retries (${MAX_RETRIES}) reached — giving up. Check logs above for errors.`);\n }\n process.exit(code ?? 1);\n }\n });\n }\n\n // ── QA gate status log (swp-sc4x / Bead F) ──────────────────────────────\n // The dev-server lifecycle module is wired (withDevServer in dev-server.ts).\n // Actual QA otter invocation is deferred to Bead D (swp-wde8) + Bead E (swp-kjkg).\n if (args.skipQa) {\n log('⚠️ Visual QA skipped by --skip-qa flag');\n } else {\n log(\n '⏸ Visual QA stub — dev server lifecycle is wired, QA otter invocation lands in swp-wde8/swp-kjkg'\n );\n }\n\n spawnSession(false);\n}\n\nmain().catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : String(err);\n die(`Unexpected fatal error: ${msg}`);\n});\n","/**\n * @stackwright-pro/raft — Pure utility functions\n *\n * Extracted from the CLI entry point so they're independently testable.\n * All side-effectful helpers (die, log, verbose) live here too —\n * the CLI entry point (`index.ts`) just wires them into `main()`.\n */\n\n// Raft is a process launcher — spawning a child process is its core job.\n// The single spawnSync call site (findCodePuppy, ~line 578) passes hardcoded\n// args ['raft-puppy'|'code-puppy'] to `which`; no user input ever reaches it.\n// All spawn call sites in this file are guarded by:\n// - findCodePuppy() — binary path resolution, fd-pinned TOCTOU mitigation\n// (CWE-367), mode-bit checks, and setuid/setgid rejection (see ~line 544)\n// - CODE_PUPPY_ENV_ALLOWLIST / buildCodePuppyEnv() — explicit env-var\n// allowlist that prevents credential leakage to the child (CWE-526, ~line 38)\n// This is a targeted line suppression, not a file-wide disable — any new\n// unguarded spawn/exec/fork added to this file will still trip the rule.\n// nosemgrep: javascript.lang.security.detect-child-process.detect-child-process\nimport { spawnSync } from 'child_process';\nimport {\n existsSync,\n lstatSync,\n mkdirSync,\n openSync,\n fstatSync,\n closeSync,\n readFileSync,\n readdirSync,\n realpathSync,\n renameSync,\n writeFileSync,\n rmSync,\n} from 'fs';\nimport { join, resolve } from 'path';\nimport { hostname, homedir, platform } from 'os';\n\n// ─── Code-Puppy Environment Allowlist ────────────────────────────────────────\n//\n// CWE-526: Sensitive environment variables must not be passed to child processes.\n// CVSS v4.0 ~3.0. The spawn call in index.ts MUST use buildCodePuppyEnv() — never\n// spread ...process.env directly.\n//\n// Only env vars in this allowlist (or matching the prefix list) are forwarded to\n// the code-puppy child process. AWS_*, GITHUB_TOKEN, DATABASE_URL, NPM_TOKEN, etc.\n// are silently stripped.\n\n/**\n * Exact env var names that code-puppy legitimately needs.\n */\nconst CODE_PUPPY_ENV_ALLOWLIST: ReadonlySet<string> = new Set([\n // Shell / execution environment\n 'PATH',\n 'HOME',\n 'USER',\n 'USERNAME',\n 'SHELL',\n 'TMPDIR',\n 'TMP',\n 'TEMP',\n // Terminal\n 'TERM',\n 'COLORTERM',\n 'COLUMNS',\n 'LINES',\n 'NO_COLOR',\n 'FORCE_COLOR',\n // Language / locale\n 'LANG',\n 'LC_ALL',\n 'LC_CTYPE',\n 'LC_MESSAGES',\n // Network proxy — needed for outbound LLM API calls behind corporate proxies\n 'HTTP_PROXY',\n 'HTTPS_PROXY',\n 'NO_PROXY',\n 'http_proxy',\n 'https_proxy',\n 'no_proxy',\n // LLM API keys — the only credentials code-puppy should have\n 'ANTHROPIC_API_KEY',\n 'ANTHROPIC_BASE_URL',\n 'OPENAI_API_KEY',\n // Node runtime\n 'NODE_ENV',\n // Stackwright-specific\n 'STACKWRIGHT_PROJECT_ROOT',\n 'STACKWRIGHT_SKIP_PREFLIGHT',\n 'STACKWRIGHT_CODE_PUPPY_PATH',\n // Telemetry vars (swp-bhw1): NDJSON path is force-injected by the launcher (see index.ts),\n // but listing here allows user override via shell env. DISABLED/DEBUG are opt-in toggles.\n 'STACKWRIGHT_TELEMETRY_NDJSON',\n 'STACKWRIGHT_TELEMETRY_NDJSON_SUBAGENT_RESPONSES',\n 'STACKWRIGHT_TELEMETRY_DISABLED',\n 'STACKWRIGHT_TELEMETRY_DEBUG',\n]);\n\n/**\n * Prefix-based allowlist — any env var starting with these prefixes is allowed.\n * Covers families like PYTHONPATH, PYTHONHOME, LC_NUMERIC, etc.\n */\nconst CODE_PUPPY_ENV_PREFIXES: readonly string[] = ['PYTHON', 'LC_'];\n\n// ─── Pre-flight Constants ────────────────────────────────────────────────────\n\n/** Minimum code-puppy / raft-puppy version required by this raft release.\n *\n * raft-puppy / code-puppy uses 0.0.x patch-only versioning on PyPI (e.g. 0.0.575).\n *\n * Bump this when a new raft-puppy feature becomes a hard dependency.\n * 0.0.575 — first version with the project_workspace builtin plugin\n * (profile-based scoping). Older versions silently degrade to additive\n * merge mode, which breaks raft's reproducibility guarantee.\n *\n * Dev builds (0.0.0 / 0.0.0-*) are handled separately by the isDevBuild bypass.\n */\nexport const MIN_SUPPORTED_CODE_PUPPY_VERSION = '0.0.575';\n\n// ─── CLI Argument Parsing ────────────────────────────────────────────────────\n\nexport interface ParsedArgs {\n projectRoot: string;\n verbose: boolean;\n help: boolean;\n /** Keys explicitly passed via --pass-env flags. */\n passEnvKeys: string[];\n /** Run without TUI prompts — use sane defaults for all questions. */\n nonInteractive: boolean;\n /** Skip real auth config — use mock auth with roles derived from use case. */\n devOnly: boolean;\n /** Path to a use-case file that seeds the build context. */\n useCasePath: string | null;\n /** Skip the visual QA gate entirely (writes skipQa: true to init-context). */\n skipQa: boolean;\n /** Skip the otter pipeline and jump straight to visual QA (writes qaOnly: true). */\n qaOnly: boolean;\n /** Dev server startup timeout in ms for the visual QA gate. Default: 60000. */\n qaDevServerTimeoutMs: number;\n /** Enable parallel specialist invocation within dependency waves (~40% wall-time reduction). Default: off (serial). */\n parallelPhases: boolean;\n /** Forward STACKWRIGHT_TELEMETRY_NDJSON_SUBAGENT_RESPONSES=1 to raft-puppy so it emits agent_response events carrying full sub-agent response text. Off by default — sub-agent responses can be very large and noisy. */\n subagentResponses: boolean;\n}\n\nexport function parseArgs(argv: string[]): ParsedArgs {\n const args: ParsedArgs = {\n projectRoot: process.cwd(),\n verbose: false,\n help: false,\n passEnvKeys: [],\n nonInteractive: false,\n devOnly: false,\n useCasePath: null,\n skipQa: false,\n qaOnly: false,\n qaDevServerTimeoutMs: 60_000,\n parallelPhases: false,\n subagentResponses: false,\n };\n\n const raw = argv.slice(2);\n for (let i = 0; i < raw.length; i++) {\n const token = raw[i];\n switch (token) {\n case '--project-root':\n args.projectRoot = raw[++i] ?? die('--project-root requires a path argument');\n break;\n case '--verbose':\n args.verbose = true;\n break;\n case '--help':\n case '-h':\n args.help = true;\n break;\n case '--pass-env':\n args.passEnvKeys.push(raw[++i] ?? die('--pass-env requires a KEY argument'));\n break;\n case '--non-interactive':\n args.nonInteractive = true;\n break;\n case '--dev-only':\n args.devOnly = true;\n break;\n case '--use-case':\n args.useCasePath = raw[++i] ?? die('--use-case requires a file path argument');\n break;\n case '--skip-qa':\n args.skipQa = true;\n break;\n case '--qa-only':\n args.qaOnly = true;\n break;\n case '--parallel-phases':\n args.parallelPhases = true;\n break;\n case '--subagent-responses':\n args.subagentResponses = true;\n break;\n default:\n // --qa-dev-server-timeout-ms=<n> form\n if (token.startsWith('--qa-dev-server-timeout-ms=')) {\n const val = parseInt(token.split('=')[1] ?? '', 10);\n if (isNaN(val) || val <= 0) {\n die('--qa-dev-server-timeout-ms requires a positive integer (ms)');\n }\n args.qaDevServerTimeoutMs = val;\n break;\n }\n die(`Unknown option: ${token}\\nRun with --help for usage.`);\n }\n }\n\n return args;\n}\n\nexport function printHelp(): void {\n console.log(\n `\n🦦 @stackwright-pro/raft — Spawn raft-puppy (or code-puppy) in foreman mode\n\nUsage: launch-raft [options]\n\nOptions:\n --project-root <path> Project root directory (default: cwd)\n --verbose Enable verbose logging\n --pass-env <KEY> Forward an additional env var to code-puppy (repeatable)\n --non-interactive Skip all TUI prompts, use sane defaults for questions\n --dev-only Mock auth only — roles derived from use case, no real providers\n --use-case <file> Seed \"what to build\" from a file (plain text or markdown)\n --skip-qa Skip the visual QA gate (useful for CI or fast iterations)\n --qa-only Skip the otter pipeline; spawn dev server and run QA directly\n --qa-dev-server-timeout-ms=<n> Dev server startup timeout in ms for QA gate (default: 60000)\n --parallel-phases Enable parallel specialist invocation within dependency waves (~40% wall-time reduction). Default: off (serial).\n --subagent-responses Capture full sub-agent response text in raft-puppy NDJSON telemetry (agent_response events). Verbose — can produce large logs. Default: off.\n --help, -h Show this help\n\nPrerequisites:\n pip install stackwright-puppy # provides raft-puppy + code-puppy alias\n Or: pip install code-puppy # fallback (MCP tools may not auto-start)\n`.trim()\n );\n}\n\n// ─── Utilities ───────────────────────────────────────────────────────────────\n\nexport function die(message: string): never {\n console.error(`❌ ${message}`);\n process.exit(1);\n}\n\nexport function log(message: string): void {\n console.log(`🦦 ${message}`);\n}\n\nexport function verbose(message: string, isVerbose: boolean): void {\n if (isVerbose) {\n console.log(` ${message}`);\n }\n}\n\n/**\n * Build a restricted environment for the code-puppy child process.\n *\n * Filters `process.env` to only keys in CODE_PUPPY_ENV_ALLOWLIST or matching\n * CODE_PUPPY_ENV_PREFIXES, then merges in `extraVars` (always wins) and any\n * keys explicitly requested via `--pass-env` (escape hatch).\n *\n * CWE-526 mitigation: credentials like AWS_SECRET_ACCESS_KEY, GITHUB_TOKEN,\n * DATABASE_URL, NPM_TOKEN, etc. are silently excluded.\n *\n * @param extraVars Vars to force-inject (e.g. STACKWRIGHT_PROJECT_ROOT).\n * @param passEnvKeys Additional keys from --pass-env flags.\n */\nexport function buildCodePuppyEnv(\n extraVars: Record<string, string> = {},\n passEnvKeys: string[] = []\n): NodeJS.ProcessEnv {\n const env: NodeJS.ProcessEnv = {};\n\n for (const [key, value] of Object.entries(process.env)) {\n if (value === undefined) continue;\n\n const allowed =\n CODE_PUPPY_ENV_ALLOWLIST.has(key) ||\n CODE_PUPPY_ENV_PREFIXES.some((prefix) => key.startsWith(prefix)) ||\n passEnvKeys.includes(key);\n\n if (allowed) {\n env[key] = value;\n }\n }\n\n // extraVars always win — they're injected by the raft, not from the parent env\n for (const [key, value] of Object.entries(extraVars)) {\n env[key] = value;\n }\n\n return env;\n}\n\n/**\n * Builds the force-injected env overrides the launcher always passes to code-puppy.\n *\n * Extracted for testability — the spawn block in index.ts should call this and\n * pass the result as `extraVars` to `buildCodePuppyEnv()`. Any key returned here\n * bypasses the allowlist (extraVars always win) AND is also listed in\n * `CODE_PUPPY_ENV_ALLOWLIST` so user shell overrides continue to work.\n *\n * @param projectRoot Absolute resolved project root (same as `args.projectRoot`).\n */\nexport interface LauncherEnvOptions {\n /** When true, set STACKWRIGHT_TELEMETRY_NDJSON_SUBAGENT_RESPONSES=1 to capture sub-agent .response text in NDJSON. Default off. */\n subagentResponses?: boolean;\n}\n\nexport function buildLauncherEnvOverrides(\n projectRoot: string,\n options: LauncherEnvOptions = {}\n): Record<string, string> {\n const overrides: Record<string, string> = {\n STACKWRIGHT_PROJECT_ROOT: projectRoot,\n // swp-bhw1: route raft-puppy telemetry to a SEPARATE file from Pro telemetry.\n // Two-file separation paves the way for the eventual message-bus architecture\n // (distributed deployments, restart, replay). User can override via shell env.\n STACKWRIGHT_TELEMETRY_NDJSON: join(projectRoot, '.stackwright', 'raft-puppy-events.ndjson'),\n };\n if (options.subagentResponses) {\n overrides['STACKWRIGHT_TELEMETRY_NDJSON_SUBAGENT_RESPONSES'] = '1';\n }\n return overrides;\n}\n\n// ─── Pipeline Lock ──────────────────────────────────────────────────────────\n\nconst LOCK_FILE = '.stackwright/.lock';\n\nexport function acquireLock(projectRoot: string): boolean {\n const lockPath = join(projectRoot, LOCK_FILE);\n\n // Symlink guard\n if (existsSync(lockPath) && lstatSync(lockPath).isSymbolicLink()) {\n die('.stackwright/.lock is a symlink — refusing to acquire lock. Check for tampering.');\n }\n\n const pid = process.pid;\n const host = hostname();\n const timestamp = new Date().toISOString();\n\n const lockContent = JSON.stringify({\n pid,\n hostname: host,\n acquiredAt: timestamp,\n version: '1.0',\n });\n\n mkdirSync(join(projectRoot, '.stackwright'), { recursive: true });\n\n try {\n // O_EXCL makes this atomic on POSIX — fails if file already exists\n writeFileSync(lockPath, lockContent, { flag: 'wx' });\n return true;\n } catch (err) {\n // EEXIST means lock already held by another process\n if (err instanceof Error && 'code' in err && (err as { code: string }).code === 'EEXIST') {\n // Lock exists — try to read it and check if the process is still alive\n try {\n const existing = JSON.parse(readFileSync(lockPath, 'utf8')) as Record<string, unknown>;\n const oldPid = existing['pid'] as number;\n\n // On Unix, check if process still exists via kill(0, pid)\n try {\n process.kill(oldPid, 0); // Signal 0 = check existence only\n // Process exists — lock is held by live process, cannot acquire\n return false;\n } catch {\n // Process is dead — stale lock, can take over\n writeFileSync(lockPath, lockContent, 'utf-8');\n return true;\n }\n } catch {\n // Can't read lock file or not JSON — treat as stale, take over\n writeFileSync(lockPath, lockContent, 'utf-8');\n return true;\n }\n }\n throw err;\n }\n}\n\nexport function releaseLock(projectRoot: string): void {\n const lockPath = join(projectRoot, LOCK_FILE);\n try {\n rmSync(lockPath);\n } catch {\n // Lock file may not exist — that's fine\n }\n}\n\n// ─── Workspace Directory Migration ────────────────────────────────────────────\n\n/**\n * Migrate the legacy `.code-puppy/` workspace directory to `.code_puppy/`.\n *\n * PR #290 renamed the workspace directory from `.code-puppy` (hyphen) to\n * `.code_puppy` (underscore) to match the canonical `PROJECT_WORKSPACE_DIR_NAME`\n * in code-puppy's `config.py`. Projects scaffolded before that change still\n * have the old directory; without migration, `get_project_workspace()` walks\n * past it and falls through to `$HOME/.code_puppy/` (global config), causing\n * `strict-local` profile and local MCP server loading to silently fail.\n *\n * Safe to call on every launch — no-ops when there's nothing to migrate.\n */\nexport function migrateWorkspaceDir(projectRoot: string): void {\n const oldDir = join(projectRoot, '.code-puppy');\n const newDir = join(projectRoot, '.code_puppy');\n\n const oldExists = existsSync(oldDir);\n const newExists = existsSync(newDir);\n\n if (!oldExists) return; // Nothing to migrate\n\n // Symlink guards — refuse to follow symlinks on either directory\n if (lstatSync(oldDir).isSymbolicLink()) {\n die('.code-puppy/ is a symlink — refusing to migrate. Check for tampering.');\n }\n if (newExists && lstatSync(newDir).isSymbolicLink()) {\n die('.code_puppy/ is a symlink — refusing to migrate. Check for tampering.');\n }\n\n if (!newExists) {\n // Clean migration: old exists, new doesn't → rename\n renameSync(oldDir, newDir);\n log('Migrated .code-puppy/ → .code_puppy/ (underscore convention)');\n } else {\n // Both exist — remove old to avoid confusion (underscore is canonical)\n rmSync(oldDir, { recursive: true });\n log('Removed stale .code-puppy/ (underscore version already exists)');\n }\n}\n\n// ─── Write Init Context ─────────────────────────────────────────────────────\n\nexport function writeInitContext(projectRoot: string): void {\n // Acquire pipeline lock — prevent concurrent launch-raft processes\n if (!acquireLock(projectRoot)) {\n let existingPid: string | number = 'unknown';\n try {\n const lockPath = join(projectRoot, LOCK_FILE);\n if (existsSync(lockPath)) {\n const lockData = JSON.parse(readFileSync(lockPath, 'utf8')) as Record<string, unknown>;\n const rawPid = lockData['pid'];\n existingPid = typeof rawPid === 'string' || typeof rawPid === 'number' ? rawPid : 'unknown';\n }\n } catch {\n // Couldn't read lock file\n }\n die(\n `Pipeline lock already held by PID ${existingPid}. Another launch-raft process is running.`\n );\n }\n\n const stackwrightDir = join(projectRoot, '.stackwright');\n const initContextPath = join(stackwrightDir, 'init-context.json');\n\n // Merge, don't clobber — respect anything the launcher already wrote\n const MAX_INIT_CONTEXT_BYTES = 1 * 1024 * 1024; // 1MB\n let existing: Record<string, unknown> = {};\n try {\n const raw = readFileSync(initContextPath, 'utf-8');\n\n // Size guard — reject oversized init-context.json before parsing\n if (raw.length > MAX_INIT_CONTEXT_BYTES) {\n die(\n `init-context.json exceeds ${MAX_INIT_CONTEXT_BYTES.toLocaleString()} bytes (got ${raw.length.toLocaleString()}). Refusing to parse. This may be an attack or a corrupted file.`\n );\n }\n\n existing = JSON.parse(raw) as Record<string, unknown>;\n } catch {\n // Fresh project or malformed file — start from scratch\n }\n\n // Enrich: only fill in gaps\n existing['projectRoot'] = projectRoot;\n\n if (!existing['projectName']) {\n try {\n const pkgRaw = readFileSync(join(projectRoot, 'package.json'), 'utf-8');\n const pkg = JSON.parse(pkgRaw) as Record<string, unknown>;\n if (typeof pkg['name'] === 'string') {\n existing['projectName'] = pkg['name'];\n }\n } catch {\n // No package.json name — that's fine\n }\n }\n\n if (!existing['specPath']) {\n try {\n const specsDir = join(projectRoot, 'specs');\n if (existsSync(specsDir)) {\n const files = readdirSync(specsDir);\n const first = files[0];\n if (first) {\n existing['specPath'] = join('specs', first);\n }\n }\n } catch {\n // No specs dir — that's fine\n }\n }\n\n if (!existing['theme']) {\n try {\n const ymlPath = join(projectRoot, 'stackwright.yml');\n const ymlContent = readFileSync(ymlPath, 'utf-8');\n const match = /theme:\\s*\\n\\s+id:\\s*(.+)/.exec(ymlContent);\n if (match?.[1]) {\n existing['theme'] = match[1].trim();\n }\n } catch {\n // No stackwright.yml — that's fine\n }\n }\n\n existing['generatedBy'] = 'launch-raft';\n existing['version'] = '1.0';\n\n mkdirSync(stackwrightDir, { recursive: true });\n\n // Symlink guard — refuse to follow symlinks (prevents symlink-based overwrites)\n if (existsSync(stackwrightDir) && lstatSync(stackwrightDir).isSymbolicLink()) {\n die('.stackwright is a symlink — refusing to write. Check for tampering.');\n }\n if (existsSync(initContextPath) && lstatSync(initContextPath).isSymbolicLink()) {\n die('init-context.json is a symlink — refusing to write. Check for tampering.');\n }\n\n writeFileSync(initContextPath, JSON.stringify(existing, null, 2), 'utf-8');\n}\n\n// ─── Shutdown handler — release lock on exit ──────────────────────────────────\n\nprocess.on('exit', () => {\n try {\n releaseLock(process.cwd());\n } catch {\n // Lock release is best-effort on shutdown — don't block exit\n }\n});\nprocess.on('SIGINT', () => {\n try {\n releaseLock(process.cwd());\n } catch {\n // Lock release is best-effort on signal — don't block exit\n }\n process.exit(0);\n});\nprocess.on('SIGTERM', () => {\n try {\n releaseLock(process.cwd());\n } catch {\n // Lock release is best-effort on signal — don't block exit\n }\n process.exit(0);\n});\n\n// ─── Trusted Binary Directories ────────────────────────────────────────────\n\n/**\n * Returns the ordered list of trusted installation directories to probe\n * for raft-puppy / code-puppy before falling back to $PATH (`which`).\n *\n * These represent well-known, user-controlled install locations that a\n * malicious npm postinstall script cannot shadow by merely prepending to\n * $PATH — providing a meaningful defence against CWE-426 (Untrusted\n * Search Path). raft-puppy / code-puppy are Python tools, so pip user\n * install (~/.local/bin), brew, and system package managers cover all\n * supported install methods.\n *\n * Exported for unit testing.\n */\nexport function getTrustedBinaryDirs(): string[] {\n return [\n join(homedir(), '.local', 'bin'), // pip user install, uvx, pipx (Linux/macOS)\n '/opt/homebrew/bin', // brew Apple Silicon\n '/usr/local/bin', // brew Intel, pip system install\n '/usr/bin', // system package managers (apt, dnf)\n ];\n}\n\n// ─── Find code-puppy Executable ─────────────────────────────────────────────\n\n/**\n * Result of findCodePuppy() — includes the resolved real path AND an open\n * O_RDONLY file descriptor to close the TOCTOU window between validation\n * and spawn. Keep the fd alive until spawn() returns (Linux: exec via\n * /proc/self/fd/N pins the inode; macOS: fd is best-effort, residual\n * TOCTOU documented).\n */\nexport interface FindCodePuppyResult {\n /** Resolved real path (symlinks followed) — use for display/logging only on Linux */\n path: string;\n /** Open O_RDONLY fd — caller MUST closeSync(fd) after spawn() returns */\n fd: number;\n}\n\nexport function findCodePuppy(trustedDirsOverride?: string[]): FindCodePuppyResult {\n let candidate: string | null = null;\n\n // 1. Explicit env var override — highest priority\n const envPath = process.env['STACKWRIGHT_CODE_PUPPY_PATH'];\n if (envPath) {\n candidate = envPath;\n }\n\n // 2. Trusted installation paths — probe before falling back to $PATH.\n // Prevents CWE-426: a malicious npm postinstall script cannot shadow\n // these paths by merely prepending to $PATH.\n if (!candidate) {\n const trustedDirs = trustedDirsOverride ?? getTrustedBinaryDirs();\n outer: for (const bin of ['raft-puppy', 'code-puppy']) {\n for (const dir of trustedDirs) {\n const p = join(dir, bin);\n if (existsSync(p)) {\n candidate = p;\n break outer;\n }\n }\n }\n }\n\n // 3. Prefer raft-puppy (stackwright-puppy fork) over vanilla code-puppy.\n // raft-puppy ships the MCP auto-enable fix and local .code-puppy.json\n // loading — both required for the raft to work on a clean install.\n // Falls back to code-puppy so existing installs don't break.\n if (!candidate) {\n for (const bin of ['raft-puppy', 'code-puppy']) {\n // spawnSync avoids spawning a shell — args passed directly to execvp,\n // no shell interpolation, no injection surface (CWE-78 / detect-child-process).\n const whichResult = spawnSync('which', [bin], { encoding: 'utf-8' });\n if (whichResult.status === 0 && whichResult.stdout) {\n candidate = whichResult.stdout.trim();\n if (candidate) break;\n }\n }\n }\n\n if (!candidate) {\n die(\n 'raft-puppy (or code-puppy) not found.\\n' +\n '\\n' +\n 'Install the Stackwright-patched build (recommended):\\n' +\n ' pip install stackwright-puppy\\n' +\n '\\n' +\n 'Or install vanilla code-puppy (MCP tools may not auto-start):\\n' +\n ' pip install code-puppy\\n' +\n '\\n' +\n 'Or set STACKWRIGHT_CODE_PUPPY_PATH to the binary path.'\n );\n }\n\n // Resolve to absolute path — prevents bare-name or relative-path shenanigans\n const resolved = resolve(candidate);\n\n if (!existsSync(resolved)) {\n die(`raft-puppy/code-puppy not found at resolved path: ${resolved}`);\n }\n\n // Follow the full symlink chain to the real binary.\n // pip/pipx/uvx installs create a wrapper symlink in ~/.local/bin/ that points\n // to the actual binary inside a virtualenv — this is expected and safe.\n let realBinary: string;\n try {\n realBinary = realpathSync(resolved);\n } catch {\n die(\n `raft-puppy at ${resolved} has a broken symlink — cannot resolve to a real path. ` +\n `Try reinstalling stackwright-puppy or set STACKWRIGHT_CODE_PUPPY_PATH to the real binary.`\n );\n }\n\n if (!existsSync(realBinary)) {\n die(`raft-puppy symlink at ${resolved} points to a missing file: ${realBinary}`);\n }\n\n // Open an fd BEFORE permission checks — fstatSync(fd) operates on the already-open\n // inode, so the checks and the spawn target cannot diverge. CWE-367 mitigation.\n let fd: number;\n try {\n fd = openSync(realBinary, 'r');\n } catch (err) {\n die(\n `Cannot open raft-puppy at ${realBinary} for validation: ${String(err)}. ` +\n `Try reinstalling stackwright-puppy or set STACKWRIGHT_CODE_PUPPY_PATH.`\n );\n }\n\n // Use fstatSync(fd) — operates on the pinned inode, not the filename.\n const stat = fstatSync(fd!);\n\n // Refuse world-writable or group-writable binaries\n if (stat.mode & 0o022) {\n closeSync(fd!);\n die(\n `raft-puppy at ${realBinary} is group- or world-writable (mode: ${(stat.mode & 0o777).toString(8)}) — refusing to exec.`\n );\n }\n\n // Refuse setuid/setgid binaries\n if (stat.mode & 0o6000) {\n closeSync(fd!);\n die(`raft-puppy at ${realBinary} has setuid/setgid bits — refusing to exec.`);\n }\n\n return { path: realBinary, fd: fd! };\n}\n\n// ─── Pre-flight Environment Validation ──────────────────────────────────────\n\nfunction parseSemver(version: string): [number, number, number] {\n const parts = version.split('-')[0]!.split('.').map(Number);\n return [parts[0] ?? 0, parts[1] ?? 0, parts[2] ?? 0];\n}\n\nfunction semverGte(a: string, b: string): boolean {\n const [aMaj, aMin, aPatch] = parseSemver(a);\n const [bMaj, bMin, bPatch] = parseSemver(b);\n if (aMaj !== bMaj) return aMaj > bMaj;\n if (aMin !== bMin) return aMin > bMin;\n return aPatch >= bPatch;\n}\n\n/**\n * Validates the runtime environment before spawning code-puppy.\n * Calls die() (process.exit(1)) with a human-readable remediation message\n * on failure — never reaches the spawn if a check fails.\n *\n * Auth checking is intentionally NOT done here — code-puppy/raft-puppy owns\n * the auth layer and will surface a clear error at spawn time for any\n * missing/invalid credentials. This keeps the raft agnostic to model\n * providers (Anthropic API key, claude auth login OAuth, AWS Bedrock,\n * Google Vertex, Ollama, air-gapped inference, etc.).\n *\n * Checks:\n * 1. STACKWRIGHT_SKIP_PREFLIGHT — if 'true', skip all checks and return\n * 2. code-puppy --version reports >= MIN_SUPPORTED_CODE_PUPPY_VERSION\n */\nexport function validateBinaryVersion(binaryPath: string): void {\n // Escape hatch for air-gapped / custom model-provider deployments.\n // Set STACKWRIGHT_SKIP_PREFLIGHT=true to bypass pre-flight checks.\n if (process.env['STACKWRIGHT_SKIP_PREFLIGHT'] === 'true') {\n log('Pre-flight checks skipped via STACKWRIGHT_SKIP_PREFLIGHT');\n return;\n }\n\n // 1. Version check — run binary with --version, parse semver, compare\n // binaryPath is validated by findCodePuppy(): realpathSync'd, symlink-checked,\n // mode/setuid-checked, and confirmed to exist. spawnSync passes args directly\n // to execvp — no shell spawned, no quoting needed (CWE-78).\n // binaryPath is never user-supplied: it is the output of findCodePuppy(), which\n // resolves it through realpathSync and validates mode bits and setuid before\n // returning it. This is a targeted suppression — not a file-wide disable.\n const versionStartTime = Date.now();\n // nosemgrep: javascript.lang.security.detect-child-process.detect-child-process\n const versionResult = spawnSync(binaryPath, ['--version'], {\n encoding: 'utf-8',\n // 15s: uv-installed Python tools cold-start (venv activation + interpreter\n // startup) can take 5-10s on first invocation; subsequent runs are ms.\n timeout: 15000,\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n const versionElapsed = Date.now() - versionStartTime;\n\n if (versionResult.error !== undefined || versionResult.status !== 0) {\n const errorDetail = versionResult.error\n ? `${(versionResult.error as NodeJS.ErrnoException).code ?? 'unknown'}: ${versionResult.error.message}`\n : `exit status ${String(versionResult.status)}`;\n const stdoutSnip = (versionResult.stdout ?? '').trim() || '<empty>';\n const stderrSnip = (versionResult.stderr ?? '').trim() || '<empty>';\n die(\n `Could not determine raft-puppy / code-puppy version.\\n` +\n ` Binary: ${binaryPath}\\n` +\n ` Failure: ${errorDetail}\\n` +\n ` Elapsed: ${String(versionElapsed)}ms (timeout: 15000ms)\\n` +\n ` Stdout: ${stdoutSnip}\\n` +\n ` Stderr: ${stderrSnip}\\n` +\n ` Required: ${MIN_SUPPORTED_CODE_PUPPY_VERSION}\\n` +\n `\\n` +\n ` Run: pip install --upgrade stackwright-puppy\\n` +\n ` Or: pip install --upgrade code-puppy\\n` +\n `\\n` +\n ` If the binary works manually but fails here, set\\n` +\n ` STACKWRIGHT_SKIP_PREFLIGHT=true to bypass this check.`\n );\n }\n\n // Some CLIs print --version to stderr instead of stdout. Accept either;\n // prefer stdout, fall back to stderr if stdout is empty.\n const versionStdout = versionResult.stdout.trim();\n const versionStderr = versionResult.stderr.trim();\n const versionOutput = versionStdout || versionStderr;\n\n const match = /(\\d+\\.\\d+\\.\\d+(?:-[^\\s]+)?)/.exec(versionOutput);\n if (!match || !match[1]) {\n die(\n `Could not parse version from raft-puppy / code-puppy output.\\n` +\n ` Binary: ${binaryPath}\\n` +\n ` Stdout: ${JSON.stringify(versionStdout) || '<empty>'}\\n` +\n ` Stderr: ${JSON.stringify(versionStderr) || '<empty>'}\\n` +\n ` Required: ${MIN_SUPPORTED_CODE_PUPPY_VERSION}\\n` +\n `\\n` +\n ` Run: pip install --upgrade stackwright-puppy\\n` +\n ` Or: pip install --upgrade code-puppy`\n );\n }\n\n const installedVersion = match[1];\n\n // Dev builds self-identify as 0.0.0-dev (or bare 0.0.0) — treat as a\n // local-source build and warn instead of hard-failing the version gate.\n // Any real published release should use a proper semver (e.g. 0.1.0-alpha.1).\n // NOTE: 0.0.0 bare is also treated as dev — it's never a valid published version.\n const isDevBuild = installedVersion === '0.0.0' || /^0\\.0\\.0-.+$/.test(installedVersion);\n if (isDevBuild) {\n console.warn(\n `⚠️ Dev build detected (${installedVersion}) — skipping minimum version check.\\n` +\n ` Minimum required for production: ${MIN_SUPPORTED_CODE_PUPPY_VERSION}\\n` +\n ` To suppress this warning in CI: set STACKWRIGHT_SKIP_PREFLIGHT=true`\n );\n return;\n }\n\n if (!semverGte(installedVersion, MIN_SUPPORTED_CODE_PUPPY_VERSION)) {\n die(\n `raft-puppy / code-puppy ${installedVersion} is below the minimum required version.\\n` +\n ` Installed: ${installedVersion}\\n` +\n ` Minimum required: ${MIN_SUPPORTED_CODE_PUPPY_VERSION}\\n` +\n `\\n` +\n ` Run: pip install --upgrade stackwright-puppy\\n` +\n ` Or: pip install --upgrade code-puppy`\n );\n }\n}\n\n// ─── Ensure MCP Config ─────────────────────────────────────────────────────\n\nexport function ensureMcpConfig(projectRoot: string): void {\n const workspaceDir = join(projectRoot, '.code_puppy');\n const configPath = join(workspaceDir, 'mcp_servers.json');\n\n // Symlink guard — consistent with other security patterns\n if (existsSync(configPath) && lstatSync(configPath).isSymbolicLink()) {\n die('.code_puppy/mcp_servers.json is a symlink — refusing to write. Check for tampering.');\n }\n\n mkdirSync(workspaceDir, { recursive: true });\n\n // Read existing → merge, don't clobber other registered servers\n let existing: { mcp_servers?: Record<string, unknown> } = {};\n if (existsSync(configPath)) {\n try {\n const raw = readFileSync(configPath, 'utf-8');\n existing = JSON.parse(raw) as { mcp_servers?: Record<string, unknown> };\n } catch {\n // Malformed file — start fresh\n }\n }\n\n // Resolve the pnpm-generated shim directly instead of going through `pnpm exec`.\n //\n // Why: the MCP Python SDK (mcp.client.stdio.get_default_environment) only inherits\n // 6 env vars by default — HOME, LOGNAME, PATH, SHELL, TERM, USER. Pnpm needs\n // PNPM_HOME, NODE_PATH, XDG_DATA_HOME, etc. to locate its store; without them it\n // exits in ~3-9ms without ever printing the \"running on stdio\" banner.\n //\n // The .bin/ shim is self-sufficient: it exports its own NODE_PATH and execs `node`\n // directly — only PATH is needed (already in the default 6). This also makes the\n // config package-manager agnostic: pnpm/npm/yarn all produce the same shim format.\n const shimName = platform() === 'win32' ? 'stackwright-pro-mcp.cmd' : 'stackwright-pro-mcp';\n const shimPath = join(projectRoot, 'node_modules', '.bin', shimName);\n\n if (!existsSync(shimPath)) {\n die(\n `MCP server shim not found: ${shimPath}\\n` +\n ` Run \\`pnpm install\\` (or your package manager equivalent) before \\`raft\\`.`\n );\n }\n\n const serverConfig = {\n type: 'stdio',\n command: shimPath,\n args: [] as string[],\n enabled: true,\n cwd: projectRoot,\n };\n\n const merged = {\n ...existing,\n mcp_servers: {\n ...(existing.mcp_servers ?? {}),\n 'stackwright-pro-mcp': serverConfig,\n },\n };\n\n // Atomic write via .tmp rename swap\n const tmpPath = `${configPath}.tmp`;\n writeFileSync(tmpPath, JSON.stringify(merged, null, 2), 'utf-8');\n renameSync(tmpPath, configPath);\n\n log('MCP server registered → .code_puppy/mcp_servers.json');\n}\n\n// ─── Ensure Workspace Config ─────────────────────────────────────────────────\n\n/**\n * Write .code_puppy/config.json with profile: \"strict-local\".\n *\n * code-puppy ≥ 0.0.575 uses a profile-based workspace scoping system\n * (the `project_workspace` builtin plugin). `strict-local` scopes ALL\n * six extension surfaces — agents, skills, plugins, mcp, hooks,\n * file_permissions — to the project directory only. This ensures raft\n * runs are fully isolated from the user's global ~/.code_puppy/ config,\n * which is raft's core reproducibility guarantee.\n *\n * Merges with any existing config.json without clobbering other fields.\n * Uses atomic write (.tmp rename swap) and symlink guards throughout.\n */\nexport function ensureWorkspaceConfig(projectRoot: string): void {\n const workspaceDir = join(projectRoot, '.code_puppy');\n const configPath = join(workspaceDir, 'config.json');\n\n // Symlink guard on the .code_puppy/ dir itself\n if (existsSync(workspaceDir) && lstatSync(workspaceDir).isSymbolicLink()) {\n die('.code_puppy/ is a symlink — refusing to write. Check for tampering.');\n }\n\n mkdirSync(workspaceDir, { recursive: true });\n\n // Symlink guard on config.json\n if (existsSync(configPath) && lstatSync(configPath).isSymbolicLink()) {\n die('.code_puppy/config.json is a symlink — refusing to write. Check for tampering.');\n }\n\n // Merge — don't clobber existing fields\n let existing: Record<string, unknown> = {};\n if (existsSync(configPath)) {\n try {\n existing = JSON.parse(readFileSync(configPath, 'utf-8')) as Record<string, unknown>;\n } catch {\n // Malformed — start fresh\n }\n }\n\n const merged = {\n ...existing,\n $schema: 'https://stackwright.dev/schemas/workspace-config/v2.json',\n profile: 'strict-local',\n };\n\n // Atomic write via .tmp rename swap\n const tmpPath = `${configPath}.tmp`;\n writeFileSync(tmpPath, JSON.stringify(merged, null, 2), 'utf-8');\n renameSync(tmpPath, configPath);\n\n log('Workspace config written → .code_puppy/config.json (profile: strict-local)');\n}\n\n// ─── Print Resume Status ────────────────────────────────────────────────────\n\nexport function printResumeStatus(projectRoot: string): void {\n const pipelineStatePath = join(projectRoot, '.stackwright', 'pipeline-state.json');\n\n try {\n const raw = readFileSync(pipelineStatePath, 'utf-8');\n const state = JSON.parse(raw) as Record<string, unknown>;\n const status = state['status'];\n const phases = state['phases'];\n\n if (typeof phases !== 'object' || phases === null || Array.isArray(phases)) {\n return;\n }\n\n const phaseEntries = Object.values(phases as Record<string, Record<string, unknown>>);\n const totalPhases = phaseEntries.length || 8;\n\n switch (status) {\n case 'setup':\n log('📍 Starting fresh');\n break;\n case 'questions': {\n const answered = phaseEntries.filter((p) => p['answered'] === true).length;\n log(`📍 Resuming: questions phase (${answered}/${totalPhases} phases answered)`);\n break;\n }\n case 'execution': {\n const executed = phaseEntries.filter((p) => p['executed'] === true).length;\n log(`📍 Resuming: execution phase (${executed}/${totalPhases} phases complete)`);\n break;\n }\n case 'done':\n log('📍 Pipeline complete — re-entering to review');\n break;\n default:\n break;\n }\n } catch {\n // No pipeline state yet — fresh project\n }\n}\n\n// ─── Pipeline Resumability Check ─────────────────────────────────────────────\n\n/**\n * Returns true if the pipeline is in a state that can be automatically resumed.\n *\n * Used by the retry/backoff logic in index.ts to decide whether a non-zero\n * code-puppy exit warrants a re-spawn. 'questions' and 'execution' statuses\n * mean the Foreman was mid-run; 'done' means it finished cleanly; no file\n * means it never started (no point retrying a blank slate).\n */\nexport function isPipelineResumable(projectRoot: string): boolean {\n const pipelineStatePath = join(projectRoot, '.stackwright', 'pipeline-state.json');\n try {\n const raw = readFileSync(pipelineStatePath, 'utf-8');\n const state = JSON.parse(raw) as Record<string, unknown>;\n const status = state['status'];\n return status === 'questions' || status === 'execution';\n } catch {\n return false;\n }\n}\n\n// ─── Resolve Otter Directory ────────────────────────────────────────────────\n\nexport function resolveOtterDir(projectRoot: string): string | null {\n const candidates = [\n join(projectRoot, 'node_modules', '@stackwright-pro', 'otters', 'src'),\n join(projectRoot, 'packages', 'otters', 'src'),\n ];\n\n for (const candidate of candidates) {\n if (existsSync(candidate)) {\n return candidate;\n }\n }\n\n return null;\n}\n\n// ─── Sync Agents ────────────────────────────────────────────────────────────\n\n/**\n * Copy all *-otter.json files from the resolved otters package to\n * .code_puppy/agents/ on every raft startup.\n *\n * This mirrors the pattern used by ensureMcpConfig() for mcp_servers.json.\n * Relying solely on the postinstall hook in @stackwright-pro/otters is fragile:\n * pnpm/npm don't always re-run scripts on package updates, and postinstall\n * order is non-deterministic when multiple packages run scripts. Active sync\n * on every raft launch guarantees agents are always the installed version.\n */\nexport function syncAgents(projectRoot: string, isVerbose: boolean = false): void {\n const agentsDir = join(projectRoot, '.code_puppy', 'agents');\n\n const otterDir = resolveOtterDir(projectRoot);\n if (!otterDir) {\n verbose('No otters directory found — skipping agent sync', isVerbose);\n return;\n }\n\n // Symlink guard on agents dir\n if (existsSync(agentsDir) && lstatSync(agentsDir).isSymbolicLink()) {\n die('.code_puppy/agents is a symlink — refusing to write. Check for tampering.');\n }\n\n mkdirSync(agentsDir, { recursive: true });\n\n let synced = 0;\n let skipped = 0;\n\n try {\n const files = readdirSync(otterDir);\n for (const file of files) {\n if (!file.endsWith('-otter.json')) continue;\n\n const src = join(otterDir, file);\n const dest = join(agentsDir, file);\n\n // Symlink guard on individual file\n if (existsSync(dest) && lstatSync(dest).isSymbolicLink()) {\n verbose(`Skipping ${file} — dest is a symlink`, isVerbose);\n skipped++;\n continue;\n }\n\n // Atomic write: copy to .tmp then rename\n const tmp = `${dest}.tmp`;\n const content = readFileSync(src);\n writeFileSync(tmp, content);\n renameSync(tmp, dest);\n verbose(`Synced: ${file}`, isVerbose);\n synced++;\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n // Non-fatal: log and continue — a stale agent is better than a crash\n console.warn(`⚠️ Agent sync partial failure: ${msg}`);\n return;\n }\n\n if (synced > 0) {\n log(\n `Agents synced → .code_puppy/agents/ (${synced} otters${skipped > 0 ? `, ${skipped} skipped` : ''})`\n );\n }\n}\n\n// ─── Spawn Config Builder ───────────────────────────────────────────────────\n\n/**\n * Build the spawn target and stdio config for code-puppy/raft-puppy.\n *\n * On Linux: pass the validated binary fd through stdio at index 3 (dup2 clears\n * O_CLOEXEC), then exec via /proc/self/fd/3. This works for both ELF and\n * shebang (Python) scripts — the fd survives the interpreter's execve.\n *\n * On macOS/other: /proc does not exist — fall back to the validated path.\n * Residual TOCTOU on macOS is documented and accepted.\n *\n * Extracted from main() for testability.\n *\n * @param platform - process.platform value\n * @param executable - resolved binary path (from findCodePuppy)\n * @param binaryFd - open fd to the binary (from findCodePuppy)\n */\nexport function buildSpawnConfig(\n platform: string,\n executable: string,\n binaryFd: number\n): { spawnTarget: string; stdioConfig: import('child_process').StdioOptions } {\n const PINNED_CHILD_FD = 3;\n\n if (platform === 'linux') {\n return {\n spawnTarget: `/proc/self/fd/${PINNED_CHILD_FD}`,\n stdioConfig: ['inherit', 'inherit', 'inherit', binaryFd],\n };\n }\n\n return {\n spawnTarget: executable,\n stdioConfig: 'inherit',\n };\n}\n","/**\n * @stackwright-pro/raft — Dev-server lifecycle bracket\n *\n * Spawns `pnpm dev` in a project, probes until HTTP is ready, invokes a\n * callback body, then kills the server cleanly (SIGTERM → 5s grace → SIGKILL).\n *\n * swp-sc4x: Bead F — lifecycle module. QA otter invocation is Bead D (swp-wde8).\n */\n\nimport { spawn } from 'child_process';\nimport type { ChildProcess } from 'child_process';\nimport {\n existsSync,\n mkdirSync,\n openSync,\n closeSync,\n writeFileSync,\n unlinkSync,\n readFileSync,\n} from 'fs';\nimport { join } from 'path';\n\n// ─── Public Interfaces ───────────────────────────────────────────────────────\n\nexport interface DevServerInfo {\n pid: number;\n baseUrl: string;\n prismUrl?: string;\n startedAt: number;\n logPath: string;\n}\n\nexport interface DevServerOptions {\n /** Total budget to wait for the server to be ready. Default: 60000ms */\n timeoutMs?: number;\n /** Base URL to probe. Default: http://localhost:3000 */\n baseUrl?: string;\n /** Explicit prism URL override (skips auto-detection). Default: http://localhost:4010 */\n prismUrl?: string;\n /** Detect prism from package.json scripts.dev. Default: true */\n detectPrism?: boolean;\n}\n\nexport interface PrismConfig {\n hasPrism: boolean;\n prismUrl: string;\n}\n\nexport class DevServerStartupError extends Error {\n constructor(\n message: string,\n public readonly logTail: string[],\n public readonly elapsedMs: number\n ) {\n super(message);\n this.name = 'DevServerStartupError';\n }\n}\n\n// ─── Pure Utilities ──────────────────────────────────────────────────────────\n\n/**\n * Inspect a project's `package.json` dev script to determine if Prism is in use.\n * Pure function — exported for independent testability.\n */\nexport function detectPrismConfig(\n projectRoot: string,\n defaultPrismUrl = 'http://localhost:4010'\n): PrismConfig {\n try {\n const pkgPath = join(projectRoot, 'package.json');\n const raw = readFileSync(pkgPath, 'utf-8');\n const pkg = JSON.parse(raw) as Record<string, unknown>;\n const scripts = pkg['scripts'] as Record<string, unknown> | undefined;\n const devScript = typeof scripts?.['dev'] === 'string' ? scripts['dev'] : '';\n const hasConcurrently = devScript.includes('concurrently');\n const hasPrism = /prism/i.test(devScript);\n return { hasPrism: hasConcurrently && hasPrism, prismUrl: defaultPrismUrl };\n } catch {\n return { hasPrism: false, prismUrl: defaultPrismUrl };\n }\n}\n\n/** Read the last N non-empty lines from a file (best-effort). */\nfunction tailLog(logPath: string, maxLines: number): string[] {\n try {\n const content = readFileSync(logPath, 'utf-8');\n const lines = content.split('\\n').filter((l) => l.length > 0);\n return lines.slice(-maxLines);\n } catch {\n return [];\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n// ─── HTTP Probing ────────────────────────────────────────────────────────────\n\n/**\n * Attempt a single fetch with per-request timeout.\n * Returns true on 2xx, false on any error/timeout. Never throws.\n */\nasync function singleFetch(url: string, timeoutMs: number): Promise<boolean> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n try {\n const res = await globalThis.fetch(url, { signal: controller.signal });\n clearTimeout(timer);\n return res.status >= 200 && res.status < 300;\n } catch {\n clearTimeout(timer);\n return false;\n }\n}\n\n/**\n * Poll a set of URLs every `pollIntervalMs` until ALL respond 2xx or `timeoutMs` elapses.\n * Returns true if all became ready within budget, false on timeout.\n */\nasync function pollUntilAllReady(\n urls: string[],\n timeoutMs: number,\n pollIntervalMs: number,\n perRequestTimeoutMs: number\n): Promise<boolean> {\n const deadline = Date.now() + timeoutMs;\n\n while (Date.now() < deadline) {\n const remaining = deadline - Date.now();\n const perReq = Math.min(perRequestTimeoutMs, remaining);\n const results = await Promise.all(urls.map((url) => singleFetch(url, perReq)));\n if (results.every(Boolean)) return true;\n\n const sleepMs = Math.min(pollIntervalMs, deadline - Date.now());\n if (sleepMs <= 0) break;\n await sleep(sleepMs);\n }\n\n return false;\n}\n\n/**\n * Probe a URL until it responds 2xx or times out.\n * Exported for tests and for callers that need readiness checks without managing lifecycle.\n */\nexport async function probeUrl(\n url: string,\n options: { timeoutMs: number; pollIntervalMs?: number; perRequestTimeoutMs?: number }\n): Promise<boolean> {\n const { timeoutMs, pollIntervalMs = 2000, perRequestTimeoutMs = 1000 } = options;\n return pollUntilAllReady([url], timeoutMs, pollIntervalMs, perRequestTimeoutMs);\n}\n\n// ─── Process Cleanup ─────────────────────────────────────────────────────────\n\nasync function killChildProcess(child: ChildProcess, pid: number): Promise<void> {\n return new Promise((resolve) => {\n // Race: exit event vs 5s SIGKILL escalation\n let settled = false;\n const settle = (): void => {\n if (!settled) {\n settled = true;\n clearTimeout(escalateTimer);\n resolve();\n }\n };\n\n child.once('exit', settle);\n\n const escalateTimer = setTimeout(() => {\n try {\n process.kill(pid, 'SIGKILL');\n } catch {\n // Process already gone\n }\n settle();\n }, 5_000);\n\n try {\n process.kill(pid, 'SIGTERM');\n } catch {\n // Process already gone — still resolve\n settle();\n }\n });\n}\n\n// ─── Dev Server Lifecycle ────────────────────────────────────────────────────\n\n/**\n * Spawn `pnpm dev` in the project, wait for it to be ready, invoke `body`,\n * then ALWAYS kill the server cleanly — even if `body` throws.\n *\n * Writes `.stackwright/dev-server-ready.json` on success.\n * Throws `DevServerStartupError` on timeout/failure with log tail.\n *\n * Critical (swp-e4y6 family): uses spawn (not exec) for pid, no zombie leaks.\n */\nexport async function withDevServer<T>(\n projectRoot: string,\n options: DevServerOptions,\n body: (info: DevServerInfo) => Promise<T>\n): Promise<T> {\n const {\n timeoutMs = 60_000,\n baseUrl: baseUrlOpt,\n prismUrl: prismUrlOpt,\n detectPrism: shouldDetectPrism = true,\n } = options;\n\n const baseUrl = baseUrlOpt ?? 'http://localhost:3000';\n\n // Prism detection: explicit override > auto-detect > disabled\n let prismUrl: string | undefined;\n if (prismUrlOpt !== undefined) {\n prismUrl = prismUrlOpt;\n } else if (shouldDetectPrism) {\n const cfg = detectPrismConfig(projectRoot);\n prismUrl = cfg.hasPrism ? cfg.prismUrl : undefined;\n }\n\n // Set up log file — truncate per run, no append\n const stackwrightDir = join(projectRoot, '.stackwright');\n mkdirSync(stackwrightDir, { recursive: true });\n const logPath = join(stackwrightDir, 'dev-server.log');\n const logFd = openSync(logPath, 'w');\n\n const readyFilePath = join(stackwrightDir, 'dev-server-ready.json');\n const startedAt = Date.now();\n\n let child: ChildProcess | undefined;\n let pid: number | undefined;\n let spawnErr: Error | undefined;\n\n try {\n // nosemgrep: javascript.lang.security.detect-child-process\n child = spawn('pnpm', ['dev'], {\n cwd: projectRoot,\n detached: false,\n stdio: ['ignore', logFd, logFd],\n });\n\n pid = child.pid;\n child.on('error', (err) => {\n spawnErr = err;\n });\n\n if (pid === undefined) {\n throw new DevServerStartupError(\n 'pnpm dev failed to spawn (no PID)',\n tailLog(logPath, 50),\n Date.now() - startedAt\n );\n }\n\n // Probe all required URLs\n const urlsToProbe = [baseUrl, ...(prismUrl ? [prismUrl] : [])];\n const ready = await pollUntilAllReady(urlsToProbe, timeoutMs, 2_000, 1_000);\n\n if (!ready) {\n const elapsedMs = Date.now() - startedAt;\n const tail = tailLog(logPath, 50);\n const spawnErrMsg = spawnErr ? ` Spawn error: ${spawnErr.message}.` : '';\n throw new DevServerStartupError(\n `Dev server did not become ready within ${timeoutMs}ms.${spawnErrMsg}`,\n tail,\n elapsedMs\n );\n }\n\n const info: DevServerInfo = {\n pid,\n baseUrl,\n ...(prismUrl ? { prismUrl } : {}),\n startedAt,\n logPath,\n };\n\n // Write ready file — Bead D / E consumers can read this for reference\n writeFileSync(\n readyFilePath,\n JSON.stringify({ ...info, readyAt: new Date().toISOString() }, null, 2) + '\\n'\n );\n\n return await body(info);\n } finally {\n // Cleanup always runs — even if probe threw or body threw\n if (child && pid !== undefined) {\n await killChildProcess(child, pid);\n }\n\n // Remove ready file (best-effort — ignore ENOENT)\n try {\n unlinkSync(readyFilePath);\n } catch {\n /* best-effort */\n }\n\n // Close log fd (best-effort)\n try {\n closeSync(logFd);\n } catch {\n /* best-effort */\n }\n }\n}\n"],"mappings":"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","names":["import_fs","import_path","import_child_process","platform","import_child_process","import_fs","import_path","resolve","resolve"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/lib.ts","../src/dev-server.ts"],"sourcesContent":["/**\n * @stackwright-pro/raft — Launch the Pro Otter Raft\n *\n * Writes init-context, verifies otter integrity, and spawns code-puppy\n * in foreman mode. The MCP tools handle everything after that.\n *\n * Replaces the deprecated Python cli_adapter.py → ForemanSession → os.execvpe() chain.\n */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync, closeSync } from 'fs';\nimport { join, resolve } from 'path';\nimport { spawn } from 'child_process';\nimport { verifyAllOtters } from '@stackwright-pro/mcp/integrity';\nimport { buildTypeSchemaSummary } from '@stackwright-pro/mcp/type-schemas';\nimport {\n parseArgs,\n printHelp,\n writeInitContext,\n findCodePuppy,\n validateBinaryVersion,\n migrateWorkspaceDir,\n ensureWorkspaceConfig,\n ensureMcpConfig,\n syncAgents,\n printResumeStatus,\n isPipelineResumable,\n resolveOtterDir,\n buildCodePuppyEnv,\n buildLauncherEnvOverrides,\n buildSpawnConfig,\n die,\n log,\n verbose,\n} from './lib.js';\nimport { withDevServer } from './dev-server.js';\n\n// ─── Retry / Backoff Constants ───────────────────────────────────────────────\n\n/** Max automatic re-spawn attempts after an unexpected non-zero exit. */\nconst MAX_RETRIES = 3;\n/** Initial backoff delay in ms. Doubles on each attempt, capped at MAX_BACKOFF_MS. */\nconst BACKOFF_INITIAL_MS = 5_000;\n/** Hard ceiling on backoff delay. */\nconst BACKOFF_MAX_MS = 60_000;\n\n/** Exponential backoff: 5s → 10s → 20s → (cap) 60s. */\nfunction backoffMs(attempt: number): number {\n return Math.min(BACKOFF_INITIAL_MS * Math.pow(2, attempt), BACKOFF_MAX_MS);\n}\n\n// ─── Main ────────────────────────────────────────────────────────────────────\n\nasync function main(): Promise<void> {\n const args = parseArgs(process.argv);\n\n if (args.help) {\n printHelp();\n process.exit(0);\n }\n\n const projectRoot = resolve(args.projectRoot);\n if (!existsSync(projectRoot)) {\n die(`Project root does not exist: ${projectRoot}`);\n }\n\n // Sanity check: is this a Stackwright project?\n if (!existsSync(join(projectRoot, 'package.json'))) {\n die('No package.json found. Run npx @stackwright-pro/launch-stackwright-pro first.');\n }\n\n log('Launching Pro Otter Raft...');\n\n // 1. Write/enrich init context\n writeInitContext(projectRoot);\n verbose('Init context written', args.verbose);\n\n // 1a. Write type schemas sink for foreman routing\n try {\n const stackwrightDir = join(projectRoot, '.stackwright');\n mkdirSync(stackwrightDir, { recursive: true });\n const schemaSummary = buildTypeSchemaSummary();\n writeFileSync(\n join(stackwrightDir, 'type-schemas.json'),\n JSON.stringify(schemaSummary, null, 2) + '\\n'\n );\n verbose('Type schemas sink written', args.verbose);\n } catch (err) {\n // Non-blocking — foreman can still operate without type-schemas.json\n console.warn('⚠️ Could not write type-schemas.json:', String(err));\n }\n\n // 1b. Write raft flags to init-context for foreman consumption\n {\n const stackwrightDir = join(projectRoot, '.stackwright');\n const initContextPath = join(stackwrightDir, 'init-context.json');\n\n // Merge flags into existing init-context (writeInitContext already created it)\n const initRaw = readFileSync(initContextPath, 'utf-8');\n const initCtx = JSON.parse(initRaw) as Record<string, unknown>;\n\n if (args.nonInteractive) initCtx['nonInteractive'] = true;\n if (args.devOnly) initCtx['devOnly'] = true;\n if (args.parallelPhases) initCtx['parallelPhases'] = true;\n if (args.dataflowScheduling) initCtx['dataflowScheduling'] = true;\n\n // QA gate flags (swp-sc4x / Bead F)\n initCtx['skipQa'] = args.skipQa;\n initCtx['qaOnly'] = args.qaOnly;\n initCtx['devServer'] = {\n managed: true,\n baseUrl: 'http://localhost:3000',\n timeoutMs: args.qaDevServerTimeoutMs,\n };\n\n writeFileSync(initContextPath, JSON.stringify(initCtx, null, 2) + '\\n', 'utf-8');\n verbose('Raft flags written to init-context', args.verbose);\n }\n\n // 1c. Seed build context from --use-case file or generic fallback\n {\n const buildContextPath = join(projectRoot, '.stackwright', 'build-context.json');\n const MAX_USE_CASE_BYTES = 1 * 1024 * 1024; // 1MB\n const WARN_USE_CASE_BYTES = 200 * 1024; // 200KB\n\n if (args.useCasePath) {\n const resolvedPath = resolve(args.useCasePath);\n if (!existsSync(resolvedPath)) {\n die(`Use-case file not found: ${resolvedPath}`);\n }\n\n const content = readFileSync(resolvedPath, 'utf-8');\n if (content.length > MAX_USE_CASE_BYTES) {\n die(\n `Use-case file exceeds ${(MAX_USE_CASE_BYTES / 1024 / 1024).toFixed(0)}MB ` +\n `(got ${(content.length / 1024).toFixed(0)}KB). Trim the file or split into sections.`\n );\n }\n if (content.length > WARN_USE_CASE_BYTES) {\n console.warn(\n `⚠️ Large use-case file (${(content.length / 1024).toFixed(0)}KB) — ` +\n `specialist otters will receive the full context via build prompts.`\n );\n }\n\n const payload = {\n version: '1.0',\n savedAt: new Date().toISOString(),\n buildContext: content,\n };\n writeFileSync(buildContextPath, JSON.stringify(payload, null, 2) + '\\n', 'utf-8');\n log(`Build context seeded from ${args.useCasePath}`);\n } else if (args.nonInteractive && !existsSync(buildContextPath)) {\n // Generic fallback for --non-interactive without --use-case\n const payload = {\n version: '1.0',\n savedAt: new Date().toISOString(),\n buildContext:\n 'Build a sample application from the available API specs and data sources ' +\n 'with default settings. Use all available endpoints and standard page layouts.',\n };\n writeFileSync(buildContextPath, JSON.stringify(payload, null, 2) + '\\n', 'utf-8');\n log('Build context seeded with generic defaults (--non-interactive, no --use-case)');\n }\n }\n\n // 1d. Migrate legacy .code-puppy/ to .code_puppy/ if needed\n migrateWorkspaceDir(projectRoot);\n\n // 1e. Ensure workspace config — write .code_puppy/config.json (profile: strict-local)\n // Must come before ensureMcpConfig so the workspace dir exists.\n ensureWorkspaceConfig(projectRoot);\n\n // 1f. Register MCP server — do this BEFORE any die() calls so clean installs\n // get MCP config even if otter install is incomplete\n ensureMcpConfig(projectRoot);\n\n // 1g. Sync agent files from installed @stackwright-pro/otters — always reflects\n // the installed version without relying on postinstall hooks\n syncAgents(projectRoot, args.verbose);\n\n // 2. Verify otter integrity\n const otterDir = resolveOtterDir(projectRoot);\n if (!otterDir) {\n die(\n 'Could not find otter directory. Is @stackwright-pro/otters installed?\\n' +\n ' Run: pnpm add @stackwright-pro/otters'\n );\n }\n\n const result = verifyAllOtters(otterDir);\n if (result.failed.length > 0) {\n console.warn('⚠️ Otter integrity check warnings (non-blocking):');\n for (const f of result.failed) {\n console.warn(` ${f.filename}: ${f.error}`);\n }\n console.warn(' Note: SHA-256 pinning will be replaced by PKI-signed deployment manifests.');\n console.warn(\n ' See: https://github.com/Per-Aspera-LLC/stackwright-pro/issues (signing model issue)'\n );\n } else {\n log(`✅ All ${result.verified.length} otters verified`);\n }\n\n // 3. Print resume status\n printResumeStatus(projectRoot);\n\n // 4. Resolve raft-puppy / code-puppy and validate version (once, before retry loop)\n const { path: executable, fd: initialFd } = findCodePuppy();\n verbose(`Resolved raft-puppy / code-puppy: ${executable}`, args.verbose);\n try {\n closeSync(initialFd);\n } catch {\n /* best-effort */\n }\n\n // 4a. Pre-flight: validate binary version before spawning\n validateBinaryVersion(executable);\n\n // 5. Spawn raft-puppy / code-puppy — with automatic retry on unexpected exits.\n //\n // If code-puppy exits with a non-zero code (e.g. Cloudflare 400 mid-session,\n // transient API error) AND the pipeline state is resumable (status = 'questions'\n // or 'execution'), we wait with exponential backoff then re-spawn with a \"Resume\"\n // prompt. The Foreman reads pipeline-state.json and picks up where it left off.\n //\n // SIGINT/SIGTERM from the user are NOT retried — they abort cleanly.\n // ── QA-only short-circuit (swp-sc4x / Bead F) ────────────────────────────\n // --qa-only: skip the full otter pipeline and jump straight to the visual QA gate.\n // The withDevServer bracket is live; the QA otter invocation lands in swp-wde8.\n if (args.qaOnly) {\n log('--qa-only mode: skipping otter pipeline, spawning dev server for visual QA...');\n await withDevServer(projectRoot, { timeoutMs: args.qaDevServerTimeoutMs }, async (info) => {\n // TODO(swp-wde8): invoke QA otter directly via code-puppy.\n // For now, log info and exit — the bracket is wired and tested.\n log('--qa-only: dev server ready ✅');\n log(` pid: ${info.pid}`);\n log(` baseUrl: ${info.baseUrl}`);\n if (info.prismUrl) log(` prismUrl: ${info.prismUrl}`);\n log(` logPath: ${info.logPath}`);\n log('⏸ QA otter invocation not yet implemented (blocked on swp-wde8 + swp-kjkg)');\n });\n process.exit(0);\n }\n\n log('Spawning raft-puppy / code-puppy raft session...');\n\n let retryCount = 0;\n let userInterrupted = false;\n\n function spawnSession(isResume: boolean): void {\n // Re-open the binary on each spawn attempt so we get a fresh inode lock.\n // On Linux: spawn via /proc/self/fd/N — the fd was opened (and inode-checked) by\n // findCodePuppy(), so an attacker cannot swap the binary between validation and exec.\n //\n // Shebang-aware: raft-puppy is a Python script, not an ELF binary. The kernel's\n // shebang handler re-execs the interpreter (python) with the fd path as a string\n // argument. O_CLOEXEC (set by Node's openSync) would close the fd during that exec,\n // causing Python to get ENOENT on /proc/self/fd/N.\n //\n // Fix: pass the fd through the stdio array at index 3. dup2() clears O_CLOEXEC,\n // so fd 3 survives the interpreter exec. We then spawn via /proc/self/fd/3.\n //\n // On macOS: /proc does not exist — we fall back to the validated path.\n // Residual TOCTOU on macOS is documented and accepted for the current threat model.\n // See: CWE-367, GH#128, beads issue stackwright-pro-s1g.\n const { path: resolvedBinary, fd: binaryFd } = findCodePuppy();\n\n if (process.platform !== 'linux') {\n verbose(\n `macOS/other: spawning via path (residual TOCTOU — /proc unavailable). ` +\n `See GH#128 for threat model documentation.`,\n args.verbose\n );\n }\n\n // First run: \"Begin\". Retry runs: \"Resume\" so the Foreman reads pipeline-state.json.\n const prompt = isResume ? 'Resume' : 'Begin';\n const spawnArgs = [prompt, '--interactive', '--agent', 'stackwright-pro-foreman-otter'];\n\n const { spawnTarget, stdioConfig } = buildSpawnConfig(\n process.platform,\n resolvedBinary,\n binaryFd\n );\n\n verbose(\n `cmd: ${spawnTarget} ${spawnArgs.join(' ')}` +\n `${spawnTarget !== resolvedBinary ? ` (real: ${resolvedBinary})` : ''}` +\n `${isResume ? ` [retry ${retryCount}/${MAX_RETRIES}]` : ''}`,\n args.verbose\n );\n\n const child = spawn(spawnTarget, spawnArgs, {\n stdio: stdioConfig,\n cwd: projectRoot,\n env: buildCodePuppyEnv(\n buildLauncherEnvOverrides(projectRoot, { subagentResponses: args.subagentResponses }),\n args.passEnvKeys\n ),\n });\n\n // Close the parent's copy of the fd. On Linux the child inherited it via dup2\n // to fd 3 (without O_CLOEXEC) so the child's copy survives exec independently.\n try {\n closeSync(binaryFd);\n } catch {\n /* best-effort */\n }\n\n // Forward signals to child — let it clean up gracefully.\n // Track interrupts so we DON'T retry on user-initiated termination.\n const forward = (signal: NodeJS.Signals): void => {\n if (child.pid) child.kill(signal);\n };\n const onSigint = (): void => {\n userInterrupted = true;\n forward('SIGINT');\n };\n const onSigterm = (): void => {\n userInterrupted = true;\n forward('SIGTERM');\n };\n process.on('SIGINT', onSigint);\n process.on('SIGTERM', onSigterm);\n\n child.on('error', (err) => die(`Failed to spawn raft-puppy / code-puppy: ${err.message}`));\n child.on('close', (code, signal) => {\n process.off('SIGINT', onSigint);\n process.off('SIGTERM', onSigterm);\n\n // Signal exit (e.g. SIGINT from user) — re-raise and exit cleanly\n if (signal) {\n process.kill(process.pid, signal);\n return;\n }\n\n // Clean exit or user-interrupted — done\n if (code === 0 || userInterrupted) {\n process.exit(code ?? 0);\n return;\n }\n\n // Non-zero exit — check if retry is warranted\n if (retryCount < MAX_RETRIES && isPipelineResumable(projectRoot)) {\n retryCount++;\n const delay = backoffMs(retryCount - 1);\n log(\n `⚠️ Raft session exited unexpectedly (code ${code ?? '?'}). ` +\n `Pipeline is mid-run — retrying in ${delay / 1_000}s… ` +\n `(attempt ${retryCount}/${MAX_RETRIES})`\n );\n setTimeout(() => spawnSession(true), delay);\n } else {\n if (retryCount >= MAX_RETRIES) {\n log(`❌ Max retries (${MAX_RETRIES}) reached — giving up. Check logs above for errors.`);\n }\n process.exit(code ?? 1);\n }\n });\n }\n\n // ── QA gate status log (swp-sc4x / Bead F) ──────────────────────────────\n // The dev-server lifecycle module is wired (withDevServer in dev-server.ts).\n // Actual QA otter invocation is deferred to Bead D (swp-wde8) + Bead E (swp-kjkg).\n if (args.skipQa) {\n log('⚠️ Visual QA skipped by --skip-qa flag');\n } else {\n log(\n '⏸ Visual QA stub — dev server lifecycle is wired, QA otter invocation lands in swp-wde8/swp-kjkg'\n );\n }\n\n spawnSession(false);\n}\n\nmain().catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : String(err);\n die(`Unexpected fatal error: ${msg}`);\n});\n","/**\n * @stackwright-pro/raft — Pure utility functions\n *\n * Extracted from the CLI entry point so they're independently testable.\n * All side-effectful helpers (die, log, verbose) live here too —\n * the CLI entry point (`index.ts`) just wires them into `main()`.\n */\n\n// Raft is a process launcher — spawning a child process is its core job.\n// The single spawnSync call site (findCodePuppy, ~line 578) passes hardcoded\n// args ['raft-puppy'|'code-puppy'] to `which`; no user input ever reaches it.\n// All spawn call sites in this file are guarded by:\n// - findCodePuppy() — binary path resolution, fd-pinned TOCTOU mitigation\n// (CWE-367), mode-bit checks, and setuid/setgid rejection (see ~line 544)\n// - CODE_PUPPY_ENV_ALLOWLIST / buildCodePuppyEnv() — explicit env-var\n// allowlist that prevents credential leakage to the child (CWE-526, ~line 38)\n// This is a targeted line suppression, not a file-wide disable — any new\n// unguarded spawn/exec/fork added to this file will still trip the rule.\n// nosemgrep: javascript.lang.security.detect-child-process.detect-child-process\nimport { spawnSync } from 'child_process';\nimport {\n existsSync,\n lstatSync,\n mkdirSync,\n openSync,\n fstatSync,\n closeSync,\n readFileSync,\n readdirSync,\n realpathSync,\n renameSync,\n writeFileSync,\n rmSync,\n} from 'fs';\nimport { join, resolve } from 'path';\nimport { hostname, homedir, platform } from 'os';\n\n// ─── Code-Puppy Environment Allowlist ────────────────────────────────────────\n//\n// CWE-526: Sensitive environment variables must not be passed to child processes.\n// CVSS v4.0 ~3.0. The spawn call in index.ts MUST use buildCodePuppyEnv() — never\n// spread ...process.env directly.\n//\n// Only env vars in this allowlist (or matching the prefix list) are forwarded to\n// the code-puppy child process. AWS_*, GITHUB_TOKEN, DATABASE_URL, NPM_TOKEN, etc.\n// are silently stripped.\n\n/**\n * Exact env var names that code-puppy legitimately needs.\n */\nconst CODE_PUPPY_ENV_ALLOWLIST: ReadonlySet<string> = new Set([\n // Shell / execution environment\n 'PATH',\n 'HOME',\n 'USER',\n 'USERNAME',\n 'SHELL',\n 'TMPDIR',\n 'TMP',\n 'TEMP',\n // Terminal\n 'TERM',\n 'COLORTERM',\n 'COLUMNS',\n 'LINES',\n 'NO_COLOR',\n 'FORCE_COLOR',\n // Language / locale\n 'LANG',\n 'LC_ALL',\n 'LC_CTYPE',\n 'LC_MESSAGES',\n // Network proxy — needed for outbound LLM API calls behind corporate proxies\n 'HTTP_PROXY',\n 'HTTPS_PROXY',\n 'NO_PROXY',\n 'http_proxy',\n 'https_proxy',\n 'no_proxy',\n // LLM API keys — the only credentials code-puppy should have\n 'ANTHROPIC_API_KEY',\n 'ANTHROPIC_BASE_URL',\n 'OPENAI_API_KEY',\n // Node runtime\n 'NODE_ENV',\n // Stackwright-specific\n 'STACKWRIGHT_PROJECT_ROOT',\n 'STACKWRIGHT_SKIP_PREFLIGHT',\n 'STACKWRIGHT_CODE_PUPPY_PATH',\n // Telemetry vars (swp-bhw1): NDJSON path is force-injected by the launcher (see index.ts),\n // but listing here allows user override via shell env. DISABLED/DEBUG are opt-in toggles.\n 'STACKWRIGHT_TELEMETRY_NDJSON',\n 'STACKWRIGHT_TELEMETRY_NDJSON_SUBAGENT_RESPONSES',\n 'STACKWRIGHT_TELEMETRY_DISABLED',\n 'STACKWRIGHT_TELEMETRY_DEBUG',\n]);\n\n/**\n * Prefix-based allowlist — any env var starting with these prefixes is allowed.\n * Covers families like PYTHONPATH, PYTHONHOME, LC_NUMERIC, etc.\n */\nconst CODE_PUPPY_ENV_PREFIXES: readonly string[] = ['PYTHON', 'LC_'];\n\n// ─── Pre-flight Constants ────────────────────────────────────────────────────\n\n/** Minimum code-puppy / raft-puppy version required by this raft release.\n *\n * raft-puppy / code-puppy uses 0.0.x patch-only versioning on PyPI (e.g. 0.0.575).\n *\n * Bump this when a new raft-puppy feature becomes a hard dependency.\n * 0.0.575 — first version with the project_workspace builtin plugin\n * (profile-based scoping). Older versions silently degrade to additive\n * merge mode, which breaks raft's reproducibility guarantee.\n *\n * Dev builds (0.0.0 / 0.0.0-*) are handled separately by the isDevBuild bypass.\n */\nexport const MIN_SUPPORTED_CODE_PUPPY_VERSION = '0.0.575';\n\n// ─── CLI Argument Parsing ────────────────────────────────────────────────────\n\nexport interface ParsedArgs {\n projectRoot: string;\n verbose: boolean;\n help: boolean;\n /** Keys explicitly passed via --pass-env flags. */\n passEnvKeys: string[];\n /** Run without TUI prompts — use sane defaults for all questions. */\n nonInteractive: boolean;\n /** Skip real auth config — use mock auth with roles derived from use case. */\n devOnly: boolean;\n /** Path to a use-case file that seeds the build context. */\n useCasePath: string | null;\n /** Skip the visual QA gate entirely (writes skipQa: true to init-context). */\n skipQa: boolean;\n /** Skip the otter pipeline and jump straight to visual QA (writes qaOnly: true). */\n qaOnly: boolean;\n /** Dev server startup timeout in ms for the visual QA gate. Default: 60000. */\n qaDevServerTimeoutMs: number;\n /** Enable parallel specialist invocation within dependency waves (~40% wall-time reduction). Default: off (serial). */\n parallelPhases: boolean;\n /** Dataflow scheduling: dissolve wave barriers, fire phases as deps satisfy (swp-ioc7). Default: off. */\n dataflowScheduling: boolean;\n /** Forward STACKWRIGHT_TELEMETRY_NDJSON_SUBAGENT_RESPONSES=1 to raft-puppy so it emits agent_response events carrying full sub-agent response text. Off by default — sub-agent responses can be very large and noisy. */\n subagentResponses: boolean;\n}\n\nexport function parseArgs(argv: string[]): ParsedArgs {\n const args: ParsedArgs = {\n projectRoot: process.cwd(),\n verbose: false,\n help: false,\n passEnvKeys: [],\n nonInteractive: false,\n devOnly: false,\n useCasePath: null,\n skipQa: false,\n qaOnly: false,\n qaDevServerTimeoutMs: 60_000,\n parallelPhases: false,\n dataflowScheduling: false,\n subagentResponses: false,\n };\n\n const raw = argv.slice(2);\n for (let i = 0; i < raw.length; i++) {\n const token = raw[i];\n switch (token) {\n case '--project-root':\n args.projectRoot = raw[++i] ?? die('--project-root requires a path argument');\n break;\n case '--verbose':\n args.verbose = true;\n break;\n case '--help':\n case '-h':\n args.help = true;\n break;\n case '--pass-env':\n args.passEnvKeys.push(raw[++i] ?? die('--pass-env requires a KEY argument'));\n break;\n case '--non-interactive':\n args.nonInteractive = true;\n break;\n case '--dev-only':\n args.devOnly = true;\n break;\n case '--use-case':\n args.useCasePath = raw[++i] ?? die('--use-case requires a file path argument');\n break;\n case '--skip-qa':\n args.skipQa = true;\n break;\n case '--qa-only':\n args.qaOnly = true;\n break;\n case '--parallel-phases':\n args.parallelPhases = true;\n break;\n case '--dataflow-scheduling':\n args.dataflowScheduling = true;\n break;\n case '--subagent-responses':\n args.subagentResponses = true;\n break;\n default:\n // --qa-dev-server-timeout-ms=<n> form\n if (token.startsWith('--qa-dev-server-timeout-ms=')) {\n const val = parseInt(token.split('=')[1] ?? '', 10);\n if (isNaN(val) || val <= 0) {\n die('--qa-dev-server-timeout-ms requires a positive integer (ms)');\n }\n args.qaDevServerTimeoutMs = val;\n break;\n }\n die(`Unknown option: ${token}\\nRun with --help for usage.`);\n }\n }\n\n return args;\n}\n\nexport function printHelp(): void {\n console.log(\n `\n🦦 @stackwright-pro/raft — Spawn raft-puppy (or code-puppy) in foreman mode\n\nUsage: launch-raft [options]\n\nOptions:\n --project-root <path> Project root directory (default: cwd)\n --verbose Enable verbose logging\n --pass-env <KEY> Forward an additional env var to code-puppy (repeatable)\n --non-interactive Skip all TUI prompts, use sane defaults for questions\n --dev-only Mock auth only — roles derived from use case, no real providers\n --use-case <file> Seed \"what to build\" from a file (plain text or markdown)\n --skip-qa Skip the visual QA gate (useful for CI or fast iterations)\n --qa-only Skip the otter pipeline; spawn dev server and run QA directly\n --qa-dev-server-timeout-ms=<n> Dev server startup timeout in ms for QA gate (default: 60000)\n --parallel-phases Enable parallel specialist invocation within dependency waves (~40% wall-time reduction). Default: off (serial).\n --dataflow-scheduling Dissolve wave barriers: fire phases as deps satisfy, not when the whole wave clears. Default: off (wave-parallel mode). (swp-ioc7)\n --subagent-responses Capture full sub-agent response text in raft-puppy NDJSON telemetry (agent_response events). Verbose — can produce large logs. Default: off.\n --help, -h Show this help\n\nPrerequisites:\n pip install stackwright-puppy # provides raft-puppy + code-puppy alias\n Or: pip install code-puppy # fallback (MCP tools may not auto-start)\n`.trim()\n );\n}\n\n// ─── Utilities ───────────────────────────────────────────────────────────────\n\nexport function die(message: string): never {\n console.error(`❌ ${message}`);\n process.exit(1);\n}\n\nexport function log(message: string): void {\n console.log(`🦦 ${message}`);\n}\n\nexport function verbose(message: string, isVerbose: boolean): void {\n if (isVerbose) {\n console.log(` ${message}`);\n }\n}\n\n/**\n * Build a restricted environment for the code-puppy child process.\n *\n * Filters `process.env` to only keys in CODE_PUPPY_ENV_ALLOWLIST or matching\n * CODE_PUPPY_ENV_PREFIXES, then merges in `extraVars` (always wins) and any\n * keys explicitly requested via `--pass-env` (escape hatch).\n *\n * CWE-526 mitigation: credentials like AWS_SECRET_ACCESS_KEY, GITHUB_TOKEN,\n * DATABASE_URL, NPM_TOKEN, etc. are silently excluded.\n *\n * @param extraVars Vars to force-inject (e.g. STACKWRIGHT_PROJECT_ROOT).\n * @param passEnvKeys Additional keys from --pass-env flags.\n */\nexport function buildCodePuppyEnv(\n extraVars: Record<string, string> = {},\n passEnvKeys: string[] = []\n): NodeJS.ProcessEnv {\n const env: NodeJS.ProcessEnv = {};\n\n for (const [key, value] of Object.entries(process.env)) {\n if (value === undefined) continue;\n\n const allowed =\n CODE_PUPPY_ENV_ALLOWLIST.has(key) ||\n CODE_PUPPY_ENV_PREFIXES.some((prefix) => key.startsWith(prefix)) ||\n passEnvKeys.includes(key);\n\n if (allowed) {\n env[key] = value;\n }\n }\n\n // extraVars always win — they're injected by the raft, not from the parent env\n for (const [key, value] of Object.entries(extraVars)) {\n env[key] = value;\n }\n\n return env;\n}\n\n/**\n * Builds the force-injected env overrides the launcher always passes to code-puppy.\n *\n * Extracted for testability — the spawn block in index.ts should call this and\n * pass the result as `extraVars` to `buildCodePuppyEnv()`. Any key returned here\n * bypasses the allowlist (extraVars always win) AND is also listed in\n * `CODE_PUPPY_ENV_ALLOWLIST` so user shell overrides continue to work.\n *\n * @param projectRoot Absolute resolved project root (same as `args.projectRoot`).\n */\nexport interface LauncherEnvOptions {\n /** When true, set STACKWRIGHT_TELEMETRY_NDJSON_SUBAGENT_RESPONSES=1 to capture sub-agent .response text in NDJSON. Default off. */\n subagentResponses?: boolean;\n}\n\nexport function buildLauncherEnvOverrides(\n projectRoot: string,\n options: LauncherEnvOptions = {}\n): Record<string, string> {\n const overrides: Record<string, string> = {\n STACKWRIGHT_PROJECT_ROOT: projectRoot,\n // swp-bhw1: route raft-puppy telemetry to a SEPARATE file from Pro telemetry.\n // Two-file separation paves the way for the eventual message-bus architecture\n // (distributed deployments, restart, replay). User can override via shell env.\n STACKWRIGHT_TELEMETRY_NDJSON: join(projectRoot, '.stackwright', 'raft-puppy-events.ndjson'),\n };\n if (options.subagentResponses) {\n overrides['STACKWRIGHT_TELEMETRY_NDJSON_SUBAGENT_RESPONSES'] = '1';\n }\n return overrides;\n}\n\n// ─── Pipeline Lock ──────────────────────────────────────────────────────────\n\nconst LOCK_FILE = '.stackwright/.lock';\n\nexport function acquireLock(projectRoot: string): boolean {\n const lockPath = join(projectRoot, LOCK_FILE);\n\n // Symlink guard\n if (existsSync(lockPath) && lstatSync(lockPath).isSymbolicLink()) {\n die('.stackwright/.lock is a symlink — refusing to acquire lock. Check for tampering.');\n }\n\n const pid = process.pid;\n const host = hostname();\n const timestamp = new Date().toISOString();\n\n const lockContent = JSON.stringify({\n pid,\n hostname: host,\n acquiredAt: timestamp,\n version: '1.0',\n });\n\n mkdirSync(join(projectRoot, '.stackwright'), { recursive: true });\n\n try {\n // O_EXCL makes this atomic on POSIX — fails if file already exists\n writeFileSync(lockPath, lockContent, { flag: 'wx' });\n return true;\n } catch (err) {\n // EEXIST means lock already held by another process\n if (err instanceof Error && 'code' in err && (err as { code: string }).code === 'EEXIST') {\n // Lock exists — try to read it and check if the process is still alive\n try {\n const existing = JSON.parse(readFileSync(lockPath, 'utf8')) as Record<string, unknown>;\n const oldPid = existing['pid'] as number;\n\n // On Unix, check if process still exists via kill(0, pid)\n try {\n process.kill(oldPid, 0); // Signal 0 = check existence only\n // Process exists — lock is held by live process, cannot acquire\n return false;\n } catch {\n // Process is dead — stale lock, can take over\n writeFileSync(lockPath, lockContent, 'utf-8');\n return true;\n }\n } catch {\n // Can't read lock file or not JSON — treat as stale, take over\n writeFileSync(lockPath, lockContent, 'utf-8');\n return true;\n }\n }\n throw err;\n }\n}\n\nexport function releaseLock(projectRoot: string): void {\n const lockPath = join(projectRoot, LOCK_FILE);\n try {\n rmSync(lockPath);\n } catch {\n // Lock file may not exist — that's fine\n }\n}\n\n// ─── Workspace Directory Migration ────────────────────────────────────────────\n\n/**\n * Migrate the legacy `.code-puppy/` workspace directory to `.code_puppy/`.\n *\n * PR #290 renamed the workspace directory from `.code-puppy` (hyphen) to\n * `.code_puppy` (underscore) to match the canonical `PROJECT_WORKSPACE_DIR_NAME`\n * in code-puppy's `config.py`. Projects scaffolded before that change still\n * have the old directory; without migration, `get_project_workspace()` walks\n * past it and falls through to `$HOME/.code_puppy/` (global config), causing\n * `strict-local` profile and local MCP server loading to silently fail.\n *\n * Safe to call on every launch — no-ops when there's nothing to migrate.\n */\nexport function migrateWorkspaceDir(projectRoot: string): void {\n const oldDir = join(projectRoot, '.code-puppy');\n const newDir = join(projectRoot, '.code_puppy');\n\n const oldExists = existsSync(oldDir);\n const newExists = existsSync(newDir);\n\n if (!oldExists) return; // Nothing to migrate\n\n // Symlink guards — refuse to follow symlinks on either directory\n if (lstatSync(oldDir).isSymbolicLink()) {\n die('.code-puppy/ is a symlink — refusing to migrate. Check for tampering.');\n }\n if (newExists && lstatSync(newDir).isSymbolicLink()) {\n die('.code_puppy/ is a symlink — refusing to migrate. Check for tampering.');\n }\n\n if (!newExists) {\n // Clean migration: old exists, new doesn't → rename\n renameSync(oldDir, newDir);\n log('Migrated .code-puppy/ → .code_puppy/ (underscore convention)');\n } else {\n // Both exist — remove old to avoid confusion (underscore is canonical)\n rmSync(oldDir, { recursive: true });\n log('Removed stale .code-puppy/ (underscore version already exists)');\n }\n}\n\n// ─── Write Init Context ─────────────────────────────────────────────────────\n\nexport function writeInitContext(projectRoot: string): void {\n // Acquire pipeline lock — prevent concurrent launch-raft processes\n if (!acquireLock(projectRoot)) {\n let existingPid: string | number = 'unknown';\n try {\n const lockPath = join(projectRoot, LOCK_FILE);\n if (existsSync(lockPath)) {\n const lockData = JSON.parse(readFileSync(lockPath, 'utf8')) as Record<string, unknown>;\n const rawPid = lockData['pid'];\n existingPid = typeof rawPid === 'string' || typeof rawPid === 'number' ? rawPid : 'unknown';\n }\n } catch {\n // Couldn't read lock file\n }\n die(\n `Pipeline lock already held by PID ${existingPid}. Another launch-raft process is running.`\n );\n }\n\n const stackwrightDir = join(projectRoot, '.stackwright');\n const initContextPath = join(stackwrightDir, 'init-context.json');\n\n // Merge, don't clobber — respect anything the launcher already wrote\n const MAX_INIT_CONTEXT_BYTES = 1 * 1024 * 1024; // 1MB\n let existing: Record<string, unknown> = {};\n try {\n const raw = readFileSync(initContextPath, 'utf-8');\n\n // Size guard — reject oversized init-context.json before parsing\n if (raw.length > MAX_INIT_CONTEXT_BYTES) {\n die(\n `init-context.json exceeds ${MAX_INIT_CONTEXT_BYTES.toLocaleString()} bytes (got ${raw.length.toLocaleString()}). Refusing to parse. This may be an attack or a corrupted file.`\n );\n }\n\n existing = JSON.parse(raw) as Record<string, unknown>;\n } catch {\n // Fresh project or malformed file — start from scratch\n }\n\n // Enrich: only fill in gaps\n existing['projectRoot'] = projectRoot;\n\n if (!existing['projectName']) {\n try {\n const pkgRaw = readFileSync(join(projectRoot, 'package.json'), 'utf-8');\n const pkg = JSON.parse(pkgRaw) as Record<string, unknown>;\n if (typeof pkg['name'] === 'string') {\n existing['projectName'] = pkg['name'];\n }\n } catch {\n // No package.json name — that's fine\n }\n }\n\n if (!existing['specPath']) {\n try {\n const specsDir = join(projectRoot, 'specs');\n if (existsSync(specsDir)) {\n const files = readdirSync(specsDir);\n const first = files[0];\n if (first) {\n existing['specPath'] = join('specs', first);\n }\n }\n } catch {\n // No specs dir — that's fine\n }\n }\n\n if (!existing['theme']) {\n try {\n const ymlPath = join(projectRoot, 'stackwright.yml');\n const ymlContent = readFileSync(ymlPath, 'utf-8');\n const match = /theme:\\s*\\n\\s+id:\\s*(.+)/.exec(ymlContent);\n if (match?.[1]) {\n existing['theme'] = match[1].trim();\n }\n } catch {\n // No stackwright.yml — that's fine\n }\n }\n\n existing['generatedBy'] = 'launch-raft';\n existing['version'] = '1.0';\n\n mkdirSync(stackwrightDir, { recursive: true });\n\n // Symlink guard — refuse to follow symlinks (prevents symlink-based overwrites)\n if (existsSync(stackwrightDir) && lstatSync(stackwrightDir).isSymbolicLink()) {\n die('.stackwright is a symlink — refusing to write. Check for tampering.');\n }\n if (existsSync(initContextPath) && lstatSync(initContextPath).isSymbolicLink()) {\n die('init-context.json is a symlink — refusing to write. Check for tampering.');\n }\n\n writeFileSync(initContextPath, JSON.stringify(existing, null, 2), 'utf-8');\n}\n\n// ─── Shutdown handler — release lock on exit ──────────────────────────────────\n\nprocess.on('exit', () => {\n try {\n releaseLock(process.cwd());\n } catch {\n // Lock release is best-effort on shutdown — don't block exit\n }\n});\nprocess.on('SIGINT', () => {\n try {\n releaseLock(process.cwd());\n } catch {\n // Lock release is best-effort on signal — don't block exit\n }\n process.exit(0);\n});\nprocess.on('SIGTERM', () => {\n try {\n releaseLock(process.cwd());\n } catch {\n // Lock release is best-effort on signal — don't block exit\n }\n process.exit(0);\n});\n\n// ─── Trusted Binary Directories ────────────────────────────────────────────\n\n/**\n * Returns the ordered list of trusted installation directories to probe\n * for raft-puppy / code-puppy before falling back to $PATH (`which`).\n *\n * These represent well-known, user-controlled install locations that a\n * malicious npm postinstall script cannot shadow by merely prepending to\n * $PATH — providing a meaningful defence against CWE-426 (Untrusted\n * Search Path). raft-puppy / code-puppy are Python tools, so pip user\n * install (~/.local/bin), brew, and system package managers cover all\n * supported install methods.\n *\n * Exported for unit testing.\n */\nexport function getTrustedBinaryDirs(): string[] {\n return [\n join(homedir(), '.local', 'bin'), // pip user install, uvx, pipx (Linux/macOS)\n '/opt/homebrew/bin', // brew Apple Silicon\n '/usr/local/bin', // brew Intel, pip system install\n '/usr/bin', // system package managers (apt, dnf)\n ];\n}\n\n// ─── Find code-puppy Executable ─────────────────────────────────────────────\n\n/**\n * Result of findCodePuppy() — includes the resolved real path AND an open\n * O_RDONLY file descriptor to close the TOCTOU window between validation\n * and spawn. Keep the fd alive until spawn() returns (Linux: exec via\n * /proc/self/fd/N pins the inode; macOS: fd is best-effort, residual\n * TOCTOU documented).\n */\nexport interface FindCodePuppyResult {\n /** Resolved real path (symlinks followed) — use for display/logging only on Linux */\n path: string;\n /** Open O_RDONLY fd — caller MUST closeSync(fd) after spawn() returns */\n fd: number;\n}\n\nexport function findCodePuppy(trustedDirsOverride?: string[]): FindCodePuppyResult {\n let candidate: string | null = null;\n\n // 1. Explicit env var override — highest priority\n const envPath = process.env['STACKWRIGHT_CODE_PUPPY_PATH'];\n if (envPath) {\n candidate = envPath;\n }\n\n // 2. Trusted installation paths — probe before falling back to $PATH.\n // Prevents CWE-426: a malicious npm postinstall script cannot shadow\n // these paths by merely prepending to $PATH.\n if (!candidate) {\n const trustedDirs = trustedDirsOverride ?? getTrustedBinaryDirs();\n outer: for (const bin of ['raft-puppy', 'code-puppy']) {\n for (const dir of trustedDirs) {\n const p = join(dir, bin);\n if (existsSync(p)) {\n candidate = p;\n break outer;\n }\n }\n }\n }\n\n // 3. Prefer raft-puppy (stackwright-puppy fork) over vanilla code-puppy.\n // raft-puppy ships the MCP auto-enable fix and local .code-puppy.json\n // loading — both required for the raft to work on a clean install.\n // Falls back to code-puppy so existing installs don't break.\n if (!candidate) {\n for (const bin of ['raft-puppy', 'code-puppy']) {\n // spawnSync avoids spawning a shell — args passed directly to execvp,\n // no shell interpolation, no injection surface (CWE-78 / detect-child-process).\n const whichResult = spawnSync('which', [bin], { encoding: 'utf-8' });\n if (whichResult.status === 0 && whichResult.stdout) {\n candidate = whichResult.stdout.trim();\n if (candidate) break;\n }\n }\n }\n\n if (!candidate) {\n die(\n 'raft-puppy (or code-puppy) not found.\\n' +\n '\\n' +\n 'Install the Stackwright-patched build (recommended):\\n' +\n ' pip install stackwright-puppy\\n' +\n '\\n' +\n 'Or install vanilla code-puppy (MCP tools may not auto-start):\\n' +\n ' pip install code-puppy\\n' +\n '\\n' +\n 'Or set STACKWRIGHT_CODE_PUPPY_PATH to the binary path.'\n );\n }\n\n // Resolve to absolute path — prevents bare-name or relative-path shenanigans\n const resolved = resolve(candidate);\n\n if (!existsSync(resolved)) {\n die(`raft-puppy/code-puppy not found at resolved path: ${resolved}`);\n }\n\n // Follow the full symlink chain to the real binary.\n // pip/pipx/uvx installs create a wrapper symlink in ~/.local/bin/ that points\n // to the actual binary inside a virtualenv — this is expected and safe.\n let realBinary: string;\n try {\n realBinary = realpathSync(resolved);\n } catch {\n die(\n `raft-puppy at ${resolved} has a broken symlink — cannot resolve to a real path. ` +\n `Try reinstalling stackwright-puppy or set STACKWRIGHT_CODE_PUPPY_PATH to the real binary.`\n );\n }\n\n if (!existsSync(realBinary)) {\n die(`raft-puppy symlink at ${resolved} points to a missing file: ${realBinary}`);\n }\n\n // Open an fd BEFORE permission checks — fstatSync(fd) operates on the already-open\n // inode, so the checks and the spawn target cannot diverge. CWE-367 mitigation.\n let fd: number;\n try {\n fd = openSync(realBinary, 'r');\n } catch (err) {\n die(\n `Cannot open raft-puppy at ${realBinary} for validation: ${String(err)}. ` +\n `Try reinstalling stackwright-puppy or set STACKWRIGHT_CODE_PUPPY_PATH.`\n );\n }\n\n // Use fstatSync(fd) — operates on the pinned inode, not the filename.\n const stat = fstatSync(fd!);\n\n // Refuse world-writable or group-writable binaries\n if (stat.mode & 0o022) {\n closeSync(fd!);\n die(\n `raft-puppy at ${realBinary} is group- or world-writable (mode: ${(stat.mode & 0o777).toString(8)}) — refusing to exec.`\n );\n }\n\n // Refuse setuid/setgid binaries\n if (stat.mode & 0o6000) {\n closeSync(fd!);\n die(`raft-puppy at ${realBinary} has setuid/setgid bits — refusing to exec.`);\n }\n\n return { path: realBinary, fd: fd! };\n}\n\n// ─── Pre-flight Environment Validation ──────────────────────────────────────\n\nfunction parseSemver(version: string): [number, number, number] {\n const parts = version.split('-')[0]!.split('.').map(Number);\n return [parts[0] ?? 0, parts[1] ?? 0, parts[2] ?? 0];\n}\n\nfunction semverGte(a: string, b: string): boolean {\n const [aMaj, aMin, aPatch] = parseSemver(a);\n const [bMaj, bMin, bPatch] = parseSemver(b);\n if (aMaj !== bMaj) return aMaj > bMaj;\n if (aMin !== bMin) return aMin > bMin;\n return aPatch >= bPatch;\n}\n\n/**\n * Validates the runtime environment before spawning code-puppy.\n * Calls die() (process.exit(1)) with a human-readable remediation message\n * on failure — never reaches the spawn if a check fails.\n *\n * Auth checking is intentionally NOT done here — code-puppy/raft-puppy owns\n * the auth layer and will surface a clear error at spawn time for any\n * missing/invalid credentials. This keeps the raft agnostic to model\n * providers (Anthropic API key, claude auth login OAuth, AWS Bedrock,\n * Google Vertex, Ollama, air-gapped inference, etc.).\n *\n * Checks:\n * 1. STACKWRIGHT_SKIP_PREFLIGHT — if 'true', skip all checks and return\n * 2. code-puppy --version reports >= MIN_SUPPORTED_CODE_PUPPY_VERSION\n */\nexport function validateBinaryVersion(binaryPath: string): void {\n // Escape hatch for air-gapped / custom model-provider deployments.\n // Set STACKWRIGHT_SKIP_PREFLIGHT=true to bypass pre-flight checks.\n if (process.env['STACKWRIGHT_SKIP_PREFLIGHT'] === 'true') {\n log('Pre-flight checks skipped via STACKWRIGHT_SKIP_PREFLIGHT');\n return;\n }\n\n // 1. Version check — run binary with --version, parse semver, compare\n // binaryPath is validated by findCodePuppy(): realpathSync'd, symlink-checked,\n // mode/setuid-checked, and confirmed to exist. spawnSync passes args directly\n // to execvp — no shell spawned, no quoting needed (CWE-78).\n // binaryPath is never user-supplied: it is the output of findCodePuppy(), which\n // resolves it through realpathSync and validates mode bits and setuid before\n // returning it. This is a targeted suppression — not a file-wide disable.\n const versionStartTime = Date.now();\n // nosemgrep: javascript.lang.security.detect-child-process.detect-child-process\n const versionResult = spawnSync(binaryPath, ['--version'], {\n encoding: 'utf-8',\n // 15s: uv-installed Python tools cold-start (venv activation + interpreter\n // startup) can take 5-10s on first invocation; subsequent runs are ms.\n timeout: 15000,\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n const versionElapsed = Date.now() - versionStartTime;\n\n if (versionResult.error !== undefined || versionResult.status !== 0) {\n const errorDetail = versionResult.error\n ? `${(versionResult.error as NodeJS.ErrnoException).code ?? 'unknown'}: ${versionResult.error.message}`\n : `exit status ${String(versionResult.status)}`;\n const stdoutSnip = (versionResult.stdout ?? '').trim() || '<empty>';\n const stderrSnip = (versionResult.stderr ?? '').trim() || '<empty>';\n die(\n `Could not determine raft-puppy / code-puppy version.\\n` +\n ` Binary: ${binaryPath}\\n` +\n ` Failure: ${errorDetail}\\n` +\n ` Elapsed: ${String(versionElapsed)}ms (timeout: 15000ms)\\n` +\n ` Stdout: ${stdoutSnip}\\n` +\n ` Stderr: ${stderrSnip}\\n` +\n ` Required: ${MIN_SUPPORTED_CODE_PUPPY_VERSION}\\n` +\n `\\n` +\n ` Run: pip install --upgrade stackwright-puppy\\n` +\n ` Or: pip install --upgrade code-puppy\\n` +\n `\\n` +\n ` If the binary works manually but fails here, set\\n` +\n ` STACKWRIGHT_SKIP_PREFLIGHT=true to bypass this check.`\n );\n }\n\n // Some CLIs print --version to stderr instead of stdout. Accept either;\n // prefer stdout, fall back to stderr if stdout is empty.\n const versionStdout = versionResult.stdout.trim();\n const versionStderr = versionResult.stderr.trim();\n const versionOutput = versionStdout || versionStderr;\n\n const match = /(\\d+\\.\\d+\\.\\d+(?:-[^\\s]+)?)/.exec(versionOutput);\n if (!match || !match[1]) {\n die(\n `Could not parse version from raft-puppy / code-puppy output.\\n` +\n ` Binary: ${binaryPath}\\n` +\n ` Stdout: ${JSON.stringify(versionStdout) || '<empty>'}\\n` +\n ` Stderr: ${JSON.stringify(versionStderr) || '<empty>'}\\n` +\n ` Required: ${MIN_SUPPORTED_CODE_PUPPY_VERSION}\\n` +\n `\\n` +\n ` Run: pip install --upgrade stackwright-puppy\\n` +\n ` Or: pip install --upgrade code-puppy`\n );\n }\n\n const installedVersion = match[1];\n\n // Dev builds self-identify as 0.0.0-dev (or bare 0.0.0) — treat as a\n // local-source build and warn instead of hard-failing the version gate.\n // Any real published release should use a proper semver (e.g. 0.1.0-alpha.1).\n // NOTE: 0.0.0 bare is also treated as dev — it's never a valid published version.\n const isDevBuild = installedVersion === '0.0.0' || /^0\\.0\\.0-.+$/.test(installedVersion);\n if (isDevBuild) {\n console.warn(\n `⚠️ Dev build detected (${installedVersion}) — skipping minimum version check.\\n` +\n ` Minimum required for production: ${MIN_SUPPORTED_CODE_PUPPY_VERSION}\\n` +\n ` To suppress this warning in CI: set STACKWRIGHT_SKIP_PREFLIGHT=true`\n );\n return;\n }\n\n if (!semverGte(installedVersion, MIN_SUPPORTED_CODE_PUPPY_VERSION)) {\n die(\n `raft-puppy / code-puppy ${installedVersion} is below the minimum required version.\\n` +\n ` Installed: ${installedVersion}\\n` +\n ` Minimum required: ${MIN_SUPPORTED_CODE_PUPPY_VERSION}\\n` +\n `\\n` +\n ` Run: pip install --upgrade stackwright-puppy\\n` +\n ` Or: pip install --upgrade code-puppy`\n );\n }\n}\n\n// ─── Ensure MCP Config ─────────────────────────────────────────────────────\n\nexport function ensureMcpConfig(projectRoot: string): void {\n const workspaceDir = join(projectRoot, '.code_puppy');\n const configPath = join(workspaceDir, 'mcp_servers.json');\n\n // Symlink guard — consistent with other security patterns\n if (existsSync(configPath) && lstatSync(configPath).isSymbolicLink()) {\n die('.code_puppy/mcp_servers.json is a symlink — refusing to write. Check for tampering.');\n }\n\n mkdirSync(workspaceDir, { recursive: true });\n\n // Read existing → merge, don't clobber other registered servers\n let existing: { mcp_servers?: Record<string, unknown> } = {};\n if (existsSync(configPath)) {\n try {\n const raw = readFileSync(configPath, 'utf-8');\n existing = JSON.parse(raw) as { mcp_servers?: Record<string, unknown> };\n } catch {\n // Malformed file — start fresh\n }\n }\n\n // Resolve the pnpm-generated shim directly instead of going through `pnpm exec`.\n //\n // Why: the MCP Python SDK (mcp.client.stdio.get_default_environment) only inherits\n // 6 env vars by default — HOME, LOGNAME, PATH, SHELL, TERM, USER. Pnpm needs\n // PNPM_HOME, NODE_PATH, XDG_DATA_HOME, etc. to locate its store; without them it\n // exits in ~3-9ms without ever printing the \"running on stdio\" banner.\n //\n // The .bin/ shim is self-sufficient: it exports its own NODE_PATH and execs `node`\n // directly — only PATH is needed (already in the default 6). This also makes the\n // config package-manager agnostic: pnpm/npm/yarn all produce the same shim format.\n const shimName = platform() === 'win32' ? 'stackwright-pro-mcp.cmd' : 'stackwright-pro-mcp';\n const shimPath = join(projectRoot, 'node_modules', '.bin', shimName);\n\n if (!existsSync(shimPath)) {\n die(\n `MCP server shim not found: ${shimPath}\\n` +\n ` Run \\`pnpm install\\` (or your package manager equivalent) before \\`raft\\`.`\n );\n }\n\n const serverConfig = {\n type: 'stdio',\n command: shimPath,\n args: [] as string[],\n enabled: true,\n cwd: projectRoot,\n };\n\n const merged = {\n ...existing,\n mcp_servers: {\n ...(existing.mcp_servers ?? {}),\n 'stackwright-pro-mcp': serverConfig,\n },\n };\n\n // Atomic write via .tmp rename swap\n const tmpPath = `${configPath}.tmp`;\n writeFileSync(tmpPath, JSON.stringify(merged, null, 2), 'utf-8');\n renameSync(tmpPath, configPath);\n\n log('MCP server registered → .code_puppy/mcp_servers.json');\n}\n\n// ─── Ensure Workspace Config ─────────────────────────────────────────────────\n\n/**\n * Write .code_puppy/config.json with profile: \"strict-local\".\n *\n * code-puppy ≥ 0.0.575 uses a profile-based workspace scoping system\n * (the `project_workspace` builtin plugin). `strict-local` scopes ALL\n * six extension surfaces — agents, skills, plugins, mcp, hooks,\n * file_permissions — to the project directory only. This ensures raft\n * runs are fully isolated from the user's global ~/.code_puppy/ config,\n * which is raft's core reproducibility guarantee.\n *\n * Merges with any existing config.json without clobbering other fields.\n * Uses atomic write (.tmp rename swap) and symlink guards throughout.\n */\nexport function ensureWorkspaceConfig(projectRoot: string): void {\n const workspaceDir = join(projectRoot, '.code_puppy');\n const configPath = join(workspaceDir, 'config.json');\n\n // Symlink guard on the .code_puppy/ dir itself\n if (existsSync(workspaceDir) && lstatSync(workspaceDir).isSymbolicLink()) {\n die('.code_puppy/ is a symlink — refusing to write. Check for tampering.');\n }\n\n mkdirSync(workspaceDir, { recursive: true });\n\n // Symlink guard on config.json\n if (existsSync(configPath) && lstatSync(configPath).isSymbolicLink()) {\n die('.code_puppy/config.json is a symlink — refusing to write. Check for tampering.');\n }\n\n // Merge — don't clobber existing fields\n let existing: Record<string, unknown> = {};\n if (existsSync(configPath)) {\n try {\n existing = JSON.parse(readFileSync(configPath, 'utf-8')) as Record<string, unknown>;\n } catch {\n // Malformed — start fresh\n }\n }\n\n const merged = {\n ...existing,\n $schema: 'https://stackwright.dev/schemas/workspace-config/v2.json',\n profile: 'strict-local',\n };\n\n // Atomic write via .tmp rename swap\n const tmpPath = `${configPath}.tmp`;\n writeFileSync(tmpPath, JSON.stringify(merged, null, 2), 'utf-8');\n renameSync(tmpPath, configPath);\n\n log('Workspace config written → .code_puppy/config.json (profile: strict-local)');\n}\n\n// ─── Print Resume Status ────────────────────────────────────────────────────\n\nexport function printResumeStatus(projectRoot: string): void {\n const pipelineStatePath = join(projectRoot, '.stackwright', 'pipeline-state.json');\n\n try {\n const raw = readFileSync(pipelineStatePath, 'utf-8');\n const state = JSON.parse(raw) as Record<string, unknown>;\n const status = state['status'];\n const phases = state['phases'];\n\n if (typeof phases !== 'object' || phases === null || Array.isArray(phases)) {\n return;\n }\n\n const phaseEntries = Object.values(phases as Record<string, Record<string, unknown>>);\n const totalPhases = phaseEntries.length || 8;\n\n switch (status) {\n case 'setup':\n log('📍 Starting fresh');\n break;\n case 'questions': {\n const answered = phaseEntries.filter((p) => p['answered'] === true).length;\n log(`📍 Resuming: questions phase (${answered}/${totalPhases} phases answered)`);\n break;\n }\n case 'execution': {\n const executed = phaseEntries.filter((p) => p['executed'] === true).length;\n log(`📍 Resuming: execution phase (${executed}/${totalPhases} phases complete)`);\n break;\n }\n case 'done':\n log('📍 Pipeline complete — re-entering to review');\n break;\n default:\n break;\n }\n } catch {\n // No pipeline state yet — fresh project\n }\n}\n\n// ─── Pipeline Resumability Check ─────────────────────────────────────────────\n\n/**\n * Returns true if the pipeline is in a state that can be automatically resumed.\n *\n * Used by the retry/backoff logic in index.ts to decide whether a non-zero\n * code-puppy exit warrants a re-spawn. 'questions' and 'execution' statuses\n * mean the Foreman was mid-run; 'done' means it finished cleanly; no file\n * means it never started (no point retrying a blank slate).\n */\nexport function isPipelineResumable(projectRoot: string): boolean {\n const pipelineStatePath = join(projectRoot, '.stackwright', 'pipeline-state.json');\n try {\n const raw = readFileSync(pipelineStatePath, 'utf-8');\n const state = JSON.parse(raw) as Record<string, unknown>;\n const status = state['status'];\n return status === 'questions' || status === 'execution';\n } catch {\n return false;\n }\n}\n\n// ─── Resolve Otter Directory ────────────────────────────────────────────────\n\nexport function resolveOtterDir(projectRoot: string): string | null {\n const candidates = [\n join(projectRoot, 'node_modules', '@stackwright-pro', 'otters', 'src'),\n join(projectRoot, 'packages', 'otters', 'src'),\n ];\n\n for (const candidate of candidates) {\n if (existsSync(candidate)) {\n return candidate;\n }\n }\n\n return null;\n}\n\n// ─── Sync Agents ────────────────────────────────────────────────────────────\n\n/**\n * Copy all *-otter.json files from the resolved otters package to\n * .code_puppy/agents/ on every raft startup.\n *\n * This mirrors the pattern used by ensureMcpConfig() for mcp_servers.json.\n * Relying solely on the postinstall hook in @stackwright-pro/otters is fragile:\n * pnpm/npm don't always re-run scripts on package updates, and postinstall\n * order is non-deterministic when multiple packages run scripts. Active sync\n * on every raft launch guarantees agents are always the installed version.\n */\nexport function syncAgents(projectRoot: string, isVerbose: boolean = false): void {\n const agentsDir = join(projectRoot, '.code_puppy', 'agents');\n\n const otterDir = resolveOtterDir(projectRoot);\n if (!otterDir) {\n verbose('No otters directory found — skipping agent sync', isVerbose);\n return;\n }\n\n // Symlink guard on agents dir\n if (existsSync(agentsDir) && lstatSync(agentsDir).isSymbolicLink()) {\n die('.code_puppy/agents is a symlink — refusing to write. Check for tampering.');\n }\n\n mkdirSync(agentsDir, { recursive: true });\n\n let synced = 0;\n let skipped = 0;\n\n try {\n const files = readdirSync(otterDir);\n for (const file of files) {\n if (!file.endsWith('-otter.json')) continue;\n\n const src = join(otterDir, file);\n const dest = join(agentsDir, file);\n\n // Symlink guard on individual file\n if (existsSync(dest) && lstatSync(dest).isSymbolicLink()) {\n verbose(`Skipping ${file} — dest is a symlink`, isVerbose);\n skipped++;\n continue;\n }\n\n // Atomic write: copy to .tmp then rename\n const tmp = `${dest}.tmp`;\n const content = readFileSync(src);\n writeFileSync(tmp, content);\n renameSync(tmp, dest);\n verbose(`Synced: ${file}`, isVerbose);\n synced++;\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n // Non-fatal: log and continue — a stale agent is better than a crash\n console.warn(`⚠️ Agent sync partial failure: ${msg}`);\n return;\n }\n\n if (synced > 0) {\n log(\n `Agents synced → .code_puppy/agents/ (${synced} otters${skipped > 0 ? `, ${skipped} skipped` : ''})`\n );\n }\n}\n\n// ─── Spawn Config Builder ───────────────────────────────────────────────────\n\n/**\n * Build the spawn target and stdio config for code-puppy/raft-puppy.\n *\n * On Linux: pass the validated binary fd through stdio at index 3 (dup2 clears\n * O_CLOEXEC), then exec via /proc/self/fd/3. This works for both ELF and\n * shebang (Python) scripts — the fd survives the interpreter's execve.\n *\n * On macOS/other: /proc does not exist — fall back to the validated path.\n * Residual TOCTOU on macOS is documented and accepted.\n *\n * Extracted from main() for testability.\n *\n * @param platform - process.platform value\n * @param executable - resolved binary path (from findCodePuppy)\n * @param binaryFd - open fd to the binary (from findCodePuppy)\n */\nexport function buildSpawnConfig(\n platform: string,\n executable: string,\n binaryFd: number\n): { spawnTarget: string; stdioConfig: import('child_process').StdioOptions } {\n const PINNED_CHILD_FD = 3;\n\n if (platform === 'linux') {\n return {\n spawnTarget: `/proc/self/fd/${PINNED_CHILD_FD}`,\n stdioConfig: ['inherit', 'inherit', 'inherit', binaryFd],\n };\n }\n\n return {\n spawnTarget: executable,\n stdioConfig: 'inherit',\n };\n}\n","/**\n * @stackwright-pro/raft — Dev-server lifecycle bracket\n *\n * Spawns `pnpm dev` in a project, probes until HTTP is ready, invokes a\n * callback body, then kills the server cleanly (SIGTERM → 5s grace → SIGKILL).\n *\n * swp-sc4x: Bead F — lifecycle module. QA otter invocation is Bead D (swp-wde8).\n */\n\nimport { spawn } from 'child_process';\nimport type { ChildProcess } from 'child_process';\nimport {\n existsSync,\n mkdirSync,\n openSync,\n closeSync,\n writeFileSync,\n unlinkSync,\n readFileSync,\n} from 'fs';\nimport { join } from 'path';\n\n// ─── Public Interfaces ───────────────────────────────────────────────────────\n\nexport interface DevServerInfo {\n pid: number;\n baseUrl: string;\n prismUrl?: string;\n startedAt: number;\n logPath: string;\n}\n\nexport interface DevServerOptions {\n /** Total budget to wait for the server to be ready. Default: 60000ms */\n timeoutMs?: number;\n /** Base URL to probe. Default: http://localhost:3000 */\n baseUrl?: string;\n /** Explicit prism URL override (skips auto-detection). Default: http://localhost:4010 */\n prismUrl?: string;\n /** Detect prism from package.json scripts.dev. Default: true */\n detectPrism?: boolean;\n}\n\nexport interface PrismConfig {\n hasPrism: boolean;\n prismUrl: string;\n}\n\nexport class DevServerStartupError extends Error {\n constructor(\n message: string,\n public readonly logTail: string[],\n public readonly elapsedMs: number\n ) {\n super(message);\n this.name = 'DevServerStartupError';\n }\n}\n\n// ─── Pure Utilities ──────────────────────────────────────────────────────────\n\n/**\n * Inspect a project's `package.json` dev script to determine if Prism is in use.\n * Pure function — exported for independent testability.\n */\nexport function detectPrismConfig(\n projectRoot: string,\n defaultPrismUrl = 'http://localhost:4010'\n): PrismConfig {\n try {\n const pkgPath = join(projectRoot, 'package.json');\n const raw = readFileSync(pkgPath, 'utf-8');\n const pkg = JSON.parse(raw) as Record<string, unknown>;\n const scripts = pkg['scripts'] as Record<string, unknown> | undefined;\n const devScript = typeof scripts?.['dev'] === 'string' ? scripts['dev'] : '';\n const hasConcurrently = devScript.includes('concurrently');\n const hasPrism = /prism/i.test(devScript);\n return { hasPrism: hasConcurrently && hasPrism, prismUrl: defaultPrismUrl };\n } catch {\n return { hasPrism: false, prismUrl: defaultPrismUrl };\n }\n}\n\n/** Read the last N non-empty lines from a file (best-effort). */\nfunction tailLog(logPath: string, maxLines: number): string[] {\n try {\n const content = readFileSync(logPath, 'utf-8');\n const lines = content.split('\\n').filter((l) => l.length > 0);\n return lines.slice(-maxLines);\n } catch {\n return [];\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n// ─── HTTP Probing ────────────────────────────────────────────────────────────\n\n/**\n * Attempt a single fetch with per-request timeout.\n * Returns true on 2xx, false on any error/timeout. Never throws.\n */\nasync function singleFetch(url: string, timeoutMs: number): Promise<boolean> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n try {\n const res = await globalThis.fetch(url, { signal: controller.signal });\n clearTimeout(timer);\n return res.status >= 200 && res.status < 300;\n } catch {\n clearTimeout(timer);\n return false;\n }\n}\n\n/**\n * Poll a set of URLs every `pollIntervalMs` until ALL respond 2xx or `timeoutMs` elapses.\n * Returns true if all became ready within budget, false on timeout.\n */\nasync function pollUntilAllReady(\n urls: string[],\n timeoutMs: number,\n pollIntervalMs: number,\n perRequestTimeoutMs: number\n): Promise<boolean> {\n const deadline = Date.now() + timeoutMs;\n\n while (Date.now() < deadline) {\n const remaining = deadline - Date.now();\n const perReq = Math.min(perRequestTimeoutMs, remaining);\n const results = await Promise.all(urls.map((url) => singleFetch(url, perReq)));\n if (results.every(Boolean)) return true;\n\n const sleepMs = Math.min(pollIntervalMs, deadline - Date.now());\n if (sleepMs <= 0) break;\n await sleep(sleepMs);\n }\n\n return false;\n}\n\n/**\n * Probe a URL until it responds 2xx or times out.\n * Exported for tests and for callers that need readiness checks without managing lifecycle.\n */\nexport async function probeUrl(\n url: string,\n options: { timeoutMs: number; pollIntervalMs?: number; perRequestTimeoutMs?: number }\n): Promise<boolean> {\n const { timeoutMs, pollIntervalMs = 2000, perRequestTimeoutMs = 1000 } = options;\n return pollUntilAllReady([url], timeoutMs, pollIntervalMs, perRequestTimeoutMs);\n}\n\n// ─── Process Cleanup ─────────────────────────────────────────────────────────\n\nasync function killChildProcess(child: ChildProcess, pid: number): Promise<void> {\n return new Promise((resolve) => {\n // Race: exit event vs 5s SIGKILL escalation\n let settled = false;\n const settle = (): void => {\n if (!settled) {\n settled = true;\n clearTimeout(escalateTimer);\n resolve();\n }\n };\n\n child.once('exit', settle);\n\n const escalateTimer = setTimeout(() => {\n try {\n process.kill(pid, 'SIGKILL');\n } catch {\n // Process already gone\n }\n settle();\n }, 5_000);\n\n try {\n process.kill(pid, 'SIGTERM');\n } catch {\n // Process already gone — still resolve\n settle();\n }\n });\n}\n\n// ─── Dev Server Lifecycle ────────────────────────────────────────────────────\n\n/**\n * Spawn `pnpm dev` in the project, wait for it to be ready, invoke `body`,\n * then ALWAYS kill the server cleanly — even if `body` throws.\n *\n * Writes `.stackwright/dev-server-ready.json` on success.\n * Throws `DevServerStartupError` on timeout/failure with log tail.\n *\n * Critical (swp-e4y6 family): uses spawn (not exec) for pid, no zombie leaks.\n */\nexport async function withDevServer<T>(\n projectRoot: string,\n options: DevServerOptions,\n body: (info: DevServerInfo) => Promise<T>\n): Promise<T> {\n const {\n timeoutMs = 60_000,\n baseUrl: baseUrlOpt,\n prismUrl: prismUrlOpt,\n detectPrism: shouldDetectPrism = true,\n } = options;\n\n const baseUrl = baseUrlOpt ?? 'http://localhost:3000';\n\n // Prism detection: explicit override > auto-detect > disabled\n let prismUrl: string | undefined;\n if (prismUrlOpt !== undefined) {\n prismUrl = prismUrlOpt;\n } else if (shouldDetectPrism) {\n const cfg = detectPrismConfig(projectRoot);\n prismUrl = cfg.hasPrism ? cfg.prismUrl : undefined;\n }\n\n // Set up log file — truncate per run, no append\n const stackwrightDir = join(projectRoot, '.stackwright');\n mkdirSync(stackwrightDir, { recursive: true });\n const logPath = join(stackwrightDir, 'dev-server.log');\n const logFd = openSync(logPath, 'w');\n\n const readyFilePath = join(stackwrightDir, 'dev-server-ready.json');\n const startedAt = Date.now();\n\n let child: ChildProcess | undefined;\n let pid: number | undefined;\n let spawnErr: Error | undefined;\n\n try {\n // nosemgrep: javascript.lang.security.detect-child-process\n child = spawn('pnpm', ['dev'], {\n cwd: projectRoot,\n detached: false,\n stdio: ['ignore', logFd, logFd],\n });\n\n pid = child.pid;\n child.on('error', (err) => {\n spawnErr = err;\n });\n\n if (pid === undefined) {\n throw new DevServerStartupError(\n 'pnpm dev failed to spawn (no PID)',\n tailLog(logPath, 50),\n Date.now() - startedAt\n );\n }\n\n // Probe all required URLs\n const urlsToProbe = [baseUrl, ...(prismUrl ? [prismUrl] : [])];\n const ready = await pollUntilAllReady(urlsToProbe, timeoutMs, 2_000, 1_000);\n\n if (!ready) {\n const elapsedMs = Date.now() - startedAt;\n const tail = tailLog(logPath, 50);\n const spawnErrMsg = spawnErr ? ` Spawn error: ${spawnErr.message}.` : '';\n throw new DevServerStartupError(\n `Dev server did not become ready within ${timeoutMs}ms.${spawnErrMsg}`,\n tail,\n elapsedMs\n );\n }\n\n const info: DevServerInfo = {\n pid,\n baseUrl,\n ...(prismUrl ? { prismUrl } : {}),\n startedAt,\n logPath,\n };\n\n // Write ready file — Bead D / E consumers can read this for reference\n writeFileSync(\n readyFilePath,\n JSON.stringify({ ...info, readyAt: new Date().toISOString() }, null, 2) + '\\n'\n );\n\n return await body(info);\n } finally {\n // Cleanup always runs — even if probe threw or body threw\n if (child && pid !== undefined) {\n await killChildProcess(child, pid);\n }\n\n // Remove ready file (best-effort — ignore ENOENT)\n try {\n unlinkSync(readyFilePath);\n } catch {\n /* best-effort */\n }\n\n // Close log fd (best-effort)\n try {\n closeSync(logFd);\n } catch {\n /* best-effort */\n }\n }\n}\n"],"mappings":"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","names":["import_fs","import_path","import_child_process","platform","import_child_process","import_fs","import_path","resolve","resolve"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stackwright-pro/raft",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.124",
|
|
4
4
|
"description": "Launch the Pro Otter Raft — verifies integrity, writes init context, spawns code-puppy in foreman mode",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"repository": {
|
|
@@ -22,10 +22,10 @@
|
|
|
22
22
|
"tag": "alpha"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@stackwright-pro/mcp": "0.2.0-alpha.
|
|
25
|
+
"@stackwright-pro/mcp": "0.2.0-alpha.103"
|
|
26
26
|
},
|
|
27
27
|
"peerDependencies": {
|
|
28
|
-
"@stackwright-pro/otters": ">=1.0.0-alpha.
|
|
28
|
+
"@stackwright-pro/otters": ">=1.0.0-alpha.70"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@types/node": "^25.9.2",
|