spine-framework 0.3.144 → 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 +20 -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,791 @@
1
+ // CIL[BEGIN stable_id="~/.framework/mcp/tools/cil/project.ts:module_header:module_header" type="module_header" identity="module_header" parent="~/.framework/mcp/tools/cil/project.ts"]
2
+ // CIL[DOC-MACRO purpose="Module header for CIL project MCP tools, importing dependencies used for project lifecycle and version management"]
3
+ // CIL[DOC-RESPONSIBILITY responsibility="Import MCP server SDK, Zod, CIL pipeline utilities, and shared database helpers required by this tool module"]
4
+ // CIL[DOC-CONTEXT module="mcp/tools/cil/project" layer="mcp" related_files=".framework/functions/_shared/cil.ts,.framework/mcp/shared/db.ts"]
5
+ // CIL[DOC-CONTRACT inputs="" outputs="" errors="" side_effects=""]
6
+ // CIL[DOC-MICRO logic_summary="Sets up the module imports and makes the register function available for MCP server wiring" notable_decisions="[]"]
7
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
8
+ // CIL[DOC-STABILITY stability="stable"]
9
+ // CIL[DOC-AUDIENCE audience="ai"]
10
+
11
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
12
+ import { z } from 'zod'
13
+ import { PIPELINE_STATUS_RANK, pipelineRank } from '../../../functions/_shared/cil.js'
14
+ import { adminDb, fetchFunction } from '../../shared/db.js'
15
+ // CIL[DOC-VECTOR type="context" content="purpose='Module header for CIL project MCP tools, importing dependencies used for project lifecycle and version management'; No verified external callers; imports: .framework/functions/_shared/cil.ts,.framework/mcp/shared/db.ts"]
16
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
17
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
18
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
19
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
20
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
21
+ // CIL[END stable_id="~/.framework/mcp/tools/cil/project.ts:module_header:module_header"]
22
+
23
+ // CIL[BEGIN stable_id="~/.framework/mcp/tools/cil/project.ts:getCompletedSteps:function" type="function" identity="getCompletedSteps" parent="~/.framework/mcp/tools/cil/project.ts"]
24
+ // CIL[DOC-MACRO purpose="Compute the list of completed pipeline step indices for a file based on its current status"]
25
+ // CIL[DOC-RESPONSIBILITY responsibility="Convert a file's pipeline status rank into the set of completed step numbers"]
26
+ // CIL[DOC-CONTEXT module="mcp/tools/cil/project" layer="mcp" related_files=".framework/functions/_shared/cil.ts,.framework/mcp/shared/db.ts,.framework/mcp/tools/cil/project.ts"]
27
+ // CIL[DOC-CONTRACT inputs="file_path: string, fileStatuses: Record<string, string>" outputs="number[]" errors="none" side_effects="none"]
28
+ // CIL[DOC-MICRO logic_summary="Looks up the file status rank, caps it to the maximum rank, and returns a range of completed step numbers" notable_decisions="[]"]
29
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
30
+ // CIL[DOC-STABILITY stability="stable"]
31
+ function getCompletedSteps(file_path: string, fileStatuses: Record<string, string>): number[] {
32
+ const rank = pipelineRank(fileStatuses[file_path])
33
+ const maxRank = Math.max(...Object.values(PIPELINE_STATUS_RANK))
34
+ const capped = Math.min(rank, maxRank)
35
+ const completed: number[] = []
36
+ for (let i = 0; i <= capped; i++) completed.push(i)
37
+ return completed
38
+ }
39
+ // CIL[DOC-VECTOR type="context" content="purpose='Compute the list of completed pipeline step indices for a file based on its current status'; No verified external callers; imports: .framework/functions/_shared/cil.ts,.framework/mcp/shared/db.ts"]
40
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
41
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
42
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
43
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
44
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
45
+ // CIL[END stable_id="~/.framework/mcp/tools/cil/project.ts:getCompletedSteps:function"]
46
+
47
+ // CIL[BEGIN stable_id="~/.framework/mcp/tools/cil/project.ts:getReadySteps:function" type="function" identity="getReadySteps" parent="~/.framework/mcp/tools/cil/project.ts"]
48
+ // CIL[DOC-MACRO purpose="Determine which pipeline steps are ready to be executed next"]
49
+ // CIL[DOC-RESPONSIBILITY responsibility="Return step numbers whose prerequisites are all completed but which are not yet completed"]
50
+ // CIL[DOC-CONTEXT module="mcp/tools/cil/project" layer="mcp" related_files=".framework/functions/_shared/cil.ts,.framework/mcp/shared/db.ts,.framework/mcp/tools/cil/project.ts"]
51
+ // CIL[DOC-CONTRACT inputs="file_path: string, fileStatuses: Record<string, string>" outputs="number[]" errors="none" side_effects="none"]
52
+ // CIL[DOC-MICRO logic_summary="Iterates steps 0-8 and collects those not completed with all prerequisites fulfilled" notable_decisions="[]"]
53
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
54
+ // CIL[DOC-STABILITY stability="stable"]
55
+ function getReadySteps(file_path: string, fileStatuses: Record<string, string>): number[] {
56
+ const completed = getCompletedSteps(file_path, fileStatuses)
57
+ const ready: number[] = []
58
+ for (let step = 0; step <= 8; step++) {
59
+ const prerequisites = Array.from({ length: step }, (_, i) => i)
60
+ if (!completed.includes(step) && prerequisites.every(p => completed.includes(p))) {
61
+ ready.push(step)
62
+ }
63
+ }
64
+ return ready
65
+ }
66
+ // CIL[DOC-VECTOR type="context" content="purpose='Determine which pipeline steps are ready to be executed next'; No verified external callers; imports: .framework/functions/_shared/cil.ts,.framework/mcp/shared/db.ts"]
67
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
68
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
69
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
70
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
71
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
72
+ // CIL[END stable_id="~/.framework/mcp/tools/cil/project.ts:getReadySteps:function"]
73
+
74
+ // CIL[BEGIN stable_id="~/.framework/mcp/tools/cil/project.ts:getBlockedSteps:function" type="function" identity="getBlockedSteps" parent="~/.framework/mcp/tools/cil/project.ts"]
75
+ // CIL[DOC-MACRO purpose="Identify pipeline steps that are neither completed nor ready to execute"]
76
+ // CIL[DOC-RESPONSIBILITY responsibility="Return step numbers that cannot yet be executed because prerequisites are missing"]
77
+ // CIL[DOC-STABILITY stability="internal"]
78
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
79
+ // CIL[DOC-CONTEXT module="mcp/tools/cil/project" layer="mcp" related_files=".framework/functions/_shared/cil.ts,.framework/mcp/shared/db.ts,.framework/mcp/tools/cil/project.ts"]
80
+ // CIL[DOC-CONTRACT inputs="file_path: string, fileStatuses: Record<string, string>" outputs="number[]" errors="none" side_effects="none"]
81
+ // CIL[DOC-MICRO logic_summary="Compares all steps 0-8 against completed and ready sets and returns the remainder" notable_decisions="[]"]
82
+ function getBlockedSteps(file_path: string, fileStatuses: Record<string, string>): number[] {
83
+ const completed = getCompletedSteps(file_path, fileStatuses)
84
+ const ready = getReadySteps(file_path, fileStatuses)
85
+ const blocked: number[] = []
86
+ for (let step = 0; step <= 8; step++) {
87
+ if (!completed.includes(step) && !ready.includes(step)) blocked.push(step)
88
+ }
89
+ return blocked
90
+ }
91
+ // CIL[END stable_id="~/.framework/mcp/tools/cil/project.ts:getBlockedSteps:function"]
92
+
93
+ // CIL[DOC-VECTOR type="context" content="purpose='Identify pipeline steps that are neither completed nor ready to execute'; No verified external callers; imports: .framework/functions/_shared/cil.ts,.framework/mcp/shared/db.ts"]
94
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
95
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
96
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
97
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
98
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
99
+ // CIL[BEGIN stable_id="~/.framework/mcp/tools/cil/project.ts:calculateProgress:function" type="function" identity="calculateProgress" parent="~/.framework/mcp/tools/cil/project.ts"]
100
+ // CIL[DOC-MACRO purpose="Calculate a percentage progress value for a file based on its pipeline status rank"]
101
+ // CIL[DOC-RESPONSIBILITY responsibility="Convert a file's pipeline status rank into a 0-100 progress percentage"]
102
+ // CIL[DOC-CONTEXT module="mcp/tools/cil/project" layer="mcp" related_files=".framework/functions/_shared/cil.ts,.framework/mcp/shared/db.ts,.framework/mcp/tools/cil/project.ts"]
103
+ // CIL[DOC-CONTRACT inputs="file_path: string, fileStatuses: Record<string, string>" outputs="number" errors="none" side_effects="none"]
104
+ // CIL[DOC-MICRO logic_summary="Divides the capped status rank by the maximum rank and rounds to the nearest integer" notable_decisions="[]"]
105
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
106
+ // CIL[DOC-STABILITY stability="stable"]
107
+ function calculateProgress(file_path: string, fileStatuses: Record<string, string>): number {
108
+ const rank = pipelineRank(fileStatuses[file_path])
109
+ const maxRank = Math.max(...Object.values(PIPELINE_STATUS_RANK))
110
+ return Math.round((Math.min(rank, maxRank) / maxRank) * 100)
111
+ }
112
+ // CIL[DOC-VECTOR type="context" content="purpose='Calculate a percentage progress value for a file based on its pipeline status rank'; No verified external callers; imports: .framework/functions/_shared/cil.ts,.framework/mcp/shared/db.ts"]
113
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
114
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
115
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
116
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
117
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
118
+ // CIL[END stable_id="~/.framework/mcp/tools/cil/project.ts:calculateProgress:function"]
119
+
120
+ // CIL[BEGIN stable_id="~/.framework/mcp/tools/cil/project.ts:register:function" type="function" identity="register" parent="~/.framework/mcp/tools/cil/project.ts"]
121
+ // CIL[DOC-MACRO purpose="Register CIL project management and version lifecycle tools on an MCP server"]
122
+ // CIL[DOC-RESPONSIBILITY responsibility="Bind MCP tool handlers for listing projects, reporting status, managing versions, and updating pipeline status"]
123
+ // CIL[DOC-CONTEXT module="mcp/tools/cil/project" layer="mcp" related_files=".framework/functions/_shared/cil.ts,.framework/mcp/shared/db.ts,.framework/mcp/tools/cil/project.ts"]
124
+ // CIL[DOC-CONTRACT inputs="server: McpServer" outputs="void" errors="Throws if fetchFunction or database calls fail" side_effects="Registers tools on the provided MCP server and may read or update database records"]
125
+ // CIL[DOC-MICRO logic_summary="Registers a suite of CIL project tools including status reporting, version lifecycle, file selection, and pipeline publishing" notable_decisions="[]"]
126
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
127
+ // CIL[DOC-STABILITY stability="stable"]
128
+ export function register(server: McpServer) {
129
+
130
+ server.registerTool(
131
+ 'spine_cil_list_projects',
132
+ {
133
+ description: 'List all CIL projects registered in the database. Returns project names, repo paths, and basic file/chunk counts.',
134
+ inputSchema: z.object({}),
135
+ },
136
+ async () => {
137
+ const { data } = await adminDb.from('items').select('id, title, data')
138
+ .filter('data->>kb_type', 'eq', 'cil_project')
139
+ .eq('is_active', true)
140
+ .order('created_at', { ascending: true })
141
+
142
+ const projects = (data || []).map((p: any) => ({
143
+ id: p.id,
144
+ name: p.title,
145
+ repo_path: p.data?.repo_path || '',
146
+ language_mix: p.data?.language_mix || '',
147
+ file_count: p.data?.file_count || 0,
148
+ chunk_count: p.data?.chunk_count || 0,
149
+ ingestion_status: p.data?.ingestion_status || 'not_started',
150
+ last_ingested_at: p.data?.last_ingested_at || null,
151
+ selected_paths: p.data?.selected_paths || [],
152
+ }))
153
+
154
+ return { content: [{ type: 'text', text: JSON.stringify({ project_count: projects.length, projects }, null, 2) }] }
155
+ }
156
+ )
157
+
158
+ server.registerTool(
159
+ 'spine_cil_project_status',
160
+ {
161
+ description: 'Get the full pipeline status for a CIL project: unique file count (from chunks), chunk count, embedding coverage, documentation coverage, and link counts.',
162
+ inputSchema: z.object({
163
+ project_id: z.string().uuid().describe('The cil_project UUID'),
164
+ }),
165
+ },
166
+ async ({ project_id }) => {
167
+ const PAGE_SIZE = 1000
168
+
169
+ const [projectRes, totalChunksRes] = await Promise.allSettled([
170
+ adminDb.from('items').select('id, title, data').eq('id', project_id).maybeSingle(),
171
+ adminDb.from('items').select('*', { count: 'exact', head: true })
172
+ .filter('data->>kb_type', 'eq', 'cil_chunk')
173
+ .filter('data->>project_id', 'eq', project_id)
174
+ .eq('is_active', true),
175
+ ])
176
+
177
+ const project = projectRes.status === 'fulfilled' ? projectRes.value.data : null
178
+ const totalChunks = totalChunksRes.status === 'fulfilled' ? (totalChunksRes.value.count || 0) : 0
179
+
180
+ const chunks: any[] = []
181
+ let from = 0
182
+ while (true) {
183
+ const { data, error } = await adminDb.from('items').select('id, data')
184
+ .filter('data->>kb_type', 'eq', 'cil_chunk')
185
+ .filter('data->>project_id', 'eq', project_id)
186
+ .eq('is_active', true)
187
+ .order('id')
188
+ .range(from, from + PAGE_SIZE - 1)
189
+ if (error) break
190
+ if (!data || data.length === 0) break
191
+ chunks.push(...data)
192
+ if (data.length < PAGE_SIZE) break
193
+ from += PAGE_SIZE
194
+ }
195
+
196
+ const chunkIds = new Set(chunks.map((c: any) => c.id))
197
+ const embeddedIds = new Set<string>()
198
+ if (chunkIds.size > 0) {
199
+ let embFrom = 0
200
+ while (true) {
201
+ const { data: embeddings, error: embErr } = await adminDb.from('embeddings').select('document_id')
202
+ .order('id')
203
+ .range(embFrom, embFrom + PAGE_SIZE - 1)
204
+ if (embErr) break
205
+ if (!embeddings || embeddings.length === 0) break
206
+ for (const row of embeddings) {
207
+ if (chunkIds.has(row.document_id)) embeddedIds.add(row.document_id)
208
+ }
209
+ if (embeddings.length < PAGE_SIZE) break
210
+ embFrom += PAGE_SIZE
211
+ }
212
+ }
213
+
214
+ const embeddedChunks = chunks.filter((c: any) => embeddedIds.has(c.id)).length
215
+ const documentedChunks = chunks.filter((c: any) => c.data?.macro && c.data?.doc_confidence !== 'unverified_legacy').length
216
+ const verifiedChunks = chunks.filter((c: any) => c.data?.doc_confidence === 'human_verified').length
217
+ const aiChunks = chunks.filter((c: any) => c.data?.doc_confidence === 'ai_generated').length
218
+ const pendingReview = chunks.filter((c: any) => c.data?.review_status === 'pending_review').length
219
+ const deadCode = chunks.filter((c: any) => c.data?.is_dead_code).length
220
+
221
+ const uniqueFilePaths = new Set(chunks.map((c: any) => c.data?.file_path).filter(Boolean))
222
+
223
+ return {
224
+ content: [{
225
+ type: 'text',
226
+ text: JSON.stringify({
227
+ project_id,
228
+ project_name: project?.title || '',
229
+ repo_path: project?.data?.repo_path || '',
230
+ selected_paths: project?.data?.selected_paths || [],
231
+ pipeline: {
232
+ files: { total: uniqueFilePaths.size },
233
+ chunks: {
234
+ total: totalChunks,
235
+ dead_code: deadCode,
236
+ pending_review: pendingReview,
237
+ embedded: embeddedChunks,
238
+ embedding_coverage_pct: totalChunks > 0 ? Math.round((embeddedChunks / totalChunks) * 100) : 0,
239
+ documented: documentedChunks,
240
+ human_verified: verifiedChunks,
241
+ ai_generated: aiChunks,
242
+ doc_coverage_pct: totalChunks > 0 ? Math.round((documentedChunks / totalChunks) * 100) : 0,
243
+ },
244
+ },
245
+ }, null, 2),
246
+ }],
247
+ }
248
+ }
249
+ )
250
+
251
+ server.registerTool(
252
+ 'spine_cil_doc_gap_report',
253
+ {
254
+ description: 'List chunks that have no AI-generated or human-verified documentation. Use this to find chunks that need to be documented before they can be safely edited.',
255
+ inputSchema: z.object({
256
+ project_id: z.string().uuid().describe('The cil_project UUID'),
257
+ limit: z.number().int().min(1).max(100).default(20).optional(),
258
+ }),
259
+ },
260
+ async ({ project_id, limit = 20 }) => {
261
+ const { data: chunks } = await adminDb.from('items').select('id, title, data')
262
+ .filter('data->>kb_type', 'eq', 'cil_chunk')
263
+ .filter('data->>project_id', 'eq', project_id)
264
+ .filter('data->>doc_confidence', 'eq', 'unverified_legacy')
265
+ .eq('is_active', true)
266
+ .limit(limit)
267
+
268
+ const undocumented = (chunks || []).map((c: any) => ({
269
+ chunk_id: c.id,
270
+ chunk_name: c.data?.chunk_name || '',
271
+ file_path: c.data?.file_path || '',
272
+ chunk_type: c.data?.chunk_type || '',
273
+ line_start: c.data?.line_start || 0,
274
+ line_end: c.data?.line_end || 0,
275
+ }))
276
+
277
+ const { data: allChunks } = await adminDb.from('items').select('id')
278
+ .filter('data->>kb_type', 'eq', 'cil_chunk')
279
+ .filter('data->>project_id', 'eq', project_id)
280
+ .eq('is_active', true)
281
+
282
+ const total = allChunks?.length || 0
283
+ const undocCount = undocumented.length
284
+
285
+ return {
286
+ content: [{
287
+ type: 'text',
288
+ text: JSON.stringify({
289
+ project_id,
290
+ summary: {
291
+ total_chunks: total,
292
+ undocumented_shown: undocCount,
293
+ coverage_pct: total > 0 ? Math.round(((total - undocCount) / total) * 100) : 100,
294
+ },
295
+ undocumented,
296
+ }, null, 2),
297
+ }],
298
+ }
299
+ }
300
+ )
301
+
302
+ server.registerTool(
303
+ 'spine_cil_get_files_by_status',
304
+ {
305
+ description: `Get the ordered work queue for a pipeline step — all files in a project at a given pipeline status.
306
+ Use this at the start of each pipeline step to get the list of files to process.
307
+ Valid statuses: "0-discovered", "1-selected", "2-chunked", "3-documented", "4-chunk-validated", "5-doc-validated", "6-enriched", "7-embedded-mapped", "8-published".
308
+ Returns file_path, chunk_count, and last_updated for each file. Process files in the order returned — complete all steps for one file before moving to the next.`,
309
+ inputSchema: z.object({
310
+ project_id: z.string().uuid().describe('The cil_project UUID'),
311
+ status: z.string().describe('Pipeline status to filter by, e.g. "0-discovered", "2-chunked"'),
312
+ include_progress_info: z.boolean().default(false).describe('Include pipeline progress and dependency information'),
313
+ }),
314
+ },
315
+ async ({ project_id, status, include_progress_info }) => {
316
+ const { data: project } = await adminDb.from('items').select('data')
317
+ .eq('id', project_id).maybeSingle()
318
+ if (!project) {
319
+ return { content: [{ type: 'text', text: JSON.stringify({ ok: false, error: `Project not found: ${project_id}` }) }] }
320
+ }
321
+
322
+ const fileStatuses: Record<string, string> = project.data?.file_statuses || {}
323
+
324
+ const matchingPaths = Object.entries(fileStatuses)
325
+ .filter(([, v]) => v === status)
326
+ .map(([file_path]) => file_path)
327
+
328
+ if (matchingPaths.length === 0) {
329
+ return {
330
+ content: [{
331
+ type: 'text',
332
+ text: JSON.stringify({ ok: true, project_id, status, file_count: 0, files: [], message: `No files found with status "${status}"` }, null, 2),
333
+ }],
334
+ }
335
+ }
336
+
337
+ const { data: chunks } = await adminDb.from('items').select('data')
338
+ .filter('data->>kb_type', 'eq', 'cil_chunk')
339
+ .filter('data->>project_id', 'eq', project_id)
340
+ .eq('is_active', true)
341
+ .in('data->>file_path' as any, matchingPaths)
342
+
343
+ const chunkCountByFile: Record<string, number> = {}
344
+ for (const c of chunks || []) {
345
+ const fp = c.data?.file_path
346
+ if (fp) chunkCountByFile[fp] = (chunkCountByFile[fp] || 0) + 1
347
+ }
348
+
349
+ const files = matchingPaths.map(file_path => {
350
+ const baseFile = {
351
+ file_path,
352
+ status,
353
+ chunk_count: chunkCountByFile[file_path] || 0,
354
+ }
355
+
356
+ if (include_progress_info) {
357
+ const completedSteps = getCompletedSteps(file_path, fileStatuses)
358
+ const readySteps = getReadySteps(file_path, fileStatuses)
359
+ const blockedSteps = getBlockedSteps(file_path, fileStatuses)
360
+
361
+ return {
362
+ ...baseFile,
363
+ completed_steps: completedSteps,
364
+ ready_for: readySteps,
365
+ blocked_by: blockedSteps,
366
+ progress_percentage: calculateProgress(file_path, fileStatuses),
367
+ can_proceed_to: readySteps
368
+ }
369
+ }
370
+
371
+ return baseFile
372
+ })
373
+
374
+ return {
375
+ content: [{
376
+ type: 'text',
377
+ text: JSON.stringify({ ok: true, project_id, status, file_count: files.length, files }, null, 2),
378
+ }],
379
+ }
380
+ }
381
+ )
382
+
383
+ server.registerTool(
384
+ 'spine_cil_update_pipeline_status_flexible',
385
+ {
386
+ description: 'Update pipeline status with dependency tracking and progress monitoring for flexible processing.',
387
+ inputSchema: z.object({
388
+ project_id: z.string().uuid().describe('The cil_project UUID'),
389
+ file_path: z.string().describe('File path relative to repo root'),
390
+ status: z.string().describe('New pipeline status'),
391
+ step_completed: z.number().optional().describe('Step number that was just completed'),
392
+ force: z.boolean().default(false).describe('Force update even if dependencies not met'),
393
+ }),
394
+ },
395
+ async ({ project_id, file_path, status, step_completed, force }) => {
396
+
397
+ const { data: project } = await adminDb.from('items').select('data')
398
+ .eq('id', project_id).maybeSingle()
399
+
400
+ if (!project) {
401
+ return { content: [{ type: 'text', text: JSON.stringify({ ok: false, error: `Project not found: ${project_id}` }) }] }
402
+ }
403
+
404
+ if (!force) {
405
+ const fileStatuses = project.data?.file_statuses || {}
406
+ const completedSteps = getCompletedSteps(file_path, fileStatuses)
407
+
408
+ const statusRequirements: Record<string, number[]> = {
409
+ '0-discovered': [],
410
+ '1-selected': [0],
411
+ '2-chunked': [1],
412
+ '3-documented': [2],
413
+ '4-chunk-validated': [3],
414
+ '5-doc-validated': [4],
415
+ '6-enriched': [5],
416
+ '7-embedded-mapped': [6],
417
+ '8-published': [7],
418
+ }
419
+
420
+ const requiredSteps = statusRequirements[status] || []
421
+ const missingSteps = requiredSteps.filter(step => !completedSteps.includes(step))
422
+
423
+ if (missingSteps.length > 0) {
424
+ return {
425
+ content: [{
426
+ type: 'text',
427
+ text: JSON.stringify({
428
+ ok: false,
429
+ error: `Cannot update to ${status}. Missing prerequisite steps: ${missingSteps.join(', ')}`,
430
+ current_completed: completedSteps,
431
+ required: requiredSteps
432
+ })
433
+ }]
434
+ }
435
+ }
436
+ }
437
+
438
+ const fileStatuses = project.data?.file_statuses || {}
439
+ const previousStatus = fileStatuses[file_path]
440
+ fileStatuses[file_path] = status
441
+
442
+ const { error } = await adminDb.from('items')
443
+ .update({
444
+ data: {
445
+ ...project.data,
446
+ file_statuses: fileStatuses,
447
+ last_ingested_at: new Date().toISOString()
448
+ }
449
+ })
450
+ .eq('id', project_id)
451
+
452
+ if (error) {
453
+ return { content: [{ type: 'text', text: JSON.stringify({ ok: false, error: error.message }) }] }
454
+ }
455
+
456
+ const updatedState = {
457
+ file_path,
458
+ previous_status: previousStatus,
459
+ new_status: status,
460
+ step_completed,
461
+ completed_steps: getCompletedSteps(file_path, fileStatuses),
462
+ ready_for: getReadySteps(file_path, fileStatuses),
463
+ blocked_by: getBlockedSteps(file_path, fileStatuses),
464
+ progress_percentage: calculateProgress(file_path, fileStatuses)
465
+ }
466
+
467
+ return {
468
+ content: [{
469
+ type: 'text',
470
+ text: JSON.stringify({ ok: true, ...updatedState }, null, 2)
471
+ }],
472
+ }
473
+ }
474
+ )
475
+
476
+ server.registerTool(
477
+ 'spine_cil_list_files',
478
+ {
479
+ description: 'List indexed files for a CIL project with their status and chunk counts.',
480
+ inputSchema: z.object({
481
+ project_id: z.string().uuid().describe('The cil_project UUID'),
482
+ limit: z.number().int().min(1).max(1000).default(100).optional(),
483
+ }),
484
+ },
485
+ async ({ project_id, limit = 100 }) => {
486
+ const { data: project } = await adminDb.from('items').select('data')
487
+ .eq('id', project_id).maybeSingle()
488
+ if (!project) {
489
+ return { content: [{ type: 'text', text: JSON.stringify({ ok: false, error: `Project not found: ${project_id}` }) }] }
490
+ }
491
+
492
+ const { data: chunks, error } = await adminDb.from('items').select('id, data')
493
+ .filter('data->>kb_type', 'eq', 'cil_chunk')
494
+ .filter('data->>project_id', 'eq', project_id)
495
+ .eq('is_active', true)
496
+ .limit(limit)
497
+
498
+ if (error) {
499
+ return { content: [{ type: 'text', text: JSON.stringify({ ok: false, error: error.message }) }] }
500
+ }
501
+
502
+ const fileMap: Record<string, { chunk_count: number; statuses: Set<string> }> = {}
503
+ for (const c of chunks || []) {
504
+ const fp = c.data?.file_path || ''
505
+ if (!fp) continue
506
+ if (!fileMap[fp]) fileMap[fp] = { chunk_count: 0, statuses: new Set() }
507
+ fileMap[fp].chunk_count += 1
508
+ if (c.data?.status) fileMap[fp].statuses.add(c.data.status)
509
+ }
510
+
511
+ const fileStatuses: Record<string, string> = project.data?.file_statuses || {}
512
+ const files = Object.entries(fileMap).map(([file_path, info]) => ({
513
+ file_path,
514
+ status: fileStatuses[file_path] || null,
515
+ chunk_count: info.chunk_count,
516
+ chunk_statuses: Array.from(info.statuses),
517
+ })).sort((a, b) => a.file_path.localeCompare(b.file_path))
518
+
519
+ return { content: [{ type: 'text', text: JSON.stringify({ ok: true, project_id, file_count: files.length, files }, null, 2) }] }
520
+ }
521
+ )
522
+
523
+ server.registerTool(
524
+ 'spine_cil_list_versions',
525
+ {
526
+ description: 'List all versions for a CIL project.',
527
+ inputSchema: z.object({
528
+ project_id: z.string().describe('The cil_project UUID'),
529
+ }),
530
+ },
531
+ async ({ project_id }) => {
532
+ try {
533
+ const { data, error } = await adminDb.from('items')
534
+ .select('id, title, data')
535
+ .filter('data->>kb_type', 'eq', 'cil_version')
536
+ .filter('data->>project_id', 'eq', project_id)
537
+ .order('created_at', { ascending: false })
538
+ if (error) return { content: [{ type: 'text', text: JSON.stringify({ ok: false, error: error.message }) }] }
539
+ return { content: [{ type: 'text', text: JSON.stringify({ ok: true, versions: data || [] }) }] }
540
+ } catch (err: any) {
541
+ return { content: [{ type: 'text', text: JSON.stringify({ ok: false, error: err?.message || String(err) }) }] }
542
+ }
543
+ }
544
+ )
545
+
546
+ server.registerTool(
547
+ 'spine_cil_create_version',
548
+ {
549
+ description: 'Create a new CIL version for a project. The version must be valid semver; prerelease versions are automatically marked as iterations.',
550
+ inputSchema: z.object({
551
+ project_id: z.string().describe('The cil_project UUID'),
552
+ version: z.string().describe('Version string, e.g. "0.1.0" or "0.1.6-dev.467.1"'),
553
+ base_version: z.string().optional().describe('Optional base version string to copy from'),
554
+ }),
555
+ },
556
+ async ({ project_id, version, base_version }) => {
557
+ try {
558
+ const result = await fetchFunction('cil-project', 'create_version', {}, {
559
+ project_id,
560
+ version,
561
+ base_version,
562
+ })
563
+ return { content: [{ type: 'text', text: JSON.stringify({ ok: true, version: result.version }) }] }
564
+ } catch (err: any) {
565
+ return { content: [{ type: 'text', text: JSON.stringify({ ok: false, error: err?.message || String(err) }) }] }
566
+ }
567
+ }
568
+ )
569
+
570
+ server.registerTool(
571
+ 'spine_cil_set_active_version',
572
+ {
573
+ description: 'Set the active version for a CIL project.',
574
+ inputSchema: z.object({
575
+ project_id: z.string().describe('The cil_project UUID'),
576
+ version_id: z.string().describe('The cil_version UUID to make active'),
577
+ }),
578
+ },
579
+ async ({ project_id, version_id }) => {
580
+ try {
581
+ const result = await fetchFunction('cil-project', 'set_active_version', {}, {
582
+ project_id,
583
+ version_id,
584
+ })
585
+ return { content: [{ type: 'text', text: JSON.stringify({ ok: true, project: result.project, version_id: result.version_id }) }] }
586
+ } catch (err: any) {
587
+ return { content: [{ type: 'text', text: JSON.stringify({ ok: false, error: err?.message || String(err) }) }] }
588
+ }
589
+ }
590
+ )
591
+
592
+ server.registerTool(
593
+ 'spine_cil_publish_version',
594
+ {
595
+ description: 'Publish a new CIL version. Marks all other versions as inactive and sets it as the active and default version.',
596
+ inputSchema: z.object({
597
+ project_id: z.string().describe('The cil_project UUID'),
598
+ version: z.string().describe('Version string to publish, e.g. "0.2.0"'),
599
+ base_version: z.string().optional().describe('Optional base version whose prerelease iteration should be linked'),
600
+ }),
601
+ },
602
+ async ({ project_id, version, base_version }) => {
603
+ try {
604
+ const result = await fetchFunction('cil-project', 'publish_version', {}, {
605
+ project_id,
606
+ version,
607
+ base_version,
608
+ })
609
+ return { content: [{ type: 'text', text: JSON.stringify({ ok: true, version: result.version, project: result.project }) }] }
610
+ } catch (err: any) {
611
+ return { content: [{ type: 'text', text: JSON.stringify({ ok: false, error: err?.message || String(err) }) }] }
612
+ }
613
+ }
614
+ )
615
+
616
+ server.registerTool(
617
+ 'spine_cil_mark_versions_inactive',
618
+ {
619
+ description: 'Mark all CIL versions whose version string matches a pattern as inactive.',
620
+ inputSchema: z.object({
621
+ project_id: z.string().describe('The cil_project UUID'),
622
+ version_pattern: z.string().describe('Substring to match in version strings'),
623
+ }),
624
+ },
625
+ async ({ project_id, version_pattern }) => {
626
+ try {
627
+ const result = await fetchFunction('cil-project', 'mark_versions_inactive', {}, {
628
+ project_id,
629
+ version_pattern,
630
+ })
631
+ return { content: [{ type: 'text', text: JSON.stringify({ ok: true, ...result }) }] }
632
+ } catch (err: any) {
633
+ return { content: [{ type: 'text', text: JSON.stringify({ ok: false, error: err?.message || String(err) }) }] }
634
+ }
635
+ }
636
+ )
637
+
638
+ server.registerTool(
639
+ 'spine_cil_archive_versions',
640
+ {
641
+ description: 'Archive the oldest inactive CIL versions, keeping only the most recent N inactive versions.',
642
+ inputSchema: z.object({
643
+ project_id: z.string().describe('The cil_project UUID'),
644
+ keep_last_n: z.number().int().min(0).describe('Number of most recent inactive versions to keep'),
645
+ }),
646
+ },
647
+ async ({ project_id, keep_last_n }) => {
648
+ try {
649
+ const result = await fetchFunction('cil-project', 'archive_versions', {}, {
650
+ project_id,
651
+ keep_last_n,
652
+ })
653
+ return { content: [{ type: 'text', text: JSON.stringify({ ok: true, ...result }) }] }
654
+ } catch (err: any) {
655
+ return { content: [{ type: 'text', text: JSON.stringify({ ok: false, error: err?.message || String(err) }) }] }
656
+ }
657
+ }
658
+ )
659
+
660
+ server.registerTool(
661
+ 'spine_cil_create_project',
662
+ {
663
+ description: 'Create a new CIL project for a repository. The project will be created with discovery not yet run.',
664
+ inputSchema: z.object({
665
+ title: z.string().describe('Project title'),
666
+ repo_path: z.string().describe('Absolute path to the repository root'),
667
+ npm_name: z.string().describe('NPM package name'),
668
+ canonical_root: z.string().describe('Canonical root prefix for stable IDs'),
669
+ kind: z.enum(['core', 'custom']).default('custom').describe('Project kind: core (framework) or custom app'),
670
+ globs: z.array(z.string()).default(['**/*.{ts,tsx,js,jsx}']).optional().describe('Globs for source discovery'),
671
+ exclude_globs: z.array(z.string()).default(['**/node_modules/**', '**/dist/**', '**/*.d.ts']).optional().describe('Globs to exclude'),
672
+ }),
673
+ },
674
+ async ({ title, repo_path, npm_name, canonical_root, kind, globs, exclude_globs }) => {
675
+ try {
676
+ const result = await fetchFunction('cil-project', 'create', {}, {
677
+ title,
678
+ repo_path,
679
+ npm_name,
680
+ canonical_root,
681
+ kind,
682
+ globs,
683
+ exclude_globs,
684
+ })
685
+ return { content: [{ type: 'text', text: JSON.stringify({ ok: true, ...result }) }] }
686
+ } catch (err: any) {
687
+ return { content: [{ type: 'text', text: JSON.stringify({ ok: false, error: err?.message || String(err) }) }] }
688
+ }
689
+ }
690
+ )
691
+
692
+ server.registerTool(
693
+ 'spine_cil_delete_project',
694
+ {
695
+ description: 'Delete a CIL project and all attached records: files, chunks, proposals, versions, suggestions, embeddings, and links.',
696
+ inputSchema: z.object({
697
+ project_id: z.string().uuid().describe('The cil_project UUID to delete'),
698
+ }),
699
+ },
700
+ async ({ project_id }) => {
701
+ try {
702
+ const result = await fetchFunction('cil-project', 'delete', { id: project_id })
703
+ return { content: [{ type: 'text', text: JSON.stringify({ ok: true, ...result }) }] }
704
+ } catch (err: any) {
705
+ return { content: [{ type: 'text', text: JSON.stringify({ ok: false, error: err?.message || String(err) }) }] }
706
+ }
707
+ }
708
+ )
709
+
710
+ server.registerTool(
711
+ 'spine_cil_select_files',
712
+ {
713
+ description: 'Set the selected file paths for a CIL project and advance matching discovered files to 1-selected. Files must already have been discovered (step 0). Only selected files are processed by steps 2-8.',
714
+ inputSchema: z.object({
715
+ project_id: z.string().uuid().describe('The cil_project UUID'),
716
+ selected_paths: z.array(z.string()).describe('List of relative file paths or directories to include in ingestion'),
717
+ }),
718
+ },
719
+ async ({ project_id, selected_paths }) => {
720
+ try {
721
+ const result = await fetchFunction('cil-ingest', 'select', {}, { project_id, selected_paths })
722
+ return { content: [{ type: 'text', text: JSON.stringify({ ok: true, ...result }) }] }
723
+ } catch (err: any) {
724
+ return { content: [{ type: 'text', text: JSON.stringify({ ok: false, error: err?.message || String(err) }) }] }
725
+ }
726
+ }
727
+ )
728
+
729
+ server.registerTool(
730
+ 'spine_cil_publish',
731
+ {
732
+ description: 'Publish a CIL project version and mark all files at 7-embedded-mapped as 8-published. If file_path is provided, only that file is published.',
733
+ inputSchema: z.object({
734
+ project_id: z.string().uuid().describe('The cil_project UUID'),
735
+ version: z.string().describe('Version string to publish, e.g. "0.2.0"'),
736
+ file_path: z.string().optional().describe('Optional single file to publish'),
737
+ }),
738
+ },
739
+ async ({ project_id, version, file_path }) => {
740
+ try {
741
+ const pubResult = await fetchFunction('cil-project', 'publish_version', {}, {
742
+ project_id,
743
+ version,
744
+ })
745
+
746
+ const { data: project } = await adminDb.from('items').select('data').eq('id', project_id).maybeSingle()
747
+ const fileStatuses: Record<string, string> = project?.data?.file_statuses || {}
748
+ const now = new Date().toISOString()
749
+ const updated: Record<string, string> = {}
750
+ for (const [fp, status] of Object.entries(fileStatuses)) {
751
+ if (file_path && fp !== file_path) continue
752
+ if (status === '7-embedded-mapped') {
753
+ updated[fp] = '8-published'
754
+ }
755
+ }
756
+
757
+ if (Object.keys(updated).length > 0) {
758
+ const newStatuses = { ...fileStatuses, ...updated }
759
+ await adminDb.from('items').update({
760
+ data: { ...project.data, file_statuses: newStatuses, published_at: now },
761
+ updated_at: now,
762
+ }).eq('id', project_id)
763
+ }
764
+
765
+ return {
766
+ content: [{
767
+ type: 'text',
768
+ text: JSON.stringify({
769
+ ok: true,
770
+ project_id,
771
+ version,
772
+ version_id: pubResult.version?.id,
773
+ published_files: Object.keys(updated).length,
774
+ file_path: file_path || null,
775
+ }, null, 2),
776
+ }],
777
+ }
778
+ } catch (err: any) {
779
+ return { content: [{ type: 'text', text: JSON.stringify({ ok: false, error: err?.message || String(err) }) }] }
780
+ }
781
+ }
782
+ )
783
+
784
+ }
785
+ // CIL[DOC-VECTOR type="context" content="purpose='Register CIL project management and version lifecycle tools on an MCP server'; No verified external callers; imports: .framework/functions/_shared/cil.ts,.framework/mcp/shared/db.ts"]
786
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
787
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
788
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
789
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
790
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
791
+ // CIL[END stable_id="~/.framework/mcp/tools/cil/project.ts:register:function"]