@hasna/todos 0.9.30 → 0.9.32

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
@@ -2188,6 +2188,13 @@ function ensureSchema(db) {
2188
2188
  db.exec(sql);
2189
2189
  } catch {}
2190
2190
  };
2191
+ ensureTable("orgs", `
2192
+ CREATE TABLE orgs (
2193
+ id TEXT PRIMARY KEY, name TEXT NOT NULL UNIQUE, description TEXT,
2194
+ metadata TEXT DEFAULT '{}',
2195
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
2196
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
2197
+ )`);
2191
2198
  ensureTable("agents", `
2192
2199
  CREATE TABLE agents (
2193
2200
  id TEXT PRIMARY KEY, name TEXT NOT NULL UNIQUE, description TEXT,
@@ -2254,6 +2261,10 @@ function ensureSchema(db) {
2254
2261
  ensureColumn("agents", "role", "TEXT DEFAULT 'agent'");
2255
2262
  ensureColumn("agents", "permissions", `TEXT DEFAULT '["*"]'`);
2256
2263
  ensureColumn("agents", "reports_to", "TEXT");
2264
+ ensureColumn("agents", "title", "TEXT");
2265
+ ensureColumn("agents", "level", "TEXT");
2266
+ ensureColumn("agents", "org_id", "TEXT");
2267
+ ensureColumn("projects", "org_id", "TEXT");
2257
2268
  ensureColumn("plans", "task_list_id", "TEXT");
2258
2269
  ensureColumn("plans", "agent_id", "TEXT");
2259
2270
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_plan ON tasks(plan_id)");
@@ -2548,7 +2559,22 @@ var init_database = __esm(() => {
2548
2559
  `,
2549
2560
  `
2550
2561
  ALTER TABLE agents ADD COLUMN reports_to TEXT;
2562
+ ALTER TABLE agents ADD COLUMN title TEXT;
2563
+ ALTER TABLE agents ADD COLUMN level TEXT;
2551
2564
  INSERT OR IGNORE INTO _migrations (id) VALUES (11);
2565
+ `,
2566
+ `
2567
+ CREATE TABLE IF NOT EXISTS orgs (
2568
+ id TEXT PRIMARY KEY,
2569
+ name TEXT NOT NULL UNIQUE,
2570
+ description TEXT,
2571
+ metadata TEXT DEFAULT '{}',
2572
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
2573
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
2574
+ );
2575
+ ALTER TABLE agents ADD COLUMN org_id TEXT REFERENCES orgs(id) ON DELETE SET NULL;
2576
+ ALTER TABLE projects ADD COLUMN org_id TEXT REFERENCES orgs(id) ON DELETE SET NULL;
2577
+ INSERT OR IGNORE INTO _migrations (id) VALUES (12);
2552
2578
  `
2553
2579
  ];
2554
2580
  });
@@ -3393,14 +3419,17 @@ function registerAgent(input, db) {
3393
3419
  }
3394
3420
  const id = shortUuid();
3395
3421
  const timestamp = now();
3396
- d.run(`INSERT INTO agents (id, name, description, role, permissions, reports_to, metadata, created_at, last_seen_at)
3397
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
3422
+ d.run(`INSERT INTO agents (id, name, description, role, title, level, permissions, reports_to, org_id, metadata, created_at, last_seen_at)
3423
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
3398
3424
  id,
3399
3425
  input.name,
3400
3426
  input.description || null,
3401
3427
  input.role || "agent",
3428
+ input.title || null,
3429
+ input.level || null,
3402
3430
  JSON.stringify(input.permissions || ["*"]),
3403
3431
  input.reports_to || null,
3432
+ input.org_id || null,
3404
3433
  JSON.stringify(input.metadata || {}),
3405
3434
  timestamp,
3406
3435
  timestamp
@@ -3448,10 +3477,22 @@ function updateAgent(id, input, db) {
3448
3477
  sets.push("permissions = ?");
3449
3478
  params.push(JSON.stringify(input.permissions));
3450
3479
  }
3480
+ if (input.title !== undefined) {
3481
+ sets.push("title = ?");
3482
+ params.push(input.title);
3483
+ }
3484
+ if (input.level !== undefined) {
3485
+ sets.push("level = ?");
3486
+ params.push(input.level);
3487
+ }
3451
3488
  if (input.reports_to !== undefined) {
3452
3489
  sets.push("reports_to = ?");
3453
3490
  params.push(input.reports_to);
3454
3491
  }
3492
+ if (input.org_id !== undefined) {
3493
+ sets.push("org_id = ?");
3494
+ params.push(input.org_id);
3495
+ }
3455
3496
  if (input.metadata !== undefined) {
3456
3497
  sets.push("metadata = ?");
3457
3498
  params.push(JSON.stringify(input.metadata));
@@ -9241,8 +9282,9 @@ In Progress:`);
9241
9282
  let render = function(nodes, indent = 0) {
9242
9283
  return nodes.map((n) => {
9243
9284
  const prefix = " ".repeat(indent);
9244
- const role = n.agent.role ? ` (${n.agent.role})` : "";
9245
- const line = `${prefix}${n.agent.name}${role} [${n.agent.id}]`;
9285
+ const title = n.agent.title ? ` \u2014 ${n.agent.title}` : "";
9286
+ const level = n.agent.level ? ` (${n.agent.level})` : "";
9287
+ const line = `${prefix}${n.agent.name}${title}${level}`;
9246
9288
  const children = n.reports.length > 0 ? `
9247
9289
  ` + render(n.reports, indent + 1) : "";
9248
9290
  return line + children;
@@ -9375,6 +9417,71 @@ In Progress:`);
9375
9417
  });
