@qazuor/claude-code-config 0.2.0 → 0.3.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.js CHANGED
@@ -7647,6 +7647,304 @@ function cleanup() {
7647
7647
  stopEscListener();
7648
7648
  }
7649
7649
 
7650
+ // src/lib/wizard/index.ts
7651
+ init_esm_shims();
7652
+
7653
+ // src/lib/wizard/step.ts
7654
+ init_esm_shims();
7655
+ function createStepState(definition, index) {
7656
+ return {
7657
+ metadata: {
7658
+ ...definition.metadata,
7659
+ index
7660
+ },
7661
+ status: index === 0 ? "current" : "pending",
7662
+ value: void 0,
7663
+ history: [],
7664
+ isModified: false
7665
+ };
7666
+ }
7667
+ function recordStepHistory(state, value, exitDirection) {
7668
+ const entry = {
7669
+ timestamp: /* @__PURE__ */ new Date(),
7670
+ value,
7671
+ exitDirection,
7672
+ visitCount: state.history.length + 1
7673
+ };
7674
+ return {
7675
+ ...state,
7676
+ value,
7677
+ history: [...state.history, entry],
7678
+ isModified: true
7679
+ };
7680
+ }
7681
+ function shouldSkipStep(definition, context) {
7682
+ return definition.skipCondition?.(context) ?? false;
7683
+ }
7684
+
7685
+ // src/lib/wizard/navigator.ts
7686
+ init_esm_shims();
7687
+ import { Separator } from "@inquirer/prompts";
7688
+ var BACK_OPTION_VALUE = "__wizard_back__";
7689
+ function calculateNextStep(state, direction) {
7690
+ const currentIndex = state.stepOrder.indexOf(state.currentStepId);
7691
+ const totalSteps = state.stepOrder.length;
7692
+ switch (direction) {
7693
+ case "next": {
7694
+ if (currentIndex >= totalSteps - 1) {
7695
+ return {
7696
+ nextStepId: null,
7697
+ currentStepNewStatus: "completed"
7698
+ };
7699
+ }
7700
+ return {
7701
+ nextStepId: state.stepOrder[currentIndex + 1],
7702
+ currentStepNewStatus: "completed"
7703
+ };
7704
+ }
7705
+ case "back": {
7706
+ if (currentIndex <= 0) {
7707
+ return {
7708
+ nextStepId: state.currentStepId,
7709
+ currentStepNewStatus: "current"
7710
+ };
7711
+ }
7712
+ return {
7713
+ nextStepId: state.stepOrder[currentIndex - 1],
7714
+ currentStepNewStatus: "pending"
7715
+ };
7716
+ }
7717
+ case "skip": {
7718
+ if (currentIndex >= totalSteps - 1) {
7719
+ return {
7720
+ nextStepId: null,
7721
+ currentStepNewStatus: "skipped"
7722
+ };
7723
+ }
7724
+ return {
7725
+ nextStepId: state.stepOrder[currentIndex + 1],
7726
+ currentStepNewStatus: "skipped"
7727
+ };
7728
+ }
7729
+ case "cancel": {
7730
+ return {
7731
+ nextStepId: null,
7732
+ currentStepNewStatus: "pending"
7733
+ };
7734
+ }
7735
+ }
7736
+ }
7737
+ function applyNavigation(state, direction, currentValue) {
7738
+ const { nextStepId, currentStepNewStatus } = calculateNextStep(state, direction);
7739
+ const currentStepState = state.steps[state.currentStepId];
7740
+ const updatedCurrentStep = {
7741
+ ...currentStepState,
7742
+ status: currentStepNewStatus,
7743
+ value: currentValue
7744
+ };
7745
+ const updatedSteps = {
7746
+ ...state.steps,
7747
+ [state.currentStepId]: updatedCurrentStep
7748
+ };
7749
+ if (nextStepId === null) {
7750
+ return {
7751
+ ...state,
7752
+ steps: updatedSteps,
7753
+ isComplete: direction === "next" || direction === "skip",
7754
+ isCancelled: direction === "cancel"
7755
+ };
7756
+ }
7757
+ const nextStepState = state.steps[nextStepId];
7758
+ const updatedNextStep = {
7759
+ ...nextStepState,
7760
+ status: "current"
7761
+ };
7762
+ return {
7763
+ ...state,
7764
+ steps: {
7765
+ ...updatedSteps,
7766
+ [nextStepId]: updatedNextStep
7767
+ },
7768
+ currentStepId: nextStepId
7769
+ };
7770
+ }
7771
+ async function promptKeepOrReconfigure(stepName) {
7772
+ logger.newline();
7773
+ const result = await select({
7774
+ message: `"${stepName}" was already configured. What would you like to do?`,
7775
+ choices: [
7776
+ {
7777
+ name: "Keep current values and continue",
7778
+ value: "keep",
7779
+ description: "Skip this step and proceed to the next one"
7780
+ },
7781
+ {
7782
+ name: "Re-configure this step",
7783
+ value: "reconfigure",
7784
+ description: "Show the prompts again with your previous values as defaults"
7785
+ }
7786
+ ],
7787
+ default: "keep"
7788
+ });
7789
+ return result;
7790
+ }
7791
+ function showStepProgress(state, isRevisit) {
7792
+ if (!state.metadata.showProgress) {
7793
+ return;
7794
+ }
7795
+ const currentIndex = state.stepOrder.indexOf(state.currentStepId);
7796
+ const total = state.metadata.totalSteps;
7797
+ const currentStep = state.steps[state.currentStepId];
7798
+ const progressChars = state.stepOrder.map((_stepId, idx) => {
7799
+ if (idx < currentIndex) {
7800
+ return colors.success("\u25CF");
7801
+ }
7802
+ if (idx === currentIndex) {
7803
+ return colors.primary("\u25C9");
7804
+ }
7805
+ return colors.muted("\u25CB");
7806
+ });
7807
+ const progressBar = progressChars.join(" ");
7808
+ const stepIndicator = `[${currentIndex + 1}/${total}]`;
7809
+ const revisitBadge = isRevisit ? colors.warning(" (revisiting)") : "";
7810
+ logger.newline();
7811
+ logger.info(`${colors.muted(stepIndicator)} ${progressBar}${revisitBadge}`);
7812
+ logger.subtitle(currentStep.metadata.name);
7813
+ if (currentStep.metadata.description) {
7814
+ logger.info(colors.muted(currentStep.metadata.description));
7815
+ }
7816
+ }
7817
+
7818
+ // src/lib/wizard/history.ts
7819
+ init_esm_shims();
7820
+ function getLastValue(stepState) {
7821
+ if (stepState.history.length === 0) {
7822
+ return stepState.value;
7823
+ }
7824
+ return stepState.history[stepState.history.length - 1].value;
7825
+ }
7826
+ function wasStepCompleted(stepState) {
7827
+ return stepState.status === "completed" || stepState.history.length > 0;
7828
+ }
7829
+
7830
+ // src/lib/wizard/engine.ts
7831
+ init_esm_shims();
7832
+ function createWizardState(config) {
7833
+ const stepOrder = config.steps.map((s) => s.id);
7834
+ const steps = {};
7835
+ for (let i = 0; i < config.steps.length; i++) {
7836
+ const step = config.steps[i];
7837
+ steps[step.id] = createStepState(step.definition, i);
7838
+ }
7839
+ const metadata = {
7840
+ id: config.id,
7841
+ title: config.title,
7842
+ totalSteps: config.steps.length,
7843
+ startTime: /* @__PURE__ */ new Date(),
7844
+ allowSkip: config.allowSkip ?? true,
7845
+ showProgress: config.showProgress ?? true
7846
+ };
7847
+ return {
7848
+ steps,
7849
+ currentStepId: stepOrder[0],
7850
+ stepOrder,
7851
+ isComplete: false,
7852
+ isCancelled: false,
7853
+ metadata
7854
+ };
7855
+ }
7856
+ async function runWizard(config, initialContext = {}) {
7857
+ let state = createWizardState(config);
7858
+ const stepMap = /* @__PURE__ */ new Map();
7859
+ for (const step of config.steps) {
7860
+ stepMap.set(step.id, step.definition);
7861
+ }
7862
+ let context = { ...initialContext };
7863
+ let movingForwardAfterBack = false;
7864
+ while (!state.isComplete && !state.isCancelled) {
7865
+ const currentStepId = state.currentStepId;
7866
+ const stepDef = stepMap.get(currentStepId);
7867
+ if (!stepDef) {
7868
+ throw new Error(`Step definition not found for: ${currentStepId}`);
7869
+ }
7870
+ const stepState = state.steps[currentStepId];
7871
+ if (shouldSkipStep(stepDef, context)) {
7872
+ state = applyNavigation(state, "skip", void 0);
7873
+ continue;
7874
+ }
7875
+ const previouslyCompleted = wasStepCompleted(stepState);
7876
+ if (movingForwardAfterBack && previouslyCompleted) {
7877
+ const action = await promptKeepOrReconfigure(stepState.metadata.name);
7878
+ if (action === "keep") {
7879
+ state = applyNavigation(state, "next", stepState.value);
7880
+ continue;
7881
+ }
7882
+ }
7883
+ const isRevisit = stepState.history.length > 0;
7884
+ showStepProgress(state, isRevisit);
7885
+ const previousValue = getLastValue(stepState);
7886
+ const defaults = previousValue ?? stepDef.computeDefaults(context);
7887
+ const result = await stepDef.execute(context, defaults);
7888
+ if (result.navigation !== "back") {
7889
+ context = {
7890
+ ...context,
7891
+ [currentStepId]: result.value
7892
+ };
7893
+ const updatedStepState = recordStepHistory(stepState, result.value, result.navigation);
7894
+ state = {
7895
+ ...state,
7896
+ steps: {
7897
+ ...state.steps,
7898
+ [currentStepId]: updatedStepState
7899
+ }
7900
+ };
7901
+ }
7902
+ if (result.navigation === "back") {
7903
+ movingForwardAfterBack = false;
7904
+ } else if (result.navigation === "next") {
7905
+ movingForwardAfterBack = isRevisit || movingForwardAfterBack;
7906
+ }
7907
+ state = applyNavigation(state, result.navigation, result.value);
7908
+ }
7909
+ const values = {};
7910
+ for (const stepId of state.stepOrder) {
7911
+ const step = state.steps[stepId];
7912
+ if (step.value !== void 0) {
7913
+ values[stepId] = step.value;
7914
+ }
7915
+ }
7916
+ return {
7917
+ values,
7918
+ state,
7919
+ cancelled: state.isCancelled
7920
+ };
7921
+ }
7922
+ function showWizardSummary(state) {
7923
+ const completed = state.stepOrder.filter((id) => {
7924
+ const step = state.steps[id];
7925
+ return step.status === "completed";
7926
+ }).length;
7927
+ const skipped = state.stepOrder.filter((id) => {
7928
+ const step = state.steps[id];
7929
+ return step.status === "skipped";
7930
+ }).length;
7931
+ const revisited = state.stepOrder.filter((id) => {
7932
+ const step = state.steps[id];
7933
+ return step.history.length > 1;
7934
+ }).length;
7935
+ logger.newline();
7936
+ logger.success(`Wizard completed: ${completed} steps configured`);
7937
+ if (skipped > 0) {
7938
+ logger.info(` ${skipped} steps skipped`);
7939
+ }
7940
+ if (revisited > 0) {
7941
+ logger.info(` ${revisited} steps were reconfigured`);
7942
+ }
7943
+ }
7944
+
7945
+ // src/lib/wizard/init-steps.ts
7946
+ init_esm_shims();
7947
+
7650
7948
  // src/cli/prompts/bundle-select.ts
7651
7949
  init_esm_shims();
7652
7950
 
@@ -8086,111 +8384,541 @@ async function promptCICDConfig(options) {
8086
8384
  };
8087
8385
  }
8088
8386
 
8089
- // src/cli/prompts/folder-preferences.ts
8090
- init_esm_shims();
8091
-
8092
- // src/constants/folder-preferences.ts
8387
+ // src/cli/prompts/code-style.ts
8093
8388
  init_esm_shims();
