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