universal-dev-standards 6.8.0 → 6.10.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 (225) hide show
  1. package/bin/uds.js +12 -2
  2. package/bundled/ai/standards/acceptance-criteria-traceability.ai.yaml +14 -2
  3. package/bundled/ai/standards/adr-standards.ai.yaml +14 -2
  4. package/bundled/ai/standards/code-review.ai.yaml +13 -3
  5. package/bundled/ai/standards/commit-message.ai.yaml +8 -4
  6. package/bundled/ai/standards/deferred-item-exit.ai.yaml +225 -0
  7. package/bundled/ai/standards/feature-discovery-standards.ai.yaml +14 -2
  8. package/bundled/ai/standards/governance-layer.ai.yaml +128 -2
  9. package/bundled/ai/standards/logging.ai.yaml +2 -2
  10. package/bundled/ai/standards/retrospective-standards.ai.yaml +14 -2
  11. package/bundled/ai/standards/reverse-engineering-standards.ai.yaml +73 -2
  12. package/bundled/ai/standards/security-standards.ai.yaml +2 -2
  13. package/bundled/ai/standards/spec-driven-development.ai.yaml +14 -2
  14. package/bundled/ai/standards/tech-debt-standards.ai.yaml +87 -3
  15. package/bundled/ai/standards/turn-completion-integrity.ai.yaml +131 -0
  16. package/bundled/core/acceptance-criteria-traceability.md +5 -2
  17. package/bundled/core/adr-standards.md +26 -2
  18. package/bundled/core/agent-communication-protocol.md +8 -0
  19. package/bundled/core/branch-completion.md +8 -0
  20. package/bundled/core/change-batching-standards.md +8 -0
  21. package/bundled/core/code-review-checklist.md +5 -2
  22. package/bundled/core/context-aware-loading.md +1 -1
  23. package/bundled/core/deferred-item-exit.md +254 -0
  24. package/bundled/core/execution-history.md +8 -0
  25. package/bundled/core/feature-discovery-standards.md +5 -1
  26. package/bundled/core/governance-layer.md +114 -2
  27. package/bundled/core/pipeline-integration-standards.md +8 -0
  28. package/bundled/core/retrospective-standards.md +4 -2
  29. package/bundled/core/reverse-engineering-standards.md +81 -2
  30. package/bundled/core/spec-driven-development.md +8 -2
  31. package/bundled/core/tech-debt-standards.md +67 -8
  32. package/bundled/core/turn-completion-integrity.md +196 -0
  33. package/bundled/core/workflow-enforcement.md +8 -0
  34. package/bundled/core/workflow-state-protocol.md +8 -0
  35. package/bundled/hooks/check-dangerous-cmd.mjs +60 -0
  36. package/bundled/hooks/check-logging-standard.mjs +59 -0
  37. package/bundled/hooks/check-turn-completion.mjs +233 -0
  38. package/bundled/hooks/inject-standards.mjs +183 -0
  39. package/bundled/hooks/telemetry-wrapper.mjs +77 -0
  40. package/bundled/hooks/turn-completion/detect.mjs +99 -0
  41. package/bundled/hooks/turn-completion/locales/en.mjs +159 -0
  42. package/bundled/hooks/turn-completion/locales/zh-TW.mjs +166 -0
  43. package/bundled/hooks/validate-commit-msg.mjs +104 -0
  44. package/bundled/locales/zh-CN/CHANGELOG.md +73 -3
  45. package/bundled/locales/zh-CN/CLAUDE.md +1 -1
  46. package/bundled/locales/zh-CN/README.md +2 -2
  47. package/bundled/locales/zh-CN/SECURITY.md +1 -1
  48. package/bundled/locales/zh-CN/core/adr-standards.md +1 -1
  49. package/bundled/locales/zh-CN/core/agent-communication-protocol.md +7 -0
  50. package/bundled/locales/zh-CN/core/branch-completion.md +7 -0
  51. package/bundled/locales/zh-CN/core/change-batching-standards.md +7 -0
  52. package/bundled/locales/zh-CN/core/execution-history.md +7 -0
  53. package/bundled/locales/zh-CN/core/governance-layer.md +118 -6
  54. package/bundled/locales/zh-CN/core/pipeline-integration-standards.md +7 -0
  55. package/bundled/locales/zh-CN/core/retrospective-standards.md +1 -1
  56. package/bundled/locales/zh-CN/core/tech-debt-standards.md +71 -4
  57. package/bundled/locales/zh-CN/core/turn-completion-integrity.md +190 -0
  58. package/bundled/locales/zh-CN/core/workflow-enforcement.md +7 -0
  59. package/bundled/locales/zh-CN/core/workflow-state-protocol.md +7 -0
  60. package/bundled/locales/zh-CN/docs/CHEATSHEET.md +8 -1
  61. package/bundled/locales/zh-CN/docs/CLI-INIT-OPTIONS.md +29 -68
  62. package/bundled/locales/zh-CN/docs/FEATURE-REFERENCE.md +25 -15
  63. package/bundled/locales/zh-CN/docs/MIGRATION-v6.md +8 -4
  64. package/bundled/locales/zh-CN/docs/USAGE-MODES-COMPARISON.md +1 -2
  65. package/bundled/locales/zh-CN/integrations/google-antigravity/{INSTRUCTIONS.md → AGENTS.md} +1 -1
  66. package/bundled/locales/zh-CN/integrations/google-antigravity/README.md +3 -3
  67. package/bundled/locales/zh-CN/skills/atdd-assistant/SKILL.md +2 -0
  68. package/bundled/locales/zh-CN/skills/bdd-assistant/SKILL.md +2 -0
  69. package/bundled/locales/zh-CN/skills/brainstorm-assistant/SKILL.md +22 -12
  70. package/bundled/locales/zh-CN/skills/brainstorm-assistant/guide.md +12 -9
  71. package/bundled/locales/zh-CN/skills/code-review-assistant/SKILL.md +1 -0
  72. package/bundled/locales/zh-CN/skills/commands/brainstorm.md +17 -13
  73. package/bundled/locales/zh-CN/skills/commands/config.md +0 -1
  74. package/bundled/locales/zh-CN/skills/commands/init.md +1 -2
  75. package/bundled/locales/zh-CN/skills/commit-standards/SKILL.md +2 -0
  76. package/bundled/locales/zh-CN/skills/contract-test-assistant/SKILL.md +1 -0
  77. package/bundled/locales/zh-CN/skills/dev-methodology/SKILL.md +2 -0
  78. package/bundled/locales/zh-CN/skills/observability-assistant/SKILL.md +1 -0
  79. package/bundled/locales/zh-CN/skills/project-structure-guide/SKILL.md +1 -0
  80. package/bundled/locales/zh-CN/skills/release-standards/SKILL.md +3 -0
  81. package/bundled/locales/zh-CN/skills/requirement-assistant/SKILL.md +2 -0
  82. package/bundled/locales/zh-CN/skills/reverse-engineer/SKILL.md +3 -0
  83. package/bundled/locales/zh-CN/skills/runbook-assistant/SKILL.md +1 -0
  84. package/bundled/locales/zh-CN/skills/slo-assistant/SKILL.md +1 -0
  85. package/bundled/locales/zh-CN/skills/tdd-assistant/SKILL.md +2 -0
  86. package/bundled/locales/zh-TW/CHANGELOG.md +74 -3
  87. package/bundled/locales/zh-TW/CLAUDE.md +1 -1
  88. package/bundled/locales/zh-TW/README.md +2 -2
  89. package/bundled/locales/zh-TW/SECURITY.md +1 -1
  90. package/bundled/locales/zh-TW/core/acceptance-criteria-traceability.md +2 -0
  91. package/bundled/locales/zh-TW/core/adr-standards.md +26 -5
  92. package/bundled/locales/zh-TW/core/agent-communication-protocol.md +7 -0
  93. package/bundled/locales/zh-TW/core/branch-completion.md +7 -0
  94. package/bundled/locales/zh-TW/core/change-batching-standards.md +7 -0
  95. package/bundled/locales/zh-TW/core/code-review-checklist.md +2 -0
  96. package/bundled/locales/zh-TW/core/container-image-standards.md +2 -2
  97. package/bundled/locales/zh-TW/core/contract-testing-standards.md +2 -2
  98. package/bundled/locales/zh-TW/core/cross-flow-regression.md +8 -7
  99. package/bundled/locales/zh-TW/core/data-contract.md +2 -2
  100. package/bundled/locales/zh-TW/core/data-migration-testing.md +2 -2
  101. package/bundled/locales/zh-TW/core/data-pipeline.md +2 -2
  102. package/bundled/locales/zh-TW/core/deferred-item-exit.md +251 -0
  103. package/bundled/locales/zh-TW/core/documentation-writing-standards.md +228 -3
  104. package/bundled/locales/zh-TW/core/execution-history.md +7 -0
  105. package/bundled/locales/zh-TW/core/full-coverage-testing.md +15 -2
  106. package/bundled/locales/zh-TW/core/governance-layer.md +118 -5
  107. package/bundled/locales/zh-TW/core/iac-design-principles.md +2 -2
  108. package/bundled/locales/zh-TW/core/incident-response.md +2 -2
  109. package/bundled/locales/zh-TW/core/model-provenance.md +4 -2
  110. package/bundled/locales/zh-TW/core/pii-classification.md +42 -6
  111. package/bundled/locales/zh-TW/core/pipeline-integration-standards.md +7 -0
  112. package/bundled/locales/zh-TW/core/prd-standards.md +4 -2
  113. package/bundled/locales/zh-TW/core/product-metrics-standards.md +4 -2
  114. package/bundled/locales/zh-TW/core/release-readiness-gate.md +2 -2
  115. package/bundled/locales/zh-TW/core/resource-cost-boundary.md +2 -2
  116. package/bundled/locales/zh-TW/core/retrospective-standards.md +5 -3
  117. package/bundled/locales/zh-TW/core/reverse-engineering-standards.md +83 -5
  118. package/bundled/locales/zh-TW/core/runbook.md +2 -2
  119. package/bundled/locales/zh-TW/core/schema-evolution.md +2 -2
  120. package/bundled/locales/zh-TW/core/secret-management-standards.md +2 -2
  121. package/bundled/locales/zh-TW/core/slo-sli.md +2 -2
  122. package/bundled/locales/zh-TW/core/spec-driven-development.md +2 -0
  123. package/bundled/locales/zh-TW/core/tech-debt-standards.md +71 -4
  124. package/bundled/locales/zh-TW/core/turn-completion-integrity.md +190 -0
  125. package/bundled/locales/zh-TW/core/user-journey-testing.md +2 -2
  126. package/bundled/locales/zh-TW/core/user-story-mapping.md +2 -2
  127. package/bundled/locales/zh-TW/core/verification-oracle.md +2 -2
  128. package/bundled/locales/zh-TW/core/workflow-enforcement.md +7 -0
  129. package/bundled/locales/zh-TW/core/workflow-state-protocol.md +7 -0
  130. package/bundled/locales/zh-TW/docs/CHEATSHEET.md +8 -1
  131. package/bundled/locales/zh-TW/docs/CLI-INIT-OPTIONS.md +29 -68
  132. package/bundled/locales/zh-TW/docs/FEATURE-REFERENCE.md +25 -15
  133. package/bundled/locales/zh-TW/docs/MIGRATION-v6.md +8 -4
  134. package/bundled/locales/zh-TW/docs/USAGE-MODES-COMPARISON.md +1 -2
  135. package/bundled/locales/zh-TW/integrations/google-antigravity/{INSTRUCTIONS.md → AGENTS.md} +1 -1
  136. package/bundled/locales/zh-TW/integrations/google-antigravity/README.md +3 -3
  137. package/bundled/locales/zh-TW/skills/adr-assistant/SKILL.md +1 -1
  138. package/bundled/locales/zh-TW/skills/atdd-assistant/SKILL.md +2 -0
  139. package/bundled/locales/zh-TW/skills/bdd-assistant/SKILL.md +2 -0
  140. package/bundled/locales/zh-TW/skills/brainstorm-assistant/SKILL.md +22 -12
  141. package/bundled/locales/zh-TW/skills/brainstorm-assistant/guide.md +12 -9
  142. package/bundled/locales/zh-TW/skills/code-review-assistant/SKILL.md +1 -0
  143. package/bundled/locales/zh-TW/skills/commands/brainstorm.md +17 -13
  144. package/bundled/locales/zh-TW/skills/commands/config.md +0 -1
  145. package/bundled/locales/zh-TW/skills/commands/init.md +1 -2
  146. package/bundled/locales/zh-TW/skills/commit-standards/SKILL.md +2 -0
  147. package/bundled/locales/zh-TW/skills/contract-test-assistant/SKILL.md +2 -1
  148. package/bundled/locales/zh-TW/skills/dev-methodology/SKILL.md +2 -0
  149. package/bundled/locales/zh-TW/skills/dev-workflow-guide/SKILL.md +1 -1
  150. package/bundled/locales/zh-TW/skills/knowledge-graph/guide.md +2 -2
  151. package/bundled/locales/zh-TW/skills/migration-assistant/SKILL.md +1 -1
  152. package/bundled/locales/zh-TW/skills/observability-assistant/SKILL.md +1 -0
  153. package/bundled/locales/zh-TW/skills/project-discovery/SKILL.md +1 -0
  154. package/bundled/locales/zh-TW/skills/project-structure-guide/SKILL.md +1 -0
  155. package/bundled/locales/zh-TW/skills/release-standards/SKILL.md +3 -0
  156. package/bundled/locales/zh-TW/skills/requirement-assistant/SKILL.md +2 -0
  157. package/bundled/locales/zh-TW/skills/reverse-engineer/SKILL.md +3 -0
  158. package/bundled/locales/zh-TW/skills/runbook-assistant/SKILL.md +1 -0
  159. package/bundled/locales/zh-TW/skills/slo-assistant/SKILL.md +1 -0
  160. package/bundled/locales/zh-TW/skills/tdd-assistant/SKILL.md +2 -0
  161. package/bundled/skills/atdd-assistant/SKILL.md +2 -0
  162. package/bundled/skills/bdd-assistant/SKILL.md +2 -0
  163. package/bundled/skills/brainstorm-assistant/SKILL.md +31 -13
  164. package/bundled/skills/brainstorm-assistant/guide.md +9 -6
  165. package/bundled/skills/code-review-assistant/SKILL.md +1 -0
  166. package/bundled/skills/commands/brainstorm.md +12 -9
  167. package/bundled/skills/commands/config.md +0 -1
  168. package/bundled/skills/commands/init.md +2 -3
  169. package/bundled/skills/commit-standards/SKILL.md +2 -0
  170. package/bundled/skills/contract-test-assistant/SKILL.md +1 -0
  171. package/bundled/skills/dev-methodology/SKILL.md +4 -0
  172. package/bundled/skills/observability-assistant/SKILL.md +1 -0
  173. package/bundled/skills/project-discovery/SKILL.md +1 -0
  174. package/bundled/skills/project-structure-guide/SKILL.md +1 -0
  175. package/bundled/skills/release-standards/SKILL.md +3 -0
  176. package/bundled/skills/requirement-assistant/SKILL.md +2 -0
  177. package/bundled/skills/reverse-engineer/SKILL.md +3 -0
  178. package/bundled/skills/runbook-assistant/SKILL.md +1 -0
  179. package/bundled/skills/slo-assistant/SKILL.md +1 -0
  180. package/bundled/skills/tdd-assistant/SKILL.md +2 -0
  181. package/bundled/templates/.ai-context.yaml.template +194 -0
  182. package/bundled/templates/CLAUDE.md.template +145 -0
  183. package/bundled/templates/DESIGN.md +237 -0
  184. package/bundled/templates/SKILL-BRIEF-TEMPLATE.md +57 -0
  185. package/bundled/templates/SKILL-CANDIDATES.md +39 -0
  186. package/bundled/templates/gates/check-error-exit.mjs +309 -0
  187. package/bundled/templates/mcp-config.json +10 -0
  188. package/bundled/templates/methodology-template.yaml +209 -0
  189. package/bundled/templates/migration-template.md +408 -0
  190. package/bundled/templates/requirement-checklist.md +410 -0
  191. package/bundled/templates/requirement-document-template.md +591 -0
  192. package/bundled/templates/requirement-template.md +881 -0
  193. package/bundled/templates/reverse-spec-template.md +409 -0
  194. package/bundled/templates/test-case-template.md +74 -0
  195. package/bundled/templates/test-plan-template.md +74 -0
  196. package/package.json +12 -9
  197. package/src/commands/audit.js +82 -0
  198. package/src/commands/check.js +227 -34
  199. package/src/commands/config.js +19 -19
  200. package/src/commands/init.js +174 -20
  201. package/src/commands/spec.js +2 -2
  202. package/src/commands/update.js +537 -43
  203. package/src/compilers/claude-code-compiler.js +4 -1
  204. package/src/config/ai-agent-paths.js +62 -17
  205. package/src/core/constants.js +42 -11
  206. package/src/core/manifest.js +201 -3
  207. package/src/core/paths.js +2 -2
  208. package/src/i18n/messages.js +42 -29
  209. package/src/installers/hooks-installer.js +167 -75
  210. package/src/installers/integration-installer.js +13 -9
  211. package/src/installers/skills-installer.js +4 -4
  212. package/src/installers/standards-installer.js +3 -3
  213. package/src/prompts/init.js +47 -18
  214. package/src/reconciler/diff-engine.js +57 -2
  215. package/src/reconciler/plan-executor.js +31 -11
  216. package/src/utils/detector.js +21 -1
  217. package/src/utils/effect-boundary.js +1093 -0
  218. package/src/utils/hasher.js +166 -1
  219. package/src/utils/hook-stats.js +1 -1
  220. package/src/utils/integration-generator.js +288 -82
  221. package/src/utils/reference-sync.js +107 -8
  222. package/src/utils/registry.js +57 -0
  223. package/src/utils/spinner.js +31 -0
  224. package/src/utils/yaml-generator.js +51 -9
  225. package/standards-registry.json +32 -9
