@stackwright-pro/launch-stackwright-pro 0.4.0-alpha.9 → 0.4.0-alpha.94

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.7",
34
+ version: "0.4.0-alpha.93",
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
  },
@@ -57,27 +57,33 @@ var require_package = __commonJS({
57
57
  "launch-stackwright-pro": "dist/index.js"
58
58
  },
59
59
  scripts: {
60
- build: "tsup",
60
+ build: "node ../../scripts/sync-versions.mjs && tsup",
61
+ "sync-versions": "node ../../scripts/sync-versions.mjs",
61
62
  dev: "tsup --watch",
62
63
  test: "vitest run",
63
64
  "test:coverage": "vitest run --coverage"
64
65
  },
65
66
  dependencies: {
67
+ "@stackwright-pro/auth": "workspace:*",
68
+ "@stackwright-pro/auth-nextjs": "workspace:*",
69
+ "@stackwright-pro/mcp": "workspace:*",
70
+ "@stackwright-pro/openapi": "workspace:*",
71
+ "@stackwright-pro/otters": "workspace:*",
66
72
  "@stackwright-pro/scaffold-hooks": "workspace:*",
67
- "@stackwright/cli": "^0.7.0",
68
- "@stackwright/scaffold-core": "^0.1.0",
73
+ "@stackwright/cli": "^0.9.0",
74
+ "@stackwright/scaffold-core": "^0.3.2",
69
75
  chalk: "^5.6.2",
70
- commander: "^14.0.3",
71
- "fs-extra": "^11.3",
72
- "js-yaml": "^4.1.0"
76
+ commander: "^15.0.0",
77
+ "fs-extra": "^11.3.5",
78
+ "js-yaml": "^4.2.0"
73
79
  },
74
80
  devDependencies: {
75
81
  "@types/fs-extra": "^11.0",
76
82
  "@types/js-yaml": "^4.0.9",
77
- "@types/node": "^24.0.0",
78
- typescript: "^5.0",
79
- tsup: "^8.5",
80
- vitest: "^4.0.18"
83
+ "@types/node": "catalog:",
84
+ typescript: "catalog:",
85
+ tsup: "catalog:",
86
+ vitest: "catalog:"
81
87
  }
82
88
  };
83
89
  }
