@hasna/todos 0.10.11 → 0.10.14

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
@@ -2897,6 +2897,20 @@ var init_database = __esm(() => {
2897
2897
  ALTER TABLE agents ADD COLUMN status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active', 'archived'));
2898
2898
  CREATE INDEX IF NOT EXISTS idx_agents_status ON agents(status);
2899
2899
  INSERT OR IGNORE INTO _migrations (id) VALUES (30);
2900
+ `,
2901
+ `
2902
+ CREATE TABLE IF NOT EXISTS project_agent_roles (
2903
+ id TEXT PRIMARY KEY,
2904
+ project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
2905
+ agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
2906
+ role TEXT NOT NULL,
2907
+ is_lead INTEGER NOT NULL DEFAULT 0,
2908
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
2909
+ UNIQUE(project_id, agent_id, role)
2910
+ );
2911
+ CREATE INDEX IF NOT EXISTS idx_project_agent_roles_project ON project_agent_roles(project_id);
2912
+ CREATE INDEX IF NOT EXISTS idx_project_agent_roles_agent ON project_agent_roles(agent_id);
2913
+ INSERT OR IGNORE INTO _migrations (id) VALUES (31);
2900
2914
  `,
2901
2915
  `
2902
2916
  CREATE TABLE IF NOT EXISTS file_locks (
@@ -2910,7 +2924,7 @@ var init_database = __esm(() => {
2910
2924
  CREATE INDEX IF NOT EXISTS idx_file_locks_path ON file_locks(path);
2911
2925
  CREATE INDEX IF NOT EXISTS idx_file_locks_agent ON file_locks(agent_id);
2912
2926
  CREATE INDEX IF NOT EXISTS idx_file_locks_expires ON file_locks(expires_at);
2913
- INSERT OR IGNORE INTO _migrations (id) VALUES (31);
2927
+ INSERT OR IGNORE INTO _migrations (id) VALUES (32);
2914
2928
  `
2915
2929
  ];
2916
2930
  });
@@ -9803,6 +9817,161 @@ var init_zod = __esm(() => {
9803
9817
  init_external();
9804
9818
  });
9805
9819
 
9820
+ // src/lib/auto-assign.ts
9821
+ var exports_auto_assign = {};
9822
+ __export(exports_auto_assign, {
9823
+ findBestAgent: () => findBestAgent,
9824
+ autoAssignTask: () => autoAssignTask
9825
+ });
9826
+ function findBestAgent(_task, db) {
9827
+ const d = db || getDatabase();
9828
+ const agents = listAgents(d).filter((a) => (a.role || "agent") === "agent");
9829
+ if (agents.length === 0)
9830
+ return null;
9831
+ const inProgressTasks = listTasks({ status: "in_progress" }, d);
9832
+ const load = new Map;
9833
+ for (const a of agents)
9834
+ load.set(a.name, 0);
9835
+ for (const t of inProgressTasks) {
9836
+ const name = t.assigned_to || t.agent_id;
9837
+ if (name && load.has(name)) {
9838
+ load.set(name, (load.get(name) || 0) + 1);
9839
+ }
9840
+ }
9841
+ let bestAgent = agents[0].name;
9842
+ let bestLoad = load.get(bestAgent) ?? 0;
9843
+ for (const a of agents) {
9844
+ const l = load.get(a.name) ?? 0;
9845
+ if (l < bestLoad) {
9846
+ bestAgent = a.name;
9847
+ bestLoad = l;
9848
+ }
9849
+ }
9850
+ return bestAgent;
9851
+ }
9852
+ function getAgentWorkloads(d) {
9853
+ const rows = d.query("SELECT assigned_to, COUNT(*) as count FROM tasks WHERE status = 'in_progress' AND assigned_to IS NOT NULL GROUP BY assigned_to").all();
9854
+ return new Map(rows.map((r) => [r.assigned_to, r.count]));
9855
+ }
9856
+ function buildPrompt(task, agents) {
9857
+ const agentList = agents.map((a) => `- ${a.name} (role: ${a.role}, caps: [${a.capabilities.join(", ")}], active_tasks: ${a.in_progress_tasks})`).join(`
9858
+ `);
9859
+ return `You are a task routing assistant. Given a task and available agents, choose the SINGLE best agent.
9860
+
9861
+ TASK:
9862
+ Title: ${task.title}
9863
+ Priority: ${task.priority}
9864
+ Tags: ${task.tags.join(", ") || "none"}
9865
+ Description: ${task.description?.slice(0, 300) || "none"}
9866
+
9867
+ AVAILABLE AGENTS:
9868
+ ${agentList}
9869
+
9870
+ Rules:
9871
+ - Match task tags/content to agent capabilities
9872
+ - Prefer agents with fewer active tasks
9873
+ - Prefer agents whose role fits the task (lead for critical, developer for features, qa for testing)
9874
+ - If no clear match, pick the agent with fewest active tasks
9875
+
9876
+ Respond with ONLY a JSON object: {"agent_name": "<name>", "reason": "<one sentence>"}`;
9877
+ }
9878
+ async function callCerebras(prompt, apiKey) {
9879
+ try {
9880
+ const resp = await fetch(CEREBRAS_API_URL, {
9881
+ method: "POST",
9882
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` },
9883
+ body: JSON.stringify({
9884
+ model: CEREBRAS_MODEL,
9885
+ messages: [{ role: "user", content: prompt }],
9886
+ max_tokens: 150,
9887
+ temperature: 0
9888
+ }),
9889
+ signal: AbortSignal.timeout(1e4)
9890
+ });
9891
+ if (!resp.ok)
9892
+ return null;
9893
+ const data = await resp.json();
9894
+ const content = data?.choices?.[0]?.message?.content?.trim();
9895
+ if (!content)
9896
+ return null;
9897
+ const match = content.match(/\{[^}]+\}/s);
9898
+ if (!match)
9899
+ return null;
9900
+ return JSON.parse(match[0]);
9901
+ } catch {
9902
+ return null;
9903
+ }
9904
+ }
9905
+ async function autoAssignTask(taskId, db) {
9906
+ const d = db || getDatabase();
9907
+ const task = getTask(taskId, d);
9908
+ if (!task)
9909
+ throw new Error(`Task ${taskId} not found`);
9910
+ const agents = listAgents(d).filter((a) => a.status === "active");
9911
+ if (agents.length === 0) {
9912
+ return { task_id: taskId, assigned_to: null, agent_name: null, method: "no_agents" };
9913
+ }
9914
+ const workloads = getAgentWorkloads(d);
9915
+ const apiKey = process.env["CEREBRAS_API_KEY"];
9916
+ let selectedAgent = null;
9917
+ let method = "capability_match";
9918
+ let reason;
9919
+ if (apiKey) {
9920
+ const agentData = agents.map((a) => ({
9921
+ id: a.id,
9922
+ name: a.name,
9923
+ role: a.role || "agent",
9924
+ capabilities: a.capabilities || [],
9925
+ in_progress_tasks: workloads.get(a.id) ?? 0
9926
+ }));
9927
+ const result = await callCerebras(buildPrompt({
9928
+ title: task.title,
9929
+ description: task.description,
9930
+ priority: task.priority,
9931
+ tags: task.tags || []
9932
+ }, agentData), apiKey);
9933
+ if (result?.agent_name) {
9934
+ selectedAgent = agents.find((a) => a.name === result.agent_name) ?? null;
9935
+ if (selectedAgent) {
9936
+ method = "cerebras";
9937
+ reason = result.reason;
9938
+ }
9939
+ }
9940
+ }
9941
+ if (!selectedAgent) {
9942
+ const taskTags = task.tags || [];
9943
+ const capable = getCapableAgents(taskTags, { min_score: 0, limit: 10 }, d);
9944
+ if (capable.length > 0) {
9945
+ const sorted = capable.sort((a, b) => {
9946
+ if (b.score !== a.score)
9947
+ return b.score - a.score;
9948
+ return (workloads.get(a.agent.id) ?? 0) - (workloads.get(b.agent.id) ?? 0);
9949
+ });
9950
+ selectedAgent = sorted[0].agent;
9951
+ reason = `Capability match (score: ${sorted[0].score.toFixed(2)})`;
9952
+ } else {
9953
+ selectedAgent = agents.slice().sort((a, b) => (workloads.get(a.id) ?? 0) - (workloads.get(b.id) ?? 0))[0];
9954
+ reason = `Least busy agent (${workloads.get(selectedAgent.id) ?? 0} active tasks)`;
9955
+ }
9956
+ }
9957
+ if (selectedAgent) {
9958
+ updateTask(taskId, { assigned_to: selectedAgent.id, version: task.version }, d);
9959
+ }
9960
+ return {
9961
+ task_id: taskId,
9962
+ assigned_to: selectedAgent?.id ?? null,
9963
+ agent_name: selectedAgent?.name ?? null,
9964
+ method,
9965
+ reason
9966
+ };
9967
+ }
9968
+ var CEREBRAS_API_URL = "https://api.cerebras.ai/v1/chat/completions", CEREBRAS_MODEL = "llama-3.3-70b";
9969
+ var init_auto_assign = __esm(() => {
9970
+ init_database();
9971
+ init_tasks();
9972
+ init_agents();
9973
+ });
9974
+
9806
9975
  // src/db/task-files.ts
