spine-framework 0.3.125 → 0.3.127

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 (319) hide show
  1. package/.framework/cli/commands/agents.ts +35 -37
  2. package/.framework/cli/commands/auth.ts +35 -34
  3. package/.framework/cli/commands/create-app.ts +104 -40
  4. package/.framework/cli/commands/dev.ts +104 -69
  5. package/.framework/cli/commands/doctor.ts +169 -97
  6. package/.framework/cli/commands/generate.ts +82 -161
  7. package/.framework/cli/commands/init.ts +72 -34
  8. package/.framework/cli/commands/install-app.ts +260 -125
  9. package/.framework/cli/commands/items.ts +33 -44
  10. package/.framework/cli/commands/migrate.ts +122 -40
  11. package/.framework/cli/commands/migrations.ts +33 -40
  12. package/.framework/cli/commands/pipelines.ts +31 -39
  13. package/.framework/cli/commands/status.ts +47 -23
  14. package/.framework/cli/commands/system.ts +47 -37
  15. package/.framework/cli/commands/test.ts +112 -68
  16. package/.framework/cli/commands/validate-app.ts +215 -121
  17. package/.framework/cli/context.ts +112 -130
  18. package/.framework/cli/env-loader.ts +47 -18
  19. package/.framework/cli/index.ts +81 -42
  20. package/.framework/docs/runtime-types.md +1024 -0
  21. package/.framework/functions/_shared/agent-runner.ts +1296 -961
  22. package/.framework/functions/_shared/app-manifest.ts +82 -68
  23. package/.framework/functions/_shared/audit.ts +135 -83
  24. package/.framework/functions/_shared/{cil-state.ts → cil.ts} +147 -54
  25. package/.framework/functions/_shared/db.ts +80 -126
  26. package/.framework/functions/_shared/hash-api-key.ts +63 -0
  27. package/.framework/functions/_shared/index.ts +222 -347
  28. package/.framework/functions/_shared/kb.ts +319 -0
  29. package/.framework/functions/_shared/llm.ts +92 -0
  30. package/.framework/functions/_shared/logging.ts +288 -0
  31. package/.framework/functions/_shared/middleware.ts +329 -365
  32. package/.framework/functions/_shared/outcomes.ts +36 -0
  33. package/.framework/functions/_shared/permissions.ts +359 -643
  34. package/.framework/functions/_shared/pipeline-runner.ts +243 -316
  35. package/.framework/functions/_shared/principal.ts +376 -468
  36. package/.framework/functions/_shared/resolve-credentials.ts +102 -0
  37. package/.framework/functions/_shared/resolve-ids.ts +162 -14
  38. package/.framework/functions/_shared/retry-deliveries.ts +104 -0
  39. package/.framework/functions/_shared/schema-utils.ts +611 -373
  40. package/.framework/functions/_shared/testing.ts +207 -137
  41. package/.framework/functions/_shared/tracing.ts +276 -0
  42. package/.framework/functions/_shared/trigger-engine.ts +159 -211
  43. package/.framework/functions/_shared/webhook-registration.ts +95 -81
  44. package/.framework/functions/_shared/webhook-registry.ts +153 -75
  45. package/.framework/functions/account-nodes.ts +31 -53
  46. package/.framework/functions/adapters/generic-webhook-inbound.ts +56 -0
  47. package/.framework/functions/adapters/generic-webhook-outbound.ts +43 -0
  48. package/.framework/functions/adapters/openai-outbound.ts +40 -0
  49. package/.framework/functions/admin-data.ts +687 -533
  50. package/.framework/functions/admin-funnel.ts +31 -20
  51. package/.framework/functions/admin-install-funnel.ts +47 -28
  52. package/.framework/functions/admin-signals.ts +47 -29
  53. package/.framework/functions/ai-agents.ts +122 -147
  54. package/.framework/functions/ai-interactions.ts +180 -90
  55. package/.framework/functions/api-keys.ts +250 -192
  56. package/.framework/functions/apps.ts +203 -197
  57. package/.framework/functions/auth.ts +98 -91
  58. package/.framework/functions/cil-agent-ingest.ts +773 -0
  59. package/.framework/functions/cil-agent.ts +592 -0
  60. package/.framework/functions/cil-callmap.ts +1227 -0
  61. package/.framework/functions/cil-config.ts +583 -0
  62. package/.framework/functions/cil-context.ts +445 -0
  63. package/.framework/functions/cil-doc.ts +1752 -0
  64. package/.framework/functions/cil-embeddings.ts +1202 -0
  65. package/.framework/functions/cil-graph.ts +585 -0
  66. package/.framework/functions/cil-ingest.ts +2416 -0
  67. package/.framework/functions/cil-pipeline.ts +318 -0
  68. package/.framework/functions/cil-project.ts +499 -0
  69. package/.framework/functions/cil-validate.ts +469 -0
  70. package/.framework/functions/debug-auth.ts +73 -53
  71. package/.framework/functions/deliveries.ts +117 -0
  72. package/.framework/functions/embeddings.ts +528 -516
  73. package/.framework/functions/eval-harness.ts +158 -106
  74. package/.framework/functions/integration-outbound.ts +311 -0
  75. package/.framework/functions/integration-routes.ts +240 -466
  76. package/.framework/functions/integrations.ts +132 -140
  77. package/.framework/functions/invites.ts +401 -201
  78. package/.framework/functions/item-progress.ts +113 -53
  79. package/.framework/functions/kb.ts +28 -0
  80. package/.framework/functions/logs.ts +239 -222
  81. package/.framework/functions/observability.ts +308 -259
  82. package/.framework/functions/pipeline-executions.ts +179 -235
  83. package/.framework/functions/pipelines.ts +163 -205
  84. package/.framework/functions/prompt-configs.ts +109 -156
  85. package/.framework/functions/roles.ts +97 -147
  86. package/.framework/functions/system-cron.ts +132 -218
  87. package/.framework/functions/system.ts +112 -52
  88. package/.framework/functions/tests.ts +32 -28
  89. package/.framework/functions/timers.ts +162 -210
  90. package/.framework/functions/triggers.ts +282 -346
  91. package/.framework/functions/types.ts +283 -400
  92. package/.framework/migrations/001_core_schema.sql +630 -0
  93. package/.framework/migrations/002_core_rls_and_functions.sql +532 -0
  94. package/.framework/migrations/{003_system_data.sql → 003_core_system_data.sql} +26 -153
  95. package/.framework/migrations/004_core_rpc_and_broker.sql +421 -0
  96. package/.framework/migrations/005_core_observability.sql +598 -0
  97. package/.framework/migrations/006_security_hardening_rls.sql +97 -0
  98. package/.framework/migrations/007_kb_article_list.sql +237 -0
  99. package/.framework/migrations/008_items_target.sql +8 -0
  100. package/.framework/migrations/009_roles_slug_unique.sql +11 -0
  101. package/.framework/public/spine.config.json +91 -0
  102. package/.framework/scripts/app-install-cli.ts +112 -98
  103. package/.framework/src/App.tsx +67 -20
  104. package/.framework/src/apps/admin/index.tsx +97 -27
  105. package/.framework/src/components/AppWrapper.tsx +50 -11
  106. package/.framework/src/components/CustomAppLoader.tsx +131 -22
  107. package/.framework/src/components/admin/AdminListPage.tsx +75 -8
  108. package/.framework/src/components/admin/AdminSidebar.tsx +148 -36
  109. package/.framework/src/components/admin/AdminStatsCard.tsx +48 -29
  110. package/.framework/src/components/admin/PageInstructions.tsx +49 -0
  111. package/.framework/src/components/admin/SortableTableHeader.tsx +49 -2
  112. package/.framework/src/components/app-shell/GenericAppShell.tsx +69 -11
  113. package/.framework/src/components/app-shell/GenericDetailPage.tsx +55 -10
  114. package/.framework/src/components/app-shell/GenericListPage.tsx +76 -9
  115. package/.framework/src/components/app-sidebar.tsx +50 -3
  116. package/.framework/src/components/auth/ProtectedRoute.tsx +53 -45
  117. package/.framework/src/components/kb/KBArticleEditor.tsx +409 -0
  118. package/.framework/src/components/kb/KBArticleList.tsx +557 -0
  119. package/.framework/src/components/layout/AppShell.tsx +54 -16
  120. package/.framework/src/components/layout/Header.tsx +50 -38
  121. package/.framework/src/components/layout/Layout.tsx +48 -32
  122. package/.framework/src/components/layout/Sidebar.tsx +167 -16
  123. package/.framework/src/components/runtime/DataDetailHeader.tsx +33 -1
  124. package/.framework/src/components/runtime/DataDetailPage.tsx +289 -4
  125. package/.framework/src/components/runtime/DataFilters.tsx +78 -1
  126. package/.framework/src/components/runtime/DataHeader.tsx +32 -29
  127. package/.framework/src/components/runtime/DataListPage.tsx +161 -2
  128. package/.framework/src/components/runtime/DataStats.tsx +48 -1
  129. package/.framework/src/components/runtime/DataTable.tsx +153 -1
  130. package/.framework/src/components/runtime/SchemaDetailForm.tsx +77 -46
  131. package/.framework/src/components/runtime/index.ts +16 -9
  132. package/.framework/src/components/search-form.tsx +33 -1
  133. package/.framework/src/components/shared/AgentView.tsx +48 -51
  134. package/.framework/src/components/shared/FieldRenderer.tsx +84 -67
  135. package/.framework/src/components/shared/InviteManager.tsx +113 -52
  136. package/.framework/src/components/shared/MemberList.tsx +182 -0
  137. package/.framework/src/components/shared/SchemaFields.tsx +96 -78
  138. package/.framework/src/components/ui/DataTable.tsx +86 -56
  139. package/.framework/src/components/ui/Form.tsx +188 -109
  140. package/.framework/src/components/ui/ItemCard.tsx +86 -58
  141. package/.framework/src/components/ui/ItemListView.tsx +88 -61
  142. package/.framework/src/components/ui/LoadingSpinner.tsx +52 -27
  143. package/.framework/src/components/ui/Modal.tsx +81 -4
  144. package/.framework/src/components/ui/RichTextEditor.tsx +101 -9
  145. package/.framework/src/components/ui/accordion.tsx +97 -5
  146. package/.framework/src/components/ui/alert-dialog.tsx +225 -13
  147. package/.framework/src/components/ui/alert.tsx +113 -6
  148. package/.framework/src/components/ui/aspect-ratio.tsx +49 -2
  149. package/.framework/src/components/ui/avatar.tsx +129 -7
  150. package/.framework/src/components/ui/badge.tsx +65 -3
  151. package/.framework/src/components/ui/breadcrumb.tsx +145 -8
  152. package/.framework/src/components/ui/button-group.tsx +81 -4
  153. package/.framework/src/components/ui/button.tsx +49 -2
  154. package/.framework/src/components/ui/calendar.tsx +65 -3
  155. package/.framework/src/components/ui/card.tsx +145 -8
  156. package/.framework/src/components/ui/carousel.tsx +129 -7
  157. package/.framework/src/components/ui/chart.tsx +161 -10
  158. package/.framework/src/components/ui/checkbox.tsx +49 -2
  159. package/.framework/src/components/ui/collapsible.tsx +81 -5
  160. package/.framework/src/components/ui/combobox.tsx +305 -20
  161. package/.framework/src/components/ui/command.tsx +177 -11
  162. package/.framework/src/components/ui/context-menu.tsx +273 -17
  163. package/.framework/src/components/ui/dialog.tsx +209 -27
  164. package/.framework/src/components/ui/direction.tsx +64 -4
  165. package/.framework/src/components/ui/drawer.tsx +193 -12
  166. package/.framework/src/components/ui/dropdown-menu.tsx +273 -18
  167. package/.framework/src/components/ui/empty.tsx +145 -9
  168. package/.framework/src/components/ui/field.tsx +209 -14
  169. package/.framework/src/components/ui/hover-card.tsx +81 -4
  170. package/.framework/src/components/ui/input-group.tsx +162 -10
  171. package/.framework/src/components/ui/input-otp.tsx +98 -5
  172. package/.framework/src/components/ui/input.tsx +49 -2
  173. package/.framework/src/components/ui/item.tsx +225 -13
  174. package/.framework/src/components/ui/kbd.tsx +65 -3
  175. package/.framework/src/components/ui/label.tsx +49 -2
  176. package/.framework/src/components/ui/menubar.tsx +289 -17
  177. package/.framework/src/components/ui/native-select.tsx +97 -5
  178. package/.framework/src/components/ui/navigation-menu.tsx +177 -10
  179. package/.framework/src/components/ui/pagination.tsx +161 -9
  180. package/.framework/src/components/ui/popover.tsx +145 -8
  181. package/.framework/src/components/ui/progress.tsx +49 -2
  182. package/.framework/src/components/ui/radio-group.tsx +65 -3
  183. package/.framework/src/components/ui/resizable.tsx +82 -4
  184. package/.framework/src/components/ui/scroll-area.tsx +65 -3
  185. package/.framework/src/components/ui/select.tsx +193 -11
  186. package/.framework/src/components/ui/separator.tsx +49 -2
  187. package/.framework/src/components/ui/sheet.tsx +193 -11
  188. package/.framework/src/components/ui/sidebar.tsx +481 -38
  189. package/.framework/src/components/ui/skeleton.tsx +49 -2
  190. package/.framework/src/components/ui/slider.tsx +49 -2
  191. package/.framework/src/components/ui/sonner.tsx +49 -2
  192. package/.framework/src/components/ui/spinner.tsx +49 -2
  193. package/.framework/src/components/ui/switch.tsx +49 -2
  194. package/.framework/src/components/ui/table-primitives.tsx +160 -33
  195. package/.framework/src/components/ui/table.tsx +161 -9
  196. package/.framework/src/components/ui/tabs.tsx +113 -6
  197. package/.framework/src/components/ui/textarea.tsx +49 -2
  198. package/.framework/src/components/ui/toggle-group.tsx +81 -4
  199. package/.framework/src/components/ui/toggle.tsx +65 -3
  200. package/.framework/src/components/ui/tooltip.tsx +97 -5
  201. package/.framework/src/contexts/AppContext.tsx +129 -45
  202. package/.framework/src/contexts/AuthContext.tsx +173 -145
  203. package/.framework/src/hooks/use-mobile.ts +48 -2
  204. package/.framework/src/hooks/useApi.ts +208 -159
  205. package/.framework/src/hooks/useAppPath.ts +32 -23
  206. package/.framework/src/hooks/useApps.ts +51 -20
  207. package/.framework/src/hooks/useEntityList.ts +32 -78
  208. package/.framework/src/hooks/useEntityRecord.ts +35 -96
  209. package/.framework/src/hooks/useForm.ts +32 -92
  210. package/.framework/src/hooks/useListSchema.ts +49 -78
  211. package/.framework/src/hooks/useSchemaRecord.ts +114 -105
  212. package/.framework/src/lib/api.ts +192 -115
  213. package/.framework/src/lib/supabase.ts +98 -64
  214. package/.framework/src/lib/utils.ts +287 -151
  215. package/.framework/src/main.tsx +49 -3
  216. package/.framework/src/pages/DashboardPage.tsx +52 -29
  217. package/.framework/src/pages/NotFoundPage.tsx +32 -10
  218. package/.framework/src/pages/SetupPage.tsx +32 -12
  219. package/.framework/src/pages/admin/AIAgentDetailPage.tsx +119 -10
  220. package/.framework/src/pages/admin/AIAgentsPage.tsx +48 -19
  221. package/.framework/src/pages/admin/APIKeyDetailPage.tsx +187 -17
  222. package/.framework/src/pages/admin/APIKeysPage.tsx +98 -22
  223. package/.framework/src/pages/admin/AlertsConfigPage.tsx +113 -26
  224. package/.framework/src/pages/admin/AppDetailPage.tsx +125 -8
  225. package/.framework/src/pages/admin/AppsPage.tsx +49 -2
  226. package/.framework/src/pages/admin/DeliveriesPage.tsx +196 -0
  227. package/.framework/src/pages/admin/DesignedPage.tsx +32 -18
  228. package/.framework/src/pages/admin/EmbeddingDetailPage.tsx +215 -484
  229. package/.framework/src/pages/admin/EmbeddingsPage.tsx +116 -319
  230. package/.framework/src/pages/admin/ExtendedShadcnTestPage.tsx +34 -9
  231. package/.framework/src/pages/admin/IncrementalShadcnTestPage.tsx +34 -9
  232. package/.framework/src/pages/admin/IntegratedDashboard.tsx +32 -16
  233. package/.framework/src/pages/admin/IntegrationDetailPage.tsx +183 -26
  234. package/.framework/src/pages/admin/IntegrationsPage.tsx +48 -19
  235. package/.framework/src/pages/admin/InvitesPage.tsx +48 -14
  236. package/.framework/src/pages/admin/LogsPage.tsx +422 -75
  237. package/.framework/src/pages/admin/MinimalShadcnTestPage.tsx +34 -9
  238. package/.framework/src/pages/admin/ObservabilityDashboard.tsx +113 -24
  239. package/.framework/src/pages/admin/PipelineDetailPage.tsx +206 -34
  240. package/.framework/src/pages/admin/PipelineExecutionsPage.tsx +50 -17
  241. package/.framework/src/pages/admin/PipelinesPage.tsx +160 -21
  242. package/.framework/src/pages/admin/PromptConfigDetailPage.tsx +378 -88
  243. package/.framework/src/pages/admin/PromptConfigsPage.tsx +125 -16
  244. package/.framework/src/pages/admin/ProperlyDesignedPage.tsx +49 -10
  245. package/.framework/src/pages/admin/RoleDetailPage.tsx +130 -10
  246. package/.framework/src/pages/admin/RolesPage.tsx +171 -18
  247. package/.framework/src/pages/admin/SelectTestPage.tsx +34 -9
  248. package/.framework/src/pages/admin/ShadcnTestPage.tsx +49 -11
  249. package/.framework/src/pages/admin/SimpleDashboard.tsx +49 -10
  250. package/.framework/src/pages/admin/TestRunDetailPage.tsx +49 -4
  251. package/.framework/src/pages/admin/TestingDashboard.tsx +98 -28
  252. package/.framework/src/pages/admin/TimerDetailPage.tsx +143 -33
  253. package/.framework/src/pages/admin/TimersPage.tsx +48 -20
  254. package/.framework/src/pages/admin/TriggerDetailPage.tsx +118 -30
  255. package/.framework/src/pages/admin/TriggersPage.tsx +64 -19
  256. package/.framework/src/pages/admin/TypeDetailPage.tsx +138 -15
  257. package/.framework/src/pages/admin/TypesPage.tsx +48 -20
  258. package/.framework/src/pages/admin/cil/CILAgentPage.tsx +465 -0
  259. package/.framework/src/pages/admin/cil/CILChunkDetailPage.tsx +787 -0
  260. package/.framework/src/pages/admin/cil/CILDashboardPage.tsx +699 -0
  261. package/.framework/src/pages/admin/cil/CILFileListPage.tsx +647 -0
  262. package/.framework/src/pages/admin/cil/CILGraphPage.tsx +616 -0
  263. package/.framework/src/pages/admin/cil/CILIngestionPage.tsx +1402 -0
  264. package/.framework/src/pages/admin/cil/CILProjectsPage.tsx +614 -0
  265. package/.framework/src/pages/admin/cil/CILSearchPage.tsx +571 -0
  266. package/.framework/src/pages/admin/cil/FlexiblePipelinePage.tsx +731 -0
  267. package/.framework/src/pages/admin/kb/KBAdminPage.tsx +64 -0
  268. package/.framework/src/pages/auth/AuthConfirmPage.tsx +52 -51
  269. package/.framework/src/pages/auth/ForgotPasswordPage.tsx +31 -22
  270. package/.framework/src/pages/auth/LoginPage.tsx +50 -23
  271. package/.framework/src/pages/auth/PendingApprovalPage.tsx +31 -15
  272. package/.framework/src/pages/auth/RegisterPage.tsx +281 -317
  273. package/.framework/src/pages/spine-framework/APIPage.tsx +33 -0
  274. package/.framework/src/pages/spine-framework/CLIPage.tsx +33 -0
  275. package/.framework/src/types/auth.ts +128 -71
  276. package/.framework/src/types/types.ts +373 -157
  277. package/custom/.devin/skills/spine-workbench-discovery/SKILL.md +39 -0
  278. package/custom/.devin/skills/spine-workbench-idea/SKILL.md +36 -0
  279. package/custom/.devin/skills/spine-workbench-implement/SKILL.md +46 -0
  280. package/custom/.devin/skills/spine-workbench-initiative/SKILL.md +28 -0
  281. package/custom/.devin/skills/spine-workbench-plan/SKILL.md +38 -0
  282. package/docs/README.md +9 -130
  283. package/netlify.toml +3 -3
  284. package/package.json +13 -25
  285. package/scripts/kb-ingest-docs.ts +203 -0
  286. package/spine.config.json +65 -4
  287. package/.framework/migrations/001_schema.sql +0 -425
  288. package/.framework/migrations/002_rls_and_functions.sql +0 -763
  289. package/.framework/migrations/004_enhancements.sql +0 -43
  290. package/.framework/migrations/005_auth_user_trigger.sql +0 -105
  291. package/.framework/migrations/006_fix_current_actor_id.sql +0 -21
  292. package/.framework/migrations/016_invites.sql +0 -27
  293. package/.framework/migrations/017_fix_get_account_apps.sql +0 -78
  294. package/.framework/migrations/018_apps_required_roles.sql +0 -17
  295. package/.framework/migrations/019_triggers_unique_constraint.sql +0 -13
  296. package/.framework/migrations/020_get_account_apps_role_filter.sql +0 -117
  297. package/.framework/migrations/021_app_domains.sql +0 -112
  298. package/.framework/migrations/022_route_prefix_domain_unique.sql +0 -21
  299. package/.framework/migrations/023_match_embeddings_rpc.sql +0 -78
  300. package/.framework/migrations/024_messages_account_id.sql +0 -9
  301. package/.framework/migrations/025_retype_threads_messages.sql +0 -240
  302. package/.framework/migrations/026_fix_rls_policies.sql +0 -47
  303. package/.framework/migrations/027_embedding_lifecycle.sql +0 -176
  304. package/.framework/migrations/028_fix_message_data_permissions.sql +0 -49
  305. package/.framework/migrations/029_tighten_support_triage_prompt.sql +0 -41
  306. package/.framework/migrations/030_messages_updated_by.sql +0 -6
  307. package/.framework/migrations/031_seed_upsert_constraints.sql +0 -35
  308. package/docs/APPS.md +0 -232
  309. package/docs/ARCHITECTURE.md +0 -246
  310. package/docs/INSTALL.md +0 -155
  311. package/docs/design-schema-spec.md +0 -1837
  312. package/docs/dev-guides/01-create-an-app.md +0 -183
  313. package/docs/dev-guides/02-create-a-function.md +0 -178
  314. package/docs/dev-guides/03-create-a-component.md +0 -178
  315. package/docs/dev-guides/04-webhook-handlers.md +0 -129
  316. package/docs/dev-guides/05-testing.md +0 -168
  317. package/docs/dev-guides/README.md +0 -40
  318. package/docs/npm-packages-plan.md +0 -288
  319. package/docs/spine-core--api-reference.md +0 -822
