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.
- package/.env.example +13 -3
- package/.framework/cli/commands/bootstrap.ts +392 -0
- package/.framework/cli/commands/dev.ts +91 -218
- package/.framework/cli/commands/doctor.ts +132 -26
- package/.framework/cli/commands/generate.ts +38 -298
- package/.framework/cli/commands/init.ts +20 -1
- package/.framework/cli/commands/migrate.ts +36 -30
- package/.framework/cli/commands/migrations.ts +16 -56
- package/.framework/cli/commands/upgrade.ts +35 -0
- package/.framework/cli/commands/validate-schema.ts +147 -0
- package/.framework/cli/context.ts +223 -110
- package/.framework/cli/index.ts +6 -0
- package/.framework/cli/lib/project-files.ts +241 -1
- package/.framework/functions/_shared/agent-runner.ts +1000 -81
- package/.framework/functions/_shared/cil.ts +2 -1
- package/.framework/functions/_shared/db.ts +3 -6
- package/.framework/functions/_shared/hash-api-key.ts +183 -79
- package/.framework/functions/_shared/kb.ts +61 -7
- package/.framework/functions/_shared/logging.ts +562 -224
- package/.framework/functions/_shared/message-sequence.ts +53 -0
- package/.framework/functions/_shared/middleware.ts +47 -44
- package/.framework/functions/_shared/pipeline-runner.ts +150 -37
- package/.framework/functions/_shared/principal.ts +9 -6
- package/.framework/functions/_shared/trigger-engine.ts +149 -40
- package/.framework/functions/admin-data.ts +48 -31
- package/.framework/functions/ai-agents.ts +103 -33
- package/.framework/functions/api-keys.ts +301 -122
- package/.framework/functions/apps.ts +43 -26
- package/.framework/functions/cil-ingest.ts +48 -15
- package/.framework/functions/debug-auth.ts +14 -8
- package/.framework/functions/deliveries.ts +39 -12
- package/.framework/functions/embeddings.ts +145 -76
- package/.framework/functions/integration-outbound.ts +67 -27
- package/.framework/functions/integrations.ts +254 -127
- package/.framework/functions/logs.ts +273 -133
- package/.framework/functions/observability.ts +277 -147
- package/.framework/functions/pipeline-executions.ts +1 -1
- package/.framework/functions/pipeline-runner-background.ts +120 -0
- package/.framework/functions/pipelines.ts +14 -9
- package/.framework/functions/prompt-configs.ts +7 -4
- package/.framework/functions/roles.ts +8 -5
- package/.framework/functions/security.ts +30 -17
- package/.framework/functions/system-cron.ts +250 -128
- package/.framework/functions/tests.ts +7 -6
- package/.framework/functions/timers.ts +310 -107
- package/.framework/functions/trigger-scheduler.ts +65 -5
- package/.framework/functions/triggers.ts +28 -13
- package/.framework/functions/types.ts +13 -10
- package/.framework/migrations/064_episode_leases.sql +196 -0
- package/.framework/migrations/065_execution_tables_updated_at.sql +20 -0
- package/.framework/migrations/066_episode_steal_thread_merge.sql +148 -0
- package/.framework/migrations/067_episode_lease_stale_steal.sql +93 -0
- package/.framework/migrations/068_cron_schedule_timer_rpcs.sql +251 -0
- package/.framework/migrations/069_log_stats_window.sql +54 -0
- package/.framework/migrations/070_app_type_schema_rpcs.sql +166 -0
- package/.framework/migrations/071_timer_interval_keys.sql +96 -0
- package/.framework/migrations/072_slug_autofill.sql +77 -0
- package/.framework/migrations/073_seed_member_roles.sql +26 -0
- package/.framework/schemas/spine.config.schema.json +62 -0
- package/.framework/src/App.tsx +3 -1
- package/.framework/src/apps/admin/index.tsx +1141 -775
- package/.framework/src/components/ErrorBoundary.tsx +136 -0
- package/.framework/src/components/admin/AdminListPage.tsx +81 -73
- package/.framework/src/components/admin/AdminSidebar.tsx +92 -81
- package/.framework/src/components/admin/EntitySelect.tsx +181 -0
- package/.framework/src/components/admin/PageInstructions.tsx +86 -127
- package/.framework/src/components/app-shell/GenericAppShell.tsx +3 -1
- package/.framework/src/components/app-shell/GenericDetailPage.tsx +37 -13
- package/.framework/src/components/app-shell/GenericListPage.tsx +28 -32
- package/.framework/src/components/auth/TurnstileWidget.tsx +9 -6
- package/.framework/src/components/kb/KBArticleEditor.tsx +49 -6
- package/.framework/src/components/layout/Layout.tsx +4 -2
- package/.framework/src/components/layout/Sidebar.tsx +304 -179
- package/.framework/src/components/runtime/DataDetailPage.tsx +67 -35
- package/.framework/src/components/runtime/DataHeader.tsx +96 -48
- package/.framework/src/components/runtime/DataListPage.tsx +3 -26
- package/.framework/src/components/runtime/DataStats.tsx +10 -10
- package/.framework/src/components/runtime/DataTable.tsx +12 -10
- package/.framework/src/components/ui/sheet.tsx +24 -26
- package/.framework/src/components/ui/textarea.tsx +74 -48
- package/.framework/src/hooks/useBranding.ts +47 -0
- package/.framework/src/hooks/useEntityList.ts +12 -9
- package/.framework/src/hooks/useListSchema.ts +108 -58
- package/.framework/src/instructions/README.md +57 -0
- package/.framework/src/instructions/ai-agents.md +69 -0
- package/.framework/src/instructions/alerts.md +80 -0
- package/.framework/src/instructions/api-keys.md +87 -0
- package/.framework/src/instructions/apps.md +77 -0
- package/.framework/src/instructions/concepts/ai-and-prompts.md +150 -0
- package/.framework/src/instructions/concepts/apps-and-renderers.md +162 -0
- package/.framework/src/instructions/concepts/auth-and-api-keys.md +133 -0
- package/.framework/src/instructions/concepts/automation.md +186 -0
- package/.framework/src/instructions/concepts/concurrency-and-idempotency.md +125 -0
- package/.framework/src/instructions/concepts/core-vs-custom.md +124 -0
- package/.framework/src/instructions/concepts/design-schema.md +152 -0
- package/.framework/src/instructions/concepts/integrations-and-webhooks.md +127 -0
- package/.framework/src/instructions/concepts/observability.md +116 -0
- package/.framework/src/instructions/concepts/permissions.md +150 -0
- package/.framework/src/instructions/concepts/runtime-records.md +111 -0
- package/.framework/src/instructions/concepts/scope-and-accounts.md +112 -0
- package/.framework/src/instructions/concepts/spine-way.md +172 -0
- package/.framework/src/instructions/deliveries.md +59 -0
- package/.framework/src/instructions/guide/00-orientation.md +106 -0
- package/.framework/src/instructions/guide/01-install-and-first-run.md +253 -0
- package/.framework/src/instructions/guide/02-project-map.md +147 -0
- package/.framework/src/instructions/guide/03-domain-model.md +223 -0
- package/.framework/src/instructions/guide/04-first-app.md +227 -0
- package/.framework/src/instructions/guide/05-glossary.md +183 -0
- package/.framework/src/instructions/integrations.md +96 -0
- package/.framework/src/instructions/internal/cil-markers.md +83 -0
- package/.framework/src/instructions/invites.md +60 -0
- package/.framework/src/instructions/kb.md +80 -0
- package/.framework/src/instructions/logs.md +77 -0
- package/.framework/src/instructions/observability.md +61 -0
- package/.framework/src/instructions/operations/deployment.md +115 -0
- package/.framework/src/instructions/operations/migrations.md +125 -0
- package/.framework/src/instructions/operations/security-checklist.md +148 -0
- package/.framework/src/instructions/operations/troubleshooting.md +170 -0
- package/.framework/src/instructions/operations/upgrade.md +116 -0
- package/.framework/src/instructions/pipeline-executions.md +62 -0
- package/.framework/src/instructions/pipelines.md +78 -0
- package/.framework/src/instructions/prompts.md +95 -0
- package/.framework/src/instructions/reference/api/admin-data.md +162 -0
- package/.framework/src/instructions/reference/api/auth.md +113 -0
- package/.framework/src/instructions/reference/api/errors-and-envelopes.md +113 -0
- package/.framework/src/instructions/reference/app-manifest.md +150 -0
- package/.framework/src/instructions/reference/cli.md +124 -0
- package/.framework/src/instructions/reference/database.md +132 -0
- package/.framework/src/instructions/reference/design-schema.md +280 -0
- package/.framework/src/instructions/reference/env-vars.md +133 -0
- package/.framework/src/instructions/reference/functions/account-nodes.md +75 -0
- package/.framework/src/instructions/reference/functions/admin-data.md +195 -0
- package/.framework/src/instructions/reference/functions/admin-funnel.md +90 -0
- package/.framework/src/instructions/reference/functions/admin-install-funnel.md +107 -0
- package/.framework/src/instructions/reference/functions/admin-signals.md +102 -0
- package/.framework/src/instructions/reference/functions/ai-agents.md +148 -0
- package/.framework/src/instructions/reference/functions/ai-interactions.md +156 -0
- package/.framework/src/instructions/reference/functions/api-keys.md +130 -0
- package/.framework/src/instructions/reference/functions/apps.md +140 -0
- package/.framework/src/instructions/reference/functions/auth.md +99 -0
- package/.framework/src/instructions/reference/functions/debug-auth.md +91 -0
- package/.framework/src/instructions/reference/functions/deliveries.md +110 -0
- package/.framework/src/instructions/reference/functions/embeddings.md +166 -0
- package/.framework/src/instructions/reference/functions/eval-harness.md +149 -0
- package/.framework/src/instructions/reference/functions/index.md +103 -0
- package/.framework/src/instructions/reference/functions/integration-outbound.md +200 -0
- package/.framework/src/instructions/reference/functions/integration-routes.md +151 -0
- package/.framework/src/instructions/reference/functions/integrations.md +130 -0
- package/.framework/src/instructions/reference/functions/invites.md +182 -0
- package/.framework/src/instructions/reference/functions/item-progress.md +107 -0
- package/.framework/src/instructions/reference/functions/kb.md +166 -0
- package/.framework/src/instructions/reference/functions/logs.md +141 -0
- package/.framework/src/instructions/reference/functions/observability.md +105 -0
- package/.framework/src/instructions/reference/functions/pipeline-executions.md +132 -0
- package/.framework/src/instructions/reference/functions/pipeline-runner-background.md +121 -0
- package/.framework/src/instructions/reference/functions/pipelines.md +136 -0
- package/.framework/src/instructions/reference/functions/prompt-configs.md +121 -0
- package/.framework/src/instructions/reference/functions/roles.md +121 -0
- package/.framework/src/instructions/reference/functions/security.md +140 -0
- package/.framework/src/instructions/reference/functions/system-cron.md +136 -0
- package/.framework/src/instructions/reference/functions/system.md +106 -0
- package/.framework/src/instructions/reference/functions/tests.md +97 -0
- package/.framework/src/instructions/reference/functions/timers.md +117 -0
- package/.framework/src/instructions/reference/functions/trigger-scheduler.md +102 -0
- package/.framework/src/instructions/reference/functions/triggers.md +140 -0
- package/.framework/src/instructions/reference/functions/types.md +146 -0
- package/.framework/src/instructions/reference/seeds.md +126 -0
- package/.framework/src/instructions/reference/shared-modules.md +127 -0
- package/.framework/src/instructions/reference/spine-config.md +114 -0
- package/.framework/src/instructions/roles.md +80 -0
- package/.framework/src/instructions/runtime-detail.md +70 -0
- package/.framework/src/instructions/runtime-list.md +77 -0
- package/.framework/src/instructions/security.md +67 -0
- package/.framework/src/instructions/tasks/add-a-pipeline.md +110 -0
- package/.framework/src/instructions/tasks/add-a-role.md +96 -0
- package/.framework/src/instructions/tasks/add-a-timer.md +114 -0
- package/.framework/src/instructions/tasks/add-a-trigger.md +106 -0
- package/.framework/src/instructions/tasks/add-a-type.md +151 -0
- package/.framework/src/instructions/tasks/add-a-webhook.md +108 -0
- package/.framework/src/instructions/tasks/add-an-ai-agent.md +108 -0
- package/.framework/src/instructions/tasks/add-an-integration.md +124 -0
- package/.framework/src/instructions/tasks/build-a-custom-page.md +118 -0
- package/.framework/src/instructions/tasks/package-and-install-an-app.md +127 -0
- package/.framework/src/instructions/tasks/test-your-code.md +108 -0
- package/.framework/src/instructions/tasks/write-a-function.md +132 -0
- package/.framework/src/instructions/tasks/write-a-migration.md +110 -0
- package/.framework/src/instructions/testing.md +59 -0
- package/.framework/src/instructions/timers.md +82 -0
- package/.framework/src/instructions/triggers.md +69 -0
- package/.framework/src/instructions/types.md +93 -0
- package/.framework/src/lib/branding.ts +128 -0
- package/.framework/src/pages/NoAppPage.tsx +3 -1
- package/.framework/src/pages/NotFoundPage.tsx +62 -31
- package/.framework/src/pages/SetupPage.tsx +5 -2
- package/.framework/src/pages/admin/AIAgentDetailPage.tsx +117 -106
- package/.framework/src/pages/admin/AIAgentsPage.tsx +86 -48
- package/.framework/src/pages/admin/APIKeyDetailPage.tsx +153 -152
- package/.framework/src/pages/admin/APIKeysPage.tsx +91 -90
- package/.framework/src/pages/admin/AlertsConfigPage.tsx +29 -18
- package/.framework/src/pages/admin/AppDetailPage.tsx +6 -60
- package/.framework/src/pages/admin/AppsPage.tsx +104 -66
- package/.framework/src/pages/admin/DeliveriesPage.tsx +5 -5
- package/.framework/src/pages/admin/EmbeddingDetailPage.tsx +5 -23
- package/.framework/src/pages/admin/EmbeddingsPage.tsx +5 -26
- package/.framework/src/pages/admin/IntegrationDetailPage.tsx +7 -7
- package/.framework/src/pages/admin/IntegrationsPage.tsx +85 -43
- package/.framework/src/pages/admin/LogsPage.tsx +144 -76
- package/.framework/src/pages/admin/ObservabilityDashboard.tsx +223 -140
- package/.framework/src/pages/admin/PipelineDetailPage.tsx +134 -113
- package/.framework/src/pages/admin/PipelineExecutionsPage.tsx +108 -45
- package/.framework/src/pages/admin/PipelinesPage.tsx +160 -115
- package/.framework/src/pages/admin/PromptConfigDetailPage.tsx +96 -98
- package/.framework/src/pages/admin/PromptConfigsPage.tsx +101 -59
- package/.framework/src/pages/admin/RoleDetailPage.tsx +90 -79
- package/.framework/src/pages/admin/RolesPage.tsx +86 -43
- package/.framework/src/pages/admin/SecurityPage.tsx +1 -1
- package/.framework/src/pages/admin/TestingDashboard.tsx +16 -28
- package/.framework/src/pages/admin/TimerDetailPage.tsx +117 -100
- package/.framework/src/pages/admin/TimersPage.tsx +90 -62
- package/.framework/src/pages/admin/TriggerDetailPage.tsx +90 -83
- package/.framework/src/pages/admin/TriggersPage.tsx +124 -99
- package/.framework/src/pages/admin/TypeDetailPage.tsx +152 -138
- package/.framework/src/pages/admin/TypesPage.tsx +90 -48
- package/.framework/src/pages/admin/cil/CILAgentPage.tsx +17 -25
- package/.framework/src/pages/admin/cil/CILChunkDetailPage.tsx +17 -27
- package/.framework/src/pages/admin/cil/CILDashboardPage.tsx +72 -40
- package/.framework/src/pages/admin/cil/CILFileListPage.tsx +321 -129
- package/.framework/src/pages/admin/cil/CILFileSelectPage.tsx +18 -29
- package/.framework/src/pages/admin/cil/CILGraphPage.tsx +26 -38
- package/.framework/src/pages/admin/cil/CILIngestionPage.tsx +34 -46
- package/.framework/src/pages/admin/cil/CILProjectsPage.tsx +189 -103
- package/.framework/src/pages/admin/cil/CILSearchPage.tsx +246 -136
- package/.framework/src/pages/admin/cil/CILSuggestionsPage.tsx +11 -22
- package/.framework/src/pages/admin/cil/CILVersionsPage.tsx +9 -20
- package/.framework/src/pages/admin/cil/FlexiblePipelinePage.tsx +26 -38
- package/.framework/src/pages/admin/kb/KBAdminPage.tsx +7 -26
- package/.framework/src/pages/auth/AuthConfirmPage.tsx +3 -1
- package/.framework/src/pages/auth/ForgotPasswordPage.tsx +3 -1
- package/.framework/src/pages/auth/LoginPage.tsx +4 -2
- package/.framework/src/pages/auth/RegisterPage.tsx +3 -1
- package/.framework/templates/agent/AGENTS.md +150 -0
- package/.framework/templates/agent/skills/spine-add-ai-agent/SKILL.md +58 -0
- package/.framework/templates/agent/skills/spine-add-automation/SKILL.md +62 -0
- package/.framework/templates/agent/skills/spine-add-integration/SKILL.md +63 -0
- package/.framework/templates/agent/skills/spine-architect/SKILL.md +86 -0
- package/.framework/templates/agent/skills/spine-install-app/SKILL.md +72 -0
- package/.framework/templates/agent/skills/spine-migration/SKILL.md +75 -0
- package/.framework/templates/agent/skills/spine-new-app/SKILL.md +67 -0
- package/.framework/templates/agent/skills/spine-new-function/SKILL.md +65 -0
- package/.framework/templates/agent/skills/spine-new-type/SKILL.md +65 -0
- package/.framework/templates/agent/skills/spine-schema/SKILL.md +77 -0
- package/.framework/templates/agent/skills/spine-test/SKILL.md +67 -0
- package/.framework/templates/agent/skills/spine-troubleshoot/SKILL.md +74 -0
- package/.framework/templates/agent/skills/spine-upgrade/SKILL.md +69 -0
- package/.framework/templates/project.json +5 -1
- package/.framework/tests/unit/account-scoping-gates.test.ts +392 -0
- package/.framework/tests/unit/agent-files.test.ts +355 -0
- package/.framework/tests/unit/agent-runner.test.ts +496 -47
- package/.framework/tests/unit/api-keys-create-scope-gate.test.ts +160 -0
- package/.framework/tests/unit/api-keys-omit-secret-fields.test.ts +38 -0
- package/.framework/tests/unit/bootstrap.test.ts +242 -0
- package/.framework/tests/unit/cli-context-api-key.test.ts +114 -0
- package/.framework/tests/unit/debug-auth.test.ts +38 -0
- package/.framework/tests/unit/deliveries-account-scoping.test.ts +185 -0
- package/.framework/tests/unit/episode-lease.test.ts +695 -0
- package/.framework/tests/unit/integration-outbound-credentials.test.ts +167 -0
- package/.framework/tests/unit/integrations-audit-redaction.test.ts +130 -0
- package/.framework/tests/unit/kb-ingest-lease.test.ts +19 -0
- package/.framework/tests/unit/logging-app-id.test.ts +75 -0
- package/.framework/tests/unit/pipeline-runner-background.test.ts +120 -0
- package/.framework/tests/unit/pipeline-runner.test.ts +153 -1
- package/.framework/tests/unit/principal-cache.test.ts +15 -0
- package/.framework/tests/unit/system-context-denial.test.ts +145 -0
- package/.framework/tests/unit/system-cron.test.ts +240 -0
- package/.framework/tests/unit/timers.test.ts +159 -0
- package/.framework/tests/unit/trigger-engine.test.ts +278 -91
- package/.framework/tests/unit/trigger-scheduler.test.ts +78 -0
- package/README.md +125 -166
- package/config/tsconfig.cli.json +2 -2
- package/netlify.toml +5 -0
- package/package.json +15 -4
- package/templates/project/package-lock.json +2 -2
- package/.framework/docs/admin-routes.md +0 -76
- package/.framework/docs/api-docs-progress.md +0 -38
- package/.framework/docs/api-governance.md +0 -146
- package/.framework/docs/api-testing-results.md +0 -212
- package/.framework/docs/assembly-launch-guide.md +0 -138
- package/.framework/docs/audit-results.md +0 -590
- package/.framework/docs/db-api-inventory.md +0 -95
- package/.framework/docs/gap-remediation-backlog.md +0 -103
- package/.framework/docs/ui-adoption-verification.md +0 -111
- package/.framework/docs/ui-api-coverage.md +0 -84
- package/.framework/docs/v2-compatibility-audit.md +0 -228
- package/.framework/mcp/index.ts +0 -133
- package/.framework/mcp/load-env.ts +0 -60
- package/.framework/mcp/shared/app-manifest.ts +0 -275
- package/.framework/mcp/shared/db.ts +0 -477
- package/.framework/mcp/shared/kb.ts +0 -118
- package/.framework/mcp/tools/architect.ts +0 -431
- package/.framework/mcp/tools/cil/context.ts +0 -345
- package/.framework/mcp/tools/cil/graph.ts +0 -242
- package/.framework/mcp/tools/cil/ingest.ts +0 -1217
- package/.framework/mcp/tools/cil/pipeline-orchestrator.ts +0 -693
- package/.framework/mcp/tools/cil/pipeline.ts +0 -767
- package/.framework/mcp/tools/cil/project.ts +0 -791
- package/.framework/mcp/tools/cil/retrieval.ts +0 -336
- package/.framework/mcp/tools/cil/search-core.ts +0 -454
- package/.framework/mcp/tools/cil/search.ts +0 -97
- package/.framework/mcp/tools/cil/suggest.ts +0 -186
- package/.framework/mcp/tools/cil/validate.ts +0 -1369
- package/.framework/mcp/tools/kb/delete.ts +0 -139
- package/.framework/mcp/tools/kb/index.ts +0 -52
- package/.framework/mcp/tools/kb/ingest.ts +0 -84
- package/.framework/mcp/tools/kb/list-types.ts +0 -102
- package/.framework/mcp/tools/kb/publish.ts +0 -100
- package/.framework/mcp/tools/kb/reindex.ts +0 -86
- package/.framework/mcp/tools/kb/revoke.ts +0 -85
- package/.framework/mcp/tools/kb/search.ts +0 -101
- package/.framework/mcp/tools/kb/upsert.ts +0 -125
- 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']
|
|
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=""]
|