spine-framework 0.3.145 → 0.4.0

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 (451) hide show
  1. package/.env.example +76 -0
  2. package/.framework/.spine-manifest.json +19 -0
  3. package/.framework/cli/commands/agents.ts +61 -33
  4. package/.framework/cli/commands/auth.ts +60 -28
  5. package/.framework/cli/commands/create-app.ts +212 -80
  6. package/.framework/cli/commands/dev.ts +184 -68
  7. package/.framework/cli/commands/doctor.ts +324 -152
  8. package/.framework/cli/commands/generate.ts +119 -68
  9. package/.framework/cli/commands/init.ts +141 -242
  10. package/.framework/cli/commands/install-app.ts +1336 -314
  11. package/.framework/cli/commands/items.ts +31 -29
  12. package/.framework/cli/commands/migrate.ts +299 -156
  13. package/.framework/cli/commands/migrations.ts +62 -30
  14. package/.framework/cli/commands/pipelines.ts +31 -29
  15. package/.framework/cli/commands/status.ts +105 -45
  16. package/.framework/cli/commands/system.ts +47 -42
  17. package/.framework/cli/commands/test.ts +161 -102
  18. package/.framework/cli/commands/upgrade.ts +260 -0
  19. package/.framework/cli/commands/validate-app.ts +517 -161
  20. package/.framework/cli/context.ts +117 -85
  21. package/.framework/cli/env-loader.ts +74 -43
  22. package/.framework/cli/index.ts +70 -46
  23. package/.framework/cli/lib/project-files.ts +404 -0
  24. package/.framework/docs/guides/testing-guide.md +1 -1
  25. package/.framework/docs/runtime-types.md +72 -72
  26. package/.framework/functions/_shared/access.ts +84 -0
  27. package/.framework/functions/_shared/agent-runner.ts +5051 -1026
  28. package/.framework/functions/_shared/app-manifest.ts +214 -94
  29. package/.framework/functions/_shared/ast.ts +214 -0
  30. package/.framework/functions/_shared/audit.ts +91 -55
  31. package/.framework/functions/_shared/cil.ts +368 -127
  32. package/.framework/functions/_shared/db.ts +337 -68
  33. package/.framework/functions/_shared/funnel.ts +228 -0
  34. package/.framework/functions/_shared/geo.ts +105 -0
  35. package/.framework/functions/_shared/hash-api-key.ts +94 -17
  36. package/.framework/functions/_shared/index.ts +22 -224
  37. package/.framework/functions/_shared/kb.ts +832 -58
  38. package/.framework/functions/_shared/llm.ts +118 -18
  39. package/.framework/functions/_shared/logging.ts +317 -19
  40. package/.framework/functions/_shared/middleware.ts +701 -131
  41. package/.framework/functions/_shared/notify.ts +70 -0
  42. package/.framework/functions/_shared/outcomes.ts +34 -19
  43. package/.framework/functions/_shared/permissions.ts +501 -315
  44. package/.framework/functions/_shared/pipeline-runner.ts +704 -155
  45. package/.framework/functions/_shared/principal.ts +436 -263
  46. package/.framework/functions/_shared/rate-limit.ts +233 -0
  47. package/.framework/functions/_shared/resolve-credentials.ts +86 -19
  48. package/.framework/functions/_shared/resolve-ids.ts +203 -139
  49. package/.framework/functions/_shared/retry-deliveries.ts +51 -16
  50. package/.framework/functions/_shared/schema-utils.ts +602 -521
  51. package/.framework/functions/_shared/security.ts +345 -0
  52. package/.framework/functions/_shared/testing.ts +197 -172
  53. package/.framework/functions/_shared/tracing.ts +351 -21
  54. package/.framework/functions/_shared/trigger-engine.ts +425 -136
  55. package/.framework/functions/_shared/webhook-registration.ts +71 -70
  56. package/.framework/functions/_shared/webhook-registry.ts +126 -83
  57. package/.framework/functions/account-nodes.ts +30 -30
  58. package/.framework/functions/adapters/generic-webhook-inbound.ts +49 -16
  59. package/.framework/functions/adapters/generic-webhook-outbound.ts +17 -17
  60. package/.framework/functions/adapters/openai-outbound.ts +15 -16
  61. package/.framework/functions/admin-data.ts +596 -173
  62. package/.framework/functions/admin-funnel.ts +64 -60
  63. package/.framework/functions/admin-install-funnel.ts +84 -66
  64. package/.framework/functions/admin-signals.ts +69 -51
  65. package/.framework/functions/ai-agents.ts +431 -106
  66. package/.framework/functions/ai-interactions.ts +186 -112
  67. package/.framework/functions/api-keys.ts +107 -107
  68. package/.framework/functions/apps.ts +154 -136
  69. package/.framework/functions/auth.ts +83 -65
  70. package/.framework/functions/cil-agent-ingest.ts +189 -232
  71. package/.framework/functions/cil-agent.ts +433 -177
  72. package/.framework/functions/cil-callmap.ts +642 -430
  73. package/.framework/functions/cil-config.ts +139 -135
  74. package/.framework/functions/cil-context.ts +214 -127
  75. package/.framework/functions/cil-doc.ts +587 -458
  76. package/.framework/functions/cil-embeddings.ts +453 -364
  77. package/.framework/functions/cil-graph.ts +427 -142
  78. package/.framework/functions/cil-ingest.ts +1418 -1154
  79. package/.framework/functions/cil-pipeline.ts +276 -21
  80. package/.framework/functions/cil-project.ts +659 -132
  81. package/.framework/functions/cil-validate.ts +198 -164
  82. package/.framework/functions/debug-auth.ts +57 -55
  83. package/.framework/functions/deliveries.ts +80 -21
  84. package/.framework/functions/embeddings.ts +1023 -318
  85. package/.framework/functions/eval-harness.ts +146 -108
  86. package/.framework/functions/integration-outbound.ts +205 -75
  87. package/.framework/functions/integration-routes.ts +128 -23
  88. package/.framework/functions/integrations.ts +125 -100
  89. package/.framework/functions/invites.ts +1095 -460
  90. package/.framework/functions/item-progress.ts +158 -102
  91. package/.framework/functions/kb.ts +34 -10
  92. package/.framework/functions/logs.ts +133 -133
  93. package/.framework/functions/observability.ts +152 -122
  94. package/.framework/functions/pipeline-executions.ts +147 -132
  95. package/.framework/functions/pipelines.ts +134 -119
  96. package/.framework/functions/prompt-configs.ts +96 -80
  97. package/.framework/functions/roles.ts +112 -82
  98. package/.framework/functions/security.ts +450 -0
  99. package/.framework/functions/system-cron.ts +117 -80
  100. package/.framework/functions/system.ts +158 -98
  101. package/.framework/functions/tests.ts +29 -29
  102. package/.framework/functions/timers.ts +94 -95
  103. package/.framework/functions/trigger-scheduler.ts +10 -0
  104. package/.framework/functions/triggers.ts +179 -149
  105. package/.framework/functions/types.ts +137 -125
  106. package/.framework/mcp/index.ts +133 -0
  107. package/.framework/mcp/load-env.ts +60 -0
  108. package/.framework/mcp/shared/app-manifest.ts +275 -0
  109. package/.framework/mcp/shared/db.ts +477 -0
  110. package/.framework/mcp/shared/kb.ts +118 -0
  111. package/.framework/mcp/tools/architect.ts +431 -0
  112. package/.framework/mcp/tools/cil/context.ts +345 -0
  113. package/.framework/mcp/tools/cil/graph.ts +242 -0
  114. package/.framework/mcp/tools/cil/ingest.ts +1217 -0
  115. package/.framework/mcp/tools/cil/pipeline-orchestrator.ts +693 -0
  116. package/.framework/mcp/tools/cil/pipeline.ts +767 -0
  117. package/.framework/mcp/tools/cil/project.ts +791 -0
  118. package/.framework/mcp/tools/cil/retrieval.ts +336 -0
  119. package/.framework/mcp/tools/cil/search-core.ts +454 -0
  120. package/.framework/mcp/tools/cil/search.ts +97 -0
  121. package/.framework/mcp/tools/cil/suggest.ts +186 -0
  122. package/.framework/mcp/tools/cil/validate.ts +1369 -0
  123. package/.framework/mcp/tools/kb/delete.ts +139 -0
  124. package/.framework/mcp/tools/kb/index.ts +52 -0
  125. package/.framework/mcp/tools/kb/ingest.ts +84 -0
  126. package/.framework/mcp/tools/kb/list-types.ts +102 -0
  127. package/.framework/mcp/tools/kb/publish.ts +100 -0
  128. package/.framework/mcp/tools/kb/reindex.ts +86 -0
  129. package/.framework/mcp/tools/kb/revoke.ts +85 -0
  130. package/.framework/mcp/tools/kb/search.ts +101 -0
  131. package/.framework/mcp/tools/kb/upsert.ts +125 -0
  132. package/.framework/mcp/tools/observability.ts +510 -0
  133. package/.framework/migrations/001_core_schema.sql +15 -0
  134. package/.framework/migrations/002_core_rls_and_functions.sql +15 -0
  135. package/.framework/migrations/003_core_system_data.sql +16 -1
  136. package/.framework/migrations/004_core_rpc_and_broker.sql +17 -1
  137. package/.framework/migrations/005_core_observability.sql +17 -1
  138. package/.framework/migrations/006_security_hardening_rls.sql +15 -0
  139. package/.framework/migrations/007_kb_article_list.sql +17 -1
  140. package/.framework/migrations/008_items_target.sql +15 -0
  141. package/.framework/migrations/009_roles_slug_unique.sql +15 -0
  142. package/.framework/migrations/010_auth_user_trigger.sql +15 -0
  143. package/.framework/migrations/011_spine_system_account.sql +16 -0
  144. package/.framework/migrations/012_service_role_grants.sql +16 -0
  145. package/.framework/migrations/013_get_account_apps_role_filter.sql +15 -0
  146. package/.framework/migrations/014_apps_required_roles.sql +16 -0
  147. package/.framework/migrations/015_types_kind_slug_unique.sql +15 -0
  148. package/.framework/migrations/016_cil_types.sql +281 -0
  149. package/.framework/migrations/017_cil_stable_ids.sql +37 -0
  150. package/.framework/migrations/018_runtime_tables.sql +129 -0
  151. package/.framework/migrations/019_agent_prompt_columns.sql +56 -0
  152. package/.framework/migrations/020_schema_reconciliation.sql +67 -0
  153. package/.framework/migrations/021_kb_embedding_lifecycle.sql +31 -0
  154. package/.framework/migrations/032_cil_status_migration.sql +73 -0
  155. package/.framework/migrations/033_cil_versioning.sql +73 -0
  156. package/.framework/migrations/034_match_embeddings_knn.sql +115 -0
  157. package/.framework/migrations/035_rate_limits.sql +55 -0
  158. package/.framework/migrations/036_demo_record_flags.sql +72 -0
  159. package/.framework/migrations/037_kb_types_canonical.sql +244 -0
  160. package/.framework/migrations/038_drop_demo_record_flags.sql +38 -0
  161. package/.framework/migrations/042_registration_hierarchy.sql +309 -0
  162. package/.framework/migrations/043_policy_actor_cleanup.sql +39 -0
  163. package/.framework/migrations/044_match_embeddings_filtered_knn.sql +151 -0
  164. package/.framework/migrations/045_security_critical_rls.sql +139 -0
  165. package/.framework/migrations/046_security_missing_rls.sql +79 -0
  166. package/.framework/migrations/047_people_updated_by.sql +27 -0
  167. package/.framework/migrations/048_watcher_last_seen_at.sql +24 -0
  168. package/.framework/migrations/049_security_blocklist.sql +148 -0
  169. package/.framework/migrations/050_notifications.sql +49 -0
  170. package/.framework/migrations/051_message_agent_fields.sql +74 -0
  171. package/.framework/migrations/052_registration_security.sql +421 -0
  172. package/.framework/migrations/053_builtin_action_seeds.sql +66 -0
  173. package/.framework/migrations/054_kb_ingest_lease.sql +157 -0
  174. package/.framework/migrations/055_type_id_drift_reconciliation.sql +33 -0
  175. package/.framework/migrations/056_slug_column_reconciliation.sql +38 -0
  176. package/.framework/migrations/057_pipelines_slug_identity.sql +14 -0
  177. package/.framework/migrations/058_account_data_field_declarations.sql +53 -0
  178. package/.framework/migrations/059_registration_placement_fixes.sql +269 -0
  179. package/.framework/migrations/060_account_person_record_permissions.sql +38 -0
  180. package/.framework/migrations/061_fk_dedup.sql +32 -0
  181. package/.framework/public/spine-logo.svg +10 -3
  182. package/.framework/scripts/app-install-cli.ts +190 -93
  183. package/.framework/scripts/assemble-frontend.sh +127 -44
  184. package/.framework/scripts/assemble-functions.sh +128 -30
  185. package/.framework/scripts/boundary-check.sh +6 -2
  186. package/.framework/scripts/build-manifest.sh +24 -68
  187. package/.framework/scripts/check-core-integrity.sh +15 -76
  188. package/.framework/scripts/checksum-manifest.mts +343 -0
  189. package/.framework/scripts/cil-migrate-markers.ts +403 -0
  190. package/.framework/scripts/fix-cil-markers.mjs +176 -0
  191. package/.framework/scripts/verify-integrity.sh +21 -57
  192. package/.framework/scripts/verify-manifest.mts +305 -0
  193. package/.framework/seeds/types/funnel_signal_use.json +74 -21
  194. package/.framework/seeds/types/spine_instance.json +73 -21
  195. package/.framework/src/App.tsx +118 -72
  196. package/.framework/src/apps/admin/index.tsx +855 -58
  197. package/.framework/src/components/AppWrapper.tsx +43 -45
  198. package/.framework/src/components/CustomAppLoader.tsx +160 -116
  199. package/.framework/src/components/admin/AdminListPage.tsx +46 -44
  200. package/.framework/src/components/admin/AdminSidebar.tsx +57 -47
  201. package/.framework/src/components/admin/AdminStatsCard.tsx +46 -44
  202. package/.framework/src/components/admin/PageInstructions.tsx +98 -2
  203. package/.framework/src/components/admin/SortableTableHeader.tsx +44 -45
  204. package/.framework/src/components/app-shell/GenericAppShell.tsx +64 -65
  205. package/.framework/src/components/app-shell/GenericDetailPage.tsx +44 -47
  206. package/.framework/src/components/app-shell/GenericListPage.tsx +45 -49
  207. package/.framework/src/components/app-sidebar.tsx +42 -45
  208. package/.framework/src/components/auth/ProtectedRoute.tsx +48 -41
  209. package/.framework/src/components/auth/TurnstileWidget.tsx +27 -0
  210. package/.framework/src/components/kb/KBArticleEditor.tsx +231 -22
  211. package/.framework/src/components/kb/KBArticleList.tsx +251 -10
  212. package/.framework/src/components/layout/AppShell.tsx +51 -45
  213. package/.framework/src/components/layout/Header.tsx +42 -43
  214. package/.framework/src/components/layout/Layout.tsx +46 -46
  215. package/.framework/src/components/layout/Sidebar.tsx +196 -96
  216. package/.framework/src/components/runtime/DataDetailHeader.tsx +55 -30
  217. package/.framework/src/components/runtime/DataDetailPage.tsx +31 -269
  218. package/.framework/src/components/runtime/DataFilters.tsx +49 -74
  219. package/.framework/src/components/runtime/DataHeader.tsx +53 -32
  220. package/.framework/src/components/runtime/DataListPage.tsx +51 -155
  221. package/.framework/src/components/runtime/DataStats.tsx +66 -44
  222. package/.framework/src/components/runtime/DataTable.tsx +49 -149
  223. package/.framework/src/components/runtime/SchemaDetailForm.tsx +45 -75
  224. package/.framework/src/components/runtime/index.ts +119 -16
  225. package/.framework/src/components/search-form.tsx +46 -29
  226. package/.framework/src/components/shared/AgentView.tsx +65 -48
  227. package/.framework/src/components/shared/FieldRenderer.tsx +144 -50
  228. package/.framework/src/components/shared/InviteManager.tsx +207 -97
  229. package/.framework/src/components/shared/MemberList.tsx +97 -0
  230. package/.framework/src/components/shared/SchemaFields.tsx +107 -86
  231. package/.framework/src/components/ui/DataTable.tsx +80 -68
  232. package/.framework/src/components/ui/Form.tsx +157 -146
  233. package/.framework/src/components/ui/ItemCard.tsx +74 -67
  234. package/.framework/src/components/ui/ItemListView.tsx +73 -68
  235. package/.framework/src/components/ui/LoadingSpinner.tsx +43 -48
  236. package/.framework/src/components/ui/Modal.tsx +78 -68
  237. package/.framework/src/components/ui/RichTextEditor.tsx +89 -69
  238. package/.framework/src/components/ui/accordion.tsx +85 -86
  239. package/.framework/src/components/ui/alert-dialog.tsx +197 -184
  240. package/.framework/src/components/ui/alert.tsx +105 -94
  241. package/.framework/src/components/ui/aspect-ratio.tsx +57 -43
  242. package/.framework/src/components/ui/avatar.tsx +122 -107
  243. package/.framework/src/components/ui/badge.tsx +60 -55
  244. package/.framework/src/components/ui/breadcrumb.tsx +127 -128
  245. package/.framework/src/components/ui/button-group.tsx +86 -72
  246. package/.framework/src/components/ui/button.tsx +71 -50
  247. package/.framework/src/components/ui/calendar.tsx +72 -57
  248. package/.framework/src/components/ui/card.tsx +136 -129
  249. package/.framework/src/components/ui/carousel.tsx +226 -107
  250. package/.framework/src/components/ui/chart.tsx +252 -147
  251. package/.framework/src/components/ui/checkbox.tsx +43 -44
  252. package/.framework/src/components/ui/collapsible.tsx +86 -71
  253. package/.framework/src/components/ui/combobox.tsx +282 -249
  254. package/.framework/src/components/ui/command.tsx +158 -146
  255. package/.framework/src/components/ui/context-menu.tsx +239 -240
  256. package/.framework/src/components/ui/dialog.tsx +174 -157
  257. package/.framework/src/components/ui/direction.tsx +72 -56
  258. package/.framework/src/components/ui/drawer.tsx +168 -169
  259. package/.framework/src/components/ui/dropdown-menu.tsx +254 -223
  260. package/.framework/src/components/ui/empty.tsx +131 -120
  261. package/.framework/src/components/ui/field.tsx +205 -173
  262. package/.framework/src/components/ui/hover-card.tsx +71 -72
  263. package/.framework/src/components/ui/input-group.tsx +148 -133
  264. package/.framework/src/components/ui/input-otp.tsx +99 -86
  265. package/.framework/src/components/ui/input.tsx +43 -41
  266. package/.framework/src/components/ui/item.tsx +207 -185
  267. package/.framework/src/components/ui/kbd.tsx +57 -56
  268. package/.framework/src/components/ui/label.tsx +45 -41
  269. package/.framework/src/components/ui/menubar.tsx +268 -236
  270. package/.framework/src/components/ui/native-select.tsx +85 -86
  271. package/.framework/src/components/ui/navigation-menu.tsx +166 -156
  272. package/.framework/src/components/ui/pagination.tsx +131 -141
  273. package/.framework/src/components/ui/popover.tsx +125 -128
  274. package/.framework/src/components/ui/progress.tsx +47 -44
  275. package/.framework/src/components/ui/radio-group.tsx +60 -55
  276. package/.framework/src/components/ui/resizable.tsx +85 -72
  277. package/.framework/src/components/ui/scroll-area.tsx +57 -58
  278. package/.framework/src/components/ui/select.tsx +182 -169
  279. package/.framework/src/components/ui/separator.tsx +42 -43
  280. package/.framework/src/components/ui/sheet.tsx +170 -159
  281. package/.framework/src/components/ui/sidebar.tsx +395 -398
  282. package/.framework/src/components/ui/skeleton.tsx +42 -43
  283. package/.framework/src/components/ui/slider.tsx +61 -44
  284. package/.framework/src/components/ui/sonner.tsx +44 -44
  285. package/.framework/src/components/ui/spinner.tsx +47 -42
  286. package/.framework/src/components/ui/switch.tsx +56 -42
  287. package/.framework/src/components/ui/table-primitives.tsx +272 -132
  288. package/.framework/src/components/ui/table.tsx +140 -142
  289. package/.framework/src/components/ui/tabs.tsx +110 -93
  290. package/.framework/src/components/ui/textarea.tsx +43 -44
  291. package/.framework/src/components/ui/toggle-group.tsx +87 -71
  292. package/.framework/src/components/ui/toggle.tsx +56 -56
  293. package/.framework/src/components/ui/tooltip.tsx +99 -85
  294. package/.framework/src/contexts/AppContext.tsx +166 -114
  295. package/.framework/src/contexts/AuthContext.tsx +232 -137
  296. package/.framework/src/hooks/use-mobile.ts +43 -44
  297. package/.framework/src/hooks/useApi.ts +185 -172
  298. package/.framework/src/hooks/useAppPath.ts +31 -29
  299. package/.framework/src/hooks/useApps.ts +77 -42
  300. package/.framework/src/hooks/useEntityList.ts +66 -29
  301. package/.framework/src/hooks/useEntityRecord.ts +76 -32
  302. package/.framework/src/hooks/useForm.ts +62 -30
  303. package/.framework/src/hooks/useListSchema.ts +60 -43
  304. package/.framework/src/hooks/useSchemaRecord.ts +115 -95
  305. package/.framework/src/lib/api.ts +258 -134
  306. package/.framework/src/lib/registration.ts +181 -0
  307. package/.framework/src/lib/supabase.ts +118 -81
  308. package/.framework/src/lib/utils.ts +266 -266
  309. package/.framework/src/main.tsx +43 -44
  310. package/.framework/src/pages/DashboardPage.tsx +44 -43
  311. package/.framework/src/pages/NoAppPage.tsx +50 -0
  312. package/.framework/src/pages/NotFoundPage.tsx +32 -29
  313. package/.framework/src/pages/SetupPage.tsx +34 -33
  314. package/.framework/src/pages/admin/AIAgentDetailPage.tsx +69 -58
  315. package/.framework/src/pages/admin/AIAgentsPage.tsx +43 -40
  316. package/.framework/src/pages/admin/APIKeyDetailPage.tsx +63 -55
  317. package/.framework/src/pages/admin/APIKeysPage.tsx +44 -42
  318. package/.framework/src/pages/admin/AlertsConfigPage.tsx +117 -94
  319. package/.framework/src/pages/admin/AppDetailPage.tsx +48 -42
  320. package/.framework/src/pages/admin/AppsPage.tsx +45 -42
  321. package/.framework/src/pages/admin/DeliveriesPage.tsx +49 -17
  322. package/.framework/src/pages/admin/DesignedPage.tsx +31 -30
  323. package/.framework/src/pages/admin/EmbeddingDetailPage.tsx +58 -0
  324. package/.framework/src/pages/admin/EmbeddingsPage.tsx +49 -0
  325. package/.framework/src/pages/admin/ExtendedShadcnTestPage.tsx +31 -29
  326. package/.framework/src/pages/admin/IncrementalShadcnTestPage.tsx +46 -29
  327. package/.framework/src/pages/admin/IntegratedDashboard.tsx +32 -29
  328. package/.framework/src/pages/admin/IntegrationDetailPage.tsx +68 -55
  329. package/.framework/src/pages/admin/IntegrationsPage.tsx +45 -42
  330. package/.framework/src/pages/admin/InvitesPage.tsx +60 -44
  331. package/.framework/src/pages/admin/LogsPage.tsx +87 -137
  332. package/.framework/src/pages/admin/MinimalShadcnTestPage.tsx +31 -29
  333. package/.framework/src/pages/admin/ObservabilityDashboard.tsx +150 -89
  334. package/.framework/src/pages/admin/PipelineDetailPage.tsx +77 -104
  335. package/.framework/src/pages/admin/PipelineExecutionsPage.tsx +44 -43
  336. package/.framework/src/pages/admin/PipelinesPage.tsx +59 -145
  337. package/.framework/src/pages/admin/PromptConfigDetailPage.tsx +53 -42
  338. package/.framework/src/pages/admin/PromptConfigsPage.tsx +47 -117
  339. package/.framework/src/pages/admin/ProperlyDesignedPage.tsx +31 -44
  340. package/.framework/src/pages/admin/RoleDetailPage.tsx +49 -92
  341. package/.framework/src/pages/admin/RolesPage.tsx +47 -164
  342. package/.framework/src/pages/admin/SecurityPage.tsx +310 -0
  343. package/.framework/src/pages/admin/SelectTestPage.tsx +30 -32
  344. package/.framework/src/pages/admin/ShadcnTestPage.tsx +37 -44
  345. package/.framework/src/pages/admin/SimpleDashboard.tsx +32 -44
  346. package/.framework/src/pages/admin/TestRunDetailPage.tsx +97 -44
  347. package/.framework/src/pages/admin/TestingDashboard.tsx +126 -71
  348. package/.framework/src/pages/admin/TimerDetailPage.tsx +67 -55
  349. package/.framework/src/pages/admin/TimersPage.tsx +44 -42
  350. package/.framework/src/pages/admin/TriggerDetailPage.tsx +47 -42
  351. package/.framework/src/pages/admin/TriggersPage.tsx +58 -57
  352. package/.framework/src/pages/admin/TypeDetailPage.tsx +91 -55
  353. package/.framework/src/pages/admin/TypesPage.tsx +45 -42
  354. package/.framework/src/pages/admin/cil/CILAgentPage.tsx +258 -128
  355. package/.framework/src/pages/admin/cil/CILChunkDetailPage.tsx +127 -123
  356. package/.framework/src/pages/admin/cil/CILDashboardPage.tsx +308 -235
  357. package/.framework/src/pages/admin/cil/CILFileListPage.tsx +159 -110
  358. package/.framework/src/pages/admin/cil/CILFileSelectPage.tsx +432 -0
  359. package/.framework/src/pages/admin/cil/CILGraphPage.tsx +200 -178
  360. package/.framework/src/pages/admin/cil/CILIngestionPage.tsx +286 -239
  361. package/.framework/src/pages/admin/cil/CILProjectsPage.tsx +211 -90
  362. package/.framework/src/pages/admin/cil/CILSearchPage.tsx +156 -143
  363. package/.framework/src/pages/admin/cil/CILSuggestionsPage.tsx +379 -0
  364. package/.framework/src/pages/admin/cil/CILVersionsPage.tsx +337 -0
  365. package/.framework/src/pages/admin/cil/FlexiblePipelinePage.tsx +219 -214
  366. package/.framework/src/pages/admin/kb/KBAdminPage.tsx +64 -0
  367. package/.framework/src/pages/auth/AuthConfirmPage.tsx +135 -65
  368. package/.framework/src/pages/auth/ForgotPasswordPage.tsx +39 -31
  369. package/.framework/src/pages/auth/LoginPage.tsx +38 -32
  370. package/.framework/src/pages/auth/PendingApprovalPage.tsx +31 -28
  371. package/.framework/src/pages/auth/RegisterPage.tsx +429 -155
  372. package/.framework/src/pages/spine-framework/APIPage.tsx +33 -30
  373. package/.framework/src/pages/spine-framework/CLIPage.tsx +32 -30
  374. package/.framework/src/types/auth.ts +101 -93
  375. package/.framework/src/types/types.ts +283 -262
  376. package/.framework/templates/project.json +87 -0
  377. package/.framework/templates/spine.config.neutral.json +7 -0
  378. package/.framework/tests/api/api-surface.test.ts +381 -0
  379. package/.framework/tests/fixtures/seed.ts +427 -0
  380. package/.framework/tests/fixtures/teardown.ts +228 -0
  381. package/.framework/tests/reporter.ts +351 -0
  382. package/.framework/tests/setup.ts +135 -0
  383. package/.framework/tests/ui/ui-sweep.ts +278 -0
  384. package/.framework/tests/unit/admin-data-logger.test.ts +310 -0
  385. package/.framework/tests/unit/agent-runner.test.ts +2389 -0
  386. package/.framework/tests/unit/ai-interactions.test.ts +81 -0
  387. package/.framework/tests/unit/app-manifest.test.ts +359 -0
  388. package/.framework/tests/unit/assembly-scripts.test.ts +172 -0
  389. package/.framework/tests/unit/cil-determinism.test.ts +85 -0
  390. package/.framework/tests/unit/core-isolation.test.ts +153 -0
  391. package/.framework/tests/unit/funnel-signals.test.ts +135 -0
  392. package/.framework/tests/unit/install-app.test.ts +1759 -0
  393. package/.framework/tests/unit/invites-security.test.ts +572 -0
  394. package/.framework/tests/unit/kb-ingest-lease.test.ts +287 -0
  395. package/.framework/tests/unit/kb-search.test.ts +119 -0
  396. package/.framework/tests/unit/migrate.test.ts +509 -0
  397. package/.framework/tests/unit/notify.test.ts +136 -0
  398. package/.framework/tests/unit/permissions.test.ts +269 -0
  399. package/.framework/tests/unit/pipeline-runner.test.ts +755 -0
  400. package/.framework/tests/unit/principal-cache.test.ts +138 -0
  401. package/.framework/tests/unit/retrieval-golden.test.ts +520 -0
  402. package/.framework/tests/unit/runtime-isolation.test.ts +642 -0
  403. package/.framework/tests/unit/schema-utils.test.ts +225 -0
  404. package/.framework/tests/unit/trigger-engine.test.ts +513 -0
  405. package/.framework/tests/unit/validate-app.test.ts +527 -0
  406. package/README.md +34 -3
  407. package/bin/spine-framework.cjs +1 -0
  408. package/config/tailwind.config.cjs +7 -0
  409. package/config/tsconfig.functions.json +42 -0
  410. package/config/tsconfig.json +37 -9
  411. package/config/vite.config.ts +31 -2
  412. package/netlify.toml +31 -3
  413. package/package.json +46 -29
  414. package/scripts/assemble-frontend.sh +127 -44
  415. package/scripts/assemble-functions.sh +127 -29
  416. package/scripts/boundary-check.sh +6 -2
  417. package/scripts/build-manifest.sh +24 -68
  418. package/scripts/checksum-manifest.mts +345 -0
  419. package/scripts/dev-clean.sh +86 -0
  420. package/scripts/dev-netlify.sh +49 -0
  421. package/scripts/netlify-dev-wrapper.sh +2 -0
  422. package/scripts/verify-install.ts +358 -0
  423. package/scripts/verify-integrity.sh +21 -57
  424. package/scripts/verify-manifest.mts +305 -0
  425. package/templates/project/gitignore +45 -0
  426. package/{config → templates/project}/package-lock.json +22335 -9366
  427. package/vitest.config.ts +46 -0
  428. package/.framework/public/spine.config.json +0 -91
  429. package/config/deno.lock +0 -108
  430. package/config/tsconfig.node.tsbuildinfo +0 -1
  431. package/config/tsconfig.tsbuildinfo +0 -1
  432. package/config/vite.config.d.ts +0 -2
  433. package/custom/.devin/AGENT.md +0 -357
  434. package/custom/.devin/skills/install-app/SKILL.md +0 -39
  435. package/custom/.devin/skills/spine-workbench-discovery/SKILL.md +0 -39
  436. package/custom/.devin/skills/spine-workbench-idea/SKILL.md +0 -36
  437. package/custom/.devin/skills/spine-workbench-implement/SKILL.md +0 -46
  438. package/custom/.devin/skills/spine-workbench-initiative/SKILL.md +0 -28
  439. package/custom/.devin/skills/spine-workbench-plan/SKILL.md +0 -38
  440. package/custom/.devin/workflows/app-install.yaml +0 -83
  441. package/custom/README.md +0 -79
  442. package/docs/README.md +0 -15
  443. package/scripts/app-install-cli.ts +0 -286
  444. package/scripts/check-core-integrity.sh +0 -82
  445. package/scripts/ingest-chunks.cjs +0 -202
  446. package/scripts/kb-chunk-parser.cjs +0 -312
  447. package/scripts/kb-chunk-parser.ts +0 -330
  448. package/scripts/kb-ingest-docs.ts +0 -203
  449. package/scripts/load-test-app-install.ts +0 -484
  450. package/scripts/test-login.js +0 -33
  451. package/spine.config.json +0 -91
