@hasna/todos 0.9.29 → 0.9.30

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
@@ -2253,6 +2253,7 @@ function ensureSchema(db) {
2253
2253
  ensureColumn("tasks", "approved_at", "TEXT");
2254
2254
  ensureColumn("agents", "role", "TEXT DEFAULT 'agent'");
2255
2255
  ensureColumn("agents", "permissions", `TEXT DEFAULT '["*"]'`);
2256
+ ensureColumn("agents", "reports_to", "TEXT");
2256
2257
  ensureColumn("plans", "task_list_id", "TEXT");
2257
2258
  ensureColumn("plans", "agent_id", "TEXT");
2258
2259
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_plan ON tasks(plan_id)");
@@ -2544,6 +2545,10 @@ var init_database = __esm(() => {
2544
2545
  ALTER TABLE agents ADD COLUMN permissions TEXT DEFAULT '["*"]';
2545
2546
 
2546
2547
  INSERT OR IGNORE INTO _migrations (id) VALUES (10);
2548
+ `,
2549
+ `
2550
+ ALTER TABLE agents ADD COLUMN reports_to TEXT;
2551
+ INSERT OR IGNORE INTO _migrations (id) VALUES (11);
2547
2552
  `
2548
2553
  ];
2549
2554
  });
@@ -3363,6 +3368,8 @@ __export(exports_agents, {
3363
3368
  updateAgent: () => updateAgent,
3364
3369
  registerAgent: () => registerAgent,
3365
3370
  listAgents: () => listAgents,
3371
+ getOrgChart: () => getOrgChart,
3372
+ getDirectReports: () => getDirectReports,
3366
3373
  getAgentByName: () => getAgentByName,
3367
3374
  getAgent: () => getAgent,
3368
3375
  deleteAgent: () => deleteAgent
@@ -3386,13 +3393,14 @@ function registerAgent(input, db) {
3386
3393
  }
3387
3394
  const id = shortUuid();
3388
3395
  const timestamp = now();
3389
- d.run(`INSERT INTO agents (id, name, description, role, permissions, metadata, created_at, last_seen_at)
3390
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [
3396
+ d.run(`INSERT INTO agents (id, name, description, role, permissions, reports_to, metadata, created_at, last_seen_at)
3397
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
3391
3398
  id,
3392
3399
  input.name,
3393
3400
  input.description || null,
3394
3401
  input.role || "agent",
3395
3402
  JSON.stringify(input.permissions || ["*"]),
3403
+ input.reports_to || null,
3396
3404
  JSON.stringify(input.metadata || {}),
3397
3405
  timestamp,
3398
3406
  timestamp
@@ -3440,6 +3448,10 @@ function updateAgent(id, input, db) {
3440
3448
  sets.push("permissions = ?");
3441
3449
  params.push(JSON.stringify(input.permissions));
3442
3450
  }
3451
+ if (input.reports_to !== undefined) {
3452
+ sets.push("reports_to = ?");
3453
+ params.push(input.reports_to);
3454
+ }
3443
3455
  if (input.metadata !== undefined) {
3444
3456
  sets.push("metadata = ?");
3445
3457
  params.push(JSON.stringify(input.metadata));
@@ -3452,6 +3464,25 @@ function deleteAgent(id, db) {
3452
3464
  const d = db || getDatabase();
3453
3465
  return d.run("DELETE FROM agents WHERE id = ?", [id]).changes > 0;
3454
3466
  }
3467
+ function getDirectReports(agentId, db) {
3468
+ const d = db || getDatabase();
3469
+ return d.query("SELECT * FROM agents WHERE reports_to = ? ORDER BY name").all(agentId).map(rowToAgent);
3470
+ }
3471
+ function getOrgChart(db) {
3472
+ const agents = listAgents(db);
3473
+ const byManager = new Map;
3474
+ for (const a of agents) {
3475
+ const key = a.reports_to;
3476
+ if (!byManager.has(key))
3477
+ byManager.set(key, []);
3478
+ byManager.get(key).push(a);
3479
+ }
3480
+ function buildTree(parentId) {
3481
+ const children = byManager.get(parentId) || [];
3482
+ return children.map((a) => ({ agent: a, reports: buildTree(a.id) }));
3483
+ }
3484
+ return buildTree(null);
3485
+ }
3455
3486
  var init_agents = __esm(() => {
3456
3487
  init_database();
3457
3488
  });
@@ -9205,6 +9236,50 @@ In Progress:`);
9205
9236
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
9206
9237
  }
9207
9238
  });
9239
+ server.tool("get_org_chart", "Get agent org chart \u2014 who reports to who.", {}, async () => {
9240
+ try {
9241
+ let render = function(nodes, indent = 0) {
9242
+ return nodes.map((n) => {
9243
+ const prefix = " ".repeat(indent);
9244
+ const role = n.agent.role ? ` (${n.agent.role})` : "";
9245
+ const line = `${prefix}${n.agent.name}${role} [${n.agent.id}]`;
9246
+ const children = n.reports.length > 0 ? `
9247
+ ` + render(n.reports, indent + 1) : "";
9248
+ return line + children;
9249
+ }).join(`
9250
+ `);
9251
+ };
9252
+ const { getOrgChart: getOrgChart2 } = await Promise.resolve().then(() => (init_agents(), exports_agents));
9253
+ const tree = getOrgChart2();
9254
+ const text = tree.length > 0 ? render(tree) : "No agents registered.";
9255
+ return { content: [{ type: "text", text }] };
9256
+ } catch (e) {
9257
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
9258
+ }
9259
+ });
9260
+ server.tool("set_reports_to", "Set who an agent reports to in the org chart.", {
9261
+ agent_name: exports_external.string(),
9262
+ manager_name: exports_external.string().optional()
9263
+ }, async ({ agent_name, manager_name }) => {
9264
+ try {
9265
+ const agent = getAgentByName(agent_name);
9266
+ if (!agent)
9267
+ return { content: [{ type: "text", text: `Agent not found: ${agent_name}` }], isError: true };
9268
+ let managerId = null;
9269
+ if (manager_name) {
9270
+ const manager = getAgentByName(manager_name);
9271
+ if (!manager)
9272
+ return { content: [{ type: "text", text: `Manager not found: ${manager_name}` }], isError: true };
9273
+ managerId = manager.id;
9274
+ }
9275
+ const { updateAgent: updateAgent2 } = await Promise.resolve().then(() => (init_agents(), exports_agents));
9276
+ updateAgent2(agent.id, { reports_to: managerId });
9277
+ const result = managerId ? `${agent_name} now reports to ${manager_name}` : `${agent_name} reports to no one (top-level)`;
9278
+ return { content: [{ type: "text", text: result }] };
9279
+ } catch (e) {
9280
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
9281
+ }
9282
+ });
9208
9283
  server.tool("search_tools", "List tool names matching a query.", { query: exports_external.string().optional() }, async ({ query }) => {
9209
9284
  const all = [
9210
9285
  "create_task",
@@ -9656,6 +9731,16 @@ Dashboard not found at: ${dashboardDir}`);
9656
9731
  return json({ error: e instanceof Error ? e.message : "Failed to claim" }, 500, port);
9657
9732
  }
9658
9733
  }
9734
+ if (path === "/api/org" && method === "GET") {
9735
+ const { getOrgChart: getOrgChart2 } = await Promise.resolve().then(() => (init_agents(), exports_agents));
9736
+ return json(getOrgChart2(), 200, port);
9737
+ }
9738
+ const teamMatch = path.match(/^\/api\/agents\/([^/]+)\/team$/);
9739
+ if (teamMatch && method === "GET") {
9740
+ const agentId = decodeURIComponent(teamMatch[1]);
9741
+ const { getDirectReports: getDirectReports2 } = await Promise.resolve().then(() => (init_agents(), exports_agents));
9742
+ return json(getDirectReports2(agentId), 200, port);
9743
+ }
9659
9744
  if (path === "/api/agents" && method === "GET") {
9660
9745
  return json(listAgents(), 200, port);
9661
9746
  }
@@ -12194,6 +12279,52 @@ program2.command("agents").description("List registered agents").action(() => {
12194
12279
  handleError(e);
12195
12280
  }
12196
12281
  });
12282
+ 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) => {
12283
+ const globalOpts = program2.opts();
12284
+ const { getOrgChart: getOrgChart2, getAgentByName: getByName, updateAgent: update } = (init_agents(), __toCommonJS(exports_agents));
12285
+ if (opts.set) {
12286
+ const [agentName, managerName] = opts.set.split("=");
12287
+ const agent = getByName(agentName);
12288
+ if (!agent) {
12289
+ console.error(chalk.red(`Agent not found: ${agentName}`));
12290
+ process.exit(1);
12291
+ }
12292
+ let managerId = null;
12293
+ if (managerName) {
12294
+ const manager = getByName(managerName);
12295
+ if (!manager) {
12296
+ console.error(chalk.red(`Manager not found: ${managerName}`));
12297
+ process.exit(1);
12298
+ }
12299
+ managerId = manager.id;
12300
+ }
12301
+ update(agent.id, { reports_to: managerId });
12302
+ if (globalOpts.json) {
12303
+ output({ agent: agentName, reports_to: managerName || null }, true);
12304
+ } else {
12305
+ console.log(chalk.green(managerId ? `${agentName} \u2192 ${managerName}` : `${agentName} \u2192 (top-level)`));
12306
+ }
12307
+ return;
12308
+ }
12309
+ const tree = getOrgChart2();
12310
+ if (globalOpts.json) {
12311
+ output(tree, true);
12312
+ return;
12313
+ }
12314
+ if (tree.length === 0) {
12315
+ console.log(chalk.dim("No agents registered."));
12316
+ return;
12317
+ }
12318
+ function render2(nodes, indent = 0) {
12319
+ for (const n of nodes) {
12320
+ const prefix = " ".repeat(indent);
12321
+ const role = n.agent.role ? chalk.dim(` (${n.agent.role})`) : "";
12322
+ console.log(`${prefix}${indent > 0 ? "\u251C\u2500\u2500 " : ""}${chalk.bold(n.agent.name)}${role} ${chalk.dim(n.agent.id)}`);
12323
+ render2(n.reports, indent + 1);
12324
+ }
12325
+ }
12326
+ render2(tree);
12327
+ });
12197
12328
  program2.command("lists").aliases(["task-lists", "tl"]).description("List and manage task lists").option("--add <name>", "Create a task list").option("--slug <slug>", "Custom slug (with --add)").option("-d, --description <text>", "Description (with --add)").option("--delete <id>", "Delete a task list").action((opts) => {
12198
12329
  try {
12199
12330
  const globalOpts = program2.opts();
package/dist/index.js CHANGED
@@ -264,6 +264,10 @@ var MIGRATIONS = [
264
264
  ALTER TABLE agents ADD COLUMN permissions TEXT DEFAULT '["*"]';
265
265
 
266
266
  INSERT OR IGNORE INTO _migrations (id) VALUES (10);
267
+ `,
268
+ `
269
+ ALTER TABLE agents ADD COLUMN reports_to TEXT;
270
+ INSERT OR IGNORE INTO _migrations (id) VALUES (11);
267
271
  `
268
272
  ];
269
273
  var _db = null;
@@ -385,6 +389,7 @@ function ensureSchema(db) {
385
389
  ensureColumn("tasks", "approved_at", "TEXT");
386
390
  ensureColumn("agents", "role", "TEXT DEFAULT 'agent'");
387
391
  ensureColumn("agents", "permissions", `TEXT DEFAULT '["*"]'`);
392
+ ensureColumn("agents", "reports_to", "TEXT");
388
393
  ensureColumn("plans", "task_list_id", "TEXT");
389
394
  ensureColumn("plans", "agent_id", "TEXT");
390
395
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_plan ON tasks(plan_id)");
@@ -1388,13 +1393,14 @@ function registerAgent(input, db) {
1388
1393
  }
1389
1394
  const id = shortUuid();
1390
1395
  const timestamp = now();
1391
- d.run(`INSERT INTO agents (id, name, description, role, permissions, metadata, created_at, last_seen_at)
1392
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [
1396
+ d.run(`INSERT INTO agents (id, name, description, role, permissions, reports_to, metadata, created_at, last_seen_at)
1397
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
1393
1398
  id,
1394
1399
  input.name,
1395
1400
  input.description || null,
1396
1401
  input.role || "agent",
1397
1402
  JSON.stringify(input.permissions || ["*"]),
1403
+ input.reports_to || null,
1398
1404
  JSON.stringify(input.metadata || {}),
1399
1405
  timestamp,
1400
1406
  timestamp
@@ -1442,6 +1448,10 @@ function updateAgent(id, input, db) {
1442
1448
  sets.push("permissions = ?");
1443
1449
  params.push(JSON.stringify(input.permissions));
1444
1450
  }
1451
+ if (input.reports_to !== undefined) {
1452
+ sets.push("reports_to = ?");
1453
+ params.push(input.reports_to);
1454
+ }
1445
1455
  if (input.metadata !== undefined) {
1446
1456
  sets.push("metadata = ?");
1447
1457
  params.push(JSON.stringify(input.metadata));
@@ -1454,6 +1464,25 @@ function deleteAgent(id, db) {
1454
1464
  const d = db || getDatabase();
1455
1465
  return d.run("DELETE FROM agents WHERE id = ?", [id]).changes > 0;
1456
1466
  }
1467
+ function getDirectReports(agentId, db) {
1468
+ const d = db || getDatabase();
1469
+ return d.query("SELECT * FROM agents WHERE reports_to = ? ORDER BY name").all(agentId).map(rowToAgent);
1470
+ }
1471
+ function getOrgChart(db) {
1472
+ const agents = listAgents(db);
1473
+ const byManager = new Map;
1474
+ for (const a of agents) {
1475
+ const key = a.reports_to;
1476
+ if (!byManager.has(key))
1477
+ byManager.set(key, []);
1478
+ byManager.get(key).push(a);
1479
+ }
1480
+ function buildTree(parentId) {
1481
+ const children = byManager.get(parentId) || [];
1482
+ return children.map((a) => ({ agent: a, reports: buildTree(a.id) }));
1483
+ }
1484
+ return buildTree(null);
1485
+ }
1457
1486
  // src/db/task-lists.ts
1458
1487
  function rowToTaskList(row) {
1459
1488
  return {
@@ -2264,6 +2293,8 @@ export {
2264
2293
  getProjectByPath,
2265
2294
  getProject,
2266
2295
  getPlan,
2296
+ getOrgChart,
2297
+ getDirectReports,
2267
2298
  getDatabase,
2268
2299
  getCompletionGuardConfig,
2269
2300
  getComment,
package/dist/mcp/index.js CHANGED
@@ -187,6 +187,7 @@ function ensureSchema(db) {
187
187
  ensureColumn("tasks", "approved_at", "TEXT");
188
188
  ensureColumn("agents", "role", "TEXT DEFAULT 'agent'");
189
189
  ensureColumn("agents", "permissions", `TEXT DEFAULT '["*"]'`);
190
+ ensureColumn("agents", "reports_to", "TEXT");
190
191
  ensureColumn("plans", "task_list_id", "TEXT");
191
192
  ensureColumn("plans", "agent_id", "TEXT");
192
193
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_plan ON tasks(plan_id)");
@@ -478,6 +479,10 @@ var init_database = __esm(() => {
478
479
  ALTER TABLE agents ADD COLUMN permissions TEXT DEFAULT '["*"]';
479
480
 
480
481
  INSERT OR IGNORE INTO _migrations (id) VALUES (10);
482
+ `,
483
+ `
484
+ ALTER TABLE agents ADD COLUMN reports_to TEXT;
485
+ INSERT OR IGNORE INTO _migrations (id) VALUES (11);
481
486
  `
482
487
  ];
483
488
  });
@@ -509,6 +514,132 @@ var init_audit = __esm(() => {
509
514
  init_database();
510
515
  });
511
516
 
517
+ // src/db/agents.ts
518
+ var exports_agents = {};
519
+ __export(exports_agents, {
520
+ updateAgentActivity: () => updateAgentActivity,
521
+ updateAgent: () => updateAgent,
522
+ registerAgent: () => registerAgent,
523
+ listAgents: () => listAgents,
524
+ getOrgChart: () => getOrgChart,
525
+ getDirectReports: () => getDirectReports,
526
+ getAgentByName: () => getAgentByName,
527
+ getAgent: () => getAgent,
528
+ deleteAgent: () => deleteAgent
529
+ });
530
+ function shortUuid() {
531
+ return crypto.randomUUID().slice(0, 8);
532
+ }
533
+ function rowToAgent(row) {
534
+ return {
535
+ ...row,
536
+ permissions: JSON.parse(row.permissions || '["*"]'),
537
+ metadata: JSON.parse(row.metadata || "{}")
538
+ };
539
+ }
540
+ function registerAgent(input, db) {
541
+ const d = db || getDatabase();
542
+ const existing = getAgentByName(input.name, d);
543
+ if (existing) {
544
+ d.run("UPDATE agents SET last_seen_at = ? WHERE id = ?", [now(), existing.id]);
545
+ return getAgent(existing.id, d);
546
+ }
547
+ const id = shortUuid();
548
+ const timestamp = now();
549
+ d.run(`INSERT INTO agents (id, name, description, role, permissions, reports_to, metadata, created_at, last_seen_at)
550
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
551
+ id,
552
+ input.name,
553
+ input.description || null,
554
+ input.role || "agent",
555
+ JSON.stringify(input.permissions || ["*"]),
556
+ input.reports_to || null,
557
+ JSON.stringify(input.metadata || {}),
558
+ timestamp,
559
+ timestamp
560
+ ]);
561
+ return getAgent(id, d);
562
+ }
563
+ function getAgent(id, db) {
564
+ const d = db || getDatabase();
565
+ const row = d.query("SELECT * FROM agents WHERE id = ?").get(id);
566
+ return row ? rowToAgent(row) : null;
567
+ }
568
+ function getAgentByName(name, db) {
569
+ const d = db || getDatabase();
570
+ const row = d.query("SELECT * FROM agents WHERE name = ?").get(name);
571
+ return row ? rowToAgent(row) : null;
572
+ }
573
+ function listAgents(db) {
574
+ const d = db || getDatabase();
575
+ return d.query("SELECT * FROM agents ORDER BY name").all().map(rowToAgent);
576
+ }
577
+ function updateAgentActivity(id, db) {
578
+ const d = db || getDatabase();
579
+ d.run("UPDATE agents SET last_seen_at = ? WHERE id = ?", [now(), id]);
580
+ }
581
+ function updateAgent(id, input, db) {
582
+ const d = db || getDatabase();
583
+ const agent = getAgent(id, d);
584
+ if (!agent)
585
+ throw new Error(`Agent not found: ${id}`);
586
+ const sets = ["last_seen_at = ?"];
587
+ const params = [now()];
588
+ if (input.name !== undefined) {
589
+ sets.push("name = ?");
590
+ params.push(input.name);
591
+ }
592
+ if (input.description !== undefined) {
593
+ sets.push("description = ?");
594
+ params.push(input.description);
595
+ }
596
+ if (input.role !== undefined) {
597
+ sets.push("role = ?");
598
+ params.push(input.role);
599
+ }
600
+ if (input.permissions !== undefined) {
601
+ sets.push("permissions = ?");
602
+ params.push(JSON.stringify(input.permissions));
603
+ }
604
+ if (input.reports_to !== undefined) {
605
+ sets.push("reports_to = ?");
606
+ params.push(input.reports_to);
607
+ }
608
+ if (input.metadata !== undefined) {
609
+ sets.push("metadata = ?");
610
+ params.push(JSON.stringify(input.metadata));
611
+ }
612
+ params.push(id);
613
+ d.run(`UPDATE agents SET ${sets.join(", ")} WHERE id = ?`, params);
614
+ return getAgent(id, d);
615
+ }
616
+ function deleteAgent(id, db) {
617
+ const d = db || getDatabase();
618
+ return d.run("DELETE FROM agents WHERE id = ?", [id]).changes > 0;
619
+ }
620
+ function getDirectReports(agentId, db) {
621
+ const d = db || getDatabase();
622
+ return d.query("SELECT * FROM agents WHERE reports_to = ? ORDER BY name").all(agentId).map(rowToAgent);
623
+ }
624
+ function getOrgChart(db) {
625
+ const agents = listAgents(db);
626
+ const byManager = new Map;
627
+ for (const a of agents) {
628
+ const key = a.reports_to;
629
+ if (!byManager.has(key))
630
+ byManager.set(key, []);
631
+ byManager.get(key).push(a);
632
+ }
633
+ function buildTree(parentId) {
634
+ const children = byManager.get(parentId) || [];
635
+ return children.map((a) => ({ agent: a, reports: buildTree(a.id) }));
636
+ }
637
+ return buildTree(null);
638
+ }
639
+ var init_agents = __esm(() => {
640
+ init_database();
641
+ });
642
+
512
643
  // src/db/webhooks.ts
513
644
  var exports_webhooks = {};
514
645
  __export(exports_webhooks, {
@@ -5399,54 +5530,8 @@ function deletePlan(id, db) {
5399
5530
  return result.changes > 0;
5400
5531
  }
5401
5532
 
5402
- // src/db/agents.ts
5403
- init_database();
5404
- function shortUuid() {
5405
- return crypto.randomUUID().slice(0, 8);
5406
- }
5407
- function rowToAgent(row) {
5408
- return {
5409
- ...row,
5410
- permissions: JSON.parse(row.permissions || '["*"]'),
5411
- metadata: JSON.parse(row.metadata || "{}")
5412
- };
5413
- }
5414
- function registerAgent(input, db) {
5415
- const d = db || getDatabase();
5416
- const existing = getAgentByName(input.name, d);
5417
- if (existing) {
5418
- d.run("UPDATE agents SET last_seen_at = ? WHERE id = ?", [now(), existing.id]);
5419
- return getAgent(existing.id, d);
5420
- }
5421
- const id = shortUuid();
5422
- const timestamp = now();
5423
- d.run(`INSERT INTO agents (id, name, description, role, permissions, metadata, created_at, last_seen_at)
5424
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [
5425
- id,
5426
- input.name,
5427
- input.description || null,
5428
- input.role || "agent",
5429
- JSON.stringify(input.permissions || ["*"]),
5430
- JSON.stringify(input.metadata || {}),
5431
- timestamp,
5432
- timestamp
5433
- ]);
5434
- return getAgent(id, d);
5435
- }
5436
- function getAgent(id, db) {
5437
- const d = db || getDatabase();
5438
- const row = d.query("SELECT * FROM agents WHERE id = ?").get(id);
5439
- return row ? rowToAgent(row) : null;
5440
- }
5441
- function getAgentByName(name, db) {
5442
- const d = db || getDatabase();
5443
- const row = d.query("SELECT * FROM agents WHERE name = ?").get(name);
5444
- return row ? rowToAgent(row) : null;
5445
- }
5446
- function listAgents(db) {
5447
- const d = db || getDatabase();
5448
- return d.query("SELECT * FROM agents ORDER BY name").all().map(rowToAgent);
5449
- }
5533
+ // src/mcp/index.ts
5534
+ init_agents();
5450
5535
 
5451
5536
  // src/db/task-lists.ts
5452
5537
  init_database();
@@ -6971,6 +7056,50 @@ In Progress:`);
6971
7056
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
6972
7057
  }
6973
7058
  });
7059
+ server.tool("get_org_chart", "Get agent org chart \u2014 who reports to who.", {}, async () => {
7060
+ try {
7061
+ let render = function(nodes, indent = 0) {
7062
+ return nodes.map((n) => {
7063
+ const prefix = " ".repeat(indent);
7064
+ const role = n.agent.role ? ` (${n.agent.role})` : "";
7065
+ const line = `${prefix}${n.agent.name}${role} [${n.agent.id}]`;
7066
+ const children = n.reports.length > 0 ? `
7067
+ ` + render(n.reports, indent + 1) : "";
7068
+ return line + children;
7069
+ }).join(`
7070
+ `);
7071
+ };
7072
+ const { getOrgChart: getOrgChart2 } = await Promise.resolve().then(() => (init_agents(), exports_agents));
7073
+ const tree = getOrgChart2();
7074
+ const text = tree.length > 0 ? render(tree) : "No agents registered.";
7075
+ return { content: [{ type: "text", text }] };
7076
+ } catch (e) {
7077
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
7078
+ }
7079
+ });
7080
+ server.tool("set_reports_to", "Set who an agent reports to in the org chart.", {
7081
+ agent_name: exports_external.string(),
7082
+ manager_name: exports_external.string().optional()
7083
+ }, async ({ agent_name, manager_name }) => {
7084
+ try {
7085
+ const agent = getAgentByName(agent_name);
7086
+ if (!agent)
7087
+ return { content: [{ type: "text", text: `Agent not found: ${agent_name}` }], isError: true };
7088
+ let managerId = null;
7089
+ if (manager_name) {
7090
+ const manager = getAgentByName(manager_name);
7091
+ if (!manager)
7092
+ return { content: [{ type: "text", text: `Manager not found: ${manager_name}` }], isError: true };
7093
+ managerId = manager.id;
7094
+ }
7095
+ const { updateAgent: updateAgent2 } = await Promise.resolve().then(() => (init_agents(), exports_agents));
7096
+ updateAgent2(agent.id, { reports_to: managerId });
7097
+ const result = managerId ? `${agent_name} now reports to ${manager_name}` : `${agent_name} reports to no one (top-level)`;
7098
+ return { content: [{ type: "text", text: result }] };
7099
+ } catch (e) {
7100
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
7101
+ }
7102
+ });
6974
7103
  server.tool("search_tools", "List tool names matching a query.", { query: exports_external.string().optional() }, async ({ query }) => {
6975
7104
  const all = [
6976
7105
  "create_task",
@@ -249,6 +249,7 @@ function ensureSchema(db) {
249
249
  ensureColumn("tasks", "approved_at", "TEXT");
250
250
  ensureColumn("agents", "role", "TEXT DEFAULT 'agent'");
251
251
  ensureColumn("agents", "permissions", `TEXT DEFAULT '["*"]'`);
252
+ ensureColumn("agents", "reports_to", "TEXT");
252
253
  ensureColumn("plans", "task_list_id", "TEXT");
253
254
  ensureColumn("plans", "agent_id", "TEXT");
254
255
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_plan ON tasks(plan_id)");
@@ -526,6 +527,10 @@ var init_database = __esm(() => {
526
527
  ALTER TABLE agents ADD COLUMN permissions TEXT DEFAULT '["*"]';
527
528
 
528
529
  INSERT OR IGNORE INTO _migrations (id) VALUES (10);
530
+ `,
531
+ `
532
+ ALTER TABLE agents ADD COLUMN reports_to TEXT;
533
+ INSERT OR IGNORE INTO _migrations (id) VALUES (11);
529
534
  `
530
535
  ];
531
536
  });
@@ -680,6 +685,8 @@ __export(exports_agents, {
680
685
  updateAgent: () => updateAgent,
681
686
  registerAgent: () => registerAgent,
682
687
  listAgents: () => listAgents,
688
+ getOrgChart: () => getOrgChart,
689
+ getDirectReports: () => getDirectReports,
683
690
  getAgentByName: () => getAgentByName,
684
691
  getAgent: () => getAgent,
685
692
  deleteAgent: () => deleteAgent
@@ -703,13 +710,14 @@ function registerAgent(input, db) {
703
710
  }
704
711
  const id = shortUuid();
705
712
  const timestamp = now();
706
- d.run(`INSERT INTO agents (id, name, description, role, permissions, metadata, created_at, last_seen_at)
707
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [
713
+ d.run(`INSERT INTO agents (id, name, description, role, permissions, reports_to, metadata, created_at, last_seen_at)
714
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
708
715
  id,
709
716
  input.name,
710
717
  input.description || null,
711
718
  input.role || "agent",
712
719
  JSON.stringify(input.permissions || ["*"]),
720
+ input.reports_to || null,
713
721
  JSON.stringify(input.metadata || {}),
714
722
  timestamp,
715
723
  timestamp
@@ -757,6 +765,10 @@ function updateAgent(id, input, db) {
757
765
  sets.push("permissions = ?");
758
766
  params.push(JSON.stringify(input.permissions));
759
767
  }
768
+ if (input.reports_to !== undefined) {
769
+ sets.push("reports_to = ?");
770
+ params.push(input.reports_to);
771
+ }
760
772
  if (input.metadata !== undefined) {
761
773
  sets.push("metadata = ?");
762
774
  params.push(JSON.stringify(input.metadata));
@@ -769,6 +781,25 @@ function deleteAgent(id, db) {
769
781
  const d = db || getDatabase();
770
782
  return d.run("DELETE FROM agents WHERE id = ?", [id]).changes > 0;
771
783
  }
784
+ function getDirectReports(agentId, db) {
785
+ const d = db || getDatabase();
786
+ return d.query("SELECT * FROM agents WHERE reports_to = ? ORDER BY name").all(agentId).map(rowToAgent);
787
+ }
788
+ function getOrgChart(db) {
789
+ const agents = listAgents(db);
790
+ const byManager = new Map;
791
+ for (const a of agents) {
792
+ const key = a.reports_to;
793
+ if (!byManager.has(key))
794
+ byManager.set(key, []);
795
+ byManager.get(key).push(a);
796
+ }
797
+ function buildTree(parentId) {
798
+ const children = byManager.get(parentId) || [];
799
+ return children.map((a) => ({ agent: a, reports: buildTree(a.id) }));
800
+ }
801
+ return buildTree(null);
802
+ }
772
803
  var init_agents = __esm(() => {
773
804
  init_database();
774
805
  });
@@ -1751,6 +1782,16 @@ Dashboard not found at: ${dashboardDir}`);
1751
1782
  return json({ error: e instanceof Error ? e.message : "Failed to claim" }, 500, port);
1752
1783
  }
1753
1784
  }
1785
+ if (path === "/api/org" && method === "GET") {
1786
+ const { getOrgChart: getOrgChart2 } = await Promise.resolve().then(() => (init_agents(), exports_agents));
1787
+ return json(getOrgChart2(), 200, port);
1788
+ }
1789
+ const teamMatch = path.match(/^\/api\/agents\/([^/]+)\/team$/);
1790
+ if (teamMatch && method === "GET") {
1791
+ const agentId = decodeURIComponent(teamMatch[1]);
1792
+ const { getDirectReports: getDirectReports2 } = await Promise.resolve().then(() => (init_agents(), exports_agents));
1793
+ return json(getDirectReports2(agentId), 200, port);
1794
+ }
1754
1795
  if (path === "/api/agents" && method === "GET") {
1755
1796
  return json(listAgents(), 200, port);
1756
1797
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/todos",
3
- "version": "0.9.29",
3
+ "version": "0.9.30",
4
4
  "description": "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",