@hasna/todos 0.9.74 → 0.9.75

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -2079,6 +2079,19 @@ var require_commander = __commonJS((exports) => {
2079
2079
  });
2080
2080
 
2081
2081
  // src/db/database.ts
2082
+ var exports_database = {};
2083
+ __export(exports_database, {
2084
+ uuid: () => uuid,
2085
+ resolvePartialId: () => resolvePartialId,
2086
+ resetDatabase: () => resetDatabase,
2087
+ now: () => now,
2088
+ lockExpiryCutoff: () => lockExpiryCutoff,
2089
+ isLockExpired: () => isLockExpired,
2090
+ getDatabase: () => getDatabase,
2091
+ closeDatabase: () => closeDatabase,
2092
+ clearExpiredLocks: () => clearExpiredLocks,
2093
+ LOCK_EXPIRY_MINUTES: () => LOCK_EXPIRY_MINUTES
2094
+ });
2082
2095
  import { Database } from "bun:sqlite";
2083
2096
  import { existsSync, mkdirSync } from "fs";
2084
2097
  import { dirname, join, resolve } from "path";
@@ -2247,6 +2260,18 @@ function ensureSchema(db) {
2247
2260
  metadata TEXT DEFAULT '{}',
2248
2261
  created_at TEXT NOT NULL DEFAULT (datetime('now'))
2249
2262
  )`);
2263
+ ensureTable("project_sources", `
2264
+ CREATE TABLE project_sources (
2265
+ id TEXT PRIMARY KEY,
2266
+ project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
2267
+ type TEXT NOT NULL,
2268
+ name TEXT NOT NULL,
2269
+ uri TEXT NOT NULL,
2270
+ description TEXT,
2271
+ metadata TEXT DEFAULT '{}',
2272
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
2273
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
2274
+ )`);
2250
2275
  ensureColumn("projects", "task_list_id", "TEXT");
2251
2276
  ensureColumn("projects", "task_prefix", "TEXT");
2252
2277
  ensureColumn("projects", "task_counter", "INTEGER NOT NULL DEFAULT 0");
@@ -2291,6 +2316,8 @@ function ensureSchema(db) {
2291
2316
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_history_agent ON task_history(agent_id)");
2292
2317
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_recurrence_parent ON tasks(recurrence_parent_id)");
2293
2318
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_recurrence_rule ON tasks(recurrence_rule) WHERE recurrence_rule IS NOT NULL");
2319
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_project_sources_project ON project_sources(project_id)");
2320
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_project_sources_type ON project_sources(type)");
2294
2321
  }
2295
2322
  function backfillTaskTags(db) {
2296
2323
  try {
@@ -2321,6 +2348,15 @@ function backfillTaskTags(db) {
2321
2348
  }
2322
2349
  } catch {}
2323
2350
  }
2351
+ function closeDatabase() {
2352
+ if (_db) {
2353
+ _db.close();
2354
+ _db = null;
2355
+ }
2356
+ }
2357
+ function resetDatabase() {
2358
+ _db = null;
2359
+ }
2324
2360
  function now() {
2325
2361
  return new Date().toISOString();
2326
2362
  }
@@ -2668,6 +2704,36 @@ var init_database = __esm(() => {
2668
2704
  ALTER TABLE tasks ADD COLUMN reason TEXT;
2669
2705
  ALTER TABLE tasks ADD COLUMN spawned_from_session TEXT;
2670
2706
  INSERT OR IGNORE INTO _migrations (id) VALUES (19);
2707
+ `,
2708
+ `
2709
+ CREATE TABLE IF NOT EXISTS handoffs (
2710
+ id TEXT PRIMARY KEY,
2711
+ agent_id TEXT,
2712
+ project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,
2713
+ summary TEXT NOT NULL,
2714
+ completed TEXT,
2715
+ in_progress TEXT,
2716
+ blockers TEXT,
2717
+ next_steps TEXT,
2718
+ created_at TEXT NOT NULL
2719
+ );
2720
+ INSERT OR IGNORE INTO _migrations (id) VALUES (20);
2721
+ `,
2722
+ `
2723
+ CREATE TABLE IF NOT EXISTS project_sources (
2724
+ id TEXT PRIMARY KEY,
2725
+ project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
2726
+ type TEXT NOT NULL,
2727
+ name TEXT NOT NULL,
2728
+ uri TEXT NOT NULL,
2729
+ description TEXT,
2730
+ metadata TEXT DEFAULT '{}',
2731
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
2732
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
2733
+ );
2734
+ CREATE INDEX IF NOT EXISTS idx_project_sources_project ON project_sources(project_id);
2735
+ CREATE INDEX IF NOT EXISTS idx_project_sources_type ON project_sources(type);
2736
+ INSERT OR IGNORE INTO _migrations (id) VALUES (21);
2671
2737
  `
2672
2738
  ];
2673
2739
  });
@@ -2782,13 +2848,17 @@ var exports_projects = {};
2782
2848
  __export(exports_projects, {
2783
2849
  updateProject: () => updateProject,
2784
2850
  slugify: () => slugify,
2851
+ removeProjectSource: () => removeProjectSource,
2785
2852
  nextTaskShortId: () => nextTaskShortId,
2786
2853
  listProjects: () => listProjects,
2854
+ listProjectSources: () => listProjectSources,
2855
+ getProjectWithSources: () => getProjectWithSources,
2787
2856
  getProjectByPath: () => getProjectByPath,
2788
2857
  getProject: () => getProject,
2789
2858
  ensureProject: () => ensureProject,
2790
2859
  deleteProject: () => deleteProject,
2791
- createProject: () => createProject
2860
+ createProject: () => createProject,
2861
+ addProjectSource: () => addProjectSource
2792
2862
  });
