@namewta/speculo 0.7.4 → 0.7.5

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.
Files changed (157) hide show
  1. package/README.md +2 -2
  2. package/package.json +1 -1
  3. package/template/canonical/README.md +208 -63
  4. package/template/canonical/canonical-person-steelman-deliberation.md +518 -0
  5. package/template/skills/engineering-standards-builder/README.md +43 -0
  6. package/template/skills/engineering-standards-builder/SKILL.md +233 -0
  7. package/template/skills/engineering-standards-builder/examples/README.md +15 -0
  8. package/template/skills/engineering-standards-builder/examples/fallback/kotlin-gradle/build.gradle.kts +5 -0
  9. package/template/skills/engineering-standards-builder/examples/fallback/kotlin-gradle/expected.json +12 -0
  10. package/template/skills/engineering-standards-builder/examples/fallback/kotlin-gradle/src/main/kotlin/example/App.kt +2 -0
  11. package/template/skills/engineering-standards-builder/examples/go/service/.golangci.yml +3 -0
  12. package/template/skills/engineering-standards-builder/examples/go/service/cmd/api/main.go +3 -0
  13. package/template/skills/engineering-standards-builder/examples/go/service/expected.json +23 -0
  14. package/template/skills/engineering-standards-builder/examples/go/service/go.mod +3 -0
  15. package/template/skills/engineering-standards-builder/examples/go/service/internal/service/service.go +2 -0
  16. package/template/skills/engineering-standards-builder/examples/go/service/internal/service/service_test.go +3 -0
  17. package/template/skills/engineering-standards-builder/examples/java/spring-boot-maven/expected.json +22 -0
  18. package/template/skills/engineering-standards-builder/examples/java/spring-boot-maven/pom.xml +12 -0
  19. package/template/skills/engineering-standards-builder/examples/java/spring-boot-maven/src/main/java/dev/speculo/orders/OrdersApplication.java +7 -0
  20. package/template/skills/engineering-standards-builder/examples/java/spring-boot-maven/src/test/java/dev/speculo/orders/OrdersApplicationTest.java +3 -0
  21. package/template/skills/engineering-standards-builder/examples/polyglot/monorepo/.github/workflows/ci.yml +11 -0
  22. package/template/skills/engineering-standards-builder/examples/polyglot/monorepo/apps/web/package.json +11 -0
  23. package/template/skills/engineering-standards-builder/examples/polyglot/monorepo/apps/web/src/App.vue +2 -0
  24. package/template/skills/engineering-standards-builder/examples/polyglot/monorepo/apps/web/tsconfig.json +8 -0
  25. package/template/skills/engineering-standards-builder/examples/polyglot/monorepo/expected.json +22 -0
  26. package/template/skills/engineering-standards-builder/examples/polyglot/monorepo/package.json +11 -0
  27. package/template/skills/engineering-standards-builder/examples/polyglot/monorepo/pnpm-workspace.yaml +2 -0
  28. package/template/skills/engineering-standards-builder/examples/polyglot/monorepo/services/orders/pom.xml +12 -0
  29. package/template/skills/engineering-standards-builder/examples/polyglot/monorepo/services/orders/src/main/java/dev/speculo/orders/OrdersApplication.java +7 -0
  30. package/template/skills/engineering-standards-builder/examples/polyglot/monorepo/services/worker/go.mod +3 -0
  31. package/template/skills/engineering-standards-builder/examples/polyglot/monorepo/services/worker/main.go +2 -0
  32. package/template/skills/engineering-standards-builder/examples/rust/workspace/Cargo.toml +7 -0
  33. package/template/skills/engineering-standards-builder/examples/rust/workspace/crates/cli/Cargo.toml +8 -0
  34. package/template/skills/engineering-standards-builder/examples/rust/workspace/crates/cli/src/main.rs +1 -0
  35. package/template/skills/engineering-standards-builder/examples/rust/workspace/crates/core/Cargo.toml +5 -0
  36. package/template/skills/engineering-standards-builder/examples/rust/workspace/crates/core/src/lib.rs +3 -0
  37. package/template/skills/engineering-standards-builder/examples/rust/workspace/expected.json +14 -0
  38. package/template/skills/engineering-standards-builder/examples/rust/workspace/rust-toolchain.toml +3 -0
  39. package/template/skills/engineering-standards-builder/examples/typescript/react-vite/expected.json +21 -0
  40. package/template/skills/engineering-standards-builder/examples/typescript/react-vite/package.json +20 -0
  41. package/template/skills/engineering-standards-builder/examples/typescript/react-vite/src/App.test.tsx +2 -0
  42. package/template/skills/engineering-standards-builder/examples/typescript/react-vite/src/App.tsx +1 -0
  43. package/template/skills/engineering-standards-builder/examples/typescript/react-vite/src/main.tsx +3 -0
  44. package/template/skills/engineering-standards-builder/examples/typescript/react-vite/tsconfig.json +10 -0
  45. package/template/skills/engineering-standards-builder/examples/typescript/vue-vite/expected.json +22 -0
  46. package/template/skills/engineering-standards-builder/examples/typescript/vue-vite/package.json +21 -0
  47. package/template/skills/engineering-standards-builder/examples/typescript/vue-vite/src/App.test.ts +2 -0
  48. package/template/skills/engineering-standards-builder/examples/typescript/vue-vite/src/App.vue +7 -0
  49. package/template/skills/engineering-standards-builder/examples/typescript/vue-vite/src/main.ts +4 -0
  50. package/template/skills/engineering-standards-builder/examples/typescript/vue-vite/tsconfig.json +11 -0
  51. package/template/skills/engineering-standards-builder/examples/typescript/vue-vite/vite.config.ts +2 -0
  52. package/template/skills/engineering-standards-builder/manifest.txt +112 -0
  53. package/template/skills/engineering-standards-builder/references/go/00-detection-and-scope.md +37 -0
  54. package/template/skills/engineering-standards-builder/references/go/01-language-and-idioms.md +34 -0
  55. package/template/skills/engineering-standards-builder/references/go/02-modules-packages-and-layout.md +34 -0
  56. package/template/skills/engineering-standards-builder/references/go/03-errors-context-and-concurrency.md +38 -0
  57. package/template/skills/engineering-standards-builder/references/go/04-testing-tooling-quality-gates.md +46 -0
  58. package/template/skills/engineering-standards-builder/references/go/README.md +9 -0
  59. package/template/skills/engineering-standards-builder/references/java/00-detection-and-scope.md +40 -0
  60. package/template/skills/engineering-standards-builder/references/java/01-language-packages-and-api.md +35 -0
  61. package/template/skills/engineering-standards-builder/references/java/02-build-modules-and-dependencies.md +39 -0
  62. package/template/skills/engineering-standards-builder/references/java/03-errors-nullability-resources-concurrency.md +36 -0
  63. package/template/skills/engineering-standards-builder/references/java/04-testing-tooling-quality-gates.md +36 -0
  64. package/template/skills/engineering-standards-builder/references/java/README.md +10 -0
  65. package/template/skills/engineering-standards-builder/references/java/frameworks/spring-boot.md +74 -0
  66. package/template/skills/engineering-standards-builder/references/rules/00-governance-and-precedence.md +52 -0
  67. package/template/skills/engineering-standards-builder/references/rules/01-project-discovery.md +79 -0
  68. package/template/skills/engineering-standards-builder/references/rules/02-evidence-topology-and-scope.md +87 -0
  69. package/template/skills/engineering-standards-builder/references/rules/03-interview-and-decisions.md +77 -0
  70. package/template/skills/engineering-standards-builder/references/rules/04-architecture-and-dependency-boundaries.md +58 -0
  71. package/template/skills/engineering-standards-builder/references/rules/05-files-directories-and-naming.md +53 -0
  72. package/template/skills/engineering-standards-builder/references/rules/06-apis-errors-resources-and-concurrency.md +53 -0
  73. package/template/skills/engineering-standards-builder/references/rules/07-documentation-and-comments.md +46 -0
  74. package/template/skills/engineering-standards-builder/references/rules/08-testing-strategy.md +45 -0
  75. package/template/skills/engineering-standards-builder/references/rules/09-security-configuration-and-data.md +44 -0
  76. package/template/skills/engineering-standards-builder/references/rules/10-performance-observability-and-i18n.md +36 -0
  77. package/template/skills/engineering-standards-builder/references/rules/11-tooling-quality-gates-and-ci.md +69 -0
  78. package/template/skills/engineering-standards-builder/references/rules/12-git-review-and-delivery.md +46 -0
  79. package/template/skills/engineering-standards-builder/references/rules/13-adoption-exceptions-and-ratchets.md +60 -0
  80. package/template/skills/engineering-standards-builder/references/rules/14-generation-contract.md +105 -0
  81. package/template/skills/engineering-standards-builder/references/rules/15-validation-contract.md +65 -0
  82. package/template/skills/engineering-standards-builder/references/rules/16-language-adapter-contract.md +55 -0
  83. package/template/skills/engineering-standards-builder/references/rules/README.md +23 -0
  84. package/template/skills/engineering-standards-builder/references/rust/00-detection-and-scope.md +38 -0
  85. package/template/skills/engineering-standards-builder/references/rust/01-language-api-and-safety.md +35 -0
  86. package/template/skills/engineering-standards-builder/references/rust/02-crates-workspaces-and-layout.md +36 -0
  87. package/template/skills/engineering-standards-builder/references/rust/03-errors-ownership-and-concurrency.md +37 -0
  88. package/template/skills/engineering-standards-builder/references/rust/04-testing-tooling-quality-gates.md +44 -0
  89. package/template/skills/engineering-standards-builder/references/rust/README.md +9 -0
  90. package/template/skills/engineering-standards-builder/references/typescript/00-detection-and-scope.md +58 -0
  91. package/template/skills/engineering-standards-builder/references/typescript/01-language-and-type-system.md +49 -0
  92. package/template/skills/engineering-standards-builder/references/typescript/02-modules-packages-and-runtime-boundaries.md +49 -0
  93. package/template/skills/engineering-standards-builder/references/typescript/03-functions-async-errors-resources.md +42 -0
  94. package/template/skills/engineering-standards-builder/references/typescript/04-testing-tooling-quality-gates.md +51 -0
  95. package/template/skills/engineering-standards-builder/references/typescript/README.md +27 -0
  96. package/template/skills/engineering-standards-builder/references/typescript/app-types/cli.md +11 -0
  97. package/template/skills/engineering-standards-builder/references/typescript/app-types/library.md +11 -0
  98. package/template/skills/engineering-standards-builder/references/typescript/frameworks/react.md +71 -0
  99. package/template/skills/engineering-standards-builder/references/typescript/frameworks/vue.md +131 -0
  100. package/template/skills/engineering-standards-builder/references/typescript/runtimes/browser.md +15 -0
  101. package/template/skills/engineering-standards-builder/references/typescript/runtimes/electron.md +25 -0
  102. package/template/skills/engineering-standards-builder/references/typescript/runtimes/node.md +13 -0
  103. package/template/skills/engineering-standards-builder/scripts/discover-project.mjs +957 -0
  104. package/template/skills/engineering-standards-builder/scripts/self-test.mjs +203 -0
  105. package/template/skills/engineering-standards-builder/scripts/sync-manifest.mjs +105 -0
  106. package/template/skills/engineering-standards-builder/scripts/validate-builder.mjs +255 -0
  107. package/template/skills/engineering-standards-builder/scripts/validate-generated-skill.mjs +286 -0
  108. package/template/skills/engineering-standards-builder/templates/README.md +18 -0
  109. package/template/skills/engineering-standards-builder/templates/compatibility/agents-typescript--standards.md +6 -0
  110. package/template/skills/engineering-standards-builder/templates/compatibility/agents-typescript-standards.md +6 -0
  111. package/template/skills/engineering-standards-builder/templates/compatibility/claude-engineering-standards.md +6 -0
  112. package/template/skills/engineering-standards-builder/templates/compatibility/claude-typescript-standards.md +6 -0
  113. package/template/skills/engineering-standards-builder/templates/project-skill/SKILL.md.template +26 -0
  114. package/template/skills/engineering-standards-builder/templates/project-skill/references/project/00-project-profile.md.template +26 -0
  115. package/template/skills/engineering-standards-builder/templates/project-skill/references/project/01-module-map.md.template +27 -0
  116. package/template/skills/engineering-standards-builder/templates/project-skill/references/project/02-decisions-and-exceptions.md.template +31 -0
  117. package/template/skills/engineering-standards-builder/templates/project-skill/references/project/review-checklist.md.template +14 -0
  118. package/template/skills/source-code-zip-skill/SKILL.md +343 -0
  119. package/template/skills/source-code-zip-skill/scripts/zip_source_code.py +638 -0
  120. package/template/workflows/person/INDEX.md +1 -0
  121. package/template/workflows/person/S-steelman-deliberation/S-steelman-deliberation.md +218 -0
  122. package/template/workflows/person/S-steelman-deliberation/_templates/decision-template.md +51 -0
  123. package/template/workflows/person/S-steelman-deliberation/_templates/steelman-dossier-template.md +96 -0
  124. package/template/workflows/person/S-steelman-deliberation/deliberate.md +127 -0
  125. package/template/workflows/person/S-steelman-deliberation/evidence-gate.md +37 -0
  126. package/template/workflows/person/S-steelman-deliberation/judge.md +69 -0
  127. package/template/workflows/person/S-steelman-deliberation/tools/validate-steelman-change.mjs +697 -0
  128. package/template/skills/typescript-standards-builder/README.md +0 -53
  129. package/template/skills/typescript-standards-builder/SKILL.md +0 -245
  130. package/template/skills/typescript-standards-builder/examples/sample-generated-tree.md +0 -30
  131. package/template/skills/typescript-standards-builder/examples/sample-interview-decisions.md +0 -26
  132. package/template/skills/typescript-standards-builder/manifest.txt +0 -29
  133. package/template/skills/typescript-standards-builder/references/00-governance-and-fixed-defaults.md +0 -77
  134. package/template/skills/typescript-standards-builder/references/01-project-discovery.md +0 -100
  135. package/template/skills/typescript-standards-builder/references/02-interview-workflow.md +0 -129
  136. package/template/skills/typescript-standards-builder/references/03-project-architecture-and-directory-layout.md +0 -84
  137. package/template/skills/typescript-standards-builder/references/04-file-directory-and-symbol-naming.md +0 -92
  138. package/template/skills/typescript-standards-builder/references/05-modules-imports-exports-and-dependencies.md +0 -63
  139. package/template/skills/typescript-standards-builder/references/06-typescript-type-system.md +0 -64
  140. package/template/skills/typescript-standards-builder/references/07-functions-async-errors-and-resources.md +0 -42
  141. package/template/skills/typescript-standards-builder/references/08-comments-jsdoc-and-documentation.md +0 -51
  142. package/template/skills/typescript-standards-builder/references/09-testing-strategy.md +0 -58
  143. package/template/skills/typescript-standards-builder/references/10-react-and-frontend.md +0 -39
  144. package/template/skills/typescript-standards-builder/references/11-node-cli-and-cross-platform.md +0 -31
  145. package/template/skills/typescript-standards-builder/references/12-formatting-lint-and-complexity.md +0 -58
  146. package/template/skills/typescript-standards-builder/references/13-configuration-dependencies-and-ci.md +0 -71
  147. package/template/skills/typescript-standards-builder/references/14-security-performance-and-i18n.md +0 -32
  148. package/template/skills/typescript-standards-builder/references/15-git-review-and-delivery.md +0 -28
  149. package/template/skills/typescript-standards-builder/references/16-adoption-exceptions-and-migration.md +0 -61
  150. package/template/skills/typescript-standards-builder/references/17-generation-contract.md +0 -104
  151. package/template/skills/typescript-standards-builder/references/README.md +0 -37
  152. package/template/skills/typescript-standards-builder/templates/agents-compat-skill/SKILL.md +0 -1
  153. package/template/skills/typescript-standards-builder/templates/claude-skill/SKILL.md +0 -1
  154. package/template/skills/typescript-standards-builder/templates/project-skill/SKILL.md.template +0 -34
  155. package/template/skills/typescript-standards-builder/templates/project-skill/references/00-project-profile.md.template +0 -17
  156. package/template/skills/typescript-standards-builder/templates/project-skill/references/10-review-checklist.md +0 -23
  157. package/template/skills/typescript-standards-builder/templates/project-skill/references/11-decisions-and-exceptions.md.template +0 -19
