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,246 @@
1
+ ---
2
+ name: obsidian-source-setup
3
+ description: Scaffold an Obsidian vault into a sync source config — explore vault structure, map directories to entity types, build field maps from frontmatter
4
+ loading: on-demand
5
+ ---
6
+
7
+ # Obsidian Source Setup
8
+
9
+ Guide for mapping any Obsidian vault to a Studiograph sync source config. Every vault is different — follow these steps to explore the vault and build a config tailored to its structure.
10
+
11
+ ## Step 1: Explore the Vault
12
+
13
+ Before writing any config, understand the vault's organization.
14
+
15
+ 1. **List top-level directories** — use the Obsidian MCP connector or read the filesystem directly:
16
+ - Note every top-level folder name
17
+ - Identify which are content folders vs system folders (`.obsidian/`, `.smart-env/`, `Attachments/`, `Templates/`)
18
+ - Check for subdirectory structure (e.g., `Organizations/Clients/`, `Organizations/Vendors/`)
19
+
20
+ 2. **Count files per directory** — understand the scale:
21
+ - Large directories (100+) are likely core entity types
22
+ - Small directories (<10) may not be worth a dedicated mapping
23
+
24
+ 3. **Check for nested content** — some vaults use flat files, others use subfolders:
25
+ - `People/John Smith.md` (flat) — each file is one entity
26
+ - `Projects/Acme/Acme.md` + `Projects/Acme/SOW.md` (nested) — folder per entity with sub-documents
27
+
28
+ ## Step 2: Sample Frontmatter
29
+
30
+ Read the first 25-30 lines of 2-3 files per directory to see what YAML frontmatter exists.
31
+
32
+ **Key things to look for:**
33
+ - Which fields are present (even if empty)
34
+ - Wikilink references: `client: "[[Acme Corp]]"` or `related_people: ["[[John Smith]]"]`
35
+ - Tag conventions: `tags: [project/active]`, `tags: ["#person"]`, `tags: [sop, operations]`
36
+ - Date formats: `date: 2026-01-15`, `start_date: 2026-01-15`
37
+ - Status encoding: frontmatter `status:` field vs tag-based `tags: [project/active]`
38
+
39
+ **Frontmatter vs no frontmatter:**
40
+ - Files WITH frontmatter (YAML between `---` fences) → use `frontmatter` extraction mode
41
+ - Files WITHOUT frontmatter (plain markdown) → use `unstructured` mode (LLM extraction, slower)
42
+ - If a directory has a mix, prefer `frontmatter` — files without frontmatter will be skipped, which is often acceptable
43
+
44
+ ## Step 3: Map Directories to Entity Types
45
+
46
+ For each content directory, decide which Studiograph entity type it maps to. Use the directory name, file content, and frontmatter fields as signals.
47
+
48
+ ### Entity Type Reference
49
+
50
+ **Project repo types** (time-bound, engagement-specific):
51
+ | Type | Use when | Key fields |
52
+ |------|----------|------------|
53
+ | `project` | Client engagements, initiatives | name, client, start_date, end_date, status, team |
54
+ | `meeting` | Meetings, events, conferences | name, date, attendees, agenda, action_items |
55
+ | `task` | Action items, tickets | name, assignee, due_date, status, priority |
56
+ | `deliverable` | Outputs, milestones | name, project, status, due_date |
57
+ | `brief` | Project briefs, RFPs | project_name, client, objectives |
58
+ | `artifact` | Files, documents, assets | name, type, project, url |
59
+ | `decision` | Architectural/business decisions | title, date, status, rationale |
60
+
61
+ **Function repo types** (operational, cross-project):
62
+ | Type | Use when | Key fields |
63
+ |------|----------|------------|
64
+ | `person` | People, contacts, team members | name, role, email, status, organization |
65
+ | `client` | Companies, organizations (as clients) | name, type, status, industry, contacts |
66
+ | `vendor` | Vendors, partners, freelancers | name, type, status, services |
67
+ | `deal` | Sales pipeline entries | name, client, stage, deal_value |
68
+ | `proposal` | Proposals, SOWs | name, client, status, deliverables |
69
+ | `contract` | Contracts, agreements | name, type, client, status |
70
+ | `financial-plan` | Budgets, rate cards, P&L | name, type, period |
71
+ | `role` | Job descriptions, org structure | title, department, level, type |
72
+
73
+ **Shared resource types** (reference, reusable):
74
+ | Type | Use when | Key fields |
75
+ |------|----------|------------|
76
+ | `process` | SOPs, workflows, methodologies | name, description, steps, owner |
77
+ | `standard` | Guidelines, conventions | name, category, guidelines |
78
+ | `template` | Reusable templates | name, category, template_content |
79
+ | `reference` | Articles, research, bookmarks | name, type, source, link |
80
+ | `technique` | Methods, frameworks | name, category, difficulty |
81
+ | `practice-area` | Service lines, capabilities | name, description, capabilities |
82
+ | `case-study` | Portfolio work, showcases | title, client, year, practice_areas |
83
+
84
+ ### Common Obsidian Directory Mappings
85
+
86
+ These are patterns seen across vaults — adapt to the actual vault:
87
+
88
+ | Directory pattern | Likely entity type | Notes |
89
+ |-------------------|--------------------|-------|
90
+ | `Projects/` | `project` | May have sub-documents (see nested content below) |
91
+ | `People/`, `Contacts/` | `person` | Usually flat files |
92
+ | `Team/`, `Staff/` | `person` | Often richer frontmatter than external contacts |
93
+ | `Clients/`, `Companies/`, `Organizations/` | `client` | May have subdirectories by relationship type |
94
+ | `Meetings/`, `Meeting Notes/` | `meeting` | Date-prefixed filenames common |
95
+ | `Tasks/`, `Action Items/` | `task` | May overlap with a task manager integration |
96
+ | `SOPs/`, `Processes/`, `Workflows/` | `process` | Often has category/status/owner fields |
97
+ | `Areas/`, `Practice Areas/`, `Capabilities/` | `practice-area` | May lack frontmatter |
98
+ | `Case Studies/`, `Portfolio/`, `Work/` | `case-study` | Often has client, year, url fields |
99
+ | `Research/`, `References/`, `Resources/` | `reference` | Bookmarks, articles, links |
100
+ | `HR/`, `Roles/`, `Job Descriptions/` | `role` | Job postings, org structure |
101
+ | `Finances/`, `Finance/`, `Budgets/` | `financial-plan` | Rate cards, P&L, budgets |
102
+ | `Standards/`, `Guidelines/` | `standard` | Naming conventions, code standards |
103
+ | `Templates/` | `template` | Reusable document templates |
104
+ | `Proposals/`, `SOWs/` | `proposal` | Client proposals |
105
+ | `Contracts/` | `contract` | Agreements, NDAs |
106
+ | `Events/`, `Conferences/` | `meeting` | No dedicated event type — meeting is closest |
107
+ | `Emails/` | `artifact` | No dedicated email type |
108
+ | `Archive/` | same as active, disabled | Enable later if needed |
109
+
110
+ ### Directories to Skip
111
+
112
+ - `.obsidian/` — Obsidian config
113
+ - `.smart-env/` — Smart Connections plugin data
114
+ - `Attachments/`, `Assets/`, `Media/` — binary files
115
+ - `Templates/` — often just scaffolding, not real entities (evaluate per vault)
116
+ - `Daily Notes/` — usually ephemeral, not entity-worthy
117
+ - `Clippings/` — web clippings, low signal
118
+
119
+ ### Handling Nested Project Content
120
+
121
+ When a `Projects/` directory has subfolders with multiple files per project, ALL files get classified as the mapping's entity type. Sub-documents (meeting agendas, SOWs, concepts) become project entities.
122
+
123
+ **Options:**
124
+ 1. **Accept and review** — the `studiograph review` step lets users fix misclassifications before committing
125
+ 2. **Use `unstructured` mode** — the LLM can infer the correct entity type from content (slower, costs per file)
126
+ 3. **Co-locate sub-files** — if sub-documents reference their parent project in frontmatter (e.g., `related_projects: ["[[My Project]]"]`), add `co_locate: { parent_field: "related_projects", subfolder: "notes" }` to write them as sub-content
127
+
128
+ ## Step 4: Choose Extraction Mode Per Mapping
129
+
130
+ | Condition | Mode | Cost |
131
+ |-----------|------|------|
132
+ | Files have YAML frontmatter with useful fields | `frontmatter` | Free (no LLM) |
133
+ | Files are plain markdown, no frontmatter | `unstructured` | LLM call per file |
134
+ | Mix of both | `frontmatter` | Free — files without frontmatter are skipped |
135
+ | Small directory (<20 files) with no frontmatter | `unstructured` | Acceptable LLM cost |
136
+ | Large directory (100+) with no frontmatter | `frontmatter` or skip | `unstructured` would be expensive |
137
+
138
+ ## Step 5: Build Field Maps
139
+
140
+ For each `frontmatter` mapping, build a `field_map` from the sampled YAML fields.
141
+
142
+ **Rules:**
143
+ - Keys are the Obsidian frontmatter field names (as they appear in the YAML)
144
+ - Values are the Studiograph entity field names (from the entity type reference above)
145
+ - Only map fields that actually exist in the frontmatter — don't invent mappings
146
+ - Wikilink fields (`"[[Entity Name]]"`) map naturally — the sync pipeline handles them
147
+ - Always include `tags` if the vault uses tags
148
+ - Use `id_from: "_filename"` and `content_from: "_body"` for all Obsidian mappings
149
+
150
+ **Common Obsidian frontmatter fields and their typical targets:**
151
+
152
+ | Obsidian field | Target field | Notes |
153
+ |----------------|-------------|-------|
154
+ | `title` / `name` | `name` or `title` | Entity display name |
155
+ | `status` | `status` | Map values with `status_map` if conventions differ |
156
+ | `client` | `client` | Often a wikilink |
157
+ | `date` | `date` | ISO date |
158
+ | `start_date` / `end_date` | `start_date` / `end_date` | Date range |
159
+ | `tags` | `tags` | Array of strings |
160
+ | `email` | `email` | |
161
+ | `role` / `title` (on person) | `role` | Obsidian often uses `title` for role |
162
+ | `related_people` | `contacts` or `attendees` | Usually wikilink array |
163
+ | `related_organizations` | `related_organizations` | Usually wikilink array |
164
+ | `related_projects` | `related_projects` | Usually wikilink array |
165
+ | `related_areas` | `related_areas` | Usually wikilink array |
166
+ | `url` / `website` | `website_url` or `link` | |
167
+ | `sectors` | `sectors` | Industry/domain tags |
168
+ | `practice_areas` | `practice_areas` | Service line references |
169
+ | `year` | `year` | For case studies |
170
+
171
+ ## Step 6: Determine Target Repos
172
+
173
+ Check which repos exist in the workspace (`ls` the workspace directory or use `graph_list_repos`). Map entity types to existing repos:
174
+
175
+ | Entity types | Typical repo |
176
+ |-------------|-------------|
177
+ | project, case-study, deliverable, brief, artifact | `projects` |
178
+ | person, role | `people` |
179
+ | client, vendor | `clients` |
180
+ | meeting | `meetings` |
181
+ | process, standard, template, reference, technique, practice-area | A shared-resource repo if one exists, otherwise `projects` |
182
+ | deal, proposal, contract, financial-plan | A function repo if one exists, otherwise `projects` |
183
+
184
+ If a needed repo doesn't exist, the sync pipeline auto-creates it on `sync_apply`.
185
+
186
+ ## Step 7: Write the Config
187
+
188
+ Generate `.studiograph/sources/obsidian.json` with all mappings. Present it to the user for review before writing.
189
+
190
+ **Template:**
191
+ ```json
192
+ {
193
+ "name": "obsidian-<vault-name>",
194
+ "connector": "obsidian-<vault-name>",
195
+ "enabled": true,
196
+ "added_at": "<ISO timestamp>",
197
+ "entity_mappings": [
198
+ {
199
+ "source_type": "<descriptive-name>",
200
+ "entity_type": "<studiograph-type>",
201
+ "target_repo": "<repo-name>",
202
+ "extraction_mode": "frontmatter",
203
+ "enabled": true,
204
+ "directory_patterns": ["<VaultDir>/"],
205
+ "list_tool": "list_vault_files",
206
+ "read_tool": "get_vault_file",
207
+ "field_map": { ... },
208
+ "id_from": "_filename",
209
+ "content_from": "_body"
210
+ }
211
+ ]
212
+ }
213
+ ```
214
+
215
+ **Checklist before writing:**
216
+ - [ ] Every content directory has a mapping (or a reason to skip)
217
+ - [ ] `directory_patterns` use trailing slash and match the vault's exact directory names (case-sensitive)
218
+ - [ ] `list_tool` and `read_tool` are set to `"list_vault_files"` and `"get_vault_file"` for Obsidian
219
+ - [ ] Field maps only reference frontmatter fields that actually exist
220
+ - [ ] Directories with no frontmatter use `unstructured` mode or are skipped
221
+ - [ ] Subdirectories are handled (e.g., `Organizations/Clients/` not just `Organizations/`)
222
+ - [ ] `target_repo` values match existing repos or the user is aware new ones will be created
223
+ - [ ] Disabled mappings are marked `enabled: false` with a comment-like `source_type` explaining why
224
+
225
+ ## Multiple Vaults
226
+
227
+ Each vault needs its own connector and source config. The MCP Tools binary is vault-specific — it connects to the Local REST API instance running in its vault.
228
+
229
+ 1. Install both plugins (Local REST API + MCP Tools) in each vault
230
+ 2. Create a connector per vault at `~/.studiograph/connectors/obsidian-<name>.json`, each pointing to that vault's `mcp-server` binary and API key
231
+ 3. Create a source config per vault at `.studiograph/sources/obsidian-<name>.json` with directory patterns matching that vault's structure
232
+ 4. Run `studiograph sync` (no arguments) to sync all enabled sources in one pass
233
+
234
+ **Port conflict:** Each vault's Local REST API runs on port 27124 by default. Only one vault can use a given port at a time. If both vaults need to be open simultaneously, change the port in one vault's Local REST API settings.
235
+
236
+ See `docs/obsidian-setup.md` for full connector setup and troubleshooting.
237
+
238
+ ## After Setup
239
+
240
+ 1. **Dry run:** `sync_run({ sources: ["obsidian"], dryRun: true })` — verify extraction counts look right
241
+ 2. **Full run:** `sync_run({ sources: ["obsidian"] })` — extract to staging
242
+ 3. **Review:** `sync_status` then `studiograph review` — spot-check entity types and field values
243
+ 4. **Apply:** `sync_apply` — write entity files
244
+ 5. **Commit:** `studiograph commit` — git commit across repos
245
+
246
+ See the `sync-setup` skill for the full sync workflow and the `sync-configuration` skill for field map syntax details.
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Skill Loader — prompt skills for the Studiograph agent.
3
+ *
4
+ * Skills are markdown files with YAML frontmatter that inject domain-specific
5
+ * knowledge into the agent's system prompt. Two formats supported:
6
+ *
7
+ * Directory skill:
8
+ * skills/create-deck/
9
+ * skill.md ← required entrypoint
10
+ * slide-types.md ← optional sub-files
11
+ *
12
+ * Single-file skill:
13
+ * skills/quick-tip.md
14
+ *
15
+ * Frontmatter fields:
16
+ * name: kebab-case identifier (defaults to directory/filename)
17
+ * description: shown in skill index so agent knows when to load it
18
+ * loading: "on-demand" (default) | "eager" (inlined in system prompt)
19
+ */
20
+ export interface SkillMeta {
21
+ name: string;
22
+ description: string;
23
+ loading: 'eager' | 'on-demand';
24
+ dir: string;
25
+ entrypoint: string;
26
+ isDirectory: boolean;
27
+ }
28
+ /**
29
+ * Scan one or more skill directories and return a de-duplicated skill index.
30
+ * Directories are processed in order — first occurrence of a name wins,
31
+ * so workspace-level skills override app-bundled skills.
32
+ */
33
+ export declare function loadSkillIndex(skillsDirs: string[]): SkillMeta[];
34
+ /**
35
+ * Load the full content of a skill's entrypoint or a named sub-file.
36
+ * Sanitises the file parameter to prevent path traversal.
37
+ */
38
+ export declare function loadSkillContent(meta: SkillMeta, file?: string): string;
39
+ /**
40
+ * Build the skill index section to append to the system prompt.
41
+ * Eager skills are inlined separately; this lists only on-demand skills.
42
+ */
43
+ export declare function buildSkillIndexPrompt(skills: SkillMeta[]): string;
44
+ /**
45
+ * Return the full content of all eager skills, to be injected directly
46
+ * into the system prompt at startup.
47
+ */
48
+ export declare function loadEagerSkills(skills: SkillMeta[]): string;
@@ -0,0 +1,166 @@
1
+ /**
2
+ * Skill Loader — prompt skills for the Studiograph agent.
3
+ *
4
+ * Skills are markdown files with YAML frontmatter that inject domain-specific
5
+ * knowledge into the agent's system prompt. Two formats supported:
6
+ *
7
+ * Directory skill:
8
+ * skills/create-deck/
9
+ * skill.md ← required entrypoint
10
+ * slide-types.md ← optional sub-files
11
+ *
12
+ * Single-file skill:
13
+ * skills/quick-tip.md
14
+ *
15
+ * Frontmatter fields:
16
+ * name: kebab-case identifier (defaults to directory/filename)
17
+ * description: shown in skill index so agent knows when to load it
18
+ * loading: "on-demand" (default) | "eager" (inlined in system prompt)
19
+ */
20
+ import { existsSync, readdirSync, readFileSync } from 'fs';
21
+ import { join, extname, basename } from 'path';
22
+ /**
23
+ * Minimal YAML frontmatter parser — handles the subset used by skills.
24
+ */
25
+ function parseFrontmatter(content) {
26
+ const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);
27
+ if (!match) {
28
+ return { meta: {}, body: content };
29
+ }
30
+ const meta = {};
31
+ for (const line of match[1].split('\n')) {
32
+ const colon = line.indexOf(':');
33
+ if (colon === -1)
34
+ continue;
35
+ const key = line.slice(0, colon).trim();
36
+ const value = line.slice(colon + 1).trim();
37
+ if (key)
38
+ meta[key] = value;
39
+ }
40
+ return { meta, body: match[2] };
41
+ }
42
+ /**
43
+ * Scan one or more skill directories and return a de-duplicated skill index.
44
+ * Directories are processed in order — first occurrence of a name wins,
45
+ * so workspace-level skills override app-bundled skills.
46
+ */
47
+ export function loadSkillIndex(skillsDirs) {
48
+ const skills = [];
49
+ const seen = new Set();
50
+ for (const dir of skillsDirs) {
51
+ if (!existsSync(dir))
52
+ continue;
53
+ let entries;
54
+ try {
55
+ entries = readdirSync(dir);
56
+ }
57
+ catch {
58
+ continue;
59
+ }
60
+ for (const entry of entries) {
61
+ if (entry.startsWith('.'))
62
+ continue;
63
+ const entryPath = join(dir, entry);
64
+ // Directory skill: subdirectory containing skill.md
65
+ const skillMd = join(entryPath, 'skill.md');
66
+ if (existsSync(skillMd)) {
67
+ try {
68
+ const content = readFileSync(skillMd, 'utf-8');
69
+ const { meta } = parseFrontmatter(content);
70
+ const name = meta.name || entry;
71
+ if (!seen.has(name)) {
72
+ seen.add(name);
73
+ skills.push({
74
+ name,
75
+ description: meta.description || '',
76
+ loading: meta.loading === 'eager' ? 'eager' : 'on-demand',
77
+ dir: entryPath,
78
+ entrypoint: skillMd,
79
+ isDirectory: true,
80
+ });
81
+ }
82
+ }
83
+ catch {
84
+ // skip malformed skill
85
+ }
86
+ continue;
87
+ }
88
+ // Single-file skill: top-level .md file with valid frontmatter
89
+ if (extname(entry) === '.md') {
90
+ try {
91
+ const content = readFileSync(entryPath, 'utf-8');
92
+ const { meta } = parseFrontmatter(content);
93
+ // Must have at least name or description to be a skill
94
+ if (!meta.name && !meta.description)
95
+ continue;
96
+ const name = meta.name || basename(entry, '.md');
97
+ if (!seen.has(name)) {
98
+ seen.add(name);
99
+ skills.push({
100
+ name,
101
+ description: meta.description || '',
102
+ loading: meta.loading === 'eager' ? 'eager' : 'on-demand',
103
+ dir,
104
+ entrypoint: entryPath,
105
+ isDirectory: false,
106
+ });
107
+ }
108
+ }
109
+ catch {
110
+ // skip
111
+ }
112
+ }
113
+ }
114
+ }
115
+ return skills;
116
+ }
117
+ /**
118
+ * Load the full content of a skill's entrypoint or a named sub-file.
119
+ * Sanitises the file parameter to prevent path traversal.
120
+ */
121
+ export function loadSkillContent(meta, file) {
122
+ if (file) {
123
+ if (!meta.isDirectory) {
124
+ return `Error: skill "${meta.name}" is a single-file skill with no sub-files.`;
125
+ }
126
+ // Allow only simple filenames — no slashes, no dots at start
127
+ const safeName = file.replace(/[^a-zA-Z0-9._-]/g, '');
128
+ if (!safeName || safeName.startsWith('.')) {
129
+ return `Error: invalid sub-file name "${file}".`;
130
+ }
131
+ const filePath = join(meta.dir, safeName);
132
+ if (!existsSync(filePath)) {
133
+ return `Error: sub-file "${safeName}" not found in skill "${meta.name}".`;
134
+ }
135
+ return readFileSync(filePath, 'utf-8');
136
+ }
137
+ return readFileSync(meta.entrypoint, 'utf-8');
138
+ }
139
+ /**
140
+ * Build the skill index section to append to the system prompt.
141
+ * Eager skills are inlined separately; this lists only on-demand skills.
142
+ */
143
+ export function buildSkillIndexPrompt(skills) {
144
+ const onDemand = skills.filter(s => s.loading === 'on-demand');
145
+ if (onDemand.length === 0)
146
+ return '';
147
+ const lines = [
148
+ '## Available Skills',
149
+ '',
150
+ 'Call `load_skill` to load domain-specific guidance when it is relevant to the current task.',
151
+ '',
152
+ ...onDemand.map(s => `- **${s.name}** — ${s.description}`),
153
+ ];
154
+ return lines.join('\n');
155
+ }
156
+ /**
157
+ * Return the full content of all eager skills, to be injected directly
158
+ * into the system prompt at startup.
159
+ */
160
+ export function loadEagerSkills(skills) {
161
+ return skills
162
+ .filter(s => s.loading === 'eager')
163
+ .map(s => loadSkillContent(s))
164
+ .join('\n\n---\n\n');
165
+ }
166
+ //# sourceMappingURL=skill-loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-loader.js","sourceRoot":"","sources":["../../../src/agent/skills/skill-loader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,144 @@
1
+ ---
2
+ name: sync-configuration
3
+ description: Reference for building sync source configs — field maps, transforms, co-location, extraction modes
4
+ loading: eager
5
+ ---
6
+
7
+ # Sync Configuration Reference
8
+
9
+ Use this reference when helping users build or modify source configs at `.studiograph/sources/<name>.json`.
10
+
11
+ ## Source Config Structure
12
+
13
+ ```json
14
+ {
15
+ "name": "linear",
16
+ "connector": "linear",
17
+ "enabled": true,
18
+ "added_at": "2026-02-15T10:00:00.000Z",
19
+ "entity_mappings": [ ... ],
20
+ "field_priority": { "email": "pipedrive" }
21
+ }
22
+ ```
23
+
24
+ - `connector` references a connector in `~/.studiograph/connectors/`
25
+ - `field_priority` — optional per-field merge priority when multiple sources write the same entity
26
+ - `entity_mappings` — array of mappings, each maps one external entity type to a Studiograph type
27
+
28
+ ## Entity Mapping Fields
29
+
30
+ | Field | Required | Description |
31
+ |-------|----------|-------------|
32
+ | `source_type` | Yes | What the source calls this type (e.g. `"deal"`, `"issue"`) |
33
+ | `entity_type` | Yes | Studiograph type (e.g. `"project"`, `"task"`, `"meeting"`) |
34
+ | `target_repo` | Yes | Repo to write to (e.g. `"projects"`) |
35
+ | `extraction_mode` | Yes | `"structured"`, `"frontmatter"`, or `"unstructured"` |
36
+ | `id_from` | No | Source field for entity_id (default `"title"`, or `"_filename"` for frontmatter mode) |
37
+ | `content_from` | No | Source field for markdown body (or `"_body"` for frontmatter mode) |
38
+ | `field_map` | No | Maps source fields to entity fields (see Field Map Syntax) |
39
+ | `status_map` | No | Maps source status values to entity statuses (case-insensitive) |
40
+ | `fetch_details` | No | Fetch full record per listed ID (for sparse list APIs) |
41
+ | `list_limit` | No | Override limit param for list tool |
42
+ | `list_params` | No | Extra params for list tool. Pass array for multiple list calls merged |
43
+ | `directory_patterns` | No | Directory patterns for file sources (frontmatter/unstructured) |
44
+ | `write_mode` | No | `"entity"` (default, `id/main.md` folder) or `"collection"` (flat `id.md` file) |
45
+ | `co_locate` | No | Co-locate as sub-content inside parent entity folder |
46
+ | `list_tool` | No | Explicit MCP tool name for listing records (bypasses heuristic matching) |
47
+ | `read_tool` | No | Explicit MCP tool name for reading records (bypasses heuristic matching) |
48
+
49
+ ## Extraction Modes
50
+
51
+ - **`structured`** — API JSON → field_map → frontmatter. No LLM. For Pipedrive, Linear, Granola, Asana.
52
+ - **`frontmatter`** — Markdown files with YAML → field_map. No LLM. For Obsidian. Supports `_filename` and `_body` sentinels.
53
+ - **`unstructured`** — Freeform text → LLM extraction. Slow, expensive. Last resort when no structured metadata exists.
54
+
55
+ ## Field Map Syntax
56
+
57
+ Keys are source field paths, values are entity field names or FieldTransform objects.
58
+
59
+ **Simple string:** `"title": "name"` — direct copy
60
+ **Dot-notation:** `"org_id.name": "organization"` — nested access
61
+ **Array index:** `"email[0].value": "email"` — specific array element
62
+ **FieldTransform object:**
63
+ ```json
64
+ { "org_name": { "field": "client", "transform": "wikilink" } }
65
+ ```
66
+ FieldTransform fields: `field` (target name), `transform` (transform type), `subfield` (optional — extract nested path from each array element before transforming)
67
+
68
+ ### Subfield example
69
+ Source returns: `[{ "details": { "person": { "name": "Alice" } } }, ...]`
70
+ Config: `{ "field": "attendees", "transform": "wikilink-array", "subfield": "details.person.name" }`
71
+ Result: `["[[alice]]", ...]`
72
+
73
+ ## Transforms
74
+
75
+ | Transform | Input | Output |
76
+ |-----------|-------|--------|
77
+ | `direct` | `"hello"` | `"hello"` (passthrough) |
78
+ | `kebab-case` | `"Acme Corp"` | `"acme-corp"` |
79
+ | `date-iso` | `"2026-02-20T14:30:00Z"` | `"2026-02-20"` |
80
+ | `wikilink` | `"Acme Corp"` | `"[[acme-corp]]"` |
81
+ | `wikilink-array` | `["Alice", "Bob"]` | `["[[alice]]", "[[bob]]"]` |
82
+
83
+ ## Co-Location
84
+
85
+ Writes entities as sub-content files inside a parent entity folder instead of standalone entity folders.
86
+
87
+ **Config:** Add `co_locate` to a mapping:
88
+ ```json
89
+ {
90
+ "co_locate": { "parent_field": "project", "subfolder": "tasks" },
91
+ "field_map": { "project": { "field": "project", "transform": "wikilink" } }
92
+ }
93
+ ```
94
+
95
+ **Result path:** `{target_repo}/{parent_id}/{subfolder}/{entity_id}.md`
96
+ Example: `projects/cooper-hewitt-brand-refresh/tasks/sch-101.md`
97
+
98
+ **Rules:**
99
+ - `parent_field` must be a wikilink target in `field_map` so the reference is available
100
+ - Entities without a parent value are **skipped** (not an error)
101
+ - Co-located files are sub-content, not graph entities (no `entity_id/main.md` folder)
102
+ - `target_repo` should be the repo where the **parent** entities live
103
+
104
+ ## Write Mode
105
+
106
+ Controls how entities are written to disk:
107
+
108
+ - **`entity`** (default) — `repo/entity-id/main.md` folder. Fully indexed, supports sub-content. Use for core types (projects, clients, people).
109
+ - **`collection`** — `repo/entity-id.md` flat file. Simpler, no folder overhead. Use for high-volume types (meetings, notes, references).
110
+
111
+ ## Explicit MCP Tool Selection
112
+
113
+ For file-based sources (frontmatter, unstructured), add `list_tool` and `read_tool` when the connector's tool names don't follow predictable patterns:
114
+ ```json
115
+ { "list_tool": "list_vault_files", "read_tool": "get_vault_file" }
116
+ ```
117
+ When omitted, the pipeline uses heuristic matching (looking for tools containing "list", "read", "get", etc.).
118
+
119
+ ## Sentinels (frontmatter mode only)
120
+
121
+ - `_filename` in `id_from` — entity ID from the file's name (slugified, `.md` stripped)
122
+ - `_body` in `content_from` — markdown body from file content below the `---` frontmatter fence
123
+
124
+ ## Built-in Definitions
125
+
126
+ | Source | Mappings | Notes |
127
+ |--------|----------|-------|
128
+ | **Pipedrive** | deal→project, person→person, org→client | Ready to sync |
129
+ | **Granola** | document→meeting | `fetch_details: true`, `list_limit: 1000` |
130
+ | **Linear** | issue→task (co-located under projects), project→project | Issues use `co_locate` |
131
+ | **Asana** | task→task, project→project | Needs `list_params: { workspace: "GID" }` |
132
+ | **Obsidian** | Example mappings only (`.template` file) | Needs customization per vault |
133
+
134
+ ## Configuration Patterns
135
+
136
+ **Adding co-location to any mapping:** Add `co_locate: { parent_field, subfolder }` and ensure `parent_field` is a wikilink target in `field_map`.
137
+
138
+ **Multiple list calls:** Set `list_params` as an array: `[{ workspace: "123", archived: false }, { workspace: "123", archived: true }]`
139
+
140
+ **Custom source (no built-in):** Create a source config with `entity_mappings` from scratch. Choose extraction_mode, define field_map targeting entity schema fields, set target_repo.
141
+
142
+ **Overriding built-in defaults:** Pass custom `entity_mappings` to `sync_setup_source` — they replace the defaults entirely. Or edit `.studiograph/sources/<name>.json` directly.
143
+
144
+ **Status maps:** Map source status strings (case-insensitive) to Studiograph enums. Common patterns: Pipedrive (`won→completed, lost→archived, open→active`), Linear issues (`Backlog→open, In Progress→in-progress, Done→done, Canceled→cancelled`).