spine-framework 0.4.4 → 0.5.0-rc.5
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/install-app.ts +183 -13
- 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-app.ts +127 -1
- 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/app-manifest.ts +2 -0
- package/.framework/functions/_shared/hash-api-key.ts +183 -79
- package/.framework/functions/_shared/logging.ts +562 -224
- package/.framework/functions/_shared/middleware.ts +47 -44
- package/.framework/functions/_shared/permissions.ts +4 -0
- package/.framework/functions/admin-data.ts +51 -28
- package/.framework/functions/ai-agents.ts +10 -6
- package/.framework/functions/api-keys.ts +301 -122
- package/.framework/functions/apps.ts +63 -29
- 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 +149 -78
- 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 +278 -148
- package/.framework/functions/pipeline-executions.ts +1 -1
- 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 +240 -127
- package/.framework/functions/tests.ts +7 -6
- package/.framework/functions/timers.ts +310 -107
- package/.framework/functions/triggers.ts +28 -13
- package/.framework/functions/types.ts +13 -10
- 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/migrations/074_app_route_prefix_domains.sql +168 -0
- package/.framework/migrations/075_security_definer_grants.sql +157 -0
- 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 +88 -79
- package/.framework/src/components/admin/EntitySelect.tsx +181 -0
- package/.framework/src/components/admin/PageInstructions.tsx +86 -127
- package/.framework/src/components/app-shell/GenericDetailPage.tsx +37 -13
- package/.framework/src/components/app-shell/GenericListPage.tsx +28 -32
- package/.framework/src/components/layout/Sidebar.tsx +300 -177
- 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/contexts/AppContext.tsx +5 -12
- package/.framework/src/contexts/AuthContext.tsx +17 -9
- package/.framework/src/hooks/useApps.ts +18 -22
- package/.framework/src/hooks/useEntityList.ts +12 -9
- package/.framework/src/hooks/useListSchema.ts +108 -58
- package/.framework/src/instructions/README.md +74 -0
- package/.framework/src/instructions/accounts.md +97 -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 +228 -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/internal/docs-contract.md +158 -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/notifications.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 +170 -0
- package/.framework/src/instructions/operations/troubleshooting.md +170 -0
- package/.framework/src/instructions/operations/upgrade.md +116 -0
- package/.framework/src/instructions/people.md +91 -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 +184 -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/frontend-sdk.md +207 -0
- package/.framework/src/instructions/reference/functions/account-nodes.md +75 -0
- package/.framework/src/instructions/reference/functions/admin-data.md +197 -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-collaboration.md +106 -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/document-your-app.md +117 -0
- package/.framework/src/instructions/tasks/package-and-install-an-app.md +136 -0
- package/.framework/src/instructions/tasks/register-an-app.md +137 -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 +84 -0
- package/.framework/src/instructions/triggers.md +69 -0
- package/.framework/src/instructions/types.md +93 -0
- package/.framework/src/lib/app-mount.ts +88 -0
- package/.framework/src/pages/NotFoundPage.tsx +62 -31
- 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/LoginPage.tsx +3 -2
- package/.framework/src/pages/auth/RegisterPage.tsx +16 -8
- package/.framework/templates/agent/AGENTS.md +167 -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 +79 -0
- package/.framework/templates/agent/skills/spine-migration/SKILL.md +75 -0
- package/.framework/templates/agent/skills/spine-new-app/SKILL.md +79 -0
- package/.framework/templates/agent/skills/spine-new-function/SKILL.md +71 -0
- package/.framework/templates/agent/skills/spine-new-type/SKILL.md +72 -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 +4 -1
- package/.framework/tests/unit/account-scoping-gates.test.ts +394 -0
- package/.framework/tests/unit/agent-files.test.ts +355 -0
- package/.framework/tests/unit/agent-runner.test.ts +479 -44
- 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/app-mount.test.ts +80 -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/integration-outbound-credentials.test.ts +167 -0
- package/.framework/tests/unit/integrations-audit-redaction.test.ts +130 -0
- package/.framework/tests/unit/logging-app-id.test.ts +75 -0
- package/.framework/tests/unit/security-definer-grants.test.ts +154 -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/README.md +125 -166
- package/config/tsconfig.cli.json +2 -2
- 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
|
@@ -20,6 +20,8 @@ import {
|
|
|
20
20
|
mergePackageJson,
|
|
21
21
|
writeFrozenManifest,
|
|
22
22
|
sanitizePackageName,
|
|
23
|
+
syncAgentFiles,
|
|
24
|
+
collectAgentFileHashes,
|
|
23
25
|
} from '../lib/project-files.ts'
|
|
24
26
|
// CIL[END stable_id="~/.framework/cli/commands/init.ts:module_header:module_header"]
|
|
25
27
|
|
|
@@ -137,11 +139,23 @@ export function registerInitCommands(program: Command) {
|
|
|
137
139
|
console.log(' ✓ spine.config.json written (neutral template)')
|
|
138
140
|
}
|
|
139
141
|
|
|
140
|
-
// 8.
|
|
142
|
+
// 8. Agent assets — AGENTS.md framework block + .devin/skills/spine-*
|
|
143
|
+
const agentActions = syncAgentFiles({
|
|
144
|
+
pkgRoot: PACKAGE_ROOT,
|
|
145
|
+
projectRoot: target,
|
|
146
|
+
entries: profile.agent_files ?? [],
|
|
147
|
+
version: frameworkVersion,
|
|
148
|
+
})
|
|
149
|
+
for (const a of agentActions) {
|
|
150
|
+
console.log(` ✓ ${a.rel} (${a.action}${a.detail ? ` — ${a.detail}` : ''})`)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// 9. Frozen integrity manifest — version + framework-owned dep inventory
|
|
141
154
|
const manifest = writeFrozenManifest({
|
|
142
155
|
projectRoot: target,
|
|
143
156
|
frameworkVersion,
|
|
144
157
|
frameworkDeps: merged.frameworkDeps,
|
|
158
|
+
agentFiles: collectAgentFileHashes(target, profile.agent_files ?? []),
|
|
145
159
|
})
|
|
146
160
|
console.log(` ✓ .spine-manifest.json written (${manifest.fileCount} protected files, frozen)`)
|
|
147
161
|
|
|
@@ -158,6 +172,11 @@ export function registerInitCommands(program: Command) {
|
|
|
158
172
|
console.log(' 5. npx spine-framework migrate')
|
|
159
173
|
console.log(' 6. npm run dev:netlify')
|
|
160
174
|
console.log()
|
|
175
|
+
console.log('Docs + agent assets:')
|
|
176
|
+
console.log(' - Instruction book: .framework/src/instructions/README.md')
|
|
177
|
+
console.log(' - Agent rules: AGENTS.md (framework block refreshes on upgrade)')
|
|
178
|
+
console.log(' - Agent skills: .devin/skills/spine-*')
|
|
179
|
+
console.log()
|
|
161
180
|
} catch (err: any) {
|
|
162
181
|
console.error('\n❌ Init failed:', err.message)
|
|
163
182
|
if (process.env.SPINE_CLI_DEBUG) console.error(err.stack)
|
|
@@ -142,7 +142,7 @@ interface SpineConfig {
|
|
|
142
142
|
// CIL[DOC-AUDIENCE audience="internal"]
|
|
143
143
|
|
|
144
144
|
import type { Command } from 'commander'
|
|
145
|
-
import { existsSync, readFileSync, mkdirSync, cpSync, writeFileSync, realpathSync, statSync } from 'fs'
|
|
145
|
+
import { existsSync, readFileSync, mkdirSync, cpSync, writeFileSync, realpathSync, statSync, readdirSync, rmSync } from 'fs'
|
|
146
146
|
import { resolve, dirname, join, basename, isAbsolute, sep } from 'path'
|
|
147
147
|
import { fileURLToPath } from 'url'
|
|
148
148
|
import { execSync } from 'child_process'
|
|
@@ -319,6 +319,165 @@ const SPINE_CONFIG_PATH = join(PROJECT_ROOT, 'spine.config.json')
|
|
|
319
319
|
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
320
320
|
|
|
321
321
|
|
|
322
|
+
// CIL[BEGIN stable_id="~/.framework/cli/commands/install-app.ts:appcopyentries:constant" type="constant" identity="APP_COPY_ENTRIES" parent="none"]
|
|
323
|
+
// CIL[DOC-MACRO purpose="Single allowlist of package entries copied into custom/apps/<slug>/ on install and update"]
|
|
324
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Declare the shared copy allowlist so install-app and update-app cannot drift apart"]
|
|
325
|
+
// CIL[DOC-CONTEXT module="install-app" layer="cli" related_files=".framework/cli/commands/validate-app.ts"]
|
|
326
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="readonly string[] of top-level package entries" errors="none" side_effects="none"]
|
|
327
|
+
// CIL[DOC-MICRO logic_summary="Union of both commands' previous lists plus entries real apps ship (lib/views/docs/installer/mcp/migrations/tests/standalone.config.json); 'agent' is handled separately by syncAppAgentAssets" notable_decisions="['shared_allowlist_prevents_drift','agent_staged_not_copied_verbatim']"]
|
|
328
|
+
// CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
|
|
329
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
330
|
+
const APP_COPY_ENTRIES: readonly string[] = [
|
|
331
|
+
'index.tsx', 'manifest.json', 'package.json', 'standalone.config.json',
|
|
332
|
+
'pages', 'components', 'hooks', 'utils', 'lib', 'views', 'docs',
|
|
333
|
+
'functions', 'api', 'seed', 'public', 'installer', 'mcp',
|
|
334
|
+
'migrations', 'migrations-archive', 'tests', 'agent',
|
|
335
|
+
]
|
|
336
|
+
// CIL[END stable_id="~/.framework/cli/commands/install-app.ts:appcopyentries:constant"]
|
|
337
|
+
|
|
338
|
+
// CIL[BEGIN stable_id="~/.framework/cli/commands/install-app.ts:agentskillprefixre:constant" type="constant" identity="AGENT_SKILL_PREFIX_RE" parent="none"]
|
|
339
|
+
// CIL[DOC-MACRO purpose="Validation regex for manifest agent.skill_prefix"]
|
|
340
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Declare the single source of truth for the skill-prefix rule shared by install and validate paths"]
|
|
341
|
+
// CIL[DOC-CONTEXT module="install-app" layer="cli" related_files=".framework/cli/commands/validate-app.ts"]
|
|
342
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="RegExp matching 1-8 char lowercase-alphanumeric prefixes starting with a letter" errors="none" side_effects="none"]
|
|
343
|
+
// CIL[DOC-MICRO logic_summary="/^[a-z][a-z0-9]{0,7}$/ — letter first, then up to 7 lowercase letters or digits" notable_decisions="['exported_for_shared_use']"]
|
|
344
|
+
// CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
|
|
345
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
346
|
+
export const AGENT_SKILL_PREFIX_RE = /^[a-z][a-z0-9]{0,7}$/
|
|
347
|
+
// CIL[END stable_id="~/.framework/cli/commands/install-app.ts:agentskillprefixre:constant"]
|
|
348
|
+
|
|
349
|
+
// CIL[BEGIN stable_id="~/.framework/cli/commands/install-app.ts:syncappagentassets:function" type="function" identity="syncAppAgentAssets" parent="none"]
|
|
350
|
+
// CIL[DOC-MACRO purpose="Sync an installed app's staged agent assets (agent/skills, agent/instructions) to their live locations"]
|
|
351
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Copy agent/skills/<prefix>_* to custom/.devin/skills/ and agent/instructions to the app's src/instructions/, enforcing the manifest-declared skill prefix"]
|
|
352
|
+
// CIL[DOC-CONTEXT module="install-app" layer="cli" related_files=".framework/cli/commands/validate-app.ts,.framework/cli/lib/project-files.ts"]
|
|
353
|
+
// CIL[DOC-CONTRACT inputs="appDir (installed app dir), appSlug" outputs="void" errors="Throws when agent/skills exists without manifest.agent.skill_prefix, a dir violates <prefix>_ naming, or SKILL.md name mismatches its dir" side_effects="writes custom/.devin/skills and app src/instructions"]
|
|
354
|
+
// CIL[DOC-MICRO logic_summary="No-op without an agent/ dir; requires manifest.agent.skill_prefix /^[a-z][a-z0-9]{0,7}$/ when skills are staged; fails on cross-app prefix collisions by scanning installed manifests; copies instructions to src/instructions" notable_decisions="['fail_closed_on_missing_prefix','prefix_scoped_ownership']"]
|
|
355
|
+
// CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
|
|
356
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
357
|
+
function syncAppAgentAssets(appDir: string, appSlug: string): void {
|
|
358
|
+
const agentDir = join(appDir, 'agent')
|
|
359
|
+
if (!existsSync(agentDir)) return
|
|
360
|
+
|
|
361
|
+
const stagedSkills = join(agentDir, 'skills')
|
|
362
|
+
const stagedInstructions = join(agentDir, 'instructions')
|
|
363
|
+
const hasSkills = existsSync(stagedSkills)
|
|
364
|
+
const hasInstructions = existsSync(stagedInstructions)
|
|
365
|
+
if (!hasSkills && !hasInstructions) return
|
|
366
|
+
|
|
367
|
+
const manifest: Record<string, any> = JSON.parse(readFileSync(join(appDir, 'manifest.json'), 'utf8'))
|
|
368
|
+
const prefix = manifest.agent?.skill_prefix
|
|
369
|
+
|
|
370
|
+
if (hasSkills) {
|
|
371
|
+
if (typeof prefix !== 'string' || !AGENT_SKILL_PREFIX_RE.test(prefix)) {
|
|
372
|
+
throw new Error(`manifest.json agent.skill_prefix for "${appSlug}" is required (^[a-z][a-z0-9]{0,7}$) when the package ships agent/skills/`)
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
for (const other of readdirSync(join(PROJECT_ROOT, 'custom', 'apps'), { withFileTypes: true })) {
|
|
376
|
+
if (!other.isDirectory() || other.name === appSlug) continue
|
|
377
|
+
const otherManifest = join(PROJECT_ROOT, 'custom', 'apps', other.name, 'manifest.json')
|
|
378
|
+
try {
|
|
379
|
+
const m = JSON.parse(readFileSync(otherManifest, 'utf8'))
|
|
380
|
+
if (m.agent?.skill_prefix === prefix) {
|
|
381
|
+
throw new Error(`skill_prefix "${prefix}" is already used by app "${other.name}" — prefixes must be unique across installed apps`)
|
|
382
|
+
}
|
|
383
|
+
} catch (e: any) {
|
|
384
|
+
if (e.message?.includes('skill_prefix')) throw e
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const skillsDest = join(PROJECT_ROOT, 'custom', '.devin', 'skills')
|
|
389
|
+
for (const dir of readdirSync(stagedSkills, { withFileTypes: true })) {
|
|
390
|
+
if (!dir.isDirectory()) continue
|
|
391
|
+
if (!dir.name.startsWith(`${prefix}_`)) {
|
|
392
|
+
throw new Error(`agent/skills/${dir.name} does not match the declared prefix "${prefix}_"`)
|
|
393
|
+
}
|
|
394
|
+
const skillFile = join(stagedSkills, dir.name, 'SKILL.md')
|
|
395
|
+
if (!existsSync(skillFile)) {
|
|
396
|
+
throw new Error(`agent/skills/${dir.name} is missing SKILL.md`)
|
|
397
|
+
}
|
|
398
|
+
const nameMatch = /^name:\s*(.+)$/m.exec(readFileSync(skillFile, 'utf8'))
|
|
399
|
+
if (!nameMatch || nameMatch[1].trim() !== dir.name) {
|
|
400
|
+
throw new Error(`agent/skills/${dir.name}/SKILL.md name field must equal its directory name "${dir.name}"`)
|
|
401
|
+
}
|
|
402
|
+
cpSync(join(stagedSkills, dir.name), join(skillsDest, dir.name), { recursive: true })
|
|
403
|
+
console.log(` Skill installed: custom/.devin/skills/${dir.name}`)
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
if (hasInstructions) {
|
|
408
|
+
const instrDest = join(appDir, 'src', 'instructions')
|
|
409
|
+
mkdirSync(instrDest, { recursive: true })
|
|
410
|
+
cpSync(stagedInstructions, instrDest, { recursive: true })
|
|
411
|
+
console.log(` Instructions installed: custom/apps/${appSlug}/src/instructions/`)
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
// CIL[END stable_id="~/.framework/cli/commands/install-app.ts:syncappagentassets:function"]
|
|
415
|
+
|
|
416
|
+
// CIL[BEGIN stable_id="~/.framework/cli/commands/install-app.ts:removeappagentskills:function" type="function" identity="removeAppAgentSkills" parent="none"]
|
|
417
|
+
// CIL[DOC-MACRO purpose="Remove an app's prefix-scoped skills from custom/.devin/skills on uninstall"]
|
|
418
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Delete only the skill dirs owned by the uninstalled app's declared skill_prefix"]
|
|
419
|
+
// CIL[DOC-CONTEXT module="install-app" layer="cli" related_files=".framework/cli/commands/install-app.ts"]
|
|
420
|
+
// CIL[DOC-CONTRACT inputs="appDir, appSlug" outputs="void" errors="none" side_effects="removes custom/.devin/skills/<prefix>_* dirs"]
|
|
421
|
+
// CIL[DOC-MICRO logic_summary="Reads manifest.agent.skill_prefix from the app dir before deletion; globs <prefix>_* dirs and removes them; never touches unowned dirs" notable_decisions="['prefix_glob_scoped_removal']"]
|
|
422
|
+
// CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
|
|
423
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
424
|
+
function removeAppAgentSkills(appDir: string, appSlug: string): void {
|
|
425
|
+
try {
|
|
426
|
+
const manifest: Record<string, any> = JSON.parse(readFileSync(join(appDir, 'manifest.json'), 'utf8'))
|
|
427
|
+
const prefix = manifest.agent?.skill_prefix
|
|
428
|
+
if (typeof prefix !== 'string' || !prefix) return
|
|
429
|
+
const skillsDir = join(PROJECT_ROOT, 'custom', '.devin', 'skills')
|
|
430
|
+
if (!existsSync(skillsDir)) return
|
|
431
|
+
for (const dir of readdirSync(skillsDir, { withFileTypes: true })) {
|
|
432
|
+
if (dir.isDirectory() && dir.name.startsWith(`${prefix}_`)) {
|
|
433
|
+
rmSync(join(skillsDir, dir.name), { recursive: true, force: true })
|
|
434
|
+
console.log(` ✓ Removed skill custom/.devin/skills/${dir.name}`)
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
} catch {
|
|
438
|
+
console.log(` ○ No agent.skill_prefix for "${appSlug}" — no skills to remove`)
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
// CIL[END stable_id="~/.framework/cli/commands/install-app.ts:removeappagentskills:function"]
|
|
442
|
+
|
|
443
|
+
// CIL[BEGIN stable_id="~/.framework/cli/commands/install-app.ts:islocalpackagespec:function" type="function" identity="isLocalPackageSpec" parent="none"]
|
|
444
|
+
// CIL[DOC-MACRO purpose="Detect package specs that resolve to a local path instead of an npm registry name"]
|
|
445
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Return true for file:, ./, ../, / and *.tgz specs so commands can skip registry-only syntax like @latest"]
|
|
446
|
+
// CIL[DOC-CONTEXT module="install-app" layer="cli" related_files=".framework/cli/commands/install-app.ts"]
|
|
447
|
+
// CIL[DOC-CONTRACT inputs="packageName: string (the CLI <package> argument)" outputs="boolean" errors="none" side_effects="none"]
|
|
448
|
+
// CIL[DOC-MICRO logic_summary="Path-shaped specs (file:, dot-relative, absolute, .tgz) are local; everything else is a registry spec" notable_decisions="['local_specs_have_no_version_tag']"]
|
|
449
|
+
// CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
|
|
450
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
451
|
+
function isLocalPackageSpec(packageName: string): boolean {
|
|
452
|
+
return packageName.startsWith('file:')
|
|
453
|
+
|| packageName.startsWith('.')
|
|
454
|
+
|| packageName.startsWith('/')
|
|
455
|
+
|| packageName.endsWith('.tgz')
|
|
456
|
+
}
|
|
457
|
+
// CIL[END stable_id="~/.framework/cli/commands/install-app.ts:islocalpackagespec:function"]
|
|
458
|
+
|
|
459
|
+
// CIL[BEGIN stable_id="~/.framework/cli/commands/install-app.ts:resolvepackagedirname:function" type="function" identity="resolvePackageDirName" parent="none"]
|
|
460
|
+
// CIL[DOC-MACRO purpose="Resolve the node_modules directory name an install spec lands under"]
|
|
461
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Map install-app/update-app/uninstall-app <package> args — registry names (plain or scoped), tarball paths, and file:/path dirs — to the name npm uses for node_modules/<name>"]
|
|
462
|
+
// CIL[DOC-CONTEXT module="install-app" layer="cli" related_files=".framework/cli/commands/install-app.ts"]
|
|
463
|
+
// CIL[DOC-CONTRACT inputs="packageName: string" outputs="npm package name (may be scoped, e.g. @scope/name)" errors="throws if a tarball or directory spec has no readable package.json" side_effects="execSync tar for .tgz specs"]
|
|
464
|
+
// CIL[DOC-MICRO logic_summary="Tarballs yield their embedded package/package.json name via tar -xOf; file:/path and relative/absolute dirs read package.json directly; registry specs strip only the trailing @version so scoped names survive" notable_decisions="['tarball_name_comes_from_the_package_not_the_filename','split_on_last_at_preserves_scopes']"]
|
|
465
|
+
// CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
|
|
466
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
467
|
+
function resolvePackageDirName(packageName: string): string {
|
|
468
|
+
let spec = packageName
|
|
469
|
+
if (spec.startsWith('file:')) spec = spec.slice(5)
|
|
470
|
+
if (spec.endsWith('.tgz')) {
|
|
471
|
+
const json = execSync(`tar -xOf "${spec}" package/package.json`, { cwd: PROJECT_ROOT, encoding: 'utf8' })
|
|
472
|
+
return JSON.parse(json).name
|
|
473
|
+
}
|
|
474
|
+
if (spec.startsWith('.') || spec.startsWith('/')) {
|
|
475
|
+
return JSON.parse(readFileSync(join(PROJECT_ROOT, spec, 'package.json'), 'utf8')).name
|
|
476
|
+
}
|
|
477
|
+
return packageName.replace(/@[^@/]+$/, '')
|
|
478
|
+
}
|
|
479
|
+
// CIL[END stable_id="~/.framework/cli/commands/install-app.ts:resolvepackagedirname:function"]
|
|
480
|
+
|
|
322
481
|
// CIL[DOC-MACRO purpose="Command registration and installation handler for Spine Framework apps."]
|
|
323
482
|
// CIL[DOC-RESPONSIBILITY responsibility="Registers a command to install a Spine Framework app from npm and handles the installation process, including file copying and database migrations. This function ensures that the app is properly set up within the project environment."]
|
|
324
483
|
// CIL[DOC-CONTEXT module="install-app" layer="app-specific" related_files=".framework/cli/commands/migrate.js"]
|
|
@@ -371,7 +530,7 @@ export function registerInstallAppCommands(program: Command) {
|
|
|
371
530
|
console.log(' ✓ npm install complete')
|
|
372
531
|
|
|
373
532
|
|
|
374
|
-
const pkgSlug = packageName
|
|
533
|
+
const pkgSlug = resolvePackageDirName(packageName)
|
|
375
534
|
const pkgPath = join(PROJECT_ROOT, 'node_modules', pkgSlug)
|
|
376
535
|
|
|
377
536
|
if (!existsSync(pkgPath)) {
|
|
@@ -392,14 +551,9 @@ export function registerInstallAppCommands(program: Command) {
|
|
|
392
551
|
}
|
|
393
552
|
|
|
394
553
|
|
|
395
|
-
const allowedEntries = [
|
|
396
|
-
'index.tsx', 'manifest.json', 'package.json',
|
|
397
|
-
'pages', 'components', 'hooks', 'utils', 'functions', 'seed', 'public'
|
|
398
|
-
]
|
|
399
|
-
|
|
400
554
|
console.log(` Copying files from ${pkgPath} to ${appDest}`)
|
|
401
555
|
|
|
402
|
-
for (const entry of
|
|
556
|
+
for (const entry of APP_COPY_ENTRIES) {
|
|
403
557
|
const src = join(pkgPath, entry)
|
|
404
558
|
if (!existsSync(src)) continue
|
|
405
559
|
|
|
@@ -415,6 +569,9 @@ export function registerInstallAppCommands(program: Command) {
|
|
|
415
569
|
console.log(` ✓ App files copied to custom/apps/${appSlug}/`)
|
|
416
570
|
|
|
417
571
|
|
|
572
|
+
syncAppAgentAssets(appDest, appSlug)
|
|
573
|
+
|
|
574
|
+
|
|
418
575
|
console.log(' Running migrations...')
|
|
419
576
|
const migrateResult = await runMigrations({ frameworkOnly: false })
|
|
420
577
|
if (migrateResult === null) {
|
|
@@ -468,14 +625,14 @@ export function registerInstallAppCommands(program: Command) {
|
|
|
468
625
|
console.log(`\nUninstalling app: ${packageName}\n`)
|
|
469
626
|
loadEnv()
|
|
470
627
|
|
|
471
|
-
const pkgSlug = packageName
|
|
628
|
+
const pkgSlug = resolvePackageDirName(packageName)
|
|
472
629
|
const pkgPath = join(PROJECT_ROOT, 'node_modules', pkgSlug)
|
|
473
630
|
const appSlug = resolveAppSlug(pkgPath, pkgSlug)
|
|
474
631
|
const appDir = join(PROJECT_ROOT, 'custom/apps', appSlug)
|
|
475
632
|
|
|
476
633
|
try {
|
|
477
634
|
if (existsSync(appDir)) {
|
|
478
|
-
|
|
635
|
+
removeAppAgentSkills(appDir, appSlug)
|
|
479
636
|
rmSync(appDir, { recursive: true, force: true })
|
|
480
637
|
console.log(` ✓ Removed custom/apps/${appSlug}/`)
|
|
481
638
|
} else {
|
|
@@ -501,12 +658,12 @@ export function registerInstallAppCommands(program: Command) {
|
|
|
501
658
|
loadEnv()
|
|
502
659
|
|
|
503
660
|
try {
|
|
504
|
-
execSync(`npm install ${packageName}@latest --save`, {
|
|
661
|
+
execSync(`npm install ${isLocalPackageSpec(packageName) ? packageName : `${packageName}@latest`} --save`, {
|
|
505
662
|
cwd: PROJECT_ROOT,
|
|
506
663
|
stdio: 'inherit',
|
|
507
664
|
})
|
|
508
665
|
|
|
509
|
-
const pkgSlug = packageName
|
|
666
|
+
const pkgSlug = resolvePackageDirName(packageName)
|
|
510
667
|
const pkgPath = join(PROJECT_ROOT, 'node_modules', pkgSlug)
|
|
511
668
|
const appSlug = resolveAppSlug(pkgPath, pkgSlug)
|
|
512
669
|
const appDest = join(PROJECT_ROOT, 'custom/apps', appSlug)
|
|
@@ -515,13 +672,16 @@ export function registerInstallAppCommands(program: Command) {
|
|
|
515
672
|
mkdirSync(appDest, { recursive: true })
|
|
516
673
|
}
|
|
517
674
|
|
|
518
|
-
for (const entry of
|
|
675
|
+
for (const entry of APP_COPY_ENTRIES) {
|
|
519
676
|
const src = join(pkgPath, entry)
|
|
520
677
|
if (existsSync(src)) {
|
|
678
|
+
if (entry === 'manifest.json' && existsSync(join(appDest, 'manifest.json'))) continue
|
|
521
679
|
cpSync(src, join(appDest, entry), { recursive: true })
|
|
522
680
|
}
|
|
523
681
|
}
|
|
524
682
|
|
|
683
|
+
syncAppAgentAssets(appDest, appSlug)
|
|
684
|
+
|
|
525
685
|
|
|
526
686
|
const manifestPath = join(appDest, 'manifest.json')
|
|
527
687
|
let manifest: Record<string, any> = {}
|
|
@@ -1195,6 +1355,14 @@ function preflightAppInputs(appDir: string, appSlug: string, configPath: string)
|
|
|
1195
1355
|
if (manifest.config !== undefined && manifest.config !== null && !isPlainObject(manifest.config)) {
|
|
1196
1356
|
throw new Error(`manifest.json config for "${appSlug}" must be an object`)
|
|
1197
1357
|
}
|
|
1358
|
+
if (manifest.agent !== undefined && manifest.agent !== null) {
|
|
1359
|
+
if (!isPlainObject(manifest.agent)) {
|
|
1360
|
+
throw new Error(`manifest.json agent for "${appSlug}" must be an object`)
|
|
1361
|
+
}
|
|
1362
|
+
if (manifest.agent.skill_prefix !== undefined && !AGENT_SKILL_PREFIX_RE.test(manifest.agent.skill_prefix)) {
|
|
1363
|
+
throw new Error(`manifest.json agent.skill_prefix for "${appSlug}" must match ${AGENT_SKILL_PREFIX_RE}`)
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1198
1366
|
if (manifest.owned_types !== undefined) {
|
|
1199
1367
|
if (!Array.isArray(manifest.owned_types) || manifest.owned_types.some((s: any) => typeof s !== 'string' || !s.trim())) {
|
|
1200
1368
|
throw new Error(`manifest.json owned_types for "${appSlug}" must be an array of non-empty strings`)
|
|
@@ -1483,6 +1651,8 @@ function appRowPayload(manifest: Record<string, any>, appSlug: string, existingC
|
|
|
1483
1651
|
min_role: manifest.required_roles?.[0] ?? manifest.min_role ?? null,
|
|
1484
1652
|
preview_domain: manifest.preview_domain ?? null,
|
|
1485
1653
|
prod_domain: manifest.prod_domain ?? null,
|
|
1654
|
+
preview_route_prefix: manifest.preview_route_prefix ?? null,
|
|
1655
|
+
prod_route_prefix: manifest.prod_route_prefix ?? null,
|
|
1486
1656
|
config: mergedConfig,
|
|
1487
1657
|
metadata: manifest.metadata ?? undefined,
|
|
1488
1658
|
}
|
|
@@ -22,15 +22,14 @@ import type { Command } from 'commander'
|
|
|
22
22
|
import { existsSync, readdirSync, readFileSync } from 'fs'
|
|
23
23
|
import { resolve, dirname, join } from 'path'
|
|
24
24
|
import { fileURLToPath } from 'url'
|
|
25
|
+
// CIL[END stable_id="~/.framework/cli/commands/migrate.ts:module_header:module_header"]
|
|
25
26
|
// CIL[DOC-VECTOR type="context" content="purpose='Provides the migrate command for applying database migrations to a Supabase project.'; No verified external callers; imports: none"]
|
|
26
27
|
// CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
|
|
27
28
|
// CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
|
|
28
29
|
// CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
|
|
29
30
|
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
30
31
|
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
31
|
-
// CIL[END stable_id="~/.framework/cli/commands/migrate.ts:module_header:module_header"]
|
|
32
32
|
|
|
33
|
-
// CIL[BEGIN stable_id="~/.framework/cli/commands/migrate.ts:__filename:constant" type="constant" identity="__filename" parent="~/.framework/cli/commands/migrate.ts"]
|
|
34
33
|
// CIL[DOC-MACRO purpose="Defines the current module file path."]
|
|
35
34
|
// CIL[DOC-RESPONSIBILITY responsibility="Derives __filename from the module import meta URL."]
|
|
36
35
|
// CIL[DOC-CONTEXT module="cli" layer="framework" related_files=".framework/cli/commands/migrate.ts"]
|
|
@@ -39,6 +38,7 @@ import { fileURLToPath } from 'url'
|
|
|
39
38
|
// CIL[DOC-CONTRACT inputs="none" outputs="none" errors="none" side_effects="none"]
|
|
40
39
|
// CIL[DOC-MICRO logic_summary="Converts import.meta.url to a file path and assigns to __filename." notable_decisions="none"]
|
|
41
40
|
// CIL[DOC-AUDIENCE audience="engineers"]
|
|
41
|
+
// CIL[BEGIN stable_id="~/.framework/cli/commands/migrate.ts:__filename:constant" type="constant" identity="__filename" parent="~/.framework/cli/commands/migrate.ts"]
|
|
42
42
|
const __filename = fileURLToPath(import.meta.url)
|
|
43
43
|
// CIL[END stable_id="~/.framework/cli/commands/migrate.ts:__filename:constant"]
|
|
44
44
|
// CIL[DOC-CONTEXT module="unknown" layer="unknown" related_files=".framework/cli/commands/migrate.ts"]
|
|
@@ -48,7 +48,6 @@ const __filename = fileURLToPath(import.meta.url)
|
|
|
48
48
|
// CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
|
|
49
49
|
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
50
50
|
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
51
|
-
// CIL[BEGIN stable_id="~/.framework/cli/commands/migrate.ts:__dirname:constant" type="constant" identity="__dirname" parent="~/.framework/cli/commands/migrate.ts"]
|
|
52
51
|
// CIL[DOC-MACRO purpose="Defines the current module directory."]
|
|
53
52
|
// CIL[DOC-RESPONSIBILITY responsibility="Derives __dirname from __filename."]
|
|
54
53
|
// CIL[DOC-CONTEXT module="cli" layer="framework" related_files=".framework/cli/commands/migrate.ts"]
|
|
@@ -57,9 +56,9 @@ const __filename = fileURLToPath(import.meta.url)
|
|
|
57
56
|
// CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
|
|
58
57
|
// CIL[DOC-MICRO logic_summary="Computes the directory of the current file and assigns to __dirname." notable_decisions="none"]
|
|
59
58
|
// CIL[DOC-AUDIENCE audience="engineers"]
|
|
59
|
+
// CIL[BEGIN stable_id="~/.framework/cli/commands/migrate.ts:__dirname:constant" type="constant" identity="__dirname" parent="~/.framework/cli/commands/migrate.ts"]
|
|
60
60
|
const __dirname = dirname(__filename)
|
|
61
61
|
// CIL[END stable_id="~/.framework/cli/commands/migrate.ts:__dirname:constant"]
|
|
62
|
-
// CIL[BEGIN stable_id="~/.framework/cli/commands/migrate.ts:PACKAGE_ROOT:constant" type="constant" identity="PACKAGE_ROOT" parent="~/.framework/cli/commands/migrate.ts"]
|
|
63
62
|
// CIL[DOC-VECTOR type="context" content="purpose='Defines the current module directory.'; No verified external callers; imports: none"]
|
|
64
63
|
// CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
|
|
65
64
|
// CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
|
|
@@ -74,6 +73,7 @@ const __dirname = dirname(__filename)
|
|
|
74
73
|
// CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
|
|
75
74
|
// CIL[DOC-STABILITY stability="stable"]
|
|
76
75
|
// CIL[DOC-AUDIENCE audience="engineers"]
|
|
76
|
+
// CIL[BEGIN stable_id="~/.framework/cli/commands/migrate.ts:PACKAGE_ROOT:constant" type="constant" identity="PACKAGE_ROOT" parent="~/.framework/cli/commands/migrate.ts"]
|
|
77
77
|
const PACKAGE_ROOT = resolve(__dirname, '../../../')
|
|
78
78
|
// CIL[END stable_id="~/.framework/cli/commands/migrate.ts:PACKAGE_ROOT:constant"]
|
|
79
79
|
// CIL[DOC-VECTOR type="context" content="purpose='Defines the package root path.'; No verified external callers; imports: none"]
|
|
@@ -82,7 +82,6 @@ const PACKAGE_ROOT = resolve(__dirname, '../../../')
|
|
|
82
82
|
// CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
|
|
83
83
|
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
84
84
|
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
85
|
-
// CIL[BEGIN stable_id="~/.framework/cli/commands/migrate.ts:PROJECT_ROOT:constant" type="constant" identity="PROJECT_ROOT" parent="~/.framework/cli/commands/migrate.ts"]
|
|
86
85
|
// CIL[DOC-MACRO purpose="Defines the project root path."]
|
|
87
86
|
// CIL[DOC-RESPONSIBILITY responsibility="Uses the current working directory as the project root."]
|
|
88
87
|
// CIL[DOC-CONTEXT module="cli" layer="framework" related_files=".framework/cli/commands/migrate.ts"]
|
|
@@ -91,17 +90,17 @@ const PACKAGE_ROOT = resolve(__dirname, '../../../')
|
|
|
91
90
|
// CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
|
|
92
91
|
// CIL[DOC-STABILITY stability="stable"]
|
|
93
92
|
// CIL[DOC-AUDIENCE audience="engineers"]
|
|
93
|
+
// CIL[BEGIN stable_id="~/.framework/cli/commands/migrate.ts:PROJECT_ROOT:constant" type="constant" identity="PROJECT_ROOT" parent="~/.framework/cli/commands/migrate.ts"]
|
|
94
94
|
const PROJECT_ROOT = process.cwd()
|
|
95
|
+
// CIL[END stable_id="~/.framework/cli/commands/migrate.ts:PROJECT_ROOT:constant"]
|
|
95
96
|
// CIL[DOC-VECTOR type="context" content="purpose='Defines the project root path.'; No verified external callers; imports: none"]
|
|
96
97
|
// CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
|
|
97
98
|
// CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
|
|
98
99
|
// CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
|
|
99
100
|
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
100
101
|
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
101
|
-
// CIL[END stable_id="~/.framework/cli/commands/migrate.ts:PROJECT_ROOT:constant"]
|
|
102
102
|
|
|
103
103
|
|
|
104
|
-
// CIL[BEGIN stable_id="~/.framework/cli/commands/migrate.ts:MigrateOptions:interface" type="interface" identity="MigrateOptions" parent="~/.framework/cli/commands/migrate.ts"]
|
|
105
104
|
// CIL[DOC-MACRO purpose="Declares the migrate command options."]
|
|
106
105
|
// CIL[DOC-RESPONSIBILITY responsibility="Types the CLI flags and optional db password."]
|
|
107
106
|
// CIL[DOC-CONTEXT module="cli" layer="framework" related_files=".framework/cli/commands/migrate.ts"]
|
|
@@ -110,20 +109,20 @@ const PROJECT_ROOT = process.cwd()
|
|
|
110
109
|
// CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
|
|
111
110
|
// CIL[DOC-STABILITY stability="stable"]
|
|
112
111
|
// CIL[DOC-AUDIENCE audience="engineers"]
|
|
112
|
+
// CIL[BEGIN stable_id="~/.framework/cli/commands/migrate.ts:MigrateOptions:interface" type="interface" identity="MigrateOptions" parent="~/.framework/cli/commands/migrate.ts"]
|
|
113
113
|
interface MigrateOptions {
|
|
114
114
|
dbPassword?: string
|
|
115
115
|
dryRun: boolean
|
|
116
116
|
frameworkOnly: boolean
|
|
117
117
|
}
|
|
118
|
+
// CIL[END stable_id="~/.framework/cli/commands/migrate.ts:MigrateOptions:interface"]
|
|
118
119
|
// CIL[DOC-VECTOR type="context" content="purpose='Declares the migrate command options.'; No verified external callers; imports: none"]
|
|
119
120
|
// CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
|
|
120
121
|
// CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
|
|
121
122
|
// CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
|
|
122
123
|
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
123
124
|
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
124
|
-
// CIL[END stable_id="~/.framework/cli/commands/migrate.ts:MigrateOptions:interface"]
|
|
125
125
|
|
|
126
|
-
// CIL[BEGIN stable_id="~/.framework/cli/commands/migrate.ts:runMigrations:function" type="function" identity="runMigrations" parent="~/.framework/cli/commands/migrate.ts"]
|
|
127
126
|
// CIL[DOC-MACRO purpose="Applies pending SQL migration files to the Supabase database and tracks which have been applied."]
|
|
128
127
|
// CIL[DOC-RESPONSIBILITY responsibility="Executes migrations safely in transactions and records them in the tracking table."]
|
|
129
128
|
// CIL[DOC-CONTEXT module="cli" layer="framework" related_files=".framework/cli/commands/migrate.ts"]
|
|
@@ -132,6 +131,7 @@ interface MigrateOptions {
|
|
|
132
131
|
// CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
|
|
133
132
|
// CIL[DOC-STABILITY stability="stable"]
|
|
134
133
|
// CIL[DOC-AUDIENCE audience="engineers"]
|
|
134
|
+
// CIL[BEGIN stable_id="~/.framework/cli/commands/migrate.ts:runMigrations:function" type="function" identity="runMigrations" parent="~/.framework/cli/commands/migrate.ts"]
|
|
135
135
|
export async function runMigrations(opts: { frameworkOnly?: boolean } = {}): Promise<{ applied: number; skipped: number; failed: number } | null> {
|
|
136
136
|
const supabaseUrl = process.env.SUPABASE_URL
|
|
137
137
|
if (!supabaseUrl || supabaseUrl.includes('placeholder') || supabaseUrl.includes('your-project')) {
|
|
@@ -139,7 +139,7 @@ export async function runMigrations(opts: { frameworkOnly?: boolean } = {}): Pro
|
|
|
139
139
|
}
|
|
140
140
|
|
|
141
141
|
const dbPassword = process.env.SUPABASE_DB_PASSWORD
|
|
142
|
-
if (!dbPassword) {
|
|
142
|
+
if (!dbPassword && !process.env.SUPABASE_DB_URL) {
|
|
143
143
|
return null
|
|
144
144
|
}
|
|
145
145
|
|
|
@@ -167,13 +167,17 @@ export async function runMigrations(opts: { frameworkOnly?: boolean } = {}): Pro
|
|
|
167
167
|
if (migrationFiles.length === 0) return { applied: 0, skipped: 0, failed: 0 }
|
|
168
168
|
|
|
169
169
|
const { default: pg } = await import('pg')
|
|
170
|
-
const client = new pg.Client(
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
170
|
+
const client = new pg.Client(
|
|
171
|
+
process.env.SUPABASE_DB_URL
|
|
172
|
+
? { connectionString: process.env.SUPABASE_DB_URL }
|
|
173
|
+
: {
|
|
174
|
+
user: 'postgres',
|
|
175
|
+
password: dbPassword,
|
|
176
|
+
host: `db.${projectRef}.supabase.co`,
|
|
177
|
+
port: 5432,
|
|
178
|
+
database: 'postgres',
|
|
179
|
+
}
|
|
180
|
+
)
|
|
177
181
|
await client.connect()
|
|
178
182
|
|
|
179
183
|
let applied = 0, skipped = 0, failed = 0
|
|
@@ -224,15 +228,14 @@ export async function runMigrations(opts: { frameworkOnly?: boolean } = {}): Pro
|
|
|
224
228
|
|
|
225
229
|
return { applied, skipped, failed }
|
|
226
230
|
}
|
|
231
|
+
// CIL[END stable_id="~/.framework/cli/commands/migrate.ts:runMigrations:function"]
|
|
227
232
|
// CIL[DOC-VECTOR type="context" content="purpose='Applies pending SQL migration files to the Supabase database and tracks which have been applied.'; No verified external callers; imports: none"]
|
|
228
233
|
// CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
|
|
229
234
|
// CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
|
|
230
235
|
// CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
|
|
231
236
|
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
232
237
|
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
233
|
-
// CIL[END stable_id="~/.framework/cli/commands/migrate.ts:runMigrations:function"]
|
|
234
238
|
|
|
235
|
-
// CIL[BEGIN stable_id="~/.framework/cli/commands/migrate.ts:migrateCommand:function" type="function" identity="migrateCommand" parent="~/.framework/cli/commands/migrate.ts"]
|
|
236
239
|
// CIL[DOC-MACRO purpose="CLI action that collects, optionally dry-runs, and applies framework migrations."]
|
|
237
240
|
// CIL[DOC-RESPONSIBILITY responsibility="Validates environment, loads .env, handles dry run, and prints result."]
|
|
238
241
|
// CIL[DOC-CONTEXT module="cli" layer="framework" related_files=".framework/cli/commands/migrate.ts"]
|
|
@@ -241,6 +244,7 @@ export async function runMigrations(opts: { frameworkOnly?: boolean } = {}): Pro
|
|
|
241
244
|
// CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
|
|
242
245
|
// CIL[DOC-STABILITY stability="stable"]
|
|
243
246
|
// CIL[DOC-AUDIENCE audience="engineers"]
|
|
247
|
+
// CIL[BEGIN stable_id="~/.framework/cli/commands/migrate.ts:migrateCommand:function" type="function" identity="migrateCommand" parent="~/.framework/cli/commands/migrate.ts"]
|
|
244
248
|
async function migrateCommand(options: MigrateOptions): Promise<void> {
|
|
245
249
|
console.log('\nRunning Spine Framework migrations...\n')
|
|
246
250
|
|
|
@@ -265,13 +269,14 @@ async function migrateCommand(options: MigrateOptions): Promise<void> {
|
|
|
265
269
|
}
|
|
266
270
|
|
|
267
271
|
const dbPassword = options.dbPassword || process.env.SUPABASE_DB_PASSWORD
|
|
268
|
-
if (!dbPassword) {
|
|
272
|
+
if (!dbPassword && !process.env.SUPABASE_DB_URL) {
|
|
269
273
|
console.error('❌ SUPABASE_DB_PASSWORD is not set.\n')
|
|
270
274
|
if (!envExists) {
|
|
271
275
|
console.error(' No .env file found. Run: npx spine-framework init')
|
|
272
276
|
} else {
|
|
273
277
|
console.error(' Edit .env and add: SUPABASE_DB_PASSWORD=your-db-password')
|
|
274
278
|
console.error(' Or pass it directly: npx spine-framework migrate --db-password <password>')
|
|
279
|
+
console.error(' Or a full connection string (pooler/IPv4): SUPABASE_DB_URL=postgres://postgres.<ref>:<pw>@aws-0-<region>.pooler.supabase.com:5432/postgres')
|
|
275
280
|
}
|
|
276
281
|
console.error('\n Get it from: https://supabase.com/dashboard/project/_/settings/database')
|
|
277
282
|
process.exit(1)
|
|
@@ -313,12 +318,12 @@ async function migrateCommand(options: MigrateOptions): Promise<void> {
|
|
|
313
318
|
|
|
314
319
|
console.log('\n✅ All migrations applied successfully!\n')
|
|
315
320
|
console.log('Next steps:')
|
|
316
|
-
console.log('
|
|
317
|
-
console.log('
|
|
321
|
+
console.log(' npm install && npm run dev:netlify')
|
|
322
|
+
console.log(' (optional) npx spine-framework install-app <app-package> — only for')
|
|
323
|
+
console.log(' additional app packs; core/admin apps are already seeded\n')
|
|
318
324
|
}
|
|
319
325
|
// CIL[END stable_id="~/.framework/cli/commands/migrate.ts:migrateCommand:function"]
|
|
320
326
|
|
|
321
|
-
// CIL[BEGIN stable_id="~/.framework/cli/commands/migrate.ts:registerMigrateCommands:function" type="function" identity="registerMigrateCommands" parent="~/.framework/cli/commands/migrate.ts"]
|
|
322
327
|
// CIL[DOC-VECTOR type="context" content="purpose='CLI action that collects, optionally dry-runs, and applies framework migrations.'; No verified external callers; imports: none"]
|
|
323
328
|
// CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
|
|
324
329
|
// CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
|
|
@@ -333,6 +338,7 @@ async function migrateCommand(options: MigrateOptions): Promise<void> {
|
|
|
333
338
|
// CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
|
|
334
339
|
// CIL[DOC-STABILITY stability="stable"]
|
|
335
340
|
// CIL[DOC-AUDIENCE audience="engineers"]
|
|
341
|
+
// CIL[BEGIN stable_id="~/.framework/cli/commands/migrate.ts:registerMigrateCommands:function" type="function" identity="registerMigrateCommands" parent="~/.framework/cli/commands/migrate.ts"]
|
|
336
342
|
export function registerMigrateCommands(program: Command) {
|
|
337
343
|
program
|
|
338
344
|
.command('migrate')
|
|
@@ -350,15 +356,14 @@ export function registerMigrateCommands(program: Command) {
|
|
|
350
356
|
}
|
|
351
357
|
})
|
|
352
358
|
}
|
|
359
|
+
// CIL[END stable_id="~/.framework/cli/commands/migrate.ts:registerMigrateCommands:function"]
|
|
353
360
|
// CIL[DOC-VECTOR type="context" content="purpose='Registers the migrate command with the Commander program.'; No verified external callers; imports: none"]
|
|
354
361
|
// CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
|
|
355
362
|
// CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
|
|
356
363
|
// CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
|
|
357
364
|
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
358
365
|
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
359
|
-
// CIL[END stable_id="~/.framework/cli/commands/migrate.ts:registerMigrateCommands:function"]
|
|
360
366
|
|
|
361
|
-
// CIL[BEGIN stable_id="~/.framework/cli/commands/migrate.ts:loadEnv:function" type="function" identity="loadEnv" parent="~/.framework/cli/commands/migrate.ts"]
|
|
362
367
|
// CIL[DOC-MACRO purpose="Loads key=value pairs from the project .env file into process.env."]
|
|
363
368
|
// CIL[DOC-RESPONSIBILITY responsibility="Reads .env without overwriting existing environment variables."]
|
|
364
369
|
// CIL[DOC-CONTEXT module="cli" layer="framework" related_files=".framework/cli/commands/migrate.ts"]
|
|
@@ -367,6 +372,7 @@ export function registerMigrateCommands(program: Command) {
|
|
|
367
372
|
// CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
|
|
368
373
|
// CIL[DOC-STABILITY stability="stable"]
|
|
369
374
|
// CIL[DOC-AUDIENCE audience="engineers"]
|
|
375
|
+
// CIL[BEGIN stable_id="~/.framework/cli/commands/migrate.ts:loadEnv:function" type="function" identity="loadEnv" parent="~/.framework/cli/commands/migrate.ts"]
|
|
370
376
|
function loadEnv() {
|
|
371
377
|
const envPath = join(PROJECT_ROOT, '.env')
|
|
372
378
|
if (!existsSync(envPath)) return
|
|
@@ -381,15 +387,14 @@ function loadEnv() {
|
|
|
381
387
|
if (key && !(key in process.env)) process.env[key] = val
|
|
382
388
|
}
|
|
383
389
|
}
|
|
390
|
+
// CIL[END stable_id="~/.framework/cli/commands/migrate.ts:loadEnv:function"]
|
|
384
391
|
// CIL[DOC-VECTOR type="context" content="purpose='Loads key=value pairs from the project .env file into process.env.'; No verified external callers; imports: none"]
|
|
385
392
|
// CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
|
|
386
393
|
// CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
|
|
387
394
|
// CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
|
|
388
395
|
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
389
396
|
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
390
|
-
// CIL[END stable_id="~/.framework/cli/commands/migrate.ts:loadEnv:function"]
|
|
391
397
|
|
|
392
|
-
// CIL[BEGIN stable_id="~/.framework/cli/commands/migrate.ts:collectMigrations:function" type="function" identity="collectMigrations" parent="~/.framework/cli/commands/migrate.ts"]
|
|
393
398
|
// CIL[DOC-MACRO purpose="Collects SQL migration files from framework and custom directories."]
|
|
394
399
|
// CIL[DOC-RESPONSIBILITY responsibility="Sorts and returns file paths for pending migrations, optionally including custom migrations."]
|
|
395
400
|
// CIL[DOC-CONTEXT module="cli" layer="framework" related_files=".framework/cli/commands/migrate.ts"]
|
|
@@ -398,6 +403,7 @@ function loadEnv() {
|
|
|
398
403
|
// CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
|
|
399
404
|
// CIL[DOC-STABILITY stability="stable"]
|
|
400
405
|
// CIL[DOC-AUDIENCE audience="engineers"]
|
|
406
|
+
// CIL[BEGIN stable_id="~/.framework/cli/commands/migrate.ts:collectMigrations:function" type="function" identity="collectMigrations" parent="~/.framework/cli/commands/migrate.ts"]
|
|
401
407
|
export function collectMigrations(frameworkOnly: boolean): { name: string; path: string }[] {
|
|
402
408
|
const files: { name: string; path: string }[] = []
|
|
403
409
|
|
|
@@ -426,15 +432,14 @@ export function collectMigrations(frameworkOnly: boolean): { name: string; path:
|
|
|
426
432
|
|
|
427
433
|
return files
|
|
428
434
|
}
|
|
435
|
+
// CIL[END stable_id="~/.framework/cli/commands/migrate.ts:collectMigrations:function"]
|
|
429
436
|
// CIL[DOC-VECTOR type="context" content="purpose='Collects SQL migration files from framework and custom directories.'; No verified external callers; imports: none"]
|
|
430
437
|
// CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
|
|
431
438
|
// CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
|
|
432
439
|
// CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
|
|
433
440
|
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
434
441
|
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
435
|
-
// CIL[END stable_id="~/.framework/cli/commands/migrate.ts:collectMigrations:function"]
|
|
436
442
|
|
|
437
|
-
// CIL[BEGIN stable_id="~/.framework/cli/commands/migrate.ts:splitSqlStatements:function" type="function" identity="splitSqlStatements" parent="~/.framework/cli/commands/migrate.ts"]
|
|
438
443
|
// CIL[DOC-MACRO purpose="Splits SQL into executable statements while preserving dollar-quoted blocks."]
|
|
439
444
|
// CIL[DOC-RESPONSIBILITY responsibility="Removes single-line comments, splits on semicolons, restores protected dollar-quoted blocks, and returns trimmed statements."]
|
|
440
445
|
// CIL[DOC-CONTEXT module="cli" layer="framework" related_files=".framework/cli/commands/migrate.ts"]
|
|
@@ -443,6 +448,7 @@ export function collectMigrations(frameworkOnly: boolean): { name: string; path:
|
|
|
443
448
|
// CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
|
|
444
449
|
// CIL[DOC-STABILITY stability="stable"]
|
|
445
450
|
// CIL[DOC-AUDIENCE audience="engineers"]
|
|
451
|
+
// CIL[BEGIN stable_id="~/.framework/cli/commands/migrate.ts:splitSqlStatements:function" type="function" identity="splitSqlStatements" parent="~/.framework/cli/commands/migrate.ts"]
|
|
446
452
|
export function splitSqlStatements(sql: string): string[] {
|
|
447
453
|
// Single-pass scanner: a ; only ends a statement in normal state. Comments
|
|
448
454
|
// are dropped; string literals, quoted identifiers, and dollar-quoted blocks
|
|
@@ -497,10 +503,10 @@ export function splitSqlStatements(sql: string): string[] {
|
|
|
497
503
|
if (tail) statements.push(tail + ';')
|
|
498
504
|
return statements
|
|
499
505
|
}
|
|
506
|
+
// CIL[END stable_id="~/.framework/cli/commands/migrate.ts:splitSqlStatements:function"]
|
|
500
507
|
// CIL[DOC-VECTOR type="context" content="purpose='Splits SQL into executable statements while preserving dollar-quoted blocks.'; No verified external callers; imports: none"]
|
|
501
508
|
// CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
|
|
502
509
|
// CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
|
|
503
510
|
// CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
|
|
504
511
|
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
505
512
|
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
506
|
-
// CIL[END stable_id="~/.framework/cli/commands/migrate.ts:splitSqlStatements:function"]
|