// SPEC-597-EXEMPT: canonical mapping (npm-package↔framework, keyword catalogue)
// — must enumerate framework names by design; no detection logic to migrate.
/* eslint-disable max-lines, max-lines-per-function -- 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-infra.ts — Declarative registry: infrastructure & platform tools.
//
// Phase 2 (refactor/registry-phase2): consolidates register-*.ts files for:
//   webhook, dashboard, usage, license, auto-update, deploy, cross-repo-search, browser-validate,
//   workers, plugins, registry, skill-registry, tool-groups, rbac, workspace, memory-config,
//   onboarding, docs-registry, well-known
import { z } from 'zod';
// ── safe handlers ────────────────────────────────────────────────────────────
import { safe, safeLicensed, safeTracked } from '../safe-handler.js';
// ── License tools (register-license-tools.ts) ───────────────────────────────
import { handleActivateLicense, handleDeactivateLicense } from '../activate-license.js';
import { handleLicenseStatus } from '../license-status.js';
import { LicenseStatusOutputSchema, SpecIdSchema } from '../schemas/index.js';
// ── OAuth tools (register-oauth-tools.ts / register-configure-oauth-tool.ts) ─
import { handleStartOAuthFlow, handleOAuthStatus, StartOAuthFlowInputSchema, OAuthStatusInputSchema, } from '../oauth-handler.js';
import { handleConfigureOAuth } from '../configure-oauth-handler.js';
// ── Usage tools (register-usage-tools.ts) ────────────────────────────────────
import { handleUsageStats } from '../usage-stats.js';
import { handleUsageReport } from '../usage-report.js';
import { handleToolUsageReport } from '../tool-usage-report.js';
// ── Webhook tools (register-webhook-tools.ts) ────────────────────────────────
import { handleStartWebhook, handleStopWebhook, handleWebhookStatus } from '../webhook.js';
// ── Outbound webhook tools (register-outbound-webhook-tools.ts) ──────────────
import { handleConfigureWebhook, handleOutboundWebhookStatus, handleRemoveWebhook, } from '../outbound-webhook-handler.js';
// ── Dashboard tools (register-dashboard-tools.ts) ────────────────────────────
import { handleStartDashboard, handleStopDashboard } from '../dashboard.js';
// ── Auto-update tools (register-auto-update-tools.ts) ────────────────────────
import { handleCheckUpdateStatus, handleEnableAutoUpdate, handlePinVersion, } from '../auto-update-handler.js';
// ── Deploy tools (register-deploy-tools.ts) ──────────────────────────────────
import { handleConfigureDeployTarget } from '../deploy/configure-target.js';
import { handleDeploySpec } from '../deploy/deploy-spec.js';
import { handleDeployStatus } from '../deploy/deploy-status.js';
// ── Cross-repo search tools (register-cross-repo-search-tools.ts) ─────────────
import { handleRegisterProjectPath, handleUnregisterProjectPath, handleListRegisteredProjects, handleSearchAllProjects, } from '../cross-repo-search-handler.js';
// ── Browser validate tools (register-browser-validate-tools.ts) ──────────────
import { handleValidateBrowser } from '../browser-validate-handler.js';
// ── Worker tools (register-worker-tools.ts) ───────────────────────────────────
import { WorkerStatusInputSchema, ConfigureWorkersInputSchema } from '../schemas/workers-schema.js';
import { handleWorkerStatus } from '../worker-status-handler.js';
import { handleConfigureWorkers } from '../configure-workers-handler.js';
// ── Plugin tools (register-plugin-tools.ts) ──────────────────────────────────
import { handleManagePlugins } from '../manage-plugins-handler.js';
import { handleScaffoldPlugin } from '../scaffold-plugin-handler.js';
import { ManagePluginsSchema, ScaffoldPluginSchema } from '../schemas/plugins-schemas.js';
// ── Token optimizer tools (register-token-optimizer-tools.ts) ────────────────
import { handleSuggestTokenOptimizer, handleTokenOptimizerStatus, } from '../ecosystem-tools-handler.js';
// ── Framework registry tools (register-registry-tools.ts) ───────────────────
import { handleCheckSpecAccuracy } from '../check-spec-accuracy.js';
import { handleUpdateRegistry } from '../update-registry.js';
import { CheckSpecAccuracySchema, UpdateRegistrySchema } from '../schemas/registry.js';
// ── Skill registry tools (register-skill-registry-tools.ts) ─────────────────
import { SkillSearchInputSchema, SkillInstallInputSchema, } from '../schemas/skill-registry-schemas.js';
import { handleSkillSearch, handleSkillInstall } from '../skill-registry/index.js';
// ── Well-known registry tools (register-well-known-tools.ts) ─────────────────
import { handlePublishRegistry } from '../publish-registry.js';
import { handleDiscoverRegistry } from '../discover-registry.js';
import { handleRegistryLogin, handleRegistryLogout, handleRegistryPublish, handleRegistrySearch, handleRegistryInstall, handleRegistryWhoami, } from '../registry/index.js';
// ── Tool groups (register-tool-groups.ts) ─────────────────────────────────────
import { handleActivateGroup, handleListGroups } from '../tool-group-handler.js';
// ── RBAC tools (register-rbac-tools.ts) ──────────────────────────────────────
import { ConfigureRolesInputSchema, AssignRoleInputSchema } from '../schemas/rbac.js';
import { handleConfigureRoles } from '../configure-roles-handler.js';
import { handleAssignRole } from '../assign-role-handler.js';
// ── Well-known tools schemas ──────────────────────────────────────────────────
import { PublishRegistryInputSchema, DiscoverRegistryInputSchema } from '../schemas/well-known.js';
// ── Workspace tools (register-workspace-tools.ts) ────────────────────────────
import { handleAuditClaudeConfig } from '../audit-claude-config.js';
import { handleGenerateDocsSite } from '../generate-docs-site.js';
// ── Memory config tools (register-memory-config-tools.ts) ────────────────────
import { handleConfigureMemory, handleMemoryStatus } from '../memory-config-handler.js';
// ── Code graph tools (SPEC-376 / SPEC-1060) ─────────────────────────────────
import { handleConfigureCodeGraph, handleCodeGraphStatus } from '../code-graph-handler.js';
// ── Onboarding tools (register-onboarding-tools.ts) ──────────────────────────
import { handleStartOnboarding, handleGuidedTour, handleOnboardingChecklist, handleQuickStart, handleOnboardingProgress, } from '../onboarding-handler.js';
// ── Docs registry tools (register-docs-registry-tools.ts) ────────────────────
import { handleValidateDocsRegistry, handleDiscoverDocsUrl } from '../docs-registry-handler.js';
// ── GC tools (SPEC-581) ───────────────────────────────────────────────────────
import { handleGcDataProjects } from '../gc-data-projects.js';
const SUPPORTED_EVENTS = [
    'spec.created',
    'spec.approved',
    'spec.status_changed',
    'spec.done',
    'spec.drift_detected',
    'feedback.submitted',
    'validate.failed',
];
const deployProviderEnum = z
    .enum(['vercel', 'netlify', 'cloudflare-pages', 'fly', 'railway'])
    .describe('Deploy provider: vercel | netlify | cloudflare-pages | fly | railway');
export function registerInfraGroupTools(server) {
    // ── License tools ──────────────────────────────────────────────────────────
    server.registerTool('activate_license', {
        description: 'Activate a Planu license key to unlock Pro/Team/Enterprise features. ' +
            'Get your key at https://planu.dev/pricing.',
        inputSchema: {
            licenseKey: z
                .string()
                .max(500)
                .describe('License key from Lemon Squeezy (e.g. XXXXXXXX-XXXX-...)'),
            instanceName: z
                .string()
                .max(500)
                .optional()
                .describe('Optional name for this machine (defaults to hostname)'),
        },
        annotations: { title: 'Activate License', readOnlyHint: false },
    }, safe(async (args) => handleActivateLicense(args)));
    server.registerTool('deactivate_license', {
        description: 'Deactivate your Planu license on this machine. ' +
            'Frees up the activation slot so you can use it elsewhere.',
        inputSchema: {
            confirm: z
                .boolean()
                .describe('Set to true to confirm deactivation. This reverts to the Free plan.'),
        },
        annotations: { title: 'Deactivate License', destructiveHint: true },
    }, safe(async (args) => handleDeactivateLicense(args)));
    server.registerTool('license_status', {
        description: 'Show your current Planu plan, limits, usage, and upgrade options.',
        inputSchema: {
            projectId: z
                .string()
                .max(500)
                .optional()
                .describe('Optional project ID to include project-specific usage'),
        },
        outputSchema: LicenseStatusOutputSchema,
        annotations: { title: 'License Status', readOnlyHint: true },
    }, safe(async (args) => handleLicenseStatus(args)));
    // ── OAuth tools ────────────────────────────────────────────────────────────
    server.registerTool('start_oauth_flow', {
        description: 'Start guided OAuth setup for supported integrations such as Figma, GitHub, Sentry, or Supabase.',
        inputSchema: StartOAuthFlowInputSchema,
        annotations: { title: 'Start OAuth Flow', readOnlyHint: false },
    }, safeLicensed('start_oauth_flow', async (args) => handleStartOAuthFlow(args)));
    server.registerTool('oauth_status', {
        description: 'Show which OAuth-backed integrations are connected for this project.',
        inputSchema: OAuthStatusInputSchema,
        annotations: { title: 'OAuth Status', readOnlyHint: true },
    }, safeLicensed('oauth_status', async (args) => handleOAuthStatus(args)));
    server.registerTool('configure_oauth', {
        description: 'Inspect Planu MCP OAuth/API-key auth status or test a bearer token without exposing secrets.',
        inputSchema: {
            action: z.enum(['status', 'test']).describe('Action: status | test'),
            token: z.string().max(4096).optional().describe('Bearer token to test when action=test'),
        },
        annotations: { title: 'Configure OAuth', readOnlyHint: true },
    }, safe(async (args) => handleConfigureOAuth(args)));
    // ── Usage tools ────────────────────────────────────────────────────────────
    server.registerTool('usage_stats', {
        description: 'View your Planu usage statistics — tool calls, top tools, remaining daily calls. ' +
            'Helps you understand how much of your plan you are using. ' +
            'For the last 30 days with no parameters prefer resource: planu://usage/stats.',
        inputSchema: {
            period: z
                .enum(['7d', '30d', 'all'])
                .optional()
                .describe('Time period to analyze. Type: 7d | 30d | all. Defaults to 30d.'),
        },
        annotations: { title: 'Usage Statistics', readOnlyHint: true },
    }, safe(async (args) => handleUsageStats(args)));
    server.registerTool('usage_report', {
        description: 'Generate a full usage report for your Planu installation. ' +
            'Available in JSON (for integrations) or Markdown (for reading).',
        inputSchema: {
            format: z
                .enum(['json', 'markdown'])
                .optional()
                .describe('Output format. Type: json | markdown. Defaults to markdown.'),
        },
        annotations: { title: 'Usage Report', readOnlyHint: true },
    }, safe(async (args) => handleUsageReport(args)));
    server.registerTool('tool_usage_report', {
        description: 'Analyze which of the 461 Planu tools are actually used. ' +
            'Returns top tools by call count (with trend), low-usage tools (1-2 calls), ' +
            'zero-usage tools (never called in the window), and common A→B tool sequences ' +
            'within 60 seconds of each other. Useful for identifying dead code and UX bottlenecks.',
        inputSchema: {
            days: z
                .number()
                .int()
                .min(1)
                .max(365)
                .optional()
                .describe('Time window in days to analyze (default: 30).'),
            projectPath: z
                .string()
                .max(4096)
                .optional()
                .describe('Optional project path for context (not yet used for filtering).'),
        },
        annotations: { title: 'Tool Usage Report', readOnlyHint: true },
    }, safe(async (args) => handleToolUsageReport(args)));
    // ── Webhook tools (inbound) ────────────────────────────────────────────────
    server.registerTool('start_webhook', {
        description: 'Start a local HTTP server to receive Lemon Squeezy webhook events. ' +
            'Configure the server URL in your Lemon Squeezy dashboard under Settings > Webhooks.',
        inputSchema: {
            port: z
                .number()
                .int()
                .min(1)
                .max(65535)
                .optional()
                .describe('Port to listen on (default: 2358)'),
            secret: z
                .string()
                .optional()
                .describe('Webhook secret from Lemon Squeezy dashboard for HMAC-SHA256 signature validation'),
        },
        annotations: { title: 'Start Webhook Server', readOnlyHint: false },
    }, safe((args) => handleStartWebhook(args)));
    server.registerTool('stop_webhook', {
        description: 'Stop the running Lemon Squeezy webhook server.',
        inputSchema: {
            force: z
                .boolean()
                .optional()
                .describe('Force stop even if events are being processed (default: false)'),
        },
        annotations: { title: 'Stop Webhook Server', destructiveHint: false },
    }, safe((args) => handleStopWebhook(args)));
    server.registerTool('webhook_status', {
        description: 'Show whether the webhook server is running and display recently received events.',
        inputSchema: {
            includeEvents: z
                .boolean()
                .optional()
                .describe('Include recent event log in the output (default: true)'),
        },
        annotations: { title: 'Webhook Status', readOnlyHint: true },
    }, safe((args) => Promise.resolve(handleWebhookStatus(args))));
    // ── Outbound webhook tools ─────────────────────────────────────────────────
    server.registerTool('configure_webhook', {
        description: 'Register an outbound webhook endpoint to receive Planu spec lifecycle events. ' +
            'Payloads are signed with HMAC-SHA256 (X-Planu-Signature header) using the provided secret. ' +
            'Retry logic: 3 attempts with exponential backoff (1s, 5s, 30s). ' +
            'Events: spec.created | spec.approved | spec.status_changed | spec.done | ' +
            'spec.drift_detected | feedback.submitted | validate.failed',
        inputSchema: {
            projectPath: z.string().min(1).max(4096).describe('Absolute path to the project directory'),
            url: z
                .url()
                .max(2048)
                .describe('HTTPS endpoint URL that will receive webhook POST requests'),
            secret: z
                .string()
                .describe('Shared secret (min 8 chars) used to sign payloads with HMAC-SHA256. ' +
                'Verify X-Planu-Signature on your endpoint.'),
            events: z
                .array(z.enum(SUPPORTED_EVENTS))
                .optional()
                .describe('Events to subscribe to. Valid values: ' +
                SUPPORTED_EVENTS.join(', ') +
                '. Leave empty to receive all events.'),
            label: z
                .string()
                .optional()
                .describe('Optional human-readable label to identify this webhook'),
        },
        annotations: {
            title: 'Configure Outbound Webhook',
            destructiveHint: true,
            readOnlyHint: false,
        },
    }, safe((args) => handleConfigureWebhook(args)));
    server.registerTool('outbound_webhook_status', {
        description: 'List all configured outbound webhooks and their delivery status. ' +
            'Shows health (healthy/failing), last delivery result, and success rate. ' +
            'Use webhookId to inspect a specific webhook in detail.',
        inputSchema: {
            projectPath: z.string().min(1).max(4096).describe('Absolute path to the project directory'),
            webhookId: z
                .string()
                .optional()
                .describe('ID of a specific webhook to inspect (from configure_webhook output)'),
        },
        annotations: { title: 'Outbound Webhook Status', readOnlyHint: true },
    }, safe((args) => handleOutboundWebhookStatus(args)));
    server.registerTool('remove_webhook', {
        description: 'Deactivate an outbound webhook so it no longer receives events.',
        inputSchema: {
            projectPath: z.string().min(1).max(4096).describe('Absolute path to the project directory'),
            webhookId: z
                .string()
                .describe('ID of the webhook to deactivate (from configure_webhook or outbound_webhook_status)'),
        },
        annotations: {
            title: 'Remove Outbound Webhook',
            destructiveHint: true,
            readOnlyHint: false,
        },
    }, safe((args) => handleRemoveWebhook(args)));
    // ── Dashboard tools ────────────────────────────────────────────────────────
    server.registerTool('start_dashboard', {
        description: 'Start a local web dashboard at http://localhost:5050 (or a custom port) that shows ' +
            'all your projects, specs, progress bars, and health metrics. ' +
            'Open the URL in your browser after calling this tool.',
        inputSchema: {
            port: z
                .number()
                .int()
                .min(1024)
                .max(65535)
                .optional()
                .describe('Port to serve the dashboard on (default: 5050, range: 1024-65535). Falls back to port+1, +2, +3 if busy.'),
            open_browser: z
                .boolean()
                .optional()
                .describe('Auto-open the dashboard URL in the default browser (default: false)'),
        },
        annotations: { title: 'Start Dashboard', readOnlyHint: true },
    }, safe(async (args) => handleStartDashboard(args)));
    server.registerTool('stop_dashboard', {
        description: 'Stop the Planu web dashboard and release the port.',
        inputSchema: {},
        annotations: { title: 'Stop Dashboard', readOnlyHint: false },
    }, safe(async (args) => handleStopDashboard(args)));
    // ── Auto-update tools ──────────────────────────────────────────────────────
    server.registerTool('check_update_status', {
        description: 'Check whether a newer version of Planu is available on npm. ' +
            'Shows the current version, latest version, install method (npx/global/local), ' +
            'and whether auto-update is active. No parameters required.',
        inputSchema: {},
        annotations: { readOnlyHint: true },
    }, safe(async (_args) => handleCheckUpdateStatus()));
    server.registerTool('enable_auto_update', {
        description: 'Enable automatic updates for Planu by switching the MCP config to use ' +
            '"npx -y @planu/cli@latest". Detects the config file automatically (~/.claude.json or ' +
            'Claude Desktop config). Creates a backup before any modification. ' +
            'Call with confirm=false (default) to preview the change; confirm=true to apply it.',
        inputSchema: {
            confirm: z
                .boolean()
                .optional()
                .describe('Set to true to apply the config change immediately. ' +
                'Defaults to false (preview only, no changes made).'),
            configPath: z
                .string()
                .max(4096)
                .optional()
                .describe('Override the path to the MCP config file. ' +
                'Useful for custom setups. If omitted, the config is auto-detected.'),
        },
        annotations: { readOnlyHint: false },
    }, safe(async (args) => handleEnableAutoUpdate(args)));
    server.registerTool('pin_version', {
        description: 'Pin Planu to a specific version in the MCP config (e.g. "1.21.0"). ' +
            'Useful for teams that need version stability before adopting new releases. ' +
            'Creates a backup before modifying the config. ' +
            'Call with confirm=false (default) to preview; confirm=true to apply. ' +
            'To return to auto-update, call enable_auto_update.',
        inputSchema: {
            version: z
                .string()
                .min(1)
                .max(50)
                .describe('Semver version to pin, e.g. "1.21.0". Do not include the @ prefix.'),
            confirm: z
                .boolean()
                .optional()
                .describe('Set to true to apply the pin immediately. Defaults to false (preview only).'),
        },
        annotations: { readOnlyHint: false, destructiveHint: false },
    }, safeLicensed('pin_version', async (args) => handlePinVersion(args)));
    // ── Deploy tools ────────────────────────────────────────────────────────────
    server.registerTool('configure_deploy_target', {
        description: 'Save credentials for a deploy provider (Vercel, Netlify, etc.). Stores the API token in data/{projectId}/deploy-config.json. Required before deploy_spec.',
        inputSchema: {
            projectPath: z.string().min(1).describe('Absolute path to the project root'),
            provider: deployProviderEnum,
            token: z.string().min(1).describe('Provider API token'),
            projectId: z.string().optional().describe('Provider-side project ID (e.g. Vercel project)'),
            team: z.string().optional().describe('Optional team/org scope'),
        },
        annotations: { title: 'Configure Deploy Target', destructiveHint: false },
    }, safe(async (args) => {
        return handleConfigureDeployTarget(args);
    }));
    server.registerTool('deploy_spec', {
        description: 'Trigger a deployment to a configured provider. Phase 1: only Vercel is supported. Returns the deployment URL and status.',
        inputSchema: {
            projectPath: z.string().min(1).describe('Absolute path to the project root'),
            provider: deployProviderEnum,
            specId: SpecIdSchema.optional().describe('Optional spec ID for traceability'),
            environment: z
                .enum(['production', 'preview'])
                .optional()
                .describe('Deploy environment (default: preview)'),
        },
        annotations: { title: 'Deploy Spec', destructiveHint: false },
    }, safe(async (args) => {
        return handleDeploySpec(args);
    }));
    server.registerTool('deploy_status', {
        description: 'List recent deployments or refresh status for a specific deployment ID.',
        inputSchema: {
            projectPath: z.string().min(1).describe('Absolute path to the project root'),
            provider: deployProviderEnum.optional(),
            deploymentId: z.string().optional().describe('Specific deployment ID to check'),
            refresh: z.boolean().optional().describe('If true, fetch live status from provider'),
        },
        annotations: { title: 'Deploy Status', readOnlyHint: true },
    }, safe(async (args) => {
        return handleDeployStatus(args);
    }));
    // ── Cross-repo search tools ────────────────────────────────────────────────
    server.registerTool('register_project_path', {
        description: 'Register an additional project path so its specs are included in cross-repo ' +
            'searches (search_all_projects). The path must be an absolute filesystem path ' +
            'pointing to a Planu-initialized project root.',
        inputSchema: {
            projectPath: z
                .string()
                .min(1)
                .max(4096)
                .describe('Absolute path to the project root to register'),
        },
        annotations: { title: 'Register Project Path' },
    }, safe(async (args) => handleRegisterProjectPath(args)));
    server.registerTool('unregister_project_path', {
        description: 'Remove a previously registered project path from the cross-repo registry. ' +
            'Its specs will no longer appear in search_all_projects results.',
        inputSchema: {
            projectPath: z
                .string()
                .min(1)
                .max(4096)
                .describe('Absolute path to the project root to unregister'),
        },
        annotations: { title: 'Unregister Project Path', destructiveHint: true },
    }, safe(async (args) => handleUnregisterProjectPath(args)));
    server.registerTool('list_registered_projects', {
        description: 'List all project paths registered for cross-repo search, along with spec ' +
            'counts and last-scan timestamps.',
        inputSchema: {},
        annotations: { title: 'List Registered Projects', readOnlyHint: true },
    }, safe(async (args) => handleListRegisteredProjects(args)));
    server.registerTool('search_all_projects', {
        description: 'Search specs across ALL registered project paths simultaneously. ' +
            'Returns merged, relevance-scored results from every registered project. ' +
            'Use register_project_path to add projects first.',
        inputSchema: {
            query: z
                .string()
                .max(10_000)
                .optional()
                .describe('Free-text search query matched against spec title and tags'),
            status: z
                .array(z.string())
                .optional()
                .describe('Filter by spec status values. Valid values: draft | review | approved | ' +
                'implementing | done | cancelled'),
            tags: z
                .array(z.string())
                .optional()
                .describe('Filter by tags — specs must have at least one matching tag'),
            type: z
                .array(z.string())
                .optional()
                .describe('Filter by spec type. Valid values: feature | bugfix | refactor | chore | ' +
                'spike | epic | task'),
            limit: z
                .number()
                .min(1)
                .max(500)
                .default(50)
                .describe('Maximum results to return across all projects (default 50)'),
        },
        annotations: { title: 'Search All Projects', readOnlyHint: true },
    }, safeLicensed('search_all_projects', async (args) => handleSearchAllProjects(args)));
    // ── Browser validate tools ─────────────────────────────────────────────────
    server.registerTool('validate_browser', {
        description: 'Extract UI-testable assertions from a spec and run them against a live URL using Playwright. ' +
            'If Playwright is not installed, generates a ready-to-run test file in `tests/e2e/`. ' +
            'Parses acceptance criteria checkboxes (- [ ]) to detect visibility, text, navigation, and form assertions. ' +
            'Useful for validating that a deployed UI meets the spec before marking it done.',
        inputSchema: {
            projectPath: z
                .string()
                .min(1)
                .describe('Absolute path to the project root (e.g. "/home/user/my-app").'),
            specId: SpecIdSchema.describe('Spec ID to validate (e.g. "SPEC-042").'),
            baseUrl: z
                .string()
                .min(1)
                .describe('Base URL of the running application to test against (e.g. "http://localhost:3000"). ' +
                'The application must be running before calling this tool.'),
        },
        annotations: {
            title: 'Browser Validation',
            readOnlyHint: false,
            destructiveHint: false,
        },
    }, safe((args) => handleValidateBrowser(args)));
    // ── Worker tools ───────────────────────────────────────────────────────────
    server.registerTool('worker_status', {
        description: 'Show the status of background analysis workers (test-gaps, security-audit, optimization, code-map, documentation, dependency-health). ' +
            'Returns enabled/disabled state, last run results, queue length, and findings. ' +
            'Optionally filter by worker name for detailed findings.',
        inputSchema: {
            projectId: WorkerStatusInputSchema.shape.projectId,
            workerName: WorkerStatusInputSchema.shape.workerName,
        },
        annotations: { title: 'Worker Status', readOnlyHint: true },
    }, safeTracked('worker_status', (args) => Promise.resolve(handleWorkerStatus(args))));
    server.registerTool('configure_workers', {
        description: 'Configure background analysis workers: enable/disable, set cooldown, set priority, add/remove trigger patterns. ' +
            'Changes are persisted per-project and override default registry values.',
        inputSchema: {
            projectId: ConfigureWorkersInputSchema.shape.projectId,
            workerName: ConfigureWorkersInputSchema.shape.workerName,
            action: ConfigureWorkersInputSchema.shape.action,
            value: ConfigureWorkersInputSchema.shape.value,
        },
        annotations: { title: 'Configure Workers', readOnlyHint: false },
    }, safeLicensed('configure_workers', (args) => handleConfigureWorkers(args)));
    // ── Plugin tools ───────────────────────────────────────────────────────────
    server.registerTool('manage_plugins', {
        description: 'Manage Planu plugin lifecycle: search, list, activate, deactivate, uninstall, info. ' +
            'Use the search action to discover available plugins by keyword or stack. ' +
            'Use install_plugins for automated stack-based batch installation.',
        inputSchema: ManagePluginsSchema,
        annotations: {
            title: 'Manage Plugins',
            readOnlyHint: false,
            destructiveHint: false,
        },
    }, safeLicensed('manage_plugins', async (args) => handleManagePlugins(args)));
    server.registerTool('scaffold_plugin', {
        description: 'Generate a starter plugin project with manifest, entry point, tests, and examples. ' +
            'The scaffold includes TypeScript config and example extensions for the selected types.',
        inputSchema: ScaffoldPluginSchema,
        annotations: {
            title: 'Scaffold Plugin',
            readOnlyHint: false,
        },
    }, safeLicensed('scaffold_plugin', async (args) => handleScaffoldPlugin(args)));
    // ── Token optimizer tools ──────────────────────────────────────────────────
    server.registerTool('suggest_token_optimizer', {
        description: 'Detect whether RTK (Response Token Killer) or Headroom are configured in the project. ' +
            'Returns recommendations with install commands to reduce token usage by 30-60% on large codebases.',
        annotations: { title: 'Suggest Token Optimizer', readOnlyHint: true },
        inputSchema: {
            projectPath: z.string().describe('Absolute path to the project root directory'),
        },
    }, safeLicensed('suggest_token_optimizer', async (args) => handleSuggestTokenOptimizer(args)));
    server.registerTool('token_optimizer_status', {
        description: 'Returns the installation status of RTK and Headroom token optimization tools. ' +
            'Includes version info and number of configured PreToolUse hooks.',
        annotations: { title: 'Token Optimizer Status', readOnlyHint: true },
        inputSchema: {
            projectPath: z.string().describe('Absolute path to the project root directory'),
        },
    }, safeLicensed('token_optimizer_status', async (args) => handleTokenOptimizerStatus(args)));
    // ── RBAC tools ─────────────────────────────────────────────────────────────
    server.registerTool('configure_roles', {
        description: 'Enable or disable Role-Based Access Control (RBAC) for a project. ' +
            'When RBAC is enabled, users must have an assigned role to perform operations. ' +
            'When disabled (default), all operations are allowed (single-user mode). ' +
            'Use assign_role to grant users access after enabling RBAC.',
        inputSchema: {
            projectId: ConfigureRolesInputSchema.shape.projectId,
            enabled: ConfigureRolesInputSchema.shape.enabled,
        },
        annotations: { title: 'Configure Roles (RBAC)', readOnlyHint: false },
    }, safeLicensed('configure_roles', (args) => handleConfigureRoles(args)));
    server.registerTool('assign_role', {
        description: 'Assign a role to a user for a project or a specific spec. ' +
            'Roles: Admin (full access), Architect (create/approve/configure), ' +
            'Developer (read/update/implement), Reviewer (read/approve), Observer (read-only). ' +
            'Spec-scoped assignments override project-level role for that spec. ' +
            'Requires RBAC to be enabled via configure_roles.',
        inputSchema: {
            projectId: AssignRoleInputSchema.shape.projectId,
            userId: AssignRoleInputSchema.shape.userId,
            role: AssignRoleInputSchema.shape.role,
            specId: AssignRoleInputSchema.shape.specId,
            assignedBy: AssignRoleInputSchema.shape.assignedBy,
        },
        annotations: { title: 'Assign Role', readOnlyHint: false },
    }, safeLicensed('assign_role', (args) => handleAssignRole(args)));
    // ── Framework registry tools ───────────────────────────────────────────────
    server.registerTool('check_spec_accuracy', {
        description: 'Validate spec content (spec.md text or acceptance criteria) against the framework registry ' +
            'to detect hallucinated APIs, deprecated patterns, and common mistakes. ' +
            'Returns a confidence score (0-100) and actionable suggestions.',
        inputSchema: {
            specContent: CheckSpecAccuracySchema.shape.specContent,
            frameworks: CheckSpecAccuracySchema.shape.frameworks,
        },
        annotations: { title: 'Check Spec Accuracy', readOnlyHint: true },
    }, safeTracked('check_spec_accuracy', async (args) => handleCheckSpecAccuracy(args)));
    server.registerTool('update_registry', {
        description: 'Add a custom framework entry to the local registry. ' +
            'Custom entries override base entries and are stored in data/global/registry-custom.json. ' +
            'Use this to register internal frameworks or override outdated base entries.',
        inputSchema: {
            framework: UpdateRegistrySchema.shape.framework,
            displayName: UpdateRegistrySchema.shape.displayName,
            ecosystem: UpdateRegistrySchema.shape.ecosystem,
            frameworkVersion: UpdateRegistrySchema.shape.frameworkVersion,
            correctPatterns: UpdateRegistrySchema.shape.correctPatterns,
            deprecatedPatterns: UpdateRegistrySchema.shape.deprecatedPatterns,
            commonMistakes: UpdateRegistrySchema.shape.commonMistakes,
        },
        annotations: { title: 'Update Framework Registry', destructiveHint: false },
    }, safeTracked('update_registry', async (args) => handleUpdateRegistry(args)));
    // ── Skill registry tools ───────────────────────────────────────────────────
    server.registerTool('skill_search', {
        description: 'Search for installable Claude skills across multiple registries (Anthropic, AgentSkill.sh, Skills.sh). ' +
            'Filter by technology stack, restrict to a specific registry, or search all at once. ' +
            'Returns ranked results with install commands ready to use.',
        inputSchema: {
            query: SkillSearchInputSchema.shape.query,
            registry: SkillSearchInputSchema.shape.registry,
            stack: SkillSearchInputSchema.shape.stack,
            limit: SkillSearchInputSchema.shape.limit,
        },
        annotations: { title: 'Skill Search', readOnlyHint: true },
    }, safeTracked('skill_search', async (args) => handleSkillSearch(args)));
    server.registerTool('skill_install', {
        description: "Install a skill from a registry into a project's .claude/skills/ directory. " +
            'Detects the source registry from the skill name prefix if not specified. ' +
            'Skips installation if the skill is already present and warns about executable scripts.',
        inputSchema: {
            name: SkillInstallInputSchema.shape.name,
            registry: SkillInstallInputSchema.shape.registry,
            projectPath: SkillInstallInputSchema.shape.projectPath,
        },
        annotations: { title: 'Skill Install', destructiveHint: false },
    }, safeTracked('skill_install', async (args) => handleSkillInstall(args)));
    // ── Well-known / registry tools ────────────────────────────────────────────
    server.registerTool('publish_registry', {
        description: 'Generate a .well-known/planu.json manifest from your project specs. ' +
            'Follows RFC 8615 .well-known URI pattern (used by OpenID Connect, OAuth). ' +
            'Other teams can discover and reference your specs using discover_registry.',
        inputSchema: {
            projectId: PublishRegistryInputSchema.shape.projectId,
            outputPath: PublishRegistryInputSchema.shape.outputPath,
            includePrivate: PublishRegistryInputSchema.shape.includePrivate,
        },
        annotations: {
            title: 'Publish Registry Manifest',
            destructiveHint: false,
        },
    }, safeTracked('publish_registry', async (args) => handlePublishRegistry(args)));
    server.registerTool('discover_registry', {
        description: 'Read and validate a .well-known/planu.json manifest from a local file path. ' +
            'Returns the project name, spec count, capabilities, and spec summaries. ' +
            "Use this to discover specs from another team's project.",
        inputSchema: {
            manifestPath: DiscoverRegistryInputSchema.shape.manifestPath,
        },
        annotations: {
            title: 'Discover Registry',
            readOnlyHint: true,
        },
    }, safeTracked('discover_registry', async (args) => handleDiscoverRegistry(args)));
    // ── Skill registry tools ───────────────────────────────────────────────────
    server.registerTool('registry_login', {
        description: 'Log in to a Planu skill registry. Required before publishing or installing private skills. Persists token in ~/.planu/registry.json.',
        inputSchema: {
            registryUrl: z
                .string()
                .max(2048)
                .optional()
                .describe('Registry URL (default: https://registry.planu.dev). Use for custom/private registries.'),
            token: z
                .string()
                .max(1000)
                .optional()
                .describe('Personal access token. If omitted, an interactive prompt is shown (non-MCP environments).'),
        },
        annotations: { title: 'Registry Login', readOnlyHint: false },
    }, safeTracked('registry_login', async (args) => handleRegistryLogin(args)));
    server.registerTool('registry_logout', {
        description: 'Log out from the Planu skill registry and remove stored credentials.',
        inputSchema: {
            registryUrl: z
                .string()
                .max(2048)
                .optional()
                .describe('Registry URL to log out from (default: https://registry.planu.dev).'),
        },
        annotations: { title: 'Registry Logout', readOnlyHint: false },
    }, safeTracked('registry_logout', async (args) => handleRegistryLogout(args)));
    server.registerTool('registry_publish', {
        description: 'Publish a skill to the Planu registry. Reads skill metadata from .claude/skills/<name>/skill.json. Requires registry_login first.',
        inputSchema: {
            skillPath: z
                .string()
                .max(4096)
                .describe('Absolute path to the skill directory (e.g. /project/.claude/skills/my-skill).'),
            access: z
                .enum(['public', 'private'])
                .optional()
                .describe('Visibility of the published skill (default: public).'),
            tag: z
                .string()
                .max(100)
                .optional()
                .describe('Version tag to publish (default: taken from skill.json version).'),
        },
        annotations: { title: 'Publish Skill', readOnlyHint: false },
    }, safeTracked('registry_publish', async (args) => handleRegistryPublish(args)));
    server.registerTool('registry_search', {
        description: 'Search the Planu skill registry for skills by name, tag, or keyword. Returns skill metadata with install commands.',
        inputSchema: {
            query: z.string().max(500).describe('Search query — skill name, keyword, or tag.'),
            tag: z
                .string()
                .max(100)
                .optional()
                .describe('Filter results to skills with this tag (e.g. "testing", "security").'),
            limit: z
                .number()
                .int()
                .min(1)
                .max(100)
                .optional()
                .describe('Maximum number of results (default: 20).'),
        },
        annotations: { title: 'Search Registry', readOnlyHint: true },
    }, safeTracked('registry_search', async (args) => handleRegistrySearch(args)));
    server.registerTool('registry_install', {
        description: 'Install a skill from the Planu registry into a project. Downloads and extracts the skill into .claude/skills/.',
        inputSchema: {
            skillName: z.string().max(100).describe('Name of the skill to install (e.g. "my-skill").'),
            projectPath: z.string().max(4096).describe('Absolute path to the project root.'),
            version: z.string().max(50).optional().describe('Version to install (default: latest).'),
        },
        annotations: { title: 'Install Skill from Registry', readOnlyHint: false },
    }, safeTracked('registry_install', async (args) => handleRegistryInstall(args)));
    server.registerTool('registry_whoami', {
        description: 'Show the currently logged-in registry user and registry URL.',
        inputSchema: {
            registryUrl: z
                .string()
                .max(2048)
                .optional()
                .describe('Registry URL to check (default: https://registry.planu.dev).'),
        },
        annotations: { title: 'Registry Whoami', readOnlyHint: true },
    }, safeTracked('registry_whoami', async (args) => handleRegistryWhoami(args)));
    // ── Tool groups ────────────────────────────────────────────────────────────
    server.registerTool('activate_group', {
        description: 'Enable, disable, or toggle a group of tools. ' +
            "Tool groups organize Planu's 80+ tools into workflow categories. " +
            'Only enabled groups appear in the tool list, reducing noise and improving LLM relevance. ' +
            'Groups: core | spec-lifecycle | analysis | generation | orchestration | devops | governance | learning',
        inputSchema: {
            group: z
                .string()
                .describe('Group ID to activate/deactivate. ' +
                'Valid groups: core | spec-lifecycle | analysis | generation | orchestration | devops | governance | learning'),
            action: z
                .enum(['enable', 'disable', 'toggle'])
                .describe('Action: enable | disable | toggle'),
        },
        annotations: { title: 'Activate Tool Group', readOnlyHint: false },
    }, safe(async (args) => handleActivateGroup(args)));
    server.registerTool('list_groups', {
        description: 'List all tool groups with their status (enabled/disabled), tool count, and descriptions. ' +
            'Shows which groups are active and how many tools are currently visible to the LLM.',
        inputSchema: {},
        annotations: { title: 'List Tool Groups', readOnlyHint: true },
    }, safe(() => handleListGroups()));
    // ── Workspace tools ────────────────────────────────────────────────────────
    server.registerTool('audit_claude_config', {
        description: 'Audit and optimize your Claude Code workspace configuration. Analyzes CLAUDE.md size, .claude/rules/ structure, auto-memory, settings, and more against official best practices. Returns a scored report with prioritized suggestions.',
        inputSchema: {
            projectPath: z
                .string()
                .min(1)
                .max(4096)
                .describe('Absolute path to the project root to audit'),
            includeGlobal: z
                .boolean()
                .optional()
                .default(true)
                .describe('Also audit ~/.claude/ global config (default: true)'),
        },
        annotations: { readOnlyHint: true },
    }, safeLicensed('audit_claude_config', async (args) => handleAuditClaudeConfig(args)));
    server.registerTool('generate_docs_site', {
        description: 'Generate a static HTML documentation site from project specs, tools catalog, architecture diagrams, and metrics. ' +
            'Produces a navigable portal with dashboard, spec pages, tools catalog, Mermaid diagrams, and search.',
        inputSchema: {
            projectPath: z.string().min(1).max(4096).describe('Absolute path to the project root'),
            title: z
                .string()
                .max(500)
                .optional()
                .describe('Site title (default: "Planu Documentation")'),
        },
        annotations: { readOnlyHint: false },
    }, safeLicensed('generate_docs_site', async (args) => handleGenerateDocsSite(args)));
    // ── Memory config tools ────────────────────────────────────────────────────
    server.registerTool('configure_memory', {
        description: 'Configure persistent AI memory for your project using OpenMemory (local) or Mem0 Cloud. ' +
            'Generates the .claude.json MCP entry so Claude Code can access memory across sessions. ' +
            'Actions: setup-openmemory (local, no API key), setup-mem0-cloud (cloud, requires API key), ' +
            'status (check current config), reset (remove memory MCP entry).',
        annotations: { title: 'Configure Memory', readOnlyHint: false },
        inputSchema: {
            projectPath: z.string().describe('Absolute path to the project root directory'),
            action: z
                .enum(['setup-openmemory', 'setup-mem0-cloud', 'status', 'reset'])
                .describe('setup-openmemory: configure local OpenMemory (no API key needed) | ' +
                'setup-mem0-cloud: configure Mem0 Cloud (requires apiKey) | ' +
                'status: show current memory configuration | ' +
                'reset: remove memory MCP entry from .claude.json'),
            apiKey: z
                .string()
                .optional()
                .describe('Mem0 API key — required for setup-mem0-cloud action. Get from https://app.mem0.ai'),
        },
    }, safeLicensed('configure_memory', async (args) => handleConfigureMemory(args)));
    server.registerTool('memory_status', {
        description: 'Returns the current AI memory configuration status. ' +
            'Shows which provider is configured (none, openmemory, mem0-cloud) and the config file path.',
        annotations: { title: 'Memory Status', readOnlyHint: true },
        inputSchema: {
            projectPath: z.string().describe('Absolute path to the project root directory'),
        },
    }, safeLicensed('memory_status', async (args) => handleMemoryStatus(args)));
    // ── Code graph tools ──────────────────────────────────────────────────────
    server.registerTool('configure_code_graph', {
        description: 'Configure a local code graph MCP provider for the project. Supports Colby CodeGraph, CodeGraphContext, Pathfinder, and Axon. Writes the provider entry to .claude.json.',
        annotations: { title: 'Configure Code Graph', readOnlyHint: false },
        inputSchema: {
            projectPath: z.string().min(1).max(4096).describe('Absolute path to the project root'),
            action: z
                .enum([
                'setup-colby-codegraph',
                'setup-codegraph-context',
                'setup-pathfinder',
                'setup-axon',
                'status',
            ])
                .describe('setup-colby-codegraph: configure @colbymchenry/codegraph | setup-codegraph-context: configure CodeGraphContext | setup-pathfinder: configure Pathfinder | setup-axon: configure Axon | status: show current provider status'),
        },
    }, safeLicensed('configure_code_graph', async (args) => handleConfigureCodeGraph(args)));
    server.registerTool('code_graph_status', {
        description: 'Returns the current code graph MCP provider status from .claude.json, including the active provider if one is configured.',
        annotations: { title: 'Code Graph Status', readOnlyHint: true },
        inputSchema: {
            projectPath: z.string().min(1).max(4096).describe('Absolute path to the project root'),
        },
    }, safeLicensed('code_graph_status', async (args) => handleCodeGraphStatus(args)));
    // ── Onboarding tools ───────────────────────────────────────────────────────
    server.registerTool('start_onboarding', {
        description: 'Start interactive onboarding for a new project. Generates personalized recommendations based on your role, project type, and team size. Returns a markdown guide with next steps.',
        inputSchema: {
            projectPath: z.string().min(1).max(4096).describe('Absolute path to the project root'),
            role: z
                .enum(['solo-dev', 'team-lead', 'pm', 'architect'])
                .describe('Your role: solo-dev, team-lead, pm, architect'),
            projectType: z
                .enum(['saas', 'mobile', 'api', 'cli', 'library'])
                .describe('Project type: saas, mobile, api, cli, library'),
            teamSize: z.enum(['1', '2-5', '6-20', '20+']).describe('Team size: 1, 2-5, 6-20, 20+'),
        },
        annotations: { readOnlyHint: false },
    }, safeLicensed('start_onboarding', async (args) => handleStartOnboarding(args)));
    server.registerTool('guided_tour', {
        description: 'Get a step-by-step guided tour of a Planu feature area. Returns markdown with numbered steps, examples, and tool hints.',
        inputSchema: {
            area: z
                .enum([
                'spec-lifecycle',
                'team-collaboration',
                'integrations',
                'ai-tools',
                'security',
                'reporting',
            ])
                .describe('Tour area: spec-lifecycle, team-collaboration, integrations, ai-tools, security, reporting'),
        },
        annotations: { readOnlyHint: true },
    }, safeLicensed('guided_tour', async (args) => handleGuidedTour(args)));
    server.registerTool('onboarding_checklist', {
        description: 'Check what onboarding steps are complete for a project. Verifies planu.json, CLAUDE.md, specs, rules, and skills. Returns a checklist with progress percentage.',
        inputSchema: {
            projectPath: z.string().min(1).max(4096).describe('Absolute path to the project root'),
        },
        annotations: { readOnlyHint: true },
    }, safeLicensed('onboarding_checklist', async (args) => handleOnboardingChecklist(args)));
    server.registerTool('quick_start', {
        description: 'Get a quick start guide for a common project archetype. Returns a markdown guide with copy-paste commands.',
        inputSchema: {
            template: z
                .enum(['solo-dev', 'team-project', 'api-first', 'mobile-app', 'open-source'])
                .describe('Template: solo-dev, team-project, api-first, mobile-app, open-source'),
            projectPath: z
                .string()
                .min(1)
                .max(4096)
                .optional()
                .describe('Optional project path to personalize the guide'),
        },
        annotations: { readOnlyHint: true },
    }, safeLicensed('quick_start', async (args) => handleQuickStart(args)));
    server.registerTool('onboarding_progress', {
        description: 'View onboarding progress for a project. Shows setup checklist, profile, and next recommended steps.',
        inputSchema: {
            projectPath: z.string().min(1).max(4096).describe('Absolute path to the project root'),
        },
        annotations: { readOnlyHint: true },
    }, safeLicensed('onboarding_progress', async (args) => handleOnboardingProgress(args)));
    // ── Docs registry tools ────────────────────────────────────────────────────
    server.registerTool('validate_docs_registry', {
        description: 'Check all entries in the built-in docs-registry.json for broken or slow links. ' +
            'Performs HEAD requests against each registered documentation URL and categorizes them ' +
            'as healthy, slow (>2s), or broken. Useful for auditing docs-registry health.',
        inputSchema: {
            projectPath: z
                .string()
                .max(4096)
                .describe('Absolute path to the project root (used for context only).'),
        },
        annotations: { readOnlyHint: true },
    }, safeLicensed('validate_docs_registry', async (args) => {
        return handleValidateDocsRegistry(args);
    }));
    server.registerTool('discover_docs_url', {
        description: 'Discover the official documentation URL for a package/library by querying ' +
            'npm, PyPI, and crates.io registries. Returns the docs URL and whether it was ' +
            'verified as alive. Useful for finding docs for packages not yet in the registry.',
        inputSchema: {
            tech: z
                .string()
                .min(1)
                .max(200)
                .describe('Package or library name to look up (e.g. "axios", "requests", "tokio").'),
        },
        annotations: { readOnlyHint: true },
    }, safeLicensed('discover_docs_url', async (args) => {
        return handleDiscoverDocsUrl(args);
    }));
    // ── GC tools ───────────────────────────────────────────────────────────────
    server.registerTool('gc_data_projects', {
        description: 'Garbage-collect the data/projects/ directory by identifying and optionally deleting ' +
            'test-named dirs (proj-A, test-*, e2e-*, etc.) and inactive dirs ' +
            '(no file writes in the last N days). ' +
            'Dry-run by default — pass apply=true to actually delete. ' +
            'Optionally also flag orphaned hash dirs (hash not in the global project registry).',
        inputSchema: {
            projectPath: z
                .string()
                .max(4096)
                .optional()
                .describe('Absolute path to the project root where data/projects/ lives. ' +
                'Defaults to process.cwd().'),
            inactiveDays: z
                .number()
                .int()
                .min(1)
                .max(3650)
                .optional()
                .describe('Days of inactivity before a dir is treated as stale (default: 90).'),
            apply: z
                .boolean()
                .optional()
                .describe('Set to true to actually delete candidates. ' +
                'Default false = dry-run (report only, nothing deleted).'),
            includeOrphans: z
                .boolean()
                .optional()
                .describe('Also flag hash dirs that are not in the global project registry. ' +
                'Default false. Requires the registry to be readable.'),
        },
        annotations: {
            title: 'GC Data Projects',
            destructiveHint: true,
            readOnlyHint: false,
        },
    }, safe(async (args) => handleGcDataProjects(args)));
}
//# sourceMappingURL=group-infra.js.map