// tools/bump-spec-version.ts — MCP tool handler for bump_spec_version (SPEC-717)
//
// Bumps the SemVer spec_version field in a spec's frontmatter and appends a
// history entry. Uses atomic writes (tmp → rename). Does NOT invoke update_status.
import { z } from 'zod';
import { getSpec } from '../storage/spec-store.js';
import { safeTracked } from './safe-handler.js';
import { bumpSpecVersion } from '../engine/spec-versioning/bump-version.js';
import { resolveProjectId } from './resolve-project-id.js';
import { SpecIdSchema } from './schemas/index.js';
// SPEC-747: Grant unlock token after successful bump
import { grantUnlock } from '../engine/freeze/unlock-token.js';
// ---------------------------------------------------------------------------
// Zod schema
// ---------------------------------------------------------------------------
const BumpSpecVersionSchema = {
    projectPath: z
        .string()
        .min(1)
        .max(4096)
        .optional()
        .describe('Absolute path to the project root. Takes precedence over projectId.'),
    projectId: z
        .string()
        .min(1)
        .max(500)
        .optional()
        .describe('Project ID (hash). Required if projectPath is not provided.'),
    specId: SpecIdSchema.describe('Spec ID to bump, e.g. SPEC-042.'),
    kind: z
        .enum(['patch', 'minor', 'major'])
        .describe('Bump kind: patch (typo/format), minor (new criterion/scope expansion), major (breaking change).'),
    reason: z
        .string()
        .min(5)
        .max(500)
        .describe('Free-text reason for the bump (min 5 chars). Recorded in history.'),
    by: z
        .string()
        .min(1)
        .max(200)
        .optional()
        .describe('Actor identifier (session id, plugin id, username). Defaults to a session token.'),
};
// ---------------------------------------------------------------------------
// Handler
// ---------------------------------------------------------------------------
/**
 * Handle the bump_spec_version MCP tool.
 *
 * Reads the spec.md at the spec's recorded specPath, bumps spec_version via
 * SemVer, appends a history entry, and writes the file atomically.
 *
 * @throws if the spec is in format 0.x — it must be written once first to trigger auto-migration.
 */
export async function handleBumpSpecVersion(args) {
    const maybeProjectId = resolveProjectId(args);
    const { specId, kind, reason, by } = args;
    if (!maybeProjectId) {
        return {
            content: [
                {
                    type: 'text',
                    text: 'Error: projectPath or projectId is required.',
                },
            ],
        };
    }
    const projectId = maybeProjectId;
    const spec = await getSpec(projectId, specId);
    if (!spec) {
        return {
            content: [
                {
                    type: 'text',
                    text: `Error: spec "${specId}" not found in project "${projectId}".`,
                },
            ],
        };
    }
    if (!spec.specPath) {
        return {
            content: [
                {
                    type: 'text',
                    text: `Error: spec "${specId}" has no specPath — cannot locate spec.md.`,
                },
            ],
        };
    }
    // At this point spec.specPath is guaranteed non-null/empty (checked above)
    const specPath = spec.specPath;
    try {
        const result = await bumpSpecVersion({
            specId,
            kind,
            reason,
            by,
            specPath,
        });
        // SPEC-747: Grant single-edit unlock token after successful bump
        grantUnlock(specId, specPath);
        return {
            content: [
                {
                    type: 'text',
                    text: [
                        `spec_version bumped for ${specId}.`,
                        ``,
                        `Previous: ${result.previous}`,
                        `Next:     ${result.next}`,
                        `Kind:     ${kind}`,
                        `Reason:   ${reason}`,
                        `At:       ${result.entry.at}`,
                        `By:       ${result.entry.by}`,
                    ].join('\n'),
                },
            ],
        };
    }
    catch (err) {
        const msg = err instanceof Error ? err.message : String(err);
        return {
            content: [
                {
                    type: 'text',
                    text: `Error bumping spec_version for ${specId}: ${msg}`,
                },
            ],
        };
    }
}
// ---------------------------------------------------------------------------
// MCP registration
// ---------------------------------------------------------------------------
export function registerBumpSpecVersionTool(s) {
    s.registerTool('bump_spec_version', {
        description: "Bump the SemVer spec_version field of a spec's frontmatter and record a history entry. " +
            'Use "patch" for typos/formatting, "minor" for new criteria or scope expansion, ' +
            '"major" for breaking interpretation changes. ' +
            'Required before editing a done spec (SPEC-M2 enforcement will be added later). ' +
            'Reads and writes spec.md atomically. Does not modify the data store.',
        inputSchema: BumpSpecVersionSchema,
    }, safeTracked('bump_spec_version', async (args) => handleBumpSpecVersion(args)));
}
//# sourceMappingURL=bump-spec-version.js.map