/* eslint-disable @typescript-eslint/no-unsafe-argument, max-lines */
// tools/tool-registry/core-tools.ts — Declarative registry for simple single-tool files.
//
// Phase 1 of the declarative-registry refactor. Each entry here replaces one
// register-*-tools.ts file that followed the trivial pattern:
//   export function registerXTool(server): void {
//     server.registerTool(name, { description, inputSchema: Schema.shape }, safeXxx(name, handler))
//   }
//
// To add a tool: import its handler + schema, add an entry to the array.
// To read them: import coreToolsRegistry from this file.
import { z } from 'zod';
import { SpecIdSchema } from '../schemas/index.js';
// ── Handlers ────────────────────────────────────────────────────────────────
import { GenerateBatchScriptInputSchema, handleGenerateBatchScript, } from '../generate-batch-script.js';
import { GenerateAutomationGuideInputSchema, handleGenerateAutomationGuide, } from '../generate-automation-guide.js';
import { handleMultiAgentReview, MultiAgentReviewInputSchema, } from '../multi-agent-review/index.js';
import { handleGenerateDiagram } from '../diagram-handler.js';
import { handleCheckParallelSafety } from '../parallel-safety-handler.js';
import { handleAnalyzePerformanceImpact } from '../performance-analysis-handler.js';
import { handlePlanStatus } from '../status-handler.js';
import { handleValidateWorkflow } from '../validate-workflow.js';
import { handleTransformCode } from '../transform-code-handler.js';
import { handleConfigureTelemetry } from '../configure-telemetry.js';
import { handleTelemetryHealth } from '../telemetry-health-handler.js';
import { handleConfigureObservability } from '../configure-observability.js';
import { handleRecommendModel } from '../recommend-model-handler.js';
import { handleExportSpec } from '../export-spec.js';
import { handleDecomposeSpec } from '../decompose-spec.js';
import { handleFacilitate } from '../facilitate.js';
import { handleScanProject } from '../scan-project.js';
import { handleEstimateAiCost } from '../estimate-ai-cost.js';
import { handleHealSpecDocs } from '../heal-spec-docs.js';
import { handleConvertToBdd } from '../convert-to-bdd.js';
import { handleGenerateSpecFromIssue } from '../generate-spec-from-issue.js';
import { handleIdeConfig } from '../ide-config-handler.js';
import { IdeTargetEnum, IdeConfigScopeEnum } from '../schemas/ide-config.js';
import { handleReconcileSpecLiving } from '../reconcile-spec-living-handler.js';
import { handleSecurityReport } from '../security-report-handler.js';
import { SecurityReportTimeRangeEnum, SecurityReportFormatEnum, } from '../schemas/runtime-security.js';
import { RenderSpecForProviderInputSchema, handleRenderSpecForProvider, } from '../render-spec-for-provider.js';
import { handleTriageRequest, TriageRequestInputSchema } from '../triage-request.js';
// ── Registry ─────────────────────────────────────────────────────────────────
const coreToolsRegistry = [
    // ── generate_batch_script (SPEC-253) ──────────────────────────────────────
    {
        name: 'generate_batch_script',
        description: 'Generate a bash script for fan-out batch processing with `claude -p`. Creates a full script (all matching files) and a pilot script (3-file sample) to test before running at scale. Supports stream-json output, permission modes, and custom tool allowlists.',
        schema: GenerateBatchScriptInputSchema.shape,
        handler: async (args) => handleGenerateBatchScript(args),
        annotations: { readOnlyHint: false },
        wrap: 'tracked',
        group: 'automation',
    },
    // ── generate_automation_guide (SPEC-257) ──────────────────────────────────
    {
        name: 'generate_automation_guide',
        description: 'Generate a comprehensive Claude Code automation guide covering: non-interactive mode (claude -p), permission modes, output formats (stream-json), CI/CD integration, fan-out batch patterns, session resumption (--from-pr), and SubAgent prompt design. Optionally saves to docs/automation-guide.md.',
        schema: GenerateAutomationGuideInputSchema.shape,
        handler: async (args) => handleGenerateAutomationGuide(args),
        annotations: { readOnlyHint: true },
        wrap: 'tracked',
        group: 'automation',
    },
    // ── multi_agent_review (SPEC-483) ─────────────────────────────────────────
    {
        name: 'multi_agent_review',
        description: 'Runs three internal rule-based analyzers in parallel (design, security, testing) ' +
            'and returns a ranked markdown report of findings. ' +
            'Design analyzer checks SOLID violations, naming smells, and complexity hotspots. ' +
            'Security analyzer detects hardcoded secrets, dangerous APIs (eval, exec, shell:true), ' +
            'missing Zod validation, and OWASP markers. ' +
            'Testing analyzer checks for missing test files, test smells, and missing edge cases.',
        schema: MultiAgentReviewInputSchema,
        handler: async (args) => handleMultiAgentReview(args),
        annotations: {
            title: 'Multi-Agent Code Review',
            readOnlyHint: true,
            destructiveHint: false,
            openWorldHint: false,
        },
        wrap: 'tracked',
        group: 'analysis',
    },
    // ── generate_diagram (SPEC-311) ───────────────────────────────────────────
    {
        name: 'generate_diagram',
        description: 'Generate a visual diagram from a text description. ' +
            'Supports Excalidraw (interactive JSON + shareable URL) and Mermaid (text-based) output formats. ' +
            'Use for architecture, data-flow, sequence, ER, and flow diagrams.',
        schema: {
            description: z
                .string()
                .min(1)
                .max(2000)
                .describe('Text description of the diagram. Use "->" or "→" to connect nodes. Example: "User -> API -> DB"'),
            diagramType: z
                .enum(['architecture', 'data-flow', 'sequence', 'er', 'flow'])
                .describe('Type of diagram. Valid values: architecture, data-flow, sequence, er, flow'),
            format: z
                .enum(['mermaid', 'excalidraw'])
                .default('excalidraw')
                .describe('Output format. Valid values: mermaid (text diagram), excalidraw (interactive JSON + URL)'),
        },
        handler: async (args) => handleGenerateDiagram(args),
        annotations: { readOnlyHint: true },
        wrap: 'licensed',
        group: 'analysis',
    },
    // ── check_parallel_safety (SPEC-373) ──────────────────────────────────────
    {
        name: 'check_parallel_safety',
        description: 'Detects worktree collision risks before launching parallel agents. ' +
            'Call this before spawning 3+ parallel agents to get safe agent names and avoid silent worktree conflicts.',
        schema: {
            projectPath: z.string().describe('Absolute path to project root'),
            numAgents: z
                .number()
                .optional()
                .describe('Number of agents to prepare safe names for (default: 6)'),
        },
        handler: (args) => Promise.resolve(handleCheckParallelSafety(args)),
        annotations: { title: 'Check Parallel Safety', readOnlyHint: true },
        wrap: 'licensed',
        group: 'analysis',
    },
    // ── analyze_performance_impact (SPEC-300) ─────────────────────────────────
    {
        name: 'analyze_performance_impact',
        description: 'Analyze a spec for performance risks before implementation. ' +
            'Detects common anti-patterns such as N+1 queries, missing pagination, missing caching, ' +
            'blocking operations, large data loads, and missing database indexes. ' +
            'Returns a risk report with severity levels (high/medium/low) and recommendations.',
        schema: {
            specId: z
                .string()
                .max(500)
                .describe('Spec ID to analyze for performance risks (e.g. SPEC-042)'),
            projectPath: z
                .string()
                .max(2000)
                .describe('Absolute path to the project root (e.g. /home/user/my-app)'),
        },
        handler: async (args) => handleAnalyzePerformanceImpact(args),
        annotations: { readOnlyHint: true },
        wrap: 'licensed',
        group: 'analysis',
    },
    // ── planu_status (SPEC-223) ───────────────────────────────────────────────
    {
        name: 'planu_status',
        description: 'Get a compact world-snapshot of the current Planu project state in one call: active spec (in-progress), next recommended spec (highest-priority approved), queue count, git state (staged/modified files), and an actionable suggestion. Designed to return the full picture in ≤150 tokens.',
        schema: {
            projectPath: z.string().min(1).max(4096).describe('Absolute path to project root'),
            projectId: z
                .string()
                .max(500)
                .optional()
                .describe('Project ID (optional — derived from projectPath if omitted)'),
        },
        handler: async (args) => handlePlanStatus(args),
        annotations: { readOnlyHint: true },
        wrap: 'tracked',
        group: 'core',
    },
    // ── validate_workflow (SPEC-053) ──────────────────────────────────────────
    {
        name: 'validate_workflow',
        description: 'Validate developer workflow conventions: suggest the right AI model for a task, detect Git Flow violations, audit MCP tool code conventions, verify worktree base, or run a full workflow audit. Supports suggest-model | check-git-flow | check-mcp-conventions | check-worktree | full-audit.',
        schema: {
            action: z
                .enum([
                'suggest-model',
                'check-git-flow',
                'check-mcp-conventions',
                'check-worktree',
                'full-audit',
            ])
                .describe('Action: suggest-model (AI model recommendation) | check-git-flow (branch discipline) | check-mcp-conventions (Zod/hints audit) | check-worktree (branch base check) | full-audit (all checks)'),
            projectPath: z
                .string()
                .optional()
                .describe('Absolute path to the project root (defaults to cwd)'),
            taskDescription: z
                .string()
                .optional()
                .describe('suggest-model: natural-language description of the task to score (e.g. "implement the login handler")'),
            toolsDir: z
                .string()
                .optional()
                .describe('check-mcp-conventions | full-audit: directory containing MCP tool *.ts files (defaults to projectPath/src/tools)'),
            worktreePath: z
                .string()
                .optional()
                .describe('check-worktree: absolute path of the worktree to validate (defaults to projectPath)'),
        },
        handler: async (args) => handleValidateWorkflow(args),
        annotations: { readOnlyHint: true },
        wrap: 'licensed',
        group: 'core',
    },
    // ── transform_code (SPEC-077) ─────────────────────────────────────────────
    {
        name: 'transform_code',
        description: 'Apply deterministic code transformations using the TypeScript Compiler API. ' +
            'Supports: rename-symbol, add-types, add-error-handling, modernize-syntax, extract-interface, add-jsdoc. ' +
            'Returns the transformed source code with a list of changes made.',
        schema: {
            source: z.string().max(1_000_000).describe('The source code to transform'),
            intent: z
                .string()
                .max(500)
                .describe('Transform intent: rename-symbol | add-types | add-error-handling | modernize-syntax | extract-interface | add-jsdoc'),
            options: z
                .record(z.string().max(500), z.unknown())
                .optional()
                .describe('Intent-specific options. rename-symbol: {oldName, newName}. add-types: {inferFromUsage}. ' +
                'add-error-handling: {style: "rethrow"|"return-null"|"log-and-rethrow"}. ' +
                'modernize-syntax: {varToConst, requireToImport, moduleExportsToExport, templateLiterals}. ' +
                'extract-interface: {className, interfaceName}. add-jsdoc: {includeParams, includeReturns, includeThrows}.'),
        },
        handler: (args) => Promise.resolve(handleTransformCode(args)),
        annotations: { title: 'Transform Code', readOnlyHint: true },
        wrap: 'licensed',
        group: 'core',
    },
    // ── configure_telemetry (SPEC-200) ────────────────────────────────────────
    {
        name: 'configure_telemetry',
        description: 'Enable, disable, or check the status of anonymous opt-in telemetry. ' +
            'Telemetry is OFF by default. When enabled, only anonymous usage events are sent ' +
            '(tool names, stack info) — no paths, emails, or personal data. ' +
            'A random installation ID is generated locally and is the only identifier used.',
        schema: {
            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.'),
            action: z
                .enum(['enable', 'disable', 'status'])
                .describe('Action to perform: "enable" to opt in, "disable" to opt out, "status" to check current state.'),
            enableObservatory: z
                .boolean()
                .optional()
                .describe('SPEC-572: Opt in to cross-project Observatory aggregated insights from planu.dev. ' +
                'Requires telemetry to be enabled. Only aggregate patterns are fetched — no personal data.'),
        },
        handler: async (args) => handleConfigureTelemetry(args),
        annotations: { title: 'Configure Telemetry', readOnlyHint: false },
        wrap: 'tracked',
        group: 'core',
    },
    // ── telemetry_health (SPEC-346) ───────────────────────────────────────────
    {
        name: 'telemetry_health',
        description: 'Diagnose why telemetry data is not reaching the dashboard. ' +
            'Checks whether telemetry is enabled, the endpoint is configured, the network is reachable, ' +
            'when the last event was sent, and whether any errors are recorded. ' +
            'Optionally sends a test diagnostic event to verify the full pipeline end-to-end.',
        schema: {
            projectPath: z.string().min(1).max(4096).describe('Absolute path to project root'),
            sendTestEvent: z
                .boolean()
                .optional()
                .default(false)
                .describe('Send a test diagnostic event to verify the telemetry pipeline end-to-end. ' +
                'Default: false.'),
        },
        handler: async (args) => handleTelemetryHealth(args),
        annotations: { title: 'Telemetry Health', readOnlyHint: true },
        wrap: 'licensed',
        group: 'core',
    },
    // ── configure_observability (SPEC-165) ────────────────────────────────────
    {
        name: 'configure_observability',
        description: 'Configure OpenTelemetry (OTel) tracing and metrics for the Planu MCP server. ' +
            'When enabled, every tool call becomes a span and spec lifecycle transitions are ' +
            'exported as distributed traces. Uses OTLP/HTTP by default. ' +
            'Requires @opentelemetry/api as a peer dependency for actual export; ' +
            'when not installed, the server runs in no-op mode (zero overhead).',
        schema: {
            enabled: z
                .boolean()
                .optional()
                .describe('Enable or disable OTel tracing. Pass false to disable. Default: false (no-op mode).'),
            endpoint: z
                .url()
                .optional()
                .describe('OTLP exporter endpoint URL. Examples: http://localhost:4318 (OTLP/HTTP), ' +
                'grpc://localhost:4317 (OTLP/gRPC). Required when enabled is true.'),
            samplingRate: z
                .number()
                .min(0)
                .max(1)
                .optional()
                .describe('Fraction of traces to sample. 1.0 = trace every call, 0.0 = trace nothing. ' +
                'Default: 1.0. Valid range: 0..1.'),
            resourceAttributes: z
                .record(z.string(), z.string())
                .optional()
                .describe('Additional OTel resource attributes attached to every span. ' +
                'Example: { "deployment.environment": "production", "team": "platform" }'),
            serviceName: z
                .string()
                .min(1)
                .max(200)
                .optional()
                .describe('Service name reported to the OTel backend. Default: planu-mcp-server.'),
        },
        handler: async (args) => handleConfigureObservability(args),
        annotations: {
            title: 'Configure Observability',
            readOnlyHint: false,
            destructiveHint: false,
        },
        wrap: 'none',
        group: 'core',
    },
    // ── recommend_model (SPEC-076) ────────────────────────────────────────────
    {
        name: 'recommend_model',
        description: 'Recommend the best AI model tier (haiku, sonnet, or opus) for a given task or spec. ' +
            'Analyzes complexity signals, applies configurable rules, learns from past outcomes, ' +
            'and estimates token cost in USD. Returns a recommendation with reasoning and alternatives.',
        schema: {
            specId: z
                .string()
                .optional()
                .describe('Optional spec ID to analyse (e.g., "SPEC-076"). ' +
                'The router will extract complexity signals from the spec content.'),
            taskDescription: z
                .string()
                .optional()
                .describe('Optional natural-language description of the task. ' +
                'Used to infer complexity signals when no specId is provided.'),
            signals: z
                .object({
                criteriaCount: z.number().optional().describe('Number of acceptance criteria.'),
                fileCount: z.number().optional().describe('Number of files to create or modify.'),
                dependencyDepth: z.number().optional().describe('Depth of dependency chain.'),
                crossLayerCount: z
                    .number()
                    .optional()
                    .describe('Number of architectural layers involved.'),
                hasNewTypes: z.boolean().optional().describe('Whether new types are introduced.'),
                hasDBChanges: z.boolean().optional().describe('Whether database changes are needed.'),
                hasSecurityImplications: z
                    .boolean()
                    .optional()
                    .describe('Whether the task has security implications.'),
                estimatedHours: z.number().optional().describe('Estimated hours to complete.'),
                isReadOnly: z.boolean().optional().describe('Whether the task is read-only.'),
                taskType: z
                    .enum(['search', 'generation', 'architecture', 'debugging', 'refactor'])
                    .optional()
                    .describe('Type of task: search | generation | architecture | debugging | refactor'),
            })
                .optional()
                .describe('Optional explicit complexity signals. Overrides signals extracted from text.'),
        },
        handler: (args) => Promise.resolve(handleRecommendModel(args)),
        annotations: { title: 'Recommend Model', readOnlyHint: true },
        wrap: 'licensed',
        group: 'analysis',
    },
    // ── export_spec (SPEC-097) ────────────────────────────────────────────────
    {
        name: 'export_spec',
        description: 'Export a spec to an external format. Supports spec-kit format ' +
            '(GitHub spec-kit compatible: spec.md, plan.md, tasks.md) and agent-ready format ' +
            '(structured JSON optimized for autonomous coding agents: Devin, Kiro, SWE-agent, generic). ' +
            'Writes files to .specify/specs/{slug}/ by default.',
        schema: {
            specId: SpecIdSchema.describe('Spec ID to export (e.g. "SPEC-001")'),
            projectPath: z.string().min(1).max(2000).describe('Absolute path to the project root'),
            format: z
                .enum(['spec-kit', 'agent-ready'])
                .describe('Export format. Valid values: spec-kit, agent-ready'),
            outputDir: z
                .string()
                .max(2000)
                .optional()
                .describe('Custom output directory (default: .specify/specs/{slug}/ inside projectPath)'),
            agentTarget: z
                .enum(['devin', 'kiro', 'swe-agent', 'generic'])
                .optional()
                .describe('Target agent for agent-ready format. Valid values: devin, kiro, swe-agent, generic. Defaults to generic. Only used when format is agent-ready.'),
        },
        handler: async (args) => handleExportSpec(args),
        annotations: { readOnlyHint: false },
        wrap: 'licensed',
        group: 'spec',
    },
    // ── decompose_spec (SPEC-157) ─────────────────────────────────────────────
    {
        name: 'decompose_spec',
        description: 'Auto-decompose a spec into independent agent tasks with non-overlapping file ownership. ' +
            'Groups acceptance criteria by category (types, engine, storage, tool, tests), detects file conflicts, ' +
            'resolves ownership, assigns model recommendations, and outputs a ready-to-execute orchestration plan ' +
            'compatible with orchestrate_runtime.',
        schema: {
            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.'),
            specId: z
                .string()
                .max(500)
                .describe('Spec ID to decompose (e.g. "SPEC-042"). Must exist in the project.'),
            maxTasks: z
                .number()
                .int()
                .min(1)
                .max(20)
                .optional()
                .describe('Maximum number of agent tasks to generate (default: 5, max: 20). ' +
                'Excess work units are merged into the last task.'),
        },
        handler: async (args) => handleDecomposeSpec(args),
        annotations: { readOnlyHint: true },
        wrap: 'licensed',
        group: 'spec',
    },
    // ── facilitate (SPEC-037 + SPEC-264) ──────────────────────────────────────
    {
        name: 'facilitate',
        description: 'Universal SDD workflow entry point (SPEC-037 + SPEC-264). ' +
            'Classifies intent, loads project context, routes intelligently: ' +
            'trivial/exploration → direct answer; feature → create_spec; in-progress → resume. ' +
            'Also bridges Plan Mode output into spec creation via planContent. ' +
            'If the response contains interactiveQuestions[], present them to the user using AskUserQuestion (Claude Code) or equivalent interactive UI. DO NOT display the JSON as text.',
        schema: {
            description: z
                .string()
                .max(10_000)
                .describe('Free-form description: what you want to build, your problem, or your request'),
            projectId: z
                .string()
                .max(500)
                .optional()
                .describe('Project ID or project path (used for context loading and focus persistence)'),
            sessionId: z
                .string()
                .max(500)
                .optional()
                .describe('Session ID (overrides projectId for focus tracking)'),
            scenario: z
                .enum(['new_project', 'existing_project', 'concrete_problem'])
                .optional()
                .describe('Override auto-detected scenario. Valid values: new_project, existing_project, concrete_problem'),
            collectedInfo: z
                .record(z.string().max(500), z.string().max(10_000))
                .optional()
                .describe('Answers collected in previous steps'),
            parentWorkspacePath: z
                .string()
                .max(4096)
                .optional()
                .describe('Parent workspace path that should contain a new project folder'),
            appName: z.string().max(500).optional().describe('Human-readable new app name'),
            appSlug: z
                .string()
                .max(200)
                .optional()
                .describe('Safe folder name for the new app; must not contain path separators'),
            projectType: z.string().max(500).optional().describe('Kind of project to create'),
            platform: z.string().max(500).optional().describe('Runtime or deployment platform'),
            language: z.string().max(500).optional().describe('User-confirmed programming language'),
            framework: z
                .string()
                .max(500)
                .nullable()
                .optional()
                .describe('User-confirmed framework, null for no framework'),
            database: z
                .string()
                .max(500)
                .nullable()
                .optional()
                .describe('User-confirmed database, null for no database'),
            createDirectory: z
                .boolean()
                .optional()
                .describe('When true in new_project flow, create only the resolved child project folder'),
            clarificationAnswers: z
                .record(z.string().max(500), z.string().max(10_000))
                .optional()
                .describe('Answers to interactiveQuestions emitted by facilitate'),
            step: z
                .number()
                .int()
                .min(0)
                .max(10)
                .optional()
                .describe('Current step index for new_project flow (0–2)'),
            targetTool: z
                .string()
                .max(500)
                .optional()
                .describe('Planu tool name to check ambiguity for before calling it'),
            checkFocus: z
                .boolean()
                .optional()
                .describe('Check whether this request is aligned with the current session focus'),
            changeFocus: z
                .string()
                .max(10_000)
                .optional()
                .describe('Explicitly declare a new session objective'),
            planContent: z
                .string()
                .max(10_000)
                .optional()
                .describe('Plan Mode output to convert into a spec. Paste the plan markdown here to extract ACs and route to create_spec.'),
        },
        handler: (args) => handleFacilitate(args),
        annotations: { readOnlyHint: false },
        wrap: 'tracked',
        group: 'core',
    },
    // ── scan_project (SPEC-134) ───────────────────────────────────────────────
    {
        name: 'scan_project',
        description: 'Scan an entire project, discover modules, analyze each in parallel, detect cross-module dependencies and antipatterns, calculate a project health score, and generate specs for every module. Returns ready-to-use team plan for parallel implementation.',
        schema: {
            path: z.string().max(4096).describe('Absolute path to the project root directory'),
            projectId: z.string().max(500).optional().describe('Project ID hash. Prefer projectPath.'),
            depth: z
                .enum(['shallow', 'deep', 'deep-v2'])
                .optional()
                .describe('Analysis depth: shallow (fast, structure only) | deep (includes deps) | deep-v2 (full 5-analyzer parallel analysis). Default: deep-v2'),
            modules: z
                .array(z.string())
                .optional()
                .describe('Override auto-discovery with explicit module paths (relative to project root). When provided, skips auto-detection.'),
            concurrency: z
                .number()
                .min(1)
                .max(8)
                .optional()
                .describe('Max parallel module analyses (1-8). Default: 4'),
            generateOverview: z
                .boolean()
                .optional()
                .describe('Generate a project overview spec with architecture diagram and health breakdown. Default: true'),
            generateTeamPlan: z
                .boolean()
                .optional()
                .describe('Auto-generate implementation team plan with file ownership. Default: true'),
        },
        handler: async (args) => handleScanProject(args),
        annotations: { readOnlyHint: false },
        wrap: 'licensed',
        group: 'analysis',
    },
    // ── estimate_ai_cost (SPEC-069) ───────────────────────────────────────────
    {
        name: 'estimate_ai_cost',
        description: 'Estimate how many tokens and dollars it will cost to implement a spec with different AI models ' +
            '(Claude Opus, Sonnet, Haiku, GPT-4o, GPT-4.1, Gemini 2.5 Pro). ' +
            'Returns a cost breakdown table and a model recommendation based on spec complexity.',
        schema: {
            specId: z
                .string()
                .describe('Spec ID to estimate costs for (e.g., "SPEC-069"). ' +
                'Must match a spec directory in planu/specs/ or docs/sdd/specs/.'),
            projectPath: z
                .string()
                .optional()
                .describe('Optional absolute path to the project directory. ' +
                'When provided, the estimator scans the codebase to include context tokens in the estimate.'),
        },
        handler: async (args) => handleEstimateAiCost(args),
        annotations: { title: 'Estimate AI Cost', readOnlyHint: true },
        wrap: 'licensed',
        group: 'analysis',
    },
    // ── heal_spec_docs (SPEC-493) ─────────────────────────────────────────────
    {
        name: 'heal_spec_docs',
        description: 'Retroactively heals spec documentation quality issues in a client project. ' +
            'Repairs status.json totalSpecs count from actual on-disk specs, ' +
            'replaces placeholder ## Technical sections of spec.md (containing slugified paths or "(pending)" markers) ' +
            'with properly structured entries, and reports non-English spec titles for manual review. ' +
            'Safe to run multiple times (idempotent). Does NOT translate titles automatically.',
        schema: {
            projectPath: z.string().min(1).max(4096).describe('Absolute path to the project root'),
            specId: z
                .string()
                .min(1)
                .max(500)
                .optional()
                .describe('Optional spec ID (e.g. SPEC-123) to scope healing to a single spec. ' +
                'When omitted, heals all specs in the project. Autopilot cascades ' +
                'should always pass the triggering spec ID to avoid mass-rewrites.'),
        },
        handler: async (args) => handleHealSpecDocs(args),
        annotations: {
            title: 'heal_spec_docs',
            readOnlyHint: false,
            destructiveHint: false,
        },
        wrap: 'licensed',
        group: 'spec',
    },
    // ── convert_to_bdd (SPEC-481) ─────────────────────────────────────────────
    {
        name: 'convert_to_bdd',
        description: 'Convert a spec.md from checkbox acceptance criteria format to BDD/Gherkin Given-When-Then scenarios. ' +
            'Reads the existing criteria: block, transforms each item into a Scenario with Then steps, ' +
            'and writes the updated spec.md back to disk. Safe to run multiple times — no-ops if already in BDD format.',
        schema: {
            projectPath: z
                .string()
                .describe('Absolute path to the project root (must contain planu.json or planu/specs/).'),
            specId: z
                .string()
                .describe('Spec ID to convert, e.g. "SPEC-042". The tool will locate the corresponding spec.md automatically.'),
        },
        handler: async (args) => handleConvertToBdd(args),
        annotations: {
            title: 'Convert Spec to BDD Format',
            readOnlyHint: false,
            destructiveHint: false,
            openWorldHint: false,
        },
        wrap: 'tracked',
        group: 'spec',
    },
    // ── generate_spec_from_issue (SPEC-472) ───────────────────────────────────
    {
        name: 'generate_spec_from_issue',
        description: 'Generate a Planu spec draft from an external issue/ticket (GitHub, Linear, Jira, Asana, Notion). ' +
            'Reads the issue title/body/labels/comments and infers target, scope, type, and risk. ' +
            'Returns a structured spec draft ready to pass to create_spec.',
        schema: {
            source: z
                .enum(['github', 'linear', 'jira', 'asana', 'notion'])
                .describe('Issue source: github | linear | jira | asana | notion'),
            issueId: z
                .string()
                .min(1)
                .describe('Issue ID (GH issue number, Linear identifier, Jira key, Asana GID, Notion page ID)'),
            projectPath: z.string().min(1).describe('Absolute path to the project root'),
            enrichWithCode: z
                .boolean()
                .optional()
                .describe('If true, also scan related code paths to enrich the description'),
        },
        handler: async (args) => handleGenerateSpecFromIssue(args),
        annotations: { title: 'Generate Spec from Issue', readOnlyHint: true },
        wrap: 'none',
        group: 'spec',
    },
    // ── generate_ide_config (SPEC-080) ────────────────────────────────────────
    {
        name: 'generate_ide_config',
        description: 'Generate MCP server configuration for your IDE. ' +
            'Supports VS Code, Cursor, Windsurf, JetBrains, Claude Desktop, Claude Code, Neovim, and ChatGPT. ' +
            'Use ide="auto" to detect installed IDEs and generate config for all of them.',
        schema: {
            ide: IdeTargetEnum,
            projectPath: z.string().max(4096).optional().default('.').describe('Project root path'),
            scope: IdeConfigScopeEnum.optional()
                .default('project')
                .describe('Config scope: project | global'),
            dryRun: z
                .boolean()
                .optional()
                .default(false)
                .describe('Preview config without writing files'),
        },
        handler: (args) => Promise.resolve(handleIdeConfig(args)),
        annotations: { title: 'Generate IDE Config' },
        wrap: 'licensed',
        group: 'core',
    },
    // ── living_reconcile_spec (SPEC-330) ──────────────────────────────────────
    {
        name: 'living_reconcile_spec',
        description: 'Auto-reconcile spec acceptance criteria against actual implementation files. ' +
            'Scans your codebase for files referenced in the ## Technical section of spec.md, checks exports, ' +
            'detects drift, and updates the ## Progress section of spec.md with real coverage data. ' +
            'Works entirely offline — pure local file analysis.',
        schema: {
            specId: SpecIdSchema.describe('Spec ID, e.g. SPEC-042'),
            projectPath: z.string().min(1).max(4096).describe('Absolute path to project root'),
        },
        handler: async (args) => handleReconcileSpecLiving({
            specId: args.specId,
            projectPath: args.projectPath,
        }),
        annotations: {
            title: 'Reconcile Spec vs Implementation',
            readOnlyHint: false,
            destructiveHint: false,
        },
        wrap: 'licensed',
        group: 'spec',
    },
    // ── security_report (SPEC-084) ────────────────────────────────────────────
    {
        name: 'security_report',
        description: 'Generate a security report from the audit log — shows threats, blocked calls, and recommendations.',
        schema: {
            projectPath: z.string().min(1).max(4096).describe('Absolute path to the project directory'),
            timeRange: SecurityReportTimeRangeEnum.optional().describe('Time range for the report: last24h | last7d | last30d | all (default: all)'),
            format: SecurityReportFormatEnum.optional().describe('Report format: summary | detailed (default: summary)'),
        },
        handler: (args) => Promise.resolve(handleSecurityReport(args)),
        annotations: { readOnlyHint: true },
        wrap: 'licensed',
        group: 'core',
    },
    // ── render_spec_for_provider (SPEC-670) ───────────────────────────────────
    {
        name: 'render_spec_for_provider',
        description: 'Render the canonical (unified per SPEC-630) spec.md for a target LLM provider. ' +
            "Valid providers: 'claude' (MCP + model hints + mcp__planu__* tool callouts), " +
            "'gpt4' (AGENTS.md compatible — replaces <thinking> with > Reasoning quotes), " +
            "'gemini' (GEMINI.md with Google function declarations + Inputs/Outputs blocks), " +
            "'markdown' (≤800-token plain context written to planu/context.md). " +
            'Omit `provider` to auto-detect via SPEC-645 client detection. ' +
            'The canonical spec.md is never modified — only the rendered output differs per provider.',
        schema: RenderSpecForProviderInputSchema.shape,
        handler: async (args) => handleRenderSpecForProvider(args),
        annotations: { readOnlyHint: false, destructiveHint: false },
        wrap: 'tracked',
        group: 'spec',
    },
    // ── triage_request (SPEC-726) ─────────────────────────────────────────────
    {
        name: 'triage_request',
        description: 'Classify a user request into {idea, quick-fix, bug-spec, feature-spec, epic} using ' +
            'a Haiku-backed heuristic classifier, then route to the appropriate next tool. ' +
            'Writes an intake.json handoff artifact when projectId is provided. ' +
            'Returns interactiveQuestions if confidence < 0.6 — the host must call AskUserQuestion.',
        schema: TriageRequestInputSchema.shape,
        handler: async (args) => handleTriageRequest(args),
        annotations: {
            title: 'Triage Request',
            readOnlyHint: false,
            destructiveHint: false,
        },
        wrap: 'tracked',
        group: 'core',
    },
];
export default coreToolsRegistry;
//# sourceMappingURL=core-tools.js.map