jfl 0.1.1 → 0.2.0

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.
Files changed (112) hide show
  1. package/README.md +77 -7
  2. package/clawdbot-plugin/clawdbot.plugin.json +20 -0
  3. package/clawdbot-plugin/index.js +555 -0
  4. package/clawdbot-plugin/index.ts +582 -0
  5. package/clawdbot-skill/SKILL.md +33 -336
  6. package/clawdbot-skill/index.ts +491 -321
  7. package/clawdbot-skill/skill.json +4 -13
  8. package/dist/commands/clawdbot.d.ts +11 -0
  9. package/dist/commands/clawdbot.d.ts.map +1 -0
  10. package/dist/commands/clawdbot.js +215 -0
  11. package/dist/commands/clawdbot.js.map +1 -0
  12. package/dist/commands/gtm-process-update.d.ts +10 -0
  13. package/dist/commands/gtm-process-update.d.ts.map +1 -0
  14. package/dist/commands/gtm-process-update.js +101 -0
  15. package/dist/commands/gtm-process-update.js.map +1 -0
  16. package/dist/commands/onboard.d.ts.map +1 -1
  17. package/dist/commands/onboard.js +203 -15
  18. package/dist/commands/onboard.js.map +1 -1
  19. package/dist/commands/openclaw.d.ts +56 -0
  20. package/dist/commands/openclaw.d.ts.map +1 -0
  21. package/dist/commands/openclaw.js +700 -0
  22. package/dist/commands/openclaw.js.map +1 -0
  23. package/dist/commands/service-validate.d.ts +12 -0
  24. package/dist/commands/service-validate.d.ts.map +1 -0
  25. package/dist/commands/service-validate.js +611 -0
  26. package/dist/commands/service-validate.js.map +1 -0
  27. package/dist/commands/services-create.d.ts +15 -0
  28. package/dist/commands/services-create.d.ts.map +1 -0
  29. package/dist/commands/services-create.js +1452 -0
  30. package/dist/commands/services-create.js.map +1 -0
  31. package/dist/commands/services-sync-agents.d.ts +23 -0
  32. package/dist/commands/services-sync-agents.d.ts.map +1 -0
  33. package/dist/commands/services-sync-agents.js +207 -0
  34. package/dist/commands/services-sync-agents.js.map +1 -0
  35. package/dist/commands/services.d.ts +7 -1
  36. package/dist/commands/services.d.ts.map +1 -1
  37. package/dist/commands/services.js +347 -22
  38. package/dist/commands/services.js.map +1 -1
  39. package/dist/commands/update.js +0 -0
  40. package/dist/commands/validate-settings.d.ts +37 -0
  41. package/dist/commands/validate-settings.d.ts.map +1 -0
  42. package/dist/commands/validate-settings.js +197 -0
  43. package/dist/commands/validate-settings.js.map +1 -0
  44. package/dist/index.js +155 -60
  45. package/dist/index.js.map +1 -1
  46. package/dist/lib/agent-generator.d.ts.map +1 -1
  47. package/dist/lib/agent-generator.js +94 -1
  48. package/dist/lib/agent-generator.js.map +1 -1
  49. package/dist/lib/openclaw-registry.d.ts +48 -0
  50. package/dist/lib/openclaw-registry.d.ts.map +1 -0
  51. package/dist/lib/openclaw-registry.js +181 -0
  52. package/dist/lib/openclaw-registry.js.map +1 -0
  53. package/dist/lib/openclaw-sdk.d.ts +107 -0
  54. package/dist/lib/openclaw-sdk.d.ts.map +1 -0
  55. package/dist/lib/openclaw-sdk.js +208 -0
  56. package/dist/lib/openclaw-sdk.js.map +1 -0
  57. package/dist/lib/peer-agent-generator.d.ts +44 -0
  58. package/dist/lib/peer-agent-generator.d.ts.map +1 -0
  59. package/dist/lib/peer-agent-generator.js +286 -0
  60. package/dist/lib/peer-agent-generator.js.map +1 -0
  61. package/dist/lib/service-detector.d.ts +1 -1
  62. package/dist/lib/service-detector.d.ts.map +1 -1
  63. package/dist/lib/service-detector.js +118 -5
  64. package/dist/lib/service-detector.js.map +1 -1
  65. package/dist/lib/service-gtm.d.ts +157 -0
  66. package/dist/lib/service-gtm.d.ts.map +1 -0
  67. package/dist/lib/service-gtm.js +786 -0
  68. package/dist/lib/service-gtm.js.map +1 -0
  69. package/dist/lib/service-mcp-base.d.ts +10 -1
  70. package/dist/lib/service-mcp-base.d.ts.map +1 -1
  71. package/dist/lib/service-mcp-base.js +20 -1
  72. package/dist/lib/service-mcp-base.js.map +1 -1
  73. package/dist/mcp/service-peer-mcp.d.ts +36 -0
  74. package/dist/mcp/service-peer-mcp.d.ts.map +1 -0
  75. package/dist/mcp/service-peer-mcp.js +220 -0
  76. package/dist/mcp/service-peer-mcp.js.map +1 -0
  77. package/dist/mcp/service-registry-mcp.js +0 -0
  78. package/dist/utils/settings-validator.d.ts +4 -1
  79. package/dist/utils/settings-validator.d.ts.map +1 -1
  80. package/dist/utils/settings-validator.js +25 -1
  81. package/dist/utils/settings-validator.js.map +1 -1
  82. package/package.json +2 -1
  83. package/template/.claude/service-settings.json +32 -0
  84. package/template/.claude/settings.json +10 -0
  85. package/template/.claude/skills/end/SKILL.md +1780 -0
  86. package/template/.jfl/config.json +2 -1
  87. package/template/.mcp.json +1 -7
  88. package/template/CLAUDE.md +1042 -248
  89. package/template/CLAUDE.md.bak +1187 -0
  90. package/template/scripts/commit-gtm.sh +56 -0
  91. package/template/scripts/commit-product.sh +68 -0
  92. package/template/scripts/migrate-to-branch-sessions.sh +201 -0
  93. package/template/scripts/session/auto-commit.sh +4 -3
  94. package/template/scripts/session/jfl-doctor.sh +222 -83
  95. package/template/scripts/session/session-cleanup.sh +109 -21
  96. package/template/scripts/session/session-end.sh +26 -13
  97. package/template/scripts/session/session-init.sh +280 -98
  98. package/template/scripts/session/test-critical-infrastructure.sh +293 -0
  99. package/template/scripts/session/test-experience-level.sh +336 -0
  100. package/template/scripts/session/test-session-cleanup.sh +268 -0
  101. package/template/scripts/session/test-session-sync.sh +320 -0
  102. package/template/scripts/where-am-i.sh +78 -0
  103. package/template/templates/service-agent/.claude/settings.json +32 -0
  104. package/template/templates/service-agent/CLAUDE.md +334 -0
  105. package/template/templates/service-agent/knowledge/ARCHITECTURE.md +115 -0
  106. package/template/templates/service-agent/knowledge/DEPLOYMENT.md +199 -0
  107. package/template/templates/service-agent/knowledge/RUNBOOK.md +412 -0
  108. package/template/templates/service-agent/knowledge/SERVICE_SPEC.md +77 -0
  109. package/dist/commands/session-mgmt.d.ts +0 -33
  110. package/dist/commands/session-mgmt.d.ts.map +0 -1
  111. package/dist/commands/session-mgmt.js +0 -404
  112. package/dist/commands/session-mgmt.js.map +0 -1
