@stackwright-pro/launch-stackwright-pro 0.4.0-alpha.14 → 0.4.0-alpha.141

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ PROPRIETARY SOFTWARE LICENSE
2
+
3
+ Copyright (c) 2024-2026 Per Aspera LLC. All Rights Reserved.
4
+
5
+ This software and associated documentation files (the "Software") are the
6
+ proprietary and confidential property of Per Aspera LLC ("Company").
7
+
8
+ RESTRICTIONS: You may not use, copy, modify, merge, publish, distribute,
9
+ sublicense, sell, or otherwise exploit this Software or any portion thereof
10
+ without the express prior written consent of the Company.
11
+
12
+ GOVERNMENT USE: Use, duplication, or disclosure by the U.S. Government is
13
+ subject to restrictions as set forth in FAR 52.227-19 (Commercial Computer
14
+ Software - Restricted Rights) and DFARS 252.227-7013 (Rights in Technical
15
+ Data and Computer Software), as applicable.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED. IN NO EVENT SHALL THE COMPANY BE LIABLE FOR ANY CLAIM, DAMAGES, OR
19
+ OTHER LIABILITY ARISING FROM THE USE OF THE SOFTWARE.
20
+
21
+ For licensing inquiries: legal@peraspera.com
package/README.md CHANGED
@@ -21,10 +21,14 @@ to automatically wire up Pro features:
21
21
  ## Quick Start
22
22
 
23
23
  ```bash
24
- pnpx @stackwright-pro/launch-stackwright-pro my-app -y
24
+ # Works — project created in $cwd/my-app
25
+ pnpx @stackwright-pro/launch-stackwright-pro --name my-app -y
25
26
  # Dependencies install automatically (~7s)
26
27
  cd my-app
27
- uvx stackwright-pro-raft # Start the otter raft
28
+ npx @stackwright-pro/raft # Start the otter raft
29
+
30
+ # Equivalent explicit form
31
+ pnpx @stackwright-pro/launch-stackwright-pro my-app --name my-app -y
28
32
  ```
29
33
 
30
34
  ### With an OpenAPI Spec
@@ -72,7 +76,7 @@ Options:
72
76
  --force Overwrite existing directory
73
77
  --skip-otters Skip otter raft setup
74
78
  -y, --yes Skip prompts, use defaults
75
- --spec <path> Path to an OpenAPI spec (YAML or JSON)
79
+ --spec <paths...> Paths to OpenAPI specs (can be specified multiple times)
76
80
  --spec-name <name> Name for the API integration (default: derived from filename)
77
81
  -V, --version Output the version number
78
82
  -h, --help Display help
@@ -120,24 +124,11 @@ for architecture details, auth deep-dives, and deployment guides.
120
124
 
121
125
  ## Starting the Otter Raft
122
126
 
123
- The otter raft runs in two modes:
124
-
125
- ### Interactive Mode (Default)
126
-
127
127
  ```bash
128
- python -m stackwright_pro.raft.cli_adapter foreman --spawn
128
+ cd my-app
129
+ npx @stackwright-pro/raft
129
130
  ```
130
131
 
131
- This spawns code-puppy with the foreman system prompt. The foreman:
132
-
133
- - Asks questions about your project via TUI
134
- - Coordinates specialist otters (brand, theme, api, auth, page)
135
- - Uses certificate pinning to ensure only approved otters are invoked
136
-
137
- ### Phases Mode (Debugging)
138
-
139
- ```bash
140
- python -m stackwright_pro.raft.cli_adapter foreman --phases
141
- ```
132
+ The raft verifies otter integrity, loads project context from `.stackwright/init-context.json`, and spawns code-puppy in foreman mode. Tell the foreman about your specs in natural language — it coordinates the API, auth, data, and page otters to build your app.
142
133
 
143
- Executes phases sequentially via the Python state machine. Useful for debugging.
134
+ All state lives in `.stackwright/` you can interrupt and resume at any time.
package/dist/index.js CHANGED
@@ -31,9 +31,9 @@ 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.14",
34
+ version: "0.4.0-alpha.141",
35
35
  description: "Launch a new Stackwright Pro project with OpenAPI integration, auth, and the otter raft",
36
- license: "MIT",
36
+ license: "SEE LICENSE IN LICENSE",
37
37
  publishConfig: {
38
38
  access: "public"
39
39
  },