2793
2863
  function slugify(name) {
2794
2864
  return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
@@ -2865,6 +2935,48 @@ function deleteProject(id, db) {
2865
2935
  const result = d.run("DELETE FROM projects WHERE id = ?", [id]);
2866
2936
  return result.changes > 0;
2867
2937
  }
2938
+ function rowToSource(row) {
2939
+ return {
2940
+ ...row,
2941
+ metadata: row.metadata ? JSON.parse(row.metadata) : {}
2942
+ };
2943
+ }
2944
+ function addProjectSource(input, db) {
2945
+ const d = db || getDatabase();
2946
+ const id = uuid();
2947
+ const timestamp = now();
2948
+ d.run(`INSERT INTO project_sources (id, project_id, type, name, uri, description, metadata, created_at, updated_at)
2949
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
2950
+ id,
2951
+ input.project_id,
2952
+ input.type,
2953
+ input.name,
2954
+ input.uri,
2955
+ input.description || null,
2956
+ JSON.stringify(input.metadata || {}),
2957
+ timestamp,
2958
+ timestamp
2959
+ ]);
2960
+ return rowToSource(d.query("SELECT * FROM project_sources WHERE id = ?").get(id));
2961
+ }
2962
+ function removeProjectSource(id, db) {
2963
+ const d = db || getDatabase();
2964
+ const result = d.run("DELETE FROM project_sources WHERE id = ?", [id]);
2965
+ return result.changes > 0;
2966
+ }
2967
+ function listProjectSources(projectId, db) {
2968
+ const d = db || getDatabase();
2969
+ const rows = d.query("SELECT * FROM project_sources WHERE project_id = ? ORDER BY name").all(projectId);
2970
+ return rows.map(rowToSource);
2971
+ }
2972
+ function getProjectWithSources(id, db) {
2973
+ const d = db || getDatabase();
2974
+ const project = getProject(id, d);
2975
+ if (!project)
2976
+ return null;
2977
+ project.sources = listProjectSources(id, d);
2978
+ return project;
2979
+ }
2868
2980
  function nextTaskShortId(projectId, db) {
2869
2981
  const d = db || getDatabase();
2870
2982
  const project = getProject(projectId, d);
@@ -3342,6 +3454,7 @@ __export(exports_tasks, {
3342
3454
  getTask: () => getTask,
3343
3455
  getStatus: () => getStatus,
3344
3456
  getStaleTasks: () => getStaleTasks,
3457
+ getOverdueTasks: () => getOverdueTasks,
3345
3458
  getNextTask: () => getNextTask,
3346
3459
  getBlockingDeps: () => getBlockingDeps,
3347
3460
  getActiveWork: () => getActiveWork,
@@ -4380,6 +4493,19 @@ function bulkUpdateTasks(taskIds, updates, db) {
4380
4493
  tx();
4381
4494
  return { updated, failed };
4382
4495
  }
4496
+ function getOverdueTasks(projectId, db) {
4497
+ const d = db || getDatabase();
4498
+ const nowStr = new Date().toISOString();
4499
+ let query = `SELECT * FROM tasks WHERE due_at IS NOT NULL AND due_at < ? AND status NOT IN ('completed', 'cancelled', 'failed')`;
4500
+ const params = [nowStr];
4501
+ if (projectId) {
4502
+ query += ` AND project_id = ?`;
4503
+ params.push(projectId);
4504
+ }
4505
+ query += ` ORDER BY due_at ASC`;
4506
+ const rows = d.query(query).all(...params);
4507
+ return rows.map(rowToTask);
4508
+ }
4383
4509
  var init_tasks = __esm(() => {
4384
4510
  init_types();
4385
4511
  init_database();
@@ -9368,6 +9494,77 @@ var init_zod = __esm(() => {
9368
9494
  init_external();
9369
9495
  });
9370
9496
 
9497
+ // src/db/handoffs.ts
9498
+ var exports_handoffs = {};
9499
+ __export(exports_handoffs, {
9500
+ listHandoffs: () => listHandoffs,
9501
+ getLatestHandoff: () => getLatestHandoff,
9502
+ createHandoff: () => createHandoff
9503
+ });
9504
+ function createHandoff(input, db) {
9505
+ const d = db || getDatabase();
9506
+ const id = uuid();
9507
+ const timestamp = now();
9508
+ d.run(`INSERT INTO handoffs (id, agent_id, project_id, summary, completed, in_progress, blockers, next_steps, created_at)
9509
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
9510
+ id,
9511
+ input.agent_id || null,
9512
+ input.project_id || null,
9513
+ input.summary,
9514
+ input.completed ? JSON.stringify(input.completed) : null,
9515
+ input.in_progress ? JSON.stringify(input.in_progress) : null,
9516
+ input.blockers ? JSON.stringify(input.blockers) : null,
9517
+ input.next_steps ? JSON.stringify(input.next_steps) : null,
9518
+ timestamp
9519
+ ]);
9520
+ return {
9521
+ id,
9522
+ agent_id: input.agent_id || null,
9523
+ project_id: input.project_id || null,
9524
+ summary: input.summary,
9525
+ completed: input.completed || null,
9526
+ in_progress: input.in_progress || null,
9527
+ blockers: input.blockers || null,
9528
+ next_steps: input.next_steps || null,
9529
+ created_at: timestamp
9530
+ };
9531
+ }
9532
+ function rowToHandoff(row) {
9533
+ return {
9534
+ ...row,
9535
+ completed: row.completed ? JSON.parse(row.completed) : null,
9536
+ in_progress: row.in_progress ? JSON.parse(row.in_progress) : null,
9537
+ blockers: row.blockers ? JSON.parse(row.blockers) : null,
9538
+ next_steps: row.next_steps ? JSON.parse(row.next_steps) : null
9539
+ };
9540
+ }
9541
+ function listHandoffs(projectId, limit = 10, db) {
9542
+ const d = db || getDatabase();
9543
+ if (projectId) {
9544
+ return d.query("SELECT * FROM handoffs WHERE project_id = ? ORDER BY rowid DESC LIMIT ?").all(projectId, limit).map(rowToHandoff);
9545
+ }
9546
+ return d.query("SELECT * FROM handoffs ORDER BY rowid DESC LIMIT ?").all(limit).map(rowToHandoff);
9547
+ }
9548
+ function getLatestHandoff(agentId, projectId, db) {
9549
+ const d = db || getDatabase();
9550
+ let query = "SELECT * FROM handoffs WHERE 1=1";
9551
+ const params = [];
9552
+ if (agentId) {
9553
+ query += " AND agent_id = ?";
9554
+ params.push(agentId);
9555
+ }
9556
+ if (projectId) {
9557
+ query += " AND project_id = ?";
9558
+ params.push(projectId);
9559
+ }
9560
+ query += " ORDER BY rowid DESC LIMIT 1";
9561
+ const row = d.query(query).get(...params);
9562
+ return row ? rowToHandoff(row) : null;
9563
+ }
9564
+ var init_handoffs = __esm(() => {
9565
+ init_database();
9566
+ });
9567
+
9371
9568
  // src/mcp/index.ts
9372
9569
  var exports_mcp = {};
