/**
 * Crewly Auditor Tool Registry
 *
 * Read-only tools for the Auditor agent. These tools allow the auditor
 * to observe agent activity, read logs, check task alignment, and write
 * structured bug reports — without any ability to modify system state.
 *
 * @module services/agent/crewly-agent/auditor-tools
 */
import { promises as fsPromises } from 'fs';
import * as path from 'path';
import { z } from 'zod';
import { convertMarkdownToSlackMrkdwn } from './tool-registry.js';
/** Severity levels for audit findings */
const AUDIT_SEVERITIES = ['critical', 'high', 'medium', 'low'];
/**
 * Create the auditor tool set for the Crewly Auditor agent.
 *
 * All tools are read-only except `write_audit_report` which appends
 * to the audit log file.
 *
 * @param client - API client for Crewly REST API calls
 * @param projectPath - Project root path for file operations
 * @returns Object of named tools for the auditor agent
 */
export function createAuditorTools(client, projectPath) {
    const auditDir = path.join(projectPath, '.crewly', 'audit');
    const bugsFile = path.join(auditDir, 'bugs.md');
    return {
        get_team_status: {
            description: 'Get status of all teams and their agents. Returns team names, member statuses, and runtime types.',
            inputSchema: z.object({}),
            execute: async () => {
                const result = await client.get('/teams');
                return result.success ? result.data : { error: result.error };
            },
        },
        get_agent_logs: {
            description: 'Get recent terminal output from an agent session. Use to inspect agent activity and detect errors.',
            inputSchema: z.object({
                sessionName: z.string().describe('Agent session name'),
                lines: z.number().default(50).describe('Number of lines to retrieve'),
            }),
            execute: async (args) => {
                const { sessionName, lines } = args;
                const result = await client.get(`/terminal/${sessionName}/output?lines=${lines}`);
                return result.success ? result.data : { error: result.error };
            },
        },
        get_tasks: {
            description: 'Get all tracked tasks for a project. Use to detect stale, failed, or orphaned tasks.',
            inputSchema: z.object({
                projectPath: z.string().describe('Project path'),
                status: z.string().optional().describe('Filter by status (open, in_progress, done, failed)'),
            }),
            execute: async (args) => {
                // V3-only as of spec 2026-05-06-task-management-v1-deprecation.md.
                // The V3 task-pool is global; `projectPath` is no longer a filter.
                const { status } = args;
                const query = status ? `?status=${encodeURIComponent(status)}` : '';
                const result = await client.get(`/task-pool/items${query}`);
                return result.success ? result.data : { error: result.error };
            },
        },
        recall_goals: {
            description: 'Retrieve current project goals, OKRs, and objectives from memory. Use to check goal alignment.',
            inputSchema: z.object({
                context: z.string().default('current project goals OKR objectives priorities').describe('Search context'),
            }),
            execute: async (args) => {
                const { context } = args;
                const result = await client.post('/memory/recall', {
                    context,
                    scope: 'project',
                });
                return result.success ? result.data : { error: result.error };
            },
        },
        heartbeat: {
            description: 'System health check. Returns teams, projects, and queue status in one call.',
            inputSchema: z.object({}),
            execute: async () => {
                const [teams, projects] = await Promise.all([
                    client.get('/teams'),
                    client.get('/projects'),
                ]);
                return {
                    teams: teams.success ? teams.data : { error: teams.error },
                    projects: projects.success ? projects.data : { error: projects.error },
                    timestamp: new Date().toISOString(),
                };
            },
        },
        get_agent_status: {
            description: 'Get the current status of a specific agent by session name.',
            inputSchema: z.object({
                sessionName: z.string().describe('Agent session name to check'),
            }),
            execute: async (args) => {
                const { sessionName } = args;
                const result = await client.get('/teams');
                if (!result.success)
                    return { error: result.error };
                const teams = result.data;
                for (const team of teams) {
                    const member = team.members?.find(m => m.sessionName === sessionName);
                    if (member)
                        return member;
                }
                return { error: `Agent '${sessionName}' not found` };
            },
        },
        subscribe_event: {
            description: 'Subscribe to agent lifecycle events for monitoring. Read-only observation.',
            inputSchema: z.object({
                eventType: z.string().describe('Event type (e.g., "agent:idle", "agent:busy", "agent:status_changed")'),
                filter: z.record(z.string()).optional().describe('Event filter criteria'),
                oneShot: z.boolean().default(false).describe('Auto-unsubscribe after first event'),
            }),
            execute: async (args) => {
                const { eventType, filter, oneShot } = args;
                const result = await client.post('/events/subscribe', {
                    eventType,
                    filter,
                    oneShot,
                    target: 'crewly-auditor',
                });
                return result.success ? result.data : { error: result.error };
            },
        },
        write_audit_report: {
            description: 'Append a structured audit finding to the bugs.md file. Use when you detect a problem.',
            inputSchema: z.object({
                severity: z.enum(AUDIT_SEVERITIES).describe('Problem severity level'),
                agents: z.array(z.string()).describe('Session names of involved agents'),
                title: z.string().describe('Short problem title'),
                description: z.string().describe('Detailed problem description'),
                evidence: z.string().describe('Log excerpts or data supporting the finding'),
                suggestion: z.string().describe('Recommended fix or action'),
            }),
            execute: async (args) => {
                const { severity, agents, title, description, evidence, suggestion } = args;
                const timestamp = new Date().toISOString();
                const severityIcon = severity === 'critical' ? '🔴' : severity === 'high' ? '🟠' : severity === 'medium' ? '🟡' : '🔵';
                const entry = [
                    '',
                    `### ${severityIcon} [${severity.toUpperCase()}] ${title}`,
                    `**Time:** ${timestamp}`,
                    `**Agents:** ${agents.join(', ')}`,
                    '',
                    description,
                    '',
                    '**Evidence:**',
                    '```',
                    evidence,
                    '```',
                    '',
                    `**Suggestion:** ${suggestion}`,
                    '',
                    '---',
                    '',
                ].join('\n');
                // Ensure audit directory exists
                await fsPromises.mkdir(auditDir, { recursive: true });
                // Append to bugs.md (create with header if new)
                let existing = '';
                try {
                    existing = await fsPromises.readFile(bugsFile, 'utf8');
                }
                catch {
                    existing = '# Crewly Audit — Bug Reports\n\nAutomated findings from the Crewly Auditor agent.\n\n---\n';
                }
                await fsPromises.writeFile(bugsFile, existing + entry, 'utf8');
                return {
                    success: true,
                    file: bugsFile,
                    severity,
                    title,
                    timestamp,
                };
            },
        },
        reply_slack: {
            description: 'Send a message directly to a Slack thread. Use this to respond to user questions when SLACK_CONTEXT is present. Supports Slack mrkdwn formatting.',
            inputSchema: z.object({
                text: z.string().describe('Message text in Slack mrkdwn format'),
                channelId: z.string().describe('Slack channel ID from SLACK_CONTEXT'),
                threadTs: z.string().describe('Slack thread timestamp from SLACK_CONTEXT'),
            }),
            execute: async (args) => {
                const { text, channelId, threadTs } = args;
                // #181: Convert markdown to Slack mrkdwn format
                const slackText = convertMarkdownToSlackMrkdwn(text);
                const result = await client.post('/slack/send', { channelId, text: slackText, threadTs });
                return result.success ? { sent: true } : { error: result.error };
            },
        },
        create_github_issue: {
            description: 'Create a GitHub issue for a bug or enhancement found during audit (#178). Deduplicates: checks existing open issues before creating. Requires gh CLI to be available.',
            inputSchema: z.object({
                title: z.string().describe('Issue title (include severity prefix like [Critical], [High], [Medium])'),
                body: z.string().describe('Issue body in markdown format'),
                labels: z.array(z.string()).default(['bug']).describe('GitHub labels (e.g., bug, enhancement)'),
            }),
            execute: async (args) => {
                const { title, body, labels } = args;
                const { execSync } = await import('child_process');
                try {
                    // Dedup: search for existing open issues with similar title
                    const searchQuery = title.replace(/\[.*?\]\s*/, '').slice(0, 50);
                    let existingIssues = '';
                    try {
                        existingIssues = execSync(`gh issue list --state open --search "${searchQuery.replace(/"/g, '\\"')}" --json number,title --limit 5`, { encoding: 'utf-8', timeout: 15000 });
                    }
                    catch { /* gh not available or no repo */ }
                    if (existingIssues) {
                        const issues = JSON.parse(existingIssues);
                        const duplicate = issues.find((i) => i.title.toLowerCase().includes(searchQuery.toLowerCase().slice(0, 30)));
                        if (duplicate) {
                            return {
                                success: true,
                                created: false,
                                duplicate: true,
                                existingIssue: `#${duplicate.number}: ${duplicate.title}`,
                            };
                        }
                    }
                    // Create the issue
                    const labelFlag = labels.map(l => `--label "${l}"`).join(' ');
                    const result = execSync(`gh issue create --title "${title.replace(/"/g, '\\"')}" --body "${body.replace(/"/g, '\\"')}" ${labelFlag}`, { encoding: 'utf-8', timeout: 30000 });
                    const issueUrl = result.trim();
                    return { success: true, created: true, url: issueUrl, title };
                }
                catch (error) {
                    return {
                        success: false,
                        error: error instanceof Error ? error.message : String(error),
                        note: 'GitHub CLI (gh) may not be installed or authenticated',
                    };
                }
            },
        },
        read_audit_history: {
            description: 'Read previous audit findings from bugs.md. Use to avoid duplicate reports.',
            inputSchema: z.object({
                lastN: z.number().default(10).describe('Number of recent findings to return'),
            }),
            execute: async (args) => {
                const { lastN } = args;
                try {
                    const content = await fsPromises.readFile(bugsFile, 'utf8');
                    // Split on --- separator, take last N entries
                    const entries = content.split('---').filter(e => e.trim());
                    const recent = entries.slice(-lastN);
                    return {
                        totalFindings: entries.length - 1, // subtract header
                        recentFindings: recent.join('---'),
                    };
                }
                catch {
                    return { totalFindings: 0, recentFindings: 'No audit history found.' };
                }
            },
        },
    };
}
/**
 * Get the list of auditor tool names.
 *
 * @returns Array of auditor tool name strings
 */
export function getAuditorToolNames() {
    return [
        'get_team_status', 'get_agent_logs', 'get_tasks', 'recall_goals',
        'heartbeat', 'get_agent_status', 'subscribe_event',
        'write_audit_report', 'reply_slack', 'read_audit_history', 'create_github_issue',
    ];
}
//# sourceMappingURL=auditor-tools.js.map