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,778 @@
1
+ /**
2
+ * .agents/workspace/workspace-graph.js
3
+ * ContextOS — Workspace Evidence Graph Builder
4
+ *
5
+ * Discovers and builds a structured evidence graph of the workspace repository:
6
+ * - Monorepo & multi-package workspace discovery (npm/yarn workspaces, pnpm, Turborepo, Lerna, Nx)
7
+ * - Polyglot ecosystem support (npm, python, rust, go)
8
+ * - Nearest-package resolution with bounded traversal & symlink escape guards
9
+ * - Package dependency extraction, internal cross-package edges, and stack configs
10
+ * - Cryptographic deterministic fingerprinting (SHA-256)
11
+ * - Cross-platform POSIX path normalization
12
+ * - Zero install-time runtime dependencies (pure Node.js)
13
+ */
14
+
15
+ 'use strict';
16
+
17
+ const fs = require('fs');
18
+ const path = require('path');
19
+ const crypto = require('crypto');
20
+
21
+ // Default directories to ignore during traversal
22
+ const DEFAULT_IGNORED_DIRS = new Set([
23
+ '.git',
24
+ 'node_modules',
25
+ '.contextos-worktrees',
26
+ '.agents',
27
+ 'dist',
28
+ 'build',
29
+ '.next',
30
+ 'out',
31
+ 'target',
32
+ 'vendor',
33
+ '__pycache__',
34
+ '.venv',
35
+ 'venv',
36
+ '.turbo',
37
+ '.cache',
38
+ 'coverage',
39
+ '.svn',
40
+ '.hg',
41
+ 'scratch',
42
+ 'temp',
43
+ 'tmp'
44
+ ]);
45
+
46
+ // Known stack config patterns to detect per package
47
+ const KNOWN_CONFIG_FILES = [
48
+ 'tsconfig.json',
49
+ 'jsconfig.json',
50
+ 'next.config.js',
51
+ 'next.config.mjs',
52
+ 'next.config.ts',
53
+ 'vite.config.js',
54
+ 'vite.config.mjs',
55
+ 'vite.config.ts',
56
+ 'tailwind.config.js',
57
+ 'tailwind.config.cjs',
58
+ 'tailwind.config.mjs',
59
+ 'tailwind.config.ts',
60
+ 'Dockerfile',
61
+ 'docker-compose.yml',
62
+ 'docker-compose.yaml',
63
+ 'vitest.config.js',
64
+ 'vitest.config.ts',
65
+ 'vitest.config.mjs',
66
+ 'jest.config.js',
67
+ 'jest.config.ts',
68
+ 'nest-cli.json',
69
+ 'prisma/schema.prisma'
70
+ ];
71
+
72
+ /**
73
+ * Normalizes any path to a POSIX path with forward slashes.
74
+ * @param {string} p
75
+ * @returns {string}
76
+ */
77
+ function toPosix(p) {
78
+ if (!p) return '';
79
+ return p.replace(/\\/g, '/');
80
+ }
81
+
82
+ /**
83
+ * Standardizes directory paths for safe relative comparisons.
84
+ * @param {string} p
85
+ * @returns {string}
86
+ */
87
+ function cleanRelative(p) {
88
+ const posix = toPosix(p).replace(/^\.\//, '');
89
+ if (posix === '' || posix === '.') return '.';
90
+ return posix.replace(/\/+$/, '');
91
+ }
92
+
93
+ /**
94
+ * Simple glob-to-RegExp compiler for workspace patterns (e.g. "packages/*", "apps/**").
95
+ * @param {string} glob
96
+ * @returns {RegExp}
97
+ */
98
+ function globToRegex(glob) {
99
+ const normalized = toPosix(glob).replace(/^\.\//, '').replace(/\/+$/, '');
100
+ let regStr = '';
101
+ for (let i = 0; i < normalized.length; i++) {
102
+ const char = normalized[i];
103
+ if (char === '*' && normalized[i + 1] === '*') {
104
+ regStr += '.*';
105
+ i++; // skip second asterisk
106
+ } else if (char === '*') {
107
+ regStr += '[^/]+';
108
+ } else if (char === '?') {
109
+ regStr += '[^/]';
110
+ } else if (['.', '+', '^', '$', '(', ')', '[', ']', '{', '}', '|', '\\'].includes(char)) {
111
+ regStr += '\\' + char;
112
+ } else {
113
+ regStr += char;
114
+ }
115
+ }
116
+ return new RegExp(`^${regStr}$`);
117
+ }
118
+
119
+ class WorkspaceGraphBuilder {
120
+ constructor(options = {}) {
121
+ this.maxDepth = options.maxDepth || 8;
122
+ this.maxPackages = options.maxPackages || 500;
123
+ this.ignoredDirs = new Set([...DEFAULT_IGNORED_DIRS, ...(options.ignoredDirs || [])]);
124
+ }
125
+
126
+ /**
127
+ * Discovers the real repository root starting from a directory.
128
+ * Traverses upwards looking for markers (.git, pnpm-workspace.yaml, lerna.json, root package.json with workspaces).
129
+ * @param {string} startDir
130
+ * @returns {string} Real absolute path
131
+ */
132
+ findRepositoryRoot(startDir) {
133
+ let current = path.resolve(startDir || process.cwd());
134
+ try {
135
+ current = fs.realpathSync(current);
136
+ } catch {
137
+ // keep current as is
138
+ }
139
+
140
+ const { root } = path.parse(current);
141
+ let candidate = current;
142
+
143
+ while (current && current !== root) {
144
+ // Priority 1: .git folder or file
145
+ if (fs.existsSync(path.join(current, '.git'))) {
146
+ return current;
147
+ }
148
+ // Priority 2: monorepo workspace markers
149
+ if (
150
+ fs.existsSync(path.join(current, 'pnpm-workspace.yaml')) ||
151
+ fs.existsSync(path.join(current, 'lerna.json')) ||
152
+ fs.existsSync(path.join(current, 'turbo.json')) ||
153
+ fs.existsSync(path.join(current, 'nx.json'))
154
+ ) {
155
+ candidate = current;
156
+ }
157
+ // Check package.json for workspaces
158
+ const pkgPath = path.join(current, 'package.json');
159
+ if (fs.existsSync(pkgPath)) {
160
+ try {
161
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
162
+ if (pkg.workspaces) {
163
+ candidate = current;
164
+ }
165
+ } catch {
166
+ // ignore corrupted package.json
167
+ }
168
+ }
169
+
170
+ const parent = path.dirname(current);
171
+ if (parent === current) break;
172
+ current = parent;
173
+ }
174
+
175
+ return candidate;
176
+ }
177
+
178
+ /**
179
+ * Builds the Workspace Evidence Graph for the given root directory.
180
+ * @param {string} rootDir
181
+ * @param {object} [options]
182
+ * @returns {object} WorkspaceGraph
183
+ */
184
+ build(rootDir, options = {}) {
185
+ const realRoot = this.findRepositoryRoot(rootDir);
186
+ const posixRoot = toPosix(realRoot);
187
+ let hitLimit = false;
188
+
189
+ // Read workspace globs if defined (npm/yarn or pnpm)
190
+ const workspaceGlobs = this._detectDeclaredWorkspaces(realRoot);
191
+
192
+ const packages = [];
193
+ const evidence = [];
194
+ const packageIdSet = new Set();
195
+ const manifestHashes = [];
196
+
197
+ // Check root package
198
+ const rootPkg = this._inspectDirectoryForPackage(realRoot, '.', realRoot);
199
+ if (rootPkg) {
200
+ packages.push(rootPkg);
201
+ packageIdSet.add(rootPkg.id);
202
+ manifestHashes.push(...rootPkg.manifestHashes);
203
+ }
204
+
205
+ // Traverse directory tree
206
+ const visitedDirs = new Set();
207
+ try {
208
+ visitedDirs.add(fs.realpathSync(realRoot));
209
+ } catch {
210
+ visitedDirs.add(realRoot);
211
+ }
212
+
213
+ const walk = (currentDir, depth) => {
214
+ if (packages.length >= this.maxPackages || depth > this.maxDepth) {
215
+ hitLimit = true;
216
+ return;
217
+ }
218
+
219
+ let entries;
220
+ try {
221
+ entries = fs.readdirSync(currentDir, { withFileTypes: true });
222
+ } catch {
223
+ return;
224
+ }
225
+
226
+ for (const entry of entries) {
227
+ if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
228
+ if (this.ignoredDirs.has(entry.name)) continue;
229
+
230
+ const fullPath = path.join(currentDir, entry.name);
231
+ let realEntryPath;
232
+ try {
233
+ realEntryPath = fs.realpathSync(fullPath);
234
+ } catch {
235
+ continue;
236
+ }
237
+
238
+ // Symlink escape guard: must remain within root realpath
239
+ if (
240
+ !realEntryPath.startsWith(visitedDirs.values().next().value) &&
241
+ !realEntryPath.startsWith(realRoot)
242
+ ) {
243
+ continue;
244
+ }
245
+
246
+ if (visitedDirs.has(realEntryPath)) continue;
247
+ visitedDirs.add(realEntryPath);
248
+
249
+ const relPath = cleanRelative(path.relative(realRoot, fullPath));
250
+
251
+ // Check if this directory is a package
252
+ const isDeclared =
253
+ workspaceGlobs.length === 0 ||
254
+ workspaceGlobs.some((g) => g.test(relPath));
255
+
256
+ const pkg = this._inspectDirectoryForPackage(fullPath, relPath, realRoot);
257
+ if (pkg) {
258
+ packages.push(pkg);
259
+ packageIdSet.add(pkg.id);
260
+ manifestHashes.push(...pkg.manifestHashes);
261
+ if (packages.length >= this.maxPackages) {
262
+ hitLimit = true;
263
+ break;
264
+ }
265
+ }
266
+
267
+ // Recurse into subdirectories (if not deeper than maxDepth)
268
+ if (depth < this.maxDepth) {
269
+ walk(fullPath, depth + 1);
270
+ }
271
+ }
272
+ };
273
+
274
+ walk(realRoot, 1);
275
+
276
+ // Compute internal cross-package dependencies
277
+ for (const pkg of packages) {
278
+ pkg.internalDependencies = pkg.dependencies.filter(
279
+ (dep) => packageIdSet.has(dep) && dep !== pkg.id
280
+ );
281
+ // Clean up internal hashes before returning
282
+ delete pkg.manifestHashes;
283
+ }
284
+
285
+ // Sort packages deterministically by root path
286
+ packages.sort((a, b) => a.root.localeCompare(b.root));
287
+
288
+ // Compute deterministic fingerprint
289
+ manifestHashes.sort();
290
+ const fingerprint = crypto
291
+ .createHash('sha256')
292
+ .update(manifestHashes.join('\n') || realRoot)
293
+ .digest('hex');
294
+
295
+ // Aggregate baseline evidence for detected packages
296
+ for (const pkg of packages) {
297
+ for (const dep of pkg.dependencies) {
298
+ evidence.push({
299
+ target: dep,
300
+ source: pkg.root === '.' ? 'root-package' : 'workspace-package',
301
+ weight: pkg.root === '.' ? 20 : 30,
302
+ description: `package ${pkg.id} (${pkg.ecosystem}) depends on ${dep}`
303
+ });
304
+ }
305
+ }
306
+
307
+ return {
308
+ schemaVersion: 1,
309
+ repositoryRoot: posixRoot,
310
+ packages,
311
+ evidence,
312
+ fingerprint,
313
+ partial: hitLimit
314
+ };
315
+ }
316
+
317
+ /**
318
+ * Finds the nearest enclosing package for a given file path.
319
+ * Returns the package with the longest matching relative root prefix.
320
+ * @param {string} filePath
321
+ * @param {object} graph WorkspaceGraph
322
+ * @returns {object|null}
323
+ */
324
+ findNearestPackage(filePath, graph) {
325
+ if (!filePath || !graph || !Array.isArray(graph.packages)) return null;
326
+
327
+ let targetRel = toPosix(filePath).trim();
328
+ const repoRoot = toPosix(graph.repositoryRoot);
329
+
330
+ // Handle absolute paths
331
+ if (path.isAbsolute(filePath)) {
332
+ if (process.platform === 'win32') {
333
+ if (targetRel.toLowerCase().startsWith(repoRoot.toLowerCase() + '/')) {
334
+ targetRel = targetRel.slice(repoRoot.length + 1);
335
+ } else if (targetRel.toLowerCase() === repoRoot.toLowerCase()) {
336
+ targetRel = '.';
337
+ }
338
+ } else {
339
+ if (targetRel.startsWith(repoRoot + '/')) {
340
+ targetRel = targetRel.slice(repoRoot.length + 1);
341
+ } else if (targetRel === repoRoot) {
342
+ targetRel = '.';
343
+ }
344
+ }
345
+ }
346
+
347
+ targetRel = cleanRelative(targetRel);
348
+
349
+ let bestMatch = null;
350
+ let longestPrefixLen = -1;
351
+
352
+ for (const pkg of graph.packages) {
353
+ const pkgRoot = cleanRelative(pkg.root);
354
+
355
+ if (pkgRoot === '.') {
356
+ // Root package matches everything as a baseline fallback
357
+ if (longestPrefixLen < 0) {
358
+ bestMatch = pkg;
359
+ longestPrefixLen = 0;
360
+ }
361
+ continue;
362
+ }
363
+
364
+ // Check if targetRel is inside pkgRoot
365
+ const isMatch =
366
+ process.platform === 'win32'
367
+ ? targetRel.toLowerCase() === pkgRoot.toLowerCase() ||
368
+ targetRel.toLowerCase().startsWith(pkgRoot.toLowerCase() + '/')
369
+ : targetRel === pkgRoot || targetRel.startsWith(pkgRoot + '/');
370
+
371
+ if (isMatch) {
372
+ if (pkgRoot.length > longestPrefixLen) {
373
+ bestMatch = pkg;
374
+ longestPrefixLen = pkgRoot.length;
375
+ }
376
+ }
377
+ }
378
+
379
+ return bestMatch;
380
+ }
381
+
382
+ /**
383
+ * Extracts evidence signals for a file based on its nearest enclosing package.
384
+ * @param {string} filePath
385
+ * @param {object} graph
386
+ * @returns {Array<object>} Evidence items with nearest package weights
387
+ */
388
+ extractPackageEvidence(filePath, graph) {
389
+ const pkg = this.findNearestPackage(filePath, graph);
390
+ if (!pkg) return [];
391
+
392
+ const evidence = [];
393
+
394
+ // Dependencies in nearest package get high confidence weight (60)
395
+ for (const dep of pkg.dependencies) {
396
+ evidence.push({
397
+ target: dep,
398
+ source: 'nearest-package',
399
+ weight: 60,
400
+ description: `nearest workspace package "${dep}" in ${pkg.id}`
401
+ });
402
+ }
403
+
404
+ // Config files in nearest package get weight 50
405
+ for (const cfg of pkg.configs) {
406
+ evidence.push({
407
+ target: cfg,
408
+ source: 'package-config',
409
+ weight: 50,
410
+ description: `package config "${cfg}" in ${pkg.id}`
411
+ });
412
+ }
413
+
414
+ return evidence;
415
+ }
416
+
417
+ /**
418
+ * Inspects a directory to determine if it is a package in any supported ecosystem.
419
+ * @private
420
+ */
421
+ _inspectDirectoryForPackage(dirPath, relativeDir, repoRoot) {
422
+ const cleanRel = cleanRelative(relativeDir);
423
+ const manifests = [];
424
+ const manifestHashes = [];
425
+ const dependencies = new Set();
426
+ const languages = new Set();
427
+ let ecosystem = 'unknown';
428
+ let id = cleanRel === '.' ? path.basename(repoRoot) : path.basename(dirPath);
429
+
430
+ // 1. Node / NPM (package.json)
431
+ const pkgJsonPath = path.join(dirPath, 'package.json');
432
+ if (fs.existsSync(pkgJsonPath)) {
433
+ try {
434
+ const content = fs.readFileSync(pkgJsonPath, 'utf8');
435
+ const parsed = JSON.parse(content);
436
+ ecosystem = 'npm';
437
+ manifests.push(cleanRel === '.' ? 'package.json' : `${cleanRel}/package.json`);
438
+ manifestHashes.push(
439
+ crypto.createHash('sha256').update(content).digest('hex')
440
+ );
441
+
442
+ if (parsed.name) id = parsed.name;
443
+
444
+ // Collect all dependencies
445
+ const allDeps = {
446
+ ...parsed.dependencies,
447
+ ...parsed.devDependencies,
448
+ ...parsed.peerDependencies
449
+ };
450
+ for (const dep of Object.keys(allDeps)) {
451
+ dependencies.add(dep);
452
+ }
453
+ } catch {
454
+ // invalid package.json
455
+ }
456
+ }
457
+
458
+ // 2. Python (pyproject.toml, requirements.txt, Pipfile, setup.py)
459
+ const pyprojectPath = path.join(dirPath, 'pyproject.toml');
460
+ const requirementsPath = path.join(dirPath, 'requirements.txt');
461
+ const pipfilePath = path.join(dirPath, 'Pipfile');
462
+ const setupPyPath = path.join(dirPath, 'setup.py');
463
+
464
+ if (
465
+ fs.existsSync(pyprojectPath) ||
466
+ fs.existsSync(requirementsPath) ||
467
+ fs.existsSync(pipfilePath) ||
468
+ fs.existsSync(setupPyPath)
469
+ ) {
470
+ if (ecosystem === 'unknown') ecosystem = 'python';
471
+ languages.add('python');
472
+
473
+ if (fs.existsSync(pyprojectPath)) {
474
+ try {
475
+ const content = fs.readFileSync(pyprojectPath, 'utf8');
476
+ manifests.push(cleanRel === '.' ? 'pyproject.toml' : `${cleanRel}/pyproject.toml`);
477
+ manifestHashes.push(
478
+ crypto.createHash('sha256').update(content).digest('hex')
479
+ );
480
+ const pyDeps = this._parsePyprojectDependencies(content);
481
+ for (const d of pyDeps) dependencies.add(d);
482
+ const nameMatch = content.match(/name\s*=\s*["']([^"']+)["']/);
483
+ if (nameMatch && ecosystem === 'python') id = nameMatch[1];
484
+ } catch {
485
+ // ignore
486
+ }
487
+ }
488
+
489
+ if (fs.existsSync(requirementsPath)) {
490
+ try {
491
+ const content = fs.readFileSync(requirementsPath, 'utf8');
492
+ manifests.push(cleanRel === '.' ? 'requirements.txt' : `${cleanRel}/requirements.txt`);
493
+ manifestHashes.push(
494
+ crypto.createHash('sha256').update(content).digest('hex')
495
+ );
496
+ const reqDeps = this._parseRequirementsDependencies(content);
497
+ for (const d of reqDeps) dependencies.add(d);
498
+ } catch {
499
+ // ignore
500
+ }
501
+ }
502
+ }
503
+
504
+ // 3. Rust (Cargo.toml)
505
+ const cargoPath = path.join(dirPath, 'Cargo.toml');
506
+ if (fs.existsSync(cargoPath)) {
507
+ if (ecosystem === 'unknown') ecosystem = 'rust';
508
+ languages.add('rust');
509
+ try {
510
+ const content = fs.readFileSync(cargoPath, 'utf8');
511
+ manifests.push(cleanRel === '.' ? 'Cargo.toml' : `${cleanRel}/Cargo.toml`);
512
+ manifestHashes.push(
513
+ crypto.createHash('sha256').update(content).digest('hex')
514
+ );
515
+ const nameMatch = content.match(/\[package\][\s\S]*?name\s*=\s*["']([^"']+)["']/);
516
+ if (nameMatch && ecosystem === 'rust') id = nameMatch[1];
517
+ const cargoDeps = this._parseCargoDependencies(content);
518
+ for (const d of cargoDeps) dependencies.add(d);
519
+ } catch {
520
+ // ignore
521
+ }
522
+ }
523
+
524
+ // 4. Go (go.mod)
525
+ const goModPath = path.join(dirPath, 'go.mod');
526
+ if (fs.existsSync(goModPath)) {
527
+ if (ecosystem === 'unknown') ecosystem = 'go';
528
+ languages.add('go');
529
+ try {
530
+ const content = fs.readFileSync(goModPath, 'utf8');
531
+ manifests.push(cleanRel === '.' ? 'go.mod' : `${cleanRel}/go.mod`);
532
+ manifestHashes.push(
533
+ crypto.createHash('sha256').update(content).digest('hex')
534
+ );
535
+ const modMatch = content.match(/module\s+([^\s\r\n]+)/);
536
+ if (modMatch && ecosystem === 'go') id = modMatch[1];
537
+ const goDeps = this._parseGoModDependencies(content);
538
+ for (const d of goDeps) dependencies.add(d);
539
+ } catch {
540
+ // ignore
541
+ }
542
+ }
543
+
544
+ // If no manifest found, not a package
545
+ if (manifests.length === 0) {
546
+ return null;
547
+ }
548
+
549
+ // Detect configs
550
+ const configs = this._detectConfigs(dirPath, cleanRel);
551
+
552
+ // Detect languages if not already determined
553
+ this._detectLanguages(dirPath, languages);
554
+
555
+ return {
556
+ id,
557
+ root: cleanRel,
558
+ ecosystem,
559
+ manifests,
560
+ dependencies: Array.from(dependencies).sort(),
561
+ configs: configs.sort(),
562
+ languages: Array.from(languages).sort(),
563
+ internalDependencies: [],
564
+ manifestHashes
565
+ };
566
+ }
567
+
568
+ /**
569
+ * Detects declared workspace patterns from root manifest files.
570
+ * @private
571
+ */
572
+ _detectDeclaredWorkspaces(rootDir) {
573
+ const patterns = [];
574
+
575
+ // Check package.json workspaces
576
+ const pkgPath = path.join(rootDir, 'package.json');
577
+ if (fs.existsSync(pkgPath)) {
578
+ try {
579
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
580
+ let ws = pkg.workspaces;
581
+ if (ws && typeof ws === 'object' && Array.isArray(ws.packages)) {
582
+ ws = ws.packages;
583
+ }
584
+ if (Array.isArray(ws)) {
585
+ for (const item of ws) {
586
+ if (typeof item === 'string') {
587
+ patterns.push(globToRegex(item));
588
+ }
589
+ }
590
+ }
591
+ } catch {
592
+ // ignore
593
+ }
594
+ }
595
+
596
+ // Check pnpm-workspace.yaml
597
+ const pnpmPath = path.join(rootDir, 'pnpm-workspace.yaml');
598
+ if (fs.existsSync(pnpmPath)) {
599
+ try {
600
+ const content = fs.readFileSync(pnpmPath, 'utf8');
601
+ const lines = content.split(/\r?\n/);
602
+ let inPackages = false;
603
+ for (const line of lines) {
604
+ const trimmed = line.trim();
605
+ if (trimmed.startsWith('packages:')) {
606
+ inPackages = true;
607
+ continue;
608
+ }
609
+ if (inPackages) {
610
+ if (trimmed.startsWith('-')) {
611
+ const val = trimmed.replace(/^-\s*/, '').replace(/['"]/g, '').trim();
612
+ if (val && !val.startsWith('!')) {
613
+ patterns.push(globToRegex(val));
614
+ }
615
+ } else if (trimmed && !trimmed.startsWith('#')) {
616
+ inPackages = false;
617
+ }
618
+ }
619
+ }
620
+ } catch {
621
+ // ignore
622
+ }
623
+ }
624
+
625
+ return patterns;
626
+ }
627
+
628
+ /**
629
+ * Detects known configuration files in a package directory.
630
+ * @private
631
+ */
632
+ _detectConfigs(dirPath, relativeDir) {
633
+ const configs = [];
634
+ for (const cfg of KNOWN_CONFIG_FILES) {
635
+ const fullPath = path.join(dirPath, cfg);
636
+ if (fs.existsSync(fullPath)) {
637
+ configs.push(relativeDir === '.' ? cfg : `${relativeDir}/${cfg}`);
638
+ }
639
+ }
640
+ return configs;
641
+ }
642
+
643
+ /**
644
+ * Scans shallowly for language indicators in the package directory.
645
+ * @private
646
+ */
647
+ _detectLanguages(dirPath, languagesSet) {
648
+ let entries;
649
+ try {
650
+ entries = fs.readdirSync(dirPath, { withFileTypes: true });
651
+ } catch {
652
+ return;
653
+ }
654
+
655
+ for (const entry of entries) {
656
+ if (entry.isFile()) {
657
+ const ext = path.extname(entry.name).toLowerCase();
658
+ if (ext === '.ts' || ext === '.tsx') {
659
+ languagesSet.add('typescript');
660
+ } else if (ext === '.js' || ext === '.jsx' || ext === '.mjs' || ext === '.cjs') {
661
+ languagesSet.add('javascript');
662
+ } else if (ext === '.py') {
663
+ languagesSet.add('python');
664
+ } else if (ext === '.rs') {
665
+ languagesSet.add('rust');
666
+ } else if (ext === '.go') {
667
+ languagesSet.add('go');
668
+ }
669
+ }
670
+ }
671
+ }
672
+
673
+ /**
674
+ * Parses dependencies from requirements.txt content.
675
+ * @private
676
+ */
677
+ _parseRequirementsDependencies(content) {
678
+ const deps = [];
679
+ const lines = content.split(/\r?\n/);
680
+ for (const line of lines) {
681
+ const clean = line.split('#')[0].trim();
682
+ if (!clean) continue;
683
+ const match = clean.match(/^([a-zA-Z0-9_\-\.]+)/);
684
+ if (match) {
685
+ deps.push(match[1].toLowerCase());
686
+ }
687
+ }
688
+ return deps;
689
+ }
690
+
691
+ /**
692
+ * Parses dependencies from pyproject.toml content.
693
+ * @private
694
+ */
695
+ _parsePyprojectDependencies(content) {
696
+ const deps = [];
697
+ // Match dependencies array: dependencies = [ "foo>=1.0", "bar" ]
698
+ const arrayMatch = content.match(/dependencies\s*=\s*\[([\s\S]*?)\]/);
699
+ if (arrayMatch) {
700
+ const inner = arrayMatch[1];
701
+ const items = inner.match(/["']([^"']+)["']/g) || [];
702
+ for (const item of items) {
703
+ const raw = item.replace(/["']/g, '').trim();
704
+ const m = raw.match(/^([a-zA-Z0-9_\-\.]+)/);
705
+ if (m) deps.push(m[1].toLowerCase());
706
+ }
707
+ }
708
+
709
+ // Match poetry dependencies [tool.poetry.dependencies]
710
+ const poetrySection = content.match(/\[tool\.poetry\.dependencies\]([\s\S]*?)(?=\n\[|$)/);
711
+ if (poetrySection) {
712
+ const lines = poetrySection[1].split(/\r?\n/);
713
+ for (const line of lines) {
714
+ const trimmed = line.trim();
715
+ if (!trimmed || trimmed.startsWith('#')) continue;
716
+ const m = trimmed.match(/^([a-zA-Z0-9_\-\.]+)\s*=/);
717
+ if (m && m[1].toLowerCase() !== 'python') {
718
+ deps.push(m[1].toLowerCase());
719
+ }
720
+ }
721
+ }
722
+
723
+ return deps;
724
+ }
725
+
726
+ /**
727
+ * Parses dependencies from Cargo.toml content.
728
+ * @private
729
+ */
730
+ _parseCargoDependencies(content) {
731
+ const deps = [];
732
+ const depSection = content.match(/\[(?:workspace\.)?dependencies\]([\s\S]*?)(?=\n\[|$)/);
733
+ if (depSection) {
734
+ const lines = depSection[1].split(/\r?\n/);
735
+ for (const line of lines) {
736
+ const trimmed = line.trim();
737
+ if (!trimmed || trimmed.startsWith('#')) continue;
738
+ const m = trimmed.match(/^([a-zA-Z0-9_\-]+)\s*=/);
739
+ if (m) deps.push(m[1]);
740
+ }
741
+ }
742
+ return deps;
743
+ }
744
+
745
+ /**
746
+ * Parses dependencies from go.mod content.
747
+ * @private
748
+ */
749
+ _parseGoModDependencies(content) {
750
+ const deps = [];
751
+ // Match single require
752
+ const singleMatches = content.matchAll(/require\s+([^\s\r\n]+)\s+/g);
753
+ for (const m of singleMatches) {
754
+ deps.push(m[1]);
755
+ }
756
+ // Match require block
757
+ const blockMatch = content.match(/require\s*\(([\s\S]*?)\)/);
758
+ if (blockMatch) {
759
+ const lines = blockMatch[1].split(/\r?\n/);
760
+ for (const line of lines) {
761
+ const trimmed = line.trim();
762
+ if (!trimmed || trimmed.startsWith('//')) continue;
763
+ const m = trimmed.match(/^([^\s]+)/);
764
+ if (m) deps.push(m[1]);
765
+ }
766
+ }
767
+ return deps;
768
+ }
769
+ }
770
+
771
+ module.exports = {
772
+ WorkspaceGraphBuilder,
773
+ toPosix,
774
+ cleanRelative,
775
+ globToRegex,
776
+ DEFAULT_IGNORED_DIRS,
777
+ KNOWN_CONFIG_FILES
778
+ };