9373
9570
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -9929,6 +10126,65 @@ ${text}` }] };
9929
10126
  }
9930
10127
  });
9931
10128
  }
10129
+ if (shouldRegisterTool("add_project_source")) {
10130
+ server.tool("add_project_source", "Add a data source to a project (S3 bucket, Google Drive folder, local path, GitHub repo, Notion page, etc.). Sources are revealed to agents when they load the project.", {
10131
+ project_id: exports_external.string().describe("Project ID"),
10132
+ type: exports_external.string().describe("Source type: 's3', 'gdrive', 'local', 'github', 'notion', 'http', or any custom label"),
10133
+ name: exports_external.string().describe("Human-readable label for this source"),
10134
+ uri: exports_external.string().describe("The source URI (bucket path, folder URL, local path, repo URL, etc.)"),
10135
+ description: exports_external.string().optional().describe("What this source contains or how agents should use it"),
10136
+ metadata: exports_external.record(exports_external.unknown()).optional().describe("Extra config (e.g. region, access role, subfolder)")
10137
+ }, async (params) => {
10138
+ try {
10139
+ const resolvedProjectId = resolveId(params.project_id, "projects");
10140
+ const source = addProjectSource({ ...params, project_id: resolvedProjectId });
10141
+ return {
10142
+ content: [{
10143
+ type: "text",
10144
+ text: `Source added: ${source.id.slice(0, 8)} | [${source.type}] ${source.name} \u2192 ${source.uri}`
10145
+ }]
10146
+ };
10147
+ } catch (e) {
10148
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
10149
+ }
10150
+ });
10151
+ }
10152
+ if (shouldRegisterTool("remove_project_source")) {
10153
+ server.tool("remove_project_source", "Remove a data source from a project by source ID.", {
10154
+ source_id: exports_external.string().describe("Source ID to remove")
10155
+ }, async ({ source_id }) => {
10156
+ try {
10157
+ const db = getDatabase();
10158
+ const row = db.query("SELECT * FROM project_sources WHERE id LIKE ?").get(`${source_id}%`);
10159
+ if (!row)
10160
+ return { content: [{ type: "text", text: `Source not found: ${source_id}` }], isError: true };
10161
+ removeProjectSource(row.id);
10162
+ return { content: [{ type: "text", text: `Source removed: ${row.name}` }] };
10163
+ } catch (e) {
10164
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
10165
+ }
10166
+ });
10167
+ }
10168
+ if (shouldRegisterTool("list_project_sources")) {
10169
+ server.tool("list_project_sources", "List all data sources attached to a project.", {
10170
+ project_id: exports_external.string().describe("Project ID")
10171
+ }, async ({ project_id }) => {
10172
+ try {
10173
+ const resolvedId = resolveId(project_id, "projects");
10174
+ const sources = listProjectSources(resolvedId);
10175
+ if (sources.length === 0) {
10176
+ return { content: [{ type: "text", text: "No sources configured for this project." }] };
10177
+ }
10178
+ const lines = sources.map((s) => `${s.id.slice(0, 8)} | [${s.type}] ${s.name} \u2192 ${s.uri}${s.description ? `
10179
+ ${s.description}` : ""}`);
10180
+ return { content: [{ type: "text", text: `${sources.length} source(s):
10181
+ ${lines.join(`
10182
+ `)}` }] };
10183
+ } catch (e) {
10184
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
10185
+ }
10186
+ });
10187
+ }
9932
10188
  if (shouldRegisterTool("create_plan")) {
9933
10189
  server.tool("create_plan", "Create a plan to group related tasks.", {
9934
10190
  name: exports_external.string(),
@@ -11245,6 +11501,19 @@ ${checks.map((c) => ` ${c.status === "ok" ? "\u2713" : "\u26A0"} ${c.name}: ${c
11245
11501
  lines.push(`Other agents active: ${others.slice(0, 3).map((w) => `${w.short_id || w.id.slice(0, 8)} (${w.assigned_to || "?"})`).join(", ")}`);
11246
11502
  }
11247
11503
  }
11504
+ if (project_id) {
11505
+ const resolvedId = resolveId(project_id, "projects");
11506
+ const sources = listProjectSources(resolvedId);
11507
+ if (sources.length > 0) {
11508
+ lines.push("");
11509
+ lines.push(`## Data Sources`);
11510
+ for (const s of sources) {
11511
+ lines.push(`[${s.type}] ${s.name}: ${s.uri}${s.description ? ` \u2014 ${s.description}` : ""}`);
11512
+ }
11513
+ }
11514
+ }
11515
+ lines.push(`
11516
+ as_of: ${new Date().toISOString()}`);
11248
11517
  return { content: [{ type: "text", text: lines.join(`
11249
11518
  `) }] };
11250
11519
  } catch (e) {
@@ -11295,6 +11564,9 @@ Claimed: ${formatTask(result.claimed)}`);
11295
11564
  "log_progress",
11296
11565
  "create_project",
11297
11566
  "list_projects",
11567
+ "add_project_source",
11568
+ "remove_project_source",
11569
+ "list_project_sources",
11298
11570
  "create_plan",
11299
11571
  "list_plans",
11300
11572
  "get_plan",
@@ -11403,6 +11675,15 @@ Claimed: ${formatTask(result.claimed)}`);
11403
11675
  Params: name(string, req), path(string, req \u2014 unique absolute path), description(string), task_list_id(string)
11404
11676
  Example: {name: 'my-app', path: '/Users/dev/my-app'}`,
11405
11677
  list_projects: "List all registered projects. No params.",
11678
+ add_project_source: `Add a data source to a project (S3, GDrive, local path, GitHub, Notion, HTTP, etc.).
11679
+ Params: project_id(string, req), type(string, req \u2014 e.g. 's3','gdrive','local','github','notion','http'), name(string, req), uri(string, req), description(string), metadata(object)
11680
+ Example: {project_id: 'a1b2c3d4', type: 's3', name: 'Assets bucket', uri: 's3://my-bucket/assets/', description: 'Project media files'}`,
11681
+ remove_project_source: `Remove a data source from a project.
11682
+ Params: source_id(string, req \u2014 source ID or prefix)
11683
+ Example: {source_id: 'abc12345'}`,
11684
+ list_project_sources: `List all data sources for a project.
11685
+ Params: project_id(string, req)
11686
+ Example: {project_id: 'a1b2c3d4'}`,
11406
11687
  create_plan: `Create a plan to group related tasks.
11407
11688
  Params: name(string, req), project_id(string), description(string), status(active|completed|archived, default:active), task_list_id(string), agent_id(string)
11408
11689
  Example: {name: 'Sprint 1', project_id: 'a1b2c3d4'}`,
@@ -11563,6 +11844,62 @@ Claimed: ${formatTask(result.claimed)}`);
11563
11844
  const agents = listAgents();
11564
11845
  return { contents: [{ uri: "todos://agents", text: JSON.stringify(agents, null, 2), mimeType: "application/json" }] };
11565
11846
  });
