@qazuor/claude-code-config 0.5.0 → 0.6.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 (57) hide show
  1. package/README.md +106 -41
  2. package/dist/bin.cjs +963 -84
  3. package/dist/bin.cjs.map +1 -1
  4. package/dist/bin.js +963 -84
  5. package/dist/bin.js.map +1 -1
  6. package/dist/index.cjs +73 -56
  7. package/dist/index.cjs.map +1 -1
  8. package/dist/index.d.cts +2 -0
  9. package/dist/index.d.ts +2 -0
  10. package/dist/index.js +73 -56
  11. package/dist/index.js.map +1 -1
  12. package/package.json +23 -24
  13. package/templates/CLAUDE.md.template +60 -5
  14. package/templates/agents/README.md +58 -39
  15. package/templates/agents/_registry.json +43 -202
  16. package/templates/agents/engineering/{hono-engineer.md → api-engineer.md} +61 -70
  17. package/templates/agents/engineering/database-engineer.md +253 -0
  18. package/templates/agents/engineering/frontend-engineer.md +302 -0
  19. package/templates/hooks/on-notification.sh +0 -0
  20. package/templates/scripts/add-changelogs.sh +0 -0
  21. package/templates/scripts/generate-code-registry.ts +0 -0
  22. package/templates/scripts/health-check.sh +0 -0
  23. package/templates/scripts/sync-registry.sh +0 -0
  24. package/templates/scripts/telemetry-report.ts +0 -0
  25. package/templates/scripts/validate-docs.sh +0 -0
  26. package/templates/scripts/validate-registry.sh +0 -0
  27. package/templates/scripts/validate-structure.sh +0 -0
  28. package/templates/scripts/worktree-cleanup.sh +0 -0
  29. package/templates/scripts/worktree-create.sh +0 -0
  30. package/templates/skills/README.md +99 -90
  31. package/templates/skills/_registry.json +323 -16
  32. package/templates/skills/api-frameworks/express-patterns.md +411 -0
  33. package/templates/skills/api-frameworks/fastify-patterns.md +419 -0
  34. package/templates/skills/api-frameworks/hono-patterns.md +388 -0
  35. package/templates/skills/api-frameworks/nestjs-patterns.md +497 -0
  36. package/templates/skills/database/drizzle-patterns.md +449 -0
  37. package/templates/skills/database/mongoose-patterns.md +503 -0
  38. package/templates/skills/database/prisma-patterns.md +487 -0
  39. package/templates/skills/frontend-frameworks/astro-patterns.md +415 -0
  40. package/templates/skills/frontend-frameworks/nextjs-patterns.md +470 -0
  41. package/templates/skills/frontend-frameworks/react-patterns.md +516 -0
  42. package/templates/skills/frontend-frameworks/tanstack-start-patterns.md +469 -0
  43. package/templates/skills/patterns/atdd-methodology.md +364 -0
  44. package/templates/skills/patterns/bdd-methodology.md +281 -0
  45. package/templates/skills/patterns/clean-architecture.md +444 -0
  46. package/templates/skills/patterns/hexagonal-architecture.md +567 -0
  47. package/templates/skills/patterns/vertical-slice-architecture.md +502 -0
  48. package/templates/agents/engineering/astro-engineer.md +0 -293
  49. package/templates/agents/engineering/db-drizzle-engineer.md +0 -360
  50. package/templates/agents/engineering/express-engineer.md +0 -316
  51. package/templates/agents/engineering/fastify-engineer.md +0 -399
  52. package/templates/agents/engineering/mongoose-engineer.md +0 -473
  53. package/templates/agents/engineering/nestjs-engineer.md +0 -429
  54. package/templates/agents/engineering/nextjs-engineer.md +0 -451
  55. package/templates/agents/engineering/prisma-engineer.md +0 -432
  56. package/templates/agents/engineering/react-senior-dev.md +0 -394
  57. package/templates/agents/engineering/tanstack-start-engineer.md +0 -447