@@ -0,0 +1,39 @@
1
+ ---
2
+ name: spine-workbench-discovery
3
+ description: "Promote an idea to discovery, run the Workbench Research Agent, and optionally link relevant code chunks."
4
+ allowed-tools:
5
+ - mcp_call_tool
6
+ - read
7
+ - ask_user_question
8
+ ---
9
+
10
+ # Spine Workbench — Discovery
11
+
12
+ Promote an existing Workbench idea to a `workbench_discovery` and gather related code or knowledge.
13
+
14
+ ## When to invoke
15
+
16
+ - The user wants to start discovery on an idea.
17
+ - They want to investigate the problem, find related code, and decide whether to approve.
18
+
19
+ ## Steps
20
+
21
+ 1. **Identify the idea**
22
+ - Ask for the `idea_id` if not provided.
23
+ - If the user gave a title instead of an ID, search with `spine_cil_search_chunks` or list relevant work items to resolve it.
24
+
25
+ 2. **Start discovery**
26
+ - Call `spine_workbench_start_discovery` with the `idea_id` and `account_id`.
27
+ - Capture the returned `discovery.id`.
28
+
29
+ 3. **Run the Research Agent (optional)**
30
+ - Call `spine_workbench_run_research_agent` with the `idea_id` or new `discovery_id`.
31
+ - Summarize the retrieved sources and recommendations.
32
+
33
+ 4. **Link relevant code (optional)**
34
+ - Use `spine_cil_search_chunks` to find CIL chunks related to the discovery topic.
35
+ - For the strongest matches, call `spine_workbench_link_code` with `link_type: workbench_concerns_chunk`.
36
+
37
+ 5. **Return the result**
38
+ - Show the discovery `id`, `title`, `status`, and any linked code.
39
+ - Suggest next step: `spine-workbench-initiative` to approve, or `spine-workbench-plan` if it is small enough to skip the initiative stage.
@@ -0,0 +1,36 @@
1
+ ---
2
+ name: spine-workbench-idea
3
+ description: "Capture a new idea in the Workbench and, if helpful, gather relevant context from CIL before tracking it."
4
+ allowed-tools:
5
+ - mcp_call_tool
6
+ - read
7
+ - ask_user_question
8
+ ---
9
+
10
+ # Spine Workbench — Idea
11
+
12
+ Capture a new idea as a `workbench_work_item` with `work_type: idea`.
13
+
14
+ ## When to invoke
15
+
16
+ - The user says "I have an idea", "track this", "capture a feature request", or similar.
17
+ - They describe something that should go through the Workbench lifecycle (idea → discovery → initiative → plan → implement).
18
+
19
+ ## Steps
20
+
21
+ 1. **Confirm the title and description**
22
+ - If the user only mentioned a vague concept, ask for a 1-sentence title and a short description before side effects.
23
+ - If the input is clear, proceed directly.
24
+
25
+ 2. **Optionally gather context**
26
+ - If the idea relates to an existing file, chunk, or feature, call `spine_cil_search_chunks` to find relevant CIL context.
27
+ - Summarize the top 1-3 most relevant chunks in your reasoning.
28
+
29
+ 3. **Track the idea**
30
+ - Call `spine_workbench_track_idea` with:
31
+ - `account_id` — ask the user if not known.
32
+ - `title` and `description` from the input.
33
+
34
+ 4. **Return the result**
35
+ - Show the new idea's `id`, `title`, and `status`.
36
+ - Suggest next step: `spine-workbench-discovery` or `spine-workbench-plan`.
@@ -0,0 +1,46 @@
1
+ ---
2
+ name: spine-workbench-implement
3
+ description: "Implement a Workbench work item: read context, edit code, verify with CIL, and post progress."
4
+ allowed-tools:
5
+ - mcp_call_tool
6
+ - read
7
+ - edit
8
+ - write
9
+ - exec
10
+ - ask_user_question
11
+ ---
12
+
13
+ # Spine Workbench — Implement
14
+
15
+ Implement the code for a Workbench work item and keep CIL in sync.
16
+
17
+ ## When to invoke
18
+
19
+ - The user says "implement this", "build it", "fix it", or is ready to code a planned work item.
20
+
21
+ ## Steps
22
+
23
+ 1. **Identify the work item**
24
+ - Ask for `work_item_id` if not provided.
25
+
26
+ 2. **Load context**
27
+ - Use `spine_cil_get_context` or `spine_cil_find_context` on the most relevant chunk(s) linked to the work item.
28
+ - If no chunks are linked, use `spine_cil_search_chunks` first.
29
+
30
+ 3. **Implement the change**
31
+ - Edit or create the files needed.
32
+ - Follow the Spine Way: core-vs-custom boundary, design schema, permissions, and admin-data for runtime records.
33
+
34
+ 4. **Re-index changed files in CIL**
35
+ - After saving, call `spine_cil_reindex_chunk` with `also_embed: true` for each changed chunk.
36
+ - Use `spine_cil_validate_file` to confirm no drift remains.
37
+
38
+ 5. **Run verification**
39
+ - Run the relevant build, lint, typecheck, or test commands for the changed code.
40
+ - If verification fails, fix the issues before finishing.
41
+
42
+ 6. **Post progress**
43
+ - Call `spine_workbench_add_thread_message` with an implementation summary.
44
+
45
+ 7. **Return the result**
46
+ - Summarize the files changed, verification status, and next step (e.g., open a PR, run end-to-end tests).
@@ -0,0 +1,28 @@
1
+ ---
2
+ name: spine-workbench-initiative
3
+ description: "Approve a discovery and promote it to a workbench_initiative."
4
+ allowed-tools:
5
+ - mcp_call_tool
6
+ - read
7
+ - ask_user_question
8
+ ---
9
+
10
+ # Spine Workbench — Initiative
11
+
12
+ Approve an existing `workbench_discovery` and create a linked `workbench_initiative`.
13
+
14
+ ## When to invoke
15
+
16
+ - The user says "approve discovery", "promote to initiative", or wants to move from discovery to roadmap.
17
+
18
+ ## Steps
19
+
20
+ 1. **Identify the discovery**
21
+ - Ask for the `discovery_id` if not provided.
22
+
23
+ 2. **Approve the discovery**
24
+ - Call `spine_workbench_approve_discovery` with the `discovery_id` and `account_id`.
25
+
26
+ 3. **Return the result**
27
+ - Show the new initiative `id`, `title`, and `status`.
28
+ - Suggest next step: `spine-workbench-plan`.
@@ -0,0 +1,38 @@
1
+ ---
2
+ name: spine-workbench-plan
3
+ description: "Create documentation and test-plan tickets for a work item and link the code chunks it concerns."
4
+ allowed-tools:
5
+ - mcp_call_tool
6
+ - read
7
+ - ask_user_question
8
+ ---
9
+
10
+ # Spine Workbench — Plan
11
+
12
+ Create planning artifacts for a work item: docs ticket, test plan, and code links.
13
+
14
+ ## When to invoke
15
+
16
+ - The user wants to "write a test plan", "create docs", "link code", or generally prepare a work item for implementation.
17
+ - The work item is an idea, discovery, or initiative.
18
+
19
+ ## Steps
20
+
21
+ 1. **Identify the work item**
22
+ - Ask for `work_item_id` if not provided.
23
+ - If the user mentions the initiative/discovery/idea by title, ask for the UUID or use search.
24
+
25
+ 2. **Create the planning artifacts**
26
+ - Call `spine_workbench_create_docs_ticket` to create a `workbench_docs_ticket`.
27
+ - Call `spine_workbench_create_test_plan` to create a `workbench_test_plan`.
28
+
29
+ 3. **Link relevant code**
30
+ - Use `spine_cil_search_chunks` to find the most relevant chunks/files for this work item.
31
+ - For each strongly relevant chunk, call `spine_workbench_link_code` with `work_item_id`, `chunk_id`, and `link_type: workbench_concerns_chunk`.
32
+
33
+ 4. **Log progress**
34
+ - Call `spine_workbench_add_thread_message` with a short progress note.
35
+
36
+ 5. **Return the result**
37
+ - Show the created docs ticket, test plan, and any code links.
38
+ - Suggest next step: `spine-workbench-implement`.
package/docs/README.md CHANGED
@@ -1,136 +1,15 @@
1
- # Spine - Internal Dev Repo
1
+ # Documentation
2
2
 
