@hasna/todos 0.11.30 → 0.11.31
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/commands/machines.d.ts +3 -0
- package/dist/cli/commands/machines.d.ts.map +1 -0
- package/dist/cli/index.js +966 -574
- package/dist/db/machines.d.ts +30 -1
- package/dist/db/machines.d.ts.map +1 -1
- package/dist/db/schema.d.ts.map +1 -1
- package/dist/index.js +19 -3
- package/dist/lib/logger.d.ts +17 -0
- package/dist/lib/logger.d.ts.map +1 -0
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +269 -2
- package/dist/mcp/tools/machines.d.ts +8 -0
- package/dist/mcp/tools/machines.d.ts.map +1 -0
- package/dist/server/index.js +7 -1
- package/dist/types/index.d.ts +6 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +3 -2
package/dist/cli/index.js
CHANGED
|
@@ -3049,10 +3049,15 @@ function ensureSchema(db) {
|
|
|
3049
3049
|
ensureTable("machines", `
|
|
3050
3050
|
CREATE TABLE machines (
|
|
3051
3051
|
id TEXT PRIMARY KEY, name TEXT NOT NULL UNIQUE, hostname TEXT, platform TEXT,
|
|
3052
|
+
ssh_address TEXT, is_primary INTEGER NOT NULL DEFAULT 0,
|
|
3052
3053
|
last_seen_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
3054
|
+
archived_at TEXT,
|
|
3053
3055
|
metadata TEXT DEFAULT '{}',
|
|
3054
3056
|
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
3055
3057
|
)`);
|
|
3058
|
+
ensureColumn("machines", "ssh_address", "TEXT");
|
|
3059
|
+
ensureColumn("machines", "is_primary", "INTEGER NOT NULL DEFAULT 0");
|
|
3060
|
+
ensureColumn("machines", "archived_at", "TEXT");
|
|
3056
3061
|
ensureColumn("projects", "task_list_id", "TEXT");
|
|
3057
3062
|
ensureColumn("projects", "task_prefix", "TEXT");
|
|
3058
3063
|
ensureColumn("projects", "task_counter", "INTEGER NOT NULL DEFAULT 0");
|
|
@@ -3281,19 +3286,25 @@ var init_schema = __esm(() => {
|
|
|
3281
3286
|
// src/db/machines.ts
|
|
3282
3287
|
var exports_machines = {};
|
|
3283
3288
|
__export(exports_machines, {
|
|
3289
|
+
unarchiveMachine: () => unarchiveMachine,
|
|
3290
|
+
setPrimaryMachine: () => setPrimaryMachine,
|
|
3284
3291
|
resetMachineId: () => resetMachineId,
|
|
3292
|
+
registerMachine: () => registerMachine,
|
|
3285
3293
|
listMachines: () => listMachines,
|
|
3294
|
+
getPrimaryMachine: () => getPrimaryMachine,
|
|
3286
3295
|
getOrCreateLocalMachine: () => getOrCreateLocalMachine,
|
|
3287
3296
|
getMachineId: () => getMachineId,
|
|
3288
3297
|
getMachineByName: () => getMachineByName,
|
|
3289
3298
|
getMachine: () => getMachine,
|
|
3290
3299
|
deleteMachine: () => deleteMachine,
|
|
3291
|
-
backfillMachineId: () => backfillMachineId
|
|
3300
|
+
backfillMachineId: () => backfillMachineId,
|
|
3301
|
+
archiveMachine: () => archiveMachine
|
|
3292
3302
|
});
|
|
3293
3303
|
import { hostname as osHostname, platform as osPlatform } from "os";
|
|
3294
3304
|
function rowToMachine(row) {
|
|
3295
3305
|
return {
|
|
3296
3306
|
...row,
|
|
3307
|
+
is_primary: !!row.is_primary,
|
|
3297
3308
|
metadata: row.metadata ? JSON.parse(row.metadata) : {}
|
|
3298
3309
|
};
|
|
3299
3310
|
}
|
|
@@ -3310,7 +3321,7 @@ function getOrCreateLocalMachine(db) {
|
|
|
3310
3321
|
const id = uuid();
|
|
3311
3322
|
const ts = now();
|
|
3312
3323
|
d.run("INSERT INTO machines (id, name, hostname, platform, last_seen_at, metadata, created_at) VALUES (?, ?, ?, ?, ?, '{}', ?)", [id, name, host, plat, ts, ts]);
|
|
3313
|
-
return { id, name, hostname: host, platform: plat, last_seen_at: ts, metadata: {}, created_at: ts };
|
|
3324
|
+
return { id, name, hostname: host, platform: plat, ssh_address: null, is_primary: false, last_seen_at: ts, archived_at: null, metadata: {}, created_at: ts };
|
|
3314
3325
|
}
|
|
3315
3326
|
function getMachineId(db) {
|
|
3316
3327
|
if (_machineId)
|
|
@@ -3332,13 +3343,77 @@ function getMachineByName(name, db) {
|
|
|
3332
3343
|
const row = d.query("SELECT * FROM machines WHERE name = ?").get(name);
|
|
3333
3344
|
return row ? rowToMachine(row) : null;
|
|
3334
3345
|
}
|
|
3335
|
-
function listMachines(db) {
|
|
3346
|
+
function listMachines(db, includeArchived = false) {
|
|
3336
3347
|
const d = db || getDatabase();
|
|
3337
|
-
const
|
|
3348
|
+
const query = includeArchived ? "SELECT * FROM machines ORDER BY last_seen_at DESC" : "SELECT * FROM machines WHERE archived_at IS NULL ORDER BY last_seen_at DESC";
|
|
3349
|
+
const rows = d.query(query).all();
|
|
3338
3350
|
return rows.map(rowToMachine);
|
|
3339
3351
|
}
|
|
3352
|
+
function registerMachine(name, opts, db) {
|
|
3353
|
+
const d = db || getDatabase();
|
|
3354
|
+
const existing = d.query("SELECT * FROM machines WHERE name = ?").get(name);
|
|
3355
|
+
if (existing) {
|
|
3356
|
+
d.run("UPDATE machines SET hostname = ?, ssh_address = ?, last_seen_at = ? WHERE id = ?", [opts.hostname ?? existing.hostname, opts.ssh_address ?? existing.ssh_address, now(), existing.id]);
|
|
3357
|
+
if (opts.primary) {
|
|
3358
|
+
setPrimaryMachine(name, d);
|
|
3359
|
+
}
|
|
3360
|
+
return getMachine(existing.id, d);
|
|
3361
|
+
}
|
|
3362
|
+
const id = uuid();
|
|
3363
|
+
const ts = now();
|
|
3364
|
+
const host = opts.hostname || osHostname();
|
|
3365
|
+
const plat = osPlatform();
|
|
3366
|
+
d.run("INSERT INTO machines (id, name, hostname, platform, ssh_address, last_seen_at, is_primary, metadata, created_at) VALUES (?, ?, ?, ?, ?, ?, 0, '{}', ?)", [id, name, host, plat, opts.ssh_address ?? null, ts, ts]);
|
|
3367
|
+
if (opts.primary) {
|
|
3368
|
+
setPrimaryMachine(name, d);
|
|
3369
|
+
}
|
|
3370
|
+
return getMachine(id, d);
|
|
3371
|
+
}
|
|
3372
|
+
function setPrimaryMachine(name, db) {
|
|
3373
|
+
const d = db || getDatabase();
|
|
3374
|
+
const row = d.query("SELECT * FROM machines WHERE name = ?").get(name);
|
|
3375
|
+
if (!row)
|
|
3376
|
+
throw new Error(`Machine '${name}' not found`);
|
|
3377
|
+
if (row.archived_at)
|
|
3378
|
+
throw new Error(`Cannot set archived machine '${name}' as primary`);
|
|
3379
|
+
d.run("UPDATE machines SET is_primary = 0 WHERE archived_at IS NULL");
|
|
3380
|
+
d.run("UPDATE machines SET is_primary = 1 WHERE id = ?", [row.id]);
|
|
3381
|
+
return rowToMachine({ ...row, is_primary: 1 });
|
|
3382
|
+
}
|
|
3383
|
+
function getPrimaryMachine(db) {
|
|
3384
|
+
const d = db || getDatabase();
|
|
3385
|
+
const row = d.query("SELECT * FROM machines WHERE is_primary = 1").get();
|
|
3386
|
+
return row ? rowToMachine(row) : null;
|
|
3387
|
+
}
|
|
3388
|
+
function archiveMachine(id, db) {
|
|
3389
|
+
const d = db || getDatabase();
|
|
3390
|
+
const row = d.query("SELECT * FROM machines WHERE id = ?").get(id);
|
|
3391
|
+
if (!row)
|
|
3392
|
+
throw new Error(`Machine not found: ${id}`);
|
|
3393
|
+
if (row.is_primary)
|
|
3394
|
+
throw new Error("Cannot archive the primary machine");
|
|
3395
|
+
const activeCount = d.query("SELECT COUNT(*) as cnt FROM tasks WHERE machine_id = ? AND status IN ('pending', 'in_progress')").get(id);
|
|
3396
|
+
if (activeCount.cnt > 0) {
|
|
3397
|
+
throw new Error(`Cannot archive machine with ${activeCount.cnt} active/pending tasks`);
|
|
3398
|
+
}
|
|
3399
|
+
d.run("UPDATE machines SET archived_at = ? WHERE id = ?", [now(), id]);
|
|
3400
|
+
}
|
|
3401
|
+
function unarchiveMachine(id, db) {
|
|
3402
|
+
const d = db || getDatabase();
|
|
3403
|
+
d.run("UPDATE machines SET archived_at = NULL WHERE id = ?", [id]);
|
|
3404
|
+
return getMachine(id, d);
|
|
3405
|
+
}
|
|
3340
3406
|
function deleteMachine(id, db) {
|
|
3341
3407
|
const d = db || getDatabase();
|
|
3408
|
+
const row = d.query("SELECT * FROM machines WHERE id = ?").get(id);
|
|
3409
|
+
if (!row)
|
|
3410
|
+
return false;
|
|
3411
|
+
if (row.is_primary)
|
|
3412
|
+
throw new Error("Cannot delete the primary machine");
|
|
3413
|
+
const activeCount = d.query("SELECT COUNT(*) as cnt FROM tasks WHERE machine_id = ? AND status IN ('pending', 'in_progress')").get(id);
|
|
3414
|
+
if (activeCount.cnt > 0) {
|
|
3415
|
+
throw new Error(`Cannot delete machine with ${activeCount.cnt} active/pending tasks`);
|
|
3416
|
+
}
|
|
3342
3417
|
const result = d.run("DELETE FROM machines WHERE id = ?", [id]);
|
|
3343
3418
|
return result.changes > 0;
|
|
3344
3419
|
}
|
|
@@ -23061,6 +23136,38 @@ var init_cloud = __esm(() => {
|
|
|
23061
23136
|
]);
|
|
23062
23137
|
});
|
|
23063
23138
|
|
|
23139
|
+
// src/lib/logger.ts
|
|
23140
|
+
import { LogsClient } from "@hasna/logs";
|
|
23141
|
+
function getLogger() {
|
|
23142
|
+
if (client)
|
|
23143
|
+
return client;
|
|
23144
|
+
const url = process.env.LOGS_URL;
|
|
23145
|
+
const projectId = process.env.LOGS_PROJECT_ID;
|
|
23146
|
+
const apiKey = process.env.LOGS_API_KEY;
|
|
23147
|
+
if (!projectId)
|
|
23148
|
+
return null;
|
|
23149
|
+
client = new LogsClient({ url, projectId, apiKey });
|
|
23150
|
+
return client;
|
|
23151
|
+
}
|
|
23152
|
+
async function logError(message, opts) {
|
|
23153
|
+
const logger = getLogger();
|
|
23154
|
+
if (!logger)
|
|
23155
|
+
return;
|
|
23156
|
+
try {
|
|
23157
|
+
await logger.push({
|
|
23158
|
+
level: "error",
|
|
23159
|
+
message,
|
|
23160
|
+
source: "sdk",
|
|
23161
|
+
service: opts?.service,
|
|
23162
|
+
stack_trace: opts?.stack,
|
|
23163
|
+
trace_id: opts?.traceId,
|
|
23164
|
+
metadata: opts?.metadata
|
|
23165
|
+
});
|
|
23166
|
+
} catch {}
|
|
23167
|
+
}
|
|
23168
|
+
var client = null;
|
|
23169
|
+
var init_logger = () => {};
|
|
23170
|
+
|
|
23064
23171
|
// src/mcp/tools/dispatch.ts
|
|
23065
23172
|
function registerDispatchTools(server, { shouldRegisterTool, resolveId, formatError }) {
|
|
23066
23173
|
if (shouldRegisterTool("dispatch_tasks")) {
|
|
@@ -26858,6 +26965,152 @@ var init_code_tools = __esm(() => {
|
|
|
26858
26965
|
init_zod();
|
|
26859
26966
|
});
|
|
26860
26967
|
|
|
26968
|
+
// src/mcp/tools/machines.ts
|
|
26969
|
+
import { hostname as osHostname2 } from "os";
|
|
26970
|
+
function getDb() {
|
|
26971
|
+
return getDatabase();
|
|
26972
|
+
}
|
|
26973
|
+
function registerMachineTools(server, ctx) {
|
|
26974
|
+
if (ctx.shouldRegisterTool("machines_register")) {
|
|
26975
|
+
server.tool("machines_register", "Register a machine in the todos machine registry", {
|
|
26976
|
+
name: exports_external.string().optional().describe("Machine name (defaults to hostname if omitted)"),
|
|
26977
|
+
hostname: exports_external.string().optional().describe("OS hostname"),
|
|
26978
|
+
ssh_address: exports_external.string().optional().describe("SSH address for cross-machine access (e.g. user@host)"),
|
|
26979
|
+
primary: exports_external.boolean().optional().describe("Set as primary machine")
|
|
26980
|
+
}, async (params) => {
|
|
26981
|
+
try {
|
|
26982
|
+
const db = getDb();
|
|
26983
|
+
const name = params.name || osHostname2();
|
|
26984
|
+
const machine = registerMachine(name, {
|
|
26985
|
+
hostname: params.hostname,
|
|
26986
|
+
ssh_address: params.ssh_address,
|
|
26987
|
+
primary: params.primary
|
|
26988
|
+
}, db);
|
|
26989
|
+
return {
|
|
26990
|
+
content: [
|
|
26991
|
+
{
|
|
26992
|
+
type: "text",
|
|
26993
|
+
text: `Machine registered: ${machine.name} (${machine.id.slice(0, 8)})
|
|
26994
|
+
Host: ${machine.hostname}
|
|
26995
|
+
Platform: ${machine.platform}
|
|
26996
|
+
Primary: ${machine.is_primary}
|
|
26997
|
+
SSH: ${machine.ssh_address ?? "(not set)"}`
|
|
26998
|
+
}
|
|
26999
|
+
]
|
|
27000
|
+
};
|
|
27001
|
+
} catch (error) {
|
|
27002
|
+
return { content: [{ type: "text", text: ctx.formatError(error) }] };
|
|
27003
|
+
}
|
|
27004
|
+
});
|
|
27005
|
+
}
|
|
27006
|
+
if (ctx.shouldRegisterTool("machines_list")) {
|
|
27007
|
+
server.tool("machines_list", "List all registered machines", {
|
|
27008
|
+
include_archived: exports_external.boolean().optional().describe("Include archived machines")
|
|
27009
|
+
}, async (params) => {
|
|
27010
|
+
try {
|
|
27011
|
+
const db = getDb();
|
|
27012
|
+
const machines = listMachines(db, params.include_archived);
|
|
27013
|
+
if (machines.length === 0) {
|
|
27014
|
+
return {
|
|
27015
|
+
content: [{ type: "text", text: "No machines registered. Use machines_register to add one." }]
|
|
27016
|
+
};
|
|
27017
|
+
}
|
|
27018
|
+
const lines = machines.map((m) => {
|
|
27019
|
+
const primaryTag = m.is_primary ? " [PRIMARY]" : "";
|
|
27020
|
+
const archivedTag = m.archived_at ? " [ARCHIVED]" : "";
|
|
27021
|
+
return `${m.name} (${m.id.slice(0, 8)}) | ${m.hostname ?? "unknown"} | ${m.platform ?? "unknown"} | last: ${m.last_seen_at}${primaryTag}${archivedTag}`;
|
|
27022
|
+
});
|
|
27023
|
+
return { content: [{ type: "text", text: `Machines:
|
|
27024
|
+
${lines.join(`
|
|
27025
|
+
`)}` }] };
|
|
27026
|
+
} catch (error) {
|
|
27027
|
+
return { content: [{ type: "text", text: ctx.formatError(error) }] };
|
|
27028
|
+
}
|
|
27029
|
+
});
|
|
27030
|
+
}
|
|
27031
|
+
if (ctx.shouldRegisterTool("machines_set_primary")) {
|
|
27032
|
+
server.tool("machines_set_primary", "Set the primary machine", {
|
|
27033
|
+
name: exports_external.string().describe("Machine name to set as primary")
|
|
27034
|
+
}, async (params) => {
|
|
27035
|
+
try {
|
|
27036
|
+
const db = getDb();
|
|
27037
|
+
const machine = setPrimaryMachine(params.name, db);
|
|
27038
|
+
return {
|
|
27039
|
+
content: [
|
|
27040
|
+
{ type: "text", text: `Primary machine set to: ${machine.name} (${machine.id.slice(0, 8)})` }
|
|
27041
|
+
]
|
|
27042
|
+
};
|
|
27043
|
+
} catch (error) {
|
|
27044
|
+
return { content: [{ type: "text", text: ctx.formatError(error) }] };
|
|
27045
|
+
}
|
|
27046
|
+
});
|
|
27047
|
+
}
|
|
27048
|
+
if (ctx.shouldRegisterTool("machines_archive")) {
|
|
27049
|
+
server.tool("machines_archive", "Archive (soft-delete) a machine. Cannot archive primary or machines with active tasks.", {
|
|
27050
|
+
name: exports_external.string().describe("Machine name to archive")
|
|
27051
|
+
}, async (params) => {
|
|
27052
|
+
try {
|
|
27053
|
+
const db = getDb();
|
|
27054
|
+
const machine = getMachineByName(params.name, db);
|
|
27055
|
+
if (!machine) {
|
|
27056
|
+
return { content: [{ type: "text", text: `Machine '${params.name}' not found` }] };
|
|
27057
|
+
}
|
|
27058
|
+
archiveMachine(machine.id, db);
|
|
27059
|
+
return { content: [{ type: "text", text: `Machine '${params.name}' archived` }] };
|
|
27060
|
+
} catch (error) {
|
|
27061
|
+
return { content: [{ type: "text", text: ctx.formatError(error) }] };
|
|
27062
|
+
}
|
|
27063
|
+
});
|
|
27064
|
+
}
|
|
27065
|
+
if (ctx.shouldRegisterTool("machines_unarchive")) {
|
|
27066
|
+
server.tool("machines_unarchive", "Unarchive a machine", {
|
|
27067
|
+
name: exports_external.string().describe("Machine name to unarchive")
|
|
27068
|
+
}, async (params) => {
|
|
27069
|
+
try {
|
|
27070
|
+
const db = getDb();
|
|
27071
|
+
const machine = getMachineByName(params.name, db);
|
|
27072
|
+
if (!machine) {
|
|
27073
|
+
return { content: [{ type: "text", text: `Machine '${params.name}' not found` }] };
|
|
27074
|
+
}
|
|
27075
|
+
const result = unarchiveMachine(machine.id, db);
|
|
27076
|
+
return {
|
|
27077
|
+
content: [
|
|
27078
|
+
{ type: "text", text: `Machine '${params.name}' unarchived${result ? ` (${result.id.slice(0, 8)})` : ""}` }
|
|
27079
|
+
]
|
|
27080
|
+
};
|
|
27081
|
+
} catch (error) {
|
|
27082
|
+
return { content: [{ type: "text", text: ctx.formatError(error) }] };
|
|
27083
|
+
}
|
|
27084
|
+
});
|
|
27085
|
+
}
|
|
27086
|
+
if (ctx.shouldRegisterTool("machines_delete")) {
|
|
27087
|
+
server.tool("machines_delete", "Hard-delete a machine. Cannot delete primary or machines with active tasks.", {
|
|
27088
|
+
name: exports_external.string().describe("Machine name to delete")
|
|
27089
|
+
}, async (params) => {
|
|
27090
|
+
try {
|
|
27091
|
+
const db = getDb();
|
|
27092
|
+
const machine = getMachineByName(params.name, db);
|
|
27093
|
+
if (!machine) {
|
|
27094
|
+
return { content: [{ type: "text", text: `Machine '${params.name}' not found` }] };
|
|
27095
|
+
}
|
|
27096
|
+
const result = deleteMachine(machine.id, db);
|
|
27097
|
+
return {
|
|
27098
|
+
content: [
|
|
27099
|
+
{ type: "text", text: result ? `Machine '${params.name}' deleted` : `Failed to delete machine '${params.name}'` }
|
|
27100
|
+
]
|
|
27101
|
+
};
|
|
27102
|
+
} catch (error) {
|
|
27103
|
+
return { content: [{ type: "text", text: ctx.formatError(error) }] };
|
|
27104
|
+
}
|
|
27105
|
+
});
|
|
27106
|
+
}
|
|
27107
|
+
}
|
|
27108
|
+
var init_machines2 = __esm(() => {
|
|
27109
|
+
init_zod();
|
|
27110
|
+
init_machines();
|
|
27111
|
+
init_database();
|
|
27112
|
+
});
|
|
27113
|
+
|
|
26861
27114
|
// src/mcp/index.ts
|
|
26862
27115
|
var exports_mcp = {};
|
|
26863
27116
|
__export(exports_mcp, {
|
|
@@ -26958,6 +27211,7 @@ function formatError(error) {
|
|
|
26958
27211
|
return JSON.stringify({ code: "REFERENCE_ERROR", message: "Referenced record does not exist. Check that the ID is correct.", suggestion: "Verify the referenced ID exists before creating this record." });
|
|
26959
27212
|
}
|
|
26960
27213
|
console.error("[mcp] Unhandled error:", msg);
|
|
27214
|
+
logError(`[mcp] Unhandled error: ${msg}`, { service: "mcp" }).catch(() => {});
|
|
26961
27215
|
return JSON.stringify({ code: "UNKNOWN_ERROR", message: "An unexpected error occurred. Check server logs for details." });
|
|
26962
27216
|
}
|
|
26963
27217
|
return JSON.stringify({ code: "UNKNOWN_ERROR", message: "An unexpected error occurred." });
|
|
@@ -27023,6 +27277,7 @@ var init_mcp = __esm(() => {
|
|
|
27023
27277
|
init_cloud();
|
|
27024
27278
|
init_agents();
|
|
27025
27279
|
init_database();
|
|
27280
|
+
init_logger();
|
|
27026
27281
|
init_types();
|
|
27027
27282
|
init_dispatch2();
|
|
27028
27283
|
init_task_crud2();
|
|
@@ -27034,6 +27289,7 @@ var init_mcp = __esm(() => {
|
|
|
27034
27289
|
init_task_resources();
|
|
27035
27290
|
init_task_rel_tools();
|
|
27036
27291
|
init_code_tools();
|
|
27292
|
+
init_machines2();
|
|
27037
27293
|
server = new McpServer({
|
|
27038
27294
|
name: "todos",
|
|
27039
27295
|
version: getMcpVersion()
|
|
@@ -27091,10 +27347,15 @@ var init_mcp = __esm(() => {
|
|
|
27091
27347
|
registerTaskResources(server, toolContext);
|
|
27092
27348
|
registerTaskRelTools(server, toolContext);
|
|
27093
27349
|
registerCodeTools(server, toolContext);
|
|
27350
|
+
registerMachineTools(server, { shouldRegisterTool, formatError });
|
|
27094
27351
|
registerDispatchTools(server, { shouldRegisterTool, resolveId, formatError });
|
|
27095
27352
|
registerCloudSyncTools(server, { shouldRegisterTool, formatError });
|
|
27096
|
-
main().catch((err) => {
|
|
27353
|
+
main().catch(async (err) => {
|
|
27097
27354
|
console.error("MCP server error:", err);
|
|
27355
|
+
await logError(`MCP server startup failed: ${err instanceof Error ? err.message : String(err)}`, {
|
|
27356
|
+
service: "mcp",
|
|
27357
|
+
stack: err instanceof Error ? err.stack : undefined
|
|
27358
|
+
}).catch(() => {});
|
|
27098
27359
|
process.exit(1);
|
|
27099
27360
|
});
|
|
27100
27361
|
});
|
|
@@ -27223,17 +27484,17 @@ function handleSseEvents(_req, url, ctx) {
|
|
|
27223
27484
|
const agentId = url.searchParams.get("agent_id") || undefined;
|
|
27224
27485
|
const projectId = url.searchParams.get("project_id") || undefined;
|
|
27225
27486
|
if (agentId || projectId) {
|
|
27226
|
-
const
|
|
27487
|
+
const client2 = { controller: null, agentId, projectId, events: undefined };
|
|
27227
27488
|
const stream2 = new ReadableStream({
|
|
27228
27489
|
start(controller) {
|
|
27229
|
-
|
|
27230
|
-
ctx.filteredSseClients.add(
|
|
27490
|
+
client2.controller = controller;
|
|
27491
|
+
ctx.filteredSseClients.add(client2);
|
|
27231
27492
|
controller.enqueue(`data: ${JSON.stringify({ type: "connected", agent_id: agentId, project_id: projectId, timestamp: new Date().toISOString() })}
|
|
27232
27493
|
|
|
27233
27494
|
`);
|
|
27234
27495
|
},
|
|
27235
27496
|
cancel() {
|
|
27236
|
-
ctx.filteredSseClients.delete(
|
|
27497
|
+
ctx.filteredSseClients.delete(client2);
|
|
27237
27498
|
}
|
|
27238
27499
|
});
|
|
27239
27500
|
return new Response(stream2, {
|
|
@@ -27271,11 +27532,11 @@ function handleTasksStream(_req, url, ctx) {
|
|
|
27271
27532
|
const projectId = url.searchParams.get("project_id") || undefined;
|
|
27272
27533
|
const eventsParam = url.searchParams.get("events");
|
|
27273
27534
|
const eventFilter = eventsParam ? new Set(eventsParam.split(",").map((e) => e.trim())) : undefined;
|
|
27274
|
-
const
|
|
27535
|
+
const client2 = { controller: null, agentId, projectId, events: eventFilter };
|
|
27275
27536
|
const stream = new ReadableStream({
|
|
27276
27537
|
start(controller) {
|
|
27277
|
-
|
|
27278
|
-
ctx.filteredSseClients.add(
|
|
27538
|
+
client2.controller = controller;
|
|
27539
|
+
ctx.filteredSseClients.add(client2);
|
|
27279
27540
|
controller.enqueue(`: connected
|
|
27280
27541
|
|
|
27281
27542
|
data: ${JSON.stringify({ type: "connected", agent_id: agentId, timestamp: new Date().toISOString() })}
|
|
@@ -27283,7 +27544,7 @@ data: ${JSON.stringify({ type: "connected", agent_id: agentId, timestamp: new Da
|
|
|
27283
27544
|
`);
|
|
27284
27545
|
},
|
|
27285
27546
|
cancel() {
|
|
27286
|
-
ctx.filteredSseClients.delete(
|
|
27547
|
+
ctx.filteredSseClients.delete(client2);
|
|
27287
27548
|
}
|
|
27288
27549
|
});
|
|
27289
27550
|
return new Response(stream, {
|
|
@@ -28041,24 +28302,24 @@ async function startServer(port, options) {
|
|
|
28041
28302
|
for (const controller of deadClients)
|
|
28042
28303
|
sseClients.delete(controller);
|
|
28043
28304
|
const deadFiltered = [];
|
|
28044
|
-
for (const
|
|
28045
|
-
if (
|
|
28305
|
+
for (const client2 of filteredSseClients) {
|
|
28306
|
+
if (client2.events && !client2.events.has(eventName) && !client2.events.has("*"))
|
|
28046
28307
|
continue;
|
|
28047
|
-
if (
|
|
28308
|
+
if (client2.agentId && event.agent_id !== client2.agentId)
|
|
28048
28309
|
continue;
|
|
28049
|
-
if (
|
|
28310
|
+
if (client2.projectId && event.project_id !== client2.projectId)
|
|
28050
28311
|
continue;
|
|
28051
28312
|
try {
|
|
28052
|
-
|
|
28313
|
+
client2.controller.enqueue(`event: ${eventName}
|
|
28053
28314
|
data: ${data}
|
|
28054
28315
|
|
|
28055
28316
|
`);
|
|
28056
28317
|
} catch {
|
|
28057
|
-
deadFiltered.push(
|
|
28318
|
+
deadFiltered.push(client2);
|
|
28058
28319
|
}
|
|
28059
28320
|
}
|
|
28060
|
-
for (const
|
|
28061
|
-
filteredSseClients.delete(
|
|
28321
|
+
for (const client2 of deadFiltered)
|
|
28322
|
+
filteredSseClients.delete(client2);
|
|
28062
28323
|
}
|
|
28063
28324
|
const dashboardDir = resolveDashboardDir();
|
|
28064
28325
|
const dashboardExists = existsSync9(dashboardDir);
|
|
@@ -29952,7 +30213,7 @@ init_task_lists();
|
|
|
29952
30213
|
init_plans();
|
|
29953
30214
|
init_comments();
|
|
29954
30215
|
init_search();
|
|
29955
|
-
import
|
|
30216
|
+
import chalk4 from "chalk";
|
|
29956
30217
|
import { execSync } from "child_process";
|
|
29957
30218
|
import { existsSync as existsSync10, mkdirSync as mkdirSync6, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "fs";
|
|
29958
30219
|
import { basename, dirname as dirname5, join as join13, resolve as resolve4 } from "path";
|
|
@@ -30627,6 +30888,136 @@ function registerDispatchCommands(program2) {
|
|
|
30627
30888
|
});
|
|
30628
30889
|
}
|
|
30629
30890
|
|
|
30891
|
+
// src/cli/commands/machines.tsx
|
|
30892
|
+
init_machines();
|
|
30893
|
+
init_database();
|
|
30894
|
+
import chalk2 from "chalk";
|
|
30895
|
+
function registerMachineCommands(program2) {
|
|
30896
|
+
program2.command("machines").description("List registered machines").option("-a, --all", "Include archived machines").action((opts) => {
|
|
30897
|
+
const db = getDatabase();
|
|
30898
|
+
const machines = listMachines(db, opts.all);
|
|
30899
|
+
if (machines.length === 0) {
|
|
30900
|
+
console.log(chalk2.yellow("No machines registered."));
|
|
30901
|
+
console.log(chalk2.dim("Use `todos machines register` to add one."));
|
|
30902
|
+
return;
|
|
30903
|
+
}
|
|
30904
|
+
for (const m of machines) {
|
|
30905
|
+
const primaryTag = m.is_primary ? chalk2.bold(" [PRIMARY]") : "";
|
|
30906
|
+
const archivedTag = m.archived_at ? chalk2.dim(" [ARCHIVED]") : "";
|
|
30907
|
+
console.log(`${chalk2.cyan(m.name)} (${m.id.slice(0, 8)})${primaryTag}${archivedTag}`);
|
|
30908
|
+
console.log(chalk2.dim(` Host: ${m.hostname ?? "unknown"} | Platform: ${m.platform ?? "unknown"}`));
|
|
30909
|
+
console.log(chalk2.dim(` SSH: ${m.ssh_address ?? "(not set)"}`));
|
|
30910
|
+
console.log(chalk2.dim(` Last seen: ${m.last_seen_at}`));
|
|
30911
|
+
}
|
|
30912
|
+
});
|
|
30913
|
+
program2.command("machines register").description("Register a machine").argument("<name>", "Machine name").option("--hostname <host>", "OS hostname").option("--ssh <address>", "SSH address (e.g. user@host)").option("--arch <arch>", "Architecture (e.g. linux-arm64)").option("--primary", "Set as primary machine").action((name, opts) => {
|
|
30914
|
+
try {
|
|
30915
|
+
const db = getDatabase();
|
|
30916
|
+
const machine = registerMachine(name, {
|
|
30917
|
+
hostname: opts.hostname,
|
|
30918
|
+
ssh_address: opts.ssh,
|
|
30919
|
+
primary: opts.primary
|
|
30920
|
+
}, db);
|
|
30921
|
+
console.log(chalk2.green(`Machine registered: ${machine.name} (${machine.id.slice(0, 8)})`));
|
|
30922
|
+
console.log(chalk2.dim(` Host: ${machine.hostname} | Platform: ${machine.platform}`));
|
|
30923
|
+
console.log(chalk2.dim(` Primary: ${machine.is_primary}`));
|
|
30924
|
+
if (machine.ssh_address)
|
|
30925
|
+
console.log(chalk2.dim(` SSH: ${machine.ssh_address}`));
|
|
30926
|
+
} catch (err) {
|
|
30927
|
+
console.error(chalk2.red(err instanceof Error ? err.message : String(err)));
|
|
30928
|
+
process.exit(1);
|
|
30929
|
+
}
|
|
30930
|
+
});
|
|
30931
|
+
program2.command("machines set-primary").description("Set the primary machine").argument("<name>", "Machine name").action((name) => {
|
|
30932
|
+
try {
|
|
30933
|
+
const db = getDatabase();
|
|
30934
|
+
const machine = setPrimaryMachine(name, db);
|
|
30935
|
+
console.log(chalk2.green(`Primary machine set to: ${machine.name} (${machine.id.slice(0, 8)})`));
|
|
30936
|
+
} catch (err) {
|
|
30937
|
+
console.error(chalk2.red(err instanceof Error ? err.message : String(err)));
|
|
30938
|
+
process.exit(1);
|
|
30939
|
+
}
|
|
30940
|
+
});
|
|
30941
|
+
program2.command("machines archive").description("Archive a machine (soft-delete)").argument("<name>", "Machine name").action((name) => {
|
|
30942
|
+
try {
|
|
30943
|
+
const db = getDatabase();
|
|
30944
|
+
const machine = getMachineByName(name, db);
|
|
30945
|
+
if (!machine) {
|
|
30946
|
+
console.error(chalk2.red(`Machine '${name}' not found`));
|
|
30947
|
+
process.exit(1);
|
|
30948
|
+
}
|
|
30949
|
+
archiveMachine(machine.id, db);
|
|
30950
|
+
console.log(chalk2.green(`Machine '${name}' archived`));
|
|
30951
|
+
} catch (err) {
|
|
30952
|
+
console.error(chalk2.red(err instanceof Error ? err.message : String(err)));
|
|
30953
|
+
process.exit(1);
|
|
30954
|
+
}
|
|
30955
|
+
});
|
|
30956
|
+
program2.command("machines unarchive").description("Unarchive a machine").argument("<name>", "Machine name").action((name) => {
|
|
30957
|
+
try {
|
|
30958
|
+
const db = getDatabase();
|
|
30959
|
+
const machine = getMachineByName(name, db);
|
|
30960
|
+
if (!machine) {
|
|
30961
|
+
console.error(chalk2.red(`Machine '${name}' not found`));
|
|
30962
|
+
process.exit(1);
|
|
30963
|
+
}
|
|
30964
|
+
unarchiveMachine(machine.id, db);
|
|
30965
|
+
console.log(chalk2.green(`Machine '${name}' unarchived`));
|
|
30966
|
+
} catch (err) {
|
|
30967
|
+
console.error(chalk2.red(err instanceof Error ? err.message : String(err)));
|
|
30968
|
+
process.exit(1);
|
|
30969
|
+
}
|
|
30970
|
+
});
|
|
30971
|
+
program2.command("machines delete").description("Delete a machine (hard delete)").argument("<name>", "Machine name").action((name) => {
|
|
30972
|
+
try {
|
|
30973
|
+
const db = getDatabase();
|
|
30974
|
+
const machine = getMachineByName(name, db);
|
|
30975
|
+
if (!machine) {
|
|
30976
|
+
console.error(chalk2.red(`Machine '${name}' not found`));
|
|
30977
|
+
process.exit(1);
|
|
30978
|
+
}
|
|
30979
|
+
deleteMachine(machine.id, db);
|
|
30980
|
+
console.log(chalk2.green(`Machine '${name}' deleted`));
|
|
30981
|
+
} catch (err) {
|
|
30982
|
+
console.error(chalk2.red(err instanceof Error ? err.message : String(err)));
|
|
30983
|
+
process.exit(1);
|
|
30984
|
+
}
|
|
30985
|
+
});
|
|
30986
|
+
program2.command("machines status").description("Show machine health status").action(() => {
|
|
30987
|
+
const db = getDatabase();
|
|
30988
|
+
const machines = listMachines(db);
|
|
30989
|
+
const primary = getPrimaryMachine(db);
|
|
30990
|
+
if (machines.length === 0) {
|
|
30991
|
+
console.log(chalk2.yellow("No machines registered."));
|
|
30992
|
+
return;
|
|
30993
|
+
}
|
|
30994
|
+
console.log(chalk2.bold(`
|
|
30995
|
+
Machine Health`));
|
|
30996
|
+
console.log(chalk2.dim("\u2500".repeat(60)));
|
|
30997
|
+
for (const m of machines) {
|
|
30998
|
+
const primaryTag = m.is_primary ? chalk2.bold(" [PRIMARY]") : "";
|
|
30999
|
+
const lastSeen = new Date(m.last_seen_at);
|
|
31000
|
+
const now2 = new Date;
|
|
31001
|
+
const diffMs = now2.getTime() - lastSeen.getTime();
|
|
31002
|
+
const diffMin = Math.round(diffMs / 60000);
|
|
31003
|
+
let status;
|
|
31004
|
+
if (diffMin < 5) {
|
|
31005
|
+
status = chalk2.green("online");
|
|
31006
|
+
} else if (diffMin < 60) {
|
|
31007
|
+
status = chalk2.yellow("stale");
|
|
31008
|
+
} else {
|
|
31009
|
+
status = chalk2.red("offline");
|
|
31010
|
+
}
|
|
31011
|
+
console.log(`${chalk2.cyan(m.name)}${primaryTag} ${status} last: ${diffMin}m ago`);
|
|
31012
|
+
}
|
|
31013
|
+
if (!primary) {
|
|
31014
|
+
console.log(chalk2.yellow(`
|
|
31015
|
+
Warning: No primary machine set.`));
|
|
31016
|
+
console.log(chalk2.dim("Use `todos machines set-primary <name>` to set one."));
|
|
31017
|
+
}
|
|
31018
|
+
});
|
|
31019
|
+
}
|
|
31020
|
+
|
|
30630
31021
|
// src/cli/brains.ts
|
|
30631
31022
|
import {
|
|
30632
31023
|
existsSync as existsSync7,
|
|
@@ -30636,7 +31027,7 @@ import {
|
|
|
30636
31027
|
} from "fs";
|
|
30637
31028
|
import { homedir as homedir2 } from "os";
|
|
30638
31029
|
import { join as join7 } from "path";
|
|
30639
|
-
import
|
|
31030
|
+
import chalk3 from "chalk";
|
|
30640
31031
|
|
|
30641
31032
|
// src/lib/gatherer.ts
|
|
30642
31033
|
init_tasks();
|
|
@@ -30776,13 +31167,13 @@ function getTodosGlobalDir4() {
|
|
|
30776
31167
|
return newDir;
|
|
30777
31168
|
}
|
|
30778
31169
|
function printSuccess(msg) {
|
|
30779
|
-
console.log(
|
|
31170
|
+
console.log(chalk3.green("\u2713 " + msg));
|
|
30780
31171
|
}
|
|
30781
31172
|
function printError(msg) {
|
|
30782
|
-
console.error(
|
|
31173
|
+
console.error(chalk3.red("\u2717 " + msg));
|
|
30783
31174
|
}
|
|
30784
31175
|
function printInfo(msg) {
|
|
30785
|
-
console.log(
|
|
31176
|
+
console.log(chalk3.cyan("\u2139 " + msg));
|
|
30786
31177
|
}
|
|
30787
31178
|
function makeBrainsCommand() {
|
|
30788
31179
|
const brains = new Command("brains");
|
|
@@ -30819,7 +31210,7 @@ function makeBrainsCommand() {
|
|
|
30819
31210
|
}));
|
|
30820
31211
|
} else {
|
|
30821
31212
|
printSuccess(`Gathered ${result.count} training examples from tasks`);
|
|
30822
|
-
console.log(
|
|
31213
|
+
console.log(chalk3.dim(` Output: ${outputPath}`));
|
|
30823
31214
|
}
|
|
30824
31215
|
} catch (err) {
|
|
30825
31216
|
printError(err instanceof Error ? err.message : String(err));
|
|
@@ -30873,9 +31264,9 @@ function makeBrainsCommand() {
|
|
|
30873
31264
|
console.log(JSON.stringify(jobResult));
|
|
30874
31265
|
} else {
|
|
30875
31266
|
printSuccess(`Fine-tuning job started: ${String(jobResult["jobId"] ?? "(unknown)")}`);
|
|
30876
|
-
console.log(
|
|
30877
|
-
console.log(
|
|
30878
|
-
console.log(
|
|
31267
|
+
console.log(chalk3.dim(` Provider: ${opts.provider}`));
|
|
31268
|
+
console.log(chalk3.dim(` Base model: ${opts.baseModel}`));
|
|
31269
|
+
console.log(chalk3.dim(` Name: ${modelName}`));
|
|
30879
31270
|
if (jobResult["jobId"]) {
|
|
30880
31271
|
console.log();
|
|
30881
31272
|
printInfo(`Use \`todos brains model set <model-id>\` once training completes.`);
|
|
@@ -30895,9 +31286,9 @@ function makeBrainsCommand() {
|
|
|
30895
31286
|
console.log(JSON.stringify({ activeModel: active, isDefault }));
|
|
30896
31287
|
} else {
|
|
30897
31288
|
if (isDefault) {
|
|
30898
|
-
console.log(`Active model: ${
|
|
31289
|
+
console.log(`Active model: ${chalk3.cyan(active)} ${chalk3.dim("(default)")}`);
|
|
30899
31290
|
} else {
|
|
30900
|
-
console.log(`Active model: ${
|
|
31291
|
+
console.log(`Active model: ${chalk3.green(active)}`);
|
|
30901
31292
|
}
|
|
30902
31293
|
}
|
|
30903
31294
|
} catch (err) {
|
|
@@ -30931,9 +31322,9 @@ function makeBrainsCommand() {
|
|
|
30931
31322
|
console.log(JSON.stringify({ activeModel: active, isDefault }));
|
|
30932
31323
|
} else {
|
|
30933
31324
|
if (isDefault) {
|
|
30934
|
-
console.log(`Active model: ${
|
|
31325
|
+
console.log(`Active model: ${chalk3.cyan(active)} ${chalk3.dim("(default)")}`);
|
|
30935
31326
|
} else {
|
|
30936
|
-
console.log(`Active model: ${
|
|
31327
|
+
console.log(`Active model: ${chalk3.green(active)}`);
|
|
30937
31328
|
}
|
|
30938
31329
|
}
|
|
30939
31330
|
} catch (err) {
|
|
@@ -30959,7 +31350,7 @@ function handleError(e) {
|
|
|
30959
31350
|
if (globalOpts.json) {
|
|
30960
31351
|
console.log(JSON.stringify({ error: e instanceof Error ? e.message : String(e) }));
|
|
30961
31352
|
} else {
|
|
30962
|
-
console.error(
|
|
31353
|
+
console.error(chalk4.red(e instanceof Error ? e.message : String(e)));
|
|
30963
31354
|
}
|
|
30964
31355
|
process.exit(1);
|
|
30965
31356
|
}
|
|
@@ -30969,10 +31360,10 @@ function resolveTaskId(partialId) {
|
|
|
30969
31360
|
if (!id) {
|
|
30970
31361
|
const similar = db.query("SELECT id FROM tasks WHERE id LIKE ? LIMIT 3").all(`%${partialId}%`);
|
|
30971
31362
|
if (similar.length > 0) {
|
|
30972
|
-
console.error(
|
|
30973
|
-
console.error(
|
|
31363
|
+
console.error(chalk4.red(`Could not resolve task ID: ${partialId}`));
|
|
31364
|
+
console.error(chalk4.dim(`Did you mean: ${similar.map((s) => s.id.slice(0, 8)).join(", ")}?`));
|
|
30974
31365
|
} else {
|
|
30975
|
-
console.error(
|
|
31366
|
+
console.error(chalk4.red(`Could not resolve task ID: ${partialId}`));
|
|
30976
31367
|
}
|
|
30977
31368
|
process.exit(1);
|
|
30978
31369
|
}
|
|
@@ -31029,26 +31420,26 @@ function output(data, jsonMode) {
|
|
|
31029
31420
|
}
|
|
31030
31421
|
}
|
|
31031
31422
|
var statusColors4 = {
|
|
31032
|
-
pending:
|
|
31033
|
-
in_progress:
|
|
31034
|
-
completed:
|
|
31035
|
-
failed:
|
|
31036
|
-
cancelled:
|
|
31423
|
+
pending: chalk4.yellow,
|
|
31424
|
+
in_progress: chalk4.blue,
|
|
31425
|
+
completed: chalk4.green,
|
|
31426
|
+
failed: chalk4.red,
|
|
31427
|
+
cancelled: chalk4.gray
|
|
31037
31428
|
};
|
|
31038
31429
|
var priorityColors2 = {
|
|
31039
|
-
critical:
|
|
31040
|
-
high:
|
|
31041
|
-
medium:
|
|
31042
|
-
low:
|
|
31430
|
+
critical: chalk4.red.bold,
|
|
31431
|
+
high: chalk4.red,
|
|
31432
|
+
medium: chalk4.yellow,
|
|
31433
|
+
low: chalk4.gray
|
|
31043
31434
|
};
|
|
31044
31435
|
function formatTaskLine(t) {
|
|
31045
|
-
const statusFn = statusColors4[t.status] ||
|
|
31046
|
-
const priorityFn = priorityColors2[t.priority] ||
|
|
31047
|
-
const lock = t.locked_by ?
|
|
31048
|
-
const assigned = t.assigned_to ?
|
|
31049
|
-
const tags = t.tags.length > 0 ?
|
|
31050
|
-
const plan = t.plan_id ?
|
|
31051
|
-
return `${
|
|
31436
|
+
const statusFn = statusColors4[t.status] || chalk4.white;
|
|
31437
|
+
const priorityFn = priorityColors2[t.priority] || chalk4.white;
|
|
31438
|
+
const lock = t.locked_by ? chalk4.magenta(` [locked:${t.locked_by}]`) : "";
|
|
31439
|
+
const assigned = t.assigned_to ? chalk4.cyan(` -> ${t.assigned_to}`) : "";
|
|
31440
|
+
const tags = t.tags.length > 0 ? chalk4.dim(` [${t.tags.join(",")}]`) : "";
|
|
31441
|
+
const plan = t.plan_id ? chalk4.magenta(` [plan:${t.plan_id.slice(0, 8)}]`) : "";
|
|
31442
|
+
return `${chalk4.dim(t.id.slice(0, 8))} ${statusFn(t.status.padEnd(11))} ${priorityFn(t.priority.padEnd(8))} ${t.title}${assigned}${lock}${tags}${plan}`;
|
|
31052
31443
|
}
|
|
31053
31444
|
program2.name("todos").description("Universal task management for AI coding agents").version(getPackageVersion()).option("--project <path>", "Project path").option("-j, --json", "Output as JSON").option("--agent <name>", "Agent name").option("--session <id>", "Session ID");
|
|
31054
31445
|
program2.command("add <title>").description("Create a new task").option("-d, --description <text>", "Task description").option("-p, --priority <level>", "Priority: low, medium, high, critical").option("--parent <id>", "Parent task ID").option("-t, --tags <tags>", "Comma-separated tags").option("--tag <tags>", "Comma-separated tags (alias for --tags)").option("--plan <id>", "Assign to a plan").option("--assign <agent>", "Assign to agent").option("--status <status>", "Initial status").option("--list <id>", "Task list ID").option("--task-list <id>", "Task list ID (alias for --list)").option("--estimated <minutes>", "Estimated time in minutes").option("--approval", "Require approval before completion").option("--recurrence <rule>", "Recurrence rule, e.g. 'every day', 'every weekday', 'every 2 weeks'").option("--due <date>", "Due date (ISO string or YYYY-MM-DD)").option("--reason <text>", "Why this task exists").action((title, opts) => {
|
|
@@ -31060,7 +31451,7 @@ program2.command("add <title>").description("Create a new task").option("-d, --d
|
|
|
31060
31451
|
const db = getDatabase();
|
|
31061
31452
|
const id = resolvePartialId(db, "task_lists", opts.list);
|
|
31062
31453
|
if (!id) {
|
|
31063
|
-
console.error(
|
|
31454
|
+
console.error(chalk4.red(`Could not resolve task list ID: ${opts.list}`));
|
|
31064
31455
|
process.exit(1);
|
|
31065
31456
|
}
|
|
31066
31457
|
return id;
|
|
@@ -31075,7 +31466,7 @@ program2.command("add <title>").description("Create a new task").option("-d, --d
|
|
|
31075
31466
|
const db = getDatabase();
|
|
31076
31467
|
const id = resolvePartialId(db, "plans", opts.plan);
|
|
31077
31468
|
if (!id) {
|
|
31078
|
-
console.error(
|
|
31469
|
+
console.error(chalk4.red(`Could not resolve plan ID: ${opts.plan}`));
|
|
31079
31470
|
process.exit(1);
|
|
31080
31471
|
}
|
|
31081
31472
|
return id;
|
|
@@ -31096,7 +31487,7 @@ program2.command("add <title>").description("Create a new task").option("-d, --d
|
|
|
31096
31487
|
if (globalOpts.json) {
|
|
31097
31488
|
output(task, true);
|
|
31098
31489
|
} else {
|
|
31099
|
-
console.log(
|
|
31490
|
+
console.log(chalk4.green("Task created:"));
|
|
31100
31491
|
console.log(formatTaskLine(task));
|
|
31101
31492
|
}
|
|
31102
31493
|
});
|
|
@@ -31109,12 +31500,12 @@ program2.command("list").description("List tasks").option("-s, --status <status>
|
|
|
31109
31500
|
const hasExplicitProjectFilter = Boolean(globalOpts.project || opts.projectName);
|
|
31110
31501
|
const allowedSortFields = new Set(["updated", "created", "priority", "status"]);
|
|
31111
31502
|
if (opts.sort && !allowedSortFields.has(opts.sort)) {
|
|
31112
|
-
console.error(
|
|
31503
|
+
console.error(chalk4.red(`Invalid --sort value: ${opts.sort}. Allowed values: updated, created, priority, status.`));
|
|
31113
31504
|
process.exit(1);
|
|
31114
31505
|
}
|
|
31115
31506
|
const allowedFormats = new Set(["table", "compact", "csv", "json"]);
|
|
31116
31507
|
if (opts.format && !allowedFormats.has(opts.format)) {
|
|
31117
|
-
console.error(
|
|
31508
|
+
console.error(chalk4.red(`Invalid --format value: ${opts.format}. Allowed values: table, compact, csv, json.`));
|
|
31118
31509
|
process.exit(1);
|
|
31119
31510
|
}
|
|
31120
31511
|
const filter = {};
|
|
@@ -31125,7 +31516,7 @@ program2.command("list").description("List tasks").option("-s, --status <status>
|
|
|
31125
31516
|
const db = getDatabase();
|
|
31126
31517
|
const listId = resolvePartialId(db, "task_lists", opts.list);
|
|
31127
31518
|
if (!listId) {
|
|
31128
|
-
console.error(
|
|
31519
|
+
console.error(chalk4.red(`Could not resolve task list ID: ${opts.list}`));
|
|
31129
31520
|
process.exit(1);
|
|
31130
31521
|
}
|
|
31131
31522
|
filter["task_list_id"] = listId;
|
|
@@ -31147,7 +31538,7 @@ program2.command("list").description("List tasks").option("-s, --status <status>
|
|
|
31147
31538
|
if (match) {
|
|
31148
31539
|
filter["project_id"] = match.id;
|
|
31149
31540
|
} else {
|
|
31150
|
-
console.error(
|
|
31541
|
+
console.error(chalk4.red(`No project matching: ${opts.projectName}`));
|
|
31151
31542
|
process.exit(1);
|
|
31152
31543
|
}
|
|
31153
31544
|
}
|
|
@@ -31159,7 +31550,7 @@ program2.command("list").description("List tasks").option("-s, --status <status>
|
|
|
31159
31550
|
if (opts.limit !== undefined) {
|
|
31160
31551
|
const parsedLimit = Number.parseInt(String(opts.limit), 10);
|
|
31161
31552
|
if (!Number.isInteger(parsedLimit) || parsedLimit <= 0) {
|
|
31162
|
-
console.error(
|
|
31553
|
+
console.error(chalk4.red(`Invalid --limit value: ${opts.limit}. Must be a positive integer.`));
|
|
31163
31554
|
process.exit(1);
|
|
31164
31555
|
}
|
|
31165
31556
|
filter["limit"] = parsedLimit;
|
|
@@ -31197,7 +31588,7 @@ program2.command("list").description("List tasks").option("-s, --status <status>
|
|
|
31197
31588
|
if (fmt === "compact" || fmt === "csv")
|
|
31198
31589
|
process.stdout.write("");
|
|
31199
31590
|
else
|
|
31200
|
-
console.log(
|
|
31591
|
+
console.log(chalk4.dim("No tasks found."));
|
|
31201
31592
|
return;
|
|
31202
31593
|
}
|
|
31203
31594
|
if (fmt === "csv") {
|
|
@@ -31224,7 +31615,7 @@ program2.command("list").description("List tasks").option("-s, --status <status>
|
|
|
31224
31615
|
}
|
|
31225
31616
|
return;
|
|
31226
31617
|
}
|
|
31227
|
-
console.log(
|
|
31618
|
+
console.log(chalk4.bold(`${tasks.length} task(s):
|
|
31228
31619
|
`));
|
|
31229
31620
|
for (const t of tasks) {
|
|
31230
31621
|
console.log(formatTaskLine(t));
|
|
@@ -31241,12 +31632,12 @@ program2.command("count").description("Show task count by status").action(() =>
|
|
|
31241
31632
|
output(counts, true);
|
|
31242
31633
|
} else {
|
|
31243
31634
|
const parts = [
|
|
31244
|
-
`total: ${
|
|
31245
|
-
`pending: ${
|
|
31246
|
-
`in_progress: ${
|
|
31247
|
-
`completed: ${
|
|
31248
|
-
`failed: ${
|
|
31249
|
-
`cancelled: ${
|
|
31635
|
+
`total: ${chalk4.bold(String(counts.total))}`,
|
|
31636
|
+
`pending: ${chalk4.yellow(String(counts["pending"] || 0))}`,
|
|
31637
|
+
`in_progress: ${chalk4.blue(String(counts["in_progress"] || 0))}`,
|
|
31638
|
+
`completed: ${chalk4.green(String(counts["completed"] || 0))}`,
|
|
31639
|
+
`failed: ${chalk4.red(String(counts["failed"] || 0))}`,
|
|
31640
|
+
`cancelled: ${chalk4.gray(String(counts["cancelled"] || 0))}`
|
|
31250
31641
|
];
|
|
31251
31642
|
console.log(parts.join(" "));
|
|
31252
31643
|
}
|
|
@@ -31256,83 +31647,83 @@ program2.command("show <id>").description("Show full task details").action((id)
|
|
|
31256
31647
|
const resolvedId = resolveTaskId(id);
|
|
31257
31648
|
const task = getTaskWithRelations(resolvedId);
|
|
31258
31649
|
if (!task) {
|
|
31259
|
-
console.error(
|
|
31650
|
+
console.error(chalk4.red(`Task not found: ${id}`));
|
|
31260
31651
|
process.exit(1);
|
|
31261
31652
|
}
|
|
31262
31653
|
if (globalOpts.json) {
|
|
31263
31654
|
output(task, true);
|
|
31264
31655
|
return;
|
|
31265
31656
|
}
|
|
31266
|
-
console.log(
|
|
31657
|
+
console.log(chalk4.bold(`Task Details:
|
|
31267
31658
|
`));
|
|
31268
|
-
console.log(` ${
|
|
31269
|
-
console.log(` ${
|
|
31270
|
-
console.log(` ${
|
|
31271
|
-
console.log(` ${
|
|
31659
|
+
console.log(` ${chalk4.dim("ID:")} ${task.id}`);
|
|
31660
|
+
console.log(` ${chalk4.dim("Title:")} ${task.title}`);
|
|
31661
|
+
console.log(` ${chalk4.dim("Status:")} ${(statusColors4[task.status] || chalk4.white)(task.status)}`);
|
|
31662
|
+
console.log(` ${chalk4.dim("Priority:")} ${(priorityColors2[task.priority] || chalk4.white)(task.priority)}`);
|
|
31272
31663
|
if (task.description)
|
|
31273
|
-
console.log(` ${
|
|
31664
|
+
console.log(` ${chalk4.dim("Desc:")} ${task.description}`);
|
|
31274
31665
|
if (task.assigned_to)
|
|
31275
|
-
console.log(` ${
|
|
31666
|
+
console.log(` ${chalk4.dim("Assigned:")} ${task.assigned_to}`);
|
|
31276
31667
|
if (task.agent_id)
|
|
31277
|
-
console.log(` ${
|
|
31668
|
+
console.log(` ${chalk4.dim("Agent:")} ${task.agent_id}`);
|
|
31278
31669
|
if (task.session_id)
|
|
31279
|
-
console.log(` ${
|
|
31670
|
+
console.log(` ${chalk4.dim("Session:")} ${task.session_id}`);
|
|
31280
31671
|
if (task.locked_by)
|
|
31281
|
-
console.log(` ${
|
|
31672
|
+
console.log(` ${chalk4.dim("Locked:")} ${task.locked_by} (at ${task.locked_at})`);
|
|
31282
31673
|
if (task.requires_approval) {
|
|
31283
|
-
const approvalStatus = task.approved_by ?
|
|
31284
|
-
console.log(` ${
|
|
31674
|
+
const approvalStatus = task.approved_by ? chalk4.green(`approved by ${task.approved_by}`) : chalk4.yellow("pending approval");
|
|
31675
|
+
console.log(` ${chalk4.dim("Approval:")} ${approvalStatus}`);
|
|
31285
31676
|
}
|
|
31286
31677
|
if (task.estimated_minutes)
|
|
31287
|
-
console.log(` ${
|
|
31678
|
+
console.log(` ${chalk4.dim("Estimate:")} ${task.estimated_minutes} minutes`);
|
|
31288
31679
|
if (task.project_id)
|
|
31289
|
-
console.log(` ${
|
|
31680
|
+
console.log(` ${chalk4.dim("Project:")} ${task.project_id}`);
|
|
31290
31681
|
if (task.plan_id)
|
|
31291
|
-
console.log(` ${
|
|
31682
|
+
console.log(` ${chalk4.dim("Plan:")} ${task.plan_id}`);
|
|
31292
31683
|
if (task.working_dir)
|
|
31293
|
-
console.log(` ${
|
|
31684
|
+
console.log(` ${chalk4.dim("WorkDir:")} ${task.working_dir}`);
|
|
31294
31685
|
if (task.parent)
|
|
31295
|
-
console.log(` ${
|
|
31686
|
+
console.log(` ${chalk4.dim("Parent:")} ${task.parent.id.slice(0, 8)} | ${task.parent.title}`);
|
|
31296
31687
|
if (task.tags.length > 0)
|
|
31297
|
-
console.log(` ${
|
|
31298
|
-
console.log(` ${
|
|
31299
|
-
console.log(` ${
|
|
31688
|
+
console.log(` ${chalk4.dim("Tags:")} ${task.tags.join(", ")}`);
|
|
31689
|
+
console.log(` ${chalk4.dim("Version:")} ${task.version}`);
|
|
31690
|
+
console.log(` ${chalk4.dim("Created:")} ${task.created_at}`);
|
|
31300
31691
|
if (task.started_at)
|
|
31301
|
-
console.log(` ${
|
|
31692
|
+
console.log(` ${chalk4.dim("Started:")} ${task.started_at}`);
|
|
31302
31693
|
if (task.completed_at) {
|
|
31303
|
-
console.log(` ${
|
|
31694
|
+
console.log(` ${chalk4.dim("Done:")} ${task.completed_at}`);
|
|
31304
31695
|
if (task.started_at) {
|
|
31305
31696
|
const dur = Math.round((new Date(task.completed_at).getTime() - new Date(task.started_at).getTime()) / 60000);
|
|
31306
|
-
console.log(` ${
|
|
31697
|
+
console.log(` ${chalk4.dim("Duration:")} ${dur}m`);
|
|
31307
31698
|
}
|
|
31308
31699
|
}
|
|
31309
31700
|
if (task.subtasks.length > 0) {
|
|
31310
|
-
console.log(
|
|
31701
|
+
console.log(chalk4.bold(`
|
|
31311
31702
|
Subtasks (${task.subtasks.length}):`));
|
|
31312
31703
|
for (const st of task.subtasks) {
|
|
31313
31704
|
console.log(` ${formatTaskLine(st)}`);
|
|
31314
31705
|
}
|
|
31315
31706
|
}
|
|
31316
31707
|
if (task.dependencies.length > 0) {
|
|
31317
|
-
console.log(
|
|
31708
|
+
console.log(chalk4.bold(`
|
|
31318
31709
|
Depends on (${task.dependencies.length}):`));
|
|
31319
31710
|
for (const dep of task.dependencies) {
|
|
31320
31711
|
console.log(` ${formatTaskLine(dep)}`);
|
|
31321
31712
|
}
|
|
31322
31713
|
}
|
|
31323
31714
|
if (task.blocked_by.length > 0) {
|
|
31324
|
-
console.log(
|
|
31715
|
+
console.log(chalk4.bold(`
|
|
31325
31716
|
Blocks (${task.blocked_by.length}):`));
|
|
31326
31717
|
for (const b of task.blocked_by) {
|
|
31327
31718
|
console.log(` ${formatTaskLine(b)}`);
|
|
31328
31719
|
}
|
|
31329
31720
|
}
|
|
31330
31721
|
if (task.comments.length > 0) {
|
|
31331
|
-
console.log(
|
|
31722
|
+
console.log(chalk4.bold(`
|
|
31332
31723
|
Comments (${task.comments.length}):`));
|
|
31333
31724
|
for (const c of task.comments) {
|
|
31334
|
-
const agent = c.agent_id ?
|
|
31335
|
-
console.log(` ${agent}${
|
|
31725
|
+
const agent = c.agent_id ? chalk4.cyan(`[${c.agent_id}] `) : "";
|
|
31726
|
+
console.log(` ${agent}${chalk4.dim(c.created_at)}: ${c.content}`);
|
|
31336
31727
|
}
|
|
31337
31728
|
}
|
|
31338
31729
|
});
|
|
@@ -31346,12 +31737,12 @@ program2.command("inspect [id]").description("Full orientation for a task \u2014
|
|
|
31346
31737
|
resolvedId = active[0].id;
|
|
31347
31738
|
}
|
|
31348
31739
|
if (!resolvedId) {
|
|
31349
|
-
console.error(
|
|
31740
|
+
console.error(chalk4.red("No task ID given and no active task found. Pass an ID or use --agent."));
|
|
31350
31741
|
process.exit(1);
|
|
31351
31742
|
}
|
|
31352
31743
|
const task = getTaskWithRelations(resolvedId);
|
|
31353
31744
|
if (!task) {
|
|
31354
|
-
console.error(
|
|
31745
|
+
console.error(chalk4.red(`Task not found: ${id || resolvedId}`));
|
|
31355
31746
|
process.exit(1);
|
|
31356
31747
|
}
|
|
31357
31748
|
if (globalOpts.json) {
|
|
@@ -31371,59 +31762,59 @@ program2.command("inspect [id]").description("Full orientation for a task \u2014
|
|
|
31371
31762
|
return;
|
|
31372
31763
|
}
|
|
31373
31764
|
const sid = task.short_id || task.id.slice(0, 8);
|
|
31374
|
-
const statusColor = statusColors4[task.status] ||
|
|
31375
|
-
const prioColor = priorityColors2[task.priority] ||
|
|
31376
|
-
console.log(
|
|
31377
|
-
${
|
|
31765
|
+
const statusColor = statusColors4[task.status] || chalk4.white;
|
|
31766
|
+
const prioColor = priorityColors2[task.priority] || chalk4.white;
|
|
31767
|
+
console.log(chalk4.bold(`
|
|
31768
|
+
${chalk4.cyan(sid)} ${statusColor(task.status)} ${prioColor(task.priority)} ${task.title}
|
|
31378
31769
|
`));
|
|
31379
31770
|
if (task.description) {
|
|
31380
|
-
console.log(
|
|
31771
|
+
console.log(chalk4.dim("Description:"));
|
|
31381
31772
|
console.log(` ${task.description}
|
|
31382
31773
|
`);
|
|
31383
31774
|
}
|
|
31384
31775
|
if (task.assigned_to)
|
|
31385
|
-
console.log(` ${
|
|
31776
|
+
console.log(` ${chalk4.dim("Assigned:")} ${task.assigned_to}`);
|
|
31386
31777
|
if (task.locked_by)
|
|
31387
|
-
console.log(` ${
|
|
31778
|
+
console.log(` ${chalk4.dim("Locked by:")} ${task.locked_by}`);
|
|
31388
31779
|
if (task.project_id)
|
|
31389
|
-
console.log(` ${
|
|
31780
|
+
console.log(` ${chalk4.dim("Project:")} ${task.project_id}`);
|
|
31390
31781
|
if (task.plan_id)
|
|
31391
|
-
console.log(` ${
|
|
31782
|
+
console.log(` ${chalk4.dim("Plan:")} ${task.plan_id}`);
|
|
31392
31783
|
if (task.started_at)
|
|
31393
|
-
console.log(` ${
|
|
31784
|
+
console.log(` ${chalk4.dim("Started:")} ${task.started_at}`);
|
|
31394
31785
|
if (task.completed_at) {
|
|
31395
|
-
console.log(` ${
|
|
31786
|
+
console.log(` ${chalk4.dim("Completed:")} ${task.completed_at}`);
|
|
31396
31787
|
if (task.started_at) {
|
|
31397
31788
|
const dur = Math.round((new Date(task.completed_at).getTime() - new Date(task.started_at).getTime()) / 60000);
|
|
31398
|
-
console.log(` ${
|
|
31789
|
+
console.log(` ${chalk4.dim("Duration:")} ${dur}m`);
|
|
31399
31790
|
}
|
|
31400
31791
|
}
|
|
31401
31792
|
if (task.estimated_minutes)
|
|
31402
|
-
console.log(` ${
|
|
31793
|
+
console.log(` ${chalk4.dim("Estimate:")} ${task.estimated_minutes}m`);
|
|
31403
31794
|
if (task.tags.length > 0)
|
|
31404
|
-
console.log(` ${
|
|
31795
|
+
console.log(` ${chalk4.dim("Tags:")} ${task.tags.join(", ")}`);
|
|
31405
31796
|
const unfinishedDeps = task.dependencies.filter((d) => d.status !== "completed" && d.status !== "cancelled");
|
|
31406
31797
|
if (task.dependencies.length > 0) {
|
|
31407
|
-
console.log(
|
|
31798
|
+
console.log(chalk4.bold(`
|
|
31408
31799
|
Depends on (${task.dependencies.length}):`));
|
|
31409
31800
|
for (const dep of task.dependencies) {
|
|
31410
31801
|
const blocked = dep.status !== "completed" && dep.status !== "cancelled";
|
|
31411
|
-
const icon = blocked ?
|
|
31802
|
+
const icon = blocked ? chalk4.red("\u2717") : chalk4.green("\u2713");
|
|
31412
31803
|
console.log(` ${icon} ${formatTaskLine(dep)}`);
|
|
31413
31804
|
}
|
|
31414
31805
|
}
|
|
31415
31806
|
if (unfinishedDeps.length > 0) {
|
|
31416
|
-
console.log(
|
|
31807
|
+
console.log(chalk4.red(`
|
|
31417
31808
|
BLOCKED by ${unfinishedDeps.length} unfinished dep(s)`));
|
|
31418
31809
|
}
|
|
31419
31810
|
if (task.blocked_by.length > 0) {
|
|
31420
|
-
console.log(
|
|
31811
|
+
console.log(chalk4.bold(`
|
|
31421
31812
|
Blocks (${task.blocked_by.length}):`));
|
|
31422
31813
|
for (const b of task.blocked_by)
|
|
31423
31814
|
console.log(` ${formatTaskLine(b)}`);
|
|
31424
31815
|
}
|
|
31425
31816
|
if (task.subtasks.length > 0) {
|
|
31426
|
-
console.log(
|
|
31817
|
+
console.log(chalk4.bold(`
|
|
31427
31818
|
Subtasks (${task.subtasks.length}):`));
|
|
31428
31819
|
for (const st of task.subtasks)
|
|
31429
31820
|
console.log(` ${formatTaskLine(st)}`);
|
|
@@ -31432,36 +31823,36 @@ ${chalk3.cyan(sid)} ${statusColor(task.status)} ${prioColor(task.priority)} ${ta
|
|
|
31432
31823
|
const { listTaskFiles: listTaskFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
|
|
31433
31824
|
const files = listTaskFiles2(task.id);
|
|
31434
31825
|
if (files.length > 0) {
|
|
31435
|
-
console.log(
|
|
31826
|
+
console.log(chalk4.bold(`
|
|
31436
31827
|
Files (${files.length}):`));
|
|
31437
31828
|
for (const f of files)
|
|
31438
|
-
console.log(` ${
|
|
31829
|
+
console.log(` ${chalk4.dim(f.role || "file")} ${f.path}`);
|
|
31439
31830
|
}
|
|
31440
31831
|
} catch {}
|
|
31441
31832
|
try {
|
|
31442
31833
|
const { getTaskCommits: getTaskCommits2 } = (init_task_commits(), __toCommonJS(exports_task_commits));
|
|
31443
31834
|
const commits = getTaskCommits2(task.id);
|
|
31444
31835
|
if (commits.length > 0) {
|
|
31445
|
-
console.log(
|
|
31836
|
+
console.log(chalk4.bold(`
|
|
31446
31837
|
Commits (${commits.length}):`));
|
|
31447
31838
|
for (const c of commits)
|
|
31448
|
-
console.log(` ${
|
|
31839
|
+
console.log(` ${chalk4.yellow(c.commit_hash.slice(0, 7))} ${c.message || ""}`);
|
|
31449
31840
|
}
|
|
31450
31841
|
} catch {}
|
|
31451
31842
|
if (task.comments.length > 0) {
|
|
31452
|
-
console.log(
|
|
31843
|
+
console.log(chalk4.bold(`
|
|
31453
31844
|
Comments (${task.comments.length}):`));
|
|
31454
31845
|
for (const c of task.comments) {
|
|
31455
|
-
const agent = c.agent_id ?
|
|
31456
|
-
console.log(` ${agent}${
|
|
31846
|
+
const agent = c.agent_id ? chalk4.cyan(`[${c.agent_id}] `) : "";
|
|
31847
|
+
console.log(` ${agent}${chalk4.dim(c.created_at)}: ${c.content}`);
|
|
31457
31848
|
}
|
|
31458
31849
|
}
|
|
31459
31850
|
if (task.checklist && task.checklist.length > 0) {
|
|
31460
31851
|
const done = task.checklist.filter((c) => c.checked).length;
|
|
31461
|
-
console.log(
|
|
31852
|
+
console.log(chalk4.bold(`
|
|
31462
31853
|
Checklist (${done}/${task.checklist.length}):`));
|
|
31463
31854
|
for (const item of task.checklist) {
|
|
31464
|
-
const icon = item.checked ?
|
|
31855
|
+
const icon = item.checked ? chalk4.green("\u2611") : chalk4.dim("\u2610");
|
|
31465
31856
|
console.log(` ${icon} ${item.text || item.title}`);
|
|
31466
31857
|
}
|
|
31467
31858
|
}
|
|
@@ -31477,16 +31868,16 @@ program2.command("history <id>").description("Show change history for a task (au
|
|
|
31477
31868
|
return;
|
|
31478
31869
|
}
|
|
31479
31870
|
if (history.length === 0) {
|
|
31480
|
-
console.log(
|
|
31871
|
+
console.log(chalk4.dim("No history for this task."));
|
|
31481
31872
|
return;
|
|
31482
31873
|
}
|
|
31483
|
-
console.log(
|
|
31874
|
+
console.log(chalk4.bold(`${history.length} change(s):
|
|
31484
31875
|
`));
|
|
31485
31876
|
for (const h of history) {
|
|
31486
|
-
const agent = h.agent_id ?
|
|
31487
|
-
const field = h.field ?
|
|
31488
|
-
const change = h.old_value && h.new_value ? ` ${
|
|
31489
|
-
console.log(` ${
|
|
31877
|
+
const agent = h.agent_id ? chalk4.cyan(` by ${h.agent_id}`) : "";
|
|
31878
|
+
const field = h.field ? chalk4.yellow(` ${h.field}`) : "";
|
|
31879
|
+
const change = h.old_value && h.new_value ? ` ${chalk4.red(h.old_value)} \u2192 ${chalk4.green(h.new_value)}` : h.new_value ? ` \u2192 ${chalk4.green(h.new_value)}` : "";
|
|
31880
|
+
console.log(` ${chalk4.dim(h.created_at)} ${chalk4.bold(h.action)}${field}${change}${agent}`);
|
|
31490
31881
|
}
|
|
31491
31882
|
});
|
|
31492
31883
|
program2.command("update <id>").description("Update a task").option("--title <text>", "New title").option("-d, --description <text>", "New description").option("-s, --status <status>", "New status").option("-p, --priority <priority>", "New priority").option("--assign <agent>", "Assign to agent").option("--tags <tags>", "New tags (comma-separated)").option("--tag <tags>", "New tags (alias for --tags)").option("--list <id>", "Move to a task list").option("--task-list <id>", "Move to a task list (alias for --list)").option("--estimated <minutes>", "Estimated time in minutes").option("--approval", "Require approval before completion").action((id, opts) => {
|
|
@@ -31496,14 +31887,14 @@ program2.command("update <id>").description("Update a task").option("--title <te
|
|
|
31496
31887
|
const resolvedId = resolveTaskId(id);
|
|
31497
31888
|
const current = getTask2(resolvedId);
|
|
31498
31889
|
if (!current) {
|
|
31499
|
-
console.error(
|
|
31890
|
+
console.error(chalk4.red(`Task not found: ${id}`));
|
|
31500
31891
|
process.exit(1);
|
|
31501
31892
|
}
|
|
31502
31893
|
const taskListId = opts.list ? (() => {
|
|
31503
31894
|
const db = getDatabase();
|
|
31504
31895
|
const resolved = resolvePartialId(db, "task_lists", opts.list);
|
|
31505
31896
|
if (!resolved) {
|
|
31506
|
-
console.error(
|
|
31897
|
+
console.error(chalk4.red(`Could not resolve task list ID: ${opts.list}`));
|
|
31507
31898
|
process.exit(1);
|
|
31508
31899
|
}
|
|
31509
31900
|
return resolved;
|
|
@@ -31528,7 +31919,7 @@ program2.command("update <id>").description("Update a task").option("--title <te
|
|
|
31528
31919
|
if (globalOpts.json) {
|
|
31529
31920
|
output(task, true);
|
|
31530
31921
|
} else {
|
|
31531
|
-
console.log(
|
|
31922
|
+
console.log(chalk4.green("Task updated:"));
|
|
31532
31923
|
console.log(formatTaskLine(task));
|
|
31533
31924
|
}
|
|
31534
31925
|
});
|
|
@@ -31548,7 +31939,7 @@ program2.command("done <id>").description("Mark a task as completed").option("--
|
|
|
31548
31939
|
if (globalOpts.json) {
|
|
31549
31940
|
output(task, true);
|
|
31550
31941
|
} else {
|
|
31551
|
-
console.log(
|
|
31942
|
+
console.log(chalk4.green("Task completed:"));
|
|
31552
31943
|
console.log(formatTaskLine(task));
|
|
31553
31944
|
}
|
|
31554
31945
|
});
|
|
@@ -31557,15 +31948,15 @@ program2.command("approve <id>").description("Approve a task that requires appro
|
|
|
31557
31948
|
const resolvedId = resolveTaskId(id);
|
|
31558
31949
|
const task = getTask2(resolvedId);
|
|
31559
31950
|
if (!task) {
|
|
31560
|
-
console.error(
|
|
31951
|
+
console.error(chalk4.red(`Task not found: ${id}`));
|
|
31561
31952
|
process.exit(1);
|
|
31562
31953
|
}
|
|
31563
31954
|
if (!task.requires_approval) {
|
|
31564
|
-
console.log(
|
|
31955
|
+
console.log(chalk4.yellow("This task does not require approval."));
|
|
31565
31956
|
return;
|
|
31566
31957
|
}
|
|
31567
31958
|
if (task.approved_by) {
|
|
31568
|
-
console.log(
|
|
31959
|
+
console.log(chalk4.yellow(`Already approved by ${task.approved_by}.`));
|
|
31569
31960
|
return;
|
|
31570
31961
|
}
|
|
31571
31962
|
try {
|
|
@@ -31573,7 +31964,7 @@ program2.command("approve <id>").description("Approve a task that requires appro
|
|
|
31573
31964
|
if (globalOpts.json) {
|
|
31574
31965
|
output(updated, true);
|
|
31575
31966
|
} else {
|
|
31576
|
-
console.log(
|
|
31967
|
+
console.log(chalk4.green(`Task approved by ${globalOpts.agent || "cli"}:`));
|
|
31577
31968
|
console.log(formatTaskLine(updated));
|
|
31578
31969
|
}
|
|
31579
31970
|
} catch (e) {
|
|
@@ -31593,7 +31984,7 @@ program2.command("start <id>").description("Claim, lock, and start a task").acti
|
|
|
31593
31984
|
if (globalOpts.json) {
|
|
31594
31985
|
output(task, true);
|
|
31595
31986
|
} else {
|
|
31596
|
-
console.log(
|
|
31987
|
+
console.log(chalk4.green(`Task started by ${agentId}:`));
|
|
31597
31988
|
console.log(formatTaskLine(task));
|
|
31598
31989
|
}
|
|
31599
31990
|
});
|
|
@@ -31610,9 +32001,9 @@ program2.command("lock <id>").description("Acquire exclusive lock on a task").ac
|
|
|
31610
32001
|
if (globalOpts.json) {
|
|
31611
32002
|
output(result, true);
|
|
31612
32003
|
} else if (result.success) {
|
|
31613
|
-
console.log(
|
|
32004
|
+
console.log(chalk4.green(`Lock acquired by ${agentId}`));
|
|
31614
32005
|
} else {
|
|
31615
|
-
console.error(
|
|
32006
|
+
console.error(chalk4.red(`Lock failed: ${result.error}`));
|
|
31616
32007
|
process.exit(1);
|
|
31617
32008
|
}
|
|
31618
32009
|
});
|
|
@@ -31627,7 +32018,7 @@ program2.command("unlock <id>").description("Release lock on a task").action((id
|
|
|
31627
32018
|
if (globalOpts.json) {
|
|
31628
32019
|
output({ success: true }, true);
|
|
31629
32020
|
} else {
|
|
31630
|
-
console.log(
|
|
32021
|
+
console.log(chalk4.green("Lock released."));
|
|
31631
32022
|
}
|
|
31632
32023
|
});
|
|
31633
32024
|
program2.command("delete <id>").description("Delete a task").action((id) => {
|
|
@@ -31637,9 +32028,9 @@ program2.command("delete <id>").description("Delete a task").action((id) => {
|
|
|
31637
32028
|
if (globalOpts.json) {
|
|
31638
32029
|
output({ deleted }, true);
|
|
31639
32030
|
} else if (deleted) {
|
|
31640
|
-
console.log(
|
|
32031
|
+
console.log(chalk4.green("Task deleted."));
|
|
31641
32032
|
} else {
|
|
31642
|
-
console.error(
|
|
32033
|
+
console.error(chalk4.red("Task not found."));
|
|
31643
32034
|
process.exit(1);
|
|
31644
32035
|
}
|
|
31645
32036
|
});
|
|
@@ -31650,9 +32041,9 @@ program2.command("remove <id>").description("Remove/delete a task (alias for del
|
|
|
31650
32041
|
if (globalOpts.json) {
|
|
31651
32042
|
output({ deleted }, true);
|
|
31652
32043
|
} else if (deleted) {
|
|
31653
|
-
console.log(
|
|
32044
|
+
console.log(chalk4.green("Task removed."));
|
|
31654
32045
|
} else {
|
|
31655
|
-
console.error(
|
|
32046
|
+
console.error(chalk4.red("Task not found."));
|
|
31656
32047
|
process.exit(1);
|
|
31657
32048
|
}
|
|
31658
32049
|
});
|
|
@@ -31672,7 +32063,7 @@ program2.command("bulk <action> <ids...>").description("Bulk operation on multip
|
|
|
31672
32063
|
deleteTask(resolvedId);
|
|
31673
32064
|
results.push({ id: resolvedId, success: true });
|
|
31674
32065
|
} else {
|
|
31675
|
-
console.error(
|
|
32066
|
+
console.error(chalk4.red(`Unknown action: ${action}. Use: done, start, delete`));
|
|
31676
32067
|
process.exit(1);
|
|
31677
32068
|
}
|
|
31678
32069
|
} catch (e) {
|
|
@@ -31684,9 +32075,9 @@ program2.command("bulk <action> <ids...>").description("Bulk operation on multip
|
|
|
31684
32075
|
if (globalOpts.json) {
|
|
31685
32076
|
output({ results, succeeded, failed }, true);
|
|
31686
32077
|
} else {
|
|
31687
|
-
console.log(
|
|
32078
|
+
console.log(chalk4.green(`${action}: ${succeeded} succeeded, ${failed} failed`));
|
|
31688
32079
|
for (const r of results.filter((r2) => !r2.success)) {
|
|
31689
|
-
console.log(
|
|
32080
|
+
console.log(chalk4.red(` ${r.id}: ${r.error}`));
|
|
31690
32081
|
}
|
|
31691
32082
|
}
|
|
31692
32083
|
});
|
|
@@ -31702,8 +32093,8 @@ program2.command("plans").description("List and manage plans").option("--add <na
|
|
|
31702
32093
|
if (globalOpts.json) {
|
|
31703
32094
|
output(plan, true);
|
|
31704
32095
|
} else {
|
|
31705
|
-
console.log(
|
|
31706
|
-
console.log(`${
|
|
32096
|
+
console.log(chalk4.green("Plan created:"));
|
|
32097
|
+
console.log(`${chalk4.dim(plan.id.slice(0, 8))} ${chalk4.bold(plan.name)} ${chalk4.cyan(`[${plan.status}]`)}`);
|
|
31707
32098
|
}
|
|
31708
32099
|
return;
|
|
31709
32100
|
}
|
|
@@ -31711,12 +32102,12 @@ program2.command("plans").description("List and manage plans").option("--add <na
|
|
|
31711
32102
|
const db = getDatabase();
|
|
31712
32103
|
const resolvedId = resolvePartialId(db, "plans", opts.show);
|
|
31713
32104
|
if (!resolvedId) {
|
|
31714
|
-
console.error(
|
|
32105
|
+
console.error(chalk4.red(`Could not resolve plan ID: ${opts.show}`));
|
|
31715
32106
|
process.exit(1);
|
|
31716
32107
|
}
|
|
31717
32108
|
const plan = getPlan(resolvedId);
|
|
31718
32109
|
if (!plan) {
|
|
31719
|
-
console.error(
|
|
32110
|
+
console.error(chalk4.red(`Plan not found: ${opts.show}`));
|
|
31720
32111
|
process.exit(1);
|
|
31721
32112
|
}
|
|
31722
32113
|
const tasks = listTasks({ plan_id: resolvedId });
|
|
@@ -31724,24 +32115,24 @@ program2.command("plans").description("List and manage plans").option("--add <na
|
|
|
31724
32115
|
output({ plan, tasks }, true);
|
|
31725
32116
|
return;
|
|
31726
32117
|
}
|
|
31727
|
-
console.log(
|
|
32118
|
+
console.log(chalk4.bold(`Plan Details:
|
|
31728
32119
|
`));
|
|
31729
|
-
console.log(` ${
|
|
31730
|
-
console.log(` ${
|
|
31731
|
-
console.log(` ${
|
|
32120
|
+
console.log(` ${chalk4.dim("ID:")} ${plan.id}`);
|
|
32121
|
+
console.log(` ${chalk4.dim("Name:")} ${plan.name}`);
|
|
32122
|
+
console.log(` ${chalk4.dim("Status:")} ${chalk4.cyan(plan.status)}`);
|
|
31732
32123
|
if (plan.description)
|
|
31733
|
-
console.log(` ${
|
|
32124
|
+
console.log(` ${chalk4.dim("Desc:")} ${plan.description}`);
|
|
31734
32125
|
if (plan.project_id)
|
|
31735
|
-
console.log(` ${
|
|
31736
|
-
console.log(` ${
|
|
32126
|
+
console.log(` ${chalk4.dim("Project:")} ${plan.project_id}`);
|
|
32127
|
+
console.log(` ${chalk4.dim("Created:")} ${plan.created_at}`);
|
|
31737
32128
|
if (tasks.length > 0) {
|
|
31738
|
-
console.log(
|
|
32129
|
+
console.log(chalk4.bold(`
|
|
31739
32130
|
Tasks (${tasks.length}):`));
|
|
31740
32131
|
for (const t of tasks) {
|
|
31741
32132
|
console.log(` ${formatTaskLine(t)}`);
|
|
31742
32133
|
}
|
|
31743
32134
|
} else {
|
|
31744
|
-
console.log(
|
|
32135
|
+
console.log(chalk4.dim(`
|
|
31745
32136
|
No tasks in this plan.`));
|
|
31746
32137
|
}
|
|
31747
32138
|
return;
|
|
@@ -31750,16 +32141,16 @@ program2.command("plans").description("List and manage plans").option("--add <na
|
|
|
31750
32141
|
const db = getDatabase();
|
|
31751
32142
|
const resolvedId = resolvePartialId(db, "plans", opts.delete);
|
|
31752
32143
|
if (!resolvedId) {
|
|
31753
|
-
console.error(
|
|
32144
|
+
console.error(chalk4.red(`Could not resolve plan ID: ${opts.delete}`));
|
|
31754
32145
|
process.exit(1);
|
|
31755
32146
|
}
|
|
31756
32147
|
const deleted = deletePlan(resolvedId);
|
|
31757
32148
|
if (globalOpts.json) {
|
|
31758
32149
|
output({ deleted }, true);
|
|
31759
32150
|
} else if (deleted) {
|
|
31760
|
-
console.log(
|
|
32151
|
+
console.log(chalk4.green("Plan deleted."));
|
|
31761
32152
|
} else {
|
|
31762
|
-
console.error(
|
|
32153
|
+
console.error(chalk4.red("Plan not found."));
|
|
31763
32154
|
process.exit(1);
|
|
31764
32155
|
}
|
|
31765
32156
|
return;
|
|
@@ -31768,7 +32159,7 @@ program2.command("plans").description("List and manage plans").option("--add <na
|
|
|
31768
32159
|
const db = getDatabase();
|
|
31769
32160
|
const resolvedId = resolvePartialId(db, "plans", opts.complete);
|
|
31770
32161
|
if (!resolvedId) {
|
|
31771
|
-
console.error(
|
|
32162
|
+
console.error(chalk4.red(`Could not resolve plan ID: ${opts.complete}`));
|
|
31772
32163
|
process.exit(1);
|
|
31773
32164
|
}
|
|
31774
32165
|
try {
|
|
@@ -31776,8 +32167,8 @@ program2.command("plans").description("List and manage plans").option("--add <na
|
|
|
31776
32167
|
if (globalOpts.json) {
|
|
31777
32168
|
output(plan, true);
|
|
31778
32169
|
} else {
|
|
31779
|
-
console.log(
|
|
31780
|
-
console.log(`${
|
|
32170
|
+
console.log(chalk4.green("Plan completed:"));
|
|
32171
|
+
console.log(`${chalk4.dim(plan.id.slice(0, 8))} ${chalk4.bold(plan.name)} ${chalk4.cyan(`[${plan.status}]`)}`);
|
|
31781
32172
|
}
|
|
31782
32173
|
} catch (e) {
|
|
31783
32174
|
handleError(e);
|
|
@@ -31790,14 +32181,14 @@ program2.command("plans").description("List and manage plans").option("--add <na
|
|
|
31790
32181
|
return;
|
|
31791
32182
|
}
|
|
31792
32183
|
if (plans.length === 0) {
|
|
31793
|
-
console.log(
|
|
32184
|
+
console.log(chalk4.dim("No plans found."));
|
|
31794
32185
|
return;
|
|
31795
32186
|
}
|
|
31796
|
-
console.log(
|
|
32187
|
+
console.log(chalk4.bold(`${plans.length} plan(s):
|
|
31797
32188
|
`));
|
|
31798
32189
|
for (const p of plans) {
|
|
31799
|
-
const desc = p.description ?
|
|
31800
|
-
console.log(`${
|
|
32190
|
+
const desc = p.description ? chalk4.dim(` - ${p.description}`) : "";
|
|
32191
|
+
console.log(`${chalk4.dim(p.id.slice(0, 8))} ${chalk4.bold(p.name)} ${chalk4.cyan(`[${p.status}]`)}${desc}`);
|
|
31801
32192
|
}
|
|
31802
32193
|
});
|
|
31803
32194
|
program2.command("templates").description("List and manage task templates").option("--add <name>", "Create a template").option("--title <pattern>", "Title pattern (with --add)").option("-d, --description <text>", "Default description").option("-p, --priority <level>", "Default priority").option("-t, --tags <tags>", "Default tags (comma-separated)").option("--delete <id>", "Delete a template").option("--update <id>", "Update a template").option("--use <id>", "Create a task from a template").option("--var <vars...>", "Variable substitutions: key=value (e.g. --var feature=login)").action((opts) => {
|
|
@@ -31805,7 +32196,7 @@ program2.command("templates").description("List and manage task templates").opti
|
|
|
31805
32196
|
const { createTemplate: createTemplate2, listTemplates: listTemplates2, deleteTemplate: deleteTemplate2, updateTemplate: updateTemplate2, taskFromTemplate: taskFromTemplate2 } = (init_templates(), __toCommonJS(exports_templates));
|
|
31806
32197
|
if (opts.add) {
|
|
31807
32198
|
if (!opts.title) {
|
|
31808
|
-
console.error(
|
|
32199
|
+
console.error(chalk4.red("--title is required with --add"));
|
|
31809
32200
|
process.exit(1);
|
|
31810
32201
|
}
|
|
31811
32202
|
const projectId = autoProject(globalOpts);
|
|
@@ -31820,7 +32211,7 @@ program2.command("templates").description("List and manage task templates").opti
|
|
|
31820
32211
|
if (globalOpts.json) {
|
|
31821
32212
|
output(template, true);
|
|
31822
32213
|
} else {
|
|
31823
|
-
console.log(
|
|
32214
|
+
console.log(chalk4.green(`Template created: ${template.id.slice(0, 8)} | ${template.name} | "${template.title_pattern}"`));
|
|
31824
32215
|
}
|
|
31825
32216
|
return;
|
|
31826
32217
|
}
|
|
@@ -31829,9 +32220,9 @@ program2.command("templates").description("List and manage task templates").opti
|
|
|
31829
32220
|
if (globalOpts.json) {
|
|
31830
32221
|
output({ deleted }, true);
|
|
31831
32222
|
} else if (deleted) {
|
|
31832
|
-
console.log(
|
|
32223
|
+
console.log(chalk4.green("Template deleted."));
|
|
31833
32224
|
} else {
|
|
31834
|
-
console.error(
|
|
32225
|
+
console.error(chalk4.red("Template not found."));
|
|
31835
32226
|
process.exit(1);
|
|
31836
32227
|
}
|
|
31837
32228
|
return;
|
|
@@ -31850,13 +32241,13 @@ program2.command("templates").description("List and manage task templates").opti
|
|
|
31850
32241
|
updates.tags = opts.tags.split(",").map((t) => t.trim());
|
|
31851
32242
|
const updated = updateTemplate2(opts.update, updates);
|
|
31852
32243
|
if (!updated) {
|
|
31853
|
-
console.error(
|
|
32244
|
+
console.error(chalk4.red("Template not found."));
|
|
31854
32245
|
process.exit(1);
|
|
31855
32246
|
}
|
|
31856
32247
|
if (globalOpts.json) {
|
|
31857
32248
|
output(updated, true);
|
|
31858
32249
|
} else {
|
|
31859
|
-
console.log(
|
|
32250
|
+
console.log(chalk4.green(`Template updated: ${updated.id.slice(0, 8)} | ${updated.name} | "${updated.title_pattern}"`));
|
|
31860
32251
|
}
|
|
31861
32252
|
return;
|
|
31862
32253
|
}
|
|
@@ -31867,7 +32258,7 @@ program2.command("templates").description("List and manage task templates").opti
|
|
|
31867
32258
|
for (const v of opts.var) {
|
|
31868
32259
|
const eq = v.indexOf("=");
|
|
31869
32260
|
if (eq === -1) {
|
|
31870
|
-
console.error(
|
|
32261
|
+
console.error(chalk4.red(`Invalid variable format: ${v} (expected key=value)`));
|
|
31871
32262
|
process.exit(1);
|
|
31872
32263
|
}
|
|
31873
32264
|
variables[v.slice(0, eq)] = v.slice(eq + 1);
|
|
@@ -31889,7 +32280,7 @@ program2.command("templates").description("List and manage task templates").opti
|
|
|
31889
32280
|
if (globalOpts.json) {
|
|
31890
32281
|
output(task, true);
|
|
31891
32282
|
} else {
|
|
31892
|
-
console.log(
|
|
32283
|
+
console.log(chalk4.green("Task created from template:"));
|
|
31893
32284
|
console.log(formatTaskLine(task));
|
|
31894
32285
|
}
|
|
31895
32286
|
} catch (e) {
|
|
@@ -31903,14 +32294,14 @@ program2.command("templates").description("List and manage task templates").opti
|
|
|
31903
32294
|
return;
|
|
31904
32295
|
}
|
|
31905
32296
|
if (templates.length === 0) {
|
|
31906
|
-
console.log(
|
|
32297
|
+
console.log(chalk4.dim("No templates."));
|
|
31907
32298
|
return;
|
|
31908
32299
|
}
|
|
31909
|
-
console.log(
|
|
32300
|
+
console.log(chalk4.bold(`${templates.length} template(s):
|
|
31910
32301
|
`));
|
|
31911
32302
|
for (const t of templates) {
|
|
31912
|
-
const vars = t.variables && t.variables.length > 0 ? ` ${
|
|
31913
|
-
console.log(` ${
|
|
32303
|
+
const vars = t.variables && t.variables.length > 0 ? ` ${chalk4.dim(`(${t.variables.map((v) => `${v.name}${v.required ? "*" : ""}${v.default ? `=${v.default}` : ""}`).join(", ")})`)}` : "";
|
|
32304
|
+
console.log(` ${chalk4.dim(t.id.slice(0, 8))} ${chalk4.bold(t.name)} ${chalk4.cyan(`"${t.title_pattern}"`)} ${chalk4.yellow(t.priority)}${vars}`);
|
|
31914
32305
|
}
|
|
31915
32306
|
});
|
|
31916
32307
|
program2.command("template-init").alias("templates-init").description("Initialize built-in starter templates (open-source-project, bug-fix, feature, security-audit)").action(() => {
|
|
@@ -31922,9 +32313,9 @@ program2.command("template-init").alias("templates-init").description("Initializ
|
|
|
31922
32313
|
return;
|
|
31923
32314
|
}
|
|
31924
32315
|
if (result.created === 0) {
|
|
31925
|
-
console.log(
|
|
32316
|
+
console.log(chalk4.dim(`All ${result.skipped} built-in template(s) already exist.`));
|
|
31926
32317
|
} else {
|
|
31927
|
-
console.log(
|
|
32318
|
+
console.log(chalk4.green(`Created ${result.created} template(s): ${result.names.join(", ")}. Skipped ${result.skipped} existing.`));
|
|
31928
32319
|
}
|
|
31929
32320
|
});
|
|
31930
32321
|
program2.command("template-preview <id>").alias("templates-preview").description("Preview a template without creating tasks \u2014 shows resolved titles, deps, and priorities").option("--var <vars...>", "Variable substitution in key=value format (e.g. --var name=invoices)").action((id, opts) => {
|
|
@@ -31935,7 +32326,7 @@ program2.command("template-preview <id>").alias("templates-preview").description
|
|
|
31935
32326
|
for (const v of opts.var) {
|
|
31936
32327
|
const eq = v.indexOf("=");
|
|
31937
32328
|
if (eq === -1) {
|
|
31938
|
-
console.error(
|
|
32329
|
+
console.error(chalk4.red(`Invalid variable format: ${v} (expected key=value)`));
|
|
31939
32330
|
process.exit(1);
|
|
31940
32331
|
}
|
|
31941
32332
|
variables[v.slice(0, eq)] = v.slice(eq + 1);
|
|
@@ -31947,19 +32338,19 @@ program2.command("template-preview <id>").alias("templates-preview").description
|
|
|
31947
32338
|
output(preview, true);
|
|
31948
32339
|
return;
|
|
31949
32340
|
}
|
|
31950
|
-
console.log(
|
|
32341
|
+
console.log(chalk4.bold(`Preview: ${preview.template_name} (${preview.tasks.length} tasks)`));
|
|
31951
32342
|
if (preview.description)
|
|
31952
|
-
console.log(
|
|
32343
|
+
console.log(chalk4.dim(` ${preview.description}`));
|
|
31953
32344
|
if (preview.variables.length > 0) {
|
|
31954
|
-
console.log(
|
|
32345
|
+
console.log(chalk4.dim(` Variables: ${preview.variables.map((v) => `${v.name}${v.required ? "*" : ""}${v.default ? `=${v.default}` : ""}`).join(", ")}`));
|
|
31955
32346
|
}
|
|
31956
32347
|
if (Object.keys(preview.resolved_variables).length > 0) {
|
|
31957
|
-
console.log(
|
|
32348
|
+
console.log(chalk4.dim(` Resolved: ${Object.entries(preview.resolved_variables).map(([k, v]) => `${k}=${v}`).join(", ")}`));
|
|
31958
32349
|
}
|
|
31959
32350
|
console.log();
|
|
31960
32351
|
for (const t of preview.tasks) {
|
|
31961
|
-
const deps = t.depends_on_positions.length > 0 ?
|
|
31962
|
-
console.log(` ${
|
|
32352
|
+
const deps = t.depends_on_positions.length > 0 ? chalk4.dim(` (after: ${t.depends_on_positions.join(", ")})`) : "";
|
|
32353
|
+
console.log(` ${chalk4.dim(`[${t.position}]`)} ${chalk4.yellow(t.priority)} | ${t.title}${deps}`);
|
|
31963
32354
|
}
|
|
31964
32355
|
} catch (e) {
|
|
31965
32356
|
handleError(e);
|
|
@@ -31981,7 +32372,7 @@ program2.command("template-import [file]").alias("templates-import").description
|
|
|
31981
32372
|
try {
|
|
31982
32373
|
const filePath = file || opts.file;
|
|
31983
32374
|
if (!filePath) {
|
|
31984
|
-
console.error(
|
|
32375
|
+
console.error(chalk4.red("Provide a file path: todos template-import <file> or --file <path>"));
|
|
31985
32376
|
process.exit(1);
|
|
31986
32377
|
}
|
|
31987
32378
|
const content = readFileSync8(filePath, "utf-8");
|
|
@@ -31990,7 +32381,7 @@ program2.command("template-import [file]").alias("templates-import").description
|
|
|
31990
32381
|
if (globalOpts.json) {
|
|
31991
32382
|
output(template, true);
|
|
31992
32383
|
} else {
|
|
31993
|
-
console.log(
|
|
32384
|
+
console.log(chalk4.green(`Template imported: ${template.id.slice(0, 8)} | ${template.name} | "${template.title_pattern}"`));
|
|
31994
32385
|
}
|
|
31995
32386
|
} catch (e) {
|
|
31996
32387
|
handleError(e);
|
|
@@ -32002,7 +32393,7 @@ program2.command("template-history <id>").alias("templates-history").description
|
|
|
32002
32393
|
try {
|
|
32003
32394
|
const template = getTemplate2(id);
|
|
32004
32395
|
if (!template) {
|
|
32005
|
-
console.error(
|
|
32396
|
+
console.error(chalk4.red("Template not found."));
|
|
32006
32397
|
process.exit(1);
|
|
32007
32398
|
}
|
|
32008
32399
|
const versions = listTemplateVersions2(id);
|
|
@@ -32010,13 +32401,13 @@ program2.command("template-history <id>").alias("templates-history").description
|
|
|
32010
32401
|
output({ current_version: template.version, versions }, true);
|
|
32011
32402
|
return;
|
|
32012
32403
|
}
|
|
32013
|
-
console.log(
|
|
32404
|
+
console.log(chalk4.bold(`${template.name} \u2014 current version: ${template.version}`));
|
|
32014
32405
|
if (versions.length === 0) {
|
|
32015
|
-
console.log(
|
|
32406
|
+
console.log(chalk4.dim(" No previous versions."));
|
|
32016
32407
|
} else {
|
|
32017
32408
|
for (const v of versions) {
|
|
32018
32409
|
const snap = JSON.parse(v.snapshot);
|
|
32019
|
-
console.log(` ${
|
|
32410
|
+
console.log(` ${chalk4.dim(`v${v.version}`)} | ${v.created_at} | ${snap.name} | "${snap.title_pattern}"`);
|
|
32020
32411
|
}
|
|
32021
32412
|
}
|
|
32022
32413
|
} catch (e) {
|
|
@@ -32035,7 +32426,7 @@ program2.command("comment <id> <text>").description("Add a comment to a task").a
|
|
|
32035
32426
|
if (globalOpts.json) {
|
|
32036
32427
|
output(comment, true);
|
|
32037
32428
|
} else {
|
|
32038
|
-
console.log(
|
|
32429
|
+
console.log(chalk4.green("Comment added."));
|
|
32039
32430
|
}
|
|
32040
32431
|
});
|
|
32041
32432
|
program2.command("search <query>").description("Search tasks").option("--status <status>", "Filter by status").option("--priority <p>", "Filter by priority").option("--assigned <agent>", "Filter by assigned agent").option("--since <date>", "Only tasks updated after this date (ISO)").option("--blocked", "Only blocked tasks (incomplete dependencies)").option("--has-deps", "Only tasks with dependencies").action((query, opts) => {
|
|
@@ -32060,10 +32451,10 @@ program2.command("search <query>").description("Search tasks").option("--status
|
|
|
32060
32451
|
return;
|
|
32061
32452
|
}
|
|
32062
32453
|
if (tasks.length === 0) {
|
|
32063
|
-
console.log(
|
|
32454
|
+
console.log(chalk4.dim(`No tasks matching "${query}".`));
|
|
32064
32455
|
return;
|
|
32065
32456
|
}
|
|
32066
|
-
console.log(
|
|
32457
|
+
console.log(chalk4.bold(`${tasks.length} result(s) for "${query}":
|
|
32067
32458
|
`));
|
|
32068
32459
|
for (const t of tasks) {
|
|
32069
32460
|
console.log(formatTaskLine(t));
|
|
@@ -32077,13 +32468,13 @@ program2.command("deps <id>").description("Manage task dependencies").option("--
|
|
|
32077
32468
|
try {
|
|
32078
32469
|
addDependency(resolvedId, depId);
|
|
32079
32470
|
} catch (e) {
|
|
32080
|
-
console.error(
|
|
32471
|
+
console.error(chalk4.red(e instanceof Error ? e.message : String(e)));
|
|
32081
32472
|
process.exit(1);
|
|
32082
32473
|
}
|
|
32083
32474
|
if (globalOpts.json) {
|
|
32084
32475
|
output({ task_id: resolvedId, depends_on: depId }, true);
|
|
32085
32476
|
} else {
|
|
32086
|
-
console.log(
|
|
32477
|
+
console.log(chalk4.green("Dependency added."));
|
|
32087
32478
|
}
|
|
32088
32479
|
} else if (opts.remove) {
|
|
32089
32480
|
const depId = resolveTaskId(opts.remove);
|
|
@@ -32091,12 +32482,12 @@ program2.command("deps <id>").description("Manage task dependencies").option("--
|
|
|
32091
32482
|
if (globalOpts.json) {
|
|
32092
32483
|
output({ removed }, true);
|
|
32093
32484
|
} else {
|
|
32094
|
-
console.log(removed ?
|
|
32485
|
+
console.log(removed ? chalk4.green("Dependency removed.") : chalk4.red("Dependency not found."));
|
|
32095
32486
|
}
|
|
32096
32487
|
} else {
|
|
32097
32488
|
const task = getTaskWithRelations(resolvedId);
|
|
32098
32489
|
if (!task) {
|
|
32099
|
-
console.error(
|
|
32490
|
+
console.error(chalk4.red("Task not found."));
|
|
32100
32491
|
process.exit(1);
|
|
32101
32492
|
}
|
|
32102
32493
|
if (globalOpts.json) {
|
|
@@ -32104,19 +32495,19 @@ program2.command("deps <id>").description("Manage task dependencies").option("--
|
|
|
32104
32495
|
return;
|
|
32105
32496
|
}
|
|
32106
32497
|
if (task.dependencies.length > 0) {
|
|
32107
|
-
console.log(
|
|
32498
|
+
console.log(chalk4.bold("Depends on:"));
|
|
32108
32499
|
for (const dep of task.dependencies) {
|
|
32109
32500
|
console.log(` ${formatTaskLine(dep)}`);
|
|
32110
32501
|
}
|
|
32111
32502
|
}
|
|
32112
32503
|
if (task.blocked_by.length > 0) {
|
|
32113
|
-
console.log(
|
|
32504
|
+
console.log(chalk4.bold("Blocks:"));
|
|
32114
32505
|
for (const b of task.blocked_by) {
|
|
32115
32506
|
console.log(` ${formatTaskLine(b)}`);
|
|
32116
32507
|
}
|
|
32117
32508
|
}
|
|
32118
32509
|
if (task.dependencies.length === 0 && task.blocked_by.length === 0) {
|
|
32119
|
-
console.log(
|
|
32510
|
+
console.log(chalk4.dim("No dependencies."));
|
|
32120
32511
|
}
|
|
32121
32512
|
}
|
|
32122
32513
|
});
|
|
@@ -32142,9 +32533,9 @@ program2.command("projects").description("List and manage projects").option("--a
|
|
|
32142
32533
|
if (globalOpts.json) {
|
|
32143
32534
|
output(project, true);
|
|
32144
32535
|
} else {
|
|
32145
|
-
console.log(
|
|
32536
|
+
console.log(chalk4.green(`Project registered: ${project.name} (${project.path})`));
|
|
32146
32537
|
if (project.task_list_id)
|
|
32147
|
-
console.log(
|
|
32538
|
+
console.log(chalk4.dim(` Task list: ${project.task_list_id}`));
|
|
32148
32539
|
}
|
|
32149
32540
|
return;
|
|
32150
32541
|
}
|
|
@@ -32154,14 +32545,14 @@ program2.command("projects").description("List and manage projects").option("--a
|
|
|
32154
32545
|
return;
|
|
32155
32546
|
}
|
|
32156
32547
|
if (projects.length === 0) {
|
|
32157
|
-
console.log(
|
|
32548
|
+
console.log(chalk4.dim("No projects registered."));
|
|
32158
32549
|
return;
|
|
32159
32550
|
}
|
|
32160
|
-
console.log(
|
|
32551
|
+
console.log(chalk4.bold(`${projects.length} project(s):
|
|
32161
32552
|
`));
|
|
32162
32553
|
for (const p of projects) {
|
|
32163
|
-
const taskList = p.task_list_id ?
|
|
32164
|
-
console.log(`${
|
|
32554
|
+
const taskList = p.task_list_id ? chalk4.cyan(` [${p.task_list_id}]`) : "";
|
|
32555
|
+
console.log(`${chalk4.dim(p.id.slice(0, 8))} ${chalk4.bold(p.name)} ${chalk4.dim(p.path)}${taskList}${p.description ? ` - ${p.description}` : ""}`);
|
|
32165
32556
|
}
|
|
32166
32557
|
});
|
|
32167
32558
|
program2.command("project-rename <id-or-slug> <new-slug>").description("Rename a project slug. Cascades to matching task lists. Task prefixes (e.g. APP-00001) are unchanged.").option("--name <name>", "Also update the project display name").option("-j, --json", "Output as JSON").action((idOrSlug, newSlug, opts) => {
|
|
@@ -32176,20 +32567,20 @@ program2.command("project-rename <id-or-slug> <new-slug>").description("Rename a
|
|
|
32176
32567
|
resolvedId = bySlug?.id ?? null;
|
|
32177
32568
|
}
|
|
32178
32569
|
if (!resolvedId) {
|
|
32179
|
-
console.error(
|
|
32570
|
+
console.error(chalk4.red(`Project not found: ${idOrSlug}`));
|
|
32180
32571
|
process.exit(1);
|
|
32181
32572
|
}
|
|
32182
32573
|
const result = renameProject2(resolvedId, { name: opts.name, new_slug: newSlug });
|
|
32183
32574
|
if (useJson) {
|
|
32184
32575
|
output({ project: result.project, task_lists_updated: result.task_lists_updated }, true);
|
|
32185
32576
|
} else {
|
|
32186
|
-
console.log(
|
|
32577
|
+
console.log(chalk4.green(`Project renamed: ${result.project.name} (slug: ${result.project.task_list_id})`));
|
|
32187
32578
|
if (result.task_lists_updated > 0) {
|
|
32188
|
-
console.log(
|
|
32579
|
+
console.log(chalk4.dim(` Updated ${result.task_lists_updated} task list slug(s).`));
|
|
32189
32580
|
}
|
|
32190
32581
|
}
|
|
32191
32582
|
} catch (e) {
|
|
32192
|
-
console.error(
|
|
32583
|
+
console.error(chalk4.red(e instanceof Error ? e.message : String(e)));
|
|
32193
32584
|
process.exit(1);
|
|
32194
32585
|
}
|
|
32195
32586
|
});
|
|
@@ -32202,17 +32593,17 @@ projectsPathCmd.command("set <project-id> <path>").description("Set the local pa
|
|
|
32202
32593
|
const db = getDatabase();
|
|
32203
32594
|
const resolved = resolvePartialId(db, "projects", projectId);
|
|
32204
32595
|
if (!resolved) {
|
|
32205
|
-
console.error(
|
|
32596
|
+
console.error(chalk4.red(`Project not found: ${projectId}`));
|
|
32206
32597
|
process.exit(1);
|
|
32207
32598
|
}
|
|
32208
32599
|
const entry = setMachineLocalPath2(resolved, resolve4(projectPath));
|
|
32209
32600
|
if (useJson) {
|
|
32210
32601
|
output(entry, true);
|
|
32211
32602
|
} else {
|
|
32212
|
-
console.log(
|
|
32603
|
+
console.log(chalk4.green(`Local path set: ${entry.path} (machine: ${entry.machine_id.slice(0, 8)})`));
|
|
32213
32604
|
}
|
|
32214
32605
|
} catch (e) {
|
|
32215
|
-
console.error(
|
|
32606
|
+
console.error(chalk4.red(e instanceof Error ? e.message : String(e)));
|
|
32216
32607
|
process.exit(1);
|
|
32217
32608
|
}
|
|
32218
32609
|
});
|
|
@@ -32224,7 +32615,7 @@ projectsPathCmd.command("list <project-id>").description("List all machine path
|
|
|
32224
32615
|
const db = getDatabase();
|
|
32225
32616
|
const resolved = resolvePartialId(db, "projects", projectId);
|
|
32226
32617
|
if (!resolved) {
|
|
32227
|
-
console.error(
|
|
32618
|
+
console.error(chalk4.red(`Project not found: ${projectId}`));
|
|
32228
32619
|
process.exit(1);
|
|
32229
32620
|
}
|
|
32230
32621
|
const paths = listMachineLocalPaths2(resolved);
|
|
@@ -32233,14 +32624,14 @@ projectsPathCmd.command("list <project-id>").description("List all machine path
|
|
|
32233
32624
|
return;
|
|
32234
32625
|
}
|
|
32235
32626
|
if (paths.length === 0) {
|
|
32236
|
-
console.log(
|
|
32627
|
+
console.log(chalk4.dim("No machine path overrides."));
|
|
32237
32628
|
return;
|
|
32238
32629
|
}
|
|
32239
32630
|
for (const p of paths) {
|
|
32240
|
-
console.log(`${
|
|
32631
|
+
console.log(`${chalk4.dim(p.machine_id.slice(0, 8))} ${p.path} ${chalk4.dim(p.updated_at)}`);
|
|
32241
32632
|
}
|
|
32242
32633
|
} catch (e) {
|
|
32243
|
-
console.error(
|
|
32634
|
+
console.error(chalk4.red(e instanceof Error ? e.message : String(e)));
|
|
32244
32635
|
process.exit(1);
|
|
32245
32636
|
}
|
|
32246
32637
|
});
|
|
@@ -32250,17 +32641,17 @@ projectsPathCmd.command("remove <project-id>").description("Remove the local pat
|
|
|
32250
32641
|
const db = getDatabase();
|
|
32251
32642
|
const resolved = resolvePartialId(db, "projects", projectId);
|
|
32252
32643
|
if (!resolved) {
|
|
32253
|
-
console.error(
|
|
32644
|
+
console.error(chalk4.red(`Project not found: ${projectId}`));
|
|
32254
32645
|
process.exit(1);
|
|
32255
32646
|
}
|
|
32256
32647
|
const removed = removeMachineLocalPath2(resolved, opts.machine);
|
|
32257
32648
|
if (removed) {
|
|
32258
|
-
console.log(
|
|
32649
|
+
console.log(chalk4.green("Machine path override removed."));
|
|
32259
32650
|
} else {
|
|
32260
|
-
console.log(
|
|
32651
|
+
console.log(chalk4.dim("No override found to remove."));
|
|
32261
32652
|
}
|
|
32262
32653
|
} catch (e) {
|
|
32263
|
-
console.error(
|
|
32654
|
+
console.error(chalk4.red(e instanceof Error ? e.message : String(e)));
|
|
32264
32655
|
process.exit(1);
|
|
32265
32656
|
}
|
|
32266
32657
|
});
|
|
@@ -32274,7 +32665,7 @@ program2.command("extract <path>").description("Extract TODO/FIXME/HACK/BUG/XXX/
|
|
|
32274
32665
|
const db = getDatabase();
|
|
32275
32666
|
const id = resolvePartialId(db, "task_lists", opts.list);
|
|
32276
32667
|
if (!id) {
|
|
32277
|
-
console.error(
|
|
32668
|
+
console.error(chalk4.red(`Could not resolve task list ID: ${opts.list}`));
|
|
32278
32669
|
process.exit(1);
|
|
32279
32670
|
}
|
|
32280
32671
|
return id;
|
|
@@ -32293,18 +32684,18 @@ program2.command("extract <path>").description("Extract TODO/FIXME/HACK/BUG/XXX/
|
|
|
32293
32684
|
if (globalOpts.json) {
|
|
32294
32685
|
console.log(JSON.stringify(opts.dryRun ? { comments: result.comments } : { tasks_created: result.tasks.length, skipped: result.skipped, comments: result.comments.length }, null, 2));
|
|
32295
32686
|
} else if (opts.dryRun) {
|
|
32296
|
-
console.log(
|
|
32687
|
+
console.log(chalk4.cyan(`Found ${result.comments.length} comment(s):
|
|
32297
32688
|
`));
|
|
32298
32689
|
for (const c of result.comments) {
|
|
32299
|
-
console.log(` ${
|
|
32300
|
-
console.log(` ${
|
|
32690
|
+
console.log(` ${chalk4.yellow(`[${c.tag}]`)} ${c.message}`);
|
|
32691
|
+
console.log(` ${chalk4.gray(`${c.file}:${c.line}`)}`);
|
|
32301
32692
|
}
|
|
32302
32693
|
} else {
|
|
32303
|
-
console.log(
|
|
32694
|
+
console.log(chalk4.green(`Created ${result.tasks.length} task(s)`));
|
|
32304
32695
|
if (result.skipped > 0) {
|
|
32305
|
-
console.log(
|
|
32696
|
+
console.log(chalk4.gray(`Skipped ${result.skipped} duplicate(s)`));
|
|
32306
32697
|
}
|
|
32307
|
-
console.log(
|
|
32698
|
+
console.log(chalk4.gray(`Total comments found: ${result.comments.length}`));
|
|
32308
32699
|
for (const t of result.tasks) {
|
|
32309
32700
|
console.log(formatTaskLine(t));
|
|
32310
32701
|
}
|
|
@@ -32354,7 +32745,7 @@ program2.command("sync").description("Sync tasks with an agent task list (Claude
|
|
|
32354
32745
|
const agent = opts.agent || "claude";
|
|
32355
32746
|
const taskListId = resolveTaskListId(agent, opts.taskList, project?.task_list_id);
|
|
32356
32747
|
if (!taskListId) {
|
|
32357
|
-
console.error(
|
|
32748
|
+
console.error(chalk4.red(`Could not detect task list ID for ${agent}. Use --task-list <id> or set appropriate env vars.`));
|
|
32358
32749
|
process.exit(1);
|
|
32359
32750
|
}
|
|
32360
32751
|
result = syncWithAgent(agent, taskListId, projectId, direction, { prefer });
|
|
@@ -32364,14 +32755,14 @@ program2.command("sync").description("Sync tasks with an agent task list (Claude
|
|
|
32364
32755
|
return;
|
|
32365
32756
|
}
|
|
32366
32757
|
if (result.pulled > 0)
|
|
32367
|
-
console.log(
|
|
32758
|
+
console.log(chalk4.green(`Pulled ${result.pulled} task(s).`));
|
|
32368
32759
|
if (result.pushed > 0)
|
|
32369
|
-
console.log(
|
|
32760
|
+
console.log(chalk4.green(`Pushed ${result.pushed} task(s).`));
|
|
32370
32761
|
if (result.pulled === 0 && result.pushed === 0 && result.errors.length === 0) {
|
|
32371
|
-
console.log(
|
|
32762
|
+
console.log(chalk4.dim("Nothing to sync."));
|
|
32372
32763
|
}
|
|
32373
32764
|
for (const err of result.errors) {
|
|
32374
|
-
console.error(
|
|
32765
|
+
console.error(chalk4.red(` Error: ${err}`));
|
|
32375
32766
|
}
|
|
32376
32767
|
});
|
|
32377
32768
|
var hooks = program2.command("hooks").description("Manage Claude Code hook integration");
|
|
@@ -32411,7 +32802,7 @@ exit 0
|
|
|
32411
32802
|
const hookPath = join13(hooksDir, "todos-sync.sh");
|
|
32412
32803
|
writeFileSync6(hookPath, hookScript);
|
|
32413
32804
|
execSync(`chmod +x "${hookPath}"`);
|
|
32414
|
-
console.log(
|
|
32805
|
+
console.log(chalk4.green(`Hook script created: ${hookPath}`));
|
|
32415
32806
|
const settingsPath = join13(process.cwd(), ".claude", "settings.json");
|
|
32416
32807
|
const settings = readJsonFile2(settingsPath);
|
|
32417
32808
|
if (!settings["hooks"]) {
|
|
@@ -32438,8 +32829,8 @@ exit 0
|
|
|
32438
32829
|
});
|
|
32439
32830
|
hooksConfig["PostToolUse"] = filtered;
|
|
32440
32831
|
writeJsonFile2(settingsPath, settings);
|
|
32441
|
-
console.log(
|
|
32442
|
-
console.log(
|
|
32832
|
+
console.log(chalk4.green(`Claude Code hooks configured in: ${settingsPath}`));
|
|
32833
|
+
console.log(chalk4.dim("Task list ID auto-detected from project."));
|
|
32443
32834
|
});
|
|
32444
32835
|
program2.command("mcp").description("Start MCP server (stdio)").option("--register <agent>", "Register MCP server with an agent (claude, codex, gemini, all)").option("--unregister <agent>", "Unregister MCP server from an agent (claude, codex, gemini, all)").option("-g, --global", "Register/unregister globally (user-level) instead of project-level").action(async (opts) => {
|
|
32445
32836
|
if (opts.register) {
|
|
@@ -32497,20 +32888,20 @@ function registerClaude(binPath, global2) {
|
|
|
32497
32888
|
try {
|
|
32498
32889
|
const { execSync: execSync2 } = __require("child_process");
|
|
32499
32890
|
execSync2(cmd, { stdio: "pipe" });
|
|
32500
|
-
console.log(
|
|
32891
|
+
console.log(chalk4.green(`Claude Code (${scope}): registered via 'claude mcp add'`));
|
|
32501
32892
|
} catch {
|
|
32502
|
-
console.log(
|
|
32503
|
-
console.log(
|
|
32893
|
+
console.log(chalk4.yellow(`Claude Code: could not auto-register. Run this command manually:`));
|
|
32894
|
+
console.log(chalk4.cyan(` ${cmd}`));
|
|
32504
32895
|
}
|
|
32505
32896
|
}
|
|
32506
32897
|
function unregisterClaude(_global) {
|
|
32507
32898
|
try {
|
|
32508
32899
|
const { execSync: execSync2 } = __require("child_process");
|
|
32509
32900
|
execSync2("claude mcp remove todos", { stdio: "pipe" });
|
|
32510
|
-
console.log(
|
|
32901
|
+
console.log(chalk4.green(`Claude Code: removed todos MCP server`));
|
|
32511
32902
|
} catch {
|
|
32512
|
-
console.log(
|
|
32513
|
-
console.log(
|
|
32903
|
+
console.log(chalk4.yellow(`Claude Code: could not auto-remove. Run manually:`));
|
|
32904
|
+
console.log(chalk4.cyan(" claude mcp remove todos"));
|
|
32514
32905
|
}
|
|
32515
32906
|
}
|
|
32516
32907
|
function registerCodex(binPath) {
|
|
@@ -32525,19 +32916,19 @@ args = []
|
|
|
32525
32916
|
content = content.trimEnd() + `
|
|
32526
32917
|
` + block;
|
|
32527
32918
|
writeTomlFile(configPath, content);
|
|
32528
|
-
console.log(
|
|
32919
|
+
console.log(chalk4.green(`Codex CLI: registered in ${configPath}`));
|
|
32529
32920
|
}
|
|
32530
32921
|
function unregisterCodex() {
|
|
32531
32922
|
const configPath = join13(HOME2, ".codex", "config.toml");
|
|
32532
32923
|
let content = readTomlFile(configPath);
|
|
32533
32924
|
if (!content.includes("[mcp_servers.todos]")) {
|
|
32534
|
-
console.log(
|
|
32925
|
+
console.log(chalk4.dim(`Codex CLI: todos not found in ${configPath}`));
|
|
32535
32926
|
return;
|
|
32536
32927
|
}
|
|
32537
32928
|
content = removeTomlBlock(content, "mcp_servers.todos");
|
|
32538
32929
|
writeTomlFile(configPath, content.trimEnd() + `
|
|
32539
32930
|
`);
|
|
32540
|
-
console.log(
|
|
32931
|
+
console.log(chalk4.green(`Codex CLI: unregistered from ${configPath}`));
|
|
32541
32932
|
}
|
|
32542
32933
|
function removeTomlBlock(content, blockName) {
|
|
32543
32934
|
const lines = content.split(`
|
|
@@ -32572,19 +32963,19 @@ function registerGemini(binPath) {
|
|
|
32572
32963
|
args: []
|
|
32573
32964
|
};
|
|
32574
32965
|
writeJsonFile2(configPath, config);
|
|
32575
|
-
console.log(
|
|
32966
|
+
console.log(chalk4.green(`Gemini CLI: registered in ${configPath}`));
|
|
32576
32967
|
}
|
|
32577
32968
|
function unregisterGemini() {
|
|
32578
32969
|
const configPath = join13(HOME2, ".gemini", "settings.json");
|
|
32579
32970
|
const config = readJsonFile2(configPath);
|
|
32580
32971
|
const servers = config["mcpServers"];
|
|
32581
32972
|
if (!servers || !("todos" in servers)) {
|
|
32582
|
-
console.log(
|
|
32973
|
+
console.log(chalk4.dim(`Gemini CLI: todos not found in ${configPath}`));
|
|
32583
32974
|
return;
|
|
32584
32975
|
}
|
|
32585
32976
|
delete servers["todos"];
|
|
32586
32977
|
writeJsonFile2(configPath, config);
|
|
32587
|
-
console.log(
|
|
32978
|
+
console.log(chalk4.green(`Gemini CLI: unregistered from ${configPath}`));
|
|
32588
32979
|
}
|
|
32589
32980
|
function registerMcp(agent, global2) {
|
|
32590
32981
|
const agents = agent === "all" ? ["claude", "codex", "gemini"] : [agent];
|
|
@@ -32601,7 +32992,7 @@ function registerMcp(agent, global2) {
|
|
|
32601
32992
|
registerGemini(binPath);
|
|
32602
32993
|
break;
|
|
32603
32994
|
default:
|
|
32604
|
-
console.error(
|
|
32995
|
+
console.error(chalk4.red(`Unknown agent: ${a}. Use: claude, codex, gemini, all`));
|
|
32605
32996
|
}
|
|
32606
32997
|
}
|
|
32607
32998
|
}
|
|
@@ -32619,7 +33010,7 @@ function unregisterMcp(agent, global2) {
|
|
|
32619
33010
|
unregisterGemini();
|
|
32620
33011
|
break;
|
|
32621
33012
|
default:
|
|
32622
|
-
console.error(
|
|
33013
|
+
console.error(chalk4.red(`Unknown agent: ${a}. Use: claude, codex, gemini, all`));
|
|
32623
33014
|
}
|
|
32624
33015
|
}
|
|
32625
33016
|
}
|
|
@@ -32628,7 +33019,7 @@ program2.command("import <url>").description("Import a GitHub issue as a task").
|
|
|
32628
33019
|
const { parseGitHubUrl: parseGitHubUrl2, fetchGitHubIssue: fetchGitHubIssue2, issueToTask: issueToTask2 } = (init_github(), __toCommonJS(exports_github));
|
|
32629
33020
|
const parsed = parseGitHubUrl2(url);
|
|
32630
33021
|
if (!parsed) {
|
|
32631
|
-
console.error(
|
|
33022
|
+
console.error(chalk4.red("Invalid GitHub issue URL. Expected: https://github.com/owner/repo/issues/123"));
|
|
32632
33023
|
process.exit(1);
|
|
32633
33024
|
}
|
|
32634
33025
|
try {
|
|
@@ -32640,15 +33031,15 @@ program2.command("import <url>").description("Import a GitHub issue as a task").
|
|
|
32640
33031
|
output(task, true);
|
|
32641
33032
|
return;
|
|
32642
33033
|
}
|
|
32643
|
-
console.log(
|
|
32644
|
-
console.log(` ${
|
|
32645
|
-
console.log(` ${
|
|
32646
|
-
console.log(` ${
|
|
33034
|
+
console.log(chalk4.green(`Imported GH#${issue.number}: ${issue.title}`));
|
|
33035
|
+
console.log(` ${chalk4.dim("Task ID:")} ${task.short_id || task.id}`);
|
|
33036
|
+
console.log(` ${chalk4.dim("Labels:")} ${issue.labels.join(", ") || "none"}`);
|
|
33037
|
+
console.log(` ${chalk4.dim("Priority:")} ${task.priority}`);
|
|
32647
33038
|
} catch (e) {
|
|
32648
33039
|
if (e.message?.includes("gh")) {
|
|
32649
|
-
console.error(
|
|
33040
|
+
console.error(chalk4.red("GitHub CLI (gh) not found or not authenticated. Install: https://cli.github.com"));
|
|
32650
33041
|
} else {
|
|
32651
|
-
console.error(
|
|
33042
|
+
console.error(chalk4.red(`Import failed: ${e.message}`));
|
|
32652
33043
|
}
|
|
32653
33044
|
process.exit(1);
|
|
32654
33045
|
}
|
|
@@ -32668,7 +33059,7 @@ program2.command("link-commit <task-id> <sha>").description("Link a git commit t
|
|
|
32668
33059
|
output(commit, true);
|
|
32669
33060
|
return;
|
|
32670
33061
|
}
|
|
32671
|
-
console.log(
|
|
33062
|
+
console.log(chalk4.green(`Linked commit ${sha.slice(0, 7)} to task ${taskId}`));
|
|
32672
33063
|
});
|
|
32673
33064
|
var hookCmd = program2.command("hook").description("Manage git hooks for auto-linking commits to tasks");
|
|
32674
33065
|
hookCmd.command("install").description("Install post-commit hook that auto-links commits to tasks").action(() => {
|
|
@@ -32681,7 +33072,7 @@ hookCmd.command("install").description("Install post-commit hook that auto-links
|
|
|
32681
33072
|
if (existsSync11(hookPath)) {
|
|
32682
33073
|
const existing = readFileSync8(hookPath, "utf-8");
|
|
32683
33074
|
if (existing.includes(marker)) {
|
|
32684
|
-
console.log(
|
|
33075
|
+
console.log(chalk4.yellow("Hook already installed."));
|
|
32685
33076
|
return;
|
|
32686
33077
|
}
|
|
32687
33078
|
writeFileSync7(hookPath, existing + `
|
|
@@ -32695,9 +33086,9 @@ $(dirname "$0")/../../scripts/post-commit-hook.sh
|
|
|
32695
33086
|
`);
|
|
32696
33087
|
chmodSync(hookPath, 493);
|
|
32697
33088
|
}
|
|
32698
|
-
console.log(
|
|
33089
|
+
console.log(chalk4.green("Post-commit hook installed. Commits with task IDs (e.g. OPE-00042) will auto-link."));
|
|
32699
33090
|
} catch (e) {
|
|
32700
|
-
console.error(
|
|
33091
|
+
console.error(chalk4.red("Not in a git repository or hook install failed."));
|
|
32701
33092
|
process.exit(1);
|
|
32702
33093
|
}
|
|
32703
33094
|
});
|
|
@@ -32709,12 +33100,12 @@ hookCmd.command("uninstall").description("Remove the todos post-commit hook").ac
|
|
|
32709
33100
|
const { existsSync: existsSync11, readFileSync: readFileSync8, writeFileSync: writeFileSync7 } = __require("fs");
|
|
32710
33101
|
const marker = "# todos-auto-link";
|
|
32711
33102
|
if (!existsSync11(hookPath)) {
|
|
32712
|
-
console.log(
|
|
33103
|
+
console.log(chalk4.dim("No post-commit hook found."));
|
|
32713
33104
|
return;
|
|
32714
33105
|
}
|
|
32715
33106
|
const content = readFileSync8(hookPath, "utf-8");
|
|
32716
33107
|
if (!content.includes(marker)) {
|
|
32717
|
-
console.log(
|
|
33108
|
+
console.log(chalk4.dim("Hook not managed by todos."));
|
|
32718
33109
|
return;
|
|
32719
33110
|
}
|
|
32720
33111
|
const cleaned = content.split(`
|
|
@@ -32726,9 +33117,9 @@ hookCmd.command("uninstall").description("Remove the todos post-commit hook").ac
|
|
|
32726
33117
|
writeFileSync7(hookPath, cleaned + `
|
|
32727
33118
|
`);
|
|
32728
33119
|
}
|
|
32729
|
-
console.log(
|
|
33120
|
+
console.log(chalk4.green("Post-commit hook removed."));
|
|
32730
33121
|
} catch (e) {
|
|
32731
|
-
console.error(
|
|
33122
|
+
console.error(chalk4.red("Not in a git repository or hook removal failed."));
|
|
32732
33123
|
process.exit(1);
|
|
32733
33124
|
}
|
|
32734
33125
|
});
|
|
@@ -32737,17 +33128,17 @@ program2.command("init <name>").description("Register an agent and get a short U
|
|
|
32737
33128
|
try {
|
|
32738
33129
|
const result = registerAgent({ name, description: opts.description });
|
|
32739
33130
|
if (isAgentConflict(result)) {
|
|
32740
|
-
console.error(
|
|
33131
|
+
console.error(chalk4.red("CONFLICT:"), result.message);
|
|
32741
33132
|
process.exit(1);
|
|
32742
33133
|
}
|
|
32743
33134
|
if (globalOpts.json) {
|
|
32744
33135
|
output(result, true);
|
|
32745
33136
|
} else {
|
|
32746
|
-
console.log(
|
|
32747
|
-
console.log(` ${
|
|
32748
|
-
console.log(` ${
|
|
33137
|
+
console.log(chalk4.green("Agent registered:"));
|
|
33138
|
+
console.log(` ${chalk4.dim("ID:")} ${result.id}`);
|
|
33139
|
+
console.log(` ${chalk4.dim("Name:")} ${result.name}`);
|
|
32749
33140
|
console.log(`
|
|
32750
|
-
Use ${
|
|
33141
|
+
Use ${chalk4.cyan(`--agent ${result.id}`)} on future commands.`);
|
|
32751
33142
|
}
|
|
32752
33143
|
} catch (e) {
|
|
32753
33144
|
handleError(e);
|
|
@@ -32757,51 +33148,51 @@ program2.command("heartbeat [agent]").description("Update last_seen_at to signal
|
|
|
32757
33148
|
const globalOpts = program2.opts();
|
|
32758
33149
|
const agentId = agent || globalOpts.agent;
|
|
32759
33150
|
if (!agentId) {
|
|
32760
|
-
console.error(
|
|
33151
|
+
console.error(chalk4.red("Agent ID required. Use --agent or pass as argument."));
|
|
32761
33152
|
process.exit(1);
|
|
32762
33153
|
}
|
|
32763
33154
|
const { updateAgentActivity: updateAgentActivity2, getAgent: getAgent2 } = (init_agents(), __toCommonJS(exports_agents));
|
|
32764
33155
|
const a = getAgent2(agentId) || (init_agents(), __toCommonJS(exports_agents)).getAgentByName(agentId);
|
|
32765
33156
|
if (!a) {
|
|
32766
|
-
console.error(
|
|
33157
|
+
console.error(chalk4.red(`Agent not found: ${agentId}`));
|
|
32767
33158
|
process.exit(1);
|
|
32768
33159
|
}
|
|
32769
33160
|
updateAgentActivity2(a.id);
|
|
32770
33161
|
if (globalOpts.json) {
|
|
32771
33162
|
console.log(JSON.stringify({ agent_id: a.id, name: a.name, last_seen_at: new Date().toISOString() }));
|
|
32772
33163
|
} else {
|
|
32773
|
-
console.log(
|
|
33164
|
+
console.log(chalk4.green(`\u2665 ${a.name} (${a.id.slice(0, 8)}) \u2014 heartbeat sent`));
|
|
32774
33165
|
}
|
|
32775
33166
|
});
|
|
32776
33167
|
program2.command("release [agent]").description("Release/logout an agent \u2014 clears session binding so the name is immediately available").option("--session-id <id>", "Only release if session ID matches").action((agent, opts) => {
|
|
32777
33168
|
const globalOpts = program2.opts();
|
|
32778
33169
|
const agentId = agent || globalOpts.agent;
|
|
32779
33170
|
if (!agentId) {
|
|
32780
|
-
console.error(
|
|
33171
|
+
console.error(chalk4.red("Agent ID or name required. Use --agent or pass as argument."));
|
|
32781
33172
|
process.exit(1);
|
|
32782
33173
|
}
|
|
32783
33174
|
const { getAgent: getAgent2, getAgentByName: getAgentByName2 } = (init_agents(), __toCommonJS(exports_agents));
|
|
32784
33175
|
const a = getAgent2(agentId) || getAgentByName2(agentId);
|
|
32785
33176
|
if (!a) {
|
|
32786
|
-
console.error(
|
|
33177
|
+
console.error(chalk4.red(`Agent not found: ${agentId}`));
|
|
32787
33178
|
process.exit(1);
|
|
32788
33179
|
}
|
|
32789
33180
|
const released = releaseAgent(a.id, opts?.sessionId);
|
|
32790
33181
|
if (!released) {
|
|
32791
|
-
console.error(
|
|
33182
|
+
console.error(chalk4.red("Release denied: session_id does not match agent's current session."));
|
|
32792
33183
|
process.exit(1);
|
|
32793
33184
|
}
|
|
32794
33185
|
if (globalOpts.json) {
|
|
32795
33186
|
console.log(JSON.stringify({ agent_id: a.id, name: a.name, released: true }));
|
|
32796
33187
|
} else {
|
|
32797
|
-
console.log(
|
|
33188
|
+
console.log(chalk4.green(`\u2713 ${a.name} (${a.id}) released \u2014 name is now available.`));
|
|
32798
33189
|
}
|
|
32799
33190
|
});
|
|
32800
33191
|
program2.command("focus [project]").description("Focus on a project (or clear focus if no project given)").action((project) => {
|
|
32801
33192
|
const globalOpts = program2.opts();
|
|
32802
33193
|
const agentId = globalOpts.agent;
|
|
32803
33194
|
if (!agentId) {
|
|
32804
|
-
console.error(
|
|
33195
|
+
console.error(chalk4.red("Agent ID required. Use --agent."));
|
|
32805
33196
|
process.exit(1);
|
|
32806
33197
|
}
|
|
32807
33198
|
const db = getDatabase();
|
|
@@ -32813,10 +33204,10 @@ program2.command("focus [project]").description("Focus on a project (or clear fo
|
|
|
32813
33204
|
})() || db.query("SELECT * FROM projects WHERE name = ? OR task_list_id = ?").get(project, project);
|
|
32814
33205
|
const projectId = p?.id || project;
|
|
32815
33206
|
db.run("UPDATE agents SET active_project_id = ? WHERE id = ? OR name = ?", [projectId, agentId, agentId]);
|
|
32816
|
-
console.log(
|
|
33207
|
+
console.log(chalk4.green(`Focused on: ${p?.name || projectId}`));
|
|
32817
33208
|
} else {
|
|
32818
33209
|
db.run("UPDATE agents SET active_project_id = NULL WHERE id = ? OR name = ?", [agentId, agentId]);
|
|
32819
|
-
console.log(
|
|
33210
|
+
console.log(chalk4.dim("Focus cleared."));
|
|
32820
33211
|
}
|
|
32821
33212
|
});
|
|
32822
33213
|
program2.command("agents").description("List registered agents").action(() => {
|
|
@@ -32828,11 +33219,11 @@ program2.command("agents").description("List registered agents").action(() => {
|
|
|
32828
33219
|
return;
|
|
32829
33220
|
}
|
|
32830
33221
|
if (agents.length === 0) {
|
|
32831
|
-
console.log(
|
|
33222
|
+
console.log(chalk4.dim("No agents registered. Use 'todos init <name>' to register."));
|
|
32832
33223
|
return;
|
|
32833
33224
|
}
|
|
32834
33225
|
for (const a of agents) {
|
|
32835
|
-
console.log(` ${
|
|
33226
|
+
console.log(` ${chalk4.cyan(a.id)} ${chalk4.bold(a.name)} ${chalk4.dim(a.last_seen_at)}`);
|
|
32836
33227
|
}
|
|
32837
33228
|
} catch (e) {
|
|
32838
33229
|
handleError(e);
|
|
@@ -32844,7 +33235,7 @@ program2.command("agent-update <name>").alias("agents-update").description("Upda
|
|
|
32844
33235
|
const { getAgentByName: findByName, updateAgent: doUpdate } = (init_agents(), __toCommonJS(exports_agents));
|
|
32845
33236
|
const agent = findByName(name);
|
|
32846
33237
|
if (!agent) {
|
|
32847
|
-
console.error(
|
|
33238
|
+
console.error(chalk4.red(`Agent not found: ${name}`));
|
|
32848
33239
|
process.exit(1);
|
|
32849
33240
|
}
|
|
32850
33241
|
const updates = {};
|
|
@@ -32858,11 +33249,11 @@ program2.command("agent-update <name>").alias("agents-update").description("Upda
|
|
|
32858
33249
|
if (globalOpts.json) {
|
|
32859
33250
|
output(updated, true);
|
|
32860
33251
|
} else {
|
|
32861
|
-
console.log(
|
|
33252
|
+
console.log(chalk4.green(`Updated agent: ${updated.name} (${updated.id.slice(0, 8)})`));
|
|
32862
33253
|
if (updated.description)
|
|
32863
|
-
console.log(
|
|
33254
|
+
console.log(chalk4.dim(` Description: ${updated.description}`));
|
|
32864
33255
|
if (updated.role)
|
|
32865
|
-
console.log(
|
|
33256
|
+
console.log(chalk4.dim(` Role: ${updated.role}`));
|
|
32866
33257
|
}
|
|
32867
33258
|
} catch (e) {
|
|
32868
33259
|
handleError(e);
|
|
@@ -32873,7 +33264,7 @@ program2.command("agent <name>").description("Show all info about an agent: task
|
|
|
32873
33264
|
const { getAgentByName: findByName } = (init_agents(), __toCommonJS(exports_agents));
|
|
32874
33265
|
const agent = findByName(name);
|
|
32875
33266
|
if (!agent) {
|
|
32876
|
-
console.error(
|
|
33267
|
+
console.error(chalk4.red(`Agent not found: ${name}`));
|
|
32877
33268
|
process.exit(1);
|
|
32878
33269
|
}
|
|
32879
33270
|
const byAssigned = listTasks({ assigned_to: agent.name });
|
|
@@ -32892,53 +33283,53 @@ program2.command("agent <name>").description("Show all info about an agent: task
|
|
|
32892
33283
|
const rate = allTasks.length > 0 ? Math.round(completed.length / allTasks.length * 100) : 0;
|
|
32893
33284
|
const lastSeenMs = Date.now() - new Date(agent.last_seen_at).getTime();
|
|
32894
33285
|
const lastSeenMins = Math.floor(lastSeenMs / 60000);
|
|
32895
|
-
const lastSeenStr = lastSeenMins < 2 ?
|
|
33286
|
+
const lastSeenStr = lastSeenMins < 2 ? chalk4.green("just now") : lastSeenMins < 60 ? chalk4.yellow(`${lastSeenMins}m ago`) : lastSeenMins < 1440 ? chalk4.yellow(`${Math.floor(lastSeenMins / 60)}h ago`) : chalk4.dim(`${Math.floor(lastSeenMins / 1440)}d ago`);
|
|
32896
33287
|
const isOnline = lastSeenMins < 5;
|
|
32897
33288
|
if (opts.json || globalOpts.json) {
|
|
32898
33289
|
console.log(JSON.stringify({ agent, tasks: { pending: pending.length, in_progress: inProgress.length, completed: completed.length, failed: failed.length, total: allTasks.length, completion_rate: rate }, all_tasks: allTasks }, null, 2));
|
|
32899
33290
|
return;
|
|
32900
33291
|
}
|
|
32901
33292
|
console.log(`
|
|
32902
|
-
${isOnline ?
|
|
33293
|
+
${isOnline ? chalk4.green("\u25CF") : chalk4.dim("\u25CB")} ${chalk4.bold(agent.name)} ${chalk4.dim(`(${agent.id})`)} ${lastSeenStr}`);
|
|
32903
33294
|
if (agent.description)
|
|
32904
|
-
console.log(
|
|
33295
|
+
console.log(chalk4.dim(` ${agent.description}`));
|
|
32905
33296
|
if (agent.role)
|
|
32906
|
-
console.log(
|
|
33297
|
+
console.log(chalk4.dim(` Role: ${agent.role}`));
|
|
32907
33298
|
console.log();
|
|
32908
|
-
console.log(` ${
|
|
33299
|
+
console.log(` ${chalk4.yellow(String(pending.length))} pending ${chalk4.blue(String(inProgress.length))} active ${chalk4.green(String(completed.length))} done ${chalk4.dim(`${rate}% rate`)}`);
|
|
32909
33300
|
console.log();
|
|
32910
33301
|
if (inProgress.length > 0) {
|
|
32911
|
-
console.log(
|
|
33302
|
+
console.log(chalk4.bold(" In progress:"));
|
|
32912
33303
|
for (const t of inProgress) {
|
|
32913
33304
|
const id = t.short_id || t.id.slice(0, 8);
|
|
32914
|
-
const staleFlag = new Date(t.updated_at).getTime() < Date.now() - 30 * 60 * 1000 ?
|
|
32915
|
-
console.log(` ${
|
|
33305
|
+
const staleFlag = new Date(t.updated_at).getTime() < Date.now() - 30 * 60 * 1000 ? chalk4.red(" [stale]") : "";
|
|
33306
|
+
console.log(` ${chalk4.cyan(id)} ${chalk4.yellow(t.priority)} ${t.title}${staleFlag}`);
|
|
32916
33307
|
}
|
|
32917
33308
|
console.log();
|
|
32918
33309
|
}
|
|
32919
33310
|
if (pending.length > 0) {
|
|
32920
|
-
console.log(
|
|
33311
|
+
console.log(chalk4.bold(` Pending (${pending.length}):`));
|
|
32921
33312
|
for (const t of pending.slice(0, 5)) {
|
|
32922
33313
|
const id = t.short_id || t.id.slice(0, 8);
|
|
32923
|
-
const due = t.due_at ?
|
|
32924
|
-
console.log(` ${
|
|
33314
|
+
const due = t.due_at ? chalk4.dim(` due:${t.due_at.slice(0, 10)}`) : "";
|
|
33315
|
+
console.log(` ${chalk4.dim(id)} ${t.priority.padEnd(8)} ${t.title}${due}`);
|
|
32925
33316
|
}
|
|
32926
33317
|
if (pending.length > 5)
|
|
32927
|
-
console.log(
|
|
33318
|
+
console.log(chalk4.dim(` ... and ${pending.length - 5} more`));
|
|
32928
33319
|
console.log();
|
|
32929
33320
|
}
|
|
32930
33321
|
const recentDone = completed.filter((t) => t.completed_at).sort((a, b) => new Date(b.completed_at).getTime() - new Date(a.completed_at).getTime()).slice(0, 3);
|
|
32931
33322
|
if (recentDone.length > 0) {
|
|
32932
|
-
console.log(
|
|
33323
|
+
console.log(chalk4.bold(" Recently completed:"));
|
|
32933
33324
|
for (const t of recentDone) {
|
|
32934
33325
|
const id = t.short_id || t.id.slice(0, 8);
|
|
32935
|
-
const when = t.completed_at ?
|
|
32936
|
-
console.log(` ${
|
|
33326
|
+
const when = t.completed_at ? chalk4.dim(new Date(t.completed_at).toLocaleDateString()) : "";
|
|
33327
|
+
console.log(` ${chalk4.green("\u2713")} ${chalk4.dim(id)} ${t.title} ${when}`);
|
|
32937
33328
|
}
|
|
32938
33329
|
console.log();
|
|
32939
33330
|
}
|
|
32940
33331
|
if (allTasks.length === 0) {
|
|
32941
|
-
console.log(
|
|
33332
|
+
console.log(chalk4.dim(" No tasks assigned to this agent."));
|
|
32942
33333
|
}
|
|
32943
33334
|
});
|
|
32944
33335
|
program2.command("org").description("Show agent org chart \u2014 who reports to who").option("--set <agent=manager>", "Set reporting: 'seneca=julius' or 'seneca=' to clear").action((opts) => {
|
|
@@ -32948,14 +33339,14 @@ program2.command("org").description("Show agent org chart \u2014 who reports to
|
|
|
32948
33339
|
const [agentName, managerName] = opts.set.split("=");
|
|
32949
33340
|
const agent = getByName(agentName);
|
|
32950
33341
|
if (!agent) {
|
|
32951
|
-
console.error(
|
|
33342
|
+
console.error(chalk4.red(`Agent not found: ${agentName}`));
|
|
32952
33343
|
process.exit(1);
|
|
32953
33344
|
}
|
|
32954
33345
|
let managerId = null;
|
|
32955
33346
|
if (managerName) {
|
|
32956
33347
|
const manager = getByName(managerName);
|
|
32957
33348
|
if (!manager) {
|
|
32958
|
-
console.error(
|
|
33349
|
+
console.error(chalk4.red(`Manager not found: ${managerName}`));
|
|
32959
33350
|
process.exit(1);
|
|
32960
33351
|
}
|
|
32961
33352
|
managerId = manager.id;
|
|
@@ -32964,7 +33355,7 @@ program2.command("org").description("Show agent org chart \u2014 who reports to
|
|
|
32964
33355
|
if (globalOpts.json) {
|
|
32965
33356
|
output({ agent: agentName, reports_to: managerName || null }, true);
|
|
32966
33357
|
} else {
|
|
32967
|
-
console.log(
|
|
33358
|
+
console.log(chalk4.green(managerId ? `${agentName} \u2192 ${managerName}` : `${agentName} \u2192 (top-level)`));
|
|
32968
33359
|
}
|
|
32969
33360
|
return;
|
|
32970
33361
|
}
|
|
@@ -32974,15 +33365,15 @@ program2.command("org").description("Show agent org chart \u2014 who reports to
|
|
|
32974
33365
|
return;
|
|
32975
33366
|
}
|
|
32976
33367
|
if (tree.length === 0) {
|
|
32977
|
-
console.log(
|
|
33368
|
+
console.log(chalk4.dim("No agents registered."));
|
|
32978
33369
|
return;
|
|
32979
33370
|
}
|
|
32980
33371
|
function render2(nodes, indent = 0) {
|
|
32981
33372
|
for (const n of nodes) {
|
|
32982
33373
|
const prefix = " ".repeat(indent);
|
|
32983
|
-
const title = n.agent.title ?
|
|
32984
|
-
const level = n.agent.level ?
|
|
32985
|
-
console.log(`${prefix}${indent > 0 ? "\u251C\u2500\u2500 " : ""}${
|
|
33374
|
+
const title = n.agent.title ? chalk4.cyan(` \u2014 ${n.agent.title}`) : "";
|
|
33375
|
+
const level = n.agent.level ? chalk4.dim(` (${n.agent.level})`) : "";
|
|
33376
|
+
console.log(`${prefix}${indent > 0 ? "\u251C\u2500\u2500 " : ""}${chalk4.bold(n.agent.name)}${title}${level}`);
|
|
32986
33377
|
render2(n.reports, indent + 1);
|
|
32987
33378
|
}
|
|
32988
33379
|
}
|
|
@@ -32998,21 +33389,21 @@ program2.command("lists").aliases(["task-lists", "tl"]).description("List and ma
|
|
|
32998
33389
|
output(list, true);
|
|
32999
33390
|
return;
|
|
33000
33391
|
}
|
|
33001
|
-
console.log(
|
|
33002
|
-
console.log(` ${
|
|
33003
|
-
console.log(` ${
|
|
33004
|
-
console.log(` ${
|
|
33392
|
+
console.log(chalk4.green("Task list created:"));
|
|
33393
|
+
console.log(` ${chalk4.dim("ID:")} ${list.id.slice(0, 8)}`);
|
|
33394
|
+
console.log(` ${chalk4.dim("Slug:")} ${list.slug}`);
|
|
33395
|
+
console.log(` ${chalk4.dim("Name:")} ${list.name}`);
|
|
33005
33396
|
return;
|
|
33006
33397
|
}
|
|
33007
33398
|
if (opts.delete) {
|
|
33008
33399
|
const db = getDatabase();
|
|
33009
33400
|
const resolved = resolvePartialId(db, "task_lists", opts.delete);
|
|
33010
33401
|
if (!resolved) {
|
|
33011
|
-
console.error(
|
|
33402
|
+
console.error(chalk4.red("Task list not found"));
|
|
33012
33403
|
process.exit(1);
|
|
33013
33404
|
}
|
|
33014
33405
|
deleteTaskList(resolved);
|
|
33015
|
-
console.log(
|
|
33406
|
+
console.log(chalk4.green("Task list deleted."));
|
|
33016
33407
|
return;
|
|
33017
33408
|
}
|
|
33018
33409
|
const lists = listTaskLists(projectId);
|
|
@@ -33021,11 +33412,11 @@ program2.command("lists").aliases(["task-lists", "tl"]).description("List and ma
|
|
|
33021
33412
|
return;
|
|
33022
33413
|
}
|
|
33023
33414
|
if (lists.length === 0) {
|
|
33024
|
-
console.log(
|
|
33415
|
+
console.log(chalk4.dim("No task lists. Use 'todos lists --add <name>' to create one."));
|
|
33025
33416
|
return;
|
|
33026
33417
|
}
|
|
33027
33418
|
for (const l of lists) {
|
|
33028
|
-
console.log(` ${
|
|
33419
|
+
console.log(` ${chalk4.dim(l.id.slice(0, 8))} ${chalk4.bold(l.name)} ${chalk4.dim(`(${l.slug})`)}`);
|
|
33029
33420
|
}
|
|
33030
33421
|
} catch (e) {
|
|
33031
33422
|
handleError(e);
|
|
@@ -33036,20 +33427,20 @@ program2.command("upgrade").alias("self-update").description("Update todos to th
|
|
|
33036
33427
|
const currentVersion = getPackageVersion();
|
|
33037
33428
|
const res = await fetch("https://registry.npmjs.org/@hasna/todos/latest");
|
|
33038
33429
|
if (!res.ok) {
|
|
33039
|
-
console.error(
|
|
33430
|
+
console.error(chalk4.red("Failed to check for updates."));
|
|
33040
33431
|
process.exit(1);
|
|
33041
33432
|
}
|
|
33042
33433
|
const data = await res.json();
|
|
33043
33434
|
const latestVersion = data.version;
|
|
33044
|
-
console.log(` Current: ${
|
|
33045
|
-
console.log(` Latest: ${
|
|
33435
|
+
console.log(` Current: ${chalk4.dim(currentVersion)}`);
|
|
33436
|
+
console.log(` Latest: ${chalk4.green(latestVersion)}`);
|
|
33046
33437
|
if (currentVersion === latestVersion) {
|
|
33047
|
-
console.log(
|
|
33438
|
+
console.log(chalk4.green(`
|
|
33048
33439
|
Already up to date!`));
|
|
33049
33440
|
return;
|
|
33050
33441
|
}
|
|
33051
33442
|
if (opts.check) {
|
|
33052
|
-
console.log(
|
|
33443
|
+
console.log(chalk4.yellow(`
|
|
33053
33444
|
Update available: ${currentVersion} \u2192 ${latestVersion}`));
|
|
33054
33445
|
return;
|
|
33055
33446
|
}
|
|
@@ -33059,10 +33450,10 @@ Update available: ${currentVersion} \u2192 ${latestVersion}`));
|
|
|
33059
33450
|
useBun = true;
|
|
33060
33451
|
} catch {}
|
|
33061
33452
|
const cmd = useBun ? "bun add -g @hasna/todos@latest" : "npm install -g @hasna/todos@latest";
|
|
33062
|
-
console.log(
|
|
33453
|
+
console.log(chalk4.dim(`
|
|
33063
33454
|
Running: ${cmd}`));
|
|
33064
33455
|
execSync(cmd, { stdio: "inherit" });
|
|
33065
|
-
console.log(
|
|
33456
|
+
console.log(chalk4.green(`
|
|
33066
33457
|
Updated to ${latestVersion}!`));
|
|
33067
33458
|
} catch (e) {
|
|
33068
33459
|
handleError(e);
|
|
@@ -33084,7 +33475,7 @@ program2.command("config").description("View or update configuration").option("-
|
|
|
33084
33475
|
if (globalOpts.json) {
|
|
33085
33476
|
output({ key: opts.get, value }, true);
|
|
33086
33477
|
} else {
|
|
33087
|
-
console.log(value !== undefined ? JSON.stringify(value, null, 2) :
|
|
33478
|
+
console.log(value !== undefined ? JSON.stringify(value, null, 2) : chalk4.dim("(not set)"));
|
|
33088
33479
|
}
|
|
33089
33480
|
return;
|
|
33090
33481
|
}
|
|
@@ -33116,7 +33507,7 @@ program2.command("config").description("View or update configuration").option("-
|
|
|
33116
33507
|
if (globalOpts.json) {
|
|
33117
33508
|
output({ key, value: parsedValue }, true);
|
|
33118
33509
|
} else {
|
|
33119
|
-
console.log(
|
|
33510
|
+
console.log(chalk4.green(`Set ${key} = ${JSON.stringify(parsedValue)}`));
|
|
33120
33511
|
}
|
|
33121
33512
|
return;
|
|
33122
33513
|
}
|
|
@@ -33157,25 +33548,25 @@ program2.command("watch").description("Live-updating task list (refreshes every
|
|
|
33157
33548
|
counts[t.status] = (counts[t.status] || 0) + 1;
|
|
33158
33549
|
process.stdout.write("\x1B[2J\x1B[0f");
|
|
33159
33550
|
const now2 = new Date().toLocaleTimeString();
|
|
33160
|
-
console.log(
|
|
33551
|
+
console.log(chalk4.bold(`todos watch`) + chalk4.dim(` \u2014 ${now2} \u2014 refreshing every ${opts.interval}s \u2014 Ctrl+C to stop
|
|
33161
33552
|
`));
|
|
33162
33553
|
const parts = [
|
|
33163
|
-
`total: ${
|
|
33164
|
-
`pending: ${
|
|
33165
|
-
`in_progress: ${
|
|
33166
|
-
`completed: ${
|
|
33167
|
-
`failed: ${
|
|
33554
|
+
`total: ${chalk4.bold(String(all.length))}`,
|
|
33555
|
+
`pending: ${chalk4.yellow(String(counts["pending"] || 0))}`,
|
|
33556
|
+
`in_progress: ${chalk4.blue(String(counts["in_progress"] || 0))}`,
|
|
33557
|
+
`completed: ${chalk4.green(String(counts["completed"] || 0))}`,
|
|
33558
|
+
`failed: ${chalk4.red(String(counts["failed"] || 0))}`
|
|
33168
33559
|
];
|
|
33169
33560
|
console.log(parts.join(" ") + `
|
|
33170
33561
|
`);
|
|
33171
33562
|
if (tasks.length === 0) {
|
|
33172
|
-
console.log(
|
|
33563
|
+
console.log(chalk4.dim("No matching tasks."));
|
|
33173
33564
|
return;
|
|
33174
33565
|
}
|
|
33175
33566
|
for (const t of tasks) {
|
|
33176
33567
|
console.log(formatTaskLine(t));
|
|
33177
33568
|
}
|
|
33178
|
-
console.log(
|
|
33569
|
+
console.log(chalk4.dim(`
|
|
33179
33570
|
${tasks.length} task(s) shown`));
|
|
33180
33571
|
}
|
|
33181
33572
|
render2();
|
|
@@ -33195,19 +33586,19 @@ program2.command("stream").description("Subscribe to real-time task events via S
|
|
|
33195
33586
|
params.set("events", opts.events);
|
|
33196
33587
|
const url = `${baseUrl}/api/tasks/stream?${params}`;
|
|
33197
33588
|
const eventColors = {
|
|
33198
|
-
"task.created":
|
|
33199
|
-
"task.started":
|
|
33200
|
-
"task.completed":
|
|
33201
|
-
"task.failed":
|
|
33202
|
-
"task.assigned":
|
|
33203
|
-
"task.status_changed":
|
|
33589
|
+
"task.created": chalk4.blue,
|
|
33590
|
+
"task.started": chalk4.cyan,
|
|
33591
|
+
"task.completed": chalk4.green,
|
|
33592
|
+
"task.failed": chalk4.red,
|
|
33593
|
+
"task.assigned": chalk4.yellow,
|
|
33594
|
+
"task.status_changed": chalk4.magenta
|
|
33204
33595
|
};
|
|
33205
|
-
console.log(
|
|
33596
|
+
console.log(chalk4.dim(`Connecting to ${url} \u2014 Ctrl+C to stop
|
|
33206
33597
|
`));
|
|
33207
33598
|
try {
|
|
33208
33599
|
const resp = await fetch(url);
|
|
33209
33600
|
if (!resp.ok || !resp.body) {
|
|
33210
|
-
console.error(
|
|
33601
|
+
console.error(chalk4.red(`Failed to connect: ${resp.status}`));
|
|
33211
33602
|
process.exit(1);
|
|
33212
33603
|
}
|
|
33213
33604
|
const reader = resp.body.getReader();
|
|
@@ -33233,11 +33624,11 @@ program2.command("stream").description("Subscribe to real-time task events via S
|
|
|
33233
33624
|
if (opts.json) {
|
|
33234
33625
|
console.log(JSON.stringify({ event: eventName, ...data }));
|
|
33235
33626
|
} else {
|
|
33236
|
-
const colorFn = eventColors[eventName] ||
|
|
33627
|
+
const colorFn = eventColors[eventName] || chalk4.white;
|
|
33237
33628
|
const ts = new Date(data.timestamp || Date.now()).toLocaleTimeString();
|
|
33238
33629
|
const taskId = data.task_id ? data.task_id.slice(0, 8) : "";
|
|
33239
33630
|
const agentInfo = data.agent_id ? ` [${data.agent_id}]` : "";
|
|
33240
|
-
console.log(`${
|
|
33631
|
+
console.log(`${chalk4.dim(ts)} ${colorFn(eventName.padEnd(25))} ${taskId}${agentInfo}`);
|
|
33241
33632
|
}
|
|
33242
33633
|
} catch {}
|
|
33243
33634
|
eventName = "";
|
|
@@ -33245,8 +33636,8 @@ program2.command("stream").description("Subscribe to real-time task events via S
|
|
|
33245
33636
|
}
|
|
33246
33637
|
}
|
|
33247
33638
|
} catch (e) {
|
|
33248
|
-
console.error(
|
|
33249
|
-
console.error(
|
|
33639
|
+
console.error(chalk4.red(`Connection error: ${e instanceof Error ? e.message : e}`));
|
|
33640
|
+
console.error(chalk4.dim("Is `todos serve` running?"));
|
|
33250
33641
|
process.exit(1);
|
|
33251
33642
|
}
|
|
33252
33643
|
});
|
|
@@ -33267,29 +33658,29 @@ program2.command("blame <file>").description("Show which tasks/agents touched a
|
|
|
33267
33658
|
output({ file: filePath, task_files: taskFiles, commits: commitRows }, true);
|
|
33268
33659
|
return;
|
|
33269
33660
|
}
|
|
33270
|
-
console.log(
|
|
33661
|
+
console.log(chalk4.bold(`
|
|
33271
33662
|
Blame: ${filePath}
|
|
33272
33663
|
`));
|
|
33273
33664
|
if (taskFiles.length > 0) {
|
|
33274
|
-
console.log(
|
|
33665
|
+
console.log(chalk4.bold("Task File Links:"));
|
|
33275
33666
|
for (const tf of taskFiles) {
|
|
33276
33667
|
const task = getTask5(tf.task_id, db);
|
|
33277
33668
|
const title = task ? task.title : "unknown";
|
|
33278
33669
|
const sid = task?.short_id || tf.task_id.slice(0, 8);
|
|
33279
|
-
console.log(` ${
|
|
33670
|
+
console.log(` ${chalk4.cyan(sid)} ${title} \u2014 ${chalk4.dim(tf.role || "file")} ${chalk4.dim(tf.updated_at)}`);
|
|
33280
33671
|
}
|
|
33281
33672
|
}
|
|
33282
33673
|
if (commitRows.length > 0) {
|
|
33283
|
-
console.log(
|
|
33674
|
+
console.log(chalk4.bold(`
|
|
33284
33675
|
Commit Links (${commitRows.length}):`));
|
|
33285
33676
|
for (const c of commitRows) {
|
|
33286
33677
|
const sid = c.short_id || c.task_id.slice(0, 8);
|
|
33287
|
-
console.log(` ${
|
|
33678
|
+
console.log(` ${chalk4.yellow(c.sha?.slice(0, 7) || "?")} ${chalk4.cyan(sid)} ${c.title || ""} \u2014 ${chalk4.dim(c.author || "")} ${chalk4.dim(c.committed_at || "")}`);
|
|
33288
33679
|
}
|
|
33289
33680
|
}
|
|
33290
33681
|
if (taskFiles.length === 0 && commitRows.length === 0) {
|
|
33291
|
-
console.log(
|
|
33292
|
-
console.log(
|
|
33682
|
+
console.log(chalk4.dim("No task or commit links found for this file."));
|
|
33683
|
+
console.log(chalk4.dim("Use 'todos hook install' to auto-link future commits."));
|
|
33293
33684
|
}
|
|
33294
33685
|
console.log();
|
|
33295
33686
|
});
|
|
@@ -33313,17 +33704,17 @@ program2.command("next").description("Show the best pending task to work on next
|
|
|
33313
33704
|
}
|
|
33314
33705
|
const task = getNextTask(opts.agent, Object.keys(filters).length ? filters : undefined, db);
|
|
33315
33706
|
if (!task) {
|
|
33316
|
-
console.log(
|
|
33707
|
+
console.log(chalk4.dim("No tasks available."));
|
|
33317
33708
|
return;
|
|
33318
33709
|
}
|
|
33319
33710
|
if (opts.json) {
|
|
33320
33711
|
console.log(JSON.stringify(task, null, 2));
|
|
33321
33712
|
return;
|
|
33322
33713
|
}
|
|
33323
|
-
console.log(
|
|
33324
|
-
console.log(` ${
|
|
33714
|
+
console.log(chalk4.bold("Next task:"));
|
|
33715
|
+
console.log(` ${chalk4.cyan(task.short_id || task.id.slice(0, 8))} ${chalk4.yellow(task.priority)} ${task.title}`);
|
|
33325
33716
|
if (task.description)
|
|
33326
|
-
console.log(
|
|
33717
|
+
console.log(chalk4.dim(` ${task.description.slice(0, 100)}`));
|
|
33327
33718
|
});
|
|
33328
33719
|
program2.command("claim <agent>").description("Atomically claim the best pending task for an agent").option("--project <id>", "Filter to project").option("-j, --json", "Output as JSON").action(async (agent, opts) => {
|
|
33329
33720
|
const db = getDatabase();
|
|
@@ -33332,28 +33723,28 @@ program2.command("claim <agent>").description("Atomically claim the best pending
|
|
|
33332
33723
|
filters.project_id = opts.project;
|
|
33333
33724
|
const task = claimNextTask(agent, Object.keys(filters).length ? filters : undefined, db);
|
|
33334
33725
|
if (!task) {
|
|
33335
|
-
console.log(
|
|
33726
|
+
console.log(chalk4.dim("No tasks available to claim."));
|
|
33336
33727
|
return;
|
|
33337
33728
|
}
|
|
33338
33729
|
if (opts.json) {
|
|
33339
33730
|
console.log(JSON.stringify(task, null, 2));
|
|
33340
33731
|
return;
|
|
33341
33732
|
}
|
|
33342
|
-
console.log(
|
|
33733
|
+
console.log(chalk4.green(`Claimed: ${task.short_id || task.id.slice(0, 8)} | ${task.priority} | ${task.title}`));
|
|
33343
33734
|
});
|
|
33344
33735
|
program2.command("steal <agent>").description("Work-stealing: take the highest-priority stale task from another agent").option("--stale-minutes <n>", "How long a task must be stale (default: 30)", "30").option("--project <id>", "Filter to project").action((agent, opts) => {
|
|
33345
33736
|
const globalOpts = program2.opts();
|
|
33346
33737
|
const { stealTask: stealTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
33347
33738
|
const task = stealTask2(agent, { stale_minutes: parseInt(opts.staleMinutes, 10), project_id: opts.project });
|
|
33348
33739
|
if (!task) {
|
|
33349
|
-
console.log(
|
|
33740
|
+
console.log(chalk4.dim("No stale tasks available to steal."));
|
|
33350
33741
|
return;
|
|
33351
33742
|
}
|
|
33352
33743
|
if (globalOpts.json) {
|
|
33353
33744
|
output(task, true);
|
|
33354
33745
|
return;
|
|
33355
33746
|
}
|
|
33356
|
-
console.log(
|
|
33747
|
+
console.log(chalk4.green(`Stolen: ${task.short_id || task.id.slice(0, 8)} | ${task.priority} | ${task.title}`));
|
|
33357
33748
|
});
|
|
33358
33749
|
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("-j, --json", "Output as JSON").action(async (opts) => {
|
|
33359
33750
|
const db = getDatabase();
|
|
@@ -33365,24 +33756,24 @@ program2.command("status").description("Show full project health snapshot").opti
|
|
|
33365
33756
|
console.log(JSON.stringify(s, null, 2));
|
|
33366
33757
|
return;
|
|
33367
33758
|
}
|
|
33368
|
-
console.log(`Tasks: ${
|
|
33759
|
+
console.log(`Tasks: ${chalk4.yellow(s.pending)} pending | ${chalk4.blue(s.in_progress)} active | ${chalk4.green(s.completed)} done | ${s.total} total`);
|
|
33369
33760
|
if (s.stale_count > 0)
|
|
33370
|
-
console.log(
|
|
33761
|
+
console.log(chalk4.red(`\u26A0\uFE0F ${s.stale_count} stale tasks (stuck in_progress)`));
|
|
33371
33762
|
if (s.overdue_recurring > 0)
|
|
33372
|
-
console.log(
|
|
33763
|
+
console.log(chalk4.yellow(`\uD83D\uDD01 ${s.overdue_recurring} overdue recurring`));
|
|
33373
33764
|
if (s.active_work.length > 0) {
|
|
33374
|
-
console.log(
|
|
33765
|
+
console.log(chalk4.bold(`
|
|
33375
33766
|
Active:`));
|
|
33376
33767
|
for (const w of s.active_work.slice(0, 5)) {
|
|
33377
33768
|
const id = w.short_id || w.id.slice(0, 8);
|
|
33378
|
-
console.log(` ${
|
|
33769
|
+
console.log(` ${chalk4.cyan(id)} | ${w.assigned_to || w.locked_by || "?"} | ${w.title}`);
|
|
33379
33770
|
}
|
|
33380
33771
|
}
|
|
33381
33772
|
if (s.next_task) {
|
|
33382
|
-
console.log(
|
|
33773
|
+
console.log(chalk4.bold(`
|
|
33383
33774
|
Next up:`));
|
|
33384
33775
|
const t = s.next_task;
|
|
33385
|
-
console.log(` ${
|
|
33776
|
+
console.log(` ${chalk4.cyan(t.short_id || t.id.slice(0, 8))} ${chalk4.yellow(t.priority)} ${t.title}`);
|
|
33386
33777
|
}
|
|
33387
33778
|
});
|
|
33388
33779
|
program2.command("recap").description("Show what happened in the last N hours \u2014 completed tasks, new tasks, agent activity, blockers").option("--hours <n>", "Look back N hours (default: 8)", "8").option("--project <id>", "Filter to project").action((opts) => {
|
|
@@ -33393,60 +33784,60 @@ program2.command("recap").description("Show what happened in the last N hours \u
|
|
|
33393
33784
|
output(recap, true);
|
|
33394
33785
|
return;
|
|
33395
33786
|
}
|
|
33396
|
-
console.log(
|
|
33787
|
+
console.log(chalk4.bold(`
|
|
33397
33788
|
Recap \u2014 last ${recap.hours} hours (since ${new Date(recap.since).toLocaleString()})
|
|
33398
33789
|
`));
|
|
33399
33790
|
if (recap.completed.length > 0) {
|
|
33400
|
-
console.log(
|
|
33791
|
+
console.log(chalk4.green.bold(`Completed (${recap.completed.length}):`));
|
|
33401
33792
|
for (const t of recap.completed) {
|
|
33402
33793
|
const id = t.short_id || t.id.slice(0, 8);
|
|
33403
33794
|
const dur = t.duration_minutes != null ? ` (${t.duration_minutes}m)` : "";
|
|
33404
|
-
console.log(` ${
|
|
33795
|
+
console.log(` ${chalk4.green("\u2713")} ${chalk4.cyan(id)} ${t.title}${dur}${t.assigned_to ? ` \u2014 ${chalk4.dim(t.assigned_to)}` : ""}`);
|
|
33405
33796
|
}
|
|
33406
33797
|
} else {
|
|
33407
|
-
console.log(
|
|
33798
|
+
console.log(chalk4.dim("No tasks completed in this period."));
|
|
33408
33799
|
}
|
|
33409
33800
|
if (recap.in_progress.length > 0) {
|
|
33410
|
-
console.log(
|
|
33801
|
+
console.log(chalk4.blue.bold(`
|
|
33411
33802
|
In Progress (${recap.in_progress.length}):`));
|
|
33412
33803
|
for (const t of recap.in_progress) {
|
|
33413
33804
|
const id = t.short_id || t.id.slice(0, 8);
|
|
33414
|
-
console.log(` ${
|
|
33805
|
+
console.log(` ${chalk4.blue("\u2192")} ${chalk4.cyan(id)} ${t.title}${t.assigned_to ? ` \u2014 ${chalk4.dim(t.assigned_to)}` : ""}`);
|
|
33415
33806
|
}
|
|
33416
33807
|
}
|
|
33417
33808
|
if (recap.blocked.length > 0) {
|
|
33418
|
-
console.log(
|
|
33809
|
+
console.log(chalk4.red.bold(`
|
|
33419
33810
|
Blocked (${recap.blocked.length}):`));
|
|
33420
33811
|
for (const t of recap.blocked) {
|
|
33421
33812
|
const id = t.short_id || t.id.slice(0, 8);
|
|
33422
|
-
console.log(` ${
|
|
33813
|
+
console.log(` ${chalk4.red("\u2717")} ${chalk4.cyan(id)} ${t.title}`);
|
|
33423
33814
|
}
|
|
33424
33815
|
}
|
|
33425
33816
|
if (recap.stale.length > 0) {
|
|
33426
|
-
console.log(
|
|
33817
|
+
console.log(chalk4.yellow.bold(`
|
|
33427
33818
|
Stale (${recap.stale.length}):`));
|
|
33428
33819
|
for (const t of recap.stale) {
|
|
33429
33820
|
const id = t.short_id || t.id.slice(0, 8);
|
|
33430
33821
|
const ago = Math.round((Date.now() - new Date(t.updated_at).getTime()) / 60000);
|
|
33431
|
-
console.log(` ${
|
|
33822
|
+
console.log(` ${chalk4.yellow("!")} ${chalk4.cyan(id)} ${t.title} \u2014 last update ${ago}m ago`);
|
|
33432
33823
|
}
|
|
33433
33824
|
}
|
|
33434
33825
|
if (recap.created.length > 0) {
|
|
33435
|
-
console.log(
|
|
33826
|
+
console.log(chalk4.dim.bold(`
|
|
33436
33827
|
Created (${recap.created.length}):`));
|
|
33437
33828
|
for (const t of recap.created.slice(0, 10)) {
|
|
33438
33829
|
const id = t.short_id || t.id.slice(0, 8);
|
|
33439
|
-
console.log(` ${
|
|
33830
|
+
console.log(` ${chalk4.dim("+")} ${chalk4.cyan(id)} ${t.title}`);
|
|
33440
33831
|
}
|
|
33441
33832
|
if (recap.created.length > 10)
|
|
33442
|
-
console.log(
|
|
33833
|
+
console.log(chalk4.dim(` ... and ${recap.created.length - 10} more`));
|
|
33443
33834
|
}
|
|
33444
33835
|
if (recap.agents.length > 0) {
|
|
33445
|
-
console.log(
|
|
33836
|
+
console.log(chalk4.bold(`
|
|
33446
33837
|
Agents:`));
|
|
33447
33838
|
for (const a of recap.agents) {
|
|
33448
33839
|
const seen = Math.round((Date.now() - new Date(a.last_seen_at).getTime()) / 60000);
|
|
33449
|
-
console.log(` ${a.name}: ${
|
|
33840
|
+
console.log(` ${a.name}: ${chalk4.green(a.completed_count + " done")} | ${chalk4.blue(a.in_progress_count + " active")} | last seen ${seen}m ago`);
|
|
33450
33841
|
}
|
|
33451
33842
|
}
|
|
33452
33843
|
console.log();
|
|
@@ -33461,7 +33852,7 @@ program2.command("standup").description("Generate standup notes \u2014 completed
|
|
|
33461
33852
|
output(recap, true);
|
|
33462
33853
|
return;
|
|
33463
33854
|
}
|
|
33464
|
-
console.log(
|
|
33855
|
+
console.log(chalk4.bold(`
|
|
33465
33856
|
Standup \u2014 since ${sinceDate.toLocaleDateString()}
|
|
33466
33857
|
`));
|
|
33467
33858
|
const byAgent = new Map;
|
|
@@ -33472,29 +33863,29 @@ Standup \u2014 since ${sinceDate.toLocaleDateString()}
|
|
|
33472
33863
|
byAgent.get(agent).push(t);
|
|
33473
33864
|
}
|
|
33474
33865
|
if (byAgent.size > 0) {
|
|
33475
|
-
console.log(
|
|
33866
|
+
console.log(chalk4.green.bold("Done:"));
|
|
33476
33867
|
for (const [agent, tasks] of byAgent) {
|
|
33477
|
-
console.log(` ${
|
|
33868
|
+
console.log(` ${chalk4.cyan(agent)}:`);
|
|
33478
33869
|
for (const t of tasks) {
|
|
33479
33870
|
const dur = t.duration_minutes != null ? ` (${t.duration_minutes}m)` : "";
|
|
33480
|
-
console.log(` ${
|
|
33871
|
+
console.log(` ${chalk4.green("\u2713")} ${t.short_id || t.id.slice(0, 8)} ${t.title}${dur}`);
|
|
33481
33872
|
}
|
|
33482
33873
|
}
|
|
33483
33874
|
} else {
|
|
33484
|
-
console.log(
|
|
33875
|
+
console.log(chalk4.dim("Nothing completed."));
|
|
33485
33876
|
}
|
|
33486
33877
|
if (recap.in_progress.length > 0) {
|
|
33487
|
-
console.log(
|
|
33878
|
+
console.log(chalk4.blue.bold(`
|
|
33488
33879
|
In Progress:`));
|
|
33489
33880
|
for (const t of recap.in_progress) {
|
|
33490
|
-
console.log(` ${
|
|
33881
|
+
console.log(` ${chalk4.blue("\u2192")} ${t.short_id || t.id.slice(0, 8)} ${t.title}${t.assigned_to ? ` \u2014 ${chalk4.dim(t.assigned_to)}` : ""}`);
|
|
33491
33882
|
}
|
|
33492
33883
|
}
|
|
33493
33884
|
if (recap.blocked.length > 0) {
|
|
33494
|
-
console.log(
|
|
33885
|
+
console.log(chalk4.red.bold(`
|
|
33495
33886
|
Blocked:`));
|
|
33496
33887
|
for (const t of recap.blocked) {
|
|
33497
|
-
console.log(` ${
|
|
33888
|
+
console.log(` ${chalk4.red("\u2717")} ${t.short_id || t.id.slice(0, 8)} ${t.title}`);
|
|
33498
33889
|
}
|
|
33499
33890
|
}
|
|
33500
33891
|
console.log();
|
|
@@ -33503,7 +33894,7 @@ program2.command("fail <id>").description("Mark a task as failed with optional r
|
|
|
33503
33894
|
const db = getDatabase();
|
|
33504
33895
|
const resolvedId = resolvePartialId(db, "tasks", id);
|
|
33505
33896
|
if (!resolvedId) {
|
|
33506
|
-
console.error(
|
|
33897
|
+
console.error(chalk4.red(`Task not found: ${id}`));
|
|
33507
33898
|
process.exit(1);
|
|
33508
33899
|
}
|
|
33509
33900
|
const result = failTask(resolvedId, opts.agent, opts.reason, { retry: opts.retry }, db);
|
|
@@ -33511,11 +33902,11 @@ program2.command("fail <id>").description("Mark a task as failed with optional r
|
|
|
33511
33902
|
console.log(JSON.stringify(result, null, 2));
|
|
33512
33903
|
return;
|
|
33513
33904
|
}
|
|
33514
|
-
console.log(
|
|
33905
|
+
console.log(chalk4.red(`Failed: ${result.task.short_id || result.task.id.slice(0, 8)} | ${result.task.title}`));
|
|
33515
33906
|
if (opts.reason)
|
|
33516
|
-
console.log(
|
|
33907
|
+
console.log(chalk4.dim(`Reason: ${opts.reason}`));
|
|
33517
33908
|
if (result.retryTask)
|
|
33518
|
-
console.log(
|
|
33909
|
+
console.log(chalk4.yellow(`Retry created: ${result.retryTask.short_id || result.retryTask.id.slice(0, 8)} | ${result.retryTask.title}`));
|
|
33519
33910
|
});
|
|
33520
33911
|
program2.command("active").description("Show all currently in-progress tasks").option("--project <id>", "Filter to project").option("-j, --json", "Output as JSON").action(async (opts) => {
|
|
33521
33912
|
const db = getDatabase();
|
|
@@ -33528,14 +33919,14 @@ program2.command("active").description("Show all currently in-progress tasks").o
|
|
|
33528
33919
|
return;
|
|
33529
33920
|
}
|
|
33530
33921
|
if (work.length === 0) {
|
|
33531
|
-
console.log(
|
|
33922
|
+
console.log(chalk4.dim("No active work."));
|
|
33532
33923
|
return;
|
|
33533
33924
|
}
|
|
33534
|
-
console.log(
|
|
33925
|
+
console.log(chalk4.bold(`Active work (${work.length}):`));
|
|
33535
33926
|
for (const w of work) {
|
|
33536
33927
|
const id = w.short_id || w.id.slice(0, 8);
|
|
33537
33928
|
const agent = w.assigned_to || w.locked_by || "unassigned";
|
|
33538
|
-
console.log(` ${
|
|
33929
|
+
console.log(` ${chalk4.cyan(id)} | ${chalk4.yellow(w.priority)} | ${agent.padEnd(12)} | ${w.title}`);
|
|
33539
33930
|
}
|
|
33540
33931
|
});
|
|
33541
33932
|
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("-j, --json", "Output as JSON").action(async (opts) => {
|
|
@@ -33549,14 +33940,14 @@ program2.command("stale").description("Find tasks stuck in_progress with no rece
|
|
|
33549
33940
|
return;
|
|
33550
33941
|
}
|
|
33551
33942
|
if (tasks.length === 0) {
|
|
33552
|
-
console.log(
|
|
33943
|
+
console.log(chalk4.dim("No stale tasks."));
|
|
33553
33944
|
return;
|
|
33554
33945
|
}
|
|
33555
|
-
console.log(
|
|
33946
|
+
console.log(chalk4.bold(`Stale tasks (${tasks.length}):`));
|
|
33556
33947
|
for (const t of tasks) {
|
|
33557
33948
|
const id = t.short_id || t.id.slice(0, 8);
|
|
33558
33949
|
const staleMin = Math.round((Date.now() - new Date(t.updated_at).getTime()) / 60000);
|
|
33559
|
-
console.log(` ${
|
|
33950
|
+
console.log(` ${chalk4.cyan(id)} | ${t.locked_by || t.assigned_to || "?"} | ${t.title} ${chalk4.dim(`(${staleMin}min stale)`)}`);
|
|
33560
33951
|
}
|
|
33561
33952
|
});
|
|
33562
33953
|
program2.command("redistribute <agent>").description("Release stale in-progress tasks and claim the best one (work-stealing)").option("--max-age <minutes>", "Stale threshold in minutes", "60").option("--project <id>", "Limit to a specific project").option("--limit <n>", "Max stale tasks to release").option("-j, --json", "Output as JSON").action(async (agent, opts) => {
|
|
@@ -33572,17 +33963,17 @@ program2.command("redistribute <agent>").description("Release stale in-progress
|
|
|
33572
33963
|
console.log(JSON.stringify(result, null, 2));
|
|
33573
33964
|
return;
|
|
33574
33965
|
}
|
|
33575
|
-
console.log(
|
|
33966
|
+
console.log(chalk4.bold(`Released ${result.released.length} stale task(s).`));
|
|
33576
33967
|
for (const t of result.released) {
|
|
33577
33968
|
const id = t.short_id || t.id.slice(0, 8);
|
|
33578
|
-
console.log(` ${
|
|
33969
|
+
console.log(` ${chalk4.yellow("released")} ${chalk4.cyan(id)} ${t.title}`);
|
|
33579
33970
|
}
|
|
33580
33971
|
if (result.claimed) {
|
|
33581
33972
|
const id = result.claimed.short_id || result.claimed.id.slice(0, 8);
|
|
33582
|
-
console.log(
|
|
33583
|
-
Claimed: ${
|
|
33973
|
+
console.log(chalk4.green(`
|
|
33974
|
+
Claimed: ${chalk4.cyan(id)} ${result.claimed.title}`));
|
|
33584
33975
|
} else {
|
|
33585
|
-
console.log(
|
|
33976
|
+
console.log(chalk4.dim(`
|
|
33586
33977
|
No task claimed (nothing available).`));
|
|
33587
33978
|
}
|
|
33588
33979
|
});
|
|
@@ -33592,7 +33983,7 @@ program2.command("assign <id> <agent>").description("Assign a task to an agent")
|
|
|
33592
33983
|
const db = getDatabase();
|
|
33593
33984
|
const task = getTask2(resolvedId, db);
|
|
33594
33985
|
if (!task) {
|
|
33595
|
-
console.error(
|
|
33986
|
+
console.error(chalk4.red(`Task not found: ${id}`));
|
|
33596
33987
|
process.exit(1);
|
|
33597
33988
|
}
|
|
33598
33989
|
try {
|
|
@@ -33601,7 +33992,7 @@ program2.command("assign <id> <agent>").description("Assign a task to an agent")
|
|
|
33601
33992
|
console.log(JSON.stringify(updated));
|
|
33602
33993
|
return;
|
|
33603
33994
|
}
|
|
33604
|
-
console.log(
|
|
33995
|
+
console.log(chalk4.green(`Assigned to ${agent}: ${formatTaskLine(updated)}`));
|
|
33605
33996
|
} catch {
|
|
33606
33997
|
handleError(new Error("Failed to assign"));
|
|
33607
33998
|
}
|
|
@@ -33612,7 +34003,7 @@ program2.command("unassign <id>").description("Remove task assignment").option("
|
|
|
33612
34003
|
const db = getDatabase();
|
|
33613
34004
|
const task = getTask2(resolvedId, db);
|
|
33614
34005
|
if (!task) {
|
|
33615
|
-
console.error(
|
|
34006
|
+
console.error(chalk4.red(`Task not found: ${id}`));
|
|
33616
34007
|
process.exit(1);
|
|
33617
34008
|
}
|
|
33618
34009
|
try {
|
|
@@ -33621,7 +34012,7 @@ program2.command("unassign <id>").description("Remove task assignment").option("
|
|
|
33621
34012
|
console.log(JSON.stringify(updated));
|
|
33622
34013
|
return;
|
|
33623
34014
|
}
|
|
33624
|
-
console.log(
|
|
34015
|
+
console.log(chalk4.green(`Unassigned: ${formatTaskLine(updated)}`));
|
|
33625
34016
|
} catch {
|
|
33626
34017
|
handleError(new Error("Failed to unassign"));
|
|
33627
34018
|
}
|
|
@@ -33632,7 +34023,7 @@ program2.command("tag <id> <tag>").description("Add a tag to a task").option("-j
|
|
|
33632
34023
|
const db = getDatabase();
|
|
33633
34024
|
const task = getTask2(resolvedId, db);
|
|
33634
34025
|
if (!task) {
|
|
33635
|
-
console.error(
|
|
34026
|
+
console.error(chalk4.red(`Task not found: ${id}`));
|
|
33636
34027
|
process.exit(1);
|
|
33637
34028
|
}
|
|
33638
34029
|
const newTags = [...new Set([...task.tags, tag])];
|
|
@@ -33642,7 +34033,7 @@ program2.command("tag <id> <tag>").description("Add a tag to a task").option("-j
|
|
|
33642
34033
|
console.log(JSON.stringify(updated));
|
|
33643
34034
|
return;
|
|
33644
34035
|
}
|
|
33645
|
-
console.log(
|
|
34036
|
+
console.log(chalk4.green(`Tagged [${tag}]: ${formatTaskLine(updated)}`));
|
|
33646
34037
|
} catch {
|
|
33647
34038
|
handleError(new Error("Failed to tag"));
|
|
33648
34039
|
}
|
|
@@ -33653,7 +34044,7 @@ program2.command("untag <id> <tag>").description("Remove a tag from a task").opt
|
|
|
33653
34044
|
const db = getDatabase();
|
|
33654
34045
|
const task = getTask2(resolvedId, db);
|
|
33655
34046
|
if (!task) {
|
|
33656
|
-
console.error(
|
|
34047
|
+
console.error(chalk4.red(`Task not found: ${id}`));
|
|
33657
34048
|
process.exit(1);
|
|
33658
34049
|
}
|
|
33659
34050
|
const newTags = task.tags.filter((t) => t !== tag);
|
|
@@ -33663,7 +34054,7 @@ program2.command("untag <id> <tag>").description("Remove a tag from a task").opt
|
|
|
33663
34054
|
console.log(JSON.stringify(updated));
|
|
33664
34055
|
return;
|
|
33665
34056
|
}
|
|
33666
|
-
console.log(
|
|
34057
|
+
console.log(chalk4.green(`Untagged [${tag}]: ${formatTaskLine(updated)}`));
|
|
33667
34058
|
} catch {
|
|
33668
34059
|
handleError(new Error("Failed to untag"));
|
|
33669
34060
|
}
|
|
@@ -33679,7 +34070,7 @@ program2.command("pin <id>").description("Escalate task to critical priority").o
|
|
|
33679
34070
|
console.log(JSON.stringify(updated));
|
|
33680
34071
|
return;
|
|
33681
34072
|
}
|
|
33682
|
-
console.log(
|
|
34073
|
+
console.log(chalk4.red(`\uD83D\uDCCC Pinned (critical): ${formatTaskLine(updated)}`));
|
|
33683
34074
|
} catch {
|
|
33684
34075
|
handleError(new Error("Failed to pin"));
|
|
33685
34076
|
}
|
|
@@ -33766,19 +34157,19 @@ program2.command("doctor").description("Diagnose common task data issues").optio
|
|
|
33766
34157
|
console.log(JSON.stringify({ issues, ok: !issues.some((i) => i.severity === "error") }));
|
|
33767
34158
|
return;
|
|
33768
34159
|
}
|
|
33769
|
-
console.log(
|
|
34160
|
+
console.log(chalk4.bold(`todos doctor
|
|
33770
34161
|
`));
|
|
33771
34162
|
for (const issue of issues) {
|
|
33772
|
-
const icon = issue.severity === "error" ?
|
|
34163
|
+
const icon = issue.severity === "error" ? chalk4.red("\u2717") : issue.severity === "warn" ? chalk4.yellow("\u26A0") : chalk4.green("\u2713");
|
|
33773
34164
|
console.log(` ${icon} ${issue.message}`);
|
|
33774
34165
|
}
|
|
33775
34166
|
const errors2 = issues.filter((i) => i.severity === "error").length;
|
|
33776
34167
|
const warns = issues.filter((i) => i.severity === "warn").length;
|
|
33777
34168
|
if (errors2 === 0 && warns === 0)
|
|
33778
|
-
console.log(
|
|
34169
|
+
console.log(chalk4.green(`
|
|
33779
34170
|
All clear.`));
|
|
33780
34171
|
else
|
|
33781
|
-
console.log(
|
|
34172
|
+
console.log(chalk4[errors2 > 0 ? "red" : "yellow"](`
|
|
33782
34173
|
${errors2} error(s), ${warns} warning(s). Run with --fix to auto-resolve where possible.`));
|
|
33783
34174
|
});
|
|
33784
34175
|
program2.command("health").description("Check todos system health \u2014 database, config, connectivity").option("-j, --json", "Output as JSON").action(async (opts) => {
|
|
@@ -33793,7 +34184,7 @@ program2.command("health").description("Check todos system health \u2014 databas
|
|
|
33793
34184
|
try {
|
|
33794
34185
|
size = `${(statSync3(dbPath).size / 1024 / 1024).toFixed(1)} MB`;
|
|
33795
34186
|
} catch {}
|
|
33796
|
-
checks.push({ name: "Database", ok: true, message: `${row.count} tasks \xB7 ${size} \xB7 ${
|
|
34187
|
+
checks.push({ name: "Database", ok: true, message: `${row.count} tasks \xB7 ${size} \xB7 ${chalk4.dim(dbPath)}` });
|
|
33797
34188
|
} catch (e) {
|
|
33798
34189
|
checks.push({ name: "Database", ok: false, message: e instanceof Error ? e.message : "Failed" });
|
|
33799
34190
|
}
|
|
@@ -33826,15 +34217,15 @@ program2.command("health").description("Check todos system health \u2014 databas
|
|
|
33826
34217
|
console.log(JSON.stringify({ ok, checks }));
|
|
33827
34218
|
return;
|
|
33828
34219
|
}
|
|
33829
|
-
console.log(
|
|
34220
|
+
console.log(chalk4.bold(`todos health
|
|
33830
34221
|
`));
|
|
33831
34222
|
for (const c of checks) {
|
|
33832
|
-
const icon = c.ok ?
|
|
34223
|
+
const icon = c.ok ? chalk4.green("\u2713") : chalk4.yellow("\u26A0");
|
|
33833
34224
|
console.log(` ${icon} ${c.name.padEnd(14)} ${c.message}`);
|
|
33834
34225
|
}
|
|
33835
34226
|
const allOk = checks.every((c) => c.ok);
|
|
33836
34227
|
console.log(`
|
|
33837
|
-
${allOk ?
|
|
34228
|
+
${allOk ? chalk4.green("All checks passed.") : chalk4.yellow("Some checks need attention.")}`);
|
|
33838
34229
|
});
|
|
33839
34230
|
program2.command("report").description("Analytics report: task activity, completion rates, agent breakdown").option("--days <n>", "Days to include in report", "7").option("--project <id>", "Filter to project").option("--markdown", "Output as markdown").option("-j, --json", "Output as JSON").action(async (opts) => {
|
|
33840
34231
|
const globalOpts = program2.opts();
|
|
@@ -33883,15 +34274,15 @@ program2.command("report").description("Analytics report: task activity, complet
|
|
|
33883
34274
|
if (sparkline)
|
|
33884
34275
|
lines.push(`| Activity | \`${sparkline}\` |`);
|
|
33885
34276
|
} else {
|
|
33886
|
-
lines.push(
|
|
34277
|
+
lines.push(chalk4.bold(`todos report \u2014 last ${days} day${days !== 1 ? "s" : ""}`));
|
|
33887
34278
|
lines.push("");
|
|
33888
|
-
lines.push(` Total: ${
|
|
33889
|
-
lines.push(` Changed: ${
|
|
33890
|
-
lines.push(` Completed: ${
|
|
34279
|
+
lines.push(` Total: ${chalk4.bold(String(all.length))} tasks (${chalk4.yellow(String(stats.by_status?.pending ?? 0))} pending, ${chalk4.blue(String(stats.by_status?.in_progress ?? 0))} active)`);
|
|
34280
|
+
lines.push(` Changed: ${chalk4.bold(String(changed.length))} in period`);
|
|
34281
|
+
lines.push(` Completed: ${chalk4.green(String(completed.length))} (${completionRate}% rate)`);
|
|
33891
34282
|
if (failed.length > 0)
|
|
33892
|
-
lines.push(` Failed: ${
|
|
34283
|
+
lines.push(` Failed: ${chalk4.red(String(failed.length))}`);
|
|
33893
34284
|
if (sparkline)
|
|
33894
|
-
lines.push(` Activity: ${
|
|
34285
|
+
lines.push(` Activity: ${chalk4.dim(sparkline)}`);
|
|
33895
34286
|
if (Object.keys(byAgent).length > 0) {
|
|
33896
34287
|
lines.push(` By agent: ${Object.entries(byAgent).map(([a, n]) => `${a}=${n}`).join(" ")}`);
|
|
33897
34288
|
}
|
|
@@ -33915,21 +34306,21 @@ program2.command("today").description("Show task activity from today").option("-
|
|
|
33915
34306
|
console.log(JSON.stringify({ date: start.toISOString().slice(0, 10), completed, started, changed: other }));
|
|
33916
34307
|
return;
|
|
33917
34308
|
}
|
|
33918
|
-
console.log(
|
|
34309
|
+
console.log(chalk4.bold(`Today \u2014 ${start.toISOString().slice(0, 10)}
|
|
33919
34310
|
`));
|
|
33920
34311
|
if (completed.length > 0) {
|
|
33921
|
-
console.log(
|
|
34312
|
+
console.log(chalk4.green(` \u2713 Completed (${completed.length}):`));
|
|
33922
34313
|
for (const t of completed)
|
|
33923
|
-
console.log(` ${
|
|
34314
|
+
console.log(` ${chalk4.cyan(t.short_id || t.id.slice(0, 8))} ${t.title}${t.assigned_to ? chalk4.dim(` \u2014 ${t.assigned_to}`) : ""}`);
|
|
33924
34315
|
}
|
|
33925
34316
|
if (started.length > 0) {
|
|
33926
|
-
console.log(
|
|
34317
|
+
console.log(chalk4.blue(`
|
|
33927
34318
|
\u25B6 Started (${started.length}):`));
|
|
33928
34319
|
for (const t of started)
|
|
33929
|
-
console.log(` ${
|
|
34320
|
+
console.log(` ${chalk4.cyan(t.short_id || t.id.slice(0, 8))} ${t.title}${t.assigned_to ? chalk4.dim(` \u2014 ${t.assigned_to}`) : ""}`);
|
|
33930
34321
|
}
|
|
33931
34322
|
if (completed.length === 0 && started.length === 0)
|
|
33932
|
-
console.log(
|
|
34323
|
+
console.log(chalk4.dim(" No activity today."));
|
|
33933
34324
|
});
|
|
33934
34325
|
program2.command("yesterday").description("Show task activity from yesterday").option("-j, --json", "Output as JSON").action(async (opts) => {
|
|
33935
34326
|
const globalOpts = program2.opts();
|
|
@@ -33948,21 +34339,21 @@ program2.command("yesterday").description("Show task activity from yesterday").o
|
|
|
33948
34339
|
console.log(JSON.stringify({ date: start.toISOString().slice(0, 10), completed, started }));
|
|
33949
34340
|
return;
|
|
33950
34341
|
}
|
|
33951
|
-
console.log(
|
|
34342
|
+
console.log(chalk4.bold(`Yesterday \u2014 ${start.toISOString().slice(0, 10)}
|
|
33952
34343
|
`));
|
|
33953
34344
|
if (completed.length > 0) {
|
|
33954
|
-
console.log(
|
|
34345
|
+
console.log(chalk4.green(` \u2713 Completed (${completed.length}):`));
|
|
33955
34346
|
for (const t of completed)
|
|
33956
|
-
console.log(` ${
|
|
34347
|
+
console.log(` ${chalk4.cyan(t.short_id || t.id.slice(0, 8))} ${t.title}${t.assigned_to ? chalk4.dim(` \u2014 ${t.assigned_to}`) : ""}`);
|
|
33957
34348
|
}
|
|
33958
34349
|
if (started.length > 0) {
|
|
33959
|
-
console.log(
|
|
34350
|
+
console.log(chalk4.blue(`
|
|
33960
34351
|
\u25B6 Started (${started.length}):`));
|
|
33961
34352
|
for (const t of started)
|
|
33962
|
-
console.log(` ${
|
|
34353
|
+
console.log(` ${chalk4.cyan(t.short_id || t.id.slice(0, 8))} ${t.title}`);
|
|
33963
34354
|
}
|
|
33964
34355
|
if (completed.length === 0 && started.length === 0)
|
|
33965
|
-
console.log(
|
|
34356
|
+
console.log(chalk4.dim(" No activity yesterday."));
|
|
33966
34357
|
});
|
|
33967
34358
|
program2.command("mine").description("Show tasks assigned to you, grouped by status").argument("<agent>", "Agent name or ID").option("-j, --json", "Output as JSON").action(async (agent, opts) => {
|
|
33968
34359
|
const globalOpts = program2.opts();
|
|
@@ -33997,23 +34388,23 @@ program2.command("mine").description("Show tasks assigned to you, grouped by sta
|
|
|
33997
34388
|
}
|
|
33998
34389
|
const statusOrder = ["in_progress", "pending", "blocked", "completed", "failed", "cancelled"];
|
|
33999
34390
|
const statusIcons2 = { in_progress: "\u25B6", pending: "\u25CB", blocked: "\u2298", completed: "\u2713", failed: "\u2717", cancelled: "\u2014" };
|
|
34000
|
-
const statusColors5 = { in_progress:
|
|
34001
|
-
console.log(
|
|
34391
|
+
const statusColors5 = { in_progress: chalk4.blue, pending: chalk4.white, blocked: chalk4.red, completed: chalk4.green, failed: chalk4.red, cancelled: chalk4.dim };
|
|
34392
|
+
console.log(chalk4.bold(`Tasks for ${agent} (${tasks.length} total):
|
|
34002
34393
|
`));
|
|
34003
34394
|
for (const status of statusOrder) {
|
|
34004
34395
|
const group = groups[status];
|
|
34005
34396
|
if (!group || group.length === 0)
|
|
34006
34397
|
continue;
|
|
34007
|
-
const color = statusColors5[status] ||
|
|
34398
|
+
const color = statusColors5[status] || chalk4.white;
|
|
34008
34399
|
const icon = statusIcons2[status] || "?";
|
|
34009
34400
|
console.log(color(` ${icon} ${status.replace("_", " ")} (${group.length}):`));
|
|
34010
34401
|
for (const t of group) {
|
|
34011
|
-
const priority = t.priority === "critical" || t.priority === "high" ?
|
|
34012
|
-
console.log(` ${
|
|
34402
|
+
const priority = t.priority === "critical" || t.priority === "high" ? chalk4.red(` [${t.priority}]`) : "";
|
|
34403
|
+
console.log(` ${chalk4.cyan(t.short_id || t.id.slice(0, 8))} ${t.title}${priority}`);
|
|
34013
34404
|
}
|
|
34014
34405
|
}
|
|
34015
34406
|
if (tasks.length === 0)
|
|
34016
|
-
console.log(
|
|
34407
|
+
console.log(chalk4.dim(` No tasks assigned to ${agent}.`));
|
|
34017
34408
|
});
|
|
34018
34409
|
program2.command("blocked").description("Show tasks blocked by incomplete dependencies").option("-j, --json", "Output as JSON").option("--project <id>", "Filter to project").action(async (opts) => {
|
|
34019
34410
|
const globalOpts = program2.opts();
|
|
@@ -34035,15 +34426,15 @@ program2.command("blocked").description("Show tasks blocked by incomplete depend
|
|
|
34035
34426
|
return;
|
|
34036
34427
|
}
|
|
34037
34428
|
if (blockedTasks.length === 0) {
|
|
34038
|
-
console.log(
|
|
34429
|
+
console.log(chalk4.green(" No blocked tasks!"));
|
|
34039
34430
|
return;
|
|
34040
34431
|
}
|
|
34041
|
-
console.log(
|
|
34432
|
+
console.log(chalk4.bold(`Blocked (${blockedTasks.length}):
|
|
34042
34433
|
`));
|
|
34043
34434
|
for (const { task, blockers } of blockedTasks) {
|
|
34044
|
-
console.log(` ${
|
|
34435
|
+
console.log(` ${chalk4.cyan(task.short_id || task.id.slice(0, 8))} ${task.title}`);
|
|
34045
34436
|
for (const bl of blockers) {
|
|
34046
|
-
console.log(` ${
|
|
34437
|
+
console.log(` ${chalk4.red("\u2298")} ${chalk4.dim(bl.short_id || bl.id.slice(0, 8))} ${chalk4.dim(bl.title)} ${chalk4.yellow(`[${bl.status}]`)}`);
|
|
34047
34438
|
}
|
|
34048
34439
|
}
|
|
34049
34440
|
});
|
|
@@ -34057,16 +34448,16 @@ program2.command("overdue").description("Show tasks past their due date").option
|
|
|
34057
34448
|
return;
|
|
34058
34449
|
}
|
|
34059
34450
|
if (tasks.length === 0) {
|
|
34060
|
-
console.log(
|
|
34451
|
+
console.log(chalk4.green(" No overdue tasks!"));
|
|
34061
34452
|
return;
|
|
34062
34453
|
}
|
|
34063
|
-
console.log(
|
|
34454
|
+
console.log(chalk4.bold.red(`Overdue (${tasks.length}):
|
|
34064
34455
|
`));
|
|
34065
34456
|
for (const t of tasks) {
|
|
34066
34457
|
const dueDate = t.due_at.slice(0, 10);
|
|
34067
34458
|
const daysOverdue = Math.floor((Date.now() - new Date(t.due_at).getTime()) / 86400000);
|
|
34068
|
-
const urgency = daysOverdue > 7 ?
|
|
34069
|
-
console.log(` ${urgency} ${
|
|
34459
|
+
const urgency = daysOverdue > 7 ? chalk4.bgRed.white(` ${daysOverdue}d `) : chalk4.red(`${daysOverdue}d`);
|
|
34460
|
+
console.log(` ${urgency} ${chalk4.cyan(t.short_id || t.id.slice(0, 8))} ${t.title}${t.assigned_to ? chalk4.dim(` \u2014 ${t.assigned_to}`) : ""} ${chalk4.dim(`(due ${dueDate})`)}`);
|
|
34070
34461
|
}
|
|
34071
34462
|
});
|
|
34072
34463
|
program2.command("week").description("Show task activity from the past 7 days").option("-j, --json", "Output as JSON").action(async (opts) => {
|
|
@@ -34101,8 +34492,8 @@ program2.command("week").description("Show task activity from the past 7 days").
|
|
|
34101
34492
|
}
|
|
34102
34493
|
const totalCompleted = tasks.filter((t) => t.status === "completed").length;
|
|
34103
34494
|
const totalStarted = tasks.filter((t) => t.status === "in_progress").length;
|
|
34104
|
-
console.log(
|
|
34105
|
-
console.log(
|
|
34495
|
+
console.log(chalk4.bold(`Week \u2014 ${start.toISOString().slice(0, 10)} to ${now2.toISOString().slice(0, 10)}`));
|
|
34496
|
+
console.log(chalk4.dim(` ${totalCompleted} completed, ${totalStarted} in progress, ${tasks.length} total changes
|
|
34106
34497
|
`));
|
|
34107
34498
|
const sortedDays = Object.keys(days).sort().reverse();
|
|
34108
34499
|
for (const day of sortedDays) {
|
|
@@ -34113,14 +34504,14 @@ program2.command("week").description("Show task activity from the past 7 days").
|
|
|
34113
34504
|
if (completed.length === 0 && started.length === 0)
|
|
34114
34505
|
continue;
|
|
34115
34506
|
const weekday = new Date(day + "T12:00:00").toLocaleDateString("en-US", { weekday: "short" });
|
|
34116
|
-
console.log(
|
|
34507
|
+
console.log(chalk4.bold(` ${weekday} ${day}`));
|
|
34117
34508
|
for (const t of completed)
|
|
34118
|
-
console.log(` ${
|
|
34509
|
+
console.log(` ${chalk4.green("\u2713")} ${chalk4.cyan(t.short_id || t.id.slice(0, 8))} ${t.title}${t.assigned_to ? chalk4.dim(` \u2014 ${t.assigned_to}`) : ""}`);
|
|
34119
34510
|
for (const t of started)
|
|
34120
|
-
console.log(` ${
|
|
34511
|
+
console.log(` ${chalk4.blue("\u25B6")} ${chalk4.cyan(t.short_id || t.id.slice(0, 8))} ${t.title}${t.assigned_to ? chalk4.dim(` \u2014 ${t.assigned_to}`) : ""}`);
|
|
34121
34512
|
}
|
|
34122
34513
|
if (tasks.length === 0)
|
|
34123
|
-
console.log(
|
|
34514
|
+
console.log(chalk4.dim(" No activity this week."));
|
|
34124
34515
|
});
|
|
34125
34516
|
program2.command("burndown").description("Show task completion velocity over the past 7 days").option("--days <n>", "Number of days", "7").option("-j, --json", "Output as JSON").action(async (opts) => {
|
|
34126
34517
|
const globalOpts = program2.opts();
|
|
@@ -34148,20 +34539,20 @@ program2.command("burndown").description("Show task completion velocity over the
|
|
|
34148
34539
|
}
|
|
34149
34540
|
const maxVal = Math.max(...dayStats.map((d) => Math.max(d.completed, d.created)), 1);
|
|
34150
34541
|
const barWidth = 30;
|
|
34151
|
-
console.log(
|
|
34542
|
+
console.log(chalk4.bold("Burndown (last " + numDays + ` days):
|
|
34152
34543
|
`));
|
|
34153
|
-
console.log(
|
|
34544
|
+
console.log(chalk4.dim(" Date Done New Failed Chart"));
|
|
34154
34545
|
for (const day of dayStats) {
|
|
34155
34546
|
const weekday = new Date(day.date + "T12:00:00").toLocaleDateString("en-US", { weekday: "short" });
|
|
34156
|
-
const completedBar =
|
|
34157
|
-
const createdBar =
|
|
34158
|
-
const failed = day.failed > 0 ?
|
|
34159
|
-
console.log(` ${weekday} ${day.date.slice(5)} ${
|
|
34547
|
+
const completedBar = chalk4.green("\u2588".repeat(Math.round(day.completed / maxVal * barWidth)));
|
|
34548
|
+
const createdBar = chalk4.blue("\u2591".repeat(Math.round(day.created / maxVal * barWidth)));
|
|
34549
|
+
const failed = day.failed > 0 ? chalk4.red(String(day.failed).padStart(4)) : chalk4.dim(" 0");
|
|
34550
|
+
console.log(` ${weekday} ${day.date.slice(5)} ${chalk4.green(String(day.completed).padStart(4))} ${chalk4.blue(String(day.created).padStart(4))} ${failed} ${completedBar}${createdBar}`);
|
|
34160
34551
|
}
|
|
34161
34552
|
const totalCompleted = dayStats.reduce((s, d) => s + d.completed, 0);
|
|
34162
34553
|
const totalCreated = dayStats.reduce((s, d) => s + d.created, 0);
|
|
34163
34554
|
const velocity = (totalCompleted / numDays).toFixed(1);
|
|
34164
|
-
console.log(
|
|
34555
|
+
console.log(chalk4.dim(`
|
|
34165
34556
|
Velocity: ${velocity}/day \xB7 ${totalCompleted} done \xB7 ${totalCreated} created`));
|
|
34166
34557
|
});
|
|
34167
34558
|
program2.command("log").description("Show recent task activity log (git-log style)").option("--limit <n>", "Number of entries", "30").option("-j, --json", "Output as JSON").action(async (opts) => {
|
|
@@ -34174,38 +34565,38 @@ program2.command("log").description("Show recent task activity log (git-log styl
|
|
|
34174
34565
|
return;
|
|
34175
34566
|
}
|
|
34176
34567
|
if (entries.length === 0) {
|
|
34177
|
-
console.log(
|
|
34568
|
+
console.log(chalk4.dim(" No activity yet."));
|
|
34178
34569
|
return;
|
|
34179
34570
|
}
|
|
34180
34571
|
const actionIcons = {
|
|
34181
|
-
create:
|
|
34182
|
-
start:
|
|
34183
|
-
complete:
|
|
34184
|
-
fail:
|
|
34185
|
-
update:
|
|
34186
|
-
approve:
|
|
34187
|
-
lock:
|
|
34188
|
-
unlock:
|
|
34572
|
+
create: chalk4.green("+"),
|
|
34573
|
+
start: chalk4.blue("\u25B6"),
|
|
34574
|
+
complete: chalk4.green("\u2713"),
|
|
34575
|
+
fail: chalk4.red("\u2717"),
|
|
34576
|
+
update: chalk4.yellow("~"),
|
|
34577
|
+
approve: chalk4.green("\u2605"),
|
|
34578
|
+
lock: chalk4.dim("\uD83D\uDD12"),
|
|
34579
|
+
unlock: chalk4.dim("\uD83D\uDD13")
|
|
34189
34580
|
};
|
|
34190
34581
|
let lastDate = "";
|
|
34191
34582
|
for (const e of entries) {
|
|
34192
34583
|
const date = e.created_at.slice(0, 10);
|
|
34193
34584
|
const time = e.created_at.slice(11, 16);
|
|
34194
34585
|
if (date !== lastDate) {
|
|
34195
|
-
console.log(
|
|
34586
|
+
console.log(chalk4.bold(`
|
|
34196
34587
|
${date}`));
|
|
34197
34588
|
lastDate = date;
|
|
34198
34589
|
}
|
|
34199
|
-
const icon = actionIcons[e.action] ||
|
|
34200
|
-
const agent = e.agent_id ?
|
|
34201
|
-
const taskRef =
|
|
34590
|
+
const icon = actionIcons[e.action] || chalk4.dim("\xB7");
|
|
34591
|
+
const agent = e.agent_id ? chalk4.dim(` (${e.agent_id})`) : "";
|
|
34592
|
+
const taskRef = chalk4.cyan(e.task_id.slice(0, 8));
|
|
34202
34593
|
let detail = "";
|
|
34203
34594
|
if (e.field && e.old_value && e.new_value) {
|
|
34204
|
-
detail =
|
|
34595
|
+
detail = chalk4.dim(` ${e.field}: ${e.old_value} \u2192 ${e.new_value}`);
|
|
34205
34596
|
} else if (e.field && e.new_value) {
|
|
34206
|
-
detail =
|
|
34597
|
+
detail = chalk4.dim(` ${e.field}: ${e.new_value}`);
|
|
34207
34598
|
}
|
|
34208
|
-
console.log(` ${
|
|
34599
|
+
console.log(` ${chalk4.dim(time)} ${icon} ${e.action.padEnd(8)} ${taskRef}${detail}${agent}`);
|
|
34209
34600
|
}
|
|
34210
34601
|
});
|
|
34211
34602
|
program2.command("ready").description("Show all tasks ready to be claimed (pending, unblocked, unlocked)").option("-j, --json", "Output as JSON").option("--project <id>", "Filter to project").option("--limit <n>", "Max tasks to show", "20").action(async (opts) => {
|
|
@@ -34230,15 +34621,15 @@ program2.command("ready").description("Show all tasks ready to be claimed (pendi
|
|
|
34230
34621
|
return;
|
|
34231
34622
|
}
|
|
34232
34623
|
if (limited.length === 0) {
|
|
34233
|
-
console.log(
|
|
34624
|
+
console.log(chalk4.dim(" No tasks ready to claim."));
|
|
34234
34625
|
return;
|
|
34235
34626
|
}
|
|
34236
|
-
console.log(
|
|
34627
|
+
console.log(chalk4.bold(`Ready to claim (${ready.length}${ready.length > limited.length ? `, showing ${limited.length}` : ""}):
|
|
34237
34628
|
`));
|
|
34238
34629
|
for (const t of limited) {
|
|
34239
|
-
const pri = t.priority === "critical" ?
|
|
34240
|
-
const due = t.due_at ?
|
|
34241
|
-
console.log(` ${
|
|
34630
|
+
const pri = t.priority === "critical" ? chalk4.bgRed.white(" CRIT ") : t.priority === "high" ? chalk4.red("[high]") : t.priority === "medium" ? chalk4.yellow("[med]") : "";
|
|
34631
|
+
const due = t.due_at ? chalk4.dim(` due ${t.due_at.slice(0, 10)}`) : "";
|
|
34632
|
+
console.log(` ${chalk4.cyan(t.short_id || t.id.slice(0, 8))} ${t.title} ${pri}${due}`);
|
|
34242
34633
|
}
|
|
34243
34634
|
});
|
|
34244
34635
|
program2.command("sprint").description("Sprint dashboard: in-progress, next up, blockers, and overdue").option("-j, --json", "Output as JSON").option("--project <id>", "Filter to project").action(async (opts) => {
|
|
@@ -34265,41 +34656,41 @@ program2.command("sprint").description("Sprint dashboard: in-progress, next up,
|
|
|
34265
34656
|
console.log(JSON.stringify({ in_progress: inProgress, next_up: nextUp, blocked, overdue }));
|
|
34266
34657
|
return;
|
|
34267
34658
|
}
|
|
34268
|
-
console.log(
|
|
34659
|
+
console.log(chalk4.bold(`Sprint Dashboard
|
|
34269
34660
|
`));
|
|
34270
|
-
console.log(
|
|
34661
|
+
console.log(chalk4.blue(` \u25B6 In Progress (${inProgress.length}):`));
|
|
34271
34662
|
if (inProgress.length === 0)
|
|
34272
|
-
console.log(
|
|
34663
|
+
console.log(chalk4.dim(" (none)"));
|
|
34273
34664
|
for (const t of inProgress) {
|
|
34274
|
-
const agent = t.assigned_to ?
|
|
34275
|
-
console.log(` ${
|
|
34665
|
+
const agent = t.assigned_to ? chalk4.dim(` \u2014 ${t.assigned_to}`) : "";
|
|
34666
|
+
console.log(` ${chalk4.cyan(t.short_id || t.id.slice(0, 8))} ${t.title}${agent}`);
|
|
34276
34667
|
}
|
|
34277
|
-
console.log(
|
|
34668
|
+
console.log(chalk4.white(`
|
|
34278
34669
|
\u25CB Next Up (${nextUp.length}):`));
|
|
34279
34670
|
if (nextUp.length === 0)
|
|
34280
|
-
console.log(
|
|
34671
|
+
console.log(chalk4.dim(" (none)"));
|
|
34281
34672
|
for (const t of nextUp) {
|
|
34282
|
-
const pri = t.priority === "critical" ?
|
|
34283
|
-
console.log(` ${
|
|
34673
|
+
const pri = t.priority === "critical" ? chalk4.bgRed.white(" CRIT ") : t.priority === "high" ? chalk4.red("[high]") : "";
|
|
34674
|
+
console.log(` ${chalk4.cyan(t.short_id || t.id.slice(0, 8))} ${t.title} ${pri}`);
|
|
34284
34675
|
}
|
|
34285
34676
|
if (blocked.length > 0) {
|
|
34286
|
-
console.log(
|
|
34677
|
+
console.log(chalk4.red(`
|
|
34287
34678
|
\u2298 Blocked (${blocked.length}):`));
|
|
34288
34679
|
for (const { task, blockers } of blocked) {
|
|
34289
|
-
console.log(` ${
|
|
34680
|
+
console.log(` ${chalk4.cyan(task.short_id || task.id.slice(0, 8))} ${task.title}`);
|
|
34290
34681
|
for (const bl of blockers)
|
|
34291
|
-
console.log(` ${
|
|
34682
|
+
console.log(` ${chalk4.dim("\u2190 " + (bl.short_id || bl.id.slice(0, 8)) + " " + bl.title)} ${chalk4.yellow(`[${bl.status}]`)}`);
|
|
34292
34683
|
}
|
|
34293
34684
|
}
|
|
34294
34685
|
if (overdue.length > 0) {
|
|
34295
|
-
console.log(
|
|
34686
|
+
console.log(chalk4.red(`
|
|
34296
34687
|
\u26A0 Overdue (${overdue.length}):`));
|
|
34297
34688
|
for (const t of overdue) {
|
|
34298
34689
|
const daysOver = Math.floor((Date.now() - new Date(t.due_at).getTime()) / 86400000);
|
|
34299
|
-
console.log(` ${
|
|
34690
|
+
console.log(` ${chalk4.red(`${daysOver}d`)} ${chalk4.cyan(t.short_id || t.id.slice(0, 8))} ${t.title}`);
|
|
34300
34691
|
}
|
|
34301
34692
|
}
|
|
34302
|
-
console.log(
|
|
34693
|
+
console.log(chalk4.dim(`
|
|
34303
34694
|
${inProgress.length} active \xB7 ${pending.length} pending \xB7 ${blocked.length} blocked \xB7 ${overdue.length} overdue`));
|
|
34304
34695
|
});
|
|
34305
34696
|
program2.command("handoff").description("Create or view agent session handoffs").option("--create", "Create a new handoff").option("--agent <name>", "Agent name").option("--summary <text>", "Handoff summary").option("--completed <items>", "Comma-separated completed items").option("--in-progress <items>", "Comma-separated in-progress items").option("--blockers <items>", "Comma-separated blockers").option("--next <items>", "Comma-separated next steps").option("-j, --json", "Output as JSON").option("--limit <n>", "Number of handoffs to show", "5").action(async (opts) => {
|
|
@@ -34309,7 +34700,7 @@ program2.command("handoff").description("Create or view agent session handoffs")
|
|
|
34309
34700
|
const projectId = autoProject(globalOpts) || undefined;
|
|
34310
34701
|
if (opts.create || opts.summary) {
|
|
34311
34702
|
if (!opts.summary) {
|
|
34312
|
-
console.error(
|
|
34703
|
+
console.error(chalk4.red(" --summary is required for creating a handoff"));
|
|
34313
34704
|
process.exit(1);
|
|
34314
34705
|
}
|
|
34315
34706
|
const handoff = createHandoff2({
|
|
@@ -34325,7 +34716,7 @@ program2.command("handoff").description("Create or view agent session handoffs")
|
|
|
34325
34716
|
console.log(JSON.stringify(handoff));
|
|
34326
34717
|
return;
|
|
34327
34718
|
}
|
|
34328
|
-
console.log(
|
|
34719
|
+
console.log(chalk4.green(` \u2713 Handoff created by ${handoff.agent_id || "unknown"}`));
|
|
34329
34720
|
return;
|
|
34330
34721
|
}
|
|
34331
34722
|
const handoffs = listHandoffs2(projectId, parseInt(opts.limit, 10), db);
|
|
@@ -34334,31 +34725,31 @@ program2.command("handoff").description("Create or view agent session handoffs")
|
|
|
34334
34725
|
return;
|
|
34335
34726
|
}
|
|
34336
34727
|
if (handoffs.length === 0) {
|
|
34337
|
-
console.log(
|
|
34728
|
+
console.log(chalk4.dim(" No handoffs yet."));
|
|
34338
34729
|
return;
|
|
34339
34730
|
}
|
|
34340
34731
|
for (const h of handoffs) {
|
|
34341
34732
|
const time = h.created_at.slice(0, 16).replace("T", " ");
|
|
34342
|
-
console.log(
|
|
34733
|
+
console.log(chalk4.bold(`
|
|
34343
34734
|
${time} ${h.agent_id || "unknown"}`));
|
|
34344
34735
|
console.log(` ${h.summary}`);
|
|
34345
34736
|
if (h.completed?.length) {
|
|
34346
|
-
console.log(
|
|
34737
|
+
console.log(chalk4.green(` \u2713 Completed:`));
|
|
34347
34738
|
for (const c of h.completed)
|
|
34348
34739
|
console.log(` - ${c}`);
|
|
34349
34740
|
}
|
|
34350
34741
|
if (h.in_progress?.length) {
|
|
34351
|
-
console.log(
|
|
34742
|
+
console.log(chalk4.blue(` \u25B6 In progress:`));
|
|
34352
34743
|
for (const c of h.in_progress)
|
|
34353
34744
|
console.log(` - ${c}`);
|
|
34354
34745
|
}
|
|
34355
34746
|
if (h.blockers?.length) {
|
|
34356
|
-
console.log(
|
|
34747
|
+
console.log(chalk4.red(` \u2298 Blockers:`));
|
|
34357
34748
|
for (const c of h.blockers)
|
|
34358
34749
|
console.log(` - ${c}`);
|
|
34359
34750
|
}
|
|
34360
34751
|
if (h.next_steps?.length) {
|
|
34361
|
-
console.log(
|
|
34752
|
+
console.log(chalk4.cyan(` \u2192 Next steps:`));
|
|
34362
34753
|
for (const c of h.next_steps)
|
|
34363
34754
|
console.log(` - ${c}`);
|
|
34364
34755
|
}
|
|
@@ -34384,16 +34775,16 @@ program2.command("priorities").description("Show task counts grouped by priority
|
|
|
34384
34775
|
console.log(JSON.stringify(counts));
|
|
34385
34776
|
return;
|
|
34386
34777
|
}
|
|
34387
|
-
console.log(
|
|
34778
|
+
console.log(chalk4.bold(`Priority Breakdown:
|
|
34388
34779
|
`));
|
|
34389
|
-
const priColors = { critical:
|
|
34780
|
+
const priColors = { critical: chalk4.bgRed.white, high: chalk4.red, medium: chalk4.yellow, low: chalk4.blue, none: chalk4.dim };
|
|
34390
34781
|
for (const p of priorities) {
|
|
34391
34782
|
const c = counts[p];
|
|
34392
34783
|
if (!c || c.total === 0)
|
|
34393
34784
|
continue;
|
|
34394
|
-
const color = priColors[p] ||
|
|
34395
|
-
const bar =
|
|
34396
|
-
console.log(` ${color(p.padEnd(9))} ${String(c.total).padStart(4)} total ${
|
|
34785
|
+
const color = priColors[p] || chalk4.white;
|
|
34786
|
+
const bar = chalk4.green("\u2588".repeat(Math.min(c.completed, 30))) + chalk4.blue("\u2591".repeat(Math.min(c.in_progress, 10))) + chalk4.dim("\xB7".repeat(Math.min(c.pending, 20)));
|
|
34787
|
+
console.log(` ${color(p.padEnd(9))} ${String(c.total).padStart(4)} total ${chalk4.green(String(c.completed).padStart(3))} done ${chalk4.blue(String(c.in_progress).padStart(3))} active ${chalk4.dim(String(c.pending).padStart(3))} pending ${bar}`);
|
|
34397
34788
|
}
|
|
34398
34789
|
});
|
|
34399
34790
|
program2.command("context").description("Session start context: status, latest handoff, next task, overdue").option("--agent <name>", "Agent name for handoff lookup").option("-j, --json", "Output as JSON").action(async (opts) => {
|
|
@@ -34413,21 +34804,21 @@ program2.command("context").description("Session start context: status, latest h
|
|
|
34413
34804
|
console.log(JSON.stringify({ status, next_task: nextTask, overdue_count: overdue.length, latest_handoff: handoff, as_of: new Date().toISOString() }));
|
|
34414
34805
|
return;
|
|
34415
34806
|
}
|
|
34416
|
-
console.log(
|
|
34807
|
+
console.log(chalk4.bold(`Session Context
|
|
34417
34808
|
`));
|
|
34418
34809
|
console.log(` ${status.pending} pending \xB7 ${status.in_progress} active \xB7 ${status.completed} done \xB7 ${status.total} total`);
|
|
34419
34810
|
if (status.stale_count > 0)
|
|
34420
|
-
console.log(
|
|
34811
|
+
console.log(chalk4.yellow(` \u26A0 ${status.stale_count} stale tasks`));
|
|
34421
34812
|
if (overdue.length > 0)
|
|
34422
|
-
console.log(
|
|
34813
|
+
console.log(chalk4.red(` \u26A0 ${overdue.length} overdue tasks`));
|
|
34423
34814
|
if (nextTask) {
|
|
34424
|
-
const pri = nextTask.priority === "critical" || nextTask.priority === "high" ?
|
|
34425
|
-
console.log(
|
|
34815
|
+
const pri = nextTask.priority === "critical" || nextTask.priority === "high" ? chalk4.red(` [${nextTask.priority}]`) : "";
|
|
34816
|
+
console.log(chalk4.bold(`
|
|
34426
34817
|
Next up:`));
|
|
34427
|
-
console.log(` ${
|
|
34818
|
+
console.log(` ${chalk4.cyan(nextTask.short_id || nextTask.id.slice(0, 8))} ${nextTask.title}${pri}`);
|
|
34428
34819
|
}
|
|
34429
34820
|
if (handoff) {
|
|
34430
|
-
console.log(
|
|
34821
|
+
console.log(chalk4.bold(`
|
|
34431
34822
|
Last handoff (${handoff.agent_id || "unknown"}, ${handoff.created_at.slice(0, 16).replace("T", " ")}):`));
|
|
34432
34823
|
console.log(` ${handoff.summary}`);
|
|
34433
34824
|
if (handoff.next_steps?.length) {
|
|
@@ -34435,7 +34826,7 @@ program2.command("context").description("Session start context: status, latest h
|
|
|
34435
34826
|
console.log(` \u2192 ${s}`);
|
|
34436
34827
|
}
|
|
34437
34828
|
}
|
|
34438
|
-
console.log(
|
|
34829
|
+
console.log(chalk4.dim(`
|
|
34439
34830
|
as_of: ${new Date().toISOString()}`));
|
|
34440
34831
|
});
|
|
34441
34832
|
program2.command("report-failure").description("Create a task from a test/build/typecheck failure and auto-assign it").requiredOption("--error <message>", "Error message or summary").option("--type <type>", "Failure type: test, build, typecheck, runtime, other", "test").option("--file <path>", "File where failure occurred").option("--stack <trace>", "Stack trace or detailed output").option("--title <title>", "Custom task title (auto-generated if omitted)").option("--priority <p>", "Priority: low, medium, high, critical").option("-j, --json", "Output as JSON").action(async (opts) => {
|
|
@@ -34474,11 +34865,11 @@ ${opts.stack.slice(0, 1500)}
|
|
|
34474
34865
|
console.log(JSON.stringify({ task_id: task.id, short_id: task.short_id, title: task.title, assigned_to: assignResult.agent_name, method: assignResult.method }));
|
|
34475
34866
|
return;
|
|
34476
34867
|
}
|
|
34477
|
-
console.log(
|
|
34868
|
+
console.log(chalk4.green(`\u2713 Created task ${task.short_id || task.id.slice(0, 8)}: ${task.title}`));
|
|
34478
34869
|
if (assignResult.agent_name) {
|
|
34479
|
-
console.log(
|
|
34870
|
+
console.log(chalk4.cyan(` Assigned to: ${assignResult.agent_name} (via ${assignResult.method})`));
|
|
34480
34871
|
if (assignResult.reason)
|
|
34481
|
-
console.log(
|
|
34872
|
+
console.log(chalk4.dim(` Reason: ${assignResult.reason}`));
|
|
34482
34873
|
}
|
|
34483
34874
|
});
|
|
34484
34875
|
program2.action(async () => {
|
|
@@ -34512,7 +34903,7 @@ dbCmd.command("migrate-pg").description("Apply PostgreSQL migrations to the conf
|
|
|
34512
34903
|
if (useJson) {
|
|
34513
34904
|
console.log(JSON.stringify({ error: msg }));
|
|
34514
34905
|
} else {
|
|
34515
|
-
console.error(
|
|
34906
|
+
console.error(chalk4.red(msg));
|
|
34516
34907
|
}
|
|
34517
34908
|
process.exit(1);
|
|
34518
34909
|
}
|
|
@@ -34525,26 +34916,26 @@ dbCmd.command("migrate-pg").description("Apply PostgreSQL migrations to the conf
|
|
|
34525
34916
|
return;
|
|
34526
34917
|
}
|
|
34527
34918
|
if (result.applied.length > 0) {
|
|
34528
|
-
console.log(
|
|
34919
|
+
console.log(chalk4.green(`Applied ${result.applied.length} migration(s): ${result.applied.join(", ")}`));
|
|
34529
34920
|
}
|
|
34530
34921
|
if (result.alreadyApplied.length > 0) {
|
|
34531
|
-
console.log(
|
|
34922
|
+
console.log(chalk4.dim(`Already applied: ${result.alreadyApplied.length} migration(s)`));
|
|
34532
34923
|
}
|
|
34533
34924
|
if (result.errors.length > 0) {
|
|
34534
34925
|
for (const err of result.errors) {
|
|
34535
|
-
console.error(
|
|
34926
|
+
console.error(chalk4.red(` Error: ${err}`));
|
|
34536
34927
|
}
|
|
34537
34928
|
process.exit(1);
|
|
34538
34929
|
}
|
|
34539
34930
|
if (result.applied.length === 0 && result.errors.length === 0) {
|
|
34540
|
-
console.log(
|
|
34931
|
+
console.log(chalk4.dim("Schema is up to date."));
|
|
34541
34932
|
}
|
|
34542
34933
|
} catch (e) {
|
|
34543
34934
|
const msg = e instanceof Error ? e.message : String(e);
|
|
34544
34935
|
if (useJson) {
|
|
34545
34936
|
console.log(JSON.stringify({ error: msg }));
|
|
34546
34937
|
} else {
|
|
34547
|
-
console.error(
|
|
34938
|
+
console.error(chalk4.red(`Migration failed: ${msg}`));
|
|
34548
34939
|
}
|
|
34549
34940
|
process.exit(1);
|
|
34550
34941
|
}
|
|
@@ -34602,34 +34993,34 @@ cloudCmd.command("status").description("Show cloud config, connection health, ma
|
|
|
34602
34993
|
if (useJson) {
|
|
34603
34994
|
console.log(JSON.stringify(info, null, 2));
|
|
34604
34995
|
} else {
|
|
34605
|
-
console.log(
|
|
34996
|
+
console.log(chalk4.bold("Cloud Status"));
|
|
34606
34997
|
console.log(` Mode: ${info.mode}`);
|
|
34607
34998
|
console.log(` Machine: ${machineId}`);
|
|
34608
34999
|
console.log(` RDS Host: ${info.rds_host}`);
|
|
34609
35000
|
if (info.postgresql)
|
|
34610
35001
|
console.log(` PostgreSQL: ${info.postgresql}`);
|
|
34611
35002
|
if (machines.length > 0) {
|
|
34612
|
-
console.log(
|
|
35003
|
+
console.log(chalk4.bold(`
|
|
34613
35004
|
Machines`));
|
|
34614
35005
|
for (const m of machines) {
|
|
34615
|
-
const current = m.id === machineId ?
|
|
35006
|
+
const current = m.id === machineId ? chalk4.green(" (this)") : "";
|
|
34616
35007
|
console.log(` ${m.name}${current} \u2014 ${m.hostname || "?"} / ${m.platform || "?"} \u2014 last seen ${m.last_seen_at}`);
|
|
34617
35008
|
}
|
|
34618
35009
|
}
|
|
34619
35010
|
const healthItems = info.sync_health.filter((s) => s.total > 0);
|
|
34620
35011
|
if (healthItems.length > 0) {
|
|
34621
|
-
console.log(
|
|
35012
|
+
console.log(chalk4.bold(`
|
|
34622
35013
|
Sync Health`));
|
|
34623
35014
|
for (const s of healthItems) {
|
|
34624
35015
|
const pct = s.total > 0 ? Math.round((s.total - s.unsynced) / s.total * 100) : 100;
|
|
34625
|
-
const color = pct === 100 ?
|
|
35016
|
+
const color = pct === 100 ? chalk4.green : pct > 50 ? chalk4.yellow : chalk4.red;
|
|
34626
35017
|
console.log(` ${s.table}: ${color(`${pct}%`)} synced (${s.unsynced} unsynced / ${s.total} total)${s.last_synced ? ` \u2014 last: ${s.last_synced}` : ""}`);
|
|
34627
35018
|
}
|
|
34628
35019
|
}
|
|
34629
35020
|
if (info.conflicts_unresolved > 0) {
|
|
34630
|
-
console.log(
|
|
35021
|
+
console.log(chalk4.bold(`
|
|
34631
35022
|
Conflicts`));
|
|
34632
|
-
console.log(` ${
|
|
35023
|
+
console.log(` ${chalk4.yellow(`${info.conflicts_unresolved} unresolved`)} \u2014 run \`todos cloud conflicts\` to review`);
|
|
34633
35024
|
}
|
|
34634
35025
|
}
|
|
34635
35026
|
} catch (e) {
|
|
@@ -34637,7 +35028,7 @@ Conflicts`));
|
|
|
34637
35028
|
if (useJson) {
|
|
34638
35029
|
console.log(JSON.stringify({ error: msg }));
|
|
34639
35030
|
} else {
|
|
34640
|
-
console.error(
|
|
35031
|
+
console.error(chalk4.red(msg));
|
|
34641
35032
|
}
|
|
34642
35033
|
}
|
|
34643
35034
|
});
|
|
@@ -34654,7 +35045,7 @@ cloudCmd.command("push").description("Push local data to cloud PostgreSQL").opti
|
|
|
34654
35045
|
if (useJson) {
|
|
34655
35046
|
console.log(JSON.stringify({ error: msg }));
|
|
34656
35047
|
} else {
|
|
34657
|
-
console.error(
|
|
35048
|
+
console.error(chalk4.red(msg));
|
|
34658
35049
|
}
|
|
34659
35050
|
process.exit(1);
|
|
34660
35051
|
}
|
|
@@ -34689,14 +35080,14 @@ cloudCmd.command("push").description("Push local data to cloud PostgreSQL").opti
|
|
|
34689
35080
|
if (useJson) {
|
|
34690
35081
|
console.log(JSON.stringify({ total, machine_id: machineId, tables: results }));
|
|
34691
35082
|
} else {
|
|
34692
|
-
console.log(
|
|
35083
|
+
console.log(chalk4.green(`Done. ${total} rows pushed (machine: ${machineId}).`));
|
|
34693
35084
|
}
|
|
34694
35085
|
} catch (e) {
|
|
34695
35086
|
const msg = e instanceof Error ? e.message : String(e);
|
|
34696
35087
|
if (useJson) {
|
|
34697
35088
|
console.log(JSON.stringify({ error: msg }));
|
|
34698
35089
|
} else {
|
|
34699
|
-
console.error(
|
|
35090
|
+
console.error(chalk4.red(msg));
|
|
34700
35091
|
}
|
|
34701
35092
|
process.exit(1);
|
|
34702
35093
|
}
|
|
@@ -34713,7 +35104,7 @@ cloudCmd.command("pull").description("Pull cloud data to local \u2014 merges by
|
|
|
34713
35104
|
if (useJson) {
|
|
34714
35105
|
console.log(JSON.stringify({ error: msg }));
|
|
34715
35106
|
} else {
|
|
34716
|
-
console.error(
|
|
35107
|
+
console.error(chalk4.red(msg));
|
|
34717
35108
|
}
|
|
34718
35109
|
process.exit(1);
|
|
34719
35110
|
}
|
|
@@ -34747,14 +35138,14 @@ cloudCmd.command("pull").description("Pull cloud data to local \u2014 merges by
|
|
|
34747
35138
|
if (useJson) {
|
|
34748
35139
|
console.log(JSON.stringify({ total, tables: results }));
|
|
34749
35140
|
} else {
|
|
34750
|
-
console.log(
|
|
35141
|
+
console.log(chalk4.green(`Done. ${total} rows pulled.`));
|
|
34751
35142
|
}
|
|
34752
35143
|
} catch (e) {
|
|
34753
35144
|
const msg = e instanceof Error ? e.message : String(e);
|
|
34754
35145
|
if (useJson) {
|
|
34755
35146
|
console.log(JSON.stringify({ error: msg }));
|
|
34756
35147
|
} else {
|
|
34757
|
-
console.error(
|
|
35148
|
+
console.error(chalk4.red(msg));
|
|
34758
35149
|
}
|
|
34759
35150
|
process.exit(1);
|
|
34760
35151
|
}
|
|
@@ -34772,7 +35163,7 @@ cloudCmd.command("sync").description("Bidirectional sync \u2014 pull remote chan
|
|
|
34772
35163
|
if (useJson) {
|
|
34773
35164
|
console.log(JSON.stringify({ error: msg }));
|
|
34774
35165
|
} else {
|
|
34775
|
-
console.error(
|
|
35166
|
+
console.error(chalk4.red(msg));
|
|
34776
35167
|
}
|
|
34777
35168
|
process.exit(1);
|
|
34778
35169
|
}
|
|
@@ -34788,7 +35179,7 @@ cloudCmd.command("sync").description("Bidirectional sync \u2014 pull remote chan
|
|
|
34788
35179
|
tableList = [...new Set([...localTables, ...remoteTables])];
|
|
34789
35180
|
}
|
|
34790
35181
|
if (!useJson)
|
|
34791
|
-
console.log(
|
|
35182
|
+
console.log(chalk4.bold("Pulling..."));
|
|
34792
35183
|
const pullResults = await syncPull2(cloud, local, {
|
|
34793
35184
|
tables: tableList,
|
|
34794
35185
|
onProgress: (p) => {
|
|
@@ -34803,7 +35194,7 @@ cloudCmd.command("sync").description("Bidirectional sync \u2014 pull remote chan
|
|
|
34803
35194
|
} catch {}
|
|
34804
35195
|
}
|
|
34805
35196
|
if (!useJson)
|
|
34806
|
-
console.log(
|
|
35197
|
+
console.log(chalk4.bold("Pushing..."));
|
|
34807
35198
|
const pushResults = await syncPush2(local, cloud, {
|
|
34808
35199
|
tables: tableList,
|
|
34809
35200
|
onProgress: (p) => {
|
|
@@ -34823,14 +35214,14 @@ cloudCmd.command("sync").description("Bidirectional sync \u2014 pull remote chan
|
|
|
34823
35214
|
if (useJson) {
|
|
34824
35215
|
console.log(JSON.stringify({ pulled: pullTotal, pushed: pushTotal, machine_id: machineId, tables: tableList.length }));
|
|
34825
35216
|
} else {
|
|
34826
|
-
console.log(
|
|
35217
|
+
console.log(chalk4.green(`Done. Pulled ${pullTotal}, pushed ${pushTotal} rows across ${tableList.length} table(s) (machine: ${machineId}).`));
|
|
34827
35218
|
}
|
|
34828
35219
|
} catch (e) {
|
|
34829
35220
|
const msg = e instanceof Error ? e.message : String(e);
|
|
34830
35221
|
if (useJson) {
|
|
34831
35222
|
console.log(JSON.stringify({ error: msg }));
|
|
34832
35223
|
} else {
|
|
34833
|
-
console.error(
|
|
35224
|
+
console.error(chalk4.red(msg));
|
|
34834
35225
|
}
|
|
34835
35226
|
process.exit(1);
|
|
34836
35227
|
}
|
|
@@ -34851,17 +35242,17 @@ cloudCmd.command("conflicts").description("List sync conflicts detected during p
|
|
|
34851
35242
|
return;
|
|
34852
35243
|
}
|
|
34853
35244
|
if (shown.length === 0) {
|
|
34854
|
-
console.log(
|
|
35245
|
+
console.log(chalk4.dim(opts.resolved ? "No resolved conflicts." : "No unresolved conflicts."));
|
|
34855
35246
|
return;
|
|
34856
35247
|
}
|
|
34857
35248
|
console.log(`${conflicts.length} conflict(s)${conflicts.length > shown.length ? ` (showing ${shown.length})` : ""}:
|
|
34858
35249
|
`);
|
|
34859
35250
|
for (const c of shown) {
|
|
34860
|
-
console.log(
|
|
35251
|
+
console.log(chalk4.yellow(`[${c.id}]`) + ` ${c.table_name}/${c.row_id}`);
|
|
34861
35252
|
console.log(` Local: ${c.local_updated_at}`);
|
|
34862
35253
|
console.log(` Remote: ${c.remote_updated_at}`);
|
|
34863
35254
|
if (c.resolution)
|
|
34864
|
-
console.log(` Resolution: ${
|
|
35255
|
+
console.log(` Resolution: ${chalk4.green(c.resolution)} at ${c.resolved_at}`);
|
|
34865
35256
|
console.log();
|
|
34866
35257
|
}
|
|
34867
35258
|
} catch (e) {
|
|
@@ -34869,9 +35260,10 @@ cloudCmd.command("conflicts").description("List sync conflicts detected during p
|
|
|
34869
35260
|
if (useJson) {
|
|
34870
35261
|
console.log(JSON.stringify({ error: msg }));
|
|
34871
35262
|
} else {
|
|
34872
|
-
console.error(
|
|
35263
|
+
console.error(chalk4.red(msg));
|
|
34873
35264
|
}
|
|
34874
35265
|
}
|
|
34875
35266
|
});
|
|
34876
35267
|
registerDispatchCommands(program2);
|
|
35268
|
+
registerMachineCommands(program2);
|
|
34877
35269
|
program2.parse();
|