package/dist/bin.cjs CHANGED
@@ -274,15 +274,10 @@ var BUNDLES = [
274
274
  moduleDetails: {
275
275
  agents: [
276
276
  {
277
- id: "react-senior-dev",
278
- role: "React Architecture",
277
+ id: "frontend-engineer",
278
+ role: "Frontend Development",
279
279
  responsibilities: ["Component design", "State management", "Performance optimization"]
280
280
  },
281
- {
282
- id: "tanstack-start-engineer",
283
- role: "TanStack Specialist",
284
- responsibilities: ["Router setup", "Query patterns", "SSR configuration"]
285
- },
286
281
  {
287
282
  id: "ux-ui-designer",
288
283
  role: "UI/UX Design",
@@ -290,6 +285,8 @@ var BUNDLES = [
290
285
  }
291
286
  ],
292
287
  skills: [
288
+ { id: "react-patterns", purpose: "React component patterns" },
289
+ { id: "tanstack-start-patterns", purpose: "TanStack Router/Start patterns" },
293
290
  { id: "web-app-testing", purpose: "React Testing Library patterns" },
294
291
  { id: "shadcn-specialist", purpose: "Shadcn UI component usage" },
295
292
  { id: "accessibility-audit", purpose: "WCAG compliance" },
@@ -301,9 +298,10 @@ var BUNDLES = [
301
298
  docs: [{ id: "design-standards", topic: "UI/UX design standards" }]
302
299
  },
303
300
  modules: [
304
- { id: "react-senior-dev", category: "agents" },
305
- { id: "tanstack-start-engineer", category: "agents" },
301
+ { id: "frontend-engineer", category: "agents" },
306
302
  { id: "ux-ui-designer", category: "agents" },
303
+ { id: "react-patterns", category: "skills" },
304
+ { id: "tanstack-start-patterns", category: "skills" },
307
305
  { id: "web-app-testing", category: "skills" },
308
306
  { id: "shadcn-specialist", category: "skills" },
309
307
  { id: "accessibility-audit", category: "skills" },
@@ -339,15 +337,10 @@ var BUNDLES = [
339
337
  moduleDetails: {
340
338
  agents: [
341
339
  {
342
- id: "astro-engineer",
343
- role: "Astro Specialist",
340
+ id: "frontend-engineer",
341
+ role: "Frontend Development",
344
342
  responsibilities: ["Routing", "Islands architecture", "Build optimization"]
345
343
  },
346
- {
347
- id: "react-senior-dev",
348
- role: "React Components",
349
- responsibilities: ["Interactive components", "Client hydration"]
350
- },
351
344
  {
352
345
  id: "seo-ai-specialist",
353
346
  role: "SEO Optimization",
@@ -355,6 +348,8 @@ var BUNDLES = [
355
348
  }
356
349
  ],
357
350
  skills: [
351
+ { id: "astro-patterns", purpose: "Astro-specific patterns" },
352
+ { id: "react-patterns", purpose: "React island components" },
358
353
  { id: "web-app-testing", purpose: "Component testing" },
359
354
  { id: "vercel-specialist", purpose: "Deployment optimization" },
360
355
  { id: "performance-audit", purpose: "Core Web Vitals" }
@@ -363,9 +358,10 @@ var BUNDLES = [
363
358
  docs: []
364
359
  },
365
360
  modules: [
366
- { id: "astro-engineer", category: "agents" },
367
- { id: "react-senior-dev", category: "agents" },
361
+ { id: "frontend-engineer", category: "agents" },
368
362
  { id: "seo-ai-specialist", category: "agents" },
363
+ { id: "astro-patterns", category: "skills" },
364
+ { id: "react-patterns", category: "skills" },
369
365
  { id: "web-app-testing", category: "skills" },
370
366
  { id: "vercel-specialist", category: "skills" },
371
367
  { id: "performance-audit", category: "skills" }
@@ -398,17 +394,12 @@ var BUNDLES = [
398
394
  moduleDetails: {
399
395
  agents: [
400
396
  {
401
- id: "nextjs-engineer",
402
- role: "Next.js Specialist",
403
- responsibilities: ["App Router", "Server Actions", "Caching strategies"]
404
- },
405
- {
406
- id: "react-senior-dev",
407
- role: "React Components",
408
- responsibilities: ["Client components", "State management"]
397
+ id: "frontend-engineer",
398
+ role: "Frontend Development",
399
+ responsibilities: ["App Router", "Server Actions", "Client components"]
409
400
  },
410
401
  {
411
- id: "prisma-engineer",
402
+ id: "database-engineer",
412
403
  role: "Database",
413
404
  responsibilities: ["Schema design", "Migrations", "Query optimization"]
414
405
  },
@@ -419,6 +410,9 @@ var BUNDLES = [
419
410
  }
420
411
  ],
421
412
  skills: [
413
+ { id: "nextjs-patterns", purpose: "Next.js App Router patterns" },
414
+ { id: "react-patterns", purpose: "React component patterns" },
415
+ { id: "prisma-patterns", purpose: "Prisma ORM patterns" },
422
416
  { id: "web-app-testing", purpose: "Next.js testing patterns" },
423
417
  { id: "shadcn-specialist", purpose: "UI components" },
424
418
  { id: "vercel-specialist", purpose: "Deployment" },
@@ -430,10 +424,12 @@ var BUNDLES = [
430
424
  docs: []
431
425
  },
432
426
  modules: [
433
- { id: "nextjs-engineer", category: "agents" },
434
- { id: "react-senior-dev", category: "agents" },
435
- { id: "prisma-engineer", category: "agents" },
427
+ { id: "frontend-engineer", category: "agents" },
428
+ { id: "database-engineer", category: "agents" },
436
429
  { id: "ux-ui-designer", category: "agents" },
430
+ { id: "nextjs-patterns", category: "skills" },
431
+ { id: "react-patterns", category: "skills" },
432
+ { id: "prisma-patterns", category: "skills" },
437
433
  { id: "web-app-testing", category: "skills" },
438
434
  { id: "shadcn-specialist", category: "skills" },
439
435
  { id: "vercel-specialist", category: "skills" },
@@ -469,12 +465,12 @@ var BUNDLES = [
469
465
  moduleDetails: {
470
466
  agents: [
471
467
  {
472
- id: "express-engineer",
473
- role: "Express Specialist",
468
+ id: "api-engineer",
469
+ role: "API Development",
474
470
  responsibilities: ["Route design", "Middleware", "Error handling"]
475
471
  },
476
472
  {
477
- id: "prisma-engineer",
473
+ id: "database-engineer",
478
474
  role: "Database",
479
475
  responsibilities: ["Schema", "Migrations", "Queries"]
480
476
  },
@@ -485,6 +481,8 @@ var BUNDLES = [
485
481
  }
486
482
  ],
487
483
  skills: [
484
+ { id: "express-patterns", purpose: "Express.js patterns" },
485
+ { id: "prisma-patterns", purpose: "Prisma ORM patterns" },
488
486
  { id: "api-app-testing", purpose: "API testing with supertest" },
489
487
  { id: "error-handling-patterns", purpose: "Error middleware" },
490
488
  { id: "security-testing", purpose: "Security best practices" }
@@ -493,9 +491,11 @@ var BUNDLES = [
493
491
  docs: [{ id: "architecture-patterns", topic: "API architecture patterns" }]
494
492
  },
495
493
  modules: [
496
- { id: "express-engineer", category: "agents" },
497
- { id: "prisma-engineer", category: "agents" },
494
+ { id: "api-engineer", category: "agents" },
495
+ { id: "database-engineer", category: "agents" },
498
496
  { id: "node-typescript-engineer", category: "agents" },
497
+ { id: "express-patterns", category: "skills" },
498
+ { id: "prisma-patterns", category: "skills" },
499
499
  { id: "api-app-testing", category: "skills" },
500
500
  { id: "error-handling-patterns", category: "skills" },
501
501
  { id: "security-testing", category: "skills" },
@@ -529,13 +529,13 @@ var BUNDLES = [
529
529
  moduleDetails: {
530
530
  agents: [
531
531
  {
532
- id: "hono-engineer",
533
- role: "Hono Specialist",
532
+ id: "api-engineer",
533
+ role: "API Development",
534
534
  responsibilities: ["Route handlers", "Middleware", "OpenAPI integration"]
535
535
  },
536
536
  {
537
- id: "db-drizzle-engineer",
538
- role: "Drizzle Database",
537
+ id: "database-engineer",
538
+ role: "Database",
539
539
  responsibilities: ["Schema design", "Migrations", "Type-safe queries"]
540
540
  },
541
541
  {
@@ -545,6 +545,8 @@ var BUNDLES = [
545
545
  }
546
546
  ],
547
547
  skills: [
548
+ { id: "hono-patterns", purpose: "Hono framework patterns" },
549
+ { id: "drizzle-patterns", purpose: "Drizzle ORM patterns" },
548
550
  { id: "api-app-testing", purpose: "Hono testing patterns" },
549
551
  { id: "error-handling-patterns", purpose: "Error middleware" },
550
552
  { id: "security-testing", purpose: "Security validation" }
@@ -553,9 +555,11 @@ var BUNDLES = [
553
555
  docs: [{ id: "architecture-patterns", topic: "API architecture patterns" }]
554
556
  },
555
557
  modules: [
556
- { id: "hono-engineer", category: "agents" },
557
- { id: "db-drizzle-engineer", category: "agents" },
558
+ { id: "api-engineer", category: "agents" },
559
+ { id: "database-engineer", category: "agents" },
558
560
  { id: "node-typescript-engineer", category: "agents" },
561
+ { id: "hono-patterns", category: "skills" },
562
+ { id: "drizzle-patterns", category: "skills" },
559
563
  { id: "api-app-testing", category: "skills" },
560
564
  { id: "error-handling-patterns", category: "skills" },
561
565
  { id: "security-testing", category: "skills" },
@@ -786,7 +790,8 @@ var BUNDLES = [
786
790
  tags: ["database", "drizzle", "orm"],
787
791
  alternativeTo: ["prisma-database", "mongoose-database"],
788
792
  modules: [
789
- { id: "db-drizzle-engineer", category: "agents" },
793
+ { id: "database-engineer", category: "agents" },
794
+ { id: "drizzle-patterns", category: "skills" },
790
795
  { id: "json-data-auditor", category: "skills" }
791
796
  ]
792
797
  },
@@ -800,7 +805,8 @@ var BUNDLES = [
800
805
  tags: ["database", "prisma", "orm"],
801
806
  alternativeTo: ["drizzle-database", "mongoose-database"],
802
807
  modules: [
803
- { id: "prisma-engineer", category: "agents" },
808
+ { id: "database-engineer", category: "agents" },
809
+ { id: "prisma-patterns", category: "skills" },
804
810
  { id: "json-data-auditor", category: "skills" }
805
811
  ]
806
812
  },
@@ -814,7 +820,8 @@ var BUNDLES = [
814
820
  tags: ["database", "mongodb", "mongoose", "nosql"],
815
821
  alternativeTo: ["drizzle-database", "prisma-database"],
816
822
  modules: [
817
- { id: "mongoose-engineer", category: "agents" },
823
+ { id: "database-engineer", category: "agents" },
824
+ { id: "mongoose-patterns", category: "skills" },
818
825
  { id: "json-data-auditor", category: "skills" }
819
826
  ]
820
827
  },
@@ -831,7 +838,8 @@ var BUNDLES = [
831
838
  tags: ["api", "hono", "backend"],
832
839
  alternativeTo: ["express-api", "fastify-api", "nestjs-api"],
833
840
  modules: [
834
- { id: "hono-engineer", category: "agents" },
841
+ { id: "api-engineer", category: "agents" },
842
+ { id: "hono-patterns", category: "skills" },
835
843
  { id: "api-app-testing", category: "skills" },
836
844
  { id: "error-handling-patterns", category: "skills" }
837
845
  ]
@@ -846,7 +854,8 @@ var BUNDLES = [
846
854
  tags: ["api", "express", "backend"],
847
855
  alternativeTo: ["hono-api", "fastify-api", "nestjs-api"],
848
856
  modules: [
849
- { id: "express-engineer", category: "agents" },
857
+ { id: "api-engineer", category: "agents" },
858
+ { id: "express-patterns", category: "skills" },
850
859
  { id: "api-app-testing", category: "skills" },
851
860
  { id: "error-handling-patterns", category: "skills" }
852
861
  ]
@@ -861,7 +870,8 @@ var BUNDLES = [
861
870
  tags: ["api", "fastify", "backend", "performance"],
862
871
  alternativeTo: ["hono-api", "express-api", "nestjs-api"],
863
872
  modules: [
864
- { id: "fastify-engineer", category: "agents" },
873
+ { id: "api-engineer", category: "agents" },
874
+ { id: "fastify-patterns", category: "skills" },
865
875
  { id: "api-app-testing", category: "skills" },
866
876
  { id: "error-handling-patterns", category: "skills" }
867
877
  ]
@@ -876,7 +886,8 @@ var BUNDLES = [
876
886
  tags: ["api", "nestjs", "backend", "enterprise"],
877
887
  alternativeTo: ["hono-api", "express-api", "fastify-api"],
878
888
  modules: [
879
- { id: "nestjs-engineer", category: "agents" },
889
+ { id: "api-engineer", category: "agents" },
890
+ { id: "nestjs-patterns", category: "skills" },
880
891
  { id: "api-app-testing", category: "skills" },
881
892
  { id: "error-handling-patterns", category: "skills" }
882
893
  ]
@@ -893,8 +904,9 @@ var BUNDLES = [
893
904
  techStack: ["React", "Shadcn UI", "Tailwind CSS", "Radix UI"],
894
905
  tags: ["react", "ui", "components"],
895
906
  modules: [
896
- { id: "react-senior-dev", category: "agents" },
907
+ { id: "frontend-engineer", category: "agents" },
897
908
  { id: "ux-ui-designer", category: "agents" },
909
+ { id: "react-patterns", category: "skills" },
898
910
  { id: "shadcn-specialist", category: "skills" },
899
911
  { id: "brand-guidelines", category: "skills" },
900
912
  { id: "accessibility-audit", category: "skills" }
@@ -909,7 +921,8 @@ var BUNDLES = [
909
921
  techStack: ["React Hook Form", "Zod", "React", "TypeScript"],
910
922
  tags: ["react", "forms", "validation"],
911
923
  modules: [
912
- { id: "react-senior-dev", category: "agents" },
924
+ { id: "frontend-engineer", category: "agents" },
925
+ { id: "react-patterns", category: "skills" },
913
926
  { id: "react-hook-form-patterns", category: "skills" },
914
927
  { id: "shadcn-specialist", category: "skills" }
915
928
  ]
@@ -924,7 +937,8 @@ var BUNDLES = [
924
937
  tags: ["react", "state", "zustand"],
925
938
  alternativeTo: ["react-state-redux"],
926
939
  modules: [
927
- { id: "react-senior-dev", category: "agents" },
940
+ { id: "frontend-engineer", category: "agents" },
941
+ { id: "react-patterns", category: "skills" },
928
942
  { id: "zustand-patterns", category: "skills" },
929
943
  { id: "tanstack-query-patterns", category: "skills" }
930
944
  ]
@@ -939,7 +953,8 @@ var BUNDLES = [
939
953
  tags: ["react", "state", "redux"],
940
954
  alternativeTo: ["react-state-zustand"],
941
955
  modules: [
942
- { id: "react-senior-dev", category: "agents" },
956
+ { id: "frontend-engineer", category: "agents" },
957
+ { id: "react-patterns", category: "skills" },
943
958
  { id: "redux-toolkit-patterns", category: "skills" },
944
959
  { id: "tanstack-query-patterns", category: "skills", optional: true }
945
960
  ]
@@ -953,7 +968,8 @@ var BUNDLES = [
953
968
  techStack: ["NextAuth.js", "Auth.js", "Next.js", "Prisma"],
954
969
  tags: ["nextjs", "auth", "oauth"],
955
970
  modules: [
956
- { id: "nextjs-engineer", category: "agents" },
971
+ { id: "frontend-engineer", category: "agents" },
972
+ { id: "nextjs-patterns", category: "skills" },
957
973
  { id: "nextauth-patterns", category: "skills" },
958
974
  { id: "security-testing", category: "skills" }
959
975
  ]
@@ -967,8 +983,9 @@ var BUNDLES = [
967
983
  techStack: ["next-intl", "Next.js", "React", "TypeScript"],
968
984
  tags: ["nextjs", "i18n", "internationalization"],
969
985
  modules: [
970
- { id: "nextjs-engineer", category: "agents" },
986
+ { id: "frontend-engineer", category: "agents" },
971
987
  { id: "i18n-specialist", category: "agents", optional: true },
988
+ { id: "nextjs-patterns", category: "skills" },
972
989
  { id: "i18n-patterns", category: "skills" }
973
990
  ]
974
991
  },
@@ -1088,7 +1105,7 @@ var BUNDLES = [
1088
1105
  }
1089
1106
  ],
1090
1107
  skills: [
1091
- { id: "documentation-writer", purpose: "Documentation best practices" },
1108
+ { id: "markdown-formatter", purpose: "Markdown formatting" },
1092
1109
  { id: "mermaid-diagram-specialist", purpose: "Diagram creation" }
1093
1110
  ],
1094
1111
  commands: [
@@ -1103,7 +1120,7 @@ var BUNDLES = [
1103
1120
  },
1104
1121
  modules: [
1105
1122
  { id: "tech-writer", category: "agents" },
1106
- { id: "documentation-writer", category: "skills" },
1123
+ { id: "markdown-formatter", category: "skills" },
1107
1124
  { id: "mermaid-diagram-specialist", category: "skills" },
1108
1125
  { id: "update-docs", category: "commands" },
1109
1126
  { id: "markdown-format", category: "commands" },
@@ -6124,11 +6141,13 @@ function processTemplate(template, projectInfo, options) {
6124
6141
  const commands = options?.templateConfig?.commands;
6125
6142
  const targets = options?.templateConfig?.targets;
6126
6143
  const preferences = options?.claudeConfig?.preferences;
6144
+ const standards = options?.claudeConfig?.extras?.standards;
6127
6145
  content = content.replace(/\{\{PROJECT_NAME\}\}/g, projectInfo.name).replace(/\{\{PROJECT_DESCRIPTION\}\}/g, projectInfo.description).replace(/\{\{ORG\}\}/g, projectInfo.org).replace(/\{\{REPO\}\}/g, projectInfo.repo).replace(/\{\{ENTITY_TYPE\}\}/g, projectInfo.entityType).replace(/\{\{ENTITY_TYPE_PLURAL\}\}/g, projectInfo.entityTypePlural).replace(/\{\{LOCATION\}\}/g, projectInfo.location || "");
6128
6146
  const packageManager = preferences?.packageManager || "pnpm";
6129
6147
  content = content.replace(/\{\{PACKAGE_MANAGER\}\}/g, packageManager);
6130
- const coverageTarget = targets && "coverage" in targets ? String(targets.coverage) : "90";
6148
+ const coverageTarget = standards?.testing?.coverageTarget ? String(standards.testing.coverageTarget) : targets && "coverage" in targets ? String(targets.coverage) : "90";
6131
6149
  content = content.replace(/\{\{COVERAGE_TARGET\}\}/g, coverageTarget);
6150
+ content = processStandardsPlaceholders(content, standards, preferences);
6132
6151
  if (projectInfo.domain) {
6133
6152
  content = content.replace(/\{\{#if DOMAIN\}\}/g, "").replace(/\{\{\/if\}\}/g, "").replace(/\{\{DOMAIN\}\}/g, projectInfo.domain);
6134
6153
  } else {
@@ -6225,6 +6244,55 @@ function generateCommandsSection(commands, packageManager) {
6225
6244
  lines.push("```");
6226
6245
  return lines.join("\n");
6227
6246
  }
6247
+ function processStandardsPlaceholders(content, standards, preferences) {
6248
+ let result = content;
6249
+ const primaryLanguage = "TypeScript";
6250
+ result = result.replace(/\{\{PRIMARY_LANGUAGE\}\}/g, primaryLanguage);
6251
+ const maxFileLines = standards?.code?.maxFileLines?.toString() || "500";
6252
+ result = result.replace(/\{\{MAX_FILE_LINES\}\}/g, maxFileLines);
6253
+ const testPattern = standards?.testing?.testPattern === "gwt" ? "GWT (Given-When-Then)" : "AAA (Arrange, Act, Assert)";
6254
+ result = result.replace(/\{\{TEST_PATTERN\}\}/g, testPattern);
6255
+ const responseLanguage = preferences?.responseLanguage === "es" ? "Spanish" : preferences?.responseLanguage === "en" ? "English" : "Spanish";
6256
+ result = result.replace(/\{\{RESPONSE_LANGUAGE\}\}/g, responseLanguage);
6257
+ if (standards?.code?.namedExportsOnly) {
6258
+ result = result.replace(/\{\{#if NAMED_EXPORTS_ONLY\}\}/g, "").replace(/\{\{\/if\}\}/g, "");
6259
+ } else {
6260
+ result = result.replace(/\{\{#if NAMED_EXPORTS_ONLY\}\}[\s\S]*?\{\{\/if\}\}/g, "");
6261
+ }
6262
+ if (standards?.code?.jsDocRequired) {
6263
+ result = result.replace(/\{\{#if JSDOC_REQUIRED\}\}/g, "").replace(/\{\{\/if\}\}/g, "");
6264
+ } else {
6265
+ result = result.replace(/\{\{#if JSDOC_REQUIRED\}\}[\s\S]*?\{\{\/if\}\}/g, "");
6266
+ }
6267
+ if (standards?.code?.roroPattern) {
6268
+ result = result.replace(/\{\{#if RORO_PATTERN\}\}/g, "").replace(/\{\{\/if\}\}/g, "");
6269
+ } else {
6270
+ result = result.replace(/\{\{#if RORO_PATTERN\}\}[\s\S]*?\{\{\/if\}\}/g, "");
6271
+ }
6272
+ if (standards?.testing?.tddRequired) {
6273
+ result = result.replace(/\{\{#if TDD_REQUIRED\}\}/g, "").replace(/\{\{else\}\}[\s\S]*?\{\{\/if\}\}/g, "");
6274
+ } else {
6275
+ result = result.replace(/\{\{#if TDD_REQUIRED\}\}[\s\S]*?\{\{else\}\}/g, "").replace(/\{\{\/if\}\}/g, "");
6276
+ }
6277
+ const testLocation = standards?.testing?.testLocation;
6278
+ if (testLocation) {
6279
+ const testLocationText = testLocation === "colocated" ? "Co-located with source" : "Separate test directory";
6280
+ result = result.replace(/\{\{#if TEST_LOCATION\}\}/g, "").replace(/\{\{\/if\}\}/g, "").replace(/\{\{TEST_LOCATION\}\}/g, testLocationText);
6281
+ } else {
6282
+ result = result.replace(/\{\{#if TEST_LOCATION\}\}[\s\S]*?\{\{\/if\}\}/g, "");
6283
+ }
6284
+ if (preferences?.includeCoAuthor) {
6285
+ result = result.replace(/\{\{#if INCLUDE_CO_AUTHOR\}\}/g, "").replace(/\{\{\/if\}\}/g, "");
6286
+ } else {
6287
+ result = result.replace(/\{\{#if INCLUDE_CO_AUTHOR\}\}[\s\S]*?\{\{\/if\}\}/g, "");
6288
+ }
6289
+ if (preferences?.responseLanguage) {
6290
+ result = result.replace(/\{\{#if RESPONSE_LANGUAGE\}\}/g, "").replace(/\{\{\/if\}\}/g, "");
6291
+ } else {
6292
+ result = result.replace(/\{\{#if RESPONSE_LANGUAGE\}\}[\s\S]*?\{\{\/if\}\}/g, "");
6293
+ }
6294
+ return result;
6295
+ }
6228
6296
  function getMinimalTemplate() {
6229
6297
  return `# CLAUDE.md
6230
6298
 
@@ -6770,6 +6838,242 @@ async function generateScaffoldWithProgress(projectPath, options) {
6770
6838
  );
6771
6839
  }
6772
6840
 
6841
+ // src/lib/scaffold/settings-generator.ts
6842
+ init_cjs_shims();
6843
+
6844
+ // src/constants/claude-settings-defaults.ts
6845
+ init_cjs_shims();
6846
+ var DEFAULT_CLAUDE_SETTINGS = {
6847
+ model: "sonnet",
6848
+ alwaysThinkingEnabled: false,
6849
+ sandbox: {
6850
+ enabled: false,
6851
+ autoAllowBashIfSandboxed: true
6852
+ },
6853
+ permissions: {
6854
+ allow: [],
6855
+ deny: [],
6856
+ ask: [],
6857
+ defaultMode: "acceptEdits"
6858
+ },
6859
+ attribution: {
6860
+ commit: "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)\n\n Co-Authored-By: Claude <noreply@anthropic.com>",
6861
+ pr: "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)"
6862
+ },
6863
+ cleanupPeriodDays: 30,
6864
+ stopNotification: "beep"
6865
+ };
6866
+ var ATTRIBUTION_NO_COAUTHOR = {
6867
+ commit: "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)",
6868
+ pr: "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)"
6869
+ };
6870
+ var ATTRIBUTION_WITH_COAUTHOR = {
6871
+ commit: "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)\n\n Co-Authored-By: Claude <noreply@anthropic.com>",
6872
+ pr: "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)"
6873
+ };
6874
+ var BEEP_COMMAND = "echo -ne '\\007'";
6875
+ var MODEL_DESCRIPTIONS = {
6876
+ sonnet: "Claude Sonnet - Balanced performance and speed (recommended)",
6877
+ opus: "Claude Opus - Most capable, best for complex tasks",
6878
+ haiku: "Claude Haiku - Fastest, best for simple tasks",
6879
+ default: "Default - Let Claude Code decide based on task"
6880
+ };
6881
+ var PERMISSION_MODE_DESCRIPTIONS = {
6882
+ acceptEdits: "Accept edits automatically, ask for other operations",
6883
+ askAlways: "Ask for confirmation on all operations",
6884
+ viewOnly: "Read-only mode, no modifications allowed"
6885
+ };
6886
+ var STOP_NOTIFICATION_DESCRIPTIONS = {
6887
+ beep: "Play a beep sound when task completes",
6888
+ custom: "Run a custom command when task completes",
6889
+ none: "No notification"
6890
+ };
6891
+ var CLAUDE_SETTINGS_PRESETS = {
6892
+ /** Default preset - balanced settings */
6893
+ default: {
6894
+ ...DEFAULT_CLAUDE_SETTINGS
6895
+ },
6896
+ /** Performance preset - faster model, less confirmations */
6897
+ performance: {
6898
+ ...DEFAULT_CLAUDE_SETTINGS,
6899
+ model: "haiku",
6900
+ permissions: {
6901
+ ...DEFAULT_CLAUDE_SETTINGS.permissions,
6902
+ defaultMode: "acceptEdits"
6903
+ }
6904
+ },
6905
+ /** Quality preset - best model, extended thinking */
6906
+ quality: {
6907
+ ...DEFAULT_CLAUDE_SETTINGS,
6908
+ model: "opus",
6909
+ alwaysThinkingEnabled: true
6910
+ },
6911
+ /** Secure preset - sandbox enabled, more confirmations */
6912
+ secure: {
6913
+ ...DEFAULT_CLAUDE_SETTINGS,
6914
+ sandbox: {
6915
+ enabled: true,
6916
+ autoAllowBashIfSandboxed: true
6917
+ },
6918
+ permissions: {
6919
+ ...DEFAULT_CLAUDE_SETTINGS.permissions,
6920
+ defaultMode: "askAlways"
6921
+ }
6922
+ }
6923
+ };
6924
+ var PRESET_DESCRIPTIONS2 = {
6925
+ default: "Balanced settings for most projects",
6926
+ performance: "Faster responses with Haiku model",
6927
+ quality: "Best quality with Opus model and extended thinking",
6928
+ secure: "Enhanced security with sandbox and confirmations"
6929
+ };
6930
+
6931
+ // src/lib/scaffold/settings-generator.ts
6932
+ init_fs();
6933
+ async function generateSettings(projectPath, options) {
6934
+ const settingsPath = joinPath(projectPath, ".claude", "settings.json");
6935
+ const exists = await pathExists(settingsPath);
6936
+ if (exists && !options?.overwrite) {
6937
+ return {
6938
+ created: false,
6939
+ skipped: true,
6940
+ path: settingsPath
6941
+ };
6942
+ }
6943
+ try {
6944
+ const settings = buildSettingsJson(options);
6945
+ await writeJson(settingsPath, settings, { spaces: 2 });
6946
+ return {
6947
+ created: true,
6948
+ skipped: false,
6949
+ path: settingsPath
6950
+ };
6951
+ } catch (error) {
6952
+ return {
6953
+ created: false,
6954
+ skipped: false,
6955
+ path: settingsPath,
6956
+ error: error instanceof Error ? error.message : String(error)
6957
+ };
6958
+ }
6959
+ }
6960
+ async function generateSettingsWithSpinner(projectPath, options) {
6961
+ return withSpinner("Generating settings.json...", () => generateSettings(projectPath, options), {
6962
+ successText: "Created settings.json"
6963
+ });
6964
+ }
6965
+ async function generateSettingsLocal(projectPath, options) {
6966
+ const settingsPath = joinPath(projectPath, ".claude", "settings.local.json");
6967
+ const exists = await pathExists(settingsPath);
6968
+ if (exists && !options?.overwrite) {
6969
+ return {
6970
+ created: false,
6971
+ skipped: true,
6972
+ path: settingsPath
6973
+ };
6974
+ }
6975
+ try {
6976
+ const settings = buildSettingsLocalJson(options);
6977
+ await writeJson(settingsPath, settings, { spaces: 2 });
6978
+ return {
6979
+ created: true,
6980
+ skipped: false,
6981
+ path: settingsPath
6982
+ };
6983
+ } catch (error) {
6984
+ return {
6985
+ created: false,
6986
+ skipped: false,
6987
+ path: settingsPath,
6988
+ error: error instanceof Error ? error.message : String(error)
6989
+ };
6990
+ }
6991
+ }
6992
+ async function generateSettingsLocalWithSpinner(projectPath, options) {
6993
+ return withSpinner(
6994
+ "Generating settings.local.json...",
6995
+ () => generateSettingsLocal(projectPath, options),
6996
+ {
6997
+ successText: "Created settings.local.json"
6998
+ }
6999
+ );
7000
+ }
7001
+ function buildSettingsJson(options) {
7002
+ const claudeSettings = options?.claudeSettings || DEFAULT_CLAUDE_SETTINGS;
7003
+ const includeCoAuthor = options?.includeCoAuthor ?? true;
7004
+ const settings = {
7005
+ $schema: "https://json.schemastore.org/claude-code-settings.json"
7006
+ };
7007
+ if (claudeSettings.model && claudeSettings.model !== "default") {
7008
+ settings.model = claudeSettings.model;
7009
+ }
7010
+ if (claudeSettings.alwaysThinkingEnabled) {
7011
+ settings.alwaysThinkingEnabled = true;
7012
+ }
7013
+ if (claudeSettings.cleanupPeriodDays !== 30) {
7014
+ settings.cleanupPeriodDays = claudeSettings.cleanupPeriodDays;
7015
+ }
7016
+ settings.attribution = includeCoAuthor ? ATTRIBUTION_WITH_COAUTHOR : ATTRIBUTION_NO_COAUTHOR;
7017
+ settings.permissions = {
7018
+ allow: [],
7019
+ deny: []
7020
+ };
7021
+ if (claudeSettings.sandbox?.enabled) {
7022
+ settings.sandbox = {
7023
+ enabled: true,
7024
+ autoAllowBashIfSandboxed: claudeSettings.sandbox.autoAllowBashIfSandboxed ?? true
7025
+ };
7026
+ }
7027
+ return settings;
7028
+ }
7029
+ function buildSettingsLocalJson(options) {
7030
+ const claudeSettings = options?.claudeSettings || DEFAULT_CLAUDE_SETTINGS;
7031
+ const settings = {};
7032
+ const allow = [...options?.additionalAllow || []];
7033
+ const deny = [...options?.additionalDeny || []];
7034
+ if (claudeSettings.permissions?.allow) {
7035
+ allow.push(...claudeSettings.permissions.allow);
7036
+ }
7037
+ if (claudeSettings.permissions?.deny) {
7038
+ deny.push(...claudeSettings.permissions.deny);
7039
+ }
7040
+ settings.permissions = {
7041
+ allow: [...new Set(allow)],
7042
+ // Deduplicate
7043
+ deny: [...new Set(deny)],
7044
+ ask: claudeSettings.permissions?.ask || [],
7045
+ defaultMode: claudeSettings.permissions?.defaultMode || "acceptEdits"
7046
+ };
7047
+ if (claudeSettings.stopNotification && claudeSettings.stopNotification !== "none") {
7048
+ const command = claudeSettings.stopNotification === "custom" && claudeSettings.customStopCommand ? claudeSettings.customStopCommand : BEEP_COMMAND;
7049
+ settings.hooks = {
7050
+ Stop: [
7051
+ {
7052
+ hooks: [
7053
+ {
7054
+ type: "command",
7055
+ command,
7056
+ timeout: 5
7057
+ }
7058
+ ]
7059
+ }
7060
+ ],
7061
+ SubagentStop: [
7062
+ {
7063
+ hooks: [
7064
+ {
7065
+ type: "command",
7066
+ command,
7067
+ timeout: 5
7068
+ }
7069
+ ]
7070
+ }
7071
+ ]
7072
+ };
7073
+ }
7074
+ return settings;
7075
+ }
7076
+
6773
7077
  // src/lib/templates/config-replacer.ts
6774
7078
  init_cjs_shims();
6775
7079
  var fs4 = __toESM(require("fs/promises"), 1);
@@ -8717,6 +9021,142 @@ async function promptCICDConfig(options) {
8717
9021
  };
8718
9022
  }
8719
9023
 
9024
+ // src/cli/prompts/claude-settings.ts
9025
+ init_cjs_shims();
9026
+ async function promptClaudeSettings(options) {
9027
+ logger.section("Claude Code Settings", "\u{1F916}");
9028
+ const wantToConfigure = await confirm({
9029
+ message: "Would you like to configure Claude Code settings?",
9030
+ default: true
9031
+ });
9032
+ if (!wantToConfigure) {
9033
+ return {
9034
+ ...DEFAULT_CLAUDE_SETTINGS,
9035
+ attribution: options?.includeCoAuthor ? DEFAULT_CLAUDE_SETTINGS.attribution : {
9036
+ commit: "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)",
9037
+ pr: "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)"
9038
+ }
9039
+ };
9040
+ }
9041
+ const setupMode = await select({
9042
+ message: "How would you like to configure?",
9043
+ choices: [
9044
+ { name: "Use a preset", value: "preset" },
9045
+ { name: "Customize settings", value: "custom" }
9046
+ ],
9047
+ default: "preset"
9048
+ });
9049
+ if (setupMode === "preset") {
9050
+ return promptClaudeSettingsPreset(options);
9051
+ }
9052
+ return promptClaudeSettingsCustom(options);
9053
+ }
9054
+ async function promptClaudeSettingsPreset(options) {
9055
+ const preset = await select({
9056
+ message: "Choose a settings preset:",
9057
+ choices: Object.entries(PRESET_DESCRIPTIONS2).map(([key, description]) => ({
9058
+ name: `${key.charAt(0).toUpperCase() + key.slice(1)} - ${description}`,
9059
+ value: key
9060
+ })),
9061
+ default: "default"
9062
+ });
9063
+ const config = { ...CLAUDE_SETTINGS_PRESETS[preset] };
9064
+ if (options?.includeCoAuthor === false) {
9065
+ config.attribution = {
9066
+ commit: "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)",
9067
+ pr: "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)"
9068
+ };
9069
+ }
9070
+ return config;
9071
+ }
9072
+ async function promptClaudeSettingsCustom(options) {
9073
+ const defaults = options?.defaults || DEFAULT_CLAUDE_SETTINGS;
9074
+ const model = await select({
9075
+ message: "Default Claude model:",
9076
+ choices: Object.entries(MODEL_DESCRIPTIONS).map(([key, description]) => ({
9077
+ name: description,
9078
+ value: key
9079
+ })),
9080
+ default: defaults.model || "sonnet"
9081
+ });
9082
+ const alwaysThinkingEnabled = await confirm({
9083
+ message: "Enable extended thinking by default? (better for complex tasks)",
9084
+ default: defaults.alwaysThinkingEnabled ?? false
9085
+ });
9086
+ const sandboxEnabled = await confirm({
9087
+ message: "Enable bash sandboxing? (isolates shell commands for security)",
9088
+ default: defaults.sandbox?.enabled ?? false
9089
+ });
9090
+ const permissionMode = await select({
9091
+ message: "Default permission mode:",
9092
+ choices: Object.entries(PERMISSION_MODE_DESCRIPTIONS).map(([key, description]) => ({
9093
+ name: description,
9094
+ value: key
9095
+ })),
9096
+ default: defaults.permissions?.defaultMode || "acceptEdits"
9097
+ });
9098
+ const cleanupPeriodStr = await input({
9099
+ message: "Session cleanup period (days, 0 = delete all immediately):",
9100
+ default: String(defaults.cleanupPeriodDays ?? 30),
9101
+ validate: (value) => {
9102
+ const num = Number.parseInt(value, 10);
9103
+ if (Number.isNaN(num) || num < 0 || num > 365) {
9104
+ return "Please enter a number between 0 and 365";
9105
+ }
9106
+ return true;
9107
+ }
9108
+ });
9109
+ const cleanupPeriodDays = Number.parseInt(cleanupPeriodStr, 10);
9110
+ const stopNotification = await select({
9111
+ message: "Notification when task completes:",
9112
+ choices: Object.entries(STOP_NOTIFICATION_DESCRIPTIONS).map(([key, description]) => ({
9113
+ name: description,
9114
+ value: key
9115
+ })),
9116
+ default: defaults.stopNotification || "beep"
9117
+ });
9118
+ let customStopCommand;
9119
+ if (stopNotification === "custom") {
9120
+ customStopCommand = await input({
9121
+ message: "Custom command to run on task completion:",
9122
+ default: defaults.customStopCommand || ""
9123
+ });
9124
+ }
9125
+ const config = {
9126
+ model,
9127
+ alwaysThinkingEnabled,
9128
+ sandbox: {
9129
+ enabled: sandboxEnabled,
9130
+ autoAllowBashIfSandboxed: true
9131
+ },
9132
+ permissions: {
9133
+ allow: defaults.permissions?.allow || [],
9134
+ deny: defaults.permissions?.deny || [],
9135
+ ask: defaults.permissions?.ask || [],
9136
+ defaultMode: permissionMode
9137
+ },
9138
+ attribution: options?.includeCoAuthor === false ? {
9139
+ commit: "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)",
9140
+ pr: "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)"
9141
+ } : DEFAULT_CLAUDE_SETTINGS.attribution,
9142
+ cleanupPeriodDays,
9143
+ stopNotification,
9144
+ customStopCommand
9145
+ };
9146
+ if (stopNotification === "beep") {
9147
+ config.hooks = {
9148
+ Stop: [{ hooks: [{ type: "command", command: BEEP_COMMAND, timeout: 5 }] }],
9149
+ SubagentStop: [{ hooks: [{ type: "command", command: BEEP_COMMAND, timeout: 5 }] }]
9150
+ };
9151
+ } else if (stopNotification === "custom" && customStopCommand) {
9152
+ config.hooks = {
9153
+ Stop: [{ hooks: [{ type: "command", command: customStopCommand, timeout: 10 }] }],
9154
+ SubagentStop: [{ hooks: [{ type: "command", command: customStopCommand, timeout: 10 }] }]
9155
+ };
9156
+ }
9157
+ return config;
9158
+ }
9159
+
8720
9160
  // src/cli/prompts/code-style.ts
8721
9161
  init_cjs_shims();
8722
9162
  var CODE_STYLE_TOOLS = [
@@ -9854,7 +10294,95 @@ async function promptSubagentStopHook() {
9854
10294
 
9855
10295
  // src/cli/prompts/item-select.ts
9856
10296
  init_cjs_shims();
9857
- async function promptBatchAction(category, totalItems, hasPreset) {
10297
+
10298
+ // src/cli/prompts/mutual-exclusivity.ts
10299
+ init_cjs_shims();
10300
+ function getConflictingSelections(module2, selectedIds) {
10301
+ if (!module2.alternativeTo) {
10302
+ return [];
10303
+ }
10304
+ return module2.alternativeTo.filter((altId) => selectedIds.includes(altId));
10305
+ }
10306
+ function createChoicesWithExclusivity(modules, selectedIds, options) {
10307
+ const choices = [];
10308
+ for (const module2 of modules) {
10309
+ const conflicts = getConflictingSelections(module2, selectedIds);
10310
+ const isConflicting = conflicts.length > 0;
10311
+ const isPreselected = options?.preselected?.includes(module2.id);
10312
+ const isSelected = selectedIds.includes(module2.id);
10313
+ let name = module2.name;
10314
+ let description = module2.description;
10315
+ let disabled = false;
10316
+ if (isConflicting) {
10317
+ const conflictNames = conflicts.join(", ");
10318
+ disabled = options?.showConflictReason ? `Conflicts with: ${conflictNames}` : `Alternative to ${conflictNames} (already selected)`;
10319
+ name = colors.muted(`${module2.name} (incompatible)`);
10320
+ description = `${colors.muted("\u26A0")} ${disabled}`;
10321
+ }
10322
+ choices.push({
10323
+ name,
10324
+ value: module2.id,
10325
+ description,
10326
+ disabled: isConflicting ? disabled : false,
10327
+ checked: isSelected || isPreselected && !isConflicting
10328
+ });
10329
+ }
10330
+ return choices;
10331
+ }
10332
+ function validateNoConflicts(selectedIds, allModules) {
10333
+ const conflicts = [];
10334
+ for (const id of selectedIds) {
10335
+ const module2 = allModules.find((m) => m.id === id);
10336
+ if (!module2?.alternativeTo) continue;
10337
+ for (const altId of module2.alternativeTo) {
10338
+ if (selectedIds.includes(altId)) {
10339
+ const existingConflict = conflicts.find(
10340
+ (c) => c.selected === id && c.conflictsWith === altId || c.selected === altId && c.conflictsWith === id
10341
+ );
10342
+ if (!existingConflict) {
10343
+ conflicts.push({ selected: id, conflictsWith: altId });
10344
+ }
10345
+ }
10346
+ }
10347
+ }
10348
+ return conflicts;
10349
+ }
10350
+ function groupByExclusivity(modules) {
10351
+ const groups = /* @__PURE__ */ new Map();
10352
+ const processedIds = /* @__PURE__ */ new Set();
10353
+ for (const module2 of modules) {
10354
+ if (processedIds.has(module2.id)) continue;
10355
+ const groupMembers = /* @__PURE__ */ new Set([module2.id]);
10356
+ const toProcess = [...module2.alternativeTo || []];
10357
+ while (toProcess.length > 0) {
10358
+ const altId = toProcess.pop();
10359
+ if (!altId || groupMembers.has(altId)) continue;
10360
+ const altModule = modules.find((m) => m.id === altId);
10361
+ if (altModule) {
10362
+ groupMembers.add(altId);
10363
+ for (const transitiveAlt of altModule.alternativeTo || []) {
10364
+ if (!groupMembers.has(transitiveAlt)) {
10365
+ toProcess.push(transitiveAlt);
10366
+ }
10367
+ }
10368
+ }
10369
+ }
10370
+ if (groupMembers.size > 1) {
10371
+ const sortedMembers = Array.from(groupMembers).sort();
10372
+ const groupId = `exclusivity-group-${sortedMembers[0]}`;
10373
+ groups.set(groupId, sortedMembers);
10374
+ for (const memberId of groupMembers) {
10375
+ processedIds.add(memberId);
10376
+ }
10377
+ } else {
10378
+ processedIds.add(module2.id);
10379
+ }
10380
+ }
10381
+ return groups;
10382
+ }
10383
+
10384
+ // src/cli/prompts/item-select.ts
10385
+ async function promptBatchAction(category, totalItems, options) {
9858
10386
  const choices = [
9859
10387
  {
9860
10388
  name: "Install all (recommended)",
@@ -9872,7 +10400,14 @@ async function promptBatchAction(category, totalItems, hasPreset) {
9872
10400
  description: `Skip all ${category}`
9873
10401
  }
9874
10402
  ];
9875
- if (hasPreset) {
10403
+ if (options?.hasExclusivityGroups && options.exclusivityGroupCount) {
10404
+ choices.splice(1, 0, {
10405
+ name: colors.primary("Smart selection (handles conflicts)"),
10406
+ value: "smart",
10407
+ description: `Intelligent selection with ${options.exclusivityGroupCount} mutually exclusive group(s)`
10408
+ });
10409
+ }
10410
+ if (options?.hasPreset) {
9876
10411
  choices.splice(1, 0, {
9877
10412
  name: "Use preset for this category",
9878
10413
  value: "preset",
@@ -9882,27 +10417,43 @@ async function promptBatchAction(category, totalItems, hasPreset) {
9882
10417
  return select({
9883
10418
  message: `${capitalize(category)} selection (${totalItems} available):`,
9884
10419
  choices,
9885
- default: "all"
10420
+ default: options?.hasExclusivityGroups ? "smart" : "all"
9886
10421
  });
9887
10422
  }
9888
10423
  async function selectItemsFromCategory(category, items, options) {
9889
- const selectedItems = [];
9890
- const skippedItems = [];
9891
10424
  logger.newline();
9892
10425
  logger.subtitle(`${capitalize(category)} Selection`);
9893
10426
  logger.info(`${items.length} ${category} available`);
10427
+ const exclusivityGroups = groupByExclusivity(items);
10428
+ const hasExclusivityGroups = exclusivityGroups.size > 0;
10429
+ if (hasExclusivityGroups) {
10430
+ logger.info(
10431
+ colors.warning(
10432
+ `${exclusivityGroups.size} group(s) of mutually exclusive ${category} detected`
10433
+ )
10434
+ );
10435
+ }
9894
10436
  logger.newline();
9895
- const batchAction = await promptBatchAction(
9896
- category,
9897
- items.length,
9898
- options?.preselected && options.preselected.length > 0
9899
- );
10437
+ const batchAction = await promptBatchAction(category, items.length, {
10438
+ hasPreset: options?.preselected && options.preselected.length > 0,
10439
+ hasExclusivityGroups,
10440
+ exclusivityGroupCount: exclusivityGroups.size
10441
+ });
9900
10442
  if (batchAction === "all") {
9901
- return {
9902
- category,
9903
- selectedItems: items.map((i) => i.id),
9904
- skippedItems: []
9905
- };
10443
+ const conflicts = validateNoConflicts(
10444
+ items.map((i) => i.id),
10445
+ items
10446
+ );
10447
+ if (conflicts.length > 0) {
10448
+ logger.warn("Cannot install all: some modules are mutually exclusive.");
10449
+ logger.info("Switching to smart selection mode...");
10450
+ } else {
10451
+ return {
10452
+ category,
10453
+ selectedItems: items.map((i) => i.id),
10454
+ skippedItems: []
10455
+ };
10456
+ }
9906
10457
  }
9907
10458
  if (batchAction === "none") {
9908
10459
  return {
@@ -9913,24 +10464,106 @@ async function selectItemsFromCategory(category, items, options) {
9913
10464
  }
9914
10465
  if (batchAction === "preset" && options?.preselected) {
9915
10466
  const preselectedSet = new Set(options.preselected);
9916
- return {
10467
+ const preselectedIds = items.filter((i) => preselectedSet.has(i.id)).map((i) => i.id);
10468
+ const conflicts = validateNoConflicts(preselectedIds, items);
10469
+ if (conflicts.length > 0) {
10470
+ logger.warn("Preset contains conflicting modules. Switching to smart selection...");
10471
+ } else {
10472
+ return {
10473
+ category,
10474
+ selectedItems: preselectedIds,
10475
+ skippedItems: items.filter((i) => !preselectedSet.has(i.id)).map((i) => i.id)
10476
+ };
10477
+ }
10478
+ }
10479
+ if (batchAction === "smart" || batchAction === "all" || batchAction === "preset") {
10480
+ return selectItemsWithExclusivity(category, items, options);
10481
+ }
10482
+ return selectItemsOneByOne(category, items, options);
10483
+ }
10484
+ async function selectItemsWithExclusivity(category, items, options) {
10485
+ let selectedIds = options?.preselected?.filter((id) => items.some((i) => i.id === id)) || [];
10486
+ let confirmed = false;
10487
+ while (!confirmed) {
10488
+ const choices = createChoicesWithExclusivity(items, selectedIds, {
10489
+ preselected: options?.preselected,
10490
+ showConflictReason: true
10491
+ });
10492
+ logger.newline();
10493
+ logger.info(
10494
+ colors.muted("Items marked as incompatible are disabled based on your selections.")
10495
+ );
10496
+ logger.info(colors.muted("Uncheck items to enable their alternatives."));
10497
+ logger.newline();
10498
+ const newSelection = await checkbox({
10499
+ message: `Select ${category} (Space to toggle, Enter to confirm):`,
10500
+ choices: choices.map((c) => ({
10501
+ name: c.name,
10502
+ value: c.value,
10503
+ description: c.description,
10504
+ disabled: c.disabled,
10505
+ checked: c.checked
10506
+ })),
10507
+ required: false
10508
+ });
10509
+ const conflicts = validateNoConflicts(newSelection, items);
10510
+ if (conflicts.length > 0) {
10511
+ logger.warn("Your selection contains conflicts:");
10512
+ for (const conflict of conflicts) {
10513
+ const module1 = items.find((i) => i.id === conflict.selected)?.name || conflict.selected;
10514
+ const module2 = items.find((i) => i.id === conflict.conflictsWith)?.name || conflict.conflictsWith;
10515
+ logger.info(` ${colors.error("\u2022")} ${module1} and ${module2} are mutually exclusive`);
10516
+ }
10517
+ logger.info("Please adjust your selection.");
10518
+ selectedIds = newSelection;
10519
+ continue;
10520
+ }
10521
+ selectedIds = newSelection;
10522
+ showCategorySelectionSummary({
9917
10523
  category,
9918
- selectedItems: items.filter((i) => preselectedSet.has(i.id)).map((i) => i.id),
9919
- skippedItems: items.filter((i) => !preselectedSet.has(i.id)).map((i) => i.id)
9920
- };
10524
+ selectedItems: selectedIds,
10525
+ skippedItems: items.filter((i) => !selectedIds.includes(i.id)).map((i) => i.id)
10526
+ });
10527
+ confirmed = await confirm({
10528
+ message: "Is this selection correct?",
10529
+ default: true
10530
+ });
9921
10531
  }
10532
+ return {
10533
+ category,
10534
+ selectedItems: selectedIds,
10535
+ skippedItems: items.filter((i) => !selectedIds.includes(i.id)).map((i) => i.id)
10536
+ };
10537
+ }
10538
+ async function selectItemsOneByOne(category, items, options) {
10539
+ const selectedItems = [];
10540
+ const skippedItems = [];
9922
10541
  const remainingItems = [...items];
9923
10542
  let currentIndex = 0;
9924
10543
  while (currentIndex < remainingItems.length) {
9925
10544
  const item = remainingItems[currentIndex];
9926
10545
  const remaining = remainingItems.length - currentIndex - 1;
9927
10546
  const isPreselected = options?.preselected?.includes(item.id);
10547
+ const conflicts = item.alternativeTo?.filter((altId) => selectedItems.includes(altId)) || [];
10548
+ if (conflicts.length > 0) {
10549
+ const conflictNames = conflicts.map((id) => items.find((i) => i.id === id)?.name || id).join(", ");
10550
+ logger.info(
10551
+ colors.muted(`Skipping ${item.name} (conflicts with selected: ${conflictNames})`)
10552
+ );
10553
+ skippedItems.push(item.id);
10554
+ currentIndex++;
10555
+ continue;
10556
+ }
9928
10557
  const progress = colors.muted(`[${currentIndex + 1}/${remainingItems.length}]`);
9929
10558
  console.log(`
9930
10559
  ${progress} ${colors.bold(item.name)}`);
9931
10560
  if (options?.showDescriptions && item.description) {
9932
10561
  logger.note(item.description);
9933
10562
  }
10563
+ if (item.alternativeTo && item.alternativeTo.length > 0) {
10564
+ const altNames = item.alternativeTo.map((id) => items.find((i) => i.id === id)?.name || id).join(", ");
10565
+ logger.info(colors.warning(`\u26A0 Selecting this will disable: ${altNames}`));
10566
+ }
9934
10567
  const action = await promptItemWithShortcuts(item, {
9935
10568
  defaultInstall: isPreselected,
9936
10569
  remainingCount: remaining
@@ -9947,7 +10580,13 @@ ${progress} ${colors.bold(item.name)}`);
9947
10580
  case "install-rest":
9948
10581
  selectedItems.push(item.id);
9949
10582
  for (let i = currentIndex + 1; i < remainingItems.length; i++) {
9950
- selectedItems.push(remainingItems[i].id);
10583
+ const nextItem = remainingItems[i];
10584
+ const nextConflicts = nextItem.alternativeTo?.filter((altId) => selectedItems.includes(altId)) || [];
10585
+ if (nextConflicts.length === 0) {
10586
+ selectedItems.push(nextItem.id);
10587
+ } else {
10588
+ skippedItems.push(nextItem.id);
10589
+ }
9951
10590
  }
9952
10591
  currentIndex = remainingItems.length;
9953
10592
  break;
@@ -9989,6 +10628,17 @@ async function promptItemWithShortcuts(item, options) {
9989
10628
  default: options.defaultInstall !== false ? "install" : "skip"
9990
10629
  });
9991
10630
  }
10631
+ function showCategorySelectionSummary(result) {
10632
+ const { category, selectedItems, skippedItems } = result;
10633
+ logger.newline();
10634
+ logger.subtitle(`${capitalize(category)} Summary`);
10635
+ if (selectedItems.length > 0) {
10636
+ logger.success(`Selected (${selectedItems.length}): ${selectedItems.join(", ")}`);
10637
+ }
10638
+ if (skippedItems.length > 0) {
10639
+ logger.info(`Skipped (${skippedItems.length}): ${colors.muted(skippedItems.join(", "))}`);
10640
+ }
10641
+ }
9992
10642
  function capitalize(str) {
9993
10643
  return str.charAt(0).toUpperCase() + str.slice(1);
9994
10644
  }
@@ -10752,6 +11402,122 @@ async function confirmProjectInfo(info) {
10752
11402
  });
10753
11403
  }
10754
11404
 
11405
+ // src/cli/prompts/related-skills.ts
11406
+ init_cjs_shims();
11407
+ function getAllRelatedSkillIds(agentModules) {
11408
+ const relatedIds = /* @__PURE__ */ new Set();
11409
+ for (const agent of agentModules) {
11410
+ if (agent.relatedSkills) {
11411
+ for (const skillId of agent.relatedSkills) {
11412
+ relatedIds.add(skillId);
11413
+ }
11414
+ }
11415
+ }
11416
+ return Array.from(relatedIds);
11417
+ }
11418
+ function getAgentsWithRelatedSkills(selectedAgentIds, agentModules) {
11419
+ return agentModules.filter(
11420
+ (agent) => selectedAgentIds.includes(agent.id) && agent.relatedSkills && agent.relatedSkills.length > 0
11421
+ );
11422
+ }
11423
+ async function promptRelatedSkillsForAgent(agent, skillModules, options) {
11424
+ if (!agent.relatedSkills || agent.relatedSkills.length === 0) {
11425
+ return [];
11426
+ }
11427
+ const relatedSkillDefs = agent.relatedSkills.map((skillId) => skillModules.find((s) => s.id === skillId)).filter((s) => s !== void 0);
11428
+ if (relatedSkillDefs.length === 0) {
11429
+ return [];
11430
+ }
11431
+ if (relatedSkillDefs.length === 1) {
11432
+ const skill = relatedSkillDefs[0];
11433
+ const action = await select({
11434
+ message: `${colors.primary(agent.name)} has a related skill. Install it?`,
11435
+ choices: [
11436
+ {
11437
+ name: `Install ${skill.name}`,
11438
+ value: "install",
11439
+ description: skill.description
11440
+ },
11441
+ {
11442
+ name: "Skip",
11443
+ value: "skip",
11444
+ description: "Do not install this skill"
11445
+ }
11446
+ ],
11447
+ default: "install"
11448
+ });
11449
+ return action === "install" ? [skill.id] : [];
11450
+ }
11451
+ const choices = relatedSkillDefs.map((skill) => ({
11452
+ name: skill.name,
11453
+ value: skill.id,
11454
+ description: skill.description,
11455
+ checked: options?.preselected?.includes(skill.id) ?? false
11456
+ }));
11457
+ if (options?.allowMultiple !== false) {
11458
+ const selected2 = await checkbox({
11459
+ message: `${colors.primary(agent.name)} supports multiple frameworks. Select which patterns to install:`,
11460
+ choices,
11461
+ required: false
11462
+ });
11463
+ return selected2;
11464
+ }
11465
+ const choicesWithSkip = [
11466
+ ...choices.map((c) => ({ name: c.name, value: c.value, description: c.description })),
11467
+ {
11468
+ name: colors.muted("Skip (none)"),
11469
+ value: "__skip__",
11470
+ description: "Do not install any related skill for this agent"
11471
+ }
11472
+ ];
11473
+ const selected = await select({
11474
+ message: `${colors.primary(agent.name)} supports multiple frameworks. Select which patterns to use:`,
11475
+ choices: choicesWithSkip,
11476
+ default: choicesWithSkip[0]?.value
11477
+ });
11478
+ return selected === "__skip__" ? [] : [selected];
11479
+ }
11480
+ async function promptAllRelatedSkills(selectedAgentIds, registry, options) {
11481
+ const agentsWithRelated = getAgentsWithRelatedSkills(selectedAgentIds, registry.agents);
11482
+ const allRelatedSkillIds = getAllRelatedSkillIds(registry.agents);
11483
+ if (agentsWithRelated.length === 0) {
11484
+ return {
11485
+ relatedSkillsSelected: [],
11486
+ relatedSkillsSkipped: [],
11487
+ allRelatedSkillIds
11488
+ };
11489
+ }
11490
+ logger.newline();
11491
+ logger.subtitle("Framework-Specific Skills");
11492
+ logger.info(
11493
+ "The selected agents support framework-specific patterns. Select which ones to install."
11494
+ );
11495
+ logger.newline();
11496
+ const selectedSkills = [];
11497
+ const skippedSkills = [];
11498
+ for (const agent of agentsWithRelated) {
11499
+ const selected = await promptRelatedSkillsForAgent(agent, registry.skills, {
11500
+ preselected: options?.preselectedSkills,
11501
+ allowMultiple: options?.allowMultiplePerAgent
11502
+ });
11503
+ selectedSkills.push(...selected);
11504
+ const agentRelatedIds = agent.relatedSkills || [];
11505
+ const skipped = agentRelatedIds.filter((id) => !selected.includes(id));
11506
+ skippedSkills.push(...skipped);
11507
+ }
11508
+ const uniqueSelected = [...new Set(selectedSkills)];
11509
+ const uniqueSkipped = [...new Set(skippedSkills)].filter((id) => !uniqueSelected.includes(id));
11510
+ return {
11511
+ relatedSkillsSelected: uniqueSelected,
11512
+ relatedSkillsSkipped: uniqueSkipped,
11513
+ allRelatedSkillIds
11514
+ };
11515
+ }
11516
+ function filterOutRelatedSkills(skillModules, relatedSkillIds) {
11517
+ const relatedSet = new Set(relatedSkillIds);
11518
+ return skillModules.filter((skill) => !relatedSet.has(skill.id));
11519
+ }
11520
+
10755
11521
  // src/cli/prompts/scaffold.ts
10756
11522
  init_cjs_shims();
10757
11523
  async function promptScaffoldType(options) {
@@ -11513,11 +12279,66 @@ function createBundleSelectionStep() {
11513
12279
  }
11514
12280
  if (mode === "individual" || mode === "both") {
11515
12281
  const preselectedFromBundles = resolveBundles(result.selectedBundles);
11516
- const categories = ["agents", "skills", "commands", "docs"];
11517
12282
  if (ctx.registry) {
11518
12283
  logger.newline();
11519
12284
  logger.subtitle("Individual Module Selection");
11520
- for (const category of categories) {
12285
+ const agentPreselected = mode === "both" ? preselectedFromBundles.agents : [];
12286
+ const agentResult = await selectItemsFromCategory("agents", ctx.registry.agents, {
12287
+ preselected: agentPreselected,
12288
+ showDescriptions: true
12289
+ });
12290
+ if (mode === "both") {
12291
+ result.additionalModules.agents = agentResult.selectedItems.filter(
12292
+ (id) => !agentPreselected.includes(id)
12293
+ );
12294
+ } else {
12295
+ result.additionalModules.agents = agentResult.selectedItems;
12296
+ }
12297
+ const allSelectedAgents = [
12298
+ ...preselectedFromBundles.agents,
12299
+ ...result.additionalModules.agents
12300
+ ];
12301
+ const relatedSkillsResult = await promptAllRelatedSkills(
12302
+ allSelectedAgents,
12303
+ ctx.registry,
12304
+ {
12305
+ preselectedSkills: mode === "both" ? preselectedFromBundles.skills : [],
12306
+ allowMultiplePerAgent: true
12307
+ }
12308
+ );
12309
+ const skillPreselected = mode === "both" ? preselectedFromBundles.skills : [];
12310
+ const independentSkills = filterOutRelatedSkills(
12311
+ ctx.registry.skills,
12312
+ relatedSkillsResult.allRelatedSkillIds
12313
+ );
12314
+ const independentPreselected = skillPreselected.filter(
12315
+ (id) => !relatedSkillsResult.allRelatedSkillIds.includes(id)
12316
+ );
12317
+ let independentSkillsSelected = [];
12318
+ if (independentSkills.length > 0) {
12319
+ logger.newline();
12320
+ logger.info(
12321
+ colors.muted("Now selecting general-purpose skills (not framework-specific):")
12322
+ );
12323
+ const skillResult = await selectItemsFromCategory("skills", independentSkills, {
12324
+ preselected: independentPreselected,
12325
+ showDescriptions: true
12326
+ });
12327
+ independentSkillsSelected = skillResult.selectedItems;
12328
+ }
12329
+ const allSelectedSkills = [
12330
+ ...relatedSkillsResult.relatedSkillsSelected,
12331
+ ...independentSkillsSelected
12332
+ ];
12333
+ if (mode === "both") {
12334
+ result.additionalModules.skills = allSelectedSkills.filter(
12335
+ (id) => !skillPreselected.includes(id)
12336
+ );
12337
+ } else {
12338
+ result.additionalModules.skills = allSelectedSkills;
12339
+ }
12340
+ const otherCategories = ["commands", "docs"];
12341
+ for (const category of otherCategories) {
11521
12342
  const preselected = mode === "both" ? preselectedFromBundles[category] : [];
11522
12343
  const categoryResult = await selectItemsFromCategory(category, ctx.registry[category], {
11523
12344
  preselected,
@@ -11689,6 +12510,28 @@ function createTemplateConfigStep() {
11689
12510
  }
11690
12511
  };
11691
12512
  }
12513
+ function createClaudeSettingsStep() {
12514
+ return {
12515
+ metadata: {
12516
+ id: "claudeSettings",
12517
+ name: "Claude Code Settings",
12518
+ description: "Configure Claude Code model, permissions, and behavior",
12519
+ required: false
12520
+ },
12521
+ computeDefaults: (ctx) => ctx.claudeSettings,
12522
+ execute: async (ctx, defaults) => {
12523
+ const goBack = await promptBackOption(11, "Configure Claude Code settings or go back?");
12524
+ if (goBack) {
12525
+ return createResult(defaults, "back");
12526
+ }
12527
+ const value = await promptClaudeSettings({
12528
+ defaults,
12529
+ includeCoAuthor: ctx.preferences?.includeCoAuthor
12530
+ });
12531
+ return createResult(value, "next");
12532
+ }
12533
+ };
12534
+ }
11692
12535
  function createInitWizardConfig(projectPath, detection, registry) {
11693
12536
  void projectPath;
11694
12537
  void detection;
@@ -11737,6 +12580,10 @@ function createInitWizardConfig(projectPath, detection, registry) {
11737
12580
  {
11738
12581
  id: "templateConfig",
11739
12582
  definition: createTemplateConfigStep()
12583
+ },
12584
+ {
12585
+ id: "claudeSettings",
12586
+ definition: createClaudeSettingsStep()
11740
12587
  }
11741
12588
  ];
11742
12589
  return {
@@ -12061,7 +12908,7 @@ async function runInit(path10, options) {
12061
12908
  logger.warn("Configuration cancelled");
12062
12909
  return;
12063
12910
  }
12064
- const { config, skippedMcpConfigs, templateConfig, cicdConfig } = buildResult;
12911
+ const { config, skippedMcpConfigs, templateConfig, cicdConfig, claudeSettings } = buildResult;
12065
12912
  if (templateConfig) {
12066
12913
  config.templateConfig = templateConfig;
12067
12914
  }
@@ -12078,7 +12925,15 @@ async function runInit(path10, options) {
12078
12925
  showConfigSummary(config);
12079
12926
  return;
12080
12927
  }
12081
- await executeInstallation(projectPath, config, registry, templatesPath, options, cicdConfig);
12928
+ await executeInstallation(
12929
+ projectPath,
12930
+ config,
12931
+ registry,
12932
+ templatesPath,
12933
+ options,
12934
+ cicdConfig,
12935
+ claudeSettings
12936
+ );
12082
12937
  if (templateConfig && !options.noPlaceholders) {
12083
12938
  const claudePath = joinPath(projectPath, ".claude");
12084
12939
  await replaceTemplateConfigWithSpinner(claudePath, templateConfig);
@@ -12295,7 +13150,8 @@ async function buildInteractiveConfig(projectPath, detection, registry, options,
12295
13150
  codeStyleConfig,
12296
13151
  cicdConfig,
12297
13152
  folderPreferences,
12298
- templateConfig: templateConfigResult
13153
+ templateConfig: templateConfigResult,
13154
+ claudeSettings
12299
13155
  } = wizardResult.values;
12300
13156
  let mcpConfig = { level: "project", servers: [] };
12301
13157
  let skippedMcpConfigs = [];
@@ -12353,10 +13209,11 @@ async function buildInteractiveConfig(projectPath, detection, registry, options,
12353
13209
  config,
12354
13210
  skippedMcpConfigs,
12355
13211
  templateConfig: templateConfigResult,
12356
- cicdConfig
13212
+ cicdConfig,
13213
+ claudeSettings
12357
13214
  };
12358
13215
  }
12359
- async function executeInstallation(projectPath, config, registry, templatesPath, options, cicdConfig) {
13216
+ async function executeInstallation(projectPath, config, registry, templatesPath, options, cicdConfig, claudeSettings) {
12360
13217
  logger.newline();
12361
13218
  logger.title("Installing Configuration");
12362
13219
  if (config.scaffold.type === "full-project") {
@@ -12378,6 +13235,28 @@ async function executeInstallation(projectPath, config, registry, templatesPath,
12378
13235
  } else if (claudeMdResult.skipped) {
12379
13236
  logger.info("CLAUDE.md already exists, skipped");
12380
13237
  }
13238
+ if (claudeSettings) {
13239
+ const settingsResult = await generateSettingsWithSpinner(projectPath, {
13240
+ claudeSettings,
13241
+ includeCoAuthor: config.preferences.includeCoAuthor,
13242
+ overwrite: options.force
13243
+ });
13244
+ if (settingsResult.error) {
13245
+ logger.warn(`settings.json generation warning: ${settingsResult.error}`);
13246
+ } else if (settingsResult.skipped) {
13247
+ logger.info("settings.json already exists, skipped");
13248
+ }
13249
+ const settingsLocalResult = await generateSettingsLocalWithSpinner(projectPath, {
13250
+ claudeSettings,
13251
+ includeCoAuthor: config.preferences.includeCoAuthor,
13252
+ overwrite: options.force
13253
+ });
13254
+ if (settingsLocalResult.error) {
13255
+ logger.warn(`settings.local.json generation warning: ${settingsLocalResult.error}`);
13256
+ } else if (settingsLocalResult.skipped) {
13257
+ logger.info("settings.local.json already exists, skipped");
13258
+ }
13259
+ }
12381
13260
  const modulesByCategory = {
12382
13261
  agents: filterModules(registry, "agents", config.modules.agents.selected),
12383
13262
  skills: filterModules(registry, "skills", config.modules.skills.selected),