@qazuor/claude-code-config 0.4.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 (64) hide show
  1. package/README.md +395 -50
  2. package/dist/bin.cjs +3207 -165
  3. package/dist/bin.cjs.map +1 -1
  4. package/dist/bin.js +3207 -165
  5. package/dist/bin.js.map +1 -1
  6. package/dist/index.cjs +75 -58
  7. package/dist/index.cjs.map +1 -1
  8. package/dist/index.d.cts +284 -1
  9. package/dist/index.d.ts +284 -1
  10. package/dist/index.js +75 -58
  11. package/dist/index.js.map +1 -1
  12. package/package.json +24 -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/docs/_registry.json +54 -0
  20. package/templates/docs/standards/code-standards.md +20 -0
  21. package/templates/docs/standards/design-standards.md +13 -0
  22. package/templates/docs/standards/documentation-standards.md +13 -0
  23. package/templates/docs/standards/performance-standards.md +524 -0
  24. package/templates/docs/standards/security-standards.md +496 -0
  25. package/templates/docs/standards/testing-standards.md +15 -0
  26. package/templates/hooks/on-notification.sh +0 -0
  27. package/templates/scripts/add-changelogs.sh +0 -0
  28. package/templates/scripts/generate-code-registry.ts +0 -0
  29. package/templates/scripts/health-check.sh +0 -0
  30. package/templates/scripts/sync-registry.sh +0 -0
  31. package/templates/scripts/telemetry-report.ts +0 -0
  32. package/templates/scripts/validate-docs.sh +0 -0
  33. package/templates/scripts/validate-registry.sh +0 -0
  34. package/templates/scripts/validate-structure.sh +0 -0
  35. package/templates/scripts/worktree-cleanup.sh +0 -0
  36. package/templates/scripts/worktree-create.sh +0 -0
  37. package/templates/skills/README.md +99 -90
  38. package/templates/skills/_registry.json +323 -16
  39. package/templates/skills/api-frameworks/express-patterns.md +411 -0
  40. package/templates/skills/api-frameworks/fastify-patterns.md +419 -0
  41. package/templates/skills/api-frameworks/hono-patterns.md +388 -0
  42. package/templates/skills/api-frameworks/nestjs-patterns.md +497 -0
  43. package/templates/skills/database/drizzle-patterns.md +449 -0
  44. package/templates/skills/database/mongoose-patterns.md +503 -0
  45. package/templates/skills/database/prisma-patterns.md +487 -0
  46. package/templates/skills/frontend-frameworks/astro-patterns.md +415 -0
  47. package/templates/skills/frontend-frameworks/nextjs-patterns.md +470 -0
  48. package/templates/skills/frontend-frameworks/react-patterns.md +516 -0
  49. package/templates/skills/frontend-frameworks/tanstack-start-patterns.md +469 -0
  50. package/templates/skills/patterns/atdd-methodology.md +364 -0
  51. package/templates/skills/patterns/bdd-methodology.md +281 -0
  52. package/templates/skills/patterns/clean-architecture.md +444 -0
  53. package/templates/skills/patterns/hexagonal-architecture.md +567 -0
  54. package/templates/skills/patterns/vertical-slice-architecture.md +502 -0
  55. package/templates/agents/engineering/astro-engineer.md +0 -293
  56. package/templates/agents/engineering/db-drizzle-engineer.md +0 -360
  57. package/templates/agents/engineering/express-engineer.md +0 -316
  58. package/templates/agents/engineering/fastify-engineer.md +0 -399
  59. package/templates/agents/engineering/mongoose-engineer.md +0 -473
  60. package/templates/agents/engineering/nestjs-engineer.md +0 -429
  61. package/templates/agents/engineering/nextjs-engineer.md +0 -451
  62. package/templates/agents/engineering/prisma-engineer.md +0 -432
  63. package/templates/agents/engineering/react-senior-dev.md +0 -394
  64. package/templates/agents/engineering/tanstack-start-engineer.md +0 -447
package/dist/bin.cjs CHANGED
@@ -230,7 +230,7 @@ var import_node_module = require("module");
230
230
 
231
231
  // src/cli/index.ts
232
232
  init_cjs_shims();
233
- var import_commander8 = require("commander");
233
+ var import_commander9 = require("commander");
234
234
 
235
235
  // src/cli/commands/index.ts
236
236
  init_cjs_shims();
@@ -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" },
@@ -2594,7 +2611,7 @@ init_cjs_shims();
2594
2611
  // src/lib/config/reader.ts
2595
2612
  init_cjs_shims();
2596
2613
  init_fs();
2597
- var CONFIG_FILE = "config.json";
2614
+ var CONFIG_FILE = "qazuor-claude-config.json";
2598
2615
  var CLAUDE_DIR = ".claude";
