contextos-agents 1.7.0 → 2.0.0-beta.2

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 (309) hide show
  1. package/.agents/AGENTS.md +1 -1
  2. package/.agents/adapters/aider/export.js +117 -99
  3. package/.agents/adapters/claude/export.js +68 -26
  4. package/.agents/adapters/copilot/export.js +90 -53
  5. package/.agents/adapters/cursor/export.js +80 -101
  6. package/.agents/adapters/drift-detector.js +196 -0
  7. package/.agents/adapters/gemini/export.js +76 -45
  8. package/.agents/adapters/pure-compiler.js +443 -0
  9. package/.agents/adapters/zed/export.js +104 -96
  10. package/.agents/compiled/registry.v2.json +504 -0
  11. package/.agents/compiled/registry.v2.sha256 +1 -0
  12. package/.agents/compiler/manifest-compiler.js +963 -0
  13. package/.agents/compiler/vendor/yaml.LICENSE.txt +13 -0
  14. package/.agents/compiler/vendor/yaml.SBOM.json +6 -0
  15. package/.agents/compiler/vendor/yaml.js +139 -0
  16. package/.agents/core/profiles/init.yaml +25 -0
  17. package/.agents/core/skills/context-manager/skill.yaml +3 -5
  18. package/.agents/core/skills/context-os/SKILL.md +3 -6
  19. package/.agents/core/skills/context-os/skill.yaml +3 -8
  20. package/.agents/core/skills/engineering-workflow/skill.yaml +1 -7
  21. package/.agents/core/skills/gemini-precision/skill.yaml +1 -6
  22. package/.agents/core/skills/gstack-roles/skill.yaml +3 -6
  23. package/.agents/core/skills/ponytail-mindset/skill.yaml +1 -7
  24. package/.agents/core/skills/security/skill.yaml +15 -3
  25. package/.agents/ctx.js +574 -111
  26. package/.agents/customization-dx.js +282 -0
  27. package/.agents/doctor.js +719 -64
  28. package/.agents/filesystem/index.js +71 -0
  29. package/.agents/filesystem/journaled-transaction.js +451 -0
  30. package/.agents/filesystem/lockfile-v2.js +275 -0
  31. package/.agents/filesystem/platform-hardening.js +222 -0
  32. package/.agents/filesystem/project-lock.js +218 -0
  33. package/.agents/filesystem/safe-path.js +256 -0
  34. package/.agents/generated/claude/skills/context-os/SKILL.md +1 -1
  35. package/.agents/generated/gemini/skills/context-os/SKILL.md +2 -2
  36. package/.agents/plugins/contextos/plugin.json +1 -1
  37. package/.agents/plugins.js +259 -60
  38. package/.agents/profiles.js +486 -51
  39. package/.agents/resolver.js +50 -534
  40. package/.agents/schemas/attestation.review.v1.json +111 -0
  41. package/.agents/schemas/attestation.verification.v1.json +85 -0
  42. package/.agents/schemas/lockfile.v2.schema.json +134 -0
  43. package/.agents/schemas/profile.v2.schema.json +114 -0
  44. package/.agents/schemas/runtime.thread.v1.json +192 -0
  45. package/.agents/schemas/skill.manifest.v2.json +177 -0
  46. package/.agents/schemas/verification.spec.v1.json +39 -0
  47. package/.agents/schemas/workspace.graph.schema.json +106 -0
  48. package/.agents/transaction-core/event-store.js +288 -0
  49. package/.agents/transaction-core/idempotency.js +129 -0
  50. package/.agents/transaction-core/ipc-lock.js +311 -0
  51. package/.agents/transaction-core/plugin-supply-chain-bundle.js +436 -0
  52. package/.agents/validate.js +44 -1
  53. package/.agents/watch.js +354 -102
  54. package/.agents/workspace/workspace-graph.js +778 -0
  55. package/README.md +59 -387
  56. package/benchmarks/v2/analysis/statistics.js +140 -0
  57. package/benchmarks/v2/analysis/stats.js +69 -0
  58. package/benchmarks/v2/arms/arm-definitions.js +79 -0
  59. package/benchmarks/v2/dataset.schema.json +34 -0
  60. package/benchmarks/v2/evaluators/index.js +25 -0
  61. package/benchmarks/v2/evaluators/verified-success.js +116 -0
  62. package/benchmarks/v2/harness/runner.js +88 -0
  63. package/benchmarks/v2/pilot-tasks.json +392 -0
  64. package/bin/commands/recover.js +88 -0
  65. package/bin/commands/update.js +80 -17
  66. package/bin/commands.js +62 -25
  67. package/bin/index.js +138 -81
  68. package/bin/lib/lockfile.js +5 -3
  69. package/bin/lib/safe-writer.js +34 -3
  70. package/package.json +85 -72
  71. package/registry.json +2 -2
  72. package/registry.v2.schema.json +86 -0
  73. package/.agents/core/profiles/backend.yaml +0 -47
  74. package/.agents/core/profiles/enterprise.yaml +0 -46
  75. package/.agents/core/profiles/frontend.yaml +0 -46
  76. package/.agents/core/profiles/hackathon.yaml +0 -45
  77. package/.agents/core/profiles/mvp.yaml +0 -44
  78. package/.agents/core/profiles/startup.yaml +0 -48
  79. package/.agents/core/skills/adapters/EXAMPLES.md +0 -19
  80. package/.agents/core/skills/adapters/SKILL.md +0 -105
  81. package/.agents/core/skills/adapters/TROUBLESHOOTING.md +0 -7
  82. package/.agents/core/skills/adapters/VALIDATION.json +0 -12
  83. package/.agents/core/skills/adapters/skill.yaml +0 -16
  84. package/.agents/core/skills/architecture-diagrams/SKILL.md +0 -108
  85. package/.agents/core/skills/architecture-diagrams/VALIDATION.json +0 -12
  86. package/.agents/core/skills/architecture-diagrams/skill.yaml +0 -12
  87. package/.agents/core/skills/brutalist-design/SKILL.md +0 -150
  88. package/.agents/core/skills/brutalist-design/VALIDATION.json +0 -12
  89. package/.agents/core/skills/brutalist-design/skill.yaml +0 -12
  90. package/.agents/core/skills/database/EXAMPLES.md +0 -74
  91. package/.agents/core/skills/database/SKILL.md +0 -101
  92. package/.agents/core/skills/database/TROUBLESHOOTING.md +0 -18
  93. package/.agents/core/skills/database/VALIDATION.json +0 -11
  94. package/.agents/core/skills/database/skill.yaml +0 -31
  95. package/.agents/core/skills/ddd/EXAMPLES.md +0 -42
  96. package/.agents/core/skills/ddd/SKILL.md +0 -247
  97. package/.agents/core/skills/ddd/TROUBLESHOOTING.md +0 -19
  98. package/.agents/core/skills/ddd/VALIDATION.json +0 -12
  99. package/.agents/core/skills/ddd/ddd.md +0 -178
  100. package/.agents/core/skills/ddd/skill.yaml +0 -17
  101. package/.agents/core/skills/decisions/EXAMPLES.md +0 -35
  102. package/.agents/core/skills/decisions/SKILL.md +0 -90
  103. package/.agents/core/skills/decisions/TROUBLESHOOTING.md +0 -13
  104. package/.agents/core/skills/decisions/VALIDATION.json +0 -12
  105. package/.agents/core/skills/decisions/skill.yaml +0 -16
  106. package/.agents/core/skills/docker/EXAMPLES.md +0 -56
  107. package/.agents/core/skills/docker/SKILL.md +0 -63
  108. package/.agents/core/skills/docker/TROUBLESHOOTING.md +0 -18
  109. package/.agents/core/skills/docker/VALIDATION.json +0 -11
  110. package/.agents/core/skills/docker/skill.yaml +0 -29
  111. package/.agents/core/skills/fastapi/EXAMPLES.md +0 -36
  112. package/.agents/core/skills/fastapi/SKILL.md +0 -148
  113. package/.agents/core/skills/fastapi/TROUBLESHOOTING.md +0 -19
  114. package/.agents/core/skills/fastapi/VALIDATION.json +0 -12
  115. package/.agents/core/skills/fastapi/fastapi.md +0 -112
  116. package/.agents/core/skills/fastapi/skill.yaml +0 -17
  117. package/.agents/core/skills/generators/EXAMPLES.md +0 -19
  118. package/.agents/core/skills/generators/SKILL.md +0 -112
  119. package/.agents/core/skills/generators/TROUBLESHOOTING.md +0 -7
  120. package/.agents/core/skills/generators/VALIDATION.json +0 -12
  121. package/.agents/core/skills/generators/skill.yaml +0 -25
  122. package/.agents/core/skills/generators/templates/API.md +0 -77
  123. package/.agents/core/skills/generators/templates/ARCHITECTURE.md +0 -70
  124. package/.agents/core/skills/generators/templates/DATABASE.md +0 -42
  125. package/.agents/core/skills/generators/templates/DECISION.md +0 -46
  126. package/.agents/core/skills/generators/templates/PRD.md +0 -67
  127. package/.agents/core/skills/generators/templates/PROJECT_GRAPH.md +0 -56
  128. package/.agents/core/skills/generators/templates/ROADMAP.md +0 -51
  129. package/.agents/core/skills/generators/templates/TASKS.md +0 -43
  130. package/.agents/core/skills/generators/templates/UI.md +0 -73
  131. package/.agents/core/skills/graphify/EXAMPLES.md +0 -73
  132. package/.agents/core/skills/graphify/SKILL.md +0 -130
  133. package/.agents/core/skills/graphify/VALIDATION.json +0 -12
  134. package/.agents/core/skills/graphify/skill.yaml +0 -18
  135. package/.agents/core/skills/impeccable-design/EXAMPLES.md +0 -26
  136. package/.agents/core/skills/impeccable-design/SKILL.md +0 -201
  137. package/.agents/core/skills/impeccable-design/TROUBLESHOOTING.md +0 -19
  138. package/.agents/core/skills/impeccable-design/VALIDATION.json +0 -12
  139. package/.agents/core/skills/impeccable-design/skill.yaml +0 -20
  140. package/.agents/core/skills/interview-me/SKILL.md +0 -97
  141. package/.agents/core/skills/interview-me/VALIDATION.json +0 -12
  142. package/.agents/core/skills/interview-me/skill.yaml +0 -12
  143. package/.agents/core/skills/microservices/EXAMPLES.md +0 -38
  144. package/.agents/core/skills/microservices/SKILL.md +0 -164
  145. package/.agents/core/skills/microservices/TROUBLESHOOTING.md +0 -19
  146. package/.agents/core/skills/microservices/VALIDATION.json +0 -12
  147. package/.agents/core/skills/microservices/microservices.md +0 -119
  148. package/.agents/core/skills/microservices/skill.yaml +0 -17
  149. package/.agents/core/skills/minimalist-design/SKILL.md +0 -113
  150. package/.agents/core/skills/minimalist-design/VALIDATION.json +0 -12
  151. package/.agents/core/skills/minimalist-design/skill.yaml +0 -12
  152. package/.agents/core/skills/nestjs/EXAMPLES.md +0 -40
  153. package/.agents/core/skills/nestjs/SKILL.md +0 -139
  154. package/.agents/core/skills/nestjs/TROUBLESHOOTING.md +0 -19
  155. package/.agents/core/skills/nestjs/VALIDATION.json +0 -12
  156. package/.agents/core/skills/nestjs/nestjs.md +0 -103
  157. package/.agents/core/skills/nestjs/skill.yaml +0 -17
  158. package/.agents/core/skills/nextjs/EXAMPLES.md +0 -40
  159. package/.agents/core/skills/nextjs/SKILL.md +0 -163
  160. package/.agents/core/skills/nextjs/TROUBLESHOOTING.md +0 -19
  161. package/.agents/core/skills/nextjs/VALIDATION.json +0 -12
  162. package/.agents/core/skills/nextjs/nextjs.md +0 -67
  163. package/.agents/core/skills/nextjs/skill.yaml +0 -17
  164. package/.agents/core/skills/node/EXAMPLES.md +0 -80
  165. package/.agents/core/skills/node/SKILL.md +0 -128
  166. package/.agents/core/skills/node/TROUBLESHOOTING.md +0 -19
  167. package/.agents/core/skills/node/VALIDATION.json +0 -12
  168. package/.agents/core/skills/node/node.md +0 -87
  169. package/.agents/core/skills/node/skill.yaml +0 -17
  170. package/.agents/core/skills/performance/EXAMPLES.md +0 -30
  171. package/.agents/core/skills/performance/SKILL.md +0 -75
  172. package/.agents/core/skills/performance/TROUBLESHOOTING.md +0 -19
  173. package/.agents/core/skills/performance/VALIDATION.json +0 -12
  174. package/.agents/core/skills/performance/performance.md +0 -52
  175. package/.agents/core/skills/performance/skill.yaml +0 -17
  176. package/.agents/core/skills/react/EXAMPLES.md +0 -79
  177. package/.agents/core/skills/react/SKILL.md +0 -132
  178. package/.agents/core/skills/react/TROUBLESHOOTING.md +0 -19
  179. package/.agents/core/skills/react/VALIDATION.json +0 -12
  180. package/.agents/core/skills/react/react.md +0 -93
  181. package/.agents/core/skills/react/skill.yaml +0 -17
  182. package/.agents/core/skills/react-best-practices/SKILL.md +0 -155
  183. package/.agents/core/skills/react-best-practices/VALIDATION.json +0 -12
  184. package/.agents/core/skills/react-best-practices/skill.yaml +0 -14
  185. package/.agents/core/skills/redesign-audit/SKILL.md +0 -117
  186. package/.agents/core/skills/redesign-audit/VALIDATION.json +0 -12
  187. package/.agents/core/skills/redesign-audit/skill.yaml +0 -12
  188. package/.agents/core/skills/soft-design/SKILL.md +0 -108
  189. package/.agents/core/skills/soft-design/VALIDATION.json +0 -12
  190. package/.agents/core/skills/soft-design/skill.yaml +0 -12
  191. package/.agents/core/skills/state-management/EXAMPLES.md +0 -56
  192. package/.agents/core/skills/state-management/SKILL.md +0 -48
  193. package/.agents/core/skills/state-management/TROUBLESHOOTING.md +0 -18
  194. package/.agents/core/skills/state-management/VALIDATION.json +0 -11
  195. package/.agents/core/skills/state-management/skill.yaml +0 -28
  196. package/.agents/core/skills/subagent-orchestrator/SKILL.md +0 -117
  197. package/.agents/core/skills/subagent-orchestrator/VALIDATION.json +0 -12
  198. package/.agents/core/skills/subagent-orchestrator/skill.yaml +0 -12
  199. package/.agents/core/skills/system-design/EXAMPLES.md +0 -75
  200. package/.agents/core/skills/system-design/SKILL.md +0 -419
  201. package/.agents/core/skills/system-design/TROUBLESHOOTING.md +0 -19
  202. package/.agents/core/skills/system-design/VALIDATION.json +0 -12
  203. package/.agents/core/skills/system-design/skill.yaml +0 -20
  204. package/.agents/core/skills/system-design/system-design.md +0 -112
  205. package/.agents/core/skills/testing/EXAMPLES.md +0 -71
  206. package/.agents/core/skills/testing/SKILL.md +0 -70
  207. package/.agents/core/skills/testing/TROUBLESHOOTING.md +0 -18
  208. package/.agents/core/skills/testing/VALIDATION.json +0 -11
  209. package/.agents/core/skills/testing/skill.yaml +0 -32
  210. package/.agents/core/skills/typescript/EXAMPLES.md +0 -64
  211. package/.agents/core/skills/typescript/SKILL.md +0 -112
  212. package/.agents/core/skills/typescript/TROUBLESHOOTING.md +0 -19
  213. package/.agents/core/skills/typescript/VALIDATION.json +0 -12
  214. package/.agents/core/skills/typescript/skill.yaml +0 -17
  215. package/.agents/core/skills/typescript/typescript.md +0 -71
  216. package/.agents/core/skills/ui-design/EXAMPLES.md +0 -21
  217. package/.agents/core/skills/ui-design/SKILL.md +0 -124
  218. package/.agents/core/skills/ui-design/TROUBLESHOOTING.md +0 -19
  219. package/.agents/core/skills/ui-design/VALIDATION.json +0 -12
  220. package/.agents/core/skills/ui-design/skill.yaml +0 -17
  221. package/.agents/core/skills/ui-design/ui.md +0 -88
  222. package/.agents/core/skills/ui-ux-pro/EXAMPLES.md +0 -62
  223. package/.agents/core/skills/ui-ux-pro/SKILL.md +0 -375
  224. package/.agents/core/skills/ui-ux-pro/TROUBLESHOOTING.md +0 -19
  225. package/.agents/core/skills/ui-ux-pro/VALIDATION.json +0 -12
  226. package/.agents/core/skills/ui-ux-pro/skill.yaml +0 -19
  227. package/.agents/core/skills/ux-design/EXAMPLES.md +0 -36
  228. package/.agents/core/skills/ux-design/SKILL.md +0 -116
  229. package/.agents/core/skills/ux-design/TROUBLESHOOTING.md +0 -19
  230. package/.agents/core/skills/ux-design/VALIDATION.json +0 -12
  231. package/.agents/core/skills/ux-design/skill.yaml +0 -17
  232. package/.agents/core/skills/ux-design/ux.md +0 -80
  233. package/.agents/core/skills/vercel-optimize/SKILL.md +0 -83
  234. package/.agents/core/skills/vercel-optimize/VALIDATION.json +0 -12
  235. package/.agents/core/skills/vercel-optimize/scripts/collect-signals.mjs +0 -131
  236. package/.agents/core/skills/vercel-optimize/scripts/gate-investigations.mjs +0 -142
  237. package/.agents/core/skills/vercel-optimize/scripts/merge-signals.mjs +0 -143
  238. package/.agents/core/skills/vercel-optimize/scripts/scan-codebase.mjs +0 -174
  239. package/.agents/core/skills/vercel-optimize/skill.yaml +0 -18
  240. package/.agents/core/skills/web-accessibility/EXAMPLES.md +0 -39
  241. package/.agents/core/skills/web-accessibility/SKILL.md +0 -170
  242. package/.agents/core/skills/web-accessibility/TROUBLESHOOTING.md +0 -19
  243. package/.agents/core/skills/web-accessibility/VALIDATION.json +0 -12
  244. package/.agents/core/skills/web-accessibility/accessibility.md +0 -63
  245. package/.agents/core/skills/web-accessibility/skill.yaml +0 -17
  246. package/.agents/generated/claude/skills/adapters/SKILL.md +0 -126
  247. package/.agents/generated/claude/skills/architecture-diagrams/SKILL.md +0 -101
  248. package/.agents/generated/claude/skills/brutalist-design/SKILL.md +0 -145
  249. package/.agents/generated/claude/skills/database/SKILL.md +0 -191
  250. package/.agents/generated/claude/skills/ddd/SKILL.md +0 -305
  251. package/.agents/generated/claude/skills/decisions/SKILL.md +0 -134
  252. package/.agents/generated/claude/skills/docker/SKILL.md +0 -135
  253. package/.agents/generated/claude/skills/fastapi/SKILL.md +0 -200
  254. package/.agents/generated/claude/skills/generators/SKILL.md +0 -133
  255. package/.agents/generated/claude/skills/graphify/SKILL.md +0 -198
  256. package/.agents/generated/claude/skills/impeccable-design/SKILL.md +0 -241
  257. package/.agents/generated/claude/skills/interview-me/SKILL.md +0 -90
  258. package/.agents/generated/claude/skills/microservices/SKILL.md +0 -218
  259. package/.agents/generated/claude/skills/minimalist-design/SKILL.md +0 -108
  260. package/.agents/generated/claude/skills/nestjs/SKILL.md +0 -195
  261. package/.agents/generated/claude/skills/nextjs/SKILL.md +0 -219
  262. package/.agents/generated/claude/skills/node/SKILL.md +0 -224
  263. package/.agents/generated/claude/skills/performance/SKILL.md +0 -121
  264. package/.agents/generated/claude/skills/react/SKILL.md +0 -227
  265. package/.agents/generated/claude/skills/react-best-practices/SKILL.md +0 -146
  266. package/.agents/generated/claude/skills/redesign-audit/SKILL.md +0 -112
  267. package/.agents/generated/claude/skills/soft-design/SKILL.md +0 -103
  268. package/.agents/generated/claude/skills/state-management/SKILL.md +0 -120
  269. package/.agents/generated/claude/skills/subagent-orchestrator/SKILL.md +0 -110
  270. package/.agents/generated/claude/skills/system-design/SKILL.md +0 -507
  271. package/.agents/generated/claude/skills/testing/SKILL.md +0 -157
  272. package/.agents/generated/claude/skills/typescript/SKILL.md +0 -192
  273. package/.agents/generated/claude/skills/ui-design/SKILL.md +0 -161
  274. package/.agents/generated/claude/skills/ui-ux-pro/SKILL.md +0 -451
  275. package/.agents/generated/claude/skills/ux-design/SKILL.md +0 -168
  276. package/.agents/generated/claude/skills/vercel-optimize/SKILL.md +0 -76
  277. package/.agents/generated/claude/skills/web-accessibility/SKILL.md +0 -225
  278. package/.agents/generated/gemini/skills/adapters/SKILL.md +0 -135
  279. package/.agents/generated/gemini/skills/architecture-diagrams/SKILL.md +0 -107
  280. package/.agents/generated/gemini/skills/brutalist-design/SKILL.md +0 -151
  281. package/.agents/generated/gemini/skills/database/SKILL.md +0 -200
  282. package/.agents/generated/gemini/skills/ddd/SKILL.md +0 -314
  283. package/.agents/generated/gemini/skills/decisions/SKILL.md +0 -143
  284. package/.agents/generated/gemini/skills/docker/SKILL.md +0 -144
  285. package/.agents/generated/gemini/skills/fastapi/SKILL.md +0 -209
  286. package/.agents/generated/gemini/skills/generators/SKILL.md +0 -142
  287. package/.agents/generated/gemini/skills/graphify/SKILL.md +0 -205
  288. package/.agents/generated/gemini/skills/impeccable-design/SKILL.md +0 -250
  289. package/.agents/generated/gemini/skills/interview-me/SKILL.md +0 -96
  290. package/.agents/generated/gemini/skills/microservices/SKILL.md +0 -227
  291. package/.agents/generated/gemini/skills/minimalist-design/SKILL.md +0 -114
  292. package/.agents/generated/gemini/skills/nestjs/SKILL.md +0 -204
  293. package/.agents/generated/gemini/skills/nextjs/SKILL.md +0 -298
  294. package/.agents/generated/gemini/skills/node/SKILL.md +0 -323
  295. package/.agents/generated/gemini/skills/performance/SKILL.md +0 -185
  296. package/.agents/generated/gemini/skills/react/SKILL.md +0 -332
  297. package/.agents/generated/gemini/skills/react-best-practices/SKILL.md +0 -152
  298. package/.agents/generated/gemini/skills/redesign-audit/SKILL.md +0 -118
  299. package/.agents/generated/gemini/skills/soft-design/SKILL.md +0 -109
  300. package/.agents/generated/gemini/skills/state-management/SKILL.md +0 -129
  301. package/.agents/generated/gemini/skills/subagent-orchestrator/SKILL.md +0 -116
  302. package/.agents/generated/gemini/skills/system-design/SKILL.md +0 -631
  303. package/.agents/generated/gemini/skills/testing/SKILL.md +0 -166
  304. package/.agents/generated/gemini/skills/typescript/SKILL.md +0 -275
  305. package/.agents/generated/gemini/skills/ui-design/SKILL.md +0 -170
  306. package/.agents/generated/gemini/skills/ui-ux-pro/SKILL.md +0 -460
  307. package/.agents/generated/gemini/skills/ux-design/SKILL.md +0 -177
  308. package/.agents/generated/gemini/skills/vercel-optimize/SKILL.md +0 -82
  309. package/.agents/generated/gemini/skills/web-accessibility/SKILL.md +0 -300