9376
9418
  });
9377
9419
 
9420
+ // src/db/orgs.ts
9421
+ var exports_orgs = {};
9422
+ __export(exports_orgs, {
9423
+ updateOrg: () => updateOrg,
9424
+ listOrgs: () => listOrgs,
9425
+ getOrgByName: () => getOrgByName,
9426
+ getOrg: () => getOrg,
9427
+ deleteOrg: () => deleteOrg,
9428
+ createOrg: () => createOrg
9429
+ });
9430
+ function rowToOrg(row) {
9431
+ return { ...row, metadata: JSON.parse(row.metadata || "{}") };
9432
+ }
9433
+ function createOrg(input, db) {
9434
+ const d = db || getDatabase();
9435
+ const id = uuid();
9436
+ const timestamp = now();
9437
+ d.run(`INSERT INTO orgs (id, name, description, metadata, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)`, [id, input.name, input.description || null, JSON.stringify(input.metadata || {}), timestamp, timestamp]);
9438
+ return getOrg(id, d);
9439
+ }
9440
+ function getOrg(id, db) {
9441
+ const d = db || getDatabase();
9442
+ const row = d.query("SELECT * FROM orgs WHERE id = ?").get(id);
9443
+ return row ? rowToOrg(row) : null;
9444
+ }
9445
+ function getOrgByName(name, db) {
9446
+ const d = db || getDatabase();
9447
+ const row = d.query("SELECT * FROM orgs WHERE name = ?").get(name);
9448
+ return row ? rowToOrg(row) : null;
9449
+ }
9450
+ function listOrgs(db) {
9451
+ const d = db || getDatabase();
9452
+ return d.query("SELECT * FROM orgs ORDER BY name").all().map(rowToOrg);
9453
+ }
9454
+ function updateOrg(id, input, db) {
9455
+ const d = db || getDatabase();
9456
+ const org = getOrg(id, d);
9457
+ if (!org)
9458
+ throw new Error(`Org not found: ${id}`);
9459
+ const sets = ["updated_at = ?"];
9460
+ const params = [now()];
9461
+ if (input.name !== undefined) {
9462
+ sets.push("name = ?");
9463
+ params.push(input.name);
9464
+ }
9465
+ if (input.description !== undefined) {
9466
+ sets.push("description = ?");
9467
+ params.push(input.description);
9468
+ }
9469
+ if (input.metadata !== undefined) {
9470
+ sets.push("metadata = ?");
9471
+ params.push(JSON.stringify(input.metadata));
9472
+ }
9473
+ params.push(id);
9474
+ d.run(`UPDATE orgs SET ${sets.join(", ")} WHERE id = ?`, params);
9475
+ return getOrg(id, d);
9476
+ }
9477
+ function deleteOrg(id, db) {
9478
+ const d = db || getDatabase();
9479
+ return d.run("DELETE FROM orgs WHERE id = ?", [id]).changes > 0;
9480
+ }
9481
+ var init_orgs = __esm(() => {
9482
+ init_database();
9483
+ });
9484
+
9378
9485
  // src/server/serve.ts
9379
9486
  var exports_serve = {};
