/* eslint-disable max-lines -- thematic registry group consolidated by SPEC refactor-phase3 (commit aafeea60); each block is a declarative tool registration ~20 lines, split would re-create the ~120 register-*.ts files this phase intentionally collapsed */
// tools/tool-registry/group-quality-compliance.ts — Declarative registry: quality & compliance tools.
//
// Phase 3 (refactor/registry-phase3): consolidates register-*.ts files for:
//   compliance, compliance-audit, compliance-gate, compliance-test,
//   security, audit-trail, schema-parity, schema-generator,
//   tdd, coverage, eval-skill, ears, spec-lint, readiness, approval,
//   governance, spec-322 (api contract), spec-323 (tdd scaffold), spec-331 (orchestration plan)
import { z } from 'zod';
import { safeLicensed, safeTracked } from '../safe-handler.js';
import { projectIdSchema, withProject } from '../tool-registry-helpers.js';
import { t } from '../../i18n/index.js';
import { makeDeprecationStub } from './deprecated-stubs.js';
import { registerFromEntries } from '../tool-entry.js';
// ── Compliance (register-compliance-tools.ts) ─────────────────────────────────
import { handleCheckComplianceUnified, handleCheckComplianceLegacyShim, handleComplianceScoreReportShim, handleComplianceGapAnalysisShim, handleVerifySpecComplianceShim, } from '../compliance-unified-handler.js';
import { handleConfigureCompliance } from '../compliance-handler.js';
// ── Compliance audit (register-compliance-audit-tools.ts) ────────────────────
import { handleGenerateComplianceReport, handleAuditLogExport, handleDataRetentionPolicy, } from '../compliance-audit-handler.js';
// ── Compliance gate (register-compliance-gate-tools.ts) ──────────────────────
import { handleConfigureComplianceGate, handleComplianceGateStatus, ConfigureComplianceGateSchema, } from '../compliance-gate-handler.js';
// ── Compliance test (register-compliance-test-tools.ts) ──────────────────────
import { handleGenerateComplianceTests, handleComplianceCoverageReport, } from '../compliance-test-handler.js';
// ── Security (register-security-tools.ts) ────────────────────────────────────
import { handleSecurityCheck } from '../security-check.js';
// ── Audit trail (register-audit-trail-tools.ts) ──────────────────────────────
import { handleAuditTrail, handleGenerateAttestation } from '../audit-trail-handler.js';
import { handleExportAuditTrail } from '../export-audit-trail-handler.js';
// ── Schema parity (register-schema-parity-tools.ts) ──────────────────────────
import { handleAuditSchemaParity } from '../audit-schema-parity-handler.js';
import { handleFixSchemaParity } from '../fix-schema-parity-handler.js';
// ── Schema generator (register-schema-generator-tools.ts) ────────────────────
import { handleGenerateValidationSchema, handleGenerateApiClient, } from '../schema-generator-handler.js';
// ── TDD (register-tdd-tools.ts) ───────────────────────────────────────────────
import { handleConfigureTddPolicy, handleGenerateEdgeTests, handleTddStatus, } from '../tdd-handler.js';
// ── Coverage (register-coverage-tools.ts) ────────────────────────────────────
import { handleSpecCoverage } from '../spec-coverage.js';
// ── Eval skill (register-eval-skill-tools.ts) ─────────────────────────────────
import { handleEvalSkill, handleEvalRule } from '../eval-skill-handler.js';
// ── EARS (register-ears.ts) ───────────────────────────────────────────────────
import { lintSpec, lintAllSpecs } from '../../engine/ears/spec-linter.js';
import { scoreCriterion } from '../../engine/ears/criterion-scorer.js';
import { generateRewrites } from '../../engine/ears/rewriter.js';
import { detectEarsPattern } from '../../engine/ears/pattern-matcher.js';
import { checkCriteriaQuality } from '../../engine/criteria-quality-checker.js';
import { compactJson } from '../output-formatter.js';
// ── Spec lint (register-spec-lint-tools.ts) ───────────────────────────────────
import { handleDefineLintRule, handleListLintRules, handleRunSpecLint, handleDeleteLintRule, } from '../spec-lint-handler.js';
// ── Readiness (register-readiness-tools.ts) ───────────────────────────────────
import { handleCheckReadiness } from '../check-readiness.js';
import { handlePackageHandoff } from '../package-handoff.js';
import { CheckReadinessOutputSchema, SpecIdSchema } from '../schemas/index.js';
// ── Approval (register-approval-tools.ts) ────────────────────────────────────
import { handleConfigureApprovalPolicy, handleApproveSpec, handleRequestChanges, handleApprovalStatus, handleIssueReviewerToken, } from '../approval-handler.js';
// ── SPEC-961: Architecture lint ─────────────────────────────────────────────
import { handleArchitectureLint } from '../architecture-lint.js';
// ── SPEC-962: SOLID check ───────────────────────────────────────────────────
import { handleSolidCheck } from '../solid-check.js';
// ── SPEC-965: Verify Integrations ─────────────────────────────────────────────
import { handleVerifyIntegrations } from '../verify-integrations.js';
// ── SPEC-971: Spec Obesity Healer ────────────────────────────────────────────
import { handleSpecObesityHealer } from '../spec-obesity-healer.js';
// ── Governance (register-governance-tools.ts) ─────────────────────────────────
import { handleDataGovernance } from '../data-governance.js';
import { handleLegalComplianceReport } from '../legal-compliance-report.js';
// ── SPEC-322: API contract validation ─────────────────────────────────────────
import { handleValidateApiContract } from '../validate-api-contract-handler.js';
// ── SPEC-323: TDD scaffold & coverage gap ────────────────────────────────────
import { handleTddScaffold, handleCoverageGapAnalyzer, handleRunMutationHints, } from '../tdd-scaffold-handler.js';
// ── SPEC-331: Orchestration plan ──────────────────────────────────────────────
// handleOrchestrateAgents import removed — registration replaced by deprecation stub (SPEC-658)
// ---------------------------------------------------------------------------
// Shared schema constants
// ---------------------------------------------------------------------------
const FRAMEWORK_ENUM = ['gdpr', 'hipaa', 'pci-dss', 'soc2', 'ccpa'];
const STANDARD_ENUM = ['soc2-type2', 'iso27001', 'gdpr', 'hipaa', 'custom'];
const frameworkSchema = z
    .enum(FRAMEWORK_ENUM)
    .describe('Compliance framework. Valid values: gdpr, hipaa, pci-dss, soc2, ccpa.');
