@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.js CHANGED
@@ -207,7 +207,7 @@ import { createRequire } from "module";
207
207
 
208
208
  // src/cli/index.ts
209
209
  init_esm_shims();
210
- import { Command as Command8 } from "commander";
210
+ import { Command as Command9 } from "commander";
211
211
 
212
212
  // src/cli/commands/index.ts
213
213
  init_esm_shims();
@@ -251,15 +251,10 @@ var BUNDLES = [
251
251
  moduleDetails: {
252
252
  agents: [
253
253
  {
254
- id: "react-senior-dev",
255
- role: "React Architecture",
254
+ id: "frontend-engineer",
255
+ role: "Frontend Development",
256
256
  responsibilities: ["Component design", "State management", "Performance optimization"]
257
257
  },
258
- {
259
- id: "tanstack-start-engineer",
260
- role: "TanStack Specialist",
261
- responsibilities: ["Router setup", "Query patterns", "SSR configuration"]
262
- },
263
258
  {
264
259
  id: "ux-ui-designer",
265
260
  role: "UI/UX Design",
@@ -267,6 +262,8 @@ var BUNDLES = [
267
262
  }
268
263
  ],
269
264
  skills: [
265
+ { id: "react-patterns", purpose: "React component patterns" },
266
+ { id: "tanstack-start-patterns", purpose: "TanStack Router/Start patterns" },
270
267
  { id: "web-app-testing", purpose: "React Testing Library patterns" },
271
268
  { id: "shadcn-specialist", purpose: "Shadcn UI component usage" },
272
269
  { id: "accessibility-audit", purpose: "WCAG compliance" },
@@ -278,9 +275,10 @@ var BUNDLES = [
278
275
  docs: [{ id: "design-standards", topic: "UI/UX design standards" }]
279
276
  },
280
277
  modules: [
281
- { id: "react-senior-dev", category: "agents" },
282
- { id: "tanstack-start-engineer", category: "agents" },
278
+ { id: "frontend-engineer", category: "agents" },
283
279
  { id: "ux-ui-designer", category: "agents" },
280
+ { id: "react-patterns", category: "skills" },
281
+ { id: "tanstack-start-patterns", category: "skills" },
284
282
  { id: "web-app-testing", category: "skills" },
285
283
  { id: "shadcn-specialist", category: "skills" },
286
284
  { id: "accessibility-audit", category: "skills" },
@@ -316,15 +314,10 @@ var BUNDLES = [
316
314
  moduleDetails: {
317
315
  agents: [
318
316
  {
319
- id: "astro-engineer",
320
- role: "Astro Specialist",
317
+ id: "frontend-engineer",
318
+ role: "Frontend Development",
321
319
  responsibilities: ["Routing", "Islands architecture", "Build optimization"]
322
320
  },
323
- {
324
- id: "react-senior-dev",
325
- role: "React Components",
326
- responsibilities: ["Interactive components", "Client hydration"]
327
- },
328
321
  {
329
322
  id: "seo-ai-specialist",
330
323
  role: "SEO Optimization",
@@ -332,6 +325,8 @@ var BUNDLES = [
332
325
  }
333
326
  ],
334
327
  skills: [
328
+ { id: "astro-patterns", purpose: "Astro-specific patterns" },
329
+ { id: "react-patterns", purpose: "React island components" },
335
330
  { id: "web-app-testing", purpose: "Component testing" },
336
331
  { id: "vercel-specialist", purpose: "Deployment optimization" },
337
332
  { id: "performance-audit", purpose: "Core Web Vitals" }
@@ -340,9 +335,10 @@ var BUNDLES = [
340
335
  docs: []
341
336
  },
342
337
  modules: [
343
- { id: "astro-engineer", category: "agents" },
344
- { id: "react-senior-dev", category: "agents" },
338
+ { id: "frontend-engineer", category: "agents" },
345
339
  { id: "seo-ai-specialist", category: "agents" },
340
+ { id: "astro-patterns", category: "skills" },
341
+ { id: "react-patterns", category: "skills" },
346
342
  { id: "web-app-testing", category: "skills" },
347
343
  { id: "vercel-specialist", category: "skills" },
348
344
  { id: "performance-audit", category: "skills" }
@@ -375,17 +371,12 @@ var BUNDLES = [
375
371
  moduleDetails: {
376
372
  agents: [
377
373
  {
378
- id: "nextjs-engineer",
379
- role: "Next.js Specialist",
380
- responsibilities: ["App Router", "Server Actions", "Caching strategies"]
381
- },
382
- {
383
- id: "react-senior-dev",
384
- role: "React Components",
385
- responsibilities: ["Client components", "State management"]
374
+ id: "frontend-engineer",
375
+ role: "Frontend Development",
376
+ responsibilities: ["App Router", "Server Actions", "Client components"]
386
377
  },
387
378
  {
388
- id: "prisma-engineer",
379
+ id: "database-engineer",
389
380
  role: "Database",
390
381
  responsibilities: ["Schema design", "Migrations", "Query optimization"]
391
382
  },
@@ -396,6 +387,9 @@ var BUNDLES = [
396
387
  }
397
388
  ],
398
389
  skills: [
390
+ { id: "nextjs-patterns", purpose: "Next.js App Router patterns" },
391
+ { id: "react-patterns", purpose: "React component patterns" },
392
+ { id: "prisma-patterns", purpose: "Prisma ORM patterns" },
399
393
  { id: "web-app-testing", purpose: "Next.js testing patterns" },
400
394
  { id: "shadcn-specialist", purpose: "UI components" },
401
395
  { id: "vercel-specialist", purpose: "Deployment" },
@@ -407,10 +401,12 @@ var BUNDLES = [
407
401
  docs: []
408
402
  },
409
403
  modules: [
410
- { id: "nextjs-engineer", category: "agents" },
411
- { id: "react-senior-dev", category: "agents" },
412
- { id: "prisma-engineer", category: "agents" },
404
+ { id: "frontend-engineer", category: "agents" },
405
+ { id: "database-engineer", category: "agents" },
413
406
  { id: "ux-ui-designer", category: "agents" },
407
+ { id: "nextjs-patterns", category: "skills" },
408
+ { id: "react-patterns", category: "skills" },
409
+ { id: "prisma-patterns", category: "skills" },
414
410
  { id: "web-app-testing", category: "skills" },
415
411
  { id: "shadcn-specialist", category: "skills" },
416
412
  { id: "vercel-specialist", category: "skills" },
@@ -446,12 +442,12 @@ var BUNDLES = [
446
442
  moduleDetails: {
447
443
  agents: [
448
444
  {
449
- id: "express-engineer",
450
- role: "Express Specialist",
445
+ id: "api-engineer",
446
+ role: "API Development",
451
447
  responsibilities: ["Route design", "Middleware", "Error handling"]
452
448
  },
453
449
  {
454
- id: "prisma-engineer",
450
+ id: "database-engineer",
455
451
  role: "Database",
456
452
  responsibilities: ["Schema", "Migrations", "Queries"]
457
453
  },
@@ -462,6 +458,8 @@ var BUNDLES = [
462
458
  }
463
459
  ],
464
460
  skills: [
461
+ { id: "express-patterns", purpose: "Express.js patterns" },
462
+ { id: "prisma-patterns", purpose: "Prisma ORM patterns" },
465
463
  { id: "api-app-testing", purpose: "API testing with supertest" },
466
464
  { id: "error-handling-patterns", purpose: "Error middleware" },
467
465
  { id: "security-testing", purpose: "Security best practices" }
@@ -470,9 +468,11 @@ var BUNDLES = [
470
468
  docs: [{ id: "architecture-patterns", topic: "API architecture patterns" }]
471
469
  },
472
470
  modules: [
473
- { id: "express-engineer", category: "agents" },
474
- { id: "prisma-engineer", category: "agents" },
471
+ { id: "api-engineer", category: "agents" },
472
+ { id: "database-engineer", category: "agents" },
475
473
  { id: "node-typescript-engineer", category: "agents" },
474
+ { id: "express-patterns", category: "skills" },
475
+ { id: "prisma-patterns", category: "skills" },
476
476
  { id: "api-app-testing", category: "skills" },
477
477
  { id: "error-handling-patterns", category: "skills" },
478
478
  { id: "security-testing", category: "skills" },
@@ -506,13 +506,13 @@ var BUNDLES = [
506
506
  moduleDetails: {
507
507
  agents: [
508
508
  {
509
- id: "hono-engineer",
510
- role: "Hono Specialist",
509
+ id: "api-engineer",
510
+ role: "API Development",
511
511
  responsibilities: ["Route handlers", "Middleware", "OpenAPI integration"]
512
512
  },
513
513
  {
514
- id: "db-drizzle-engineer",
515
- role: "Drizzle Database",
514
+ id: "database-engineer",
515
+ role: "Database",
516
516
  responsibilities: ["Schema design", "Migrations", "Type-safe queries"]
517
517
  },
518
518
  {
@@ -522,6 +522,8 @@ var BUNDLES = [
522
522
  }
523
523
  ],
524
524
  skills: [
525
+ { id: "hono-patterns", purpose: "Hono framework patterns" },
526
+ { id: "drizzle-patterns", purpose: "Drizzle ORM patterns" },
525
527
  { id: "api-app-testing", purpose: "Hono testing patterns" },
526
528
  { id: "error-handling-patterns", purpose: "Error middleware" },
527
529
  { id: "security-testing", purpose: "Security validation" }
@@ -530,9 +532,11 @@ var BUNDLES = [
530
532
  docs: [{ id: "architecture-patterns", topic: "API architecture patterns" }]
531
533
  },
532
534
  modules: [
533
- { id: "hono-engineer", category: "agents" },
534
- { id: "db-drizzle-engineer", category: "agents" },
535
+ { id: "api-engineer", category: "agents" },
536
+ { id: "database-engineer", category: "agents" },
535
537
  { id: "node-typescript-engineer", category: "agents" },
538
+ { id: "hono-patterns", category: "skills" },
539
+ { id: "drizzle-patterns", category: "skills" },
536
540
  { id: "api-app-testing", category: "skills" },
537
541
  { id: "error-handling-patterns", category: "skills" },
538
542
  { id: "security-testing", category: "skills" },
@@ -763,7 +767,8 @@ var BUNDLES = [
763
767
  tags: ["database", "drizzle", "orm"],
764
768
  alternativeTo: ["prisma-database", "mongoose-database"],
765
769
  modules: [
766
- { id: "db-drizzle-engineer", category: "agents" },
770
+ { id: "database-engineer", category: "agents" },
771
+ { id: "drizzle-patterns", category: "skills" },
767
772
  { id: "json-data-auditor", category: "skills" }
768
773
  ]
769
774
  },
@@ -777,7 +782,8 @@ var BUNDLES = [
777
782
  tags: ["database", "prisma", "orm"],
778
783
  alternativeTo: ["drizzle-database", "mongoose-database"],
779
784
  modules: [
780
- { id: "prisma-engineer", category: "agents" },
785
+ { id: "database-engineer", category: "agents" },
786
+ { id: "prisma-patterns", category: "skills" },
781
787
  { id: "json-data-auditor", category: "skills" }
782
788
  ]
783
789
  },
@@ -791,7 +797,8 @@ var BUNDLES = [
791
797
  tags: ["database", "mongodb", "mongoose", "nosql"],
792
798
  alternativeTo: ["drizzle-database", "prisma-database"],
793
799
  modules: [
794
- { id: "mongoose-engineer", category: "agents" },
800
+ { id: "database-engineer", category: "agents" },
801
+ { id: "mongoose-patterns", category: "skills" },
795
802
  { id: "json-data-auditor", category: "skills" }
796
803
  ]
797
804
  },
@@ -808,7 +815,8 @@ var BUNDLES = [
808
815
  tags: ["api", "hono", "backend"],
809
816
  alternativeTo: ["express-api", "fastify-api", "nestjs-api"],
810
817
  modules: [
811
- { id: "hono-engineer", category: "agents" },
818
+ { id: "api-engineer", category: "agents" },
819
+ { id: "hono-patterns", category: "skills" },
812
820
  { id: "api-app-testing", category: "skills" },
813
821
  { id: "error-handling-patterns", category: "skills" }
814
822
  ]
@@ -823,7 +831,8 @@ var BUNDLES = [
823
831
  tags: ["api", "express", "backend"],
824
832
  alternativeTo: ["hono-api", "fastify-api", "nestjs-api"],
825
833
  modules: [
826
- { id: "express-engineer", category: "agents" },
834
+ { id: "api-engineer", category: "agents" },
835
+ { id: "express-patterns", category: "skills" },
827
836
  { id: "api-app-testing", category: "skills" },
828
837
  { id: "error-handling-patterns", category: "skills" }
829
838
  ]
@@ -838,7 +847,8 @@ var BUNDLES = [
838
847
  tags: ["api", "fastify", "backend", "performance"],
839
848
  alternativeTo: ["hono-api", "express-api", "nestjs-api"],
840
849
  modules: [
841
- { id: "fastify-engineer", category: "agents" },
850
+ { id: "api-engineer", category: "agents" },
851
+ { id: "fastify-patterns", category: "skills" },
842
852
  { id: "api-app-testing", category: "skills" },
843
853
  { id: "error-handling-patterns", category: "skills" }
844
854
  ]
@@ -853,7 +863,8 @@ var BUNDLES = [
853
863
  tags: ["api", "nestjs", "backend", "enterprise"],
854
864
  alternativeTo: ["hono-api", "express-api", "fastify-api"],
855
865
  modules: [
856
- { id: "nestjs-engineer", category: "agents" },
866
+ { id: "api-engineer", category: "agents" },
867
+ { id: "nestjs-patterns", category: "skills" },
857
868
  { id: "api-app-testing", category: "skills" },
858
869
  { id: "error-handling-patterns", category: "skills" }
859
870
  ]
@@ -870,8 +881,9 @@ var BUNDLES = [
870
881
  techStack: ["React", "Shadcn UI", "Tailwind CSS", "Radix UI"],
871
882
  tags: ["react", "ui", "components"],
872
883
  modules: [
873
- { id: "react-senior-dev", category: "agents" },
884
+ { id: "frontend-engineer", category: "agents" },
874
885
  { id: "ux-ui-designer", category: "agents" },
886
+ { id: "react-patterns", category: "skills" },
875
887
  { id: "shadcn-specialist", category: "skills" },
876
888
  { id: "brand-guidelines", category: "skills" },
877
889
  { id: "accessibility-audit", category: "skills" }
@@ -886,7 +898,8 @@ var BUNDLES = [
886
898
  techStack: ["React Hook Form", "Zod", "React", "TypeScript"],
887
899
  tags: ["react", "forms", "validation"],
888
900
  modules: [
889
- { id: "react-senior-dev", category: "agents" },
901
+ { id: "frontend-engineer", category: "agents" },
902
+ { id: "react-patterns", category: "skills" },
890
903
  { id: "react-hook-form-patterns", category: "skills" },
891
904
  { id: "shadcn-specialist", category: "skills" }
892
905
  ]
@@ -901,7 +914,8 @@ var BUNDLES = [
901
914
  tags: ["react", "state", "zustand"],
902
915
  alternativeTo: ["react-state-redux"],
903
916
  modules: [
904
- { id: "react-senior-dev", category: "agents" },
917
+ { id: "frontend-engineer", category: "agents" },
918
+ { id: "react-patterns", category: "skills" },
905
919
  { id: "zustand-patterns", category: "skills" },
906
920
  { id: "tanstack-query-patterns", category: "skills" }
907
921
  ]
@@ -916,7 +930,8 @@ var BUNDLES = [
916
930
  tags: ["react", "state", "redux"],
917
931
  alternativeTo: ["react-state-zustand"],
918
932
  modules: [
919
- { id: "react-senior-dev", category: "agents" },
933
+ { id: "frontend-engineer", category: "agents" },
934
+ { id: "react-patterns", category: "skills" },
920
935
  { id: "redux-toolkit-patterns", category: "skills" },
921
936
  { id: "tanstack-query-patterns", category: "skills", optional: true }
922
937
  ]
@@ -930,7 +945,8 @@ var BUNDLES = [
930
945
  techStack: ["NextAuth.js", "Auth.js", "Next.js", "Prisma"],
931
946
  tags: ["nextjs", "auth", "oauth"],
932
947
  modules: [
933
- { id: "nextjs-engineer", category: "agents" },
948
+ { id: "frontend-engineer", category: "agents" },
949
+ { id: "nextjs-patterns", category: "skills" },
934
950
  { id: "nextauth-patterns", category: "skills" },
935
951
  { id: "security-testing", category: "skills" }
936
952
  ]
@@ -944,8 +960,9 @@ var BUNDLES = [
944
960
  techStack: ["next-intl", "Next.js", "React", "TypeScript"],
945
961
  tags: ["nextjs", "i18n", "internationalization"],
946
962
  modules: [
947
- { id: "nextjs-engineer", category: "agents" },
963
+ { id: "frontend-engineer", category: "agents" },
948
964
  { id: "i18n-specialist", category: "agents", optional: true },
965
+ { id: "nextjs-patterns", category: "skills" },
949
966
  { id: "i18n-patterns", category: "skills" }
950
967
  ]
951
968
  },
@@ -1065,7 +1082,7 @@ var BUNDLES = [
1065
1082
  }
1066
1083
  ],
1067
1084
  skills: [
1068
- { id: "documentation-writer", purpose: "Documentation best practices" },
1085
+ { id: "markdown-formatter", purpose: "Markdown formatting" },
1069
1086
  { id: "mermaid-diagram-specialist", purpose: "Diagram creation" }
1070
1087
  ],
1071
1088
  commands: [
@@ -1080,7 +1097,7 @@ var BUNDLES = [
1080
1097
  },
1081
1098
  modules: [
1082
1099
  { id: "tech-writer", category: "agents" },
1083
- { id: "documentation-writer", category: "skills" },
1100
+ { id: "markdown-formatter", category: "skills" },
1084
1101
  { id: "mermaid-diagram-specialist", category: "skills" },
1085
1102
  { id: "update-docs", category: "commands" },
1086
1103
  { id: "markdown-format", category: "commands" },
@@ -2571,7 +2588,7 @@ init_esm_shims();
2571
2588
  // src/lib/config/reader.ts
2572
2589
  init_esm_shims();
2573
2590
  init_fs();
2574
- var CONFIG_FILE = "config.json";
2591
+ var CONFIG_FILE = "qazuor-claude-config.json";
2575
2592
  var CLAUDE_DIR = ".claude";
2576
2593
  async function readConfig(projectPath) {
2577
2594
  const configPath = joinPath(projectPath, CLAUDE_DIR, CONFIG_FILE);
@@ -2593,7 +2610,7 @@ async function hasClaudeDir(projectPath) {
2593
2610
  // src/lib/config/writer.ts
2594
2611
  init_esm_shims();
2595
2612
  init_fs();
2596
- var CONFIG_FILE2 = "config.json";
2613
+ var CONFIG_FILE2 = "qazuor-claude-config.json";
2597
2614
  var CLAUDE_DIR2 = ".claude";
2598
2615
  async function writeConfig(projectPath, config, options) {
2599
2616
  const claudePath = joinPath(projectPath, CLAUDE_DIR2);
@@ -3235,21 +3252,333 @@ init_esm_shims();
3235
3252
  // src/lib/git-hooks/husky-installer.ts
3236
3253
  init_esm_shims();
3237
3254
  init_fs();
3238
- function generateCommitMsgHook() {
3255
+
3256
+ // src/lib/git-hooks/precommit-generator.ts
3257
+ init_esm_shims();
3258
+ function generatePreCommitScript(config) {
3259
+ if (!config.enabled) {
3260
+ return generateDisabledScript();
3261
+ }
3262
+ const sections = [];
3263
+ sections.push(generateHeader());
3264
+ if (config.showTiming) {
3265
+ sections.push(generateTimingSetup());
3266
+ }
3267
+ sections.push(generateErrorHandling(config.continueOnFailure));
3268
+ if (config.lint.enabled) {
3269
+ sections.push(generateLintSection(config));
3270
+ }
3271
+ if (config.typecheck.enabled) {
3272
+ sections.push(generateTypecheckSection(config));
3273
+ }
3274
+ if (config.formatCheck.enabled) {
3275
+ sections.push(generateFormatCheckSection(config));
3276
+ }
3277
+ if (config.tests.enabled && config.tests.mode !== "none") {
3278
+ sections.push(generateTestSection(config));
3279
+ }
3280
+ const sortedCustom = [...config.customCommands].sort(
3281
+ (a, b) => (a.order ?? 100) - (b.order ?? 100)
3282
+ );
3283
+ for (const cmd of sortedCustom) {
3284
+ sections.push(generateCustomCommandSection(cmd, config));
3285
+ }
3286
+ sections.push(generateFooter(config));
3287
+ return sections.join("\n\n");
3288
+ }
3289
+ function generateDisabledScript() {
3239
3290
  return `#!/usr/bin/env sh
3240
3291
  . "$(dirname -- "$0")/_/husky.sh"
3241
3292
 
3242
- npx --no -- commitlint --edit "\${1}"
3243
- `;
3293
+ # Pre-commit hook disabled
3294
+ exit 0`;
3244
3295
  }
3245
- function generatePreCommitHook(lintCommand) {
3246
- const command = lintCommand || "pnpm lint-staged";
3296
+ function generateHeader() {
3247
3297
  return `#!/usr/bin/env sh
3248
3298
  . "$(dirname -- "$0")/_/husky.sh"
3249
3299
 
3250
- ${command}
3300
+ # Pre-commit hook - Generated by @qazuor/claude-code-config
3301
+ # Bypass with: git commit --no-verify -m "message"
3302
+
3303
+ echo "\u{1F50D} Running pre-commit checks..."`;
3304
+ }
3305
+ function generateTimingSetup() {
3306
+ return `# Timing setup
3307
+ START_TIME=$(date +%s)
3308
+ step_start() { STEP_START=$(date +%s); }
3309
+ step_end() {
3310
+ STEP_END=$(date +%s)
3311
+ ELAPSED_TIME=$((STEP_END - STEP_START))
3312
+ echo " \u23F1\uFE0F Completed in \${ELAPSED_TIME}s"
3313
+ }`;
3314
+ }
3315
+ function generateErrorHandling(continueOnFailure) {
3316
+ if (continueOnFailure) {
3317
+ return `# Error tracking (continue on failure mode)
3318
+ ERRORS=0
3319
+ track_error() {
3320
+ ERRORS=$((ERRORS + 1))
3321
+ }`;
3322
+ }
3323
+ return `# Fail fast mode - exit on first error
3324
+ set -e`;
3325
+ }
3326
+ function generateLintSection(config) {
3327
+ const lint = config.lint;
3328
+ let command;
3329
+ if (lint.command) {
3330
+ command = lint.command;
3331
+ } else if (lint.stagedOnly) {
3332
+ command = getLintStagedCommand(lint.tool);
3333
+ } else {
3334
+ command = getLintCommand(lint.tool);
3335
+ }
3336
+ const lines = [];
3337
+ lines.push("# Linting");
3338
+ lines.push('echo ""');
3339
+ lines.push('echo "\u{1F4DD} Linting..."');
3340
+ if (config.showTiming) {
3341
+ lines.push("step_start");
3342
+ }
3343
+ if (lint.allowFailure) {
3344
+ lines.push(`if ${command}; then`);
3345
+ lines.push(' echo " \u2705 Lint passed"');
3346
+ lines.push("else");
3347
+ lines.push(' echo " \u26A0\uFE0F Lint warnings (non-blocking)"');
3348
+ lines.push("fi");
3349
+ } else if (config.continueOnFailure) {
3350
+ lines.push(`if ${command}; then`);
3351
+ lines.push(' echo " \u2705 Lint passed"');
3352
+ lines.push("else");
3353
+ lines.push(' echo " \u274C Lint failed"');
3354
+ lines.push(" track_error");
3355
+ lines.push("fi");
3356
+ } else {
3357
+ lines.push(`${command} || { echo " \u274C Lint failed"; exit 1; }`);
3358
+ lines.push('echo " \u2705 Lint passed"');
3359
+ }
3360
+ if (config.showTiming) {
3361
+ lines.push("step_end");
3362
+ }
3363
+ return lines.join("\n");
3364
+ }
3365
+ function getLintStagedCommand(tool) {
3366
+ switch (tool) {
3367
+ case "biome":
3368
+ return "pnpm biome check --staged --no-errors-on-unmatched";
3369
+ case "eslint":
3370
+ return "pnpm lint-staged";
3371
+ default:
3372
+ return "pnpm lint-staged";
3373
+ }
3374
+ }
3375
+ function getLintCommand(tool) {
3376
+ switch (tool) {
3377
+ case "biome":
3378
+ return "pnpm biome check .";
3379
+ case "eslint":
3380
+ return "pnpm eslint .";
3381
+ default:
3382
+ return "pnpm lint";
3383
+ }
3384
+ }
3385
+ function generateTypecheckSection(config) {
3386
+ const typecheck = config.typecheck;
3387
+ const command = typecheck.command ?? "pnpm typecheck";
3388
+ const lines = [];
3389
+ lines.push("# Type checking");
3390
+ lines.push('echo ""');
3391
+ lines.push('echo "\u{1F537} Type checking..."');
3392
+ if (config.showTiming) {
3393
+ lines.push("step_start");
3394
+ }
3395
+ if (typecheck.allowFailure) {
3396
+ lines.push(`if ${command}; then`);
3397
+ lines.push(' echo " \u2705 Types OK"');
3398
+ lines.push("else");
3399
+ lines.push(' echo " \u26A0\uFE0F Type warnings (non-blocking)"');
3400
+ lines.push("fi");
3401
+ } else if (config.continueOnFailure) {
3402
+ lines.push(`if ${command}; then`);
3403
+ lines.push(' echo " \u2705 Types OK"');
3404
+ lines.push("else");
3405
+ lines.push(' echo " \u274C Type check failed"');
3406
+ lines.push(" track_error");
3407
+ lines.push("fi");
3408
+ } else {
3409
+ lines.push(`${command} || { echo " \u274C Type check failed"; exit 1; }`);
3410
+ lines.push('echo " \u2705 Types OK"');
3411
+ }
3412
+ if (config.showTiming) {
3413
+ lines.push("step_end");
3414
+ }
3415
+ return lines.join("\n");
3416
+ }
3417
+ function generateFormatCheckSection(config) {
3418
+ const format = config.formatCheck;
3419
+ let command;
3420
+ if (format.command) {
3421
+ command = format.command;
3422
+ } else {
3423
+ command = getFormatCheckCommand(format.tool);
3424
+ }
3425
+ const lines = [];
3426
+ lines.push("# Format check");
3427
+ lines.push('echo ""');
3428
+ lines.push('echo "\u2728 Format check..."');
3429
+ if (config.showTiming) {
3430
+ lines.push("step_start");
3431
+ }
3432
+ if (format.allowFailure) {
3433
+ lines.push(`if ${command}; then`);
3434
+ lines.push(' echo " \u2705 Format OK"');
3435
+ lines.push("else");
3436
+ lines.push(' echo " \u26A0\uFE0F Format warnings (non-blocking)"');
3437
+ lines.push("fi");
3438
+ } else if (config.continueOnFailure) {
3439
+ lines.push(`if ${command}; then`);
3440
+ lines.push(' echo " \u2705 Format OK"');
3441
+ lines.push("else");
3442
+ lines.push(' echo " \u274C Format check failed"');
3443
+ lines.push(" track_error");
3444
+ lines.push("fi");
3445
+ } else {
3446
+ lines.push(`${command} || { echo " \u274C Format check failed"; exit 1; }`);
3447
+ lines.push('echo " \u2705 Format OK"');
3448
+ }
3449
+ if (config.showTiming) {
3450
+ lines.push("step_end");
3451
+ }
3452
+ return lines.join("\n");
3453
+ }
3454
+ function getFormatCheckCommand(tool) {
3455
+ switch (tool) {
3456
+ case "biome":
3457
+ return "pnpm biome format --check .";
3458
+ case "prettier":
3459
+ return "pnpm prettier --check .";
3460
+ default:
3461
+ return "pnpm format:check";
3462
+ }
3463
+ }
3464
+ function generateTestSection(config) {
3465
+ const tests = config.tests;
3466
+ let command;
3467
+ if (tests.command) {
3468
+ command = tests.command;
3469
+ } else if (tests.mode === "affected") {
3470
+ command = "pnpm vitest related --run";
3471
+ } else {
3472
+ command = "pnpm test";
3473
+ }
3474
+ if (tests.coverageThreshold > 0) {
3475
+ command = `${command} --coverage --coverage.thresholds.lines=${tests.coverageThreshold}`;
3476
+ }
3477
+ const lines = [];
3478
+ lines.push("# Tests");
3479
+ lines.push('echo ""');
3480
+ lines.push(`echo "\u{1F9EA} Running ${tests.mode === "affected" ? "affected" : "all"} tests..."`);
3481
+ if (config.showTiming) {
3482
+ lines.push("step_start");
3483
+ }
3484
+ if (tests.allowFailure) {
3485
+ lines.push(`if ${command}; then`);
3486
+ lines.push(' echo " \u2705 Tests passed"');
3487
+ lines.push("else");
3488
+ lines.push(' echo " \u26A0\uFE0F Test warnings (non-blocking)"');
3489
+ lines.push("fi");
3490
+ } else if (config.continueOnFailure) {
3491
+ lines.push(`if ${command}; then`);
3492
+ lines.push(' echo " \u2705 Tests passed"');
3493
+ lines.push("else");
3494
+ lines.push(' echo " \u274C Tests failed"');
3495
+ lines.push(" track_error");
3496
+ lines.push("fi");
3497
+ } else {
3498
+ lines.push(`${command} || { echo " \u274C Tests failed"; exit 1; }`);
3499
+ lines.push('echo " \u2705 Tests passed"');
3500
+ }
3501
+ if (config.showTiming) {
3502
+ lines.push("step_end");
3503
+ }
3504
+ return lines.join("\n");
3505
+ }
3506
+ function generateCustomCommandSection(cmd, config) {
3507
+ const lines = [];
3508
+ lines.push(`# Custom: ${cmd.name}`);
3509
+ lines.push('echo ""');
3510
+ lines.push(`echo "\u{1F527} ${cmd.name}..."`);
3511
+ if (config.showTiming) {
3512
+ lines.push("step_start");
3513
+ }
3514
+ if (cmd.allowFailure) {
3515
+ lines.push(`if ${cmd.command}; then`);
3516
+ lines.push(` echo " \u2705 ${cmd.name} passed"`);
3517
+ lines.push("else");
3518
+ lines.push(` echo " \u26A0\uFE0F ${cmd.name} warnings (non-blocking)"`);
3519
+ lines.push("fi");
3520
+ } else if (config.continueOnFailure) {
3521
+ lines.push(`if ${cmd.command}; then`);
3522
+ lines.push(` echo " \u2705 ${cmd.name} passed"`);
3523
+ lines.push("else");
3524
+ lines.push(` echo " \u274C ${cmd.name} failed"`);
3525
+ lines.push(" track_error");
3526
+ lines.push("fi");
3527
+ } else {
3528
+ lines.push(`${cmd.command} || { echo " \u274C ${cmd.name} failed"; exit 1; }`);
3529
+ lines.push(`echo " \u2705 ${cmd.name} passed"`);
3530
+ }
3531
+ if (config.showTiming) {
3532
+ lines.push("step_end");
3533
+ }
3534
+ return lines.join("\n");
3535
+ }
3536
+ function generateFooter(config) {
3537
+ const lines = [];
3538
+ lines.push("# Final status");
3539
+ lines.push('echo ""');
3540
+ if (config.showTiming) {
3541
+ lines.push("END_TIME=$(date +%s)");
3542
+ lines.push("TOTAL_TIME=$((END_TIME - START_TIME))");
3543
+ }
3544
+ if (config.continueOnFailure) {
3545
+ lines.push("if [ $ERRORS -gt 0 ]; then");
3546
+ lines.push(' echo "\u274C Pre-commit failed with $ERRORS error(s)"');
3547
+ if (config.showTiming) {
3548
+ lines.push(' echo "\u23F1\uFE0F Total time: ${TOTAL_TIME}s"');
3549
+ }
3550
+ lines.push(" exit 1");
3551
+ lines.push("fi");
3552
+ }
3553
+ if (config.showTiming) {
3554
+ lines.push('echo "\u2728 All checks passed! (${TOTAL_TIME}s)"');
3555
+ } else {
3556
+ lines.push('echo "\u2728 All checks passed!"');
3557
+ }
3558
+ return lines.join("\n");
3559
+ }
3560
+ function generateSimplePreCommitHook(command) {
3561
+ return `#!/usr/bin/env sh
3562
+ . "$(dirname -- "$0")/_/husky.sh"
3563
+
3564
+ ${command}`;
3565
+ }
3566
+
3567
+ // src/lib/git-hooks/husky-installer.ts
3568
+ function generateCommitMsgHook() {
3569
+ return `#!/usr/bin/env sh
3570
+ . "$(dirname -- "$0")/_/husky.sh"
3571
+
3572
+ npx --no -- commitlint --edit "\${1}"
3251
3573
  `;
3252
3574
  }
3575
+ function generatePreCommitHook(lintCommand, preCommitConfig) {
3576
+ if (preCommitConfig) {
3577
+ return generatePreCommitScript(preCommitConfig);
3578
+ }
3579
+ const command = lintCommand || "pnpm lint-staged";
3580
+ return generateSimplePreCommitHook(command);
3581
+ }
3253
3582
  function generatePrePushHook(testCommand) {
3254
3583
  const command = testCommand || "pnpm test";
3255
3584
  return `#!/usr/bin/env sh
@@ -3340,7 +3669,10 @@ async function installHusky(projectPath, config, options) {
3340
3669
  if (config.preCommit) {
3341
3670
  const preCommitPath = joinPath(huskyDir, "pre-commit");
3342
3671
  if (!await pathExists(preCommitPath) || options?.overwrite) {
3343
- await writeFile(preCommitPath, generatePreCommitHook(config.lintCommand));
3672
+ await writeFile(
3673
+ preCommitPath,
3674
+ generatePreCommitHook(config.lintCommand, config.preCommitConfig)
3675
+ );
3344
3676
  await makeExecutable(preCommitPath);
3345
3677
  result.created.push("pre-commit");
3346
3678
  } else {
@@ -3572,8 +3904,8 @@ async function getHooksStatus(projectPath) {
3572
3904
  let executable = false;
3573
3905
  if (exists) {
3574
3906
  try {
3575
- const fs6 = await import("fs/promises");
3576
- const stats = await fs6.stat(filePath);
3907
+ const fs9 = await import("fs/promises");
3908
+ const stats = await fs9.stat(filePath);
3577
3909
  executable = (stats.mode & 73) !== 0;
3578
3910
  } catch {
3579
3911
  }
@@ -5786,11 +6118,13 @@ function processTemplate(template, projectInfo, options) {
5786
6118
  const commands = options?.templateConfig?.commands;
5787
6119
  const targets = options?.templateConfig?.targets;
5788
6120
  const preferences = options?.claudeConfig?.preferences;
6121
+ const standards = options?.claudeConfig?.extras?.standards;
5789
6122
  content = content.replace(/\{\{PROJECT_NAME\}\}/g, projectInfo.name).replace(/\{\{PROJECT_DESCRIPTION\}\}/g, projectInfo.description).replace(/\{\{ORG\}\}/g, projectInfo.org).replace(/\{\{REPO\}\}/g, projectInfo.repo).replace(/\{\{ENTITY_TYPE\}\}/g, projectInfo.entityType).replace(/\{\{ENTITY_TYPE_PLURAL\}\}/g, projectInfo.entityTypePlural).replace(/\{\{LOCATION\}\}/g, projectInfo.location || "");
5790
6123
  const packageManager = preferences?.packageManager || "pnpm";
5791
6124
  content = content.replace(/\{\{PACKAGE_MANAGER\}\}/g, packageManager);
5792
- const coverageTarget = targets && "coverage" in targets ? String(targets.coverage) : "90";
6125
+ const coverageTarget = standards?.testing?.coverageTarget ? String(standards.testing.coverageTarget) : targets && "coverage" in targets ? String(targets.coverage) : "90";
5793
6126
  content = content.replace(/\{\{COVERAGE_TARGET\}\}/g, coverageTarget);
6127
+ content = processStandardsPlaceholders(content, standards, preferences);
5794
6128
  if (projectInfo.domain) {
5795
6129
  content = content.replace(/\{\{#if DOMAIN\}\}/g, "").replace(/\{\{\/if\}\}/g, "").replace(/\{\{DOMAIN\}\}/g, projectInfo.domain);
5796
6130
  } else {
@@ -5887,6 +6221,55 @@ function generateCommandsSection(commands, packageManager) {
5887
6221
  lines.push("```");
5888
6222
  return lines.join("\n");
5889
6223
  }
6224
+ function processStandardsPlaceholders(content, standards, preferences) {
6225
+ let result = content;
6226
+ const primaryLanguage = "TypeScript";
6227
+ result = result.replace(/\{\{PRIMARY_LANGUAGE\}\}/g, primaryLanguage);
6228
+ const maxFileLines = standards?.code?.maxFileLines?.toString() || "500";
6229
+ result = result.replace(/\{\{MAX_FILE_LINES\}\}/g, maxFileLines);
6230
+ const testPattern = standards?.testing?.testPattern === "gwt" ? "GWT (Given-When-Then)" : "AAA (Arrange, Act, Assert)";
6231
+ result = result.replace(/\{\{TEST_PATTERN\}\}/g, testPattern);
6232
+ const responseLanguage = preferences?.responseLanguage === "es" ? "Spanish" : preferences?.responseLanguage === "en" ? "English" : "Spanish";
6233
+ result = result.replace(/\{\{RESPONSE_LANGUAGE\}\}/g, responseLanguage);
6234
+ if (standards?.code?.namedExportsOnly) {
6235
+ result = result.replace(/\{\{#if NAMED_EXPORTS_ONLY\}\}/g, "").replace(/\{\{\/if\}\}/g, "");
6236
+ } else {
6237
+ result = result.replace(/\{\{#if NAMED_EXPORTS_ONLY\}\}[\s\S]*?\{\{\/if\}\}/g, "");
6238
+ }
6239
+ if (standards?.code?.jsDocRequired) {
6240
+ result = result.replace(/\{\{#if JSDOC_REQUIRED\}\}/g, "").replace(/\{\{\/if\}\}/g, "");
6241
+ } else {
6242
+ result = result.replace(/\{\{#if JSDOC_REQUIRED\}\}[\s\S]*?\{\{\/if\}\}/g, "");
6243
+ }
6244
+ if (standards?.code?.roroPattern) {
6245
+ result = result.replace(/\{\{#if RORO_PATTERN\}\}/g, "").replace(/\{\{\/if\}\}/g, "");
6246
+ } else {
6247
+ result = result.replace(/\{\{#if RORO_PATTERN\}\}[\s\S]*?\{\{\/if\}\}/g, "");
6248
+ }
6249
+ if (standards?.testing?.tddRequired) {
6250
+ result = result.replace(/\{\{#if TDD_REQUIRED\}\}/g, "").replace(/\{\{else\}\}[\s\S]*?\{\{\/if\}\}/g, "");
6251
+ } else {
6252
+ result = result.replace(/\{\{#if TDD_REQUIRED\}\}[\s\S]*?\{\{else\}\}/g, "").replace(/\{\{\/if\}\}/g, "");
6253
+ }
6254
+ const testLocation = standards?.testing?.testLocation;
6255
+ if (testLocation) {
6256
+ const testLocationText = testLocation === "colocated" ? "Co-located with source" : "Separate test directory";
6257
+ result = result.replace(/\{\{#if TEST_LOCATION\}\}/g, "").replace(/\{\{\/if\}\}/g, "").replace(/\{\{TEST_LOCATION\}\}/g, testLocationText);
6258
+ } else {
6259
+ result = result.replace(/\{\{#if TEST_LOCATION\}\}[\s\S]*?\{\{\/if\}\}/g, "");
6260
+ }
6261
+ if (preferences?.includeCoAuthor) {
6262
+ result = result.replace(/\{\{#if INCLUDE_CO_AUTHOR\}\}/g, "").replace(/\{\{\/if\}\}/g, "");
6263
+ } else {
6264
+ result = result.replace(/\{\{#if INCLUDE_CO_AUTHOR\}\}[\s\S]*?\{\{\/if\}\}/g, "");
6265
+ }
6266
+ if (preferences?.responseLanguage) {
6267
+ result = result.replace(/\{\{#if RESPONSE_LANGUAGE\}\}/g, "").replace(/\{\{\/if\}\}/g, "");
6268
+ } else {
6269
+ result = result.replace(/\{\{#if RESPONSE_LANGUAGE\}\}[\s\S]*?\{\{\/if\}\}/g, "");
6270
+ }
6271
+ return result;
6272
+ }
5890
6273
  function getMinimalTemplate() {
5891
6274
  return `# CLAUDE.md
5892
6275
 
@@ -6432,6 +6815,242 @@ async function generateScaffoldWithProgress(projectPath, options) {
6432
6815
  );
6433
6816
  }
6434
6817
 
6818
+ // src/lib/scaffold/settings-generator.ts
6819
+ init_esm_shims();
6820
+
6821
+ // src/constants/claude-settings-defaults.ts
6822
+ init_esm_shims();
6823
+ var DEFAULT_CLAUDE_SETTINGS = {
6824
+ model: "sonnet",
6825
+ alwaysThinkingEnabled: false,
6826
+ sandbox: {
6827
+ enabled: false,
6828
+ autoAllowBashIfSandboxed: true
6829
+ },
6830
+ permissions: {
6831
+ allow: [],
6832
+ deny: [],
6833
+ ask: [],
6834
+ defaultMode: "acceptEdits"
6835
+ },
6836
+ attribution: {
6837
+ commit: "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)\n\n Co-Authored-By: Claude <noreply@anthropic.com>",
6838
+ pr: "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)"
6839
+ },
6840
+ cleanupPeriodDays: 30,
6841
+ stopNotification: "beep"
6842
+ };
6843
+ var ATTRIBUTION_NO_COAUTHOR = {
6844
+ commit: "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)",
6845
+ pr: "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)"
6846
+ };
6847
+ var ATTRIBUTION_WITH_COAUTHOR = {
6848
+ commit: "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)\n\n Co-Authored-By: Claude <noreply@anthropic.com>",
6849
+ pr: "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)"
6850
+ };
6851
+ var BEEP_COMMAND = "echo -ne '\\007'";
6852
+ var MODEL_DESCRIPTIONS = {
6853
+ sonnet: "Claude Sonnet - Balanced performance and speed (recommended)",
6854
+ opus: "Claude Opus - Most capable, best for complex tasks",
6855
+ haiku: "Claude Haiku - Fastest, best for simple tasks",
6856
+ default: "Default - Let Claude Code decide based on task"
6857
+ };
6858
+ var PERMISSION_MODE_DESCRIPTIONS = {
6859
+ acceptEdits: "Accept edits automatically, ask for other operations",
6860
+ askAlways: "Ask for confirmation on all operations",
6861
+ viewOnly: "Read-only mode, no modifications allowed"
6862
+ };
6863
+ var STOP_NOTIFICATION_DESCRIPTIONS = {
6864
+ beep: "Play a beep sound when task completes",
6865
+ custom: "Run a custom command when task completes",
6866
+ none: "No notification"
6867
+ };
6868
+ var CLAUDE_SETTINGS_PRESETS = {
6869
+ /** Default preset - balanced settings */
6870
+ default: {
6871
+ ...DEFAULT_CLAUDE_SETTINGS
6872
+ },
6873
+ /** Performance preset - faster model, less confirmations */
6874
+ performance: {
6875
+ ...DEFAULT_CLAUDE_SETTINGS,
6876
+ model: "haiku",
6877
+ permissions: {
6878
+ ...DEFAULT_CLAUDE_SETTINGS.permissions,
6879
+ defaultMode: "acceptEdits"
6880
+ }
6881
+ },
6882
+ /** Quality preset - best model, extended thinking */
6883
+ quality: {
6884
+ ...DEFAULT_CLAUDE_SETTINGS,
6885
+ model: "opus",
6886
+ alwaysThinkingEnabled: true
6887
+ },
6888
+ /** Secure preset - sandbox enabled, more confirmations */
6889
+ secure: {
6890
+ ...DEFAULT_CLAUDE_SETTINGS,
6891
+ sandbox: {
6892
+ enabled: true,
6893
+ autoAllowBashIfSandboxed: true
6894
+ },
6895
+ permissions: {
6896
+ ...DEFAULT_CLAUDE_SETTINGS.permissions,
6897
+ defaultMode: "askAlways"
6898
+ }
6899
+ }
6900
+ };
6901
+ var PRESET_DESCRIPTIONS2 = {
6902
+ default: "Balanced settings for most projects",
6903
+ performance: "Faster responses with Haiku model",
6904
+ quality: "Best quality with Opus model and extended thinking",
6905
+ secure: "Enhanced security with sandbox and confirmations"
6906
+ };
6907
+
6908
+ // src/lib/scaffold/settings-generator.ts
6909
+ init_fs();
6910
+ async function generateSettings(projectPath, options) {
6911
+ const settingsPath = joinPath(projectPath, ".claude", "settings.json");
6912
+ const exists = await pathExists(settingsPath);
6913
+ if (exists && !options?.overwrite) {
6914
+ return {
6915
+ created: false,
6916
+ skipped: true,
6917
+ path: settingsPath
6918
+ };
6919
+ }
6920
+ try {
6921
+ const settings = buildSettingsJson(options);
6922
+ await writeJson(settingsPath, settings, { spaces: 2 });
6923
+ return {
6924
+ created: true,
6925
+ skipped: false,
6926
+ path: settingsPath
6927
+ };
6928
+ } catch (error) {
6929
+ return {
6930
+ created: false,
6931
+ skipped: false,
6932
+ path: settingsPath,
6933
+ error: error instanceof Error ? error.message : String(error)
6934
+ };
6935
+ }
6936
+ }
6937
+ async function generateSettingsWithSpinner(projectPath, options) {
6938
+ return withSpinner("Generating settings.json...", () => generateSettings(projectPath, options), {
6939
+ successText: "Created settings.json"
6940
+ });
6941
+ }
6942
+ async function generateSettingsLocal(projectPath, options) {
6943
+ const settingsPath = joinPath(projectPath, ".claude", "settings.local.json");
6944
+ const exists = await pathExists(settingsPath);
6945
+ if (exists && !options?.overwrite) {
6946
+ return {
6947
+ created: false,
6948
+ skipped: true,
6949
+ path: settingsPath
6950
+ };
6951
+ }
6952
+ try {
6953
+ const settings = buildSettingsLocalJson(options);
6954
+ await writeJson(settingsPath, settings, { spaces: 2 });
6955
+ return {
6956
+ created: true,
6957
+ skipped: false,
6958
+ path: settingsPath
6959
+ };
6960
+ } catch (error) {
6961
+ return {
6962
+ created: false,
6963
+ skipped: false,
6964
+ path: settingsPath,
6965
+ error: error instanceof Error ? error.message : String(error)
6966
+ };
6967
+ }
6968
+ }
6969
+ async function generateSettingsLocalWithSpinner(projectPath, options) {
6970
+ return withSpinner(
6971
+ "Generating settings.local.json...",
6972
+ () => generateSettingsLocal(projectPath, options),
6973
+ {
6974
+ successText: "Created settings.local.json"
6975
+ }
6976
+ );
6977
+ }
6978
+ function buildSettingsJson(options) {
6979
+ const claudeSettings = options?.claudeSettings || DEFAULT_CLAUDE_SETTINGS;
6980
+ const includeCoAuthor = options?.includeCoAuthor ?? true;
6981
+ const settings = {
6982
+ $schema: "https://json.schemastore.org/claude-code-settings.json"
6983
+ };
6984
+ if (claudeSettings.model && claudeSettings.model !== "default") {
6985
+ settings.model = claudeSettings.model;
6986
+ }
6987
+ if (claudeSettings.alwaysThinkingEnabled) {
6988
+ settings.alwaysThinkingEnabled = true;
6989
+ }
6990
+ if (claudeSettings.cleanupPeriodDays !== 30) {
6991
+ settings.cleanupPeriodDays = claudeSettings.cleanupPeriodDays;
6992
+ }
6993
+ settings.attribution = includeCoAuthor ? ATTRIBUTION_WITH_COAUTHOR : ATTRIBUTION_NO_COAUTHOR;
6994
+ settings.permissions = {
6995
+ allow: [],
6996
+ deny: []
6997
+ };
6998
+ if (claudeSettings.sandbox?.enabled) {
6999
+ settings.sandbox = {
7000
+ enabled: true,
7001
+ autoAllowBashIfSandboxed: claudeSettings.sandbox.autoAllowBashIfSandboxed ?? true
7002
+ };
7003
+ }
7004
+ return settings;
7005
+ }
7006
+ function buildSettingsLocalJson(options) {
7007
+ const claudeSettings = options?.claudeSettings || DEFAULT_CLAUDE_SETTINGS;
7008
+ const settings = {};
7009
+ const allow = [...options?.additionalAllow || []];
7010
+ const deny = [...options?.additionalDeny || []];
7011
+ if (claudeSettings.permissions?.allow) {
7012
+ allow.push(...claudeSettings.permissions.allow);
7013
+ }
7014
+ if (claudeSettings.permissions?.deny) {
7015
+ deny.push(...claudeSettings.permissions.deny);
7016
+ }
7017
+ settings.permissions = {
7018
+ allow: [...new Set(allow)],
7019
+ // Deduplicate
7020
+ deny: [...new Set(deny)],
7021
+ ask: claudeSettings.permissions?.ask || [],
7022
+ defaultMode: claudeSettings.permissions?.defaultMode || "acceptEdits"
7023
+ };
7024
+ if (claudeSettings.stopNotification && claudeSettings.stopNotification !== "none") {
7025
+ const command = claudeSettings.stopNotification === "custom" && claudeSettings.customStopCommand ? claudeSettings.customStopCommand : BEEP_COMMAND;
7026
+ settings.hooks = {
7027
+ Stop: [
7028
+ {
7029
+ hooks: [
7030
+ {
7031
+ type: "command",
7032
+ command,
7033
+ timeout: 5
7034
+ }
7035
+ ]
7036
+ }
7037
+ ],
7038
+ SubagentStop: [
7039
+ {
7040
+ hooks: [
7041
+ {
7042
+ type: "command",
7043
+ command,
7044
+ timeout: 5
7045
+ }
7046
+ ]
7047
+ }
7048
+ ]
7049
+ };
7050
+ }
7051
+ return settings;
7052
+ }
7053
+
6435
7054
  // src/lib/templates/config-replacer.ts
6436
7055
  init_esm_shims();
6437
7056
  import * as fs4 from "fs/promises";
@@ -8385,53 +9004,189 @@ async function promptCICDConfig(options) {
8385
9004
  };
8386
9005
  }
8387
9006
 
8388
- // src/cli/prompts/code-style.ts
9007
+ // src/cli/prompts/claude-settings.ts
8389
9008
  init_esm_shims();
8390
- var CODE_STYLE_TOOLS = [
8391
- {
8392
- name: "EditorConfig",
8393
- value: "editorconfig",
8394
- description: "Consistent coding styles across editors",
8395
- checked: true
8396
- },
8397
- {
8398
- name: "Commitlint",
8399
- value: "commitlint",
8400
- description: "Lint commit messages (conventional commits)",
8401
- checked: true
8402
- },
8403
- {
8404
- name: "Biome",
8405
- value: "biome",
8406
- description: "Fast linter and formatter (ESLint + Prettier alternative)",
8407
- checked: false
8408
- },
8409
- {
8410
- name: "Prettier",
8411
- value: "prettier",
8412
- description: "Code formatter (use if not using Biome)",
8413
- checked: false
8414
- }
8415
- ];
8416
- async function promptCodeStyleConfig(options) {
8417
- logger.section("Code Style", "\u{1F3A8}");
8418
- logger.info("Configure code formatting and linting tools");
8419
- logger.newline();
8420
- const enableCodeStyle = await confirm({
8421
- message: "Would you like to install code style configuration files?",
9009
+ async function promptClaudeSettings(options) {
9010
+ logger.section("Claude Code Settings", "\u{1F916}");
9011
+ const wantToConfigure = await confirm({
9012
+ message: "Would you like to configure Claude Code settings?",
8422
9013
  default: true
8423
9014
  });
8424
- if (!enableCodeStyle) {
9015
+ if (!wantToConfigure) {
8425
9016
  return {
8426
- enabled: false,
8427
- editorconfig: false,
8428
- commitlint: false,
8429
- biome: false,
8430
- prettier: false
9017
+ ...DEFAULT_CLAUDE_SETTINGS,
9018
+ attribution: options?.includeCoAuthor ? DEFAULT_CLAUDE_SETTINGS.attribution : {
9019
+ commit: "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)",
9020
+ pr: "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)"
9021
+ }
8431
9022
  };
8432
9023
  }
8433
- const selectedTools = await checkbox({
8434
- message: "Select the tools to configure:",
9024
+ const setupMode = await select({
9025
+ message: "How would you like to configure?",
9026
+ choices: [
9027
+ { name: "Use a preset", value: "preset" },
9028
+ { name: "Customize settings", value: "custom" }
9029
+ ],
9030
+ default: "preset"
9031
+ });
9032
+ if (setupMode === "preset") {
9033
+ return promptClaudeSettingsPreset(options);
9034
+ }
9035
+ return promptClaudeSettingsCustom(options);
9036
+ }
9037
+ async function promptClaudeSettingsPreset(options) {
9038
+ const preset = await select({
9039
+ message: "Choose a settings preset:",
9040
+ choices: Object.entries(PRESET_DESCRIPTIONS2).map(([key, description]) => ({
9041
+ name: `${key.charAt(0).toUpperCase() + key.slice(1)} - ${description}`,
9042
+ value: key
9043
+ })),
9044
+ default: "default"
9045
+ });
9046
+ const config = { ...CLAUDE_SETTINGS_PRESETS[preset] };
9047
+ if (options?.includeCoAuthor === false) {
9048
+ config.attribution = {
9049
+ commit: "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)",
9050
+ pr: "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)"
9051
+ };
9052
+ }
9053
+ return config;
9054
+ }
9055
+ async function promptClaudeSettingsCustom(options) {
9056
+ const defaults = options?.defaults || DEFAULT_CLAUDE_SETTINGS;
9057
+ const model = await select({
9058
+ message: "Default Claude model:",
9059
+ choices: Object.entries(MODEL_DESCRIPTIONS).map(([key, description]) => ({
9060
+ name: description,
9061
+ value: key
9062
+ })),
9063
+ default: defaults.model || "sonnet"
9064
+ });
9065
+ const alwaysThinkingEnabled = await confirm({
9066
+ message: "Enable extended thinking by default? (better for complex tasks)",
9067
+ default: defaults.alwaysThinkingEnabled ?? false
9068
+ });
9069
+ const sandboxEnabled = await confirm({
9070
+ message: "Enable bash sandboxing? (isolates shell commands for security)",
9071
+ default: defaults.sandbox?.enabled ?? false
9072
+ });
9073
+ const permissionMode = await select({
9074
+ message: "Default permission mode:",
9075
+ choices: Object.entries(PERMISSION_MODE_DESCRIPTIONS).map(([key, description]) => ({
9076
+ name: description,
9077
+ value: key
9078
+ })),
9079
+ default: defaults.permissions?.defaultMode || "acceptEdits"
9080
+ });
9081
+ const cleanupPeriodStr = await input({
9082
+ message: "Session cleanup period (days, 0 = delete all immediately):",
9083
+ default: String(defaults.cleanupPeriodDays ?? 30),
9084
+ validate: (value) => {
9085
+ const num = Number.parseInt(value, 10);
9086
+ if (Number.isNaN(num) || num < 0 || num > 365) {
9087
+ return "Please enter a number between 0 and 365";
9088
+ }
9089
+ return true;
9090
+ }
9091
+ });
9092
+ const cleanupPeriodDays = Number.parseInt(cleanupPeriodStr, 10);
9093
+ const stopNotification = await select({
9094
+ message: "Notification when task completes:",
9095
+ choices: Object.entries(STOP_NOTIFICATION_DESCRIPTIONS).map(([key, description]) => ({
9096
+ name: description,
9097
+ value: key
9098
+ })),
9099
+ default: defaults.stopNotification || "beep"
9100
+ });
9101
+ let customStopCommand;
9102
+ if (stopNotification === "custom") {
9103
+ customStopCommand = await input({
9104
+ message: "Custom command to run on task completion:",
9105
+ default: defaults.customStopCommand || ""
9106
+ });
9107
+ }
9108
+ const config = {
9109
+ model,
9110
+ alwaysThinkingEnabled,
9111
+ sandbox: {
9112
+ enabled: sandboxEnabled,
9113
+ autoAllowBashIfSandboxed: true
9114
+ },
9115
+ permissions: {
9116
+ allow: defaults.permissions?.allow || [],
9117
+ deny: defaults.permissions?.deny || [],
9118
+ ask: defaults.permissions?.ask || [],
9119
+ defaultMode: permissionMode
9120
+ },
9121
+ attribution: options?.includeCoAuthor === false ? {
9122
+ commit: "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)",
9123
+ pr: "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)"
9124
+ } : DEFAULT_CLAUDE_SETTINGS.attribution,
9125
+ cleanupPeriodDays,
9126
+ stopNotification,
9127
+ customStopCommand
9128
+ };
9129
+ if (stopNotification === "beep") {
9130
+ config.hooks = {
9131
+ Stop: [{ hooks: [{ type: "command", command: BEEP_COMMAND, timeout: 5 }] }],
9132
+ SubagentStop: [{ hooks: [{ type: "command", command: BEEP_COMMAND, timeout: 5 }] }]
9133
+ };
9134
+ } else if (stopNotification === "custom" && customStopCommand) {
9135
+ config.hooks = {
9136
+ Stop: [{ hooks: [{ type: "command", command: customStopCommand, timeout: 10 }] }],
9137
+ SubagentStop: [{ hooks: [{ type: "command", command: customStopCommand, timeout: 10 }] }]
9138
+ };
9139
+ }
9140
+ return config;
9141
+ }
9142
+
9143
+ // src/cli/prompts/code-style.ts
9144
+ init_esm_shims();
9145
+ var CODE_STYLE_TOOLS = [
9146
+ {
9147
+ name: "EditorConfig",
9148
+ value: "editorconfig",
9149
+ description: "Consistent coding styles across editors",
9150
+ checked: true
9151
+ },
9152
+ {
9153
+ name: "Commitlint",
9154
+ value: "commitlint",
9155
+ description: "Lint commit messages (conventional commits)",
9156
+ checked: true
9157
+ },
9158
+ {
9159
+ name: "Biome",
9160
+ value: "biome",
9161
+ description: "Fast linter and formatter (ESLint + Prettier alternative)",
9162
+ checked: false
9163
+ },
9164
+ {
9165
+ name: "Prettier",
9166
+ value: "prettier",
9167
+ description: "Code formatter (use if not using Biome)",
9168
+ checked: false
9169
+ }
9170
+ ];
9171
+ async function promptCodeStyleConfig(options) {
9172
+ logger.section("Code Style", "\u{1F3A8}");
9173
+ logger.info("Configure code formatting and linting tools");
9174
+ logger.newline();
9175
+ const enableCodeStyle = await confirm({
9176
+ message: "Would you like to install code style configuration files?",
9177
+ default: true
9178
+ });
9179
+ if (!enableCodeStyle) {
9180
+ return {
9181
+ enabled: false,
9182
+ editorconfig: false,
9183
+ commitlint: false,
9184
+ biome: false,
9185
+ prettier: false
9186
+ };
9187
+ }
9188
+ const selectedTools = await checkbox({
9189
+ message: "Select the tools to configure:",
8435
9190
  choices: CODE_STYLE_TOOLS.map((tool) => ({
8436
9191
  name: `${tool.name} - ${tool.description}`,
8437
9192
  value: tool.value,
@@ -9522,7 +10277,95 @@ async function promptSubagentStopHook() {
9522
10277
 
9523
10278
  // src/cli/prompts/item-select.ts
9524
10279
  init_esm_shims();
9525
- async function promptBatchAction(category, totalItems, hasPreset) {
10280
+
10281
+ // src/cli/prompts/mutual-exclusivity.ts
10282
+ init_esm_shims();
10283
+ function getConflictingSelections(module, selectedIds) {
10284
+ if (!module.alternativeTo) {
10285
+ return [];
10286
+ }
10287
+ return module.alternativeTo.filter((altId) => selectedIds.includes(altId));
10288
+ }
10289
+ function createChoicesWithExclusivity(modules, selectedIds, options) {
10290
+ const choices = [];
10291
+ for (const module of modules) {
10292
+ const conflicts = getConflictingSelections(module, selectedIds);
10293
+ const isConflicting = conflicts.length > 0;
10294
+ const isPreselected = options?.preselected?.includes(module.id);
10295
+ const isSelected = selectedIds.includes(module.id);
10296
+ let name = module.name;
10297
+ let description = module.description;
10298
+ let disabled = false;
10299
+ if (isConflicting) {
10300
+ const conflictNames = conflicts.join(", ");
10301
+ disabled = options?.showConflictReason ? `Conflicts with: ${conflictNames}` : `Alternative to ${conflictNames} (already selected)`;
10302
+ name = colors.muted(`${module.name} (incompatible)`);
10303
+ description = `${colors.muted("\u26A0")} ${disabled}`;
10304
+ }
10305
+ choices.push({
10306
+ name,
10307
+ value: module.id,
10308
+ description,
10309
+ disabled: isConflicting ? disabled : false,
10310
+ checked: isSelected || isPreselected && !isConflicting
10311
+ });
10312
+ }
10313
+ return choices;
10314
+ }
10315
+ function validateNoConflicts(selectedIds, allModules) {
10316
+ const conflicts = [];
10317
+ for (const id of selectedIds) {
10318
+ const module = allModules.find((m) => m.id === id);
10319
+ if (!module?.alternativeTo) continue;
10320
+ for (const altId of module.alternativeTo) {
10321
+ if (selectedIds.includes(altId)) {
10322
+ const existingConflict = conflicts.find(
10323
+ (c) => c.selected === id && c.conflictsWith === altId || c.selected === altId && c.conflictsWith === id
10324
+ );
10325
+ if (!existingConflict) {
10326
+ conflicts.push({ selected: id, conflictsWith: altId });
10327
+ }
10328
+ }
10329
+ }
10330
+ }
10331
+ return conflicts;
10332
+ }
10333
+ function groupByExclusivity(modules) {
10334
+ const groups = /* @__PURE__ */ new Map();
10335
+ const processedIds = /* @__PURE__ */ new Set();
10336
+ for (const module of modules) {
10337
+ if (processedIds.has(module.id)) continue;
10338
+ const groupMembers = /* @__PURE__ */ new Set([module.id]);
10339
+ const toProcess = [...module.alternativeTo || []];
10340
+ while (toProcess.length > 0) {
10341
+ const altId = toProcess.pop();
10342
+ if (!altId || groupMembers.has(altId)) continue;
10343
+ const altModule = modules.find((m) => m.id === altId);
10344
+ if (altModule) {
10345
+ groupMembers.add(altId);
10346
+ for (const transitiveAlt of altModule.alternativeTo || []) {
10347
+ if (!groupMembers.has(transitiveAlt)) {
10348
+ toProcess.push(transitiveAlt);
10349
+ }
10350
+ }
10351
+ }
10352
+ }
10353
+ if (groupMembers.size > 1) {
10354
+ const sortedMembers = Array.from(groupMembers).sort();
10355
+ const groupId = `exclusivity-group-${sortedMembers[0]}`;
10356
+ groups.set(groupId, sortedMembers);
10357
+ for (const memberId of groupMembers) {
10358
+ processedIds.add(memberId);
10359
+ }
10360
+ } else {
10361
+ processedIds.add(module.id);
10362
+ }
10363
+ }
10364
+ return groups;
10365
+ }
10366
+
10367
+ // src/cli/prompts/item-select.ts
10368
+ async function promptBatchAction(category, totalItems, options) {
9526
10369
  const choices = [
9527
10370
  {
9528
10371
  name: "Install all (recommended)",
@@ -9540,7 +10383,14 @@ async function promptBatchAction(category, totalItems, hasPreset) {
9540
10383
  description: `Skip all ${category}`
9541
10384
  }
9542
10385
  ];
9543
- if (hasPreset) {
10386
+ if (options?.hasExclusivityGroups && options.exclusivityGroupCount) {
10387
+ choices.splice(1, 0, {
10388
+ name: colors.primary("Smart selection (handles conflicts)"),
10389
+ value: "smart",
10390
+ description: `Intelligent selection with ${options.exclusivityGroupCount} mutually exclusive group(s)`
10391
+ });
10392
+ }
10393
+ if (options?.hasPreset) {
9544
10394
  choices.splice(1, 0, {
9545
10395
  name: "Use preset for this category",
9546
10396
  value: "preset",
@@ -9550,27 +10400,43 @@ async function promptBatchAction(category, totalItems, hasPreset) {
9550
10400
  return select({
9551
10401
  message: `${capitalize(category)} selection (${totalItems} available):`,
9552
10402
  choices,
9553
- default: "all"
10403
+ default: options?.hasExclusivityGroups ? "smart" : "all"
9554
10404
  });
9555
10405
  }
9556
10406
  async function selectItemsFromCategory(category, items, options) {
9557
- const selectedItems = [];
9558
- const skippedItems = [];
9559
10407
  logger.newline();
9560
10408
  logger.subtitle(`${capitalize(category)} Selection`);
9561
10409
  logger.info(`${items.length} ${category} available`);
10410
+ const exclusivityGroups = groupByExclusivity(items);
10411
+ const hasExclusivityGroups = exclusivityGroups.size > 0;
10412
+ if (hasExclusivityGroups) {
10413
+ logger.info(
10414
+ colors.warning(
10415
+ `${exclusivityGroups.size} group(s) of mutually exclusive ${category} detected`
10416
+ )
10417
+ );
10418
+ }
9562
10419
  logger.newline();
9563
- const batchAction = await promptBatchAction(
9564
- category,
9565
- items.length,
9566
- options?.preselected && options.preselected.length > 0
9567
- );
10420
+ const batchAction = await promptBatchAction(category, items.length, {
10421
+ hasPreset: options?.preselected && options.preselected.length > 0,
10422
+ hasExclusivityGroups,
10423
+ exclusivityGroupCount: exclusivityGroups.size
10424
+ });
9568
10425
  if (batchAction === "all") {
9569
- return {
9570
- category,
9571
- selectedItems: items.map((i) => i.id),
9572
- skippedItems: []
9573
- };
10426
+ const conflicts = validateNoConflicts(
10427
+ items.map((i) => i.id),
10428
+ items
10429
+ );
10430
+ if (conflicts.length > 0) {
10431
+ logger.warn("Cannot install all: some modules are mutually exclusive.");
10432
+ logger.info("Switching to smart selection mode...");
10433
+ } else {
10434
+ return {
10435
+ category,
10436
+ selectedItems: items.map((i) => i.id),
10437
+ skippedItems: []
10438
+ };
10439
+ }
9574
10440
  }
9575
10441
  if (batchAction === "none") {
9576
10442
  return {
@@ -9581,24 +10447,106 @@ async function selectItemsFromCategory(category, items, options) {
9581
10447
  }
9582
10448
  if (batchAction === "preset" && options?.preselected) {
9583
10449
  const preselectedSet = new Set(options.preselected);
9584
- return {
10450
+ const preselectedIds = items.filter((i) => preselectedSet.has(i.id)).map((i) => i.id);
10451
+ const conflicts = validateNoConflicts(preselectedIds, items);
10452
+ if (conflicts.length > 0) {
10453
+ logger.warn("Preset contains conflicting modules. Switching to smart selection...");
10454
+ } else {
10455
+ return {
10456
+ category,
10457
+ selectedItems: preselectedIds,
10458
+ skippedItems: items.filter((i) => !preselectedSet.has(i.id)).map((i) => i.id)
10459
+ };
10460
+ }
10461
+ }
10462
+ if (batchAction === "smart" || batchAction === "all" || batchAction === "preset") {
10463
+ return selectItemsWithExclusivity(category, items, options);
10464
+ }
10465
+ return selectItemsOneByOne(category, items, options);
10466
+ }
10467
+ async function selectItemsWithExclusivity(category, items, options) {
10468
+ let selectedIds = options?.preselected?.filter((id) => items.some((i) => i.id === id)) || [];
10469
+ let confirmed = false;
10470
+ while (!confirmed) {
10471
+ const choices = createChoicesWithExclusivity(items, selectedIds, {
10472
+ preselected: options?.preselected,
10473
+ showConflictReason: true
10474
+ });
10475
+ logger.newline();
10476
+ logger.info(
10477
+ colors.muted("Items marked as incompatible are disabled based on your selections.")
10478
+ );
10479
+ logger.info(colors.muted("Uncheck items to enable their alternatives."));
10480
+ logger.newline();
10481
+ const newSelection = await checkbox({
10482
+ message: `Select ${category} (Space to toggle, Enter to confirm):`,
10483
+ choices: choices.map((c) => ({
10484
+ name: c.name,
10485
+ value: c.value,
10486
+ description: c.description,
10487
+ disabled: c.disabled,
10488
+ checked: c.checked
10489
+ })),
10490
+ required: false
10491
+ });
10492
+ const conflicts = validateNoConflicts(newSelection, items);
10493
+ if (conflicts.length > 0) {
10494
+ logger.warn("Your selection contains conflicts:");
10495
+ for (const conflict of conflicts) {
10496
+ const module1 = items.find((i) => i.id === conflict.selected)?.name || conflict.selected;
10497
+ const module2 = items.find((i) => i.id === conflict.conflictsWith)?.name || conflict.conflictsWith;
10498
+ logger.info(` ${colors.error("\u2022")} ${module1} and ${module2} are mutually exclusive`);
10499
+ }
10500
+ logger.info("Please adjust your selection.");
10501
+ selectedIds = newSelection;
10502
+ continue;
10503
+ }
10504
+ selectedIds = newSelection;
10505
+ showCategorySelectionSummary({
9585
10506
  category,
9586
- selectedItems: items.filter((i) => preselectedSet.has(i.id)).map((i) => i.id),
9587
- skippedItems: items.filter((i) => !preselectedSet.has(i.id)).map((i) => i.id)
9588
- };
10507
+ selectedItems: selectedIds,
10508
+ skippedItems: items.filter((i) => !selectedIds.includes(i.id)).map((i) => i.id)
10509
+ });
10510
+ confirmed = await confirm({
10511
+ message: "Is this selection correct?",
10512
+ default: true
10513
+ });
9589
10514
  }
10515
+ return {
10516
+ category,
10517
+ selectedItems: selectedIds,
10518
+ skippedItems: items.filter((i) => !selectedIds.includes(i.id)).map((i) => i.id)
10519
+ };
10520
+ }
10521
+ async function selectItemsOneByOne(category, items, options) {
10522
+ const selectedItems = [];
10523
+ const skippedItems = [];
9590
10524
  const remainingItems = [...items];
9591
10525
  let currentIndex = 0;
9592
10526
  while (currentIndex < remainingItems.length) {
9593
10527
  const item = remainingItems[currentIndex];
9594
10528
  const remaining = remainingItems.length - currentIndex - 1;
9595
10529
  const isPreselected = options?.preselected?.includes(item.id);
10530
+ const conflicts = item.alternativeTo?.filter((altId) => selectedItems.includes(altId)) || [];
10531
+ if (conflicts.length > 0) {
10532
+ const conflictNames = conflicts.map((id) => items.find((i) => i.id === id)?.name || id).join(", ");
10533
+ logger.info(
10534
+ colors.muted(`Skipping ${item.name} (conflicts with selected: ${conflictNames})`)
10535
+ );
10536
+ skippedItems.push(item.id);
10537
+ currentIndex++;
10538
+ continue;
10539
+ }
9596
10540
  const progress = colors.muted(`[${currentIndex + 1}/${remainingItems.length}]`);
9597
10541
  console.log(`
9598
10542
  ${progress} ${colors.bold(item.name)}`);
9599
10543
  if (options?.showDescriptions && item.description) {
9600
10544
  logger.note(item.description);
9601
10545
  }
10546
+ if (item.alternativeTo && item.alternativeTo.length > 0) {
10547
+ const altNames = item.alternativeTo.map((id) => items.find((i) => i.id === id)?.name || id).join(", ");
10548
+ logger.info(colors.warning(`\u26A0 Selecting this will disable: ${altNames}`));
10549
+ }
9602
10550
  const action = await promptItemWithShortcuts(item, {
9603
10551
  defaultInstall: isPreselected,
9604
10552
  remainingCount: remaining
@@ -9615,7 +10563,13 @@ ${progress} ${colors.bold(item.name)}`);
9615
10563
  case "install-rest":
9616
10564
  selectedItems.push(item.id);
9617
10565
  for (let i = currentIndex + 1; i < remainingItems.length; i++) {
9618
- selectedItems.push(remainingItems[i].id);
10566
+ const nextItem = remainingItems[i];
10567
+ const nextConflicts = nextItem.alternativeTo?.filter((altId) => selectedItems.includes(altId)) || [];
10568
+ if (nextConflicts.length === 0) {
10569
+ selectedItems.push(nextItem.id);
10570
+ } else {
10571
+ skippedItems.push(nextItem.id);
10572
+ }
9619
10573
  }
9620
10574
  currentIndex = remainingItems.length;
9621
10575
  break;
@@ -9657,6 +10611,17 @@ async function promptItemWithShortcuts(item, options) {
9657
10611
  default: options.defaultInstall !== false ? "install" : "skip"
9658
10612
  });
9659
10613
  }
10614
+ function showCategorySelectionSummary(result) {
10615
+ const { category, selectedItems, skippedItems } = result;
10616
+ logger.newline();
10617
+ logger.subtitle(`${capitalize(category)} Summary`);
10618
+ if (selectedItems.length > 0) {
10619
+ logger.success(`Selected (${selectedItems.length}): ${selectedItems.join(", ")}`);
10620
+ }
10621
+ if (skippedItems.length > 0) {
10622
+ logger.info(`Skipped (${skippedItems.length}): ${colors.muted(skippedItems.join(", "))}`);
10623
+ }
10624
+ }
9660
10625
  function capitalize(str) {
9661
10626
  return str.charAt(0).toUpperCase() + str.slice(1);
9662
10627
  }
@@ -10420,6 +11385,122 @@ async function confirmProjectInfo(info) {
10420
11385
  });
10421
11386
  }
10422
11387
 
11388
+ // src/cli/prompts/related-skills.ts
11389
+ init_esm_shims();
11390
+ function getAllRelatedSkillIds(agentModules) {
11391
+ const relatedIds = /* @__PURE__ */ new Set();
11392
+ for (const agent of agentModules) {
11393
+ if (agent.relatedSkills) {
11394
+ for (const skillId of agent.relatedSkills) {
11395
+ relatedIds.add(skillId);
11396
+ }
11397
+ }
11398
+ }
11399
+ return Array.from(relatedIds);
11400
+ }
11401
+ function getAgentsWithRelatedSkills(selectedAgentIds, agentModules) {
11402
+ return agentModules.filter(
11403
+ (agent) => selectedAgentIds.includes(agent.id) && agent.relatedSkills && agent.relatedSkills.length > 0
11404
+ );
11405
+ }
11406
+ async function promptRelatedSkillsForAgent(agent, skillModules, options) {
11407
+ if (!agent.relatedSkills || agent.relatedSkills.length === 0) {
11408
+ return [];
11409
+ }
11410
+ const relatedSkillDefs = agent.relatedSkills.map((skillId) => skillModules.find((s) => s.id === skillId)).filter((s) => s !== void 0);
11411
+ if (relatedSkillDefs.length === 0) {
11412
+ return [];
11413
+ }
11414
+ if (relatedSkillDefs.length === 1) {
11415
+ const skill = relatedSkillDefs[0];
11416
+ const action = await select({
11417
+ message: `${colors.primary(agent.name)} has a related skill. Install it?`,
11418
+ choices: [
11419
+ {
11420
+ name: `Install ${skill.name}`,
11421
+ value: "install",
11422
+ description: skill.description
11423
+ },
11424
+ {
11425
+ name: "Skip",
11426
+ value: "skip",
11427
+ description: "Do not install this skill"
11428
+ }
11429
+ ],
11430
+ default: "install"
11431
+ });
11432
+ return action === "install" ? [skill.id] : [];
11433
+ }
11434
+ const choices = relatedSkillDefs.map((skill) => ({
11435
+ name: skill.name,
11436
+ value: skill.id,
11437
+ description: skill.description,
11438
+ checked: options?.preselected?.includes(skill.id) ?? false
11439
+ }));
11440
+ if (options?.allowMultiple !== false) {
11441
+ const selected2 = await checkbox({
11442
+ message: `${colors.primary(agent.name)} supports multiple frameworks. Select which patterns to install:`,
11443
+ choices,
11444
+ required: false
11445
+ });
11446
+ return selected2;
11447
+ }
11448
+ const choicesWithSkip = [
11449
+ ...choices.map((c) => ({ name: c.name, value: c.value, description: c.description })),
11450
+ {
11451
+ name: colors.muted("Skip (none)"),
11452
+ value: "__skip__",
11453
+ description: "Do not install any related skill for this agent"
11454
+ }
11455
+ ];
11456
+ const selected = await select({
11457
+ message: `${colors.primary(agent.name)} supports multiple frameworks. Select which patterns to use:`,
11458
+ choices: choicesWithSkip,
11459
+ default: choicesWithSkip[0]?.value
11460
+ });
11461
+ return selected === "__skip__" ? [] : [selected];
11462
+ }
11463
+ async function promptAllRelatedSkills(selectedAgentIds, registry, options) {
11464
+ const agentsWithRelated = getAgentsWithRelatedSkills(selectedAgentIds, registry.agents);
11465
+ const allRelatedSkillIds = getAllRelatedSkillIds(registry.agents);
11466
+ if (agentsWithRelated.length === 0) {
11467
+ return {
11468
+ relatedSkillsSelected: [],
11469
+ relatedSkillsSkipped: [],
11470
+ allRelatedSkillIds
11471
+ };
11472
+ }
11473
+ logger.newline();
11474
+ logger.subtitle("Framework-Specific Skills");
11475
+ logger.info(
11476
+ "The selected agents support framework-specific patterns. Select which ones to install."
11477
+ );
11478
+ logger.newline();
11479
+ const selectedSkills = [];
11480
+ const skippedSkills = [];
11481
+ for (const agent of agentsWithRelated) {
11482
+ const selected = await promptRelatedSkillsForAgent(agent, registry.skills, {
11483
+ preselected: options?.preselectedSkills,
11484
+ allowMultiple: options?.allowMultiplePerAgent
11485
+ });
11486
+ selectedSkills.push(...selected);
11487
+ const agentRelatedIds = agent.relatedSkills || [];
11488
+ const skipped = agentRelatedIds.filter((id) => !selected.includes(id));
11489
+ skippedSkills.push(...skipped);
11490
+ }
11491
+ const uniqueSelected = [...new Set(selectedSkills)];
11492
+ const uniqueSkipped = [...new Set(skippedSkills)].filter((id) => !uniqueSelected.includes(id));
11493
+ return {
11494
+ relatedSkillsSelected: uniqueSelected,
11495
+ relatedSkillsSkipped: uniqueSkipped,
11496
+ allRelatedSkillIds
11497
+ };
11498
+ }
11499
+ function filterOutRelatedSkills(skillModules, relatedSkillIds) {
11500
+ const relatedSet = new Set(relatedSkillIds);
11501
+ return skillModules.filter((skill) => !relatedSet.has(skill.id));
11502
+ }
11503
+
10423
11504
  // src/cli/prompts/scaffold.ts
10424
11505
  init_esm_shims();
10425
11506
  async function promptScaffoldType(options) {
@@ -10609,27 +11690,27 @@ async function buildConfigContext(projectPath) {
10609
11690
  values: {}
10610
11691
  };
10611
11692
  try {
10612
- const fs6 = await import("fs/promises");
10613
- const path8 = await import("path");
10614
- const pkgPath = path8.join(projectPath, "package.json");
10615
- const pkgContent = await fs6.readFile(pkgPath, "utf-8");
11693
+ const fs9 = await import("fs/promises");
11694
+ const path11 = await import("path");
11695
+ const pkgPath = path11.join(projectPath, "package.json");
11696
+ const pkgContent = await fs9.readFile(pkgPath, "utf-8");
10616
11697
  const pkg = JSON.parse(pkgContent);
10617
11698
  context.scripts = pkg.scripts || {};
10618
11699
  context.dependencies = {
10619
11700
  ...pkg.dependencies || {},
10620
11701
  ...pkg.devDependencies || {}
10621
11702
  };
10622
- context.hasTypeScript = Boolean(context.dependencies.typescript) || await fileExists(path8.join(projectPath, "tsconfig.json"));
10623
- if (await fileExists(path8.join(projectPath, "pnpm-lock.yaml"))) {
11703
+ context.hasTypeScript = Boolean(context.dependencies.typescript) || await fileExists(path11.join(projectPath, "tsconfig.json"));
11704
+ if (await fileExists(path11.join(projectPath, "pnpm-lock.yaml"))) {
10624
11705
  context.packageManager = "pnpm";
10625
- } else if (await fileExists(path8.join(projectPath, "yarn.lock"))) {
11706
+ } else if (await fileExists(path11.join(projectPath, "yarn.lock"))) {
10626
11707
  context.packageManager = "yarn";
10627
- } else if (await fileExists(path8.join(projectPath, "bun.lockb"))) {
11708
+ } else if (await fileExists(path11.join(projectPath, "bun.lockb"))) {
10628
11709
  context.packageManager = "bun";
10629
11710
  } else {
10630
11711
  context.packageManager = "npm";
10631
11712
  }
10632
- context.isGitRepo = await fileExists(path8.join(projectPath, ".git"));
11713
+ context.isGitRepo = await fileExists(path11.join(projectPath, ".git"));
10633
11714
  if (context.isGitRepo) {
10634
11715
  try {
10635
11716
  const { execSync } = await import("child_process");
@@ -10648,8 +11729,8 @@ async function buildConfigContext(projectPath) {
10648
11729
  }
10649
11730
  async function fileExists(filePath) {
10650
11731
  try {
10651
- const fs6 = await import("fs/promises");
10652
- await fs6.access(filePath);
11732
+ const fs9 = await import("fs/promises");
11733
+ await fs9.access(filePath);
10653
11734
  return true;
10654
11735
  } catch {
10655
11736
  return false;
@@ -11181,11 +12262,66 @@ function createBundleSelectionStep() {
11181
12262
  }
11182
12263
  if (mode === "individual" || mode === "both") {
11183
12264
  const preselectedFromBundles = resolveBundles(result.selectedBundles);
11184
- const categories = ["agents", "skills", "commands", "docs"];
11185
12265
  if (ctx.registry) {
11186
12266
  logger.newline();
11187
12267
  logger.subtitle("Individual Module Selection");
11188
- for (const category of categories) {
12268
+ const agentPreselected = mode === "both" ? preselectedFromBundles.agents : [];
12269
+ const agentResult = await selectItemsFromCategory("agents", ctx.registry.agents, {
12270
+ preselected: agentPreselected,
12271
+ showDescriptions: true
12272
+ });
12273
+ if (mode === "both") {
12274
+ result.additionalModules.agents = agentResult.selectedItems.filter(
12275
+ (id) => !agentPreselected.includes(id)
12276
+ );
12277
+ } else {
12278
+ result.additionalModules.agents = agentResult.selectedItems;
12279
+ }
12280
+ const allSelectedAgents = [
12281
+ ...preselectedFromBundles.agents,
12282
+ ...result.additionalModules.agents
12283
+ ];
12284
+ const relatedSkillsResult = await promptAllRelatedSkills(
12285
+ allSelectedAgents,
12286
+ ctx.registry,
12287
+ {
12288
+ preselectedSkills: mode === "both" ? preselectedFromBundles.skills : [],
12289
+ allowMultiplePerAgent: true
12290
+ }
12291
+ );
12292
+ const skillPreselected = mode === "both" ? preselectedFromBundles.skills : [];
12293
+ const independentSkills = filterOutRelatedSkills(
12294
+ ctx.registry.skills,
12295
+ relatedSkillsResult.allRelatedSkillIds
12296
+ );
12297
+ const independentPreselected = skillPreselected.filter(
12298
+ (id) => !relatedSkillsResult.allRelatedSkillIds.includes(id)
12299
+ );
12300
+ let independentSkillsSelected = [];
12301
+ if (independentSkills.length > 0) {
12302
+ logger.newline();
12303
+ logger.info(
12304
+ colors.muted("Now selecting general-purpose skills (not framework-specific):")
12305
+ );
12306
+ const skillResult = await selectItemsFromCategory("skills", independentSkills, {
12307
+ preselected: independentPreselected,
12308
+ showDescriptions: true
12309
+ });
12310
+ independentSkillsSelected = skillResult.selectedItems;
12311
+ }
12312
+ const allSelectedSkills = [
12313
+ ...relatedSkillsResult.relatedSkillsSelected,
12314
+ ...independentSkillsSelected
12315
+ ];
12316
+ if (mode === "both") {
12317
+ result.additionalModules.skills = allSelectedSkills.filter(
12318
+ (id) => !skillPreselected.includes(id)
12319
+ );
12320
+ } else {
12321
+ result.additionalModules.skills = allSelectedSkills;
12322
+ }
12323
+ const otherCategories = ["commands", "docs"];
12324
+ for (const category of otherCategories) {
11189
12325
  const preselected = mode === "both" ? preselectedFromBundles[category] : [];
11190
12326
  const categoryResult = await selectItemsFromCategory(category, ctx.registry[category], {
11191
12327
  preselected,
@@ -11357,6 +12493,28 @@ function createTemplateConfigStep() {
11357
12493
  }
11358
12494
  };
11359
12495
  }
12496
+ function createClaudeSettingsStep() {
12497
+ return {
12498
+ metadata: {
12499
+ id: "claudeSettings",
12500
+ name: "Claude Code Settings",
12501
+ description: "Configure Claude Code model, permissions, and behavior",
12502
+ required: false
12503
+ },
12504
+ computeDefaults: (ctx) => ctx.claudeSettings,
12505
+ execute: async (ctx, defaults) => {
12506
+ const goBack = await promptBackOption(11, "Configure Claude Code settings or go back?");
12507
+ if (goBack) {
12508
+ return createResult(defaults, "back");
12509
+ }
12510
+ const value = await promptClaudeSettings({
12511
+ defaults,
12512
+ includeCoAuthor: ctx.preferences?.includeCoAuthor
12513
+ });
12514
+ return createResult(value, "next");
12515
+ }
12516
+ };
12517
+ }
11360
12518
  function createInitWizardConfig(projectPath, detection, registry) {
11361
12519
  void projectPath;
11362
12520
  void detection;
@@ -11405,6 +12563,10 @@ function createInitWizardConfig(projectPath, detection, registry) {
11405
12563
  {
11406
12564
  id: "templateConfig",
11407
12565
  definition: createTemplateConfigStep()
12566
+ },
12567
+ {
12568
+ id: "claudeSettings",
12569
+ definition: createClaudeSettingsStep()
11408
12570
  }
11409
12571
  ];
11410
12572
  return {
@@ -11683,8 +12845,8 @@ function createInitCommand() {
11683
12845
  ).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);
11684
12846
  return cmd;
11685
12847
  }
11686
- async function runInit(path8, options) {
11687
- const projectPath = resolvePath(path8 || ".");
12848
+ async function runInit(path11, options) {
12849
+ const projectPath = resolvePath(path11 || ".");
11688
12850
  logger.configure({ verbose: options.verbose, silent: false });
11689
12851
  logger.title("@qazuor/claude-code-config");
11690
12852
  logger.info(`Initializing Claude configuration in ${colors.primary(projectPath)}`);
@@ -11693,6 +12855,7 @@ async function runInit(path8, options) {
11693
12855
  showCancelHint();
11694
12856
  }
11695
12857
  try {
12858
+ let existingConfig = null;
11696
12859
  if (await hasExistingClaudeConfig(projectPath)) {
11697
12860
  if (!options.force) {
11698
12861
  const action = await promptExistingProjectAction();
@@ -11703,6 +12866,10 @@ async function runInit(path8, options) {
11703
12866
  if (action !== "overwrite" && action !== "merge") {
11704
12867
  return;
11705
12868
  }
12869
+ existingConfig = await readConfig(projectPath);
12870
+ if (existingConfig) {
12871
+ logger.info("Using existing configuration as defaults");
12872
+ }
11706
12873
  }
11707
12874
  }
11708
12875
  const detection = await detectProject(projectPath);
@@ -11719,12 +12886,12 @@ async function runInit(path8, options) {
11719
12886
  () => loadRegistry(templatesPath),
11720
12887
  { silent: options.dryRun }
11721
12888
  );
11722
- const buildResult = options.yes ? await buildDefaultConfig(projectPath, detection, options) : await buildInteractiveConfig(projectPath, detection, registry, options);
12889
+ const buildResult = options.yes ? await buildDefaultConfig(projectPath, detection, options) : await buildInteractiveConfig(projectPath, detection, registry, options, existingConfig);
11723
12890
  if (!buildResult) {
11724
12891
  logger.warn("Configuration cancelled");
11725
12892
  return;
11726
12893
  }
11727
- const { config, skippedMcpConfigs, templateConfig, cicdConfig } = buildResult;
12894
+ const { config, skippedMcpConfigs, templateConfig, cicdConfig, claudeSettings } = buildResult;
11728
12895
  if (templateConfig) {
11729
12896
  config.templateConfig = templateConfig;
11730
12897
  }
@@ -11741,7 +12908,15 @@ async function runInit(path8, options) {
11741
12908
  showConfigSummary(config);
11742
12909
  return;
11743
12910
  }
11744
- await executeInstallation(projectPath, config, registry, templatesPath, options, cicdConfig);
12911
+ await executeInstallation(
12912
+ projectPath,
12913
+ config,
12914
+ registry,
12915
+ templatesPath,
12916
+ options,
12917
+ cicdConfig,
12918
+ claudeSettings
12919
+ );
11745
12920
  if (templateConfig && !options.noPlaceholders) {
11746
12921
  const claudePath = joinPath(projectPath, ".claude");
11747
12922
  await replaceTemplateConfigWithSpinner(claudePath, templateConfig);
@@ -11885,7 +13060,7 @@ async function buildDefaultConfig(projectPath, detection, options) {
11885
13060
  // No MCP servers in default mode
11886
13061
  };
11887
13062
  }
11888
- async function buildInteractiveConfig(projectPath, detection, registry, options) {
13063
+ async function buildInteractiveConfig(projectPath, detection, registry, options, existingConfig) {
11889
13064
  const projectName = await getProjectName(projectPath);
11890
13065
  const projectDesc = await getProjectDescription(projectPath);
11891
13066
  const wizardConfig = createInitWizardConfig(
@@ -11899,24 +13074,45 @@ async function buildInteractiveConfig(projectPath, detection, registry, options)
11899
13074
  },
11900
13075
  registry
11901
13076
  );
13077
+ const existingProject = existingConfig?.project;
13078
+ const existingPrefs = existingConfig?.preferences;
11902
13079
  const initialContext = {
11903
13080
  projectPath,
11904
13081
  registry,
11905
13082
  detection: {
11906
13083
  detected: detection.detected,
11907
13084
  projectType: detection.projectType,
11908
- packageManager: detection.packageManager,
13085
+ packageManager: existingPrefs?.packageManager || detection.packageManager,
11909
13086
  suggestedBundles: detection.suggestedBundles,
11910
13087
  detectedTechnologies: detection.detectedTechnologies
11911
13088
  },
13089
+ // Use existing project info as defaults, fallback to detected values
11912
13090
  projectInfo: {
11913
- name: projectName || "",
11914
- description: projectDesc || "",
11915
- org: "",
11916
- repo: projectName?.toLowerCase().replace(/\s+/g, "-") || "",
11917
- entityType: "item",
11918
- entityTypePlural: "items"
11919
- }
13091
+ name: existingProject?.name || projectName || "",
13092
+ description: existingProject?.description || projectDesc || "",
13093
+ org: existingProject?.org || "",
13094
+ repo: existingProject?.repo || projectName?.toLowerCase().replace(/\s+/g, "-") || "",
13095
+ entityType: existingProject?.entityType || "item",
13096
+ entityTypePlural: existingProject?.entityTypePlural || "items",
13097
+ domain: existingProject?.domain,
13098
+ location: existingProject?.location,
13099
+ author: existingProject?.author
13100
+ },
13101
+ // Use existing preferences as defaults
13102
+ preferences: existingPrefs ? {
13103
+ language: existingPrefs.language,
13104
+ responseLanguage: existingPrefs.responseLanguage,
13105
+ includeCoAuthor: existingPrefs.includeCoAuthor,
13106
+ packageManager: existingPrefs.packageManager
13107
+ } : void 0,
13108
+ // Use existing hook config as defaults
13109
+ hookConfig: existingConfig?.extras?.hooks,
13110
+ // Use existing code style config as defaults
13111
+ codeStyleConfig: existingConfig?.extras?.codeStyle,
13112
+ // Use existing folder preferences as defaults
13113
+ folderPreferences: existingConfig?.extras?.folderPreferences,
13114
+ // Use existing permissions config as defaults
13115
+ permissionsConfig: existingConfig?.customizations?.permissions
11920
13116
  };
11921
13117
  const wizardResult = await runWizard(
11922
13118
  wizardConfig,
@@ -11937,7 +13133,8 @@ async function buildInteractiveConfig(projectPath, detection, registry, options)
11937
13133
  codeStyleConfig,
11938
13134
  cicdConfig,
11939
13135
  folderPreferences,
11940
- templateConfig: templateConfigResult
13136
+ templateConfig: templateConfigResult,
13137
+ claudeSettings
11941
13138
  } = wizardResult.values;
11942
13139
  let mcpConfig = { level: "project", servers: [] };
11943
13140
  let skippedMcpConfigs = [];
@@ -11995,10 +13192,11 @@ async function buildInteractiveConfig(projectPath, detection, registry, options)
11995
13192
  config,
11996
13193
  skippedMcpConfigs,
11997
13194
  templateConfig: templateConfigResult,
11998
- cicdConfig
13195
+ cicdConfig,
13196
+ claudeSettings
11999
13197
  };
12000
13198
  }
12001
- async function executeInstallation(projectPath, config, registry, templatesPath, options, cicdConfig) {
13199
+ async function executeInstallation(projectPath, config, registry, templatesPath, options, cicdConfig, claudeSettings) {
12002
13200
  logger.newline();
12003
13201
  logger.title("Installing Configuration");
12004
13202
  if (config.scaffold.type === "full-project") {
@@ -12020,6 +13218,28 @@ async function executeInstallation(projectPath, config, registry, templatesPath,
12020
13218
  } else if (claudeMdResult.skipped) {
12021
13219
  logger.info("CLAUDE.md already exists, skipped");
12022
13220
  }
13221
+ if (claudeSettings) {
13222
+ const settingsResult = await generateSettingsWithSpinner(projectPath, {
13223
+ claudeSettings,
13224
+ includeCoAuthor: config.preferences.includeCoAuthor,
13225
+ overwrite: options.force
13226
+ });
13227
+ if (settingsResult.error) {
13228
+ logger.warn(`settings.json generation warning: ${settingsResult.error}`);
13229
+ } else if (settingsResult.skipped) {
13230
+ logger.info("settings.json already exists, skipped");
13231
+ }
13232
+ const settingsLocalResult = await generateSettingsLocalWithSpinner(projectPath, {
13233
+ claudeSettings,
13234
+ includeCoAuthor: config.preferences.includeCoAuthor,
13235
+ overwrite: options.force
13236
+ });
13237
+ if (settingsLocalResult.error) {
13238
+ logger.warn(`settings.local.json generation warning: ${settingsLocalResult.error}`);
13239
+ } else if (settingsLocalResult.skipped) {
13240
+ logger.info("settings.local.json already exists, skipped");
13241
+ }
13242
+ }
12023
13243
  const modulesByCategory = {
12024
13244
  agents: filterModules(registry, "agents", config.modules.agents.selected),
12025
13245
  skills: filterModules(registry, "skills", config.modules.skills.selected),
@@ -12642,7 +13862,7 @@ async function runStatus(options) {
12642
13862
  }
12643
13863
  const config = await readConfig(projectPath);
12644
13864
  if (!config) {
12645
- logger.warn(".claude directory exists but config.json is missing");
13865
+ logger.warn(".claude directory exists but qazuor-claude-config.json is missing");
12646
13866
  logger.info('Run "claude-config init" to initialize properly');
12647
13867
  process.exit(0);
12648
13868
  }
@@ -13142,8 +14362,8 @@ function createConfigureCommand() {
13142
14362
  ).option("--preview", "Preview changes without applying").option("--show-defaults", "Show global defaults").option("-v, --verbose", "Detailed output").action(runConfigure);
13143
14363
  return cmd;
13144
14364
  }
13145
- async function runConfigure(path8, options) {
13146
- const projectPath = resolvePath(path8 || ".");
14365
+ async function runConfigure(path11, options) {
14366
+ const projectPath = resolvePath(path11 || ".");
13147
14367
  const claudePath = joinPath(projectPath, ".claude");
13148
14368
  logger.configure({ verbose: options.verbose, silent: false });
13149
14369
  logger.title("Template Configuration");
@@ -13274,16 +14494,1837 @@ async function interactiveMode(claudePath, projectPath, options) {
13274
14494
  };
13275
14495
  existingConfig.customizations.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
13276
14496
  await writeConfig(projectPath, existingConfig);
13277
- logger.success("Configuration saved to .claude/config.json");
14497
+ logger.success("Configuration saved to .claude/qazuor-claude-config.json");
13278
14498
  }
13279
14499
  await promptSaveGlobalDefaults(templateConfig);
13280
14500
  logger.newline();
13281
14501
  logger.success("Template configuration complete!");
13282
14502
  }
13283
14503
 
14504
+ // src/cli/commands/standards.ts
14505
+ init_esm_shims();
14506
+ import { Command as Command8 } from "commander";
14507
+
14508
+ // src/constants/standards-defaults.ts
14509
+ init_esm_shims();
14510
+ var DEFAULT_CODE_STANDARDS = {
14511
+ indentStyle: "space",
14512
+ indentSize: 2,
14513
+ maxLineLength: 100,
14514
+ maxFileLines: 500,
14515
+ quoteStyle: "single",
14516
+ semicolons: true,
14517
+ trailingCommas: "es5",
14518
+ allowAny: false,
14519
+ namedExportsOnly: true,
14520
+ roroPattern: true,
14521
+ jsDocRequired: true
14522
+ };
14523
+ var DEFAULT_TESTING_STANDARDS = {
14524
+ coverageTarget: 80,
14525
+ tddRequired: true,
14526
+ testPattern: "aaa",
14527
+ testLocation: "separate",
14528
+ unitTestMaxMs: 100,
14529
+ integrationTestMaxMs: 1e3
14530
+ };
14531
+ var DEFAULT_DOCUMENTATION_STANDARDS = {
14532
+ jsDocLevel: "standard",
14533
+ requireExamples: false,
14534
+ changelogFormat: "conventional",
14535
+ inlineCommentPolicy: "why-not-what"
14536
+ };
14537
+ var DEFAULT_DESIGN_STANDARDS = {
14538
+ cssFramework: "tailwind",
14539
+ componentLibrary: "shadcn",
14540
+ accessibilityLevel: "AA",
14541
+ darkModeSupport: true
14542
+ };
14543
+ var DEFAULT_SECURITY_STANDARDS = {
14544
+ authPattern: "jwt",
14545
+ inputValidation: "zod",
14546
+ csrfProtection: true,
14547
+ rateLimiting: true
14548
+ };
14549
+ var DEFAULT_PERFORMANCE_STANDARDS = {
14550
+ lcpTarget: 2500,
14551
+ fidTarget: 100,
14552
+ clsTarget: 0.1,
14553
+ bundleSizeTargetKb: 250,
14554
+ apiResponseTargetMs: 300
14555
+ };
14556
+ var DEFAULT_STANDARDS_CONFIG = {
14557
+ code: DEFAULT_CODE_STANDARDS,
14558
+ testing: DEFAULT_TESTING_STANDARDS,
14559
+ documentation: DEFAULT_DOCUMENTATION_STANDARDS,
14560
+ design: DEFAULT_DESIGN_STANDARDS,
14561
+ security: DEFAULT_SECURITY_STANDARDS,
14562
+ performance: DEFAULT_PERFORMANCE_STANDARDS
14563
+ };
14564
+ var STANDARDS_PRESETS = {
14565
+ strict: {
14566
+ name: "Strict",
14567
+ description: "High quality standards with strict enforcement (90%+ coverage, TDD required)",
14568
+ config: {
14569
+ code: {
14570
+ ...DEFAULT_CODE_STANDARDS,
14571
+ allowAny: false,
14572
+ jsDocRequired: true
14573
+ },
14574
+ testing: {
14575
+ ...DEFAULT_TESTING_STANDARDS,
14576
+ coverageTarget: 90,
14577
+ tddRequired: true
14578
+ },
14579
+ documentation: {
14580
+ ...DEFAULT_DOCUMENTATION_STANDARDS,
14581
+ jsDocLevel: "comprehensive",
14582
+ requireExamples: true
14583
+ },
14584
+ design: {
14585
+ ...DEFAULT_DESIGN_STANDARDS,
14586
+ accessibilityLevel: "AAA"
14587
+ },
14588
+ security: {
14589
+ ...DEFAULT_SECURITY_STANDARDS,
14590
+ csrfProtection: true,
14591
+ rateLimiting: true
14592
+ },
14593
+ performance: {
14594
+ ...DEFAULT_PERFORMANCE_STANDARDS,
14595
+ lcpTarget: 2e3,
14596
+ apiResponseTargetMs: 200
14597
+ }
14598
+ }
14599
+ },
14600
+ balanced: {
14601
+ name: "Balanced",
14602
+ description: "Good balance between quality and pragmatism (80% coverage)",
14603
+ config: DEFAULT_STANDARDS_CONFIG
14604
+ },
14605
+ relaxed: {
14606
+ name: "Relaxed",
14607
+ description: "More flexible standards for rapid development (70% coverage)",
14608
+ config: {
14609
+ code: {
14610
+ ...DEFAULT_CODE_STANDARDS,
14611
+ maxFileLines: 800,
14612
+ jsDocRequired: false
14613
+ },
14614
+ testing: {
14615
+ ...DEFAULT_TESTING_STANDARDS,
14616
+ coverageTarget: 70,
14617
+ tddRequired: false
14618
+ },
14619
+ documentation: {
14620
+ ...DEFAULT_DOCUMENTATION_STANDARDS,
14621
+ jsDocLevel: "minimal",
14622
+ requireExamples: false
14623
+ },
14624
+ design: {
14625
+ ...DEFAULT_DESIGN_STANDARDS,
14626
+ accessibilityLevel: "A"
14627
+ },
14628
+ security: {
14629
+ ...DEFAULT_SECURITY_STANDARDS
14630
+ },
14631
+ performance: {
14632
+ ...DEFAULT_PERFORMANCE_STANDARDS,
14633
+ lcpTarget: 4e3,
14634
+ apiResponseTargetMs: 500
14635
+ }
14636
+ }
14637
+ },
14638
+ startup: {
14639
+ name: "Startup",
14640
+ description: "Fast iteration with minimum viable standards (60% coverage)",
14641
+ config: {
14642
+ code: {
14643
+ ...DEFAULT_CODE_STANDARDS,
14644
+ maxFileLines: 1e3,
14645
+ jsDocRequired: false,
14646
+ roroPattern: false
14647
+ },
14648
+ testing: {
14649
+ coverageTarget: 60,
14650
+ tddRequired: false,
14651
+ testPattern: "aaa",
14652
+ testLocation: "colocated",
14653
+ unitTestMaxMs: 200,
14654
+ integrationTestMaxMs: 2e3
14655
+ },
14656
+ documentation: {
14657
+ jsDocLevel: "minimal",
14658
+ requireExamples: false,
14659
+ changelogFormat: "conventional",
14660
+ inlineCommentPolicy: "minimal"
14661
+ },
14662
+ design: {
14663
+ cssFramework: "tailwind",
14664
+ componentLibrary: "shadcn",
14665
+ accessibilityLevel: "A",
14666
+ darkModeSupport: false
14667
+ },
14668
+ security: {
14669
+ authPattern: "jwt",
14670
+ inputValidation: "zod",
14671
+ csrfProtection: false,
14672
+ rateLimiting: false
14673
+ },
14674
+ performance: {
14675
+ lcpTarget: 4e3,
14676
+ fidTarget: 300,
14677
+ clsTarget: 0.25,
14678
+ bundleSizeTargetKb: 500,
14679
+ apiResponseTargetMs: 500
14680
+ }
14681
+ }
14682
+ },
14683
+ enterprise: {
14684
+ name: "Enterprise",
14685
+ description: "Enterprise-grade standards with full compliance (95%+ coverage)",
14686
+ config: {
14687
+ code: {
14688
+ indentStyle: "space",
14689
+ indentSize: 2,
14690
+ maxLineLength: 120,
14691
+ maxFileLines: 400,
14692
+ quoteStyle: "single",
14693
+ semicolons: true,
14694
+ trailingCommas: "all",
14695
+ allowAny: false,
14696
+ namedExportsOnly: true,
14697
+ roroPattern: true,
14698
+ jsDocRequired: true
14699
+ },
14700
+ testing: {
14701
+ coverageTarget: 95,
14702
+ tddRequired: true,
14703
+ testPattern: "aaa",
14704
+ testLocation: "separate",
14705
+ unitTestMaxMs: 50,
14706
+ integrationTestMaxMs: 500
14707
+ },
14708
+ documentation: {
14709
+ jsDocLevel: "comprehensive",
14710
+ requireExamples: true,
14711
+ changelogFormat: "keepachangelog",
14712
+ inlineCommentPolicy: "why-not-what"
14713
+ },
14714
+ design: {
14715
+ cssFramework: "tailwind",
14716
+ componentLibrary: "radix",
14717
+ accessibilityLevel: "AAA",
14718
+ darkModeSupport: true
14719
+ },
14720
+ security: {
14721
+ authPattern: "oauth",
14722
+ inputValidation: "zod",
14723
+ csrfProtection: true,
14724
+ rateLimiting: true
14725
+ },
14726
+ performance: {
14727
+ lcpTarget: 1500,
14728
+ fidTarget: 50,
14729
+ clsTarget: 0.05,
14730
+ bundleSizeTargetKb: 150,
14731
+ apiResponseTargetMs: 150
14732
+ }
14733
+ }
14734
+ },
14735
+ custom: {
14736
+ name: "Custom",
14737
+ description: "Configure each standard manually",
14738
+ config: DEFAULT_STANDARDS_CONFIG
14739
+ }
14740
+ };
14741
+
14742
+ // src/lib/standards/index.ts
14743
+ init_esm_shims();
14744
+
14745
+ // src/lib/standards/definitions.ts
14746
+ init_esm_shims();
14747
+ var CODE_STANDARDS_DEFINITION = {
14748
+ id: "code",
14749
+ name: "Code Standards",
14750
+ description: "Code style, formatting, and TypeScript conventions",
14751
+ icon: "\u{1F4DD}",
14752
+ options: [
14753
+ {
14754
+ id: "indentStyle",
14755
+ label: "Indent Style",
14756
+ description: "Use spaces or tabs for indentation",
14757
+ type: "select",
14758
+ choices: [
14759
+ { name: "Spaces", value: "space", description: "Standard for most projects" },
14760
+ { name: "Tabs", value: "tab", description: "Better for accessibility" }
14761
+ ],
14762
+ affectsPlaceholders: ["{{INDENT_STYLE}}"]
14763
+ },
14764
+ {
14765
+ id: "indentSize",
14766
+ label: "Indent Size",
14767
+ description: "Number of spaces/tab width",
14768
+ type: "select",
14769
+ choices: [
14770
+ { name: "2 spaces", value: "2", description: "Most common in JS/TS" },
14771
+ { name: "4 spaces", value: "4", description: "More readable for some" }
14772
+ ],
14773
+ affectsPlaceholders: ["{{INDENT_SIZE}}"]
14774
+ },
14775
+ {
14776
+ id: "maxLineLength",
14777
+ label: "Max Line Length",
14778
+ description: "Maximum characters per line",
14779
+ type: "select",
14780
+ choices: [
14781
+ { name: "80 characters", value: "80", description: "Classic terminal width" },
14782
+ { name: "100 characters", value: "100", description: "Modern balance" },
14783
+ { name: "120 characters", value: "120", description: "Wide screens" }
14784
+ ],
14785
+ affectsPlaceholders: ["{{MAX_LINE_LENGTH}}"]
14786
+ },
14787
+ {
14788
+ id: "maxFileLines",
14789
+ label: "Max File Lines",
14790
+ description: "Maximum lines per file (excluding tests, docs, JSON)",
14791
+ type: "select",
14792
+ choices: [
14793
+ { name: "300 lines", value: "300", description: "Very strict" },
14794
+ { name: "500 lines", value: "500", description: "Recommended" },
14795
+ { name: "800 lines", value: "800", description: "Relaxed" },
14796
+ { name: "1000 lines", value: "1000", description: "Very relaxed" }
14797
+ ],
14798
+ affectsPlaceholders: ["{{MAX_FILE_LINES}}"]
14799
+ },
14800
+ {
14801
+ id: "quoteStyle",
14802
+ label: "Quote Style",
14803
+ description: "Single or double quotes for strings",
14804
+ type: "select",
14805
+ choices: [
14806
+ { name: "Single quotes", value: "single", description: "const x = 'hello'" },
14807
+ { name: "Double quotes", value: "double", description: 'const x = "hello"' }
14808
+ ],
14809
+ affectsPlaceholders: ["{{QUOTE_STYLE}}"]
14810
+ },
14811
+ {
14812
+ id: "semicolons",
14813
+ label: "Semicolons",
14814
+ description: "Use semicolons at end of statements",
14815
+ type: "boolean",
14816
+ affectsPlaceholders: ["{{USE_SEMICOLONS}}"]
14817
+ },
14818
+ {
14819
+ id: "trailingCommas",
14820
+ label: "Trailing Commas",
14821
+ description: "Add trailing commas in multiline constructs",
14822
+ type: "select",
14823
+ choices: [
14824
+ { name: "ES5", value: "es5", description: "Where valid in ES5 (objects, arrays)" },
14825
+ { name: "All", value: "all", description: "Everywhere possible" },
14826
+ { name: "None", value: "none", description: "No trailing commas" }
14827
+ ],
14828
+ affectsPlaceholders: ["{{TRAILING_COMMAS}}"]
14829
+ },
14830
+ {
14831
+ id: "allowAny",
14832
+ label: 'Allow "any" Type',
14833
+ description: 'Allow the "any" type in TypeScript',
14834
+ type: "boolean",
14835
+ affectsPlaceholders: ["{{ALLOW_ANY}}"]
14836
+ },
14837
+ {
14838
+ id: "namedExportsOnly",
14839
+ label: "Named Exports Only",
14840
+ description: "Require named exports (no default exports)",
14841
+ type: "boolean",
14842
+ affectsPlaceholders: ["{{NAMED_EXPORTS_ONLY}}"]
14843
+ },
14844
+ {
14845
+ id: "roroPattern",
14846
+ label: "RO-RO Pattern",
14847
+ description: "Require Receive Object, Return Object pattern",
14848
+ type: "boolean",
14849
+ affectsPlaceholders: ["{{RORO_PATTERN}}"]
14850
+ },
14851
+ {
14852
+ id: "jsDocRequired",
14853
+ label: "JSDoc Required",
14854
+ description: "Require JSDoc for all exports",
14855
+ type: "boolean",
14856
+ affectsPlaceholders: ["{{JSDOC_REQUIRED}}"]
14857
+ }
14858
+ ],
14859
+ targetFiles: ["code-standards.md", "architecture-patterns.md"]
14860
+ };
14861
+ var TESTING_STANDARDS_DEFINITION = {
14862
+ id: "testing",
14863
+ name: "Testing Standards",
14864
+ description: "Test coverage, TDD, and testing methodology",
14865
+ icon: "\u{1F9EA}",
14866
+ options: [
14867
+ {
14868
+ id: "coverageTarget",
14869
+ label: "Coverage Target",
14870
+ description: "Minimum code coverage percentage",
14871
+ type: "select",
14872
+ choices: [
14873
+ { name: "60%", value: "60", description: "Minimum viable" },
14874
+ { name: "70%", value: "70", description: "Relaxed" },
14875
+ { name: "80%", value: "80", description: "Standard" },
14876
+ { name: "90%", value: "90", description: "Strict" },
14877
+ { name: "95%", value: "95", description: "Enterprise" }
14878
+ ],
14879
+ affectsPlaceholders: ["{{COVERAGE_TARGET}}"]
14880
+ },
14881
+ {
14882
+ id: "tddRequired",
14883
+ label: "TDD Required",
14884
+ description: "Require Test-Driven Development (Red-Green-Refactor)",
14885
+ type: "boolean",
14886
+ affectsPlaceholders: ["{{TDD_REQUIRED}}"]
14887
+ },
14888
+ {
14889
+ id: "testPattern",
14890
+ label: "Test Pattern",
14891
+ description: "Test structure pattern",
14892
+ type: "select",
14893
+ choices: [
14894
+ { name: "AAA", value: "aaa", description: "Arrange-Act-Assert" },
14895
+ { name: "GWT", value: "gwt", description: "Given-When-Then" }
14896
+ ],
14897
+ affectsPlaceholders: ["{{TEST_PATTERN}}"]
14898
+ },
14899
+ {
14900
+ id: "testLocation",
14901
+ label: "Test Location",
14902
+ description: "Where to place test files",
14903
+ type: "select",
14904
+ choices: [
14905
+ { name: "Separate", value: "separate", description: "test/ folder at root" },
14906
+ { name: "Colocated", value: "colocated", description: "__tests__ near source" }
14907
+ ],
14908
+ affectsPlaceholders: ["{{TEST_LOCATION}}"]
14909
+ },
14910
+ {
14911
+ id: "unitTestMaxMs",
14912
+ label: "Unit Test Max (ms)",
14913
+ description: "Maximum milliseconds per unit test",
14914
+ type: "select",
14915
+ choices: [
14916
+ { name: "50ms", value: "50", description: "Very fast" },
14917
+ { name: "100ms", value: "100", description: "Standard" },
14918
+ { name: "200ms", value: "200", description: "Relaxed" }
14919
+ ],
14920
+ affectsPlaceholders: ["{{UNIT_TEST_MAX_MS}}"]
14921
+ },
14922
+ {
14923
+ id: "integrationTestMaxMs",
14924
+ label: "Integration Test Max (ms)",
14925
+ description: "Maximum milliseconds per integration test",
14926
+ type: "select",
14927
+ choices: [
14928
+ { name: "500ms", value: "500", description: "Fast" },
14929
+ { name: "1000ms", value: "1000", description: "Standard" },
14930
+ { name: "2000ms", value: "2000", description: "Relaxed" }
14931
+ ],
14932
+ affectsPlaceholders: ["{{INTEGRATION_TEST_MAX_MS}}"]
14933
+ }
14934
+ ],
14935
+ targetFiles: ["testing-standards.md"]
14936
+ };
14937
+ var DOCUMENTATION_STANDARDS_DEFINITION = {
14938
+ id: "documentation",
14939
+ name: "Documentation Standards",
14940
+ description: "JSDoc, comments, and changelog conventions",
14941
+ icon: "\u{1F4DA}",
14942
+ options: [
14943
+ {
14944
+ id: "jsDocLevel",
14945
+ label: "JSDoc Level",
14946
+ description: "Level of detail in JSDoc comments",
14947
+ type: "select",
14948
+ choices: [
14949
+ { name: "Minimal", value: "minimal", description: "Brief description only" },
14950
+ { name: "Standard", value: "standard", description: "Description + params + returns" },
14951
+ { name: "Comprehensive", value: "comprehensive", description: "Full docs with examples" }
14952
+ ],
14953
+ affectsPlaceholders: ["{{JSDOC_LEVEL}}"]
14954
+ },
14955
+ {
14956
+ id: "requireExamples",
14957
+ label: "Require Examples",
14958
+ description: "Require @example in JSDoc",
14959
+ type: "boolean",
14960
+ affectsPlaceholders: ["{{REQUIRE_EXAMPLES}}"]
14961
+ },
14962
+ {
14963
+ id: "changelogFormat",
14964
+ label: "Changelog Format",
14965
+ description: "Changelog format to follow",
14966
+ type: "select",
14967
+ choices: [
14968
+ { name: "Conventional", value: "conventional", description: "Auto-generated from commits" },
14969
+ { name: "Keep a Changelog", value: "keepachangelog", description: "Manual, semantic" }
14970
+ ],
14971
+ affectsPlaceholders: ["{{CHANGELOG_FORMAT}}"]
14972
+ },
14973
+ {
14974
+ id: "inlineCommentPolicy",
14975
+ label: "Inline Comment Policy",
14976
+ description: "Policy for inline code comments",
14977
+ type: "select",
14978
+ choices: [
14979
+ {
14980
+ name: "Why not What",
14981
+ value: "why-not-what",
14982
+ description: "Explain reasoning, not obvious"
14983
+ },
14984
+ { name: "Minimal", value: "minimal", description: "Only when necessary" },
14985
+ { name: "Extensive", value: "extensive", description: "Comment thoroughly" }
14986
+ ],
14987
+ affectsPlaceholders: ["{{INLINE_COMMENT_POLICY}}"]
14988
+ }
14989
+ ],
14990
+ targetFiles: ["documentation-standards.md"]
14991
+ };
14992
+ var DESIGN_STANDARDS_DEFINITION = {
14993
+ id: "design",
14994
+ name: "Design Standards",
14995
+ description: "UI/UX, CSS, and accessibility standards",
14996
+ icon: "\u{1F3A8}",
14997
+ options: [
14998
+ {
14999
+ id: "cssFramework",
15000
+ label: "CSS Framework",
15001
+ description: "CSS/styling approach",
15002
+ type: "select",
15003
+ choices: [
15004
+ { name: "Tailwind CSS", value: "tailwind", description: "Utility-first CSS" },
15005
+ { name: "CSS Modules", value: "css-modules", description: "Scoped CSS" },
15006
+ { name: "Styled Components", value: "styled-components", description: "CSS-in-JS" },
15007
+ { name: "Vanilla CSS", value: "vanilla", description: "Plain CSS" }
15008
+ ],
15009
+ affectsPlaceholders: ["{{CSS_FRAMEWORK}}"]
15010
+ },
15011
+ {
15012
+ id: "componentLibrary",
15013
+ label: "Component Library",
15014
+ description: "UI component library",
15015
+ type: "select",
15016
+ choices: [
15017
+ { name: "shadcn/ui", value: "shadcn", description: "Copy-paste components" },
15018
+ { name: "Radix UI", value: "radix", description: "Unstyled primitives" },
15019
+ { name: "Headless UI", value: "headless", description: "Unstyled, accessible" },
15020
+ { name: "None", value: "none", description: "Build from scratch" }
15021
+ ],
15022
+ affectsPlaceholders: ["{{COMPONENT_LIBRARY}}"]
15023
+ },
15024
+ {
15025
+ id: "accessibilityLevel",
15026
+ label: "Accessibility Level",
15027
+ description: "WCAG accessibility compliance level",
15028
+ type: "select",
15029
+ choices: [
15030
+ { name: "Level A", value: "A", description: "Minimum" },
15031
+ { name: "Level AA", value: "AA", description: "Standard (recommended)" },
15032
+ { name: "Level AAA", value: "AAA", description: "Highest" }
15033
+ ],
15034
+ affectsPlaceholders: ["{{WCAG_LEVEL}}", "{{ACCESSIBILITY_LEVEL}}"]
15035
+ },
15036
+ {
15037
+ id: "darkModeSupport",
15038
+ label: "Dark Mode Support",
15039
+ description: "Support dark mode theme",
15040
+ type: "boolean",
15041
+ affectsPlaceholders: ["{{DARK_MODE_SUPPORT}}"]
15042
+ }
15043
+ ],
15044
+ targetFiles: ["design-standards.md"]
15045
+ };
15046
+ var SECURITY_STANDARDS_DEFINITION = {
15047
+ id: "security",
15048
+ name: "Security Standards",
15049
+ description: "Authentication, validation, and security practices",
15050
+ icon: "\u{1F512}",
15051
+ options: [
15052
+ {
15053
+ id: "authPattern",
15054
+ label: "Auth Pattern",
15055
+ description: "Authentication approach",
15056
+ type: "select",
15057
+ choices: [
15058
+ { name: "JWT", value: "jwt", description: "JSON Web Tokens" },
15059
+ { name: "Session", value: "session", description: "Server-side sessions" },
15060
+ { name: "OAuth", value: "oauth", description: "OAuth 2.0 / OIDC" },
15061
+ { name: "None", value: "none", description: "No authentication" }
15062
+ ],
15063
+ affectsPlaceholders: ["{{AUTH_PATTERN}}"]
15064
+ },
15065
+ {
15066
+ id: "inputValidation",
15067
+ label: "Input Validation",
15068
+ description: "Validation library",
15069
+ type: "select",
15070
+ choices: [
15071
+ { name: "Zod", value: "zod", description: "TypeScript-first validation" },
15072
+ { name: "Yup", value: "yup", description: "Schema builder" },
15073
+ { name: "Joi", value: "joi", description: "Data validation" },
15074
+ { name: "Manual", value: "manual", description: "Custom validation" }
15075
+ ],
15076
+ affectsPlaceholders: ["{{VALIDATION_LIBRARY}}", "{{INPUT_VALIDATION}}"]
15077
+ },
15078
+ {
15079
+ id: "csrfProtection",
15080
+ label: "CSRF Protection",
15081
+ description: "Enable Cross-Site Request Forgery protection",
15082
+ type: "boolean",
15083
+ affectsPlaceholders: ["{{CSRF_PROTECTION}}"]
15084
+ },
15085
+ {
15086
+ id: "rateLimiting",
15087
+ label: "Rate Limiting",
15088
+ description: "Enable API rate limiting",
15089
+ type: "boolean",
15090
+ affectsPlaceholders: ["{{RATE_LIMITING}}"]
15091
+ }
15092
+ ],
15093
+ targetFiles: ["security-standards.md"]
15094
+ };
15095
+ var PERFORMANCE_STANDARDS_DEFINITION = {
15096
+ id: "performance",
15097
+ name: "Performance Standards",
15098
+ description: "Core Web Vitals and performance targets",
15099
+ icon: "\u26A1",
15100
+ options: [
15101
+ {
15102
+ id: "lcpTarget",
15103
+ label: "LCP Target (ms)",
15104
+ description: "Largest Contentful Paint target",
15105
+ type: "select",
15106
+ choices: [
15107
+ { name: "1500ms", value: "1500", description: "Excellent" },
15108
+ { name: "2000ms", value: "2000", description: "Good" },
15109
+ { name: "2500ms", value: "2500", description: "Standard" },
15110
+ { name: "4000ms", value: "4000", description: "Needs improvement" }
15111
+ ],
15112
+ affectsPlaceholders: ["{{LCP_TARGET}}"]
15113
+ },
15114
+ {
15115
+ id: "fidTarget",
15116
+ label: "FID Target (ms)",
15117
+ description: "First Input Delay target",
15118
+ type: "select",
15119
+ choices: [
15120
+ { name: "50ms", value: "50", description: "Excellent" },
15121
+ { name: "100ms", value: "100", description: "Good" },
15122
+ { name: "200ms", value: "200", description: "Standard" },
15123
+ { name: "300ms", value: "300", description: "Needs improvement" }
15124
+ ],
15125
+ affectsPlaceholders: ["{{FID_TARGET}}"]
15126
+ },
15127
+ {
15128
+ id: "clsTarget",
15129
+ label: "CLS Target",
15130
+ description: "Cumulative Layout Shift target",
15131
+ type: "select",
15132
+ choices: [
15133
+ { name: "0.05", value: "0.05", description: "Excellent" },
15134
+ { name: "0.1", value: "0.1", description: "Good" },
15135
+ { name: "0.15", value: "0.15", description: "Standard" },
15136
+ { name: "0.25", value: "0.25", description: "Needs improvement" }
15137
+ ],
15138
+ affectsPlaceholders: ["{{CLS_TARGET}}"]
15139
+ },
15140
+ {
15141
+ id: "bundleSizeTargetKb",
15142
+ label: "Bundle Size (KB)",
15143
+ description: "Maximum initial bundle size",
15144
+ type: "select",
15145
+ choices: [
15146
+ { name: "100KB", value: "100", description: "Very strict" },
15147
+ { name: "150KB", value: "150", description: "Strict" },
15148
+ { name: "250KB", value: "250", description: "Standard" },
15149
+ { name: "500KB", value: "500", description: "Relaxed" }
15150
+ ],
15151
+ affectsPlaceholders: ["{{BUNDLE_SIZE_TARGET}}"]
15152
+ },
15153
+ {
15154
+ id: "apiResponseTargetMs",
15155
+ label: "API Response (ms)",
15156
+ description: "Maximum API response time",
15157
+ type: "select",
15158
+ choices: [
15159
+ { name: "100ms", value: "100", description: "Very fast" },
15160
+ { name: "200ms", value: "200", description: "Fast" },
15161
+ { name: "300ms", value: "300", description: "Standard" },
15162
+ { name: "500ms", value: "500", description: "Relaxed" }
15163
+ ],
15164
+ affectsPlaceholders: ["{{API_RESPONSE_TARGET}}"]
15165
+ }
15166
+ ],
15167
+ targetFiles: ["performance-standards.md"]
15168
+ };
15169
+ var STANDARDS_DEFINITIONS = {
15170
+ code: CODE_STANDARDS_DEFINITION,
15171
+ testing: TESTING_STANDARDS_DEFINITION,
15172
+ documentation: DOCUMENTATION_STANDARDS_DEFINITION,
15173
+ design: DESIGN_STANDARDS_DEFINITION,
15174
+ security: SECURITY_STANDARDS_DEFINITION,
15175
+ performance: PERFORMANCE_STANDARDS_DEFINITION
15176
+ };
15177
+
15178
+ // src/lib/standards/replacer.ts
15179
+ init_esm_shims();
15180
+ import * as fs6 from "fs/promises";
15181
+ import * as path8 from "path";
15182
+ import ora3 from "ora";
15183
+ var PROCESSABLE_EXTENSIONS2 = [".md", ".json", ".yaml", ".yml", ".txt"];
15184
+ var SKIP_DIRECTORIES3 = ["node_modules", ".git", "dist", "build", ".next", ".turbo"];
15185
+ function flattenStandardsConfig(config) {
15186
+ const flattened = {};
15187
+ if (config.code) {
15188
+ flattened["{{INDENT_STYLE}}"] = config.code.indentStyle;
15189
+ flattened["{{INDENT_SIZE}}"] = String(config.code.indentSize);
15190
+ flattened["{{MAX_LINE_LENGTH}}"] = String(config.code.maxLineLength);
15191
+ flattened["{{MAX_FILE_LINES}}"] = String(config.code.maxFileLines);
15192
+ flattened["{{QUOTE_STYLE}}"] = config.code.quoteStyle;
15193
+ flattened["{{USE_SEMICOLONS}}"] = config.code.semicolons ? "yes" : "no";
15194
+ flattened["{{TRAILING_COMMAS}}"] = config.code.trailingCommas;
15195
+ flattened["{{ALLOW_ANY}}"] = config.code.allowAny ? "yes" : "no";
15196
+ flattened["{{NAMED_EXPORTS_ONLY}}"] = config.code.namedExportsOnly ? "yes" : "no";
15197
+ flattened["{{RORO_PATTERN}}"] = config.code.roroPattern ? "yes" : "no";
15198
+ flattened["{{JSDOC_REQUIRED}}"] = config.code.jsDocRequired ? "yes" : "no";
15199
+ }
15200
+ if (config.testing) {
15201
+ flattened["{{COVERAGE_TARGET}}"] = String(config.testing.coverageTarget);
15202
+ flattened["{{TDD_REQUIRED}}"] = config.testing.tddRequired ? "yes" : "no";
15203
+ flattened["{{TEST_PATTERN}}"] = config.testing.testPattern.toUpperCase();
15204
+ flattened["{{TEST_LOCATION}}"] = config.testing.testLocation;
15205
+ flattened["{{UNIT_TEST_MAX_MS}}"] = String(config.testing.unitTestMaxMs);
15206
+ flattened["{{INTEGRATION_TEST_MAX_MS}}"] = String(config.testing.integrationTestMaxMs);
15207
+ }
15208
+ if (config.documentation) {
15209
+ flattened["{{JSDOC_LEVEL}}"] = config.documentation.jsDocLevel;
15210
+ flattened["{{REQUIRE_EXAMPLES}}"] = config.documentation.requireExamples ? "yes" : "no";
15211
+ flattened["{{CHANGELOG_FORMAT}}"] = config.documentation.changelogFormat;
15212
+ flattened["{{INLINE_COMMENT_POLICY}}"] = config.documentation.inlineCommentPolicy;
15213
+ }
15214
+ if (config.design) {
15215
+ flattened["{{CSS_FRAMEWORK}}"] = config.design.cssFramework;
15216
+ flattened["{{COMPONENT_LIBRARY}}"] = config.design.componentLibrary;
15217
+ flattened["{{WCAG_LEVEL}}"] = config.design.accessibilityLevel;
15218
+ flattened["{{ACCESSIBILITY_LEVEL}}"] = config.design.accessibilityLevel;
15219
+ flattened["{{DARK_MODE_SUPPORT}}"] = config.design.darkModeSupport ? "yes" : "no";
15220
+ }
15221
+ if (config.security) {
15222
+ flattened["{{AUTH_PATTERN}}"] = config.security.authPattern;
15223
+ flattened["{{VALIDATION_LIBRARY}}"] = config.security.inputValidation;
15224
+ flattened["{{INPUT_VALIDATION}}"] = config.security.inputValidation;
15225
+ flattened["{{CSRF_PROTECTION}}"] = config.security.csrfProtection ? "yes" : "no";
15226
+ flattened["{{RATE_LIMITING}}"] = config.security.rateLimiting ? "yes" : "no";
15227
+ }
15228
+ if (config.performance) {
15229
+ flattened["{{LCP_TARGET}}"] = String(config.performance.lcpTarget);
15230
+ flattened["{{FID_TARGET}}"] = String(config.performance.fidTarget);
15231
+ flattened["{{CLS_TARGET}}"] = String(config.performance.clsTarget);
15232
+ flattened["{{BUNDLE_SIZE_TARGET}}"] = String(config.performance.bundleSizeTargetKb);
15233
+ flattened["{{API_RESPONSE_TARGET}}"] = String(config.performance.apiResponseTargetMs);
15234
+ }
15235
+ return flattened;
15236
+ }
15237
+ function shouldProcessFile2(filePath) {
15238
+ const ext = path8.extname(filePath).toLowerCase();
15239
+ return PROCESSABLE_EXTENSIONS2.includes(ext);
15240
+ }
15241
+ function shouldSkipDirectory3(dirName) {
15242
+ return SKIP_DIRECTORIES3.includes(dirName) || dirName.startsWith(".");
15243
+ }
15244
+ async function getAllFiles3(dir) {
15245
+ const files = [];
15246
+ try {
15247
+ const entries = await fs6.readdir(dir, { withFileTypes: true });
15248
+ for (const entry of entries) {
15249
+ const fullPath = path8.join(dir, entry.name);
15250
+ if (entry.isDirectory()) {
15251
+ if (!shouldSkipDirectory3(entry.name)) {
15252
+ const subFiles = await getAllFiles3(fullPath);
15253
+ files.push(...subFiles);
15254
+ }
15255
+ } else if (entry.isFile() && shouldProcessFile2(entry.name)) {
15256
+ files.push(fullPath);
15257
+ }
15258
+ }
15259
+ } catch {
15260
+ }
15261
+ return files;
15262
+ }
15263
+ async function replaceInFile3(filePath, replacements) {
15264
+ const changes = [];
15265
+ try {
15266
+ let content = await fs6.readFile(filePath, "utf-8");
15267
+ let modified = false;
15268
+ for (const [placeholder, value] of Object.entries(replacements)) {
15269
+ if (content.includes(placeholder)) {
15270
+ content = content.split(placeholder).join(value);
15271
+ changes.push({ placeholder, value });
15272
+ modified = true;
15273
+ }
15274
+ }
15275
+ if (modified) {
15276
+ await fs6.writeFile(filePath, content, "utf-8");
15277
+ }
15278
+ } catch {
15279
+ }
15280
+ return changes;
15281
+ }
15282
+ async function replaceStandardsPlaceholders(claudePath, config) {
15283
+ const replacements = flattenStandardsConfig(config);
15284
+ const standardsDir = path8.join(claudePath, "docs", "standards");
15285
+ const files = await getAllFiles3(standardsDir);
15286
+ const report = {
15287
+ modifiedFiles: [],
15288
+ replacedPlaceholders: [],
15289
+ unusedPlaceholders: [],
15290
+ errors: []
15291
+ };
15292
+ const usedPlaceholders = /* @__PURE__ */ new Set();
15293
+ for (const file of files) {
15294
+ try {
15295
+ const changes = await replaceInFile3(file, replacements);
15296
+ if (changes.length > 0) {
15297
+ report.modifiedFiles.push(path8.relative(claudePath, file));
15298
+ for (const change of changes) {
15299
+ if (!report.replacedPlaceholders.includes(change.placeholder)) {
15300
+ report.replacedPlaceholders.push(change.placeholder);
15301
+ }
15302
+ usedPlaceholders.add(change.placeholder);
15303
+ }
15304
+ }
15305
+ } catch (error) {
15306
+ report.errors.push(`Error processing ${file}: ${String(error)}`);
15307
+ }
15308
+ }
15309
+ for (const placeholder of Object.keys(replacements)) {
15310
+ if (!usedPlaceholders.has(placeholder)) {
15311
+ report.unusedPlaceholders.push(placeholder);
15312
+ }
15313
+ }
15314
+ return report;
15315
+ }
15316
+ async function replaceStandardsWithSpinner(claudePath, config) {
15317
+ const spinner2 = ora3("Applying standards configuration...").start();
15318
+ try {
15319
+ const report = await replaceStandardsPlaceholders(claudePath, config);
15320
+ if (report.modifiedFiles.length > 0) {
15321
+ spinner2.succeed(
15322
+ `Applied ${report.replacedPlaceholders.length} standards to ${report.modifiedFiles.length} files`
15323
+ );
15324
+ } else {
15325
+ spinner2.info("No standards placeholders found to replace");
15326
+ }
15327
+ return report;
15328
+ } catch (error) {
15329
+ spinner2.fail("Failed to apply standards configuration");
15330
+ throw error;
15331
+ }
15332
+ }
15333
+ async function previewStandardsReplacements(claudePath, config) {
15334
+ const replacements = flattenStandardsConfig(config);
15335
+ const standardsDir = path8.join(claudePath, "docs", "standards");
15336
+ const files = await getAllFiles3(standardsDir);
15337
+ const preview = [];
15338
+ for (const file of files) {
15339
+ try {
15340
+ const content = await fs6.readFile(file, "utf-8");
15341
+ for (const [placeholder, value] of Object.entries(replacements)) {
15342
+ if (content.includes(placeholder)) {
15343
+ preview.push({
15344
+ file: path8.relative(claudePath, file),
15345
+ placeholder,
15346
+ value
15347
+ });
15348
+ }
15349
+ }
15350
+ } catch {
15351
+ }
15352
+ }
15353
+ return preview;
15354
+ }
15355
+ function formatStandardsReport(report) {
15356
+ const lines = [];
15357
+ lines.push("Standards Configuration Applied");
15358
+ lines.push("\u2500".repeat(40));
15359
+ lines.push(`Files modified: ${report.modifiedFiles.length}`);
15360
+ lines.push(`Placeholders replaced: ${report.replacedPlaceholders.length}`);
15361
+ if (report.modifiedFiles.length > 0) {
15362
+ lines.push("");
15363
+ lines.push("Modified files:");
15364
+ for (const file of report.modifiedFiles) {
15365
+ lines.push(` \u2713 ${file}`);
15366
+ }
15367
+ }
15368
+ if (report.unusedPlaceholders.length > 0) {
15369
+ lines.push("");
15370
+ lines.push("Unused placeholders (no matching templates):");
15371
+ for (const p of report.unusedPlaceholders.slice(0, 5)) {
15372
+ lines.push(` - ${p}`);
15373
+ }
15374
+ if (report.unusedPlaceholders.length > 5) {
15375
+ lines.push(` ... and ${report.unusedPlaceholders.length - 5} more`);
15376
+ }
15377
+ }
15378
+ if (report.errors.length > 0) {
15379
+ lines.push("");
15380
+ lines.push("Errors:");
15381
+ for (const error of report.errors) {
15382
+ lines.push(` \u2717 ${error}`);
15383
+ }
15384
+ }
15385
+ return lines.join("\n");
15386
+ }
15387
+
15388
+ // src/lib/standards/scanner.ts
15389
+ init_esm_shims();
15390
+ import * as fs7 from "fs/promises";
15391
+ import * as path9 from "path";
15392
+ var SCANNABLE_EXTENSIONS2 = [".md", ".json", ".yaml", ".yml", ".txt"];
15393
+ var PLACEHOLDER_PATTERN = /\{\{([A-Z_]+)\}\}/g;
15394
+ async function getScanableFiles(dir) {
15395
+ const files = [];
15396
+ try {
15397
+ const entries = await fs7.readdir(dir, { withFileTypes: true });
15398
+ for (const entry of entries) {
15399
+ const fullPath = path9.join(dir, entry.name);
15400
+ if (entry.isDirectory()) {
15401
+ const subFiles = await getScanableFiles(fullPath);
15402
+ files.push(...subFiles);
15403
+ } else if (entry.isFile()) {
15404
+ const ext = path9.extname(entry.name).toLowerCase();
15405
+ if (SCANNABLE_EXTENSIONS2.includes(ext)) {
15406
+ files.push(fullPath);
15407
+ }
15408
+ }
15409
+ }
15410
+ } catch {
15411
+ }
15412
+ return files;
15413
+ }
15414
+ function extractPlaceholders2(content) {
15415
+ const matches = content.match(PLACEHOLDER_PATTERN);
15416
+ return matches ? [...new Set(matches)] : [];
15417
+ }
15418
+ async function scanStandardsPlaceholders(claudePath, config) {
15419
+ const standardsDir = path9.join(claudePath, "docs", "standards");
15420
+ const files = await getScanableFiles(standardsDir);
15421
+ const placeholdersByFile = /* @__PURE__ */ new Map();
15422
+ const allPlaceholders = /* @__PURE__ */ new Set();
15423
+ for (const file of files) {
15424
+ try {
15425
+ const content = await fs7.readFile(file, "utf-8");
15426
+ const placeholders = extractPlaceholders2(content);
15427
+ if (placeholders.length > 0) {
15428
+ const relPath = path9.relative(claudePath, file);
15429
+ placeholdersByFile.set(relPath, placeholders);
15430
+ for (const p of placeholders) {
15431
+ allPlaceholders.add(p);
15432
+ }
15433
+ }
15434
+ } catch {
15435
+ }
15436
+ }
15437
+ const configuredPlaceholders = config ? new Set(Object.keys(flattenStandardsConfig(config))) : /* @__PURE__ */ new Set();
15438
+ const unconfigured = /* @__PURE__ */ new Map();
15439
+ for (const placeholder of allPlaceholders) {
15440
+ if (!configuredPlaceholders.has(placeholder)) {
15441
+ const filesWithPlaceholder = [];
15442
+ for (const [file, placeholders] of placeholdersByFile) {
15443
+ if (placeholders.includes(placeholder)) {
15444
+ filesWithPlaceholder.push(file);
15445
+ }
15446
+ }
15447
+ unconfigured.set(placeholder, filesWithPlaceholder);
15448
+ }
15449
+ }
15450
+ return {
15451
+ unconfiguredPlaceholders: Array.from(unconfigured.entries()).map(([placeholder, files2]) => ({
15452
+ placeholder,
15453
+ files: files2
15454
+ })),
15455
+ totalPlaceholders: allPlaceholders.size,
15456
+ configuredPlaceholders: configuredPlaceholders.size
15457
+ };
15458
+ }
15459
+ function formatScanResult(result) {
15460
+ const lines = [];
15461
+ lines.push("Standards Placeholder Scan");
15462
+ lines.push("\u2500".repeat(40));
15463
+ lines.push(`Total placeholders found: ${result.totalPlaceholders}`);
15464
+ lines.push(`Already configured: ${result.configuredPlaceholders}`);
15465
+ lines.push(`Unconfigured: ${result.unconfiguredPlaceholders.length}`);
15466
+ if (result.unconfiguredPlaceholders.length > 0) {
15467
+ lines.push("");
15468
+ lines.push("Unconfigured placeholders:");
15469
+ for (const { placeholder, files } of result.unconfiguredPlaceholders) {
15470
+ lines.push(` ${placeholder}`);
15471
+ for (const file of files.slice(0, 3)) {
15472
+ lines.push(` \u2514\u2500 ${file}`);
15473
+ }
15474
+ if (files.length > 3) {
15475
+ lines.push(` \u2514\u2500 ... and ${files.length - 3} more files`);
15476
+ }
15477
+ }
15478
+ } else {
15479
+ lines.push("");
15480
+ lines.push("\u2713 All placeholders are configured!");
15481
+ }
15482
+ return lines.join("\n");
15483
+ }
15484
+
15485
+ // src/lib/standards/template-sync.ts
15486
+ init_esm_shims();
15487
+ import * as fs8 from "fs/promises";
15488
+ import * as path10 from "path";
15489
+ import ora4 from "ora";
15490
+ var STANDARDS_TEMPLATES = [
15491
+ "code-standards.md",
15492
+ "testing-standards.md",
15493
+ "documentation-standards.md",
15494
+ "design-standards.md",
15495
+ "security-standards.md",
15496
+ "performance-standards.md"
15497
+ ];
15498
+ async function fileExists2(filePath) {
15499
+ try {
15500
+ await fs8.access(filePath);
15501
+ return true;
15502
+ } catch {
15503
+ return false;
15504
+ }
15505
+ }
15506
+ async function createBackup(filePath) {
15507
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
15508
+ const backupPath = `${filePath}.backup-${timestamp}`;
15509
+ await fs8.copyFile(filePath, backupPath);
15510
+ return backupPath;
15511
+ }
15512
+ async function hasPlaceholders(filePath) {
15513
+ try {
15514
+ const content = await fs8.readFile(filePath, "utf-8");
15515
+ return content.includes("AUTO-GENERATED: Configured values") || /\{\{[A-Z_]+\}\}/.test(content);
15516
+ } catch {
15517
+ return false;
15518
+ }
15519
+ }
15520
+ async function syncTemplate(templateName, sourcePath, targetPath, options) {
15521
+ const sourceFile = path10.join(sourcePath, templateName);
15522
+ const targetFile = path10.join(targetPath, templateName);
15523
+ if (!await fileExists2(sourceFile)) {
15524
+ return { status: "skipped" };
15525
+ }
15526
+ const targetExists = await fileExists2(targetFile);
15527
+ if (targetExists) {
15528
+ const alreadyHasPlaceholders = await hasPlaceholders(targetFile);
15529
+ if (alreadyHasPlaceholders && !options.overwrite) {
15530
+ return { status: "skipped" };
15531
+ }
15532
+ let backupPath;
15533
+ if (options.backup) {
15534
+ backupPath = await createBackup(targetFile);
15535
+ }
15536
+ await fs8.copyFile(sourceFile, targetFile);
15537
+ return { status: "updated", backup: backupPath };
15538
+ }
15539
+ await fs8.mkdir(path10.dirname(targetFile), { recursive: true });
15540
+ await fs8.copyFile(sourceFile, targetFile);
15541
+ return { status: "created" };
15542
+ }
15543
+ async function syncStandardsTemplates(claudePath, options = {}) {
15544
+ const result = {
15545
+ created: [],
15546
+ updated: [],
15547
+ skipped: [],
15548
+ errors: []
15549
+ };
15550
+ const packagesTemplatesPath = path10.join(getTemplatesPath(), "docs", "standards");
15551
+ const projectTemplatesPath = path10.join(claudePath, "docs", "standards");
15552
+ await fs8.mkdir(projectTemplatesPath, { recursive: true });
15553
+ for (const template of STANDARDS_TEMPLATES) {
15554
+ try {
15555
+ const syncResult = await syncTemplate(
15556
+ template,
15557
+ packagesTemplatesPath,
15558
+ projectTemplatesPath,
15559
+ options
15560
+ );
15561
+ switch (syncResult.status) {
15562
+ case "created":
15563
+ result.created.push(template);
15564
+ break;
15565
+ case "updated":
15566
+ result.updated.push(template);
15567
+ break;
15568
+ case "skipped":
15569
+ result.skipped.push(template);
15570
+ break;
15571
+ }
15572
+ } catch (error) {
15573
+ result.errors.push(`${template}: ${String(error)}`);
15574
+ }
15575
+ }
15576
+ return result;
15577
+ }
15578
+ async function syncStandardsTemplatesWithSpinner(claudePath, options = {}) {
15579
+ const spinner2 = ora4("Syncing standards templates...").start();
15580
+ try {
15581
+ const result = await syncStandardsTemplates(claudePath, options);
15582
+ const total = result.created.length + result.updated.length;
15583
+ if (total > 0) {
15584
+ spinner2.succeed(
15585
+ `Synced ${total} template${total !== 1 ? "s" : ""} (${result.created.length} created, ${result.updated.length} updated)`
15586
+ );
15587
+ } else if (result.skipped.length > 0) {
15588
+ spinner2.info("All templates already up to date");
15589
+ } else {
15590
+ spinner2.warn("No templates to sync");
15591
+ }
15592
+ return result;
15593
+ } catch (error) {
15594
+ spinner2.fail("Failed to sync templates");
15595
+ throw error;
15596
+ }
15597
+ }
15598
+ async function checkTemplatesNeedUpdate(claudePath) {
15599
+ const projectTemplatesPath = path10.join(claudePath, "docs", "standards");
15600
+ const missing = [];
15601
+ const outdated = [];
15602
+ for (const template of STANDARDS_TEMPLATES) {
15603
+ const targetFile = path10.join(projectTemplatesPath, template);
15604
+ if (!await fileExists2(targetFile)) {
15605
+ missing.push(template);
15606
+ } else if (!await hasPlaceholders(targetFile)) {
15607
+ outdated.push(template);
15608
+ }
15609
+ }
15610
+ return {
15611
+ needsUpdate: missing.length > 0 || outdated.length > 0,
15612
+ missing,
15613
+ outdated
15614
+ };
15615
+ }
15616
+ function formatSyncResult(result) {
15617
+ const lines = [];
15618
+ lines.push("Template Sync Results");
15619
+ lines.push("\u2500".repeat(40));
15620
+ if (result.created.length > 0) {
15621
+ lines.push(`Created: ${result.created.length}`);
15622
+ for (const f of result.created) {
15623
+ lines.push(` \u2713 ${f}`);
15624
+ }
15625
+ }
15626
+ if (result.updated.length > 0) {
15627
+ lines.push(`Updated: ${result.updated.length}`);
15628
+ for (const f of result.updated) {
15629
+ lines.push(` \u2713 ${f}`);
15630
+ }
15631
+ }
15632
+ if (result.skipped.length > 0) {
15633
+ lines.push(`Skipped (already up to date): ${result.skipped.length}`);
15634
+ }
15635
+ if (result.errors.length > 0) {
15636
+ lines.push(`Errors: ${result.errors.length}`);
15637
+ for (const e of result.errors) {
15638
+ lines.push(` \u2717 ${e}`);
15639
+ }
15640
+ }
15641
+ return lines.join("\n");
15642
+ }
15643
+
15644
+ // src/cli/commands/standards.ts
15645
+ init_fs();
15646
+
15647
+ // src/cli/prompts/standards.ts
15648
+ init_esm_shims();
15649
+ async function promptStandardsConfig(options) {
15650
+ logger.section("Project Standards", "\u{1F4D0}");
15651
+ logger.info("Configure quality standards for your project");
15652
+ logger.newline();
15653
+ if (options?.category) {
15654
+ const existingConfig = options.defaults ?? DEFAULT_STANDARDS_CONFIG;
15655
+ const categoryConfig = await promptCategoryConfig(options.category, existingConfig);
15656
+ return {
15657
+ ...existingConfig,
15658
+ [options.category]: categoryConfig
15659
+ };
15660
+ }
15661
+ const enableStandards = await confirm({
15662
+ message: "Would you like to configure project standards?",
15663
+ default: true
15664
+ });
15665
+ if (!enableStandards) {
15666
+ return DEFAULT_STANDARDS_CONFIG;
15667
+ }
15668
+ const preset = await promptStandardsPreset();
15669
+ if (preset !== "custom") {
15670
+ const presetConfig = STANDARDS_PRESETS[preset];
15671
+ logger.success(`Using "${presetConfig.name}" preset`);
15672
+ return presetConfig.config;
15673
+ }
15674
+ logger.newline();
15675
+ logger.info("Configure each standards category:");
15676
+ const codeConfig = await promptCodeStandards(options?.defaults?.code);
15677
+ const testingConfig = await promptTestingStandards(options?.defaults?.testing);
15678
+ const documentationConfig = await promptDocumentationStandards(options?.defaults?.documentation);
15679
+ const designConfig = await promptDesignStandards(options?.defaults?.design);
15680
+ const securityConfig = await promptSecurityStandards(options?.defaults?.security);
15681
+ const performanceConfig = await promptPerformanceStandards(options?.defaults?.performance);
15682
+ return {
15683
+ code: codeConfig,
15684
+ testing: testingConfig,
15685
+ documentation: documentationConfig,
15686
+ design: designConfig,
15687
+ security: securityConfig,
15688
+ performance: performanceConfig
15689
+ };
15690
+ }
15691
+ async function promptStandardsPreset() {
15692
+ return select({
15693
+ message: "Choose a standards preset:",
15694
+ choices: Object.entries(STANDARDS_PRESETS).map(([key, preset]) => ({
15695
+ name: `${preset.name} - ${preset.description}`,
15696
+ value: key
15697
+ })),
15698
+ default: "balanced"
15699
+ });
15700
+ }
15701
+ async function promptCategoryConfig(category, existingConfig) {
15702
+ switch (category) {
15703
+ case "code":
15704
+ return promptCodeStandards(existingConfig.code);
15705
+ case "testing":
15706
+ return promptTestingStandards(existingConfig.testing);
15707
+ case "documentation":
15708
+ return promptDocumentationStandards(existingConfig.documentation);
15709
+ case "design":
15710
+ return promptDesignStandards(existingConfig.design);
15711
+ case "security":
15712
+ return promptSecurityStandards(existingConfig.security);
15713
+ case "performance":
15714
+ return promptPerformanceStandards(existingConfig.performance);
15715
+ }
15716
+ }
15717
+ async function promptCodeStandards(defaults) {
15718
+ const def = STANDARDS_DEFINITIONS.code;
15719
+ logger.newline();
15720
+ logger.subtitle(`${def.icon} ${def.name}`);
15721
+ const indentStyle = await select({
15722
+ message: "Indent style:",
15723
+ choices: [
15724
+ { name: "Spaces", value: "space" },
15725
+ { name: "Tabs", value: "tab" }
15726
+ ],
15727
+ default: defaults?.indentStyle ?? DEFAULT_STANDARDS_CONFIG.code.indentStyle
15728
+ });
15729
+ const indentSize = await select({
15730
+ message: "Indent size:",
15731
+ choices: [
15732
+ { name: "2 spaces", value: 2 },
15733
+ { name: "4 spaces", value: 4 }
15734
+ ],
15735
+ default: defaults?.indentSize ?? DEFAULT_STANDARDS_CONFIG.code.indentSize
15736
+ });
15737
+ const maxLineLength = await select({
15738
+ message: "Max line length:",
15739
+ choices: [
15740
+ { name: "80 characters", value: 80 },
15741
+ { name: "100 characters", value: 100 },
15742
+ { name: "120 characters", value: 120 }
15743
+ ],
15744
+ default: defaults?.maxLineLength ?? DEFAULT_STANDARDS_CONFIG.code.maxLineLength
15745
+ });
15746
+ const maxFileLines = await select({
15747
+ message: "Max file lines:",
15748
+ choices: [
15749
+ { name: "300 lines (strict)", value: 300 },
15750
+ { name: "500 lines (standard)", value: 500 },
15751
+ { name: "800 lines (relaxed)", value: 800 }
15752
+ ],
15753
+ default: defaults?.maxFileLines ?? DEFAULT_STANDARDS_CONFIG.code.maxFileLines
15754
+ });
15755
+ const quoteStyle = await select({
15756
+ message: "Quote style:",
15757
+ choices: [
15758
+ { name: "Single quotes", value: "single" },
15759
+ { name: "Double quotes", value: "double" }
15760
+ ],
15761
+ default: defaults?.quoteStyle ?? DEFAULT_STANDARDS_CONFIG.code.quoteStyle
15762
+ });
15763
+ const semicolons = await confirm({
15764
+ message: "Use semicolons?",
15765
+ default: defaults?.semicolons ?? DEFAULT_STANDARDS_CONFIG.code.semicolons
15766
+ });
15767
+ const trailingCommas = await select({
15768
+ message: "Trailing commas:",
15769
+ choices: [
15770
+ { name: "ES5 (recommended)", value: "es5" },
15771
+ { name: "All", value: "all" },
15772
+ { name: "None", value: "none" }
15773
+ ],
15774
+ default: defaults?.trailingCommas ?? DEFAULT_STANDARDS_CONFIG.code.trailingCommas
15775
+ });
15776
+ const allowAny = await confirm({
15777
+ message: 'Allow "any" type in TypeScript?',
15778
+ default: defaults?.allowAny ?? DEFAULT_STANDARDS_CONFIG.code.allowAny
15779
+ });
15780
+ const namedExportsOnly = await confirm({
15781
+ message: "Require named exports only (no default exports)?",
15782
+ default: defaults?.namedExportsOnly ?? DEFAULT_STANDARDS_CONFIG.code.namedExportsOnly
15783
+ });
15784
+ const roroPattern = await confirm({
15785
+ message: "Require RO-RO pattern (Receive Object, Return Object)?",
15786
+ default: defaults?.roroPattern ?? DEFAULT_STANDARDS_CONFIG.code.roroPattern
15787
+ });
15788
+ const jsDocRequired = await confirm({
15789
+ message: "Require JSDoc for all exports?",
15790
+ default: defaults?.jsDocRequired ?? DEFAULT_STANDARDS_CONFIG.code.jsDocRequired
15791
+ });
15792
+ return {
15793
+ indentStyle,
15794
+ indentSize,
15795
+ maxLineLength,
15796
+ maxFileLines,
15797
+ quoteStyle,
15798
+ semicolons,
15799
+ trailingCommas,
15800
+ allowAny,
15801
+ namedExportsOnly,
15802
+ roroPattern,
15803
+ jsDocRequired
15804
+ };
15805
+ }
15806
+ async function promptTestingStandards(defaults) {
15807
+ const def = STANDARDS_DEFINITIONS.testing;
15808
+ logger.newline();
15809
+ logger.subtitle(`${def.icon} ${def.name}`);
15810
+ const coverageTarget = await select({
15811
+ message: "Minimum code coverage:",
15812
+ choices: [
15813
+ { name: "60% (startup)", value: 60 },
15814
+ { name: "70% (relaxed)", value: 70 },
15815
+ { name: "80% (standard)", value: 80 },
15816
+ { name: "90% (strict)", value: 90 },
15817
+ { name: "95% (enterprise)", value: 95 }
15818
+ ],
15819
+ default: defaults?.coverageTarget ?? DEFAULT_STANDARDS_CONFIG.testing.coverageTarget
15820
+ });
15821
+ const tddRequired = await confirm({
15822
+ message: "Require TDD methodology (Red-Green-Refactor)?",
15823
+ default: defaults?.tddRequired ?? DEFAULT_STANDARDS_CONFIG.testing.tddRequired
15824
+ });
15825
+ const testPattern = await select({
15826
+ message: "Test pattern:",
15827
+ choices: [
15828
+ { name: "AAA (Arrange-Act-Assert)", value: "aaa" },
15829
+ { name: "GWT (Given-When-Then)", value: "gwt" }
15830
+ ],
15831
+ default: defaults?.testPattern ?? DEFAULT_STANDARDS_CONFIG.testing.testPattern
15832
+ });
15833
+ const testLocation = await select({
15834
+ message: "Test file location:",
15835
+ choices: [
15836
+ { name: "Separate (test/ folder)", value: "separate" },
15837
+ { name: "Colocated (__tests__ near source)", value: "colocated" }
15838
+ ],
15839
+ default: defaults?.testLocation ?? DEFAULT_STANDARDS_CONFIG.testing.testLocation
15840
+ });
15841
+ const unitTestMaxMs = await select({
15842
+ message: "Max time per unit test:",
15843
+ choices: [
15844
+ { name: "50ms (fast)", value: 50 },
15845
+ { name: "100ms (standard)", value: 100 },
15846
+ { name: "200ms (relaxed)", value: 200 }
15847
+ ],
15848
+ default: defaults?.unitTestMaxMs ?? DEFAULT_STANDARDS_CONFIG.testing.unitTestMaxMs
15849
+ });
15850
+ const integrationTestMaxMs = await select({
15851
+ message: "Max time per integration test:",
15852
+ choices: [
15853
+ { name: "500ms (fast)", value: 500 },
15854
+ { name: "1000ms (standard)", value: 1e3 },
15855
+ { name: "2000ms (relaxed)", value: 2e3 }
15856
+ ],
15857
+ default: defaults?.integrationTestMaxMs ?? DEFAULT_STANDARDS_CONFIG.testing.integrationTestMaxMs
15858
+ });
15859
+ return {
15860
+ coverageTarget,
15861
+ tddRequired,
15862
+ testPattern,
15863
+ testLocation,
15864
+ unitTestMaxMs,
15865
+ integrationTestMaxMs
15866
+ };
15867
+ }
15868
+ async function promptDocumentationStandards(defaults) {
15869
+ const def = STANDARDS_DEFINITIONS.documentation;
15870
+ logger.newline();
15871
+ logger.subtitle(`${def.icon} ${def.name}`);
15872
+ const jsDocLevel = await select({
15873
+ message: "JSDoc detail level:",
15874
+ choices: [
15875
+ { name: "Minimal (brief description)", value: "minimal" },
15876
+ { name: "Standard (description + params + returns)", value: "standard" },
15877
+ { name: "Comprehensive (full docs with examples)", value: "comprehensive" }
15878
+ ],
15879
+ default: defaults?.jsDocLevel ?? DEFAULT_STANDARDS_CONFIG.documentation.jsDocLevel
15880
+ });
15881
+ const requireExamples = await confirm({
15882
+ message: "Require @example in JSDoc?",
15883
+ default: defaults?.requireExamples ?? DEFAULT_STANDARDS_CONFIG.documentation.requireExamples
15884
+ });
15885
+ const changelogFormat = await select({
15886
+ message: "Changelog format:",
15887
+ choices: [
15888
+ { name: "Conventional (auto-generated from commits)", value: "conventional" },
15889
+ { name: "Keep a Changelog (manual, semantic)", value: "keepachangelog" }
15890
+ ],
15891
+ default: defaults?.changelogFormat ?? DEFAULT_STANDARDS_CONFIG.documentation.changelogFormat
15892
+ });
15893
+ const inlineCommentPolicy = await select({
15894
+ message: "Inline comment policy:",
15895
+ choices: [
15896
+ { name: "Why not What (explain reasoning)", value: "why-not-what" },
15897
+ { name: "Minimal (only when necessary)", value: "minimal" },
15898
+ { name: "Extensive (comment thoroughly)", value: "extensive" }
15899
+ ],
15900
+ default: defaults?.inlineCommentPolicy ?? DEFAULT_STANDARDS_CONFIG.documentation.inlineCommentPolicy
15901
+ });
15902
+ return {
15903
+ jsDocLevel,
15904
+ requireExamples,
15905
+ changelogFormat,
15906
+ inlineCommentPolicy
15907
+ };
15908
+ }
15909
+ async function promptDesignStandards(defaults) {
15910
+ const def = STANDARDS_DEFINITIONS.design;
15911
+ logger.newline();
15912
+ logger.subtitle(`${def.icon} ${def.name}`);
15913
+ const cssFramework = await select({
15914
+ message: "CSS framework:",
15915
+ choices: [
15916
+ { name: "Tailwind CSS", value: "tailwind" },
15917
+ { name: "CSS Modules", value: "css-modules" },
15918
+ { name: "Styled Components", value: "styled-components" },
15919
+ { name: "Vanilla CSS", value: "vanilla" }
15920
+ ],
15921
+ default: defaults?.cssFramework ?? DEFAULT_STANDARDS_CONFIG.design.cssFramework
15922
+ });
15923
+ const componentLibrary = await select({
15924
+ message: "Component library:",
15925
+ choices: [
15926
+ { name: "shadcn/ui", value: "shadcn" },
15927
+ { name: "Radix UI", value: "radix" },
15928
+ { name: "Headless UI", value: "headless" },
15929
+ { name: "None", value: "none" }
15930
+ ],
15931
+ default: defaults?.componentLibrary ?? DEFAULT_STANDARDS_CONFIG.design.componentLibrary
15932
+ });
15933
+ const accessibilityLevel = await select({
15934
+ message: "WCAG accessibility level:",
15935
+ choices: [
15936
+ { name: "Level A (minimum)", value: "A" },
15937
+ { name: "Level AA (recommended)", value: "AA" },
15938
+ { name: "Level AAA (highest)", value: "AAA" }
15939
+ ],
15940
+ default: defaults?.accessibilityLevel ?? DEFAULT_STANDARDS_CONFIG.design.accessibilityLevel
15941
+ });
15942
+ const darkModeSupport = await confirm({
15943
+ message: "Support dark mode?",
15944
+ default: defaults?.darkModeSupport ?? DEFAULT_STANDARDS_CONFIG.design.darkModeSupport
15945
+ });
15946
+ return {
15947
+ cssFramework,
15948
+ componentLibrary,
15949
+ accessibilityLevel,
15950
+ darkModeSupport
15951
+ };
15952
+ }
15953
+ async function promptSecurityStandards(defaults) {
15954
+ const def = STANDARDS_DEFINITIONS.security;
15955
+ logger.newline();
15956
+ logger.subtitle(`${def.icon} ${def.name}`);
15957
+ const authPattern = await select({
15958
+ message: "Authentication pattern:",
15959
+ choices: [
15960
+ { name: "JWT (JSON Web Tokens)", value: "jwt" },
15961
+ { name: "Session (server-side)", value: "session" },
15962
+ { name: "OAuth 2.0 / OIDC", value: "oauth" },
15963
+ { name: "None", value: "none" }
15964
+ ],
15965
+ default: defaults?.authPattern ?? DEFAULT_STANDARDS_CONFIG.security.authPattern
15966
+ });
15967
+ const inputValidation = await select({
15968
+ message: "Input validation library:",
15969
+ choices: [
15970
+ { name: "Zod (TypeScript-first)", value: "zod" },
15971
+ { name: "Yup (schema builder)", value: "yup" },
15972
+ { name: "Joi (data validation)", value: "joi" },
15973
+ { name: "Manual (custom)", value: "manual" }
15974
+ ],
15975
+ default: defaults?.inputValidation ?? DEFAULT_STANDARDS_CONFIG.security.inputValidation
15976
+ });
15977
+ const csrfProtection = await confirm({
15978
+ message: "Enable CSRF protection?",
15979
+ default: defaults?.csrfProtection ?? DEFAULT_STANDARDS_CONFIG.security.csrfProtection
15980
+ });
15981
+ const rateLimiting = await confirm({
15982
+ message: "Enable rate limiting?",
15983
+ default: defaults?.rateLimiting ?? DEFAULT_STANDARDS_CONFIG.security.rateLimiting
15984
+ });
15985
+ return {
15986
+ authPattern,
15987
+ inputValidation,
15988
+ csrfProtection,
15989
+ rateLimiting
15990
+ };
15991
+ }
15992
+ async function promptPerformanceStandards(defaults) {
15993
+ const def = STANDARDS_DEFINITIONS.performance;
15994
+ logger.newline();
15995
+ logger.subtitle(`${def.icon} ${def.name}`);
15996
+ const lcpTarget = await select({
15997
+ message: "LCP target (Largest Contentful Paint):",
15998
+ choices: [
15999
+ { name: "1500ms (excellent)", value: 1500 },
16000
+ { name: "2000ms (good)", value: 2e3 },
16001
+ { name: "2500ms (standard)", value: 2500 },
16002
+ { name: "4000ms (needs improvement)", value: 4e3 }
16003
+ ],
16004
+ default: defaults?.lcpTarget ?? DEFAULT_STANDARDS_CONFIG.performance.lcpTarget
16005
+ });
16006
+ const fidTarget = await select({
16007
+ message: "FID target (First Input Delay):",
16008
+ choices: [
16009
+ { name: "50ms (excellent)", value: 50 },
16010
+ { name: "100ms (good)", value: 100 },
16011
+ { name: "200ms (standard)", value: 200 },
16012
+ { name: "300ms (needs improvement)", value: 300 }
16013
+ ],
16014
+ default: defaults?.fidTarget ?? DEFAULT_STANDARDS_CONFIG.performance.fidTarget
16015
+ });
16016
+ const clsTarget = await select({
16017
+ message: "CLS target (Cumulative Layout Shift):",
16018
+ choices: [
16019
+ { name: "0.05 (excellent)", value: 0.05 },
16020
+ { name: "0.1 (good)", value: 0.1 },
16021
+ { name: "0.15 (standard)", value: 0.15 },
16022
+ { name: "0.25 (needs improvement)", value: 0.25 }
16023
+ ],
16024
+ default: defaults?.clsTarget ?? DEFAULT_STANDARDS_CONFIG.performance.clsTarget
16025
+ });
16026
+ const bundleSizeTargetKb = await select({
16027
+ message: "Bundle size target (KB):",
16028
+ choices: [
16029
+ { name: "100KB (strict)", value: 100 },
16030
+ { name: "150KB (good)", value: 150 },
16031
+ { name: "250KB (standard)", value: 250 },
16032
+ { name: "500KB (relaxed)", value: 500 }
16033
+ ],
16034
+ default: defaults?.bundleSizeTargetKb ?? DEFAULT_STANDARDS_CONFIG.performance.bundleSizeTargetKb
16035
+ });
16036
+ const apiResponseTargetMs = await select({
16037
+ message: "API response time target (ms):",
16038
+ choices: [
16039
+ { name: "100ms (fast)", value: 100 },
16040
+ { name: "200ms (good)", value: 200 },
16041
+ { name: "300ms (standard)", value: 300 },
16042
+ { name: "500ms (relaxed)", value: 500 }
16043
+ ],
16044
+ default: defaults?.apiResponseTargetMs ?? DEFAULT_STANDARDS_CONFIG.performance.apiResponseTargetMs
16045
+ });
16046
+ return {
16047
+ lcpTarget,
16048
+ fidTarget,
16049
+ clsTarget,
16050
+ bundleSizeTargetKb,
16051
+ apiResponseTargetMs
16052
+ };
16053
+ }
16054
+ function showStandardsSummary(config) {
16055
+ logger.newline();
16056
+ logger.subtitle("Standards Summary");
16057
+ logger.item(
16058
+ `Code: ${config.code.indentSize} ${config.code.indentStyle}s, ${config.code.quoteStyle} quotes`
16059
+ );
16060
+ logger.info(
16061
+ colors.muted(
16062
+ ` Max: ${config.code.maxLineLength} chars/line, ${config.code.maxFileLines} lines/file`
16063
+ )
16064
+ );
16065
+ logger.info(
16066
+ colors.muted(
16067
+ ` Rules: ${config.code.allowAny ? "any allowed" : "no any"}, ${config.code.namedExportsOnly ? "named exports" : "default exports"}`
16068
+ )
16069
+ );
16070
+ logger.item(
16071
+ `Testing: ${config.testing.coverageTarget}% coverage, ${config.testing.tddRequired ? "TDD required" : "TDD optional"}`
16072
+ );
16073
+ logger.info(
16074
+ colors.muted(
16075
+ ` Pattern: ${config.testing.testPattern.toUpperCase()}, Location: ${config.testing.testLocation}`
16076
+ )
16077
+ );
16078
+ logger.item(`Documentation: ${config.documentation.jsDocLevel} JSDoc`);
16079
+ logger.info(colors.muted(` Comments: ${config.documentation.inlineCommentPolicy}`));
16080
+ logger.item(`Design: ${config.design.cssFramework}, ${config.design.componentLibrary}`);
16081
+ logger.info(
16082
+ colors.muted(
16083
+ ` A11y: WCAG ${config.design.accessibilityLevel}, ${config.design.darkModeSupport ? "dark mode" : "no dark mode"}`
16084
+ )
16085
+ );
16086
+ logger.item(`Security: ${config.security.authPattern}, ${config.security.inputValidation}`);
16087
+ logger.info(
16088
+ colors.muted(
16089
+ ` ${config.security.csrfProtection ? "CSRF" : "no CSRF"}, ${config.security.rateLimiting ? "rate limiting" : "no rate limiting"}`
16090
+ )
16091
+ );
16092
+ logger.item(
16093
+ `Performance: LCP ${config.performance.lcpTarget}ms, FID ${config.performance.fidTarget}ms`
16094
+ );
16095
+ logger.info(
16096
+ colors.muted(
16097
+ ` Bundle: ${config.performance.bundleSizeTargetKb}KB, API: ${config.performance.apiResponseTargetMs}ms`
16098
+ )
16099
+ );
16100
+ }
16101
+ async function confirmStandardsConfig(config) {
16102
+ showStandardsSummary(config);
16103
+ logger.newline();
16104
+ return confirm({
16105
+ message: "Apply these standards?",
16106
+ default: true
16107
+ });
16108
+ }
16109
+
16110
+ // src/cli/commands/standards.ts
16111
+ function createStandardsCommand() {
16112
+ const cmd = new Command8("standards").description("Configure project standards interactively").argument("[path]", "Project path (default: current directory)").option("--scan", "Scan for unconfigured standard placeholders").option(
16113
+ "-c, --category <name>",
16114
+ "Configure specific category (code|testing|documentation|design|security|performance)"
16115
+ ).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);
16116
+ return cmd;
16117
+ }
16118
+ async function runStandards(path11, options) {
16119
+ const projectPath = resolvePath(path11 || ".");
16120
+ const claudePath = joinPath(projectPath, ".claude");
16121
+ logger.configure({ verbose: options.verbose, silent: false });
16122
+ logger.title("Project Standards Configuration");
16123
+ try {
16124
+ if (options.scan) {
16125
+ await scanMode2(claudePath, projectPath);
16126
+ return;
16127
+ }
16128
+ if (options.updateTemplates) {
16129
+ await updateTemplatesMode(claudePath, options);
16130
+ return;
16131
+ }
16132
+ if (options.preview) {
16133
+ await previewMode2(claudePath, projectPath);
16134
+ return;
16135
+ }
16136
+ if (options.yes) {
16137
+ await defaultsMode(claudePath, projectPath, options);
16138
+ return;
16139
+ }
16140
+ await interactiveMode2(claudePath, projectPath, options);
16141
+ } catch (error) {
16142
+ logger.error(
16143
+ `Standards configuration failed: ${error instanceof Error ? error.message : error}`
16144
+ );
16145
+ process.exit(1);
16146
+ }
16147
+ }
16148
+ async function scanMode2(claudePath, projectPath) {
16149
+ logger.info(`Scanning ${colors.primary(claudePath)} for standard placeholders...`);
16150
+ logger.newline();
16151
+ const existingConfig = await readConfig(projectPath);
16152
+ const standardsConfig = existingConfig?.extras?.standards;
16153
+ const scanResult = await scanStandardsPlaceholders(claudePath, standardsConfig);
16154
+ logger.info(formatScanResult(scanResult));
16155
+ if (scanResult.unconfiguredPlaceholders.length > 0) {
16156
+ logger.newline();
16157
+ logger.info("Run `claude-config standards` to configure them");
16158
+ }
16159
+ }
16160
+ async function updateTemplatesMode(claudePath, options) {
16161
+ logger.info("Checking for template updates...");
16162
+ logger.newline();
16163
+ const status = await checkTemplatesNeedUpdate(claudePath);
16164
+ if (!status.needsUpdate) {
16165
+ logger.success("All templates are up to date!");
16166
+ return;
16167
+ }
16168
+ if (status.missing.length > 0) {
16169
+ logger.info(`Missing templates (${status.missing.length}):`);
16170
+ for (const template of status.missing) {
16171
+ logger.info(` ${colors.warning("+")} ${template}`);
16172
+ }
16173
+ logger.newline();
16174
+ }
16175
+ if (status.outdated.length > 0) {
16176
+ logger.info(`Outdated templates (${status.outdated.length}):`);
16177
+ for (const template of status.outdated) {
16178
+ logger.info(` ${colors.primary("~")} ${template}`);
16179
+ }
16180
+ logger.newline();
16181
+ }
16182
+ const result = await syncStandardsTemplatesWithSpinner(claudePath, {
16183
+ overwrite: true,
16184
+ backup: true
16185
+ });
16186
+ if (options.verbose) {
16187
+ logger.newline();
16188
+ logger.info(formatSyncResult(result));
16189
+ }
16190
+ logger.newline();
16191
+ logger.success("Templates updated successfully!");
16192
+ logger.info("Run `claude-config standards` to configure the new placeholders");
16193
+ }
16194
+ async function previewMode2(claudePath, projectPath) {
16195
+ const existingConfig = await readConfig(projectPath);
16196
+ if (!existingConfig?.extras?.standards) {
16197
+ logger.warn("No standards configuration found");
16198
+ logger.info("Run `claude-config standards` first to set up configuration");
16199
+ return;
16200
+ }
16201
+ logger.info("Preview of replacements:");
16202
+ logger.newline();
16203
+ const replacements = await previewStandardsReplacements(
16204
+ claudePath,
16205
+ existingConfig.extras.standards
16206
+ );
16207
+ if (replacements.length === 0) {
16208
+ logger.info("No standard placeholders to replace");
16209
+ return;
16210
+ }
16211
+ const byFile = {};
16212
+ for (const r of replacements) {
16213
+ if (!byFile[r.file]) {
16214
+ byFile[r.file] = [];
16215
+ }
16216
+ byFile[r.file].push({ placeholder: r.placeholder, value: r.value });
16217
+ }
16218
+ for (const [file, changes] of Object.entries(byFile)) {
16219
+ logger.subtitle(file);
16220
+ for (const change of changes) {
16221
+ logger.info(` ${change.placeholder} \u2192 ${colors.primary(change.value)}`);
16222
+ }
16223
+ logger.newline();
16224
+ }
16225
+ logger.success(
16226
+ `Total: ${replacements.length} replacements in ${Object.keys(byFile).length} files`
16227
+ );
16228
+ }
16229
+ async function defaultsMode(claudePath, projectPath, options) {
16230
+ const templateStatus = await checkTemplatesNeedUpdate(claudePath);
16231
+ if (templateStatus.needsUpdate) {
16232
+ logger.warn("Some templates are missing or outdated:");
16233
+ if (templateStatus.missing.length > 0) {
16234
+ logger.info(` Missing: ${templateStatus.missing.length} template(s)`);
16235
+ }
16236
+ if (templateStatus.outdated.length > 0) {
16237
+ logger.info(` Outdated: ${templateStatus.outdated.length} template(s)`);
16238
+ }
16239
+ logger.newline();
16240
+ logger.info(
16241
+ `Run ${colors.primary("qazuor-claude-config standards --update-templates")} first to sync templates`
16242
+ );
16243
+ logger.newline();
16244
+ }
16245
+ logger.info("Applying default standards configuration...");
16246
+ logger.newline();
16247
+ const standardsConfig = DEFAULT_STANDARDS_CONFIG;
16248
+ showStandardsSummary(standardsConfig);
16249
+ logger.newline();
16250
+ const report = await replaceStandardsWithSpinner(claudePath, standardsConfig);
16251
+ if (options.verbose) {
16252
+ logger.newline();
16253
+ logger.info(formatStandardsReport(report));
16254
+ }
16255
+ if (report.modifiedFiles.length === 0 && report.unusedPlaceholders.length > 0) {
16256
+ logger.newline();
16257
+ logger.warn("No placeholders were replaced in templates.");
16258
+ logger.info(
16259
+ `Your templates might be outdated. Run ${colors.primary("--update-templates")} to sync them.`
16260
+ );
16261
+ }
16262
+ await saveStandardsConfig(projectPath, standardsConfig);
16263
+ logger.newline();
16264
+ logger.success("Standards configuration complete!");
16265
+ }
16266
+ async function interactiveMode2(claudePath, projectPath, options) {
16267
+ const templateStatus = await checkTemplatesNeedUpdate(claudePath);
16268
+ if (templateStatus.needsUpdate) {
16269
+ logger.warn("Some templates are missing or outdated:");
16270
+ if (templateStatus.missing.length > 0) {
16271
+ logger.info(` Missing: ${templateStatus.missing.length} template(s)`);
16272
+ }
16273
+ if (templateStatus.outdated.length > 0) {
16274
+ logger.info(` Outdated: ${templateStatus.outdated.length} template(s)`);
16275
+ }
16276
+ logger.newline();
16277
+ logger.info(
16278
+ `Run ${colors.primary("qazuor-claude-config standards --update-templates")} first to sync templates`
16279
+ );
16280
+ logger.newline();
16281
+ }
16282
+ const existingConfig = await readConfig(projectPath);
16283
+ const existingStandards = existingConfig?.extras?.standards;
16284
+ const standardsConfig = await promptStandardsConfig({
16285
+ defaults: existingStandards,
16286
+ category: options.category
16287
+ });
16288
+ const confirmed = await confirmStandardsConfig(standardsConfig);
16289
+ if (!confirmed) {
16290
+ logger.warn("Configuration cancelled");
16291
+ return;
16292
+ }
16293
+ const report = await replaceStandardsWithSpinner(claudePath, standardsConfig);
16294
+ if (options.verbose) {
16295
+ logger.newline();
16296
+ logger.info(formatStandardsReport(report));
16297
+ }
16298
+ if (report.modifiedFiles.length === 0 && report.unusedPlaceholders.length > 0) {
16299
+ logger.newline();
16300
+ logger.warn("No placeholders were replaced in templates.");
16301
+ logger.info(
16302
+ `Your templates might be outdated. Run ${colors.primary("--update-templates")} to sync them.`
16303
+ );
16304
+ }
16305
+ await saveStandardsConfig(projectPath, standardsConfig);
16306
+ logger.newline();
16307
+ logger.success("Standards configuration complete!");
16308
+ }
16309
+ async function saveStandardsConfig(projectPath, standardsConfig) {
16310
+ const existingConfig = await readConfig(projectPath);
16311
+ if (existingConfig) {
16312
+ existingConfig.extras = {
16313
+ ...existingConfig.extras,
16314
+ standards: standardsConfig
16315
+ };
16316
+ existingConfig.customizations.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
16317
+ await writeConfig(projectPath, existingConfig);
16318
+ logger.success("Configuration saved to .claude/qazuor-claude-config.json");
16319
+ } else {
16320
+ logger.warn("No existing config found - standards not saved");
16321
+ logger.info("Run `claude-config init` first to initialize the project");
16322
+ }
16323
+ }
16324
+
13284
16325
  // src/cli/index.ts
13285
16326
  var VERSION2 = "0.1.0";
13286
- var program = new Command8().name("qazuor-claude-config").description("CLI tool to install and manage Claude Code configurations").version(VERSION2);
16327
+ var program = new Command9().name("qazuor-claude-config").description("CLI tool to install and manage Claude Code configurations").version(VERSION2);
13287
16328
  program.addCommand(createInitCommand());
13288
16329
  program.addCommand(createListCommand());
13289
16330
  program.addCommand(createAddCommand());
@@ -13291,6 +16332,7 @@ program.addCommand(createRemoveCommand());
13291
16332
  program.addCommand(createStatusCommand());
13292
16333
  program.addCommand(createUpdateCommand());
13293
16334
  program.addCommand(createConfigureCommand());
16335
+ program.addCommand(createStandardsCommand());
13294
16336
 
13295
16337
  // src/lib/utils/banner.ts
13296
16338
  init_esm_shims();