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
@@ -1,70 +1,119 @@
1
-
2
-
3
-
4
-
5
-
6
-
7
- // CIL[BEGIN chunk_id="test_moduleheader_1784068794" type="module_header" identity="imports" parent="none"]
8
- // CIL[DOC-MACRO purpose="Import dependencies and set up test configuration"]
9
- // CIL[DOC-RESPONSIBILITY responsibility="Load required modules and define test types"]
10
- // CIL[DOC-CONTEXT module="cli" layer="commands" related_files="[\"custom/apps/portal/pages/ContentPage.tsx\",\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\".framework/src/pages/admin/RolesPage.tsx\",\".framework/src/components/ui/dialog.tsx\",\"\\\"]"]
11
- // CIL[DOC-CONTRACT inputs="none" outputs="loaded_modules" errors="none" side_effects="none"]
12
- // CIL[DOC-MICRO logic_summary="Imports Commander, child process, path utilities and defines TestSuite type" notable_decisions="[]"]
13
- // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
14
- // CIL[DOC-STABILITY stability="stable"]
15
- // CIL[DOC-AUDIENCE audience="developer"]
16
- // CIL[DOC-VECTOR type="context" content="Layer: commands. Direct callers: 0. Direct calls: 0. Related files: custom/apps/portal/pages/ContentPage.tsx, custom/apps/cortex/pages/operations/OperationsDashboard.tsx, .framework/src/pages/admin/RolesPage.tsx, .framework/src/components/ui/dialog.tsx, \."]
17
- // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
18
- // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
19
- // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
20
- // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
21
- // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
1
+ // CIL[BEGIN stable_id="~/.framework/cli/commands/test.ts:module_header:module_header" type="module_header" identity="module_header" parent="~/.framework/cli/commands/test.ts"]
2
+ // CIL[DOC-MACRO purpose="Module-level setup and shared imports for the CLI test command"]
3
+ // CIL[DOC-RESPONSIBILITY responsibility="Provide ESM path helpers, type definitions, and runner configuration used by test command functions"]
4
+ // CIL[DOC-CONTEXT module="cli" layer="commands" related_files=".framework/cli/commands/test.ts"]
5
+ // CIL[DOC-CONTRACT inputs="" outputs="" errors="" side_effects=""]
6
+ // CIL[DOC-MICRO logic_summary="Imports Commander, child_process, and path helpers; defines constants and types for test execution" notable_decisions="ESM-compatible __filename and __dirname resolution"]
7
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
8
+ // CIL[DOC-STABILITY stable]
9
+ // CIL[DOC-AUDIENCE audience="cli developer, maintainer"]
22
10
  import type { Command } from 'commander'
23
11
  import { spawn } from 'child_process'
24
12
  import { resolve, dirname } from 'path'
25
13
  import { fileURLToPath } from 'url'
26
-
14
+ // CIL[DOC-VECTOR type="context" content="purpose='Module-level setup and shared imports for the CLI test command'; No verified external callers; imports: none"]
15
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
16
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
17
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
18
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
19
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
20
+ // CIL[END stable_id="~/.framework/cli/commands/test.ts:module_header:module_header"]
21
+
22
+ // CIL[BEGIN stable_id="~/.framework/cli/commands/test.ts:__filename:constant" type="constant" identity="__filename" parent="~/.framework/cli/commands/test.ts"]
23
+ // CIL[DOC-MACRO purpose="Compute the absolute file path for this ESM module"]
24
+ // CIL[DOC-RESPONSIBILITY responsibility="Resolve the current modules __filename equivalent for path calculations"]
25
+ // CIL[DOC-CONTEXT module="cli" layer="commands" related_files=".framework/cli/commands/test.ts"]
26
+ // CIL[DOC-CONTRACT inputs="import.meta.url" outputs="string absolute file path" errors="none" side_effects="none"]
27
+ // CIL[DOC-MICRO logic_summary="Converts the ESM import.meta.url to a file URL path using fileURLToPath" notable_decisions="Polyfills CommonJS __filename in an ESM module"]
28
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
29
+ // CIL[DOC-STABILITY stable]
27
30
  const __filename = fileURLToPath(import.meta.url)
31
+ // CIL[DOC-VECTOR type="context" content="purpose='Compute the absolute file path for this ESM module'; No verified external callers; imports: none"]
32
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
33
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
34
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
35
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
36
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
37
+ // CIL[END stable_id="~/.framework/cli/commands/test.ts:__filename:constant"]
38
+ // CIL[BEGIN stable_id="~/.framework/cli/commands/test.ts:__dirname:constant" type="constant" identity="__dirname" parent="~/.framework/cli/commands/test.ts"]
39
+ // CIL[DOC-MACRO purpose="Compute the directory path of this ESM module"]
40
+ // CIL[DOC-RESPONSIBILITY responsibility="Resolve the current modules __dirname equivalent for project-root lookup"]
41
+ // CIL[DOC-CONTEXT module="cli" layer="commands" related_files=".framework/cli/commands/test.ts"]
42
+ // CIL[DOC-CONTRACT inputs="__filename" outputs="string directory path" errors="none" side_effects="none"]
43
+ // CIL[DOC-MICRO logic_summary="Uses path.dirname to derive the directory from the ESM __filename" notable_decisions="Polyfills CommonJS __dirname in an ESM module"]
44
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
45
+ // CIL[DOC-STABILITY stable]
28
46
  const __dirname = dirname(__filename)
