universal-dev-standards 6.8.0 → 6.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/uds.js +12 -2
- package/bundled/ai/standards/acceptance-criteria-traceability.ai.yaml +14 -2
- package/bundled/ai/standards/adr-standards.ai.yaml +14 -2
- package/bundled/ai/standards/code-review.ai.yaml +13 -3
- package/bundled/ai/standards/commit-message.ai.yaml +8 -4
- package/bundled/ai/standards/deferred-item-exit.ai.yaml +225 -0
- package/bundled/ai/standards/feature-discovery-standards.ai.yaml +14 -2
- package/bundled/ai/standards/governance-layer.ai.yaml +128 -2
- package/bundled/ai/standards/logging.ai.yaml +2 -2
- package/bundled/ai/standards/retrospective-standards.ai.yaml +14 -2
- package/bundled/ai/standards/reverse-engineering-standards.ai.yaml +73 -2
- package/bundled/ai/standards/security-standards.ai.yaml +2 -2
- package/bundled/ai/standards/spec-driven-development.ai.yaml +14 -2
- package/bundled/ai/standards/tech-debt-standards.ai.yaml +87 -3
- package/bundled/ai/standards/turn-completion-integrity.ai.yaml +131 -0
- package/bundled/core/acceptance-criteria-traceability.md +5 -2
- package/bundled/core/adr-standards.md +26 -2
- package/bundled/core/code-review-checklist.md +5 -2
- package/bundled/core/context-aware-loading.md +1 -1
- package/bundled/core/deferred-item-exit.md +254 -0
- package/bundled/core/feature-discovery-standards.md +5 -1
- package/bundled/core/governance-layer.md +114 -2
- package/bundled/core/retrospective-standards.md +4 -2
- package/bundled/core/reverse-engineering-standards.md +81 -2
- package/bundled/core/spec-driven-development.md +8 -2
- package/bundled/core/tech-debt-standards.md +67 -8
- package/bundled/core/turn-completion-integrity.md +196 -0
- package/bundled/hooks/check-dangerous-cmd.mjs +60 -0
- package/bundled/hooks/check-logging-standard.mjs +59 -0
- package/bundled/hooks/check-turn-completion.mjs +233 -0
- package/bundled/hooks/inject-standards.mjs +183 -0
- package/bundled/hooks/telemetry-wrapper.mjs +77 -0
- package/bundled/hooks/turn-completion/detect.mjs +99 -0
- package/bundled/hooks/turn-completion/locales/en.mjs +159 -0
- package/bundled/hooks/turn-completion/locales/zh-TW.mjs +166 -0
- package/bundled/hooks/validate-commit-msg.mjs +104 -0
- package/bundled/locales/zh-CN/CHANGELOG.md +47 -3
- package/bundled/locales/zh-CN/CLAUDE.md +1 -1
- package/bundled/locales/zh-CN/README.md +2 -2
- package/bundled/locales/zh-CN/SECURITY.md +1 -1
- package/bundled/locales/zh-CN/core/adr-standards.md +1 -1
- package/bundled/locales/zh-CN/core/governance-layer.md +118 -6
- package/bundled/locales/zh-CN/core/retrospective-standards.md +1 -1
- package/bundled/locales/zh-CN/core/tech-debt-standards.md +71 -4
- package/bundled/locales/zh-CN/core/turn-completion-integrity.md +190 -0
- package/bundled/locales/zh-CN/docs/CHEATSHEET.md +8 -1
- package/bundled/locales/zh-CN/docs/CLI-INIT-OPTIONS.md +29 -68
- package/bundled/locales/zh-CN/docs/FEATURE-REFERENCE.md +25 -15
- package/bundled/locales/zh-CN/docs/USAGE-MODES-COMPARISON.md +1 -2
- package/bundled/locales/zh-CN/integrations/google-antigravity/{INSTRUCTIONS.md → AGENTS.md} +1 -1
- package/bundled/locales/zh-CN/integrations/google-antigravity/README.md +3 -3
- package/bundled/locales/zh-CN/skills/atdd-assistant/SKILL.md +2 -0
- package/bundled/locales/zh-CN/skills/bdd-assistant/SKILL.md +2 -0
- package/bundled/locales/zh-CN/skills/brainstorm-assistant/SKILL.md +22 -12
- package/bundled/locales/zh-CN/skills/brainstorm-assistant/guide.md +12 -9
- package/bundled/locales/zh-CN/skills/code-review-assistant/SKILL.md +1 -0
- package/bundled/locales/zh-CN/skills/commands/brainstorm.md +17 -13
- package/bundled/locales/zh-CN/skills/commands/config.md +0 -1
- package/bundled/locales/zh-CN/skills/commands/init.md +1 -2
- package/bundled/locales/zh-CN/skills/commit-standards/SKILL.md +2 -0
- package/bundled/locales/zh-CN/skills/contract-test-assistant/SKILL.md +1 -0
- package/bundled/locales/zh-CN/skills/dev-methodology/SKILL.md +2 -0
- package/bundled/locales/zh-CN/skills/observability-assistant/SKILL.md +1 -0
- package/bundled/locales/zh-CN/skills/project-structure-guide/SKILL.md +1 -0
- package/bundled/locales/zh-CN/skills/release-standards/SKILL.md +3 -0
- package/bundled/locales/zh-CN/skills/requirement-assistant/SKILL.md +2 -0
- package/bundled/locales/zh-CN/skills/reverse-engineer/SKILL.md +3 -0
- package/bundled/locales/zh-CN/skills/runbook-assistant/SKILL.md +1 -0
- package/bundled/locales/zh-CN/skills/slo-assistant/SKILL.md +1 -0
- package/bundled/locales/zh-CN/skills/tdd-assistant/SKILL.md +2 -0
- package/bundled/locales/zh-TW/CHANGELOG.md +47 -3
- package/bundled/locales/zh-TW/CLAUDE.md +1 -1
- package/bundled/locales/zh-TW/README.md +2 -2
- package/bundled/locales/zh-TW/SECURITY.md +1 -1
- package/bundled/locales/zh-TW/core/acceptance-criteria-traceability.md +2 -0
- package/bundled/locales/zh-TW/core/adr-standards.md +26 -5
- package/bundled/locales/zh-TW/core/code-review-checklist.md +2 -0
- package/bundled/locales/zh-TW/core/container-image-standards.md +2 -2
- package/bundled/locales/zh-TW/core/contract-testing-standards.md +2 -2
- package/bundled/locales/zh-TW/core/cross-flow-regression.md +8 -7
- package/bundled/locales/zh-TW/core/data-contract.md +2 -2
- package/bundled/locales/zh-TW/core/data-migration-testing.md +2 -2
- package/bundled/locales/zh-TW/core/data-pipeline.md +2 -2
- package/bundled/locales/zh-TW/core/deferred-item-exit.md +251 -0
- package/bundled/locales/zh-TW/core/documentation-writing-standards.md +228 -3
- package/bundled/locales/zh-TW/core/full-coverage-testing.md +15 -2
- package/bundled/locales/zh-TW/core/governance-layer.md +118 -5
- package/bundled/locales/zh-TW/core/iac-design-principles.md +2 -2
- package/bundled/locales/zh-TW/core/incident-response.md +2 -2
- package/bundled/locales/zh-TW/core/model-provenance.md +4 -2
- package/bundled/locales/zh-TW/core/pii-classification.md +42 -6
- package/bundled/locales/zh-TW/core/prd-standards.md +4 -2
- package/bundled/locales/zh-TW/core/product-metrics-standards.md +4 -2
- package/bundled/locales/zh-TW/core/release-readiness-gate.md +2 -2
- package/bundled/locales/zh-TW/core/resource-cost-boundary.md +2 -2
- package/bundled/locales/zh-TW/core/retrospective-standards.md +5 -3
- package/bundled/locales/zh-TW/core/reverse-engineering-standards.md +83 -5
- package/bundled/locales/zh-TW/core/runbook.md +2 -2
- package/bundled/locales/zh-TW/core/schema-evolution.md +2 -2
- package/bundled/locales/zh-TW/core/secret-management-standards.md +2 -2
- package/bundled/locales/zh-TW/core/slo-sli.md +2 -2
- package/bundled/locales/zh-TW/core/spec-driven-development.md +2 -0
- package/bundled/locales/zh-TW/core/tech-debt-standards.md +71 -4
- package/bundled/locales/zh-TW/core/turn-completion-integrity.md +190 -0
- package/bundled/locales/zh-TW/core/user-journey-testing.md +2 -2
- package/bundled/locales/zh-TW/core/user-story-mapping.md +2 -2
- package/bundled/locales/zh-TW/core/verification-oracle.md +2 -2
- package/bundled/locales/zh-TW/docs/CHEATSHEET.md +8 -1
- package/bundled/locales/zh-TW/docs/CLI-INIT-OPTIONS.md +29 -68
- package/bundled/locales/zh-TW/docs/FEATURE-REFERENCE.md +25 -15
- package/bundled/locales/zh-TW/docs/USAGE-MODES-COMPARISON.md +1 -2
- package/bundled/locales/zh-TW/integrations/google-antigravity/{INSTRUCTIONS.md → AGENTS.md} +1 -1
- package/bundled/locales/zh-TW/integrations/google-antigravity/README.md +3 -3
- package/bundled/locales/zh-TW/skills/adr-assistant/SKILL.md +1 -1
- package/bundled/locales/zh-TW/skills/atdd-assistant/SKILL.md +2 -0
- package/bundled/locales/zh-TW/skills/bdd-assistant/SKILL.md +2 -0
- package/bundled/locales/zh-TW/skills/brainstorm-assistant/SKILL.md +22 -12
- package/bundled/locales/zh-TW/skills/brainstorm-assistant/guide.md +12 -9
- package/bundled/locales/zh-TW/skills/code-review-assistant/SKILL.md +1 -0
- package/bundled/locales/zh-TW/skills/commands/brainstorm.md +17 -13
- package/bundled/locales/zh-TW/skills/commands/config.md +0 -1
- package/bundled/locales/zh-TW/skills/commands/init.md +1 -2
- package/bundled/locales/zh-TW/skills/commit-standards/SKILL.md +2 -0
- package/bundled/locales/zh-TW/skills/contract-test-assistant/SKILL.md +2 -1
- package/bundled/locales/zh-TW/skills/dev-methodology/SKILL.md +2 -0
- package/bundled/locales/zh-TW/skills/dev-workflow-guide/SKILL.md +1 -1
- package/bundled/locales/zh-TW/skills/knowledge-graph/guide.md +2 -2
- package/bundled/locales/zh-TW/skills/migration-assistant/SKILL.md +1 -1
- package/bundled/locales/zh-TW/skills/observability-assistant/SKILL.md +1 -0
- package/bundled/locales/zh-TW/skills/project-discovery/SKILL.md +1 -0
- package/bundled/locales/zh-TW/skills/project-structure-guide/SKILL.md +1 -0
- package/bundled/locales/zh-TW/skills/release-standards/SKILL.md +3 -0
- package/bundled/locales/zh-TW/skills/requirement-assistant/SKILL.md +2 -0
- package/bundled/locales/zh-TW/skills/reverse-engineer/SKILL.md +3 -0
- package/bundled/locales/zh-TW/skills/runbook-assistant/SKILL.md +1 -0
- package/bundled/locales/zh-TW/skills/slo-assistant/SKILL.md +1 -0
- package/bundled/locales/zh-TW/skills/tdd-assistant/SKILL.md +2 -0
- package/bundled/skills/atdd-assistant/SKILL.md +2 -0
- package/bundled/skills/bdd-assistant/SKILL.md +2 -0
- package/bundled/skills/brainstorm-assistant/SKILL.md +31 -13
- package/bundled/skills/brainstorm-assistant/guide.md +9 -6
- package/bundled/skills/code-review-assistant/SKILL.md +1 -0
- package/bundled/skills/commands/brainstorm.md +12 -9
- package/bundled/skills/commands/config.md +0 -1
- package/bundled/skills/commands/init.md +2 -3
- package/bundled/skills/commit-standards/SKILL.md +2 -0
- package/bundled/skills/contract-test-assistant/SKILL.md +1 -0
- package/bundled/skills/dev-methodology/SKILL.md +4 -0
- package/bundled/skills/observability-assistant/SKILL.md +1 -0
- package/bundled/skills/project-discovery/SKILL.md +1 -0
- package/bundled/skills/project-structure-guide/SKILL.md +1 -0
- package/bundled/skills/release-standards/SKILL.md +3 -0
- package/bundled/skills/requirement-assistant/SKILL.md +2 -0
- package/bundled/skills/reverse-engineer/SKILL.md +3 -0
- package/bundled/skills/runbook-assistant/SKILL.md +1 -0
- package/bundled/skills/slo-assistant/SKILL.md +1 -0
- package/bundled/skills/tdd-assistant/SKILL.md +2 -0
- package/bundled/templates/.ai-context.yaml.template +194 -0
- package/bundled/templates/CLAUDE.md.template +145 -0
- package/bundled/templates/DESIGN.md +237 -0
- package/bundled/templates/SKILL-BRIEF-TEMPLATE.md +57 -0
- package/bundled/templates/SKILL-CANDIDATES.md +39 -0
- package/bundled/templates/gates/check-error-exit.mjs +309 -0
- package/bundled/templates/mcp-config.json +10 -0
- package/bundled/templates/methodology-template.yaml +209 -0
- package/bundled/templates/migration-template.md +408 -0
- package/bundled/templates/requirement-checklist.md +410 -0
- package/bundled/templates/requirement-document-template.md +591 -0
- package/bundled/templates/requirement-template.md +881 -0
- package/bundled/templates/reverse-spec-template.md +409 -0
- package/bundled/templates/test-case-template.md +74 -0
- package/bundled/templates/test-plan-template.md +74 -0
- package/package.json +7 -5
- package/src/commands/audit.js +82 -0
- package/src/commands/check.js +66 -10
- package/src/commands/init.js +161 -16
- package/src/commands/update.js +286 -14
- package/src/compilers/claude-code-compiler.js +4 -1
- package/src/config/ai-agent-paths.js +62 -17
- package/src/core/constants.js +42 -11
- package/src/core/manifest.js +201 -3
- package/src/core/paths.js +2 -2
- package/src/i18n/messages.js +6 -29
- package/src/installers/hooks-installer.js +167 -75
- package/src/installers/integration-installer.js +9 -5
- package/src/prompts/init.js +14 -14
- package/src/utils/detector.js +21 -1
- package/src/utils/effect-boundary.js +1093 -0
- package/src/utils/hasher.js +166 -1
- package/src/utils/hook-stats.js +1 -1
- package/src/utils/integration-generator.js +79 -1
- package/src/utils/reference-sync.js +4 -1
- package/src/utils/yaml-generator.js +51 -9
- package/standards-registry.json +31 -8
package/src/commands/check.js
CHANGED
|
@@ -29,7 +29,8 @@ import {
|
|
|
29
29
|
parseReferences,
|
|
30
30
|
compareStandardsWithReferences
|
|
31
31
|
} from '../utils/reference-sync.js';
|
|
32
|
-
import { extractMarkedContent, getToolFilePath, parseStandardsIndexCount } from '../utils/integration-generator.js';
|
|
32
|
+
import { extractMarkedContent, getToolFilePath, parseStandardsIndexCount, writeIntegrationFile } from '../utils/integration-generator.js';
|
|
33
|
+
import { INTEGRATION_MAPPINGS } from '../installers/integration-installer.js';
|
|
33
34
|
import { getToolFormat } from '../core/constants.js';
|
|
34
35
|
import { checkForUpdates } from '../utils/npm-registry.js';
|
|
35
36
|
import { writeUpdateCache } from '../utils/update-checker.js';
|
|
@@ -415,6 +416,9 @@ export async function checkCommand(options = {}) {
|
|
|
415
416
|
// XSPEC-178: Full coverage compliance check
|
|
416
417
|
checkFullCoverageCompliance(manifest, projectPath);
|
|
417
418
|
|
|
419
|
+
// 錯誤訊息單一出口閘門是否已安裝(只報告,不寫入)
|
|
420
|
+
checkErrorExitGate(projectPath);
|
|
421
|
+
|
|
418
422
|
// Workflow status
|
|
419
423
|
displayWorkflowStatus(projectPath);
|
|
420
424
|
|
|
@@ -664,6 +668,27 @@ export async function restoreSingleFile(projectPath, manifest, relativePath, msg
|
|
|
664
668
|
msg = t().commands.check;
|
|
665
669
|
}
|
|
666
670
|
|
|
671
|
+
// 🔴 Integration files are GENERATED, not copied. Restoring one by downloading a
|
|
672
|
+
// repo template overwrites the adopter's file with a document they never had —
|
|
673
|
+
// `integrations/claude-code/CLAUDE.md` is 140 lines last touched 2026-03-25, and no
|
|
674
|
+
// code path writes it at install time. The installer already records how each file
|
|
675
|
+
// was generated (`manifest.integrationConfigs`), so a correct restore regenerates
|
|
676
|
+
// from that, exactly as `uds update --sync-refs` does.
|
|
677
|
+
const genConfig = manifest.integrationConfigs?.[relativePath];
|
|
678
|
+
if (genConfig?.tool) {
|
|
679
|
+
const result = writeIntegrationFile(genConfig.tool, {
|
|
680
|
+
...genConfig,
|
|
681
|
+
installedStandards: genConfig.installedStandards || manifest.standards || []
|
|
682
|
+
}, projectPath);
|
|
683
|
+
if (result.success) {
|
|
684
|
+
updateFileHash(projectPath, manifest, relativePath);
|
|
685
|
+
console.log(chalk.green(` ✓ ${relativePath}: ${msg.restored}`));
|
|
686
|
+
return true;
|
|
687
|
+
}
|
|
688
|
+
console.log(chalk.red(` ✗ ${relativePath}: ${result.error}`));
|
|
689
|
+
return false;
|
|
690
|
+
}
|
|
691
|
+
|
|
667
692
|
const sourcePath = getSourcePathFromRelative(manifest, relativePath);
|
|
668
693
|
if (!sourcePath) {
|
|
669
694
|
console.log(chalk.red(` ✗ ${relativePath}: ${msg.couldNotDetermineSource}`));
|
|
@@ -775,15 +800,18 @@ export function getSourcePathFromRelative(manifest, relativePath) {
|
|
|
775
800
|
|
|
776
801
|
// Check integrations - these might need special handling
|
|
777
802
|
if (manifest.integrations.some(i => (resolveIntegrationFile(i) || i) === relativePath)) {
|
|
778
|
-
//
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
803
|
+
// 🔴 This was a hand-copied list of five targets. The installer's own
|
|
804
|
+
// INTEGRATION_MAPPINGS covers eight and is the map actually used when a
|
|
805
|
+
// generation falls back to a static copy, so a second copy here could only
|
|
806
|
+
// drift — and had: it named `CLAUDE.md`, which INTEGRATION_MAPPINGS does not
|
|
807
|
+
// contain because no code path ever writes that template.
|
|
808
|
+
//
|
|
809
|
+
// Reached only for legacy manifests with no `integrationConfigs`; a current
|
|
810
|
+
// install regenerates instead (see restoreSingleFile).
|
|
811
|
+
for (const { source, target } of Object.values(INTEGRATION_MAPPINGS)) {
|
|
812
|
+
if (target === relativePath) return source;
|
|
813
|
+
}
|
|
814
|
+
return null;
|
|
787
815
|
}
|
|
788
816
|
|
|
789
817
|
return null;
|
|
@@ -1003,6 +1031,34 @@ function displaySkillsStatus(manifest, projectPath, msg) {
|
|
|
1003
1031
|
return { missingSkills, missingCommands };
|
|
1004
1032
|
}
|
|
1005
1033
|
|
|
1034
|
+
/**
|
|
1035
|
+
* 錯誤訊息單一出口閘門:**偵測並報告,絕不寫入。**
|
|
1036
|
+
*
|
|
1037
|
+
* 🔴 為什麼只報告:`uds update` 之外沒有任何時刻是採用者同意我們動他的 repo 的。
|
|
1038
|
+
* 未經同意往別人的專案寫可執行腳本,比它要修的那個問題更糟。
|
|
1039
|
+
* 所以這裡只說「你沒有這道閘門」與「怎麼拿」,寫入留給 `uds update`,
|
|
1040
|
+
* 而那裡會顯示內容並要人點頭。
|
|
1041
|
+
*
|
|
1042
|
+
* 起因(VibeOps 2026-09-03):使用者照著建議打上名字,畫面回他一句 `Bad Request`。
|
|
1043
|
+
* 真正的原因寫在回應的 `details` 裡,而呼叫端只讀了 `error`。當天的修法改了一個
|
|
1044
|
+
* 呼叫端;走訪之後發現同型的有二十四個——其中三個在同一個檔案裡。
|
|
1045
|
+
*/
|
|
1046
|
+
function checkErrorExitGate(projectPath) {
|
|
1047
|
+
const gatePath = join(projectPath, 'scripts', 'check-error-exit.mjs');
|
|
1048
|
+
if (existsSync(gatePath)) {
|
|
1049
|
+
return; // 裝了就不出聲。這裡的沉默代表「有」,不代表「跑過了」——那是它自己的事。
|
|
1050
|
+
}
|
|
1051
|
+
// 沒有 src/ 的專案(純文件、純設定)本來就沒有這個問題,不要對他們嘮叨。
|
|
1052
|
+
if (!existsSync(join(projectPath, 'src'))) {
|
|
1053
|
+
return;
|
|
1054
|
+
}
|
|
1055
|
+
console.log(chalk.yellow(' ⚠ [error-exit] 沒有錯誤訊息單一出口檢查(scripts/check-error-exit.mjs)。'));
|
|
1056
|
+
console.log(chalk.gray(' 這道閘門防的是「每個呼叫端各自把錯誤回應拼成給人看的字串」——'));
|
|
1057
|
+
console.log(chalk.gray(' 第一處是實作,第二處開始就會各寫各的,而畫面上只剩一句 Bad Request。'));
|
|
1058
|
+
console.log(chalk.gray(' 要裝的話:`uds update` 會顯示內容並徵求同意後寫入。'));
|
|
1059
|
+
console.log();
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1006
1062
|
/**
|
|
1007
1063
|
* XSPEC-178: Check full-coverage-testing standard presence and STUB markers
|
|
1008
1064
|
*/
|
package/src/commands/init.js
CHANGED
|
@@ -4,7 +4,9 @@ import { execSync } from 'child_process';
|
|
|
4
4
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
5
5
|
import { basename, join } from 'path';
|
|
6
6
|
import {
|
|
7
|
-
manifestExists as isInitialized
|
|
7
|
+
manifestExists as isInitialized,
|
|
8
|
+
normalizeContentMode,
|
|
9
|
+
SUPPORTED_CONTENT_MODES
|
|
8
10
|
} from '../core/manifest.js';
|
|
9
11
|
import { t, detectLanguage } from '../i18n/messages.js';
|
|
10
12
|
import { detectAll } from '../utils/detector.js';
|
|
@@ -156,9 +158,29 @@ export async function initCommand(options) {
|
|
|
156
158
|
skillHashes: {},
|
|
157
159
|
commandHashes: {}
|
|
158
160
|
};
|
|
161
|
+
// A detected tool that can take skills and has no verified path gets
|
|
162
|
+
// nothing — and must be told so. Silence was the actual defect measured
|
|
163
|
+
// on 2026-09-08: `--mode skills` installed nothing, printed no skills
|
|
164
|
+
// line, and exited 0. An adopter cannot know to go looking.
|
|
165
|
+
for (const tool of config.skillsConfig.skillsPathUnknownTools || []) {
|
|
166
|
+
console.log(chalk.yellow(
|
|
167
|
+
`⚠ ${getAgentDisplayName(tool) || tool}: ` +
|
|
168
|
+
`${msg.skillsPathUnknown || 'no verified skills path — nothing was installed for this tool'}`
|
|
169
|
+
));
|
|
170
|
+
}
|
|
159
171
|
await installSkills(config.skillsConfig, projectPath, msg, skillsResults);
|
|
160
172
|
await installCommands(config.skillsConfig, projectPath, msg, skillsResults);
|
|
161
173
|
|
|
174
|
+
// A tool may need something from the adopter before it will read what
|
|
175
|
+
// we just wrote. Driven by an `installNote` on the path table, so a new
|
|
176
|
+
// precondition is a field and not a branch here.
|
|
177
|
+
for (const { agent } of config.skillsConfig.skillsInstallations || []) {
|
|
178
|
+
const note = getAgentConfig(agent)?.installNote;
|
|
179
|
+
if (note) {
|
|
180
|
+
console.log(chalk.yellow(` ⚠ ${getAgentDisplayName(agent) || agent}: ${note}`));
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
162
184
|
// Combine results
|
|
163
185
|
installErrors = [
|
|
164
186
|
...standardsResults.errors,
|
|
@@ -225,7 +247,25 @@ export async function initCommand(options) {
|
|
|
225
247
|
const { installHooks } = await import('../installers/hooks-installer.js');
|
|
226
248
|
const hookResult = installHooks(projectPath);
|
|
227
249
|
if (hookResult.installed) {
|
|
228
|
-
console.log(chalk.green(
|
|
250
|
+
console.log(chalk.green(
|
|
251
|
+
` ✓ Enforcement hooks installed (${hookResult.scriptsCount} scripts, `
|
|
252
|
+
+ `${hookResult.events.join(', ')})`));
|
|
253
|
+
} else {
|
|
254
|
+
// Silence here is how three broken installs reported success.
|
|
255
|
+
console.log(chalk.yellow(' ⚠ No enforcement hooks installed — no standard produced a usable hook.'));
|
|
256
|
+
}
|
|
257
|
+
for (const s of hookResult.skipped ?? []) {
|
|
258
|
+
console.log(chalk.yellow(` · skipped ${s.id}: ${s.why}`));
|
|
259
|
+
}
|
|
260
|
+
// turn-completion-integrity R8: a prose-reading hook in a language it does
|
|
261
|
+
// not ship is installed, running, and unable to fire — and that looks
|
|
262
|
+
// exactly like a turn with nothing wrong. Say so at install time; there is
|
|
263
|
+
// no later moment where the adopter could find out.
|
|
264
|
+
for (const l of hookResult.languageLimits ?? []) {
|
|
265
|
+
console.log(chalk.yellow(` · ${l.script} reads prose, and ships:`));
|
|
266
|
+
for (const line of l.languages.split('\n')) {
|
|
267
|
+
console.log(chalk.gray(` ${line}`));
|
|
268
|
+
}
|
|
229
269
|
}
|
|
230
270
|
}
|
|
231
271
|
|
|
@@ -433,16 +473,95 @@ function buildNonInteractiveConfig(options, detected, projectPath) {
|
|
|
433
473
|
});
|
|
434
474
|
|
|
435
475
|
// Skills Configuration Logic
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
476
|
+
//
|
|
477
|
+
// 🔴 Which tools can take skills is asked of the path table, never listed here.
|
|
478
|
+
// This used to read `t === 'claude-code' || t === 'opencode'`, and the two
|
|
479
|
+
// consequences were both measured on 2026-09-08 (XSPEC-408 §18.3):
|
|
480
|
+
//
|
|
481
|
+
// - A repo detected as codex ran `uds init --mode skills -y` and got **zero
|
|
482
|
+
// skills, in any directory, with no message saying so.**
|
|
483
|
+
// - When a location was passed explicitly, the install went to `.claude/skills/`
|
|
484
|
+
// for every tool, because the branch below never built `skillsInstallations`
|
|
485
|
+
// and `installSkills()` then fell through to the legacy path, whose target is
|
|
486
|
+
// the hardcoded `getProjectSkillsDir()`. Codex does not read that directory:
|
|
487
|
+
// two arms differing only in path gave 15,235 vs 18,096 input tokens, and only
|
|
488
|
+
// the `.agents/skills` arm made Codex say it could see the skills at all.
|
|
489
|
+
//
|
|
490
|
+
// 🔴 This is the second time in the same shape. `ai-agent-paths.js` records the
|
|
491
|
+
// first: `.codex/skills/` was "a directory UDS invented", skills there were
|
|
492
|
+
// invisible, and a behavioural probe failed while Codex behaved correctly. That
|
|
493
|
+
// fix corrected the path table. This one broke in the code that never asked it.
|
|
494
|
+
// The predicate below is the same one `promptSkillsInstallLocation` already used —
|
|
495
|
+
// the interactive flow was never wrong.
|
|
496
|
+
const skillsCapableTools = aiToolsNormalized.filter(tool => {
|
|
497
|
+
const config = getAgentConfig(tool);
|
|
498
|
+
return Boolean(config?.supportsSkills && config?.skills);
|
|
499
|
+
});
|
|
500
|
+
const hasSkillsCompatibleTool = skillsCapableTools.length > 0;
|
|
501
|
+
// Marketplace is Claude Code's. A tool reaches it either by having one
|
|
502
|
+
// (`supportsMarketplace`) or by reading Claude's directory (`fallbackSkillsPath`).
|
|
503
|
+
// ⚠️ Those fallback claims are NOT all verified — `opencode`'s carries the bare
|
|
504
|
+
// comment "Can read Claude skills" and no evidence. It is preserved here rather
|
|
505
|
+
// than flipped, because changing behaviour on an unverified field in either
|
|
506
|
+
// direction is a guess. Codex's identical claim WAS measured, and falsified, so
|
|
507
|
+
// its field is now null (see `ai-agent-paths.js`) and it drops out here by data.
|
|
508
|
+
// 🔴 The marketplace is all-or-nothing for a repo: it installs into Claude's
|
|
509
|
+
// directory, so it serves a tool only if that tool has a marketplace of its own or
|
|
510
|
+
// declares Claude's directory as its fallback. If ANY detected tool is not served
|
|
511
|
+
// by it, file installs are used instead — every tool then gets its own path, and
|
|
512
|
+
// nobody is left with a successful init and an empty directory.
|
|
513
|
+
const reachesMarketplace = t => {
|
|
514
|
+
const c = getAgentConfig(t);
|
|
515
|
+
return Boolean(c?.supportsMarketplace || c?.fallbackSkillsPath);
|
|
516
|
+
};
|
|
517
|
+
const onlySkillsCompatibleTools =
|
|
518
|
+
hasSkillsCompatibleTool && aiToolsNormalized.every(reachesMarketplace);
|
|
519
|
+
|
|
439
520
|
let skillsLocationFlag = options.skillsLocation;
|
|
440
521
|
if (!skillsLocationFlag) {
|
|
441
|
-
skillsLocationFlag =
|
|
522
|
+
skillsLocationFlag = hasSkillsCompatibleTool
|
|
523
|
+
? (onlySkillsCompatibleTools ? 'marketplace' : 'project')
|
|
524
|
+
: 'none';
|
|
442
525
|
}
|
|
443
526
|
|
|
444
|
-
|
|
527
|
+
// A detected tool that can take skills and is getting none must SAY SO. The worst
|
|
528
|
+
// property of the old default was not the wrong directory, it was the silence:
|
|
529
|
+
// `--mode skills` printed no skills line at all and exited 0.
|
|
530
|
+
const skippedSkillsTools = aiToolsNormalized.filter(tool => {
|
|
531
|
+
const config = getAgentConfig(tool);
|
|
532
|
+
return Boolean(config?.supportsSkills) && !config?.skills;
|
|
533
|
+
});
|
|
534
|
+
// Carried on the config and printed by the caller — this function builds config
|
|
535
|
+
// and has no message bundle in scope. (First version called `msg` here and every
|
|
536
|
+
// init that detected a pathless tool crashed with `ReferenceError: msg is not
|
|
537
|
+
// defined`; the gate's own control arm missed it, because the string it looked for
|
|
538
|
+
// appeared in the stack trace.)
|
|
539
|
+
|
|
540
|
+
// `auto` is resolved later per tool by resolveContentModeForTool, so it is
|
|
541
|
+
// not a content mode and must not go through the normalizer.
|
|
542
|
+
let contentModeFlag = options.contentMode || 'auto';
|
|
543
|
+
if (contentModeFlag !== 'auto') {
|
|
544
|
+
// An unrecognised value is never substituted quietly: a typo that still
|
|
545
|
+
// produces a normal-looking run is how `--content-mode` would lie about
|
|
546
|
+
// what it generated. (XSPEC-357 R7) In 6.x it warns and keeps 6.8.0's
|
|
547
|
+
// result (`index`); it becomes an error in 7.0.0 — see normalizeContentMode.
|
|
548
|
+
const normalized = normalizeContentMode(contentModeFlag);
|
|
549
|
+
if (normalized.unrecognized) {
|
|
550
|
+
console.log(chalk.yellow(
|
|
551
|
+
`⚠ Unknown content mode '${normalized.unrecognized}'; using '${normalized.mode}'. ` +
|
|
552
|
+
`Supported: ${SUPPORTED_CONTENT_MODES.join(', ')}. This will be an error in 7.0.0.`
|
|
553
|
+
));
|
|
554
|
+
}
|
|
555
|
+
if (normalized.migratedFrom) {
|
|
556
|
+
console.log(chalk.yellow(
|
|
557
|
+
`⚠ --content-mode ${normalized.migratedFrom} is retired; using '${normalized.mode}'. ` +
|
|
558
|
+
'The two produced identical files, so nothing about your output changes.'
|
|
559
|
+
));
|
|
560
|
+
}
|
|
561
|
+
contentModeFlag = normalized.mode;
|
|
562
|
+
}
|
|
445
563
|
let skillsConfig = {};
|
|
564
|
+
let skillsInstallationsForYes = [];
|
|
446
565
|
|
|
447
566
|
if (skillsLocationFlag === 'marketplace') {
|
|
448
567
|
skillsConfig = {
|
|
@@ -472,14 +591,39 @@ function buildNonInteractiveConfig(options, detected, projectPath) {
|
|
|
472
591
|
location = 'project';
|
|
473
592
|
}
|
|
474
593
|
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
594
|
+
// 🔴 One installation per skills-capable detected tool, each at that tool's own
|
|
595
|
+
// path from the table. Without this the config carries only `updateTargets`,
|
|
596
|
+
// `installSkills()` takes its legacy branch, and every tool's skills land in
|
|
597
|
+
// `.claude/skills/` — see the block above for what that measured.
|
|
598
|
+
skillsInstallationsForYes = skillsCapableTools.map(agent => ({ agent, level: location }));
|
|
599
|
+
|
|
600
|
+
// 🔴 Detected tools, none of which can take skills, is NOT the same as "no tools
|
|
601
|
+
// detected". The first is knowledge — install nothing and say why. The second is
|
|
602
|
+
// ignorance, and there the historical `.claude/skills/` fallback is the least-bad
|
|
603
|
+
// guess and is kept. Without this split, `--skills-location project` in an
|
|
604
|
+
// Antigravity-only repo wrote 55 skills into `.claude/skills/`, a directory
|
|
605
|
+
// nothing in that repo reads. Caught by `check:install-paths`, not by me.
|
|
606
|
+
const knowinglyUnsupported = skillsCapableTools.length === 0 && aiToolsNormalized.length > 0;
|
|
607
|
+
|
|
608
|
+
skillsConfig = knowinglyUnsupported
|
|
609
|
+
? {
|
|
610
|
+
installed: false,
|
|
611
|
+
location: null,
|
|
612
|
+
needsInstall: false,
|
|
613
|
+
updateTargets: [],
|
|
614
|
+
skillsInstallations: [],
|
|
615
|
+
standardsScope: 'full',
|
|
616
|
+
contentMode: contentModeFlag
|
|
617
|
+
}
|
|
618
|
+
: {
|
|
619
|
+
installed: true,
|
|
620
|
+
location,
|
|
621
|
+
skillsInstallations: skillsInstallationsForYes,
|
|
622
|
+
needsInstall: skillsLocationFlag === 'project' || skillsLocationFlag === 'user' || (!userSkillsInfo?.installed && !projectSkillsInfo?.installed),
|
|
623
|
+
updateTargets: [location],
|
|
624
|
+
standardsScope: 'minimal',
|
|
625
|
+
contentMode: contentModeFlag
|
|
626
|
+
};
|
|
483
627
|
}
|
|
484
628
|
|
|
485
629
|
// Auto-install commands
|
|
@@ -496,6 +640,7 @@ function buildNonInteractiveConfig(options, detected, projectPath) {
|
|
|
496
640
|
}
|
|
497
641
|
|
|
498
642
|
skillsConfig.locale = displayLanguageToLocale(displayLanguage);
|
|
643
|
+
skillsConfig.skillsPathUnknownTools = skippedSkillsTools;
|
|
499
644
|
|
|
500
645
|
// AGENTS.md: default to true in --yes mode unless codex/opencode selected or --no-agents-md
|
|
501
646
|
// When codex/opencode is selected, they already generate AGENTS.md — no need for universal output
|
|
@@ -639,7 +784,7 @@ function displaySummary(config, msg, common) {
|
|
|
639
784
|
}
|
|
640
785
|
|
|
641
786
|
// 9. Content Mode (STEP 11)
|
|
642
|
-
const contentModeLabels = t().contentMode?.labels || { index: 'Standard',
|
|
787
|
+
const contentModeLabels = t().contentMode?.labels || { index: 'Standard', minimal: 'Minimal' };
|
|
643
788
|
console.log(chalk.gray(` ${msg.contentModeLabel}: ${contentModeLabels[config.contentMode] || config.contentMode}`));
|
|
644
789
|
|
|
645
790
|
// 10. Methodology (STEP 12, experimental)
|