3
- **A multi-tenant, event-driven operational OS for modern SaaS systems.**
3
+ This directory contains the documentation for the Spine framework.
4
4
 
5
- Spine is not a CRM, a helpdesk, or a workflow tool. It is the operational nervous system underneath applications — the central coordination layer that handles identity, state machines, audit trails, event emission, automation, webhooks, semantic intelligence, and theming so that vertical products don't have to rebuild these primitives.
5
+ ## Current documentation
6
6
 
7
- > See [docs/MANIFESTO.md](docs/MANIFESTO.md) for the full design philosophy.
7
+ The canonical, up-to-date documentation lives in [`spine-framework-documentation/`](./spine-framework-documentation/README.md).
8
8
 
9
- ## What Spine Knows
9
+ - [`spine-framework-documentation/start-here.md`](./spine-framework-documentation/start-here.md) — find the right doc for your task.
10
+ - [`spine-framework-documentation/quickstart.md`](./spine-framework-documentation/quickstart.md) — get a Spine project running locally.
11
+ - [`spine-framework-documentation/concepts/spine-way.md`](./spine-framework-documentation/concepts/spine-way.md) — the ten fundamentals every extension must satisfy.
10
12
 
11
- | Primitive | Not Domain-Specific |
12
- |---|---|
13
- | Accounts | Not "companies" or "clients" |
14
- | Persons | Not "employees" or "candidates" |
15
- | Workflow Items | Not "deals" or "jobs" |
16
- | Events | Not "notifications" |
17
- | Automation Rules | Not "zapier zaps" |
18
- | Embeddings | Not "documents" |
13
+ ## Archived documentation
19
14
 
