@qazuor/claude-code-config 0.3.1 → 0.5.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/dist/bin.cjs CHANGED
@@ -230,7 +230,7 @@ var import_node_module = require("module");
230
230
 
231
231
  // src/cli/index.ts
232
232
  init_cjs_shims();
233
- var import_commander8 = require("commander");
233
+ var import_commander9 = require("commander");
234
234
 
235
235
  // src/cli/commands/index.ts
236
236
  init_cjs_shims();
@@ -2594,7 +2594,7 @@ init_cjs_shims();
2594
2594
  // src/lib/config/reader.ts
2595
2595
  init_cjs_shims();
2596
2596
  init_fs();
2597
- var CONFIG_FILE = "config.json";
2597
+ var CONFIG_FILE = "qazuor-claude-config.json";
2598
2598
  var CLAUDE_DIR = ".claude";
2599
2599
  async function readConfig(projectPath) {
2600
2600
  const configPath = joinPath(projectPath, CLAUDE_DIR, CONFIG_FILE);
@@ -2616,7 +2616,7 @@ async function hasClaudeDir(projectPath) {
2616
2616
  // src/lib/config/writer.ts
2617
2617
  init_cjs_shims();
2618
2618
  init_fs();
2619
- var CONFIG_FILE2 = "config.json";
2619
+ var CONFIG_FILE2 = "qazuor-claude-config.json";
2620
2620
  var CLAUDE_DIR2 = ".claude";
2621
2621
  async function writeConfig(projectPath, config, options) {
2622
2622
  const claudePath = joinPath(projectPath, CLAUDE_DIR2);
@@ -3258,21 +3258,333 @@ init_cjs_shims();
3258
3258
  // src/lib/git-hooks/husky-installer.ts
3259
3259
  init_cjs_shims();
3260
3260
  init_fs();
3261
- function generateCommitMsgHook() {
3261
+
3262
+ // src/lib/git-hooks/precommit-generator.ts
3263
+ init_cjs_shims();
3264
+ function generatePreCommitScript(config) {
3265
+ if (!config.enabled) {
3266
+ return generateDisabledScript();
3267
+ }
3268
+ const sections = [];
3269
+ sections.push(generateHeader());
3270
+ if (config.showTiming) {
3271
+ sections.push(generateTimingSetup());
3272
+ }
3273
+ sections.push(generateErrorHandling(config.continueOnFailure));
3274
+ if (config.lint.enabled) {
3275
+ sections.push(generateLintSection(config));
3276
+ }
3277
+ if (config.typecheck.enabled) {
3278
+ sections.push(generateTypecheckSection(config));
3279
+ }
3280
+ if (config.formatCheck.enabled) {
3281
+ sections.push(generateFormatCheckSection(config));
3282
+ }
3283
+ if (config.tests.enabled && config.tests.mode !== "none") {
3284
+ sections.push(generateTestSection(config));
3285
+ }
3286
+ const sortedCustom = [...config.customCommands].sort(
3287
+ (a, b) => (a.order ?? 100) - (b.order ?? 100)
3288
+ );
3289
+ for (const cmd of sortedCustom) {
3290
+ sections.push(generateCustomCommandSection(cmd, config));
3291
+ }
3292
+ sections.push(generateFooter(config));
3293
+ return sections.join("\n\n");
3294
+ }
3295
+ function generateDisabledScript() {
3262
3296
  return `#!/usr/bin/env sh
3263
3297
  . "$(dirname -- "$0")/_/husky.sh"
3264
3298
 
3265
- npx --no -- commitlint --edit "\${1}"
3266
- `;
3299
+ # Pre-commit hook disabled
3300
+ exit 0`;
3267
3301
  }
3268
- function generatePreCommitHook(lintCommand) {
3269
- const command = lintCommand || "pnpm lint-staged";
3302
+ function generateHeader() {
3270
3303
  return `#!/usr/bin/env sh
3271
3304
  . "$(dirname -- "$0")/_/husky.sh"
3272
3305
 
3273
- ${command}
3306
+ # Pre-commit hook - Generated by @qazuor/claude-code-config
3307
+ # Bypass with: git commit --no-verify -m "message"
3308
+
3309
+ echo "\u{1F50D} Running pre-commit checks..."`;
3310
+ }
3311
+ function generateTimingSetup() {
3312
+ return `# Timing setup
3313
+ START_TIME=$(date +%s)
3314
+ step_start() { STEP_START=$(date +%s); }
3315
+ step_end() {
3316
+ STEP_END=$(date +%s)
3317
+ ELAPSED_TIME=$((STEP_END - STEP_START))
3318
+ echo " \u23F1\uFE0F Completed in \${ELAPSED_TIME}s"
3319
+ }`;
3320
+ }
3321
+ function generateErrorHandling(continueOnFailure) {
3322
+ if (continueOnFailure) {
3323
+ return `# Error tracking (continue on failure mode)
3324
+ ERRORS=0
3325
+ track_error() {
3326
+ ERRORS=$((ERRORS + 1))
3327
+ }`;
3328
+ }
3329
+ return `# Fail fast mode - exit on first error
3330
+ set -e`;
3331
+ }
3332
+ function generateLintSection(config) {
3333
+ const lint = config.lint;
3334
+ let command;
3335
+ if (lint.command) {
3336
+ command = lint.command;
3337
+ } else if (lint.stagedOnly) {
3338
+ command = getLintStagedCommand(lint.tool);
3339
+ } else {
3340
+ command = getLintCommand(lint.tool);
3341
+ }
3342
+ const lines = [];
3343
+ lines.push("# Linting");
3344
+ lines.push('echo ""');
3345
+ lines.push('echo "\u{1F4DD} Linting..."');
3346
+ if (config.showTiming) {
3347
+ lines.push("step_start");
3348
+ }
3349
+ if (lint.allowFailure) {
3350
+ lines.push(`if ${command}; then`);
3351
+ lines.push(' echo " \u2705 Lint passed"');
3352
+ lines.push("else");
3353
+ lines.push(' echo " \u26A0\uFE0F Lint warnings (non-blocking)"');
3354
+ lines.push("fi");
3355
+ } else if (config.continueOnFailure) {
3356
+ lines.push(`if ${command}; then`);
3357
+ lines.push(' echo " \u2705 Lint passed"');
3358
+ lines.push("else");
3359
+ lines.push(' echo " \u274C Lint failed"');
3360
+ lines.push(" track_error");
3361
+ lines.push("fi");
3362
+ } else {
3363
+ lines.push(`${command} || { echo " \u274C Lint failed"; exit 1; }`);
3364
+ lines.push('echo " \u2705 Lint passed"');
3365
+ }
3366
+ if (config.showTiming) {
3367
+ lines.push("step_end");
3368
+ }
3369
+ return lines.join("\n");
3370
+ }
3371
+ function getLintStagedCommand(tool) {
3372
+ switch (tool) {
3373
+ case "biome":
3374
+ return "pnpm biome check --staged --no-errors-on-unmatched";
3375
+ case "eslint":
3376
+ return "pnpm lint-staged";
3377
+ default:
3378
+ return "pnpm lint-staged";
3379
+ }
3380
+ }
3381
+ function getLintCommand(tool) {
3382
+ switch (tool) {
3383
+ case "biome":
3384
+ return "pnpm biome check .";
3385
+ case "eslint":
3386
+ return "pnpm eslint .";
3387
+ default:
3388
+ return "pnpm lint";
3389
+ }
3390
+ }
3391
+ function generateTypecheckSection(config) {
3392
+ const typecheck = config.typecheck;
3393
+ const command = typecheck.command ?? "pnpm typecheck";
3394
+ const lines = [];
3395
+ lines.push("# Type checking");
3396
+ lines.push('echo ""');
3397
+ lines.push('echo "\u{1F537} Type checking..."');
3398
+ if (config.showTiming) {
3399
+ lines.push("step_start");
3400
+ }
3401
+ if (typecheck.allowFailure) {
3402
+ lines.push(`if ${command}; then`);
3403
+ lines.push(' echo " \u2705 Types OK"');
3404
+ lines.push("else");
3405
+ lines.push(' echo " \u26A0\uFE0F Type warnings (non-blocking)"');
3406
+ lines.push("fi");
3407
+ } else if (config.continueOnFailure) {
3408
+ lines.push(`if ${command}; then`);
3409
+ lines.push(' echo " \u2705 Types OK"');
3410
+ lines.push("else");
3411
+ lines.push(' echo " \u274C Type check failed"');
3412
+ lines.push(" track_error");
3413
+ lines.push("fi");
3414
+ } else {
3415
+ lines.push(`${command} || { echo " \u274C Type check failed"; exit 1; }`);
3416
+ lines.push('echo " \u2705 Types OK"');
3417
+ }
3418
+ if (config.showTiming) {
3419
+ lines.push("step_end");
3420
+ }
3421
+ return lines.join("\n");
3422
+ }
3423
+ function generateFormatCheckSection(config) {
3424
+ const format = config.formatCheck;
3425
+ let command;
3426
+ if (format.command) {
3427
+ command = format.command;
3428
+ } else {
3429
+ command = getFormatCheckCommand(format.tool);
3430
+ }
3431
+ const lines = [];
3432
+ lines.push("# Format check");
3433
+ lines.push('echo ""');
3434
+ lines.push('echo "\u2728 Format check..."');
3435
+ if (config.showTiming) {
3436
+ lines.push("step_start");
3437
+ }
3438
+ if (format.allowFailure) {
3439
+ lines.push(`if ${command}; then`);
3440
+ lines.push(' echo " \u2705 Format OK"');
3441
+ lines.push("else");
3442
+ lines.push(' echo " \u26A0\uFE0F Format warnings (non-blocking)"');
3443
+ lines.push("fi");
3444
+ } else if (config.continueOnFailure) {
3445
+ lines.push(`if ${command}; then`);
3446
+ lines.push(' echo " \u2705 Format OK"');
3447
+ lines.push("else");
3448
+ lines.push(' echo " \u274C Format check failed"');
3449
+ lines.push(" track_error");
3450
+ lines.push("fi");
3451
+ } else {
3452
+ lines.push(`${command} || { echo " \u274C Format check failed"; exit 1; }`);
3453
+ lines.push('echo " \u2705 Format OK"');
3454
+ }
3455
+ if (config.showTiming) {
3456
+ lines.push("step_end");
3457
+ }
3458
+ return lines.join("\n");
3459
+ }
3460
+ function getFormatCheckCommand(tool) {
3461
+ switch (tool) {
3462
+ case "biome":
3463
+ return "pnpm biome format --check .";
3464
+ case "prettier":
3465
+ return "pnpm prettier --check .";
3466
+ default:
3467
+ return "pnpm format:check";
3468
+ }
3469
+ }
3470
+ function generateTestSection(config) {
3471
+ const tests = config.tests;
3472
+ let command;
3473
+ if (tests.command) {
3474
+ command = tests.command;
3475
+ } else if (tests.mode === "affected") {
3476
+ command = "pnpm vitest related --run";
3477
+ } else {
3478
+ command = "pnpm test";
3479
+ }
3480
+ if (tests.coverageThreshold > 0) {
3481
+ command = `${command} --coverage --coverage.thresholds.lines=${tests.coverageThreshold}`;
3482
+ }
3483
+ const lines = [];
3484
+ lines.push("# Tests");
3485
+ lines.push('echo ""');
3486
+ lines.push(`echo "\u{1F9EA} Running ${tests.mode === "affected" ? "affected" : "all"} tests..."`);
3487
+ if (config.showTiming) {
3488
+ lines.push("step_start");
3489
+ }
3490
+ if (tests.allowFailure) {
3491
+ lines.push(`if ${command}; then`);
3492
+ lines.push(' echo " \u2705 Tests passed"');
3493
+ lines.push("else");
3494
+ lines.push(' echo " \u26A0\uFE0F Test warnings (non-blocking)"');
3495
+ lines.push("fi");
3496
+ } else if (config.continueOnFailure) {
3497
+ lines.push(`if ${command}; then`);
3498
+ lines.push(' echo " \u2705 Tests passed"');
3499
+ lines.push("else");
3500
+ lines.push(' echo " \u274C Tests failed"');
3501
+ lines.push(" track_error");
3502
+ lines.push("fi");
3503
+ } else {
3504
+ lines.push(`${command} || { echo " \u274C Tests failed"; exit 1; }`);
3505
+ lines.push('echo " \u2705 Tests passed"');
3506
+ }
3507
+ if (config.showTiming) {
3508
+ lines.push("step_end");
3509
+ }
3510
+ return lines.join("\n");
3511
+ }
3512
+ function generateCustomCommandSection(cmd, config) {
3513
+ const lines = [];
3514
+ lines.push(`# Custom: ${cmd.name}`);
3515
+ lines.push('echo ""');
3516
+ lines.push(`echo "\u{1F527} ${cmd.name}..."`);
3517
+ if (config.showTiming) {
3518
+ lines.push("step_start");
3519
+ }
3520
+ if (cmd.allowFailure) {
3521
+ lines.push(`if ${cmd.command}; then`);
3522
+ lines.push(` echo " \u2705 ${cmd.name} passed"`);
3523
+ lines.push("else");
3524
+ lines.push(` echo " \u26A0\uFE0F ${cmd.name} warnings (non-blocking)"`);
3525
+ lines.push("fi");
3526
+ } else if (config.continueOnFailure) {
3527
+ lines.push(`if ${cmd.command}; then`);
3528
+ lines.push(` echo " \u2705 ${cmd.name} passed"`);
3529
+ lines.push("else");
3530
+ lines.push(` echo " \u274C ${cmd.name} failed"`);
3531
+ lines.push(" track_error");
3532
+ lines.push("fi");
3533
+ } else {
3534
+ lines.push(`${cmd.command} || { echo " \u274C ${cmd.name} failed"; exit 1; }`);
3535
+ lines.push(`echo " \u2705 ${cmd.name} passed"`);
3536
+ }
3537
+ if (config.showTiming) {
3538
+ lines.push("step_end");
3539
+ }
3540
+ return lines.join("\n");
3541
+ }
3542
+ function generateFooter(config) {
3543
+ const lines = [];
3544
+ lines.push("# Final status");
3545
+ lines.push('echo ""');
3546
+ if (config.showTiming) {
3547
+ lines.push("END_TIME=$(date +%s)");
3548
+ lines.push("TOTAL_TIME=$((END_TIME - START_TIME))");
3549
+ }
3550
+ if (config.continueOnFailure) {
3551
+ lines.push("if [ $ERRORS -gt 0 ]; then");
3552
+ lines.push(' echo "\u274C Pre-commit failed with $ERRORS error(s)"');
3553
+ if (config.showTiming) {
3554
+ lines.push(' echo "\u23F1\uFE0F Total time: ${TOTAL_TIME}s"');
3555
+ }
3556
+ lines.push(" exit 1");
3557
+ lines.push("fi");
3558
+ }
3559
+ if (config.showTiming) {
3560
+ lines.push('echo "\u2728 All checks passed! (${TOTAL_TIME}s)"');
3561
+ } else {
3562
+ lines.push('echo "\u2728 All checks passed!"');
3563
+ }
3564
+ return lines.join("\n");
3565
+ }
3566
+ function generateSimplePreCommitHook(command) {
3567
+ return `#!/usr/bin/env sh
3568
+ . "$(dirname -- "$0")/_/husky.sh"
3569
+
3570
+ ${command}`;
3571
+ }
3572
+
3573
+ // src/lib/git-hooks/husky-installer.ts
3574
+ function generateCommitMsgHook() {
3575
+ return `#!/usr/bin/env sh
3576
+ . "$(dirname -- "$0")/_/husky.sh"
3577
+
3578
+ npx --no -- commitlint --edit "\${1}"
3274
3579
  `;
3275
3580
  }
3581
+ function generatePreCommitHook(lintCommand, preCommitConfig) {
3582
+ if (preCommitConfig) {
3583
+ return generatePreCommitScript(preCommitConfig);
3584
+ }
3585
+ const command = lintCommand || "pnpm lint-staged";
3586
+ return generateSimplePreCommitHook(command);
3587
+ }
3276
3588
  function generatePrePushHook(testCommand) {
3277
3589
  const command = testCommand || "pnpm test";
3278
3590
  return `#!/usr/bin/env sh
@@ -3363,7 +3675,10 @@ async function installHusky(projectPath, config, options) {
3363
3675
  if (config.preCommit) {
3364
3676
  const preCommitPath = joinPath(huskyDir, "pre-commit");
3365
3677
  if (!await pathExists(preCommitPath) || options?.overwrite) {
3366
- await writeFile(preCommitPath, generatePreCommitHook(config.lintCommand));
3678
+ await writeFile(
3679
+ preCommitPath,
3680
+ generatePreCommitHook(config.lintCommand, config.preCommitConfig)
3681
+ );
3367
3682
  await makeExecutable(preCommitPath);
3368
3683
  result.created.push("pre-commit");
3369
3684
  } else {
@@ -3595,8 +3910,8 @@ async function getHooksStatus(projectPath) {
3595
3910
  let executable = false;
3596
3911
  if (exists) {
3597
3912
  try {
3598
- const fs6 = await import("fs/promises");
3599
- const stats = await fs6.stat(filePath);
3913
+ const fs9 = await import("fs/promises");
3914
+ const stats = await fs9.stat(filePath);
3600
3915
  executable = (stats.mode & 73) !== 0;
3601
3916
  } catch {
3602
3917
  }
@@ -9695,6 +10010,10 @@ async function promptMcpConfig(options) {
9695
10010
  skippedConfigs: []
9696
10011
  };
9697
10012
  }
10013
+ const projectPath = options?.projectPath || process.cwd();
10014
+ const installedServers = await getInstalledMcpServers(projectPath);
10015
+ const userInstalledSet = new Set(installedServers.user);
10016
+ const projectInstalledSet = new Set(installedServers.project);
9698
10017
  const level = await select({
9699
10018
  message: "Where should MCP servers be configured?",
9700
10019
  choices: [
@@ -9715,13 +10034,44 @@ async function promptMcpConfig(options) {
9715
10034
  const selectedServerIds = [];
9716
10035
  logger.newline();
9717
10036
  logger.info("Select MCP servers to install:");
10037
+ const totalInstalled = userInstalledSet.size + projectInstalledSet.size;
10038
+ if (totalInstalled > 0) {
10039
+ const parts = [];
10040
+ if (userInstalledSet.size > 0) {
10041
+ parts.push(`${userInstalledSet.size} at user level`);
10042
+ }
10043
+ if (projectInstalledSet.size > 0) {
10044
+ parts.push(`${projectInstalledSet.size} at project level`);
10045
+ }
10046
+ logger.info(colors.muted(` (${parts.join(", ")} already installed)`));
10047
+ }
9718
10048
  logger.newline();
9719
10049
  for (const [category, servers] of Object.entries(serversByCategory)) {
9720
- const choices = servers.map((s) => ({
9721
- name: `${s.name} - ${s.description}`,
9722
- value: s.id,
9723
- checked: options?.defaults?.servers?.some((i) => i.serverId === s.id) ?? false
9724
- }));
10050
+ const choices = servers.map((s) => {
10051
+ const isInstalledAtUserLevel = userInstalledSet.has(s.id);
10052
+ const isInstalledAtProjectLevel = projectInstalledSet.has(s.id);
10053
+ if (isInstalledAtUserLevel) {
10054
+ return {
10055
+ name: `${s.name} - ${s.description} ${colors.muted("(already installed at user level)")}`,
10056
+ value: s.id,
10057
+ checked: false,
10058
+ disabled: "already installed at user level"
10059
+ };
10060
+ }
10061
+ if (isInstalledAtProjectLevel) {
10062
+ return {
10063
+ name: `${s.name} - ${s.description} ${colors.muted("(already installed at project level)")}`,
10064
+ value: s.id,
10065
+ checked: false,
10066
+ disabled: "already installed at project level"
10067
+ };
10068
+ }
10069
+ return {
10070
+ name: `${s.name} - ${s.description}`,
10071
+ value: s.id,
10072
+ checked: options?.defaults?.servers?.some((i) => i.serverId === s.id) ?? false
10073
+ };
10074
+ });
9725
10075
  const categoryLabel = formatCategory(category);
9726
10076
  const selected = await checkbox({
9727
10077
  message: `${categoryLabel}:`,
@@ -10591,27 +10941,27 @@ async function buildConfigContext(projectPath) {
10591
10941
  values: {}
10592
10942
  };
10593
10943
  try {
10594
- const fs6 = await import("fs/promises");
10595
- const path7 = await import("path");
10596
- const pkgPath = path7.join(projectPath, "package.json");
10597
- const pkgContent = await fs6.readFile(pkgPath, "utf-8");
10944
+ const fs9 = await import("fs/promises");
10945
+ const path10 = await import("path");
10946
+ const pkgPath = path10.join(projectPath, "package.json");
10947
+ const pkgContent = await fs9.readFile(pkgPath, "utf-8");
10598
10948
  const pkg = JSON.parse(pkgContent);
10599
10949
  context.scripts = pkg.scripts || {};
10600
10950
  context.dependencies = {
10601
10951
  ...pkg.dependencies || {},
10602
10952
  ...pkg.devDependencies || {}
10603
10953
  };
10604
- context.hasTypeScript = Boolean(context.dependencies.typescript) || await fileExists(path7.join(projectPath, "tsconfig.json"));
10605
- if (await fileExists(path7.join(projectPath, "pnpm-lock.yaml"))) {
10954
+ context.hasTypeScript = Boolean(context.dependencies.typescript) || await fileExists(path10.join(projectPath, "tsconfig.json"));
10955
+ if (await fileExists(path10.join(projectPath, "pnpm-lock.yaml"))) {
10606
10956
  context.packageManager = "pnpm";
10607
- } else if (await fileExists(path7.join(projectPath, "yarn.lock"))) {
10957
+ } else if (await fileExists(path10.join(projectPath, "yarn.lock"))) {
10608
10958
  context.packageManager = "yarn";
10609
- } else if (await fileExists(path7.join(projectPath, "bun.lockb"))) {
10959
+ } else if (await fileExists(path10.join(projectPath, "bun.lockb"))) {
10610
10960
  context.packageManager = "bun";
10611
10961
  } else {
10612
10962
  context.packageManager = "npm";
10613
10963
  }
10614
- context.isGitRepo = await fileExists(path7.join(projectPath, ".git"));
10964
+ context.isGitRepo = await fileExists(path10.join(projectPath, ".git"));
10615
10965
  if (context.isGitRepo) {
10616
10966
  try {
10617
10967
  const { execSync } = await import("child_process");
@@ -10630,8 +10980,8 @@ async function buildConfigContext(projectPath) {
10630
10980
  }
10631
10981
  async function fileExists(filePath) {
10632
10982
  try {
10633
- const fs6 = await import("fs/promises");
10634
- await fs6.access(filePath);
10983
+ const fs9 = await import("fs/promises");
10984
+ await fs9.access(filePath);
10635
10985
  return true;
10636
10986
  } catch {
10637
10987
  return false;
@@ -11219,7 +11569,7 @@ function createMcpConfigStep() {
11219
11569
  required: false
11220
11570
  },
11221
11571
  computeDefaults: (ctx) => ctx.mcpConfig,
11222
- execute: async (_ctx, defaults) => {
11572
+ execute: async (ctx, defaults) => {
11223
11573
  const goBack = await promptBackOption(5, "Configure MCP servers or go back?");
11224
11574
  if (goBack) {
11225
11575
  return createResult(
@@ -11227,7 +11577,7 @@ function createMcpConfigStep() {
11227
11577
  "back"
11228
11578
  );
11229
11579
  }
11230
- const result = await promptMcpConfig();
11580
+ const result = await promptMcpConfig({ projectPath: ctx.projectPath });
11231
11581
  return createResult(result, "next");
11232
11582
  }
11233
11583
  };
@@ -11665,8 +12015,8 @@ function createInitCommand() {
11665
12015
  ).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);
11666
12016
  return cmd;
11667
12017
  }
11668
- async function runInit(path7, options) {
11669
- const projectPath = resolvePath(path7 || ".");
12018
+ async function runInit(path10, options) {
12019
+ const projectPath = resolvePath(path10 || ".");
11670
12020
  logger.configure({ verbose: options.verbose, silent: false });
11671
12021
  logger.title("@qazuor/claude-code-config");
11672
12022
  logger.info(`Initializing Claude configuration in ${colors.primary(projectPath)}`);
@@ -11675,6 +12025,7 @@ async function runInit(path7, options) {
11675
12025
  showCancelHint();
11676
12026
  }
11677
12027
  try {
12028
+ let existingConfig = null;
11678
12029
  if (await hasExistingClaudeConfig(projectPath)) {
11679
12030
  if (!options.force) {
11680
12031
  const action = await promptExistingProjectAction();
@@ -11685,6 +12036,10 @@ async function runInit(path7, options) {
11685
12036
  if (action !== "overwrite" && action !== "merge") {
11686
12037
  return;
11687
12038
  }
12039
+ existingConfig = await readConfig(projectPath);
12040
+ if (existingConfig) {
12041
+ logger.info("Using existing configuration as defaults");
12042
+ }
11688
12043
  }
11689
12044
  }
11690
12045
  const detection = await detectProject(projectPath);
@@ -11701,7 +12056,7 @@ async function runInit(path7, options) {
11701
12056
  () => loadRegistry(templatesPath),
11702
12057
  { silent: options.dryRun }
11703
12058
  );
11704
- const buildResult = options.yes ? await buildDefaultConfig(projectPath, detection, options) : await buildInteractiveConfig(projectPath, detection, registry, options);
12059
+ const buildResult = options.yes ? await buildDefaultConfig(projectPath, detection, options) : await buildInteractiveConfig(projectPath, detection, registry, options, existingConfig);
11705
12060
  if (!buildResult) {
11706
12061
  logger.warn("Configuration cancelled");
11707
12062
  return;
@@ -11867,7 +12222,7 @@ async function buildDefaultConfig(projectPath, detection, options) {
11867
12222
  // No MCP servers in default mode
11868
12223
  };
11869
12224
  }
11870
- async function buildInteractiveConfig(projectPath, detection, registry, options) {
12225
+ async function buildInteractiveConfig(projectPath, detection, registry, options, existingConfig) {
11871
12226
  const projectName = await getProjectName(projectPath);
11872
12227
  const projectDesc = await getProjectDescription(projectPath);
11873
12228
  const wizardConfig = createInitWizardConfig(
@@ -11881,24 +12236,45 @@ async function buildInteractiveConfig(projectPath, detection, registry, options)
11881
12236
  },
11882
12237
  registry
11883
12238
  );
12239
+ const existingProject = existingConfig?.project;
12240
+ const existingPrefs = existingConfig?.preferences;
11884
12241
  const initialContext = {
11885
12242
  projectPath,
11886
12243
  registry,
11887
12244
  detection: {
11888
12245
  detected: detection.detected,
11889
12246
  projectType: detection.projectType,
11890
- packageManager: detection.packageManager,
12247
+ packageManager: existingPrefs?.packageManager || detection.packageManager,
11891
12248
  suggestedBundles: detection.suggestedBundles,
11892
12249
  detectedTechnologies: detection.detectedTechnologies
11893
12250
  },
12251
+ // Use existing project info as defaults, fallback to detected values
11894
12252
  projectInfo: {
11895
- name: projectName || "",
11896
- description: projectDesc || "",
11897
- org: "",
11898
- repo: projectName?.toLowerCase().replace(/\s+/g, "-") || "",
11899
- entityType: "item",
11900
- entityTypePlural: "items"
11901
- }
12253
+ name: existingProject?.name || projectName || "",
12254
+ description: existingProject?.description || projectDesc || "",
12255
+ org: existingProject?.org || "",
12256
+ repo: existingProject?.repo || projectName?.toLowerCase().replace(/\s+/g, "-") || "",
12257
+ entityType: existingProject?.entityType || "item",
12258
+ entityTypePlural: existingProject?.entityTypePlural || "items",
12259
+ domain: existingProject?.domain,
12260
+ location: existingProject?.location,
12261
+ author: existingProject?.author
12262
+ },
12263
+ // Use existing preferences as defaults
12264
+ preferences: existingPrefs ? {
12265
+ language: existingPrefs.language,
12266
+ responseLanguage: existingPrefs.responseLanguage,
12267
+ includeCoAuthor: existingPrefs.includeCoAuthor,
12268
+ packageManager: existingPrefs.packageManager
12269
+ } : void 0,
12270
+ // Use existing hook config as defaults
12271
+ hookConfig: existingConfig?.extras?.hooks,
12272
+ // Use existing code style config as defaults
12273
+ codeStyleConfig: existingConfig?.extras?.codeStyle,
12274
+ // Use existing folder preferences as defaults
12275
+ folderPreferences: existingConfig?.extras?.folderPreferences,
12276
+ // Use existing permissions config as defaults
12277
+ permissionsConfig: existingConfig?.customizations?.permissions
11902
12278
  };
11903
12279
  const wizardResult = await runWizard(
11904
12280
  wizardConfig,
@@ -12624,7 +13000,7 @@ async function runStatus(options) {
12624
13000
  }
12625
13001
  const config = await readConfig(projectPath);
12626
13002
  if (!config) {
12627
- logger.warn(".claude directory exists but config.json is missing");
13003
+ logger.warn(".claude directory exists but qazuor-claude-config.json is missing");
12628
13004
  logger.info('Run "claude-config init" to initialize properly');
12629
13005
  process.exit(0);
12630
13006
  }
@@ -12936,7 +13312,8 @@ async function handleConfigUpdates(projectPath, config, _options) {
12936
13312
  }
12937
13313
  if (reconfigureOptions.includes("mcp")) {
12938
13314
  const mcpResult = await promptMcpConfig({
12939
- defaults: config.mcp
13315
+ defaults: config.mcp,
13316
+ projectPath
12940
13317
  });
12941
13318
  config.mcp = mcpResult.config;
12942
13319
  await installMcpServers(projectPath, mcpResult.config);
@@ -13123,8 +13500,8 @@ function createConfigureCommand() {
13123
13500
  ).option("--preview", "Preview changes without applying").option("--show-defaults", "Show global defaults").option("-v, --verbose", "Detailed output").action(runConfigure);
13124
13501
  return cmd;
13125
13502
  }
13126
- async function runConfigure(path7, options) {
13127
- const projectPath = resolvePath(path7 || ".");
13503
+ async function runConfigure(path10, options) {
13504
+ const projectPath = resolvePath(path10 || ".");
13128
13505
  const claudePath = joinPath(projectPath, ".claude");
13129
13506
  logger.configure({ verbose: options.verbose, silent: false });
13130
13507
  logger.title("Template Configuration");
@@ -13255,16 +13632,1837 @@ async function interactiveMode(claudePath, projectPath, options) {
13255
13632
  };
13256
13633
  existingConfig.customizations.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
13257
13634
  await writeConfig(projectPath, existingConfig);
13258
- logger.success("Configuration saved to .claude/config.json");
13635
+ logger.success("Configuration saved to .claude/qazuor-claude-config.json");
13259
13636
  }
13260
13637
  await promptSaveGlobalDefaults(templateConfig);
13261
13638
  logger.newline();
13262
13639
  logger.success("Template configuration complete!");
13263
13640
  }
13264
13641
 
13642
+ // src/cli/commands/standards.ts
13643
+ init_cjs_shims();
13644
+ var import_commander8 = require("commander");
13645
+
13646
+ // src/constants/standards-defaults.ts
13647
+ init_cjs_shims();
13648
+ var DEFAULT_CODE_STANDARDS = {
13649
+ indentStyle: "space",
13650
+ indentSize: 2,
13651
+ maxLineLength: 100,
13652
+ maxFileLines: 500,
13653
+ quoteStyle: "single",
13654
+ semicolons: true,
13655
+ trailingCommas: "es5",
13656
+ allowAny: false,
13657
+ namedExportsOnly: true,
13658
+ roroPattern: true,
13659
+ jsDocRequired: true
13660
+ };
13661
+ var DEFAULT_TESTING_STANDARDS = {
13662
+ coverageTarget: 80,
13663
+ tddRequired: true,
13664
+ testPattern: "aaa",
13665
+ testLocation: "separate",
13666
+ unitTestMaxMs: 100,
13667
+ integrationTestMaxMs: 1e3
13668
+ };
13669
+ var DEFAULT_DOCUMENTATION_STANDARDS = {
13670
+ jsDocLevel: "standard",
13671
+ requireExamples: false,
13672
+ changelogFormat: "conventional",
13673
+ inlineCommentPolicy: "why-not-what"
13674
+ };
13675
+ var DEFAULT_DESIGN_STANDARDS = {
13676
+ cssFramework: "tailwind",
13677
+ componentLibrary: "shadcn",
13678
+ accessibilityLevel: "AA",
13679
+ darkModeSupport: true
13680
+ };
13681
+ var DEFAULT_SECURITY_STANDARDS = {
13682
+ authPattern: "jwt",
13683
+ inputValidation: "zod",
13684
+ csrfProtection: true,
13685
+ rateLimiting: true
13686
+ };
13687
+ var DEFAULT_PERFORMANCE_STANDARDS = {
13688
+ lcpTarget: 2500,
13689
+ fidTarget: 100,
13690
+ clsTarget: 0.1,
13691
+ bundleSizeTargetKb: 250,
13692
+ apiResponseTargetMs: 300
13693
+ };
13694
+ var DEFAULT_STANDARDS_CONFIG = {
13695
+ code: DEFAULT_CODE_STANDARDS,
13696
+ testing: DEFAULT_TESTING_STANDARDS,
13697
+ documentation: DEFAULT_DOCUMENTATION_STANDARDS,
13698
+ design: DEFAULT_DESIGN_STANDARDS,
13699
+ security: DEFAULT_SECURITY_STANDARDS,
13700
+ performance: DEFAULT_PERFORMANCE_STANDARDS
13701
+ };
13702
+ var STANDARDS_PRESETS = {
13703
+ strict: {
13704
+ name: "Strict",
13705
+ description: "High quality standards with strict enforcement (90%+ coverage, TDD required)",
13706
+ config: {
13707
+ code: {
13708
+ ...DEFAULT_CODE_STANDARDS,
13709
+ allowAny: false,
13710
+ jsDocRequired: true
13711
+ },
13712
+ testing: {
13713
+ ...DEFAULT_TESTING_STANDARDS,
13714
+ coverageTarget: 90,
13715
+ tddRequired: true
13716
+ },
13717
+ documentation: {
13718
+ ...DEFAULT_DOCUMENTATION_STANDARDS,
13719
+ jsDocLevel: "comprehensive",
13720
+ requireExamples: true
13721
+ },
13722
+ design: {
13723
+ ...DEFAULT_DESIGN_STANDARDS,
13724
+ accessibilityLevel: "AAA"
13725
+ },
13726
+ security: {
13727
+ ...DEFAULT_SECURITY_STANDARDS,
13728
+ csrfProtection: true,
13729
+ rateLimiting: true
13730
+ },
13731
+ performance: {
13732
+ ...DEFAULT_PERFORMANCE_STANDARDS,
13733
+ lcpTarget: 2e3,
13734
+ apiResponseTargetMs: 200
13735
+ }
13736
+ }
13737
+ },
13738
+ balanced: {
13739
+ name: "Balanced",
13740
+ description: "Good balance between quality and pragmatism (80% coverage)",
13741
+ config: DEFAULT_STANDARDS_CONFIG
13742
+ },
13743
+ relaxed: {
13744
+ name: "Relaxed",
13745
+ description: "More flexible standards for rapid development (70% coverage)",
13746
+ config: {
13747
+ code: {
13748
+ ...DEFAULT_CODE_STANDARDS,
13749
+ maxFileLines: 800,
13750
+ jsDocRequired: false
13751
+ },
13752
+ testing: {
13753
+ ...DEFAULT_TESTING_STANDARDS,
13754
+ coverageTarget: 70,
13755
+ tddRequired: false
13756
+ },
13757
+ documentation: {
13758
+ ...DEFAULT_DOCUMENTATION_STANDARDS,
13759
+ jsDocLevel: "minimal",
13760
+ requireExamples: false
13761
+ },
13762
+ design: {
13763
+ ...DEFAULT_DESIGN_STANDARDS,
13764
+ accessibilityLevel: "A"
13765
+ },
13766
+ security: {
13767
+ ...DEFAULT_SECURITY_STANDARDS
13768
+ },
13769
+ performance: {
13770
+ ...DEFAULT_PERFORMANCE_STANDARDS,
13771
+ lcpTarget: 4e3,
13772
+ apiResponseTargetMs: 500
13773
+ }
13774
+ }
13775
+ },
13776
+ startup: {
13777
+ name: "Startup",
13778
+ description: "Fast iteration with minimum viable standards (60% coverage)",
13779
+ config: {
13780
+ code: {
13781
+ ...DEFAULT_CODE_STANDARDS,
13782
+ maxFileLines: 1e3,
13783
+ jsDocRequired: false,
13784
+ roroPattern: false
13785
+ },
13786
+ testing: {
13787
+ coverageTarget: 60,
13788
+ tddRequired: false,
13789
+ testPattern: "aaa",
13790
+ testLocation: "colocated",
13791
+ unitTestMaxMs: 200,
13792
+ integrationTestMaxMs: 2e3
13793
+ },
13794
+ documentation: {
13795
+ jsDocLevel: "minimal",
13796
+ requireExamples: false,
13797
+ changelogFormat: "conventional",
13798
+ inlineCommentPolicy: "minimal"
13799
+ },
13800
+ design: {
13801
+ cssFramework: "tailwind",
13802
+ componentLibrary: "shadcn",
13803
+ accessibilityLevel: "A",
13804
+ darkModeSupport: false
13805
+ },
13806
+ security: {
13807
+ authPattern: "jwt",
13808
+ inputValidation: "zod",
13809
+ csrfProtection: false,
13810
+ rateLimiting: false
13811
+ },
13812
+ performance: {
13813
+ lcpTarget: 4e3,
13814
+ fidTarget: 300,
13815
+ clsTarget: 0.25,
13816
+ bundleSizeTargetKb: 500,
13817
+ apiResponseTargetMs: 500
13818
+ }
13819
+ }
13820
+ },
13821
+ enterprise: {
13822
+ name: "Enterprise",
13823
+ description: "Enterprise-grade standards with full compliance (95%+ coverage)",
13824
+ config: {
13825
+ code: {
13826
+ indentStyle: "space",
13827
+ indentSize: 2,
13828
+ maxLineLength: 120,
13829
+ maxFileLines: 400,
13830
+ quoteStyle: "single",
13831
+ semicolons: true,
13832
+ trailingCommas: "all",
13833
+ allowAny: false,
13834
+ namedExportsOnly: true,
13835
+ roroPattern: true,
13836
+ jsDocRequired: true
13837
+ },
13838
+ testing: {
13839
+ coverageTarget: 95,
13840
+ tddRequired: true,
13841
+ testPattern: "aaa",
13842
+ testLocation: "separate",
13843
+ unitTestMaxMs: 50,
13844
+ integrationTestMaxMs: 500
13845
+ },
13846
+ documentation: {
13847
+ jsDocLevel: "comprehensive",
13848
+ requireExamples: true,
13849
+ changelogFormat: "keepachangelog",
13850
+ inlineCommentPolicy: "why-not-what"
13851
+ },
13852
+ design: {
13853
+ cssFramework: "tailwind",
13854
+ componentLibrary: "radix",
13855
+ accessibilityLevel: "AAA",
13856
+ darkModeSupport: true
13857
+ },
13858
+ security: {
13859
+ authPattern: "oauth",
13860
+ inputValidation: "zod",
13861
+ csrfProtection: true,
13862
+ rateLimiting: true
13863
+ },
13864
+ performance: {
13865
+ lcpTarget: 1500,
13866
+ fidTarget: 50,
13867
+ clsTarget: 0.05,
13868
+ bundleSizeTargetKb: 150,
13869
+ apiResponseTargetMs: 150
13870
+ }
13871
+ }
13872
+ },
13873
+ custom: {
13874
+ name: "Custom",
13875
+ description: "Configure each standard manually",
13876
+ config: DEFAULT_STANDARDS_CONFIG
13877
+ }
13878
+ };
13879
+
13880
+ // src/lib/standards/index.ts
13881
+ init_cjs_shims();
13882
+
13883
+ // src/lib/standards/definitions.ts
13884
+ init_cjs_shims();
13885
+ var CODE_STANDARDS_DEFINITION = {
13886
+ id: "code",
13887
+ name: "Code Standards",
13888
+ description: "Code style, formatting, and TypeScript conventions",
13889
+ icon: "\u{1F4DD}",
13890
+ options: [
13891
+ {
13892
+ id: "indentStyle",
13893
+ label: "Indent Style",
13894
+ description: "Use spaces or tabs for indentation",
13895
+ type: "select",
13896
+ choices: [
13897
+ { name: "Spaces", value: "space", description: "Standard for most projects" },
13898
+ { name: "Tabs", value: "tab", description: "Better for accessibility" }
13899
+ ],
13900
+ affectsPlaceholders: ["{{INDENT_STYLE}}"]
13901
+ },
13902
+ {
13903
+ id: "indentSize",
13904
+ label: "Indent Size",
13905
+ description: "Number of spaces/tab width",
13906
+ type: "select",
13907
+ choices: [
13908
+ { name: "2 spaces", value: "2", description: "Most common in JS/TS" },
13909
+ { name: "4 spaces", value: "4", description: "More readable for some" }
13910
+ ],
13911
+ affectsPlaceholders: ["{{INDENT_SIZE}}"]
13912
+ },
13913
+ {
13914
+ id: "maxLineLength",
13915
+ label: "Max Line Length",
13916
+ description: "Maximum characters per line",
13917
+ type: "select",
13918
+ choices: [
13919
+ { name: "80 characters", value: "80", description: "Classic terminal width" },
13920
+ { name: "100 characters", value: "100", description: "Modern balance" },
13921
+ { name: "120 characters", value: "120", description: "Wide screens" }
13922
+ ],
13923
+ affectsPlaceholders: ["{{MAX_LINE_LENGTH}}"]
13924
+ },
13925
+ {
13926
+ id: "maxFileLines",
13927
+ label: "Max File Lines",
13928
+ description: "Maximum lines per file (excluding tests, docs, JSON)",
13929
+ type: "select",
13930
+ choices: [
13931
+ { name: "300 lines", value: "300", description: "Very strict" },
13932
+ { name: "500 lines", value: "500", description: "Recommended" },
13933
+ { name: "800 lines", value: "800", description: "Relaxed" },
13934
+ { name: "1000 lines", value: "1000", description: "Very relaxed" }
13935
+ ],
13936
+ affectsPlaceholders: ["{{MAX_FILE_LINES}}"]
13937
+ },
13938
+ {
13939
+ id: "quoteStyle",
13940
+ label: "Quote Style",
13941
+ description: "Single or double quotes for strings",
13942
+ type: "select",
13943
+ choices: [
13944
+ { name: "Single quotes", value: "single", description: "const x = 'hello'" },
13945
+ { name: "Double quotes", value: "double", description: 'const x = "hello"' }
13946
+ ],
13947
+ affectsPlaceholders: ["{{QUOTE_STYLE}}"]
13948
+ },
13949
+ {
13950
+ id: "semicolons",
13951
+ label: "Semicolons",
13952
+ description: "Use semicolons at end of statements",
13953
+ type: "boolean",
13954
+ affectsPlaceholders: ["{{USE_SEMICOLONS}}"]
13955
+ },
13956
+ {
13957
+ id: "trailingCommas",
13958
+ label: "Trailing Commas",
13959
+ description: "Add trailing commas in multiline constructs",
13960
+ type: "select",
13961
+ choices: [
13962
+ { name: "ES5", value: "es5", description: "Where valid in ES5 (objects, arrays)" },
13963
+ { name: "All", value: "all", description: "Everywhere possible" },
13964
+ { name: "None", value: "none", description: "No trailing commas" }
13965
+ ],
13966
+ affectsPlaceholders: ["{{TRAILING_COMMAS}}"]
13967
+ },
13968
+ {
13969
+ id: "allowAny",
13970
+ label: 'Allow "any" Type',
13971
+ description: 'Allow the "any" type in TypeScript',
13972
+ type: "boolean",
13973
+ affectsPlaceholders: ["{{ALLOW_ANY}}"]
13974
+ },
13975
+ {
13976
+ id: "namedExportsOnly",
13977
+ label: "Named Exports Only",
13978
+ description: "Require named exports (no default exports)",
13979
+ type: "boolean",
13980
+ affectsPlaceholders: ["{{NAMED_EXPORTS_ONLY}}"]
13981
+ },
13982
+ {
13983
+ id: "roroPattern",
13984
+ label: "RO-RO Pattern",
13985
+ description: "Require Receive Object, Return Object pattern",
13986
+ type: "boolean",
13987
+ affectsPlaceholders: ["{{RORO_PATTERN}}"]
13988
+ },
13989
+ {
13990
+ id: "jsDocRequired",
13991
+ label: "JSDoc Required",
13992
+ description: "Require JSDoc for all exports",
13993
+ type: "boolean",
13994
+ affectsPlaceholders: ["{{JSDOC_REQUIRED}}"]
13995
+ }
13996
+ ],
13997
+ targetFiles: ["code-standards.md", "architecture-patterns.md"]
13998
+ };
13999
+ var TESTING_STANDARDS_DEFINITION = {
14000
+ id: "testing",
14001
+ name: "Testing Standards",
14002
+ description: "Test coverage, TDD, and testing methodology",
14003
+ icon: "\u{1F9EA}",
14004
+ options: [
14005
+ {
14006
+ id: "coverageTarget",
14007
+ label: "Coverage Target",
14008
+ description: "Minimum code coverage percentage",
14009
+ type: "select",
14010
+ choices: [
14011
+ { name: "60%", value: "60", description: "Minimum viable" },
14012
+ { name: "70%", value: "70", description: "Relaxed" },
14013
+ { name: "80%", value: "80", description: "Standard" },
14014
+ { name: "90%", value: "90", description: "Strict" },
14015
+ { name: "95%", value: "95", description: "Enterprise" }
14016
+ ],
14017
+ affectsPlaceholders: ["{{COVERAGE_TARGET}}"]
14018
+ },
14019
+ {
14020
+ id: "tddRequired",
14021
+ label: "TDD Required",
14022
+ description: "Require Test-Driven Development (Red-Green-Refactor)",
14023
+ type: "boolean",
14024
+ affectsPlaceholders: ["{{TDD_REQUIRED}}"]
14025
+ },
14026
+ {
14027
+ id: "testPattern",
14028
+ label: "Test Pattern",
14029
+ description: "Test structure pattern",
14030
+ type: "select",
14031
+ choices: [
14032
+ { name: "AAA", value: "aaa", description: "Arrange-Act-Assert" },
14033
+ { name: "GWT", value: "gwt", description: "Given-When-Then" }
14034
+ ],
14035
+ affectsPlaceholders: ["{{TEST_PATTERN}}"]
14036
+ },
14037
+ {
14038
+ id: "testLocation",
14039
+ label: "Test Location",
14040
+ description: "Where to place test files",
14041
+ type: "select",
14042
+ choices: [
14043
+ { name: "Separate", value: "separate", description: "test/ folder at root" },
14044
+ { name: "Colocated", value: "colocated", description: "__tests__ near source" }
14045
+ ],
14046
+ affectsPlaceholders: ["{{TEST_LOCATION}}"]
14047
+ },
14048
+ {
14049
+ id: "unitTestMaxMs",
14050
+ label: "Unit Test Max (ms)",
14051
+ description: "Maximum milliseconds per unit test",
14052
+ type: "select",
14053
+ choices: [
14054
+ { name: "50ms", value: "50", description: "Very fast" },
14055
+ { name: "100ms", value: "100", description: "Standard" },
14056
+ { name: "200ms", value: "200", description: "Relaxed" }
14057
+ ],
14058
+ affectsPlaceholders: ["{{UNIT_TEST_MAX_MS}}"]
14059
+ },
14060
+ {
14061
+ id: "integrationTestMaxMs",
14062
+ label: "Integration Test Max (ms)",
14063
+ description: "Maximum milliseconds per integration test",
14064
+ type: "select",
14065
+ choices: [
14066
+ { name: "500ms", value: "500", description: "Fast" },
14067
+ { name: "1000ms", value: "1000", description: "Standard" },
14068
+ { name: "2000ms", value: "2000", description: "Relaxed" }
14069
+ ],
14070
+ affectsPlaceholders: ["{{INTEGRATION_TEST_MAX_MS}}"]
14071
+ }
14072
+ ],
14073
+ targetFiles: ["testing-standards.md"]
14074
+ };
14075
+ var DOCUMENTATION_STANDARDS_DEFINITION = {
14076
+ id: "documentation",
14077
+ name: "Documentation Standards",
14078
+ description: "JSDoc, comments, and changelog conventions",
14079
+ icon: "\u{1F4DA}",
14080
+ options: [
14081
+ {
14082
+ id: "jsDocLevel",
14083
+ label: "JSDoc Level",
14084
+ description: "Level of detail in JSDoc comments",
14085
+ type: "select",
14086
+ choices: [
14087
+ { name: "Minimal", value: "minimal", description: "Brief description only" },
14088
+ { name: "Standard", value: "standard", description: "Description + params + returns" },
14089
+ { name: "Comprehensive", value: "comprehensive", description: "Full docs with examples" }
14090
+ ],
14091
+ affectsPlaceholders: ["{{JSDOC_LEVEL}}"]
14092
+ },
14093
+ {
14094
+ id: "requireExamples",
14095
+ label: "Require Examples",
14096
+ description: "Require @example in JSDoc",
14097
+ type: "boolean",
14098
+ affectsPlaceholders: ["{{REQUIRE_EXAMPLES}}"]
14099
+ },
14100
+ {
14101
+ id: "changelogFormat",
14102
+ label: "Changelog Format",
14103
+ description: "Changelog format to follow",
14104
+ type: "select",
14105
+ choices: [
14106
+ { name: "Conventional", value: "conventional", description: "Auto-generated from commits" },
14107
+ { name: "Keep a Changelog", value: "keepachangelog", description: "Manual, semantic" }
14108
+ ],
14109
+ affectsPlaceholders: ["{{CHANGELOG_FORMAT}}"]
14110
+ },
14111
+ {
14112
+ id: "inlineCommentPolicy",
14113
+ label: "Inline Comment Policy",
14114
+ description: "Policy for inline code comments",
14115
+ type: "select",
14116
+ choices: [
14117
+ {
14118
+ name: "Why not What",
14119
+ value: "why-not-what",
14120
+ description: "Explain reasoning, not obvious"
14121
+ },
14122
+ { name: "Minimal", value: "minimal", description: "Only when necessary" },
14123
+ { name: "Extensive", value: "extensive", description: "Comment thoroughly" }
14124
+ ],
14125
+ affectsPlaceholders: ["{{INLINE_COMMENT_POLICY}}"]
14126
+ }
14127
+ ],
14128
+ targetFiles: ["documentation-standards.md"]
14129
+ };
14130
+ var DESIGN_STANDARDS_DEFINITION = {
14131
+ id: "design",
14132
+ name: "Design Standards",
14133
+ description: "UI/UX, CSS, and accessibility standards",
14134
+ icon: "\u{1F3A8}",
14135
+ options: [
14136
+ {
14137
+ id: "cssFramework",
14138
+ label: "CSS Framework",
14139
+ description: "CSS/styling approach",
14140
+ type: "select",
14141
+ choices: [
14142
+ { name: "Tailwind CSS", value: "tailwind", description: "Utility-first CSS" },
14143
+ { name: "CSS Modules", value: "css-modules", description: "Scoped CSS" },
14144
+ { name: "Styled Components", value: "styled-components", description: "CSS-in-JS" },
14145
+ { name: "Vanilla CSS", value: "vanilla", description: "Plain CSS" }
14146
+ ],
14147
+ affectsPlaceholders: ["{{CSS_FRAMEWORK}}"]
14148
+ },
14149
+ {
14150
+ id: "componentLibrary",
14151
+ label: "Component Library",
14152
+ description: "UI component library",
14153
+ type: "select",
14154
+ choices: [
14155
+ { name: "shadcn/ui", value: "shadcn", description: "Copy-paste components" },
14156
+ { name: "Radix UI", value: "radix", description: "Unstyled primitives" },
14157
+ { name: "Headless UI", value: "headless", description: "Unstyled, accessible" },
14158
+ { name: "None", value: "none", description: "Build from scratch" }
14159
+ ],
14160
+ affectsPlaceholders: ["{{COMPONENT_LIBRARY}}"]
14161
+ },
14162
+ {
14163
+ id: "accessibilityLevel",
14164
+ label: "Accessibility Level",
14165
+ description: "WCAG accessibility compliance level",
14166
+ type: "select",
14167
+ choices: [
14168
+ { name: "Level A", value: "A", description: "Minimum" },
14169
+ { name: "Level AA", value: "AA", description: "Standard (recommended)" },
14170
+ { name: "Level AAA", value: "AAA", description: "Highest" }
14171
+ ],
14172
+ affectsPlaceholders: ["{{WCAG_LEVEL}}", "{{ACCESSIBILITY_LEVEL}}"]
14173
+ },
14174
+ {
14175
+ id: "darkModeSupport",
14176
+ label: "Dark Mode Support",
14177
+ description: "Support dark mode theme",
14178
+ type: "boolean",
14179
+ affectsPlaceholders: ["{{DARK_MODE_SUPPORT}}"]
14180
+ }
14181
+ ],
14182
+ targetFiles: ["design-standards.md"]
14183
+ };
14184
+ var SECURITY_STANDARDS_DEFINITION = {
14185
+ id: "security",
14186
+ name: "Security Standards",
14187
+ description: "Authentication, validation, and security practices",
14188
+ icon: "\u{1F512}",
14189
+ options: [
14190
+ {
14191
+ id: "authPattern",
14192
+ label: "Auth Pattern",
14193
+ description: "Authentication approach",
14194
+ type: "select",
14195
+ choices: [
14196
+ { name: "JWT", value: "jwt", description: "JSON Web Tokens" },
14197
+ { name: "Session", value: "session", description: "Server-side sessions" },
14198
+ { name: "OAuth", value: "oauth", description: "OAuth 2.0 / OIDC" },
14199
+ { name: "None", value: "none", description: "No authentication" }
14200
+ ],
14201
+ affectsPlaceholders: ["{{AUTH_PATTERN}}"]
14202
+ },
14203
+ {
14204
+ id: "inputValidation",
14205
+ label: "Input Validation",
14206
+ description: "Validation library",
14207
+ type: "select",
14208
+ choices: [
14209
+ { name: "Zod", value: "zod", description: "TypeScript-first validation" },
14210
+ { name: "Yup", value: "yup", description: "Schema builder" },
14211
+ { name: "Joi", value: "joi", description: "Data validation" },
14212
+ { name: "Manual", value: "manual", description: "Custom validation" }
14213
+ ],
14214
+ affectsPlaceholders: ["{{VALIDATION_LIBRARY}}", "{{INPUT_VALIDATION}}"]
14215
+ },
14216
+ {
14217
+ id: "csrfProtection",
14218
+ label: "CSRF Protection",
14219
+ description: "Enable Cross-Site Request Forgery protection",
14220
+ type: "boolean",
14221
+ affectsPlaceholders: ["{{CSRF_PROTECTION}}"]
14222
+ },
14223
+ {
14224
+ id: "rateLimiting",
14225
+ label: "Rate Limiting",
14226
+ description: "Enable API rate limiting",
14227
+ type: "boolean",
14228
+ affectsPlaceholders: ["{{RATE_LIMITING}}"]
14229
+ }
14230
+ ],
14231
+ targetFiles: ["security-standards.md"]
14232
+ };
14233
+ var PERFORMANCE_STANDARDS_DEFINITION = {
14234
+ id: "performance",
14235
+ name: "Performance Standards",
14236
+ description: "Core Web Vitals and performance targets",
14237
+ icon: "\u26A1",
14238
+ options: [
14239
+ {
14240
+ id: "lcpTarget",
14241
+ label: "LCP Target (ms)",
14242
+ description: "Largest Contentful Paint target",
14243
+ type: "select",
14244
+ choices: [
14245
+ { name: "1500ms", value: "1500", description: "Excellent" },
14246
+ { name: "2000ms", value: "2000", description: "Good" },
14247
+ { name: "2500ms", value: "2500", description: "Standard" },
14248
+ { name: "4000ms", value: "4000", description: "Needs improvement" }
14249
+ ],
14250
+ affectsPlaceholders: ["{{LCP_TARGET}}"]
14251
+ },
14252
+ {
14253
+ id: "fidTarget",
14254
+ label: "FID Target (ms)",
14255
+ description: "First Input Delay target",
14256
+ type: "select",
14257
+ choices: [
14258
+ { name: "50ms", value: "50", description: "Excellent" },
14259
+ { name: "100ms", value: "100", description: "Good" },
14260
+ { name: "200ms", value: "200", description: "Standard" },
14261
+ { name: "300ms", value: "300", description: "Needs improvement" }
14262
+ ],
14263
+ affectsPlaceholders: ["{{FID_TARGET}}"]
14264
+ },
14265
+ {
14266
+ id: "clsTarget",
14267
+ label: "CLS Target",
14268
+ description: "Cumulative Layout Shift target",
14269
+ type: "select",
14270
+ choices: [
14271
+ { name: "0.05", value: "0.05", description: "Excellent" },
14272
+ { name: "0.1", value: "0.1", description: "Good" },
14273
+ { name: "0.15", value: "0.15", description: "Standard" },
14274
+ { name: "0.25", value: "0.25", description: "Needs improvement" }
14275
+ ],
14276
+ affectsPlaceholders: ["{{CLS_TARGET}}"]
14277
+ },
14278
+ {
14279
+ id: "bundleSizeTargetKb",
14280
+ label: "Bundle Size (KB)",
14281
+ description: "Maximum initial bundle size",
14282
+ type: "select",
14283
+ choices: [
14284
+ { name: "100KB", value: "100", description: "Very strict" },
14285
+ { name: "150KB", value: "150", description: "Strict" },
14286
+ { name: "250KB", value: "250", description: "Standard" },
14287
+ { name: "500KB", value: "500", description: "Relaxed" }
14288
+ ],
14289
+ affectsPlaceholders: ["{{BUNDLE_SIZE_TARGET}}"]
14290
+ },
14291
+ {
14292
+ id: "apiResponseTargetMs",
14293
+ label: "API Response (ms)",
14294
+ description: "Maximum API response time",
14295
+ type: "select",
14296
+ choices: [
14297
+ { name: "100ms", value: "100", description: "Very fast" },
14298
+ { name: "200ms", value: "200", description: "Fast" },
14299
+ { name: "300ms", value: "300", description: "Standard" },
14300
+ { name: "500ms", value: "500", description: "Relaxed" }
14301
+ ],
14302
+ affectsPlaceholders: ["{{API_RESPONSE_TARGET}}"]
14303
+ }
14304
+ ],
14305
+ targetFiles: ["performance-standards.md"]
14306
+ };
14307
+ var STANDARDS_DEFINITIONS = {
14308
+ code: CODE_STANDARDS_DEFINITION,
14309
+ testing: TESTING_STANDARDS_DEFINITION,
14310
+ documentation: DOCUMENTATION_STANDARDS_DEFINITION,
14311
+ design: DESIGN_STANDARDS_DEFINITION,
14312
+ security: SECURITY_STANDARDS_DEFINITION,
14313
+ performance: PERFORMANCE_STANDARDS_DEFINITION
14314
+ };
14315
+
14316
+ // src/lib/standards/replacer.ts
14317
+ init_cjs_shims();
14318
+ var fs6 = __toESM(require("fs/promises"), 1);
14319
+ var path7 = __toESM(require("path"), 1);
14320
+ var import_ora3 = __toESM(require("ora"), 1);
14321
+ var PROCESSABLE_EXTENSIONS2 = [".md", ".json", ".yaml", ".yml", ".txt"];
14322
+ var SKIP_DIRECTORIES3 = ["node_modules", ".git", "dist", "build", ".next", ".turbo"];
14323
+ function flattenStandardsConfig(config) {
14324
+ const flattened = {};
14325
+ if (config.code) {
14326
+ flattened["{{INDENT_STYLE}}"] = config.code.indentStyle;
14327
+ flattened["{{INDENT_SIZE}}"] = String(config.code.indentSize);
14328
+ flattened["{{MAX_LINE_LENGTH}}"] = String(config.code.maxLineLength);
14329
+ flattened["{{MAX_FILE_LINES}}"] = String(config.code.maxFileLines);
14330
+ flattened["{{QUOTE_STYLE}}"] = config.code.quoteStyle;
14331
+ flattened["{{USE_SEMICOLONS}}"] = config.code.semicolons ? "yes" : "no";
14332
+ flattened["{{TRAILING_COMMAS}}"] = config.code.trailingCommas;
14333
+ flattened["{{ALLOW_ANY}}"] = config.code.allowAny ? "yes" : "no";
14334
+ flattened["{{NAMED_EXPORTS_ONLY}}"] = config.code.namedExportsOnly ? "yes" : "no";
14335
+ flattened["{{RORO_PATTERN}}"] = config.code.roroPattern ? "yes" : "no";
14336
+ flattened["{{JSDOC_REQUIRED}}"] = config.code.jsDocRequired ? "yes" : "no";
14337
+ }
14338
+ if (config.testing) {
14339
+ flattened["{{COVERAGE_TARGET}}"] = String(config.testing.coverageTarget);
14340
+ flattened["{{TDD_REQUIRED}}"] = config.testing.tddRequired ? "yes" : "no";
14341
+ flattened["{{TEST_PATTERN}}"] = config.testing.testPattern.toUpperCase();
14342
+ flattened["{{TEST_LOCATION}}"] = config.testing.testLocation;
14343
+ flattened["{{UNIT_TEST_MAX_MS}}"] = String(config.testing.unitTestMaxMs);
14344
+ flattened["{{INTEGRATION_TEST_MAX_MS}}"] = String(config.testing.integrationTestMaxMs);
14345
+ }
14346
+ if (config.documentation) {
14347
+ flattened["{{JSDOC_LEVEL}}"] = config.documentation.jsDocLevel;
14348
+ flattened["{{REQUIRE_EXAMPLES}}"] = config.documentation.requireExamples ? "yes" : "no";
14349
+ flattened["{{CHANGELOG_FORMAT}}"] = config.documentation.changelogFormat;
14350
+ flattened["{{INLINE_COMMENT_POLICY}}"] = config.documentation.inlineCommentPolicy;
14351
+ }
14352
+ if (config.design) {
14353
+ flattened["{{CSS_FRAMEWORK}}"] = config.design.cssFramework;
14354
+ flattened["{{COMPONENT_LIBRARY}}"] = config.design.componentLibrary;
14355
+ flattened["{{WCAG_LEVEL}}"] = config.design.accessibilityLevel;
14356
+ flattened["{{ACCESSIBILITY_LEVEL}}"] = config.design.accessibilityLevel;
14357
+ flattened["{{DARK_MODE_SUPPORT}}"] = config.design.darkModeSupport ? "yes" : "no";
14358
+ }
14359
+ if (config.security) {
14360
+ flattened["{{AUTH_PATTERN}}"] = config.security.authPattern;
14361
+ flattened["{{VALIDATION_LIBRARY}}"] = config.security.inputValidation;
14362
+ flattened["{{INPUT_VALIDATION}}"] = config.security.inputValidation;
14363
+ flattened["{{CSRF_PROTECTION}}"] = config.security.csrfProtection ? "yes" : "no";
14364
+ flattened["{{RATE_LIMITING}}"] = config.security.rateLimiting ? "yes" : "no";
14365
+ }
14366
+ if (config.performance) {
14367
+ flattened["{{LCP_TARGET}}"] = String(config.performance.lcpTarget);
14368
+ flattened["{{FID_TARGET}}"] = String(config.performance.fidTarget);
14369
+ flattened["{{CLS_TARGET}}"] = String(config.performance.clsTarget);
14370
+ flattened["{{BUNDLE_SIZE_TARGET}}"] = String(config.performance.bundleSizeTargetKb);
14371
+ flattened["{{API_RESPONSE_TARGET}}"] = String(config.performance.apiResponseTargetMs);
14372
+ }
14373
+ return flattened;
14374
+ }
14375
+ function shouldProcessFile2(filePath) {
14376
+ const ext = path7.extname(filePath).toLowerCase();
14377
+ return PROCESSABLE_EXTENSIONS2.includes(ext);
14378
+ }
14379
+ function shouldSkipDirectory3(dirName) {
14380
+ return SKIP_DIRECTORIES3.includes(dirName) || dirName.startsWith(".");
14381
+ }
14382
+ async function getAllFiles3(dir) {
14383
+ const files = [];
14384
+ try {
14385
+ const entries = await fs6.readdir(dir, { withFileTypes: true });
14386
+ for (const entry of entries) {
14387
+ const fullPath = path7.join(dir, entry.name);
14388
+ if (entry.isDirectory()) {
14389
+ if (!shouldSkipDirectory3(entry.name)) {
14390
+ const subFiles = await getAllFiles3(fullPath);
14391
+ files.push(...subFiles);
14392
+ }
14393
+ } else if (entry.isFile() && shouldProcessFile2(entry.name)) {
14394
+ files.push(fullPath);
14395
+ }
14396
+ }
14397
+ } catch {
14398
+ }
14399
+ return files;
14400
+ }
14401
+ async function replaceInFile3(filePath, replacements) {
14402
+ const changes = [];
14403
+ try {
14404
+ let content = await fs6.readFile(filePath, "utf-8");
14405
+ let modified = false;
14406
+ for (const [placeholder, value] of Object.entries(replacements)) {
14407
+ if (content.includes(placeholder)) {
14408
+ content = content.split(placeholder).join(value);
14409
+ changes.push({ placeholder, value });
14410
+ modified = true;
14411
+ }
14412
+ }
14413
+ if (modified) {
14414
+ await fs6.writeFile(filePath, content, "utf-8");
14415
+ }
14416
+ } catch {
14417
+ }
14418
+ return changes;
14419
+ }
14420
+ async function replaceStandardsPlaceholders(claudePath, config) {
14421
+ const replacements = flattenStandardsConfig(config);
14422
+ const standardsDir = path7.join(claudePath, "docs", "standards");
14423
+ const files = await getAllFiles3(standardsDir);
14424
+ const report = {
14425
+ modifiedFiles: [],
14426
+ replacedPlaceholders: [],
14427
+ unusedPlaceholders: [],
14428
+ errors: []
14429
+ };
14430
+ const usedPlaceholders = /* @__PURE__ */ new Set();
14431
+ for (const file of files) {
14432
+ try {
14433
+ const changes = await replaceInFile3(file, replacements);
14434
+ if (changes.length > 0) {
14435
+ report.modifiedFiles.push(path7.relative(claudePath, file));
14436
+ for (const change of changes) {
14437
+ if (!report.replacedPlaceholders.includes(change.placeholder)) {
14438
+ report.replacedPlaceholders.push(change.placeholder);
14439
+ }
14440
+ usedPlaceholders.add(change.placeholder);
14441
+ }
14442
+ }
14443
+ } catch (error) {
14444
+ report.errors.push(`Error processing ${file}: ${String(error)}`);
14445
+ }
14446
+ }
14447
+ for (const placeholder of Object.keys(replacements)) {
14448
+ if (!usedPlaceholders.has(placeholder)) {
14449
+ report.unusedPlaceholders.push(placeholder);
14450
+ }
14451
+ }
14452
+ return report;
14453
+ }
14454
+ async function replaceStandardsWithSpinner(claudePath, config) {
14455
+ const spinner2 = (0, import_ora3.default)("Applying standards configuration...").start();
14456
+ try {
14457
+ const report = await replaceStandardsPlaceholders(claudePath, config);
14458
+ if (report.modifiedFiles.length > 0) {
14459
+ spinner2.succeed(
14460
+ `Applied ${report.replacedPlaceholders.length} standards to ${report.modifiedFiles.length} files`
14461
+ );
14462
+ } else {
14463
+ spinner2.info("No standards placeholders found to replace");
14464
+ }
14465
+ return report;
14466
+ } catch (error) {
14467
+ spinner2.fail("Failed to apply standards configuration");
14468
+ throw error;
14469
+ }
14470
+ }
14471
+ async function previewStandardsReplacements(claudePath, config) {
14472
+ const replacements = flattenStandardsConfig(config);
14473
+ const standardsDir = path7.join(claudePath, "docs", "standards");
14474
+ const files = await getAllFiles3(standardsDir);
14475
+ const preview = [];
14476
+ for (const file of files) {
14477
+ try {
14478
+ const content = await fs6.readFile(file, "utf-8");
14479
+ for (const [placeholder, value] of Object.entries(replacements)) {
14480
+ if (content.includes(placeholder)) {
14481
+ preview.push({
14482
+ file: path7.relative(claudePath, file),
14483
+ placeholder,
14484
+ value
14485
+ });
14486
+ }
14487
+ }
14488
+ } catch {
14489
+ }
14490
+ }
14491
+ return preview;
14492
+ }
14493
+ function formatStandardsReport(report) {
14494
+ const lines = [];
14495
+ lines.push("Standards Configuration Applied");
14496
+ lines.push("\u2500".repeat(40));
14497
+ lines.push(`Files modified: ${report.modifiedFiles.length}`);
14498
+ lines.push(`Placeholders replaced: ${report.replacedPlaceholders.length}`);
14499
+ if (report.modifiedFiles.length > 0) {
14500
+ lines.push("");
14501
+ lines.push("Modified files:");
14502
+ for (const file of report.modifiedFiles) {
14503
+ lines.push(` \u2713 ${file}`);
14504
+ }
14505
+ }
14506
+ if (report.unusedPlaceholders.length > 0) {
14507
+ lines.push("");
14508
+ lines.push("Unused placeholders (no matching templates):");
14509
+ for (const p of report.unusedPlaceholders.slice(0, 5)) {
14510
+ lines.push(` - ${p}`);
14511
+ }
14512
+ if (report.unusedPlaceholders.length > 5) {
14513
+ lines.push(` ... and ${report.unusedPlaceholders.length - 5} more`);
14514
+ }
14515
+ }
14516
+ if (report.errors.length > 0) {
14517
+ lines.push("");
14518
+ lines.push("Errors:");
14519
+ for (const error of report.errors) {
14520
+ lines.push(` \u2717 ${error}`);
14521
+ }
14522
+ }
14523
+ return lines.join("\n");
14524
+ }
14525
+
14526
+ // src/lib/standards/scanner.ts
14527
+ init_cjs_shims();
14528
+ var fs7 = __toESM(require("fs/promises"), 1);
14529
+ var path8 = __toESM(require("path"), 1);
14530
+ var SCANNABLE_EXTENSIONS2 = [".md", ".json", ".yaml", ".yml", ".txt"];
14531
+ var PLACEHOLDER_PATTERN = /\{\{([A-Z_]+)\}\}/g;
14532
+ async function getScanableFiles(dir) {
14533
+ const files = [];
14534
+ try {
14535
+ const entries = await fs7.readdir(dir, { withFileTypes: true });
14536
+ for (const entry of entries) {
14537
+ const fullPath = path8.join(dir, entry.name);
14538
+ if (entry.isDirectory()) {
14539
+ const subFiles = await getScanableFiles(fullPath);
14540
+ files.push(...subFiles);
14541
+ } else if (entry.isFile()) {
14542
+ const ext = path8.extname(entry.name).toLowerCase();
14543
+ if (SCANNABLE_EXTENSIONS2.includes(ext)) {
14544
+ files.push(fullPath);
14545
+ }
14546
+ }
14547
+ }
14548
+ } catch {
14549
+ }
14550
+ return files;
14551
+ }
14552
+ function extractPlaceholders2(content) {
14553
+ const matches = content.match(PLACEHOLDER_PATTERN);
14554
+ return matches ? [...new Set(matches)] : [];
14555
+ }
14556
+ async function scanStandardsPlaceholders(claudePath, config) {
14557
+ const standardsDir = path8.join(claudePath, "docs", "standards");
14558
+ const files = await getScanableFiles(standardsDir);
14559
+ const placeholdersByFile = /* @__PURE__ */ new Map();
14560
+ const allPlaceholders = /* @__PURE__ */ new Set();
14561
+ for (const file of files) {
14562
+ try {
14563
+ const content = await fs7.readFile(file, "utf-8");
14564
+ const placeholders = extractPlaceholders2(content);
14565
+ if (placeholders.length > 0) {
14566
+ const relPath = path8.relative(claudePath, file);
14567
+ placeholdersByFile.set(relPath, placeholders);
14568
+ for (const p of placeholders) {
14569
+ allPlaceholders.add(p);
14570
+ }
14571
+ }
14572
+ } catch {
14573
+ }
14574
+ }
14575
+ const configuredPlaceholders = config ? new Set(Object.keys(flattenStandardsConfig(config))) : /* @__PURE__ */ new Set();
14576
+ const unconfigured = /* @__PURE__ */ new Map();
14577
+ for (const placeholder of allPlaceholders) {
14578
+ if (!configuredPlaceholders.has(placeholder)) {
14579
+ const filesWithPlaceholder = [];
14580
+ for (const [file, placeholders] of placeholdersByFile) {
14581
+ if (placeholders.includes(placeholder)) {
14582
+ filesWithPlaceholder.push(file);
14583
+ }
14584
+ }
14585
+ unconfigured.set(placeholder, filesWithPlaceholder);
14586
+ }
14587
+ }
14588
+ return {
14589
+ unconfiguredPlaceholders: Array.from(unconfigured.entries()).map(([placeholder, files2]) => ({
14590
+ placeholder,
14591
+ files: files2
14592
+ })),
14593
+ totalPlaceholders: allPlaceholders.size,
14594
+ configuredPlaceholders: configuredPlaceholders.size
14595
+ };
14596
+ }
14597
+ function formatScanResult(result) {
14598
+ const lines = [];
14599
+ lines.push("Standards Placeholder Scan");
14600
+ lines.push("\u2500".repeat(40));
14601
+ lines.push(`Total placeholders found: ${result.totalPlaceholders}`);
14602
+ lines.push(`Already configured: ${result.configuredPlaceholders}`);
14603
+ lines.push(`Unconfigured: ${result.unconfiguredPlaceholders.length}`);
14604
+ if (result.unconfiguredPlaceholders.length > 0) {
14605
+ lines.push("");
14606
+ lines.push("Unconfigured placeholders:");
14607
+ for (const { placeholder, files } of result.unconfiguredPlaceholders) {
14608
+ lines.push(` ${placeholder}`);
14609
+ for (const file of files.slice(0, 3)) {
14610
+ lines.push(` \u2514\u2500 ${file}`);
14611
+ }
14612
+ if (files.length > 3) {
14613
+ lines.push(` \u2514\u2500 ... and ${files.length - 3} more files`);
14614
+ }
14615
+ }
14616
+ } else {
14617
+ lines.push("");
14618
+ lines.push("\u2713 All placeholders are configured!");
14619
+ }
14620
+ return lines.join("\n");
14621
+ }
14622
+
14623
+ // src/lib/standards/template-sync.ts
14624
+ init_cjs_shims();
14625
+ var fs8 = __toESM(require("fs/promises"), 1);
14626
+ var path9 = __toESM(require("path"), 1);
14627
+ var import_ora4 = __toESM(require("ora"), 1);
14628
+ var STANDARDS_TEMPLATES = [
14629
+ "code-standards.md",
14630
+ "testing-standards.md",
14631
+ "documentation-standards.md",
14632
+ "design-standards.md",
14633
+ "security-standards.md",
14634
+ "performance-standards.md"
14635
+ ];
14636
+ async function fileExists2(filePath) {
14637
+ try {
14638
+ await fs8.access(filePath);
14639
+ return true;
14640
+ } catch {
14641
+ return false;
14642
+ }
14643
+ }
14644
+ async function createBackup(filePath) {
14645
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
14646
+ const backupPath = `${filePath}.backup-${timestamp}`;
14647
+ await fs8.copyFile(filePath, backupPath);
14648
+ return backupPath;
14649
+ }
14650
+ async function hasPlaceholders(filePath) {
14651
+ try {
14652
+ const content = await fs8.readFile(filePath, "utf-8");
14653
+ return content.includes("AUTO-GENERATED: Configured values") || /\{\{[A-Z_]+\}\}/.test(content);
14654
+ } catch {
14655
+ return false;
14656
+ }
14657
+ }
14658
+ async function syncTemplate(templateName, sourcePath, targetPath, options) {
14659
+ const sourceFile = path9.join(sourcePath, templateName);
14660
+ const targetFile = path9.join(targetPath, templateName);
14661
+ if (!await fileExists2(sourceFile)) {
14662
+ return { status: "skipped" };
14663
+ }
14664
+ const targetExists = await fileExists2(targetFile);
14665
+ if (targetExists) {
14666
+ const alreadyHasPlaceholders = await hasPlaceholders(targetFile);
14667
+ if (alreadyHasPlaceholders && !options.overwrite) {
14668
+ return { status: "skipped" };
14669
+ }
14670
+ let backupPath;
14671
+ if (options.backup) {
14672
+ backupPath = await createBackup(targetFile);
14673
+ }
14674
+ await fs8.copyFile(sourceFile, targetFile);
14675
+ return { status: "updated", backup: backupPath };
14676
+ }
14677
+ await fs8.mkdir(path9.dirname(targetFile), { recursive: true });
14678
+ await fs8.copyFile(sourceFile, targetFile);
14679
+ return { status: "created" };
14680
+ }
14681
+ async function syncStandardsTemplates(claudePath, options = {}) {
14682
+ const result = {
14683
+ created: [],
14684
+ updated: [],
14685
+ skipped: [],
14686
+ errors: []
14687
+ };
14688
+ const packagesTemplatesPath = path9.join(getTemplatesPath(), "docs", "standards");
14689
+ const projectTemplatesPath = path9.join(claudePath, "docs", "standards");
14690
+ await fs8.mkdir(projectTemplatesPath, { recursive: true });
14691
+ for (const template of STANDARDS_TEMPLATES) {
14692
+ try {
14693
+ const syncResult = await syncTemplate(
14694
+ template,
14695
+ packagesTemplatesPath,
14696
+ projectTemplatesPath,
14697
+ options
14698
+ );
14699
+ switch (syncResult.status) {
14700
+ case "created":
14701
+ result.created.push(template);
14702
+ break;
14703
+ case "updated":
14704
+ result.updated.push(template);
14705
+ break;
14706
+ case "skipped":
14707
+ result.skipped.push(template);
14708
+ break;
14709
+ }
14710
+ } catch (error) {
14711
+ result.errors.push(`${template}: ${String(error)}`);
14712
+ }
14713
+ }
14714
+ return result;
14715
+ }
14716
+ async function syncStandardsTemplatesWithSpinner(claudePath, options = {}) {
14717
+ const spinner2 = (0, import_ora4.default)("Syncing standards templates...").start();
14718
+ try {
14719
+ const result = await syncStandardsTemplates(claudePath, options);
14720
+ const total = result.created.length + result.updated.length;
14721
+ if (total > 0) {
14722
+ spinner2.succeed(
14723
+ `Synced ${total} template${total !== 1 ? "s" : ""} (${result.created.length} created, ${result.updated.length} updated)`
14724
+ );
14725
+ } else if (result.skipped.length > 0) {
14726
+ spinner2.info("All templates already up to date");
14727
+ } else {
14728
+ spinner2.warn("No templates to sync");
14729
+ }
14730
+ return result;
14731
+ } catch (error) {
14732
+ spinner2.fail("Failed to sync templates");
14733
+ throw error;
14734
+ }
14735
+ }
14736
+ async function checkTemplatesNeedUpdate(claudePath) {
14737
+ const projectTemplatesPath = path9.join(claudePath, "docs", "standards");
14738
+ const missing = [];
14739
+ const outdated = [];
14740
+ for (const template of STANDARDS_TEMPLATES) {
14741
+ const targetFile = path9.join(projectTemplatesPath, template);
14742
+ if (!await fileExists2(targetFile)) {
14743
+ missing.push(template);
14744
+ } else if (!await hasPlaceholders(targetFile)) {
14745
+ outdated.push(template);
14746
+ }
14747
+ }
14748
+ return {
14749
+ needsUpdate: missing.length > 0 || outdated.length > 0,
14750
+ missing,
14751
+ outdated
14752
+ };
14753
+ }
14754
+ function formatSyncResult(result) {
14755
+ const lines = [];
14756
+ lines.push("Template Sync Results");
14757
+ lines.push("\u2500".repeat(40));
14758
+ if (result.created.length > 0) {
14759
+ lines.push(`Created: ${result.created.length}`);
14760
+ for (const f of result.created) {
14761
+ lines.push(` \u2713 ${f}`);
14762
+ }
14763
+ }
14764
+ if (result.updated.length > 0) {
14765
+ lines.push(`Updated: ${result.updated.length}`);
14766
+ for (const f of result.updated) {
14767
+ lines.push(` \u2713 ${f}`);
14768
+ }
14769
+ }
14770
+ if (result.skipped.length > 0) {
14771
+ lines.push(`Skipped (already up to date): ${result.skipped.length}`);
14772
+ }
14773
+ if (result.errors.length > 0) {
14774
+ lines.push(`Errors: ${result.errors.length}`);
14775
+ for (const e of result.errors) {
14776
+ lines.push(` \u2717 ${e}`);
14777
+ }
14778
+ }
14779
+ return lines.join("\n");
14780
+ }
14781
+
14782
+ // src/cli/commands/standards.ts
14783
+ init_fs();
14784
+
14785
+ // src/cli/prompts/standards.ts
14786
+ init_cjs_shims();
14787
+ async function promptStandardsConfig(options) {
14788
+ logger.section("Project Standards", "\u{1F4D0}");
14789
+ logger.info("Configure quality standards for your project");
14790
+ logger.newline();
14791
+ if (options?.category) {
14792
+ const existingConfig = options.defaults ?? DEFAULT_STANDARDS_CONFIG;
14793
+ const categoryConfig = await promptCategoryConfig(options.category, existingConfig);
14794
+ return {
14795
+ ...existingConfig,
14796
+ [options.category]: categoryConfig
14797
+ };
14798
+ }
14799
+ const enableStandards = await confirm({
14800
+ message: "Would you like to configure project standards?",
14801
+ default: true
14802
+ });
14803
+ if (!enableStandards) {
14804
+ return DEFAULT_STANDARDS_CONFIG;
14805
+ }
14806
+ const preset = await promptStandardsPreset();
14807
+ if (preset !== "custom") {
14808
+ const presetConfig = STANDARDS_PRESETS[preset];
14809
+ logger.success(`Using "${presetConfig.name}" preset`);
14810
+ return presetConfig.config;
14811
+ }
14812
+ logger.newline();
14813
+ logger.info("Configure each standards category:");
14814
+ const codeConfig = await promptCodeStandards(options?.defaults?.code);
14815
+ const testingConfig = await promptTestingStandards(options?.defaults?.testing);
14816
+ const documentationConfig = await promptDocumentationStandards(options?.defaults?.documentation);
14817
+ const designConfig = await promptDesignStandards(options?.defaults?.design);
14818
+ const securityConfig = await promptSecurityStandards(options?.defaults?.security);
14819
+ const performanceConfig = await promptPerformanceStandards(options?.defaults?.performance);
14820
+ return {
14821
+ code: codeConfig,
14822
+ testing: testingConfig,
14823
+ documentation: documentationConfig,
14824
+ design: designConfig,
14825
+ security: securityConfig,
14826
+ performance: performanceConfig
14827
+ };
14828
+ }
14829
+ async function promptStandardsPreset() {
14830
+ return select({
14831
+ message: "Choose a standards preset:",
14832
+ choices: Object.entries(STANDARDS_PRESETS).map(([key, preset]) => ({
14833
+ name: `${preset.name} - ${preset.description}`,
14834
+ value: key
14835
+ })),
14836
+ default: "balanced"
14837
+ });
14838
+ }
14839
+ async function promptCategoryConfig(category, existingConfig) {
14840
+ switch (category) {
14841
+ case "code":
14842
+ return promptCodeStandards(existingConfig.code);
14843
+ case "testing":
14844
+ return promptTestingStandards(existingConfig.testing);
14845
+ case "documentation":
14846
+ return promptDocumentationStandards(existingConfig.documentation);
14847
+ case "design":
14848
+ return promptDesignStandards(existingConfig.design);
14849
+ case "security":
14850
+ return promptSecurityStandards(existingConfig.security);
14851
+ case "performance":
14852
+ return promptPerformanceStandards(existingConfig.performance);
14853
+ }
14854
+ }
14855
+ async function promptCodeStandards(defaults) {
14856
+ const def = STANDARDS_DEFINITIONS.code;
14857
+ logger.newline();
14858
+ logger.subtitle(`${def.icon} ${def.name}`);
14859
+ const indentStyle = await select({
14860
+ message: "Indent style:",
14861
+ choices: [
14862
+ { name: "Spaces", value: "space" },
14863
+ { name: "Tabs", value: "tab" }
14864
+ ],
14865
+ default: defaults?.indentStyle ?? DEFAULT_STANDARDS_CONFIG.code.indentStyle
14866
+ });
14867
+ const indentSize = await select({
14868
+ message: "Indent size:",
14869
+ choices: [
14870
+ { name: "2 spaces", value: 2 },
14871
+ { name: "4 spaces", value: 4 }
14872
+ ],
14873
+ default: defaults?.indentSize ?? DEFAULT_STANDARDS_CONFIG.code.indentSize
14874
+ });
14875
+ const maxLineLength = await select({
14876
+ message: "Max line length:",
14877
+ choices: [
14878
+ { name: "80 characters", value: 80 },
14879
+ { name: "100 characters", value: 100 },
14880
+ { name: "120 characters", value: 120 }
14881
+ ],
14882
+ default: defaults?.maxLineLength ?? DEFAULT_STANDARDS_CONFIG.code.maxLineLength
14883
+ });
14884
+ const maxFileLines = await select({
14885
+ message: "Max file lines:",
14886
+ choices: [
14887
+ { name: "300 lines (strict)", value: 300 },
14888
+ { name: "500 lines (standard)", value: 500 },
14889
+ { name: "800 lines (relaxed)", value: 800 }
14890
+ ],
14891
+ default: defaults?.maxFileLines ?? DEFAULT_STANDARDS_CONFIG.code.maxFileLines
14892
+ });
14893
+ const quoteStyle = await select({
14894
+ message: "Quote style:",
14895
+ choices: [
14896
+ { name: "Single quotes", value: "single" },
14897
+ { name: "Double quotes", value: "double" }
14898
+ ],
14899
+ default: defaults?.quoteStyle ?? DEFAULT_STANDARDS_CONFIG.code.quoteStyle
14900
+ });
14901
+ const semicolons = await confirm({
14902
+ message: "Use semicolons?",
14903
+ default: defaults?.semicolons ?? DEFAULT_STANDARDS_CONFIG.code.semicolons
14904
+ });
14905
+ const trailingCommas = await select({
14906
+ message: "Trailing commas:",
14907
+ choices: [
14908
+ { name: "ES5 (recommended)", value: "es5" },
14909
+ { name: "All", value: "all" },
14910
+ { name: "None", value: "none" }
14911
+ ],
14912
+ default: defaults?.trailingCommas ?? DEFAULT_STANDARDS_CONFIG.code.trailingCommas
14913
+ });
14914
+ const allowAny = await confirm({
14915
+ message: 'Allow "any" type in TypeScript?',
14916
+ default: defaults?.allowAny ?? DEFAULT_STANDARDS_CONFIG.code.allowAny
14917
+ });
14918
+ const namedExportsOnly = await confirm({
14919
+ message: "Require named exports only (no default exports)?",
14920
+ default: defaults?.namedExportsOnly ?? DEFAULT_STANDARDS_CONFIG.code.namedExportsOnly
14921
+ });
14922
+ const roroPattern = await confirm({
14923
+ message: "Require RO-RO pattern (Receive Object, Return Object)?",
14924
+ default: defaults?.roroPattern ?? DEFAULT_STANDARDS_CONFIG.code.roroPattern
14925
+ });
14926
+ const jsDocRequired = await confirm({
14927
+ message: "Require JSDoc for all exports?",
14928
+ default: defaults?.jsDocRequired ?? DEFAULT_STANDARDS_CONFIG.code.jsDocRequired
14929
+ });
14930
+ return {
14931
+ indentStyle,
14932
+ indentSize,
14933
+ maxLineLength,
14934
+ maxFileLines,
14935
+ quoteStyle,
14936
+ semicolons,
14937
+ trailingCommas,
14938
+ allowAny,
14939
+ namedExportsOnly,
14940
+ roroPattern,
14941
+ jsDocRequired
14942
+ };
14943
+ }
14944
+ async function promptTestingStandards(defaults) {
14945
+ const def = STANDARDS_DEFINITIONS.testing;
14946
+ logger.newline();
14947
+ logger.subtitle(`${def.icon} ${def.name}`);
14948
+ const coverageTarget = await select({
14949
+ message: "Minimum code coverage:",
14950
+ choices: [
14951
+ { name: "60% (startup)", value: 60 },
14952
+ { name: "70% (relaxed)", value: 70 },
14953
+ { name: "80% (standard)", value: 80 },
14954
+ { name: "90% (strict)", value: 90 },
14955
+ { name: "95% (enterprise)", value: 95 }
14956
+ ],
14957
+ default: defaults?.coverageTarget ?? DEFAULT_STANDARDS_CONFIG.testing.coverageTarget
14958
+ });
14959
+ const tddRequired = await confirm({
14960
+ message: "Require TDD methodology (Red-Green-Refactor)?",
14961
+ default: defaults?.tddRequired ?? DEFAULT_STANDARDS_CONFIG.testing.tddRequired
14962
+ });
14963
+ const testPattern = await select({
14964
+ message: "Test pattern:",
14965
+ choices: [
14966
+ { name: "AAA (Arrange-Act-Assert)", value: "aaa" },
14967
+ { name: "GWT (Given-When-Then)", value: "gwt" }
14968
+ ],
14969
+ default: defaults?.testPattern ?? DEFAULT_STANDARDS_CONFIG.testing.testPattern
14970
+ });
14971
+ const testLocation = await select({
14972
+ message: "Test file location:",
14973
+ choices: [
14974
+ { name: "Separate (test/ folder)", value: "separate" },
14975
+ { name: "Colocated (__tests__ near source)", value: "colocated" }
14976
+ ],
14977
+ default: defaults?.testLocation ?? DEFAULT_STANDARDS_CONFIG.testing.testLocation
14978
+ });
14979
+ const unitTestMaxMs = await select({
14980
+ message: "Max time per unit test:",
14981
+ choices: [
14982
+ { name: "50ms (fast)", value: 50 },
14983
+ { name: "100ms (standard)", value: 100 },
14984
+ { name: "200ms (relaxed)", value: 200 }
14985
+ ],
14986
+ default: defaults?.unitTestMaxMs ?? DEFAULT_STANDARDS_CONFIG.testing.unitTestMaxMs
14987
+ });
14988
+ const integrationTestMaxMs = await select({
14989
+ message: "Max time per integration test:",
14990
+ choices: [
14991
+ { name: "500ms (fast)", value: 500 },
14992
+ { name: "1000ms (standard)", value: 1e3 },
14993
+ { name: "2000ms (relaxed)", value: 2e3 }
14994
+ ],
14995
+ default: defaults?.integrationTestMaxMs ?? DEFAULT_STANDARDS_CONFIG.testing.integrationTestMaxMs
14996
+ });
14997
+ return {
14998
+ coverageTarget,
14999
+ tddRequired,
15000
+ testPattern,
15001
+ testLocation,
15002
+ unitTestMaxMs,
15003
+ integrationTestMaxMs
15004
+ };
15005
+ }
15006
+ async function promptDocumentationStandards(defaults) {
15007
+ const def = STANDARDS_DEFINITIONS.documentation;
15008
+ logger.newline();
15009
+ logger.subtitle(`${def.icon} ${def.name}`);
15010
+ const jsDocLevel = await select({
15011
+ message: "JSDoc detail level:",
15012
+ choices: [
15013
+ { name: "Minimal (brief description)", value: "minimal" },
15014
+ { name: "Standard (description + params + returns)", value: "standard" },
15015
+ { name: "Comprehensive (full docs with examples)", value: "comprehensive" }
15016
+ ],
15017
+ default: defaults?.jsDocLevel ?? DEFAULT_STANDARDS_CONFIG.documentation.jsDocLevel
15018
+ });
15019
+ const requireExamples = await confirm({
15020
+ message: "Require @example in JSDoc?",
15021
+ default: defaults?.requireExamples ?? DEFAULT_STANDARDS_CONFIG.documentation.requireExamples
15022
+ });
15023
+ const changelogFormat = await select({
15024
+ message: "Changelog format:",
15025
+ choices: [
15026
+ { name: "Conventional (auto-generated from commits)", value: "conventional" },
15027
+ { name: "Keep a Changelog (manual, semantic)", value: "keepachangelog" }
15028
+ ],
15029
+ default: defaults?.changelogFormat ?? DEFAULT_STANDARDS_CONFIG.documentation.changelogFormat
15030
+ });
15031
+ const inlineCommentPolicy = await select({
15032
+ message: "Inline comment policy:",
15033
+ choices: [
15034
+ { name: "Why not What (explain reasoning)", value: "why-not-what" },
15035
+ { name: "Minimal (only when necessary)", value: "minimal" },
15036
+ { name: "Extensive (comment thoroughly)", value: "extensive" }
15037
+ ],
15038
+ default: defaults?.inlineCommentPolicy ?? DEFAULT_STANDARDS_CONFIG.documentation.inlineCommentPolicy
15039
+ });
15040
+ return {
15041
+ jsDocLevel,
15042
+ requireExamples,
15043
+ changelogFormat,
15044
+ inlineCommentPolicy
15045
+ };
15046
+ }
15047
+ async function promptDesignStandards(defaults) {
15048
+ const def = STANDARDS_DEFINITIONS.design;
15049
+ logger.newline();
15050
+ logger.subtitle(`${def.icon} ${def.name}`);
15051
+ const cssFramework = await select({
15052
+ message: "CSS framework:",
15053
+ choices: [
15054
+ { name: "Tailwind CSS", value: "tailwind" },
15055
+ { name: "CSS Modules", value: "css-modules" },
15056
+ { name: "Styled Components", value: "styled-components" },
15057
+ { name: "Vanilla CSS", value: "vanilla" }
15058
+ ],
15059
+ default: defaults?.cssFramework ?? DEFAULT_STANDARDS_CONFIG.design.cssFramework
15060
+ });
15061
+ const componentLibrary = await select({
15062
+ message: "Component library:",
15063
+ choices: [
15064
+ { name: "shadcn/ui", value: "shadcn" },
15065
+ { name: "Radix UI", value: "radix" },
15066
+ { name: "Headless UI", value: "headless" },
15067
+ { name: "None", value: "none" }
15068
+ ],
15069
+ default: defaults?.componentLibrary ?? DEFAULT_STANDARDS_CONFIG.design.componentLibrary
15070
+ });
15071
+ const accessibilityLevel = await select({
15072
+ message: "WCAG accessibility level:",
15073
+ choices: [
15074
+ { name: "Level A (minimum)", value: "A" },
15075
+ { name: "Level AA (recommended)", value: "AA" },
15076
+ { name: "Level AAA (highest)", value: "AAA" }
15077
+ ],
15078
+ default: defaults?.accessibilityLevel ?? DEFAULT_STANDARDS_CONFIG.design.accessibilityLevel
15079
+ });
15080
+ const darkModeSupport = await confirm({
15081
+ message: "Support dark mode?",
15082
+ default: defaults?.darkModeSupport ?? DEFAULT_STANDARDS_CONFIG.design.darkModeSupport
15083
+ });
15084
+ return {
15085
+ cssFramework,
15086
+ componentLibrary,
15087
+ accessibilityLevel,
15088
+ darkModeSupport
15089
+ };
15090
+ }
15091
+ async function promptSecurityStandards(defaults) {
15092
+ const def = STANDARDS_DEFINITIONS.security;
15093
+ logger.newline();
15094
+ logger.subtitle(`${def.icon} ${def.name}`);
15095
+ const authPattern = await select({
15096
+ message: "Authentication pattern:",
15097
+ choices: [
15098
+ { name: "JWT (JSON Web Tokens)", value: "jwt" },
15099
+ { name: "Session (server-side)", value: "session" },
15100
+ { name: "OAuth 2.0 / OIDC", value: "oauth" },
15101
+ { name: "None", value: "none" }
15102
+ ],
15103
+ default: defaults?.authPattern ?? DEFAULT_STANDARDS_CONFIG.security.authPattern
15104
+ });
15105
+ const inputValidation = await select({
15106
+ message: "Input validation library:",
15107
+ choices: [
15108
+ { name: "Zod (TypeScript-first)", value: "zod" },
15109
+ { name: "Yup (schema builder)", value: "yup" },
15110
+ { name: "Joi (data validation)", value: "joi" },
15111
+ { name: "Manual (custom)", value: "manual" }
15112
+ ],
15113
+ default: defaults?.inputValidation ?? DEFAULT_STANDARDS_CONFIG.security.inputValidation
15114
+ });
15115
+ const csrfProtection = await confirm({
15116
+ message: "Enable CSRF protection?",
15117
+ default: defaults?.csrfProtection ?? DEFAULT_STANDARDS_CONFIG.security.csrfProtection
15118
+ });
15119
+ const rateLimiting = await confirm({
15120
+ message: "Enable rate limiting?",
15121
+ default: defaults?.rateLimiting ?? DEFAULT_STANDARDS_CONFIG.security.rateLimiting
15122
+ });
15123
+ return {
15124
+ authPattern,
15125
+ inputValidation,
15126
+ csrfProtection,
15127
+ rateLimiting
15128
+ };
15129
+ }
15130
+ async function promptPerformanceStandards(defaults) {
15131
+ const def = STANDARDS_DEFINITIONS.performance;
15132
+ logger.newline();
15133
+ logger.subtitle(`${def.icon} ${def.name}`);
15134
+ const lcpTarget = await select({
15135
+ message: "LCP target (Largest Contentful Paint):",
15136
+ choices: [
15137
+ { name: "1500ms (excellent)", value: 1500 },
15138
+ { name: "2000ms (good)", value: 2e3 },
15139
+ { name: "2500ms (standard)", value: 2500 },
15140
+ { name: "4000ms (needs improvement)", value: 4e3 }
15141
+ ],
15142
+ default: defaults?.lcpTarget ?? DEFAULT_STANDARDS_CONFIG.performance.lcpTarget
15143
+ });
15144
+ const fidTarget = await select({
15145
+ message: "FID target (First Input Delay):",
15146
+ choices: [
15147
+ { name: "50ms (excellent)", value: 50 },
15148
+ { name: "100ms (good)", value: 100 },
15149
+ { name: "200ms (standard)", value: 200 },
15150
+ { name: "300ms (needs improvement)", value: 300 }
15151
+ ],
15152
+ default: defaults?.fidTarget ?? DEFAULT_STANDARDS_CONFIG.performance.fidTarget
15153
+ });
15154
+ const clsTarget = await select({
15155
+ message: "CLS target (Cumulative Layout Shift):",
15156
+ choices: [
15157
+ { name: "0.05 (excellent)", value: 0.05 },
15158
+ { name: "0.1 (good)", value: 0.1 },
15159
+ { name: "0.15 (standard)", value: 0.15 },
15160
+ { name: "0.25 (needs improvement)", value: 0.25 }
15161
+ ],
15162
+ default: defaults?.clsTarget ?? DEFAULT_STANDARDS_CONFIG.performance.clsTarget
15163
+ });
15164
+ const bundleSizeTargetKb = await select({
15165
+ message: "Bundle size target (KB):",
15166
+ choices: [
15167
+ { name: "100KB (strict)", value: 100 },
15168
+ { name: "150KB (good)", value: 150 },
15169
+ { name: "250KB (standard)", value: 250 },
15170
+ { name: "500KB (relaxed)", value: 500 }
15171
+ ],
15172
+ default: defaults?.bundleSizeTargetKb ?? DEFAULT_STANDARDS_CONFIG.performance.bundleSizeTargetKb
15173
+ });
15174
+ const apiResponseTargetMs = await select({
15175
+ message: "API response time target (ms):",
15176
+ choices: [
15177
+ { name: "100ms (fast)", value: 100 },
15178
+ { name: "200ms (good)", value: 200 },
15179
+ { name: "300ms (standard)", value: 300 },
15180
+ { name: "500ms (relaxed)", value: 500 }
15181
+ ],
15182
+ default: defaults?.apiResponseTargetMs ?? DEFAULT_STANDARDS_CONFIG.performance.apiResponseTargetMs
15183
+ });
15184
+ return {
15185
+ lcpTarget,
15186
+ fidTarget,
15187
+ clsTarget,
15188
+ bundleSizeTargetKb,
15189
+ apiResponseTargetMs
15190
+ };
15191
+ }
15192
+ function showStandardsSummary(config) {
15193
+ logger.newline();
15194
+ logger.subtitle("Standards Summary");
15195
+ logger.item(
15196
+ `Code: ${config.code.indentSize} ${config.code.indentStyle}s, ${config.code.quoteStyle} quotes`
15197
+ );
15198
+ logger.info(
15199
+ colors.muted(
15200
+ ` Max: ${config.code.maxLineLength} chars/line, ${config.code.maxFileLines} lines/file`
15201
+ )
15202
+ );
15203
+ logger.info(
15204
+ colors.muted(
15205
+ ` Rules: ${config.code.allowAny ? "any allowed" : "no any"}, ${config.code.namedExportsOnly ? "named exports" : "default exports"}`
15206
+ )
15207
+ );
15208
+ logger.item(
15209
+ `Testing: ${config.testing.coverageTarget}% coverage, ${config.testing.tddRequired ? "TDD required" : "TDD optional"}`
15210
+ );
15211
+ logger.info(
15212
+ colors.muted(
15213
+ ` Pattern: ${config.testing.testPattern.toUpperCase()}, Location: ${config.testing.testLocation}`
15214
+ )
15215
+ );
15216
+ logger.item(`Documentation: ${config.documentation.jsDocLevel} JSDoc`);
15217
+ logger.info(colors.muted(` Comments: ${config.documentation.inlineCommentPolicy}`));
15218
+ logger.item(`Design: ${config.design.cssFramework}, ${config.design.componentLibrary}`);
15219
+ logger.info(
15220
+ colors.muted(
15221
+ ` A11y: WCAG ${config.design.accessibilityLevel}, ${config.design.darkModeSupport ? "dark mode" : "no dark mode"}`
15222
+ )
15223
+ );
15224
+ logger.item(`Security: ${config.security.authPattern}, ${config.security.inputValidation}`);
15225
+ logger.info(
15226
+ colors.muted(
15227
+ ` ${config.security.csrfProtection ? "CSRF" : "no CSRF"}, ${config.security.rateLimiting ? "rate limiting" : "no rate limiting"}`
15228
+ )
15229
+ );
15230
+ logger.item(
15231
+ `Performance: LCP ${config.performance.lcpTarget}ms, FID ${config.performance.fidTarget}ms`
15232
+ );
15233
+ logger.info(
15234
+ colors.muted(
15235
+ ` Bundle: ${config.performance.bundleSizeTargetKb}KB, API: ${config.performance.apiResponseTargetMs}ms`
15236
+ )
15237
+ );
15238
+ }
15239
+ async function confirmStandardsConfig(config) {
15240
+ showStandardsSummary(config);
15241
+ logger.newline();
15242
+ return confirm({
15243
+ message: "Apply these standards?",
15244
+ default: true
15245
+ });
15246
+ }
15247
+
15248
+ // src/cli/commands/standards.ts
15249
+ function createStandardsCommand() {
15250
+ const cmd = new import_commander8.Command("standards").description("Configure project standards interactively").argument("[path]", "Project path (default: current directory)").option("--scan", "Scan for unconfigured standard placeholders").option(
15251
+ "-c, --category <name>",
15252
+ "Configure specific category (code|testing|documentation|design|security|performance)"
15253
+ ).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);
15254
+ return cmd;
15255
+ }
15256
+ async function runStandards(path10, options) {
15257
+ const projectPath = resolvePath(path10 || ".");
15258
+ const claudePath = joinPath(projectPath, ".claude");
15259
+ logger.configure({ verbose: options.verbose, silent: false });
15260
+ logger.title("Project Standards Configuration");
15261
+ try {
15262
+ if (options.scan) {
15263
+ await scanMode2(claudePath, projectPath);
15264
+ return;
15265
+ }
15266
+ if (options.updateTemplates) {
15267
+ await updateTemplatesMode(claudePath, options);
15268
+ return;
15269
+ }
15270
+ if (options.preview) {
15271
+ await previewMode2(claudePath, projectPath);
15272
+ return;
15273
+ }
15274
+ if (options.yes) {
15275
+ await defaultsMode(claudePath, projectPath, options);
15276
+ return;
15277
+ }
15278
+ await interactiveMode2(claudePath, projectPath, options);
15279
+ } catch (error) {
15280
+ logger.error(
15281
+ `Standards configuration failed: ${error instanceof Error ? error.message : error}`
15282
+ );
15283
+ process.exit(1);
15284
+ }
15285
+ }
15286
+ async function scanMode2(claudePath, projectPath) {
15287
+ logger.info(`Scanning ${colors.primary(claudePath)} for standard placeholders...`);
15288
+ logger.newline();
15289
+ const existingConfig = await readConfig(projectPath);
15290
+ const standardsConfig = existingConfig?.extras?.standards;
15291
+ const scanResult = await scanStandardsPlaceholders(claudePath, standardsConfig);
15292
+ logger.info(formatScanResult(scanResult));
15293
+ if (scanResult.unconfiguredPlaceholders.length > 0) {
15294
+ logger.newline();
15295
+ logger.info("Run `claude-config standards` to configure them");
15296
+ }
15297
+ }
15298
+ async function updateTemplatesMode(claudePath, options) {
15299
+ logger.info("Checking for template updates...");
15300
+ logger.newline();
15301
+ const status = await checkTemplatesNeedUpdate(claudePath);
15302
+ if (!status.needsUpdate) {
15303
+ logger.success("All templates are up to date!");
15304
+ return;
15305
+ }
15306
+ if (status.missing.length > 0) {
15307
+ logger.info(`Missing templates (${status.missing.length}):`);
15308
+ for (const template of status.missing) {
15309
+ logger.info(` ${colors.warning("+")} ${template}`);
15310
+ }
15311
+ logger.newline();
15312
+ }
15313
+ if (status.outdated.length > 0) {
15314
+ logger.info(`Outdated templates (${status.outdated.length}):`);
15315
+ for (const template of status.outdated) {
15316
+ logger.info(` ${colors.primary("~")} ${template}`);
15317
+ }
15318
+ logger.newline();
15319
+ }
15320
+ const result = await syncStandardsTemplatesWithSpinner(claudePath, {
15321
+ overwrite: true,
15322
+ backup: true
15323
+ });
15324
+ if (options.verbose) {
15325
+ logger.newline();
15326
+ logger.info(formatSyncResult(result));
15327
+ }
15328
+ logger.newline();
15329
+ logger.success("Templates updated successfully!");
15330
+ logger.info("Run `claude-config standards` to configure the new placeholders");
15331
+ }
15332
+ async function previewMode2(claudePath, projectPath) {
15333
+ const existingConfig = await readConfig(projectPath);
15334
+ if (!existingConfig?.extras?.standards) {
15335
+ logger.warn("No standards configuration found");
15336
+ logger.info("Run `claude-config standards` first to set up configuration");
15337
+ return;
15338
+ }
15339
+ logger.info("Preview of replacements:");
15340
+ logger.newline();
15341
+ const replacements = await previewStandardsReplacements(
15342
+ claudePath,
15343
+ existingConfig.extras.standards
15344
+ );
15345
+ if (replacements.length === 0) {
15346
+ logger.info("No standard placeholders to replace");
15347
+ return;
15348
+ }
15349
+ const byFile = {};
15350
+ for (const r of replacements) {
15351
+ if (!byFile[r.file]) {
15352
+ byFile[r.file] = [];
15353
+ }
15354
+ byFile[r.file].push({ placeholder: r.placeholder, value: r.value });
15355
+ }
15356
+ for (const [file, changes] of Object.entries(byFile)) {
15357
+ logger.subtitle(file);
15358
+ for (const change of changes) {
15359
+ logger.info(` ${change.placeholder} \u2192 ${colors.primary(change.value)}`);
15360
+ }
15361
+ logger.newline();
15362
+ }
15363
+ logger.success(
15364
+ `Total: ${replacements.length} replacements in ${Object.keys(byFile).length} files`
15365
+ );
15366
+ }
15367
+ async function defaultsMode(claudePath, projectPath, options) {
15368
+ const templateStatus = await checkTemplatesNeedUpdate(claudePath);
15369
+ if (templateStatus.needsUpdate) {
15370
+ logger.warn("Some templates are missing or outdated:");
15371
+ if (templateStatus.missing.length > 0) {
15372
+ logger.info(` Missing: ${templateStatus.missing.length} template(s)`);
15373
+ }
15374
+ if (templateStatus.outdated.length > 0) {
15375
+ logger.info(` Outdated: ${templateStatus.outdated.length} template(s)`);
15376
+ }
15377
+ logger.newline();
15378
+ logger.info(
15379
+ `Run ${colors.primary("qazuor-claude-config standards --update-templates")} first to sync templates`
15380
+ );
15381
+ logger.newline();
15382
+ }
15383
+ logger.info("Applying default standards configuration...");
15384
+ logger.newline();
15385
+ const standardsConfig = DEFAULT_STANDARDS_CONFIG;
15386
+ showStandardsSummary(standardsConfig);
15387
+ logger.newline();
15388
+ const report = await replaceStandardsWithSpinner(claudePath, standardsConfig);
15389
+ if (options.verbose) {
15390
+ logger.newline();
15391
+ logger.info(formatStandardsReport(report));
15392
+ }
15393
+ if (report.modifiedFiles.length === 0 && report.unusedPlaceholders.length > 0) {
15394
+ logger.newline();
15395
+ logger.warn("No placeholders were replaced in templates.");
15396
+ logger.info(
15397
+ `Your templates might be outdated. Run ${colors.primary("--update-templates")} to sync them.`
15398
+ );
15399
+ }
15400
+ await saveStandardsConfig(projectPath, standardsConfig);
15401
+ logger.newline();
15402
+ logger.success("Standards configuration complete!");
15403
+ }
15404
+ async function interactiveMode2(claudePath, projectPath, options) {
15405
+ const templateStatus = await checkTemplatesNeedUpdate(claudePath);
15406
+ if (templateStatus.needsUpdate) {
15407
+ logger.warn("Some templates are missing or outdated:");
15408
+ if (templateStatus.missing.length > 0) {
15409
+ logger.info(` Missing: ${templateStatus.missing.length} template(s)`);
15410
+ }
15411
+ if (templateStatus.outdated.length > 0) {
15412
+ logger.info(` Outdated: ${templateStatus.outdated.length} template(s)`);
15413
+ }
15414
+ logger.newline();
15415
+ logger.info(
15416
+ `Run ${colors.primary("qazuor-claude-config standards --update-templates")} first to sync templates`
15417
+ );
15418
+ logger.newline();
15419
+ }
15420
+ const existingConfig = await readConfig(projectPath);
15421
+ const existingStandards = existingConfig?.extras?.standards;
15422
+ const standardsConfig = await promptStandardsConfig({
15423
+ defaults: existingStandards,
15424
+ category: options.category
15425
+ });
15426
+ const confirmed = await confirmStandardsConfig(standardsConfig);
15427
+ if (!confirmed) {
15428
+ logger.warn("Configuration cancelled");
15429
+ return;
15430
+ }
15431
+ const report = await replaceStandardsWithSpinner(claudePath, standardsConfig);
15432
+ if (options.verbose) {
15433
+ logger.newline();
15434
+ logger.info(formatStandardsReport(report));
15435
+ }
15436
+ if (report.modifiedFiles.length === 0 && report.unusedPlaceholders.length > 0) {
15437
+ logger.newline();
15438
+ logger.warn("No placeholders were replaced in templates.");
15439
+ logger.info(
15440
+ `Your templates might be outdated. Run ${colors.primary("--update-templates")} to sync them.`
15441
+ );
15442
+ }
15443
+ await saveStandardsConfig(projectPath, standardsConfig);
15444
+ logger.newline();
15445
+ logger.success("Standards configuration complete!");
15446
+ }
15447
+ async function saveStandardsConfig(projectPath, standardsConfig) {
15448
+ const existingConfig = await readConfig(projectPath);
15449
+ if (existingConfig) {
15450
+ existingConfig.extras = {
15451
+ ...existingConfig.extras,
15452
+ standards: standardsConfig
15453
+ };
15454
+ existingConfig.customizations.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
15455
+ await writeConfig(projectPath, existingConfig);
15456
+ logger.success("Configuration saved to .claude/qazuor-claude-config.json");
15457
+ } else {
15458
+ logger.warn("No existing config found - standards not saved");
15459
+ logger.info("Run `claude-config init` first to initialize the project");
15460
+ }
15461
+ }
15462
+
13265
15463
  // src/cli/index.ts
13266
15464
  var VERSION2 = "0.1.0";
13267
- var program = new import_commander8.Command().name("qazuor-claude-config").description("CLI tool to install and manage Claude Code configurations").version(VERSION2);
15465
+ var program = new import_commander9.Command().name("qazuor-claude-config").description("CLI tool to install and manage Claude Code configurations").version(VERSION2);
13268
15466
  program.addCommand(createInitCommand());
13269
15467
  program.addCommand(createListCommand());
13270
15468
  program.addCommand(createAddCommand());
@@ -13272,6 +15470,7 @@ program.addCommand(createRemoveCommand());
13272
15470
  program.addCommand(createStatusCommand());
13273
15471
  program.addCommand(createUpdateCommand());
13274
15472
  program.addCommand(createConfigureCommand());
15473
+ program.addCommand(createStandardsCommand());
13275
15474
 
13276
15475
  // src/lib/utils/banner.ts
13277
15476
  init_cjs_shims();