11847
+ if (shouldRegisterTool("create_handoff")) {
11848
+ server.tool("create_handoff", "Create a session handoff note for agent coordination.", {
11849
+ agent_id: exports_external.string().optional().describe("Agent creating the handoff"),
11850
+ project_id: exports_external.string().optional().describe("Project ID"),
11851
+ summary: exports_external.string().describe("What was accomplished this session"),
11852
+ completed: exports_external.array(exports_external.string()).optional().describe("Items completed"),
11853
+ in_progress: exports_external.array(exports_external.string()).optional().describe("Items still in progress"),
11854
+ blockers: exports_external.array(exports_external.string()).optional().describe("Blocking issues"),
11855
+ next_steps: exports_external.array(exports_external.string()).optional().describe("Recommended next actions")
11856
+ }, async ({ agent_id, project_id, summary, completed, in_progress, blockers, next_steps }) => {
11857
+ try {
11858
+ const { createHandoff: createHandoff2 } = (init_handoffs(), __toCommonJS(exports_handoffs));
11859
+ const handoff = createHandoff2({
11860
+ agent_id,
11861
+ project_id: project_id ? resolveId(project_id, "projects") : undefined,
11862
+ summary,
11863
+ completed,
11864
+ in_progress,
11865
+ blockers,
11866
+ next_steps
11867
+ });
11868
+ return { content: [{ type: "text", text: `Handoff created: ${handoff.id.slice(0, 8)} by ${handoff.agent_id || "unknown"}` }] };
11869
+ } catch (e) {
11870
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
11871
+ }
11872
+ });
11873
+ }
11874
+ if (shouldRegisterTool("get_latest_handoff")) {
11875
+ server.tool("get_latest_handoff", "Get the most recent handoff for an agent or project.", {
11876
+ agent_id: exports_external.string().optional().describe("Filter by agent"),
11877
+ project_id: exports_external.string().optional().describe("Filter by project")
11878
+ }, async ({ agent_id, project_id }) => {
11879
+ try {
11880
+ const { getLatestHandoff: getLatestHandoff2 } = (init_handoffs(), __toCommonJS(exports_handoffs));
11881
+ const handoff = getLatestHandoff2(agent_id, project_id ? resolveId(project_id, "projects") : undefined);
11882
+ if (!handoff)
11883
+ return { content: [{ type: "text", text: "No handoffs found." }] };
11884
+ const lines = [
11885
+ `${handoff.created_at.slice(0, 16)} ${handoff.agent_id || "unknown"}`,
11886
+ handoff.summary
11887
+ ];
11888
+ if (handoff.completed?.length)
11889
+ lines.push(`Done: ${handoff.completed.join(", ")}`);
11890
+ if (handoff.in_progress?.length)
11891
+ lines.push(`In progress: ${handoff.in_progress.join(", ")}`);
11892
+ if (handoff.blockers?.length)
11893
+ lines.push(`Blocked: ${handoff.blockers.join(", ")}`);
11894
+ if (handoff.next_steps?.length)
11895
+ lines.push(`Next: ${handoff.next_steps.join(", ")}`);
11896
+ return { content: [{ type: "text", text: lines.join(`
11897
+ `) }] };
11898
+ } catch (e) {
11899
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
11900
+ }
11901
+ });
11902
+ }
11566
11903
  server.resource("task-lists", "todos://task-lists", { description: "All task lists", mimeType: "application/json" }, async () => {
11567
11904
  const lists = listTaskLists();
11568
11905
  return { contents: [{ uri: "todos://task-lists", text: JSON.stringify(lists, null, 2), mimeType: "application/json" }] };
@@ -15749,6 +16086,480 @@ program2.command("yesterday").description("Show task activity from yesterday").o
15749
16086
  if (completed.length === 0 && started.length === 0)
15750
16087
  console.log(chalk.dim(" No activity yesterday."));
15751
16088
  });