9380
9487
  __export(exports_serve, {
@@ -9731,6 +9838,36 @@ Dashboard not found at: ${dashboardDir}`);
9731
9838
  return json({ error: e instanceof Error ? e.message : "Failed to claim" }, 500, port);
9732
9839
  }
9733
9840
  }
9841
+ if (path === "/api/orgs" && method === "GET") {
9842
+ const { listOrgs: listOrgs2 } = await Promise.resolve().then(() => (init_orgs(), exports_orgs));
9843
+ return json(listOrgs2(), 200, port);
9844
+ }
9845
+ if (path === "/api/orgs" && method === "POST") {
9846
+ try {
9847
+ const body = await req.json();
9848
+ if (!body.name)
9849
+ return json({ error: "Missing name" }, 400, port);
9850
+ const { createOrg: createOrg2 } = await Promise.resolve().then(() => (init_orgs(), exports_orgs));
9851
+ return json(createOrg2(body), 201, port);
9852
+ } catch (e) {
9853
+ return json({ error: e instanceof Error ? e.message : "Failed" }, 500, port);
9854
+ }
9855
+ }
9856
+ const orgMatch = path.match(/^\/api\/orgs\/([^/]+)$/);
9857
+ if (orgMatch && method === "PATCH") {
9858
+ try {
9859
+ const body = await req.json();
9860
+ const { updateOrg: updateOrg2 } = await Promise.resolve().then(() => (init_orgs(), exports_orgs));
9861
+ return json(updateOrg2(orgMatch[1], body), 200, port);
9862
+ } catch (e) {
9863
+ return json({ error: e instanceof Error ? e.message : "Failed" }, 500, port);
9864
+ }
9865
+ }
9866
+ if (orgMatch && method === "DELETE") {
9867
+ const { deleteOrg: deleteOrg2 } = await Promise.resolve().then(() => (init_orgs(), exports_orgs));
9868
+ const deleted = deleteOrg2(orgMatch[1]);
9869
+ return json(deleted ? { success: true } : { error: "Not found" }, deleted ? 200 : 404, port);
9870
+ }
9734
9871
  if (path === "/api/org" && method === "GET") {
9735
9872
  const { getOrgChart: getOrgChart2 } = await Promise.resolve().then(() => (init_agents(), exports_agents));
9736
9873
  return json(getOrgChart2(), 200, port);
@@ -12318,8 +12455,9 @@ program2.command("org").description("Show agent org chart \u2014 who reports to
12318
12455
  function render2(nodes, indent = 0) {
12319
12456
  for (const n of nodes) {
12320
12457
  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)}`);
12458
+ const title = n.agent.title ? chalk.cyan(` \u2014 ${n.agent.title}`) : "";
12459
+ const level = n.agent.level ? chalk.dim(` (${n.agent.level})`) : "";
12460
+ console.log(`${prefix}${indent > 0 ? "\u251C\u2500\u2500 " : ""}${chalk.bold(n.agent.name)}${title}${level}`);
12323
12461
  render2(n.reports, indent + 1);
12324
12462
  }
12325
12463
  }