const STANDARD_DESCRIBE = 'Compliance standard. Valid values: soc2-type2 | iso27001 | gdpr | hipaa | custom';
const FORMAT_DESCRIBE = 'Output format. Valid values: html | json';
const EXPORT_FORMAT_DESCRIBE = 'Export format. Valid values: csv | json | json-ld';
const AUDIT_EVENT_TYPES = [
    'spec.created',
    'spec.updated',
    'spec.status_changed',
    'spec.approved',
    'spec.drift_detected',
    'spec.reconciled',
    'tool.called',
    'role.assigned',
];
const COMPLIANCE_TEST_FRAMEWORK_DESCRIBE = 'Compliance framework. Valid values: soc2 | pci-dss | iso-42001 | hipaa | gdpr';
const TEST_FORMAT_DESCRIBE = 'Output format for tests. Valid values: jest | gherkin | markdown';
const LintConditionSchema = z.object({
    field: z
        .enum(['tags', 'acceptanceCriteria', 'estimatedHours', 'priority', 'description', 'title'])
        .describe('Field to check. Values: tags, acceptanceCriteria, estimatedHours, priority, description, title.'),
    operator: z
        .enum([
        'includes',
        'not-includes',
        'min-length',
        'max-length',
        'any-contains',
        'none-contain',
        'greater-than',
        'less-than',
        'exists',
        'not-exists',
    ])
        .describe('Operator. Values: includes, not-includes, min-length, max-length, any-contains, none-contain, greater-than, less-than, exists, not-exists.'),
    value: z
        .union([z.string(), z.number()])
        .optional()
        .describe('Value to compare against. Not required for exists/not-exists operators.'),
});
const evalScenarioSchema = z.object({
    input: z
        .string()
        .min(1)
        .max(2000)
        .describe('Natural language description of the user input or trigger for this scenario.'),
    expectedOutput: z
        .string()
        .min(1)
        .max(4000)
        .describe('The expected text fragment or output that the skill/rule should produce or reference.'),
    criteria: z
        .array(z.string().min(1).max(500))
        .min(1)
        .describe('List of keyword or phrase criteria that must appear in the skill/rule content to pass this scenario.'),
});
// ---------------------------------------------------------------------------
// Registration
// ---------------------------------------------------------------------------
// eslint-disable-next-line max-lines-per-function -- registration catalog: one block per tool, each ~20 lines
export function registerQualityComplianceGroupTools(s) {
    s.registerTool('check_compliance', {
        description: 'Unified compliance tool. Use mode to select the operation: ' +
            'score (project-wide compliance score report), ' +
            'gaps (gap analysis against a compliance standard), ' +
            'verify (verify a spec against its codebase), ' +
            'full (runs score + gaps + verify in one call). ' +
            'Legacy mode (no mode field): evaluate spec AC against regulatory frameworks.',
        inputSchema: {
            mode: z
                .enum(['score', 'gaps', 'verify', 'full'])
                .optional()
                .describe('Compliance check mode. Values: score (scoring report), gaps (gap analysis), verify (spec verification), full (all three combined). Omit for legacy framework-based AC check.'),
            specId: z
                .string()
                .min(1)
                .max(500)
                .optional()
                .describe('Spec ID to check or verify (e.g. SPEC-042). Required for verify/full modes.'),
            projectPath: z
                .string()
                .min(1)
                .max(4096)
                .optional()
                .describe('Absolute path to the project root.'),
            projectId: z
                .string()
                .max(500)
                .optional()
                .describe('Project ID (alternative to projectPath for score mode).'),
            minScore: z
                .number()
                .min(0)
                .max(100)
                .optional()
                .describe('Only include specs with score below this value (score/full modes). Default: show all.'),
            standard: z
                .enum(STANDARD_ENUM)
                .optional()
                .describe('Compliance standard for gap analysis (gaps/full modes). Valid values: soc2-type2, iso27001, gdpr, hipaa, custom. Default: soc2-type2.'),
            frameworks: z
                .array(frameworkSchema)
                .optional()
                .describe('Compliance frameworks for legacy AC check. If omitted, auto-detects from spec content. Valid values: gdpr, hipaa, pci-dss, soc2, ccpa.'),
        },
        annotations: { title: 'Check Compliance', readOnlyHint: true },
    }, safeTracked('check_compliance', (args) => {
        const raw = args;
        if (raw.mode !== undefined) {
            return handleCheckComplianceUnified(raw);
        }
        if (raw.specId === undefined || raw.projectPath === undefined) {
            return Promise.resolve({
                content: [
                    {
                        type: 'text',
                        text: 'Provide mode to select compliance operation, or provide specId + projectPath for legacy AC check.',
                    },
                ],
                isError: true,
            });
        }
        // eslint-disable-next-line @typescript-eslint/no-deprecated
        return handleCheckComplianceLegacyShim({
            specId: raw.specId,
            projectPath: raw.projectPath,
            frameworks: raw.frameworks,
        });
    }));
    s.registerTool('configure_compliance', {
        description: 'Configure project-level compliance frameworks. ' +
            'Once set, check_compliance uses these frameworks when none are specified. ' +
            'Supported frameworks: gdpr, hipaa, pci-dss, soc2, ccpa.',
        inputSchema: {
            projectPath: z.string().min(1).max(4096).describe('Absolute path to the project root.'),
            frameworks: z
                .array(frameworkSchema)
                .min(1)
                .describe('Compliance frameworks to activate for this project. Valid values: gdpr, hipaa, pci-dss, soc2, ccpa.'),
        },
        annotations: { title: 'Configure Compliance', destructiveHint: false },
    }, safeTracked('configure_compliance', (args) => handleConfigureCompliance(args)));
    s.registerTool('compliance_score_report', {
        description: '[DEPRECATED] Use check_compliance with mode=score instead. Returns a project-wide compliance summary of previously computed spec scores.',
        inputSchema: {
            projectPath: z.string().max(4096).describe('Absolute path to the project root.'),
            minScore: z
                .number()
                .min(0)
                .max(100)
                .optional()
                .describe('Only include specs with score below this value. Default: show all.'),
        },
        annotations: { title: 'Compliance Score Report (deprecated)', readOnlyHint: true },
    }, safeTracked('compliance_score_report', (args) => 
    // eslint-disable-next-line @typescript-eslint/no-deprecated
    handleComplianceScoreReportShim(args)));
    s.registerTool('compliance_gap_analysis', {
        description: '[DEPRECATED] Use check_compliance with mode=gaps instead. Analyze which controls of a compliance standard are not fully satisfied.',
        inputSchema: {
            projectPath: z
                .string()
                .min(1)
                .max(4096)
                .describe('Absolute path to the project root directory'),
            standard: z.enum(STANDARD_ENUM).describe(STANDARD_DESCRIBE),
        },
        annotations: { title: 'Compliance Gap Analysis (deprecated)', readOnlyHint: true },
    }, safeTracked('compliance_gap_analysis', (args) => Promise.resolve(
    // eslint-disable-next-line @typescript-eslint/no-deprecated
    handleComplianceGapAnalysisShim(args))));
    s.registerTool('verify_spec_compliance', {
        description: '[DEPRECATED] Use check_compliance with mode=verify instead. Checks whether implemented code matches an approved spec. Score >=80 = compliant, 60-79 = partial, <60 = non-compliant.',
        annotations: { readOnlyHint: false },
        inputSchema: {
            specId: SpecIdSchema.describe('Spec ID to verify (e.g. SPEC-042).'),
            projectPath: z
                .string()
                .max(4096)
                .describe('Absolute path to the project root containing planu/specs/ and source code.'),
        },
    }, safeTracked('verify_spec_compliance', (args) => 
    // eslint-disable-next-line @typescript-eslint/no-deprecated
    handleVerifySpecComplianceShim(args)));
    // ── Compliance audit ─────────────────────────────────────────────────────────
    s.registerTool('generate_compliance_report', {
        description: 'Generate a compliance report mapping Planu features to controls for a given standard ' +
            '(SOC2 Type 2, ISO 27001, GDPR, HIPAA). ' +
            'Returns HTML or JSON with control status (satisfied / partial / gap).',
        inputSchema: {
            projectPath: z
                .string()
                .min(1)
                .max(4096)
                .describe('Absolute path to the project root directory'),
            standard: z
                .enum(['soc2-type2', 'iso27001', 'gdpr', 'hipaa', 'custom'])
                .describe(STANDARD_DESCRIBE),
            outputFormat: z.enum(['html', 'json']).optional().describe(FORMAT_DESCRIBE),
        },
        annotations: { title: 'Generate Compliance Report', readOnlyHint: true },
    }, safeLicensed('generate_compliance_report', (args) => Promise.resolve(handleGenerateComplianceReport(args))));
    s.registerTool('audit_log_export', {
        description: 'Export the project audit trail in CSV, JSON, or JSON-LD format. ' +
            'Supports optional date range filtering. ' +
            'Use for compliance reviews, security audits, or data archiving.',
        inputSchema: {
            projectPath: z
                .string()
                .min(1)
                .max(4096)
                .describe('Absolute path to the project root directory'),
            format: z.enum(['csv', 'json', 'json-ld']).optional().describe(EXPORT_FORMAT_DESCRIBE),
            dateRange: z
                .object({
                from: z.string().max(50).optional().describe('Start date ISO string (e.g. 2024-01-01)'),
                to: z.string().max(50).optional().describe('End date ISO string (e.g. 2024-12-31)'),
            })
                .optional()
                .describe('Optional date range filter for audit entries'),
        },
        annotations: { title: 'Export Audit Log', readOnlyHint: true },
    }, safeLicensed('audit_log_export', async (args) => handleAuditLogExport(args)));
    s.registerTool('data_retention_policy', {
        description: 'Configure the data retention policy for the project. ' +
            'Specifies how long data should be retained and whether auto-deletion is enabled. ' +
            'NOTE: Policy describes intent; auto-delete does not run automatically.',
        inputSchema: {
            projectPath: z
                .string()
                .min(1)
                .max(4096)
                .describe('Absolute path to the project root directory'),
            retentionDays: z
                .number()
                .int()
                .min(1)
                .max(3650)
                .describe('Number of days to retain data (e.g. 365 for one year)'),
            autoDelete: z
                .boolean()
                .optional()
                .describe('Whether data should be auto-deleted after retentionDays (default: false)'),
            notifyBeforeDeleteDays: z
                .number()
                .int()
                .min(0)
                .max(365)
                .optional()
                .describe('Days before deletion to notify (default: 7)'),
        },
        annotations: { title: 'Configure Data Retention Policy', readOnlyHint: false },
    }, safeLicensed('data_retention_policy', async (args) => handleDataRetentionPolicy(args)));
    // ── Compliance gate ───────────────────────────────────────────────────────────
    s.registerTool('configure_compliance_gate', {
        description: 'Configure the compliance gate that runs automatically when a spec is sent to review. ' +
            'In soft mode, a warning is shown if compliance score falls below the threshold. ' +
            'In hard mode, the review transition is blocked. Requires verify_spec_compliance to be run first. ' +
            'Pro feature.',
        inputSchema: ConfigureComplianceGateSchema.shape,
        annotations: { title: 'Configure Compliance Gate', destructiveHint: false },
    }, safeLicensed('configure_compliance_gate', async (args) => handleConfigureComplianceGate(args)));
    s.registerTool('compliance_gate_status', {
        description: 'Check the compliance gate configuration for this project. Returns the current mode and threshold. Free tool — no license required.',
        inputSchema: {
            projectPath: z.string().describe('Absolute path to the project root'),
        },
        annotations: { title: 'Compliance Gate Status', readOnlyHint: true },
    }, safeTracked('compliance_gate_status', async (args) => handleComplianceGateStatus(args)));
    // ── Compliance test ───────────────────────────────────────────────────────────
    s.registerTool('generate_compliance_tests', {
        description: 'Generate compliance-specific test suites from spec acceptance criteria. ' +
            'Maps criteria to regulatory controls (SOC2, PCI-DSS, ISO-42001) and outputs ' +
            'Jest/Vitest test files, Gherkin scenarios, or markdown checklists.',
        inputSchema: {
            projectPath: z
                .string()
                .min(1)
                .max(4096)
                .describe('Absolute path to the project root directory'),
            specId: z
                .string()
                .min(1)
                .max(50)
                .describe('Spec ID to generate compliance tests for (e.g. SPEC-001)'),
            frameworks: z
                .array(z.enum(['soc2', 'pci-dss', 'iso-42001', 'hipaa', 'gdpr']))
                .min(1)
                .describe(COMPLIANCE_TEST_FRAMEWORK_DESCRIBE),
            outputFormat: z
                .enum(['jest', 'gherkin', 'markdown'])
                .optional()
                .describe(TEST_FORMAT_DESCRIBE),
        },
        annotations: { title: 'Generate Compliance Tests', readOnlyHint: true },
    }, safeLicensed('generate_compliance_tests', async (args) => handleGenerateComplianceTests(args)));
    s.registerTool('compliance_coverage_report', {
        description: 'List all compliance controls for the requested frameworks, showing their IDs, ' +
            'titles, descriptions, and matching keywords. ' +
            'Use before generate_compliance_tests to understand which controls are available.',
        inputSchema: {
            projectPath: z
                .string()
                .min(1)
                .max(4096)
                .describe('Absolute path to the project root directory'),
            frameworks: z
                .array(z.enum(['soc2', 'pci-dss', 'iso-42001', 'hipaa', 'gdpr']))
                .min(1)
                .describe(COMPLIANCE_TEST_FRAMEWORK_DESCRIBE),
        },
        annotations: { title: 'Compliance Coverage Report', readOnlyHint: true },
    }, safeLicensed('compliance_coverage_report', (args) => Promise.resolve(handleComplianceCoverageReport(args))));
    // ── Security ─────────────────────────────────────────────────────────────────
    s.registerTool('security_check', {
        description: t('tools.security_check.description'),
        annotations: { readOnlyHint: true },
        inputSchema: {
            specId: SpecIdSchema.describe('Spec ID to analyze'),
            ...projectIdSchema,
            subcommand: z
                .enum(['analyze', 'score', 'drift'])
                .optional()
                .describe('analyze: OWASP gap list; score: security grade 0-100; drift: detect removed security criteria (default: analyze)'),
            codeFiles: z
                .array(z.string().max(4096))
                .max(1000)
                .optional()
                .describe('Absolute paths to code files to scan for insecure patterns'),
        },
    }, safeLicensed('security_check', withProject((args) => handleSecurityCheck(args))));
    // ── Audit trail ──────────────────────────────────────────────────────────────
    s.registerTool('audit_trail', {
        description: 'Query the immutable audit log for a project. Supports filtering by specId, userId, eventType, and date range. Returns events with SHA-256 chain integrity status.',
        annotations: { readOnlyHint: true },
        inputSchema: {
            ...projectIdSchema,
            specId: z
                .string()
                .max(500)
                .optional()
                .describe('Filter events by spec ID (e.g. SPEC-001).'),
            eventType: z
                .enum(AUDIT_EVENT_TYPES)
                .optional()
                .describe('Filter by event type. Valid values: spec.created, spec.updated, spec.status_changed, spec.approved, spec.drift_detected, spec.reconciled, tool.called, role.assigned.'),
            from: z
                .string()
                .optional()
                .describe('ISO-8601 start date (inclusive). Example: 2026-01-01T00:00:00Z'),
            to: z
                .string()
                .optional()
                .describe('ISO-8601 end date (inclusive). Example: 2026-12-31T23:59:59Z'),
            limit: z
                .number()
                .int()
                .min(1)
                .max(1000)
                .optional()
                .describe('Maximum number of events to return (default: 100, max: 1000).'),
        },
    }, safeLicensed('audit_trail', withProject((args) => handleAuditTrail(args))));
    s.registerTool('generate_attestation', {
        description: 'Generate a compliance attestation document for a specific spec. Includes full audit history, chain integrity verification, and event summary for SOC2/HIPAA/ISO27001 audits.',
        annotations: { readOnlyHint: true },
        inputSchema: {
            ...projectIdSchema,
            specId: z
                .string()
                .min(1)
                .max(500)
                .describe('Spec ID to generate attestation for (e.g. SPEC-042).'),
        },
    }, safeLicensed('generate_attestation', withProject((args) => handleGenerateAttestation(args))));
    s.registerTool('export_audit_trail', {
        description: 'Export the EU AI Act Article 12 immutable audit trail for every Planu tool invocation. ' +
            'Supports filtering by project, date range, or spec. Verifies SHA-256 chain integrity. ' +
            'Formats: json (default), json-ld (W3C PROV-O), csv.',
        annotations: { readOnlyHint: true },
        inputSchema: {
            projectPath: z
                .string()
                .max(4096)
                .describe('Absolute path to project root. Used to filter entries for this project.'),
            fromDate: z
                .string()
                .optional()
                .describe('ISO-8601 start date (inclusive). Example: 2026-01-01T00:00:00Z'),
            toDate: z
                .string()
                .optional()
                .describe('ISO-8601 end date (inclusive). Example: 2026-12-31T23:59:59Z'),
            specId: z
                .string()
                .max(500)
                .optional()
                .describe('Filter entries by spec ID (e.g. SPEC-001).'),
            format: z
                .enum(['json', 'json-ld', 'csv'])
                .optional()
                .describe('Output format. Valid values: json (default), json-ld (W3C PROV-O), csv.'),
            limit: z
                .number()
                .int()
                .min(1)
                .max(10000)
                .optional()
                .describe('Maximum number of entries to return.'),
        },
    }, safeLicensed('export_audit_trail', (args) => Promise.resolve(handleExportAuditTrail(args))));
    // ── Schema parity ────────────────────────────────────────────────────────────
    s.registerTool('audit_schema_type_parity', {
        description: 'Scans all register-*.ts tool files in the project for mismatches between Zod optional ' +
            'fields (.optional()) and TypeScript types (missing "?"). Also detects unsafe handler ' +
            'access (field.method() without null check). Returns a structured report with severity ' +
            'levels: "critical" (unsafe access) or "warning" (missing "?" in type).',
        annotations: { readOnlyHint: true },
        inputSchema: {
            projectPath: z
                .string()
                .min(1)
                .max(4096)
                .describe('Absolute path to the project root to scan. The auditor will look for src/tools/register-*.ts and src/types/**/*.ts.'),
        },
    }, safeLicensed('audit_schema_type_parity', async (args) => handleAuditSchemaParity(args)));
    s.registerTool('fix_schema_type_parity', {
        description: 'Automatically fixes Zod/TypeScript optional parity issues found by audit_schema_type_parity. ' +
            'Adds "?" to TypeScript type definitions for fields that are marked .optional() in Zod schemas. ' +
            'Use dryRun: true first to preview changes before applying them.',
        annotations: { readOnlyHint: false, destructiveHint: true },
        inputSchema: {
            projectPath: z
                .string()
                .min(1)
                .max(4096)
                .describe('Absolute path to the project root to fix.'),
            dryRun: z
                .boolean()
                .default(true)
                .describe('When true (default), shows what would change without writing any files. Set to false to apply the fixes.'),
        },
    }, safeLicensed('fix_schema_type_parity', async (args) => handleFixSchemaParity(args)));
    // ── Schema generator ─────────────────────────────────────────────────────────
    s.registerTool('generate_validation_schema', {
        description: 'Analyze a spec and generate validation schema code (Zod, Yup, or Valibot). ' +
            'Detects entities, fields, and types from the spec text using pattern matching. ' +
            'Unknown types are generated as z.unknown() with a TODO comment. ' +
            'Use library="all" to generate schemas for all three libraries at once.',
        inputSchema: {
            projectPath: z.string().min(1).max(4096).describe('Absolute path to the project root.'),
            specId: SpecIdSchema.describe('Spec ID to analyze (e.g. SPEC-042).'),
            library: z
                .enum(['zod', 'yup', 'valibot', 'all'])
                .describe('Schema library. Values: zod, yup, valibot, all.'),
            outputFormat: z
                .enum(['typescript', 'javascript'])
                .describe('Output format. Values: typescript, javascript.'),
            outputPath: z
                .string()
                .optional()
                .describe('Optional file path to write the generated schema to.'),
        },
        annotations: { title: 'Generate Validation Schema' },
    }, safeTracked('generate_validation_schema', (args) => handleGenerateValidationSchema(args)));
    s.registerTool('generate_api_client', {
        description: 'Generate a TypeScript API client from a spec. ' +
            'Detects API routes (GET /api/..., POST /api/..., etc.) from the spec text. ' +
            'Use framework="fetch" for a basic fetch wrapper, ' +
            '"tanstack-query" for React Query hooks.',
        inputSchema: {
            projectPath: z.string().min(1).max(4096).describe('Absolute path to the project root.'),
            specId: SpecIdSchema.describe('Spec ID to analyze (e.g. SPEC-042).'),
            framework: z
                .enum(['fetch', 'tanstack-query'])
                .describe('Client framework. Values: fetch, tanstack-query.'),
            outputPath: z
                .string()
                .optional()
                .describe('Optional file path to write the generated client to.'),
        },
        annotations: { title: 'Generate API Client' },
    }, safeTracked('generate_api_client', (args) => handleGenerateApiClient(args)));
    // ── TDD enforcement ───────────────────────────────────────────────────────────
    s.registerTool('configure_tdd_policy', {
        description: 'Configures TDD enforcement policy for the project: off (no enforcement), ' +
            'soft (warnings on missing tests), hard (blocks status transitions without tests).',
        inputSchema: {
            projectPath: z.string().describe('Absolute path to the project root'),
            mode: z
                .enum(['off', 'soft', 'hard'])
                .describe('TDD enforcement mode. Values: off, soft, hard'),
            coverageThreshold: z
                .number()
                .optional()
                .describe('Minimum coverage % for spec files (default 80)'),
            requireEdgeTests: z
                .boolean()
                .optional()
                .describe('Require edge-case tests for optional/boundary fields'),
        },
    }, async (params) => handleConfigureTddPolicy(params));
    s.registerTool('generate_edge_tests', {
        description: 'Generates TDD test stubs from spec acceptance criteria, or edge-case boundary ' +
            'tests from Zod schemas. Catches bugs like undefined optional fields, empty strings, empty arrays.',
        annotations: { readOnlyHint: true },
        inputSchema: {
            projectPath: z.string().describe('Absolute path to the project root'),
            specId: SpecIdSchema.optional().describe('Spec ID to generate acceptance criteria stubs from'),
            registerFile: z
                .string()
                .optional()
                .describe('Path to a register-*.ts file to generate Zod boundary tests from'),
        },
    }, async (params) => handleGenerateEdgeTests(params));
    s.registerTool('tdd_status', {
        description: 'Shows current TDD enforcement status: policy mode, test stub coverage, unresolved stubs per spec.',
        annotations: { readOnlyHint: true },
        inputSchema: {
            projectPath: z.string().describe('Absolute path to the project root'),
            specId: SpecIdSchema.optional().describe('Spec ID to check readiness/completion for'),
        },
    }, async (params) => handleTddStatus(params));
    // ── Coverage ─────────────────────────────────────────────────────────────────
    s.registerTool('spec_coverage', {
        description: 'Analyze how many acceptance criteria in a spec are backed by real test files in your project. ' +
            'Performs static analysis across 10 ecosystems (TypeScript, Python, Go, Java, Rust, Swift, C#, PHP, Ruby, Kotlin). ' +
            'Returns a per-criterion breakdown with coverage grade (A/B/C/D) and identifies untested criteria as risk items.',
        inputSchema: {
            ...projectIdSchema,
            specId: z
                .string()
                .min(1)
                .max(500)
                .describe('Spec ID to analyze (e.g., "SPEC-001"), or "all" to analyze every spec in the project'),
        },
        annotations: { title: 'Spec Test Coverage', readOnlyHint: true },
    }, safeLicensed('spec_coverage', withProject((args) => handleSpecCoverage(args))));
    // ── Eval skill ───────────────────────────────────────────────────────────────
    s.registerTool('eval_skill', {
        description: 'Evaluate the quality of a Claude skill file against a set of test scenarios. ' +
            "Reads the skill from .claude/skills/{skillName}.md and checks whether each scenario's " +
            'criteria appear in the skill content. ' +
            'Returns a pass rate, average score, per-scenario results, and recommendations for improvement.',
        inputSchema: {
            skillName: z
                .string()
                .min(1)
                .max(500)
                .describe('Skill file name without extension (e.g. "vitest") or with extension (e.g. "vitest.md").'),
            projectPath: z
                .string()
                .min(1)
                .max(4096)
                .describe('Absolute path to the project root. Skills are read from {projectPath}/.claude/skills/.'),
            testScenarios: z
                .array(evalScenarioSchema)
                .min(1)
                .describe('One or more test scenarios to evaluate the skill against.'),
        },
        annotations: { title: 'Evaluate Skill Quality', readOnlyHint: true },
    }, safeTracked('eval_skill', (args) => handleEvalSkill(args)));
    s.registerTool('eval_rule', {
        description: 'Evaluate the quality of a Claude rule file against a set of test scenarios. ' +
            "Reads the rule from .claude/rules/{ruleName}.md and checks whether each scenario's " +
            'criteria appear in the rule content. ' +
            'Returns a pass rate, average score, per-scenario results, and recommendations for improvement.',
        inputSchema: {
            ruleName: z
                .string()
                .min(1)
                .max(500)
                .describe('Rule file name without extension (e.g. "testing") or with extension (e.g. "testing.md").'),
            projectPath: z
                .string()
                .min(1)
                .max(4096)
                .describe('Absolute path to the project root. Rules are read from {projectPath}/.claude/rules/.'),
            testScenarios: z
                .array(evalScenarioSchema)
                .min(1)
                .describe('One or more test scenarios to evaluate the rule against.'),
        },
        annotations: { title: 'Evaluate Rule Quality', readOnlyHint: true },
    }, safeTracked('eval_rule', (args) => handleEvalRule(args)));
    // ── EARS criteria quality ─────────────────────────────────────────────────────
    s.registerTool('validate_criteria_quality', {
        description: 'Score acceptance criteria quality using EARS (Easy Approach to Requirements Syntax). ' +
            'Detects vague language, scores testability and specificity (0-10 each), ' +
            'identifies the EARS pattern, and suggests rewrites for low-scoring criteria. ' +
            'Provide either specId (reads from spec file) or criteria (raw text array). ' +
            'Returns per-criterion scores, overall grade (A-F), and rewrite suggestions.',
        inputSchema: {
            projectPath: z
                .string()
                .min(1)
                .max(4096)
                .describe('Absolute path to the project root where planu/ directory lives.'),
            specId: z
                .string()
                .min(1)
                .max(500)
                .optional()
                .describe('Spec ID to validate (e.g. SPEC-042). Reads criteria from the spec file.'),
            criteria: z
                .array(z.string().min(1).max(2000))
                .optional()
                .describe('Raw acceptance criteria text array to score. Used when specId is not provided.'),
        },
        annotations: { title: 'Validate Criteria Quality', readOnlyHint: true },
    }, safeTracked('validate_criteria_quality', async (args) => {
        const { projectPath, specId, criteria } = args;
        if (specId !== undefined) {
            const report = await lintSpec(specId, projectPath);
            return { content: [{ type: 'text', text: compactJson(report) }] };
        }
        if (criteria !== undefined && criteria.length > 0) {
            const scored = criteria.map(scoreCriterion);
            const overallScore = scored.length > 0
                ? Math.round((scored.reduce((sum, c) => sum + c.overallScore, 0) / scored.length) * 10) / 10
                : 0;
            const qualityWarnings = criteria
                .map((c) => ({ criterion: c, quality: checkCriteriaQuality(c) }))
                .filter(({ quality }) => quality.score < 50)
                .map(({ criterion, quality }) => ({
                criterion,
                score: quality.score,
                reason: quality.reason,
            }));
            return {
                content: [
                    {
                        type: 'text',
                        text: compactJson({ criteria: scored, overallScore, qualityWarnings }),
                    },
                ],
            };
        }
        return {
            content: [{ type: 'text', text: 'Provide either specId or criteria array.' }],
            isError: true,
        };
    }));
    s.registerTool('rewrite_criteria_ears', {
        description: 'Score a single acceptance criterion and generate 2 EARS-format rewrite suggestions. ' +
            'Returns current testability and specificity scores, detected vagueness flags, ' +
            'EARS pattern classification, and concrete rewrite alternatives. ' +
            'No LLM call — rewrites are template-driven for consistency and speed.',
        inputSchema: {
            projectPath: z.string().min(1).max(4096).describe('Absolute path to the project root.'),
            criterion: z
                .string()
                .min(1)
                .max(2000)
                .describe('The acceptance criterion text to score and rewrite.'),
        },
        annotations: { title: 'Rewrite Criteria as EARS', readOnlyHint: true },
    }, safeTracked('rewrite_criteria_ears', (args) => {
        const { criterion } = args;
        const scored = scoreCriterion(criterion);
        const pattern = detectEarsPattern(criterion);
        const rewrites = generateRewrites(criterion, pattern);
        return Promise.resolve({
            content: [{ type: 'text', text: compactJson({ ...scored, rewrites }) }],
        });
    }));
    s.registerTool('ears_lint', {
        description: 'Lint all specs in the project for EARS compliance and criteria quality. ' +
            'Returns project-wide average score, letter grade, and top 10 worst criteria ranked by score. ' +
            'Use minScore to filter and only report criteria below a threshold. ' +
            'Helpful for identifying which specs need the most improvement.',
        inputSchema: {
            projectPath: z
                .string()
                .min(1)
                .max(4096)
                .describe('Absolute path to the project root where planu/specs/ directory lives.'),
            minScore: z
                .number()
                .min(0)
                .max(10)
                .optional()
                .describe('Optional score threshold (0-10). Only report criteria with overallScore below this value. Defaults to 10 (report all criteria regardless of score).'),
        },
        annotations: { title: 'EARS Lint All Specs', readOnlyHint: true },
    }, safeTracked('ears_lint', async (args) => {
        const { projectPath, minScore } = args;
        const report = await lintAllSpecs(projectPath);
        const threshold = minScore ?? 10;
        const filteredWorst = report.topWorstCriteria.filter((c) => c.score < threshold);
        const summary = {
            projectPath: report.projectPath,
            totalSpecs: report.totalSpecs,
            averageScore: report.averageScore,
            grade: report.averageScore >= 9
                ? 'A'
                : report.averageScore >= 7
                    ? 'B'
                    : report.averageScore >= 5
                        ? 'C'
                        : report.averageScore >= 3
                            ? 'D'
                            : 'F',
            topWorstCriteria: filteredWorst,
            generatedAt: report.generatedAt,
        };
        return { content: [{ type: 'text', text: compactJson(summary) }] };
    }));
    // ── Spec lint ────────────────────────────────────────────────────────────────
    s.registerTool('define_spec_lint_rule', {
        description: 'Define a custom lint rule for specs. Rules check that specs satisfy structural or content conditions. ' +
            'Use "when" for a guard condition (rule only applies when guard is true), ' +
            '"then" for the condition that must be satisfied. ' +
            'Use appliesTo.all=true to apply to all specs, or appliesTo.tags to target specific tags.',
        inputSchema: {
            projectPath: z.string().min(1).max(4096).describe('Absolute path to the project root.'),
            name: z.string().min(1).max(200).describe('Human-readable name for the rule.'),
            description: z.string().min(1).max(500).describe('What this rule checks.'),
            when: LintConditionSchema.optional().describe('Optional guard condition. Rule only activates when this is true.'),
            then: LintConditionSchema.describe('Condition that MUST be satisfied.'),
            severity: z.enum(['warning', 'error']).describe('Severity. Values: warning, error.'),
            appliesTo: z
                .object({
                tags: z.array(z.string()).optional().describe('Spec tags this rule applies to.'),
                all: z.boolean().optional().describe('If true, applies to all specs.'),
            })
                .describe('Scope of the rule.'),
        },
        annotations: { title: 'Define Spec Lint Rule' },
    }, safeTracked('define_spec_lint_rule', (args) => handleDefineLintRule(args)));
    s.registerTool('list_spec_lint_rules', {
        description: 'List all custom lint rules defined for a project.',
        inputSchema: {
            projectPath: z.string().min(1).max(4096).describe('Absolute path to the project root.'),
        },
        annotations: { title: 'List Spec Lint Rules', readOnlyHint: true },
    }, safeTracked('list_spec_lint_rules', (args) => handleListLintRules(args)));
    s.registerTool('run_spec_lint', {
        description: 'Run all custom lint rules against specs in a project. ' +
            'If specId is provided, only that spec is linted. Otherwise all specs are checked. ' +
            'Returns a list of violations with severity, field, and message.',
        inputSchema: {
            projectPath: z.string().min(1).max(4096).describe('Absolute path to the project root.'),
            specId: z
                .string()
                .optional()
                .describe('Optional spec ID to lint (e.g. SPEC-042). Omit to lint all specs.'),
        },
        annotations: { title: 'Run Spec Lint', readOnlyHint: true },
    }, safeTracked('run_spec_lint', (args) => handleRunSpecLint(args)));
    s.registerTool('delete_spec_lint_rule', {
        description: 'Delete a custom lint rule by ID.',
        inputSchema: {
            projectPath: z.string().min(1).max(4096).describe('Absolute path to the project root.'),
            ruleId: z.string().min(1).max(100).describe('ID of the rule to delete (e.g. rule-abc123).'),
        },
        annotations: { title: 'Delete Spec Lint Rule', destructiveHint: true },
    }, safeTracked('delete_spec_lint_rule', (args) => handleDeleteLintRule(args)));
    // ── Readiness ────────────────────────────────────────────────────────────────
    s.registerTool('check_readiness', {
        description: t('tools.check_readiness.description'),
        inputSchema: {
            ...projectIdSchema,
            specId: SpecIdSchema.describe('Spec ID to evaluate'),
            mode: z
                .enum(['strict', 'lenient'])
                .optional()
                .describe('Evaluation mode: strict (default) requires score >=70; lenient requires >=60'),
        },
        outputSchema: CheckReadinessOutputSchema,
    }, safeLicensed('check_readiness', withProject((args) => handleCheckReadiness(args))));
    // ── SPEC-961: Architecture lint ─────────────────────────────────────────────
    s.registerTool('architecture_lint', {
        description: 'Scan project codebase for Clean Architecture and SOLID principle violations. Reports handler bloat, layer violations, and missing repository patterns.',
        inputSchema: {
            ...projectIdSchema,
            projectPath: z.string().min(1).describe('Absolute path to project root to scan'),
            maxHandlerLines: z
                .number()
                .min(1)
                .max(500)
                .optional()
                .describe('Max lines per handler file (default: 50)'),
            exclude: z
                .array(z.string())
                .optional()
                .describe('Glob patterns to exclude (default: node_modules, .git, dist)'),
            failFast: z.boolean().optional().describe('Stop on first error (default: false)'),
        },
    }, safeLicensed('architecture_lint', withProject((args) => handleArchitectureLint(args))));
    // ── SPEC-962: SOLID check ───────────────────────────────────────────────────
    s.registerTool('solid_check', {
        description: 'Scan project codebase for SOLID principle violations. Reports SRP, OCP, LSP, ISP, and DIP issues with a scored report (0-100) and per-principle breakdown.',
        inputSchema: {
            ...projectIdSchema,
            projectPath: z.string().min(1).describe('Absolute path to project root to scan'),
            exclude: z
                .array(z.string())
                .optional()
                .describe('Glob patterns to exclude (default: node_modules, .git, dist)'),
            failFast: z.boolean().optional().describe('Stop on first error (default: false)'),
        },
    }, safeLicensed('solid_check', withProject((args) => handleSolidCheck({
        projectPath: args.projectPath,
        exclude: args.exclude,
        failFast: args.failFast,
    }))));
    // ── SPEC-965: Verify Integrations ───────────────────────────────────────────
    s.registerTool('verify_integrations', {
        description: 'Ping configured external API integrations to verify they are alive and responding correctly before marking a spec as done.',
        inputSchema: {
            ...projectIdSchema,
            projectPath: z
                .string()
                .min(1)
                .describe('Absolute path to project root (looks for .planu/integrations.json)'),
            endpoints: z
                .array(z.object({
                name: z.string().min(1).describe('Human-readable endpoint name'),
                url: z.url().describe('URL to ping'),
                method: z
                    .enum(['GET', 'HEAD', 'POST'])
                    .optional()
                    .describe('HTTP method (default: HEAD)'),
                expectedStatus: z
                    .number()
                    .min(100)
                    .max(599)
                    .optional()
                    .describe('Expected HTTP status code (default: 200)'),
                authorization: z.string().optional().describe('Optional Authorization header value'),
                expectedContentType: z
                    .string()
                    .optional()
                    .describe('Optional expected Content-Type response header'),
            }))
                .optional()
                .describe('Explicit endpoints to verify (overrides .planu/integrations.json)'),
            timeout: z
                .number()
                .min(1000)
                .max(60000)
                .optional()
                .describe('Timeout per request in ms (default: 10000)'),
            failFast: z
                .boolean()
                .optional()
                .describe('Fail on first failing endpoint (default: false)'),
        },
    }, safeLicensed('verify_integrations', withProject((args) => handleVerifyIntegrations({
        projectPath: args.projectPath,
        endpoints: args.endpoints,
        timeout: args.timeout,
        failFast: args.failFast,
    }))));
    // ── SPEC-971: Spec Obesity Healer ────────────────────────────────────────────
    s.registerTool('spec_obesity_healer', {
        description: 'Heal bloated specs (>500 lines) by removing auto-generated generic criteria, HTML scrape artifacts, and duplicate checklists. ' +
            'Runs in dry-run mode by default; set apply=true to write changes. Generates backups before modification.',
        inputSchema: {
            projectPath: z.string().min(1).max(4096).describe('Absolute path to the project root.'),
            threshold: z
                .number()
                .min(100)
                .max(5000)
                .optional()
                .describe('Line threshold to flag a spec as obese (default: 500)'),
            apply: z.boolean().optional().describe('Apply changes to disk. Default false (dry-run)'),
            cutoffDate: z
                .string()
                .optional()
                .describe('ISO date. Specs created after this date are skipped (default: 2026-04-01)'),
        },
        annotations: { title: 'Spec Obesity Healer', readOnlyHint: false, destructiveHint: true },
    }, safeTracked('spec_obesity_healer', (args) => handleSpecObesityHealer({
        projectPath: args.projectPath,
        threshold: args.threshold,
        apply: args.apply,
        cutoffDate: args.cutoffDate,
    })));
    s.registerTool('package_handoff', {
        description: t('tools.package_handoff.description'),
        inputSchema: {
            ...projectIdSchema,
            specId: SpecIdSchema.describe('Spec ID to generate handoff package for'),
        },
    }, safeLicensed('package_handoff', withProject((args) => handlePackageHandoff(args))));
    // ── Approval ─────────────────────────────────────────────────────────────────
    s.registerTool('configure_approval_policy', {
        description: 'Define the approval policy for a project: how many approvals are required, ' +
            'the review SLA in hours, optional escalation contact, and required reviewer roles.',
        inputSchema: {
            projectPath: z.string().min(1).max(4096).describe('Absolute path to the project root.'),
            requiredApprovals: z
                .number()
                .int()
                .min(1)
                .describe('Minimum number of approvals needed before a spec can be marked approved.'),
            reviewSLAHours: z
                .number()
                .min(0)
                .describe('Maximum hours a spec may remain in review before SLA breach is flagged.'),
            escalateTo: z
                .string()
                .optional()
                .describe('Email or username to escalate to when SLA is breached (e.g. "team-lead@example.com").'),
            requireRoles: z
                .array(z.string())
                .optional()
                .describe('Roles allowed to approve specs. If empty or omitted, any reviewer can approve.'),
        },
        annotations: { title: 'Configure Approval Policy', destructiveHint: false },
    }, safeLicensed('configure_approval_policy', (args) => handleConfigureApprovalPolicy(args)));
    s.registerTool('issue_reviewer_token', {
        description: '[SPEC-722] Issue a signed reviewer token for approve_spec. ' +
            'Must be called from a DIFFERENT session than the one that created the spec. ' +
            "Pass the plannerToken emitted by create_spec and this session's identity fields.",
        inputSchema: {
            projectPath: z.string().min(1).max(4096).describe('Absolute path to the project root.'),
            plannerToken: z
                .string()
                .min(1)
                .describe('Planner token emitted by create_spec for this spec.'),
            sessionId: z
                .string()
                .min(1)
                .describe('Session ID of the current reviewer session (unique per LLM invocation).'),
            modelId: z.string().min(1).describe('Model ID of the reviewer (e.g. "claude-opus-4-5").'),
            host: z
                .string()
                .min(1)
                .describe('Host/agent platform identifier (e.g. "claude-code", "cursor").'),
        },
        annotations: { title: 'Issue Reviewer Token', destructiveHint: false },
    }, safeLicensed('issue_reviewer_token', (args) => handleIssueReviewerToken(args)));
    s.registerTool('approve_spec', {
        description: '[SPEC-722] Record that a reviewer has approved a spec. ' +
            'Requires a signed reviewerToken (from issue_reviewer_token) and the plannerToken ' +
            'emitted by create_spec, unless legacyReviewMode is set in planu.json (deprecated). ' +
            'When the required number of approvals is reached, the approval gate opens.',
        inputSchema: {
            projectPath: z.string().min(1).max(4096).describe('Absolute path to the project root.'),
            specId: SpecIdSchema.describe('Spec ID to approve (e.g. SPEC-042).'),
            reviewer: z
                .string()
                .describe('Name, email, or username of the reviewer approving the spec.'),
            role: z
                .string()
                .optional()
                .describe('Role of the reviewer (e.g. "tech-lead", "architect").'),
            comment: z.string().optional().describe('Optional approval comment or notes.'),
            reviewerToken: z
                .string()
                .optional()
                .describe('[SPEC-722] Signed reviewer token issued by issue_reviewer_token. ' +
                'Required unless legacyReviewMode is configured in planu.json.'),
            plannerToken: z
                .string()
                .optional()
                .describe('[SPEC-722] Planner token emitted by create_spec. ' +
                'Required when reviewerToken is provided.'),
            multiTeammateReviewId: z
                .string()
                .optional()
                .describe('[SPEC-722] Multi-teammate review panel ID. ' +
                'Allows approval from the same model if the panel was heterogeneous.'),
        },
        annotations: { title: 'Approve Spec', destructiveHint: true },
    }, safeLicensed('approve_spec', (args) => handleApproveSpec(args)));
    s.registerTool('request_changes', {
        description: 'Request changes on a spec. The spec is moved back to draft and prior approvals are cleared. ' +
            'A comment explaining the required changes is mandatory.',
        inputSchema: {
            projectPath: z.string().min(1).max(4096).describe('Absolute path to the project root.'),
            specId: z
                .string()
                .min(1)
                .max(500)
                .describe('Spec ID to request changes on (e.g. SPEC-042).'),
            reviewer: z
                .string()
                .describe('Name, email, or username of the reviewer requesting changes.'),
            comment: z
                .string()
                .min(1)
                .describe('Required explanation of what needs to change before re-approval.'),
            role: z.string().optional().describe('Role of the reviewer (e.g. "tech-lead", "qa").'),
        },
        annotations: { title: 'Request Changes', destructiveHint: true },
    }, safeLicensed('request_changes', (args) => handleRequestChanges(args)));
    s.registerTool('approval_status', {
        description: 'Show the current approval status for a spec: who approved, how many are needed, ' +
            'time in review, and SLA breach warning if applicable.',
        inputSchema: {
            projectPath: z.string().min(1).max(4096).describe('Absolute path to the project root.'),
            specId: SpecIdSchema.describe('Spec ID to check (e.g. SPEC-042).'),
        },
        annotations: { title: 'Approval Status', readOnlyHint: true },
    }, safeTracked('approval_status', (args) => handleApprovalStatus(args)));
    // ── Governance ───────────────────────────────────────────────────────────────
    s.registerTool('data_governance', {
        description: t('tools.data_governance.description'),
        annotations: { readOnlyHint: true },
        inputSchema: {
            ...projectIdSchema,
            subcommand: z
                .enum(['detect', 'retention-policy', 'privacy-notice', 'audit'])
                .describe('detect: find PII in a spec; retention-policy: generate retention policy for a model; privacy-notice: draft technical privacy notice for the project; audit: list all specs with PII and their compliance status'),
            specId: z
                .string()
                .max(500)
                .optional()
                .describe('Spec ID to analyze (required for detect subcommand)'),
            modelName: z
                .string()
                .max(500)
                .optional()
                .describe('Model name for retention-policy subcommand (default: User)'),
            specContent: z
                .string()
                .max(1_000_000)
                .optional()
                .describe('Raw spec content to analyze when specId is not available'),
        },
    }, safeLicensed('data_governance', withProject((args) => handleDataGovernance(args))));
    s.registerTool('legal_compliance_report', {
        description: 'Generate a legal compliance report for the project. ' +
            'Shows detected legal domains, applicable regulations by country, ' +
            'and per-spec compliance status. Use this to understand which laws ' +
            'and regulations apply to your project based on industry, target countries, and data types.',
        annotations: { readOnlyHint: true },
        inputSchema: {
            ...projectIdSchema,
        },
    }, safeLicensed('legal_compliance_report', withProject((args) => handleLegalComplianceReport(args))));
    // ── SPEC-322: API Contract Validation ────────────────────────────────────────
    s.registerTool('validate_api_contract', {
        description: 'Validate an OpenAPI 3.x/Swagger or GraphQL SDL contract against the actual implementation in a project. ' +
            'Scans source files for route handlers (Express, Fastify, Hono, Next.js API routes, FastAPI) or ' +
            'GraphQL resolvers and reports: missing implementations, undocumented endpoints, schema drift, and ' +
            'breaking changes. Useful for keeping API documentation in sync with the codebase.',
        annotations: {
            title: 'Validate API Contract vs Implementation',
            readOnlyHint: true,
            destructiveHint: false,
        },
        inputSchema: {
            projectPath: z
                .string()
                .min(1)
                .max(4096)
                .describe('Absolute path to the project root to scan for route implementations.'),
            contractPath: z
                .string()
                .min(1)
                .max(4096)
                .describe('Absolute path to the API contract file. For OpenAPI: .json or .yaml file. For GraphQL: .graphql, .gql, or .sdl file.'),
            contractFormat: z
                .enum(['openapi', 'graphql', 'auto'])
                .optional()
                .describe('Format of the contract file. Valid values: openapi, graphql, auto. auto (default) detects format from the file extension: .json/.yaml → openapi, .graphql/.gql/.sdl → graphql.'),
        },
    }, safeTracked('validate_api_contract', async (args) => handleValidateApiContract({
        projectPath: args.projectPath,
        contractPath: args.contractPath,
        contractFormat: args.contractFormat,
    })));
    // ── SPEC-323: TDD Scaffold & Coverage Gap ────────────────────────────────────
    s.registerTool('tdd_scaffold', {
        description: 'Generate a test-first TDD scaffold from spec acceptance criteria. Creates a failing test stub for each "- [ ] criterion" in spec.md and an implementation skeleton. Supports vitest, jest, mocha, pytest, and junit — auto-detected from package.json or build files. Use this BEFORE writing implementation code.',
        inputSchema: {
            specPath: z
                .string()
                .min(1)
                .max(4096)
                .describe('Absolute path to the spec directory (e.g. /project/planu/specs/SPEC-042-payments) or directly to spec.md.'),
            projectPath: z
                .string()
                .min(1)
                .max(4096)
                .describe('Absolute path to the project root. Used to detect test framework.'),
        },
    }, safeTracked('tdd_scaffold', async (args) => handleTddScaffold({ specPath: args.specPath, projectPath: args.projectPath })));
    s.registerTool('coverage_gap_analyzer', {
        description: 'Analyze which acceptance criteria from a spec are NOT covered by existing tests. Scans tests/**/*.test.ts and similar patterns, maps each AC to test content via keyword matching, and ranks untested ACs by risk domain: auth/payment/data-integrity/security = HIGH, api/notification = MEDIUM, ui/cosmetic = LOW. Returns a coverage score and actionable recommendations.',
        annotations: { readOnlyHint: true },
        inputSchema: {
            specPath: z
                .string()
                .min(1)
                .max(4096)
                .describe('Absolute path to the spec directory or spec.md file to analyze.'),
            projectPath: z
                .string()
                .min(1)
                .max(4096)
                .describe('Absolute path to the project root. Test files are discovered relative to this.'),
        },
    }, safeTracked('coverage_gap_analyzer', async (args) => handleCoverageGapAnalyzer({ specPath: args.specPath, projectPath: args.projectPath })));
    s.registerTool('run_mutation_hints', {
        description: 'Generate a mutation testing configuration file for the detected project stack. Detects vitest → stryker.config.mjs, pytest → setup.cfg (mutmut), junit → pom.xml pitest plugin. Returns the config file content and the CI step command to add to your pipeline. Run after achieving >=90% test coverage.',
        inputSchema: {
            projectPath: z
                .string()
                .min(1)
                .max(4096)
                .describe('Absolute path to the project root. Used to detect language and test framework.'),
        },
    }, safeTracked('run_mutation_hints', async (args) => handleRunMutationHints({ projectPath: args.projectPath })));
    // ── SPEC-331: Multi-Agent Orchestration Plan (migrated to skill, SPEC-658) ────
    // Tool stays registered for backward compat; callers receive a deprecation notice.
    registerFromEntries(s, [
        makeDeprecationStub('generate_orchestration_plan', '.claude/skills/parallel-safe.md', 'parallel-safe'),
    ]);
}
//# sourceMappingURL=group-quality-compliance.js.map