@hasna/todos 0.9.29 → 0.9.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/index.js CHANGED
@@ -2253,6 +2253,9 @@ 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");
2257
+ ensureColumn("agents", "title", "TEXT");
2258
+ ensureColumn("agents", "level", "TEXT");
2256
2259
  ensureColumn("plans", "task_list_id", "TEXT");
2257
2260
  ensureColumn("plans", "agent_id", "TEXT");
2258
2261
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_plan ON tasks(plan_id)");
@@ -2544,6 +2547,12 @@ var init_database = __esm(() => {
2544
2547
  ALTER TABLE agents ADD COLUMN permissions TEXT DEFAULT '["*"]';
2545
2548
 
2546
2549
  INSERT OR IGNORE INTO _migrations (id) VALUES (10);
2550
+ `,
2551
+ `
2552
+ ALTER TABLE agents ADD COLUMN reports_to TEXT;
2553
+ ALTER TABLE agents ADD COLUMN title TEXT;
2554
+ ALTER TABLE agents ADD COLUMN level TEXT;
2555
+ INSERT OR IGNORE INTO _migrations (id) VALUES (11);
2547
2556
  `
2548
2557
  ];
2549
2558
  });
@@ -3363,6 +3372,8 @@ __export(exports_agents, {
3363
3372
  updateAgent: () => updateAgent,
3364
3373
  registerAgent: () => registerAgent,
3365
3374
  listAgents: () => listAgents,
3375
+ getOrgChart: () => getOrgChart,
3376
+ getDirectReports: () => getDirectReports,
3366
3377
  getAgentByName: () => getAgentByName,
3367
3378
  getAgent: () => getAgent,
3368
3379
  deleteAgent: () => deleteAgent
@@ -3386,13 +3397,16 @@ function registerAgent(input, db) {
3386
3397
  }
3387
3398
  const id = shortUuid();
3388
3399
  const timestamp = now();
3389
- d.run(`INSERT INTO agents (id, name, description, role, permissions, metadata, created_at, last_seen_at)
3390
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [
3400
+ d.run(`INSERT INTO agents (id, name, description, role, title, level, permissions, reports_to, metadata, created_at, last_seen_at)
3401
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
3391
3402
  id,
3392
3403
  input.name,
3393
3404
  input.description || null,
3394
3405
  input.role || "agent",
3406
+ input.title || null,
3407
+ input.level || null,
3395
3408
  JSON.stringify(input.permissions || ["*"]),
3409
+ input.reports_to || null,
3396
3410
  JSON.stringify(input.metadata || {}),
3397
3411
  timestamp,
3398
3412
  timestamp
@@ -3440,6 +3454,18 @@ function updateAgent(id, input, db) {
3440
3454
  sets.push("permissions = ?");
3441
3455
  params.push(JSON.stringify(input.permissions));
3442
3456
  }
3457
+ if (input.title !== undefined) {
3458
+ sets.push("title = ?");
3459
+ params.push(input.title);
3460
+ }
3461
+ if (input.level !== undefined) {
3462
+ sets.push("level = ?");
3463
+ params.push(input.level);
3464
+ }
3465
+ if (input.reports_to !== undefined) {
3466
+ sets.push("reports_to = ?");
3467
+ params.push(input.reports_to);
3468
+ }
3443
3469
  if (input.metadata !== undefined) {
3444
3470
  sets.push("metadata = ?");
3445
3471
  params.push(JSON.stringify(input.metadata));
@@ -3452,6 +3478,25 @@ function deleteAgent(id, db) {
3452
3478
  const d = db || getDatabase();
3453
3479
  return d.run("DELETE FROM agents WHERE id = ?", [id]).changes > 0;
3454
3480
  }
3481
+ function getDirectReports(agentId, db) {
3482
+ const d = db || getDatabase();
3483
+ return d.query("SELECT * FROM agents WHERE reports_to = ? ORDER BY name").all(agentId).map(rowToAgent);
3484
+ }
3485
+ function getOrgChart(db) {
3486
+ const agents = listAgents(db);
3487
+ const byManager = new Map;
3488
+ for (const a of agents) {
3489
+ const key = a.reports_to;
3490
+ if (!byManager.has(key))
3491
+ byManager.set(key, []);
3492
+ byManager.get(key).push(a);
3493
+ }
3494
+ function buildTree(parentId) {
3495
+ const children = byManager.get(parentId) || [];
3496
+ return children.map((a) => ({ agent: a, reports: buildTree(a.id) }));
3497
+ }
3498
+ return buildTree(null);
3499
+ }
3455
3500
  var init_agents = __esm(() => {
3456
3501
  init_database();
3457
3502
  });
@@ -9205,6 +9250,51 @@ In Progress:`);
9205
9250
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
9206
9251
  }
9207
9252
  });
