contextos-agents 1.7.0 → 2.0.0-beta.3

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 (313) hide show
  1. package/.agents/AGENTS.md +16 -50
  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 +100 -104
  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 +537 -112
  26. package/.agents/customization-dx.js +282 -0
  27. package/.agents/doctor.js +855 -66
  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 +278 -64
  38. package/.agents/profiles.js +486 -51
  39. package/.agents/resolver/canonical-resolver.js +1348 -0
  40. package/.agents/resolver.js +50 -534
  41. package/.agents/rules/rule-catalog.js +525 -0
  42. package/.agents/schemas/attestation.review.v1.json +111 -0
  43. package/.agents/schemas/attestation.verification.v1.json +85 -0
  44. package/.agents/schemas/lockfile.v2.schema.json +134 -0
  45. package/.agents/schemas/profile.v2.schema.json +114 -0
  46. package/.agents/schemas/runtime.thread.v1.json +192 -0
  47. package/.agents/schemas/skill.manifest.v2.json +177 -0
  48. package/.agents/schemas/verification.spec.v1.json +39 -0
  49. package/.agents/schemas/workspace.graph.schema.json +106 -0
  50. package/.agents/skills-index.json +6 -166
  51. package/.agents/stats.js +9 -9
  52. package/.agents/transaction-core/event-store.js +288 -0
  53. package/.agents/transaction-core/idempotency.js +129 -0
  54. package/.agents/transaction-core/ipc-lock.js +311 -0
  55. package/.agents/transaction-core/plugin-supply-chain-bundle.js +436 -0
  56. package/.agents/validate.js +44 -1
  57. package/.agents/watch.js +354 -102
  58. package/.agents/workspace/workspace-graph.js +778 -0
  59. package/README.md +59 -387
  60. package/benchmarks/v2/analysis/statistics.js +140 -0
  61. package/benchmarks/v2/analysis/stats.js +69 -0
  62. package/benchmarks/v2/arms/arm-definitions.js +79 -0
  63. package/benchmarks/v2/dataset.schema.json +34 -0
  64. package/benchmarks/v2/evaluators/index.js +25 -0
  65. package/benchmarks/v2/evaluators/verified-success.js +116 -0
  66. package/benchmarks/v2/harness/runner.js +88 -0
  67. package/benchmarks/v2/pilot-tasks.json +392 -0
  68. package/bin/commands/recover.js +88 -0
  69. package/bin/commands/update.js +80 -17
  70. package/bin/commands.js +62 -25
  71. package/bin/index.js +138 -81
  72. package/bin/lib/lockfile.js +5 -3
  73. package/bin/lib/safe-writer.js +34 -3
  74. package/package.json +87 -72
  75. package/registry.json +2 -2
  76. package/registry.v2.schema.json +86 -0
  77. package/.agents/core/profiles/backend.yaml +0 -47
  78. package/.agents/core/profiles/enterprise.yaml +0 -46
  79. package/.agents/core/profiles/frontend.yaml +0 -46
  80. package/.agents/core/profiles/hackathon.yaml +0 -45
  81. package/.agents/core/profiles/mvp.yaml +0 -44
  82. package/.agents/core/profiles/startup.yaml +0 -48
  83. package/.agents/core/skills/adapters/EXAMPLES.md +0 -19
  84. package/.agents/core/skills/adapters/SKILL.md +0 -105
  85. package/.agents/core/skills/adapters/TROUBLESHOOTING.md +0 -7
  86. package/.agents/core/skills/adapters/VALIDATION.json +0 -12
  87. package/.agents/core/skills/adapters/skill.yaml +0 -16
  88. package/.agents/core/skills/architecture-diagrams/SKILL.md +0 -108
  89. package/.agents/core/skills/architecture-diagrams/VALIDATION.json +0 -12
  90. package/.agents/core/skills/architecture-diagrams/skill.yaml +0 -12
  91. package/.agents/core/skills/brutalist-design/SKILL.md +0 -150
  92. package/.agents/core/skills/brutalist-design/VALIDATION.json +0 -12
  93. package/.agents/core/skills/brutalist-design/skill.yaml +0 -12
  94. package/.agents/core/skills/database/EXAMPLES.md +0 -74
  95. package/.agents/core/skills/database/SKILL.md +0 -101
  96. package/.agents/core/skills/database/TROUBLESHOOTING.md +0 -18
  97. package/.agents/core/skills/database/VALIDATION.json +0 -11
  98. package/.agents/core/skills/database/skill.yaml +0 -31
  99. package/.agents/core/skills/ddd/EXAMPLES.md +0 -42
  100. package/.agents/core/skills/ddd/SKILL.md +0 -247
  101. package/.agents/core/skills/ddd/TROUBLESHOOTING.md +0 -19
  102. package/.agents/core/skills/ddd/VALIDATION.json +0 -12
  103. package/.agents/core/skills/ddd/ddd.md +0 -178
  104. package/.agents/core/skills/ddd/skill.yaml +0 -17
  105. package/.agents/core/skills/decisions/EXAMPLES.md +0 -35
  106. package/.agents/core/skills/decisions/SKILL.md +0 -90
  107. package/.agents/core/skills/decisions/TROUBLESHOOTING.md +0 -13
  108. package/.agents/core/skills/decisions/VALIDATION.json +0 -12
  109. package/.agents/core/skills/decisions/skill.yaml +0 -16
  110. package/.agents/core/skills/docker/EXAMPLES.md +0 -56
  111. package/.agents/core/skills/docker/SKILL.md +0 -63
  112. package/.agents/core/skills/docker/TROUBLESHOOTING.md +0 -18
  113. package/.agents/core/skills/docker/VALIDATION.json +0 -11
  114. package/.agents/core/skills/docker/skill.yaml +0 -29
  115. package/.agents/core/skills/fastapi/EXAMPLES.md +0 -36
  116. package/.agents/core/skills/fastapi/SKILL.md +0 -148
  117. package/.agents/core/skills/fastapi/TROUBLESHOOTING.md +0 -19
  118. package/.agents/core/skills/fastapi/VALIDATION.json +0 -12
  119. package/.agents/core/skills/fastapi/fastapi.md +0 -112
  120. package/.agents/core/skills/fastapi/skill.yaml +0 -17
  121. package/.agents/core/skills/generators/EXAMPLES.md +0 -19
  122. package/.agents/core/skills/generators/SKILL.md +0 -112
  123. package/.agents/core/skills/generators/TROUBLESHOOTING.md +0 -7
  124. package/.agents/core/skills/generators/VALIDATION.json +0 -12
  125. package/.agents/core/skills/generators/skill.yaml +0 -25
  126. package/.agents/core/skills/generators/templates/API.md +0 -77
  127. package/.agents/core/skills/generators/templates/ARCHITECTURE.md +0 -70
  128. package/.agents/core/skills/generators/templates/DATABASE.md +0 -42
  129. package/.agents/core/skills/generators/templates/DECISION.md +0 -46
  130. package/.agents/core/skills/generators/templates/PRD.md +0 -67
  131. package/.agents/core/skills/generators/templates/PROJECT_GRAPH.md +0 -56
  132. package/.agents/core/skills/generators/templates/ROADMAP.md +0 -51
  133. package/.agents/core/skills/generators/templates/TASKS.md +0 -43
  134. package/.agents/core/skills/generators/templates/UI.md +0 -73
  135. package/.agents/core/skills/graphify/EXAMPLES.md +0 -73
  136. package/.agents/core/skills/graphify/SKILL.md +0 -130
  137. package/.agents/core/skills/graphify/VALIDATION.json +0 -12
  138. package/.agents/core/skills/graphify/skill.yaml +0 -18
  139. package/.agents/core/skills/impeccable-design/EXAMPLES.md +0 -26
  140. package/.agents/core/skills/impeccable-design/SKILL.md +0 -201
  141. package/.agents/core/skills/impeccable-design/TROUBLESHOOTING.md +0 -19
  142. package/.agents/core/skills/impeccable-design/VALIDATION.json +0 -12
  143. package/.agents/core/skills/impeccable-design/skill.yaml +0 -20
  144. package/.agents/core/skills/interview-me/SKILL.md +0 -97
  145. package/.agents/core/skills/interview-me/VALIDATION.json +0 -12
  146. package/.agents/core/skills/interview-me/skill.yaml +0 -12
  147. package/.agents/core/skills/microservices/EXAMPLES.md +0 -38
  148. package/.agents/core/skills/microservices/SKILL.md +0 -164
  149. package/.agents/core/skills/microservices/TROUBLESHOOTING.md +0 -19
  150. package/.agents/core/skills/microservices/VALIDATION.json +0 -12
  151. package/.agents/core/skills/microservices/microservices.md +0 -119
  152. package/.agents/core/skills/microservices/skill.yaml +0 -17
  153. package/.agents/core/skills/minimalist-design/SKILL.md +0 -113
  154. package/.agents/core/skills/minimalist-design/VALIDATION.json +0 -12
  155. package/.agents/core/skills/minimalist-design/skill.yaml +0 -12
  156. package/.agents/core/skills/nestjs/EXAMPLES.md +0 -40
  157. package/.agents/core/skills/nestjs/SKILL.md +0 -139
  158. package/.agents/core/skills/nestjs/TROUBLESHOOTING.md +0 -19
  159. package/.agents/core/skills/nestjs/VALIDATION.json +0 -12
  160. package/.agents/core/skills/nestjs/nestjs.md +0 -103
  161. package/.agents/core/skills/nestjs/skill.yaml +0 -17
  162. package/.agents/core/skills/nextjs/EXAMPLES.md +0 -40
  163. package/.agents/core/skills/nextjs/SKILL.md +0 -163
  164. package/.agents/core/skills/nextjs/TROUBLESHOOTING.md +0 -19
  165. package/.agents/core/skills/nextjs/VALIDATION.json +0 -12
  166. package/.agents/core/skills/nextjs/nextjs.md +0 -67
  167. package/.agents/core/skills/nextjs/skill.yaml +0 -17
  168. package/.agents/core/skills/node/EXAMPLES.md +0 -80
  169. package/.agents/core/skills/node/SKILL.md +0 -128
  170. package/.agents/core/skills/node/TROUBLESHOOTING.md +0 -19
  171. package/.agents/core/skills/node/VALIDATION.json +0 -12
  172. package/.agents/core/skills/node/node.md +0 -87
  173. package/.agents/core/skills/node/skill.yaml +0 -17
  174. package/.agents/core/skills/performance/EXAMPLES.md +0 -30
  175. package/.agents/core/skills/performance/SKILL.md +0 -75
  176. package/.agents/core/skills/performance/TROUBLESHOOTING.md +0 -19
  177. package/.agents/core/skills/performance/VALIDATION.json +0 -12
  178. package/.agents/core/skills/performance/performance.md +0 -52
  179. package/.agents/core/skills/performance/skill.yaml +0 -17
  180. package/.agents/core/skills/react/EXAMPLES.md +0 -79
  181. package/.agents/core/skills/react/SKILL.md +0 -132
  182. package/.agents/core/skills/react/TROUBLESHOOTING.md +0 -19
  183. package/.agents/core/skills/react/VALIDATION.json +0 -12
  184. package/.agents/core/skills/react/react.md +0 -93
  185. package/.agents/core/skills/react/skill.yaml +0 -17
  186. package/.agents/core/skills/react-best-practices/SKILL.md +0 -155
  187. package/.agents/core/skills/react-best-practices/VALIDATION.json +0 -12
  188. package/.agents/core/skills/react-best-practices/skill.yaml +0 -14
  189. package/.agents/core/skills/redesign-audit/SKILL.md +0 -117
  190. package/.agents/core/skills/redesign-audit/VALIDATION.json +0 -12
  191. package/.agents/core/skills/redesign-audit/skill.yaml +0 -12
  192. package/.agents/core/skills/soft-design/SKILL.md +0 -108
  193. package/.agents/core/skills/soft-design/VALIDATION.json +0 -12
  194. package/.agents/core/skills/soft-design/skill.yaml +0 -12
  195. package/.agents/core/skills/state-management/EXAMPLES.md +0 -56
  196. package/.agents/core/skills/state-management/SKILL.md +0 -48
  197. package/.agents/core/skills/state-management/TROUBLESHOOTING.md +0 -18
  198. package/.agents/core/skills/state-management/VALIDATION.json +0 -11
  199. package/.agents/core/skills/state-management/skill.yaml +0 -28
  200. package/.agents/core/skills/subagent-orchestrator/SKILL.md +0 -117
  201. package/.agents/core/skills/subagent-orchestrator/VALIDATION.json +0 -12
  202. package/.agents/core/skills/subagent-orchestrator/skill.yaml +0 -12
  203. package/.agents/core/skills/system-design/EXAMPLES.md +0 -75
  204. package/.agents/core/skills/system-design/SKILL.md +0 -419
  205. package/.agents/core/skills/system-design/TROUBLESHOOTING.md +0 -19
  206. package/.agents/core/skills/system-design/VALIDATION.json +0 -12
  207. package/.agents/core/skills/system-design/skill.yaml +0 -20
  208. package/.agents/core/skills/system-design/system-design.md +0 -112
  209. package/.agents/core/skills/testing/EXAMPLES.md +0 -71
  210. package/.agents/core/skills/testing/SKILL.md +0 -70
  211. package/.agents/core/skills/testing/TROUBLESHOOTING.md +0 -18
  212. package/.agents/core/skills/testing/VALIDATION.json +0 -11
  213. package/.agents/core/skills/testing/skill.yaml +0 -32
  214. package/.agents/core/skills/typescript/EXAMPLES.md +0 -64
  215. package/.agents/core/skills/typescript/SKILL.md +0 -112
  216. package/.agents/core/skills/typescript/TROUBLESHOOTING.md +0 -19
  217. package/.agents/core/skills/typescript/VALIDATION.json +0 -12
  218. package/.agents/core/skills/typescript/skill.yaml +0 -17
  219. package/.agents/core/skills/typescript/typescript.md +0 -71
  220. package/.agents/core/skills/ui-design/EXAMPLES.md +0 -21
  221. package/.agents/core/skills/ui-design/SKILL.md +0 -124
  222. package/.agents/core/skills/ui-design/TROUBLESHOOTING.md +0 -19
  223. package/.agents/core/skills/ui-design/VALIDATION.json +0 -12
  224. package/.agents/core/skills/ui-design/skill.yaml +0 -17
  225. package/.agents/core/skills/ui-design/ui.md +0 -88
  226. package/.agents/core/skills/ui-ux-pro/EXAMPLES.md +0 -62
  227. package/.agents/core/skills/ui-ux-pro/SKILL.md +0 -375
  228. package/.agents/core/skills/ui-ux-pro/TROUBLESHOOTING.md +0 -19
  229. package/.agents/core/skills/ui-ux-pro/VALIDATION.json +0 -12
  230. package/.agents/core/skills/ui-ux-pro/skill.yaml +0 -19
  231. package/.agents/core/skills/ux-design/EXAMPLES.md +0 -36
  232. package/.agents/core/skills/ux-design/SKILL.md +0 -116
  233. package/.agents/core/skills/ux-design/TROUBLESHOOTING.md +0 -19
  234. package/.agents/core/skills/ux-design/VALIDATION.json +0 -12
  235. package/.agents/core/skills/ux-design/skill.yaml +0 -17
  236. package/.agents/core/skills/ux-design/ux.md +0 -80
  237. package/.agents/core/skills/vercel-optimize/SKILL.md +0 -83
  238. package/.agents/core/skills/vercel-optimize/VALIDATION.json +0 -12
  239. package/.agents/core/skills/vercel-optimize/scripts/collect-signals.mjs +0 -131
  240. package/.agents/core/skills/vercel-optimize/scripts/gate-investigations.mjs +0 -142
  241. package/.agents/core/skills/vercel-optimize/scripts/merge-signals.mjs +0 -143
  242. package/.agents/core/skills/vercel-optimize/scripts/scan-codebase.mjs +0 -174
  243. package/.agents/core/skills/vercel-optimize/skill.yaml +0 -18
  244. package/.agents/core/skills/web-accessibility/EXAMPLES.md +0 -39
  245. package/.agents/core/skills/web-accessibility/SKILL.md +0 -170
  246. package/.agents/core/skills/web-accessibility/TROUBLESHOOTING.md +0 -19
  247. package/.agents/core/skills/web-accessibility/VALIDATION.json +0 -12
  248. package/.agents/core/skills/web-accessibility/accessibility.md +0 -63
  249. package/.agents/core/skills/web-accessibility/skill.yaml +0 -17
  250. package/.agents/generated/claude/skills/adapters/SKILL.md +0 -126
  251. package/.agents/generated/claude/skills/architecture-diagrams/SKILL.md +0 -101
  252. package/.agents/generated/claude/skills/brutalist-design/SKILL.md +0 -145
  253. package/.agents/generated/claude/skills/database/SKILL.md +0 -191
  254. package/.agents/generated/claude/skills/ddd/SKILL.md +0 -305
  255. package/.agents/generated/claude/skills/decisions/SKILL.md +0 -134
  256. package/.agents/generated/claude/skills/docker/SKILL.md +0 -135
  257. package/.agents/generated/claude/skills/fastapi/SKILL.md +0 -200
  258. package/.agents/generated/claude/skills/generators/SKILL.md +0 -133
  259. package/.agents/generated/claude/skills/graphify/SKILL.md +0 -198
  260. package/.agents/generated/claude/skills/impeccable-design/SKILL.md +0 -241
  261. package/.agents/generated/claude/skills/interview-me/SKILL.md +0 -90
  262. package/.agents/generated/claude/skills/microservices/SKILL.md +0 -218
  263. package/.agents/generated/claude/skills/minimalist-design/SKILL.md +0 -108
  264. package/.agents/generated/claude/skills/nestjs/SKILL.md +0 -195
  265. package/.agents/generated/claude/skills/nextjs/SKILL.md +0 -219
  266. package/.agents/generated/claude/skills/node/SKILL.md +0 -224
  267. package/.agents/generated/claude/skills/performance/SKILL.md +0 -121
  268. package/.agents/generated/claude/skills/react/SKILL.md +0 -227
  269. package/.agents/generated/claude/skills/react-best-practices/SKILL.md +0 -146
  270. package/.agents/generated/claude/skills/redesign-audit/SKILL.md +0 -112
  271. package/.agents/generated/claude/skills/soft-design/SKILL.md +0 -103
  272. package/.agents/generated/claude/skills/state-management/SKILL.md +0 -120
  273. package/.agents/generated/claude/skills/subagent-orchestrator/SKILL.md +0 -110
  274. package/.agents/generated/claude/skills/system-design/SKILL.md +0 -507
  275. package/.agents/generated/claude/skills/testing/SKILL.md +0 -157
  276. package/.agents/generated/claude/skills/typescript/SKILL.md +0 -192
  277. package/.agents/generated/claude/skills/ui-design/SKILL.md +0 -161
  278. package/.agents/generated/claude/skills/ui-ux-pro/SKILL.md +0 -451
  279. package/.agents/generated/claude/skills/ux-design/SKILL.md +0 -168
  280. package/.agents/generated/claude/skills/vercel-optimize/SKILL.md +0 -76
  281. package/.agents/generated/claude/skills/web-accessibility/SKILL.md +0 -225
  282. package/.agents/generated/gemini/skills/adapters/SKILL.md +0 -135
  283. package/.agents/generated/gemini/skills/architecture-diagrams/SKILL.md +0 -107
  284. package/.agents/generated/gemini/skills/brutalist-design/SKILL.md +0 -151
  285. package/.agents/generated/gemini/skills/database/SKILL.md +0 -200
  286. package/.agents/generated/gemini/skills/ddd/SKILL.md +0 -314
  287. package/.agents/generated/gemini/skills/decisions/SKILL.md +0 -143
  288. package/.agents/generated/gemini/skills/docker/SKILL.md +0 -144
  289. package/.agents/generated/gemini/skills/fastapi/SKILL.md +0 -209
  290. package/.agents/generated/gemini/skills/generators/SKILL.md +0 -142
  291. package/.agents/generated/gemini/skills/graphify/SKILL.md +0 -205
  292. package/.agents/generated/gemini/skills/impeccable-design/SKILL.md +0 -250
  293. package/.agents/generated/gemini/skills/interview-me/SKILL.md +0 -96
  294. package/.agents/generated/gemini/skills/microservices/SKILL.md +0 -227
  295. package/.agents/generated/gemini/skills/minimalist-design/SKILL.md +0 -114
  296. package/.agents/generated/gemini/skills/nestjs/SKILL.md +0 -204
  297. package/.agents/generated/gemini/skills/nextjs/SKILL.md +0 -298
  298. package/.agents/generated/gemini/skills/node/SKILL.md +0 -323
  299. package/.agents/generated/gemini/skills/performance/SKILL.md +0 -185
  300. package/.agents/generated/gemini/skills/react/SKILL.md +0 -332
  301. package/.agents/generated/gemini/skills/react-best-practices/SKILL.md +0 -152
  302. package/.agents/generated/gemini/skills/redesign-audit/SKILL.md +0 -118
  303. package/.agents/generated/gemini/skills/soft-design/SKILL.md +0 -109
  304. package/.agents/generated/gemini/skills/state-management/SKILL.md +0 -129
  305. package/.agents/generated/gemini/skills/subagent-orchestrator/SKILL.md +0 -116
  306. package/.agents/generated/gemini/skills/system-design/SKILL.md +0 -631
  307. package/.agents/generated/gemini/skills/testing/SKILL.md +0 -166
  308. package/.agents/generated/gemini/skills/typescript/SKILL.md +0 -275
  309. package/.agents/generated/gemini/skills/ui-design/SKILL.md +0 -170
  310. package/.agents/generated/gemini/skills/ui-ux-pro/SKILL.md +0 -460
  311. package/.agents/generated/gemini/skills/ux-design/SKILL.md +0 -177
  312. package/.agents/generated/gemini/skills/vercel-optimize/SKILL.md +0 -82
  313. 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
+ };