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
@@ -9,11 +9,30 @@ import { getModel } from '@mariozechner/pi-ai';
9
9
  import { Workspace } from '../../core/workspace.js';
10
10
  import { WorkspaceManager } from '../../core/workspace-manager.js';
11
11
  import { MemoryService } from '../../services/memory-service.js';
12
+ import { SchemaRegistry } from '../../core/schema-registry.js';
12
13
  import { createGraphTools } from '../../agent/tools/graph-tools.js';
14
+ import { createSyncTools } from '../../agent/tools/sync-tools.js';
15
+ import { createFsTools } from '../../agent/tools/fs-tools.js';
16
+ import { loadSkillIndex, buildSkillIndexPrompt, loadEagerSkills } from '../../agent/skill-loader.js';
17
+ import { createLoadSkillTool } from '../../agent/tools/load-skill.js';
18
+ import { loadCustomTools } from '../../agent/tools/tool-loader.js';
19
+ import { ConnectorManager } from '../../mcp/connector-manager.js';
20
+ import { buildConnectorRegistry, createConnectorTools } from '../../agent/tools/connector-tools.js';
13
21
  import { intro, select, text, outro, spinner } from '@clack/prompts';
14
22
  import { runPreflight } from '../../utils/preflight.js';
15
- import { loadUserConfig } from '../../core/user-config.js';
23
+ import { loadUserConfig, saveUserConfig, getAnthropicCredential } from '../../core/user-config.js';
24
+ import { notifyUpdateAvailable } from '../../utils/version-checker.js';
25
+ import { join, dirname } from 'path';
26
+ import { homedir } from 'os';
27
+ import { fileURLToPath } from 'url';
28
+ import { readFileSync, existsSync } from 'fs';
29
+ import { execSync } from 'child_process';
16
30
  import * as readline from 'readline';
31
+ import { Markdown } from '@mariozechner/pi-tui';
32
+ import { info, dimText, header, warn, toolRef, renderUserPrompt } from '../colors.js';
33
+ import { box } from 'consola/utils';
34
+ import ora from 'ora';
35
+ import { markdownTheme } from '../theme.js';
17
36
  // Model provider configurations
18
37
  const MODEL_PROVIDERS = {
19
38
  anthropic: {
@@ -91,14 +110,14 @@ export const startCommand = new Command('start')
91
110
  return;
92
111
  }
93
112
  // Load workspace config and user config
94
- const config = workspace.loadConfig();
95
- const repos = workspace.getRepos();
113
+ const config = await workspace.loadConfig();
114
+ const repos = await workspace.getRepos();
96
115
  const userConfig = loadUserConfig();
97
116
  // Check if model is configured — user config takes precedence over workspace config
98
117
  let modelProvider = userConfig.model_provider || config.model_provider;
99
118
  let modelId = userConfig.model_id || config.model_id;
100
- // API key: user config → workspace.json legacy field → env var (resolved below)
101
- let apiKey = userConfig.api_key || config.api_key || config.anthropic_api_key;
119
+ // API key: user config (oauth token or api key) → workspace.json → env var (resolved below)
120
+ let apiKey = getAnthropicCredential(userConfig) || config.api_key;
102
121
  if (!modelProvider || !modelId) {
103
122
  // First-time setup
104
123
  intro('🎨 Studiograph Setup');
@@ -143,6 +162,27 @@ export const startCommand = new Command('start')
143
162
  console.log(`✓ Using ${provider.envVar} from environment\n`);
144
163
  apiKey = envKey;
145
164
  }
165
+ else if (modelProvider === 'anthropic') {
166
+ // Anthropic requires API key from console.anthropic.com
167
+ const keyInput = await text({
168
+ message: 'Enter your Anthropic API key:',
169
+ placeholder: 'sk-ant-api03-...',
170
+ validate: (value) => {
171
+ if (!value || value.length < 10)
172
+ return 'API key seems too short';
173
+ },
174
+ });
175
+ if (typeof keyInput !== 'string') {
176
+ outro('Cancelled');
177
+ process.exit(0);
178
+ }
179
+ apiKey = keyInput;
180
+ const s = spinner();
181
+ s.start('Saving configuration...');
182
+ workspace.setModelConfig(modelProvider, modelId, apiKey);
183
+ s.stop('Configuration saved ✓');
184
+ outro(`Model configured: ${provider.name} - ${modelId}\n`);
185
+ }
146
186
  else {
147
187
  const keyInput = await text({
148
188
  message: `Enter your ${provider.name} API key:`,
@@ -157,14 +197,21 @@ export const startCommand = new Command('start')
157
197
  process.exit(0);
158
198
  }
159
199
  apiKey = keyInput;
200
+ const s = spinner();
201
+ s.start('Saving configuration...');
202
+ workspace.setModelConfig(modelProvider, modelId, apiKey);
203
+ s.stop('Configuration saved ✓');
204
+ outro(`Model configured: ${provider.name} - ${modelId}\n`);
160
205
  }
161
206
  }
162
- // Save configuration
163
- const s = spinner();
164
- s.start('Saving configuration...');
165
- workspace.setModelConfig(modelProvider, modelId, apiKey);
166
- s.stop('Configuration saved ✓');
167
- outro(`Model configured: ${provider.name} - ${modelId}\n`);
207
+ else {
208
+ // Save configuration (non-Anthropic or key already available)
209
+ const s = spinner();
210
+ s.start('Saving configuration...');
211
+ workspace.setModelConfig(modelProvider, modelId, apiKey);
212
+ s.stop('Configuration saved ✓');
213
+ outro(`Model configured: ${provider.name} - ${modelId}\n`);
214
+ }
168
215
  }
