studiograph 1.1.2 → 1.2.0-beta.1

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 (349) hide show
  1. package/LICENSE +191 -0
  2. package/README.md +301 -10
  3. package/dist/agent/orchestrator.d.ts +17 -9
  4. package/dist/agent/orchestrator.js +142 -97
  5. package/dist/agent/orchestrator.js.map +1 -1
  6. package/dist/agent/prompts/system.md +186 -0
  7. package/dist/agent/skill-loader.d.ts +48 -0
  8. package/dist/agent/skill-loader.js +166 -0
  9. package/dist/agent/skill-loader.js.map +1 -0
  10. package/dist/agent/skills/enrich-entities.md +136 -0
  11. package/dist/agent/skills/entity-schema.md +502 -0
  12. package/dist/agent/skills/gather-context.md +46 -0
  13. package/dist/agent/skills/obsidian-source-setup.md +246 -0
  14. package/dist/agent/skills/skill-loader.d.ts +48 -0
  15. package/dist/agent/skills/skill-loader.js +166 -0
  16. package/dist/agent/skills/skill-loader.js.map +1 -0
  17. package/dist/agent/skills/sync-configuration.md +144 -0
  18. package/dist/agent/skills/sync-setup.md +68 -0
  19. package/dist/agent/tools/connector-tools.d.ts +37 -0
  20. package/dist/agent/tools/connector-tools.js +132 -0
  21. package/dist/agent/tools/connector-tools.js.map +1 -0
  22. package/dist/agent/tools/fs-tools.d.ts +39 -0
  23. package/dist/agent/tools/fs-tools.js +106 -0
  24. package/dist/agent/tools/fs-tools.js.map +1 -0
  25. package/dist/agent/tools/graph-tools.d.ts +30 -2
  26. package/dist/agent/tools/graph-tools.js +154 -37
  27. package/dist/agent/tools/graph-tools.js.map +1 -1
  28. package/dist/agent/tools/load-skill.d.ts +42 -0
  29. package/dist/agent/tools/load-skill.js +45 -0
  30. package/dist/agent/tools/load-skill.js.map +1 -0
  31. package/dist/agent/tools/sync-tools.d.ts +25 -0
  32. package/dist/agent/tools/sync-tools.js +691 -0
  33. package/dist/agent/tools/sync-tools.js.map +1 -0
  34. package/dist/agent/tools/tool-loader.d.ts +25 -0
  35. package/dist/agent/tools/tool-loader.js +73 -0
  36. package/dist/agent/tools/tool-loader.js.map +1 -0
  37. package/dist/auth/github.d.ts +11 -8
  38. package/dist/auth/github.js +56 -75
  39. package/dist/auth/github.js.map +1 -1
  40. package/dist/cli/colors.d.ts +54 -0
  41. package/dist/cli/colors.js +133 -0
  42. package/dist/cli/colors.js.map +1 -0
  43. package/dist/cli/commands/app.d.ts +7 -0
  44. package/dist/cli/commands/app.js +167 -0
  45. package/dist/cli/commands/app.js.map +1 -0
  46. package/dist/cli/commands/auth.d.ts +1 -1
  47. package/dist/cli/commands/auth.js +26 -10
  48. package/dist/cli/commands/auth.js.map +1 -1
  49. package/dist/cli/commands/clone.d.ts +9 -0
  50. package/dist/cli/commands/clone.js +167 -0
  51. package/dist/cli/commands/clone.js.map +1 -0
  52. package/dist/cli/commands/commit.d.ts +8 -0
  53. package/dist/cli/commands/commit.js +43 -0
  54. package/dist/cli/commands/commit.js.map +1 -0
  55. package/dist/cli/commands/config.d.ts +13 -0
  56. package/dist/cli/commands/config.js +276 -0
  57. package/dist/cli/commands/config.js.map +1 -0
  58. package/dist/cli/commands/connector.d.ts +33 -0
  59. package/dist/cli/commands/connector.js +178 -0
  60. package/dist/cli/commands/connector.js.map +1 -0
  61. package/dist/cli/commands/deploy.d.ts +11 -0
  62. package/dist/cli/commands/deploy.js +153 -0
  63. package/dist/cli/commands/deploy.js.map +1 -0
  64. package/dist/cli/commands/enrich.d.ts +11 -0
  65. package/dist/cli/commands/enrich.js +135 -0
  66. package/dist/cli/commands/enrich.js.map +1 -0
  67. package/dist/cli/commands/graphrag.d.ts +12 -0
  68. package/dist/cli/commands/graphrag.js +122 -0
  69. package/dist/cli/commands/graphrag.js.map +1 -0
  70. package/dist/cli/commands/index.d.ts +15 -0
  71. package/dist/cli/commands/index.js +117 -0
  72. package/dist/cli/commands/index.js.map +1 -0
  73. package/dist/cli/commands/init.js +110 -210
  74. package/dist/cli/commands/init.js.map +1 -1
  75. package/dist/cli/commands/join.js +89 -24
  76. package/dist/cli/commands/join.js.map +1 -1
  77. package/dist/cli/commands/lint.d.ts +8 -0
  78. package/dist/cli/commands/lint.js +70 -0
  79. package/dist/cli/commands/lint.js.map +1 -0
  80. package/dist/cli/commands/mcp.d.ts +27 -0
  81. package/dist/cli/commands/mcp.js +56 -0
  82. package/dist/cli/commands/mcp.js.map +1 -0
  83. package/dist/cli/commands/orphans.d.ts +8 -0
  84. package/dist/cli/commands/orphans.js +125 -0
  85. package/dist/cli/commands/orphans.js.map +1 -0
  86. package/dist/cli/commands/provision.d.ts +8 -0
  87. package/dist/cli/commands/provision.js +116 -0
  88. package/dist/cli/commands/provision.js.map +1 -0
  89. package/dist/cli/commands/r2.d.ts +2 -0
  90. package/dist/cli/commands/r2.js +87 -6
  91. package/dist/cli/commands/r2.js.map +1 -1
  92. package/dist/cli/commands/reset.d.ts +12 -0
  93. package/dist/cli/commands/reset.js +137 -0
  94. package/dist/cli/commands/reset.js.map +1 -0
  95. package/dist/cli/commands/review.d.ts +19 -0
  96. package/dist/cli/commands/review.js +128 -0
  97. package/dist/cli/commands/review.js.map +1 -0
  98. package/dist/cli/commands/serve.js +47 -2
  99. package/dist/cli/commands/serve.js.map +1 -1
  100. package/dist/cli/commands/source.d.ts +16 -0
  101. package/dist/cli/commands/source.js +159 -0
  102. package/dist/cli/commands/source.js.map +1 -0
  103. package/dist/cli/commands/start.js +472 -103
  104. package/dist/cli/commands/start.js.map +1 -1
  105. package/dist/cli/commands/sync-entities.d.ts +13 -0
  106. package/dist/cli/commands/sync-entities.js +242 -0
  107. package/dist/cli/commands/sync-entities.js.map +1 -0
  108. package/dist/cli/commands/sync.js +40 -9
  109. package/dist/cli/commands/sync.js.map +1 -1
  110. package/dist/cli/commands/update.d.ts +8 -0
  111. package/dist/cli/commands/update.js +155 -0
  112. package/dist/cli/commands/update.js.map +1 -0
  113. package/dist/cli/index.js +114 -3
  114. package/dist/cli/index.js.map +1 -1
  115. package/dist/cli/scaffolding.d.ts +10 -0
  116. package/dist/cli/scaffolding.js +302 -0
  117. package/dist/cli/scaffolding.js.map +1 -0
  118. package/dist/cli/setup-wizard.d.ts +30 -0
  119. package/dist/cli/setup-wizard.js +244 -0
  120. package/dist/cli/setup-wizard.js.map +1 -0
  121. package/dist/cli/sync-review-interactive.d.ts +31 -0
  122. package/dist/cli/sync-review-interactive.js +393 -0
  123. package/dist/cli/sync-review-interactive.js.map +1 -0
  124. package/dist/cli/theme.d.ts +31 -0
  125. package/dist/cli/theme.js +116 -0
  126. package/dist/cli/theme.js.map +1 -0
  127. package/dist/core/graph.d.ts +16 -9
  128. package/dist/core/graph.js +263 -145
  129. package/dist/core/graph.js.map +1 -1
  130. package/dist/core/migration-runner.d.ts +42 -0
  131. package/dist/core/migration-runner.js +232 -0
  132. package/dist/core/migration-runner.js.map +1 -0
  133. package/dist/core/migration-types.d.ts +101 -0
  134. package/dist/core/migration-types.js +21 -0
  135. package/dist/core/migration-types.js.map +1 -0
  136. package/dist/core/migrations/20260219-formalize-memory-location.d.ts +2 -0
  137. package/dist/core/migrations/20260219-formalize-memory-location.js +35 -0
  138. package/dist/core/migrations/20260219-formalize-memory-location.js.map +1 -0
  139. package/dist/core/migrations/20260220-add-workspace-metadata.d.ts +12 -0
  140. package/dist/core/migrations/20260220-add-workspace-metadata.js +65 -0
  141. package/dist/core/migrations/20260220-add-workspace-metadata.js.map +1 -0
  142. package/dist/core/migrations/20260220-add-workspace-readme.d.ts +11 -0
  143. package/dist/core/migrations/20260220-add-workspace-readme.js +82 -0
  144. package/dist/core/migrations/20260220-add-workspace-readme.js.map +1 -0
  145. package/dist/core/migrations/20260220-migrate-yaml-to-json.d.ts +9 -0
  146. package/dist/core/migrations/20260220-migrate-yaml-to-json.js +64 -0
  147. package/dist/core/migrations/20260220-migrate-yaml-to-json.js.map +1 -0
  148. package/dist/core/migrations/index.d.ts +11 -0
  149. package/dist/core/migrations/index.js +23 -0
  150. package/dist/core/migrations/index.js.map +1 -0
  151. package/dist/core/schema-registry.d.ts +36 -0
  152. package/dist/core/schema-registry.js +161 -0
  153. package/dist/core/schema-registry.js.map +1 -0
  154. package/dist/core/types.d.ts +242 -3
  155. package/dist/core/types.js +21 -2
  156. package/dist/core/types.js.map +1 -1
  157. package/dist/core/user-config.d.ts +16 -0
  158. package/dist/core/user-config.js +8 -0
  159. package/dist/core/user-config.js.map +1 -1
  160. package/dist/core/validation.d.ts +973 -32
  161. package/dist/core/validation.js +163 -4
  162. package/dist/core/validation.js.map +1 -1
  163. package/dist/core/workspace-manager.d.ts +26 -2
  164. package/dist/core/workspace-manager.js +113 -15
  165. package/dist/core/workspace-manager.js.map +1 -1
  166. package/dist/core/workspace.d.ts +20 -11
  167. package/dist/core/workspace.js +123 -34
  168. package/dist/core/workspace.js.map +1 -1
  169. package/dist/mcp/connector-manager.d.ts +65 -0
  170. package/dist/mcp/connector-manager.js +223 -0
  171. package/dist/mcp/connector-manager.js.map +1 -0
  172. package/dist/mcp/connectors/asana.d.ts +2 -0
  173. package/dist/mcp/connectors/asana.js +20 -0
  174. package/dist/mcp/connectors/asana.js.map +1 -0
  175. package/dist/mcp/connectors/definitions.d.ts +45 -0
  176. package/dist/mcp/connectors/definitions.js +32 -0
  177. package/dist/mcp/connectors/definitions.js.map +1 -0
  178. package/dist/mcp/connectors/figma.d.ts +5 -0
  179. package/dist/mcp/connectors/figma.js +21 -0
  180. package/dist/mcp/connectors/figma.js.map +1 -0
  181. package/dist/mcp/connectors/gdrive.d.ts +2 -0
  182. package/dist/mcp/connectors/gdrive.js +20 -0
  183. package/dist/mcp/connectors/gdrive.js.map +1 -0
  184. package/dist/mcp/connectors/granola.d.ts +2 -0
  185. package/dist/mcp/connectors/granola.js +12 -0
  186. package/dist/mcp/connectors/granola.js.map +1 -0
  187. package/dist/mcp/connectors/linear.d.ts +2 -0
  188. package/dist/mcp/connectors/linear.js +19 -0
  189. package/dist/mcp/connectors/linear.js.map +1 -0
  190. package/dist/mcp/connectors/obsidian.d.ts +2 -0
  191. package/dist/mcp/connectors/obsidian.js +19 -0
  192. package/dist/mcp/connectors/obsidian.js.map +1 -0
  193. package/dist/mcp/connectors/pipedrive.d.ts +2 -0
  194. package/dist/mcp/connectors/pipedrive.js +20 -0
  195. package/dist/mcp/connectors/pipedrive.js.map +1 -0
  196. package/dist/mcp/connectors/slack.d.ts +2 -0
  197. package/dist/mcp/connectors/slack.js +21 -0
  198. package/dist/mcp/connectors/slack.js.map +1 -0
  199. package/dist/mcp/oauth-provider.d.ts +41 -0
  200. package/dist/mcp/oauth-provider.js +160 -0
  201. package/dist/mcp/oauth-provider.js.map +1 -0
  202. package/dist/mcp/server.d.ts +11 -0
  203. package/dist/mcp/server.js +28 -0
  204. package/dist/mcp/server.js.map +1 -0
  205. package/dist/mcp/tools.d.ts +14 -0
  206. package/dist/mcp/tools.js +172 -0
  207. package/dist/mcp/tools.js.map +1 -0
  208. package/dist/server/index.js +17 -4
  209. package/dist/server/index.js.map +1 -1
  210. package/dist/server/plugin-loader.d.ts +15 -0
  211. package/dist/server/plugin-loader.js +68 -2
  212. package/dist/server/plugin-loader.js.map +1 -1
  213. package/dist/server/routes/graph-api.js +1 -1
  214. package/dist/server/routes/graph-api.js.map +1 -1
  215. package/dist/server/routes/webhook.js +33 -0
  216. package/dist/server/routes/webhook.js.map +1 -1
  217. package/dist/services/github-provisioner.d.ts +9 -3
  218. package/dist/services/github-provisioner.js +46 -8
  219. package/dist/services/github-provisioner.js.map +1 -1
  220. package/dist/services/lint-service.d.ts +27 -0
  221. package/dist/services/lint-service.js +83 -0
  222. package/dist/services/lint-service.js.map +1 -0
  223. package/dist/services/markdown.d.ts +9 -0
  224. package/dist/services/markdown.js +26 -5
  225. package/dist/services/markdown.js.map +1 -1
  226. package/dist/services/memory-service.d.ts +1 -2
  227. package/dist/services/memory-service.js +5 -4
  228. package/dist/services/memory-service.js.map +1 -1
  229. package/dist/services/orphan-service.d.ts +31 -0
  230. package/dist/services/orphan-service.js +100 -0
  231. package/dist/services/orphan-service.js.map +1 -0
  232. package/dist/services/sync/commit.d.ts +58 -0
  233. package/dist/services/sync/commit.js +350 -0
  234. package/dist/services/sync/commit.js.map +1 -0
  235. package/dist/services/sync/context-index.d.ts +69 -0
  236. package/dist/services/sync/context-index.js +280 -0
  237. package/dist/services/sync/context-index.js.map +1 -0
  238. package/dist/services/sync/derive.d.ts +34 -0
  239. package/dist/services/sync/derive.js +164 -0
  240. package/dist/services/sync/derive.js.map +1 -0
  241. package/dist/services/sync/enrichment-state.d.ts +31 -0
  242. package/dist/services/sync/enrichment-state.js +63 -0
  243. package/dist/services/sync/enrichment-state.js.map +1 -0
  244. package/dist/services/sync/enrichment.d.ts +25 -0
  245. package/dist/services/sync/enrichment.js +121 -0
  246. package/dist/services/sync/enrichment.js.map +1 -0
  247. package/dist/services/sync/frontmatter-extractor.d.ts +40 -0
  248. package/dist/services/sync/frontmatter-extractor.js +273 -0
  249. package/dist/services/sync/frontmatter-extractor.js.map +1 -0
  250. package/dist/services/sync/graph-match-state.d.ts +33 -0
  251. package/dist/services/sync/graph-match-state.js +61 -0
  252. package/dist/services/sync/graph-match-state.js.map +1 -0
  253. package/dist/services/sync/graph-match.d.ts +53 -0
  254. package/dist/services/sync/graph-match.js +316 -0
  255. package/dist/services/sync/graph-match.js.map +1 -0
  256. package/dist/services/sync/graphrag-client.d.ts +43 -0
  257. package/dist/services/sync/graphrag-client.js +94 -0
  258. package/dist/services/sync/graphrag-client.js.map +1 -0
  259. package/dist/services/sync/graphrag-config.d.ts +16 -0
  260. package/dist/services/sync/graphrag-config.js +39 -0
  261. package/dist/services/sync/graphrag-config.js.map +1 -0
  262. package/dist/services/sync/graphrag-context.d.ts +14 -0
  263. package/dist/services/sync/graphrag-context.js +109 -0
  264. package/dist/services/sync/graphrag-context.js.map +1 -0
  265. package/dist/services/sync/graphrag-indexer.d.ts +30 -0
  266. package/dist/services/sync/graphrag-indexer.js +358 -0
  267. package/dist/services/sync/graphrag-indexer.js.map +1 -0
  268. package/dist/services/sync/llm.d.ts +32 -0
  269. package/dist/services/sync/llm.js +115 -0
  270. package/dist/services/sync/llm.js.map +1 -0
  271. package/dist/services/sync/mcp-client.d.ts +59 -0
  272. package/dist/services/sync/mcp-client.js +285 -0
  273. package/dist/services/sync/mcp-client.js.map +1 -0
  274. package/dist/services/sync/model-factory.d.ts +10 -0
  275. package/dist/services/sync/model-factory.js +24 -0
  276. package/dist/services/sync/model-factory.js.map +1 -0
  277. package/dist/services/sync/name-quality.d.ts +31 -0
  278. package/dist/services/sync/name-quality.js +60 -0
  279. package/dist/services/sync/name-quality.js.map +1 -0
  280. package/dist/services/sync/output-schemas.d.ts +92 -0
  281. package/dist/services/sync/output-schemas.js +43 -0
  282. package/dist/services/sync/output-schemas.js.map +1 -0
  283. package/dist/services/sync/prompts.d.ts +19 -0
  284. package/dist/services/sync/prompts.js +128 -0
  285. package/dist/services/sync/prompts.js.map +1 -0
  286. package/dist/services/sync/reconciler.d.ts +48 -0
  287. package/dist/services/sync/reconciler.js +295 -0
  288. package/dist/services/sync/reconciler.js.map +1 -0
  289. package/dist/services/sync/source-config.d.ts +45 -0
  290. package/dist/services/sync/source-config.js +208 -0
  291. package/dist/services/sync/source-config.js.map +1 -0
  292. package/dist/services/sync/source-definitions/asana.d.ts +15 -0
  293. package/dist/services/sync/source-definitions/asana.js +48 -0
  294. package/dist/services/sync/source-definitions/asana.js.map +1 -0
  295. package/dist/services/sync/source-definitions/definitions.d.ts +21 -0
  296. package/dist/services/sync/source-definitions/definitions.js +26 -0
  297. package/dist/services/sync/source-definitions/definitions.js.map +1 -0
  298. package/dist/services/sync/source-definitions/gdrive.d.ts +16 -0
  299. package/dist/services/sync/source-definitions/gdrive.js +68 -0
  300. package/dist/services/sync/source-definitions/gdrive.js.map +1 -0
  301. package/dist/services/sync/source-definitions/granola.d.ts +2 -0
  302. package/dist/services/sync/source-definitions/granola.js +28 -0
  303. package/dist/services/sync/source-definitions/granola.js.map +1 -0
  304. package/dist/services/sync/source-definitions/linear.d.ts +2 -0
  305. package/dist/services/sync/source-definitions/linear.js +60 -0
  306. package/dist/services/sync/source-definitions/linear.js.map +1 -0
  307. package/dist/services/sync/source-definitions/obsidian.d.ts +2 -0
  308. package/dist/services/sync/source-definitions/obsidian.js +55 -0
  309. package/dist/services/sync/source-definitions/obsidian.js.map +1 -0
  310. package/dist/services/sync/source-definitions/pipedrive.d.ts +2 -0
  311. package/dist/services/sync/source-definitions/pipedrive.js +52 -0
  312. package/dist/services/sync/source-definitions/pipedrive.js.map +1 -0
  313. package/dist/services/sync/staging.d.ts +53 -0
  314. package/dist/services/sync/staging.js +131 -0
  315. package/dist/services/sync/staging.js.map +1 -0
  316. package/dist/services/sync/structured-extractor.d.ts +49 -0
  317. package/dist/services/sync/structured-extractor.js +344 -0
  318. package/dist/services/sync/structured-extractor.js.map +1 -0
  319. package/dist/services/sync/sync-runner.d.ts +32 -0
  320. package/dist/services/sync/sync-runner.js +195 -0
  321. package/dist/services/sync/sync-runner.js.map +1 -0
  322. package/dist/services/sync/sync-state.d.ts +43 -0
  323. package/dist/services/sync/sync-state.js +154 -0
  324. package/dist/services/sync/sync-state.js.map +1 -0
  325. package/dist/services/sync/types.d.ts +203 -0
  326. package/dist/services/sync/types.js +8 -0
  327. package/dist/services/sync/types.js.map +1 -0
  328. package/dist/services/sync/unstructured-extractor.d.ts +29 -0
  329. package/dist/services/sync/unstructured-extractor.js +197 -0
  330. package/dist/services/sync/unstructured-extractor.js.map +1 -0
  331. package/dist/services/vector-service.d.ts +88 -0
  332. package/dist/services/vector-service.js +322 -0
  333. package/dist/services/vector-service.js.map +1 -0
  334. package/dist/utils/git.d.ts +26 -4
  335. package/dist/utils/git.js +55 -7
  336. package/dist/utils/git.js.map +1 -1
  337. package/dist/utils/merge-resolver.d.ts +34 -0
  338. package/dist/utils/merge-resolver.js +201 -0
  339. package/dist/utils/merge-resolver.js.map +1 -0
  340. package/dist/utils/preflight.d.ts +2 -1
  341. package/dist/utils/preflight.js +8 -1
  342. package/dist/utils/preflight.js.map +1 -1
  343. package/dist/utils/version-checker.d.ts +23 -0
  344. package/dist/utils/version-checker.js +116 -0
  345. package/dist/utils/version-checker.js.map +1 -0
  346. package/dist/utils/workspace-config.d.ts +8 -0
  347. package/dist/utils/workspace-config.js +22 -0
  348. package/dist/utils/workspace-config.js.map +1 -0
  349. package/package.json +24 -11
