refactor(bridge): update tasks-file route

This commit is contained in:
Manohar 2026-06-05 19:49:23 +00:00
parent e00c6db544
commit 598dbdd4a4

View file

@ -6,18 +6,6 @@
* GET /tiger/file-tasks/active only active/pending-action tasks * GET /tiger/file-tasks/active only active/pending-action tasks
* GET /tiger/file-tasks/completed only completed tasks * GET /tiger/file-tasks/completed only completed tasks
* GET /tiger/file-tasks/projects all projects from PROJECTS.md * GET /tiger/file-tasks/projects all projects from PROJECTS.md
*
* Parser contract (TASKS.md):
* Tiger maintains a fenced ```json TASKS ... ``` block at the bottom of
* TASKS.md. The parser reads ONLY from that block no regex over markdown.
* If the block is absent, the endpoint returns HTTP 502 with a clear error.
* Tiger's MEMORY.md contains the rule: always emit the TASKS_JSON block
* on every TASKS.md write.
*
* Task JSON schema per entry:
* { id, title, agent, status, section, note?, age?, project? }
* section values: "pending-action" | "in-progress" | "completed"
* status values: "pending-action" | "in-progress" | "blocked" | "done"
*/ */
import { Router, Request, Response } from "express"; import { Router, Request, Response } from "express";
@ -25,16 +13,10 @@ import { execInSandbox } from "../tiger.js";
const router = Router(); const router = Router();
// ── Parser: read TASKS_JSON fenced block from TASKS.md ───────────────────────
function parseTasksJsonBlock(stdout: string): any[] { function parseTasksJsonBlock(stdout: string): any[] {
// Match: ```json\nTASKS\n<json>\n```
const match = stdout.match(/```json\s+TASKS\s*\n([\s\S]+?)\n```/); const match = stdout.match(/```json\s+TASKS\s*\n([\s\S]+?)\n```/);
if (!match) { if (!match) {
throw new Error( throw new Error("TASKS.md missing TASKS_JSON block.");
"TASKS.md missing TASKS_JSON block. Tiger must emit:\n" +
"```json\nTASKS\n[...]\n```\n" +
"at the bottom of every TASKS.md write."
);
} }
try { try {
return JSON.parse(match[1]); return JSON.parse(match[1]);
@ -43,7 +25,6 @@ function parseTasksJsonBlock(stdout: string): any[] {
} }
} }
// ── Parser: projects from PROJECTS.md ────────────────────────────────────────
function parseProjectsMarkdown(stdout: string) { function parseProjectsMarkdown(stdout: string) {
const projects: any[] = []; const projects: any[] = [];
const lines = stdout.split("\n"); const lines = stdout.split("\n");
@ -53,7 +34,6 @@ function parseProjectsMarkdown(stdout: string) {
const trimmed = line.trim(); const trimmed = line.trim();
if (trimmed.startsWith("## Active Projects")) { inActive = true; continue; } if (trimmed.startsWith("## Active Projects")) { inActive = true; continue; }
if (trimmed.startsWith("## Completed Projects")) break; if (trimmed.startsWith("## Completed Projects")) break;
if (inActive && trimmed.match(/^\| \d/)) { if (inActive && trimmed.match(/^\| \d/)) {
const cols = trimmed.split("|").map((c: string) => c.trim()).filter(Boolean); const cols = trimmed.split("|").map((c: string) => c.trim()).filter(Boolean);
if (cols.length >= 5) { if (cols.length >= 5) {
@ -68,12 +48,9 @@ function parseProjectsMarkdown(stdout: string) {
} }
} }
} }
return projects; return projects;
} }
// ── Routes ────────────────────────────────────────────────────────────────────
// GET /tiger/file-tasks — all tasks // GET /tiger/file-tasks — all tasks
router.get("/", async (req: Request, res: Response) => { router.get("/", async (req: Request, res: Response) => {
try { try {
@ -81,7 +58,14 @@ router.get("/", async (req: Request, res: Response) => {
const allTasks = parseTasksJsonBlock(stdout); const allTasks = parseTasksJsonBlock(stdout);
const projectFilter = (req.query.project as string || "").trim().toLowerCase(); const projectFilter = (req.query.project as string || "").trim().toLowerCase();
const filtered = projectFilter const filtered = projectFilter
? allTasks.filter((t: any) => t.project?.toLowerCase().includes(projectFilter)) ? allTasks.filter((t: any) => {
const pid = (t.project || "").toLowerCase().replace(/^p0?/, "");
return (
pid.includes(projectFilter) ||
t.project?.toLowerCase().includes(projectFilter) ||
t.title?.toLowerCase().includes(projectFilter)
);
})
: allTasks; : allTasks;
res.json({ ok: true, source: "TASKS.md", count: filtered.length, tasks: filtered }); res.json({ ok: true, source: "TASKS.md", count: filtered.length, tasks: filtered });
} catch (err: any) { } catch (err: any) {