diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index a5e5866..429ace2 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -21,6 +21,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). `newer_db` / `unstamped` / `no_schema`) is unchanged. ### Changed +- The published `atomicmemory-core migrate` command now calls the + programmatic migration API directly, so npm installs can run the documented + migration command without the command word being reparsed as a migration + option. - **BREAKING**: All API endpoints are now mounted under `/v1/` (e.g. `POST /v1/memories/ingest`, `PUT /v1/agents/trust`). Update clients to prefix requests with `/v1`. The unversioned `/health` liveness probe is unchanged. - Phase 2 removes `src/db/schema.sql`; the migrations folder is now the single source of truth. The build-time `dist/db/schema-sha256.json` diff --git a/packages/core/package.json b/packages/core/package.json index 6c17622..605f7ef 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@atomicmemory/core", - "version": "1.0.5", + "version": "1.0.6", "description": "Open-source memory engine for AI applications — semantic retrieval, AUDN mutation, and contradiction-safe claim versioning.", "type": "module", "license": "Apache-2.0", diff --git a/packages/core/src/__tests__/bin.test.ts b/packages/core/src/__tests__/bin.test.ts new file mode 100644 index 0000000..23412cc --- /dev/null +++ b/packages/core/src/__tests__/bin.test.ts @@ -0,0 +1,63 @@ +/** + * Tests for the published `atomicmemory-core` npm binary entry point. + * + * These cover command dispatch only; migration behavior itself lives in the + * migration API test suite. The key regression guard is that `migrate` must + * call the programmatic API directly instead of importing the standalone + * migration CLI, which reparses the original process argv. + */ + +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +const mocks = vi.hoisted(() => ({ + migrate: vi.fn(), +})); + +vi.mock('../db/migration-api.js', () => ({ + migrate: mocks.migrate, +})); + +vi.mock('../db/migrate.js', () => { + throw new Error('standalone migration CLI must not be imported by bin.ts'); +}); + +import { parseCliArgs, runCommand } from '../bin.js'; + +describe('core CLI entry point', () => { + beforeEach(() => { + vi.clearAllMocks(); + vi.spyOn(console, 'log').mockImplementation(() => {}); + mocks.migrate.mockResolvedValue({ + ranSchemaSql: true, + schemaVersion: { + sdkVersion: '1.0.6', + schemaSha256: 'abcdef1234567890', + appliedAt: new Date('2026-05-20T00:00:00.000Z'), + notes: null, + }, + reconciledEmbeddingDimension: false, + }); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + it('parses the local migrate command without passing command text onward', () => { + expect(parseCliArgs(['migrate', '--profile', 'local'])).toEqual({ + command: 'migrate', + help: false, + profile: 'local', + }); + }); + + it('runs migrations through the programmatic API', async () => { + await runCommand('migrate'); + + expect(mocks.migrate).toHaveBeenCalledOnce(); + expect(mocks.migrate).toHaveBeenCalledWith(); + expect(console.log).toHaveBeenCalledWith( + '[migrate] Migration complete (ranSchemaSql=true, version=1.0.6, sha=abcdef123456..., reconciledEmbeddingDimension=false).', + ); + }); +}); diff --git a/packages/core/src/bin.ts b/packages/core/src/bin.ts index 4573ccf..20c697c 100644 --- a/packages/core/src/bin.ts +++ b/packages/core/src/bin.ts @@ -107,12 +107,20 @@ function assertLocalProfileReady(): void { ); } -async function runCommand(command: CommandName): Promise { +export async function runCommand(command: CommandName): Promise { if (command === 'start') { await import('./server.js'); return; } - await import('./db/migrate.js'); + const { migrate } = await import('./db/migration-api.js'); + const result = await migrate(); + console.log( + `[migrate] Migration complete ` + + `(ranSchemaSql=${result.ranSchemaSql}, ` + + `version=${result.schemaVersion.sdkVersion}, ` + + `sha=${result.schemaVersion.schemaSha256.slice(0, 12)}..., ` + + `reconciledEmbeddingDimension=${result.reconciledEmbeddingDimension}).`, + ); } async function main(argv: string[]): Promise {