2599
2616
  async function readConfig(projectPath) {
2600
2617
  const configPath = joinPath(projectPath, CLAUDE_DIR, CONFIG_FILE);
@@ -2616,7 +2633,7 @@ async function hasClaudeDir(projectPath) {
2616
2633
  // src/lib/config/writer.ts
2617
2634
  init_cjs_shims();
2618
2635
  init_fs();
2619
- var CONFIG_FILE2 = "config.json";
2636
+ var CONFIG_FILE2 = "qazuor-claude-config.json";
2620
2637
  var CLAUDE_DIR2 = ".claude";
2621
2638
  async function writeConfig(projectPath, config, options) {
2622
2639
  const claudePath = joinPath(projectPath, CLAUDE_DIR2);
@@ -3258,21 +3275,333 @@ init_cjs_shims();
3258
3275
  // src/lib/git-hooks/husky-installer.ts
3259
3276
  init_cjs_shims();
3260
3277
  init_fs();
3261
- function generateCommitMsgHook() {
3278
+
3279
+ // src/lib/git-hooks/precommit-generator.ts
3280
+ init_cjs_shims();
3281
+ function generatePreCommitScript(config) {
3282
+ if (!config.enabled) {
3283
+ return generateDisabledScript();
3284
+ }
3285
+ const sections = [];
3286
+ sections.push(generateHeader());
3287
+ if (config.showTiming) {
3288
+ sections.push(generateTimingSetup());
3289
+ }
3290
+ sections.push(generateErrorHandling(config.continueOnFailure));
3291
+ if (config.lint.enabled) {
3292
+ sections.push(generateLintSection(config));
3293
+ }
3294
+ if (config.typecheck.enabled) {
3295
+ sections.push(generateTypecheckSection(config));
3296
+ }
3297
+ if (config.formatCheck.enabled) {
3298
+ sections.push(generateFormatCheckSection(config));
3299
+ }
3300
+ if (config.tests.enabled && config.tests.mode !== "none") {
3301
+ sections.push(generateTestSection(config));
3302
+ }
3303
+ const sortedCustom = [...config.customCommands].sort(
3304
+ (a, b) => (a.order ?? 100) - (b.order ?? 100)
3305
+ );
3306
+ for (const cmd of sortedCustom) {
3307
+ sections.push(generateCustomCommandSection(cmd, config));
3308
+ }
3309
+ sections.push(generateFooter(config));
3310
+ return sections.join("\n\n");
3311
+ }
3312
+ function generateDisabledScript() {
3262
3313
  return `#!/usr/bin/env sh
3263
3314
  . "$(dirname -- "$0")/_/husky.sh"
3264
3315
 
3265
- npx --no -- commitlint --edit "\${1}"
3266
- `;
3316
+ # Pre-commit hook disabled
3317
+ exit 0`;
3267
3318
  }
3268
- function generatePreCommitHook(lintCommand) {
3269
- const command = lintCommand || "pnpm lint-staged";
3319
+ function generateHeader() {
3270
3320
  return `#!/usr/bin/env sh
3271
3321
  . "$(dirname -- "$0")/_/husky.sh"
3272
3322
 
3273
- ${command}
3323
+ # Pre-commit hook - Generated by @qazuor/claude-code-config
3324
+ # Bypass with: git commit --no-verify -m "message"
3325
+
3326
+ echo "\u{1F50D} Running pre-commit checks..."`;
3327
+ }
3328
+ function generateTimingSetup() {
3329
+ return `# Timing setup
3330
+ START_TIME=$(date +%s)
3331
+ step_start() { STEP_START=$(date +%s); }
3332
+ step_end() {
3333
+ STEP_END=$(date +%s)
3334
+ ELAPSED_TIME=$((STEP_END - STEP_START))
3335
+ echo " \u23F1\uFE0F Completed in \${ELAPSED_TIME}s"
3336
+ }`;
3337
+ }
3338
+ function generateErrorHandling(continueOnFailure) {
3339
+ if (continueOnFailure) {
3340
+ return `# Error tracking (continue on failure mode)
3341
+ ERRORS=0
3342
+ track_error() {
3343
+ ERRORS=$((ERRORS + 1))
3344
+ }`;
3345
+ }
3346
+ return `# Fail fast mode - exit on first error
3347
+ set -e`;
3348
+ }
3349
+ function generateLintSection(config) {
3350
+ const lint = config.lint;
3351
+ let command;
3352
+ if (lint.command) {
3353
+ command = lint.command;
3354
+ } else if (lint.stagedOnly) {
3355
+ command = getLintStagedCommand(lint.tool);
3356
+ } else {
3357
+ command = getLintCommand(lint.tool);
3358
+ }
3359
+ const lines = [];
3360
+ lines.push("# Linting");
3361
+ lines.push('echo ""');
3362
+ lines.push('echo "\u{1F4DD} Linting..."');
3363
+ if (config.showTiming) {
3364
+ lines.push("step_start");
3365
+ }
3366
+ if (lint.allowFailure) {
3367
+ lines.push(`if ${command}; then`);
3368
+ lines.push(' echo " \u2705 Lint passed"');
3369
+ lines.push("else");
3370
+ lines.push(' echo " \u26A0\uFE0F Lint warnings (non-blocking)"');
3371
+ lines.push("fi");
3372
+ } else if (config.continueOnFailure) {
3373
+ lines.push(`if ${command}; then`);
3374
+ lines.push(' echo " \u2705 Lint passed"');
3375
+ lines.push("else");
3376
+ lines.push(' echo " \u274C Lint failed"');
3377
+ lines.push(" track_error");
3378
+ lines.push("fi");
3379
+ } else {
3380
+ lines.push(`${command} || { echo " \u274C Lint failed"; exit 1; }`);
3381
+ lines.push('echo " \u2705 Lint passed"');
3382
+ }
3383
+ if (config.showTiming) {
3384
+ lines.push("step_end");
3385
+ }
3386
+ return lines.join("\n");
3387
+ }
3388
+ function getLintStagedCommand(tool) {
3389
+ switch (tool) {
3390
+ case "biome":
3391
+ return "pnpm biome check --staged --no-errors-on-unmatched";
3392
+ case "eslint":
3393
+ return "pnpm lint-staged";
3394
+ default:
3395
+ return "pnpm lint-staged";
3396
+ }
3397
+ }
3398
+ function getLintCommand(tool) {
3399
+ switch (tool) {
3400
+ case "biome":
3401
+ return "pnpm biome check .";
3402
+ case "eslint":
3403
+ return "pnpm eslint .";
3404
+ default:
3405
+ return "pnpm lint";
3406
+ }
3407
+ }
3408
+ function generateTypecheckSection(config) {
3409
+ const typecheck = config.typecheck;
3410
+ const command = typecheck.command ?? "pnpm typecheck";
3411
+ const lines = [];
3412
+ lines.push("# Type checking");
3413
+ lines.push('echo ""');
3414
+ lines.push('echo "\u{1F537} Type checking..."');
3415
+ if (config.showTiming) {
3416
+ lines.push("step_start");
3417
+ }
3418
+ if (typecheck.allowFailure) {
3419
+ lines.push(`if ${command}; then`);
3420
+ lines.push(' echo " \u2705 Types OK"');
3421
+ lines.push("else");
3422
+ lines.push(' echo " \u26A0\uFE0F Type warnings (non-blocking)"');
3423
+ lines.push("fi");
3424
+ } else if (config.continueOnFailure) {
3425
+ lines.push(`if ${command}; then`);
3426
+ lines.push(' echo " \u2705 Types OK"');
3427
+ lines.push("else");
3428
+ lines.push(' echo " \u274C Type check failed"');
3429
+ lines.push(" track_error");
3430
+ lines.push("fi");
3431
+ } else {
3432
+ lines.push(`${command} || { echo " \u274C Type check failed"; exit 1; }`);
3433
+ lines.push('echo " \u2705 Types OK"');
3434
+ }
3435
+ if (config.showTiming) {
3436
+ lines.push("step_end");
3437
+ }
3438
+ return lines.join("\n");
3439
+ }
3440
+ function generateFormatCheckSection(config) {
3441
+ const format = config.formatCheck;
3442
+ let command;
3443
+ if (format.command) {
3444
+ command = format.command;
3445
+ } else {
3446
+ command = getFormatCheckCommand(format.tool);
3447
+ }
3448
+ const lines = [];
3449
+ lines.push("# Format check");
3450
+ lines.push('echo ""');
3451
+ lines.push('echo "\u2728 Format check..."');
3452
+ if (config.showTiming) {
3453
+ lines.push("step_start");
3454
+ }
3455
+ if (format.allowFailure) {
3456
+ lines.push(`if ${command}; then`);
3457
+ lines.push(' echo " \u2705 Format OK"');
3458
+ lines.push("else");
3459
+ lines.push(' echo " \u26A0\uFE0F Format warnings (non-blocking)"');
3460
+ lines.push("fi");
3461
+ } else if (config.continueOnFailure) {
3462
+ lines.push(`if ${command}; then`);
3463
+ lines.push(' echo " \u2705 Format OK"');
3464
+ lines.push("else");
3465
+ lines.push(' echo " \u274C Format check failed"');
3466
+ lines.push(" track_error");
3467
+ lines.push("fi");
3468
+ } else {
3469
+ lines.push(`${command} || { echo " \u274C Format check failed"; exit 1; }`);
3470
+ lines.push('echo " \u2705 Format OK"');
3471
+ }
3472
+ if (config.showTiming) {
3473
+ lines.push("step_end");
3474
+ }
3475
+ return lines.join("\n");
3476
+ }
3477
+ function getFormatCheckCommand(tool) {
3478
+ switch (tool) {
3479
+ case "biome":
3480
+ return "pnpm biome format --check .";
3481
+ case "prettier":
3482
+ return "pnpm prettier --check .";
3483
+ default:
3484
+ return "pnpm format:check";
3485
+ }
3486
+ }
3487
+ function generateTestSection(config) {
3488
+ const tests = config.tests;
3489
+ let command;
3490
+ if (tests.command) {
3491
+ command = tests.command;
3492
+ } else if (tests.mode === "affected") {
3493
+ command = "pnpm vitest related --run";
3494
+ } else {
3495
+ command = "pnpm test";
3496
+ }
3497
+ if (tests.coverageThreshold > 0) {
3498
+ command = `${command} --coverage --coverage.thresholds.lines=${tests.coverageThreshold}`;
3499
+ }
3500
+ const lines = [];
3501
+ lines.push("# Tests");
3502
+ lines.push('echo ""');
3503
+ lines.push(`echo "\u{1F9EA} Running ${tests.mode === "affected" ? "affected" : "all"} tests..."`);
3504
+ if (config.showTiming) {
3505
+ lines.push("step_start");
3506
+ }
3507
+ if (tests.allowFailure) {
3508
+ lines.push(`if ${command}; then`);
3509
+ lines.push(' echo " \u2705 Tests passed"');
3510
+ lines.push("else");
3511
+ lines.push(' echo " \u26A0\uFE0F Test warnings (non-blocking)"');
3512
+ lines.push("fi");
3513
+ } else if (config.continueOnFailure) {
3514
+ lines.push(`if ${command}; then`);
3515
+ lines.push(' echo " \u2705 Tests passed"');
3516
+ lines.push("else");
3517
+ lines.push(' echo " \u274C Tests failed"');
3518
+ lines.push(" track_error");
3519
+ lines.push("fi");
3520
+ } else {
3521
+ lines.push(`${command} || { echo " \u274C Tests failed"; exit 1; }`);
3522
+ lines.push('echo " \u2705 Tests passed"');
3523
+ }
3524
+ if (config.showTiming) {
3525
+ lines.push("step_end");
3526
+ }
3527
+ return lines.join("\n");
3528
+ }
3529
+ function generateCustomCommandSection(cmd, config) {
3530
+ const lines = [];
3531
+ lines.push(`# Custom: ${cmd.name}`);
3532
+ lines.push('echo ""');
3533
+ lines.push(`echo "\u{1F527} ${cmd.name}..."`);
3534
+ if (config.showTiming) {
3535
+ lines.push("step_start");
3536
+ }
3537
+ if (cmd.allowFailure) {
3538
+ lines.push(`if ${cmd.command}; then`);
3539
+ lines.push(` echo " \u2705 ${cmd.name} passed"`);
3540
+ lines.push("else");
3541
+ lines.push(` echo " \u26A0\uFE0F ${cmd.name} warnings (non-blocking)"`);
3542
+ lines.push("fi");
3543
+ } else if (config.continueOnFailure) {
3544
+ lines.push(`if ${cmd.command}; then`);
3545
+ lines.push(` echo " \u2705 ${cmd.name} passed"`);
3546
+ lines.push("else");
3547
+ lines.push(` echo " \u274C ${cmd.name} failed"`);
3548
+ lines.push(" track_error");
3549
+ lines.push("fi");
3550
+ } else {
3551
+ lines.push(`${cmd.command} || { echo " \u274C ${cmd.name} failed"; exit 1; }`);
3552
+ lines.push(`echo " \u2705 ${cmd.name} passed"`);
3553
+ }
3554
+ if (config.showTiming) {
3555
+ lines.push("step_end");
3556
+ }
3557
+ return lines.join("\n");
3558
+ }
3559
+ function generateFooter(config) {
3560
+ const lines = [];
3561
+ lines.push("# Final status");
3562
+ lines.push('echo ""');
3563
+ if (config.showTiming) {
3564
+ lines.push("END_TIME=$(date +%s)");
3565
+ lines.push("TOTAL_TIME=$((END_TIME - START_TIME))");
3566
+ }
3567
+ if (config.continueOnFailure) {
3568
+ lines.push("if [ $ERRORS -gt 0 ]; then");
3569
+ lines.push(' echo "\u274C Pre-commit failed with $ERRORS error(s)"');
3570
+ if (config.showTiming) {
3571
+ lines.push(' echo "\u23F1\uFE0F Total time: ${TOTAL_TIME}s"');
3572
+ }
3573
+ lines.push(" exit 1");
3574
+ lines.push("fi");
3575
+ }
3576
+ if (config.showTiming) {
3577
+ lines.push('echo "\u2728 All checks passed! (${TOTAL_TIME}s)"');
3578
+ } else {
3579
+ lines.push('echo "\u2728 All checks passed!"');
3580
+ }
3581
+ return lines.join("\n");
3582
+ }
3583
+ function generateSimplePreCommitHook(command) {
3584
+ return `#!/usr/bin/env sh
3585
+ . "$(dirname -- "$0")/_/husky.sh"
3586
+
3587
+ ${command}`;
3588
+ }
3589
+
3590
+ // src/lib/git-hooks/husky-installer.ts
3591
+ function generateCommitMsgHook() {
3592
+ return `#!/usr/bin/env sh
3593
+ . "$(dirname -- "$0")/_/husky.sh"
3594
+
3595
+ npx --no -- commitlint --edit "\${1}"
3274
3596
  `;
3275
3597
  }
3598
+ function generatePreCommitHook(lintCommand, preCommitConfig) {
3599
+ if (preCommitConfig) {
3600
+ return generatePreCommitScript(preCommitConfig);
3601
+ }
3602
+ const command = lintCommand || "pnpm lint-staged";
3603
+ return generateSimplePreCommitHook(command);
3604
+ }
3276
3605
  function generatePrePushHook(testCommand) {
3277
3606
  const command = testCommand || "pnpm test";
3278
3607
  return `#!/usr/bin/env sh
@@ -3363,7 +3692,10 @@ async function installHusky(projectPath, config, options) {
3363
3692
  if (config.preCommit) {
3364
3693
  const preCommitPath = joinPath(huskyDir, "pre-commit");
3365
3694
  if (!await pathExists(preCommitPath) || options?.overwrite) {
3366
- await writeFile(preCommitPath, generatePreCommitHook(config.lintCommand));
3695
+ await writeFile(
3696
+ preCommitPath,
3697
+ generatePreCommitHook(config.lintCommand, config.preCommitConfig)
3698
+ );
3367
3699
  await makeExecutable(preCommitPath);
3368
3700
  result.created.push("pre-commit");
3369
3701
  } else {
@@ -3595,8 +3927,8 @@ async function getHooksStatus(projectPath) {
3595
3927
  let executable = false;
3596
3928
  if (exists) {
3597
3929
  try {
3598
- const fs6 = await import("fs/promises");
3599
- const stats = await fs6.stat(filePath);
3930
+ const fs9 = await import("fs/promises");
3931
+ const stats = await fs9.stat(filePath);
3600
3932
  executable = (stats.mode & 73) !== 0;
3601
3933
  } catch {
3602
3934
  }
@@ -5809,11 +6141,13 @@ function processTemplate(template, projectInfo, options) {
5809
6141
  const commands = options?.templateConfig?.commands;
5810
6142
  const targets = options?.templateConfig?.targets;
5811
6143
  const preferences = options?.claudeConfig?.preferences;
6144
+ const standards = options?.claudeConfig?.extras?.standards;
5812
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 || "");
5813
6146
  const packageManager = preferences?.packageManager || "pnpm";
5814
6147
  content = content.replace(/\{\{PACKAGE_MANAGER\}\}/g, packageManager);
5815
- 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";
5816
6149
  content = content.replace(/\{\{COVERAGE_TARGET\}\}/g, coverageTarget);
6150
+ content = processStandardsPlaceholders(content, standards, preferences);
5817
6151
  if (projectInfo.domain) {
5818
6152
  content = content.replace(/\{\{#if DOMAIN\}\}/g, "").replace(/\{\{\/if\}\}/g, "").replace(/\{\{DOMAIN\}\}/g, projectInfo.domain);
5819
6153
  } else {
@@ -5910,6 +6244,55 @@ function generateCommandsSection(commands, packageManager) {
5910
6244
  lines.push("```");
5911
6245
  return lines.join("\n");
5912
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
+ }
5913
6296
  function getMinimalTemplate() {
5914
6297
  return `# CLAUDE.md
5915
6298
 
@@ -6455,6 +6838,242 @@ async function generateScaffoldWithProgress(projectPath, options) {
6455
6838
  );
6456
6839
  }
6457
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
+
6458
7077
  // src/lib/templates/config-replacer.ts
6459
7078
  init_cjs_shims();
6460
7079
  var fs4 = __toESM(require("fs/promises"), 1);
@@ -8402,53 +9021,189 @@ async function promptCICDConfig(options) {
8402
9021
  };
8403
9022
  }
8404
9023
 
8405
- // src/cli/prompts/code-style.ts
9024
+ // src/cli/prompts/claude-settings.ts
8406
9025
  init_cjs_shims();
8407
- var CODE_STYLE_TOOLS = [
8408
- {
8409
- name: "EditorConfig",
8410
- value: "editorconfig",
8411
- description: "Consistent coding styles across editors",
8412
- checked: true
8413
- },
8414
- {
8415
- name: "Commitlint",
8416
- value: "commitlint",
8417
- description: "Lint commit messages (conventional commits)",
8418
- checked: true
8419
- },
8420
- {
8421
- name: "Biome",
8422
- value: "biome",
8423
- description: "Fast linter and formatter (ESLint + Prettier alternative)",
8424
- checked: false
8425
- },
8426
- {
8427
- name: "Prettier",
8428
- value: "prettier",
8429
- description: "Code formatter (use if not using Biome)",
8430
- checked: false
8431
- }
8432
- ];
8433
- async function promptCodeStyleConfig(options) {
8434
- logger.section("Code Style", "\u{1F3A8}");
8435
- logger.info("Configure code formatting and linting tools");
8436
- logger.newline();
8437
- const enableCodeStyle = await confirm({
8438
- message: "Would you like to install code style configuration files?",
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?",
8439
9030
  default: true
8440
9031
  });
8441
- if (!enableCodeStyle) {
9032
+ if (!wantToConfigure) {
8442
9033
  return {
8443
- enabled: false,
8444
- editorconfig: false,
8445
- commitlint: false,
8446
- biome: false,
8447
- prettier: false
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
+ }
8448
9039
  };
8449
9040
  }
8450
- const selectedTools = await checkbox({
8451
- message: "Select the tools to configure:",
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
+
9160
+ // src/cli/prompts/code-style.ts
9161
+ init_cjs_shims();
9162
+ var CODE_STYLE_TOOLS = [
9163
+ {
9164
+ name: "EditorConfig",
9165
+ value: "editorconfig",
9166
+ description: "Consistent coding styles across editors",
9167
+ checked: true
9168
+ },
9169
+ {
9170
+ name: "Commitlint",
9171
+ value: "commitlint",
9172
+ description: "Lint commit messages (conventional commits)",
9173
+ checked: true
9174
+ },
9175
+ {
9176
+ name: "Biome",
9177
+ value: "biome",
9178
+ description: "Fast linter and formatter (ESLint + Prettier alternative)",
9179
+ checked: false
9180
+ },
9181
+ {
9182
+ name: "Prettier",
9183
+ value: "prettier",
9184
+ description: "Code formatter (use if not using Biome)",
9185
+ checked: false
9186
+ }
9187
+ ];
9188
+ async function promptCodeStyleConfig(options) {
9189
+ logger.section("Code Style", "\u{1F3A8}");
9190
+ logger.info("Configure code formatting and linting tools");
9191
+ logger.newline();
9192
+ const enableCodeStyle = await confirm({
9193
+ message: "Would you like to install code style configuration files?",
9194
+ default: true
9195
+ });
9196
+ if (!enableCodeStyle) {
9197
+ return {
9198
+ enabled: false,
9199
+ editorconfig: false,
9200
+ commitlint: false,
9201
+ biome: false,
9202
+ prettier: false
9203
+ };
9204
+ }
9205
+ const selectedTools = await checkbox({
9206
+ message: "Select the tools to configure:",
8452
9207
  choices: CODE_STYLE_TOOLS.map((tool) => ({
8453
9208
  name: `${tool.name} - ${tool.description}`,
8454
9209
  value: tool.value,
@@ -9539,7 +10294,95 @@ async function promptSubagentStopHook() {
9539
10294
 
9540
10295
  // src/cli/prompts/item-select.ts
9541
10296
  init_cjs_shims();
9542
- 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) {
9543
10386
  const choices = [
9544
10387
  {
9545
10388
  name: "Install all (recommended)",
@@ -9557,7 +10400,14 @@ async function promptBatchAction(category, totalItems, hasPreset) {
9557
10400
  description: `Skip all ${category}`
9558
10401
  }
9559
10402
  ];
9560
- 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) {
9561
10411
  choices.splice(1, 0, {
9562
10412
  name: "Use preset for this category",
9563
10413
  value: "preset",
@@ -9567,27 +10417,43 @@ async function promptBatchAction(category, totalItems, hasPreset) {
9567
10417
  return select({
9568
10418
  message: `${capitalize(category)} selection (${totalItems} available):`,
9569
10419
  choices,
9570
- default: "all"
10420
+ default: options?.hasExclusivityGroups ? "smart" : "all"
9571
10421
  });
9572
10422
  }
9573
10423
  async function selectItemsFromCategory(category, items, options) {
9574
- const selectedItems = [];
9575
- const skippedItems = [];
9576
10424
  logger.newline();
9577
10425
  logger.subtitle(`${capitalize(category)} Selection`);
9578
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
+ }
9579
10436
  logger.newline();
9580
- const batchAction = await promptBatchAction(
9581
- category,
9582
- items.length,
9583
- options?.preselected && options.preselected.length > 0
9584
- );
10437
+ const batchAction = await promptBatchAction(category, items.length, {
10438
+ hasPreset: options?.preselected && options.preselected.length > 0,
10439
+ hasExclusivityGroups,
10440
+ exclusivityGroupCount: exclusivityGroups.size
10441
+ });
9585
10442
  if (batchAction === "all") {
9586
- return {
9587
- category,
9588
- selectedItems: items.map((i) => i.id),
9589
- skippedItems: []
9590
- };
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
+ }
9591
10457
  }
9592
10458
  if (batchAction === "none") {
9593
10459
  return {
@@ -9598,24 +10464,106 @@ async function selectItemsFromCategory(category, items, options) {
9598
10464
  }
9599
10465
  if (batchAction === "preset" && options?.preselected) {
9600
10466
  const preselectedSet = new Set(options.preselected);
9601
- 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({
9602
10523
  category,
9603
- selectedItems: items.filter((i) => preselectedSet.has(i.id)).map((i) => i.id),
9604
- skippedItems: items.filter((i) => !preselectedSet.has(i.id)).map((i) => i.id)
9605
- };
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
+ });
9606
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 = [];
9607
10541
  const remainingItems = [...items];
9608
10542
  let currentIndex = 0;
9609
10543
  while (currentIndex < remainingItems.length) {
9610
10544
  const item = remainingItems[currentIndex];
9611
10545
  const remaining = remainingItems.length - currentIndex - 1;
9612
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
+ }
9613
10557
  const progress = colors.muted(`[${currentIndex + 1}/${remainingItems.length}]`);
9614
10558
  console.log(`
9615
10559
  ${progress} ${colors.bold(item.name)}`);
9616
10560
  if (options?.showDescriptions && item.description) {
9617
10561
  logger.note(item.description);
9618
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
+ }
9619
10567
  const action = await promptItemWithShortcuts(item, {
9620
10568
  defaultInstall: isPreselected,
9621
10569
  remainingCount: remaining
@@ -9632,7 +10580,13 @@ ${progress} ${colors.bold(item.name)}`);
9632
10580
  case "install-rest":
9633
10581
  selectedItems.push(item.id);
9634
10582
  for (let i = currentIndex + 1; i < remainingItems.length; i++) {
9635
- 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
+ }
9636
10590
  }
9637
10591
  currentIndex = remainingItems.length;
9638
10592
  break;
@@ -9674,6 +10628,17 @@ async function promptItemWithShortcuts(item, options) {
9674
10628
  default: options.defaultInstall !== false ? "install" : "skip"
9675
10629
  });
9676
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
+ }
9677
10642
  function capitalize(str) {
9678
10643
  return str.charAt(0).toUpperCase() + str.slice(1);
9679
10644
  }
@@ -10437,6 +11402,122 @@ async function confirmProjectInfo(info) {
10437
11402
  });
10438
11403
  }
10439
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
+
10440
11521
  // src/cli/prompts/scaffold.ts
10441
11522
  init_cjs_shims();
