@stackwright-pro/launch-stackwright-pro 0.4.0-alpha.1 → 0.4.0-alpha.101

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -31,9 +31,12 @@ 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.0",
34
+ version: "0.4.0-alpha.96",
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
+ publishConfig: {
38
+ access: "public"
39
+ },
37
40
  repository: {
38
41
  type: "git",
39
42
  url: "https://github.com/Per-Aspera-LLC/stackwright-pro"
@@ -54,27 +57,33 @@ var require_package = __commonJS({
54
57
  "launch-stackwright-pro": "dist/index.js"
55
58
  },
56
59
  scripts: {
57
- build: "tsup",
60
+ build: "node ../../scripts/sync-versions.mjs && tsup",
61
+ "sync-versions": "node ../../scripts/sync-versions.mjs",
58
62
  dev: "tsup --watch",
59
63
  test: "vitest run",
60
64
  "test:coverage": "vitest run --coverage"
61
65
  },
62
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:*",
63
72
  "@stackwright-pro/scaffold-hooks": "workspace:*",
64
- "@stackwright/cli": "^0.7.0",
65
- "@stackwright/scaffold-core": "^0.1.0",
73
+ "@stackwright/cli": "^0.9.0",
74
+ "@stackwright/scaffold-core": "^0.3.2",
66
75
  chalk: "^5.6.2",
67
- commander: "^14.0.3",
68
- "fs-extra": "^11.3",
69
- "js-yaml": "^4.1.0"
76
+ commander: "^15.0.0",
77
+ "fs-extra": "^11.3.5",
78
+ "js-yaml": "^4.2.0"
70
79
  },
71
80
  devDependencies: {
72
81
  "@types/fs-extra": "^11.0",
73
82
  "@types/js-yaml": "^4.0.9",
74
- "@types/node": "^24.0.0",
75
- typescript: "^5.0",
76
- tsup: "^8.5",
77
- vitest: "^4.0.18"
83
+ "@types/node": "catalog:",
84
+ typescript: "catalog:",
85
+ tsup: "catalog:",
86
+ vitest: "catalog:"
78
87
  }
79
88
  };
80
89
  }