20
- Spine provides the operational backbone. Your vertical product provides the meaning.
21
-
22
- ## Architecture
23
-
24
- - **Frontend**: React 18 + Vite + TailwindCSS + shadcn/ui
25
- - **API**: Netlify Functions (TypeScript)
26
- - **Database**: Supabase Postgres + pgvector
27
- - **Auth**: Supabase Auth (JWT) — browser handles login/session; all data access via API
28
-
29
- ### Core Principles
30
-
31
- - **Event-driven**: Every state change produces an outbox event. Every event can trigger automations and webhooks.
32
- - **Multi-tenant**: Every row scoped by `account_id`. Every request resolves person, account, and role context.
33
- - **Automation-native**: Automation rules are first-class primitives with condition evaluation and action execution.
34
- - **Audit-first**: Every write emits `audit_log` (before/after diff) and `activity_event` records. Nothing mutates silently.
35
- - **AI-aware**: Multi-vector embeddings (pgvector) with auto-indexing on write and semantic search.
36
- - **API-first**: Frontend never touches the database. All reads/writes go through Netlify Functions.
37
- - **RBAC**: Two-tier — `account_role` (admin/operator/member) + `system_role` (system_admin/system_operator/support_operator).
38
-
39
- ## Modules
40
-
41
- | Module | Description |
42
- |---|---|
43
- | **Identity & Tenancy** | Accounts, persons, profiles, memberships, invites, RBAC, auto-provisioning |
44
- | **Workflow Engine** | Definitions, stage builder, items, kanban, stage transitions, automation rules |
45
- | **Support** | Tickets, threaded messages, knowledge base articles |
46
- | **Activity & Audit** | Append-only activity feed + immutable audit log |
47
- | **Integrations** | Outbox events, webhook subscriptions, HMAC-signed deliveries with retry/backoff/dead-letter |
48
- | **Automation** | Event-triggered rules with conditions, actions (transition, emit, webhook, update) |
49
- | **Intelligence** | Multi-vector embeddings, auto-embed on write, semantic search UI |
50
- | **Theming** | DB-stored tokens, 3 presets (Clean/Bold/Muted), live Theme Editor |
51
-
52
- ## Setup
53
-
54
- > ### TODO
55
- > - [ ] Enable **Leaked password protection** in Supabase: Dashboard → **Auth → Settings → Password security**.
56
-
57
- ### Prerequisites
58
-
59
- - Node.js 18+
60
- - Netlify CLI (`npm i -g netlify-cli`)
61
- - Supabase project (with pgvector enabled)
62
-
63
- ### Environment Variables
64
-
65
- Copy `.env.example` to `.env` and fill in:
66
-
67
- ```
68
- VITE_SUPABASE_URL=https://your-project.supabase.co
69
- VITE_SUPABASE_ANON_KEY=your-anon-key
70
- SUPABASE_URL=https://your-project.supabase.co
71
- SUPABASE_SERVICE_ROLE_KEY=your-service-role-key
72
- ```
73
-
74
- ### Database Setup
75
-
76
- Apply migrations in order (001–016) via Supabase SQL editor or CLI:
77
-
78
- ```bash
79
- # Each file in supabase/migrations/ should be applied sequentially
80
- ```
81
-
82
- ### Local Development
83
-
84
- ```bash
85
- npm install
86
- netlify dev
87
- ```
88
-
89
- This starts:
90
- - Vite dev server on port 5173
91
- - Netlify Functions proxy on port 8888
92
- - App at `http://localhost:8888`
93
-
94
- New users are auto-provisioned on first login — no manual database setup required.
95
-
96
- ### Build
97
-
98
- ```bash
99
- npm run build
100
- ```
101
-
102
- ## Project Structure
103
-
104
- ```
105
- ├── core/ # Canonical Spine runtime (DO NOT EDIT)
106
- │ ├── functions/ # 59 endpoint handlers
107
- │ │ ├── _shared/ # Shared utilities (middleware, db, audit, etc.)
108
- │ │ └── *.ts # Core API endpoints
109
- │ └── src/ # Core React frontend
110
- │ ├── components/ # UI components (ui/, layout/, shared/, etc.)
111
- │ ├── pages/ # Route pages (admin, portal, public)
112
- │ ├── lib/ # Utilities (api, auth, config, theme, etc.)
113
- │ └── hooks/ # React hooks (useAuth, useCustomFields, etc.)
114
- ├── custom/ # Your extensions (MODIFY FREELY)
115
- │ ├── functions/ # Custom serverless functions (override/extend core)
116
- │ └── src/ # Custom frontend code
117
- │ └── manifest/routes.ts # Custom route + nav registrations
118
- ├── src/ # Assembled frontend output (auto-generated, .gitignored)
119
- ├── netlify/functions/ # Assembled API output (auto-generated, .gitignored)
120
- ├── supabase/
121
- │ └── migrations/ # 33 SQL migrations
122
- ├── scripts/
123
- │ ├── assemble-functions.sh # Merge core/functions + custom/functions
124
- │ ├── assemble-frontend.sh # Merge core/src + custom/src → src/
125
- │ ├── build-manifest.sh # Generate integrity hashes
126
- │ └── verify-integrity.sh # Verify core integrity
127
- ├── docs/
128
- │ ├── MANIFESTO.md # Design philosophy
129
- │ ├── FRONTEND.md # Frontend architecture
130
- │ ├── EXTENDING.md # Extension guide
131
- │ └── API.md # API reference
132
- ├── netlify.toml
133
- └── package.json
134
- ```
135
-
136
- # Testing GIT flow
15
+ Older documentation has been moved to [`archive/2026-07-28/`](./archive/2026-07-28/) and is kept for reference only. If a conflict exists between the new tree and an archived document, the new tree wins.
package/netlify.toml CHANGED
@@ -11,7 +11,7 @@
11
11
  # Spine functions configuration - assembled from .framework + custom