@@ -0,0 +1,280 @@
1
+ /**
2
+ * ContextIndex
3
+ *
4
+ * Cross-source context index for enrichment. Scans all repos for entity
5
+ * references using schema relationship fields and wikilinks.
6
+ *
7
+ * Ranks references by recency, enforces a max_tokens budget, and truncates
8
+ * excess context before the LLM call.
9
+ */
10
+ import { existsSync, readdirSync, readFileSync, statSync } from 'fs';
11
+ import { join, basename } from 'path';
12
+ // basename is still used for repoName extraction from repoDir
13
+ import { ENTITY_SCHEMAS } from '../../core/validation.js';
14
+ import { MarkdownService } from '../../services/markdown.js';
15
+ const DEFAULT_MAX_TOKENS = 4000;
16
+ const APPROX_CHARS_PER_TOKEN = 4;
17
+ export class ContextIndex {
18
+ workspacePath;
19
+ schemaRegistry;
20
+ markdownService;
21
+ references = new Map();
22
+ built = false;
23
+ constructor(workspacePath, schemaRegistry) {
24
+ this.workspacePath = workspacePath;
25
+ this.schemaRegistry = schemaRegistry;
26
+ this.markdownService = new MarkdownService();
27
+ }
28
+ /**
29
+ * Scan all repos for entity references.
30
+ */
31
+ build() {
32
+ this.references.clear();
33
+ const repoDirs = this.findRepoDirs();
34
+ for (const repoDir of repoDirs) {
35
+ const repoName = basename(repoDir);
36
+ this.scanRepo(repoDir, repoName);
37
+ }
38
+ this.built = true;
39
+ }
40
+ /**
41
+ * Get all context references for one entity.
42
+ */
43
+ getReferences(entityId) {
44
+ if (!this.built)
45
+ this.build();
46
+ return this.references.get(entityId) ?? [];
47
+ }
48
+ /**
49
+ * Calculate field completeness (0–1) for an entity.
50
+ */
51
+ getCompleteness(entityType, frontmatter) {
52
+ const schema = ENTITY_SCHEMAS[entityType];
53
+ if (!schema)
54
+ return 1;
55
+ const shape = schema._def?.shape?.() ?? schema.shape ?? {};
56
+ const fields = Object.keys(shape).filter(k => !['entity_type', 'entity_id', 'created_at', 'updated_at', 'created_by', 'updated_by', 'visibility'].includes(k));
57
+ if (fields.length === 0)
58
+ return 1;
59
+ let filled = 0;
60
+ for (const field of fields) {
61
+ const value = frontmatter[field];
62
+ if (value !== undefined && value !== null && value !== '' && !(Array.isArray(value) && value.length === 0)) {
63
+ filled++;
64
+ }
65
+ }
66
+ return filled / fields.length;
67
+ }
68
+ /**
69
+ * Filter to entities worth enriching (low completeness + high context).
70
+ */
71
+ getEnrichable(minRefs = 2, maxCompleteness = 0.6) {
72
+ if (!this.built)
73
+ this.build();
74
+ const enrichable = [];
75
+ const repoDirs = this.findRepoDirs();
76
+ for (const repoDir of repoDirs) {
77
+ const repoName = basename(repoDir);
78
+ const subdirs = this.listSubDirs(repoDir);
79
+ for (const entityId of subdirs) {
80
+ const mainPath = join(repoDir, entityId, 'main.md');
81
+ if (!existsSync(mainPath))
82
+ continue;
83
+ const refs = this.getReferences(entityId);
84
+ if (refs.length < minRefs)
85
+ continue;
86
+ try {
87
+ const doc = this.markdownService.parseFile(mainPath);
88
+ const entityType = doc.frontmatter.entity_type ?? repoName;
89
+ const completeness = this.getCompleteness(entityType, doc.frontmatter);
90
+ if (completeness > maxCompleteness)
91
+ continue;
92
+ enrichable.push({
93
+ entity_id: entityId,
94
+ entity_type: entityType,
95
+ repo: repoName,
96
+ path: mainPath,
97
+ frontmatter: doc.frontmatter,
98
+ completeness,
99
+ refCount: refs.length,
100
+ });
101
+ }
102
+ catch {
103
+ // skip unparseable files
104
+ }
105
+ }
106
+ }
107
+ // Sort by most references (richest context first)
108
+ return enrichable.sort((a, b) => b.refCount - a.refCount);
109
+ }
110
+ /**
111
+ * Assemble LLM context bundle for one entity.
112
+ * Ranks references by recency, enforces max_tokens budget.
113
+ */
114
+ assembleContextBundle(entityId, entityName, maxTokens = DEFAULT_MAX_TOKENS) {
115
+ const refs = this.getReferences(entityId);
116
+ if (refs.length === 0)
117
+ return '';
118
+ // Sort by date (most recent first), then by snippet length
119
+ const sorted = [...refs].sort((a, b) => {
120
+ if (a.date && b.date)
121
+ return b.date.localeCompare(a.date);
122
+ if (a.date)
123
+ return -1;
124
+ if (b.date)
125
+ return 1;
126
+ return 0;
127
+ });
128
+ const maxChars = maxTokens * APPROX_CHARS_PER_TOKEN;
129
+ const lines = [];
130
+ let totalChars = 0;
131
+ lines.push(`Context for "${entityName}" (${entityId}):\n`);
132
+ totalChars += lines[0].length;
133
+ for (const ref of sorted) {
134
+ const line = `- [${ref.entity_type}/${ref.entity_id}] (${ref.field}${ref.date ? ', ' + ref.date : ''}): ${ref.snippet}`;
135
+ if (totalChars + line.length > maxChars) {
136
+ lines.push(`\n... and ${sorted.length - lines.length + 1} more references (truncated)`);
137
+ break;
138
+ }
139
+ lines.push(line);
140
+ totalChars += line.length;
141
+ }
142
+ return lines.join('\n');
143
+ }
144
+ // ── Private ────────────────────────────────────────────────────────────────
145
+ scanRepo(repoDir, repoName) {
146
+ const subdirs = this.listSubDirs(repoDir);
147
+ for (const entityId of subdirs) {
148
+ const mainPath = join(repoDir, entityId, 'main.md');
149
+ if (!existsSync(mainPath))
150
+ continue;
151
+ try {
152
+ const doc = this.markdownService.parseFile(mainPath);
153
+ const entityType = doc.frontmatter.entity_type ?? repoName;
154
+ const relFields = this.getRelationshipFields(entityType);
155
+ // Scan relationship fields
156
+ for (const field of relFields) {
157
+ const value = doc.frontmatter[field];
158
+ if (!value)
159
+ continue;
160
+ const refs = Array.isArray(value) ? value : [value];
161
+ for (const ref of refs) {
162
+ const targetId = this.extractEntityId(String(ref));
163
+ if (!targetId)
164
+ continue;
165
+ this.addReference(targetId, {
166
+ entity_id: entityId,
167
+ entity_type: entityType,
168
+ field,
169
+ repo: repoName,
170
+ date: doc.frontmatter.date ?? doc.frontmatter.created_at,
171
+ snippet: this.makeSnippet(doc.frontmatter, entityType),
172
+ });
173
+ }
174
+ }
175
+ // Scan wikilinks in content
176
+ for (const link of doc.wikilinks) {
177
+ const targetId = this.extractEntityId(link);
178
+ if (!targetId || targetId === entityId)
179
+ continue;
180
+ this.addReference(targetId, {
181
+ entity_id: entityId,
182
+ entity_type: entityType,
183
+ field: 'content',
184
+ repo: repoName,
185
+ date: doc.frontmatter.date ?? doc.frontmatter.created_at,
186
+ snippet: this.makeSnippet(doc.frontmatter, entityType),
187
+ });
188
+ }
189
+ }
190
+ catch {
191
+ // skip unparseable files
192
+ }
193
+ }
194
+ }
195
+ addReference(entityId, ref) {
196
+ if (!this.references.has(entityId)) {
197
+ this.references.set(entityId, []);
198
+ }
199
+ this.references.get(entityId).push(ref);
200
+ }
201
+ /**
202
+ * Get relationship fields for an entity type by inspecting schema descriptions.
203
+ */
204
+ getRelationshipFields(entityType) {
205
+ const schema = ENTITY_SCHEMAS[entityType];
206
+ if (!schema)
207
+ return [];
208
+ const shape = schema._def?.shape?.() ?? schema.shape ?? {};
209
+ const fields = [];
210
+ for (const [name, zodType] of Object.entries(shape)) {
211
+ const desc = this.getDescription(zodType);
212
+ if (desc && (desc.includes('wikilink') || desc.includes('Wikilink'))) {
213
+ fields.push(name);
214
+ }
215
+ }
216
+ return fields;
217
+ }
218
+ getDescription(zodType) {
219
+ const def = zodType?._def;
220
+ if (!def)
221
+ return undefined;
222
+ if (def.description)
223
+ return def.description;
224
+ // Unwrap optional/default
225
+ if (def.innerType)
226
+ return this.getDescription(def.innerType);
227
+ return undefined;
228
+ }
229
+ extractEntityId(value) {
230
+ // [[entity-id]] or [[entity-id|Display Name]]
231
+ const match = value.match(/\[\[([^\]|]+)/);
232
+ if (match)
233
+ return match[1];
234
+ // Plain kebab-case
235
+ if (/^[a-z0-9_-]+$/.test(value))
236
+ return value;
237
+ return null;
238
+ }
239
+ makeSnippet(frontmatter, entityType) {
240
+ const name = frontmatter.name ?? frontmatter.title ?? '';
241
+ const date = frontmatter.date ?? '';
242
+ return `${entityType}: ${name}${date ? ' (' + date + ')' : ''}`;
243
+ }
244
+ findRepoDirs() {
245
+ // Look for workspace config to find repos
246
+ const configPath = join(this.workspacePath, '.studiograph', 'workspace.json');
247
+ if (existsSync(configPath)) {
248
+ try {
249
+ const config = JSON.parse(readFileSync(configPath, 'utf-8'));
250
+ if (config.repos && Array.isArray(config.repos)) {
251
+ return config.repos
252
+ .map((r) => join(this.workspacePath, r.path))
253
+ .filter((p) => existsSync(p));
254
+ }
255
+ }
256
+ catch {
257
+ // fall through to directory scan
258
+ }
259
+ }
260
+ // Fallback: scan top-level directories
261
+ return this.listSubDirs(this.workspacePath)
262
+ .map(d => join(this.workspacePath, d))
263
+ .filter(d => !d.includes('.studiograph') && !d.includes('node_modules') && !d.includes('.git'));
264
+ }
265
+ listSubDirs(dir) {
266
+ if (!existsSync(dir))
267
+ return [];
268
+ return readdirSync(dir).filter(f => {
269
+ if (f.startsWith('.'))
270
+ return false;
271
+ try {
272
+ return statSync(join(dir, f)).isDirectory();
273
+ }
274
+ catch {
275
+ return false;
276
+ }
277
+ });
278
+ }
279
+ }
280
+ //# sourceMappingURL=context-index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-index.js","sourceRoot":"","sources":["../../../src/services/sync/context-index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * DerivationEngine
3
+ *
4
+ * Derives secondary entities (e.g. person, client) from primary extracted
5
+ * records by inspecting schema relationship fields.
6
+ *
7
+ * If a mapping references entity type X via relationship fields but no mapping
8
+ * directly provides X, then X needs derivation.
9
+ */
10
+ import type { SchemaRegistry } from '../../core/schema-registry.js';
11
+ import type { EntityMapping, ExtractedRecord, DerivationRule } from './types.js';
12
+ export declare class DerivationEngine {
13
+ private schemaRegistry;
14
+ constructor(schemaRegistry: SchemaRegistry);
15
+ /**
16
+ * Detect which entity types need derivation.
17
+ *
18
+ * Logic: if a mapping references entity type X via relationship fields,
19
+ * but no mapping directly provides X, then X needs derivation.
20
+ */
21
+ detectDerivationNeeds(mappings: EntityMapping[]): DerivationRule[];
22
+ /**
23
+ * Derive secondary entities from primary extracted records.
24
+ */
25
+ derive(records: ExtractedRecord[], rules: DerivationRule[]): ExtractedRecord[];
26
+ /**
27
+ * Get relationship fields and their referenced entity types for a given entity type.
28
+ */
29
+ private getReferencedTypes;
30
+ /**
31
+ * Extract values from a frontmatter field (handles arrays and single values).
32
+ */
33
+ private extractValues;
34
+ }
@@ -0,0 +1,164 @@
1
+ /**
2
+ * DerivationEngine
3
+ *
4
+ * Derives secondary entities (e.g. person, client) from primary extracted
5
+ * records by inspecting schema relationship fields.
6
+ *
7
+ * If a mapping references entity type X via relationship fields but no mapping
8
+ * directly provides X, then X needs derivation.
9
+ */
10
+ import { ENTITY_SCHEMAS } from '../../core/validation.js';
11
+ import { slugify } from './structured-extractor.js';
12
+ /**
13
+ * Known relationship fields and the entity types they reference.
14
+ * Derived from schema .describe() annotations containing "wikilink".
15
+ */
16
+ const RELATIONSHIP_FIELDS = {
17
+ attendees: 'person',
18
+ contacts: 'person',
19
+ team: 'person',
20
+ decision_makers: 'person',
21
+ stakeholders: 'person',
22
+ assignee: 'person',
23
+ person_name: 'person',
24
+ primary_contact: 'person',
25
+ reports_to: 'person',
26
+ client: 'client',
27
+ organization: 'client',
28
+ related_projects: 'project',
29
+ related_organizations: 'client',
30
+ deal: 'deal',
31
+ project: 'project',
32
+ proposals: 'proposal',
33
+ related_client: 'client',
34
+ };
35
+ /**
36
+ * Default repos for derived entity types.
37
+ */
38
+ const DEFAULT_REPOS = {
39
+ person: 'people',
40
+ client: 'clients',
41
+ project: 'projects',
42
+ deal: 'deals',
43
+ proposal: 'proposals',
44
+ };
45
+ export class DerivationEngine {
46
+ schemaRegistry;
47
+ constructor(schemaRegistry) {
48
+ this.schemaRegistry = schemaRegistry;
49
+ }
50
+ /**
51
+ * Detect which entity types need derivation.
52
+ *
53
+ * Logic: if a mapping references entity type X via relationship fields,
54
+ * but no mapping directly provides X, then X needs derivation.
55
+ */
56
+ detectDerivationNeeds(mappings) {
57
+ const providedTypes = new Set(mappings.map(m => m.entity_type));
58
+ const rules = [];
59
+ const seen = new Set();
60
+ for (const mapping of mappings) {
61
+ // Inspect the entity type schema for relationship fields
62
+ const referencedTypes = this.getReferencedTypes(mapping.entity_type);
63
+ for (const [field, referencedType] of Object.entries(referencedTypes)) {
64
+ // Skip if already provided by a mapping
65
+ if (providedTypes.has(referencedType))
66
+ continue;
67
+ if (seen.has(referencedType)) {
68
+ // Add to existing rule's source fields
69
+ const existing = rules.find(r => r.entity_type === referencedType);
70
+ if (existing && !existing.source_fields.includes(field)) {
71
+ existing.source_fields.push(field);
72
+ if (!existing.from_entity_types.includes(mapping.entity_type)) {
73
+ existing.from_entity_types.push(mapping.entity_type);
74
+ }
75
+ }
76
+ continue;
77
+ }
78
+ seen.add(referencedType);
79
+ rules.push({
80
+ entity_type: referencedType,
81
+ source_fields: [field],
82
+ from_entity_types: [mapping.entity_type],
83
+ target_repo: DEFAULT_REPOS[referencedType] ?? `${referencedType}s`,
84
+ });
85
+ }
86
+ }
87
+ return rules;
88
+ }
89
+ /**
90
+ * Derive secondary entities from primary extracted records.
91
+ */
92
+ derive(records, rules) {
93
+ const derived = [];
94
+ const seenIds = new Set();
95
+ for (const rule of rules) {
96
+ // Filter records to those from entity types that feed this derivation
97
+ const sourceRecords = records.filter(r => rule.from_entity_types.includes(r.entity_type));
98
+ for (const record of sourceRecords) {
99
+ for (const field of rule.source_fields) {
100
+ const values = this.extractValues(record.frontmatter, field);
101
+ for (const value of values) {
102
+ const entityId = slugify(cleanWikilink(value));
103
+ if (!entityId || seenIds.has(entityId))
104
+ continue;
105
+ seenIds.add(entityId);
106
+ const name = cleanWikilink(value);
107
+ const frontmatter = {
108
+ entity_type: rule.entity_type,
109
+ entity_id: entityId,
110
+ name,
111
+ };
112
+ derived.push({
113
+ entity_type: rule.entity_type,
114
+ entity_id: entityId,
115
+ target_repo: rule.target_repo,
116
+ frontmatter,
117
+ content: '',
118
+ source_name: record.source_name,
119
+ source_ref: `${record.source_name}:#${rule.entity_type}/${entityId}`,
120
+ confidence: 0.5,
121
+ });
122
+ }
123
+ }
124
+ }
125
+ }
126
+ return derived;
127
+ }
128
+ /**
129
+ * Get relationship fields and their referenced entity types for a given entity type.
130
+ */
131
+ getReferencedTypes(entityType) {
132
+ const result = {};
133
+ // Check known relationship fields against the schema
134
+ const schema = ENTITY_SCHEMAS[entityType];
135
+ if (!schema)
136
+ return result;
137
+ const shape = schema._def?.shape?.() ?? schema.shape ?? {};
138
+ for (const [fieldName, refType] of Object.entries(RELATIONSHIP_FIELDS)) {
139
+ if (fieldName in shape) {
140
+ result[fieldName] = refType;
141
+ }
142
+ }
143
+ return result;
144
+ }
145
+ /**
146
+ * Extract values from a frontmatter field (handles arrays and single values).
147
+ */
148
+ extractValues(frontmatter, field) {
149
+ const value = frontmatter[field];
150
+ if (!value)
151
+ return [];
152
+ if (Array.isArray(value))
153
+ return value.map(String).filter(Boolean);
154
+ return [String(value)];
155
+ }
156
+ }
157
+ /**
158
+ * Strip wikilink brackets: "[[john-doe]]" → "john-doe", "[[john|John Doe]]" → "john"
159
+ */
160
+ function cleanWikilink(value) {
161
+ const match = value.match(/^\[\[([^\]|]+)(?:\|[^\]]+)?\]\]$/);
162
+ return match ? match[1] : value;
163
+ }
164
+ //# sourceMappingURL=derive.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"derive.js","sourceRoot":"","sources":["../../../src/services/sync/derive.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAC;AAIpD;;;GAGG;AACH,MAAM,mBAAmB,GAA2B;IAClD,SAAS,EAAE,QAAQ;IACnB,QAAQ,EAAE,QAAQ;IAClB,IAAI,EAAE,QAAQ;IACd,eAAe,EAAE,QAAQ;IACzB,YAAY,EAAE,QAAQ;IACtB,QAAQ,EAAE,QAAQ;IAClB,WAAW,EAAE,QAAQ;IACrB,eAAe,EAAE,QAAQ;IACzB,UAAU,EAAE,QAAQ;IACpB,MAAM,EAAE,QAAQ;IAChB,YAAY,EAAE,QAAQ;IACtB,gBAAgB,EAAE,SAAS;IAC3B,qBAAqB,EAAE,QAAQ;IAC/B,IAAI,EAAE,MAAM;IACZ,OAAO,EAAE,SAAS;IAClB,SAAS,EAAE,UAAU;IACrB,cAAc,EAAE,QAAQ;CACzB,CAAC;AAEF;;GAEG;AACH,MAAM,aAAa,GAA2B;IAC5C,MAAM,EAAE,QAAQ;IAChB,MAAM,EAAE,SAAS;IACjB,OAAO,EAAE,UAAU;IACnB,IAAI,EAAE,OAAO;IACb,QAAQ,EAAE,WAAW;CACtB,CAAC;AAEF,MAAM,OAAO,gBAAgB;IACnB,cAAc,CAAiB;IAEvC,YAAY,cAA8B;QACxC,IAAI,CAAC,cAAc,GAAG,cAAc,CAAC;IACvC,CAAC;IAED;;;;;OAKG;IACH,qBAAqB,CAAC,QAAyB;QAC7C,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC;QAChE,MAAM,KAAK,GAAqB,EAAE,CAAC;QACnC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;QAE/B,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,yDAAyD;YACzD,MAAM,eAAe,GAAG,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;YAErE,KAAK,MAAM,CAAC,KAAK,EAAE,cAAc,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,EAAE,CAAC;gBACtE,wCAAwC;gBACxC,IAAI,aAAa,CAAC,GAAG,CAAC,cAAc,CAAC;oBAAE,SAAS;gBAChD,IAAI,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE,CAAC;oBAC7B,uCAAuC;oBACvC,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,KAAK,cAAc,CAAC,CAAC;oBACnE,IAAI,QAAQ,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;wBACxD,QAAQ,CAAC,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;wBACnC,IAAI,CAAC,QAAQ,CAAC,iBAAiB,CAAC,QAAQ,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;4BAC9D,QAAQ,CAAC,iBAAiB,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;wBACvD,CAAC;oBACH,CAAC;oBACD,SAAS;gBACX,CAAC;gBAED,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;gBACzB,KAAK,CAAC,IAAI,CAAC;oBACT,WAAW,EAAE,cAAc;oBAC3B,aAAa,EAAE,CAAC,KAAK,CAAC;oBACtB,iBAAiB,EAAE,CAAC,OAAO,CAAC,WAAW,CAAC;oBACxC,WAAW,EAAE,aAAa,CAAC,cAAc,CAAC,IAAI,GAAG,cAAc,GAAG;iBACnE,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,OAA0B,EAAE,KAAuB;QACxD,MAAM,OAAO,GAAsB,EAAE,CAAC;QACtC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;QAElC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,sEAAsE;YACtE,MAAM,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC;YAE1F,KAAK,MAAM,MAAM,IAAI,aAAa,EAAE,CAAC;gBACnC,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;oBACvC,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;oBAE7D,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;wBAC3B,MAAM,QAAQ,GAAG,OAAO,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC;wBAC/C,IAAI,CAAC,QAAQ,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC;4BAAE,SAAS;wBACjD,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;wBAEtB,MAAM,IAAI,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;wBAClC,MAAM,WAAW,GAAwB;4BACvC,WAAW,EAAE,IAAI,CAAC,WAAW;4BAC7B,SAAS,EAAE,QAAQ;4BACnB,IAAI;yBACL,CAAC;wBAEF,OAAO,CAAC,IAAI,CAAC;4BACX,WAAW,EAAE,IAAI,CAAC,WAAW;4BAC7B,SAAS,EAAE,QAAQ;4BACnB,WAAW,EAAE,IAAI,CAAC,WAAW;4BAC7B,WAAW;4BACX,OAAO,EAAE,EAAE;4BACX,WAAW,EAAE,MAAM,CAAC,WAAW;4BAC/B,UAAU,EAAE,GAAG,MAAM,CAAC,WAAW,KAAK,IAAI,CAAC,WAAW,IAAI,QAAQ,EAAE;4BACpE,UAAU,EAAE,GAAG;yBAChB,CAAC,CAAC;oBACL,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;IAED;;OAEG;IACK,kBAAkB,CAAC,UAAkB;QAC3C,MAAM,MAAM,GAA2B,EAAE,CAAC;QAE1C,qDAAqD;QACrD,MAAM,MAAM,GAAG,cAAc,CAAC,UAAyC,CAAC,CAAC;QACzE,IAAI,CAAC,MAAM;YAAE,OAAO,MAAM,CAAC;QAE3B,MAAM,KAAK,GAAI,MAAc,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,IAAK,MAAc,CAAC,KAAK,IAAI,EAAE,CAAC;QAE7E,KAAK,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,mBAAmB,CAAC,EAAE,CAAC;YACvE,IAAI,SAAS,IAAI,KAAK,EAAE,CAAC;gBACvB,MAAM,CAAC,SAAS,CAAC,GAAG,OAAO,CAAC;YAC9B,CAAC;QACH,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;OAEG;IACK,aAAa,CAAC,WAAgC,EAAE,KAAa;QACnE,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;QACjC,IAAI,CAAC,KAAK;YAAE,OAAO,EAAE,CAAC;QACtB,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACnE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACzB,CAAC;CACF;AAED;;GAEG;AACH,SAAS,aAAa,CAAC,KAAa;IAClC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,kCAAkC,CAAC,CAAC;IAC9D,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;AAClC,CAAC"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * EnrichmentState
3
+ *
4
+ * Tracks context hashes per entity to avoid redundant LLM calls.
5
+ * Stored at .studiograph/enrichment-state.json.
6
+ *
7
+ * Before enriching, we hash the assembled context bundle. If the hash
8
+ * matches the stored value, we skip — the entity has already been
9
+ * enriched with identical context. When new references appear, the
10
+ * hash changes and the entity is re-enriched.
11
+ */
12
+ export interface EnrichmentStateData {
13
+ /** entity_id → SHA-256 hex of the context bundle used for last enrichment */
14
+ entities: Record<string, string>;
15
+ }
16
+ export declare class EnrichmentState {
17
+ private filePath;
18
+ private data;
19
+ constructor(workspacePath: string);
20
+ private load;
21
+ save(): void;
22
+ /**
23
+ * Check whether the context bundle has changed since last enrichment.
24
+ * Returns true if the entity should be enriched (new or changed context).
25
+ */
26
+ hasChanged(entityId: string, contextBundle: string): boolean;
27
+ /**
28
+ * Record the context hash after successful enrichment.
29
+ */
30
+ record(entityId: string, contextBundle: string): void;
31
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * EnrichmentState
3
+ *
4
+ * Tracks context hashes per entity to avoid redundant LLM calls.
5
+ * Stored at .studiograph/enrichment-state.json.
6
+ *
7
+ * Before enriching, we hash the assembled context bundle. If the hash
8
+ * matches the stored value, we skip — the entity has already been
9
+ * enriched with identical context. When new references appear, the
10
+ * hash changes and the entity is re-enriched.
11
+ */
12
+ import { createHash } from 'crypto';
13
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
14
+ import { join, dirname } from 'path';
15
+ export class EnrichmentState {
16
+ filePath;
17
+ data = null;
18
+ constructor(workspacePath) {
19
+ this.filePath = join(workspacePath, '.studiograph', 'enrichment-state.json');
20
+ }
21
+ load() {
22
+ if (this.data)
23
+ return this.data;
24
+ if (existsSync(this.filePath)) {
25
+ try {
26
+ this.data = JSON.parse(readFileSync(this.filePath, 'utf-8'));
27
+ }
28
+ catch {
29
+ this.data = { entities: {} };
30
+ }
31
+ }
32
+ else {
33
+ this.data = { entities: {} };
34
+ }
35
+ return this.data;
36
+ }
37
+ save() {
38
+ const dir = dirname(this.filePath);
39
+ if (!existsSync(dir)) {
40
+ mkdirSync(dir, { recursive: true });
41
+ }
42
+ writeFileSync(this.filePath, JSON.stringify(this.load(), null, 2) + '\n', 'utf-8');
43
+ }
44
+ /**
45
+ * Check whether the context bundle has changed since last enrichment.
46
+ * Returns true if the entity should be enriched (new or changed context).
47
+ */
48
+ hasChanged(entityId, contextBundle) {
49
+ const hash = hashContext(contextBundle);
50
+ const stored = this.load().entities[entityId];
51
+ return stored !== hash;
52
+ }
53
+ /**
54
+ * Record the context hash after successful enrichment.
55
+ */
56
+ record(entityId, contextBundle) {
57
+ this.load().entities[entityId] = hashContext(contextBundle);
58
+ }
59
+ }
60
+ function hashContext(contextBundle) {
61
+ return createHash('sha256').update(contextBundle).digest('hex');
62
+ }
63
+ //# sourceMappingURL=enrichment-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"enrichment-state.js","sourceRoot":"","sources":["../../../src/services/sync/enrichment-state.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AACxE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAOrC,MAAM,OAAO,eAAe;IAClB,QAAQ,CAAS;IACjB,IAAI,GAA+B,IAAI,CAAC;IAEhD,YAAY,aAAqB;QAC/B,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,aAAa,EAAE,cAAc,EAAE,uBAAuB,CAAC,CAAC;IAC/E,CAAC;IAEO,IAAI;QACV,IAAI,IAAI,CAAC,IAAI;YAAE,OAAO,IAAI,CAAC,IAAI,CAAC;QAEhC,IAAI,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC9B,IAAI,CAAC;gBACH,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAwB,CAAC;YACtF,CAAC;YAAC,MAAM,CAAC;gBACP,IAAI,CAAC,IAAI,GAAG,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;YAC/B,CAAC;QACH,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,IAAI,GAAG,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;QAC/B,CAAC;QAED,OAAO,IAAI,CAAC,IAAI,CAAC;IACnB,CAAC;IAED,IAAI;QACF,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACnC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACrB,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACtC,CAAC;QACD,aAAa,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;IACrF,CAAC;IAED;;;OAGG;IACH,UAAU,CAAC,QAAgB,EAAE,aAAqB;QAChD,MAAM,IAAI,GAAG,WAAW,CAAC,aAAa,CAAC,CAAC;QACxC,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAC9C,OAAO,MAAM,KAAK,IAAI,CAAC;IACzB,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,QAAgB,EAAE,aAAqB;QAC5C,IAAI,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,WAAW,CAAC,aAAa,CAAC,CAAC;IAC9D,CAAC;CACF;AAED,SAAS,WAAW,CAAC,aAAqB;IACxC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAClE,CAAC"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Enrichment runner
3
+ *
4
+ * Enriches existing entities with cross-source context via LLM.
5
+ * Writes enriched files directly to repos (overwrite, no commit).
6
+ * User reviews via git diff, then commits or aborts.
7
+ */
8
+ export interface EnrichOptions {
9
+ workspacePath: string;
10
+ entityTypes?: string[];
11
+ dryRun?: boolean;
12
+ minRefs?: number;
13
+ maxCompleteness?: number;
14
+ concurrency?: number;
15
+ schemaExtensions?: Record<string, any>;
16
+ }
17
+ export interface EnrichResult {
18
+ enriched: number;
19
+ skipped: number;
20
+ errors: {
21
+ entityId: string;
22
+ error: string;
23
+ }[];
24
+ }
25
+ export declare function runEnrichment(options: EnrichOptions, onProgress?: (msg: string) => void): Promise<EnrichResult>;