contextos-agents 1.7.0 → 2.0.0-beta.2

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