@@ -1,13 +1,13 @@
1
1
  import chalk from 'chalk';
2
2
  import { select } from '@inquirer/prompts';
3
- import ora from 'ora';
3
+ import { createSpinner } from '../utils/spinner.js';
4
4
  import { existsSync, readFileSync } from 'fs';
5
5
  import { join, basename } from 'path';
6
6
  import { execSync } from 'child_process';
7
7
  import { readManifest, writeManifest, isInitialized, copyStandard, copyIntegration } from '../utils/copier.js';
8
8
  import {
9
9
  getAllStandards,
10
- getRepositoryInfo, resolveStandardFilename, resolveStandardSourcePath } from '../utils/registry.js';
10
+ getRepositoryInfo, isShippedFilename, resolveStandardFilename, resolveStandardSourcePath } from '../utils/registry.js';
11
11
  import {
12
12
  computeFileHash,
13
13
  compareFileHash,
@@ -27,9 +27,11 @@ import {
27
27
  } from '../config/ai-agent-paths.js';
28
28
  import {
29
29
  parseReferences,
30
+ findBrokenPathMentions,
30
31
  compareStandardsWithReferences
31
32
  } from '../utils/reference-sync.js';
32
- import { extractMarkedContent, getToolFilePath, parseStandardsIndexCount } from '../utils/integration-generator.js';
33
+ import { extractMarkedContent, getToolFilePath, parseStandardsIndexCount, writeIntegrationFile } from '../utils/integration-generator.js';
34
+ import { INTEGRATION_MAPPINGS } from '../installers/integration-installer.js';
33
35
  import { getToolFormat } from '../core/constants.js';
34
36
  import { checkForUpdates } from '../utils/npm-registry.js';
35
37
  import { writeUpdateCache } from '../utils/update-checker.js';
@@ -59,6 +61,19 @@ function displayFileIntegritySummary(fileStatus, msg) {
59
61
  }
60
62
  }
