universal-dev-standards 6.9.0 → 6.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/uds.js +2 -0
- package/bundled/core/agent-communication-protocol.md +8 -0
- package/bundled/core/branch-completion.md +8 -0
- package/bundled/core/change-batching-standards.md +8 -0
- package/bundled/core/execution-history.md +8 -0
- package/bundled/core/pipeline-integration-standards.md +8 -0
- package/bundled/core/workflow-enforcement.md +8 -0
- package/bundled/core/workflow-state-protocol.md +8 -0
- package/bundled/locales/zh-CN/CHANGELOG.md +49 -3
- package/bundled/locales/zh-CN/README.md +1 -1
- package/bundled/locales/zh-CN/SECURITY.md +1 -1
- package/bundled/locales/zh-CN/core/agent-communication-protocol.md +7 -0
- package/bundled/locales/zh-CN/core/branch-completion.md +7 -0
- package/bundled/locales/zh-CN/core/change-batching-standards.md +7 -0
- package/bundled/locales/zh-CN/core/execution-history.md +7 -0
- package/bundled/locales/zh-CN/core/pipeline-integration-standards.md +7 -0
- package/bundled/locales/zh-CN/core/workflow-enforcement.md +7 -0
- package/bundled/locales/zh-CN/core/workflow-state-protocol.md +7 -0
- package/bundled/locales/zh-CN/docs/CHEATSHEET.md +1 -1
- package/bundled/locales/zh-CN/docs/CLI-INIT-OPTIONS.md +52 -5
- package/bundled/locales/zh-CN/docs/FEATURE-REFERENCE.md +3 -1
- package/bundled/locales/zh-CN/docs/MIGRATION-v6.md +8 -4
- package/bundled/locales/zh-TW/CHANGELOG.md +50 -3
- package/bundled/locales/zh-TW/README.md +1 -1
- package/bundled/locales/zh-TW/SECURITY.md +1 -1
- package/bundled/locales/zh-TW/core/agent-communication-protocol.md +7 -0
- package/bundled/locales/zh-TW/core/branch-completion.md +7 -0
- package/bundled/locales/zh-TW/core/change-batching-standards.md +7 -0
- package/bundled/locales/zh-TW/core/execution-history.md +7 -0
- package/bundled/locales/zh-TW/core/pipeline-integration-standards.md +7 -0
- package/bundled/locales/zh-TW/core/workflow-enforcement.md +7 -0
- package/bundled/locales/zh-TW/core/workflow-state-protocol.md +7 -0
- package/bundled/locales/zh-TW/docs/CHEATSHEET.md +1 -1
- package/bundled/locales/zh-TW/docs/CLI-INIT-OPTIONS.md +52 -5
- package/bundled/locales/zh-TW/docs/FEATURE-REFERENCE.md +3 -1
- package/bundled/locales/zh-TW/docs/MIGRATION-v6.md +8 -4
- package/bundled/locales/zh-TW/integrations/claude-code/README.md +14 -5
- package/package.json +7 -6
- package/src/commands/check.js +413 -44
- package/src/commands/config.js +34 -28
- package/src/commands/init.js +37 -7
- package/src/commands/spec.js +2 -2
- package/src/commands/update.js +560 -74
- package/src/core/manifest.js +39 -1
- package/src/flows/init-flow.js +9 -1
- package/src/generators/layered-claudemd.js +13 -4
- package/src/i18n/messages.js +39 -3
- package/src/installers/integration-installer.js +17 -10
- package/src/installers/manifest-installer.js +4 -0
- package/src/installers/skills-installer.js +4 -4
- package/src/installers/standards-installer.js +3 -3
- package/src/prompts/init.js +33 -4
- package/src/reconciler/actual-state-scanner.js +29 -2
- package/src/reconciler/desired-state-calculator.js +51 -2
- package/src/reconciler/diff-engine.js +76 -5
- package/src/reconciler/plan-executor.js +48 -27
- package/src/utils/hasher.js +61 -5
- package/src/utils/integration-generator.js +431 -92
- package/src/utils/marker-locator.js +140 -0
- package/src/utils/reference-sync.js +156 -8
- package/src/utils/registry.js +57 -0
- package/src/utils/spinner.js +31 -0
- package/standards-registry.json +8 -8
|
@@ -3,8 +3,10 @@ import { dirname, join, basename } from 'path';
|
|
|
3
3
|
import { getLanguageRules } from '../prompts/integrations.js';
|
|
4
4
|
import { computeIntegrationBlockHash } from './hasher.js';
|
|
5
5
|
import { UDS_MARKERS, SUPPORTED_AI_TOOLS, LEGACY_TOOL_MAPPINGS } from '../core/constants.js';
|
|
6
|
-
import {
|
|
6
|
+
import { locateMarkerBlock } from './marker-locator.js';
|
|
7
|
+
import { resolveSelectedOptionSources, resolveStandardFilename, getAllStandards } from './registry.js';
|
|
7
8
|
import { getAgentConfig, getAgentTier } from '../config/ai-agent-paths.js';
|
|
9
|
+
import { STANDARD_ID_MAPPING } from './conversion-rules.js';
|
|
8
10
|
|
|
9
11
|
/**
|
|
10
12
|
* Resolve contentMode and level based on AI tool's tier and capabilities.
|
|
@@ -2003,6 +2005,16 @@ src/
|
|
|
2003
2005
|
/**
|
|
2004
2006
|
* Tool-specific file headers
|
|
2005
2007
|
*/
|
|
2008
|
+
// 🔴 These headers used to open by telling the agent to "PRIORITIZE reading the
|
|
2009
|
+
// concise rules in `core/`". `uds init` installs standards into `.standards/`
|
|
2010
|
+
// and has never created a `core/` directory in an adopter's project — `core/`
|
|
2011
|
+
// is where the standards live in the UDS repo, not in the repos that adopt it.
|
|
2012
|
+
// So the first instruction in every generated file, for every tool, in every
|
|
2013
|
+
// language, pointed at nothing. An adopter reported the symptom on 2026-09-16
|
|
2014
|
+
// and read it as residue from an old install; it was not residue, a clean
|
|
2015
|
+
// `uds init` wrote it. 36 copies of the same two sentences, all wrong the same
|
|
2016
|
+
// way. `tests/unit/utils/tool-headers-no-core.test.js` walks the generated
|
|
2017
|
+
// output so a tenth tool cannot reintroduce it.
|
|
2006
2018
|
const TOOL_HEADERS = {
|
|
2007
2019
|
cursor: {
|
|
2008
2020
|
en: `# Cursor Rules
|
|
@@ -2015,8 +2027,8 @@ You are an expert software engineer assistant. Follow these project standards.
|
|
|
2015
2027
|
All responses should be in **English**.
|
|
2016
2028
|
|
|
2017
2029
|
## Core Standards Usage Rule
|
|
2018
|
-
> When verifying standards, checking code, or performing tasks,
|
|
2019
|
-
>
|
|
2030
|
+
> When verifying standards, checking code, or performing tasks, read the rules in \`.standards/\` — that is where this project's standards are installed.
|
|
2031
|
+
> Each file states its own requirements; read the one the task is about rather than all of them.
|
|
2020
2032
|
> This ensures token efficiency and focused context.`,
|
|
2021
2033
|
'zh-tw': `# Cursor 規則
|
|
2022
2034
|
# 由 Universal Dev Standards CLI 生成
|
|
@@ -2028,8 +2040,8 @@ All responses should be in **English**.
|
|
|
2028
2040
|
所有回覆必須使用**繁體中文 (Traditional Chinese)**。
|
|
2029
2041
|
|
|
2030
2042
|
## 核心標準使用規則 / Core Standards Usage Rule
|
|
2031
|
-
>
|
|
2032
|
-
>
|
|
2043
|
+
> 當驗證標準、檢查程式碼或執行任務時,讀取 \`.standards/\` 裡的規則——那是本專案標準實際安裝的位置。
|
|
2044
|
+
> 每個檔案各自寫明它要求什麼;讀與當下任務相關的那一份,不必全部讀完。
|
|
2033
2045
|
> 這確保了 Token 效率和上下文聚焦。`,
|
|
2034
2046
|
bilingual: `# Cursor Rules | Cursor 規則
|
|
2035
2047
|
# Generated by Universal Dev Standards CLI
|
|
@@ -2044,10 +2056,10 @@ All responses should be in **Traditional Chinese (繁體中文)**, with technica
|
|
|
2044
2056
|
所有回覆必須使用**繁體中文**,技術術語可保留英文。
|
|
2045
2057
|
|
|
2046
2058
|
## Core Standards Usage Rule / 核心標準使用規則
|
|
2047
|
-
> When verifying standards, checking code, or performing tasks,
|
|
2048
|
-
>
|
|
2049
|
-
>
|
|
2050
|
-
>
|
|
2059
|
+
> When verifying standards, checking code, or performing tasks, read the rules in \`.standards/\` — that is where this project's standards are installed.
|
|
2060
|
+
> 當驗證標準、檢查程式碼或執行任務時,讀取 \`.standards/\` 裡的規則——那是本專案標準實際安裝的位置。
|
|
2061
|
+
> Each file states its own requirements; read the one the task is about rather than all of them.
|
|
2062
|
+
> 每個檔案各自寫明它要求什麼;讀與當下任務相關的那一份,不必全部讀完。
|
|
2051
2063
|
> This ensures token efficiency and focused context.
|
|
2052
2064
|
> 這確保了 Token 效率和上下文聚焦。`
|
|
2053
2065
|
},
|
|
@@ -2062,8 +2074,8 @@ You are an expert software engineer assistant. Follow these project standards.
|
|
|
2062
2074
|
All responses should be in **English**.
|
|
2063
2075
|
|
|
2064
2076
|
## Core Standards Usage Rule
|
|
2065
|
-
> When verifying standards, checking code, or performing tasks,
|
|
2066
|
-
>
|
|
2077
|
+
> When verifying standards, checking code, or performing tasks, read the rules in \`.standards/\` — that is where this project's standards are installed.
|
|
2078
|
+
> Each file states its own requirements; read the one the task is about rather than all of them.
|
|
2067
2079
|
> This ensures token efficiency and focused context.`,
|
|
2068
2080
|
'zh-tw': `# Windsurf 規則
|
|
2069
2081
|
# 由 Universal Dev Standards CLI 生成
|
|
@@ -2075,8 +2087,8 @@ All responses should be in **English**.
|
|
|
2075
2087
|
所有回覆必須使用**繁體中文 (Traditional Chinese)**。
|
|
2076
2088
|
|
|
2077
2089
|
## 核心標準使用規則 / Core Standards Usage Rule
|
|
2078
|
-
>
|
|
2079
|
-
>
|
|
2090
|
+
> 當驗證標準、檢查程式碼或執行任務時,讀取 \`.standards/\` 裡的規則——那是本專案標準實際安裝的位置。
|
|
2091
|
+
> 每個檔案各自寫明它要求什麼;讀與當下任務相關的那一份,不必全部讀完。
|
|
2080
2092
|
> 這確保了 Token 效率和上下文聚焦。`,
|
|
2081
2093
|
bilingual: `# Windsurf Rules | Windsurf 規則
|
|
2082
2094
|
# Generated by Universal Dev Standards CLI
|
|
@@ -2091,10 +2103,10 @@ All responses should be in **Traditional Chinese (繁體中文)**, with technica
|
|
|
2091
2103
|
所有回覆必須使用**繁體中文**,技術術語可保留英文。
|
|
2092
2104
|
|
|
2093
2105
|
## Core Standards Usage Rule / 核心標準使用規則
|
|
2094
|
-
> When verifying standards, checking code, or performing tasks,
|
|
2095
|
-
>
|
|
2096
|
-
>
|
|
2097
|
-
>
|
|
2106
|
+
> When verifying standards, checking code, or performing tasks, read the rules in \`.standards/\` — that is where this project's standards are installed.
|
|
2107
|
+
> 當驗證標準、檢查程式碼或執行任務時,讀取 \`.standards/\` 裡的規則——那是本專案標準實際安裝的位置。
|
|
2108
|
+
> Each file states its own requirements; read the one the task is about rather than all of them.
|
|
2109
|
+
> 每個檔案各自寫明它要求什麼;讀與當下任務相關的那一份,不必全部讀完。
|
|
2098
2110
|
> This ensures token efficiency and focused context.
|
|
2099
2111
|
> 這確保了 Token 效率和上下文聚焦。`
|
|
2100
2112
|
},
|
|
@@ -2109,8 +2121,8 @@ You are an expert software engineer assistant. Follow these project standards.
|
|
|
2109
2121
|
All responses should be in **English**.
|
|
2110
2122
|
|
|
2111
2123
|
## Core Standards Usage Rule
|
|
2112
|
-
> When verifying standards, checking code, or performing tasks,
|
|
2113
|
-
>
|
|
2124
|
+
> When verifying standards, checking code, or performing tasks, read the rules in \`.standards/\` — that is where this project's standards are installed.
|
|
2125
|
+
> Each file states its own requirements; read the one the task is about rather than all of them.
|
|
2114
2126
|
> This ensures token efficiency and focused context.`,
|
|
2115
2127
|
'zh-tw': `# Cline 規則
|
|
2116
2128
|
# 由 Universal Dev Standards CLI 生成
|
|
@@ -2122,8 +2134,8 @@ All responses should be in **English**.
|
|
|
2122
2134
|
所有回覆必須使用**繁體中文 (Traditional Chinese)**。
|
|
2123
2135
|
|
|
2124
2136
|
## 核心標準使用規則 / Core Standards Usage Rule
|
|
2125
|
-
>
|
|
2126
|
-
>
|
|
2137
|
+
> 當驗證標準、檢查程式碼或執行任務時,讀取 \`.standards/\` 裡的規則——那是本專案標準實際安裝的位置。
|
|
2138
|
+
> 每個檔案各自寫明它要求什麼;讀與當下任務相關的那一份,不必全部讀完。
|
|
2127
2139
|
> 這確保了 Token 效率和上下文聚焦。`,
|
|
2128
2140
|
bilingual: `# Cline Rules | Cline 規則
|
|
2129
2141
|
# Generated by Universal Dev Standards CLI
|
|
@@ -2138,10 +2150,10 @@ All responses should be in **Traditional Chinese (繁體中文)**, with technica
|
|
|
2138
2150
|
所有回覆必須使用**繁體中文**,技術術語可保留英文。
|
|
2139
2151
|
|
|
2140
2152
|
## Core Standards Usage Rule / 核心標準使用規則
|
|
2141
|
-
> When verifying standards, checking code, or performing tasks,
|
|
2142
|
-
>
|
|
2143
|
-
>
|
|
2144
|
-
>
|
|
2153
|
+
> When verifying standards, checking code, or performing tasks, read the rules in \`.standards/\` — that is where this project's standards are installed.
|
|
2154
|
+
> 當驗證標準、檢查程式碼或執行任務時,讀取 \`.standards/\` 裡的規則——那是本專案標準實際安裝的位置。
|
|
2155
|
+
> Each file states its own requirements; read the one the task is about rather than all of them.
|
|
2156
|
+
> 每個檔案各自寫明它要求什麼;讀與當下任務相關的那一份,不必全部讀完。
|
|
2145
2157
|
> This ensures token efficiency and focused context.
|
|
2146
2158
|
> 這確保了 Token 效率和上下文聚焦。`
|
|
2147
2159
|
},
|
|
@@ -2156,8 +2168,8 @@ You are an expert software engineer assistant. Follow these project standards.
|
|
|
2156
2168
|
All responses should be in **English**.
|
|
2157
2169
|
|
|
2158
2170
|
## Core Standards Usage Rule
|
|
2159
|
-
> When verifying standards, checking code, or performing tasks,
|
|
2160
|
-
>
|
|
2171
|
+
> When verifying standards, checking code, or performing tasks, read the rules in \`.standards/\` — that is where this project's standards are installed.
|
|
2172
|
+
> Each file states its own requirements; read the one the task is about rather than all of them.
|
|
2161
2173
|
> This ensures token efficiency and focused context.`,
|
|
2162
2174
|
'zh-tw': `# GitHub Copilot 說明
|
|
2163
2175
|
# 由 Universal Dev Standards CLI 生成
|
|
@@ -2169,8 +2181,8 @@ All responses should be in **English**.
|
|
|
2169
2181
|
所有回覆必須使用**繁體中文 (Traditional Chinese)**。
|
|
2170
2182
|
|
|
2171
2183
|
## 核心標準使用規則 / Core Standards Usage Rule
|
|
2172
|
-
>
|
|
2173
|
-
>
|
|
2184
|
+
> 當驗證標準、檢查程式碼或執行任務時,讀取 \`.standards/\` 裡的規則——那是本專案標準實際安裝的位置。
|
|
2185
|
+
> 每個檔案各自寫明它要求什麼;讀與當下任務相關的那一份,不必全部讀完。
|
|
2174
2186
|
> 這確保了 Token 效率和上下文聚焦。`,
|
|
2175
2187
|
bilingual: `# GitHub Copilot Instructions | GitHub Copilot 說明
|
|
2176
2188
|
# Generated by Universal Dev Standards CLI
|
|
@@ -2185,10 +2197,10 @@ All responses should be in **Traditional Chinese (繁體中文)**, with technica
|
|
|
2185
2197
|
所有回覆必須使用**繁體中文**,技術術語可保留英文。
|
|
2186
2198
|
|
|
2187
2199
|
## Core Standards Usage Rule / 核心標準使用規則
|
|
2188
|
-
> When verifying standards, checking code, or performing tasks,
|
|
2189
|
-
>
|
|
2190
|
-
>
|
|
2191
|
-
>
|
|
2200
|
+
> When verifying standards, checking code, or performing tasks, read the rules in \`.standards/\` — that is where this project's standards are installed.
|
|
2201
|
+
> 當驗證標準、檢查程式碼或執行任務時,讀取 \`.standards/\` 裡的規則——那是本專案標準實際安裝的位置。
|
|
2202
|
+
> Each file states its own requirements; read the one the task is about rather than all of them.
|
|
2203
|
+
> 每個檔案各自寫明它要求什麼;讀與當下任務相關的那一份,不必全部讀完。
|
|
2192
2204
|
> This ensures token efficiency and focused context.
|
|
2193
2205
|
> 這確保了 Token 效率和上下文聚焦。`
|
|
2194
2206
|
},
|
|
@@ -2204,8 +2216,8 @@ Follow these project standards.
|
|
|
2204
2216
|
All responses should be in **English**.
|
|
2205
2217
|
|
|
2206
2218
|
## Core Standards Usage Rule
|
|
2207
|
-
> When verifying standards, checking code, or performing tasks,
|
|
2208
|
-
>
|
|
2219
|
+
> When verifying standards, checking code, or performing tasks, read the rules in \`.standards/\` — that is where this project's standards are installed.
|
|
2220
|
+
> Each file states its own requirements; read the one the task is about rather than all of them.
|
|
2209
2221
|
> This ensures token efficiency and focused context.`,
|
|
2210
2222
|
'zh-tw': `# Antigravity 系統指令
|
|
2211
2223
|
# 由 Universal Dev Standards CLI 生成
|
|
@@ -2218,8 +2230,8 @@ Google Antigravity (Gemini Advanced Agent) 的推薦系統指令。
|
|
|
2218
2230
|
所有回覆必須使用**繁體中文 (Traditional Chinese)**。
|
|
2219
2231
|
|
|
2220
2232
|
## 核心標準使用規則 / Core Standards Usage Rule
|
|
2221
|
-
>
|
|
2222
|
-
>
|
|
2233
|
+
> 當驗證標準、檢查程式碼或執行任務時,讀取 \`.standards/\` 裡的規則——那是本專案標準實際安裝的位置。
|
|
2234
|
+
> 每個檔案各自寫明它要求什麼;讀與當下任務相關的那一份,不必全部讀完。
|
|
2223
2235
|
> 這確保了 Token 效率和上下文聚焦。`,
|
|
2224
2236
|
bilingual: `# Antigravity System Instructions | Antigravity 系統指令
|
|
2225
2237
|
# Generated by Universal Dev Standards CLI
|
|
@@ -2234,10 +2246,10 @@ All responses should be in **Traditional Chinese (繁體中文)**, with technica
|
|
|
2234
2246
|
所有回覆必須使用**繁體中文**,技術術語可保留英文。
|
|
2235
2247
|
|
|
2236
2248
|
## Core Standards Usage Rule / 核心標準使用規則
|
|
2237
|
-
> When verifying standards, checking code, or performing tasks,
|
|
2238
|
-
>
|
|
2239
|
-
>
|
|
2240
|
-
>
|
|
2249
|
+
> When verifying standards, checking code, or performing tasks, read the rules in \`.standards/\` — that is where this project's standards are installed.
|
|
2250
|
+
> 當驗證標準、檢查程式碼或執行任務時,讀取 \`.standards/\` 裡的規則——那是本專案標準實際安裝的位置。
|
|
2251
|
+
> Each file states its own requirements; read the one the task is about rather than all of them.
|
|
2252
|
+
> 每個檔案各自寫明它要求什麼;讀與當下任務相關的那一份,不必全部讀完。
|
|
2241
2253
|
> This ensures token efficiency and focused context.
|
|
2242
2254
|
> 這確保了 Token 效率和上下文聚焦。`
|
|
2243
2255
|
},
|
|
@@ -2250,8 +2262,8 @@ All responses should be in **Traditional Chinese (繁體中文)**, with technica
|
|
|
2250
2262
|
All responses should be in **English**.
|
|
2251
2263
|
|
|
2252
2264
|
## Core Standards Usage Rule
|
|
2253
|
-
> When verifying standards, checking code, or performing tasks,
|
|
2254
|
-
>
|
|
2265
|
+
> When verifying standards, checking code, or performing tasks, read the rules in \`.standards/\` — that is where this project's standards are installed.
|
|
2266
|
+
> Each file states its own requirements; read the one the task is about rather than all of them.
|
|
2255
2267
|
> This ensures token efficiency and focused context.`,
|
|
2256
2268
|
'zh-tw': `# Claude Code 專案指南
|
|
2257
2269
|
# 由 Universal Dev Standards CLI 生成
|
|
@@ -2262,8 +2274,8 @@ All responses should be in **English**.
|
|
|
2262
2274
|
AI 助手應以繁體中文回覆使用者的問題與請求。
|
|
2263
2275
|
|
|
2264
2276
|
## 核心標準使用規則 / Core Standards Usage Rule
|
|
2265
|
-
>
|
|
2266
|
-
>
|
|
2277
|
+
> 當驗證標準、檢查程式碼或執行任務時,讀取 \`.standards/\` 裡的規則——那是本專案標準實際安裝的位置。
|
|
2278
|
+
> 每個檔案各自寫明它要求什麼;讀與當下任務相關的那一份,不必全部讀完。
|
|
2267
2279
|
> 這確保了 Token 效率和上下文聚焦。`,
|
|
2268
2280
|
bilingual: `# Project Guidelines for Claude Code | Claude Code 專案指南
|
|
2269
2281
|
# Generated by Universal Dev Standards CLI
|
|
@@ -2276,10 +2288,10 @@ All responses should be in **Traditional Chinese (繁體中文)**, with technica
|
|
|
2276
2288
|
AI 助手應以繁體中文回覆使用者的問題與請求。
|
|
2277
2289
|
|
|
2278
2290
|
## Core Standards Usage Rule / 核心標準使用規則
|
|
2279
|
-
> When verifying standards, checking code, or performing tasks,
|
|
2280
|
-
>
|
|
2281
|
-
>
|
|
2282
|
-
>
|
|
2291
|
+
> When verifying standards, checking code, or performing tasks, read the rules in \`.standards/\` — that is where this project's standards are installed.
|
|
2292
|
+
> 當驗證標準、檢查程式碼或執行任務時,讀取 \`.standards/\` 裡的規則——那是本專案標準實際安裝的位置。
|
|
2293
|
+
> Each file states its own requirements; read the one the task is about rather than all of them.
|
|
2294
|
+
> 每個檔案各自寫明它要求什麼;讀與當下任務相關的那一份,不必全部讀完。
|
|
2283
2295
|
> This ensures token efficiency and focused context.
|
|
2284
2296
|
> 這確保了 Token 效率和上下文聚焦。`
|
|
2285
2297
|
},
|
|
@@ -2294,8 +2306,8 @@ You are an expert software engineer assistant. Follow these project standards.
|
|
|
2294
2306
|
All responses should be in **English**.
|
|
2295
2307
|
|
|
2296
2308
|
## Core Standards Usage Rule
|
|
2297
|
-
> When verifying standards, checking code, or performing tasks,
|
|
2298
|
-
>
|
|
2309
|
+
> When verifying standards, checking code, or performing tasks, read the rules in \`.standards/\` — that is where this project's standards are installed.
|
|
2310
|
+
> Each file states its own requirements; read the one the task is about rather than all of them.
|
|
2299
2311
|
> This ensures token efficiency and focused context.`,
|
|
2300
2312
|
'zh-tw': `# AGENTS.md - OpenAI Codex CLI 規則
|
|
2301
2313
|
# 由 Universal Dev Standards CLI 生成
|
|
@@ -2307,8 +2319,8 @@ All responses should be in **English**.
|
|
|
2307
2319
|
所有回覆必須使用**繁體中文 (Traditional Chinese)**。
|
|
2308
2320
|
|
|
2309
2321
|
## 核心標準使用規則 / Core Standards Usage Rule
|
|
2310
|
-
>
|
|
2311
|
-
>
|
|
2322
|
+
> 當驗證標準、檢查程式碼或執行任務時,讀取 \`.standards/\` 裡的規則——那是本專案標準實際安裝的位置。
|
|
2323
|
+
> 每個檔案各自寫明它要求什麼;讀與當下任務相關的那一份,不必全部讀完。
|
|
2312
2324
|
> 這確保了 Token 效率和上下文聚焦。`,
|
|
2313
2325
|
bilingual: `# AGENTS.md - OpenAI Codex CLI Rules | OpenAI Codex CLI 規則
|
|
2314
2326
|
# Generated by Universal Dev Standards CLI
|
|
@@ -2323,10 +2335,10 @@ All responses should be in **Traditional Chinese (繁體中文)**, with technica
|
|
|
2323
2335
|
所有回覆必須使用**繁體中文**,技術術語可保留英文。
|
|
2324
2336
|
|
|
2325
2337
|
## Core Standards Usage Rule / 核心標準使用規則
|
|
2326
|
-
> When verifying standards, checking code, or performing tasks,
|
|
2327
|
-
>
|
|
2328
|
-
>
|
|
2329
|
-
>
|
|
2338
|
+
> When verifying standards, checking code, or performing tasks, read the rules in \`.standards/\` — that is where this project's standards are installed.
|
|
2339
|
+
> 當驗證標準、檢查程式碼或執行任務時,讀取 \`.standards/\` 裡的規則——那是本專案標準實際安裝的位置。
|
|
2340
|
+
> Each file states its own requirements; read the one the task is about rather than all of them.
|
|
2341
|
+
> 每個檔案各自寫明它要求什麼;讀與當下任務相關的那一份,不必全部讀完。
|
|
2330
2342
|
> This ensures token efficiency and focused context.
|
|
2331
2343
|
> 這確保了 Token 效率和上下文聚焦。`
|
|
2332
2344
|
},
|
|
@@ -2341,8 +2353,8 @@ You are an expert software engineer assistant powered by Gemini. Follow these pr
|
|
|
2341
2353
|
All responses should be in **English**.
|
|
2342
2354
|
|
|
2343
2355
|
## Core Standards Usage Rule
|
|
2344
|
-
> When verifying standards, checking code, or performing tasks,
|
|
2345
|
-
>
|
|
2356
|
+
> When verifying standards, checking code, or performing tasks, read the rules in \`.standards/\` — that is where this project's standards are installed.
|
|
2357
|
+
> Each file states its own requirements; read the one the task is about rather than all of them.
|
|
2346
2358
|
> This ensures token efficiency and focused context.`,
|
|
2347
2359
|
'zh-tw': `# GEMINI.md - Gemini CLI 規則
|
|
2348
2360
|
# 由 Universal Dev Standards CLI 生成
|
|
@@ -2354,8 +2366,8 @@ All responses should be in **English**.
|
|
|
2354
2366
|
所有回覆必須使用**繁體中文 (Traditional Chinese)**。
|
|
2355
2367
|
|
|
2356
2368
|
## 核心標準使用規則 / Core Standards Usage Rule
|
|
2357
|
-
>
|
|
2358
|
-
>
|
|
2369
|
+
> 當驗證標準、檢查程式碼或執行任務時,讀取 \`.standards/\` 裡的規則——那是本專案標準實際安裝的位置。
|
|
2370
|
+
> 每個檔案各自寫明它要求什麼;讀與當下任務相關的那一份,不必全部讀完。
|
|
2359
2371
|
> 這確保了 Token 效率和上下文聚焦。`,
|
|
2360
2372
|
bilingual: `# GEMINI.md - Gemini CLI Rules | Gemini CLI 規則
|
|
2361
2373
|
# Generated by Universal Dev Standards CLI
|
|
@@ -2370,10 +2382,10 @@ All responses should be in **Traditional Chinese (繁體中文)**, with technica
|
|
|
2370
2382
|
所有回覆必須使用**繁體中文**,技術術語可保留英文。
|
|
2371
2383
|
|
|
2372
2384
|
## Core Standards Usage Rule / 核心標準使用規則
|
|
2373
|
-
> When verifying standards, checking code, or performing tasks,
|
|
2374
|
-
>
|
|
2375
|
-
>
|
|
2376
|
-
>
|
|
2385
|
+
> When verifying standards, checking code, or performing tasks, read the rules in \`.standards/\` — that is where this project's standards are installed.
|
|
2386
|
+
> 當驗證標準、檢查程式碼或執行任務時,讀取 \`.standards/\` 裡的規則——那是本專案標準實際安裝的位置。
|
|
2387
|
+
> Each file states its own requirements; read the one the task is about rather than all of them.
|
|
2388
|
+
> 每個檔案各自寫明它要求什麼;讀與當下任務相關的那一份,不必全部讀完。
|
|
2377
2389
|
> This ensures token efficiency and focused context.
|
|
2378
2390
|
> 這確保了 Token 效率和上下文聚焦。`
|
|
2379
2391
|
},
|
|
@@ -2388,8 +2400,8 @@ You are an expert software engineer assistant. Follow these project standards.
|
|
|
2388
2400
|
All responses should be in **English**.
|
|
2389
2401
|
|
|
2390
2402
|
## Core Standards Usage Rule
|
|
2391
|
-
> When verifying standards, checking code, or performing tasks,
|
|
2392
|
-
>
|
|
2403
|
+
> When verifying standards, checking code, or performing tasks, read the rules in \`.standards/\` — that is where this project's standards are installed.
|
|
2404
|
+
> Each file states its own requirements; read the one the task is about rather than all of them.
|
|
2393
2405
|
> This ensures token efficiency and focused context.`,
|
|
2394
2406
|
'zh-tw': `# AGENTS.md - OpenCode 規則
|
|
2395
2407
|
# 由 Universal Dev Standards CLI 生成
|
|
@@ -2401,8 +2413,8 @@ All responses should be in **English**.
|
|
|
2401
2413
|
所有回覆必須使用**繁體中文 (Traditional Chinese)**。
|
|
2402
2414
|
|
|
2403
2415
|
## 核心標準使用規則 / Core Standards Usage Rule
|
|
2404
|
-
>
|
|
2405
|
-
>
|
|
2416
|
+
> 當驗證標準、檢查程式碼或執行任務時,讀取 \`.standards/\` 裡的規則——那是本專案標準實際安裝的位置。
|
|
2417
|
+
> 每個檔案各自寫明它要求什麼;讀與當下任務相關的那一份,不必全部讀完。
|
|
2406
2418
|
> 這確保了 Token 效率和上下文聚焦。`,
|
|
2407
2419
|
bilingual: `# AGENTS.md - OpenCode Rules | OpenCode 規則
|
|
2408
2420
|
# Generated by Universal Dev Standards CLI
|
|
@@ -2417,10 +2429,10 @@ All responses should be in **Traditional Chinese (繁體中文)**, with technica
|
|
|
2417
2429
|
所有回覆必須使用**繁體中文**,技術術語可保留英文。
|
|
2418
2430
|
|
|
2419
2431
|
## Core Standards Usage Rule / 核心標準使用規則
|
|
2420
|
-
> When verifying standards, checking code, or performing tasks,
|
|
2421
|
-
>
|
|
2422
|
-
>
|
|
2423
|
-
>
|
|
2432
|
+
> When verifying standards, checking code, or performing tasks, read the rules in \`.standards/\` — that is where this project's standards are installed.
|
|
2433
|
+
> 當驗證標準、檢查程式碼或執行任務時,讀取 \`.standards/\` 裡的規則——那是本專案標準實際安裝的位置。
|
|
2434
|
+
> Each file states its own requirements; read the one the task is about rather than all of them.
|
|
2435
|
+
> 每個檔案各自寫明它要求什麼;讀與當下任務相關的那一份,不必全部讀完。
|
|
2424
2436
|
> This ensures token efficiency and focused context.
|
|
2425
2437
|
> 這確保了 Token 效率和上下文聚焦。`
|
|
2426
2438
|
}
|
|
@@ -2736,6 +2748,237 @@ function generateWorkflowGateContent(language) {
|
|
|
2736
2748
|
* @param {Object} config - Integration configuration
|
|
2737
2749
|
* @returns {string} Generated content
|
|
2738
2750
|
*/
|
|
2751
|
+
/**
|
|
2752
|
+
* Strip a standard's extension to get its bare identifying stem, e.g.
|
|
2753
|
+
* 'commit-message.ai.yaml' -> 'commit-message'. Shared by
|
|
2754
|
+
* resolveStandardReferences and getHeadingToPrimaryStandardStem below.
|
|
2755
|
+
*/
|
|
2756
|
+
function stemOf(value) {
|
|
2757
|
+
return basename(String(value)).replace(/\.(ai\.yaml|yaml|md)$/i, '');
|
|
2758
|
+
}
|
|
2759
|
+
|
|
2760
|
+
/**
|
|
2761
|
+
* Normalize a Markdown ATX heading line for comparison, e.g.
|
|
2762
|
+
* '## 提交訊息標準 ' -> '提交訊息標準'. Returns null for a non-heading line.
|
|
2763
|
+
*/
|
|
2764
|
+
function normalizeHeadingLine(line) {
|
|
2765
|
+
const m = String(line).match(/^#{1,6}\s+(.+?)\s*$/);
|
|
2766
|
+
return m ? m[1].trim() : null;
|
|
2767
|
+
}
|
|
2768
|
+
|
|
2769
|
+
let _headingToPrimaryStandardStem = null;
|
|
2770
|
+
|
|
2771
|
+
/**
|
|
2772
|
+
* Map every RULE_TEMPLATES section heading (any category, detail level,
|
|
2773
|
+
* language that has a Reference:/參考: line) to the stem of the FIRST
|
|
2774
|
+
* `.standards/...` path on that line — that section's "primary standard".
|
|
2775
|
+
*
|
|
2776
|
+
* XSPEC adopter-report, narrow auto-repair (decision 1, 2026-09-18):
|
|
2777
|
+
* 6.10.0's resolveStandardReferences deleted `.standards/commit-message-guide.md`
|
|
2778
|
+
* from an existing file's "## 提交訊息標準" section — it looked exactly like a
|
|
2779
|
+
* retired, uninstalled UDS standard — leaving only the trailing options
|
|
2780
|
+
* reference behind. Rewriting away the resulting dangling comma (the earlier
|
|
2781
|
+
* fix, same XSPEC) does not bring the deleted item back: it is gone from the
|
|
2782
|
+
* file, and nothing else regenerates that section, because rule-template
|
|
2783
|
+
* sections live OUTSIDE the UDS markers by design.
|
|
2784
|
+
*
|
|
2785
|
+
* This map is how resolveStandardReferences recognizes "this heading belongs
|
|
2786
|
+
* to a UDS rule template, and here is the one reference it must never be
|
|
2787
|
+
* missing" — so it can restore ONLY that single item, ONLY when it is
|
|
2788
|
+
* genuinely installed and missing from the line. A heading that does not
|
|
2789
|
+
* match any template (a user's own section) is simply absent from this map,
|
|
2790
|
+
* so it is never touched.
|
|
2791
|
+
*
|
|
2792
|
+
* Computed once and cached — RULE_TEMPLATES is a static module constant.
|
|
2793
|
+
*
|
|
2794
|
+
* @returns {Map<string, string>} heading text (without leading '#'s) -> primary standard stem
|
|
2795
|
+
*/
|
|
2796
|
+
function getHeadingToPrimaryStandardStem() {
|
|
2797
|
+
if (_headingToPrimaryStandardStem) return _headingToPrimaryStandardStem;
|
|
2798
|
+
|
|
2799
|
+
const map = new Map();
|
|
2800
|
+
const refLineRe = /^(?:Reference|參考|参考)[::]([^\n]*)$/im;
|
|
2801
|
+
|
|
2802
|
+
for (const categoryTemplates of Object.values(RULE_TEMPLATES)) {
|
|
2803
|
+
for (const modeTemplates of Object.values(categoryTemplates)) {
|
|
2804
|
+
for (const text of Object.values(modeTemplates)) {
|
|
2805
|
+
const heading = normalizeHeadingLine(String(text).split('\n')[0]);
|
|
2806
|
+
if (!heading) continue;
|
|
2807
|
+
|
|
2808
|
+
// 'minimal' mode has no Reference: line at all — nothing to repair,
|
|
2809
|
+
// and no primary standard to record for that heading.
|
|
2810
|
+
const refMatch = text.match(refLineRe);
|
|
2811
|
+
if (!refMatch) continue;
|
|
2812
|
+
|
|
2813
|
+
const firstItem = refMatch[1].split(',')[0].trim();
|
|
2814
|
+
const pathMatch = firstItem.match(/^\.standards\/([^\s,`)\]]+)$/);
|
|
2815
|
+
if (!pathMatch) continue;
|
|
2816
|
+
|
|
2817
|
+
map.set(heading, stemOf(pathMatch[1]));
|
|
2818
|
+
}
|
|
2819
|
+
}
|
|
2820
|
+
}
|
|
2821
|
+
|
|
2822
|
+
_headingToPrimaryStandardStem = map;
|
|
2823
|
+
return map;
|
|
2824
|
+
}
|
|
2825
|
+
|
|
2826
|
+
/**
|
|
2827
|
+
* Point every `.standards/<file>` reference at the file this project actually has.
|
|
2828
|
+
*
|
|
2829
|
+
* `RULE_TEMPLATES` spells its references as `.standards/<name>.md` because that
|
|
2830
|
+
* is what UDS shipped when they were written. A project installed with
|
|
2831
|
+
* `--format ai` holds `<name>.ai.yaml`, so those lines named files that are not
|
|
2832
|
+
* there — reproduced on 6.9.0 (2026-09-16): a clean `--format ai` project got
|
|
2833
|
+
* `.standards/anti-hallucination.md` and `.standards/checkin-standards.md`, and
|
|
2834
|
+
* `uds check` called the file in sync because it compared names with the
|
|
2835
|
+
* extension stripped and never asked whether the file existed.
|
|
2836
|
+
*
|
|
2837
|
+
* Rewrites by stem against the installed set; a reference whose standard this
|
|
2838
|
+
* project did not install is dropped rather than left dangling, and if that
|
|
2839
|
+
* empties the line, the line goes too.
|
|
2840
|
+
*
|
|
2841
|
+
* @param {string} content - Generated integration content
|
|
2842
|
+
* @param {string[]} installedStandards - Manifest standards (IDs or paths)
|
|
2843
|
+
* @param {string} standardsFormat - 'ai' | 'human'
|
|
2844
|
+
* @returns {string}
|
|
2845
|
+
*/
|
|
2846
|
+
function resolveStandardReferences(content, installedStandards = [], standardsFormat = 'ai', projectPath = null) {
|
|
2847
|
+
if (!content) return content;
|
|
2848
|
+
|
|
2849
|
+
// Every stem UDS itself ships — the only references this function may delete.
|
|
2850
|
+
//
|
|
2851
|
+
// Read defensively: this runs inside pure content generation, and callers
|
|
2852
|
+
// (including this repo's own tests) may have the registry or `fs` mocked. A
|
|
2853
|
+
// registry that cannot be read means "delete nothing", which degrades to
|
|
2854
|
+
// rewriting matched paths only — never to removing a line.
|
|
2855
|
+
const udsOwnedStems = new Set();
|
|
2856
|
+
try {
|
|
2857
|
+
for (const std of getAllStandards()) {
|
|
2858
|
+
if (std?.id) udsOwnedStems.add(stemOf(std.id));
|
|
2859
|
+
const sources = typeof std?.source === 'string'
|
|
2860
|
+
? [std.source]
|
|
2861
|
+
: [std?.source?.ai, std?.source?.human].filter(Boolean);
|
|
2862
|
+
for (const src of sources) udsOwnedStems.add(stemOf(src));
|
|
2863
|
+
}
|
|
2864
|
+
} catch {
|
|
2865
|
+
// Registry unavailable — keep every reference that does not match.
|
|
2866
|
+
}
|
|
2867
|
+
|
|
2868
|
+
const installedByStem = new Map();
|
|
2869
|
+
for (const entry of installedStandards) {
|
|
2870
|
+
const filename = resolveStandardFilename(entry, standardsFormat) || basename(String(entry));
|
|
2871
|
+
installedByStem.set(stemOf(entry), filename);
|
|
2872
|
+
installedByStem.set(stemOf(filename), filename);
|
|
2873
|
+
}
|
|
2874
|
+
|
|
2875
|
+
// XSPEC adopter-report Q2: a reference written against a pre-6.0.0
|
|
2876
|
+
// filename (`commit-message-guide.md`) must still resolve to whatever this
|
|
2877
|
+
// project installed under the CURRENT id (`commit-message`) — that rename
|
|
2878
|
+
// is exactly what STANDARD_ID_MAPPING already records, previously
|
|
2879
|
+
// consulted only by the human→AI YAML generator. Without it, the old name
|
|
2880
|
+
// was indistinguishable from a retired, UDS-owned standard the project
|
|
2881
|
+
// chose not to install, and got deleted instead of rewritten.
|
|
2882
|
+
const resolveActualFilename = (path) => {
|
|
2883
|
+
const stem = stemOf(path);
|
|
2884
|
+
return installedByStem.get(stem) || installedByStem.get(STANDARD_ID_MAPPING[stem] || stem) || null;
|
|
2885
|
+
};
|
|
2886
|
+
|
|
2887
|
+
const headingToPrimaryStem = getHeadingToPrimaryStandardStem();
|
|
2888
|
+
const headingLineRe = /^#{1,6}\s+/;
|
|
2889
|
+
const referenceLineRe = /^([^\n]*(?:Reference|參考|参考)[::])([^\n]*)$/i;
|
|
2890
|
+
|
|
2891
|
+
// XSPEC adopter-report Q2: rebuilt as a line-by-line split → filter → join
|
|
2892
|
+
// instead of a single global regex, so each "Reference:" line's PRECEDING
|
|
2893
|
+
// heading is known (needed for decision 1's narrow auto-repair below) —
|
|
2894
|
+
// and instead of patching a deleted substring's separators with more
|
|
2895
|
+
// regexes. The old single-regex approach deleted only the matched
|
|
2896
|
+
// `.standards/...` text in place and then tried to tidy up whatever
|
|
2897
|
+
// punctuation was left around the hole; it handled a trailing comma, a
|
|
2898
|
+
// doubled comma, and a comma with stray space, but not a comma left
|
|
2899
|
+
// dangling right after the colon when the FIRST item was the one dropped
|
|
2900
|
+
// (`Reference:, .standards/other.md`).
|
|
2901
|
+
const lines = content.split('\n');
|
|
2902
|
+
let currentHeading = null;
|
|
2903
|
+
const outLines = [];
|
|
2904
|
+
|
|
2905
|
+
for (const rawLine of lines) {
|
|
2906
|
+
if (headingLineRe.test(rawLine)) {
|
|
2907
|
+
currentHeading = normalizeHeadingLine(rawLine);
|
|
2908
|
+
outLines.push(rawLine);
|
|
2909
|
+
continue;
|
|
2910
|
+
}
|
|
2911
|
+
|
|
2912
|
+
const refMatch = rawLine.match(referenceLineRe);
|
|
2913
|
+
if (!refMatch) {
|
|
2914
|
+
outLines.push(rawLine);
|
|
2915
|
+
continue;
|
|
2916
|
+
}
|
|
2917
|
+
|
|
2918
|
+
const [, label, rest] = refMatch;
|
|
2919
|
+
const items = rest.split(',').map((s) => s.trim()).filter(Boolean);
|
|
2920
|
+
const kept = [];
|
|
2921
|
+
const presentStems = new Set();
|
|
2922
|
+
|
|
2923
|
+
for (const item of items) {
|
|
2924
|
+
const pathMatch = item.match(/^(\.standards\/)([^\s,`)\]]+)$/);
|
|
2925
|
+
if (!pathMatch) {
|
|
2926
|
+
// Not a bare `.standards/...` reference (e.g. trailing prose) — leave untouched.
|
|
2927
|
+
kept.push(item);
|
|
2928
|
+
continue;
|
|
2929
|
+
}
|
|
2930
|
+
const [, prefix, path] = pathMatch;
|
|
2931
|
+
|
|
2932
|
+
// Option files live in `.standards/options/…` and are named by path, not ID.
|
|
2933
|
+
if (path.startsWith('options/')) { kept.push(item); presentStems.add(stemOf(path)); continue; }
|
|
2934
|
+
|
|
2935
|
+
const actual = resolveActualFilename(path);
|
|
2936
|
+
if (actual) { kept.push(`${prefix}${actual}`); presentStems.add(stemOf(actual)); continue; }
|
|
2937
|
+
|
|
2938
|
+
// 🔴 Only UDS's own standards may be dropped. The first version of this
|
|
2939
|
+
// rewrite removed every unmatched reference, which deleted a project's own
|
|
2940
|
+
// `.standards/our-house-rule.md` line — a file it had put there on purpose.
|
|
2941
|
+
// Removing a user's line is worse than the dead link this exists to fix.
|
|
2942
|
+
let onDisk = false;
|
|
2943
|
+
try {
|
|
2944
|
+
onDisk = Boolean(projectPath) && existsSync(join(projectPath, '.standards', path));
|
|
2945
|
+
} catch {
|
|
2946
|
+
onDisk = false;
|
|
2947
|
+
}
|
|
2948
|
+
if (onDisk || !udsOwnedStems.has(stemOf(path))) {
|
|
2949
|
+
kept.push(item);
|
|
2950
|
+
presentStems.add(stemOf(path));
|
|
2951
|
+
continue;
|
|
2952
|
+
}
|
|
2953
|
+
// Dropped: a UDS-owned reference to a standard this project did not install.
|
|
2954
|
+
}
|
|
2955
|
+
|
|
2956
|
+
// XSPEC adopter-report, narrow auto-repair (decision 1): this line's
|
|
2957
|
+
// heading matches a UDS rule template EXACTLY (see
|
|
2958
|
+
// getHeadingToPrimaryStandardStem's docblock) and that template's
|
|
2959
|
+
// primary standard is genuinely installed but missing from the line —
|
|
2960
|
+
// put it back at the front. Idempotent: a line that already has it does
|
|
2961
|
+
// nothing. Everything else about the line — the project's own entries,
|
|
2962
|
+
// options files, existing order — is untouched, and no OTHER (secondary)
|
|
2963
|
+
// template item is ever added back.
|
|
2964
|
+
const primaryStem = currentHeading ? headingToPrimaryStem.get(currentHeading) : null;
|
|
2965
|
+
if (primaryStem && !presentStems.has(primaryStem)) {
|
|
2966
|
+
const primaryActual = installedByStem.get(primaryStem);
|
|
2967
|
+
if (primaryActual) {
|
|
2968
|
+
kept.unshift(`.standards/${primaryActual}`);
|
|
2969
|
+
}
|
|
2970
|
+
}
|
|
2971
|
+
|
|
2972
|
+
if (kept.length === 0) {
|
|
2973
|
+
outLines.push('');
|
|
2974
|
+
continue;
|
|
2975
|
+
}
|
|
2976
|
+
outLines.push(`${label} ${kept.join(', ')}`);
|
|
2977
|
+
}
|
|
2978
|
+
|
|
2979
|
+
return outLines.join('\n').replace(/\n{3,}/g, '\n\n');
|
|
2980
|
+
}
|
|
2981
|
+
|
|
2739
2982
|
export function generateIntegrationContent(config) {
|
|
2740
2983
|
const {
|
|
2741
2984
|
tool,
|
|
@@ -2890,7 +3133,8 @@ export function generateIntegrationContent(config) {
|
|
|
2890
3133
|
sections.push('\n');
|
|
2891
3134
|
}
|
|
2892
3135
|
|
|
2893
|
-
|
|
3136
|
+
const assembled = sections.join('\n').trim() + '\n';
|
|
3137
|
+
return resolveStandardReferences(assembled, installedStandards, standardsFormat, config.projectPath || null);
|
|
2894
3138
|
}
|
|
2895
3139
|
|
|
2896
3140
|
/**
|
|
@@ -2939,7 +3183,10 @@ export function mergeRules(existingContent, newContent, strategy) {
|
|
|
2939
3183
|
* @returns {Object} Result with success status
|
|
2940
3184
|
*/
|
|
2941
3185
|
export function writeIntegrationFile(tool, config, projectPath) {
|
|
2942
|
-
|
|
3186
|
+
// XSPEC-418 R2/R3: config.integrationTargets carries manifest.integrationTargets
|
|
3187
|
+
// (or its init-time equivalent) through unchanged — this is the actual write path,
|
|
3188
|
+
// so it must be the one place that honors a per-tool target override.
|
|
3189
|
+
const fileName = resolveIntegrationTargetFile(tool, { integrationTargets: config.integrationTargets });
|
|
2943
3190
|
if (!fileName) {
|
|
2944
3191
|
return { success: false, error: `Unknown tool: ${tool}` };
|
|
2945
3192
|
}
|
|
@@ -2973,6 +3220,19 @@ export function writeIntegrationFile(tool, config, projectPath) {
|
|
|
2973
3220
|
}
|
|
2974
3221
|
}
|
|
2975
3222
|
|
|
3223
|
+
// 🔴 The rule sections live OUTSIDE the markers, so a marker-based update
|
|
3224
|
+
// never refreshes them: a file first written by an older CLI kept
|
|
3225
|
+
// `.standards/commit-message-guide.md` for ever, and a regenerate on the
|
|
3226
|
+
// fixed generator still left it (measured 2026-09-16). Repoint stale
|
|
3227
|
+
// `.standards/` paths across the whole file — it touches nothing but the
|
|
3228
|
+
// paths, and a path to a file that is not there helps no reader.
|
|
3229
|
+
content = resolveStandardReferences(
|
|
3230
|
+
content,
|
|
3231
|
+
config.installedStandards || [],
|
|
3232
|
+
config.standardsFormat || 'ai',
|
|
3233
|
+
projectPath
|
|
3234
|
+
);
|
|
3235
|
+
|
|
2976
3236
|
writeFileSync(filePath, content);
|
|
2977
3237
|
|
|
2978
3238
|
// Compute block hash for tracking UDS content separately from user content
|
|
@@ -2993,10 +3253,13 @@ export function writeIntegrationFile(tool, config, projectPath) {
|
|
|
2993
3253
|
* Check if integration file exists
|
|
2994
3254
|
* @param {string} tool - Tool name
|
|
2995
3255
|
* @param {string} projectPath - Project root path
|
|
3256
|
+
* @param {Object} [manifestLike] - Manifest-like object; only `integrationTargets` is
|
|
3257
|
+
* read (XSPEC-418 R2) — pass the real manifest, or `{ integrationTargets }` at
|
|
3258
|
+
* init time before a manifest object exists.
|
|
2996
3259
|
* @returns {boolean} True if file exists
|
|
2997
3260
|
*/
|
|
2998
|
-
export function integrationFileExists(tool, projectPath) {
|
|
2999
|
-
const fileName =
|
|
3261
|
+
export function integrationFileExists(tool, projectPath, manifestLike) {
|
|
3262
|
+
const fileName = resolveIntegrationTargetFile(tool, manifestLike);
|
|
3000
3263
|
return fileName && existsSync(join(projectPath, fileName));
|
|
3001
3264
|
}
|
|
3002
3265
|
|
|
@@ -3201,9 +3464,19 @@ function withSelectedOptions(manifest) {
|
|
|
3201
3464
|
|
|
3202
3465
|
export function buildToolIntegrationConfig(manifest, tool) {
|
|
3203
3466
|
const selected = manifest.options?.output_language || manifest.options?.commit_language || 'english';
|
|
3204
|
-
|
|
3467
|
+
|
|
3468
|
+
// Content language comes from `display_language` — the setting whose entire
|
|
3469
|
+
// job is "what language do you want to read" — and only falls back to
|
|
3470
|
+
// `output_language`, which is the commit-message language, when the project
|
|
3471
|
+
// has no display setting to go on. They are different questions: `uds init`
|
|
3472
|
+
// has always used the first, every regeneration path used the second, and a
|
|
3473
|
+
// project installed with `--locale zh-tw` therefore got Chinese instructions
|
|
3474
|
+
// from `init` and English ones from the next `uds update`, silently.
|
|
3475
|
+
const display = manifest.options?.display_language;
|
|
3476
|
+
const fromOutput = selected === 'bilingual'
|
|
3205
3477
|
? 'bilingual'
|
|
3206
3478
|
: selected === 'traditional-chinese' ? 'zh-tw' : 'en';
|
|
3479
|
+
const language = ['en', 'zh-tw', 'bilingual'].includes(display) ? display : fromOutput;
|
|
3207
3480
|
const resolved = resolveContentModeForTool(tool, manifest.contentMode || 'auto');
|
|
3208
3481
|
|
|
3209
3482
|
return {
|
|
@@ -3220,7 +3493,12 @@ export function buildToolIntegrationConfig(manifest, tool) {
|
|
|
3220
3493
|
contentMode: resolved.contentMode,
|
|
3221
3494
|
level: resolved.level,
|
|
3222
3495
|
outputLanguage: selected,
|
|
3223
|
-
methodology: manifest.methodology
|
|
3496
|
+
methodology: manifest.methodology,
|
|
3497
|
+
// XSPEC-418 R2/R3: threads the per-tool target override through to
|
|
3498
|
+
// writeIntegrationFile via resolveIntegrationTargetFile. Every caller of
|
|
3499
|
+
// this function (regenerateIntegrations, plan-executor's migrate_block,
|
|
3500
|
+
// backfillIntegrationConfigs) gets the override for free.
|
|
3501
|
+
integrationTargets: manifest.integrationTargets
|
|
3224
3502
|
};
|
|
3225
3503
|
}
|
|
3226
3504
|
|
|
@@ -3280,13 +3558,18 @@ export function wrapWithMarkers(content, format) {
|
|
|
3280
3558
|
*/
|
|
3281
3559
|
export function extractMarkedContent(fileContent, format) {
|
|
3282
3560
|
const markers = UDS_MARKERS[format] || UDS_MARKERS.markdown;
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3561
|
+
// XSPEC adopter-report Q5: locateMarkerBlock requires the marker to occupy
|
|
3562
|
+
// a whole line by itself (and not be inside a fenced code block), so a
|
|
3563
|
+
// sentence that merely mentions the marker text is never mistaken for the
|
|
3564
|
+
// real boundary. It throws AmbiguousMarkerError if more than one real pair
|
|
3565
|
+
// exists — callers must not catch that away silently.
|
|
3566
|
+
const block = locateMarkerBlock(fileContent, markers);
|
|
3567
|
+
|
|
3568
|
+
if (!block) {
|
|
3287
3569
|
return { before: fileContent, content: '', after: '' };
|
|
3288
3570
|
}
|
|
3289
3571
|
|
|
3572
|
+
const { startIdx, endIdx } = block;
|
|
3290
3573
|
return {
|
|
3291
3574
|
before: fileContent.substring(0, startIdx),
|
|
3292
3575
|
content: fileContent.substring(startIdx + markers.start.length, endIdx).trim(),
|
|
@@ -3303,15 +3586,18 @@ export function extractMarkedContent(fileContent, format) {
|
|
|
3303
3586
|
*/
|
|
3304
3587
|
export function updateMarkedSection(existingContent, newMarkedContent, format) {
|
|
3305
3588
|
const markers = UDS_MARKERS[format] || UDS_MARKERS.markdown;
|
|
3306
|
-
|
|
3307
|
-
|
|
3589
|
+
// XSPEC adopter-report Q5: same rule as extractMarkedContent — a line that
|
|
3590
|
+
// merely mentions the marker text is not a boundary, so it is never
|
|
3591
|
+
// deleted along with (what used to be mistaken for) "the UDS block".
|
|
3592
|
+
const block = locateMarkerBlock(existingContent, markers);
|
|
3308
3593
|
|
|
3309
|
-
if (
|
|
3594
|
+
if (!block) {
|
|
3310
3595
|
// No existing markers, append new content
|
|
3311
3596
|
return existingContent.trim() + '\n\n' + wrapWithMarkers(newMarkedContent, format) + '\n';
|
|
3312
3597
|
}
|
|
3313
3598
|
|
|
3314
3599
|
// Replace existing marked section
|
|
3600
|
+
const { startIdx, endIdx } = block;
|
|
3315
3601
|
const before = existingContent.substring(0, startIdx);
|
|
3316
3602
|
const after = existingContent.substring(endIdx + markers.end.length);
|
|
3317
3603
|
|
|
@@ -3327,6 +3613,45 @@ export function getToolFilePath(tool) {
|
|
|
3327
3613
|
return getToolFileName(tool) || null;
|
|
3328
3614
|
}
|
|
3329
3615
|
|
|
3616
|
+
/**
|
|
3617
|
+
* Tool × manifest → the integration file this tool actually reads/writes.
|
|
3618
|
+
*
|
|
3619
|
+
* XSPEC-418 R2: honors a per-tool override recorded in
|
|
3620
|
+
* `manifest.integrationTargets` (currently only ever set for `claude-code`, to
|
|
3621
|
+
* `CLAUDE.local.md` — a personal UDS adoption in a repo with a team-owned,
|
|
3622
|
+
* version-controlled `CLAUDE.md`). No override set → same file
|
|
3623
|
+
* `getToolFilePath` already returns, so every manifest that never sets
|
|
3624
|
+
* `integrationTargets` gets byte-identical output (XSPEC-418 AC-5).
|
|
3625
|
+
*
|
|
3626
|
+
* This is the single place "tool → target file" is decided once a manifest (or
|
|
3627
|
+
* a manifest-shaped config carrying `integrationTargets`) is in scope. Every
|
|
3628
|
+
* call site that used to call `getToolFilePath`/`getToolFileName` to answer
|
|
3629
|
+
* "what file do I read/write for this tool right now" must call this instead
|
|
3630
|
+
* — enforced by the static-scan guard in
|
|
3631
|
+
* tests/unit/core/integration-target-resolver-guard.test.js.
|
|
3632
|
+
*
|
|
3633
|
+
* Placed here rather than core/constants.js, where XSPEC-418 originally
|
|
3634
|
+
* suggested it next to `resolveIntegrationFile`: the default-file fallback
|
|
3635
|
+
* must be the `getToolFileName` above — the one with the legacy-mapping guard,
|
|
3636
|
+
* the already-a-filename passthrough, and the "known agent missing from
|
|
3637
|
+
* SUPPORTED_AI_TOOLS" throw (see its own comment) — not a re-implementation in
|
|
3638
|
+
* constants.js, which would silently drop that throw for every call site this
|
|
3639
|
+
* migrates. constants.js is imported BY this file already, so this file
|
|
3640
|
+
* cannot be imported back from constants.js without a cycle; it lives beside
|
|
3641
|
+
* the function it wraps instead.
|
|
3642
|
+
*
|
|
3643
|
+
* @param {string} tool - Tool key (e.g. 'claude-code'), or an entry already
|
|
3644
|
+
* resolved to a file path — passed straight to `getToolFileName`, which
|
|
3645
|
+
* already tolerates that shape (XSPEC-208 BUG-208-01).
|
|
3646
|
+
* @param {Object} [manifest] - Manifest-like object; only `integrationTargets` is read.
|
|
3647
|
+
* @returns {string|null} Repo-relative target file, or null for a genuinely unknown tool.
|
|
3648
|
+
*/
|
|
3649
|
+
export function resolveIntegrationTargetFile(tool, manifest) {
|
|
3650
|
+
const key = LEGACY_TOOL_MAPPINGS[tool] || tool;
|
|
3651
|
+
const override = manifest?.integrationTargets?.[key];
|
|
3652
|
+
if (typeof override === 'string' && override.length > 0) return override;
|
|
3653
|
+
return getToolFileName(tool) || null;
|
|
3654
|
+
}
|
|
3330
3655
|
|
|
3331
3656
|
/**
|
|
3332
3657
|
* Get default commands based on ecosystem
|
|
@@ -3431,6 +3756,7 @@ export function generateAgentsMdSummary(config = {}) {
|
|
|
3431
3756
|
language = 'en',
|
|
3432
3757
|
outputLanguage = 'english',
|
|
3433
3758
|
standardOptions = {},
|
|
3759
|
+
standardsFormat = 'ai',
|
|
3434
3760
|
projectPath
|
|
3435
3761
|
} = config;
|
|
3436
3762
|
|
|
@@ -3523,12 +3849,25 @@ export function generateAgentsMdSummary(config = {}) {
|
|
|
3523
3849
|
// only the option files. On one adopter that meant seven lines standing in
|
|
3524
3850
|
// for seventy, under a heading reading "Installed Standards", with nothing
|
|
3525
3851
|
// anywhere reporting the sixty-three that had been dropped.
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3852
|
+
// Two things this used to get wrong, both of which produced a list of paths
|
|
3853
|
+
// that are not there — under a heading telling the agent to read them.
|
|
3854
|
+
//
|
|
3855
|
+
// Options install one directory deeper (`.standards/options/…`), and this
|
|
3856
|
+
// wrote every entry at the top level. The sibling renderer in this file has
|
|
3857
|
+
// always handled that; the docblock above it says a previous fix reached
|
|
3858
|
+
// "one of the two producers", and this is the other one.
|
|
3859
|
+
//
|
|
3860
|
+
// And the format was pinned to `ai`, so a `--format human` project was
|
|
3861
|
+
// handed `.ai.yaml` names for files it holds as `.md`.
|
|
3862
|
+
const format = standardsFormat === 'human' ? 'human' : 'ai';
|
|
3863
|
+
const suffix = format === 'human' ? '.md' : '.ai.yaml';
|
|
3864
|
+
|
|
3865
|
+
for (const entry of installedStandards) {
|
|
3866
|
+
const filename = resolveStandardFilename(entry, format);
|
|
3867
|
+
if (!filename?.endsWith(suffix)) continue;
|
|
3868
|
+
const isOption = entry.includes('/options/') || entry.includes('\\options\\');
|
|
3869
|
+
const dir = isOption ? '.standards/options' : '.standards';
|
|
3870
|
+
lines.push(`- \`${dir}/${filename}\` — ${filename.replace(suffix, '')}`);
|
|
3532
3871
|
}
|
|
3533
3872
|
} else {
|
|
3534
3873
|
lines.push('No standards installed yet. Run `npx uds init` to install.');
|