import { initStderrTee } from "../../../../lib/mcp-stderr-tee/dist/index.js";
initStderrTee("tasks");
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { taskCreate } from "./tools/task-create.js";
import { taskUpdate } from "./tools/task-update.js";
import { taskList } from "./tools/task-list.js";
import { taskGet } from "./tools/task-get.js";
import { taskRelate } from "./tools/task-relate.js";
import { taskComplete } from "./tools/task-complete.js";
import { taskReady } from "./tools/task-ready.js";
import { sessionList } from "./tools/session-list.js";
import { sessionName } from "./tools/session-name.js";
import { closeDriver } from "./lib/neo4j.js";
// Cast to `any` to avoid TS2589. TypeScript hits its 50-level instantiation
// limit when accumulating 6 typed tool signatures on a generic McpServer.
// Individual tool handlers retain full type safety in their own files.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const server = new McpServer({
    name: "maxy-tasks",
    version: "0.1.0",
});
const accountId = process.env.ACCOUNT_ID;
if (!accountId) {
    throw new Error("ACCOUNT_ID environment variable is required");
}
const sessionId = process.env.SESSION_ID || undefined;
// ---------------------------------------------------------------------------
// Shared schema fragments — extracted to keep tool registration concise
// ---------------------------------------------------------------------------
const statusSchema = z
    .enum(["pending", "active", "completed", "failed", "cancelled"])
    .optional()
    .describe("Task status");
const prioritySchema = z
    .enum(["low", "normal", "high", "urgent"])
    .optional()
    .describe("Task priority");