47
+ // CIL[DOC-VECTOR type="context" content="purpose='Compute the directory path of this ESM module'; No verified external callers; imports: none"]
48
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
49
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
50
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
51
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
52
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
53
+ // CIL[END stable_id="~/.framework/cli/commands/test.ts:__dirname:constant"]
54
+ // CIL[BEGIN stable_id="~/.framework/cli/commands/test.ts:PROJECT_ROOT:constant" type="constant" identity="PROJECT_ROOT" parent="~/.framework/cli/commands/test.ts"]
55
+ // CIL[DOC-MACRO purpose="Resolve the absolute project root from the CLI command location"]
56
+ // CIL[DOC-RESPONSIBILITY responsibility="Provide the root directory used as working directory for test runners"]
57
+ // CIL[DOC-CONTEXT module="cli" layer="commands" related_files=".framework/cli/commands/test.ts"]
58
+ // CIL[DOC-CONTRACT inputs="__dirname" outputs="string project root path" errors="none" side_effects="none"]
59
+ // CIL[DOC-MICRO logic_summary="Resolves three parent directories up from .framework/cli/commands" notable_decisions="Hardcoded relative traversal matches the framework path layout"]
60
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
61
+ // CIL[DOC-STABILITY stable]
29
62
  const PROJECT_ROOT = resolve(__dirname, '../../../')
30
-
63
+ // CIL[DOC-VECTOR type="context" content="purpose='Resolve the absolute project root from the CLI command location'; No verified external callers; imports: none"]
64
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
65
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
66
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
67
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
68
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
69
+ // CIL[END stable_id="~/.framework/cli/commands/test.ts:PROJECT_ROOT:constant"]
70
+
71
+ // CIL[BEGIN stable_id="~/.framework/cli/commands/test.ts:TestSuite:type" type="type" identity="TestSuite" parent="~/.framework/cli/commands/test.ts"]
72
+ // CIL[DOC-MACRO purpose="Define the union of supported test suite identifiers"]
73
+ // CIL[DOC-RESPONSIBILITY responsibility="Type-check the suite argument accepted by the test command"]
74
+ // CIL[DOC-CONTEXT module="cli" layer="commands" related_files=".framework/cli/commands/test.ts"]
75
+ // CIL[DOC-CONTRACT inputs="none" outputs="TestSuite union type" errors="none" side_effects="none"]
76
+ // CIL[DOC-MICRO logic_summary="Exports a string literal union of unit, integration, api, ui, and all" notable_decisions="Keeps suite names as a single source of truth for CLI validation"]
77
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
78
+ // CIL[DOC-STABILITY stable]
31
79
  type TestSuite = 'unit' | 'integration' | 'api' | 'ui' | 'all'
32
- // CIL[END chunk_id="test_moduleheader_1784068794"]
33
- // CIL[BEGIN chunk_id="test_suites_1784068794" type="variable" identity="SUITES" parent="none"]
34
- // CIL[DOC-MACRO purpose="Define test suite configurations"]
35
- // CIL[DOC-RESPONSIBILITY responsibility="Configure test patterns and runners"]
36
- // CIL[DOC-CONTEXT module="cli" layer="commands" related_files="[\".framework/src/pages/admin/IntegrationsPage.tsx\",\".framework/functions/_shared/principal.ts\",\".framework/functions/_shared/schema-utils.ts\",\"custom/apps/cortex/functions/custom_cil-embeddings.ts\",\"\\\"]"]
37
- // CIL[DOC-CONTRACT inputs="none" outputs="suite_config" errors="none" side_effects="none"]
38
- // CIL[DOC-MICRO logic_summary="Maps test suite names to file patterns and test runners" notable_decisions="[]"]
39
- // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
40
- // CIL[DOC-STABILITY stability="stable"]
41
- // CIL[DOC-VECTOR type="context" content="Layer: commands. Direct callers: 0. Direct calls: 0. Related files: .framework/src/pages/admin/IntegrationsPage.tsx, .framework/functions/_shared/principal.ts, .framework/functions/_shared/schema-utils.ts, custom/apps/cortex/functions/custom_cil-embeddings.ts, \."]
42
- // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
43
- // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
44
- // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
80
+ // CIL[DOC-VECTOR type="context" content="purpose='Define the union of supported test suite identifiers'; No verified external callers; imports: none"]
81
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
82
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
83
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
45
84
  // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
46
85
  // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
86
+ // CIL[END stable_id="~/.framework/cli/commands/test.ts:TestSuite:type"]
87
+ // CIL[BEGIN stable_id="~/.framework/cli/commands/test.ts:SUITES:constant" type="constant" identity="SUITES" parent="~/.framework/cli/commands/test.ts"]
88
+ // CIL[DOC-MACRO purpose="Map suite names to their test directory patterns and runner executables"]
89
+ // CIL[DOC-RESPONSIBILITY responsibility="Centralize suite configuration consumed by registerTestCommands and runner functions"]
90
+ // CIL[DOC-CONTEXT module="cli" layer="commands" related_files=".framework/cli/commands/test.ts"]
91
+ // CIL[DOC-CONTRACT inputs="none" outputs="Record<string, {pattern: string, runner: vitest|playwright|node}>" errors="none" side_effects="none"]
92
+ // CIL[DOC-MICRO logic_summary="Defines a lookup of suite patterns under v2-core/tests and the runner to invoke" notable_decisions="Defaults all non-ui suites to vitest; ui suite uses playwright"]
93
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
94
+ // CIL[DOC-STABILITY stable]
47
95
  const SUITES: Record<string, { pattern: string; runner: 'vitest' | 'playwright' | 'node' }> = {
48
96
  unit: { pattern: 'v2-core/tests/unit', runner: 'vitest' },
49
97
  integration: { pattern: 'v2-core/tests/integration', runner: 'vitest' },
50
98
  api: { pattern: 'v2-core/tests/api', runner: 'vitest' },
51
99
  ui: { pattern: 'v2-core/tests/ui', runner: 'playwright' }
52
100
  }
