/**
 * V1 pattern detection.
 *
 * Analyses a project directory and returns a structured report of every v1
 * artefact that needs to be migrated to v2.  No files are modified here.
 */
import { existsSync, readdirSync, statSync } from 'node:fs';
import { join, relative, resolve } from 'node:path';
import ts from 'typescript';
// ---------------------------------------------------------------------------
// Removed frontend API names
// ---------------------------------------------------------------------------
const REMOVED_REACT_APIS = new Set([
    'createAPIClient',
    'useAPI',
    'createClient',
    'RouteRegistry',
    'RPCRouteRegistry',
    'SSERouteRegistry',
    'WebSocketRouteRegistry',
]);
/**
 * APIs that are deprecated (still work but will be removed).
 * Users should migrate away from these.
 */
const DEPRECATED_REACT_APIS = new Set([
    'AgentuityProvider',
    'AgentuityContext',
    'useAgentuity',
    'useAuth',
    'useAnalytics',
    'useTrackOnMount',
    'withPageTracking',
    'useWebRTCCall',
    'useJsonMemo',
]);
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
function rel(projectDir, abs) {
    return relative(projectDir, abs);
}
/**
 * Walk a directory recursively, yielding all file paths that match the
 * given extension filter.
 */
function* walkFiles(dir, exts) {
    if (!existsSync(dir))
        return;
    for (const entry of readdirSync(dir, { withFileTypes: true })) {
        const full = join(dir, entry.name);
        if (entry.isDirectory()) {
            // Skip known non-source dirs
            if (['node_modules', 'dist', '.agentuity', '.git'].includes(entry.name))
                continue;
            yield* walkFiles(full, exts);
        }
        else if (entry.isFile() && exts.some((e) => entry.name.endsWith(e))) {
            yield full;
        }
    }
}
/** Parse a TypeScript source file and return the AST */
async function parseTs(filePath) {
    const src = await Bun.file(filePath).text();
    return ts.createSourceFile(filePath, src, ts.ScriptTarget.ESNext, true);
}
/**
 * Collect the property names present inside a specific function call's first
 * object-literal argument.
 *
 * e.g. given `createApp({ router, agents, setup })` → ['router','agents','setup']
 */
function getCreateAppProps(sourceFile) {
    const props = new Set();
    function visit(node) {
        if (ts.isCallExpression(node) &&
            ts.isIdentifier(node.expression) &&
            node.expression.text === 'createApp') {
            const arg = node.arguments[0];
            if (arg && ts.isObjectLiteralExpression(arg)) {
                for (const prop of arg.properties) {
                    if ((ts.isPropertyAssignment(prop) || ts.isShorthandPropertyAssignment(prop)) &&
                        ts.isIdentifier(prop.name)) {
                        props.add(prop.name.text);
                    }
                }
            }
        }
        ts.forEachChild(node, visit);
    }
    visit(sourceFile);
    return props;
}
/**
 * Returns true if the source file contains a call to `bootstrapRuntimeEnv()`
 */
function hasBootstrapCall(sourceFile) {
    let found = false;
    function visit(node) {
        if (found)
            return;
        if (ts.isCallExpression(node) &&
            ts.isIdentifier(node.expression) &&
            node.expression.text === 'bootstrapRuntimeEnv') {
            found = true;
            return;
        }
        ts.forEachChild(node, visit);
    }
    visit(sourceFile);
    return found;
}
/**
 * Detect whether a route file uses v1 mutable-style `createRouter()` +
 * `router.get(...)` vs v2 chained `new Hono<Env>().get(...)`.
 *
 * Returns 'v1' | 'v2' | 'unknown' (file doesn't look like a route at all).
 */
