@stackwright-pro/raft 0.1.0-alpha.1

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 ADDED
@@ -0,0 +1,403 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ // src/index.ts
5
+ var import_fs2 = require("fs");
6
+ var import_path2 = require("path");
7
+ var import_child_process2 = require("child_process");
8
+ var import_integrity = require("@stackwright-pro/mcp/integrity");
9
+
10
+ // src/lib.ts
11
+ var import_child_process = require("child_process");
12
+ var import_fs = require("fs");
13
+ var import_path = require("path");
14
+ var import_os = require("os");
15
+ function parseArgs(argv) {
16
+ const args = {
17
+ projectRoot: process.cwd(),
18
+ mcpConfig: null,
19
+ verbose: false,
20
+ help: false
21
+ };
22
+ const raw = argv.slice(2);
23
+ for (let i = 0; i < raw.length; i++) {
24
+ const token = raw[i];
25
+ switch (token) {
26
+ case "--project-root":
27
+ args.projectRoot = raw[++i] ?? die("--project-root requires a path argument");
28
+ break;
29
+ case "--mcp-config":
30
+ args.mcpConfig = raw[++i] ?? die("--mcp-config requires a path argument");
31
+ break;
32
+ case "--verbose":
33
+ args.verbose = true;
34
+ break;
35
+ case "--help":
36
+ case "-h":
37
+ args.help = true;
38
+ break;
39
+ default:
40
+ die(`Unknown option: ${token}
41
+ Run with --help for usage.`);
42
+ }
43
+ }
44
+ return args;
45
+ }
46
+ function printHelp() {
47
+ console.log(
48
+ `
49
+ \u{1F9A6} launch-raft \u2014 Spawn code-puppy in foreman mode
50
+
51
+ Usage: launch-raft [options]
52
+
53
+ Options:
54
+ --project-root <path> Project root directory (default: cwd)
55
+ --mcp-config <path> Path to MCP configuration file
56
+ --verbose Enable verbose logging
57
+ --help, -h Show this help
58
+ `.trim()
59
+ );
60
+ }
61
+ function die(message) {
62
+ console.error(`\u274C ${message}`);
63
+ process.exit(1);
64
+ }
65
+ function log(message) {
66
+ console.log(`\u{1F9A6} ${message}`);
67
+ }
68
+ function verbose(message, isVerbose) {
69
+ if (isVerbose) {
70
+ console.log(` ${message}`);
71
+ }
72
+ }
73
+ var LOCK_FILE = ".stackwright/.lock";
74
+ function acquireLock(projectRoot) {
75
+ const lockPath = (0, import_path.join)(projectRoot, LOCK_FILE);
76
+ if ((0, import_fs.existsSync)(lockPath) && (0, import_fs.lstatSync)(lockPath).isSymbolicLink()) {
77
+ die(".stackwright/.lock is a symlink \u2014 refusing to acquire lock. Check for tampering.");
78
+ }
79
+ const pid = process.pid;
80
+ const host = (0, import_os.hostname)();
81
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
82
+ const lockContent = JSON.stringify({
83
+ pid,
84
+ hostname: host,
85
+ acquiredAt: timestamp,
86
+ version: "1.0"
87
+ });
88
+ (0, import_fs.mkdirSync)((0, import_path.join)(projectRoot, ".stackwright"), { recursive: true });
89
+ try {
90
+ (0, import_fs.writeFileSync)(lockPath, lockContent, { flag: "wx" });
91
+ return true;
92
+ } catch (err) {
93
+ if (err instanceof Error && "code" in err && err.code === "EEXIST") {
94
+ try {
95
+ const existing = JSON.parse((0, import_fs.readFileSync)(lockPath, "utf8"));
96
+ const oldPid = existing["pid"];
97
+ try {
98
+ process.kill(oldPid, 0);
99
+ return false;
100
+ } catch {
101
+ (0, import_fs.writeFileSync)(lockPath, lockContent, "utf-8");
102
+ return true;
103
+ }
104
+ } catch {
105
+ (0, import_fs.writeFileSync)(lockPath, lockContent, "utf-8");
106
+ return true;
107
+ }
108
+ }
109
+ throw err;
110
+ }
111
+ }
112
+ function releaseLock(projectRoot) {
113
+ const lockPath = (0, import_path.join)(projectRoot, LOCK_FILE);
114
+ try {
115
+ (0, import_fs.rmSync)(lockPath);
116
+ } catch {
117
+ }
118
+ }
119
+ function writeInitContext(projectRoot) {
120
+ if (!acquireLock(projectRoot)) {
121
+ let existingPid = "unknown";
122
+ try {
123
+ const lockPath = (0, import_path.join)(projectRoot, LOCK_FILE);
124
+ if ((0, import_fs.existsSync)(lockPath)) {
125
+ const lockData = JSON.parse((0, import_fs.readFileSync)(lockPath, "utf8"));
126
+ existingPid = lockData["pid"] ?? "unknown";
127
+ }
128
+ } catch {
129
+ }
130
+ die(
131
+ `Pipeline lock already held by PID ${existingPid}. Another launch-raft process is running.`
132
+ );
133
+ }
134
+ const stackwrightDir = (0, import_path.join)(projectRoot, ".stackwright");
135
+ const initContextPath = (0, import_path.join)(stackwrightDir, "init-context.json");
136
+ const MAX_INIT_CONTEXT_BYTES = 1 * 1024 * 1024;
137
+ let existing = {};
138
+ try {
139
+ const raw = (0, import_fs.readFileSync)(initContextPath, "utf-8");
140
+ if (raw.length > MAX_INIT_CONTEXT_BYTES) {
141
+ die(
142
+ `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.`
143
+ );
144
+ }
145
+ existing = JSON.parse(raw);
146
+ } catch {
147
+ }
148
+ existing["projectRoot"] = projectRoot;
149
+ if (!existing["projectName"]) {
150
+ try {
151
+ const pkgRaw = (0, import_fs.readFileSync)((0, import_path.join)(projectRoot, "package.json"), "utf-8");
152
+ const pkg = JSON.parse(pkgRaw);
153
+ if (typeof pkg["name"] === "string") {
154
+ existing["projectName"] = pkg["name"];
155
+ }
156
+ } catch {
157
+ }
158
+ }
159
+ if (!existing["specPath"]) {
160
+ try {
161
+ const specsDir = (0, import_path.join)(projectRoot, "specs");
162
+ if ((0, import_fs.existsSync)(specsDir)) {
163
+ const files = (0, import_fs.readdirSync)(specsDir);
164
+ const first = files[0];
165
+ if (first) {
166
+ existing["specPath"] = (0, import_path.join)("specs", first);
167
+ }
168
+ }
169
+ } catch {
170
+ }
171
+ }
172
+ if (!existing["theme"]) {
173
+ try {
174
+ const ymlPath = (0, import_path.join)(projectRoot, "stackwright.yml");
175
+ const ymlContent = (0, import_fs.readFileSync)(ymlPath, "utf-8");
176
+ const match = /theme:\s*\n\s+id:\s*(.+)/.exec(ymlContent);
177
+ if (match?.[1]) {
178
+ existing["theme"] = match[1].trim();
179
+ }
180
+ } catch {
181
+ }
182
+ }
183
+ existing["generatedBy"] = "launch-raft";
184
+ existing["version"] = "1.0";
185
+ (0, import_fs.mkdirSync)(stackwrightDir, { recursive: true });
186
+ if ((0, import_fs.existsSync)(stackwrightDir) && (0, import_fs.lstatSync)(stackwrightDir).isSymbolicLink()) {
187
+ die(".stackwright is a symlink \u2014 refusing to write. Check for tampering.");
188
+ }
189
+ if ((0, import_fs.existsSync)(initContextPath) && (0, import_fs.lstatSync)(initContextPath).isSymbolicLink()) {
190
+ die("init-context.json is a symlink \u2014 refusing to write. Check for tampering.");
191
+ }
192
+ (0, import_fs.writeFileSync)(initContextPath, JSON.stringify(existing, null, 2), "utf-8");
193
+ }
194
+ process.on("exit", () => {
195
+ try {
196
+ releaseLock(process.cwd());
197
+ } catch {
198
+ }
199
+ });
200
+ process.on("SIGINT", () => {
201
+ try {
202
+ releaseLock(process.cwd());
203
+ } catch {
204
+ }
205
+ process.exit(0);
206
+ });
207
+ process.on("SIGTERM", () => {
208
+ try {
209
+ releaseLock(process.cwd());
210
+ } catch {
211
+ }
212
+ process.exit(0);
213
+ });
214
+ function findCodePuppy() {
215
+ let candidate = null;
216
+ const envPath = process.env["STACKWRIGHT_CODE_PUPPY_PATH"];
217
+ if (envPath) {
218
+ candidate = envPath;
219
+ }
220
+ if (!candidate) {
221
+ try {
222
+ candidate = (0, import_child_process.execSync)("which code-puppy", { encoding: "utf-8" }).trim();
223
+ } catch {
224
+ }
225
+ }
226
+ if (!candidate) {
227
+ die(
228
+ "code-puppy not found. Install with:\n pip install code-puppy\n or: uvx code-puppy\nOr set STACKWRIGHT_CODE_PUPPY_PATH."
229
+ );
230
+ }
231
+ const resolved = (0, import_path.resolve)(candidate);
232
+ if (!(0, import_fs.existsSync)(resolved)) {
233
+ die(`code-puppy not found at resolved path: ${resolved}`);
234
+ }
235
+ const stat = (0, import_fs.lstatSync)(resolved);
236
+ if (stat.isSymbolicLink()) {
237
+ die(
238
+ `code-puppy at ${resolved} is a symlink \u2014 refusing to exec. Use an absolute path to the real binary.`
239
+ );
240
+ }
241
+ if (stat.mode & 18) {
242
+ die(
243
+ `code-puppy at ${resolved} is group- or world-writable (mode: ${(stat.mode & 511).toString(8)}) \u2014 refusing to exec.`
244
+ );
245
+ }
246
+ if (stat.mode & 3072) {
247
+ die(`code-puppy at ${resolved} has setuid/setgid bits \u2014 refusing to exec.`);
248
+ }
249
+ return resolved;
250
+ }
251
+ function findMcpConfig(projectRoot, explicit) {
252
+ if (explicit) {
253
+ const resolved = (0, import_path.resolve)(explicit);
254
+ if (!(0, import_fs.existsSync)(resolved)) {
255
+ die(`MCP config not found: ${resolved}`);
256
+ }
257
+ const configStat = (0, import_fs.lstatSync)(resolved);
258
+ if (configStat.isSymbolicLink()) {
259
+ die(`MCP config is a symlink \u2014 refusing to load: ${resolved}`);
260
+ }
261
+ if (configStat.size > 1024 * 1024) {
262
+ die(`MCP config exceeds 1MB \u2014 suspicious: ${resolved}`);
263
+ }
264
+ try {
265
+ JSON.parse((0, import_fs.readFileSync)(resolved, "utf-8"));
266
+ } catch {
267
+ die(`MCP config is not valid JSON: ${resolved}`);
268
+ }
269
+ return resolved;
270
+ }
271
+ const projectLocal = (0, import_path.join)(projectRoot, ".code_puppy", "mcp.json");
272
+ if ((0, import_fs.existsSync)(projectLocal) && !(0, import_fs.lstatSync)(projectLocal).isSymbolicLink()) {
273
+ return projectLocal;
274
+ }
275
+ const userLevel = (0, import_path.join)((0, import_os.homedir)(), ".code_puppy", "mcp.json");
276
+ if ((0, import_fs.existsSync)(userLevel) && !(0, import_fs.lstatSync)(userLevel).isSymbolicLink()) {
277
+ return userLevel;
278
+ }
279
+ return null;
280
+ }
281
+ function printResumeStatus(projectRoot) {
282
+ const pipelineStatePath = (0, import_path.join)(projectRoot, ".stackwright", "pipeline-state.json");
283
+ try {
284
+ const raw = (0, import_fs.readFileSync)(pipelineStatePath, "utf-8");
285
+ const state = JSON.parse(raw);
286
+ const status = state["status"];
287
+ const phases = state["phases"];
288
+ if (typeof phases !== "object" || phases === null || Array.isArray(phases)) {
289
+ return;
290
+ }
291
+ const phaseEntries = Object.values(phases);
292
+ const totalPhases = phaseEntries.length || 8;
293
+ switch (status) {
294
+ case "setup":
295
+ log("\u{1F4CD} Starting fresh");
296
+ break;
297
+ case "questions": {
298
+ const answered = phaseEntries.filter((p) => p["answered"] === true).length;
299
+ log(`\u{1F4CD} Resuming: questions phase (${answered}/${totalPhases} phases answered)`);
300
+ break;
301
+ }
302
+ case "execution": {
303
+ const executed = phaseEntries.filter((p) => p["executed"] === true).length;
304
+ log(`\u{1F4CD} Resuming: execution phase (${executed}/${totalPhases} phases complete)`);
305
+ break;
306
+ }
307
+ case "done":
308
+ log("\u{1F4CD} Pipeline complete \u2014 re-entering to review");
309
+ break;
310
+ default:
311
+ break;
312
+ }
313
+ } catch {
314
+ }
315
+ }
316
+ function resolveOtterDir(projectRoot) {
317
+ const candidates = [
318
+ (0, import_path.join)(projectRoot, "node_modules", "@stackwright-pro", "otters", "src"),
319
+ (0, import_path.join)(projectRoot, "packages", "otters", "src")
320
+ ];
321
+ for (const candidate of candidates) {
322
+ if ((0, import_fs.existsSync)(candidate)) {
323
+ return candidate;
324
+ }
325
+ }
326
+ return null;
327
+ }
328
+
329
+ // src/index.ts
330
+ function main() {
331
+ const args = parseArgs(process.argv);
332
+ if (args.help) {
333
+ printHelp();
334
+ process.exit(0);
335
+ }
336
+ const projectRoot = (0, import_path2.resolve)(args.projectRoot);
337
+ if (!(0, import_fs2.existsSync)(projectRoot)) {
338
+ die(`Project root does not exist: ${projectRoot}`);
339
+ }
340
+ if (!(0, import_fs2.existsSync)((0, import_path2.join)(projectRoot, "package.json"))) {
341
+ die("No package.json found. Run npx @stackwright-pro/launch-stackwright-pro first.");
342
+ }
343
+ log("Launching Pro Otter Raft...");
344
+ writeInitContext(projectRoot);
345
+ verbose("Init context written", args.verbose);
346
+ const otterDir = resolveOtterDir(projectRoot);
347
+ if (!otterDir) {
348
+ die(
349
+ "Could not find otter directory. Is @stackwright-pro/otters installed?\n Run: pnpm add @stackwright-pro/otters"
350
+ );
351
+ }
352
+ const result = (0, import_integrity.verifyAllOtters)(otterDir);
353
+ if (result.failed.length > 0) {
354
+ console.error("\u274C Otter integrity check failed:");
355
+ for (const f of result.failed) {
356
+ console.error(` ${f.filename}: ${f.error}`);
357
+ }
358
+ die("Fix the above integrity failures before launching the raft.");
359
+ }
360
+ log(`\u2705 All ${result.verified.length} otters verified`);
361
+ printResumeStatus(projectRoot);
362
+ const executable = findCodePuppy();
363
+ verbose(`Resolved code-puppy: ${executable}`, args.verbose);
364
+ const mcpConfig = findMcpConfig(projectRoot, args.mcpConfig ?? void 0);
365
+ if (mcpConfig) {
366
+ verbose(`Using MCP config: ${mcpConfig}`, args.verbose);
367
+ }
368
+ log("Spawning code-puppy raft session...");
369
+ const spawnArgs = [
370
+ "Begin",
371
+ "--interactive",
372
+ "--agent",
373
+ "stackwright-pro-foreman-otter",
374
+ ...mcpConfig ? ["--mcp-config", mcpConfig] : []
375
+ ];
376
+ verbose(`cmd: ${executable} ${spawnArgs.join(" ")}`, args.verbose);
377
+ const child = (0, import_child_process2.spawn)(executable, spawnArgs, {
378
+ stdio: "inherit",
379
+ env: {
380
+ ...process.env,
381
+ STACKWRIGHT_PROJECT_ROOT: projectRoot
382
+ }
383
+ });
384
+ const forward = (signal) => {
385
+ if (child.pid) child.kill(signal);
386
+ };
387
+ const onSigint = () => forward("SIGINT");
388
+ const onSigterm = () => forward("SIGTERM");
389
+ process.on("SIGINT", onSigint);
390
+ process.on("SIGTERM", onSigterm);
391
+ child.on("error", (err) => die(`Failed to spawn code-puppy: ${err.message}`));
392
+ child.on("close", (code, signal) => {
393
+ process.off("SIGINT", onSigint);
394
+ process.off("SIGTERM", onSigterm);
395
+ if (signal) {
396
+ process.kill(process.pid, signal);
397
+ } else {
398
+ process.exit(code ?? 1);
399
+ }
400
+ });
401
+ }
402
+ main();
403
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/lib.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 } from 'fs';\nimport { join, resolve } from 'path';\nimport { spawn } from 'child_process';\nimport { verifyAllOtters } from '@stackwright-pro/mcp/integrity';\nimport {\n parseArgs,\n printHelp,\n writeInitContext,\n findCodePuppy,\n findMcpConfig,\n printResumeStatus,\n resolveOtterDir,\n die,\n log,\n verbose,\n} from './lib.js';\n\n// ─── Main ────────────────────────────────────────────────────────────────────\n\nfunction main(): 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 // 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.error('❌ Otter integrity check failed:');\n for (const f of result.failed) {\n console.error(` ${f.filename}: ${f.error}`);\n }\n die('Fix the above integrity failures before launching the raft.');\n }\n log(`✅ All ${result.verified.length} otters verified`);\n\n // 3. Print resume status\n printResumeStatus(projectRoot);\n\n // 4. Resolve code-puppy\n const executable = findCodePuppy();\n verbose(`Resolved code-puppy: ${executable}`, args.verbose);\n\n // 5. Find MCP config\n const mcpConfig = findMcpConfig(projectRoot, args.mcpConfig ?? undefined);\n if (mcpConfig) {\n verbose(`Using MCP config: ${mcpConfig}`, args.verbose);\n }\n\n // 6. Spawn code-puppy\n log('Spawning code-puppy raft session...');\n\n const spawnArgs = [\n 'Begin',\n '--interactive',\n '--agent',\n 'stackwright-pro-foreman-otter',\n ...(mcpConfig ? ['--mcp-config', mcpConfig] : []),\n ];\n\n verbose(`cmd: ${executable} ${spawnArgs.join(' ')}`, args.verbose);\n\n const child = spawn(executable, spawnArgs, {\n stdio: 'inherit',\n env: {\n ...process.env,\n STACKWRIGHT_PROJECT_ROOT: projectRoot,\n },\n });\n\n // Forward signals to child — let it clean up gracefully\n const forward = (signal: NodeJS.Signals) => {\n if (child.pid) child.kill(signal);\n };\n const onSigint = () => forward('SIGINT');\n const onSigterm = () => forward('SIGTERM');\n process.on('SIGINT', onSigint);\n process.on('SIGTERM', onSigterm);\n\n child.on('error', (err) => die(`Failed to spawn code-puppy: ${err.message}`));\n child.on('close', (code, signal) => {\n process.off('SIGINT', onSigint);\n process.off('SIGTERM', onSigterm);\n if (signal) {\n process.kill(process.pid, signal);\n } else {\n process.exit(code ?? 1);\n }\n });\n}\n\nmain();\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\nimport { execSync } from 'child_process';\nimport {\n existsSync,\n lstatSync,\n mkdirSync,\n readFileSync,\n readdirSync,\n writeFileSync,\n rmSync,\n} from 'fs';\nimport { join, resolve } from 'path';\nimport { homedir, hostname } from 'os';\n\n// ─── CLI Argument Parsing ────────────────────────────────────────────────────\n\nexport interface ParsedArgs {\n projectRoot: string;\n mcpConfig: string | null;\n verbose: boolean;\n help: boolean;\n}\n\nexport function parseArgs(argv: string[]): ParsedArgs {\n const args: ParsedArgs = {\n projectRoot: process.cwd(),\n mcpConfig: null,\n verbose: false,\n help: 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 '--mcp-config':\n args.mcpConfig = raw[++i] ?? die('--mcp-config 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 default:\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🦦 launch-raft — Spawn code-puppy in foreman mode\n\nUsage: launch-raft [options]\n\nOptions:\n --project-root <path> Project root directory (default: cwd)\n --mcp-config <path> Path to MCP configuration file\n --verbose Enable verbose logging\n --help, -h Show this help\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// ─── 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// ─── 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 existingPid = lockData['pid'] ?? '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// ─── Find code-puppy Executable ─────────────────────────────────────────────\n\nexport function findCodePuppy(): string {\n let candidate: string | null = null;\n\n // 1. Explicit env var override\n const envPath = process.env['STACKWRIGHT_CODE_PUPPY_PATH'];\n if (envPath) {\n candidate = envPath;\n }\n\n // 2. PATH lookup via `which`\n if (!candidate) {\n try {\n candidate = execSync('which code-puppy', { encoding: 'utf-8' }).trim();\n } catch {\n // Not on PATH\n }\n }\n\n if (!candidate) {\n die(\n 'code-puppy not found. Install with:\\n' +\n ' pip install code-puppy\\n' +\n ' or: uvx code-puppy\\n' +\n 'Or set STACKWRIGHT_CODE_PUPPY_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(`code-puppy not found at resolved path: ${resolved}`);\n }\n\n // Security checks — consistent with integrity.ts patterns\n const stat = lstatSync(resolved);\n\n // Symlink guard — refuse to follow symlinks (prevents symlink-based binary swaps)\n if (stat.isSymbolicLink()) {\n die(\n `code-puppy at ${resolved} is a symlink — refusing to exec. Use an absolute path to the real binary.`\n );\n }\n\n // Refuse world-writable or group-writable binaries\n\n if (stat.mode & 0o022) {\n die(\n `code-puppy at ${resolved} is group- or world-writable (mode: ${(stat.mode & 0o777).toString(8)}) — refusing to exec.`\n );\n }\n\n // Refuse setuid/setgid binaries\n\n if (stat.mode & 0o6000) {\n die(`code-puppy at ${resolved} has setuid/setgid bits — refusing to exec.`);\n }\n\n return resolved;\n}\n\n// ─── Find MCP Config ────────────────────────────────────────────────────────\n\nexport function findMcpConfig(projectRoot: string, explicit?: string): string | null {\n // 1. Explicit path — validate it\n if (explicit) {\n const resolved = resolve(explicit);\n if (!existsSync(resolved)) {\n die(`MCP config not found: ${resolved}`);\n }\n const configStat = lstatSync(resolved);\n if (configStat.isSymbolicLink()) {\n die(`MCP config is a symlink — refusing to load: ${resolved}`);\n }\n if (configStat.size > 1024 * 1024) {\n die(`MCP config exceeds 1MB — suspicious: ${resolved}`);\n }\n try {\n JSON.parse(readFileSync(resolved, 'utf-8'));\n } catch {\n die(`MCP config is not valid JSON: ${resolved}`);\n }\n return resolved;\n }\n\n // 2. Project-local config\n const projectLocal = join(projectRoot, '.code_puppy', 'mcp.json');\n if (existsSync(projectLocal) && !lstatSync(projectLocal).isSymbolicLink()) {\n return projectLocal;\n }\n\n // 3. User-level config\n const userLevel = join(homedir(), '.code_puppy', 'mcp.json');\n if (existsSync(userLevel) && !lstatSync(userLevel).isSymbolicLink()) {\n return userLevel;\n }\n\n // 4. Let code-puppy discover on its own\n return null;\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// ─── 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"],"mappings":"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","names":["import_fs","import_path","import_child_process"]}
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@stackwright-pro/raft",
3
+ "version": "0.1.0-alpha.1",
4
+ "description": "Launch the Pro Otter Raft — verifies integrity, writes init context, spawns code-puppy in foreman mode",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/Per-Aspera-LLC/stackwright-pro"
9
+ },
10
+ "bin": {
11
+ "launch-raft": "dist/index.js"
12
+ },
13
+ "files": [
14
+ "dist"
15
+ ],
16
+ "publishConfig": {
17
+ "access": "public"
18
+ },
19
+ "dependencies": {
20
+ "@stackwright-pro/mcp": "0.2.0-alpha.5"
21
+ },
22
+ "peerDependencies": {
23
+ "@stackwright-pro/otters": ">=1.0.0-alpha.14"
24
+ },
25
+ "devDependencies": {
26
+ "@types/node": "^24.0.0",
27
+ "typescript": "^5.0",
28
+ "tsup": "^8.5",
29
+ "vitest": "^4.0.18"
30
+ },
31
+ "scripts": {
32
+ "build": "tsup",
33
+ "dev": "tsup --watch",
34
+ "test": "vitest run"
35
+ }
36
+ }