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