/**
 * MCP server `release-tools` — exposes M17 release operations to agents
 * (chat, external MCP clients). Mirrors `m17mcp001`: 5 tools — create, list,
 * show, diff, update. `release_restore` is intentionally absent (decyzja 9 +
 * `m17open01` open #3 — restore is human-initiated only).
 */
import { createMcpServer, mcpTool } from '@inharness/agent-adapters';
import { z } from 'zod';
import { DomainError } from '../services/tags.js';
export function createReleaseToolsServer(deps) {
    const ok = (payload) => ({
        content: [{ type: 'text', text: JSON.stringify(payload) }],
    });
    const fail = (err) => {
        const code = err instanceof DomainError ? err.code : 'INTERNAL';
        const message = err instanceof Error ? err.message : String(err);
        return {
            content: [{ type: 'text', text: JSON.stringify({ error: message, code }) }],
            isError: true,
        };
    };
    const releaseCreate = mcpTool('release_create', 'Create a named release (snapshot of current spec state). Assigns release_id to all unreleased entity_version + page_version rows in one transaction. Always manual — there are no auto-triggers (M17 decyzja 9). Both name (UNIQUE) and description (non-empty) are required.', {
        name: z.string().describe('Release name, must be unique. e.g. "v1.0.0", "pre-launch"'),
        description: z.string().describe('Non-empty intent of the release — surfaced to M18 brief-builder'),
    }, async (args) => {
        try {
            const release = deps.releaseService.createRelease({ name: String(args.name), description: String(args.description) }, 'agent');
            deps.ws.broadcast({ kind: 'release:created', releaseId: release.id, name: release.name });
            return ok(release);
        }
        catch (err) {
            return fail(err);
        }
    });
    const releaseList = mcpTool('release_list', 'List all releases newest-first. Returns id, name, description, createdBy, createdAt per release.', {}, async () => ok({ releases: deps.releaseService.listReleases() }));
    const releaseShow = mcpTool('release_show', 'Show release detail with count breakdown (entities per type + pages). Accepts numeric id or release name.', { idOrName: z.union([z.string(), z.number()]) }, async (args) => {
        try {
            return ok(deps.releaseService.getRelease(args.idOrName));
        }
        catch (err) {
            return fail(err);
        }
    });
    const releaseDiff = mcpTool('release_diff', 'Compute structured RawDelta between two releases. Per-entity uses plugin-owned host.diff (semantic); pages use section-level B variant. M21 brief-author consumes this as input. Pass `from: null` for initial brief (compares `to` against synthetic empty state, all entries become op:create).', {
        from: z.union([z.string(), z.number(), z.null()]).describe('Earlier release id or name. `null` = initial brief (compare to empty state).'),
        to: z.union([z.string(), z.number()]).describe('Later release id or name'),
    }, async (args) => {
        try {
            return ok(deps.releaseService.getReleaseDiff(args.from, args.to));
        }
        catch (err) {
            return fail(err);
        }
    });
    const releaseUpdate = mcpTool('release_update', 'Update the LATEST release only — older releases are frozen. Mutates name/description in-place and optionally pulls all unreleased entity_version + page_version rows (release_id IS NULL) into this release. 409 RELEASE_FROZEN if id != MAX(id). 409 RELEASE_NAME_CONFLICT on rename collision.', {
        idOrName: z.union([z.string(), z.number()]).describe('Numeric id or release name'),
        name: z.string().optional().describe('New name (must be unique). Omit to leave unchanged.'),
        description: z.string().optional().describe('New description (non-empty). Omit to leave unchanged.'),
        assignUnreleased: z
            .boolean()
            .optional()
            .describe('When true, assigns all entity_version/page_version rows where release_id IS NULL to this release. No-op when queue is empty.'),
    }, async (args) => {
        try {
            const release = deps.releaseService.updateRelease({
                idOrName: args.idOrName,
                name: args.name,
                description: args.description,
                assignUnreleased: args.assignUnreleased,
            });
            deps.ws.broadcast({ kind: 'release:updated', releaseId: release.id, name: release.name });
            return ok(release);
        }
        catch (err) {
            return fail(err);
        }
    });
    return createMcpServer({
        name: 'release-tools',
        tools: [releaseCreate, releaseList, releaseShow, releaseDiff, releaseUpdate],
    });
}
//# sourceMappingURL=release-tools.js.map