@@ -85,43 +91,99 @@ var require_package = __commonJS({
85
91
 
86
92
  // src/index.ts
87
93
  var import_commander = require("commander");
88
- var import_path = __toESM(require("path"));
94
+ var import_path2 = __toESM(require("path"));
89
95
  var import_os = __toESM(require("os"));
90
- var import_fs_extra = __toESM(require("fs-extra"));
96
+ var import_fs_extra2 = __toESM(require("fs-extra"));
91
97
  var import_chalk = __toESM(require("chalk"));
92
- var import_js_yaml = __toESM(require("js-yaml"));
93
98
  var import_child_process = require("child_process");
94
99
  var import_cli = require("@stackwright/cli");
95
100
  var import_scaffold_core = require("@stackwright/scaffold-core");
96
101
  var import_scaffold_hooks = require("@stackwright-pro/scaffold-hooks");
97
- (0, import_scaffold_hooks.registerProScaffoldHooks)();
98
- var { version } = require_package();
99
- async function copyProTemplate(templateName, destPath) {
100
- const src = import_path.default.resolve(__dirname, "..", "templates", "pro", templateName);
101
- await import_fs_extra.default.copy(src, destPath);
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
+
108
+ // src/generated/workspace-versions.ts
109
+ var PRO_VERSIONS = {
110
+ "@stackwright-pro/auth": "0.2.0-alpha.11",
111
+ "@stackwright-pro/auth-nextjs": "0.2.0-alpha.13",
112
+ "@stackwright-pro/display-components": "0.2.0-alpha.10",
113
+ "@stackwright-pro/mcp": "0.2.0-alpha.58",
114
+ "@stackwright-pro/openapi": "0.3.0-alpha.23",
115
+ "@stackwright-pro/otters": "1.0.0-alpha.43",
116
+ "@stackwright-pro/pulse": "0.3.0-alpha.16",
117
+ "@stackwright-pro/workflow": "0.1.0-alpha.15",
118
+ "@stackwright-pro/workflow-components": "0.1.0-alpha.17"
119
+ };
120
+ var OSS_VERSIONS = {
121
+ "@stackwright/build-scripts": "^0.8.0",
122
+ "@stackwright/collections": "^0.1.2",
123
+ "@stackwright/core": "^0.9.0",
124
+ "@stackwright/icons": "^0.5.2",
125
+ "@stackwright/mcp": "^0.5.0",
126
+ "@stackwright/nextjs": "^0.6.0",
127
+ "@stackwright/otters": "^0.2.2",
128
+ "@stackwright/types": "^1.6.0",
129
+ "@stackwright/ui-shadcn": "^0.1.3"
130
+ };
131
+
132
+ // src/scaffold.ts
133
+ async function addProDepsToPackageJson(targetDir) {
134
+ const pkgPath = import_path.default.join(targetDir, "package.json");
135
+ const content = await import_fs_extra.default.readFile(pkgPath, "utf-8");
136
+ const pkg = JSON.parse(content);
137
+ const dependencies = typeof pkg.dependencies === "object" && pkg.dependencies !== null ? { ...pkg.dependencies } : {};
138
+ const devDependencies = typeof pkg.devDependencies === "object" && pkg.devDependencies !== null ? { ...pkg.devDependencies } : {};
139
+ const scripts = typeof pkg.scripts === "object" && pkg.scripts !== null ? { ...pkg.scripts } : {};
140
+ for (const [pkgName, pinnedVersion] of Object.entries(OSS_VERSIONS)) {
141
+ if (pkgName in dependencies) {
142
+ dependencies[pkgName] = pinnedVersion;
143
+ }
144
+ if (pkgName in devDependencies) {
145
+ devDependencies[pkgName] = pinnedVersion;
146
+ }
147
+ }
148
+ Object.assign(dependencies, {
149
+ "@stackwright-pro/display-components": PRO_VERSIONS["@stackwright-pro/display-components"],
150
+ "@stackwright-pro/mcp": PRO_VERSIONS["@stackwright-pro/mcp"],
151
+ "@stackwright-pro/otters": PRO_VERSIONS["@stackwright-pro/otters"],
152
+ "@stackwright-pro/openapi": PRO_VERSIONS["@stackwright-pro/openapi"],
153
+ "@stackwright-pro/auth": PRO_VERSIONS["@stackwright-pro/auth"],
154
+ "@stackwright-pro/auth-nextjs": PRO_VERSIONS["@stackwright-pro/auth-nextjs"],
155
+ "@stackwright-pro/pulse": PRO_VERSIONS["@stackwright-pro/pulse"],
156
+ "@stackwright-pro/workflow-components": PRO_VERSIONS["@stackwright-pro/workflow-components"],
157
+ "@stackwright-pro/workflow": PRO_VERSIONS["@stackwright-pro/workflow"],
158
+ "@stackwright/build-scripts": OSS_VERSIONS["@stackwright/build-scripts"],
159
+ zod: "^4.0.0"
160
+ });
161
+ Object.assign(devDependencies, {
162
+ "@stoplight/prism-cli": "^5.15.10"
163
+ });
164
+ Object.assign(scripts, {
165
+ "dev:admin": "MOCK_USER=admin next dev",
166
+ "dev:analyst": "MOCK_USER=analyst next dev",
167
+ "dev:viewer": "MOCK_USER=viewer next dev",
168
+ prebuild: "node scripts/prebuild.js",
169
+ predev: "node scripts/prebuild.js"
170
+ });
171
+ pkg.dependencies = dependencies;
172
+ pkg.devDependencies = devDependencies;
173
+ pkg.scripts = scripts;
174
+ await import_fs_extra.default.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf-8");
102
175
  }
103
176
  async function addAuthToStackwrightYml(targetDir) {
104
177
  const ymlPath = import_path.default.join(targetDir, "stackwright.yml");
105
178
  const content = await import_fs_extra.default.readFile(ymlPath, "utf-8");
106
- const config = import_js_yaml.default.load(content);
107
- config.auth = {
108
- type: "pki",
109
- profile: "dod_cac",
110
- source: "gateway_headers",
111
- roles: [
112
- { name: "ADMIN", permissions: ["*"] },
113
- { name: "ANALYST", permissions: ["data:read", "data:write"] },
114
- { name: "VIEWER", permissions: ["data:read"] }
115
- ],
116
- protected_routes: [{ path: "/*", roles: ["VIEWER", "ANALYST", "ADMIN"] }],
117
- public_routes: ["/", "/getting-started"]
118
- };
179
+ const config = import_js_yaml.default.load(content, { schema: import_js_yaml.default.CORE_SCHEMA });
180
+ config.auth = {};
119
181
  await import_fs_extra.default.writeFile(ymlPath, import_js_yaml.default.dump(config, { lineWidth: 120 }));
120
182
  }
121
183
  async function addIntegrationToStackwrightYml(targetDir, specName, specFilename, mockUrl) {
122
184
  const ymlPath = import_path.default.join(targetDir, "stackwright.yml");
123
185
  const content = await import_fs_extra.default.readFile(ymlPath, "utf-8");
124
- const config = import_js_yaml.default.load(content);
186
+ const config = import_js_yaml.default.load(content, { schema: import_js_yaml.default.CORE_SCHEMA });
125
187
  const integration = {
126
188
  type: "openapi",
127
189
  name: specName,
@@ -131,20 +193,71 @@ async function addIntegrationToStackwrightYml(targetDir, specName, specFilename,
131
193
  if (mockUrl) {
132
194
  integration.mockUrl = mockUrl;
133
195
  }
134
- config.integrations = [integration];
196
+ config.integrations = [
197
+ ...Array.isArray(config.integrations) ? config.integrations : [],
198
+ integration
199
+ ];
135
200
  await import_fs_extra.default.writeFile(ymlPath, import_js_yaml.default.dump(config, { lineWidth: 120 }));
136
201
  }
137
- async function handleSpec(targetDir, specPath, specName) {
138
- const resolvedSpec = import_path.default.resolve(specPath);
139
- if (!import_fs_extra.default.existsSync(resolvedSpec)) {
140
- throw new Error(`Spec file not found: ${resolvedSpec}`);
202
+ async function scaffoldWorkspaceConfig(targetDir) {
203
+ const workspaceDir = import_path.default.join(targetDir, ".code_puppy");
204
+ await import_fs_extra.default.ensureDir(workspaceDir);
205
+ const workspaceStats = await import_fs_extra.default.lstat(workspaceDir);
206
+ if (workspaceStats.isSymbolicLink()) {
207
+ throw new Error(".code_puppy/ is a symlink \u2014 refusing to write. Check for tampering.");
208
+ }
209
+ const agentsDir = import_path.default.join(workspaceDir, "agents");
210
+ await import_fs_extra.default.ensureDir(agentsDir);
211
+ if (await import_fs_extra.default.pathExists(agentsDir)) {
212
+ const agentsStats = await import_fs_extra.default.lstat(agentsDir);
213
+ if (agentsStats.isSymbolicLink()) {
214
+ throw new Error(".code_puppy/agents is a symlink \u2014 refusing to write. Check for tampering.");
215
+ }
216
+ }
217
+ const configPath = import_path.default.join(workspaceDir, "config.json");
218
+ let existingConfig = {};
219
+ if (import_fs_extra.default.existsSync(configPath)) {
220
+ try {
221
+ existingConfig = JSON.parse(await import_fs_extra.default.readFile(configPath, "utf-8"));
222
+ } catch {
223
+ }
224
+ }
225
+ if (await import_fs_extra.default.pathExists(configPath)) {
226
+ const configStats = await import_fs_extra.default.lstat(configPath);
227
+ if (configStats.isSymbolicLink()) {
228
+ throw new Error(
229
+ ".code_puppy/config.json is a symlink \u2014 refusing to write. Check for tampering."
230
+ );
231
+ }
232
+ }
233
+ await import_fs_extra.default.writeFile(
234
+ configPath,
235
+ JSON.stringify({ ...existingConfig, projectOnly: true }, null, 2) + "\n",
236
+ "utf-8"
237
+ );
238
+ const mcpServersPath = import_path.default.join(workspaceDir, "mcp_servers.json");
239
+ if (await import_fs_extra.default.pathExists(mcpServersPath)) {
240
+ const mcpStats = await import_fs_extra.default.lstat(mcpServersPath);
241
+ if (mcpStats.isSymbolicLink()) {
242
+ throw new Error(
243
+ ".code_puppy/mcp_servers.json is a symlink \u2014 refusing to write. Check for tampering."
244
+ );
245
+ }
246
+ }
247
+ if (!import_fs_extra.default.existsSync(mcpServersPath)) {
248
+ const mcpConfig = {
249
+ mcp_servers: {
250
+ "stackwright-pro-mcp": {
251
+ type: "stdio",
252
+ command: "pnpm",
253
+ args: ["exec", "stackwright-pro-mcp"],
254
+ enabled: true,
255
+ cwd: "${PROJECT_ROOT}"
256
+ }
257
+ }
258
+ };
259
+ await import_fs_extra.default.writeFile(mcpServersPath, JSON.stringify(mcpConfig, null, 2) + "\n", "utf-8");
141
260
  }
142
- const specFilename = import_path.default.basename(resolvedSpec);
143
- const derivedName = specName || import_path.default.basename(specFilename, import_path.default.extname(specFilename));
144
- const specsDir = import_path.default.join(targetDir, "specs");
145
- await import_fs_extra.default.ensureDir(specsDir);
146
- await import_fs_extra.default.copy(resolvedSpec, import_path.default.join(specsDir, specFilename));
147
- return { specFilename, derivedName };
148
261
  }
149
262
  function generateReadme(options) {
150
263
  const { projectName, hasSpec, specName, specFilename, hasOtters, hasMock } = options;
@@ -167,7 +280,7 @@ export const EquipmentSchema = z.object({
167
280
  export type Equipment = z.infer<typeof EquipmentSchema>;
168
281
 
169
282
  // src/generated/${specName}/client.ts
170
- export class ${specName.charAt(0).toUpperCase() + specName.slice(1)}Client {
283
+ export class ${(specName ?? "api").charAt(0).toUpperCase() + (specName ?? "api").slice(1)}Client {
171
284
  async getEquipment(id: string): Promise<Equipment> {
172
285
  // Auto-wired with auth, validation, error handling
173
286
  }
@@ -180,10 +293,10 @@ export class ${specName.charAt(0).toUpperCase() + specName.slice(1)}Client {
180
293
  3. You import and use it:
181
294
 
182
295
  \`\`\`typescript
183
- import { ${specName.charAt(0).toUpperCase() + specName.slice(1)}Client } from '@/generated/${specName}/client';
296
+ import { ${(specName ?? "api").charAt(0).toUpperCase() + (specName ?? "api").slice(1)}Client } from '@/generated/${specName}/client';
184
297
  import type { Equipment } from '@/generated/${specName}/types';
185
298
 
186
- const client = new ${specName.charAt(0).toUpperCase() + specName.slice(1)}Client();
299
+ const client = new ${(specName ?? "api").charAt(0).toUpperCase() + (specName ?? "api").slice(1)}Client();
187
300
  const gear: Equipment = await client.getEquipment('123');
188
301
  \`\`\`
189
302
 
@@ -219,12 +332,10 @@ Your project includes **SIX specialized AI agents** in \`node_modules/@stackwrig
219
332
  **Start the otter raft:**
220
333
 
221
334
  \`\`\`bash
222
- uvx stackwright-pro-raft
335
+ npx @stackwright-pro/raft
223
336
  \`\`\`
224
337
 
225
- > **Local dev (before PyPI publish):** \`python -m stackwright_pro.raft.cli_adapter foreman\`
226
-
227
- 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.
338
+ 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.
228
339
 
229
340
  **Example session prompt:**
230
341
 
@@ -267,12 +378,16 @@ ${mockSection}## \u{1F4C1} Project Structure
267
378
 
268
379
  \`\`\`
269
380
  .
381
+ \u251C\u2500\u2500 app/
382
+ \u2502 \u251C\u2500\u2500 layout.tsx # Root layout (StackwrightLayout + Providers)
383
+ \u2502 \u251C\u2500\u2500 page.tsx # Home page (renders root content)
384
+ \u2502 \u251C\u2500\u2500 [...slug]/
385
+ \u2502 \u2502 \u2514\u2500\u2500 page.tsx # Dynamic pages from YAML content
386
+ \u2502 \u2514\u2500\u2500 _components/
387
+ \u2502 \u251C\u2500\u2500 providers.tsx # Pro: AuthProvider + component registration
388
+ \u2502 \u2514\u2500\u2500 page-client.tsx # Client-side page renderer
270
389
  \u251C\u2500\u2500 pages/
271
- \u2502 \u251C\u2500\u2500 _app.tsx # Pro version with AuthProvider + shadcn
272
- \u2502 \u251C\u2500\u2500 index.tsx # Home page (auto-generated by Stackwright)
273
- \u2502 \u251C\u2500\u2500 about/
274
- \u2502 \u2502 \u2514\u2500\u2500 content.yml # Page content in YAML
275
- \u2502 \u2514\u2500\u2500 ...
390
+ \u2502 \u2514\u2500\u2500 content.yml # Root page content in YAML
276
391
  \u251C\u2500\u2500 lib/
277
392
  \u2502 \u2514\u2500\u2500 mock-auth.ts # Dev-only auth mocking (no backend needed)
278
393
  \u251C\u2500\u2500 scripts/
@@ -292,7 +407,7 @@ ${mockSection}## \u{1F4C1} Project Structure
292
407
  \`\`\`
293
408
 
294
409
  **Key Files:**
295
- - \`pages/_app.tsx\` - Wraps your app with \`AuthProvider\` for RBAC
410
+ - \`app/_components/providers.tsx\` - Wraps your app with \`AuthProvider\` for RBAC
296
411
  - \`stackwright.yml\` - Single source of truth for theme, auth roles, and API wiring
297
412
  - \`lib/mock-auth.ts\` - Mocks CAC/PIV headers locally so you can test auth flows
298
413
  - \`scripts/prebuild.js\` - Runs before dev/build to generate types from your OpenAPI spec
@@ -402,11 +517,33 @@ ${aiAgentsSection}## \u{1F4DA} Learn More
402
517
  **Questions?** File an issue or ping us in the discussions. We're here to help you ship faster.
403
518
  `;
404
519
  }
520
+
521
+ // src/index.ts
522
+ (0, import_scaffold_hooks.registerProScaffoldHooks)();
523
+ var { version } = require_package();
524
+ async function copyProTemplate(templateName, destPath) {
525
+ const src = import_path2.default.resolve(__dirname, "..", "templates", "pro", templateName);
526
+ await import_fs_extra2.default.copy(src, destPath);
527
+ }
528
+ async function handleSpec(targetDir, specPath, specName) {
529
+ const resolvedSpec = import_path2.default.resolve(specPath);
530
+ if (!import_fs_extra2.default.existsSync(resolvedSpec)) {
531
+ throw new Error(`Spec file not found: ${resolvedSpec}`);
532
+ }
533
+ const specFilename = import_path2.default.basename(resolvedSpec);
534
+ const derivedName = specName || import_path2.default.basename(specFilename, import_path2.default.extname(specFilename));
535
+ const specsDir = import_path2.default.join(targetDir, "specs");
536
+ await import_fs_extra2.default.ensureDir(specsDir);
537
+ await import_fs_extra2.default.copy(resolvedSpec, import_path2.default.join(specsDir, specFilename));
538
+ return { specFilename, derivedName };
539
+ }
405
540
  async function launch(targetDir, options) {
406
- const dirName = import_path.default.basename(targetDir);
541
+ const dirName = import_path2.default.basename(targetDir);
407
542
  console.log(import_chalk.default.cyan.bold("\n\u{1F6A2} Launching Stackwright Pro...\n"));
408
543
  const scaffoldOpts = {
409
544
  name: options.name || dirName,
545
+ standalone: true,
546
+ // prevents `workspace:*` refs when CLI runs inside the Pro monorepo
410
547
  ...options.title !== void 0 && { title: options.title },
411
548
  ...options.theme !== void 0 && { theme: options.theme },
412
549
  ...options.force !== void 0 && { force: options.force },
@@ -414,13 +551,41 @@ async function launch(targetDir, options) {
414
551
  };
415
552
  await (0, import_cli.scaffold)(targetDir, scaffoldOpts);
416
553
  console.log(import_chalk.default.green("\u2705 Base project scaffolded (hooks added Pro deps + MCP config)"));
554
+ await addProDepsToPackageJson(targetDir);
555
+ console.log(import_chalk.default.green("\u{1F9A6} Pro packages added to package.json"));
556
+ const verifyPkg = JSON.parse(
557
+ await import_fs_extra2.default.readFile(import_path2.default.join(targetDir, "package.json"), "utf-8")
558
+ );
559
+ const proDepCount = Object.keys(
560
+ typeof verifyPkg.dependencies === "object" && verifyPkg.dependencies !== null ? verifyPkg.dependencies : {}
561
+ ).filter((k) => k.startsWith("@stackwright-pro")).length;
562
+ console.log(import_chalk.default.dim(` \u2192 package.json verified: ${proDepCount} @stackwright-pro/* deps ready`));
417
563
  console.log(import_chalk.default.cyan("\n\u{1F4E6} Installing dependencies (this takes a moment)..."));
418
564
  if (!options.skipInstall) {
419
565
  try {
420
- (0, import_child_process.execSync)("pnpm install", { cwd: targetDir, stdio: "inherit", timeout: 12e4 });
566
+ (0, import_child_process.execSync)("pnpm install --ignore-workspace", {
567
+ cwd: targetDir,
568
+ stdio: "inherit",
569
+ timeout: 12e4
570
+ });
421
571
  console.log(import_chalk.default.green("\u2705 Dependencies installed"));
422
- } catch {
423
- console.warn(import_chalk.default.yellow("\u26A0\uFE0F pnpm install failed \u2014 run it manually: pnpm install"));
572
+ } catch (err) {
573
+ const isTimeout = err instanceof Error && (err.message.includes("ETIMEDOUT") || err.message.includes("timeout"));
574
+ console.error(
575
+ import_chalk.default.red("\n\u274C pnpm install failed. Auth and Pro packages are NOT installed.")
576
+ );
577
+ console.error(
578
+ import_chalk.default.red(" Your project is in an incomplete state \u2014 do NOT run or deploy it.")
579
+ );
580
+ if (isTimeout) {
581
+ console.error(
582
+ import_chalk.default.yellow(" The install timed out (>2 min). Check your network connection.")
583
+ );
584
+ }
585
+ console.error(
586
+ import_chalk.default.yellow(` Fix the error above, then run: pnpm install --ignore-workspace`)
587
+ );
588
+ process.exit(1);
424
589
  }
425
590
  } else {
426
591
  console.log(import_chalk.default.dim("\u2139\uFE0F Skipping install (--skip-install). Run: pnpm install"));
@@ -428,33 +593,42 @@ async function launch(targetDir, options) {
428
593
  const dependencyMode = "standalone";
429
594
  await (0, import_scaffold_core.runScaffoldHooks)("postInstall", {
430
595
  targetDir,
431
- projectName: options.name || import_path.default.basename(targetDir),
432
- siteTitle: options.title || options.name || import_path.default.basename(targetDir),
596
+ projectName: options.name || import_path2.default.basename(targetDir),
597
+ siteTitle: options.title || options.name || import_path2.default.basename(targetDir),
433
598
  themeId: options.theme || "corporate",
434
599
  packageJson: {},
435
600
  dependencyMode
436
601
  });
437
- await copyProTemplate("_app.tsx", import_path.default.join(targetDir, "pages", "_app.tsx"));
438
- await copyProTemplate("content.yml", import_path.default.join(targetDir, "pages", "content.yml"));
439
- await copyProTemplate("next.config.js", import_path.default.join(targetDir, "next.config.js"));
440
- await import_fs_extra.default.ensureDir(import_path.default.join(targetDir, "lib"));
441
- await copyProTemplate("mock-auth.ts", import_path.default.join(targetDir, "lib", "mock-auth.ts"));
442
- await copyProTemplate("yaml.d.ts", import_path.default.join(targetDir, "yaml.d.ts"));
443
- await import_fs_extra.default.ensureDir(import_path.default.join(targetDir, "scripts"));
444
- await copyProTemplate("prebuild.js", import_path.default.join(targetDir, "scripts", "prebuild.js"));
445
- console.log(import_chalk.default.green("\u{1F510} Auth integration added (RBAC with 3 roles)"));
602
+ await copyProTemplate(
603
+ "pro-providers.tsx",
604
+ import_path2.default.join(targetDir, "app", "_components", "providers.tsx")
605
+ );
606
+ await copyProTemplate("content.yml", import_path2.default.join(targetDir, "pages", "content.yml"));
607
+ await copyProTemplate("next.config.js", import_path2.default.join(targetDir, "next.config.js"));
608
+ await import_fs_extra2.default.ensureDir(import_path2.default.join(targetDir, "lib"));
609
+ await copyProTemplate("mock-auth.ts", import_path2.default.join(targetDir, "lib", "mock-auth.ts"));
610
+ await copyProTemplate("yaml.d.ts", import_path2.default.join(targetDir, "yaml.d.ts"));
611
+ await import_fs_extra2.default.ensureDir(import_path2.default.join(targetDir, "scripts"));
612
+ await copyProTemplate("prebuild.js", import_path2.default.join(targetDir, "scripts", "prebuild.js"));
613
+ await copyProTemplate(
614
+ "scripts/pro-content-plugin.js",
615
+ import_path2.default.join(targetDir, "scripts", "pro-content-plugin.js")
616
+ );
617
+ console.log(import_chalk.default.green(" Auth integration added (App Router providers + RBAC)"));
446
618
  await addAuthToStackwrightYml(targetDir);
447
619
  let specInfo = null;
448
620
  const MOCK_URL = "http://localhost:4010";
449
- if (options.spec) {
450
- specInfo = await handleSpec(targetDir, options.spec, options.specName);
451
- await addIntegrationToStackwrightYml(
452
- targetDir,
453
- specInfo.derivedName,
454
- specInfo.specFilename,
455
- options.mock ? MOCK_URL : void 0
456
- );
457
- console.log(import_chalk.default.green("\u{1F4E1} OpenAPI integration wired up"));
621
+ if (options.spec && options.spec.length > 0) {
622
+ for (const specPath of options.spec) {
623
+ specInfo = await handleSpec(targetDir, specPath, options.specName);
624
+ await addIntegrationToStackwrightYml(
625
+ targetDir,
626
+ specInfo.derivedName,
627
+ specInfo.specFilename,
628
+ options.mock ? MOCK_URL : void 0
629
+ );
630
+ }
631
+ console.log(import_chalk.default.green(`\u{1F4E1} ${options.spec.length} OpenAPI integration(s) wired up`));
458
632
  if (options.mock) {
459
633
  console.log(import_chalk.default.green("\u{1F3AD} Prism mock server configured"));
460
634
  }
@@ -464,9 +638,9 @@ async function launch(targetDir, options) {
464
638
  "\u26A0\uFE0F No spec provided with --mock. Using sample Petstore spec for demo. Replace with your API spec."
465
639
  )
466
640
  );
467
- const specsDir = import_path.default.join(targetDir, "specs");
468
- await import_fs_extra.default.ensureDir(specsDir);
469
- await copyProTemplate("petstore.yaml", import_path.default.join(specsDir, "petstore.yaml"));
641
+ const specsDir = import_path2.default.join(targetDir, "specs");
642
+ await import_fs_extra2.default.ensureDir(specsDir);
643
+ await copyProTemplate("petstore.yaml", import_path2.default.join(specsDir, "petstore.yaml"));
470
644
  specInfo = { specFilename: "petstore.yaml", derivedName: "petstore" };
471
645
  await addIntegrationToStackwrightYml(
472
646
  targetDir,
@@ -485,13 +659,33 @@ async function launch(targetDir, options) {
485
659
  hasOtters: !options.skipOtters,
486
660
  hasMock: !!options.mock
487
661
  });
488
- await import_fs_extra.default.writeFile(import_path.default.join(targetDir, "README.md"), readmeContent);
662
+ await import_fs_extra2.default.writeFile(import_path2.default.join(targetDir, "README.md"), readmeContent);
489
663
  console.log(import_chalk.default.green("\u{1F4C4} README.md created"));
490
- const relDir = import_path.default.relative(process.cwd(), targetDir) || ".";
664
+ const initContext = {
665
+ projectRoot: targetDir,
666
+ projectName: options.name || dirName,
667
+ generatedBy: "launch-stackwright-pro",
668
+ version: "1.0"
669
+ };
670
+ if (specInfo) {
671
+ initContext.specPath = `specs/${specInfo.specFilename}`;
672
+ initContext.specName = specInfo.derivedName;
673
+ }
674
+ initContext.theme = options.theme || "corporate";
675
+ const stackwrightDir = import_path2.default.join(targetDir, ".stackwright");
676
+ await import_fs_extra2.default.ensureDir(stackwrightDir);
677
+ await import_fs_extra2.default.writeFile(
678
+ import_path2.default.join(stackwrightDir, "init-context.json"),
679
+ JSON.stringify(initContext, null, 2) + "\n",
680
+ "utf-8"
681
+ );
682
+ await scaffoldWorkspaceConfig(targetDir);
683
+ console.log(import_chalk.default.green("\u{1F43E} .code_puppy/ workspace scaffolded (projectOnly: true)"));
684
+ const relDir = import_path2.default.relative(process.cwd(), targetDir) || ".";
491
685
  console.log(import_chalk.default.cyan.bold("\n\u{1F389} All set! Here's what to do next:\n"));
492
686
  console.log(import_chalk.default.white(` 1. cd ${relDir}`));
493
- console.log(import_chalk.default.white(" 2. uvx stackwright-pro-raft"));
494
- console.log(import_chalk.default.dim(" (local dev: python -m stackwright_pro.raft.cli_adapter foreman)"));
687
+ console.log(import_chalk.default.white(" 2. npx @stackwright-pro/raft"));
688
+ console.log(import_chalk.default.dim(" (use --verbose for troubleshooting)"));
495
689
  if (specInfo) {
496
690
  console.log(import_chalk.default.cyan(`
497
691
  \u{1F4E1} Your API spec was copied to specs/${specInfo.specFilename}`));
@@ -509,8 +703,8 @@ async function launch(targetDir, options) {
509
703
  async function main() {
510
704
  const program = new import_commander.Command();
511
705
  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(
512
- "--spec <path>",
513
- "Path to an OpenAPI spec (YAML or JSON) \u2014 copies into project and wires up integration"
706
+ "--spec <paths...>",
707
+ "Paths to OpenAPI specs (YAML or JSON) \u2014 copies into project and wires up integrations (can be specified multiple times)"
514
708
  ).option(
515
709
  "--spec-name <name>",
516
710
  "Name for the API integration (default: derived from spec filename)"
@@ -519,8 +713,8 @@ async function main() {
519
713
  "Skip automatic pnpm install (useful in CI or when using a different package manager)"
520
714
  ).action(async (directory, options) => {
521
715
  const homeDir = import_os.default.homedir();
522
- const targetDir = import_path.default.resolve(directory);
523
- if (!targetDir.startsWith(homeDir + import_path.default.sep) && targetDir !== homeDir) {
716
+ const targetDir = directory !== "." ? import_path2.default.resolve(process.cwd(), directory) : options.name ? import_path2.default.resolve(process.cwd(), options.name) : process.cwd();
717
+ if (!targetDir.startsWith(homeDir + import_path2.default.sep) && targetDir !== homeDir) {
524
718
  console.warn(import_chalk.default.yellow(`\u26A0\uFE0F Scaffolding outside your home directory: ${targetDir}`));
525
719
  }
526
720
  await launch(targetDir, options);