@@ -69,21 +69,21 @@ var require_package = __commonJS({
69
69
  "@stackwright-pro/mcp": "workspace:*",
70
70
  "@stackwright-pro/openapi": "workspace:*",
71
71
  "@stackwright-pro/otters": "workspace:*",
72
- "@stackwright-pro/scaffold-hooks": "latest",
73
- "@stackwright/cli": "^0.7.1-alpha.0",
74
- "@stackwright/scaffold-core": "^0.1.0",
72
+ "@stackwright-pro/scaffold-hooks": "workspace:*",
73
+ "@stackwright/cli": "^0.9.0",
74
+ "@stackwright/scaffold-core": "^0.3.3",
75
75
  chalk: "^5.6.2",
76
- commander: "^14.0.3",
77
- "fs-extra": "^11.3",
78
- "js-yaml": "^4.1.0"
76
+ commander: "^15.0.0",
77
+ "fs-extra": "^11.3.5",
78
+ "js-yaml": "^4.2.0"
79
79
  },
80
80
  devDependencies: {
81
81
  "@types/fs-extra": "^11.0",
82
82
  "@types/js-yaml": "^4.0.9",
83
- "@types/node": "^24.0.0",
84
- typescript: "^5.0",
85
- tsup: "^8.5",
86
- vitest: "^4.0.18"
83
+ "@types/node": "catalog:",
84
+ tsup: "catalog:",
85
+ typescript: "catalog:",
86
+ vitest: "catalog:"
87
87
  }
88
88
  };
89
89
  }