9807
9976
  var exports_task_files = {};
9808
9977
  __export(exports_task_files, {
@@ -10432,6 +10601,81 @@ var init_kg = __esm(() => {
10432
10601
  init_database();
10433
10602
  });
10434
10603
 
10604
+ // src/db/project-agent-roles.ts
10605
+ var exports_project_agent_roles = {};
10606
+ __export(exports_project_agent_roles, {
10607
+ setProjectAgentRole: () => setProjectAgentRole,
10608
+ removeProjectAgentRole: () => removeProjectAgentRole,
10609
+ listProjectAgentRoles: () => listProjectAgentRoles,
10610
+ getProjectOrgChart: () => getProjectOrgChart,
10611
+ getAgentProjectRoles: () => getAgentProjectRoles
10612
+ });
10613
+ function rowToRole(row) {
10614
+ return { ...row, is_lead: row.is_lead === 1 };
10615
+ }
10616
+ function setProjectAgentRole(projectId, agentId, role, isLead = false, db) {
10617
+ const d = db || getDatabase();
10618
+ const existing = d.query("SELECT * FROM project_agent_roles WHERE project_id = ? AND agent_id = ? AND role = ?").get(projectId, agentId, role);
10619
+ if (existing) {
10620
+ d.run("UPDATE project_agent_roles SET is_lead = ? WHERE id = ?", [isLead ? 1 : 0, existing.id]);
10621
+ return rowToRole(d.query("SELECT * FROM project_agent_roles WHERE id = ?").get(existing.id));
10622
+ }
10623
+ const id = uuid();
10624
+ d.run("INSERT INTO project_agent_roles (id, project_id, agent_id, role, is_lead, created_at) VALUES (?, ?, ?, ?, ?, ?)", [id, projectId, agentId, role, isLead ? 1 : 0, now()]);
10625
+ return rowToRole(d.query("SELECT * FROM project_agent_roles WHERE id = ?").get(id));
10626
+ }
10627
+ function removeProjectAgentRole(projectId, agentId, role, db) {
10628
+ const d = db || getDatabase();
10629
+ if (role) {
10630
+ return d.run("DELETE FROM project_agent_roles WHERE project_id = ? AND agent_id = ? AND role = ?", [projectId, agentId, role]).changes;
10631
+ }
10632
+ return d.run("DELETE FROM project_agent_roles WHERE project_id = ? AND agent_id = ?", [projectId, agentId]).changes;
10633
+ }
10634
+ function listProjectAgentRoles(projectId, db) {
10635
+ const d = db || getDatabase();
10636
+ return d.query("SELECT * FROM project_agent_roles WHERE project_id = ? ORDER BY role, created_at").all(projectId).map(rowToRole);
10637
+ }
10638
+ function getAgentProjectRoles(agentId, db) {
10639
+ const d = db || getDatabase();
10640
+ return d.query("SELECT * FROM project_agent_roles WHERE agent_id = ? ORDER BY project_id, role").all(agentId).map(rowToRole);
10641
+ }
10642
+ function getProjectOrgChart(projectId, opts, db) {
10643
+ const d = db || getDatabase();
10644
+ const globalTree = getOrgChart(d);
10645
+ const projectRoles = listProjectAgentRoles(projectId, d);
10646
+ const rolesByAgent = new Map;
10647
+ for (const pr of projectRoles) {
10648
+ if (!rolesByAgent.has(pr.agent_id))
10649
+ rolesByAgent.set(pr.agent_id, { roles: [], isLead: false });
10650
+ const entry = rolesByAgent.get(pr.agent_id);
10651
+ entry.roles.push(pr.role);
10652
+ if (pr.is_lead)
10653
+ entry.isLead = true;
10654
+ }
10655
+ function augmentTree(nodes) {
10656
+ return nodes.map((n) => {
10657
+ const override = rolesByAgent.get(n.agent.id);
10658
+ return {
10659
+ ...n,
10660
+ reports: augmentTree(n.reports),
10661
+ project_roles: override?.roles ?? [],
10662
+ is_project_lead: override?.isLead ?? false
10663
+ };
10664
+ }).filter((n) => {
10665
+ if (!opts?.filter_to_project)
10666
+ return true;
10667
+ const hasRole = n.project_roles.length > 0;
10668
+ const hasDescendant = n.reports.length > 0;
10669
+ return hasRole || hasDescendant;
10670
+ });
10671
+ }
10672
+ return augmentTree(globalTree);
10673
+ }
10674
+ var init_project_agent_roles = __esm(() => {
10675
+ init_database();
10676
+ init_agents();
10677
+ });
10678
+
10435
10679
  // src/db/patrol.ts
10436
10680
  var exports_patrol = {};
10437
10681
  __export(exports_patrol, {
@@ -12994,6 +13238,57 @@ Claimed: ${formatTask(result.claimed)}`);
12994
13238
  }
12995
13239
  });
12996
13240
  }
13241
+ if (shouldRegisterTool("auto_assign_task")) {
13242
+ server.tool("auto_assign_task", "Auto-assign a task to the best available agent. Uses Cerebras LLM (llama-3.3-70b) if CEREBRAS_API_KEY is set, otherwise falls back to capability-based matching.", {
13243
+ task_id: exports_external.string().describe("Task to auto-assign")
13244
+ }, async ({ task_id }) => {
13245
+ try {
13246
+ const { autoAssignTask: autoAssignTask2 } = await Promise.resolve().then(() => (init_auto_assign(), exports_auto_assign));
13247
+ const resolvedId = resolveId(task_id);
13248
+ const result = await autoAssignTask2(resolvedId);
13249
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
13250
+ } catch (e) {
13251
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
13252
+ }
13253
+ });
13254
+ }
13255
+ if (shouldRegisterTool("auto_assign_unassigned")) {
13256
+ server.tool("auto_assign_unassigned", "Auto-assign all unassigned pending tasks in a project using Cerebras LLM routing. Returns summary of assignments made.", {
13257
+ project_id: exports_external.string().optional().describe("Filter to a specific project"),
13258
+ limit: exports_external.number().optional().describe("Max tasks to assign (default: 20)")
13259
+ }, async ({ project_id, limit }) => {
13260
+ try {
13261
+ const { autoAssignTask: autoAssignTask2 } = await Promise.resolve().then(() => (init_auto_assign(), exports_auto_assign));
13262
+ const { listTasks: listTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
13263
+ const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
13264
+ const tasks = listTasks2({
13265
+ status: "pending",
13266
+ project_id: resolvedProjectId
13267
+ }).tasks.filter((t) => !t.assigned_to).slice(0, limit ?? 20);
13268
+ const results = [];
13269
+ for (const task of tasks) {
13270
+ try {
13271
+ const r = await autoAssignTask2(task.id);
13272
+ results.push(r);
13273
+ } catch {}
13274
+ }
13275
+ const assigned = results.filter((r) => r.assigned_to);
13276
+ return {
13277
+ content: [{
13278
+ type: "text",
13279
+ text: JSON.stringify({
13280
+ total_checked: tasks.length,
13281
+ assigned: assigned.length,
13282
+ skipped: tasks.length - assigned.length,
13283
+ results
13284
+ }, null, 2)
13285
+ }]
13286
+ };
13287
+ } catch (e) {
13288
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
13289
+ }
13290
+ });
13291
+ }
12997
13292
  if (shouldRegisterTool("search_tools")) {
12998
13293
  server.tool("search_tools", "List all tool names, optionally filtered by substring.", { query: exports_external.string().optional() }, async ({ query }) => {
12999
13294
  const all = [
@@ -13768,6 +14063,78 @@ ${lines.join(`
13768
14063
  }
13769
14064
  });
13770
14065
  }
