universal-dev-standards 6.9.0 → 6.11.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.
Files changed (63) hide show
  1. package/bin/uds.js +2 -0
  2. package/bundled/core/agent-communication-protocol.md +8 -0
  3. package/bundled/core/branch-completion.md +8 -0
  4. package/bundled/core/change-batching-standards.md +8 -0
  5. package/bundled/core/execution-history.md +8 -0
  6. package/bundled/core/pipeline-integration-standards.md +8 -0
  7. package/bundled/core/workflow-enforcement.md +8 -0
  8. package/bundled/core/workflow-state-protocol.md +8 -0
  9. package/bundled/locales/zh-CN/CHANGELOG.md +49 -3
  10. package/bundled/locales/zh-CN/README.md +1 -1
  11. package/bundled/locales/zh-CN/SECURITY.md +1 -1
  12. package/bundled/locales/zh-CN/core/agent-communication-protocol.md +7 -0
  13. package/bundled/locales/zh-CN/core/branch-completion.md +7 -0
  14. package/bundled/locales/zh-CN/core/change-batching-standards.md +7 -0
  15. package/bundled/locales/zh-CN/core/execution-history.md +7 -0
  16. package/bundled/locales/zh-CN/core/pipeline-integration-standards.md +7 -0
  17. package/bundled/locales/zh-CN/core/workflow-enforcement.md +7 -0
  18. package/bundled/locales/zh-CN/core/workflow-state-protocol.md +7 -0
  19. package/bundled/locales/zh-CN/docs/CHEATSHEET.md +1 -1
  20. package/bundled/locales/zh-CN/docs/CLI-INIT-OPTIONS.md +52 -5
  21. package/bundled/locales/zh-CN/docs/FEATURE-REFERENCE.md +3 -1
  22. package/bundled/locales/zh-CN/docs/MIGRATION-v6.md +8 -4
  23. package/bundled/locales/zh-TW/CHANGELOG.md +50 -3
  24. package/bundled/locales/zh-TW/README.md +1 -1
  25. package/bundled/locales/zh-TW/SECURITY.md +1 -1
  26. package/bundled/locales/zh-TW/core/agent-communication-protocol.md +7 -0
  27. package/bundled/locales/zh-TW/core/branch-completion.md +7 -0
  28. package/bundled/locales/zh-TW/core/change-batching-standards.md +7 -0
  29. package/bundled/locales/zh-TW/core/execution-history.md +7 -0
  30. package/bundled/locales/zh-TW/core/pipeline-integration-standards.md +7 -0
  31. package/bundled/locales/zh-TW/core/workflow-enforcement.md +7 -0
  32. package/bundled/locales/zh-TW/core/workflow-state-protocol.md +7 -0
  33. package/bundled/locales/zh-TW/docs/CHEATSHEET.md +1 -1
  34. package/bundled/locales/zh-TW/docs/CLI-INIT-OPTIONS.md +52 -5
  35. package/bundled/locales/zh-TW/docs/FEATURE-REFERENCE.md +3 -1
  36. package/bundled/locales/zh-TW/docs/MIGRATION-v6.md +8 -4
  37. package/bundled/locales/zh-TW/integrations/claude-code/README.md +14 -5
  38. package/package.json +7 -6
  39. package/src/commands/check.js +413 -44
  40. package/src/commands/config.js +34 -28
  41. package/src/commands/init.js +37 -7
  42. package/src/commands/spec.js +2 -2
  43. package/src/commands/update.js +560 -74
  44. package/src/core/manifest.js +39 -1
  45. package/src/flows/init-flow.js +9 -1
  46. package/src/generators/layered-claudemd.js +13 -4
  47. package/src/i18n/messages.js +39 -3
  48. package/src/installers/integration-installer.js +17 -10
  49. package/src/installers/manifest-installer.js +4 -0
  50. package/src/installers/skills-installer.js +4 -4
  51. package/src/installers/standards-installer.js +3 -3
  52. package/src/prompts/init.js +33 -4
  53. package/src/reconciler/actual-state-scanner.js +29 -2
  54. package/src/reconciler/desired-state-calculator.js +51 -2
  55. package/src/reconciler/diff-engine.js +76 -5
  56. package/src/reconciler/plan-executor.js +48 -27
  57. package/src/utils/hasher.js +61 -5
  58. package/src/utils/integration-generator.js +431 -92
  59. package/src/utils/marker-locator.js +140 -0
  60. package/src/utils/reference-sync.js +156 -8
  61. package/src/utils/registry.js +57 -0
  62. package/src/utils/spinner.js +31 -0
  63. package/standards-registry.json +8 -8