package/dist/index.js CHANGED
@@ -267,7 +267,22 @@ var MIGRATIONS = [
267
267
  `,
268
268
  `
269
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;
270
272
  INSERT OR IGNORE INTO _migrations (id) VALUES (11);
273
+ `,
274
+ `
275
+ CREATE TABLE IF NOT EXISTS orgs (
276
+ id TEXT PRIMARY KEY,
277
+ name TEXT NOT NULL UNIQUE,
278
+ description TEXT,
279
+ metadata TEXT DEFAULT '{}',
280
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
281
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
282
+ );
283
+ ALTER TABLE agents ADD COLUMN org_id TEXT REFERENCES orgs(id) ON DELETE SET NULL;
284
+ ALTER TABLE projects ADD COLUMN org_id TEXT REFERENCES orgs(id) ON DELETE SET NULL;
285
+ INSERT OR IGNORE INTO _migrations (id) VALUES (12);
271
286
  `
272
287
  ];
273
288
  var _db = null;
@@ -324,6 +339,13 @@ function ensureSchema(db) {
324
339
  db.exec(sql);
325
340
  } catch {}
326
341
  };
342
+ ensureTable("orgs", `
343
+ CREATE TABLE orgs (
344
+ id TEXT PRIMARY KEY, name TEXT NOT NULL UNIQUE, description TEXT,
345
+ metadata TEXT DEFAULT '{}',
346
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
347
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
348
+ )`);
327
349
  ensureTable("agents", `
328
350
  CREATE TABLE agents (
329
351
  id TEXT PRIMARY KEY, name TEXT NOT NULL UNIQUE, description TEXT,
@@ -390,6 +412,10 @@ function ensureSchema(db) {
390
412
  ensureColumn("agents", "role", "TEXT DEFAULT 'agent'");
391
413
  ensureColumn("agents", "permissions", `TEXT DEFAULT '["*"]'`);
392
414
  ensureColumn("agents", "reports_to", "TEXT");
415
+ ensureColumn("agents", "title", "TEXT");
416
+ ensureColumn("agents", "level", "TEXT");
417
+ ensureColumn("agents", "org_id", "TEXT");
418
+ ensureColumn("projects", "org_id", "TEXT");
393
419
  ensureColumn("plans", "task_list_id", "TEXT");
394
420
  ensureColumn("plans", "agent_id", "TEXT");
395
421
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_plan ON tasks(plan_id)");
@@ -1393,14 +1419,17 @@ function registerAgent(input, db) {
1393
1419
  }
1394
1420
  const id = shortUuid();
1395
1421
  const timestamp = now();
1396
- d.run(`INSERT INTO agents (id, name, description, role, permissions, reports_to, metadata, created_at, last_seen_at)
1397
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
1422
+ d.run(`INSERT INTO agents (id, name, description, role, title, level, permissions, reports_to, org_id, metadata, created_at, last_seen_at)
1423
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
1398
1424
  id,
1399
1425
  input.name,
1400
1426
  input.description || null,
1401
1427
  input.role || "agent",
1428
+ input.title || null,
1429
+ input.level || null,
1402
1430
  JSON.stringify(input.permissions || ["*"]),
1403
1431
  input.reports_to || null,
1432
+ input.org_id || null,
1404
1433
  JSON.stringify(input.metadata || {}),
1405
1434
  timestamp,
1406
1435
  timestamp
@@ -1448,10 +1477,22 @@ function updateAgent(id, input, db) {
1448
1477
  sets.push("permissions = ?");
1449
1478
  params.push(JSON.stringify(input.permissions));
1450
1479
  }
1480
+ if (input.title !== undefined) {
1481
+ sets.push("title = ?");
1482
+ params.push(input.title);
1483
+ }
1484
+ if (input.level !== undefined) {
1485
+ sets.push("level = ?");
1486
+ params.push(input.level);
1487
+ }
1451
1488
  if (input.reports_to !== undefined) {
1452
1489
  sets.push("reports_to = ?");
1453
1490
  params.push(input.reports_to);
1454
1491
  }
1492
+ if (input.org_id !== undefined) {
1493
+ sets.push("org_id = ?");
1494
+ params.push(input.org_id);
1495
+ }
1455
1496
  if (input.metadata !== undefined) {
1456
1497
  sets.push("metadata = ?");
1457
1498
  params.push(JSON.stringify(input.metadata));
@@ -1699,6 +1740,58 @@ function taskFromTemplate(templateId, overrides = {}, db) {
1699
1740
  ...overrides
1700
1741
  };
1701
1742
  }
1743
+ // src/db/orgs.ts
1744
+ function rowToOrg(row) {
1745
+ return { ...row, metadata: JSON.parse(row.metadata || "{}") };
1746
+ }
1747
+ function createOrg(input, db) {
1748
+ const d = db || getDatabase();
1749
+ const id = uuid();
1750
+ const timestamp = now();
1751
+ d.run(`INSERT INTO orgs (id, name, description, metadata, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)`, [id, input.name, input.description || null, JSON.stringify(input.metadata || {}), timestamp, timestamp]);
1752
+ return getOrg(id, d);
1753
+ }
1754
+ function getOrg(id, db) {
1755
+ const d = db || getDatabase();
1756
+ const row = d.query("SELECT * FROM orgs WHERE id = ?").get(id);
1757
+ return row ? rowToOrg(row) : null;
1758
+ }
1759
+ function getOrgByName(name, db) {
1760
+ const d = db || getDatabase();
1761
+ const row = d.query("SELECT * FROM orgs WHERE name = ?").get(name);
1762
+ return row ? rowToOrg(row) : null;
1763
+ }
1764
+ function listOrgs(db) {
1765
+ const d = db || getDatabase();
1766
+ return d.query("SELECT * FROM orgs ORDER BY name").all().map(rowToOrg);
1767
+ }
1768
+ function updateOrg(id, input, db) {
1769
+ const d = db || getDatabase();
1770
+ const org = getOrg(id, d);
1771
+ if (!org)
1772
+ throw new Error(`Org not found: ${id}`);
1773
+ const sets = ["updated_at = ?"];
1774
+ const params = [now()];
1775
+ if (input.name !== undefined) {
1776
+ sets.push("name = ?");
1777
+ params.push(input.name);
1778
+ }
1779
+ if (input.description !== undefined) {
1780
+ sets.push("description = ?");
1781
+ params.push(input.description);
1782
+ }
1783
+ if (input.metadata !== undefined) {
1784
+ sets.push("metadata = ?");
1785
+ params.push(JSON.stringify(input.metadata));
1786
+ }
1787
+ params.push(id);
1788
+ d.run(`UPDATE orgs SET ${sets.join(", ")} WHERE id = ?`, params);
1789
+ return getOrg(id, d);
1790
+ }
1791
+ function deleteOrg(id, db) {
1792
+ const d = db || getDatabase();
1793
+ return d.run("DELETE FROM orgs WHERE id = ?", [id]).changes > 0;
1794
+ }
1702
1795
  // src/lib/search.ts
1703
1796
  function rowToTask2(row) {
1704
1797
  return {
@@ -2252,6 +2345,7 @@ export {
2252
2345
  updateSessionActivity,
2253
2346
  updateProject,
2254
2347
  updatePlan,
2348
+ updateOrg,
2255
2349
  updateAgentActivity,
2256
2350
  updateAgent,
2257
2351
  unlockTask,
@@ -2277,6 +2371,7 @@ export {
2277
2371
  listSessions,
2278
2372
  listProjects,
2279
2373
  listPlans,
2374
+ listOrgs,
2280
2375
  listComments,
2281
2376
  listAgents,
2282
2377
  getWebhook,
@@ -2294,6 +2389,8 @@ export {
2294
2389
  getProject,
2295
2390
  getPlan,
2296
2391
  getOrgChart,
2392
+ getOrgByName,
2393
+ getOrg,
2297
2394
  getDirectReports,
2298
2395
  getDatabase,
2299
2396
  getCompletionGuardConfig,
@@ -2311,6 +2408,7 @@ export {
2311
2408
  deleteSession,
2312
2409
  deleteProject,
2313
2410
  deletePlan,
2411
+ deleteOrg,
2314
2412
  deleteComment,
2315
2413
  deleteAgent,
2316
2414
  defaultSyncAgents,
@@ -2321,6 +2419,7 @@ export {
2321
2419
  createSession,
2322
2420
  createProject,
2323
2421
  createPlan,
2422
+ createOrg,
2324
2423
  completeTask,
2325
2424
  closeDatabase,
2326
2425
  checkCompletionGuard,
package/dist/mcp/index.js CHANGED
@@ -122,6 +122,13 @@ function ensureSchema(db) {
122
122
  db.exec(sql);
123
123
  } catch {}
124
124
  };
125
+ ensureTable("orgs", `
126
+ CREATE TABLE orgs (
127
+ id TEXT PRIMARY KEY, name TEXT NOT NULL UNIQUE, description TEXT,
128
+ metadata TEXT DEFAULT '{}',
129
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
130
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
131
+ )`);
125
132
  ensureTable("agents", `
126
133
  CREATE TABLE agents (
127
134
  id TEXT PRIMARY KEY, name TEXT NOT NULL UNIQUE, description TEXT,
@@ -188,6 +195,10 @@ function ensureSchema(db) {
188
195
  ensureColumn("agents", "role", "TEXT DEFAULT 'agent'");
189
196
  ensureColumn("agents", "permissions", `TEXT DEFAULT '["*"]'`);
190
197
  ensureColumn("agents", "reports_to", "TEXT");
198
+ ensureColumn("agents", "title", "TEXT");
199
+ ensureColumn("agents", "level", "TEXT");
200
+ ensureColumn("agents", "org_id", "TEXT");
201
+ ensureColumn("projects", "org_id", "TEXT");
191
202
  ensureColumn("plans", "task_list_id", "TEXT");
192
203
  ensureColumn("plans", "agent_id", "TEXT");
193
204
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_plan ON tasks(plan_id)");
@@ -482,7 +493,22 @@ var init_database = __esm(() => {
482
493
  `,
483
494
  `
484
495
  ALTER TABLE agents ADD COLUMN reports_to TEXT;
496
+ ALTER TABLE agents ADD COLUMN title TEXT;
497
+ ALTER TABLE agents ADD COLUMN level TEXT;
485
498
  INSERT OR IGNORE INTO _migrations (id) VALUES (11);
499
+ `,
500
+ `
501
+ CREATE TABLE IF NOT EXISTS orgs (
502
+ id TEXT PRIMARY KEY,
503
+ name TEXT NOT NULL UNIQUE,
504
+ description TEXT,
505
+ metadata TEXT DEFAULT '{}',
506
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
507
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
508
+ );
509
+ ALTER TABLE agents ADD COLUMN org_id TEXT REFERENCES orgs(id) ON DELETE SET NULL;
510
+ ALTER TABLE projects ADD COLUMN org_id TEXT REFERENCES orgs(id) ON DELETE SET NULL;
511
+ INSERT OR IGNORE INTO _migrations (id) VALUES (12);
486
512
  `
487
513
  ];
488
514
  });
@@ -546,14 +572,17 @@ function registerAgent(input, db) {
546
572
  }
547
573
  const id = shortUuid();
548
574
  const timestamp = now();
549
- d.run(`INSERT INTO agents (id, name, description, role, permissions, reports_to, metadata, created_at, last_seen_at)
550
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
575
+ d.run(`INSERT INTO agents (id, name, description, role, title, level, permissions, reports_to, org_id, metadata, created_at, last_seen_at)
576
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
551
577
  id,
552
578
  input.name,
553
579
  input.description || null,
554
580
  input.role || "agent",
581
+ input.title || null,
582
+ input.level || null,
555
583
  JSON.stringify(input.permissions || ["*"]),
556
584
  input.reports_to || null,
585
+ input.org_id || null,
557
586
  JSON.stringify(input.metadata || {}),
558
587
  timestamp,
559
588
  timestamp
@@ -601,10 +630,22 @@ function updateAgent(id, input, db) {
601
630
  sets.push("permissions = ?");
602
631
  params.push(JSON.stringify(input.permissions));
603
632
  }
633
+ if (input.title !== undefined) {
634
+ sets.push("title = ?");
635
+ params.push(input.title);
636
+ }
637
+ if (input.level !== undefined) {
638
+ sets.push("level = ?");
639
+ params.push(input.level);
640
+ }
604
641
  if (input.reports_to !== undefined) {
605
642
  sets.push("reports_to = ?");
606
643
  params.push(input.reports_to);
607
644
  }
645
+ if (input.org_id !== undefined) {
646
+ sets.push("org_id = ?");
647
+ params.push(input.org_id);
648
+ }
608
649
  if (input.metadata !== undefined) {
609
650
  sets.push("metadata = ?");
610
651
  params.push(JSON.stringify(input.metadata));
@@ -7061,8 +7102,9 @@ server.tool("get_org_chart", "Get agent org chart \u2014 who reports to who.", {
7061
7102
  let render = function(nodes, indent = 0) {
7062
7103
  return nodes.map((n) => {
7063
7104
  const prefix = " ".repeat(indent);
7064
- const role = n.agent.role ? ` (${n.agent.role})` : "";
7065
- const line = `${prefix}${n.agent.name}${role} [${n.agent.id}]`;
7105
+ const title = n.agent.title ? ` \u2014 ${n.agent.title}` : "";
7106
+ const level = n.agent.level ? ` (${n.agent.level})` : "";
7107
+ const line = `${prefix}${n.agent.name}${title}${level}`;
7066
7108
  const children = n.reports.length > 0 ? `
7067
7109
  ` + render(n.reports, indent + 1) : "";
7068
7110
  return line + children;
@@ -184,6 +184,13 @@ function ensureSchema(db) {
184
184
  db.exec(sql);
185
185
  } catch {}
186
186
  };
187
+ ensureTable("orgs", `
188
+ CREATE TABLE orgs (
189
+ id TEXT PRIMARY KEY, name TEXT NOT NULL UNIQUE, description TEXT,
190
+ metadata TEXT DEFAULT '{}',
191
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
192
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
193
+ )`);
187
194
  ensureTable("agents", `
188
195
  CREATE TABLE agents (
189
196
  id TEXT PRIMARY KEY, name TEXT NOT NULL UNIQUE, description TEXT,
@@ -250,6 +257,10 @@ function ensureSchema(db) {
250
257
  ensureColumn("agents", "role", "TEXT DEFAULT 'agent'");
251
258
  ensureColumn("agents", "permissions", `TEXT DEFAULT '["*"]'`);
252
259
  ensureColumn("agents", "reports_to", "TEXT");
260
+ ensureColumn("agents", "title", "TEXT");
261
+ ensureColumn("agents", "level", "TEXT");
262
+ ensureColumn("agents", "org_id", "TEXT");
263
+ ensureColumn("projects", "org_id", "TEXT");
253
264
  ensureColumn("plans", "task_list_id", "TEXT");
254
265
  ensureColumn("plans", "agent_id", "TEXT");
255
266
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_plan ON tasks(plan_id)");
@@ -530,7 +541,22 @@ var init_database = __esm(() => {
530
541
  `,
531
542
  `
532
543
  ALTER TABLE agents ADD COLUMN reports_to TEXT;
544
+ ALTER TABLE agents ADD COLUMN title TEXT;
545
+ ALTER TABLE agents ADD COLUMN level TEXT;
533
546
  INSERT OR IGNORE INTO _migrations (id) VALUES (11);
547
+ `,
548
+ `
549
+ CREATE TABLE IF NOT EXISTS orgs (
550
+ id TEXT PRIMARY KEY,
551
+ name TEXT NOT NULL UNIQUE,
552
+ description TEXT,
553
+ metadata TEXT DEFAULT '{}',
554
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
555
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
556
+ );
557
+ ALTER TABLE agents ADD COLUMN org_id TEXT REFERENCES orgs(id) ON DELETE SET NULL;
558
+ ALTER TABLE projects ADD COLUMN org_id TEXT REFERENCES orgs(id) ON DELETE SET NULL;
559
+ INSERT OR IGNORE INTO _migrations (id) VALUES (12);
534
560
  `
535
561
  ];
536
562
  });
@@ -710,14 +736,17 @@ function registerAgent(input, db) {
710
736
  }
711
737
  const id = shortUuid();
712
738
  const timestamp = now();
713
- d.run(`INSERT INTO agents (id, name, description, role, permissions, reports_to, metadata, created_at, last_seen_at)
714
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
739
+ d.run(`INSERT INTO agents (id, name, description, role, title, level, permissions, reports_to, org_id, metadata, created_at, last_seen_at)
740
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
715
741
  id,
716
742
  input.name,
717
743
  input.description || null,
718
744
  input.role || "agent",
745
+ input.title || null,
746
+ input.level || null,
719
747
  JSON.stringify(input.permissions || ["*"]),
720
748
  input.reports_to || null,
749
+ input.org_id || null,
721
750
  JSON.stringify(input.metadata || {}),
722
751
  timestamp,
723
752
  timestamp
@@ -765,10 +794,22 @@ function updateAgent(id, input, db) {
765
794
  sets.push("permissions = ?");
766
795
  params.push(JSON.stringify(input.permissions));
767
796
  }
797
+ if (input.title !== undefined) {
798
+ sets.push("title = ?");
799
+ params.push(input.title);
800
+ }
801
+ if (input.level !== undefined) {
802
+ sets.push("level = ?");
803
+ params.push(input.level);
804
+ }
768
805
  if (input.reports_to !== undefined) {
769
806
  sets.push("reports_to = ?");
770
807
  params.push(input.reports_to);
771
808
  }
809
+ if (input.org_id !== undefined) {
810
+ sets.push("org_id = ?");
811
+ params.push(input.org_id);
812
+ }
772
813
  if (input.metadata !== undefined) {
773
814
  sets.push("metadata = ?");
774
815
  params.push(JSON.stringify(input.metadata));
@@ -804,6 +845,71 @@ var init_agents = __esm(() => {
804
845
  init_database();
805
846
  });
806
847
 
848
+ // src/db/orgs.ts
849
+ var exports_orgs = {};
850
+ __export(exports_orgs, {
851
+ updateOrg: () => updateOrg,
852
+ listOrgs: () => listOrgs,
853
+ getOrgByName: () => getOrgByName,
854
+ getOrg: () => getOrg,
855
+ deleteOrg: () => deleteOrg,
856
+ createOrg: () => createOrg
857
+ });
858
+ function rowToOrg(row) {
859
+ return { ...row, metadata: JSON.parse(row.metadata || "{}") };
860
+ }
861
+ function createOrg(input, db) {
862
+ const d = db || getDatabase();
863
+ const id = uuid();
864
+ const timestamp = now();
865
+ d.run(`INSERT INTO orgs (id, name, description, metadata, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)`, [id, input.name, input.description || null, JSON.stringify(input.metadata || {}), timestamp, timestamp]);
866
+ return getOrg(id, d);
867
+ }
868
+ function getOrg(id, db) {
869
+ const d = db || getDatabase();
870
+ const row = d.query("SELECT * FROM orgs WHERE id = ?").get(id);
871
+ return row ? rowToOrg(row) : null;
872
+ }
873
+ function getOrgByName(name, db) {
874
+ const d = db || getDatabase();
875
+ const row = d.query("SELECT * FROM orgs WHERE name = ?").get(name);
876
+ return row ? rowToOrg(row) : null;
877
+ }
878
+ function listOrgs(db) {
879
+ const d = db || getDatabase();
880
+ return d.query("SELECT * FROM orgs ORDER BY name").all().map(rowToOrg);
881
+ }
882
+ function updateOrg(id, input, db) {
883
+ const d = db || getDatabase();
884
+ const org = getOrg(id, d);
885
+ if (!org)
886
+ throw new Error(`Org not found: ${id}`);
887
+ const sets = ["updated_at = ?"];
888
+ const params = [now()];
889
+ if (input.name !== undefined) {
890
+ sets.push("name = ?");
891
+ params.push(input.name);
892
+ }
893
+ if (input.description !== undefined) {
894
+ sets.push("description = ?");
895
+ params.push(input.description);
896
+ }
897
+ if (input.metadata !== undefined) {
898
+ sets.push("metadata = ?");
899
+ params.push(JSON.stringify(input.metadata));
900
+ }
901
+ params.push(id);
902
+ d.run(`UPDATE orgs SET ${sets.join(", ")} WHERE id = ?`, params);
903
+ return getOrg(id, d);
904
+ }
905
+ function deleteOrg(id, db) {
906
+ const d = db || getDatabase();
907
+ return d.run("DELETE FROM orgs WHERE id = ?", [id]).changes > 0;
908
+ }
909
+ var init_orgs = __esm(() => {
910
+ init_database();
911
+ });
912
+
807
913
  // src/db/webhooks.ts
808
914
  var exports_webhooks = {};
809
915
  __export(exports_webhooks, {
@@ -1782,6 +1888,36 @@ Dashboard not found at: ${dashboardDir}`);
1782
1888
  return json({ error: e instanceof Error ? e.message : "Failed to claim" }, 500, port);
1783
1889
  }
1784
1890
  }
1891
+ if (path === "/api/orgs" && method === "GET") {
1892
+ const { listOrgs: listOrgs2 } = await Promise.resolve().then(() => (init_orgs(), exports_orgs));
1893
+ return json(listOrgs2(), 200, port);
1894
+ }
1895
+ if (path === "/api/orgs" && method === "POST") {
1896
+ try {
1897
+ const body = await req.json();
1898
+ if (!body.name)
1899
+ return json({ error: "Missing name" }, 400, port);
1900
+ const { createOrg: createOrg2 } = await Promise.resolve().then(() => (init_orgs(), exports_orgs));
1901
+ return json(createOrg2(body), 201, port);
1902
+ } catch (e) {
1903
+ return json({ error: e instanceof Error ? e.message : "Failed" }, 500, port);
1904
+ }
1905
+ }
1906
+ const orgMatch = path.match(/^\/api\/orgs\/([^/]+)$/);
1907
+ if (orgMatch && method === "PATCH") {
1908
+ try {
1909
+ const body = await req.json();
1910
+ const { updateOrg: updateOrg2 } = await Promise.resolve().then(() => (init_orgs(), exports_orgs));
1911
+ return json(updateOrg2(orgMatch[1], body), 200, port);
1912
+ } catch (e) {
1913
+ return json({ error: e instanceof Error ? e.message : "Failed" }, 500, port);
1914
+ }
1915
+ }
1916
+ if (orgMatch && method === "DELETE") {
1917
+ const { deleteOrg: deleteOrg2 } = await Promise.resolve().then(() => (init_orgs(), exports_orgs));
1918
+ const deleted = deleteOrg2(orgMatch[1]);
1919
+ return json(deleted ? { success: true } : { error: "Not found" }, deleted ? 200 : 404, port);
1920
+ }
1785
1921
  if (path === "/api/org" && method === "GET") {
1786
1922
  const { getOrgChart: getOrgChart2 } = await Promise.resolve().then(() => (init_agents(), exports_agents));
1787
1923
  return json(getOrgChart2(), 200, port);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/todos",
3
- "version": "0.9.30",
3
+ "version": "0.9.32",
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",