spine-framework 0.4.3 → 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 (320) 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/agent-runner.ts +1000 -81
  15. package/.framework/functions/_shared/cil.ts +2 -1
  16. package/.framework/functions/_shared/db.ts +3 -6
  17. package/.framework/functions/_shared/hash-api-key.ts +183 -79
  18. package/.framework/functions/_shared/kb.ts +61 -7
  19. package/.framework/functions/_shared/logging.ts +562 -224
  20. package/.framework/functions/_shared/message-sequence.ts +53 -0
  21. package/.framework/functions/_shared/middleware.ts +47 -44
  22. package/.framework/functions/_shared/pipeline-runner.ts +150 -37
  23. package/.framework/functions/_shared/principal.ts +9 -6
  24. package/.framework/functions/_shared/trigger-engine.ts +149 -40
  25. package/.framework/functions/admin-data.ts +48 -31
  26. package/.framework/functions/ai-agents.ts +103 -33
  27. package/.framework/functions/api-keys.ts +301 -122
  28. package/.framework/functions/apps.ts +43 -26
  29. package/.framework/functions/cil-ingest.ts +48 -15
  30. package/.framework/functions/debug-auth.ts +14 -8
  31. package/.framework/functions/deliveries.ts +39 -12
  32. package/.framework/functions/embeddings.ts +145 -76
  33. package/.framework/functions/integration-outbound.ts +67 -27
  34. package/.framework/functions/integrations.ts +254 -127
  35. package/.framework/functions/logs.ts +273 -133
  36. package/.framework/functions/observability.ts +277 -147
  37. package/.framework/functions/pipeline-executions.ts +1 -1
  38. package/.framework/functions/pipeline-runner-background.ts +120 -0
  39. package/.framework/functions/pipelines.ts +14 -9
  40. package/.framework/functions/prompt-configs.ts +7 -4
  41. package/.framework/functions/roles.ts +8 -5
  42. package/.framework/functions/security.ts +30 -17
  43. package/.framework/functions/system-cron.ts +250 -128
  44. package/.framework/functions/tests.ts +7 -6
  45. package/.framework/functions/timers.ts +310 -107
  46. package/.framework/functions/trigger-scheduler.ts +65 -5
  47. package/.framework/functions/triggers.ts +28 -13
  48. package/.framework/functions/types.ts +13 -10
  49. package/.framework/migrations/064_episode_leases.sql +196 -0
  50. package/.framework/migrations/065_execution_tables_updated_at.sql +20 -0
  51. package/.framework/migrations/066_episode_steal_thread_merge.sql +148 -0
  52. package/.framework/migrations/067_episode_lease_stale_steal.sql +93 -0
  53. package/.framework/migrations/068_cron_schedule_timer_rpcs.sql +251 -0
  54. package/.framework/migrations/069_log_stats_window.sql +54 -0
  55. package/.framework/migrations/070_app_type_schema_rpcs.sql +166 -0
  56. package/.framework/migrations/071_timer_interval_keys.sql +96 -0
  57. package/.framework/migrations/072_slug_autofill.sql +77 -0
  58. package/.framework/migrations/073_seed_member_roles.sql +26 -0
  59. package/.framework/schemas/spine.config.schema.json +62 -0
  60. package/.framework/src/App.tsx +3 -1
  61. package/.framework/src/apps/admin/index.tsx +1141 -775
  62. package/.framework/src/components/ErrorBoundary.tsx +136 -0
  63. package/.framework/src/components/admin/AdminListPage.tsx +81 -73
  64. package/.framework/src/components/admin/AdminSidebar.tsx +92 -81
  65. package/.framework/src/components/admin/EntitySelect.tsx +181 -0
  66. package/.framework/src/components/admin/PageInstructions.tsx +86 -127
  67. package/.framework/src/components/app-shell/GenericAppShell.tsx +3 -1
  68. package/.framework/src/components/app-shell/GenericDetailPage.tsx +37 -13
  69. package/.framework/src/components/app-shell/GenericListPage.tsx +28 -32
  70. package/.framework/src/components/auth/TurnstileWidget.tsx +9 -6
  71. package/.framework/src/components/kb/KBArticleEditor.tsx +49 -6
  72. package/.framework/src/components/layout/Layout.tsx +4 -2
  73. package/.framework/src/components/layout/Sidebar.tsx +304 -179
  74. package/.framework/src/components/runtime/DataDetailPage.tsx +67 -35
  75. package/.framework/src/components/runtime/DataHeader.tsx +96 -48
  76. package/.framework/src/components/runtime/DataListPage.tsx +3 -26
  77. package/.framework/src/components/runtime/DataStats.tsx +10 -10
  78. package/.framework/src/components/runtime/DataTable.tsx +12 -10
  79. package/.framework/src/components/ui/sheet.tsx +24 -26
  80. package/.framework/src/components/ui/textarea.tsx +74 -48
  81. package/.framework/src/hooks/useBranding.ts +47 -0
  82. package/.framework/src/hooks/useEntityList.ts +12 -9
  83. package/.framework/src/hooks/useListSchema.ts +108 -58
  84. package/.framework/src/instructions/README.md +57 -0
  85. package/.framework/src/instructions/ai-agents.md +69 -0
  86. package/.framework/src/instructions/alerts.md +80 -0
  87. package/.framework/src/instructions/api-keys.md +87 -0
  88. package/.framework/src/instructions/apps.md +77 -0
  89. package/.framework/src/instructions/concepts/ai-and-prompts.md +150 -0
  90. package/.framework/src/instructions/concepts/apps-and-renderers.md +162 -0
  91. package/.framework/src/instructions/concepts/auth-and-api-keys.md +133 -0
  92. package/.framework/src/instructions/concepts/automation.md +186 -0
  93. package/.framework/src/instructions/concepts/concurrency-and-idempotency.md +125 -0
  94. package/.framework/src/instructions/concepts/core-vs-custom.md +124 -0
  95. package/.framework/src/instructions/concepts/design-schema.md +152 -0
  96. package/.framework/src/instructions/concepts/integrations-and-webhooks.md +127 -0
  97. package/.framework/src/instructions/concepts/observability.md +116 -0
  98. package/.framework/src/instructions/concepts/permissions.md +150 -0
  99. package/.framework/src/instructions/concepts/runtime-records.md +111 -0
  100. package/.framework/src/instructions/concepts/scope-and-accounts.md +112 -0
  101. package/.framework/src/instructions/concepts/spine-way.md +172 -0
  102. package/.framework/src/instructions/deliveries.md +59 -0
  103. package/.framework/src/instructions/guide/00-orientation.md +106 -0
  104. package/.framework/src/instructions/guide/01-install-and-first-run.md +253 -0
  105. package/.framework/src/instructions/guide/02-project-map.md +147 -0
  106. package/.framework/src/instructions/guide/03-domain-model.md +223 -0
  107. package/.framework/src/instructions/guide/04-first-app.md +227 -0
  108. package/.framework/src/instructions/guide/05-glossary.md +183 -0
  109. package/.framework/src/instructions/integrations.md +96 -0
  110. package/.framework/src/instructions/internal/cil-markers.md +83 -0
  111. package/.framework/src/instructions/invites.md +60 -0
  112. package/.framework/src/instructions/kb.md +80 -0
  113. package/.framework/src/instructions/logs.md +77 -0
  114. package/.framework/src/instructions/observability.md +61 -0
  115. package/.framework/src/instructions/operations/deployment.md +115 -0
  116. package/.framework/src/instructions/operations/migrations.md +125 -0
  117. package/.framework/src/instructions/operations/security-checklist.md +148 -0
  118. package/.framework/src/instructions/operations/troubleshooting.md +170 -0
  119. package/.framework/src/instructions/operations/upgrade.md +116 -0
  120. package/.framework/src/instructions/pipeline-executions.md +62 -0
  121. package/.framework/src/instructions/pipelines.md +78 -0
  122. package/.framework/src/instructions/prompts.md +95 -0
  123. package/.framework/src/instructions/reference/api/admin-data.md +162 -0
  124. package/.framework/src/instructions/reference/api/auth.md +113 -0
  125. package/.framework/src/instructions/reference/api/errors-and-envelopes.md +113 -0
  126. package/.framework/src/instructions/reference/app-manifest.md +150 -0
  127. package/.framework/src/instructions/reference/cli.md +124 -0
  128. package/.framework/src/instructions/reference/database.md +132 -0
  129. package/.framework/src/instructions/reference/design-schema.md +280 -0
  130. package/.framework/src/instructions/reference/env-vars.md +133 -0
  131. package/.framework/src/instructions/reference/functions/account-nodes.md +75 -0
  132. package/.framework/src/instructions/reference/functions/admin-data.md +195 -0
  133. package/.framework/src/instructions/reference/functions/admin-funnel.md +90 -0
  134. package/.framework/src/instructions/reference/functions/admin-install-funnel.md +107 -0
  135. package/.framework/src/instructions/reference/functions/admin-signals.md +102 -0
  136. package/.framework/src/instructions/reference/functions/ai-agents.md +148 -0
  137. package/.framework/src/instructions/reference/functions/ai-interactions.md +156 -0
  138. package/.framework/src/instructions/reference/functions/api-keys.md +130 -0
  139. package/.framework/src/instructions/reference/functions/apps.md +140 -0
  140. package/.framework/src/instructions/reference/functions/auth.md +99 -0
  141. package/.framework/src/instructions/reference/functions/debug-auth.md +91 -0
  142. package/.framework/src/instructions/reference/functions/deliveries.md +110 -0
  143. package/.framework/src/instructions/reference/functions/embeddings.md +166 -0
  144. package/.framework/src/instructions/reference/functions/eval-harness.md +149 -0
  145. package/.framework/src/instructions/reference/functions/index.md +103 -0
  146. package/.framework/src/instructions/reference/functions/integration-outbound.md +200 -0
  147. package/.framework/src/instructions/reference/functions/integration-routes.md +151 -0
  148. package/.framework/src/instructions/reference/functions/integrations.md +130 -0
  149. package/.framework/src/instructions/reference/functions/invites.md +182 -0
  150. package/.framework/src/instructions/reference/functions/item-progress.md +107 -0
  151. package/.framework/src/instructions/reference/functions/kb.md +166 -0
  152. package/.framework/src/instructions/reference/functions/logs.md +141 -0
  153. package/.framework/src/instructions/reference/functions/observability.md +105 -0
  154. package/.framework/src/instructions/reference/functions/pipeline-executions.md +132 -0
  155. package/.framework/src/instructions/reference/functions/pipeline-runner-background.md +121 -0
  156. package/.framework/src/instructions/reference/functions/pipelines.md +136 -0
  157. package/.framework/src/instructions/reference/functions/prompt-configs.md +121 -0
  158. package/.framework/src/instructions/reference/functions/roles.md +121 -0
  159. package/.framework/src/instructions/reference/functions/security.md +140 -0
  160. package/.framework/src/instructions/reference/functions/system-cron.md +136 -0
  161. package/.framework/src/instructions/reference/functions/system.md +106 -0
  162. package/.framework/src/instructions/reference/functions/tests.md +97 -0
  163. package/.framework/src/instructions/reference/functions/timers.md +117 -0
  164. package/.framework/src/instructions/reference/functions/trigger-scheduler.md +102 -0
  165. package/.framework/src/instructions/reference/functions/triggers.md +140 -0
  166. package/.framework/src/instructions/reference/functions/types.md +146 -0
  167. package/.framework/src/instructions/reference/seeds.md +126 -0
  168. package/.framework/src/instructions/reference/shared-modules.md +127 -0
  169. package/.framework/src/instructions/reference/spine-config.md +114 -0
  170. package/.framework/src/instructions/roles.md +80 -0
  171. package/.framework/src/instructions/runtime-detail.md +70 -0
  172. package/.framework/src/instructions/runtime-list.md +77 -0
  173. package/.framework/src/instructions/security.md +67 -0
  174. package/.framework/src/instructions/tasks/add-a-pipeline.md +110 -0
  175. package/.framework/src/instructions/tasks/add-a-role.md +96 -0
  176. package/.framework/src/instructions/tasks/add-a-timer.md +114 -0
  177. package/.framework/src/instructions/tasks/add-a-trigger.md +106 -0
  178. package/.framework/src/instructions/tasks/add-a-type.md +151 -0
  179. package/.framework/src/instructions/tasks/add-a-webhook.md +108 -0
  180. package/.framework/src/instructions/tasks/add-an-ai-agent.md +108 -0
  181. package/.framework/src/instructions/tasks/add-an-integration.md +124 -0
  182. package/.framework/src/instructions/tasks/build-a-custom-page.md +118 -0
  183. package/.framework/src/instructions/tasks/package-and-install-an-app.md +127 -0
  184. package/.framework/src/instructions/tasks/test-your-code.md +108 -0
  185. package/.framework/src/instructions/tasks/write-a-function.md +132 -0
  186. package/.framework/src/instructions/tasks/write-a-migration.md +110 -0
  187. package/.framework/src/instructions/testing.md +59 -0
  188. package/.framework/src/instructions/timers.md +82 -0
  189. package/.framework/src/instructions/triggers.md +69 -0
  190. package/.framework/src/instructions/types.md +93 -0
  191. package/.framework/src/lib/branding.ts +128 -0
  192. package/.framework/src/pages/NoAppPage.tsx +3 -1
  193. package/.framework/src/pages/NotFoundPage.tsx +62 -31
  194. package/.framework/src/pages/SetupPage.tsx +5 -2
  195. package/.framework/src/pages/admin/AIAgentDetailPage.tsx +117 -106
  196. package/.framework/src/pages/admin/AIAgentsPage.tsx +86 -48
  197. package/.framework/src/pages/admin/APIKeyDetailPage.tsx +153 -152
  198. package/.framework/src/pages/admin/APIKeysPage.tsx +91 -90
  199. package/.framework/src/pages/admin/AlertsConfigPage.tsx +29 -18
  200. package/.framework/src/pages/admin/AppDetailPage.tsx +6 -60
  201. package/.framework/src/pages/admin/AppsPage.tsx +104 -66
  202. package/.framework/src/pages/admin/DeliveriesPage.tsx +5 -5
  203. package/.framework/src/pages/admin/EmbeddingDetailPage.tsx +5 -23
  204. package/.framework/src/pages/admin/EmbeddingsPage.tsx +5 -26
  205. package/.framework/src/pages/admin/IntegrationDetailPage.tsx +7 -7
  206. package/.framework/src/pages/admin/IntegrationsPage.tsx +85 -43
  207. package/.framework/src/pages/admin/LogsPage.tsx +144 -76
  208. package/.framework/src/pages/admin/ObservabilityDashboard.tsx +223 -140
  209. package/.framework/src/pages/admin/PipelineDetailPage.tsx +134 -113
  210. package/.framework/src/pages/admin/PipelineExecutionsPage.tsx +108 -45
  211. package/.framework/src/pages/admin/PipelinesPage.tsx +160 -115
  212. package/.framework/src/pages/admin/PromptConfigDetailPage.tsx +96 -98
  213. package/.framework/src/pages/admin/PromptConfigsPage.tsx +101 -59
  214. package/.framework/src/pages/admin/RoleDetailPage.tsx +90 -79
  215. package/.framework/src/pages/admin/RolesPage.tsx +86 -43
  216. package/.framework/src/pages/admin/SecurityPage.tsx +1 -1
  217. package/.framework/src/pages/admin/TestingDashboard.tsx +16 -28
  218. package/.framework/src/pages/admin/TimerDetailPage.tsx +117 -100
  219. package/.framework/src/pages/admin/TimersPage.tsx +90 -62
  220. package/.framework/src/pages/admin/TriggerDetailPage.tsx +90 -83
  221. package/.framework/src/pages/admin/TriggersPage.tsx +124 -99
  222. package/.framework/src/pages/admin/TypeDetailPage.tsx +152 -138
  223. package/.framework/src/pages/admin/TypesPage.tsx +90 -48
  224. package/.framework/src/pages/admin/cil/CILAgentPage.tsx +17 -25
  225. package/.framework/src/pages/admin/cil/CILChunkDetailPage.tsx +17 -27
  226. package/.framework/src/pages/admin/cil/CILDashboardPage.tsx +72 -40
  227. package/.framework/src/pages/admin/cil/CILFileListPage.tsx +321 -129
  228. package/.framework/src/pages/admin/cil/CILFileSelectPage.tsx +18 -29
  229. package/.framework/src/pages/admin/cil/CILGraphPage.tsx +26 -38
  230. package/.framework/src/pages/admin/cil/CILIngestionPage.tsx +34 -46
  231. package/.framework/src/pages/admin/cil/CILProjectsPage.tsx +189 -103
  232. package/.framework/src/pages/admin/cil/CILSearchPage.tsx +246 -136
  233. package/.framework/src/pages/admin/cil/CILSuggestionsPage.tsx +11 -22
  234. package/.framework/src/pages/admin/cil/CILVersionsPage.tsx +9 -20
  235. package/.framework/src/pages/admin/cil/FlexiblePipelinePage.tsx +26 -38
  236. package/.framework/src/pages/admin/kb/KBAdminPage.tsx +7 -26
  237. package/.framework/src/pages/auth/AuthConfirmPage.tsx +3 -1
  238. package/.framework/src/pages/auth/ForgotPasswordPage.tsx +3 -1
  239. package/.framework/src/pages/auth/LoginPage.tsx +4 -2
  240. package/.framework/src/pages/auth/RegisterPage.tsx +3 -1
  241. package/.framework/templates/agent/AGENTS.md +150 -0
  242. package/.framework/templates/agent/skills/spine-add-ai-agent/SKILL.md +58 -0
  243. package/.framework/templates/agent/skills/spine-add-automation/SKILL.md +62 -0
  244. package/.framework/templates/agent/skills/spine-add-integration/SKILL.md +63 -0
  245. package/.framework/templates/agent/skills/spine-architect/SKILL.md +86 -0
  246. package/.framework/templates/agent/skills/spine-install-app/SKILL.md +72 -0
  247. package/.framework/templates/agent/skills/spine-migration/SKILL.md +75 -0
  248. package/.framework/templates/agent/skills/spine-new-app/SKILL.md +67 -0
  249. package/.framework/templates/agent/skills/spine-new-function/SKILL.md +65 -0
  250. package/.framework/templates/agent/skills/spine-new-type/SKILL.md +65 -0
  251. package/.framework/templates/agent/skills/spine-schema/SKILL.md +77 -0
  252. package/.framework/templates/agent/skills/spine-test/SKILL.md +67 -0
  253. package/.framework/templates/agent/skills/spine-troubleshoot/SKILL.md +74 -0
  254. package/.framework/templates/agent/skills/spine-upgrade/SKILL.md +69 -0
  255. package/.framework/templates/project.json +5 -1
  256. package/.framework/tests/unit/account-scoping-gates.test.ts +392 -0
  257. package/.framework/tests/unit/agent-files.test.ts +355 -0
  258. package/.framework/tests/unit/agent-runner.test.ts +496 -47
  259. package/.framework/tests/unit/api-keys-create-scope-gate.test.ts +160 -0
  260. package/.framework/tests/unit/api-keys-omit-secret-fields.test.ts +38 -0
  261. package/.framework/tests/unit/bootstrap.test.ts +242 -0
  262. package/.framework/tests/unit/cli-context-api-key.test.ts +114 -0
  263. package/.framework/tests/unit/debug-auth.test.ts +38 -0
  264. package/.framework/tests/unit/deliveries-account-scoping.test.ts +185 -0
  265. package/.framework/tests/unit/episode-lease.test.ts +695 -0
  266. package/.framework/tests/unit/integration-outbound-credentials.test.ts +167 -0
  267. package/.framework/tests/unit/integrations-audit-redaction.test.ts +130 -0
  268. package/.framework/tests/unit/kb-ingest-lease.test.ts +19 -0
  269. package/.framework/tests/unit/logging-app-id.test.ts +75 -0
  270. package/.framework/tests/unit/pipeline-runner-background.test.ts +120 -0
  271. package/.framework/tests/unit/pipeline-runner.test.ts +153 -1
  272. package/.framework/tests/unit/principal-cache.test.ts +15 -0
  273. package/.framework/tests/unit/system-context-denial.test.ts +145 -0
  274. package/.framework/tests/unit/system-cron.test.ts +240 -0
  275. package/.framework/tests/unit/timers.test.ts +159 -0
  276. package/.framework/tests/unit/trigger-engine.test.ts +278 -91
  277. package/.framework/tests/unit/trigger-scheduler.test.ts +78 -0
  278. package/README.md +125 -166
  279. package/config/tsconfig.cli.json +2 -2
  280. package/netlify.toml +5 -0
  281. package/package.json +15 -4
  282. package/templates/project/package-lock.json +2 -2
  283. package/.framework/docs/admin-routes.md +0 -76
  284. package/.framework/docs/api-docs-progress.md +0 -38
  285. package/.framework/docs/api-governance.md +0 -146
  286. package/.framework/docs/api-testing-results.md +0 -212
  287. package/.framework/docs/assembly-launch-guide.md +0 -138
  288. package/.framework/docs/audit-results.md +0 -590
  289. package/.framework/docs/db-api-inventory.md +0 -95
  290. package/.framework/docs/gap-remediation-backlog.md +0 -103
  291. package/.framework/docs/ui-adoption-verification.md +0 -111
  292. package/.framework/docs/ui-api-coverage.md +0 -84
  293. package/.framework/docs/v2-compatibility-audit.md +0 -228
  294. package/.framework/mcp/index.ts +0 -133
  295. package/.framework/mcp/load-env.ts +0 -60
  296. package/.framework/mcp/shared/app-manifest.ts +0 -275
  297. package/.framework/mcp/shared/db.ts +0 -477
  298. package/.framework/mcp/shared/kb.ts +0 -118
  299. package/.framework/mcp/tools/architect.ts +0 -431
  300. package/.framework/mcp/tools/cil/context.ts +0 -345
  301. package/.framework/mcp/tools/cil/graph.ts +0 -242
  302. package/.framework/mcp/tools/cil/ingest.ts +0 -1217
  303. package/.framework/mcp/tools/cil/pipeline-orchestrator.ts +0 -693
  304. package/.framework/mcp/tools/cil/pipeline.ts +0 -767
  305. package/.framework/mcp/tools/cil/project.ts +0 -791
  306. package/.framework/mcp/tools/cil/retrieval.ts +0 -336
  307. package/.framework/mcp/tools/cil/search-core.ts +0 -454
  308. package/.framework/mcp/tools/cil/search.ts +0 -97
  309. package/.framework/mcp/tools/cil/suggest.ts +0 -186
  310. package/.framework/mcp/tools/cil/validate.ts +0 -1369
  311. package/.framework/mcp/tools/kb/delete.ts +0 -139
  312. package/.framework/mcp/tools/kb/index.ts +0 -52
  313. package/.framework/mcp/tools/kb/ingest.ts +0 -84
  314. package/.framework/mcp/tools/kb/list-types.ts +0 -102
  315. package/.framework/mcp/tools/kb/publish.ts +0 -100
  316. package/.framework/mcp/tools/kb/reindex.ts +0 -86
  317. package/.framework/mcp/tools/kb/revoke.ts +0 -85
  318. package/.framework/mcp/tools/kb/search.ts +0 -101
  319. package/.framework/mcp/tools/kb/upsert.ts +0 -125
  320. package/.framework/mcp/tools/observability.ts +0 -510