10442
11523
  async function promptScaffoldType(options) {
@@ -10626,27 +11707,27 @@ async function buildConfigContext(projectPath) {
10626
11707
  values: {}
10627
11708
  };
10628
11709
  try {
10629
- const fs6 = await import("fs/promises");
10630
- const path7 = await import("path");
10631
- const pkgPath = path7.join(projectPath, "package.json");
10632
- const pkgContent = await fs6.readFile(pkgPath, "utf-8");
11710
+ const fs9 = await import("fs/promises");
11711
+ const path10 = await import("path");
11712
+ const pkgPath = path10.join(projectPath, "package.json");
11713
+ const pkgContent = await fs9.readFile(pkgPath, "utf-8");
10633
11714
  const pkg = JSON.parse(pkgContent);
10634
11715
  context.scripts = pkg.scripts || {};
10635
11716
  context.dependencies = {
10636
11717
  ...pkg.dependencies || {},
10637
11718
  ...pkg.devDependencies || {}
10638
11719
  };
10639
- context.hasTypeScript = Boolean(context.dependencies.typescript) || await fileExists(path7.join(projectPath, "tsconfig.json"));
10640
- if (await fileExists(path7.join(projectPath, "pnpm-lock.yaml"))) {
11720
+ context.hasTypeScript = Boolean(context.dependencies.typescript) || await fileExists(path10.join(projectPath, "tsconfig.json"));
11721
+ if (await fileExists(path10.join(projectPath, "pnpm-lock.yaml"))) {
10641
11722
  context.packageManager = "pnpm";
10642
- } else if (await fileExists(path7.join(projectPath, "yarn.lock"))) {
11723
+ } else if (await fileExists(path10.join(projectPath, "yarn.lock"))) {
10643
11724
  context.packageManager = "yarn";
10644
- } else if (await fileExists(path7.join(projectPath, "bun.lockb"))) {
11725
+ } else if (await fileExists(path10.join(projectPath, "bun.lockb"))) {
10645
11726
  context.packageManager = "bun";
10646
11727
  } else {
10647
11728
  context.packageManager = "npm";
10648
11729
  }
10649
- context.isGitRepo = await fileExists(path7.join(projectPath, ".git"));
11730
+ context.isGitRepo = await fileExists(path10.join(projectPath, ".git"));
10650
11731
  if (context.isGitRepo) {
10651
11732
  try {
10652
11733
  const { execSync } = await import("child_process");
@@ -10665,8 +11746,8 @@ async function buildConfigContext(projectPath) {
10665
11746
  }
10666
11747
  async function fileExists(filePath) {
10667
11748
  try {
10668
- const fs6 = await import("fs/promises");
10669
- await fs6.access(filePath);
11749
+ const fs9 = await import("fs/promises");
11750
+ await fs9.access(filePath);
10670
11751
  return true;
10671
11752
  } catch {
10672
11753
  return false;
@@ -11198,11 +12279,66 @@ function createBundleSelectionStep() {
11198
12279
  }
11199
12280
  if (mode === "individual" || mode === "both") {
11200
12281
  const preselectedFromBundles = resolveBundles(result.selectedBundles);
11201
- const categories = ["agents", "skills", "commands", "docs"];
11202
12282
  if (ctx.registry) {
11203
12283
  logger.newline();
11204
12284
  logger.subtitle("Individual Module Selection");
11205
- 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) {
11206
12342
  const preselected = mode === "both" ? preselectedFromBundles[category] : [];
11207
12343
  const categoryResult = await selectItemsFromCategory(category, ctx.registry[category], {
11208
12344
  preselected,
@@ -11374,6 +12510,28 @@ function createTemplateConfigStep() {
11374
12510
  }
11375
12511
  };
11376
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
+ }
11377
12535
  function createInitWizardConfig(projectPath, detection, registry) {
11378
12536
  void projectPath;
11379
12537
  void detection;
@@ -11422,6 +12580,10 @@ function createInitWizardConfig(projectPath, detection, registry) {
11422
12580
  {
11423
12581
  id: "templateConfig",
11424
12582
  definition: createTemplateConfigStep()
12583
+ },
12584
+ {
12585
+ id: "claudeSettings",
12586
+ definition: createClaudeSettingsStep()
11425
12587
  }
11426
12588
  ];
11427
12589
  return {
@@ -11700,8 +12862,8 @@ function createInitCommand() {
11700
12862
  ).option("-t, --template <url>", "Remote git repo for templates").option("--branch <name>", "Branch/tag for remote template").option("-y, --yes", "Accept defaults, skip prompts").option("-f, --force", "Overwrite existing .claude/").option("--dry-run", "Show what would happen without making changes").option("--claude-only", "Only Claude config, no project scaffold").option("--no-placeholders", "Skip placeholder replacement").option("--no-mcp", "Skip MCP configuration").option("-v, --verbose", "Detailed output").action(runInit);
11701
12863
  return cmd;
11702
12864
  }
11703
- async function runInit(path7, options) {
11704
- const projectPath = resolvePath(path7 || ".");
12865
+ async function runInit(path10, options) {
12866
+ const projectPath = resolvePath(path10 || ".");
11705
12867
  logger.configure({ verbose: options.verbose, silent: false });
11706
12868
  logger.title("@qazuor/claude-code-config");
11707
12869
  logger.info(`Initializing Claude configuration in ${colors.primary(projectPath)}`);
@@ -11710,6 +12872,7 @@ async function runInit(path7, options) {
11710
12872
  showCancelHint();
11711
12873
  }
11712
12874
  try {
12875
+ let existingConfig = null;
11713
12876
  if (await hasExistingClaudeConfig(projectPath)) {
11714
12877
  if (!options.force) {
11715
12878
  const action = await promptExistingProjectAction();
@@ -11720,6 +12883,10 @@ async function runInit(path7, options) {
11720
12883
  if (action !== "overwrite" && action !== "merge") {
11721
12884
  return;
11722
12885
  }
12886
+ existingConfig = await readConfig(projectPath);
12887
+ if (existingConfig) {
12888
+ logger.info("Using existing configuration as defaults");
12889
+ }
11723
12890
  }
11724
12891
  }
11725
12892
  const detection = await detectProject(projectPath);
@@ -11736,12 +12903,12 @@ async function runInit(path7, options) {
11736
12903
  () => loadRegistry(templatesPath),
11737
12904
  { silent: options.dryRun }
11738
12905
  );
11739
- const buildResult = options.yes ? await buildDefaultConfig(projectPath, detection, options) : await buildInteractiveConfig(projectPath, detection, registry, options);
12906
+ const buildResult = options.yes ? await buildDefaultConfig(projectPath, detection, options) : await buildInteractiveConfig(projectPath, detection, registry, options, existingConfig);
11740
12907
  if (!buildResult) {
11741
12908
  logger.warn("Configuration cancelled");
11742
12909
  return;
11743
12910
  }
11744
- const { config, skippedMcpConfigs, templateConfig, cicdConfig } = buildResult;
12911
+ const { config, skippedMcpConfigs, templateConfig, cicdConfig, claudeSettings } = buildResult;
11745
12912
  if (templateConfig) {
11746
12913
  config.templateConfig = templateConfig;
11747
12914
  }
@@ -11758,7 +12925,15 @@ async function runInit(path7, options) {
11758
12925
  showConfigSummary(config);
11759
12926
  return;
11760
12927
  }
11761
- 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
+ );
11762
12937
  if (templateConfig && !options.noPlaceholders) {
11763
12938
  const claudePath = joinPath(projectPath, ".claude");
11764
12939
  await replaceTemplateConfigWithSpinner(claudePath, templateConfig);
@@ -11902,7 +13077,7 @@ async function buildDefaultConfig(projectPath, detection, options) {
11902
13077
  // No MCP servers in default mode
11903
13078
  };
11904
13079
  }
11905
- async function buildInteractiveConfig(projectPath, detection, registry, options) {
13080
+ async function buildInteractiveConfig(projectPath, detection, registry, options, existingConfig) {
11906
13081
  const projectName = await getProjectName(projectPath);
11907
13082
  const projectDesc = await getProjectDescription(projectPath);
11908
13083
  const wizardConfig = createInitWizardConfig(
@@ -11916,24 +13091,45 @@ async function buildInteractiveConfig(projectPath, detection, registry, options)
11916
13091
  },
11917
13092
  registry
11918
13093
  );
13094
+ const existingProject = existingConfig?.project;
13095
+ const existingPrefs = existingConfig?.preferences;
11919
13096
  const initialContext = {
11920
13097
  projectPath,
11921
13098
  registry,
11922
13099
  detection: {
11923
13100
  detected: detection.detected,
11924
13101
  projectType: detection.projectType,
11925
- packageManager: detection.packageManager,
13102
+ packageManager: existingPrefs?.packageManager || detection.packageManager,
11926
13103
  suggestedBundles: detection.suggestedBundles,
11927
13104
  detectedTechnologies: detection.detectedTechnologies
11928
13105
  },
13106
+ // Use existing project info as defaults, fallback to detected values
11929
13107
  projectInfo: {
11930
- name: projectName || "",
11931
- description: projectDesc || "",
11932
- org: "",
11933
- repo: projectName?.toLowerCase().replace(/\s+/g, "-") || "",
11934
- entityType: "item",
11935
- entityTypePlural: "items"
11936
- }
13108
+ name: existingProject?.name || projectName || "",
13109
+ description: existingProject?.description || projectDesc || "",
13110
+ org: existingProject?.org || "",
13111
+ repo: existingProject?.repo || projectName?.toLowerCase().replace(/\s+/g, "-") || "",
13112
+ entityType: existingProject?.entityType || "item",
13113
+ entityTypePlural: existingProject?.entityTypePlural || "items",
13114
+ domain: existingProject?.domain,
13115
+ location: existingProject?.location,
13116
+ author: existingProject?.author
13117
+ },
13118
+ // Use existing preferences as defaults
13119
+ preferences: existingPrefs ? {
13120
+ language: existingPrefs.language,
13121
+ responseLanguage: existingPrefs.responseLanguage,
13122
+ includeCoAuthor: existingPrefs.includeCoAuthor,
13123
+ packageManager: existingPrefs.packageManager
13124
+ } : void 0,
13125
+ // Use existing hook config as defaults
13126
+ hookConfig: existingConfig?.extras?.hooks,
13127
+ // Use existing code style config as defaults
13128
+ codeStyleConfig: existingConfig?.extras?.codeStyle,
13129
+ // Use existing folder preferences as defaults
13130
+ folderPreferences: existingConfig?.extras?.folderPreferences,
13131
+ // Use existing permissions config as defaults
13132
+ permissionsConfig: existingConfig?.customizations?.permissions
11937
13133
  };
11938
13134
  const wizardResult = await runWizard(
11939
13135
  wizardConfig,
@@ -11954,7 +13150,8 @@ async function buildInteractiveConfig(projectPath, detection, registry, options)
11954
13150
  codeStyleConfig,
11955
13151
  cicdConfig,
11956
13152
  folderPreferences,
11957
- templateConfig: templateConfigResult
13153
+ templateConfig: templateConfigResult,
13154
+ claudeSettings
11958
13155
  } = wizardResult.values;
11959
13156
  let mcpConfig = { level: "project", servers: [] };
11960
13157
  let skippedMcpConfigs = [];
@@ -12012,10 +13209,11 @@ async function buildInteractiveConfig(projectPath, detection, registry, options)
12012
13209
  config,
12013
13210
  skippedMcpConfigs,
12014
13211
  templateConfig: templateConfigResult,
12015
- cicdConfig
13212
+ cicdConfig,
13213
+ claudeSettings
12016
13214
  };
12017
13215
  }
12018
- async function executeInstallation(projectPath, config, registry, templatesPath, options, cicdConfig) {
13216
+ async function executeInstallation(projectPath, config, registry, templatesPath, options, cicdConfig, claudeSettings) {
12019
13217
  logger.newline();
12020
13218
  logger.title("Installing Configuration");
12021
13219
  if (config.scaffold.type === "full-project") {
@@ -12037,6 +13235,28 @@ async function executeInstallation(projectPath, config, registry, templatesPath,
12037
13235
  } else if (claudeMdResult.skipped) {
12038
13236
  logger.info("CLAUDE.md already exists, skipped");
12039
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
+ }
12040
13260
  const modulesByCategory = {
12041
13261
  agents: filterModules(registry, "agents", config.modules.agents.selected),
12042
13262
  skills: filterModules(registry, "skills", config.modules.skills.selected),
@@ -12659,7 +13879,7 @@ async function runStatus(options) {
12659
13879
  }
12660
13880
  const config = await readConfig(projectPath);
12661
13881
  if (!config) {
12662
- logger.warn(".claude directory exists but config.json is missing");
13882
+ logger.warn(".claude directory exists but qazuor-claude-config.json is missing");
12663
13883
  logger.info('Run "claude-config init" to initialize properly');
12664
13884
  process.exit(0);
12665
13885
  }
@@ -13159,8 +14379,8 @@ function createConfigureCommand() {
13159
14379
  ).option("--preview", "Preview changes without applying").option("--show-defaults", "Show global defaults").option("-v, --verbose", "Detailed output").action(runConfigure);
13160
14380
  return cmd;
13161
14381
  }
13162
- async function runConfigure(path7, options) {
13163
- const projectPath = resolvePath(path7 || ".");
14382
+ async function runConfigure(path10, options) {
14383
+ const projectPath = resolvePath(path10 || ".");
13164
14384
  const claudePath = joinPath(projectPath, ".claude");
13165
14385
  logger.configure({ verbose: options.verbose, silent: false });
13166
14386
  logger.title("Template Configuration");
@@ -13291,16 +14511,1837 @@ async function interactiveMode(claudePath, projectPath, options) {
13291
14511
  };
13292
14512
  existingConfig.customizations.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
13293
14513
  await writeConfig(projectPath, existingConfig);
13294
- logger.success("Configuration saved to .claude/config.json");
14514
+ logger.success("Configuration saved to .claude/qazuor-claude-config.json");
13295
14515
  }
13296
14516
  await promptSaveGlobalDefaults(templateConfig);
13297
14517
  logger.newline();
13298
14518
  logger.success("Template configuration complete!");
13299
14519
  }
13300
14520
 
14521
+ // src/cli/commands/standards.ts
14522
+ init_cjs_shims();
14523
+ var import_commander8 = require("commander");
14524
+
14525
+ // src/constants/standards-defaults.ts
14526
+ init_cjs_shims();
14527
+ var DEFAULT_CODE_STANDARDS = {
14528
+ indentStyle: "space",
14529
+ indentSize: 2,
14530
+ maxLineLength: 100,
14531
+ maxFileLines: 500,
14532
+ quoteStyle: "single",
14533
+ semicolons: true,
14534
+ trailingCommas: "es5",
14535
+ allowAny: false,
14536
+ namedExportsOnly: true,
14537
+ roroPattern: true,
14538
+ jsDocRequired: true
14539
+ };
14540
+ var DEFAULT_TESTING_STANDARDS = {
14541
+ coverageTarget: 80,
14542
+ tddRequired: true,
14543
+ testPattern: "aaa",
14544
+ testLocation: "separate",
14545
+ unitTestMaxMs: 100,
14546
+ integrationTestMaxMs: 1e3
14547
+ };
14548
+ var DEFAULT_DOCUMENTATION_STANDARDS = {
14549
+ jsDocLevel: "standard",
14550
+ requireExamples: false,
14551
+ changelogFormat: "conventional",
14552
+ inlineCommentPolicy: "why-not-what"
14553
+ };
14554
+ var DEFAULT_DESIGN_STANDARDS = {
14555
+ cssFramework: "tailwind",
14556
+ componentLibrary: "shadcn",
14557
+ accessibilityLevel: "AA",
14558
+ darkModeSupport: true
14559
+ };
14560
+ var DEFAULT_SECURITY_STANDARDS = {
14561
+ authPattern: "jwt",
14562
+ inputValidation: "zod",
14563
+ csrfProtection: true,
14564
+ rateLimiting: true
14565
+ };
14566
+ var DEFAULT_PERFORMANCE_STANDARDS = {
14567
+ lcpTarget: 2500,
14568
+ fidTarget: 100,
14569
+ clsTarget: 0.1,
14570
+ bundleSizeTargetKb: 250,
14571
+ apiResponseTargetMs: 300
14572
+ };
14573
+ var DEFAULT_STANDARDS_CONFIG = {
14574
+ code: DEFAULT_CODE_STANDARDS,
14575
+ testing: DEFAULT_TESTING_STANDARDS,
14576
+ documentation: DEFAULT_DOCUMENTATION_STANDARDS,
14577
+ design: DEFAULT_DESIGN_STANDARDS,
14578
+ security: DEFAULT_SECURITY_STANDARDS,
14579
+ performance: DEFAULT_PERFORMANCE_STANDARDS
14580
+ };
14581
+ var STANDARDS_PRESETS = {
14582
+ strict: {
14583
+ name: "Strict",
14584
+ description: "High quality standards with strict enforcement (90%+ coverage, TDD required)",
14585
+ config: {
14586
+ code: {
14587
+ ...DEFAULT_CODE_STANDARDS,
14588
+ allowAny: false,
14589
+ jsDocRequired: true
14590
+ },
14591
+ testing: {
14592
+ ...DEFAULT_TESTING_STANDARDS,
14593
+ coverageTarget: 90,
14594
+ tddRequired: true
14595
+ },
14596
+ documentation: {
14597
+ ...DEFAULT_DOCUMENTATION_STANDARDS,
14598
+ jsDocLevel: "comprehensive",
14599
+ requireExamples: true
14600
+ },
14601
+ design: {
14602
+ ...DEFAULT_DESIGN_STANDARDS,
14603
+ accessibilityLevel: "AAA"
14604
+ },
14605
+ security: {
14606
+ ...DEFAULT_SECURITY_STANDARDS,
14607
+ csrfProtection: true,
14608
+ rateLimiting: true
14609
+ },
14610
+ performance: {
14611
+ ...DEFAULT_PERFORMANCE_STANDARDS,
14612
+ lcpTarget: 2e3,
14613
+ apiResponseTargetMs: 200
14614
+ }
14615
+ }
14616
+ },
14617
+ balanced: {
14618
+ name: "Balanced",
14619
+ description: "Good balance between quality and pragmatism (80% coverage)",
14620
+ config: DEFAULT_STANDARDS_CONFIG
14621
+ },
14622
+ relaxed: {
14623
+ name: "Relaxed",
14624
+ description: "More flexible standards for rapid development (70% coverage)",
14625
+ config: {
14626
+ code: {
14627
+ ...DEFAULT_CODE_STANDARDS,
14628
+ maxFileLines: 800,
14629
+ jsDocRequired: false
14630
+ },
14631
+ testing: {
14632
+ ...DEFAULT_TESTING_STANDARDS,
14633
+ coverageTarget: 70,
14634
+ tddRequired: false
14635
+ },
14636
+ documentation: {
14637
+ ...DEFAULT_DOCUMENTATION_STANDARDS,
14638
+ jsDocLevel: "minimal",
14639
+ requireExamples: false
14640
+ },
14641
+ design: {
14642
+ ...DEFAULT_DESIGN_STANDARDS,
14643
+ accessibilityLevel: "A"
14644
+ },
14645
+ security: {
14646
+ ...DEFAULT_SECURITY_STANDARDS
14647
+ },
14648
+ performance: {
14649
+ ...DEFAULT_PERFORMANCE_STANDARDS,
14650
+ lcpTarget: 4e3,
14651
+ apiResponseTargetMs: 500
14652
+ }
14653
+ }
14654
+ },
14655
+ startup: {
14656
+ name: "Startup",
14657
+ description: "Fast iteration with minimum viable standards (60% coverage)",
14658
+ config: {
14659
+ code: {
14660
+ ...DEFAULT_CODE_STANDARDS,
14661
+ maxFileLines: 1e3,
14662
+ jsDocRequired: false,
14663
+ roroPattern: false
14664
+ },
14665
+ testing: {
14666
+ coverageTarget: 60,
14667
+ tddRequired: false,
14668
+ testPattern: "aaa",
14669
+ testLocation: "colocated",
14670
+ unitTestMaxMs: 200,
14671
+ integrationTestMaxMs: 2e3
14672
+ },
14673
+ documentation: {
14674
+ jsDocLevel: "minimal",
14675
+ requireExamples: false,
14676
+ changelogFormat: "conventional",
14677
+ inlineCommentPolicy: "minimal"
14678
+ },
14679
+ design: {
14680
+ cssFramework: "tailwind",
14681
+ componentLibrary: "shadcn",
14682
+ accessibilityLevel: "A",
14683
+ darkModeSupport: false
14684
+ },
14685
+ security: {
14686
+ authPattern: "jwt",
14687
+ inputValidation: "zod",
14688
+ csrfProtection: false,
14689
+ rateLimiting: false
14690
+ },
14691
+ performance: {
14692
+ lcpTarget: 4e3,
14693
+ fidTarget: 300,
14694
+ clsTarget: 0.25,
14695
+ bundleSizeTargetKb: 500,
14696
+ apiResponseTargetMs: 500
14697
+ }
14698
+ }
14699
+ },
14700
+ enterprise: {
14701
+ name: "Enterprise",
14702
+ description: "Enterprise-grade standards with full compliance (95%+ coverage)",
14703
+ config: {
14704
+ code: {
14705
+ indentStyle: "space",
14706
+ indentSize: 2,
14707
+ maxLineLength: 120,
14708
+ maxFileLines: 400,
14709
+ quoteStyle: "single",
14710
+ semicolons: true,
14711
+ trailingCommas: "all",
14712
+ allowAny: false,
14713
+ namedExportsOnly: true,
14714
+ roroPattern: true,
14715
+ jsDocRequired: true
14716
+ },
14717
+ testing: {
14718
+ coverageTarget: 95,
14719
+ tddRequired: true,
14720
+ testPattern: "aaa",
14721
+ testLocation: "separate",
14722
+ unitTestMaxMs: 50,
14723
+ integrationTestMaxMs: 500
14724
+ },
14725
+ documentation: {
14726
+ jsDocLevel: "comprehensive",
14727
+ requireExamples: true,
14728
+ changelogFormat: "keepachangelog",
14729
+ inlineCommentPolicy: "why-not-what"
14730
+ },
14731
+ design: {
14732
+ cssFramework: "tailwind",
14733
+ componentLibrary: "radix",
14734
+ accessibilityLevel: "AAA",
14735
+ darkModeSupport: true
14736
+ },
14737
+ security: {
14738
+ authPattern: "oauth",
14739
+ inputValidation: "zod",
14740
+ csrfProtection: true,
14741
+ rateLimiting: true
14742
+ },
14743
+ performance: {
14744
+ lcpTarget: 1500,
14745
+ fidTarget: 50,
14746
+ clsTarget: 0.05,
14747
+ bundleSizeTargetKb: 150,
14748
+ apiResponseTargetMs: 150
14749
+ }
14750
+ }
14751
+ },
14752
+ custom: {
14753
+ name: "Custom",
14754
+ description: "Configure each standard manually",
14755
+ config: DEFAULT_STANDARDS_CONFIG
14756
+ }
14757
+ };
14758
+
14759
+ // src/lib/standards/index.ts
14760
+ init_cjs_shims();
14761
+
14762
+ // src/lib/standards/definitions.ts
14763
+ init_cjs_shims();
14764
+ var CODE_STANDARDS_DEFINITION = {
14765
+ id: "code",
14766
+ name: "Code Standards",
14767
+ description: "Code style, formatting, and TypeScript conventions",
14768
+ icon: "\u{1F4DD}",
14769
+ options: [
14770
+ {
14771
+ id: "indentStyle",
14772
+ label: "Indent Style",
14773
+ description: "Use spaces or tabs for indentation",
14774
+ type: "select",
14775
+ choices: [
14776
+ { name: "Spaces", value: "space", description: "Standard for most projects" },
14777
+ { name: "Tabs", value: "tab", description: "Better for accessibility" }
14778
+ ],
14779
+ affectsPlaceholders: ["{{INDENT_STYLE}}"]
14780
+ },
14781
+ {
14782
+ id: "indentSize",
14783
+ label: "Indent Size",
14784
+ description: "Number of spaces/tab width",
14785
+ type: "select",
14786
+ choices: [
14787
+ { name: "2 spaces", value: "2", description: "Most common in JS/TS" },
14788
+ { name: "4 spaces", value: "4", description: "More readable for some" }
14789
+ ],
14790
+ affectsPlaceholders: ["{{INDENT_SIZE}}"]
14791
+ },
14792
+ {
14793
+ id: "maxLineLength",
14794
+ label: "Max Line Length",
14795
+ description: "Maximum characters per line",
14796
+ type: "select",
14797
+ choices: [
14798
+ { name: "80 characters", value: "80", description: "Classic terminal width" },
14799
+ { name: "100 characters", value: "100", description: "Modern balance" },
14800
+ { name: "120 characters", value: "120", description: "Wide screens" }
14801
+ ],
14802
+ affectsPlaceholders: ["{{MAX_LINE_LENGTH}}"]
14803
+ },
14804
+ {
14805
+ id: "maxFileLines",
14806
+ label: "Max File Lines",
14807
+ description: "Maximum lines per file (excluding tests, docs, JSON)",
14808
+ type: "select",
14809
+ choices: [
14810
+ { name: "300 lines", value: "300", description: "Very strict" },
14811
+ { name: "500 lines", value: "500", description: "Recommended" },
14812
+ { name: "800 lines", value: "800", description: "Relaxed" },
14813
+ { name: "1000 lines", value: "1000", description: "Very relaxed" }
14814
+ ],
14815
+ affectsPlaceholders: ["{{MAX_FILE_LINES}}"]
14816
+ },
14817
+ {
14818
+ id: "quoteStyle",
14819
+ label: "Quote Style",
14820
+ description: "Single or double quotes for strings",
14821
+ type: "select",
14822
+ choices: [
14823
+ { name: "Single quotes", value: "single", description: "const x = 'hello'" },
14824
+ { name: "Double quotes", value: "double", description: 'const x = "hello"' }
14825
+ ],
14826
+ affectsPlaceholders: ["{{QUOTE_STYLE}}"]
14827
+ },
14828
+ {
14829
+ id: "semicolons",
14830
+ label: "Semicolons",
14831
+ description: "Use semicolons at end of statements",
14832
+ type: "boolean",
14833
+ affectsPlaceholders: ["{{USE_SEMICOLONS}}"]
14834
+ },
14835
+ {
14836
+ id: "trailingCommas",
14837
+ label: "Trailing Commas",
14838
+ description: "Add trailing commas in multiline constructs",
14839
+ type: "select",
14840
+ choices: [
14841
+ { name: "ES5", value: "es5", description: "Where valid in ES5 (objects, arrays)" },
14842
+ { name: "All", value: "all", description: "Everywhere possible" },
14843
+ { name: "None", value: "none", description: "No trailing commas" }
14844
+ ],
14845
+ affectsPlaceholders: ["{{TRAILING_COMMAS}}"]
14846
+ },
14847
+ {
14848
+ id: "allowAny",
14849
+ label: 'Allow "any" Type',
14850
+ description: 'Allow the "any" type in TypeScript',
14851
+ type: "boolean",
14852
+ affectsPlaceholders: ["{{ALLOW_ANY}}"]
14853
+ },
14854
+ {
14855
+ id: "namedExportsOnly",
14856
+ label: "Named Exports Only",
14857
+ description: "Require named exports (no default exports)",
14858
+ type: "boolean",
14859
+ affectsPlaceholders: ["{{NAMED_EXPORTS_ONLY}}"]
14860
+ },
14861
+ {
14862
+ id: "roroPattern",
14863
+ label: "RO-RO Pattern",
14864
+ description: "Require Receive Object, Return Object pattern",
14865
+ type: "boolean",
14866
+ affectsPlaceholders: ["{{RORO_PATTERN}}"]
14867
+ },
14868
+ {
14869
+ id: "jsDocRequired",
14870
+ label: "JSDoc Required",
14871
+ description: "Require JSDoc for all exports",
14872
+ type: "boolean",
14873
+ affectsPlaceholders: ["{{JSDOC_REQUIRED}}"]
14874
+ }
14875
+ ],
14876
+ targetFiles: ["code-standards.md", "architecture-patterns.md"]
14877
+ };
14878
+ var TESTING_STANDARDS_DEFINITION = {
14879
+ id: "testing",
14880
+ name: "Testing Standards",
14881
+ description: "Test coverage, TDD, and testing methodology",
14882
+ icon: "\u{1F9EA}",
14883
+ options: [
14884
+ {
14885
+ id: "coverageTarget",
14886
+ label: "Coverage Target",
14887
+ description: "Minimum code coverage percentage",
14888
+ type: "select",
14889
+ choices: [
14890
+ { name: "60%", value: "60", description: "Minimum viable" },
14891
+ { name: "70%", value: "70", description: "Relaxed" },
14892
+ { name: "80%", value: "80", description: "Standard" },
14893
+ { name: "90%", value: "90", description: "Strict" },
14894
+ { name: "95%", value: "95", description: "Enterprise" }
14895
+ ],
14896
+ affectsPlaceholders: ["{{COVERAGE_TARGET}}"]
14897
+ },
14898
+ {
14899
+ id: "tddRequired",
14900
+ label: "TDD Required",
14901
+ description: "Require Test-Driven Development (Red-Green-Refactor)",
14902
+ type: "boolean",
14903
+ affectsPlaceholders: ["{{TDD_REQUIRED}}"]
14904
+ },
14905
+ {
14906
+ id: "testPattern",
14907
+ label: "Test Pattern",
14908
+ description: "Test structure pattern",
14909
+ type: "select",
14910
+ choices: [
14911
+ { name: "AAA", value: "aaa", description: "Arrange-Act-Assert" },
14912
+ { name: "GWT", value: "gwt", description: "Given-When-Then" }
14913
+ ],
14914
+ affectsPlaceholders: ["{{TEST_PATTERN}}"]
14915
+ },
14916
+ {
14917
+ id: "testLocation",
14918
+ label: "Test Location",
14919
+ description: "Where to place test files",
14920
+ type: "select",
14921
+ choices: [
14922
+ { name: "Separate", value: "separate", description: "test/ folder at root" },
14923
+ { name: "Colocated", value: "colocated", description: "__tests__ near source" }
14924
+ ],
14925
+ affectsPlaceholders: ["{{TEST_LOCATION}}"]
14926
+ },
14927
+ {
14928
+ id: "unitTestMaxMs",
14929
+ label: "Unit Test Max (ms)",
14930
+ description: "Maximum milliseconds per unit test",
14931
+ type: "select",
14932
+ choices: [
14933
+ { name: "50ms", value: "50", description: "Very fast" },
14934
+ { name: "100ms", value: "100", description: "Standard" },
14935
+ { name: "200ms", value: "200", description: "Relaxed" }
14936
+ ],
14937
+ affectsPlaceholders: ["{{UNIT_TEST_MAX_MS}}"]
14938
+ },
14939
+ {
14940
+ id: "integrationTestMaxMs",
14941
+ label: "Integration Test Max (ms)",
14942
+ description: "Maximum milliseconds per integration test",
14943
+ type: "select",
14944
+ choices: [
14945
+ { name: "500ms", value: "500", description: "Fast" },
14946
+ { name: "1000ms", value: "1000", description: "Standard" },
14947
+ { name: "2000ms", value: "2000", description: "Relaxed" }
14948
+ ],
14949
+ affectsPlaceholders: ["{{INTEGRATION_TEST_MAX_MS}}"]
14950
+ }
14951
+ ],
14952
+ targetFiles: ["testing-standards.md"]
14953
+ };
14954
+ var DOCUMENTATION_STANDARDS_DEFINITION = {
14955
+ id: "documentation",
14956
+ name: "Documentation Standards",
14957
+ description: "JSDoc, comments, and changelog conventions",
14958
+ icon: "\u{1F4DA}",
14959
+ options: [
14960
+ {
14961
+ id: "jsDocLevel",
14962
+ label: "JSDoc Level",
14963
+ description: "Level of detail in JSDoc comments",
14964
+ type: "select",
14965
+ choices: [
14966
+ { name: "Minimal", value: "minimal", description: "Brief description only" },
14967
+ { name: "Standard", value: "standard", description: "Description + params + returns" },
14968
+ { name: "Comprehensive", value: "comprehensive", description: "Full docs with examples" }
14969
+ ],
14970
+ affectsPlaceholders: ["{{JSDOC_LEVEL}}"]
14971
+ },
14972
+ {
14973
+ id: "requireExamples",
14974
+ label: "Require Examples",
14975
+ description: "Require @example in JSDoc",
14976
+ type: "boolean",
14977
+ affectsPlaceholders: ["{{REQUIRE_EXAMPLES}}"]
14978
+ },
14979
+ {
14980
+ id: "changelogFormat",
14981
+ label: "Changelog Format",
14982
+ description: "Changelog format to follow",
14983
+ type: "select",
14984
+ choices: [
14985
+ { name: "Conventional", value: "conventional", description: "Auto-generated from commits" },
14986
+ { name: "Keep a Changelog", value: "keepachangelog", description: "Manual, semantic" }
14987
+ ],
14988
+ affectsPlaceholders: ["{{CHANGELOG_FORMAT}}"]
14989
+ },
14990
+ {
14991
+ id: "inlineCommentPolicy",
14992
+ label: "Inline Comment Policy",
14993
+ description: "Policy for inline code comments",
14994
+ type: "select",
14995
+ choices: [
14996
+ {
14997
+ name: "Why not What",
14998
+ value: "why-not-what",
14999
+ description: "Explain reasoning, not obvious"
15000
+ },
15001
+ { name: "Minimal", value: "minimal", description: "Only when necessary" },
15002
+ { name: "Extensive", value: "extensive", description: "Comment thoroughly" }
15003
+ ],
15004
+ affectsPlaceholders: ["{{INLINE_COMMENT_POLICY}}"]
15005
+ }
15006
+ ],
15007
+ targetFiles: ["documentation-standards.md"]
15008
+ };
15009
+ var DESIGN_STANDARDS_DEFINITION = {
15010
+ id: "design",
15011
+ name: "Design Standards",
15012
+ description: "UI/UX, CSS, and accessibility standards",
15013
+ icon: "\u{1F3A8}",
15014
+ options: [
15015
+ {
15016
+ id: "cssFramework",
15017
+ label: "CSS Framework",
15018
+ description: "CSS/styling approach",
15019
+ type: "select",
15020
+ choices: [
15021
+ { name: "Tailwind CSS", value: "tailwind", description: "Utility-first CSS" },
15022
+ { name: "CSS Modules", value: "css-modules", description: "Scoped CSS" },
15023
+ { name: "Styled Components", value: "styled-components", description: "CSS-in-JS" },
15024
+ { name: "Vanilla CSS", value: "vanilla", description: "Plain CSS" }
15025
+ ],
15026
+ affectsPlaceholders: ["{{CSS_FRAMEWORK}}"]
15027
+ },
15028
+ {
15029
+ id: "componentLibrary",
15030
+ label: "Component Library",
15031
+ description: "UI component library",
15032
+ type: "select",
15033
+ choices: [
15034
+ { name: "shadcn/ui", value: "shadcn", description: "Copy-paste components" },
15035
+ { name: "Radix UI", value: "radix", description: "Unstyled primitives" },
15036
+ { name: "Headless UI", value: "headless", description: "Unstyled, accessible" },
15037
+ { name: "None", value: "none", description: "Build from scratch" }
15038
+ ],
15039
+ affectsPlaceholders: ["{{COMPONENT_LIBRARY}}"]
15040
+ },
15041
+ {
15042
+ id: "accessibilityLevel",
15043
+ label: "Accessibility Level",
15044
+ description: "WCAG accessibility compliance level",
15045
+ type: "select",
15046
+ choices: [
15047
+ { name: "Level A", value: "A", description: "Minimum" },
15048
+ { name: "Level AA", value: "AA", description: "Standard (recommended)" },
15049
+ { name: "Level AAA", value: "AAA", description: "Highest" }
15050
+ ],
15051
+ affectsPlaceholders: ["{{WCAG_LEVEL}}", "{{ACCESSIBILITY_LEVEL}}"]
15052
+ },
15053
+ {
15054
+ id: "darkModeSupport",
15055
+ label: "Dark Mode Support",
15056
+ description: "Support dark mode theme",
15057
+ type: "boolean",
15058
+ affectsPlaceholders: ["{{DARK_MODE_SUPPORT}}"]
15059
+ }
15060
+ ],
15061
+ targetFiles: ["design-standards.md"]
15062
+ };
15063
+ var SECURITY_STANDARDS_DEFINITION = {
15064
+ id: "security",
15065
+ name: "Security Standards",
15066
+ description: "Authentication, validation, and security practices",
15067
+ icon: "\u{1F512}",
15068
+ options: [
15069
+ {
15070
+ id: "authPattern",
15071
+ label: "Auth Pattern",
15072
+ description: "Authentication approach",
15073
+ type: "select",
15074
+ choices: [
15075
+ { name: "JWT", value: "jwt", description: "JSON Web Tokens" },
15076
+ { name: "Session", value: "session", description: "Server-side sessions" },
15077
+ { name: "OAuth", value: "oauth", description: "OAuth 2.0 / OIDC" },
15078
+ { name: "None", value: "none", description: "No authentication" }
15079
+ ],
15080
+ affectsPlaceholders: ["{{AUTH_PATTERN}}"]
15081
+ },
15082
+ {
15083
+ id: "inputValidation",
15084
+ label: "Input Validation",
15085
+ description: "Validation library",
15086
+ type: "select",
15087
+ choices: [
15088
+ { name: "Zod", value: "zod", description: "TypeScript-first validation" },
15089
+ { name: "Yup", value: "yup", description: "Schema builder" },
15090
+ { name: "Joi", value: "joi", description: "Data validation" },
15091
+ { name: "Manual", value: "manual", description: "Custom validation" }
15092
+ ],
15093
+ affectsPlaceholders: ["{{VALIDATION_LIBRARY}}", "{{INPUT_VALIDATION}}"]
15094
+ },
15095
+ {
15096
+ id: "csrfProtection",
15097
+ label: "CSRF Protection",
15098
+ description: "Enable Cross-Site Request Forgery protection",
15099
+ type: "boolean",
15100
+ affectsPlaceholders: ["{{CSRF_PROTECTION}}"]
15101
+ },
15102
+ {
15103
+ id: "rateLimiting",
15104
+ label: "Rate Limiting",
15105
+ description: "Enable API rate limiting",
15106
+ type: "boolean",
15107
+ affectsPlaceholders: ["{{RATE_LIMITING}}"]
15108
+ }
15109
+ ],
15110
+ targetFiles: ["security-standards.md"]
15111
+ };
15112
+ var PERFORMANCE_STANDARDS_DEFINITION = {
15113
+ id: "performance",
15114
+ name: "Performance Standards",
15115
+ description: "Core Web Vitals and performance targets",
15116
+ icon: "\u26A1",
15117
+ options: [
15118
+ {
15119
+ id: "lcpTarget",
15120
+ label: "LCP Target (ms)",
15121
+ description: "Largest Contentful Paint target",
15122
+ type: "select",
15123
+ choices: [
15124
+ { name: "1500ms", value: "1500", description: "Excellent" },
15125
+ { name: "2000ms", value: "2000", description: "Good" },
15126
+ { name: "2500ms", value: "2500", description: "Standard" },
15127
+ { name: "4000ms", value: "4000", description: "Needs improvement" }
15128
+ ],
15129
+ affectsPlaceholders: ["{{LCP_TARGET}}"]
15130
+ },
15131
+ {
15132
+ id: "fidTarget",
15133
+ label: "FID Target (ms)",
15134
+ description: "First Input Delay target",
15135
+ type: "select",
15136
+ choices: [
15137
+ { name: "50ms", value: "50", description: "Excellent" },
15138
+ { name: "100ms", value: "100", description: "Good" },
15139
+ { name: "200ms", value: "200", description: "Standard" },
15140
+ { name: "300ms", value: "300", description: "Needs improvement" }
15141
+ ],
15142
+ affectsPlaceholders: ["{{FID_TARGET}}"]
15143
+ },
15144
+ {
15145
+ id: "clsTarget",
15146
+ label: "CLS Target",
15147
+ description: "Cumulative Layout Shift target",
15148
+ type: "select",
15149
+ choices: [
15150
+ { name: "0.05", value: "0.05", description: "Excellent" },
15151
+ { name: "0.1", value: "0.1", description: "Good" },
15152
+ { name: "0.15", value: "0.15", description: "Standard" },
15153
+ { name: "0.25", value: "0.25", description: "Needs improvement" }
15154
+ ],
15155
+ affectsPlaceholders: ["{{CLS_TARGET}}"]
15156
+ },
15157
+ {
15158
+ id: "bundleSizeTargetKb",
15159
+ label: "Bundle Size (KB)",
15160
+ description: "Maximum initial bundle size",
15161
+ type: "select",
15162
+ choices: [
15163
+ { name: "100KB", value: "100", description: "Very strict" },
15164
+ { name: "150KB", value: "150", description: "Strict" },
15165
+ { name: "250KB", value: "250", description: "Standard" },
15166
+ { name: "500KB", value: "500", description: "Relaxed" }
15167
+ ],
15168
+ affectsPlaceholders: ["{{BUNDLE_SIZE_TARGET}}"]
15169
+ },
15170
+ {
15171
+ id: "apiResponseTargetMs",
15172
+ label: "API Response (ms)",
15173
+ description: "Maximum API response time",
15174
+ type: "select",
15175
+ choices: [
15176
+ { name: "100ms", value: "100", description: "Very fast" },
15177
+ { name: "200ms", value: "200", description: "Fast" },
15178
+ { name: "300ms", value: "300", description: "Standard" },
15179
+ { name: "500ms", value: "500", description: "Relaxed" }
15180
+ ],
15181
+ affectsPlaceholders: ["{{API_RESPONSE_TARGET}}"]
15182
+ }
15183
+ ],
15184
+ targetFiles: ["performance-standards.md"]
15185
+ };
15186
+ var STANDARDS_DEFINITIONS = {
15187
+ code: CODE_STANDARDS_DEFINITION,
15188
+ testing: TESTING_STANDARDS_DEFINITION,
15189
+ documentation: DOCUMENTATION_STANDARDS_DEFINITION,
15190
+ design: DESIGN_STANDARDS_DEFINITION,
15191
+ security: SECURITY_STANDARDS_DEFINITION,
15192
+ performance: PERFORMANCE_STANDARDS_DEFINITION
15193
+ };
15194
+
15195
+ // src/lib/standards/replacer.ts
15196
+ init_cjs_shims();
15197
+ var fs6 = __toESM(require("fs/promises"), 1);
15198
+ var path7 = __toESM(require("path"), 1);
15199
+ var import_ora3 = __toESM(require("ora"), 1);
15200
+ var PROCESSABLE_EXTENSIONS2 = [".md", ".json", ".yaml", ".yml", ".txt"];
15201
+ var SKIP_DIRECTORIES3 = ["node_modules", ".git", "dist", "build", ".next", ".turbo"];
15202
+ function flattenStandardsConfig(config) {
15203
+ const flattened = {};
15204
+ if (config.code) {
15205
+ flattened["{{INDENT_STYLE}}"] = config.code.indentStyle;
15206
+ flattened["{{INDENT_SIZE}}"] = String(config.code.indentSize);
15207
+ flattened["{{MAX_LINE_LENGTH}}"] = String(config.code.maxLineLength);
15208
+ flattened["{{MAX_FILE_LINES}}"] = String(config.code.maxFileLines);
15209
+ flattened["{{QUOTE_STYLE}}"] = config.code.quoteStyle;
15210
+ flattened["{{USE_SEMICOLONS}}"] = config.code.semicolons ? "yes" : "no";
15211
+ flattened["{{TRAILING_COMMAS}}"] = config.code.trailingCommas;
15212
+ flattened["{{ALLOW_ANY}}"] = config.code.allowAny ? "yes" : "no";
15213
+ flattened["{{NAMED_EXPORTS_ONLY}}"] = config.code.namedExportsOnly ? "yes" : "no";
15214
+ flattened["{{RORO_PATTERN}}"] = config.code.roroPattern ? "yes" : "no";
15215
+ flattened["{{JSDOC_REQUIRED}}"] = config.code.jsDocRequired ? "yes" : "no";
15216
+ }
15217
+ if (config.testing) {
15218
+ flattened["{{COVERAGE_TARGET}}"] = String(config.testing.coverageTarget);
15219
+ flattened["{{TDD_REQUIRED}}"] = config.testing.tddRequired ? "yes" : "no";
15220
+ flattened["{{TEST_PATTERN}}"] = config.testing.testPattern.toUpperCase();
15221
+ flattened["{{TEST_LOCATION}}"] = config.testing.testLocation;
15222
+ flattened["{{UNIT_TEST_MAX_MS}}"] = String(config.testing.unitTestMaxMs);
15223
+ flattened["{{INTEGRATION_TEST_MAX_MS}}"] = String(config.testing.integrationTestMaxMs);
15224
+ }
15225
+ if (config.documentation) {
15226
+ flattened["{{JSDOC_LEVEL}}"] = config.documentation.jsDocLevel;
15227
+ flattened["{{REQUIRE_EXAMPLES}}"] = config.documentation.requireExamples ? "yes" : "no";
15228
+ flattened["{{CHANGELOG_FORMAT}}"] = config.documentation.changelogFormat;
15229
+ flattened["{{INLINE_COMMENT_POLICY}}"] = config.documentation.inlineCommentPolicy;
15230
+ }
15231
+ if (config.design) {
15232
+ flattened["{{CSS_FRAMEWORK}}"] = config.design.cssFramework;
15233
+ flattened["{{COMPONENT_LIBRARY}}"] = config.design.componentLibrary;
15234
+ flattened["{{WCAG_LEVEL}}"] = config.design.accessibilityLevel;
15235
+ flattened["{{ACCESSIBILITY_LEVEL}}"] = config.design.accessibilityLevel;
15236
+ flattened["{{DARK_MODE_SUPPORT}}"] = config.design.darkModeSupport ? "yes" : "no";
15237
+ }
15238
+ if (config.security) {
15239
+ flattened["{{AUTH_PATTERN}}"] = config.security.authPattern;
15240
+ flattened["{{VALIDATION_LIBRARY}}"] = config.security.inputValidation;
15241
+ flattened["{{INPUT_VALIDATION}}"] = config.security.inputValidation;
15242
+ flattened["{{CSRF_PROTECTION}}"] = config.security.csrfProtection ? "yes" : "no";
15243
+ flattened["{{RATE_LIMITING}}"] = config.security.rateLimiting ? "yes" : "no";
15244
+ }
15245
+ if (config.performance) {
15246
+ flattened["{{LCP_TARGET}}"] = String(config.performance.lcpTarget);
15247
+ flattened["{{FID_TARGET}}"] = String(config.performance.fidTarget);
15248
+ flattened["{{CLS_TARGET}}"] = String(config.performance.clsTarget);
15249
+ flattened["{{BUNDLE_SIZE_TARGET}}"] = String(config.performance.bundleSizeTargetKb);
15250
+ flattened["{{API_RESPONSE_TARGET}}"] = String(config.performance.apiResponseTargetMs);
15251
+ }
15252
+ return flattened;
15253
+ }
15254
+ function shouldProcessFile2(filePath) {
15255
+ const ext = path7.extname(filePath).toLowerCase();
15256
+ return PROCESSABLE_EXTENSIONS2.includes(ext);
15257
+ }
15258
+ function shouldSkipDirectory3(dirName) {
15259
+ return SKIP_DIRECTORIES3.includes(dirName) || dirName.startsWith(".");
15260
+ }
15261
+ async function getAllFiles3(dir) {
15262
+ const files = [];
15263
+ try {
15264
+ const entries = await fs6.readdir(dir, { withFileTypes: true });
15265
+ for (const entry of entries) {
15266
+ const fullPath = path7.join(dir, entry.name);
15267
+ if (entry.isDirectory()) {
15268
+ if (!shouldSkipDirectory3(entry.name)) {
15269
+ const subFiles = await getAllFiles3(fullPath);
15270
+ files.push(...subFiles);
15271
+ }
15272
+ } else if (entry.isFile() && shouldProcessFile2(entry.name)) {
15273
+ files.push(fullPath);
15274
+ }
15275
+ }
15276
+ } catch {
15277
+ }
15278
+ return files;
15279
+ }
15280
+ async function replaceInFile3(filePath, replacements) {
15281
+ const changes = [];
15282
+ try {
15283
+ let content = await fs6.readFile(filePath, "utf-8");
15284
+ let modified = false;
15285
+ for (const [placeholder, value] of Object.entries(replacements)) {
15286
+ if (content.includes(placeholder)) {
15287
+ content = content.split(placeholder).join(value);
15288
+ changes.push({ placeholder, value });
15289
+ modified = true;
15290
+ }
15291
+ }
15292
+ if (modified) {
15293
+ await fs6.writeFile(filePath, content, "utf-8");
15294
+ }
15295
+ } catch {
15296
+ }
15297
+ return changes;
15298
+ }
15299
+ async function replaceStandardsPlaceholders(claudePath, config) {
15300
+ const replacements = flattenStandardsConfig(config);
15301
+ const standardsDir = path7.join(claudePath, "docs", "standards");
15302
+ const files = await getAllFiles3(standardsDir);
15303
+ const report = {
15304
+ modifiedFiles: [],
15305
+ replacedPlaceholders: [],
15306
+ unusedPlaceholders: [],
15307
+ errors: []
15308
+ };
15309
+ const usedPlaceholders = /* @__PURE__ */ new Set();
15310
+ for (const file of files) {
15311
+ try {
15312
+ const changes = await replaceInFile3(file, replacements);
15313
+ if (changes.length > 0) {
15314
+ report.modifiedFiles.push(path7.relative(claudePath, file));
15315
+ for (const change of changes) {
15316
+ if (!report.replacedPlaceholders.includes(change.placeholder)) {
15317
+ report.replacedPlaceholders.push(change.placeholder);
15318
+ }
15319
+ usedPlaceholders.add(change.placeholder);
15320
+ }
15321
+ }
15322
+ } catch (error) {
15323
+ report.errors.push(`Error processing ${file}: ${String(error)}`);
15324
+ }
15325
+ }
15326
+ for (const placeholder of Object.keys(replacements)) {
15327
+ if (!usedPlaceholders.has(placeholder)) {
15328
+ report.unusedPlaceholders.push(placeholder);
15329
+ }
15330
+ }
15331
+ return report;
15332
+ }
15333
+ async function replaceStandardsWithSpinner(claudePath, config) {
15334
+ const spinner2 = (0, import_ora3.default)("Applying standards configuration...").start();
15335
+ try {
15336
+ const report = await replaceStandardsPlaceholders(claudePath, config);
15337
+ if (report.modifiedFiles.length > 0) {
15338
+ spinner2.succeed(
15339
+ `Applied ${report.replacedPlaceholders.length} standards to ${report.modifiedFiles.length} files`
15340
+ );
15341
+ } else {
15342
+ spinner2.info("No standards placeholders found to replace");
15343
+ }
15344
+ return report;
15345
+ } catch (error) {
15346
+ spinner2.fail("Failed to apply standards configuration");
15347
+ throw error;
15348
+ }
15349
+ }
15350
+ async function previewStandardsReplacements(claudePath, config) {
15351
+ const replacements = flattenStandardsConfig(config);
15352
+ const standardsDir = path7.join(claudePath, "docs", "standards");
15353
+ const files = await getAllFiles3(standardsDir);
15354
+ const preview = [];
15355
+ for (const file of files) {
15356
+ try {
15357
+ const content = await fs6.readFile(file, "utf-8");
15358
+ for (const [placeholder, value] of Object.entries(replacements)) {
15359
+ if (content.includes(placeholder)) {
15360
+ preview.push({
15361
+ file: path7.relative(claudePath, file),
15362
+ placeholder,
15363
+ value
15364
+ });
15365
+ }
15366
+ }
15367
+ } catch {
15368
+ }
15369
+ }
15370
+ return preview;
15371
+ }
15372
+ function formatStandardsReport(report) {
15373
+ const lines = [];
15374
+ lines.push("Standards Configuration Applied");
15375
+ lines.push("\u2500".repeat(40));
15376
+ lines.push(`Files modified: ${report.modifiedFiles.length}`);
15377
+ lines.push(`Placeholders replaced: ${report.replacedPlaceholders.length}`);
15378
+ if (report.modifiedFiles.length > 0) {
15379
+ lines.push("");
15380
+ lines.push("Modified files:");
15381
+ for (const file of report.modifiedFiles) {
15382
+ lines.push(` \u2713 ${file}`);
15383
+ }
15384
+ }
15385
+ if (report.unusedPlaceholders.length > 0) {
15386
+ lines.push("");
15387
+ lines.push("Unused placeholders (no matching templates):");
15388
+ for (const p of report.unusedPlaceholders.slice(0, 5)) {
15389
+ lines.push(` - ${p}`);
15390
+ }
15391
+ if (report.unusedPlaceholders.length > 5) {
15392
+ lines.push(` ... and ${report.unusedPlaceholders.length - 5} more`);
15393
+ }
15394
+ }
15395
+ if (report.errors.length > 0) {
15396
+ lines.push("");
15397
+ lines.push("Errors:");
15398
+ for (const error of report.errors) {
15399
+ lines.push(` \u2717 ${error}`);
15400
+ }
15401
+ }
15402
+ return lines.join("\n");
15403
+ }
15404
+
15405
+ // src/lib/standards/scanner.ts
15406
+ init_cjs_shims();
15407
+ var fs7 = __toESM(require("fs/promises"), 1);
15408
+ var path8 = __toESM(require("path"), 1);
15409
+ var SCANNABLE_EXTENSIONS2 = [".md", ".json", ".yaml", ".yml", ".txt"];
15410
+ var PLACEHOLDER_PATTERN = /\{\{([A-Z_]+)\}\}/g;
15411
+ async function getScanableFiles(dir) {
15412
+ const files = [];
15413
+ try {
15414
+ const entries = await fs7.readdir(dir, { withFileTypes: true });
15415
+ for (const entry of entries) {
15416
+ const fullPath = path8.join(dir, entry.name);
15417
+ if (entry.isDirectory()) {
15418
+ const subFiles = await getScanableFiles(fullPath);
15419
+ files.push(...subFiles);
15420
+ } else if (entry.isFile()) {
15421
+ const ext = path8.extname(entry.name).toLowerCase();
15422
+ if (SCANNABLE_EXTENSIONS2.includes(ext)) {
15423
+ files.push(fullPath);
15424
+ }
15425
+ }
15426
+ }
15427
+ } catch {
15428
+ }
15429
+ return files;
15430
+ }
15431
+ function extractPlaceholders2(content) {
15432
+ const matches = content.match(PLACEHOLDER_PATTERN);
15433
+ return matches ? [...new Set(matches)] : [];
15434
+ }
15435
+ async function scanStandardsPlaceholders(claudePath, config) {
15436
+ const standardsDir = path8.join(claudePath, "docs", "standards");
15437
+ const files = await getScanableFiles(standardsDir);
15438
+ const placeholdersByFile = /* @__PURE__ */ new Map();
15439
+ const allPlaceholders = /* @__PURE__ */ new Set();
15440
+ for (const file of files) {
15441
+ try {
15442
+ const content = await fs7.readFile(file, "utf-8");
15443
+ const placeholders = extractPlaceholders2(content);
15444
+ if (placeholders.length > 0) {
15445
+ const relPath = path8.relative(claudePath, file);
15446
+ placeholdersByFile.set(relPath, placeholders);
15447
+ for (const p of placeholders) {
15448
+ allPlaceholders.add(p);
15449
+ }
15450
+ }
15451
+ } catch {
15452
+ }
15453
+ }
15454
+ const configuredPlaceholders = config ? new Set(Object.keys(flattenStandardsConfig(config))) : /* @__PURE__ */ new Set();
15455
+ const unconfigured = /* @__PURE__ */ new Map();
15456
+ for (const placeholder of allPlaceholders) {
15457
+ if (!configuredPlaceholders.has(placeholder)) {
15458
+ const filesWithPlaceholder = [];
15459
+ for (const [file, placeholders] of placeholdersByFile) {
15460
+ if (placeholders.includes(placeholder)) {
15461
+ filesWithPlaceholder.push(file);
15462
+ }
15463
+ }
15464
+ unconfigured.set(placeholder, filesWithPlaceholder);
15465
+ }
15466
+ }
15467
+ return {
15468
+ unconfiguredPlaceholders: Array.from(unconfigured.entries()).map(([placeholder, files2]) => ({
15469
+ placeholder,
15470
+ files: files2
15471
+ })),
15472
+ totalPlaceholders: allPlaceholders.size,
15473
+ configuredPlaceholders: configuredPlaceholders.size
15474
+ };
15475
+ }
15476
+ function formatScanResult(result) {
15477
+ const lines = [];
15478
+ lines.push("Standards Placeholder Scan");
15479
+ lines.push("\u2500".repeat(40));
15480
+ lines.push(`Total placeholders found: ${result.totalPlaceholders}`);
15481
+ lines.push(`Already configured: ${result.configuredPlaceholders}`);
15482
+ lines.push(`Unconfigured: ${result.unconfiguredPlaceholders.length}`);
15483
+ if (result.unconfiguredPlaceholders.length > 0) {
15484
+ lines.push("");
15485
+ lines.push("Unconfigured placeholders:");
15486
+ for (const { placeholder, files } of result.unconfiguredPlaceholders) {
15487
+ lines.push(` ${placeholder}`);
15488
+ for (const file of files.slice(0, 3)) {
15489
+ lines.push(` \u2514\u2500 ${file}`);
15490
+ }
15491
+ if (files.length > 3) {
15492
+ lines.push(` \u2514\u2500 ... and ${files.length - 3} more files`);
15493
+ }
15494
+ }
15495
+ } else {
15496
+ lines.push("");
15497
+ lines.push("\u2713 All placeholders are configured!");
15498
+ }
15499
+ return lines.join("\n");
15500
+ }
15501
+
15502
+ // src/lib/standards/template-sync.ts
15503
+ init_cjs_shims();
15504
+ var fs8 = __toESM(require("fs/promises"), 1);
15505
+ var path9 = __toESM(require("path"), 1);
15506
+ var import_ora4 = __toESM(require("ora"), 1);
15507
+ var STANDARDS_TEMPLATES = [
15508
+ "code-standards.md",
15509
+ "testing-standards.md",
15510
+ "documentation-standards.md",
15511
+ "design-standards.md",
15512
+ "security-standards.md",
15513
+ "performance-standards.md"
15514
+ ];
15515
+ async function fileExists2(filePath) {
15516
+ try {
15517
+ await fs8.access(filePath);
15518
+ return true;
15519
+ } catch {
15520
+ return false;
15521
+ }
15522
+ }
15523
+ async function createBackup(filePath) {
15524
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
15525
+ const backupPath = `${filePath}.backup-${timestamp}`;
15526
+ await fs8.copyFile(filePath, backupPath);
15527
+ return backupPath;
15528
+ }
15529
+ async function hasPlaceholders(filePath) {
15530
+ try {
15531
+ const content = await fs8.readFile(filePath, "utf-8");
15532
+ return content.includes("AUTO-GENERATED: Configured values") || /\{\{[A-Z_]+\}\}/.test(content);
15533
+ } catch {
15534
+ return false;
15535
+ }
15536
+ }
15537
+ async function syncTemplate(templateName, sourcePath, targetPath, options) {
15538
+ const sourceFile = path9.join(sourcePath, templateName);
15539
+ const targetFile = path9.join(targetPath, templateName);
15540
+ if (!await fileExists2(sourceFile)) {
15541
+ return { status: "skipped" };
15542
+ }
15543
+ const targetExists = await fileExists2(targetFile);
15544
+ if (targetExists) {
15545
+ const alreadyHasPlaceholders = await hasPlaceholders(targetFile);
15546
+ if (alreadyHasPlaceholders && !options.overwrite) {
15547
+ return { status: "skipped" };
15548
+ }
15549
+ let backupPath;
15550
+ if (options.backup) {
15551
+ backupPath = await createBackup(targetFile);
15552
+ }
15553
+ await fs8.copyFile(sourceFile, targetFile);
15554
+ return { status: "updated", backup: backupPath };
15555
+ }
15556
+ await fs8.mkdir(path9.dirname(targetFile), { recursive: true });
15557
+ await fs8.copyFile(sourceFile, targetFile);
15558
+ return { status: "created" };
15559
+ }
15560
+ async function syncStandardsTemplates(claudePath, options = {}) {
15561
+ const result = {
15562
+ created: [],
15563
+ updated: [],
15564
+ skipped: [],
15565
+ errors: []
15566
+ };
15567
+ const packagesTemplatesPath = path9.join(getTemplatesPath(), "docs", "standards");
15568
+ const projectTemplatesPath = path9.join(claudePath, "docs", "standards");
15569
+ await fs8.mkdir(projectTemplatesPath, { recursive: true });
15570
+ for (const template of STANDARDS_TEMPLATES) {
15571
+ try {
15572
+ const syncResult = await syncTemplate(
15573
+ template,
15574
+ packagesTemplatesPath,
15575
+ projectTemplatesPath,
15576
+ options
15577
+ );
15578
+ switch (syncResult.status) {
15579
+ case "created":
15580
+ result.created.push(template);
15581
+ break;
15582
+ case "updated":
15583
+ result.updated.push(template);
15584
+ break;
15585
+ case "skipped":
15586
+ result.skipped.push(template);
15587
+ break;
15588
+ }
15589
+ } catch (error) {
15590
+ result.errors.push(`${template}: ${String(error)}`);
15591
+ }
15592
+ }
15593
+ return result;
15594
+ }
15595
+ async function syncStandardsTemplatesWithSpinner(claudePath, options = {}) {
15596
+ const spinner2 = (0, import_ora4.default)("Syncing standards templates...").start();
15597
+ try {
15598
+ const result = await syncStandardsTemplates(claudePath, options);
15599
+ const total = result.created.length + result.updated.length;
15600
+ if (total > 0) {
15601
+ spinner2.succeed(
15602
+ `Synced ${total} template${total !== 1 ? "s" : ""} (${result.created.length} created, ${result.updated.length} updated)`
15603
+ );
15604
+ } else if (result.skipped.length > 0) {
15605
+ spinner2.info("All templates already up to date");
15606
+ } else {
15607
+ spinner2.warn("No templates to sync");
15608
+ }
15609
+ return result;
15610
+ } catch (error) {
15611
+ spinner2.fail("Failed to sync templates");
15612
+ throw error;
15613
+ }
15614
+ }
15615
+ async function checkTemplatesNeedUpdate(claudePath) {
15616
+ const projectTemplatesPath = path9.join(claudePath, "docs", "standards");
15617
+ const missing = [];
15618
+ const outdated = [];
15619
+ for (const template of STANDARDS_TEMPLATES) {
15620
+ const targetFile = path9.join(projectTemplatesPath, template);
15621
+ if (!await fileExists2(targetFile)) {
15622
+ missing.push(template);
15623
+ } else if (!await hasPlaceholders(targetFile)) {
15624
+ outdated.push(template);
15625
+ }
15626
+ }
15627
+ return {
15628
+ needsUpdate: missing.length > 0 || outdated.length > 0,
15629
+ missing,
15630
+ outdated
15631
+ };
15632
+ }
15633
+ function formatSyncResult(result) {
15634
+ const lines = [];
15635
+ lines.push("Template Sync Results");
15636
+ lines.push("\u2500".repeat(40));
15637
+ if (result.created.length > 0) {
15638
+ lines.push(`Created: ${result.created.length}`);
15639
+ for (const f of result.created) {
15640
+ lines.push(` \u2713 ${f}`);
15641
+ }
15642
+ }
15643
+ if (result.updated.length > 0) {
15644
+ lines.push(`Updated: ${result.updated.length}`);
15645
+ for (const f of result.updated) {
15646
+ lines.push(` \u2713 ${f}`);
15647
+ }
15648
+ }
15649
+ if (result.skipped.length > 0) {
15650
+ lines.push(`Skipped (already up to date): ${result.skipped.length}`);
15651
+ }
15652
+ if (result.errors.length > 0) {
15653
+ lines.push(`Errors: ${result.errors.length}`);
15654
+ for (const e of result.errors) {
15655
+ lines.push(` \u2717 ${e}`);
15656
+ }
15657
+ }
15658
+ return lines.join("\n");
15659
+ }
15660
+
15661
+ // src/cli/commands/standards.ts
15662
+ init_fs();
15663
+
15664
+ // src/cli/prompts/standards.ts
15665
+ init_cjs_shims();
15666
+ async function promptStandardsConfig(options) {
15667
+ logger.section("Project Standards", "\u{1F4D0}");
15668
+ logger.info("Configure quality standards for your project");
15669
+ logger.newline();
15670
+ if (options?.category) {
15671
+ const existingConfig = options.defaults ?? DEFAULT_STANDARDS_CONFIG;
15672
+ const categoryConfig = await promptCategoryConfig(options.category, existingConfig);
15673
+ return {
15674
+ ...existingConfig,
15675
+ [options.category]: categoryConfig
15676
+ };
15677
+ }
15678
+ const enableStandards = await confirm({
15679
+ message: "Would you like to configure project standards?",
15680
+ default: true
15681
+ });
15682
+ if (!enableStandards) {
15683
+ return DEFAULT_STANDARDS_CONFIG;
15684
+ }
15685
+ const preset = await promptStandardsPreset();
15686
+ if (preset !== "custom") {
15687
+ const presetConfig = STANDARDS_PRESETS[preset];
15688
+ logger.success(`Using "${presetConfig.name}" preset`);
15689
+ return presetConfig.config;
15690
+ }
15691
+ logger.newline();
15692
+ logger.info("Configure each standards category:");
15693
+ const codeConfig = await promptCodeStandards(options?.defaults?.code);
15694
+ const testingConfig = await promptTestingStandards(options?.defaults?.testing);
15695
+ const documentationConfig = await promptDocumentationStandards(options?.defaults?.documentation);
15696
+ const designConfig = await promptDesignStandards(options?.defaults?.design);
15697
+ const securityConfig = await promptSecurityStandards(options?.defaults?.security);
15698
+ const performanceConfig = await promptPerformanceStandards(options?.defaults?.performance);
15699
+ return {
15700
+ code: codeConfig,
15701
+ testing: testingConfig,
15702
+ documentation: documentationConfig,
15703
+ design: designConfig,
15704
+ security: securityConfig,
15705
+ performance: performanceConfig
15706
+ };
15707
+ }
15708
+ async function promptStandardsPreset() {
15709
+ return select({
15710
+ message: "Choose a standards preset:",
15711
+ choices: Object.entries(STANDARDS_PRESETS).map(([key, preset]) => ({
15712
+ name: `${preset.name} - ${preset.description}`,
15713
+ value: key
15714
+ })),
15715
+ default: "balanced"
15716
+ });
15717
+ }
15718
+ async function promptCategoryConfig(category, existingConfig) {
15719
+ switch (category) {
15720
+ case "code":
15721
+ return promptCodeStandards(existingConfig.code);
15722
+ case "testing":
15723
+ return promptTestingStandards(existingConfig.testing);
15724
+ case "documentation":
15725
+ return promptDocumentationStandards(existingConfig.documentation);
15726
+ case "design":
15727
+ return promptDesignStandards(existingConfig.design);
15728
+ case "security":
15729
+ return promptSecurityStandards(existingConfig.security);
15730
+ case "performance":
15731
+ return promptPerformanceStandards(existingConfig.performance);
15732
+ }
15733
+ }
15734
+ async function promptCodeStandards(defaults) {
15735
+ const def = STANDARDS_DEFINITIONS.code;
15736
+ logger.newline();
15737
+ logger.subtitle(`${def.icon} ${def.name}`);
15738
+ const indentStyle = await select({
15739
+ message: "Indent style:",
15740
+ choices: [
15741
+ { name: "Spaces", value: "space" },
15742
+ { name: "Tabs", value: "tab" }
15743
+ ],
15744
+ default: defaults?.indentStyle ?? DEFAULT_STANDARDS_CONFIG.code.indentStyle
15745
+ });
15746
+ const indentSize = await select({
15747
+ message: "Indent size:",
15748
+ choices: [
15749
+ { name: "2 spaces", value: 2 },
15750
+ { name: "4 spaces", value: 4 }
15751
+ ],
15752
+ default: defaults?.indentSize ?? DEFAULT_STANDARDS_CONFIG.code.indentSize
15753
+ });
15754
+ const maxLineLength = await select({
15755
+ message: "Max line length:",
15756
+ choices: [
15757
+ { name: "80 characters", value: 80 },
15758
+ { name: "100 characters", value: 100 },
15759
+ { name: "120 characters", value: 120 }
15760
+ ],
15761
+ default: defaults?.maxLineLength ?? DEFAULT_STANDARDS_CONFIG.code.maxLineLength
15762
+ });
15763
+ const maxFileLines = await select({
15764
+ message: "Max file lines:",
15765
+ choices: [
15766
+ { name: "300 lines (strict)", value: 300 },
15767
+ { name: "500 lines (standard)", value: 500 },
15768
+ { name: "800 lines (relaxed)", value: 800 }
15769
+ ],
15770
+ default: defaults?.maxFileLines ?? DEFAULT_STANDARDS_CONFIG.code.maxFileLines
15771
+ });
15772
+ const quoteStyle = await select({
15773
+ message: "Quote style:",
15774
+ choices: [
15775
+ { name: "Single quotes", value: "single" },
15776
+ { name: "Double quotes", value: "double" }
15777
+ ],
15778
+ default: defaults?.quoteStyle ?? DEFAULT_STANDARDS_CONFIG.code.quoteStyle
15779
+ });
15780
+ const semicolons = await confirm({
15781
+ message: "Use semicolons?",
15782
+ default: defaults?.semicolons ?? DEFAULT_STANDARDS_CONFIG.code.semicolons
15783
+ });
15784
+ const trailingCommas = await select({
15785
+ message: "Trailing commas:",
15786
+ choices: [
15787
+ { name: "ES5 (recommended)", value: "es5" },
15788
+ { name: "All", value: "all" },
15789
+ { name: "None", value: "none" }
15790
+ ],
15791
+ default: defaults?.trailingCommas ?? DEFAULT_STANDARDS_CONFIG.code.trailingCommas
15792
+ });
15793
+ const allowAny = await confirm({
15794
+ message: 'Allow "any" type in TypeScript?',
15795
+ default: defaults?.allowAny ?? DEFAULT_STANDARDS_CONFIG.code.allowAny
15796
+ });
15797
+ const namedExportsOnly = await confirm({
15798
+ message: "Require named exports only (no default exports)?",
15799
+ default: defaults?.namedExportsOnly ?? DEFAULT_STANDARDS_CONFIG.code.namedExportsOnly
15800
+ });
15801
+ const roroPattern = await confirm({
15802
+ message: "Require RO-RO pattern (Receive Object, Return Object)?",
15803
+ default: defaults?.roroPattern ?? DEFAULT_STANDARDS_CONFIG.code.roroPattern
15804
+ });
15805
+ const jsDocRequired = await confirm({
15806
+ message: "Require JSDoc for all exports?",
15807
+ default: defaults?.jsDocRequired ?? DEFAULT_STANDARDS_CONFIG.code.jsDocRequired
15808
+ });
15809
+ return {
15810
+ indentStyle,
15811
+ indentSize,
15812
+ maxLineLength,
15813
+ maxFileLines,
15814
+ quoteStyle,
15815
+ semicolons,
15816
+ trailingCommas,
15817
+ allowAny,
15818
+ namedExportsOnly,
15819
+ roroPattern,
15820
+ jsDocRequired
15821
+ };
15822
+ }
15823
+ async function promptTestingStandards(defaults) {
15824
+ const def = STANDARDS_DEFINITIONS.testing;
15825
+ logger.newline();
15826
+ logger.subtitle(`${def.icon} ${def.name}`);
15827
+ const coverageTarget = await select({
15828
+ message: "Minimum code coverage:",
15829
+ choices: [
15830
+ { name: "60% (startup)", value: 60 },
15831
+ { name: "70% (relaxed)", value: 70 },
15832
+ { name: "80% (standard)", value: 80 },
15833
+ { name: "90% (strict)", value: 90 },
15834
+ { name: "95% (enterprise)", value: 95 }
15835
+ ],
15836
+ default: defaults?.coverageTarget ?? DEFAULT_STANDARDS_CONFIG.testing.coverageTarget
15837
+ });
15838
+ const tddRequired = await confirm({
15839
+ message: "Require TDD methodology (Red-Green-Refactor)?",
15840
+ default: defaults?.tddRequired ?? DEFAULT_STANDARDS_CONFIG.testing.tddRequired
15841
+ });
15842
+ const testPattern = await select({
15843
+ message: "Test pattern:",
15844
+ choices: [
15845
+ { name: "AAA (Arrange-Act-Assert)", value: "aaa" },
15846
+ { name: "GWT (Given-When-Then)", value: "gwt" }
15847
+ ],
15848
+ default: defaults?.testPattern ?? DEFAULT_STANDARDS_CONFIG.testing.testPattern
15849
+ });
15850
+ const testLocation = await select({
15851
+ message: "Test file location:",
15852
+ choices: [
15853
+ { name: "Separate (test/ folder)", value: "separate" },
15854
+ { name: "Colocated (__tests__ near source)", value: "colocated" }
15855
+ ],
15856
+ default: defaults?.testLocation ?? DEFAULT_STANDARDS_CONFIG.testing.testLocation
15857
+ });
15858
+ const unitTestMaxMs = await select({
15859
+ message: "Max time per unit test:",
15860
+ choices: [
15861
+ { name: "50ms (fast)", value: 50 },
15862
+ { name: "100ms (standard)", value: 100 },
15863
+ { name: "200ms (relaxed)", value: 200 }
15864
+ ],
15865
+ default: defaults?.unitTestMaxMs ?? DEFAULT_STANDARDS_CONFIG.testing.unitTestMaxMs
15866
+ });
15867
+ const integrationTestMaxMs = await select({
15868
+ message: "Max time per integration test:",
15869
+ choices: [
15870
+ { name: "500ms (fast)", value: 500 },
15871
+ { name: "1000ms (standard)", value: 1e3 },
15872
+ { name: "2000ms (relaxed)", value: 2e3 }
15873
+ ],
15874
+ default: defaults?.integrationTestMaxMs ?? DEFAULT_STANDARDS_CONFIG.testing.integrationTestMaxMs
15875
+ });
15876
+ return {
15877
+ coverageTarget,
15878
+ tddRequired,
15879
+ testPattern,
15880
+ testLocation,
15881
+ unitTestMaxMs,
15882
+ integrationTestMaxMs
15883
+ };
15884
+ }
15885
+ async function promptDocumentationStandards(defaults) {
15886
+ const def = STANDARDS_DEFINITIONS.documentation;
15887
+ logger.newline();
15888
+ logger.subtitle(`${def.icon} ${def.name}`);
15889
+ const jsDocLevel = await select({
15890
+ message: "JSDoc detail level:",
15891
+ choices: [
15892
+ { name: "Minimal (brief description)", value: "minimal" },
15893
+ { name: "Standard (description + params + returns)", value: "standard" },
15894
+ { name: "Comprehensive (full docs with examples)", value: "comprehensive" }
15895
+ ],
15896
+ default: defaults?.jsDocLevel ?? DEFAULT_STANDARDS_CONFIG.documentation.jsDocLevel
15897
+ });
15898
+ const requireExamples = await confirm({
15899
+ message: "Require @example in JSDoc?",
15900
+ default: defaults?.requireExamples ?? DEFAULT_STANDARDS_CONFIG.documentation.requireExamples
15901
+ });
15902
+ const changelogFormat = await select({
15903
+ message: "Changelog format:",
15904
+ choices: [
15905
+ { name: "Conventional (auto-generated from commits)", value: "conventional" },
15906
+ { name: "Keep a Changelog (manual, semantic)", value: "keepachangelog" }
15907
+ ],
15908
+ default: defaults?.changelogFormat ?? DEFAULT_STANDARDS_CONFIG.documentation.changelogFormat
15909
+ });
15910
+ const inlineCommentPolicy = await select({
15911
+ message: "Inline comment policy:",
15912
+ choices: [
15913
+ { name: "Why not What (explain reasoning)", value: "why-not-what" },
15914
+ { name: "Minimal (only when necessary)", value: "minimal" },
15915
+ { name: "Extensive (comment thoroughly)", value: "extensive" }
15916
+ ],
15917
+ default: defaults?.inlineCommentPolicy ?? DEFAULT_STANDARDS_CONFIG.documentation.inlineCommentPolicy
15918
+ });
15919
+ return {
15920
+ jsDocLevel,
15921
+ requireExamples,
15922
+ changelogFormat,
15923
+ inlineCommentPolicy
15924
+ };
15925
+ }
15926
+ async function promptDesignStandards(defaults) {
15927
+ const def = STANDARDS_DEFINITIONS.design;
15928
+ logger.newline();
15929
+ logger.subtitle(`${def.icon} ${def.name}`);
15930
+ const cssFramework = await select({
15931
+ message: "CSS framework:",
15932
+ choices: [
15933
+ { name: "Tailwind CSS", value: "tailwind" },
15934
+ { name: "CSS Modules", value: "css-modules" },
15935
+ { name: "Styled Components", value: "styled-components" },
15936
+ { name: "Vanilla CSS", value: "vanilla" }
15937
+ ],
15938
+ default: defaults?.cssFramework ?? DEFAULT_STANDARDS_CONFIG.design.cssFramework
15939
+ });
15940
+ const componentLibrary = await select({
15941
+ message: "Component library:",
15942
+ choices: [
15943
+ { name: "shadcn/ui", value: "shadcn" },
15944
+ { name: "Radix UI", value: "radix" },
15945
+ { name: "Headless UI", value: "headless" },
15946
+ { name: "None", value: "none" }
15947
+ ],
15948
+ default: defaults?.componentLibrary ?? DEFAULT_STANDARDS_CONFIG.design.componentLibrary
15949
+ });
15950
+ const accessibilityLevel = await select({
15951
+ message: "WCAG accessibility level:",
15952
+ choices: [
15953
+ { name: "Level A (minimum)", value: "A" },
15954
+ { name: "Level AA (recommended)", value: "AA" },
15955
+ { name: "Level AAA (highest)", value: "AAA" }
15956
+ ],
15957
+ default: defaults?.accessibilityLevel ?? DEFAULT_STANDARDS_CONFIG.design.accessibilityLevel
15958
+ });
15959
+ const darkModeSupport = await confirm({
15960
+ message: "Support dark mode?",
15961
+ default: defaults?.darkModeSupport ?? DEFAULT_STANDARDS_CONFIG.design.darkModeSupport
15962
+ });
15963
+ return {
15964
+ cssFramework,
15965
+ componentLibrary,
15966
+ accessibilityLevel,
15967
+ darkModeSupport
15968
+ };
15969
+ }
15970
+ async function promptSecurityStandards(defaults) {
15971
+ const def = STANDARDS_DEFINITIONS.security;
15972
+ logger.newline();
15973
+ logger.subtitle(`${def.icon} ${def.name}`);
15974
+ const authPattern = await select({
15975
+ message: "Authentication pattern:",
15976
+ choices: [
15977
+ { name: "JWT (JSON Web Tokens)", value: "jwt" },
15978
+ { name: "Session (server-side)", value: "session" },
15979
+ { name: "OAuth 2.0 / OIDC", value: "oauth" },
15980
+ { name: "None", value: "none" }
15981
+ ],
15982
+ default: defaults?.authPattern ?? DEFAULT_STANDARDS_CONFIG.security.authPattern
15983
+ });
15984
+ const inputValidation = await select({
15985
+ message: "Input validation library:",
15986
+ choices: [
15987
+ { name: "Zod (TypeScript-first)", value: "zod" },
15988
+ { name: "Yup (schema builder)", value: "yup" },
15989
+ { name: "Joi (data validation)", value: "joi" },
15990
+ { name: "Manual (custom)", value: "manual" }
15991
+ ],
15992
+ default: defaults?.inputValidation ?? DEFAULT_STANDARDS_CONFIG.security.inputValidation
15993
+ });
15994
+ const csrfProtection = await confirm({
15995
+ message: "Enable CSRF protection?",
15996
+ default: defaults?.csrfProtection ?? DEFAULT_STANDARDS_CONFIG.security.csrfProtection
15997
+ });
15998
+ const rateLimiting = await confirm({
15999
+ message: "Enable rate limiting?",
16000
+ default: defaults?.rateLimiting ?? DEFAULT_STANDARDS_CONFIG.security.rateLimiting
16001
+ });
16002
+ return {
16003
+ authPattern,
16004
+ inputValidation,
16005
+ csrfProtection,
16006
+ rateLimiting
16007
+ };
16008
+ }
16009
+ async function promptPerformanceStandards(defaults) {
16010
+ const def = STANDARDS_DEFINITIONS.performance;
16011
+ logger.newline();
16012
+ logger.subtitle(`${def.icon} ${def.name}`);
16013
+ const lcpTarget = await select({
16014
+ message: "LCP target (Largest Contentful Paint):",
16015
+ choices: [
16016
+ { name: "1500ms (excellent)", value: 1500 },
16017
+ { name: "2000ms (good)", value: 2e3 },
16018
+ { name: "2500ms (standard)", value: 2500 },
16019
+ { name: "4000ms (needs improvement)", value: 4e3 }
16020
+ ],
16021
+ default: defaults?.lcpTarget ?? DEFAULT_STANDARDS_CONFIG.performance.lcpTarget
16022
+ });
16023
+ const fidTarget = await select({
16024
+ message: "FID target (First Input Delay):",
16025
+ choices: [
16026
+ { name: "50ms (excellent)", value: 50 },
16027
+ { name: "100ms (good)", value: 100 },
16028
+ { name: "200ms (standard)", value: 200 },
16029
+ { name: "300ms (needs improvement)", value: 300 }
16030
+ ],
16031
+ default: defaults?.fidTarget ?? DEFAULT_STANDARDS_CONFIG.performance.fidTarget
16032
+ });
16033
+ const clsTarget = await select({
16034
+ message: "CLS target (Cumulative Layout Shift):",
16035
+ choices: [
16036
+ { name: "0.05 (excellent)", value: 0.05 },
16037
+ { name: "0.1 (good)", value: 0.1 },
16038
+ { name: "0.15 (standard)", value: 0.15 },
16039
+ { name: "0.25 (needs improvement)", value: 0.25 }
16040
+ ],
16041
+ default: defaults?.clsTarget ?? DEFAULT_STANDARDS_CONFIG.performance.clsTarget
16042
+ });
16043
+ const bundleSizeTargetKb = await select({
16044
+ message: "Bundle size target (KB):",
16045
+ choices: [
16046
+ { name: "100KB (strict)", value: 100 },
16047
+ { name: "150KB (good)", value: 150 },
16048
+ { name: "250KB (standard)", value: 250 },
16049
+ { name: "500KB (relaxed)", value: 500 }
16050
+ ],
16051
+ default: defaults?.bundleSizeTargetKb ?? DEFAULT_STANDARDS_CONFIG.performance.bundleSizeTargetKb
16052
+ });
16053
+ const apiResponseTargetMs = await select({
16054
+ message: "API response time target (ms):",
16055
+ choices: [
16056
+ { name: "100ms (fast)", value: 100 },
16057
+ { name: "200ms (good)", value: 200 },
16058
+ { name: "300ms (standard)", value: 300 },
16059
+ { name: "500ms (relaxed)", value: 500 }
16060
+ ],
16061
+ default: defaults?.apiResponseTargetMs ?? DEFAULT_STANDARDS_CONFIG.performance.apiResponseTargetMs
16062
+ });
16063
+ return {
16064
+ lcpTarget,
16065
+ fidTarget,
16066
+ clsTarget,
16067
+ bundleSizeTargetKb,
16068
+ apiResponseTargetMs
16069
+ };
16070
+ }
16071
+ function showStandardsSummary(config) {
16072
+ logger.newline();
16073
+ logger.subtitle("Standards Summary");
16074
+ logger.item(
16075
+ `Code: ${config.code.indentSize} ${config.code.indentStyle}s, ${config.code.quoteStyle} quotes`
16076
+ );
16077
+ logger.info(
16078
+ colors.muted(
16079
+ ` Max: ${config.code.maxLineLength} chars/line, ${config.code.maxFileLines} lines/file`
16080
+ )
16081
+ );
16082
+ logger.info(
16083
+ colors.muted(
16084
+ ` Rules: ${config.code.allowAny ? "any allowed" : "no any"}, ${config.code.namedExportsOnly ? "named exports" : "default exports"}`
16085
+ )
16086
+ );
16087
+ logger.item(
16088
+ `Testing: ${config.testing.coverageTarget}% coverage, ${config.testing.tddRequired ? "TDD required" : "TDD optional"}`
16089
+ );
16090
+ logger.info(
16091
+ colors.muted(
16092
+ ` Pattern: ${config.testing.testPattern.toUpperCase()}, Location: ${config.testing.testLocation}`
16093
+ )
16094
+ );
16095
+ logger.item(`Documentation: ${config.documentation.jsDocLevel} JSDoc`);
16096
+ logger.info(colors.muted(` Comments: ${config.documentation.inlineCommentPolicy}`));
16097
+ logger.item(`Design: ${config.design.cssFramework}, ${config.design.componentLibrary}`);
16098
+ logger.info(
16099
+ colors.muted(
16100
+ ` A11y: WCAG ${config.design.accessibilityLevel}, ${config.design.darkModeSupport ? "dark mode" : "no dark mode"}`
16101
+ )
16102
+ );
16103
+ logger.item(`Security: ${config.security.authPattern}, ${config.security.inputValidation}`);
16104
+ logger.info(
16105
+ colors.muted(
16106
+ ` ${config.security.csrfProtection ? "CSRF" : "no CSRF"}, ${config.security.rateLimiting ? "rate limiting" : "no rate limiting"}`
16107
+ )
16108
+ );
16109
+ logger.item(
16110
+ `Performance: LCP ${config.performance.lcpTarget}ms, FID ${config.performance.fidTarget}ms`
16111
+ );
16112
+ logger.info(
16113
+ colors.muted(
16114
+ ` Bundle: ${config.performance.bundleSizeTargetKb}KB, API: ${config.performance.apiResponseTargetMs}ms`
16115
+ )
16116
+ );
16117
+ }
16118
+ async function confirmStandardsConfig(config) {
16119
+ showStandardsSummary(config);
16120
+ logger.newline();
16121
+ return confirm({
16122
+ message: "Apply these standards?",
16123
+ default: true
16124
+ });
16125
+ }
16126
+
16127
+ // src/cli/commands/standards.ts
16128
+ function createStandardsCommand() {
16129
+ const cmd = new import_commander8.Command("standards").description("Configure project standards interactively").argument("[path]", "Project path (default: current directory)").option("--scan", "Scan for unconfigured standard placeholders").option(
16130
+ "-c, --category <name>",
16131
+ "Configure specific category (code|testing|documentation|design|security|performance)"
16132
+ ).option("--preview", "Preview changes without applying").option("-y, --yes", "Accept defaults without prompts").option("-v, --verbose", "Detailed output").option("--update-templates", "Update/sync templates from package to project").action(runStandards);
16133
+ return cmd;
16134
+ }
16135
+ async function runStandards(path10, options) {
16136
+ const projectPath = resolvePath(path10 || ".");
16137
+ const claudePath = joinPath(projectPath, ".claude");
16138
+ logger.configure({ verbose: options.verbose, silent: false });
16139
+ logger.title("Project Standards Configuration");
16140
+ try {
16141
+ if (options.scan) {
16142
+ await scanMode2(claudePath, projectPath);
16143
+ return;
16144
+ }
16145
+ if (options.updateTemplates) {
16146
+ await updateTemplatesMode(claudePath, options);
16147
+ return;
16148
+ }
16149
+ if (options.preview) {
16150
+ await previewMode2(claudePath, projectPath);
16151
+ return;
16152
+ }
16153
+ if (options.yes) {
16154
+ await defaultsMode(claudePath, projectPath, options);
16155
+ return;
16156
+ }
16157
+ await interactiveMode2(claudePath, projectPath, options);
16158
+ } catch (error) {
16159
+ logger.error(
16160
+ `Standards configuration failed: ${error instanceof Error ? error.message : error}`
16161
+ );
16162
+ process.exit(1);
16163
+ }
16164
+ }
16165
+ async function scanMode2(claudePath, projectPath) {
16166
+ logger.info(`Scanning ${colors.primary(claudePath)} for standard placeholders...`);
16167
+ logger.newline();
16168
+ const existingConfig = await readConfig(projectPath);
16169
+ const standardsConfig = existingConfig?.extras?.standards;
16170
+ const scanResult = await scanStandardsPlaceholders(claudePath, standardsConfig);
16171
+ logger.info(formatScanResult(scanResult));
16172
+ if (scanResult.unconfiguredPlaceholders.length > 0) {
16173
+ logger.newline();
16174
+ logger.info("Run `claude-config standards` to configure them");
16175
+ }
16176
+ }
16177
+ async function updateTemplatesMode(claudePath, options) {
16178
+ logger.info("Checking for template updates...");
16179
+ logger.newline();
16180
+ const status = await checkTemplatesNeedUpdate(claudePath);
16181
+ if (!status.needsUpdate) {
16182
+ logger.success("All templates are up to date!");
16183
+ return;
16184
+ }
16185
+ if (status.missing.length > 0) {
16186
+ logger.info(`Missing templates (${status.missing.length}):`);
16187
+ for (const template of status.missing) {
16188
+ logger.info(` ${colors.warning("+")} ${template}`);
16189
+ }
16190
+ logger.newline();
16191
+ }
16192
+ if (status.outdated.length > 0) {
16193
+ logger.info(`Outdated templates (${status.outdated.length}):`);
16194
+ for (const template of status.outdated) {
16195
+ logger.info(` ${colors.primary("~")} ${template}`);
16196
+ }
16197
+ logger.newline();
16198
+ }
16199
+ const result = await syncStandardsTemplatesWithSpinner(claudePath, {
16200
+ overwrite: true,
16201
+ backup: true
16202
+ });
16203
+ if (options.verbose) {
16204
+ logger.newline();
16205
+ logger.info(formatSyncResult(result));
16206
+ }
16207
+ logger.newline();
16208
+ logger.success("Templates updated successfully!");
16209
+ logger.info("Run `claude-config standards` to configure the new placeholders");
16210
+ }
16211
+ async function previewMode2(claudePath, projectPath) {
16212
+ const existingConfig = await readConfig(projectPath);
16213
+ if (!existingConfig?.extras?.standards) {
16214
+ logger.warn("No standards configuration found");
16215
+ logger.info("Run `claude-config standards` first to set up configuration");
16216
+ return;
16217
+ }
16218
+ logger.info("Preview of replacements:");
16219
+ logger.newline();
16220
+ const replacements = await previewStandardsReplacements(
16221
+ claudePath,
16222
+ existingConfig.extras.standards
16223
+ );
16224
+ if (replacements.length === 0) {
16225
+ logger.info("No standard placeholders to replace");
16226
+ return;
16227
+ }
16228
+ const byFile = {};
16229
+ for (const r of replacements) {
16230
+ if (!byFile[r.file]) {
16231
+ byFile[r.file] = [];
16232
+ }
16233
+ byFile[r.file].push({ placeholder: r.placeholder, value: r.value });
16234
+ }
16235
+ for (const [file, changes] of Object.entries(byFile)) {
16236
+ logger.subtitle(file);
16237
+ for (const change of changes) {
16238
+ logger.info(` ${change.placeholder} \u2192 ${colors.primary(change.value)}`);
16239
+ }
16240
+ logger.newline();
16241
+ }
16242
+ logger.success(
16243
+ `Total: ${replacements.length} replacements in ${Object.keys(byFile).length} files`
16244
+ );
16245
+ }
16246
+ async function defaultsMode(claudePath, projectPath, options) {
16247
+ const templateStatus = await checkTemplatesNeedUpdate(claudePath);
16248
+ if (templateStatus.needsUpdate) {
16249
+ logger.warn("Some templates are missing or outdated:");
16250
+ if (templateStatus.missing.length > 0) {
16251
+ logger.info(` Missing: ${templateStatus.missing.length} template(s)`);
16252
+ }
16253
+ if (templateStatus.outdated.length > 0) {
16254
+ logger.info(` Outdated: ${templateStatus.outdated.length} template(s)`);
16255
+ }
16256
+ logger.newline();
16257
+ logger.info(
16258
+ `Run ${colors.primary("qazuor-claude-config standards --update-templates")} first to sync templates`
16259
+ );
16260
+ logger.newline();
16261
+ }
16262
+ logger.info("Applying default standards configuration...");
16263
+ logger.newline();
16264
+ const standardsConfig = DEFAULT_STANDARDS_CONFIG;
16265
+ showStandardsSummary(standardsConfig);
16266
+ logger.newline();
16267
+ const report = await replaceStandardsWithSpinner(claudePath, standardsConfig);
16268
+ if (options.verbose) {
16269
+ logger.newline();
16270
+ logger.info(formatStandardsReport(report));
16271
+ }
16272
+ if (report.modifiedFiles.length === 0 && report.unusedPlaceholders.length > 0) {
16273
+ logger.newline();
16274
+ logger.warn("No placeholders were replaced in templates.");
16275
+ logger.info(
16276
+ `Your templates might be outdated. Run ${colors.primary("--update-templates")} to sync them.`
16277
+ );
16278
+ }
16279
+ await saveStandardsConfig(projectPath, standardsConfig);
16280
+ logger.newline();
16281
+ logger.success("Standards configuration complete!");
16282
+ }
16283
+ async function interactiveMode2(claudePath, projectPath, options) {
16284
+ const templateStatus = await checkTemplatesNeedUpdate(claudePath);
16285
+ if (templateStatus.needsUpdate) {
16286
+ logger.warn("Some templates are missing or outdated:");
16287
+ if (templateStatus.missing.length > 0) {
16288
+ logger.info(` Missing: ${templateStatus.missing.length} template(s)`);
16289
+ }
16290
+ if (templateStatus.outdated.length > 0) {
16291
+ logger.info(` Outdated: ${templateStatus.outdated.length} template(s)`);
16292
+ }
16293
+ logger.newline();
16294
+ logger.info(
16295
+ `Run ${colors.primary("qazuor-claude-config standards --update-templates")} first to sync templates`
16296
+ );
16297
+ logger.newline();
16298
+ }
16299
+ const existingConfig = await readConfig(projectPath);
16300
+ const existingStandards = existingConfig?.extras?.standards;
16301
+ const standardsConfig = await promptStandardsConfig({
16302
+ defaults: existingStandards,
16303
+ category: options.category
16304
+ });
16305
+ const confirmed = await confirmStandardsConfig(standardsConfig);
16306
+ if (!confirmed) {
16307
+ logger.warn("Configuration cancelled");
16308
+ return;
16309
+ }
16310
+ const report = await replaceStandardsWithSpinner(claudePath, standardsConfig);
16311
+ if (options.verbose) {
16312
+ logger.newline();
16313
+ logger.info(formatStandardsReport(report));
16314
+ }
16315
+ if (report.modifiedFiles.length === 0 && report.unusedPlaceholders.length > 0) {
16316
+ logger.newline();
16317
+ logger.warn("No placeholders were replaced in templates.");
16318
+ logger.info(
16319
+ `Your templates might be outdated. Run ${colors.primary("--update-templates")} to sync them.`
16320
+ );
16321
+ }
16322
+ await saveStandardsConfig(projectPath, standardsConfig);
16323
+ logger.newline();
16324
+ logger.success("Standards configuration complete!");
16325
+ }
16326
+ async function saveStandardsConfig(projectPath, standardsConfig) {
16327
+ const existingConfig = await readConfig(projectPath);
16328
+ if (existingConfig) {
16329
+ existingConfig.extras = {
16330
+ ...existingConfig.extras,
16331
+ standards: standardsConfig
16332
+ };
16333
+ existingConfig.customizations.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
16334
+ await writeConfig(projectPath, existingConfig);
16335
+ logger.success("Configuration saved to .claude/qazuor-claude-config.json");
16336
+ } else {
16337
+ logger.warn("No existing config found - standards not saved");
16338
+ logger.info("Run `claude-config init` first to initialize the project");
16339
+ }
16340
+ }
16341
+
13301
16342
  // src/cli/index.ts
13302
16343
  var VERSION2 = "0.1.0";
13303
- var program = new import_commander8.Command().name("qazuor-claude-config").description("CLI tool to install and manage Claude Code configurations").version(VERSION2);
16344
+ var program = new import_commander9.Command().name("qazuor-claude-config").description("CLI tool to install and manage Claude Code configurations").version(VERSION2);
13304
16345
  program.addCommand(createInitCommand());
13305
16346
  program.addCommand(createListCommand());
13306
16347
  program.addCommand(createAddCommand());
@@ -13308,6 +16349,7 @@ program.addCommand(createRemoveCommand());
13308
16349
  program.addCommand(createStatusCommand());
13309
16350
  program.addCommand(createUpdateCommand());
13310
16351
  program.addCommand(createConfigureCommand());
16352
+ program.addCommand(createStandardsCommand());
13311
16353
 
13312
16354
  // src/lib/utils/banner.ts
13313
16355
  init_cjs_shims();