/** * Vikunja integration — low-level API-wrapper skill. * * WHY THIS EXISTS. `vikunja` was registered as an integration id long before * anything implemented it: the id gated a card in the UI, and the ONLY code that * ever talked to a board was `board-runner`'s own `lib/tracker.js`, calling REST * inline. That works, and it stays — a scheduled runner wants deterministic, * batched calls it can retry. But it meant the board was reachable by exactly * one template, doing exactly the five things that template's code spelled out. * No other agent could touch it, and nobody could say "move that card to Doing". * * This skill is the other half: the same board, handed to the MODEL as tools. * The two layers are deliberate and neither replaces the other — * tracker.js = deterministic, node-side, batch, retry-safe; * this skill = interactive, model-driven, one action at a time. * * SHAPE: `linear.ts`, not `jira.ts`. Jira spawns a separate MCP server package * (@zibby/mcp-jira); Vikunja needs no such thing — the tools live on this object * and the generic skill server (bin/mcp-skill.mjs) exposes them. One file. * * API FACTS ARE INHERITED, NOT REDISCOVERED. Every endpoint below was verified * live against a real Vikunja by the board-runner tracker (its header block * documents the traps). The ones that bite: * • `GET /tasks/all` is HTTP 400 even bare — list PROJECT-scoped. * • the `labels` filter takes the NUMERIC label id, never the title (a title * 400s with code 4019); resolve via `GET /labels?s=`. * • an empty label list comes back as JSON `null`, not `[]`. * • a comment is `PUT /tasks/{id}/comments`, not POST. * • MOVING A CARD IS NOT A FIELD WRITE. There is no task.status. A board * column is a KANBAN BUCKET on a VIEW: resolve the project's kanban view, * match the bucket by title, then POST the task into it. This is the single * most surprising thing about the API and the reason `vikunja_move_task` * exists as its own verb rather than as an argument to update. * • `identifier` ('#1') is per-project, NOT globally unique — address tasks by * their numeric id. * • `description` is HTML when it was edited in the UI — strip tags on read. * * AUTH. `resolveIntegrationToken('vikunja')` returns BOTH halves — `{token, * instanceUrl}` — because a credential alone cannot address a board. Same * resolution order and the same error semantics as the tracker: a connected row * that somehow carries no instance says exactly that, rather than letting the * next call fail and blaming the token. */ /** For tests: forget the memoized credentials. */ export declare function clearVikunjaCredsCache(): void; /** * Vikunja stores descriptions and comments as Tiptap HTML. Both directions go * through ONE grammar (`lib/markup.ts`): Markdown the model writes → HTML the * board renders; HTML the board holds → Markdown the model reads. Same module * the Jira skill uses, so a body reads the same off either tracker. */ export declare function vkPlainText(html: any): string; /** Markdown → the HTML Vikunja's editor renders. */ export declare function vkRichHtml(markdown: any): string; /** Vikunja priority is 0-5; accept the words a human (or a model) would use. */ export declare function vkPriority(p: any): number | null; export declare const vikunjaSkill: any;