12
12
  [functions]
13
13
  directory = ".assembled/netlify/functions"
14
- included_files = [".assembled/netlify/functions/**"]
14
+ included_files = [".assembled/netlify/functions/_shared/**"]
15
15
  node_bundler = "esbuild"
16
16
 
17
17
  [dev]
@@ -27,10 +27,10 @@
27
27
 
28
28
  # CIL ingestion passes are long-running — extend timeout for local dev
29
29
  # TODO: migrate to background functions for production
30
- [functions."custom_cil-ingest"]
30
+ [functions."cil-ingest"]
31
31
  timeout = 300
32
32
 
33
- [functions."custom_cil-embeddings"]
33
+ [functions."cil-embeddings"]
34
34
  timeout = 300
35
35
 
36
36
  [[redirects]]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spine-framework",
3
- "version": "0.3.125",
3
+ "version": "0.3.127",
4
4
  "description": "Multi-tenant, modular application platform for modern SaaS systems",
5
5
  "type": "module",
6
6
  "bin": {
@@ -16,28 +16,15 @@
16
16
  ".framework/cli/",
17
17
  ".framework/docs/",
18
18
  ".framework/functions/",
19
- ".framework/migrations/001_schema.sql",
20
- ".framework/migrations/002_rls_and_functions.sql",
21
- ".framework/migrations/003_system_data.sql",
22
- ".framework/migrations/004_enhancements.sql",
23
- ".framework/migrations/005_auth_user_trigger.sql",
24
- ".framework/migrations/006_fix_current_actor_id.sql",
25
- ".framework/migrations/016_invites.sql",
26
- ".framework/migrations/017_fix_get_account_apps.sql",
27
- ".framework/migrations/018_apps_required_roles.sql",
28
- ".framework/migrations/019_triggers_unique_constraint.sql",
29
- ".framework/migrations/020_get_account_apps_role_filter.sql",
30
- ".framework/migrations/021_app_domains.sql",
31
- ".framework/migrations/022_route_prefix_domain_unique.sql",
32
- ".framework/migrations/023_match_embeddings_rpc.sql",
33
- ".framework/migrations/024_messages_account_id.sql",
34
- ".framework/migrations/025_retype_threads_messages.sql",
35
- ".framework/migrations/026_fix_rls_policies.sql",
36
- ".framework/migrations/027_embedding_lifecycle.sql",
37
- ".framework/migrations/028_fix_message_data_permissions.sql",
38
- ".framework/migrations/029_tighten_support_triage_prompt.sql",
39
- ".framework/migrations/030_messages_updated_by.sql",
40
- ".framework/migrations/031_seed_upsert_constraints.sql",
19
+ ".framework/migrations/001_core_schema.sql",
20
+ ".framework/migrations/002_core_rls_and_functions.sql",
21
+ ".framework/migrations/003_core_system_data.sql",
22
+ ".framework/migrations/004_core_rpc_and_broker.sql",
23
+ ".framework/migrations/005_core_observability.sql",
24
+ ".framework/migrations/006_security_hardening_rls.sql",
25
+ ".framework/migrations/007_kb_article_list.sql",
26
+ ".framework/migrations/008_items_target.sql",
27
+ ".framework/migrations/009_roles_slug_unique.sql",
41
28
  ".framework/public/",
42
29
  ".framework/scripts/",
43
30
  ".framework/seeds/",
@@ -95,7 +82,8 @@
95
82
  "test:integration": "vitest run .framework/tests/integration",
96
83
  "test:boundary": "bash scripts/boundary-check.sh",
97
84
  "docs": "typedoc --entryPoints .framework/functions/_shared/index.ts --out docs/generated",
98
- "mcp": "tsx custom/apps/cortex/mcp/index.ts"
85
+ "mcp": "tsx --env-file=.env .framework/mcp/index.ts",
86
+ "mcp:spine-framework": "tsx --env-file=.env .framework/mcp/index.ts"
99
87
  },
