universal-dev-standards 6.3.6 → 6.3.7
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/bundled/locales/zh-CN/CHANGELOG.md +9 -2
- package/bundled/locales/zh-CN/README.md +1 -1
- package/bundled/locales/zh-TW/CHANGELOG.md +9 -2
- package/bundled/locales/zh-TW/README.md +1 -1
- package/package.json +1 -1
- package/src/commands/deps.js +13 -1
- package/src/utils/dependency-resolution.js +94 -6
- package/standards-registry.json +7 -7
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
source: ../../CHANGELOG.md
|
|
3
|
-
source_version: 6.3.
|
|
4
|
-
translation_version: 6.3.
|
|
3
|
+
source_version: 6.3.7
|
|
4
|
+
translation_version: 6.3.7
|
|
5
5
|
last_synced: 2026-08-07
|
|
6
6
|
status: current
|
|
7
7
|
---
|
|
@@ -17,6 +17,13 @@ status: current
|
|
|
17
17
|
|
|
18
18
|
## [Unreleased]
|
|
19
19
|
|
|
20
|
+
## [6.3.7] - 2026-08-07
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- **`uds deps` 只读 root 的 manifest,于是 monorepo 得到一个关于自己一部分的干净答案。** npm workspaces 会把声明放在不只一份 `package.json`,而这个命令只看了其中一份。实测于一个真实项目:回报 34 个依赖,实际声明 47 个——**在 workspace 里的那 13 个是隐形的,而其中一个带着 high 级别的公告**。**一个没有带着自己范围的计数,与一个完整的计数无从分辨**,而那正是这个命令存在要回报的失效。现在会从 `workspaces` 字段展开、检查每一份 manifest,并在报告中打印纳入了哪些 workspace,让分母自己带着范围。每一行漂移都标明它来自哪个 workspace——否则读者知道某个包漂移了,却不知道该去改哪一份 `package.json`。
|
|
25
|
+
- 三个细节决定了「有覆盖 workspaces」与「看起来有覆盖」的差别。lockfile 的条目可能被 hoist 到 root,**也可能**嵌套在 workspace 底下,所以两处都查;只查一处会把另一处回报成「not present in package-lock.json」,而**一个被捏造出来的未知读起来像一个发现**。workspace 依赖于另一个 workspace 时是文件链接而非已发布包,因此跳过而不查询——问 npm 会得到 404 并被记成 unverifiable。以及,比「最后一段结尾一个 `*`」更复杂的 `workspaces` 模式现在会**大声失败**而非匹配一个子集:**静静地覆盖得比作者本意少,是同一个缺陷换个地方发生**。
|
|
26
|
+
|
|
20
27
|
## [6.3.6] - 2026-08-07
|
|
21
28
|
|
|
22
29
|
### Fixed
|
|
@@ -15,7 +15,7 @@ status: current
|
|
|
15
15
|
|
|
16
16
|
> **语言**: [English](../../README.md) | [繁體中文](../zh-TW/README.md) | 简体中文
|
|
17
17
|
|
|
18
|
-
**版本**: 6.3.
|
|
18
|
+
**版本**: 6.3.7 | **发布日期**: 2026-07-31 | **授权**: [双重授权](../../LICENSE) (CC BY 4.0 + MIT)
|
|
19
19
|
|
|
20
20
|
语言无关、框架无关的软件项目文档标准。通过 AI 原生工作流,确保不同技术栈之间的一致性、质量和可维护性。
|
|
21
21
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
source: ../../CHANGELOG.md
|
|
3
|
-
source_version: 6.3.
|
|
4
|
-
translation_version: 6.3.
|
|
3
|
+
source_version: 6.3.7
|
|
4
|
+
translation_version: 6.3.7
|
|
5
5
|
last_synced: 2026-08-07
|
|
6
6
|
status: current
|
|
7
7
|
---
|
|
@@ -17,6 +17,13 @@ status: current
|
|
|
17
17
|
|
|
18
18
|
## [Unreleased]
|
|
19
19
|
|
|
20
|
+
## [6.3.7] - 2026-08-07
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- **`uds deps` 只讀 root 的 manifest,於是 monorepo 得到一個關於自己一部分的乾淨答案。** npm workspaces 會把宣告放在不只一份 `package.json`,而這個指令只看了其中一份。實測於一個真實專案:回報 34 個相依,實際宣告 47 個——**在 workspace 裡的那 13 個是隱形的,而其中一個帶著 high 等級的公告**。**一個沒有帶著自己範圍的計數,與一個完整的計數無從分辨**,而那正是這個指令存在要回報的失效。現在會從 `workspaces` 欄位展開、檢查每一份 manifest,並在報告中印出納入了哪些 workspace,讓分母自己帶著範圍。每一列漂移都標明它來自哪個 workspace——否則讀者知道某個套件漂移了,卻不知道該去改哪一份 `package.json`。
|
|
25
|
+
- 三個細節決定了「有涵蓋 workspaces」與「看起來有涵蓋」的差別。lockfile 的條目可能被 hoist 到 root,**也可能**巢狀在 workspace 底下,所以兩處都查;只查一處會把另一處回報成「not present in package-lock.json」,而**一個被捏造出來的未知讀起來像一個發現**。workspace 相依於另一個 workspace 時是檔案連結而非已發布套件,因此跳過而不查詢——問 npm 會得到 404 並被記成 unverifiable。以及,比「最後一段結尾一個 `*`」更複雜的 `workspaces` 模式現在會**大聲失敗**而非匹配一個子集:**靜靜地涵蓋得比作者本意少,是同一個缺陷換個地方發生**。
|
|
26
|
+
|
|
20
27
|
## [6.3.6] - 2026-08-07
|
|
21
28
|
|
|
22
29
|
### Fixed
|
|
@@ -15,7 +15,7 @@ status: current
|
|
|
15
15
|
|
|
16
16
|
> **語言**: [English](../../README.md) | 繁體中文 | [简体中文](../zh-CN/README.md)
|
|
17
17
|
|
|
18
|
-
**版本**: 6.3.
|
|
18
|
+
**版本**: 6.3.7 | **發布日期**: 2026-07-31 | **授權**: [雙重授權](../../LICENSE) (CC BY 4.0 + MIT)
|
|
19
19
|
|
|
20
20
|
語言無關、框架無關的軟體專案文件標準。透過 AI 原生工作流,確保不同技術堆疊之間的一致性、品質和可維護性。
|
|
21
21
|
|
package/package.json
CHANGED
package/src/commands/deps.js
CHANGED
|
@@ -37,6 +37,15 @@ export function render(result) {
|
|
|
37
37
|
lines.push('');
|
|
38
38
|
lines.push(`${label} — ${result.examined} runtime dependenc${result.examined === 1 ? 'y' : 'ies'} checked`);
|
|
39
39
|
|
|
40
|
+
// The scope of the count, printed whenever it is more than the root. A
|
|
41
|
+
// denominator without its scope is what let this command report "34
|
|
42
|
+
// dependencies checked" for a repository that declared 47 across two
|
|
43
|
+
// manifests — the reader has no way to tell a complete answer from a subset.
|
|
44
|
+
const wsCount = result.workspaces?.length ?? 0;
|
|
45
|
+
if (wsCount > 0) {
|
|
46
|
+
lines.push(chalk.dim(` across the root and ${wsCount} workspace${wsCount === 1 ? '' : 's'}: ${result.workspaces.join(', ')}`));
|
|
47
|
+
}
|
|
48
|
+
|
|
40
49
|
if (!result.hasLockfile) {
|
|
41
50
|
lines.push(chalk.yellow(' no package-lock.json — nothing to compare the registry against'));
|
|
42
51
|
}
|
|
@@ -52,9 +61,12 @@ export function render(result) {
|
|
|
52
61
|
// underneath it" shape that R4 rewrote the supply-chain standard to stop.
|
|
53
62
|
lines.push(chalk.yellow(` ${result.drifted.length} tested ≠ resolves:`));
|
|
54
63
|
for (const d of result.drifted) {
|
|
64
|
+
// The workspace is part of the finding, not decoration: without it the
|
|
65
|
+
// reader knows a package drifted but not which package.json to edit.
|
|
66
|
+
const where = d.workspaceDir ? chalk.dim(` [${d.workspaceDir}]`) : '';
|
|
55
67
|
lines.push(
|
|
56
68
|
` ${chalk.bold(d.name)} ${chalk.dim(d.range)}` +
|
|
57
|
-
` tested=${chalk.cyan(d.locked)} resolves=${chalk.yellow(d.resolved)}`
|
|
69
|
+
` tested=${chalk.cyan(d.locked)} resolves=${chalk.yellow(d.resolved)}${where}`
|
|
58
70
|
);
|
|
59
71
|
}
|
|
60
72
|
lines.push('');
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
*/
|
|
67
67
|
|
|
68
68
|
import { spawn } from 'node:child_process';
|
|
69
|
-
import { readFileSync, existsSync } from 'node:fs';
|
|
69
|
+
import { readFileSync, existsSync, readdirSync } from 'node:fs';
|
|
70
70
|
import { join } from 'node:path';
|
|
71
71
|
import semver from 'semver';
|
|
72
72
|
|
|
@@ -200,6 +200,66 @@ function spawnNpm(cwd) {
|
|
|
200
200
|
});
|
|
201
201
|
}
|
|
202
202
|
|
|
203
|
+
/**
|
|
204
|
+
* Expand npm's `workspaces` field into directories that contain a
|
|
205
|
+
* package.json. // implements XSPEC-366 R1 (workspaces)
|
|
206
|
+
*
|
|
207
|
+
* Accepts both spellings — an array, or `{ packages: [...] }` — and supports a
|
|
208
|
+
* trailing `*` in the last segment, which is what `packages/*` needs and is the
|
|
209
|
+
* shape npm's own docs use. A pattern that matches nothing is returned as
|
|
210
|
+
* nothing rather than as an error: an empty `packages/*` is a normal state for
|
|
211
|
+
* a repository that has not added one yet.
|
|
212
|
+
*/
|
|
213
|
+
function expandWorkspaces(root, field) {
|
|
214
|
+
const patterns = Array.isArray(field) ? field : Array.isArray(field?.packages) ? field.packages : [];
|
|
215
|
+
const dirs = [];
|
|
216
|
+
for (const pattern of patterns) {
|
|
217
|
+
if (typeof pattern !== 'string' || pattern.length === 0) continue;
|
|
218
|
+
const star = pattern.indexOf('*');
|
|
219
|
+
if (star === -1) {
|
|
220
|
+
if (existsSync(join(root, pattern, 'package.json'))) dirs.push(pattern);
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
// Only a trailing `*` in the final segment is supported. Anything more
|
|
224
|
+
// exotic is rejected loudly rather than silently matching less than the
|
|
225
|
+
// author meant — a workspace quietly outside the denominator is the defect
|
|
226
|
+
// this module exists to report.
|
|
227
|
+
const prefix = pattern.slice(0, star);
|
|
228
|
+
if (!pattern.endsWith('*') || prefix.includes('*') || (prefix.length > 0 && !prefix.endsWith('/'))) {
|
|
229
|
+
throw new Error(
|
|
230
|
+
`unsupported workspaces pattern ${JSON.stringify(pattern)} — only a trailing "*" in the last segment is understood`
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
const parent = join(root, prefix);
|
|
234
|
+
if (!existsSync(parent)) continue;
|
|
235
|
+
for (const entry of readdirSync(parent, { withFileTypes: true })) {
|
|
236
|
+
if (!entry.isDirectory()) continue;
|
|
237
|
+
const rel = `${prefix}${entry.name}`;
|
|
238
|
+
if (existsSync(join(root, rel, 'package.json'))) dirs.push(rel);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return dirs;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Where the lockfile records this dependency.
|
|
246
|
+
*
|
|
247
|
+
* npm hoists what it can to the root `node_modules` and nests the rest under
|
|
248
|
+
* the workspace, so both have to be tried; checking only one under-reports and
|
|
249
|
+
* calls the miss "unverifiable", which is noise that trains people to ignore
|
|
250
|
+
* the report.
|
|
251
|
+
*/
|
|
252
|
+
function lockedVersionFor(lock, workspaceDir, name) {
|
|
253
|
+
const candidates = workspaceDir
|
|
254
|
+
? [`${workspaceDir}/node_modules/${name}`, `node_modules/${name}`]
|
|
255
|
+
: [`node_modules/${name}`];
|
|
256
|
+
for (const key of candidates) {
|
|
257
|
+
const entry = lock?.packages?.[key];
|
|
258
|
+
if (entry?.version) return entry.version;
|
|
259
|
+
}
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
|
|
203
263
|
/** Map over `items` with a bounded number of concurrent workers. */
|
|
204
264
|
async function mapWithConcurrency(items, limit, worker) {
|
|
205
265
|
const results = new Array(items.length);
|
|
@@ -238,16 +298,37 @@ export async function measureResolutionDrift(root, options = {}) {
|
|
|
238
298
|
const lockPath = join(root, 'package-lock.json');
|
|
239
299
|
const lock = existsSync(lockPath) ? JSON.parse(readFileSync(lockPath, 'utf8')) : null;
|
|
240
300
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
301
|
+
// Workspaces are examined too. A monorepo that declares its shipped
|
|
302
|
+
// front-end in a workspace and its server at the root has two manifests, and
|
|
303
|
+
// reading only the root reports a clean subset as if it were the whole — the
|
|
304
|
+
// failure this module exists to catch, performed by the tool itself.
|
|
305
|
+
const workspaceDirs = expandWorkspaces(root, pkg.workspaces);
|
|
306
|
+
const manifests = [{ dir: null, name: pkg.name ?? null, pkg }];
|
|
307
|
+
for (const dir of workspaceDirs) {
|
|
308
|
+
const wsPkg = JSON.parse(readFileSync(join(root, dir, 'package.json'), 'utf8'));
|
|
309
|
+
manifests.push({ dir, name: wsPkg.name ?? dir, pkg: wsPkg });
|
|
310
|
+
}
|
|
311
|
+
// A workspace depending on a sibling workspace is a file link, not a
|
|
312
|
+
// registry package. Querying npm for it returns 404, which would be recorded
|
|
313
|
+
// as unverifiable — a fabricated unknown, which is worse than a missing one
|
|
314
|
+
// because it looks like a finding.
|
|
315
|
+
const localNames = new Set(manifests.map((m) => m.pkg.name).filter(Boolean));
|
|
316
|
+
|
|
317
|
+
const declared = [];
|
|
318
|
+
for (const { dir, name: workspace, pkg: manifest } of manifests) {
|
|
319
|
+
for (const kind of ['dependencies', 'optionalDependencies']) {
|
|
320
|
+
for (const [name, range] of Object.entries(manifest[kind] ?? {})) {
|
|
321
|
+
if (localNames.has(name)) continue;
|
|
322
|
+
declared.push({ name, range, kind, workspace, workspaceDir: dir });
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
245
326
|
|
|
246
327
|
const run = options.run ?? spawnNpm(root);
|
|
247
328
|
const concurrency = options.concurrency ?? DEFAULT_CONCURRENCY;
|
|
248
329
|
|
|
249
330
|
const rows = await mapWithConcurrency(declared, concurrency, async (dep) => {
|
|
250
|
-
const locked = lock
|
|
331
|
+
const locked = lockedVersionFor(lock, dep.workspaceDir, dep.name);
|
|
251
332
|
let resolved;
|
|
252
333
|
try {
|
|
253
334
|
resolved = await resolveViaNpm(dep.name, dep.range, run);
|
|
@@ -282,6 +363,13 @@ export async function measureResolutionDrift(root, options = {}) {
|
|
|
282
363
|
root,
|
|
283
364
|
packageName: pkg.name ?? null,
|
|
284
365
|
hasLockfile: lock !== null,
|
|
366
|
+
/**
|
|
367
|
+
* Workspace directories examined alongside the root, so a reader can tell
|
|
368
|
+
* "no workspaces here" from "workspaces were not looked at". The count
|
|
369
|
+
* printed by the report is the denominator, and a denominator without its
|
|
370
|
+
* scope is the shape this module was written to refuse.
|
|
371
|
+
*/
|
|
372
|
+
workspaces: workspaceDirs,
|
|
285
373
|
examined: rows.length,
|
|
286
374
|
consistent,
|
|
287
375
|
drifted,
|
package/standards-registry.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"version": "6.3.
|
|
3
|
+
"version": "6.3.7",
|
|
4
4
|
"lastUpdated": "2026-05-13",
|
|
5
5
|
"description": "Standards registry for universal-dev-standards with integrated skills and AI-optimized formats",
|
|
6
6
|
"formats": {
|
|
@@ -58,14 +58,14 @@
|
|
|
58
58
|
"standards": {
|
|
59
59
|
"name": "universal-dev-standards",
|
|
60
60
|
"url": "https://github.com/AsiaOstrich/universal-dev-standards",
|
|
61
|
-
"version": "6.3.
|
|
61
|
+
"version": "6.3.7"
|
|
62
62
|
},
|
|
63
63
|
"skills": {
|
|
64
64
|
"name": "universal-dev-standards",
|
|
65
65
|
"url": "https://github.com/AsiaOstrich/universal-dev-standards",
|
|
66
66
|
"localPath": "skills",
|
|
67
67
|
"rawUrl": "https://raw.githubusercontent.com/AsiaOstrich/universal-dev-standards/main/skills",
|
|
68
|
-
"version": "6.3.
|
|
68
|
+
"version": "6.3.7",
|
|
69
69
|
"note": "Skills are now included in the main repository under skills/"
|
|
70
70
|
}
|
|
71
71
|
},
|
|
@@ -2236,7 +2236,7 @@
|
|
|
2236
2236
|
"id": "license-compliance",
|
|
2237
2237
|
"name": "License Compliance Standards",
|
|
2238
2238
|
"nameZh": "授權合規標準",
|
|
2239
|
-
"version": "6.3.
|
|
2239
|
+
"version": "6.3.7",
|
|
2240
2240
|
"source": {
|
|
2241
2241
|
"human": "core/license-compliance.md",
|
|
2242
2242
|
"ai": "ai/standards/license-compliance.ai.yaml"
|
|
@@ -2248,7 +2248,7 @@
|
|
|
2248
2248
|
"id": "verification-oracle",
|
|
2249
2249
|
"name": "Verification Oracle Standards",
|
|
2250
2250
|
"nameZh": "驗證 Oracle 標準",
|
|
2251
|
-
"version": "6.3.
|
|
2251
|
+
"version": "6.3.7",
|
|
2252
2252
|
"source": {
|
|
2253
2253
|
"human": "core/verification-oracle.md",
|
|
2254
2254
|
"ai": "ai/standards/verification-oracle.ai.yaml"
|
|
@@ -2260,7 +2260,7 @@
|
|
|
2260
2260
|
"id": "model-provenance",
|
|
2261
2261
|
"name": "Model Provenance Policy Standards",
|
|
2262
2262
|
"nameZh": "模型來源政策標準",
|
|
2263
|
-
"version": "6.3.
|
|
2263
|
+
"version": "6.3.7",
|
|
2264
2264
|
"source": {
|
|
2265
2265
|
"human": "core/model-provenance.md",
|
|
2266
2266
|
"ai": "ai/standards/model-provenance.ai.yaml"
|
|
@@ -2272,7 +2272,7 @@
|
|
|
2272
2272
|
"id": "resource-cost-boundary",
|
|
2273
2273
|
"name": "Resource / Cost Boundary Declaration Standards",
|
|
2274
2274
|
"nameZh": "資源/成本邊界宣告標準",
|
|
2275
|
-
"version": "6.3.
|
|
2275
|
+
"version": "6.3.7",
|
|
2276
2276
|
"source": {
|
|
2277
2277
|
"human": "core/resource-cost-boundary.md",
|
|
2278
2278
|
"ai": "ai/standards/resource-cost-boundary.ai.yaml"
|