@@ -0,0 +1,286 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { existsSync } from 'node:fs';
4
+ import { readFile, readdir, realpath, stat } from 'node:fs/promises';
5
+ import path from 'node:path';
6
+ import process from 'node:process';
7
+ import { spawnSync } from 'node:child_process';
8
+ import { fileURLToPath } from 'node:url';
9
+
10
+ const CANONICAL_REL = '.agents/skills/engineering-standards';
11
+ const REQUIRED_PROJECT_REFERENCES = [
12
+ 'references/project/00-project-profile.md',
13
+ 'references/project/01-module-map.md',
14
+ 'references/project/02-decisions-and-exceptions.md',
15
+ 'references/project/review-checklist.md',
16
+ ];
17
+ const ADAPTERS = new Set(['typescript', 'java', 'go', 'rust']);
18
+
19
+ function usage() {
20
+ return `Usage: node scripts/validate-generated-skill.mjs --root <project-root> [options]\n\n` +
21
+ `Validate .agents/skills/engineering-standards and optional compatibility wrappers.\n\n` +
22
+ `Options:\n` +
23
+ ` --root <path> Project root (required)\n` +
24
+ ` --inventory <path> Project Inventory JSON below --root; auto-discover when omitted\n` +
25
+ ` --strict Require every detected adapter/framework and reject unselected ones\n` +
26
+ ` --help Show this help\n`;
27
+ }
28
+
29
+ function parseArgs(argv) {
30
+ const result = { root: null, inventory: null, strict: false };
31
+ for (let i = 0; i < argv.length; i += 1) {
32
+ const arg = argv[i];
33
+ if (arg === '--help' || arg === '-h') return { help: true };
34
+ if (arg === '--strict') { result.strict = true; continue; }
35
+ if (arg === '--root' || arg === '--inventory') {
36
+ const value = argv[i + 1];
37
+ if (!value || value.startsWith('--')) throw new Error(`${arg} requires a value`);
38
+ if (arg === '--root') result.root = value;
39
+ else result.inventory = value;
40
+ i += 1; continue;
41
+ }
42
+ throw new Error(`unknown argument: ${arg}`);
43
+ }
44
+ if (!result.root) throw new Error('--root is required');
45
+ return result;
46
+ }
47
+
48
+ function toPosix(value) { return value.split(path.sep).join('/'); }
49
+ function isInside(root, candidate) {
50
+ const rel = path.relative(root, candidate);
51
+ return rel === '' || (!rel.startsWith(`..${path.sep}`) && rel !== '..' && !path.isAbsolute(rel));
52
+ }
53
+
54
+ function parseFrontmatter(text) {
55
+ if (!text.startsWith('---\n')) return null;
56
+ const end = text.indexOf('\n---\n', 4);
57
+ if (end < 0) return null;
58
+ const values = {};
59
+ for (const line of text.slice(4, end).split('\n')) {
60
+ const match = line.match(/^([A-Za-z0-9_-]+):\s*(.*)$/);
61
+ if (match) values[match[1]] = match[2].trim().replace(/^['"]|['"]$/g, '');
62
+ }
63
+ return { values, body: text.slice(end + 5) };
64
+ }
65
+
66
+ function markdownLinks(text) {
67
+ const result = [];
68
+ const pattern = /\[[^\]]*\]\(([^)]+)\)/g;
69
+ let match;
70
+ while ((match = pattern.exec(text)) !== null) result.push(match[1].trim());
71
+ return result;
72
+ }
73
+
74
+ function isExternal(link) { return /^(?:[a-z]+:|#|\/\/)/i.test(link); }
75
+
76
+ async function collectTree(rootAbs) {
77
+ const files = [];
78
+ const directories = [];
79
+ async function visit(dir) {
80
+ const entries = await readdir(dir, { withFileTypes: true });
81
+ directories.push({ abs: dir, entries });
82
+ entries.sort((a, b) => a.name.localeCompare(b.name, 'en'));
83
+ for (const entry of entries) {
84
+ const abs = path.join(dir, entry.name);
85
+ if (entry.isSymbolicLink()) throw new Error(`symlink is not allowed in generated Skill: ${toPosix(path.relative(rootAbs, abs))}`);
86
+ if (entry.isDirectory()) { await visit(abs); continue; }
87
+ if (entry.isFile()) files.push(abs);
88
+ }
89
+ }
90
+ await visit(rootAbs);
91
+ return { files, directories };
92
+ }
93
+
94
+ function sectionForOffset(text, offset) {
95
+ const headings = [...text.matchAll(/^#{1,3}\s+.+$/gm)].map((match) => ({ index: match.index, text: match[0] }));
96
+ const previous = headings.filter((heading) => heading.index <= offset).at(-1);
97
+ const next = headings.find((heading) => heading.index > offset);
98
+ return {
99
+ title: previous?.text ?? '<document>',
100
+ text: text.slice(previous?.index ?? 0, next?.index ?? text.length),
101
+ };
102
+ }
103
+
104
+ async function loadInventory(args, rootReal) {
105
+ if (args.inventory) {
106
+ const inventoryAbs = path.resolve(rootReal, args.inventory);
107
+ if (!isInside(rootReal, inventoryAbs)) throw new Error(`--inventory escapes --root: ${args.inventory}`);
108
+ return JSON.parse(await readFile(inventoryAbs, 'utf8'));
109
+ }
110
+ const discoverScript = path.join(path.dirname(fileURLToPath(import.meta.url)), 'discover-project.mjs');
111
+ const result = spawnSync(process.execPath, [discoverScript, '--root', rootReal], { encoding: 'utf8', timeout: 60000, maxBuffer: 64 * 1024 * 1024 });
112
+ if (result.status !== 0) throw new Error(`automatic project discovery failed: ${(result.stderr || result.stdout).trim()}`);
113
+ return JSON.parse(result.stdout);
114
+ }
115
+
116
+ async function validateWrapper(projectRoot, wrapperRel, errors) {
117
+ const wrapperRoot = path.join(projectRoot, wrapperRel);
118
+ if (!existsSync(wrapperRoot)) return;
119
+ const info = await stat(wrapperRoot);
120
+ if (!info.isDirectory()) { errors.push(`${wrapperRel} must be a directory`); return; }
121
+ const entries = await readdir(wrapperRoot, { withFileTypes: true });
122
+ const nonSkillEntries = entries.filter((entry) => entry.name !== 'SKILL.md');
123
+ if (nonSkillEntries.length > 0) errors.push(`${wrapperRel} compatibility wrapper must contain only SKILL.md`);
124
+ const skillPath = path.join(wrapperRoot, 'SKILL.md');
125
+ if (!existsSync(skillPath)) { errors.push(`${wrapperRel}/SKILL.md is missing`); return; }
126
+ const text = await readFile(skillPath, 'utf8');
127
+ const fm = parseFrontmatter(text);
128
+ if (!fm) { errors.push(`${wrapperRel}/SKILL.md frontmatter is malformed`); return; }
129
+ const expectedName = path.basename(wrapperRoot);
130
+ if (fm.values.name !== expectedName) errors.push(`${wrapperRel}/SKILL.md name must be ${expectedName}`);
131
+ const bodyLines = fm.body.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
132
+ if (bodyLines.length !== 1) errors.push(`${wrapperRel}/SKILL.md body must be exactly one non-empty routing line`);
133
+ if (!fm.body.includes('engineering-standards')) errors.push(`${wrapperRel}/SKILL.md does not route to engineering-standards`);
134
+ if (fm.body.length > 400) errors.push(`${wrapperRel}/SKILL.md contains too much content for a compatibility wrapper`);
135
+ if (/typescript-(?:-)?standards\/SKILL\.md/.test(fm.body) && !/engineering-standards/.test(fm.body)) errors.push(`${wrapperRel}/SKILL.md may form a wrapper loop`);
136
+ }
137
+
138
+ async function main() {
139
+ let args;
140
+ try { args = parseArgs(process.argv.slice(2)); }
141
+ catch (error) {
142
+ process.stderr.write(`validate-generated-skill: ${error.message}\n\n${usage()}`);
143
+ process.exitCode = 2; return;
144
+ }
145
+ if (args.help) { process.stdout.write(usage()); return; }
146
+
147
+ const errors = [];
148
+ const warnings = [];
149
+ try {
150
+ const rootReal = await realpath(path.resolve(args.root));
151
+ if (!(await stat(rootReal)).isDirectory()) throw new Error('--root is not a directory');
152
+ const canonicalRoot = path.join(rootReal, CANONICAL_REL);
153
+ if (!existsSync(canonicalRoot)) throw new Error(`${CANONICAL_REL} is missing`);
154
+ const canonicalReal = await realpath(canonicalRoot);
155
+ if (!isInside(rootReal, canonicalReal)) throw new Error(`${CANONICAL_REL} resolves outside project root`);
156
+ const { files, directories } = await collectTree(canonicalReal);
157
+ for (const directory of directories) {
158
+ if (directory.entries.length === 0) errors.push(`empty generated directory: ${toPosix(path.relative(rootReal, directory.abs))}`);
159
+ }
160
+ const fileSet = new Set(files);
161
+ const mainSkill = path.join(canonicalReal, 'SKILL.md');
162
+ if (!fileSet.has(mainSkill)) errors.push(`${CANONICAL_REL}/SKILL.md is missing`);
163
+ else {
164
+ const text = await readFile(mainSkill, 'utf8');
165
+ const fm = parseFrontmatter(text);
166
+ if (!fm) errors.push(`${CANONICAL_REL}/SKILL.md frontmatter is malformed`);
167
+ else {
168
+ if (fm.values.name !== 'engineering-standards') errors.push(`canonical frontmatter name must be engineering-standards, found ${fm.values.name ?? '<missing>'}`);
169
+ if (!fm.values.description) errors.push('canonical frontmatter description is missing');
170
+ }
171
+ if (/typescript-(?:-)?standards\/SKILL\.md/.test(text)) errors.push('canonical Skill must not route back to a compatibility alias');
172
+ }
173
+
174
+ for (const required of REQUIRED_PROJECT_REFERENCES) {
175
+ const abs = path.join(canonicalReal, required);
176
+ if (!fileSet.has(abs)) errors.push(`required generated reference is missing: ${required}`);
177
+ }
178
+
179
+ for (const file of files.filter((item) => item.endsWith('.md'))) {
180
+ const rel = toPosix(path.relative(canonicalReal, file));
181
+ const text = await readFile(file, 'utf8');
182
+ if (/\{\{[A-Z0-9_]+\}\}|<PROJECT_NAME>|<REPOSITORY_ROOT>/.test(text)) errors.push(`${rel}: unresolved template placeholder`);
183
+ for (let link of markdownLinks(text)) {
184
+ if (isExternal(link)) continue;
185
+ if (link.startsWith('<') && link.endsWith('>')) link = link.slice(1, -1);
186
+ const targetPart = link.split('#')[0].split('?')[0];
187
+ if (!targetPart) continue;
188
+ const target = path.resolve(path.dirname(file), decodeURIComponent(targetPart));
189
+ if (!isInside(canonicalReal, target)) {
190
+ errors.push(`${rel}: link escapes canonical Skill: ${link}`);
191
+ continue;
192
+ }
193
+ if (!existsSync(target)) errors.push(`${rel}: broken local link: ${link}`);
194
+ }
195
+
196
+ if (!rel.startsWith('references/project/')) {
197
+ for (const levelMatch of text.matchAll(/^Level:\s*(MUST|SHOULD)\s*$/gm)) {
198
+ const section = sectionForOffset(text, levelMatch.index);
199
+ for (const field of ['Scope:', 'Source:', 'Rule:', 'Verification:']) {
200
+ if (!section.text.includes(field)) errors.push(`${rel} ${section.title}: ${levelMatch[1]} rule is missing ${field}`);
201
+ }
202
+ }
203
+ }
204
+ }
205
+
206
+ const inventory = await loadInventory(args, rootReal);
207
+ const detectedAdapters = new Set(inventory?.summary?.adapters ?? []);
208
+ const generatedReferenceRoot = path.join(canonicalReal, 'references');
209
+ const generatedAdapters = new Set();
210
+ if (existsSync(generatedReferenceRoot)) {
211
+ for (const entry of await readdir(generatedReferenceRoot, { withFileTypes: true })) {
212
+ if (entry.isDirectory() && ADAPTERS.has(entry.name)) generatedAdapters.add(entry.name);
213
+ }
214
+ }
215
+ for (const adapter of generatedAdapters) {
216
+ if (!detectedAdapters.has(adapter)) {
217
+ const message = `generated adapter ${adapter} has no matching Project Inventory signal`;
218
+ if (args.strict) errors.push(message); else warnings.push(message);
219
+ }
220
+ }
221
+ for (const adapter of detectedAdapters) {
222
+ if (!generatedAdapters.has(adapter)) {
223
+ const message = `detected adapter ${adapter} is missing from generated references`;
224
+ if (args.strict) errors.push(message); else warnings.push(message);
225
+ }
226
+ }
227
+
228
+ const detectedFrameworks = new Set(inventory?.summary?.frameworks ?? []);
229
+ const frameworkContracts = [
230
+ ['vue', 'typescript', 'references/typescript/frameworks/vue.md'],
231
+ ['react', 'typescript', 'references/typescript/frameworks/react.md'],
232
+ ['spring-boot', 'java', 'references/java/frameworks/spring-boot.md'],
233
+ ];
234
+ for (const [framework, requiredAdapter, rel] of frameworkContracts) {
235
+ const present = fileSet.has(path.join(canonicalReal, rel));
236
+ const frameworkDetected = detectedFrameworks.has(framework);
237
+ const adapterDetected = detectedAdapters.has(requiredAdapter);
238
+ if (present && (!frameworkDetected || !adapterDetected)) {
239
+ const message = `generated framework rule ${framework} has no matching ${requiredAdapter} adapter and framework signal`;
240
+ if (args.strict) errors.push(message); else warnings.push(message);
241
+ }
242
+ if (!present && frameworkDetected && adapterDetected) {
243
+ const message = `detected framework ${framework} is missing generated rule ${rel}`;
244
+ if (args.strict) errors.push(message); else warnings.push(message);
245
+ }
246
+ }
247
+
248
+ for (const wrapper of [
249
+ '.agents/skills/typescript-standards',
250
+ '.agents/skills/typescript--standards',
251
+ '.claude/skills/engineering-standards',
252
+ '.claude/skills/typescript-standards',
253
+ ]) await validateWrapper(rootReal, wrapper, errors);
254
+
255
+ // Detect substantial duplicate canonical copies under known agent roots.
256
+ for (const baseRel of ['.agents/skills', '.claude/skills']) {
257
+ const base = path.join(rootReal, baseRel);
258
+ if (!existsSync(base)) continue;
259
+ for (const entry of await readdir(base, { withFileTypes: true })) {
260
+ if (!entry.isDirectory()) continue;
261
+ const candidate = path.join(base, entry.name, 'SKILL.md');
262
+ if (!existsSync(candidate) || candidate === mainSkill) continue;
263
+ const text = await readFile(candidate, 'utf8');
264
+ const fm = parseFrontmatter(text);
265
+ if (fm?.values?.name === 'engineering-standards' && fm.body.trim().length > 400) {
266
+ errors.push(`substantial duplicate engineering-standards content found at ${toPosix(path.relative(rootReal, candidate))}`);
267
+ }
268
+ }
269
+ }
270
+
271
+ if ((inventory?.scan?.truncated ?? false) === true) warnings.push('Project Inventory scan was truncated; adapter selection requires manual verification');
272
+ for (const item of inventory?.conflicts ?? []) warnings.push(`Project Inventory conflict: ${item.scope ?? 'repository'} ${item.type}`);
273
+ } catch (error) {
274
+ errors.push(error.stack ?? error.message);
275
+ }
276
+
277
+ for (const warning of warnings) process.stderr.write(`validate-generated-skill: WARN: ${warning}\n`);
278
+ if (errors.length) {
279
+ for (const error of errors) process.stderr.write(`validate-generated-skill: ERROR: ${error}\n`);
280
+ process.exitCode = 1;
281
+ return;
282
+ }
283
+ process.stdout.write(`validate-generated-skill: OK${warnings.length ? ` (${warnings.length} warning(s))` : ''}\n`);
284
+ }
285
+
286
+ await main();
@@ -0,0 +1,18 @@
1
+ # 生成模板索引
2
+
3
+ 模板只用于生成项目专属 Skill;占位符必须在写入目标前全部替换。
4
+
5
+ ## Canonical Skill
6
+
7
+ - [主入口](project-skill/SKILL.md.template)
8
+ - [项目画像](project-skill/references/project/00-project-profile.md.template)
9
+ - [模块地图](project-skill/references/project/01-module-map.md.template)
10
+ - [决策与例外](project-skill/references/project/02-decisions-and-exceptions.md.template)
11
+ - [评审清单](project-skill/references/project/review-checklist.md.template)
12
+
13
+ ## 单向兼容入口
14
+
15
+ - [Agents:typescript-standards](compatibility/agents-typescript-standards.md)
16
+ - [Agents:typescript--standards](compatibility/agents-typescript--standards.md)
17
+ - [Claude:engineering-standards](compatibility/claude-engineering-standards.md)
18
+ - [Claude:typescript-standards](compatibility/claude-typescript-standards.md)
@@ -0,0 +1,6 @@
1
+ ---
2
+ name: typescript--standards
3
+ description: 兼容历史双连字符调用方;正式工程规范位于 engineering-standards。
4
+ ---
5
+
6
+ 读取并遵循 `../engineering-standards/SKILL.md`;本目录仅为单向兼容别名,不维护规范正文。
@@ -0,0 +1,6 @@
1
+ ---
2
+ name: typescript-standards
3
+ description: 兼容旧调用方;正式工程规范位于 engineering-standards。
4
+ ---
5
+
6
+ 读取并遵循 `../engineering-standards/SKILL.md`;本目录仅为单向兼容别名,不维护规范正文。
@@ -0,0 +1,6 @@
1
+ ---
2
+ name: engineering-standards
3
+ description: 路由到 .agents 中的 canonical 工程规范。
4
+ ---
5
+
6
+ 读取并遵循 `../../../.agents/skills/engineering-standards/SKILL.md`;本目录仅为单向 Agent 兼容入口。
@@ -0,0 +1,6 @@
1
+ ---
2
+ name: typescript-standards
3
+ description: 兼容旧 Claude 调用方;正式工程规范位于 .agents/skills/engineering-standards。
4
+ ---
5
+
6
+ 读取并遵循 `../../../.agents/skills/engineering-standards/SKILL.md`;本目录仅为单向兼容别名。
@@ -0,0 +1,26 @@
1
+ ---
2
+ name: engineering-standards
3
+ description: 为 {{PROJECT_NAME}} 的实现、重构、评审与交付提供按模块路由的工程规范。处理代码、目录、测试、构建、依赖、API 或 CI 变化前使用。
4
+ ---
5
+
6
+ # {{PROJECT_NAME}} Engineering Standards
7
+
8
+ 本 Skill 只描述本项目已确认的规范。先确定变更 scope,再加载最小充分 references;不要把一个模块的语言或框架规则扩散到其他模块。
9
+
10
+ ## 每次执行
11
+
12
+ 1. 读取 [项目画像](references/project/00-project-profile.md) 和 [模块地图](references/project/01-module-map.md)。
13
+ 2. 将当前任务映射到 repository、module、language、framework、runtime 或 path scope。
14
+ 3. 读取与变更相关的通用规则:{{COMMON_RULE_LINKS}}。
15
+ 4. 按模块地图只读取适用语言/框架规则:{{ADAPTER_ROUTE_LINKS}}。
16
+ 5. 读取 [决策与例外](references/project/02-decisions-and-exceptions.md),确认 current、target、migration 与未到期 exception。
17
+ 6. 实现前检查 public API、依赖方向、安全、错误/取消/资源和测试影响。
18
+ 7. 实现后运行项目画像中该 scope 的真实质量门禁,并按 [评审清单](references/project/review-checklist.md) 报告结果。
19
+
20
+ ## 约束
21
+
22
+ - MUST 规则只有在 Verification 可执行或有精确 review 证据时成立。
23
+ - 不适用规则不加载;generated/vendor/frozen scope 不手工修改。
24
+ - 不通过删除测试、关闭核心规则、放宽编译配置或扩大例外获得通过。
25
+ - 新代码遵循 Target;存量偏差按 Migration/Ratchet 处理。
26
+ - 项目事实变化时更新 Project Profile/Module Map,再更新规则,不静默猜测。
@@ -0,0 +1,26 @@
1
+ # 项目画像
2
+
3
+ ## 身份
4
+
5
+ - Project: {{PROJECT_NAME}}
6
+ - Repository root: {{REPOSITORY_ROOT}}
7
+ - Topology: {{TOPOLOGY}}
8
+ - Inventory schema: {{INVENTORY_SCHEMA_VERSION}}
9
+
10
+ ## 事实来源
11
+
12
+ {{EVIDENCE_SUMMARY}}
13
+
14
+ ## 工具链与质量门禁
15
+
16
+ {{QUALITY_GATES}}
17
+
18
+ 每个命令必须包含 scope、working directory、来源和当前状态(active/planned)。
19
+
20
+ ## 排除与冻结区域
21
+
22
+ {{EXCLUDED_AND_FROZEN_PATHS}}
23
+
24
+ ## 未知与冲突
25
+
26
+ {{UNKNOWNS_AND_CONFLICTS}}
@@ -0,0 +1,27 @@
1
+ # 模块地图
2
+
3
+ {{MODULE_TABLE}}
4
+
5
+ 每个模块至少记录:
6
+
7
+ ```text
8
+ id
9
+ path
10
+ languages
11
+ frameworks
12
+ runtimes
13
+ build systems
14
+ source/test roots
15
+ public entrypoints
16
+ generated paths
17
+ quality gates
18
+ evidence/confidence
19
+ ```
20
+
21
+ ## 依赖方向
22
+
23
+ {{DEPENDENCY_DIRECTIONS}}
24
+
25
+ ## 路由
26
+
27
+ {{MODULE_RULE_ROUTES}}
@@ -0,0 +1,31 @@
1
+ # 决策、迁移与例外
2
+
3
+ ## 决策
4
+
5
+ {{DECISIONS}}
6
+
7
+ 推荐格式:
8
+
9
+ ```text
10
+ Decision ID:
11
+ Scope:
12
+ Decision:
13
+ Source: user-decision | repository-fact | official-guidance | builder-baseline
14
+ Rationale:
15
+ Migration:
16
+ Verification:
17
+ ```
18
+
19
+ ## 当前状态与目标状态
20
+
21
+ {{CURRENT_TARGET_MIGRATIONS}}
22
+
23
+ ## 例外
24
+
25
+ {{EXCEPTIONS}}
26
+
27
+ 每个例外必须包含 ID、Scope、Rule、Owner、Reason、Risk、Compensation、Created、Expires/removal condition 和 Verification。没有 owner 或删除条件的临时例外无效。
28
+
29
+ ## 待确认
30
+
31
+ {{PENDING_DECISIONS}}
@@ -0,0 +1,14 @@
1
+ # 变更评审清单
2
+
3
+ 只检查与当前 scope 和风险相关的条目。
4
+
5
+ - [ ] 变更映射到模块地图中的明确 scope。
6
+ - [ ] 依赖只通过允许的 public contract,未产生循环或深导入。
7
+ - [ ] 外部输入、权限、秘密和敏感数据边界已处理。
8
+ - [ ] 错误、超时、取消、资源和并发生命周期完整。
9
+ - [ ] public API、schema、事件、CLI 或持久化兼容影响已说明。
10
+ - [ ] 测试层级匹配风险,失败与清理路径被覆盖。
11
+ - [ ] generated/vendor/frozen 文件未被无意修改。
12
+ - [ ] 实际运行的 format/lint/compile/test/build 命令及退出码已记录。
13
+ - [ ] 当前状态、目标状态、Ratchet 与例外没有混写。
14
+ - [ ] 文档、迁移、回滚、可观测性和发布影响按需更新。