@stackwright-pro/launch-stackwright-pro 0.4.0-alpha.1 → 0.4.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -2
- package/dist/index.js +50 -21
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -49,8 +49,9 @@ my-app/
|
|
|
49
49
|
│ └── prebuild.js # Reads stackwright.yml → runs OpenAPI plugin
|
|
50
50
|
├── specs/ # Only if --spec was provided
|
|
51
51
|
│ └── <your-spec>.yaml
|
|
52
|
-
├──
|
|
53
|
-
│
|
|
52
|
+
├── node_modules/
|
|
53
|
+
│ ├── @stackwright/otters/ # 🦦 OSS otters (brand, theme, page, foreman)
|
|
54
|
+
│ └── @stackwright-pro/otters/ # 🦦 Pro otters (api, data, dashboard, foreman)
|
|
54
55
|
├── stackwright.yml # Theme + auth + integrations config
|
|
55
56
|
├── next.config.js # Pro config (transpile pro pkgs + yaml-loader)
|
|
56
57
|
├── yaml.d.ts # TS declarations for YAML imports
|
|
@@ -114,3 +115,20 @@ automatically via the hooks system:
|
|
|
114
115
|
|
|
115
116
|
See the main [Stackwright Pro documentation](https://github.com/Per-Aspera-LLC/stackwright-pro)
|
|
116
117
|
for architecture details, auth deep-dives, and deployment guides.
|
|
118
|
+
|
|
119
|
+
## Starting the Otter Raft
|
|
120
|
+
|
|
121
|
+
The otter raft is a Python state machine — **not** a code-puppy interactive session. Start it with:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
uvx stackwright-pro-raft
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
For local development (before PyPI publish):
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
pip install -e ./python[tui]
|
|
131
|
+
python -m stackwright_pro.raft.server
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
The raft uses a Rich terminal UI for all user interaction and calls code-puppy as a one-shot subprocess for creative LLM tasks. It reads otter configs from `node_modules/@stackwright-pro/otters/`.
|
package/dist/index.js
CHANGED
|
@@ -31,7 +31,7 @@ var require_package = __commonJS({
|
|
|
31
31
|
"package.json"(exports2, module2) {
|
|
32
32
|
module2.exports = {
|
|
33
33
|
name: "@stackwright-pro/launch-stackwright-pro",
|
|
34
|
-
version: "0.4.0-alpha.
|
|
34
|
+
version: "0.4.0-alpha.1",
|
|
35
35
|
description: "Launch a new Stackwright Pro project with OpenAPI integration, auth, and the otter raft",
|
|
36
36
|
license: "MIT",
|
|
37
37
|
repository: {
|
|
@@ -83,10 +83,13 @@ var require_package = __commonJS({
|
|
|
83
83
|
// src/index.ts
|
|
84
84
|
var import_commander = require("commander");
|
|
85
85
|
var import_path = __toESM(require("path"));
|
|
86
|
+
var import_os = __toESM(require("os"));
|
|
86
87
|
var import_fs_extra = __toESM(require("fs-extra"));
|
|
87
88
|
var import_chalk = __toESM(require("chalk"));
|
|
88
89
|
var import_js_yaml = __toESM(require("js-yaml"));
|
|
90
|
+
var import_child_process = require("child_process");
|
|
89
91
|
var import_cli = require("@stackwright/cli");
|
|
92
|
+
var import_scaffold_core = require("@stackwright/scaffold-core");
|
|
90
93
|
var import_scaffold_hooks = require("@stackwright-pro/scaffold-hooks");
|
|
91
94
|
(0, import_scaffold_hooks.registerProScaffoldHooks)();
|
|
92
95
|
var { version } = require_package();
|
|
@@ -201,26 +204,28 @@ The generated API client is automatically configured to use the mock server.
|
|
|
201
204
|
` : "";
|
|
202
205
|
const aiAgentsSection = hasOtters ? `## \u{1F9A6} AI Agents (The Otter Raft)
|
|
203
206
|
|
|
204
|
-
Your project includes **
|
|
207
|
+
Your project includes **SIX specialized AI agents** in \`node_modules/@stackwright-pro/otters/\`:
|
|
205
208
|
|
|
206
209
|
- \`stackwright-pro-foreman-otter\` - Master coordinator that orchestrates all specialist otters
|
|
207
210
|
- \`stackwright-pro-api-otter\` - Discovers entities from OpenAPI specs
|
|
208
|
-
- \`stackwright-pro-
|
|
211
|
+
- \`stackwright-pro-auth-otter\` - Configures CAC/OIDC auth, RBAC rules, and audit middleware
|
|
212
|
+
- \`stackwright-pro-data-otter\` - Configures endpoint filters & ISR revalidation
|
|
213
|
+
- \`stackwright-pro-page-otter\` - Generates pages auto-wired with data, theme, and auth
|
|
209
214
|
- \`stackwright-pro-dashboard-otter\` - Builds live data dashboards
|
|
210
215
|
|
|
211
|
-
**
|
|
216
|
+
**Start the otter raft:**
|
|
212
217
|
|
|
213
218
|
\`\`\`bash
|
|
214
|
-
uvx
|
|
219
|
+
uvx stackwright-pro-raft
|
|
215
220
|
\`\`\`
|
|
216
221
|
|
|
217
|
-
**
|
|
222
|
+
> **Local dev (before PyPI publish):** \`python -m stackwright_pro.raft.server\`
|
|
218
223
|
|
|
219
|
-
|
|
224
|
+
The raft is a Python state machine with a Rich terminal UI \u2014 it asks questions about your brand, auth, and data preferences, then coordinates specialist otters to build your app. All user interaction happens in the terminal; code-puppy is called as a one-shot subprocess for creative tasks only.
|
|
220
225
|
|
|
221
|
-
|
|
226
|
+
**Example session prompt:**
|
|
222
227
|
|
|
223
|
-
|
|
228
|
+
"Build me a logistics dashboard from our OpenAPI spec. Connect to /equipment and /supplies endpoints."
|
|
224
229
|
|
|
225
230
|
` : "";
|
|
226
231
|
return `# ${projectName}
|
|
@@ -230,10 +235,7 @@ A Stackwright Pro application with role-based auth, OpenAPI integration, and int
|
|
|
230
235
|
## \u{1F680} Quick Start
|
|
231
236
|
|
|
232
237
|
\`\`\`bash
|
|
233
|
-
#
|
|
234
|
-
pnpm install
|
|
235
|
-
|
|
236
|
-
# Start development server (no auth mock)
|
|
238
|
+
# Start development server
|
|
237
239
|
pnpm dev
|
|
238
240
|
|
|
239
241
|
# Open your browser
|
|
@@ -402,13 +404,33 @@ async function launch(targetDir, options) {
|
|
|
402
404
|
console.log(import_chalk.default.cyan.bold("\n\u{1F6A2} Launching Stackwright Pro...\n"));
|
|
403
405
|
const scaffoldOpts = {
|
|
404
406
|
name: options.name || dirName,
|
|
405
|
-
title: options.title,
|
|
406
|
-
theme: options.theme,
|
|
407
|
-
force: options.force,
|
|
408
|
-
noInteractive: options.yes
|
|
407
|
+
...options.title !== void 0 && { title: options.title },
|
|
408
|
+
...options.theme !== void 0 && { theme: options.theme },
|
|
409
|
+
...options.force !== void 0 && { force: options.force },
|
|
410
|
+
...options.yes !== void 0 && { noInteractive: options.yes }
|
|
409
411
|
};
|
|
410
412
|
await (0, import_cli.scaffold)(targetDir, scaffoldOpts);
|
|
411
413
|
console.log(import_chalk.default.green("\u2705 Base project scaffolded (hooks added Pro deps + MCP config)"));
|
|
414
|
+
console.log(import_chalk.default.cyan("\n\u{1F4E6} Installing dependencies (this takes a moment)..."));
|
|
415
|
+
if (!options.skipInstall) {
|
|
416
|
+
try {
|
|
417
|
+
(0, import_child_process.execSync)("pnpm install", { cwd: targetDir, stdio: "inherit", timeout: 12e4 });
|
|
418
|
+
console.log(import_chalk.default.green("\u2705 Dependencies installed"));
|
|
419
|
+
} catch {
|
|
420
|
+
console.warn(import_chalk.default.yellow("\u26A0\uFE0F pnpm install failed \u2014 run it manually: pnpm install"));
|
|
421
|
+
}
|
|
422
|
+
} else {
|
|
423
|
+
console.log(import_chalk.default.dim("\u2139\uFE0F Skipping install (--skip-install). Run: pnpm install"));
|
|
424
|
+
}
|
|
425
|
+
const dependencyMode = "standalone";
|
|
426
|
+
await (0, import_scaffold_core.runScaffoldHooks)("postInstall", {
|
|
427
|
+
targetDir,
|
|
428
|
+
projectName: options.name || import_path.default.basename(targetDir),
|
|
429
|
+
siteTitle: options.title || options.name || import_path.default.basename(targetDir),
|
|
430
|
+
themeId: options.theme || "corporate",
|
|
431
|
+
packageJson: {},
|
|
432
|
+
dependencyMode
|
|
433
|
+
});
|
|
412
434
|
await copyProTemplate("_app.tsx", import_path.default.join(targetDir, "pages", "_app.tsx"));
|
|
413
435
|
await copyProTemplate("content.yml", import_path.default.join(targetDir, "pages", "content.yml"));
|
|
414
436
|
await copyProTemplate("next.config.js", import_path.default.join(targetDir, "next.config.js"));
|
|
@@ -455,8 +477,8 @@ async function launch(targetDir, options) {
|
|
|
455
477
|
const readmeContent = generateReadme({
|
|
456
478
|
projectName: options.name || dirName,
|
|
457
479
|
hasSpec: !!specInfo,
|
|
458
|
-
specName: specInfo
|
|
459
|
-
specFilename: specInfo
|
|
480
|
+
...specInfo?.derivedName !== void 0 && { specName: specInfo.derivedName },
|
|
481
|
+
...specInfo?.specFilename !== void 0 && { specFilename: specInfo.specFilename },
|
|
460
482
|
hasOtters: !options.skipOtters,
|
|
461
483
|
hasMock: !!options.mock
|
|
462
484
|
});
|
|
@@ -465,8 +487,8 @@ async function launch(targetDir, options) {
|
|
|
465
487
|
const relDir = import_path.default.relative(process.cwd(), targetDir) || ".";
|
|
466
488
|
console.log(import_chalk.default.cyan.bold("\n\u{1F389} All set! Here's what to do next:\n"));
|
|
467
489
|
console.log(import_chalk.default.white(` 1. cd ${relDir}`));
|
|
468
|
-
console.log(import_chalk.default.white(" 2.
|
|
469
|
-
console.log(import_chalk.default.
|
|
490
|
+
console.log(import_chalk.default.white(" 2. uvx stackwright-pro-raft"));
|
|
491
|
+
console.log(import_chalk.default.dim(" (local dev: python -m stackwright_pro.raft.server)"));
|
|
470
492
|
if (specInfo) {
|
|
471
493
|
console.log(import_chalk.default.cyan(`
|
|
472
494
|
\u{1F4E1} Your API spec was copied to specs/${specInfo.specFilename}`));
|
|
@@ -489,8 +511,15 @@ async function main() {
|
|
|
489
511
|
).option(
|
|
490
512
|
"--spec-name <name>",
|
|
491
513
|
"Name for the API integration (default: derived from spec filename)"
|
|
514
|
+
).option(
|
|
515
|
+
"--skip-install",
|
|
516
|
+
"Skip automatic pnpm install (useful in CI or when using a different package manager)"
|
|
492
517
|
).action(async (directory, options) => {
|
|
518
|
+
const homeDir = import_os.default.homedir();
|
|
493
519
|
const targetDir = import_path.default.resolve(directory);
|
|
520
|
+
if (!targetDir.startsWith(homeDir + import_path.default.sep) && targetDir !== homeDir) {
|
|
521
|
+
console.warn(import_chalk.default.yellow(`\u26A0\uFE0F Scaffolding outside your home directory: ${targetDir}`));
|
|
522
|
+
}
|
|
494
523
|
await launch(targetDir, options);
|
|
495
524
|
});
|
|
496
525
|
await program.parseAsync(process.argv);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../package.json","../src/index.ts"],"sourcesContent":["{\n \"name\": \"@stackwright-pro/launch-stackwright-pro\",\n \"version\": \"0.4.0-alpha.0\",\n \"description\": \"Launch a new Stackwright Pro project with OpenAPI integration, auth, and the otter raft\",\n \"license\": \"MIT\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/Per-Aspera-LLC/stackwright-pro\"\n },\n \"keywords\": [\n \"stackwright\",\n \"stackwright-pro\",\n \"scaffolding\",\n \"openapi\",\n \"government\",\n \"hackathon\"\n ],\n \"files\": [\n \"dist\",\n \"templates\"\n ],\n \"bin\": {\n \"launch-stackwright-pro\": \"dist/index.js\"\n },\n \"scripts\": {\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"test\": \"vitest run\",\n \"test:coverage\": \"vitest run --coverage\"\n },\n \"dependencies\": {\n \"@stackwright-pro/scaffold-hooks\": \"workspace:*\",\n \"@stackwright/cli\": \"^0.7.0\",\n \"@stackwright/scaffold-core\": \"^0.1.0\",\n \"chalk\": \"^5.6.2\",\n \"commander\": \"^14.0.3\",\n \"fs-extra\": \"^11.3\",\n \"js-yaml\": \"^4.1.0\"\n },\n \"devDependencies\": {\n \"@types/fs-extra\": \"^11.0\",\n \"@types/js-yaml\": \"^4.0.9\",\n \"@types/node\": \"^24.0.0\",\n \"typescript\": \"^5.0\",\n \"tsup\": \"^8.5\",\n \"vitest\": \"^4.0.18\"\n }\n}\n","import { Command } from 'commander';\nimport path from 'path';\nimport fs from 'fs-extra';\nimport chalk from 'chalk';\nimport yaml from 'js-yaml';\n\nimport { scaffold, ScaffoldOptions } from '@stackwright/cli';\nimport { registerProScaffoldHooks } from '@stackwright-pro/scaffold-hooks';\n// Register Pro hooks (adds deps, MCP config, etc.)\n// Must be called before scaffold() to ensure hooks run in the CLI's scaffold-core context\nregisterProScaffoldHooks();\n\nconst { version } = require('../package.json') as { version: string };\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ninterface LaunchProOptions {\n name?: string;\n title?: string;\n theme?: string;\n force?: boolean;\n skipOtters?: boolean;\n yes?: boolean;\n spec?: string;\n specName?: string;\n mock?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Template copying (Pro-specific - hooks can't handle these)\n// ---------------------------------------------------------------------------\n\nasync function copyProTemplate(templateName: string, destPath: string): Promise<void> {\n const src = path.resolve(__dirname, '..', 'templates', 'pro', templateName);\n await fs.copy(src, destPath);\n}\n\n// ---------------------------------------------------------------------------\n// Auth config (Pro-specific - hooks can't handle these)\n// ---------------------------------------------------------------------------\n\nasync function addAuthToStackwrightYml(targetDir: string): Promise<void> {\n const ymlPath = path.join(targetDir, 'stackwright.yml');\n const content = await fs.readFile(ymlPath, 'utf-8');\n const config = yaml.load(content) as Record<string, unknown>;\n\n config.auth = {\n type: 'pki',\n profile: 'dod_cac',\n source: 'gateway_headers',\n roles: [\n { name: 'ADMIN', permissions: ['*'] },\n { name: 'ANALYST', permissions: ['data:read', 'data:write'] },\n { name: 'VIEWER', permissions: ['data:read'] },\n ],\n protected_routes: [{ path: '/*', roles: ['VIEWER', 'ANALYST', 'ADMIN'] }],\n public_routes: ['/', '/getting-started'],\n };\n\n await fs.writeFile(ymlPath, yaml.dump(config, { lineWidth: 120 }));\n}\n\nasync function addIntegrationToStackwrightYml(\n targetDir: string,\n specName: string,\n specFilename: string,\n mockUrl?: string\n): Promise<void> {\n const ymlPath = path.join(targetDir, 'stackwright.yml');\n const content = await fs.readFile(ymlPath, 'utf-8');\n const config = yaml.load(content) as Record<string, unknown>;\n\n const integration: Record<string, unknown> = {\n type: 'openapi',\n name: specName,\n spec: `./specs/${specFilename}`,\n collections: [],\n };\n\n if (mockUrl) {\n integration.mockUrl = mockUrl;\n }\n\n config.integrations = [integration];\n\n await fs.writeFile(ymlPath, yaml.dump(config, { lineWidth: 120 }));\n}\n\n// ---------------------------------------------------------------------------\n// Spec handling (Pro-specific - hooks can't handle these)\n// ---------------------------------------------------------------------------\n\nasync function handleSpec(\n targetDir: string,\n specPath: string,\n specName?: string\n): Promise<{ specFilename: string; derivedName: string }> {\n const resolvedSpec = path.resolve(specPath);\n\n if (!fs.existsSync(resolvedSpec)) {\n throw new Error(`Spec file not found: ${resolvedSpec}`);\n }\n\n const specFilename = path.basename(resolvedSpec);\n const derivedName = specName || path.basename(specFilename, path.extname(specFilename));\n\n const specsDir = path.join(targetDir, 'specs');\n await fs.ensureDir(specsDir);\n await fs.copy(resolvedSpec, path.join(specsDir, specFilename));\n\n return { specFilename, derivedName };\n}\n\n// ---------------------------------------------------------------------------\n// README generator (Pro-specific - hooks can't handle these)\n// ---------------------------------------------------------------------------\n\nfunction generateReadme(options: {\n projectName: string;\n hasSpec: boolean;\n specName?: string;\n specFilename?: string;\n hasOtters: boolean;\n hasMock: boolean;\n}): string {\n const { projectName, hasSpec, specName, specFilename, hasOtters, hasMock } = options;\n\n // Build API Integration section if spec is present\n const apiIntegrationSection = hasSpec\n ? `## 📡 API Integration\n\nYour OpenAPI spec was copied to \\`specs/${specFilename}\\` and wired up in \\`stackwright.yml\\`.\n\n**What gets generated:**\n\n\\`\\`\\`typescript\n// src/generated/${specName}/schemas.ts\nexport const EquipmentSchema = z.object({\n id: z.string(),\n name: z.string(),\n status: z.enum(['operational', 'maintenance', 'retired']),\n // ... Zod validation for runtime safety\n});\n\n// src/generated/${specName}/types.ts\nexport type Equipment = z.infer<typeof EquipmentSchema>;\n\n// src/generated/${specName}/client.ts\nexport class ${specName!.charAt(0).toUpperCase() + specName!.slice(1)}Client {\n async getEquipment(id: string): Promise<Equipment> {\n // Auto-wired with auth, validation, error handling\n }\n}\n\\`\\`\\`\n\n**How it works:**\n1. The \\`predev\\` script in \\`package.json\\` runs \\`prebuild.js\\` before every \\`pnpm dev\\`\n2. Prebuild reads your spec and generates type-safe code\n3. You import and use it:\n\n\\`\\`\\`typescript\nimport { ${specName!.charAt(0).toUpperCase() + specName!.slice(1)}Client } from '@/generated/${specName}/client';\nimport type { Equipment } from '@/generated/${specName}/types';\n\nconst client = new ${specName!.charAt(0).toUpperCase() + specName!.slice(1)}Client();\nconst gear: Equipment = await client.getEquipment('123');\n\\`\\`\\`\n\nNo manual typing. No drift. No runtime surprises.\n\n`\n : '';\n\n // Build mock server section if enabled\n const mockSection = hasMock\n ? `## 🎭 Mock Server\n\nPrism mock server is configured to serve your API at **http://localhost:4010**.\n\n\\`\\`\\`bash\npnpm dev:mock # Starts Prism + Next.js together\n\\`\\`\\`\n\nThis starts:\n1. Prism mock server on port 4010 (serves your OpenAPI spec)\n2. Next.js dev server on port 3000\n\nThe generated API client is automatically configured to use the mock server.\n\n`\n : '';\n\n // Build AI Agents section if otters are present\n const aiAgentsSection = hasOtters\n ? `## 🦦 AI Agents (The Otter Raft)\n\nYour project includes **FOUR specialized AI agents** in \\`.stackwright/\\`:\n\n- \\`stackwright-pro-foreman-otter\\` - Master coordinator that orchestrates all specialist otters\n- \\`stackwright-pro-api-otter\\` - Discovers entities from OpenAPI specs\n- \\`stackwright-pro-data-otter\\` - Configures endpoint filters & ISR\n- \\`stackwright-pro-dashboard-otter\\` - Builds live data dashboards\n\n**Invoke the Foreman to build for you:**\n\n\\`\\`\\`bash\nuvx code-puppy -i -a stackwright-pro-foreman-otter\n\\`\\`\\`\n\n**Example prompts:**\n\n\"Build me a logistics dashboard from our OpenAPI spec. Connect to /equipment and /supplies endpoints.\"\n\n\"Add a maintenance schedule page that shows equipment due for service.\"\n\nThe Pro Foreman coordinates specialist otters, validates outputs, and renders previews—all with live API data.\n\n`\n : '';\n\n return `# ${projectName}\n\nA Stackwright Pro application with role-based auth, OpenAPI integration, and intelligent AI agents.\n\n## 🚀 Quick Start\n\n\\`\\`\\`bash\n# Install dependencies\npnpm install\n\n# Start development server (no auth mock)\npnpm dev\n\n# Open your browser\nopen http://localhost:3000\n\\`\\`\\`\n\n## 👥 Role-Based Development\n\nStackwright Pro includes **mock authentication** for local development with three pre-configured roles:\n\n\\`\\`\\`bash\npnpm dev:admin # Full permissions (*)\npnpm dev:analyst # Read & write data\npnpm dev:viewer # Read-only access\n\\`\\`\\`\n\n**How it works:**\n- Each script sets the \\`MOCK_USER\\` environment variable\n- \\`lib/mock-auth.ts\\` intercepts requests and injects the appropriate user context\n- Your pages & API routes respect the RBAC rules in \\`stackwright.yml\\`\n- No backend required for testing auth flows! 🎉\n\n**Pro tip:** Test your UI for all three roles to ensure proper permissions enforcement.\n\n${mockSection}## 📁 Project Structure\n\n\\`\\`\\`\n.\n├── pages/\n│ ├── _app.tsx # Pro version with AuthProvider + shadcn\n│ ├── index.tsx # Home page (auto-generated by Stackwright)\n│ ├── about/\n│ │ └── content.yml # Page content in YAML\n│ └── ...\n├── lib/\n│ └── mock-auth.ts # Dev-only auth mocking (no backend needed)\n├── scripts/\n│ └── prebuild.js # Auto-generates code from OpenAPI specs\n├── specs/ # OpenAPI spec files (if --spec was used)\n├── src/\n│ └── generated/ # Auto-generated types & clients\n│ └── {specName}/\n│ ├── schemas.ts # Zod schemas for runtime validation\n│ ├── types.ts # TypeScript types\n│ ├── client.ts # API client with auth\n│ └── provider.ts # CollectionProvider for Stackwright\n├── .stackwright/\n│ └── otters/ # AI agent configs (4 specialized otters)\n├── stackwright.yml # Theme, auth, API integrations\n└── package.json\n\\`\\`\\`\n\n**Key Files:**\n- \\`pages/_app.tsx\\` - Wraps your app with \\`AuthProvider\\` for RBAC\n- \\`stackwright.yml\\` - Single source of truth for theme, auth roles, and API wiring\n- \\`lib/mock-auth.ts\\` - Mocks CAC/PIV headers locally so you can test auth flows\n- \\`scripts/prebuild.js\\` - Runs before dev/build to generate types from your OpenAPI spec\n\n## 📄 Adding Pages\n\nStackwright uses **YAML for content**, so you never touch JSX unless you want to.\n\n**Example:** Create \\`pages/about/content.yml\\`:\n\n\\`\\`\\`yaml\ntitle: About Us\ndescription: Learn more about our mission\n\nsections:\n - type: main\n heading: Who We Are\n body: |\n We build mission-critical logistics systems for the Marine Corps.\n\n - type: feature_list\n title: Our Capabilities\n features:\n - icon: Security\n title: Zero Trust\n description: PKI-based auth with CAC/PIV\n - icon: Api\n title: Real-time Data\n description: OpenAPI-backed, Zod-validated\n\\`\\`\\`\n\nThat's it. Navigate to \\`/about\\` and it just works. No routing config, no React components.\n\n${apiIntegrationSection}## 🎨 Customizing Theme\n\nEdit \\`stackwright.yml\\` to change colors, fonts, spacing — applies everywhere instantly.\n\n\\`\\`\\`yaml\ntheme:\n id: my-theme\n name: My Custom Theme\n colors:\n primary: \"#C41E3A\" # Marine Corps red\n background: \"#FFFFFF\"\n text: \"#1A1A1A\"\n accent: \"#FFD700\" # Gold\n typography:\n fontFamily: \"'Inter', sans-serif\"\n h1Size: \"2.5rem\"\n spacing:\n unit: 8\n\\`\\`\\`\n\nNo CSS files. No theme provider boilerplate. Just YAML. Stackwright handles the rest.\n\n## 🔐 Auth Configuration\n\nStackwright Pro comes with **3 pre-configured roles** in \\`stackwright.yml\\`:\n\n\\`\\`\\`yaml\nauth:\n roles:\n - name: ADMIN\n permissions: ['*'] # Full access\n - name: ANALYST \n permissions: ['data:read', 'data:write'] # Read/write data\n - name: VIEWER\n permissions: ['data:read'] # Read-only\n\\`\\`\\`\n\n**Gate content in your YAML:**\n\n\\`\\`\\`yaml\nsections:\n - type: button\n label: Delete Equipment\n action: /api/equipment/delete\n auth:\n required_roles: [ADMIN]\n fallback: hide # Options: hide, disable, show_message\n\\`\\`\\`\n\nOnly admins see the button. Analysts and viewers? Button doesn't exist in the DOM.\n\n**Route protection:**\n\n\\`\\`\\`yaml\nauth:\n protected_routes:\n - path: /admin/*\n roles: [ADMIN]\n - path: /equipment/* \n roles: [ANALYST, ADMIN]\n public_routes:\n - /\n - /about\n\\`\\`\\`\n\nNo middleware to write. No useAuth hooks to remember. RBAC is declarative.\n\n${aiAgentsSection}## 📚 Learn More\n\n- **Stackwright Docs:** [https://stackwright.dev](https://stackwright.dev)\n- **OSS Repo:** [https://github.com/Per-Aspera-LLC/stackwright](https://github.com/Per-Aspera-LLC/stackwright)\n- **Pro Repo:** [https://github.com/Per-Aspera-LLC/stackwright-pro](https://github.com/Per-Aspera-LLC/stackwright-pro)\n\n**Questions?** File an issue or ping us in the discussions. We're here to help you ship faster.\n`;\n}\n\n// ---------------------------------------------------------------------------\n// Main launch flow\n// ---------------------------------------------------------------------------\n\nasync function launch(targetDir: string, options: LaunchProOptions): Promise<void> {\n const dirName = path.basename(targetDir);\n\n console.log(chalk.cyan.bold('\\n🚢 Launching Stackwright Pro...\\n'));\n\n // ------------------------------------------------------------------\n // 1. Scaffold base OSS project via @stackwright/cli\n // Hooks auto-handle: Pro deps, MCP config, role-based scripts\n // ------------------------------------------------------------------\n\n const scaffoldOpts: ScaffoldOptions = {\n name: options.name || dirName,\n title: options.title,\n theme: options.theme,\n force: options.force,\n noInteractive: options.yes,\n };\n\n await scaffold(targetDir, scaffoldOpts);\n console.log(chalk.green('✅ Base project scaffolded (hooks added Pro deps + MCP config)'));\n\n // ------------------------------------------------------------------\n // 2. Copy Pro templates (hooks can't handle these)\n // ------------------------------------------------------------------\n\n // Replace _app.tsx with Pro version (AuthProvider, shadcn, etc.)\n await copyProTemplate('_app.tsx', path.join(targetDir, 'pages', '_app.tsx'));\n\n // Add root content.yml for the home page\n await copyProTemplate('content.yml', path.join(targetDir, 'pages', 'content.yml'));\n\n // Replace next.config.js with Pro version (transpile pro pkgs)\n await copyProTemplate('next.config.js', path.join(targetDir, 'next.config.js'));\n\n // Add lib/mock-auth.ts\n await fs.ensureDir(path.join(targetDir, 'lib'));\n await copyProTemplate('mock-auth.ts', path.join(targetDir, 'lib', 'mock-auth.ts'));\n\n // Add yaml.d.ts for TypeScript support\n await copyProTemplate('yaml.d.ts', path.join(targetDir, 'yaml.d.ts'));\n\n // Add scripts/prebuild.js\n await fs.ensureDir(path.join(targetDir, 'scripts'));\n await copyProTemplate('prebuild.js', path.join(targetDir, 'scripts', 'prebuild.js'));\n\n console.log(chalk.green('🔐 Auth integration added (RBAC with 3 roles)'));\n\n // ------------------------------------------------------------------\n // 3. Add auth section to stackwright.yml\n // ------------------------------------------------------------------\n\n await addAuthToStackwrightYml(targetDir);\n\n // ------------------------------------------------------------------\n // 4. Handle --spec if provided\n // ------------------------------------------------------------------\n\n let specInfo: { specFilename: string; derivedName: string } | null = null;\n const MOCK_URL = 'http://localhost:4010';\n\n if (options.spec) {\n specInfo = await handleSpec(targetDir, options.spec, options.specName);\n\n // Pass mockUrl if --mock flag is set\n await addIntegrationToStackwrightYml(\n targetDir,\n specInfo.derivedName,\n specInfo.specFilename,\n options.mock ? MOCK_URL : undefined\n );\n console.log(chalk.green('📡 OpenAPI integration wired up'));\n\n if (options.mock) {\n console.log(chalk.green('🎭 Prism mock server configured'));\n }\n } else if (options.mock) {\n // --mock without --spec: use sample Petstore spec for demo\n console.log(\n chalk.yellow(\n '⚠️ No spec provided with --mock. Using sample Petstore spec for demo. Replace with your API spec.'\n )\n );\n\n const specsDir = path.join(targetDir, 'specs');\n await fs.ensureDir(specsDir);\n await copyProTemplate('petstore.yaml', path.join(specsDir, 'petstore.yaml'));\n\n specInfo = { specFilename: 'petstore.yaml', derivedName: 'petstore' };\n\n await addIntegrationToStackwrightYml(\n targetDir,\n specInfo.derivedName,\n specInfo.specFilename,\n MOCK_URL\n );\n console.log(chalk.green('📡 Sample Petstore spec wired up'));\n console.log(chalk.green('🎭 Prism mock server configured'));\n }\n\n // ------------------------------------------------------------------\n // 5. Generate README\n // ------------------------------------------------------------------\n\n const readmeContent = generateReadme({\n projectName: options.name || dirName,\n hasSpec: !!specInfo,\n specName: specInfo?.derivedName,\n specFilename: specInfo?.specFilename,\n hasOtters: !options.skipOtters,\n hasMock: !!options.mock,\n });\n\n await fs.writeFile(path.join(targetDir, 'README.md'), readmeContent);\n console.log(chalk.green('📄 README.md created'));\n\n // ------------------------------------------------------------------\n // 6. Print next steps\n // ------------------------------------------------------------------\n\n const relDir = path.relative(process.cwd(), targetDir) || '.';\n\n console.log(chalk.cyan.bold(\"\\n🎉 All set! Here's what to do next:\\n\"));\n console.log(chalk.white(` 1. cd ${relDir}`));\n console.log(chalk.white(' 2. pnpm install'));\n console.log(chalk.white(' 3. uvx code-puppy -i -a stackwright-pro-foreman-otter'));\n\n if (specInfo) {\n console.log(chalk.cyan(`\\n📡 Your API spec was copied to specs/${specInfo.specFilename}`));\n console.log(chalk.dim(' The prebuild will generate types & client on first `pnpm dev`.'));\n }\n\n if (options.mock) {\n console.log(chalk.cyan('\\n🎭 Run with mock server:'));\n console.log(chalk.white(' pnpm dev:mock # Starts Prism + Next.js'));\n }\n\n if (!options.skipOtters) {\n console.log(chalk.cyan.bold('\\n🦦 The otter raft is ready to build your site!'));\n console.log(chalk.dim(' Run the command above to get started.\\n'));\n }\n}\n\n// ---------------------------------------------------------------------------\n// CLI definition\n// ---------------------------------------------------------------------------\n\nasync function main(): Promise<void> {\n const program = new Command();\n\n program\n .name('launch-stackwright-pro')\n .description('🚢 Launch a new Stackwright Pro project with auth, OpenAPI, and the otter raft')\n .version(version)\n .argument('[directory]', 'Project directory', '.')\n .option('--name <name>', 'Project name (used in package.json)')\n .option('--title <title>', 'Site title shown in the app bar and browser tab')\n .option('--theme <themeId>', 'Theme ID (e.g., corporate, creative, minimal)')\n .option('--force', 'Launch even if the target directory is not empty')\n .option('--skip-otters', 'Skip copying otter raft configs')\n .option('-y, --yes', 'Skip all prompts, use defaults')\n .option('--mock', 'Configure Prism mock server for API development (runs on port 4010)')\n .option(\n '--spec <path>',\n 'Path to an OpenAPI spec (YAML or JSON) — copies into project and wires up integration'\n )\n .option(\n '--spec-name <name>',\n 'Name for the API integration (default: derived from spec filename)'\n )\n .action(async (directory: string, options: LaunchProOptions) => {\n const targetDir = path.resolve(directory);\n await launch(targetDir, options);\n });\n\n await program.parseAsync(process.argv);\n}\n\nmain().catch((err: unknown) => {\n console.error(chalk.red('\\n❌ Launch failed:'), err);\n process.exit(1);\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA,iBAAAA,UAAAC,SAAA;AAAA,IAAAA,QAAA;AAAA,MACE,MAAQ;AAAA,MACR,SAAW;AAAA,MACX,aAAe;AAAA,MACf,SAAW;AAAA,MACX,YAAc;AAAA,QACZ,MAAQ;AAAA,QACR,KAAO;AAAA,MACT;AAAA,MACA,UAAY;AAAA,QACV;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,MACA,OAAS;AAAA,QACP;AAAA,QACA;AAAA,MACF;AAAA,MACA,KAAO;AAAA,QACL,0BAA0B;AAAA,MAC5B;AAAA,MACA,SAAW;AAAA,QACT,OAAS;AAAA,QACT,KAAO;AAAA,QACP,MAAQ;AAAA,QACR,iBAAiB;AAAA,MACnB;AAAA,MACA,cAAgB;AAAA,QACd,mCAAmC;AAAA,QACnC,oBAAoB;AAAA,QACpB,8BAA8B;AAAA,QAC9B,OAAS;AAAA,QACT,WAAa;AAAA,QACb,YAAY;AAAA,QACZ,WAAW;AAAA,MACb;AAAA,MACA,iBAAmB;AAAA,QACjB,mBAAmB;AAAA,QACnB,kBAAkB;AAAA,QAClB,eAAe;AAAA,QACf,YAAc;AAAA,QACd,MAAQ;AAAA,QACR,QAAU;AAAA,MACZ;AAAA,IACF;AAAA;AAAA;;;AC/CA,uBAAwB;AACxB,kBAAiB;AACjB,sBAAe;AACf,mBAAkB;AAClB,qBAAiB;AAEjB,iBAA0C;AAC1C,4BAAyC;AAAA,IAGzC,gDAAyB;AAEzB,IAAM,EAAE,QAAQ,IAAI;AAsBpB,eAAe,gBAAgB,cAAsB,UAAiC;AACpF,QAAM,MAAM,YAAAC,QAAK,QAAQ,WAAW,MAAM,aAAa,OAAO,YAAY;AAC1E,QAAM,gBAAAC,QAAG,KAAK,KAAK,QAAQ;AAC7B;AAMA,eAAe,wBAAwB,WAAkC;AACvE,QAAM,UAAU,YAAAD,QAAK,KAAK,WAAW,iBAAiB;AACtD,QAAM,UAAU,MAAM,gBAAAC,QAAG,SAAS,SAAS,OAAO;AAClD,QAAM,SAAS,eAAAC,QAAK,KAAK,OAAO;AAEhC,SAAO,OAAO;AAAA,IACZ,MAAM;AAAA,IACN,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,OAAO;AAAA,MACL,EAAE,MAAM,SAAS,aAAa,CAAC,GAAG,EAAE;AAAA,MACpC,EAAE,MAAM,WAAW,aAAa,CAAC,aAAa,YAAY,EAAE;AAAA,MAC5D,EAAE,MAAM,UAAU,aAAa,CAAC,WAAW,EAAE;AAAA,IAC/C;AAAA,IACA,kBAAkB,CAAC,EAAE,MAAM,MAAM,OAAO,CAAC,UAAU,WAAW,OAAO,EAAE,CAAC;AAAA,IACxE,eAAe,CAAC,KAAK,kBAAkB;AAAA,EACzC;AAEA,QAAM,gBAAAD,QAAG,UAAU,SAAS,eAAAC,QAAK,KAAK,QAAQ,EAAE,WAAW,IAAI,CAAC,CAAC;AACnE;AAEA,eAAe,+BACb,WACA,UACA,cACA,SACe;AACf,QAAM,UAAU,YAAAF,QAAK,KAAK,WAAW,iBAAiB;AACtD,QAAM,UAAU,MAAM,gBAAAC,QAAG,SAAS,SAAS,OAAO;AAClD,QAAM,SAAS,eAAAC,QAAK,KAAK,OAAO;AAEhC,QAAM,cAAuC;AAAA,IAC3C,MAAM;AAAA,IACN,MAAM;AAAA,IACN,MAAM,WAAW,YAAY;AAAA,IAC7B,aAAa,CAAC;AAAA,EAChB;AAEA,MAAI,SAAS;AACX,gBAAY,UAAU;AAAA,EACxB;AAEA,SAAO,eAAe,CAAC,WAAW;AAElC,QAAM,gBAAAD,QAAG,UAAU,SAAS,eAAAC,QAAK,KAAK,QAAQ,EAAE,WAAW,IAAI,CAAC,CAAC;AACnE;AAMA,eAAe,WACb,WACA,UACA,UACwD;AACxD,QAAM,eAAe,YAAAF,QAAK,QAAQ,QAAQ;AAE1C,MAAI,CAAC,gBAAAC,QAAG,WAAW,YAAY,GAAG;AAChC,UAAM,IAAI,MAAM,wBAAwB,YAAY,EAAE;AAAA,EACxD;AAEA,QAAM,eAAe,YAAAD,QAAK,SAAS,YAAY;AAC/C,QAAM,cAAc,YAAY,YAAAA,QAAK,SAAS,cAAc,YAAAA,QAAK,QAAQ,YAAY,CAAC;AAEtF,QAAM,WAAW,YAAAA,QAAK,KAAK,WAAW,OAAO;AAC7C,QAAM,gBAAAC,QAAG,UAAU,QAAQ;AAC3B,QAAM,gBAAAA,QAAG,KAAK,cAAc,YAAAD,QAAK,KAAK,UAAU,YAAY,CAAC;AAE7D,SAAO,EAAE,cAAc,YAAY;AACrC;AAMA,SAAS,eAAe,SAOb;AACT,QAAM,EAAE,aAAa,SAAS,UAAU,cAAc,WAAW,QAAQ,IAAI;AAG7E,QAAM,wBAAwB,UAC1B;AAAA;AAAA,0CAEoC,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA,mBAKnC,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAQR,QAAQ;AAAA;AAAA;AAAA,mBAGR,QAAQ;AAAA,eACZ,SAAU,OAAO,CAAC,EAAE,YAAY,IAAI,SAAU,MAAM,CAAC,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,WAa1D,SAAU,OAAO,CAAC,EAAE,YAAY,IAAI,SAAU,MAAM,CAAC,CAAC,8BAA8B,QAAQ;AAAA,8CACzD,QAAQ;AAAA;AAAA,qBAEjC,SAAU,OAAO,CAAC,EAAE,YAAY,IAAI,SAAU,MAAM,CAAC,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOrE;AAGJ,QAAM,cAAc,UAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAeA;AAGJ,QAAM,kBAAkB,YACpB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAwBA;AAEJ,SAAO,KAAK,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmCvB,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA+DX,qBAAqB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmErB,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQjB;AAMA,eAAe,OAAO,WAAmB,SAA0C;AACjF,QAAM,UAAU,YAAAA,QAAK,SAAS,SAAS;AAEvC,UAAQ,IAAI,aAAAG,QAAM,KAAK,KAAK,4CAAqC,CAAC;AAOlE,QAAM,eAAgC;AAAA,IACpC,MAAM,QAAQ,QAAQ;AAAA,IACtB,OAAO,QAAQ;AAAA,IACf,OAAO,QAAQ;AAAA,IACf,OAAO,QAAQ;AAAA,IACf,eAAe,QAAQ;AAAA,EACzB;AAEA,YAAM,qBAAS,WAAW,YAAY;AACtC,UAAQ,IAAI,aAAAA,QAAM,MAAM,oEAA+D,CAAC;AAOxF,QAAM,gBAAgB,YAAY,YAAAH,QAAK,KAAK,WAAW,SAAS,UAAU,CAAC;AAG3E,QAAM,gBAAgB,eAAe,YAAAA,QAAK,KAAK,WAAW,SAAS,aAAa,CAAC;AAGjF,QAAM,gBAAgB,kBAAkB,YAAAA,QAAK,KAAK,WAAW,gBAAgB,CAAC;AAG9E,QAAM,gBAAAC,QAAG,UAAU,YAAAD,QAAK,KAAK,WAAW,KAAK,CAAC;AAC9C,QAAM,gBAAgB,gBAAgB,YAAAA,QAAK,KAAK,WAAW,OAAO,cAAc,CAAC;AAGjF,QAAM,gBAAgB,aAAa,YAAAA,QAAK,KAAK,WAAW,WAAW,CAAC;AAGpE,QAAM,gBAAAC,QAAG,UAAU,YAAAD,QAAK,KAAK,WAAW,SAAS,CAAC;AAClD,QAAM,gBAAgB,eAAe,YAAAA,QAAK,KAAK,WAAW,WAAW,aAAa,CAAC;AAEnF,UAAQ,IAAI,aAAAG,QAAM,MAAM,sDAA+C,CAAC;AAMxE,QAAM,wBAAwB,SAAS;AAMvC,MAAI,WAAiE;AACrE,QAAM,WAAW;AAEjB,MAAI,QAAQ,MAAM;AAChB,eAAW,MAAM,WAAW,WAAW,QAAQ,MAAM,QAAQ,QAAQ;AAGrE,UAAM;AAAA,MACJ;AAAA,MACA,SAAS;AAAA,MACT,SAAS;AAAA,MACT,QAAQ,OAAO,WAAW;AAAA,IAC5B;AACA,YAAQ,IAAI,aAAAA,QAAM,MAAM,wCAAiC,CAAC;AAE1D,QAAI,QAAQ,MAAM;AAChB,cAAQ,IAAI,aAAAA,QAAM,MAAM,wCAAiC,CAAC;AAAA,IAC5D;AAAA,EACF,WAAW,QAAQ,MAAM;AAEvB,YAAQ;AAAA,MACN,aAAAA,QAAM;AAAA,QACJ;AAAA,MACF;AAAA,IACF;AAEA,UAAM,WAAW,YAAAH,QAAK,KAAK,WAAW,OAAO;AAC7C,UAAM,gBAAAC,QAAG,UAAU,QAAQ;AAC3B,UAAM,gBAAgB,iBAAiB,YAAAD,QAAK,KAAK,UAAU,eAAe,CAAC;AAE3E,eAAW,EAAE,cAAc,iBAAiB,aAAa,WAAW;AAEpE,UAAM;AAAA,MACJ;AAAA,MACA,SAAS;AAAA,MACT,SAAS;AAAA,MACT;AAAA,IACF;AACA,YAAQ,IAAI,aAAAG,QAAM,MAAM,yCAAkC,CAAC;AAC3D,YAAQ,IAAI,aAAAA,QAAM,MAAM,wCAAiC,CAAC;AAAA,EAC5D;AAMA,QAAM,gBAAgB,eAAe;AAAA,IACnC,aAAa,QAAQ,QAAQ;AAAA,IAC7B,SAAS,CAAC,CAAC;AAAA,IACX,UAAU,UAAU;AAAA,IACpB,cAAc,UAAU;AAAA,IACxB,WAAW,CAAC,QAAQ;AAAA,IACpB,SAAS,CAAC,CAAC,QAAQ;AAAA,EACrB,CAAC;AAED,QAAM,gBAAAF,QAAG,UAAU,YAAAD,QAAK,KAAK,WAAW,WAAW,GAAG,aAAa;AACnE,UAAQ,IAAI,aAAAG,QAAM,MAAM,6BAAsB,CAAC;AAM/C,QAAM,SAAS,YAAAH,QAAK,SAAS,QAAQ,IAAI,GAAG,SAAS,KAAK;AAE1D,UAAQ,IAAI,aAAAG,QAAM,KAAK,KAAK,gDAAyC,CAAC;AACtE,UAAQ,IAAI,aAAAA,QAAM,MAAM,WAAW,MAAM,EAAE,CAAC;AAC5C,UAAQ,IAAI,aAAAA,QAAM,MAAM,mBAAmB,CAAC;AAC5C,UAAQ,IAAI,aAAAA,QAAM,MAAM,yDAAyD,CAAC;AAElF,MAAI,UAAU;AACZ,YAAQ,IAAI,aAAAA,QAAM,KAAK;AAAA,8CAA0C,SAAS,YAAY,EAAE,CAAC;AACzF,YAAQ,IAAI,aAAAA,QAAM,IAAI,mEAAmE,CAAC;AAAA,EAC5F;AAEA,MAAI,QAAQ,MAAM;AAChB,YAAQ,IAAI,aAAAA,QAAM,KAAK,mCAA4B,CAAC;AACpD,YAAQ,IAAI,aAAAA,QAAM,MAAM,4CAA4C,CAAC;AAAA,EACvE;AAEA,MAAI,CAAC,QAAQ,YAAY;AACvB,YAAQ,IAAI,aAAAA,QAAM,KAAK,KAAK,yDAAkD,CAAC;AAC/E,YAAQ,IAAI,aAAAA,QAAM,IAAI,4CAA4C,CAAC;AAAA,EACrE;AACF;AAMA,eAAe,OAAsB;AACnC,QAAM,UAAU,IAAI,yBAAQ;AAE5B,UACG,KAAK,wBAAwB,EAC7B,YAAY,uFAAgF,EAC5F,QAAQ,OAAO,EACf,SAAS,eAAe,qBAAqB,GAAG,EAChD,OAAO,iBAAiB,qCAAqC,EAC7D,OAAO,mBAAmB,iDAAiD,EAC3E,OAAO,qBAAqB,+CAA+C,EAC3E,OAAO,WAAW,kDAAkD,EACpE,OAAO,iBAAiB,iCAAiC,EACzD,OAAO,aAAa,gCAAgC,EACpD,OAAO,UAAU,qEAAqE,EACtF;AAAA,IACC;AAAA,IACA;AAAA,EACF,EACC;AAAA,IACC;AAAA,IACA;AAAA,EACF,EACC,OAAO,OAAO,WAAmB,YAA8B;AAC9D,UAAM,YAAY,YAAAH,QAAK,QAAQ,SAAS;AACxC,UAAM,OAAO,WAAW,OAAO;AAAA,EACjC,CAAC;AAEH,QAAM,QAAQ,WAAW,QAAQ,IAAI;AACvC;AAEA,KAAK,EAAE,MAAM,CAAC,QAAiB;AAC7B,UAAQ,MAAM,aAAAG,QAAM,IAAI,yBAAoB,GAAG,GAAG;AAClD,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":["exports","module","path","fs","yaml","chalk"]}
|
|
1
|
+
{"version":3,"sources":["../package.json","../src/index.ts"],"sourcesContent":["{\n \"name\": \"@stackwright-pro/launch-stackwright-pro\",\n \"version\": \"0.4.0-alpha.1\",\n \"description\": \"Launch a new Stackwright Pro project with OpenAPI integration, auth, and the otter raft\",\n \"license\": \"MIT\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/Per-Aspera-LLC/stackwright-pro\"\n },\n \"keywords\": [\n \"stackwright\",\n \"stackwright-pro\",\n \"scaffolding\",\n \"openapi\",\n \"government\",\n \"hackathon\"\n ],\n \"files\": [\n \"dist\",\n \"templates\"\n ],\n \"bin\": {\n \"launch-stackwright-pro\": \"dist/index.js\"\n },\n \"scripts\": {\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"test\": \"vitest run\",\n \"test:coverage\": \"vitest run --coverage\"\n },\n \"dependencies\": {\n \"@stackwright-pro/scaffold-hooks\": \"workspace:*\",\n \"@stackwright/cli\": \"^0.7.0\",\n \"@stackwright/scaffold-core\": \"^0.1.0\",\n \"chalk\": \"^5.6.2\",\n \"commander\": \"^14.0.3\",\n \"fs-extra\": \"^11.3\",\n \"js-yaml\": \"^4.1.0\"\n },\n \"devDependencies\": {\n \"@types/fs-extra\": \"^11.0\",\n \"@types/js-yaml\": \"^4.0.9\",\n \"@types/node\": \"^24.0.0\",\n \"typescript\": \"^5.0\",\n \"tsup\": \"^8.5\",\n \"vitest\": \"^4.0.18\"\n }\n}\n","import { Command } from 'commander';\nimport path from 'path';\nimport os from 'os';\nimport fs from 'fs-extra';\nimport chalk from 'chalk';\nimport yaml from 'js-yaml';\nimport { execSync } from 'child_process';\n\nimport { scaffold, ScaffoldOptions } from '@stackwright/cli';\nimport { runScaffoldHooks } from '@stackwright/scaffold-core';\nimport { registerProScaffoldHooks } from '@stackwright-pro/scaffold-hooks';\n// Register Pro hooks (adds deps, MCP config, etc.)\n// Must be called before scaffold() to ensure hooks run in the CLI's scaffold-core context\nregisterProScaffoldHooks();\n\nconst { version } = require('../package.json') as { version: string };\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ninterface LaunchProOptions {\n name?: string;\n title?: string;\n theme?: string;\n force?: boolean;\n skipOtters?: boolean;\n skipInstall?: boolean;\n yes?: boolean;\n spec?: string;\n specName?: string;\n mock?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Template copying (Pro-specific - hooks can't handle these)\n// ---------------------------------------------------------------------------\n\nasync function copyProTemplate(templateName: string, destPath: string): Promise<void> {\n const src = path.resolve(__dirname, '..', 'templates', 'pro', templateName);\n await fs.copy(src, destPath);\n}\n\n// ---------------------------------------------------------------------------\n// Auth config (Pro-specific - hooks can't handle these)\n// ---------------------------------------------------------------------------\n\nasync function addAuthToStackwrightYml(targetDir: string): Promise<void> {\n const ymlPath = path.join(targetDir, 'stackwright.yml');\n const content = await fs.readFile(ymlPath, 'utf-8');\n const config = yaml.load(content) as Record<string, unknown>;\n\n config.auth = {\n type: 'pki',\n profile: 'dod_cac',\n source: 'gateway_headers',\n roles: [\n { name: 'ADMIN', permissions: ['*'] },\n { name: 'ANALYST', permissions: ['data:read', 'data:write'] },\n { name: 'VIEWER', permissions: ['data:read'] },\n ],\n protected_routes: [{ path: '/*', roles: ['VIEWER', 'ANALYST', 'ADMIN'] }],\n public_routes: ['/', '/getting-started'],\n };\n\n await fs.writeFile(ymlPath, yaml.dump(config, { lineWidth: 120 }));\n}\n\nasync function addIntegrationToStackwrightYml(\n targetDir: string,\n specName: string,\n specFilename: string,\n mockUrl?: string\n): Promise<void> {\n const ymlPath = path.join(targetDir, 'stackwright.yml');\n const content = await fs.readFile(ymlPath, 'utf-8');\n const config = yaml.load(content) as Record<string, unknown>;\n\n const integration: Record<string, unknown> = {\n type: 'openapi',\n name: specName,\n spec: `./specs/${specFilename}`,\n collections: [],\n };\n\n if (mockUrl) {\n integration.mockUrl = mockUrl;\n }\n\n config.integrations = [integration];\n\n await fs.writeFile(ymlPath, yaml.dump(config, { lineWidth: 120 }));\n}\n\n// ---------------------------------------------------------------------------\n// Spec handling (Pro-specific - hooks can't handle these)\n// ---------------------------------------------------------------------------\n\nasync function handleSpec(\n targetDir: string,\n specPath: string,\n specName?: string\n): Promise<{ specFilename: string; derivedName: string }> {\n const resolvedSpec = path.resolve(specPath);\n\n if (!fs.existsSync(resolvedSpec)) {\n throw new Error(`Spec file not found: ${resolvedSpec}`);\n }\n\n const specFilename = path.basename(resolvedSpec);\n const derivedName = specName || path.basename(specFilename, path.extname(specFilename));\n\n const specsDir = path.join(targetDir, 'specs');\n await fs.ensureDir(specsDir);\n await fs.copy(resolvedSpec, path.join(specsDir, specFilename));\n\n return { specFilename, derivedName };\n}\n\n// ---------------------------------------------------------------------------\n// README generator (Pro-specific - hooks can't handle these)\n// ---------------------------------------------------------------------------\n\nfunction generateReadme(options: {\n projectName: string;\n hasSpec: boolean;\n specName?: string;\n specFilename?: string;\n hasOtters: boolean;\n hasMock: boolean;\n}): string {\n const { projectName, hasSpec, specName, specFilename, hasOtters, hasMock } = options;\n\n // Build API Integration section if spec is present\n const apiIntegrationSection = hasSpec\n ? `## 📡 API Integration\n\nYour OpenAPI spec was copied to \\`specs/${specFilename}\\` and wired up in \\`stackwright.yml\\`.\n\n**What gets generated:**\n\n\\`\\`\\`typescript\n// src/generated/${specName}/schemas.ts\nexport const EquipmentSchema = z.object({\n id: z.string(),\n name: z.string(),\n status: z.enum(['operational', 'maintenance', 'retired']),\n // ... Zod validation for runtime safety\n});\n\n// src/generated/${specName}/types.ts\nexport type Equipment = z.infer<typeof EquipmentSchema>;\n\n// src/generated/${specName}/client.ts\nexport class ${specName!.charAt(0).toUpperCase() + specName!.slice(1)}Client {\n async getEquipment(id: string): Promise<Equipment> {\n // Auto-wired with auth, validation, error handling\n }\n}\n\\`\\`\\`\n\n**How it works:**\n1. The \\`predev\\` script in \\`package.json\\` runs \\`prebuild.js\\` before every \\`pnpm dev\\`\n2. Prebuild reads your spec and generates type-safe code\n3. You import and use it:\n\n\\`\\`\\`typescript\nimport { ${specName!.charAt(0).toUpperCase() + specName!.slice(1)}Client } from '@/generated/${specName}/client';\nimport type { Equipment } from '@/generated/${specName}/types';\n\nconst client = new ${specName!.charAt(0).toUpperCase() + specName!.slice(1)}Client();\nconst gear: Equipment = await client.getEquipment('123');\n\\`\\`\\`\n\nNo manual typing. No drift. No runtime surprises.\n\n`\n : '';\n\n // Build mock server section if enabled\n const mockSection = hasMock\n ? `## 🎭 Mock Server\n\nPrism mock server is configured to serve your API at **http://localhost:4010**.\n\n\\`\\`\\`bash\npnpm dev:mock # Starts Prism + Next.js together\n\\`\\`\\`\n\nThis starts:\n1. Prism mock server on port 4010 (serves your OpenAPI spec)\n2. Next.js dev server on port 3000\n\nThe generated API client is automatically configured to use the mock server.\n\n`\n : '';\n\n // Build AI Agents section if otters are present\n const aiAgentsSection = hasOtters\n ? `## 🦦 AI Agents (The Otter Raft)\n\nYour project includes **SIX specialized AI agents** in \\`node_modules/@stackwright-pro/otters/\\`:\n\n- \\`stackwright-pro-foreman-otter\\` - Master coordinator that orchestrates all specialist otters\n- \\`stackwright-pro-api-otter\\` - Discovers entities from OpenAPI specs\n- \\`stackwright-pro-auth-otter\\` - Configures CAC/OIDC auth, RBAC rules, and audit middleware\n- \\`stackwright-pro-data-otter\\` - Configures endpoint filters & ISR revalidation\n- \\`stackwright-pro-page-otter\\` - Generates pages auto-wired with data, theme, and auth\n- \\`stackwright-pro-dashboard-otter\\` - Builds live data dashboards\n\n**Start the otter raft:**\n\n\\`\\`\\`bash\nuvx stackwright-pro-raft\n\\`\\`\\`\n\n> **Local dev (before PyPI publish):** \\`python -m stackwright_pro.raft.server\\`\n\nThe raft is a Python state machine with a Rich terminal UI — it asks questions about your brand, auth, and data preferences, then coordinates specialist otters to build your app. All user interaction happens in the terminal; code-puppy is called as a one-shot subprocess for creative tasks only.\n\n**Example session prompt:**\n\n\"Build me a logistics dashboard from our OpenAPI spec. Connect to /equipment and /supplies endpoints.\"\n\n`\n : '';\n\n return `# ${projectName}\n\nA Stackwright Pro application with role-based auth, OpenAPI integration, and intelligent AI agents.\n\n## 🚀 Quick Start\n\n\\`\\`\\`bash\n# Start development server\npnpm dev\n\n# Open your browser\nopen http://localhost:3000\n\\`\\`\\`\n\n## 👥 Role-Based Development\n\nStackwright Pro includes **mock authentication** for local development with three pre-configured roles:\n\n\\`\\`\\`bash\npnpm dev:admin # Full permissions (*)\npnpm dev:analyst # Read & write data\npnpm dev:viewer # Read-only access\n\\`\\`\\`\n\n**How it works:**\n- Each script sets the \\`MOCK_USER\\` environment variable\n- \\`lib/mock-auth.ts\\` intercepts requests and injects the appropriate user context\n- Your pages & API routes respect the RBAC rules in \\`stackwright.yml\\`\n- No backend required for testing auth flows! 🎉\n\n**Pro tip:** Test your UI for all three roles to ensure proper permissions enforcement.\n\n${mockSection}## 📁 Project Structure\n\n\\`\\`\\`\n.\n├── pages/\n│ ├── _app.tsx # Pro version with AuthProvider + shadcn\n│ ├── index.tsx # Home page (auto-generated by Stackwright)\n│ ├── about/\n│ │ └── content.yml # Page content in YAML\n│ └── ...\n├── lib/\n│ └── mock-auth.ts # Dev-only auth mocking (no backend needed)\n├── scripts/\n│ └── prebuild.js # Auto-generates code from OpenAPI specs\n├── specs/ # OpenAPI spec files (if --spec was used)\n├── src/\n│ └── generated/ # Auto-generated types & clients\n│ └── {specName}/\n│ ├── schemas.ts # Zod schemas for runtime validation\n│ ├── types.ts # TypeScript types\n│ ├── client.ts # API client with auth\n│ └── provider.ts # CollectionProvider for Stackwright\n├── .stackwright/\n│ └── otters/ # AI agent configs (4 specialized otters)\n├── stackwright.yml # Theme, auth, API integrations\n└── package.json\n\\`\\`\\`\n\n**Key Files:**\n- \\`pages/_app.tsx\\` - Wraps your app with \\`AuthProvider\\` for RBAC\n- \\`stackwright.yml\\` - Single source of truth for theme, auth roles, and API wiring\n- \\`lib/mock-auth.ts\\` - Mocks CAC/PIV headers locally so you can test auth flows\n- \\`scripts/prebuild.js\\` - Runs before dev/build to generate types from your OpenAPI spec\n\n## 📄 Adding Pages\n\nStackwright uses **YAML for content**, so you never touch JSX unless you want to.\n\n**Example:** Create \\`pages/about/content.yml\\`:\n\n\\`\\`\\`yaml\ntitle: About Us\ndescription: Learn more about our mission\n\nsections:\n - type: main\n heading: Who We Are\n body: |\n We build mission-critical logistics systems for the Marine Corps.\n\n - type: feature_list\n title: Our Capabilities\n features:\n - icon: Security\n title: Zero Trust\n description: PKI-based auth with CAC/PIV\n - icon: Api\n title: Real-time Data\n description: OpenAPI-backed, Zod-validated\n\\`\\`\\`\n\nThat's it. Navigate to \\`/about\\` and it just works. No routing config, no React components.\n\n${apiIntegrationSection}## 🎨 Customizing Theme\n\nEdit \\`stackwright.yml\\` to change colors, fonts, spacing — applies everywhere instantly.\n\n\\`\\`\\`yaml\ntheme:\n id: my-theme\n name: My Custom Theme\n colors:\n primary: \"#C41E3A\" # Marine Corps red\n background: \"#FFFFFF\"\n text: \"#1A1A1A\"\n accent: \"#FFD700\" # Gold\n typography:\n fontFamily: \"'Inter', sans-serif\"\n h1Size: \"2.5rem\"\n spacing:\n unit: 8\n\\`\\`\\`\n\nNo CSS files. No theme provider boilerplate. Just YAML. Stackwright handles the rest.\n\n## 🔐 Auth Configuration\n\nStackwright Pro comes with **3 pre-configured roles** in \\`stackwright.yml\\`:\n\n\\`\\`\\`yaml\nauth:\n roles:\n - name: ADMIN\n permissions: ['*'] # Full access\n - name: ANALYST \n permissions: ['data:read', 'data:write'] # Read/write data\n - name: VIEWER\n permissions: ['data:read'] # Read-only\n\\`\\`\\`\n\n**Gate content in your YAML:**\n\n\\`\\`\\`yaml\nsections:\n - type: button\n label: Delete Equipment\n action: /api/equipment/delete\n auth:\n required_roles: [ADMIN]\n fallback: hide # Options: hide, disable, show_message\n\\`\\`\\`\n\nOnly admins see the button. Analysts and viewers? Button doesn't exist in the DOM.\n\n**Route protection:**\n\n\\`\\`\\`yaml\nauth:\n protected_routes:\n - path: /admin/*\n roles: [ADMIN]\n - path: /equipment/* \n roles: [ANALYST, ADMIN]\n public_routes:\n - /\n - /about\n\\`\\`\\`\n\nNo middleware to write. No useAuth hooks to remember. RBAC is declarative.\n\n${aiAgentsSection}## 📚 Learn More\n\n- **Stackwright Docs:** [https://stackwright.dev](https://stackwright.dev)\n- **OSS Repo:** [https://github.com/Per-Aspera-LLC/stackwright](https://github.com/Per-Aspera-LLC/stackwright)\n- **Pro Repo:** [https://github.com/Per-Aspera-LLC/stackwright-pro](https://github.com/Per-Aspera-LLC/stackwright-pro)\n\n**Questions?** File an issue or ping us in the discussions. We're here to help you ship faster.\n`;\n}\n\n// ---------------------------------------------------------------------------\n// Main launch flow\n// ---------------------------------------------------------------------------\n\nasync function launch(targetDir: string, options: LaunchProOptions): Promise<void> {\n const dirName = path.basename(targetDir);\n\n console.log(chalk.cyan.bold('\\n🚢 Launching Stackwright Pro...\\n'));\n\n // ------------------------------------------------------------------\n // 1. Scaffold base OSS project via @stackwright/cli\n // Hooks auto-handle: Pro deps, MCP config, role-based scripts\n // ------------------------------------------------------------------\n\n const scaffoldOpts: ScaffoldOptions = {\n name: options.name || dirName,\n ...(options.title !== undefined && { title: options.title }),\n ...(options.theme !== undefined && { theme: options.theme }),\n ...(options.force !== undefined && { force: options.force }),\n ...(options.yes !== undefined && { noInteractive: options.yes }),\n };\n\n await scaffold(targetDir, scaffoldOpts);\n console.log(chalk.green('✅ Base project scaffolded (hooks added Pro deps + MCP config)'));\n\n // ------------------------------------------------------------------\n // 1b. Run pnpm install — citizen devs shouldn't need to know this step exists\n // ------------------------------------------------------------------\n\n console.log(chalk.cyan('\\n📦 Installing dependencies (this takes a moment)...'));\n if (!options.skipInstall) {\n try {\n execSync('pnpm install', { cwd: targetDir, stdio: 'inherit', timeout: 120_000 });\n console.log(chalk.green('✅ Dependencies installed'));\n } catch {\n console.warn(chalk.yellow('⚠️ pnpm install failed — run it manually: pnpm install'));\n }\n } else {\n console.log(chalk.dim('ℹ️ Skipping install (--skip-install). Run: pnpm install'));\n }\n\n // Fire postInstall hooks now that install has run\n const dependencyMode: 'workspace' | 'standalone' = 'standalone';\n await runScaffoldHooks('postInstall', {\n targetDir,\n projectName: options.name || path.basename(targetDir),\n siteTitle: options.title || options.name || path.basename(targetDir),\n themeId: options.theme || 'corporate',\n packageJson: {},\n dependencyMode,\n });\n\n // ------------------------------------------------------------------\n // 2. Copy Pro templates (hooks can't handle these)\n // ------------------------------------------------------------------\n\n // Replace _app.tsx with Pro version (AuthProvider, shadcn, etc.)\n await copyProTemplate('_app.tsx', path.join(targetDir, 'pages', '_app.tsx'));\n\n // Add root content.yml for the home page\n await copyProTemplate('content.yml', path.join(targetDir, 'pages', 'content.yml'));\n\n // Replace next.config.js with Pro version (transpile pro pkgs)\n await copyProTemplate('next.config.js', path.join(targetDir, 'next.config.js'));\n\n // Add lib/mock-auth.ts\n await fs.ensureDir(path.join(targetDir, 'lib'));\n await copyProTemplate('mock-auth.ts', path.join(targetDir, 'lib', 'mock-auth.ts'));\n\n // Add yaml.d.ts for TypeScript support\n await copyProTemplate('yaml.d.ts', path.join(targetDir, 'yaml.d.ts'));\n\n // Add scripts/prebuild.js\n await fs.ensureDir(path.join(targetDir, 'scripts'));\n await copyProTemplate('prebuild.js', path.join(targetDir, 'scripts', 'prebuild.js'));\n\n console.log(chalk.green('🔐 Auth integration added (RBAC with 3 roles)'));\n\n // ------------------------------------------------------------------\n // 3. Add auth section to stackwright.yml\n // ------------------------------------------------------------------\n\n await addAuthToStackwrightYml(targetDir);\n\n // ------------------------------------------------------------------\n // 4. Handle --spec if provided\n // ------------------------------------------------------------------\n\n let specInfo: { specFilename: string; derivedName: string } | null = null;\n const MOCK_URL = 'http://localhost:4010';\n\n if (options.spec) {\n specInfo = await handleSpec(targetDir, options.spec, options.specName);\n\n // Pass mockUrl if --mock flag is set\n await addIntegrationToStackwrightYml(\n targetDir,\n specInfo.derivedName,\n specInfo.specFilename,\n options.mock ? MOCK_URL : undefined\n );\n console.log(chalk.green('📡 OpenAPI integration wired up'));\n\n if (options.mock) {\n console.log(chalk.green('🎭 Prism mock server configured'));\n }\n } else if (options.mock) {\n // --mock without --spec: use sample Petstore spec for demo\n console.log(\n chalk.yellow(\n '⚠️ No spec provided with --mock. Using sample Petstore spec for demo. Replace with your API spec.'\n )\n );\n\n const specsDir = path.join(targetDir, 'specs');\n await fs.ensureDir(specsDir);\n await copyProTemplate('petstore.yaml', path.join(specsDir, 'petstore.yaml'));\n\n specInfo = { specFilename: 'petstore.yaml', derivedName: 'petstore' };\n\n await addIntegrationToStackwrightYml(\n targetDir,\n specInfo.derivedName,\n specInfo.specFilename,\n MOCK_URL\n );\n console.log(chalk.green('📡 Sample Petstore spec wired up'));\n console.log(chalk.green('🎭 Prism mock server configured'));\n }\n\n // ------------------------------------------------------------------\n // 5. Generate README\n // ------------------------------------------------------------------\n\n const readmeContent = generateReadme({\n projectName: options.name || dirName,\n hasSpec: !!specInfo,\n ...(specInfo?.derivedName !== undefined && { specName: specInfo.derivedName }),\n ...(specInfo?.specFilename !== undefined && { specFilename: specInfo.specFilename }),\n hasOtters: !options.skipOtters,\n hasMock: !!options.mock,\n });\n\n await fs.writeFile(path.join(targetDir, 'README.md'), readmeContent);\n console.log(chalk.green('📄 README.md created'));\n\n // ------------------------------------------------------------------\n // 6. Print next steps\n // ------------------------------------------------------------------\n\n const relDir = path.relative(process.cwd(), targetDir) || '.';\n\n console.log(chalk.cyan.bold(\"\\n🎉 All set! Here's what to do next:\\n\"));\n console.log(chalk.white(` 1. cd ${relDir}`));\n console.log(chalk.white(' 2. uvx stackwright-pro-raft'));\n console.log(chalk.dim(' (local dev: python -m stackwright_pro.raft.server)'));\n\n if (specInfo) {\n console.log(chalk.cyan(`\\n📡 Your API spec was copied to specs/${specInfo.specFilename}`));\n console.log(chalk.dim(' The prebuild will generate types & client on first `pnpm dev`.'));\n }\n\n if (options.mock) {\n console.log(chalk.cyan('\\n🎭 Run with mock server:'));\n console.log(chalk.white(' pnpm dev:mock # Starts Prism + Next.js'));\n }\n\n if (!options.skipOtters) {\n console.log(chalk.cyan.bold('\\n🦦 The otter raft is ready to build your site!'));\n console.log(chalk.dim(' Run the command above to get started.\\n'));\n }\n}\n\n// ---------------------------------------------------------------------------\n// CLI definition\n// ---------------------------------------------------------------------------\n\nasync function main(): Promise<void> {\n const program = new Command();\n\n program\n .name('launch-stackwright-pro')\n .description('🚢 Launch a new Stackwright Pro project with auth, OpenAPI, and the otter raft')\n .version(version)\n .argument('[directory]', 'Project directory', '.')\n .option('--name <name>', 'Project name (used in package.json)')\n .option('--title <title>', 'Site title shown in the app bar and browser tab')\n .option('--theme <themeId>', 'Theme ID (e.g., corporate, creative, minimal)')\n .option('--force', 'Launch even if the target directory is not empty')\n .option('--skip-otters', 'Skip copying otter raft configs')\n .option('-y, --yes', 'Skip all prompts, use defaults')\n .option('--mock', 'Configure Prism mock server for API development (runs on port 4010)')\n .option(\n '--spec <path>',\n 'Path to an OpenAPI spec (YAML or JSON) — copies into project and wires up integration'\n )\n .option(\n '--spec-name <name>',\n 'Name for the API integration (default: derived from spec filename)'\n )\n .option(\n '--skip-install',\n 'Skip automatic pnpm install (useful in CI or when using a different package manager)'\n )\n .action(async (directory: string, options: LaunchProOptions) => {\n const homeDir = os.homedir();\n const targetDir = path.resolve(directory);\n\n // Warn (don't block) when scaffolding outside home dir — unusual for a user-facing CLI\n if (!targetDir.startsWith(homeDir + path.sep) && targetDir !== homeDir) {\n console.warn(chalk.yellow(`⚠️ Scaffolding outside your home directory: ${targetDir}`));\n }\n\n await launch(targetDir, options);\n });\n\n await program.parseAsync(process.argv);\n}\n\nmain().catch((err: unknown) => {\n console.error(chalk.red('\\n❌ Launch failed:'), err);\n process.exit(1);\n});\n"],"mappings":"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","names":["exports","module","path","fs","yaml","chalk","os"]}
|
package/package.json
CHANGED