spine-framework 0.3.145 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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 +5067 -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,1369 @@
1
+ /**
2
+ * @module validate
3
+ * @audience internal
4
+ * @layer app-specific
5
+ * @stability stable
6
+ *
7
+ * This module provides validation utilities for the MCP (Model Context Protocol) server, ensuring that incoming data adheres to defined schemas. It plays a crucial role in maintaining data integrity and security within the application by validating requests before they are processed. INVARIANT: All incoming data must conform to the specified Zod schemas to prevent runtime errors.
8
+ *
9
+ * @seeAlso db.js (provides database access for validation checks).
10
+ * @seeAlso server.js (defines the MCP server context and its operations).
11
+ * @seeAlso zod (used for schema validation).
12
+ *
13
+ * @cil-generated
14
+ */
15
+ // CIL[BEGIN stable_id="~/.framework/mcp/tools/cil/validate.ts:module_header:module_header" type="module_header" identity="module_header" parent="~/.framework/mcp/tools/cil/validate.ts"]
16
+ // CIL[DOC-MACRO purpose="MCP validation utilities"]
17
+ // CIL[DOC-RESPONSIBILITY responsibility="Provides validation utilities for the MCP CIL server and registers the related MCP tools."]
18
+ // CIL[DOC-CONTEXT module="validate" layer="app-specific" related_files=".framework/mcp/shared/db.ts"]
19
+ // CIL[DOC-CONTRACT inputs="none" outputs="unknown" errors="none" side_effects="none"]
20
+ // CIL[DOC-MICRO logic_summary="Provides validation utilities for the MCP CIL server and registers the related MCP tools." notable_decisions="[]"]
21
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
22
+ // CIL[DOC-STABILITY stability="stable"]
23
+ // CIL[DOC-AUDIENCE audience="internal"]
24
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
25
+ import { z } from 'zod'
26
+ import { adminDb } from '../../shared/db.js'
27
+ import fs from 'node:fs'
28
+ import path from 'node:path'
29
+ import crypto from 'node:crypto'
30
+ import * as ts from 'typescript'
31
+ import { computeContentHash } from '../../../functions/_shared/cil.js'
32
+ // CIL[DOC-VECTOR type="context" content="purpose='MCP validation utilities'; No verified external callers; imports: .framework/mcp/shared/db.ts"]
33
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
34
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
35
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
36
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
37
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
38
+ // CIL[END stable_id="~/.framework/mcp/tools/cil/validate.ts:module_header:module_header"]
39
+
40
+ // CIL[BEGIN stable_id="~/.framework/mcp/tools/cil/validate.ts:normaliseAgentChunkContent:function" type="function" identity="normaliseAgentChunkContent" parent="~/.framework/mcp/tools/cil/validate.ts"]
41
+ // CIL[DOC-MACRO purpose="normaliseAgentChunkContent function"]
42
+ // CIL[DOC-RESPONSIBILITY responsibility="Implements the normaliseAgentChunkContent function."]
43
+ // CIL[DOC-CONTEXT module="validate" layer="app-specific" related_files=".framework/mcp/shared/db.ts,.framework/mcp/tools/cil/validate.ts"]
44
+ // CIL[DOC-CONTRACT inputs="raw: string" outputs="unknown" errors="none" side_effects="none"]
45
+ // CIL[DOC-MICRO logic_summary="Implements the normaliseAgentChunkContent function." notable_decisions="[]"]
46
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
47
+ // CIL[DOC-STABILITY stability="stable"]
48
+ function normaliseAgentChunkContent(raw: string): string {
49
+ const lines = raw.replace(/\r\n/g, '\n').split('\n')
50
+ const filtered = lines.filter(line => {
51
+ const trimmed = line.trim()
52
+ return !trimmed.match(/^(?:(?:\/\/|--|#)\s*|<!--\s*)CIL\[(BEGIN|END)/) && !trimmed.match(/^(?:(?:\/\/|--|#)\s*|<!--\s*)CIL\[DOC-/)
53
+ })
54
+ const collapsed: string[] = []
55
+ let lastBlank = false
56
+ for (const line of filtered) {
57
+ const isBlank = line.trim() === ''
58
+ if (isBlank && lastBlank) continue
59
+ collapsed.push(line)
60
+ lastBlank = isBlank
61
+ }
62
+ return collapsed.join('\n').trim()
63
+ }
64
+ // CIL[DOC-VECTOR type="context" content="purpose='normaliseAgentChunkContent function'; No verified external callers; imports: .framework/mcp/shared/db.ts"]
65
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
66
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
67
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
68
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
69
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
70
+ // CIL[END stable_id="~/.framework/mcp/tools/cil/validate.ts:normaliseAgentChunkContent:function"]
71
+
72
+ // CIL[BEGIN stable_id="~/.framework/mcp/tools/cil/validate.ts:computeChunkHash:function" type="function" identity="computeChunkHash" parent="~/.framework/mcp/tools/cil/validate.ts"]
73
+ // CIL[DOC-MACRO purpose="computeChunkHash function"]
74
+ // CIL[DOC-RESPONSIBILITY responsibility="Implements the computeChunkHash function."]
75
+ // CIL[DOC-STABILITY stability="internal"]
76
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
77
+ // CIL[DOC-CONTEXT module="validate" layer="app-specific" related_files=".framework/mcp/shared/db.ts,.framework/mcp/tools/cil/validate.ts"]
78
+ // CIL[DOC-CONTRACT inputs="content: string" outputs="unknown" errors="none" side_effects="none"]
79
+ // CIL[DOC-MICRO logic_summary="Implements the computeChunkHash function." notable_decisions="[]"]
80
+ function computeChunkHash(content: string): string {
81
+ return crypto.createHash('sha256').update(normaliseAgentChunkContent(content), 'utf8').digest('hex').slice(0, 16)
82
+ }
83
+ // CIL[END stable_id="~/.framework/mcp/tools/cil/validate.ts:computeChunkHash:function"]
84
+
85
+ // CIL[DOC-VECTOR type="context" content="purpose='computeChunkHash function'; No verified external callers; imports: .framework/mcp/shared/db.ts"]
86
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
87
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
88
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
89
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
90
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
91
+ // CIL[BEGIN stable_id="~/.framework/mcp/tools/cil/validate.ts:getChunkByStableId:function" type="function" identity="getChunkByStableId" parent="~/.framework/mcp/tools/cil/validate.ts"]
92
+ // CIL[DOC-MACRO purpose="getChunkByStableId function"]
93
+ // CIL[DOC-RESPONSIBILITY responsibility="Implements the getChunkByStableId function."]
94
+ // CIL[DOC-CONTEXT module="validate" layer="app-specific" related_files=".framework/mcp/shared/db.ts,.framework/mcp/tools/cil/validate.ts"]
95
+ // CIL[DOC-CONTRACT inputs="projectId: string, stableId: string, versionId?: string" outputs="unknown" errors="none" side_effects="none"]
96
+ // CIL[DOC-MICRO logic_summary="Implements the getChunkByStableId function." notable_decisions="[]"]
97
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
98
+ // CIL[DOC-STABILITY stability="stable"]
99
+ async function getChunkByStableId(projectId: string, stableId: string, versionId?: string) {
100
+ let q = adminDb.from('items').select('id, title, data')
101
+ .filter('data->>kb_type', 'eq', 'cil_chunk')
102
+ .filter('data->>project_id', 'eq', projectId)
103
+ .filter('data->>stable_id', 'eq', stableId)
104
+ .eq('is_active', true)
105
+ if (versionId) q = (q as any).filter('data->>version_id', 'eq', versionId)
106
+ const { data } = await (q as any).maybeSingle()
107
+ return data
108
+ }
109
+ // CIL[DOC-VECTOR type="context" content="purpose='getChunkByStableId function'; No verified external callers; imports: .framework/mcp/shared/db.ts"]
110
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
111
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
112
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
113
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
114
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
115
+ // CIL[END stable_id="~/.framework/mcp/tools/cil/validate.ts:getChunkByStableId:function"]
116
+
117
+ // CIL[BEGIN stable_id="~/.framework/mcp/tools/cil/validate.ts:parseAgentAttributes:function" type="function" identity="parseAgentAttributes" parent="~/.framework/mcp/tools/cil/validate.ts"]
118
+ // CIL[DOC-MACRO purpose="parseAgentAttributes function"]
119
+ // CIL[DOC-RESPONSIBILITY responsibility="Implements the parseAgentAttributes function."]
120
+ // CIL[DOC-CONTEXT module="validate" layer="app-specific" related_files=".framework/mcp/shared/db.ts,.framework/mcp/tools/cil/validate.ts"]
121
+ // CIL[DOC-CONTRACT inputs="attrString: string" outputs="unknown" errors="none" side_effects="none"]
122
+ // CIL[DOC-MICRO logic_summary="Implements the parseAgentAttributes function." notable_decisions="[]"]
123
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
124
+ // CIL[DOC-STABILITY stability="stable"]
125
+ /**
126
+ * Parses a string of agent attributes formatted as key-value pairs into a record. This function is useful for extracting structured data from unstructured attribute strings.
127
+ *
128
+ * @inputSpec
129
+ * - attrString: string (required) — a string containing agent attributes in the format key="value".
130
+ * @outputSpec Record<string, string> — an object mapping attribute names to their corresponding values.
131
+ * @sideEffects none
132
+ * @stability stable
133
+ * @cil-generated
134
+ */
135
+ function parseAgentAttributes(attrString: string): Record<string, string> {
136
+ const attrs: Record<string, string> = {}
137
+ const regex = /(\w+)="([^"]*)"/g
138
+ let match: RegExpExecArray | null
139
+ while ((match = regex.exec(attrString)) !== null) {
140
+ attrs[match[1]] = match[2]
141
+ }
142
+ return attrs
143
+ }
144
+ // CIL[DOC-VECTOR type="context" content="purpose='parseAgentAttributes function'; No verified external callers; imports: .framework/mcp/shared/db.ts"]
145
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
146
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
147
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
148
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
149
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
150
+ // CIL[END stable_id="~/.framework/mcp/tools/cil/validate.ts:parseAgentAttributes:function"]
151
+
152
+ // CIL[BEGIN stable_id="~/.framework/mcp/tools/cil/validate.ts:detectLanguage:function" type="function" identity="detectLanguage" parent="~/.framework/mcp/tools/cil/validate.ts"]
153
+ // CIL[DOC-MACRO purpose="detectLanguage function"]
154
+ // CIL[DOC-RESPONSIBILITY responsibility="Implements the detectLanguage function."]
155
+ // CIL[DOC-CONTEXT module="validate" layer="app-specific" related_files=".framework/mcp/shared/db.ts,.framework/mcp/tools/cil/validate.ts"]
156
+ // CIL[DOC-CONTRACT inputs="filePath: string" outputs="unknown" errors="none" side_effects="none"]
157
+ // CIL[DOC-MICRO logic_summary="Implements the detectLanguage function." notable_decisions="[]"]
158
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
159
+ // CIL[DOC-STABILITY stability="stable"]
160
+ /**
161
+ * The detectLanguage function determines the programming language of a file based on its file extension. It maps known extensions to their corresponding languages for identification purposes.
162
+ *
163
+ * @macro Language detection utility for file types.
164
+ * @micro Utilizes a mapping object to return the language associated with the file extension.
165
+ * @inputSpec
166
+ * - filePath: string (required) — the path of the file whose language is to be detected.
167
+ * @outputSpec string — the detected language or 'unknown' if the extension is not recognized.
168
+ * @sideEffects none
169
+ * @stability stable
170
+ * @cil-generated
171
+ */
172
+ function detectLanguage(filePath: string): string {
173
+ const ext = path.extname(filePath).toLowerCase()
174
+ const map: Record<string, string> = {
175
+ '.ts': 'typescript',
176
+ '.tsx': 'typescriptreact',
177
+ '.js': 'javascript',
178
+ '.jsx': 'javascriptreact',
179
+ '.sql': 'sql',
180
+ '.yaml': 'yaml',
181
+ '.yml': 'yaml',
182
+ '.toml': 'toml',
183
+ '.sh': 'shell',
184
+ '.md': 'markdown',
185
+ }
186
+ return map[ext] || 'unknown'
187
+ }
188
+ // CIL[END stable_id="~/.framework/mcp/tools/cil/validate.ts:detectLanguage:function"]
189
+
190
+ // CIL[BEGIN stable_id="~/.framework/mcp/tools/cil/validate.ts:ParsedMarker:interface" type="interface" identity="ParsedMarker" parent="~/.framework/mcp/tools/cil/validate.ts"]
191
+ // CIL[DOC-VECTOR type="context" content="purpose='detectLanguage function'; No verified external callers; imports: .framework/mcp/shared/db.ts"]
192
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
193
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
194
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
195
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
196
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
197
+ // CIL[DOC-MACRO purpose="ParsedMarker interface"]
198
+ // CIL[DOC-RESPONSIBILITY responsibility="Implements the ParsedMarker interface."]
199
+ // CIL[DOC-CONTEXT module="validate" layer="app-specific" related_files=".framework/mcp/shared/db.ts,.framework/mcp/tools/cil/validate.ts"]
200
+ // CIL[DOC-CONTRACT inputs="none" outputs="none" errors="none" side_effects="none"]
201
+ // CIL[DOC-MICRO logic_summary="Implements the ParsedMarker interface." notable_decisions="[]"]
202
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
203
+ // CIL[DOC-STABILITY stability="stable"]
204
+ /**
205
+ * ParsedMarker defines the structure for a marker that has been parsed from a source file, capturing essential metadata about its location and type. This interface is used to facilitate the validation and processing of markers within the module.
206
+ *
207
+ * @sideEffects none
208
+ * @stability stable
209
+ * @cil-generated
210
+ */
211
+ interface ParsedMarker {
212
+ stable_id: string
213
+ type: string
214
+ identity: string
215
+ parent: string
216
+ start_line: number
217
+ end_line: number
218
+ }
219
+ // CIL[DOC-VECTOR type="context" content="purpose='ParsedMarker interface'; No verified external callers; imports: .framework/mcp/shared/db.ts"]
220
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
221
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
222
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
223
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
224
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
225
+ // CIL[END stable_id="~/.framework/mcp/tools/cil/validate.ts:ParsedMarker:interface"]
226
+
227
+ // CIL[BEGIN stable_id="~/.framework/mcp/tools/cil/validate.ts:ParseResult:interface" type="interface" identity="ParseResult" parent="~/.framework/mcp/tools/cil/validate.ts"]
228
+ // CIL[DOC-MACRO purpose="ParseResult interface"]
229
+ // CIL[DOC-RESPONSIBILITY responsibility="Implements the ParseResult interface."]
230
+ // CIL[DOC-CONTEXT module="validate" layer="app-specific" related_files=".framework/mcp/shared/db.ts,.framework/mcp/tools/cil/validate.ts"]
231
+ // CIL[DOC-CONTRACT inputs="none" outputs="none" errors="none" side_effects="none"]
232
+ // CIL[DOC-MICRO logic_summary="Implements the ParseResult interface." notable_decisions="[]"]
233
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
234
+ // CIL[DOC-STABILITY stability="stable"]
235
+ interface ParseResult {
236
+ markers: ParsedMarker[]
237
+ errors: Array<{ line: number; type: string; message: string }>
238
+ unclosed: Array<{ line: number; stable_id: string }>
239
+ orphan_ends: Array<{ line: number; stable_id: string }>
240
+ }
241
+ // CIL[DOC-VECTOR type="context" content="purpose='ParseResult interface'; No verified external callers; imports: .framework/mcp/shared/db.ts"]
242
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
243
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
244
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
245
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
246
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
247
+ // CIL[END stable_id="~/.framework/mcp/tools/cil/validate.ts:ParseResult:interface"]
248
+
249
+ // CIL[BEGIN stable_id="~/.framework/mcp/tools/cil/validate.ts:parseCilMarkers:function" type="function" identity="parseCilMarkers" parent="~/.framework/mcp/tools/cil/validate.ts"]
250
+ // CIL[DOC-MACRO purpose="parseCilMarkers function"]
251
+ // CIL[DOC-RESPONSIBILITY responsibility="Implements the parseCilMarkers function."]
252
+ // CIL[DOC-CONTEXT module="validate" layer="app-specific" related_files=".framework/mcp/shared/db.ts,.framework/mcp/tools/cil/validate.ts"]
253
+ // CIL[DOC-CONTRACT inputs="lines: string[]" outputs="unknown" errors="none" side_effects="none"]
254
+ // CIL[DOC-MICRO logic_summary="Implements the parseCilMarkers function." notable_decisions="[]"]
255
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
256
+ // CIL[DOC-STABILITY stability="stable"]
257
+ function parseCilMarkers(lines: string[]): ParseResult {
258
+ const result: ParseResult = { markers: [], errors: [], unclosed: [], orphan_ends: [] }
259
+ const stack: Array<{ stable_id: string; type: string; identity: string; parent: string; start_line: number; attrs: string }> = []
260
+ const beginRe = /^(?:\/\/|--|#)\s*CIL\[BEGIN\s+(.+)\]\s*$/
261
+ const endRe = /^(?:\/\/|--|#)\s*CIL\[END\s+stable_id="([^"]+)"[^\]]*\]\s*$/
262
+ const mdBeginRe = /^<!--\s*CIL\[BEGIN\s+(.+)\]\s*-->$/
263
+ const mdEndRe = /^<!--\s*CIL\[END\s+stable_id="([^"]+)"[^\]]*\]\s*-->$/
264
+
265
+ for (let i = 0; i < lines.length; i++) {
266
+ const line = lines[i]
267
+ const trimmed = line.trim()
268
+ let beginMatch = trimmed.match(beginRe)
269
+ let endMatch = trimmed.match(endRe)
270
+ if (!beginMatch) beginMatch = trimmed.match(mdBeginRe)
271
+ if (!endMatch) endMatch = trimmed.match(mdEndRe)
272
+
273
+ if (beginMatch) {
274
+ const attrs = parseAgentAttributes(beginMatch[1])
275
+ if (!attrs.stable_id) {
276
+ result.errors.push({ line: i + 1, type: 'missing_stable_id', message: 'BEGIN marker has no stable_id attribute' })
277
+ continue
278
+ }
279
+ stack.push({
280
+ stable_id: attrs.stable_id,
281
+ type: attrs.type || 'unknown',
282
+ identity: attrs.identity || '',
283
+ parent: attrs.parent || 'none',
284
+ start_line: i + 1,
285
+ attrs: beginMatch[1],
286
+ })
287
+ } else if (endMatch) {
288
+ const endStableId = endMatch[1]
289
+ if (stack.length === 0) {
290
+ result.orphan_ends.push({ line: i + 1, stable_id: endStableId })
291
+ continue
292
+ }
293
+ const opened = stack.pop()!
294
+ if (opened.stable_id !== endStableId) {
295
+ result.errors.push({
296
+ line: i + 1,
297
+ type: 'mismatched_end',
298
+ message: `END marker stable_id "${endStableId}" does not match BEGIN "${opened.stable_id}" at line ${opened.start_line}`,
299
+ })
300
+ // Continue as if it closed the opened marker so downstream checks can still run
301
+ }
302
+ result.markers.push({
303
+ stable_id: opened.stable_id,
304
+ type: opened.type,
305
+ identity: opened.identity,
306
+ parent: opened.parent,
307
+ start_line: opened.start_line,
308
+ end_line: i + 1,
309
+ })
310
+ }
311
+ }
312
+
313
+ while (stack.length > 0) {
314
+ const opened = stack.pop()!
315
+ result.unclosed.push({ line: opened.start_line, stable_id: opened.stable_id })
316
+ }
317
+
318
+ return result
319
+ }
320
+ // CIL[END stable_id="~/.framework/mcp/tools/cil/validate.ts:parseCilMarkers:function"]
321
+
322
+ // CIL[BEGIN stable_id="~/.framework/mcp/tools/cil/validate.ts:StableIdIssue:interface" type="interface" identity="StableIdIssue" parent="~/.framework/mcp/tools/cil/validate.ts"]
323
+ // CIL[DOC-VECTOR type="context" content="purpose='parseCilMarkers function'; No verified external callers; imports: .framework/mcp/shared/db.ts"]
324
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
325
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
326
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
327
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
328
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
329
+ // CIL[DOC-MACRO purpose="StableIdIssue interface"]
330
+ // CIL[DOC-RESPONSIBILITY responsibility="Implements the StableIdIssue interface."]
331
+ // CIL[DOC-CONTEXT module="validate" layer="app-specific" related_files=".framework/mcp/shared/db.ts,.framework/mcp/tools/cil/validate.ts"]
332
+ // CIL[DOC-CONTRACT inputs="none" outputs="none" errors="none" side_effects="none"]
333
+ // CIL[DOC-MICRO logic_summary="Implements the StableIdIssue interface." notable_decisions="[]"]
334
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
335
+ // CIL[DOC-STABILITY stability="stable"]
336
+ interface StableIdIssue {
337
+ stable_id: string
338
+ line: number
339
+ issue: string
340
+ }
341
+ // CIL[DOC-VECTOR type="context" content="purpose='StableIdIssue interface'; No verified external callers; imports: .framework/mcp/shared/db.ts"]
342
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
343
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
344
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
345
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
346
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
347
+ // CIL[END stable_id="~/.framework/mcp/tools/cil/validate.ts:StableIdIssue:interface"]
348
+
349
+ // CIL[BEGIN stable_id="~/.framework/mcp/tools/cil/validate.ts:validateStableIds:function" type="function" identity="validateStableIds" parent="~/.framework/mcp/tools/cil/validate.ts"]
350
+ // CIL[DOC-MACRO purpose="validateStableIds function"]
351
+ // CIL[DOC-RESPONSIBILITY responsibility="Implements the validateStableIds function."]
352
+ // CIL[DOC-CONTEXT module="validate" layer="app-specific" related_files=".framework/mcp/shared/db.ts,.framework/mcp/tools/cil/validate.ts"]
353
+ // CIL[DOC-CONTRACT inputs="markers: ParsedMarker[], canonicalPath: string" outputs="unknown" errors="none" side_effects="none"]
354
+ // CIL[DOC-MICRO logic_summary="Implements the validateStableIds function." notable_decisions="[]"]
355
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
356
+ // CIL[DOC-STABILITY stability="stable"]
357
+ /**
358
+ * Validates a list of stable IDs against a specified canonical path and checks for various structural issues. This function ensures that stable IDs conform to expected formats and values.
359
+ *
360
+ * @macro Validator for stable IDs in a given context.
361
+ * @micro Iterates through markers to check the format of stable IDs and collects issues based on specific validation rules.
362
+ * @inputSpec
363
+ * - markers: ParsedMarker[] (required) — an array of markers containing stable IDs to validate.
364
+ * - canonicalPath: string (required) — the expected canonical path that stable IDs should match.
365
+ * @outputSpec StableIdIssue[] — an array of issues found during validation, each containing details about the stable ID and the nature of the issue.
366
+ * @sideEffects none
367
+ * @stability stable
368
+ * @cil-generated
369
+ */
370
+ function validateStableIds(markers: ParsedMarker[], canonicalPath: string): StableIdIssue[] {
371
+ const issues: StableIdIssue[] = []
372
+ for (const m of markers) {
373
+ const parts = m.stable_id.split(':')
374
+ if (parts.length < 3) {
375
+ issues.push({ stable_id: m.stable_id, line: m.start_line, issue: 'stable_id must be canonical_path:identity:type' })
376
+ continue
377
+ }
378
+ const type = parts.pop()!
379
+ const identity = parts.pop()!
380
+ const pathPart = parts.join(':')
381
+ if (!pathPart.startsWith('~')) {
382
+ issues.push({ stable_id: m.stable_id, line: m.start_line, issue: 'canonical_path must start with ~' })
383
+ }
384
+ if (pathPart !== canonicalPath) {
385
+ issues.push({ stable_id: m.stable_id, line: m.start_line, issue: `canonical_path mismatch: expected ${canonicalPath}` })
386
+ }
387
+ if (!identity) {
388
+ issues.push({ stable_id: m.stable_id, line: m.start_line, issue: 'identity must not be empty' })
389
+ }
390
+ if (!type) {
391
+ issues.push({ stable_id: m.stable_id, line: m.start_line, issue: 'type must not be empty' })
392
+ }
393
+ if (m.type === 'module_header' && (identity !== 'module_header' || type !== 'module_header')) {
394
+ issues.push({ stable_id: m.stable_id, line: m.start_line, issue: 'module_header chunk identity/type must both be module_header' })
395
+ }
396
+ }
397
+ return issues
398
+ }
399
+ // CIL[DOC-VECTOR type="context" content="purpose='validateStableIds function'; No verified external callers; imports: .framework/mcp/shared/db.ts"]
400
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
401
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
402
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
403
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
404
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
405
+ // CIL[END stable_id="~/.framework/mcp/tools/cil/validate.ts:validateStableIds:function"]
406
+
407
+ // CIL[BEGIN stable_id="~/.framework/mcp/tools/cil/validate.ts:CoverageIssue:interface" type="interface" identity="CoverageIssue" parent="~/.framework/mcp/tools/cil/validate.ts"]
408
+ // CIL[DOC-MACRO purpose="CoverageIssue interface"]
409
+ // CIL[DOC-RESPONSIBILITY responsibility="Implements the CoverageIssue interface."]
410
+ // CIL[DOC-STABILITY stability="internal"]
411
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
412
+ // CIL[DOC-CONTEXT module="validate" layer="app-specific" related_files=".framework/mcp/shared/db.ts,.framework/mcp/tools/cil/validate.ts"]
413
+ // CIL[DOC-CONTRACT inputs="none" outputs="none" errors="none" side_effects="none"]
414
+ // CIL[DOC-MICRO logic_summary="Implements the CoverageIssue interface." notable_decisions="[]"]
415
+ /**
416
+ * The CoverageIssue interface defines the structure for representing issues related to code coverage, specifically identifying lines of code that are either uncovered or overlapping. It exists to facilitate the reporting and handling of coverage-related problems in the codebase.
417
+ *
418
+ * @sideEffects none
419
+ * @stability stable
420
+ * @cil-generated
421
+ */
422
+ interface CoverageIssue {
423
+ line: number
424
+ type: 'uncovered' | 'overlapping'
425
+ message: string
426
+ }
427
+ // CIL[END stable_id="~/.framework/mcp/tools/cil/validate.ts:CoverageIssue:interface"]
428
+
429
+ // CIL[DOC-VECTOR type="context" content="purpose='CoverageIssue interface'; No verified external callers; imports: .framework/mcp/shared/db.ts"]
430
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
431
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
432
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
433
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
434
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
435
+ // CIL[BEGIN stable_id="~/.framework/mcp/tools/cil/validate.ts:computeCoverage:function" type="function" identity="computeCoverage" parent="~/.framework/mcp/tools/cil/validate.ts"]
436
+ // CIL[DOC-MACRO purpose="computeCoverage function"]
437
+ // CIL[DOC-RESPONSIBILITY responsibility="Implements the computeCoverage function."]
438
+ // CIL[DOC-CONTEXT module="validate" layer="app-specific" related_files=".framework/mcp/shared/db.ts,.framework/mcp/tools/cil/validate.ts"]
439
+ // CIL[DOC-CONTRACT inputs="lines: string[], markers: ParsedMarker[]" outputs="unknown" errors="none" side_effects="none"]
440
+ // CIL[DOC-MICRO logic_summary="Implements the computeCoverage function." notable_decisions="[]"]
441
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
442
+ // CIL[DOC-STABILITY stability="stable"]
443
+ function computeCoverage(lines: string[], markers: ParsedMarker[]): CoverageIssue[] {
444
+ const issues: CoverageIssue[] = []
445
+ const coverage: number[] = new Array(lines.length).fill(0)
446
+
447
+ for (const m of markers) {
448
+ for (let i = m.start_line - 1; i <= m.end_line - 1; i++) {
449
+ if (i >= 0 && i < lines.length) coverage[i]++
450
+ }
451
+ }
452
+
453
+ let inBlockComment = false
454
+ for (let i = 0; i < lines.length; i++) {
455
+ const line = lines[i]
456
+ const trimmed = line.trim()
457
+ const isMarker = /^\s*(?:\/\/|--|#)\s*CIL\[(BEGIN|END|DOC-)/.test(line) || /^\s*<!--\s*CIL\[(BEGIN|END|DOC-)/.test(line)
458
+ const isLineComment = /^\s*(?:\/\/|--|#)/.test(line)
459
+ const isBlank = trimmed === ''
460
+
461
+ // Track multi-line /* ... */ blocks so JSDoc/module comments don't need their own chunk
462
+ if (trimmed.startsWith('/*')) inBlockComment = true
463
+ if (trimmed.endsWith('*/')) {
464
+ inBlockComment = false
465
+ continue
466
+ }
467
+ if (inBlockComment || isLineComment || isBlank || isMarker) continue
468
+
469
+ if (coverage[i] === 0) {
470
+ issues.push({ line: i + 1, type: 'uncovered', message: `Line ${i + 1} is not inside any CIL chunk` })
471
+ } else if (coverage[i] > 1) {
472
+ issues.push({ line: i + 1, type: 'overlapping', message: `Line ${i + 1} is inside ${coverage[i]} overlapping chunks` })
473
+ }
474
+ }
475
+
476
+ return issues
477
+ }
478
+ // CIL[DOC-VECTOR type="context" content="purpose='computeCoverage function'; No verified external callers; imports: .framework/mcp/shared/db.ts"]
479
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
480
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
481
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
482
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
483
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
484
+ // CIL[END stable_id="~/.framework/mcp/tools/cil/validate.ts:computeCoverage:function"]
485
+
486
+ // CIL[BEGIN stable_id="~/.framework/mcp/tools/cil/validate.ts:JsxLeak:interface" type="interface" identity="JsxLeak" parent="~/.framework/mcp/tools/cil/validate.ts"]
487
+ // CIL[DOC-MACRO purpose="JsxLeak interface"]
488
+ // CIL[DOC-RESPONSIBILITY responsibility="Implements the JsxLeak interface."]
489
+ // CIL[DOC-CONTEXT module="validate" layer="app-specific" related_files=".framework/mcp/shared/db.ts,.framework/mcp/tools/cil/validate.ts"]
490
+ // CIL[DOC-CONTRACT inputs="none" outputs="none" errors="none" side_effects="none"]
491
+ // CIL[DOC-MICRO logic_summary="Implements the JsxLeak interface." notable_decisions="[]"]
492
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
493
+ // CIL[DOC-STABILITY stability="stable"]
494
+ /**
495
+ * The JsxLeak interface defines the structure for reporting issues related to JSX leaks during validation. It provides essential information such as the line number and a descriptive message for each leak detected.
496
+ *
497
+ * @sideEffects none
498
+ * @stability stable
499
+ * @cil-generated
500
+ */
501
+ interface JsxLeak {
502
+ line: number
503
+ message: string
504
+ }
505
+ // CIL[DOC-VECTOR type="context" content="purpose='JsxLeak interface'; No verified external callers; imports: .framework/mcp/shared/db.ts"]
506
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
507
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
508
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
509
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
510
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
511
+ // CIL[END stable_id="~/.framework/mcp/tools/cil/validate.ts:JsxLeak:interface"]
512
+
513
+ // CIL[BEGIN stable_id="~/.framework/mcp/tools/cil/validate.ts:checkJsxLeakage:function" type="function" identity="checkJsxLeakage" parent="~/.framework/mcp/tools/cil/validate.ts"]
514
+ // CIL[DOC-MACRO purpose="checkJsxLeakage function"]
515
+ // CIL[DOC-RESPONSIBILITY responsibility="Implements the checkJsxLeakage function."]
516
+ // CIL[DOC-CONTEXT module="validate" layer="app-specific" related_files=".framework/mcp/shared/db.ts,.framework/mcp/tools/cil/validate.ts"]
517
+ // CIL[DOC-CONTRACT inputs="content: string, lines: string[], language: string" outputs="unknown" errors="none" side_effects="none"]
518
+ // CIL[DOC-MICRO logic_summary="Implements the checkJsxLeakage function." notable_decisions="[]"]
519
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
520
+ // CIL[DOC-STABILITY stability="stable"]
521
+ /**
522
+ * Checks for leakage of CIL markers into JSX content by analyzing the provided code. This function ensures that specific markers do not appear within JSX elements, which could lead to unintended behavior.
523
+ *
524
+ * @inputSpec
525
+ * - content: string (required) — the JSX content to be analyzed.
526
+ * - lines: string[] (required) — an array of lines from the content for leakage checking.
527
+ * - language: string (required) — specifies the language type, either 'typescriptreact' or 'javascriptreact'.
528
+ * @outputSpec JsxLeak[] — an array of leakage objects containing line numbers and messages.
529
+ * @sideEffects none
530
+ * @stability stable
531
+ * @calls ts.createSourceFile, ts.isJsxText, ts.isJsxExpression, ts.forEachChild
532
+ * @cil-generated
533
+ */
534
+ function checkJsxLeakage(content: string, lines: string[], language: string): JsxLeak[] {
535
+ const leaks: JsxLeak[] = []
536
+ if (!['typescriptreact', 'javascriptreact'].includes(language)) return leaks
537
+
538
+ const scriptKind = language === 'typescriptreact' ? ts.ScriptKind.TSX : ts.ScriptKind.JSX
539
+ const sourceFile = ts.createSourceFile('temp.tsx', content, ts.ScriptTarget.Latest, true, scriptKind)
540
+
541
+ function findJsxText(node: ts.Node) {
542
+ if (ts.isJsxText(node) || ts.isJsxExpression(node)) {
543
+ const start = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)).line
544
+ const end = sourceFile.getLineAndCharacterOfPosition(node.getEnd()).line
545
+ for (let i = start; i <= end; i++) {
546
+ if (lines[i] && lines[i].includes('CIL[')) {
547
+ leaks.push({ line: i + 1, message: `CIL marker text leaked into JSX at line ${i + 1}` })
548
+ }
549
+ }
550
+ }
551
+ ts.forEachChild(node, findJsxText)
552
+ }
553
+ findJsxText(sourceFile)
554
+ return leaks
555
+ }
556
+ // CIL[DOC-VECTOR type="context" content="purpose='checkJsxLeakage function'; No verified external callers; imports: .framework/mcp/shared/db.ts"]
557
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
558
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
559
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
560
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
561
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
562
+ // CIL[END stable_id="~/.framework/mcp/tools/cil/validate.ts:checkJsxLeakage:function"]
563
+
564
+ // CIL[BEGIN stable_id="~/.framework/mcp/tools/cil/validate.ts:AstProposal:interface" type="interface" identity="AstProposal" parent="~/.framework/mcp/tools/cil/validate.ts"]
565
+ // CIL[DOC-MACRO purpose="AstProposal interface"]
566
+ // CIL[DOC-RESPONSIBILITY responsibility="Implements the AstProposal interface."]
567
+ // CIL[DOC-CONTEXT module="validate" layer="app-specific" related_files=".framework/mcp/shared/db.ts,.framework/mcp/tools/cil/validate.ts"]
568
+ // CIL[DOC-CONTRACT inputs="none" outputs="none" errors="none" side_effects="none"]
569
+ // CIL[DOC-MICRO logic_summary="Implements the AstProposal interface." notable_decisions="[]"]
570
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
571
+ // CIL[DOC-STABILITY stability="stable"]
572
+ /**
573
+ * The AstProposal interface defines the structure for proposals in the AST validation process, encapsulating essential metadata about each proposal. It serves to standardize the representation of proposals within the module.
574
+ *
575
+ * @sideEffects none
576
+ * @stability stable
577
+ * @cil-generated
578
+ */
579
+ interface AstProposal {
580
+ stable_id: string
581
+ identity: string
582
+ type: string
583
+ line_start: number
584
+ line_end: number
585
+ }
586
+ // CIL[DOC-VECTOR type="context" content="purpose='AstProposal interface'; No verified external callers; imports: .framework/mcp/shared/db.ts"]
587
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
588
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
589
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
590
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
591
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
592
+ // CIL[END stable_id="~/.framework/mcp/tools/cil/validate.ts:AstProposal:interface"]
593
+
594
+ // CIL[BEGIN stable_id="~/.framework/mcp/tools/cil/validate.ts:proposeAstChunks:function" type="function" identity="proposeAstChunks" parent="~/.framework/mcp/tools/cil/validate.ts"]
595
+ // CIL[DOC-MACRO purpose="proposeAstChunks function"]
596
+ // CIL[DOC-RESPONSIBILITY responsibility="Implements the proposeAstChunks function."]
597
+ // CIL[DOC-CONTEXT module="validate" layer="app-specific" related_files=".framework/mcp/shared/db.ts,.framework/mcp/tools/cil/validate.ts"]
598
+ // CIL[DOC-CONTRACT inputs="content: string, canonicalPath: string, language: string" outputs="unknown" errors="none" side_effects="none"]
599
+ // CIL[DOC-STABILITY stability="internal"]
600
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
601
+ // CIL[DOC-MICRO logic_summary="Implements the proposeAstChunks function." notable_decisions="[]"]
602
+ /**
603
+ * The proposeAstChunks function analyzes a given source code content and generates a list of AST proposals based on the structure of the code. It identifies module headers, functions, classes, interfaces, type aliases, and constants for TypeScript and JavaScript files.
604
+ *
605
+ * @macro AST proposal generator for TypeScript and JavaScript source files.
606
+ * @micro Utilizes TypeScript's compiler API to parse source files and extract relevant declarations.
607
+ * @inputSpec
608
+ * - content: string (required) — the source code to analyze.
609
+ * - canonicalPath: string (required) — the path to the source file.
610
+ * - language: string (required) — the programming language, must be one of 'typescript', 'typescriptreact', 'javascript', 'javascriptreact'.
611
+ * @outputSpec AstProposal[] — an array of proposals representing different code structures.
612
+ * @sideEffects none
613
+ * @stability stable
614
+ * @calls ts.createSourceFile, ts.forEachChild, ts.isFunctionDeclaration, ts.isClassDeclaration, ts.isInterfaceDeclaration, ts.isTypeAliasDeclaration, ts.isVariableStatement, ts.isIdentifier
615
+ * @cil-generated
616
+ */
617
+ function proposeAstChunks(content: string, canonicalPath: string, language: string): AstProposal[] {
618
+ const proposals: AstProposal[] = []
619
+ if (!['typescript', 'typescriptreact', 'javascript', 'javascriptreact'].includes(language)) return proposals
620
+
621
+ const scriptKind =
622
+ language === 'typescript' ? ts.ScriptKind.TS :
623
+ language === 'typescriptreact' ? ts.ScriptKind.TSX :
624
+ language === 'javascript' ? ts.ScriptKind.JS :
625
+ ts.ScriptKind.JSX
626
+
627
+ const sourceFile = ts.createSourceFile(canonicalPath, content, ts.ScriptTarget.Latest, true, scriptKind)
628
+ const imports = sourceFile.statements.filter(s => ts.isImportDeclaration(s))
629
+ let headerStart = 1
630
+ let headerEnd = 1
631
+ if (imports.length > 0) {
632
+ const firstImport = imports[0]
633
+ const lastImport = imports[imports.length - 1]
634
+ headerStart = sourceFile.getLineAndCharacterOfPosition(firstImport.getStart(sourceFile)).line + 1
635
+ headerEnd = sourceFile.getLineAndCharacterOfPosition(lastImport.getEnd()).line + 1
636
+ }
637
+
638
+ proposals.push({ stable_id: `${canonicalPath}:module_header:module_header`, identity: 'module_header', type: 'module_header', line_start: headerStart, line_end: headerEnd })
639
+
640
+ function addSymbol(name: string, type: string, node: ts.Node) {
641
+ const start = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)).line + 1
642
+ const end = sourceFile.getLineAndCharacterOfPosition(node.getEnd()).line + 1
643
+ proposals.push({ stable_id: `${canonicalPath}:${name.toLowerCase()}:${type}`, identity: name, type, line_start: start, line_end: end })
644
+ }
645
+
646
+ ts.forEachChild(sourceFile, (node) => {
647
+ if (ts.isFunctionDeclaration(node) && node.name) addSymbol(node.name.text, 'function', node)
648
+ else if (ts.isClassDeclaration(node) && node.name) addSymbol(node.name.text, 'class', node)
649
+ else if (ts.isInterfaceDeclaration(node) && node.name) addSymbol(node.name.text, 'interface', node)
650
+ else if (ts.isTypeAliasDeclaration(node) && node.name) addSymbol(node.name.text, 'type', node)
651
+ else if (ts.isVariableStatement(node)) {
652
+ for (const decl of node.declarationList.declarations) {
653
+ if (ts.isIdentifier(decl.name)) {
654
+ addSymbol(decl.name.text, 'constant', node)
655
+ break
656
+ }
657
+ }
658
+ }
659
+ })
660
+
661
+ return proposals
662
+ }
663
+ // CIL[END stable_id="~/.framework/mcp/tools/cil/validate.ts:proposeAstChunks:function"]
664
+
665
+ // CIL[BEGIN stable_id="~/.framework/mcp/tools/cil/validate.ts:BoundaryMismatch:interface" type="interface" identity="BoundaryMismatch" parent="~/.framework/mcp/tools/cil/validate.ts"]
666
+ // CIL[DOC-VECTOR type="context" content="purpose='proposeAstChunks function'; No verified external callers; imports: .framework/mcp/shared/db.ts"]
667
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
668
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
669
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
670
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
671
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
672
+ // CIL[DOC-MACRO purpose="BoundaryMismatch interface"]
673
+ // CIL[DOC-RESPONSIBILITY responsibility="Implements the BoundaryMismatch interface."]
674
+ // CIL[DOC-CONTEXT module="validate" layer="app-specific" related_files=".framework/mcp/shared/db.ts,.framework/mcp/tools/cil/validate.ts"]
675
+ // CIL[DOC-CONTRACT inputs="none" outputs="none" errors="none" side_effects="none"]
676
+ // CIL[DOC-STABILITY stability="internal"]
677
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
678
+ // CIL[DOC-MICRO logic_summary="Implements the BoundaryMismatch interface." notable_decisions="[]"]
679
+ interface BoundaryMismatch {
680
+ stable_id: string
681
+ existing_start: number
682
+ existing_end: number
683
+ proposed_start: number
684
+ proposed_end: number
685
+ message: string
686
+ }
687
+ // CIL[END stable_id="~/.framework/mcp/tools/cil/validate.ts:BoundaryMismatch:interface"]
688
+
689
+ // CIL[DOC-VECTOR type="context" content="purpose='BoundaryMismatch interface'; No verified external callers; imports: .framework/mcp/shared/db.ts"]
690
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
691
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
692
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
693
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
694
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
695
+ // CIL[BEGIN stable_id="~/.framework/mcp/tools/cil/validate.ts:compareWithProposals:function" type="function" identity="compareWithProposals" parent="~/.framework/mcp/tools/cil/validate.ts"]
696
+ // CIL[DOC-MACRO purpose="compareWithProposals function"]
697
+ // CIL[DOC-RESPONSIBILITY responsibility="Implements the compareWithProposals function."]
698
+ // CIL[DOC-CONTEXT module="validate" layer="app-specific" related_files=".framework/mcp/shared/db.ts,.framework/mcp/tools/cil/validate.ts"]
699
+ // CIL[DOC-CONTRACT inputs="markers: ParsedMarker[], proposals: AstProposal[], lines: string[]" outputs="unknown" errors="none" side_effects="none"]
700
+ // CIL[DOC-MICRO logic_summary="Implements the compareWithProposals function." notable_decisions="[]"]
701
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
702
+ // CIL[DOC-STABILITY stability="stable"]
703
+ /**
704
+ * Compares parsed markers with AST proposals to identify boundary mismatches in code chunks. This function ensures that the code boundaries defined by markers align with the proposed boundaries in the AST.
705
+ *
706
+ * @inputSpec
707
+ * - markers: ParsedMarker[] (required) — an array of parsed markers representing code boundaries.
708
+ * - proposals: AstProposal[] (required) — an array of AST proposals containing expected boundaries.
709
+ * - lines: string[] (required) — an array of lines of code to analyze.
710
+ * @outputSpec BoundaryMismatch[] — an array of boundary mismatch objects detailing discrepancies between markers and proposals.
711
+ * @sideEffects none
712
+ * @stability stable
713
+ * @cil-generated
714
+ */
715
+ function compareWithProposals(markers: ParsedMarker[], proposals: AstProposal[], lines: string[]): BoundaryMismatch[] {
716
+ const mismatches: BoundaryMismatch[] = []
717
+ const proposalMap = new Map(proposals.map(p => [p.stable_id, p]))
718
+ const markerRe = /^\s*(?:\/\/|--|#)\s*CIL\[(BEGIN|END)\s+stable_id=/
719
+ const docRe = /^\s*(?:\/\/|--|#)\s*CIL\[DOC-/
720
+ function innerCodeBoundary(start: number, end: number): { start: number; end: number } {
721
+ let s = start
722
+ let e = end
723
+ while (s <= e && (markerRe.test(lines[s - 1]) || docRe.test(lines[s - 1]) || lines[s - 1].trim() === '')) s++
724
+ while (e >= s && (markerRe.test(lines[e - 1]) || docRe.test(lines[e - 1]) || lines[e - 1].trim() === '')) e--
725
+ return { start: s, end: e }
726
+ }
727
+ for (const m of markers) {
728
+ const p = proposalMap.get(m.stable_id)
729
+ if (!p) continue // extra markers are handled elsewhere; unproposed chunks may be intentional
730
+ const existing = innerCodeBoundary(m.start_line, m.end_line)
731
+ if (existing.start !== p.line_start || existing.end !== p.line_end) {
732
+ mismatches.push({
733
+ stable_id: m.stable_id,
734
+ existing_start: existing.start,
735
+ existing_end: existing.end,
736
+ proposed_start: p.line_start,
737
+ proposed_end: p.line_end,
738
+ message: `Chunk ${m.stable_id} inner code boundaries (${existing.start}-${existing.end}) do not match AST proposal (${p.line_start}-${p.line_end})`,
739
+ })
740
+ }
741
+ }
742
+ return mismatches
743
+ }
744
+ // CIL[DOC-VECTOR type="context" content="purpose='compareWithProposals function'; No verified external callers; imports: .framework/mcp/shared/db.ts"]
745
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
746
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
747
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
748
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
749
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
750
+ // CIL[END stable_id="~/.framework/mcp/tools/cil/validate.ts:compareWithProposals:function"]
751
+
752
+ // CIL[BEGIN stable_id="~/.framework/mcp/tools/cil/validate.ts:ValidateChunkResult:interface" type="interface" identity="ValidateChunkResult" parent="~/.framework/mcp/tools/cil/validate.ts"]
753
+ // CIL[DOC-MACRO purpose="ValidateChunkResult interface"]
754
+ // CIL[DOC-RESPONSIBILITY responsibility="Implements the ValidateChunkResult interface."]
755
+ // CIL[DOC-CONTEXT module="validate" layer="app-specific" related_files=".framework/mcp/shared/db.ts,.framework/mcp/tools/cil/validate.ts"]
756
+ // CIL[DOC-CONTRACT inputs="none" outputs="none" errors="none" side_effects="none"]
757
+ // CIL[DOC-MICRO logic_summary="Implements the ValidateChunkResult interface." notable_decisions="[]"]
758
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
759
+ // CIL[DOC-STABILITY stability="stable"]
760
+ /**
761
+ * The ValidateChunkResult interface defines the structure of the result returned from validating a chunk of code. It encapsulates the validation status, file paths, language details, and any associated errors or issues.
762
+ *
763
+ * @sideEffects none
764
+ * @stability stable
765
+ * @cil-generated
766
+ */
767
+ interface ValidateChunkResult {
768
+ ok: boolean
769
+ status: 'valid' | 'invalid' | 'not_chunked'
770
+ file_path: string
771
+ canonical_path: string
772
+ language: string
773
+ errors: Array<{ line?: number; type: string; message: string }>
774
+ marker_count: number
775
+ coverage_issues: CoverageIssue[]
776
+ stable_id_issues: StableIdIssue[]
777
+ boundary_mismatches: BoundaryMismatch[]
778
+ jsx_leaks: JsxLeak[]
779
+ proposals?: AstProposal[]
780
+ }
781
+ // CIL[DOC-VECTOR type="context" content="purpose='ValidateChunkResult interface'; No verified external callers; imports: .framework/mcp/shared/db.ts"]
782
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
783
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
784
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
785
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
786
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
787
+ // CIL[END stable_id="~/.framework/mcp/tools/cil/validate.ts:ValidateChunkResult:interface"]
788
+
789
+ // CIL[BEGIN stable_id="~/.framework/mcp/tools/cil/validate.ts:validateChunkFile:function" type="function" identity="validateChunkFile" parent="~/.framework/mcp/tools/cil/validate.ts"]
790
+ // CIL[DOC-MACRO purpose="validateChunkFile function"]
791
+ // CIL[DOC-RESPONSIBILITY responsibility="Implements the validateChunkFile function."]
792
+ // CIL[DOC-CONTEXT module="validate" layer="app-specific" related_files=".framework/mcp/shared/db.ts,.framework/mcp/tools/cil/validate.ts"]
793
+ // CIL[DOC-CONTRACT inputs="projectId: string, filePath: string" outputs="unknown" errors="none" side_effects="none"]
794
+ // CIL[DOC-MICRO logic_summary="Implements the validateChunkFile function." notable_decisions="[]"]
795
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
796
+ // CIL[DOC-STABILITY stability="stable"]
797
+ /**
798
+ * Validates the existence and structure of a chunk file associated with a project, returning detailed information about its validity and any issues found. This function is essential for ensuring that chunk files conform to expected formats and contain valid markers.
799
+ *
800
+ * @macro Async function that validates chunk files for a specified project.
801
+ * @micro Reads file content and analyzes it for markers, errors, and coverage issues.
802
+ * @inputSpec
803
+ * projectId: string (required) — The ID of the project to which the file belongs.
804
+ * filePath: string (required) — The path of the file to validate.
805
+ * @outputSpec Promise<ValidateChunkResult> — An object containing validation results including status, errors, and issue counts.
806
+ * @sideEffects Reads from the filesystem to check file existence and content.
807
+ * @stability stable
808
+ * @calls adminDb.from('items'), fs.existsSync, fs.readFileSync, detectLanguage, parseCilMarkers, validateStableIds, computeCoverage, checkJsxLeakage, proposeAstChunks, compareWithProposals
809
+ * @cil-generated
810
+ */
811
+ async function validateChunkFile(projectId: string, filePath: string): Promise<ValidateChunkResult> {
812
+ const { data: projectRows } = await adminDb.from('items').select('data').eq('id', projectId).maybeSingle()
813
+ const repoPath = projectRows?.data?.repo_path || process.env.REPO_PATH || ''
814
+ const absolutePath = repoPath ? path.join(repoPath, filePath.replace(/^\//, '')) : filePath
815
+
816
+ if (!fs.existsSync(absolutePath)) {
817
+ return { ok: false, status: 'invalid', file_path: filePath, canonical_path: '', language: '', errors: [{ type: 'file_not_found', message: `File not found: ${absolutePath}` }], marker_count: 0, coverage_issues: [], stable_id_issues: [], boundary_mismatches: [], jsx_leaks: [] }
818
+ }
819
+
820
+ const content = fs.readFileSync(absolutePath, 'utf-8')
821
+ const lines = content.split('\n')
822
+ const language = detectLanguage(filePath)
823
+ const canonicalPath = `~/${filePath.replace(/^\//, '')}`
824
+
825
+ const parseResult = parseCilMarkers(lines)
826
+ const markers = parseResult.markers
827
+
828
+ const errors: Array<{ line?: number; type: string; message: string }> = [...parseResult.errors]
829
+ for (const o of parseResult.orphan_ends) {
830
+ errors.push({ line: o.line, type: 'orphan_end', message: `Orphan END marker at line ${o.line} for ${o.stable_id}` })
831
+ }
832
+ for (const u of parseResult.unclosed) {
833
+ errors.push({ line: u.line, type: 'unclosed_begin', message: `Unclosed BEGIN marker at line ${u.line} for ${u.stable_id}` })
834
+ }
835
+
836
+ if (markers.length === 0) {
837
+ return { ok: false, status: 'not_chunked', file_path: filePath, canonical_path: canonicalPath, language, errors: [], marker_count: 0, coverage_issues: [], stable_id_issues: [], boundary_mismatches: [], jsx_leaks: [] }
838
+ }
839
+
840
+ const stableIdIssues = validateStableIds(markers, canonicalPath)
841
+ const coverageIssues = computeCoverage(lines, markers)
842
+ const jsxLeaks = checkJsxLeakage(content, lines, language)
843
+
844
+ for (const issue of stableIdIssues) {
845
+ errors.push({ line: issue.line, type: 'invalid_stable_id', message: issue.issue })
846
+ }
847
+
848
+ const proposals = ['typescript', 'typescriptreact', 'javascript', 'javascriptreact'].includes(language)
849
+ ? proposeAstChunks(content, canonicalPath, language)
850
+ : []
851
+ const boundaryMismatches = compareWithProposals(markers, proposals, lines)
852
+ for (const m of boundaryMismatches) {
853
+ errors.push({ type: 'boundary_mismatch', message: m.message })
854
+ }
855
+
856
+ const allIssues = errors.length + coverageIssues.length + jsxLeaks.length
857
+ const ok = allIssues === 0
858
+
859
+ return {
860
+ ok,
861
+ status: ok ? 'valid' : 'invalid',
862
+ file_path: filePath,
863
+ canonical_path: canonicalPath,
864
+ language,
865
+ errors,
866
+ marker_count: markers.length,
867
+ coverage_issues: coverageIssues,
868
+ stable_id_issues: stableIdIssues,
869
+ boundary_mismatches: boundaryMismatches,
870
+ jsx_leaks: jsxLeaks,
871
+ proposals,
872
+ }
873
+ }
874
+ // CIL[END stable_id="~/.framework/mcp/tools/cil/validate.ts:validateChunkFile:function"]
875
+
876
+ // CIL[BEGIN stable_id="~/.framework/mcp/tools/cil/validate.ts:ProjectValidationResult:interface" type="interface" identity="ProjectValidationResult" parent="~/.framework/mcp/tools/cil/validate.ts"]
877
+ // CIL[DOC-VECTOR type="context" content="purpose='validateChunkFile function'; No verified external callers; imports: .framework/mcp/shared/db.ts"]
878
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
879
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
880
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
881
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
882
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
883
+ // CIL[DOC-MACRO purpose="ProjectValidationResult interface"]
884
+ // CIL[DOC-RESPONSIBILITY responsibility="Implements the ProjectValidationResult interface."]
885
+ // CIL[DOC-CONTEXT module="validate" layer="app-specific" related_files=".framework/mcp/shared/db.ts,.framework/mcp/tools/cil/validate.ts"]
886
+ // CIL[DOC-CONTRACT inputs="ValidateChunkResult & { file_path: string }" outputs="none" errors="none" side_effects="none"]
887
+ // CIL[DOC-MICRO logic_summary="Implements the ProjectValidationResult interface." notable_decisions="[]"]
888
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
889
+ // CIL[DOC-STABILITY stability="stable"]
890
+ /**
891
+ * The ProjectValidationResult interface encapsulates the results of validating a project, providing details about the validation outcome and statistics. It serves to structure the response data for project validation operations.
892
+ *
893
+ * @sideEffects none
894
+ * @stability stable
895
+ * @cil-generated
896
+ */
897
+ interface ProjectValidationResult {
898
+ ok: boolean
899
+ project_id: string
900
+ file_path: string | null
901
+ limit: number
902
+ offset: number
903
+ total_files: number
904
+ valid: number
905
+ invalid: number
906
+ not_chunked: number
907
+ results: (ValidateChunkResult & { file_path: string })[]
908
+ }
909
+ // CIL[DOC-VECTOR type="context" content="purpose='ProjectValidationResult interface'; No verified external callers; imports: .framework/mcp/shared/db.ts"]
910
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
911
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
912
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
913
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
914
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
915
+ // CIL[END stable_id="~/.framework/mcp/tools/cil/validate.ts:ProjectValidationResult:interface"]
916
+
917
+ // CIL[BEGIN stable_id="~/.framework/mcp/tools/cil/validate.ts:validateProjectFiles:function" type="function" identity="validateProjectFiles" parent="~/.framework/mcp/tools/cil/validate.ts"]
918
+ // CIL[DOC-MACRO purpose="validateProjectFiles function"]
919
+ // CIL[DOC-RESPONSIBILITY responsibility="Implements the validateProjectFiles function."]
920
+ // CIL[DOC-CONTEXT module="validate" layer="app-specific" related_files=".framework/mcp/shared/db.ts,.framework/mcp/tools/cil/validate.ts"]
921
+ // CIL[DOC-CONTRACT inputs="projectId: string, filePath?: string, limit = 50, offset = 0" outputs="unknown" errors="none" side_effects="none"]
922
+ // CIL[DOC-MICRO logic_summary="Implements the validateProjectFiles function." notable_decisions="[]"]
923
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
924
+ // CIL[DOC-STABILITY stability="stable"]
925
+ async function validateProjectFiles(
926
+ projectId: string,
927
+ filePath?: string,
928
+ limit = 50,
929
+ offset = 0
930
+ ): Promise<ProjectValidationResult> {
931
+ let q = adminDb
932
+ .from('items')
933
+ .select('id, title, data')
934
+ .filter('data->>kb_type', 'eq', 'cil_file')
935
+ .filter('data->>project_id', 'eq', projectId)
936
+ .eq('is_active', true)
937
+ .order('id')
938
+
939
+ if (filePath) {
940
+ q = q.filter('data->>file_path', 'eq', filePath)
941
+ } else {
942
+ q = q.range(offset, offset + limit - 1)
943
+ }
944
+
945
+ const { data: files, error } = await q
946
+ if (error) {
947
+ return {
948
+ ok: false,
949
+ project_id: projectId,
950
+ file_path: filePath || null,
951
+ limit,
952
+ offset,
953
+ total_files: 0,
954
+ valid: 0,
955
+ invalid: 0,
956
+ not_chunked: 0,
957
+ results: [{ file_path: '', ok: false, status: 'invalid' as const, canonical_path: '', language: '', errors: [{ type: 'db_error', message: error.message }], marker_count: 0, coverage_issues: [], stable_id_issues: [], boundary_mismatches: [], jsx_leaks: [] }],
958
+ }
959
+ }
960
+
961
+ const results: (ValidateChunkResult & { file_path: string })[] = []
962
+ let valid = 0, invalid = 0, notChunked = 0
963
+
964
+ for (const f of files || []) {
965
+ const fp = f.data?.file_path || f.title || ''
966
+ const r = await validateChunkFile(projectId, fp)
967
+ results.push({ file_path: fp, ...r })
968
+ if (r.status === 'valid') valid++
969
+ else if (r.status === 'invalid') invalid++
970
+ else notChunked++
971
+ }
972
+
973
+ return {
974
+ ok: invalid === 0,
975
+ project_id: projectId,
976
+ file_path: filePath || null,
977
+ limit,
978
+ offset,
979
+ total_files: files?.length || 0,
980
+ valid,
981
+ invalid,
982
+ not_chunked: notChunked,
983
+ results,
984
+ }
985
+ }
986
+ // CIL[DOC-VECTOR type="context" content="purpose='validateProjectFiles function'; No verified external callers; imports: .framework/mcp/shared/db.ts"]
987
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
988
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
989
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
990
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
991
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
992
+ // CIL[END stable_id="~/.framework/mcp/tools/cil/validate.ts:validateProjectFiles:function"]
993
+
994
+ /**
995
+ * Registers a tool for validating chunk hashes in a specified source file. It checks the integrity of file chunks against stored hashes after modifications.
996
+ *
997
+ * @macro Tool registration function for validating file integrity.
998
+ * @micro Reads a file from disk, computes hashes for its chunks, and compares them to stored values.
999
+ * @inputSpec
1000
+ * - server: McpServer (required) — The server instance to register the tool with.
1001
+ * @outputSpec Promise<{ content: Array<{ type: string, text: string }> }> — Returns validation results or error messages.
1002
+ * @sideEffects Reads from the file system, queries the database.
1003
+ * @stability stable
1004
+ * @calls adminDb.from, fs.existsSync, fs.readFileSync, computeChunkHash
1005
+ * @cil-generated
1006
+ */
1007
+ // CIL[BEGIN stable_id="~/.framework/mcp/tools/cil/validate.ts:register:function" type="function" identity="register" parent="~/.framework/mcp/tools/cil/validate.ts"]
1008
+ // CIL[DOC-MACRO purpose="register function"]
1009
+ // CIL[DOC-RESPONSIBILITY responsibility="Implements the register function."]
1010
+ // CIL[DOC-CONTEXT module="validate" layer="app-specific" related_files=".framework/mcp/shared/db.ts,.framework/mcp/tools/cil/validate.ts"]
1011
+ // CIL[DOC-CONTRACT inputs="server: McpServer" outputs="unknown" errors="none" side_effects="none"]
1012
+ // CIL[DOC-MICRO logic_summary="Implements the register function." notable_decisions="[]"]
1013
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
1014
+ // CIL[DOC-STABILITY stability="stable"]
1015
+ // CIL[DOC-LEARNING topic="caution" content="spine_cil_reindex_chunk hashes lines.slice(lineStart+1, lineEnd) (inner lines only) while spine_cil_validate_file hashes the inclusive range — a reindex can never clear drift_detection on its own, and neither tool updates stale line ranges. The correct post-edit sync is spine_cil_store_markers, which re-derives ranges from CIL[BEGIN/END] markers on disk"]
1016
+ // CIL[DOC-LEARNING topic="convention" content="cil_validate_docs param check: input_spec is split on commas/newlines at depth 0 and only each entry's FIRST identifier counts as a documented param; only the FIRST '(function|const|let|var) name(' signature inside a chunk (including comment text) is checked — phrases like 'function sections (with comments)' in DOC lines parse as signatures. List every param as its own 'name: type' entry"]
1017
+ export function register(server: McpServer) {
1018
+ const splitParameterList = (text: string): string[] => {
1019
+ const parts: string[] = []
1020
+ let depth = 0
1021
+ let inString = false
1022
+ let quote = ''
1023
+ let current = ''
1024
+ for (const ch of text) {
1025
+ if (inString) {
1026
+ if (ch === quote) inString = false
1027
+ current += ch
1028
+ continue
1029
+ }
1030
+ if (ch === '"' || ch === "'" || ch === '`') {
1031
+ inString = true
1032
+ quote = ch
1033
+ current += ch
1034
+ continue
1035
+ }
1036
+ if (ch === '(' || ch === '[' || ch === '<' || ch === '{') depth++
1037
+ else if (ch === ')' || ch === ']' || ch === '>' || ch === '}') depth = Math.max(0, depth - 1)
1038
+ else if ((ch === ',' || ch === '\n') && depth === 0) {
1039
+ parts.push(current)
1040
+ current = ''
1041
+ continue
1042
+ }
1043
+ current += ch
1044
+ }
1045
+ parts.push(current)
1046
+ return parts.map(s => s.trim()).filter(Boolean)
1047
+ }
1048
+
1049
+ const extractIdentifierNames = (s: string): string[] => {
1050
+ const raw = s.trim()
1051
+ if (!raw) return []
1052
+
1053
+ // Strip trailing type/default, then optional ?
1054
+ let left = raw.split(/[:=]/)[0].trim().replace(/\?$/, '')
1055
+
1056
+ // Object / array destructuring
1057
+ if ((left.startsWith('{') && left.endsWith('}')) || (left.startsWith('[') && left.endsWith(']'))) {
1058
+ const inner = left.slice(1, -1)
1059
+ const names = splitParameterList(inner)
1060
+ .map(p => p.trim().split(/[:=]/)[0].replace(/\?$/, '').replace(/^\.\.\./, ''))
1061
+ .map(p => p.match(/([a-zA-Z0-9_]+)/)?.[0] || '')
1062
+ .filter(Boolean)
1063
+ return names
1064
+ }
1065
+
1066
+ // Rest parameter or regular parameter (tolerate leading "- " / "* " list bullets)
1067
+ left = left.replace(/^\.\.\./, '')
1068
+ const m = left.match(/^[-*\s]*([a-zA-Z0-9_]+)/)
1069
+ return m ? [m[1]] : []
1070
+ }
1071
+
1072
+ server.registerTool(
1073
+ 'spine_cil_validate_file',
1074
+ {
1075
+ description: 'Re-read a source file from disk and validate all chunk hashes. Returns drift status for each chunk: current | drift_detected | not_found. Use this after editing to confirm hashes are up to date.',
1076
+ inputSchema: z.object({
1077
+ project_id: z.string().uuid().describe('The cil_project UUID'),
1078
+ file_path: z.string().describe('Relative file path from repo root, e.g. ".framework/cli/commands/auth.ts"'),
1079
+ }),
1080
+ },
1081
+ async ({ project_id, file_path }) => {
1082
+ const { data: proj } = await adminDb.from('items').select('data').eq('id', project_id).maybeSingle()
1083
+ if (!proj) return { content: [{ type: 'text', text: JSON.stringify({ error: `Project not found: ${project_id}` }) }] }
1084
+
1085
+ const resolvedRepo = proj.data?.repo_path || process.env.REPO_PATH || ''
1086
+ if (!resolvedRepo) return { content: [{ type: 'text', text: JSON.stringify({ error: 'No repo_path on project and REPO_PATH env not set' }) }] }
1087
+
1088
+ const absolutePath = `${resolvedRepo.replace(/\/$/, '')}/${file_path.replace(/^\//, '')}`
1089
+
1090
+ if (!fs.existsSync(absolutePath)) {
1091
+ return { content: [{ type: 'text', text: JSON.stringify({ error: `File not found on disk: ${absolutePath}`, file_path }) }] }
1092
+ }
1093
+
1094
+ const fileContent = fs.readFileSync(absolutePath, 'utf-8')
1095
+ const lines = fileContent.split('\n')
1096
+
1097
+ const activeVersionId = proj.data?.active_version_id
1098
+ let chunkQuery = adminDb.from('items').select('id, data')
1099
+ .filter('data->>kb_type', 'eq', 'cil_chunk')
1100
+ .filter('data->>project_id', 'eq', project_id)
1101
+ .filter('data->>file_path', 'eq', file_path)
1102
+ .eq('is_active', true)
1103
+ if (activeVersionId) {
1104
+ chunkQuery = chunkQuery.filter('data->>version_id', 'eq', activeVersionId)
1105
+ }
1106
+ const { data: chunks } = await chunkQuery
1107
+
1108
+ const results: any[] = []
1109
+ let current = 0, drifted = 0, notFound = 0
1110
+
1111
+ for (const chunk of chunks || []) {
1112
+ const cd = chunk.data || {}
1113
+ const stableId = cd.stable_id || cd.legacy_chunk_id || cd.chunk_id || chunk.id
1114
+ const lineStart = (cd.line_start || 1) - 1
1115
+ const lineEnd = (cd.line_end || 1) - 1
1116
+
1117
+ if (lineEnd >= lines.length) {
1118
+ results.push({ chunk_id: chunk.id, stable_id: stableId, chunk_name: cd.chunk_name || '', status: 'not_found', reason: 'line range out of bounds' })
1119
+ notFound++
1120
+ continue
1121
+ }
1122
+
1123
+ const extractedContent = lines.slice(lineStart, lineEnd + 1).join('\n')
1124
+ const shortHash = computeChunkHash(extractedContent)
1125
+ const fullHash = computeContentHash(extractedContent)
1126
+ const storedHash = cd.stored_hash || cd.content_hash
1127
+
1128
+ if (!storedHash) {
1129
+ results.push({ chunk_id: chunk.id, stable_id: stableId, chunk_name: cd.chunk_name || '', status: 'not_found', reason: 'no stored hash' })
1130
+ notFound++
1131
+ } else if (storedHash === shortHash || storedHash === fullHash) {
1132
+ results.push({ chunk_id: chunk.id, stable_id: stableId, chunk_name: cd.chunk_name || '', status: 'current', line_start: cd.line_start, line_end: cd.line_end })
1133
+ current++
1134
+ } else {
1135
+ results.push({ chunk_id: chunk.id, stable_id: stableId, chunk_name: cd.chunk_name || '', status: 'drift_detected', stored_hash: storedHash, computed_hash: shortHash, line_start: cd.line_start, line_end: cd.line_end })
1136
+ drifted++
1137
+ }
1138
+ }
1139
+
1140
+ const total = chunks?.length || 0
1141
+ const trustRating = total > 0 ? Math.round((current / total) * 100) : 100
1142
+
1143
+ return {
1144
+ content: [{
1145
+ type: 'text',
1146
+ text: JSON.stringify({
1147
+ project_id,
1148
+ file_path,
1149
+ summary: { total, current, drift_detected: drifted, not_found: notFound, trust_rating: trustRating },
1150
+ chunks: results,
1151
+ }, null, 2),
1152
+ }],
1153
+ }
1154
+ }
1155
+ )
1156
+
1157
+ server.registerTool(
1158
+ 'spine_cil_validate_chunk',
1159
+ {
1160
+ description: 'Strictly validate CIL chunk markers in a source file before storage. Checks marker pairing, full line coverage, valid stable IDs, boundary agreement with the AST, and JSX leakage. Returns a structured error list so the caller can fix markers before calling spine_cil_store_markers.',
1161
+ inputSchema: z.object({
1162
+ project_id: z.string().uuid().describe('The cil_project UUID'),
1163
+ file_path: z.string().describe('Relative file path from repo root, e.g. ".framework/mcp/tools/cil/validate.ts"'),
1164
+ }),
1165
+ },
1166
+ async ({ project_id, file_path }) => {
1167
+ const result = await validateChunkFile(project_id, file_path)
1168
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }
1169
+ }
1170
+ )
1171
+
1172
+ server.registerTool(
1173
+ 'spine_cil_validate_project',
1174
+ {
1175
+ description: 'Validate all selected/chunked files in a CIL project. Checks marker pairing, full line coverage, valid stable IDs, boundary agreement with the AST, and JSX leakage. Supports optional file_path scope and batching.',
1176
+ inputSchema: z.object({
1177
+ project_id: z.string().uuid().describe('The cil_project UUID'),
1178
+ file_path: z.string().optional().describe('Limit validation to a single file path'),
1179
+ limit: z.number().int().min(1).max(100).optional().describe('Max files to validate in this call (default 50)'),
1180
+ offset: z.number().int().min(0).optional().describe('File offset for batching'),
1181
+ }),
1182
+ },
1183
+ async ({ project_id, file_path, limit, offset }) => {
1184
+ const result = await validateProjectFiles(project_id, file_path, limit ?? 50, offset ?? 0)
1185
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }
1186
+ }
1187
+ )
1188
+
1189
+ server.registerTool(
1190
+ 'spine_cil_reindex_chunk',
1191
+ {
1192
+ description: 'Re-read a chunk from disk by its stable_id, recompute its hash and update code_content in the database. Call this after editing a chunk to keep the CIL in sync. Set also_embed: true to also trigger an embedding update.',
1193
+ inputSchema: z.object({
1194
+ project_id: z.string().uuid().describe('The cil_project UUID'),
1195
+ stable_id: z.string().describe('The stable_id of the chunk to reindex'),
1196
+ version_id: z.string().optional().describe('Optional version UUID to scope the lookup'),
1197
+ also_embed: z.boolean().default(false).optional().describe('If true, trigger embed_chunk after updating the hash'),
1198
+ repo_path: z.string().optional().describe('Absolute path to repo root (overrides project data.repo_path)'),
1199
+ }),
1200
+ },
1201
+ async ({ project_id, stable_id, version_id, also_embed, repo_path }) => {
1202
+ const chunk = await getChunkByStableId(project_id, stable_id, version_id)
1203
+ if (!chunk) return { content: [{ type: 'text', text: JSON.stringify({ error: `Chunk not found: ${stable_id}` }) }] }
1204
+
1205
+ const cd = chunk.data || {}
1206
+
1207
+ let resolvedRepo = repo_path || process.env.REPO_PATH || ''
1208
+ if (!resolvedRepo) {
1209
+ const { data: proj } = await adminDb.from('items').select('data').eq('id', project_id).maybeSingle()
1210
+ resolvedRepo = proj?.data?.repo_path || ''
1211
+ }
1212
+
1213
+ const filePath = cd.file_path || ''
1214
+ const absolutePath = resolvedRepo ? `${resolvedRepo.replace(/\/$/, '')}/${filePath.replace(/^\//, '')}` : filePath
1215
+
1216
+ if (!fs.existsSync(absolutePath)) {
1217
+ return { content: [{ type: 'text', text: JSON.stringify({ error: `File not found on disk: ${absolutePath}` }) }] }
1218
+ }
1219
+
1220
+ const fileContent = fs.readFileSync(absolutePath, 'utf-8')
1221
+ const lines = fileContent.split('\n')
1222
+ const lineStart = (cd.line_start || 1) - 1
1223
+ const lineEnd = (cd.line_end || 1) - 1
1224
+ const extractedContent = lines.slice(lineStart + 1, lineEnd).join('\n')
1225
+
1226
+ const newHash = computeContentHash(extractedContent)
1227
+ const now = new Date().toISOString()
1228
+
1229
+ const newData = {
1230
+ ...cd,
1231
+ code_content: extractedContent,
1232
+ stored_hash: newHash,
1233
+ content_hash: newHash,
1234
+ hash_updated_at: now,
1235
+ token_count: Math.round(extractedContent.length / 4),
1236
+ }
1237
+
1238
+ const { error } = await adminDb.from('items')
1239
+ .update({ data: newData, updated_at: now })
1240
+ .eq('id', chunk.id)
1241
+
1242
+ if (error) return { content: [{ type: 'text', text: JSON.stringify({ error: `Failed to update chunk: ${error.message}` }) }] }
1243
+
1244
+ const result: any = {
1245
+ ok: true,
1246
+ chunk_id: chunk.id,
1247
+ stable_id,
1248
+ version_id: version_id || cd.version_id || null,
1249
+ chunk_name: cd.chunk_name || '',
1250
+ file_path: filePath,
1251
+ new_hash: newHash,
1252
+ hash_updated_at: now,
1253
+ lines_extracted: lineEnd - lineStart + 1,
1254
+ }
1255
+
1256
+ if (also_embed) {
1257
+ result.embed_note = `Re-embedding requires the Netlify function endpoint. Use POST /api/custom_cil-embeddings?action=embed_chunk with { project_id, stable_id, version_id }`
1258
+ }
1259
+
1260
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }
1261
+ }
1262
+ )
1263
+
1264
+ server.registerTool(
1265
+ 'spine_cil_validate_docs',
1266
+ {
1267
+ description: 'Validate that chunks meet the canonical inline documentation contract. Returns chunks that are missing required fields (module, audience, layer, macro, micro, input_spec, output_spec, side_effects, stability) or have undocumented parameters. For enriched chunks, also checks required DOC-VECTOR types and related_files.',
1268
+ inputSchema: z.object({
1269
+ project_id: z.string().uuid().describe('The cil_project UUID'),
1270
+ file_path: z.string().optional().describe('Limit validation to a single file path'),
1271
+ }),
1272
+ },
1273
+ async ({ project_id, file_path }) => {
1274
+ const PAGE_SIZE = 500
1275
+ const allChunks: any[] = []
1276
+ let from = 0
1277
+ while (true) {
1278
+ let q = adminDb.from('items').select('id, title, data')
1279
+ .filter('data->>kb_type', 'eq', 'cil_chunk')
1280
+ .filter('data->>project_id', 'eq', project_id)
1281
+ .eq('is_active', true)
1282
+ .order('id')
1283
+ .range(from, from + PAGE_SIZE - 1)
1284
+ if (file_path) q = (q as any).filter('data->>file_path', 'eq', file_path)
1285
+
1286
+ const { data: chunks, error } = await q
1287
+ if (error) return { content: [{ type: 'text', text: JSON.stringify({ error: `Failed to load chunks: ${error.message}` }) }] }
1288
+ if (!chunks || chunks.length === 0) break
1289
+ allChunks.push(...chunks)
1290
+ if (chunks.length < PAGE_SIZE) break
1291
+ from += PAGE_SIZE
1292
+ }
1293
+
1294
+ const requiredModule = ['module', 'audience', 'layer', 'stability', 'description']
1295
+ const requiredChunk = ['description', 'macro', 'micro', 'input_spec', 'output_spec', 'side_effects', 'stability']
1296
+
1297
+ const invalid: any[] = []
1298
+ for (const chunk of allChunks) {
1299
+ const d = chunk.data || {}
1300
+ const isModule = d.chunk_type === 'module_header'
1301
+ const required = isModule ? requiredModule : requiredChunk
1302
+ const errors: string[] = []
1303
+ for (const field of required) {
1304
+ if (!d[field] || String(d[field]).trim() === '') errors.push(`missing ${field}`)
1305
+ }
1306
+
1307
+ const hasVectors = d.vector_content && Object.keys(d.vector_content).length > 0
1308
+ if (hasVectors) {
1309
+ const requiredVectors = ['context', 'blast_radius', 'configuration', 'constraint']
1310
+ for (const v of requiredVectors) {
1311
+ const vec = d.vector_content[v]
1312
+ if (!vec || vec.status !== 'ready' || !String(vec.content).trim()) {
1313
+ errors.push(`missing or non-ready DOC-VECTOR: ${v}`)
1314
+ }
1315
+ }
1316
+ const relatedFilesRaw = d.related_files || d.see_also || ''
1317
+ const relatedFilesTrimmed = String(relatedFilesRaw).trim()
1318
+ const hasRelatedFiles = relatedFilesTrimmed !== '' && relatedFilesTrimmed !== '[]'
1319
+ if (!hasRelatedFiles) {
1320
+ errors.push('missing related_files')
1321
+ }
1322
+ }
1323
+
1324
+ if (!isModule && d.input_spec && d.input_spec !== 'none') {
1325
+ const paramNames = splitParameterList(d.input_spec).flatMap(extractIdentifierNames)
1326
+ const code = d.code_content || ''
1327
+ const sig = code.match(/(?:function|const|let|var)\s+\w+\s*\(([^)]*)\)/)
1328
+ if (sig) {
1329
+ const declared = splitParameterList(sig[1]).flatMap(extractIdentifierNames)
1330
+ for (const name of declared) {
1331
+ if (!paramNames.includes(name)) errors.push(`undocumented parameter: ${name}`)
1332
+ }
1333
+ }
1334
+ }
1335
+
1336
+ if (errors.length > 0) {
1337
+ invalid.push({
1338
+ chunk_id: chunk.id,
1339
+ chunk_name: d.chunk_name || chunk.title,
1340
+ file_path: d.file_path || '',
1341
+ chunk_type: d.chunk_type || '',
1342
+ errors,
1343
+ })
1344
+ }
1345
+ }
1346
+
1347
+ return {
1348
+ content: [{
1349
+ type: 'text',
1350
+ text: JSON.stringify({
1351
+ project_id,
1352
+ file_path: file_path || null,
1353
+ total_chunks: allChunks.length,
1354
+ valid_chunks: allChunks.length - invalid.length,
1355
+ invalid_chunks: invalid.length,
1356
+ invalid,
1357
+ }, null, 2),
1358
+ }],
1359
+ }
1360
+ }
1361
+ )
1362
+ }
1363
+ // CIL[DOC-VECTOR type="context" content="purpose='register function'; No verified external callers; imports: .framework/mcp/shared/db.ts"]
1364
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
1365
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
1366
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
1367
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
1368
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
1369
+ // CIL[END stable_id="~/.framework/mcp/tools/cil/validate.ts:register:function"]