53
- // CIL[END chunk_id="test_suites_1784068794"]
54
- // CIL[BEGIN chunk_id="test_registertestcommands_1784068794" type="function" identity="registerTestCommands" parent="none"]
55
- // CIL[DOC-MACRO purpose="Register test commands with CLI program"]
56
- // CIL[DOC-RESPONSIBILITY responsibility="Configure and execute test suite commands"]
57
- // CIL[DOC-CONTEXT module="cli" layer="commands" related_files="[\".framework/functions/_shared/schema-utils.ts\",\".framework/src/components/ui/dialog.tsx\",\".framework/functions/_shared/principal.ts\",\".framework/src/components/ui/table-primitives.tsx\",\".framework/src/pages/admin/TypeDetailPage.tsx\",\"\\\"]"]
58
- // CIL[DOC-CONTRACT inputs="none" outputs="test_execution" errors="test_errors" side_effects="process_exit"]
59
- // CIL[DOC-MICRO logic_summary="Registers test command with options for suite, watch, reporter, and JSON output" notable_decisions="[]"]
60
- // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="complex_dependencies"]
61
- // CIL[DOC-STABILITY stability="stable"]
62
- // CIL[DOC-VECTOR type="context" content="Layer: commands. Direct callers: 0. Direct calls: 0. Related files: .framework/functions/_shared/schema-utils.ts, .framework/src/components/ui/dialog.tsx, .framework/functions/_shared/principal.ts, .framework/src/components/ui/table-primitives.tsx, .framework/src/pages/admin/TypeDetailPage.tsx, \."]
63
- // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
64
- // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
65
- // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
101
+ // CIL[DOC-VECTOR type="context" content="purpose='Map suite names to their test directory patterns and runner executables'; No verified external callers; imports: none"]
102
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
103
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
104
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
66
105
  // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
67
106
  // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
107
+ // CIL[END stable_id="~/.framework/cli/commands/test.ts:SUITES:constant"]
108
+
109
+ // CIL[BEGIN stable_id="~/.framework/cli/commands/test.ts:registerTestCommands:function" type="function" identity="registerTestCommands" parent="~/.framework/cli/commands/test.ts"]
110
+ // CIL[DOC-MACRO purpose="Register the test subcommand with the Commander CLI program"]
111
+ // CIL[DOC-RESPONSIBILITY responsibility="Wire the test command, validate the suite argument, run suites, and print a summary"]
112
+ // CIL[DOC-CONTEXT module="cli" layer="commands" related_files=".framework/cli/commands/test.ts"]
113
+ // CIL[DOC-CONTRACT inputs="program: Command" outputs="void" errors="console.error and process.exit(1) on unknown suite" side_effects="registers test subcommand, logs to console, calls process.exit with 0 or 1"]
114
+ // CIL[DOC-MICRO logic_summary="Parses suite and options, iterates over suites, dispatches runner, collects results, and prints JSON or text summary" notable_decisions="Unknown suites cause immediate stderr and exit; all runs all known suites in order"]
115
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
116
+ // CIL[DOC-STABILITY stable]
68
117
  export function registerTestCommands(program: Command) {
69
118
  const testCmd = program
70
119
  .command('test [suite]')
@@ -154,21 +203,22 @@ export function registerTestCommands(program: Command) {
154
203
  process.exit(totalFailed > 0 ? 1 : 0)
155
204
  })
156
205
  }
157
- // CIL[END chunk_id="test_registertestcommands_1784068794"]
158
- // CIL[BEGIN chunk_id="test_runvitest_1784068794" type="function" identity="runVitest" parent="none"]
159
- // CIL[DOC-MACRO purpose="Execute Vitest test runner"]
160
- // CIL[DOC-RESPONSIBILITY responsibility="Run unit/integration/API tests with Vitest"]
161
- // CIL[DOC-CONTEXT module="cli" layer="commands" related_files="[\".framework/migrations/015_simplify_apps_table.sql\",\"\\\"]"]
162
- // CIL[DOC-CONTRACT inputs="none" outputs="exit_code" errors="vitest_errors" side_effects="process_spawn"]
163
- // CIL[DOC-MICRO logic_summary="Configures and runs Vitest with pattern, config, watch mode, and reporter options" notable_decisions="[]"]
164
- // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="external_symbols_unknown"]
165
- // CIL[DOC-STABILITY stability="stable"]
166
- // CIL[DOC-VECTOR type="context" content="Layer: commands. Direct callers: 0. Direct calls: 0. Related files: .framework/migrations/015_simplify_apps_table.sql, \."]
167
- // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
168
- // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
169
- // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
206
+ // CIL[DOC-VECTOR type="context" content="purpose='Register the test subcommand with the Commander CLI program'; No verified external callers; imports: none"]
207
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
208
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
209
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
170
210
  // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
171
211
  // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
