universal-dev-standards 6.7.5 → 6.9.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 (213) hide show
  1. package/bin/uds.js +19 -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/ai-instruction-standards.ai.yaml +6 -6
  5. package/bundled/ai/standards/code-review.ai.yaml +13 -3
  6. package/bundled/ai/standards/commit-message.ai.yaml +8 -4
  7. package/bundled/ai/standards/deferred-item-exit.ai.yaml +225 -0
  8. package/bundled/ai/standards/feature-discovery-standards.ai.yaml +14 -2
  9. package/bundled/ai/standards/governance-layer.ai.yaml +128 -2
  10. package/bundled/ai/standards/logging.ai.yaml +2 -2
  11. package/bundled/ai/standards/retrospective-standards.ai.yaml +14 -2
  12. package/bundled/ai/standards/reverse-engineering-standards.ai.yaml +73 -2
  13. package/bundled/ai/standards/security-standards.ai.yaml +2 -2
  14. package/bundled/ai/standards/spec-driven-development.ai.yaml +14 -2
  15. package/bundled/ai/standards/tech-debt-standards.ai.yaml +87 -3
  16. package/bundled/ai/standards/turn-completion-integrity.ai.yaml +131 -0
  17. package/bundled/core/acceptance-criteria-traceability.md +5 -2
  18. package/bundled/core/adr-standards.md +26 -2
  19. package/bundled/core/ai-instruction-standards.md +9 -7
  20. package/bundled/core/code-review-checklist.md +5 -2
  21. package/bundled/core/context-aware-loading.md +1 -1
  22. package/bundled/core/deferred-item-exit.md +254 -0
  23. package/bundled/core/feature-discovery-standards.md +5 -1
  24. package/bundled/core/governance-layer.md +114 -2
  25. package/bundled/core/retrospective-standards.md +4 -2
  26. package/bundled/core/reverse-engineering-standards.md +81 -2
  27. package/bundled/core/spec-driven-development.md +8 -2
  28. package/bundled/core/tech-debt-standards.md +67 -8
  29. package/bundled/core/turn-completion-integrity.md +196 -0
  30. package/bundled/hooks/check-dangerous-cmd.mjs +60 -0
  31. package/bundled/hooks/check-logging-standard.mjs +59 -0
  32. package/bundled/hooks/check-turn-completion.mjs +233 -0
  33. package/bundled/hooks/inject-standards.mjs +183 -0
  34. package/bundled/hooks/telemetry-wrapper.mjs +77 -0
  35. package/bundled/hooks/turn-completion/detect.mjs +99 -0
  36. package/bundled/hooks/turn-completion/locales/en.mjs +159 -0
  37. package/bundled/hooks/turn-completion/locales/zh-TW.mjs +166 -0
  38. package/bundled/hooks/validate-commit-msg.mjs +104 -0
  39. package/bundled/locales/zh-CN/CHANGELOG.md +65 -3
  40. package/bundled/locales/zh-CN/CLAUDE.md +1 -1
  41. package/bundled/locales/zh-CN/README.md +7 -7
  42. package/bundled/locales/zh-CN/SECURITY.md +1 -1
  43. package/bundled/locales/zh-CN/core/adr-standards.md +1 -1
  44. package/bundled/locales/zh-CN/core/ai-instruction-standards.md +10 -8
  45. package/bundled/locales/zh-CN/core/governance-layer.md +118 -6
  46. package/bundled/locales/zh-CN/core/retrospective-standards.md +1 -1
  47. package/bundled/locales/zh-CN/core/tech-debt-standards.md +71 -4
  48. package/bundled/locales/zh-CN/core/turn-completion-integrity.md +190 -0
  49. package/bundled/locales/zh-CN/docs/CHEATSHEET.md +27 -6
  50. package/bundled/locales/zh-CN/docs/CLI-INIT-OPTIONS.md +29 -68
  51. package/bundled/locales/zh-CN/docs/FEATURE-REFERENCE.md +172 -24
  52. package/bundled/locales/zh-CN/docs/USAGE-MODES-COMPARISON.md +1 -2
  53. package/bundled/locales/zh-CN/integrations/google-antigravity/{INSTRUCTIONS.md → AGENTS.md} +1 -1
  54. package/bundled/locales/zh-CN/integrations/google-antigravity/README.md +3 -3
  55. package/bundled/locales/zh-CN/skills/agents/README.md +1 -1
  56. package/bundled/locales/zh-CN/skills/atdd-assistant/SKILL.md +2 -0
  57. package/bundled/locales/zh-CN/skills/bdd-assistant/SKILL.md +2 -0
  58. package/bundled/locales/zh-CN/skills/brainstorm-assistant/SKILL.md +22 -12
  59. package/bundled/locales/zh-CN/skills/brainstorm-assistant/guide.md +12 -9
  60. package/bundled/locales/zh-CN/skills/code-review-assistant/SKILL.md +1 -0
  61. package/bundled/locales/zh-CN/skills/commands/brainstorm.md +17 -13
  62. package/bundled/locales/zh-CN/skills/commands/config.md +0 -1
  63. package/bundled/locales/zh-CN/skills/commands/init.md +1 -2
  64. package/bundled/locales/zh-CN/skills/commit-standards/SKILL.md +2 -0
  65. package/bundled/locales/zh-CN/skills/contract-test-assistant/SKILL.md +1 -0
  66. package/bundled/locales/zh-CN/skills/dev-methodology/SKILL.md +2 -0
  67. package/bundled/locales/zh-CN/skills/observability-assistant/SKILL.md +1 -0
  68. package/bundled/locales/zh-CN/skills/project-structure-guide/SKILL.md +1 -0
  69. package/bundled/locales/zh-CN/skills/release-standards/SKILL.md +3 -0
  70. package/bundled/locales/zh-CN/skills/requirement-assistant/SKILL.md +2 -0
  71. package/bundled/locales/zh-CN/skills/reverse-engineer/SKILL.md +3 -0
  72. package/bundled/locales/zh-CN/skills/reverse-engineer/tdd-analysis.md +13 -23
  73. package/bundled/locales/zh-CN/skills/runbook-assistant/SKILL.md +1 -0
  74. package/bundled/locales/zh-CN/skills/slo-assistant/SKILL.md +1 -0
  75. package/bundled/locales/zh-CN/skills/tdd-assistant/SKILL.md +2 -0
  76. package/bundled/locales/zh-CN/skills/workflows/README.md +2 -11
  77. package/bundled/locales/zh-TW/CHANGELOG.md +65 -3
  78. package/bundled/locales/zh-TW/CLAUDE.md +1 -1
  79. package/bundled/locales/zh-TW/README.md +7 -7
  80. package/bundled/locales/zh-TW/SECURITY.md +1 -1
  81. package/bundled/locales/zh-TW/core/acceptance-criteria-traceability.md +2 -0
  82. package/bundled/locales/zh-TW/core/adr-standards.md +26 -5
  83. package/bundled/locales/zh-TW/core/ai-instruction-standards.md +10 -8
  84. package/bundled/locales/zh-TW/core/code-review-checklist.md +2 -0
  85. package/bundled/locales/zh-TW/core/container-image-standards.md +2 -2
  86. package/bundled/locales/zh-TW/core/contract-testing-standards.md +2 -2
  87. package/bundled/locales/zh-TW/core/cross-flow-regression.md +8 -7
  88. package/bundled/locales/zh-TW/core/data-contract.md +2 -2
  89. package/bundled/locales/zh-TW/core/data-migration-testing.md +2 -2
  90. package/bundled/locales/zh-TW/core/data-pipeline.md +2 -2
  91. package/bundled/locales/zh-TW/core/deferred-item-exit.md +251 -0
  92. package/bundled/locales/zh-TW/core/documentation-writing-standards.md +228 -3
  93. package/bundled/locales/zh-TW/core/full-coverage-testing.md +15 -2
  94. package/bundled/locales/zh-TW/core/governance-layer.md +118 -5
  95. package/bundled/locales/zh-TW/core/iac-design-principles.md +2 -2
  96. package/bundled/locales/zh-TW/core/incident-response.md +2 -2
  97. package/bundled/locales/zh-TW/core/model-provenance.md +4 -2
  98. package/bundled/locales/zh-TW/core/pii-classification.md +42 -6
  99. package/bundled/locales/zh-TW/core/prd-standards.md +4 -2
  100. package/bundled/locales/zh-TW/core/product-metrics-standards.md +4 -2
  101. package/bundled/locales/zh-TW/core/release-readiness-gate.md +2 -2
  102. package/bundled/locales/zh-TW/core/resource-cost-boundary.md +2 -2
  103. package/bundled/locales/zh-TW/core/retrospective-standards.md +5 -3
  104. package/bundled/locales/zh-TW/core/reverse-engineering-standards.md +83 -5
  105. package/bundled/locales/zh-TW/core/runbook.md +2 -2
  106. package/bundled/locales/zh-TW/core/schema-evolution.md +2 -2
  107. package/bundled/locales/zh-TW/core/secret-management-standards.md +2 -2
  108. package/bundled/locales/zh-TW/core/slo-sli.md +2 -2
  109. package/bundled/locales/zh-TW/core/spec-driven-development.md +2 -0
  110. package/bundled/locales/zh-TW/core/tech-debt-standards.md +71 -4
  111. package/bundled/locales/zh-TW/core/turn-completion-integrity.md +190 -0
  112. package/bundled/locales/zh-TW/core/user-journey-testing.md +2 -2
  113. package/bundled/locales/zh-TW/core/user-story-mapping.md +2 -2
  114. package/bundled/locales/zh-TW/core/verification-oracle.md +2 -2
  115. package/bundled/locales/zh-TW/docs/CHEATSHEET.md +27 -6
  116. package/bundled/locales/zh-TW/docs/CLI-INIT-OPTIONS.md +29 -68
  117. package/bundled/locales/zh-TW/docs/FEATURE-REFERENCE.md +172 -24
  118. package/bundled/locales/zh-TW/docs/USAGE-MODES-COMPARISON.md +1 -2
  119. package/bundled/locales/zh-TW/integrations/google-antigravity/{INSTRUCTIONS.md → AGENTS.md} +1 -1
  120. package/bundled/locales/zh-TW/integrations/google-antigravity/README.md +3 -3
  121. package/bundled/locales/zh-TW/skills/adr-assistant/SKILL.md +1 -1
  122. package/bundled/locales/zh-TW/skills/agents/README.md +1 -1
  123. package/bundled/locales/zh-TW/skills/atdd-assistant/SKILL.md +2 -0
  124. package/bundled/locales/zh-TW/skills/bdd-assistant/SKILL.md +2 -0
  125. package/bundled/locales/zh-TW/skills/brainstorm-assistant/SKILL.md +22 -12
  126. package/bundled/locales/zh-TW/skills/brainstorm-assistant/guide.md +12 -9
  127. package/bundled/locales/zh-TW/skills/code-review-assistant/SKILL.md +1 -0
  128. package/bundled/locales/zh-TW/skills/commands/brainstorm.md +17 -13
  129. package/bundled/locales/zh-TW/skills/commands/config.md +0 -1
  130. package/bundled/locales/zh-TW/skills/commands/init.md +1 -2
  131. package/bundled/locales/zh-TW/skills/commit-standards/SKILL.md +2 -0
  132. package/bundled/locales/zh-TW/skills/contract-test-assistant/SKILL.md +2 -1
  133. package/bundled/locales/zh-TW/skills/dev-methodology/SKILL.md +2 -0
  134. package/bundled/locales/zh-TW/skills/dev-workflow-guide/SKILL.md +1 -1
  135. package/bundled/locales/zh-TW/skills/knowledge-graph/guide.md +2 -2
  136. package/bundled/locales/zh-TW/skills/migration-assistant/SKILL.md +1 -1
  137. package/bundled/locales/zh-TW/skills/observability-assistant/SKILL.md +1 -0
  138. package/bundled/locales/zh-TW/skills/project-discovery/SKILL.md +1 -0
  139. package/bundled/locales/zh-TW/skills/project-structure-guide/SKILL.md +1 -0
  140. package/bundled/locales/zh-TW/skills/release-standards/SKILL.md +3 -0
  141. package/bundled/locales/zh-TW/skills/requirement-assistant/SKILL.md +2 -0
  142. package/bundled/locales/zh-TW/skills/reverse-engineer/SKILL.md +3 -0
  143. package/bundled/locales/zh-TW/skills/reverse-engineer/tdd-analysis.md +13 -23
  144. package/bundled/locales/zh-TW/skills/runbook-assistant/SKILL.md +1 -0
  145. package/bundled/locales/zh-TW/skills/slo-assistant/SKILL.md +1 -0
  146. package/bundled/locales/zh-TW/skills/tdd-assistant/SKILL.md +2 -0
  147. package/bundled/locales/zh-TW/skills/workflows/README.md +2 -11
  148. package/bundled/skills/agents/README.md +1 -1
  149. package/bundled/skills/atdd-assistant/SKILL.md +2 -0
  150. package/bundled/skills/bdd-assistant/SKILL.md +2 -0
  151. package/bundled/skills/brainstorm-assistant/SKILL.md +31 -13
  152. package/bundled/skills/brainstorm-assistant/guide.md +9 -6
  153. package/bundled/skills/code-review-assistant/SKILL.md +1 -0
  154. package/bundled/skills/commands/brainstorm.md +12 -9
  155. package/bundled/skills/commands/config.md +0 -1
  156. package/bundled/skills/commands/init.md +2 -3
  157. package/bundled/skills/commit-standards/SKILL.md +2 -0
  158. package/bundled/skills/contract-test-assistant/SKILL.md +1 -0
  159. package/bundled/skills/dev-methodology/SKILL.md +4 -0
  160. package/bundled/skills/observability-assistant/SKILL.md +1 -0
  161. package/bundled/skills/project-discovery/SKILL.md +1 -0
  162. package/bundled/skills/project-structure-guide/SKILL.md +1 -0
  163. package/bundled/skills/release-standards/SKILL.md +3 -0
  164. package/bundled/skills/requirement-assistant/SKILL.md +2 -0
  165. package/bundled/skills/reverse-engineer/SKILL.md +3 -0
  166. package/bundled/skills/reverse-engineer/tdd-analysis.md +16 -23
  167. package/bundled/skills/runbook-assistant/SKILL.md +1 -0
  168. package/bundled/skills/slo-assistant/SKILL.md +1 -0
  169. package/bundled/skills/tdd-assistant/SKILL.md +2 -0
  170. package/bundled/skills/workflows/README.md +2 -11
  171. package/bundled/templates/.ai-context.yaml.template +194 -0
  172. package/bundled/templates/CLAUDE.md.template +145 -0
  173. package/bundled/templates/DESIGN.md +237 -0
  174. package/bundled/templates/SKILL-BRIEF-TEMPLATE.md +57 -0
  175. package/bundled/templates/SKILL-CANDIDATES.md +39 -0
  176. package/bundled/templates/gates/check-error-exit.mjs +309 -0
  177. package/bundled/templates/mcp-config.json +10 -0
  178. package/bundled/templates/methodology-template.yaml +209 -0
  179. package/bundled/templates/migration-template.md +408 -0
  180. package/bundled/templates/requirement-checklist.md +410 -0
  181. package/bundled/templates/requirement-document-template.md +591 -0
  182. package/bundled/templates/requirement-template.md +881 -0
  183. package/bundled/templates/reverse-spec-template.md +409 -0
  184. package/bundled/templates/test-case-template.md +74 -0
  185. package/bundled/templates/test-plan-template.md +74 -0
  186. package/package.json +9 -5
  187. package/src/commands/audit.js +82 -0
  188. package/src/commands/check.js +66 -10
  189. package/src/commands/init.js +161 -16
  190. package/src/commands/lint.js +96 -0
  191. package/src/commands/quickstart.js +16 -13
  192. package/src/commands/update.js +286 -14
  193. package/src/compilers/claude-code-compiler.js +4 -1
  194. package/src/config/ai-agent-paths.js +62 -17
  195. package/src/core/constants.js +42 -11
  196. package/src/core/manifest.js +201 -3
  197. package/src/core/paths.js +2 -2
  198. package/src/i18n/messages.js +9 -32
  199. package/src/installers/hooks-installer.js +167 -75
  200. package/src/installers/integration-installer.js +9 -5
  201. package/src/prompts/init.js +14 -14
  202. package/src/reconciler/actual-state-scanner.js +14 -3
  203. package/src/utils/detector.js +21 -1
  204. package/src/utils/effect-boundary.js +1093 -0
  205. package/src/utils/hasher.js +229 -9
  206. package/src/utils/hook-stats.js +1 -1
  207. package/src/utils/integration-generator.js +100 -4
  208. package/src/utils/reference-sync.js +4 -1
  209. package/src/utils/skills-installer.js +17 -3
  210. package/src/utils/spec-linter.js +35 -76
  211. package/src/utils/yaml-generator.js +51 -9
  212. package/standards-registry.json +31 -8
  213. package/src/commands/sync.js +0 -133