169
216
  // Validate we have all required config
170
217
  if (!modelProvider || !modelId) {
@@ -185,58 +232,162 @@ export const startCommand = new Command('start')
185
232
  }
186
233
  }
187
234
  // Initialize memory service
188
- const memoryService = new MemoryService(process.cwd());
235
+ const memoryService = new MemoryService();
189
236
  const recentMemory = memoryService.loadRecentMemory();
190
- // Create system prompt with memory context
191
- const systemPrompt = createSystemPrompt(config.team_name, repos, recentMemory);
237
+ // Initialize workspace manager
238
+ const gitUser = { id: 'cli-user', name: config.team_name, email: 'noreply@studiograph.local' };
239
+ const cliUser = { id: 'cli-user', name: config.team_name, email: 'noreply@studiograph.local', role: 'admin' };
240
+ const workspaceManager = new WorkspaceManager(process.cwd(), config, gitUser, cliUser);
241
+ // Build skill index from user > workspace > bundled directories
242
+ const startFileDir = dirname(fileURLToPath(import.meta.url));
243
+ const bundledSkillsDir = join(startFileDir, '../../agent/skills');
244
+ const userSkillsDir = join(homedir(), '.studiograph', 'skills');
245
+ const workspaceSkillsDir = join(process.cwd(), '.studiograph', 'skills');
246
+ const skillIndex = loadSkillIndex([userSkillsDir, workspaceSkillsDir, bundledSkillsDir]);
247
+ // Load ALL tools: graph + sync + skill loader
248
+ const tools = [
249
+ ...createGraphTools(workspaceManager, gitUser, process.cwd()),
250
+ ...createSyncTools(process.cwd(), config),
251
+ ...createFsTools(),
252
+ createLoadSkillTool(skillIndex),
253
+ ];
254
+ // Create system prompt with tool reference, skills, and memory
255
+ const systemPrompt = createSystemPrompt(config.team_name, repos, recentMemory, config.schema_extensions, tools, skillIndex);
192
256
  // Initialize agent
193
257
  const agent = new Agent({
194
258
  getApiKey: () => apiKey,
195
259
  });
196
- // Set model and system prompt
197
260
  const model = getModel(modelProvider, modelId);
198
261
  agent.setModel(model);
199
262
  agent.setSystemPrompt(systemPrompt);
200
- // Load graph tools
201
- const gitUser = { id: 'cli-user', name: config.team_name, email: 'noreply@studiograph.local' };
202
- const cliUser = { id: 'cli-user', name: config.team_name, email: 'noreply@studiograph.local', role: 'admin' };
203
- const workspaceManager = new WorkspaceManager(process.cwd(), config, gitUser, cliUser);
204
- const tools = createGraphTools(workspaceManager, gitUser);
205
263
  agent.setTools(tools);
