@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.
- package/README.md +395 -50
- package/dist/bin.cjs +3207 -165
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +3207 -165
- package/dist/bin.js.map +1 -1
- package/dist/index.cjs +75 -58
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +284 -1
- package/dist/index.d.ts +284 -1
- package/dist/index.js +75 -58
- package/dist/index.js.map +1 -1
- package/package.json +24 -24
- package/templates/CLAUDE.md.template +60 -5
- package/templates/agents/README.md +58 -39
- package/templates/agents/_registry.json +43 -202
- package/templates/agents/engineering/{hono-engineer.md → api-engineer.md} +61 -70
- package/templates/agents/engineering/database-engineer.md +253 -0
- package/templates/agents/engineering/frontend-engineer.md +302 -0
- package/templates/docs/_registry.json +54 -0
- package/templates/docs/standards/code-standards.md +20 -0
- package/templates/docs/standards/design-standards.md +13 -0
- package/templates/docs/standards/documentation-standards.md +13 -0
- package/templates/docs/standards/performance-standards.md +524 -0
- package/templates/docs/standards/security-standards.md +496 -0
- package/templates/docs/standards/testing-standards.md +15 -0
- package/templates/hooks/on-notification.sh +0 -0
- package/templates/scripts/add-changelogs.sh +0 -0
- package/templates/scripts/generate-code-registry.ts +0 -0
- package/templates/scripts/health-check.sh +0 -0
- package/templates/scripts/sync-registry.sh +0 -0
- package/templates/scripts/telemetry-report.ts +0 -0
- package/templates/scripts/validate-docs.sh +0 -0
- package/templates/scripts/validate-registry.sh +0 -0
- package/templates/scripts/validate-structure.sh +0 -0
- package/templates/scripts/worktree-cleanup.sh +0 -0
- package/templates/scripts/worktree-create.sh +0 -0
- package/templates/skills/README.md +99 -90
- package/templates/skills/_registry.json +323 -16
- package/templates/skills/api-frameworks/express-patterns.md +411 -0
- package/templates/skills/api-frameworks/fastify-patterns.md +419 -0
- package/templates/skills/api-frameworks/hono-patterns.md +388 -0
- package/templates/skills/api-frameworks/nestjs-patterns.md +497 -0
- package/templates/skills/database/drizzle-patterns.md +449 -0
- package/templates/skills/database/mongoose-patterns.md +503 -0
- package/templates/skills/database/prisma-patterns.md +487 -0
- package/templates/skills/frontend-frameworks/astro-patterns.md +415 -0
- package/templates/skills/frontend-frameworks/nextjs-patterns.md +470 -0
- package/templates/skills/frontend-frameworks/react-patterns.md +516 -0
- package/templates/skills/frontend-frameworks/tanstack-start-patterns.md +469 -0
- package/templates/skills/patterns/atdd-methodology.md +364 -0
- package/templates/skills/patterns/bdd-methodology.md +281 -0
- package/templates/skills/patterns/clean-architecture.md +444 -0
- package/templates/skills/patterns/hexagonal-architecture.md +567 -0
- package/templates/skills/patterns/vertical-slice-architecture.md +502 -0
- package/templates/agents/engineering/astro-engineer.md +0 -293
- package/templates/agents/engineering/db-drizzle-engineer.md +0 -360
- package/templates/agents/engineering/express-engineer.md +0 -316
- package/templates/agents/engineering/fastify-engineer.md +0 -399
- package/templates/agents/engineering/mongoose-engineer.md +0 -473
- package/templates/agents/engineering/nestjs-engineer.md +0 -429
- package/templates/agents/engineering/nextjs-engineer.md +0 -451
- package/templates/agents/engineering/prisma-engineer.md +0 -432
- package/templates/agents/engineering/react-senior-dev.md +0 -394
- package/templates/agents/engineering/tanstack-start-engineer.md +0 -447
package/dist/bin.cjs
CHANGED
|
@@ -230,7 +230,7 @@ var import_node_module = require("module");
|
|
|
230
230
|
|
|
231
231
|
// src/cli/index.ts
|
|
232
232
|
init_cjs_shims();
|
|
233
|
-
var
|
|
233
|
+
var import_commander9 = require("commander");
|
|
234
234
|
|
|
235
235
|
// src/cli/commands/index.ts
|
|
236
236
|
init_cjs_shims();
|
|
@@ -274,15 +274,10 @@ var BUNDLES = [
|
|
|
274
274
|
moduleDetails: {
|
|
275
275
|
agents: [
|
|
276
276
|
{
|
|
277
|
-
id: "
|
|
278
|
-
role: "
|
|
277
|
+
id: "frontend-engineer",
|
|
278
|
+
role: "Frontend Development",
|
|
279
279
|
responsibilities: ["Component design", "State management", "Performance optimization"]
|
|
280
280
|
},
|
|
281
|
-
{
|
|
282
|
-
id: "tanstack-start-engineer",
|
|
283
|
-
role: "TanStack Specialist",
|
|
284
|
-
responsibilities: ["Router setup", "Query patterns", "SSR configuration"]
|
|
285
|
-
},
|
|
286
281
|
{
|
|
287
282
|
id: "ux-ui-designer",
|
|
288
283
|
role: "UI/UX Design",
|
|
@@ -290,6 +285,8 @@ var BUNDLES = [
|
|
|
290
285
|
}
|
|
291
286
|
],
|
|
292
287
|
skills: [
|
|
288
|
+
{ id: "react-patterns", purpose: "React component patterns" },
|
|
289
|
+
{ id: "tanstack-start-patterns", purpose: "TanStack Router/Start patterns" },
|
|
293
290
|
{ id: "web-app-testing", purpose: "React Testing Library patterns" },
|
|
294
291
|
{ id: "shadcn-specialist", purpose: "Shadcn UI component usage" },
|
|
295
292
|
{ id: "accessibility-audit", purpose: "WCAG compliance" },
|
|
@@ -301,9 +298,10 @@ var BUNDLES = [
|
|
|
301
298
|
docs: [{ id: "design-standards", topic: "UI/UX design standards" }]
|
|
302
299
|
},
|
|
303
300
|
modules: [
|
|
304
|
-
{ id: "
|
|
305
|
-
{ id: "tanstack-start-engineer", category: "agents" },
|
|
301
|
+
{ id: "frontend-engineer", category: "agents" },
|
|
306
302
|
{ id: "ux-ui-designer", category: "agents" },
|
|
303
|
+
{ id: "react-patterns", category: "skills" },
|
|
304
|
+
{ id: "tanstack-start-patterns", category: "skills" },
|
|
307
305
|
{ id: "web-app-testing", category: "skills" },
|
|
308
306
|
{ id: "shadcn-specialist", category: "skills" },
|
|
309
307
|
{ id: "accessibility-audit", category: "skills" },
|
|
@@ -339,15 +337,10 @@ var BUNDLES = [
|
|
|
339
337
|
moduleDetails: {
|
|
340
338
|
agents: [
|
|
341
339
|
{
|
|
342
|
-
id: "
|
|
343
|
-
role: "
|
|
340
|
+
id: "frontend-engineer",
|
|
341
|
+
role: "Frontend Development",
|
|
344
342
|
responsibilities: ["Routing", "Islands architecture", "Build optimization"]
|
|
345
343
|
},
|
|
346
|
-
{
|
|
347
|
-
id: "react-senior-dev",
|
|
348
|
-
role: "React Components",
|
|
349
|
-
responsibilities: ["Interactive components", "Client hydration"]
|
|
350
|
-
},
|
|
351
344
|
{
|
|
352
345
|
id: "seo-ai-specialist",
|
|
353
346
|
role: "SEO Optimization",
|
|
@@ -355,6 +348,8 @@ var BUNDLES = [
|
|
|
355
348
|
}
|
|
356
349
|
],
|
|
357
350
|
skills: [
|
|
351
|
+
{ id: "astro-patterns", purpose: "Astro-specific patterns" },
|
|
352
|
+
{ id: "react-patterns", purpose: "React island components" },
|
|
358
353
|
{ id: "web-app-testing", purpose: "Component testing" },
|
|
359
354
|
{ id: "vercel-specialist", purpose: "Deployment optimization" },
|
|
360
355
|
{ id: "performance-audit", purpose: "Core Web Vitals" }
|
|
@@ -363,9 +358,10 @@ var BUNDLES = [
|
|
|
363
358
|
docs: []
|
|
364
359
|
},
|
|
365
360
|
modules: [
|
|
366
|
-
{ id: "
|
|
367
|
-
{ id: "react-senior-dev", category: "agents" },
|
|
361
|
+
{ id: "frontend-engineer", category: "agents" },
|
|
368
362
|
{ id: "seo-ai-specialist", category: "agents" },
|
|
363
|
+
{ id: "astro-patterns", category: "skills" },
|
|
364
|
+
{ id: "react-patterns", category: "skills" },
|
|
369
365
|
{ id: "web-app-testing", category: "skills" },
|
|
370
366
|
{ id: "vercel-specialist", category: "skills" },
|
|
371
367
|
{ id: "performance-audit", category: "skills" }
|
|
@@ -398,17 +394,12 @@ var BUNDLES = [
|
|
|
398
394
|
moduleDetails: {
|
|
399
395
|
agents: [
|
|
400
396
|
{
|
|
401
|
-
id: "
|
|
402
|
-
role: "
|
|
403
|
-
responsibilities: ["App Router", "Server Actions", "
|
|
404
|
-
},
|
|
405
|
-
{
|
|
406
|
-
id: "react-senior-dev",
|
|
407
|
-
role: "React Components",
|
|
408
|
-
responsibilities: ["Client components", "State management"]
|
|
397
|
+
id: "frontend-engineer",
|
|
398
|
+
role: "Frontend Development",
|
|
399
|
+
responsibilities: ["App Router", "Server Actions", "Client components"]
|
|
409
400
|
},
|
|
410
401
|
{
|
|
411
|
-
id: "
|
|
402
|
+
id: "database-engineer",
|
|
412
403
|
role: "Database",
|
|
413
404
|
responsibilities: ["Schema design", "Migrations", "Query optimization"]
|
|
414
405
|
},
|
|
@@ -419,6 +410,9 @@ var BUNDLES = [
|
|
|
419
410
|
}
|
|
420
411
|
],
|
|
421
412
|
skills: [
|
|
413
|
+
{ id: "nextjs-patterns", purpose: "Next.js App Router patterns" },
|
|
414
|
+
{ id: "react-patterns", purpose: "React component patterns" },
|
|
415
|
+
{ id: "prisma-patterns", purpose: "Prisma ORM patterns" },
|
|
422
416
|
{ id: "web-app-testing", purpose: "Next.js testing patterns" },
|
|
423
417
|
{ id: "shadcn-specialist", purpose: "UI components" },
|
|
424
418
|
{ id: "vercel-specialist", purpose: "Deployment" },
|
|
@@ -430,10 +424,12 @@ var BUNDLES = [
|
|
|
430
424
|
docs: []
|
|
431
425
|
},
|
|
432
426
|
modules: [
|
|
433
|
-
{ id: "
|
|
434
|
-
{ id: "
|
|
435
|
-
{ id: "prisma-engineer", category: "agents" },
|
|
427
|
+
{ id: "frontend-engineer", category: "agents" },
|
|
428
|
+
{ id: "database-engineer", category: "agents" },
|
|
436
429
|
{ id: "ux-ui-designer", category: "agents" },
|
|
430
|
+
{ id: "nextjs-patterns", category: "skills" },
|
|
431
|
+
{ id: "react-patterns", category: "skills" },
|
|
432
|
+
{ id: "prisma-patterns", category: "skills" },
|
|
437
433
|
{ id: "web-app-testing", category: "skills" },
|
|
438
434
|
{ id: "shadcn-specialist", category: "skills" },
|
|
439
435
|
{ id: "vercel-specialist", category: "skills" },
|
|
@@ -469,12 +465,12 @@ var BUNDLES = [
|
|
|
469
465
|
moduleDetails: {
|
|
470
466
|
agents: [
|
|
471
467
|
{
|
|
472
|
-
id: "
|
|
473
|
-
role: "
|
|
468
|
+
id: "api-engineer",
|
|
469
|
+
role: "API Development",
|
|
474
470
|
responsibilities: ["Route design", "Middleware", "Error handling"]
|
|
475
471
|
},
|
|
476
472
|
{
|
|
477
|
-
id: "
|
|
473
|
+
id: "database-engineer",
|
|
478
474
|
role: "Database",
|
|
479
475
|
responsibilities: ["Schema", "Migrations", "Queries"]
|
|
480
476
|
},
|
|
@@ -485,6 +481,8 @@ var BUNDLES = [
|
|
|
485
481
|
}
|
|
486
482
|
],
|
|
487
483
|
skills: [
|
|
484
|
+
{ id: "express-patterns", purpose: "Express.js patterns" },
|
|
485
|
+
{ id: "prisma-patterns", purpose: "Prisma ORM patterns" },
|
|
488
486
|
{ id: "api-app-testing", purpose: "API testing with supertest" },
|
|
489
487
|
{ id: "error-handling-patterns", purpose: "Error middleware" },
|
|
490
488
|
{ id: "security-testing", purpose: "Security best practices" }
|
|
@@ -493,9 +491,11 @@ var BUNDLES = [
|
|
|
493
491
|
docs: [{ id: "architecture-patterns", topic: "API architecture patterns" }]
|
|
494
492
|
},
|
|
495
493
|
modules: [
|
|
496
|
-
{ id: "
|
|
497
|
-
{ id: "
|
|
494
|
+
{ id: "api-engineer", category: "agents" },
|
|
495
|
+
{ id: "database-engineer", category: "agents" },
|
|
498
496
|
{ id: "node-typescript-engineer", category: "agents" },
|
|
497
|
+
{ id: "express-patterns", category: "skills" },
|
|
498
|
+
{ id: "prisma-patterns", category: "skills" },
|
|
499
499
|
{ id: "api-app-testing", category: "skills" },
|
|
500
500
|
{ id: "error-handling-patterns", category: "skills" },
|
|
501
501
|
{ id: "security-testing", category: "skills" },
|
|
@@ -529,13 +529,13 @@ var BUNDLES = [
|
|
|
529
529
|
moduleDetails: {
|
|
530
530
|
agents: [
|
|
531
531
|
{
|
|
532
|
-
id: "
|
|
533
|
-
role: "
|
|
532
|
+
id: "api-engineer",
|
|
533
|
+
role: "API Development",
|
|
534
534
|
responsibilities: ["Route handlers", "Middleware", "OpenAPI integration"]
|
|
535
535
|
},
|
|
536
536
|
{
|
|
537
|
-
id: "
|
|
538
|
-
role: "
|
|
537
|
+
id: "database-engineer",
|
|
538
|
+
role: "Database",
|
|
539
539
|
responsibilities: ["Schema design", "Migrations", "Type-safe queries"]
|
|
540
540
|
},
|
|
541
541
|
{
|
|
@@ -545,6 +545,8 @@ var BUNDLES = [
|
|
|
545
545
|
}
|
|
546
546
|
],
|
|
547
547
|
skills: [
|
|
548
|
+
{ id: "hono-patterns", purpose: "Hono framework patterns" },
|
|
549
|
+
{ id: "drizzle-patterns", purpose: "Drizzle ORM patterns" },
|
|
548
550
|
{ id: "api-app-testing", purpose: "Hono testing patterns" },
|
|
549
551
|
{ id: "error-handling-patterns", purpose: "Error middleware" },
|
|
550
552
|
{ id: "security-testing", purpose: "Security validation" }
|
|
@@ -553,9 +555,11 @@ var BUNDLES = [
|
|
|
553
555
|
docs: [{ id: "architecture-patterns", topic: "API architecture patterns" }]
|
|
554
556
|
},
|
|
555
557
|
modules: [
|
|
556
|
-
{ id: "
|
|
557
|
-
{ id: "
|
|
558
|
+
{ id: "api-engineer", category: "agents" },
|
|
559
|
+
{ id: "database-engineer", category: "agents" },
|
|
558
560
|
{ id: "node-typescript-engineer", category: "agents" },
|
|
561
|
+
{ id: "hono-patterns", category: "skills" },
|
|
562
|
+
{ id: "drizzle-patterns", category: "skills" },
|
|
559
563
|
{ id: "api-app-testing", category: "skills" },
|
|
560
564
|
{ id: "error-handling-patterns", category: "skills" },
|
|
561
565
|
{ id: "security-testing", category: "skills" },
|
|
@@ -786,7 +790,8 @@ var BUNDLES = [
|
|
|
786
790
|
tags: ["database", "drizzle", "orm"],
|
|
787
791
|
alternativeTo: ["prisma-database", "mongoose-database"],
|
|
788
792
|
modules: [
|
|
789
|
-
{ id: "
|
|
793
|
+
{ id: "database-engineer", category: "agents" },
|
|
794
|
+
{ id: "drizzle-patterns", category: "skills" },
|
|
790
795
|
{ id: "json-data-auditor", category: "skills" }
|
|
791
796
|
]
|
|
792
797
|
},
|
|
@@ -800,7 +805,8 @@ var BUNDLES = [
|
|
|
800
805
|
tags: ["database", "prisma", "orm"],
|
|
801
806
|
alternativeTo: ["drizzle-database", "mongoose-database"],
|
|
802
807
|
modules: [
|
|
803
|
-
{ id: "
|
|
808
|
+
{ id: "database-engineer", category: "agents" },
|
|
809
|
+
{ id: "prisma-patterns", category: "skills" },
|
|
804
810
|
{ id: "json-data-auditor", category: "skills" }
|
|
805
811
|
]
|
|
806
812
|
},
|
|
@@ -814,7 +820,8 @@ var BUNDLES = [
|
|
|
814
820
|
tags: ["database", "mongodb", "mongoose", "nosql"],
|
|
815
821
|
alternativeTo: ["drizzle-database", "prisma-database"],
|
|
816
822
|
modules: [
|
|
817
|
-
{ id: "
|
|
823
|
+
{ id: "database-engineer", category: "agents" },
|
|
824
|
+
{ id: "mongoose-patterns", category: "skills" },
|
|
818
825
|
{ id: "json-data-auditor", category: "skills" }
|
|
819
826
|
]
|
|
820
827
|
},
|
|
@@ -831,7 +838,8 @@ var BUNDLES = [
|
|
|
831
838
|
tags: ["api", "hono", "backend"],
|
|
832
839
|
alternativeTo: ["express-api", "fastify-api", "nestjs-api"],
|
|
833
840
|
modules: [
|
|
834
|
-
{ id: "
|
|
841
|
+
{ id: "api-engineer", category: "agents" },
|
|
842
|
+
{ id: "hono-patterns", category: "skills" },
|
|
835
843
|
{ id: "api-app-testing", category: "skills" },
|
|
836
844
|
{ id: "error-handling-patterns", category: "skills" }
|
|
837
845
|
]
|
|
@@ -846,7 +854,8 @@ var BUNDLES = [
|
|
|
846
854
|
tags: ["api", "express", "backend"],
|
|
847
855
|
alternativeTo: ["hono-api", "fastify-api", "nestjs-api"],
|
|
848
856
|
modules: [
|
|
849
|
-
{ id: "
|
|
857
|
+
{ id: "api-engineer", category: "agents" },
|
|
858
|
+
{ id: "express-patterns", category: "skills" },
|
|
850
859
|
{ id: "api-app-testing", category: "skills" },
|
|
851
860
|
{ id: "error-handling-patterns", category: "skills" }
|
|
852
861
|
]
|
|
@@ -861,7 +870,8 @@ var BUNDLES = [
|
|
|
861
870
|
tags: ["api", "fastify", "backend", "performance"],
|
|
862
871
|
alternativeTo: ["hono-api", "express-api", "nestjs-api"],
|
|
863
872
|
modules: [
|
|
864
|
-
{ id: "
|
|
873
|
+
{ id: "api-engineer", category: "agents" },
|
|
874
|
+
{ id: "fastify-patterns", category: "skills" },
|
|
865
875
|
{ id: "api-app-testing", category: "skills" },
|
|
866
876
|
{ id: "error-handling-patterns", category: "skills" }
|
|
867
877
|
]
|
|
@@ -876,7 +886,8 @@ var BUNDLES = [
|
|
|
876
886
|
tags: ["api", "nestjs", "backend", "enterprise"],
|
|
877
887
|
alternativeTo: ["hono-api", "express-api", "fastify-api"],
|
|
878
888
|
modules: [
|
|
879
|
-
{ id: "
|
|
889
|
+
{ id: "api-engineer", category: "agents" },
|
|
890
|
+
{ id: "nestjs-patterns", category: "skills" },
|
|
880
891
|
{ id: "api-app-testing", category: "skills" },
|
|
881
892
|
{ id: "error-handling-patterns", category: "skills" }
|
|
882
893
|
]
|
|
@@ -893,8 +904,9 @@ var BUNDLES = [
|
|
|
893
904
|
techStack: ["React", "Shadcn UI", "Tailwind CSS", "Radix UI"],
|
|
894
905
|
tags: ["react", "ui", "components"],
|
|
895
906
|
modules: [
|
|
896
|
-
{ id: "
|
|
907
|
+
{ id: "frontend-engineer", category: "agents" },
|
|
897
908
|
{ id: "ux-ui-designer", category: "agents" },
|
|
909
|
+
{ id: "react-patterns", category: "skills" },
|
|
898
910
|
{ id: "shadcn-specialist", category: "skills" },
|
|
899
911
|
{ id: "brand-guidelines", category: "skills" },
|
|
900
912
|
{ id: "accessibility-audit", category: "skills" }
|
|
@@ -909,7 +921,8 @@ var BUNDLES = [
|
|
|
909
921
|
techStack: ["React Hook Form", "Zod", "React", "TypeScript"],
|
|
910
922
|
tags: ["react", "forms", "validation"],
|
|
911
923
|
modules: [
|
|
912
|
-
{ id: "
|
|
924
|
+
{ id: "frontend-engineer", category: "agents" },
|
|
925
|
+
{ id: "react-patterns", category: "skills" },
|
|
913
926
|
{ id: "react-hook-form-patterns", category: "skills" },
|
|
914
927
|
{ id: "shadcn-specialist", category: "skills" }
|
|
915
928
|
]
|
|
@@ -924,7 +937,8 @@ var BUNDLES = [
|
|
|
924
937
|
tags: ["react", "state", "zustand"],
|
|
925
938
|
alternativeTo: ["react-state-redux"],
|
|
926
939
|
modules: [
|
|
927
|
-
{ id: "
|
|
940
|
+
{ id: "frontend-engineer", category: "agents" },
|
|
941
|
+
{ id: "react-patterns", category: "skills" },
|
|
928
942
|
{ id: "zustand-patterns", category: "skills" },
|
|
929
943
|
{ id: "tanstack-query-patterns", category: "skills" }
|
|
930
944
|
]
|
|
@@ -939,7 +953,8 @@ var BUNDLES = [
|
|
|
939
953
|
tags: ["react", "state", "redux"],
|
|
940
954
|
alternativeTo: ["react-state-zustand"],
|
|
941
955
|
modules: [
|
|
942
|
-
{ id: "
|
|
956
|
+
{ id: "frontend-engineer", category: "agents" },
|
|
957
|
+
{ id: "react-patterns", category: "skills" },
|
|
943
958
|
{ id: "redux-toolkit-patterns", category: "skills" },
|
|
944
959
|
{ id: "tanstack-query-patterns", category: "skills", optional: true }
|
|
945
960
|
]
|
|
@@ -953,7 +968,8 @@ var BUNDLES = [
|
|
|
953
968
|
techStack: ["NextAuth.js", "Auth.js", "Next.js", "Prisma"],
|
|
954
969
|
tags: ["nextjs", "auth", "oauth"],
|
|
955
970
|
modules: [
|
|
956
|
-
{ id: "
|
|
971
|
+
{ id: "frontend-engineer", category: "agents" },
|
|
972
|
+
{ id: "nextjs-patterns", category: "skills" },
|
|
957
973
|
{ id: "nextauth-patterns", category: "skills" },
|
|
958
974
|
{ id: "security-testing", category: "skills" }
|
|
959
975
|
]
|
|
@@ -967,8 +983,9 @@ var BUNDLES = [
|
|
|
967
983
|
techStack: ["next-intl", "Next.js", "React", "TypeScript"],
|
|
968
984
|
tags: ["nextjs", "i18n", "internationalization"],
|
|
969
985
|
modules: [
|
|
970
|
-
{ id: "
|
|
986
|
+
{ id: "frontend-engineer", category: "agents" },
|
|
971
987
|
{ id: "i18n-specialist", category: "agents", optional: true },
|
|
988
|
+
{ id: "nextjs-patterns", category: "skills" },
|
|
972
989
|
{ id: "i18n-patterns", category: "skills" }
|
|
973
990
|
]
|
|
974
991
|
},
|
|
@@ -1088,7 +1105,7 @@ var BUNDLES = [
|
|
|
1088
1105
|
}
|
|
1089
1106
|
],
|
|
1090
1107
|
skills: [
|
|
1091
|
-
{ id: "
|
|
1108
|
+
{ id: "markdown-formatter", purpose: "Markdown formatting" },
|
|
1092
1109
|
{ id: "mermaid-diagram-specialist", purpose: "Diagram creation" }
|
|
1093
1110
|
],
|
|
1094
1111
|
commands: [
|
|
@@ -1103,7 +1120,7 @@ var BUNDLES = [
|
|
|
1103
1120
|
},
|
|
1104
1121
|
modules: [
|
|
1105
1122
|
{ id: "tech-writer", category: "agents" },
|
|
1106
|
-
{ id: "
|
|
1123
|
+
{ id: "markdown-formatter", category: "skills" },
|
|
1107
1124
|
{ id: "mermaid-diagram-specialist", category: "skills" },
|
|
1108
1125
|
{ id: "update-docs", category: "commands" },
|
|
1109
1126
|
{ id: "markdown-format", category: "commands" },
|
|
@@ -2594,7 +2611,7 @@ init_cjs_shims();
|
|
|
2594
2611
|
// src/lib/config/reader.ts
|
|
2595
2612
|
init_cjs_shims();
|
|
2596
2613
|
init_fs();
|
|
2597
|
-
var CONFIG_FILE = "config.json";
|
|
2614
|
+
var CONFIG_FILE = "qazuor-claude-config.json";
|
|
2598
2615
|
var CLAUDE_DIR = ".claude";
|
|
2599
2616
|
async function readConfig(projectPath) {
|
|
2600
2617
|
const configPath = joinPath(projectPath, CLAUDE_DIR, CONFIG_FILE);
|
|
@@ -2616,7 +2633,7 @@ async function hasClaudeDir(projectPath) {
|
|
|
2616
2633
|
// src/lib/config/writer.ts
|
|
2617
2634
|
init_cjs_shims();
|
|
2618
2635
|
init_fs();
|
|
2619
|
-
var CONFIG_FILE2 = "config.json";
|
|
2636
|
+
var CONFIG_FILE2 = "qazuor-claude-config.json";
|
|
2620
2637
|
var CLAUDE_DIR2 = ".claude";
|
|
2621
2638
|
async function writeConfig(projectPath, config, options) {
|
|
2622
2639
|
const claudePath = joinPath(projectPath, CLAUDE_DIR2);
|
|
@@ -3258,21 +3275,333 @@ init_cjs_shims();
|
|
|
3258
3275
|
// src/lib/git-hooks/husky-installer.ts
|
|
3259
3276
|
init_cjs_shims();
|
|
3260
3277
|
init_fs();
|
|
3261
|
-
|
|
3278
|
+
|
|
3279
|
+
// src/lib/git-hooks/precommit-generator.ts
|
|
3280
|
+
init_cjs_shims();
|
|
3281
|
+
function generatePreCommitScript(config) {
|
|
3282
|
+
if (!config.enabled) {
|
|
3283
|
+
return generateDisabledScript();
|
|
3284
|
+
}
|
|
3285
|
+
const sections = [];
|
|
3286
|
+
sections.push(generateHeader());
|
|
3287
|
+
if (config.showTiming) {
|
|
3288
|
+
sections.push(generateTimingSetup());
|
|
3289
|
+
}
|
|
3290
|
+
sections.push(generateErrorHandling(config.continueOnFailure));
|
|
3291
|
+
if (config.lint.enabled) {
|
|
3292
|
+
sections.push(generateLintSection(config));
|
|
3293
|
+
}
|
|
3294
|
+
if (config.typecheck.enabled) {
|
|
3295
|
+
sections.push(generateTypecheckSection(config));
|
|
3296
|
+
}
|
|
3297
|
+
if (config.formatCheck.enabled) {
|
|
3298
|
+
sections.push(generateFormatCheckSection(config));
|
|
3299
|
+
}
|
|
3300
|
+
if (config.tests.enabled && config.tests.mode !== "none") {
|
|
3301
|
+
sections.push(generateTestSection(config));
|
|
3302
|
+
}
|
|
3303
|
+
const sortedCustom = [...config.customCommands].sort(
|
|
3304
|
+
(a, b) => (a.order ?? 100) - (b.order ?? 100)
|
|
3305
|
+
);
|
|
3306
|
+
for (const cmd of sortedCustom) {
|
|
3307
|
+
sections.push(generateCustomCommandSection(cmd, config));
|
|
3308
|
+
}
|
|
3309
|
+
sections.push(generateFooter(config));
|
|
3310
|
+
return sections.join("\n\n");
|
|
3311
|
+
}
|
|
3312
|
+
function generateDisabledScript() {
|
|
3262
3313
|
return `#!/usr/bin/env sh
|
|
3263
3314
|
. "$(dirname -- "$0")/_/husky.sh"
|
|
3264
3315
|
|
|
3265
|
-
|
|
3266
|
-
`;
|
|
3316
|
+
# Pre-commit hook disabled
|
|
3317
|
+
exit 0`;
|
|
3267
3318
|
}
|
|
3268
|
-
function
|
|
3269
|
-
const command = lintCommand || "pnpm lint-staged";
|
|
3319
|
+
function generateHeader() {
|
|
3270
3320
|
return `#!/usr/bin/env sh
|
|
3271
3321
|
. "$(dirname -- "$0")/_/husky.sh"
|
|
3272
3322
|
|
|
3273
|
-
|
|
3323
|
+
# Pre-commit hook - Generated by @qazuor/claude-code-config
|
|
3324
|
+
# Bypass with: git commit --no-verify -m "message"
|
|
3325
|
+
|
|
3326
|
+
echo "\u{1F50D} Running pre-commit checks..."`;
|
|
3327
|
+
}
|
|
3328
|
+
function generateTimingSetup() {
|
|
3329
|
+
return `# Timing setup
|
|
3330
|
+
START_TIME=$(date +%s)
|
|
3331
|
+
step_start() { STEP_START=$(date +%s); }
|
|
3332
|
+
step_end() {
|
|
3333
|
+
STEP_END=$(date +%s)
|
|
3334
|
+
ELAPSED_TIME=$((STEP_END - STEP_START))
|
|
3335
|
+
echo " \u23F1\uFE0F Completed in \${ELAPSED_TIME}s"
|
|
3336
|
+
}`;
|
|
3337
|
+
}
|
|
3338
|
+
function generateErrorHandling(continueOnFailure) {
|
|
3339
|
+
if (continueOnFailure) {
|
|
3340
|
+
return `# Error tracking (continue on failure mode)
|
|
3341
|
+
ERRORS=0
|
|
3342
|
+
track_error() {
|
|
3343
|
+
ERRORS=$((ERRORS + 1))
|
|
3344
|
+
}`;
|
|
3345
|
+
}
|
|
3346
|
+
return `# Fail fast mode - exit on first error
|
|
3347
|
+
set -e`;
|
|
3348
|
+
}
|
|
3349
|
+
function generateLintSection(config) {
|
|
3350
|
+
const lint = config.lint;
|
|
3351
|
+
let command;
|
|
3352
|
+
if (lint.command) {
|
|
3353
|
+
command = lint.command;
|
|
3354
|
+
} else if (lint.stagedOnly) {
|
|
3355
|
+
command = getLintStagedCommand(lint.tool);
|
|
3356
|
+
} else {
|
|
3357
|
+
command = getLintCommand(lint.tool);
|
|
3358
|
+
}
|
|
3359
|
+
const lines = [];
|
|
3360
|
+
lines.push("# Linting");
|
|
3361
|
+
lines.push('echo ""');
|
|
3362
|
+
lines.push('echo "\u{1F4DD} Linting..."');
|
|
3363
|
+
if (config.showTiming) {
|
|
3364
|
+
lines.push("step_start");
|
|
3365
|
+
}
|
|
3366
|
+
if (lint.allowFailure) {
|
|
3367
|
+
lines.push(`if ${command}; then`);
|
|
3368
|
+
lines.push(' echo " \u2705 Lint passed"');
|
|
3369
|
+
lines.push("else");
|
|
3370
|
+
lines.push(' echo " \u26A0\uFE0F Lint warnings (non-blocking)"');
|
|
3371
|
+
lines.push("fi");
|
|
3372
|
+
} else if (config.continueOnFailure) {
|
|
3373
|
+
lines.push(`if ${command}; then`);
|
|
3374
|
+
lines.push(' echo " \u2705 Lint passed"');
|
|
3375
|
+
lines.push("else");
|
|
3376
|
+
lines.push(' echo " \u274C Lint failed"');
|
|
3377
|
+
lines.push(" track_error");
|
|
3378
|
+
lines.push("fi");
|
|
3379
|
+
} else {
|
|
3380
|
+
lines.push(`${command} || { echo " \u274C Lint failed"; exit 1; }`);
|
|
3381
|
+
lines.push('echo " \u2705 Lint passed"');
|
|
3382
|
+
}
|
|
3383
|
+
if (config.showTiming) {
|
|
3384
|
+
lines.push("step_end");
|
|
3385
|
+
}
|
|
3386
|
+
return lines.join("\n");
|
|
3387
|
+
}
|
|
3388
|
+
function getLintStagedCommand(tool) {
|
|
3389
|
+
switch (tool) {
|
|
3390
|
+
case "biome":
|
|
3391
|
+
return "pnpm biome check --staged --no-errors-on-unmatched";
|
|
3392
|
+
case "eslint":
|
|
3393
|
+
return "pnpm lint-staged";
|
|
3394
|
+
default:
|
|
3395
|
+
return "pnpm lint-staged";
|
|
3396
|
+
}
|
|
3397
|
+
}
|
|
3398
|
+
function getLintCommand(tool) {
|
|
3399
|
+
switch (tool) {
|
|
3400
|
+
case "biome":
|
|
3401
|
+
return "pnpm biome check .";
|
|
3402
|
+
case "eslint":
|
|
3403
|
+
return "pnpm eslint .";
|
|
3404
|
+
default:
|
|
3405
|
+
return "pnpm lint";
|
|
3406
|
+
}
|
|
3407
|
+
}
|
|
3408
|
+
function generateTypecheckSection(config) {
|
|
3409
|
+
const typecheck = config.typecheck;
|
|
3410
|
+
const command = typecheck.command ?? "pnpm typecheck";
|
|
3411
|
+
const lines = [];
|
|
3412
|
+
lines.push("# Type checking");
|
|
3413
|
+
lines.push('echo ""');
|
|
3414
|
+
lines.push('echo "\u{1F537} Type checking..."');
|
|
3415
|
+
if (config.showTiming) {
|
|
3416
|
+
lines.push("step_start");
|
|
3417
|
+
}
|
|
3418
|
+
if (typecheck.allowFailure) {
|
|
3419
|
+
lines.push(`if ${command}; then`);
|
|
3420
|
+
lines.push(' echo " \u2705 Types OK"');
|
|
3421
|
+
lines.push("else");
|
|
3422
|
+
lines.push(' echo " \u26A0\uFE0F Type warnings (non-blocking)"');
|
|
3423
|
+
lines.push("fi");
|
|
3424
|
+
} else if (config.continueOnFailure) {
|
|
3425
|
+
lines.push(`if ${command}; then`);
|
|
3426
|
+
lines.push(' echo " \u2705 Types OK"');
|
|
3427
|
+
lines.push("else");
|
|
3428
|
+
lines.push(' echo " \u274C Type check failed"');
|
|
3429
|
+
lines.push(" track_error");
|
|
3430
|
+
lines.push("fi");
|
|
3431
|
+
} else {
|
|
3432
|
+
lines.push(`${command} || { echo " \u274C Type check failed"; exit 1; }`);
|
|
3433
|
+
lines.push('echo " \u2705 Types OK"');
|
|
3434
|
+
}
|
|
3435
|
+
if (config.showTiming) {
|
|
3436
|
+
lines.push("step_end");
|
|
3437
|
+
}
|
|
3438
|
+
return lines.join("\n");
|
|
3439
|
+
}
|
|
3440
|
+
function generateFormatCheckSection(config) {
|
|
3441
|
+
const format = config.formatCheck;
|
|
3442
|
+
let command;
|
|
3443
|
+
if (format.command) {
|
|
3444
|
+
command = format.command;
|
|
3445
|
+
} else {
|
|
3446
|
+
command = getFormatCheckCommand(format.tool);
|
|
3447
|
+
}
|
|
3448
|
+
const lines = [];
|
|
3449
|
+
lines.push("# Format check");
|
|
3450
|
+
lines.push('echo ""');
|
|
3451
|
+
lines.push('echo "\u2728 Format check..."');
|
|
3452
|
+
if (config.showTiming) {
|
|
3453
|
+
lines.push("step_start");
|
|
3454
|
+
}
|
|
3455
|
+
if (format.allowFailure) {
|
|
3456
|
+
lines.push(`if ${command}; then`);
|
|
3457
|
+
lines.push(' echo " \u2705 Format OK"');
|
|
3458
|
+
lines.push("else");
|
|
3459
|
+
lines.push(' echo " \u26A0\uFE0F Format warnings (non-blocking)"');
|
|
3460
|
+
lines.push("fi");
|
|
3461
|
+
} else if (config.continueOnFailure) {
|
|
3462
|
+
lines.push(`if ${command}; then`);
|
|
3463
|
+
lines.push(' echo " \u2705 Format OK"');
|
|
3464
|
+
lines.push("else");
|
|
3465
|
+
lines.push(' echo " \u274C Format check failed"');
|
|
3466
|
+
lines.push(" track_error");
|
|
3467
|
+
lines.push("fi");
|
|
3468
|
+
} else {
|
|
3469
|
+
lines.push(`${command} || { echo " \u274C Format check failed"; exit 1; }`);
|
|
3470
|
+
lines.push('echo " \u2705 Format OK"');
|
|
3471
|
+
}
|
|
3472
|
+
if (config.showTiming) {
|
|
3473
|
+
lines.push("step_end");
|
|
3474
|
+
}
|
|
3475
|
+
return lines.join("\n");
|
|
3476
|
+
}
|
|
3477
|
+
function getFormatCheckCommand(tool) {
|
|
3478
|
+
switch (tool) {
|
|
3479
|
+
case "biome":
|
|
3480
|
+
return "pnpm biome format --check .";
|
|
3481
|
+
case "prettier":
|
|
3482
|
+
return "pnpm prettier --check .";
|
|
3483
|
+
default:
|
|
3484
|
+
return "pnpm format:check";
|
|
3485
|
+
}
|
|
3486
|
+
}
|
|
3487
|
+
function generateTestSection(config) {
|
|
3488
|
+
const tests = config.tests;
|
|
3489
|
+
let command;
|
|
3490
|
+
if (tests.command) {
|
|
3491
|
+
command = tests.command;
|
|
3492
|
+
} else if (tests.mode === "affected") {
|
|
3493
|
+
command = "pnpm vitest related --run";
|
|
3494
|
+
} else {
|
|
3495
|
+
command = "pnpm test";
|
|
3496
|
+
}
|
|
3497
|
+
if (tests.coverageThreshold > 0) {
|
|
3498
|
+
command = `${command} --coverage --coverage.thresholds.lines=${tests.coverageThreshold}`;
|
|
3499
|
+
}
|
|
3500
|
+
const lines = [];
|
|
3501
|
+
lines.push("# Tests");
|
|
3502
|
+
lines.push('echo ""');
|
|
3503
|
+
lines.push(`echo "\u{1F9EA} Running ${tests.mode === "affected" ? "affected" : "all"} tests..."`);
|
|
3504
|
+
if (config.showTiming) {
|
|
3505
|
+
lines.push("step_start");
|
|
3506
|
+
}
|
|
3507
|
+
if (tests.allowFailure) {
|
|
3508
|
+
lines.push(`if ${command}; then`);
|
|
3509
|
+
lines.push(' echo " \u2705 Tests passed"');
|
|
3510
|
+
lines.push("else");
|
|
3511
|
+
lines.push(' echo " \u26A0\uFE0F Test warnings (non-blocking)"');
|
|
3512
|
+
lines.push("fi");
|
|
3513
|
+
} else if (config.continueOnFailure) {
|
|
3514
|
+
lines.push(`if ${command}; then`);
|
|
3515
|
+
lines.push(' echo " \u2705 Tests passed"');
|
|
3516
|
+
lines.push("else");
|
|
3517
|
+
lines.push(' echo " \u274C Tests failed"');
|
|
3518
|
+
lines.push(" track_error");
|
|
3519
|
+
lines.push("fi");
|
|
3520
|
+
} else {
|
|
3521
|
+
lines.push(`${command} || { echo " \u274C Tests failed"; exit 1; }`);
|
|
3522
|
+
lines.push('echo " \u2705 Tests passed"');
|
|
3523
|
+
}
|
|
3524
|
+
if (config.showTiming) {
|
|
3525
|
+
lines.push("step_end");
|
|
3526
|
+
}
|
|
3527
|
+
return lines.join("\n");
|
|
3528
|
+
}
|
|
3529
|
+
function generateCustomCommandSection(cmd, config) {
|
|
3530
|
+
const lines = [];
|
|
3531
|
+
lines.push(`# Custom: ${cmd.name}`);
|
|
3532
|
+
lines.push('echo ""');
|
|
3533
|
+
lines.push(`echo "\u{1F527} ${cmd.name}..."`);
|
|
3534
|
+
if (config.showTiming) {
|
|
3535
|
+
lines.push("step_start");
|
|
3536
|
+
}
|
|
3537
|
+
if (cmd.allowFailure) {
|
|
3538
|
+
lines.push(`if ${cmd.command}; then`);
|
|
3539
|
+
lines.push(` echo " \u2705 ${cmd.name} passed"`);
|
|
3540
|
+
lines.push("else");
|
|
3541
|
+
lines.push(` echo " \u26A0\uFE0F ${cmd.name} warnings (non-blocking)"`);
|
|
3542
|
+
lines.push("fi");
|
|
3543
|
+
} else if (config.continueOnFailure) {
|
|
3544
|
+
lines.push(`if ${cmd.command}; then`);
|
|
3545
|
+
lines.push(` echo " \u2705 ${cmd.name} passed"`);
|
|
3546
|
+
lines.push("else");
|
|
3547
|
+
lines.push(` echo " \u274C ${cmd.name} failed"`);
|
|
3548
|
+
lines.push(" track_error");
|
|
3549
|
+
lines.push("fi");
|
|
3550
|
+
} else {
|
|
3551
|
+
lines.push(`${cmd.command} || { echo " \u274C ${cmd.name} failed"; exit 1; }`);
|
|
3552
|
+
lines.push(`echo " \u2705 ${cmd.name} passed"`);
|
|
3553
|
+
}
|
|
3554
|
+
if (config.showTiming) {
|
|
3555
|
+
lines.push("step_end");
|
|
3556
|
+
}
|
|
3557
|
+
return lines.join("\n");
|
|
3558
|
+
}
|
|
3559
|
+
function generateFooter(config) {
|
|
3560
|
+
const lines = [];
|
|
3561
|
+
lines.push("# Final status");
|
|
3562
|
+
lines.push('echo ""');
|
|
3563
|
+
if (config.showTiming) {
|
|
3564
|
+
lines.push("END_TIME=$(date +%s)");
|
|
3565
|
+
lines.push("TOTAL_TIME=$((END_TIME - START_TIME))");
|
|
3566
|
+
}
|
|
3567
|
+
if (config.continueOnFailure) {
|
|
3568
|
+
lines.push("if [ $ERRORS -gt 0 ]; then");
|
|
3569
|
+
lines.push(' echo "\u274C Pre-commit failed with $ERRORS error(s)"');
|
|
3570
|
+
if (config.showTiming) {
|
|
3571
|
+
lines.push(' echo "\u23F1\uFE0F Total time: ${TOTAL_TIME}s"');
|
|
3572
|
+
}
|
|
3573
|
+
lines.push(" exit 1");
|
|
3574
|
+
lines.push("fi");
|
|
3575
|
+
}
|
|
3576
|
+
if (config.showTiming) {
|
|
3577
|
+
lines.push('echo "\u2728 All checks passed! (${TOTAL_TIME}s)"');
|
|
3578
|
+
} else {
|
|
3579
|
+
lines.push('echo "\u2728 All checks passed!"');
|
|
3580
|
+
}
|
|
3581
|
+
return lines.join("\n");
|
|
3582
|
+
}
|
|
3583
|
+
function generateSimplePreCommitHook(command) {
|
|
3584
|
+
return `#!/usr/bin/env sh
|
|
3585
|
+
. "$(dirname -- "$0")/_/husky.sh"
|
|
3586
|
+
|
|
3587
|
+
${command}`;
|
|
3588
|
+
}
|
|
3589
|
+
|
|
3590
|
+
// src/lib/git-hooks/husky-installer.ts
|
|
3591
|
+
function generateCommitMsgHook() {
|
|
3592
|
+
return `#!/usr/bin/env sh
|
|
3593
|
+
. "$(dirname -- "$0")/_/husky.sh"
|
|
3594
|
+
|
|
3595
|
+
npx --no -- commitlint --edit "\${1}"
|
|
3274
3596
|
`;
|
|
3275
3597
|
}
|
|
3598
|
+
function generatePreCommitHook(lintCommand, preCommitConfig) {
|
|
3599
|
+
if (preCommitConfig) {
|
|
3600
|
+
return generatePreCommitScript(preCommitConfig);
|
|
3601
|
+
}
|
|
3602
|
+
const command = lintCommand || "pnpm lint-staged";
|
|
3603
|
+
return generateSimplePreCommitHook(command);
|
|
3604
|
+
}
|
|
3276
3605
|
function generatePrePushHook(testCommand) {
|
|
3277
3606
|
const command = testCommand || "pnpm test";
|
|
3278
3607
|
return `#!/usr/bin/env sh
|
|
@@ -3363,7 +3692,10 @@ async function installHusky(projectPath, config, options) {
|
|
|
3363
3692
|
if (config.preCommit) {
|
|
3364
3693
|
const preCommitPath = joinPath(huskyDir, "pre-commit");
|
|
3365
3694
|
if (!await pathExists(preCommitPath) || options?.overwrite) {
|
|
3366
|
-
await writeFile(
|
|
3695
|
+
await writeFile(
|
|
3696
|
+
preCommitPath,
|
|
3697
|
+
generatePreCommitHook(config.lintCommand, config.preCommitConfig)
|
|
3698
|
+
);
|
|
3367
3699
|
await makeExecutable(preCommitPath);
|
|
3368
3700
|
result.created.push("pre-commit");
|
|
3369
3701
|
} else {
|
|
@@ -3595,8 +3927,8 @@ async function getHooksStatus(projectPath) {
|
|
|
3595
3927
|
let executable = false;
|
|
3596
3928
|
if (exists) {
|
|
3597
3929
|
try {
|
|
3598
|
-
const
|
|
3599
|
-
const stats = await
|
|
3930
|
+
const fs9 = await import("fs/promises");
|
|
3931
|
+
const stats = await fs9.stat(filePath);
|
|
3600
3932
|
executable = (stats.mode & 73) !== 0;
|
|
3601
3933
|
} catch {
|
|
3602
3934
|
}
|
|
@@ -5809,11 +6141,13 @@ function processTemplate(template, projectInfo, options) {
|
|
|
5809
6141
|
const commands = options?.templateConfig?.commands;
|
|
5810
6142
|
const targets = options?.templateConfig?.targets;
|
|
5811
6143
|
const preferences = options?.claudeConfig?.preferences;
|
|
6144
|
+
const standards = options?.claudeConfig?.extras?.standards;
|
|
5812
6145
|
content = content.replace(/\{\{PROJECT_NAME\}\}/g, projectInfo.name).replace(/\{\{PROJECT_DESCRIPTION\}\}/g, projectInfo.description).replace(/\{\{ORG\}\}/g, projectInfo.org).replace(/\{\{REPO\}\}/g, projectInfo.repo).replace(/\{\{ENTITY_TYPE\}\}/g, projectInfo.entityType).replace(/\{\{ENTITY_TYPE_PLURAL\}\}/g, projectInfo.entityTypePlural).replace(/\{\{LOCATION\}\}/g, projectInfo.location || "");
|
|
5813
6146
|
const packageManager = preferences?.packageManager || "pnpm";
|
|
5814
6147
|
content = content.replace(/\{\{PACKAGE_MANAGER\}\}/g, packageManager);
|
|
5815
|
-
const coverageTarget = targets && "coverage" in targets ? String(targets.coverage) : "90";
|
|
6148
|
+
const coverageTarget = standards?.testing?.coverageTarget ? String(standards.testing.coverageTarget) : targets && "coverage" in targets ? String(targets.coverage) : "90";
|
|
5816
6149
|
content = content.replace(/\{\{COVERAGE_TARGET\}\}/g, coverageTarget);
|
|
6150
|
+
content = processStandardsPlaceholders(content, standards, preferences);
|
|
5817
6151
|
if (projectInfo.domain) {
|
|
5818
6152
|
content = content.replace(/\{\{#if DOMAIN\}\}/g, "").replace(/\{\{\/if\}\}/g, "").replace(/\{\{DOMAIN\}\}/g, projectInfo.domain);
|
|
5819
6153
|
} else {
|
|
@@ -5910,6 +6244,55 @@ function generateCommandsSection(commands, packageManager) {
|
|
|
5910
6244
|
lines.push("```");
|
|
5911
6245
|
return lines.join("\n");
|
|
5912
6246
|
}
|
|
6247
|
+
function processStandardsPlaceholders(content, standards, preferences) {
|
|
6248
|
+
let result = content;
|
|
6249
|
+
const primaryLanguage = "TypeScript";
|
|
6250
|
+
result = result.replace(/\{\{PRIMARY_LANGUAGE\}\}/g, primaryLanguage);
|
|
6251
|
+
const maxFileLines = standards?.code?.maxFileLines?.toString() || "500";
|
|
6252
|
+
result = result.replace(/\{\{MAX_FILE_LINES\}\}/g, maxFileLines);
|
|
6253
|
+
const testPattern = standards?.testing?.testPattern === "gwt" ? "GWT (Given-When-Then)" : "AAA (Arrange, Act, Assert)";
|
|
6254
|
+
result = result.replace(/\{\{TEST_PATTERN\}\}/g, testPattern);
|
|
6255
|
+
const responseLanguage = preferences?.responseLanguage === "es" ? "Spanish" : preferences?.responseLanguage === "en" ? "English" : "Spanish";
|
|
6256
|
+
result = result.replace(/\{\{RESPONSE_LANGUAGE\}\}/g, responseLanguage);
|
|
6257
|
+
if (standards?.code?.namedExportsOnly) {
|
|
6258
|
+
result = result.replace(/\{\{#if NAMED_EXPORTS_ONLY\}\}/g, "").replace(/\{\{\/if\}\}/g, "");
|
|
6259
|
+
} else {
|
|
6260
|
+
result = result.replace(/\{\{#if NAMED_EXPORTS_ONLY\}\}[\s\S]*?\{\{\/if\}\}/g, "");
|
|
6261
|
+
}
|
|
6262
|
+
if (standards?.code?.jsDocRequired) {
|
|
6263
|
+
result = result.replace(/\{\{#if JSDOC_REQUIRED\}\}/g, "").replace(/\{\{\/if\}\}/g, "");
|
|
6264
|
+
} else {
|
|
6265
|
+
result = result.replace(/\{\{#if JSDOC_REQUIRED\}\}[\s\S]*?\{\{\/if\}\}/g, "");
|
|
6266
|
+
}
|
|
6267
|
+
if (standards?.code?.roroPattern) {
|
|
6268
|
+
result = result.replace(/\{\{#if RORO_PATTERN\}\}/g, "").replace(/\{\{\/if\}\}/g, "");
|
|
6269
|
+
} else {
|
|
6270
|
+
result = result.replace(/\{\{#if RORO_PATTERN\}\}[\s\S]*?\{\{\/if\}\}/g, "");
|
|
6271
|
+
}
|
|
6272
|
+
if (standards?.testing?.tddRequired) {
|
|
6273
|
+
result = result.replace(/\{\{#if TDD_REQUIRED\}\}/g, "").replace(/\{\{else\}\}[\s\S]*?\{\{\/if\}\}/g, "");
|
|
6274
|
+
} else {
|
|
6275
|
+
result = result.replace(/\{\{#if TDD_REQUIRED\}\}[\s\S]*?\{\{else\}\}/g, "").replace(/\{\{\/if\}\}/g, "");
|
|
6276
|
+
}
|
|
6277
|
+
const testLocation = standards?.testing?.testLocation;
|
|
6278
|
+
if (testLocation) {
|
|
6279
|
+
const testLocationText = testLocation === "colocated" ? "Co-located with source" : "Separate test directory";
|
|
6280
|
+
result = result.replace(/\{\{#if TEST_LOCATION\}\}/g, "").replace(/\{\{\/if\}\}/g, "").replace(/\{\{TEST_LOCATION\}\}/g, testLocationText);
|
|
6281
|
+
} else {
|
|
6282
|
+
result = result.replace(/\{\{#if TEST_LOCATION\}\}[\s\S]*?\{\{\/if\}\}/g, "");
|
|
6283
|
+
}
|
|
6284
|
+
if (preferences?.includeCoAuthor) {
|
|
6285
|
+
result = result.replace(/\{\{#if INCLUDE_CO_AUTHOR\}\}/g, "").replace(/\{\{\/if\}\}/g, "");
|
|
6286
|
+
} else {
|
|
6287
|
+
result = result.replace(/\{\{#if INCLUDE_CO_AUTHOR\}\}[\s\S]*?\{\{\/if\}\}/g, "");
|
|
6288
|
+
}
|
|
6289
|
+
if (preferences?.responseLanguage) {
|
|
6290
|
+
result = result.replace(/\{\{#if RESPONSE_LANGUAGE\}\}/g, "").replace(/\{\{\/if\}\}/g, "");
|
|
6291
|
+
} else {
|
|
6292
|
+
result = result.replace(/\{\{#if RESPONSE_LANGUAGE\}\}[\s\S]*?\{\{\/if\}\}/g, "");
|
|
6293
|
+
}
|
|
6294
|
+
return result;
|
|
6295
|
+
}
|
|
5913
6296
|
function getMinimalTemplate() {
|
|
5914
6297
|
return `# CLAUDE.md
|
|
5915
6298
|
|
|
@@ -6455,6 +6838,242 @@ async function generateScaffoldWithProgress(projectPath, options) {
|
|
|
6455
6838
|
);
|
|
6456
6839
|
}
|
|
6457
6840
|
|
|
6841
|
+
// src/lib/scaffold/settings-generator.ts
|
|
6842
|
+
init_cjs_shims();
|
|
6843
|
+
|
|
6844
|
+
// src/constants/claude-settings-defaults.ts
|
|
6845
|
+
init_cjs_shims();
|
|
6846
|
+
var DEFAULT_CLAUDE_SETTINGS = {
|
|
6847
|
+
model: "sonnet",
|
|
6848
|
+
alwaysThinkingEnabled: false,
|
|
6849
|
+
sandbox: {
|
|
6850
|
+
enabled: false,
|
|
6851
|
+
autoAllowBashIfSandboxed: true
|
|
6852
|
+
},
|
|
6853
|
+
permissions: {
|
|
6854
|
+
allow: [],
|
|
6855
|
+
deny: [],
|
|
6856
|
+
ask: [],
|
|
6857
|
+
defaultMode: "acceptEdits"
|
|
6858
|
+
},
|
|
6859
|
+
attribution: {
|
|
6860
|
+
commit: "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)\n\n Co-Authored-By: Claude <noreply@anthropic.com>",
|
|
6861
|
+
pr: "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)"
|
|
6862
|
+
},
|
|
6863
|
+
cleanupPeriodDays: 30,
|
|
6864
|
+
stopNotification: "beep"
|
|
6865
|
+
};
|
|
6866
|
+
var ATTRIBUTION_NO_COAUTHOR = {
|
|
6867
|
+
commit: "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)",
|
|
6868
|
+
pr: "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)"
|
|
6869
|
+
};
|
|
6870
|
+
var ATTRIBUTION_WITH_COAUTHOR = {
|
|
6871
|
+
commit: "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)\n\n Co-Authored-By: Claude <noreply@anthropic.com>",
|
|
6872
|
+
pr: "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)"
|
|
6873
|
+
};
|
|
6874
|
+
var BEEP_COMMAND = "echo -ne '\\007'";
|
|
6875
|
+
var MODEL_DESCRIPTIONS = {
|
|
6876
|
+
sonnet: "Claude Sonnet - Balanced performance and speed (recommended)",
|
|
6877
|
+
opus: "Claude Opus - Most capable, best for complex tasks",
|
|
6878
|
+
haiku: "Claude Haiku - Fastest, best for simple tasks",
|
|
6879
|
+
default: "Default - Let Claude Code decide based on task"
|
|
6880
|
+
};
|
|
6881
|
+
var PERMISSION_MODE_DESCRIPTIONS = {
|
|
6882
|
+
acceptEdits: "Accept edits automatically, ask for other operations",
|
|
6883
|
+
askAlways: "Ask for confirmation on all operations",
|
|
6884
|
+
viewOnly: "Read-only mode, no modifications allowed"
|
|
6885
|
+
};
|
|
6886
|
+
var STOP_NOTIFICATION_DESCRIPTIONS = {
|
|
6887
|
+
beep: "Play a beep sound when task completes",
|
|
6888
|
+
custom: "Run a custom command when task completes",
|
|
6889
|
+
none: "No notification"
|
|
6890
|
+
};
|
|
6891
|
+
var CLAUDE_SETTINGS_PRESETS = {
|
|
6892
|
+
/** Default preset - balanced settings */
|
|
6893
|
+
default: {
|
|
6894
|
+
...DEFAULT_CLAUDE_SETTINGS
|
|
6895
|
+
},
|
|
6896
|
+
/** Performance preset - faster model, less confirmations */
|
|
6897
|
+
performance: {
|
|
6898
|
+
...DEFAULT_CLAUDE_SETTINGS,
|
|
6899
|
+
model: "haiku",
|
|
6900
|
+
permissions: {
|
|
6901
|
+
...DEFAULT_CLAUDE_SETTINGS.permissions,
|
|
6902
|
+
defaultMode: "acceptEdits"
|
|
6903
|
+
}
|
|
6904
|
+
},
|
|
6905
|
+
/** Quality preset - best model, extended thinking */
|
|
6906
|
+
quality: {
|
|
6907
|
+
...DEFAULT_CLAUDE_SETTINGS,
|
|
6908
|
+
model: "opus",
|
|
6909
|
+
alwaysThinkingEnabled: true
|
|
6910
|
+
},
|
|
6911
|
+
/** Secure preset - sandbox enabled, more confirmations */
|
|
6912
|
+
secure: {
|
|
6913
|
+
...DEFAULT_CLAUDE_SETTINGS,
|
|
6914
|
+
sandbox: {
|
|
6915
|
+
enabled: true,
|
|
6916
|
+
autoAllowBashIfSandboxed: true
|
|
6917
|
+
},
|
|
6918
|
+
permissions: {
|
|
6919
|
+
...DEFAULT_CLAUDE_SETTINGS.permissions,
|
|
6920
|
+
defaultMode: "askAlways"
|
|
6921
|
+
}
|
|
6922
|
+
}
|
|
6923
|
+
};
|
|
6924
|
+
var PRESET_DESCRIPTIONS2 = {
|
|
6925
|
+
default: "Balanced settings for most projects",
|
|
6926
|
+
performance: "Faster responses with Haiku model",
|
|
6927
|
+
quality: "Best quality with Opus model and extended thinking",
|
|
6928
|
+
secure: "Enhanced security with sandbox and confirmations"
|
|
6929
|
+
};
|
|
6930
|
+
|
|
6931
|
+
// src/lib/scaffold/settings-generator.ts
|
|
6932
|
+
init_fs();
|
|
6933
|
+
async function generateSettings(projectPath, options) {
|
|
6934
|
+
const settingsPath = joinPath(projectPath, ".claude", "settings.json");
|
|
6935
|
+
const exists = await pathExists(settingsPath);
|
|
6936
|
+
if (exists && !options?.overwrite) {
|
|
6937
|
+
return {
|
|
6938
|
+
created: false,
|
|
6939
|
+
skipped: true,
|
|
6940
|
+
path: settingsPath
|
|
6941
|
+
};
|
|
6942
|
+
}
|
|
6943
|
+
try {
|
|
6944
|
+
const settings = buildSettingsJson(options);
|
|
6945
|
+
await writeJson(settingsPath, settings, { spaces: 2 });
|
|
6946
|
+
return {
|
|
6947
|
+
created: true,
|
|
6948
|
+
skipped: false,
|
|
6949
|
+
path: settingsPath
|
|
6950
|
+
};
|
|
6951
|
+
} catch (error) {
|
|
6952
|
+
return {
|
|
6953
|
+
created: false,
|
|
6954
|
+
skipped: false,
|
|
6955
|
+
path: settingsPath,
|
|
6956
|
+
error: error instanceof Error ? error.message : String(error)
|
|
6957
|
+
};
|
|
6958
|
+
}
|
|
6959
|
+
}
|
|
6960
|
+
async function generateSettingsWithSpinner(projectPath, options) {
|
|
6961
|
+
return withSpinner("Generating settings.json...", () => generateSettings(projectPath, options), {
|
|
6962
|
+
successText: "Created settings.json"
|
|
6963
|
+
});
|
|
6964
|
+
}
|
|
6965
|
+
async function generateSettingsLocal(projectPath, options) {
|
|
6966
|
+
const settingsPath = joinPath(projectPath, ".claude", "settings.local.json");
|
|
6967
|
+
const exists = await pathExists(settingsPath);
|
|
6968
|
+
if (exists && !options?.overwrite) {
|
|
6969
|
+
return {
|
|
6970
|
+
created: false,
|
|
6971
|
+
skipped: true,
|
|
6972
|
+
path: settingsPath
|
|
6973
|
+
};
|
|
6974
|
+
}
|
|
6975
|
+
try {
|
|
6976
|
+
const settings = buildSettingsLocalJson(options);
|
|
6977
|
+
await writeJson(settingsPath, settings, { spaces: 2 });
|
|
6978
|
+
return {
|
|
6979
|
+
created: true,
|
|
6980
|
+
skipped: false,
|
|
6981
|
+
path: settingsPath
|
|
6982
|
+
};
|
|
6983
|
+
} catch (error) {
|
|
6984
|
+
return {
|
|
6985
|
+
created: false,
|
|
6986
|
+
skipped: false,
|
|
6987
|
+
path: settingsPath,
|
|
6988
|
+
error: error instanceof Error ? error.message : String(error)
|
|
6989
|
+
};
|
|
6990
|
+
}
|
|
6991
|
+
}
|
|
6992
|
+
async function generateSettingsLocalWithSpinner(projectPath, options) {
|
|
6993
|
+
return withSpinner(
|
|
6994
|
+
"Generating settings.local.json...",
|
|
6995
|
+
() => generateSettingsLocal(projectPath, options),
|
|
6996
|
+
{
|
|
6997
|
+
successText: "Created settings.local.json"
|
|
6998
|
+
}
|
|
6999
|
+
);
|
|
7000
|
+
}
|
|
7001
|
+
function buildSettingsJson(options) {
|
|
7002
|
+
const claudeSettings = options?.claudeSettings || DEFAULT_CLAUDE_SETTINGS;
|
|
7003
|
+
const includeCoAuthor = options?.includeCoAuthor ?? true;
|
|
7004
|
+
const settings = {
|
|
7005
|
+
$schema: "https://json.schemastore.org/claude-code-settings.json"
|
|
7006
|
+
};
|
|
7007
|
+
if (claudeSettings.model && claudeSettings.model !== "default") {
|
|
7008
|
+
settings.model = claudeSettings.model;
|
|
7009
|
+
}
|
|
7010
|
+
if (claudeSettings.alwaysThinkingEnabled) {
|
|
7011
|
+
settings.alwaysThinkingEnabled = true;
|
|
7012
|
+
}
|
|
7013
|
+
if (claudeSettings.cleanupPeriodDays !== 30) {
|
|
7014
|
+
settings.cleanupPeriodDays = claudeSettings.cleanupPeriodDays;
|
|
7015
|
+
}
|
|
7016
|
+
settings.attribution = includeCoAuthor ? ATTRIBUTION_WITH_COAUTHOR : ATTRIBUTION_NO_COAUTHOR;
|
|
7017
|
+
settings.permissions = {
|
|
7018
|
+
allow: [],
|
|
7019
|
+
deny: []
|
|
7020
|
+
};
|
|
7021
|
+
if (claudeSettings.sandbox?.enabled) {
|
|
7022
|
+
settings.sandbox = {
|
|
7023
|
+
enabled: true,
|
|
7024
|
+
autoAllowBashIfSandboxed: claudeSettings.sandbox.autoAllowBashIfSandboxed ?? true
|
|
7025
|
+
};
|
|
7026
|
+
}
|
|
7027
|
+
return settings;
|
|
7028
|
+
}
|
|
7029
|
+
function buildSettingsLocalJson(options) {
|
|
7030
|
+
const claudeSettings = options?.claudeSettings || DEFAULT_CLAUDE_SETTINGS;
|
|
7031
|
+
const settings = {};
|
|
7032
|
+
const allow = [...options?.additionalAllow || []];
|
|
7033
|
+
const deny = [...options?.additionalDeny || []];
|
|
7034
|
+
if (claudeSettings.permissions?.allow) {
|
|
7035
|
+
allow.push(...claudeSettings.permissions.allow);
|
|
7036
|
+
}
|
|
7037
|
+
if (claudeSettings.permissions?.deny) {
|
|
7038
|
+
deny.push(...claudeSettings.permissions.deny);
|
|
7039
|
+
}
|
|
7040
|
+
settings.permissions = {
|
|
7041
|
+
allow: [...new Set(allow)],
|
|
7042
|
+
// Deduplicate
|
|
7043
|
+
deny: [...new Set(deny)],
|
|
7044
|
+
ask: claudeSettings.permissions?.ask || [],
|
|
7045
|
+
defaultMode: claudeSettings.permissions?.defaultMode || "acceptEdits"
|
|
7046
|
+
};
|
|
7047
|
+
if (claudeSettings.stopNotification && claudeSettings.stopNotification !== "none") {
|
|
7048
|
+
const command = claudeSettings.stopNotification === "custom" && claudeSettings.customStopCommand ? claudeSettings.customStopCommand : BEEP_COMMAND;
|
|
7049
|
+
settings.hooks = {
|
|
7050
|
+
Stop: [
|
|
7051
|
+
{
|
|
7052
|
+
hooks: [
|
|
7053
|
+
{
|
|
7054
|
+
type: "command",
|
|
7055
|
+
command,
|
|
7056
|
+
timeout: 5
|
|
7057
|
+
}
|
|
7058
|
+
]
|
|
7059
|
+
}
|
|
7060
|
+
],
|
|
7061
|
+
SubagentStop: [
|
|
7062
|
+
{
|
|
7063
|
+
hooks: [
|
|
7064
|
+
{
|
|
7065
|
+
type: "command",
|
|
7066
|
+
command,
|
|
7067
|
+
timeout: 5
|
|
7068
|
+
}
|
|
7069
|
+
]
|
|
7070
|
+
}
|
|
7071
|
+
]
|
|
7072
|
+
};
|
|
7073
|
+
}
|
|
7074
|
+
return settings;
|
|
7075
|
+
}
|
|
7076
|
+
|
|
6458
7077
|
// src/lib/templates/config-replacer.ts
|
|
6459
7078
|
init_cjs_shims();
|
|
6460
7079
|
var fs4 = __toESM(require("fs/promises"), 1);
|
|
@@ -8402,53 +9021,189 @@ async function promptCICDConfig(options) {
|
|
|
8402
9021
|
};
|
|
8403
9022
|
}
|
|
8404
9023
|
|
|
8405
|
-
// src/cli/prompts/
|
|
9024
|
+
// src/cli/prompts/claude-settings.ts
|
|
8406
9025
|
init_cjs_shims();
|
|
8407
|
-
|
|
8408
|
-
{
|
|
8409
|
-
|
|
8410
|
-
|
|
8411
|
-
description: "Consistent coding styles across editors",
|
|
8412
|
-
checked: true
|
|
8413
|
-
},
|
|
8414
|
-
{
|
|
8415
|
-
name: "Commitlint",
|
|
8416
|
-
value: "commitlint",
|
|
8417
|
-
description: "Lint commit messages (conventional commits)",
|
|
8418
|
-
checked: true
|
|
8419
|
-
},
|
|
8420
|
-
{
|
|
8421
|
-
name: "Biome",
|
|
8422
|
-
value: "biome",
|
|
8423
|
-
description: "Fast linter and formatter (ESLint + Prettier alternative)",
|
|
8424
|
-
checked: false
|
|
8425
|
-
},
|
|
8426
|
-
{
|
|
8427
|
-
name: "Prettier",
|
|
8428
|
-
value: "prettier",
|
|
8429
|
-
description: "Code formatter (use if not using Biome)",
|
|
8430
|
-
checked: false
|
|
8431
|
-
}
|
|
8432
|
-
];
|
|
8433
|
-
async function promptCodeStyleConfig(options) {
|
|
8434
|
-
logger.section("Code Style", "\u{1F3A8}");
|
|
8435
|
-
logger.info("Configure code formatting and linting tools");
|
|
8436
|
-
logger.newline();
|
|
8437
|
-
const enableCodeStyle = await confirm({
|
|
8438
|
-
message: "Would you like to install code style configuration files?",
|
|
9026
|
+
async function promptClaudeSettings(options) {
|
|
9027
|
+
logger.section("Claude Code Settings", "\u{1F916}");
|
|
9028
|
+
const wantToConfigure = await confirm({
|
|
9029
|
+
message: "Would you like to configure Claude Code settings?",
|
|
8439
9030
|
default: true
|
|
8440
9031
|
});
|
|
8441
|
-
if (!
|
|
9032
|
+
if (!wantToConfigure) {
|
|
8442
9033
|
return {
|
|
8443
|
-
|
|
8444
|
-
|
|
8445
|
-
|
|
8446
|
-
|
|
8447
|
-
|
|
9034
|
+
...DEFAULT_CLAUDE_SETTINGS,
|
|
9035
|
+
attribution: options?.includeCoAuthor ? DEFAULT_CLAUDE_SETTINGS.attribution : {
|
|
9036
|
+
commit: "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)",
|
|
9037
|
+
pr: "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)"
|
|
9038
|
+
}
|
|
8448
9039
|
};
|
|
8449
9040
|
}
|
|
8450
|
-
const
|
|
8451
|
-
message: "
|
|
9041
|
+
const setupMode = await select({
|
|
9042
|
+
message: "How would you like to configure?",
|
|
9043
|
+
choices: [
|
|
9044
|
+
{ name: "Use a preset", value: "preset" },
|
|
9045
|
+
{ name: "Customize settings", value: "custom" }
|
|
9046
|
+
],
|
|
9047
|
+
default: "preset"
|
|
9048
|
+
});
|
|
9049
|
+
if (setupMode === "preset") {
|
|
9050
|
+
return promptClaudeSettingsPreset(options);
|
|
9051
|
+
}
|
|
9052
|
+
return promptClaudeSettingsCustom(options);
|
|
9053
|
+
}
|
|
9054
|
+
async function promptClaudeSettingsPreset(options) {
|
|
9055
|
+
const preset = await select({
|
|
9056
|
+
message: "Choose a settings preset:",
|
|
9057
|
+
choices: Object.entries(PRESET_DESCRIPTIONS2).map(([key, description]) => ({
|
|
9058
|
+
name: `${key.charAt(0).toUpperCase() + key.slice(1)} - ${description}`,
|
|
9059
|
+
value: key
|
|
9060
|
+
})),
|
|
9061
|
+
default: "default"
|
|
9062
|
+
});
|
|
9063
|
+
const config = { ...CLAUDE_SETTINGS_PRESETS[preset] };
|
|
9064
|
+
if (options?.includeCoAuthor === false) {
|
|
9065
|
+
config.attribution = {
|
|
9066
|
+
commit: "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)",
|
|
9067
|
+
pr: "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)"
|
|
9068
|
+
};
|
|
9069
|
+
}
|
|
9070
|
+
return config;
|
|
9071
|
+
}
|
|
9072
|
+
async function promptClaudeSettingsCustom(options) {
|
|
9073
|
+
const defaults = options?.defaults || DEFAULT_CLAUDE_SETTINGS;
|
|
9074
|
+
const model = await select({
|
|
9075
|
+
message: "Default Claude model:",
|
|
9076
|
+
choices: Object.entries(MODEL_DESCRIPTIONS).map(([key, description]) => ({
|
|
9077
|
+
name: description,
|
|
9078
|
+
value: key
|
|
9079
|
+
})),
|
|
9080
|
+
default: defaults.model || "sonnet"
|
|
9081
|
+
});
|
|
9082
|
+
const alwaysThinkingEnabled = await confirm({
|
|
9083
|
+
message: "Enable extended thinking by default? (better for complex tasks)",
|
|
9084
|
+
default: defaults.alwaysThinkingEnabled ?? false
|
|
9085
|
+
});
|
|
9086
|
+
const sandboxEnabled = await confirm({
|
|
9087
|
+
message: "Enable bash sandboxing? (isolates shell commands for security)",
|
|
9088
|
+
default: defaults.sandbox?.enabled ?? false
|
|
9089
|
+
});
|
|
9090
|
+
const permissionMode = await select({
|
|
9091
|
+
message: "Default permission mode:",
|
|
9092
|
+
choices: Object.entries(PERMISSION_MODE_DESCRIPTIONS).map(([key, description]) => ({
|
|
9093
|
+
name: description,
|
|
9094
|
+
value: key
|
|
9095
|
+
})),
|
|
9096
|
+
default: defaults.permissions?.defaultMode || "acceptEdits"
|
|
9097
|
+
});
|
|
9098
|
+
const cleanupPeriodStr = await input({
|
|
9099
|
+
message: "Session cleanup period (days, 0 = delete all immediately):",
|
|
9100
|
+
default: String(defaults.cleanupPeriodDays ?? 30),
|
|
9101
|
+
validate: (value) => {
|
|
9102
|
+
const num = Number.parseInt(value, 10);
|
|
9103
|
+
if (Number.isNaN(num) || num < 0 || num > 365) {
|
|
9104
|
+
return "Please enter a number between 0 and 365";
|
|
9105
|
+
}
|
|
9106
|
+
return true;
|
|
9107
|
+
}
|
|
9108
|
+
});
|
|
9109
|
+
const cleanupPeriodDays = Number.parseInt(cleanupPeriodStr, 10);
|
|
9110
|
+
const stopNotification = await select({
|
|
9111
|
+
message: "Notification when task completes:",
|
|
9112
|
+
choices: Object.entries(STOP_NOTIFICATION_DESCRIPTIONS).map(([key, description]) => ({
|
|
9113
|
+
name: description,
|
|
9114
|
+
value: key
|
|
9115
|
+
})),
|
|
9116
|
+
default: defaults.stopNotification || "beep"
|
|
9117
|
+
});
|
|
9118
|
+
let customStopCommand;
|
|
9119
|
+
if (stopNotification === "custom") {
|
|
9120
|
+
customStopCommand = await input({
|
|
9121
|
+
message: "Custom command to run on task completion:",
|
|
9122
|
+
default: defaults.customStopCommand || ""
|
|
9123
|
+
});
|
|
9124
|
+
}
|
|
9125
|
+
const config = {
|
|
9126
|
+
model,
|
|
9127
|
+
alwaysThinkingEnabled,
|
|
9128
|
+
sandbox: {
|
|
9129
|
+
enabled: sandboxEnabled,
|
|
9130
|
+
autoAllowBashIfSandboxed: true
|
|
9131
|
+
},
|
|
9132
|
+
permissions: {
|
|
9133
|
+
allow: defaults.permissions?.allow || [],
|
|
9134
|
+
deny: defaults.permissions?.deny || [],
|
|
9135
|
+
ask: defaults.permissions?.ask || [],
|
|
9136
|
+
defaultMode: permissionMode
|
|
9137
|
+
},
|
|
9138
|
+
attribution: options?.includeCoAuthor === false ? {
|
|
9139
|
+
commit: "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)",
|
|
9140
|
+
pr: "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)"
|
|
9141
|
+
} : DEFAULT_CLAUDE_SETTINGS.attribution,
|
|
9142
|
+
cleanupPeriodDays,
|
|
9143
|
+
stopNotification,
|
|
9144
|
+
customStopCommand
|
|
9145
|
+
};
|
|
9146
|
+
if (stopNotification === "beep") {
|
|
9147
|
+
config.hooks = {
|
|
9148
|
+
Stop: [{ hooks: [{ type: "command", command: BEEP_COMMAND, timeout: 5 }] }],
|
|
9149
|
+
SubagentStop: [{ hooks: [{ type: "command", command: BEEP_COMMAND, timeout: 5 }] }]
|
|
9150
|
+
};
|
|
9151
|
+
} else if (stopNotification === "custom" && customStopCommand) {
|
|
9152
|
+
config.hooks = {
|
|
9153
|
+
Stop: [{ hooks: [{ type: "command", command: customStopCommand, timeout: 10 }] }],
|
|
9154
|
+
SubagentStop: [{ hooks: [{ type: "command", command: customStopCommand, timeout: 10 }] }]
|
|
9155
|
+
};
|
|
9156
|
+
}
|
|
9157
|
+
return config;
|
|
9158
|
+
}
|
|
9159
|
+
|
|
9160
|
+
// src/cli/prompts/code-style.ts
|
|
9161
|
+
init_cjs_shims();
|
|
9162
|
+
var CODE_STYLE_TOOLS = [
|
|
9163
|
+
{
|
|
9164
|
+
name: "EditorConfig",
|
|
9165
|
+
value: "editorconfig",
|
|
9166
|
+
description: "Consistent coding styles across editors",
|
|
9167
|
+
checked: true
|
|
9168
|
+
},
|
|
9169
|
+
{
|
|
9170
|
+
name: "Commitlint",
|
|
9171
|
+
value: "commitlint",
|
|
9172
|
+
description: "Lint commit messages (conventional commits)",
|
|
9173
|
+
checked: true
|
|
9174
|
+
},
|
|
9175
|
+
{
|
|
9176
|
+
name: "Biome",
|
|
9177
|
+
value: "biome",
|
|
9178
|
+
description: "Fast linter and formatter (ESLint + Prettier alternative)",
|
|
9179
|
+
checked: false
|
|
9180
|
+
},
|
|
9181
|
+
{
|
|
9182
|
+
name: "Prettier",
|
|
9183
|
+
value: "prettier",
|
|
9184
|
+
description: "Code formatter (use if not using Biome)",
|
|
9185
|
+
checked: false
|
|
9186
|
+
}
|
|
9187
|
+
];
|
|
9188
|
+
async function promptCodeStyleConfig(options) {
|
|
9189
|
+
logger.section("Code Style", "\u{1F3A8}");
|
|
9190
|
+
logger.info("Configure code formatting and linting tools");
|
|
9191
|
+
logger.newline();
|
|
9192
|
+
const enableCodeStyle = await confirm({
|
|
9193
|
+
message: "Would you like to install code style configuration files?",
|
|
9194
|
+
default: true
|
|
9195
|
+
});
|
|
9196
|
+
if (!enableCodeStyle) {
|
|
9197
|
+
return {
|
|
9198
|
+
enabled: false,
|
|
9199
|
+
editorconfig: false,
|
|
9200
|
+
commitlint: false,
|
|
9201
|
+
biome: false,
|
|
9202
|
+
prettier: false
|
|
9203
|
+
};
|
|
9204
|
+
}
|
|
9205
|
+
const selectedTools = await checkbox({
|
|
9206
|
+
message: "Select the tools to configure:",
|
|
8452
9207
|
choices: CODE_STYLE_TOOLS.map((tool) => ({
|
|
8453
9208
|
name: `${tool.name} - ${tool.description}`,
|
|
8454
9209
|
value: tool.value,
|
|
@@ -9539,7 +10294,95 @@ async function promptSubagentStopHook() {
|
|
|
9539
10294
|
|
|
9540
10295
|
// src/cli/prompts/item-select.ts
|
|
9541
10296
|
init_cjs_shims();
|
|
9542
|
-
|
|
10297
|
+
|
|
10298
|
+
// src/cli/prompts/mutual-exclusivity.ts
|
|
10299
|
+
init_cjs_shims();
|
|
10300
|
+
function getConflictingSelections(module2, selectedIds) {
|
|
10301
|
+
if (!module2.alternativeTo) {
|
|
10302
|
+
return [];
|
|
10303
|
+
}
|
|
10304
|
+
return module2.alternativeTo.filter((altId) => selectedIds.includes(altId));
|
|
10305
|
+
}
|
|
10306
|
+
function createChoicesWithExclusivity(modules, selectedIds, options) {
|
|
10307
|
+
const choices = [];
|
|
10308
|
+
for (const module2 of modules) {
|
|
10309
|
+
const conflicts = getConflictingSelections(module2, selectedIds);
|
|
10310
|
+
const isConflicting = conflicts.length > 0;
|
|
10311
|
+
const isPreselected = options?.preselected?.includes(module2.id);
|
|
10312
|
+
const isSelected = selectedIds.includes(module2.id);
|
|
10313
|
+
let name = module2.name;
|
|
10314
|
+
let description = module2.description;
|
|
10315
|
+
let disabled = false;
|
|
10316
|
+
if (isConflicting) {
|
|
10317
|
+
const conflictNames = conflicts.join(", ");
|
|
10318
|
+
disabled = options?.showConflictReason ? `Conflicts with: ${conflictNames}` : `Alternative to ${conflictNames} (already selected)`;
|
|
10319
|
+
name = colors.muted(`${module2.name} (incompatible)`);
|
|
10320
|
+
description = `${colors.muted("\u26A0")} ${disabled}`;
|
|
10321
|
+
}
|
|
10322
|
+
choices.push({
|
|
10323
|
+
name,
|
|
10324
|
+
value: module2.id,
|
|
10325
|
+
description,
|
|
10326
|
+
disabled: isConflicting ? disabled : false,
|
|
10327
|
+
checked: isSelected || isPreselected && !isConflicting
|
|
10328
|
+
});
|
|
10329
|
+
}
|
|
10330
|
+
return choices;
|
|
10331
|
+
}
|
|
10332
|
+
function validateNoConflicts(selectedIds, allModules) {
|
|
10333
|
+
const conflicts = [];
|
|
10334
|
+
for (const id of selectedIds) {
|
|
10335
|
+
const module2 = allModules.find((m) => m.id === id);
|
|
10336
|
+
if (!module2?.alternativeTo) continue;
|
|
10337
|
+
for (const altId of module2.alternativeTo) {
|
|
10338
|
+
if (selectedIds.includes(altId)) {
|
|
10339
|
+
const existingConflict = conflicts.find(
|
|
10340
|
+
(c) => c.selected === id && c.conflictsWith === altId || c.selected === altId && c.conflictsWith === id
|
|
10341
|
+
);
|
|
10342
|
+
if (!existingConflict) {
|
|
10343
|
+
conflicts.push({ selected: id, conflictsWith: altId });
|
|
10344
|
+
}
|
|
10345
|
+
}
|
|
10346
|
+
}
|
|
10347
|
+
}
|
|
10348
|
+
return conflicts;
|
|
10349
|
+
}
|
|
10350
|
+
function groupByExclusivity(modules) {
|
|
10351
|
+
const groups = /* @__PURE__ */ new Map();
|
|
10352
|
+
const processedIds = /* @__PURE__ */ new Set();
|
|
10353
|
+
for (const module2 of modules) {
|
|
10354
|
+
if (processedIds.has(module2.id)) continue;
|
|
10355
|
+
const groupMembers = /* @__PURE__ */ new Set([module2.id]);
|
|
10356
|
+
const toProcess = [...module2.alternativeTo || []];
|
|
10357
|
+
while (toProcess.length > 0) {
|
|
10358
|
+
const altId = toProcess.pop();
|
|
10359
|
+
if (!altId || groupMembers.has(altId)) continue;
|
|
10360
|
+
const altModule = modules.find((m) => m.id === altId);
|
|
10361
|
+
if (altModule) {
|
|
10362
|
+
groupMembers.add(altId);
|
|
10363
|
+
for (const transitiveAlt of altModule.alternativeTo || []) {
|
|
10364
|
+
if (!groupMembers.has(transitiveAlt)) {
|
|
10365
|
+
toProcess.push(transitiveAlt);
|
|
10366
|
+
}
|
|
10367
|
+
}
|
|
10368
|
+
}
|
|
10369
|
+
}
|
|
10370
|
+
if (groupMembers.size > 1) {
|
|
10371
|
+
const sortedMembers = Array.from(groupMembers).sort();
|
|
10372
|
+
const groupId = `exclusivity-group-${sortedMembers[0]}`;
|
|
10373
|
+
groups.set(groupId, sortedMembers);
|
|
10374
|
+
for (const memberId of groupMembers) {
|
|
10375
|
+
processedIds.add(memberId);
|
|
10376
|
+
}
|
|
10377
|
+
} else {
|
|
10378
|
+
processedIds.add(module2.id);
|
|
10379
|
+
}
|
|
10380
|
+
}
|
|
10381
|
+
return groups;
|
|
10382
|
+
}
|
|
10383
|
+
|
|
10384
|
+
// src/cli/prompts/item-select.ts
|
|
10385
|
+
async function promptBatchAction(category, totalItems, options) {
|
|
9543
10386
|
const choices = [
|
|
9544
10387
|
{
|
|
9545
10388
|
name: "Install all (recommended)",
|
|
@@ -9557,7 +10400,14 @@ async function promptBatchAction(category, totalItems, hasPreset) {
|
|
|
9557
10400
|
description: `Skip all ${category}`
|
|
9558
10401
|
}
|
|
9559
10402
|
];
|
|
9560
|
-
if (
|
|
10403
|
+
if (options?.hasExclusivityGroups && options.exclusivityGroupCount) {
|
|
10404
|
+
choices.splice(1, 0, {
|
|
10405
|
+
name: colors.primary("Smart selection (handles conflicts)"),
|
|
10406
|
+
value: "smart",
|
|
10407
|
+
description: `Intelligent selection with ${options.exclusivityGroupCount} mutually exclusive group(s)`
|
|
10408
|
+
});
|
|
10409
|
+
}
|
|
10410
|
+
if (options?.hasPreset) {
|
|
9561
10411
|
choices.splice(1, 0, {
|
|
9562
10412
|
name: "Use preset for this category",
|
|
9563
10413
|
value: "preset",
|
|
@@ -9567,27 +10417,43 @@ async function promptBatchAction(category, totalItems, hasPreset) {
|
|
|
9567
10417
|
return select({
|
|
9568
10418
|
message: `${capitalize(category)} selection (${totalItems} available):`,
|
|
9569
10419
|
choices,
|
|
9570
|
-
default: "all"
|
|
10420
|
+
default: options?.hasExclusivityGroups ? "smart" : "all"
|
|
9571
10421
|
});
|
|
9572
10422
|
}
|
|
9573
10423
|
async function selectItemsFromCategory(category, items, options) {
|
|
9574
|
-
const selectedItems = [];
|
|
9575
|
-
const skippedItems = [];
|
|
9576
10424
|
logger.newline();
|
|
9577
10425
|
logger.subtitle(`${capitalize(category)} Selection`);
|
|
9578
10426
|
logger.info(`${items.length} ${category} available`);
|
|
10427
|
+
const exclusivityGroups = groupByExclusivity(items);
|
|
10428
|
+
const hasExclusivityGroups = exclusivityGroups.size > 0;
|
|
10429
|
+
if (hasExclusivityGroups) {
|
|
10430
|
+
logger.info(
|
|
10431
|
+
colors.warning(
|
|
10432
|
+
`${exclusivityGroups.size} group(s) of mutually exclusive ${category} detected`
|
|
10433
|
+
)
|
|
10434
|
+
);
|
|
10435
|
+
}
|
|
9579
10436
|
logger.newline();
|
|
9580
|
-
const batchAction = await promptBatchAction(
|
|
9581
|
-
|
|
9582
|
-
|
|
9583
|
-
|
|
9584
|
-
);
|
|
10437
|
+
const batchAction = await promptBatchAction(category, items.length, {
|
|
10438
|
+
hasPreset: options?.preselected && options.preselected.length > 0,
|
|
10439
|
+
hasExclusivityGroups,
|
|
10440
|
+
exclusivityGroupCount: exclusivityGroups.size
|
|
10441
|
+
});
|
|
9585
10442
|
if (batchAction === "all") {
|
|
9586
|
-
|
|
9587
|
-
|
|
9588
|
-
|
|
9589
|
-
|
|
9590
|
-
|
|
10443
|
+
const conflicts = validateNoConflicts(
|
|
10444
|
+
items.map((i) => i.id),
|
|
10445
|
+
items
|
|
10446
|
+
);
|
|
10447
|
+
if (conflicts.length > 0) {
|
|
10448
|
+
logger.warn("Cannot install all: some modules are mutually exclusive.");
|
|
10449
|
+
logger.info("Switching to smart selection mode...");
|
|
10450
|
+
} else {
|
|
10451
|
+
return {
|
|
10452
|
+
category,
|
|
10453
|
+
selectedItems: items.map((i) => i.id),
|
|
10454
|
+
skippedItems: []
|
|
10455
|
+
};
|
|
10456
|
+
}
|
|
9591
10457
|
}
|
|
9592
10458
|
if (batchAction === "none") {
|
|
9593
10459
|
return {
|
|
@@ -9598,24 +10464,106 @@ async function selectItemsFromCategory(category, items, options) {
|
|
|
9598
10464
|
}
|
|
9599
10465
|
if (batchAction === "preset" && options?.preselected) {
|
|
9600
10466
|
const preselectedSet = new Set(options.preselected);
|
|
9601
|
-
|
|
10467
|
+
const preselectedIds = items.filter((i) => preselectedSet.has(i.id)).map((i) => i.id);
|
|
10468
|
+
const conflicts = validateNoConflicts(preselectedIds, items);
|
|
10469
|
+
if (conflicts.length > 0) {
|
|
10470
|
+
logger.warn("Preset contains conflicting modules. Switching to smart selection...");
|
|
10471
|
+
} else {
|
|
10472
|
+
return {
|
|
10473
|
+
category,
|
|
10474
|
+
selectedItems: preselectedIds,
|
|
10475
|
+
skippedItems: items.filter((i) => !preselectedSet.has(i.id)).map((i) => i.id)
|
|
10476
|
+
};
|
|
10477
|
+
}
|
|
10478
|
+
}
|
|
10479
|
+
if (batchAction === "smart" || batchAction === "all" || batchAction === "preset") {
|
|
10480
|
+
return selectItemsWithExclusivity(category, items, options);
|
|
10481
|
+
}
|
|
10482
|
+
return selectItemsOneByOne(category, items, options);
|
|
10483
|
+
}
|
|
10484
|
+
async function selectItemsWithExclusivity(category, items, options) {
|
|
10485
|
+
let selectedIds = options?.preselected?.filter((id) => items.some((i) => i.id === id)) || [];
|
|
10486
|
+
let confirmed = false;
|
|
10487
|
+
while (!confirmed) {
|
|
10488
|
+
const choices = createChoicesWithExclusivity(items, selectedIds, {
|
|
10489
|
+
preselected: options?.preselected,
|
|
10490
|
+
showConflictReason: true
|
|
10491
|
+
});
|
|
10492
|
+
logger.newline();
|
|
10493
|
+
logger.info(
|
|
10494
|
+
colors.muted("Items marked as incompatible are disabled based on your selections.")
|
|
10495
|
+
);
|
|
10496
|
+
logger.info(colors.muted("Uncheck items to enable their alternatives."));
|
|
10497
|
+
logger.newline();
|
|
10498
|
+
const newSelection = await checkbox({
|
|
10499
|
+
message: `Select ${category} (Space to toggle, Enter to confirm):`,
|
|
10500
|
+
choices: choices.map((c) => ({
|
|
10501
|
+
name: c.name,
|
|
10502
|
+
value: c.value,
|
|
10503
|
+
description: c.description,
|
|
10504
|
+
disabled: c.disabled,
|
|
10505
|
+
checked: c.checked
|
|
10506
|
+
})),
|
|
10507
|
+
required: false
|
|
10508
|
+
});
|
|
10509
|
+
const conflicts = validateNoConflicts(newSelection, items);
|
|
10510
|
+
if (conflicts.length > 0) {
|
|
10511
|
+
logger.warn("Your selection contains conflicts:");
|
|
10512
|
+
for (const conflict of conflicts) {
|
|
10513
|
+
const module1 = items.find((i) => i.id === conflict.selected)?.name || conflict.selected;
|
|
10514
|
+
const module2 = items.find((i) => i.id === conflict.conflictsWith)?.name || conflict.conflictsWith;
|
|
10515
|
+
logger.info(` ${colors.error("\u2022")} ${module1} and ${module2} are mutually exclusive`);
|
|
10516
|
+
}
|
|
10517
|
+
logger.info("Please adjust your selection.");
|
|
10518
|
+
selectedIds = newSelection;
|
|
10519
|
+
continue;
|
|
10520
|
+
}
|
|
10521
|
+
selectedIds = newSelection;
|
|
10522
|
+
showCategorySelectionSummary({
|
|
9602
10523
|
category,
|
|
9603
|
-
selectedItems:
|
|
9604
|
-
skippedItems: items.filter((i) => !
|
|
9605
|
-
};
|
|
10524
|
+
selectedItems: selectedIds,
|
|
10525
|
+
skippedItems: items.filter((i) => !selectedIds.includes(i.id)).map((i) => i.id)
|
|
10526
|
+
});
|
|
10527
|
+
confirmed = await confirm({
|
|
10528
|
+
message: "Is this selection correct?",
|
|
10529
|
+
default: true
|
|
10530
|
+
});
|
|
9606
10531
|
}
|
|
10532
|
+
return {
|
|
10533
|
+
category,
|
|
10534
|
+
selectedItems: selectedIds,
|
|
10535
|
+
skippedItems: items.filter((i) => !selectedIds.includes(i.id)).map((i) => i.id)
|
|
10536
|
+
};
|
|
10537
|
+
}
|
|
10538
|
+
async function selectItemsOneByOne(category, items, options) {
|
|
10539
|
+
const selectedItems = [];
|
|
10540
|
+
const skippedItems = [];
|
|
9607
10541
|
const remainingItems = [...items];
|
|
9608
10542
|
let currentIndex = 0;
|
|
9609
10543
|
while (currentIndex < remainingItems.length) {
|
|
9610
10544
|
const item = remainingItems[currentIndex];
|
|
9611
10545
|
const remaining = remainingItems.length - currentIndex - 1;
|
|
9612
10546
|
const isPreselected = options?.preselected?.includes(item.id);
|
|
10547
|
+
const conflicts = item.alternativeTo?.filter((altId) => selectedItems.includes(altId)) || [];
|
|
10548
|
+
if (conflicts.length > 0) {
|
|
10549
|
+
const conflictNames = conflicts.map((id) => items.find((i) => i.id === id)?.name || id).join(", ");
|
|
10550
|
+
logger.info(
|
|
10551
|
+
colors.muted(`Skipping ${item.name} (conflicts with selected: ${conflictNames})`)
|
|
10552
|
+
);
|
|
10553
|
+
skippedItems.push(item.id);
|
|
10554
|
+
currentIndex++;
|
|
10555
|
+
continue;
|
|
10556
|
+
}
|
|
9613
10557
|
const progress = colors.muted(`[${currentIndex + 1}/${remainingItems.length}]`);
|
|
9614
10558
|
console.log(`
|
|
9615
10559
|
${progress} ${colors.bold(item.name)}`);
|
|
9616
10560
|
if (options?.showDescriptions && item.description) {
|
|
9617
10561
|
logger.note(item.description);
|
|
9618
10562
|
}
|
|
10563
|
+
if (item.alternativeTo && item.alternativeTo.length > 0) {
|
|
10564
|
+
const altNames = item.alternativeTo.map((id) => items.find((i) => i.id === id)?.name || id).join(", ");
|
|
10565
|
+
logger.info(colors.warning(`\u26A0 Selecting this will disable: ${altNames}`));
|
|
10566
|
+
}
|
|
9619
10567
|
const action = await promptItemWithShortcuts(item, {
|
|
9620
10568
|
defaultInstall: isPreselected,
|
|
9621
10569
|
remainingCount: remaining
|
|
@@ -9632,7 +10580,13 @@ ${progress} ${colors.bold(item.name)}`);
|
|
|
9632
10580
|
case "install-rest":
|
|
9633
10581
|
selectedItems.push(item.id);
|
|
9634
10582
|
for (let i = currentIndex + 1; i < remainingItems.length; i++) {
|
|
9635
|
-
|
|
10583
|
+
const nextItem = remainingItems[i];
|
|
10584
|
+
const nextConflicts = nextItem.alternativeTo?.filter((altId) => selectedItems.includes(altId)) || [];
|
|
10585
|
+
if (nextConflicts.length === 0) {
|
|
10586
|
+
selectedItems.push(nextItem.id);
|
|
10587
|
+
} else {
|
|
10588
|
+
skippedItems.push(nextItem.id);
|
|
10589
|
+
}
|
|
9636
10590
|
}
|
|
9637
10591
|
currentIndex = remainingItems.length;
|
|
9638
10592
|
break;
|
|
@@ -9674,6 +10628,17 @@ async function promptItemWithShortcuts(item, options) {
|
|
|
9674
10628
|
default: options.defaultInstall !== false ? "install" : "skip"
|
|
9675
10629
|
});
|
|
9676
10630
|
}
|
|
10631
|
+
function showCategorySelectionSummary(result) {
|
|
10632
|
+
const { category, selectedItems, skippedItems } = result;
|
|
10633
|
+
logger.newline();
|
|
10634
|
+
logger.subtitle(`${capitalize(category)} Summary`);
|
|
10635
|
+
if (selectedItems.length > 0) {
|
|
10636
|
+
logger.success(`Selected (${selectedItems.length}): ${selectedItems.join(", ")}`);
|
|
10637
|
+
}
|
|
10638
|
+
if (skippedItems.length > 0) {
|
|
10639
|
+
logger.info(`Skipped (${skippedItems.length}): ${colors.muted(skippedItems.join(", "))}`);
|
|
10640
|
+
}
|
|
10641
|
+
}
|
|
9677
10642
|
function capitalize(str) {
|
|
9678
10643
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
9679
10644
|
}
|
|
@@ -10437,6 +11402,122 @@ async function confirmProjectInfo(info) {
|
|
|
10437
11402
|
});
|
|
10438
11403
|
}
|
|
10439
11404
|
|
|
11405
|
+
// src/cli/prompts/related-skills.ts
|
|
11406
|
+
init_cjs_shims();
|
|
11407
|
+
function getAllRelatedSkillIds(agentModules) {
|
|
11408
|
+
const relatedIds = /* @__PURE__ */ new Set();
|
|
11409
|
+
for (const agent of agentModules) {
|
|
11410
|
+
if (agent.relatedSkills) {
|
|
11411
|
+
for (const skillId of agent.relatedSkills) {
|
|
11412
|
+
relatedIds.add(skillId);
|
|
11413
|
+
}
|
|
11414
|
+
}
|
|
11415
|
+
}
|
|
11416
|
+
return Array.from(relatedIds);
|
|
11417
|
+
}
|
|
11418
|
+
function getAgentsWithRelatedSkills(selectedAgentIds, agentModules) {
|
|
11419
|
+
return agentModules.filter(
|
|
11420
|
+
(agent) => selectedAgentIds.includes(agent.id) && agent.relatedSkills && agent.relatedSkills.length > 0
|
|
11421
|
+
);
|
|
11422
|
+
}
|
|
11423
|
+
async function promptRelatedSkillsForAgent(agent, skillModules, options) {
|
|
11424
|
+
if (!agent.relatedSkills || agent.relatedSkills.length === 0) {
|
|
11425
|
+
return [];
|
|
11426
|
+
}
|
|
11427
|
+
const relatedSkillDefs = agent.relatedSkills.map((skillId) => skillModules.find((s) => s.id === skillId)).filter((s) => s !== void 0);
|
|
11428
|
+
if (relatedSkillDefs.length === 0) {
|
|
11429
|
+
return [];
|
|
11430
|
+
}
|
|
11431
|
+
if (relatedSkillDefs.length === 1) {
|
|
11432
|
+
const skill = relatedSkillDefs[0];
|
|
11433
|
+
const action = await select({
|
|
11434
|
+
message: `${colors.primary(agent.name)} has a related skill. Install it?`,
|
|
11435
|
+
choices: [
|
|
11436
|
+
{
|
|
11437
|
+
name: `Install ${skill.name}`,
|
|
11438
|
+
value: "install",
|
|
11439
|
+
description: skill.description
|
|
11440
|
+
},
|
|
11441
|
+
{
|
|
11442
|
+
name: "Skip",
|
|
11443
|
+
value: "skip",
|
|
11444
|
+
description: "Do not install this skill"
|
|
11445
|
+
}
|
|
11446
|
+
],
|
|
11447
|
+
default: "install"
|
|
11448
|
+
});
|
|
11449
|
+
return action === "install" ? [skill.id] : [];
|
|
11450
|
+
}
|
|
11451
|
+
const choices = relatedSkillDefs.map((skill) => ({
|
|
11452
|
+
name: skill.name,
|
|
11453
|
+
value: skill.id,
|
|
11454
|
+
description: skill.description,
|
|
11455
|
+
checked: options?.preselected?.includes(skill.id) ?? false
|
|
11456
|
+
}));
|
|
11457
|
+
if (options?.allowMultiple !== false) {
|
|
11458
|
+
const selected2 = await checkbox({
|
|
11459
|
+
message: `${colors.primary(agent.name)} supports multiple frameworks. Select which patterns to install:`,
|
|
11460
|
+
choices,
|
|
11461
|
+
required: false
|
|
11462
|
+
});
|
|
11463
|
+
return selected2;
|
|
11464
|
+
}
|
|
11465
|
+
const choicesWithSkip = [
|
|
11466
|
+
...choices.map((c) => ({ name: c.name, value: c.value, description: c.description })),
|
|
11467
|
+
{
|
|
11468
|
+
name: colors.muted("Skip (none)"),
|
|
11469
|
+
value: "__skip__",
|
|
11470
|
+
description: "Do not install any related skill for this agent"
|
|
11471
|
+
}
|
|
11472
|
+
];
|
|
11473
|
+
const selected = await select({
|
|
11474
|
+
message: `${colors.primary(agent.name)} supports multiple frameworks. Select which patterns to use:`,
|
|
11475
|
+
choices: choicesWithSkip,
|
|
11476
|
+
default: choicesWithSkip[0]?.value
|
|
11477
|
+
});
|
|
11478
|
+
return selected === "__skip__" ? [] : [selected];
|
|
11479
|
+
}
|
|
11480
|
+
async function promptAllRelatedSkills(selectedAgentIds, registry, options) {
|
|
11481
|
+
const agentsWithRelated = getAgentsWithRelatedSkills(selectedAgentIds, registry.agents);
|
|
11482
|
+
const allRelatedSkillIds = getAllRelatedSkillIds(registry.agents);
|
|
11483
|
+
if (agentsWithRelated.length === 0) {
|
|
11484
|
+
return {
|
|
11485
|
+
relatedSkillsSelected: [],
|
|
11486
|
+
relatedSkillsSkipped: [],
|
|
11487
|
+
allRelatedSkillIds
|
|
11488
|
+
};
|
|
11489
|
+
}
|
|
11490
|
+
logger.newline();
|
|
11491
|
+
logger.subtitle("Framework-Specific Skills");
|
|
11492
|
+
logger.info(
|
|
11493
|
+
"The selected agents support framework-specific patterns. Select which ones to install."
|
|
11494
|
+
);
|
|
11495
|
+
logger.newline();
|
|
11496
|
+
const selectedSkills = [];
|
|
11497
|
+
const skippedSkills = [];
|
|
11498
|
+
for (const agent of agentsWithRelated) {
|
|
11499
|
+
const selected = await promptRelatedSkillsForAgent(agent, registry.skills, {
|
|
11500
|
+
preselected: options?.preselectedSkills,
|
|
11501
|
+
allowMultiple: options?.allowMultiplePerAgent
|
|
11502
|
+
});
|
|
11503
|
+
selectedSkills.push(...selected);
|
|
11504
|
+
const agentRelatedIds = agent.relatedSkills || [];
|
|
11505
|
+
const skipped = agentRelatedIds.filter((id) => !selected.includes(id));
|
|
11506
|
+
skippedSkills.push(...skipped);
|
|
11507
|
+
}
|
|
11508
|
+
const uniqueSelected = [...new Set(selectedSkills)];
|
|
11509
|
+
const uniqueSkipped = [...new Set(skippedSkills)].filter((id) => !uniqueSelected.includes(id));
|
|
11510
|
+
return {
|
|
11511
|
+
relatedSkillsSelected: uniqueSelected,
|
|
11512
|
+
relatedSkillsSkipped: uniqueSkipped,
|
|
11513
|
+
allRelatedSkillIds
|
|
11514
|
+
};
|
|
11515
|
+
}
|
|
11516
|
+
function filterOutRelatedSkills(skillModules, relatedSkillIds) {
|
|
11517
|
+
const relatedSet = new Set(relatedSkillIds);
|
|
11518
|
+
return skillModules.filter((skill) => !relatedSet.has(skill.id));
|
|
11519
|
+
}
|
|
11520
|
+
|
|
10440
11521
|
// src/cli/prompts/scaffold.ts
|
|
10441
11522
|
init_cjs_shims();
|
|
10442
11523
|
async function promptScaffoldType(options) {
|
|
@@ -10626,27 +11707,27 @@ async function buildConfigContext(projectPath) {
|
|
|
10626
11707
|
values: {}
|
|
10627
11708
|
};
|
|
10628
11709
|
try {
|
|
10629
|
-
const
|
|
10630
|
-
const
|
|
10631
|
-
const pkgPath =
|
|
10632
|
-
const pkgContent = await
|
|
11710
|
+
const fs9 = await import("fs/promises");
|
|
11711
|
+
const path10 = await import("path");
|
|
11712
|
+
const pkgPath = path10.join(projectPath, "package.json");
|
|
11713
|
+
const pkgContent = await fs9.readFile(pkgPath, "utf-8");
|
|
10633
11714
|
const pkg = JSON.parse(pkgContent);
|
|
10634
11715
|
context.scripts = pkg.scripts || {};
|
|
10635
11716
|
context.dependencies = {
|
|
10636
11717
|
...pkg.dependencies || {},
|
|
10637
11718
|
...pkg.devDependencies || {}
|
|
10638
11719
|
};
|
|
10639
|
-
context.hasTypeScript = Boolean(context.dependencies.typescript) || await fileExists(
|
|
10640
|
-
if (await fileExists(
|
|
11720
|
+
context.hasTypeScript = Boolean(context.dependencies.typescript) || await fileExists(path10.join(projectPath, "tsconfig.json"));
|
|
11721
|
+
if (await fileExists(path10.join(projectPath, "pnpm-lock.yaml"))) {
|
|
10641
11722
|
context.packageManager = "pnpm";
|
|
10642
|
-
} else if (await fileExists(
|
|
11723
|
+
} else if (await fileExists(path10.join(projectPath, "yarn.lock"))) {
|
|
10643
11724
|
context.packageManager = "yarn";
|
|
10644
|
-
} else if (await fileExists(
|
|
11725
|
+
} else if (await fileExists(path10.join(projectPath, "bun.lockb"))) {
|
|
10645
11726
|
context.packageManager = "bun";
|
|
10646
11727
|
} else {
|
|
10647
11728
|
context.packageManager = "npm";
|
|
10648
11729
|
}
|
|
10649
|
-
context.isGitRepo = await fileExists(
|
|
11730
|
+
context.isGitRepo = await fileExists(path10.join(projectPath, ".git"));
|
|
10650
11731
|
if (context.isGitRepo) {
|
|
10651
11732
|
try {
|
|
10652
11733
|
const { execSync } = await import("child_process");
|
|
@@ -10665,8 +11746,8 @@ async function buildConfigContext(projectPath) {
|
|
|
10665
11746
|
}
|
|
10666
11747
|
async function fileExists(filePath) {
|
|
10667
11748
|
try {
|
|
10668
|
-
const
|
|
10669
|
-
await
|
|
11749
|
+
const fs9 = await import("fs/promises");
|
|
11750
|
+
await fs9.access(filePath);
|
|
10670
11751
|
return true;
|
|
10671
11752
|
} catch {
|
|
10672
11753
|
return false;
|
|
@@ -11198,11 +12279,66 @@ function createBundleSelectionStep() {
|
|
|
11198
12279
|
}
|
|
11199
12280
|
if (mode === "individual" || mode === "both") {
|
|
11200
12281
|
const preselectedFromBundles = resolveBundles(result.selectedBundles);
|
|
11201
|
-
const categories = ["agents", "skills", "commands", "docs"];
|
|
11202
12282
|
if (ctx.registry) {
|
|
11203
12283
|
logger.newline();
|
|
11204
12284
|
logger.subtitle("Individual Module Selection");
|
|
11205
|
-
|
|
12285
|
+
const agentPreselected = mode === "both" ? preselectedFromBundles.agents : [];
|
|
12286
|
+
const agentResult = await selectItemsFromCategory("agents", ctx.registry.agents, {
|
|
12287
|
+
preselected: agentPreselected,
|
|
12288
|
+
showDescriptions: true
|
|
12289
|
+
});
|
|
12290
|
+
if (mode === "both") {
|
|
12291
|
+
result.additionalModules.agents = agentResult.selectedItems.filter(
|
|
12292
|
+
(id) => !agentPreselected.includes(id)
|
|
12293
|
+
);
|
|
12294
|
+
} else {
|
|
12295
|
+
result.additionalModules.agents = agentResult.selectedItems;
|
|
12296
|
+
}
|
|
12297
|
+
const allSelectedAgents = [
|
|
12298
|
+
...preselectedFromBundles.agents,
|
|
12299
|
+
...result.additionalModules.agents
|
|
12300
|
+
];
|
|
12301
|
+
const relatedSkillsResult = await promptAllRelatedSkills(
|
|
12302
|
+
allSelectedAgents,
|
|
12303
|
+
ctx.registry,
|
|
12304
|
+
{
|
|
12305
|
+
preselectedSkills: mode === "both" ? preselectedFromBundles.skills : [],
|
|
12306
|
+
allowMultiplePerAgent: true
|
|
12307
|
+
}
|
|
12308
|
+
);
|
|
12309
|
+
const skillPreselected = mode === "both" ? preselectedFromBundles.skills : [];
|
|
12310
|
+
const independentSkills = filterOutRelatedSkills(
|
|
12311
|
+
ctx.registry.skills,
|
|
12312
|
+
relatedSkillsResult.allRelatedSkillIds
|
|
12313
|
+
);
|
|
12314
|
+
const independentPreselected = skillPreselected.filter(
|
|
12315
|
+
(id) => !relatedSkillsResult.allRelatedSkillIds.includes(id)
|
|
12316
|
+
);
|
|
12317
|
+
let independentSkillsSelected = [];
|
|
12318
|
+
if (independentSkills.length > 0) {
|
|
12319
|
+
logger.newline();
|
|
12320
|
+
logger.info(
|
|
12321
|
+
colors.muted("Now selecting general-purpose skills (not framework-specific):")
|
|
12322
|
+
);
|
|
12323
|
+
const skillResult = await selectItemsFromCategory("skills", independentSkills, {
|
|
12324
|
+
preselected: independentPreselected,
|
|
12325
|
+
showDescriptions: true
|
|
12326
|
+
});
|
|
12327
|
+
independentSkillsSelected = skillResult.selectedItems;
|
|
12328
|
+
}
|
|
12329
|
+
const allSelectedSkills = [
|
|
12330
|
+
...relatedSkillsResult.relatedSkillsSelected,
|
|
12331
|
+
...independentSkillsSelected
|
|
12332
|
+
];
|
|
12333
|
+
if (mode === "both") {
|
|
12334
|
+
result.additionalModules.skills = allSelectedSkills.filter(
|
|
12335
|
+
(id) => !skillPreselected.includes(id)
|
|
12336
|
+
);
|
|
12337
|
+
} else {
|
|
12338
|
+
result.additionalModules.skills = allSelectedSkills;
|
|
12339
|
+
}
|
|
12340
|
+
const otherCategories = ["commands", "docs"];
|
|
12341
|
+
for (const category of otherCategories) {
|
|
11206
12342
|
const preselected = mode === "both" ? preselectedFromBundles[category] : [];
|
|
11207
12343
|
const categoryResult = await selectItemsFromCategory(category, ctx.registry[category], {
|
|
11208
12344
|
preselected,
|
|
@@ -11374,6 +12510,28 @@ function createTemplateConfigStep() {
|
|
|
11374
12510
|
}
|
|
11375
12511
|
};
|
|
11376
12512
|
}
|
|
12513
|
+
function createClaudeSettingsStep() {
|
|
12514
|
+
return {
|
|
12515
|
+
metadata: {
|
|
12516
|
+
id: "claudeSettings",
|
|
12517
|
+
name: "Claude Code Settings",
|
|
12518
|
+
description: "Configure Claude Code model, permissions, and behavior",
|
|
12519
|
+
required: false
|
|
12520
|
+
},
|
|
12521
|
+
computeDefaults: (ctx) => ctx.claudeSettings,
|
|
12522
|
+
execute: async (ctx, defaults) => {
|
|
12523
|
+
const goBack = await promptBackOption(11, "Configure Claude Code settings or go back?");
|
|
12524
|
+
if (goBack) {
|
|
12525
|
+
return createResult(defaults, "back");
|
|
12526
|
+
}
|
|
12527
|
+
const value = await promptClaudeSettings({
|
|
12528
|
+
defaults,
|
|
12529
|
+
includeCoAuthor: ctx.preferences?.includeCoAuthor
|
|
12530
|
+
});
|
|
12531
|
+
return createResult(value, "next");
|
|
12532
|
+
}
|
|
12533
|
+
};
|
|
12534
|
+
}
|
|
11377
12535
|
function createInitWizardConfig(projectPath, detection, registry) {
|
|
11378
12536
|
void projectPath;
|
|
11379
12537
|
void detection;
|
|
@@ -11422,6 +12580,10 @@ function createInitWizardConfig(projectPath, detection, registry) {
|
|
|
11422
12580
|
{
|
|
11423
12581
|
id: "templateConfig",
|
|
11424
12582
|
definition: createTemplateConfigStep()
|
|
12583
|
+
},
|
|
12584
|
+
{
|
|
12585
|
+
id: "claudeSettings",
|
|
12586
|
+
definition: createClaudeSettingsStep()
|
|
11425
12587
|
}
|
|
11426
12588
|
];
|
|
11427
12589
|
return {
|
|
@@ -11700,8 +12862,8 @@ function createInitCommand() {
|
|
|
11700
12862
|
).option("-t, --template <url>", "Remote git repo for templates").option("--branch <name>", "Branch/tag for remote template").option("-y, --yes", "Accept defaults, skip prompts").option("-f, --force", "Overwrite existing .claude/").option("--dry-run", "Show what would happen without making changes").option("--claude-only", "Only Claude config, no project scaffold").option("--no-placeholders", "Skip placeholder replacement").option("--no-mcp", "Skip MCP configuration").option("-v, --verbose", "Detailed output").action(runInit);
|
|
11701
12863
|
return cmd;
|
|
11702
12864
|
}
|
|
11703
|
-
async function runInit(
|
|
11704
|
-
const projectPath = resolvePath(
|
|
12865
|
+
async function runInit(path10, options) {
|
|
12866
|
+
const projectPath = resolvePath(path10 || ".");
|
|
11705
12867
|
logger.configure({ verbose: options.verbose, silent: false });
|
|
11706
12868
|
logger.title("@qazuor/claude-code-config");
|
|
11707
12869
|
logger.info(`Initializing Claude configuration in ${colors.primary(projectPath)}`);
|
|
@@ -11710,6 +12872,7 @@ async function runInit(path7, options) {
|
|
|
11710
12872
|
showCancelHint();
|
|
11711
12873
|
}
|
|
11712
12874
|
try {
|
|
12875
|
+
let existingConfig = null;
|
|
11713
12876
|
if (await hasExistingClaudeConfig(projectPath)) {
|
|
11714
12877
|
if (!options.force) {
|
|
11715
12878
|
const action = await promptExistingProjectAction();
|
|
@@ -11720,6 +12883,10 @@ async function runInit(path7, options) {
|
|
|
11720
12883
|
if (action !== "overwrite" && action !== "merge") {
|
|
11721
12884
|
return;
|
|
11722
12885
|
}
|
|
12886
|
+
existingConfig = await readConfig(projectPath);
|
|
12887
|
+
if (existingConfig) {
|
|
12888
|
+
logger.info("Using existing configuration as defaults");
|
|
12889
|
+
}
|
|
11723
12890
|
}
|
|
11724
12891
|
}
|
|
11725
12892
|
const detection = await detectProject(projectPath);
|
|
@@ -11736,12 +12903,12 @@ async function runInit(path7, options) {
|
|
|
11736
12903
|
() => loadRegistry(templatesPath),
|
|
11737
12904
|
{ silent: options.dryRun }
|
|
11738
12905
|
);
|
|
11739
|
-
const buildResult = options.yes ? await buildDefaultConfig(projectPath, detection, options) : await buildInteractiveConfig(projectPath, detection, registry, options);
|
|
12906
|
+
const buildResult = options.yes ? await buildDefaultConfig(projectPath, detection, options) : await buildInteractiveConfig(projectPath, detection, registry, options, existingConfig);
|
|
11740
12907
|
if (!buildResult) {
|
|
11741
12908
|
logger.warn("Configuration cancelled");
|
|
11742
12909
|
return;
|
|
11743
12910
|
}
|
|
11744
|
-
const { config, skippedMcpConfigs, templateConfig, cicdConfig } = buildResult;
|
|
12911
|
+
const { config, skippedMcpConfigs, templateConfig, cicdConfig, claudeSettings } = buildResult;
|
|
11745
12912
|
if (templateConfig) {
|
|
11746
12913
|
config.templateConfig = templateConfig;
|
|
11747
12914
|
}
|
|
@@ -11758,7 +12925,15 @@ async function runInit(path7, options) {
|
|
|
11758
12925
|
showConfigSummary(config);
|
|
11759
12926
|
return;
|
|
11760
12927
|
}
|
|
11761
|
-
await executeInstallation(
|
|
12928
|
+
await executeInstallation(
|
|
12929
|
+
projectPath,
|
|
12930
|
+
config,
|
|
12931
|
+
registry,
|
|
12932
|
+
templatesPath,
|
|
12933
|
+
options,
|
|
12934
|
+
cicdConfig,
|
|
12935
|
+
claudeSettings
|
|
12936
|
+
);
|
|
11762
12937
|
if (templateConfig && !options.noPlaceholders) {
|
|
11763
12938
|
const claudePath = joinPath(projectPath, ".claude");
|
|
11764
12939
|
await replaceTemplateConfigWithSpinner(claudePath, templateConfig);
|
|
@@ -11902,7 +13077,7 @@ async function buildDefaultConfig(projectPath, detection, options) {
|
|
|
11902
13077
|
// No MCP servers in default mode
|
|
11903
13078
|
};
|
|
11904
13079
|
}
|
|
11905
|
-
async function buildInteractiveConfig(projectPath, detection, registry, options) {
|
|
13080
|
+
async function buildInteractiveConfig(projectPath, detection, registry, options, existingConfig) {
|
|
11906
13081
|
const projectName = await getProjectName(projectPath);
|
|
11907
13082
|
const projectDesc = await getProjectDescription(projectPath);
|
|
11908
13083
|
const wizardConfig = createInitWizardConfig(
|
|
@@ -11916,24 +13091,45 @@ async function buildInteractiveConfig(projectPath, detection, registry, options)
|
|
|
11916
13091
|
},
|
|
11917
13092
|
registry
|
|
11918
13093
|
);
|
|
13094
|
+
const existingProject = existingConfig?.project;
|
|
13095
|
+
const existingPrefs = existingConfig?.preferences;
|
|
11919
13096
|
const initialContext = {
|
|
11920
13097
|
projectPath,
|
|
11921
13098
|
registry,
|
|
11922
13099
|
detection: {
|
|
11923
13100
|
detected: detection.detected,
|
|
11924
13101
|
projectType: detection.projectType,
|
|
11925
|
-
packageManager: detection.packageManager,
|
|
13102
|
+
packageManager: existingPrefs?.packageManager || detection.packageManager,
|
|
11926
13103
|
suggestedBundles: detection.suggestedBundles,
|
|
11927
13104
|
detectedTechnologies: detection.detectedTechnologies
|
|
11928
13105
|
},
|
|
13106
|
+
// Use existing project info as defaults, fallback to detected values
|
|
11929
13107
|
projectInfo: {
|
|
11930
|
-
name: projectName || "",
|
|
11931
|
-
description: projectDesc || "",
|
|
11932
|
-
org: "",
|
|
11933
|
-
repo: projectName?.toLowerCase().replace(/\s+/g, "-") || "",
|
|
11934
|
-
entityType: "item",
|
|
11935
|
-
entityTypePlural: "items"
|
|
11936
|
-
|
|
13108
|
+
name: existingProject?.name || projectName || "",
|
|
13109
|
+
description: existingProject?.description || projectDesc || "",
|
|
13110
|
+
org: existingProject?.org || "",
|
|
13111
|
+
repo: existingProject?.repo || projectName?.toLowerCase().replace(/\s+/g, "-") || "",
|
|
13112
|
+
entityType: existingProject?.entityType || "item",
|
|
13113
|
+
entityTypePlural: existingProject?.entityTypePlural || "items",
|
|
13114
|
+
domain: existingProject?.domain,
|
|
13115
|
+
location: existingProject?.location,
|
|
13116
|
+
author: existingProject?.author
|
|
13117
|
+
},
|
|
13118
|
+
// Use existing preferences as defaults
|
|
13119
|
+
preferences: existingPrefs ? {
|
|
13120
|
+
language: existingPrefs.language,
|
|
13121
|
+
responseLanguage: existingPrefs.responseLanguage,
|
|
13122
|
+
includeCoAuthor: existingPrefs.includeCoAuthor,
|
|
13123
|
+
packageManager: existingPrefs.packageManager
|
|
13124
|
+
} : void 0,
|
|
13125
|
+
// Use existing hook config as defaults
|
|
13126
|
+
hookConfig: existingConfig?.extras?.hooks,
|
|
13127
|
+
// Use existing code style config as defaults
|
|
13128
|
+
codeStyleConfig: existingConfig?.extras?.codeStyle,
|
|
13129
|
+
// Use existing folder preferences as defaults
|
|
13130
|
+
folderPreferences: existingConfig?.extras?.folderPreferences,
|
|
13131
|
+
// Use existing permissions config as defaults
|
|
13132
|
+
permissionsConfig: existingConfig?.customizations?.permissions
|
|
11937
13133
|
};
|
|
11938
13134
|
const wizardResult = await runWizard(
|
|
11939
13135
|
wizardConfig,
|
|
@@ -11954,7 +13150,8 @@ async function buildInteractiveConfig(projectPath, detection, registry, options)
|
|
|
11954
13150
|
codeStyleConfig,
|
|
11955
13151
|
cicdConfig,
|
|
11956
13152
|
folderPreferences,
|
|
11957
|
-
templateConfig: templateConfigResult
|
|
13153
|
+
templateConfig: templateConfigResult,
|
|
13154
|
+
claudeSettings
|
|
11958
13155
|
} = wizardResult.values;
|
|
11959
13156
|
let mcpConfig = { level: "project", servers: [] };
|
|
11960
13157
|
let skippedMcpConfigs = [];
|
|
@@ -12012,10 +13209,11 @@ async function buildInteractiveConfig(projectPath, detection, registry, options)
|
|
|
12012
13209
|
config,
|
|
12013
13210
|
skippedMcpConfigs,
|
|
12014
13211
|
templateConfig: templateConfigResult,
|
|
12015
|
-
cicdConfig
|
|
13212
|
+
cicdConfig,
|
|
13213
|
+
claudeSettings
|
|
12016
13214
|
};
|
|
12017
13215
|
}
|
|
12018
|
-
async function executeInstallation(projectPath, config, registry, templatesPath, options, cicdConfig) {
|
|
13216
|
+
async function executeInstallation(projectPath, config, registry, templatesPath, options, cicdConfig, claudeSettings) {
|
|
12019
13217
|
logger.newline();
|
|
12020
13218
|
logger.title("Installing Configuration");
|
|
12021
13219
|
if (config.scaffold.type === "full-project") {
|
|
@@ -12037,6 +13235,28 @@ async function executeInstallation(projectPath, config, registry, templatesPath,
|
|
|
12037
13235
|
} else if (claudeMdResult.skipped) {
|
|
12038
13236
|
logger.info("CLAUDE.md already exists, skipped");
|
|
12039
13237
|
}
|
|
13238
|
+
if (claudeSettings) {
|
|
13239
|
+
const settingsResult = await generateSettingsWithSpinner(projectPath, {
|
|
13240
|
+
claudeSettings,
|
|
13241
|
+
includeCoAuthor: config.preferences.includeCoAuthor,
|
|
13242
|
+
overwrite: options.force
|
|
13243
|
+
});
|
|
13244
|
+
if (settingsResult.error) {
|
|
13245
|
+
logger.warn(`settings.json generation warning: ${settingsResult.error}`);
|
|
13246
|
+
} else if (settingsResult.skipped) {
|
|
13247
|
+
logger.info("settings.json already exists, skipped");
|
|
13248
|
+
}
|
|
13249
|
+
const settingsLocalResult = await generateSettingsLocalWithSpinner(projectPath, {
|
|
13250
|
+
claudeSettings,
|
|
13251
|
+
includeCoAuthor: config.preferences.includeCoAuthor,
|
|
13252
|
+
overwrite: options.force
|
|
13253
|
+
});
|
|
13254
|
+
if (settingsLocalResult.error) {
|
|
13255
|
+
logger.warn(`settings.local.json generation warning: ${settingsLocalResult.error}`);
|
|
13256
|
+
} else if (settingsLocalResult.skipped) {
|
|
13257
|
+
logger.info("settings.local.json already exists, skipped");
|
|
13258
|
+
}
|
|
13259
|
+
}
|
|
12040
13260
|
const modulesByCategory = {
|
|
12041
13261
|
agents: filterModules(registry, "agents", config.modules.agents.selected),
|
|
12042
13262
|
skills: filterModules(registry, "skills", config.modules.skills.selected),
|
|
@@ -12659,7 +13879,7 @@ async function runStatus(options) {
|
|
|
12659
13879
|
}
|
|
12660
13880
|
const config = await readConfig(projectPath);
|
|
12661
13881
|
if (!config) {
|
|
12662
|
-
logger.warn(".claude directory exists but config.json is missing");
|
|
13882
|
+
logger.warn(".claude directory exists but qazuor-claude-config.json is missing");
|
|
12663
13883
|
logger.info('Run "claude-config init" to initialize properly');
|
|
12664
13884
|
process.exit(0);
|
|
12665
13885
|
}
|
|
@@ -13159,8 +14379,8 @@ function createConfigureCommand() {
|
|
|
13159
14379
|
).option("--preview", "Preview changes without applying").option("--show-defaults", "Show global defaults").option("-v, --verbose", "Detailed output").action(runConfigure);
|
|
13160
14380
|
return cmd;
|
|
13161
14381
|
}
|
|
13162
|
-
async function runConfigure(
|
|
13163
|
-
const projectPath = resolvePath(
|
|
14382
|
+
async function runConfigure(path10, options) {
|
|
14383
|
+
const projectPath = resolvePath(path10 || ".");
|
|
13164
14384
|
const claudePath = joinPath(projectPath, ".claude");
|
|
13165
14385
|
logger.configure({ verbose: options.verbose, silent: false });
|
|
13166
14386
|
logger.title("Template Configuration");
|
|
@@ -13291,16 +14511,1837 @@ async function interactiveMode(claudePath, projectPath, options) {
|
|
|
13291
14511
|
};
|
|
13292
14512
|
existingConfig.customizations.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
|
|
13293
14513
|
await writeConfig(projectPath, existingConfig);
|
|
13294
|
-
logger.success("Configuration saved to .claude/config.json");
|
|
14514
|
+
logger.success("Configuration saved to .claude/qazuor-claude-config.json");
|
|
13295
14515
|
}
|
|
13296
14516
|
await promptSaveGlobalDefaults(templateConfig);
|
|
13297
14517
|
logger.newline();
|
|
13298
14518
|
logger.success("Template configuration complete!");
|
|
13299
14519
|
}
|
|
13300
14520
|
|
|
14521
|
+
// src/cli/commands/standards.ts
|
|
14522
|
+
init_cjs_shims();
|
|
14523
|
+
var import_commander8 = require("commander");
|
|
14524
|
+
|
|
14525
|
+
// src/constants/standards-defaults.ts
|
|
14526
|
+
init_cjs_shims();
|
|
14527
|
+
var DEFAULT_CODE_STANDARDS = {
|
|
14528
|
+
indentStyle: "space",
|
|
14529
|
+
indentSize: 2,
|
|
14530
|
+
maxLineLength: 100,
|
|
14531
|
+
maxFileLines: 500,
|
|
14532
|
+
quoteStyle: "single",
|
|
14533
|
+
semicolons: true,
|
|
14534
|
+
trailingCommas: "es5",
|
|
14535
|
+
allowAny: false,
|
|
14536
|
+
namedExportsOnly: true,
|
|
14537
|
+
roroPattern: true,
|
|
14538
|
+
jsDocRequired: true
|
|
14539
|
+
};
|
|
14540
|
+
var DEFAULT_TESTING_STANDARDS = {
|
|
14541
|
+
coverageTarget: 80,
|
|
14542
|
+
tddRequired: true,
|
|
14543
|
+
testPattern: "aaa",
|
|
14544
|
+
testLocation: "separate",
|
|
14545
|
+
unitTestMaxMs: 100,
|
|
14546
|
+
integrationTestMaxMs: 1e3
|
|
14547
|
+
};
|
|
14548
|
+
var DEFAULT_DOCUMENTATION_STANDARDS = {
|
|
14549
|
+
jsDocLevel: "standard",
|
|
14550
|
+
requireExamples: false,
|
|
14551
|
+
changelogFormat: "conventional",
|
|
14552
|
+
inlineCommentPolicy: "why-not-what"
|
|
14553
|
+
};
|
|
14554
|
+
var DEFAULT_DESIGN_STANDARDS = {
|
|
14555
|
+
cssFramework: "tailwind",
|
|
14556
|
+
componentLibrary: "shadcn",
|
|
14557
|
+
accessibilityLevel: "AA",
|
|
14558
|
+
darkModeSupport: true
|
|
14559
|
+
};
|
|
14560
|
+
var DEFAULT_SECURITY_STANDARDS = {
|
|
14561
|
+
authPattern: "jwt",
|
|
14562
|
+
inputValidation: "zod",
|
|
14563
|
+
csrfProtection: true,
|
|
14564
|
+
rateLimiting: true
|
|
14565
|
+
};
|
|
14566
|
+
var DEFAULT_PERFORMANCE_STANDARDS = {
|
|
14567
|
+
lcpTarget: 2500,
|
|
14568
|
+
fidTarget: 100,
|
|
14569
|
+
clsTarget: 0.1,
|
|
14570
|
+
bundleSizeTargetKb: 250,
|
|
14571
|
+
apiResponseTargetMs: 300
|
|
14572
|
+
};
|
|
14573
|
+
var DEFAULT_STANDARDS_CONFIG = {
|
|
14574
|
+
code: DEFAULT_CODE_STANDARDS,
|
|
14575
|
+
testing: DEFAULT_TESTING_STANDARDS,
|
|
14576
|
+
documentation: DEFAULT_DOCUMENTATION_STANDARDS,
|
|
14577
|
+
design: DEFAULT_DESIGN_STANDARDS,
|
|
14578
|
+
security: DEFAULT_SECURITY_STANDARDS,
|
|
14579
|
+
performance: DEFAULT_PERFORMANCE_STANDARDS
|
|
14580
|
+
};
|
|
14581
|
+
var STANDARDS_PRESETS = {
|
|
14582
|
+
strict: {
|
|
14583
|
+
name: "Strict",
|
|
14584
|
+
description: "High quality standards with strict enforcement (90%+ coverage, TDD required)",
|
|
14585
|
+
config: {
|
|
14586
|
+
code: {
|
|
14587
|
+
...DEFAULT_CODE_STANDARDS,
|
|
14588
|
+
allowAny: false,
|
|
14589
|
+
jsDocRequired: true
|
|
14590
|
+
},
|
|
14591
|
+
testing: {
|
|
14592
|
+
...DEFAULT_TESTING_STANDARDS,
|
|
14593
|
+
coverageTarget: 90,
|
|
14594
|
+
tddRequired: true
|
|
14595
|
+
},
|
|
14596
|
+
documentation: {
|
|
14597
|
+
...DEFAULT_DOCUMENTATION_STANDARDS,
|
|
14598
|
+
jsDocLevel: "comprehensive",
|
|
14599
|
+
requireExamples: true
|
|
14600
|
+
},
|
|
14601
|
+
design: {
|
|
14602
|
+
...DEFAULT_DESIGN_STANDARDS,
|
|
14603
|
+
accessibilityLevel: "AAA"
|
|
14604
|
+
},
|
|
14605
|
+
security: {
|
|
14606
|
+
...DEFAULT_SECURITY_STANDARDS,
|
|
14607
|
+
csrfProtection: true,
|
|
14608
|
+
rateLimiting: true
|
|
14609
|
+
},
|
|
14610
|
+
performance: {
|
|
14611
|
+
...DEFAULT_PERFORMANCE_STANDARDS,
|
|
14612
|
+
lcpTarget: 2e3,
|
|
14613
|
+
apiResponseTargetMs: 200
|
|
14614
|
+
}
|
|
14615
|
+
}
|
|
14616
|
+
},
|
|
14617
|
+
balanced: {
|
|
14618
|
+
name: "Balanced",
|
|
14619
|
+
description: "Good balance between quality and pragmatism (80% coverage)",
|
|
14620
|
+
config: DEFAULT_STANDARDS_CONFIG
|
|
14621
|
+
},
|
|
14622
|
+
relaxed: {
|
|
14623
|
+
name: "Relaxed",
|
|
14624
|
+
description: "More flexible standards for rapid development (70% coverage)",
|
|
14625
|
+
config: {
|
|
14626
|
+
code: {
|
|
14627
|
+
...DEFAULT_CODE_STANDARDS,
|
|
14628
|
+
maxFileLines: 800,
|
|
14629
|
+
jsDocRequired: false
|
|
14630
|
+
},
|
|
14631
|
+
testing: {
|
|
14632
|
+
...DEFAULT_TESTING_STANDARDS,
|
|
14633
|
+
coverageTarget: 70,
|
|
14634
|
+
tddRequired: false
|
|
14635
|
+
},
|
|
14636
|
+
documentation: {
|
|
14637
|
+
...DEFAULT_DOCUMENTATION_STANDARDS,
|
|
14638
|
+
jsDocLevel: "minimal",
|
|
14639
|
+
requireExamples: false
|
|
14640
|
+
},
|
|
14641
|
+
design: {
|
|
14642
|
+
...DEFAULT_DESIGN_STANDARDS,
|
|
14643
|
+
accessibilityLevel: "A"
|
|
14644
|
+
},
|
|
14645
|
+
security: {
|
|
14646
|
+
...DEFAULT_SECURITY_STANDARDS
|
|
14647
|
+
},
|
|
14648
|
+
performance: {
|
|
14649
|
+
...DEFAULT_PERFORMANCE_STANDARDS,
|
|
14650
|
+
lcpTarget: 4e3,
|
|
14651
|
+
apiResponseTargetMs: 500
|
|
14652
|
+
}
|
|
14653
|
+
}
|
|
14654
|
+
},
|
|
14655
|
+
startup: {
|
|
14656
|
+
name: "Startup",
|
|
14657
|
+
description: "Fast iteration with minimum viable standards (60% coverage)",
|
|
14658
|
+
config: {
|
|
14659
|
+
code: {
|
|
14660
|
+
...DEFAULT_CODE_STANDARDS,
|
|
14661
|
+
maxFileLines: 1e3,
|
|
14662
|
+
jsDocRequired: false,
|
|
14663
|
+
roroPattern: false
|
|
14664
|
+
},
|
|
14665
|
+
testing: {
|
|
14666
|
+
coverageTarget: 60,
|
|
14667
|
+
tddRequired: false,
|
|
14668
|
+
testPattern: "aaa",
|
|
14669
|
+
testLocation: "colocated",
|
|
14670
|
+
unitTestMaxMs: 200,
|
|
14671
|
+
integrationTestMaxMs: 2e3
|
|
14672
|
+
},
|
|
14673
|
+
documentation: {
|
|
14674
|
+
jsDocLevel: "minimal",
|
|
14675
|
+
requireExamples: false,
|
|
14676
|
+
changelogFormat: "conventional",
|
|
14677
|
+
inlineCommentPolicy: "minimal"
|
|
14678
|
+
},
|
|
14679
|
+
design: {
|
|
14680
|
+
cssFramework: "tailwind",
|
|
14681
|
+
componentLibrary: "shadcn",
|
|
14682
|
+
accessibilityLevel: "A",
|
|
14683
|
+
darkModeSupport: false
|
|
14684
|
+
},
|
|
14685
|
+
security: {
|
|
14686
|
+
authPattern: "jwt",
|
|
14687
|
+
inputValidation: "zod",
|
|
14688
|
+
csrfProtection: false,
|
|
14689
|
+
rateLimiting: false
|
|
14690
|
+
},
|
|
14691
|
+
performance: {
|
|
14692
|
+
lcpTarget: 4e3,
|
|
14693
|
+
fidTarget: 300,
|
|
14694
|
+
clsTarget: 0.25,
|
|
14695
|
+
bundleSizeTargetKb: 500,
|
|
14696
|
+
apiResponseTargetMs: 500
|
|
14697
|
+
}
|
|
14698
|
+
}
|
|
14699
|
+
},
|
|
14700
|
+
enterprise: {
|
|
14701
|
+
name: "Enterprise",
|
|
14702
|
+
description: "Enterprise-grade standards with full compliance (95%+ coverage)",
|
|
14703
|
+
config: {
|
|
14704
|
+
code: {
|
|
14705
|
+
indentStyle: "space",
|
|
14706
|
+
indentSize: 2,
|
|
14707
|
+
maxLineLength: 120,
|
|
14708
|
+
maxFileLines: 400,
|
|
14709
|
+
quoteStyle: "single",
|
|
14710
|
+
semicolons: true,
|
|
14711
|
+
trailingCommas: "all",
|
|
14712
|
+
allowAny: false,
|
|
14713
|
+
namedExportsOnly: true,
|
|
14714
|
+
roroPattern: true,
|
|
14715
|
+
jsDocRequired: true
|
|
14716
|
+
},
|
|
14717
|
+
testing: {
|
|
14718
|
+
coverageTarget: 95,
|
|
14719
|
+
tddRequired: true,
|
|
14720
|
+
testPattern: "aaa",
|
|
14721
|
+
testLocation: "separate",
|
|
14722
|
+
unitTestMaxMs: 50,
|
|
14723
|
+
integrationTestMaxMs: 500
|
|
14724
|
+
},
|
|
14725
|
+
documentation: {
|
|
14726
|
+
jsDocLevel: "comprehensive",
|
|
14727
|
+
requireExamples: true,
|
|
14728
|
+
changelogFormat: "keepachangelog",
|
|
14729
|
+
inlineCommentPolicy: "why-not-what"
|
|
14730
|
+
},
|
|
14731
|
+
design: {
|
|
14732
|
+
cssFramework: "tailwind",
|
|
14733
|
+
componentLibrary: "radix",
|
|
14734
|
+
accessibilityLevel: "AAA",
|
|
14735
|
+
darkModeSupport: true
|
|
14736
|
+
},
|
|
14737
|
+
security: {
|
|
14738
|
+
authPattern: "oauth",
|
|
14739
|
+
inputValidation: "zod",
|
|
14740
|
+
csrfProtection: true,
|
|
14741
|
+
rateLimiting: true
|
|
14742
|
+
},
|
|
14743
|
+
performance: {
|
|
14744
|
+
lcpTarget: 1500,
|
|
14745
|
+
fidTarget: 50,
|
|
14746
|
+
clsTarget: 0.05,
|
|
14747
|
+
bundleSizeTargetKb: 150,
|
|
14748
|
+
apiResponseTargetMs: 150
|
|
14749
|
+
}
|
|
14750
|
+
}
|
|
14751
|
+
},
|
|
14752
|
+
custom: {
|
|
14753
|
+
name: "Custom",
|
|
14754
|
+
description: "Configure each standard manually",
|
|
14755
|
+
config: DEFAULT_STANDARDS_CONFIG
|
|
14756
|
+
}
|
|
14757
|
+
};
|
|
14758
|
+
|
|
14759
|
+
// src/lib/standards/index.ts
|
|
14760
|
+
init_cjs_shims();
|
|
14761
|
+
|
|
14762
|
+
// src/lib/standards/definitions.ts
|
|
14763
|
+
init_cjs_shims();
|
|
14764
|
+
var CODE_STANDARDS_DEFINITION = {
|
|
14765
|
+
id: "code",
|
|
14766
|
+
name: "Code Standards",
|
|
14767
|
+
description: "Code style, formatting, and TypeScript conventions",
|
|
14768
|
+
icon: "\u{1F4DD}",
|
|
14769
|
+
options: [
|
|
14770
|
+
{
|
|
14771
|
+
id: "indentStyle",
|
|
14772
|
+
label: "Indent Style",
|
|
14773
|
+
description: "Use spaces or tabs for indentation",
|
|
14774
|
+
type: "select",
|
|
14775
|
+
choices: [
|
|
14776
|
+
{ name: "Spaces", value: "space", description: "Standard for most projects" },
|
|
14777
|
+
{ name: "Tabs", value: "tab", description: "Better for accessibility" }
|
|
14778
|
+
],
|
|
14779
|
+
affectsPlaceholders: ["{{INDENT_STYLE}}"]
|
|
14780
|
+
},
|
|
14781
|
+
{
|
|
14782
|
+
id: "indentSize",
|
|
14783
|
+
label: "Indent Size",
|
|
14784
|
+
description: "Number of spaces/tab width",
|
|
14785
|
+
type: "select",
|
|
14786
|
+
choices: [
|
|
14787
|
+
{ name: "2 spaces", value: "2", description: "Most common in JS/TS" },
|
|
14788
|
+
{ name: "4 spaces", value: "4", description: "More readable for some" }
|
|
14789
|
+
],
|
|
14790
|
+
affectsPlaceholders: ["{{INDENT_SIZE}}"]
|
|
14791
|
+
},
|
|
14792
|
+
{
|
|
14793
|
+
id: "maxLineLength",
|
|
14794
|
+
label: "Max Line Length",
|
|
14795
|
+
description: "Maximum characters per line",
|
|
14796
|
+
type: "select",
|
|
14797
|
+
choices: [
|
|
14798
|
+
{ name: "80 characters", value: "80", description: "Classic terminal width" },
|
|
14799
|
+
{ name: "100 characters", value: "100", description: "Modern balance" },
|
|
14800
|
+
{ name: "120 characters", value: "120", description: "Wide screens" }
|
|
14801
|
+
],
|
|
14802
|
+
affectsPlaceholders: ["{{MAX_LINE_LENGTH}}"]
|
|
14803
|
+
},
|
|
14804
|
+
{
|
|
14805
|
+
id: "maxFileLines",
|
|
14806
|
+
label: "Max File Lines",
|
|
14807
|
+
description: "Maximum lines per file (excluding tests, docs, JSON)",
|
|
14808
|
+
type: "select",
|
|
14809
|
+
choices: [
|
|
14810
|
+
{ name: "300 lines", value: "300", description: "Very strict" },
|
|
14811
|
+
{ name: "500 lines", value: "500", description: "Recommended" },
|
|
14812
|
+
{ name: "800 lines", value: "800", description: "Relaxed" },
|
|
14813
|
+
{ name: "1000 lines", value: "1000", description: "Very relaxed" }
|
|
14814
|
+
],
|
|
14815
|
+
affectsPlaceholders: ["{{MAX_FILE_LINES}}"]
|
|
14816
|
+
},
|
|
14817
|
+
{
|
|
14818
|
+
id: "quoteStyle",
|
|
14819
|
+
label: "Quote Style",
|
|
14820
|
+
description: "Single or double quotes for strings",
|
|
14821
|
+
type: "select",
|
|
14822
|
+
choices: [
|
|
14823
|
+
{ name: "Single quotes", value: "single", description: "const x = 'hello'" },
|
|
14824
|
+
{ name: "Double quotes", value: "double", description: 'const x = "hello"' }
|
|
14825
|
+
],
|
|
14826
|
+
affectsPlaceholders: ["{{QUOTE_STYLE}}"]
|
|
14827
|
+
},
|
|
14828
|
+
{
|
|
14829
|
+
id: "semicolons",
|
|
14830
|
+
label: "Semicolons",
|
|
14831
|
+
description: "Use semicolons at end of statements",
|
|
14832
|
+
type: "boolean",
|
|
14833
|
+
affectsPlaceholders: ["{{USE_SEMICOLONS}}"]
|
|
14834
|
+
},
|
|
14835
|
+
{
|
|
14836
|
+
id: "trailingCommas",
|
|
14837
|
+
label: "Trailing Commas",
|
|
14838
|
+
description: "Add trailing commas in multiline constructs",
|
|
14839
|
+
type: "select",
|
|
14840
|
+
choices: [
|
|
14841
|
+
{ name: "ES5", value: "es5", description: "Where valid in ES5 (objects, arrays)" },
|
|
14842
|
+
{ name: "All", value: "all", description: "Everywhere possible" },
|
|
14843
|
+
{ name: "None", value: "none", description: "No trailing commas" }
|
|
14844
|
+
],
|
|
14845
|
+
affectsPlaceholders: ["{{TRAILING_COMMAS}}"]
|
|
14846
|
+
},
|
|
14847
|
+
{
|
|
14848
|
+
id: "allowAny",
|
|
14849
|
+
label: 'Allow "any" Type',
|
|
14850
|
+
description: 'Allow the "any" type in TypeScript',
|
|
14851
|
+
type: "boolean",
|
|
14852
|
+
affectsPlaceholders: ["{{ALLOW_ANY}}"]
|
|
14853
|
+
},
|
|
14854
|
+
{
|
|
14855
|
+
id: "namedExportsOnly",
|
|
14856
|
+
label: "Named Exports Only",
|
|
14857
|
+
description: "Require named exports (no default exports)",
|
|
14858
|
+
type: "boolean",
|
|
14859
|
+
affectsPlaceholders: ["{{NAMED_EXPORTS_ONLY}}"]
|
|
14860
|
+
},
|
|
14861
|
+
{
|
|
14862
|
+
id: "roroPattern",
|
|
14863
|
+
label: "RO-RO Pattern",
|
|
14864
|
+
description: "Require Receive Object, Return Object pattern",
|
|
14865
|
+
type: "boolean",
|
|
14866
|
+
affectsPlaceholders: ["{{RORO_PATTERN}}"]
|
|
14867
|
+
},
|
|
14868
|
+
{
|
|
14869
|
+
id: "jsDocRequired",
|
|
14870
|
+
label: "JSDoc Required",
|
|
14871
|
+
description: "Require JSDoc for all exports",
|
|
14872
|
+
type: "boolean",
|
|
14873
|
+
affectsPlaceholders: ["{{JSDOC_REQUIRED}}"]
|
|
14874
|
+
}
|
|
14875
|
+
],
|
|
14876
|
+
targetFiles: ["code-standards.md", "architecture-patterns.md"]
|
|
14877
|
+
};
|
|
14878
|
+
var TESTING_STANDARDS_DEFINITION = {
|
|
14879
|
+
id: "testing",
|
|
14880
|
+
name: "Testing Standards",
|
|
14881
|
+
description: "Test coverage, TDD, and testing methodology",
|
|
14882
|
+
icon: "\u{1F9EA}",
|
|
14883
|
+
options: [
|
|
14884
|
+
{
|
|
14885
|
+
id: "coverageTarget",
|
|
14886
|
+
label: "Coverage Target",
|
|
14887
|
+
description: "Minimum code coverage percentage",
|
|
14888
|
+
type: "select",
|
|
14889
|
+
choices: [
|
|
14890
|
+
{ name: "60%", value: "60", description: "Minimum viable" },
|
|
14891
|
+
{ name: "70%", value: "70", description: "Relaxed" },
|
|
14892
|
+
{ name: "80%", value: "80", description: "Standard" },
|
|
14893
|
+
{ name: "90%", value: "90", description: "Strict" },
|
|
14894
|
+
{ name: "95%", value: "95", description: "Enterprise" }
|
|
14895
|
+
],
|
|
14896
|
+
affectsPlaceholders: ["{{COVERAGE_TARGET}}"]
|
|
14897
|
+
},
|
|
14898
|
+
{
|
|
14899
|
+
id: "tddRequired",
|
|
14900
|
+
label: "TDD Required",
|
|
14901
|
+
description: "Require Test-Driven Development (Red-Green-Refactor)",
|
|
14902
|
+
type: "boolean",
|
|
14903
|
+
affectsPlaceholders: ["{{TDD_REQUIRED}}"]
|
|
14904
|
+
},
|
|
14905
|
+
{
|
|
14906
|
+
id: "testPattern",
|
|
14907
|
+
label: "Test Pattern",
|
|
14908
|
+
description: "Test structure pattern",
|
|
14909
|
+
type: "select",
|
|
14910
|
+
choices: [
|
|
14911
|
+
{ name: "AAA", value: "aaa", description: "Arrange-Act-Assert" },
|
|
14912
|
+
{ name: "GWT", value: "gwt", description: "Given-When-Then" }
|
|
14913
|
+
],
|
|
14914
|
+
affectsPlaceholders: ["{{TEST_PATTERN}}"]
|
|
14915
|
+
},
|
|
14916
|
+
{
|
|
14917
|
+
id: "testLocation",
|
|
14918
|
+
label: "Test Location",
|
|
14919
|
+
description: "Where to place test files",
|
|
14920
|
+
type: "select",
|
|
14921
|
+
choices: [
|
|
14922
|
+
{ name: "Separate", value: "separate", description: "test/ folder at root" },
|
|
14923
|
+
{ name: "Colocated", value: "colocated", description: "__tests__ near source" }
|
|
14924
|
+
],
|
|
14925
|
+
affectsPlaceholders: ["{{TEST_LOCATION}}"]
|
|
14926
|
+
},
|
|
14927
|
+
{
|
|
14928
|
+
id: "unitTestMaxMs",
|
|
14929
|
+
label: "Unit Test Max (ms)",
|
|
14930
|
+
description: "Maximum milliseconds per unit test",
|
|
14931
|
+
type: "select",
|
|
14932
|
+
choices: [
|
|
14933
|
+
{ name: "50ms", value: "50", description: "Very fast" },
|
|
14934
|
+
{ name: "100ms", value: "100", description: "Standard" },
|
|
14935
|
+
{ name: "200ms", value: "200", description: "Relaxed" }
|
|
14936
|
+
],
|
|
14937
|
+
affectsPlaceholders: ["{{UNIT_TEST_MAX_MS}}"]
|
|
14938
|
+
},
|
|
14939
|
+
{
|
|
14940
|
+
id: "integrationTestMaxMs",
|
|
14941
|
+
label: "Integration Test Max (ms)",
|
|
14942
|
+
description: "Maximum milliseconds per integration test",
|
|
14943
|
+
type: "select",
|
|
14944
|
+
choices: [
|
|
14945
|
+
{ name: "500ms", value: "500", description: "Fast" },
|
|
14946
|
+
{ name: "1000ms", value: "1000", description: "Standard" },
|
|
14947
|
+
{ name: "2000ms", value: "2000", description: "Relaxed" }
|
|
14948
|
+
],
|
|
14949
|
+
affectsPlaceholders: ["{{INTEGRATION_TEST_MAX_MS}}"]
|
|
14950
|
+
}
|
|
14951
|
+
],
|
|
14952
|
+
targetFiles: ["testing-standards.md"]
|
|
14953
|
+
};
|
|
14954
|
+
var DOCUMENTATION_STANDARDS_DEFINITION = {
|
|
14955
|
+
id: "documentation",
|
|
14956
|
+
name: "Documentation Standards",
|
|
14957
|
+
description: "JSDoc, comments, and changelog conventions",
|
|
14958
|
+
icon: "\u{1F4DA}",
|
|
14959
|
+
options: [
|
|
14960
|
+
{
|
|
14961
|
+
id: "jsDocLevel",
|
|
14962
|
+
label: "JSDoc Level",
|
|
14963
|
+
description: "Level of detail in JSDoc comments",
|
|
14964
|
+
type: "select",
|
|
14965
|
+
choices: [
|
|
14966
|
+
{ name: "Minimal", value: "minimal", description: "Brief description only" },
|
|
14967
|
+
{ name: "Standard", value: "standard", description: "Description + params + returns" },
|
|
14968
|
+
{ name: "Comprehensive", value: "comprehensive", description: "Full docs with examples" }
|
|
14969
|
+
],
|
|
14970
|
+
affectsPlaceholders: ["{{JSDOC_LEVEL}}"]
|
|
14971
|
+
},
|
|
14972
|
+
{
|
|
14973
|
+
id: "requireExamples",
|
|
14974
|
+
label: "Require Examples",
|
|
14975
|
+
description: "Require @example in JSDoc",
|
|
14976
|
+
type: "boolean",
|
|
14977
|
+
affectsPlaceholders: ["{{REQUIRE_EXAMPLES}}"]
|
|
14978
|
+
},
|
|
14979
|
+
{
|
|
14980
|
+
id: "changelogFormat",
|
|
14981
|
+
label: "Changelog Format",
|
|
14982
|
+
description: "Changelog format to follow",
|
|
14983
|
+
type: "select",
|
|
14984
|
+
choices: [
|
|
14985
|
+
{ name: "Conventional", value: "conventional", description: "Auto-generated from commits" },
|
|
14986
|
+
{ name: "Keep a Changelog", value: "keepachangelog", description: "Manual, semantic" }
|
|
14987
|
+
],
|
|
14988
|
+
affectsPlaceholders: ["{{CHANGELOG_FORMAT}}"]
|
|
14989
|
+
},
|
|
14990
|
+
{
|
|
14991
|
+
id: "inlineCommentPolicy",
|
|
14992
|
+
label: "Inline Comment Policy",
|
|
14993
|
+
description: "Policy for inline code comments",
|
|
14994
|
+
type: "select",
|
|
14995
|
+
choices: [
|
|
14996
|
+
{
|
|
14997
|
+
name: "Why not What",
|
|
14998
|
+
value: "why-not-what",
|
|
14999
|
+
description: "Explain reasoning, not obvious"
|
|
15000
|
+
},
|
|
15001
|
+
{ name: "Minimal", value: "minimal", description: "Only when necessary" },
|
|
15002
|
+
{ name: "Extensive", value: "extensive", description: "Comment thoroughly" }
|
|
15003
|
+
],
|
|
15004
|
+
affectsPlaceholders: ["{{INLINE_COMMENT_POLICY}}"]
|
|
15005
|
+
}
|
|
15006
|
+
],
|
|
15007
|
+
targetFiles: ["documentation-standards.md"]
|
|
15008
|
+
};
|
|
15009
|
+
var DESIGN_STANDARDS_DEFINITION = {
|
|
15010
|
+
id: "design",
|
|
15011
|
+
name: "Design Standards",
|
|
15012
|
+
description: "UI/UX, CSS, and accessibility standards",
|
|
15013
|
+
icon: "\u{1F3A8}",
|
|
15014
|
+
options: [
|
|
15015
|
+
{
|
|
15016
|
+
id: "cssFramework",
|
|
15017
|
+
label: "CSS Framework",
|
|
15018
|
+
description: "CSS/styling approach",
|
|
15019
|
+
type: "select",
|
|
15020
|
+
choices: [
|
|
15021
|
+
{ name: "Tailwind CSS", value: "tailwind", description: "Utility-first CSS" },
|
|
15022
|
+
{ name: "CSS Modules", value: "css-modules", description: "Scoped CSS" },
|
|
15023
|
+
{ name: "Styled Components", value: "styled-components", description: "CSS-in-JS" },
|
|
15024
|
+
{ name: "Vanilla CSS", value: "vanilla", description: "Plain CSS" }
|
|
15025
|
+
],
|
|
15026
|
+
affectsPlaceholders: ["{{CSS_FRAMEWORK}}"]
|
|
15027
|
+
},
|
|
15028
|
+
{
|
|
15029
|
+
id: "componentLibrary",
|
|
15030
|
+
label: "Component Library",
|
|
15031
|
+
description: "UI component library",
|
|
15032
|
+
type: "select",
|
|
15033
|
+
choices: [
|
|
15034
|
+
{ name: "shadcn/ui", value: "shadcn", description: "Copy-paste components" },
|
|
15035
|
+
{ name: "Radix UI", value: "radix", description: "Unstyled primitives" },
|
|
15036
|
+
{ name: "Headless UI", value: "headless", description: "Unstyled, accessible" },
|
|
15037
|
+
{ name: "None", value: "none", description: "Build from scratch" }
|
|
15038
|
+
],
|
|
15039
|
+
affectsPlaceholders: ["{{COMPONENT_LIBRARY}}"]
|
|
15040
|
+
},
|
|
15041
|
+
{
|
|
15042
|
+
id: "accessibilityLevel",
|
|
15043
|
+
label: "Accessibility Level",
|
|
15044
|
+
description: "WCAG accessibility compliance level",
|
|
15045
|
+
type: "select",
|
|
15046
|
+
choices: [
|
|
15047
|
+
{ name: "Level A", value: "A", description: "Minimum" },
|
|
15048
|
+
{ name: "Level AA", value: "AA", description: "Standard (recommended)" },
|
|
15049
|
+
{ name: "Level AAA", value: "AAA", description: "Highest" }
|
|
15050
|
+
],
|
|
15051
|
+
affectsPlaceholders: ["{{WCAG_LEVEL}}", "{{ACCESSIBILITY_LEVEL}}"]
|
|
15052
|
+
},
|
|
15053
|
+
{
|
|
15054
|
+
id: "darkModeSupport",
|
|
15055
|
+
label: "Dark Mode Support",
|
|
15056
|
+
description: "Support dark mode theme",
|
|
15057
|
+
type: "boolean",
|
|
15058
|
+
affectsPlaceholders: ["{{DARK_MODE_SUPPORT}}"]
|
|
15059
|
+
}
|
|
15060
|
+
],
|
|
15061
|
+
targetFiles: ["design-standards.md"]
|
|
15062
|
+
};
|
|
15063
|
+
var SECURITY_STANDARDS_DEFINITION = {
|
|
15064
|
+
id: "security",
|
|
15065
|
+
name: "Security Standards",
|
|
15066
|
+
description: "Authentication, validation, and security practices",
|
|
15067
|
+
icon: "\u{1F512}",
|
|
15068
|
+
options: [
|
|
15069
|
+
{
|
|
15070
|
+
id: "authPattern",
|
|
15071
|
+
label: "Auth Pattern",
|
|
15072
|
+
description: "Authentication approach",
|
|
15073
|
+
type: "select",
|
|
15074
|
+
choices: [
|
|
15075
|
+
{ name: "JWT", value: "jwt", description: "JSON Web Tokens" },
|
|
15076
|
+
{ name: "Session", value: "session", description: "Server-side sessions" },
|
|
15077
|
+
{ name: "OAuth", value: "oauth", description: "OAuth 2.0 / OIDC" },
|
|
15078
|
+
{ name: "None", value: "none", description: "No authentication" }
|
|
15079
|
+
],
|
|
15080
|
+
affectsPlaceholders: ["{{AUTH_PATTERN}}"]
|
|
15081
|
+
},
|
|
15082
|
+
{
|
|
15083
|
+
id: "inputValidation",
|
|
15084
|
+
label: "Input Validation",
|
|
15085
|
+
description: "Validation library",
|
|
15086
|
+
type: "select",
|
|
15087
|
+
choices: [
|
|
15088
|
+
{ name: "Zod", value: "zod", description: "TypeScript-first validation" },
|
|
15089
|
+
{ name: "Yup", value: "yup", description: "Schema builder" },
|
|
15090
|
+
{ name: "Joi", value: "joi", description: "Data validation" },
|
|
15091
|
+
{ name: "Manual", value: "manual", description: "Custom validation" }
|
|
15092
|
+
],
|
|
15093
|
+
affectsPlaceholders: ["{{VALIDATION_LIBRARY}}", "{{INPUT_VALIDATION}}"]
|
|
15094
|
+
},
|
|
15095
|
+
{
|
|
15096
|
+
id: "csrfProtection",
|
|
15097
|
+
label: "CSRF Protection",
|
|
15098
|
+
description: "Enable Cross-Site Request Forgery protection",
|
|
15099
|
+
type: "boolean",
|
|
15100
|
+
affectsPlaceholders: ["{{CSRF_PROTECTION}}"]
|
|
15101
|
+
},
|
|
15102
|
+
{
|
|
15103
|
+
id: "rateLimiting",
|
|
15104
|
+
label: "Rate Limiting",
|
|
15105
|
+
description: "Enable API rate limiting",
|
|
15106
|
+
type: "boolean",
|
|
15107
|
+
affectsPlaceholders: ["{{RATE_LIMITING}}"]
|
|
15108
|
+
}
|
|
15109
|
+
],
|
|
15110
|
+
targetFiles: ["security-standards.md"]
|
|
15111
|
+
};
|
|
15112
|
+
var PERFORMANCE_STANDARDS_DEFINITION = {
|
|
15113
|
+
id: "performance",
|
|
15114
|
+
name: "Performance Standards",
|
|
15115
|
+
description: "Core Web Vitals and performance targets",
|
|
15116
|
+
icon: "\u26A1",
|
|
15117
|
+
options: [
|
|
15118
|
+
{
|
|
15119
|
+
id: "lcpTarget",
|
|
15120
|
+
label: "LCP Target (ms)",
|
|
15121
|
+
description: "Largest Contentful Paint target",
|
|
15122
|
+
type: "select",
|
|
15123
|
+
choices: [
|
|
15124
|
+
{ name: "1500ms", value: "1500", description: "Excellent" },
|
|
15125
|
+
{ name: "2000ms", value: "2000", description: "Good" },
|
|
15126
|
+
{ name: "2500ms", value: "2500", description: "Standard" },
|
|
15127
|
+
{ name: "4000ms", value: "4000", description: "Needs improvement" }
|
|
15128
|
+
],
|
|
15129
|
+
affectsPlaceholders: ["{{LCP_TARGET}}"]
|
|
15130
|
+
},
|
|
15131
|
+
{
|
|
15132
|
+
id: "fidTarget",
|
|
15133
|
+
label: "FID Target (ms)",
|
|
15134
|
+
description: "First Input Delay target",
|
|
15135
|
+
type: "select",
|
|
15136
|
+
choices: [
|
|
15137
|
+
{ name: "50ms", value: "50", description: "Excellent" },
|
|
15138
|
+
{ name: "100ms", value: "100", description: "Good" },
|
|
15139
|
+
{ name: "200ms", value: "200", description: "Standard" },
|
|
15140
|
+
{ name: "300ms", value: "300", description: "Needs improvement" }
|
|
15141
|
+
],
|
|
15142
|
+
affectsPlaceholders: ["{{FID_TARGET}}"]
|
|
15143
|
+
},
|
|
15144
|
+
{
|
|
15145
|
+
id: "clsTarget",
|
|
15146
|
+
label: "CLS Target",
|
|
15147
|
+
description: "Cumulative Layout Shift target",
|
|
15148
|
+
type: "select",
|
|
15149
|
+
choices: [
|
|
15150
|
+
{ name: "0.05", value: "0.05", description: "Excellent" },
|
|
15151
|
+
{ name: "0.1", value: "0.1", description: "Good" },
|
|
15152
|
+
{ name: "0.15", value: "0.15", description: "Standard" },
|
|
15153
|
+
{ name: "0.25", value: "0.25", description: "Needs improvement" }
|
|
15154
|
+
],
|
|
15155
|
+
affectsPlaceholders: ["{{CLS_TARGET}}"]
|
|
15156
|
+
},
|
|
15157
|
+
{
|
|
15158
|
+
id: "bundleSizeTargetKb",
|
|
15159
|
+
label: "Bundle Size (KB)",
|
|
15160
|
+
description: "Maximum initial bundle size",
|
|
15161
|
+
type: "select",
|
|
15162
|
+
choices: [
|
|
15163
|
+
{ name: "100KB", value: "100", description: "Very strict" },
|
|
15164
|
+
{ name: "150KB", value: "150", description: "Strict" },
|
|
15165
|
+
{ name: "250KB", value: "250", description: "Standard" },
|
|
15166
|
+
{ name: "500KB", value: "500", description: "Relaxed" }
|
|
15167
|
+
],
|
|
15168
|
+
affectsPlaceholders: ["{{BUNDLE_SIZE_TARGET}}"]
|
|
15169
|
+
},
|
|
15170
|
+
{
|
|
15171
|
+
id: "apiResponseTargetMs",
|
|
15172
|
+
label: "API Response (ms)",
|
|
15173
|
+
description: "Maximum API response time",
|
|
15174
|
+
type: "select",
|
|
15175
|
+
choices: [
|
|
15176
|
+
{ name: "100ms", value: "100", description: "Very fast" },
|
|
15177
|
+
{ name: "200ms", value: "200", description: "Fast" },
|
|
15178
|
+
{ name: "300ms", value: "300", description: "Standard" },
|
|
15179
|
+
{ name: "500ms", value: "500", description: "Relaxed" }
|
|
15180
|
+
],
|
|
15181
|
+
affectsPlaceholders: ["{{API_RESPONSE_TARGET}}"]
|
|
15182
|
+
}
|
|
15183
|
+
],
|
|
15184
|
+
targetFiles: ["performance-standards.md"]
|
|
15185
|
+
};
|
|
15186
|
+
var STANDARDS_DEFINITIONS = {
|
|
15187
|
+
code: CODE_STANDARDS_DEFINITION,
|
|
15188
|
+
testing: TESTING_STANDARDS_DEFINITION,
|
|
15189
|
+
documentation: DOCUMENTATION_STANDARDS_DEFINITION,
|
|
15190
|
+
design: DESIGN_STANDARDS_DEFINITION,
|
|
15191
|
+
security: SECURITY_STANDARDS_DEFINITION,
|
|
15192
|
+
performance: PERFORMANCE_STANDARDS_DEFINITION
|
|
15193
|
+
};
|
|
15194
|
+
|
|
15195
|
+
// src/lib/standards/replacer.ts
|
|
15196
|
+
init_cjs_shims();
|
|
15197
|
+
var fs6 = __toESM(require("fs/promises"), 1);
|
|
15198
|
+
var path7 = __toESM(require("path"), 1);
|
|
15199
|
+
var import_ora3 = __toESM(require("ora"), 1);
|
|
15200
|
+
var PROCESSABLE_EXTENSIONS2 = [".md", ".json", ".yaml", ".yml", ".txt"];
|
|
15201
|
+
var SKIP_DIRECTORIES3 = ["node_modules", ".git", "dist", "build", ".next", ".turbo"];
|
|
15202
|
+
function flattenStandardsConfig(config) {
|
|
15203
|
+
const flattened = {};
|
|
15204
|
+
if (config.code) {
|
|
15205
|
+
flattened["{{INDENT_STYLE}}"] = config.code.indentStyle;
|
|
15206
|
+
flattened["{{INDENT_SIZE}}"] = String(config.code.indentSize);
|
|
15207
|
+
flattened["{{MAX_LINE_LENGTH}}"] = String(config.code.maxLineLength);
|
|
15208
|
+
flattened["{{MAX_FILE_LINES}}"] = String(config.code.maxFileLines);
|
|
15209
|
+
flattened["{{QUOTE_STYLE}}"] = config.code.quoteStyle;
|
|
15210
|
+
flattened["{{USE_SEMICOLONS}}"] = config.code.semicolons ? "yes" : "no";
|
|
15211
|
+
flattened["{{TRAILING_COMMAS}}"] = config.code.trailingCommas;
|
|
15212
|
+
flattened["{{ALLOW_ANY}}"] = config.code.allowAny ? "yes" : "no";
|
|
15213
|
+
flattened["{{NAMED_EXPORTS_ONLY}}"] = config.code.namedExportsOnly ? "yes" : "no";
|
|
15214
|
+
flattened["{{RORO_PATTERN}}"] = config.code.roroPattern ? "yes" : "no";
|
|
15215
|
+
flattened["{{JSDOC_REQUIRED}}"] = config.code.jsDocRequired ? "yes" : "no";
|
|
15216
|
+
}
|
|
15217
|
+
if (config.testing) {
|
|
15218
|
+
flattened["{{COVERAGE_TARGET}}"] = String(config.testing.coverageTarget);
|
|
15219
|
+
flattened["{{TDD_REQUIRED}}"] = config.testing.tddRequired ? "yes" : "no";
|
|
15220
|
+
flattened["{{TEST_PATTERN}}"] = config.testing.testPattern.toUpperCase();
|
|
15221
|
+
flattened["{{TEST_LOCATION}}"] = config.testing.testLocation;
|
|
15222
|
+
flattened["{{UNIT_TEST_MAX_MS}}"] = String(config.testing.unitTestMaxMs);
|
|
15223
|
+
flattened["{{INTEGRATION_TEST_MAX_MS}}"] = String(config.testing.integrationTestMaxMs);
|
|
15224
|
+
}
|
|
15225
|
+
if (config.documentation) {
|
|
15226
|
+
flattened["{{JSDOC_LEVEL}}"] = config.documentation.jsDocLevel;
|
|
15227
|
+
flattened["{{REQUIRE_EXAMPLES}}"] = config.documentation.requireExamples ? "yes" : "no";
|
|
15228
|
+
flattened["{{CHANGELOG_FORMAT}}"] = config.documentation.changelogFormat;
|
|
15229
|
+
flattened["{{INLINE_COMMENT_POLICY}}"] = config.documentation.inlineCommentPolicy;
|
|
15230
|
+
}
|
|
15231
|
+
if (config.design) {
|
|
15232
|
+
flattened["{{CSS_FRAMEWORK}}"] = config.design.cssFramework;
|
|
15233
|
+
flattened["{{COMPONENT_LIBRARY}}"] = config.design.componentLibrary;
|
|
15234
|
+
flattened["{{WCAG_LEVEL}}"] = config.design.accessibilityLevel;
|
|
15235
|
+
flattened["{{ACCESSIBILITY_LEVEL}}"] = config.design.accessibilityLevel;
|
|
15236
|
+
flattened["{{DARK_MODE_SUPPORT}}"] = config.design.darkModeSupport ? "yes" : "no";
|
|
15237
|
+
}
|
|
15238
|
+
if (config.security) {
|
|
15239
|
+
flattened["{{AUTH_PATTERN}}"] = config.security.authPattern;
|
|
15240
|
+
flattened["{{VALIDATION_LIBRARY}}"] = config.security.inputValidation;
|
|
15241
|
+
flattened["{{INPUT_VALIDATION}}"] = config.security.inputValidation;
|
|
15242
|
+
flattened["{{CSRF_PROTECTION}}"] = config.security.csrfProtection ? "yes" : "no";
|
|
15243
|
+
flattened["{{RATE_LIMITING}}"] = config.security.rateLimiting ? "yes" : "no";
|
|
15244
|
+
}
|
|
15245
|
+
if (config.performance) {
|
|
15246
|
+
flattened["{{LCP_TARGET}}"] = String(config.performance.lcpTarget);
|
|
15247
|
+
flattened["{{FID_TARGET}}"] = String(config.performance.fidTarget);
|
|
15248
|
+
flattened["{{CLS_TARGET}}"] = String(config.performance.clsTarget);
|
|
15249
|
+
flattened["{{BUNDLE_SIZE_TARGET}}"] = String(config.performance.bundleSizeTargetKb);
|
|
15250
|
+
flattened["{{API_RESPONSE_TARGET}}"] = String(config.performance.apiResponseTargetMs);
|
|
15251
|
+
}
|
|
15252
|
+
return flattened;
|
|
15253
|
+
}
|
|
15254
|
+
function shouldProcessFile2(filePath) {
|
|
15255
|
+
const ext = path7.extname(filePath).toLowerCase();
|
|
15256
|
+
return PROCESSABLE_EXTENSIONS2.includes(ext);
|
|
15257
|
+
}
|
|
15258
|
+
function shouldSkipDirectory3(dirName) {
|
|
15259
|
+
return SKIP_DIRECTORIES3.includes(dirName) || dirName.startsWith(".");
|
|
15260
|
+
}
|
|
15261
|
+
async function getAllFiles3(dir) {
|
|
15262
|
+
const files = [];
|
|
15263
|
+
try {
|
|
15264
|
+
const entries = await fs6.readdir(dir, { withFileTypes: true });
|
|
15265
|
+
for (const entry of entries) {
|
|
15266
|
+
const fullPath = path7.join(dir, entry.name);
|
|
15267
|
+
if (entry.isDirectory()) {
|
|
15268
|
+
if (!shouldSkipDirectory3(entry.name)) {
|
|
15269
|
+
const subFiles = await getAllFiles3(fullPath);
|
|
15270
|
+
files.push(...subFiles);
|
|
15271
|
+
}
|
|
15272
|
+
} else if (entry.isFile() && shouldProcessFile2(entry.name)) {
|
|
15273
|
+
files.push(fullPath);
|
|
15274
|
+
}
|
|
15275
|
+
}
|
|
15276
|
+
} catch {
|
|
15277
|
+
}
|
|
15278
|
+
return files;
|
|
15279
|
+
}
|
|
15280
|
+
async function replaceInFile3(filePath, replacements) {
|
|
15281
|
+
const changes = [];
|
|
15282
|
+
try {
|
|
15283
|
+
let content = await fs6.readFile(filePath, "utf-8");
|
|
15284
|
+
let modified = false;
|
|
15285
|
+
for (const [placeholder, value] of Object.entries(replacements)) {
|
|
15286
|
+
if (content.includes(placeholder)) {
|
|
15287
|
+
content = content.split(placeholder).join(value);
|
|
15288
|
+
changes.push({ placeholder, value });
|
|
15289
|
+
modified = true;
|
|
15290
|
+
}
|
|
15291
|
+
}
|
|
15292
|
+
if (modified) {
|
|
15293
|
+
await fs6.writeFile(filePath, content, "utf-8");
|
|
15294
|
+
}
|
|
15295
|
+
} catch {
|
|
15296
|
+
}
|
|
15297
|
+
return changes;
|
|
15298
|
+
}
|
|
15299
|
+
async function replaceStandardsPlaceholders(claudePath, config) {
|
|
15300
|
+
const replacements = flattenStandardsConfig(config);
|
|
15301
|
+
const standardsDir = path7.join(claudePath, "docs", "standards");
|
|
15302
|
+
const files = await getAllFiles3(standardsDir);
|
|
15303
|
+
const report = {
|
|
15304
|
+
modifiedFiles: [],
|
|
15305
|
+
replacedPlaceholders: [],
|
|
15306
|
+
unusedPlaceholders: [],
|
|
15307
|
+
errors: []
|
|
15308
|
+
};
|
|
15309
|
+
const usedPlaceholders = /* @__PURE__ */ new Set();
|
|
15310
|
+
for (const file of files) {
|
|
15311
|
+
try {
|
|
15312
|
+
const changes = await replaceInFile3(file, replacements);
|
|
15313
|
+
if (changes.length > 0) {
|
|
15314
|
+
report.modifiedFiles.push(path7.relative(claudePath, file));
|
|
15315
|
+
for (const change of changes) {
|
|
15316
|
+
if (!report.replacedPlaceholders.includes(change.placeholder)) {
|
|
15317
|
+
report.replacedPlaceholders.push(change.placeholder);
|
|
15318
|
+
}
|
|
15319
|
+
usedPlaceholders.add(change.placeholder);
|
|
15320
|
+
}
|
|
15321
|
+
}
|
|
15322
|
+
} catch (error) {
|
|
15323
|
+
report.errors.push(`Error processing ${file}: ${String(error)}`);
|
|
15324
|
+
}
|
|
15325
|
+
}
|
|
15326
|
+
for (const placeholder of Object.keys(replacements)) {
|
|
15327
|
+
if (!usedPlaceholders.has(placeholder)) {
|
|
15328
|
+
report.unusedPlaceholders.push(placeholder);
|
|
15329
|
+
}
|
|
15330
|
+
}
|
|
15331
|
+
return report;
|
|
15332
|
+
}
|
|
15333
|
+
async function replaceStandardsWithSpinner(claudePath, config) {
|
|
15334
|
+
const spinner2 = (0, import_ora3.default)("Applying standards configuration...").start();
|
|
15335
|
+
try {
|
|
15336
|
+
const report = await replaceStandardsPlaceholders(claudePath, config);
|
|
15337
|
+
if (report.modifiedFiles.length > 0) {
|
|
15338
|
+
spinner2.succeed(
|
|
15339
|
+
`Applied ${report.replacedPlaceholders.length} standards to ${report.modifiedFiles.length} files`
|
|
15340
|
+
);
|
|
15341
|
+
} else {
|
|
15342
|
+
spinner2.info("No standards placeholders found to replace");
|
|
15343
|
+
}
|
|
15344
|
+
return report;
|
|
15345
|
+
} catch (error) {
|
|
15346
|
+
spinner2.fail("Failed to apply standards configuration");
|
|
15347
|
+
throw error;
|
|
15348
|
+
}
|
|
15349
|
+
}
|
|
15350
|
+
async function previewStandardsReplacements(claudePath, config) {
|
|
15351
|
+
const replacements = flattenStandardsConfig(config);
|
|
15352
|
+
const standardsDir = path7.join(claudePath, "docs", "standards");
|
|
15353
|
+
const files = await getAllFiles3(standardsDir);
|
|
15354
|
+
const preview = [];
|
|
15355
|
+
for (const file of files) {
|
|
15356
|
+
try {
|
|
15357
|
+
const content = await fs6.readFile(file, "utf-8");
|
|
15358
|
+
for (const [placeholder, value] of Object.entries(replacements)) {
|
|
15359
|
+
if (content.includes(placeholder)) {
|
|
15360
|
+
preview.push({
|
|
15361
|
+
file: path7.relative(claudePath, file),
|
|
15362
|
+
placeholder,
|
|
15363
|
+
value
|
|
15364
|
+
});
|
|
15365
|
+
}
|
|
15366
|
+
}
|
|
15367
|
+
} catch {
|
|
15368
|
+
}
|
|
15369
|
+
}
|
|
15370
|
+
return preview;
|
|
15371
|
+
}
|
|
15372
|
+
function formatStandardsReport(report) {
|
|
15373
|
+
const lines = [];
|
|
15374
|
+
lines.push("Standards Configuration Applied");
|
|
15375
|
+
lines.push("\u2500".repeat(40));
|
|
15376
|
+
lines.push(`Files modified: ${report.modifiedFiles.length}`);
|
|
15377
|
+
lines.push(`Placeholders replaced: ${report.replacedPlaceholders.length}`);
|
|
15378
|
+
if (report.modifiedFiles.length > 0) {
|
|
15379
|
+
lines.push("");
|
|
15380
|
+
lines.push("Modified files:");
|
|
15381
|
+
for (const file of report.modifiedFiles) {
|
|
15382
|
+
lines.push(` \u2713 ${file}`);
|
|
15383
|
+
}
|
|
15384
|
+
}
|
|
15385
|
+
if (report.unusedPlaceholders.length > 0) {
|
|
15386
|
+
lines.push("");
|
|
15387
|
+
lines.push("Unused placeholders (no matching templates):");
|
|
15388
|
+
for (const p of report.unusedPlaceholders.slice(0, 5)) {
|
|
15389
|
+
lines.push(` - ${p}`);
|
|
15390
|
+
}
|
|
15391
|
+
if (report.unusedPlaceholders.length > 5) {
|
|
15392
|
+
lines.push(` ... and ${report.unusedPlaceholders.length - 5} more`);
|
|
15393
|
+
}
|
|
15394
|
+
}
|
|
15395
|
+
if (report.errors.length > 0) {
|
|
15396
|
+
lines.push("");
|
|
15397
|
+
lines.push("Errors:");
|
|
15398
|
+
for (const error of report.errors) {
|
|
15399
|
+
lines.push(` \u2717 ${error}`);
|
|
15400
|
+
}
|
|
15401
|
+
}
|
|
15402
|
+
return lines.join("\n");
|
|
15403
|
+
}
|
|
15404
|
+
|
|
15405
|
+
// src/lib/standards/scanner.ts
|
|
15406
|
+
init_cjs_shims();
|
|
15407
|
+
var fs7 = __toESM(require("fs/promises"), 1);
|
|
15408
|
+
var path8 = __toESM(require("path"), 1);
|
|
15409
|
+
var SCANNABLE_EXTENSIONS2 = [".md", ".json", ".yaml", ".yml", ".txt"];
|
|
15410
|
+
var PLACEHOLDER_PATTERN = /\{\{([A-Z_]+)\}\}/g;
|
|
15411
|
+
async function getScanableFiles(dir) {
|
|
15412
|
+
const files = [];
|
|
15413
|
+
try {
|
|
15414
|
+
const entries = await fs7.readdir(dir, { withFileTypes: true });
|
|
15415
|
+
for (const entry of entries) {
|
|
15416
|
+
const fullPath = path8.join(dir, entry.name);
|
|
15417
|
+
if (entry.isDirectory()) {
|
|
15418
|
+
const subFiles = await getScanableFiles(fullPath);
|
|
15419
|
+
files.push(...subFiles);
|
|
15420
|
+
} else if (entry.isFile()) {
|
|
15421
|
+
const ext = path8.extname(entry.name).toLowerCase();
|
|
15422
|
+
if (SCANNABLE_EXTENSIONS2.includes(ext)) {
|
|
15423
|
+
files.push(fullPath);
|
|
15424
|
+
}
|
|
15425
|
+
}
|
|
15426
|
+
}
|
|
15427
|
+
} catch {
|
|
15428
|
+
}
|
|
15429
|
+
return files;
|
|
15430
|
+
}
|
|
15431
|
+
function extractPlaceholders2(content) {
|
|
15432
|
+
const matches = content.match(PLACEHOLDER_PATTERN);
|
|
15433
|
+
return matches ? [...new Set(matches)] : [];
|
|
15434
|
+
}
|
|
15435
|
+
async function scanStandardsPlaceholders(claudePath, config) {
|
|
15436
|
+
const standardsDir = path8.join(claudePath, "docs", "standards");
|
|
15437
|
+
const files = await getScanableFiles(standardsDir);
|
|
15438
|
+
const placeholdersByFile = /* @__PURE__ */ new Map();
|
|
15439
|
+
const allPlaceholders = /* @__PURE__ */ new Set();
|
|
15440
|
+
for (const file of files) {
|
|
15441
|
+
try {
|
|
15442
|
+
const content = await fs7.readFile(file, "utf-8");
|
|
15443
|
+
const placeholders = extractPlaceholders2(content);
|
|
15444
|
+
if (placeholders.length > 0) {
|
|
15445
|
+
const relPath = path8.relative(claudePath, file);
|
|
15446
|
+
placeholdersByFile.set(relPath, placeholders);
|
|
15447
|
+
for (const p of placeholders) {
|
|
15448
|
+
allPlaceholders.add(p);
|
|
15449
|
+
}
|
|
15450
|
+
}
|
|
15451
|
+
} catch {
|
|
15452
|
+
}
|
|
15453
|
+
}
|
|
15454
|
+
const configuredPlaceholders = config ? new Set(Object.keys(flattenStandardsConfig(config))) : /* @__PURE__ */ new Set();
|
|
15455
|
+
const unconfigured = /* @__PURE__ */ new Map();
|
|
15456
|
+
for (const placeholder of allPlaceholders) {
|
|
15457
|
+
if (!configuredPlaceholders.has(placeholder)) {
|
|
15458
|
+
const filesWithPlaceholder = [];
|
|
15459
|
+
for (const [file, placeholders] of placeholdersByFile) {
|
|
15460
|
+
if (placeholders.includes(placeholder)) {
|
|
15461
|
+
filesWithPlaceholder.push(file);
|
|
15462
|
+
}
|
|
15463
|
+
}
|
|
15464
|
+
unconfigured.set(placeholder, filesWithPlaceholder);
|
|
15465
|
+
}
|
|
15466
|
+
}
|
|
15467
|
+
return {
|
|
15468
|
+
unconfiguredPlaceholders: Array.from(unconfigured.entries()).map(([placeholder, files2]) => ({
|
|
15469
|
+
placeholder,
|
|
15470
|
+
files: files2
|
|
15471
|
+
})),
|
|
15472
|
+
totalPlaceholders: allPlaceholders.size,
|
|
15473
|
+
configuredPlaceholders: configuredPlaceholders.size
|
|
15474
|
+
};
|
|
15475
|
+
}
|
|
15476
|
+
function formatScanResult(result) {
|
|
15477
|
+
const lines = [];
|
|
15478
|
+
lines.push("Standards Placeholder Scan");
|
|
15479
|
+
lines.push("\u2500".repeat(40));
|
|
15480
|
+
lines.push(`Total placeholders found: ${result.totalPlaceholders}`);
|
|
15481
|
+
lines.push(`Already configured: ${result.configuredPlaceholders}`);
|
|
15482
|
+
lines.push(`Unconfigured: ${result.unconfiguredPlaceholders.length}`);
|
|
15483
|
+
if (result.unconfiguredPlaceholders.length > 0) {
|
|
15484
|
+
lines.push("");
|
|
15485
|
+
lines.push("Unconfigured placeholders:");
|
|
15486
|
+
for (const { placeholder, files } of result.unconfiguredPlaceholders) {
|
|
15487
|
+
lines.push(` ${placeholder}`);
|
|
15488
|
+
for (const file of files.slice(0, 3)) {
|
|
15489
|
+
lines.push(` \u2514\u2500 ${file}`);
|
|
15490
|
+
}
|
|
15491
|
+
if (files.length > 3) {
|
|
15492
|
+
lines.push(` \u2514\u2500 ... and ${files.length - 3} more files`);
|
|
15493
|
+
}
|
|
15494
|
+
}
|
|
15495
|
+
} else {
|
|
15496
|
+
lines.push("");
|
|
15497
|
+
lines.push("\u2713 All placeholders are configured!");
|
|
15498
|
+
}
|
|
15499
|
+
return lines.join("\n");
|
|
15500
|
+
}
|
|
15501
|
+
|
|
15502
|
+
// src/lib/standards/template-sync.ts
|
|
15503
|
+
init_cjs_shims();
|
|
15504
|
+
var fs8 = __toESM(require("fs/promises"), 1);
|
|
15505
|
+
var path9 = __toESM(require("path"), 1);
|
|
15506
|
+
var import_ora4 = __toESM(require("ora"), 1);
|
|
15507
|
+
var STANDARDS_TEMPLATES = [
|
|
15508
|
+
"code-standards.md",
|
|
15509
|
+
"testing-standards.md",
|
|
15510
|
+
"documentation-standards.md",
|
|
15511
|
+
"design-standards.md",
|
|
15512
|
+
"security-standards.md",
|
|
15513
|
+
"performance-standards.md"
|
|
15514
|
+
];
|
|
15515
|
+
async function fileExists2(filePath) {
|
|
15516
|
+
try {
|
|
15517
|
+
await fs8.access(filePath);
|
|
15518
|
+
return true;
|
|
15519
|
+
} catch {
|
|
15520
|
+
return false;
|
|
15521
|
+
}
|
|
15522
|
+
}
|
|
15523
|
+
async function createBackup(filePath) {
|
|
15524
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
15525
|
+
const backupPath = `${filePath}.backup-${timestamp}`;
|
|
15526
|
+
await fs8.copyFile(filePath, backupPath);
|
|
15527
|
+
return backupPath;
|
|
15528
|
+
}
|
|
15529
|
+
async function hasPlaceholders(filePath) {
|
|
15530
|
+
try {
|
|
15531
|
+
const content = await fs8.readFile(filePath, "utf-8");
|
|
15532
|
+
return content.includes("AUTO-GENERATED: Configured values") || /\{\{[A-Z_]+\}\}/.test(content);
|
|
15533
|
+
} catch {
|
|
15534
|
+
return false;
|
|
15535
|
+
}
|
|
15536
|
+
}
|
|
15537
|
+
async function syncTemplate(templateName, sourcePath, targetPath, options) {
|
|
15538
|
+
const sourceFile = path9.join(sourcePath, templateName);
|
|
15539
|
+
const targetFile = path9.join(targetPath, templateName);
|
|
15540
|
+
if (!await fileExists2(sourceFile)) {
|
|
15541
|
+
return { status: "skipped" };
|
|
15542
|
+
}
|
|
15543
|
+
const targetExists = await fileExists2(targetFile);
|
|
15544
|
+
if (targetExists) {
|
|
15545
|
+
const alreadyHasPlaceholders = await hasPlaceholders(targetFile);
|
|
15546
|
+
if (alreadyHasPlaceholders && !options.overwrite) {
|
|
15547
|
+
return { status: "skipped" };
|
|
15548
|
+
}
|
|
15549
|
+
let backupPath;
|
|
15550
|
+
if (options.backup) {
|
|
15551
|
+
backupPath = await createBackup(targetFile);
|
|
15552
|
+
}
|
|
15553
|
+
await fs8.copyFile(sourceFile, targetFile);
|
|
15554
|
+
return { status: "updated", backup: backupPath };
|
|
15555
|
+
}
|
|
15556
|
+
await fs8.mkdir(path9.dirname(targetFile), { recursive: true });
|
|
15557
|
+
await fs8.copyFile(sourceFile, targetFile);
|
|
15558
|
+
return { status: "created" };
|
|
15559
|
+
}
|
|
15560
|
+
async function syncStandardsTemplates(claudePath, options = {}) {
|
|
15561
|
+
const result = {
|
|
15562
|
+
created: [],
|
|
15563
|
+
updated: [],
|
|
15564
|
+
skipped: [],
|
|
15565
|
+
errors: []
|
|
15566
|
+
};
|
|
15567
|
+
const packagesTemplatesPath = path9.join(getTemplatesPath(), "docs", "standards");
|
|
15568
|
+
const projectTemplatesPath = path9.join(claudePath, "docs", "standards");
|
|
15569
|
+
await fs8.mkdir(projectTemplatesPath, { recursive: true });
|
|
15570
|
+
for (const template of STANDARDS_TEMPLATES) {
|
|
15571
|
+
try {
|
|
15572
|
+
const syncResult = await syncTemplate(
|
|
15573
|
+
template,
|
|
15574
|
+
packagesTemplatesPath,
|
|
15575
|
+
projectTemplatesPath,
|
|
15576
|
+
options
|
|
15577
|
+
);
|
|
15578
|
+
switch (syncResult.status) {
|
|
15579
|
+
case "created":
|
|
15580
|
+
result.created.push(template);
|
|
15581
|
+
break;
|
|
15582
|
+
case "updated":
|
|
15583
|
+
result.updated.push(template);
|
|
15584
|
+
break;
|
|
15585
|
+
case "skipped":
|
|
15586
|
+
result.skipped.push(template);
|
|
15587
|
+
break;
|
|
15588
|
+
}
|
|
15589
|
+
} catch (error) {
|
|
15590
|
+
result.errors.push(`${template}: ${String(error)}`);
|
|
15591
|
+
}
|
|
15592
|
+
}
|
|
15593
|
+
return result;
|
|
15594
|
+
}
|
|
15595
|
+
async function syncStandardsTemplatesWithSpinner(claudePath, options = {}) {
|
|
15596
|
+
const spinner2 = (0, import_ora4.default)("Syncing standards templates...").start();
|
|
15597
|
+
try {
|
|
15598
|
+
const result = await syncStandardsTemplates(claudePath, options);
|
|
15599
|
+
const total = result.created.length + result.updated.length;
|
|
15600
|
+
if (total > 0) {
|
|
15601
|
+
spinner2.succeed(
|
|
15602
|
+
`Synced ${total} template${total !== 1 ? "s" : ""} (${result.created.length} created, ${result.updated.length} updated)`
|
|
15603
|
+
);
|
|
15604
|
+
} else if (result.skipped.length > 0) {
|
|
15605
|
+
spinner2.info("All templates already up to date");
|
|
15606
|
+
} else {
|
|
15607
|
+
spinner2.warn("No templates to sync");
|
|
15608
|
+
}
|
|
15609
|
+
return result;
|
|
15610
|
+
} catch (error) {
|
|
15611
|
+
spinner2.fail("Failed to sync templates");
|
|
15612
|
+
throw error;
|
|
15613
|
+
}
|
|
15614
|
+
}
|
|
15615
|
+
async function checkTemplatesNeedUpdate(claudePath) {
|
|
15616
|
+
const projectTemplatesPath = path9.join(claudePath, "docs", "standards");
|
|
15617
|
+
const missing = [];
|
|
15618
|
+
const outdated = [];
|
|
15619
|
+
for (const template of STANDARDS_TEMPLATES) {
|
|
15620
|
+
const targetFile = path9.join(projectTemplatesPath, template);
|
|
15621
|
+
if (!await fileExists2(targetFile)) {
|
|
15622
|
+
missing.push(template);
|
|
15623
|
+
} else if (!await hasPlaceholders(targetFile)) {
|
|
15624
|
+
outdated.push(template);
|
|
15625
|
+
}
|
|
15626
|
+
}
|
|
15627
|
+
return {
|
|
15628
|
+
needsUpdate: missing.length > 0 || outdated.length > 0,
|
|
15629
|
+
missing,
|
|
15630
|
+
outdated
|
|
15631
|
+
};
|
|
15632
|
+
}
|
|
15633
|
+
function formatSyncResult(result) {
|
|
15634
|
+
const lines = [];
|
|
15635
|
+
lines.push("Template Sync Results");
|
|
15636
|
+
lines.push("\u2500".repeat(40));
|
|
15637
|
+
if (result.created.length > 0) {
|
|
15638
|
+
lines.push(`Created: ${result.created.length}`);
|
|
15639
|
+
for (const f of result.created) {
|
|
15640
|
+
lines.push(` \u2713 ${f}`);
|
|
15641
|
+
}
|
|
15642
|
+
}
|
|
15643
|
+
if (result.updated.length > 0) {
|
|
15644
|
+
lines.push(`Updated: ${result.updated.length}`);
|
|
15645
|
+
for (const f of result.updated) {
|
|
15646
|
+
lines.push(` \u2713 ${f}`);
|
|
15647
|
+
}
|
|
15648
|
+
}
|
|
15649
|
+
if (result.skipped.length > 0) {
|
|
15650
|
+
lines.push(`Skipped (already up to date): ${result.skipped.length}`);
|
|
15651
|
+
}
|
|
15652
|
+
if (result.errors.length > 0) {
|
|
15653
|
+
lines.push(`Errors: ${result.errors.length}`);
|
|
15654
|
+
for (const e of result.errors) {
|
|
15655
|
+
lines.push(` \u2717 ${e}`);
|
|
15656
|
+
}
|
|
15657
|
+
}
|
|
15658
|
+
return lines.join("\n");
|
|
15659
|
+
}
|
|
15660
|
+
|
|
15661
|
+
// src/cli/commands/standards.ts
|
|
15662
|
+
init_fs();
|
|
15663
|
+
|
|
15664
|
+
// src/cli/prompts/standards.ts
|
|
15665
|
+
init_cjs_shims();
|
|
15666
|
+
async function promptStandardsConfig(options) {
|
|
15667
|
+
logger.section("Project Standards", "\u{1F4D0}");
|
|
15668
|
+
logger.info("Configure quality standards for your project");
|
|
15669
|
+
logger.newline();
|
|
15670
|
+
if (options?.category) {
|
|
15671
|
+
const existingConfig = options.defaults ?? DEFAULT_STANDARDS_CONFIG;
|
|
15672
|
+
const categoryConfig = await promptCategoryConfig(options.category, existingConfig);
|
|
15673
|
+
return {
|
|
15674
|
+
...existingConfig,
|
|
15675
|
+
[options.category]: categoryConfig
|
|
15676
|
+
};
|
|
15677
|
+
}
|
|
15678
|
+
const enableStandards = await confirm({
|
|
15679
|
+
message: "Would you like to configure project standards?",
|
|
15680
|
+
default: true
|
|
15681
|
+
});
|
|
15682
|
+
if (!enableStandards) {
|
|
15683
|
+
return DEFAULT_STANDARDS_CONFIG;
|
|
15684
|
+
}
|
|
15685
|
+
const preset = await promptStandardsPreset();
|
|
15686
|
+
if (preset !== "custom") {
|
|
15687
|
+
const presetConfig = STANDARDS_PRESETS[preset];
|
|
15688
|
+
logger.success(`Using "${presetConfig.name}" preset`);
|
|
15689
|
+
return presetConfig.config;
|
|
15690
|
+
}
|
|
15691
|
+
logger.newline();
|
|
15692
|
+
logger.info("Configure each standards category:");
|
|
15693
|
+
const codeConfig = await promptCodeStandards(options?.defaults?.code);
|
|
15694
|
+
const testingConfig = await promptTestingStandards(options?.defaults?.testing);
|
|
15695
|
+
const documentationConfig = await promptDocumentationStandards(options?.defaults?.documentation);
|
|
15696
|
+
const designConfig = await promptDesignStandards(options?.defaults?.design);
|
|
15697
|
+
const securityConfig = await promptSecurityStandards(options?.defaults?.security);
|
|
15698
|
+
const performanceConfig = await promptPerformanceStandards(options?.defaults?.performance);
|
|
15699
|
+
return {
|
|
15700
|
+
code: codeConfig,
|
|
15701
|
+
testing: testingConfig,
|
|
15702
|
+
documentation: documentationConfig,
|
|
15703
|
+
design: designConfig,
|
|
15704
|
+
security: securityConfig,
|
|
15705
|
+
performance: performanceConfig
|
|
15706
|
+
};
|
|
15707
|
+
}
|
|
15708
|
+
async function promptStandardsPreset() {
|
|
15709
|
+
return select({
|
|
15710
|
+
message: "Choose a standards preset:",
|
|
15711
|
+
choices: Object.entries(STANDARDS_PRESETS).map(([key, preset]) => ({
|
|
15712
|
+
name: `${preset.name} - ${preset.description}`,
|
|
15713
|
+
value: key
|
|
15714
|
+
})),
|
|
15715
|
+
default: "balanced"
|
|
15716
|
+
});
|
|
15717
|
+
}
|
|
15718
|
+
async function promptCategoryConfig(category, existingConfig) {
|
|
15719
|
+
switch (category) {
|
|
15720
|
+
case "code":
|
|
15721
|
+
return promptCodeStandards(existingConfig.code);
|
|
15722
|
+
case "testing":
|
|
15723
|
+
return promptTestingStandards(existingConfig.testing);
|
|
15724
|
+
case "documentation":
|
|
15725
|
+
return promptDocumentationStandards(existingConfig.documentation);
|
|
15726
|
+
case "design":
|
|
15727
|
+
return promptDesignStandards(existingConfig.design);
|
|
15728
|
+
case "security":
|
|
15729
|
+
return promptSecurityStandards(existingConfig.security);
|
|
15730
|
+
case "performance":
|
|
15731
|
+
return promptPerformanceStandards(existingConfig.performance);
|
|
15732
|
+
}
|
|
15733
|
+
}
|
|
15734
|
+
async function promptCodeStandards(defaults) {
|
|
15735
|
+
const def = STANDARDS_DEFINITIONS.code;
|
|
15736
|
+
logger.newline();
|
|
15737
|
+
logger.subtitle(`${def.icon} ${def.name}`);
|
|
15738
|
+
const indentStyle = await select({
|
|
15739
|
+
message: "Indent style:",
|
|
15740
|
+
choices: [
|
|
15741
|
+
{ name: "Spaces", value: "space" },
|
|
15742
|
+
{ name: "Tabs", value: "tab" }
|
|
15743
|
+
],
|
|
15744
|
+
default: defaults?.indentStyle ?? DEFAULT_STANDARDS_CONFIG.code.indentStyle
|
|
15745
|
+
});
|
|
15746
|
+
const indentSize = await select({
|
|
15747
|
+
message: "Indent size:",
|
|
15748
|
+
choices: [
|
|
15749
|
+
{ name: "2 spaces", value: 2 },
|
|
15750
|
+
{ name: "4 spaces", value: 4 }
|
|
15751
|
+
],
|
|
15752
|
+
default: defaults?.indentSize ?? DEFAULT_STANDARDS_CONFIG.code.indentSize
|
|
15753
|
+
});
|
|
15754
|
+
const maxLineLength = await select({
|
|
15755
|
+
message: "Max line length:",
|
|
15756
|
+
choices: [
|
|
15757
|
+
{ name: "80 characters", value: 80 },
|
|
15758
|
+
{ name: "100 characters", value: 100 },
|
|
15759
|
+
{ name: "120 characters", value: 120 }
|
|
15760
|
+
],
|
|
15761
|
+
default: defaults?.maxLineLength ?? DEFAULT_STANDARDS_CONFIG.code.maxLineLength
|
|
15762
|
+
});
|
|
15763
|
+
const maxFileLines = await select({
|
|
15764
|
+
message: "Max file lines:",
|
|
15765
|
+
choices: [
|
|
15766
|
+
{ name: "300 lines (strict)", value: 300 },
|
|
15767
|
+
{ name: "500 lines (standard)", value: 500 },
|
|
15768
|
+
{ name: "800 lines (relaxed)", value: 800 }
|
|
15769
|
+
],
|
|
15770
|
+
default: defaults?.maxFileLines ?? DEFAULT_STANDARDS_CONFIG.code.maxFileLines
|
|
15771
|
+
});
|
|
15772
|
+
const quoteStyle = await select({
|
|
15773
|
+
message: "Quote style:",
|
|
15774
|
+
choices: [
|
|
15775
|
+
{ name: "Single quotes", value: "single" },
|
|
15776
|
+
{ name: "Double quotes", value: "double" }
|
|
15777
|
+
],
|
|
15778
|
+
default: defaults?.quoteStyle ?? DEFAULT_STANDARDS_CONFIG.code.quoteStyle
|
|
15779
|
+
});
|
|
15780
|
+
const semicolons = await confirm({
|
|
15781
|
+
message: "Use semicolons?",
|
|
15782
|
+
default: defaults?.semicolons ?? DEFAULT_STANDARDS_CONFIG.code.semicolons
|
|
15783
|
+
});
|
|
15784
|
+
const trailingCommas = await select({
|
|
15785
|
+
message: "Trailing commas:",
|
|
15786
|
+
choices: [
|
|
15787
|
+
{ name: "ES5 (recommended)", value: "es5" },
|
|
15788
|
+
{ name: "All", value: "all" },
|
|
15789
|
+
{ name: "None", value: "none" }
|
|
15790
|
+
],
|
|
15791
|
+
default: defaults?.trailingCommas ?? DEFAULT_STANDARDS_CONFIG.code.trailingCommas
|
|
15792
|
+
});
|
|
15793
|
+
const allowAny = await confirm({
|
|
15794
|
+
message: 'Allow "any" type in TypeScript?',
|
|
15795
|
+
default: defaults?.allowAny ?? DEFAULT_STANDARDS_CONFIG.code.allowAny
|
|
15796
|
+
});
|
|
15797
|
+
const namedExportsOnly = await confirm({
|
|
15798
|
+
message: "Require named exports only (no default exports)?",
|
|
15799
|
+
default: defaults?.namedExportsOnly ?? DEFAULT_STANDARDS_CONFIG.code.namedExportsOnly
|
|
15800
|
+
});
|
|
15801
|
+
const roroPattern = await confirm({
|
|
15802
|
+
message: "Require RO-RO pattern (Receive Object, Return Object)?",
|
|
15803
|
+
default: defaults?.roroPattern ?? DEFAULT_STANDARDS_CONFIG.code.roroPattern
|
|
15804
|
+
});
|
|
15805
|
+
const jsDocRequired = await confirm({
|
|
15806
|
+
message: "Require JSDoc for all exports?",
|
|
15807
|
+
default: defaults?.jsDocRequired ?? DEFAULT_STANDARDS_CONFIG.code.jsDocRequired
|
|
15808
|
+
});
|
|
15809
|
+
return {
|
|
15810
|
+
indentStyle,
|
|
15811
|
+
indentSize,
|
|
15812
|
+
maxLineLength,
|
|
15813
|
+
maxFileLines,
|
|
15814
|
+
quoteStyle,
|
|
15815
|
+
semicolons,
|
|
15816
|
+
trailingCommas,
|
|
15817
|
+
allowAny,
|
|
15818
|
+
namedExportsOnly,
|
|
15819
|
+
roroPattern,
|
|
15820
|
+
jsDocRequired
|
|
15821
|
+
};
|
|
15822
|
+
}
|
|
15823
|
+
async function promptTestingStandards(defaults) {
|
|
15824
|
+
const def = STANDARDS_DEFINITIONS.testing;
|
|
15825
|
+
logger.newline();
|
|
15826
|
+
logger.subtitle(`${def.icon} ${def.name}`);
|
|
15827
|
+
const coverageTarget = await select({
|
|
15828
|
+
message: "Minimum code coverage:",
|
|
15829
|
+
choices: [
|
|
15830
|
+
{ name: "60% (startup)", value: 60 },
|
|
15831
|
+
{ name: "70% (relaxed)", value: 70 },
|
|
15832
|
+
{ name: "80% (standard)", value: 80 },
|
|
15833
|
+
{ name: "90% (strict)", value: 90 },
|
|
15834
|
+
{ name: "95% (enterprise)", value: 95 }
|
|
15835
|
+
],
|
|
15836
|
+
default: defaults?.coverageTarget ?? DEFAULT_STANDARDS_CONFIG.testing.coverageTarget
|
|
15837
|
+
});
|
|
15838
|
+
const tddRequired = await confirm({
|
|
15839
|
+
message: "Require TDD methodology (Red-Green-Refactor)?",
|
|
15840
|
+
default: defaults?.tddRequired ?? DEFAULT_STANDARDS_CONFIG.testing.tddRequired
|
|
15841
|
+
});
|
|
15842
|
+
const testPattern = await select({
|
|
15843
|
+
message: "Test pattern:",
|
|
15844
|
+
choices: [
|
|
15845
|
+
{ name: "AAA (Arrange-Act-Assert)", value: "aaa" },
|
|
15846
|
+
{ name: "GWT (Given-When-Then)", value: "gwt" }
|
|
15847
|
+
],
|
|
15848
|
+
default: defaults?.testPattern ?? DEFAULT_STANDARDS_CONFIG.testing.testPattern
|
|
15849
|
+
});
|
|
15850
|
+
const testLocation = await select({
|
|
15851
|
+
message: "Test file location:",
|
|
15852
|
+
choices: [
|
|
15853
|
+
{ name: "Separate (test/ folder)", value: "separate" },
|
|
15854
|
+
{ name: "Colocated (__tests__ near source)", value: "colocated" }
|
|
15855
|
+
],
|
|
15856
|
+
default: defaults?.testLocation ?? DEFAULT_STANDARDS_CONFIG.testing.testLocation
|
|
15857
|
+
});
|
|
15858
|
+
const unitTestMaxMs = await select({
|
|
15859
|
+
message: "Max time per unit test:",
|
|
15860
|
+
choices: [
|
|
15861
|
+
{ name: "50ms (fast)", value: 50 },
|
|
15862
|
+
{ name: "100ms (standard)", value: 100 },
|
|
15863
|
+
{ name: "200ms (relaxed)", value: 200 }
|
|
15864
|
+
],
|
|
15865
|
+
default: defaults?.unitTestMaxMs ?? DEFAULT_STANDARDS_CONFIG.testing.unitTestMaxMs
|
|
15866
|
+
});
|
|
15867
|
+
const integrationTestMaxMs = await select({
|
|
15868
|
+
message: "Max time per integration test:",
|
|
15869
|
+
choices: [
|
|
15870
|
+
{ name: "500ms (fast)", value: 500 },
|
|
15871
|
+
{ name: "1000ms (standard)", value: 1e3 },
|
|
15872
|
+
{ name: "2000ms (relaxed)", value: 2e3 }
|
|
15873
|
+
],
|
|
15874
|
+
default: defaults?.integrationTestMaxMs ?? DEFAULT_STANDARDS_CONFIG.testing.integrationTestMaxMs
|
|
15875
|
+
});
|
|
15876
|
+
return {
|
|
15877
|
+
coverageTarget,
|
|
15878
|
+
tddRequired,
|
|
15879
|
+
testPattern,
|
|
15880
|
+
testLocation,
|
|
15881
|
+
unitTestMaxMs,
|
|
15882
|
+
integrationTestMaxMs
|
|
15883
|
+
};
|
|
15884
|
+
}
|
|
15885
|
+
async function promptDocumentationStandards(defaults) {
|
|
15886
|
+
const def = STANDARDS_DEFINITIONS.documentation;
|
|
15887
|
+
logger.newline();
|
|
15888
|
+
logger.subtitle(`${def.icon} ${def.name}`);
|
|
15889
|
+
const jsDocLevel = await select({
|
|
15890
|
+
message: "JSDoc detail level:",
|
|
15891
|
+
choices: [
|
|
15892
|
+
{ name: "Minimal (brief description)", value: "minimal" },
|
|
15893
|
+
{ name: "Standard (description + params + returns)", value: "standard" },
|
|
15894
|
+
{ name: "Comprehensive (full docs with examples)", value: "comprehensive" }
|
|
15895
|
+
],
|
|
15896
|
+
default: defaults?.jsDocLevel ?? DEFAULT_STANDARDS_CONFIG.documentation.jsDocLevel
|
|
15897
|
+
});
|
|
15898
|
+
const requireExamples = await confirm({
|
|
15899
|
+
message: "Require @example in JSDoc?",
|
|
15900
|
+
default: defaults?.requireExamples ?? DEFAULT_STANDARDS_CONFIG.documentation.requireExamples
|
|
15901
|
+
});
|
|
15902
|
+
const changelogFormat = await select({
|
|
15903
|
+
message: "Changelog format:",
|
|
15904
|
+
choices: [
|
|
15905
|
+
{ name: "Conventional (auto-generated from commits)", value: "conventional" },
|
|
15906
|
+
{ name: "Keep a Changelog (manual, semantic)", value: "keepachangelog" }
|
|
15907
|
+
],
|
|
15908
|
+
default: defaults?.changelogFormat ?? DEFAULT_STANDARDS_CONFIG.documentation.changelogFormat
|
|
15909
|
+
});
|
|
15910
|
+
const inlineCommentPolicy = await select({
|
|
15911
|
+
message: "Inline comment policy:",
|
|
15912
|
+
choices: [
|
|
15913
|
+
{ name: "Why not What (explain reasoning)", value: "why-not-what" },
|
|
15914
|
+
{ name: "Minimal (only when necessary)", value: "minimal" },
|
|
15915
|
+
{ name: "Extensive (comment thoroughly)", value: "extensive" }
|
|
15916
|
+
],
|
|
15917
|
+
default: defaults?.inlineCommentPolicy ?? DEFAULT_STANDARDS_CONFIG.documentation.inlineCommentPolicy
|
|
15918
|
+
});
|
|
15919
|
+
return {
|
|
15920
|
+
jsDocLevel,
|
|
15921
|
+
requireExamples,
|
|
15922
|
+
changelogFormat,
|
|
15923
|
+
inlineCommentPolicy
|
|
15924
|
+
};
|
|
15925
|
+
}
|
|
15926
|
+
async function promptDesignStandards(defaults) {
|
|
15927
|
+
const def = STANDARDS_DEFINITIONS.design;
|
|
15928
|
+
logger.newline();
|
|
15929
|
+
logger.subtitle(`${def.icon} ${def.name}`);
|
|
15930
|
+
const cssFramework = await select({
|
|
15931
|
+
message: "CSS framework:",
|
|
15932
|
+
choices: [
|
|
15933
|
+
{ name: "Tailwind CSS", value: "tailwind" },
|
|
15934
|
+
{ name: "CSS Modules", value: "css-modules" },
|
|
15935
|
+
{ name: "Styled Components", value: "styled-components" },
|
|
15936
|
+
{ name: "Vanilla CSS", value: "vanilla" }
|
|
15937
|
+
],
|
|
15938
|
+
default: defaults?.cssFramework ?? DEFAULT_STANDARDS_CONFIG.design.cssFramework
|
|
15939
|
+
});
|
|
15940
|
+
const componentLibrary = await select({
|
|
15941
|
+
message: "Component library:",
|
|
15942
|
+
choices: [
|
|
15943
|
+
{ name: "shadcn/ui", value: "shadcn" },
|
|
15944
|
+
{ name: "Radix UI", value: "radix" },
|
|
15945
|
+
{ name: "Headless UI", value: "headless" },
|
|
15946
|
+
{ name: "None", value: "none" }
|
|
15947
|
+
],
|
|
15948
|
+
default: defaults?.componentLibrary ?? DEFAULT_STANDARDS_CONFIG.design.componentLibrary
|
|
15949
|
+
});
|
|
15950
|
+
const accessibilityLevel = await select({
|
|
15951
|
+
message: "WCAG accessibility level:",
|
|
15952
|
+
choices: [
|
|
15953
|
+
{ name: "Level A (minimum)", value: "A" },
|
|
15954
|
+
{ name: "Level AA (recommended)", value: "AA" },
|
|
15955
|
+
{ name: "Level AAA (highest)", value: "AAA" }
|
|
15956
|
+
],
|
|
15957
|
+
default: defaults?.accessibilityLevel ?? DEFAULT_STANDARDS_CONFIG.design.accessibilityLevel
|
|
15958
|
+
});
|
|
15959
|
+
const darkModeSupport = await confirm({
|
|
15960
|
+
message: "Support dark mode?",
|
|
15961
|
+
default: defaults?.darkModeSupport ?? DEFAULT_STANDARDS_CONFIG.design.darkModeSupport
|
|
15962
|
+
});
|
|
15963
|
+
return {
|
|
15964
|
+
cssFramework,
|
|
15965
|
+
componentLibrary,
|
|
15966
|
+
accessibilityLevel,
|
|
15967
|
+
darkModeSupport
|
|
15968
|
+
};
|
|
15969
|
+
}
|
|
15970
|
+
async function promptSecurityStandards(defaults) {
|
|
15971
|
+
const def = STANDARDS_DEFINITIONS.security;
|
|
15972
|
+
logger.newline();
|
|
15973
|
+
logger.subtitle(`${def.icon} ${def.name}`);
|
|
15974
|
+
const authPattern = await select({
|
|
15975
|
+
message: "Authentication pattern:",
|
|
15976
|
+
choices: [
|
|
15977
|
+
{ name: "JWT (JSON Web Tokens)", value: "jwt" },
|
|
15978
|
+
{ name: "Session (server-side)", value: "session" },
|
|
15979
|
+
{ name: "OAuth 2.0 / OIDC", value: "oauth" },
|
|
15980
|
+
{ name: "None", value: "none" }
|
|
15981
|
+
],
|
|
15982
|
+
default: defaults?.authPattern ?? DEFAULT_STANDARDS_CONFIG.security.authPattern
|
|
15983
|
+
});
|
|
15984
|
+
const inputValidation = await select({
|
|
15985
|
+
message: "Input validation library:",
|
|
15986
|
+
choices: [
|
|
15987
|
+
{ name: "Zod (TypeScript-first)", value: "zod" },
|
|
15988
|
+
{ name: "Yup (schema builder)", value: "yup" },
|
|
15989
|
+
{ name: "Joi (data validation)", value: "joi" },
|
|
15990
|
+
{ name: "Manual (custom)", value: "manual" }
|
|
15991
|
+
],
|
|
15992
|
+
default: defaults?.inputValidation ?? DEFAULT_STANDARDS_CONFIG.security.inputValidation
|
|
15993
|
+
});
|
|
15994
|
+
const csrfProtection = await confirm({
|
|
15995
|
+
message: "Enable CSRF protection?",
|
|
15996
|
+
default: defaults?.csrfProtection ?? DEFAULT_STANDARDS_CONFIG.security.csrfProtection
|
|
15997
|
+
});
|
|
15998
|
+
const rateLimiting = await confirm({
|
|
15999
|
+
message: "Enable rate limiting?",
|
|
16000
|
+
default: defaults?.rateLimiting ?? DEFAULT_STANDARDS_CONFIG.security.rateLimiting
|
|
16001
|
+
});
|
|
16002
|
+
return {
|
|
16003
|
+
authPattern,
|
|
16004
|
+
inputValidation,
|
|
16005
|
+
csrfProtection,
|
|
16006
|
+
rateLimiting
|
|
16007
|
+
};
|
|
16008
|
+
}
|
|
16009
|
+
async function promptPerformanceStandards(defaults) {
|
|
16010
|
+
const def = STANDARDS_DEFINITIONS.performance;
|
|
16011
|
+
logger.newline();
|
|
16012
|
+
logger.subtitle(`${def.icon} ${def.name}`);
|
|
16013
|
+
const lcpTarget = await select({
|
|
16014
|
+
message: "LCP target (Largest Contentful Paint):",
|
|
16015
|
+
choices: [
|
|
16016
|
+
{ name: "1500ms (excellent)", value: 1500 },
|
|
16017
|
+
{ name: "2000ms (good)", value: 2e3 },
|
|
16018
|
+
{ name: "2500ms (standard)", value: 2500 },
|
|
16019
|
+
{ name: "4000ms (needs improvement)", value: 4e3 }
|
|
16020
|
+
],
|
|
16021
|
+
default: defaults?.lcpTarget ?? DEFAULT_STANDARDS_CONFIG.performance.lcpTarget
|
|
16022
|
+
});
|
|
16023
|
+
const fidTarget = await select({
|
|
16024
|
+
message: "FID target (First Input Delay):",
|
|
16025
|
+
choices: [
|
|
16026
|
+
{ name: "50ms (excellent)", value: 50 },
|
|
16027
|
+
{ name: "100ms (good)", value: 100 },
|
|
16028
|
+
{ name: "200ms (standard)", value: 200 },
|
|
16029
|
+
{ name: "300ms (needs improvement)", value: 300 }
|
|
16030
|
+
],
|
|
16031
|
+
default: defaults?.fidTarget ?? DEFAULT_STANDARDS_CONFIG.performance.fidTarget
|
|
16032
|
+
});
|
|
16033
|
+
const clsTarget = await select({
|
|
16034
|
+
message: "CLS target (Cumulative Layout Shift):",
|
|
16035
|
+
choices: [
|
|
16036
|
+
{ name: "0.05 (excellent)", value: 0.05 },
|
|
16037
|
+
{ name: "0.1 (good)", value: 0.1 },
|
|
16038
|
+
{ name: "0.15 (standard)", value: 0.15 },
|
|
16039
|
+
{ name: "0.25 (needs improvement)", value: 0.25 }
|
|
16040
|
+
],
|
|
16041
|
+
default: defaults?.clsTarget ?? DEFAULT_STANDARDS_CONFIG.performance.clsTarget
|
|
16042
|
+
});
|
|
16043
|
+
const bundleSizeTargetKb = await select({
|
|
16044
|
+
message: "Bundle size target (KB):",
|
|
16045
|
+
choices: [
|
|
16046
|
+
{ name: "100KB (strict)", value: 100 },
|
|
16047
|
+
{ name: "150KB (good)", value: 150 },
|
|
16048
|
+
{ name: "250KB (standard)", value: 250 },
|
|
16049
|
+
{ name: "500KB (relaxed)", value: 500 }
|
|
16050
|
+
],
|
|
16051
|
+
default: defaults?.bundleSizeTargetKb ?? DEFAULT_STANDARDS_CONFIG.performance.bundleSizeTargetKb
|
|
16052
|
+
});
|
|
16053
|
+
const apiResponseTargetMs = await select({
|
|
16054
|
+
message: "API response time target (ms):",
|
|
16055
|
+
choices: [
|
|
16056
|
+
{ name: "100ms (fast)", value: 100 },
|
|
16057
|
+
{ name: "200ms (good)", value: 200 },
|
|
16058
|
+
{ name: "300ms (standard)", value: 300 },
|
|
16059
|
+
{ name: "500ms (relaxed)", value: 500 }
|
|
16060
|
+
],
|
|
16061
|
+
default: defaults?.apiResponseTargetMs ?? DEFAULT_STANDARDS_CONFIG.performance.apiResponseTargetMs
|
|
16062
|
+
});
|
|
16063
|
+
return {
|
|
16064
|
+
lcpTarget,
|
|
16065
|
+
fidTarget,
|
|
16066
|
+
clsTarget,
|
|
16067
|
+
bundleSizeTargetKb,
|
|
16068
|
+
apiResponseTargetMs
|
|
16069
|
+
};
|
|
16070
|
+
}
|
|
16071
|
+
function showStandardsSummary(config) {
|
|
16072
|
+
logger.newline();
|
|
16073
|
+
logger.subtitle("Standards Summary");
|
|
16074
|
+
logger.item(
|
|
16075
|
+
`Code: ${config.code.indentSize} ${config.code.indentStyle}s, ${config.code.quoteStyle} quotes`
|
|
16076
|
+
);
|
|
16077
|
+
logger.info(
|
|
16078
|
+
colors.muted(
|
|
16079
|
+
` Max: ${config.code.maxLineLength} chars/line, ${config.code.maxFileLines} lines/file`
|
|
16080
|
+
)
|
|
16081
|
+
);
|
|
16082
|
+
logger.info(
|
|
16083
|
+
colors.muted(
|
|
16084
|
+
` Rules: ${config.code.allowAny ? "any allowed" : "no any"}, ${config.code.namedExportsOnly ? "named exports" : "default exports"}`
|
|
16085
|
+
)
|
|
16086
|
+
);
|
|
16087
|
+
logger.item(
|
|
16088
|
+
`Testing: ${config.testing.coverageTarget}% coverage, ${config.testing.tddRequired ? "TDD required" : "TDD optional"}`
|
|
16089
|
+
);
|
|
16090
|
+
logger.info(
|
|
16091
|
+
colors.muted(
|
|
16092
|
+
` Pattern: ${config.testing.testPattern.toUpperCase()}, Location: ${config.testing.testLocation}`
|
|
16093
|
+
)
|
|
16094
|
+
);
|
|
16095
|
+
logger.item(`Documentation: ${config.documentation.jsDocLevel} JSDoc`);
|
|
16096
|
+
logger.info(colors.muted(` Comments: ${config.documentation.inlineCommentPolicy}`));
|
|
16097
|
+
logger.item(`Design: ${config.design.cssFramework}, ${config.design.componentLibrary}`);
|
|
16098
|
+
logger.info(
|
|
16099
|
+
colors.muted(
|
|
16100
|
+
` A11y: WCAG ${config.design.accessibilityLevel}, ${config.design.darkModeSupport ? "dark mode" : "no dark mode"}`
|
|
16101
|
+
)
|
|
16102
|
+
);
|
|
16103
|
+
logger.item(`Security: ${config.security.authPattern}, ${config.security.inputValidation}`);
|
|
16104
|
+
logger.info(
|
|
16105
|
+
colors.muted(
|
|
16106
|
+
` ${config.security.csrfProtection ? "CSRF" : "no CSRF"}, ${config.security.rateLimiting ? "rate limiting" : "no rate limiting"}`
|
|
16107
|
+
)
|
|
16108
|
+
);
|
|
16109
|
+
logger.item(
|
|
16110
|
+
`Performance: LCP ${config.performance.lcpTarget}ms, FID ${config.performance.fidTarget}ms`
|
|
16111
|
+
);
|
|
16112
|
+
logger.info(
|
|
16113
|
+
colors.muted(
|
|
16114
|
+
` Bundle: ${config.performance.bundleSizeTargetKb}KB, API: ${config.performance.apiResponseTargetMs}ms`
|
|
16115
|
+
)
|
|
16116
|
+
);
|
|
16117
|
+
}
|
|
16118
|
+
async function confirmStandardsConfig(config) {
|
|
16119
|
+
showStandardsSummary(config);
|
|
16120
|
+
logger.newline();
|
|
16121
|
+
return confirm({
|
|
16122
|
+
message: "Apply these standards?",
|
|
16123
|
+
default: true
|
|
16124
|
+
});
|
|
16125
|
+
}
|
|
16126
|
+
|
|
16127
|
+
// src/cli/commands/standards.ts
|
|
16128
|
+
function createStandardsCommand() {
|
|
16129
|
+
const cmd = new import_commander8.Command("standards").description("Configure project standards interactively").argument("[path]", "Project path (default: current directory)").option("--scan", "Scan for unconfigured standard placeholders").option(
|
|
16130
|
+
"-c, --category <name>",
|
|
16131
|
+
"Configure specific category (code|testing|documentation|design|security|performance)"
|
|
16132
|
+
).option("--preview", "Preview changes without applying").option("-y, --yes", "Accept defaults without prompts").option("-v, --verbose", "Detailed output").option("--update-templates", "Update/sync templates from package to project").action(runStandards);
|
|
16133
|
+
return cmd;
|
|
16134
|
+
}
|
|
16135
|
+
async function runStandards(path10, options) {
|
|
16136
|
+
const projectPath = resolvePath(path10 || ".");
|
|
16137
|
+
const claudePath = joinPath(projectPath, ".claude");
|
|
16138
|
+
logger.configure({ verbose: options.verbose, silent: false });
|
|
16139
|
+
logger.title("Project Standards Configuration");
|
|
16140
|
+
try {
|
|
16141
|
+
if (options.scan) {
|
|
16142
|
+
await scanMode2(claudePath, projectPath);
|
|
16143
|
+
return;
|
|
16144
|
+
}
|
|
16145
|
+
if (options.updateTemplates) {
|
|
16146
|
+
await updateTemplatesMode(claudePath, options);
|
|
16147
|
+
return;
|
|
16148
|
+
}
|
|
16149
|
+
if (options.preview) {
|
|
16150
|
+
await previewMode2(claudePath, projectPath);
|
|
16151
|
+
return;
|
|
16152
|
+
}
|
|
16153
|
+
if (options.yes) {
|
|
16154
|
+
await defaultsMode(claudePath, projectPath, options);
|
|
16155
|
+
return;
|
|
16156
|
+
}
|
|
16157
|
+
await interactiveMode2(claudePath, projectPath, options);
|
|
16158
|
+
} catch (error) {
|
|
16159
|
+
logger.error(
|
|
16160
|
+
`Standards configuration failed: ${error instanceof Error ? error.message : error}`
|
|
16161
|
+
);
|
|
16162
|
+
process.exit(1);
|
|
16163
|
+
}
|
|
16164
|
+
}
|
|
16165
|
+
async function scanMode2(claudePath, projectPath) {
|
|
16166
|
+
logger.info(`Scanning ${colors.primary(claudePath)} for standard placeholders...`);
|
|
16167
|
+
logger.newline();
|
|
16168
|
+
const existingConfig = await readConfig(projectPath);
|
|
16169
|
+
const standardsConfig = existingConfig?.extras?.standards;
|
|
16170
|
+
const scanResult = await scanStandardsPlaceholders(claudePath, standardsConfig);
|
|
16171
|
+
logger.info(formatScanResult(scanResult));
|
|
16172
|
+
if (scanResult.unconfiguredPlaceholders.length > 0) {
|
|
16173
|
+
logger.newline();
|
|
16174
|
+
logger.info("Run `claude-config standards` to configure them");
|
|
16175
|
+
}
|
|
16176
|
+
}
|
|
16177
|
+
async function updateTemplatesMode(claudePath, options) {
|
|
16178
|
+
logger.info("Checking for template updates...");
|
|
16179
|
+
logger.newline();
|
|
16180
|
+
const status = await checkTemplatesNeedUpdate(claudePath);
|
|
16181
|
+
if (!status.needsUpdate) {
|
|
16182
|
+
logger.success("All templates are up to date!");
|
|
16183
|
+
return;
|
|
16184
|
+
}
|
|
16185
|
+
if (status.missing.length > 0) {
|
|
16186
|
+
logger.info(`Missing templates (${status.missing.length}):`);
|
|
16187
|
+
for (const template of status.missing) {
|
|
16188
|
+
logger.info(` ${colors.warning("+")} ${template}`);
|
|
16189
|
+
}
|
|
16190
|
+
logger.newline();
|
|
16191
|
+
}
|
|
16192
|
+
if (status.outdated.length > 0) {
|
|
16193
|
+
logger.info(`Outdated templates (${status.outdated.length}):`);
|
|
16194
|
+
for (const template of status.outdated) {
|
|
16195
|
+
logger.info(` ${colors.primary("~")} ${template}`);
|
|
16196
|
+
}
|
|
16197
|
+
logger.newline();
|
|
16198
|
+
}
|
|
16199
|
+
const result = await syncStandardsTemplatesWithSpinner(claudePath, {
|
|
16200
|
+
overwrite: true,
|
|
16201
|
+
backup: true
|
|
16202
|
+
});
|
|
16203
|
+
if (options.verbose) {
|
|
16204
|
+
logger.newline();
|
|
16205
|
+
logger.info(formatSyncResult(result));
|
|
16206
|
+
}
|
|
16207
|
+
logger.newline();
|
|
16208
|
+
logger.success("Templates updated successfully!");
|
|
16209
|
+
logger.info("Run `claude-config standards` to configure the new placeholders");
|
|
16210
|
+
}
|
|
16211
|
+
async function previewMode2(claudePath, projectPath) {
|
|
16212
|
+
const existingConfig = await readConfig(projectPath);
|
|
16213
|
+
if (!existingConfig?.extras?.standards) {
|
|
16214
|
+
logger.warn("No standards configuration found");
|
|
16215
|
+
logger.info("Run `claude-config standards` first to set up configuration");
|
|
16216
|
+
return;
|
|
16217
|
+
}
|
|
16218
|
+
logger.info("Preview of replacements:");
|
|
16219
|
+
logger.newline();
|
|
16220
|
+
const replacements = await previewStandardsReplacements(
|
|
16221
|
+
claudePath,
|
|
16222
|
+
existingConfig.extras.standards
|
|
16223
|
+
);
|
|
16224
|
+
if (replacements.length === 0) {
|
|
16225
|
+
logger.info("No standard placeholders to replace");
|
|
16226
|
+
return;
|
|
16227
|
+
}
|
|
16228
|
+
const byFile = {};
|
|
16229
|
+
for (const r of replacements) {
|
|
16230
|
+
if (!byFile[r.file]) {
|
|
16231
|
+
byFile[r.file] = [];
|
|
16232
|
+
}
|
|
16233
|
+
byFile[r.file].push({ placeholder: r.placeholder, value: r.value });
|
|
16234
|
+
}
|
|
16235
|
+
for (const [file, changes] of Object.entries(byFile)) {
|
|
16236
|
+
logger.subtitle(file);
|
|
16237
|
+
for (const change of changes) {
|
|
16238
|
+
logger.info(` ${change.placeholder} \u2192 ${colors.primary(change.value)}`);
|
|
16239
|
+
}
|
|
16240
|
+
logger.newline();
|
|
16241
|
+
}
|
|
16242
|
+
logger.success(
|
|
16243
|
+
`Total: ${replacements.length} replacements in ${Object.keys(byFile).length} files`
|
|
16244
|
+
);
|
|
16245
|
+
}
|
|
16246
|
+
async function defaultsMode(claudePath, projectPath, options) {
|
|
16247
|
+
const templateStatus = await checkTemplatesNeedUpdate(claudePath);
|
|
16248
|
+
if (templateStatus.needsUpdate) {
|
|
16249
|
+
logger.warn("Some templates are missing or outdated:");
|
|
16250
|
+
if (templateStatus.missing.length > 0) {
|
|
16251
|
+
logger.info(` Missing: ${templateStatus.missing.length} template(s)`);
|
|
16252
|
+
}
|
|
16253
|
+
if (templateStatus.outdated.length > 0) {
|
|
16254
|
+
logger.info(` Outdated: ${templateStatus.outdated.length} template(s)`);
|
|
16255
|
+
}
|
|
16256
|
+
logger.newline();
|
|
16257
|
+
logger.info(
|
|
16258
|
+
`Run ${colors.primary("qazuor-claude-config standards --update-templates")} first to sync templates`
|
|
16259
|
+
);
|
|
16260
|
+
logger.newline();
|
|
16261
|
+
}
|
|
16262
|
+
logger.info("Applying default standards configuration...");
|
|
16263
|
+
logger.newline();
|
|
16264
|
+
const standardsConfig = DEFAULT_STANDARDS_CONFIG;
|
|
16265
|
+
showStandardsSummary(standardsConfig);
|
|
16266
|
+
logger.newline();
|
|
16267
|
+
const report = await replaceStandardsWithSpinner(claudePath, standardsConfig);
|
|
16268
|
+
if (options.verbose) {
|
|
16269
|
+
logger.newline();
|
|
16270
|
+
logger.info(formatStandardsReport(report));
|
|
16271
|
+
}
|
|
16272
|
+
if (report.modifiedFiles.length === 0 && report.unusedPlaceholders.length > 0) {
|
|
16273
|
+
logger.newline();
|
|
16274
|
+
logger.warn("No placeholders were replaced in templates.");
|
|
16275
|
+
logger.info(
|
|
16276
|
+
`Your templates might be outdated. Run ${colors.primary("--update-templates")} to sync them.`
|
|
16277
|
+
);
|
|
16278
|
+
}
|
|
16279
|
+
await saveStandardsConfig(projectPath, standardsConfig);
|
|
16280
|
+
logger.newline();
|
|
16281
|
+
logger.success("Standards configuration complete!");
|
|
16282
|
+
}
|
|
16283
|
+
async function interactiveMode2(claudePath, projectPath, options) {
|
|
16284
|
+
const templateStatus = await checkTemplatesNeedUpdate(claudePath);
|
|
16285
|
+
if (templateStatus.needsUpdate) {
|
|
16286
|
+
logger.warn("Some templates are missing or outdated:");
|
|
16287
|
+
if (templateStatus.missing.length > 0) {
|
|
16288
|
+
logger.info(` Missing: ${templateStatus.missing.length} template(s)`);
|
|
16289
|
+
}
|
|
16290
|
+
if (templateStatus.outdated.length > 0) {
|
|
16291
|
+
logger.info(` Outdated: ${templateStatus.outdated.length} template(s)`);
|
|
16292
|
+
}
|
|
16293
|
+
logger.newline();
|
|
16294
|
+
logger.info(
|
|
16295
|
+
`Run ${colors.primary("qazuor-claude-config standards --update-templates")} first to sync templates`
|
|
16296
|
+
);
|
|
16297
|
+
logger.newline();
|
|
16298
|
+
}
|
|
16299
|
+
const existingConfig = await readConfig(projectPath);
|
|
16300
|
+
const existingStandards = existingConfig?.extras?.standards;
|
|
16301
|
+
const standardsConfig = await promptStandardsConfig({
|
|
16302
|
+
defaults: existingStandards,
|
|
16303
|
+
category: options.category
|
|
16304
|
+
});
|
|
16305
|
+
const confirmed = await confirmStandardsConfig(standardsConfig);
|
|
16306
|
+
if (!confirmed) {
|
|
16307
|
+
logger.warn("Configuration cancelled");
|
|
16308
|
+
return;
|
|
16309
|
+
}
|
|
16310
|
+
const report = await replaceStandardsWithSpinner(claudePath, standardsConfig);
|
|
16311
|
+
if (options.verbose) {
|
|
16312
|
+
logger.newline();
|
|
16313
|
+
logger.info(formatStandardsReport(report));
|
|
16314
|
+
}
|
|
16315
|
+
if (report.modifiedFiles.length === 0 && report.unusedPlaceholders.length > 0) {
|
|
16316
|
+
logger.newline();
|
|
16317
|
+
logger.warn("No placeholders were replaced in templates.");
|
|
16318
|
+
logger.info(
|
|
16319
|
+
`Your templates might be outdated. Run ${colors.primary("--update-templates")} to sync them.`
|
|
16320
|
+
);
|
|
16321
|
+
}
|
|
16322
|
+
await saveStandardsConfig(projectPath, standardsConfig);
|
|
16323
|
+
logger.newline();
|
|
16324
|
+
logger.success("Standards configuration complete!");
|
|
16325
|
+
}
|
|
16326
|
+
async function saveStandardsConfig(projectPath, standardsConfig) {
|
|
16327
|
+
const existingConfig = await readConfig(projectPath);
|
|
16328
|
+
if (existingConfig) {
|
|
16329
|
+
existingConfig.extras = {
|
|
16330
|
+
...existingConfig.extras,
|
|
16331
|
+
standards: standardsConfig
|
|
16332
|
+
};
|
|
16333
|
+
existingConfig.customizations.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
|
|
16334
|
+
await writeConfig(projectPath, existingConfig);
|
|
16335
|
+
logger.success("Configuration saved to .claude/qazuor-claude-config.json");
|
|
16336
|
+
} else {
|
|
16337
|
+
logger.warn("No existing config found - standards not saved");
|
|
16338
|
+
logger.info("Run `claude-config init` first to initialize the project");
|
|
16339
|
+
}
|
|
16340
|
+
}
|
|
16341
|
+
|
|
13301
16342
|
// src/cli/index.ts
|
|
13302
16343
|
var VERSION2 = "0.1.0";
|
|
13303
|
-
var program = new
|
|
16344
|
+
var program = new import_commander9.Command().name("qazuor-claude-config").description("CLI tool to install and manage Claude Code configurations").version(VERSION2);
|
|
13304
16345
|
program.addCommand(createInitCommand());
|
|
13305
16346
|
program.addCommand(createListCommand());
|
|
13306
16347
|
program.addCommand(createAddCommand());
|
|
@@ -13308,6 +16349,7 @@ program.addCommand(createRemoveCommand());
|
|
|
13308
16349
|
program.addCommand(createStatusCommand());
|
|
13309
16350
|
program.addCommand(createUpdateCommand());
|
|
13310
16351
|
program.addCommand(createConfigureCommand());
|
|
16352
|
+
program.addCommand(createStandardsCommand());
|
|
13311
16353
|
|
|
13312
16354
|
// src/lib/utils/banner.ts
|
|
13313
16355
|
init_cjs_shims();
|