61
63
 
64
+ // Reported apart from `missing`, and in grey rather than red, because the
65
+ // adopter who sees this did what the migration guide asked. The remedy named
66
+ // here is the one that works: `--restore` cannot resolve a source for any of
67
+ // them, and used to be the only thing offered.
68
+ if (fileStatus.retired?.length > 0) {
69
+ console.log();
70
+ console.log(chalk.gray(` ${(msg.retiredHeader || '{count} tracked file(s) are no longer shipped by UDS:').replace('{count}', fileStatus.retired.length)}`));
71
+ for (const file of fileStatus.retired) {
72
+ console.log(chalk.gray(` - ${file} (${msg.retired || 'no longer shipped by UDS'})`));
73
+ }
74
+ console.log(chalk.gray(` ${msg.retiredHint || 'Run `uds update --prune` to drop their manifest entries.'}`));
75
+ }
76
+
62
77
  if (fileStatus.noHash.length > 0) {
63
78
  for (const file of fileStatus.noHash) {
64
79
  console.log(chalk.gray(` ? ${file} (${msg.existsNoHash})`));
@@ -70,10 +85,39 @@ function displayFileIntegritySummary(fileStatus, msg) {
70
85
  .replace('{unchanged}', fileStatus.unchanged.length)
71
86
  .replace('{modified}', fileStatus.modified.length)
72
87
  .replace('{missing}', fileStatus.missing.length)}` +
88
+ (fileStatus.retired?.length > 0 ? `, ${fileStatus.retired.length} ${msg.retired || 'no longer shipped'}` : '') +
73
89
  (fileStatus.noHash.length > 0 ? `, ${fileStatus.noHash.length} no hash` : '')));
74
90
  console.log();
75
91
  }
76
92
 
93
+ /**
94
+ * A file `fileHashes` tracks has gone. Is that a problem, or is it the migration
95
+ * guide's instruction carried out?
96
+ *
97
+ * MIGRATION-v6 §2 told adopters to delete the seven descoped `.ai.yaml` copies
98
+ * by hand. Doing so left the hash entries behind, and this check called each one
99
+ * `missing` and offered `uds check --restore` — a restore that then failed on
100
+ * every one with "could not determine source", because there has been no source
101
+ * upstream since 6.0.0. The tool asked the user to undo its own documentation,
102
+ * using a command it already knew could not work.
103
+ *
104
+ * The distinguishing question is whether UDS still ships a file by that name,
105
+ * which is the registry's to answer, not the manifest's. Anything outside
106
+ * `.standards/` is never retired: integration files are generated, not shipped
107
+ * under a standard's name, so a missing CLAUDE.md is exactly what it looks like.
108
+ *
109
+ * @param {string} relativePath - Path as recorded in fileHashes
110
+ * @param {Object} manifest - Project manifest (unused today; kept so callers
111
+ * need not know whether the answer depends on project state)
112
+ * @returns {'missing'|'retired'}
113
+ */
114
+ export function classifyMissingFile(relativePath, manifest) { // eslint-disable-line no-unused-vars
115
+ const normalized = String(relativePath).replace(/\\/g, '/');
116
+ if (!normalized.startsWith('.standards/')) return 'missing';
117
+ const fileName = normalized.split('/').pop();
118
+ return isShippedFilename(fileName) ? 'missing' : 'retired';
119
+ }
120
+
77
121
  /**
78
122
  * Perform integrity check for standards and integration files
79
123
  * @returns {Object} File status object
@@ -83,6 +127,10 @@ function performFileIntegrityCheck(projectPath, manifest, msg) {
83
127
  unchanged: [],
84
128
  modified: [],
85
129
  missing: [],
130
+ // Tracked, gone, and gone on purpose: UDS stopped shipping it. Counted
131
+ // apart from `missing` so the summary line stops calling a completed
132
+ // migration a fault.
133
+ retired: [],
86
134
  noHash: []
87
135
  };
88
136
 
@@ -100,7 +148,7 @@ function performFileIntegrityCheck(projectPath, manifest, msg) {
100
148
  fileStatus.modified.push(relativePath);
101
149
  break;
102
150
  case 'missing':
103
- fileStatus.missing.push(relativePath);
151
+ fileStatus[classifyMissingFile(relativePath, manifest)].push(relativePath);
104
152
  break;
105
153
  }
106
154
  }
@@ -415,6 +463,9 @@ export async function checkCommand(options = {}) {
415
463
  // XSPEC-178: Full coverage compliance check
416
464
  checkFullCoverageCompliance(manifest, projectPath);
417
465
 
466
+ // 錯誤訊息單一出口閘門是否已安裝(只報告,不寫入)
467
+ checkErrorExitGate(projectPath);
468
+
418
469
  // Workflow status
419
470
  displayWorkflowStatus(projectPath);
420
471
 
@@ -664,6 +715,27 @@ export async function restoreSingleFile(projectPath, manifest, relativePath, msg
664
715
  msg = t().commands.check;
665
716
  }
666
717
 
718
+ // 🔴 Integration files are GENERATED, not copied. Restoring one by downloading a
719
+ // repo template overwrites the adopter's file with a document they never had —
720
+ // `integrations/claude-code/CLAUDE.md` is 140 lines last touched 2026-03-25, and no
721
+ // code path writes it at install time. The installer already records how each file
722
+ // was generated (`manifest.integrationConfigs`), so a correct restore regenerates
723
+ // from that, exactly as `uds update --sync-refs` does.
724
+ const genConfig = manifest.integrationConfigs?.[relativePath];
725
+ if (genConfig?.tool) {
726
+ const result = writeIntegrationFile(genConfig.tool, {
727
+ ...genConfig,
728
+ installedStandards: genConfig.installedStandards || manifest.standards || []
729
+ }, projectPath);
730
+ if (result.success) {
731
+ updateFileHash(projectPath, manifest, relativePath);
732
+ console.log(chalk.green(` ✓ ${relativePath}: ${msg.restored}`));
733
+ return true;
734
+ }
735
+ console.log(chalk.red(` ✗ ${relativePath}: ${result.error}`));
736
+ return false;
737
+ }
738
+
667
739
  const sourcePath = getSourcePathFromRelative(manifest, relativePath);
668
740
  if (!sourcePath) {
669
741
  console.log(chalk.red(` ✗ ${relativePath}: ${msg.couldNotDetermineSource}`));
@@ -775,15 +847,18 @@ export function getSourcePathFromRelative(manifest, relativePath) {
775
847
 
776
848
  // Check integrations - these might need special handling
777
849
  if (manifest.integrations.some(i => (resolveIntegrationFile(i) || i) === relativePath)) {
778
- // Integration files have different source paths
779
- const integrationMappings = {
780
- '.cursorrules': 'integrations/cursor/.cursorrules',
781
- '.windsurfrules': 'integrations/windsurf/.windsurfrules',
782
- '.clinerules': 'integrations/cline/.clinerules',
783
- '.github/copilot-instructions.md': 'integrations/github-copilot/copilot-instructions.md',
784
- 'CLAUDE.md': 'integrations/claude-code/CLAUDE.md'
785
- };
786
- return integrationMappings[relativePath] || null;
850
+ // 🔴 This was a hand-copied list of five targets. The installer's own
851
+ // INTEGRATION_MAPPINGS covers eight and is the map actually used when a
852
+ // generation falls back to a static copy, so a second copy here could only
853
+ // drift — and had: it named `CLAUDE.md`, which INTEGRATION_MAPPINGS does not
854
+ // contain because no code path ever writes that template.
855
+ //
856
+ // Reached only for legacy manifests with no `integrationConfigs`; a current
857
+ // install regenerates instead (see restoreSingleFile).
858
+ for (const { source, target } of Object.values(INTEGRATION_MAPPINGS)) {
859
+ if (target === relativePath) return source;
860
+ }
861
+ return null;
787
862
  }
788
863
 
789
864
  return null;
@@ -1003,6 +1078,34 @@ function displaySkillsStatus(manifest, projectPath, msg) {
1003
1078
  return { missingSkills, missingCommands };
1004
1079
  }
1005
1080
 
1081
+ /**
1082
+ * 錯誤訊息單一出口閘門:**偵測並報告,絕不寫入。**
1083
+ *
1084
+ * 🔴 為什麼只報告:`uds update` 之外沒有任何時刻是採用者同意我們動他的 repo 的。
1085
+ * 未經同意往別人的專案寫可執行腳本,比它要修的那個問題更糟。
1086
+ * 所以這裡只說「你沒有這道閘門」與「怎麼拿」,寫入留給 `uds update`,
1087
+ * 而那裡會顯示內容並要人點頭。
1088
+ *
1089
+ * 起因(VibeOps 2026-09-03):使用者照著建議打上名字,畫面回他一句 `Bad Request`。
1090
+ * 真正的原因寫在回應的 `details` 裡,而呼叫端只讀了 `error`。當天的修法改了一個
1091
+ * 呼叫端;走訪之後發現同型的有二十四個——其中三個在同一個檔案裡。
1092
+ */
1093
+ function checkErrorExitGate(projectPath) {
1094
+ const gatePath = join(projectPath, 'scripts', 'check-error-exit.mjs');
1095
+ if (existsSync(gatePath)) {
1096
+ return; // 裝了就不出聲。這裡的沉默代表「有」,不代表「跑過了」——那是它自己的事。
1097
+ }
1098
+ // 沒有 src/ 的專案(純文件、純設定)本來就沒有這個問題,不要對他們嘮叨。
1099
+ if (!existsSync(join(projectPath, 'src'))) {
1100
+ return;
1101
+ }
1102
+ console.log(chalk.yellow(' ⚠ [error-exit] 沒有錯誤訊息單一出口檢查(scripts/check-error-exit.mjs)。'));
1103
+ console.log(chalk.gray(' 這道閘門防的是「每個呼叫端各自把錯誤回應拼成給人看的字串」——'));
1104
+ console.log(chalk.gray(' 第一處是實作,第二處開始就會各寫各的,而畫面上只剩一句 Bad Request。'));
1105
+ console.log(chalk.gray(' 要裝的話:`uds update` 會顯示內容並徵求同意後寫入。'));
1106
+ console.log();
1107
+ }
1108
+
1006
1109
  /**
1007
1110
  * XSPEC-178: Check full-coverage-testing standard presence and STUB markers
1008
1111
  */
@@ -1095,19 +1198,37 @@ function checkIntegrationFiles(manifest, projectPath, msg) {
1095
1198
  // After migrateStandardsPathsToIds(), manifest.standards contains IDs, so a plain
1096
1199
  // content.includes(id) check would fail for these mismatched entries.
1097
1200
  const allRegistryStds = getAllStandards();
1098
- const idToAiFilename = new Map(
1099
- allRegistryStds
1100
- .filter(s => s.source?.ai)
1101
- .map(s => [s.id, basename(s.source.ai)])
1201
+ // 🔴 `source` is a string for some entries and an object for others. Reading
1202
+ // `s.source.ai` on a string yields undefined, so `zh-tw-locale`
1203
+ // (source: 'extensions/locales/zh-tw.md') fell out of this map and the file it
1204
+ // installs — `.standards/zh-tw.md` — was never recognised: "67/68 項標準已參考,
1205
+ // 缺少: zh-tw-locale" on a project whose index lists it (reported 2026-09-16).
1206
+ const idToFilenames = new Map(
1207
+ allRegistryStds.map(s => {
1208
+ const sources = typeof s.source === 'string'
1209
+ ? [s.source]
1210
+ : [s.source?.ai, s.source?.human].filter(Boolean);
1211
+ return [s.id, sources.map(src => basename(src))];
1212
+ })
1102
1213
  );
1103
1214
 
1104
1215
  let hasIssues = false;
1105
1216
  let checkedCount = 0;
1106
1217
 
1218
+ // codex and opencode both write AGENTS.md, so iterating tools printed the same
1219
+ // file twice with identical verdicts (reported 2026-09-16). One file, one
1220
+ // verdict — the tools that share it are named on the line instead.
1221
+ const toolsByFile = new Map();
1107
1222
  for (const tool of manifest.aiTools) {
1108
- const toolFile = getToolFilePath(tool);
1109
- if (!toolFile) continue;
1223
+ const file = getToolFilePath(tool);
1224
+ if (!file) continue;
1225
+ if (!toolsByFile.has(file)) toolsByFile.set(file, []);
1226
+ toolsByFile.get(file).push(tool);
1227
+ }
1110
1228
 
1229
+ for (const [toolFile, sharingTools] of toolsByFile) {
1230
+ const tool = sharingTools[0];
1231
+ const sharedSuffix = sharingTools.length > 1 ? ` (${sharingTools.join(', ')})` : '';
1111
1232
  const fullPath = join(projectPath, toolFile);
1112
1233
 
1113
1234
  // Check if file exists
@@ -1145,9 +1266,9 @@ function checkIntegrationFiles(manifest, projectPath, msg) {
1145
1266
  // migration. For standards where the ID doesn't match the .ai.yaml basename
1146
1267
  // (e.g. ID "error-code-standards" → file "error-codes.ai.yaml"), we must
1147
1268
  // also check the actual filename so those aren't falsely reported as missing.
1148
- const aiFilename = idToAiFilename.get(stdFile);
1269
+ const filenames = idToFilenames.get(stdFile) || [];
1149
1270
  const isReferenced = content.includes(stdFile) ||
1150
- (aiFilename !== undefined && aiFilename !== stdFile && content.includes(aiFilename)) ||
1271
+ filenames.some(name => name !== stdFile && content.includes(name)) ||
1151
1272
  content.includes(`.standards/${stdFile}`) ||
1152
1273
  content.includes(`standards/${stdFile}`);
1153
1274
 
@@ -1168,7 +1289,7 @@ function checkIntegrationFiles(manifest, projectPath, msg) {
1168
1289
  const declaredCount = parseStandardsIndexCount(content);
1169
1290
  if (declaredCount !== null) {
1170
1291
  if (declaredCount === totalTrackable) {
1171
- console.log(chalk.green(` ✓ ${toolFile}:`));
1292
+ console.log(chalk.green(` ✓ ${toolFile}${sharedSuffix}:`));
1172
1293
  console.log(chalk.gray(` ${msg.standardsIndexPresent}`));
1173
1294
  console.log(chalk.gray(` ${msg.standardsIndexCount
1174
1295
  ? msg.standardsIndexCount.replace('{count}', declaredCount)
@@ -1348,9 +1469,12 @@ function checkReferenceSync(manifest, projectPath, msg) {
1348
1469
  }
1349
1470
 
1350
1471
  const references = parseReferences(content);
1351
- if (references.length === 0) continue;
1472
+ // Whole-file scan, separate from reference sync: the paths that matter here
1473
+ // are the ones outside the UDS block, which no generation ever revisits.
1474
+ const brokenMentions = findBrokenPathMentions(content, projectPath);
1475
+ if (references.length === 0 && brokenMentions.length === 0) continue;
1352
1476
 
1353
- results.push({ integrationPath, references });
1477
+ results.push({ integrationPath, references, brokenMentions });
1354
1478
  }
1355
1479
 
1356
1480
  // Skip entire section if no files have references
@@ -1360,14 +1484,42 @@ function checkReferenceSync(manifest, projectPath, msg) {
1360
1484
 
1361
1485
  let hasIssues = false;
1362
1486
 
1363
- for (const { integrationPath, references } of results) {
1487
+ for (const { integrationPath, references, brokenMentions } of results) {
1488
+ // Paths mentioned anywhere in the file that resolve to nothing. Reported
1489
+ // first and separately from reference sync, because these are usually
1490
+ // outside the UDS markers — left by an older `uds init`, never revisited by
1491
+ // any regeneration, and followed by an AI tool that then finds nothing.
1492
+ if (brokenMentions?.length > 0) {
1493
+ hasIssues = true;
1494
+ console.log(chalk.red(` ✗ ${integrationPath}:`));
1495
+ console.log(chalk.red(` ${msg.brokenMentions || 'mentions paths that do not exist in this project:'}`));
1496
+ for (const path of brokenMentions) {
1497
+ console.log(chalk.red(` - ${path}`));
1498
+ }
1499
+ console.log(chalk.gray(` ${msg.brokenMentionsFix || 'These are usually outside the UDS block and must be corrected by hand; UDS does not rewrite text it did not write.'}`));
1500
+ }
1501
+
1364
1502
  // Compare with manifest standards
1365
- const { orphanedRefs, missingRefs, syncedRefs } = compareStandardsWithReferences(
1503
+ const { orphanedRefs, missingRefs, syncedRefs, danglingRefs } = compareStandardsWithReferences(
1366
1504
  manifest.standards,
1367
- references
1505
+ references,
1506
+ { projectPath, options: manifest.options }
1368
1507
  );
1369
1508
 
1370
1509
  // Report results
1510
+ // 🔴 A reference to a file that is not on disk is the one failure an AI tool
1511
+ // cannot work around: it follows the link and finds nothing. Reported before
1512
+ // the manifest-level findings because it needs no interpretation.
1513
+ if (danglingRefs.length > 0) {
1514
+ hasIssues = true;
1515
+ console.log(chalk.red(` ✗ ${integrationPath}:`));
1516
+ console.log(chalk.red(` ${msg.danglingRefs || 'references files that do not exist:'}`));
1517
+ for (const ref of danglingRefs) {
1518
+ console.log(chalk.red(` - .standards/${ref}`));
1519
+ }
1520
+ console.log(chalk.gray(` ${msg.danglingRefsFix || 'Run `uds update --integrations-only` to regenerate them.'}`));
1521
+ }
1522
+
1371
1523
  if (orphanedRefs.length > 0) {
1372
1524
  hasIssues = true;
1373
1525
  console.log(chalk.yellow(` ⚠ ${integrationPath}:`));
@@ -1386,14 +1538,21 @@ function checkReferenceSync(manifest, projectPath, msg) {
1386
1538
  }
1387
1539
  }
1388
1540
 
1389
- if (orphanedRefs.length === 0 && missingRefs.length === 0) {
1541
+ if (orphanedRefs.length === 0 && missingRefs.length === 0 && danglingRefs.length === 0) {
1390
1542
  console.log(chalk.green(` ✓ ${msg.refsInSync.replace('{path}', integrationPath).replace('{count}', syncedRefs.length)}`));
1391
1543
  }
1392
1544
  }
1393
1545
 
1394
1546
  if (hasIssues) {
1395
1547
  console.log();
1396
- console.log(chalk.yellow(` ${msg.runSyncRefs}`));
1548
+ // `uds update --sync-refs` refuses to run without `integrationConfigs`, and a
1549
+ // project initialised non-interactively never had that key — so this hint
1550
+ // named a command that could not work (reproduced on a clean 6.9.0 project,
1551
+ // 2026-09-16). Name the one that regenerates from the manifest instead.
1552
+ const canSyncRefs = manifest.integrationConfigs && Object.keys(manifest.integrationConfigs).length > 0;
1553
+ console.log(chalk.yellow(` ${canSyncRefs
1554
+ ? msg.runSyncRefs
1555
+ : (msg.runIntegrationsOnly || 'Run `uds update --integrations-only` to regenerate integration files.')}`));
1397
1556
  }
1398
1557
 
1399
1558
  console.log();
@@ -1405,7 +1564,7 @@ function checkReferenceSync(manifest, projectPath, msg) {
1405
1564
  */
1406
1565
  async function checkCliVersion(bundledVersion) {
1407
1566
  const msg = t().commands.check;
1408
- const spinner = ora({ text: msg.checkingCliUpdates, spinner: 'dots' }).start();
1567
+ const spinner = createSpinner({ text: msg.checkingCliUpdates, spinner: 'dots' }).start();
1409
1568
 
1410
1569
  try {
1411
1570
  const result = await checkForUpdates(bundledVersion, {
@@ -1526,17 +1685,17 @@ function checkSkillsIntegrity(manifest, projectPath, msg) {
1526
1685
  * @param {Object} msg - Localized messages
1527
1686
  * @returns {Object} Status { unchanged: [], modified: [], missing: [] }
1528
1687
  */
1529
- function checkCommandsIntegrity(manifest, projectPath, msg) {
1688
+ export function checkCommandsIntegrity(manifest, projectPath, msg) {
1530
1689
  const commandHashes = manifest.commandHashes;
1531
1690
 
1532
1691
  // Skip if no command hashes tracked
1533
1692
  if (!commandHashes || Object.keys(commandHashes).length === 0) {
1534
- return { unchanged: [], modified: [], missing: [], tracked: false };
1693
+ return { unchanged: [], modified: [], missing: [], untracked: [], tracked: false };
1535
1694
  }
1536
1695
 
1537
1696
  console.log(chalk.cyan(msg.commandsIntegrityCheck || 'Commands File Integrity'));
1538
1697
 
1539
- const status = { unchanged: [], modified: [], missing: [], tracked: true };
1698
+ const status = { unchanged: [], modified: [], missing: [], untracked: [], tracked: true };
1540
1699
 
1541
1700
  for (const [hashKey, hashInfo] of Object.entries(commandHashes)) {
1542
1701
  // Parse key format: agent/filename.md
@@ -1576,8 +1735,42 @@ function checkCommandsIntegrity(manifest, projectPath, msg) {
1576
1735
  }
1577
1736
  }
1578
1737
 
1738
+ // 🔴 The loop above measures only what the manifest happens to track. When a
1739
+ // partial apply dropped 48 of 51 entries (6.9.0, fixed in plan-executor.js),
1740
+ // this section printed "All command files intact (3 files)" while the adoption
1741
+ // summary printed "Commands: 51 installed" from a directory listing — two
1742
+ // numbers, two sources, never compared, so the gap read as a pass.
1743
+ const trackedKeys = new Set(Object.keys(commandHashes));
1744
+ const agentsToScan = new Set(Object.keys(commandHashes).map(k => k.split('/')[0]));
1745
+ for (const entry of manifest.commands?.installations || []) {
1746
+ const agentName = typeof entry === 'string' ? entry : entry?.agent;
1747
+ if (agentName) agentsToScan.add(agentName);
1748
+ }
1749
+ for (const agentName of agentsToScan) {
1750
+ // Reuse the installer's own listing so this file filter cannot drift from it.
1751
+ const info = getInstalledCommandsForAgent(agentName, 'project', projectPath);
1752
+ if (!info?.installed) continue;
1753
+ const ext = agentName === 'gemini-cli' ? '.toml' : '.md';
1754
+ for (const name of info.commands || []) {
1755
+ const key = `${agentName}/${name}${ext}`;
1756
+ if (!trackedKeys.has(key)) status.untracked.push(key);
1757
+ }
1758
+ }
1759
+ if (status.untracked.length > 0) {
1760
+ console.log(chalk.yellow(` ⚠ ${(msg.commandsUntracked || '{count} installed command file(s) are covered by no hash — integrity is not checked for them')
1761
+ .replace('{count}', status.untracked.length)}`));
1762
+ for (const key of status.untracked.slice(0, 5)) {
1763
+ console.log(chalk.yellow(` - ${key}`));
1764
+ }
1765
+ if (status.untracked.length > 5) {
1766
+ console.log(chalk.yellow(` … +${status.untracked.length - 5}`));
1767
+ }
1768
+ console.log(chalk.gray(` ${msg.commandsUntrackedFix || 'Run `uds update --commands` to record them again.'}`));
1769
+ }
1770
+
1771
+
1579
1772
  // Summary
1580
- if (status.modified.length === 0 && status.missing.length === 0) {
1773
+ if (status.modified.length === 0 && status.missing.length === 0 && status.untracked.length === 0) {
1581
1774
  console.log(chalk.green(` ✓ ${msg.allCommandsIntact || 'All command files intact'} (${status.unchanged.length} files)`));
1582
1775
  } else {
1583
1776
  console.log(chalk.gray(` ${(msg.commandsIntegritySummary || '{unchanged} unchanged, {modified} modified, {missing} missing')
@@ -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';
@@ -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,7 +665,7 @@ 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
670
  const filePath = join(projectPath, getToolFilePath(tool));
671
671
  if (existsSync(filePath)) {
@@ -854,7 +854,7 @@ export async function runProjectConfiguration(options) {
854
854
  }
855
855
 
856
856
  // Apply changes
857
- const spinner = ora(msgObj.updatingConfig).start();
857
+ const spinner = createSpinner(msgObj.updatingConfig).start();
858
858
 
859
859
  const results = {
860
860
  copied: [],
@@ -914,7 +914,7 @@ export async function runProjectConfiguration(options) {
914
914
 
915
915
  // Regenerate integration files if needed
916
916
  if (needsIntegrationRegeneration && newAITools.length > 0) {
917
- const intSpinner = ora(msgObj.regeneratingIntegrations).start();
917
+ const intSpinner = createSpinner(msgObj.regeneratingIntegrations).start();
918
918
 
919
919
  // Build installed standards list
920
920
  // Raw, not basename()d. Resolution needs the registry (an ID is not a
@@ -989,7 +989,7 @@ export async function runProjectConfiguration(options) {
989
989
 
990
990
  if (confirmSkills) {
991
991
  const newInstallations = skillCapableTools.map(agent => ({ agent, level }));
992
- const spinner = ora(t('config.installingSkillsForNewTools', 'Installing Skills for new tools...')).start();
992
+ const spinner = createSpinner(t('config.installingSkillsForNewTools', 'Installing Skills for new tools...')).start();
993
993
  const skillResult = await installSkillsToMultipleAgents(newInstallations, null, projectPath, cmdLocale);
994
994
  spinner.succeed(t('config.skillsInstalledForNewTools', 'Skills installed for new tools'));
995
995
 
@@ -1026,7 +1026,7 @@ export async function runProjectConfiguration(options) {
1026
1026
 
1027
1027
  if (confirmCmds) {
1028
1028
  const newCmdInstallations = commandCapableTools.map(agent => ({ agent, level }));
1029
- const spinner = ora(t('config.installingCommandsForNewTools', 'Installing Commands for new tools...')).start();
1029
+ const spinner = createSpinner(t('config.installingCommandsForNewTools', 'Installing Commands for new tools...')).start();
1030
1030
  const cmdResult = await installCommandsToMultipleAgents(newCmdInstallations, null, projectPath, cmdLocale);
1031
1031
  spinner.succeed(t('config.commandsInstalledForNewTools', 'Commands installed for new tools'));
1032
1032
 
@@ -1124,7 +1124,7 @@ export async function runProjectConfiguration(options) {
1124
1124
 
1125
1125
  if (options.yes) {
1126
1126
  // --yes flag: auto-apply without prompting
1127
- const applySpinner = ora(msgObj.applyingChanges).start();
1127
+ const applySpinner = createSpinner(msgObj.applyingChanges).start();
1128
1128
  const applyResults = regenerateIntegrations(projectPath, manifest);
1129
1129
  applySpinner.succeed(msgObj.changesApplied || msgObj.regeneratedIntegrations.replace('{count}', applyResults.updated.length));
1130
1130
 
@@ -1146,7 +1146,7 @@ export async function runProjectConfiguration(options) {
1146
1146
  });
1147
1147
 
1148
1148
  if (apply) {
1149
- const applySpinner = ora(msgObj.applyingChanges).start();
1149
+ const applySpinner = createSpinner(msgObj.applyingChanges).start();
1150
1150
  const applyResults = regenerateIntegrations(projectPath, manifest);
1151
1151
  applySpinner.succeed(msgObj.changesApplied || msgObj.regeneratedIntegrations.replace('{count}', applyResults.updated.length));
1152
1152
 
@@ -1172,12 +1172,12 @@ export async function runProjectConfiguration(options) {
1172
1172
  if (hasSkillInstalls || hasCommandInstalls) {
1173
1173
  const cmdLocale = displayLanguageToLocale(newOptions.display_language);
1174
1174
  if (hasSkillInstalls) {
1175
- const skillSpinner = ora(t('config.reinstallingSkills', 'Reinstalling Skills...')).start();
1175
+ const skillSpinner = createSpinner(t('config.reinstallingSkills', 'Reinstalling Skills...')).start();
1176
1176
  await installSkillsToMultipleAgents(manifest.skills.installations, null, projectPath, cmdLocale);
1177
1177
  skillSpinner.succeed(t('config.skillsReinstalled', 'Skills reinstalled'));
1178
1178
  }
1179
1179
  if (hasCommandInstalls) {
1180
- const cmdSpinner = ora(t('config.reinstallingCommands', 'Reinstalling Commands...')).start();
1180
+ const cmdSpinner = createSpinner(t('config.reinstallingCommands', 'Reinstalling Commands...')).start();
1181
1181
  await installCommandsToMultipleAgents(manifest.commands.installations, null, projectPath, cmdLocale);
1182
1182
  cmdSpinner.succeed(t('config.commandsReinstalled', 'Commands reinstalled'));
1183
1183
  }
@@ -1222,7 +1222,7 @@ async function handleSkillsConfiguration(manifest, projectPath, msgObj, common,
1222
1222
 
1223
1223
  // Install to specified level (defaults to project)
1224
1224
  const installations = [{ agent: specificTool, level }];
1225
- const spinner = ora(`Installing Skills for ${getAgentDisplayName(specificTool)} (${level} level)...`).start();
1225
+ const spinner = createSpinner(`Installing Skills for ${getAgentDisplayName(specificTool)} (${level} level)...`).start();
1226
1226
  const result = await installSkillsToMultipleAgents(installations, null, projectPath);
1227
1227
  spinner.stop();
1228
1228
 
@@ -1359,7 +1359,7 @@ async function handleSkillsConfiguration(manifest, projectPath, msgObj, common,
1359
1359
  }));
1360
1360
 
1361
1361
  // Install Skills
1362
- const spinner = ora(msgObj.installingSkills || 'Installing Skills...').start();
1362
+ const spinner = createSpinner(msgObj.installingSkills || 'Installing Skills...').start();
1363
1363
  const result = await installSkillsToMultipleAgents(installations, null, projectPath);
1364
1364
  spinner.stop();
1365
1365
 
@@ -1394,7 +1394,7 @@ async function handleSkillsConfiguration(manifest, projectPath, msgObj, common,
1394
1394
  }
1395
1395
 
1396
1396
  // Install Skills
1397
- const spinner = ora(msgObj.installingSkills || 'Installing Skills...').start();
1397
+ const spinner = createSpinner(msgObj.installingSkills || 'Installing Skills...').start();
1398
1398
  const result = await installSkillsToMultipleAgents(installations, null, projectPath);
1399
1399
  spinner.stop();
1400
1400
 
@@ -1453,7 +1453,7 @@ async function handleCommandsConfiguration(manifest, projectPath, msgObj, common
1453
1453
 
1454
1454
  // Install to selected level
1455
1455
  const installations = [{ agent: specificTool, level: commandsLevel }];
1456
- const spinner = ora(`Installing Commands for ${getAgentDisplayName(specificTool)} (${commandsLevel} level)...`).start();
1456
+ const spinner = createSpinner(`Installing Commands for ${getAgentDisplayName(specificTool)} (${commandsLevel} level)...`).start();
1457
1457
  const cmdLocale = displayLanguageToLocale(manifest.options?.display_language);
1458
1458
  const result = await installCommandsToMultipleAgents(installations, null, projectPath, cmdLocale);
1459
1459
  spinner.stop();
@@ -1559,7 +1559,7 @@ async function handleCommandsConfiguration(manifest, projectPath, msgObj, common
1559
1559
  }
1560
1560
 
1561
1561
  // Install Commands
1562
- const spinner = ora(msgObj.installingCommands || 'Installing Commands...').start();
1562
+ const spinner = createSpinner(msgObj.installingCommands || 'Installing Commands...').start();
1563
1563
  const cmdLocale = displayLanguageToLocale(manifest.options?.display_language);
1564
1564
  const result = await installCommandsToMultipleAgents(declinedCommandsWithSupport, null, projectPath, cmdLocale);
1565
1565
  spinner.stop();
@@ -1595,7 +1595,7 @@ async function handleCommandsConfiguration(manifest, projectPath, msgObj, common
1595
1595
  }
1596
1596
 
1597
1597
  // Install Commands
1598
- const spinner = ora(msgObj.installingCommands || 'Installing Commands...').start();
1598
+ const spinner = createSpinner(msgObj.installingCommands || 'Installing Commands...').start();
1599
1599
  const cmdLocale = displayLanguageToLocale(manifest.options?.display_language);
1600
1600
  const result = await installCommandsToMultipleAgents(selectedAgents, null, projectPath, cmdLocale);
1601
1601
  spinner.stop();