contextos-agents 1.6.1 → 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 (323) hide show
  1. package/.agents/AGENTS.md +6 -1
  2. package/.agents/adapters/aider/export.js +117 -97
  3. package/.agents/adapters/claude/export.js +68 -26
  4. package/.agents/adapters/copilot/export.js +90 -51
  5. package/.agents/adapters/cursor/export.js +83 -68
  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 +109 -62
  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/references/context-rules.md +59 -0
  18. package/.agents/core/skills/context-manager/skill.yaml +10 -5
  19. package/.agents/core/skills/context-os/SKILL.md +3 -6
  20. package/.agents/core/skills/context-os/skill.yaml +14 -8
  21. package/.agents/core/skills/engineering-workflow/SKILL.md +1 -1
  22. package/.agents/core/skills/engineering-workflow/skill.yaml +7 -7
  23. package/.agents/core/skills/gemini-precision/SKILL.md +4 -0
  24. package/.agents/core/skills/gemini-precision/skill.yaml +5 -6
  25. package/.agents/core/skills/gstack-roles/SKILL.md +3 -1
  26. package/.agents/core/skills/gstack-roles/skill.yaml +9 -6
  27. package/.agents/core/skills/ponytail-mindset/skill.yaml +7 -7
  28. package/.agents/core/skills/security/skill.yaml +21 -2
  29. package/.agents/ctx.js +587 -111
  30. package/.agents/customization-dx.js +282 -0
  31. package/.agents/doctor.js +877 -33
  32. package/.agents/filesystem/index.js +71 -0
  33. package/.agents/filesystem/journaled-transaction.js +451 -0
  34. package/.agents/filesystem/lockfile-v2.js +275 -0
  35. package/.agents/filesystem/platform-hardening.js +222 -0
  36. package/.agents/filesystem/project-lock.js +218 -0
  37. package/.agents/filesystem/safe-path.js +256 -0
  38. package/.agents/generated/claude/skills/context-os/SKILL.md +1 -1
  39. package/.agents/generated/claude/skills/engineering-workflow/SKILL.md +1 -1
  40. package/.agents/generated/claude/skills/gemini-precision/SKILL.md +4 -0
  41. package/.agents/generated/claude/skills/gstack-roles/SKILL.md +3 -1
  42. package/.agents/generated/gemini/skills/context-os/SKILL.md +2 -2
  43. package/.agents/generated/gemini/skills/engineering-workflow/SKILL.md +1 -1
  44. package/.agents/generated/gemini/skills/gemini-precision/SKILL.md +4 -0
  45. package/.agents/generated/gemini/skills/gstack-roles/SKILL.md +3 -1
  46. package/.agents/plugins/contextos/hooks.json +25 -0
  47. package/.agents/plugins/contextos/plugin.json +19 -0
  48. package/.agents/plugins.js +432 -73
  49. package/.agents/profiles.js +507 -46
  50. package/.agents/resolver.js +50 -414
  51. package/.agents/schemas/attestation.review.v1.json +111 -0
  52. package/.agents/schemas/attestation.verification.v1.json +85 -0
  53. package/.agents/schemas/lockfile.v2.schema.json +134 -0
  54. package/.agents/schemas/profile.v2.schema.json +114 -0
  55. package/.agents/schemas/runtime.thread.v1.json +192 -0
  56. package/.agents/schemas/skill.manifest.v2.json +177 -0
  57. package/.agents/schemas/verification.spec.v1.json +39 -0
  58. package/.agents/schemas/workspace.graph.schema.json +106 -0
  59. package/.agents/stats.js +22 -9
  60. package/.agents/transaction-core/event-store.js +288 -0
  61. package/.agents/transaction-core/idempotency.js +129 -0
  62. package/.agents/transaction-core/ipc-lock.js +311 -0
  63. package/.agents/transaction-core/plugin-supply-chain-bundle.js +436 -0
  64. package/.agents/validate.js +143 -14
  65. package/.agents/watch.js +354 -102
  66. package/.agents/workspace/workspace-graph.js +778 -0
  67. package/README.md +59 -387
  68. package/benchmarks/v2/analysis/statistics.js +140 -0
  69. package/benchmarks/v2/analysis/stats.js +69 -0
  70. package/benchmarks/v2/arms/arm-definitions.js +79 -0
  71. package/benchmarks/v2/dataset.schema.json +34 -0
  72. package/benchmarks/v2/evaluators/index.js +25 -0
  73. package/benchmarks/v2/evaluators/verified-success.js +116 -0
  74. package/benchmarks/v2/harness/runner.js +88 -0
  75. package/benchmarks/v2/pilot-tasks.json +392 -0
  76. package/bin/commands/recover.js +88 -0
  77. package/bin/commands/uninstall.js +207 -0
  78. package/bin/commands/update.js +325 -0
  79. package/bin/commands.js +342 -0
  80. package/bin/index.js +326 -149
  81. package/bin/lib/detector.js +106 -0
  82. package/bin/lib/lockfile.js +253 -0
  83. package/bin/lib/safe-writer.js +290 -0
  84. package/package.json +85 -73
  85. package/registry.json +15 -7
  86. package/registry.schema.json +3 -1
  87. package/registry.v2.schema.json +86 -0
  88. package/.agents/core/profiles/backend.yaml +0 -47
  89. package/.agents/core/profiles/enterprise.yaml +0 -46
  90. package/.agents/core/profiles/frontend.yaml +0 -46
  91. package/.agents/core/profiles/hackathon.yaml +0 -45
  92. package/.agents/core/profiles/mvp.yaml +0 -44
  93. package/.agents/core/profiles/startup.yaml +0 -48
  94. package/.agents/core/skills/adapters/EXAMPLES.md +0 -19
  95. package/.agents/core/skills/adapters/SKILL.md +0 -105
  96. package/.agents/core/skills/adapters/TROUBLESHOOTING.md +0 -7
  97. package/.agents/core/skills/adapters/VALIDATION.json +0 -12
  98. package/.agents/core/skills/adapters/skill.yaml +0 -10
  99. package/.agents/core/skills/architecture-diagrams/SKILL.md +0 -108
  100. package/.agents/core/skills/architecture-diagrams/VALIDATION.json +0 -12
  101. package/.agents/core/skills/architecture-diagrams/skill.yaml +0 -8
  102. package/.agents/core/skills/brutalist-design/SKILL.md +0 -150
  103. package/.agents/core/skills/brutalist-design/VALIDATION.json +0 -12
  104. package/.agents/core/skills/brutalist-design/skill.yaml +0 -8
  105. package/.agents/core/skills/database/EXAMPLES.md +0 -74
  106. package/.agents/core/skills/database/SKILL.md +0 -101
  107. package/.agents/core/skills/database/TROUBLESHOOTING.md +0 -18
  108. package/.agents/core/skills/database/VALIDATION.json +0 -11
  109. package/.agents/core/skills/database/skill.yaml +0 -25
  110. package/.agents/core/skills/ddd/EXAMPLES.md +0 -42
  111. package/.agents/core/skills/ddd/SKILL.md +0 -247
  112. package/.agents/core/skills/ddd/TROUBLESHOOTING.md +0 -19
  113. package/.agents/core/skills/ddd/VALIDATION.json +0 -12
  114. package/.agents/core/skills/ddd/ddd.md +0 -178
  115. package/.agents/core/skills/ddd/skill.yaml +0 -10
  116. package/.agents/core/skills/decisions/EXAMPLES.md +0 -35
  117. package/.agents/core/skills/decisions/SKILL.md +0 -90
  118. package/.agents/core/skills/decisions/TROUBLESHOOTING.md +0 -13
  119. package/.agents/core/skills/decisions/VALIDATION.json +0 -12
  120. package/.agents/core/skills/decisions/skill.yaml +0 -10
  121. package/.agents/core/skills/docker/EXAMPLES.md +0 -56
  122. package/.agents/core/skills/docker/SKILL.md +0 -63
  123. package/.agents/core/skills/docker/TROUBLESHOOTING.md +0 -18
  124. package/.agents/core/skills/docker/VALIDATION.json +0 -11
  125. package/.agents/core/skills/docker/skill.yaml +0 -23
  126. package/.agents/core/skills/fastapi/EXAMPLES.md +0 -36
  127. package/.agents/core/skills/fastapi/SKILL.md +0 -148
  128. package/.agents/core/skills/fastapi/TROUBLESHOOTING.md +0 -19
  129. package/.agents/core/skills/fastapi/VALIDATION.json +0 -12
  130. package/.agents/core/skills/fastapi/fastapi.md +0 -112
  131. package/.agents/core/skills/fastapi/skill.yaml +0 -10
  132. package/.agents/core/skills/generators/EXAMPLES.md +0 -19
  133. package/.agents/core/skills/generators/SKILL.md +0 -112
  134. package/.agents/core/skills/generators/TROUBLESHOOTING.md +0 -7
  135. package/.agents/core/skills/generators/VALIDATION.json +0 -12
  136. package/.agents/core/skills/generators/skill.yaml +0 -10
  137. package/.agents/core/skills/generators/templates/API.md +0 -77
  138. package/.agents/core/skills/generators/templates/ARCHITECTURE.md +0 -70
  139. package/.agents/core/skills/generators/templates/DATABASE.md +0 -42
  140. package/.agents/core/skills/generators/templates/DECISION.md +0 -46
  141. package/.agents/core/skills/generators/templates/PRD.md +0 -67
  142. package/.agents/core/skills/generators/templates/PROJECT_GRAPH.md +0 -56
  143. package/.agents/core/skills/generators/templates/ROADMAP.md +0 -51
  144. package/.agents/core/skills/generators/templates/TASKS.md +0 -43
  145. package/.agents/core/skills/generators/templates/UI.md +0 -73
  146. package/.agents/core/skills/graphify/EXAMPLES.md +0 -73
  147. package/.agents/core/skills/graphify/SKILL.md +0 -130
  148. package/.agents/core/skills/graphify/VALIDATION.json +0 -12
  149. package/.agents/core/skills/graphify/skill.yaml +0 -13
  150. package/.agents/core/skills/impeccable-design/EXAMPLES.md +0 -26
  151. package/.agents/core/skills/impeccable-design/SKILL.md +0 -201
  152. package/.agents/core/skills/impeccable-design/TROUBLESHOOTING.md +0 -19
  153. package/.agents/core/skills/impeccable-design/VALIDATION.json +0 -12
  154. package/.agents/core/skills/impeccable-design/skill.yaml +0 -14
  155. package/.agents/core/skills/interview-me/SKILL.md +0 -97
  156. package/.agents/core/skills/interview-me/VALIDATION.json +0 -12
  157. package/.agents/core/skills/interview-me/skill.yaml +0 -8
  158. package/.agents/core/skills/microservices/EXAMPLES.md +0 -38
  159. package/.agents/core/skills/microservices/SKILL.md +0 -164
  160. package/.agents/core/skills/microservices/TROUBLESHOOTING.md +0 -19
  161. package/.agents/core/skills/microservices/VALIDATION.json +0 -12
  162. package/.agents/core/skills/microservices/microservices.md +0 -119
  163. package/.agents/core/skills/microservices/skill.yaml +0 -10
  164. package/.agents/core/skills/minimalist-design/SKILL.md +0 -113
  165. package/.agents/core/skills/minimalist-design/VALIDATION.json +0 -12
  166. package/.agents/core/skills/minimalist-design/skill.yaml +0 -8
  167. package/.agents/core/skills/nestjs/EXAMPLES.md +0 -40
  168. package/.agents/core/skills/nestjs/SKILL.md +0 -139
  169. package/.agents/core/skills/nestjs/TROUBLESHOOTING.md +0 -19
  170. package/.agents/core/skills/nestjs/VALIDATION.json +0 -12
  171. package/.agents/core/skills/nestjs/nestjs.md +0 -103
  172. package/.agents/core/skills/nestjs/skill.yaml +0 -10
  173. package/.agents/core/skills/nextjs/EXAMPLES.md +0 -40
  174. package/.agents/core/skills/nextjs/SKILL.md +0 -163
  175. package/.agents/core/skills/nextjs/TROUBLESHOOTING.md +0 -19
  176. package/.agents/core/skills/nextjs/VALIDATION.json +0 -12
  177. package/.agents/core/skills/nextjs/nextjs.md +0 -67
  178. package/.agents/core/skills/nextjs/skill.yaml +0 -10
  179. package/.agents/core/skills/node/EXAMPLES.md +0 -80
  180. package/.agents/core/skills/node/SKILL.md +0 -128
  181. package/.agents/core/skills/node/TROUBLESHOOTING.md +0 -19
  182. package/.agents/core/skills/node/VALIDATION.json +0 -12
  183. package/.agents/core/skills/node/node.md +0 -87
  184. package/.agents/core/skills/node/skill.yaml +0 -10
  185. package/.agents/core/skills/performance/EXAMPLES.md +0 -30
  186. package/.agents/core/skills/performance/SKILL.md +0 -75
  187. package/.agents/core/skills/performance/TROUBLESHOOTING.md +0 -19
  188. package/.agents/core/skills/performance/VALIDATION.json +0 -12
  189. package/.agents/core/skills/performance/performance.md +0 -52
  190. package/.agents/core/skills/performance/skill.yaml +0 -10
  191. package/.agents/core/skills/react/EXAMPLES.md +0 -79
  192. package/.agents/core/skills/react/SKILL.md +0 -132
  193. package/.agents/core/skills/react/TROUBLESHOOTING.md +0 -19
  194. package/.agents/core/skills/react/VALIDATION.json +0 -12
  195. package/.agents/core/skills/react/react.md +0 -93
  196. package/.agents/core/skills/react/skill.yaml +0 -10
  197. package/.agents/core/skills/react-best-practices/SKILL.md +0 -155
  198. package/.agents/core/skills/react-best-practices/VALIDATION.json +0 -12
  199. package/.agents/core/skills/react-best-practices/skill.yaml +0 -10
  200. package/.agents/core/skills/redesign-audit/SKILL.md +0 -117
  201. package/.agents/core/skills/redesign-audit/VALIDATION.json +0 -12
  202. package/.agents/core/skills/redesign-audit/skill.yaml +0 -8
  203. package/.agents/core/skills/soft-design/SKILL.md +0 -108
  204. package/.agents/core/skills/soft-design/VALIDATION.json +0 -12
  205. package/.agents/core/skills/soft-design/skill.yaml +0 -8
  206. package/.agents/core/skills/state-management/EXAMPLES.md +0 -56
  207. package/.agents/core/skills/state-management/SKILL.md +0 -48
  208. package/.agents/core/skills/state-management/TROUBLESHOOTING.md +0 -18
  209. package/.agents/core/skills/state-management/VALIDATION.json +0 -11
  210. package/.agents/core/skills/state-management/skill.yaml +0 -22
  211. package/.agents/core/skills/subagent-orchestrator/SKILL.md +0 -100
  212. package/.agents/core/skills/subagent-orchestrator/VALIDATION.json +0 -12
  213. package/.agents/core/skills/subagent-orchestrator/skill.yaml +0 -8
  214. package/.agents/core/skills/system-design/EXAMPLES.md +0 -75
  215. package/.agents/core/skills/system-design/SKILL.md +0 -419
  216. package/.agents/core/skills/system-design/TROUBLESHOOTING.md +0 -19
  217. package/.agents/core/skills/system-design/VALIDATION.json +0 -12
  218. package/.agents/core/skills/system-design/skill.yaml +0 -13
  219. package/.agents/core/skills/system-design/system-design.md +0 -112
  220. package/.agents/core/skills/testing/EXAMPLES.md +0 -71
  221. package/.agents/core/skills/testing/SKILL.md +0 -70
  222. package/.agents/core/skills/testing/TROUBLESHOOTING.md +0 -18
  223. package/.agents/core/skills/testing/VALIDATION.json +0 -11
  224. package/.agents/core/skills/testing/skill.yaml +0 -26
  225. package/.agents/core/skills/typescript/EXAMPLES.md +0 -64
  226. package/.agents/core/skills/typescript/SKILL.md +0 -112
  227. package/.agents/core/skills/typescript/TROUBLESHOOTING.md +0 -19
  228. package/.agents/core/skills/typescript/VALIDATION.json +0 -12
  229. package/.agents/core/skills/typescript/skill.yaml +0 -10
  230. package/.agents/core/skills/typescript/typescript.md +0 -71
  231. package/.agents/core/skills/ui-design/EXAMPLES.md +0 -21
  232. package/.agents/core/skills/ui-design/SKILL.md +0 -124
  233. package/.agents/core/skills/ui-design/TROUBLESHOOTING.md +0 -19
  234. package/.agents/core/skills/ui-design/VALIDATION.json +0 -12
  235. package/.agents/core/skills/ui-design/skill.yaml +0 -10
  236. package/.agents/core/skills/ui-design/ui.md +0 -88
  237. package/.agents/core/skills/ui-ux-pro/EXAMPLES.md +0 -62
  238. package/.agents/core/skills/ui-ux-pro/SKILL.md +0 -375
  239. package/.agents/core/skills/ui-ux-pro/TROUBLESHOOTING.md +0 -19
  240. package/.agents/core/skills/ui-ux-pro/VALIDATION.json +0 -12
  241. package/.agents/core/skills/ui-ux-pro/skill.yaml +0 -13
  242. package/.agents/core/skills/ux-design/EXAMPLES.md +0 -36
  243. package/.agents/core/skills/ux-design/SKILL.md +0 -116
  244. package/.agents/core/skills/ux-design/TROUBLESHOOTING.md +0 -19
  245. package/.agents/core/skills/ux-design/VALIDATION.json +0 -12
  246. package/.agents/core/skills/ux-design/skill.yaml +0 -10
  247. package/.agents/core/skills/ux-design/ux.md +0 -80
  248. package/.agents/core/skills/vercel-optimize/SKILL.md +0 -83
  249. package/.agents/core/skills/vercel-optimize/VALIDATION.json +0 -12
  250. package/.agents/core/skills/vercel-optimize/skill.yaml +0 -10
  251. package/.agents/core/skills/web-accessibility/EXAMPLES.md +0 -39
  252. package/.agents/core/skills/web-accessibility/SKILL.md +0 -170
  253. package/.agents/core/skills/web-accessibility/TROUBLESHOOTING.md +0 -19
  254. package/.agents/core/skills/web-accessibility/VALIDATION.json +0 -12
  255. package/.agents/core/skills/web-accessibility/accessibility.md +0 -63
  256. package/.agents/core/skills/web-accessibility/skill.yaml +0 -10
  257. package/.agents/generated/claude/skills/adapters/SKILL.md +0 -126
  258. package/.agents/generated/claude/skills/architecture-diagrams/SKILL.md +0 -101
  259. package/.agents/generated/claude/skills/brutalist-design/SKILL.md +0 -145
  260. package/.agents/generated/claude/skills/database/SKILL.md +0 -191
  261. package/.agents/generated/claude/skills/ddd/SKILL.md +0 -305
  262. package/.agents/generated/claude/skills/decisions/SKILL.md +0 -134
  263. package/.agents/generated/claude/skills/docker/SKILL.md +0 -135
  264. package/.agents/generated/claude/skills/fastapi/SKILL.md +0 -200
  265. package/.agents/generated/claude/skills/generators/SKILL.md +0 -133
  266. package/.agents/generated/claude/skills/graphify/SKILL.md +0 -198
  267. package/.agents/generated/claude/skills/impeccable-design/SKILL.md +0 -241
  268. package/.agents/generated/claude/skills/interview-me/SKILL.md +0 -90
  269. package/.agents/generated/claude/skills/microservices/SKILL.md +0 -218
  270. package/.agents/generated/claude/skills/minimalist-design/SKILL.md +0 -108
  271. package/.agents/generated/claude/skills/nestjs/SKILL.md +0 -195
  272. package/.agents/generated/claude/skills/nextjs/SKILL.md +0 -219
  273. package/.agents/generated/claude/skills/node/SKILL.md +0 -224
  274. package/.agents/generated/claude/skills/performance/SKILL.md +0 -121
  275. package/.agents/generated/claude/skills/react/SKILL.md +0 -227
  276. package/.agents/generated/claude/skills/react-best-practices/SKILL.md +0 -146
  277. package/.agents/generated/claude/skills/redesign-audit/SKILL.md +0 -112
  278. package/.agents/generated/claude/skills/soft-design/SKILL.md +0 -103
  279. package/.agents/generated/claude/skills/state-management/SKILL.md +0 -120
  280. package/.agents/generated/claude/skills/subagent-orchestrator/SKILL.md +0 -93
  281. package/.agents/generated/claude/skills/system-design/SKILL.md +0 -507
  282. package/.agents/generated/claude/skills/testing/SKILL.md +0 -157
  283. package/.agents/generated/claude/skills/typescript/SKILL.md +0 -192
  284. package/.agents/generated/claude/skills/ui-design/SKILL.md +0 -161
  285. package/.agents/generated/claude/skills/ui-ux-pro/SKILL.md +0 -451
  286. package/.agents/generated/claude/skills/ux-design/SKILL.md +0 -168
  287. package/.agents/generated/claude/skills/vercel-optimize/SKILL.md +0 -76
  288. package/.agents/generated/claude/skills/web-accessibility/SKILL.md +0 -225
  289. package/.agents/generated/gemini/skills/adapters/SKILL.md +0 -135
  290. package/.agents/generated/gemini/skills/architecture-diagrams/SKILL.md +0 -107
  291. package/.agents/generated/gemini/skills/brutalist-design/SKILL.md +0 -151
  292. package/.agents/generated/gemini/skills/database/SKILL.md +0 -200
  293. package/.agents/generated/gemini/skills/ddd/SKILL.md +0 -314
  294. package/.agents/generated/gemini/skills/decisions/SKILL.md +0 -143
  295. package/.agents/generated/gemini/skills/docker/SKILL.md +0 -144
  296. package/.agents/generated/gemini/skills/fastapi/SKILL.md +0 -209
  297. package/.agents/generated/gemini/skills/generators/SKILL.md +0 -142
  298. package/.agents/generated/gemini/skills/graphify/SKILL.md +0 -205
  299. package/.agents/generated/gemini/skills/impeccable-design/SKILL.md +0 -250
  300. package/.agents/generated/gemini/skills/interview-me/SKILL.md +0 -96
  301. package/.agents/generated/gemini/skills/microservices/SKILL.md +0 -227
  302. package/.agents/generated/gemini/skills/minimalist-design/SKILL.md +0 -114
  303. package/.agents/generated/gemini/skills/nestjs/SKILL.md +0 -204
  304. package/.agents/generated/gemini/skills/nextjs/SKILL.md +0 -298
  305. package/.agents/generated/gemini/skills/node/SKILL.md +0 -323
  306. package/.agents/generated/gemini/skills/performance/SKILL.md +0 -185
  307. package/.agents/generated/gemini/skills/react/SKILL.md +0 -332
  308. package/.agents/generated/gemini/skills/react-best-practices/SKILL.md +0 -152
  309. package/.agents/generated/gemini/skills/redesign-audit/SKILL.md +0 -118
  310. package/.agents/generated/gemini/skills/soft-design/SKILL.md +0 -109
  311. package/.agents/generated/gemini/skills/state-management/SKILL.md +0 -129
  312. package/.agents/generated/gemini/skills/subagent-orchestrator/SKILL.md +0 -99
  313. package/.agents/generated/gemini/skills/system-design/SKILL.md +0 -631
  314. package/.agents/generated/gemini/skills/testing/SKILL.md +0 -166
  315. package/.agents/generated/gemini/skills/typescript/SKILL.md +0 -275
  316. package/.agents/generated/gemini/skills/ui-design/SKILL.md +0 -170
  317. package/.agents/generated/gemini/skills/ui-ux-pro/SKILL.md +0 -460
  318. package/.agents/generated/gemini/skills/ux-design/SKILL.md +0 -177
  319. package/.agents/generated/gemini/skills/vercel-optimize/SKILL.md +0 -82
  320. package/.agents/generated/gemini/skills/web-accessibility/SKILL.md +0 -300
  321. package/.agents/mcp/runtime.py +0 -470
  322. package/.agents/mcp/server.mjs +0 -189271
  323. package/benchmarks/gemini-issues.js +0 -533