9253
+ server.tool("get_org_chart", "Get agent org chart \u2014 who reports to who.", {}, async () => {
9254
+ try {
9255
+ let render = function(nodes, indent = 0) {
9256
+ return nodes.map((n) => {
9257
+ const prefix = " ".repeat(indent);
9258
+ const title = n.agent.title ? ` \u2014 ${n.agent.title}` : "";
9259
+ const level = n.agent.level ? ` (${n.agent.level})` : "";
9260
+ const line = `${prefix}${n.agent.name}${title}${level}`;
9261
+ const children = n.reports.length > 0 ? `
9262
+ ` + render(n.reports, indent + 1) : "";
9263
+ return line + children;
9264
+ }).join(`
9265
+ `);
9266
+ };
9267
+ const { getOrgChart: getOrgChart2 } = await Promise.resolve().then(() => (init_agents(), exports_agents));
9268
+ const tree = getOrgChart2();
9269
+ const text = tree.length > 0 ? render(tree) : "No agents registered.";
9270
+ return { content: [{ type: "text", text }] };
9271
+ } catch (e) {
9272
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
9273
+ }
9274
+ });
9275
+ server.tool("set_reports_to", "Set who an agent reports to in the org chart.", {
9276
+ agent_name: exports_external.string(),
9277
+ manager_name: exports_external.string().optional()
9278
+ }, async ({ agent_name, manager_name }) => {
9279
+ try {
9280
+ const agent = getAgentByName(agent_name);
9281
+ if (!agent)
9282
+ return { content: [{ type: "text", text: `Agent not found: ${agent_name}` }], isError: true };
9283
+ let managerId = null;
9284
+ if (manager_name) {
9285
+ const manager = getAgentByName(manager_name);
9286
+ if (!manager)
9287
+ return { content: [{ type: "text", text: `Manager not found: ${manager_name}` }], isError: true };
9288
+ managerId = manager.id;
9289
+ }
9290
+ const { updateAgent: updateAgent2 } = await Promise.resolve().then(() => (init_agents(), exports_agents));
9291
+ updateAgent2(agent.id, { reports_to: managerId });
9292
+ const result = managerId ? `${agent_name} now reports to ${manager_name}` : `${agent_name} reports to no one (top-level)`;
9293
+ return { content: [{ type: "text", text: result }] };
9294
+ } catch (e) {
9295
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
9296
+ }
9297
+ });
9208
9298
  server.tool("search_tools", "List tool names matching a query.", { query: exports_external.string().optional() }, async ({ query }) => {
9209
9299
  const all = [
9210
9300
  "create_task",
@@ -9656,6 +9746,16 @@ Dashboard not found at: ${dashboardDir}`);
9656
9746
  return json({ error: e instanceof Error ? e.message : "Failed to claim" }, 500, port);
9657
9747
  }
9658
9748
  }
9749
+ if (path === "/api/org" && method === "GET") {
9750
+ const { getOrgChart: getOrgChart2 } = await Promise.resolve().then(() => (init_agents(), exports_agents));
9751
+ return json(getOrgChart2(), 200, port);
9752
+ }
9753
+ const teamMatch = path.match(/^\/api\/agents\/([^/]+)\/team$/);
9754
+ if (teamMatch && method === "GET") {
9755
+ const agentId = decodeURIComponent(teamMatch[1]);
9756
+ const { getDirectReports: getDirectReports2 } = await Promise.resolve().then(() => (init_agents(), exports_agents));
9757
+ return json(getDirectReports2(agentId), 200, port);
9758
+ }
9659
9759
  if (path === "/api/agents" && method === "GET") {
9660
9760
  return json(listAgents(), 200, port);
9661
9761
  }
@@ -12194,6 +12294,53 @@ program2.command("agents").description("List registered agents").action(() => {
12194
12294
  handleError(e);
12195
12295
  }
12196
12296
  });
12297
+ 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) => {
12298
+ const globalOpts = program2.opts();
12299
+ const { getOrgChart: getOrgChart2, getAgentByName: getByName, updateAgent: update } = (init_agents(), __toCommonJS(exports_agents));
12300
+ if (opts.set) {
12301
+ const [agentName, managerName] = opts.set.split("=");
12302
+ const agent = getByName(agentName);
12303
+ if (!agent) {
12304
+ console.error(chalk.red(`Agent not found: ${agentName}`));
12305
+ process.exit(1);
12306
+ }
12307
+ let managerId = null;
12308
+ if (managerName) {
12309
+ const manager = getByName(managerName);
12310
+ if (!manager) {
12311
+ console.error(chalk.red(`Manager not found: ${managerName}`));
12312
+ process.exit(1);
12313
+ }
12314
+ managerId = manager.id;
12315
+ }
12316
+ update(agent.id, { reports_to: managerId });
12317
+ if (globalOpts.json) {
12318
+ output({ agent: agentName, reports_to: managerName || null }, true);
12319
+ } else {
12320
+ console.log(chalk.green(managerId ? `${agentName} \u2192 ${managerName}` : `${agentName} \u2192 (top-level)`));
12321
+ }
12322
+ return;
12323
+ }
12324
+ const tree = getOrgChart2();
12325
+ if (globalOpts.json) {
12326
+ output(tree, true);
12327
+ return;
12328
+ }
12329
+ if (tree.length === 0) {
12330
+ console.log(chalk.dim("No agents registered."));
12331
+ return;
12332
+ }
12333
+ function render2(nodes, indent = 0) {
12334
+ for (const n of nodes) {
12335
+ const prefix = " ".repeat(indent);
12336
+ const title = n.agent.title ? chalk.cyan(` \u2014 ${n.agent.title}`) : "";
12337
+ const level = n.agent.level ? chalk.dim(` (${n.agent.level})`) : "";
12338
+ console.log(`${prefix}${indent > 0 ? "\u251C\u2500\u2500 " : ""}${chalk.bold(n.agent.name)}${title}${level}`);
12339
+ render2(n.reports, indent + 1);
12340
+ }
12341
+ }
12342
+ render2(tree);
12343
+ });
12197
12344
  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
12345
  try {
12199
12346
  const globalOpts = program2.opts();
package/dist/index.js CHANGED
@@ -264,6 +264,12 @@ 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
+ ALTER TABLE agents ADD COLUMN title TEXT;
271
+ ALTER TABLE agents ADD COLUMN level TEXT;
272
+ INSERT OR IGNORE INTO _migrations (id) VALUES (11);
267
273
  `
268
274
  ];
269
275
  var _db = null;
@@ -385,6 +391,9 @@ function ensureSchema(db) {
385
391
  ensureColumn("tasks", "approved_at", "TEXT");
386
392
  ensureColumn("agents", "role", "TEXT DEFAULT 'agent'");
387
393
  ensureColumn("agents", "permissions", `TEXT DEFAULT '["*"]'`);
394
+ ensureColumn("agents", "reports_to", "TEXT");
395
+ ensureColumn("agents", "title", "TEXT");
396
+ ensureColumn("agents", "level", "TEXT");
388
397
  ensureColumn("plans", "task_list_id", "TEXT");
389
398
  ensureColumn("plans", "agent_id", "TEXT");
390
399
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_plan ON tasks(plan_id)");
@@ -1388,13 +1397,16 @@ function registerAgent(input, db) {
1388
1397
  }
1389
1398
  const id = shortUuid();
1390
1399
  const timestamp = now();
1391
- d.run(`INSERT INTO agents (id, name, description, role, permissions, metadata, created_at, last_seen_at)
1392
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [
1400
+ d.run(`INSERT INTO agents (id, name, description, role, title, level, permissions, reports_to, metadata, created_at, last_seen_at)
1401
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
1393
1402
  id,
1394
1403
  input.name,
1395
1404
  input.description || null,
1396
1405
  input.role || "agent",
1406
+ input.title || null,
1407
+ input.level || null,
1397
1408
  JSON.stringify(input.permissions || ["*"]),
1409
+ input.reports_to || null,
1398
1410
  JSON.stringify(input.metadata || {}),
1399
1411
  timestamp,
1400
1412
  timestamp
@@ -1442,6 +1454,18 @@ function updateAgent(id, input, db) {
1442
1454
  sets.push("permissions = ?");
1443
1455
  params.push(JSON.stringify(input.permissions));
1444
1456
  }
1457
+ if (input.title !== undefined) {
1458
+ sets.push("title = ?");
1459
+ params.push(input.title);
1460
+ }
1461
+ if (input.level !== undefined) {
1462
+ sets.push("level = ?");
1463
+ params.push(input.level);
1464
+ }
1465
+ if (input.reports_to !== undefined) {
1466
+ sets.push("reports_to = ?");
1467
+ params.push(input.reports_to);
1468
+ }
1445
1469
  if (input.metadata !== undefined) {
1446
1470
  sets.push("metadata = ?");
1447
1471
  params.push(JSON.stringify(input.metadata));
@@ -1454,6 +1478,25 @@ function deleteAgent(id, db) {
1454
1478
  const d = db || getDatabase();
1455
1479
  return d.run("DELETE FROM agents WHERE id = ?", [id]).changes > 0;
1456
1480
  }
1481
+ function getDirectReports(agentId, db) {
1482
+ const d = db || getDatabase();
1483
+ return d.query("SELECT * FROM agents WHERE reports_to = ? ORDER BY name").all(agentId).map(rowToAgent);
1484
+ }
1485
+ function getOrgChart(db) {
1486
+ const agents = listAgents(db);
1487
+ const byManager = new Map;
1488
+ for (const a of agents) {
1489
+ const key = a.reports_to;
1490
+ if (!byManager.has(key))
1491
+ byManager.set(key, []);
1492
+ byManager.get(key).push(a);
1493
+ }
1494
+ function buildTree(parentId) {
1495
+ const children = byManager.get(parentId) || [];
1496
+ return children.map((a) => ({ agent: a, reports: buildTree(a.id) }));
1497
+ }
1498
+ return buildTree(null);
1499
+ }
1457
1500
  // src/db/task-lists.ts
1458
1501
  function rowToTaskList(row) {
1459
1502
  return {
@@ -2264,6 +2307,8 @@ export {
2264
2307
  getProjectByPath,
2265
2308
  getProject,
2266
2309
  getPlan,
2310
+ getOrgChart,
2311
+ getDirectReports,
2267
2312
  getDatabase,
2268
2313
  getCompletionGuardConfig,
2269
2314
  getComment,
package/dist/mcp/index.js CHANGED
@@ -187,6 +187,9 @@ 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");
191
+ ensureColumn("agents", "title", "TEXT");
192
+ ensureColumn("agents", "level", "TEXT");
190
193
  ensureColumn("plans", "task_list_id", "TEXT");
191
194
  ensureColumn("plans", "agent_id", "TEXT");
192
195
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_plan ON tasks(plan_id)");
@@ -478,6 +481,12 @@ var init_database = __esm(() => {
478
481
  ALTER TABLE agents ADD COLUMN permissions TEXT DEFAULT '["*"]';
479
482
 
480
483
  INSERT OR IGNORE INTO _migrations (id) VALUES (10);
484
+ `,
485
+ `
486
+ ALTER TABLE agents ADD COLUMN reports_to TEXT;
487
+ ALTER TABLE agents ADD COLUMN title TEXT;
488
+ ALTER TABLE agents ADD COLUMN level TEXT;
489
+ INSERT OR IGNORE INTO _migrations (id) VALUES (11);
481
490
  `
482
491
  ];
483
492
  });
@@ -509,6 +518,142 @@ var init_audit = __esm(() => {
509
518
  init_database();
510
519
  });
511
520
 
521
+ // src/db/agents.ts
522
+ var exports_agents = {};
523
+ __export(exports_agents, {
524
+ updateAgentActivity: () => updateAgentActivity,
525
+ updateAgent: () => updateAgent,
526
+ registerAgent: () => registerAgent,
527
+ listAgents: () => listAgents,
528
+ getOrgChart: () => getOrgChart,
529
+ getDirectReports: () => getDirectReports,
530
+ getAgentByName: () => getAgentByName,
531
+ getAgent: () => getAgent,
532
+ deleteAgent: () => deleteAgent
533
+ });
534
+ function shortUuid() {
535
+ return crypto.randomUUID().slice(0, 8);
536
+ }
537
+ function rowToAgent(row) {
538
+ return {
539
+ ...row,
540
+ permissions: JSON.parse(row.permissions || '["*"]'),
541
+ metadata: JSON.parse(row.metadata || "{}")
542
+ };
543
+ }
544
+ function registerAgent(input, db) {
545
+ const d = db || getDatabase();
546
+ const existing = getAgentByName(input.name, d);
547
+ if (existing) {
548
+ d.run("UPDATE agents SET last_seen_at = ? WHERE id = ?", [now(), existing.id]);
549
+ return getAgent(existing.id, d);
550
+ }
551
+ const id = shortUuid();
552
+ const timestamp = now();
553
+ d.run(`INSERT INTO agents (id, name, description, role, title, level, permissions, reports_to, metadata, created_at, last_seen_at)
554
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
555
+ id,
556
+ input.name,
557
+ input.description || null,
558
+ input.role || "agent",
559
+ input.title || null,
560
+ input.level || null,
561
+ JSON.stringify(input.permissions || ["*"]),
562
+ input.reports_to || null,
563
+ JSON.stringify(input.metadata || {}),
564
+ timestamp,
565
+ timestamp
566
+ ]);
567
+ return getAgent(id, d);
568
+ }
569
+ function getAgent(id, db) {
570
+ const d = db || getDatabase();
571
+ const row = d.query("SELECT * FROM agents WHERE id = ?").get(id);
572
+ return row ? rowToAgent(row) : null;
573
+ }
574
+ function getAgentByName(name, db) {
575
+ const d = db || getDatabase();
576
+ const row = d.query("SELECT * FROM agents WHERE name = ?").get(name);
577
+ return row ? rowToAgent(row) : null;
578
+ }
579
+ function listAgents(db) {
580
+ const d = db || getDatabase();
581
+ return d.query("SELECT * FROM agents ORDER BY name").all().map(rowToAgent);
582
+ }
583
+ function updateAgentActivity(id, db) {
584
+ const d = db || getDatabase();
585
+ d.run("UPDATE agents SET last_seen_at = ? WHERE id = ?", [now(), id]);
586
+ }
587
+ function updateAgent(id, input, db) {
588
+ const d = db || getDatabase();
589
+ const agent = getAgent(id, d);
590
+ if (!agent)
591
+ throw new Error(`Agent not found: ${id}`);
592
+ const sets = ["last_seen_at = ?"];
593
+ const params = [now()];
594
+ if (input.name !== undefined) {
595
+ sets.push("name = ?");
596
+ params.push(input.name);
597
+ }
598
+ if (input.description !== undefined) {
599
+ sets.push("description = ?");
600
+ params.push(input.description);
601
+ }
602
+ if (input.role !== undefined) {
603
+ sets.push("role = ?");
604
+ params.push(input.role);
605
+ }
606
+ if (input.permissions !== undefined) {
607
+ sets.push("permissions = ?");
608
+ params.push(JSON.stringify(input.permissions));
609
+ }
610
+ if (input.title !== undefined) {
611
+ sets.push("title = ?");
612
+ params.push(input.title);
613
+ }
614
+ if (input.level !== undefined) {
615
+ sets.push("level = ?");
616
+ params.push(input.level);
617
+ }
618
+ if (input.reports_to !== undefined) {
619
+ sets.push("reports_to = ?");
620
+ params.push(input.reports_to);
621
+ }
622
+ if (input.metadata !== undefined) {
623
+ sets.push("metadata = ?");
624
+ params.push(JSON.stringify(input.metadata));
625
+ }
626
+ params.push(id);
627
+ d.run(`UPDATE agents SET ${sets.join(", ")} WHERE id = ?`, params);
628
+ return getAgent(id, d);
629
+ }
630
+ function deleteAgent(id, db) {
631
+ const d = db || getDatabase();
632
+ return d.run("DELETE FROM agents WHERE id = ?", [id]).changes > 0;
633
+ }
634
+ function getDirectReports(agentId, db) {
635
+ const d = db || getDatabase();
636
+ return d.query("SELECT * FROM agents WHERE reports_to = ? ORDER BY name").all(agentId).map(rowToAgent);
637
+ }
638
+ function getOrgChart(db) {
639
+ const agents = listAgents(db);
640
+ const byManager = new Map;
641
+ for (const a of agents) {
642
+ const key = a.reports_to;
643
+ if (!byManager.has(key))
644
+ byManager.set(key, []);
645
+ byManager.get(key).push(a);
646
+ }
647
+ function buildTree(parentId) {
648
+ const children = byManager.get(parentId) || [];
649
+ return children.map((a) => ({ agent: a, reports: buildTree(a.id) }));
650
+ }
651
+ return buildTree(null);
652
+ }
653
+ var init_agents = __esm(() => {
654
+ init_database();
655
+ });
656
+
512
657
  // src/db/webhooks.ts
513
658
  var exports_webhooks = {};
514
659
  __export(exports_webhooks, {
@@ -5399,54 +5544,8 @@ function deletePlan(id, db) {
5399
5544
  return result.changes > 0;
5400
5545
  }
5401
5546
 
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
- }
5547
+ // src/mcp/index.ts
5548
+ init_agents();
5450
5549
 
5451
5550
  // src/db/task-lists.ts
5452
5551
  init_database();
@@ -6971,6 +7070,51 @@ In Progress:`);
6971
7070
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
6972
7071
  }
6973
7072
  });
7073
+ server.tool("get_org_chart", "Get agent org chart \u2014 who reports to who.", {}, async () => {
7074
+ try {
7075
+ let render = function(nodes, indent = 0) {
7076
+ return nodes.map((n) => {
7077
+ const prefix = " ".repeat(indent);
7078
+ const title = n.agent.title ? ` \u2014 ${n.agent.title}` : "";
7079
+ const level = n.agent.level ? ` (${n.agent.level})` : "";
7080
+ const line = `${prefix}${n.agent.name}${title}${level}`;
7081
+ const children = n.reports.length > 0 ? `
7082
+ ` + render(n.reports, indent + 1) : "";
7083
+ return line + children;
7084
+ }).join(`
7085
+ `);
7086
+ };
7087
+ const { getOrgChart: getOrgChart2 } = await Promise.resolve().then(() => (init_agents(), exports_agents));
7088
+ const tree = getOrgChart2();
7089
+ const text = tree.length > 0 ? render(tree) : "No agents registered.";
7090
+ return { content: [{ type: "text", text }] };
7091
+ } catch (e) {
7092
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
7093
+ }
7094
+ });
7095
+ server.tool("set_reports_to", "Set who an agent reports to in the org chart.", {
7096
+ agent_name: exports_external.string(),
7097
+ manager_name: exports_external.string().optional()
7098
+ }, async ({ agent_name, manager_name }) => {
7099
+ try {
7100
+ const agent = getAgentByName(agent_name);
7101
+ if (!agent)
7102
+ return { content: [{ type: "text", text: `Agent not found: ${agent_name}` }], isError: true };
7103
+ let managerId = null;
7104
+ if (manager_name) {
7105
+ const manager = getAgentByName(manager_name);
7106
+ if (!manager)
7107
+ return { content: [{ type: "text", text: `Manager not found: ${manager_name}` }], isError: true };
7108
+ managerId = manager.id;
7109
+ }
7110
+ const { updateAgent: updateAgent2 } = await Promise.resolve().then(() => (init_agents(), exports_agents));
7111
+ updateAgent2(agent.id, { reports_to: managerId });
7112
+ const result = managerId ? `${agent_name} now reports to ${manager_name}` : `${agent_name} reports to no one (top-level)`;
7113
+ return { content: [{ type: "text", text: result }] };
7114
+ } catch (e) {
7115
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
7116
+ }
7117
+ });
6974
7118
  server.tool("search_tools", "List tool names matching a query.", { query: exports_external.string().optional() }, async ({ query }) => {
6975
7119
  const all = [
6976
7120
  "create_task",
@@ -249,6 +249,9 @@ 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");
253
+ ensureColumn("agents", "title", "TEXT");
254
+ ensureColumn("agents", "level", "TEXT");
252
255
  ensureColumn("plans", "task_list_id", "TEXT");
253
256
  ensureColumn("plans", "agent_id", "TEXT");
254
257
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_plan ON tasks(plan_id)");
@@ -526,6 +529,12 @@ var init_database = __esm(() => {
526
529
  ALTER TABLE agents ADD COLUMN permissions TEXT DEFAULT '["*"]';
527
530
 
528
531
  INSERT OR IGNORE INTO _migrations (id) VALUES (10);
532
+ `,
533
+ `
534
+ ALTER TABLE agents ADD COLUMN reports_to TEXT;
535
+ ALTER TABLE agents ADD COLUMN title TEXT;
536
+ ALTER TABLE agents ADD COLUMN level TEXT;
537
+ INSERT OR IGNORE INTO _migrations (id) VALUES (11);
529
538
  `
530
539
  ];
531
540
  });
@@ -680,6 +689,8 @@ __export(exports_agents, {
680
689
  updateAgent: () => updateAgent,
681
690
  registerAgent: () => registerAgent,
682
691
  listAgents: () => listAgents,
692
+ getOrgChart: () => getOrgChart,
693
+ getDirectReports: () => getDirectReports,
683
694
  getAgentByName: () => getAgentByName,
684
695
  getAgent: () => getAgent,
685
696
  deleteAgent: () => deleteAgent
@@ -703,13 +714,16 @@ function registerAgent(input, db) {
703
714
  }
704
715
  const id = shortUuid();
705
716
  const timestamp = now();
706
- d.run(`INSERT INTO agents (id, name, description, role, permissions, metadata, created_at, last_seen_at)
707
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [
717
+ d.run(`INSERT INTO agents (id, name, description, role, title, level, permissions, reports_to, metadata, created_at, last_seen_at)
718
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
708
719
  id,
709
720
  input.name,
710
721
  input.description || null,
711
722
  input.role || "agent",
723
+ input.title || null,
724
+ input.level || null,
712
725
  JSON.stringify(input.permissions || ["*"]),
726
+ input.reports_to || null,
713
727
  JSON.stringify(input.metadata || {}),
714
728
  timestamp,
715
729
  timestamp
@@ -757,6 +771,18 @@ function updateAgent(id, input, db) {
757
771
  sets.push("permissions = ?");
758
772
  params.push(JSON.stringify(input.permissions));
759
773
  }
774
+ if (input.title !== undefined) {
775
+ sets.push("title = ?");
776
+ params.push(input.title);
777
+ }
778
+ if (input.level !== undefined) {
779
+ sets.push("level = ?");
780
+ params.push(input.level);
781
+ }
782
+ if (input.reports_to !== undefined) {
783
+ sets.push("reports_to = ?");
784
+ params.push(input.reports_to);
785
+ }
760
786
  if (input.metadata !== undefined) {
761
787
  sets.push("metadata = ?");
762
788
  params.push(JSON.stringify(input.metadata));
@@ -769,6 +795,25 @@ function deleteAgent(id, db) {
769
795
  const d = db || getDatabase();
770
796
  return d.run("DELETE FROM agents WHERE id = ?", [id]).changes > 0;
771
797
  }
798
+ function getDirectReports(agentId, db) {
799
+ const d = db || getDatabase();
800
+ return d.query("SELECT * FROM agents WHERE reports_to = ? ORDER BY name").all(agentId).map(rowToAgent);
801
+ }
802
+ function getOrgChart(db) {
803
+ const agents = listAgents(db);
804
+ const byManager = new Map;
805
+ for (const a of agents) {
806
+ const key = a.reports_to;
807
+ if (!byManager.has(key))
808
+ byManager.set(key, []);
809
+ byManager.get(key).push(a);
810
+ }
811
+ function buildTree(parentId) {
812
+ const children = byManager.get(parentId) || [];
813
+ return children.map((a) => ({ agent: a, reports: buildTree(a.id) }));
814
+ }
815
+ return buildTree(null);
816
+ }
772
817
  var init_agents = __esm(() => {
773
818
  init_database();
774
819
  });
@@ -1751,6 +1796,16 @@ Dashboard not found at: ${dashboardDir}`);
1751
1796
  return json({ error: e instanceof Error ? e.message : "Failed to claim" }, 500, port);
1752
1797
  }
1753
1798
  }
1799
+ if (path === "/api/org" && method === "GET") {
1800
+ const { getOrgChart: getOrgChart2 } = await Promise.resolve().then(() => (init_agents(), exports_agents));
1801
+ return json(getOrgChart2(), 200, port);
1802
+ }
1803
+ const teamMatch = path.match(/^\/api\/agents\/([^/]+)\/team$/);
1804
+ if (teamMatch && method === "GET") {
1805
+ const agentId = decodeURIComponent(teamMatch[1]);
1806
+ const { getDirectReports: getDirectReports2 } = await Promise.resolve().then(() => (init_agents(), exports_agents));
1807
+ return json(getDirectReports2(agentId), 200, port);
1808
+ }
1754
1809
  if (path === "/api/agents" && method === "GET") {
1755
1810
  return json(listAgents(), 200, port);
1756
1811
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/todos",
3
- "version": "0.9.29",
3
+ "version": "0.9.31",
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",