8094
- var DEFAULT_FOLDER_PREFERENCES = {
8095
- tests: {
8096
- location: "test-folder-root",
8097
- pattern: "*.test.ts"
8098
- },
8099
- planning: {
8100
- location: "claude-sessions",
8101
- commitToGit: false
8102
- },
8103
- docs: {
8104
- location: "docs-root"
8105
- },
8106
- cicd: {
8107
- location: "github-workflows",
8108
- workflows: []
8109
- }
8110
- };
8111
- var TEST_LOCATION_OPTIONS = [
8112
- {
8113
- value: "colocated",
8114
- name: "Colocated with source files",
8115
- description: "Tests next to source files (e.g., src/utils/foo.ts \u2192 src/utils/foo.test.ts). Good for small projects."
8116
- },
8389
+ var CODE_STYLE_TOOLS = [
8117
8390
  {
8118
- value: "test-folder-root",
8119
- name: "Root test/ folder (Recommended)",
8120
- description: "Tests in test/ folder mirroring src structure (e.g., test/utils/foo.test.ts). Best for medium-large projects."
8391
+ name: "EditorConfig",
8392
+ value: "editorconfig",
8393
+ description: "Consistent coding styles across editors",
8394
+ checked: true
8121
8395
  },
8122
8396
  {
8123
- value: "test-folder-src",
8124
- name: "Inside src/__tests__/",
8125
- description: "Tests in src/__tests__/ folder (e.g., src/__tests__/utils/foo.test.ts). Common in Create React App."
8126
- }
8127
- ];
8128
- var PLANNING_LOCATION_OPTIONS = [
8129
- {
8130
- value: "claude-sessions",
8131
- name: ".claude/sessions/planning/ (Recommended)",
8132
- description: "Planning files in .claude/sessions/planning/. Keeps Claude-specific files organized."
8397
+ name: "Commitlint",
8398
+ value: "commitlint",
8399
+ description: "Lint commit messages (conventional commits)",
8400
+ checked: true
8133
8401
  },
8134
8402
  {
8135
- value: "docs-planning",
8136
- name: "docs/planning/",
8137
- description: "Planning files in docs/planning/. Good if you want planning docs alongside other docs."
8403
+ name: "Biome",
8404
+ value: "biome",
8405
+ description: "Fast linter and formatter (ESLint + Prettier alternative)",
8406
+ checked: false
8138
8407
  },
8139
8408
  {
8140
- value: "root-planning",
8141
- name: "planning/ at root",
8142
- description: "Planning files in planning/ at project root. Maximum visibility."
8409
+ name: "Prettier",
8410
+ value: "prettier",
8411
+ description: "Code formatter (use if not using Biome)",
8412
+ checked: false
8143
8413
  }
8144
8414
  ];
8145
- var DOCS_LOCATION_OPTIONS = [
8146
- {
8147
- value: "docs-root",
8148
- name: "docs/ at project root (Recommended)",
8149
- description: "Documentation in docs/ folder. Standard location for most projects."
8150
- },
8151
- {
8152
- value: "claude-docs",
8153
- name: ".claude/docs/",
8154
- description: "Documentation in .claude/docs/. Keeps all Claude-related files together."
8155
- },
8156
- {
8157
- value: "readme-only",
8158
- name: "README files only",
8159
- description: "Only README.md files, no separate docs folder. Good for simple projects."
8415
+ async function promptCodeStyleConfig(options) {
8416
+ logger.section("Code Style", "\u{1F3A8}");
8417
+ logger.info("Configure code formatting and linting tools");
8418
+ logger.newline();
8419
+ const enableCodeStyle = await confirm({
8420
+ message: "Would you like to install code style configuration files?",
8421
+ default: true
8422
+ });
8423
+ if (!enableCodeStyle) {
8424
+ return {
8425
+ enabled: false,
8426
+ editorconfig: false,
8427
+ commitlint: false,
8428
+ biome: false,
8429
+ prettier: false
8430
+ };
8160
8431
  }
8161
- ];
8162
- var TEST_PATTERN_OPTIONS = [
8163
- {
8164
- value: "*.test.ts",
8165
- name: "*.test.ts (Recommended)",
8166
- description: "Standard test pattern (foo.test.ts)"
8167
- },
8168
- {
8169
- value: "*.spec.ts",
8170
- name: "*.spec.ts",
8171
- description: "Spec pattern common in Angular (foo.spec.ts)"
8172
- },
8173
- {
8174
- value: "*.test.tsx",
8175
- name: "*.test.tsx",
8176
- description: "For React component tests (Component.test.tsx)"
8432
+ const selectedTools = await checkbox({
8433
+ message: "Select the tools to configure:",
8434
+ choices: CODE_STYLE_TOOLS.map((tool) => ({
8435
+ name: `${tool.name} - ${tool.description}`,
8436
+ value: tool.value,
8437
+ checked: options?.defaults?.[tool.value] ?? tool.checked
8438
+ }))
8439
+ });
8440
+ if (selectedTools.includes("biome") && selectedTools.includes("prettier")) {
8441
+ logger.warn("Note: Both Biome and Prettier selected. Biome can replace Prettier.");
8442
+ const keepBoth = await confirm({
8443
+ message: "Keep both? (Prettier may conflict with Biome)",
8444
+ default: false
8445
+ });
8446
+ if (!keepBoth) {
8447
+ const preferred = await select({
8448
+ message: "Which formatter would you prefer?",
8449
+ choices: [
8450
+ { name: "Biome (faster, all-in-one)", value: "biome" },
8451
+ { name: "Prettier (more plugins)", value: "prettier" }
8452
+ ]
8453
+ });
8454
+ const indexToRemove = preferred === "biome" ? selectedTools.indexOf("prettier") : selectedTools.indexOf("biome");
8455
+ if (indexToRemove > -1) {
8456
+ selectedTools.splice(indexToRemove, 1);
8457
+ }
8458
+ }
8177
8459
  }
8178
- ];
8179
- var GITHUB_WORKFLOW_TEMPLATES = [
8180
- // CI Workflows
8181
- {
8182
- id: "ci-node",
8183
- name: "Node.js CI",
8184
- description: "Run tests, lint, and type-check on push and PR",
8185
- filename: "ci.yml",
8186
- category: "ci",
8187
- suitedFor: ["node", "typescript", "javascript"],
8188
- recommended: true
8189
- },
8190
- {
8191
- id: "ci-node-matrix",
8192
- name: "Node.js CI (Matrix)",
8193
- description: "Test across multiple Node.js versions (18, 20, 22)",
8460
+ if (selectedTools.length === 0) {
8461
+ return {
8462
+ enabled: false,
8463
+ editorconfig: false,
8464
+ commitlint: false,
8465
+ biome: false,
8466
+ prettier: false
8467
+ };
8468
+ }
8469
+ const customizeSettings = await confirm({
8470
+ message: "Would you like to customize code style settings? (No = use standard defaults)",
8471
+ default: false
8472
+ });
8473
+ let editorconfigOptions;
8474
+ let biomeOptions;
8475
+ let prettierOptions;
8476
+ let commitlintOptions;
8477
+ if (customizeSettings) {
8478
+ const hasFormatter = selectedTools.includes("editorconfig") || selectedTools.includes("biome") || selectedTools.includes("prettier");
8479
+ if (hasFormatter) {
8480
+ const preset = await promptStylePreset();
8481
+ if (preset !== "custom") {
8482
+ const presetConfig = CODE_STYLE_PRESETS[preset];
8483
+ if (selectedTools.includes("editorconfig")) {
8484
+ editorconfigOptions = presetConfig.editorconfig;
8485
+ }
8486
+ if (selectedTools.includes("biome") && presetConfig.biome) {
8487
+ biomeOptions = {
8488
+ ...DEFAULT_BIOME_OPTIONS,
8489
+ formatter: presetConfig.biome
8490
+ };
8491
+ }
8492
+ if (selectedTools.includes("prettier") && presetConfig.prettier) {
8493
+ prettierOptions = presetConfig.prettier;
8494
+ }
8495
+ } else {
8496
+ if (selectedTools.includes("editorconfig")) {
8497
+ editorconfigOptions = await promptEditorConfigOptions();
8498
+ }
8499
+ if (selectedTools.includes("biome")) {
8500
+ biomeOptions = await promptBiomeOptions();
8501
+ }
8502
+ if (selectedTools.includes("prettier")) {
8503
+ prettierOptions = await promptPrettierOptions();
8504
+ }
8505
+ }
8506
+ }
8507
+ if (selectedTools.includes("commitlint")) {
8508
+ commitlintOptions = await promptCommitlintOptions();
8509
+ }
8510
+ } else {
8511
+ if (selectedTools.includes("editorconfig")) {
8512
+ editorconfigOptions = DEFAULT_EDITORCONFIG_OPTIONS;
8513
+ }
8514
+ if (selectedTools.includes("biome")) {
8515
+ biomeOptions = DEFAULT_BIOME_OPTIONS;
8516
+ }
8517
+ if (selectedTools.includes("prettier")) {
8518
+ prettierOptions = DEFAULT_PRETTIER_OPTIONS;
8519
+ }
8520
+ if (selectedTools.includes("commitlint")) {
8521
+ commitlintOptions = DEFAULT_COMMITLINT_OPTIONS;
8522
+ }
8523
+ }
8524
+ return {
8525
+ enabled: selectedTools.length > 0,
8526
+ editorconfig: selectedTools.includes("editorconfig"),
8527
+ editorconfigOptions,
8528
+ commitlint: selectedTools.includes("commitlint"),
8529
+ commitlintOptions,
8530
+ biome: selectedTools.includes("biome"),
8531
+ biomeOptions,
8532
+ prettier: selectedTools.includes("prettier"),
8533
+ prettierOptions
8534
+ };
8535
+ }
8536
+ async function promptStylePreset() {
8537
+ logger.newline();
8538
+ logger.info("Choose a code style preset:");
8539
+ return select({
8540
+ message: "Style preset:",
8541
+ choices: Object.entries(CODE_STYLE_PRESETS).map(([key, preset]) => ({
8542
+ name: `${preset.name} - ${preset.description}`,
8543
+ value: key
8544
+ })),
8545
+ default: "standard"
8546
+ });
8547
+ }
8548
+ async function promptEditorConfigOptions() {
8549
+ logger.newline();
8550
+ logger.subtitle("EditorConfig Options");
8551
+ const indentStyle = await select({
8552
+ message: "Indent style:",
8553
+ choices: [
8554
+ { name: "Spaces", value: "space" },
8555
+ { name: "Tabs", value: "tab" }
8556
+ ],
8557
+ default: DEFAULT_EDITORCONFIG_OPTIONS.indentStyle
8558
+ });
8559
+ const indentSizeStr = await input({
8560
+ message: "Indent size:",
8561
+ default: String(DEFAULT_EDITORCONFIG_OPTIONS.indentSize),
8562
+ validate: (v) => {
8563
+ const num = Number(v);
8564
+ if (Number.isNaN(num) || num < 1 || num > 8) return "Enter a number between 1 and 8";
8565
+ return true;
8566
+ }
8567
+ });
8568
+ const indentSize = Number(indentSizeStr);
8569
+ const endOfLine = await select({
8570
+ message: "Line endings:",
8571
+ choices: [
8572
+ { name: "LF (Unix/Mac)", value: "lf" },
8573
+ { name: "CRLF (Windows)", value: "crlf" },
8574
+ { name: "CR (Old Mac)", value: "cr" }
8575
+ ],
8576
+ default: DEFAULT_EDITORCONFIG_OPTIONS.endOfLine
8577
+ });
8578
+ const maxLineLengthStr = await input({
8579
+ message: 'Max line length (or "off"):',
8580
+ default: String(DEFAULT_EDITORCONFIG_OPTIONS.maxLineLength),
8581
+ validate: (v) => {
8582
+ if (v.toLowerCase() === "off") return true;
8583
+ const num = Number(v);
8584
+ if (Number.isNaN(num) || num < 40 || num > 200)
8585
+ return 'Enter a number between 40 and 200, or "off"';
8586
+ return true;
8587
+ }
8588
+ });
8589
+ const maxLineLength = maxLineLengthStr.toLowerCase() === "off" ? "off" : Number(maxLineLengthStr);
8590
+ return {
8591
+ indentStyle,
8592
+ indentSize,
8593
+ endOfLine,
8594
+ insertFinalNewline: true,
8595
+ trimTrailingWhitespace: true,
8596
+ charset: "utf-8",
8597
+ maxLineLength
8598
+ };
8599
+ }
8600
+ async function promptBiomeOptions() {
8601
+ logger.newline();
8602
+ logger.subtitle("Biome Options");
8603
+ const indentStyle = await select({
8604
+ message: "Indent style:",
8605
+ choices: [
8606
+ { name: "Spaces", value: "space" },
8607
+ { name: "Tabs", value: "tab" }
8608
+ ],
8609
+ default: DEFAULT_BIOME_OPTIONS.formatter.indentStyle
8610
+ });
8611
+ const indentWidthStr = await input({
8612
+ message: "Indent width:",
8613
+ default: String(DEFAULT_BIOME_OPTIONS.formatter.indentWidth),
8614
+ validate: (v) => {
8615
+ const num = Number(v);
8616
+ if (Number.isNaN(num) || num < 1 || num > 8) return "Enter a number between 1 and 8";
8617
+ return true;
8618
+ }
8619
+ });
8620
+ const indentWidth = Number(indentWidthStr);
8621
+ const lineWidthStr = await input({
8622
+ message: "Line width:",
8623
+ default: String(DEFAULT_BIOME_OPTIONS.formatter.lineWidth),
8624
+ validate: (v) => {
8625
+ const num = Number(v);
8626
+ if (Number.isNaN(num) || num < 40 || num > 200) return "Enter a number between 40 and 200";
8627
+ return true;
8628
+ }
8629
+ });
8630
+ const lineWidth = Number(lineWidthStr);
8631
+ const quoteStyle = await select({
8632
+ message: "Quote style:",
8633
+ choices: [
8634
+ { name: "Single quotes", value: "single" },
8635
+ { name: "Double quotes", value: "double" }
8636
+ ],
8637
+ default: DEFAULT_BIOME_OPTIONS.formatter.quoteStyle
8638
+ });
8639
+ const semicolons = await select({
8640
+ message: "Semicolons:",
8641
+ choices: [
8642
+ { name: "Always", value: "always" },
8643
+ { name: "As needed (ASI)", value: "asNeeded" }
8644
+ ],
8645
+ default: DEFAULT_BIOME_OPTIONS.formatter.semicolons
8646
+ });
8647
+ const trailingCommas = await select({
8648
+ message: "Trailing commas:",
8649
+ choices: [
8650
+ { name: "All", value: "all" },
8651
+ { name: "ES5 (only where valid in ES5)", value: "es5" },
8652
+ { name: "None", value: "none" }
8653
+ ],
8654
+ default: DEFAULT_BIOME_OPTIONS.formatter.trailingCommas
8655
+ });
8656
+ const enableRecommended = await confirm({
8657
+ message: "Enable recommended linter rules?",
8658
+ default: true
8659
+ });
8660
+ return {
8661
+ formatter: {
8662
+ indentStyle,
8663
+ indentWidth,
8664
+ lineWidth,
8665
+ quoteStyle,
8666
+ semicolons,
8667
+ trailingCommas,
8668
+ quoteProperties: "asNeeded",
8669
+ bracketSpacing: true,
8670
+ bracketSameLine: false,
8671
+ arrowParentheses: "always"
8672
+ },
8673
+ linter: {
8674
+ recommended: enableRecommended,
8675
+ correctness: enableRecommended,
8676
+ suspicious: enableRecommended,
8677
+ style: enableRecommended,
8678
+ complexity: enableRecommended,
8679
+ security: enableRecommended,
8680
+ performance: enableRecommended,
8681
+ a11y: enableRecommended
8682
+ },
8683
+ organizeImports: true,
8684
+ ignorePatterns: DEFAULT_BIOME_OPTIONS.ignorePatterns
8685
+ };
8686
+ }
8687
+ async function promptPrettierOptions() {
8688
+ logger.newline();
8689
+ logger.subtitle("Prettier Options");
8690
+ const printWidthStr = await input({
8691
+ message: "Print width:",
8692
+ default: String(DEFAULT_PRETTIER_OPTIONS.printWidth),
8693
+ validate: (v) => {
8694
+ const num = Number(v);
8695
+ if (Number.isNaN(num) || num < 40 || num > 200) return "Enter a number between 40 and 200";
8696
+ return true;
8697
+ }
8698
+ });
8699
+ const printWidth = Number(printWidthStr);
8700
+ const tabWidthStr = await input({
8701
+ message: "Tab width:",
8702
+ default: String(DEFAULT_PRETTIER_OPTIONS.tabWidth),
8703
+ validate: (v) => {
8704
+ const num = Number(v);
8705
+ if (Number.isNaN(num) || num < 1 || num > 8) return "Enter a number between 1 and 8";
8706
+ return true;
8707
+ }
8708
+ });
8709
+ const tabWidth = Number(tabWidthStr);
8710
+ const useTabs = await confirm({
8711
+ message: "Use tabs instead of spaces?",
8712
+ default: DEFAULT_PRETTIER_OPTIONS.useTabs
8713
+ });
8714
+ const semi = await confirm({
8715
+ message: "Use semicolons?",
8716
+ default: DEFAULT_PRETTIER_OPTIONS.semi
8717
+ });
8718
+ const singleQuote = await confirm({
8719
+ message: "Use single quotes?",
8720
+ default: DEFAULT_PRETTIER_OPTIONS.singleQuote
8721
+ });
8722
+ const trailingComma = await select({
8723
+ message: "Trailing commas:",
8724
+ choices: [
8725
+ { name: "All", value: "all" },
8726
+ { name: "ES5", value: "es5" },
8727
+ { name: "None", value: "none" }
8728
+ ],
8729
+ default: DEFAULT_PRETTIER_OPTIONS.trailingComma
8730
+ });
8731
+ const bracketSpacing = await confirm({
8732
+ message: "Bracket spacing? ({ foo: bar })",
8733
+ default: DEFAULT_PRETTIER_OPTIONS.bracketSpacing
8734
+ });
8735
+ const arrowParens = await select({
8736
+ message: "Arrow function parentheses:",
8737
+ choices: [
8738
+ { name: "Always (x) => x", value: "always" },
8739
+ { name: "Avoid x => x", value: "avoid" }
8740
+ ],
8741
+ default: DEFAULT_PRETTIER_OPTIONS.arrowParens
8742
+ });
8743
+ return {
8744
+ printWidth,
8745
+ tabWidth,
8746
+ useTabs,
8747
+ semi,
8748
+ singleQuote,
8749
+ jsxSingleQuote: false,
8750
+ trailingComma,
8751
+ bracketSpacing,
8752
+ bracketSameLine: false,
8753
+ arrowParens,
8754
+ endOfLine: "lf",
8755
+ proseWrap: "preserve",
8756
+ htmlWhitespaceSensitivity: "css",
8757
+ singleAttributePerLine: false
8758
+ };
8759
+ }
8760
+ async function promptCommitlintOptions() {
8761
+ logger.newline();
8762
+ logger.subtitle("Commitlint Options");
8763
+ const useDefaults = await confirm({
8764
+ message: "Use conventional commits defaults?",
8765
+ default: true
8766
+ });
8767
+ if (useDefaults) {
8768
+ const huskyIntegration2 = await confirm({
8769
+ message: "Enable Husky integration (git hooks)?",
8770
+ default: true
8771
+ });
8772
+ return {
8773
+ ...DEFAULT_COMMITLINT_OPTIONS,
8774
+ huskyIntegration: huskyIntegration2
8775
+ };
8776
+ }
8777
+ const typesInput = await input({
8778
+ message: "Commit types (comma-separated):",
8779
+ default: DEFAULT_COMMITLINT_OPTIONS.types.join(", ")
8780
+ });
8781
+ const types = typesInput.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
8782
+ const scopesInput = await input({
8783
+ message: "Allowed scopes (comma-separated, empty for any):",
8784
+ default: ""
8785
+ });
8786
+ const scopes = scopesInput.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
8787
+ const headerMaxLengthStr = await input({
8788
+ message: "Maximum header length:",
8789
+ default: String(DEFAULT_COMMITLINT_OPTIONS.headerMaxLength),
8790
+ validate: (v) => {
8791
+ const num = Number(v);
8792
+ if (Number.isNaN(num) || num < 20 || num > 200) return "Enter a number between 20 and 200";
8793
+ return true;
8794
+ }
8795
+ });
8796
+ const headerMaxLength = Number(headerMaxLengthStr);
8797
+ const scopeRequired = await confirm({
8798
+ message: "Require scope in commit messages?",
8799
+ default: false
8800
+ });
8801
+ const huskyIntegration = await confirm({
8802
+ message: "Enable Husky integration (git hooks)?",
8803
+ default: true
8804
+ });
8805
+ return {
8806
+ extends: ["@commitlint/config-conventional"],
8807
+ types,
8808
+ scopes,
8809
+ headerMaxLength,
8810
+ scopeRequired,
8811
+ bodyRequired: false,
8812
+ bodyMaxLineLength: 100,
8813
+ huskyIntegration
8814
+ };
8815
+ }
8816
+
8817
+ // src/cli/prompts/folder-preferences.ts
8818
+ init_esm_shims();
8819
+
8820
+ // src/constants/folder-preferences.ts
8821
+ init_esm_shims();
8822
+ var DEFAULT_FOLDER_PREFERENCES = {
8823
+ tests: {
8824
+ location: "test-folder-root",
8825
+ pattern: "*.test.ts"
8826
+ },
8827
+ planning: {
8828
+ location: "claude-sessions",
8829
+ commitToGit: false
8830
+ },
8831
+ docs: {
8832
+ location: "docs-root"
8833
+ },
8834
+ cicd: {
8835
+ location: "github-workflows",
8836
+ workflows: []
8837
+ }
8838
+ };
8839
+ var TEST_LOCATION_OPTIONS = [
8840
+ {
8841
+ value: "colocated",
8842
+ name: "Colocated with source files",
8843
+ description: "Tests next to source files (e.g., src/utils/foo.ts \u2192 src/utils/foo.test.ts). Good for small projects."
8844
+ },
8845
+ {
8846
+ value: "test-folder-root",
8847
+ name: "Root test/ folder (Recommended)",
8848
+ description: "Tests in test/ folder mirroring src structure (e.g., test/utils/foo.test.ts). Best for medium-large projects."
8849
+ },
8850
+ {
8851
+ value: "test-folder-src",
8852
+ name: "Inside src/__tests__/",
8853
+ description: "Tests in src/__tests__/ folder (e.g., src/__tests__/utils/foo.test.ts). Common in Create React App."
8854
+ }
8855
+ ];
8856
+ var PLANNING_LOCATION_OPTIONS = [
8857
+ {
8858
+ value: "claude-sessions",
8859
+ name: ".claude/sessions/planning/ (Recommended)",
8860
+ description: "Planning files in .claude/sessions/planning/. Keeps Claude-specific files organized."
8861
+ },
8862
+ {
8863
+ value: "docs-planning",
8864
+ name: "docs/planning/",
8865
+ description: "Planning files in docs/planning/. Good if you want planning docs alongside other docs."
8866
+ },
8867
+ {
8868
+ value: "root-planning",
8869
+ name: "planning/ at root",
8870
+ description: "Planning files in planning/ at project root. Maximum visibility."
8871
+ }
8872
+ ];
8873
+ var DOCS_LOCATION_OPTIONS = [
8874
+ {
8875
+ value: "docs-root",
8876
+ name: "docs/ at project root (Recommended)",
8877
+ description: "Documentation in docs/ folder. Standard location for most projects."
8878
+ },
8879
+ {
8880
+ value: "claude-docs",
8881
+ name: ".claude/docs/",
8882
+ description: "Documentation in .claude/docs/. Keeps all Claude-related files together."
8883
+ },
8884
+ {
8885
+ value: "readme-only",
8886
+ name: "README files only",
8887
+ description: "Only README.md files, no separate docs folder. Good for simple projects."
8888
+ }
8889
+ ];
8890
+ var TEST_PATTERN_OPTIONS = [
8891
+ {
8892
+ value: "*.test.ts",
8893
+ name: "*.test.ts (Recommended)",
8894
+ description: "Standard test pattern (foo.test.ts)"
8895
+ },
8896
+ {
8897
+ value: "*.spec.ts",
8898
+ name: "*.spec.ts",
8899
+ description: "Spec pattern common in Angular (foo.spec.ts)"
8900
+ },
8901
+ {
8902
+ value: "*.test.tsx",
8903
+ name: "*.test.tsx",
8904
+ description: "For React component tests (Component.test.tsx)"
8905
+ }
8906
+ ];
8907
+ var GITHUB_WORKFLOW_TEMPLATES = [
8908
+ // CI Workflows
8909
+ {
8910
+ id: "ci-node",
8911
+ name: "Node.js CI",
8912
+ description: "Run tests, lint, and type-check on push and PR",
8913
+ filename: "ci.yml",
8914
+ category: "ci",
8915
+ suitedFor: ["node", "typescript", "javascript"],
8916
+ recommended: true
8917
+ },
8918
+ {
8919
+ id: "ci-node-matrix",
8920
+ name: "Node.js CI (Matrix)",
8921
+ description: "Test across multiple Node.js versions (18, 20, 22)",
8194
8922
  filename: "ci-matrix.yml",
8195
8923
  category: "ci",
8196
8924
  suitedFor: ["node", "typescript", "library"]
@@ -8591,429 +9319,204 @@ async function promptFolderPreferences(options) {
8591
9319
  }
8592
9320
  const setupCiCd = hasCiCdBundle || await confirm({
8593
9321
  message: "Would you like to set up GitHub Actions workflows?",
8594
- default: true
8595
- });
8596
- if (setupCiCd) {
8597
- const workflows = await promptGithubWorkflows({
8598
- recommended: mergedDefaults.cicd?.workflows,
8599
- technologies
8600
- });
8601
- if (workflows.length > 0) {
8602
- preferences.cicd = {
8603
- location: "github-workflows",
8604
- workflows
8605
- };
8606
- }
8607
- }
8608
- return preferences;
8609
- }
8610
- function showFolderPreferencesSummary(preferences) {
8611
- logger.newline();
8612
- logger.subtitle("Folder Structure Summary");
8613
- if (preferences.tests) {
8614
- const testOpt = TEST_LOCATION_OPTIONS.find((o) => o.value === preferences.tests?.location);
8615
- logger.keyValue("Tests", testOpt?.name || preferences.tests.location);
8616
- logger.keyValue("Test Pattern", preferences.tests.pattern);
8617
- }
8618
- if (preferences.planning) {
8619
- const planOpt = PLANNING_LOCATION_OPTIONS.find(
8620
- (o) => o.value === preferences.planning?.location
8621
- );
8622
- logger.keyValue("Planning", planOpt?.name || preferences.planning.location);
8623
- logger.keyValue(
8624
- "Commit Planning",
8625
- preferences.planning.commitToGit ? "Yes" : "No (will be in .gitignore)"
8626
- );
8627
- }
8628
- if (preferences.docs) {
8629
- const docsOpt = DOCS_LOCATION_OPTIONS.find((o) => o.value === preferences.docs?.location);
8630
- logger.keyValue("Documentation", docsOpt?.name || preferences.docs.location);
8631
- }
8632
- if (preferences.cicd && preferences.cicd.workflows.length > 0) {
8633
- logger.keyValue("CI/CD Location", ".github/workflows/");
8634
- logger.info(` ${colors.muted("Workflows:")} ${preferences.cicd.workflows.join(", ")}`);
8635
- }
8636
- }
8637
- async function promptQuickFolderPreferences(options) {
8638
- const recommendations = getFolderRecommendationsForBundles(options.selectedBundles);
8639
- const mergedDefaults = mergeFolderPreferences(recommendations);
8640
- if (recommendations.length > 0) {
8641
- logger.section("Folder Structure", "\u{1F4C1}");
8642
- logger.info(colors.muted("Based on your bundles, we have recommended folder settings."));
8643
- showFolderPreferencesSummary(mergedDefaults);
8644
- const customize = await confirm({
8645
- message: "Would you like to customize these folder preferences?",
8646
- default: false
8647
- });
8648
- if (!customize) {
8649
- return mergedDefaults;
8650
- }
8651
- }
8652
- return promptFolderPreferences(options);
8653
- }
8654
-
8655
- // src/cli/prompts/index.ts
8656
- init_esm_shims();
8657
-
8658
- // src/cli/prompts/project-info.ts
8659
- init_esm_shims();
8660
- async function promptProjectInfo(options) {
8661
- logger.section("Project Information", "\u{1F4CB}");
8662
- const name = await input({
8663
- message: "Project name:",
8664
- default: options?.defaults?.name,
8665
- validate: (value) => {
8666
- if (!value.trim()) return "Project name is required";
8667
- if (!/^[a-zA-Z0-9-_\s]+$/.test(value)) {
8668
- return "Project name can only contain letters, numbers, dashes, underscores, and spaces";
8669
- }
8670
- return true;
8671
- }
8672
- });
8673
- const description = await input({
8674
- message: "Project description:",
8675
- default: options?.defaults?.description || "",
8676
- validate: (value) => {
8677
- if (!value.trim()) return "Description is required";
8678
- return true;
8679
- }
8680
- });
8681
- const org = await input({
8682
- message: "GitHub organization/username:",
8683
- default: options?.defaults?.org || "",
8684
- validate: (value) => {
8685
- if (!value.trim()) return "Organization is required";
8686
- if (!/^[a-zA-Z0-9-]+$/.test(value)) {
8687
- return "Organization can only contain letters, numbers, and dashes";
8688
- }
8689
- return true;
8690
- }
8691
- });
8692
- const repo = await input({
8693
- message: "Repository name:",
8694
- default: options?.defaults?.repo || name.toLowerCase().replace(/\s+/g, "-"),
8695
- validate: (value) => {
8696
- if (!value.trim()) return "Repository name is required";
8697
- if (!/^[a-zA-Z0-9-_]+$/.test(value)) {
8698
- return "Repository name can only contain letters, numbers, dashes, and underscores";
8699
- }
8700
- return true;
8701
- }
8702
- });
8703
- const author = await input({
8704
- message: 'Author (name or "Name <email>"):',
8705
- default: options?.defaults?.author || ""
8706
- });
8707
- let entityType = "item";
8708
- let entityTypePlural = "items";
8709
- const wantEntityConfig = await confirm({
8710
- message: "Configure primary entity type? (Used for code examples and templates)",
8711
- default: false
8712
- });
8713
- if (wantEntityConfig) {
8714
- logger.info("The entity type is used in code examples and templates throughout the project.");
8715
- logger.info(
8716
- 'For example, if your project manages "products", code examples will use product-related names.'
8717
- );
8718
- logger.newline();
8719
- entityType = await input({
8720
- message: "Primary entity type (e.g., product, article, user, listing):",
8721
- default: options?.defaults?.entityType || "item",
8722
- validate: (value) => {
8723
- if (!value.trim()) return "Entity type is required";
8724
- return true;
8725
- }
8726
- });
8727
- entityTypePlural = await input({
8728
- message: "Entity type plural:",
8729
- default: options?.defaults?.entityTypePlural || pluralize(entityType)
8730
- });
8731
- } else if (options?.defaults?.entityType) {
8732
- entityType = options.defaults.entityType;
8733
- entityTypePlural = options.defaults.entityTypePlural || pluralize(entityType);
8734
- }
8735
- let domain;
8736
- let location;
8737
- if (!options?.skipOptional) {
8738
- const wantDomain = await confirm({
8739
- message: "Do you want to specify a domain?",
8740
- default: false
8741
- });
8742
- if (wantDomain) {
8743
- domain = await input({
8744
- message: "Domain (e.g., myproject.com):",
8745
- default: options?.defaults?.domain || "",
8746
- validate: (value) => {
8747
- if (value && !/^[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(value)) {
8748
- return "Please enter a valid domain";
8749
- }
8750
- return true;
8751
- }
8752
- });
8753
- }
8754
- const wantLocation = await confirm({
8755
- message: "Do you want to specify a location/region?",
8756
- default: false
9322
+ default: true
9323
+ });
9324
+ if (setupCiCd) {
9325
+ const workflows = await promptGithubWorkflows({
9326
+ recommended: mergedDefaults.cicd?.workflows,
9327
+ technologies
8757
9328
  });
8758
- if (wantLocation) {
8759
- location = await input({
8760
- message: "Location/Region:",
8761
- default: options?.defaults?.location || ""
8762
- });
9329
+ if (workflows.length > 0) {
9330
+ preferences.cicd = {
9331
+ location: "github-workflows",
9332
+ workflows
9333
+ };
8763
9334
  }
8764
9335
  }
8765
- return {
8766
- name: name.trim(),
8767
- description: description.trim(),
8768
- org: org.trim(),
8769
- repo: repo.trim(),
8770
- domain,
8771
- entityType: entityType.trim().toLowerCase(),
8772
- entityTypePlural: entityTypePlural.trim().toLowerCase(),
8773
- location,
8774
- author: author.trim() || void 0
8775
- };
9336
+ return preferences;
8776
9337
  }
8777
- function pluralize(word) {
8778
- const lower = word.toLowerCase();
8779
- if (lower.endsWith("y")) {
8780
- return `${lower.slice(0, -1)}ies`;
9338
+ function showFolderPreferencesSummary(preferences) {
9339
+ logger.newline();
9340
+ logger.subtitle("Folder Structure Summary");
9341
+ if (preferences.tests) {
9342
+ const testOpt = TEST_LOCATION_OPTIONS.find((o) => o.value === preferences.tests?.location);
9343
+ logger.keyValue("Tests", testOpt?.name || preferences.tests.location);
9344
+ logger.keyValue("Test Pattern", preferences.tests.pattern);
8781
9345
  }
8782
- if (lower.endsWith("s") || lower.endsWith("x") || lower.endsWith("ch") || lower.endsWith("sh")) {
8783
- return `${lower}es`;
9346
+ if (preferences.planning) {
9347
+ const planOpt = PLANNING_LOCATION_OPTIONS.find(
9348
+ (o) => o.value === preferences.planning?.location
9349
+ );
9350
+ logger.keyValue("Planning", planOpt?.name || preferences.planning.location);
9351
+ logger.keyValue(
9352
+ "Commit Planning",
9353
+ preferences.planning.commitToGit ? "Yes" : "No (will be in .gitignore)"
9354
+ );
9355
+ }
9356
+ if (preferences.docs) {
9357
+ const docsOpt = DOCS_LOCATION_OPTIONS.find((o) => o.value === preferences.docs?.location);
9358
+ logger.keyValue("Documentation", docsOpt?.name || preferences.docs.location);
9359
+ }
9360
+ if (preferences.cicd && preferences.cicd.workflows.length > 0) {
9361
+ logger.keyValue("CI/CD Location", ".github/workflows/");
9362
+ logger.info(` ${colors.muted("Workflows:")} ${preferences.cicd.workflows.join(", ")}`);
8784
9363
  }
8785
- return `${lower}s`;
8786
9364
  }
8787
- async function confirmProjectInfo(info) {
8788
- logger.newline();
8789
- logger.subtitle("Project Summary");
8790
- logger.keyValue("Name", info.name);
8791
- logger.keyValue("Description", info.description);
8792
- logger.keyValue("GitHub", `${info.org}/${info.repo}`);
8793
- if (info.author) logger.keyValue("Author", info.author);
8794
- logger.keyValue("Entity", `${info.entityType} / ${info.entityTypePlural}`);
8795
- if (info.domain) logger.keyValue("Domain", info.domain);
8796
- if (info.location) logger.keyValue("Location", info.location);
8797
- logger.newline();
8798
- return confirm({
8799
- message: "Is this information correct?",
8800
- default: true
8801
- });
9365
+ async function promptQuickFolderPreferences(options) {
9366
+ const recommendations = getFolderRecommendationsForBundles(options.selectedBundles);
9367
+ const mergedDefaults = mergeFolderPreferences(recommendations);
9368
+ if (recommendations.length > 0) {
9369
+ logger.section("Folder Structure", "\u{1F4C1}");
9370
+ logger.info(colors.muted("Based on your bundles, we have recommended folder settings."));
9371
+ showFolderPreferencesSummary(mergedDefaults);
9372
+ const customize = await confirm({
9373
+ message: "Would you like to customize these folder preferences?",
9374
+ default: false
9375
+ });
9376
+ if (!customize) {
9377
+ return mergedDefaults;
9378
+ }
9379
+ }
9380
+ return promptFolderPreferences(options);
8802
9381
  }
8803
9382
 
8804
- // src/cli/prompts/preferences.ts
9383
+ // src/cli/prompts/hook-config.ts
8805
9384
  init_esm_shims();
8806
- async function promptPreferences(options) {
8807
- logger.section("Preferences", "\u2699\uFE0F");
8808
- const language = await select({
8809
- message: "Working language (for documentation and comments):",
8810
- choices: [
8811
- { name: "English", value: "en" },
8812
- { name: "Espa\xF1ol", value: "es" }
8813
- ],
8814
- default: options?.defaults?.language || "en"
9385
+ async function promptHookConfig(options) {
9386
+ logger.section("Hook Configuration", "\u{1FA9D}");
9387
+ const enableHooks = await confirm({
9388
+ message: "Do you want to configure hooks?",
9389
+ default: true
8815
9390
  });
8816
- const responseLanguage = await select({
8817
- message: "Claude response language:",
9391
+ if (!enableHooks) {
9392
+ return {
9393
+ enabled: false
9394
+ };
9395
+ }
9396
+ const selectedHooks = await checkbox({
9397
+ message: "Which hooks do you want to configure?",
8818
9398
  choices: [
8819
- { name: "English", value: "en" },
8820
- { name: "Espa\xF1ol", value: "es" }
8821
- ],
8822
- default: options?.defaults?.responseLanguage || language
8823
- });
8824
- const includeCoAuthor = await confirm({
8825
- message: "Include Claude as commit co-author?",
8826
- default: options?.defaults?.includeCoAuthor ?? true
9399
+ {
9400
+ name: "Notification hook (alerts when Claude needs attention)",
9401
+ value: "notification",
9402
+ checked: true
9403
+ },
9404
+ {
9405
+ name: "Stop hook (plays sound when Claude stops)",
9406
+ value: "stop",
9407
+ checked: false
9408
+ },
9409
+ {
9410
+ name: "Subagent stop hook (plays sound when subagent completes)",
9411
+ value: "subagentStop",
9412
+ checked: false
9413
+ }
9414
+ ]
8827
9415
  });
8828
- const packageManager = await promptPackageManagerPreference(options?.detectedPackageManager);
8829
- return {
8830
- language,
8831
- responseLanguage,
8832
- includeCoAuthor,
8833
- packageManager
9416
+ const config = {
9417
+ enabled: true
8834
9418
  };
8835
- }
8836
- async function promptPackageManagerPreference(detected) {
8837
- const choices = [
8838
- {
8839
- name: "pnpm (recommended)",
8840
- value: "pnpm",
8841
- description: "Fast, disk space efficient package manager"
8842
- },
8843
- {
8844
- name: "npm",
8845
- value: "npm",
8846
- description: "Node.js default package manager"
8847
- },
8848
- {
8849
- name: "yarn",
8850
- value: "yarn",
8851
- description: "Fast, reliable dependency management"
8852
- },
8853
- {
8854
- name: "bun",
8855
- value: "bun",
8856
- description: "All-in-one JavaScript runtime & toolkit"
8857
- }
8858
- ];
8859
- if (detected) {
8860
- const detectedChoice = choices.find((c) => c.value === detected);
8861
- if (detectedChoice) {
8862
- detectedChoice.name = `${detectedChoice.name} (detected)`;
8863
- }
9419
+ if (selectedHooks.includes("notification")) {
9420
+ config.notification = await promptNotificationHook(options?.defaults?.notification);
8864
9421
  }
8865
- return select({
8866
- message: "Preferred package manager:",
8867
- choices,
8868
- default: detected || "pnpm"
8869
- });
9422
+ if (selectedHooks.includes("stop")) {
9423
+ config.stop = await promptStopHook();
9424
+ }
9425
+ if (selectedHooks.includes("subagentStop")) {
9426
+ config.subagentStop = await promptSubagentStopHook();
9427
+ }
9428
+ return config;
8870
9429
  }
8871
-
8872
- // src/cli/prompts/scaffold.ts
8873
- init_esm_shims();
8874
- async function promptScaffoldType(options) {
8875
- logger.section("Configuration Scope", "\u{1F3D7}\uFE0F");
8876
- if (options?.existingProject) {
8877
- logger.info("Existing project detected");
8878
- logger.newline();
8879
- const choice = await select({
8880
- message: "What would you like to do?",
8881
- choices: [
8882
- {
8883
- name: "Add Claude config only (recommended)",
8884
- value: "claude-only",
8885
- description: "Creates .claude/ folder without touching existing files"
8886
- },
8887
- {
8888
- name: "Full project setup (\u26A0\uFE0F may overwrite files)",
8889
- value: "full-project",
8890
- description: "Includes project scaffolding - use with caution"
8891
- }
8892
- ],
8893
- default: "claude-only"
8894
- });
8895
- if (choice === "full-project") {
8896
- const confirmed = await confirm({
8897
- message: "\u26A0\uFE0F This may overwrite existing files. Are you sure?",
8898
- default: false
8899
- });
8900
- if (!confirmed) {
8901
- return "claude-only";
9430
+ async function promptNotificationHook(defaults) {
9431
+ logger.newline();
9432
+ logger.info("Notification Hook Configuration");
9433
+ const notificationTypes = await checkbox({
9434
+ message: "Select notification types:",
9435
+ choices: [
9436
+ {
9437
+ name: "Desktop notifications (notify-send / terminal-notifier)",
9438
+ value: "desktop",
9439
+ checked: defaults?.desktop ?? true
9440
+ },
9441
+ {
9442
+ name: "Audio notifications (text-to-speech)",
9443
+ value: "audio",
9444
+ checked: defaults?.audio ?? false
8902
9445
  }
8903
- }
8904
- return choice;
9446
+ ]
9447
+ });
9448
+ let customCommand;
9449
+ const wantCustom = await confirm({
9450
+ message: "Do you want to add a custom notification command?",
9451
+ default: false
9452
+ });
9453
+ if (wantCustom) {
9454
+ customCommand = await input({
9455
+ message: "Custom command (receives notification text as argument):",
9456
+ default: defaults?.customCommand
9457
+ });
8905
9458
  }
8906
- return select({
8907
- message: "What would you like to configure?",
9459
+ return {
9460
+ desktop: notificationTypes.includes("desktop"),
9461
+ audio: notificationTypes.includes("audio"),
9462
+ customCommand: customCommand || void 0
9463
+ };
9464
+ }
9465
+ async function promptStopHook() {
9466
+ logger.newline();
9467
+ logger.info("Stop Hook Configuration");
9468
+ const soundType = await select({
9469
+ message: "What should happen when Claude stops?",
8908
9470
  choices: [
8909
- {
8910
- name: "Claude configuration only",
8911
- value: "claude-only",
8912
- description: "Only creates .claude/ folder with agents, commands, skills, docs"
8913
- },
8914
- {
8915
- name: "Full project setup",
8916
- value: "full-project",
8917
- description: "Creates project structure + Claude configuration"
8918
- }
9471
+ { name: "Play beep sound", value: "beep" },
9472
+ { name: "Play custom sound", value: "custom" },
9473
+ { name: "Run custom command", value: "command" }
8919
9474
  ],
8920
- default: "claude-only"
9475
+ default: "beep"
8921
9476
  });
8922
- }
8923
- async function promptProjectType(detectedType) {
8924
- const choices = [
8925
- {
8926
- name: "Node.js (TypeScript)",
8927
- value: "node",
8928
- description: "Basic Node.js project with TypeScript"
8929
- },
8930
- {
8931
- name: "Monorepo (TurboRepo + pnpm)",
8932
- value: "monorepo",
8933
- description: "Multi-package monorepo setup"
8934
- },
8935
- {
8936
- name: "Astro",
8937
- value: "astro",
8938
- description: "Astro static/SSR site"
8939
- },
8940
- {
8941
- name: "Next.js",
8942
- value: "nextjs",
8943
- description: "Next.js full-stack React framework"
8944
- },
8945
- {
8946
- name: "Vite + React",
8947
- value: "vite-react",
8948
- description: "Vite bundled React SPA"
8949
- },
8950
- {
8951
- name: "Hono API",
8952
- value: "hono",
8953
- description: "Hono lightweight API server"
8954
- },
8955
- {
8956
- name: "Custom",
8957
- value: "custom",
8958
- description: "Define your own project structure"
8959
- }
8960
- ];
8961
- if (detectedType) {
8962
- const detected = choices.find((c) => c.value === detectedType);
8963
- if (detected) {
8964
- detected.name = `${detected.name} (detected)`;
8965
- }
9477
+ if (soundType === "beep") {
9478
+ return { beep: true };
8966
9479
  }
8967
- return select({
8968
- message: "Project type:",
8969
- choices,
8970
- default: detectedType || "node"
8971
- });
8972
- }
8973
- async function promptPackageManager(detected) {
8974
- const choices = [
8975
- { name: "pnpm (recommended)", value: "pnpm" },
8976
- { name: "npm", value: "npm" },
8977
- { name: "yarn", value: "yarn" },
8978
- { name: "bun", value: "bun" }
8979
- ];
8980
- if (detected) {
8981
- const detectedChoice = choices.find((c) => c.value === detected);
8982
- if (detectedChoice) {
8983
- detectedChoice.name = `${detectedChoice.name} (detected)`;
8984
- }
9480
+ if (soundType === "custom") {
9481
+ const soundPath = await input({
9482
+ message: "Path to sound file:",
9483
+ validate: (v) => v.trim() ? true : "Please enter a valid path"
9484
+ });
9485
+ return { customSound: soundPath };
8985
9486
  }
8986
- return select({
8987
- message: "Package manager:",
8988
- choices,
8989
- default: detected || "pnpm"
9487
+ const customCommand = await input({
9488
+ message: "Custom command to run:",
9489
+ validate: (v) => v.trim() ? true : "Please enter a command"
8990
9490
  });
9491
+ return { customCommand };
8991
9492
  }
8992
- async function promptScaffoldOptions(options) {
8993
- const type = await promptScaffoldType(options);
8994
- if (type === "claude-only") {
8995
- return { type };
8996
- }
8997
- const projectType = await promptProjectType(options?.detectedType);
8998
- const packageManager = await promptPackageManager(options?.detectedPackageManager);
9493
+ async function promptSubagentStopHook() {
8999
9494
  logger.newline();
9000
- logger.subtitle("Additional Options");
9001
- const additionalOptions = await checkbox({
9002
- message: "Select additional options:",
9495
+ logger.info("Subagent Stop Hook Configuration");
9496
+ const soundType = await select({
9497
+ message: "What should happen when a subagent completes?",
9003
9498
  choices: [
9004
- { name: "Initialize git repository", value: "initGit", checked: true },
9005
- { name: "Create README.md", value: "createReadme", checked: true },
9006
- { name: "Create .gitignore", value: "createGitignore", checked: true }
9007
- ]
9499
+ { name: "Play short beep", value: "beep" },
9500
+ { name: "Play custom sound", value: "custom" },
9501
+ { name: "Run custom command", value: "command" }
9502
+ ],
9503
+ default: "beep"
9008
9504
  });
9009
- return {
9010
- type,
9011
- projectType,
9012
- packageManager,
9013
- initGit: additionalOptions.includes("initGit"),
9014
- createReadme: additionalOptions.includes("createReadme"),
9015
- createGitignore: additionalOptions.includes("createGitignore")
9016
- };
9505
+ if (soundType === "beep") {
9506
+ return { beep: true };
9507
+ }
9508
+ if (soundType === "custom") {
9509
+ const soundPath = await input({
9510
+ message: "Path to sound file:",
9511
+ validate: (v) => v.trim() ? true : "Please enter a valid path"
9512
+ });
9513
+ return { customSound: soundPath };
9514
+ }
9515
+ const customCommand = await input({
9516
+ message: "Custom command to run:",
9517
+ validate: (v) => v.trim() ? true : "Please enter a command"
9518
+ });
9519
+ return { customCommand };
9017
9520
  }
9018
9521
 
9019
9522
  // src/cli/prompts/item-select.ts
@@ -9127,173 +9630,34 @@ ${progress} ${colors.bold(item.name)}`);
9127
9630
  return {
9128
9631
  category,
9129
9632
  selectedItems,
9130
- skippedItems
9131
- };
9132
- }
9133
- async function promptItemWithShortcuts(item, options) {
9134
- const choices = [
9135
- { name: "Yes, install", value: "install" },
9136
- { name: "No, skip", value: "skip" }
9137
- ];
9138
- if (options.remainingCount > 0) {
9139
- choices.push(
9140
- {
9141
- name: `Install all remaining (${options.remainingCount + 1} items)`,
9142
- value: "install-rest"
9143
- },
9144
- {
9145
- name: `Skip all remaining (${options.remainingCount + 1} items)`,
9146
- value: "skip-rest"
9147
- }
9148
- );
9149
- }
9150
- return select({
9151
- message: `Install ${item.name}?`,
9152
- choices,
9153
- default: options.defaultInstall !== false ? "install" : "skip"
9154
- });
9155
- }
9156
- function capitalize(str) {
9157
- return str.charAt(0).toUpperCase() + str.slice(1);
9158
- }
9159
-
9160
- // src/cli/prompts/hook-config.ts
9161
- init_esm_shims();
9162
- async function promptHookConfig(options) {
9163
- logger.section("Hook Configuration", "\u{1FA9D}");
9164
- const enableHooks = await confirm({
9165
- message: "Do you want to configure hooks?",
9166
- default: true
9167
- });
9168
- if (!enableHooks) {
9169
- return {
9170
- enabled: false
9171
- };
9172
- }
9173
- const selectedHooks = await checkbox({
9174
- message: "Which hooks do you want to configure?",
9175
- choices: [
9176
- {
9177
- name: "Notification hook (alerts when Claude needs attention)",
9178
- value: "notification",
9179
- checked: true
9180
- },
9181
- {
9182
- name: "Stop hook (plays sound when Claude stops)",
9183
- value: "stop",
9184
- checked: false
9185
- },
9186
- {
9187
- name: "Subagent stop hook (plays sound when subagent completes)",
9188
- value: "subagentStop",
9189
- checked: false
9190
- }
9191
- ]
9192
- });
9193
- const config = {
9194
- enabled: true
9195
- };
9196
- if (selectedHooks.includes("notification")) {
9197
- config.notification = await promptNotificationHook(options?.defaults?.notification);
9198
- }
9199
- if (selectedHooks.includes("stop")) {
9200
- config.stop = await promptStopHook();
9201
- }
9202
- if (selectedHooks.includes("subagentStop")) {
9203
- config.subagentStop = await promptSubagentStopHook();
9204
- }
9205
- return config;
9206
- }
9207
- async function promptNotificationHook(defaults) {
9208
- logger.newline();
9209
- logger.info("Notification Hook Configuration");
9210
- const notificationTypes = await checkbox({
9211
- message: "Select notification types:",
9212
- choices: [
9213
- {
9214
- name: "Desktop notifications (notify-send / terminal-notifier)",
9215
- value: "desktop",
9216
- checked: defaults?.desktop ?? true
9217
- },
9218
- {
9219
- name: "Audio notifications (text-to-speech)",
9220
- value: "audio",
9221
- checked: defaults?.audio ?? false
9222
- }
9223
- ]
9224
- });
9225
- let customCommand;
9226
- const wantCustom = await confirm({
9227
- message: "Do you want to add a custom notification command?",
9228
- default: false
9229
- });
9230
- if (wantCustom) {
9231
- customCommand = await input({
9232
- message: "Custom command (receives notification text as argument):",
9233
- default: defaults?.customCommand
9234
- });
9235
- }
9236
- return {
9237
- desktop: notificationTypes.includes("desktop"),
9238
- audio: notificationTypes.includes("audio"),
9239
- customCommand: customCommand || void 0
9633
+ skippedItems
9240
9634
  };
9241
9635
  }
9242
- async function promptStopHook() {
9243
- logger.newline();
9244
- logger.info("Stop Hook Configuration");
9245
- const soundType = await select({
9246
- message: "What should happen when Claude stops?",
9247
- choices: [
9248
- { name: "Play beep sound", value: "beep" },
9249
- { name: "Play custom sound", value: "custom" },
9250
- { name: "Run custom command", value: "command" }
9251
- ],
9252
- default: "beep"
9253
- });
9254
- if (soundType === "beep") {
9255
- return { beep: true };
9256
- }
9257
- if (soundType === "custom") {
9258
- const soundPath = await input({
9259
- message: "Path to sound file:",
9260
- validate: (v) => v.trim() ? true : "Please enter a valid path"
9261
- });
9262
- return { customSound: soundPath };
9636
+ async function promptItemWithShortcuts(item, options) {
9637
+ const choices = [
9638
+ { name: "Yes, install", value: "install" },
9639
+ { name: "No, skip", value: "skip" }
9640
+ ];
9641
+ if (options.remainingCount > 0) {
9642
+ choices.push(
9643
+ {
9644
+ name: `Install all remaining (${options.remainingCount + 1} items)`,
9645
+ value: "install-rest"
9646
+ },
9647
+ {
9648
+ name: `Skip all remaining (${options.remainingCount + 1} items)`,
9649
+ value: "skip-rest"
9650
+ }
9651
+ );
9263
9652
  }
9264
- const customCommand = await input({
9265
- message: "Custom command to run:",
9266
- validate: (v) => v.trim() ? true : "Please enter a command"
9653
+ return select({
9654
+ message: `Install ${item.name}?`,
9655
+ choices,
9656
+ default: options.defaultInstall !== false ? "install" : "skip"
9267
9657
  });
9268
- return { customCommand };
9269
9658
  }
9270
- async function promptSubagentStopHook() {
9271
- logger.newline();
9272
- logger.info("Subagent Stop Hook Configuration");
9273
- const soundType = await select({
9274
- message: "What should happen when a subagent completes?",
9275
- choices: [
9276
- { name: "Play short beep", value: "beep" },
9277
- { name: "Play custom sound", value: "custom" },
9278
- { name: "Run custom command", value: "command" }
9279
- ],
9280
- default: "beep"
9281
- });
9282
- if (soundType === "beep") {
9283
- return { beep: true };
9284
- }
9285
- if (soundType === "custom") {
9286
- const soundPath = await input({
9287
- message: "Path to sound file:",
9288
- validate: (v) => v.trim() ? true : "Please enter a valid path"
9289
- });
9290
- return { customSound: soundPath };
9291
- }
9292
- const customCommand = await input({
9293
- message: "Custom command to run:",
9294
- validate: (v) => v.trim() ? true : "Please enter a command"
9295
- });
9296
- return { customCommand };
9659
+ function capitalize(str) {
9660
+ return str.charAt(0).toUpperCase() + str.slice(1);
9297
9661
  }
9298
9662
 
9299
9663
  // src/cli/prompts/mcp-config.ts
@@ -9798,1178 +10162,1480 @@ async function promptCustomPermissions(defaults) {
9798
10162
  if (rule.trim()) {
9799
10163
  deny.push(rule.trim());
9800
10164
  }
9801
- addMoreDeny = await confirm({
9802
- message: "Add another deny rule?",
9803
- default: false
9804
- });
10165
+ addMoreDeny = await confirm({
10166
+ message: "Add another deny rule?",
10167
+ default: false
10168
+ });
10169
+ }
10170
+ return { allow, deny };
10171
+ }
10172
+
10173
+ // src/cli/prompts/preferences.ts
10174
+ init_esm_shims();
10175
+ async function promptPreferences(options) {
10176
+ logger.section("Preferences", "\u2699\uFE0F");
10177
+ const language = await select({
10178
+ message: "Working language (for documentation and comments):",
10179
+ choices: [
10180
+ { name: "English", value: "en" },
10181
+ { name: "Espa\xF1ol", value: "es" }
10182
+ ],
10183
+ default: options?.defaults?.language || "en"
10184
+ });
10185
+ const responseLanguage = await select({
10186
+ message: "Claude response language:",
10187
+ choices: [
10188
+ { name: "English", value: "en" },
10189
+ { name: "Espa\xF1ol", value: "es" }
10190
+ ],
10191
+ default: options?.defaults?.responseLanguage || language
10192
+ });
10193
+ const includeCoAuthor = await confirm({
10194
+ message: "Include Claude as commit co-author?",
10195
+ default: options?.defaults?.includeCoAuthor ?? true
10196
+ });
10197
+ const packageManager = await promptPackageManagerPreference(options?.detectedPackageManager);
10198
+ return {
10199
+ language,
10200
+ responseLanguage,
10201
+ includeCoAuthor,
10202
+ packageManager
10203
+ };
10204
+ }
10205
+ async function promptPackageManagerPreference(detected) {
10206
+ const choices = [
10207
+ {
10208
+ name: "pnpm (recommended)",
10209
+ value: "pnpm",
10210
+ description: "Fast, disk space efficient package manager"
10211
+ },
10212
+ {
10213
+ name: "npm",
10214
+ value: "npm",
10215
+ description: "Node.js default package manager"
10216
+ },
10217
+ {
10218
+ name: "yarn",
10219
+ value: "yarn",
10220
+ description: "Fast, reliable dependency management"
10221
+ },
10222
+ {
10223
+ name: "bun",
10224
+ value: "bun",
10225
+ description: "All-in-one JavaScript runtime & toolkit"
10226
+ }
10227
+ ];
10228
+ if (detected) {
10229
+ const detectedChoice = choices.find((c) => c.value === detected);
10230
+ if (detectedChoice) {
10231
+ detectedChoice.name = `${detectedChoice.name} (detected)`;
10232
+ }
10233
+ }
10234
+ return select({
10235
+ message: "Preferred package manager:",
10236
+ choices,
10237
+ default: detected || "pnpm"
10238
+ });
10239
+ }
10240
+
10241
+ // src/cli/prompts/project-info.ts
10242
+ init_esm_shims();
10243
+ async function promptProjectInfo(options) {
10244
+ logger.section("Project Information", "\u{1F4CB}");
10245
+ const name = await input({
10246
+ message: "Project name:",
10247
+ default: options?.defaults?.name,
10248
+ validate: (value) => {
10249
+ if (!value.trim()) return "Project name is required";
10250
+ if (!/^[a-zA-Z0-9-_\s]+$/.test(value)) {
10251
+ return "Project name can only contain letters, numbers, dashes, underscores, and spaces";
10252
+ }
10253
+ return true;
10254
+ }
10255
+ });
10256
+ const description = await input({
10257
+ message: "Project description:",
10258
+ default: options?.defaults?.description || "",
10259
+ validate: (value) => {
10260
+ if (!value.trim()) return "Description is required";
10261
+ return true;
10262
+ }
10263
+ });
10264
+ const org = await input({
10265
+ message: "GitHub organization/username:",
10266
+ default: options?.defaults?.org || "",
10267
+ validate: (value) => {
10268
+ if (!value.trim()) return "Organization is required";
10269
+ if (!/^[a-zA-Z0-9-]+$/.test(value)) {
10270
+ return "Organization can only contain letters, numbers, and dashes";
10271
+ }
10272
+ return true;
10273
+ }
10274
+ });
10275
+ const repo = await input({
10276
+ message: "Repository name:",
10277
+ default: options?.defaults?.repo || name.toLowerCase().replace(/\s+/g, "-"),
10278
+ validate: (value) => {
10279
+ if (!value.trim()) return "Repository name is required";
10280
+ if (!/^[a-zA-Z0-9-_]+$/.test(value)) {
10281
+ return "Repository name can only contain letters, numbers, dashes, and underscores";
10282
+ }
10283
+ return true;
10284
+ }
10285
+ });
10286
+ const author = await input({
10287
+ message: 'Author (name or "Name <email>"):',
10288
+ default: options?.defaults?.author || ""
10289
+ });
10290
+ let entityType = "item";
10291
+ let entityTypePlural = "items";
10292
+ const wantEntityConfig = await confirm({
10293
+ message: "Configure primary entity type? (Used for code examples and templates)",
10294
+ default: false
10295
+ });
10296
+ if (wantEntityConfig) {
10297
+ logger.info("The entity type is used in code examples and templates throughout the project.");
10298
+ logger.info(
10299
+ 'For example, if your project manages "products", code examples will use product-related names.'
10300
+ );
10301
+ logger.newline();
10302
+ entityType = await input({
10303
+ message: "Primary entity type (e.g., product, article, user, listing):",
10304
+ default: options?.defaults?.entityType || "item",
10305
+ validate: (value) => {
10306
+ if (!value.trim()) return "Entity type is required";
10307
+ return true;
10308
+ }
10309
+ });
10310
+ entityTypePlural = await input({
10311
+ message: "Entity type plural:",
10312
+ default: options?.defaults?.entityTypePlural || pluralize(entityType)
10313
+ });
10314
+ } else if (options?.defaults?.entityType) {
10315
+ entityType = options.defaults.entityType;
10316
+ entityTypePlural = options.defaults.entityTypePlural || pluralize(entityType);
10317
+ }
10318
+ let domain;
10319
+ let location;
10320
+ if (!options?.skipOptional) {
10321
+ const wantDomain = await confirm({
10322
+ message: "Do you want to specify a domain?",
10323
+ default: false
10324
+ });
10325
+ if (wantDomain) {
10326
+ domain = await input({
10327
+ message: "Domain (e.g., myproject.com):",
10328
+ default: options?.defaults?.domain || "",
10329
+ validate: (value) => {
10330
+ if (value && !/^[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(value)) {
10331
+ return "Please enter a valid domain";
10332
+ }
10333
+ return true;
10334
+ }
10335
+ });
10336
+ }
10337
+ const wantLocation = await confirm({
10338
+ message: "Do you want to specify a location/region?",
10339
+ default: false
10340
+ });
10341
+ if (wantLocation) {
10342
+ location = await input({
10343
+ message: "Location/Region:",
10344
+ default: options?.defaults?.location || ""
10345
+ });
10346
+ }
9805
10347
  }
9806
- return { allow, deny };
10348
+ return {
10349
+ name: name.trim(),
10350
+ description: description.trim(),
10351
+ org: org.trim(),
10352
+ repo: repo.trim(),
10353
+ domain,
10354
+ entityType: entityType.trim().toLowerCase(),
10355
+ entityTypePlural: entityTypePlural.trim().toLowerCase(),
10356
+ location,
10357
+ author: author.trim() || void 0
10358
+ };
10359
+ }
10360
+ function pluralize(word) {
10361
+ const lower = word.toLowerCase();
10362
+ if (lower.endsWith("y")) {
10363
+ return `${lower.slice(0, -1)}ies`;
10364
+ }
10365
+ if (lower.endsWith("s") || lower.endsWith("x") || lower.endsWith("ch") || lower.endsWith("sh")) {
10366
+ return `${lower}es`;
10367
+ }
10368
+ return `${lower}s`;
10369
+ }
10370
+ async function confirmProjectInfo(info) {
10371
+ logger.newline();
10372
+ logger.subtitle("Project Summary");
10373
+ logger.keyValue("Name", info.name);
10374
+ logger.keyValue("Description", info.description);
10375
+ logger.keyValue("GitHub", `${info.org}/${info.repo}`);
10376
+ if (info.author) logger.keyValue("Author", info.author);
10377
+ logger.keyValue("Entity", `${info.entityType} / ${info.entityTypePlural}`);
10378
+ if (info.domain) logger.keyValue("Domain", info.domain);
10379
+ if (info.location) logger.keyValue("Location", info.location);
10380
+ logger.newline();
10381
+ return confirm({
10382
+ message: "Is this information correct?",
10383
+ default: true
10384
+ });
9807
10385
  }
9808
10386
 
9809
- // src/cli/prompts/update.ts
10387
+ // src/cli/prompts/scaffold.ts
9810
10388
  init_esm_shims();
9811
- async function promptUpdateAction() {
9812
- logger.subtitle("Update Options");
10389
+ async function promptScaffoldType(options) {
10390
+ logger.section("Configuration Scope", "\u{1F3D7}\uFE0F");
10391
+ if (options?.existingProject) {
10392
+ logger.info("Existing project detected");
10393
+ logger.newline();
10394
+ const choice = await select({
10395
+ message: "What would you like to do?",
10396
+ choices: [
10397
+ {
10398
+ name: "Add Claude config only (recommended)",
10399
+ value: "claude-only",
10400
+ description: "Creates .claude/ folder without touching existing files"
10401
+ },
10402
+ {
10403
+ name: "Full project setup (\u26A0\uFE0F may overwrite files)",
10404
+ value: "full-project",
10405
+ description: "Includes project scaffolding - use with caution"
10406
+ }
10407
+ ],
10408
+ default: "claude-only"
10409
+ });
10410
+ if (choice === "full-project") {
10411
+ const confirmed = await confirm({
10412
+ message: "\u26A0\uFE0F This may overwrite existing files. Are you sure?",
10413
+ default: false
10414
+ });
10415
+ if (!confirmed) {
10416
+ return "claude-only";
10417
+ }
10418
+ }
10419
+ return choice;
10420
+ }
9813
10421
  return select({
9814
- message: "What would you like to update?",
10422
+ message: "What would you like to configure?",
9815
10423
  choices: [
9816
10424
  {
9817
- name: "Check for updates",
9818
- value: "check",
9819
- description: "Show what has changed without modifying files"
9820
- },
9821
- {
9822
- name: "Update modules only",
9823
- value: "modules",
9824
- description: "Update agents, skills, commands, docs"
9825
- },
9826
- {
9827
- name: "Reconfigure settings",
9828
- value: "config",
9829
- description: "Re-run configuration for MCP, hooks, preferences"
9830
- },
9831
- {
9832
- name: "Full update",
9833
- value: "all",
9834
- description: "Update everything"
10425
+ name: "Claude configuration only",
10426
+ value: "claude-only",
10427
+ description: "Only creates .claude/ folder with agents, commands, skills, docs"
9835
10428
  },
9836
10429
  {
9837
- name: "Cancel",
9838
- value: "cancel"
10430
+ name: "Full project setup",
10431
+ value: "full-project",
10432
+ description: "Creates project structure + Claude configuration"
9839
10433
  }
9840
10434
  ],
9841
- default: "check"
10435
+ default: "claude-only"
9842
10436
  });
9843
10437
  }
9844
- function showUpdateReport(updates) {
9845
- logger.newline();
9846
- logger.title("Update Report");
9847
- if (updates.new.length === 0 && updates.updated.length === 0 && updates.deprecated.length === 0) {
9848
- logger.success("Everything is up to date!");
9849
- return;
9850
- }
9851
- if (updates.new.length > 0) {
9852
- logger.newline();
9853
- logger.info(colors.bold(`\u271A New modules available (${updates.new.length}):`));
9854
- for (const mod of updates.new) {
9855
- logger.item(`${mod.category}: ${colors.primary(mod.name)}`);
9856
- }
9857
- }
9858
- if (updates.updated.length > 0) {
9859
- logger.newline();
9860
- logger.info(colors.bold(`\u21BB Updated modules (${updates.updated.length}):`));
9861
- for (const mod of updates.updated) {
9862
- const conflict = mod.hasLocalChanges ? colors.warning(" (local changes)") : "";
9863
- logger.item(`${mod.category}: ${colors.primary(mod.name)}${conflict}`);
9864
- }
9865
- }
9866
- if (updates.deprecated.length > 0) {
9867
- logger.newline();
9868
- logger.info(colors.bold(`\u2717 Deprecated modules (${updates.deprecated.length}):`));
9869
- for (const mod of updates.deprecated) {
9870
- logger.item(`${mod.category}: ${colors.muted(mod.name)}`);
10438
+ async function promptProjectType(detectedType) {
10439
+ const choices = [
10440
+ {
10441
+ name: "Node.js (TypeScript)",
10442
+ value: "node",
10443
+ description: "Basic Node.js project with TypeScript"
10444
+ },
10445
+ {
10446
+ name: "Monorepo (TurboRepo + pnpm)",
10447
+ value: "monorepo",
10448
+ description: "Multi-package monorepo setup"
10449
+ },
10450
+ {
10451
+ name: "Astro",
10452
+ value: "astro",
10453
+ description: "Astro static/SSR site"
10454
+ },
10455
+ {
10456
+ name: "Next.js",
10457
+ value: "nextjs",
10458
+ description: "Next.js full-stack React framework"
10459
+ },
10460
+ {
10461
+ name: "Vite + React",
10462
+ value: "vite-react",
10463
+ description: "Vite bundled React SPA"
10464
+ },
10465
+ {
10466
+ name: "Hono API",
10467
+ value: "hono",
10468
+ description: "Hono lightweight API server"
10469
+ },
10470
+ {
10471
+ name: "Custom",
10472
+ value: "custom",
10473
+ description: "Define your own project structure"
9871
10474
  }
9872
- }
9873
- if (updates.conflicts.length > 0) {
9874
- logger.newline();
9875
- logger.warn(`\u26A0 Modules with conflicts (${updates.conflicts.length}):`);
9876
- for (const mod of updates.conflicts) {
9877
- logger.item(`${mod.category}: ${colors.warning(mod.name)} - has local modifications`);
10475
+ ];
10476
+ if (detectedType) {
10477
+ const detected = choices.find((c) => c.value === detectedType);
10478
+ if (detected) {
10479
+ detected.name = `${detected.name} (detected)`;
9878
10480
  }
9879
10481
  }
9880
- }
9881
- async function promptNewModules(modules) {
9882
- if (modules.length === 0) return [];
9883
- logger.newline();
9884
- const choices = modules.map((mod) => ({
9885
- name: `${mod.category}: ${mod.name}`,
9886
- value: mod.id,
9887
- checked: false
9888
- }));
9889
- return checkbox({
9890
- message: "Select new modules to install:",
9891
- choices
9892
- });
9893
- }
9894
- async function promptUpdatedModules(modules) {
9895
- if (modules.length === 0) return [];
9896
- logger.newline();
9897
- const choices = modules.map((mod) => ({
9898
- name: `${mod.category}: ${mod.name}${mod.hasLocalChanges ? " \u26A0\uFE0F" : ""}`,
9899
- value: mod.id,
9900
- checked: !mod.hasLocalChanges
9901
- // Don't check by default if has local changes
9902
- }));
9903
- return checkbox({
9904
- message: "Select modules to update:",
9905
- choices
10482
+ return select({
10483
+ message: "Project type:",
10484
+ choices,
10485
+ default: detectedType || "node"
9906
10486
  });
9907
10487
  }
9908
- async function promptConflictResolution(module) {
9909
- logger.newline();
9910
- logger.warn(`Conflict: ${module.category}/${module.name} has local modifications`);
10488
+ async function promptPackageManager(detected) {
10489
+ const choices = [
10490
+ { name: "pnpm (recommended)", value: "pnpm" },
10491
+ { name: "npm", value: "npm" },
10492
+ { name: "yarn", value: "yarn" },
10493
+ { name: "bun", value: "bun" }
10494
+ ];
10495
+ if (detected) {
10496
+ const detectedChoice = choices.find((c) => c.value === detected);
10497
+ if (detectedChoice) {
10498
+ detectedChoice.name = `${detectedChoice.name} (detected)`;
10499
+ }
10500
+ }
9911
10501
  return select({
9912
- message: "How do you want to resolve this conflict?",
9913
- choices: [
9914
- {
9915
- name: "Keep local version",
9916
- value: "keep",
9917
- description: "Do not update this module"
9918
- },
9919
- {
9920
- name: "Use updated version",
9921
- value: "update",
9922
- description: "Replace with new version (lose local changes)"
9923
- },
9924
- {
9925
- name: "Show diff",
9926
- value: "diff",
9927
- description: "See what changed before deciding"
9928
- }
9929
- ],
9930
- default: "keep"
10502
+ message: "Package manager:",
10503
+ choices,
10504
+ default: detected || "pnpm"
9931
10505
  });
9932
10506
  }
9933
- async function promptReconfigureOptions() {
10507
+ async function promptScaffoldOptions(options) {
10508
+ const type = await promptScaffoldType(options);
10509
+ if (type === "claude-only") {
10510
+ return { type };
10511
+ }
10512
+ const projectType = await promptProjectType(options?.detectedType);
10513
+ const packageManager = await promptPackageManager(options?.detectedPackageManager);
9934
10514
  logger.newline();
9935
- return checkbox({
9936
- message: "What would you like to reconfigure?",
10515
+ logger.subtitle("Additional Options");
10516
+ const additionalOptions = await checkbox({
10517
+ message: "Select additional options:",
9937
10518
  choices: [
9938
- { name: "MCP servers", value: "mcp", checked: false },
9939
- { name: "Hooks", value: "hooks", checked: false },
9940
- { name: "Preferences (language, co-author)", value: "preferences", checked: false },
9941
- { name: "Permissions", value: "permissions", checked: false },
9942
- { name: "Add/remove modules", value: "modules", checked: false }
10519
+ { name: "Initialize git repository", value: "initGit", checked: true },
10520
+ { name: "Create README.md", value: "createReadme", checked: true },
10521
+ { name: "Create .gitignore", value: "createGitignore", checked: true }
9943
10522
  ]
9944
10523
  });
10524
+ return {
10525
+ type,
10526
+ projectType,
10527
+ packageManager,
10528
+ initGit: additionalOptions.includes("initGit"),
10529
+ createReadme: additionalOptions.includes("createReadme"),
10530
+ createGitignore: additionalOptions.includes("createGitignore")
10531
+ };
9945
10532
  }
9946
10533
 
9947
- // src/cli/prompts/confirm.ts
10534
+ // src/cli/prompts/template-config.ts
9948
10535
  init_esm_shims();
9949
- async function promptExistingProjectAction() {
9950
- return select({
9951
- message: "Existing .claude/ folder detected. What would you like to do?",
10536
+ var CATEGORY_INFO = {
10537
+ commands: { name: "CLI Commands", order: 1 },
10538
+ paths: { name: "Directory Paths", order: 2 },
10539
+ targets: { name: "Quality Targets", order: 3 },
10540
+ tracking: { name: "Issue Tracking", order: 4 },
10541
+ techStack: { name: "Technology Stack", order: 5 },
10542
+ performance: { name: "Performance Targets", order: 6 },
10543
+ environment: { name: "Environment Variables", order: 7 },
10544
+ brand: { name: "Brand Identity", order: 8 }
10545
+ };
10546
+ async function promptSetupMode() {
10547
+ const mode = await select({
10548
+ message: "Configuration mode:",
9952
10549
  choices: [
9953
10550
  {
9954
- name: "Skip (keep existing)",
9955
- value: "skip",
9956
- description: "Do not modify existing configuration"
10551
+ name: "Quick Setup (recommended)",
10552
+ value: "quick",
10553
+ description: "Auto-detect values, confirm all at once"
9957
10554
  },
9958
10555
  {
9959
- name: "Overwrite",
9960
- value: "overwrite",
9961
- description: "Replace existing configuration"
10556
+ name: "Guided Setup",
10557
+ value: "guided",
10558
+ description: "Step through each category"
9962
10559
  },
9963
10560
  {
9964
- name: "Merge",
9965
- value: "merge",
9966
- description: "Add new items, keep existing"
10561
+ name: "Advanced Setup",
10562
+ value: "advanced",
10563
+ description: "Configure all options individually"
9967
10564
  }
9968
10565
  ],
9969
- default: "skip"
9970
- });
9971
- }
9972
- function showFinalSummary(config) {
9973
- logger.newline();
9974
- logger.title("Configuration Summary");
9975
- logger.subtitle("Project");
9976
- logger.keyValue("Name", config.project.name);
9977
- logger.keyValue("GitHub", `${config.project.org}/${config.project.repo}`);
9978
- if (config.project.domain) {
9979
- logger.keyValue("Domain", config.project.domain);
9980
- }
9981
- logger.newline();
9982
- logger.subtitle("Preferences");
9983
- logger.keyValue("Language", config.preferences.language === "en" ? "English" : "Espa\xF1ol");
9984
- logger.keyValue("Co-author", config.preferences.includeCoAuthor ? "Yes" : "No");
9985
- logger.newline();
9986
- logger.subtitle("Modules");
9987
- const moduleCategories = ["agents", "skills", "commands", "docs"];
9988
- for (const category of moduleCategories) {
9989
- const selected = config.modules[category].selected;
9990
- if (selected.length > 0) {
9991
- logger.keyValue(capitalize2(category), `${selected.length} selected`);
9992
- logger.note(selected.join(", "));
9993
- }
9994
- }
9995
- logger.newline();
9996
- logger.subtitle("Extras");
9997
- const extras = [];
9998
- if (config.extras.schemas) extras.push("schemas");
9999
- if (config.extras.scripts) extras.push("scripts");
10000
- if (config.extras.hooks.enabled) extras.push("hooks");
10001
- if (config.extras.sessions) extras.push("sessions");
10002
- logger.keyValue("Included", extras.join(", ") || "none");
10003
- if (config.extras.codeStyle?.enabled) {
10004
- const codeStyleTools = [];
10005
- if (config.extras.codeStyle.editorconfig) codeStyleTools.push("EditorConfig");
10006
- if (config.extras.codeStyle.commitlint) codeStyleTools.push("Commitlint");
10007
- if (config.extras.codeStyle.biome) codeStyleTools.push("Biome");
10008
- if (config.extras.codeStyle.prettier) codeStyleTools.push("Prettier");
10009
- logger.keyValue("Code Style", codeStyleTools.join(", "));
10010
- }
10011
- if (config.mcp.servers.length > 0) {
10012
- logger.newline();
10013
- logger.subtitle("MCP Servers");
10014
- logger.keyValue("Level", config.mcp.level);
10015
- logger.keyValue("Servers", config.mcp.servers.map((s) => s.serverId).join(", "));
10016
- }
10017
- logger.newline();
10018
- logger.subtitle("Scaffold");
10019
- logger.keyValue(
10020
- "Type",
10021
- config.scaffold.type === "claude-only" ? "Claude config only" : "Full project"
10022
- );
10023
- }
10024
- async function confirmFinalConfiguration(config) {
10025
- showFinalSummary(config);
10026
- logger.newline();
10027
- return confirm({
10028
- message: "Proceed with this configuration?",
10029
- default: true
10566
+ default: "quick"
10030
10567
  });
10568
+ return mode;
10031
10569
  }
10032
- function showPostInstallInstructions(config) {
10033
- logger.newline();
10034
- logger.title("Installation Complete!");
10035
- const steps = [];
10036
- if (config.scaffold.type === "full-project") {
10037
- steps.push("Review the generated project structure");
10038
- }
10039
- steps.push("Review .claude/CLAUDE.md for agent instructions");
10040
- steps.push("Customize agents, commands, and skills as needed");
10041
- if (config.mcp.servers.length > 0) {
10042
- const mcpFile = config.mcp.level === "user" ? "~/.claude/settings.json" : ".claude/settings.local.json";
10043
- steps.push(`Configure MCP server credentials in ${mcpFile}`);
10044
- }
10045
- if (config.extras.hooks.enabled) {
10046
- steps.push("Review hook scripts in .claude/hooks/");
10047
- if (config.extras.hooks.notification?.audio) {
10048
- steps.push("Install Piper TTS for audio notifications (see docs)");
10049
- }
10050
- }
10051
- logger.instructions("Next Steps:", steps);
10052
- logger.subtitle("Useful Commands");
10053
- logger.raw("");
10054
- logger.raw(` ${colors.primary("claude-config status")} - Show current configuration`);
10055
- logger.raw(` ${colors.primary("claude-config list")} - List available modules`);
10056
- logger.raw(` ${colors.primary("claude-config add")} - Add a module`);
10057
- logger.raw(` ${colors.primary("claude-config update")} - Update configuration`);
10058
- logger.raw("");
10059
- }
10060
- function capitalize2(str) {
10061
- return str.charAt(0).toUpperCase() + str.slice(1);
10062
- }
10063
-
10064
- // src/cli/prompts/code-style.ts
10065
- init_esm_shims();
10066
- var CODE_STYLE_TOOLS = [
10067
- {
10068
- name: "EditorConfig",
10069
- value: "editorconfig",
10070
- description: "Consistent coding styles across editors",
10071
- checked: true
10072
- },
10073
- {
10074
- name: "Commitlint",
10075
- value: "commitlint",
10076
- description: "Lint commit messages (conventional commits)",
10077
- checked: true
10078
- },
10079
- {
10080
- name: "Biome",
10081
- value: "biome",
10082
- description: "Fast linter and formatter (ESLint + Prettier alternative)",
10083
- checked: false
10084
- },
10085
- {
10086
- name: "Prettier",
10087
- value: "prettier",
10088
- description: "Code formatter (use if not using Biome)",
10089
- checked: false
10090
- }
10091
- ];
10092
- async function promptCodeStyleConfig(options) {
10093
- logger.section("Code Style", "\u{1F3A8}");
10094
- logger.info("Configure code formatting and linting tools");
10095
- logger.newline();
10096
- const enableCodeStyle = await confirm({
10097
- message: "Would you like to install code style configuration files?",
10098
- default: true
10099
- });
10100
- if (!enableCodeStyle) {
10101
- return {
10102
- enabled: false,
10103
- editorconfig: false,
10104
- commitlint: false,
10105
- biome: false,
10106
- prettier: false
10570
+ async function buildConfigContext(projectPath) {
10571
+ const context = {
10572
+ projectPath,
10573
+ values: {}
10574
+ };
10575
+ try {
10576
+ const fs6 = await import("fs/promises");
10577
+ const path8 = await import("path");
10578
+ const pkgPath = path8.join(projectPath, "package.json");
10579
+ const pkgContent = await fs6.readFile(pkgPath, "utf-8");
10580
+ const pkg = JSON.parse(pkgContent);
10581
+ context.scripts = pkg.scripts || {};
10582
+ context.dependencies = {
10583
+ ...pkg.dependencies || {},
10584
+ ...pkg.devDependencies || {}
10107
10585
  };
10108
- }
10109
- const selectedTools = await checkbox({
10110
- message: "Select the tools to configure:",
10111
- choices: CODE_STYLE_TOOLS.map((tool) => ({
10112
- name: `${tool.name} - ${tool.description}`,
10113
- value: tool.value,
10114
- checked: options?.defaults?.[tool.value] ?? tool.checked
10115
- }))
10116
- });
10117
- if (selectedTools.includes("biome") && selectedTools.includes("prettier")) {
10118
- logger.warn("Note: Both Biome and Prettier selected. Biome can replace Prettier.");
10119
- const keepBoth = await confirm({
10120
- message: "Keep both? (Prettier may conflict with Biome)",
10121
- default: false
10122
- });
10123
- if (!keepBoth) {
10124
- const preferred = await select({
10125
- message: "Which formatter would you prefer?",
10126
- choices: [
10127
- { name: "Biome (faster, all-in-one)", value: "biome" },
10128
- { name: "Prettier (more plugins)", value: "prettier" }
10129
- ]
10130
- });
10131
- const indexToRemove = preferred === "biome" ? selectedTools.indexOf("prettier") : selectedTools.indexOf("biome");
10132
- if (indexToRemove > -1) {
10133
- selectedTools.splice(indexToRemove, 1);
10586
+ context.hasTypeScript = Boolean(context.dependencies.typescript) || await fileExists(path8.join(projectPath, "tsconfig.json"));
10587
+ if (await fileExists(path8.join(projectPath, "pnpm-lock.yaml"))) {
10588
+ context.packageManager = "pnpm";
10589
+ } else if (await fileExists(path8.join(projectPath, "yarn.lock"))) {
10590
+ context.packageManager = "yarn";
10591
+ } else if (await fileExists(path8.join(projectPath, "bun.lockb"))) {
10592
+ context.packageManager = "bun";
10593
+ } else {
10594
+ context.packageManager = "npm";
10595
+ }
10596
+ context.isGitRepo = await fileExists(path8.join(projectPath, ".git"));
10597
+ if (context.isGitRepo) {
10598
+ try {
10599
+ const { execSync } = await import("child_process");
10600
+ const remotes = execSync("git remote -v", {
10601
+ cwd: projectPath,
10602
+ encoding: "utf-8"
10603
+ });
10604
+ context.hasGitHubRemote = remotes.includes("github.com");
10605
+ } catch {
10606
+ context.hasGitHubRemote = false;
10134
10607
  }
10135
10608
  }
10609
+ } catch {
10136
10610
  }
10137
- if (selectedTools.length === 0) {
10138
- return {
10139
- enabled: false,
10140
- editorconfig: false,
10141
- commitlint: false,
10142
- biome: false,
10143
- prettier: false
10144
- };
10611
+ return context;
10612
+ }
10613
+ async function fileExists(filePath) {
10614
+ try {
10615
+ const fs6 = await import("fs/promises");
10616
+ await fs6.access(filePath);
10617
+ return true;
10618
+ } catch {
10619
+ return false;
10145
10620
  }
10146
- const customizeSettings = await confirm({
10147
- message: "Would you like to customize code style settings? (No = use standard defaults)",
10148
- default: false
10149
- });
10150
- let editorconfigOptions;
10151
- let biomeOptions;
10152
- let prettierOptions;
10153
- let commitlintOptions;
10154
- if (customizeSettings) {
10155
- const hasFormatter = selectedTools.includes("editorconfig") || selectedTools.includes("biome") || selectedTools.includes("prettier");
10156
- if (hasFormatter) {
10157
- const preset = await promptStylePreset();
10158
- if (preset !== "custom") {
10159
- const presetConfig = CODE_STYLE_PRESETS[preset];
10160
- if (selectedTools.includes("editorconfig")) {
10161
- editorconfigOptions = presetConfig.editorconfig;
10162
- }
10163
- if (selectedTools.includes("biome") && presetConfig.biome) {
10164
- biomeOptions = {
10165
- ...DEFAULT_BIOME_OPTIONS,
10166
- formatter: presetConfig.biome
10167
- };
10168
- }
10169
- if (selectedTools.includes("prettier") && presetConfig.prettier) {
10170
- prettierOptions = presetConfig.prettier;
10171
- }
10172
- } else {
10173
- if (selectedTools.includes("editorconfig")) {
10174
- editorconfigOptions = await promptEditorConfigOptions();
10175
- }
10176
- if (selectedTools.includes("biome")) {
10177
- biomeOptions = await promptBiomeOptions();
10178
- }
10179
- if (selectedTools.includes("prettier")) {
10180
- prettierOptions = await promptPrettierOptions();
10181
- }
10621
+ }
10622
+ async function promptPlaceholder(placeholder, context, detectedValue) {
10623
+ const defaultValue = detectedValue || computeDefaultValue(placeholder, context) || "";
10624
+ switch (placeholder.inputType) {
10625
+ case "select": {
10626
+ if (!placeholder.choices || placeholder.choices.length === 0) {
10627
+ return input({
10628
+ message: `${placeholder.label}:`,
10629
+ default: defaultValue
10630
+ });
10182
10631
  }
10632
+ const choices = placeholder.choices.map((c) => ({
10633
+ name: c.name,
10634
+ value: c.value,
10635
+ description: c.description
10636
+ }));
10637
+ return select({
10638
+ message: `${placeholder.label}:`,
10639
+ choices,
10640
+ default: defaultValue || choices[0].value
10641
+ });
10183
10642
  }
10184
- if (selectedTools.includes("commitlint")) {
10185
- commitlintOptions = await promptCommitlintOptions();
10186
- }
10187
- } else {
10188
- if (selectedTools.includes("editorconfig")) {
10189
- editorconfigOptions = DEFAULT_EDITORCONFIG_OPTIONS;
10643
+ case "number": {
10644
+ const result = await input({
10645
+ message: `${placeholder.label}:`,
10646
+ default: defaultValue,
10647
+ validate: (value) => {
10648
+ if (placeholder.validate) {
10649
+ return placeholder.validate(value, context);
10650
+ }
10651
+ const num = Number(value);
10652
+ if (Number.isNaN(num)) {
10653
+ return "Please enter a valid number";
10654
+ }
10655
+ return true;
10656
+ }
10657
+ });
10658
+ return result;
10190
10659
  }
10191
- if (selectedTools.includes("biome")) {
10192
- biomeOptions = DEFAULT_BIOME_OPTIONS;
10660
+ case "path": {
10661
+ return input({
10662
+ message: `${placeholder.label}:`,
10663
+ default: defaultValue,
10664
+ validate: (value) => {
10665
+ if (placeholder.required && !value.trim()) {
10666
+ return "This path is required";
10667
+ }
10668
+ if (placeholder.validate) {
10669
+ return placeholder.validate(value, context);
10670
+ }
10671
+ return true;
10672
+ }
10673
+ });
10193
10674
  }
10194
- if (selectedTools.includes("prettier")) {
10195
- prettierOptions = DEFAULT_PRETTIER_OPTIONS;
10675
+ case "envVar": {
10676
+ return input({
10677
+ message: `${placeholder.label}:`,
10678
+ default: defaultValue,
10679
+ validate: (value) => {
10680
+ if (placeholder.required && !value.trim()) {
10681
+ return "This environment variable is required";
10682
+ }
10683
+ if (value && !/^[A-Z][A-Z0-9_]*$/.test(value)) {
10684
+ return "Environment variable should be UPPER_SNAKE_CASE";
10685
+ }
10686
+ return true;
10687
+ }
10688
+ });
10196
10689
  }
10197
- if (selectedTools.includes("commitlint")) {
10198
- commitlintOptions = DEFAULT_COMMITLINT_OPTIONS;
10690
+ default: {
10691
+ return input({
10692
+ message: `${placeholder.label}:`,
10693
+ default: defaultValue,
10694
+ validate: (value) => {
10695
+ if (placeholder.required && !value.trim()) {
10696
+ return `${placeholder.label} is required`;
10697
+ }
10698
+ if (placeholder.validate) {
10699
+ return placeholder.validate(value, context);
10700
+ }
10701
+ return true;
10702
+ }
10703
+ });
10199
10704
  }
10200
10705
  }
10201
- return {
10202
- enabled: selectedTools.length > 0,
10203
- editorconfig: selectedTools.includes("editorconfig"),
10204
- editorconfigOptions,
10205
- commitlint: selectedTools.includes("commitlint"),
10206
- commitlintOptions,
10207
- biome: selectedTools.includes("biome"),
10208
- biomeOptions,
10209
- prettier: selectedTools.includes("prettier"),
10210
- prettierOptions
10211
- };
10212
- }
10213
- async function promptStylePreset() {
10214
- logger.newline();
10215
- logger.info("Choose a code style preset:");
10216
- return select({
10217
- message: "Style preset:",
10218
- choices: Object.entries(CODE_STYLE_PRESETS).map(([key, preset]) => ({
10219
- name: `${preset.name} - ${preset.description}`,
10220
- value: key
10221
- })),
10222
- default: "standard"
10223
- });
10224
10706
  }
10225
- async function promptEditorConfigOptions() {
10226
- logger.newline();
10227
- logger.subtitle("EditorConfig Options");
10228
- const indentStyle = await select({
10229
- message: "Indent style:",
10230
- choices: [
10231
- { name: "Spaces", value: "space" },
10232
- { name: "Tabs", value: "tab" }
10233
- ],
10234
- default: DEFAULT_EDITORCONFIG_OPTIONS.indentStyle
10235
- });
10236
- const indentSizeStr = await input({
10237
- message: "Indent size:",
10238
- default: String(DEFAULT_EDITORCONFIG_OPTIONS.indentSize),
10239
- validate: (v) => {
10240
- const num = Number(v);
10241
- if (Number.isNaN(num) || num < 1 || num > 8) return "Enter a number between 1 and 8";
10242
- return true;
10243
- }
10244
- });
10245
- const indentSize = Number(indentSizeStr);
10246
- const endOfLine = await select({
10247
- message: "Line endings:",
10248
- choices: [
10249
- { name: "LF (Unix/Mac)", value: "lf" },
10250
- { name: "CRLF (Windows)", value: "crlf" },
10251
- { name: "CR (Old Mac)", value: "cr" }
10252
- ],
10253
- default: DEFAULT_EDITORCONFIG_OPTIONS.endOfLine
10254
- });
10255
- const maxLineLengthStr = await input({
10256
- message: 'Max line length (or "off"):',
10257
- default: String(DEFAULT_EDITORCONFIG_OPTIONS.maxLineLength),
10258
- validate: (v) => {
10259
- if (v.toLowerCase() === "off") return true;
10260
- const num = Number(v);
10261
- if (Number.isNaN(num) || num < 40 || num > 200)
10262
- return 'Enter a number between 40 and 200, or "off"';
10263
- return true;
10264
- }
10265
- });
10266
- const maxLineLength = maxLineLengthStr.toLowerCase() === "off" ? "off" : Number(maxLineLengthStr);
10267
- return {
10268
- indentStyle,
10269
- indentSize,
10270
- endOfLine,
10271
- insertFinalNewline: true,
10272
- trimTrailingWhitespace: true,
10273
- charset: "utf-8",
10274
- maxLineLength
10707
+ function computeAllDefaults(context, globalDefaults) {
10708
+ const config = {
10709
+ commands: {},
10710
+ paths: {},
10711
+ targets: {},
10712
+ tracking: {},
10713
+ techStack: {},
10714
+ environment: {},
10715
+ brand: {}
10275
10716
  };
10717
+ for (const placeholder of TEMPLATE_PLACEHOLDERS) {
10718
+ const defaultValue = computeDefaultValue(placeholder, context);
10719
+ if (defaultValue) {
10720
+ setConfigValue(config, placeholder, defaultValue);
10721
+ }
10722
+ }
10723
+ if (globalDefaults) {
10724
+ return mergeWithGlobalDefaults(config, globalDefaults);
10725
+ }
10726
+ return config;
10727
+ }
10728
+ function setConfigValue(config, placeholder, value) {
10729
+ const key = placeholderKeyToConfigKey(placeholder.key, placeholder.category);
10730
+ switch (placeholder.category) {
10731
+ case "commands":
10732
+ if (!config.commands) config.commands = {};
10733
+ config.commands[key] = value;
10734
+ break;
10735
+ case "paths":
10736
+ if (!config.paths) config.paths = {};
10737
+ config.paths[key] = value;
10738
+ break;
10739
+ case "targets":
10740
+ case "performance": {
10741
+ if (!config.targets) config.targets = {};
10742
+ const numValue = Number(value);
10743
+ if (!Number.isNaN(numValue)) {
10744
+ config.targets[key] = numValue;
10745
+ } else {
10746
+ config.targets[key] = value;
10747
+ }
10748
+ break;
10749
+ }
10750
+ case "tracking":
10751
+ if (!config.tracking) config.tracking = {};
10752
+ config.tracking[key] = key.endsWith("Days") ? Number(value) : value;
10753
+ break;
10754
+ case "techStack":
10755
+ if (!config.techStack) config.techStack = {};
10756
+ config.techStack[key] = value;
10757
+ break;
10758
+ case "environment":
10759
+ if (!config.environment) config.environment = {};
10760
+ config.environment[key] = value;
10761
+ break;
10762
+ case "brand":
10763
+ if (!config.brand) config.brand = {};
10764
+ config.brand[key] = value;
10765
+ break;
10766
+ }
10276
10767
  }
10277
- async function promptBiomeOptions() {
10768
+ function placeholderKeyToConfigKey(key, category) {
10769
+ let cleanKey = key;
10770
+ if (category === "commands" && key.endsWith("_COMMAND")) {
10771
+ cleanKey = key.slice(0, -8);
10772
+ } else if (category === "environment" && key.endsWith("_ENV")) {
10773
+ cleanKey = key.slice(0, -4);
10774
+ } else if (key.endsWith("_TARGET")) {
10775
+ cleanKey = key.slice(0, -7);
10776
+ }
10777
+ return cleanKey.toLowerCase().replace(/_([a-z])/g, (_, char) => char.toUpperCase());
10778
+ }
10779
+ async function promptTemplateConfig(options) {
10780
+ const { context, mode = "quick", category, requiredOnly = false } = options;
10781
+ logger.section("Template Configuration", "\u{1F4DD}");
10278
10782
  logger.newline();
10279
- logger.subtitle("Biome Options");
10280
- const indentStyle = await select({
10281
- message: "Indent style:",
10282
- choices: [
10283
- { name: "Spaces", value: "space" },
10284
- { name: "Tabs", value: "tab" }
10285
- ],
10286
- default: DEFAULT_BIOME_OPTIONS.formatter.indentStyle
10287
- });
10288
- const indentWidthStr = await input({
10289
- message: "Indent width:",
10290
- default: String(DEFAULT_BIOME_OPTIONS.formatter.indentWidth),
10291
- validate: (v) => {
10292
- const num = Number(v);
10293
- if (Number.isNaN(num) || num < 1 || num > 8) return "Enter a number between 1 and 8";
10294
- return true;
10295
- }
10296
- });
10297
- const indentWidth = Number(indentWidthStr);
10298
- const lineWidthStr = await input({
10299
- message: "Line width:",
10300
- default: String(DEFAULT_BIOME_OPTIONS.formatter.lineWidth),
10301
- validate: (v) => {
10302
- const num = Number(v);
10303
- if (Number.isNaN(num) || num < 40 || num > 200) return "Enter a number between 40 and 200";
10304
- return true;
10305
- }
10306
- });
10307
- const lineWidth = Number(lineWidthStr);
10308
- const quoteStyle = await select({
10309
- message: "Quote style:",
10310
- choices: [
10311
- { name: "Single quotes", value: "single" },
10312
- { name: "Double quotes", value: "double" }
10313
- ],
10314
- default: DEFAULT_BIOME_OPTIONS.formatter.quoteStyle
10315
- });
10316
- const semicolons = await select({
10317
- message: "Semicolons:",
10318
- choices: [
10319
- { name: "Always", value: "always" },
10320
- { name: "As needed (ASI)", value: "asNeeded" }
10321
- ],
10322
- default: DEFAULT_BIOME_OPTIONS.formatter.semicolons
10323
- });
10324
- const trailingCommas = await select({
10325
- message: "Trailing commas:",
10326
- choices: [
10327
- { name: "All", value: "all" },
10328
- { name: "ES5 (only where valid in ES5)", value: "es5" },
10329
- { name: "None", value: "none" }
10330
- ],
10331
- default: DEFAULT_BIOME_OPTIONS.formatter.trailingCommas
10332
- });
10333
- const enableRecommended = await confirm({
10334
- message: "Enable recommended linter rules?",
10335
- default: true
10336
- });
10337
- return {
10338
- formatter: {
10339
- indentStyle,
10340
- indentWidth,
10341
- lineWidth,
10342
- quoteStyle,
10343
- semicolons,
10344
- trailingCommas,
10345
- quoteProperties: "asNeeded",
10346
- bracketSpacing: true,
10347
- bracketSameLine: false,
10348
- arrowParentheses: "always"
10349
- },
10350
- linter: {
10351
- recommended: enableRecommended,
10352
- correctness: enableRecommended,
10353
- suspicious: enableRecommended,
10354
- style: enableRecommended,
10355
- complexity: enableRecommended,
10356
- security: enableRecommended,
10357
- performance: enableRecommended,
10358
- a11y: enableRecommended
10359
- },
10360
- organizeImports: true,
10361
- ignorePatterns: DEFAULT_BIOME_OPTIONS.ignorePatterns
10362
- };
10783
+ logger.info("This step personalizes all Claude Code configuration files for YOUR project.");
10784
+ logger.info("Your answers will be used to:");
10785
+ logger.newline();
10786
+ logger.item("Replace {{PLACEHOLDERS}} in agents, commands, and skills");
10787
+ logger.item("Configure CLAUDE.md with your tech stack and commands");
10788
+ logger.item("Set up quality targets (coverage, performance, accessibility)");
10789
+ logger.item("Customize code examples to match your project structure");
10790
+ logger.newline();
10791
+ logger.info("Accurate configuration means better AI assistance tailored to your codebase!");
10792
+ logger.newline();
10793
+ const hasDefaults = await hasGlobalDefaults();
10794
+ const globalDefaults = hasDefaults ? await getGlobalTemplateConfig() : void 0;
10795
+ if (mode === "quick") {
10796
+ return promptQuickSetup(context, globalDefaults);
10797
+ }
10798
+ if (category) {
10799
+ return promptCategory(category, context, requiredOnly);
10800
+ }
10801
+ if (mode === "guided") {
10802
+ return promptGuidedSetup(context, requiredOnly);
10803
+ }
10804
+ return promptAdvancedSetup(context);
10363
10805
  }
10364
- async function promptPrettierOptions() {
10806
+ async function promptQuickSetup(context, globalDefaults) {
10807
+ logger.info("Auto-detecting configuration from project...");
10365
10808
  logger.newline();
10366
- logger.subtitle("Prettier Options");
10367
- const printWidthStr = await input({
10368
- message: "Print width:",
10369
- default: String(DEFAULT_PRETTIER_OPTIONS.printWidth),
10370
- validate: (v) => {
10371
- const num = Number(v);
10372
- if (Number.isNaN(num) || num < 40 || num > 200) return "Enter a number between 40 and 200";
10373
- return true;
10374
- }
10809
+ const detected = computeAllDefaults(context, globalDefaults);
10810
+ displayDetectedConfig(detected, context);
10811
+ const wantEdit = await confirm({
10812
+ message: "Would you like to modify any values?",
10813
+ default: false
10375
10814
  });
10376
- const printWidth = Number(printWidthStr);
10377
- const tabWidthStr = await input({
10378
- message: "Tab width:",
10379
- default: String(DEFAULT_PRETTIER_OPTIONS.tabWidth),
10380
- validate: (v) => {
10381
- const num = Number(v);
10382
- if (Number.isNaN(num) || num < 1 || num > 8) return "Enter a number between 1 and 8";
10383
- return true;
10815
+ let finalConfig = detected;
10816
+ if (wantEdit) {
10817
+ finalConfig = await promptEditConfig(detected, context);
10818
+ }
10819
+ return finalConfig;
10820
+ }
10821
+ function displayDetectedConfig(config, _context) {
10822
+ const categories = Object.entries(CATEGORY_INFO).sort(([, a], [, b]) => a.order - b.order).map(([key]) => key);
10823
+ for (const category of categories) {
10824
+ const placeholders = getPlaceholdersByCategory(category);
10825
+ if (placeholders.length === 0) continue;
10826
+ const values = getCategoryValues(config, category);
10827
+ if (Object.keys(values).length === 0) continue;
10828
+ logger.subtitle(CATEGORY_INFO[category].name);
10829
+ for (const [key, value] of Object.entries(values)) {
10830
+ if (value !== void 0) {
10831
+ const placeholder = TEMPLATE_PLACEHOLDERS.find(
10832
+ (p) => placeholderKeyToConfigKey(p.key, p.category) === key
10833
+ );
10834
+ const label = placeholder?.label || key;
10835
+ logger.keyValue(label, String(value));
10836
+ }
10384
10837
  }
10385
- });
10386
- const tabWidth = Number(tabWidthStr);
10387
- const useTabs = await confirm({
10388
- message: "Use tabs instead of spaces?",
10389
- default: DEFAULT_PRETTIER_OPTIONS.useTabs
10390
- });
10391
- const semi = await confirm({
10392
- message: "Use semicolons?",
10393
- default: DEFAULT_PRETTIER_OPTIONS.semi
10394
- });
10395
- const singleQuote = await confirm({
10396
- message: "Use single quotes?",
10397
- default: DEFAULT_PRETTIER_OPTIONS.singleQuote
10398
- });
10399
- const trailingComma = await select({
10400
- message: "Trailing commas:",
10401
- choices: [
10402
- { name: "All", value: "all" },
10403
- { name: "ES5", value: "es5" },
10404
- { name: "None", value: "none" }
10405
- ],
10406
- default: DEFAULT_PRETTIER_OPTIONS.trailingComma
10407
- });
10408
- const bracketSpacing = await confirm({
10409
- message: "Bracket spacing? ({ foo: bar })",
10410
- default: DEFAULT_PRETTIER_OPTIONS.bracketSpacing
10411
- });
10412
- const arrowParens = await select({
10413
- message: "Arrow function parentheses:",
10414
- choices: [
10415
- { name: "Always (x) => x", value: "always" },
10416
- { name: "Avoid x => x", value: "avoid" }
10417
- ],
10418
- default: DEFAULT_PRETTIER_OPTIONS.arrowParens
10419
- });
10420
- return {
10421
- printWidth,
10422
- tabWidth,
10423
- useTabs,
10424
- semi,
10425
- singleQuote,
10426
- jsxSingleQuote: false,
10427
- trailingComma,
10428
- bracketSpacing,
10429
- bracketSameLine: false,
10430
- arrowParens,
10431
- endOfLine: "lf",
10432
- proseWrap: "preserve",
10433
- htmlWhitespaceSensitivity: "css",
10434
- singleAttributePerLine: false
10435
- };
10838
+ logger.newline();
10839
+ }
10840
+ }
10841
+ function getCategoryValues(config, category) {
10842
+ switch (category) {
10843
+ case "commands":
10844
+ return config.commands || {};
10845
+ case "paths":
10846
+ return config.paths || {};
10847
+ case "targets":
10848
+ case "performance":
10849
+ return config.targets || {};
10850
+ case "tracking":
10851
+ return config.tracking || {};
10852
+ case "techStack":
10853
+ return config.techStack || {};
10854
+ case "environment":
10855
+ return config.environment || {};
10856
+ case "brand":
10857
+ return config.brand || {};
10858
+ default:
10859
+ return {};
10860
+ }
10436
10861
  }
10437
- async function promptCommitlintOptions() {
10438
- logger.newline();
10439
- logger.subtitle("Commitlint Options");
10440
- const useDefaults = await confirm({
10441
- message: "Use conventional commits defaults?",
10442
- default: true
10862
+ async function promptEditConfig(config, context) {
10863
+ const categories = Object.entries(CATEGORY_INFO).sort(([, a], [, b]) => a.order - b.order).map(([key]) => key);
10864
+ const categoryToEdit = await select({
10865
+ message: "Which category would you like to edit?",
10866
+ choices: [
10867
+ ...categories.map((cat) => ({
10868
+ name: CATEGORY_INFO[cat].name,
10869
+ value: cat
10870
+ })),
10871
+ { name: "Done editing", value: "done" }
10872
+ ]
10443
10873
  });
10444
- if (useDefaults) {
10445
- const huskyIntegration2 = await confirm({
10446
- message: "Enable Husky integration (git hooks)?",
10447
- default: true
10448
- });
10449
- return {
10450
- ...DEFAULT_COMMITLINT_OPTIONS,
10451
- huskyIntegration: huskyIntegration2
10452
- };
10874
+ if (categoryToEdit === "done") {
10875
+ return config;
10453
10876
  }
10454
- const typesInput = await input({
10455
- message: "Commit types (comma-separated):",
10456
- default: DEFAULT_COMMITLINT_OPTIONS.types.join(", ")
10457
- });
10458
- const types = typesInput.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
10459
- const scopesInput = await input({
10460
- message: "Allowed scopes (comma-separated, empty for any):",
10461
- default: ""
10462
- });
10463
- const scopes = scopesInput.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
10464
- const headerMaxLengthStr = await input({
10465
- message: "Maximum header length:",
10466
- default: String(DEFAULT_COMMITLINT_OPTIONS.headerMaxLength),
10467
- validate: (v) => {
10468
- const num = Number(v);
10469
- if (Number.isNaN(num) || num < 20 || num > 200) return "Enter a number between 20 and 200";
10470
- return true;
10471
- }
10472
- });
10473
- const headerMaxLength = Number(headerMaxLengthStr);
10474
- const scopeRequired = await confirm({
10475
- message: "Require scope in commit messages?",
10877
+ const editedCategory = await promptCategory(categoryToEdit, context, false);
10878
+ const merged = { ...config };
10879
+ switch (categoryToEdit) {
10880
+ case "commands":
10881
+ merged.commands = { ...config.commands, ...editedCategory.commands };
10882
+ break;
10883
+ case "paths":
10884
+ merged.paths = { ...config.paths, ...editedCategory.paths };
10885
+ break;
10886
+ case "targets":
10887
+ case "performance":
10888
+ merged.targets = { ...config.targets, ...editedCategory.targets };
10889
+ break;
10890
+ case "tracking":
10891
+ merged.tracking = { ...config.tracking, ...editedCategory.tracking };
10892
+ break;
10893
+ case "techStack":
10894
+ merged.techStack = { ...config.techStack, ...editedCategory.techStack };
10895
+ break;
10896
+ case "environment":
10897
+ merged.environment = { ...config.environment, ...editedCategory.environment };
10898
+ break;
10899
+ case "brand":
10900
+ merged.brand = { ...config.brand, ...editedCategory.brand };
10901
+ break;
10902
+ }
10903
+ const editMore = await confirm({
10904
+ message: "Edit another category?",
10476
10905
  default: false
10477
10906
  });
10478
- const huskyIntegration = await confirm({
10479
- message: "Enable Husky integration (git hooks)?",
10480
- default: true
10481
- });
10482
- return {
10483
- extends: ["@commitlint/config-conventional"],
10484
- types,
10485
- scopes,
10486
- headerMaxLength,
10487
- scopeRequired,
10488
- bodyRequired: false,
10489
- bodyMaxLineLength: 100,
10490
- huskyIntegration
10491
- };
10907
+ if (editMore) {
10908
+ return promptEditConfig(merged, context);
10909
+ }
10910
+ return merged;
10492
10911
  }
10493
-
10494
- // src/cli/prompts/template-config.ts
10495
- init_esm_shims();
10496
- var CATEGORY_INFO = {
10497
- commands: { name: "CLI Commands", order: 1 },
10498
- paths: { name: "Directory Paths", order: 2 },
10499
- targets: { name: "Quality Targets", order: 3 },
10500
- tracking: { name: "Issue Tracking", order: 4 },
10501
- techStack: { name: "Technology Stack", order: 5 },
10502
- performance: { name: "Performance Targets", order: 6 },
10503
- environment: { name: "Environment Variables", order: 7 },
10504
- brand: { name: "Brand Identity", order: 8 }
10505
- };
10506
- async function promptSetupMode() {
10507
- const mode = await select({
10508
- message: "Configuration mode:",
10509
- choices: [
10510
- {
10511
- name: "Quick Setup (recommended)",
10512
- value: "quick",
10513
- description: "Auto-detect values, confirm all at once"
10514
- },
10515
- {
10516
- name: "Guided Setup",
10517
- value: "guided",
10518
- description: "Step through each category"
10519
- },
10520
- {
10521
- name: "Advanced Setup",
10522
- value: "advanced",
10523
- description: "Configure all options individually"
10524
- }
10525
- ],
10526
- default: "quick"
10527
- });
10528
- return mode;
10912
+ async function promptCategory(category, context, requiredOnly) {
10913
+ const config = {
10914
+ commands: {},
10915
+ paths: {},
10916
+ targets: {},
10917
+ tracking: {},
10918
+ techStack: {},
10919
+ environment: {},
10920
+ brand: {}
10921
+ };
10922
+ logger.subtitle(CATEGORY_INFO[category].name);
10923
+ let placeholders = getPlaceholdersByCategory(category);
10924
+ if (requiredOnly) {
10925
+ placeholders = placeholders.filter((p) => p.required);
10926
+ }
10927
+ const updatedContext = { ...context };
10928
+ for (const placeholder of placeholders) {
10929
+ const value = await promptPlaceholder(placeholder, updatedContext);
10930
+ if (value) {
10931
+ setConfigValue(config, placeholder, value);
10932
+ updatedContext.values[placeholder.key] = value;
10933
+ }
10934
+ }
10935
+ return config;
10529
10936
  }
10530
- async function buildConfigContext(projectPath) {
10531
- const context = {
10532
- projectPath,
10533
- values: {}
10937
+ async function promptGuidedSetup(context, requiredOnly) {
10938
+ const config = {
10939
+ commands: {},
10940
+ paths: {},
10941
+ targets: {},
10942
+ tracking: {},
10943
+ techStack: {},
10944
+ environment: {},
10945
+ brand: {}
10534
10946
  };
10535
- try {
10536
- const fs6 = await import("fs/promises");
10537
- const path8 = await import("path");
10538
- const pkgPath = path8.join(projectPath, "package.json");
10539
- const pkgContent = await fs6.readFile(pkgPath, "utf-8");
10540
- const pkg = JSON.parse(pkgContent);
10541
- context.scripts = pkg.scripts || {};
10542
- context.dependencies = {
10543
- ...pkg.dependencies || {},
10544
- ...pkg.devDependencies || {}
10545
- };
10546
- context.hasTypeScript = Boolean(context.dependencies.typescript) || await fileExists(path8.join(projectPath, "tsconfig.json"));
10547
- if (await fileExists(path8.join(projectPath, "pnpm-lock.yaml"))) {
10548
- context.packageManager = "pnpm";
10549
- } else if (await fileExists(path8.join(projectPath, "yarn.lock"))) {
10550
- context.packageManager = "yarn";
10551
- } else if (await fileExists(path8.join(projectPath, "bun.lockb"))) {
10552
- context.packageManager = "bun";
10553
- } else {
10554
- context.packageManager = "npm";
10947
+ const categories = Object.entries(CATEGORY_INFO).sort(([, a], [, b]) => a.order - b.order).map(([key]) => key);
10948
+ const updatedContext = { ...context };
10949
+ for (const category of categories) {
10950
+ const placeholders = getPlaceholdersByCategory(category);
10951
+ const relevantPlaceholders = requiredOnly ? placeholders.filter((p) => p.required) : placeholders;
10952
+ if (relevantPlaceholders.length === 0) continue;
10953
+ const configureCategory = await confirm({
10954
+ message: `Configure ${CATEGORY_INFO[category].name}? (${relevantPlaceholders.length} options)`,
10955
+ default: true
10956
+ });
10957
+ if (!configureCategory) continue;
10958
+ const categoryConfig = await promptCategory(category, updatedContext, requiredOnly);
10959
+ switch (category) {
10960
+ case "commands":
10961
+ config.commands = { ...config.commands, ...categoryConfig.commands };
10962
+ break;
10963
+ case "paths":
10964
+ config.paths = { ...config.paths, ...categoryConfig.paths };
10965
+ break;
10966
+ case "targets":
10967
+ case "performance":
10968
+ config.targets = { ...config.targets, ...categoryConfig.targets };
10969
+ break;
10970
+ case "tracking":
10971
+ config.tracking = { ...config.tracking, ...categoryConfig.tracking };
10972
+ break;
10973
+ case "techStack":
10974
+ config.techStack = { ...config.techStack, ...categoryConfig.techStack };
10975
+ break;
10976
+ case "environment":
10977
+ config.environment = { ...config.environment, ...categoryConfig.environment };
10978
+ break;
10979
+ case "brand":
10980
+ config.brand = { ...config.brand, ...categoryConfig.brand };
10981
+ break;
10555
10982
  }
10556
- context.isGitRepo = await fileExists(path8.join(projectPath, ".git"));
10557
- if (context.isGitRepo) {
10558
- try {
10559
- const { execSync } = await import("child_process");
10560
- const remotes = execSync("git remote -v", {
10561
- cwd: projectPath,
10562
- encoding: "utf-8"
10563
- });
10564
- context.hasGitHubRemote = remotes.includes("github.com");
10565
- } catch {
10566
- context.hasGitHubRemote = false;
10983
+ for (const [key, value] of Object.entries(getCategoryValues(categoryConfig, category))) {
10984
+ if (value !== void 0) {
10985
+ updatedContext.values[key.toUpperCase()] = String(value);
10567
10986
  }
10568
10987
  }
10569
- } catch {
10570
10988
  }
10571
- return context;
10989
+ return config;
10990
+ }
10991
+ async function promptAdvancedSetup(context) {
10992
+ return promptGuidedSetup(context, false);
10993
+ }
10994
+ async function confirmTemplateConfig(config, context) {
10995
+ logger.newline();
10996
+ logger.subtitle("Configuration Summary");
10997
+ displayDetectedConfig(config, context);
10998
+ return confirm({
10999
+ message: "Apply this configuration?",
11000
+ default: true
11001
+ });
11002
+ }
11003
+ async function promptSaveGlobalDefaults(config) {
11004
+ const save = await confirm({
11005
+ message: "Save these values as global defaults for future projects?",
11006
+ default: false
11007
+ });
11008
+ if (save) {
11009
+ await updateGlobalDefaults(config);
11010
+ logger.success("Global defaults saved to ~/.claude/defaults.json");
11011
+ }
11012
+ return save;
10572
11013
  }
10573
- async function fileExists(filePath) {
10574
- try {
10575
- const fs6 = await import("fs/promises");
10576
- await fs6.access(filePath);
10577
- return true;
10578
- } catch {
11014
+
11015
+ // src/lib/wizard/init-steps.ts
11016
+ function createResult(value, navigation = "next") {
11017
+ return {
11018
+ value,
11019
+ navigation,
11020
+ wasModified: true
11021
+ };
11022
+ }
11023
+ async function promptBackOption(stepIndex, stepName) {
11024
+ if (stepIndex === 0) {
10579
11025
  return false;
10580
11026
  }
11027
+ const result = await select({
11028
+ message: `${stepName}`,
11029
+ choices: [
11030
+ {
11031
+ name: `${colors.muted("\u2190")} Back to previous step`,
11032
+ value: BACK_OPTION_VALUE,
11033
+ description: "Return to the previous step"
11034
+ },
11035
+ {
11036
+ name: "Continue with this step",
11037
+ value: "continue",
11038
+ description: "Proceed to configure this section"
11039
+ }
11040
+ ],
11041
+ default: "continue"
11042
+ });
11043
+ return result === BACK_OPTION_VALUE;
10581
11044
  }
10582
- async function promptPlaceholder(placeholder, context, detectedValue) {
10583
- const defaultValue = detectedValue || computeDefaultValue(placeholder, context) || "";
10584
- switch (placeholder.inputType) {
10585
- case "select": {
10586
- if (!placeholder.choices || placeholder.choices.length === 0) {
10587
- return input({
10588
- message: `${placeholder.label}:`,
10589
- default: defaultValue
10590
- });
11045
+ function createProjectInfoStep() {
11046
+ return {
11047
+ metadata: {
11048
+ id: "projectInfo",
11049
+ name: "Project Information",
11050
+ description: "Basic project identification and metadata",
11051
+ required: true
11052
+ },
11053
+ computeDefaults: (ctx) => ctx.projectInfo,
11054
+ execute: async (_ctx, defaults) => {
11055
+ const value = await promptProjectInfo({ defaults });
11056
+ const confirmed = await confirmProjectInfo(value);
11057
+ if (!confirmed) {
11058
+ return createResult(value, "back");
10591
11059
  }
10592
- const choices = placeholder.choices.map((c) => ({
10593
- name: c.name,
10594
- value: c.value,
10595
- description: c.description
10596
- }));
10597
- return select({
10598
- message: `${placeholder.label}:`,
10599
- choices,
10600
- default: defaultValue || choices[0].value
11060
+ return createResult(value, "next");
11061
+ }
11062
+ };
11063
+ }
11064
+ function createPreferencesStep() {
11065
+ return {
11066
+ metadata: {
11067
+ id: "preferences",
11068
+ name: "Preferences",
11069
+ description: "Language and package manager settings",
11070
+ required: true
11071
+ },
11072
+ computeDefaults: (ctx) => ctx.preferences,
11073
+ execute: async (ctx, defaults) => {
11074
+ const goBack = await promptBackOption(1, "Configure preferences or go back?");
11075
+ if (goBack) {
11076
+ return createResult(defaults, "back");
11077
+ }
11078
+ const value = await promptPreferences({
11079
+ detectedPackageManager: ctx.detection.packageManager,
11080
+ defaults
10601
11081
  });
11082
+ return createResult(value, "next");
10602
11083
  }
10603
- case "number": {
10604
- const result = await input({
10605
- message: `${placeholder.label}:`,
10606
- default: defaultValue,
10607
- validate: (value) => {
10608
- if (placeholder.validate) {
10609
- return placeholder.validate(value, context);
10610
- }
10611
- const num = Number(value);
10612
- if (Number.isNaN(num)) {
10613
- return "Please enter a valid number";
10614
- }
10615
- return true;
10616
- }
11084
+ };
11085
+ }
11086
+ function createScaffoldStep() {
11087
+ return {
11088
+ metadata: {
11089
+ id: "scaffoldOptions",
11090
+ name: "Scaffold Options",
11091
+ description: "Project structure and scaffolding type",
11092
+ required: true
11093
+ },
11094
+ computeDefaults: (ctx) => ctx.scaffoldOptions,
11095
+ execute: async (ctx, defaults) => {
11096
+ const goBack = await promptBackOption(2, "Configure scaffold or go back?");
11097
+ if (goBack) {
11098
+ return createResult(defaults, "back");
11099
+ }
11100
+ const value = await promptScaffoldOptions({
11101
+ existingProject: ctx.detection.detected,
11102
+ detectedType: ctx.detection.projectType,
11103
+ detectedPackageManager: ctx.detection.packageManager
10617
11104
  });
10618
- return result;
11105
+ return createResult(value, "next");
10619
11106
  }
10620
- case "path": {
10621
- return input({
10622
- message: `${placeholder.label}:`,
10623
- default: defaultValue,
10624
- validate: (value) => {
10625
- if (placeholder.required && !value.trim()) {
10626
- return "This path is required";
10627
- }
10628
- if (placeholder.validate) {
10629
- return placeholder.validate(value, context);
11107
+ };
11108
+ }
11109
+ function createBundleSelectionStep() {
11110
+ return {
11111
+ metadata: {
11112
+ id: "bundleSelection",
11113
+ name: "Module Bundles",
11114
+ description: "Select pre-configured module bundles",
11115
+ required: true
11116
+ },
11117
+ computeDefaults: (ctx) => ctx.bundleSelection,
11118
+ execute: async (ctx, defaults) => {
11119
+ const goBack = await promptBackOption(3, "Select bundles or go back?");
11120
+ if (goBack) {
11121
+ return createResult(defaults, "back");
11122
+ }
11123
+ if (ctx.detection.suggestedBundles?.length) {
11124
+ logger.info(
11125
+ colors.muted(
11126
+ `Suggested bundles based on your project: ${ctx.detection.suggestedBundles.join(", ")}`
11127
+ )
11128
+ );
11129
+ }
11130
+ const mode = await promptBundleMode();
11131
+ const result = {
11132
+ selectedBundles: defaults?.selectedBundles || [],
11133
+ additionalModules: defaults?.additionalModules || {
11134
+ agents: [],
11135
+ skills: [],
11136
+ commands: [],
11137
+ docs: []
11138
+ }
11139
+ };
11140
+ if (mode === "bundles" || mode === "both") {
11141
+ result.selectedBundles = await promptQuickBundleSelection();
11142
+ if (result.selectedBundles.length > 0) {
11143
+ showBundlesSummary(result.selectedBundles);
11144
+ }
11145
+ }
11146
+ if (mode === "individual" || mode === "both") {
11147
+ const preselectedFromBundles = resolveBundles(result.selectedBundles);
11148
+ const categories = ["agents", "skills", "commands", "docs"];
11149
+ if (ctx.registry) {
11150
+ logger.newline();
11151
+ logger.subtitle("Individual Module Selection");
11152
+ for (const category of categories) {
11153
+ const preselected = mode === "both" ? preselectedFromBundles[category] : [];
11154
+ const categoryResult = await selectItemsFromCategory(category, ctx.registry[category], {
11155
+ preselected,
11156
+ showDescriptions: true
11157
+ });
11158
+ if (mode === "both") {
11159
+ result.additionalModules[category] = categoryResult.selectedItems.filter(
11160
+ (id) => !preselected.includes(id)
11161
+ );
11162
+ } else {
11163
+ result.additionalModules[category] = categoryResult.selectedItems;
11164
+ }
10630
11165
  }
10631
- return true;
10632
11166
  }
11167
+ }
11168
+ return createResult(result, "next");
11169
+ }
11170
+ };
11171
+ }
11172
+ function createHookConfigStep() {
11173
+ return {
11174
+ metadata: {
11175
+ id: "hookConfig",
11176
+ name: "Notification Hooks",
11177
+ description: "Configure notification and sound hooks",
11178
+ required: false
11179
+ },
11180
+ computeDefaults: (ctx) => {
11181
+ if (ctx.hookConfig) return ctx.hookConfig;
11182
+ const hasTesting = ctx.bundleSelection?.selectedBundles.some((id) => id.includes("testing"));
11183
+ return hasTesting ? { enabled: true } : { enabled: false };
11184
+ },
11185
+ execute: async (_ctx, defaults) => {
11186
+ const goBack = await promptBackOption(4, "Configure hooks or go back?");
11187
+ if (goBack) {
11188
+ return createResult(defaults, "back");
11189
+ }
11190
+ const value = await promptHookConfig({ defaults });
11191
+ return createResult(value, "next");
11192
+ }
11193
+ };
11194
+ }
11195
+ function createMcpConfigStep() {
11196
+ return {
11197
+ metadata: {
11198
+ id: "mcpConfig",
11199
+ name: "MCP Servers",
11200
+ description: "Configure Model Context Protocol servers",
11201
+ required: false
11202
+ },
11203
+ computeDefaults: (ctx) => ctx.mcpConfig,
11204
+ execute: async (_ctx, defaults) => {
11205
+ const goBack = await promptBackOption(5, "Configure MCP servers or go back?");
11206
+ if (goBack) {
11207
+ return createResult(
11208
+ defaults,
11209
+ "back"
11210
+ );
11211
+ }
11212
+ const result = await promptMcpConfig();
11213
+ return createResult(result, "next");
11214
+ }
11215
+ };
11216
+ }
11217
+ function createPermissionsStep() {
11218
+ return {
11219
+ metadata: {
11220
+ id: "permissionsConfig",
11221
+ name: "Permissions",
11222
+ description: "Configure file, git, and bash permissions",
11223
+ required: true
11224
+ },
11225
+ computeDefaults: (ctx) => ctx.permissionsConfig,
11226
+ execute: async (_ctx, defaults) => {
11227
+ const goBack = await promptBackOption(6, "Configure permissions or go back?");
11228
+ if (goBack) {
11229
+ return createResult(defaults, "back");
11230
+ }
11231
+ const value = await promptPermissionsConfig();
11232
+ return createResult(value, "next");
11233
+ }
11234
+ };
11235
+ }
11236
+ function createCodeStyleStep() {
11237
+ return {
11238
+ metadata: {
11239
+ id: "codeStyleConfig",
11240
+ name: "Code Style",
11241
+ description: "Configure EditorConfig, Biome, Prettier, Commitlint",
11242
+ required: false
11243
+ },
11244
+ computeDefaults: (ctx) => ctx.codeStyleConfig,
11245
+ execute: async (_ctx, defaults) => {
11246
+ const goBack = await promptBackOption(7, "Configure code style or go back?");
11247
+ if (goBack) {
11248
+ return createResult(defaults, "back");
11249
+ }
11250
+ const value = await promptCodeStyleConfig({ defaults });
11251
+ return createResult(value, "next");
11252
+ }
11253
+ };
11254
+ }
11255
+ function createCICDStep() {
11256
+ return {
11257
+ metadata: {
11258
+ id: "cicdConfig",
11259
+ name: "CI/CD",
11260
+ description: "Configure GitHub Actions workflows",
11261
+ required: false
11262
+ },
11263
+ computeDefaults: (ctx) => ctx.cicdConfig,
11264
+ execute: async (ctx, defaults) => {
11265
+ const goBack = await promptBackOption(8, "Configure CI/CD or go back?");
11266
+ if (goBack) {
11267
+ return createResult(defaults, "back");
11268
+ }
11269
+ const value = await promptCICDConfig({
11270
+ packageManager: ctx.preferences?.packageManager,
11271
+ defaults
10633
11272
  });
11273
+ return createResult(value, "next");
10634
11274
  }
10635
- case "envVar": {
10636
- return input({
10637
- message: `${placeholder.label}:`,
10638
- default: defaultValue,
10639
- validate: (value) => {
10640
- if (placeholder.required && !value.trim()) {
10641
- return "This environment variable is required";
10642
- }
10643
- if (value && !/^[A-Z][A-Z0-9_]*$/.test(value)) {
10644
- return "Environment variable should be UPPER_SNAKE_CASE";
10645
- }
10646
- return true;
10647
- }
11275
+ };
11276
+ }
11277
+ function createFolderPreferencesStep() {
11278
+ return {
11279
+ metadata: {
11280
+ id: "folderPreferences",
11281
+ name: "Folder Structure",
11282
+ description: "Configure test, planning, and documentation locations",
11283
+ required: false,
11284
+ dependsOn: ["bundleSelection"]
11285
+ },
11286
+ computeDefaults: (ctx) => ctx.folderPreferences ?? null,
11287
+ execute: async (ctx, defaults) => {
11288
+ const goBack = await promptBackOption(9, "Configure folder preferences or go back?");
11289
+ if (goBack) {
11290
+ return createResult(defaults ?? null, "back");
11291
+ }
11292
+ const value = await promptQuickFolderPreferences({
11293
+ selectedBundles: ctx.bundleSelection?.selectedBundles || [],
11294
+ technologies: ctx.detection.detectedTechnologies || []
10648
11295
  });
11296
+ return createResult(value, "next");
10649
11297
  }
10650
- default: {
10651
- return input({
10652
- message: `${placeholder.label}:`,
10653
- default: defaultValue,
10654
- validate: (value) => {
10655
- if (placeholder.required && !value.trim()) {
10656
- return `${placeholder.label} is required`;
10657
- }
10658
- if (placeholder.validate) {
10659
- return placeholder.validate(value, context);
10660
- }
10661
- return true;
10662
- }
11298
+ };
11299
+ }
11300
+ function createTemplateConfigStep() {
11301
+ return {
11302
+ metadata: {
11303
+ id: "templateConfig",
11304
+ name: "Template Placeholders",
11305
+ description: "Configure template placeholder values",
11306
+ required: false
11307
+ },
11308
+ computeDefaults: (ctx) => ctx.templateConfig ?? {},
11309
+ execute: async (ctx, defaults) => {
11310
+ const goBack = await promptBackOption(10, "Configure templates or go back?");
11311
+ if (goBack) {
11312
+ return createResult(defaults ?? {}, "back");
11313
+ }
11314
+ logger.newline();
11315
+ const configContext = await buildConfigContext(ctx.projectPath);
11316
+ const value = await promptTemplateConfig({
11317
+ context: configContext,
11318
+ mode: "quick"
10663
11319
  });
11320
+ return createResult(value ?? {}, "next");
11321
+ }
11322
+ };
11323
+ }
11324
+ function createInitWizardConfig(projectPath, detection, registry) {
11325
+ void projectPath;
11326
+ void detection;
11327
+ void registry;
11328
+ const steps = [
11329
+ {
11330
+ id: "projectInfo",
11331
+ definition: createProjectInfoStep()
11332
+ },
11333
+ {
11334
+ id: "preferences",
11335
+ definition: createPreferencesStep()
11336
+ },
11337
+ {
11338
+ id: "scaffoldOptions",
11339
+ definition: createScaffoldStep()
11340
+ },
11341
+ {
11342
+ id: "bundleSelection",
11343
+ definition: createBundleSelectionStep()
11344
+ },
11345
+ {
11346
+ id: "hookConfig",
11347
+ definition: createHookConfigStep()
11348
+ },
11349
+ {
11350
+ id: "mcpConfig",
11351
+ definition: createMcpConfigStep()
11352
+ },
11353
+ {
11354
+ id: "permissionsConfig",
11355
+ definition: createPermissionsStep()
11356
+ },
11357
+ {
11358
+ id: "codeStyleConfig",
11359
+ definition: createCodeStyleStep()
11360
+ },
11361
+ {
11362
+ id: "cicdConfig",
11363
+ definition: createCICDStep()
11364
+ },
11365
+ {
11366
+ id: "folderPreferences",
11367
+ definition: createFolderPreferencesStep()
11368
+ },
11369
+ {
11370
+ id: "templateConfig",
11371
+ definition: createTemplateConfigStep()
11372
+ }
11373
+ ];
11374
+ return {
11375
+ id: "init-wizard",
11376
+ title: "Claude Code Configuration",
11377
+ allowSkip: true,
11378
+ showProgress: true,
11379
+ steps
11380
+ };
11381
+ }
11382
+
11383
+ // src/cli/prompts/index.ts
11384
+ init_esm_shims();
11385
+
11386
+ // src/cli/prompts/update.ts
11387
+ init_esm_shims();
11388
+ async function promptUpdateAction() {
11389
+ logger.subtitle("Update Options");
11390
+ return select({
11391
+ message: "What would you like to update?",
11392
+ choices: [
11393
+ {
11394
+ name: "Check for updates",
11395
+ value: "check",
11396
+ description: "Show what has changed without modifying files"
11397
+ },
11398
+ {
11399
+ name: "Update modules only",
11400
+ value: "modules",
11401
+ description: "Update agents, skills, commands, docs"
11402
+ },
11403
+ {
11404
+ name: "Reconfigure settings",
11405
+ value: "config",
11406
+ description: "Re-run configuration for MCP, hooks, preferences"
11407
+ },
11408
+ {
11409
+ name: "Full update",
11410
+ value: "all",
11411
+ description: "Update everything"
11412
+ },
11413
+ {
11414
+ name: "Cancel",
11415
+ value: "cancel"
11416
+ }
11417
+ ],
11418
+ default: "check"
11419
+ });
11420
+ }
11421
+ function showUpdateReport(updates) {
11422
+ logger.newline();
11423
+ logger.title("Update Report");
11424
+ if (updates.new.length === 0 && updates.updated.length === 0 && updates.deprecated.length === 0) {
11425
+ logger.success("Everything is up to date!");
11426
+ return;
11427
+ }
11428
+ if (updates.new.length > 0) {
11429
+ logger.newline();
11430
+ logger.info(colors.bold(`\u271A New modules available (${updates.new.length}):`));
11431
+ for (const mod of updates.new) {
11432
+ logger.item(`${mod.category}: ${colors.primary(mod.name)}`);
10664
11433
  }
10665
11434
  }
10666
- }
10667
- function computeAllDefaults(context, globalDefaults) {
10668
- const config = {
10669
- commands: {},
10670
- paths: {},
10671
- targets: {},
10672
- tracking: {},
10673
- techStack: {},
10674
- environment: {},
10675
- brand: {}
10676
- };
10677
- for (const placeholder of TEMPLATE_PLACEHOLDERS) {
10678
- const defaultValue = computeDefaultValue(placeholder, context);
10679
- if (defaultValue) {
10680
- setConfigValue(config, placeholder, defaultValue);
11435
+ if (updates.updated.length > 0) {
11436
+ logger.newline();
11437
+ logger.info(colors.bold(`\u21BB Updated modules (${updates.updated.length}):`));
11438
+ for (const mod of updates.updated) {
11439
+ const conflict = mod.hasLocalChanges ? colors.warning(" (local changes)") : "";
11440
+ logger.item(`${mod.category}: ${colors.primary(mod.name)}${conflict}`);
10681
11441
  }
10682
11442
  }
10683
- if (globalDefaults) {
10684
- return mergeWithGlobalDefaults(config, globalDefaults);
10685
- }
10686
- return config;
10687
- }
10688
- function setConfigValue(config, placeholder, value) {
10689
- const key = placeholderKeyToConfigKey(placeholder.key, placeholder.category);
10690
- switch (placeholder.category) {
10691
- case "commands":
10692
- if (!config.commands) config.commands = {};
10693
- config.commands[key] = value;
10694
- break;
10695
- case "paths":
10696
- if (!config.paths) config.paths = {};
10697
- config.paths[key] = value;
10698
- break;
10699
- case "targets":
10700
- case "performance": {
10701
- if (!config.targets) config.targets = {};
10702
- const numValue = Number(value);
10703
- if (!Number.isNaN(numValue)) {
10704
- config.targets[key] = numValue;
10705
- } else {
10706
- config.targets[key] = value;
10707
- }
10708
- break;
11443
+ if (updates.deprecated.length > 0) {
11444
+ logger.newline();
11445
+ logger.info(colors.bold(`\u2717 Deprecated modules (${updates.deprecated.length}):`));
11446
+ for (const mod of updates.deprecated) {
11447
+ logger.item(`${mod.category}: ${colors.muted(mod.name)}`);
10709
11448
  }
10710
- case "tracking":
10711
- if (!config.tracking) config.tracking = {};
10712
- config.tracking[key] = key.endsWith("Days") ? Number(value) : value;
10713
- break;
10714
- case "techStack":
10715
- if (!config.techStack) config.techStack = {};
10716
- config.techStack[key] = value;
10717
- break;
10718
- case "environment":
10719
- if (!config.environment) config.environment = {};
10720
- config.environment[key] = value;
10721
- break;
10722
- case "brand":
10723
- if (!config.brand) config.brand = {};
10724
- config.brand[key] = value;
10725
- break;
10726
11449
  }
10727
- }
10728
- function placeholderKeyToConfigKey(key, category) {
10729
- let cleanKey = key;
10730
- if (category === "commands" && key.endsWith("_COMMAND")) {
10731
- cleanKey = key.slice(0, -8);
10732
- } else if (category === "environment" && key.endsWith("_ENV")) {
10733
- cleanKey = key.slice(0, -4);
10734
- } else if (key.endsWith("_TARGET")) {
10735
- cleanKey = key.slice(0, -7);
11450
+ if (updates.conflicts.length > 0) {
11451
+ logger.newline();
11452
+ logger.warn(`\u26A0 Modules with conflicts (${updates.conflicts.length}):`);
11453
+ for (const mod of updates.conflicts) {
11454
+ logger.item(`${mod.category}: ${colors.warning(mod.name)} - has local modifications`);
11455
+ }
10736
11456
  }
10737
- return cleanKey.toLowerCase().replace(/_([a-z])/g, (_, char) => char.toUpperCase());
10738
11457
  }
10739
- async function promptTemplateConfig(options) {
10740
- const { context, mode = "quick", category, requiredOnly = false } = options;
10741
- logger.section("Template Configuration", "\u{1F4DD}");
10742
- logger.newline();
10743
- logger.info("This step personalizes all Claude Code configuration files for YOUR project.");
10744
- logger.info("Your answers will be used to:");
10745
- logger.newline();
10746
- logger.item("Replace {{PLACEHOLDERS}} in agents, commands, and skills");
10747
- logger.item("Configure CLAUDE.md with your tech stack and commands");
10748
- logger.item("Set up quality targets (coverage, performance, accessibility)");
10749
- logger.item("Customize code examples to match your project structure");
10750
- logger.newline();
10751
- logger.info("Accurate configuration means better AI assistance tailored to your codebase!");
11458
+ async function promptNewModules(modules) {
11459
+ if (modules.length === 0) return [];
10752
11460
  logger.newline();
10753
- const hasDefaults = await hasGlobalDefaults();
10754
- const globalDefaults = hasDefaults ? await getGlobalTemplateConfig() : void 0;
10755
- if (mode === "quick") {
10756
- return promptQuickSetup(context, globalDefaults);
10757
- }
10758
- if (category) {
10759
- return promptCategory(category, context, requiredOnly);
10760
- }
10761
- if (mode === "guided") {
10762
- return promptGuidedSetup(context, requiredOnly);
10763
- }
10764
- return promptAdvancedSetup(context);
11461
+ const choices = modules.map((mod) => ({
11462
+ name: `${mod.category}: ${mod.name}`,
11463
+ value: mod.id,
11464
+ checked: false
11465
+ }));
11466
+ return checkbox({
11467
+ message: "Select new modules to install:",
11468
+ choices
11469
+ });
10765
11470
  }
10766
- async function promptQuickSetup(context, globalDefaults) {
10767
- logger.info("Auto-detecting configuration from project...");
11471
+ async function promptUpdatedModules(modules) {
11472
+ if (modules.length === 0) return [];
10768
11473
  logger.newline();
10769
- const detected = computeAllDefaults(context, globalDefaults);
10770
- displayDetectedConfig(detected, context);
10771
- const wantEdit = await confirm({
10772
- message: "Would you like to modify any values?",
10773
- default: false
11474
+ const choices = modules.map((mod) => ({
11475
+ name: `${mod.category}: ${mod.name}${mod.hasLocalChanges ? " \u26A0\uFE0F" : ""}`,
11476
+ value: mod.id,
11477
+ checked: !mod.hasLocalChanges
11478
+ // Don't check by default if has local changes
11479
+ }));
11480
+ return checkbox({
11481
+ message: "Select modules to update:",
11482
+ choices
10774
11483
  });
10775
- let finalConfig = detected;
10776
- if (wantEdit) {
10777
- finalConfig = await promptEditConfig(detected, context);
10778
- }
10779
- return finalConfig;
10780
- }
10781
- function displayDetectedConfig(config, _context) {
10782
- const categories = Object.entries(CATEGORY_INFO).sort(([, a], [, b]) => a.order - b.order).map(([key]) => key);
10783
- for (const category of categories) {
10784
- const placeholders = getPlaceholdersByCategory(category);
10785
- if (placeholders.length === 0) continue;
10786
- const values = getCategoryValues(config, category);
10787
- if (Object.keys(values).length === 0) continue;
10788
- logger.subtitle(CATEGORY_INFO[category].name);
10789
- for (const [key, value] of Object.entries(values)) {
10790
- if (value !== void 0) {
10791
- const placeholder = TEMPLATE_PLACEHOLDERS.find(
10792
- (p) => placeholderKeyToConfigKey(p.key, p.category) === key
10793
- );
10794
- const label = placeholder?.label || key;
10795
- logger.keyValue(label, String(value));
10796
- }
10797
- }
10798
- logger.newline();
10799
- }
10800
- }
10801
- function getCategoryValues(config, category) {
10802
- switch (category) {
10803
- case "commands":
10804
- return config.commands || {};
10805
- case "paths":
10806
- return config.paths || {};
10807
- case "targets":
10808
- case "performance":
10809
- return config.targets || {};
10810
- case "tracking":
10811
- return config.tracking || {};
10812
- case "techStack":
10813
- return config.techStack || {};
10814
- case "environment":
10815
- return config.environment || {};
10816
- case "brand":
10817
- return config.brand || {};
10818
- default:
10819
- return {};
10820
- }
10821
11484
  }
10822
- async function promptEditConfig(config, context) {
10823
- const categories = Object.entries(CATEGORY_INFO).sort(([, a], [, b]) => a.order - b.order).map(([key]) => key);
10824
- const categoryToEdit = await select({
10825
- message: "Which category would you like to edit?",
11485
+ async function promptConflictResolution(module) {
11486
+ logger.newline();
11487
+ logger.warn(`Conflict: ${module.category}/${module.name} has local modifications`);
11488
+ return select({
11489
+ message: "How do you want to resolve this conflict?",
10826
11490
  choices: [
10827
- ...categories.map((cat) => ({
10828
- name: CATEGORY_INFO[cat].name,
10829
- value: cat
10830
- })),
10831
- { name: "Done editing", value: "done" }
10832
- ]
11491
+ {
11492
+ name: "Keep local version",
11493
+ value: "keep",
11494
+ description: "Do not update this module"
11495
+ },
11496
+ {
11497
+ name: "Use updated version",
11498
+ value: "update",
11499
+ description: "Replace with new version (lose local changes)"
11500
+ },
11501
+ {
11502
+ name: "Show diff",
11503
+ value: "diff",
11504
+ description: "See what changed before deciding"
11505
+ }
11506
+ ],
11507
+ default: "keep"
10833
11508
  });
10834
- if (categoryToEdit === "done") {
10835
- return config;
10836
- }
10837
- const editedCategory = await promptCategory(categoryToEdit, context, false);
10838
- const merged = { ...config };
10839
- switch (categoryToEdit) {
10840
- case "commands":
10841
- merged.commands = { ...config.commands, ...editedCategory.commands };
10842
- break;
10843
- case "paths":
10844
- merged.paths = { ...config.paths, ...editedCategory.paths };
10845
- break;
10846
- case "targets":
10847
- case "performance":
10848
- merged.targets = { ...config.targets, ...editedCategory.targets };
10849
- break;
10850
- case "tracking":
10851
- merged.tracking = { ...config.tracking, ...editedCategory.tracking };
10852
- break;
10853
- case "techStack":
10854
- merged.techStack = { ...config.techStack, ...editedCategory.techStack };
10855
- break;
10856
- case "environment":
10857
- merged.environment = { ...config.environment, ...editedCategory.environment };
10858
- break;
10859
- case "brand":
10860
- merged.brand = { ...config.brand, ...editedCategory.brand };
10861
- break;
10862
- }
10863
- const editMore = await confirm({
10864
- message: "Edit another category?",
10865
- default: false
11509
+ }
11510
+ async function promptReconfigureOptions() {
11511
+ logger.newline();
11512
+ return checkbox({
11513
+ message: "What would you like to reconfigure?",
11514
+ choices: [
11515
+ { name: "MCP servers", value: "mcp", checked: false },
11516
+ { name: "Hooks", value: "hooks", checked: false },
11517
+ { name: "Preferences (language, co-author)", value: "preferences", checked: false },
11518
+ { name: "Permissions", value: "permissions", checked: false },
11519
+ { name: "Add/remove modules", value: "modules", checked: false }
11520
+ ]
10866
11521
  });
10867
- if (editMore) {
10868
- return promptEditConfig(merged, context);
10869
- }
10870
- return merged;
10871
11522
  }
10872
- async function promptCategory(category, context, requiredOnly) {
10873
- const config = {
10874
- commands: {},
10875
- paths: {},
10876
- targets: {},
10877
- tracking: {},
10878
- techStack: {},
10879
- environment: {},
10880
- brand: {}
10881
- };
10882
- logger.subtitle(CATEGORY_INFO[category].name);
10883
- let placeholders = getPlaceholdersByCategory(category);
10884
- if (requiredOnly) {
10885
- placeholders = placeholders.filter((p) => p.required);
11523
+
11524
+ // src/cli/prompts/confirm.ts
11525
+ init_esm_shims();
11526
+ async function promptExistingProjectAction() {
11527
+ return select({
11528
+ message: "Existing .claude/ folder detected. What would you like to do?",
11529
+ choices: [
11530
+ {
11531
+ name: "Skip (keep existing)",
11532
+ value: "skip",
11533
+ description: "Do not modify existing configuration"
11534
+ },
11535
+ {
11536
+ name: "Overwrite",
11537
+ value: "overwrite",
11538
+ description: "Replace existing configuration"
11539
+ },
11540
+ {
11541
+ name: "Merge",
11542
+ value: "merge",
11543
+ description: "Add new items, keep existing"
11544
+ }
11545
+ ],
11546
+ default: "skip"
11547
+ });
11548
+ }
11549
+ function showFinalSummary(config) {
11550
+ logger.newline();
11551
+ logger.title("Configuration Summary");
11552
+ logger.subtitle("Project");
11553
+ logger.keyValue("Name", config.project.name);
11554
+ logger.keyValue("GitHub", `${config.project.org}/${config.project.repo}`);
11555
+ if (config.project.domain) {
11556
+ logger.keyValue("Domain", config.project.domain);
10886
11557
  }
10887
- const updatedContext = { ...context };
10888
- for (const placeholder of placeholders) {
10889
- const value = await promptPlaceholder(placeholder, updatedContext);
10890
- if (value) {
10891
- setConfigValue(config, placeholder, value);
10892
- updatedContext.values[placeholder.key] = value;
11558
+ logger.newline();
11559
+ logger.subtitle("Preferences");
11560
+ logger.keyValue("Language", config.preferences.language === "en" ? "English" : "Espa\xF1ol");
11561
+ logger.keyValue("Co-author", config.preferences.includeCoAuthor ? "Yes" : "No");
11562
+ logger.newline();
11563
+ logger.subtitle("Modules");
11564
+ const moduleCategories = ["agents", "skills", "commands", "docs"];
11565
+ for (const category of moduleCategories) {
11566
+ const selected = config.modules[category].selected;
11567
+ if (selected.length > 0) {
11568
+ logger.keyValue(capitalize2(category), `${selected.length} selected`);
11569
+ logger.note(selected.join(", "));
10893
11570
  }
10894
11571
  }
10895
- return config;
10896
- }
10897
- async function promptGuidedSetup(context, requiredOnly) {
10898
- const config = {
10899
- commands: {},
10900
- paths: {},
10901
- targets: {},
10902
- tracking: {},
10903
- techStack: {},
10904
- environment: {},
10905
- brand: {}
10906
- };
10907
- const categories = Object.entries(CATEGORY_INFO).sort(([, a], [, b]) => a.order - b.order).map(([key]) => key);
10908
- const updatedContext = { ...context };
10909
- for (const category of categories) {
10910
- const placeholders = getPlaceholdersByCategory(category);
10911
- const relevantPlaceholders = requiredOnly ? placeholders.filter((p) => p.required) : placeholders;
10912
- if (relevantPlaceholders.length === 0) continue;
10913
- const configureCategory = await confirm({
10914
- message: `Configure ${CATEGORY_INFO[category].name}? (${relevantPlaceholders.length} options)`,
10915
- default: true
10916
- });
10917
- if (!configureCategory) continue;
10918
- const categoryConfig = await promptCategory(category, updatedContext, requiredOnly);
10919
- switch (category) {
10920
- case "commands":
10921
- config.commands = { ...config.commands, ...categoryConfig.commands };
10922
- break;
10923
- case "paths":
10924
- config.paths = { ...config.paths, ...categoryConfig.paths };
10925
- break;
10926
- case "targets":
10927
- case "performance":
10928
- config.targets = { ...config.targets, ...categoryConfig.targets };
10929
- break;
10930
- case "tracking":
10931
- config.tracking = { ...config.tracking, ...categoryConfig.tracking };
10932
- break;
10933
- case "techStack":
10934
- config.techStack = { ...config.techStack, ...categoryConfig.techStack };
10935
- break;
10936
- case "environment":
10937
- config.environment = { ...config.environment, ...categoryConfig.environment };
10938
- break;
10939
- case "brand":
10940
- config.brand = { ...config.brand, ...categoryConfig.brand };
10941
- break;
10942
- }
10943
- for (const [key, value] of Object.entries(getCategoryValues(categoryConfig, category))) {
10944
- if (value !== void 0) {
10945
- updatedContext.values[key.toUpperCase()] = String(value);
10946
- }
10947
- }
11572
+ logger.newline();
11573
+ logger.subtitle("Extras");
11574
+ const extras = [];
11575
+ if (config.extras.schemas) extras.push("schemas");
11576
+ if (config.extras.scripts) extras.push("scripts");
11577
+ if (config.extras.hooks.enabled) extras.push("hooks");
11578
+ if (config.extras.sessions) extras.push("sessions");
11579
+ logger.keyValue("Included", extras.join(", ") || "none");
11580
+ if (config.extras.codeStyle?.enabled) {
11581
+ const codeStyleTools = [];
11582
+ if (config.extras.codeStyle.editorconfig) codeStyleTools.push("EditorConfig");
11583
+ if (config.extras.codeStyle.commitlint) codeStyleTools.push("Commitlint");
11584
+ if (config.extras.codeStyle.biome) codeStyleTools.push("Biome");
11585
+ if (config.extras.codeStyle.prettier) codeStyleTools.push("Prettier");
11586
+ logger.keyValue("Code Style", codeStyleTools.join(", "));
10948
11587
  }
10949
- return config;
10950
- }
10951
- async function promptAdvancedSetup(context) {
10952
- return promptGuidedSetup(context, false);
11588
+ if (config.mcp.servers.length > 0) {
11589
+ logger.newline();
11590
+ logger.subtitle("MCP Servers");
11591
+ logger.keyValue("Level", config.mcp.level);
11592
+ logger.keyValue("Servers", config.mcp.servers.map((s) => s.serverId).join(", "));
11593
+ }
11594
+ logger.newline();
11595
+ logger.subtitle("Scaffold");
11596
+ logger.keyValue(
11597
+ "Type",
11598
+ config.scaffold.type === "claude-only" ? "Claude config only" : "Full project"
11599
+ );
10953
11600
  }
10954
- async function confirmTemplateConfig(config, context) {
11601
+ async function confirmFinalConfiguration(config) {
11602
+ showFinalSummary(config);
10955
11603
  logger.newline();
10956
- logger.subtitle("Configuration Summary");
10957
- displayDetectedConfig(config, context);
10958
11604
  return confirm({
10959
- message: "Apply this configuration?",
11605
+ message: "Proceed with this configuration?",
10960
11606
  default: true
10961
11607
  });
10962
11608
  }
10963
- async function promptSaveGlobalDefaults(config) {
10964
- const save = await confirm({
10965
- message: "Save these values as global defaults for future projects?",
10966
- default: false
10967
- });
10968
- if (save) {
10969
- await updateGlobalDefaults(config);
10970
- logger.success("Global defaults saved to ~/.claude/defaults.json");
11609
+ function showPostInstallInstructions(config) {
11610
+ logger.newline();
11611
+ logger.title("Installation Complete!");
11612
+ const steps = [];
11613
+ if (config.scaffold.type === "full-project") {
11614
+ steps.push("Review the generated project structure");
10971
11615
  }
10972
- return save;
11616
+ steps.push("Review .claude/CLAUDE.md for agent instructions");
11617
+ steps.push("Customize agents, commands, and skills as needed");
11618
+ if (config.mcp.servers.length > 0) {
11619
+ const mcpFile = config.mcp.level === "user" ? "~/.claude/settings.json" : ".claude/settings.local.json";
11620
+ steps.push(`Configure MCP server credentials in ${mcpFile}`);
11621
+ }
11622
+ if (config.extras.hooks.enabled) {
11623
+ steps.push("Review hook scripts in .claude/hooks/");
11624
+ if (config.extras.hooks.notification?.audio) {
11625
+ steps.push("Install Piper TTS for audio notifications (see docs)");
11626
+ }
11627
+ }
11628
+ logger.instructions("Next Steps:", steps);
11629
+ logger.subtitle("Useful Commands");
11630
+ logger.raw("");
11631
+ logger.raw(` ${colors.primary("claude-config status")} - Show current configuration`);
11632
+ logger.raw(` ${colors.primary("claude-config list")} - List available modules`);
11633
+ logger.raw(` ${colors.primary("claude-config add")} - Add a module`);
11634
+ logger.raw(` ${colors.primary("claude-config update")} - Update configuration`);
11635
+ logger.raw("");
11636
+ }
11637
+ function capitalize2(str) {
11638
+ return str.charAt(0).toUpperCase() + str.slice(1);
10973
11639
  }
10974
11640
 
10975
11641
  // src/cli/commands/init.ts
@@ -11186,52 +11852,65 @@ async function buildDefaultConfig(projectPath, detection, options) {
11186
11852
  async function buildInteractiveConfig(projectPath, detection, registry, options) {
11187
11853
  const projectName = await getProjectName(projectPath);
11188
11854
  const projectDesc = await getProjectDescription(projectPath);
11189
- const projectInfo = await promptProjectInfo({
11190
- defaults: {
11191
- name: projectName,
11192
- description: projectDesc || ""
11855
+ const wizardConfig = createInitWizardConfig(
11856
+ projectPath,
11857
+ {
11858
+ detected: detection.detected,
11859
+ projectType: detection.projectType,
11860
+ packageManager: detection.packageManager,
11861
+ suggestedBundles: detection.suggestedBundles,
11862
+ detectedTechnologies: detection.detectedTechnologies
11863
+ },
11864
+ registry
11865
+ );
11866
+ const initialContext = {
11867
+ projectPath,
11868
+ registry,
11869
+ detection: {
11870
+ detected: detection.detected,
11871
+ projectType: detection.projectType,
11872
+ packageManager: detection.packageManager,
11873
+ suggestedBundles: detection.suggestedBundles,
11874
+ detectedTechnologies: detection.detectedTechnologies
11875
+ },
11876
+ projectInfo: {
11877
+ name: projectName || "",
11878
+ description: projectDesc || "",
11879
+ org: "",
11880
+ repo: projectName?.toLowerCase().replace(/\s+/g, "-") || "",
11881
+ entityType: "item",
11882
+ entityTypePlural: "items"
11193
11883
  }
11194
- });
11195
- const confirmed = await confirmProjectInfo(projectInfo);
11196
- if (!confirmed) {
11197
- return buildInteractiveConfig(projectPath, detection, registry, options);
11884
+ };
11885
+ const wizardResult = await runWizard(
11886
+ wizardConfig,
11887
+ initialContext
11888
+ );
11889
+ if (wizardResult.cancelled) {
11890
+ return null;
11198
11891
  }
11199
- const preferences = await promptPreferences({
11200
- detectedPackageManager: detection.packageManager
11201
- });
11202
- const scaffoldOptions = await promptScaffoldOptions({
11203
- existingProject: detection.detected,
11204
- detectedType: detection.projectType,
11205
- detectedPackageManager: detection.packageManager
11206
- });
11207
- const bundleSelection = await selectModulesWithBundles(registry, detection.suggestedBundles);
11208
- const hasPlanning = bundleSelection.selectedBundles.some((id) => id.includes("planning"));
11209
- const hasTesting = bundleSelection.selectedBundles.some((id) => id.includes("testing"));
11210
- const hookConfig = await promptHookConfig({
11211
- defaults: hasTesting ? { enabled: true } : void 0
11212
- });
11892
+ showWizardSummary(wizardResult.state);
11893
+ const {
11894
+ projectInfo,
11895
+ preferences,
11896
+ scaffoldOptions,
11897
+ bundleSelection,
11898
+ hookConfig,
11899
+ mcpConfig: mcpResult,
11900
+ permissionsConfig,
11901
+ codeStyleConfig,
11902
+ cicdConfig,
11903
+ folderPreferences,
11904
+ templateConfig: templateConfigResult
11905
+ } = wizardResult.values;
11213
11906
  let mcpConfig = { level: "project", servers: [] };
11214
11907
  let skippedMcpConfigs = [];
11215
- if (!options.noMcp) {
11216
- const mcpResult = await promptMcpConfig();
11908
+ if (!options.noMcp && mcpResult) {
11217
11909
  mcpConfig = mcpResult.config;
11218
11910
  skippedMcpConfigs = mcpResult.skippedConfigs;
11219
11911
  }
11220
- const permissionsConfig = await promptPermissionsConfig();
11221
- const codeStyleConfig = await promptCodeStyleConfig();
11222
- const cicdConfig = await promptCICDConfig({
11223
- packageManager: preferences.packageManager
11224
- });
11225
- const folderPreferences = await promptQuickFolderPreferences({
11226
- selectedBundles: bundleSelection.selectedBundles,
11227
- technologies: detection.detectedTechnologies || []
11228
- });
11229
- logger.newline();
11230
- const configContext = await buildConfigContext(projectPath);
11231
- const templateConfigResult = await promptTemplateConfig({
11232
- context: configContext,
11233
- mode: "quick"
11234
- });
11912
+ const hasPlanning = bundleSelection.selectedBundles.some((id) => id.includes("planning"));
11913
+ const hasTesting = bundleSelection.selectedBundles.some((id) => id.includes("testing"));
11235
11914
  const resolvedModules = resolveBundles(bundleSelection.selectedBundles);
11236
11915
  const modules = {
11237
11916
  agents: [.../* @__PURE__ */ new Set([...resolvedModules.agents, ...bundleSelection.additionalModules.agents])],
@@ -11283,51 +11962,6 @@ async function buildInteractiveConfig(projectPath, detection, registry, options)
11283
11962
  cicdConfig
11284
11963
  };
11285
11964
  }
11286
- async function selectModulesWithBundles(registry, suggestedBundles) {
11287
- const categories = ["agents", "skills", "commands", "docs"];
11288
- const mode = await promptBundleMode();
11289
- const result = {
11290
- selectedBundles: [],
11291
- additionalModules: {
11292
- agents: [],
11293
- skills: [],
11294
- commands: [],
11295
- docs: []
11296
- }
11297
- };
11298
- if (mode === "bundles" || mode === "both") {
11299
- if (suggestedBundles && suggestedBundles.length > 0) {
11300
- logger.newline();
11301
- logger.info(
11302
- colors.muted(`Suggested bundles based on your project: ${suggestedBundles.join(", ")}`)
11303
- );
11304
- }
11305
- result.selectedBundles = await promptQuickBundleSelection();
11306
- if (result.selectedBundles.length > 0) {
11307
- showBundlesSummary(result.selectedBundles);
11308
- }
11309
- }
11310
- if (mode === "individual" || mode === "both") {
11311
- const preselectedFromBundles = resolveBundles(result.selectedBundles);
11312
- logger.newline();
11313
- logger.subtitle("Individual Module Selection");
11314
- for (const category of categories) {
11315
- const preselected = mode === "both" ? preselectedFromBundles[category] : [];
11316
- const categoryResult = await selectItemsFromCategory(category, registry[category], {
11317
- preselected,
11318
- showDescriptions: true
11319
- });
11320
- if (mode === "both") {
11321
- result.additionalModules[category] = categoryResult.selectedItems.filter(
11322
- (id) => !preselected.includes(id)
11323
- );
11324
- } else {
11325
- result.additionalModules[category] = categoryResult.selectedItems;
11326
- }
11327
- }
11328
- }
11329
- return result;
11330
- }
11331
11965
  async function executeInstallation(projectPath, config, registry, templatesPath, options, cicdConfig) {
11332
11966
  logger.newline();
11333
11967
  logger.title("Installing Configuration");