212
+ // CIL[END stable_id="~/.framework/cli/commands/test.ts:registerTestCommands:function"]
213
+
214
+ // CIL[BEGIN stable_id="~/.framework/cli/commands/test.ts:runVitest:function" type="function" identity="runVitest" parent="~/.framework/cli/commands/test.ts"]
215
+ // CIL[DOC-MACRO purpose="Assemble and execute a Vitest test run for a given suite"]
216
+ // CIL[DOC-RESPONSIBILITY responsibility="Build Vitest CLI arguments and return the exit code"]
217
+ // CIL[DOC-CONTEXT module="cli" layer="commands" related_files=".framework/cli/commands/test.ts"]
218
+ // CIL[DOC-CONTRACT inputs="suite: string, opts: any" outputs="number (exit code)" errors="rejects if npx vitest fails to spawn or exits with an error event" side_effects="spawns npx vitest child process with inherited stdio"]
219
+ // CIL[DOC-MICRO logic_summary="Constructs run/watch and reporter arguments, then delegates to runCommand" notable_decisions="Adds a custom reporter from v2-core/tests/reporter.ts when default is selected"]
220
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
221
+ // CIL[DOC-STABILITY stable]
172
222
  async function runVitest(suite: string, opts: any): Promise<number> {
173
223
  const config = SUITES[suite]
174
224
  const args = [
@@ -190,21 +240,22 @@ async function runVitest(suite: string, opts: any): Promise<number> {
190
240
 
191
241
  return runCommand('npx', ['vitest', ...args], PROJECT_ROOT)
192
242
  }
193
- // CIL[END chunk_id="test_runvitest_1784068794"]
194
- // CIL[BEGIN chunk_id="test_runplaywright_1784068794" type="function" identity="runPlaywright" parent="none"]
195
- // CIL[DOC-MACRO purpose="Execute Playwright test runner"]
196
- // CIL[DOC-RESPONSIBILITY responsibility="Run UI tests with Playwright"]
197
- // CIL[DOC-CONTEXT module="cli" layer="commands" related_files="[\".framework/migrations/015_simplify_apps_table.sql\",\"\\\"]"]
198
- // CIL[DOC-CONTRACT inputs="none" outputs="exit_code" errors="playwright_errors" side_effects="process_spawn"]
199
- // CIL[DOC-MICRO logic_summary="Configures and runs Playwright with pattern and JSON reporter option" notable_decisions="[]"]
200
- // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="external_symbols_unknown"]
201
- // CIL[DOC-STABILITY stability="stable"]
202
- // CIL[DOC-VECTOR type="context" content="Layer: commands. Direct callers: 0. Direct calls: 0. Related files: .framework/migrations/015_simplify_apps_table.sql, \."]
203
- // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
204
- // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
205
- // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
243
+ // CIL[DOC-VECTOR type="context" content="purpose='Assemble and execute a Vitest test run for a given suite'; No verified external callers; imports: none"]
244
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
245
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
246
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
206
247
  // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
207
248
  // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
249
+ // CIL[END stable_id="~/.framework/cli/commands/test.ts:runVitest:function"]
250
+
251
+ // CIL[BEGIN stable_id="~/.framework/cli/commands/test.ts:runPlaywright:function" type="function" identity="runPlaywright" parent="~/.framework/cli/commands/test.ts"]
252
+ // CIL[DOC-MACRO purpose="Assemble and execute a Playwright test run for the UI suite"]
253
+ // CIL[DOC-RESPONSIBILITY responsibility="Build Playwright CLI arguments and return the exit code"]
254
+ // CIL[DOC-CONTEXT module="cli" layer="commands" related_files=".framework/cli/commands/test.ts"]
255
+ // CIL[DOC-CONTRACT inputs="suite: string, opts: any" outputs="number (exit code)" errors="rejects if npx playwright fails to spawn or exits with an error event" side_effects="spawns npx playwright child process with inherited stdio; logs dev server requirement"]
256
+ // CIL[DOC-MICRO logic_summary="Constructs playwright test arguments and optionally enables JSON reporter" notable_decisions="Assumes a dev server is running on localhost:8888"]
257
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
258
+ // CIL[DOC-STABILITY stable]
208
259
  async function runPlaywright(suite: string, opts: any): Promise<number> {
209
260
  console.log(' (Playwright tests require dev server on localhost:8888)')
210
261
 
@@ -216,40 +267,42 @@ async function runPlaywright(suite: string, opts: any): Promise<number> {
216
267
 
217
268
  return runCommand('npx', args, PROJECT_ROOT)
218
269
  }
219
- // CIL[END chunk_id="test_runplaywright_1784068794"]
220
- // CIL[BEGIN chunk_id="test_runnode_1784068794" type="function" identity="runNode" parent="none"]
221
- // CIL[DOC-MACRO purpose="Execute Node.js test runner"]
222
- // CIL[DOC-RESPONSIBILITY responsibility="Run tests directly with Node.js"]
223
- // CIL[DOC-CONTEXT module="cli" layer="commands" related_files="[]"]
224
- // CIL[DOC-CONTRACT inputs="none" outputs="exit_code" errors="node_errors" side_effects="process_spawn"]
225
- // CIL[DOC-MICRO logic_summary="Runs test files directly with Node.js using suite pattern" notable_decisions="[]"]
226
- // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="external_symbols_unknown"]
227
- // CIL[DOC-STABILITY stability="stable"]
228
- // CIL[DOC-VECTOR type="context" content="Layer: commands. Direct callers: 0. Direct calls: 0. Related files: none."]
229
- // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
230
- // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
231
- // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
270
+ // CIL[DOC-VECTOR type="context" content="purpose='Assemble and execute a Playwright test run for the UI suite'; No verified external callers; imports: none"]
271
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
272
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
273
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
232
274
  // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
233
275
  // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
276
+ // CIL[END stable_id="~/.framework/cli/commands/test.ts:runPlaywright:function"]
277
+
278
+ // CIL[BEGIN stable_id="~/.framework/cli/commands/test.ts:runNode:function" type="function" identity="runNode" parent="~/.framework/cli/commands/test.ts"]
279
+ // CIL[DOC-MACRO purpose="Execute a test file directly with Node.js"]
280
+ // CIL[DOC-RESPONSIBILITY responsibility="Run a suites test directory as a Node.js module and return the exit code"]
281
+ // CIL[DOC-CONTEXT module="cli" layer="commands" related_files=".framework/cli/commands/test.ts"]
282
+ // CIL[DOC-CONTRACT inputs="suite: string, opts: any" outputs="number (exit code)" errors="rejects if node process fails to spawn or emits an error event" side_effects="spawns node child process with inherited stdio"]
283
+ // CIL[DOC-MICRO logic_summary="Looks up the suite pattern and spawns node with it as the entry point" notable_decisions="Relies on Node.js for simple or one-off test scripts"]
284
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
285
+ // CIL[DOC-STABILITY stable]
234
286
  async function runNode(suite: string, opts: any): Promise<number> {
235
287
  const args = [SUITES[suite].pattern]
236
288
  return runCommand('node', args, PROJECT_ROOT)
237
289
  }
238
- // CIL[END chunk_id="test_runnode_1784068794"]
239
- // CIL[BEGIN chunk_id="test_runcommand_1784068794" type="function" identity="runCommand" parent="none"]
240
- // CIL[DOC-MACRO purpose="Execute shell command with promise wrapper"]
241
- // CIL[DOC-RESPONSIBILITY responsibility="Spawn child process and handle exit/error"]
242
- // CIL[DOC-CONTEXT module="cli" layer="commands" related_files="[]"]
243
- // CIL[DOC-CONTRACT inputs="none" outputs="exit_code" errors="spawn_errors" side_effects="process_spawn"]
244
- // CIL[DOC-MICRO logic_summary="Wraps child_process.spawn in Promise with proper error handling" notable_decisions="[]"]
245
- // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
246
- // CIL[DOC-STABILITY stability="stable"]
247
- // CIL[DOC-VECTOR type="context" content="Layer: commands. Direct callers: 0. Direct calls: 0. Related files: none."]
248
- // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
249
- // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
250
- // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
290
+ // CIL[DOC-VECTOR type="context" content="purpose='Execute a test file directly with Node.js'; No verified external callers; imports: none"]
291
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
292
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
293
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
251
294
  // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
252
295
  // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
296
+ // CIL[END stable_id="~/.framework/cli/commands/test.ts:runNode:function"]
297
+
298
+ // CIL[BEGIN stable_id="~/.framework/cli/commands/test.ts:runCommand:function" type="function" identity="runCommand" parent="~/.framework/cli/commands/test.ts"]
299
+ // CIL[DOC-MACRO purpose="Spawn a shell command and return its exit code"]
300
+ // CIL[DOC-RESPONSIBILITY responsibility="Abstract child_process spawn for test runners"]
301
+ // CIL[DOC-CONTEXT module="cli" layer="commands" related_files=".framework/cli/commands/test.ts"]
302
+ // CIL[DOC-CONTRACT inputs="command: string, args: string[], cwd: string" outputs="number (exit code)" errors="rejects on spawn error" side_effects="spawns a child process with inherited stdio and shell mode"]
303
+ // CIL[DOC-MICRO logic_summary="Returns a promise that resolves when the child process exits and rejects on spawn errors" notable_decisions="Uses shell: true and stdio: inherit to preserve terminal output"]
304
+ // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
305
+ // CIL[DOC-STABILITY stable]
253
306
  function runCommand(command: string, args: string[], cwd: string): Promise<number> {
254
307
  return new Promise((resolve, reject) => {
255
308
  const proc = spawn(command, args, {
@@ -268,4 +321,10 @@ function runCommand(command: string, args: string[], cwd: string): Promise<numbe
268
321
  })
269
322
  })
270
323
  }
271
- // CIL[END chunk_id="test_runcommand_1784068794"]
324
+ // CIL[DOC-VECTOR type="context" content="purpose='Spawn a shell command and return its exit code'; No verified external callers; imports: none"]
325
+ // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
326
+ // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
327
+ // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
328
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
329
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
330
+ // CIL[END stable_id="~/.framework/cli/commands/test.ts:runCommand:function"]
@@ -0,0 +1,260 @@
1
+ // CIL[BEGIN stable_id="~/.framework/cli/commands/upgrade.ts:module_header:module_header" type="module_header" identity="module_header" parent="~/.framework/cli/commands/upgrade.ts"]
2
+ // CIL[DOC-MACRO purpose="Sync an installed project's framework-owned files to the spine-framework version in node_modules"]
3
+ // CIL[DOC-RESPONSIBILITY responsibility="Verify the current tree against the frozen .spine-manifest.json, then overwrite .framework/config/scripts/bin from the installed package, merge framework deps+scripts into package.json, re-render netlify.toml preserving app-owned custom_* sections, and regenerate the manifest at the new framework_version"]
4
+ // CIL[DOC-CONTEXT module="cli" layer="framework" related_files=".framework/cli/lib/project-files.ts,.framework/templates/project.json,.framework/cli/commands/init.ts"]
5
+ // CIL[DOC-CONTRACT inputs="[--dry-run] [--force]; runs in the project root; new version comes from node_modules/spine-framework" outputs="synced tree + regenerated manifest + change report" errors="exit 1 when no manifest, package absent/older, or protected files were locally modified (without --force)" side_effects="writes framework-owned paths only; --dry-run writes nothing"]
6
+ // CIL[DOC-MICRO logic_summary="The executing command is always the NEW package's copy — npx spine-framework upgrade resolves node_modules. Deletions are limited to .framework/ entries tracked in the old manifest but absent upstream; config/scripts files dropped upstream are warned, not deleted. custom/, spine.config.json, .env, .spine/, and netlify.toml custom_* sections are never touched" notable_decisions="integrity abort is the safety rail — a sealed .framework/ tree means overwrite-on-upgrade loses nothing the user owned"]
7
+ // CIL[DOC-CONFIDENCE pass="first" score="0.85" flags="none"]
8
+ // CIL[DOC-STABILITY stability="experimental"]
9
+ // CIL[DOC-NOTE type="design" content="Upgrade is file-sync, not git-aware; rollback = git. Migrations are reported, never run — `npx spine-framework migrate` applies them"]
10
+ // CIL[DOC-AUDIENCE audience="developer"]
11
+
12
+ import type { Command } from 'commander'
13
+ import { existsSync, readFileSync, writeFileSync, rmSync, lstatSync } from 'fs'
14
+ import { resolve, dirname, join } from 'path'
15
+ import { fileURLToPath } from 'url'
16
+ import { hashEntry } from '../../../scripts/checksum-manifest.mts'
17
+ import {
18
+ loadProjectProfile,
19
+ collectFrameworkFiles,
20
+ copyCollectedFiles,
21
+ renderNetlifyToml,
22
+ mergePackageJson,
23
+ writeFrozenManifest,
24
+ cmpSemver,
25
+ } from '../lib/project-files.ts'
26
+ // CIL[END stable_id="~/.framework/cli/commands/upgrade.ts:module_header:module_header"]
27
+
28
+ // CIL[BEGIN stable_id="~/.framework/cli/commands/upgrade.ts:paths:block" type="block" identity="paths" parent="~/.framework/cli/commands/upgrade.ts"]
29
+ // CIL[DOC-MACRO purpose="Path constants — PACKAGE_ROOT is the new version's source of truth; PROJECT_ROOT is the installed project being upgraded"]
30
+ // CIL[DOC-CONTEXT module="cli" layer="framework" related_files="package.json,.spine-manifest.json"]
31
+ // CIL[DOC-CONTRACT inputs="none" outputs="resolved path constants" errors="none" side_effects="none"]
32
+ // CIL[DOC-MICRO logic_summary="PACKAGE_ROOT resolves inside node_modules/spine-framework — the executing command is always the NEW package's copy; PROJECT_ROOT is process.cwd()" notable_decisions="new code upgrades old files, never the reverse"]
33
+ // CIL[DOC-CONFIDENCE pass="first" score="0.95" flags="none"]
34
+ // CIL[DOC-STABILITY stability="stable"]
35
+ // CIL[DOC-AUDIENCE audience="developer"]
36
+ const __filename = fileURLToPath(import.meta.url)
37
+ const __dirname = dirname(__filename)
38
+
39
+ const PACKAGE_ROOT = resolve(__dirname, '../../../')
40
+ const PROJECT_ROOT = process.cwd()
41
+
42
+ const MANIFEST_PATH = join(PROJECT_ROOT, '.spine-manifest.json')
43
+ // CIL[END stable_id="~/.framework/cli/commands/upgrade.ts:paths:block"]
44
+
45
+ // CIL[BEGIN stable_id="~/.framework/cli/commands/upgrade.ts:registerUpgradeCommands:function" type="function" identity="registerUpgradeCommands" parent="~/.framework/cli/commands/upgrade.ts"]
46
+ // CIL[DOC-MACRO purpose="Register `upgrade [--dry-run] [--force]` — delegates to runUpgrade with a fatal-error wrapper"]
47
+ // CIL[DOC-CONTEXT module="cli" layer="framework" related_files=".framework/cli/index.ts"]
48
+ // CIL[DOC-CONTRACT inputs="program: Command" outputs="void" errors="catches runUpgrade errors → exit 1 (stack behind SPINE_CLI_DEBUG)" side_effects="mutates Commander program; may write project files via runUpgrade"]
49
+ // CIL[DOC-MICRO logic_summary="Thin Commander wiring — all logic lives in runUpgrade" notable_decisions="error wrapper prints message + exits; stack only under SPINE_CLI_DEBUG"]
50
+ // CIL[DOC-CONFIDENCE pass="first" score="0.95" flags="none"]
51
+ // CIL[DOC-STABILITY stability="stable"]
52
+ // CIL[DOC-AUDIENCE audience="developer"]
53
+ export function registerUpgradeCommands(program: Command) {
54
+ program
55
+ .command('upgrade')
56
+ .description('Sync this project to the spine-framework version installed in node_modules')
57
+ .option('--dry-run', 'Report the changes without writing anything', false)
58
+ .option('--force', 'Overwrite locally modified framework files', false)
59
+ .action(async (opts: { dryRun: boolean; force: boolean }) => {
60
+ try {
61
+ runUpgrade(opts)
62
+ } catch (err: any) {
63
+ console.error(`\n❌ Upgrade failed: ${err.message}`)
64
+ if (process.env.SPINE_CLI_DEBUG) console.error(err.stack)
65
+ process.exit(1)
66
+ }
67
+ })
68
+ }
69
+ // CIL[END stable_id="~/.framework/cli/commands/upgrade.ts:registerUpgradeCommands:function"]
70
+
71
+ // CIL[BEGIN stable_id="~/.framework/cli/commands/upgrade.ts:runUpgrade:function" type="function" identity="runUpgrade" parent="~/.framework/cli/commands/upgrade.ts"]
72
+ // CIL[DOC-MACRO purpose="Sync installed project's framework-owned files to the package version — integrity check, file sync, package.json merge, netlify.toml render, manifest regen"]
73
+ // CIL[DOC-CONTEXT module="cli" layer="framework" related_files=".spine-manifest.json,.framework/cli/lib/project-files.ts"]
74
+ // CIL[DOC-CONTRACT inputs="abs: string, opts: { dryRun: boolean; force: boolean }" outputs="change report to stdout; synced tree on disk" errors="exit 1 on missing manifest, missing/older package, or locally modified protected files without --force" side_effects="writes framework-owned paths only; --dry-run writes nothing; netlify.toml custom_* sections preserved verbatim"]
75
+ // CIL[DOC-MICRO logic_summary="Three-way compare (disk vs old manifest vs package payload): integrity abort on local mods → copy added/changed files → delete only .framework entries dropped upstream → merge package.json deps/scripts/pin → re-render netlify.toml → regenerate frozen manifest → list new migrations without running them" notable_decisions="non-.framework files dropped upstream are warned and retained; identical rendered TOML writes no .bak"]
76
+ // CIL[DOC-CONFIDENCE pass="first" score="0.85" flags="none"]
77
+ // CIL[DOC-STABILITY stability="experimental"]
78
+ // CIL[DOC-AUDIENCE audience="developer"]
79
+ function diskHash(abs: string): string {
80
+ const lst = lstatSync(abs)
81
+ return hashEntry({ rel: '', abs, symlink: lst.isSymbolicLink() })
82
+ }
83
+
84
+ function runUpgrade(opts: { dryRun: boolean; force: boolean }): void {
85
+ console.log('\nSpine Framework upgrade\n')
86
+
87
+ // 1. Preconditions
88
+ const srcPkgEarly = safeJson(join(PACKAGE_ROOT, 'package.json'))
89
+ if (srcPkgEarly?.name !== 'spine-framework') {
90
+ throw new Error(
91
+ 'This upgrade command is running from the vendored project copy, which cannot update itself.\n' +
92
+ ' Run the installed package instead: npx spine-framework upgrade (or npm run upgrade)',
93
+ )
94
+ }
95
+ if (!existsSync(MANIFEST_PATH)) {
96
+ throw new Error('No .spine-manifest.json — not a Spine-managed project (use `spine-framework init`)')
97
+ }
98
+ const oldManifest = JSON.parse(readFileSync(MANIFEST_PATH, 'utf8'))
99
+ const oldVersion: string | null = oldManifest.framework_version ?? null
100
+ const newVersion = getPackageVersion()
101
+ const prevFrameworkDeps: string[] = Array.isArray(oldManifest.framework_dependencies)
102
+ ? oldManifest.framework_dependencies
103
+ : []
104
+
105
+ if (!oldVersion) {
106
+ console.log(' ⚠ Manifest has no framework_version — proceeding carefully')
107
+ } else if (cmpSemver(newVersion, oldVersion) < 0) {
108
+ throw new Error(`Installed package (v${newVersion}) is older than this project (v${oldVersion}). Install a newer spine-framework first.`)
109
+ } else if (cmpSemver(newVersion, oldVersion) === 0) {
110
+ console.log(` ℹ Project already at spine-framework ${oldVersion}; syncing anyway`)
111
+ } else {
112
+ console.log(` ${oldVersion} → ${newVersion}`)
113
+ }
114
+
115
+ const profile = loadProjectProfile(PACKAGE_ROOT)
116
+ const srcPkg = JSON.parse(readFileSync(join(PACKAGE_ROOT, 'package.json'), 'utf8'))
117
+
118
+ // 2. Integrity — abort on locally modified or missing framework files
119
+ const oldFiles: Record<string, string> = oldManifest.files ?? {}
120
+ const modified: string[] = []
121
+ const missing: string[] = []
122
+ for (const [rel, hash] of Object.entries(oldFiles)) {
123
+ const abs = join(PROJECT_ROOT, rel)
124
+ if (!existsSync(abs)) { missing.push(rel); continue }
125
+ if (diskHash(abs) !== hash) modified.push(rel)
126
+ }
127
+ if ((modified.length > 0 || missing.length > 0) && !opts.force) {
128
+ console.error('\n❌ Framework files were modified locally — upgrade would overwrite them:')
129
+ for (const f of modified) console.error(` modified: ${f}`)
130
+ for (const f of missing) console.error(` missing: ${f}`)
131
+ console.error('\n Revert the changes, or rerun with --force to overwrite.')
132
+ process.exit(1)
133
+ }
134
+ if (opts.force && (modified.length || missing.length)) {
135
+ console.log(` ⚠ --force: overwriting ${modified.length} modified, restoring ${missing.length} missing files`)
136
+ }
137
+
138
+ // 3. Compute the new file set and the change report
139
+ const files = collectFrameworkFiles(PACKAGE_ROOT, profile)
140
+ const newRels = new Set(files.map((f) => f.rel))
141
+ const added: string[] = []
142
+ const changed: string[] = []
143
+ let kept = 0
144
+ for (const f of files) {
145
+ const oldHash = oldFiles[f.rel]
146
+ const newHash = hashEntry(f)
147
+ if (oldHash) {
148
+ if (newHash !== oldHash) changed.push(f.rel)
149
+ else kept++
150
+ } else {
151
+ // Not in the manifest (root config files aren't integrity-tracked).
152
+ // Compare against what's on disk so reruns don't report phantom adds.
153
+ const abs = join(PROJECT_ROOT, f.rel)
154
+ if (!existsSync(abs)) added.push(f.rel)
155
+ else if (diskHash(abs) !== newHash) changed.push(f.rel)
156
+ else kept++
157
+ }
158
+ }
159
+ // Deletions: only inside .framework/ (fully sealed). config/scripts entries
160
+ // dropped upstream are reported, not deleted — extras there are harmless.
161
+ const removed = Object.keys(oldFiles).filter((rel) => !newRels.has(rel) && rel.startsWith('.framework/'))
162
+ const staleOutside = Object.keys(oldFiles).filter((rel) => !newRels.has(rel) && !rel.startsWith('.framework/'))
163
+
164
+ // New framework migrations = files under .framework/migrations not in the old manifest
165
+ const newMigrations = files
166
+ .map((f) => f.rel)
167
+ .filter((rel) => rel.startsWith('.framework/migrations/') && rel.endsWith('.sql') && !(rel in oldFiles))
168
+
169
+ console.log(`\n ${added.length} added · ${changed.length} changed · ${removed.length} removed · ${kept} unchanged`)
170
+ if (opts.dryRun) {
171
+ for (const f of added) console.log(` + ${f}`)
172
+ for (const f of changed) console.log(` ~ ${f}`)
173
+ for (const f of removed) console.log(` - ${f}`)
174
+ for (const f of staleOutside) console.log(` ? ${f} (dropped upstream, left in place)`)
175
+ if (newMigrations.length > 0) {
176
+ console.log(`\n New migrations in this version (${newMigrations.length}):`)
177
+ for (const rel of newMigrations) console.log(` ${rel.split('/').pop()}`)
178
+ }
179
+ console.log('\n (dry run — nothing written)\n')
180
+ return
181
+ }
182
+
183
+ // 4. Apply: copy new files, remove dropped .framework files
184
+ copyCollectedFiles(files, PROJECT_ROOT)
185
+ for (const rel of removed) rmSync(join(PROJECT_ROOT, rel), { force: true })
186
+ for (const rel of staleOutside) console.log(` ⚠ ${rel} is no longer shipped upstream — left in place`)
187
+ console.log(` ✓ framework files synced (${files.length})`)
188
+
189
+ // 5. package.json — framework scripts/deps, exact pin
190
+ const pkgPath = join(PROJECT_ROOT, 'package.json')
191
+ const existingPkg = existsSync(pkgPath) ? JSON.parse(readFileSync(pkgPath, 'utf8')) : {}
192
+ const merged = mergePackageJson({
193
+ existing: existingPkg,
194
+ name: existingPkg.name ?? 'spine-project',
195
+ scripts: profile.package_scripts,
196
+ newDependencies: srcPkg.dependencies ?? {},
197
+ newDevDependencies: srcPkg.devDependencies ?? {},
198
+ prevFrameworkDeps,
199
+ spineVersion: newVersion,
200
+ })
201
+ writeFileSync(pkgPath, merged.text)
202
+ console.log(' ✓ package.json updated (spine-framework pin + framework deps/scripts)')
203
+
204
+ // 6. netlify.toml — framework base replaced, app custom_* sections preserved
205
+ const netlifySrc = join(PACKAGE_ROOT, 'netlify.toml')
206
+ const netlifyDest = join(PROJECT_ROOT, 'netlify.toml')
207
+ if (existsSync(netlifySrc)) {
208
+ const existingToml = existsSync(netlifyDest) ? readFileSync(netlifyDest, 'utf8') : undefined
209
+ const nextToml = renderNetlifyToml(readFileSync(netlifySrc, 'utf8'), existingToml)
210
+ if (existingToml && existingToml !== nextToml) {
211
+ writeFileSync(`${netlifyDest}.bak`, existingToml)
212
+ console.log(' ✓ netlify.toml updated (backup at netlify.toml.bak; custom_* sections preserved)')
213
+ writeFileSync(netlifyDest, nextToml)
214
+ } else {
215
+ writeFileSync(netlifyDest, nextToml)
216
+ console.log(' ✓ netlify.toml written')
217
+ }
218
+ }
219
+
220
+ // 7. Regenerate frozen manifest at the new version
221
+ const manifest = writeFrozenManifest({
222
+ projectRoot: PROJECT_ROOT,
223
+ frameworkVersion: newVersion,
224
+ frameworkDeps: merged.frameworkDeps,
225
+ })
226
+ console.log(` ✓ .spine-manifest.json regenerated (${manifest.fileCount} files)`)
227
+
228
+ if (newMigrations.length > 0) {
229
+ console.log(`\n New migrations in this version (${newMigrations.length}):`)
230
+ for (const rel of newMigrations) console.log(` ${rel.split('/').pop()}`)
231
+ }
232
+
233
+ console.log('\nNext steps:')
234
+ console.log(' 1. npm install # resolve the new dependency set')
235
+ console.log(' 2. npm run verify # confirm frozen integrity')
236
+ console.log(' 3. npx spine-framework migrate # apply new migrations (if any)')
237
+ console.log(' 4. commit + deploy\n')
238
+ }
239
+ // CIL[END stable_id="~/.framework/cli/commands/upgrade.ts:runUpgrade:function"]
240
+
241
+ // CIL[BEGIN stable_id="~/.framework/cli/commands/upgrade.ts:helpers:block" type="block" identity="helpers" parent="~/.framework/cli/commands/upgrade.ts"]
242
+ // CIL[DOC-MACRO purpose="safeJson + getPackageVersion helpers — package version read is tolerant of missing/malformed JSON"]
243
+ // CIL[DOC-CONTEXT module="cli" layer="framework" related_files="package.json"]
244
+ // CIL[DOC-CONTRACT inputs="file: string" outputs="parsed JSON | null; version string" errors="safeJson returns null on missing/malformed input" side_effects="none"]
245
+ // CIL[DOC-MICRO logic_summary="Tolerant reads — a missing or corrupt JSON file degrades to null rather than throwing" notable_decisions="absence is data for upgrade decisions, not an exception"]
246
+ // CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
247
+ // CIL[DOC-STABILITY stability="stable"]
248
+ // CIL[DOC-AUDIENCE audience="developer"]
249
+ function safeJson(file: string): Record<string, any> | null {
250
+ try {
251
+ return JSON.parse(readFileSync(file, 'utf8'))
252
+ } catch {
253
+ return null
254
+ }
255
+ }
256
+
257
+ function getPackageVersion(): string {
258
+ return safeJson(join(PACKAGE_ROOT, 'package.json'))?.version ?? '0.0.0'
259
+ }
260
+ // CIL[END stable_id="~/.framework/cli/commands/upgrade.ts:helpers:block"]