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.
- package/.framework/cli/commands/agents.ts +35 -37
- package/.framework/cli/commands/auth.ts +35 -34
- package/.framework/cli/commands/create-app.ts +104 -40
- package/.framework/cli/commands/dev.ts +104 -69
- package/.framework/cli/commands/doctor.ts +169 -97
- package/.framework/cli/commands/generate.ts +82 -161
- package/.framework/cli/commands/init.ts +72 -34
- package/.framework/cli/commands/install-app.ts +260 -125
- package/.framework/cli/commands/items.ts +33 -44
- package/.framework/cli/commands/migrate.ts +122 -40
- package/.framework/cli/commands/migrations.ts +33 -40
- package/.framework/cli/commands/pipelines.ts +31 -39
- package/.framework/cli/commands/status.ts +47 -23
- package/.framework/cli/commands/system.ts +47 -37
- package/.framework/cli/commands/test.ts +112 -68
- package/.framework/cli/commands/validate-app.ts +215 -121
- package/.framework/cli/context.ts +112 -130
- package/.framework/cli/env-loader.ts +47 -18
- package/.framework/cli/index.ts +81 -42
- package/.framework/docs/runtime-types.md +1024 -0
- package/.framework/functions/_shared/agent-runner.ts +1296 -961
- package/.framework/functions/_shared/app-manifest.ts +82 -68
- package/.framework/functions/_shared/audit.ts +135 -83
- package/.framework/functions/_shared/{cil-state.ts → cil.ts} +147 -54
- package/.framework/functions/_shared/db.ts +80 -126
- package/.framework/functions/_shared/hash-api-key.ts +63 -0
- package/.framework/functions/_shared/index.ts +222 -347
- package/.framework/functions/_shared/kb.ts +319 -0
- package/.framework/functions/_shared/llm.ts +92 -0
- package/.framework/functions/_shared/logging.ts +288 -0
- package/.framework/functions/_shared/middleware.ts +329 -365
- package/.framework/functions/_shared/outcomes.ts +36 -0
- package/.framework/functions/_shared/permissions.ts +359 -643
- package/.framework/functions/_shared/pipeline-runner.ts +243 -316
- package/.framework/functions/_shared/principal.ts +376 -468
- package/.framework/functions/_shared/resolve-credentials.ts +102 -0
- package/.framework/functions/_shared/resolve-ids.ts +162 -14
- package/.framework/functions/_shared/retry-deliveries.ts +104 -0
- package/.framework/functions/_shared/schema-utils.ts +611 -373
- package/.framework/functions/_shared/testing.ts +207 -137
- package/.framework/functions/_shared/tracing.ts +276 -0
- package/.framework/functions/_shared/trigger-engine.ts +159 -211
- package/.framework/functions/_shared/webhook-registration.ts +95 -81
- package/.framework/functions/_shared/webhook-registry.ts +153 -75
- package/.framework/functions/account-nodes.ts +31 -53
- package/.framework/functions/adapters/generic-webhook-inbound.ts +56 -0
- package/.framework/functions/adapters/generic-webhook-outbound.ts +43 -0
- package/.framework/functions/adapters/openai-outbound.ts +40 -0
- package/.framework/functions/admin-data.ts +687 -533
- package/.framework/functions/admin-funnel.ts +31 -20
- package/.framework/functions/admin-install-funnel.ts +47 -28
- package/.framework/functions/admin-signals.ts +47 -29
- package/.framework/functions/ai-agents.ts +122 -147
- package/.framework/functions/ai-interactions.ts +180 -90
- package/.framework/functions/api-keys.ts +250 -192
- package/.framework/functions/apps.ts +203 -197
- package/.framework/functions/auth.ts +98 -91
- package/.framework/functions/cil-agent-ingest.ts +773 -0
- package/.framework/functions/cil-agent.ts +592 -0
- package/.framework/functions/cil-callmap.ts +1227 -0
- package/.framework/functions/cil-config.ts +583 -0
- package/.framework/functions/cil-context.ts +445 -0
- package/.framework/functions/cil-doc.ts +1752 -0
- package/.framework/functions/cil-embeddings.ts +1202 -0
- package/.framework/functions/cil-graph.ts +585 -0
- package/.framework/functions/cil-ingest.ts +2416 -0
- package/.framework/functions/cil-pipeline.ts +318 -0
- package/.framework/functions/cil-project.ts +499 -0
- package/.framework/functions/cil-validate.ts +469 -0
- package/.framework/functions/debug-auth.ts +73 -53
- package/.framework/functions/deliveries.ts +117 -0
- package/.framework/functions/embeddings.ts +528 -516
- package/.framework/functions/eval-harness.ts +158 -106
- package/.framework/functions/integration-outbound.ts +311 -0
- package/.framework/functions/integration-routes.ts +240 -466
- package/.framework/functions/integrations.ts +132 -140
- package/.framework/functions/invites.ts +401 -201
- package/.framework/functions/item-progress.ts +113 -53
- package/.framework/functions/kb.ts +28 -0
- package/.framework/functions/logs.ts +239 -222
- package/.framework/functions/observability.ts +308 -259
- package/.framework/functions/pipeline-executions.ts +179 -235
- package/.framework/functions/pipelines.ts +163 -205
- package/.framework/functions/prompt-configs.ts +109 -156
- package/.framework/functions/roles.ts +97 -147
- package/.framework/functions/system-cron.ts +132 -218
- package/.framework/functions/system.ts +112 -52
- package/.framework/functions/tests.ts +32 -28
- package/.framework/functions/timers.ts +162 -210
- package/.framework/functions/triggers.ts +282 -346
- package/.framework/functions/types.ts +283 -400
- package/.framework/migrations/001_core_schema.sql +630 -0
- package/.framework/migrations/002_core_rls_and_functions.sql +532 -0
- package/.framework/migrations/{003_system_data.sql → 003_core_system_data.sql} +26 -153
- package/.framework/migrations/004_core_rpc_and_broker.sql +421 -0
- package/.framework/migrations/005_core_observability.sql +598 -0
- package/.framework/migrations/006_security_hardening_rls.sql +97 -0
- package/.framework/migrations/007_kb_article_list.sql +237 -0
- package/.framework/migrations/008_items_target.sql +8 -0
- package/.framework/migrations/009_roles_slug_unique.sql +11 -0
- package/.framework/public/spine.config.json +91 -0
- package/.framework/scripts/app-install-cli.ts +112 -98
- package/.framework/src/App.tsx +67 -20
- package/.framework/src/apps/admin/index.tsx +97 -27
- package/.framework/src/components/AppWrapper.tsx +50 -11
- package/.framework/src/components/CustomAppLoader.tsx +131 -22
- package/.framework/src/components/admin/AdminListPage.tsx +75 -8
- package/.framework/src/components/admin/AdminSidebar.tsx +148 -36
- package/.framework/src/components/admin/AdminStatsCard.tsx +48 -29
- package/.framework/src/components/admin/PageInstructions.tsx +49 -0
- package/.framework/src/components/admin/SortableTableHeader.tsx +49 -2
- package/.framework/src/components/app-shell/GenericAppShell.tsx +69 -11
- package/.framework/src/components/app-shell/GenericDetailPage.tsx +55 -10
- package/.framework/src/components/app-shell/GenericListPage.tsx +76 -9
- package/.framework/src/components/app-sidebar.tsx +50 -3
- package/.framework/src/components/auth/ProtectedRoute.tsx +53 -45
- package/.framework/src/components/kb/KBArticleEditor.tsx +409 -0
- package/.framework/src/components/kb/KBArticleList.tsx +557 -0
- package/.framework/src/components/layout/AppShell.tsx +54 -16
- package/.framework/src/components/layout/Header.tsx +50 -38
- package/.framework/src/components/layout/Layout.tsx +48 -32
- package/.framework/src/components/layout/Sidebar.tsx +167 -16
- package/.framework/src/components/runtime/DataDetailHeader.tsx +33 -1
- package/.framework/src/components/runtime/DataDetailPage.tsx +289 -4
- package/.framework/src/components/runtime/DataFilters.tsx +78 -1
- package/.framework/src/components/runtime/DataHeader.tsx +32 -29
- package/.framework/src/components/runtime/DataListPage.tsx +161 -2
- package/.framework/src/components/runtime/DataStats.tsx +48 -1
- package/.framework/src/components/runtime/DataTable.tsx +153 -1
- package/.framework/src/components/runtime/SchemaDetailForm.tsx +77 -46
- package/.framework/src/components/runtime/index.ts +16 -9
- package/.framework/src/components/search-form.tsx +33 -1
- package/.framework/src/components/shared/AgentView.tsx +48 -51
- package/.framework/src/components/shared/FieldRenderer.tsx +84 -67
- package/.framework/src/components/shared/InviteManager.tsx +113 -52
- package/.framework/src/components/shared/MemberList.tsx +182 -0
- package/.framework/src/components/shared/SchemaFields.tsx +96 -78
- package/.framework/src/components/ui/DataTable.tsx +86 -56
- package/.framework/src/components/ui/Form.tsx +188 -109
- package/.framework/src/components/ui/ItemCard.tsx +86 -58
- package/.framework/src/components/ui/ItemListView.tsx +88 -61
- package/.framework/src/components/ui/LoadingSpinner.tsx +52 -27
- package/.framework/src/components/ui/Modal.tsx +81 -4
- package/.framework/src/components/ui/RichTextEditor.tsx +101 -9
- package/.framework/src/components/ui/accordion.tsx +97 -5
- package/.framework/src/components/ui/alert-dialog.tsx +225 -13
- package/.framework/src/components/ui/alert.tsx +113 -6
- package/.framework/src/components/ui/aspect-ratio.tsx +49 -2
- package/.framework/src/components/ui/avatar.tsx +129 -7
- package/.framework/src/components/ui/badge.tsx +65 -3
- package/.framework/src/components/ui/breadcrumb.tsx +145 -8
- package/.framework/src/components/ui/button-group.tsx +81 -4
- package/.framework/src/components/ui/button.tsx +49 -2
- package/.framework/src/components/ui/calendar.tsx +65 -3
- package/.framework/src/components/ui/card.tsx +145 -8
- package/.framework/src/components/ui/carousel.tsx +129 -7
- package/.framework/src/components/ui/chart.tsx +161 -10
- package/.framework/src/components/ui/checkbox.tsx +49 -2
- package/.framework/src/components/ui/collapsible.tsx +81 -5
- package/.framework/src/components/ui/combobox.tsx +305 -20
- package/.framework/src/components/ui/command.tsx +177 -11
- package/.framework/src/components/ui/context-menu.tsx +273 -17
- package/.framework/src/components/ui/dialog.tsx +209 -27
- package/.framework/src/components/ui/direction.tsx +64 -4
- package/.framework/src/components/ui/drawer.tsx +193 -12
- package/.framework/src/components/ui/dropdown-menu.tsx +273 -18
- package/.framework/src/components/ui/empty.tsx +145 -9
- package/.framework/src/components/ui/field.tsx +209 -14
- package/.framework/src/components/ui/hover-card.tsx +81 -4
- package/.framework/src/components/ui/input-group.tsx +162 -10
- package/.framework/src/components/ui/input-otp.tsx +98 -5
- package/.framework/src/components/ui/input.tsx +49 -2
- package/.framework/src/components/ui/item.tsx +225 -13
- package/.framework/src/components/ui/kbd.tsx +65 -3
- package/.framework/src/components/ui/label.tsx +49 -2
- package/.framework/src/components/ui/menubar.tsx +289 -17
- package/.framework/src/components/ui/native-select.tsx +97 -5
- package/.framework/src/components/ui/navigation-menu.tsx +177 -10
- package/.framework/src/components/ui/pagination.tsx +161 -9
- package/.framework/src/components/ui/popover.tsx +145 -8
- package/.framework/src/components/ui/progress.tsx +49 -2
- package/.framework/src/components/ui/radio-group.tsx +65 -3
- package/.framework/src/components/ui/resizable.tsx +82 -4
- package/.framework/src/components/ui/scroll-area.tsx +65 -3
- package/.framework/src/components/ui/select.tsx +193 -11
- package/.framework/src/components/ui/separator.tsx +49 -2
- package/.framework/src/components/ui/sheet.tsx +193 -11
- package/.framework/src/components/ui/sidebar.tsx +481 -38
- package/.framework/src/components/ui/skeleton.tsx +49 -2
- package/.framework/src/components/ui/slider.tsx +49 -2
- package/.framework/src/components/ui/sonner.tsx +49 -2
- package/.framework/src/components/ui/spinner.tsx +49 -2
- package/.framework/src/components/ui/switch.tsx +49 -2
- package/.framework/src/components/ui/table-primitives.tsx +160 -33
- package/.framework/src/components/ui/table.tsx +161 -9
- package/.framework/src/components/ui/tabs.tsx +113 -6
- package/.framework/src/components/ui/textarea.tsx +49 -2
- package/.framework/src/components/ui/toggle-group.tsx +81 -4
- package/.framework/src/components/ui/toggle.tsx +65 -3
- package/.framework/src/components/ui/tooltip.tsx +97 -5
- package/.framework/src/contexts/AppContext.tsx +129 -45
- package/.framework/src/contexts/AuthContext.tsx +173 -145
- package/.framework/src/hooks/use-mobile.ts +48 -2
- package/.framework/src/hooks/useApi.ts +208 -159
- package/.framework/src/hooks/useAppPath.ts +32 -23
- package/.framework/src/hooks/useApps.ts +51 -20
- package/.framework/src/hooks/useEntityList.ts +32 -78
- package/.framework/src/hooks/useEntityRecord.ts +35 -96
- package/.framework/src/hooks/useForm.ts +32 -92
- package/.framework/src/hooks/useListSchema.ts +49 -78
- package/.framework/src/hooks/useSchemaRecord.ts +114 -105
- package/.framework/src/lib/api.ts +192 -115
- package/.framework/src/lib/supabase.ts +98 -64
- package/.framework/src/lib/utils.ts +287 -151
- package/.framework/src/main.tsx +49 -3
- package/.framework/src/pages/DashboardPage.tsx +52 -29
- package/.framework/src/pages/NotFoundPage.tsx +32 -10
- package/.framework/src/pages/SetupPage.tsx +32 -12
- package/.framework/src/pages/admin/AIAgentDetailPage.tsx +119 -10
- package/.framework/src/pages/admin/AIAgentsPage.tsx +48 -19
- package/.framework/src/pages/admin/APIKeyDetailPage.tsx +187 -17
- package/.framework/src/pages/admin/APIKeysPage.tsx +98 -22
- package/.framework/src/pages/admin/AlertsConfigPage.tsx +113 -26
- package/.framework/src/pages/admin/AppDetailPage.tsx +125 -8
- package/.framework/src/pages/admin/AppsPage.tsx +49 -2
- package/.framework/src/pages/admin/DeliveriesPage.tsx +196 -0
- package/.framework/src/pages/admin/DesignedPage.tsx +32 -18
- package/.framework/src/pages/admin/EmbeddingDetailPage.tsx +215 -484
- package/.framework/src/pages/admin/EmbeddingsPage.tsx +116 -319
- package/.framework/src/pages/admin/ExtendedShadcnTestPage.tsx +34 -9
- package/.framework/src/pages/admin/IncrementalShadcnTestPage.tsx +34 -9
- package/.framework/src/pages/admin/IntegratedDashboard.tsx +32 -16
- package/.framework/src/pages/admin/IntegrationDetailPage.tsx +183 -26
- package/.framework/src/pages/admin/IntegrationsPage.tsx +48 -19
- package/.framework/src/pages/admin/InvitesPage.tsx +48 -14
- package/.framework/src/pages/admin/LogsPage.tsx +422 -75
- package/.framework/src/pages/admin/MinimalShadcnTestPage.tsx +34 -9
- package/.framework/src/pages/admin/ObservabilityDashboard.tsx +113 -24
- package/.framework/src/pages/admin/PipelineDetailPage.tsx +206 -34
- package/.framework/src/pages/admin/PipelineExecutionsPage.tsx +50 -17
- package/.framework/src/pages/admin/PipelinesPage.tsx +160 -21
- package/.framework/src/pages/admin/PromptConfigDetailPage.tsx +378 -88
- package/.framework/src/pages/admin/PromptConfigsPage.tsx +125 -16
- package/.framework/src/pages/admin/ProperlyDesignedPage.tsx +49 -10
- package/.framework/src/pages/admin/RoleDetailPage.tsx +130 -10
- package/.framework/src/pages/admin/RolesPage.tsx +171 -18
- package/.framework/src/pages/admin/SelectTestPage.tsx +34 -9
- package/.framework/src/pages/admin/ShadcnTestPage.tsx +49 -11
- package/.framework/src/pages/admin/SimpleDashboard.tsx +49 -10
- package/.framework/src/pages/admin/TestRunDetailPage.tsx +49 -4
- package/.framework/src/pages/admin/TestingDashboard.tsx +98 -28
- package/.framework/src/pages/admin/TimerDetailPage.tsx +143 -33
- package/.framework/src/pages/admin/TimersPage.tsx +48 -20
- package/.framework/src/pages/admin/TriggerDetailPage.tsx +118 -30
- package/.framework/src/pages/admin/TriggersPage.tsx +64 -19
- package/.framework/src/pages/admin/TypeDetailPage.tsx +138 -15
- package/.framework/src/pages/admin/TypesPage.tsx +48 -20
- package/.framework/src/pages/admin/cil/CILAgentPage.tsx +465 -0
- package/.framework/src/pages/admin/cil/CILChunkDetailPage.tsx +787 -0
- package/.framework/src/pages/admin/cil/CILDashboardPage.tsx +699 -0
- package/.framework/src/pages/admin/cil/CILFileListPage.tsx +647 -0
- package/.framework/src/pages/admin/cil/CILGraphPage.tsx +616 -0
- package/.framework/src/pages/admin/cil/CILIngestionPage.tsx +1402 -0
- package/.framework/src/pages/admin/cil/CILProjectsPage.tsx +614 -0
- package/.framework/src/pages/admin/cil/CILSearchPage.tsx +571 -0
- package/.framework/src/pages/admin/cil/FlexiblePipelinePage.tsx +731 -0
- package/.framework/src/pages/admin/kb/KBAdminPage.tsx +64 -0
- package/.framework/src/pages/auth/AuthConfirmPage.tsx +52 -51
- package/.framework/src/pages/auth/ForgotPasswordPage.tsx +31 -22
- package/.framework/src/pages/auth/LoginPage.tsx +50 -23
- package/.framework/src/pages/auth/PendingApprovalPage.tsx +31 -15
- package/.framework/src/pages/auth/RegisterPage.tsx +281 -317
- package/.framework/src/pages/spine-framework/APIPage.tsx +33 -0
- package/.framework/src/pages/spine-framework/CLIPage.tsx +33 -0
- package/.framework/src/types/auth.ts +128 -71
- package/.framework/src/types/types.ts +373 -157
- package/custom/.devin/skills/spine-workbench-discovery/SKILL.md +39 -0
- package/custom/.devin/skills/spine-workbench-idea/SKILL.md +36 -0
- package/custom/.devin/skills/spine-workbench-implement/SKILL.md +46 -0
- package/custom/.devin/skills/spine-workbench-initiative/SKILL.md +28 -0
- package/custom/.devin/skills/spine-workbench-plan/SKILL.md +38 -0
- package/docs/README.md +9 -130
- package/netlify.toml +3 -3
- package/package.json +13 -25
- package/scripts/kb-ingest-docs.ts +203 -0
- package/spine.config.json +65 -4
- package/.framework/migrations/001_schema.sql +0 -425
- package/.framework/migrations/002_rls_and_functions.sql +0 -763
- package/.framework/migrations/004_enhancements.sql +0 -43
- package/.framework/migrations/005_auth_user_trigger.sql +0 -105
- package/.framework/migrations/006_fix_current_actor_id.sql +0 -21
- package/.framework/migrations/016_invites.sql +0 -27
- package/.framework/migrations/017_fix_get_account_apps.sql +0 -78
- package/.framework/migrations/018_apps_required_roles.sql +0 -17
- package/.framework/migrations/019_triggers_unique_constraint.sql +0 -13
- package/.framework/migrations/020_get_account_apps_role_filter.sql +0 -117
- package/.framework/migrations/021_app_domains.sql +0 -112
- package/.framework/migrations/022_route_prefix_domain_unique.sql +0 -21
- package/.framework/migrations/023_match_embeddings_rpc.sql +0 -78
- package/.framework/migrations/024_messages_account_id.sql +0 -9
- package/.framework/migrations/025_retype_threads_messages.sql +0 -240
- package/.framework/migrations/026_fix_rls_policies.sql +0 -47
- package/.framework/migrations/027_embedding_lifecycle.sql +0 -176
- package/.framework/migrations/028_fix_message_data_permissions.sql +0 -49
- package/.framework/migrations/029_tighten_support_triage_prompt.sql +0 -41
- package/.framework/migrations/030_messages_updated_by.sql +0 -6
- package/.framework/migrations/031_seed_upsert_constraints.sql +0 -35
- package/docs/APPS.md +0 -232
- package/docs/ARCHITECTURE.md +0 -246
- package/docs/INSTALL.md +0 -155
- package/docs/design-schema-spec.md +0 -1837
- package/docs/dev-guides/01-create-an-app.md +0 -183
- package/docs/dev-guides/02-create-a-function.md +0 -178
- package/docs/dev-guides/03-create-a-component.md +0 -178
- package/docs/dev-guides/04-webhook-handlers.md +0 -129
- package/docs/dev-guides/05-testing.md +0 -168
- package/docs/dev-guides/README.md +0 -40
- package/docs/npm-packages-plan.md +0 -288
- 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
|
-
#
|
|
1
|
+
# Documentation
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
This directory contains the documentation for the Spine framework.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Current documentation
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
The canonical, up-to-date documentation lives in [`spine-framework-documentation/`](./spine-framework-documentation/README.md).
|
|
8
8
|
|
|
9
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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."
|
|
30
|
+
[functions."cil-ingest"]
|
|
31
31
|
timeout = 300
|
|
32
32
|
|
|
33
|
-
[functions."
|
|
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.
|
|
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/
|
|
20
|
-
".framework/migrations/
|
|
21
|
-
".framework/migrations/
|
|
22
|
-
".framework/migrations/
|
|
23
|
-
".framework/migrations/
|
|
24
|
-
".framework/migrations/
|
|
25
|
-
".framework/migrations/
|
|
26
|
-
".framework/migrations/
|
|
27
|
-
".framework/migrations/
|
|
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
|
|
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
|
+
})
|