14066
+ if (shouldRegisterTool("set_project_agent_role")) {
14067
+ server.tool("set_project_agent_role", "Assign an agent a role on a specific project (client, lead, developer, qa, reviewer, etc.). Per-project roles extend the global org chart.", {
14068
+ project_id: exports_external.string().describe("Project ID"),
14069
+ agent_name: exports_external.string().describe("Agent name"),
14070
+ role: exports_external.string().describe("Role on this project (e.g. 'lead', 'developer', 'qa')"),
14071
+ is_lead: exports_external.coerce.boolean().optional().describe("Whether this agent is the project lead for this role")
14072
+ }, async ({ project_id, agent_name, role, is_lead }) => {
14073
+ try {
14074
+ const { setProjectAgentRole: setProjectAgentRole2 } = (init_project_agent_roles(), __toCommonJS(exports_project_agent_roles));
14075
+ const agent = getAgentByName(agent_name);
14076
+ if (!agent)
14077
+ return { content: [{ type: "text", text: `Agent not found: ${agent_name}` }], isError: true };
14078
+ const pid = resolveId(project_id, "projects");
14079
+ const result = setProjectAgentRole2(pid, agent.id, role, is_lead ?? false);
14080
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
14081
+ } catch (e) {
14082
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
14083
+ }
14084
+ });
14085
+ }
14086
+ if (shouldRegisterTool("get_project_org_chart")) {
14087
+ server.tool("get_project_org_chart", "Get org chart scoped to a project \u2014 global hierarchy with per-project role overrides merged in.", {
14088
+ project_id: exports_external.string().describe("Project ID"),
14089
+ format: exports_external.enum(["text", "json"]).optional().describe("Output format (default: text)"),
14090
+ filter_to_project: exports_external.coerce.boolean().optional().describe("Only show agents with a role on this project")
14091
+ }, async ({ project_id, format, filter_to_project }) => {
14092
+ try {
14093
+ let render = function(nodes, indent = 0) {
14094
+ return nodes.map((n) => {
14095
+ const prefix = " ".repeat(indent);
14096
+ const title = n.agent.title ? ` \u2014 ${n.agent.title}` : "";
14097
+ const globalRole = n.agent.role ? ` [${n.agent.role}]` : "";
14098
+ const projectRoles = n.project_roles.length > 0 ? ` <${n.project_roles.join(", ")}>` : "";
14099
+ const lead = n.is_project_lead ? " \u2605" : "";
14100
+ const lastSeen = new Date(n.agent.last_seen_at).getTime();
14101
+ const active = now2 - lastSeen < ACTIVE_MS ? " \u25CF" : " \u25CB";
14102
+ const line = `${prefix}${active} ${n.agent.name}${title}${globalRole}${projectRoles}${lead}`;
14103
+ const children = n.reports.length > 0 ? `
14104
+ ` + render(n.reports, indent + 1) : "";
14105
+ return line + children;
14106
+ }).join(`
14107
+ `);
14108
+ };
14109
+ const { getProjectOrgChart: getProjectOrgChart2 } = (init_project_agent_roles(), __toCommonJS(exports_project_agent_roles));
14110
+ const pid = resolveId(project_id, "projects");
14111
+ const tree = getProjectOrgChart2(pid, { filter_to_project });
14112
+ if (format === "json") {
14113
+ return { content: [{ type: "text", text: JSON.stringify(tree, null, 2) }] };
14114
+ }
14115
+ const now2 = Date.now();
14116
+ const ACTIVE_MS = 30 * 60 * 1000;
14117
+ const text = tree.length > 0 ? render(tree) : "No agents in this project's org chart.";
14118
+ return { content: [{ type: "text", text }] };
14119
+ } catch (e) {
14120
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
14121
+ }
14122
+ });
14123
+ }
14124
+ if (shouldRegisterTool("list_project_agent_roles")) {
14125
+ server.tool("list_project_agent_roles", "List all agent role assignments for a project.", {
14126
+ project_id: exports_external.string().describe("Project ID")
14127
+ }, async ({ project_id }) => {
14128
+ try {
14129
+ const { listProjectAgentRoles: listProjectAgentRoles2 } = (init_project_agent_roles(), __toCommonJS(exports_project_agent_roles));
14130
+ const pid = resolveId(project_id, "projects");
14131
+ const roles = listProjectAgentRoles2(pid);
14132
+ return { content: [{ type: "text", text: JSON.stringify(roles, null, 2) }] };
14133
+ } catch (e) {
14134
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
14135
+ }
14136
+ });
14137
+ }
13771
14138
  if (shouldRegisterTool("get_capable_agents")) {
13772
14139
  server.tool("get_capable_agents", "Find agents that match given capabilities, sorted by match score.", {
13773
14140
  capabilities: exports_external.array(exports_external.string()).describe("Required capabilities to match against"),
@@ -1 +1 @@
1
- {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/db/database.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAItC,eAAO,MAAM,mBAAmB,KAAK,CAAC;AA6hBtC,wBAAgB,WAAW,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,CAkBrD;AAyRD,wBAAgB,aAAa,IAAI,IAAI,CAKpC;AAED,wBAAgB,aAAa,IAAI,IAAI,CAEpC;AAED,wBAAgB,GAAG,IAAI,MAAM,CAE5B;AAED,wBAAgB,IAAI,IAAI,MAAM,CAE7B;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAK9D;AAED,wBAAgB,gBAAgB,CAAC,KAAK,SAAa,GAAG,MAAM,CAG3D;AAED,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,QAAQ,GAAG,IAAI,CAGpD;AAED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CA0B9F"}
1
+ {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/db/database.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAItC,eAAO,MAAM,mBAAmB,KAAK,CAAC;AA4iBtC,wBAAgB,WAAW,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,CAkBrD;AAyRD,wBAAgB,aAAa,IAAI,IAAI,CAKpC;AAED,wBAAgB,aAAa,IAAI,IAAI,CAEpC;AAED,wBAAgB,GAAG,IAAI,MAAM,CAE5B;AAED,wBAAgB,IAAI,IAAI,MAAM,CAE7B;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAK9D;AAED,wBAAgB,gBAAgB,CAAC,KAAK,SAAa,GAAG,MAAM,CAG3D;AAED,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,QAAQ,GAAG,IAAI,CAGpD;AAED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CA0B9F"}
@@ -0,0 +1,34 @@
1
+ import type { Database } from "bun:sqlite";
2
+ import type { OrgNode } from "./agents.js";
3
+ export interface ProjectAgentRole {
4
+ id: string;
5
+ project_id: string;
6
+ agent_id: string;
7
+ role: string;
8
+ is_lead: boolean;
9
+ created_at: string;
10
+ }
11
+ export interface ProjectAgentRoleRow {
12
+ id: string;
13
+ project_id: string;
14
+ agent_id: string;
15
+ role: string;
16
+ is_lead: number;
17
+ created_at: string;
18
+ }
19
+ export declare function setProjectAgentRole(projectId: string, agentId: string, role: string, isLead?: boolean, db?: Database): ProjectAgentRole;
20
+ export declare function removeProjectAgentRole(projectId: string, agentId: string, role?: string, db?: Database): number;
21
+ export declare function listProjectAgentRoles(projectId: string, db?: Database): ProjectAgentRole[];
22
+ export declare function getAgentProjectRoles(agentId: string, db?: Database): ProjectAgentRole[];
23
+ export interface ProjectOrgNode extends OrgNode {
24
+ project_roles: string[];
25
+ is_project_lead: boolean;
26
+ }
27
+ /**
28
+ * Get org chart scoped to a project. Returns global org chart with per-project
29
+ * role overrides merged in. Agents not in the project are excluded when filter=true.
30
+ */
31
+ export declare function getProjectOrgChart(projectId: string, opts?: {
32
+ filter_to_project?: boolean;
33
+ }, db?: Database): ProjectOrgNode[];
34
+ //# sourceMappingURL=project-agent-roles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-agent-roles.d.ts","sourceRoot":"","sources":["../../src/db/project-agent-roles.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAG3C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB;AAMD,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,MAAM,UAAQ,EACd,EAAE,CAAC,EAAE,QAAQ,GACZ,gBAAgB,CAoBlB;AAED,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE,MAAM,EACb,EAAE,CAAC,EAAE,QAAQ,GACZ,MAAM,CAYR;AAED,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,gBAAgB,EAAE,CAK1F;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,gBAAgB,EAAE,CAKvF;AAED,MAAM,WAAW,cAAe,SAAQ,OAAO;IAC7C,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,MAAM,EACjB,IAAI,CAAC,EAAE;IAAE,iBAAiB,CAAC,EAAE,OAAO,CAAA;CAAE,EACtC,EAAE,CAAC,EAAE,QAAQ,GACZ,cAAc,EAAE,CAkClB"}
package/dist/index.js CHANGED
@@ -704,6 +704,20 @@ var MIGRATIONS = [
704
704
  INSERT OR IGNORE INTO _migrations (id) VALUES (30);
705
705
  `,
706
706
  `
707
+ CREATE TABLE IF NOT EXISTS project_agent_roles (
708
+ id TEXT PRIMARY KEY,
709
+ project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
710
+ agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
711
+ role TEXT NOT NULL,
712
+ is_lead INTEGER NOT NULL DEFAULT 0,
713
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
714
+ UNIQUE(project_id, agent_id, role)
715
+ );
716
+ CREATE INDEX IF NOT EXISTS idx_project_agent_roles_project ON project_agent_roles(project_id);
717
+ CREATE INDEX IF NOT EXISTS idx_project_agent_roles_agent ON project_agent_roles(agent_id);
718
+ INSERT OR IGNORE INTO _migrations (id) VALUES (31);
719
+ `,
720
+ `
707
721
  CREATE TABLE IF NOT EXISTS file_locks (
708
722
  id TEXT PRIMARY KEY,
709
723
  path TEXT NOT NULL UNIQUE,
@@ -715,7 +729,7 @@ var MIGRATIONS = [
715
729
  CREATE INDEX IF NOT EXISTS idx_file_locks_path ON file_locks(path);
716
730
  CREATE INDEX IF NOT EXISTS idx_file_locks_agent ON file_locks(agent_id);
717
731
  CREATE INDEX IF NOT EXISTS idx_file_locks_expires ON file_locks(expires_at);
718
- INSERT OR IGNORE INTO _migrations (id) VALUES (31);
732
+ INSERT OR IGNORE INTO _migrations (id) VALUES (32);
719
733
  `
720
734
  ];
721
735
  var _db = null;
@@ -1,9 +1,25 @@
1
+ /**
2
+ * Auto-assign tasks to agents using Cerebras LLM for intelligent routing.
3
+ * Falls back to capability-based matching when the API key is unavailable or the call fails.
4
+ */
1
5
  import type { Database } from "bun:sqlite";
2
6
  import type { Task } from "../types/index.js";
7
+ export interface AutoAssignResult {
8
+ task_id: string;
9
+ assigned_to: string | null;
10
+ agent_name: string | null;
11
+ method: "cerebras" | "capability_match" | "no_agents";
12
+ reason?: string;
13
+ }
3
14
  /**
4
- * Find the best agent to assign a task to.
15
+ * Find the best agent to assign a task to (legacy simple version, no I/O).
5
16
  * Strategy: least-loaded agent with role "agent" (not admin/observer).
6
- * Returns agent name or null if no agents available.
7
17
  */
8
18
  export declare function findBestAgent(_task: Task, db?: Database): string | null;
19
+ /**
20
+ * Auto-assign a task to the best available agent.
21
+ * Uses Cerebras LLM (llama-3.3-70b) if CEREBRAS_API_KEY is set,
22
+ * otherwise falls back to capability-based matching.
23
+ */
24
+ export declare function autoAssignTask(taskId: string, db?: Database): Promise<AutoAssignResult>;
9
25
  //# sourceMappingURL=auto-assign.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"auto-assign.d.ts","sourceRoot":"","sources":["../../src/lib/auto-assign.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAI3C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAE9C;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,IAAI,CA6BvE"}
1
+ {"version":3,"file":"auto-assign.d.ts","sourceRoot":"","sources":["../../src/lib/auto-assign.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAI3C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAK9C,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,MAAM,EAAE,UAAU,GAAG,kBAAkB,GAAG,WAAW,CAAC;IACtD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,IAAI,CAsBvE;AA8DD;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA8D7F"}