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
@@ -0,0 +1,309 @@
1
+ #!/usr/bin/env node
2
+ // SPDX-License-Identifier: MIT
3
+ //
4
+ // ─────────────────────────────────────────────────────────────────────────────
5
+ // 錯誤訊息單一出口閘門  可攜版(複製到任何專案,只改下面的 CONFIG)
6
+ // ─────────────────────────────────────────────────────────────────────────────
7
+ //
8
+ // 它防的是什麼:
9
+ // 2026-09-03 使用者照著建議打上企業名稱,畫面回他一句 `Bad Request`。
10
+ // 真正的原因寫在回應的 `details` 裡,而呼叫端只讀了 `error`——把它丟掉了。
11
+ // 當天的修法改了**一個**呼叫端;隔天走訪整個前端,同型的有 24 處,
12
+ // 其中三處就在當時打開的那個檔案裡,一處只在往下十行。
13
+ // **「修好了」與「修好了其中一個」在畫面上無從分辨**,因為壞的在別的頁面。
14
+ //
15
+ // 它怎麼防:
16
+ // 規則收進一個檔案(單一出口),其餘每一處都必須經過它。
17
+ // 這支腳本走訪整個專案,找「有人自己把錯誤物件的欄位拼成字串」。
18
+ //
19
+ // 為什麼是獨立腳本而不是一支測試:
20
+ // 測試框架每個專案不一樣(vitest/jest/node:test),而這道規則跟框架無關。
21
+ // 寫成 .mjs 就不必在意目標專案用哪一套,也不必在意測試檔要放哪個目錄。
22
+ //
23
+ // 用法:
24
+ // node scripts/check-error-exit.mjs 走訪並檢查
25
+ // node scripts/check-error-exit.mjs --self-test 只驗判別式自己
26
+ // node scripts/check-error-exit.mjs --json 機器可讀輸出
27
+ //
28
+ // 退出碼:0 通過|1 有違規(或宣告與事實不符)|2 量不到(沒設定、走訪不到東西)
29
+ // 🔴 2 也是不通過。在一道會擋的閘門裡,如果「解析不了」放行,
30
+ // 那弄壞設定就成了繞過的方法。
31
+
32
+ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
33
+ import { join, relative, sep } from "node:path";
34
+ import { dirname } from "node:path";
35
+ import { fileURLToPath } from "node:url";
36
+
37
+ const REPO_ROOT = join(dirname(fileURLToPath(import.meta.url)), "..");
38
+
39
+ // ═══════════════════════════════════════════════════════════════════════════
40
+ // CONFIG —— 複製到新專案後,要動的只有這一塊
41
+ // ═══════════════════════════════════════════════════════════════════════════
42
+ const CONFIG = {
43
+ /**
44
+ * 🔴 沒填就是沒設定,而沒設定會 exit 2,**不會安靜地通過**。
45
+ * 一支在新專案裡靜靜回綠的閘門,跟一支根本沒裝的閘門無從分辨。
46
+ */
47
+ configured: false,
48
+
49
+ /** 走訪哪些目錄(相對 repo 根)。 */
50
+ scanDirs: ["src"],
51
+
52
+ /** 規則住在哪個檔案。它是唯一被允許讀那些欄位的地方。 */
53
+ singleExit: null,
54
+
55
+ /** 走訪時跳過的目錄名。 */
56
+ skipDirs: ["node_modules", "dist", "build", ".vite", "__tests__", "coverage"],
57
+
58
+ /** 副檔名。 */
59
+ extensions: [".ts", ".tsx", ".js", ".jsx", ".mjs"],
60
+
61
+ /** 走訪至少要看到幾個檔——低於這個數代表走訪器壞了,而那與「全部通過」輸出相同。 */
62
+ minFilesScanned: 10,
63
+
64
+ /**
65
+ * 這個專案根本不把錯誤回應轉成文字時填這裡(例如純資料處理的函式庫)。
66
+ * 🔴 **這個宣告本身會被驗證**:填了之後腳本會去找專案有沒有在讀 HTTP 回應,
67
+ * 找得到就代表宣告是假的,exit 1。不驗證的宣告只是一個有禮貌的關閉開關。
68
+ * 形狀:{ reason: "……", declaredOn: "YYYY-MM-DD" }
69
+ */
70
+ notApplicable: null,
71
+ };
72
+ // ═══════════════════════════════════════════════════════════════════════════
73
+
74
+ /**
75
+ * 判別式:抓「把錯誤物件的欄位當成給人看的字串」。
76
+ *
77
+ * ⚠️ 刻意**不**抓 `e instanceof Error ? e.message : "…"` 與 `e?.message ?? "…"`——
78
+ * 那些讀的是 JS 的 Error 物件,不是回應的 body。抓進來這道閘門就沒有人滿足得了,
79
+ * 而滿足不了的閘門的下場是被關掉。
80
+ *
81
+ * ⚠️ 也刻意**不**抓後備值是 null/undefined 的——那是成功路徑在讀選填欄位。
82
+ * 每加一個排除項就要說出它把問題改成了什麼:這裡把問題從「有沒有讀 message」
83
+ * 改成「**有沒有把 message 當成給人看的錯誤字串**」。
84
+ *
85
+ * ⚠️ 排除項必須寫成 `\?\?(?!\s*(?:null|undefined)\b)`,不能寫成 `\?\?\s*(?!null\b)`:
86
+ * 後者的 `\s*` 會回溯成零寬,讓否定環顧在空白處成立而整個排除失效。
87
+ * **這個 bug 是下面的反臂抓到的,不是看出來的。**
88
+ */
89
+ export function findsHandRolledErrorText(src) {
90
+ const re = /\b[A-Za-z_$][\w$]*\.(?:error|message)\s*\?\?(?!\s*(?:null|undefined)\b)/g;
91
+ return (src.match(re) ?? []).length;
92
+ }
93
+
94
+ /** 「這個專案有沒有在讀 HTTP 回應」——用來驗證 notApplicable 的宣告是不是真的。 */
95
+ export function readsHttpResponses(src) {
96
+ return /\bfetch\s*\(|\bres(?:ponse)?\.json\s*\(|\baxios\b|\bgot\s*\(/.test(src);
97
+ }
98
+
99
+ function walk(dirs) {
100
+ const out = [];
101
+ const skip = new Set(CONFIG.skipDirs);
102
+ const isTest = (n) => /\.(test|spec)\.[jt]sx?$/.test(n);
103
+ const visit = (dir) => {
104
+ let entries;
105
+ try {
106
+ entries = readdirSync(dir);
107
+ } catch {
108
+ return; // 目錄不存在——由 minFilesScanned 那道分母臂負責報告
109
+ }
110
+ for (const name of entries) {
111
+ const full = join(dir, name);
112
+ let st;
113
+ try {
114
+ st = statSync(full);
115
+ } catch {
116
+ continue;
117
+ }
118
+ if (st.isDirectory()) {
119
+ if (!skip.has(name)) visit(full);
120
+ continue;
121
+ }
122
+ if (!CONFIG.extensions.some((e) => name.endsWith(e))) continue;
123
+ if (isTest(name)) continue;
124
+ out.push(full);
125
+ }
126
+ };
127
+ for (const d of dirs) visit(join(REPO_ROOT, d));
128
+ return out;
129
+ }
130
+
131
+ // ── 自我測試:一個從未被觀察到會紅的閘門,與一個永遠回綠的閘門無從分辨 ──────
132
+ function selfTest() {
133
+ const arms = [
134
+ [
135
+ "正臂:抓得到真實的四種寫法",
136
+ () => {
137
+ const a = findsHandRolledErrorText("throw new Error(body.error ?? `HTTP ${res.status}`)") === 1;
138
+ const b = findsHandRolledErrorText('setError(data.error ?? "無法開始登入。")') === 1;
139
+ const c = findsHandRolledErrorText("throw new Error(err.message ?? err.error ?? `HTTP ${s}`)") === 2;
140
+ const d = findsHandRolledErrorText("throw new Error(retryErr.error ?? `HTTP ${s}`)") === 1;
141
+ return a && b && c && d;
142
+ },
143
+ ],
144
+ [
145
+ "🔴 負臂:JS Error 物件的讀法不得被抓(抓了這閘門就沒人滿足得了)",
146
+ () =>
147
+ findsHandRolledErrorText('setError(e instanceof Error ? e.message : "載入失敗")') === 0 &&
148
+ findsHandRolledErrorText('setError(e?.message ?? "Failed to load")') === 0 &&
149
+ findsHandRolledErrorText("throw new Error(await readApiError(res))") === 0,
150
+ ],
151
+ [
152
+ "🔴 負臂:成功路徑讀選填欄位不算(首跑抓到的假陽性)",
153
+ () =>
154
+ findsHandRolledErrorText("setModelListMessage(body.message ?? null)") === 0 &&
155
+ findsHandRolledErrorText("const m = body.message ?? undefined;") === 0 &&
156
+ findsHandRolledErrorText('const m = body.message ?? "失敗";') === 1,
157
+ ],
158
+ [
159
+ "宣告驗證臂:偵測得到專案有在讀 HTTP 回應",
160
+ () =>
161
+ readsHttpResponses('const r = await fetch("/api/x");') === true &&
162
+ readsHttpResponses("const b = await res.json();") === true,
163
+ ],
164
+ [
165
+ "🔴 宣告驗證的負臂:純計算的檔案不得被判為有讀回應",
166
+ () => readsHttpResponses("export const add = (a, b) => a + b;") === false,
167
+ ],
168
+ [
169
+ // ⚠️ 尚未設定、或宣告不適用時,這一臂沒有東西可量。它會**明說自己被跳過**,
170
+ // 不會靜靜回綠——「跳過」與「通過」在輸出上要分得出來。
171
+ CONFIG.configured && !CONFIG.notApplicable
172
+ ? "分母臂:走訪拿得到檔案(0 與『全部通過』的輸出相同)"
173
+ : "分母臂:跳過(尚未設定或已宣告不適用,沒有東西可走訪)",
174
+ () =>
175
+ !CONFIG.configured || CONFIG.notApplicable !== null || walk(CONFIG.scanDirs).length >= CONFIG.minFilesScanned,
176
+ ],
177
+ ];
178
+ let failed = 0;
179
+ console.log("[error-exit --self-test]");
180
+ for (const [name, fn] of arms) {
181
+ let ok = false;
182
+ // ⚠️ 用另一個變數,不要改 name——它是 for-of 解構出來的 const。
183
+ // 第一版直接 `name += …`,而那條路徑**只有在某個臂擲出例外時才會走到**,
184
+ // 也就是自我測試自己的錯誤處理是壞的,而平常永遠看不出來。lint 抓到的。
185
+ let note = "";
186
+ try {
187
+ ok = fn() === true;
188
+ } catch (e) {
189
+ ok = false;
190
+ note = ` (擲出例外:${e.message})`;
191
+ }
192
+ console.log(` ${ok ? "✓" : "✗"} ${name}${note}`);
193
+ if (!ok) failed++;
194
+ }
195
+ console.log(`[error-exit --self-test] 通過 ${arms.length - failed}/失敗 ${failed}`);
196
+ return failed === 0 ? 0 : 2;
197
+ }
198
+
199
+ // ── 主流程 ────────────────────────────────────────────────────────────────
200
+ function main() {
201
+ const argv = process.argv.slice(2);
202
+ if (argv.includes("--self-test")) return selfTest();
203
+ const asJson = argv.includes("--json");
204
+ const say = (s) => {
205
+ if (!asJson) console.log(s);
206
+ };
207
+ const emit = (verdict, extra = {}) => {
208
+ if (asJson) console.log(JSON.stringify({ verdict, ...extra }, null, 2));
209
+ return verdict === "pass" ? 0 : verdict === "violations" ? 1 : 2;
210
+ };
211
+
212
+ // 狀態一:沒設定。**不是通過。**
213
+ if (!CONFIG.configured) {
214
+ say("[error-exit] ✗ 這個專案還沒指定它的錯誤訊息單一出口。");
215
+ say(" 打開 scripts/check-error-exit.mjs 的 CONFIG 區塊,填 scanDirs 與 singleExit,");
216
+ say(" 然後把 configured 改成 true。真的用不到就填 notApplicable(要寫理由)。");
217
+ say(" 🔴 這裡刻意不放行:一支在新專案裡靜靜回綠的閘門,跟沒裝無從分辨。");
218
+ return emit("unmeasurable", { why: "not-configured" });
219
+ }
220
+
221
+ // 狀態二:宣告不適用——而宣告本身要被驗證。
222
+ if (CONFIG.notApplicable) {
223
+ const { reason, declaredOn } = CONFIG.notApplicable;
224
+ if (!reason || !declaredOn) {
225
+ say("[error-exit] ✗ notApplicable 要同時有 reason 與 declaredOn。沒有理由的豁免只是關閉開關。");
226
+ return emit("unmeasurable", { why: "declaration-incomplete" });
227
+ }
228
+ const files = walk(CONFIG.scanDirs);
229
+ const talkers = files.filter((f) => readsHttpResponses(readFileSync(f, "utf8")));
230
+ say(`[error-exit] 宣告不適用(${declaredOn}):${reason}`);
231
+ say(` 走訪 ${files.length} 個檔驗證這個宣告——不驗證的宣告只是有禮貌的關閉開關。`);
232
+ if (talkers.length > 0) {
233
+ say(`[error-exit] ✗ 宣告與程式碼不符:${talkers.length} 個檔在讀 HTTP 回應。`);
234
+ for (const t of talkers.slice(0, 5)) say(` · ${relative(REPO_ROOT, t)}`);
235
+ return emit("violations", { why: "false-declaration", files: talkers.map((t) => relative(REPO_ROOT, t)) });
236
+ }
237
+ say("[error-exit] ✓ 宣告成立:走訪範圍內沒有任何檔案在讀 HTTP 回應。");
238
+ return emit("pass", { state: "not-applicable" });
239
+ }
240
+
241
+ // 狀態三:正常走訪。
242
+ //
243
+ // 🔴 singleExit 允許是 null,而這**不是**一個放水的開關。
244
+ // 2026-09-04 把這道閘門裝進另外三個 repo 時才發現:它們今天都是 0 處。
245
+ // 要求它們先生一個「單一出口」檔案出來,等於逼一個**沒有任何呼叫端的模組**誕生,
246
+ // 而那正是我們在別的地方花整整一份規格在抓的東西(可達、但沒有人用)。
247
+ // 正確的語意是:**這道閘門要防的是第二處,不是第一處。**
248
+ // · 有指定出口 → 除了它,其餘一律 0。
249
+ // · 沒指定出口 → 最多容許 1 個檔(那個檔按定義就是出口);出現第 2 個就紅,
250
+ // 並且當場告訴你「現在有兩個了,挑一個當出口、其餘接過去」。
251
+ const hasDesignatedExit = typeof CONFIG.singleExit === "string" && CONFIG.singleExit.length > 0;
252
+ if (hasDesignatedExit && !existsSync(join(REPO_ROOT, CONFIG.singleExit))) {
253
+ say(`[error-exit] ✗ 指定的單一出口不存在:${CONFIG.singleExit}`);
254
+ say(" 檔案被搬走或改名時,這道閘門會變成在檢查一個不存在的規則。");
255
+ return emit("unmeasurable", { why: "single-exit-missing" });
256
+ }
257
+
258
+ const files = walk(CONFIG.scanDirs);
259
+ say(`[error-exit] 走訪 ${CONFIG.scanDirs.join("、")} 共 ${files.length} 個非測試檔`);
260
+ if (files.length < CONFIG.minFilesScanned) {
261
+ say(`[error-exit] ✗ 只走訪到 ${files.length} 個檔(下限 ${CONFIG.minFilesScanned})。`);
262
+ say(" 🔴 走訪不到東西與『全部通過』的輸出一模一樣,所以這裡當成量不到,不當成通過。");
263
+ return emit("unmeasurable", { why: "denominator-too-small", scanned: files.length });
264
+ }
265
+
266
+ const exitRel = hasDesignatedExit ? CONFIG.singleExit.split("/").join(sep) : null;
267
+ const carriers = []; // 含有那個形狀的檔(不含指定出口)
268
+ for (const f of files) {
269
+ const rel = relative(REPO_ROOT, f);
270
+ if (exitRel !== null && rel === exitRel) continue; // 規則本身住的地方
271
+ const hits = findsHandRolledErrorText(readFileSync(f, "utf8"));
272
+ if (hits > 0) carriers.push({ file: rel, hits });
273
+ }
274
+
275
+ // ── 有指定出口:其餘一律 0 ──────────────────────────────────────────────
276
+ if (hasDesignatedExit) {
277
+ if (carriers.length > 0) {
278
+ say(`[error-exit] ✗ ${carriers.length} 個檔自己把錯誤回應拼成字串:`);
279
+ for (const o of carriers) say(` · ${o.file}(${o.hits} 處)`);
280
+ say(` 改成經過 ${CONFIG.singleExit}——理由見那個檔的檔頭。`);
281
+ return emit("violations", { offenders: carriers });
282
+ }
283
+ say(`[error-exit] ✓ ${files.length} 個檔裡,只有 ${CONFIG.singleExit} 在讀那些欄位。`);
284
+ say(" 本閘門**沒有**證明的事:那個出口自己寫對了沒有。");
285
+ say(" 出口的優先順序若反了,所有呼叫端會一致地錯而這裡全綠——那是它自己單元測試的工作。");
286
+ return emit("pass", { scanned: files.length, exit: CONFIG.singleExit });
287
+ }
288
+
289
+ // ── 尚未指定出口:容許 1 個,第 2 個就是重複的開始 ──────────────────────
290
+ if (carriers.length > 1) {
291
+ say(`[error-exit] ✗ ${carriers.length} 個檔各自把錯誤回應拼成字串,而還沒有指定的單一出口:`);
292
+ for (const o of carriers) say(` · ${o.file}(${o.hits} 處)`);
293
+ say(" 挑其中一個當出口(或新開一個),其餘接過去,然後把 CONFIG.singleExit 填上。");
294
+ say(" 🔴 本閘門防的是**第二處**:第一處是實作,第二處開始是各寫各的。");
295
+ return emit("violations", { offenders: carriers, why: "duplication-without-exit" });
296
+ }
297
+ if (carriers.length === 1) {
298
+ say(`[error-exit] ✓ 只有一個檔在做這件事:${carriers[0].file}`);
299
+ say(" 它按定義就是這個 repo 的單一出口。出現第二個時本閘門會紅。");
300
+ say(" 把 CONFIG.singleExit 填成它,可以讓這件事變成明講的而不是碰巧的。");
301
+ return emit("pass", { scanned: files.length, deFactoExit: carriers[0].file });
302
+ }
303
+ say(`[error-exit] ✓ 走訪 ${files.length} 個檔,還沒有任何地方在把錯誤回應拼成字串。`);
304
+ say(" 這個類別目前是空的——第一個成員出現時不會紅(那是實作),");
305
+ say(" 第二個出現時會紅(那是重複)。**不是空轉:它在等第二處。**");
306
+ return emit("pass", { scanned: files.length, state: "class-empty" });
307
+ }
308
+
309
+ process.exit(main());
@@ -0,0 +1,10 @@
1
+ {
2
+ "_comment": "Add this to your Claude Code settings.json or Cursor mcp.json",
3
+ "mcpServers": {
4
+ "uds-design-standards": {
5
+ "command": "uds",
6
+ "args": ["mcp", "serve"],
7
+ "description": "UDS Frontend Design Standards Server"
8
+ }
9
+ }
10
+ }
@@ -0,0 +1,209 @@
1
+ # Custom Methodology Template
2
+ # 自訂方法論模板
3
+ #
4
+ # Use this template to create your own development methodology.
5
+ # Save to: .standards/methodologies/<your-id>.methodology.yaml
6
+ #
7
+ # 使用此模板建立您自己的開發方法論。
8
+ # 儲存到: .standards/methodologies/<your-id>.methodology.yaml
9
+
10
+ $schema: "https://raw.githubusercontent.com/AsiaOstrich/universal-dev-standards/main/methodologies/methodology-schema.json"
11
+
12
+ # === BASIC INFORMATION | 基本資訊 ===
13
+ id: my-workflow # Unique ID (lowercase, hyphens) | 唯一識別碼
14
+ name: My Custom Workflow # Display name | 顯示名稱
15
+ nameZh: 我的自訂工作流 # Chinese name (optional) | 中文名稱(選填)
16
+ version: 1.0.0 # Semantic version | 語意化版本
17
+ description: A custom development workflow for our team
18
+ descriptionZh: 我們團隊的自訂開發工作流
19
+
20
+ # === PHASES | 階段 ===
21
+ # Define your development phases. Each phase represents a distinct stage.
22
+ # 定義您的開發階段。每個階段代表一個明確的開發狀態。
23
+ phases:
24
+ # --- Phase 1: Planning ---
25
+ - id: plan
26
+ name: Planning
27
+ nameZh: 規劃
28
+ description: Plan the implementation approach
29
+ descriptionZh: 規劃實作方法
30
+ duration: "15-30 minutes"
31
+ emoji: "📋"
32
+
33
+ # Checklist items for this phase
34
+ # 此階段的檢查清單項目
35
+ checklist:
36
+ - id: requirements-clear
37
+ text: Requirements are clearly understood
38
+ textZh: 需求已清楚理解
39
+ required: true # Must complete to proceed | 必須完成才能繼續
40
+ - id: design-reviewed
41
+ text: Design approach reviewed with team
42
+ textZh: 設計方法已與團隊審查
43
+ required: false # Optional item | 選填項目
44
+
45
+ # Phase transition triggers
46
+ # 階段轉換觸發條件
47
+ triggers:
48
+ entry:
49
+ - condition: feature_start # When to enter this phase | 何時進入此階段
50
+ exit:
51
+ - condition: user_confirms_plan_complete
52
+ nextPhase: implement # Where to go next | 下一個階段
53
+
54
+ # AI guidance for this phase
55
+ # 此階段的 AI 引導提示
56
+ guidance:
57
+ prompt: |
58
+ ## Planning Phase
59
+
60
+ Before implementation, ensure:
61
+ {{ checklist | format_checklist }}
62
+
63
+ Consider:
64
+ - What is the expected outcome?
65
+ - Are there any dependencies?
66
+ - What could go wrong?
67
+ promptZh: |
68
+ ## 規劃階段
69
+
70
+ 在實作前,確保:
71
+ {{ checklist | format_checklist }}
72
+
73
+ 考慮:
74
+ - 預期結果是什麼?
75
+ - 有任何相依性嗎?
76
+ - 可能出什麼問題?
77
+
78
+ # --- Phase 2: Implementation ---
79
+ - id: implement
80
+ name: Implementation
81
+ nameZh: 實作
82
+ description: Write the code
83
+ descriptionZh: 撰寫程式碼
84
+ duration: "Variable"
85
+ emoji: "💻"
86
+
87
+ checklist:
88
+ - id: follows-patterns
89
+ text: Code follows team patterns
90
+ textZh: 程式碼遵循團隊慣例
91
+ required: true
92
+ - id: tests-written
93
+ text: Tests written alongside code
94
+ textZh: 測試與程式碼同步撰寫
95
+ required: true
96
+ - id: no-debug-code
97
+ text: No debugging code left behind
98
+ textZh: 無除錯程式碼殘留
99
+ required: true
100
+
101
+ triggers:
102
+ entry:
103
+ - condition: plan_complete
104
+ exit:
105
+ - condition: implementation_complete
106
+ nextPhase: review
107
+
108
+ guidance:
109
+ prompt: |
110
+ ## Implementation Phase
111
+
112
+ Writing code for: {{feature_description}}
113
+
114
+ {{ checklist | format_checklist }}
115
+
116
+ Remember:
117
+ - Keep commits small and focused
118
+ - Write tests as you go
119
+
120
+ # --- Phase 3: Review ---
121
+ - id: review
122
+ name: Review
123
+ nameZh: 審查
124
+ description: Self-review and prepare for PR
125
+ descriptionZh: 自我審查並準備 PR
126
+ duration: "15-30 minutes"
127
+ emoji: "👀"
128
+
129
+ checklist:
130
+ - id: self-reviewed
131
+ text: Code self-reviewed
132
+ textZh: 程式碼已自我審查
133
+ required: true
134
+ - id: tests-pass
135
+ text: All tests pass
136
+ textZh: 所有測試通過
137
+ required: true
138
+ - id: docs-updated
139
+ text: Documentation updated
140
+ textZh: 文件已更新
141
+ required: false
142
+
143
+ triggers:
144
+ entry:
145
+ - condition: implementation_complete
146
+ exit:
147
+ - condition: ready_for_pr
148
+ nextPhase: done
149
+
150
+ guidance:
151
+ prompt: |
152
+ ## Review Phase
153
+
154
+ Before marking as ready:
155
+ {{ checklist | format_checklist }}
156
+
157
+ Self-review questions:
158
+ - Would I approve this if reviewing someone else's code?
159
+ - Are edge cases handled?
160
+ - Is the code easy to understand?
161
+
162
+ # --- Phase 4: Done ---
163
+ - id: done
164
+ name: Done
165
+ nameZh: 完成
166
+ description: Ready for PR
167
+ descriptionZh: 準備提交 PR
168
+ emoji: "✅"
169
+ checklist: []
170
+ triggers:
171
+ entry:
172
+ - condition: ready_for_pr
173
+ exit: []
174
+
175
+ # === CHECKPOINTS | 檢查點 ===
176
+ # Define automatic reminders and validation points
177
+ # 定義自動提醒和驗證點
178
+ checkpoints:
179
+ - id: phase-transition
180
+ trigger: phase_transition # When a phase is completed
181
+ intensity: suggest # suggest | warn | block
182
+ action: "Phase complete. Consider committing your changes."
183
+ actionZh: "階段完成。考慮提交您的變更。"
184
+
185
+ - id: large-changes
186
+ trigger: lines_changed
187
+ threshold: 200 # Trigger after 200+ lines changed
188
+ intensity: warn
189
+ action: "You have 200+ uncommitted lines. Consider breaking into smaller commits."
190
+ actionZh: "您有超過 200 行未提交的變更。考慮拆分為較小的提交。"
191
+
192
+ # === COMMANDS | 命令 ===
193
+ # Define custom slash commands for this methodology (optional)
194
+ # 為此方法論定義自訂斜線命令(選填)
195
+ commands:
196
+ - name: workflow
197
+ description: Start custom workflow
198
+ descriptionZh: 啟動自訂工作流
199
+ usage: "/workflow [feature]"
200
+ action: start_workflow
201
+
202
+ # === AI GUIDANCE SETTINGS | AI 引導設定 ===
203
+ aiGuidance:
204
+ autoDetect: true # Detect methodology from context
205
+ proactiveReminders: true # Show phase reminders proactively
206
+ contextKeywords: # Keywords that trigger this methodology
207
+ - "team workflow"
208
+ - "our process"
209
+ - "custom workflow"