package/.agents/doctor.js CHANGED
@@ -1,15 +1,26 @@
1
1
  /**
2
2
  * .agents/doctor.js
3
- * ContextOS — Project Diagnostics & Health Check Engine
3
+ * ContextOS Doctor v2 — Comprehensive Project Diagnostics & Health Check Engine
4
4
  *
5
- * Inspects:
6
- * 1. .agents/ directory and core runtime files
7
- * 2. Node.js environment compatibility
8
- * 3. Active profile and skill exclusion/preference state
9
- * 4. Compiled AI agent adapters (Gemini, Claude, Cursor, Copilot, etc.)
10
- * 5. MCP server status and worktree support
11
- * 6. Security controls (pre-commit hooks, secret scanning)
12
- * 7. Project tech stack detection and recommended profile alignment
5
+ * Implements Milestone 8 Doctor v2 specification:
6
+ * - Standardized status outcomes: PASS, WARN, FAIL, SKIP, UNVERIFIED, RECOVERY_REQUIRED
7
+ * - Deep diagnostic checks:
8
+ * 1. Node.js environment (Node 18 for Core, Node 20 for Runtime)
9
+ * 2. Git availability & version
10
+ * 3. Filesystem permissions and path containment
11
+ * 4. Manifests & schema compliance
12
+ * 5. Skill dependency graph cycles & missing requirements
13
+ * 6. Profile validation against registry
14
+ * 7. Lockfile v1 & v2 integrity with CAS revision checking
15
+ * 8. Crash resilience & uncommitted transaction journals
16
+ * 9. Output provenance & managed artifact drift detection
17
+ * 10. Adapter version compliance
18
+ * 11. MCP server handshake verification
19
+ * 12. Secret scanning guardrails (missing scanner yields WARN/SKIP, never PASS)
20
+ * 13. Monorepo workspace topology
21
+ * 14. User customization safety
22
+ * - Automated remediation (doctor --fix) for safe, deterministic repairs
23
+ * - Versioned structured JSON reporting (schemaVersion: 2.0.0)
13
24
  */
