import { initStderrTee } from "../../../../lib/mcp-stderr-tee/dist/index.js";
initStderrTee("work");
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { eagerTool } from "../../../../lib/mcp-eager/dist/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { workCreate } from "./tools/work-create.js";
import { workUpdate } from "./tools/work-update.js";
import { workList } from "./tools/work-list.js";
import { workGet } from "./tools/work-get.js";
import { workRelate } from "./tools/work-relate.js";
import { workComplete } from "./tools/work-complete.js";
import { workReady } from "./tools/work-ready.js";
import { sessionList } from "./tools/session-list.js";
import { sessionName } from "./tools/session-name.js";
import { projectCreate } from "./tools/project-create.js";
import { projectList } from "./tools/project-list.js";
import { projectGet } from "./tools/project-get.js";
import { projectUpdate } from "./tools/project-update.js";
import { projectComplete } from "./tools/project-complete.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-work",
    version: "0.1.0",
});
// Plugin-system boot tolerance — see Task 202.
// Module-init must never throw; plugin-system spawn enumerates tools/list
// before ACCOUNT_ID is set. Per-call refusal preserves account isolation.
const accountId = process.env.ACCOUNT_ID ?? null;
process.stderr.write(`[work] boot accountId=${accountId ?? "null"}\n`);
function refuseNoAccount(toolName) {
    process.stderr.write(`[work] tool=${toolName} refuse reason=no-account-context\n`);
    return {
        content: [
            {
                type: "text",
                text: `${toolName} cannot execute: this MCP instance was launched without ACCOUNT_ID (plugin-system metadata-only mode). Per-account task/project writes require ACCOUNT_ID at spawn.`,
            },
        ],
        isError: true,
    };
}
const sessionId = process.env.SESSION_ID || undefined;
const agentSlug = process.env.AGENT_SLUG || undefined;
// Task 476 — boot signal so a stale spawn-env contract is visible at the
// first byte of stderr. `sessionId` matching `accountId`, or `agentSlug`
// empty on a specialist spawn, are the two regression shapes.
process.stderr.write(`[work] boot accountId=${accountId ?? "-"} sessionId=${sessionId ?? "-"} agentSlug=${agentSlug ?? "-"}\n`);
// ---------------------------------------------------------------------------
// Shared schema fragments — extracted to keep tool registration concise
// ---------------------------------------------------------------------------
const statusSchema = z
    .enum(["pending", "active", "running", "completed", "failed", "cancelled"])
    .optional()
    .describe("Task status. `running` marks the Task as an action mid-flight (auto-sets startedAt) — distinct from `active` (operator-accepted commitment). Transitions to `completed` or `failed` auto-set completedAt; `failed` requires errorMessage.");
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");
// ---------------------------------------------------------------------------
// work-create
// ---------------------------------------------------------------------------
eagerTool(server, "work-create", "Create a Task node in the graph. Every Task must link to at least one node — the current session's Conversation (automatic when called inside an admin session), a raisedBy Person, or at least one affects entity. Embeds for memory-search discovery. For durable-action records: pass `kind` to discriminate this Task as an action record (e.g. \"onboarding-establish-owner\", \"cloudflare-tunnel-login\") with status `running` to mark it mid-flight; pass `inputsProvided` to record the call shape; pass `raisedDuringConversationKey` when the action was raised by a different surface than the current session. Pass `inputs` with the action's form payload and `inputSchema` declaring any secret-tagged keys — values land on the Task as `inputs.<field>` props after secrets are stripped centrally.", {
    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"),
    kind: z.string().optional().describe("Discriminator that marks this Task as an action record — e.g. \"onboarding-establish-owner\", \"cloudflare-tunnel-login\". Enables the lifecycle (started/step/done) and `[task] action-*` log lines."),
    inputsProvided: z.array(z.string()).optional().describe("Property names supplied at action start — the call-shape record. Coexists with `inputs.*` redacted values."),
    inputs: z.record(z.any()).optional().describe("Form payload. Persisted as `inputs.<field>` props after `redactSecrets` strips any keys declared in `inputSchema.secretFields`. Pass values the operator supplied that are meaningful for the audit (mode, hostname, emailProvided). Never include passwords or other secret material in the schema's secretFields list — they will be dropped server-side regardless."),
    inputSchema: z
        .object({
        secretFields: z.array(z.string()).optional().describe("Field names whose values must never land on the Task. Declared at the form definition; the writer strips them before persisting."),
    })
        .optional()
        .describe("Form schema declaring secret-tagged fields. Omit when the action records no secrets."),
    raisedDuringConversationKey: z.string().optional().describe("Conversation sessionKey for the RAISED_DURING edge. Defaults to the current session. Use this when the Task is raised by an out-of-band surface (e.g. an HTTP endpoint) but should still link to a specific conversation."),
}, async (params) => {
    if (!accountId)
        return refuseNoAccount("work-create");
    try {
        const result = await workCreate({
            ...params,
            // eslint-disable-next-line @typescript-eslint/no-explicit-any
            inputs: params.inputs,
            accountId,
            sessionKey: sessionId,
            createdBy: { agent: agentSlug, session: sessionId, tool: "work-create" },
        });
        return {
            content: [{
                    type: "text",
                    text: `Task created: "${params.name}" (ID: ${result.taskId}, status: ${params.status ?? "pending"}, priority: ${params.priority ?? "normal"}${params.kind ? `, kind: ${params.kind}` : ""})`,
                }],
        };
    }
    catch (err) {
        return {
            content: [{ type: "text", text: `Failed to create task: ${err instanceof Error ? err.message : String(err)}` }],
            isError: true,
        };
    }
});
// ---------------------------------------------------------------------------
// work-update
// ---------------------------------------------------------------------------
eagerTool(server, "work-update", "Update a task's name, description, status, priority, or due date. Append a note to its history. Notes are append-only — never overwritten. Use this to correct a task's name or description without losing note history or relationships. For action-record updates: pass `appendStep` to atomically append one entry to the Task's `steps[]` array (call once per phase of an action); pass `errorMessage` when transitioning status to `failed` (required); transitions to `completed` or `failed` auto-set `completedAt`.", {
    taskId: taskIdSchema,
    name: z.string().optional().describe("New task name — replaces the existing name"),
    description: z.string().optional().describe("New task description — replaces the existing description"),
    status: statusSchema,
    priority: prioritySchema,
    dueDate: dueDateSchema,
    note: z.string().optional().describe("Progress note to append. Timestamped and appended to history — never replaces existing notes."),
    appendStep: z.string().optional().describe("Append a single step label to the Task's `steps[]` array. Atomic — one Cypher round-trip; safe under concurrent calls. Use one call per action phase."),
    errorMessage: z.string().optional().describe("Error message stored on the Task when status transitions to `failed`. Required for the `failed` transition; non-empty errorMessage strings only."),
}, async (params) => {
    if (!accountId)
        return refuseNoAccount("work-update");
    try {
        await workUpdate({ ...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,
        };
    }
});
// ---------------------------------------------------------------------------
// work-list
// ---------------------------------------------------------------------------
eagerTool(server, "work-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) => {
    if (!accountId)
        return refuseNoAccount("work-list");
    try {
        const tasks = await workList({ ...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,
        };
    }
});
// ---------------------------------------------------------------------------
// work-get
// ---------------------------------------------------------------------------
eagerTool(server, "work-get", "Fetch a single task with its full description, notes history, and all graph relationships.", {
    taskId: taskIdSchema,
}, async ({ taskId }) => {
    if (!accountId)
        return refuseNoAccount("work-get");
    try {
        const task = await workGet(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,
        };
    }
});
// ---------------------------------------------------------------------------
// work-relate
// ---------------------------------------------------------------------------
eagerTool(server, "work-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 work-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) => {
    if (!accountId)
        return refuseNoAccount("work-relate");
    try {
        await workRelate({ ...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,
        };
    }
});
// ---------------------------------------------------------------------------
// work-complete
// ---------------------------------------------------------------------------
eagerTool(server, "work-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) => {
    if (!accountId)
        return refuseNoAccount("work-complete");
    try {
        const result = await workComplete({ ...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,
        };
    }
});
// ---------------------------------------------------------------------------
// work-ready
// ---------------------------------------------------------------------------
eagerTool(server, "work-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 () => {
    if (!accountId)
        return refuseNoAccount("work-ready");
    try {
        const tasks = await workReady(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
// ---------------------------------------------------------------------------
eagerTool(server, "session-list", "List recent conversation sessions for this account, newest first. Each session shows its name (if set), sessionId, session key, agent type, timestamps, and any linked tasks. Use sessionId when calling session-resume.", {
    limit: z.number().int().min(1).max(50).optional().describe("Max sessions to return (default 20)"),
}, async (params) => {
    if (!accountId)
        return refuseNoAccount("session-list");
    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.sessionId} · ${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
// ---------------------------------------------------------------------------
eagerTool(server, "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) => {
    if (!accountId)
        return refuseNoAccount("session-name");
    try {
        const { affected } = await sessionName(params);
        // affected=0 on a webchat pre-flush first turn — Conversation not yet
        // created (deferred persistence). Surface the deferral
        // so the silent no-op is observable to the agent. The agent should
        // re-call session-name on a later turn (post-flush) if the chosen
        // title needs to land.
        const text = affected > 0
            ? `Session named: "${params.name}"`
            : `Session not yet bound — Conversation will be created at the first completed turn. Re-call session-name on a later turn to apply this title.`;
        return { content: [{ type: "text", text }] };
    }
    catch (err) {
        return {
            content: [{ type: "text", text: `Failed: ${err instanceof Error ? err.message : String(err)}` }],
            isError: true,
        };
    }
});
// ---------------------------------------------------------------------------
// project-create
// ---------------------------------------------------------------------------
const workItemSchema = z.object({
    name: z.string().min(1).describe("Work item name"),
    description: z.string().min(1).describe("Work item scope"),
    priority: prioritySchema,
    dueDate: dueDateSchema,
    blocks: z.array(z.number().int().min(0)).optional().describe("Indices of other workItems this item blocks"),
});
eagerTool(server, "project-create", "Create a project with child work items in a single atomic transaction. Each work item becomes a Task linked via HAS_TASK. Dependency ordering via blocks (array indices). Embeds parent and children for memory-search. Use for any multi-step body of work with a defined scope and client. The Project must have at least one adjacency — pass workItems (≥1), raisedBy, or affects, or invoke inside an admin session with an existing Conversation.", {
    name: z.string().min(1).describe("Project name (e.g. 'Kitchen Refit — Mrs. Chen')"),
    description: z.string().min(1).describe("Project scope description — embedded for semantic search"),
    tier: z.enum(["quick", "standard", "full"]).describe("Execution complexity: quick (1-3 tasks), standard (4-15), full (16-50)"),
    clientRef: z.string().optional().describe("Client reference (e.g. 'Mrs. Chen')"),
    targetDate: z.string().optional().describe("ISO 8601 target completion date"),
    dueDate: dueDateSchema,
    raisedBy: z.string().optional().describe("Person elementId — creates RAISED_BY relationship. Rolls back entire project if not found."),
    affects: z.array(z.string()).optional().describe("Entity elementIds — creates AFFECTS relationships. Rolls back entire project if any not found."),
    workItems: z.array(workItemSchema).optional().describe("Child tasks to create (max 50). Use blocks to express dependencies by array index."),
}, async (params) => {
    if (!accountId)
        return refuseNoAccount("project-create");
    try {
        const result = await projectCreate({
            ...params,
            accountId,
            sessionKey: sessionId,
            createdBy: { agent: agentSlug, session: sessionId, tool: "project-create" },
        });
        return {
            content: [{
                    type: "text",
                    text: `Project created: "${params.name}" (ID: ${result.projectId}, tier: ${params.tier}, ${result.childTaskIds.length} work items)`,
                }],
        };
    }
    catch (err) {
        return {
            content: [{ type: "text", text: `Failed to create project: ${err instanceof Error ? err.message : String(err)}` }],
            isError: true,
        };
    }
});
// ---------------------------------------------------------------------------
// project-list
// ---------------------------------------------------------------------------
eagerTool(server, "project-list", "List active projects with server-computed health signals. Returns structured data: completion percentage, overdue count, blocker count, and health signal (green/amber/red/grey). Use to answer 'what are my projects?' or 'how are things going?'", {
    status: z.string().optional().describe("Filter by status (default: pending + active)"),
    limit: z.number().optional().describe("Max projects to return (default 20)"),
}, async (params) => {
    if (!accountId)
        return refuseNoAccount("project-list");
    try {
        const projects = await projectList({ ...params, accountId });
        if (projects.length === 0) {
            return { content: [{ type: "text", text: "No projects match the filters." }] };
        }
        const lines = projects.map((p) => {
            const health = p.health;
            const progress = `${health.completedTasks}/${health.totalTasks} done`;
            const issues = [
                health.overdueTasks > 0 ? `${health.overdueTasks} overdue` : null,
                health.blockedTasks > 0 ? `${health.blockedTasks} blocked` : null,
            ].filter(Boolean).join(", ");
            const issueStr = issues ? ` — ${issues}` : "";
            const client = p.clientRef ? ` (${p.clientRef})` : "";
            return `[${health.signal.toUpperCase()}] ${p.name}${client} — ${p.tier} tier, ${p.phase}, ${progress}${issueStr}`;
        });
        return {
            content: [{ type: "text", text: `${projects.length} project(s):\n\n${lines.join("\n")}` }],
        };
    }
    catch (err) {
        return {
            content: [{ type: "text", text: `List failed: ${err instanceof Error ? err.message : String(err)}` }],
            isError: true,
        };
    }
});
// ---------------------------------------------------------------------------
// project-get
// ---------------------------------------------------------------------------
eagerTool(server, "project-get", "Fetch full project detail: description, children with status and blockers, health signal, lifecycle notes, and relationships. Use when the user asks about a specific project's status or details.", {
    projectId: z.string().describe("Project taskId (UUID)"),
}, async ({ projectId }) => {
    if (!accountId)
        return refuseNoAccount("project-get");
    try {
        const project = await projectGet(projectId, accountId);
        const childLines = project.children.length > 0
            ? project.children.map((c) => {
                const blocked = c.blockedBy.length > 0 ? ` [blocked by: ${c.blockedBy.join(", ")}]` : "";
                const due = c.dueDate ? ` due:${c.dueDate.slice(0, 10)}` : "";
                return `  [${c.priority.toUpperCase()}] ${c.status} — ${c.name}${due}${blocked} (${c.taskId})`;
            }).join("\n")
            : "  (no work items)";
        const relLines = project.relationships.length > 0
            ? project.relationships
                .map((r) => `  -[${r.type}]-> ${r.targetLabel}: ${r.targetName ?? r.targetId}`)
                .join("\n")
            : "  (none)";
        const health = project.health;
        const healthLine = `Health: ${health.signal.toUpperCase()} — ${health.completedTasks}/${health.totalTasks} done, ${health.overdueTasks} overdue, ${health.blockedTasks} blocked`;
        const parts = [
            `Project: ${project.name}`,
            `ID: ${project.projectId}`,
            `Tier: ${project.tier} | Phase: ${project.phase} | Status: ${project.status}`,
            project.clientRef ? `Client: ${project.clientRef}` : null,
            project.targetDate ? `Target: ${project.targetDate}` : null,
            project.dueDate ? `Due: ${project.dueDate}` : null,
            healthLine,
            ``,
            `Description:\n${project.description}`,
            project.notes ? `\nNotes:\n${project.notes}` : null,
            ``,
            `Work Items:\n${childLines}`,
            ``,
            `Relationships:\n${relLines}`,
            ``,
            `Created: ${project.createdAt} | Updated: ${project.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,
        };
    }
});
// ---------------------------------------------------------------------------
// project-update
// ---------------------------------------------------------------------------
eagerTool(server, "project-update", "Update project properties. Phase changes automatically append a [PROJECT:PHASE] lifecycle note. Re-embeds for memory-search. Use for phase transitions, scope notes, tier adjustments, and status changes.", {
    projectId: z.string().describe("Project taskId (UUID)"),
    phase: z.string().optional().describe("New lifecycle phase: planning, active, blocked, verifying, complete, abandoned"),
    tier: z.enum(["quick", "standard", "full"]).optional().describe("Updated execution tier"),
    targetDate: z.string().optional().describe("Updated target completion date (ISO 8601)"),
    clientRef: z.string().optional().describe("Updated client reference"),
    note: noteSchema,
    status: z.enum(["pending", "active", "completed", "failed", "cancelled", "abandoned"]).optional().describe("Updated status"),
}, async (params) => {
    if (!accountId)
        return refuseNoAccount("project-update");
    try {
        const result = await projectUpdate({ ...params, accountId });
        const parts = [`Project updated: ${params.projectId}`];
        if (result.phaseChanged) {
            parts.push(`Phase: ${result.phaseChanged.from} → ${result.phaseChanged.to}`);
        }
        if (result.fieldsUpdated.length > 0) {
            parts.push(`Fields: ${result.fieldsUpdated.join(", ")}`);
        }
        return { content: [{ type: "text", text: parts.join("\n") }] };
    }
    catch (err) {
        return {
            content: [{ type: "text", text: `Update failed: ${err instanceof Error ? err.message : String(err)}` }],
            isError: true,
        };
    }
});
// ---------------------------------------------------------------------------
// project-complete
// ---------------------------------------------------------------------------
eagerTool(server, "project-complete", "Complete a project. Returns any incomplete children so the agent can confirm with the user. Always completes — does not gate on unfinished work items. Appends [PROJECT:COMPLETE] lifecycle note.", {
    projectId: z.string().describe("Project taskId (UUID)"),
    note: noteSchema,
}, async (params) => {
    if (!accountId)
        return refuseNoAccount("project-complete");
    try {
        const result = await projectComplete({ ...params, accountId });
        const parts = [`Project completed: ${params.projectId} (${result.completedTasks}/${result.totalTasks} tasks done)`];
        if (result.incompleteChildren.length > 0) {
            parts.push("", "Incomplete work items:", ...result.incompleteChildren.map((c) => `  [${c.status}] ${c.name} (${c.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,
        };
    }
});
// ---------------------------------------------------------------------------
// Shutdown + start
// ---------------------------------------------------------------------------
process.on("SIGINT", async () => {
    await closeDriver();
    process.exit(0);
});
const transport = new StdioServerTransport();
await server.connect(transport);
//# sourceMappingURL=index.js.map