@@ -0,0 +1,1452 @@
1
+ /**
2
+ * Services Create Command
3
+ *
4
+ * Create new service with AI assistance and automatic GTM integration
5
+ *
6
+ * @purpose Create new services with wizard, templates, and AI assistance
7
+ */
8
+ import * as p from "@clack/prompts";
9
+ import chalk from "chalk";
10
+ import { existsSync, mkdirSync, writeFileSync } from "fs";
11
+ import { join } from "path";
12
+ import { spawn } from "child_process";
13
+ import { getCodeDirectory } from "../utils/jfl-config.js";
14
+ import { onboardCommand } from "./onboard.js";
15
+ import { execSync } from "child_process";
16
+ // ============================================================================
17
+ // Main Command
18
+ // ============================================================================
19
+ export async function servicesCreateCommand(options = {}) {
20
+ console.log();
21
+ p.intro(chalk.cyan("JFL - Create Service"));
22
+ try {
23
+ // Step 1: Collect service metadata
24
+ const metadata = await collectServiceMetadata(options);
25
+ if (!metadata) {
26
+ p.cancel("Service creation cancelled");
27
+ process.exit(0);
28
+ }
29
+ const { name, type, description, location } = metadata;
30
+ // Step 2: Check if directory exists
31
+ if (existsSync(location)) {
32
+ const overwrite = await p.confirm({
33
+ message: `Directory ${location} already exists. Overwrite?`,
34
+ initialValue: false,
35
+ });
36
+ if (p.isCancel(overwrite) || !overwrite) {
37
+ p.cancel("Service creation cancelled");
38
+ process.exit(0);
39
+ }
40
+ }
41
+ // Step 3: Choose AI tool (unless --skip-ai)
42
+ let aiTool = null;
43
+ if (!options.skipAI) {
44
+ const tools = getAvailableAITools();
45
+ if (tools.length === 0) {
46
+ console.log(chalk.yellow("\n⚠️ No AI tools detected"));
47
+ console.log(chalk.gray(" Install Claude Code: npm install -g @anthropic-ai/claude-code"));
48
+ console.log(chalk.gray(" Install Ralph: bun install -g ralph-tui\n"));
49
+ }
50
+ else {
51
+ aiTool = await p.select({
52
+ message: "Build with AI assistance?",
53
+ options: [
54
+ ...tools.map(tool => ({
55
+ value: tool,
56
+ label: getToolLabel(tool),
57
+ hint: getToolHint(tool),
58
+ })),
59
+ {
60
+ value: "skip",
61
+ label: "Skip (just scaffold, I'll build manually)",
62
+ },
63
+ ],
64
+ });
65
+ if (p.isCancel(aiTool)) {
66
+ p.cancel("Service creation cancelled");
67
+ process.exit(0);
68
+ }
69
+ if (aiTool === "skip") {
70
+ aiTool = null;
71
+ }
72
+ }
73
+ }
74
+ // Step 4: Confirm creation
75
+ const confirmed = await confirmCreation(name, type, description, location, aiTool);
76
+ if (!confirmed) {
77
+ p.cancel("Service creation cancelled");
78
+ process.exit(0);
79
+ }
80
+ // Step 5: Scaffold service
81
+ const spinner = p.spinner();
82
+ spinner.start("Creating service...");
83
+ await scaffoldService(location, name, type, description);
84
+ spinner.stop("✓ Service scaffolded");
85
+ // Step 6: Launch AI tool if selected
86
+ if (aiTool) {
87
+ await launchAITool(aiTool, location, name, type, description);
88
+ }
89
+ else {
90
+ showManualNextSteps(location, name);
91
+ }
92
+ p.outro(chalk.green("Service created successfully! 🚀"));
93
+ }
94
+ catch (error) {
95
+ p.cancel(chalk.red(`Error: ${error.message}`));
96
+ process.exit(1);
97
+ }
98
+ }
99
+ // ============================================================================
100
+ // Metadata Collection
101
+ // ============================================================================
102
+ async function collectServiceMetadata(options) {
103
+ // Service name
104
+ let name = options.name;
105
+ if (!name) {
106
+ name = await p.text({
107
+ message: "Service name?",
108
+ placeholder: "my-new-service",
109
+ validate: (value) => {
110
+ const error = validateServiceName(value);
111
+ return error || undefined;
112
+ },
113
+ });
114
+ if (p.isCancel(name)) {
115
+ return null;
116
+ }
117
+ }
118
+ else {
119
+ const error = validateServiceName(name);
120
+ if (error) {
121
+ throw new Error(error);
122
+ }
123
+ }
124
+ // Service type
125
+ let type = options.type;
126
+ if (!type) {
127
+ type = await p.select({
128
+ message: "Service type?",
129
+ options: [
130
+ { value: "api", label: "API - REST/GraphQL service" },
131
+ { value: "web", label: "Web - Frontend application" },
132
+ { value: "worker", label: "Worker - Background jobs/queue" },
133
+ { value: "cli", label: "CLI - Command-line tool" },
134
+ { value: "infrastructure", label: "Infrastructure - Database, cache, etc." },
135
+ { value: "container", label: "Container - Docker service" },
136
+ ],
137
+ });
138
+ if (p.isCancel(type)) {
139
+ return null;
140
+ }
141
+ }
142
+ // Description
143
+ let description = options.description;
144
+ if (!description) {
145
+ description = await p.text({
146
+ message: "Description?",
147
+ placeholder: "What does this service do?",
148
+ validate: (value) => {
149
+ if (!value.trim()) {
150
+ return "Description required";
151
+ }
152
+ },
153
+ });
154
+ if (p.isCancel(description)) {
155
+ return null;
156
+ }
157
+ }
158
+ // Location
159
+ const codeDir = await getCodeDirectory();
160
+ const defaultLocation = join(codeDir, name);
161
+ const location = await p.text({
162
+ message: "Where to create?",
163
+ placeholder: defaultLocation,
164
+ initialValue: defaultLocation,
165
+ });
166
+ if (p.isCancel(location)) {
167
+ return null;
168
+ }
169
+ return { name, type, description, location };
170
+ }
171
+ // ============================================================================
172
+ // Validation
173
+ // ============================================================================
174
+ function validateServiceName(name) {
175
+ if (!name || !name.trim()) {
176
+ return "Service name required";
177
+ }
178
+ // Must be lowercase with hyphens
179
+ if (!/^[a-z0-9-]+$/.test(name)) {
180
+ return "Service name must be lowercase letters, numbers, and hyphens only";
181
+ }
182
+ // Can't start or end with hyphen
183
+ if (name.startsWith("-") || name.endsWith("-")) {
184
+ return "Service name can't start or end with hyphen";
185
+ }
186
+ // No consecutive hyphens
187
+ if (name.includes("--")) {
188
+ return "Service name can't have consecutive hyphens";
189
+ }
190
+ return undefined;
191
+ }
192
+ // ============================================================================
193
+ // AI Tool Detection
194
+ // ============================================================================
195
+ function getAvailableAITools() {
196
+ const tools = [];
197
+ if (hasClaudeCLI()) {
198
+ tools.push("claude");
199
+ }
200
+ if (hasRalphTui()) {
201
+ tools.push("ralph");
202
+ }
203
+ return tools;
204
+ }
205
+ function hasClaudeCLI() {
206
+ try {
207
+ execSync("which claude", { stdio: "ignore" });
208
+ return true;
209
+ }
210
+ catch {
211
+ return false;
212
+ }
213
+ }
214
+ function hasRalphTui() {
215
+ try {
216
+ execSync("which ralph-tui", { stdio: "ignore" });
217
+ return true;
218
+ }
219
+ catch {
220
+ return false;
221
+ }
222
+ }
223
+ function getToolLabel(tool) {
224
+ switch (tool) {
225
+ case "claude":
226
+ return "Claude Code (collaborative, recommended)";
227
+ case "ralph":
228
+ return "Ralph (autonomous agent loop)";
229
+ default:
230
+ return tool;
231
+ }
232
+ }
233
+ function getToolHint(tool) {
234
+ switch (tool) {
235
+ case "claude":
236
+ return "Interactive AI pair programming";
237
+ case "ralph":
238
+ return "Autonomous loop until complete";
239
+ default:
240
+ return "";
241
+ }
242
+ }
243
+ // ============================================================================
244
+ // Confirmation
245
+ // ============================================================================
246
+ async function confirmCreation(name, type, description, location, aiTool) {
247
+ console.log();
248
+ console.log(chalk.cyan("Ready to create:"));
249
+ console.log(chalk.gray(` Name: ${name}`));
250
+ console.log(chalk.gray(` Type: ${type}`));
251
+ console.log(chalk.gray(` Description: ${description}`));
252
+ console.log(chalk.gray(` Location: ${location}`));
253
+ console.log(chalk.gray(` AI Tool: ${aiTool || "None (manual)"}`));
254
+ console.log();
255
+ const confirmed = await p.confirm({
256
+ message: "Create this service?",
257
+ initialValue: true,
258
+ });
259
+ return !p.isCancel(confirmed) && confirmed;
260
+ }
261
+ // ============================================================================
262
+ // Service Scaffolding
263
+ // ============================================================================
264
+ async function scaffoldService(servicePath, serviceName, serviceType, description) {
265
+ // Create base directories
266
+ mkdirSync(servicePath, { recursive: true });
267
+ mkdirSync(join(servicePath, ".jfl", "journal"), { recursive: true });
268
+ mkdirSync(join(servicePath, ".jfl", "logs"), { recursive: true });
269
+ mkdirSync(join(servicePath, ".claude", "agents"), { recursive: true });
270
+ mkdirSync(join(servicePath, ".claude", "skills"), { recursive: true });
271
+ mkdirSync(join(servicePath, "knowledge"), { recursive: true });
272
+ // Get template for service type
273
+ const template = getServiceTemplate(serviceType, serviceName, description);
274
+ // Write all template files
275
+ for (const file of template.files) {
276
+ const filePath = join(servicePath, file.path);
277
+ const fileDir = join(filePath, "..");
278
+ mkdirSync(fileDir, { recursive: true });
279
+ writeFileSync(filePath, file.content);
280
+ }
281
+ // Write CLAUDE.md with service-specific instructions
282
+ const claudeMd = generateClaudeMd(serviceName, serviceType, description, template.instructions);
283
+ writeFileSync(join(servicePath, "CLAUDE.md"), claudeMd);
284
+ // Write base .gitignore
285
+ const gitignore = generateGitignore(serviceType);
286
+ writeFileSync(join(servicePath, ".gitignore"), gitignore);
287
+ // Initialize git
288
+ try {
289
+ execSync("git init", { cwd: servicePath, stdio: "ignore" });
290
+ execSync("git add .", { cwd: servicePath, stdio: "ignore" });
291
+ execSync(`git commit -m "Initial commit: scaffolded ${serviceType} service"`, {
292
+ cwd: servicePath,
293
+ stdio: "ignore",
294
+ });
295
+ }
296
+ catch (error) {
297
+ // Git init failed - not critical
298
+ console.log(chalk.yellow("⚠️ Git initialization failed (not critical)"));
299
+ }
300
+ }
301
+ // ============================================================================
302
+ // Service Templates
303
+ // ============================================================================
304
+ function getServiceTemplate(type, name, description) {
305
+ switch (type) {
306
+ case "api":
307
+ return getAPITemplate(name, description);
308
+ case "web":
309
+ return getWebTemplate(name, description);
310
+ case "worker":
311
+ return getWorkerTemplate(name, description);
312
+ case "cli":
313
+ return getCLITemplate(name, description);
314
+ case "infrastructure":
315
+ return getInfrastructureTemplate(name, description);
316
+ case "container":
317
+ return getContainerTemplate(name, description);
318
+ default:
319
+ throw new Error(`Unknown service type: ${type}`);
320
+ }
321
+ }
322
+ function getAPITemplate(name, description) {
323
+ return {
324
+ files: [
325
+ {
326
+ path: "package.json",
327
+ content: JSON.stringify({
328
+ name,
329
+ version: "0.1.0",
330
+ description,
331
+ type: "module",
332
+ scripts: {
333
+ dev: "tsx watch src/index.ts",
334
+ build: "tsc",
335
+ start: "node dist/index.js",
336
+ test: "vitest",
337
+ },
338
+ dependencies: {
339
+ fastify: "^4.26.0",
340
+ "@fastify/cors": "^9.0.1",
341
+ pino: "^8.19.0",
342
+ },
343
+ devDependencies: {
344
+ typescript: "^5.3.3",
345
+ tsx: "^4.7.1",
346
+ vitest: "^1.3.1",
347
+ "@types/node": "^20.11.17",
348
+ },
349
+ }, null, 2),
350
+ },
351
+ {
352
+ path: "tsconfig.json",
353
+ content: JSON.stringify({
354
+ compilerOptions: {
355
+ target: "ES2022",
356
+ module: "ES2022",
357
+ moduleResolution: "node",
358
+ outDir: "./dist",
359
+ rootDir: "./src",
360
+ strict: true,
361
+ esModuleInterop: true,
362
+ skipLibCheck: true,
363
+ forceConsistentCasingInFileNames: true,
364
+ },
365
+ include: ["src/**/*"],
366
+ exclude: ["node_modules", "dist"],
367
+ }, null, 2),
368
+ },
369
+ {
370
+ path: "src/index.ts",
371
+ content: `import Fastify from 'fastify'
372
+ import cors from '@fastify/cors'
373
+ import { healthRoutes } from './routes/health.js'
374
+
375
+ const fastify = Fastify({
376
+ logger: true
377
+ })
378
+
379
+ fastify.register(cors)
380
+ fastify.register(healthRoutes)
381
+
382
+ const start = async () => {
383
+ try {
384
+ const port = parseInt(process.env.PORT || '3000', 10)
385
+ await fastify.listen({ port, host: '0.0.0.0' })
386
+ console.log(\`Server running on http://localhost:\${port}\`)
387
+ } catch (err) {
388
+ fastify.log.error(err)
389
+ process.exit(1)
390
+ }
391
+ }
392
+
393
+ start()
394
+ `,
395
+ },
396
+ {
397
+ path: "src/routes/health.ts",
398
+ content: `import { FastifyInstance } from 'fastify'
399
+
400
+ export async function healthRoutes(fastify: FastifyInstance) {
401
+ fastify.get('/health', async () => {
402
+ return { status: 'ok', timestamp: new Date().toISOString() }
403
+ })
404
+ }
405
+ `,
406
+ },
407
+ {
408
+ path: ".env.example",
409
+ content: `PORT=3000
410
+ NODE_ENV=development
411
+ `,
412
+ },
413
+ {
414
+ path: "README.md",
415
+ content: `# ${name}
416
+
417
+ ${description}
418
+
419
+ ## Getting Started
420
+
421
+ \`\`\`bash
422
+ npm install
423
+ npm run dev
424
+ \`\`\`
425
+
426
+ ## API Endpoints
427
+
428
+ - \`GET /health\` - Health check
429
+
430
+ ## Environment Variables
431
+
432
+ See \`.env.example\` for required environment variables.
433
+ `,
434
+ },
435
+ {
436
+ path: "knowledge/SERVICE_SPEC.md",
437
+ content: `# ${name} - Service Specification
438
+
439
+ ## Overview
440
+
441
+ ${description}
442
+
443
+ ## Responsibilities
444
+
445
+ - [ ] TODO: Define what this service is responsible for
446
+
447
+ ## API Endpoints
448
+
449
+ - \`GET /health\` - Health check endpoint
450
+
451
+ ## Dependencies
452
+
453
+ - None (update as you add dependencies)
454
+
455
+ ## Environment Variables
456
+
457
+ - \`PORT\` - Server port (default: 3000)
458
+ - \`NODE_ENV\` - Environment (development/production)
459
+ `,
460
+ },
461
+ ],
462
+ instructions: `This is a REST API service using Fastify and TypeScript.
463
+
464
+ ## Core Features to Implement
465
+ - [ ] Define API routes and endpoints
466
+ - [ ] Add request validation
467
+ - [ ] Implement business logic
468
+ - [ ] Add error handling
469
+ - [ ] Write unit tests
470
+
471
+ ## Technical Stack
472
+ - **Framework:** Fastify (fast, low-overhead web framework)
473
+ - **Language:** TypeScript
474
+ - **Testing:** Vitest
475
+
476
+ ## Key Patterns
477
+ - Keep routes thin, move logic to separate modules
478
+ - Use Fastify plugins for reusable functionality
479
+ - Validate all inputs
480
+ - Return consistent error responses
481
+ `,
482
+ };
483
+ }
484
+ function getWebTemplate(name, description) {
485
+ return {
486
+ files: [
487
+ {
488
+ path: "package.json",
489
+ content: JSON.stringify({
490
+ name,
491
+ version: "0.1.0",
492
+ description,
493
+ scripts: {
494
+ dev: "next dev",
495
+ build: "next build",
496
+ start: "next start",
497
+ lint: "next lint",
498
+ },
499
+ dependencies: {
500
+ react: "^18.2.0",
501
+ "react-dom": "^18.2.0",
502
+ next: "^14.1.0",
503
+ },
504
+ devDependencies: {
505
+ typescript: "^5.3.3",
506
+ "@types/node": "^20.11.17",
507
+ "@types/react": "^18.2.55",
508
+ "@types/react-dom": "^18.2.19",
509
+ },
510
+ }, null, 2),
511
+ },
512
+ {
513
+ path: "tsconfig.json",
514
+ content: JSON.stringify({
515
+ compilerOptions: {
516
+ target: "ES2022",
517
+ lib: ["dom", "dom.iterable", "esnext"],
518
+ allowJs: true,
519
+ skipLibCheck: true,
520
+ strict: true,
521
+ forceConsistentCasingInFileNames: true,
522
+ noEmit: true,
523
+ esModuleInterop: true,
524
+ module: "esnext",
525
+ moduleResolution: "bundler",
526
+ resolveJsonModule: true,
527
+ isolatedModules: true,
528
+ jsx: "preserve",
529
+ incremental: true,
530
+ plugins: [{ name: "next" }],
531
+ paths: {
532
+ "@/*": ["./src/*"],
533
+ },
534
+ },
535
+ include: ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
536
+ exclude: ["node_modules"],
537
+ }, null, 2),
538
+ },
539
+ {
540
+ path: "next.config.js",
541
+ content: `/** @type {import('next').NextConfig} */
542
+ const nextConfig = {}
543
+
544
+ module.exports = nextConfig
545
+ `,
546
+ },
547
+ {
548
+ path: "src/app/layout.tsx",
549
+ content: `export const metadata = {
550
+ title: '${name}',
551
+ description: '${description}',
552
+ }
553
+
554
+ export default function RootLayout({
555
+ children,
556
+ }: {
557
+ children: React.ReactNode
558
+ }) {
559
+ return (
560
+ <html lang="en">
561
+ <body>{children}</body>
562
+ </html>
563
+ )
564
+ }
565
+ `,
566
+ },
567
+ {
568
+ path: "src/app/page.tsx",
569
+ content: `export default function Home() {
570
+ return (
571
+ <main className="min-h-screen p-8">
572
+ <h1 className="text-4xl font-bold">${name}</h1>
573
+ <p className="mt-4 text-lg">${description}</p>
574
+ </main>
575
+ )
576
+ }
577
+ `,
578
+ },
579
+ {
580
+ path: "README.md",
581
+ content: `# ${name}
582
+
583
+ ${description}
584
+
585
+ ## Getting Started
586
+
587
+ \`\`\`bash
588
+ npm install
589
+ npm run dev
590
+ \`\`\`
591
+
592
+ Open [http://localhost:3000](http://localhost:3000) with your browser.
593
+ `,
594
+ },
595
+ {
596
+ path: "knowledge/SERVICE_SPEC.md",
597
+ content: `# ${name} - Service Specification
598
+
599
+ ## Overview
600
+
601
+ ${description}
602
+
603
+ ## Responsibilities
604
+
605
+ - [ ] TODO: Define what this frontend does
606
+
607
+ ## Pages/Routes
608
+
609
+ - \`/\` - Home page
610
+
611
+ ## Components
612
+
613
+ - None yet (add as you build)
614
+
615
+ ## Styling
616
+
617
+ - TailwindCSS (recommended - add if needed)
618
+ - Or CSS modules
619
+
620
+ ## API Integration
621
+
622
+ - None yet (define API endpoints to connect to)
623
+ `,
624
+ },
625
+ ],
626
+ instructions: `This is a Next.js web application with TypeScript.
627
+
628
+ ## Core Features to Implement
629
+ - [ ] Design page layouts
630
+ - [ ] Build reusable components
631
+ - [ ] Connect to backend APIs
632
+ - [ ] Add styling (TailwindCSS recommended)
633
+ - [ ] Implement client-side routing
634
+
635
+ ## Technical Stack
636
+ - **Framework:** Next.js 14 (App Router)
637
+ - **Language:** TypeScript
638
+ - **UI:** React
639
+
640
+ ## Key Patterns
641
+ - Use Server Components by default
642
+ - Use Client Components only when needed (interactivity, hooks)
643
+ - Keep components small and focused
644
+ - Co-locate related files
645
+ `,
646
+ };
647
+ }
648
+ function getWorkerTemplate(name, description) {
649
+ return {
650
+ files: [
651
+ {
652
+ path: "package.json",
653
+ content: JSON.stringify({
654
+ name,
655
+ version: "0.1.0",
656
+ description,
657
+ type: "module",
658
+ scripts: {
659
+ dev: "tsx watch src/index.ts",
660
+ build: "tsc",
661
+ start: "node dist/index.js",
662
+ },
663
+ dependencies: {
664
+ bullmq: "^5.3.0",
665
+ ioredis: "^5.3.2",
666
+ },
667
+ devDependencies: {
668
+ typescript: "^5.3.3",
669
+ tsx: "^4.7.1",
670
+ "@types/node": "^20.11.17",
671
+ },
672
+ }, null, 2),
673
+ },
674
+ {
675
+ path: "tsconfig.json",
676
+ content: JSON.stringify({
677
+ compilerOptions: {
678
+ target: "ES2022",
679
+ module: "ES2022",
680
+ moduleResolution: "node",
681
+ outDir: "./dist",
682
+ rootDir: "./src",
683
+ strict: true,
684
+ esModuleInterop: true,
685
+ skipLibCheck: true,
686
+ forceConsistentCasingInFileNames: true,
687
+ },
688
+ include: ["src/**/*"],
689
+ exclude: ["node_modules", "dist"],
690
+ }, null, 2),
691
+ },
692
+ {
693
+ path: "src/index.ts",
694
+ content: `import { Worker } from 'bullmq'
695
+
696
+ const worker = new Worker('${name}', async job => {
697
+ console.log(\`Processing job \${job.id}:\`, job.data)
698
+
699
+ // Add job processing logic here
700
+ // Example: await processTask(job.data)
701
+
702
+ return { processed: true, timestamp: new Date().toISOString() }
703
+ }, {
704
+ connection: {
705
+ host: process.env.REDIS_HOST || 'localhost',
706
+ port: parseInt(process.env.REDIS_PORT || '6379', 10),
707
+ }
708
+ })
709
+
710
+ worker.on('completed', job => {
711
+ console.log(\`Job \${job.id} completed\`)
712
+ })
713
+
714
+ worker.on('failed', (job, err) => {
715
+ console.error(\`Job \${job?.id} failed:\`, err)
716
+ })
717
+
718
+ console.log('Worker started, waiting for jobs...')
719
+ `,
720
+ },
721
+ {
722
+ path: "src/jobs/example.ts",
723
+ content: `export async function processExampleJob(data: any) {
724
+ // Implement job logic here
725
+ console.log('Processing example job:', data)
726
+
727
+ return { success: true }
728
+ }
729
+ `,
730
+ },
731
+ {
732
+ path: ".env.example",
733
+ content: `REDIS_HOST=localhost
734
+ REDIS_PORT=6379
735
+ NODE_ENV=development
736
+ `,
737
+ },
738
+ {
739
+ path: "README.md",
740
+ content: `# ${name}
741
+
742
+ ${description}
743
+
744
+ ## Getting Started
745
+
746
+ \`\`\`bash
747
+ npm install
748
+ npm run dev
749
+ \`\`\`
750
+
751
+ ## Requirements
752
+
753
+ - Redis server running on localhost:6379
754
+
755
+ ## Job Types
756
+
757
+ - Add job types as you implement them
758
+
759
+ ## Environment Variables
760
+
761
+ See \`.env.example\` for required environment variables.
762
+ `,
763
+ },
764
+ {
765
+ path: "knowledge/SERVICE_SPEC.md",
766
+ content: `# ${name} - Service Specification
767
+
768
+ ## Overview
769
+
770
+ ${description}
771
+
772
+ ## Responsibilities
773
+
774
+ - [ ] TODO: Define what jobs this worker processes
775
+
776
+ ## Job Types
777
+
778
+ - None yet (add as you implement)
779
+
780
+ ## Dependencies
781
+
782
+ - **Redis** - Job queue backend (required)
783
+
784
+ ## Environment Variables
785
+
786
+ - \`REDIS_HOST\` - Redis host (default: localhost)
787
+ - \`REDIS_PORT\` - Redis port (default: 6379)
788
+ `,
789
+ },
790
+ ],
791
+ instructions: `This is a background job worker using BullMQ and TypeScript.
792
+
793
+ ## Core Features to Implement
794
+ - [ ] Define job types and handlers
795
+ - [ ] Add error handling and retries
796
+ - [ ] Implement job prioritization
797
+ - [ ] Add monitoring/logging
798
+ - [ ] Write job tests
799
+
800
+ ## Technical Stack
801
+ - **Queue:** BullMQ (Redis-based job queue)
802
+ - **Language:** TypeScript
803
+
804
+ ## Key Patterns
805
+ - Keep job handlers focused and testable
806
+ - Use job data validation
807
+ - Implement proper error handling
808
+ - Add retry logic for transient failures
809
+ - Log job progress for debugging
810
+ `,
811
+ };
812
+ }
813
+ function getCLITemplate(name, description) {
814
+ return {
815
+ files: [
816
+ {
817
+ path: "package.json",
818
+ content: JSON.stringify({
819
+ name,
820
+ version: "0.1.0",
821
+ description,
822
+ type: "module",
823
+ bin: {
824
+ [name]: "./bin/cli.js",
825
+ },
826
+ scripts: {
827
+ dev: "tsx watch src/cli.ts",
828
+ build: "tsc && chmod +x bin/cli.js",
829
+ start: "node bin/cli.js",
830
+ },
831
+ dependencies: {
832
+ commander: "^11.1.0",
833
+ chalk: "^5.3.0",
834
+ },
835
+ devDependencies: {
836
+ typescript: "^5.3.3",
837
+ tsx: "^4.7.1",
838
+ "@types/node": "^20.11.17",
839
+ },
840
+ }, null, 2),
841
+ },
842
+ {
843
+ path: "tsconfig.json",
844
+ content: JSON.stringify({
845
+ compilerOptions: {
846
+ target: "ES2022",
847
+ module: "ES2022",
848
+ moduleResolution: "node",
849
+ outDir: "./bin",
850
+ rootDir: "./src",
851
+ strict: true,
852
+ esModuleInterop: true,
853
+ skipLibCheck: true,
854
+ forceConsistentCasingInFileNames: true,
855
+ },
856
+ include: ["src/**/*"],
857
+ exclude: ["node_modules", "bin"],
858
+ }, null, 2),
859
+ },
860
+ {
861
+ path: "src/cli.ts",
862
+ content: `#!/usr/bin/env node
863
+ import { Command } from 'commander'
864
+ import chalk from 'chalk'
865
+ import { helloCommand } from './commands/hello.js'
866
+
867
+ const program = new Command()
868
+
869
+ program
870
+ .name('${name}')
871
+ .description('${description}')
872
+ .version('0.1.0')
873
+
874
+ program
875
+ .command('hello')
876
+ .description('Say hello')
877
+ .argument('[name]', 'Name to greet')
878
+ .action(helloCommand)
879
+
880
+ program.parse()
881
+ `,
882
+ },
883
+ {
884
+ path: "src/commands/hello.ts",
885
+ content: `import chalk from 'chalk'
886
+
887
+ export function helloCommand(name?: string) {
888
+ const greeting = name ? \`Hello, \${name}!\` : 'Hello!'
889
+ console.log(chalk.cyan(greeting))
890
+ }
891
+ `,
892
+ },
893
+ {
894
+ path: "README.md",
895
+ content: `# ${name}
896
+
897
+ ${description}
898
+
899
+ ## Installation
900
+
901
+ \`\`\`bash
902
+ npm install -g ${name}
903
+ \`\`\`
904
+
905
+ ## Usage
906
+
907
+ \`\`\`bash
908
+ ${name} hello
909
+ ${name} hello World
910
+ \`\`\`
911
+
912
+ ## Commands
913
+
914
+ - \`hello [name]\` - Say hello
915
+ `,
916
+ },
917
+ {
918
+ path: "knowledge/SERVICE_SPEC.md",
919
+ content: `# ${name} - Service Specification
920
+
921
+ ## Overview
922
+
923
+ ${description}
924
+
925
+ ## Commands
926
+
927
+ - \`hello [name]\` - Example command
928
+
929
+ ## Installation
930
+
931
+ Installable globally via npm.
932
+
933
+ ## Usage Patterns
934
+
935
+ Define expected CLI usage patterns here.
936
+ `,
937
+ },
938
+ ],
939
+ instructions: `This is a CLI tool using Commander and TypeScript.
940
+
941
+ ## Core Features to Implement
942
+ - [ ] Define CLI commands
943
+ - [ ] Add command arguments and options
944
+ - [ ] Implement command logic
945
+ - [ ] Add help text and examples
946
+ - [ ] Write command tests
947
+
948
+ ## Technical Stack
949
+ - **CLI Framework:** Commander
950
+ - **Styling:** Chalk
951
+ - **Language:** TypeScript
952
+
953
+ ## Key Patterns
954
+ - Keep commands focused (one command = one responsibility)
955
+ - Provide clear help text
956
+ - Validate inputs early
957
+ - Give helpful error messages
958
+ - Support --help and --version
959
+ `,
960
+ };
961
+ }
962
+ function getInfrastructureTemplate(name, description) {
963
+ return {
964
+ files: [
965
+ {
966
+ path: "docker-compose.yml",
967
+ content: `version: '3.8'
968
+
969
+ services:
970
+ ${name}:
971
+ image: ${name}:latest
972
+ ports:
973
+ - "8080:8080"
974
+ environment:
975
+ - NODE_ENV=development
976
+ volumes:
977
+ - ./data:/data
978
+ `,
979
+ },
980
+ {
981
+ path: "Dockerfile",
982
+ content: `FROM node:20-alpine
983
+
984
+ WORKDIR /app
985
+
986
+ COPY package*.json ./
987
+ RUN npm ci
988
+
989
+ COPY . .
990
+
991
+ EXPOSE 8080
992
+
993
+ CMD ["npm", "start"]
994
+ `,
995
+ },
996
+ {
997
+ path: "README.md",
998
+ content: `# ${name}
999
+
1000
+ ${description}
1001
+
1002
+ ## Getting Started
1003
+
1004
+ \`\`\`bash
1005
+ docker-compose up
1006
+ \`\`\`
1007
+
1008
+ ## Configuration
1009
+
1010
+ Update \`docker-compose.yml\` with your configuration.
1011
+ `,
1012
+ },
1013
+ {
1014
+ path: "knowledge/SERVICE_SPEC.md",
1015
+ content: `# ${name} - Service Specification
1016
+
1017
+ ## Overview
1018
+
1019
+ ${description}
1020
+
1021
+ ## Infrastructure Components
1022
+
1023
+ - [ ] TODO: Define what infrastructure this service provides
1024
+
1025
+ ## Configuration
1026
+
1027
+ Document configuration options here.
1028
+
1029
+ ## Deployment
1030
+
1031
+ Define deployment strategy here.
1032
+ `,
1033
+ },
1034
+ ],
1035
+ instructions: `This is an infrastructure service with Docker configuration.
1036
+
1037
+ ## Core Features to Implement
1038
+ - [ ] Define infrastructure components
1039
+ - [ ] Configure networking
1040
+ - [ ] Set up volumes and persistence
1041
+ - [ ] Add health checks
1042
+ - [ ] Document deployment process
1043
+
1044
+ ## Technical Stack
1045
+ - **Containerization:** Docker
1046
+ - **Orchestration:** Docker Compose (or Kubernetes if needed)
1047
+
1048
+ ## Key Patterns
1049
+ - Use multi-stage builds for production
1050
+ - Keep images small (Alpine-based)
1051
+ - Externalize configuration via env vars
1052
+ - Set up proper health checks
1053
+ - Document all ports and volumes
1054
+ `,
1055
+ };
1056
+ }
1057
+ function getContainerTemplate(name, description) {
1058
+ return {
1059
+ files: [
1060
+ {
1061
+ path: "Dockerfile",
1062
+ content: `FROM node:20-alpine
1063
+
1064
+ WORKDIR /app
1065
+
1066
+ COPY package*.json ./
1067
+ RUN npm ci --only=production
1068
+
1069
+ COPY . .
1070
+
1071
+ EXPOSE 3000
1072
+
1073
+ CMD ["npm", "start"]
1074
+ `,
1075
+ },
1076
+ {
1077
+ path: "package.json",
1078
+ content: JSON.stringify({
1079
+ name,
1080
+ version: "0.1.0",
1081
+ description,
1082
+ scripts: {
1083
+ start: "node index.js",
1084
+ },
1085
+ }, null, 2),
1086
+ },
1087
+ {
1088
+ path: "index.js",
1089
+ content: `console.log('Container service started')
1090
+
1091
+ // Add your service logic here
1092
+ `,
1093
+ },
1094
+ {
1095
+ path: "README.md",
1096
+ content: `# ${name}
1097
+
1098
+ ${description}
1099
+
1100
+ ## Build
1101
+
1102
+ \`\`\`bash
1103
+ docker build -t ${name} .
1104
+ \`\`\`
1105
+
1106
+ ## Run
1107
+
1108
+ \`\`\`bash
1109
+ docker run -p 3000:3000 ${name}
1110
+ \`\`\`
1111
+ `,
1112
+ },
1113
+ {
1114
+ path: "knowledge/SERVICE_SPEC.md",
1115
+ content: `# ${name} - Service Specification
1116
+
1117
+ ## Overview
1118
+
1119
+ ${description}
1120
+
1121
+ ## Container Configuration
1122
+
1123
+ - **Base Image:** node:20-alpine
1124
+ - **Exposed Ports:** 3000
1125
+
1126
+ ## Environment Variables
1127
+
1128
+ - None yet (add as needed)
1129
+
1130
+ ## Volumes
1131
+
1132
+ - None yet (add for persistence)
1133
+ `,
1134
+ },
1135
+ ],
1136
+ instructions: `This is a containerized service with Docker.
1137
+
1138
+ ## Core Features to Implement
1139
+ - [ ] Define service functionality
1140
+ - [ ] Configure container properly
1141
+ - [ ] Add health checks
1142
+ - [ ] Set up logging
1143
+ - [ ] Document deployment
1144
+
1145
+ ## Technical Stack
1146
+ - **Containerization:** Docker
1147
+
1148
+ ## Key Patterns
1149
+ - Keep container small and focused
1150
+ - Use .dockerignore to exclude unnecessary files
1151
+ - Run as non-root user
1152
+ - Add health checks
1153
+ - Log to stdout/stderr
1154
+ `,
1155
+ };
1156
+ }
1157
+ // ============================================================================
1158
+ // CLAUDE.md Generation
1159
+ // ============================================================================
1160
+ function generateClaudeMd(name, type, description, instructions) {
1161
+ return `# ${name} - Service Agent
1162
+
1163
+ Your context layer for building this service.
1164
+
1165
+ ## What You're Building
1166
+
1167
+ **Type:** ${type}
1168
+ **Description:** ${description}
1169
+
1170
+ ## Requirements
1171
+
1172
+ ${instructions}
1173
+
1174
+ ## Development Guidelines
1175
+
1176
+ ### Testing Strategy
1177
+ - Write tests as you build
1178
+ - Test happy paths and error cases
1179
+ - Keep tests focused and fast
1180
+
1181
+ ### Code Organization
1182
+ - Keep files small and focused
1183
+ - Group related code together
1184
+ - Use clear, descriptive names
1185
+
1186
+ ## Integration with GTM
1187
+
1188
+ This service will be part of a GTM (Go-To-Market) workspace:
1189
+ - Will have autonomous service agent (you!)
1190
+ - Can @-mention peer services
1191
+ - Journal entries sync to GTM parent
1192
+ - Coordinates with other services via Service Manager
1193
+
1194
+ ## Getting Started
1195
+
1196
+ 1. Implement core features (see checklist above)
1197
+ 2. Add tests
1198
+ 3. Update knowledge docs as you build
1199
+ 4. Test locally
1200
+ 5. Ready to onboard to GTM: \`jfl onboard .\`
1201
+
1202
+ ## Questions to Answer
1203
+
1204
+ As you build, consider:
1205
+ - What data models are needed?
1206
+ - What external APIs or services to integrate?
1207
+ - What environment variables are required?
1208
+ - What are the deployment requirements?
1209
+ - How will this be tested?
1210
+ - How will this be monitored?
1211
+
1212
+ Update this file as you answer these questions.
1213
+ `;
1214
+ }
1215
+ // ============================================================================
1216
+ // .gitignore Generation
1217
+ // ============================================================================
1218
+ function generateGitignore(type) {
1219
+ const base = `# Dependencies
1220
+ node_modules/
1221
+ .pnpm-store/
1222
+
1223
+ # Build output
1224
+ dist/
1225
+ bin/
1226
+ build/
1227
+ .next/
1228
+
1229
+ # Environment
1230
+ .env
1231
+ .env.local
1232
+ .env.*.local
1233
+
1234
+ # Logs
1235
+ *.log
1236
+ .jfl/logs/
1237
+ logs/
1238
+
1239
+ # OS
1240
+ .DS_Store
1241
+ Thumbs.db
1242
+
1243
+ # IDE
1244
+ .vscode/
1245
+ .idea/
1246
+ *.swp
1247
+ *.swo
1248
+ `;
1249
+ if (type === "web") {
1250
+ return base + `
1251
+ # Next.js
1252
+ .next/
1253
+ out/
1254
+ `;
1255
+ }
1256
+ if (type === "infrastructure" || type === "container") {
1257
+ return base + `
1258
+ # Docker
1259
+ *.tar
1260
+ `;
1261
+ }
1262
+ return base;
1263
+ }
1264
+ // ============================================================================
1265
+ // AI Tool Launching
1266
+ // ============================================================================
1267
+ async function launchAITool(toolName, servicePath, serviceName, serviceType, description) {
1268
+ switch (toolName) {
1269
+ case "claude":
1270
+ await launchClaudeCode(servicePath, serviceName);
1271
+ break;
1272
+ case "ralph":
1273
+ await launchRalph(servicePath, serviceName, serviceType, description);
1274
+ break;
1275
+ default:
1276
+ throw new Error(`Unknown AI tool: ${toolName}`);
1277
+ }
1278
+ }
1279
+ async function launchClaudeCode(servicePath, serviceName) {
1280
+ console.log(chalk.cyan(`\n🤖 Launching Claude Code...\n`));
1281
+ console.log(chalk.gray(`Read CLAUDE.md for service requirements\n`));
1282
+ // Spawn Claude in service directory
1283
+ const child = spawn("claude", [], {
1284
+ cwd: servicePath,
1285
+ stdio: "inherit",
1286
+ shell: true,
1287
+ });
1288
+ // Wait for Claude to close
1289
+ await new Promise((resolve) => {
1290
+ child.on("exit", () => resolve());
1291
+ });
1292
+ // Offer to onboard automatically
1293
+ console.log();
1294
+ const shouldOnboard = await p.confirm({
1295
+ message: "Onboard this service to GTM now?",
1296
+ initialValue: true,
1297
+ });
1298
+ if (!p.isCancel(shouldOnboard) && shouldOnboard) {
1299
+ try {
1300
+ await onboardCommand(servicePath, { skipGit: true });
1301
+ }
1302
+ catch (error) {
1303
+ console.log(chalk.yellow(`\n⚠️ Onboard failed: ${error.message}`));
1304
+ console.log(chalk.gray(`You can onboard manually later: jfl onboard ${servicePath}\n`));
1305
+ }
1306
+ }
1307
+ else {
1308
+ console.log(chalk.gray(`\nOnboard later: jfl onboard ${servicePath}\n`));
1309
+ }
1310
+ }
1311
+ async function launchRalph(servicePath, serviceName, serviceType, description) {
1312
+ // Generate PRD file for Ralph
1313
+ const prdPath = join(servicePath, "PRD.json");
1314
+ const prd = {
1315
+ title: `Build ${serviceName}`,
1316
+ description,
1317
+ requirements: getTypeSpecificRequirements(serviceType),
1318
+ techStack: getTypeSpecificStack(serviceType),
1319
+ acceptance_criteria: getTypeSpecificCriteria(serviceType),
1320
+ };
1321
+ writeFileSync(prdPath, JSON.stringify(prd, null, 2));
1322
+ console.log(chalk.cyan(`\n🤖 Launching Ralph autonomous loop...\n`));
1323
+ console.log(chalk.gray(`PRD generated at ${prdPath}\n`));
1324
+ // Spawn Ralph with PRD
1325
+ const child = spawn("ralph-tui", ["run", "--prd", prdPath], {
1326
+ cwd: servicePath,
1327
+ stdio: "inherit",
1328
+ shell: true,
1329
+ });
1330
+ await new Promise((resolve) => {
1331
+ child.on("exit", () => resolve());
1332
+ });
1333
+ // Offer to onboard
1334
+ console.log();
1335
+ const shouldOnboard = await p.confirm({
1336
+ message: "Onboard this service to GTM now?",
1337
+ initialValue: true,
1338
+ });
1339
+ if (!p.isCancel(shouldOnboard) && shouldOnboard) {
1340
+ try {
1341
+ await onboardCommand(servicePath, { skipGit: true });
1342
+ }
1343
+ catch (error) {
1344
+ console.log(chalk.yellow(`\n⚠️ Onboard failed: ${error.message}`));
1345
+ console.log(chalk.gray(`You can onboard manually later: jfl onboard ${servicePath}\n`));
1346
+ }
1347
+ }
1348
+ else {
1349
+ console.log(chalk.gray(`\nOnboard later: jfl onboard ${servicePath}\n`));
1350
+ }
1351
+ }
1352
+ function getTypeSpecificRequirements(type) {
1353
+ switch (type) {
1354
+ case "api":
1355
+ return [
1356
+ "Implement health check endpoint",
1357
+ "Add request validation",
1358
+ "Implement core API routes",
1359
+ "Add error handling",
1360
+ "Write API tests",
1361
+ ];
1362
+ case "web":
1363
+ return [
1364
+ "Set up page layouts",
1365
+ "Build reusable components",
1366
+ "Add styling",
1367
+ "Connect to backend APIs",
1368
+ "Implement routing",
1369
+ ];
1370
+ case "worker":
1371
+ return [
1372
+ "Define job types",
1373
+ "Implement job handlers",
1374
+ "Add error handling and retries",
1375
+ "Set up job monitoring",
1376
+ "Write job tests",
1377
+ ];
1378
+ case "cli":
1379
+ return [
1380
+ "Define CLI commands",
1381
+ "Add command arguments/options",
1382
+ "Implement command logic",
1383
+ "Add help text",
1384
+ "Write command tests",
1385
+ ];
1386
+ default:
1387
+ return ["Implement core functionality", "Add tests", "Document usage"];
1388
+ }
1389
+ }
1390
+ function getTypeSpecificStack(type) {
1391
+ switch (type) {
1392
+ case "api":
1393
+ return ["Fastify", "TypeScript", "Vitest"];
1394
+ case "web":
1395
+ return ["Next.js", "React", "TypeScript"];
1396
+ case "worker":
1397
+ return ["BullMQ", "TypeScript", "Redis"];
1398
+ case "cli":
1399
+ return ["Commander", "TypeScript", "Chalk"];
1400
+ default:
1401
+ return ["TypeScript", "Node.js"];
1402
+ }
1403
+ }
1404
+ function getTypeSpecificCriteria(type) {
1405
+ switch (type) {
1406
+ case "api":
1407
+ return [
1408
+ "All endpoints return correct status codes",
1409
+ "Request validation works",
1410
+ "Error responses are consistent",
1411
+ "Health check returns 200",
1412
+ "Tests pass",
1413
+ ];
1414
+ case "web":
1415
+ return [
1416
+ "Pages render correctly",
1417
+ "Components are reusable",
1418
+ "Styling is consistent",
1419
+ "API calls work",
1420
+ "Build succeeds",
1421
+ ];
1422
+ case "worker":
1423
+ return [
1424
+ "Jobs process successfully",
1425
+ "Failed jobs retry correctly",
1426
+ "Worker handles shutdown gracefully",
1427
+ "Tests pass",
1428
+ ];
1429
+ case "cli":
1430
+ return [
1431
+ "Commands execute correctly",
1432
+ "Help text is clear",
1433
+ "Error messages are helpful",
1434
+ "Tests pass",
1435
+ ];
1436
+ default:
1437
+ return ["Core functionality works", "Tests pass", "Documentation is complete"];
1438
+ }
1439
+ }
1440
+ // ============================================================================
1441
+ // Manual Next Steps
1442
+ // ============================================================================
1443
+ function showManualNextSteps(location, name) {
1444
+ console.log(chalk.cyan("\n✓ Service scaffolded!\n"));
1445
+ console.log(chalk.gray("Next steps:"));
1446
+ console.log(chalk.gray(` 1. cd ${location}`));
1447
+ console.log(chalk.gray(` 2. npm install`));
1448
+ console.log(chalk.gray(` 3. npm run dev`));
1449
+ console.log(chalk.gray(` 4. Build your service`));
1450
+ console.log(chalk.gray(` 5. jfl onboard ${location}\n`));
1451
+ }
1452
+ //# sourceMappingURL=services-create.js.map