@@ -1,6 +1,6 @@
1
1
  import chalk from 'chalk';
2
2
  import { select, confirm as inquirerConfirm } from '@inquirer/prompts';
3
- import ora from 'ora';
3
+ import { createSpinner } from '../utils/spinner.js';
4
4
  import { unlinkSync, existsSync, writeFileSync, mkdirSync } from 'fs';
5
5
  import { join, basename } from 'path';
6
6
  import { config } from '../utils/config-manager.js';
@@ -45,11 +45,11 @@ import {
45
45
  import { displayLanguageToLocale } from '../utils/locale.js';
46
46
  import {
47
47
  writeIntegrationFile,
48
- getToolFilePath
48
+ resolveIntegrationTargetFile
49
49
  } from '../utils/integration-generator.js';
50
50
  import { getMarketplaceSkillsInfo } from '../utils/github.js';
51
51
  import { regenerateIntegrations } from './update.js';
52
- import { mergeInstalledNames } from '../core/manifest.js';
52
+ import { mergeInstalledNames, bumpManifestVersion } from '../core/manifest.js';
53
53
 
54
54
  /**
55
55
  * Get localized message with fallback (for config-specific keys)
@@ -381,12 +381,12 @@ async function handleDisplayLanguageChange() {
381
381
  if (confirmReinstall) {
382
382
  const cmdLocale = displayLanguageToLocale(newLang);
383
383
  if (hasSkillInstalls) {
384
- const spinner = ora(t('config.reinstallingSkills', 'Reinstalling Skills...')).start();
384
+ const spinner = createSpinner(t('config.reinstallingSkills', 'Reinstalling Skills...')).start();
385
385
  await installSkillsToMultipleAgents(manifest.skills.installations, null, projectPath, cmdLocale);
386
386
  spinner.succeed(t('config.skillsReinstalled', 'Skills reinstalled'));
387
387
  }
388
388
  if (hasCommandInstalls) {
389
- const spinner = ora(t('config.reinstallingCommands', 'Reinstalling Commands...')).start();
389
+ const spinner = createSpinner(t('config.reinstallingCommands', 'Reinstalling Commands...')).start();
390
390
  await installCommandsToMultipleAgents(manifest.commands.installations, null, projectPath, cmdLocale);
391
391
  spinner.succeed(t('config.commandsReinstalled', 'Commands reinstalled'));
392
392
  }
@@ -401,7 +401,7 @@ async function handleDisplayLanguageChange() {
401
401
  });
402
402
 
403
403
  if (confirmRegen) {
404
- const spinner = ora(t('config.applyingPreset', 'Applying...')).start();
404
+ const spinner = createSpinner(t('config.applyingPreset', 'Applying...')).start();
405
405
  regenerateIntegrations(projectPath, manifest);
406
406
  spinner.succeed(t('config.integrationsRegenerated', 'Integrations regenerated'));
407
407
  }
@@ -665,15 +665,18 @@ export async function runProjectConfiguration(options) {
665
665
  newAITools = newAITools.filter(tool => !result.tools.includes(tool));
666
666
 
667
667
  // Remove integration files for removed tools
668
- const spinner = ora(msgObj.removingIntegrations).start();
668
+ const spinner = createSpinner(msgObj.removingIntegrations).start();
669
669
  for (const tool of result.tools) {
670
- const filePath = join(projectPath, getToolFilePath(tool));
670
+ // XSPEC-418 R3: remove the file this tool actually targets (e.g.
671
+ // CLAUDE.local.md), not always its hardcoded default.
672
+ const removedToolFile = resolveIntegrationTargetFile(tool, manifest);
673
+ const filePath = join(projectPath, removedToolFile);
671
674
  if (existsSync(filePath)) {
672
675
  try {
673
676
  unlinkSync(filePath);
674
- console.log(chalk.gray(` ${msgObj.removed}: ${getToolFilePath(tool)}`));
677
+ console.log(chalk.gray(` ${msgObj.removed}: ${removedToolFile}`));
675
678
  } catch {
676
- console.log(chalk.yellow(` ${msgObj.couldNotRemove}: ${getToolFilePath(tool)}`));
679
+ console.log(chalk.yellow(` ${msgObj.couldNotRemove}: ${removedToolFile}`));
677
680
  }
678
681
  }
679
682
 
@@ -711,7 +714,7 @@ export async function runProjectConfiguration(options) {
711
714
  }
712
715
 
713
716
  // Remove from integrationBlockHashes (keyed by tool file path)
714
- const toolFileName = getToolFilePath(tool);
717
+ const toolFileName = removedToolFile;
715
718
  if (manifest.integrationBlockHashes?.[toolFileName]) {
716
719
  delete manifest.integrationBlockHashes[toolFileName];
717
720
  }
@@ -854,7 +857,7 @@ export async function runProjectConfiguration(options) {
854
857
  }
855
858
 
856
859
  // Apply changes
857
- const spinner = ora(msgObj.updatingConfig).start();
860
+ const spinner = createSpinner(msgObj.updatingConfig).start();
858
861
 
859
862
  const results = {
860
863
  copied: [],
@@ -914,7 +917,7 @@ export async function runProjectConfiguration(options) {
914
917
 
915
918
  // Regenerate integration files if needed
916
919
  if (needsIntegrationRegeneration && newAITools.length > 0) {
917
- const intSpinner = ora(msgObj.regeneratingIntegrations).start();
920
+ const intSpinner = createSpinner(msgObj.regeneratingIntegrations).start();
918
921
 
919
922
  // Build installed standards list
920
923
  // Raw, not basename()d. Resolution needs the registry (an ID is not a
@@ -934,7 +937,8 @@ export async function runProjectConfiguration(options) {
934
937
  const generatedFiles = new Set();
935
938
 
936
939
  for (const tool of newAITools) {
937
- const targetFile = getToolFilePath(tool);
940
+ // XSPEC-418 R3
941
+ const targetFile = resolveIntegrationTargetFile(tool, manifest);
938
942
  if (generatedFiles.has(targetFile)) {
939
943
  continue; // Skip if already generated (AGENTS.md sharing)
940
944
  }
@@ -947,7 +951,9 @@ export async function runProjectConfiguration(options) {
947
951
  standardsFormat: manifest.format || 'ai',
948
952
  contentMode: newContentMode,
949
953
  // Pass output_language for dynamic commit standards generation
950
- outputLanguage: newOptions.output_language || 'english'
954
+ outputLanguage: newOptions.output_language || 'english',
955
+ // XSPEC-418 R2/R3
956
+ integrationTargets: manifest.integrationTargets
951
957
  };
952
958
 
953
959
  const result = writeIntegrationFile(tool, toolConfig, projectPath);
@@ -989,7 +995,7 @@ export async function runProjectConfiguration(options) {
989
995
 
990
996
  if (confirmSkills) {
991
997
  const newInstallations = skillCapableTools.map(agent => ({ agent, level }));
992
- const spinner = ora(t('config.installingSkillsForNewTools', 'Installing Skills for new tools...')).start();
998
+ const spinner = createSpinner(t('config.installingSkillsForNewTools', 'Installing Skills for new tools...')).start();
993
999
  const skillResult = await installSkillsToMultipleAgents(newInstallations, null, projectPath, cmdLocale);
994
1000
  spinner.succeed(t('config.skillsInstalledForNewTools', 'Skills installed for new tools'));
995
1001
 
@@ -1026,7 +1032,7 @@ export async function runProjectConfiguration(options) {
1026
1032
 
1027
1033
  if (confirmCmds) {
1028
1034
  const newCmdInstallations = commandCapableTools.map(agent => ({ agent, level }));
1029
- const spinner = ora(t('config.installingCommandsForNewTools', 'Installing Commands for new tools...')).start();
1035
+ const spinner = createSpinner(t('config.installingCommandsForNewTools', 'Installing Commands for new tools...')).start();
1030
1036
  const cmdResult = await installCommandsToMultipleAgents(newCmdInstallations, null, projectPath, cmdLocale);
1031
1037
  spinner.succeed(t('config.commandsInstalledForNewTools', 'Commands installed for new tools'));
1032
1038
 
@@ -1052,7 +1058,7 @@ export async function runProjectConfiguration(options) {
1052
1058
  manifest.options = newOptions;
1053
1059
  manifest.contentMode = newContentMode;
1054
1060
  manifest.aiTools = newAITools;
1055
- manifest.version = '3.2.0';
1061
+ bumpManifestVersion(manifest);
1056
1062
 
1057
1063
  // Update methodology
1058
1064
  if (newMethodology) {
@@ -1124,7 +1130,7 @@ export async function runProjectConfiguration(options) {
1124
1130
 
1125
1131
  if (options.yes) {
1126
1132
  // --yes flag: auto-apply without prompting
1127
- const applySpinner = ora(msgObj.applyingChanges).start();
1133
+ const applySpinner = createSpinner(msgObj.applyingChanges).start();
1128
1134
  const applyResults = regenerateIntegrations(projectPath, manifest);
1129
1135
  applySpinner.succeed(msgObj.changesApplied || msgObj.regeneratedIntegrations.replace('{count}', applyResults.updated.length));
1130
1136
 
@@ -1146,7 +1152,7 @@ export async function runProjectConfiguration(options) {
1146
1152
  });
1147
1153
 
1148
1154
  if (apply) {
1149
- const applySpinner = ora(msgObj.applyingChanges).start();
1155
+ const applySpinner = createSpinner(msgObj.applyingChanges).start();
1150
1156
  const applyResults = regenerateIntegrations(projectPath, manifest);
1151
1157
  applySpinner.succeed(msgObj.changesApplied || msgObj.regeneratedIntegrations.replace('{count}', applyResults.updated.length));
1152
1158
 
@@ -1172,12 +1178,12 @@ export async function runProjectConfiguration(options) {
1172
1178
  if (hasSkillInstalls || hasCommandInstalls) {
1173
1179
  const cmdLocale = displayLanguageToLocale(newOptions.display_language);
1174
1180
  if (hasSkillInstalls) {
1175
- const skillSpinner = ora(t('config.reinstallingSkills', 'Reinstalling Skills...')).start();
1181
+ const skillSpinner = createSpinner(t('config.reinstallingSkills', 'Reinstalling Skills...')).start();
1176
1182
  await installSkillsToMultipleAgents(manifest.skills.installations, null, projectPath, cmdLocale);
1177
1183
  skillSpinner.succeed(t('config.skillsReinstalled', 'Skills reinstalled'));
1178
1184
  }
1179
1185
  if (hasCommandInstalls) {
1180
- const cmdSpinner = ora(t('config.reinstallingCommands', 'Reinstalling Commands...')).start();
1186
+ const cmdSpinner = createSpinner(t('config.reinstallingCommands', 'Reinstalling Commands...')).start();
1181
1187
  await installCommandsToMultipleAgents(manifest.commands.installations, null, projectPath, cmdLocale);
1182
1188
  cmdSpinner.succeed(t('config.commandsReinstalled', 'Commands reinstalled'));
1183
1189
  }
@@ -1222,7 +1228,7 @@ async function handleSkillsConfiguration(manifest, projectPath, msgObj, common,
1222
1228
 
1223
1229
  // Install to specified level (defaults to project)
1224
1230
  const installations = [{ agent: specificTool, level }];
1225
- const spinner = ora(`Installing Skills for ${getAgentDisplayName(specificTool)} (${level} level)...`).start();
1231
+ const spinner = createSpinner(`Installing Skills for ${getAgentDisplayName(specificTool)} (${level} level)...`).start();
1226
1232
  const result = await installSkillsToMultipleAgents(installations, null, projectPath);
1227
1233
  spinner.stop();
1228
1234
 
@@ -1359,7 +1365,7 @@ async function handleSkillsConfiguration(manifest, projectPath, msgObj, common,
1359
1365
  }));
1360
1366
 
1361
1367
  // Install Skills
1362
- const spinner = ora(msgObj.installingSkills || 'Installing Skills...').start();
1368
+ const spinner = createSpinner(msgObj.installingSkills || 'Installing Skills...').start();
1363
1369
  const result = await installSkillsToMultipleAgents(installations, null, projectPath);
1364
1370
  spinner.stop();
1365
1371
 
@@ -1394,7 +1400,7 @@ async function handleSkillsConfiguration(manifest, projectPath, msgObj, common,
1394
1400
  }
1395
1401
 
1396
1402
  // Install Skills
1397
- const spinner = ora(msgObj.installingSkills || 'Installing Skills...').start();
1403
+ const spinner = createSpinner(msgObj.installingSkills || 'Installing Skills...').start();
1398
1404
  const result = await installSkillsToMultipleAgents(installations, null, projectPath);
1399
1405
  spinner.stop();
1400
1406
 
@@ -1453,7 +1459,7 @@ async function handleCommandsConfiguration(manifest, projectPath, msgObj, common
1453
1459
 
1454
1460
  // Install to selected level
1455
1461
  const installations = [{ agent: specificTool, level: commandsLevel }];
1456
- const spinner = ora(`Installing Commands for ${getAgentDisplayName(specificTool)} (${commandsLevel} level)...`).start();
1462
+ const spinner = createSpinner(`Installing Commands for ${getAgentDisplayName(specificTool)} (${commandsLevel} level)...`).start();
1457
1463
  const cmdLocale = displayLanguageToLocale(manifest.options?.display_language);
1458
1464
  const result = await installCommandsToMultipleAgents(installations, null, projectPath, cmdLocale);
1459
1465
  spinner.stop();
@@ -1559,7 +1565,7 @@ async function handleCommandsConfiguration(manifest, projectPath, msgObj, common
1559
1565
  }
1560
1566
 
1561
1567
  // Install Commands
1562
- const spinner = ora(msgObj.installingCommands || 'Installing Commands...').start();
1568
+ const spinner = createSpinner(msgObj.installingCommands || 'Installing Commands...').start();
1563
1569
  const cmdLocale = displayLanguageToLocale(manifest.options?.display_language);
1564
1570
  const result = await installCommandsToMultipleAgents(declinedCommandsWithSupport, null, projectPath, cmdLocale);
1565
1571
  spinner.stop();
@@ -1595,7 +1601,7 @@ async function handleCommandsConfiguration(manifest, projectPath, msgObj, common
1595
1601
  }
1596
1602
 
1597
1603
  // Install Commands
1598
- const spinner = ora(msgObj.installingCommands || 'Installing Commands...').start();
1604
+ const spinner = createSpinner(msgObj.installingCommands || 'Installing Commands...').start();
1599
1605
  const cmdLocale = displayLanguageToLocale(manifest.options?.display_language);
1600
1606
  const result = await installCommandsToMultipleAgents(selectedAgents, null, projectPath, cmdLocale);
1601
1607
  spinner.stop();
@@ -1,8 +1,8 @@
1
1
  import chalk from 'chalk';
2
- import ora from 'ora';
2
+ import { createSpinner } from '../utils/spinner.js';
3
3
  import { execSync } from 'child_process';
4
4
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
5
- import { basename, join } from 'path';
5
+ import { join } from 'path';
6
6
  import {
7
7
  manifestExists as isInitialized,
8
8
  normalizeContentMode,
@@ -26,7 +26,7 @@ import { displayLanguageToLocale } from '../utils/locale.js';
26
26
  import { generateReleaseConfig, RELEASE_MODE_LABELS } from '../utils/release-config.js';
27
27
  import { guardAgainstSelfAdoption } from '../utils/detect-self-adoption.js';
28
28
  import { readInstallYaml } from '../utils/config-manager.js';
29
- import { getToolFilePath } from '../utils/integration-generator.js';
29
+ import { resolveIntegrationTargetFile } from '../utils/integration-generator.js';
30
30
  import { withFileTransaction } from '../utils/transaction.js';
31
31
 
32
32
  /**
@@ -55,7 +55,7 @@ export async function initCommand(options) {
55
55
  }
56
56
 
57
57
  // STEP 2: Detect project characteristics
58
- const spinner = ora(msg.detectingProject).start();
58
+ const spinner = createSpinner(msg.detectingProject).start();
59
59
  const detected = detectAll(projectPath);
60
60
  spinner.succeed(msg.analysisComplete);
61
61
 
@@ -107,7 +107,10 @@ export async function initCommand(options) {
107
107
  // `uds init` re-installs them idempotently.
108
108
  const ownedPaths = [join(projectPath, '.standards')];
109
109
  for (const tool of (config.integrations || config.aiTools || [])) {
110
- const file = getToolFilePath(tool);
110
+ // XSPEC-418 R2: honors --claude-target so a local-target install's owned
111
+ // path is CLAUDE.local.md, not CLAUDE.md — rollback must tear down the
112
+ // file actually written, not the tool's default.
113
+ const file = resolveIntegrationTargetFile(tool, { integrationTargets: config.integrationTargets });
111
114
  if (file) ownedPaths.push(join(projectPath, file));
112
115
  }
113
116
  if (config.generateAgentsMd) ownedPaths.push(join(projectPath, 'AGENTS.md'));
@@ -123,7 +126,16 @@ export async function initCommand(options) {
123
126
  apply: async () => {
124
127
  // 1. Install Standards
125
128
  const standardsResults = await installStandards(config, projectPath);
126
- config.installedStandards = standardsResults.standards.map(s => basename(s));
129
+ // Paths, not basenames. Everything downstream classifies an option
130
+ // by an `/options/` segment, so reducing to basenames here is not a
131
+ // simplification — it deletes the only signal that distinguishes the
132
+ // two, and `contentLayout: flat` installs option files with no
133
+ // directory to recover it from. The index block consequently said
134
+ // "options 0" on every project for two releases, while the manifest
135
+ // right beside it listed seven. `buildToolIntegrationConfig` (the
136
+ // update path's equivalent) has carried a comment warning against
137
+ // exactly this reduction the whole time; only this call site did it.
138
+ config.installedStandards = standardsResults.standards;
127
139
 
128
140
  // 1.5. Generate release-config.yaml if non-default mode selected
129
141
  if (config.releaseMode && config.releaseMode !== 'ci-cd') {
@@ -649,6 +661,23 @@ function buildNonInteractiveConfig(options, detected, projectPath) {
649
661
  ? false // codex/opencode handles AGENTS.md, skip universal output
650
662
  : (options.agentsMd !== undefined ? !!options.agentsMd : true);
651
663
 
664
+ // XSPEC-418 R2: --claude-target <project|local>, default 'project'. An
665
+ // unrecognized value is never substituted quietly (same policy as
666
+ // --content-mode above) — it falls back to 'project' with a warning rather
667
+ // than silently writing into CLAUDE.local.md or vice versa.
668
+ let claudeTargetFlag = options.claudeTarget || 'project';
669
+ if (claudeTargetFlag !== 'project' && claudeTargetFlag !== 'local') {
670
+ console.log(chalk.yellow(
671
+ `⚠ Unknown --claude-target '${claudeTargetFlag}'; using 'project'. Supported: project, local.`
672
+ ));
673
+ claudeTargetFlag = 'project';
674
+ }
675
+ // Only ever set when 'local' — 'project' or unspecified leaves the manifest
676
+ // shape completely unchanged (XSPEC-418 AC-5).
677
+ const integrationTargets = claudeTargetFlag === 'local'
678
+ ? { 'claude-code': 'CLAUDE.local.md' }
679
+ : undefined;
680
+
652
681
  return {
653
682
  languages: options.lang ? [options.lang] : Object.keys(detected.languages).filter(k => detected.languages[k]),
654
683
  frameworks: options.framework ? [options.framework] : Object.keys(detected.frameworks).filter(k => detected.frameworks[k]),
@@ -669,7 +698,8 @@ function buildNonInteractiveConfig(options, detected, projectPath) {
669
698
  generateAgentsMd,
670
699
  releaseMode: options.releaseMode || 'ci-cd',
671
700
  withHooks: !!options.withHooks,
672
- contentLayout: options.contentLayout || 'flat'
701
+ contentLayout: options.contentLayout || 'flat',
702
+ integrationTargets
673
703
  };
674
704
  }
675
705
 
@@ -9,7 +9,7 @@
9
9
  */
10
10
 
11
11
  import chalk from 'chalk';
12
- import ora from 'ora';
12
+ import { createSpinner } from '../utils/spinner.js';
13
13
  import { select, confirm as inquirerConfirm } from '@inquirer/prompts';
14
14
  import { existsSync, readFileSync } from 'node:fs';
15
15
  import { join } from 'node:path';
@@ -48,7 +48,7 @@ export async function specCreateCommand(intent, options = {}) {
48
48
  output: options.output
49
49
  });
50
50
 
51
- const spinner = ora({
51
+ const spinner = createSpinner({
52
52
  text: t('spec.generating', 'Generating micro-spec...'),
53
53
  color: 'cyan'
54
54
  }).start();