16089
+ program2.command("mine").description("Show tasks assigned to you, grouped by status").argument("<agent>", "Agent name or ID").option("--json", "Output as JSON").action(async (agent, opts) => {
16090
+ const globalOpts = program2.opts();
16091
+ const db = getDatabase();
16092
+ const { listTasks: listTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
16093
+ const projectId = autoProject(globalOpts) || undefined;
16094
+ const filter = { assigned_to: agent };
16095
+ if (projectId)
16096
+ filter.project_id = projectId;
16097
+ const tasks = listTasks2(filter, db);
16098
+ const filterByAgent = { agent_id: agent };
16099
+ if (projectId)
16100
+ filterByAgent.project_id = projectId;
16101
+ const agentTasks = listTasks2(filterByAgent, db);
16102
+ const seen = new Set(tasks.map((t) => t.id));
16103
+ for (const t of agentTasks) {
16104
+ if (!seen.has(t.id)) {
16105
+ tasks.push(t);
16106
+ seen.add(t.id);
16107
+ }
16108
+ }
16109
+ if (opts.json || globalOpts.json) {
16110
+ console.log(JSON.stringify(tasks));
16111
+ return;
16112
+ }
16113
+ const groups = {};
16114
+ for (const t of tasks) {
16115
+ const s = t.status || "unknown";
16116
+ if (!groups[s])
16117
+ groups[s] = [];
16118
+ groups[s].push(t);
16119
+ }
16120
+ const statusOrder = ["in_progress", "pending", "blocked", "completed", "failed", "cancelled"];
16121
+ const statusIcons2 = { in_progress: "\u25B6", pending: "\u25CB", blocked: "\u2298", completed: "\u2713", failed: "\u2717", cancelled: "\u2014" };
16122
+ const statusColors5 = { in_progress: chalk.blue, pending: chalk.white, blocked: chalk.red, completed: chalk.green, failed: chalk.red, cancelled: chalk.dim };
16123
+ console.log(chalk.bold(`Tasks for ${agent} (${tasks.length} total):
16124
+ `));
16125
+ for (const status of statusOrder) {
16126
+ const group = groups[status];
16127
+ if (!group || group.length === 0)
16128
+ continue;
16129
+ const color = statusColors5[status] || chalk.white;
16130
+ const icon = statusIcons2[status] || "?";
16131
+ console.log(color(` ${icon} ${status.replace("_", " ")} (${group.length}):`));
16132
+ for (const t of group) {
16133
+ const priority = t.priority === "critical" || t.priority === "high" ? chalk.red(` [${t.priority}]`) : "";
16134
+ console.log(` ${chalk.cyan(t.short_id || t.id.slice(0, 8))} ${t.title}${priority}`);
16135
+ }
16136
+ }
16137
+ if (tasks.length === 0)
16138
+ console.log(chalk.dim(` No tasks assigned to ${agent}.`));
16139
+ });
16140
+ program2.command("blocked").description("Show tasks blocked by incomplete dependencies").option("--json", "Output as JSON").option("--project <id>", "Filter to project").action(async (opts) => {
16141
+ const globalOpts = program2.opts();
16142
+ const db = getDatabase();
16143
+ const { listTasks: listTasks2, getBlockingDeps: getBlockingDeps2 } = (init_tasks(), __toCommonJS(exports_tasks));
16144
+ const projectId = autoProject(globalOpts) || opts.project || undefined;
16145
+ const filter = { status: "pending" };
16146
+ if (projectId)
16147
+ filter.project_id = projectId;
16148
+ const allPending = listTasks2(filter, db);
16149
+ const blockedTasks = [];
16150
+ for (const t of allPending) {
16151
+ const blockers = getBlockingDeps2(t.id, db);
16152
+ if (blockers.length > 0)
16153
+ blockedTasks.push({ task: t, blockers });
16154
+ }
16155
+ if (opts.json || globalOpts.json) {
16156
+ console.log(JSON.stringify(blockedTasks.map((b) => ({ ...b.task, blocked_by: b.blockers.map((bl) => ({ id: bl.id, short_id: bl.short_id, title: bl.title, status: bl.status })) }))));
16157
+ return;
16158
+ }
16159
+ if (blockedTasks.length === 0) {
16160
+ console.log(chalk.green(" No blocked tasks!"));
16161
+ return;
16162
+ }
16163
+ console.log(chalk.bold(`Blocked (${blockedTasks.length}):
16164
+ `));
16165
+ for (const { task, blockers } of blockedTasks) {
16166
+ console.log(` ${chalk.cyan(task.short_id || task.id.slice(0, 8))} ${task.title}`);
16167
+ for (const bl of blockers) {
16168
+ console.log(` ${chalk.red("\u2298")} ${chalk.dim(bl.short_id || bl.id.slice(0, 8))} ${chalk.dim(bl.title)} ${chalk.yellow(`[${bl.status}]`)}`);
16169
+ }
16170
+ }
16171
+ });
16172
+ program2.command("overdue").description("Show tasks past their due date").option("--json", "Output as JSON").option("--project <id>", "Filter to project").action(async (opts) => {
16173
+ const globalOpts = program2.opts();
16174
+ const projectId = autoProject(globalOpts) || opts.project || undefined;
16175
+ const { getOverdueTasks: getOverdueTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
16176
+ const tasks = getOverdueTasks2(projectId);
16177
+ if (opts.json || globalOpts.json) {
16178
+ console.log(JSON.stringify(tasks));
16179
+ return;
16180
+ }
16181
+ if (tasks.length === 0) {
16182
+ console.log(chalk.green(" No overdue tasks!"));
16183
+ return;
16184
+ }
16185
+ console.log(chalk.bold.red(`Overdue (${tasks.length}):
16186
+ `));
16187
+ for (const t of tasks) {
16188
+ const dueDate = t.due_at.slice(0, 10);
16189
+ const daysOverdue = Math.floor((Date.now() - new Date(t.due_at).getTime()) / 86400000);
16190
+ const urgency = daysOverdue > 7 ? chalk.bgRed.white(` ${daysOverdue}d `) : chalk.red(`${daysOverdue}d`);
16191
+ console.log(` ${urgency} ${chalk.cyan(t.short_id || t.id.slice(0, 8))} ${t.title}${t.assigned_to ? chalk.dim(` \u2014 ${t.assigned_to}`) : ""} ${chalk.dim(`(due ${dueDate})`)}`);
16192
+ }
16193
+ });
16194
+ program2.command("week").description("Show task activity from the past 7 days").option("--json", "Output as JSON").action(async (opts) => {
16195
+ const globalOpts = program2.opts();
16196
+ const db = getDatabase();
16197
+ const { getTasksChangedSince: getTasksChangedSince2 } = (init_tasks(), __toCommonJS(exports_tasks));
16198
+ const now2 = new Date;
16199
+ const start = new Date(now2);
16200
+ start.setDate(start.getDate() - 7);
16201
+ start.setHours(0, 0, 0, 0);
16202
+ const tasks = getTasksChangedSince2(start.toISOString(), undefined, db);
16203
+ const days = {};
16204
+ for (let i = 0;i < 7; i++) {
16205
+ const d = new Date(now2);
16206
+ d.setDate(d.getDate() - i);
16207
+ days[d.toISOString().slice(0, 10)] = { completed: [], started: [], other: [] };
16208
+ }
16209
+ for (const t of tasks) {
16210
+ const day = (t.updated_at || t.created_at).slice(0, 10);
16211
+ if (!days[day])
16212
+ days[day] = { completed: [], started: [], other: [] };
16213
+ if (t.status === "completed")
16214
+ days[day].completed.push(t);
16215
+ else if (t.status === "in_progress")
16216
+ days[day].started.push(t);
16217
+ else
16218
+ days[day].other.push(t);
16219
+ }
16220
+ if (opts.json || globalOpts.json) {
16221
+ console.log(JSON.stringify({ from: start.toISOString().slice(0, 10), to: now2.toISOString().slice(0, 10), days }));
16222
+ return;
16223
+ }
16224
+ const totalCompleted = tasks.filter((t) => t.status === "completed").length;
16225
+ const totalStarted = tasks.filter((t) => t.status === "in_progress").length;
16226
+ console.log(chalk.bold(`Week \u2014 ${start.toISOString().slice(0, 10)} to ${now2.toISOString().slice(0, 10)}`));
16227
+ console.log(chalk.dim(` ${totalCompleted} completed, ${totalStarted} in progress, ${tasks.length} total changes
16228
+ `));
16229
+ const sortedDays = Object.keys(days).sort().reverse();
16230
+ for (const day of sortedDays) {
16231
+ const dayData = days[day];
16232
+ if (!dayData)
16233
+ continue;
16234
+ const { completed, started } = dayData;
16235
+ if (completed.length === 0 && started.length === 0)
16236
+ continue;
16237
+ const weekday = new Date(day + "T12:00:00").toLocaleDateString("en-US", { weekday: "short" });
16238
+ console.log(chalk.bold(` ${weekday} ${day}`));
16239
+ for (const t of completed)
16240
+ console.log(` ${chalk.green("\u2713")} ${chalk.cyan(t.short_id || t.id.slice(0, 8))} ${t.title}${t.assigned_to ? chalk.dim(` \u2014 ${t.assigned_to}`) : ""}`);
16241
+ for (const t of started)
16242
+ console.log(` ${chalk.blue("\u25B6")} ${chalk.cyan(t.short_id || t.id.slice(0, 8))} ${t.title}${t.assigned_to ? chalk.dim(` \u2014 ${t.assigned_to}`) : ""}`);
16243
+ }
16244
+ if (tasks.length === 0)
16245
+ console.log(chalk.dim(" No activity this week."));
16246
+ });
16247
+ program2.command("burndown").description("Show task completion velocity over the past 7 days").option("--days <n>", "Number of days", "7").option("--json", "Output as JSON").action(async (opts) => {
16248
+ const globalOpts = program2.opts();
16249
+ const db = getDatabase();
16250
+ const { getRecentActivity: getRecentActivity2 } = (init_audit(), __toCommonJS(exports_audit));
16251
+ const numDays = parseInt(opts.days, 10);
16252
+ const entries = getRecentActivity2(5000, db);
16253
+ const now2 = new Date;
16254
+ const dayStats = [];
16255
+ for (let i = numDays - 1;i >= 0; i--) {
16256
+ const d = new Date(now2);
16257
+ d.setDate(d.getDate() - i);
16258
+ const dateStr = d.toISOString().slice(0, 10);
16259
+ const dayEntries = entries.filter((e) => e.created_at.slice(0, 10) === dateStr);
16260
+ dayStats.push({
16261
+ date: dateStr,
16262
+ completed: dayEntries.filter((e) => e.action === "complete").length,
16263
+ created: dayEntries.filter((e) => e.action === "create").length,
16264
+ failed: dayEntries.filter((e) => e.action === "fail").length
16265
+ });
16266
+ }
16267
+ if (opts.json || globalOpts.json) {
16268
+ console.log(JSON.stringify(dayStats));
16269
+ return;
16270
+ }
16271
+ const maxVal = Math.max(...dayStats.map((d) => Math.max(d.completed, d.created)), 1);
16272
+ const barWidth = 30;
16273
+ console.log(chalk.bold("Burndown (last " + numDays + ` days):
16274
+ `));
16275
+ console.log(chalk.dim(" Date Done New Failed Chart"));
16276
+ for (const day of dayStats) {
16277
+ const weekday = new Date(day.date + "T12:00:00").toLocaleDateString("en-US", { weekday: "short" });
16278
+ const completedBar = chalk.green("\u2588".repeat(Math.round(day.completed / maxVal * barWidth)));
16279
+ const createdBar = chalk.blue("\u2591".repeat(Math.round(day.created / maxVal * barWidth)));
16280
+ const failed = day.failed > 0 ? chalk.red(String(day.failed).padStart(4)) : chalk.dim(" 0");
16281
+ console.log(` ${weekday} ${day.date.slice(5)} ${chalk.green(String(day.completed).padStart(4))} ${chalk.blue(String(day.created).padStart(4))} ${failed} ${completedBar}${createdBar}`);
16282
+ }
16283
+ const totalCompleted = dayStats.reduce((s, d) => s + d.completed, 0);
16284
+ const totalCreated = dayStats.reduce((s, d) => s + d.created, 0);
16285
+ const velocity = (totalCompleted / numDays).toFixed(1);
16286
+ console.log(chalk.dim(`
16287
+ Velocity: ${velocity}/day \xB7 ${totalCompleted} done \xB7 ${totalCreated} created`));
16288
+ });
16289
+ program2.command("log").description("Show recent task activity log (git-log style)").option("--limit <n>", "Number of entries", "30").option("--json", "Output as JSON").action(async (opts) => {
16290
+ const globalOpts = program2.opts();
16291
+ const db = getDatabase();
16292
+ const { getRecentActivity: getRecentActivity2 } = (init_audit(), __toCommonJS(exports_audit));
16293
+ const entries = getRecentActivity2(parseInt(opts.limit, 10), db);
16294
+ if (opts.json || globalOpts.json) {
16295
+ console.log(JSON.stringify(entries));
16296
+ return;
16297
+ }
16298
+ if (entries.length === 0) {
16299
+ console.log(chalk.dim(" No activity yet."));
16300
+ return;
16301
+ }
16302
+ const actionIcons = {
16303
+ create: chalk.green("+"),
16304
+ start: chalk.blue("\u25B6"),
16305
+ complete: chalk.green("\u2713"),
16306
+ fail: chalk.red("\u2717"),
16307
+ update: chalk.yellow("~"),
16308
+ approve: chalk.green("\u2605"),
16309
+ lock: chalk.dim("\uD83D\uDD12"),
16310
+ unlock: chalk.dim("\uD83D\uDD13")
16311
+ };
16312
+ let lastDate = "";
16313
+ for (const e of entries) {
16314
+ const date = e.created_at.slice(0, 10);
16315
+ const time = e.created_at.slice(11, 16);
16316
+ if (date !== lastDate) {
16317
+ console.log(chalk.bold(`
16318
+ ${date}`));
16319
+ lastDate = date;
16320
+ }
16321
+ const icon = actionIcons[e.action] || chalk.dim("\xB7");
16322
+ const agent = e.agent_id ? chalk.dim(` (${e.agent_id})`) : "";
16323
+ const taskRef = chalk.cyan(e.task_id.slice(0, 8));
16324
+ let detail = "";
16325
+ if (e.field && e.old_value && e.new_value) {
16326
+ detail = chalk.dim(` ${e.field}: ${e.old_value} \u2192 ${e.new_value}`);
16327
+ } else if (e.field && e.new_value) {
16328
+ detail = chalk.dim(` ${e.field}: ${e.new_value}`);
16329
+ }
16330
+ console.log(` ${chalk.dim(time)} ${icon} ${e.action.padEnd(8)} ${taskRef}${detail}${agent}`);
16331
+ }
16332
+ });
16333
+ program2.command("ready").description("Show all tasks ready to be claimed (pending, unblocked, unlocked)").option("--json", "Output as JSON").option("--project <id>", "Filter to project").option("--limit <n>", "Max tasks to show", "20").action(async (opts) => {
16334
+ const globalOpts = program2.opts();
16335
+ const db = getDatabase();
16336
+ const { listTasks: listTasks2, getBlockingDeps: getBlockingDeps2 } = (init_tasks(), __toCommonJS(exports_tasks));
16337
+ const { isLockExpired: isLockExpired2 } = (init_database(), __toCommonJS(exports_database));
16338
+ const projectId = autoProject(globalOpts) || opts.project || undefined;
16339
+ const filter = { status: "pending" };
16340
+ if (projectId)
16341
+ filter.project_id = projectId;
16342
+ const pending = listTasks2(filter, db);
16343
+ const ready = pending.filter((t) => {
16344
+ if (t.locked_by && !isLockExpired2(t.locked_at))
16345
+ return false;
16346
+ const blockers = getBlockingDeps2(t.id, db);
16347
+ return blockers.length === 0;
16348
+ });
16349
+ const limited = ready.slice(0, parseInt(opts.limit, 10));
16350
+ if (opts.json || globalOpts.json) {
16351
+ console.log(JSON.stringify(limited));
16352
+ return;
16353
+ }
16354
+ if (limited.length === 0) {
16355
+ console.log(chalk.dim(" No tasks ready to claim."));
16356
+ return;
16357
+ }
16358
+ console.log(chalk.bold(`Ready to claim (${ready.length}${ready.length > limited.length ? `, showing ${limited.length}` : ""}):
16359
+ `));
16360
+ for (const t of limited) {
16361
+ const pri = t.priority === "critical" ? chalk.bgRed.white(" CRIT ") : t.priority === "high" ? chalk.red("[high]") : t.priority === "medium" ? chalk.yellow("[med]") : "";
16362
+ const due = t.due_at ? chalk.dim(` due ${t.due_at.slice(0, 10)}`) : "";
16363
+ console.log(` ${chalk.cyan(t.short_id || t.id.slice(0, 8))} ${t.title} ${pri}${due}`);
16364
+ }
16365
+ });
16366
+ program2.command("sprint").description("Sprint dashboard: in-progress, next up, blockers, and overdue").option("--json", "Output as JSON").option("--project <id>", "Filter to project").action(async (opts) => {
16367
+ const globalOpts = program2.opts();
16368
+ const db = getDatabase();
16369
+ const { listTasks: listTasks2, getBlockingDeps: getBlockingDeps2 } = (init_tasks(), __toCommonJS(exports_tasks));
16370
+ const projectId = autoProject(globalOpts) || opts.project || undefined;
16371
+ const baseFilter = {};
16372
+ if (projectId)
16373
+ baseFilter.project_id = projectId;
16374
+ const inProgress = listTasks2({ ...baseFilter, status: "in_progress" }, db);
16375
+ const pending = listTasks2({ ...baseFilter, status: "pending" }, db);
16376
+ const nowStr = new Date().toISOString();
16377
+ const blocked = [];
16378
+ for (const t of pending) {
16379
+ const blockers = getBlockingDeps2(t.id, db);
16380
+ if (blockers.length > 0)
16381
+ blocked.push({ task: t, blockers });
16382
+ }
16383
+ const overdue = [...inProgress, ...pending].filter((t) => t.due_at && t.due_at < nowStr);
16384
+ const blockedIds = new Set(blocked.map((b) => b.task.id));
16385
+ const nextUp = pending.filter((t) => !blockedIds.has(t.id)).slice(0, 5);
16386
+ if (opts.json || globalOpts.json) {
16387
+ console.log(JSON.stringify({ in_progress: inProgress, next_up: nextUp, blocked, overdue }));
16388
+ return;
16389
+ }
16390
+ console.log(chalk.bold(`Sprint Dashboard
16391
+ `));
16392
+ console.log(chalk.blue(` \u25B6 In Progress (${inProgress.length}):`));
16393
+ if (inProgress.length === 0)
16394
+ console.log(chalk.dim(" (none)"));
16395
+ for (const t of inProgress) {
16396
+ const agent = t.assigned_to ? chalk.dim(` \u2014 ${t.assigned_to}`) : "";
16397
+ console.log(` ${chalk.cyan(t.short_id || t.id.slice(0, 8))} ${t.title}${agent}`);
16398
+ }
16399
+ console.log(chalk.white(`
16400
+ \u25CB Next Up (${nextUp.length}):`));
16401
+ if (nextUp.length === 0)
16402
+ console.log(chalk.dim(" (none)"));
16403
+ for (const t of nextUp) {
16404
+ const pri = t.priority === "critical" ? chalk.bgRed.white(" CRIT ") : t.priority === "high" ? chalk.red("[high]") : "";
16405
+ console.log(` ${chalk.cyan(t.short_id || t.id.slice(0, 8))} ${t.title} ${pri}`);
16406
+ }
16407
+ if (blocked.length > 0) {
16408
+ console.log(chalk.red(`
16409
+ \u2298 Blocked (${blocked.length}):`));
16410
+ for (const { task, blockers } of blocked) {
16411
+ console.log(` ${chalk.cyan(task.short_id || task.id.slice(0, 8))} ${task.title}`);
16412
+ for (const bl of blockers)
16413
+ console.log(` ${chalk.dim("\u2190 " + (bl.short_id || bl.id.slice(0, 8)) + " " + bl.title)} ${chalk.yellow(`[${bl.status}]`)}`);
16414
+ }
16415
+ }
16416
+ if (overdue.length > 0) {
16417
+ console.log(chalk.red(`
16418
+ \u26A0 Overdue (${overdue.length}):`));
16419
+ for (const t of overdue) {
16420
+ const daysOver = Math.floor((Date.now() - new Date(t.due_at).getTime()) / 86400000);
16421
+ console.log(` ${chalk.red(`${daysOver}d`)} ${chalk.cyan(t.short_id || t.id.slice(0, 8))} ${t.title}`);
16422
+ }
16423
+ }
16424
+ console.log(chalk.dim(`
16425
+ ${inProgress.length} active \xB7 ${pending.length} pending \xB7 ${blocked.length} blocked \xB7 ${overdue.length} overdue`));
16426
+ });
16427
+ 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("--json", "Output as JSON").option("--limit <n>", "Number of handoffs to show", "5").action(async (opts) => {
16428
+ const globalOpts = program2.opts();
16429
+ const db = getDatabase();
16430
+ const { createHandoff: createHandoff2, listHandoffs: listHandoffs2 } = (init_handoffs(), __toCommonJS(exports_handoffs));
16431
+ const projectId = autoProject(globalOpts) || undefined;
16432
+ if (opts.create || opts.summary) {
16433
+ if (!opts.summary) {
16434
+ console.error(chalk.red(" --summary is required for creating a handoff"));
16435
+ process.exit(1);
16436
+ }
16437
+ const handoff = createHandoff2({
16438
+ agent_id: opts.agent || globalOpts.agent || undefined,
16439
+ project_id: projectId,
16440
+ summary: opts.summary,
16441
+ completed: opts.completed ? opts.completed.split(",").map((s) => s.trim()) : undefined,
16442
+ inProgress: opts.inProgress ? opts.inProgress.split(",").map((s) => s.trim()) : undefined,
16443
+ blockers: opts.blockers ? opts.blockers.split(",").map((s) => s.trim()) : undefined,
16444
+ next_steps: opts.next ? opts.next.split(",").map((s) => s.trim()) : undefined
16445
+ }, db);
16446
+ if (opts.json || globalOpts.json) {
16447
+ console.log(JSON.stringify(handoff));
16448
+ return;
16449
+ }
16450
+ console.log(chalk.green(` \u2713 Handoff created by ${handoff.agent_id || "unknown"}`));
16451
+ return;
16452
+ }
16453
+ const handoffs = listHandoffs2(projectId, parseInt(opts.limit, 10), db);
16454
+ if (opts.json || globalOpts.json) {
16455
+ console.log(JSON.stringify(handoffs));
16456
+ return;
16457
+ }
16458
+ if (handoffs.length === 0) {
16459
+ console.log(chalk.dim(" No handoffs yet."));
16460
+ return;
16461
+ }
16462
+ for (const h of handoffs) {
16463
+ const time = h.created_at.slice(0, 16).replace("T", " ");
16464
+ console.log(chalk.bold(`
16465
+ ${time} ${h.agent_id || "unknown"}`));
16466
+ console.log(` ${h.summary}`);
16467
+ if (h.completed?.length) {
16468
+ console.log(chalk.green(` \u2713 Completed:`));
16469
+ for (const c of h.completed)
16470
+ console.log(` - ${c}`);
16471
+ }
16472
+ if (h.in_progress?.length) {
16473
+ console.log(chalk.blue(` \u25B6 In progress:`));
16474
+ for (const c of h.in_progress)
16475
+ console.log(` - ${c}`);
16476
+ }
16477
+ if (h.blockers?.length) {
16478
+ console.log(chalk.red(` \u2298 Blockers:`));
16479
+ for (const c of h.blockers)
16480
+ console.log(` - ${c}`);
16481
+ }
16482
+ if (h.next_steps?.length) {
16483
+ console.log(chalk.cyan(` \u2192 Next steps:`));
16484
+ for (const c of h.next_steps)
16485
+ console.log(` - ${c}`);
16486
+ }
16487
+ }
16488
+ });
16489
+ program2.command("priorities").description("Show task counts grouped by priority").option("--json", "Output as JSON").option("--project <id>", "Filter to project").action(async (opts) => {
16490
+ const globalOpts = program2.opts();
16491
+ const db = getDatabase();
16492
+ const { countTasks: countTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
16493
+ const projectId = autoProject(globalOpts) || opts.project || undefined;
16494
+ const base = projectId ? { project_id: projectId } : {};
16495
+ const priorities = ["critical", "high", "medium", "low", "none"];
16496
+ const counts = {};
16497
+ for (const p of priorities) {
16498
+ counts[p] = {
16499
+ total: countTasks2({ ...base, priority: p }, db),
16500
+ pending: countTasks2({ ...base, priority: p, status: "pending" }, db),
16501
+ in_progress: countTasks2({ ...base, priority: p, status: "in_progress" }, db),
16502
+ completed: countTasks2({ ...base, priority: p, status: "completed" }, db)
16503
+ };
16504
+ }
16505
+ if (opts.json || globalOpts.json) {
16506
+ console.log(JSON.stringify(counts));
16507
+ return;
16508
+ }
16509
+ console.log(chalk.bold(`Priority Breakdown:
16510
+ `));
16511
+ const priColors = { critical: chalk.bgRed.white, high: chalk.red, medium: chalk.yellow, low: chalk.blue, none: chalk.dim };
16512
+ for (const p of priorities) {
16513
+ const c = counts[p];
16514
+ if (!c || c.total === 0)
16515
+ continue;
16516
+ const color = priColors[p] || chalk.white;
16517
+ const bar = chalk.green("\u2588".repeat(Math.min(c.completed, 30))) + chalk.blue("\u2591".repeat(Math.min(c.in_progress, 10))) + chalk.dim("\xB7".repeat(Math.min(c.pending, 20)));
16518
+ console.log(` ${color(p.padEnd(9))} ${String(c.total).padStart(4)} total ${chalk.green(String(c.completed).padStart(3))} done ${chalk.blue(String(c.in_progress).padStart(3))} active ${chalk.dim(String(c.pending).padStart(3))} pending ${bar}`);
16519
+ }
16520
+ });
16521
+ program2.command("context").description("Session start context: status, latest handoff, next task, overdue").option("--agent <name>", "Agent name for handoff lookup").option("--json", "Output as JSON").action(async (opts) => {
16522
+ const globalOpts = program2.opts();
16523
+ const db = getDatabase();
16524
+ const { getStatus: getStatus2 } = (init_tasks(), __toCommonJS(exports_tasks));
16525
+ const { getNextTask: getNextTask2, getOverdueTasks: getOverdueTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
16526
+ const { getLatestHandoff: getLatestHandoff2 } = (init_handoffs(), __toCommonJS(exports_handoffs));
16527
+ const projectId = autoProject(globalOpts) || undefined;
16528
+ const agentName = opts.agent || globalOpts.agent || undefined;
16529
+ const filters = projectId ? { project_id: projectId } : undefined;
16530
+ const status = getStatus2(filters, agentName);
16531
+ const nextTask = getNextTask2(agentName, filters, db);
16532
+ const overdue = getOverdueTasks2(projectId, db);
16533
+ const handoff = agentName ? getLatestHandoff2(agentName, projectId, db) : getLatestHandoff2(undefined, projectId, db);
16534
+ if (opts.json || globalOpts.json) {
16535
+ console.log(JSON.stringify({ status, next_task: nextTask, overdue_count: overdue.length, latest_handoff: handoff, as_of: new Date().toISOString() }));
16536
+ return;
16537
+ }
16538
+ console.log(chalk.bold(`Session Context
16539
+ `));
16540
+ console.log(` ${status.pending} pending \xB7 ${status.in_progress} active \xB7 ${status.completed} done \xB7 ${status.total} total`);
16541
+ if (status.stale_count > 0)
16542
+ console.log(chalk.yellow(` \u26A0 ${status.stale_count} stale tasks`));
16543
+ if (overdue.length > 0)
16544
+ console.log(chalk.red(` \u26A0 ${overdue.length} overdue tasks`));
16545
+ if (nextTask) {
16546
+ const pri = nextTask.priority === "critical" || nextTask.priority === "high" ? chalk.red(` [${nextTask.priority}]`) : "";
16547
+ console.log(chalk.bold(`
16548
+ Next up:`));
16549
+ console.log(` ${chalk.cyan(nextTask.short_id || nextTask.id.slice(0, 8))} ${nextTask.title}${pri}`);
16550
+ }
16551
+ if (handoff) {
16552
+ console.log(chalk.bold(`
16553
+ Last handoff (${handoff.agent_id || "unknown"}, ${handoff.created_at.slice(0, 16).replace("T", " ")}):`));
16554
+ console.log(` ${handoff.summary}`);
16555
+ if (handoff.next_steps?.length) {
16556
+ for (const s of handoff.next_steps)
16557
+ console.log(` \u2192 ${s}`);
16558
+ }
16559
+ }
16560
+ console.log(chalk.dim(`
16561
+ as_of: ${new Date().toISOString()}`));
16562
+ });
15752
16563
  program2.action(async () => {
15753
16564
  if (process.stdout.isTTY) {
15754
16565
  try {