206
- console.log('🎨 Studiograph agent started');
207
- console.log(`📁 Workspace: ${config.team_name}`);
208
- console.log(`🤖 Model: ${modelProvider}/${modelId}`);
209
- console.log(`💾 Memory loaded: ${memoryService.listMemoryFiles().length} days`);
210
- console.log('\nType your message and press Enter. Type "exit" to quit.\n');
264
+ // Load custom tools and connector tools before starting the chat loop
265
+ const userToolsDir = join(homedir(), '.studiograph', 'tools');
266
+ const workspaceToolsDir = join(process.cwd(), '.studiograph', 'tools');
267
+ let connectorSummary = '';
268
+ try {
269
+ const [customTools, connectorTools] = await Promise.all([
270
+ loadCustomTools([userToolsDir, workspaceToolsDir], { workspaceManager, gitUser }),
271
+ ConnectorManager.buildTools(ConnectorManager.loadConfigs()),
272
+ ]);
273
+ const extra = [...customTools];
274
+ // Connector tools go through a proxy registry (connector_list + connector_call)
275
+ // instead of registering hundreds of individual tools
276
+ if (connectorTools.length > 0) {
277
+ const registry = buildConnectorRegistry(connectorTools);
278
+ extra.push(...createConnectorTools(registry));
279
+ connectorSummary = [...registry.connectors.keys()].join(', ');
280
+ }
281
+ if (extra.length > 0) {
282
+ const existing = agent.state.tools;
283
+ agent.setTools([...existing, ...extra]);
284
+ }
285
+ }
286
+ catch (err) {
287
+ // Non-fatal — agent starts with core tools only
288
+ }
289
+ // Search index: first-run setup and staleness check
290
+ await initSearchIndex(workspaceManager, config.repos, process.cwd());
291
+ const totalTools = agent.state.tools.length;
292
+ const memoryDays = memoryService.listMemoryFiles().length;
293
+ console.log(box([
294
+ `Model: ${header(modelProvider + '/' + modelId)}`,
295
+ `Tools: ${totalTools} registered${connectorSummary ? ` (${connectorSummary})` : ''}`,
296
+ `Memory: ${memoryDays} day${memoryDays === 1 ? '' : 's'} loaded`,
297
+ ].join('\n'), {
298
+ title: `Studiograph — ${config.team_name}`,
299
+ style: { borderColor: 'cyan', padding: 1 },
300
+ }));
301
+ // Check for updates (non-blocking, cached)
302
+ await notifyUpdateAvailable();
303
+ console.log(dimText('Type your message and press Enter. Type "exit" to quit.\n'));
211
304
  // Track conversation for memory
212
305
  const conversationLog = [];
213
- // Track first delta to fix leading spacing
214
- let isFirstDelta = false;
215
- // Track tool sequence for spacing
306
+ // Track whether any non-whitespace content has been written this turn
307
+ let hasWrittenOutput = false;
308
+ // Track whether we're in a consecutive tool sequence (for spacing)
216
309
  let inToolSequence = false;
310
+ // Markdown rendering state — pi-tui Markdown component for styled output
311
+ let mdRenderer = new Markdown('', 1, 0, markdownTheme);
312
+ let fullText = '';
313
+ let displayedLineCount = 0;
314
+ let lastRenderedText = '';
315
+ // Collapse consecutive blank lines in rendered output
316
+ let lastLineWasBlank = false;
317
+ // Spinner state for tool execution
318
+ let toolSpinner = null;
217
319
  // Subscribe to agent events
