spine-framework 0.4.4 → 0.5.0-rc.4

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