const dueDateSchema = z.string().optional().describe("ISO 8601 due date");
const taskIdSchema = z.string().describe("Task ID (UUID)");
const noteSchema = z.string().optional().describe("Note appended to task history with timestamp");
// ---------------------------------------------------------------------------
// task-create
// ---------------------------------------------------------------------------
server.tool("task-create", "Create a Task node in the graph. Automatically linked to the current session. Optionally link to a person (RAISED_BY) or entities the task will modify (AFFECTS) for conflict detection. Embeds for memory-search discovery.", {
    name: z.string().describe("Short task title"),
    description: z.string().describe("Full scope and acceptance criteria — embedded for semantic search"),
    status: statusSchema,
    priority: prioritySchema,
    dueDate: dueDateSchema,
    raisedBy: z.string().optional().describe("Person elementId — creates a RAISED_BY relationship"),
    affects: z.array(z.string()).optional().describe("Entity elementIds — creates AFFECTS relationships for conflict detection"),
}, async (params) => {
    try {
        const result = await taskCreate({ ...params, accountId, sessionKey: sessionId });
        return {
            content: [{
                    type: "text",
                    text: `Task created: "${params.name}" (ID: ${result.taskId}, status: ${params.status ?? "pending"}, priority: ${params.priority ?? "normal"})`,
                }],
        };
    }
    catch (err) {
        return {
            content: [{ type: "text", text: `Failed to create task: ${err instanceof Error ? err.message : String(err)}` }],
            isError: true,
        };
    }
});
// ---------------------------------------------------------------------------
// task-update
// ---------------------------------------------------------------------------
server.tool("task-update", "Update a task's status, priority, or due date. Append a note to its history. Notes are append-only — never overwritten.", {
    taskId: taskIdSchema,
    status: statusSchema,
    priority: prioritySchema,
    dueDate: dueDateSchema,
    note: z.string().optional().describe("Progress note to append. Timestamped and appended to history — never replaces existing notes."),
}, async (params) => {
    try {
        await taskUpdate({ ...params, accountId });
        return { content: [{ type: "text", text: `Task updated: ${params.taskId}` }] };
    }
    catch (err) {
        return {
            content: [{ type: "text", text: `Failed to update task: ${err instanceof Error ? err.message : String(err)}` }],
            isError: true,
        };
    }
});
// ---------------------------------------------------------------------------
// task-list
// ---------------------------------------------------------------------------
server.tool("task-list", "List tasks filtered by status, priority, or related entity. Always scoped to this account. Ordered by priority then creation date.", {
    status: statusSchema,
    priority: prioritySchema,
    relatedEntityId: z.string().optional().describe("Neo4j elementId — return tasks related to this entity"),
    limit: z.number().optional().describe("Max results (default 50)"),
}, async (params) => {
    try {
        const tasks = await taskList({ ...params, accountId });
        if (tasks.length === 0) {
            return { content: [{ type: "text", text: "No tasks match the filters." }] };
        }
        const lines = tasks.map((t) => {
            const due = t.dueDate ? ` due:${t.dueDate.slice(0, 10)}` : "";
            return `[${t.priority.toUpperCase()}] ${t.status} — ${t.name}${due} (${t.taskId})`;
        });
        return {
            content: [{ type: "text", text: `${tasks.length} task(s):\n\n${lines.join("\n")}` }],
        };
    }
    catch (err) {
        return {
            content: [{ type: "text", text: `List failed: ${err instanceof Error ? err.message : String(err)}` }],
            isError: true,
        };
    }
});
// ---------------------------------------------------------------------------
// task-get
// ---------------------------------------------------------------------------
server.tool("task-get", "Fetch a single task with its full description, notes history, and all graph relationships.", {
    taskId: taskIdSchema,
}, async ({ taskId }) => {
    try {
        const task = await taskGet(taskId, accountId);
        if (!task) {
            return { content: [{ type: "text", text: `Task not found: ${taskId}` }] };
        }
        const relLines = task.relationships.length > 0
            ? task.relationships
                .map((r) => `  -[${r.type}]-> ${r.targetLabel}: ${r.targetName ?? r.targetId}`)
                .join("\n")
            : "  (none)";
        const parts = [
            `Task: ${task.name}`,
            `ID: ${task.taskId}`,
            `Status: ${task.status} | Priority: ${task.priority}`,
            task.dueDate ? `Due: ${task.dueDate}` : null,
            ``,
            `Description:\n${task.description}`,
            task.notes ? `\nNotes:\n${task.notes}` : null,
            ``,
            `Relationships:\n${relLines}`,
            ``,
            `Created: ${task.createdAt} | Updated: ${task.updatedAt}`,
        ].filter((line) => line !== null);
        return { content: [{ type: "text", text: parts.join("\n") }] };
    }
    catch (err) {
        return {
            content: [{ type: "text", text: `Get failed: ${err instanceof Error ? err.message : String(err)}` }],
            isError: true,
        };
    }
});
// ---------------------------------------------------------------------------
// task-relate
// ---------------------------------------------------------------------------
server.tool("task-relate", "Add a typed relationship from a task to any graph entity. AFFECTS marks entities the task will modify (used for conflict detection). BLOCKS creates sequential dependencies. PART_OF links subtasks. RAISED_BY links to the requesting person.", {
    taskId: taskIdSchema,
    targetId: z.string().describe("Target identifier. For Task targets: the taskId (UUID) returned by task-create. For all other labels: the Neo4j elementId from memory-search."),
    targetLabel: z.string().describe("Label of the target node (e.g. Person, Service, LocalBusiness, Task)"),
    relType: z.enum(["AFFECTS", "RAISED_BY", "PART_OF", "BLOCKS"]).describe("Relationship type"),
}, async (params) => {
    try {
        await taskRelate({ ...params, accountId });
        return {
            content: [{
                    type: "text",
                    text: `Relationship created: Task(${params.taskId}) -[${params.relType}]-> ${params.targetLabel}(${params.targetId})`,
                }],
        };
    }
    catch (err) {
        return {
            content: [{ type: "text", text: `Relate failed: ${err instanceof Error ? err.message : String(err)}` }],
            isError: true,
        };
    }
});
// ---------------------------------------------------------------------------
// task-complete
// ---------------------------------------------------------------------------
server.tool("task-complete", "Mark a task as completed. Re-embeds for memory-search and surfaces any tasks that were blocked by this one and are now ready to start.", {
    taskId: taskIdSchema,
    note: noteSchema,
}, async (params) => {
    try {
        const result = await taskComplete({ ...params, accountId });
        const parts = [`Task completed: ${params.taskId}`];
        if (result.unblockedTasks.length > 0) {
            parts.push("", "Newly unblocked tasks:", ...result.unblockedTasks.map((t) => `  [${t.priority}] ${t.name} (${t.taskId})`));
        }
        return { content: [{ type: "text", text: parts.join("\n") }] };
    }
    catch (err) {
        return {
            content: [{ type: "text", text: `Complete failed: ${err instanceof Error ? err.message : String(err)}` }],
            isError: true,
        };
    }
});
// ---------------------------------------------------------------------------
// task-ready
// ---------------------------------------------------------------------------
server.tool("task-ready", "Returns tasks that are safe to start: status=pending, zero unresolved BLOCKS, and no AFFECTS conflict with currently active tasks. Use at session start to surface the ready set, or before starting any task to verify it is safe.", {}, async () => {
    try {
        const tasks = await taskReady(accountId);
        if (tasks.length === 0) {
            return {
                content: [{
                        type: "text",
                        text: "No tasks are ready to start. All pending tasks have unresolved blockers or AFFECTS conflicts with active tasks.",
                    }],
            };
        }
        const lines = tasks.map((t) => `[${t.priority}] ${t.name} (${t.taskId})${t.dueDate ? ` — due: ${t.dueDate}` : ""}`);
        return {
            content: [{
                    type: "text",
                    text: `${tasks.length} task(s) ready to start:\n\n${lines.join("\n")}`,
                }],
        };
    }
    catch (err) {
        return {
            content: [{ type: "text", text: `Ready check failed: ${err instanceof Error ? err.message : String(err)}` }],
            isError: true,
        };
    }
});
// ---------------------------------------------------------------------------
// session-list
// ---------------------------------------------------------------------------
server.tool("session-list", "List recent conversation sessions for this account, newest first. Each session shows its name (if set), conversationId, session key, agent type, timestamps, and any linked tasks. Use conversationId when calling session-resume.", {
    limit: z.number().int().min(1).max(50).optional().describe("Max sessions to return (default 20)"),
}, async (params) => {
    try {
        const sessions = await sessionList({ accountId, limit: params.limit });
        if (sessions.length === 0) {
            return { content: [{ type: "text", text: "No sessions found." }] };
        }
        const lines = sessions.map(s => {
            const label = s.name ? `"${s.name}"` : "(unnamed)";
            const tasks = s.tasks.length > 0
                ? s.tasks.map(t => `    • ${t.name} [${t.status}]`).join("\n")
                : "    (no linked tasks)";
            return `${label} · ${s.conversationId} · ${s.agentType} · updated ${s.updatedAt}\n${tasks}`;
        });
        return { content: [{ type: "text", text: lines.join("\n\n") }] };
    }
    catch (err) {
        return {
            content: [{ type: "text", text: `Failed: ${err instanceof Error ? err.message : String(err)}` }],
            isError: true,
        };
    }
});
// ---------------------------------------------------------------------------
// session-name
// ---------------------------------------------------------------------------
server.tool("session-name", "Set a human-readable name on the current conversation session. Call this as soon as the session's purpose is clear. Use a short label that reflects the work being done (e.g. 'Telegram setup', 'Q1 invoicing').", {
    sessionKey: z.string().describe("Current session key (from system prompt)"),
    name: z.string().describe("Short human-readable session label"),
}, async (params) => {
    try {
        await sessionName(params);
        return { content: [{ type: "text", text: `Session named: "${params.name}"` }] };
    }
    catch (err) {
        return {
            content: [{ type: "text", text: `Failed: ${err instanceof Error ? err.message : String(err)}` }],
            isError: true,
        };
    }
});
// ---------------------------------------------------------------------------
// Shutdown + start
// ---------------------------------------------------------------------------
process.on("SIGINT", async () => {
    await closeDriver();
    process.exit(0);
});
const transport = new StdioServerTransport();
await server.connect(transport);
//# sourceMappingURL=index.js.map