@@ -91,89 +91,84 @@ var require_package = __commonJS({
91
91
 
92
92
  // src/index.ts
93
93
  var import_commander = require("commander");
94
- var import_path = __toESM(require("path"));
94
+ var import_path2 = __toESM(require("path"));
95
95
  var import_os = __toESM(require("os"));
96
- var import_fs_extra = __toESM(require("fs-extra"));
96
+ var import_fs_extra2 = __toESM(require("fs-extra"));
97
97
  var import_chalk = __toESM(require("chalk"));
98
- var import_js_yaml = __toESM(require("js-yaml"));
99
98
  var import_child_process = require("child_process");
100
99
  var import_cli = require("@stackwright/cli");
101
100
  var import_scaffold_core = require("@stackwright/scaffold-core");
102
101
  var import_scaffold_hooks = require("@stackwright-pro/scaffold-hooks");
103
102
 
103
+ // src/scaffold.ts
104
+ var import_fs_extra = __toESM(require("fs-extra"));
105
+ var import_js_yaml = __toESM(require("js-yaml"));
106
+ var import_path = __toESM(require("path"));
107
+
104
108
  // src/generated/workspace-versions.ts
105
109
  var PRO_VERSIONS = {
106
- "@stackwright-pro/auth": "0.1.1-alpha.0",
107
- "@stackwright-pro/auth-nextjs": "0.1.1-alpha.0",
108
- "@stackwright-pro/mcp": "0.2.0-alpha.4",
109
- "@stackwright-pro/openapi": "0.2.2-alpha.2",
110
- "@stackwright-pro/otters": "1.0.0-alpha.12"
110
+ "@stackwright-pro/auth": "0.2.0-alpha.22",
111
+ "@stackwright-pro/auth-nextjs": "0.3.0-alpha.11",
112
+ "@stackwright-pro/build-scripts-plugins": "0.1.0-alpha.7",
113
+ "@stackwright-pro/display-components": "0.2.0-alpha.11",
114
+ "@stackwright-pro/mcp": "0.2.0-alpha.103",
115
+ "@stackwright-pro/openapi": "0.3.0-alpha.36",
116
+ "@stackwright-pro/otters": "1.0.0-alpha.70",
117
+ "@stackwright-pro/pulse": "0.3.0-alpha.32",
118
+ "@stackwright-pro/workflow": "0.1.0-alpha.25",
119
+ "@stackwright-pro/workflow-components": "0.1.0-alpha.27"
111
120
  };
112
121
  var OSS_VERSIONS = {
113
- "@stackwright/build-scripts": "^0.4.0",
114
- "@stackwright/core": "^0.7.0",
115
- "@stackwright/icons": "^0.3.0",
116
- "@stackwright/mcp": "^0.2.0",
117
- "@stackwright/nextjs": "^0.3.1",
118
- "@stackwright/otters": "^0.2.0",
119
- "@stackwright/ui-shadcn": "^0.1.0"
122
+ "@stackwright/build-scripts": "0.10.0-alpha.0",
123
+ "@stackwright/collections": "^0.1.2",
124
+ "@stackwright/core": "^0.10.0",
125
+ "@stackwright/icons": "^0.5.2",
126
+ "@stackwright/mcp": "^0.5.0",
127
+ "@stackwright/nextjs": "^0.6.0",
128
+ "@stackwright/otters": "^0.2.2",
129
+ "@stackwright/types": "^1.7.0",
130
+ "@stackwright/ui-shadcn": "^0.1.3"
120
131
  };
121
132
 
122
- // src/index.ts
123
- (0, import_scaffold_hooks.registerProScaffoldHooks)();
124
- var { version } = require_package();
125
- async function copyProTemplate(templateName, destPath) {
126
- const src = import_path.default.resolve(__dirname, "..", "templates", "pro", templateName);
127
- await import_fs_extra.default.copy(src, destPath);
128
- }
129
- async function addAuthToStackwrightYml(targetDir) {
130
- const ymlPath = import_path.default.join(targetDir, "stackwright.yml");
131
- const content = await import_fs_extra.default.readFile(ymlPath, "utf-8");
132
- const config = import_js_yaml.default.load(content, { schema: import_js_yaml.default.CORE_SCHEMA });
133
- config.auth = {
134
- type: "pki",
135
- profile: "dod_cac",
136
- source: "gateway_headers",
137
- roles: [
138
- { name: "ADMIN", permissions: ["*"] },
139
- { name: "ANALYST", permissions: ["data:read", "data:write"] },
140
- { name: "VIEWER", permissions: ["data:read"] }
141
- ],
142
- protected_routes: [{ path: "/*", roles: ["VIEWER", "ANALYST", "ADMIN"] }],
143
- public_routes: ["/", "/getting-started"]
144
- };
145
- await import_fs_extra.default.writeFile(ymlPath, import_js_yaml.default.dump(config, { lineWidth: 120 }));
146
- }
133
+ // src/scaffold.ts
147
134
  async function addProDepsToPackageJson(targetDir) {
148
135
  const pkgPath = import_path.default.join(targetDir, "package.json");
149
136
  const content = await import_fs_extra.default.readFile(pkgPath, "utf-8");
150
137
  const pkg = JSON.parse(content);
151
- const dependencies = pkg.dependencies || {};
152
- const devDependencies = pkg.devDependencies || {};
153
- const scripts = pkg.scripts || {};
138
+ const dependencies = typeof pkg.dependencies === "object" && pkg.dependencies !== null ? { ...pkg.dependencies } : {};
139
+ const devDependencies = typeof pkg.devDependencies === "object" && pkg.devDependencies !== null ? { ...pkg.devDependencies } : {};
140
+ const scripts = typeof pkg.scripts === "object" && pkg.scripts !== null ? { ...pkg.scripts } : {};
154
141
  for (const [pkgName, pinnedVersion] of Object.entries(OSS_VERSIONS)) {
155
- if (dependencies[pkgName] === "workspace:*") {
142
+ if (pkgName in dependencies) {
156
143
  dependencies[pkgName] = pinnedVersion;
157
144
  }
158
- if (devDependencies[pkgName] === "workspace:*") {
145
+ if (pkgName in devDependencies) {
159
146
  devDependencies[pkgName] = pinnedVersion;
160
147
  }
161
148
  }
162
149
  Object.assign(dependencies, {
163
- "@stackwright-pro/mcp": PRO_VERSIONS["@stackwright-pro/mcp"],
164
- "@stackwright-pro/otters": PRO_VERSIONS["@stackwright-pro/otters"],
165
- "@stackwright-pro/openapi": PRO_VERSIONS["@stackwright-pro/openapi"],
166
150
  "@stackwright-pro/auth": PRO_VERSIONS["@stackwright-pro/auth"],
167
151
  "@stackwright-pro/auth-nextjs": PRO_VERSIONS["@stackwright-pro/auth-nextjs"],
168
- zod: "^3.23.0"
152
+ "@stackwright-pro/build-scripts-plugins": PRO_VERSIONS["@stackwright-pro/build-scripts-plugins"],
153
+ "@stackwright-pro/display-components": PRO_VERSIONS["@stackwright-pro/display-components"],
154
+ "@stackwright-pro/mcp": PRO_VERSIONS["@stackwright-pro/mcp"],
155
+ "@stackwright-pro/openapi": PRO_VERSIONS["@stackwright-pro/openapi"],
156
+ "@stackwright-pro/otters": PRO_VERSIONS["@stackwright-pro/otters"],
157
+ "@stackwright-pro/pulse": PRO_VERSIONS["@stackwright-pro/pulse"],
158
+ "@stackwright-pro/workflow": PRO_VERSIONS["@stackwright-pro/workflow"],
159
+ "@stackwright-pro/workflow-components": PRO_VERSIONS["@stackwright-pro/workflow-components"],
160
+ "@stackwright/build-scripts": OSS_VERSIONS["@stackwright/build-scripts"],
161
+ zod: "^4.0.0"
169
162
  });
170
163
  Object.assign(devDependencies, {
171
- "@stoplight/prism-cli": "^5.14.2"
164
+ "@stoplight/prism-cli": "^5.15.10",
165
+ concurrently: "^9.0.0"
172
166
  });
173
167
  Object.assign(scripts, {
174
- "dev:admin": "MOCK_USER=admin next dev",
175
- "dev:analyst": "MOCK_USER=analyst next dev",
176
- "dev:viewer": "MOCK_USER=viewer next dev",
168
+ mock: "node scripts/start-mocks.js",
169
+ "dev:admin": 'concurrently "pnpm mock" "NEXT_PUBLIC_MOCK_BACKEND=true MOCK_USER=admin next dev"',
170
+ "dev:analyst": 'concurrently "pnpm mock" "NEXT_PUBLIC_MOCK_BACKEND=true MOCK_USER=analyst next dev"',
171
+ "dev:viewer": 'concurrently "pnpm mock" "NEXT_PUBLIC_MOCK_BACKEND=true MOCK_USER=viewer next dev"',
177
172
  prebuild: "node scripts/prebuild.js",
178
173
  predev: "node scripts/prebuild.js"
179
174
  });
@@ -182,6 +177,13 @@ async function addProDepsToPackageJson(targetDir) {
182
177
  pkg.scripts = scripts;
183
178
  await import_fs_extra.default.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf-8");
184
179
  }
180
+ async function addAuthToStackwrightYml(targetDir) {
181
+ const ymlPath = import_path.default.join(targetDir, "stackwright.yml");
182
+ const content = await import_fs_extra.default.readFile(ymlPath, "utf-8");
183
+ const config = import_js_yaml.default.load(content, { schema: import_js_yaml.default.CORE_SCHEMA });
184
+ config.auth = {};
185
+ await import_fs_extra.default.writeFile(ymlPath, import_js_yaml.default.dump(config, { lineWidth: 120 }));
186
+ }
185
187
  async function addIntegrationToStackwrightYml(targetDir, specName, specFilename, mockUrl) {
186
188
  const ymlPath = import_path.default.join(targetDir, "stackwright.yml");
187
189
  const content = await import_fs_extra.default.readFile(ymlPath, "utf-8");
@@ -195,20 +197,71 @@ async function addIntegrationToStackwrightYml(targetDir, specName, specFilename,
195
197
  if (mockUrl) {
196
198
  integration.mockUrl = mockUrl;
197
199
  }
198
- config.integrations = [integration];
200
+ config.integrations = [
201
+ ...Array.isArray(config.integrations) ? config.integrations : [],
202
+ integration
203
+ ];
199
204
  await import_fs_extra.default.writeFile(ymlPath, import_js_yaml.default.dump(config, { lineWidth: 120 }));
200
205
  }
201
- async function handleSpec(targetDir, specPath, specName) {
202
- const resolvedSpec = import_path.default.resolve(specPath);
203
- if (!import_fs_extra.default.existsSync(resolvedSpec)) {
204
- throw new Error(`Spec file not found: ${resolvedSpec}`);
206
+ async function scaffoldWorkspaceConfig(targetDir) {
207
+ const workspaceDir = import_path.default.join(targetDir, ".code_puppy");
208
+ await import_fs_extra.default.ensureDir(workspaceDir);
209
+ const workspaceStats = await import_fs_extra.default.lstat(workspaceDir);
210
+ if (workspaceStats.isSymbolicLink()) {
211
+ throw new Error(".code_puppy/ is a symlink \u2014 refusing to write. Check for tampering.");
212
+ }
213
+ const agentsDir = import_path.default.join(workspaceDir, "agents");
214
+ await import_fs_extra.default.ensureDir(agentsDir);
215
+ if (await import_fs_extra.default.pathExists(agentsDir)) {
216
+ const agentsStats = await import_fs_extra.default.lstat(agentsDir);
217
+ if (agentsStats.isSymbolicLink()) {
218
+ throw new Error(".code_puppy/agents is a symlink \u2014 refusing to write. Check for tampering.");
219
+ }
220
+ }
221
+ const configPath = import_path.default.join(workspaceDir, "config.json");
222
+ let existingConfig = {};
223
+ if (import_fs_extra.default.existsSync(configPath)) {
224
+ try {
225
+ existingConfig = JSON.parse(await import_fs_extra.default.readFile(configPath, "utf-8"));
226
+ } catch {
227
+ }
228
+ }
229
+ if (await import_fs_extra.default.pathExists(configPath)) {
230
+ const configStats = await import_fs_extra.default.lstat(configPath);
231
+ if (configStats.isSymbolicLink()) {
232
+ throw new Error(
233
+ ".code_puppy/config.json is a symlink \u2014 refusing to write. Check for tampering."
234
+ );
235
+ }
236
+ }
237
+ await import_fs_extra.default.writeFile(
238
+ configPath,
239
+ JSON.stringify({ ...existingConfig, projectOnly: true }, null, 2) + "\n",
240
+ "utf-8"
241
+ );
242
+ const mcpServersPath = import_path.default.join(workspaceDir, "mcp_servers.json");
243
+ if (await import_fs_extra.default.pathExists(mcpServersPath)) {
244
+ const mcpStats = await import_fs_extra.default.lstat(mcpServersPath);
245
+ if (mcpStats.isSymbolicLink()) {
246
+ throw new Error(
247
+ ".code_puppy/mcp_servers.json is a symlink \u2014 refusing to write. Check for tampering."
248
+ );
249
+ }
250
+ }
251
+ if (!import_fs_extra.default.existsSync(mcpServersPath)) {
252
+ const mcpConfig = {
253
+ mcp_servers: {
254
+ "stackwright-pro-mcp": {
255
+ type: "stdio",
256
+ command: "pnpm",
257
+ args: ["exec", "stackwright-pro-mcp"],
258
+ enabled: true,
259
+ cwd: "${PROJECT_ROOT}"
260
+ }
261
+ }
262
+ };
263
+ await import_fs_extra.default.writeFile(mcpServersPath, JSON.stringify(mcpConfig, null, 2) + "\n", "utf-8");
205
264
  }
206
- const specFilename = import_path.default.basename(resolvedSpec);
207
- const derivedName = specName || import_path.default.basename(specFilename, import_path.default.extname(specFilename));
208
- const specsDir = import_path.default.join(targetDir, "specs");
209
- await import_fs_extra.default.ensureDir(specsDir);
210
- await import_fs_extra.default.copy(resolvedSpec, import_path.default.join(specsDir, specFilename));
211
- return { specFilename, derivedName };
212
265
  }
213
266
  function generateReadme(options) {
214
267
  const { projectName, hasSpec, specName, specFilename, hasOtters, hasMock } = options;
@@ -231,7 +284,7 @@ export const EquipmentSchema = z.object({
231
284
  export type Equipment = z.infer<typeof EquipmentSchema>;
232
285
 
233
286
  // src/generated/${specName}/client.ts
234
- export class ${specName.charAt(0).toUpperCase() + specName.slice(1)}Client {
287
+ export class ${(specName ?? "api").charAt(0).toUpperCase() + (specName ?? "api").slice(1)}Client {
235
288
  async getEquipment(id: string): Promise<Equipment> {
236
289
  // Auto-wired with auth, validation, error handling
237
290
  }
@@ -244,10 +297,10 @@ export class ${specName.charAt(0).toUpperCase() + specName.slice(1)}Client {
244
297
  3. You import and use it:
245
298
 
246
299
  \`\`\`typescript
247
- import { ${specName.charAt(0).toUpperCase() + specName.slice(1)}Client } from '@/generated/${specName}/client';
300
+ import { ${(specName ?? "api").charAt(0).toUpperCase() + (specName ?? "api").slice(1)}Client } from '@/generated/${specName}/client';
248
301
  import type { Equipment } from '@/generated/${specName}/types';
249
302
 
250
- const client = new ${specName.charAt(0).toUpperCase() + specName.slice(1)}Client();
303
+ const client = new ${(specName ?? "api").charAt(0).toUpperCase() + (specName ?? "api").slice(1)}Client();
251
304
  const gear: Equipment = await client.getEquipment('123');
252
305
  \`\`\`
253
306
 
@@ -283,12 +336,10 @@ Your project includes **SIX specialized AI agents** in \`node_modules/@stackwrig
283
336
  **Start the otter raft:**
284
337
 
285
338
  \`\`\`bash
286
- uvx stackwright-pro-raft
339
+ npx @stackwright-pro/raft
287
340
  \`\`\`
288
341
 
289
- > **Local dev (before PyPI publish):** \`python -m stackwright_pro.raft.cli_adapter foreman\`
290
-
291
- 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.
342
+ The raft verifies otter integrity, writes project context, and spawns code-puppy in foreman mode. The foreman asks questions about your brand, auth, and data preferences, then coordinates specialist otters to build your app. All state lives in \`.stackwright/\` \u2014 interrupt and resume anytime.
292
343
 
293
344
  **Example session prompt:**
294
345
 
@@ -331,12 +382,16 @@ ${mockSection}## \u{1F4C1} Project Structure
331
382
 
332
383
  \`\`\`
333
384
  .
385
+ \u251C\u2500\u2500 app/
386
+ \u2502 \u251C\u2500\u2500 layout.tsx # Root layout (StackwrightLayout + Providers)
387
+ \u2502 \u251C\u2500\u2500 page.tsx # Home page (renders root content)
388
+ \u2502 \u251C\u2500\u2500 [...slug]/
389
+ \u2502 \u2502 \u2514\u2500\u2500 page.tsx # Dynamic pages from YAML content
390
+ \u2502 \u2514\u2500\u2500 _components/
391
+ \u2502 \u251C\u2500\u2500 providers.tsx # Pro: AuthProvider + component registration
392
+ \u2502 \u2514\u2500\u2500 page-client.tsx # Client-side page renderer
334
393
  \u251C\u2500\u2500 pages/
335
- \u2502 \u251C\u2500\u2500 _app.tsx # Pro version with AuthProvider + shadcn
336
- \u2502 \u251C\u2500\u2500 index.tsx # Home page (auto-generated by Stackwright)
337
- \u2502 \u251C\u2500\u2500 about/
338
- \u2502 \u2502 \u2514\u2500\u2500 content.yml # Page content in YAML
339
- \u2502 \u2514\u2500\u2500 ...
394
+ \u2502 \u2514\u2500\u2500 content.yml # Root page content in YAML
340
395
  \u251C\u2500\u2500 lib/
341
396
  \u2502 \u2514\u2500\u2500 mock-auth.ts # Dev-only auth mocking (no backend needed)
342
397
  \u251C\u2500\u2500 scripts/
@@ -356,7 +411,7 @@ ${mockSection}## \u{1F4C1} Project Structure
356
411
  \`\`\`
357
412
 
358
413
  **Key Files:**
359
- - \`pages/_app.tsx\` - Wraps your app with \`AuthProvider\` for RBAC
414
+ - \`app/_components/providers.tsx\` - Wraps your app with \`AuthProvider\` for RBAC
360
415
  - \`stackwright.yml\` - Single source of truth for theme, auth roles, and API wiring
361
416
  - \`lib/mock-auth.ts\` - Mocks CAC/PIV headers locally so you can test auth flows
362
417
  - \`scripts/prebuild.js\` - Runs before dev/build to generate types from your OpenAPI spec
@@ -466,8 +521,28 @@ ${aiAgentsSection}## \u{1F4DA} Learn More
466
521
  **Questions?** File an issue or ping us in the discussions. We're here to help you ship faster.
467
522
  `;
468
523
  }
524
+
525
+ // src/index.ts
526
+ (0, import_scaffold_hooks.registerProScaffoldHooks)();
527
+ var { version } = require_package();
528
+ async function copyProTemplate(templateName, destPath) {
529
+ const src = import_path2.default.resolve(__dirname, "..", "templates", "pro", templateName);
530
+ await import_fs_extra2.default.copy(src, destPath);
531
+ }
532
+ async function handleSpec(targetDir, specPath, specName) {
533
+ const resolvedSpec = import_path2.default.resolve(specPath);
534
+ if (!import_fs_extra2.default.existsSync(resolvedSpec)) {
535
+ throw new Error(`Spec file not found: ${resolvedSpec}`);
536
+ }
537
+ const specFilename = import_path2.default.basename(resolvedSpec);
538
+ const derivedName = specName || import_path2.default.basename(specFilename, import_path2.default.extname(specFilename));
539
+ const specsDir = import_path2.default.join(targetDir, "specs");
540
+ await import_fs_extra2.default.ensureDir(specsDir);
541
+ await import_fs_extra2.default.copy(resolvedSpec, import_path2.default.join(specsDir, specFilename));
542
+ return { specFilename, derivedName };
543
+ }
469
544
  async function launch(targetDir, options) {
470
- const dirName = import_path.default.basename(targetDir);
545
+ const dirName = import_path2.default.basename(targetDir);
471
546
  console.log(import_chalk.default.cyan.bold("\n\u{1F6A2} Launching Stackwright Pro...\n"));
472
547
  const scaffoldOpts = {
473
548
  name: options.name || dirName,
@@ -483,11 +558,11 @@ async function launch(targetDir, options) {
483
558
  await addProDepsToPackageJson(targetDir);
484
559
  console.log(import_chalk.default.green("\u{1F9A6} Pro packages added to package.json"));
485
560
  const verifyPkg = JSON.parse(
486
- await import_fs_extra.default.readFile(import_path.default.join(targetDir, "package.json"), "utf-8")
561
+ await import_fs_extra2.default.readFile(import_path2.default.join(targetDir, "package.json"), "utf-8")
487
562
  );
488
- const proDepCount = Object.keys(verifyPkg.dependencies || {}).filter(
489
- (k) => k.startsWith("@stackwright-pro")
490
- ).length;
563
+ const proDepCount = Object.keys(
564
+ typeof verifyPkg.dependencies === "object" && verifyPkg.dependencies !== null ? verifyPkg.dependencies : {}
565
+ ).filter((k) => k.startsWith("@stackwright-pro")).length;
491
566
  console.log(import_chalk.default.dim(` \u2192 package.json verified: ${proDepCount} @stackwright-pro/* deps ready`));
492
567
  console.log(import_chalk.default.cyan("\n\u{1F4E6} Installing dependencies (this takes a moment)..."));
493
568
  if (!options.skipInstall) {
@@ -522,33 +597,51 @@ async function launch(targetDir, options) {
522
597
  const dependencyMode = "standalone";
523
598
  await (0, import_scaffold_core.runScaffoldHooks)("postInstall", {
524
599
  targetDir,
525
- projectName: options.name || import_path.default.basename(targetDir),
526
- siteTitle: options.title || options.name || import_path.default.basename(targetDir),
600
+ projectName: options.name || import_path2.default.basename(targetDir),
601
+ siteTitle: options.title || options.name || import_path2.default.basename(targetDir),
527
602
  themeId: options.theme || "corporate",
528
603
  packageJson: {},
529
604
  dependencyMode
530
605
  });
531
- await copyProTemplate("_app.tsx", import_path.default.join(targetDir, "pages", "_app.tsx"));
532
- await copyProTemplate("content.yml", import_path.default.join(targetDir, "pages", "content.yml"));
533
- await copyProTemplate("next.config.js", import_path.default.join(targetDir, "next.config.js"));
534
- await import_fs_extra.default.ensureDir(import_path.default.join(targetDir, "lib"));
535
- await copyProTemplate("mock-auth.ts", import_path.default.join(targetDir, "lib", "mock-auth.ts"));
536
- await copyProTemplate("yaml.d.ts", import_path.default.join(targetDir, "yaml.d.ts"));
537
- await import_fs_extra.default.ensureDir(import_path.default.join(targetDir, "scripts"));
538
- await copyProTemplate("prebuild.js", import_path.default.join(targetDir, "scripts", "prebuild.js"));
539
- console.log(import_chalk.default.green("\u{1F510} Auth integration added (RBAC with 3 roles)"));
606
+ await copyProTemplate(
607
+ "pro-providers.tsx",
608
+ import_path2.default.join(targetDir, "app", "_components", "providers.tsx")
609
+ );
610
+ await copyProTemplate("content.yml", import_path2.default.join(targetDir, "pages", "content.yml"));
611
+ await copyProTemplate("next.config.js", import_path2.default.join(targetDir, "next.config.js"));
612
+ await import_fs_extra2.default.ensureDir(import_path2.default.join(targetDir, "lib"));
613
+ await copyProTemplate("mock-auth.ts", import_path2.default.join(targetDir, "lib", "mock-auth.ts"));
614
+ await copyProTemplate("yaml.d.ts", import_path2.default.join(targetDir, "yaml.d.ts"));
615
+ await import_fs_extra2.default.ensureDir(import_path2.default.join(targetDir, "scripts"));
616
+ await copyProTemplate("prebuild.js", import_path2.default.join(targetDir, "scripts", "prebuild.js"));
617
+ await copyProTemplate(
618
+ "scripts/pro-content-plugin.js",
619
+ import_path2.default.join(targetDir, "scripts", "pro-content-plugin.js")
620
+ );
621
+ await copyProTemplate(
622
+ "scripts/start-mocks.js",
623
+ import_path2.default.join(targetDir, "scripts", "start-mocks.js")
624
+ );
625
+ await import_fs_extra2.default.ensureDir(import_path2.default.join(targetDir, "app", "[...slug]"));
626
+ await copyProTemplate(
627
+ "app/slug-page/page.tsx",
628
+ import_path2.default.join(targetDir, "app", "[...slug]", "page.tsx")
629
+ );
630
+ console.log(import_chalk.default.green(" Auth integration added (App Router providers + RBAC)"));
540
631
  await addAuthToStackwrightYml(targetDir);
541
632
  let specInfo = null;
542
633
  const MOCK_URL = "http://localhost:4010";
543
- if (options.spec) {
544
- specInfo = await handleSpec(targetDir, options.spec, options.specName);
545
- await addIntegrationToStackwrightYml(
546
- targetDir,
547
- specInfo.derivedName,
548
- specInfo.specFilename,
549
- options.mock ? MOCK_URL : void 0
550
- );
551
- console.log(import_chalk.default.green("\u{1F4E1} OpenAPI integration wired up"));
634
+ if (options.spec && options.spec.length > 0) {
635
+ for (const specPath of options.spec) {
636
+ specInfo = await handleSpec(targetDir, specPath, options.specName);
637
+ await addIntegrationToStackwrightYml(
638
+ targetDir,
639
+ specInfo.derivedName,
640
+ specInfo.specFilename,
641
+ options.mock ? MOCK_URL : void 0
642
+ );
643
+ }
644
+ console.log(import_chalk.default.green(`\u{1F4E1} ${options.spec.length} OpenAPI integration(s) wired up`));
552
645
  if (options.mock) {
553
646
  console.log(import_chalk.default.green("\u{1F3AD} Prism mock server configured"));
554
647
  }
@@ -558,9 +651,9 @@ async function launch(targetDir, options) {
558
651
  "\u26A0\uFE0F No spec provided with --mock. Using sample Petstore spec for demo. Replace with your API spec."
559
652
  )
560
653
  );
561
- const specsDir = import_path.default.join(targetDir, "specs");
562
- await import_fs_extra.default.ensureDir(specsDir);
563
- await copyProTemplate("petstore.yaml", import_path.default.join(specsDir, "petstore.yaml"));
654
+ const specsDir = import_path2.default.join(targetDir, "specs");
655
+ await import_fs_extra2.default.ensureDir(specsDir);
656
+ await copyProTemplate("petstore.yaml", import_path2.default.join(specsDir, "petstore.yaml"));
564
657
  specInfo = { specFilename: "petstore.yaml", derivedName: "petstore" };
565
658
  await addIntegrationToStackwrightYml(
566
659
  targetDir,
@@ -579,13 +672,33 @@ async function launch(targetDir, options) {
579
672
  hasOtters: !options.skipOtters,
580
673
  hasMock: !!options.mock
581
674
  });
582
- await import_fs_extra.default.writeFile(import_path.default.join(targetDir, "README.md"), readmeContent);
675
+ await import_fs_extra2.default.writeFile(import_path2.default.join(targetDir, "README.md"), readmeContent);
583
676
  console.log(import_chalk.default.green("\u{1F4C4} README.md created"));
584
- const relDir = import_path.default.relative(process.cwd(), targetDir) || ".";
677
+ const initContext = {
678
+ projectRoot: targetDir,
679
+ projectName: options.name || dirName,
680
+ generatedBy: "launch-stackwright-pro",
681
+ version: "1.0"
682
+ };
683
+ if (specInfo) {
684
+ initContext.specPath = `specs/${specInfo.specFilename}`;
685
+ initContext.specName = specInfo.derivedName;
686
+ }
687
+ initContext.theme = options.theme || "corporate";
688
+ const stackwrightDir = import_path2.default.join(targetDir, ".stackwright");
689
+ await import_fs_extra2.default.ensureDir(stackwrightDir);
690
+ await import_fs_extra2.default.writeFile(
691
+ import_path2.default.join(stackwrightDir, "init-context.json"),
692
+ JSON.stringify(initContext, null, 2) + "\n",
693
+ "utf-8"
694
+ );
695
+ await scaffoldWorkspaceConfig(targetDir);
696
+ console.log(import_chalk.default.green("\u{1F43E} .code_puppy/ workspace scaffolded (projectOnly: true)"));
697
+ const relDir = import_path2.default.relative(process.cwd(), targetDir) || ".";
585
698
  console.log(import_chalk.default.cyan.bold("\n\u{1F389} All set! Here's what to do next:\n"));
586
699
  console.log(import_chalk.default.white(` 1. cd ${relDir}`));
587
- console.log(import_chalk.default.white(" 2. uvx stackwright-pro-raft"));
588
- console.log(import_chalk.default.dim(" (local dev: python -m stackwright_pro.raft.cli_adapter foreman)"));
700
+ console.log(import_chalk.default.white(" 2. npx @stackwright-pro/raft"));
701
+ console.log(import_chalk.default.dim(" (use --verbose for troubleshooting)"));
589
702
  if (specInfo) {
590
703
  console.log(import_chalk.default.cyan(`
591
704
  \u{1F4E1} Your API spec was copied to specs/${specInfo.specFilename}`));
@@ -603,8 +716,8 @@ async function launch(targetDir, options) {
603
716
  async function main() {
604
717
  const program = new import_commander.Command();
605
718
  program.name("launch-stackwright-pro").description("\u{1F6A2} Launch a new Stackwright Pro project with auth, OpenAPI, and the otter raft").version(version).argument("[directory]", "Project directory", ".").option("--name <name>", "Project name (used in package.json)").option("--title <title>", "Site title shown in the app bar and browser tab").option("--theme <themeId>", "Theme ID (e.g., corporate, creative, minimal)").option("--force", "Launch even if the target directory is not empty").option("--skip-otters", "Skip copying otter raft configs").option("-y, --yes", "Skip all prompts, use defaults").option("--mock", "Configure Prism mock server for API development (runs on port 4010)").option(
606
- "--spec <path>",
607
- "Path to an OpenAPI spec (YAML or JSON) \u2014 copies into project and wires up integration"
719
+ "--spec <paths...>",
720
+ "Paths to OpenAPI specs (YAML or JSON) \u2014 copies into project and wires up integrations (can be specified multiple times)"
608
721
  ).option(
609
722
  "--spec-name <name>",
610
723
  "Name for the API integration (default: derived from spec filename)"
@@ -613,8 +726,8 @@ async function main() {
613
726
  "Skip automatic pnpm install (useful in CI or when using a different package manager)"
614
727
  ).action(async (directory, options) => {
615
728
  const homeDir = import_os.default.homedir();
616
- const targetDir = import_path.default.resolve(directory);
617
- if (!targetDir.startsWith(homeDir + import_path.default.sep) && targetDir !== homeDir) {
729
+ const targetDir = directory !== "." ? import_path2.default.resolve(process.cwd(), directory) : options.name ? import_path2.default.resolve(process.cwd(), options.name) : process.cwd();
730
+ if (!targetDir.startsWith(homeDir + import_path2.default.sep) && targetDir !== homeDir) {
618
731
  console.warn(import_chalk.default.yellow(`\u26A0\uFE0F Scaffolding outside your home directory: ${targetDir}`));
619
732
  }
620
733
  await launch(targetDir, options);