100
88
  "dependencies": {
101
89
  "@base-ui/react": "^1.4.1",
@@ -137,6 +125,7 @@
137
125
  "cmdk": "^1.1.1",
138
126
  "dagre": "^0.8.5",
139
127
  "date-fns": "^4.1.0",
128
+ "dompurify": "^3.4.13",
140
129
  "embla-carousel-react": "^8.6.0",
141
130
  "framer-motion": "^12.34.3",
142
131
  "github-markdown-css": "^5.9.0",
@@ -167,7 +156,6 @@
167
156
  "remark-gfm": "^4.0.1",
168
157
  "shadcn": "^4.7.0",
169
158
  "sonner": "^2.0.7",
170
- "spine-framework-cortex": "^0.2.12",
171
159
  "tailwind-merge": "^3.5.0",
172
160
  "tailwindcss-animate": "^1.0.7",
173
161
  "tiptap-markdown": "^0.9.0",
@@ -0,0 +1,203 @@
1
+ import fs from 'fs/promises'
2
+ import path from 'path'
3
+ import yaml from 'js-yaml'
4
+ import { create as adminCreate, update as adminUpdate } from '../.framework/functions/admin-data.ts'
5
+ import { kbIngest } from '../.framework/functions/_shared/kb.ts'
6
+ import { adminDb } from '../.framework/functions/_shared/db.ts'
7
+
8
+ const DOCS_ROOT = 'docs/spine-framework-documentation'
9
+ const MANIFEST_PATH = path.join(DOCS_ROOT, 'manifest.json')
10
+ const TARGET_TYPE_SLUG = 'kb-spine-internal'
11
+ const ACCOUNT_SLUG = 'spine-system'
12
+ const CREATED_BY_PERSON_ID = '69fbb941-344a-446b-85ad-50a73cab9051'
13
+
14
+ const VALID_TAGS = new Set(['support', 'how-to', 'faq', 'tutorial'])
15
+ const VALID_CATEGORIES = new Set(['how-to', 'runbook', 'policy', 'faq', 'tutorial'])
16
+
17
+ const LAYER_TO_CATEGORY: Record<string, string> = {
18
+ overview: 'how-to',
19
+ concept: 'how-to',
20
+ reference: 'runbook',
21
+ guide: 'tutorial',
22
+ quickstart: 'tutorial',
23
+ 'task-guide': 'tutorial',
24
+ operations: 'runbook',
25
+ }
26
+
27
+ function normaliseTags(input: string[]): string[] {
28
+ return input
29
+ .map(t => {
30
+ const lower = String(t).toLowerCase()
31
+ if (lower.includes('faq')) return 'faq'
32
+ if (lower.includes('how-to') || lower.includes('howto') || lower.includes('guide')) return 'how-to'
33
+ if (lower.includes('tutorial')) return 'tutorial'
34
+ if (lower.includes('support')) return 'support'
35
+ return lower
36
+ })
37
+ .filter(t => VALID_TAGS.has(t))
38
+ }
39
+
40
+ function parseArrayValue(value: string): string[] {
41
+ const inside = value.replace(/^\[/, '').replace(/\]$/, '').trim()
42
+ if (!inside) return []
43
+ return inside
44
+ .split(/,\s*/)
45
+ .map(s => s.trim().replace(/^["']|["']$/g, ''))
46
+ .filter(Boolean)
47
+ }
48
+
49
+ function parseFrontmatter(raw: string): any {
50
+ const frontmatter: any = {}
51
+ const lines = raw.split('\n')
52
+ for (const line of lines) {
53
+ const idx = line.indexOf(':')
54
+ if (idx === -1) continue
55
+ const key = line.slice(0, idx).trim()
56
+ const value = line.slice(idx + 1).trim()
57
+ if (value.startsWith('[') && value.endsWith(']')) {
58
+ frontmatter[key] = parseArrayValue(value)
59
+ } else if (value === 'true' || value === 'false') {
60
+ frontmatter[key] = value === 'true'
61
+ } else {
62
+ frontmatter[key] = value.replace(/^["']|["']$/g, '')
63
+ }
64
+ }
65
+ return frontmatter
66
+ }
67
+
68
+ function parseMarkdown(filePath: string, content: string): { frontmatter: any; body: string } {
69
+ const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/)
70
+ if (!match) {
71
+ return { frontmatter: {}, body: content.trim() }
72
+ }
73
+ const frontmatter = parseFrontmatter(match[1])
74
+ const body = match[2].trim()
75
+ return { frontmatter, body }
76
+ }
77
+
78
+ async function buildCtx(accountId: string) {
79
+ return {
80
+ requestId: `kb-ingest-${Date.now()}`,
81
+ accountId,
82
+ appId: null,
83
+ principal: {
84
+ id: CREATED_BY_PERSON_ID,
85
+ type: 'machine' as const,
86
+ roles: [{ slug: 'system_admin', is_system: true }],
87
+ },
88
+ db: adminDb,
89
+ query: {},
90
+ requestPath: '/admin-data',
91
+ } as any
92
+ }
93
+
94
+ async function main() {
95
+ const { data: account } = await adminDb
96
+ .from('accounts')
97
+ .select('id')
98
+ .eq('slug', ACCOUNT_SLUG)
99
+ .single()
100
+
101
+ if (!account) throw new Error(`Account not found: ${ACCOUNT_SLUG}`)
102
+ const accountId = account.id
103
+
104
+ const { data: typeRecord } = await adminDb
105
+ .from('types')
106
+ .select('id')
107
+ .eq('slug', TARGET_TYPE_SLUG)
108
+ .eq('kind', 'item')
109
+ .single()
110
+
111
+ if (!typeRecord) throw new Error(`Type not found: ${TARGET_TYPE_SLUG}`)
112
+ const typeId = typeRecord.id
113
+
114
+ const manifest = JSON.parse(await fs.readFile(MANIFEST_PATH, 'utf8'))
115
+ const pages: Array<{ path: string }> = []
116
+
117
+ function collectPages(obj: any) {
118
+ if (Array.isArray(obj)) {
119
+ for (const item of obj) collectPages(item)
120
+ } else if (typeof obj === 'object' && obj) {
121
+ if (obj.path) pages.push({ path: obj.path })
122
+ for (const key of Object.keys(obj)) collectPages(obj[key])
123
+ }
124
+ }
125
+ collectPages(manifest)
126
+
127
+ const stats = { created: 0, updated: 0, failed: 0, skipped: 0, errors: [] as string[] }
128
+
129
+ for (const page of pages) {
130
+ if (!page.path.endsWith('.md')) continue
131
+ const filePath = path.join(DOCS_ROOT, page.path)
132
+ const sourceFile = filePath.replace(/^docs\//, '')
133
+
134
+ try {
135
+ const raw = await fs.readFile(filePath, 'utf8')
136
+ const { frontmatter, body } = parseMarkdown(filePath, raw)
137
+
138
+ const title = frontmatter.title || path.basename(page.path, '.md')
139
+ const description = frontmatter.description || ''
140
+ const tags = normaliseTags(frontmatter.tags || [])
141
+ const layer = frontmatter.layer || 'overview'
142
+ const category = [LAYER_TO_CATEGORY[layer] || 'how-to'].filter(c => VALID_CATEGORIES.has(c))
143
+ const product = 'spine-framework'
144
+
145
+ const existing = await adminDb
146
+ .from('items')
147
+ .select('id')
148
+ .eq('type_id', typeId)
149
+ .eq('data->>source_file', sourceFile)
150
+ .eq('is_active', true)
151
+ .maybeSingle()
152
+
153
+ const ctx = await buildCtx(accountId)
154
+ const recordData: any = {
155
+ entity: 'items',
156
+ title,
157
+ description,
158
+ status: 'published',
159
+ data: {
160
+ content: body,
161
+ tags,
162
+ category,
163
+ product,
164
+ source_file: sourceFile,
165
+ version: frontmatter.version || '',
166
+ is_ai_generated: false,
167
+ },
168
+ }
169
+
170
+ let record: any
171
+ if (existing?.data?.id) {
172
+ recordData.id = existing.data.id
173
+ record = await adminUpdate(
174
+ { ...ctx, query: { entity: 'items', id: existing.data.id, method: 'PATCH' } },
175
+ recordData
176
+ )
177
+ stats.updated++
178
+ } else {
179
+ recordData.type_slug = TARGET_TYPE_SLUG
180
+ record = await adminCreate(
181
+ { ...ctx, query: { entity: 'items', method: 'POST' } },
182
+ recordData
183
+ )
184
+ stats.created++
185
+ }
186
+
187
+ await kbIngest(ctx, { id: record.id })
188
+ console.log(`[${existing?.data?.id ? 'updated' : 'created'}] ${sourceFile} -> ${record.id}`)
189
+ } catch (err: any) {
190
+ stats.failed++
191
+ const msg = `${sourceFile}: ${err?.message || err}`
192
+ stats.errors.push(msg)
193
+ console.error(`[failed] ${msg}`)
194
+ }
195
+ }
196
+
197
+ console.log(JSON.stringify({ stats }, null, 2))
198
+ }
199
+
200
+ main().catch(e => {
201
+ console.error(e)
202
+ process.exit(1)
203
+ })