@@ -29,7 +29,8 @@ import {
29
29
  parseReferences,
30
30
  compareStandardsWithReferences
31
31
  } from '../utils/reference-sync.js';
32
- import { extractMarkedContent, getToolFilePath, parseStandardsIndexCount } from '../utils/integration-generator.js';
32
+ import { extractMarkedContent, getToolFilePath, parseStandardsIndexCount, writeIntegrationFile } from '../utils/integration-generator.js';
33
+ import { INTEGRATION_MAPPINGS } from '../installers/integration-installer.js';
33
34
  import { getToolFormat } from '../core/constants.js';
34
35
  import { checkForUpdates } from '../utils/npm-registry.js';
35
36
  import { writeUpdateCache } from '../utils/update-checker.js';
@@ -415,6 +416,9 @@ export async function checkCommand(options = {}) {
415
416
  // XSPEC-178: Full coverage compliance check
416
417
  checkFullCoverageCompliance(manifest, projectPath);
417
418
 
419
+ // 錯誤訊息單一出口閘門是否已安裝(只報告,不寫入)
420
+ checkErrorExitGate(projectPath);
421
+
418
422
  // Workflow status
419
423
  displayWorkflowStatus(projectPath);
420
424
 
@@ -664,6 +668,27 @@ export async function restoreSingleFile(projectPath, manifest, relativePath, msg
664
668
  msg = t().commands.check;
665
669
  }
666
670
 
671
+ // 🔴 Integration files are GENERATED, not copied. Restoring one by downloading a
672
+ // repo template overwrites the adopter's file with a document they never had —
673
+ // `integrations/claude-code/CLAUDE.md` is 140 lines last touched 2026-03-25, and no
674
+ // code path writes it at install time. The installer already records how each file
675
+ // was generated (`manifest.integrationConfigs`), so a correct restore regenerates
676
+ // from that, exactly as `uds update --sync-refs` does.
677
+ const genConfig = manifest.integrationConfigs?.[relativePath];
678
+ if (genConfig?.tool) {
679
+ const result = writeIntegrationFile(genConfig.tool, {
680
+ ...genConfig,
681
+ installedStandards: genConfig.installedStandards || manifest.standards || []
682
+ }, projectPath);
683
+ if (result.success) {
684
+ updateFileHash(projectPath, manifest, relativePath);
685
+ console.log(chalk.green(` ✓ ${relativePath}: ${msg.restored}`));
686
+ return true;
687
+ }
688
+ console.log(chalk.red(` ✗ ${relativePath}: ${result.error}`));
689
+ return false;
690
+ }
691
+
667
692
  const sourcePath = getSourcePathFromRelative(manifest, relativePath);
668
693
  if (!sourcePath) {
669
694
  console.log(chalk.red(` ✗ ${relativePath}: ${msg.couldNotDetermineSource}`));
@@ -775,15 +800,18 @@ export function getSourcePathFromRelative(manifest, relativePath) {
775
800
 
776
801
  // Check integrations - these might need special handling
777
802
  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;
803
+ // 🔴 This was a hand-copied list of five targets. The installer's own
804
+ // INTEGRATION_MAPPINGS covers eight and is the map actually used when a
805
+ // generation falls back to a static copy, so a second copy here could only
806
+ // drift — and had: it named `CLAUDE.md`, which INTEGRATION_MAPPINGS does not
807
+ // contain because no code path ever writes that template.
808
+ //
809
+ // Reached only for legacy manifests with no `integrationConfigs`; a current
810
+ // install regenerates instead (see restoreSingleFile).
811
+ for (const { source, target } of Object.values(INTEGRATION_MAPPINGS)) {
812
+ if (target === relativePath) return source;
813
+ }
814
+ return null;
787
815
  }
788
816
 
789
817
  return null;
@@ -1003,6 +1031,34 @@ function displaySkillsStatus(manifest, projectPath, msg) {
1003
1031
  return { missingSkills, missingCommands };
1004
1032
  }
1005
1033
 
1034
+ /**
1035
+ * 錯誤訊息單一出口閘門:**偵測並報告,絕不寫入。**
1036
+ *
1037
+ * 🔴 為什麼只報告:`uds update` 之外沒有任何時刻是採用者同意我們動他的 repo 的。
1038
+ * 未經同意往別人的專案寫可執行腳本,比它要修的那個問題更糟。
1039
+ * 所以這裡只說「你沒有這道閘門」與「怎麼拿」,寫入留給 `uds update`,
1040
+ * 而那裡會顯示內容並要人點頭。
1041
+ *
1042
+ * 起因(VibeOps 2026-09-03):使用者照著建議打上名字,畫面回他一句 `Bad Request`。
1043
+ * 真正的原因寫在回應的 `details` 裡,而呼叫端只讀了 `error`。當天的修法改了一個
1044
+ * 呼叫端;走訪之後發現同型的有二十四個——其中三個在同一個檔案裡。
1045
+ */
1046
+ function checkErrorExitGate(projectPath) {
1047
+ const gatePath = join(projectPath, 'scripts', 'check-error-exit.mjs');
1048
+ if (existsSync(gatePath)) {
1049
+ return; // 裝了就不出聲。這裡的沉默代表「有」,不代表「跑過了」——那是它自己的事。
1050
+ }
1051
+ // 沒有 src/ 的專案(純文件、純設定)本來就沒有這個問題,不要對他們嘮叨。
1052
+ if (!existsSync(join(projectPath, 'src'))) {
1053
+ return;
1054
+ }
1055
+ console.log(chalk.yellow(' ⚠ [error-exit] 沒有錯誤訊息單一出口檢查(scripts/check-error-exit.mjs)。'));
1056
+ console.log(chalk.gray(' 這道閘門防的是「每個呼叫端各自把錯誤回應拼成給人看的字串」——'));
1057
+ console.log(chalk.gray(' 第一處是實作,第二處開始就會各寫各的,而畫面上只剩一句 Bad Request。'));
1058
+ console.log(chalk.gray(' 要裝的話:`uds update` 會顯示內容並徵求同意後寫入。'));
1059
+ console.log();
1060
+ }
1061
+
1006
1062
  /**
1007
1063
  * XSPEC-178: Check full-coverage-testing standard presence and STUB markers
1008
1064
  */
@@ -4,7 +4,9 @@ import { execSync } from 'child_process';
4
4
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
5
5
  import { basename, join } from 'path';
6
6
  import {
7
- manifestExists as isInitialized
7
+ manifestExists as isInitialized,
8
+ normalizeContentMode,
9
+ SUPPORTED_CONTENT_MODES
8
10
  } from '../core/manifest.js';
9
11
  import { t, detectLanguage } from '../i18n/messages.js';
10
12
  import { detectAll } from '../utils/detector.js';
@@ -156,9 +158,29 @@ export async function initCommand(options) {
156
158
  skillHashes: {},
157
159
  commandHashes: {}
158
160
  };
161
+ // A detected tool that can take skills and has no verified path gets
162
+ // nothing — and must be told so. Silence was the actual defect measured
163
+ // on 2026-09-08: `--mode skills` installed nothing, printed no skills
164
+ // line, and exited 0. An adopter cannot know to go looking.
165
+ for (const tool of config.skillsConfig.skillsPathUnknownTools || []) {
166
+ console.log(chalk.yellow(
167
+ `⚠ ${getAgentDisplayName(tool) || tool}: ` +
168
+ `${msg.skillsPathUnknown || 'no verified skills path — nothing was installed for this tool'}`
169
+ ));
170
+ }
159
171
  await installSkills(config.skillsConfig, projectPath, msg, skillsResults);
160
172
  await installCommands(config.skillsConfig, projectPath, msg, skillsResults);
161
173
 
174
+ // A tool may need something from the adopter before it will read what
175
+ // we just wrote. Driven by an `installNote` on the path table, so a new
176
+ // precondition is a field and not a branch here.
177
+ for (const { agent } of config.skillsConfig.skillsInstallations || []) {
178
+ const note = getAgentConfig(agent)?.installNote;
179
+ if (note) {
180
+ console.log(chalk.yellow(` ⚠ ${getAgentDisplayName(agent) || agent}: ${note}`));
181
+ }
182
+ }
183
+
162
184
  // Combine results
163
185
  installErrors = [
164
186
  ...standardsResults.errors,
@@ -225,7 +247,25 @@ export async function initCommand(options) {
225
247
  const { installHooks } = await import('../installers/hooks-installer.js');
226
248
  const hookResult = installHooks(projectPath);
227
249
  if (hookResult.installed) {
228
- console.log(chalk.green(` ✓ Enforcement hooks installed (${hookResult.scriptsCount} scripts)`));
250
+ console.log(chalk.green(
251
+ ` ✓ Enforcement hooks installed (${hookResult.scriptsCount} scripts, `
252
+ + `${hookResult.events.join(', ')})`));
253
+ } else {
254
+ // Silence here is how three broken installs reported success.
255
+ console.log(chalk.yellow(' ⚠ No enforcement hooks installed — no standard produced a usable hook.'));
256
+ }
257
+ for (const s of hookResult.skipped ?? []) {
258
+ console.log(chalk.yellow(` · skipped ${s.id}: ${s.why}`));
259
+ }
260
+ // turn-completion-integrity R8: a prose-reading hook in a language it does
261
+ // not ship is installed, running, and unable to fire — and that looks
262
+ // exactly like a turn with nothing wrong. Say so at install time; there is
263
+ // no later moment where the adopter could find out.
264
+ for (const l of hookResult.languageLimits ?? []) {
265
+ console.log(chalk.yellow(` · ${l.script} reads prose, and ships:`));
266
+ for (const line of l.languages.split('\n')) {
267
+ console.log(chalk.gray(` ${line}`));
268
+ }
229
269
  }
230
270
  }
231
271
 
@@ -433,16 +473,95 @@ function buildNonInteractiveConfig(options, detected, projectPath) {
433
473
  });
434
474
 
435
475
  // Skills Configuration Logic
436
- const hasSkillsCompatibleTool = aiToolsNormalized.some(t => t === 'claude-code' || t === 'opencode');
437
- const onlySkillsCompatibleTools = aiToolsNormalized.every(t => t === 'claude-code' || t === 'opencode');
438
-
476
+ //
477
+ // 🔴 Which tools can take skills is asked of the path table, never listed here.
478
+ // This used to read `t === 'claude-code' || t === 'opencode'`, and the two
479
+ // consequences were both measured on 2026-09-08 (XSPEC-408 §18.3):
480
+ //
481
+ // - A repo detected as codex ran `uds init --mode skills -y` and got **zero
482
+ // skills, in any directory, with no message saying so.**
483
+ // - When a location was passed explicitly, the install went to `.claude/skills/`
484
+ // for every tool, because the branch below never built `skillsInstallations`
485
+ // and `installSkills()` then fell through to the legacy path, whose target is
486
+ // the hardcoded `getProjectSkillsDir()`. Codex does not read that directory:
487
+ // two arms differing only in path gave 15,235 vs 18,096 input tokens, and only
488
+ // the `.agents/skills` arm made Codex say it could see the skills at all.
489
+ //
490
+ // 🔴 This is the second time in the same shape. `ai-agent-paths.js` records the
491
+ // first: `.codex/skills/` was "a directory UDS invented", skills there were
492
+ // invisible, and a behavioural probe failed while Codex behaved correctly. That
493
+ // fix corrected the path table. This one broke in the code that never asked it.
494
+ // The predicate below is the same one `promptSkillsInstallLocation` already used —
495
+ // the interactive flow was never wrong.
496
+ const skillsCapableTools = aiToolsNormalized.filter(tool => {
497
+ const config = getAgentConfig(tool);
498
+ return Boolean(config?.supportsSkills && config?.skills);
499
+ });
500
+ const hasSkillsCompatibleTool = skillsCapableTools.length > 0;
501
+ // Marketplace is Claude Code's. A tool reaches it either by having one
502
+ // (`supportsMarketplace`) or by reading Claude's directory (`fallbackSkillsPath`).
503
+ // ⚠️ Those fallback claims are NOT all verified — `opencode`'s carries the bare
504
+ // comment "Can read Claude skills" and no evidence. It is preserved here rather
505
+ // than flipped, because changing behaviour on an unverified field in either
506
+ // direction is a guess. Codex's identical claim WAS measured, and falsified, so
507
+ // its field is now null (see `ai-agent-paths.js`) and it drops out here by data.
508
+ // 🔴 The marketplace is all-or-nothing for a repo: it installs into Claude's
509
+ // directory, so it serves a tool only if that tool has a marketplace of its own or
510
+ // declares Claude's directory as its fallback. If ANY detected tool is not served
511
+ // by it, file installs are used instead — every tool then gets its own path, and
512
+ // nobody is left with a successful init and an empty directory.
513
+ const reachesMarketplace = t => {
514
+ const c = getAgentConfig(t);
515
+ return Boolean(c?.supportsMarketplace || c?.fallbackSkillsPath);
516
+ };
517
+ const onlySkillsCompatibleTools =
518
+ hasSkillsCompatibleTool && aiToolsNormalized.every(reachesMarketplace);
519
+
439
520
  let skillsLocationFlag = options.skillsLocation;
440
521
  if (!skillsLocationFlag) {
441
- skillsLocationFlag = (hasSkillsCompatibleTool && onlySkillsCompatibleTools) ? 'marketplace' : 'none';
522
+ skillsLocationFlag = hasSkillsCompatibleTool
523
+ ? (onlySkillsCompatibleTools ? 'marketplace' : 'project')
524
+ : 'none';
442
525
  }
443
526
 
444
- const contentModeFlag = options.contentMode || 'auto';
527
+ // A detected tool that can take skills and is getting none must SAY SO. The worst
528
+ // property of the old default was not the wrong directory, it was the silence:
529
+ // `--mode skills` printed no skills line at all and exited 0.
530
+ const skippedSkillsTools = aiToolsNormalized.filter(tool => {
531
+ const config = getAgentConfig(tool);
532
+ return Boolean(config?.supportsSkills) && !config?.skills;
533
+ });
534
+ // Carried on the config and printed by the caller — this function builds config
535
+ // and has no message bundle in scope. (First version called `msg` here and every
536
+ // init that detected a pathless tool crashed with `ReferenceError: msg is not
537
+ // defined`; the gate's own control arm missed it, because the string it looked for
538
+ // appeared in the stack trace.)
539
+
540
+ // `auto` is resolved later per tool by resolveContentModeForTool, so it is
541
+ // not a content mode and must not go through the normalizer.
542
+ let contentModeFlag = options.contentMode || 'auto';
543
+ if (contentModeFlag !== 'auto') {
544
+ // An unrecognised value is never substituted quietly: a typo that still
545
+ // produces a normal-looking run is how `--content-mode` would lie about
546
+ // what it generated. (XSPEC-357 R7) In 6.x it warns and keeps 6.8.0's
547
+ // result (`index`); it becomes an error in 7.0.0 — see normalizeContentMode.
548
+ const normalized = normalizeContentMode(contentModeFlag);
549
+ if (normalized.unrecognized) {
550
+ console.log(chalk.yellow(
551
+ `⚠ Unknown content mode '${normalized.unrecognized}'; using '${normalized.mode}'. ` +
552
+ `Supported: ${SUPPORTED_CONTENT_MODES.join(', ')}. This will be an error in 7.0.0.`
553
+ ));
554
+ }
555
+ if (normalized.migratedFrom) {
556
+ console.log(chalk.yellow(
557
+ `⚠ --content-mode ${normalized.migratedFrom} is retired; using '${normalized.mode}'. ` +
558
+ 'The two produced identical files, so nothing about your output changes.'
559
+ ));
560
+ }
561
+ contentModeFlag = normalized.mode;
562
+ }
445
563
  let skillsConfig = {};
564
+ let skillsInstallationsForYes = [];
446
565
 
447
566
  if (skillsLocationFlag === 'marketplace') {
448
567
  skillsConfig = {
@@ -472,14 +591,39 @@ function buildNonInteractiveConfig(options, detected, projectPath) {
472
591
  location = 'project';
473
592
  }
474
593
 
475
- skillsConfig = {
476
- installed: true,
477
- location,
478
- needsInstall: skillsLocationFlag === 'project' || skillsLocationFlag === 'user' || (!userSkillsInfo?.installed && !projectSkillsInfo?.installed),
479
- updateTargets: [location],
480
- standardsScope: 'minimal',
481
- contentMode: contentModeFlag
482
- };
594
+ // 🔴 One installation per skills-capable detected tool, each at that tool's own
595
+ // path from the table. Without this the config carries only `updateTargets`,
596
+ // `installSkills()` takes its legacy branch, and every tool's skills land in
597
+ // `.claude/skills/` — see the block above for what that measured.
598
+ skillsInstallationsForYes = skillsCapableTools.map(agent => ({ agent, level: location }));
599
+
600
+ // 🔴 Detected tools, none of which can take skills, is NOT the same as "no tools
601
+ // detected". The first is knowledge — install nothing and say why. The second is
602
+ // ignorance, and there the historical `.claude/skills/` fallback is the least-bad
603
+ // guess and is kept. Without this split, `--skills-location project` in an
604
+ // Antigravity-only repo wrote 55 skills into `.claude/skills/`, a directory
605
+ // nothing in that repo reads. Caught by `check:install-paths`, not by me.
606
+ const knowinglyUnsupported = skillsCapableTools.length === 0 && aiToolsNormalized.length > 0;
607
+
608
+ skillsConfig = knowinglyUnsupported
609
+ ? {
610
+ installed: false,
611
+ location: null,
612
+ needsInstall: false,
613
+ updateTargets: [],
614
+ skillsInstallations: [],
615
+ standardsScope: 'full',
616
+ contentMode: contentModeFlag
617
+ }
618
+ : {
619
+ installed: true,
620
+ location,
621
+ skillsInstallations: skillsInstallationsForYes,
622
+ needsInstall: skillsLocationFlag === 'project' || skillsLocationFlag === 'user' || (!userSkillsInfo?.installed && !projectSkillsInfo?.installed),
623
+ updateTargets: [location],
624
+ standardsScope: 'minimal',
625
+ contentMode: contentModeFlag
626
+ };
483
627
  }
484
628
 
485
629
  // Auto-install commands
@@ -496,6 +640,7 @@ function buildNonInteractiveConfig(options, detected, projectPath) {
496
640
  }
497
641
 
498
642
  skillsConfig.locale = displayLanguageToLocale(displayLanguage);
643
+ skillsConfig.skillsPathUnknownTools = skippedSkillsTools;
499
644
 
500
645
  // AGENTS.md: default to true in --yes mode unless codex/opencode selected or --no-agents-md
501
646
  // When codex/opencode is selected, they already generate AGENTS.md — no need for universal output
@@ -639,7 +784,7 @@ function displaySummary(config, msg, common) {
639
784
  }
640
785
 
641
786
  // 9. Content Mode (STEP 11)
642
- const contentModeLabels = t().contentMode?.labels || { index: 'Standard', full: 'Full', minimal: 'Minimal' };
787
+ const contentModeLabels = t().contentMode?.labels || { index: 'Standard', minimal: 'Minimal' };
643
788
  console.log(chalk.gray(` ${msg.contentModeLabel}: ${contentModeLabels[config.contentMode] || config.contentMode}`));
644
789
 
645
790
  // 10. Methodology (STEP 12, experimental)
@@ -0,0 +1,96 @@
1
+ /**
2
+ * `uds lint` — dependency validity + size checks against installed specs
3
+ * (specs/*.md).
4
+ *
5
+ * XSPEC-383 R5 (Option E). `cli/src/utils/spec-linter.js` and this command's
6
+ * JSON shape existed since 2026-04-07, but no `uds lint` command was ever
7
+ * registered — VibeOps's `lint-executor.ts` has been calling
8
+ * `npx uds lint --json` since the same day and getting `command not found`
9
+ * every time, four and a half months, without anyone noticing (see
10
+ * `cli/scripts/check-module-reachability.mjs` for the full incident).
11
+ *
12
+ * This registers the command with exactly the two checks that survived
13
+ * `lintAll()`'s AC-coverage removal (see spec-linter.js for why AC coverage
14
+ * was dropped rather than patched). The `--json` shape is deliberately NOT a
15
+ * new design — it matches the shape VibeOps's `lint-executor.ts` has already
16
+ * been parsing (`result.summary.fail`, `result.results[].specId/.status/.message`)
17
+ * since it was written, so wiring this up does not also require a change on
18
+ * the VibeOps side.
19
+ *
20
+ * @module commands/lint
21
+ */
22
+
23
+ import chalk from 'chalk';
24
+ import { lintAll } from '../utils/spec-linter.js';
25
+
26
+ /**
27
+ * Render a one-line human message for a single spec's lint result.
28
+ * Exported for tests; also used to build the `message` field of `--json`
29
+ * output.
30
+ */
31
+ export function buildMessage(result) {
32
+ const parts = [];
33
+ if (result.deps.broken.length > 0) {
34
+ const targets = result.deps.broken.map((b) => b.target).join(', ');
35
+ parts.push(
36
+ `${result.deps.broken.length} broken dependenc${result.deps.broken.length === 1 ? 'y' : 'ies'}: ${targets}`
37
+ );
38
+ }
39
+ parts.push(`${result.size.effectiveLines} effective lines (${result.size.status})`);
40
+ return parts.join('; ');
41
+ }
42
+
43
+ export async function lintCommand(options = {}) {
44
+ const projectPath = process.cwd();
45
+ const result = lintAll(projectPath);
46
+
47
+ if (options.json) {
48
+ const payload = {
49
+ summary: result.summary,
50
+ results: result.results.map((r) => ({
51
+ specId: r.spec,
52
+ status: r.status,
53
+ message: buildMessage(r),
54
+ })),
55
+ };
56
+ console.log(JSON.stringify(payload, null, 2));
57
+ if (result.summary.fail > 0) process.exitCode = 1;
58
+ return;
59
+ }
60
+
61
+ console.log();
62
+ console.log(chalk.bold('Spec Lint'));
63
+ console.log(chalk.gray('─'.repeat(50)));
64
+
65
+ if (!result.specsDirExists) {
66
+ // 查無 spec 目錄 must say so explicitly — an empty { pass: 0, warn: 0,
67
+ // fail: 0 } summary is indistinguishable from "checked, all clean" unless
68
+ // the command says out loud that nothing was scanned.
69
+ console.log(chalk.yellow(` 查無 spec 目錄(./${result.specsDir}/ 不存在)`));
70
+ console.log(chalk.gray(' 沒有東西被掃描——這不代表沒有問題,代表沒有檢查。'));
71
+ console.log();
72
+ return;
73
+ }
74
+
75
+ console.log(chalk.gray(` 掃描 ${result.results.length} 份 spec(./${result.specsDir}/)`));
76
+ console.log();
77
+
78
+ for (const r of result.results) {
79
+ const msg = buildMessage(r);
80
+ if (r.status === 'fail') {
81
+ console.log(chalk.red(` ✗ ${r.spec}: ${msg}`));
82
+ } else if (r.status === 'warn') {
83
+ console.log(chalk.yellow(` ⚠ ${r.spec}: ${msg}`));
84
+ } else {
85
+ console.log(chalk.green(` ✓ ${r.spec}: ${msg}`));
86
+ }
87
+ }
88
+
89
+ console.log();
90
+ console.log(
91
+ chalk.gray(` ${result.summary.pass} pass, ${result.summary.warn} warn, ${result.summary.fail} fail`)
92
+ );
93
+ console.log();
94
+
95
+ if (result.summary.fail > 0) process.exitCode = 1;
96
+ }
@@ -3,6 +3,19 @@
3
3
  *
4
4
  * Reduces cognitive load by guiding users to common workflow paths.
5
5
  *
6
+ * ⚠️ Every `cmd` that starts with `uds ` MUST be a command this CLI actually
7
+ * registers, with flags it actually accepts. This is the one place whose entire
8
+ * job is "find the right commands quickly", so a wrong entry here does the exact
9
+ * opposite of what the command exists for.
10
+ *
11
+ * 2026-08-19: four entries pointed at things that do not exist — `uds lint`
12
+ * (twice), `uds sync` (twice, one of them the sole content of a whole
13
+ * workflow), `uds check --spec-size`, and `uds spec create --boost`. Verify with
14
+ * `uds <cmd> --definitely-not-a-real-flag` and look for "unknown command";
15
+ * `uds <cmd> --help` is NOT a valid check — commander prints the general help
16
+ * for an unknown command instead of erroring, so --help reports every name as
17
+ * valid.
18
+ *
6
19
  * @module commands/quickstart
7
20
  */
8
21
 
@@ -24,11 +37,10 @@ export const WORKFLOWS = [
24
37
  name: 'Full SDD Spec Flow (Boost)',
25
38
  description: 'Complete spec-driven development for complex features',
26
39
  steps: [
27
- { cmd: 'uds spec create "your feature" --boost', desc: 'Create full SDD spec with design sections' },
28
- { cmd: 'uds lint', desc: 'Validate spec quality and cross-references' },
40
+ { cmd: '# Use the /sdd skill', desc: 'Full spec lifecycle with review (see `uds spec --help`)' },
41
+ { cmd: 'uds spec create "your feature" --scope fullstack', desc: 'Create the spec, scoped' },
29
42
  { cmd: 'uds spec confirm SPEC-XXX', desc: 'Confirm after review' },
30
43
  { cmd: '# Implement with /derive → /tdd', desc: 'Use forward derivation and TDD' },
31
- { cmd: 'uds sync', desc: 'Export context for session resume' },
32
44
  ],
33
45
  },
34
46
  {
@@ -46,19 +58,10 @@ export const WORKFLOWS = [
46
58
  description: 'Audit standards compliance and spec quality',
47
59
  steps: [
48
60
  { cmd: 'uds check', desc: 'Check standards file integrity' },
49
- { cmd: 'uds check --spec-size', desc: 'Check spec sizes against limits' },
50
- { cmd: 'uds lint', desc: 'Lint specs for AC coverage and dependency validity' },
61
+ { cmd: 'uds check --i18n', desc: 'Run i18n lint rules across canonical + locale variants' },
51
62
  { cmd: 'uds audit', desc: 'Deep health diagnosis' },
52
63
  ],
53
64
  },
54
- {
55
- name: 'Resume Previous Work',
56
- description: 'Restore context from a previous session',
57
- steps: [
58
- { cmd: 'uds sync', desc: 'Generate context.md from git diff + workflow state' },
59
- { cmd: 'cat .workflow-state/context.md', desc: 'Read context in new session' },
60
- ],
61
- },
62
65
  ];
63
66
 
64
67
  /**