function classifyRouteFile(sourceFile) {
    // sync — receives parsed AST
    let hasCreateRouter = false;
    let hasMutatingCall = false; // router.get / router.post etc.
    let hasHonoNew = false;
    function visit(node) {
        // createRouter() call
        if (ts.isCallExpression(node) &&
            ts.isIdentifier(node.expression) &&
            node.expression.text === 'createRouter') {
            hasCreateRouter = true;
        }
        // router.<method>(...) — mutating style
        if (ts.isCallExpression(node) &&
            ts.isPropertyAccessExpression(node.expression) &&
            ts.isIdentifier(node.expression.name)) {
            const methodName = node.expression.name.text;
            if (['get', 'post', 'put', 'patch', 'delete', 'all', 'use', 'route'].includes(methodName)) {
                // Check that the object being accessed is NOT a chained call (i.e., it's an identifier)
                if (ts.isIdentifier(node.expression.expression)) {
                    hasMutatingCall = true;
                }
            }
        }
        // new Hono<...>()
        if (ts.isNewExpression(node) &&
            ts.isIdentifier(node.expression) &&
            node.expression.text === 'Hono') {
            hasHonoNew = true;
        }
        ts.forEachChild(node, visit);
    }
    visit(sourceFile);
    if (hasHonoNew)
        return 'v2';
    if (hasCreateRouter && hasMutatingCall)
        return 'v1';
    return 'unknown';
}
/**
 * Scan frontend (web) source files for removed API usages.
 */
async function scanFrontendFiles(projectDir) {
    const webDir = join(projectDir, 'src', 'web');
    const findings = [];
    for (const file of walkFiles(webDir, ['.ts', '.tsx'])) {
        const sourceFile = await parseTs(file);
        const usedApis = [];
        const deprecatedApis = [];
        function visit(node) {
            // Check named imports from @agentuity/react or @agentuity/frontend
            if (ts.isImportDeclaration(node)) {
                const moduleSpecifier = node.moduleSpecifier.text;
                if (moduleSpecifier === '@agentuity/react' ||
                    moduleSpecifier === '@agentuity/frontend') {
                    const namedBindings = node.importClause?.namedBindings;
                    if (namedBindings && ts.isNamedImports(namedBindings)) {
                        for (const element of namedBindings.elements) {
                            const name = element.name.text;
                            if (REMOVED_REACT_APIS.has(name)) {
                                usedApis.push(name);
                            }
                            if (DEPRECATED_REACT_APIS.has(name)) {
                                deprecatedApis.push(name);
                            }
                        }
                    }
                }
            }
            ts.forEachChild(node, visit);
        }
        visit(sourceFile);
        if (usedApis.length > 0 || deprecatedApis.length > 0) {
            findings.push({
                file: rel(projectDir, file),
                apis: [...new Set(usedApis)],
                deprecatedApis: [...new Set(deprecatedApis)],
            });
        }
    }
    return findings;
}
/**
 * Find all route files under src/api (recursively, files named `route.ts` or
 * `route.tsx` or `router.ts`).
 */
