// tools/graph-specs.ts — SPEC-746: graph_specs tool handler
// Returns the spec dependency graph in JSON or DOT format.
import { z } from 'zod';
import { safeTracked } from './safe-handler.js';
import { resolveProjectId, missingProjectIdError } from './resolve-project-id.js';
import { listSpecs } from '../storage/spec-store.js';
import { buildSpecGraph } from '../engine/spec-dependency-graph/graph-builder.js';
import { toDot } from '../engine/spec-dependency-graph/dot-exporter.js';
export async function graphSpecs(opts) {
    const pid = resolveProjectId({
        projectId: opts.projectId,
        projectPath: opts.projectPath ?? opts.projectRoot,
    });
    if (!pid) {
        throw new Error('projectPath or projectId is required');
    }
    const specs = await listSpecs(pid);
    const graph = buildSpecGraph(specs);
    if (opts.format === 'dot') {
        return { format: 'dot', dot: toDot(graph) };
    }
    return { format: 'json', graph };
}
// ---------------------------------------------------------------------------
// MCP registration
// ---------------------------------------------------------------------------
export function registerGraphSpecsTool(server) {
    server.registerTool('graph_specs', {
        description: 'Build and return the spec dependency graph. Reads `supersedes`, `depends_on`, and ' +
            '`dependencies` frontmatter fields. Returns JSON (nodes + edges) or Graphviz DOT format. ' +
            'Detects cycles in the supersedes chain and reports them as SUPERSEDES_CYCLE diagnostics.',
        annotations: { readOnlyHint: true },
        inputSchema: {
            projectId: z.string().max(500).optional().describe('Project ID hash. Prefer projectPath.'),
            projectPath: z
                .string()
                .max(4096)
                .optional()
                .describe('Absolute path to project root. Derives projectId automatically.'),
            format: z
                .enum(['json', 'dot'])
                .optional()
                .describe('Output format: json (nodes + edges) | dot (Graphviz DOT). Default: json.'),
        },
    }, safeTracked('graph_specs', async (args) => {
        const pid = resolveProjectId(args);
        if (!pid) {
            return missingProjectIdError;
        }
        const specs = await listSpecs(pid);
        const graph = buildSpecGraph(specs);
        const rawFormat = args.format;
        const format = rawFormat === 'dot' ? 'dot' : 'json';
        if (format === 'dot') {
            const dot = toDot(graph);
            const lines = [
                `# Spec Dependency Graph — DOT format`,
                ``,
                `Specs: ${graph.nodes.length}, Edges: ${graph.edges.length}`,
            ];
            if (graph.diagnostics.length > 0) {
                lines.push(`Warning: ${graph.diagnostics.length} cycle(s) detected in supersedes chain.`);
            }
            lines.push(``, '```dot', dot, '```');
            return {
                content: [{ type: 'text', text: lines.join('\n') }],
            };
        }
        const cycleReport = graph.diagnostics.length > 0
            ? `\n\nSUPERSEDES_CYCLE detected (${graph.diagnostics.length}):\n` +
                graph.diagnostics.map((d) => `- ${d.cycle.join(' -> ')}`).join('\n')
            : '';
        return {
            content: [
                {
                    type: 'text',
                    text: `# Spec Dependency Graph\n\n` +
                        `**Specs**: ${graph.nodes.length}  **Edges**: ${graph.edges.length}${cycleReport}\n\n` +
                        `\`\`\`json\n${JSON.stringify(graph, null, 2)}\n\`\`\``,
                },
            ],
        };
    }));
}
//# sourceMappingURL=graph-specs.js.map