@@ -0,0 +1,963 @@
1
+ /**
2
+ * .agents/compiler/manifest-compiler.js
3
+ * ContextOS — Manifest Compiler & Registry v2 Generator
4
+ *
5
+ * Compiles skill manifests (v1 and v2) into a unified, deterministic,
6
+ * cryptographically verified registry (registry.v2.json) with:
7
+ * - Strict schema validation (fail-closed, additionalProperties: false)
8
+ * - Frontmatter parity enforcement between SKILL.md and manifest
9
+ * - Dependency closure, cycle detection, self-dependency and conflict validation
10
+ * - Resource tree verification & path traversal / symlink escape guards
11
+ * - SHA-256 content hashing (entrypointHash, resourceTreeHash, sourceGraphHash)
12
+ * - Standardized error contracts & SARIF 2.1.0 reporting
13
+ * - Zero install-time runtime dependencies (pure Node.js)
14
+ */
15
+
16
+ 'use strict';
17
+
18
+ const fs = require('fs');
19
+ const path = require('path');
20
+ const crypto = require('crypto');
21
+
22
+ // Standard error codes for manifest diagnostics
23
+ const CODES = {
24
+ PARSE_ERROR: 'CTX_MANIFEST_PARSE_ERROR',
25
+ SCHEMA_ERROR: 'CTX_MANIFEST_SCHEMA_ERROR',
26
+ UNKNOWN_DEPENDENCY: 'CTX_MANIFEST_UNKNOWN_DEPENDENCY',
27
+ CYCLE_DETECTED: 'CTX_MANIFEST_CYCLE_DETECTED',
28
+ SELF_DEPENDENCY: 'CTX_MANIFEST_SELF_DEPENDENCY',
29
+ CONFLICT_VIOLATION: 'CTX_MANIFEST_CONFLICT_VIOLATION',
30
+ FRONTMATTER_MISMATCH: 'CTX_MANIFEST_FRONTMATTER_MISMATCH',
31
+ MISSING_ENTRYPOINT: 'CTX_MANIFEST_MISSING_ENTRYPOINT',
32
+ MISSING_RESOURCE: 'CTX_MANIFEST_MISSING_RESOURCE',
33
+ INVALID_PATH: 'CTX_MANIFEST_INVALID_PATH',
34
+ DUPLICATE_ID: 'CTX_MANIFEST_DUPLICATE_ID',
35
+ DUPLICATE_ALIAS: 'CTX_MANIFEST_DUPLICATE_ALIAS',
36
+ MISSING_RULE_CHECKER: 'CTX_MANIFEST_MISSING_RULE_CHECKER',
37
+ INVALID_RULE: 'CTX_MANIFEST_INVALID_RULE',
38
+ };
39
+
40
+ // Default signals by skill ID (extracted from canonical resolver patterns for v1 backward compatibility)
41
+ const DEFAULT_SIGNALS = {
42
+ nextjs: {
43
+ aliases: ['next', 'next.js', 'nextjs'],
44
+ keywords: [
45
+ { value: 'next', weight: 40 },
46
+ { value: 'next.js', weight: 40 },
47
+ { value: 'app router', weight: 35 },
48
+ { value: 'server actions', weight: 30 },
49
+ { value: 'rsc', weight: 30 },
50
+ { value: 'некст', locale: 'ru', weight: 35 },
51
+ ],
52
+ fileGlobs: [
53
+ { value: 'app/**/*.{tsx,jsx,ts,js}', weight: 30 },
54
+ { value: '**/next.config.{js,mjs,ts}', weight: 35 },
55
+ ],
56
+ packages: [{ ecosystem: 'npm', name: 'next', weight: 35 }],
57
+ },
58
+ react: {
59
+ aliases: ['react', 'reactjs'],
60
+ keywords: [
61
+ { value: 'react', weight: 40 },
62
+ { value: 'useOptimistic', weight: 30 },
63
+ { value: 'component', weight: 20 },
64
+ { value: 'hooks', weight: 25 },
65
+ { value: 'компонент', locale: 'ru', weight: 30 },
66
+ { value: 'хук', locale: 'ru', weight: 30 },
67
+ ],
68
+ fileGlobs: [{ value: '**/*.{tsx,jsx}', weight: 25 }],
69
+ packages: [{ ecosystem: 'npm', name: 'react', weight: 35 }],
70
+ },
71
+ typescript: {
72
+ aliases: ['ts', 'typescript'],
73
+ keywords: [
74
+ { value: 'typescript', weight: 40 },
75
+ { value: 'type-safe', weight: 30 },
76
+ { value: 'generics', weight: 25 },
77
+ { value: 'tsconfig', weight: 30 },
78
+ { value: 'тайпскрипт', locale: 'ru', weight: 35 },
79
+ { value: 'типизац', locale: 'ru', weight: 30 },
80
+ ],
81
+ fileGlobs: [
82
+ { value: '**/*.tsx?', weight: 20 },
83
+ { value: '**/tsconfig.json', weight: 35 },
84
+ ],
85
+ packages: [{ ecosystem: 'npm', name: 'typescript', weight: 35 }],
86
+ },
87
+ security: {
88
+ aliases: ['sec', 'auth', 'security'],
89
+ keywords: [
90
+ { value: 'security', weight: 40 },
91
+ { value: 'auth', weight: 35 },
92
+ { value: 'jwt', weight: 30 },
93
+ { value: 'csrf', weight: 30 },
94
+ { value: 'xss', weight: 30 },
95
+ { value: 'безопасност', locale: 'ru', weight: 35 },
96
+ { value: 'авториз', locale: 'ru', weight: 30 },
97
+ { value: 'парол', locale: 'ru', weight: 30 },
98
+ ],
99
+ fileGlobs: [
100
+ { value: '**/*auth*', weight: 25 },
101
+ { value: '**/*security*', weight: 25 },
102
+ ],
103
+ packages: [],
104
+ },
105
+ database: {
106
+ aliases: ['db', 'database', 'sql'],
107
+ keywords: [
108
+ { value: 'database', weight: 40 },
109
+ { value: 'sql', weight: 35 },
110
+ { value: 'postgres', weight: 35 },
111
+ { value: 'prisma', weight: 35 },
112
+ { value: 'drizzle', weight: 35 },
113
+ { value: 'migration', weight: 30 },
114
+ { value: 'баз.*данн', locale: 'ru', weight: 35 },
115
+ { value: 'миграц', locale: 'ru', weight: 30 },
116
+ ],
117
+ fileGlobs: [
118
+ { value: '**/*.prisma', weight: 35 },
119
+ { value: '**/drizzle.config.*', weight: 35 },
120
+ { value: '**/migrations/**/*.sql', weight: 30 },
121
+ ],
122
+ packages: [
123
+ { ecosystem: 'npm', name: 'prisma', weight: 30 },
124
+ { ecosystem: 'npm', name: 'drizzle-orm', weight: 30 },
125
+ ],
126
+ },
127
+ testing: {
128
+ aliases: ['test', 'tests', 'testing'],
129
+ keywords: [
130
+ { value: 'vitest', weight: 40 },
131
+ { value: 'jest', weight: 40 },
132
+ { value: 'playwright', weight: 40 },
133
+ { value: 'tdd', weight: 35 },
134
+ { value: 'bdd', weight: 30 },
135
+ { value: 'тестирован', locale: 'ru', weight: 35 },
136
+ { value: 'юнит', locale: 'ru', weight: 30 },
137
+ ],
138
+ fileGlobs: [
139
+ { value: '**/*.{test,spec}.{ts,js,tsx,jsx}', weight: 35 },
140
+ { value: '**/vitest.config.*', weight: 35 },
141
+ { value: '**/playwright.config.*', weight: 35 },
142
+ ],
143
+ packages: [],
144
+ },
145
+ 'ui-ux-pro': {
146
+ aliases: ['ui', 'ux', 'ui-ux'],
147
+ keywords: [
148
+ { value: 'ui', weight: 35 },
149
+ { value: 'ux', weight: 35 },
150
+ { value: 'tailwind', weight: 35 },
151
+ { value: 'styling', weight: 30 },
152
+ { value: 'дизайн', locale: 'ru', weight: 35 },
153
+ { value: 'интерфейс', locale: 'ru', weight: 30 },
154
+ ],
155
+ fileGlobs: [
156
+ { value: '**/*.{css,scss,sass}', weight: 25 },
157
+ { value: '**/tailwind.config.*', weight: 35 },
158
+ ],
159
+ packages: [{ ecosystem: 'npm', name: 'tailwindcss', weight: 30 }],
160
+ },
161
+ node: {
162
+ aliases: ['nodejs', 'node'],
163
+ keywords: [
164
+ { value: 'node', weight: 35 },
165
+ { value: 'nodejs', weight: 35 },
166
+ { value: 'express', weight: 30 },
167
+ { value: 'fastify', weight: 30 },
168
+ ],
169
+ fileGlobs: [{ value: '**/package.json', weight: 15 }],
170
+ packages: [],
171
+ },
172
+ fastapi: {
173
+ aliases: ['fastapi'],
174
+ keywords: [
175
+ { value: 'fastapi', weight: 45 },
176
+ { value: 'pydantic', weight: 35 },
177
+ { value: 'uvicorn', weight: 30 },
178
+ ],
179
+ fileGlobs: [
180
+ { value: '**/main.py', weight: 20 },
181
+ { value: '**/requirements.txt', weight: 15 },
182
+ ],
183
+ packages: [{ ecosystem: 'pypi', name: 'fastapi', weight: 40 }],
184
+ },
185
+ nestjs: {
186
+ aliases: ['nestjs', 'nest'],
187
+ keywords: [
188
+ { value: 'nestjs', weight: 45 },
189
+ { value: 'nest.js', weight: 40 },
190
+ { value: 'injectable', weight: 25 },
191
+ ],
192
+ fileGlobs: [{ value: '**/nest-cli.json', weight: 40 }],
193
+ packages: [{ ecosystem: 'npm', name: '@nestjs/core', weight: 40 }],
194
+ },
195
+ docker: {
196
+ aliases: ['docker'],
197
+ keywords: [
198
+ { value: 'docker', weight: 45 },
199
+ { value: 'dockerfile', weight: 45 },
200
+ { value: 'compose', weight: 30 },
201
+ { value: 'докер', locale: 'ru', weight: 40 },
202
+ { value: 'container', weight: 5 },
203
+ { value: 'контейнер', locale: 'ru', weight: 5 },
204
+ ],
205
+ fileGlobs: [
206
+ { value: '**/Dockerfile*', weight: 40 },
207
+ { value: '**/docker-compose*.yml', weight: 40 },
208
+ ],
209
+ packages: [],
210
+ },
211
+ };
212
+
213
+ /**
214
+ * Diagnostic error object.
215
+ */
216
+ class DiagnosticError extends Error {
217
+ /**
218
+ * @param {Object} diag - Diagnostic payload
219
+ */
220
+ constructor(diag) {
221
+ super(diag.message);
222
+ this.name = 'DiagnosticError';
223
+ this.diag = diag;
224
+ }
225
+ }
226
+
227
+ const yaml = require('./vendor/yaml.js');
228
+
229
+ /**
230
+ * Robust zero-dep YAML parser for skill manifests.
231
+ * Handles scalars, inline arrays, block lists, nested maps, and quoted multiline strings.
232
+ */
233
+ function parseYaml(yamlText) {
234
+ if (typeof yamlText !== 'string' || !yamlText.trim()) return {};
235
+ const doc = yaml.parseDocument(yamlText, { merge: true, strict: true, uniqueKeys: true });
236
+ if (doc.errors && doc.errors.length > 0) {
237
+ throw new DiagnosticError({
238
+ message: `YAML Parse Error: ${doc.errors[0].message}`,
239
+ code: CODES.PARSE_ERROR
240
+ });
241
+ }
242
+ return doc.toJS() || {};
243
+ }
244
+
245
+ /**
246
+ * ManifestCompiler class.
247
+ */
248
+ class ManifestCompiler {
249
+ constructor(options = {}) {
250
+ this.rootDir = options.rootDir || process.cwd();
251
+ this.agentsDir = options.agentsDir || path.join(this.rootDir, '.agents');
252
+ this.coreSkillsDir = options.coreSkillsDir || path.join(this.agentsDir, 'core', 'skills');
253
+ this.compiledDir = options.compiledDir || path.join(this.agentsDir, 'compiled');
254
+ this.diagnostics = [];
255
+ }
256
+
257
+ addDiagnostic(code, message, options = {}) {
258
+ const diag = {
259
+ code,
260
+ severity: options.severity || 'error',
261
+ component: 'manifest-compiler',
262
+ file: options.file || '',
263
+ path: options.path || '',
264
+ line: options.line,
265
+ column: options.column,
266
+ message,
267
+ remediation: options.remediation || 'Correct the manifest according to the schema specification.',
268
+ };
269
+ this.diagnostics.push(diag);
270
+ return diag;
271
+ }
272
+
273
+ /**
274
+ * Reads and parses frontmatter from a markdown file.
275
+ */
276
+ readFrontmatter(filePath) {
277
+ if (!fs.existsSync(filePath)) return null;
278
+ const content = fs.readFileSync(filePath, 'utf8');
279
+ if (!content.startsWith('---')) return null;
280
+ const endIdx = content.indexOf('---', 3);
281
+ if (endIdx === -1) return { malformed: true };
282
+ const rawYaml = content.slice(3, endIdx).trim();
283
+ const parsed = parseYaml(rawYaml);
284
+ return {
285
+ raw: rawYaml,
286
+ parsed,
287
+ contentWithoutFrontmatter: content.slice(endIdx + 3).trim(),
288
+ };
289
+ }
290
+
291
+ /**
292
+ * Normalizes a v1 or v2 manifest into canonical SkillManifestV2 format.
293
+ */
294
+ normalizeManifest(skillDir, rawYamlText) {
295
+ const parsed = parseYaml(rawYamlText);
296
+ const skillId = parsed.id || parsed.name || path.basename(skillDir);
297
+ const displayName = parsed.displayName || parsed.name || skillId;
298
+ const isV2 = parsed.schemaVersion === 2;
299
+
300
+ const entrypoint = parsed.entrypoint || 'SKILL.md';
301
+ const version = parsed.version ? String(parsed.version) : '1.0.0';
302
+ const description = parsed.description || `ContextOS skill for ${displayName}`;
303
+ const type = parsed.type || 'instruction-only';
304
+ const category = parsed.category || 'general';
305
+
306
+ // Normalizing dependencies
307
+ const requires = Array.isArray(parsed.dependencies?.requires)
308
+ ? parsed.dependencies.requires
309
+ : (Array.isArray(parsed.requires) ? parsed.requires : []);
310
+ const optional = Array.isArray(parsed.dependencies?.optional)
311
+ ? parsed.dependencies.optional
312
+ : (Array.isArray(parsed.optional) ? parsed.optional : []);
313
+ const conflicts = Array.isArray(parsed.dependencies?.conflicts)
314
+ ? parsed.dependencies.conflicts
315
+ : (Array.isArray(parsed.conflicts) ? parsed.conflicts : []);
316
+
317
+ // Read SKILL.md frontmatter name if present to ensure alias parity
318
+ const discoveredAliases = [];
319
+ const entrypointFile = path.join(skillDir, entrypoint);
320
+ if (fs.existsSync(entrypointFile)) {
321
+ const fm = this.readFrontmatter(entrypointFile);
322
+ if (fm && fm.parsed && fm.parsed.name) {
323
+ const fmSlug = String(fm.parsed.name).trim().toLowerCase().replace(/\s+/g, '-');
324
+ discoveredAliases.push(fmSlug);
325
+ }
326
+ }
327
+
328
+ // Normalizing signals (merge provided signals with default signals for known skills)
329
+ const defaults = DEFAULT_SIGNALS[skillId] || {};
330
+ const signals = {
331
+ aliases: Array.from(new Set([
332
+ ...(parsed.signals?.aliases || []),
333
+ ...(defaults.aliases || []),
334
+ ...discoveredAliases,
335
+ skillId
336
+ ])),
337
+ keywords: parsed.signals?.keywords || defaults.keywords || [],
338
+ fileGlobs: parsed.signals?.fileGlobs || defaults.fileGlobs || [],
339
+ packages: parsed.signals?.packages || defaults.packages || [],
340
+ };
341
+
342
+ // Normalizing context
343
+ const priority = parsed.context?.priority !== undefined
344
+ ? parsed.context.priority
345
+ : (parsed.weight !== undefined ? parsed.weight * 10 : 50);
346
+
347
+ // Normalizing resources
348
+ let resources = [];
349
+ if (Array.isArray(parsed.resources)) {
350
+ resources = parsed.resources.map(r => {
351
+ if (typeof r === 'string') return { path: r, mode: 'on-demand' };
352
+ return { path: r.path, mode: r.mode || 'on-demand' };
353
+ });
354
+ }
355
+
356
+ const deprecated = Boolean(parsed.deprecated);
357
+ const canonical = typeof parsed.canonical === 'string' ? parsed.canonical : null;
358
+ const rules = Array.isArray(parsed.rules) ? parsed.rules : [];
359
+
360
+ return {
361
+ schemaVersion: 2,
362
+ id: skillId,
363
+ displayName,
364
+ version,
365
+ description,
366
+ type,
367
+ category,
368
+ entrypoint,
369
+ signals,
370
+ dependencies: {
371
+ requires: Array.from(new Set(requires)),
372
+ optional: Array.from(new Set(optional)),
373
+ conflicts: Array.from(new Set(conflicts)),
374
+ },
375
+ context: {
376
+ priority,
377
+ estimatedTokens: 0, // Calculated during compilation
378
+ },
379
+ resources,
380
+ deprecated,
381
+ canonical,
382
+ rules,
383
+ _raw: parsed,
384
+ _isV2: isV2,
385
+ };
386
+ }
387
+
388
+ /**
389
+ * Validates a single manifest against strict security, schema and filesystem rules.
390
+ */
391
+ validateSingleManifest(skillDir, manifest, manifestFile) {
392
+ const relManifestPath = path.relative(this.rootDir, manifestFile);
393
+
394
+ // 0. Strict Schema Validation (additionalProperties: false)
395
+ const allowedKeys = new Set([
396
+ 'schemaVersion', 'id', 'name', 'displayName', 'entrypoint', 'version',
397
+ 'description', 'type', 'category', 'dependencies', 'requires', 'optional',
398
+ 'conflicts', 'signals', 'context', 'weight', 'resources', 'deprecated',
399
+ 'canonical', 'rules'
400
+ ]);
401
+ if (manifest._raw) {
402
+ for (const key of Object.keys(manifest._raw)) {
403
+ if (!allowedKeys.has(key)) {
404
+ this.addDiagnostic(
405
+ CODES.SCHEMA_ERROR,
406
+ `Strict Schema Validation Failed: Unknown property '${key}' is not allowed in schemaVersion 2.`,
407
+ { file: relManifestPath, path: `/${key}` }
408
+ );
409
+ }
410
+ }
411
+ }
412
+
413
+ // 1. Check ID format
414
+ if (!manifest.id || !/^[a-z0-9-]+$/.test(manifest.id)) {
415
+ this.addDiagnostic(
416
+ CODES.SCHEMA_ERROR,
417
+ `Invalid skill id '${manifest.id}'. Must contain only lowercase alphanumeric characters and hyphens.`,
418
+ { file: relManifestPath, path: '/id' }
419
+ );
420
+ }
421
+
422
+ // 2. Check entrypoint on disk
423
+ if (manifest.entrypoint.includes('..') || manifest.entrypoint.startsWith('/') || manifest.entrypoint.startsWith('\\') || manifest.entrypoint.includes('\0')) {
424
+ this.addDiagnostic(
425
+ CODES.INVALID_PATH,
426
+ `Entrypoint path '${manifest.entrypoint}' contains invalid characters or directory traversal.`,
427
+ { file: relManifestPath, path: '/entrypoint' }
428
+ );
429
+ } else {
430
+ const fullEntrypoint = path.join(skillDir, manifest.entrypoint);
431
+ if (!fs.existsSync(fullEntrypoint)) {
432
+ this.addDiagnostic(
433
+ CODES.MISSING_ENTRYPOINT,
434
+ `Required entrypoint file '${manifest.entrypoint}' does not exist on disk.`,
435
+ { file: relManifestPath, path: '/entrypoint', remediation: `Create ${manifest.entrypoint} in ${path.relative(this.rootDir, skillDir)}` }
436
+ );
437
+ }
438
+ }
439
+
440
+ // 3. Frontmatter parity check with SKILL.md
441
+ const skillMdPath = path.join(skillDir, manifest.entrypoint);
442
+ if (fs.existsSync(skillMdPath)) {
443
+ const fm = this.readFrontmatter(skillMdPath);
444
+ if (fm && fm.parsed && fm.parsed.name) {
445
+ const fmName = String(fm.parsed.name).trim().toLowerCase();
446
+ const idMatches = manifest.id.toLowerCase() === fmName;
447
+ const displayNameMatches = manifest.displayName.toLowerCase() === fmName;
448
+ const aliasMatches = manifest.signals && Array.isArray(manifest.signals.aliases)
449
+ ? manifest.signals.aliases.some(a => a.toLowerCase() === fmName || a.toLowerCase() === fmName.replace(/\s+/g, '-'))
450
+ : false;
451
+
452
+ if (!idMatches && !displayNameMatches && !aliasMatches) {
453
+ this.addDiagnostic(
454
+ CODES.FRONTMATTER_MISMATCH,
455
+ `Frontmatter name '${fm.parsed.name}' in ${manifest.entrypoint} does not match manifest id '${manifest.id}', displayName '${manifest.displayName}', or registered aliases.`,
456
+ { file: relManifestPath, path: '/displayName', remediation: `Update ${manifest.entrypoint} frontmatter name to match manifest displayName '${manifest.displayName}' or add '${fm.parsed.name}' to aliases.` }
457
+ );
458
+ }
459
+ }
460
+ }
461
+
462
+ // 4. Resource paths validation
463
+ const realSkillDir = fs.realpathSync(skillDir);
464
+ for (let i = 0; i < manifest.resources.length; i++) {
465
+ const res = manifest.resources[i];
466
+ const resPath = res.path;
467
+ if (!resPath || resPath.includes('..') || path.isAbsolute(resPath) || resPath.includes('\0')) {
468
+ this.addDiagnostic(
469
+ CODES.INVALID_PATH,
470
+ `Resource path '${resPath}' is invalid or attempts path traversal.`,
471
+ { file: relManifestPath, path: `/resources/${i}/path` }
472
+ );
473
+ continue;
474
+ }
475
+
476
+ const fullResPath = path.join(skillDir, resPath);
477
+ if (!fs.existsSync(fullResPath)) {
478
+ this.addDiagnostic(
479
+ CODES.MISSING_RESOURCE,
480
+ `Resource file '${resPath}' listed in manifest does not exist.`,
481
+ { file: relManifestPath, path: `/resources/${i}/path`, remediation: `Create the file or remove it from resources.` }
482
+ );
483
+ } else {
484
+ // Symlink escape check
485
+ const realResPath = fs.realpathSync(fullResPath);
486
+ if (!realResPath.startsWith(realSkillDir)) {
487
+ this.addDiagnostic(
488
+ CODES.INVALID_PATH,
489
+ `Resource file '${resPath}' resolves outside skill directory (symlink escape).`,
490
+ { file: relManifestPath, path: `/resources/${i}/path` }
491
+ );
492
+ }
493
+ }
494
+ }
495
+
496
+ // 5. Check self-dependency
497
+ if (manifest.dependencies.requires.includes(manifest.id)) {
498
+ this.addDiagnostic(
499
+ CODES.SELF_DEPENDENCY,
500
+ `Skill '${manifest.id}' cannot require itself.`,
501
+ { file: relManifestPath, path: '/dependencies/requires' }
502
+ );
503
+ }
504
+
505
+ // 6. Validate declared rules and checkers
506
+ if (Array.isArray(manifest.rules)) {
507
+ const { AUTOMATED_CHECKERS } = require('../rules/rule-catalog.js');
508
+ for (let i = 0; i < manifest.rules.length; i++) {
509
+ const rule = manifest.rules[i];
510
+ if (!rule.id || !/^[A-Z0-9_-]+$/.test(rule.id)) {
511
+ this.addDiagnostic(
512
+ CODES.SCHEMA_ERROR,
513
+ `Rule [${i}] in '${manifest.id}' has invalid ID '${rule.id}'. Must match pattern ^[A-Z0-9_-]+$.`,
514
+ { file: relManifestPath, path: `/rules/${i}/id` }
515
+ );
516
+ }
517
+ if (!['must', 'should', 'may'].includes(rule.level)) {
518
+ this.addDiagnostic(
519
+ CODES.SCHEMA_ERROR,
520
+ `Rule '${rule.id}' has invalid level '${rule.level}'. Must be 'must', 'should', or 'may'.`,
521
+ { file: relManifestPath, path: `/rules/${i}/level` }
522
+ );
523
+ }
524
+ if (!['runtime', 'linter', 'prompt-guidance', 'reference', 'example'].includes(rule.enforcement)) {
525
+ this.addDiagnostic(
526
+ CODES.SCHEMA_ERROR,
527
+ `Rule '${rule.id}' has invalid enforcement '${rule.enforcement}'.`,
528
+ { file: relManifestPath, path: `/rules/${i}/enforcement` }
529
+ );
530
+ }
531
+ if (['runtime', 'linter'].includes(rule.enforcement)) {
532
+ if (!rule.checker) {
533
+ this.addDiagnostic(
534
+ CODES.MISSING_RULE_CHECKER,
535
+ `Rule '${rule.id}' marked enforcement '${rule.enforcement}' requires an automated checker, but none was provided.`,
536
+ { file: relManifestPath, path: `/rules/${i}/checker` }
537
+ );
538
+ } else if (!AUTOMATED_CHECKERS[rule.checker]) {
539
+ this.addDiagnostic(
540
+ CODES.MISSING_RULE_CHECKER,
541
+ `Rule '${rule.id}' references unknown automated checker '${rule.checker}'.`,
542
+ { file: relManifestPath, path: `/rules/${i}/checker` }
543
+ );
544
+ }
545
+ }
546
+ }
547
+ }
548
+ }
549
+
550
+ /**
551
+ * Validates cross-skill dependency graph (closure, cycles, conflicts).
552
+ */
553
+ validateDependencyGraph(manifestsById) {
554
+ const knownIds = new Set(Object.keys(manifestsById));
555
+
556
+ // Well-known external frameworks (not internal skills)
557
+ const EXTERNAL_NAMES = new Set([
558
+ 'vue', 'angular', 'svelte', 'remix', 'express', 'koa', 'hapi', 'sails',
559
+ 'tailwind', 'prisma', 'drizzle', 'mongoose', 'sequelize', 'typeorm',
560
+ 'next-auth', 'react-query', 'zustand', 'redux', 'mobx', 'jotai', 'valtio',
561
+ 'vitest', 'jest', 'mocha', 'cypress', 'playwright', 'storybook',
562
+ 'graphql', 'trpc', 'apollo', 'relay', 'urql',
563
+ 'sqlite', 'simple-auth', 'kubernetes', 'monitoring', 'cicd', 'cqrs',
564
+ 'redis', 'postgres', 'postgresql', 'jwt', 'auth'
565
+ ]);
566
+
567
+ // 1. Unknown dependencies & direct conflict violations
568
+ for (const [id, m] of Object.entries(manifestsById)) {
569
+ for (const req of m.dependencies.requires) {
570
+ if (!knownIds.has(req)) {
571
+ this.addDiagnostic(
572
+ CODES.UNKNOWN_DEPENDENCY,
573
+ `Skill '${id}' requires unknown skill '${req}'.`,
574
+ { file: m.source, path: '/dependencies/requires', remediation: `Ensure skill '${req}' is present in .agents/core/skills.` }
575
+ );
576
+ }
577
+
578
+ // Direct conflict violation
579
+ if (m.dependencies.conflicts.includes(req)) {
580
+ this.addDiagnostic(
581
+ CODES.CONFLICT_VIOLATION,
582
+ `Skill '${id}' both requires and conflicts with '${req}'.`,
583
+ { file: m.source, path: '/dependencies/conflicts' }
584
+ );
585
+ }
586
+
587
+ const reqManifest = manifestsById[req];
588
+ if (reqManifest && reqManifest.dependencies.conflicts.includes(id)) {
589
+ this.addDiagnostic(
590
+ CODES.CONFLICT_VIOLATION,
591
+ `Skill '${id}' requires '${req}', but '${req}' conflicts with '${id}'.`,
592
+ { file: m.source, path: '/dependencies/requires' }
593
+ );
594
+ }
595
+ }
596
+ }
597
+
598
+ // 2. Cycle detection via DFS
599
+ const visited = new Map(); // id -> 'unvisited' | 'visiting' | 'visited'
600
+ for (const id of knownIds) visited.set(id, 'unvisited');
601
+
602
+ const checkCycle = (curr, stack) => {
603
+ visited.set(curr, 'visiting');
604
+ stack.push(curr);
605
+
606
+ const m = manifestsById[curr];
607
+ if (m) {
608
+ for (const dep of m.dependencies.requires) {
609
+ if (!knownIds.has(dep)) continue;
610
+ const status = visited.get(dep);
611
+ if (status === 'visiting') {
612
+ const cyclePath = [...stack.slice(stack.indexOf(dep)), dep].join(' -> ');
613
+ this.addDiagnostic(
614
+ CODES.CYCLE_DETECTED,
615
+ `Dependency cycle detected: ${cyclePath}`,
616
+ { file: m.source, path: '/dependencies/requires' }
617
+ );
618
+ } else if (status === 'unvisited') {
619
+ checkCycle(dep, stack);
620
+ }
621
+ }
622
+ }
623
+
624
+ stack.pop();
625
+ visited.set(curr, 'visited');
626
+ };
627
+
628
+ for (const id of knownIds) {
629
+ if (visited.get(id) === 'unvisited') {
630
+ checkCycle(id, []);
631
+ }
632
+ }
633
+ }
634
+
635
+ /**
636
+ * Computes sha256 hash for a file.
637
+ */
638
+ hashFile(filePath) {
639
+ if (!fs.existsSync(filePath)) return null;
640
+ const content = fs.readFileSync(filePath);
641
+ return 'sha256:' + crypto.createHash('sha256').update(content).digest('hex');
642
+ }
643
+
644
+ /**
645
+ * Computes sha256 hash for a string.
646
+ */
647
+ hashString(str) {
648
+ return 'sha256:' + crypto.createHash('sha256').update(str, 'utf8').digest('hex');
649
+ }
650
+
651
+ /**
652
+ * Compiles all skills in coreSkillsDir and plugins.
653
+ * Generates deterministic CompiledRegistryV2 structure.
654
+ */
655
+ compile() {
656
+ this.diagnostics = [];
657
+ const manifestsById = {};
658
+ const skillDirs = [];
659
+
660
+ // 1. Discover skill directories
661
+ if (fs.existsSync(this.coreSkillsDir)) {
662
+ const entries = fs.readdirSync(this.coreSkillsDir, { withFileTypes: true });
663
+ for (const entry of entries) {
664
+ if (entry.isDirectory()) {
665
+ skillDirs.push(path.join(this.coreSkillsDir, entry.name));
666
+ }
667
+ }
668
+ }
669
+
670
+ // Also check plugins
671
+ const pluginsDir = path.join(this.agentsDir, 'plugins');
672
+ if (fs.existsSync(pluginsDir)) {
673
+ const pluginEntries = fs.readdirSync(pluginsDir, { withFileTypes: true });
674
+ for (const pe of pluginEntries) {
675
+ if (pe.isDirectory()) {
676
+ const pSkills = path.join(pluginsDir, pe.name, 'skills');
677
+ if (fs.existsSync(pSkills)) {
678
+ const subs = fs.readdirSync(pSkills, { withFileTypes: true });
679
+ for (const sub of subs) {
680
+ if (sub.isDirectory()) skillDirs.push(path.join(pSkills, sub.name));
681
+ }
682
+ }
683
+ }
684
+ }
685
+ }
686
+
687
+ // Sort skill directories for determinism
688
+ skillDirs.sort();
689
+
690
+ // 2. Parse and normalize manifests
691
+ for (const dir of skillDirs) {
692
+ const v2File = path.join(dir, 'skill.v2.yaml');
693
+ const v1File = path.join(dir, 'skill.yaml');
694
+ const manifestFile = fs.existsSync(v2File) ? v2File : (fs.existsSync(v1File) ? v1File : null);
695
+
696
+ if (!manifestFile) {
697
+ // Synthesize minimal manifest from directory name & SKILL.md
698
+ const id = path.basename(dir);
699
+ const norm = this.normalizeManifest(dir, `id: ${id}\nname: ${id}\n`);
700
+ norm.source = path.relative(this.rootDir, dir).replace(/\\/g, '/');
701
+ manifestsById[id] = norm;
702
+ continue;
703
+ }
704
+
705
+ try {
706
+ const rawYaml = fs.readFileSync(manifestFile, 'utf8');
707
+ const norm = this.normalizeManifest(dir, rawYaml);
708
+ norm.source = path.relative(this.rootDir, manifestFile).replace(/\\/g, '/');
709
+
710
+ if (manifestsById[norm.id]) {
711
+ this.addDiagnostic(
712
+ CODES.DUPLICATE_ID,
713
+ `Duplicate skill id '${norm.id}' found in ${norm.source}`,
714
+ { file: norm.source, path: '/id' }
715
+ );
716
+ }
717
+
718
+ manifestsById[norm.id] = norm;
719
+ this.validateSingleManifest(dir, norm, manifestFile);
720
+ } catch (err) {
721
+ this.addDiagnostic(
722
+ err.diag ? err.diag.code : CODES.PARSE_ERROR,
723
+ `Failed to parse manifest at ${path.relative(this.rootDir, manifestFile)}: ${err.message}`,
724
+ {
725
+ file: path.relative(this.rootDir, manifestFile),
726
+ line: err.diag ? err.diag.line : undefined,
727
+ column: err.diag ? err.diag.column : undefined
728
+ }
729
+ );
730
+ }
731
+ }
732
+
733
+ // 3. Cross-skill dependency graph validation
734
+ this.validateDependencyGraph(manifestsById);
735
+
736
+ // If any errors exist, stop and return diagnostics
737
+ const hasErrors = this.diagnostics.some(d => d.severity === 'error');
738
+ if (hasErrors) {
739
+ return {
740
+ success: false,
741
+ diagnostics: this.diagnostics,
742
+ registry: null,
743
+ };
744
+ }
745
+
746
+ // 4. Compute hashes, estimated tokens, aliases and packageMap
747
+ const compiledSkills = {};
748
+ const aliases = {};
749
+ const deprecatedAliases = {};
750
+ const packageMap = {};
751
+ const dependencyGraph = {};
752
+ const rules = {};
753
+
754
+ const sortedIds = Object.keys(manifestsById).sort();
755
+
756
+ for (const id of sortedIds) {
757
+ const m = manifestsById[id];
758
+ const dir = path.join(this.rootDir, path.dirname(m.source));
759
+ const entrypointFile = path.join(dir, m.entrypoint);
760
+
761
+ // Entrypoint hash
762
+ const entrypointHash = this.hashFile(entrypointFile) || 'sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855';
763
+
764
+ // Estimated tokens: entrypoint character length / 3.8
765
+ let estimatedTokens = 1000;
766
+ if (fs.existsSync(entrypointFile)) {
767
+ const chars = fs.readFileSync(entrypointFile, 'utf8').length;
768
+ estimatedTokens = Math.max(100, Math.ceil(chars / 3.8));
769
+ }
770
+
771
+ // Resource tree hash
772
+ const resHashes = [];
773
+ for (const res of m.resources) {
774
+ const rFile = path.join(dir, res.path);
775
+ const rHash = this.hashFile(rFile);
776
+ if (rHash) resHashes.push(`${res.path}:${rHash}`);
777
+ }
778
+ resHashes.sort();
779
+ const resourceTreeHash = this.hashString(resHashes.join('\n'));
780
+
781
+ compiledSkills[id] = {
782
+ id: m.id,
783
+ displayName: m.displayName,
784
+ version: m.version,
785
+ description: m.description,
786
+ category: m.category,
787
+ type: m.type,
788
+ entrypoint: m.entrypoint,
789
+ entrypointHash,
790
+ resourceTreeHash,
791
+ estimatedTokens,
792
+ source: m.source,
793
+ signals: {
794
+ aliases: m.signals.aliases.sort(),
795
+ keywords: m.signals.keywords,
796
+ fileGlobs: m.signals.fileGlobs,
797
+ packages: m.signals.packages,
798
+ },
799
+ dependencies: {
800
+ requires: m.dependencies.requires.sort(),
801
+ optional: m.dependencies.optional.sort(),
802
+ conflicts: m.dependencies.conflicts.sort(),
803
+ },
804
+ context: {
805
+ priority: m.context.priority,
806
+ estimatedTokens,
807
+ },
808
+ resources: m.resources,
809
+ deprecated: m.deprecated || false,
810
+ canonical: m.canonical || null,
811
+ rules: m.rules || [],
812
+ };
813
+
814
+ // Register deprecated alias mapping
815
+ if (m.deprecated && m.canonical) {
816
+ deprecatedAliases[id] = m.canonical;
817
+ }
818
+
819
+ // Register rules
820
+ if (Array.isArray(m.rules)) {
821
+ for (const r of m.rules) {
822
+ rules[r.id] = {
823
+ ...r,
824
+ sourceSkill: id,
825
+ };
826
+ }
827
+ }
828
+
829
+ // Register aliases
830
+ for (const alias of m.signals.aliases) {
831
+ if (!aliases[alias]) {
832
+ aliases[alias] = id;
833
+ }
834
+ }
835
+
836
+ // Register packageMap
837
+ for (const pkg of m.signals.packages) {
838
+ packageMap[`${pkg.ecosystem}:${pkg.name}`] = id;
839
+ }
840
+
841
+ // Register dependencyGraph
842
+ dependencyGraph[id] = m.dependencies.requires.sort();
843
+ }
844
+
845
+ // Overall sourceGraphHash
846
+ const sourceGraphHash = this.hashString(JSON.stringify({
847
+ skills: compiledSkills,
848
+ dependencyGraph,
849
+ }));
850
+
851
+ const registry = {
852
+ schemaVersion: 2,
853
+ compilerVersion: '2.0.0',
854
+ sourceGraphHash,
855
+ skills: compiledSkills,
856
+ aliases: sortObjectKeys(aliases),
857
+ deprecatedAliases: sortObjectKeys(deprecatedAliases),
858
+ packageMap: sortObjectKeys(packageMap),
859
+ dependencyGraph: sortObjectKeys(dependencyGraph),
860
+ rules: sortObjectKeys(rules),
861
+ };
862
+
863
+ return {
864
+ success: true,
865
+ diagnostics: this.diagnostics,
866
+ registry,
867
+ };
868
+ }
869
+
870
+ /**
871
+ * Compiles and writes .agents/compiled/registry.v2.json and .agents/compiled/registry.v2.sha256.
872
+ */
873
+ compileAndWrite() {
874
+ const result = this.compile();
875
+ if (!result.success) {
876
+ return result;
877
+ }
878
+
879
+ if (!fs.existsSync(this.compiledDir)) {
880
+ fs.mkdirSync(this.compiledDir, { recursive: true });
881
+ }
882
+
883
+ const registryJsonPath = path.join(this.compiledDir, 'registry.v2.json');
884
+ const shaPath = path.join(this.compiledDir, 'registry.v2.sha256');
885
+
886
+ const jsonString = JSON.stringify(result.registry, null, 2) + '\n';
887
+ fs.writeFileSync(registryJsonPath, jsonString, 'utf8');
888
+
889
+ const shaContent = result.registry.sourceGraphHash + ' registry.v2.json\n';
890
+ fs.writeFileSync(shaPath, shaContent, 'utf8');
891
+
892
+ return {
893
+ ...result,
894
+ registryJsonPath,
895
+ shaPath,
896
+ };
897
+ }
898
+
899
+ /**
900
+ * Formats diagnostics as SARIF 2.1.0 output.
901
+ */
902
+ formatSarif() {
903
+ const rules = [];
904
+ const results = [];
905
+
906
+ const ruleIds = new Set(this.diagnostics.map(d => d.code));
907
+ for (const code of ruleIds) {
908
+ rules.push({
909
+ id: code,
910
+ name: code,
911
+ shortDescription: { text: `ContextOS Manifest Rule ${code}` },
912
+ });
913
+ }
914
+
915
+ for (const d of this.diagnostics) {
916
+ results.push({
917
+ ruleId: d.code,
918
+ level: d.severity === 'error' ? 'error' : 'warning',
919
+ message: { text: d.message },
920
+ locations: [
921
+ {
922
+ physicalLocation: {
923
+ artifactLocation: { uri: d.file.replace(/\\/g, '/') },
924
+ ...(d.line ? { region: { startLine: d.line, ...(d.column ? { startColumn: d.column } : {}) } } : {})
925
+ },
926
+ },
927
+ ],
928
+ });
929
+ }
930
+
931
+ return {
932
+ $schema: 'https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json',
933
+ version: '2.1.0',
934
+ runs: [
935
+ {
936
+ tool: {
937
+ driver: {
938
+ name: 'contextos-manifest-compiler',
939
+ version: '2.0.0',
940
+ rules,
941
+ },
942
+ },
943
+ results,
944
+ },
945
+ ],
946
+ };
947
+ }
948
+ }
949
+
950
+ function sortObjectKeys(obj) {
951
+ const sorted = {};
952
+ for (const k of Object.keys(obj).sort()) {
953
+ sorted[k] = obj[k];
954
+ }
955
+ return sorted;
956
+ }
957
+
958
+ module.exports = {
959
+ ManifestCompiler,
960
+ DiagnosticError,
961
+ CODES,
962
+ parseYaml,
963
+ };