function findRouteFiles(projectDir) {
    const apiDir = join(projectDir, 'src', 'api');
    const files = [];
    for (const file of walkFiles(apiDir, ['.ts', '.tsx'])) {
        const base = file.split('/').pop() ?? '';
        if (base === 'route.ts' || base === 'route.tsx' || base === 'router.ts') {
            files.push(file);
        }
    }
    return files;
}
// ---------------------------------------------------------------------------
// Main detector
// ---------------------------------------------------------------------------
export async function detect(projectDir) {
    const absDir = resolve(projectDir);
    const findings = [];
    const result = {
        projectDir: absDir,
        findings,
        v1RouteFiles: [],
        v2RouteFiles: [],
        hasAgentBarrel: false,
        hasApiBarrel: false,
        hasAgentuityConfig: false,
        analyticsInCreateApp: false,
        workbenchInCreateApp: false,
        setupInCreateApp: false,
        shutdownInCreateApp: false,
        bootstrapCallInAppTs: false,
        frontendRemovedApis: [],
        outdatedPackages: [],
    };
    // ── 1. src/generated/ ───────────────────────────────────────────────────
    const generatedDir = join(absDir, 'src', 'generated');
    if (existsSync(generatedDir) && statSync(generatedDir).isDirectory()) {
        result.generatedDir = generatedDir;
        findings.push({
            id: 'generated-dir',
            severity: 'auto',
            message: 'src/generated/ directory exists (CLI-managed codegen artefacts)',
            file: rel(absDir, generatedDir),
            hint: 'Will be deleted. In v2, types come from Hono RPC inference and direct imports.',
        });
    }
    // ── 2. app.ts ────────────────────────────────────────────────────────────
    const appTsPath = join(absDir, 'app.ts');
    if (existsSync(appTsPath)) {
        result.appTsPath = appTsPath;
        const sourceFile = await parseTs(appTsPath);
        const props = getCreateAppProps(sourceFile);
        result.bootstrapCallInAppTs = hasBootstrapCall(sourceFile);
        if (result.bootstrapCallInAppTs) {
            findings.push({
                id: 'bootstrap-call',
                severity: 'auto',
                message: 'app.ts calls bootstrapRuntimeEnv() — removed in v2',
                file: 'app.ts',
                hint: 'createApp() handles runtime bootstrapping internally; the call will be removed.',
            });
        }
        result.setupInCreateApp = props.has('setup');
        if (result.setupInCreateApp) {
            findings.push({
                id: 'setup-in-createapp',
                severity: 'guided',
                message: 'createApp({ setup }) — setup() lifecycle removed in v2',
                file: 'app.ts',
                hint: 'Move initialisation logic to module-level top-of-file code in app.ts. ' +
                    "For cleanup, use Hono's standard patterns or Bun's process hooks (e.g., process.on('beforeExit', ...)).",
            });
        }
        result.shutdownInCreateApp = props.has('shutdown');
        if (result.shutdownInCreateApp) {
            findings.push({
                id: 'shutdown-in-createapp',
                severity: 'guided',
                message: 'createApp({ shutdown }) — shutdown() lifecycle removed in v2',
                file: 'app.ts',
                hint: "Use Hono's standard patterns or Bun's process hooks for cleanup " +
                    "(e.g., process.on('beforeExit', async () => { /* cleanup */ })).",
            });
        }
        result.analyticsInCreateApp = props.has('analytics');
        if (result.analyticsInCreateApp) {
            // v2 keeps analytics in createApp() - no migration needed
            // This is tracked for informational purposes only
        }
        result.workbenchInCreateApp = props.has('workbench');
        if (result.workbenchInCreateApp) {
            // v2 keeps workbench in createApp() - no migration needed
            // This is tracked for informational purposes only
        }
        // Check if router/agents are already wired (v2 pattern)
        const hasRouter = props.has('router');
        const hasAgents = props.has('agents');
        if (!hasRouter && !hasAgents) {
            findings.push({
                id: 'app-no-router-agents',
                severity: 'guided',
                message: 'app.ts does not pass router or agents to createApp()',
                file: 'app.ts',
                hint: 'In v2, you must explicitly import and pass your router and agents array.\n' +
                    '\n' +
                    "  import router from './src/api';\n" +
                    "  import agents from './src/agent';\n" +
                    '\n' +
                    '  const { server, logger } = await createApp({\n' +
                    "    router: { path: '/api', router },\n" +
                    '    agents,\n' +
                    '  });\n' +
                    '\n' +
                    'The src/api/index.ts and src/agent/index.ts barrels are generated by this tool.',
            });
        }
    }
    else {
        findings.push({
            id: 'no-app-ts',
            severity: 'manual',
            message: 'No app.ts found at project root',
            hint: 'Create app.ts importing createApp from @agentuity/runtime.',
        });
    }
    // ── 3. agentuity.config.ts ───────────────────────────────────────────────
    const configPath = join(absDir, 'agentuity.config.ts');
    result.hasAgentuityConfig = existsSync(configPath);
    // Check for vite.config.ts (v2 approach)
    const viteConfigPath = join(absDir, 'vite.config.ts');
    const hasViteConfig = existsSync(viteConfigPath);
    // In v2, agentuity.config.ts is DEPRECATED.
    // ALL config is consolidated into createApp() or native Vite config:
    // - Vite keys (plugins, define, render, bundle) → vite.config.ts
    // - Runtime keys (analytics, workbench) → createApp() ONLY (single source of truth)
    if (result.hasAgentuityConfig) {
        const configSrc = await Bun.file(configPath).text();
        // Categorize config keys
        const viteKeys = [];
        const runtimeKeys = [];
        if (configSrc.includes('plugins:'))
            viteKeys.push('plugins');
        if (configSrc.includes('define:'))
            viteKeys.push('define');
        if (configSrc.includes('render:'))
            viteKeys.push('render');
        if (configSrc.includes('bundle:'))
            viteKeys.push('bundle');
        if (configSrc.includes('analytics:'))
            runtimeKeys.push('analytics');
        if (configSrc.includes('workbench:'))
            runtimeKeys.push('workbench');
        if (viteKeys.length > 0) {
            findings.push({
                id: 'agentuity-config-vite-keys',
                severity: 'guided',
                message: `agentuity.config.ts has Vite config: ${viteKeys.join(', ')}`,
                file: 'agentuity.config.ts',
                hint: 'Vite config should go in vite.config.ts. Example:\n' +
                    '\n' +
                    '  // vite.config.ts\n' +
                    "  import { defineConfig } from 'vite';\n" +
                    "  import react from '@vitejs/plugin-react';\n" +
                    "  import { join } from 'node:path';\n" +
                    '\n' +
                    '  export default defineConfig({\n' +
                    '    plugins: [react()],\n' +
                    "    define: { CUSTOM: JSON.stringify('value') },\n" +
                    '    build: {\n' +
                    '      rollupOptions: {\n' +
                    "        input: join(import.meta.dirname, 'src/web/index.html'),\n" +
                    '      },\n' +
                    '    },\n' +
                    '  });\n' +
                    '\n' +
                    'The build.rollupOptions.input is required so Vite finds your\n' +
                    'HTML entry point at src/web/index.html.\n' +
                    '\n' +
                    'After creating vite.config.ts, delete agentuity.config.ts.',
            });
        }
        if (runtimeKeys.length > 0) {
            findings.push({
                id: 'agentuity-config-runtime-keys',
                severity: 'guided',
                message: `agentuity.config.ts has ${runtimeKeys.join(', ')} — remove (use createApp() only)`,
                file: 'agentuity.config.ts',
                hint: 'In v2, ALL runtime config goes in createApp().\n' +
                    'The CLI reads these options directly from your createApp() call.\n' +
                    'Remove them from agentuity.config.ts and keep only in createApp().',
            });
        }
        if (viteKeys.length === 0 && runtimeKeys.length === 0) {
            // Empty or minimal config
            findings.push({
                id: 'agentuity-config-empty',
                severity: 'guided',
                message: 'agentuity.config.ts exists but has no config keys — can be deleted',
                file: 'agentuity.config.ts',
                hint: 'This file is no longer needed in v2. Delete it.',
            });
        }
    }
    // ── 4. Route files ───────────────────────────────────────────────────────
    const routeFiles = findRouteFiles(absDir);
    for (const file of routeFiles) {
        const sourceFile = await parseTs(file);
        const classification = classifyRouteFile(sourceFile);
        const relFile = rel(absDir, file);
        if (classification === 'v1') {
            result.v1RouteFiles.push(file);
            findings.push({
                id: `route-v1:${relFile}`,
                severity: 'auto',
                message: `Route file uses v1 mutable createRouter() style`,
                file: relFile,
                hint: 'Will be rewritten to chained new Hono<Env>().get().post()… style.\n' +
                    'Chaining is required for Hono RPC — only a single chained expression\n' +
                    'accumulates all route schemas into `typeof router`, which becomes the\n' +
                    'AppRouter type used by hc<AppRouter>() on the frontend.',
            });
        }
        else if (classification === 'v2') {
            result.v2RouteFiles.push(file);
        }
    }
    // ── 5. src/api/index.ts barrel ──────────────────────────────────────────
    const apiIndexPath = join(absDir, 'src', 'api', 'index.ts');
    result.hasApiBarrel = existsSync(apiIndexPath);
    if (!result.hasApiBarrel && result.v1RouteFiles.length > 0) {
        findings.push({
            id: 'missing-api-barrel',
            severity: 'auto',
            message: 'src/api/index.ts barrel does not exist',
            hint: 'Will be generated with a single chained Hono<Env>().route()… expression.\n' +
                'This exports AppRouter = typeof router, the type needed for hc<AppRouter>()\n' +
                'on the frontend. Chaining is required — broken chains produce an empty type.',
        });
    }
    else if (result.hasApiBarrel) {
        // Check if the existing index.ts is the old empty stub
        const src = await Bun.file(apiIndexPath).text();
        const isStub = src.includes('createRouter()') &&
            !src.includes('.route(') &&
            src.split('\n').filter((l) => l.trim()).length < 8;
        if (isStub) {
            findings.push({
                id: 'api-barrel-stub',
                severity: 'auto',
                message: 'src/api/index.ts is the empty v1 stub (createRouter() with no routes)',
                file: 'src/api/index.ts',
                hint: 'Will be replaced with an explicit barrel that imports and composes all route files.',
            });
        }
    }
    // ── 6. src/agent/index.ts barrel ────────────────────────────────────────
    const agentIndexPath = join(absDir, 'src', 'agent', 'index.ts');
    result.hasAgentBarrel = existsSync(agentIndexPath);
    if (!result.hasAgentBarrel) {
        const agentDir = join(absDir, 'src', 'agent');
        if (existsSync(agentDir)) {
            findings.push({
                id: 'missing-agent-barrel',
                severity: 'auto',
                message: 'src/agent/index.ts barrel does not exist',
                hint: 'Will be generated by scanning src/agent/*/agent.ts files and exporting a default array.',
            });
        }
    }
    // ── 7. Frontend removed APIs ─────────────────────────────────────────────
    result.frontendRemovedApis = await scanFrontendFiles(absDir);
    for (const fe of result.frontendRemovedApis) {
        if (fe.apis.length > 0) {
            findings.push({
                id: `frontend-removed:${fe.file}`,
                severity: 'manual',
                message: `Frontend file uses removed APIs: ${fe.apis.join(', ')}`,
                file: fe.file,
                hint: 'Replace with Hono RPC client (hono/client):\n' +
                    '\n' +
                    "  import { hc } from 'hono/client';\n" +
                    "  import type { AppRouter } from '../api'; // your barrel\n" +
                    "  const client = hc<AppRouter>(window.location.origin + '/api');\n" +
                    "  const res = await client.hello.$post({ json: { name: 'World' } });\n" +
                    '\n' +
                    'See: https://hono.dev/docs/guides/rpc',
            });
        }
        if (fe.deprecatedApis && fe.deprecatedApis.length > 0) {
            findings.push({
                id: `frontend-deprecated:${fe.file}`,
                severity: 'guided',
                message: `Frontend file uses deprecated @agentuity/react APIs: ${fe.deprecatedApis.join(', ')}`,
                file: fe.file,
                hint: '@agentuity/react is deprecated. Migration options:\n' +
                    '\n' +
                    '• AgentuityProvider/useAuth → Use your auth provider directly (better-auth, Clerk, etc.)\n' +
                    '• useAnalytics → Use getAnalytics() from @agentuity/frontend\n' +
                    '• useWebRTCCall → Use WebRTCManager from @agentuity/frontend\n' +
                    '• WebSocketManager/EventStreamManager → Import from @agentuity/frontend\n' +
                    '\n' +
                    'The package will continue to work but will not receive updates.',
            });
        }
    }
    // ── 7a. Outdated @agentuity/* packages ──────────────────────────────────
    const packageJsonPath = join(absDir, 'package.json');
    if (existsSync(packageJsonPath)) {
        try {
            const packageJson = JSON.parse(await Bun.file(packageJsonPath).text());
            const outdatedPackages = [];
            // Check both dependencies and devDependencies
            for (const section of ['dependencies', 'devDependencies']) {
                const deps = packageJson[section];
                if (!deps || typeof deps !== 'object')
                    continue;
                for (const [name, version] of Object.entries(deps)) {
                    // Only check @agentuity/* packages
                    if (!name.startsWith('@agentuity/'))
                        continue;
                    const versionStr = String(version);
                    // Check if version needs updating:
                    // - "latest" tag → needs update
                    // - v1.x.x versions (e.g., "^1.0.60", "1.0.0") → needs update
                    // - Already v2 (e.g., "^2.0.0") → skip
                    const needsUpdate = versionStr === 'latest' ||
                        versionStr === '*' ||
                        /^[~^]?1\./.test(versionStr) ||
                        /^1\./.test(versionStr);
                    if (needsUpdate) {
                        outdatedPackages.push({
                            name,
                            currentVersion: versionStr,
                            section,
                        });
                    }
                }
            }
            result.outdatedPackages = outdatedPackages;
            if (outdatedPackages.length > 0) {
                const packageList = outdatedPackages
                    .map((p) => `${p.name}@${p.currentVersion}`)
                    .join(', ');
                findings.push({
                    id: 'outdated-agentuity-packages',
                    severity: 'auto',
                    message: `Outdated @agentuity/* packages: ${packageList}`,
                    file: 'package.json',
                    hint: 'Will be updated to ^2.0.0.\n' +
                        'All @agentuity/* packages must be on v2 for the migration to work correctly.',
                });
            }
        }
        catch {
            // Ignore parse errors
        }
    }
    // ── 7b. Hono RPC recommendation (whenever routes exist) ─────────────────
    // Fire this as a "guided" finding whenever there are any API routes,
    // regardless of whether the frontend already uses removed APIs.
    // It surfaces the RPC pattern to users who may not know about it yet.
    const hasApiRoutes = result.v1RouteFiles.length > 0 ||
        result.v2RouteFiles.length > 0 ||
        existsSync(join(absDir, 'src', 'api'));
    const frontendDir = join(absDir, 'src', 'web');
    const hasFrontend = existsSync(frontendDir);
    // ── 7c. Vite config check ───────────────────────────────────────────────
    // If there's a frontend, vite.config.ts should exist with framework plugins
    if (hasFrontend && !hasViteConfig) {
        findings.push({
            id: 'missing-vite-config',
            severity: 'guided',
            message: 'No vite.config.ts found - frontend requires Vite configuration',
            hint: 'Create vite.config.ts with your frontend framework plugin.\n' +
                'The build.rollupOptions.input must point to src/web/index.html:\n' +
                '\n' +
                '  // vite.config.ts\n' +
                "  import { defineConfig } from 'vite';\n" +
                "  import react from '@vitejs/plugin-react';\n" +
                "  import { join } from 'node:path';\n" +
                '\n' +
                '  export default defineConfig({\n' +
                '    plugins: [react()],\n' +
                '    build: {\n' +
                '      rollupOptions: {\n' +
                "        input: join(import.meta.dirname, 'src/web/index.html'),\n" +
                '      },\n' +
                '    },\n' +
                '  });\n' +
                '\n' +
                'For other frameworks:\n' +
                "  • Svelte: import { svelte } from '@sveltejs/vite-plugin-svelte'\n" +
                "  • Vue: import vue from '@vitejs/plugin-vue'\n" +
                "  • Solid: import solid from 'vite-plugin-solid'",
        });
    }
    // If vite.config.ts exists but is missing build.rollupOptions.input, warn about it
    if (hasFrontend && hasViteConfig) {
        const viteSrc = await Bun.file(viteConfigPath).text();
        if (!viteSrc.includes('rollupOptions') ||
            !viteSrc.includes('input') ||
            !viteSrc.includes('src/web/index.html')) {
            findings.push({
                id: 'vite-config-missing-rollup-input',
                severity: 'guided',
                message: 'vite.config.ts may be missing build.rollupOptions.input',
                file: 'vite.config.ts',
                hint: 'Agentuity expects the HTML entry point at src/web/index.html.\n' +
                    'Add build.rollupOptions.input to your vite.config.ts:\n' +
                    '\n' +
                    "  import { join } from 'node:path';\n" +
                    '\n' +
                    '  export default defineConfig({\n' +
                    '    // ...your existing config\n' +
                    '    build: {\n' +
                    '      rollupOptions: {\n' +
                    "        input: join(import.meta.dirname, 'src/web/index.html'),\n" +
                    '      },\n' +
                    '    },\n' +
                    '  });',
            });
        }
    }
    if (hasApiRoutes && hasFrontend && result.frontendRemovedApis.length === 0) {
        // Only show this if there are no already-detected frontend issues (avoid duplication)
        findings.push({
            id: 'hono-rpc-recommendation',
            severity: 'guided',
            message: 'Consider using Hono RPC for fully type-safe frontend ↔ backend calls',
            hint: "v2 uses Hono's native RPC system instead of the old Agentuity typed client.\n" +
                'The barrel at src/api/index.ts exports AppRouter = typeof router.\n' +
                'Routes MUST be chained in a single expression for types to accumulate.\n' +
                '\n' +
                'In your frontend (src/web/):\n' +
                '\n' +
                "  import { hc } from 'hono/client';\n" +
                "  import type { AppRouter } from '../api';\n" +
                '\n' +
                "  const client = hc<AppRouter>(window.location.origin + '/api');\n" +
                '\n' +
                '  // Typed call — method name mirrors route path, prefixed with $\n' +
                "  const res = await client.hello.$post({ json: { name: 'World' } });\n" +
                '  const data = await res.json();\n' +
                '\n' +
                'See: https://hono.dev/docs/guides/rpc',
        });
    }
    // Sort: auto first, guided second, manual last
    const order = { auto: 0, guided: 1, manual: 2 };
    findings.sort((a, b) => order[a.severity] - order[b.severity]);
    return result;
}
//# sourceMappingURL=detect.js.map