universal-dev-standards 6.8.0 → 6.10.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/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/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/execution-history.md +8 -0
- package/bundled/core/feature-discovery-standards.md +5 -1
- package/bundled/core/governance-layer.md +114 -2
- package/bundled/core/pipeline-integration-standards.md +8 -0
- 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/core/workflow-enforcement.md +8 -0
- package/bundled/core/workflow-state-protocol.md +8 -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 +73 -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/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/governance-layer.md +118 -6
- package/bundled/locales/zh-CN/core/pipeline-integration-standards.md +7 -0
- 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/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 +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/MIGRATION-v6.md +8 -4
- 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 +74 -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/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/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/execution-history.md +7 -0
- 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/pipeline-integration-standards.md +7 -0
- 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/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 +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/MIGRATION-v6.md +8 -4
- 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 +12 -9
- package/src/commands/audit.js +82 -0
- package/src/commands/check.js +227 -34
- package/src/commands/config.js +19 -19
- package/src/commands/init.js +174 -20
- package/src/commands/spec.js +2 -2
- package/src/commands/update.js +537 -43
- 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 +42 -29
- package/src/installers/hooks-installer.js +167 -75
- package/src/installers/integration-installer.js +13 -9
- package/src/installers/skills-installer.js +4 -4
- package/src/installers/standards-installer.js +3 -3
- package/src/prompts/init.js +47 -18
- package/src/reconciler/diff-engine.js +57 -2
- package/src/reconciler/plan-executor.js +31 -11
- 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 +288 -82
- package/src/utils/reference-sync.js +107 -8
- package/src/utils/registry.js +57 -0
- package/src/utils/spinner.js +31 -0
- package/src/utils/yaml-generator.js +51 -9
- package/standards-registry.json +32 -9
|
@@ -33,9 +33,12 @@ export class ClaudeCodeCompiler extends BaseCompiler {
|
|
|
33
33
|
hooks[trigger] = [];
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
// Claude Code dispatches on `type`; an entry without one is skipped
|
|
37
|
+
// silently. Measured 2026-09-08 with both shapes in the same event of the
|
|
38
|
+
// same session: the object ran, the bare string did not.
|
|
36
39
|
hooks[trigger].push({
|
|
37
40
|
matcher,
|
|
38
|
-
hooks: [`node ${hook_script}`],
|
|
41
|
+
hooks: [{ type: 'command', command: `node ${hook_script}` }],
|
|
39
42
|
});
|
|
40
43
|
}
|
|
41
44
|
|
|
@@ -139,7 +139,19 @@ export const AI_AGENT_PATHS = {
|
|
|
139
139
|
user: join(homedir(), '.roo', 'workflows')
|
|
140
140
|
},
|
|
141
141
|
supportsMarketplace: false,
|
|
142
|
-
|
|
142
|
+
// 🔴 Was '.claude/skills/', with no provenance. Roo Code's docs ENUMERATE where it
|
|
143
|
+
// looks for skills — `.roo/skills/`, `.agents/skills/`, and their `~` twins — and
|
|
144
|
+
// `.claude/skills/` is not among them. An enumerated discovery list that omits a
|
|
145
|
+
// path is a statement, not silence (same shape as the Codex list that this field
|
|
146
|
+
// was falsified against by measurement).
|
|
147
|
+
//
|
|
148
|
+
// ⚠️ Docs only, 2026-09-08: NOT measured, because there is no Roo Code CLI on this
|
|
149
|
+
// machine. Acting on it anyway because the asymmetry is one-sided — wrong here
|
|
150
|
+
// means skills land in `.roo/skills/`, which the docs confirm is read; leaving it
|
|
151
|
+
// meant `uds init` routed Roo Code to Claude's marketplace and installed **zero**
|
|
152
|
+
// skills, which is what it actually did until this change.
|
|
153
|
+
// (roocodeinc.github.io/Roo-Code/features/skills)
|
|
154
|
+
fallbackSkillsPath: null,
|
|
143
155
|
supportsSkills: true,
|
|
144
156
|
supportsTask: true,
|
|
145
157
|
supportsAgents: true
|
|
@@ -169,7 +181,14 @@ export const AI_AGENT_PATHS = {
|
|
|
169
181
|
},
|
|
170
182
|
workflows: null,
|
|
171
183
|
supportsMarketplace: false,
|
|
172
|
-
|
|
184
|
+
// 🔴 Was '.claude/skills/'. MEASURED FALSE 2026-09-08 (XSPEC-408 §18.3): two probe
|
|
185
|
+
// arms differing only in install path. At `.agents/skills` Codex reported "Skill
|
|
186
|
+
// descriptions were shortened to fit the 2% skills context budget — Codex can
|
|
187
|
+
// still see every skill", 18,096 input tokens. At `.claude/skills` it said
|
|
188
|
+
// nothing, 15,235. The discovery list documented above never included that
|
|
189
|
+
// directory; this field asserted otherwise with no provenance, and `check.js`
|
|
190
|
+
// printed it to adopters as "Can use fallback".
|
|
191
|
+
fallbackSkillsPath: null,
|
|
173
192
|
supportsSkills: true,
|
|
174
193
|
supportsTask: false,
|
|
175
194
|
supportsAgents: true
|
|
@@ -251,28 +270,54 @@ export const AI_AGENT_PATHS = {
|
|
|
251
270
|
},
|
|
252
271
|
'antigravity': {
|
|
253
272
|
name: 'Google Antigravity',
|
|
254
|
-
|
|
255
|
-
//
|
|
256
|
-
//
|
|
257
|
-
//
|
|
258
|
-
|
|
273
|
+
// Raised from 'minimal' 2026-09-08. It was minimal because UDS installed
|
|
274
|
+
// nothing for it — the tier was describing our own gap, not the tool. It now
|
|
275
|
+
// gets 55 skills and an instruction file, the same as the other 'partial'
|
|
276
|
+
// tools, and passes all six of that tier's rules.
|
|
277
|
+
tier: 'partial',
|
|
278
|
+
// Skills path CONFIRMED 2026-09-08, three independent ways:
|
|
279
|
+
// 1. The agy 1.0.14 binary carries the literal template
|
|
280
|
+
// `{workspace}/.agents/skills/{skill_name}/SKILL.md`.
|
|
281
|
+
// 2. The guide shipped with the tool
|
|
282
|
+
// (~/.gemini/antigravity-cli/builtin/skills/antigravity_guide/references/ide.md)
|
|
283
|
+
// says `<project-root>/.agents/` holds "project-specific rules, custom skills,
|
|
284
|
+
// and plugins".
|
|
285
|
+
// 3. A live two-arm run: a skill at `.agents/skills/` was listed back with its
|
|
286
|
+
// description verbatim; a decoy at `.agent/skills/` was not listed at all.
|
|
287
|
+
//
|
|
288
|
+
// 🔴 BOTH candidates previously recorded here were wrong — the official-plugin path
|
|
289
|
+
// and `.agent/skills/` (singular). Neither had been tested; declining to install was
|
|
290
|
+
// the right call for six months, and the answer came from the tool's own binary, not
|
|
291
|
+
// from either document.
|
|
259
292
|
//
|
|
260
|
-
//
|
|
261
|
-
//
|
|
262
|
-
//
|
|
263
|
-
//
|
|
264
|
-
//
|
|
293
|
+
// 🔴 PRECONDITION the adopter must meet: agy only discovers `.agents/` when the
|
|
294
|
+
// directory is a registered Antigravity project (~/.gemini/projects.json). Measured:
|
|
295
|
+
// the same probe repo returned only the builtin skill before `agy --new-project` and
|
|
296
|
+
// both skills after. Installing here is correct, but a user who never opens the
|
|
297
|
+
// project in Antigravity will see nothing — that is the tool's condition, not ours.
|
|
265
298
|
//
|
|
266
|
-
//
|
|
267
|
-
//
|
|
268
|
-
//
|
|
269
|
-
skills:
|
|
299
|
+
// No `user` path: the binary has exactly one skills template and it is
|
|
300
|
+
// workspace-scoped. A user-level path would be invented, which is how the two wrong
|
|
301
|
+
// candidates above got written down in the first place.
|
|
302
|
+
skills: {
|
|
303
|
+
project: '.agents/skills/'
|
|
304
|
+
},
|
|
305
|
+
// Shown to the adopter after a successful install. A field rather than a
|
|
306
|
+
// special case for this tool: the next tool with a precondition fills it in
|
|
307
|
+
// instead of adding a branch. Without it the condition lives only in a spec,
|
|
308
|
+
// and the adopter — who reads neither — sees a successful install and an
|
|
309
|
+
// assistant that picked nothing up.
|
|
310
|
+
installNote:
|
|
311
|
+
'Antigravity only discovers .agents/ once the directory is a registered '
|
|
312
|
+
+ 'Antigravity project. Open it in the IDE, or run `agy --new-project` here '
|
|
313
|
+
+ 'once. Measured 2026-09-08: the same repo reported only the built-in skill '
|
|
314
|
+
+ 'before that and all of them after.',
|
|
270
315
|
commands: null,
|
|
271
316
|
agents: null,
|
|
272
317
|
workflows: null,
|
|
273
318
|
supportsMarketplace: false,
|
|
274
319
|
fallbackSkillsPath: null,
|
|
275
|
-
supportsSkills: true,
|
|
320
|
+
supportsSkills: true,
|
|
276
321
|
supportsTask: false,
|
|
277
322
|
supportsAgents: false
|
|
278
323
|
}
|
package/src/core/constants.js
CHANGED
|
@@ -94,16 +94,25 @@ export const SUPPORTED_AI_TOOLS = {
|
|
|
94
94
|
category: 'secondary',
|
|
95
95
|
supports: ['skills']
|
|
96
96
|
},
|
|
97
|
-
'roo'
|
|
98
|
-
|
|
99
|
-
|
|
97
|
+
// 🔴 Key was 'roo' and the file was 'ROO.md'. Every other module calls this tool
|
|
98
|
+
// `roo-code` (`ai-agent-paths.js`, `REGISTRY.json`, `agent-adapter.js`,
|
|
99
|
+
// `agents-installer.js`), so every lookup here MISSED — and the miss was silent:
|
|
100
|
+
// `getToolFileName()` ends in `return \`${tool}.md\``, so a real `uds init` in a Roo
|
|
101
|
+
// Code repo wrote its instructions to `roo-code.md` in the repo root. Roo Code's
|
|
102
|
+
// docs name `.roo/rules/`, `.roorules`, and `AGENTS.md`; `roo-code.md` is read by
|
|
103
|
+
// nothing, and `ROO.md` was never written by anything either.
|
|
104
|
+
// File value matches REGISTRY.json's `instructionFile` for this agent.
|
|
105
|
+
// (roocodeinc.github.io/Roo-Code/features/custom-instructions, read 2026-09-08)
|
|
106
|
+
'roo-code': {
|
|
107
|
+
name: 'Roo Code',
|
|
108
|
+
file: '.roo/rules/uds-standards.md',
|
|
100
109
|
format: 'markdown',
|
|
101
110
|
category: 'secondary',
|
|
102
111
|
supports: ['skills', 'workflows']
|
|
103
112
|
},
|
|
104
113
|
'antigravity': {
|
|
105
114
|
name: 'Antigravity',
|
|
106
|
-
file: '
|
|
115
|
+
file: '.agents/AGENTS.md',
|
|
107
116
|
format: 'markdown',
|
|
108
117
|
category: 'secondary',
|
|
109
118
|
supports: ['skills', 'workflows']
|
|
@@ -122,7 +131,12 @@ export const SUPPORTED_AI_TOOLS = {
|
|
|
122
131
|
*/
|
|
123
132
|
export const LEGACY_TOOL_MAPPINGS = {
|
|
124
133
|
'codex': 'opencode',
|
|
125
|
-
'copilot': 'github-copilot'
|
|
134
|
+
'copilot': 'github-copilot',
|
|
135
|
+
// Maps the OLD key to the CORRECT one, never the reverse — an alias pointing at
|
|
136
|
+
// the wrong name would make the wrong name authoritative. No manifest in any of
|
|
137
|
+
// the five adopting repos records 'roo' (checked 2026-09-08); this exists for
|
|
138
|
+
// manifests written before the rename.
|
|
139
|
+
'roo': 'roo-code'
|
|
126
140
|
};
|
|
127
141
|
|
|
128
142
|
/**
|
|
@@ -141,10 +155,13 @@ export const FILE_EXTENSIONS = {
|
|
|
141
155
|
/**
|
|
142
156
|
* Content modes for integration files
|
|
143
157
|
*/
|
|
158
|
+
// `FULL: 'full'` was removed here (XSPEC-357 R7). It never produced different
|
|
159
|
+
// output from INDEX — the generator's only test on the mode is `=== 'minimal'`
|
|
160
|
+
// — so it was a second name for one behaviour, offered to adopters as a
|
|
161
|
+
// distinct choice. See SUPPORTED_CONTENT_MODES in core/manifest.js.
|
|
144
162
|
export const CONTENT_MODES = {
|
|
145
163
|
MINIMAL: 'minimal', // Reference-only content
|
|
146
|
-
INDEX: 'index'
|
|
147
|
-
FULL: 'full' // Full standard content embedded
|
|
164
|
+
INDEX: 'index' // Standard index with descriptions + compliance instructions
|
|
148
165
|
};
|
|
149
166
|
|
|
150
167
|
/**
|
|
@@ -541,13 +558,27 @@ export function resolveToolKey(entry) {
|
|
|
541
558
|
if (typeof entry !== 'string' || entry.length === 0) return null;
|
|
542
559
|
if (SUPPORTED_AI_TOOLS[entry]) return entry;
|
|
543
560
|
const norm = entry.replace(/\\/g, '/');
|
|
561
|
+
|
|
562
|
+
// Exact or path-suffix only. A bare basename match would let `MY-CLAUDE.md`
|
|
563
|
+
// or `CLAUDE.md.bak` pass as the managed integration file.
|
|
564
|
+
//
|
|
565
|
+
// 🔴 Two passes, and the order matters. Antigravity's file is
|
|
566
|
+
// `.agents/AGENTS.md` while OpenCode's is `AGENTS.md`, so a single pass
|
|
567
|
+
// resolved `.agents/AGENTS.md` to OpenCode — whichever tool happened to be
|
|
568
|
+
// declared first won a path the other had spelled out in full. An exactly
|
|
569
|
+
// declared path must beat another tool's suffix, and among suffixes the
|
|
570
|
+
// longest (most specific) one wins.
|
|
571
|
+
for (const [key, cfg] of Object.entries(SUPPORTED_AI_TOOLS)) {
|
|
572
|
+
if (cfg?.file && norm === cfg.file) return key;
|
|
573
|
+
}
|
|
574
|
+
let best = null;
|
|
544
575
|
for (const [key, cfg] of Object.entries(SUPPORTED_AI_TOOLS)) {
|
|
545
576
|
if (!cfg?.file) continue;
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
577
|
+
if (norm.endsWith(`/${cfg.file}`) && (!best || cfg.file.length > best.len)) {
|
|
578
|
+
best = { key, len: cfg.file.length };
|
|
579
|
+
}
|
|
549
580
|
}
|
|
550
|
-
return null;
|
|
581
|
+
return best ? best.key : null;
|
|
551
582
|
}
|
|
552
583
|
|
|
553
584
|
/** Entry (tool key OR file path) → repo-relative integration file, or null. */
|
package/src/core/manifest.js
CHANGED
|
@@ -55,6 +55,62 @@ export const DEFAULT_MANIFEST = {
|
|
|
55
55
|
integrationBlockHashes: {}
|
|
56
56
|
};
|
|
57
57
|
|
|
58
|
+
/**
|
|
59
|
+
* Content modes an adopter may choose.
|
|
60
|
+
*
|
|
61
|
+
* `full` was retired in favour of `index` (XSPEC-357 R7). It was never a third
|
|
62
|
+
* behaviour: the only place the mode is read is a single `=== 'minimal'` test
|
|
63
|
+
* in `generateComplianceInstructions`, so `full` and `index` took the same
|
|
64
|
+
* branch and produced byte-identical files — 24 of 24 tool × language
|
|
65
|
+
* combinations, against a control confirming the comparison does detect a real
|
|
66
|
+
* difference (`full` vs `minimal` differed in all 24). Meanwhile the manifest
|
|
67
|
+
* schema accepted it, `--content-mode` offered it, the init prompt listed it as
|
|
68
|
+
* a distinct trade-off ("highest compliance, uses more context"), and every
|
|
69
|
+
* `partial`-tier tool was assigned it automatically. Adopters were choosing
|
|
70
|
+
* between two labels for one behaviour, and the label promised a difference
|
|
71
|
+
* that did not exist.
|
|
72
|
+
*/
|
|
73
|
+
export const SUPPORTED_CONTENT_MODES = ['minimal', 'index'];
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Retired content modes and what they now resolve to.
|
|
77
|
+
*
|
|
78
|
+
* The rewrite changes no adopter's output, because the two modes already
|
|
79
|
+
* generated the same bytes — which is what makes this breaking change cheap
|
|
80
|
+
* enough to do at all. Kept as a map rather than special-cased at each call
|
|
81
|
+
* site so the next retirement has somewhere to go.
|
|
82
|
+
*/
|
|
83
|
+
export const RETIRED_CONTENT_MODES = { full: 'index' };
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Resolve a content mode, migrating retired ones and flagging unknown ones.
|
|
87
|
+
*
|
|
88
|
+
* An unknown value resolves to `fallback` and is reported in `unrecognized`, so
|
|
89
|
+
* the caller can warn. The original intent was to throw — a *silent* fallback
|
|
90
|
+
* turns `--content-mode indx` into a normal run with nothing saying so — and
|
|
91
|
+
* that remains the goal for 7.0.0. It does not throw in 6.x because 6.8.0
|
|
92
|
+
* accepted unknown values and generated the `index` output, and
|
|
93
|
+
* `core/versioning.md` requires a deprecation warning in a MINOR release before
|
|
94
|
+
* behaviour that used to work starts failing. The warning is what removes the
|
|
95
|
+
* silence; the error is the MAJOR-version step.
|
|
96
|
+
*
|
|
97
|
+
* @param {string|undefined} mode - Requested mode
|
|
98
|
+
* @param {string} fallback - Mode to use when nothing was requested, or when the value is unknown
|
|
99
|
+
* @returns {{mode: string, migratedFrom: string|null, unrecognized: string|null}}
|
|
100
|
+
*/
|
|
101
|
+
export function normalizeContentMode(mode, fallback = 'index') {
|
|
102
|
+
if (mode === undefined || mode === null || mode === '') {
|
|
103
|
+
return { mode: fallback, migratedFrom: null, unrecognized: null };
|
|
104
|
+
}
|
|
105
|
+
if (SUPPORTED_CONTENT_MODES.includes(mode)) {
|
|
106
|
+
return { mode, migratedFrom: null, unrecognized: null };
|
|
107
|
+
}
|
|
108
|
+
if (Object.prototype.hasOwnProperty.call(RETIRED_CONTENT_MODES, mode)) {
|
|
109
|
+
return { mode: RETIRED_CONTENT_MODES[mode], migratedFrom: mode, unrecognized: null };
|
|
110
|
+
}
|
|
111
|
+
return { mode: fallback, migratedFrom: null, unrecognized: mode };
|
|
112
|
+
}
|
|
113
|
+
|
|
58
114
|
/**
|
|
59
115
|
* Validation schema for manifest
|
|
60
116
|
*/
|
|
@@ -63,10 +119,125 @@ const MANIFEST_SCHEMA = {
|
|
|
63
119
|
properties: {
|
|
64
120
|
version: { type: 'string', pattern: '^\\d+\\.\\d+\\.\\d+$' },
|
|
65
121
|
format: { type: 'string', enum: ['ai', 'human', 'both'] },
|
|
66
|
-
contentMode: { type: 'string', enum:
|
|
122
|
+
contentMode: { type: 'string', enum: SUPPORTED_CONTENT_MODES }
|
|
67
123
|
}
|
|
68
124
|
};
|
|
69
125
|
|
|
126
|
+
/**
|
|
127
|
+
* Provenance — the record of which files UDS itself wrote. (XSPEC-384 R1)
|
|
128
|
+
*
|
|
129
|
+
* Ownership is a question about *origin*, and origin is something you record at
|
|
130
|
+
* the moment you write, not something you infer afterwards from another table.
|
|
131
|
+
* `fileHashes` was doing the inferring: absence from it was read as "not ours",
|
|
132
|
+
* although a manifest loses entries through version migrations, hand edits,
|
|
133
|
+
* merge conflicts, and older CLI bugs. The tool then picked the destructive
|
|
134
|
+
* branch for the case it could not actually distinguish.
|
|
135
|
+
*
|
|
136
|
+
* Two properties matter and neither is shared with `fileHashes`:
|
|
137
|
+
*
|
|
138
|
+
* - It is append-only. `fileHashes` is current state and gets rewritten every
|
|
139
|
+
* run; provenance is history. If both were written and pruned together,
|
|
140
|
+
* provenance would add nothing — it would just be a second copy that fails
|
|
141
|
+
* the same way at the same moment.
|
|
142
|
+
* - It is keyed by the path actually written, taken from the copy result,
|
|
143
|
+
* not by re-deriving the path from the source name. The re-derivation is
|
|
144
|
+
* wrong for nested option files (`options/<category>/<name>.ai.yaml` is
|
|
145
|
+
* flattened to `options/<name>.ai.yaml`), so a path-math-based record would
|
|
146
|
+
* claim ownership of files that do not exist while disowning ones that do.
|
|
147
|
+
*
|
|
148
|
+
* An entry is removed only when UDS deliberately deletes the file — deleting it
|
|
149
|
+
* is how we relinquish the claim, so that a file a user later creates under the
|
|
150
|
+
* same name is not silently inherited.
|
|
151
|
+
*/
|
|
152
|
+
export const PROVENANCE_SCHEMA_VERSION = 1;
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Valid writers, used so a typo becomes an error rather than an unrecognised
|
|
156
|
+
* provenance record that reads as "not ours".
|
|
157
|
+
*/
|
|
158
|
+
export const PROVENANCE_WRITERS = ['init', 'update', 'config', 'sync'];
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Record that UDS wrote a file.
|
|
162
|
+
* @param {Object} manifest - Manifest (not mutated)
|
|
163
|
+
* @param {string} relPath - Path relative to project root
|
|
164
|
+
* @param {string} writer - One of PROVENANCE_WRITERS
|
|
165
|
+
* @returns {Object} Updated manifest
|
|
166
|
+
*/
|
|
167
|
+
export function recordFileProvenance(manifest, relPath, writer) {
|
|
168
|
+
if (!relPath) return manifest;
|
|
169
|
+
const normalized = relPath.replace(/\\/g, '/');
|
|
170
|
+
const at = new Date().toISOString();
|
|
171
|
+
const existing = manifest?.provenance?.files?.[normalized];
|
|
172
|
+
return {
|
|
173
|
+
...manifest,
|
|
174
|
+
provenance: {
|
|
175
|
+
schema: PROVENANCE_SCHEMA_VERSION,
|
|
176
|
+
...(manifest?.provenance || {}),
|
|
177
|
+
files: {
|
|
178
|
+
...(manifest?.provenance?.files || {}),
|
|
179
|
+
[normalized]: {
|
|
180
|
+
firstWrittenBy: existing?.firstWrittenBy || writer,
|
|
181
|
+
firstWrittenAt: existing?.firstWrittenAt || at,
|
|
182
|
+
lastWrittenBy: writer,
|
|
183
|
+
lastWrittenAt: at
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Drop a provenance record, relinquishing UDS's claim on a path.
|
|
192
|
+
* @param {Object} manifest - Manifest (not mutated)
|
|
193
|
+
* @param {string} relPath - Path relative to project root
|
|
194
|
+
* @returns {Object} Updated manifest
|
|
195
|
+
*/
|
|
196
|
+
export function forgetFileProvenance(manifest, relPath) {
|
|
197
|
+
const normalized = (relPath || '').replace(/\\/g, '/');
|
|
198
|
+
const files = { ...(manifest?.provenance?.files || {}) };
|
|
199
|
+
delete files[normalized];
|
|
200
|
+
return {
|
|
201
|
+
...manifest,
|
|
202
|
+
provenance: {
|
|
203
|
+
schema: PROVENANCE_SCHEMA_VERSION,
|
|
204
|
+
...(manifest?.provenance || {}),
|
|
205
|
+
files
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Mark provenance as a complete account of what UDS owns.
|
|
212
|
+
*
|
|
213
|
+
* Called at the end of a run that wrote the full installed set. Until this has
|
|
214
|
+
* happened at least once, a file with no provenance record is `unknown` rather
|
|
215
|
+
* than `foreign`, because the absence of a record proves nothing yet.
|
|
216
|
+
*
|
|
217
|
+
* @param {Object} manifest - Manifest (not mutated)
|
|
218
|
+
* @returns {Object} Updated manifest
|
|
219
|
+
*/
|
|
220
|
+
export function establishProvenance(manifest) {
|
|
221
|
+
return {
|
|
222
|
+
...manifest,
|
|
223
|
+
provenance: {
|
|
224
|
+
schema: PROVENANCE_SCHEMA_VERSION,
|
|
225
|
+
files: {},
|
|
226
|
+
...(manifest?.provenance || {}),
|
|
227
|
+
establishedAt: manifest?.provenance?.establishedAt || new Date().toISOString()
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Whether provenance can be trusted as a complete list of UDS-owned files.
|
|
234
|
+
* @param {Object} manifest - Manifest
|
|
235
|
+
* @returns {boolean}
|
|
236
|
+
*/
|
|
237
|
+
export function isProvenanceEstablished(manifest) {
|
|
238
|
+
return !!manifest?.provenance?.establishedAt;
|
|
239
|
+
}
|
|
240
|
+
|
|
70
241
|
/**
|
|
71
242
|
* Get manifest file path for a project
|
|
72
243
|
* @param {string} projectPath - Project root path
|
|
@@ -354,10 +525,25 @@ function migrateToV320(manifest) {
|
|
|
354
525
|
return {
|
|
355
526
|
...manifest,
|
|
356
527
|
version: '3.2.0',
|
|
357
|
-
contentMode: manifest.contentMode
|
|
528
|
+
contentMode: migrateRetiredContentMode(manifest.contentMode)
|
|
358
529
|
};
|
|
359
530
|
}
|
|
360
531
|
|
|
532
|
+
/**
|
|
533
|
+
* Rewrite a retired content mode to its replacement, leaving anything else
|
|
534
|
+
* alone. Unlike `normalizeContentMode` this never throws: it runs while reading
|
|
535
|
+
* a manifest, and refusing to load a project because of one stale string would
|
|
536
|
+
* be a worse outcome than loading it and letting `validateManifest` report.
|
|
537
|
+
* @param {string|undefined} mode
|
|
538
|
+
* @returns {string}
|
|
539
|
+
*/
|
|
540
|
+
function migrateRetiredContentMode(mode) {
|
|
541
|
+
if (mode && Object.prototype.hasOwnProperty.call(RETIRED_CONTENT_MODES, mode)) {
|
|
542
|
+
return RETIRED_CONTENT_MODES[mode];
|
|
543
|
+
}
|
|
544
|
+
return mode || 'index';
|
|
545
|
+
}
|
|
546
|
+
|
|
361
547
|
/**
|
|
362
548
|
* Migration to version 3.3.0
|
|
363
549
|
* @param {Object} manifest - V3.2.x manifest
|
|
@@ -502,11 +688,23 @@ function ensureRequiredFields(manifest) {
|
|
|
502
688
|
...manifest,
|
|
503
689
|
options,
|
|
504
690
|
standards,
|
|
505
|
-
|
|
691
|
+
// Rewrites a stored `full` to `index`. This is the migration path for
|
|
692
|
+
// existing adopters and it runs on every read, so a manifest written by an
|
|
693
|
+
// older CLI is corrected the first time a newer one touches it — without
|
|
694
|
+
// changing a single byte of what that project's AI tool files receive.
|
|
695
|
+
contentMode: migrateRetiredContentMode(manifest.contentMode),
|
|
506
696
|
fileHashes: normalizedHashes,
|
|
507
697
|
skillHashes: manifest.skillHashes || {},
|
|
508
698
|
commandHashes: manifest.commandHashes || {},
|
|
509
699
|
integrationBlockHashes: manifest.integrationBlockHashes || {},
|
|
700
|
+
// Present but deliberately NOT established. Filling in `establishedAt`
|
|
701
|
+
// here would declare "these records are complete" about a manifest that
|
|
702
|
+
// has never had a provenance-aware run, which is the one claim that turns
|
|
703
|
+
// every pre-existing file into `foreign` — and `foreign` is the state the
|
|
704
|
+
// deletion path acts on. Only a completed run may establish it.
|
|
705
|
+
// A fresh object per call: a shared default would be one mutable record
|
|
706
|
+
// handed to every manifest in the process. (XSPEC-384 R1)
|
|
707
|
+
provenance: manifest.provenance || { schema: PROVENANCE_SCHEMA_VERSION, files: {} },
|
|
510
708
|
};
|
|
511
709
|
}
|
|
512
710
|
|
package/src/core/paths.js
CHANGED
|
@@ -149,8 +149,8 @@ export class PathResolver {
|
|
|
149
149
|
'copilot': 'copilot-instructions.md',
|
|
150
150
|
'aider': '.aider.conf.yml',
|
|
151
151
|
'opencode': 'AGENTS.md',
|
|
152
|
-
'roo': '
|
|
153
|
-
'antigravity': '
|
|
152
|
+
'roo-code': '.roo/rules/uds-standards.md',
|
|
153
|
+
'antigravity': '.agents/AGENTS.md'
|
|
154
154
|
};
|
|
155
155
|
|
|
156
156
|
const targetFile = toolTargets[tool] || fileName;
|