218
320
  agent.subscribe((event) => {
219
321
  if (event.type === 'message_start') {
220
- isFirstDelta = true;
322
+ // Reset per-turn output tracker but preserve inToolSequence across
323
+ // message boundaries so text following a tool turn gets a blank line.
324
+ hasWrittenOutput = false;
325
+ // Reset markdown rendering state for new message
326
+ mdRenderer = new Markdown('', 1, 0, markdownTheme);
327
+ fullText = '';
328
+ displayedLineCount = 0;
329
+ lastRenderedText = '';
330
+ lastLineWasBlank = false;
221
331
  }
222
332
  else if (event.type === 'message_update') {
223
333
  const msgEvent = event.assistantMessageEvent;
224
- if (msgEvent.type === 'text_delta') {
334
+ if (msgEvent && msgEvent.type === 'text_delta') {
225
335
  let output = msgEvent.delta;
226
- // Strip both leading newlines from first delta (we already moved cursor)
227
- if (isFirstDelta) {
228
- output = output.replace(/^\n\n/, '');
229
- isFirstDelta = false;
336
+ // Suppress whitespace-only deltas until real content appears.
337
+ if (!hasWrittenOutput) {
338
+ output = output.replace(/^\s+/, '');
339
+ if (!output)
340
+ return;
230
341
  }
231
- // Add blank line before text if we just finished a tool sequence
342
+ // Add breathing room before text that follows a tool sequence
232
343
  if (inToolSequence) {
233
344
  process.stdout.write('\n');
234
345
  inToolSequence = false;
346
+ lastLineWasBlank = true; // prevent double blank after tool gap
347
+ }
348
+ hasWrittenOutput = true;
349
+ // Accumulate text and render complete lines through Markdown
350
+ fullText += output;
351
+ const lastNewline = fullText.lastIndexOf('\n');
352
+ if (lastNewline >= 0) {
353
+ const completeText = fullText.substring(0, lastNewline + 1);
354
+ if (completeText !== lastRenderedText) {
355
+ // Defer rendering while inside an unclosed code fence — pi-tui
356
+ // renders code blocks with borders, which shifts line indices
357
+ // and causes content to be skipped by the incremental display.
358
+ const fenceCount = (completeText.match(/^```/gm) || []).length;
359
+ if (fenceCount % 2 === 1)
360
+ return;
361
+ mdRenderer.setText(completeText);
362
+ const width = process.stdout.columns || 80;
363
+ const allLines = mdRenderer.render(width);
364
+ for (let i = displayedLineCount; i < allLines.length; i++) {
365
+ const isBlank = allLines[i].trim() === '';
366
+ if (isBlank && lastLineWasBlank)
367
+ continue;
368
+ lastLineWasBlank = isBlank;
369
+ process.stdout.write(allLines[i] + '\n');
370
+ }
371
+ displayedLineCount = allLines.length;
372
+ lastRenderedText = completeText;
373
+ }
235
374
  }
236
- process.stdout.write(output);
237
375
  }
238
376
  }
239
377
  else if (event.type === 'message_end') {
378
+ // Render any remaining text (trailing incomplete line)
379
+ if (fullText && fullText !== lastRenderedText) {
380
+ mdRenderer.setText(fullText);
381
+ const width = process.stdout.columns || 80;
382
+ const allLines = mdRenderer.render(width);
383
+ for (let i = displayedLineCount; i < allLines.length; i++) {
384
+ const isBlank = allLines[i].trim() === '';
385
+ if (isBlank && lastLineWasBlank)
386
+ continue;
387
+ lastLineWasBlank = isBlank;
388
+ process.stdout.write(allLines[i] + '\n');
389
+ }
390
+ }
240
391
  // Message complete - extract text for logging
241
392
  const msg = event.message;
242
393
  if (msg.role === 'assistant') {
@@ -246,29 +397,43 @@ export const startCommand = new Command('start')
246
397
  .join('');
247
398
  if (textContent) {
248
399
  conversationLog.push(`Assistant: ${textContent}`);
400
+ // Reset all formatting, then one blank line before next prompt
401
+ process.stdout.write('\x1b[0m\n');
402
+ inToolSequence = false;
249
403
  }
250
- // Add blank line after assistant response before next prompt
251
- process.stdout.write('\n\n');
252
404
  }
253
405
  }
254
406
  else if (event.type === 'tool_execution_start') {
255
- // Add blank line between consecutive tools (not before first)
256
407
  if (inToolSequence) {
257
- process.stdout.write('\n');
408
+ process.stderr.write('\n');
258
409
  }
259
410
  else {
260
411
  inToolSequence = true;
261
412
  }
262
- // Log tool execution
263
- process.stdout.write(`🔧 Using tool: ${event.toolName}\n`);
413
+ toolSpinner = ora({
414
+ text: info(`Using tool: ${toolRef(event.toolName)}`),
415
+ stream: process.stderr,
416
+ color: 'cyan',
417
+ }).start();
418
+ hasWrittenOutput = true;
419
+ }
420
+ else if (event.type === 'agent_end') {
421
+ // Agent completely done - fire terminal bell once
422
+ process.stdout.write('\x07');
264
423
  }
265
424
  else if (event.type === 'tool_execution_end') {
266
- // Log tool execution errors
267
- if (event.isError) {
268
- process.stdout.write(`⚠️ Tool error (${event.toolName}): Execution failed\n`);
269
- }
270
- else if (event.result?.details?.success === false) {
271
- process.stdout.write(`⚠️ Tool error (${event.toolName}): ${event.result.details.error || 'Unknown error'}\n`);
425
+ if (toolSpinner) {
426
+ const toolLabel = info(`Using tool: ${toolRef(event.toolName)}`);
427
+ if (event.isError) {
428
+ toolSpinner.fail(warn(`${toolRef(event.toolName)}: Execution failed`));
429
+ }
430
+ else if (event.result?.details?.success === false) {
431
+ toolSpinner.fail(warn(`${toolRef(event.toolName)}: ${event.result.details.error || 'Unknown error'}`));
432
+ }
433
+ else {
434
+ toolSpinner.stopAndPersist({ symbol: info('🔧'), text: toolLabel });
435
+ }
436
+ toolSpinner = null;
272
437
  }
273
438
  }
274
439
  });
@@ -278,9 +443,19 @@ export const startCommand = new Command('start')
278
443
  output: process.stdout,
279
444
  prompt: '> ',
280
445
  });
281
- rl.prompt();
282
- rl.on('line', async (input) => {
283
- const message = input.trim();
446
+ // ── Multiline paste handling ──────────────────────────────────────────────
447
+ // When text is pasted, readline fires 'line' for each \n in rapid
448
+ // succession. We collect lines that arrive within a short window and
449
+ // join them into a single message (like Claude Code).
450
+ const PASTE_DEBOUNCE_MS = 50;
451
+ let pasteBuffer = [];
452
+ let pasteTimer = null;
453
+ // ── Prompt serialization ──────────────────────────────────────────────────
454
+ // If the user pastes multiple chunks or types while the agent is busy,
455
+ // we queue messages and process them one at a time.
456
+ let agentBusy = false;
457
+ const messageQueue = [];
458
+ async function processMessage(message) {
284
459
  if (message.toLowerCase() === 'exit') {
285
460
  // Save session summary to memory
286
461
  if (conversationLog.length > 0) {
@@ -295,13 +470,27 @@ export const startCommand = new Command('start')
295
470
  return;
296
471
  }
297
472
  if (!message) {
473
+ process.stdout.write('\x1b[0m');
298
474
  rl.prompt();
299
475
  return;
300
476
  }
477
+ agentBusy = true;
301
478
  try {
302
479
  conversationLog.push(`User: ${message}`);
303
- // Move cursor to where agent response will appear (prevents double-jump)
304
- process.stdout.write('\n');
480
+ // Re-render user input with dark gray background (overwrites readline echo)
481
+ // Account for multi-line wrapping: calculate how many terminal rows
482
+ // the readline echo occupied, then move up and clear each one.
483
+ const cols = process.stdout.columns || 80;
484
+ const echoLen = 2 + message.length; // "> " prompt + message
485
+ const wrappedLines = Math.ceil(echoLen / cols) || 1;
486
+ process.stdout.write(`\x1b[${wrappedLines}A`); // move up N lines
487
+ for (let i = 0; i < wrappedLines; i++) {
488
+ process.stdout.write('\x1b[2K'); // clear current line
489
+ if (i < wrappedLines - 1)
490
+ process.stdout.write('\x1b[1B'); // move down
491
+ }
492
+ process.stdout.write('\x1b[G'); // move to column 1
493
+ process.stdout.write(renderUserPrompt(message) + '\n\n');
305
494
  // Send message to agent and wait for completion
306
495
  await agent.prompt(message);
307
496
  await agent.waitForIdle();
@@ -312,7 +501,46 @@ export const startCommand = new Command('start')
312
501
  console.error('Stack trace:', error.stack);
313
502
  }
314
503
  }
315
- rl.prompt();
504
+ agentBusy = false;
505
+ // Drain the queue
506
+ if (messageQueue.length > 0) {
507
+ const next = messageQueue.shift();
508
+ process.stdout.write('\x1b[0m');
509
+ rl.prompt();
510
+ // Small delay so the prompt renders before processing
511
+ setImmediate(() => processMessage(next));
512
+ }
513
+ else {
514
+ process.stdout.write('\x1b[0m');
515
+ rl.prompt();
516
+ }
517
+ }
518
+ function flushPasteBuffer() {
519
+ pasteTimer = null;
520
+ const message = pasteBuffer.join('\n').trim();
521
+ pasteBuffer = [];
522
+ if (!message) {
523
+ if (!agentBusy) {
524
+ process.stdout.write('\x1b[0m');
525
+ rl.prompt();
526
+ }
527
+ return;
528
+ }
529
+ if (agentBusy) {
530
+ messageQueue.push(message);
531
+ }
532
+ else {
533
+ processMessage(message);
534
+ }
535
+ }
536
+ rl.prompt();
537
+ rl.on('line', (input) => {
538
+ pasteBuffer.push(input);
539
+ // Reset the debounce timer — if another line arrives within
540
+ // PASTE_DEBOUNCE_MS it's part of the same paste operation
541
+ if (pasteTimer)
542
+ clearTimeout(pasteTimer);
543
+ pasteTimer = setTimeout(flushPasteBuffer, PASTE_DEBOUNCE_MS);
316
544
  });
317
545
  rl.on('close', () => {
318
546
  console.log('\n👋 Goodbye!');
@@ -320,62 +548,203 @@ export const startCommand = new Command('start')
320
548
  });
321
549
  });
322
550
  /**
323
- * Create system prompt with workspace and memory context
551
+ * Initialize the search index on startup.
552
+ *
553
+ * First run: prompts for Voyage AI key (optional), then runs full index.
554
+ * Subsequent runs: checks git log for commits since last index and re-indexes changed entities.
324
555
  */
325
- function createSystemPrompt(teamName, repos, recentMemory) {
326
- return `You are Studiograph, an AI agent that helps creative teams manage their knowledge graphs.
327
-
328
- # Workspace: ${teamName}
556
+ async function initSearchIndex(workspaceManager, repos, workspacePath) {
557
+ const userConfig = loadUserConfig();
558
+ const dbPath = userConfig.vector_db_path || join(homedir(), '.studiograph', 'vector');
559
+ const vectorService = workspaceManager.vectorService;
560
+ // First-run: no repos have been indexed yet
561
+ const isFirstRun = repos.every(r => !vectorService.getLastIndexedTime(r.name));
562
+ if (isFirstRun) {
563
+ // Prompt for Voyage AI key (optional)
564
+ if (!userConfig.voyage_api_key) {
565
+ const choice = await select({
566
+ message: 'Enable semantic search? (optional)',
567
+ options: [
568
+ { value: 'skip', label: 'Skip — use FTS keyword search', hint: 'No setup required' },
569
+ { value: 'voyage', label: 'Enable semantic search', hint: 'Voyage AI key required' },
570
+ ],
571
+ });
572
+ if (choice === 'voyage' && typeof choice === 'string') {
573
+ const keyInput = await text({
574
+ message: 'Enter your Voyage AI API key:',
575
+ placeholder: 'pa-...',
576
+ validate: (v) => (!v || v.length < 10 ? 'Key seems too short' : undefined),
577
+ });
578
+ if (typeof keyInput === 'string') {
579
+ userConfig.voyage_api_key = keyInput;
580
+ saveUserConfig(userConfig);
581
+ }
582
+ }
583
+ }
584
+ // Full index on first run
585
+ const s = spinner();
586
+ s.start('Indexing workspace for search...');
587
+ let total = 0;
588
+ for (const repo of repos) {
589
+ const repoPath = join(workspacePath, repo.path);
590
+ try {
591
+ const graph = workspaceManager['graphs'].get(repo.name);
592
+ if (!graph)
593
+ continue;
594
+ const entities = graph.list();
595
+ if (entities.length > 0) {
596
+ await vectorService.upsertMany(repo.name, entities);
597
+ total += entities.length;
598
+ }
599
+ vectorService.setLastIndexedTime(repo.name);
600
+ }
601
+ catch { /* non-fatal */ }
602
+ }
603
+ s.stop(`Search index ready (${total} entities)`);
604
+ return;
605
+ }
606
+ // Subsequent runs: check git log for changes since last index
607
+ const staleEntities = [];
608
+ for (const repo of repos) {
609
+ const lastIndexed = vectorService.getLastIndexedTime(repo.name);
610
+ if (!lastIndexed)
611
+ continue;
612
+ const repoPath = join(workspacePath, repo.path);
613
+ try {
614
+ const output = execSync(`git log --since="${lastIndexed}" --name-status --format="" -- "*.md"`, { cwd: repoPath, encoding: 'utf-8', stdio: 'pipe' }).trim();
615
+ if (!output)
616
+ continue;
617
+ for (const line of output.split('\n')) {
618
+ const match = line.match(/^([AMD])\s+(\w+)\/([^/]+)\.md$/);
619
+ if (!match)
620
+ continue;
621
+ const [, status, entityType, entityId] = match;
622
+ staleEntities.push({ repoName: repo.name, entityType, entityId, deleted: status === 'D' });
623
+ }
624
+ }
625
+ catch { /* non-fatal */ }
626
+ }
627
+ if (staleEntities.length === 0)
628
+ return;
629
+ const s = spinner();
630
+ s.start(`Updating search index (${staleEntities.length} change${staleEntities.length === 1 ? '' : 's'})...`);
631
+ for (const item of staleEntities) {
632
+ try {
633
+ if (item.deleted) {
634
+ await vectorService.delete(item.repoName, item.entityType, item.entityId);
635
+ }
636
+ else {
637
+ const graph = workspaceManager['graphs'].get(item.repoName);
638
+ if (!graph)
639
+ continue;
640
+ const entity = graph.get(item.entityType, item.entityId);
641
+ await vectorService.upsert(item.repoName, entity);
642
+ }
643
+ }
644
+ catch { /* entity may not exist — skip */ }
645
+ }
646
+ for (const repo of repos) {
647
+ vectorService.setLastIndexedTime(repo.name);
648
+ }
649
+ s.stop('Search index updated');
650
+ }
651
+ /**
652
+ * Create system prompt with workspace context, tool reference, skills, and memory.
653
+ */
654
+ function createSystemPrompt(teamName, repos, recentMemory, schemaExtensions, tools, skillIndex) {
655
+ // Load the authoritative system prompt from system.md
656
+ const __filename = fileURLToPath(import.meta.url);
657
+ const __dirname = dirname(__filename);
658
+ const promptPath = join(__dirname, '../../agent/prompts/system.md');
659
+ let base = existsSync(promptPath)
660
+ ? readFileSync(promptPath, 'utf-8')
661
+ : 'You are Studiograph, an AI agent that helps creative teams manage their knowledge graphs.';
662
+ // Auto-generate tool reference from registered tools, grouped by category
663
+ if (tools && tools.length > 0) {
664
+ const categories = {
665
+ 'Entity Management': [],
666
+ 'Data Sync & Source Config': [],
667
+ 'Enrichment': [],
668
+ 'Workspace Git': [],
669
+ 'External Connectors': [],
670
+ 'Skills': [],
671
+ 'Other': [],
672
+ };
673
+ for (const t of tools) {
674
+ if (t.description?.startsWith('DEPRECATED'))
675
+ continue;
676
+ const name = t.name ?? '';
677
+ if (name.startsWith('sync_'))
678
+ categories['Data Sync & Source Config'].push(t);
679
+ else if (name.startsWith('enrich_'))
680
+ categories['Enrichment'].push(t);
681
+ else if (name.startsWith('workspace_'))
682
+ categories['Workspace Git'].push(t);
683
+ else if (name.startsWith('connector_'))
684
+ categories['External Connectors'].push(t);
685
+ else if (name === 'load_skill')
686
+ categories['Skills'].push(t);
687
+ else if (['create_entity', 'get_entity', 'update_entity', 'delete_entity', 'search_entities', 'list_entities', 'get_related', 'get_backlinks', 'validate_wikilinks', 'get_workspace_summary', 'list_repos', 'query_dataset', 'append_rows', 'get_entity_schema', 'lint_entities'].includes(name))
688
+ categories['Entity Management'].push(t);
689
+ else
690
+ categories['Other'].push(t);
691
+ }
692
+ const toolRef = Object.entries(categories)
693
+ .filter(([, items]) => items.length > 0)
694
+ .map(([cat, items]) => `**${cat}:**\n${items.map((t) => `- \`${t.name}\` — ${t.description}`).join('\n')}`)
695
+ .join('\n\n');
696
+ base = base.replace('{{TOOL_REFERENCE}}', toolRef);
697
+ }
698
+ else {
699
+ base = base.replace('{{TOOL_REFERENCE}}', '');
700
+ }
701
+ const registry = new SchemaRegistry(schemaExtensions);
702
+ const schemaSection = registry.toPromptSection();
703
+ const workspaceSection = `## Workspace: ${teamName}
329
704
 
330
705
  You are working in a Studiograph workspace with the following repositories:
331
706
 
332
- ${repos.map(repo => `- ${repo.name} (${repo.type}): ${repo.description || 'No description'}`).join('\n')}
707
+ ${repos.map(repo => `- ${repo.name} (${repo.type}): ${repo.description || 'No description'}`).join('\n')}`;
708
+ const memorySection = recentMemory
709
+ ? `## Memory Context\n\n${recentMemory}`
710
+ : '';
711
+ const outputSection = `## Output Formatting
333
712
 
334
- # Memory Context
713
+ You are running in a CLI terminal. Keep responses **short and scannable**.
335
714
 
336
- ${recentMemory}
715
+ **Length rules:**
716
+ - Default to 2-4 sentences for simple answers
717
+ - Use bullet lists for 3+ items — never write items as prose paragraphs
718
+ - Limit tool result summaries to the most relevant 5-10 items
719
+ - Only use headings (##) when the response has 2+ distinct sections
720
+ - If the user asks a yes/no question, lead with the answer
337
721
 
338
- # Your Role
722
+ **Formatting rules:**
723
+ - Use **bold** for emphasis and \`inline code\` for commands, entity types, and field names
724
+ - Use fenced code blocks for multi-line code, YAML, or file contents
725
+ - Use > blockquotes sparingly for callouts
726
+ - NEVER use markdown tables (| pipes) — use bullet lists instead
727
+ - Do NOT repeat information the user already knows
339
728
 
340
- You help the team:
341
- - Create and manage entities in their knowledge graphs
342
- - Search and discover relationships between entities
343
- - Generate presentations and documentation
344
- - Track decisions and project history
345
-
346
- # Entity Types by Repository Type
347
-
348
- PROJECT repositories contain:
349
- - Projects, Clients, People, Meetings, Decisions, Artifacts
350
-
351
- FUNCTION repositories contain:
352
- - Function-specific entities (varies by team)
353
-
354
- SHARED_RESOURCE repositories contain:
355
- - Templates, Standards, Processes, Research, References
356
-
357
- # Guidelines
358
-
359
- - Be conversational and helpful
360
- - Ask clarifying questions when needed
361
- - Suggest related entities and connections
362
- - Track important decisions and context in memory
363
- - Always save significant insights to memory for future sessions
364
-
365
- # Output Formatting
366
-
367
- IMPORTANT: You are running in a CLI (command-line interface). Use plain text formatting:
368
- - NO markdown syntax: no bold (**text**), italic (*text*), code formatting (\`code\`), headings (# Heading), or tables
369
- - For section headers, use ALL CAPS on a single line with NO blank line after:
370
- Example:
371
- MANAGE YOUR KNOWLEDGE
372
- - Create entities
373
- - Search and discover
374
- - Use simple bullet points with single hyphens: "- item"
375
- - For punctuation, use single hyphens (-) not double hyphens (--)
376
- - Separate different sections with ONE blank line between them
377
- - Keep responses concise and conversational
378
-
379
- Start by greeting the user and asking how you can help them today.`;
729
+ Start by greeting the user briefly and asking how you can help.`;
730
+ const parts = [base.trim(), workspaceSection];
731
+ if (memorySection)
732
+ parts.push(memorySection);
733
+ if (schemaSection)
734
+ parts.push(schemaSection);
735
+ // Inline eager skills directly into the system prompt
736
+ if (skillIndex) {
737
+ const eagerContent = loadEagerSkills(skillIndex);
738
+ if (eagerContent)
739
+ parts.push(eagerContent);
740
+ }
741
+ // Append skill index for on-demand skills
742
+ if (skillIndex) {
743
+ const index = buildSkillIndexPrompt(skillIndex);
744
+ if (index)
745
+ parts.push(index);
746
+ }
747
+ parts.push(outputSection);
748
+ return parts.join('\n\n');
380
749
  }
381
750
  //# sourceMappingURL=start.js.map