@@ -33,10 +33,31 @@ export interface ProjectProfile {
33
33
  config_files: string[]
34
34
  root_scripts: string[]
35
35
  bin: string[]
36
+ /**
37
+ * Agent-facing assets init copies into the customer repo. Unlike the
38
+ * categories above, these are NOT copied verbatim into a matching path —
39
+ * each entry names a package `source`, a project `dest`, and a `merge`
40
+ * mode handled by syncAgentFiles:
41
+ * framework-block — render the template, substitute {{SPINE_VERSION}},
42
+ * and merge only the marked framework block into dest (init writes the
43
+ * whole file; upgrade replaces the block between the markers and
44
+ * preserves everything else).
45
+ * dir-replace — copy the source directory tree to dest; on every run,
46
+ * `spine-*` subdirectories under dest are removed first, so framework
47
+ * skills are replaced wholesale while customer-owned sibling dirs
48
+ * (any name not starting with `spine-`) are left alone.
49
+ */
50
+ agent_files?: AgentFileEntry[]
36
51
  exclude_globs: string[]
37
52
  package_scripts: Record<string, string>
38
53
  }
39
54
 
55
+ export interface AgentFileEntry {
56
+ source: string
57
+ dest: string
58
+ merge: 'framework-block' | 'dir-replace'
59
+ }
60
+
40
61
  export interface CollectedFile {
41
62
  rel: string
42
63
  abs: string
@@ -280,7 +301,11 @@ export function mergePackageJson(opts: {
280
301
  }
281
302
  for (const [k, v] of Object.entries(opts.newDependencies)) dependencies[k] = v
282
303
  for (const [k, v] of Object.entries(opts.newDevDependencies)) devDependencies[k] = v
283
- dependencies['spine-framework'] = opts.spineVersion
304
+ const existingSpec = dependencies['spine-framework']
305
+ dependencies['spine-framework'] =
306
+ existingSpec && /^(file:|link:|workspace:|portal:)/.test(existingSpec)
307
+ ? existingSpec
308
+ : opts.spineVersion
284
309
 
285
310
  const frameworkDeps = [
286
311
  ...Object.keys(opts.newDependencies),
@@ -322,6 +347,7 @@ export function writeFrozenManifest(opts: {
322
347
  projectRoot: string
323
348
  frameworkVersion: string
324
349
  frameworkDeps: string[]
350
+ agentFiles?: Record<string, string>
325
351
  }): { fileCount: number; treeDigest: string } {
326
352
  const { projectRoot } = opts
327
353
  const entries = collectProtectedFiles(projectRoot, ['bin'], new Set(['.spine-manifest.json']))
@@ -338,6 +364,9 @@ export function writeFrozenManifest(opts: {
338
364
  node: '>=22.13.0',
339
365
  npm: '>=10.0.0',
340
366
  files,
367
+ // Agent assets live outside `files` — see the agent-files block comment
368
+ // above for why sealing them would break verify and upgrade.
369
+ ...(opts.agentFiles ? { agent_files: opts.agentFiles } : {}),
341
370
  }
342
371
  fs.writeFileSync(
343
372
  path.join(projectRoot, '.spine-manifest.json'),
@@ -347,6 +376,217 @@ export function writeFrozenManifest(opts: {
347
376
  }
348
377
  // CIL[END stable_id="~/.framework/cli/lib/project-files.ts:manifest:block"]
349
378
 
379
+ /*
380
+ * Agent asset sync — AGENTS.md framework block + .devin/skills/spine-*.
381
+ *
382
+ * Marker contract (upgrade.ts, doctor.ts, and tests depend on this format):
383
+ *
384
+ * <!-- spine-framework:begin version=X.Y.Z -->
385
+ * ...framework-owned lines...
386
+ * <!-- spine-framework:end -->
387
+ *
388
+ * - The markers are HTML comments so they are inert in rendered markdown.
389
+ * - `version=` on the begin marker lets doctor/doctor-style checks read the
390
+ * block's framework version without hashing.
391
+ * - {{SPINE_VERSION}} anywhere in the template (including inside the begin
392
+ * marker) is substituted at copy time by renderAgentTemplate.
393
+ * - Everything outside the markers is customer-owned and never rewritten.
394
+ *
395
+ * Manifest semantics: agent files are hashed into the manifest's
396
+ * `agent_files` map — NOT `files`. Keeping them out of `files` matters:
397
+ * verify-manifest treats `files` entries as sealed and unions their top-level
398
+ * dirs into the protected trees, which would flag every customer file under
399
+ * .devin/ as EXTRA and every edit to AGENTS.md's user section as MODIFIED.
400
+ * `agent_files` records, per dest rel:
401
+ * - framework-block dests: sha256 of the extracted block (LF-normalized),
402
+ * so edits outside the markers do not count as drift.
403
+ * - dir-replace dests: sha256 of each file under `spine-*` subdirs.
404
+ */
405
+
406
+ /** Begin marker: `<!-- spine-framework:begin version=X.Y.Z -->` (version optional on read). */
407
+ export const AGENTS_BEGIN_RE = /<!--\s*spine-framework:begin(?:\s+version=([^\s>]*))?\s*-->/
408
+ /** End marker: `<!-- spine-framework:end -->`. */
409
+ export const AGENTS_END_RE = /<!--\s*spine-framework:end\s*-->/
410
+
411
+ /** Substitute {{SPINE_VERSION}} placeholders in an agent template. */
412
+ export function renderAgentTemplate(template: string, version: string): string {
413
+ return template.replace(/\{\{SPINE_VERSION\}\}/g, version)
414
+ }
415
+
416
+ /**
417
+ * Locate the framework block inside newline-split lines.
418
+ * Returns inclusive line indices, or null when no begin marker exists.
419
+ * An unterminated begin marker spans only its own line — the orphan marker is
420
+ * consumed on merge while the stale body below degrades into user content
421
+ * (safe: user content is never deleted, only the framework block is managed).
422
+ */
423
+ export function findAgentsBlock(lines: string[]): { begin: number; end: number } | null {
424
+ let begin = -1
425
+ for (let i = 0; i < lines.length; i++) {
426
+ if (AGENTS_BEGIN_RE.test(lines[i])) { begin = i; break }
427
+ }
428
+ if (begin === -1) return null
429
+ for (let i = begin + 1; i < lines.length; i++) {
430
+ if (AGENTS_END_RE.test(lines[i])) return { begin, end: i }
431
+ }
432
+ return { begin, end: begin }
433
+ }
434
+
435
+ /** The framework block's declared version, or null when absent. */
436
+ export function agentsBlockVersion(text: string): string | null {
437
+ const m = text.match(AGENTS_BEGIN_RE)
438
+ return m ? (m[1] ?? null) : null
439
+ }
440
+
441
+ export interface AgentsMergeResult {
442
+ text: string
443
+ status: 'replaced' | 'prepended'
444
+ }
445
+
446
+ /**
447
+ * Merge the rendered template's framework block into existing AGENTS.md text.
448
+ * Replaces the marker-delimited span when markers exist (EOL preserved),
449
+ * otherwise prepends the block ahead of the user's content.
450
+ */
451
+ export function mergeAgentsFile(existing: string, renderedTemplate: string): AgentsMergeResult {
452
+ const eol = existing.includes('\r\n') ? '\r\n' : '\n'
453
+ const tplLines = renderedTemplate.split(/\r?\n/)
454
+ const tplRange = findAgentsBlock(tplLines)
455
+ if (!tplRange) {
456
+ throw new Error('agent template is missing spine-framework begin/end markers')
457
+ }
458
+ const blockLines = tplLines.slice(tplRange.begin, tplRange.end + 1)
459
+
460
+ const lines = existing.split(/\r?\n/)
461
+ const range = findAgentsBlock(lines)
462
+ if (!range) {
463
+ const body = existing.replace(/^\s+/, '')
464
+ return { text: `${blockLines.join(eol)}${eol}${eol}${body}`, status: 'prepended' }
465
+ }
466
+ const next = [...lines.slice(0, range.begin), ...blockLines, ...lines.slice(range.end + 1)]
467
+ return { text: next.join(eol), status: 'replaced' }
468
+ }
469
+
470
+ export interface AgentSyncAction {
471
+ rel: string
472
+ action: 'written' | 'replaced' | 'prepended' | 'synced' | 'unchanged' | 'skipped'
473
+ detail?: string
474
+ }
475
+
476
+ function copyDirRecursive(srcAbs: string, destAbs: string): void {
477
+ fs.mkdirSync(destAbs, { recursive: true })
478
+ for (const dent of fs.readdirSync(srcAbs, { withFileTypes: true })) {
479
+ const src = path.join(srcAbs, dent.name)
480
+ const dest = path.join(destAbs, dent.name)
481
+ if (dent.isDirectory()) copyDirRecursive(src, dest)
482
+ else if (dent.isFile()) {
483
+ fs.copyFileSync(src, dest)
484
+ fs.chmodSync(dest, fs.statSync(src).mode)
485
+ }
486
+ }
487
+ }
488
+
489
+ /**
490
+ * Apply the profile's agent_files entries to a project tree.
491
+ * `framework-block` renders + merges the template; `dir-replace` removes
492
+ * `spine-*` subdirs under dest then copies the source tree in.
493
+ * dryRun reports the same actions without writing.
494
+ */
495
+ export function syncAgentFiles(opts: {
496
+ pkgRoot: string
497
+ projectRoot: string
498
+ entries: AgentFileEntry[]
499
+ version: string
500
+ dryRun?: boolean
501
+ }): AgentSyncAction[] {
502
+ const out: AgentSyncAction[] = []
503
+ for (const entry of opts.entries ?? []) {
504
+ const srcAbs = path.join(opts.pkgRoot, entry.source)
505
+ const destAbs = path.join(opts.projectRoot, entry.dest)
506
+
507
+ if (entry.merge === 'framework-block') {
508
+ if (!fs.existsSync(srcAbs)) {
509
+ out.push({ rel: entry.dest, action: 'skipped', detail: 'template absent from package' })
510
+ continue
511
+ }
512
+ const rendered = renderAgentTemplate(fs.readFileSync(srcAbs, 'utf8'), opts.version)
513
+ if (!fs.existsSync(destAbs)) {
514
+ if (!opts.dryRun) {
515
+ fs.mkdirSync(path.dirname(destAbs), { recursive: true })
516
+ fs.writeFileSync(destAbs, rendered)
517
+ }
518
+ out.push({ rel: entry.dest, action: 'written' })
519
+ } else {
520
+ const existing = fs.readFileSync(destAbs, 'utf8')
521
+ const merged = mergeAgentsFile(existing, rendered)
522
+ if (merged.text === existing) {
523
+ out.push({ rel: entry.dest, action: 'unchanged' })
524
+ } else {
525
+ if (!opts.dryRun) fs.writeFileSync(destAbs, merged.text)
526
+ out.push({ rel: entry.dest, action: merged.status })
527
+ }
528
+ }
529
+ } else if (entry.merge === 'dir-replace') {
530
+ if (!fs.existsSync(srcAbs) || !fs.statSync(srcAbs).isDirectory()) {
531
+ out.push({ rel: entry.dest, action: 'skipped', detail: 'source dir absent from package' })
532
+ continue
533
+ }
534
+ // Framework owns only the spine-* subdirs of dest — remove them, then
535
+ // copy the source tree in. Sibling dirs with any other name are
536
+ // customer-owned and untouched.
537
+ const removed = fs.existsSync(destAbs)
538
+ ? fs.readdirSync(destAbs, { withFileTypes: true })
539
+ .filter((d) => d.isDirectory() && d.name.startsWith('spine-'))
540
+ .map((d) => d.name)
541
+ : []
542
+ if (!opts.dryRun) {
543
+ for (const name of removed) {
544
+ fs.rmSync(path.join(destAbs, name), { recursive: true, force: true })
545
+ }
546
+ copyDirRecursive(srcAbs, destAbs)
547
+ }
548
+ const detail = removed.length ? `removed spine-* dirs: ${removed.join(', ')}` : undefined
549
+ out.push({ rel: entry.dest, action: 'synced', detail })
550
+ } else {
551
+ out.push({ rel: entry.dest, action: 'skipped', detail: `unknown merge mode '${entry.merge}'` })
552
+ }
553
+ }
554
+ return out
555
+ }
556
+
557
+ /**
558
+ * Hash the project's installed agent files for the manifest `agent_files` map.
559
+ * framework-block → hash of the extracted block (LF-normalized, so EOL style
560
+ * does not matter); dir-replace → one entry per file under `spine-*` subdirs.
561
+ */
562
+ export function collectAgentFileHashes(
563
+ projectRoot: string,
564
+ entries: AgentFileEntry[],
565
+ ): Record<string, string> {
566
+ const hashes: Record<string, string> = {}
567
+ for (const entry of entries ?? []) {
568
+ const destAbs = path.join(projectRoot, entry.dest)
569
+ if (entry.merge === 'framework-block') {
570
+ if (!fs.existsSync(destAbs)) continue
571
+ const lines = fs.readFileSync(destAbs, 'utf8').split(/\r?\n/)
572
+ const range = findAgentsBlock(lines)
573
+ if (!range) continue
574
+ hashes[entry.dest] = sha256Text(lines.slice(range.begin, range.end + 1).join('\n') + '\n')
575
+ } else if (entry.merge === 'dir-replace') {
576
+ if (!fs.existsSync(destAbs)) continue
577
+ const managed = fs.readdirSync(destAbs, { withFileTypes: true })
578
+ .filter((d) => d.isDirectory() && d.name.startsWith('spine-'))
579
+ for (const dir of managed) {
580
+ const dirAbs = path.join(destAbs, dir.name)
581
+ for (const f of walkDir(dirAbs, projectRoot, [])) {
582
+ hashes[f.rel] = sha256File(f.abs)
583
+ }
584
+ }
585
+ }
586
+ }
587
+ return hashes
588
+ }
589
+
350
590
  // CIL[BEGIN stable_id="~/.framework/cli/lib/project-files.ts:file_io:block" type="block" identity="file_io" parent="~/.framework/cli/lib/project-files.ts"]
351
591
  // CIL[DOC-MACRO purpose="Copy collected files (mode-preserving, symlink safety-checked), sanitize dir names to npm package names, compare semvers"]
352
592
  // CIL[DOC-CONTEXT module="cli" layer="lib" related_files=""]