@hasna/todos 0.9.37 → 0.9.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +125 -0
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -13970,6 +13970,131 @@ program2.command("interactive").description("Launch interactive TUI").action(asy
|
|
|
13970
13970
|
const projectId = autoProject(globalOpts);
|
|
13971
13971
|
renderApp2(projectId);
|
|
13972
13972
|
});
|
|
13973
|
+
program2.command("next").description("Show the best pending task to work on next").option("--agent <id>", "Prefer tasks assigned to this agent").option("--project <id>", "Filter to project").option("--json", "Output as JSON").action(async (opts) => {
|
|
13974
|
+
const db = getDatabase();
|
|
13975
|
+
const filters = {};
|
|
13976
|
+
if (opts.project)
|
|
13977
|
+
filters.project_id = opts.project;
|
|
13978
|
+
const task = getNextTask(opts.agent, Object.keys(filters).length ? filters : undefined, db);
|
|
13979
|
+
if (!task) {
|
|
13980
|
+
console.log(chalk.dim("No tasks available."));
|
|
13981
|
+
return;
|
|
13982
|
+
}
|
|
13983
|
+
if (opts.json) {
|
|
13984
|
+
console.log(JSON.stringify(task, null, 2));
|
|
13985
|
+
return;
|
|
13986
|
+
}
|
|
13987
|
+
console.log(chalk.bold("Next task:"));
|
|
13988
|
+
console.log(` ${chalk.cyan(task.short_id || task.id.slice(0, 8))} ${chalk.yellow(task.priority)} ${task.title}`);
|
|
13989
|
+
if (task.description)
|
|
13990
|
+
console.log(chalk.dim(` ${task.description.slice(0, 100)}`));
|
|
13991
|
+
});
|
|
13992
|
+
program2.command("claim <agent>").description("Atomically claim the best pending task for an agent").option("--project <id>", "Filter to project").option("--json", "Output as JSON").action(async (agent, opts) => {
|
|
13993
|
+
const db = getDatabase();
|
|
13994
|
+
const filters = {};
|
|
13995
|
+
if (opts.project)
|
|
13996
|
+
filters.project_id = opts.project;
|
|
13997
|
+
const task = claimNextTask(agent, Object.keys(filters).length ? filters : undefined, db);
|
|
13998
|
+
if (!task) {
|
|
13999
|
+
console.log(chalk.dim("No tasks available to claim."));
|
|
14000
|
+
return;
|
|
14001
|
+
}
|
|
14002
|
+
if (opts.json) {
|
|
14003
|
+
console.log(JSON.stringify(task, null, 2));
|
|
14004
|
+
return;
|
|
14005
|
+
}
|
|
14006
|
+
console.log(chalk.green(`Claimed: ${task.short_id || task.id.slice(0, 8)} | ${task.priority} | ${task.title}`));
|
|
14007
|
+
});
|
|
14008
|
+
program2.command("status").description("Show full project health snapshot").option("--agent <id>", "Include next task for this agent").option("--project <id>", "Filter to project").option("--json", "Output as JSON").action(async (opts) => {
|
|
14009
|
+
const db = getDatabase();
|
|
14010
|
+
const filters = {};
|
|
14011
|
+
if (opts.project)
|
|
14012
|
+
filters.project_id = opts.project;
|
|
14013
|
+
const s = getStatus(Object.keys(filters).length ? filters : undefined, opts.agent, db);
|
|
14014
|
+
if (opts.json) {
|
|
14015
|
+
console.log(JSON.stringify(s, null, 2));
|
|
14016
|
+
return;
|
|
14017
|
+
}
|
|
14018
|
+
console.log(`Tasks: ${chalk.yellow(s.pending)} pending | ${chalk.blue(s.in_progress)} active | ${chalk.green(s.completed)} done | ${s.total} total`);
|
|
14019
|
+
if (s.stale_count > 0)
|
|
14020
|
+
console.log(chalk.red(`\u26A0\uFE0F ${s.stale_count} stale tasks (stuck in_progress)`));
|
|
14021
|
+
if (s.overdue_recurring > 0)
|
|
14022
|
+
console.log(chalk.yellow(`\uD83D\uDD01 ${s.overdue_recurring} overdue recurring`));
|
|
14023
|
+
if (s.active_work.length > 0) {
|
|
14024
|
+
console.log(chalk.bold(`
|
|
14025
|
+
Active:`));
|
|
14026
|
+
for (const w of s.active_work.slice(0, 5)) {
|
|
14027
|
+
const id = w.short_id || w.id.slice(0, 8);
|
|
14028
|
+
console.log(` ${chalk.cyan(id)} | ${w.assigned_to || w.locked_by || "?"} | ${w.title}`);
|
|
14029
|
+
}
|
|
14030
|
+
}
|
|
14031
|
+
if (s.next_task) {
|
|
14032
|
+
console.log(chalk.bold(`
|
|
14033
|
+
Next up:`));
|
|
14034
|
+
const t = s.next_task;
|
|
14035
|
+
console.log(` ${chalk.cyan(t.short_id || t.id.slice(0, 8))} ${chalk.yellow(t.priority)} ${t.title}`);
|
|
14036
|
+
}
|
|
14037
|
+
});
|
|
14038
|
+
program2.command("fail <id>").description("Mark a task as failed with optional reason and retry").option("--reason <text>", "Why it failed").option("--agent <id>", "Agent reporting the failure").option("--retry", "Auto-create a retry copy").option("--json", "Output as JSON").action(async (id, opts) => {
|
|
14039
|
+
const db = getDatabase();
|
|
14040
|
+
const resolvedId = resolvePartialId(db, "tasks", id);
|
|
14041
|
+
if (!resolvedId) {
|
|
14042
|
+
console.error(chalk.red(`Task not found: ${id}`));
|
|
14043
|
+
process.exit(1);
|
|
14044
|
+
}
|
|
14045
|
+
const result = failTask(resolvedId, opts.agent, opts.reason, { retry: opts.retry }, db);
|
|
14046
|
+
if (opts.json) {
|
|
14047
|
+
console.log(JSON.stringify(result, null, 2));
|
|
14048
|
+
return;
|
|
14049
|
+
}
|
|
14050
|
+
console.log(chalk.red(`Failed: ${result.task.short_id || result.task.id.slice(0, 8)} | ${result.task.title}`));
|
|
14051
|
+
if (opts.reason)
|
|
14052
|
+
console.log(chalk.dim(`Reason: ${opts.reason}`));
|
|
14053
|
+
if (result.retryTask)
|
|
14054
|
+
console.log(chalk.yellow(`Retry created: ${result.retryTask.short_id || result.retryTask.id.slice(0, 8)} | ${result.retryTask.title}`));
|
|
14055
|
+
});
|
|
14056
|
+
program2.command("active").description("Show all currently in-progress tasks").option("--project <id>", "Filter to project").option("--json", "Output as JSON").action(async (opts) => {
|
|
14057
|
+
const db = getDatabase();
|
|
14058
|
+
const filters = {};
|
|
14059
|
+
if (opts.project)
|
|
14060
|
+
filters.project_id = opts.project;
|
|
14061
|
+
const work = getActiveWork(Object.keys(filters).length ? filters : undefined, db);
|
|
14062
|
+
if (opts.json) {
|
|
14063
|
+
console.log(JSON.stringify(work, null, 2));
|
|
14064
|
+
return;
|
|
14065
|
+
}
|
|
14066
|
+
if (work.length === 0) {
|
|
14067
|
+
console.log(chalk.dim("No active work."));
|
|
14068
|
+
return;
|
|
14069
|
+
}
|
|
14070
|
+
console.log(chalk.bold(`Active work (${work.length}):`));
|
|
14071
|
+
for (const w of work) {
|
|
14072
|
+
const id = w.short_id || w.id.slice(0, 8);
|
|
14073
|
+
const agent = w.assigned_to || w.locked_by || "unassigned";
|
|
14074
|
+
console.log(` ${chalk.cyan(id)} | ${chalk.yellow(w.priority)} | ${agent.padEnd(12)} | ${w.title}`);
|
|
14075
|
+
}
|
|
14076
|
+
});
|
|
14077
|
+
program2.command("stale").description("Find tasks stuck in_progress with no recent activity").option("--minutes <n>", "Stale threshold in minutes", "30").option("--project <id>", "Filter to project").option("--json", "Output as JSON").action(async (opts) => {
|
|
14078
|
+
const db = getDatabase();
|
|
14079
|
+
const filters = {};
|
|
14080
|
+
if (opts.project)
|
|
14081
|
+
filters.project_id = opts.project;
|
|
14082
|
+
const tasks = getStaleTasks(parseInt(opts.minutes, 10), Object.keys(filters).length ? filters : undefined, db);
|
|
14083
|
+
if (opts.json) {
|
|
14084
|
+
console.log(JSON.stringify(tasks, null, 2));
|
|
14085
|
+
return;
|
|
14086
|
+
}
|
|
14087
|
+
if (tasks.length === 0) {
|
|
14088
|
+
console.log(chalk.dim("No stale tasks."));
|
|
14089
|
+
return;
|
|
14090
|
+
}
|
|
14091
|
+
console.log(chalk.bold(`Stale tasks (${tasks.length}):`));
|
|
14092
|
+
for (const t of tasks) {
|
|
14093
|
+
const id = t.short_id || t.id.slice(0, 8);
|
|
14094
|
+
const staleMin = Math.round((Date.now() - new Date(t.updated_at).getTime()) / 60000);
|
|
14095
|
+
console.log(` ${chalk.cyan(id)} | ${t.locked_by || t.assigned_to || "?"} | ${t.title} ${chalk.dim(`(${staleMin}min stale)`)}`);
|
|
14096
|
+
}
|
|
14097
|
+
});
|
|
13973
14098
|
program2.action(async () => {
|
|
13974
14099
|
if (process.stdout.isTTY) {
|
|
13975
14100
|
try {
|