@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.js CHANGED
@@ -251,15 +251,10 @@ var BUNDLES = [
251
251
  moduleDetails: {
252
252
  agents: [
253
253
  {
254
- id: "react-senior-dev",
255
- role: "React Architecture",
254
+ id: "frontend-engineer",
255
+ role: "Frontend Development",
256
256
  responsibilities: ["Component design", "State management", "Performance optimization"]
257
257
  },
258
- {
259
- id: "tanstack-start-engineer",
260
- role: "TanStack Specialist",
261
- responsibilities: ["Router setup", "Query patterns", "SSR configuration"]
262
- },
263
258
  {
264
259
  id: "ux-ui-designer",
265
260
  role: "UI/UX Design",
@@ -267,6 +262,8 @@ var BUNDLES = [
267
262
  }
268
263
  ],
269
264
  skills: [
265
+ { id: "react-patterns", purpose: "React component patterns" },
266
+ { id: "tanstack-start-patterns", purpose: "TanStack Router/Start patterns" },
270
267
  { id: "web-app-testing", purpose: "React Testing Library patterns" },
271
268
  { id: "shadcn-specialist", purpose: "Shadcn UI component usage" },
272
269
  { id: "accessibility-audit", purpose: "WCAG compliance" },
@@ -278,9 +275,10 @@ var BUNDLES = [
278
275
  docs: [{ id: "design-standards", topic: "UI/UX design standards" }]
279
276
  },
280
277
  modules: [
281
- { id: "react-senior-dev", category: "agents" },
282
- { id: "tanstack-start-engineer", category: "agents" },
278
+ { id: "frontend-engineer", category: "agents" },
283
279
  { id: "ux-ui-designer", category: "agents" },
280
+ { id: "react-patterns", category: "skills" },
281
+ { id: "tanstack-start-patterns", category: "skills" },
284
282
  { id: "web-app-testing", category: "skills" },
285
283
  { id: "shadcn-specialist", category: "skills" },
286
284
  { id: "accessibility-audit", category: "skills" },
@@ -316,15 +314,10 @@ var BUNDLES = [
316
314
  moduleDetails: {
317
315
  agents: [
318
316
  {
319
- id: "astro-engineer",
320
- role: "Astro Specialist",
317
+ id: "frontend-engineer",
318
+ role: "Frontend Development",
321
319
  responsibilities: ["Routing", "Islands architecture", "Build optimization"]
322
320
  },
323
- {
324
- id: "react-senior-dev",
325
- role: "React Components",
326
- responsibilities: ["Interactive components", "Client hydration"]
327
- },
328
321
  {
329
322
  id: "seo-ai-specialist",
330
323
  role: "SEO Optimization",
@@ -332,6 +325,8 @@ var BUNDLES = [
332
325
  }
333
326
  ],
334
327
  skills: [
328
+ { id: "astro-patterns", purpose: "Astro-specific patterns" },
329
+ { id: "react-patterns", purpose: "React island components" },
335
330
  { id: "web-app-testing", purpose: "Component testing" },
336
331
  { id: "vercel-specialist", purpose: "Deployment optimization" },
337
332
  { id: "performance-audit", purpose: "Core Web Vitals" }
@@ -340,9 +335,10 @@ var BUNDLES = [
340
335
  docs: []
341
336
  },
342
337
  modules: [
343
- { id: "astro-engineer", category: "agents" },
344
- { id: "react-senior-dev", category: "agents" },
338
+ { id: "frontend-engineer", category: "agents" },
345
339
  { id: "seo-ai-specialist", category: "agents" },
340
+ { id: "astro-patterns", category: "skills" },
341
+ { id: "react-patterns", category: "skills" },
346
342
  { id: "web-app-testing", category: "skills" },
347
343
  { id: "vercel-specialist", category: "skills" },
348
344
  { id: "performance-audit", category: "skills" }
@@ -375,17 +371,12 @@ var BUNDLES = [
375
371
  moduleDetails: {
376
372
  agents: [
377
373
  {
378
- id: "nextjs-engineer",
379
- role: "Next.js Specialist",
380
- responsibilities: ["App Router", "Server Actions", "Caching strategies"]
381
- },
382
- {
383
- id: "react-senior-dev",
384
- role: "React Components",
385
- responsibilities: ["Client components", "State management"]
374
+ id: "frontend-engineer",
375
+ role: "Frontend Development",
376
+ responsibilities: ["App Router", "Server Actions", "Client components"]
386
377
  },
387
378
  {
388
- id: "prisma-engineer",
379
+ id: "database-engineer",
389
380
  role: "Database",
390
381
  responsibilities: ["Schema design", "Migrations", "Query optimization"]
391
382
  },
@@ -396,6 +387,9 @@ var BUNDLES = [
396
387
  }
397
388
  ],
398
389
  skills: [
390
+ { id: "nextjs-patterns", purpose: "Next.js App Router patterns" },
391
+ { id: "react-patterns", purpose: "React component patterns" },
392
+ { id: "prisma-patterns", purpose: "Prisma ORM patterns" },
399
393
  { id: "web-app-testing", purpose: "Next.js testing patterns" },
400
394
  { id: "shadcn-specialist", purpose: "UI components" },
401
395
  { id: "vercel-specialist", purpose: "Deployment" },
@@ -407,10 +401,12 @@ var BUNDLES = [
407
401
  docs: []
408
402
  },
409
403
  modules: [
410
- { id: "nextjs-engineer", category: "agents" },
411
- { id: "react-senior-dev", category: "agents" },
412
- { id: "prisma-engineer", category: "agents" },
404
+ { id: "frontend-engineer", category: "agents" },
405
+ { id: "database-engineer", category: "agents" },
413
406
  { id: "ux-ui-designer", category: "agents" },
407
+ { id: "nextjs-patterns", category: "skills" },
408
+ { id: "react-patterns", category: "skills" },
409
+ { id: "prisma-patterns", category: "skills" },
414
410
  { id: "web-app-testing", category: "skills" },
415
411
  { id: "shadcn-specialist", category: "skills" },
416
412
  { id: "vercel-specialist", category: "skills" },
@@ -446,12 +442,12 @@ var BUNDLES = [
446
442
  moduleDetails: {
447
443
  agents: [
448
444
  {
449
- id: "express-engineer",
450
- role: "Express Specialist",
445
+ id: "api-engineer",
446
+ role: "API Development",
451
447
  responsibilities: ["Route design", "Middleware", "Error handling"]
452
448
  },
453
449
  {
454
- id: "prisma-engineer",
450
+ id: "database-engineer",
455
451
  role: "Database",
456
452
  responsibilities: ["Schema", "Migrations", "Queries"]
457
453
  },
@@ -462,6 +458,8 @@ var BUNDLES = [
462
458
  }
463
459
  ],
464
460
  skills: [
461
+ { id: "express-patterns", purpose: "Express.js patterns" },
462
+ { id: "prisma-patterns", purpose: "Prisma ORM patterns" },
465
463
  { id: "api-app-testing", purpose: "API testing with supertest" },
466
464
  { id: "error-handling-patterns", purpose: "Error middleware" },
467
465
  { id: "security-testing", purpose: "Security best practices" }
@@ -470,9 +468,11 @@ var BUNDLES = [
470
468
  docs: [{ id: "architecture-patterns", topic: "API architecture patterns" }]
471
469
  },
472
470
  modules: [
473
- { id: "express-engineer", category: "agents" },
474
- { id: "prisma-engineer", category: "agents" },
471
+ { id: "api-engineer", category: "agents" },
472
+ { id: "database-engineer", category: "agents" },
475
473
  { id: "node-typescript-engineer", category: "agents" },
474
+ { id: "express-patterns", category: "skills" },
475
+ { id: "prisma-patterns", category: "skills" },
476
476
  { id: "api-app-testing", category: "skills" },
477
477
  { id: "error-handling-patterns", category: "skills" },
478
478
  { id: "security-testing", category: "skills" },
@@ -506,13 +506,13 @@ var BUNDLES = [
506
506
  moduleDetails: {
507
507
  agents: [
508
508
  {
509
- id: "hono-engineer",
510
- role: "Hono Specialist",
509
+ id: "api-engineer",
510
+ role: "API Development",
511
511
  responsibilities: ["Route handlers", "Middleware", "OpenAPI integration"]
512
512
  },
513
513
  {
514
- id: "db-drizzle-engineer",
515
- role: "Drizzle Database",
514
+ id: "database-engineer",
515
+ role: "Database",
516
516
  responsibilities: ["Schema design", "Migrations", "Type-safe queries"]
517
517
  },
518
518
  {
@@ -522,6 +522,8 @@ var BUNDLES = [
522
522
  }
523
523
  ],
524
524
  skills: [
525
+ { id: "hono-patterns", purpose: "Hono framework patterns" },
526
+ { id: "drizzle-patterns", purpose: "Drizzle ORM patterns" },
525
527
  { id: "api-app-testing", purpose: "Hono testing patterns" },
526
528
  { id: "error-handling-patterns", purpose: "Error middleware" },
527
529
  { id: "security-testing", purpose: "Security validation" }
@@ -530,9 +532,11 @@ var BUNDLES = [
530
532
  docs: [{ id: "architecture-patterns", topic: "API architecture patterns" }]
531
533
  },
532
534
  modules: [
533
- { id: "hono-engineer", category: "agents" },
534
- { id: "db-drizzle-engineer", category: "agents" },
535
+ { id: "api-engineer", category: "agents" },
536
+ { id: "database-engineer", category: "agents" },
535
537
  { id: "node-typescript-engineer", category: "agents" },
538
+ { id: "hono-patterns", category: "skills" },
539
+ { id: "drizzle-patterns", category: "skills" },
536
540
  { id: "api-app-testing", category: "skills" },
537
541
  { id: "error-handling-patterns", category: "skills" },
538
542
  { id: "security-testing", category: "skills" },
@@ -763,7 +767,8 @@ var BUNDLES = [
763
767
  tags: ["database", "drizzle", "orm"],
764
768
  alternativeTo: ["prisma-database", "mongoose-database"],
765
769
  modules: [
766
- { id: "db-drizzle-engineer", category: "agents" },
770
+ { id: "database-engineer", category: "agents" },
771
+ { id: "drizzle-patterns", category: "skills" },
767
772
  { id: "json-data-auditor", category: "skills" }
768
773
  ]
769
774
  },
@@ -777,7 +782,8 @@ var BUNDLES = [
777
782
  tags: ["database", "prisma", "orm"],
778
783
  alternativeTo: ["drizzle-database", "mongoose-database"],
779
784
  modules: [
780
- { id: "prisma-engineer", category: "agents" },
785
+ { id: "database-engineer", category: "agents" },
786
+ { id: "prisma-patterns", category: "skills" },
781
787
  { id: "json-data-auditor", category: "skills" }
782
788
  ]
783
789
  },
@@ -791,7 +797,8 @@ var BUNDLES = [
791
797
  tags: ["database", "mongodb", "mongoose", "nosql"],
792
798
  alternativeTo: ["drizzle-database", "prisma-database"],
793
799
  modules: [
794
- { id: "mongoose-engineer", category: "agents" },
800
+ { id: "database-engineer", category: "agents" },
801
+ { id: "mongoose-patterns", category: "skills" },
795
802
  { id: "json-data-auditor", category: "skills" }
796
803
  ]
797
804
  },
@@ -808,7 +815,8 @@ var BUNDLES = [
808
815
  tags: ["api", "hono", "backend"],
809
816
  alternativeTo: ["express-api", "fastify-api", "nestjs-api"],
810
817
  modules: [
811
- { id: "hono-engineer", category: "agents" },
818
+ { id: "api-engineer", category: "agents" },
819
+ { id: "hono-patterns", category: "skills" },
812
820
  { id: "api-app-testing", category: "skills" },
813
821
  { id: "error-handling-patterns", category: "skills" }
814
822
  ]
@@ -823,7 +831,8 @@ var BUNDLES = [
823
831
  tags: ["api", "express", "backend"],
824
832
  alternativeTo: ["hono-api", "fastify-api", "nestjs-api"],
825
833
  modules: [
826
- { id: "express-engineer", category: "agents" },
834
+ { id: "api-engineer", category: "agents" },
835
+ { id: "express-patterns", category: "skills" },
827
836
  { id: "api-app-testing", category: "skills" },
828
837
  { id: "error-handling-patterns", category: "skills" }
829
838
  ]
@@ -838,7 +847,8 @@ var BUNDLES = [
838
847
  tags: ["api", "fastify", "backend", "performance"],
839
848
  alternativeTo: ["hono-api", "express-api", "nestjs-api"],
840
849
  modules: [
841
- { id: "fastify-engineer", category: "agents" },
850
+ { id: "api-engineer", category: "agents" },
851
+ { id: "fastify-patterns", category: "skills" },
842
852
  { id: "api-app-testing", category: "skills" },
843
853
  { id: "error-handling-patterns", category: "skills" }
844
854
  ]
@@ -853,7 +863,8 @@ var BUNDLES = [
853
863
  tags: ["api", "nestjs", "backend", "enterprise"],
854
864
  alternativeTo: ["hono-api", "express-api", "fastify-api"],
855
865
  modules: [
856
- { id: "nestjs-engineer", category: "agents" },
866
+ { id: "api-engineer", category: "agents" },
867
+ { id: "nestjs-patterns", category: "skills" },
857
868
  { id: "api-app-testing", category: "skills" },
858
869
  { id: "error-handling-patterns", category: "skills" }
859
870
  ]
@@ -870,8 +881,9 @@ var BUNDLES = [
870
881
  techStack: ["React", "Shadcn UI", "Tailwind CSS", "Radix UI"],
871
882
  tags: ["react", "ui", "components"],
872
883
  modules: [
873
- { id: "react-senior-dev", category: "agents" },
884
+ { id: "frontend-engineer", category: "agents" },
874
885
  { id: "ux-ui-designer", category: "agents" },
886
+ { id: "react-patterns", category: "skills" },
875
887
  { id: "shadcn-specialist", category: "skills" },
876
888
  { id: "brand-guidelines", category: "skills" },
877
889
  { id: "accessibility-audit", category: "skills" }
@@ -886,7 +898,8 @@ var BUNDLES = [
886
898
  techStack: ["React Hook Form", "Zod", "React", "TypeScript"],
887
899
  tags: ["react", "forms", "validation"],
888
900
  modules: [
889
- { id: "react-senior-dev", category: "agents" },
901
+ { id: "frontend-engineer", category: "agents" },
902
+ { id: "react-patterns", category: "skills" },
890
903
  { id: "react-hook-form-patterns", category: "skills" },
891
904
  { id: "shadcn-specialist", category: "skills" }
892
905
  ]
@@ -901,7 +914,8 @@ var BUNDLES = [
901
914
  tags: ["react", "state", "zustand"],
902
915
  alternativeTo: ["react-state-redux"],
903
916
  modules: [
904
- { id: "react-senior-dev", category: "agents" },
917
+ { id: "frontend-engineer", category: "agents" },
918
+ { id: "react-patterns", category: "skills" },
905
919
  { id: "zustand-patterns", category: "skills" },
906
920
  { id: "tanstack-query-patterns", category: "skills" }
907
921
  ]
@@ -916,7 +930,8 @@ var BUNDLES = [
916
930
  tags: ["react", "state", "redux"],
917
931
  alternativeTo: ["react-state-zustand"],
918
932
  modules: [
919
- { id: "react-senior-dev", category: "agents" },
933
+ { id: "frontend-engineer", category: "agents" },
934
+ { id: "react-patterns", category: "skills" },
920
935
  { id: "redux-toolkit-patterns", category: "skills" },
921
936
  { id: "tanstack-query-patterns", category: "skills", optional: true }
922
937
  ]
@@ -930,7 +945,8 @@ var BUNDLES = [
930
945
  techStack: ["NextAuth.js", "Auth.js", "Next.js", "Prisma"],
931
946
  tags: ["nextjs", "auth", "oauth"],
932
947
  modules: [
933
- { id: "nextjs-engineer", category: "agents" },
948
+ { id: "frontend-engineer", category: "agents" },
949
+ { id: "nextjs-patterns", category: "skills" },
934
950
  { id: "nextauth-patterns", category: "skills" },
935
951
  { id: "security-testing", category: "skills" }
936
952
  ]
@@ -944,8 +960,9 @@ var BUNDLES = [
944
960
  techStack: ["next-intl", "Next.js", "React", "TypeScript"],
945
961
  tags: ["nextjs", "i18n", "internationalization"],
946
962
  modules: [
947
- { id: "nextjs-engineer", category: "agents" },
963
+ { id: "frontend-engineer", category: "agents" },
948
964
  { id: "i18n-specialist", category: "agents", optional: true },
965
+ { id: "nextjs-patterns", category: "skills" },
949
966
  { id: "i18n-patterns", category: "skills" }
950
967
  ]
951
968
  },
@@ -1065,7 +1082,7 @@ var BUNDLES = [
1065
1082
  }
1066
1083
  ],
1067
1084
  skills: [
1068
- { id: "documentation-writer", purpose: "Documentation best practices" },
1085
+ { id: "markdown-formatter", purpose: "Markdown formatting" },
1069
1086
  { id: "mermaid-diagram-specialist", purpose: "Diagram creation" }
1070
1087
  ],
1071
1088
  commands: [
@@ -1080,7 +1097,7 @@ var BUNDLES = [
1080
1097
  },
1081
1098
  modules: [
1082
1099
  { id: "tech-writer", category: "agents" },
1083
- { id: "documentation-writer", category: "skills" },
1100
+ { id: "markdown-formatter", category: "skills" },
1084
1101
  { id: "mermaid-diagram-specialist", category: "skills" },
1085
1102
  { id: "update-docs", category: "commands" },
1086
1103
  { id: "markdown-format", category: "commands" },
@@ -6101,11 +6118,13 @@ function processTemplate(template, projectInfo, options) {
6101
6118
  const commands = options?.templateConfig?.commands;
6102
6119
  const targets = options?.templateConfig?.targets;
6103
6120
  const preferences = options?.claudeConfig?.preferences;
6121
+ const standards = options?.claudeConfig?.extras?.standards;
6104
6122
  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 || "");
6105
6123
  const packageManager = preferences?.packageManager || "pnpm";
6106
6124
  content = content.replace(/\{\{PACKAGE_MANAGER\}\}/g, packageManager);
6107
- const coverageTarget = targets && "coverage" in targets ? String(targets.coverage) : "90";
6125
+ const coverageTarget = standards?.testing?.coverageTarget ? String(standards.testing.coverageTarget) : targets && "coverage" in targets ? String(targets.coverage) : "90";
6108
6126
  content = content.replace(/\{\{COVERAGE_TARGET\}\}/g, coverageTarget);
6127
+ content = processStandardsPlaceholders(content, standards, preferences);
6109
6128
  if (projectInfo.domain) {
6110
6129
  content = content.replace(/\{\{#if DOMAIN\}\}/g, "").replace(/\{\{\/if\}\}/g, "").replace(/\{\{DOMAIN\}\}/g, projectInfo.domain);
6111
6130
  } else {
@@ -6202,6 +6221,55 @@ function generateCommandsSection(commands, packageManager) {
6202
6221
  lines.push("```");
6203
6222
  return lines.join("\n");
6204
6223
  }
6224
+ function processStandardsPlaceholders(content, standards, preferences) {
6225
+ let result = content;
6226
+ const primaryLanguage = "TypeScript";
6227
+ result = result.replace(/\{\{PRIMARY_LANGUAGE\}\}/g, primaryLanguage);
6228
+ const maxFileLines = standards?.code?.maxFileLines?.toString() || "500";
6229
+ result = result.replace(/\{\{MAX_FILE_LINES\}\}/g, maxFileLines);
6230
+ const testPattern = standards?.testing?.testPattern === "gwt" ? "GWT (Given-When-Then)" : "AAA (Arrange, Act, Assert)";
6231
+ result = result.replace(/\{\{TEST_PATTERN\}\}/g, testPattern);
6232
+ const responseLanguage = preferences?.responseLanguage === "es" ? "Spanish" : preferences?.responseLanguage === "en" ? "English" : "Spanish";
6233
+ result = result.replace(/\{\{RESPONSE_LANGUAGE\}\}/g, responseLanguage);
6234
+ if (standards?.code?.namedExportsOnly) {
6235
+ result = result.replace(/\{\{#if NAMED_EXPORTS_ONLY\}\}/g, "").replace(/\{\{\/if\}\}/g, "");
6236
+ } else {
6237
+ result = result.replace(/\{\{#if NAMED_EXPORTS_ONLY\}\}[\s\S]*?\{\{\/if\}\}/g, "");
6238
+ }
6239
+ if (standards?.code?.jsDocRequired) {
6240
+ result = result.replace(/\{\{#if JSDOC_REQUIRED\}\}/g, "").replace(/\{\{\/if\}\}/g, "");
6241
+ } else {
6242
+ result = result.replace(/\{\{#if JSDOC_REQUIRED\}\}[\s\S]*?\{\{\/if\}\}/g, "");
6243
+ }
6244
+ if (standards?.code?.roroPattern) {
6245
+ result = result.replace(/\{\{#if RORO_PATTERN\}\}/g, "").replace(/\{\{\/if\}\}/g, "");
6246
+ } else {
6247
+ result = result.replace(/\{\{#if RORO_PATTERN\}\}[\s\S]*?\{\{\/if\}\}/g, "");
6248
+ }
6249
+ if (standards?.testing?.tddRequired) {
6250
+ result = result.replace(/\{\{#if TDD_REQUIRED\}\}/g, "").replace(/\{\{else\}\}[\s\S]*?\{\{\/if\}\}/g, "");
6251
+ } else {
6252
+ result = result.replace(/\{\{#if TDD_REQUIRED\}\}[\s\S]*?\{\{else\}\}/g, "").replace(/\{\{\/if\}\}/g, "");
6253
+ }
6254
+ const testLocation = standards?.testing?.testLocation;
6255
+ if (testLocation) {
6256
+ const testLocationText = testLocation === "colocated" ? "Co-located with source" : "Separate test directory";
6257
+ result = result.replace(/\{\{#if TEST_LOCATION\}\}/g, "").replace(/\{\{\/if\}\}/g, "").replace(/\{\{TEST_LOCATION\}\}/g, testLocationText);
6258
+ } else {
6259
+ result = result.replace(/\{\{#if TEST_LOCATION\}\}[\s\S]*?\{\{\/if\}\}/g, "");
6260
+ }
6261
+ if (preferences?.includeCoAuthor) {
6262
+ result = result.replace(/\{\{#if INCLUDE_CO_AUTHOR\}\}/g, "").replace(/\{\{\/if\}\}/g, "");
6263
+ } else {
6264
+ result = result.replace(/\{\{#if INCLUDE_CO_AUTHOR\}\}[\s\S]*?\{\{\/if\}\}/g, "");
6265
+ }
6266
+ if (preferences?.responseLanguage) {
6267
+ result = result.replace(/\{\{#if RESPONSE_LANGUAGE\}\}/g, "").replace(/\{\{\/if\}\}/g, "");
6268
+ } else {
6269
+ result = result.replace(/\{\{#if RESPONSE_LANGUAGE\}\}[\s\S]*?\{\{\/if\}\}/g, "");
6270
+ }
6271
+ return result;
6272
+ }
6205
6273
  function getMinimalTemplate() {
6206
6274
  return `# CLAUDE.md
6207
6275
 
@@ -6747,6 +6815,242 @@ async function generateScaffoldWithProgress(projectPath, options) {
6747
6815
  );
6748
6816
  }
6749
6817
 
6818
+ // src/lib/scaffold/settings-generator.ts
6819
+ init_esm_shims();
6820
+
6821
+ // src/constants/claude-settings-defaults.ts
6822
+ init_esm_shims();
6823
+ var DEFAULT_CLAUDE_SETTINGS = {
6824
+ model: "sonnet",
6825
+ alwaysThinkingEnabled: false,
6826
+ sandbox: {
6827
+ enabled: false,
6828
+ autoAllowBashIfSandboxed: true
6829
+ },
6830
+ permissions: {
6831
+ allow: [],
6832
+ deny: [],
6833
+ ask: [],
6834
+ defaultMode: "acceptEdits"
6835
+ },
6836
+ attribution: {
6837
+ commit: "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)\n\n Co-Authored-By: Claude <noreply@anthropic.com>",
6838
+ pr: "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)"
6839
+ },
6840
+ cleanupPeriodDays: 30,
6841
+ stopNotification: "beep"
6842
+ };
6843
+ var ATTRIBUTION_NO_COAUTHOR = {
6844
+ commit: "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)",
6845
+ pr: "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)"
6846
+ };
6847
+ var ATTRIBUTION_WITH_COAUTHOR = {
6848
+ commit: "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)\n\n Co-Authored-By: Claude <noreply@anthropic.com>",
6849
+ pr: "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)"
6850
+ };
6851
+ var BEEP_COMMAND = "echo -ne '\\007'";
6852
+ var MODEL_DESCRIPTIONS = {
6853
+ sonnet: "Claude Sonnet - Balanced performance and speed (recommended)",
6854
+ opus: "Claude Opus - Most capable, best for complex tasks",
6855
+ haiku: "Claude Haiku - Fastest, best for simple tasks",
6856
+ default: "Default - Let Claude Code decide based on task"
6857
+ };
6858
+ var PERMISSION_MODE_DESCRIPTIONS = {
6859
+ acceptEdits: "Accept edits automatically, ask for other operations",
6860
+ askAlways: "Ask for confirmation on all operations",
6861
+ viewOnly: "Read-only mode, no modifications allowed"
6862
+ };
6863
+ var STOP_NOTIFICATION_DESCRIPTIONS = {
6864
+ beep: "Play a beep sound when task completes",
6865
+ custom: "Run a custom command when task completes",
6866
+ none: "No notification"
6867
+ };
6868
+ var CLAUDE_SETTINGS_PRESETS = {
6869
+ /** Default preset - balanced settings */
6870
+ default: {
6871
+ ...DEFAULT_CLAUDE_SETTINGS
6872
+ },
6873
+ /** Performance preset - faster model, less confirmations */
6874
+ performance: {
6875
+ ...DEFAULT_CLAUDE_SETTINGS,
6876
+ model: "haiku",
6877
+ permissions: {
6878
+ ...DEFAULT_CLAUDE_SETTINGS.permissions,
6879
+ defaultMode: "acceptEdits"
6880
+ }
6881
+ },
6882
+ /** Quality preset - best model, extended thinking */
6883
+ quality: {
6884
+ ...DEFAULT_CLAUDE_SETTINGS,
6885
+ model: "opus",
6886
+ alwaysThinkingEnabled: true
6887
+ },
6888
+ /** Secure preset - sandbox enabled, more confirmations */
6889
+ secure: {
6890
+ ...DEFAULT_CLAUDE_SETTINGS,
6891
+ sandbox: {
6892
+ enabled: true,
6893
+ autoAllowBashIfSandboxed: true
6894
+ },
6895
+ permissions: {
6896
+ ...DEFAULT_CLAUDE_SETTINGS.permissions,
6897
+ defaultMode: "askAlways"
6898
+ }
6899
+ }
6900
+ };
6901
+ var PRESET_DESCRIPTIONS2 = {
6902
+ default: "Balanced settings for most projects",
6903
+ performance: "Faster responses with Haiku model",
6904
+ quality: "Best quality with Opus model and extended thinking",
6905
+ secure: "Enhanced security with sandbox and confirmations"
6906
+ };
6907
+
6908
+ // src/lib/scaffold/settings-generator.ts
6909
+ init_fs();
6910
+ async function generateSettings(projectPath, options) {
6911
+ const settingsPath = joinPath(projectPath, ".claude", "settings.json");
6912
+ const exists = await pathExists(settingsPath);
6913
+ if (exists && !options?.overwrite) {
6914
+ return {
6915
+ created: false,
6916
+ skipped: true,
6917
+ path: settingsPath
6918
+ };
6919
+ }
6920
+ try {
6921
+ const settings = buildSettingsJson(options);
6922
+ await writeJson(settingsPath, settings, { spaces: 2 });
6923
+ return {
6924
+ created: true,
6925
+ skipped: false,
6926
+ path: settingsPath
6927
+ };
6928
+ } catch (error) {
6929
+ return {
6930
+ created: false,
6931
+ skipped: false,
6932
+ path: settingsPath,
6933
+ error: error instanceof Error ? error.message : String(error)
6934
+ };
6935
+ }
6936
+ }
6937
+ async function generateSettingsWithSpinner(projectPath, options) {
6938
+ return withSpinner("Generating settings.json...", () => generateSettings(projectPath, options), {
6939
+ successText: "Created settings.json"
6940
+ });
6941
+ }
6942
+ async function generateSettingsLocal(projectPath, options) {
6943
+ const settingsPath = joinPath(projectPath, ".claude", "settings.local.json");
6944
+ const exists = await pathExists(settingsPath);
6945
+ if (exists && !options?.overwrite) {
6946
+ return {
6947
+ created: false,
6948
+ skipped: true,
6949
+ path: settingsPath
6950
+ };
6951
+ }
6952
+ try {
6953
+ const settings = buildSettingsLocalJson(options);
6954
+ await writeJson(settingsPath, settings, { spaces: 2 });
6955
+ return {
6956
+ created: true,
6957
+ skipped: false,
6958
+ path: settingsPath
6959
+ };
6960
+ } catch (error) {
6961
+ return {
6962
+ created: false,
6963
+ skipped: false,
6964
+ path: settingsPath,
6965
+ error: error instanceof Error ? error.message : String(error)
6966
+ };
6967
+ }
6968
+ }
6969
+ async function generateSettingsLocalWithSpinner(projectPath, options) {
6970
+ return withSpinner(
6971
+ "Generating settings.local.json...",
6972
+ () => generateSettingsLocal(projectPath, options),
6973
+ {
6974
+ successText: "Created settings.local.json"
6975
+ }
6976
+ );
6977
+ }
6978
+ function buildSettingsJson(options) {
6979
+ const claudeSettings = options?.claudeSettings || DEFAULT_CLAUDE_SETTINGS;
6980
+ const includeCoAuthor = options?.includeCoAuthor ?? true;
6981
+ const settings = {
6982
+ $schema: "https://json.schemastore.org/claude-code-settings.json"
6983
+ };
6984
+ if (claudeSettings.model && claudeSettings.model !== "default") {
6985
+ settings.model = claudeSettings.model;
6986
+ }
6987
+ if (claudeSettings.alwaysThinkingEnabled) {
6988
+ settings.alwaysThinkingEnabled = true;
6989
+ }
6990
+ if (claudeSettings.cleanupPeriodDays !== 30) {
6991
+ settings.cleanupPeriodDays = claudeSettings.cleanupPeriodDays;
6992
+ }
6993
+ settings.attribution = includeCoAuthor ? ATTRIBUTION_WITH_COAUTHOR : ATTRIBUTION_NO_COAUTHOR;
6994
+ settings.permissions = {
6995
+ allow: [],
6996
+ deny: []
6997
+ };
6998
+ if (claudeSettings.sandbox?.enabled) {
6999
+ settings.sandbox = {
7000
+ enabled: true,
7001
+ autoAllowBashIfSandboxed: claudeSettings.sandbox.autoAllowBashIfSandboxed ?? true
7002
+ };
7003
+ }
7004
+ return settings;
7005
+ }
7006
+ function buildSettingsLocalJson(options) {
7007
+ const claudeSettings = options?.claudeSettings || DEFAULT_CLAUDE_SETTINGS;
7008
+ const settings = {};
7009
+ const allow = [...options?.additionalAllow || []];
7010
+ const deny = [...options?.additionalDeny || []];
7011
+ if (claudeSettings.permissions?.allow) {
7012
+ allow.push(...claudeSettings.permissions.allow);
7013
+ }
7014
+ if (claudeSettings.permissions?.deny) {
7015
+ deny.push(...claudeSettings.permissions.deny);
7016
+ }
7017
+ settings.permissions = {
7018
+ allow: [...new Set(allow)],
7019
+ // Deduplicate
7020
+ deny: [...new Set(deny)],
7021
+ ask: claudeSettings.permissions?.ask || [],
7022
+ defaultMode: claudeSettings.permissions?.defaultMode || "acceptEdits"
7023
+ };
7024
+ if (claudeSettings.stopNotification && claudeSettings.stopNotification !== "none") {
7025
+ const command = claudeSettings.stopNotification === "custom" && claudeSettings.customStopCommand ? claudeSettings.customStopCommand : BEEP_COMMAND;
7026
+ settings.hooks = {
7027
+ Stop: [
7028
+ {
7029
+ hooks: [
7030
+ {
7031
+ type: "command",
7032
+ command,
7033
+ timeout: 5
7034
+ }
7035
+ ]
7036
+ }
7037
+ ],
7038
+ SubagentStop: [
7039
+ {
7040
+ hooks: [
7041
+ {
7042
+ type: "command",
7043
+ command,
7044
+ timeout: 5
7045
+ }
7046
+ ]
7047
+ }
7048
+ ]
7049
+ };
7050
+ }
7051
+ return settings;
7052
+ }
7053
+
6750
7054
  // src/lib/templates/config-replacer.ts
6751
7055
  init_esm_shims();
6752
7056
  import * as fs4 from "fs/promises";
@@ -8700,6 +9004,142 @@ async function promptCICDConfig(options) {
8700
9004
  };
8701
9005
  }
8702
9006
 
9007
+ // src/cli/prompts/claude-settings.ts
9008
+ init_esm_shims();
9009
+ async function promptClaudeSettings(options) {
9010
+ logger.section("Claude Code Settings", "\u{1F916}");
9011
+ const wantToConfigure = await confirm({
9012
+ message: "Would you like to configure Claude Code settings?",
9013
+ default: true
9014
+ });
9015
+ if (!wantToConfigure) {
9016
+ return {
9017
+ ...DEFAULT_CLAUDE_SETTINGS,
9018
+ attribution: options?.includeCoAuthor ? DEFAULT_CLAUDE_SETTINGS.attribution : {
9019
+ commit: "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)",
9020
+ pr: "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)"
9021
+ }
9022
+ };
9023
+ }
9024
+ const setupMode = await select({
9025
+ message: "How would you like to configure?",
9026
+ choices: [
9027
+ { name: "Use a preset", value: "preset" },
9028
+ { name: "Customize settings", value: "custom" }
9029
+ ],
9030
+ default: "preset"
9031
+ });
9032
+ if (setupMode === "preset") {
9033
+ return promptClaudeSettingsPreset(options);
9034
+ }
9035
+ return promptClaudeSettingsCustom(options);
9036
+ }
9037
+ async function promptClaudeSettingsPreset(options) {
9038
+ const preset = await select({
9039
+ message: "Choose a settings preset:",
9040
+ choices: Object.entries(PRESET_DESCRIPTIONS2).map(([key, description]) => ({
9041
+ name: `${key.charAt(0).toUpperCase() + key.slice(1)} - ${description}`,
9042
+ value: key
9043
+ })),
9044
+ default: "default"
9045
+ });
9046
+ const config = { ...CLAUDE_SETTINGS_PRESETS[preset] };
9047
+ if (options?.includeCoAuthor === false) {
9048
+ config.attribution = {
9049
+ commit: "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)",
9050
+ pr: "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)"
9051
+ };
9052
+ }
9053
+ return config;
9054
+ }
9055
+ async function promptClaudeSettingsCustom(options) {
9056
+ const defaults = options?.defaults || DEFAULT_CLAUDE_SETTINGS;
9057
+ const model = await select({
9058
+ message: "Default Claude model:",
9059
+ choices: Object.entries(MODEL_DESCRIPTIONS).map(([key, description]) => ({
9060
+ name: description,
9061
+ value: key
9062
+ })),
9063
+ default: defaults.model || "sonnet"
9064
+ });
9065
+ const alwaysThinkingEnabled = await confirm({
9066
+ message: "Enable extended thinking by default? (better for complex tasks)",
9067
+ default: defaults.alwaysThinkingEnabled ?? false
9068
+ });
9069
+ const sandboxEnabled = await confirm({
9070
+ message: "Enable bash sandboxing? (isolates shell commands for security)",
9071
+ default: defaults.sandbox?.enabled ?? false
9072
+ });
9073
+ const permissionMode = await select({
9074
+ message: "Default permission mode:",
9075
+ choices: Object.entries(PERMISSION_MODE_DESCRIPTIONS).map(([key, description]) => ({
9076
+ name: description,
9077
+ value: key
9078
+ })),
9079
+ default: defaults.permissions?.defaultMode || "acceptEdits"
9080
+ });
9081
+ const cleanupPeriodStr = await input({
9082
+ message: "Session cleanup period (days, 0 = delete all immediately):",
9083
+ default: String(defaults.cleanupPeriodDays ?? 30),
9084
+ validate: (value) => {
9085
+ const num = Number.parseInt(value, 10);
9086
+ if (Number.isNaN(num) || num < 0 || num > 365) {
9087
+ return "Please enter a number between 0 and 365";
9088
+ }
9089
+ return true;
9090
+ }
9091
+ });
9092
+ const cleanupPeriodDays = Number.parseInt(cleanupPeriodStr, 10);
9093
+ const stopNotification = await select({
9094
+ message: "Notification when task completes:",
9095
+ choices: Object.entries(STOP_NOTIFICATION_DESCRIPTIONS).map(([key, description]) => ({
9096
+ name: description,
9097
+ value: key
9098
+ })),
9099
+ default: defaults.stopNotification || "beep"
9100
+ });
9101
+ let customStopCommand;
9102
+ if (stopNotification === "custom") {
9103
+ customStopCommand = await input({
9104
+ message: "Custom command to run on task completion:",
9105
+ default: defaults.customStopCommand || ""
9106
+ });
9107
+ }
9108
+ const config = {
9109
+ model,
9110
+ alwaysThinkingEnabled,
9111
+ sandbox: {
9112
+ enabled: sandboxEnabled,
9113
+ autoAllowBashIfSandboxed: true
9114
+ },
9115
+ permissions: {
9116
+ allow: defaults.permissions?.allow || [],
9117
+ deny: defaults.permissions?.deny || [],
9118
+ ask: defaults.permissions?.ask || [],
9119
+ defaultMode: permissionMode
9120
+ },
9121
+ attribution: options?.includeCoAuthor === false ? {
9122
+ commit: "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)",
9123
+ pr: "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)"
9124
+ } : DEFAULT_CLAUDE_SETTINGS.attribution,
9125
+ cleanupPeriodDays,
9126
+ stopNotification,
9127
+ customStopCommand
9128
+ };
9129
+ if (stopNotification === "beep") {
9130
+ config.hooks = {
9131
+ Stop: [{ hooks: [{ type: "command", command: BEEP_COMMAND, timeout: 5 }] }],
9132
+ SubagentStop: [{ hooks: [{ type: "command", command: BEEP_COMMAND, timeout: 5 }] }]
9133
+ };
9134
+ } else if (stopNotification === "custom" && customStopCommand) {
9135
+ config.hooks = {
9136
+ Stop: [{ hooks: [{ type: "command", command: customStopCommand, timeout: 10 }] }],
9137
+ SubagentStop: [{ hooks: [{ type: "command", command: customStopCommand, timeout: 10 }] }]
9138
+ };
9139
+ }
9140
+ return config;
9141
+ }
9142
+
8703
9143
  // src/cli/prompts/code-style.ts
8704
9144
  init_esm_shims();
8705
9145
  var CODE_STYLE_TOOLS = [
@@ -9837,7 +10277,95 @@ async function promptSubagentStopHook() {
9837
10277
 
9838
10278
  // src/cli/prompts/item-select.ts
9839
10279
  init_esm_shims();
9840
- async function promptBatchAction(category, totalItems, hasPreset) {
10280
+
10281
+ // src/cli/prompts/mutual-exclusivity.ts
10282
+ init_esm_shims();
10283
+ function getConflictingSelections(module, selectedIds) {
10284
+ if (!module.alternativeTo) {
10285
+ return [];
10286
+ }
10287
+ return module.alternativeTo.filter((altId) => selectedIds.includes(altId));
10288
+ }
10289
+ function createChoicesWithExclusivity(modules, selectedIds, options) {
10290
+ const choices = [];
10291
+ for (const module of modules) {
10292
+ const conflicts = getConflictingSelections(module, selectedIds);
10293
+ const isConflicting = conflicts.length > 0;
10294
+ const isPreselected = options?.preselected?.includes(module.id);
10295
+ const isSelected = selectedIds.includes(module.id);
10296
+ let name = module.name;
10297
+ let description = module.description;
10298
+ let disabled = false;
10299
+ if (isConflicting) {
10300
+ const conflictNames = conflicts.join(", ");
10301
+ disabled = options?.showConflictReason ? `Conflicts with: ${conflictNames}` : `Alternative to ${conflictNames} (already selected)`;
10302
+ name = colors.muted(`${module.name} (incompatible)`);
10303
+ description = `${colors.muted("\u26A0")} ${disabled}`;
10304
+ }
10305
+ choices.push({
10306
+ name,
10307
+ value: module.id,
10308
+ description,
10309
+ disabled: isConflicting ? disabled : false,
10310
+ checked: isSelected || isPreselected && !isConflicting
10311
+ });
10312
+ }
10313
+ return choices;
10314
+ }
10315
+ function validateNoConflicts(selectedIds, allModules) {
10316
+ const conflicts = [];
10317
+ for (const id of selectedIds) {
10318
+ const module = allModules.find((m) => m.id === id);
10319
+ if (!module?.alternativeTo) continue;
10320
+ for (const altId of module.alternativeTo) {
10321
+ if (selectedIds.includes(altId)) {
10322
+ const existingConflict = conflicts.find(
10323
+ (c) => c.selected === id && c.conflictsWith === altId || c.selected === altId && c.conflictsWith === id
10324
+ );
10325
+ if (!existingConflict) {
10326
+ conflicts.push({ selected: id, conflictsWith: altId });
10327
+ }
10328
+ }
10329
+ }
10330
+ }
10331
+ return conflicts;
10332
+ }
10333
+ function groupByExclusivity(modules) {
10334
+ const groups = /* @__PURE__ */ new Map();
10335
+ const processedIds = /* @__PURE__ */ new Set();
10336
+ for (const module of modules) {
10337
+ if (processedIds.has(module.id)) continue;
10338
+ const groupMembers = /* @__PURE__ */ new Set([module.id]);
10339
+ const toProcess = [...module.alternativeTo || []];
10340
+ while (toProcess.length > 0) {
10341
+ const altId = toProcess.pop();
10342
+ if (!altId || groupMembers.has(altId)) continue;
10343
+ const altModule = modules.find((m) => m.id === altId);
10344
+ if (altModule) {
10345
+ groupMembers.add(altId);
10346
+ for (const transitiveAlt of altModule.alternativeTo || []) {
10347
+ if (!groupMembers.has(transitiveAlt)) {
10348
+ toProcess.push(transitiveAlt);
10349
+ }
10350
+ }
10351
+ }
10352
+ }
10353
+ if (groupMembers.size > 1) {
10354
+ const sortedMembers = Array.from(groupMembers).sort();
10355
+ const groupId = `exclusivity-group-${sortedMembers[0]}`;
10356
+ groups.set(groupId, sortedMembers);
10357
+ for (const memberId of groupMembers) {
10358
+ processedIds.add(memberId);
10359
+ }
10360
+ } else {
10361
+ processedIds.add(module.id);
10362
+ }
10363
+ }
10364
+ return groups;
10365
+ }
10366
+
10367
+ // src/cli/prompts/item-select.ts
10368
+ async function promptBatchAction(category, totalItems, options) {
9841
10369
  const choices = [
9842
10370
  {
9843
10371
  name: "Install all (recommended)",
@@ -9855,7 +10383,14 @@ async function promptBatchAction(category, totalItems, hasPreset) {
9855
10383
  description: `Skip all ${category}`
9856
10384
  }
9857
10385
  ];
9858
- if (hasPreset) {
10386
+ if (options?.hasExclusivityGroups && options.exclusivityGroupCount) {
10387
+ choices.splice(1, 0, {
10388
+ name: colors.primary("Smart selection (handles conflicts)"),
10389
+ value: "smart",
10390
+ description: `Intelligent selection with ${options.exclusivityGroupCount} mutually exclusive group(s)`
10391
+ });
10392
+ }
10393
+ if (options?.hasPreset) {
9859
10394
  choices.splice(1, 0, {
9860
10395
  name: "Use preset for this category",
9861
10396
  value: "preset",
@@ -9865,27 +10400,43 @@ async function promptBatchAction(category, totalItems, hasPreset) {
9865
10400
  return select({
9866
10401
  message: `${capitalize(category)} selection (${totalItems} available):`,
9867
10402
  choices,
9868
- default: "all"
10403
+ default: options?.hasExclusivityGroups ? "smart" : "all"
9869
10404
  });
9870
10405
  }
9871
10406
  async function selectItemsFromCategory(category, items, options) {
9872
- const selectedItems = [];
9873
- const skippedItems = [];
9874
10407
  logger.newline();
9875
10408
  logger.subtitle(`${capitalize(category)} Selection`);
9876
10409
  logger.info(`${items.length} ${category} available`);
10410
+ const exclusivityGroups = groupByExclusivity(items);
10411
+ const hasExclusivityGroups = exclusivityGroups.size > 0;
10412
+ if (hasExclusivityGroups) {
10413
+ logger.info(
10414
+ colors.warning(
10415
+ `${exclusivityGroups.size} group(s) of mutually exclusive ${category} detected`
10416
+ )
10417
+ );
10418
+ }
9877
10419
  logger.newline();
9878
- const batchAction = await promptBatchAction(
9879
- category,
9880
- items.length,
9881
- options?.preselected && options.preselected.length > 0
9882
- );
10420
+ const batchAction = await promptBatchAction(category, items.length, {
10421
+ hasPreset: options?.preselected && options.preselected.length > 0,
10422
+ hasExclusivityGroups,
10423
+ exclusivityGroupCount: exclusivityGroups.size
10424
+ });
9883
10425
  if (batchAction === "all") {
9884
- return {
9885
- category,
9886
- selectedItems: items.map((i) => i.id),
9887
- skippedItems: []
9888
- };
10426
+ const conflicts = validateNoConflicts(
10427
+ items.map((i) => i.id),
10428
+ items
10429
+ );
10430
+ if (conflicts.length > 0) {
10431
+ logger.warn("Cannot install all: some modules are mutually exclusive.");
10432
+ logger.info("Switching to smart selection mode...");
10433
+ } else {
10434
+ return {
10435
+ category,
10436
+ selectedItems: items.map((i) => i.id),
10437
+ skippedItems: []
10438
+ };
10439
+ }
9889
10440
  }
9890
10441
  if (batchAction === "none") {
9891
10442
  return {
@@ -9896,24 +10447,106 @@ async function selectItemsFromCategory(category, items, options) {
9896
10447
  }
9897
10448
  if (batchAction === "preset" && options?.preselected) {
9898
10449
  const preselectedSet = new Set(options.preselected);
9899
- return {
10450
+ const preselectedIds = items.filter((i) => preselectedSet.has(i.id)).map((i) => i.id);
10451
+ const conflicts = validateNoConflicts(preselectedIds, items);
10452
+ if (conflicts.length > 0) {
10453
+ logger.warn("Preset contains conflicting modules. Switching to smart selection...");
10454
+ } else {
10455
+ return {
10456
+ category,
10457
+ selectedItems: preselectedIds,
10458
+ skippedItems: items.filter((i) => !preselectedSet.has(i.id)).map((i) => i.id)
10459
+ };
10460
+ }
10461
+ }
10462
+ if (batchAction === "smart" || batchAction === "all" || batchAction === "preset") {
10463
+ return selectItemsWithExclusivity(category, items, options);
10464
+ }
10465
+ return selectItemsOneByOne(category, items, options);
10466
+ }
10467
+ async function selectItemsWithExclusivity(category, items, options) {
10468
+ let selectedIds = options?.preselected?.filter((id) => items.some((i) => i.id === id)) || [];
10469
+ let confirmed = false;
10470
+ while (!confirmed) {
10471
+ const choices = createChoicesWithExclusivity(items, selectedIds, {
10472
+ preselected: options?.preselected,
10473
+ showConflictReason: true
10474
+ });
10475
+ logger.newline();
10476
+ logger.info(
10477
+ colors.muted("Items marked as incompatible are disabled based on your selections.")
10478
+ );
10479
+ logger.info(colors.muted("Uncheck items to enable their alternatives."));
10480
+ logger.newline();
10481
+ const newSelection = await checkbox({
10482
+ message: `Select ${category} (Space to toggle, Enter to confirm):`,
10483
+ choices: choices.map((c) => ({
10484
+ name: c.name,
10485
+ value: c.value,
10486
+ description: c.description,
10487
+ disabled: c.disabled,
10488
+ checked: c.checked
10489
+ })),
10490
+ required: false
10491
+ });
10492
+ const conflicts = validateNoConflicts(newSelection, items);
10493
+ if (conflicts.length > 0) {
10494
+ logger.warn("Your selection contains conflicts:");
10495
+ for (const conflict of conflicts) {
10496
+ const module1 = items.find((i) => i.id === conflict.selected)?.name || conflict.selected;
10497
+ const module2 = items.find((i) => i.id === conflict.conflictsWith)?.name || conflict.conflictsWith;
10498
+ logger.info(` ${colors.error("\u2022")} ${module1} and ${module2} are mutually exclusive`);
10499
+ }
10500
+ logger.info("Please adjust your selection.");
10501
+ selectedIds = newSelection;
10502
+ continue;
10503
+ }
10504
+ selectedIds = newSelection;
10505
+ showCategorySelectionSummary({
9900
10506
  category,
9901
- selectedItems: items.filter((i) => preselectedSet.has(i.id)).map((i) => i.id),
9902
- skippedItems: items.filter((i) => !preselectedSet.has(i.id)).map((i) => i.id)
9903
- };
10507
+ selectedItems: selectedIds,
10508
+ skippedItems: items.filter((i) => !selectedIds.includes(i.id)).map((i) => i.id)
10509
+ });
10510
+ confirmed = await confirm({
10511
+ message: "Is this selection correct?",
10512
+ default: true
10513
+ });
9904
10514
  }
10515
+ return {
10516
+ category,
10517
+ selectedItems: selectedIds,
10518
+ skippedItems: items.filter((i) => !selectedIds.includes(i.id)).map((i) => i.id)
10519
+ };
10520
+ }
10521
+ async function selectItemsOneByOne(category, items, options) {
10522
+ const selectedItems = [];
10523
+ const skippedItems = [];
9905
10524
  const remainingItems = [...items];
9906
10525
  let currentIndex = 0;
9907
10526
  while (currentIndex < remainingItems.length) {
9908
10527
  const item = remainingItems[currentIndex];
9909
10528
  const remaining = remainingItems.length - currentIndex - 1;
9910
10529
  const isPreselected = options?.preselected?.includes(item.id);
10530
+ const conflicts = item.alternativeTo?.filter((altId) => selectedItems.includes(altId)) || [];
10531
+ if (conflicts.length > 0) {
10532
+ const conflictNames = conflicts.map((id) => items.find((i) => i.id === id)?.name || id).join(", ");
10533
+ logger.info(
10534
+ colors.muted(`Skipping ${item.name} (conflicts with selected: ${conflictNames})`)
10535
+ );
10536
+ skippedItems.push(item.id);
10537
+ currentIndex++;
10538
+ continue;
10539
+ }
9911
10540
  const progress = colors.muted(`[${currentIndex + 1}/${remainingItems.length}]`);
9912
10541
  console.log(`
9913
10542
  ${progress} ${colors.bold(item.name)}`);
9914
10543
  if (options?.showDescriptions && item.description) {
9915
10544
  logger.note(item.description);
9916
10545
  }
10546
+ if (item.alternativeTo && item.alternativeTo.length > 0) {
10547
+ const altNames = item.alternativeTo.map((id) => items.find((i) => i.id === id)?.name || id).join(", ");
10548
+ logger.info(colors.warning(`\u26A0 Selecting this will disable: ${altNames}`));
10549
+ }
9917
10550
  const action = await promptItemWithShortcuts(item, {
9918
10551
  defaultInstall: isPreselected,
9919
10552
  remainingCount: remaining
@@ -9930,7 +10563,13 @@ ${progress} ${colors.bold(item.name)}`);
9930
10563
  case "install-rest":
9931
10564
  selectedItems.push(item.id);
9932
10565
  for (let i = currentIndex + 1; i < remainingItems.length; i++) {
9933
- selectedItems.push(remainingItems[i].id);
10566
+ const nextItem = remainingItems[i];
10567
+ const nextConflicts = nextItem.alternativeTo?.filter((altId) => selectedItems.includes(altId)) || [];
10568
+ if (nextConflicts.length === 0) {
10569
+ selectedItems.push(nextItem.id);
10570
+ } else {
10571
+ skippedItems.push(nextItem.id);
10572
+ }
9934
10573
  }
9935
10574
  currentIndex = remainingItems.length;
9936
10575
  break;
@@ -9972,6 +10611,17 @@ async function promptItemWithShortcuts(item, options) {
9972
10611
  default: options.defaultInstall !== false ? "install" : "skip"
9973
10612
  });
9974
10613
  }
10614
+ function showCategorySelectionSummary(result) {
10615
+ const { category, selectedItems, skippedItems } = result;
10616
+ logger.newline();
10617
+ logger.subtitle(`${capitalize(category)} Summary`);
10618
+ if (selectedItems.length > 0) {
10619
+ logger.success(`Selected (${selectedItems.length}): ${selectedItems.join(", ")}`);
10620
+ }
10621
+ if (skippedItems.length > 0) {
10622
+ logger.info(`Skipped (${skippedItems.length}): ${colors.muted(skippedItems.join(", "))}`);
10623
+ }
10624
+ }
9975
10625
  function capitalize(str) {
9976
10626
  return str.charAt(0).toUpperCase() + str.slice(1);
9977
10627
  }
@@ -10735,6 +11385,122 @@ async function confirmProjectInfo(info) {
10735
11385
  });
10736
11386
  }
10737
11387
 
11388
+ // src/cli/prompts/related-skills.ts
11389
+ init_esm_shims();
11390
+ function getAllRelatedSkillIds(agentModules) {
11391
+ const relatedIds = /* @__PURE__ */ new Set();
11392
+ for (const agent of agentModules) {
11393
+ if (agent.relatedSkills) {
11394
+ for (const skillId of agent.relatedSkills) {
11395
+ relatedIds.add(skillId);
11396
+ }
11397
+ }
11398
+ }
11399
+ return Array.from(relatedIds);
11400
+ }
11401
+ function getAgentsWithRelatedSkills(selectedAgentIds, agentModules) {
11402
+ return agentModules.filter(
11403
+ (agent) => selectedAgentIds.includes(agent.id) && agent.relatedSkills && agent.relatedSkills.length > 0
11404
+ );
11405
+ }
11406
+ async function promptRelatedSkillsForAgent(agent, skillModules, options) {
11407
+ if (!agent.relatedSkills || agent.relatedSkills.length === 0) {
11408
+ return [];
11409
+ }
11410
+ const relatedSkillDefs = agent.relatedSkills.map((skillId) => skillModules.find((s) => s.id === skillId)).filter((s) => s !== void 0);
11411
+ if (relatedSkillDefs.length === 0) {
11412
+ return [];
11413
+ }
11414
+ if (relatedSkillDefs.length === 1) {
11415
+ const skill = relatedSkillDefs[0];
11416
+ const action = await select({
11417
+ message: `${colors.primary(agent.name)} has a related skill. Install it?`,
11418
+ choices: [
11419
+ {
11420
+ name: `Install ${skill.name}`,
11421
+ value: "install",
11422
+ description: skill.description
11423
+ },
11424
+ {
11425
+ name: "Skip",
11426
+ value: "skip",
11427
+ description: "Do not install this skill"
11428
+ }
11429
+ ],
11430
+ default: "install"
11431
+ });
11432
+ return action === "install" ? [skill.id] : [];
11433
+ }
11434
+ const choices = relatedSkillDefs.map((skill) => ({
11435
+ name: skill.name,
11436
+ value: skill.id,
11437
+ description: skill.description,
11438
+ checked: options?.preselected?.includes(skill.id) ?? false
11439
+ }));
11440
+ if (options?.allowMultiple !== false) {
11441
+ const selected2 = await checkbox({
11442
+ message: `${colors.primary(agent.name)} supports multiple frameworks. Select which patterns to install:`,
11443
+ choices,
11444
+ required: false
11445
+ });
11446
+ return selected2;
11447
+ }
11448
+ const choicesWithSkip = [
11449
+ ...choices.map((c) => ({ name: c.name, value: c.value, description: c.description })),
11450
+ {
11451
+ name: colors.muted("Skip (none)"),
11452
+ value: "__skip__",
11453
+ description: "Do not install any related skill for this agent"
11454
+ }
11455
+ ];
11456
+ const selected = await select({
11457
+ message: `${colors.primary(agent.name)} supports multiple frameworks. Select which patterns to use:`,
11458
+ choices: choicesWithSkip,
11459
+ default: choicesWithSkip[0]?.value
11460
+ });
11461
+ return selected === "__skip__" ? [] : [selected];
11462
+ }
11463
+ async function promptAllRelatedSkills(selectedAgentIds, registry, options) {
11464
+ const agentsWithRelated = getAgentsWithRelatedSkills(selectedAgentIds, registry.agents);
11465
+ const allRelatedSkillIds = getAllRelatedSkillIds(registry.agents);
11466
+ if (agentsWithRelated.length === 0) {
11467
+ return {
11468
+ relatedSkillsSelected: [],
11469
+ relatedSkillsSkipped: [],
11470
+ allRelatedSkillIds
11471
+ };
11472
+ }
11473
+ logger.newline();
11474
+ logger.subtitle("Framework-Specific Skills");
11475
+ logger.info(
11476
+ "The selected agents support framework-specific patterns. Select which ones to install."
11477
+ );
11478
+ logger.newline();
11479
+ const selectedSkills = [];
11480
+ const skippedSkills = [];
11481
+ for (const agent of agentsWithRelated) {
11482
+ const selected = await promptRelatedSkillsForAgent(agent, registry.skills, {
11483
+ preselected: options?.preselectedSkills,
11484
+ allowMultiple: options?.allowMultiplePerAgent
11485
+ });
11486
+ selectedSkills.push(...selected);
11487
+ const agentRelatedIds = agent.relatedSkills || [];
11488
+ const skipped = agentRelatedIds.filter((id) => !selected.includes(id));
11489
+ skippedSkills.push(...skipped);
11490
+ }
11491
+ const uniqueSelected = [...new Set(selectedSkills)];
11492
+ const uniqueSkipped = [...new Set(skippedSkills)].filter((id) => !uniqueSelected.includes(id));
11493
+ return {
11494
+ relatedSkillsSelected: uniqueSelected,
11495
+ relatedSkillsSkipped: uniqueSkipped,
11496
+ allRelatedSkillIds
11497
+ };
11498
+ }
11499
+ function filterOutRelatedSkills(skillModules, relatedSkillIds) {
11500
+ const relatedSet = new Set(relatedSkillIds);
11501
+ return skillModules.filter((skill) => !relatedSet.has(skill.id));
11502
+ }
11503
+
10738
11504
  // src/cli/prompts/scaffold.ts
10739
11505
  init_esm_shims();
10740
11506
  async function promptScaffoldType(options) {
@@ -11496,11 +12262,66 @@ function createBundleSelectionStep() {
11496
12262
  }
11497
12263
  if (mode === "individual" || mode === "both") {
11498
12264
  const preselectedFromBundles = resolveBundles(result.selectedBundles);
11499
- const categories = ["agents", "skills", "commands", "docs"];
11500
12265
  if (ctx.registry) {
11501
12266
  logger.newline();
11502
12267
  logger.subtitle("Individual Module Selection");
11503
- for (const category of categories) {
12268
+ const agentPreselected = mode === "both" ? preselectedFromBundles.agents : [];
12269
+ const agentResult = await selectItemsFromCategory("agents", ctx.registry.agents, {
12270
+ preselected: agentPreselected,
12271
+ showDescriptions: true
12272
+ });
12273
+ if (mode === "both") {
12274
+ result.additionalModules.agents = agentResult.selectedItems.filter(
12275
+ (id) => !agentPreselected.includes(id)
12276
+ );
12277
+ } else {
12278
+ result.additionalModules.agents = agentResult.selectedItems;
12279
+ }
12280
+ const allSelectedAgents = [
12281
+ ...preselectedFromBundles.agents,
12282
+ ...result.additionalModules.agents
12283
+ ];
12284
+ const relatedSkillsResult = await promptAllRelatedSkills(
12285
+ allSelectedAgents,
12286
+ ctx.registry,
12287
+ {
12288
+ preselectedSkills: mode === "both" ? preselectedFromBundles.skills : [],
12289
+ allowMultiplePerAgent: true
12290
+ }
12291
+ );
12292
+ const skillPreselected = mode === "both" ? preselectedFromBundles.skills : [];
12293
+ const independentSkills = filterOutRelatedSkills(
12294
+ ctx.registry.skills,
12295
+ relatedSkillsResult.allRelatedSkillIds
12296
+ );
12297
+ const independentPreselected = skillPreselected.filter(
12298
+ (id) => !relatedSkillsResult.allRelatedSkillIds.includes(id)
12299
+ );
12300
+ let independentSkillsSelected = [];
12301
+ if (independentSkills.length > 0) {
12302
+ logger.newline();
12303
+ logger.info(
12304
+ colors.muted("Now selecting general-purpose skills (not framework-specific):")
12305
+ );
12306
+ const skillResult = await selectItemsFromCategory("skills", independentSkills, {
12307
+ preselected: independentPreselected,
12308
+ showDescriptions: true
12309
+ });
12310
+ independentSkillsSelected = skillResult.selectedItems;
12311
+ }
12312
+ const allSelectedSkills = [
12313
+ ...relatedSkillsResult.relatedSkillsSelected,
12314
+ ...independentSkillsSelected
12315
+ ];
12316
+ if (mode === "both") {
12317
+ result.additionalModules.skills = allSelectedSkills.filter(
12318
+ (id) => !skillPreselected.includes(id)
12319
+ );
12320
+ } else {
12321
+ result.additionalModules.skills = allSelectedSkills;
12322
+ }
12323
+ const otherCategories = ["commands", "docs"];
12324
+ for (const category of otherCategories) {
11504
12325
  const preselected = mode === "both" ? preselectedFromBundles[category] : [];
11505
12326
  const categoryResult = await selectItemsFromCategory(category, ctx.registry[category], {
11506
12327
  preselected,
@@ -11672,6 +12493,28 @@ function createTemplateConfigStep() {
11672
12493
  }
11673
12494
  };
11674
12495
  }
12496
+ function createClaudeSettingsStep() {
12497
+ return {
12498
+ metadata: {
12499
+ id: "claudeSettings",
12500
+ name: "Claude Code Settings",
12501
+ description: "Configure Claude Code model, permissions, and behavior",
12502
+ required: false
12503
+ },
12504
+ computeDefaults: (ctx) => ctx.claudeSettings,
12505
+ execute: async (ctx, defaults) => {
12506
+ const goBack = await promptBackOption(11, "Configure Claude Code settings or go back?");
12507
+ if (goBack) {
12508
+ return createResult(defaults, "back");
12509
+ }
12510
+ const value = await promptClaudeSettings({
12511
+ defaults,
12512
+ includeCoAuthor: ctx.preferences?.includeCoAuthor
12513
+ });
12514
+ return createResult(value, "next");
12515
+ }
12516
+ };
12517
+ }
11675
12518
  function createInitWizardConfig(projectPath, detection, registry) {
11676
12519
  void projectPath;
11677
12520
  void detection;
@@ -11720,6 +12563,10 @@ function createInitWizardConfig(projectPath, detection, registry) {
11720
12563
  {
11721
12564
  id: "templateConfig",
11722
12565
  definition: createTemplateConfigStep()
12566
+ },
12567
+ {
12568
+ id: "claudeSettings",
12569
+ definition: createClaudeSettingsStep()
11723
12570
  }
11724
12571
  ];
11725
12572
  return {
@@ -12044,7 +12891,7 @@ async function runInit(path11, options) {
12044
12891
  logger.warn("Configuration cancelled");
12045
12892
  return;
12046
12893
  }
12047
- const { config, skippedMcpConfigs, templateConfig, cicdConfig } = buildResult;
12894
+ const { config, skippedMcpConfigs, templateConfig, cicdConfig, claudeSettings } = buildResult;
12048
12895
  if (templateConfig) {
12049
12896
  config.templateConfig = templateConfig;
12050
12897
  }
@@ -12061,7 +12908,15 @@ async function runInit(path11, options) {
12061
12908
  showConfigSummary(config);
12062
12909
  return;
12063
12910
  }
12064
- await executeInstallation(projectPath, config, registry, templatesPath, options, cicdConfig);
12911
+ await executeInstallation(
12912
+ projectPath,
12913
+ config,
12914
+ registry,
12915
+ templatesPath,
12916
+ options,
12917
+ cicdConfig,
12918
+ claudeSettings
12919
+ );
12065
12920
  if (templateConfig && !options.noPlaceholders) {
12066
12921
  const claudePath = joinPath(projectPath, ".claude");
12067
12922
  await replaceTemplateConfigWithSpinner(claudePath, templateConfig);
@@ -12278,7 +13133,8 @@ async function buildInteractiveConfig(projectPath, detection, registry, options,
12278
13133
  codeStyleConfig,
12279
13134
  cicdConfig,
12280
13135
  folderPreferences,
12281
- templateConfig: templateConfigResult
13136
+ templateConfig: templateConfigResult,
13137
+ claudeSettings
12282
13138
  } = wizardResult.values;
12283
13139
  let mcpConfig = { level: "project", servers: [] };
12284
13140
  let skippedMcpConfigs = [];
@@ -12336,10 +13192,11 @@ async function buildInteractiveConfig(projectPath, detection, registry, options,
12336
13192
  config,
12337
13193
  skippedMcpConfigs,
12338
13194
  templateConfig: templateConfigResult,
12339
- cicdConfig
13195
+ cicdConfig,
13196
+ claudeSettings
12340
13197
  };
12341
13198
  }
12342
- async function executeInstallation(projectPath, config, registry, templatesPath, options, cicdConfig) {
13199
+ async function executeInstallation(projectPath, config, registry, templatesPath, options, cicdConfig, claudeSettings) {
12343
13200
  logger.newline();
12344
13201
  logger.title("Installing Configuration");
12345
13202
  if (config.scaffold.type === "full-project") {
@@ -12361,6 +13218,28 @@ async function executeInstallation(projectPath, config, registry, templatesPath,
12361
13218
  } else if (claudeMdResult.skipped) {
12362
13219
  logger.info("CLAUDE.md already exists, skipped");
12363
13220
  }
13221
+ if (claudeSettings) {
13222
+ const settingsResult = await generateSettingsWithSpinner(projectPath, {
13223
+ claudeSettings,
13224
+ includeCoAuthor: config.preferences.includeCoAuthor,
13225
+ overwrite: options.force
13226
+ });
13227
+ if (settingsResult.error) {
13228
+ logger.warn(`settings.json generation warning: ${settingsResult.error}`);
13229
+ } else if (settingsResult.skipped) {
13230
+ logger.info("settings.json already exists, skipped");
13231
+ }
13232
+ const settingsLocalResult = await generateSettingsLocalWithSpinner(projectPath, {
13233
+ claudeSettings,
13234
+ includeCoAuthor: config.preferences.includeCoAuthor,
13235
+ overwrite: options.force
13236
+ });
13237
+ if (settingsLocalResult.error) {
13238
+ logger.warn(`settings.local.json generation warning: ${settingsLocalResult.error}`);
13239
+ } else if (settingsLocalResult.skipped) {
13240
+ logger.info("settings.local.json already exists, skipped");
13241
+ }
13242
+ }
12364
13243
  const modulesByCategory = {
12365
13244
  agents: filterModules(registry, "agents", config.modules.agents.selected),
12366
13245
  skills: filterModules(registry, "skills", config.modules.skills.selected),