@@ -0,0 +1,345 @@
1
+ // CIL[BEGIN stable_id="~/.framework/mcp/tools/cil/context.ts:module_header:module_header" type="module_header" identity="module_header" parent="~/.framework/mcp/tools/cil/context.ts"]
2
+ // CIL[DOC-MACRO purpose="MCP tool module that exposes CIL context and post-edit helpers for named chunks"]
3
+ // CIL[DOC-RESPONSIBILITY responsibility="Register MCP tools providing pre-edit context briefs and post-edit next steps"]
4
+ // CIL[DOC-CONTEXT module="mcp-cil-context" layer="tools" related_files=".framework/mcp/shared/db.ts"]
5
+ // CIL[DOC-CONTRACT inputs="McpServer instance" outputs="registered tools" errors="none" side_effects="registers three tools on the server"]
6
+ // CIL[DOC-MICRO logic_summary="Imports SDK, zod, and shared DB helpers; no runtime logic" notable_decisions="[]"]
7
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
8
+ // CIL[DOC-STABILITY stability="stable"]
9
+ // CIL[DOC-AUDIENCE audience="internal"]
10
+
11
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
12
+ import { z } from 'zod'
13
+ import { adminDb, resolveLinkTypeId } from '../../shared/db.js'
14
+ // CIL[DOC-VECTOR type="context" content="purpose='MCP tool module that exposes CIL context and post-edit helpers for named chunks'; No verified external callers; imports: .framework/mcp/shared/db.ts"]
15
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
16
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
17
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
18
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
19
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
20
+ // CIL[END stable_id="~/.framework/mcp/tools/cil/context.ts:module_header:module_header"]
21
+
22
+ // CIL[BEGIN stable_id="~/.framework/mcp/tools/cil/context.ts:getChunkById:function" type="function" identity="getChunkById" parent="~/.framework/mcp/tools/cil/context.ts"]
23
+ // CIL[DOC-MACRO purpose="Fetch a CIL chunk row by its primary id"]
24
+ // CIL[DOC-RESPONSIBILITY responsibility="Return raw chunk data for a given id"]
25
+ // CIL[DOC-CONTEXT module="mcp-cil-context" layer="data-access" related_files=".framework/mcp/shared/db.ts,.framework/mcp/tools/cil/context.ts"]
26
+ // CIL[DOC-CONTRACT inputs="id: string" outputs="chunk data or undefined" errors="none" side_effects="queries adminDb"]
27
+ // CIL[DOC-MICRO logic_summary="Selects from items table by id" notable_decisions="[]"]
28
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
29
+ // CIL[DOC-STABILITY stability="stable"]
30
+ async function getChunkById(id: string) {
31
+ const { data } = await adminDb.from('items').select('id, title, data')
32
+ .eq('id', id).maybeSingle()
33
+ return data
34
+ }
35
+ // CIL[DOC-VECTOR type="context" content="purpose='Fetch a CIL chunk row by its primary id'; No verified external callers; imports: .framework/mcp/shared/db.ts"]
36
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
37
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
38
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
39
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
40
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
41
+ // CIL[END stable_id="~/.framework/mcp/tools/cil/context.ts:getChunkById:function"]
42
+
43
+ // CIL[BEGIN stable_id="~/.framework/mcp/tools/cil/context.ts:getChunkByStableId:function" type="function" identity="getChunkByStableId" parent="~/.framework/mcp/tools/cil/context.ts"]
44
+ // CIL[DOC-MACRO purpose="Fetch an active CIL chunk by project and stable id"]
45
+ // CIL[DOC-RESPONSIBILITY responsibility="Return the matching chunk or undefined"]
46
+ // CIL[DOC-CONTEXT module="mcp-cil-context" layer="data-access" related_files=".framework/mcp/shared/db.ts,.framework/mcp/tools/cil/context.ts"]
47
+ // CIL[DOC-CONTRACT inputs="projectId: string, stableId: string, versionId?: string" outputs="chunk data" errors="none" side_effects="queries adminDb with filters"]
48
+ // CIL[DOC-MICRO logic_summary="Builds a filtered Supabase query and returns maybeSingle result" notable_decisions="[]"]
49
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
50
+ // CIL[DOC-STABILITY stability="stable"]
51
+ async function getChunkByStableId(projectId: string, stableId: string, versionId?: string) {
52
+ let q = adminDb.from('items').select('id, title, data')
53
+ .filter('data->>kb_type', 'eq', 'cil_chunk')
54
+ .filter('data->>project_id', 'eq', projectId)
55
+ .filter('data->>stable_id', 'eq', stableId)
56
+ .eq('is_active', true)
57
+ if (versionId) q = (q as any).filter('data->>version_id', 'eq', versionId)
58
+ const { data } = await (q as any).maybeSingle()
59
+ return data
60
+ }
61
+ // CIL[DOC-VECTOR type="context" content="purpose='Fetch an active CIL chunk by project and stable id'; No verified external callers; imports: .framework/mcp/shared/db.ts"]
62
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
63
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
64
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
65
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
66
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
67
+ // CIL[END stable_id="~/.framework/mcp/tools/cil/context.ts:getChunkByStableId:function"]
68
+
69
+ // CIL[BEGIN stable_id="~/.framework/mcp/tools/cil/context.ts:findChunkByName:function" type="function" identity="findChunkByName" parent="~/.framework/mcp/tools/cil/context.ts"]
70
+ // CIL[DOC-MACRO purpose="Find an active CIL chunk by chunk name"]
71
+ // CIL[DOC-RESPONSIBILITY responsibility="Return the first matching chunk by name"]
72
+ // CIL[DOC-STABILITY stability="internal"]
73
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
74
+ // CIL[DOC-CONTEXT module="mcp-cil-context" layer="data-access" related_files=".framework/mcp/shared/db.ts,.framework/mcp/tools/cil/context.ts"]
75
+ // CIL[DOC-CONTRACT inputs="name: string, projectId?: string" outputs="chunk data" errors="none" side_effects="queries adminDb"]
76
+ // CIL[DOC-MICRO logic_summary="Filters by chunk_name and optional project, returns first result" notable_decisions="[]"]
77
+ async function findChunkByName(name: string, projectId?: string) {
78
+ let q = adminDb.from('items').select('id, title, data')
79
+ .filter('data->>kb_type', 'eq', 'cil_chunk')
80
+ .filter('data->>chunk_name', 'eq', name)
81
+ .eq('is_active', true)
82
+ if (projectId) q = (q as any).filter('data->>project_id', 'eq', projectId)
83
+ const { data } = await (q as any).limit(1).maybeSingle()
84
+ return data
85
+ }
86
+ // CIL[END stable_id="~/.framework/mcp/tools/cil/context.ts:findChunkByName:function"]
87
+
88
+ // CIL[DOC-VECTOR type="context" content="purpose='Find an active CIL chunk by chunk name'; No verified external callers; imports: .framework/mcp/shared/db.ts"]
89
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
90
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
91
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
92
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
93
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
94
+ // CIL[BEGIN stable_id="~/.framework/mcp/tools/cil/context.ts:blastRadiusBFS:function" type="function" identity="blastRadiusBFS" parent="~/.framework/mcp/tools/cil/context.ts"]
95
+ // CIL[DOC-MACRO purpose="Compute transitive callers of a chunk up to a max depth"]
96
+ // CIL[DOC-RESPONSIBILITY responsibility="Return a list of caller chunks with depth metadata"]
97
+ // CIL[DOC-CONTEXT module="mcp-cil-context" layer="analysis" related_files=".framework/mcp/shared/db.ts,.framework/mcp/tools/cil/context.ts"]
98
+ // CIL[DOC-CONTRACT inputs="chunkId, maxDepth, callsLinkTypeId" outputs="caller records" errors="none" side_effects="queries links and items tables"]
99
+ // CIL[DOC-MICRO logic_summary="Breadth-first search over cil_calls links in reverse direction" notable_decisions="[]"]
100
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
101
+ // CIL[DOC-STABILITY stability="stable"]
102
+ async function blastRadiusBFS(
103
+ chunkId: string, maxDepth: number, callsLinkTypeId: string
104
+ ): Promise<{ chunk_id: string; chunk_name: string; file_path: string; depth: number }[]> {
105
+ const visited = new Set([chunkId])
106
+ const queue: { id: string; depth: number }[] = [{ id: chunkId, depth: 0 }]
107
+ const result: { chunk_id: string; chunk_name: string; file_path: string; depth: number }[] = []
108
+
109
+ while (queue.length > 0) {
110
+ const { id, depth } = queue.shift()!
111
+ if (depth >= maxDepth) continue
112
+ const { data: callerLinks } = await adminDb.from('links').select('source_id')
113
+ .eq('link_type_id', callsLinkTypeId).eq('target_id', id)
114
+ for (const link of callerLinks || []) {
115
+ if (visited.has(link.source_id)) continue
116
+ visited.add(link.source_id)
117
+ const c = await getChunkById(link.source_id)
118
+ if (!c) continue
119
+ result.push({ chunk_id: c.id, chunk_name: c.data?.chunk_name || '', file_path: c.data?.file_path || '', depth: depth + 1 })
120
+ queue.push({ id: c.id, depth: depth + 1 })
121
+ }
122
+ }
123
+ return result
124
+ }
125
+ // CIL[END stable_id="~/.framework/mcp/tools/cil/context.ts:blastRadiusBFS:function"]
126
+
127
+ // CIL[BEGIN stable_id="~/.framework/mcp/tools/cil/context.ts:safetyVerdict:function" type="function" identity="safetyVerdict" parent="~/.framework/mcp/tools/cil/context.ts"]
128
+ // CIL[DOC-VECTOR type="context" content="purpose='Compute transitive callers of a chunk up to a max depth'; No verified external callers; imports: .framework/mcp/shared/db.ts"]
129
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
130
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
131
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
132
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
133
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
134
+ // CIL[DOC-MACRO purpose="Assess edit safety for a chunk based on stability, blast radius, docs, and config dependencies"]
135
+ // CIL[DOC-RESPONSIBILITY responsibility="Return a safety level and list of risk flags"]
136
+ // CIL[DOC-CONTEXT module="mcp-cil-context" layer="analysis" related_files=".framework/mcp/shared/db.ts,.framework/mcp/tools/cil/context.ts"]
137
+ // CIL[DOC-CONTRACT inputs="chunk, blastRadius, configDeps" outputs="{level, flags, summary}" errors="none" side_effects="none"]
138
+ // CIL[DOC-MICRO logic_summary="Inspects chunk data and derived metrics to classify risk" notable_decisions="[]"]
139
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
140
+ // CIL[DOC-STABILITY stability="stable"]
141
+ function safetyVerdict(chunk: any, blastRadius: any[], configDeps: any[]) {
142
+ const d = chunk.data || {}
143
+ const flags: string[] = []
144
+ if (d.stability === 'stable') flags.push('stability=stable: downstream consumers may break on signature changes')
145
+ if (blastRadius.length >= 10) flags.push(`high blast radius: ${blastRadius.length} transitive callers`)
146
+ else if (blastRadius.length >= 3) flags.push(`moderate blast radius: ${blastRadius.length} callers`)
147
+ if (configDeps.length > 0) flags.push(`reads ${configDeps.length} config field(s)`)
148
+ if (!d.macro && !d.micro) flags.push('no contract documentation')
149
+ if (d.is_dead_code) flags.push('flagged as dead code')
150
+ const level = flags.length === 0 ? 'safe' : (d.stability === 'stable' || blastRadius.length >= 10) ? 'risky' : 'caution'
151
+ return { level, flags, summary: level === 'safe' ? 'Low risk.' : `${level === 'risky' ? 'High' : 'Moderate'} risk — review flags before editing.` }
152
+ }
153
+ // CIL[DOC-VECTOR type="context" content="purpose='Assess edit safety for a chunk based on stability, blast radius, docs, and config dependencies'; No verified external callers; imports: .framework/mcp/shared/db.ts"]
154
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
155
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
156
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
157
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
158
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
159
+ // CIL[END stable_id="~/.framework/mcp/tools/cil/context.ts:safetyVerdict:function"]
160
+
161
+ // CIL[BEGIN stable_id="~/.framework/mcp/tools/cil/context.ts:buildBrief:function" type="function" identity="buildBrief" parent="~/.framework/mcp/tools/cil/context.ts"]
162
+ // CIL[DOC-MACRO purpose="Assemble a full pre-edit context brief for a chunk"]
163
+ // CIL[DOC-RESPONSIBILITY responsibility="Return contract, blast radius, callees, tests, config deps, and safety verdict"]
164
+ // CIL[DOC-CONTEXT module="mcp-cil-context" layer="analysis" related_files=".framework/mcp/shared/db.ts,.framework/mcp/tools/cil/context.ts"]
165
+ // CIL[DOC-CONTRACT inputs="chunk: any, maxDepth: number = 4" outputs="context brief object" errors="none" side_effects="queries multiple link and item tables"]
166
+ // CIL[DOC-MICRO logic_summary="Resolves link types, computes blast radius, gathers callees, tests, and config dependencies" notable_decisions="[]"]
167
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
168
+ // CIL[DOC-STABILITY stability="stable"]
169
+ async function buildBrief(chunk: any, maxDepth = 4) {
170
+ const d = chunk.data || {}
171
+ const [callsId, testedById, configuresId] = await Promise.all([
172
+ resolveLinkTypeId('cil_calls').catch(() => null),
173
+ resolveLinkTypeId('cil_tested_by').catch(() => null),
174
+ resolveLinkTypeId('cil_configures').catch(() => null),
175
+ ])
176
+
177
+ const blastRadius = callsId ? await blastRadiusBFS(chunk.id, maxDepth, callsId) : []
178
+
179
+ const callees: any[] = []
180
+ if (callsId) {
181
+ const { data: ls } = await adminDb.from('links').select('target_id').eq('link_type_id', callsId).eq('source_id', chunk.id)
182
+ for (const l of ls || []) {
183
+ const c = await getChunkById(l.target_id)
184
+ if (c) callees.push({ chunk_id: c.id, chunk_name: c.data?.chunk_name || '', file_path: c.data?.file_path || '' })
185
+ }
186
+ }
187
+
188
+ const testPaths: { path: string; is_e2e: boolean }[] = []
189
+ if (testedById) {
190
+ const { data: ls } = await adminDb.from('links').select('target_id, metadata').eq('link_type_id', testedById).eq('source_id', chunk.id)
191
+ for (const l of ls || []) testPaths.push({ path: l.metadata?.test_path || '', is_e2e: l.metadata?.is_e2e || false })
192
+ }
193
+ if (testPaths.length === 0) {
194
+ if (d.test_unit_path) testPaths.push({ path: d.test_unit_path, is_e2e: false })
195
+ if (d.test_e2e_path) testPaths.push({ path: d.test_e2e_path, is_e2e: true })
196
+ }
197
+
198
+ const configDeps: { field_name: string; config_id: string }[] = []
199
+ if (configuresId) {
200
+ const { data: ls } = await adminDb.from('links').select('source_id, metadata').eq('link_type_id', configuresId).eq('target_id', chunk.id)
201
+ for (const l of ls || []) {
202
+ const ci = await getChunkById(l.source_id)
203
+ if (ci) configDeps.push({ field_name: ci.data?.field_name || '', config_id: l.source_id })
204
+ }
205
+ }
206
+
207
+ return {
208
+ chunk_id: chunk.id,
209
+ chunk_name: d.chunk_name || '',
210
+ file_path: d.file_path || '',
211
+ line_start: d.line_start || null,
212
+ line_end: d.line_end || null,
213
+ chunk_type: d.chunk_type || '',
214
+ source_set_id: d.source_set_id || null,
215
+ layer: d.layer || null,
216
+ contract: {
217
+ description: d.description || '',
218
+ module: d.module || '',
219
+ audience: d.audience || '',
220
+ macro: d.macro || '',
221
+ micro: d.micro || '',
222
+ input_spec: d.input_spec || '',
223
+ output_spec: d.output_spec || '',
224
+ side_effects: d.side_effects || '',
225
+ throws: d.throws || '',
226
+ stability: d.stability || 'unknown',
227
+ called_by: d.called_by || '',
228
+ calls: d.calls || '',
229
+ tags: d.tags || '',
230
+ complexity: d.complexity || '',
231
+ error_handling: d.error_handling || '',
232
+ related_files: d.related_files || d.see_also || '',
233
+ depends_on: d.depends_on || [],
234
+ depended_by: d.depended_by || [],
235
+ doc_confidence: d.doc_confidence || 'unverified_legacy',
236
+ validation_errors: d.doc_validation_errors || [],
237
+ },
238
+ hash: { stored_hash: d.stored_hash || null, hash_updated_at: d.hash_updated_at || null },
239
+ blast_radius: { direct_caller_count: blastRadius.filter(r => r.depth === 1).length, total_affected: blastRadius.length, callers: blastRadius },
240
+ calls: callees,
241
+ test_paths: testPaths,
242
+ config_dependencies: configDeps,
243
+ edit_safety: safetyVerdict(chunk, blastRadius, configDeps),
244
+ }
245
+ }
246
+ // CIL[DOC-VECTOR type="context" content="purpose='Assemble a full pre-edit context brief for a chunk'; No verified external callers; imports: .framework/mcp/shared/db.ts"]
247
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
248
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
249
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
250
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
251
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
252
+ // CIL[END stable_id="~/.framework/mcp/tools/cil/context.ts:buildBrief:function"]
253
+
254
+ // CIL[BEGIN stable_id="~/.framework/mcp/tools/cil/context.ts:register:function" type="function" identity="register" parent="~/.framework/mcp/tools/cil/context.ts"]
255
+ // CIL[DOC-MACRO purpose="Register CIL context tools on an MCP server"]
256
+ // CIL[DOC-RESPONSIBILITY responsibility="Expose spine_cil_find_context, spine_cil_get_context, and spine_cil_post_edit"]
257
+ // CIL[DOC-CONTEXT module="mcp-cil-context" layer="tools" related_files=".framework/mcp/shared/db.ts,.framework/mcp/tools/cil/context.ts"]
258
+ // CIL[DOC-CONTRACT inputs="server: McpServer" outputs="none" errors="none" side_effects="registers three tools"]
259
+ // CIL[DOC-MICRO logic_summary="Calls server.registerTool for find context, get context, and post edit handlers" notable_decisions="[]"]
260
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
261
+ // CIL[DOC-STABILITY stability="stable"]
262
+ export function register(server: McpServer) {
263
+
264
+ server.registerTool(
265
+ 'spine_cil_find_context',
266
+ {
267
+ description: 'Get a full pre-edit context brief for a chunk by name. Call this BEFORE editing any named chunk. Returns contract, blast radius, test paths, config dependencies, and an edit_safety verdict (safe/caution/risky).',
268
+ inputSchema: z.object({
269
+ stable_id: z.string().describe('Exact stable_id of the chunk'),
270
+ project_id: z.string().uuid().describe('The cil_project UUID'),
271
+ version_id: z.string().optional().describe('Optional version UUID to scope the lookup'),
272
+ max_depth: z.number().int().min(1).max(6).default(4).optional().describe('BFS depth for blast radius traversal'),
273
+ }),
274
+ },
275
+ async ({ stable_id, project_id, version_id, max_depth }) => {
276
+ const chunk = await getChunkByStableId(project_id, stable_id, version_id)
277
+ if (!chunk) {
278
+ return { content: [{ type: 'text', text: JSON.stringify({ ok: false, error: `No chunk with stable_id "${stable_id}" found`, hint: 'Use spine_cil_search_chunks to find chunks by description' }) }] }
279
+ }
280
+ const brief = await buildBrief(chunk, max_depth ?? 4)
281
+ return { content: [{ type: 'text', text: JSON.stringify({ ok: true, brief }, null, 2) }] }
282
+ }
283
+ )
284
+
285
+ server.registerTool(
286
+ 'spine_cil_get_context',
287
+ {
288
+ description: 'Get a full pre-edit context brief for a chunk by its stable_id. Returns contract, blast radius, test paths, config dependencies, and edit_safety verdict.',
289
+ inputSchema: z.object({
290
+ project_id: z.string().uuid().describe('The cil_project UUID'),
291
+ stable_id: z.string().describe('The chunk stable_id'),
292
+ version_id: z.string().optional().describe('Optional version UUID to scope the lookup'),
293
+ max_depth: z.number().int().min(1).max(6).default(4).optional(),
294
+ }),
295
+ },
296
+ async ({ project_id, stable_id, version_id, max_depth }) => {
297
+ const chunk = await getChunkByStableId(project_id, stable_id, version_id)
298
+ if (!chunk) return { content: [{ type: 'text', text: JSON.stringify({ ok: false, error: `Chunk not found: ${stable_id}` }) }] }
299
+ const brief = await buildBrief(chunk, max_depth ?? 4)
300
+ return { content: [{ type: 'text', text: JSON.stringify({ ok: true, brief }, null, 2) }] }
301
+ }
302
+ )
303
+
304
+ server.registerTool(
305
+ 'spine_cil_post_edit',
306
+ {
307
+ description: 'Call this AFTER editing a chunk. Returns its stored hash status and exact next_steps commands to reindex and validate the change.',
308
+ inputSchema: z.object({
309
+ project_id: z.string().uuid().describe('The cil_project UUID'),
310
+ stable_id: z.string().describe('The stable_id of the chunk you just edited'),
311
+ version_id: z.string().optional().describe('Optional version UUID to scope the lookup'),
312
+ }),
313
+ },
314
+ async ({ project_id, stable_id, version_id }) => {
315
+ const chunk = await getChunkByStableId(project_id, stable_id, version_id)
316
+ if (!chunk) return { content: [{ type: 'text', text: JSON.stringify({ ok: false, error: `Chunk not found: ${stable_id}` }) }] }
317
+ const d = chunk.data || {}
318
+ const result = {
319
+ ok: true,
320
+ stable_id,
321
+ project_id,
322
+ version_id: version_id || d.version_id || null,
323
+ chunk_name: d.chunk_name || '',
324
+ file_path: d.file_path || '',
325
+ stored_hash: d.stored_hash || null,
326
+ hash_updated_at: d.hash_updated_at || null,
327
+ action_required: d.stored_hash
328
+ ? 'Run spine_cil_reindex_chunk to refresh the stored hash after your edit, then spine_cil_validate_file to confirm drift is resolved.'
329
+ : 'No stored hash yet — run spine_cil_reindex_chunk or Pass 2 ingestion.',
330
+ next_steps: [
331
+ `spine_cil_reindex_chunk({ project_id: "${d.project_id || project_id}", stable_id: "${stable_id}", version_id: "${version_id || d.version_id || ''}", also_embed: true })`,
332
+ `spine_cil_validate_file({ project_id: "${d.project_id || project_id}", file_path: "${d.file_path || ''}" })`,
333
+ ],
334
+ }
335
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }
336
+ }
337
+ )
338
+ }
339
+ // CIL[DOC-VECTOR type="context" content="purpose='Register CIL context tools on an MCP server'; No verified external callers; imports: .framework/mcp/shared/db.ts"]
340
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
341
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
342
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
343
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
344
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
345
+ // CIL[END stable_id="~/.framework/mcp/tools/cil/context.ts:register:function"]
@@ -0,0 +1,242 @@
1
+ // CIL[BEGIN stable_id="~/.framework/mcp/tools/cil/graph.ts:module_header:module_header" type="module_header" identity="module_header" parent="~/.framework/mcp/tools/cil/graph.ts"]
2
+ // CIL[DOC-MACRO purpose="Import dependencies and set up the CIL graph MCP tool module"]
3
+ // CIL[DOC-RESPONSIBILITY responsibility="Declare module-level imports for the MCP CIL graph tool"]
4
+ // CIL[DOC-CONTEXT module="mcp-tools-cil" layer="tools" related_files=".framework/mcp/shared/db.ts"]
5
+ // CIL[DOC-CONTRACT inputs="none" outputs="none" errors="none" side_effects="none"]
6
+ // CIL[DOC-MICRO logic_summary="Import McpServer SDK, zod, and shared database helpers" notable_decisions="none"]
7
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
8
+ // CIL[DOC-STABILITY stability="stable"]
9
+ // CIL[DOC-AUDIENCE audience="maintainer"]
10
+
11
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
12
+ import { z } from 'zod'
13
+ import { adminDb, resolveLinkTypeId, fetchFunction } from '../../shared/db.js'
14
+ // CIL[DOC-VECTOR type="context" content="purpose='Import dependencies and set up the CIL graph MCP tool module'; No verified external callers; imports: .framework/mcp/shared/db.ts"]
15
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
16
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
17
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
18
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
19
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
20
+ // CIL[END stable_id="~/.framework/mcp/tools/cil/graph.ts:module_header:module_header"]
21
+
22
+ // CIL[BEGIN stable_id="~/.framework/mcp/tools/cil/graph.ts:getChunkById:function" type="function" identity="getChunkById" parent="~/.framework/mcp/tools/cil/graph.ts"]
23
+ // CIL[DOC-MACRO purpose="Fetch a single CIL chunk by its primary item id"]
24
+ // CIL[DOC-RESPONSIBILITY responsibility="Load a chunk record from the items table by id"]
25
+ // CIL[DOC-CONTEXT module="mcp-tools-cil" layer="tools" related_files=".framework/mcp/shared/db.ts,.framework/mcp/tools/cil/graph.ts"]
26
+ // CIL[DOC-CONTRACT inputs="id: string" outputs="chunk record or null" errors="none" side_effects="database read"]
27
+ // CIL[DOC-MICRO logic_summary="Query adminDb items table for id, title, data matching the given id and return the data" notable_decisions="Uses maybeSingle to return null when no match"]
28
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
29
+ // CIL[DOC-STABILITY stability="stable"]
30
+ async function getChunkById(id: string) {
31
+ const { data } = await adminDb.from('items').select('id, title, data')
32
+ .eq('id', id).maybeSingle()
33
+ return data
34
+ }
35
+ // CIL[DOC-VECTOR type="context" content="purpose='Fetch a single CIL chunk by its primary item id'; No verified external callers; imports: .framework/mcp/shared/db.ts"]
36
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
37
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
38
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
39
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
40
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
41
+ // CIL[END stable_id="~/.framework/mcp/tools/cil/graph.ts:getChunkById:function"]
42
+
43
+ // CIL[BEGIN stable_id="~/.framework/mcp/tools/cil/graph.ts:getChunkByStableId:function" type="function" identity="getChunkByStableId" parent="~/.framework/mcp/tools/cil/graph.ts"]
44
+ // CIL[DOC-MACRO purpose="Fetch a CIL chunk by its stable_id within a project and optional version"]
45
+ // CIL[DOC-RESPONSIBILITY responsibility="Locate a unique active CIL chunk by stable_id"]
46
+ // CIL[DOC-CONTEXT module="mcp-tools-cil" layer="tools" related_files=".framework/mcp/shared/db.ts,.framework/mcp/tools/cil/graph.ts"]
47
+ // CIL[DOC-CONTRACT inputs="projectId: string, stableId: string, versionId?: string" outputs="chunk record or null" errors="none" side_effects="database read"]
48
+ // CIL[DOC-MICRO logic_summary="Filter items by kb_type, project_id, stable_id, is_active and optional version_id, then return data" notable_decisions="Casts query to any when adding the optional version filter"]
49
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
50
+ // CIL[DOC-STABILITY stability="stable"]
51
+ async function getChunkByStableId(projectId: string, stableId: string, versionId?: string) {
52
+ let q = adminDb.from('items').select('id, title, data')
53
+ .filter('data->>kb_type', 'eq', 'cil_chunk')
54
+ .filter('data->>project_id', 'eq', projectId)
55
+ .filter('data->>stable_id', 'eq', stableId)
56
+ .eq('is_active', true)
57
+ if (versionId) q = (q as any).filter('data->>version_id', 'eq', versionId)
58
+ const { data } = await (q as any).maybeSingle()
59
+ return data
60
+ }
61
+ // CIL[DOC-VECTOR type="context" content="purpose='Fetch a CIL chunk by its stable_id within a project and optional version'; No verified external callers; imports: .framework/mcp/shared/db.ts"]
62
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
63
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
64
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
65
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
66
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
67
+ // CIL[END stable_id="~/.framework/mcp/tools/cil/graph.ts:getChunkByStableId:function"]
68
+
69
+ // CIL[BEGIN stable_id="~/.framework/mcp/tools/cil/graph.ts:register:function" type="function" identity="register" parent="~/.framework/mcp/tools/cil/graph.ts"]
70
+ // CIL[DOC-MACRO purpose="Register CIL graph query tools on an MCP server"]
71
+ // CIL[DOC-RESPONSIBILITY responsibility="Wire up MCP tools for blast radius, callers, file chunks and graph diff"]
72
+ // CIL[DOC-CONTEXT module="mcp-tools-cil" layer="tools" related_files=".framework/mcp/shared/db.ts,.framework/mcp/tools/cil/graph.ts"]
73
+ // CIL[DOC-CONTRACT inputs="server: McpServer" outputs="registered tool handlers" errors="throws if cil_calls link type or chunk is not found" side_effects="registers tools on the server and queries the database and links table"]
74
+ // CIL[DOC-MICRO logic_summary="Use server.registerTool to expose four CIL graph introspection endpoints backed by Supabase items and links" notable_decisions="Returns JSON-stringified content; uses BFS for blast radius; filters symbol references for direct callers; delegates graph_diff to fetchFunction"]
75
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
76
+ // CIL[DOC-STABILITY stability="stable"]
77
+ export function register(server: McpServer) {
78
+
79
+ server.registerTool(
80
+ 'spine_cil_blast_radius',
81
+ {
82
+ description: 'Find all chunks that transitively call a given chunk (BFS traversal). Use this before refactoring to understand how many callers will be affected.',
83
+ inputSchema: z.object({
84
+ project_id: z.string().uuid().describe('The cil_project UUID'),
85
+ stable_id: z.string().describe('The chunk stable_id to analyse'),
86
+ version_id: z.string().optional().describe('Optional version UUID to scope the lookup'),
87
+ max_depth: z.number().int().min(1).max(6).default(4).optional().describe('Maximum BFS hop depth'),
88
+ }),
89
+ },
90
+ async ({ project_id, stable_id, version_id, max_depth = 4 }) => {
91
+ let callsId: string
92
+ try { callsId = await resolveLinkTypeId('cil_calls') }
93
+ catch { return { content: [{ type: 'text', text: JSON.stringify({ error: 'cil_calls link type not found — run Pass 4b first' }) }] } }
94
+
95
+ const chunk = await getChunkByStableId(project_id, stable_id, version_id)
96
+ if (!chunk) return { content: [{ type: 'text', text: JSON.stringify({ error: `Chunk not found: ${stable_id}` }) }] }
97
+
98
+ const startId = chunk.id
99
+ const visited = new Set([startId])
100
+ const queue: { id: string; depth: number }[] = [{ id: startId, depth: 0 }]
101
+ const callers: { stable_id: string; chunk_name: string; file_path: string; depth: number }[] = []
102
+
103
+ while (queue.length > 0) {
104
+ const { id, depth } = queue.shift()!
105
+ if (depth >= max_depth) continue
106
+ const { data: links } = await adminDb.from('links').select('source_id')
107
+ .eq('link_type_id', callsId).eq('target_id', id)
108
+ for (const l of links || []) {
109
+ if (visited.has(l.source_id)) continue
110
+ visited.add(l.source_id)
111
+ const c = await getChunkById(l.source_id)
112
+ if (!c) continue
113
+ callers.push({ stable_id: c.data?.stable_id || c.id, chunk_name: c.data?.chunk_name || '', file_path: c.data?.file_path || '', depth: depth + 1 })
114
+ queue.push({ id: c.id, depth: depth + 1 })
115
+ }
116
+ }
117
+
118
+ const result = {
119
+ stable_id,
120
+ chunk_name: chunk.data?.chunk_name || '',
121
+ file_path: chunk.data?.file_path || '',
122
+ max_depth,
123
+ direct_callers: callers.filter(c => c.depth === 1).length,
124
+ total_affected: callers.length,
125
+ callers,
126
+ }
127
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }
128
+ }
129
+ )
130
+
131
+ server.registerTool(
132
+ 'spine_cil_get_callers',
133
+ {
134
+ description: 'Get the direct callers and symbol references (imports/references) of a chunk (depth-1 only). Faster than spine_cil_blast_radius when you just need to know what directly uses a chunk.',
135
+ inputSchema: z.object({
136
+ project_id: z.string().uuid().describe('The cil_project UUID'),
137
+ stable_id: z.string().describe('The chunk stable_id'),
138
+ version_id: z.string().optional().describe('Optional version UUID to scope the lookup'),
139
+ }),
140
+ },
141
+ async ({ project_id, stable_id, version_id }) => {
142
+ let callsId: string
143
+ try { callsId = await resolveLinkTypeId('cil_calls') }
144
+ catch { return { content: [{ type: 'text', text: JSON.stringify({ error: 'cil_calls link type not found' }) }] } }
145
+
146
+ const chunk = await getChunkByStableId(project_id, stable_id, version_id)
147
+ if (!chunk) return { content: [{ type: 'text', text: JSON.stringify({ error: `Chunk not found: ${stable_id}` }) }] }
148
+
149
+ const { data: links } = await adminDb.from('links').select('source_id, data, metadata')
150
+ .eq('link_type_id', callsId).eq('target_id', chunk.id)
151
+
152
+ const callers = []
153
+ for (const l of links || []) {
154
+ const c = await getChunkById(l.source_id)
155
+ if (c) callers.push({ stable_id: c.data?.stable_id || c.id, chunk_name: c.data?.chunk_name || '', file_path: c.data?.file_path || '' })
156
+ }
157
+
158
+ return { content: [{ type: 'text', text: JSON.stringify({ stable_id, caller_count: callers.length, callers }, null, 2) }] }
159
+ }
160
+ )
161
+
162
+ server.registerTool(
163
+ 'spine_cil_get_file_chunks',
164
+ {
165
+ description: 'List all indexed chunks for a file, sorted by line number. Useful for understanding the structure of a file before editing.',
166
+ inputSchema: z.object({
167
+ project_id: z.string().uuid().describe('The cil_project UUID'),
168
+ file_path: z.string().describe('Relative file path from repo root, e.g. ".framework/cli/commands/auth.ts"'),
169
+ }),
170
+ },
171
+ async ({ project_id, file_path }) => {
172
+ const { data: chunks } = await adminDb.from('items').select('id, title, data')
173
+ .filter('data->>kb_type', 'eq', 'cil_chunk')
174
+ .filter('data->>project_id', 'eq', project_id)
175
+ .filter('data->>file_path', 'eq', file_path)
176
+ .eq('is_active', true)
177
+
178
+ if (!chunks || chunks.length === 0) {
179
+ return { content: [{ type: 'text', text: JSON.stringify({ error: `No chunks found for file: ${file_path}` }) }] }
180
+ }
181
+
182
+ const sorted = chunks
183
+ .map((c: any) => ({
184
+ id: c.id,
185
+ stable_id: c.data?.stable_id || '',
186
+ version_id: c.data?.version_id || '',
187
+ chunk_name: c.data?.chunk_name || '',
188
+ chunk_type: c.data?.chunk_type || '',
189
+ line_start: c.data?.line_start || 0,
190
+ line_end: c.data?.line_end || 0,
191
+ macro: c.data?.macro || '',
192
+ doc_confidence: c.data?.doc_confidence || 'unverified_legacy',
193
+ is_dead_code: c.data?.is_dead_code || false,
194
+ stability: c.data?.stability || '',
195
+ }))
196
+ .sort((a: any, b: any) => a.line_start - b.line_start)
197
+
198
+ return {
199
+ content: [{
200
+ type: 'text',
201
+ text: JSON.stringify({
202
+ project_id,
203
+ file_path,
204
+ chunk_count: sorted.length,
205
+ chunks: sorted,
206
+ }, null, 2),
207
+ }],
208
+ }
209
+ }
210
+ )
211
+
212
+ server.registerTool(
213
+ 'spine_cil_graph_diff',
214
+ {
215
+ description: 'Compare the CIL call and import graph between two project versions. Returns added, removed, and unchanged edges keyed by stable IDs and file paths.',
216
+ inputSchema: z.object({
217
+ project_id: z.string().uuid().describe('The cil_project UUID'),
218
+ from_version_id: z.string().uuid().describe('The source version UUID to diff from'),
219
+ to_version_id: z.string().uuid().describe('The target version UUID to diff to'),
220
+ }),
221
+ },
222
+ async ({ project_id, from_version_id, to_version_id }) => {
223
+ try {
224
+ const result = await fetchFunction('cil-graph', 'graph_diff', {
225
+ project_id,
226
+ from_version_id,
227
+ to_version_id,
228
+ })
229
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }
230
+ } catch (err: any) {
231
+ return { content: [{ type: 'text', text: JSON.stringify({ error: err?.message || String(err) }) }] }
232
+ }
233
+ }
234
+ )
235
+ }
236
+ // CIL[DOC-VECTOR type="context" content="purpose='Register CIL graph query tools on an MCP server'; No verified external callers; imports: .framework/mcp/shared/db.ts"]
237
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
238
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
239
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
240
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
241
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
242
+ // CIL[END stable_id="~/.framework/mcp/tools/cil/graph.ts:register:function"]