@@ -82,40 +91,99 @@ var require_package = __commonJS({
82
91
 
83
92
  // src/index.ts
84
93
  var import_commander = require("commander");
85
- var import_path = __toESM(require("path"));
86
- var import_fs_extra = __toESM(require("fs-extra"));
94
+ var import_path2 = __toESM(require("path"));
95
+ var import_os = __toESM(require("os"));
96
+ var import_fs_extra2 = __toESM(require("fs-extra"));
87
97
  var import_chalk = __toESM(require("chalk"));
88
- var import_js_yaml = __toESM(require("js-yaml"));
98
+ var import_child_process = require("child_process");
89
99
  var import_cli = require("@stackwright/cli");
100
+ var import_scaffold_core = require("@stackwright/scaffold-core");
90
101
  var import_scaffold_hooks = require("@stackwright-pro/scaffold-hooks");
91
- (0, import_scaffold_hooks.registerProScaffoldHooks)();
92
- var { version } = require_package();
93
- async function copyProTemplate(templateName, destPath) {
94
- const src = import_path.default.resolve(__dirname, "..", "templates", "pro", templateName);
95
- 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.61",
114
+ "@stackwright-pro/openapi": "0.3.0-alpha.24",
115
+ "@stackwright-pro/otters": "1.0.0-alpha.45",
116
+ "@stackwright-pro/pulse": "0.3.0-alpha.17",
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");
96
175
  }
97
176
  async function addAuthToStackwrightYml(targetDir) {
98
177
  const ymlPath = import_path.default.join(targetDir, "stackwright.yml");
99
178
  const content = await import_fs_extra.default.readFile(ymlPath, "utf-8");
100
- const config = import_js_yaml.default.load(content);
101
- config.auth = {
102
- type: "pki",
103
- profile: "dod_cac",
104
- source: "gateway_headers",
105
- roles: [
106
- { name: "ADMIN", permissions: ["*"] },
107
- { name: "ANALYST", permissions: ["data:read", "data:write"] },
108
- { name: "VIEWER", permissions: ["data:read"] }
109
- ],
110
- protected_routes: [{ path: "/*", roles: ["VIEWER", "ANALYST", "ADMIN"] }],
111
- public_routes: ["/", "/getting-started"]
112
- };
179
+ const config = import_js_yaml.default.load(content, { schema: import_js_yaml.default.CORE_SCHEMA });
180
+ config.auth = {};
113
181
  await import_fs_extra.default.writeFile(ymlPath, import_js_yaml.default.dump(config, { lineWidth: 120 }));
114
182
  }
115
183
  async function addIntegrationToStackwrightYml(targetDir, specName, specFilename, mockUrl) {
116
184
  const ymlPath = import_path.default.join(targetDir, "stackwright.yml");
117
185
  const content = await import_fs_extra.default.readFile(ymlPath, "utf-8");
118
- const config = import_js_yaml.default.load(content);
186
+ const config = import_js_yaml.default.load(content, { schema: import_js_yaml.default.CORE_SCHEMA });
119
187
  const integration = {
120
188
  type: "openapi",
121
189
  name: specName,
@@ -125,20 +193,71 @@ async function addIntegrationToStackwrightYml(targetDir, specName, specFilename,
125
193
  if (mockUrl) {
126
194
  integration.mockUrl = mockUrl;
127
195
  }
128
- config.integrations = [integration];
196
+ config.integrations = [
197
+ ...Array.isArray(config.integrations) ? config.integrations : [],
198
+ integration
199
+ ];
129
200
  await import_fs_extra.default.writeFile(ymlPath, import_js_yaml.default.dump(config, { lineWidth: 120 }));
130
201
  }
131
- async function handleSpec(targetDir, specPath, specName) {
132
- const resolvedSpec = import_path.default.resolve(specPath);
133
- if (!import_fs_extra.default.existsSync(resolvedSpec)) {
134
- 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");
135
260
  }
136
- const specFilename = import_path.default.basename(resolvedSpec);
137
- const derivedName = specName || import_path.default.basename(specFilename, import_path.default.extname(specFilename));
138
- const specsDir = import_path.default.join(targetDir, "specs");
139
- await import_fs_extra.default.ensureDir(specsDir);
140
- await import_fs_extra.default.copy(resolvedSpec, import_path.default.join(specsDir, specFilename));
141
- return { specFilename, derivedName };
142
261
  }
143
262
  function generateReadme(options) {
144
263
  const { projectName, hasSpec, specName, specFilename, hasOtters, hasMock } = options;
@@ -161,7 +280,7 @@ export const EquipmentSchema = z.object({
161
280
  export type Equipment = z.infer<typeof EquipmentSchema>;
162
281
 
163
282
  // src/generated/${specName}/client.ts
164
- export class ${specName.charAt(0).toUpperCase() + specName.slice(1)}Client {
283
+ export class ${(specName ?? "api").charAt(0).toUpperCase() + (specName ?? "api").slice(1)}Client {
165
284
  async getEquipment(id: string): Promise<Equipment> {
166
285
  // Auto-wired with auth, validation, error handling
167
286
  }
@@ -174,10 +293,10 @@ export class ${specName.charAt(0).toUpperCase() + specName.slice(1)}Client {
174
293
  3. You import and use it:
175
294
 
176
295
  \`\`\`typescript
177
- 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';
178
297
  import type { Equipment } from '@/generated/${specName}/types';
179
298
 
180
- 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();
181
300
  const gear: Equipment = await client.getEquipment('123');
182
301
  \`\`\`
183
302
 
@@ -201,26 +320,26 @@ The generated API client is automatically configured to use the mock server.
201
320
  ` : "";
202
321
  const aiAgentsSection = hasOtters ? `## \u{1F9A6} AI Agents (The Otter Raft)
203
322
 
204
- Your project includes **FOUR specialized AI agents** in \`.stackwright/\`:
323
+ Your project includes **SIX specialized AI agents** in \`node_modules/@stackwright-pro/otters/\`:
205
324
 
206
325
  - \`stackwright-pro-foreman-otter\` - Master coordinator that orchestrates all specialist otters
207
326
  - \`stackwright-pro-api-otter\` - Discovers entities from OpenAPI specs
208
- - \`stackwright-pro-data-otter\` - Configures endpoint filters & ISR
327
+ - \`stackwright-pro-auth-otter\` - Configures CAC/OIDC auth, RBAC rules, and audit middleware
328
+ - \`stackwright-pro-data-otter\` - Configures endpoint filters & ISR revalidation
329
+ - \`stackwright-pro-page-otter\` - Generates pages auto-wired with data, theme, and auth
209
330
  - \`stackwright-pro-dashboard-otter\` - Builds live data dashboards
210
331
 
211
- **Invoke the Foreman to build for you:**
332
+ **Start the otter raft:**
212
333
 
213
334
  \`\`\`bash
214
- uvx code-puppy -i -a stackwright-pro-foreman-otter
335
+ npx @stackwright-pro/raft
215
336
  \`\`\`
216
337
 
217
- **Example prompts:**
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.
218
339
 
219
- "Build me a logistics dashboard from our OpenAPI spec. Connect to /equipment and /supplies endpoints."
340
+ **Example session prompt:**
220
341
 
221
- "Add a maintenance schedule page that shows equipment due for service."
222
-
223
- The Pro Foreman coordinates specialist otters, validates outputs, and renders previews\u2014all with live API data.
342
+ "Build me a logistics dashboard from our OpenAPI spec. Connect to /equipment and /supplies endpoints."
224
343
 
225
344
  ` : "";
226
345
  return `# ${projectName}
@@ -230,10 +349,7 @@ A Stackwright Pro application with role-based auth, OpenAPI integration, and int
230
349
  ## \u{1F680} Quick Start
231
350
 
232
351
  \`\`\`bash
233
- # Install dependencies
234
- pnpm install
235
-
236
- # Start development server (no auth mock)
352
+ # Start development server
237
353
  pnpm dev
238
354
 
239
355
  # Open your browser
@@ -262,12 +378,16 @@ ${mockSection}## \u{1F4C1} Project Structure
262
378
 
263
379
  \`\`\`
264
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
265
389
  \u251C\u2500\u2500 pages/
266
- \u2502 \u251C\u2500\u2500 _app.tsx # Pro version with AuthProvider + shadcn
267
- \u2502 \u251C\u2500\u2500 index.tsx # Home page (auto-generated by Stackwright)
268
- \u2502 \u251C\u2500\u2500 about/
269
- \u2502 \u2502 \u2514\u2500\u2500 content.yml # Page content in YAML
270
- \u2502 \u2514\u2500\u2500 ...
390
+ \u2502 \u2514\u2500\u2500 content.yml # Root page content in YAML
271
391
  \u251C\u2500\u2500 lib/
272
392
  \u2502 \u2514\u2500\u2500 mock-auth.ts # Dev-only auth mocking (no backend needed)
273
393
  \u251C\u2500\u2500 scripts/
@@ -287,7 +407,7 @@ ${mockSection}## \u{1F4C1} Project Structure
287
407
  \`\`\`
288
408
 
289
409
  **Key Files:**
290
- - \`pages/_app.tsx\` - Wraps your app with \`AuthProvider\` for RBAC
410
+ - \`app/_components/providers.tsx\` - Wraps your app with \`AuthProvider\` for RBAC
291
411
  - \`stackwright.yml\` - Single source of truth for theme, auth roles, and API wiring
292
412
  - \`lib/mock-auth.ts\` - Mocks CAC/PIV headers locally so you can test auth flows
293
413
  - \`scripts/prebuild.js\` - Runs before dev/build to generate types from your OpenAPI spec
@@ -397,39 +517,118 @@ ${aiAgentsSection}## \u{1F4DA} Learn More
397
517
  **Questions?** File an issue or ping us in the discussions. We're here to help you ship faster.
398
518
  `;
399
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
+ }
400
540
  async function launch(targetDir, options) {
401
- const dirName = import_path.default.basename(targetDir);
541
+ const dirName = import_path2.default.basename(targetDir);
402
542
  console.log(import_chalk.default.cyan.bold("\n\u{1F6A2} Launching Stackwright Pro...\n"));
403
543
  const scaffoldOpts = {
404
544
  name: options.name || dirName,
405
- title: options.title,
406
- theme: options.theme,
407
- force: options.force,
408
- noInteractive: options.yes
545
+ standalone: true,
546
+ // prevents `workspace:*` refs when CLI runs inside the Pro monorepo
547
+ ...options.title !== void 0 && { title: options.title },
548
+ ...options.theme !== void 0 && { theme: options.theme },
549
+ ...options.force !== void 0 && { force: options.force },
550
+ ...options.yes !== void 0 && { noInteractive: options.yes }
409
551
  };
410
552
  await (0, import_cli.scaffold)(targetDir, scaffoldOpts);
411
553
  console.log(import_chalk.default.green("\u2705 Base project scaffolded (hooks added Pro deps + MCP config)"));
412
- await copyProTemplate("_app.tsx", import_path.default.join(targetDir, "pages", "_app.tsx"));
413
- await copyProTemplate("content.yml", import_path.default.join(targetDir, "pages", "content.yml"));
414
- await copyProTemplate("next.config.js", import_path.default.join(targetDir, "next.config.js"));
415
- await import_fs_extra.default.ensureDir(import_path.default.join(targetDir, "lib"));
416
- await copyProTemplate("mock-auth.ts", import_path.default.join(targetDir, "lib", "mock-auth.ts"));
417
- await copyProTemplate("yaml.d.ts", import_path.default.join(targetDir, "yaml.d.ts"));
418
- await import_fs_extra.default.ensureDir(import_path.default.join(targetDir, "scripts"));
419
- await copyProTemplate("prebuild.js", import_path.default.join(targetDir, "scripts", "prebuild.js"));
420
- console.log(import_chalk.default.green("\u{1F510} Auth integration added (RBAC with 3 roles)"));
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`));
563
+ console.log(import_chalk.default.cyan("\n\u{1F4E6} Installing dependencies (this takes a moment)..."));
564
+ if (!options.skipInstall) {
565
+ try {
566
+ (0, import_child_process.execSync)("pnpm install --ignore-workspace", {
567
+ cwd: targetDir,
568
+ stdio: "inherit",
569
+ timeout: 12e4
570
+ });
571
+ console.log(import_chalk.default.green("\u2705 Dependencies installed"));
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);
589
+ }
590
+ } else {
591
+ console.log(import_chalk.default.dim("\u2139\uFE0F Skipping install (--skip-install). Run: pnpm install"));
592
+ }
593
+ const dependencyMode = "standalone";
594
+ await (0, import_scaffold_core.runScaffoldHooks)("postInstall", {
595
+ targetDir,
596
+ projectName: options.name || import_path2.default.basename(targetDir),
597
+ siteTitle: options.title || options.name || import_path2.default.basename(targetDir),
598
+ themeId: options.theme || "corporate",
599
+ packageJson: {},
600
+ dependencyMode
601
+ });
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)"));
421
618
  await addAuthToStackwrightYml(targetDir);
422
619
  let specInfo = null;
423
620
  const MOCK_URL = "http://localhost:4010";
424
- if (options.spec) {
425
- specInfo = await handleSpec(targetDir, options.spec, options.specName);
426
- await addIntegrationToStackwrightYml(
427
- targetDir,
428
- specInfo.derivedName,
429
- specInfo.specFilename,
430
- options.mock ? MOCK_URL : void 0
431
- );
432
- 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`));
433
632
  if (options.mock) {
434
633
  console.log(import_chalk.default.green("\u{1F3AD} Prism mock server configured"));
435
634
  }
@@ -439,9 +638,9 @@ async function launch(targetDir, options) {
439
638
  "\u26A0\uFE0F No spec provided with --mock. Using sample Petstore spec for demo. Replace with your API spec."
440
639
  )
441
640
  );
442
- const specsDir = import_path.default.join(targetDir, "specs");
443
- await import_fs_extra.default.ensureDir(specsDir);
444
- 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"));
445
644
  specInfo = { specFilename: "petstore.yaml", derivedName: "petstore" };
446
645
  await addIntegrationToStackwrightYml(
447
646
  targetDir,
@@ -455,18 +654,38 @@ async function launch(targetDir, options) {
455
654
  const readmeContent = generateReadme({
456
655
  projectName: options.name || dirName,
457
656
  hasSpec: !!specInfo,
458
- specName: specInfo?.derivedName,
459
- specFilename: specInfo?.specFilename,
657
+ ...specInfo?.derivedName !== void 0 && { specName: specInfo.derivedName },
658
+ ...specInfo?.specFilename !== void 0 && { specFilename: specInfo.specFilename },
460
659
  hasOtters: !options.skipOtters,
461
660
  hasMock: !!options.mock
462
661
  });
463
- 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);
464
663
  console.log(import_chalk.default.green("\u{1F4C4} README.md created"));
465
- 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) || ".";
466
685
  console.log(import_chalk.default.cyan.bold("\n\u{1F389} All set! Here's what to do next:\n"));
467
686
  console.log(import_chalk.default.white(` 1. cd ${relDir}`));
468
- console.log(import_chalk.default.white(" 2. pnpm install"));
469
- console.log(import_chalk.default.white(" 3. uvx code-puppy -i -a stackwright-pro-foreman-otter"));
687
+ console.log(import_chalk.default.white(" 2. npx @stackwright-pro/raft"));
688
+ console.log(import_chalk.default.dim(" (use --verbose for troubleshooting)"));
470
689
  if (specInfo) {
471
690
  console.log(import_chalk.default.cyan(`
472
691
  \u{1F4E1} Your API spec was copied to specs/${specInfo.specFilename}`));
@@ -484,13 +703,20 @@ async function launch(targetDir, options) {
484
703
  async function main() {
485
704
  const program = new import_commander.Command();
486
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(
487
- "--spec <path>",
488
- "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)"
489
708
  ).option(
490
709
  "--spec-name <name>",
491
710
  "Name for the API integration (default: derived from spec filename)"
711
+ ).option(
712
+ "--skip-install",
713
+ "Skip automatic pnpm install (useful in CI or when using a different package manager)"
492
714
  ).action(async (directory, options) => {
493
- const targetDir = import_path.default.resolve(directory);
715
+ const homeDir = import_os.default.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) {
718
+ console.warn(import_chalk.default.yellow(`\u26A0\uFE0F Scaffolding outside your home directory: ${targetDir}`));
719
+ }
494
720
  await launch(targetDir, options);
495
721
  });
496
722
  await program.parseAsync(process.argv);