universal-dev-standards 6.3.8 → 6.3.10
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 +18 -3
- package/bundled/locales/zh-CN/README.md +1 -1
- package/bundled/locales/zh-TW/CHANGELOG.md +18 -3
- package/bundled/locales/zh-TW/README.md +1 -1
- package/package.json +1 -1
- package/src/commands/update.js +187 -39
- package/src/utils/integration-generator.js +27 -6
- package/standards-registry.json +7 -7
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
source: ../../CHANGELOG.md
|
|
3
|
-
source_version: 6.3.
|
|
4
|
-
translation_version: 6.3.
|
|
5
|
-
last_synced: 2026-08-
|
|
3
|
+
source_version: 6.3.10
|
|
4
|
+
translation_version: 6.3.10
|
|
5
|
+
last_synced: 2026-08-10
|
|
6
6
|
status: current
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -17,6 +17,21 @@ status: current
|
|
|
17
17
|
|
|
18
18
|
## [Unreleased]
|
|
19
19
|
|
|
20
|
+
## [6.3.10] - 2026-08-10
|
|
21
|
+
|
|
22
|
+
### 修复
|
|
23
|
+
|
|
24
|
+
- **`--plan` 会被另外四个标志吃掉,而唯一能防止破坏的正是被忽略的那一个。** `uds update --plan --skills` 会安装 Skills;`--plan --sync-refs` 会改写集成文件与 manifest。那个文档写着「Show reconciliation plan without executing (like terraform plan)」的标志被静默丢弃——因为范围标志(`--skills`、`--commands`、`--integrations-only`、`--sync-refs`)在一条先到先得、每个分支都 return 的链里,排在模式标志(`--plan`、`--apply`、`--force`、`--rollback`)之前。`--integrations-only` 早在 2026-07-30 就为了同一件事修过;另外三个没有被碰,十一天后它们仍在写文件。现在模式先于范围决定,而测试改为**从 CLI 定义读出标志清单**而非人工列举——之后新增的标志不需要有人记得就会被覆盖。第四个实例正是那个测试找出来的。
|
|
25
|
+
- **`--apply --skills` 只升级 Skills、安静地把标准留在原地,并报告成功。** 同一条链:`--skills` 在协调器执行之前就 return 了。以此方式升级一个真实项目,结果它停在旧的标准版本,而屏幕上没有任何一行说明。现在 `--apply` 与 `--force` 会执行协调**并且**执行所要求的范围。
|
|
26
|
+
- **没有人能回答的确认提示,返回 exit 0。** 非交互 shell 下 `@inquirer/prompts` 会抛出 `ExitPromptError`,该异常从未被捕获而进程仍以 exit code 0 结束——于是在 CI 里「什么都没写」与「更新成功」返回同一个值。现在它会说明没有任何东西被写入、指向 `--yes`,并以 exit 2 结束。检测方式是**提示真的失败了**,而不是探测 `process.stdin.isTTY`——后者在两个方向上都会答错。
|
|
27
|
+
- **`--rollback` 现在会说明 `--skills`/`--commands` 无法缩小它的范围**,而不是接受它们却照样还原全部。
|
|
28
|
+
|
|
29
|
+
## [6.3.9] - 2026-08-09
|
|
30
|
+
|
|
31
|
+
### 修复
|
|
32
|
+
|
|
33
|
+
- **标准索引声明的数字與 sync 检查对不上,而两者描述的是同一份 manifest。** 6.3.8 让检查改為透過 registry 解析 manifest 项目;而索引区块仍直接声明 manifest 的 `installedStandards.length`。在一個真实项目上那是 78 對 70,差额正是 `MIGRATION-v6` §2 於 6.0.0 移除的八個机器可读标准——它们在兩個主版本之后仍声明於 manifest 中,因为 `uds update` 不会修剪它们。撇开检查不谈,七十八本来就是错的数字:它就写在「權威清單為 `.standards/manifest.json` 的 `standards` 字段」正上方——把读者指向这个差異的来源、仿佛那就解決了它——并且告訴 agent 去期待八個并不存在的文件。区块现在数的是解析得到文件的项目,其余在下方指名並说明它们为何还在,于是兩個数字都会出现,都不必靠推测。
|
|
34
|
+
|
|
20
35
|
## [6.3.8] - 2026-08-08
|
|
21
36
|
|
|
22
37
|
### 修复
|
|
@@ -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.10 | **发布日期**: 2026-07-31 | **授权**: [双重授权](../../LICENSE) (CC BY 4.0 + MIT)
|
|
19
19
|
|
|
20
20
|
语言无关、框架无关的软件项目文档标准。通过 AI 原生工作流,确保不同技术栈之间的一致性、质量和可维护性。
|
|
21
21
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
source: ../../CHANGELOG.md
|
|
3
|
-
source_version: 6.3.
|
|
4
|
-
translation_version: 6.3.
|
|
5
|
-
last_synced: 2026-08-
|
|
3
|
+
source_version: 6.3.10
|
|
4
|
+
translation_version: 6.3.10
|
|
5
|
+
last_synced: 2026-08-10
|
|
6
6
|
status: current
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -17,6 +17,21 @@ status: current
|
|
|
17
17
|
|
|
18
18
|
## [Unreleased]
|
|
19
19
|
|
|
20
|
+
## [6.3.10] - 2026-08-10
|
|
21
|
+
|
|
22
|
+
### 修正
|
|
23
|
+
|
|
24
|
+
- **`--plan` 會被另外四個旗標吃掉,而唯一能防止破壞的正是被忽略的那一個。** `uds update --plan --skills` 會安裝 Skills;`--plan --sync-refs` 會改寫整合檔與 manifest。那個文件寫著「Show reconciliation plan without executing (like terraform plan)」的旗標被靜默丟棄——因為範圍旗標(`--skills`、`--commands`、`--integrations-only`、`--sync-refs`)在一條先到先得、每個分支都 return 的鏈裡,排在模式旗標(`--plan`、`--apply`、`--force`、`--rollback`)之前。`--integrations-only` 早在 2026-07-30 就為了同一件事修過;另外三個沒有被碰,十一天後它們仍在寫檔。現在模式先於範圍決定,而測試改為**從 CLI 定義讀出旗標清單**而非人工列舉——之後新增的旗標不需要有人記得就會被涵蓋。第四個實例正是那個測試找出來的。
|
|
25
|
+
- **`--apply --skills` 只升級 Skills、安靜地把標準留在原地,並回報成功。** 同一條鏈:`--skills` 在調和器執行之前就 return 了。以此方式升級一個真實專案,結果它停在舊的標準版本,而畫面上沒有任何一行說明。現在 `--apply` 與 `--force` 會執行調和**並且**執行所要求的範圍。
|
|
26
|
+
- **沒有人能回答的確認提示,回傳 exit 0。** 非互動 shell 下 `@inquirer/prompts` 會擲 `ExitPromptError`,該例外從未被攔截而程序仍以 exit code 0 結束——於是在 CI 裡「什麼都沒寫」與「更新成功」回傳同一個值。現在它會說明沒有任何東西被寫入、指向 `--yes`,並以 exit 2 結束。偵測方式是**提示真的失敗了**,而不是探測 `process.stdin.isTTY`——後者在兩個方向上都會答錯。
|
|
27
|
+
- **`--rollback` 現在會說明 `--skills`/`--commands` 無法縮小它的範圍**,而不是接受它們卻照樣還原全部。
|
|
28
|
+
|
|
29
|
+
## [6.3.9] - 2026-08-09
|
|
30
|
+
|
|
31
|
+
### 修正
|
|
32
|
+
|
|
33
|
+
- **標準索引宣告的數字與 sync 檢查對不上,而兩者描述的是同一份 manifest。** 6.3.8 讓檢查改為透過 registry 解析 manifest 項目;而索引區塊仍直接宣告 manifest 的 `installedStandards.length`。在一個真實專案上那是 78 對 70,差額正是 `MIGRATION-v6` §2 於 6.0.0 移除的八個機器可讀標準——它們在兩個主版本之後仍宣告於 manifest 中,因為 `uds update` 不會修剪它們。撇開檢查不談,七十八本來就是錯的數字:它就寫在「權威清單為 `.standards/manifest.json` 的 `standards` 欄位」正上方——把讀者指向這個差異的來源、彷彿那就解決了它——並且告訴 agent 去期待八個並不存在的檔案。區塊現在數的是解析得到檔案的項目,其餘在下方指名並說明它們為何還在,於是兩個數字都會出現,都不必用推的。
|
|
34
|
+
|
|
20
35
|
## [6.3.8] - 2026-08-08
|
|
21
36
|
|
|
22
37
|
### 修正
|
|
@@ -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.10 | **發布日期**: 2026-07-31 | **授權**: [雙重授權](../../LICENSE) (CC BY 4.0 + MIT)
|
|
19
19
|
|
|
20
20
|
語言無關、框架無關的軟體專案文件標準。透過 AI 原生工作流,確保不同技術堆疊之間的一致性、品質和可維護性。
|
|
21
21
|
|
package/package.json
CHANGED
package/src/commands/update.js
CHANGED
|
@@ -334,9 +334,13 @@ export async function updateCommand(options) {
|
|
|
334
334
|
console.log(chalk.bold(msg.title));
|
|
335
335
|
console.log(chalk.gray('─'.repeat(50)));
|
|
336
336
|
|
|
337
|
-
// Handle --sync-refs option
|
|
337
|
+
// Handle --sync-refs option.
|
|
338
|
+
// `--plan` is honoured here too. This branch is above the mode dispatch
|
|
339
|
+
// because sync-refs is its own operation rather than a scope of the
|
|
340
|
+
// reconciler — but "above the dispatch" is exactly how --plan got dropped by
|
|
341
|
+
// three other branches, so it passes the flag down instead of assuming.
|
|
338
342
|
if (options.syncRefs) {
|
|
339
|
-
await syncIntegrationReferences(projectPath, manifest);
|
|
343
|
+
await syncIntegrationReferences(projectPath, manifest, { plan: !!options.plan });
|
|
340
344
|
return;
|
|
341
345
|
}
|
|
342
346
|
|
|
@@ -350,39 +354,68 @@ export async function updateCommand(options) {
|
|
|
350
354
|
return;
|
|
351
355
|
}
|
|
352
356
|
|
|
353
|
-
//
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
//
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
//
|
|
357
|
+
// ── Flags come in two axes, and they must not eat each other ──────────
|
|
358
|
+
// // implements XSPEC-372 R1/R2
|
|
359
|
+
//
|
|
360
|
+
// mode : --plan / --apply / --force / --rollback (what to do)
|
|
361
|
+
// scope : --skills / --commands / --integrations-only / --standards-only
|
|
362
|
+
//
|
|
363
|
+
// These used to sit in one first-match-wins if/return chain with the scope
|
|
364
|
+
// flags first. `--plan --skills` therefore *installed* Skills — the flag
|
|
365
|
+
// documented as "without executing" was dropped with nothing said — and
|
|
366
|
+
// `--apply --skills` upgraded Skills while silently leaving the standards
|
|
367
|
+
// behind, reporting success either way. Both measured 2026-08-10 while
|
|
368
|
+
// upgrading four repos to 6.3.9.
|
|
369
|
+
//
|
|
370
|
+
// Mode is now decided first, and scope narrows it instead of replacing it.
|
|
371
|
+
const scopedToSkills = !!options.skills;
|
|
372
|
+
const scopedToCommands = !!options.commands;
|
|
373
|
+
|
|
374
|
+
// Handle --rollback option (DSR). It restores a whole backup, so a scope
|
|
375
|
+
// flag cannot narrow it — say so rather than appearing to honour it.
|
|
366
376
|
if (options.rollback) {
|
|
377
|
+
if (scopedToSkills || scopedToCommands) {
|
|
378
|
+
console.log(chalk.yellow(' ! --rollback restores a complete backup; --skills/--commands cannot narrow it and are ignored.'));
|
|
379
|
+
console.log();
|
|
380
|
+
}
|
|
367
381
|
await handleRollback(projectPath);
|
|
368
382
|
return;
|
|
369
383
|
}
|
|
370
384
|
|
|
371
|
-
// Handle --plan option (DSR dry-run)
|
|
385
|
+
// Handle --plan option (DSR dry-run). Nothing below this line writes.
|
|
372
386
|
if (options.plan) {
|
|
373
|
-
|
|
387
|
+
if (!scopedToSkills && !scopedToCommands) {
|
|
388
|
+
await handlePlan(projectPath, options);
|
|
389
|
+
}
|
|
390
|
+
if (scopedToSkills) await planSkills(projectPath, manifest, options);
|
|
391
|
+
if (scopedToCommands) await planCommands(projectPath, manifest, options);
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// Handle --apply / --force (DSR reconciliation), composing with scope.
|
|
396
|
+
// A scope flag alone still means "only that scope"; combined with a mode it
|
|
397
|
+
// means "that mode, and also that scope" — the reconciler tracks Skills
|
|
398
|
+
// files only when the manifest records them, so `--skills` is not redundant.
|
|
399
|
+
if (options.apply || options.force) {
|
|
400
|
+
if (!scopedToSkills && !scopedToCommands) {
|
|
401
|
+
await handleReconcile(projectPath, options, { force: !!options.force });
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
await handleReconcile(projectPath, options, { force: !!options.force });
|
|
405
|
+
// These exit the process on completion, so run Skills last.
|
|
406
|
+
if (scopedToCommands) await updateCommandsOnly(projectPath, manifest, options);
|
|
407
|
+
if (scopedToSkills) await updateSkillsOnly(projectPath, manifest, options);
|
|
374
408
|
return;
|
|
375
409
|
}
|
|
376
410
|
|
|
377
|
-
//
|
|
378
|
-
if (
|
|
379
|
-
await
|
|
411
|
+
// Scope without a mode: the original shortcuts.
|
|
412
|
+
if (scopedToSkills) {
|
|
413
|
+
await updateSkillsOnly(projectPath, manifest, options);
|
|
380
414
|
return;
|
|
381
415
|
}
|
|
382
416
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
await handleReconcile(projectPath, options, { force: true });
|
|
417
|
+
if (scopedToCommands) {
|
|
418
|
+
await updateCommandsOnly(projectPath, manifest, options);
|
|
386
419
|
return;
|
|
387
420
|
}
|
|
388
421
|
|
|
@@ -494,11 +527,8 @@ export async function updateCommand(options) {
|
|
|
494
527
|
console.log();
|
|
495
528
|
|
|
496
529
|
// Confirm
|
|
497
|
-
|
|
498
|
-
const confirmed = await
|
|
499
|
-
message: msg.confirmUpdate,
|
|
500
|
-
default: true
|
|
501
|
-
});
|
|
530
|
+
{
|
|
531
|
+
const confirmed = await confirmOrFail({ message: msg.confirmUpdate, options });
|
|
502
532
|
|
|
503
533
|
if (!confirmed) {
|
|
504
534
|
console.log(chalk.yellow(msg.updateCancelled));
|
|
@@ -558,10 +588,7 @@ export async function updateCommand(options) {
|
|
|
558
588
|
shouldInstallNew = true;
|
|
559
589
|
} else {
|
|
560
590
|
// Interactive mode: ask user
|
|
561
|
-
const installNew = await
|
|
562
|
-
message: msg.installNewStandards,
|
|
563
|
-
default: true
|
|
564
|
-
});
|
|
591
|
+
const installNew = await confirmOrFail({ message: msg.installNewStandards, options });
|
|
565
592
|
shouldInstallNew = installNew;
|
|
566
593
|
}
|
|
567
594
|
|
|
@@ -886,9 +913,9 @@ export async function updateCommand(options) {
|
|
|
886
913
|
if (options.yes) {
|
|
887
914
|
shouldRestore = true;
|
|
888
915
|
} else {
|
|
889
|
-
const restoreMissing = await
|
|
916
|
+
const restoreMissing = await confirmOrFail({
|
|
890
917
|
message: (msg.restoreMissingPrompt || 'Restore {count} missing file(s)?').replace('{count}', missingFiles.length),
|
|
891
|
-
|
|
918
|
+
options
|
|
892
919
|
});
|
|
893
920
|
shouldRestore = restoreMissing;
|
|
894
921
|
}
|
|
@@ -1440,6 +1467,40 @@ export function regenerateIntegrations(projectPath, manifest) {
|
|
|
1440
1467
|
* @param {string} projectPath - Project path
|
|
1441
1468
|
* @param {Object} manifest - Manifest object
|
|
1442
1469
|
*/
|
|
1470
|
+
/**
|
|
1471
|
+
* Ask for confirmation, or fail loudly when there is nobody to ask.
|
|
1472
|
+
* // implements XSPEC-372 R3
|
|
1473
|
+
*
|
|
1474
|
+
* `@inquirer/prompts` throws `ExitPromptError` when stdin is not a TTY. That
|
|
1475
|
+
* exception was never caught, and the process still ended with **exit code 0**
|
|
1476
|
+
* — so in CI a run that wrote nothing was indistinguishable from a successful
|
|
1477
|
+
* update. Measured 2026-08-10 on `uds update --apply` and reproduced with
|
|
1478
|
+
* `--force --apply`: zero files written, `echo $?` printed 0.
|
|
1479
|
+
*
|
|
1480
|
+
* Not auto-confirming: that would give an unattended environment broader
|
|
1481
|
+
* permission than an interactive one. Exit 2 rather than 1 keeps "could not
|
|
1482
|
+
* run" distinct from "ran and found a problem".
|
|
1483
|
+
*/
|
|
1484
|
+
async function confirmOrFail({ message, defaultValue = true, options }) {
|
|
1485
|
+
if (options?.yes) return true;
|
|
1486
|
+
try {
|
|
1487
|
+
return await inquirerConfirm({ message, default: defaultValue });
|
|
1488
|
+
} catch (err) {
|
|
1489
|
+
// Detect by what the prompt does, not by probing `process.stdin.isTTY`.
|
|
1490
|
+
// isTTY is a proxy for "somebody can answer", and it is wrong in both
|
|
1491
|
+
// directions — a mocked or wrapped stdin answers fine with isTTY unset.
|
|
1492
|
+
// The prompt failing IS the condition; anything else is a guess about it.
|
|
1493
|
+
const closed = err?.name === 'ExitPromptError' || /force closed the prompt/i.test(err?.message || '');
|
|
1494
|
+
if (!closed) throw err;
|
|
1495
|
+
console.log();
|
|
1496
|
+
console.log(chalk.red('Cannot ask for confirmation: there is nothing attached to answer the prompt (non-interactive shell, CI, or a pipe).'));
|
|
1497
|
+
console.log(chalk.gray(` Pending question: ${message}`));
|
|
1498
|
+
console.log(chalk.gray(' Re-run with --yes to confirm up front. Nothing has been written.'));
|
|
1499
|
+
console.log();
|
|
1500
|
+
process.exit(2);
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1443
1504
|
async function updateIntegrationsOnly(projectPath, manifest, options = {}) {
|
|
1444
1505
|
const msg = t().commands.update;
|
|
1445
1506
|
|
|
@@ -1548,7 +1609,7 @@ async function updateIntegrationsOnly(projectPath, manifest, options = {}) {
|
|
|
1548
1609
|
* @param {string} projectPath - Project path
|
|
1549
1610
|
* @param {Object} manifest - Manifest object
|
|
1550
1611
|
*/
|
|
1551
|
-
async function syncIntegrationReferences(projectPath, manifest) {
|
|
1612
|
+
async function syncIntegrationReferences(projectPath, manifest, { plan = false } = {}) {
|
|
1552
1613
|
const msg = t().commands.update;
|
|
1553
1614
|
|
|
1554
1615
|
console.log(chalk.cyan(msg.syncingRefs));
|
|
@@ -1615,6 +1676,12 @@ async function syncIntegrationReferences(projectPath, manifest) {
|
|
|
1615
1676
|
outputLanguage: manifest.options?.output_language || manifest.options?.commit_language || config.outputLanguage || config.commitLanguage || 'english'
|
|
1616
1677
|
};
|
|
1617
1678
|
|
|
1679
|
+
if (plan) {
|
|
1680
|
+
console.log(chalk.yellow(` ~ ${integrationPath}: categories would change to ${expectedCategories.join(', ') || '(none)'} (dry run — nothing is written)`));
|
|
1681
|
+
updatedCount++;
|
|
1682
|
+
continue;
|
|
1683
|
+
}
|
|
1684
|
+
|
|
1618
1685
|
const result = writeIntegrationFile(toolName, newConfig, projectPath);
|
|
1619
1686
|
|
|
1620
1687
|
if (result.success) {
|
|
@@ -1657,7 +1724,11 @@ async function syncIntegrationReferences(projectPath, manifest) {
|
|
|
1657
1724
|
if (updatedCount > 0) {
|
|
1658
1725
|
manifest.version = '3.3.0';
|
|
1659
1726
|
refreshIntegrationBlockHashes(manifest, projectPath);
|
|
1660
|
-
|
|
1727
|
+
if (plan) {
|
|
1728
|
+
console.log(chalk.gray(' (dry run — the manifest was not written)'));
|
|
1729
|
+
} else {
|
|
1730
|
+
writeManifest(manifest, projectPath);
|
|
1731
|
+
}
|
|
1661
1732
|
}
|
|
1662
1733
|
|
|
1663
1734
|
// Summary
|
|
@@ -1680,6 +1751,83 @@ async function syncIntegrationReferences(projectPath, manifest) {
|
|
|
1680
1751
|
* @param {Object} manifest - Manifest object
|
|
1681
1752
|
* @param {Object} [options] - CLI options (forwarded for locale resolution)
|
|
1682
1753
|
*/
|
|
1754
|
+
/**
|
|
1755
|
+
* `--plan --skills`: say what a Skills update would do, and write nothing.
|
|
1756
|
+
* // implements XSPEC-372 R1
|
|
1757
|
+
*
|
|
1758
|
+
* Deliberately does not call installSkillsToMultipleAgents and roll back.
|
|
1759
|
+
* "Write then restore" leaves a broken tree if it dies halfway, which is worse
|
|
1760
|
+
* than having no dry run at all — the same reasoning as the integrations plan.
|
|
1761
|
+
*/
|
|
1762
|
+
async function planSkills(projectPath, manifest, options) {
|
|
1763
|
+
const repoInfo = getRepositoryInfo();
|
|
1764
|
+
const latestVersion = repoInfo.skills.version;
|
|
1765
|
+
const installations = (manifest.skills?.installations || []).filter(i => i.level !== 'marketplace');
|
|
1766
|
+
|
|
1767
|
+
console.log(chalk.bold('=== Skills Plan (dry run — nothing is written) ==='));
|
|
1768
|
+
|
|
1769
|
+
if (installations.length === 0) {
|
|
1770
|
+
// Not "up to date". The manifest records no installation, so a Skills
|
|
1771
|
+
// update has nothing to act on — which is a different answer, and the one
|
|
1772
|
+
// that explains why `--skills` appears to do nothing here.
|
|
1773
|
+
console.log(chalk.yellow(' No Skills installations recorded in the manifest — nothing for --skills to update.'));
|
|
1774
|
+
if (manifest.skills?.installed) {
|
|
1775
|
+
console.log(chalk.gray(' (manifest.skills.installed is true but installations[] is empty; run `uds skills` to re-register)'));
|
|
1776
|
+
}
|
|
1777
|
+
console.log();
|
|
1778
|
+
return;
|
|
1779
|
+
}
|
|
1780
|
+
|
|
1781
|
+
let changed = 0;
|
|
1782
|
+
for (const inst of installations) {
|
|
1783
|
+
const info = getInstalledSkillsInfoForAgent(inst.agent, inst.level, projectPath);
|
|
1784
|
+
const current = info?.version || 'unknown';
|
|
1785
|
+
const dir = getSkillsDirForAgent(inst.agent, inst.level, projectPath);
|
|
1786
|
+
if (current === latestVersion) {
|
|
1787
|
+
console.log(chalk.gray(` = ${getAgentDisplayName(inst.agent)} (${inst.level}): v${current} — unchanged`));
|
|
1788
|
+
} else {
|
|
1789
|
+
changed++;
|
|
1790
|
+
console.log(chalk.yellow(` ~ ${getAgentDisplayName(inst.agent)} (${inst.level}): v${current} → v${latestVersion} ${dir}`));
|
|
1791
|
+
}
|
|
1792
|
+
}
|
|
1793
|
+
|
|
1794
|
+
console.log();
|
|
1795
|
+
console.log(`Summary: ${changed} installation(s) would be updated, ${installations.length - changed} unchanged.`);
|
|
1796
|
+
console.log(chalk.gray(` Locale that would be used: ${resolveLocale(manifest, projectPath, options)}`));
|
|
1797
|
+
console.log(chalk.gray(' Run `uds update --apply --yes --skills` to apply.'));
|
|
1798
|
+
console.log();
|
|
1799
|
+
}
|
|
1800
|
+
|
|
1801
|
+
/**
|
|
1802
|
+
* `--plan --commands`: same contract as planSkills.
|
|
1803
|
+
* // implements XSPEC-372 R1
|
|
1804
|
+
*/
|
|
1805
|
+
async function planCommands(projectPath, manifest, options) {
|
|
1806
|
+
const installations = manifest.commands?.installations || [];
|
|
1807
|
+
|
|
1808
|
+
console.log(chalk.bold('=== Commands Plan (dry run — nothing is written) ==='));
|
|
1809
|
+
|
|
1810
|
+
if (installations.length === 0) {
|
|
1811
|
+
console.log(chalk.yellow(' No slash-command installations recorded in the manifest — nothing for --commands to update.'));
|
|
1812
|
+
console.log();
|
|
1813
|
+
return;
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1816
|
+
for (const inst of installations) {
|
|
1817
|
+
const agent = typeof inst === 'string' ? inst : inst.agent;
|
|
1818
|
+
const level = typeof inst === 'string' ? 'project' : (inst.level || 'project');
|
|
1819
|
+
const info = getInstalledCommandsForAgent(agent, level, projectPath);
|
|
1820
|
+
const dir = getCommandsDirForAgent(agent, level, projectPath);
|
|
1821
|
+
console.log(chalk.yellow(` ~ ${getAgentDisplayName(agent)} (${level}): ${info?.count || 0} command(s) would be reinstalled ${dir}`));
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
console.log();
|
|
1825
|
+
console.log(`Summary: ${installations.length} installation(s) would be reinstalled.`);
|
|
1826
|
+
console.log(chalk.gray(` Locale that would be used: ${resolveLocale(manifest, projectPath, options)}`));
|
|
1827
|
+
console.log(chalk.gray(' Run `uds update --apply --yes --commands` to apply.'));
|
|
1828
|
+
console.log();
|
|
1829
|
+
}
|
|
1830
|
+
|
|
1683
1831
|
async function updateSkillsOnly(projectPath, manifest, options) {
|
|
1684
1832
|
const msg = t().commands.update;
|
|
1685
1833
|
const repoInfo = getRepositoryInfo();
|
|
@@ -2382,10 +2530,10 @@ async function handleReconcile(projectPath, options, { force }) {
|
|
|
2382
2530
|
console.log();
|
|
2383
2531
|
|
|
2384
2532
|
// Confirm unless --yes
|
|
2385
|
-
|
|
2386
|
-
const confirmed = await
|
|
2533
|
+
{
|
|
2534
|
+
const confirmed = await confirmOrFail({
|
|
2387
2535
|
message: `Apply ${planResult.plan.actions.length} changes?`,
|
|
2388
|
-
|
|
2536
|
+
options
|
|
2389
2537
|
});
|
|
2390
2538
|
|
|
2391
2539
|
if (!confirmed) {
|
|
@@ -2754,7 +2754,8 @@ export function generateIntegrationContent(config) {
|
|
|
2754
2754
|
const standardsIndex = generateStandardsIndex(
|
|
2755
2755
|
installedStandards,
|
|
2756
2756
|
format,
|
|
2757
|
-
language
|
|
2757
|
+
language,
|
|
2758
|
+
standardsFormat
|
|
2758
2759
|
);
|
|
2759
2760
|
|
|
2760
2761
|
standardsContent += complianceInstructions + '\n\n' + standardsIndex;
|
|
@@ -3042,13 +3043,27 @@ export function generateComplianceInstructions(
|
|
|
3042
3043
|
* @param {string} language - Language: 'en', 'zh-tw', or 'bilingual'
|
|
3043
3044
|
* @returns {string} Generated standards index
|
|
3044
3045
|
*/
|
|
3045
|
-
export function generateStandardsIndex(installedStandards, format, language = 'zh-tw') {
|
|
3046
|
+
export function generateStandardsIndex(installedStandards, format, language = 'zh-tw', standardsFormat = 'ai') {
|
|
3046
3047
|
if (!installedStandards || installedStandards.length === 0) {
|
|
3047
3048
|
return '';
|
|
3048
3049
|
}
|
|
3049
3050
|
|
|
3050
|
-
|
|
3051
|
-
|
|
3051
|
+
// Counted after resolution, so the number matches the files that exist.
|
|
3052
|
+
//
|
|
3053
|
+
// It used to be `installedStandards.length` straight off the manifest, which
|
|
3054
|
+
// includes entries with nothing behind them — the eight standards
|
|
3055
|
+
// MIGRATION-v6 §2 removed in 6.0.0 are still declared in manifests two
|
|
3056
|
+
// majors later. On dev-platform that made the block announce 78 while 70
|
|
3057
|
+
// resolve, and it announced it directly above "the authoritative list is the
|
|
3058
|
+
// `standards` field of `.standards/manifest.json`", pointing the reader at
|
|
3059
|
+
// the source of the discrepancy as if it settled it. The sync check counts
|
|
3060
|
+
// resolvable entries, so the two disagreed by exactly those eight.
|
|
3061
|
+
//
|
|
3062
|
+
// Saying 70 and naming the 8 is the honest version of both numbers.
|
|
3063
|
+
const resolvable = installedStandards.filter((e) => resolveStandardFilename(e, standardsFormat));
|
|
3064
|
+
const unresolved = installedStandards.filter((e) => !resolveStandardFilename(e, standardsFormat));
|
|
3065
|
+
const total = resolvable.length;
|
|
3066
|
+
const optionCount = resolvable.filter(
|
|
3052
3067
|
(p) => p.includes('/options/') || p.includes('\\options\\')
|
|
3053
3068
|
).length;
|
|
3054
3069
|
const coreCount = total - optionCount;
|
|
@@ -3059,12 +3074,18 @@ export function generateStandardsIndex(installedStandards, format, language = 'z
|
|
|
3059
3074
|
? [
|
|
3060
3075
|
`This project has adopted **${total}** UDS standards (${coreCount} core, ${optionCount} options), installed in \`.standards/\`.`,
|
|
3061
3076
|
'',
|
|
3062
|
-
'The authoritative list is the `standards` field of `.standards/manifest.json`. Read a standard\'s own file to see what it requires — and check its first line, which is where a deprecated standard says so.'
|
|
3077
|
+
'The authoritative list is the `standards` field of `.standards/manifest.json`. Read a standard\'s own file to see what it requires — and check its first line, which is where a deprecated standard says so.',
|
|
3078
|
+
...(unresolved.length > 0
|
|
3079
|
+
? ['', `${unresolved.length} further manifest entr${unresolved.length === 1 ? 'y has' : 'ies have'} no installed file and ${unresolved.length === 1 ? 'is' : 'are'} not counted above: ${unresolved.join(', ')}. Standards removed upstream stay in a manifest until someone drops them; \`uds update\` does not prune them.`]
|
|
3080
|
+
: [])
|
|
3063
3081
|
]
|
|
3064
3082
|
: [
|
|
3065
3083
|
`本專案採用 UDS 標準共 **${total}** 條(core ${coreCount}、options ${optionCount}),安裝於 \`.standards/\`。`,
|
|
3066
3084
|
'',
|
|
3067
|
-
'權威清單為 `.standards/manifest.json` 的 `standards` 欄位。要知道某標準要求什麼,讀它自己的檔案——並看第一行,已棄用的標準會在那裡寫明。'
|
|
3085
|
+
'權威清單為 `.standards/manifest.json` 的 `standards` 欄位。要知道某標準要求什麼,讀它自己的檔案——並看第一行,已棄用的標準會在那裡寫明。',
|
|
3086
|
+
...(unresolved.length > 0
|
|
3087
|
+
? ['', `另有 ${unresolved.length} 筆 manifest 項目沒有已安裝的檔案,未計入上方數字:${unresolved.join('、')}。上游移除的標準會留在 manifest 裡直到有人拿掉它;\`uds update\` 不會修剪它們。`]
|
|
3088
|
+
: [])
|
|
3068
3089
|
];
|
|
3069
3090
|
|
|
3070
3091
|
const plainBody = body.map((l) => l.replace(/`/g, ''));
|
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.10",
|
|
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.10"
|
|
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.10",
|
|
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.10",
|
|
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.10",
|
|
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.10",
|
|
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.10",
|
|
2276
2276
|
"source": {
|
|
2277
2277
|
"human": "core/resource-cost-boundary.md",
|
|
2278
2278
|
"ai": "ai/standards/resource-cost-boundary.ai.yaml"
|