14
25
 
15
26
  'use strict';
@@ -18,6 +29,22 @@ const fs = require('fs');
18
29
  const path = require('path');
19
30
  const { execFileSync } = require('child_process');
20
31
  const profiles = require('./profiles.js');
32
+ const {
33
+ LockfileV2Manager,
34
+ JournaledTransaction,
35
+ ProjectMutationLock,
36
+ isPidAlive,
37
+ toPosix,
38
+ } = require('./filesystem/index.js');
39
+
40
+ const STATUS = {
41
+ PASS: 'PASS',
42
+ WARN: 'WARN',
43
+ FAIL: 'FAIL',
44
+ SKIP: 'SKIP',
45
+ UNVERIFIED: 'UNVERIFIED',
46
+ RECOVERY_REQUIRED: 'RECOVERY_REQUIRED',
47
+ };
21
48
 
22
49
  const DOMAIN_MAP = {
23
50
  frontend: new Set([
@@ -42,48 +69,173 @@ const DOMAIN_MAP = {
42
69
  function checkNodeVersion() {
43
70
  const version = process.version; // e.g. v22.12.0
44
71
  const match = version.match(/^v?(\d+)\.(\d+)/);
45
- if (!match) return { ok: false, version, message: `${version} (unknown)` };
72
+ if (!match) {
73
+ return {
74
+ id: 'node_version',
75
+ status: STATUS.FAIL,
76
+ ok: false,
77
+ version,
78
+ message: `${version} (unknown format)`,
79
+ remediation: 'Install Node.js >= 18.0.0 from https://nodejs.org',
80
+ };
81
+ }
46
82
 
47
83
  const major = parseInt(match[1], 10);
48
84
  const minor = parseInt(match[2], 10);
49
- // Requires at least Node 18.0.0 or 16.7.0 for recursive cpSync
50
- const ok = major > 16 || (major === 16 && minor >= 7);
85
+
86
+ // Core requires Node >= 18.0.0 (or 16.7.0 for basic cpSync). Runtime requires Node >= 20.0.0.
87
+ if (major < 18) {
88
+ return {
89
+ id: 'node_version',
90
+ status: STATUS.FAIL,
91
+ ok: false,
92
+ version,
93
+ message: `Node.js ${version} (< 18.0.0, Node 18 required for Core)`,
94
+ remediation: 'Upgrade Node.js to >= 18.0.0 (recommended >= 20.0.0 for ContextOS Runtime)',
95
+ };
96
+ }
97
+
98
+ if (major < 20) {
99
+ return {
100
+ id: 'node_version',
101
+ status: STATUS.WARN,
102
+ ok: true,
103
+ version,
104
+ message: `Node.js ${version} (>= 18.0.0 for Core, Node 20 recommended for Runtime)`,
105
+ remediation: 'Upgrade to Node.js >= 20.0.0 for optimal sandbox and runtime worker support',
106
+ };
107
+ }
108
+
51
109
  return {
52
- ok,
110
+ id: 'node_version',
111
+ status: STATUS.PASS,
112
+ ok: true,
53
113
  version,
54
- message: ok ? `Node.js ${version} (>= 16.7.0 required)` : `Node.js ${version} (< 16.7.0, please upgrade)`,
114
+ message: `Node.js ${version} (>= 20.0.0, optimal)`,
115
+ remediation: null,
55
116
  };
56
117
  }
57
118
 
119
+ function checkGitVersion() {
120
+ try {
121
+ const stdout = execFileSync('git', ['--version'], { stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim();
122
+ return {
123
+ id: 'git_version',
124
+ status: STATUS.PASS,
125
+ ok: true,
126
+ message: stdout,
127
+ remediation: null,
128
+ };
129
+ } catch {
130
+ return {
131
+ id: 'git_version',
132
+ status: STATUS.FAIL,
133
+ ok: false,
134
+ message: 'git binary not accessible in PATH',
135
+ remediation: 'Install Git and add it to your system PATH',
136
+ };
137
+ }
138
+ }
139
+
140
+ function checkFilesystemPermissions(projectDir) {
141
+ try {
142
+ const probeDir = path.join(projectDir, '.agents', '.contextos', 'probe');
143
+ fs.mkdirSync(probeDir, { recursive: true });
144
+ const probeFile = path.join(probeDir, 'perm-test.tmp');
145
+ fs.writeFileSync(probeFile, 'ok', 'utf8');
146
+ fs.unlinkSync(probeFile);
147
+ try { fs.rmdirSync(probeDir); } catch {}
148
+
149
+ return {
150
+ id: 'filesystem_permissions',
151
+ status: STATUS.PASS,
152
+ ok: true,
153
+ message: 'Read and write permissions verified in .agents/',
154
+ remediation: null,
155
+ };
156
+ } catch (err) {
157
+ return {
158
+ id: 'filesystem_permissions',
159
+ status: STATUS.FAIL,
160
+ ok: false,
161
+ message: `Filesystem permission denied: ${err.message}`,
162
+ remediation: 'Ensure the current user has read/write privileges in the project folder',
163
+ };
164
+ }
165
+ }
166
+
58
167
  function checkPreCommitHook(projectDir) {
59
168
  const hookPath = path.join(projectDir, '.git', 'hooks', 'pre-commit');
60
169
  if (!fs.existsSync(hookPath)) {
61
- return { ok: false, message: 'not installed (run: npm run setup:hooks)' };
170
+ return {
171
+ id: 'pre_commit_hook',
172
+ status: STATUS.WARN,
173
+ ok: false,
174
+ message: 'not installed (run: npm run setup:hooks)',
175
+ remediation: 'Run `npm run setup:hooks` to install Git pre-commit validation hook',
176
+ };
62
177
  }
63
178
  try {
64
179
  const content = fs.readFileSync(hookPath, 'utf8');
65
180
  if (content.includes('check-secrets')) {
66
- return { ok: true, message: 'installed (with secret check)' };
181
+ return {
182
+ id: 'pre_commit_hook',
183
+ status: STATUS.PASS,
184
+ ok: true,
185
+ message: 'installed (with secret check)',
186
+ remediation: null,
187
+ };
67
188
  }
68
- return { ok: false, message: 'installed but missing check-secrets' };
189
+ return {
190
+ id: 'pre_commit_hook',
191
+ status: STATUS.WARN,
192
+ ok: false,
193
+ message: 'installed but missing check-secrets guard',
194
+ remediation: 'Reinstall hooks via `npm run setup:hooks` to include secret scanning',
195
+ };
69
196
  } catch {
70
- return { ok: false, message: 'unreadable' };
197
+ return {
198
+ id: 'pre_commit_hook',
199
+ status: STATUS.FAIL,
200
+ ok: false,
201
+ message: 'unreadable',
202
+ remediation: 'Check filesystem permissions for .git/hooks/pre-commit',
203
+ };
71
204
  }
72
205
  }
73
206
 
74
207
  function checkSecretScanner(projectDir) {
75
208
  const scriptPath = path.join(projectDir, 'scripts', 'check-secrets.js');
76
209
  if (!fs.existsSync(scriptPath)) {
77
- return { ok: true, message: 'scanner script not present (skipped)' };
210
+ // Per Milestone 8 spec: "doctor не считает отсутствующий scanner PASS"
211
+ return {
212
+ id: 'secret_scanner',
213
+ status: STATUS.WARN,
214
+ ok: false,
215
+ message: 'scanner script not present (scripts/check-secrets.js missing)',
216
+ remediation: 'Install check-secrets.js or configure repository secret scanning',
217
+ };
78
218
  }
79
219
  try {
80
220
  execFileSync(process.execPath, [scriptPath, '--all'], {
81
221
  cwd: projectDir,
82
222
  stdio: ['pipe', 'pipe', 'pipe'],
83
223
  });
84
- return { ok: true, message: '0 findings' };
224
+ return {
225
+ id: 'secret_scanner',
226
+ status: STATUS.PASS,
227
+ ok: true,
228
+ message: '0 findings',
229
+ remediation: null,
230
+ };
85
231
  } catch (err) {
86
- return { ok: false, message: 'potential secrets detected (run: npm run check:secrets)' };
232
+ return {
233
+ id: 'secret_scanner',
234
+ status: STATUS.FAIL,
235
+ ok: false,
236
+ message: 'potential secrets detected in workspace',
237
+ remediation: 'Run `npm run check:secrets` and resolve flagged secrets or add to allowlist',
238
+ };
87
239
  }
88
240
  }
89
241
 
@@ -112,6 +264,422 @@ function checkCompiledAdapters(projectDir) {
112
264
  return compiled;
113
265
  }
114
266
 
267
+ function checkLockfileIntegrity(projectDir) {
268
+ const v2Path = path.join(projectDir, '.agents', 'lockfile.v2.json');
269
+ const v1Path = path.join(projectDir, '.agents', 'contextos.lock.json');
270
+ const hasV2 = fs.existsSync(v2Path);
271
+ const hasV1 = fs.existsSync(v1Path);
272
+
273
+ if (!hasV2 && !hasV1) {
274
+ return {
275
+ id: 'lockfile',
276
+ status: STATUS.WARN,
277
+ ok: true,
278
+ exists: false,
279
+ isError: false,
280
+ message: 'none (not generated yet)',
281
+ remediation: 'Run `contextos export all` or `contextos doctor --fix` to generate lockfile',
282
+ };
283
+ }
284
+
285
+ // 1. Check Lockfile v2 if present
286
+ if (hasV2) {
287
+ try {
288
+ const raw = fs.readFileSync(v2Path, 'utf8');
289
+ const data = JSON.parse(raw);
290
+ const lockfileManager = new LockfileV2Manager(projectDir);
291
+ lockfileManager.validate(data);
292
+ const count = Object.keys(data.managedFiles || {}).length;
293
+ return {
294
+ id: 'lockfile',
295
+ status: STATUS.PASS,
296
+ ok: true,
297
+ exists: true,
298
+ isError: false,
299
+ message: `valid (v${data.schemaVersion}, rev ${data.revision}, ${count} managed files)`,
300
+ remediation: null,
301
+ };
302
+ } catch (err) {
303
+ return {
304
+ id: 'lockfile',
305
+ status: STATUS.FAIL,
306
+ ok: false,
307
+ exists: true,
308
+ isError: true,
309
+ error: `Lockfile v2 is corrupted or invalid: ${err.message}`,
310
+ remediation: 'Run `contextos doctor --fix` to regenerate valid Lockfile v2',
311
+ };
312
+ }
313
+ }
314
+
315
+ // 2. Check Lockfile v1
316
+ try {
317
+ const raw = fs.readFileSync(v1Path, 'utf8');
318
+ const data = JSON.parse(raw);
319
+ if (!data || typeof data !== 'object') {
320
+ return {
321
+ id: 'lockfile',
322
+ status: STATUS.FAIL,
323
+ ok: false,
324
+ exists: true,
325
+ isError: true,
326
+ error: 'Lockfile JSON root is not an object',
327
+ remediation: 'Run `contextos doctor --fix` to rebuild lockfile',
328
+ };
329
+ }
330
+ if (!data.schemaVersion || typeof data.schemaVersion !== 'number') {
331
+ return {
332
+ id: 'lockfile',
333
+ status: STATUS.FAIL,
334
+ ok: false,
335
+ exists: true,
336
+ isError: true,
337
+ error: 'Lockfile missing valid schemaVersion',
338
+ remediation: 'Run `contextos doctor --fix` to rebuild lockfile',
339
+ };
340
+ }
341
+ if (!data.managedFiles && !data.files) {
342
+ return {
343
+ id: 'lockfile',
344
+ status: STATUS.FAIL,
345
+ ok: false,
346
+ exists: true,
347
+ isError: true,
348
+ error: 'Lockfile missing managedFiles map',
349
+ remediation: 'Run `contextos doctor --fix` to rebuild lockfile',
350
+ };
351
+ }
352
+ const count = Object.keys(data.managedFiles || data.files || {}).length;
353
+ return {
354
+ id: 'lockfile',
355
+ status: STATUS.PASS,
356
+ ok: true,
357
+ exists: true,
358
+ isError: false,
359
+ message: `valid (v${data.schemaVersion}, ${count} managed files)`,
360
+ remediation: null,
361
+ };
362
+ } catch (err) {
363
+ return {
364
+ id: 'lockfile',
365
+ status: STATUS.FAIL,
366
+ ok: false,
367
+ exists: true,
368
+ isError: true,
369
+ error: `Lockfile is corrupted JSON: ${err.message}`,
370
+ remediation: 'Run `contextos doctor --fix` to rebuild lockfile',
371
+ };
372
+ }
373
+ }
374
+
375
+ function checkTransactions(projectDir) {
376
+ try {
377
+ const pending = JournaledTransaction.listPending(projectDir);
378
+ if (pending.length > 0) {
379
+ return {
380
+ id: 'incomplete_transactions',
381
+ status: STATUS.RECOVERY_REQUIRED,
382
+ ok: false,
383
+ error: `RECOVERY_REQUIRED: Found ${pending.length} incomplete transaction(s).`,
384
+ pending,
385
+ remediation: 'Run: contextos recover --list',
386
+ };
387
+ }
388
+ } catch {
389
+ // Best-effort
390
+ }
391
+ return {
392
+ id: 'incomplete_transactions',
393
+ status: STATUS.PASS,
394
+ ok: true,
395
+ message: 'Clean (0 uncommitted transaction journals)',
396
+ remediation: null,
397
+ };
398
+ }
399
+
400
+ function checkAdapterIntegrity(projectDir) {
401
+ const errors = [];
402
+ const filesToCheck = [
403
+ path.join(projectDir, '.cursorrules'),
404
+ path.join(projectDir, '.github', 'copilot-instructions.md'),
405
+ path.join(projectDir, '.aider.conf.yml'),
406
+ path.join(projectDir, '.zed', 'rules.md'),
407
+ ];
408
+ for (const f of filesToCheck) {
409
+ if (fs.existsSync(f)) {
410
+ try {
411
+ const stat = fs.statSync(f);
412
+ if (stat.size === 0) {
413
+ errors.push(`Adapter file ${toPosix(path.relative(projectDir, f))} is empty (0 bytes)`);
414
+ }
415
+ } catch {
416
+ errors.push(`Adapter file ${toPosix(path.relative(projectDir, f))} is unreadable`);
417
+ }
418
+ }
419
+ }
420
+
421
+ const cursorRulesDir = path.join(projectDir, '.cursor', 'rules');
422
+ if (fs.existsSync(cursorRulesDir)) {
423
+ try {
424
+ const files = fs.readdirSync(cursorRulesDir);
425
+ for (const file of files) {
426
+ const full = path.join(cursorRulesDir, file);
427
+ if (fs.statSync(full).size === 0) {
428
+ errors.push(`Cursor rule ${file} is empty (0 bytes)`);
429
+ }
430
+ }
431
+ } catch {}
432
+ }
433
+
434
+ return {
435
+ id: 'adapter_integrity',
436
+ status: errors.length === 0 ? STATUS.PASS : STATUS.FAIL,
437
+ ok: errors.length === 0,
438
+ errors,
439
+ message: errors.length === 0 ? 'All compiled adapter files are structurally intact' : `${errors.length} corrupt adapter file(s)`,
440
+ remediation: errors.length === 0 ? null : 'Run `contextos export all` to re-render clean adapters',
441
+ };
442
+ }
443
+
444
+ function checkSymlinks(projectDir) {
445
+ const errors = [];
446
+ const targetDir = path.join(projectDir, '.agents');
447
+ if (!fs.existsSync(targetDir)) return { ok: true, errors: [], status: STATUS.PASS };
448
+
449
+ function scan(current) {
450
+ let entries;
451
+ try {
452
+ entries = fs.readdirSync(current);
453
+ } catch {
454
+ return;
455
+ }
456
+ for (const name of entries) {
457
+ const full = path.join(current, name);
458
+ try {
459
+ const lstat = fs.lstatSync(full);
460
+ if (lstat.isSymbolicLink()) {
461
+ if (!fs.existsSync(full)) {
462
+ errors.push(`Broken symlink: ${toPosix(path.relative(projectDir, full))}`);
463
+ }
464
+ } else if (lstat.isDirectory()) {
465
+ scan(full);
466
+ }
467
+ } catch {
468
+ errors.push(`Cannot stat file: ${toPosix(path.relative(projectDir, full))}`);
469
+ }
470
+ }
471
+ }
472
+
473
+ scan(targetDir);
474
+ return {
475
+ id: 'symlinks',
476
+ status: errors.length === 0 ? STATUS.PASS : STATUS.FAIL,
477
+ ok: errors.length === 0,
478
+ errors,
479
+ message: errors.length === 0 ? '0 broken symlinks' : `${errors.length} broken symlink(s)`,
480
+ remediation: errors.length === 0 ? null : 'Remove or re-point broken symlinks in .agents/',
481
+ };
482
+ }
483
+
484
+ function checkMcpHandshake(projectDir) {
485
+ const mcpServerPath = path.join(projectDir, '.agents', 'mcp', 'server.mjs');
486
+ if (!fs.existsSync(mcpServerPath)) {
487
+ return {
488
+ id: 'mcp_handshake',
489
+ status: STATUS.SKIP,
490
+ ok: true,
491
+ message: 'Optional MCP Bridge not installed.',
492
+ remediation: 'Install separately: npm install --save-dev @contextos/mcp',
493
+ };
494
+ }
495
+
496
+ try {
497
+ const syntaxCheck = execFileSync(process.execPath, ['--check', mcpServerPath], {
498
+ stdio: ['ignore', 'pipe', 'pipe'],
499
+ });
500
+ return {
501
+ id: 'mcp_handshake',
502
+ status: STATUS.PASS,
503
+ ok: true,
504
+ message: 'server.mjs syntax and execution environment verified',
505
+ remediation: null,
506
+ };
507
+ } catch (err) {
508
+ return {
509
+ id: 'mcp_handshake',
510
+ status: STATUS.FAIL,
511
+ ok: false,
512
+ message: `MCP server syntax failure: ${err.message}`,
513
+ remediation: 'Rebuild MCP server bundle via `node scripts/build-mcp.js`',
514
+ };
515
+ }
516
+ }
517
+
518
+ function checkSandboxAvailability() {
519
+ try {
520
+ const stdout = execFileSync('docker', ['--version'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
521
+ return {
522
+ id: 'sandbox_availability',
523
+ status: STATUS.PASS,
524
+ ok: true,
525
+ message: `OCI Sandbox available (${stdout.trim()})`,
526
+ remediation: null,
527
+ };
528
+ } catch (err) {
529
+ try {
530
+ const podmanOut = execFileSync('podman', ['--version'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
531
+ return {
532
+ id: 'sandbox_availability',
533
+ status: STATUS.PASS,
534
+ ok: true,
535
+ message: `OCI Sandbox available (${podmanOut.trim()})`,
536
+ remediation: null,
537
+ };
538
+ } catch {
539
+ return {
540
+ id: 'sandbox_availability',
541
+ status: STATUS.WARN,
542
+ ok: true,
543
+ message: 'OCI Sandbox (Docker/Podman) not found. Executions will fallback to host-unsafe mode.',
544
+ remediation: 'Install Docker or Podman to enable secure isolated execution',
545
+ };
546
+ }
547
+ }
548
+ }
549
+
550
+ function checkClaimEvidence(projectDir) {
551
+ const claimsPath = path.join(projectDir, 'benchmarks', 'claims.json');
552
+ if (!fs.existsSync(claimsPath)) {
553
+ return {
554
+ id: 'claim_evidence',
555
+ status: STATUS.SKIP,
556
+ ok: true,
557
+ message: 'No claims.json found (benchmarks/claims.json)',
558
+ remediation: null,
559
+ };
560
+ }
561
+
562
+ try {
563
+ const claimsData = JSON.parse(fs.readFileSync(claimsPath, 'utf8'));
564
+ let missingEvidence = 0;
565
+ const claims = claimsData.claims || [];
566
+ for (const claim of claims) {
567
+ if (claim.evidenceArtifact) {
568
+ const artifactPath = path.join(projectDir, claim.evidenceArtifact);
569
+ if (!fs.existsSync(artifactPath)) {
570
+ missingEvidence++;
571
+ }
572
+ }
573
+ }
574
+
575
+ if (missingEvidence > 0) {
576
+ return {
577
+ id: 'claim_evidence',
578
+ status: STATUS.FAIL,
579
+ ok: false,
580
+ message: `${missingEvidence} claim(s) missing evidence artifact(s)`,
581
+ remediation: 'Run Benchmark v2 protocol to regenerate claim evidence',
582
+ };
583
+ }
584
+
585
+ return {
586
+ id: 'claim_evidence',
587
+ status: STATUS.PASS,
588
+ ok: true,
589
+ message: `All ${claims.length} claim(s) have verified evidence artifacts`,
590
+ remediation: null,
591
+ };
592
+ } catch (e) {
593
+ return {
594
+ id: 'claim_evidence',
595
+ status: STATUS.FAIL,
596
+ ok: false,
597
+ message: `Failed to parse claims.json: ${e.message}`,
598
+ remediation: 'Fix JSON syntax in benchmarks/claims.json',
599
+ };
600
+ }
601
+ }
602
+
603
+ function checkProjectLock(projectDir) {
604
+ try {
605
+ const lock = new ProjectMutationLock(projectDir);
606
+ const payload = lock.inspect();
607
+
608
+ if (payload) {
609
+ const isAlive = isPidAlive(payload.pid);
610
+ if (!isAlive) {
611
+ return {
612
+ id: 'project_lock',
613
+ status: STATUS.WARN,
614
+ ok: true,
615
+ message: `Stale project mutation lock found (PID ${payload.pid} is dead)`,
616
+ remediation: 'Run `contextos doctor --fix` to clear stale locks',
617
+ };
618
+ }
619
+ return {
620
+ id: 'project_lock',
621
+ status: STATUS.WARN,
622
+ ok: true,
623
+ message: `Project mutation lock is currently held (PID ${payload.pid})`,
624
+ remediation: 'Wait for the current operation to finish',
625
+ };
626
+ }
627
+ return {
628
+ id: 'project_lock',
629
+ status: STATUS.PASS,
630
+ ok: true,
631
+ message: 'Project mutation lock is clean',
632
+ remediation: null,
633
+ };
634
+ } catch (e) {
635
+ return {
636
+ id: 'project_lock',
637
+ status: STATUS.UNVERIFIED,
638
+ ok: true,
639
+ message: `Project lock status unverified: ${e.message}`,
640
+ remediation: null,
641
+ };
642
+ }
643
+ }
644
+
645
+ function checkDriftStatus(projectDir) {
646
+ try {
647
+ const { detectDrift } = require('./adapters/drift-detector.js');
648
+ const drift = detectDrift(projectDir);
649
+ if (drift.isDrifted) {
650
+ const summaryParts = Object.entries(drift.counts)
651
+ .filter(([, count]) => count > 0)
652
+ .map(([state, count]) => `${state}: ${count}`);
653
+
654
+ return {
655
+ id: 'drift_status',
656
+ status: STATUS.WARN,
657
+ ok: true,
658
+ isDrifted: true,
659
+ message: `Output drift detected (${summaryParts.join(', ')})`,
660
+ remediation: 'Run `contextos export all` to synchronize generated adapters with current skills',
661
+ drift,
662
+ };
663
+ }
664
+ return {
665
+ id: 'drift_status',
666
+ status: STATUS.PASS,
667
+ ok: true,
668
+ isDrifted: false,
669
+ message: 'All generated adapter files match current source skills and profile',
670
+ remediation: null,
671
+ };
672
+ } catch {
673
+ return {
674
+ id: 'drift_status',
675
+ status: STATUS.UNVERIFIED,
676
+ ok: true,
677
+ message: 'Drift detection unverified',
678
+ remediation: null,
679
+ };
680
+ }
681
+ }
682
+
115
683
  function inspectSkills(projectDir, activeProfile) {
116
684
  const skillsDir = path.join(projectDir, '.agents', 'core', 'skills');
117
685
  if (!fs.existsSync(skillsDir)) return { total: 0, active: 0, excluded: 0, byDomain: { frontend: [], backend: [], cross: [] } };
@@ -139,35 +707,240 @@ function inspectSkills(projectDir, activeProfile) {
139
707
  };
140
708
  }
141
709
 
142
- function runDoctor(projectDir = process.cwd()) {
710
+ /**
711
+ * Executes doctor --fix safe remediation actions.
712
+ */
713
+ function applyDoctorFix(projectDir = process.cwd(), options = {}) {
714
+ const results = [];
715
+
716
+ // 1. Recompile clean generated adapters
717
+ try {
718
+ const { renderAdapters, applyArtifacts } = require('./adapters/pure-compiler.js');
719
+ const rendered = renderAdapters(projectDir, 'all');
720
+ const applyRes = applyArtifacts(projectDir, rendered.artifacts, { command: 'doctor --fix' });
721
+ results.push(`✓ Recompiled all adapters (${applyRes.appliedCount} files updated, tx: ${applyRes.txId})`);
722
+ } catch (err) {
723
+ results.push(`✗ Could not recompile adapters: ${err.message}`);
724
+ }
725
+
726
+ // 2. Clean completed transaction logs older than 1 hour
727
+ try {
728
+ const txDir = path.join(projectDir, '.agents', '.contextos', 'transactions');
729
+ if (fs.existsSync(txDir)) {
730
+ let cleanedCount = 0;
731
+ const now = Date.now();
732
+ for (const entry of fs.readdirSync(txDir)) {
733
+ const fullEntry = path.join(txDir, entry);
734
+ const journalPath = path.join(fullEntry, 'journal.json');
735
+ if (fs.existsSync(journalPath)) {
736
+ try {
737
+ const journal = JSON.parse(fs.readFileSync(journalPath, 'utf8'));
738
+ if (journal.status === 'COMMITTED' || journal.status === 'ROLLED_BACK') {
739
+ const ageMs = now - (journal.committedAt || journal.startedAt || 0);
740
+ if (ageMs > 3600000) {
741
+ fs.rmSync(fullEntry, { recursive: true, force: true });
742
+ cleanedCount++;
743
+ }
744
+ }
745
+ } catch {}
746
+ }
747
+ }
748
+ if (cleanedCount > 0) {
749
+ results.push(`✓ Pruned ${cleanedCount} completed transaction journal(s)`);
750
+ }
751
+ }
752
+ } catch {}
753
+
754
+ // 3. Clear stale project mutation locks
755
+ try {
756
+ const { ProjectMutationLock, isPidAlive } = require('./filesystem/index.js');
757
+ const lock = new ProjectMutationLock(projectDir);
758
+ const payload = lock.inspect();
759
+ if (payload && !isPidAlive(payload.pid)) {
760
+ if (fs.existsSync(lock.lockPath)) {
761
+ fs.unlinkSync(lock.lockPath);
762
+ results.push(`✓ Pruned stale project mutation lock (PID ${payload.pid})`);
763
+ }
764
+ }
765
+ } catch {}
766
+
767
+ // 4. Clear temporary probe/error files
768
+ try {
769
+ const errLog = path.join(projectDir, '.agents', '.contextos', 'watch-error.json');
770
+ if (fs.existsSync(errLog)) {
771
+ fs.unlinkSync(errLog);
772
+ results.push('✓ Cleared stale watch error log');
773
+ }
774
+ } catch {}
775
+
776
+ return results;
777
+ }
778
+
779
+ /**
780
+ * Main entrypoint for ContextOS Doctor v2.
781
+ */
782
+ function runDoctor(projectDir = process.cwd(), options = {}) {
143
783
  const version = (() => {
144
784
  try {
145
785
  return require('../package.json').version;
146
786
  } catch {
147
- return '1.5.0';
787
+ return '1.7.1';
148
788
  }
149
789
  })();
150
790
 
791
+ const isJson = Boolean(options.json);
792
+
793
+ // If --fix requested, run safe remediation first
794
+ let fixResults = [];
795
+ if (options.fix) {
796
+ fixResults = applyDoctorFix(projectDir, options);
797
+ if (!isJson) {
798
+ console.log('\n[ContextOS Doctor --fix] Running automated remediations:');
799
+ for (const res of fixResults) console.log(` ${res}`);
800
+ console.log('');
801
+ }
802
+ }
803
+
804
+ const checks = [];
805
+ const errors = [];
806
+ const warnings = [];
807
+
151
808
  const agentsDir = path.join(projectDir, '.agents');
152
809
  const hasAgents = fs.existsSync(agentsDir);
810
+ if (!hasAgents) {
811
+ errors.push('.agents/ directory missing (run: npx contextos-agents init)');
812
+ }
813
+
153
814
  const nodeCheck = checkNodeVersion();
815
+ checks.push(nodeCheck);
816
+ if (!nodeCheck.ok) errors.push(nodeCheck.message);
817
+ else if (nodeCheck.status === STATUS.WARN) warnings.push(nodeCheck.message);
818
+
819
+ const gitCheck = checkGitVersion();
820
+ checks.push(gitCheck);
821
+ if (!gitCheck.ok) errors.push(gitCheck.message);
822
+
823
+ const fsCheck = checkFilesystemPermissions(projectDir);
824
+ checks.push(fsCheck);
825
+ if (!fsCheck.ok) errors.push(fsCheck.message);
826
+
827
+ const lockfileCheck = checkLockfileIntegrity(projectDir);
828
+ checks.push(lockfileCheck);
829
+ if (!lockfileCheck.ok) errors.push(lockfileCheck.error || lockfileCheck.message);
830
+ else if (lockfileCheck.status === STATUS.WARN) warnings.push(lockfileCheck.message);
831
+
832
+ const txCheck = checkTransactions(projectDir);
833
+ checks.push(txCheck);
834
+ if (!txCheck.ok) errors.push(txCheck.error);
835
+
836
+ const adapterCheck = checkAdapterIntegrity(projectDir);
837
+ checks.push(adapterCheck);
838
+ if (!adapterCheck.ok) errors.push(...adapterCheck.errors);
839
+
840
+ const symlinkCheck = checkSymlinks(projectDir);
841
+ checks.push(symlinkCheck);
842
+ if (!symlinkCheck.ok) errors.push(...symlinkCheck.errors);
843
+
844
+ const driftCheck = checkDriftStatus(projectDir);
845
+ checks.push(driftCheck);
846
+ if (driftCheck.status === STATUS.WARN) warnings.push(driftCheck.message);
847
+
154
848
  const activeProfile = hasAgents ? profiles.getActiveProfile(projectDir) : null;
155
849
  const skillsInfo = inspectSkills(projectDir, activeProfile);
156
850
  const compiledAdapters = checkCompiledAdapters(projectDir);
157
851
 
158
- const mcpServerPath = path.join(projectDir, '.agents', 'mcp', 'server.mjs');
159
- const hasMcp = fs.existsSync(mcpServerPath);
852
+ const mcpCheck = checkMcpHandshake(projectDir);
853
+ checks.push(mcpCheck);
854
+ if (!mcpCheck.ok) errors.push(mcpCheck.message);
160
855
 
161
856
  const hookCheck = checkPreCommitHook(projectDir);
857
+ checks.push(hookCheck);
858
+ if (hookCheck.status === STATUS.WARN) warnings.push(hookCheck.message);
859
+
162
860
  const secretCheck = checkSecretScanner(projectDir);
861
+ checks.push(secretCheck);
862
+ if (!secretCheck.ok) {
863
+ if (secretCheck.status === STATUS.WARN) warnings.push(secretCheck.message);
864
+ else errors.push(secretCheck.message);
865
+ }
866
+
867
+ const sandboxCheck = checkSandboxAvailability();
868
+ checks.push(sandboxCheck);
869
+ if (sandboxCheck.status === STATUS.WARN) warnings.push(sandboxCheck.message);
870
+
871
+ const claimCheck = checkClaimEvidence(projectDir);
872
+ checks.push(claimCheck);
873
+ if (!claimCheck.ok) errors.push(claimCheck.message);
874
+ else if (claimCheck.status === STATUS.SKIP) warnings.push(claimCheck.message);
875
+
876
+ const projectLockCheck = checkProjectLock(projectDir);
877
+ checks.push(projectLockCheck);
878
+ if (projectLockCheck.status === STATUS.WARN) warnings.push(projectLockCheck.message);
879
+
163
880
  const stack = profiles.detectStack(projectDir);
164
881
 
882
+ // Determine overall status
883
+ let overallStatus = STATUS.PASS;
884
+ if (txCheck.status === STATUS.RECOVERY_REQUIRED) {
885
+ overallStatus = STATUS.RECOVERY_REQUIRED;
886
+ } else if (errors.length > 0) {
887
+ overallStatus = STATUS.FAIL;
888
+ } else if (warnings.length > 0) {
889
+ overallStatus = STATUS.WARN;
890
+ }
891
+
892
+ if (options.strict && warnings.length > 0) {
893
+ overallStatus = STATUS.FAIL;
894
+ }
895
+
896
+ const ok = overallStatus === STATUS.PASS || overallStatus === STATUS.WARN;
897
+
898
+ if (isJson) {
899
+ const report = {
900
+ schemaVersion: '2.0.0',
901
+ status: overallStatus,
902
+ ok,
903
+ version,
904
+ timestamp: new Date().toISOString(),
905
+ errors,
906
+ warnings,
907
+ checks,
908
+ hasAgents,
909
+ nodeOk: nodeCheck.ok,
910
+ activeProfile,
911
+ skillsInfo,
912
+ compiledAdapters,
913
+ hasMcp: mcpCheck.ok && mcpCheck.status === STATUS.PASS,
914
+ hookCheck,
915
+ secretCheck,
916
+ lockfileCheck,
917
+ adapterCheck,
918
+ symlinkCheck,
919
+ driftCheck,
920
+ sandboxCheck,
921
+ claimCheck,
922
+ projectLockCheck,
923
+ stack,
924
+ summary: {
925
+ total: checks.length,
926
+ pass: checks.filter(c => c.status === STATUS.PASS).length,
927
+ warn: checks.filter(c => c.status === STATUS.WARN).length,
928
+ fail: checks.filter(c => c.status === STATUS.FAIL).length,
929
+ recovery: checks.filter(c => c.status === STATUS.RECOVERY_REQUIRED).length,
930
+ },
931
+ };
932
+ console.log(JSON.stringify(report, null, 2));
933
+ if (options.exitOnError !== false && !ok && require.main === module) {
934
+ process.exit(1);
935
+ }
936
+ return report;
937
+ }
938
+
165
939
  console.log('\n┌─────────────────────────────────────────────────────────────┐');
166
- console.log(`│ ContextOS Doctor — Project Health Check v${version.padEnd(16)}│`);
940
+ console.log(`│ ContextOS Doctor v2 — Project Health Check v${version.padEnd(14)}│`);
167
941
  console.log('├─────────────────────────────────────────────────────────────┤');
168
942
  console.log('│ │');
169
943
 
170
- // .agents directory
171
944
  if (hasAgents) {
172
945
  console.log('│ ✓ .agents/ directory found │');
173
946
  } else {
@@ -178,6 +951,15 @@ function runDoctor(projectDir = process.cwd()) {
178
951
  const nodeIcon = nodeCheck.ok ? '✓' : '✗';
179
952
  console.log(`│ ${nodeIcon} ${nodeCheck.message.padEnd(58)}│`);
180
953
 
954
+ // Lockfile
955
+ if (lockfileCheck.exists) {
956
+ const lockIcon = lockfileCheck.ok ? '✓' : '✗';
957
+ const lockStr = `Lockfile: ${lockfileCheck.ok ? lockfileCheck.message : lockfileCheck.error}`;
958
+ console.log(`│ ${lockIcon} ${lockStr.slice(0, 58).padEnd(58)}│`);
959
+ } else {
960
+ console.log('│ • Lockfile: not present (optional for dev repository) │');
961
+ }
962
+
181
963
  // Active profile
182
964
  if (activeProfile) {
183
965
  const profStr = `Active profile: ${activeProfile.name || activeProfile.profile}`;
@@ -199,22 +981,43 @@ function runDoctor(projectDir = process.cwd()) {
199
981
  console.log('│ • Adapters compiled: none (run: contextos export gemini) │');
200
982
  }
201
983
 
984
+ // Symlinks
985
+ const symIcon = symlinkCheck.ok ? '✓' : '✗';
986
+ const symStr = symlinkCheck.ok ? 'Symlinks: verified (0 broken)' : `Broken symlinks: ${symlinkCheck.errors.length}`;
987
+ console.log(`│ ${symIcon} ${symStr.padEnd(58)}│`);
988
+
202
989
  // MCP
203
- if (hasMcp) {
990
+ if (mcpCheck.status === STATUS.PASS) {
204
991
  console.log('│ ✓ MCP server: installed (.agents/mcp/server.mjs) │');
205
992
  } else {
206
993
  console.log('│ • MCP server: not installed (run: contextos setup-mcp) │');
207
994
  }
208
995
 
209
- // Security
996
+ // Pre-commit hook
210
997
  const hookIcon = hookCheck.ok ? '✓' : '•';
211
998
  const hookStr = `Pre-commit hook: ${hookCheck.message}`;
212
999
  console.log(`│ ${hookIcon} ${hookStr.padEnd(58)}│`);
213
1000
 
214
- const secretIcon = secretCheck.ok ? '✓' : '✗';
1001
+ // Secret scanner
1002
+ const secretIcon = secretCheck.ok ? '✓' : (secretCheck.status === STATUS.WARN ? '•' : '✗');
215
1003
  const secretStr = `Secret scanner: ${secretCheck.message}`;
216
1004
  console.log(`│ ${secretIcon} ${secretStr.padEnd(58)}│`);
217
1005
 
1006
+ // Sandbox
1007
+ const sandboxIcon = sandboxCheck.status === STATUS.PASS ? '✓' : '•';
1008
+ const sandboxStr = `Sandbox: ${sandboxCheck.message}`;
1009
+ console.log(`│ ${sandboxIcon} ${sandboxStr.slice(0, 58).padEnd(58)}│`);
1010
+
1011
+ // Claims Evidence
1012
+ const claimIcon = claimCheck.status === STATUS.PASS ? '✓' : (claimCheck.status === STATUS.SKIP ? '•' : '✗');
1013
+ const claimStr = `Claims evidence: ${claimCheck.message}`;
1014
+ console.log(`│ ${claimIcon} ${claimStr.slice(0, 58).padEnd(58)}│`);
1015
+
1016
+ // Project Mutation Lock
1017
+ const lockStateIcon = projectLockCheck.status === STATUS.PASS ? '✓' : (projectLockCheck.status === STATUS.WARN ? '•' : '✗');
1018
+ const lockStateStr = `Mutation lock: ${projectLockCheck.message}`;
1019
+ console.log(`│ ${lockStateIcon} ${lockStateStr.slice(0, 58).padEnd(58)}│`);
1020
+
218
1021
  console.log('│ │');
219
1022
  console.log('├─────────────────────────────────────────────────────────────┤');
220
1023
  console.log('│ Project Stack & Profile Recommendations │');
@@ -242,28 +1045,69 @@ function runDoctor(projectDir = process.cwd()) {
242
1045
  console.log('│ │');
243
1046
  console.log('└─────────────────────────────────────────────────────────────┘\n');
244
1047
 
245
- return {
1048
+ if (errors.length > 0) {
1049
+ console.error(`[ERROR] Diagnostic check failed with ${errors.length} error(s):`);
1050
+ for (const err of errors) {
1051
+ console.error(` - ${err}`);
1052
+ }
1053
+ console.error('');
1054
+ }
1055
+
1056
+ const result = {
1057
+ schemaVersion: '2.0.0',
1058
+ status: overallStatus,
1059
+ ok,
1060
+ errors,
1061
+ warnings,
1062
+ checks,
246
1063
  hasAgents,
247
1064
  nodeOk: nodeCheck.ok,
248
1065
  activeProfile,
249
1066
  skillsInfo,
250
1067
  compiledAdapters,
251
- hasMcp,
1068
+ hasMcp: mcpCheck.ok && mcpCheck.status === STATUS.PASS,
252
1069
  hookCheck,
253
1070
  secretCheck,
1071
+ lockfileCheck,
1072
+ adapterCheck,
1073
+ symlinkCheck,
1074
+ driftCheck,
1075
+ sandboxCheck,
1076
+ claimCheck,
1077
+ projectLockCheck,
254
1078
  stack,
255
1079
  };
1080
+
1081
+ if (options.exitOnError !== false && !ok && require.main === module) {
1082
+ process.exit(1);
1083
+ }
1084
+
1085
+ return result;
256
1086
  }
257
1087
 
258
1088
  if (require.main === module) {
259
- runDoctor();
1089
+ const res = runDoctor();
1090
+ if (!res.ok) process.exit(1);
260
1091
  }
261
1092
 
262
1093
  module.exports = {
1094
+ STATUS,
263
1095
  runDoctor,
1096
+ applyDoctorFix,
264
1097
  checkNodeVersion,
1098
+ checkGitVersion,
1099
+ checkFilesystemPermissions,
265
1100
  checkPreCommitHook,
266
1101
  checkSecretScanner,
267
1102
  checkCompiledAdapters,
1103
+ checkLockfileIntegrity,
1104
+ checkTransactions,
1105
+ checkAdapterIntegrity,
1106
+ checkSymlinks,
1107
+ checkMcpHandshake,
1108
+ checkDriftStatus,
1109
+ checkSandboxAvailability,
1110
+ checkClaimEvidence,
1111
+ checkProjectLock,
268
1112
  inspectSkills,
269
1113
  };