UNPKG

scai

Version:

> **A local-first AI CLI for understanding, querying, and iterating on large codebases.** > **100% local • No token costs • No cloud • No prompt injection • Private by design**

157 lines (156 loc) 5.53 kB
import chalk from "chalk"; import { getDbForRepo } from "../db/client.js"; import { getRl } from "./ReadlineSingleton.js"; export async function runTasksCommand() { const { rl, isTemporary } = getRl(); try { const db = getDbForRepo(); const tasks = db.prepare(` SELECT id, status, initial_query, created_at FROM tasks ORDER BY id DESC `).all(); if (!tasks.length) { console.log(chalk.dim("No tasks found.")); return; } console.log(chalk.bold("\nTasks:\n")); for (const t of tasks) { console.log(`${chalk.cyan(`[${t.id}]`)} ${chalk.yellow(t.status.padEnd(9))} ${oneLineTruncate(t.initial_query)}`); } await new Promise((resolve) => { rl.question("\nSelect task id (q to quit): ", answer => { const trimmed = answer.trim(); if (trimmed.toLowerCase() === "q") { resolve(); return; } if (!trimmed) { console.log(chalk.red("Please enter a task id or 'q' to quit.")); resolve(); return; } const id = Number(trimmed); if (!Number.isInteger(id)) { console.log(chalk.red("Invalid task id.")); resolve(); return; } showTaskDetails(id); resolve(); }); }); } finally { if (isTemporary) { rl.close(); } } } export function showTaskDetails(taskId) { const db = getDbForRepo(); const task = db.prepare(`SELECT * FROM tasks WHERE id = ?`).get(taskId); if (!task) { console.log(chalk.red(`Task ${taskId} not found.`)); return; } console.log(chalk.bold(`\nTask ${task.id}${chalk.yellow(task.status)}\n`)); const print = (label, value, indent = " ") => { if (value == null) return; const isArray = Array.isArray(value); const isObject = typeof value === "object" && !isArray; if (typeof value === "string" && value.length < 80) { console.log(chalk.yellow(`${label}:`), value); } else if (isArray) { console.log(chalk.yellow(`${label}:`)); const items = value; const displayCount = Math.min(5, items.length); for (let i = 0; i < displayCount; i++) { console.log(`${indent}- ${oneLineTruncate(String(items[i]), 100)}`); } if (items.length > displayCount) { console.log(`${indent}… (+${items.length - displayCount} more)`); } } else if (isObject) { console.log(chalk.yellow(`${label}:`)); console.log(indent + JSON.stringify(value, null, 2).replace(/\n/g, "\n" + indent)); } else { console.log(chalk.yellow(`${label}:`)); console.log(indent + String(value)); } }; // Lifecycle print("Initial query", task.initial_query); if (task.summary) { console.log(chalk.green.bold("\nFinal answer:\n")); console.log(chalk.white(task.summary)); console.log(); } // Intent print("Agreed intent", task.agreed_intent); print("Intent category", task.intent_category); print("Normalized query", task.normalized_query); print("Intent confidence", task.intent_confidence); // Focus / files if (task.relevant_files_json) { try { print("Relevant files", JSON.parse(task.relevant_files_json)); } catch { print("Relevant files", "[invalid JSON]"); } } if (task.missing_files_json) { try { print("Missing files", JSON.parse(task.missing_files_json)); } catch { print("Missing files", "[invalid JSON]"); } } // Routing decision if (task.routing_decision_json) { try { print("Routing decision", JSON.parse(task.routing_decision_json)); } catch { print("Routing decision", "[invalid JSON]"); } } // Bookkeeping print("Created", task.created_at); print("Updated", task.updated_at); // ----------------- TASK STEPS ----------------- const steps = db.prepare(` SELECT id, file_path, action, status, step_index, start_time, end_time, created_at, updated_at FROM task_steps WHERE task_id = ? ORDER BY step_index ASC `).all(taskId); if (steps.length === 0) { console.log(chalk.dim("\nNo task steps recorded.")); } else { console.log(chalk.bold("\nTask Steps:\n")); steps.forEach(s => { console.log(`${chalk.cyan(`[${s.id}]`)} ` + `${chalk.yellow((s.status || "").padEnd(9))} ` + `${s.file_path}` + (s.action ? ` → ${s.action}` : "") + ` (index=${s.step_index ?? "-"})` + (s.start_time ? ` started=${s.start_time}` : "") + (s.end_time ? ` ended=${s.end_time}` : "")); }); } } /** Helper: truncate long text to a single line */ function oneLineTruncate(input, max = 80) { const singleLine = input.replace(/\s+/g, " ").trim(); if (singleLine.length <= max) return singleLine; return singleLine.slice(0, max - 1) + "…"; }