@hasna/todos 0.11.33 → 0.11.34
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 +1712 -456
- package/dist/db/comments.d.ts +3 -0
- package/dist/db/comments.d.ts.map +1 -1
- package/dist/db/migrations.d.ts.map +1 -1
- package/dist/db/task-crud.d.ts.map +1 -1
- package/dist/db/task-lifecycle.d.ts +1 -0
- package/dist/db/task-lifecycle.d.ts.map +1 -1
- package/dist/db/task-relations.d.ts +24 -0
- package/dist/db/task-relations.d.ts.map +1 -1
- package/dist/db/tasks.d.ts +1 -1
- package/dist/db/tasks.d.ts.map +1 -1
- package/dist/index.js +67 -8
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +2346 -563
- package/dist/mcp/tools/agents.d.ts.map +1 -1
- package/dist/mcp/tools/task-adv-tools.d.ts.map +1 -1
- package/dist/mcp/tools/task-auto-tools.d.ts.map +1 -1
- package/dist/mcp/tools/task-crud.d.ts.map +1 -1
- package/dist/mcp/tools/task-meta-tools.d.ts.map +1 -1
- package/dist/mcp/tools/task-project-tools.d.ts.map +1 -1
- package/dist/mcp/tools/task-workflow-tools.d.ts.map +1 -1
- package/dist/server/index.js +68 -9
- package/dist/types/index.d.ts +11 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -2888,6 +2888,26 @@ var init_migrations = __esm(() => {
|
|
|
2888
2888
|
ALTER TABLE tasks ADD COLUMN current_step TEXT;
|
|
2889
2889
|
ALTER TABLE tasks ADD COLUMN total_steps INTEGER;
|
|
2890
2890
|
INSERT OR IGNORE INTO _migrations (id) VALUES (48);
|
|
2891
|
+
`,
|
|
2892
|
+
`
|
|
2893
|
+
CREATE TABLE IF NOT EXISTS cycles (
|
|
2894
|
+
id TEXT PRIMARY KEY,
|
|
2895
|
+
project_id TEXT REFERENCES projects(id) ON DELETE CASCADE,
|
|
2896
|
+
number INTEGER NOT NULL,
|
|
2897
|
+
start_date TEXT NOT NULL,
|
|
2898
|
+
end_date TEXT NOT NULL,
|
|
2899
|
+
duration_weeks INTEGER NOT NULL DEFAULT 1,
|
|
2900
|
+
status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active', 'completed', 'archived')),
|
|
2901
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
2902
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
2903
|
+
);
|
|
2904
|
+
CREATE INDEX IF NOT EXISTS idx_cycles_project ON cycles(project_id);
|
|
2905
|
+
CREATE INDEX IF NOT EXISTS idx_cycles_number ON cycles(number);
|
|
2906
|
+
CREATE INDEX IF NOT EXISTS idx_cycles_status ON cycles(status);
|
|
2907
|
+
CREATE INDEX IF NOT EXISTS idx_cycles_dates ON cycles(start_date, end_date);
|
|
2908
|
+
ALTER TABLE tasks ADD COLUMN cycle_id TEXT REFERENCES cycles(id) ON DELETE SET NULL;
|
|
2909
|
+
CREATE INDEX IF NOT EXISTS idx_tasks_cycle ON tasks(cycle_id) WHERE cycle_id IS NOT NULL;
|
|
2910
|
+
INSERT OR IGNORE INTO _migrations (id) VALUES (49);
|
|
2891
2911
|
`
|
|
2892
2912
|
];
|
|
2893
2913
|
});
|
|
@@ -4675,8 +4695,8 @@ function createTask(input, db) {
|
|
|
4675
4695
|
let id = uuid();
|
|
4676
4696
|
for (let attempt = 0;attempt < 3; attempt++) {
|
|
4677
4697
|
try {
|
|
4678
|
-
d.run(`INSERT INTO tasks (id, short_id, project_id, parent_id, plan_id, task_list_id, cycle_id, title, description, status, priority, agent_id, assigned_to, session_id, working_dir, tags, metadata, version, created_at, updated_at, due_at, estimated_minutes, requires_approval, approved_by, approved_at, recurrence_rule, recurrence_parent_id, spawns_template_id, reason, spawned_from_session, assigned_by, assigned_from_project, task_type)
|
|
4679
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
4698
|
+
d.run(`INSERT INTO tasks (id, short_id, project_id, parent_id, plan_id, task_list_id, cycle_id, title, description, status, priority, agent_id, assigned_to, session_id, working_dir, tags, metadata, version, created_at, updated_at, due_at, estimated_minutes, confidence, retry_count, max_retries, retry_after, requires_approval, approved_by, approved_at, recurrence_rule, recurrence_parent_id, spawns_template_id, reason, spawned_from_session, assigned_by, assigned_from_project, task_type)
|
|
4699
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
4680
4700
|
id,
|
|
4681
4701
|
null,
|
|
4682
4702
|
input.project_id || null,
|
|
@@ -4698,6 +4718,10 @@ function createTask(input, db) {
|
|
|
4698
4718
|
timestamp,
|
|
4699
4719
|
input.due_at || null,
|
|
4700
4720
|
input.estimated_minutes || null,
|
|
4721
|
+
input.confidence ?? null,
|
|
4722
|
+
input.retry_count ?? 0,
|
|
4723
|
+
input.max_retries ?? 3,
|
|
4724
|
+
input.retry_after ?? null,
|
|
4701
4725
|
input.requires_approval ? 1 : 0,
|
|
4702
4726
|
null,
|
|
4703
4727
|
null,
|
|
@@ -4722,11 +4746,11 @@ function createTask(input, db) {
|
|
|
4722
4746
|
if (tags.length > 0) {
|
|
4723
4747
|
insertTaskTags(id, tags, d);
|
|
4724
4748
|
}
|
|
4725
|
-
const task =
|
|
4749
|
+
const task = getTask(id, d);
|
|
4726
4750
|
dispatchWebhook("task.created", { id: task.id, short_id: task.short_id, title: task.title, status: task.status, priority: task.priority, project_id: task.project_id, assigned_to: task.assigned_to }, d).catch(() => {});
|
|
4727
4751
|
return task;
|
|
4728
4752
|
}
|
|
4729
|
-
function
|
|
4753
|
+
function getTask(id, db) {
|
|
4730
4754
|
const d = db || getDatabase();
|
|
4731
4755
|
const row = d.query("SELECT * FROM tasks WHERE id = ?").get(id);
|
|
4732
4756
|
if (!row)
|
|
@@ -4735,7 +4759,7 @@ function getTask2(id, db) {
|
|
|
4735
4759
|
}
|
|
4736
4760
|
function getTaskWithRelations(id, db) {
|
|
4737
4761
|
const d = db || getDatabase();
|
|
4738
|
-
const task =
|
|
4762
|
+
const task = getTask(id, d);
|
|
4739
4763
|
if (!task)
|
|
4740
4764
|
return null;
|
|
4741
4765
|
const subtaskRows = d.query("SELECT * FROM tasks WHERE parent_id = ? ORDER BY created_at").all(id);
|
|
@@ -4749,7 +4773,7 @@ function getTaskWithRelations(id, db) {
|
|
|
4749
4773
|
WHERE td.depends_on = ?`).all(id);
|
|
4750
4774
|
const blocked_by = blockedByRows.map(rowToTask);
|
|
4751
4775
|
const comments = d.query("SELECT * FROM task_comments WHERE task_id = ? ORDER BY created_at").all(id);
|
|
4752
|
-
const parent = task.parent_id ?
|
|
4776
|
+
const parent = task.parent_id ? getTask(task.parent_id, d) : null;
|
|
4753
4777
|
const checklist = getChecklist(id, d);
|
|
4754
4778
|
return {
|
|
4755
4779
|
...task,
|
|
@@ -4936,14 +4960,16 @@ function countTasks(filter = {}, db) {
|
|
|
4936
4960
|
}
|
|
4937
4961
|
function updateTask(id, input, db) {
|
|
4938
4962
|
const d = db || getDatabase();
|
|
4939
|
-
const task =
|
|
4963
|
+
const task = getTask(id, d);
|
|
4940
4964
|
if (!task)
|
|
4941
4965
|
throw new TaskNotFoundError(id);
|
|
4942
4966
|
if (task.version !== input.version) {
|
|
4943
4967
|
throw new VersionConflictError(id, input.version, task.version);
|
|
4944
4968
|
}
|
|
4969
|
+
const timestamp = now();
|
|
4970
|
+
const completionTimestamp = input.completed_at ?? timestamp;
|
|
4945
4971
|
const sets = ["version = version + 1", "updated_at = ?"];
|
|
4946
|
-
const params = [
|
|
4972
|
+
const params = [timestamp];
|
|
4947
4973
|
if (input.title !== undefined) {
|
|
4948
4974
|
sets.push("title = ?");
|
|
4949
4975
|
params.push(input.title);
|
|
@@ -4960,13 +4986,17 @@ function updateTask(id, input, db) {
|
|
|
4960
4986
|
params.push(input.status);
|
|
4961
4987
|
if (input.status === "completed") {
|
|
4962
4988
|
sets.push("completed_at = ?");
|
|
4963
|
-
params.push(
|
|
4989
|
+
params.push(completionTimestamp);
|
|
4964
4990
|
}
|
|
4965
4991
|
}
|
|
4966
4992
|
if (input.priority !== undefined) {
|
|
4967
4993
|
sets.push("priority = ?");
|
|
4968
4994
|
params.push(input.priority);
|
|
4969
4995
|
}
|
|
4996
|
+
if (input.project_id !== undefined) {
|
|
4997
|
+
sets.push("project_id = ?");
|
|
4998
|
+
params.push(input.project_id);
|
|
4999
|
+
}
|
|
4970
5000
|
if (input.assigned_to !== undefined) {
|
|
4971
5001
|
sets.push("assigned_to = ?");
|
|
4972
5002
|
params.push(input.assigned_to);
|
|
@@ -4995,6 +5025,30 @@ function updateTask(id, input, db) {
|
|
|
4995
5025
|
sets.push("estimated_minutes = ?");
|
|
4996
5026
|
params.push(input.estimated_minutes);
|
|
4997
5027
|
}
|
|
5028
|
+
if (input.actual_minutes !== undefined) {
|
|
5029
|
+
sets.push("actual_minutes = ?");
|
|
5030
|
+
params.push(input.actual_minutes);
|
|
5031
|
+
}
|
|
5032
|
+
if (input.completed_at !== undefined && input.status !== "completed") {
|
|
5033
|
+
sets.push("completed_at = ?");
|
|
5034
|
+
params.push(input.completed_at);
|
|
5035
|
+
}
|
|
5036
|
+
if (input.confidence !== undefined) {
|
|
5037
|
+
sets.push("confidence = ?");
|
|
5038
|
+
params.push(input.confidence);
|
|
5039
|
+
}
|
|
5040
|
+
if (input.retry_count !== undefined) {
|
|
5041
|
+
sets.push("retry_count = ?");
|
|
5042
|
+
params.push(input.retry_count);
|
|
5043
|
+
}
|
|
5044
|
+
if (input.max_retries !== undefined) {
|
|
5045
|
+
sets.push("max_retries = ?");
|
|
5046
|
+
params.push(input.max_retries);
|
|
5047
|
+
}
|
|
5048
|
+
if (input.retry_after !== undefined) {
|
|
5049
|
+
sets.push("retry_after = ?");
|
|
5050
|
+
params.push(input.retry_after);
|
|
5051
|
+
}
|
|
4998
5052
|
if (input.requires_approval !== undefined) {
|
|
4999
5053
|
sets.push("requires_approval = ?");
|
|
5000
5054
|
params.push(input.requires_approval ? 1 : 0);
|
|
@@ -5016,7 +5070,7 @@ function updateTask(id, input, db) {
|
|
|
5016
5070
|
params.push(id, input.version);
|
|
5017
5071
|
const result = d.run(`UPDATE tasks SET ${sets.join(", ")} WHERE id = ? AND version = ?`, params);
|
|
5018
5072
|
if (result.changes === 0) {
|
|
5019
|
-
const current =
|
|
5073
|
+
const current = getTask(id, d);
|
|
5020
5074
|
throw new VersionConflictError(id, input.version, current?.version ?? -1);
|
|
5021
5075
|
}
|
|
5022
5076
|
if (input.tags !== undefined) {
|
|
@@ -5045,11 +5099,16 @@ function updateTask(id, input, db) {
|
|
|
5045
5099
|
tags: input.tags ?? task.tags,
|
|
5046
5100
|
metadata: input.metadata ?? task.metadata,
|
|
5047
5101
|
version: task.version + 1,
|
|
5048
|
-
updated_at:
|
|
5049
|
-
completed_at: input.status === "completed" ?
|
|
5102
|
+
updated_at: timestamp,
|
|
5103
|
+
completed_at: input.status === "completed" ? completionTimestamp : input.completed_at !== undefined ? input.completed_at : task.completed_at,
|
|
5104
|
+
actual_minutes: input.actual_minutes ?? task.actual_minutes,
|
|
5105
|
+
confidence: input.confidence !== undefined ? input.confidence : task.confidence,
|
|
5106
|
+
retry_count: input.retry_count ?? task.retry_count,
|
|
5107
|
+
max_retries: input.max_retries ?? task.max_retries,
|
|
5108
|
+
retry_after: input.retry_after !== undefined ? input.retry_after : task.retry_after,
|
|
5050
5109
|
requires_approval: input.requires_approval !== undefined ? input.requires_approval : task.requires_approval,
|
|
5051
5110
|
approved_by: input.approved_by ?? task.approved_by,
|
|
5052
|
-
approved_at: input.approved_by ?
|
|
5111
|
+
approved_at: input.approved_by ? timestamp : task.approved_at
|
|
5053
5112
|
};
|
|
5054
5113
|
}
|
|
5055
5114
|
function deleteTask(id, db) {
|
|
@@ -5622,9 +5681,9 @@ var init_templates = __esm(() => {
|
|
|
5622
5681
|
// src/db/task-graph.ts
|
|
5623
5682
|
function addDependency(taskId, dependsOn, db) {
|
|
5624
5683
|
const d = db || getDatabase();
|
|
5625
|
-
if (!
|
|
5684
|
+
if (!getTask(taskId, d))
|
|
5626
5685
|
throw new TaskNotFoundError(taskId);
|
|
5627
|
-
if (!
|
|
5686
|
+
if (!getTask(dependsOn, d))
|
|
5628
5687
|
throw new TaskNotFoundError(dependsOn);
|
|
5629
5688
|
if (wouldCreateCycle(taskId, dependsOn, d)) {
|
|
5630
5689
|
throw new DependencyCycleError(taskId, dependsOn);
|
|
@@ -5646,7 +5705,7 @@ function getTaskDependents(taskId, db) {
|
|
|
5646
5705
|
}
|
|
5647
5706
|
function cloneTask(taskId, overrides, db) {
|
|
5648
5707
|
const d = db || getDatabase();
|
|
5649
|
-
const source =
|
|
5708
|
+
const source = getTask(taskId, d);
|
|
5650
5709
|
if (!source)
|
|
5651
5710
|
throw new TaskNotFoundError(taskId);
|
|
5652
5711
|
const input = {
|
|
@@ -5669,13 +5728,13 @@ function cloneTask(taskId, overrides, db) {
|
|
|
5669
5728
|
}
|
|
5670
5729
|
function getTaskGraph(taskId, direction = "both", db) {
|
|
5671
5730
|
const d = db || getDatabase();
|
|
5672
|
-
const task =
|
|
5731
|
+
const task = getTask(taskId, d);
|
|
5673
5732
|
if (!task)
|
|
5674
5733
|
throw new TaskNotFoundError(taskId);
|
|
5675
5734
|
function toNode(t) {
|
|
5676
5735
|
const deps = getTaskDependencies(t.id, d);
|
|
5677
5736
|
const hasUnfinishedDeps = deps.some((dep) => {
|
|
5678
|
-
const depTask =
|
|
5737
|
+
const depTask = getTask(dep.depends_on, d);
|
|
5679
5738
|
return depTask && depTask.status !== "completed";
|
|
5680
5739
|
});
|
|
5681
5740
|
return { id: t.id, short_id: t.short_id, title: t.title, status: t.status, priority: t.priority, is_blocked: hasUnfinishedDeps };
|
|
@@ -5686,7 +5745,7 @@ function getTaskGraph(taskId, direction = "both", db) {
|
|
|
5686
5745
|
visited.add(id);
|
|
5687
5746
|
const deps = d.query("SELECT depends_on FROM task_dependencies WHERE task_id = ?").all(id);
|
|
5688
5747
|
return deps.map((dep) => {
|
|
5689
|
-
const depTask =
|
|
5748
|
+
const depTask = getTask(dep.depends_on, d);
|
|
5690
5749
|
if (!depTask)
|
|
5691
5750
|
return null;
|
|
5692
5751
|
return { task: toNode(depTask), depends_on: buildUp(dep.depends_on, visited), blocks: [] };
|
|
@@ -5698,7 +5757,7 @@ function getTaskGraph(taskId, direction = "both", db) {
|
|
|
5698
5757
|
visited.add(id);
|
|
5699
5758
|
const dependents = d.query("SELECT task_id FROM task_dependencies WHERE depends_on = ?").all(id);
|
|
5700
5759
|
return dependents.map((dep) => {
|
|
5701
|
-
const depTask =
|
|
5760
|
+
const depTask = getTask(dep.task_id, d);
|
|
5702
5761
|
if (!depTask)
|
|
5703
5762
|
return null;
|
|
5704
5763
|
return { task: toNode(depTask), depends_on: [], blocks: buildDown(dep.task_id, visited) };
|
|
@@ -5711,7 +5770,7 @@ function getTaskGraph(taskId, direction = "both", db) {
|
|
|
5711
5770
|
}
|
|
5712
5771
|
function moveTask(taskId, target, db) {
|
|
5713
5772
|
const d = db || getDatabase();
|
|
5714
|
-
const task =
|
|
5773
|
+
const task = getTask(taskId, d);
|
|
5715
5774
|
if (!task)
|
|
5716
5775
|
throw new TaskNotFoundError(taskId);
|
|
5717
5776
|
const sets = ["updated_at = ?", "version = version + 1"];
|
|
@@ -5730,7 +5789,7 @@ function moveTask(taskId, target, db) {
|
|
|
5730
5789
|
}
|
|
5731
5790
|
params.push(taskId);
|
|
5732
5791
|
d.run(`UPDATE tasks SET ${sets.join(", ")} WHERE id = ?`, params);
|
|
5733
|
-
return
|
|
5792
|
+
return getTask(taskId, d);
|
|
5734
5793
|
}
|
|
5735
5794
|
function wouldCreateCycle(taskId, dependsOn, db) {
|
|
5736
5795
|
const visited = new Set;
|
|
@@ -5763,7 +5822,7 @@ function getBlockingDeps(id, db) {
|
|
|
5763
5822
|
return [];
|
|
5764
5823
|
const blocking = [];
|
|
5765
5824
|
for (const dep of deps) {
|
|
5766
|
-
const task =
|
|
5825
|
+
const task = getTask(dep.depends_on, d);
|
|
5767
5826
|
if (task && task.status !== "completed")
|
|
5768
5827
|
blocking.push(task);
|
|
5769
5828
|
}
|
|
@@ -5771,7 +5830,7 @@ function getBlockingDeps(id, db) {
|
|
|
5771
5830
|
}
|
|
5772
5831
|
function startTask(id, agentId, db) {
|
|
5773
5832
|
const d = db || getDatabase();
|
|
5774
|
-
const task =
|
|
5833
|
+
const task = getTask(id, d);
|
|
5775
5834
|
if (!task)
|
|
5776
5835
|
throw new TaskNotFoundError(id);
|
|
5777
5836
|
const blocking = getBlockingDeps(id, d);
|
|
@@ -5794,7 +5853,7 @@ function startTask(id, agentId, db) {
|
|
|
5794
5853
|
}
|
|
5795
5854
|
function completeTask(id, agentId, db, options) {
|
|
5796
5855
|
const d = db || getDatabase();
|
|
5797
|
-
const task =
|
|
5856
|
+
const task = getTask(id, d);
|
|
5798
5857
|
if (!task)
|
|
5799
5858
|
throw new TaskNotFoundError(id);
|
|
5800
5859
|
if (agentId && task.locked_by && task.locked_by !== agentId && !isLockExpired(task.locked_at)) {
|
|
@@ -5810,14 +5869,14 @@ function completeTask(id, agentId, db, options) {
|
|
|
5810
5869
|
completionMeta._completion = { confidence: options.confidence };
|
|
5811
5870
|
}
|
|
5812
5871
|
const hasMeta = Object.keys(completionMeta).length > 0;
|
|
5813
|
-
const timestamp = now();
|
|
5872
|
+
const timestamp = options?.completed_at || now();
|
|
5814
5873
|
const confidence = options?.confidence !== undefined ? options.confidence : null;
|
|
5815
5874
|
const tx = d.transaction(() => {
|
|
5816
5875
|
if (hasMeta) {
|
|
5817
5876
|
const meta2 = { ...task.metadata, ...completionMeta };
|
|
5818
5877
|
const metaResult = d.run("UPDATE tasks SET metadata = ?, version = version + 1, updated_at = ? WHERE id = ? AND version = ?", [JSON.stringify(meta2), timestamp, id, task.version]);
|
|
5819
5878
|
if (metaResult.changes === 0) {
|
|
5820
|
-
const current =
|
|
5879
|
+
const current = getTask(id, d);
|
|
5821
5880
|
throw new VersionConflictError(id, task.version, current?.version ?? -1);
|
|
5822
5881
|
}
|
|
5823
5882
|
}
|
|
@@ -5874,7 +5933,7 @@ function completeTask(id, agentId, db, options) {
|
|
|
5874
5933
|
}
|
|
5875
5934
|
function lockTask(id, agentId, db) {
|
|
5876
5935
|
const d = db || getDatabase();
|
|
5877
|
-
const task =
|
|
5936
|
+
const task = getTask(id, d);
|
|
5878
5937
|
if (!task)
|
|
5879
5938
|
throw new TaskNotFoundError(id);
|
|
5880
5939
|
if (task.locked_by === agentId && !isLockExpired(task.locked_at)) {
|
|
@@ -5885,7 +5944,7 @@ function lockTask(id, agentId, db) {
|
|
|
5885
5944
|
const result = d.run(`UPDATE tasks SET locked_by = ?, locked_at = ?, version = version + 1, updated_at = ?
|
|
5886
5945
|
WHERE id = ? AND (locked_by IS NULL OR locked_by = ? OR locked_at < ?)`, [agentId, timestamp, timestamp, id, agentId, cutoff]);
|
|
5887
5946
|
if (result.changes === 0) {
|
|
5888
|
-
const current =
|
|
5947
|
+
const current = getTask(id, d);
|
|
5889
5948
|
if (!current)
|
|
5890
5949
|
throw new TaskNotFoundError(id);
|
|
5891
5950
|
if (current.locked_by && !isLockExpired(current.locked_at)) {
|
|
@@ -5901,7 +5960,7 @@ function lockTask(id, agentId, db) {
|
|
|
5901
5960
|
}
|
|
5902
5961
|
function unlockTask(id, agentId, db) {
|
|
5903
5962
|
const d = db || getDatabase();
|
|
5904
|
-
const task =
|
|
5963
|
+
const task = getTask(id, d);
|
|
5905
5964
|
if (!task)
|
|
5906
5965
|
throw new TaskNotFoundError(id);
|
|
5907
5966
|
if (agentId && task.locked_by && task.locked_by !== agentId) {
|
|
@@ -6002,7 +6061,7 @@ function getTasksChangedSince(since, filters, db) {
|
|
|
6002
6061
|
}
|
|
6003
6062
|
function failTask(id, agentId, reason, options, db) {
|
|
6004
6063
|
const d = db || getDatabase();
|
|
6005
|
-
const task =
|
|
6064
|
+
const task = getTask(id, d);
|
|
6006
6065
|
if (!task)
|
|
6007
6066
|
throw new TaskNotFoundError(id);
|
|
6008
6067
|
const meta = {
|
|
@@ -6201,7 +6260,7 @@ function getStatus(filters, agentId, options, db) {
|
|
|
6201
6260
|
}
|
|
6202
6261
|
function decomposeTasks(parentId, subtasks, options, db) {
|
|
6203
6262
|
const d = db || getDatabase();
|
|
6204
|
-
const parent =
|
|
6263
|
+
const parent = getTask(parentId, d);
|
|
6205
6264
|
if (!parent)
|
|
6206
6265
|
throw new TaskNotFoundError(parentId);
|
|
6207
6266
|
const created = [];
|
|
@@ -6232,7 +6291,7 @@ function decomposeTasks(parentId, subtasks, options, db) {
|
|
|
6232
6291
|
function setTaskStatus(id, status, _agentId, db) {
|
|
6233
6292
|
const d = db || getDatabase();
|
|
6234
6293
|
for (let attempt = 0;attempt < 3; attempt++) {
|
|
6235
|
-
const task =
|
|
6294
|
+
const task = getTask(id, d);
|
|
6236
6295
|
if (!task)
|
|
6237
6296
|
throw new TaskNotFoundError(id);
|
|
6238
6297
|
if (task.status === status)
|
|
@@ -6250,7 +6309,7 @@ function setTaskStatus(id, status, _agentId, db) {
|
|
|
6250
6309
|
function setTaskPriority(id, priority, _agentId, db) {
|
|
6251
6310
|
const d = db || getDatabase();
|
|
6252
6311
|
for (let attempt = 0;attempt < 3; attempt++) {
|
|
6253
|
-
const task =
|
|
6312
|
+
const task = getTask(id, d);
|
|
6254
6313
|
if (!task)
|
|
6255
6314
|
throw new TaskNotFoundError(id);
|
|
6256
6315
|
if (task.priority === priority)
|
|
@@ -6358,7 +6417,7 @@ function bulkUpdateTasks(taskIds, updates, db) {
|
|
|
6358
6417
|
const tx = d.transaction(() => {
|
|
6359
6418
|
for (const id of taskIds) {
|
|
6360
6419
|
try {
|
|
6361
|
-
const task =
|
|
6420
|
+
const task = getTask(id, d);
|
|
6362
6421
|
if (!task) {
|
|
6363
6422
|
failed.push({ id, error: "Task not found" });
|
|
6364
6423
|
continue;
|
|
@@ -6373,6 +6432,30 @@ function bulkUpdateTasks(taskIds, updates, db) {
|
|
|
6373
6432
|
tx();
|
|
6374
6433
|
return { updated, failed };
|
|
6375
6434
|
}
|
|
6435
|
+
function bulkDeleteTasks(taskIds, force = false, db) {
|
|
6436
|
+
const d = db || getDatabase();
|
|
6437
|
+
let deleted = 0;
|
|
6438
|
+
let skipped = 0;
|
|
6439
|
+
const failed = [];
|
|
6440
|
+
const tx = d.transaction(() => {
|
|
6441
|
+
for (const id of taskIds) {
|
|
6442
|
+
try {
|
|
6443
|
+
const childCount = d.query("SELECT COUNT(*) as count FROM tasks WHERE parent_id = ?").get(id);
|
|
6444
|
+
if (!force && childCount.count > 0) {
|
|
6445
|
+
skipped++;
|
|
6446
|
+
continue;
|
|
6447
|
+
}
|
|
6448
|
+
const result = d.run("DELETE FROM tasks WHERE id = ?", [id]);
|
|
6449
|
+
if (result.changes > 0)
|
|
6450
|
+
deleted++;
|
|
6451
|
+
} catch (e) {
|
|
6452
|
+
failed.push({ id, error: e instanceof Error ? e.message : String(e) });
|
|
6453
|
+
}
|
|
6454
|
+
}
|
|
6455
|
+
});
|
|
6456
|
+
tx();
|
|
6457
|
+
return { deleted, skipped, failed };
|
|
6458
|
+
}
|
|
6376
6459
|
function archiveTasks(options, db) {
|
|
6377
6460
|
const d = db || getDatabase();
|
|
6378
6461
|
const conditions = ["archived_at IS NULL"];
|
|
@@ -6397,10 +6480,33 @@ function archiveTasks(options, db) {
|
|
|
6397
6480
|
const result = d.run(`UPDATE tasks SET archived_at = ? WHERE ${conditions.join(" AND ")}`, [ts, ...params]);
|
|
6398
6481
|
return { archived: result.changes };
|
|
6399
6482
|
}
|
|
6483
|
+
function archiveCompletedTasks(days = 7, projectId, db) {
|
|
6484
|
+
return archiveTasks({
|
|
6485
|
+
project_id: projectId,
|
|
6486
|
+
older_than_days: days,
|
|
6487
|
+
status: ["completed"]
|
|
6488
|
+
}, db).archived;
|
|
6489
|
+
}
|
|
6490
|
+
function getArchivedTasks(opts = {}, db) {
|
|
6491
|
+
const d = db || getDatabase();
|
|
6492
|
+
const conditions = ["archived_at IS NOT NULL"];
|
|
6493
|
+
const params = [];
|
|
6494
|
+
if (opts.project_id) {
|
|
6495
|
+
conditions.push("project_id = ?");
|
|
6496
|
+
params.push(opts.project_id);
|
|
6497
|
+
}
|
|
6498
|
+
let limitClause = "";
|
|
6499
|
+
if (opts.limit) {
|
|
6500
|
+
limitClause = " LIMIT ?";
|
|
6501
|
+
params.push(opts.limit);
|
|
6502
|
+
}
|
|
6503
|
+
const rows = d.query(`SELECT * FROM tasks WHERE ${conditions.join(" AND ")} ORDER BY archived_at DESC${limitClause}`).all(...params);
|
|
6504
|
+
return rows.map(rowToTask);
|
|
6505
|
+
}
|
|
6400
6506
|
function unarchiveTask(id, db) {
|
|
6401
6507
|
const d = db || getDatabase();
|
|
6402
6508
|
d.run("UPDATE tasks SET archived_at = NULL WHERE id = ?", [id]);
|
|
6403
|
-
return
|
|
6509
|
+
return getTask(id, d);
|
|
6404
6510
|
}
|
|
6405
6511
|
function getOverdueTasks(projectId, db) {
|
|
6406
6512
|
const d = db || getDatabase();
|
|
@@ -6415,6 +6521,80 @@ function getOverdueTasks(projectId, db) {
|
|
|
6415
6521
|
const rows = d.query(query).all(...params);
|
|
6416
6522
|
return rows.map(rowToTask);
|
|
6417
6523
|
}
|
|
6524
|
+
function notifyUpcomingDeadlines(opts = {}, db) {
|
|
6525
|
+
const d = db || getDatabase();
|
|
6526
|
+
const hours = opts.hours ?? 24;
|
|
6527
|
+
const start = new Date().toISOString();
|
|
6528
|
+
const end = new Date(Date.now() + hours * 60 * 60 * 1000).toISOString();
|
|
6529
|
+
const conditions = [
|
|
6530
|
+
"archived_at IS NULL",
|
|
6531
|
+
"due_at IS NOT NULL",
|
|
6532
|
+
"due_at >= ?",
|
|
6533
|
+
"due_at <= ?",
|
|
6534
|
+
"status NOT IN ('completed', 'cancelled', 'failed')"
|
|
6535
|
+
];
|
|
6536
|
+
const params = [start, end];
|
|
6537
|
+
if (opts.project_id) {
|
|
6538
|
+
conditions.push("project_id = ?");
|
|
6539
|
+
params.push(opts.project_id);
|
|
6540
|
+
}
|
|
6541
|
+
if (opts.agent_id) {
|
|
6542
|
+
conditions.push("assigned_to = ?");
|
|
6543
|
+
params.push(opts.agent_id);
|
|
6544
|
+
}
|
|
6545
|
+
const rows = d.query(`SELECT * FROM tasks WHERE ${conditions.join(" AND ")} ORDER BY due_at ASC`).all(...params);
|
|
6546
|
+
return rows.map(rowToTask);
|
|
6547
|
+
}
|
|
6548
|
+
function getBlockedTasks(projectId, db) {
|
|
6549
|
+
const d = db || getDatabase();
|
|
6550
|
+
const params = [];
|
|
6551
|
+
let projectClause = "";
|
|
6552
|
+
if (projectId) {
|
|
6553
|
+
projectClause = "AND t.project_id = ?";
|
|
6554
|
+
params.push(projectId);
|
|
6555
|
+
}
|
|
6556
|
+
const rows = d.query(`
|
|
6557
|
+
SELECT t.*, GROUP_CONCAT(dep.id) AS blocked_by_ids
|
|
6558
|
+
FROM tasks t
|
|
6559
|
+
JOIN task_dependencies td ON td.task_id = t.id
|
|
6560
|
+
JOIN tasks dep ON dep.id = td.depends_on
|
|
6561
|
+
WHERE t.archived_at IS NULL
|
|
6562
|
+
AND t.status NOT IN ('completed', 'cancelled', 'failed')
|
|
6563
|
+
AND dep.status NOT IN ('completed', 'cancelled')
|
|
6564
|
+
${projectClause}
|
|
6565
|
+
GROUP BY t.id
|
|
6566
|
+
ORDER BY t.priority DESC, t.created_at DESC
|
|
6567
|
+
`).all(...params);
|
|
6568
|
+
return rows.map((row) => ({
|
|
6569
|
+
...rowToTask(row),
|
|
6570
|
+
blocked_by: row.blocked_by_ids ? row.blocked_by_ids.split(",") : []
|
|
6571
|
+
}));
|
|
6572
|
+
}
|
|
6573
|
+
function getBlockingTasks(projectId, db) {
|
|
6574
|
+
const d = db || getDatabase();
|
|
6575
|
+
const params = [];
|
|
6576
|
+
let projectClause = "";
|
|
6577
|
+
if (projectId) {
|
|
6578
|
+
projectClause = "AND blocker.project_id = ?";
|
|
6579
|
+
params.push(projectId);
|
|
6580
|
+
}
|
|
6581
|
+
const rows = d.query(`
|
|
6582
|
+
SELECT blocker.*, COUNT(DISTINCT blocked.id) AS blocking_count
|
|
6583
|
+
FROM tasks blocker
|
|
6584
|
+
JOIN task_dependencies td ON td.depends_on = blocker.id
|
|
6585
|
+
JOIN tasks blocked ON blocked.id = td.task_id
|
|
6586
|
+
WHERE blocker.archived_at IS NULL
|
|
6587
|
+
AND blocker.status NOT IN ('completed', 'cancelled', 'failed')
|
|
6588
|
+
AND blocked.status NOT IN ('completed', 'cancelled', 'failed')
|
|
6589
|
+
${projectClause}
|
|
6590
|
+
GROUP BY blocker.id
|
|
6591
|
+
ORDER BY blocking_count DESC, blocker.created_at DESC
|
|
6592
|
+
`).all(...params);
|
|
6593
|
+
return rows.map((row) => ({
|
|
6594
|
+
...rowToTask(row),
|
|
6595
|
+
blocking_count: row.blocking_count
|
|
6596
|
+
}));
|
|
6597
|
+
}
|
|
6418
6598
|
function logTime(input, db) {
|
|
6419
6599
|
const d = db || getDatabase();
|
|
6420
6600
|
const id = uuid();
|
|
@@ -6495,6 +6675,7 @@ __export(exports_tasks, {
|
|
|
6495
6675
|
removeDependency: () => removeDependency,
|
|
6496
6676
|
redistributeStaleTasks: () => redistributeStaleTasks,
|
|
6497
6677
|
notifyWatchers: () => notifyWatchers,
|
|
6678
|
+
notifyUpcomingDeadlines: () => notifyUpcomingDeadlines,
|
|
6498
6679
|
moveTask: () => moveTask,
|
|
6499
6680
|
logTime: () => logTime,
|
|
6500
6681
|
logCost: () => logCost,
|
|
@@ -6510,12 +6691,15 @@ __export(exports_tasks, {
|
|
|
6510
6691
|
getTaskGraph: () => getTaskGraph,
|
|
6511
6692
|
getTaskDependents: () => getTaskDependents,
|
|
6512
6693
|
getTaskDependencies: () => getTaskDependencies,
|
|
6513
|
-
getTask: () =>
|
|
6694
|
+
getTask: () => getTask,
|
|
6514
6695
|
getStatus: () => getStatus,
|
|
6515
6696
|
getStaleTasks: () => getStaleTasks,
|
|
6516
6697
|
getOverdueTasks: () => getOverdueTasks,
|
|
6517
6698
|
getNextTask: () => getNextTask,
|
|
6699
|
+
getBlockingTasks: () => getBlockingTasks,
|
|
6518
6700
|
getBlockingDeps: () => getBlockingDeps,
|
|
6701
|
+
getBlockedTasks: () => getBlockedTasks,
|
|
6702
|
+
getArchivedTasks: () => getArchivedTasks,
|
|
6519
6703
|
getActiveWork: () => getActiveWork,
|
|
6520
6704
|
failTask: () => failTask,
|
|
6521
6705
|
deleteTask: () => deleteTask,
|
|
@@ -6527,8 +6711,10 @@ __export(exports_tasks, {
|
|
|
6527
6711
|
claimOrSteal: () => claimOrSteal,
|
|
6528
6712
|
claimNextTask: () => claimNextTask,
|
|
6529
6713
|
bulkUpdateTasks: () => bulkUpdateTasks,
|
|
6714
|
+
bulkDeleteTasks: () => bulkDeleteTasks,
|
|
6530
6715
|
bulkCreateTasks: () => bulkCreateTasks,
|
|
6531
6716
|
archiveTasks: () => archiveTasks,
|
|
6717
|
+
archiveCompletedTasks: () => archiveCompletedTasks,
|
|
6532
6718
|
addDependency: () => addDependency
|
|
6533
6719
|
});
|
|
6534
6720
|
var init_tasks = __esm(() => {
|
|
@@ -6934,9 +7120,18 @@ var init_builtin_templates = __esm(() => {
|
|
|
6934
7120
|
});
|
|
6935
7121
|
|
|
6936
7122
|
// src/db/comments.ts
|
|
7123
|
+
var exports_comments = {};
|
|
7124
|
+
__export(exports_comments, {
|
|
7125
|
+
updateComment: () => updateComment,
|
|
7126
|
+
logProgress: () => logProgress,
|
|
7127
|
+
listComments: () => listComments,
|
|
7128
|
+
getComment: () => getComment,
|
|
7129
|
+
deleteComment: () => deleteComment,
|
|
7130
|
+
addComment: () => addComment
|
|
7131
|
+
});
|
|
6937
7132
|
function addComment(input, db) {
|
|
6938
7133
|
const d = db || getDatabase();
|
|
6939
|
-
if (!
|
|
7134
|
+
if (!getTask(input.task_id, d)) {
|
|
6940
7135
|
throw new TaskNotFoundError(input.task_id);
|
|
6941
7136
|
}
|
|
6942
7137
|
const id = uuid();
|
|
@@ -6965,6 +7160,15 @@ function listComments(taskId, db) {
|
|
|
6965
7160
|
const d = db || getDatabase();
|
|
6966
7161
|
return d.query("SELECT * FROM task_comments WHERE task_id = ? ORDER BY created_at").all(taskId);
|
|
6967
7162
|
}
|
|
7163
|
+
function updateComment(id, input, db) {
|
|
7164
|
+
const d = db || getDatabase();
|
|
7165
|
+
d.run("UPDATE task_comments SET content = ? WHERE id = ?", [input.content, id]);
|
|
7166
|
+
const comment = getComment(id, d);
|
|
7167
|
+
if (!comment) {
|
|
7168
|
+
throw new Error(`Comment not found: ${id}`);
|
|
7169
|
+
}
|
|
7170
|
+
return comment;
|
|
7171
|
+
}
|
|
6968
7172
|
function deleteComment(id, db) {
|
|
6969
7173
|
const d = db || getDatabase();
|
|
6970
7174
|
const result = d.run("DELETE FROM task_comments WHERE id = ?", [id]);
|
|
@@ -6977,6 +7181,10 @@ var init_comments = __esm(() => {
|
|
|
6977
7181
|
});
|
|
6978
7182
|
|
|
6979
7183
|
// src/lib/search.ts
|
|
7184
|
+
var exports_search = {};
|
|
7185
|
+
__export(exports_search, {
|
|
7186
|
+
searchTasks: () => searchTasks
|
|
7187
|
+
});
|
|
6980
7188
|
function rowToTask2(row) {
|
|
6981
7189
|
return {
|
|
6982
7190
|
...row,
|
|
@@ -7994,7 +8202,7 @@ function handleTasksContext(_req, url, _ctx, json2, taskToSummary2) {
|
|
|
7994
8202
|
return new Response(text, { headers: { "Content-Type": "text/plain" } });
|
|
7995
8203
|
}
|
|
7996
8204
|
function handleTaskAttachments(id, _ctx, json2) {
|
|
7997
|
-
const task =
|
|
8205
|
+
const task = getTask(id);
|
|
7998
8206
|
if (!task)
|
|
7999
8207
|
return json2({ error: "Task not found" }, 404);
|
|
8000
8208
|
const evidence = task.metadata?._evidence || {};
|
|
@@ -8002,7 +8210,7 @@ function handleTaskAttachments(id, _ctx, json2) {
|
|
|
8002
8210
|
return json2({ task_id: id, short_id: task.short_id, attachment_ids: attachmentIds, count: attachmentIds.length, files_changed: evidence.files_changed, commit_hash: evidence.commit_hash, notes: evidence.notes });
|
|
8003
8211
|
}
|
|
8004
8212
|
async function handleTaskProgress(id, req, method, _ctx, json2) {
|
|
8005
|
-
const task =
|
|
8213
|
+
const task = getTask(id);
|
|
8006
8214
|
if (!task)
|
|
8007
8215
|
return json2({ error: "Task not found" }, 404);
|
|
8008
8216
|
if (method === "GET") {
|
|
@@ -8025,7 +8233,7 @@ async function handleTaskProgress(id, req, method, _ctx, json2) {
|
|
|
8025
8233
|
return null;
|
|
8026
8234
|
}
|
|
8027
8235
|
function handleGetTask(id, _ctx, json2, taskToSummary2) {
|
|
8028
|
-
const task =
|
|
8236
|
+
const task = getTask(id);
|
|
8029
8237
|
if (!task)
|
|
8030
8238
|
return json2({ error: "Task not found" }, 404);
|
|
8031
8239
|
return json2(taskToSummary2(task));
|
|
@@ -8033,10 +8241,10 @@ function handleGetTask(id, _ctx, json2, taskToSummary2) {
|
|
|
8033
8241
|
async function handlePatchTask(id, req, _ctx, json2, taskToSummary2) {
|
|
8034
8242
|
try {
|
|
8035
8243
|
const body = await req.json();
|
|
8036
|
-
const task =
|
|
8244
|
+
const task = getTask(id);
|
|
8037
8245
|
if (!task)
|
|
8038
8246
|
return json2({ error: "Task not found" }, 404);
|
|
8039
|
-
const ALLOWED = new Set(["title", "description", "status", "priority", "assigned_to", "plan_id", "task_list_id", "tags", "metadata", "due_at", "estimated_minutes", "task_type"]);
|
|
8247
|
+
const ALLOWED = new Set(["title", "description", "status", "priority", "assigned_to", "plan_id", "task_list_id", "tags", "metadata", "due_at", "estimated_minutes", "actual_minutes", "confidence", "retry_count", "max_retries", "retry_after", "task_type"]);
|
|
8040
8248
|
const safeBody = {};
|
|
8041
8249
|
for (const [key, value] of Object.entries(body)) {
|
|
8042
8250
|
if (ALLOWED.has(key))
|
|
@@ -10199,6 +10407,77 @@ var init_Dashboard = __esm(() => {
|
|
|
10199
10407
|
init_audit();
|
|
10200
10408
|
});
|
|
10201
10409
|
|
|
10410
|
+
// src/db/handoffs.ts
|
|
10411
|
+
var exports_handoffs = {};
|
|
10412
|
+
__export(exports_handoffs, {
|
|
10413
|
+
listHandoffs: () => listHandoffs,
|
|
10414
|
+
getLatestHandoff: () => getLatestHandoff,
|
|
10415
|
+
createHandoff: () => createHandoff
|
|
10416
|
+
});
|
|
10417
|
+
function createHandoff(input, db) {
|
|
10418
|
+
const d = db || getDatabase();
|
|
10419
|
+
const id = uuid();
|
|
10420
|
+
const timestamp = now();
|
|
10421
|
+
d.run(`INSERT INTO handoffs (id, agent_id, project_id, summary, completed, in_progress, blockers, next_steps, created_at)
|
|
10422
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
10423
|
+
id,
|
|
10424
|
+
input.agent_id || null,
|
|
10425
|
+
input.project_id || null,
|
|
10426
|
+
input.summary,
|
|
10427
|
+
input.completed ? JSON.stringify(input.completed) : null,
|
|
10428
|
+
input.in_progress ? JSON.stringify(input.in_progress) : null,
|
|
10429
|
+
input.blockers ? JSON.stringify(input.blockers) : null,
|
|
10430
|
+
input.next_steps ? JSON.stringify(input.next_steps) : null,
|
|
10431
|
+
timestamp
|
|
10432
|
+
]);
|
|
10433
|
+
return {
|
|
10434
|
+
id,
|
|
10435
|
+
agent_id: input.agent_id || null,
|
|
10436
|
+
project_id: input.project_id || null,
|
|
10437
|
+
summary: input.summary,
|
|
10438
|
+
completed: input.completed || null,
|
|
10439
|
+
in_progress: input.in_progress || null,
|
|
10440
|
+
blockers: input.blockers || null,
|
|
10441
|
+
next_steps: input.next_steps || null,
|
|
10442
|
+
created_at: timestamp
|
|
10443
|
+
};
|
|
10444
|
+
}
|
|
10445
|
+
function rowToHandoff(row) {
|
|
10446
|
+
return {
|
|
10447
|
+
...row,
|
|
10448
|
+
completed: row.completed ? JSON.parse(row.completed) : null,
|
|
10449
|
+
in_progress: row.in_progress ? JSON.parse(row.in_progress) : null,
|
|
10450
|
+
blockers: row.blockers ? JSON.parse(row.blockers) : null,
|
|
10451
|
+
next_steps: row.next_steps ? JSON.parse(row.next_steps) : null
|
|
10452
|
+
};
|
|
10453
|
+
}
|
|
10454
|
+
function listHandoffs(projectId, limit = 10, db) {
|
|
10455
|
+
const d = db || getDatabase();
|
|
10456
|
+
if (projectId) {
|
|
10457
|
+
return d.query("SELECT * FROM handoffs WHERE project_id = ? ORDER BY rowid DESC LIMIT ?").all(projectId, limit).map(rowToHandoff);
|
|
10458
|
+
}
|
|
10459
|
+
return d.query("SELECT * FROM handoffs ORDER BY rowid DESC LIMIT ?").all(limit).map(rowToHandoff);
|
|
10460
|
+
}
|
|
10461
|
+
function getLatestHandoff(agentId, projectId, db) {
|
|
10462
|
+
const d = db || getDatabase();
|
|
10463
|
+
let query = "SELECT * FROM handoffs WHERE 1=1";
|
|
10464
|
+
const params = [];
|
|
10465
|
+
if (agentId) {
|
|
10466
|
+
query += " AND agent_id = ?";
|
|
10467
|
+
params.push(agentId);
|
|
10468
|
+
}
|
|
10469
|
+
if (projectId) {
|
|
10470
|
+
query += " AND project_id = ?";
|
|
10471
|
+
params.push(projectId);
|
|
10472
|
+
}
|
|
10473
|
+
query += " ORDER BY rowid DESC LIMIT 1";
|
|
10474
|
+
const row = d.query(query).get(...params);
|
|
10475
|
+
return row ? rowToHandoff(row) : null;
|
|
10476
|
+
}
|
|
10477
|
+
var init_handoffs = __esm(() => {
|
|
10478
|
+
init_database();
|
|
10479
|
+
});
|
|
10480
|
+
|
|
10202
10481
|
// src/lib/auto-assign.ts
|
|
10203
10482
|
var exports_auto_assign = {};
|
|
10204
10483
|
__export(exports_auto_assign, {
|
|
@@ -10289,7 +10568,7 @@ async function callCerebras(prompt, apiKey) {
|
|
|
10289
10568
|
}
|
|
10290
10569
|
async function autoAssignTask(taskId, db) {
|
|
10291
10570
|
const d = db || getDatabase();
|
|
10292
|
-
const task =
|
|
10571
|
+
const task = getTask(taskId, d);
|
|
10293
10572
|
if (!task)
|
|
10294
10573
|
throw new Error(`Task ${taskId} not found`);
|
|
10295
10574
|
const agents = listAgents(d).filter((a) => a.status === "active");
|
|
@@ -26215,12 +26494,21 @@ var init_dispatch2 = __esm(() => {
|
|
|
26215
26494
|
// src/mcp/tools/task-crud.ts
|
|
26216
26495
|
function registerTaskCrudTools(server, ctx) {
|
|
26217
26496
|
const { shouldRegisterTool, resolveId, formatError, formatTask } = ctx;
|
|
26497
|
+
function versionFor(taskId, version) {
|
|
26498
|
+
const current = getTask(taskId);
|
|
26499
|
+
if (!current)
|
|
26500
|
+
throw new TaskNotFoundError(taskId);
|
|
26501
|
+
if (version !== undefined && current.version !== version) {
|
|
26502
|
+
throw new VersionConflictError(taskId, version, current.version);
|
|
26503
|
+
}
|
|
26504
|
+
return current.version;
|
|
26505
|
+
}
|
|
26218
26506
|
if (shouldRegisterTool("create_task")) {
|
|
26219
26507
|
server.tool("create_task", "Create a new task in a project. Pass short_id=null to auto-generate.", {
|
|
26220
26508
|
title: exports_external2.string().describe("Task title"),
|
|
26221
26509
|
description: exports_external2.string().optional().describe("Task description (markdown)"),
|
|
26222
|
-
status: exports_external2.enum(["pending", "in_progress", "completed", "cancelled"]).optional().describe("Initial status (default: pending)"),
|
|
26223
|
-
priority: exports_external2.enum(["low", "medium", "high", "
|
|
26510
|
+
status: exports_external2.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional().describe("Initial status (default: pending)"),
|
|
26511
|
+
priority: exports_external2.enum(["low", "medium", "high", "critical"]).optional().describe("Priority (default: medium)"),
|
|
26224
26512
|
project_id: exports_external2.string().optional().describe("Project ID"),
|
|
26225
26513
|
task_list_id: exports_external2.string().optional().describe("Task list ID"),
|
|
26226
26514
|
assigned_to: exports_external2.string().optional().describe("Agent ID or name to assign to"),
|
|
@@ -26250,9 +26538,9 @@ function registerTaskCrudTools(server, ctx) {
|
|
|
26250
26538
|
if (confidence !== undefined)
|
|
26251
26539
|
resolved.confidence = confidence;
|
|
26252
26540
|
if (retry_count !== undefined)
|
|
26253
|
-
resolved.
|
|
26541
|
+
resolved.max_retries = retry_count;
|
|
26254
26542
|
if (deadline)
|
|
26255
|
-
resolved.
|
|
26543
|
+
resolved.due_at = deadline;
|
|
26256
26544
|
const task = createTask(resolved);
|
|
26257
26545
|
return { content: [{ type: "text", text: formatTask(task) }] };
|
|
26258
26546
|
} catch (e) {
|
|
@@ -26262,8 +26550,8 @@ function registerTaskCrudTools(server, ctx) {
|
|
|
26262
26550
|
}
|
|
26263
26551
|
if (shouldRegisterTool("list_tasks")) {
|
|
26264
26552
|
server.tool("list_tasks", "List tasks with optional filters. Pass empty arrays for multi-value filters (e.g. status=[] shows all).", {
|
|
26265
|
-
status: exports_external2.union([exports_external2.enum(["pending", "in_progress", "completed", "cancelled"]), exports_external2.array(exports_external2.enum(["pending", "in_progress", "completed", "cancelled"]))]).optional().describe("Filter by status"),
|
|
26266
|
-
priority: exports_external2.union([exports_external2.enum(["low", "medium", "high", "
|
|
26553
|
+
status: exports_external2.union([exports_external2.enum(["pending", "in_progress", "completed", "failed", "cancelled"]), exports_external2.array(exports_external2.enum(["pending", "in_progress", "completed", "failed", "cancelled"]))]).optional().describe("Filter by status"),
|
|
26554
|
+
priority: exports_external2.union([exports_external2.enum(["low", "medium", "high", "critical"]), exports_external2.array(exports_external2.enum(["low", "medium", "high", "critical"]))]).optional().describe("Filter by priority"),
|
|
26267
26555
|
project_id: exports_external2.string().optional().describe("Filter by project"),
|
|
26268
26556
|
task_list_id: exports_external2.string().optional().describe("Filter by task list"),
|
|
26269
26557
|
assigned_to: exports_external2.string().optional().describe("Filter by assignee (agent ID or name, empty string = unassigned)"),
|
|
@@ -26298,9 +26586,9 @@ function registerTaskCrudTools(server, ctx) {
|
|
|
26298
26586
|
}, async ({ task_id }) => {
|
|
26299
26587
|
try {
|
|
26300
26588
|
const resolvedId = resolveId(task_id);
|
|
26301
|
-
const task =
|
|
26589
|
+
const task = getTask(resolvedId);
|
|
26302
26590
|
if (!task)
|
|
26303
|
-
throw new
|
|
26591
|
+
throw new TaskNotFoundError(task_id);
|
|
26304
26592
|
const focus = ctx.getAgentFocus(task.assigned_to || "");
|
|
26305
26593
|
const lines = [
|
|
26306
26594
|
`ID: ${task.id}`,
|
|
@@ -26314,7 +26602,7 @@ function registerTaskCrudTools(server, ctx) {
|
|
|
26314
26602
|
task.estimated_minutes != null ? `Estimate: ${task.estimated_minutes} min` : null,
|
|
26315
26603
|
task.actual_minutes != null ? `Actual: ${task.actual_minutes} min` : null,
|
|
26316
26604
|
task.confidence != null ? `Confidence: ${task.confidence}` : null,
|
|
26317
|
-
task.
|
|
26605
|
+
task.due_at ? `Due: ${task.due_at}` : null,
|
|
26318
26606
|
task.completed_at ? `Completed: ${task.completed_at}` : null,
|
|
26319
26607
|
focus ? `Focus: agent=${focus.agent_id} project=${focus.project_id || "(global)"}` : null,
|
|
26320
26608
|
task.created_at ? `Created: ${task.created_at}` : null,
|
|
@@ -26336,8 +26624,8 @@ ${task.description}` : null
|
|
|
26336
26624
|
task_id: exports_external2.string().describe("Task ID"),
|
|
26337
26625
|
title: exports_external2.string().optional(),
|
|
26338
26626
|
description: exports_external2.string().optional(),
|
|
26339
|
-
status: exports_external2.enum(["pending", "in_progress", "completed", "cancelled"]).optional(),
|
|
26340
|
-
priority: exports_external2.enum(["low", "medium", "high", "
|
|
26627
|
+
status: exports_external2.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
|
|
26628
|
+
priority: exports_external2.enum(["low", "medium", "high", "critical"]).optional(),
|
|
26341
26629
|
assigned_to: exports_external2.string().nullable().optional().describe("Agent ID or name, null to unassign"),
|
|
26342
26630
|
project_id: exports_external2.string().nullable().optional(),
|
|
26343
26631
|
task_list_id: exports_external2.string().nullable().optional(),
|
|
@@ -26366,9 +26654,15 @@ ${task.description}` : null
|
|
|
26366
26654
|
resolved.task_list_id = resolveId(resolved.task_list_id, "task_lists");
|
|
26367
26655
|
if (resolved.depends_on && Array.isArray(resolved.depends_on))
|
|
26368
26656
|
resolved.depends_on = resolved.depends_on.map(resolveId);
|
|
26369
|
-
if (resolved.estimate !== undefined)
|
|
26657
|
+
if (resolved.estimate !== undefined) {
|
|
26370
26658
|
resolved.estimated_minutes = resolved.estimate;
|
|
26371
|
-
|
|
26659
|
+
delete resolved.estimate;
|
|
26660
|
+
}
|
|
26661
|
+
if (resolved.deadline !== undefined) {
|
|
26662
|
+
resolved.due_at = resolved.deadline;
|
|
26663
|
+
delete resolved.deadline;
|
|
26664
|
+
}
|
|
26665
|
+
const task = updateTask(resolvedId, { ...resolved, version: versionFor(resolvedId, version) });
|
|
26372
26666
|
return { content: [{ type: "text", text: formatTask(task) }] };
|
|
26373
26667
|
} catch (e) {
|
|
26374
26668
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -26393,11 +26687,24 @@ ${task.description}` : null
|
|
|
26393
26687
|
var init_task_crud2 = __esm(() => {
|
|
26394
26688
|
init_zod2();
|
|
26395
26689
|
init_tasks();
|
|
26690
|
+
init_types();
|
|
26396
26691
|
});
|
|
26397
26692
|
|
|
26398
26693
|
// src/mcp/tools/task-project-tools.ts
|
|
26399
26694
|
function registerTaskProjectTools(server, ctx) {
|
|
26400
26695
|
const { shouldRegisterTool, resolveId, formatError, formatTask } = ctx;
|
|
26696
|
+
function versionFor(taskId, version) {
|
|
26697
|
+
const current = getTask(taskId);
|
|
26698
|
+
if (!current)
|
|
26699
|
+
throw new TaskNotFoundError(taskId);
|
|
26700
|
+
if (version !== undefined && current.version !== version) {
|
|
26701
|
+
throw new VersionConflictError(taskId, version, current.version);
|
|
26702
|
+
}
|
|
26703
|
+
return current.version;
|
|
26704
|
+
}
|
|
26705
|
+
function updateWithOptionalVersion(taskId, updates, version) {
|
|
26706
|
+
return updateTask(taskId, { ...updates, version: versionFor(taskId, version) });
|
|
26707
|
+
}
|
|
26401
26708
|
if (shouldRegisterTool("start_task")) {
|
|
26402
26709
|
server.tool("start_task", "Mark a task as in_progress. Uses optimistic locking via version if provided.", {
|
|
26403
26710
|
task_id: exports_external2.string().describe("Task ID"),
|
|
@@ -26405,7 +26712,12 @@ function registerTaskProjectTools(server, ctx) {
|
|
|
26405
26712
|
}, async ({ task_id, version }) => {
|
|
26406
26713
|
try {
|
|
26407
26714
|
const resolvedId = resolveId(task_id);
|
|
26408
|
-
|
|
26715
|
+
if (version !== undefined)
|
|
26716
|
+
versionFor(resolvedId, version);
|
|
26717
|
+
const current = getTask(resolvedId);
|
|
26718
|
+
if (!current)
|
|
26719
|
+
throw new TaskNotFoundError(resolvedId);
|
|
26720
|
+
const task = startTask(resolvedId, current.assigned_to || current.agent_id || "mcp");
|
|
26409
26721
|
return { content: [{ type: "text", text: formatTask(task) }] };
|
|
26410
26722
|
} catch (e) {
|
|
26411
26723
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -26421,7 +26733,12 @@ function registerTaskProjectTools(server, ctx) {
|
|
|
26421
26733
|
}, async ({ task_id, confidence, completed_at, version }) => {
|
|
26422
26734
|
try {
|
|
26423
26735
|
const resolvedId = resolveId(task_id);
|
|
26424
|
-
|
|
26736
|
+
if (version !== undefined)
|
|
26737
|
+
versionFor(resolvedId, version);
|
|
26738
|
+
const current = getTask(resolvedId);
|
|
26739
|
+
if (!current)
|
|
26740
|
+
throw new TaskNotFoundError(resolvedId);
|
|
26741
|
+
const task = completeTask(resolvedId, current.assigned_to || current.agent_id || undefined, undefined, { confidence, completed_at });
|
|
26425
26742
|
return { content: [{ type: "text", text: formatTask(task) }] };
|
|
26426
26743
|
} catch (e) {
|
|
26427
26744
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -26435,7 +26752,7 @@ function registerTaskProjectTools(server, ctx) {
|
|
|
26435
26752
|
}, async ({ task_id, version }) => {
|
|
26436
26753
|
try {
|
|
26437
26754
|
const resolvedId = resolveId(task_id);
|
|
26438
|
-
const task =
|
|
26755
|
+
const task = version === undefined ? setTaskStatus(resolvedId, "cancelled") : updateWithOptionalVersion(resolvedId, { status: "cancelled" }, version);
|
|
26439
26756
|
return { content: [{ type: "text", text: formatTask(task) }] };
|
|
26440
26757
|
} catch (e) {
|
|
26441
26758
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -26451,7 +26768,7 @@ function registerTaskProjectTools(server, ctx) {
|
|
|
26451
26768
|
try {
|
|
26452
26769
|
const resolvedId = resolveId(task_id);
|
|
26453
26770
|
const resolvedAssignee = resolveId(new_assignee, "agents");
|
|
26454
|
-
const task =
|
|
26771
|
+
const task = updateWithOptionalVersion(resolvedId, { assigned_to: resolvedAssignee }, version);
|
|
26455
26772
|
return { content: [{ type: "text", text: formatTask(task) }] };
|
|
26456
26773
|
} catch (e) {
|
|
26457
26774
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -26466,7 +26783,7 @@ function registerTaskProjectTools(server, ctx) {
|
|
|
26466
26783
|
}, async ({ task_id, deadline, version }) => {
|
|
26467
26784
|
try {
|
|
26468
26785
|
const resolvedId = resolveId(task_id);
|
|
26469
|
-
const task =
|
|
26786
|
+
const task = updateWithOptionalVersion(resolvedId, { due_at: deadline }, version);
|
|
26470
26787
|
return { content: [{ type: "text", text: formatTask(task) }] };
|
|
26471
26788
|
} catch (e) {
|
|
26472
26789
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -26476,12 +26793,12 @@ function registerTaskProjectTools(server, ctx) {
|
|
|
26476
26793
|
if (shouldRegisterTool("prioritize_task")) {
|
|
26477
26794
|
server.tool("prioritize_task", "Set a task's priority.", {
|
|
26478
26795
|
task_id: exports_external2.string().describe("Task ID"),
|
|
26479
|
-
priority: exports_external2.enum(["low", "medium", "high", "
|
|
26796
|
+
priority: exports_external2.enum(["low", "medium", "high", "critical"]).describe("New priority"),
|
|
26480
26797
|
version: exports_external2.number().optional().describe("Expected version for optimistic locking")
|
|
26481
26798
|
}, async ({ task_id, priority, version }) => {
|
|
26482
26799
|
try {
|
|
26483
26800
|
const resolvedId = resolveId(task_id);
|
|
26484
|
-
const task =
|
|
26801
|
+
const task = version === undefined ? setTaskPriority(resolvedId, priority) : updateWithOptionalVersion(resolvedId, { priority }, version);
|
|
26485
26802
|
return { content: [{ type: "text", text: formatTask(task) }] };
|
|
26486
26803
|
} catch (e) {
|
|
26487
26804
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -26537,18 +26854,18 @@ function registerTaskProjectTools(server, ctx) {
|
|
|
26537
26854
|
if (shouldRegisterTool("bulk_update_tasks")) {
|
|
26538
26855
|
server.tool("bulk_update_tasks", "Update multiple tasks at once. All tasks must pass the dependency check.", {
|
|
26539
26856
|
task_ids: exports_external2.array(exports_external2.string()).describe("Array of task IDs to update"),
|
|
26540
|
-
status: exports_external2.enum(["pending", "in_progress", "completed", "cancelled"]).optional(),
|
|
26541
|
-
priority: exports_external2.enum(["low", "medium", "high", "
|
|
26857
|
+
status: exports_external2.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
|
|
26858
|
+
priority: exports_external2.enum(["low", "medium", "high", "critical"]).optional(),
|
|
26542
26859
|
assigned_to: exports_external2.string().nullable().optional().describe("Agent ID or name, null to unassign")
|
|
26543
26860
|
}, async ({ task_ids, status, priority, assigned_to }) => {
|
|
26544
26861
|
try {
|
|
26545
|
-
const { bulkUpdateTasks: bulkUpdateTasks2 } = (()
|
|
26862
|
+
const { bulkUpdateTasks: bulkUpdateTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
26546
26863
|
const resolved = task_ids.map(resolveId);
|
|
26547
26864
|
let resolvedAssignee = assigned_to;
|
|
26548
26865
|
if (resolvedAssignee && typeof resolvedAssignee === "string")
|
|
26549
26866
|
resolvedAssignee = resolveId(resolvedAssignee, "agents");
|
|
26550
26867
|
const result = bulkUpdateTasks2(resolved, { status, priority, assigned_to: resolvedAssignee });
|
|
26551
|
-
return { content: [{ type: "text", text: `${result.updated} task(s) updated, ${result.
|
|
26868
|
+
return { content: [{ type: "text", text: `${result.updated} task(s) updated, ${result.failed.length} failed.` }] };
|
|
26552
26869
|
} catch (e) {
|
|
26553
26870
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
26554
26871
|
}
|
|
@@ -26559,8 +26876,8 @@ function registerTaskProjectTools(server, ctx) {
|
|
|
26559
26876
|
tasks: exports_external2.array(exports_external2.object({
|
|
26560
26877
|
title: exports_external2.string(),
|
|
26561
26878
|
description: exports_external2.string().optional(),
|
|
26562
|
-
status: exports_external2.enum(["pending", "in_progress", "completed", "cancelled"]).optional(),
|
|
26563
|
-
priority: exports_external2.enum(["low", "medium", "high", "
|
|
26879
|
+
status: exports_external2.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
|
|
26880
|
+
priority: exports_external2.enum(["low", "medium", "high", "critical"]).optional(),
|
|
26564
26881
|
project_id: exports_external2.string().optional(),
|
|
26565
26882
|
task_list_id: exports_external2.string().optional(),
|
|
26566
26883
|
assigned_to: exports_external2.string().optional(),
|
|
@@ -26571,7 +26888,7 @@ function registerTaskProjectTools(server, ctx) {
|
|
|
26571
26888
|
})).describe("Array of task objects")
|
|
26572
26889
|
}, async ({ tasks }) => {
|
|
26573
26890
|
try {
|
|
26574
|
-
const { bulkCreateTasks: bulkCreateTasks2 } = (()
|
|
26891
|
+
const { bulkCreateTasks: bulkCreateTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
26575
26892
|
const resolved = tasks.map((t) => {
|
|
26576
26893
|
const r = { ...t };
|
|
26577
26894
|
if (r.project_id)
|
|
@@ -26585,7 +26902,7 @@ function registerTaskProjectTools(server, ctx) {
|
|
|
26585
26902
|
return r;
|
|
26586
26903
|
});
|
|
26587
26904
|
const result = bulkCreateTasks2(resolved);
|
|
26588
|
-
return { content: [{ type: "text", text: `${result.created} task(s) created
|
|
26905
|
+
return { content: [{ type: "text", text: `${result.created.length} task(s) created.` }] };
|
|
26589
26906
|
} catch (e) {
|
|
26590
26907
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
26591
26908
|
}
|
|
@@ -26597,9 +26914,9 @@ function registerTaskProjectTools(server, ctx) {
|
|
|
26597
26914
|
force: exports_external2.boolean().optional().describe("Skip child check for all tasks (dangerous)")
|
|
26598
26915
|
}, async ({ task_ids, force }) => {
|
|
26599
26916
|
try {
|
|
26600
|
-
const { bulkDeleteTasks } = (()
|
|
26917
|
+
const { bulkDeleteTasks: bulkDeleteTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
26601
26918
|
const resolved = task_ids.map(resolveId);
|
|
26602
|
-
const result =
|
|
26919
|
+
const result = bulkDeleteTasks2(resolved, force);
|
|
26603
26920
|
return { content: [{ type: "text", text: `${result.deleted} task(s) deleted, ${result.skipped} skipped (has children).` }] };
|
|
26604
26921
|
} catch (e) {
|
|
26605
26922
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -26648,7 +26965,7 @@ function registerTaskProjectTools(server, ctx) {
|
|
|
26648
26965
|
const project = getProject(resolvedId);
|
|
26649
26966
|
if (!project)
|
|
26650
26967
|
throw new TaskNotFoundError(`Project not found: ${project_id}`);
|
|
26651
|
-
const { listTasks: listTasks3 } = (()
|
|
26968
|
+
const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
26652
26969
|
const tasks = listTasks3({ project_id: resolvedId, limit: 100 }, undefined);
|
|
26653
26970
|
const lines = [
|
|
26654
26971
|
`ID: ${project.id}`,
|
|
@@ -26749,7 +27066,7 @@ function registerTaskProjectTools(server, ctx) {
|
|
|
26749
27066
|
throw new TaskNotFoundError(`Task list not found: ${task_list_id}`);
|
|
26750
27067
|
let tasks = [];
|
|
26751
27068
|
if (include_tasks) {
|
|
26752
|
-
const { listTasks: listTasks3 } = (()
|
|
27069
|
+
const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
26753
27070
|
tasks = listTasks3({ task_list_id: resolvedId, limit: 200 }, undefined);
|
|
26754
27071
|
}
|
|
26755
27072
|
const lines = [
|
|
@@ -26849,7 +27166,7 @@ Tasks:` : null,
|
|
|
26849
27166
|
throw new TaskNotFoundError(`Plan not found: ${plan_id}`);
|
|
26850
27167
|
let tasks = [];
|
|
26851
27168
|
if (include_tasks) {
|
|
26852
|
-
const { listTasks: listTasks3 } = (()
|
|
27169
|
+
const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
26853
27170
|
tasks = listTasks3({ plan_id: resolvedId, limit: 200 }, undefined);
|
|
26854
27171
|
}
|
|
26855
27172
|
const lines = [
|
|
@@ -26938,7 +27255,7 @@ Tasks:` : null,
|
|
|
26938
27255
|
const tag = getTag(tag_id);
|
|
26939
27256
|
if (!tag)
|
|
26940
27257
|
throw new TaskNotFoundError(`Tag not found: ${tag_id}`);
|
|
26941
|
-
const { listTasks: listTasks3 } = (()
|
|
27258
|
+
const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
26942
27259
|
const tasks = listTasks3({ tags: [tag.name], limit: 100 }, undefined);
|
|
26943
27260
|
const lines = [
|
|
26944
27261
|
`Tag: ${tag.name}${tag.color ? ` (${tag.color})` : ""}`,
|
|
@@ -27017,7 +27334,7 @@ Tasks:` : null,
|
|
|
27017
27334
|
const label = getLabel(label_id);
|
|
27018
27335
|
if (!label)
|
|
27019
27336
|
throw new TaskNotFoundError(`Label not found: ${label_id}`);
|
|
27020
|
-
const { listTasks: listTasks3 } = (()
|
|
27337
|
+
const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27021
27338
|
const tasks = listTasks3({ tags: [label.name], limit: 100 }, undefined);
|
|
27022
27339
|
const lines = [
|
|
27023
27340
|
`Label: ${label.name}${label.color ? ` (${label.color})` : ""}`,
|
|
@@ -27068,7 +27385,7 @@ Tasks:` : null,
|
|
|
27068
27385
|
try {
|
|
27069
27386
|
const resolvedId = resolveId(task_id);
|
|
27070
27387
|
const resolvedAuthor = author ? resolveId(author, "agents") : undefined;
|
|
27071
|
-
const comment = addComment({ task_id: resolvedId, body,
|
|
27388
|
+
const comment = addComment({ task_id: resolvedId, content: body, agent_id: resolvedAuthor });
|
|
27072
27389
|
return { content: [{ type: "text", text: `Comment added to ${task_id.slice(0, 8)}: ${body.slice(0, 50)}${body.length > 50 ? "..." : ""}` }] };
|
|
27073
27390
|
} catch (e) {
|
|
27074
27391
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -27084,8 +27401,8 @@ Tasks:` : null,
|
|
|
27084
27401
|
const comments = listComments(resolvedId);
|
|
27085
27402
|
if (comments.length === 0)
|
|
27086
27403
|
return { content: [{ type: "text", text: "No comments." }] };
|
|
27087
|
-
const lines = comments.map((c) => `[${c.
|
|
27088
|
-
${c.
|
|
27404
|
+
const lines = comments.map((c) => `[${c.agent_id || "unknown"}] ${c.created_at?.slice(0, 16)}:
|
|
27405
|
+
${c.content}`);
|
|
27089
27406
|
return { content: [{ type: "text", text: lines.join(`
|
|
27090
27407
|
|
|
27091
27408
|
`) }] };
|
|
@@ -27100,7 +27417,7 @@ Tasks:` : null,
|
|
|
27100
27417
|
body: exports_external2.string().describe("New comment body")
|
|
27101
27418
|
}, async ({ comment_id, body }) => {
|
|
27102
27419
|
try {
|
|
27103
|
-
const comment = updateComment(comment_id, { body });
|
|
27420
|
+
const comment = updateComment(comment_id, { content: body });
|
|
27104
27421
|
return { content: [{ type: "text", text: `Comment ${comment_id.slice(0, 8)} updated.` }] };
|
|
27105
27422
|
} catch (e) {
|
|
27106
27423
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -27123,11 +27440,11 @@ Tasks:` : null,
|
|
|
27123
27440
|
server.tool("search_tasks", "Full-text search across task titles and descriptions.", {
|
|
27124
27441
|
query: exports_external2.string().describe("Search query"),
|
|
27125
27442
|
project_id: exports_external2.string().optional().describe("Filter by project"),
|
|
27126
|
-
status: exports_external2.enum(["pending", "in_progress", "completed", "cancelled"]).optional(),
|
|
27443
|
+
status: exports_external2.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
|
|
27127
27444
|
limit: exports_external2.number().optional().describe("Max results (default: 20)")
|
|
27128
27445
|
}, async ({ query, project_id, status, limit }) => {
|
|
27129
27446
|
try {
|
|
27130
|
-
const { searchTasks: searchTasks2 } = (()
|
|
27447
|
+
const { searchTasks: searchTasks2 } = (init_search(), __toCommonJS(exports_search));
|
|
27131
27448
|
const resolved = { query, limit };
|
|
27132
27449
|
if (project_id)
|
|
27133
27450
|
resolved.project_id = resolveId(project_id, "projects");
|
|
@@ -27145,29 +27462,6 @@ ${lines.join(`
|
|
|
27145
27462
|
}
|
|
27146
27463
|
});
|
|
27147
27464
|
}
|
|
27148
|
-
if (shouldRegisterTool("sync")) {
|
|
27149
|
-
server.tool("sync", "Sync tasks from a GitHub PR or external source into the project.", {
|
|
27150
|
-
source: exports_external2.enum(["github_pr", "linear", "asana"]).describe("Source type"),
|
|
27151
|
-
source_id: exports_external2.string().describe("PR number, Linear issue ID, or Asana task ID"),
|
|
27152
|
-
project_id: exports_external2.string().optional().describe("Project ID to import into"),
|
|
27153
|
-
options: exports_external2.record(exports_external2.unknown()).optional().describe("Source-specific options")
|
|
27154
|
-
}, async ({ source, source_id, project_id, options }) => {
|
|
27155
|
-
try {
|
|
27156
|
-
const { syncFromGithubPR, syncFromLinear, syncFromAsana } = (()=>{throw new Error("Cannot require module "+"../lib/sync.js");})();
|
|
27157
|
-
let result;
|
|
27158
|
-
if (source === "github_pr") {
|
|
27159
|
-
result = await syncFromGithubPR({ prNumber: parseInt(source_id), project_id: project_id ? resolveId(project_id, "projects") : undefined, ...options });
|
|
27160
|
-
} else if (source === "linear") {
|
|
27161
|
-
result = await syncFromLinear({ issueId: source_id, project_id: project_id ? resolveId(project_id, "projects") : undefined, ...options });
|
|
27162
|
-
} else {
|
|
27163
|
-
result = await syncFromAsana({ taskId: source_id, project_id: project_id ? resolveId(project_id, "projects") : undefined, ...options });
|
|
27164
|
-
}
|
|
27165
|
-
return { content: [{ type: "text", text: `Synced from ${source}: ${result.task?.title || source_id}` }] };
|
|
27166
|
-
} catch (e) {
|
|
27167
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
27168
|
-
}
|
|
27169
|
-
});
|
|
27170
|
-
}
|
|
27171
27465
|
}
|
|
27172
27466
|
var init_task_project_tools = __esm(() => {
|
|
27173
27467
|
init_zod2();
|
|
@@ -27182,6 +27476,16 @@ var init_task_project_tools = __esm(() => {
|
|
|
27182
27476
|
// src/mcp/tools/task-workflow-tools.ts
|
|
27183
27477
|
function registerTaskWorkflowTools(server, ctx) {
|
|
27184
27478
|
const { shouldRegisterTool, resolveId, formatError, formatTask } = ctx;
|
|
27479
|
+
function versionFor(taskId, version) {
|
|
27480
|
+
const { getTask: getTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27481
|
+
const current = getTask2(taskId);
|
|
27482
|
+
if (!current)
|
|
27483
|
+
throw new TaskNotFoundError(taskId);
|
|
27484
|
+
if (version !== undefined && current.version !== version) {
|
|
27485
|
+
throw new VersionConflictError(taskId, version, current.version);
|
|
27486
|
+
}
|
|
27487
|
+
return current.version;
|
|
27488
|
+
}
|
|
27185
27489
|
if (shouldRegisterTool("approve_task")) {
|
|
27186
27490
|
server.tool("approve_task", "Approve a task that requires_approval. Records who approved it.", {
|
|
27187
27491
|
task_id: exports_external2.string().describe("Task ID"),
|
|
@@ -27190,13 +27494,14 @@ function registerTaskWorkflowTools(server, ctx) {
|
|
|
27190
27494
|
version: exports_external2.number().optional().describe("Expected version for optimistic locking")
|
|
27191
27495
|
}, async ({ task_id, approved_by, notes, version }) => {
|
|
27192
27496
|
try {
|
|
27193
|
-
const { updateTask: updateTask2 } = (()
|
|
27497
|
+
const { updateTask: updateTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27194
27498
|
const resolvedId = resolveId(task_id);
|
|
27195
27499
|
const resolvedApprover = approved_by ? resolveId(approved_by, "agents") : undefined;
|
|
27196
27500
|
const task = updateTask2(resolvedId, {
|
|
27197
27501
|
approved_by: resolvedApprover,
|
|
27198
|
-
metadata: notes ? { approval_notes: notes } : {}
|
|
27199
|
-
|
|
27502
|
+
metadata: notes ? { approval_notes: notes } : {},
|
|
27503
|
+
version: versionFor(resolvedId, version)
|
|
27504
|
+
});
|
|
27200
27505
|
return { content: [{ type: "text", text: `Task ${task_id.slice(0, 8)} approved by ${resolvedApprover || "unknown"}` }] };
|
|
27201
27506
|
} catch (e) {
|
|
27202
27507
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -27211,15 +27516,11 @@ function registerTaskWorkflowTools(server, ctx) {
|
|
|
27211
27516
|
version: exports_external2.number().optional().describe("Expected version for optimistic locking")
|
|
27212
27517
|
}, async ({ task_id, reason, agent_id, version }) => {
|
|
27213
27518
|
try {
|
|
27214
|
-
const {
|
|
27519
|
+
const { failTask: failTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27215
27520
|
const resolvedId = resolveId(task_id);
|
|
27216
|
-
|
|
27217
|
-
|
|
27218
|
-
|
|
27219
|
-
back_on_board: false,
|
|
27220
|
-
metadata: reason ? { failure_reason: reason } : {}
|
|
27221
|
-
}, version);
|
|
27222
|
-
return { content: [{ type: "text", text: `Task ${task_id.slice(0, 8)} marked failed. Retry count: ${task.retry_count}` }] };
|
|
27521
|
+
versionFor(resolvedId, version);
|
|
27522
|
+
const result = failTask2(resolvedId, agent_id, reason);
|
|
27523
|
+
return { content: [{ type: "text", text: `Task ${task_id.slice(0, 8)} marked failed. Retry count: ${result.task.retry_count}` }] };
|
|
27223
27524
|
} catch (e) {
|
|
27224
27525
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
27225
27526
|
}
|
|
@@ -27228,12 +27529,12 @@ function registerTaskWorkflowTools(server, ctx) {
|
|
|
27228
27529
|
if (shouldRegisterTool("get_my_tasks")) {
|
|
27229
27530
|
server.tool("get_my_tasks", "Get tasks assigned to the calling agent. Supports focus mode scoping.", {
|
|
27230
27531
|
agent_id: exports_external2.string().optional().describe("Agent ID (defaults to context agent)"),
|
|
27231
|
-
status: exports_external2.enum(["pending", "in_progress", "completed", "cancelled"]).optional().describe("Filter by status"),
|
|
27532
|
+
status: exports_external2.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional().describe("Filter by status"),
|
|
27232
27533
|
project_id: exports_external2.string().optional().describe("Filter by project (respects focus mode)"),
|
|
27233
27534
|
limit: exports_external2.number().optional().describe("Max results (default: 50)")
|
|
27234
27535
|
}, async ({ agent_id, status, project_id, limit }) => {
|
|
27235
27536
|
try {
|
|
27236
|
-
const { listTasks: listTasks3 } = (()
|
|
27537
|
+
const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27237
27538
|
const focus = ctx.getAgentFocus(agent_id || "");
|
|
27238
27539
|
const effectiveAgentId = focus ? focus.agent_id : agent_id || "";
|
|
27239
27540
|
const effectiveProjectId = focus?.project_id || project_id;
|
|
@@ -27253,6 +27554,122 @@ function registerTaskWorkflowTools(server, ctx) {
|
|
|
27253
27554
|
}
|
|
27254
27555
|
});
|
|
27255
27556
|
}
|
|
27557
|
+
if (shouldRegisterTool("get_next_task")) {
|
|
27558
|
+
server.tool("get_next_task", "Get the best available pending task without claiming it.", {
|
|
27559
|
+
agent_id: exports_external2.string().optional().describe("Agent ID or name for assignment affinity"),
|
|
27560
|
+
project_id: exports_external2.string().optional().describe("Filter by project"),
|
|
27561
|
+
task_list_id: exports_external2.string().optional().describe("Filter by task list"),
|
|
27562
|
+
plan_id: exports_external2.string().optional().describe("Filter by plan"),
|
|
27563
|
+
tags: exports_external2.array(exports_external2.string()).optional().describe("Filter by tags")
|
|
27564
|
+
}, async ({ agent_id, project_id, task_list_id, plan_id, tags }) => {
|
|
27565
|
+
try {
|
|
27566
|
+
const { getNextTask: getNextTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27567
|
+
const filters = {};
|
|
27568
|
+
if (project_id)
|
|
27569
|
+
filters.project_id = resolveId(project_id, "projects");
|
|
27570
|
+
if (task_list_id)
|
|
27571
|
+
filters.task_list_id = resolveId(task_list_id, "task_lists");
|
|
27572
|
+
if (plan_id)
|
|
27573
|
+
filters.plan_id = resolveId(plan_id, "plans");
|
|
27574
|
+
if (tags)
|
|
27575
|
+
filters.tags = tags;
|
|
27576
|
+
const task = getNextTask2(agent_id, filters);
|
|
27577
|
+
return { content: [{ type: "text", text: task ? formatTask(task) : "No available task." }] };
|
|
27578
|
+
} catch (e) {
|
|
27579
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
27580
|
+
}
|
|
27581
|
+
});
|
|
27582
|
+
}
|
|
27583
|
+
if (shouldRegisterTool("claim_next_task")) {
|
|
27584
|
+
server.tool("claim_next_task", "Atomically claim and start the best available pending task for an agent.", {
|
|
27585
|
+
agent_id: exports_external2.string().describe("Agent ID or name claiming the task"),
|
|
27586
|
+
project_id: exports_external2.string().optional().describe("Filter by project"),
|
|
27587
|
+
task_list_id: exports_external2.string().optional().describe("Filter by task list"),
|
|
27588
|
+
plan_id: exports_external2.string().optional().describe("Filter by plan"),
|
|
27589
|
+
tags: exports_external2.array(exports_external2.string()).optional().describe("Filter by tags")
|
|
27590
|
+
}, async ({ agent_id, project_id, task_list_id, plan_id, tags }) => {
|
|
27591
|
+
try {
|
|
27592
|
+
const { claimNextTask: claimNextTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27593
|
+
const filters = {};
|
|
27594
|
+
if (project_id)
|
|
27595
|
+
filters.project_id = resolveId(project_id, "projects");
|
|
27596
|
+
if (task_list_id)
|
|
27597
|
+
filters.task_list_id = resolveId(task_list_id, "task_lists");
|
|
27598
|
+
if (plan_id)
|
|
27599
|
+
filters.plan_id = resolveId(plan_id, "plans");
|
|
27600
|
+
if (tags)
|
|
27601
|
+
filters.tags = tags;
|
|
27602
|
+
const task = claimNextTask2(agent_id, filters);
|
|
27603
|
+
return { content: [{ type: "text", text: task ? formatTask(task) : "No available task to claim." }] };
|
|
27604
|
+
} catch (e) {
|
|
27605
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
27606
|
+
}
|
|
27607
|
+
});
|
|
27608
|
+
}
|
|
27609
|
+
if (shouldRegisterTool("get_tasks_changed_since")) {
|
|
27610
|
+
server.tool("get_tasks_changed_since", "List tasks changed since an ISO timestamp.", {
|
|
27611
|
+
since: exports_external2.string().describe("ISO timestamp"),
|
|
27612
|
+
project_id: exports_external2.string().optional().describe("Filter by project"),
|
|
27613
|
+
task_list_id: exports_external2.string().optional().describe("Filter by task list"),
|
|
27614
|
+
limit: exports_external2.number().optional().describe("Maximum tasks to return")
|
|
27615
|
+
}, async ({ since, project_id, task_list_id, limit }) => {
|
|
27616
|
+
try {
|
|
27617
|
+
const { getTasksChangedSince: getTasksChangedSince2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27618
|
+
const filters = {};
|
|
27619
|
+
if (project_id)
|
|
27620
|
+
filters.project_id = resolveId(project_id, "projects");
|
|
27621
|
+
if (task_list_id)
|
|
27622
|
+
filters.task_list_id = resolveId(task_list_id, "task_lists");
|
|
27623
|
+
const tasks = getTasksChangedSince2(since, filters).slice(0, limit || 50);
|
|
27624
|
+
if (tasks.length === 0)
|
|
27625
|
+
return { content: [{ type: "text", text: "No changed tasks." }] };
|
|
27626
|
+
return { content: [{ type: "text", text: tasks.map(formatTask).join(`
|
|
27627
|
+
`) }] };
|
|
27628
|
+
} catch (e) {
|
|
27629
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
27630
|
+
}
|
|
27631
|
+
});
|
|
27632
|
+
}
|
|
27633
|
+
function registerContextTool(name, description) {
|
|
27634
|
+
if (!shouldRegisterTool(name))
|
|
27635
|
+
return;
|
|
27636
|
+
server.tool(name, description, {
|
|
27637
|
+
agent_id: exports_external2.string().optional().describe("Agent ID or name"),
|
|
27638
|
+
project_id: exports_external2.string().optional().describe("Filter by project"),
|
|
27639
|
+
task_list_id: exports_external2.string().optional().describe("Filter by task list"),
|
|
27640
|
+
explain_blocked: exports_external2.boolean().optional().describe("Include blocked task details")
|
|
27641
|
+
}, async ({ agent_id, project_id, task_list_id, explain_blocked }) => {
|
|
27642
|
+
try {
|
|
27643
|
+
const { getStatus: getStatus2, getNextTask: getNextTask2, getOverdueTasks: getOverdueTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27644
|
+
const { getLatestHandoff: getLatestHandoff2 } = (init_handoffs(), __toCommonJS(exports_handoffs));
|
|
27645
|
+
const filters = {};
|
|
27646
|
+
if (project_id)
|
|
27647
|
+
filters.project_id = resolveId(project_id, "projects");
|
|
27648
|
+
if (task_list_id)
|
|
27649
|
+
filters.task_list_id = resolveId(task_list_id, "task_lists");
|
|
27650
|
+
const status = getStatus2(filters, agent_id, { explain_blocked });
|
|
27651
|
+
const next_task = getNextTask2(agent_id, filters);
|
|
27652
|
+
const overdue = getOverdueTasks2(filters.project_id);
|
|
27653
|
+
const latest_handoff = getLatestHandoff2(agent_id, filters.project_id);
|
|
27654
|
+
return {
|
|
27655
|
+
content: [{
|
|
27656
|
+
type: "text",
|
|
27657
|
+
text: JSON.stringify({
|
|
27658
|
+
status,
|
|
27659
|
+
next_task,
|
|
27660
|
+
overdue_count: overdue.length,
|
|
27661
|
+
latest_handoff,
|
|
27662
|
+
as_of: new Date().toISOString()
|
|
27663
|
+
}, null, 2)
|
|
27664
|
+
}]
|
|
27665
|
+
};
|
|
27666
|
+
} catch (e) {
|
|
27667
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
27668
|
+
}
|
|
27669
|
+
});
|
|
27670
|
+
}
|
|
27671
|
+
registerContextTool("get_context", "Get session start context: queue status, next task, overdue count, and latest handoff.");
|
|
27672
|
+
registerContextTool("bootstrap", "Bootstrap an agent session with queue context and the next available task.");
|
|
27256
27673
|
if (shouldRegisterTool("get_org_chart")) {
|
|
27257
27674
|
server.tool("get_org_chart", "Get the global org chart (agent hierarchy + titles).", {
|
|
27258
27675
|
format: exports_external2.enum(["text", "json"]).optional().describe("Output format (default: text)")
|
|
@@ -27273,7 +27690,7 @@ function registerTaskWorkflowTools(server, ctx) {
|
|
|
27273
27690
|
}).join(`
|
|
27274
27691
|
`);
|
|
27275
27692
|
};
|
|
27276
|
-
const { getOrgChart: getOrgChart2 } = (()
|
|
27693
|
+
const { getOrgChart: getOrgChart2 } = (init_agents(), __toCommonJS(exports_agents));
|
|
27277
27694
|
const tree = getOrgChart2();
|
|
27278
27695
|
if (format === "json") {
|
|
27279
27696
|
return { content: [{ type: "text", text: JSON.stringify(tree, null, 2) }] };
|
|
@@ -27293,10 +27710,10 @@ function registerTaskWorkflowTools(server, ctx) {
|
|
|
27293
27710
|
reports_to: exports_external2.string().describe("Manager agent ID or name")
|
|
27294
27711
|
}, async ({ agent_id, reports_to }) => {
|
|
27295
27712
|
try {
|
|
27296
|
-
const {
|
|
27713
|
+
const { updateAgent: updateAgent2 } = (init_agents(), __toCommonJS(exports_agents));
|
|
27297
27714
|
const resolvedAgent = resolveId(agent_id, "agents");
|
|
27298
27715
|
const resolvedManager = resolveId(reports_to, "agents");
|
|
27299
|
-
|
|
27716
|
+
updateAgent2(resolvedAgent, { reports_to: resolvedManager });
|
|
27300
27717
|
return { content: [{ type: "text", text: `${agent_id} now reports to ${reports_to}` }] };
|
|
27301
27718
|
} catch (e) {
|
|
27302
27719
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -27306,6 +27723,7 @@ function registerTaskWorkflowTools(server, ctx) {
|
|
|
27306
27723
|
}
|
|
27307
27724
|
var init_task_workflow_tools = __esm(() => {
|
|
27308
27725
|
init_zod2();
|
|
27726
|
+
init_types();
|
|
27309
27727
|
});
|
|
27310
27728
|
|
|
27311
27729
|
// src/mcp/tools/task-auto-tools.ts
|
|
@@ -27317,9 +27735,9 @@ function registerTaskAutoTools(server, ctx) {
|
|
|
27317
27735
|
project_id: exports_external2.string().optional().describe("Scope to a project")
|
|
27318
27736
|
}, async ({ days = 7, project_id }) => {
|
|
27319
27737
|
try {
|
|
27320
|
-
const { archiveCompletedTasks } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27738
|
+
const { archiveCompletedTasks: archiveCompletedTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27321
27739
|
const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
|
|
27322
|
-
const count =
|
|
27740
|
+
const count = archiveCompletedTasks2(days, resolvedProjectId);
|
|
27323
27741
|
return { content: [{ type: "text", text: `Archived ${count} completed task(s) older than ${days} days.` }] };
|
|
27324
27742
|
} catch (e) {
|
|
27325
27743
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -27345,9 +27763,9 @@ function registerTaskAutoTools(server, ctx) {
|
|
|
27345
27763
|
limit: exports_external2.number().optional().describe("Max results (default: 50)")
|
|
27346
27764
|
}, async ({ project_id, limit }) => {
|
|
27347
27765
|
try {
|
|
27348
|
-
const { getArchivedTasks } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27766
|
+
const { getArchivedTasks: getArchivedTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27349
27767
|
const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
|
|
27350
|
-
const tasks =
|
|
27768
|
+
const tasks = getArchivedTasks2({ project_id: resolvedProjectId, limit: limit || 50 });
|
|
27351
27769
|
if (tasks.length === 0)
|
|
27352
27770
|
return { content: [{ type: "text", text: "No archived tasks." }] };
|
|
27353
27771
|
const lines = tasks.map((t) => `${t.short_id || t.id.slice(0, 8)} ${t.title} archived ${t.archived_at}`);
|
|
@@ -27363,14 +27781,13 @@ function registerTaskAutoTools(server, ctx) {
|
|
|
27363
27781
|
task_id: exports_external2.string().describe("Task ID")
|
|
27364
27782
|
}, async ({ task_id }) => {
|
|
27365
27783
|
try {
|
|
27366
|
-
const { autoAssign } = (init_agents(), __toCommonJS(exports_agents));
|
|
27367
27784
|
const resolvedId = resolveId(task_id);
|
|
27368
|
-
const
|
|
27369
|
-
|
|
27785
|
+
const { autoAssignTask: autoAssignTask2 } = (init_auto_assign(), __toCommonJS(exports_auto_assign));
|
|
27786
|
+
const assignment = await autoAssignTask2(resolvedId);
|
|
27787
|
+
if (!assignment.assigned_to)
|
|
27370
27788
|
return { content: [{ type: "text", text: "No suitable agent found for this task." }] };
|
|
27371
|
-
const
|
|
27372
|
-
|
|
27373
|
-
return { content: [{ type: "text", text: `Task ${task_id.slice(0, 8)} assigned to ${assignment.agent_name} (score: ${assignment.score.toFixed(2)})` }] };
|
|
27789
|
+
const reason = assignment.reason ? ` \u2014 ${assignment.reason}` : "";
|
|
27790
|
+
return { content: [{ type: "text", text: `Task ${task_id.slice(0, 8)} assigned to ${assignment.agent_name || assignment.assigned_to} via ${assignment.method}${reason}` }] };
|
|
27374
27791
|
} catch (e) {
|
|
27375
27792
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
27376
27793
|
}
|
|
@@ -27381,10 +27798,23 @@ function registerTaskAutoTools(server, ctx) {
|
|
|
27381
27798
|
agent_id: exports_external2.string().optional().describe("Agent ID (defaults to context agent)")
|
|
27382
27799
|
}, async ({ agent_id }) => {
|
|
27383
27800
|
try {
|
|
27384
|
-
const {
|
|
27801
|
+
const { listTasks: listTasks3, getBlockedTasks: getBlockedTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27385
27802
|
const focus = ctx.getAgentFocus(agent_id || "");
|
|
27386
27803
|
const effectiveAgentId = focus ? focus.agent_id : agent_id || "";
|
|
27387
|
-
|
|
27804
|
+
if (!effectiveAgentId) {
|
|
27805
|
+
return { content: [{ type: "text", text: "No agent_id provided and no agent focus is active." }], isError: true };
|
|
27806
|
+
}
|
|
27807
|
+
const assigned = listTasks3({ assigned_to: effectiveAgentId, limit: 500 }, undefined);
|
|
27808
|
+
const now2 = Date.now();
|
|
27809
|
+
const dueSoonCutoff = now2 + 24 * 60 * 60 * 1000;
|
|
27810
|
+
const blocked = getBlockedTasks2().filter((t) => t.assigned_to === effectiveAgentId);
|
|
27811
|
+
const workload = {
|
|
27812
|
+
in_progress: assigned.filter((t) => t.status === "in_progress").length,
|
|
27813
|
+
pending: assigned.filter((t) => t.status === "pending").length,
|
|
27814
|
+
completed_recent: assigned.filter((t) => t.status === "completed" && t.completed_at && now2 - new Date(t.completed_at).getTime() <= 7 * 24 * 60 * 60 * 1000).length,
|
|
27815
|
+
due_soon: assigned.filter((t) => t.due_at && new Date(t.due_at).getTime() <= dueSoonCutoff && new Date(t.due_at).getTime() >= now2 && !["completed", "cancelled", "failed"].includes(t.status)).length,
|
|
27816
|
+
blocked: blocked.length
|
|
27817
|
+
};
|
|
27388
27818
|
const lines = [
|
|
27389
27819
|
`Agent: ${effectiveAgentId}`,
|
|
27390
27820
|
`In Progress: ${workload.in_progress}`,
|
|
@@ -27406,10 +27836,29 @@ function registerTaskAutoTools(server, ctx) {
|
|
|
27406
27836
|
max_per_agent: exports_external2.number().optional().describe("Max tasks per agent (default: 5)")
|
|
27407
27837
|
}, async ({ project_id, max_per_agent }) => {
|
|
27408
27838
|
try {
|
|
27409
|
-
const {
|
|
27839
|
+
const { listAgents: listAgents2 } = (init_agents(), __toCommonJS(exports_agents));
|
|
27840
|
+
const { listTasks: listTasks3, updateTask: updateTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27410
27841
|
const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
|
|
27411
|
-
const
|
|
27412
|
-
|
|
27842
|
+
const limit = max_per_agent || 5;
|
|
27843
|
+
const agents = listAgents2().filter((agent) => agent.status === "active");
|
|
27844
|
+
if (agents.length === 0)
|
|
27845
|
+
return { content: [{ type: "text", text: "No active agents available for rebalancing." }] };
|
|
27846
|
+
const activeTasks = listTasks3({ project_id: resolvedProjectId, status: ["pending", "in_progress"], limit: 1000 }, undefined);
|
|
27847
|
+
const load = new Map(agents.map((agent) => [agent.id, activeTasks.filter((t) => t.assigned_to === agent.id).length]));
|
|
27848
|
+
let moved = 0;
|
|
27849
|
+
let skipped = 0;
|
|
27850
|
+
for (const task of activeTasks.filter((t) => t.status === "pending" && t.assigned_to && (load.get(t.assigned_to) ?? 0) > limit)) {
|
|
27851
|
+
const target = agents.filter((agent) => agent.id !== task.assigned_to).sort((a, b) => (load.get(a.id) ?? 0) - (load.get(b.id) ?? 0))[0];
|
|
27852
|
+
if (!target || (load.get(target.id) ?? 0) >= limit) {
|
|
27853
|
+
skipped++;
|
|
27854
|
+
continue;
|
|
27855
|
+
}
|
|
27856
|
+
updateTask2(task.id, { assigned_to: target.id, version: task.version });
|
|
27857
|
+
load.set(task.assigned_to, (load.get(task.assigned_to) ?? 1) - 1);
|
|
27858
|
+
load.set(target.id, (load.get(target.id) ?? 0) + 1);
|
|
27859
|
+
moved++;
|
|
27860
|
+
}
|
|
27861
|
+
return { content: [{ type: "text", text: `Rebalanced: moved ${moved} task(s), ${skipped} skipped.` }] };
|
|
27413
27862
|
} catch (e) {
|
|
27414
27863
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
27415
27864
|
}
|
|
@@ -27422,13 +27871,13 @@ function registerTaskAutoTools(server, ctx) {
|
|
|
27422
27871
|
agent_id: exports_external2.string().optional().describe("Filter by assignee")
|
|
27423
27872
|
}, async ({ hours = 24, project_id, agent_id }) => {
|
|
27424
27873
|
try {
|
|
27425
|
-
const { notifyUpcomingDeadlines } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27874
|
+
const { notifyUpcomingDeadlines: notifyUpcomingDeadlines2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27426
27875
|
const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
|
|
27427
27876
|
const resolvedAgentId = agent_id ? resolveId(agent_id, "agents") : undefined;
|
|
27428
|
-
const tasks =
|
|
27877
|
+
const tasks = notifyUpcomingDeadlines2({ hours, project_id: resolvedProjectId, agent_id: resolvedAgentId });
|
|
27429
27878
|
if (tasks.length === 0)
|
|
27430
27879
|
return { content: [{ type: "text", text: "No deadlines approaching." }] };
|
|
27431
|
-
const lines = tasks.map((t) => `${t.short_id || t.id.slice(0, 8)} ${t.title} due ${t.
|
|
27880
|
+
const lines = tasks.map((t) => `${t.short_id || t.id.slice(0, 8)} ${t.title} due ${t.due_at}`);
|
|
27432
27881
|
return { content: [{ type: "text", text: `${tasks.length} task(s) due within ${hours}h:
|
|
27433
27882
|
${lines.join(`
|
|
27434
27883
|
`)}` }] };
|
|
@@ -27462,9 +27911,9 @@ ${lines.join(`
|
|
|
27462
27911
|
project_id: exports_external2.string().optional().describe("Filter by project")
|
|
27463
27912
|
}, async ({ project_id }) => {
|
|
27464
27913
|
try {
|
|
27465
|
-
const { getBlockedTasks } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27914
|
+
const { getBlockedTasks: getBlockedTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27466
27915
|
const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
|
|
27467
|
-
const tasks =
|
|
27916
|
+
const tasks = getBlockedTasks2(resolvedProjectId);
|
|
27468
27917
|
if (tasks.length === 0)
|
|
27469
27918
|
return { content: [{ type: "text", text: "No blocked tasks." }] };
|
|
27470
27919
|
const lines = tasks.map((t) => `${t.short_id || t.id.slice(0, 8)} [${t.status}] ${t.title} \u2014 blocked by ${(t.blocked_by || []).map((b) => b.slice(0, 8)).join(", ")}`);
|
|
@@ -27481,9 +27930,9 @@ ${lines.join(`
|
|
|
27481
27930
|
project_id: exports_external2.string().optional().describe("Filter by project")
|
|
27482
27931
|
}, async ({ project_id }) => {
|
|
27483
27932
|
try {
|
|
27484
|
-
const { getBlockingTasks } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27933
|
+
const { getBlockingTasks: getBlockingTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27485
27934
|
const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
|
|
27486
|
-
const tasks =
|
|
27935
|
+
const tasks = getBlockingTasks2(resolvedProjectId);
|
|
27487
27936
|
if (tasks.length === 0)
|
|
27488
27937
|
return { content: [{ type: "text", text: "No tasks blocking others." }] };
|
|
27489
27938
|
const lines = tasks.map((t) => `${t.short_id || t.id.slice(0, 8)} [${t.status}] ${t.title} \u2014 blocking ${t.blocking_count} task(s)`);
|
|
@@ -27498,20 +27947,18 @@ ${lines.join(`
|
|
|
27498
27947
|
if (shouldRegisterTool("get_health")) {
|
|
27499
27948
|
server.tool("get_health", "Get system health: task counts by status, active agents, project summary.", async () => {
|
|
27500
27949
|
try {
|
|
27501
|
-
const {
|
|
27502
|
-
const { listProjects: listProjects2 } = (
|
|
27950
|
+
const { countTasks: countTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27951
|
+
const { listProjects: listProjects2 } = (init_projects(), __toCommonJS(exports_projects));
|
|
27503
27952
|
const { listAgents: listAgents2 } = (init_agents(), __toCommonJS(exports_agents));
|
|
27504
|
-
const
|
|
27505
|
-
|
|
27506
|
-
|
|
27507
|
-
|
|
27508
|
-
|
|
27509
|
-
|
|
27510
|
-
const projects = listProjects2({ limit: 100 });
|
|
27511
|
-
const agents = listAgents2({ limit: 100 });
|
|
27953
|
+
const pending = countTasks2({ status: "pending" });
|
|
27954
|
+
const inProgress = countTasks2({ status: "in_progress" });
|
|
27955
|
+
const completed = countTasks2({ status: "completed" });
|
|
27956
|
+
const cancelled = countTasks2({ status: "cancelled" });
|
|
27957
|
+
const projects = listProjects2();
|
|
27958
|
+
const agents = listAgents2();
|
|
27512
27959
|
const lines = [
|
|
27513
27960
|
`=== System Health ===`,
|
|
27514
|
-
`Tasks: ${pending
|
|
27961
|
+
`Tasks: ${pending} pending | ${inProgress} in progress | ${completed} completed | ${cancelled} cancelled`,
|
|
27515
27962
|
`Projects: ${projects.length} total`,
|
|
27516
27963
|
`Agents: ${agents.length} registered`
|
|
27517
27964
|
];
|
|
@@ -27657,17 +28104,31 @@ var init_task_relationships = __esm(() => {
|
|
|
27657
28104
|
function registerTaskAdvTools(server, ctx) {
|
|
27658
28105
|
const { shouldRegisterTool, resolveId, formatError, formatTask, formatTaskDetail } = ctx;
|
|
27659
28106
|
if (shouldRegisterTool("get_status")) {
|
|
27660
|
-
server.tool("get_status", "Get
|
|
27661
|
-
task_id: exports_external2.string().describe("Task ID")
|
|
27662
|
-
|
|
28107
|
+
server.tool("get_status", "Get queue status summary, or pass task_id for a task's detailed status.", {
|
|
28108
|
+
task_id: exports_external2.string().optional().describe("Task ID for task-specific status"),
|
|
28109
|
+
project_id: exports_external2.string().optional().describe("Filter summary by project"),
|
|
28110
|
+
task_list_id: exports_external2.string().optional().describe("Filter summary by task list"),
|
|
28111
|
+
agent_id: exports_external2.string().optional().describe("Agent for next-task affinity"),
|
|
28112
|
+
explain_blocked: exports_external2.boolean().optional().describe("Include blocked task explanations in summary")
|
|
28113
|
+
}, async ({ task_id, project_id, task_list_id, agent_id, explain_blocked }) => {
|
|
27663
28114
|
try {
|
|
28115
|
+
if (!task_id) {
|
|
28116
|
+
const { getStatus: getStatus2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
28117
|
+
const filters = {};
|
|
28118
|
+
if (project_id)
|
|
28119
|
+
filters.project_id = resolveId(project_id, "projects");
|
|
28120
|
+
if (task_list_id)
|
|
28121
|
+
filters.task_list_id = resolveId(task_list_id, "task_lists");
|
|
28122
|
+
const status = getStatus2(filters, agent_id, { explain_blocked });
|
|
28123
|
+
return { content: [{ type: "text", text: JSON.stringify(status, null, 2) }] };
|
|
28124
|
+
}
|
|
27664
28125
|
const resolvedId = resolveId(task_id);
|
|
27665
|
-
const { getTask:
|
|
27666
|
-
const task =
|
|
28126
|
+
const { getTask: getTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
28127
|
+
const task = getTask2(resolvedId);
|
|
27667
28128
|
if (!task)
|
|
27668
28129
|
throw new Error(`Task not found: ${task_id}`);
|
|
27669
28130
|
const { getTaskDependencies: getTaskDependencies3 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27670
|
-
const { listComments: listComments2 } = (
|
|
28131
|
+
const { listComments: listComments2 } = (init_comments(), __toCommonJS(exports_comments));
|
|
27671
28132
|
const { listTaskFiles: listTaskFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
|
|
27672
28133
|
const [deps, comments, files] = await Promise.all([
|
|
27673
28134
|
Promise.resolve(getTaskDependencies3(resolvedId, "both")),
|
|
@@ -27677,14 +28138,14 @@ function registerTaskAdvTools(server, ctx) {
|
|
|
27677
28138
|
const lines = [
|
|
27678
28139
|
`Status: ${task.status} | Priority: ${task.priority}`,
|
|
27679
28140
|
task.assigned_to ? `Assigned: ${task.assigned_to}` : "Unassigned",
|
|
27680
|
-
task.
|
|
28141
|
+
task.due_at ? `Due: ${task.due_at}` : null,
|
|
27681
28142
|
task.confidence != null ? `Confidence: ${task.confidence}` : null,
|
|
27682
28143
|
deps.length > 0 ? `
|
|
27683
28144
|
Dependencies (${deps.length}):` : null,
|
|
27684
28145
|
...deps.map((d) => ` [${d.direction}] ${d.task_id.slice(0, 8)} (${d.status})`),
|
|
27685
28146
|
comments.length > 0 ? `
|
|
27686
28147
|
Comments (${comments.length}):` : null,
|
|
27687
|
-
...comments.map((c) => ` [${c.
|
|
28148
|
+
...comments.map((c) => ` [${c.agent_id || "?"}] ${c.created_at?.slice(0, 16)}: ${c.content.slice(0, 80)}`),
|
|
27688
28149
|
files.length > 0 ? `
|
|
27689
28150
|
Files (${files.length}):` : null,
|
|
27690
28151
|
...files.map((f) => ` ${f.status} ${f.path}`)
|
|
@@ -27702,15 +28163,15 @@ Files (${files.length}):` : null,
|
|
|
27702
28163
|
}, async ({ task_id }) => {
|
|
27703
28164
|
try {
|
|
27704
28165
|
const resolvedId = resolveId(task_id);
|
|
27705
|
-
const { getTask:
|
|
27706
|
-
const task =
|
|
28166
|
+
const { getTask: getTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
28167
|
+
const task = getTask2(resolvedId);
|
|
27707
28168
|
if (!task)
|
|
27708
28169
|
throw new Error(`Task not found: ${task_id}`);
|
|
27709
28170
|
const { getTaskDependencies: getTaskDependencies3 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27710
28171
|
const { getTaskRelationships: getTaskRelationships2 } = (init_task_relationships(), __toCommonJS(exports_task_relationships));
|
|
27711
|
-
const { listComments: listComments2 } = (
|
|
28172
|
+
const { listComments: listComments2 } = (init_comments(), __toCommonJS(exports_comments));
|
|
27712
28173
|
const { listTaskFiles: listTaskFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
|
|
27713
|
-
const { getTaskCommits: getTaskCommits2 } = (
|
|
28174
|
+
const { getTaskCommits: getTaskCommits2 } = (init_task_commits(), __toCommonJS(exports_task_commits));
|
|
27714
28175
|
const { getTaskWatchers: getTaskWatchers2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27715
28176
|
const [deps, rels, comments, files, commits, watchers] = await Promise.all([
|
|
27716
28177
|
Promise.resolve(getTaskDependencies3(resolvedId, "both")),
|
|
@@ -27731,7 +28192,7 @@ Files (${files.length}):` : null,
|
|
|
27731
28192
|
task.tags?.length ? `Tags: ${task.tags.join(", ")}` : null,
|
|
27732
28193
|
task.created_at ? `Created: ${task.created_at}` : null,
|
|
27733
28194
|
task.updated_at ? `Updated: ${task.updated_at}` : null,
|
|
27734
|
-
task.
|
|
28195
|
+
task.due_at ? `Due: ${task.due_at}` : null,
|
|
27735
28196
|
task.completed_at ? `Completed: ${task.completed_at}` : null,
|
|
27736
28197
|
deps.length > 0 ? `
|
|
27737
28198
|
--- Dependencies (${deps.length}) ---` : null,
|
|
@@ -27741,7 +28202,7 @@ Files (${files.length}):` : null,
|
|
|
27741
28202
|
...rels.map((r) => ` ${r.source_task_id.slice(0, 8)} --[${r.relationship_type}]--> ${r.target_task_id.slice(0, 8)}`),
|
|
27742
28203
|
comments.length > 0 ? `
|
|
27743
28204
|
--- Comments (${comments.length}) ---` : null,
|
|
27744
|
-
...comments.map((c) => ` [${c.
|
|
28205
|
+
...comments.map((c) => ` [${c.agent_id || "?"}] ${c.created_at?.slice(0, 16)}: ${c.content.slice(0, 120)}`),
|
|
27745
28206
|
files.length > 0 ? `
|
|
27746
28207
|
--- Files (${files.length}) ---` : null,
|
|
27747
28208
|
...files.map((f) => ` [${f.status}] ${f.path}`),
|
|
@@ -27790,8 +28251,8 @@ ${JSON.stringify(task.metadata, null, 2)}` : null
|
|
|
27790
28251
|
limit: 20
|
|
27791
28252
|
}, undefined);
|
|
27792
28253
|
const completedYesterday = completed.filter((t) => t.completed_at && t.completed_at.startsWith(yesterdayStr));
|
|
27793
|
-
const { getBlockedTasks } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27794
|
-
const blocked =
|
|
28254
|
+
const { getBlockedTasks: getBlockedTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
28255
|
+
const blocked = getBlockedTasks2(effectiveProjectId ? resolveId(effectiveProjectId, "projects") : undefined).filter((t) => t.assigned_to === effectiveAgentId);
|
|
27795
28256
|
const lines = [
|
|
27796
28257
|
`Standup for ${effectiveAgentId} (${effectiveProjectId ? `project: ${effectiveProjectId.slice(0, 8)}` : "all projects"})`,
|
|
27797
28258
|
inProgress.length > 0 ? `
|
|
@@ -27820,11 +28281,11 @@ No blocked tasks.`,
|
|
|
27820
28281
|
agent_id: exports_external2.string().optional().describe("Agent claiming (defaults to context)")
|
|
27821
28282
|
}, async ({ task_id, agent_id }) => {
|
|
27822
28283
|
try {
|
|
27823
|
-
const {
|
|
28284
|
+
const { startTask: startTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27824
28285
|
const resolvedId = resolveId(task_id);
|
|
27825
28286
|
const focus = ctx.getAgentFocus(agent_id || "");
|
|
27826
|
-
const effectiveAgent = focus ? focus.agent_id : agent_id || "";
|
|
27827
|
-
const task =
|
|
28287
|
+
const effectiveAgent = focus ? focus.agent_id : agent_id || "mcp";
|
|
28288
|
+
const task = startTask2(resolvedId, effectiveAgent);
|
|
27828
28289
|
return { content: [{ type: "text", text: formatTask(task) }] };
|
|
27829
28290
|
} catch (e) {
|
|
27830
28291
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -27836,8 +28297,12 @@ No blocked tasks.`,
|
|
|
27836
28297
|
task_id: exports_external2.string().describe("Task ID")
|
|
27837
28298
|
}, async ({ task_id }) => {
|
|
27838
28299
|
try {
|
|
27839
|
-
const { updateTask: updateTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27840
|
-
const
|
|
28300
|
+
const { getTask: getTask2, updateTask: updateTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
28301
|
+
const resolvedId = resolveId(task_id);
|
|
28302
|
+
const current = getTask2(resolvedId);
|
|
28303
|
+
if (!current)
|
|
28304
|
+
throw new Error(`Task not found: ${task_id}`);
|
|
28305
|
+
const task = updateTask2(resolvedId, { status: "pending", assigned_to: null, version: current.version });
|
|
27841
28306
|
return { content: [{ type: "text", text: formatTask(task) }] };
|
|
27842
28307
|
} catch (e) {
|
|
27843
28308
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -27850,13 +28315,13 @@ No blocked tasks.`,
|
|
|
27850
28315
|
minutes: exports_external2.number().describe("Additional minutes to add to estimate")
|
|
27851
28316
|
}, async ({ task_id, minutes }) => {
|
|
27852
28317
|
try {
|
|
27853
|
-
const { getTask:
|
|
28318
|
+
const { getTask: getTask2, updateTask: updateTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27854
28319
|
const resolvedId = resolveId(task_id);
|
|
27855
|
-
const task =
|
|
28320
|
+
const task = getTask2(resolvedId);
|
|
27856
28321
|
if (!task)
|
|
27857
28322
|
throw new Error(`Task not found: ${task_id}`);
|
|
27858
28323
|
const currentEstimate = task.estimated_minutes || 0;
|
|
27859
|
-
const updated = updateTask2(resolvedId, { estimated_minutes: currentEstimate + minutes });
|
|
28324
|
+
const updated = updateTask2(resolvedId, { estimated_minutes: currentEstimate + minutes, version: task.version });
|
|
27860
28325
|
return { content: [{ type: "text", text: `Estimate updated: ${currentEstimate} \u2192 ${updated.estimated_minutes} min (+${minutes})` }] };
|
|
27861
28326
|
} catch (e) {
|
|
27862
28327
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -27870,10 +28335,10 @@ No blocked tasks.`,
|
|
|
27870
28335
|
author: exports_external2.string().optional().describe("Author agent ID or name")
|
|
27871
28336
|
}, async ({ task_id, body, author }) => {
|
|
27872
28337
|
try {
|
|
27873
|
-
const {
|
|
28338
|
+
const { addComment: addComment2 } = (init_comments(), __toCommonJS(exports_comments));
|
|
27874
28339
|
const resolvedId = resolveId(task_id);
|
|
27875
28340
|
const resolvedAuthor = author ? resolveId(author, "agents") : undefined;
|
|
27876
|
-
|
|
28341
|
+
addComment2({ task_id: resolvedId, content: body, agent_id: resolvedAuthor });
|
|
27877
28342
|
return { content: [{ type: "text", text: `Comment added to ${task_id.slice(0, 8)}` }] };
|
|
27878
28343
|
} catch (e) {
|
|
27879
28344
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -27885,11 +28350,11 @@ No blocked tasks.`,
|
|
|
27885
28350
|
task_id: exports_external2.string().describe("Task ID")
|
|
27886
28351
|
}, async ({ task_id }) => {
|
|
27887
28352
|
try {
|
|
27888
|
-
const { listComments: listComments2 } = (
|
|
28353
|
+
const { listComments: listComments2 } = (init_comments(), __toCommonJS(exports_comments));
|
|
27889
28354
|
const comments = listComments2(resolveId(task_id));
|
|
27890
28355
|
if (comments.length === 0)
|
|
27891
28356
|
return { content: [{ type: "text", text: "No comments." }] };
|
|
27892
|
-
const lines = comments.map((c) => `[${c.
|
|
28357
|
+
const lines = comments.map((c) => `[${c.agent_id || "?"}] ${c.created_at?.slice(0, 16)}: ${c.content}`);
|
|
27893
28358
|
return { content: [{ type: "text", text: lines.join(`
|
|
27894
28359
|
|
|
27895
28360
|
`) }] };
|
|
@@ -27901,7 +28366,7 @@ No blocked tasks.`,
|
|
|
27901
28366
|
if (shouldRegisterTool("list_my_tasks")) {
|
|
27902
28367
|
server.tool("list_my_tasks", "Alias for get_my_tasks.", {
|
|
27903
28368
|
agent_id: exports_external2.string().optional().describe("Agent ID (defaults to context agent)"),
|
|
27904
|
-
status: exports_external2.enum(["pending", "in_progress", "completed", "cancelled"]).optional(),
|
|
28369
|
+
status: exports_external2.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
|
|
27905
28370
|
project_id: exports_external2.string().optional().describe("Filter by project"),
|
|
27906
28371
|
limit: exports_external2.number().optional()
|
|
27907
28372
|
}, async ({ agent_id, status, project_id, limit }) => {
|
|
@@ -27926,126 +28391,6 @@ No blocked tasks.`,
|
|
|
27926
28391
|
}
|
|
27927
28392
|
});
|
|
27928
28393
|
}
|
|
27929
|
-
if (shouldRegisterTool("list_agents")) {
|
|
27930
|
-
server.tool("list_agents", "List all registered agents.", {
|
|
27931
|
-
project_id: exports_external2.string().optional().describe("Filter by project"),
|
|
27932
|
-
role: exports_external2.string().optional().describe("Filter by global role"),
|
|
27933
|
-
capabilities: exports_external2.array(exports_external2.string()).optional().describe("Filter by capabilities")
|
|
27934
|
-
}, async ({ project_id, role, capabilities }) => {
|
|
27935
|
-
try {
|
|
27936
|
-
const { listAgents: listAgents2 } = (init_agents(), __toCommonJS(exports_agents));
|
|
27937
|
-
const resolved = {};
|
|
27938
|
-
if (project_id)
|
|
27939
|
-
resolved.project_id = resolveId(project_id, "projects");
|
|
27940
|
-
if (role)
|
|
27941
|
-
resolved.role = role;
|
|
27942
|
-
if (capabilities)
|
|
27943
|
-
resolved.capabilities = capabilities;
|
|
27944
|
-
const agents = listAgents2(resolved);
|
|
27945
|
-
if (agents.length === 0)
|
|
27946
|
-
return { content: [{ type: "text", text: "No agents found." }] };
|
|
27947
|
-
const lines = agents.map((a) => `\u25CF ${a.name} [${a.role || "no role"}]${a.capabilities?.length ? ` caps:[${a.capabilities.join(",")}]` : ""}`);
|
|
27948
|
-
return { content: [{ type: "text", text: lines.join(`
|
|
27949
|
-
`) }] };
|
|
27950
|
-
} catch (e) {
|
|
27951
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
27952
|
-
}
|
|
27953
|
-
});
|
|
27954
|
-
}
|
|
27955
|
-
if (shouldRegisterTool("get_agent")) {
|
|
27956
|
-
server.tool("get_agent", "Get full details for an agent.", {
|
|
27957
|
-
agent_id: exports_external2.string().describe("Agent ID or name")
|
|
27958
|
-
}, async ({ agent_id }) => {
|
|
27959
|
-
try {
|
|
27960
|
-
const { getAgentByName: getAgentByName2, getAgent: getAgent2 } = (init_agents(), __toCommonJS(exports_agents));
|
|
27961
|
-
const resolvedId = resolveId(agent_id, "agents");
|
|
27962
|
-
const agent = agent_id.includes("@") || !resolvedId.startsWith("agent_") ? getAgentByName2(agent_id) : getAgent2(resolvedId);
|
|
27963
|
-
if (!agent)
|
|
27964
|
-
return { content: [{ type: "text", text: `Agent not found: ${agent_id}` }], isError: true };
|
|
27965
|
-
const lines = [
|
|
27966
|
-
`ID: ${agent.id}`,
|
|
27967
|
-
`Name: ${agent.name}`,
|
|
27968
|
-
agent.email ? `Email: ${agent.email}` : null,
|
|
27969
|
-
agent.title ? `Title: ${agent.title}` : null,
|
|
27970
|
-
agent.role ? `Role: ${agent.role}` : null,
|
|
27971
|
-
agent.capabilities?.length ? `Capabilities: ${agent.capabilities.join(", ")}` : null,
|
|
27972
|
-
agent.last_seen_at ? `Last Seen: ${agent.last_seen_at}` : null
|
|
27973
|
-
].filter(Boolean);
|
|
27974
|
-
return { content: [{ type: "text", text: lines.join(`
|
|
27975
|
-
`) }] };
|
|
27976
|
-
} catch (e) {
|
|
27977
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
27978
|
-
}
|
|
27979
|
-
});
|
|
27980
|
-
}
|
|
27981
|
-
if (shouldRegisterTool("update_agent")) {
|
|
27982
|
-
server.tool("update_agent", "Update an agent's fields.", {
|
|
27983
|
-
agent_id: exports_external2.string().describe("Agent ID or name"),
|
|
27984
|
-
name: exports_external2.string().optional(),
|
|
27985
|
-
email: exports_external2.string().optional(),
|
|
27986
|
-
title: exports_external2.string().optional(),
|
|
27987
|
-
role: exports_external2.string().optional(),
|
|
27988
|
-
capabilities: exports_external2.array(exports_external2.string()).optional()
|
|
27989
|
-
}, async ({ agent_id, ...updates }) => {
|
|
27990
|
-
try {
|
|
27991
|
-
const { getAgentByName: getAgentByName2, updateAgent: updateAgent2 } = (init_agents(), __toCommonJS(exports_agents));
|
|
27992
|
-
const resolvedId = resolveId(agent_id, "agents");
|
|
27993
|
-
const agent = agent_id.includes("@") || !resolvedId.startsWith("agent_") ? getAgentByName2(agent_id) : { id: resolvedId };
|
|
27994
|
-
updateAgent2(agent.id, updates);
|
|
27995
|
-
return { content: [{ type: "text", text: `Agent ${agent.id.slice(0, 8)} updated.` }] };
|
|
27996
|
-
} catch (e) {
|
|
27997
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
27998
|
-
}
|
|
27999
|
-
});
|
|
28000
|
-
}
|
|
28001
|
-
if (shouldRegisterTool("delete_agent")) {
|
|
28002
|
-
server.tool("delete_agent", "Deregister an agent.", {
|
|
28003
|
-
agent_id: exports_external2.string().describe("Agent ID or name")
|
|
28004
|
-
}, async ({ agent_id }) => {
|
|
28005
|
-
try {
|
|
28006
|
-
const { getAgentByName: getAgentByName2, deleteAgent: deleteAgent2 } = (init_agents(), __toCommonJS(exports_agents));
|
|
28007
|
-
const resolvedId = resolveId(agent_id, "agents");
|
|
28008
|
-
const agent = agent_id.includes("@") || !resolvedId.startsWith("agent_") ? getAgentByName2(agent_id) : { id: resolvedId };
|
|
28009
|
-
if (!agent)
|
|
28010
|
-
return { content: [{ type: "text", text: `Agent not found: ${agent_id}` }], isError: true };
|
|
28011
|
-
deleteAgent2(agent.id);
|
|
28012
|
-
return { content: [{ type: "text", text: `Agent ${agent_id} deleted.` }] };
|
|
28013
|
-
} catch (e) {
|
|
28014
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
28015
|
-
}
|
|
28016
|
-
});
|
|
28017
|
-
}
|
|
28018
|
-
if (shouldRegisterTool("register_agent")) {
|
|
28019
|
-
server.tool("register_agent", "Register a new agent.", {
|
|
28020
|
-
name: exports_external2.string().describe("Agent name"),
|
|
28021
|
-
email: exports_external2.string().optional(),
|
|
28022
|
-
title: exports_external2.string().optional(),
|
|
28023
|
-
role: exports_external2.string().optional(),
|
|
28024
|
-
capabilities: exports_external2.array(exports_external2.string()).optional()
|
|
28025
|
-
}, async ({ name, email, title, role, capabilities }) => {
|
|
28026
|
-
try {
|
|
28027
|
-
const { registerAgent: registerAgent2 } = (init_agents(), __toCommonJS(exports_agents));
|
|
28028
|
-
const agent = registerAgent2({ name, email, title, role, capabilities });
|
|
28029
|
-
return { content: [{ type: "text", text: `Agent registered: ${agent.name} (${agent.id})` }] };
|
|
28030
|
-
} catch (e) {
|
|
28031
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
28032
|
-
}
|
|
28033
|
-
});
|
|
28034
|
-
}
|
|
28035
|
-
if (shouldRegisterTool("heartbeat")) {
|
|
28036
|
-
server.tool("heartbeat", "Agent heartbeat \u2014 updates last_seen_at timestamp.", {
|
|
28037
|
-
agent_id: exports_external2.string().describe("Agent ID"),
|
|
28038
|
-
status: exports_external2.string().optional().describe("Current status message")
|
|
28039
|
-
}, async ({ agent_id, status }) => {
|
|
28040
|
-
try {
|
|
28041
|
-
const { heartbeat } = (init_agents(), __toCommonJS(exports_agents));
|
|
28042
|
-
heartbeat(resolveId(agent_id, "agents"), status);
|
|
28043
|
-
return { content: [{ type: "text", text: "Heartbeat received." }] };
|
|
28044
|
-
} catch (e) {
|
|
28045
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
28046
|
-
}
|
|
28047
|
-
});
|
|
28048
|
-
}
|
|
28049
28394
|
}
|
|
28050
28395
|
var init_task_adv_tools = __esm(() => {
|
|
28051
28396
|
init_zod2();
|
|
@@ -28090,6 +28435,11 @@ function registerTaskMetaTools(server, ctx) {
|
|
|
28090
28435
|
prioritize_task: "prioritize_task \u2014 Set priority. Params: task_id, priority, version",
|
|
28091
28436
|
search_tasks: "search_tasks \u2014 Full-text search. Params: query, project_id, status, limit",
|
|
28092
28437
|
get_my_tasks: "get_my_tasks \u2014 Get tasks for calling agent. Params: agent_id, status, project_id, limit",
|
|
28438
|
+
get_next_task: "get_next_task \u2014 Get the next available task without claiming it. Params: agent_id, project_id, task_list_id, plan_id, tags",
|
|
28439
|
+
claim_next_task: "claim_next_task \u2014 Atomically claim and start the next available task. Params: agent_id, project_id, task_list_id, plan_id, tags",
|
|
28440
|
+
get_tasks_changed_since: "get_tasks_changed_since \u2014 List tasks changed since an ISO timestamp. Params: since, project_id, task_list_id, limit",
|
|
28441
|
+
get_context: "get_context \u2014 Get session start context. Params: agent_id, project_id, task_list_id, explain_blocked",
|
|
28442
|
+
bootstrap: "bootstrap \u2014 Bootstrap an agent session with queue context. Params: agent_id, project_id, task_list_id, explain_blocked",
|
|
28093
28443
|
standup: "standup \u2014 Get standup report. Params: agent_id, project_id",
|
|
28094
28444
|
patrol_tasks: "patrol_tasks \u2014 Scan for task issues. Params: stuck_minutes, confidence_threshold, project_id",
|
|
28095
28445
|
get_review_queue: "get_review_queue \u2014 Get tasks needing review. Params: project_id, limit",
|
|
@@ -28147,9 +28497,20 @@ function registerTaskMetaTools(server, ctx) {
|
|
|
28147
28497
|
get_health: "get_health \u2014 Get system health stats",
|
|
28148
28498
|
approve_task: "approve_task \u2014 Approve a task. Params: task_id, approved_by, notes, version",
|
|
28149
28499
|
fail_task: "fail_task \u2014 Mark task failed. Params: task_id, reason, agent_id, version",
|
|
28500
|
+
register_agent: "register_agent \u2014 Register an agent. Params: name, description, role, title, capabilities, session_id, working_dir, force",
|
|
28501
|
+
list_agents: "list_agents \u2014 List registered agents. Params: include_archived",
|
|
28502
|
+
get_agent: "get_agent \u2014 Get agent details. Params: agent_id, id, name",
|
|
28503
|
+
update_agent: "update_agent \u2014 Update an agent. Params: agent_id, id, name, description, role, title, level, capabilities, permissions, metadata",
|
|
28504
|
+
delete_agent: "delete_agent \u2014 Archive an agent. Params: agent_id, id, name",
|
|
28505
|
+
unarchive_agent: "unarchive_agent \u2014 Restore an archived agent. Params: agent_id, id, name",
|
|
28506
|
+
heartbeat: "heartbeat \u2014 Update an agent heartbeat. Params: agent_id",
|
|
28507
|
+
release_agent: "release_agent \u2014 Release an agent session/name. Params: agent_id, session_id",
|
|
28508
|
+
set_focus: "set_focus \u2014 Focus an agent on a project. Params: agent_id, project_id, task_list_id",
|
|
28509
|
+
get_focus: "get_focus \u2014 Get current agent focus. Params: agent_id",
|
|
28510
|
+
unfocus: "unfocus \u2014 Clear agent focus. Params: agent_id",
|
|
28511
|
+
suggest_agent_name: "suggest_agent_name \u2014 Suggest available agent names. Params: working_dir",
|
|
28150
28512
|
get_org_chart: "get_org_chart \u2014 Get global org chart. Params: format",
|
|
28151
28513
|
set_reports_to: "set_reports_to \u2014 Set org hierarchy. Params: agent_id, reports_to",
|
|
28152
|
-
sync: "sync \u2014 Sync from external source. Params: source, source_id, project_id, options",
|
|
28153
28514
|
extract_todos: "extract_todos \u2014 Scan code for TODO comments. Params: path, project_id, task_list_id, patterns, tags, assigned_to, agent_id, dry_run, extensions",
|
|
28154
28515
|
migrate_pg: "migrate_pg \u2014 Apply PostgreSQL migrations. Params: connection_string"
|
|
28155
28516
|
};
|
|
@@ -28167,6 +28528,74 @@ var init_task_meta_tools = __esm(() => {
|
|
|
28167
28528
|
init_zod2();
|
|
28168
28529
|
});
|
|
28169
28530
|
|
|
28531
|
+
// src/db/file-locks.ts
|
|
28532
|
+
var exports_file_locks = {};
|
|
28533
|
+
__export(exports_file_locks, {
|
|
28534
|
+
unlockFile: () => unlockFile,
|
|
28535
|
+
lockFile: () => lockFile,
|
|
28536
|
+
listFileLocks: () => listFileLocks,
|
|
28537
|
+
forceUnlockFile: () => forceUnlockFile,
|
|
28538
|
+
cleanExpiredFileLocks: () => cleanExpiredFileLocks,
|
|
28539
|
+
checkFileLock: () => checkFileLock,
|
|
28540
|
+
FILE_LOCK_DEFAULT_TTL_SECONDS: () => FILE_LOCK_DEFAULT_TTL_SECONDS
|
|
28541
|
+
});
|
|
28542
|
+
function expiresAt(ttlSeconds) {
|
|
28543
|
+
return new Date(Date.now() + ttlSeconds * 1000).toISOString();
|
|
28544
|
+
}
|
|
28545
|
+
function cleanExpiredFileLocks(db) {
|
|
28546
|
+
const d = db || getDatabase();
|
|
28547
|
+
const result = d.run("DELETE FROM file_locks WHERE expires_at <= ?", [now()]);
|
|
28548
|
+
return result.changes;
|
|
28549
|
+
}
|
|
28550
|
+
function lockFile(input, db) {
|
|
28551
|
+
const d = db || getDatabase();
|
|
28552
|
+
const ttl = input.ttl_seconds ?? FILE_LOCK_DEFAULT_TTL_SECONDS;
|
|
28553
|
+
const expiry = expiresAt(ttl);
|
|
28554
|
+
const timestamp = now();
|
|
28555
|
+
cleanExpiredFileLocks(d);
|
|
28556
|
+
const existing = d.query("SELECT * FROM file_locks WHERE path = ?").get(input.path);
|
|
28557
|
+
if (existing) {
|
|
28558
|
+
if (existing.agent_id === input.agent_id) {
|
|
28559
|
+
d.run("UPDATE file_locks SET expires_at = ?, task_id = COALESCE(?, task_id) WHERE id = ?", [expiry, input.task_id ?? null, existing.id]);
|
|
28560
|
+
return d.query("SELECT * FROM file_locks WHERE id = ?").get(existing.id);
|
|
28561
|
+
}
|
|
28562
|
+
throw new LockError(input.path, existing.agent_id);
|
|
28563
|
+
}
|
|
28564
|
+
const id = uuid();
|
|
28565
|
+
d.run("INSERT INTO file_locks (id, path, agent_id, task_id, expires_at, created_at) VALUES (?, ?, ?, ?, ?, ?)", [id, input.path, input.agent_id, input.task_id ?? null, expiry, timestamp]);
|
|
28566
|
+
return d.query("SELECT * FROM file_locks WHERE id = ?").get(id);
|
|
28567
|
+
}
|
|
28568
|
+
function unlockFile(path, agentId, db) {
|
|
28569
|
+
const d = db || getDatabase();
|
|
28570
|
+
cleanExpiredFileLocks(d);
|
|
28571
|
+
const result = d.run("DELETE FROM file_locks WHERE path = ? AND agent_id = ?", [path, agentId]);
|
|
28572
|
+
return result.changes > 0;
|
|
28573
|
+
}
|
|
28574
|
+
function checkFileLock(path, db) {
|
|
28575
|
+
const d = db || getDatabase();
|
|
28576
|
+
cleanExpiredFileLocks(d);
|
|
28577
|
+
return d.query("SELECT * FROM file_locks WHERE path = ?").get(path);
|
|
28578
|
+
}
|
|
28579
|
+
function listFileLocks(agentId, db) {
|
|
28580
|
+
const d = db || getDatabase();
|
|
28581
|
+
cleanExpiredFileLocks(d);
|
|
28582
|
+
if (agentId) {
|
|
28583
|
+
return d.query("SELECT * FROM file_locks WHERE agent_id = ? ORDER BY created_at DESC").all(agentId);
|
|
28584
|
+
}
|
|
28585
|
+
return d.query("SELECT * FROM file_locks ORDER BY created_at DESC").all();
|
|
28586
|
+
}
|
|
28587
|
+
function forceUnlockFile(path, db) {
|
|
28588
|
+
const d = db || getDatabase();
|
|
28589
|
+
const result = d.run("DELETE FROM file_locks WHERE path = ?", [path]);
|
|
28590
|
+
return result.changes > 0;
|
|
28591
|
+
}
|
|
28592
|
+
var FILE_LOCK_DEFAULT_TTL_SECONDS;
|
|
28593
|
+
var init_file_locks = __esm(() => {
|
|
28594
|
+
init_database();
|
|
28595
|
+
init_types();
|
|
28596
|
+
FILE_LOCK_DEFAULT_TTL_SECONDS = 30 * 60;
|
|
28597
|
+
});
|
|
28598
|
+
|
|
28170
28599
|
// src/mcp/tools/task-resources.ts
|
|
28171
28600
|
function registerTaskResources(server, ctx) {
|
|
28172
28601
|
const { shouldRegisterTool, resolveId, formatError } = ctx;
|
|
@@ -28192,7 +28621,7 @@ function registerTaskResources(server, ctx) {
|
|
|
28192
28621
|
note: exports_external2.string().optional().describe("Note about why this file is linked")
|
|
28193
28622
|
}, async ({ task_id, path, paths: multiplePaths, status, agent_id, note }) => {
|
|
28194
28623
|
try {
|
|
28195
|
-
const { addTaskFile: addTaskFile2, bulkAddTaskFiles: bulkAddTaskFiles2, detectFileConflicts: detectFileConflicts2 } = (()
|
|
28624
|
+
const { addTaskFile: addTaskFile2, bulkAddTaskFiles: bulkAddTaskFiles2, detectFileConflicts: detectFileConflicts2 } = (init_task_files(), __toCommonJS(exports_task_files));
|
|
28196
28625
|
const resolvedId = resolveId(task_id);
|
|
28197
28626
|
let addedFiles;
|
|
28198
28627
|
if (multiplePaths && multiplePaths.length > 0) {
|
|
@@ -28236,7 +28665,7 @@ function registerTaskResources(server, ctx) {
|
|
|
28236
28665
|
if (shouldRegisterTool("list_task_files")) {
|
|
28237
28666
|
server.tool("list_task_files", "List all files linked to a task.", { task_id: exports_external2.string().describe("Task ID") }, async ({ task_id }) => {
|
|
28238
28667
|
try {
|
|
28239
|
-
const { listTaskFiles: listTaskFiles2 } = (()
|
|
28668
|
+
const { listTaskFiles: listTaskFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
|
|
28240
28669
|
const resolvedId = resolveId(task_id);
|
|
28241
28670
|
const files = listTaskFiles2(resolvedId);
|
|
28242
28671
|
if (files.length === 0)
|
|
@@ -28253,7 +28682,7 @@ ${lines.join(`
|
|
|
28253
28682
|
if (shouldRegisterTool("find_tasks_by_file")) {
|
|
28254
28683
|
server.tool("find_tasks_by_file", "Find which tasks are linked to a specific file path. Shows who's working on what files.", { path: exports_external2.string().describe("File path to search for") }, async ({ path }) => {
|
|
28255
28684
|
try {
|
|
28256
|
-
const { findTasksByFile: findTasksByFile2 } = (()
|
|
28685
|
+
const { findTasksByFile: findTasksByFile2 } = (init_task_files(), __toCommonJS(exports_task_files));
|
|
28257
28686
|
const files = findTasksByFile2(path);
|
|
28258
28687
|
if (files.length === 0)
|
|
28259
28688
|
return { content: [{ type: "text", text: `No tasks linked to ${path}` }] };
|
|
@@ -28273,7 +28702,7 @@ ${lines.join(`
|
|
|
28273
28702
|
min_edits: exports_external2.number().optional().describe("Minimum edit count to include (default: 1)")
|
|
28274
28703
|
}, async ({ limit, project_id, min_edits }) => {
|
|
28275
28704
|
try {
|
|
28276
|
-
const { getFileHeatMap: getFileHeatMap2 } = (()
|
|
28705
|
+
const { getFileHeatMap: getFileHeatMap2 } = (init_task_files(), __toCommonJS(exports_task_files));
|
|
28277
28706
|
const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
|
|
28278
28707
|
const results = getFileHeatMap2({ limit, project_id: resolvedProjectId, min_edits });
|
|
28279
28708
|
return { content: [{ type: "text", text: JSON.stringify(results, null, 2) }] };
|
|
@@ -28287,7 +28716,7 @@ ${lines.join(`
|
|
|
28287
28716
|
paths: exports_external2.array(exports_external2.string()).describe("Array of file paths to check")
|
|
28288
28717
|
}, async ({ paths }) => {
|
|
28289
28718
|
try {
|
|
28290
|
-
const { bulkFindTasksByFiles: bulkFindTasksByFiles2 } = (()
|
|
28719
|
+
const { bulkFindTasksByFiles: bulkFindTasksByFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
|
|
28291
28720
|
const results = bulkFindTasksByFiles2(paths);
|
|
28292
28721
|
return { content: [{ type: "text", text: JSON.stringify(results, null, 2) }] };
|
|
28293
28722
|
} catch (e) {
|
|
@@ -28300,11 +28729,11 @@ ${lines.join(`
|
|
|
28300
28729
|
project_id: exports_external2.string().optional().describe("Filter by project")
|
|
28301
28730
|
}, async ({ project_id }) => {
|
|
28302
28731
|
try {
|
|
28303
|
-
const { listActiveFiles: listActiveFiles2 } = (()
|
|
28732
|
+
const { listActiveFiles: listActiveFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
|
|
28304
28733
|
let files = listActiveFiles2();
|
|
28305
28734
|
if (project_id) {
|
|
28306
28735
|
const pid = resolveId(project_id, "projects");
|
|
28307
|
-
const db = (()
|
|
28736
|
+
const db = (init_database(), __toCommonJS(exports_database)).getDatabase();
|
|
28308
28737
|
files = db.query(`
|
|
28309
28738
|
SELECT
|
|
28310
28739
|
tf.path,
|
|
@@ -28386,8 +28815,8 @@ ${lines.join(`
|
|
|
28386
28815
|
ttl_seconds: exports_external2.number().optional().describe("Lock TTL in seconds (default: 1800 = 30 min)")
|
|
28387
28816
|
}, async ({ path, agent_id, task_id, ttl_seconds }) => {
|
|
28388
28817
|
try {
|
|
28389
|
-
const { lockFile } = (()
|
|
28390
|
-
const lock =
|
|
28818
|
+
const { lockFile: lockFile2 } = (init_file_locks(), __toCommonJS(exports_file_locks));
|
|
28819
|
+
const lock = lockFile2({ path, agent_id, task_id, ttl_seconds });
|
|
28391
28820
|
return { content: [{ type: "text", text: JSON.stringify(lock, null, 2) }] };
|
|
28392
28821
|
} catch (e) {
|
|
28393
28822
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -28400,8 +28829,8 @@ ${lines.join(`
|
|
|
28400
28829
|
agent_id: exports_external2.string().describe("Agent releasing the lock (must be the lock holder)")
|
|
28401
28830
|
}, async ({ path, agent_id }) => {
|
|
28402
28831
|
try {
|
|
28403
|
-
const { unlockFile } = (()
|
|
28404
|
-
const released =
|
|
28832
|
+
const { unlockFile: unlockFile2 } = (init_file_locks(), __toCommonJS(exports_file_locks));
|
|
28833
|
+
const released = unlockFile2(path, agent_id);
|
|
28405
28834
|
return { content: [{ type: "text", text: JSON.stringify({ released, path }) }] };
|
|
28406
28835
|
} catch (e) {
|
|
28407
28836
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -28413,8 +28842,8 @@ ${lines.join(`
|
|
|
28413
28842
|
path: exports_external2.string().describe("File path to check")
|
|
28414
28843
|
}, async ({ path }) => {
|
|
28415
28844
|
try {
|
|
28416
|
-
const { checkFileLock } = (()
|
|
28417
|
-
const lock =
|
|
28845
|
+
const { checkFileLock: checkFileLock2 } = (init_file_locks(), __toCommonJS(exports_file_locks));
|
|
28846
|
+
const lock = checkFileLock2(path);
|
|
28418
28847
|
if (!lock)
|
|
28419
28848
|
return { content: [{ type: "text", text: JSON.stringify({ path, locked: false }) }] };
|
|
28420
28849
|
return { content: [{ type: "text", text: JSON.stringify({ path, locked: true, ...lock }) }] };
|
|
@@ -28428,8 +28857,8 @@ ${lines.join(`
|
|
|
28428
28857
|
agent_id: exports_external2.string().optional().describe("Filter locks by agent")
|
|
28429
28858
|
}, async ({ agent_id }) => {
|
|
28430
28859
|
try {
|
|
28431
|
-
const { listFileLocks } = (()
|
|
28432
|
-
const locks =
|
|
28860
|
+
const { listFileLocks: listFileLocks2 } = (init_file_locks(), __toCommonJS(exports_file_locks));
|
|
28861
|
+
const locks = listFileLocks2(agent_id);
|
|
28433
28862
|
return { content: [{ type: "text", text: JSON.stringify(locks, null, 2) }] };
|
|
28434
28863
|
} catch (e) {
|
|
28435
28864
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -28445,6 +28874,533 @@ var init_task_resources = __esm(() => {
|
|
|
28445
28874
|
init_task_commits();
|
|
28446
28875
|
});
|
|
28447
28876
|
|
|
28877
|
+
// src/db/kg.ts
|
|
28878
|
+
var exports_kg = {};
|
|
28879
|
+
__export(exports_kg, {
|
|
28880
|
+
syncKgEdges: () => syncKgEdges,
|
|
28881
|
+
removeKgEdges: () => removeKgEdges,
|
|
28882
|
+
getRelated: () => getRelated,
|
|
28883
|
+
getImpactAnalysis: () => getImpactAnalysis,
|
|
28884
|
+
getCriticalPath: () => getCriticalPath,
|
|
28885
|
+
findPath: () => findPath,
|
|
28886
|
+
addKgEdge: () => addKgEdge
|
|
28887
|
+
});
|
|
28888
|
+
function rowToEdge(row) {
|
|
28889
|
+
return {
|
|
28890
|
+
...row,
|
|
28891
|
+
metadata: JSON.parse(row.metadata || "{}")
|
|
28892
|
+
};
|
|
28893
|
+
}
|
|
28894
|
+
function syncKgEdges(db) {
|
|
28895
|
+
const d = db || getDatabase();
|
|
28896
|
+
let synced = 0;
|
|
28897
|
+
const tx = d.transaction(() => {
|
|
28898
|
+
const deps = d.query("SELECT task_id, depends_on FROM task_dependencies").all();
|
|
28899
|
+
for (const dep of deps) {
|
|
28900
|
+
synced += upsertEdge(d, dep.task_id, "task", dep.depends_on, "task", "depends_on");
|
|
28901
|
+
}
|
|
28902
|
+
const assignments = d.query("SELECT id, assigned_to FROM tasks WHERE assigned_to IS NOT NULL").all();
|
|
28903
|
+
for (const a of assignments) {
|
|
28904
|
+
synced += upsertEdge(d, a.id, "task", a.assigned_to, "agent", "assigned_to");
|
|
28905
|
+
}
|
|
28906
|
+
const agents = d.query("SELECT id, reports_to FROM agents WHERE reports_to IS NOT NULL").all();
|
|
28907
|
+
for (const a of agents) {
|
|
28908
|
+
synced += upsertEdge(d, a.id, "agent", a.reports_to, "agent", "reports_to");
|
|
28909
|
+
}
|
|
28910
|
+
const files = d.query("SELECT task_id, path FROM task_files WHERE status != 'removed'").all();
|
|
28911
|
+
for (const f of files) {
|
|
28912
|
+
synced += upsertEdge(d, f.task_id, "task", f.path, "file", "references_file");
|
|
28913
|
+
}
|
|
28914
|
+
const taskProjects = d.query("SELECT id, project_id FROM tasks WHERE project_id IS NOT NULL").all();
|
|
28915
|
+
for (const tp of taskProjects) {
|
|
28916
|
+
synced += upsertEdge(d, tp.id, "task", tp.project_id, "project", "in_project");
|
|
28917
|
+
}
|
|
28918
|
+
const taskPlans = d.query("SELECT id, plan_id FROM tasks WHERE plan_id IS NOT NULL").all();
|
|
28919
|
+
for (const tp of taskPlans) {
|
|
28920
|
+
synced += upsertEdge(d, tp.id, "task", tp.plan_id, "plan", "in_plan");
|
|
28921
|
+
}
|
|
28922
|
+
try {
|
|
28923
|
+
const rels = d.query("SELECT source_task_id, target_task_id, relationship_type FROM task_relationships").all();
|
|
28924
|
+
for (const r of rels) {
|
|
28925
|
+
synced += upsertEdge(d, r.source_task_id, "task", r.target_task_id, "task", r.relationship_type);
|
|
28926
|
+
}
|
|
28927
|
+
} catch {}
|
|
28928
|
+
});
|
|
28929
|
+
tx();
|
|
28930
|
+
return { synced };
|
|
28931
|
+
}
|
|
28932
|
+
function upsertEdge(d, sourceId, sourceType, targetId, targetType, relationType, weight = 1) {
|
|
28933
|
+
try {
|
|
28934
|
+
d.run(`INSERT OR IGNORE INTO kg_edges (id, source_id, source_type, target_id, target_type, relation_type, weight, metadata, created_at)
|
|
28935
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, '{}', ?)`, [uuid(), sourceId, sourceType, targetId, targetType, relationType, weight, now()]);
|
|
28936
|
+
return 1;
|
|
28937
|
+
} catch {
|
|
28938
|
+
return 0;
|
|
28939
|
+
}
|
|
28940
|
+
}
|
|
28941
|
+
function getRelated(entityId, opts, db) {
|
|
28942
|
+
const d = db || getDatabase();
|
|
28943
|
+
const direction = opts?.direction || "both";
|
|
28944
|
+
const conditions = [];
|
|
28945
|
+
const params = [];
|
|
28946
|
+
if (direction === "outgoing" || direction === "both") {
|
|
28947
|
+
conditions.push("source_id = ?");
|
|
28948
|
+
params.push(entityId);
|
|
28949
|
+
}
|
|
28950
|
+
if (direction === "incoming" || direction === "both") {
|
|
28951
|
+
conditions.push("target_id = ?");
|
|
28952
|
+
params.push(entityId);
|
|
28953
|
+
}
|
|
28954
|
+
let sql = `SELECT * FROM kg_edges WHERE (${conditions.join(" OR ")})`;
|
|
28955
|
+
if (opts?.relation_type) {
|
|
28956
|
+
sql += " AND relation_type = ?";
|
|
28957
|
+
params.push(opts.relation_type);
|
|
28958
|
+
}
|
|
28959
|
+
if (opts?.entity_type) {
|
|
28960
|
+
sql += " AND (source_type = ? OR target_type = ?)";
|
|
28961
|
+
params.push(opts.entity_type, opts.entity_type);
|
|
28962
|
+
}
|
|
28963
|
+
sql += " ORDER BY weight DESC, created_at DESC";
|
|
28964
|
+
if (opts?.limit) {
|
|
28965
|
+
sql += " LIMIT ?";
|
|
28966
|
+
params.push(opts.limit);
|
|
28967
|
+
}
|
|
28968
|
+
return d.query(sql).all(...params).map(rowToEdge);
|
|
28969
|
+
}
|
|
28970
|
+
function findPath(sourceId, targetId, opts, db) {
|
|
28971
|
+
const d = db || getDatabase();
|
|
28972
|
+
const maxDepth = opts?.max_depth || 5;
|
|
28973
|
+
const visited = new Set;
|
|
28974
|
+
const queue = [{ id: sourceId, path: [] }];
|
|
28975
|
+
const results = [];
|
|
28976
|
+
visited.add(sourceId);
|
|
28977
|
+
while (queue.length > 0) {
|
|
28978
|
+
const current = queue.shift();
|
|
28979
|
+
if (current.path.length >= maxDepth)
|
|
28980
|
+
continue;
|
|
28981
|
+
let sql = "SELECT * FROM kg_edges WHERE source_id = ?";
|
|
28982
|
+
const params = [current.id];
|
|
28983
|
+
if (opts?.relation_types && opts.relation_types.length > 0) {
|
|
28984
|
+
const placeholders = opts.relation_types.map(() => "?").join(",");
|
|
28985
|
+
sql += ` AND relation_type IN (${placeholders})`;
|
|
28986
|
+
params.push(...opts.relation_types);
|
|
28987
|
+
}
|
|
28988
|
+
const edges = d.query(sql).all(...params).map(rowToEdge);
|
|
28989
|
+
for (const edge of edges) {
|
|
28990
|
+
const nextId = edge.target_id;
|
|
28991
|
+
const newPath = [...current.path, edge];
|
|
28992
|
+
if (nextId === targetId) {
|
|
28993
|
+
results.push(newPath);
|
|
28994
|
+
if (results.length >= 3)
|
|
28995
|
+
return results;
|
|
28996
|
+
continue;
|
|
28997
|
+
}
|
|
28998
|
+
if (!visited.has(nextId)) {
|
|
28999
|
+
visited.add(nextId);
|
|
29000
|
+
queue.push({ id: nextId, path: newPath });
|
|
29001
|
+
}
|
|
29002
|
+
}
|
|
29003
|
+
}
|
|
29004
|
+
return results;
|
|
29005
|
+
}
|
|
29006
|
+
function getImpactAnalysis(entityId, opts, db) {
|
|
29007
|
+
const d = db || getDatabase();
|
|
29008
|
+
const maxDepth = opts?.max_depth || 3;
|
|
29009
|
+
const results = [];
|
|
29010
|
+
const visited = new Set;
|
|
29011
|
+
visited.add(entityId);
|
|
29012
|
+
const queue = [{ id: entityId, depth: 0 }];
|
|
29013
|
+
while (queue.length > 0) {
|
|
29014
|
+
const current = queue.shift();
|
|
29015
|
+
if (current.depth >= maxDepth)
|
|
29016
|
+
continue;
|
|
29017
|
+
let sql = "SELECT * FROM kg_edges WHERE source_id = ?";
|
|
29018
|
+
const params = [current.id];
|
|
29019
|
+
if (opts?.relation_types && opts.relation_types.length > 0) {
|
|
29020
|
+
const placeholders = opts.relation_types.map(() => "?").join(",");
|
|
29021
|
+
sql += ` AND relation_type IN (${placeholders})`;
|
|
29022
|
+
params.push(...opts.relation_types);
|
|
29023
|
+
}
|
|
29024
|
+
const edges = d.query(sql).all(...params).map(rowToEdge);
|
|
29025
|
+
for (const edge of edges) {
|
|
29026
|
+
if (!visited.has(edge.target_id)) {
|
|
29027
|
+
visited.add(edge.target_id);
|
|
29028
|
+
results.push({
|
|
29029
|
+
entity_id: edge.target_id,
|
|
29030
|
+
entity_type: edge.target_type,
|
|
29031
|
+
depth: current.depth + 1,
|
|
29032
|
+
relation: edge.relation_type
|
|
29033
|
+
});
|
|
29034
|
+
queue.push({ id: edge.target_id, depth: current.depth + 1 });
|
|
29035
|
+
}
|
|
29036
|
+
}
|
|
29037
|
+
}
|
|
29038
|
+
return results;
|
|
29039
|
+
}
|
|
29040
|
+
function getCriticalPath(opts, db) {
|
|
29041
|
+
const d = db || getDatabase();
|
|
29042
|
+
let sql = `SELECT source_id, target_id FROM kg_edges WHERE relation_type = 'depends_on'`;
|
|
29043
|
+
const params = [];
|
|
29044
|
+
if (opts?.project_id) {
|
|
29045
|
+
sql += ` AND source_id IN (SELECT id FROM tasks WHERE project_id = ?)`;
|
|
29046
|
+
params.push(opts.project_id);
|
|
29047
|
+
}
|
|
29048
|
+
const edges = d.query(sql).all(...params);
|
|
29049
|
+
const blocks = new Map;
|
|
29050
|
+
for (const e of edges) {
|
|
29051
|
+
if (!blocks.has(e.target_id))
|
|
29052
|
+
blocks.set(e.target_id, new Set);
|
|
29053
|
+
blocks.get(e.target_id).add(e.source_id);
|
|
29054
|
+
}
|
|
29055
|
+
const results = [];
|
|
29056
|
+
for (const [taskId] of blocks) {
|
|
29057
|
+
const visited = new Set;
|
|
29058
|
+
const q = [taskId];
|
|
29059
|
+
let maxDepth = 0;
|
|
29060
|
+
let depth = 0;
|
|
29061
|
+
let levelSize = q.length;
|
|
29062
|
+
while (q.length > 0) {
|
|
29063
|
+
const node = q.shift();
|
|
29064
|
+
levelSize--;
|
|
29065
|
+
const downstream = blocks.get(node);
|
|
29066
|
+
if (downstream) {
|
|
29067
|
+
for (const d2 of downstream) {
|
|
29068
|
+
if (!visited.has(d2)) {
|
|
29069
|
+
visited.add(d2);
|
|
29070
|
+
q.push(d2);
|
|
29071
|
+
}
|
|
29072
|
+
}
|
|
29073
|
+
}
|
|
29074
|
+
if (levelSize === 0) {
|
|
29075
|
+
depth++;
|
|
29076
|
+
maxDepth = Math.max(maxDepth, depth);
|
|
29077
|
+
levelSize = q.length;
|
|
29078
|
+
}
|
|
29079
|
+
}
|
|
29080
|
+
if (visited.size > 0) {
|
|
29081
|
+
results.push({ task_id: taskId, blocking_count: visited.size, depth: maxDepth });
|
|
29082
|
+
}
|
|
29083
|
+
}
|
|
29084
|
+
results.sort((a, b) => b.blocking_count - a.blocking_count);
|
|
29085
|
+
return results.slice(0, opts?.limit || 20);
|
|
29086
|
+
}
|
|
29087
|
+
function addKgEdge(sourceId, sourceType, targetId, targetType, relationType, weight = 1, metadata, db) {
|
|
29088
|
+
const d = db || getDatabase();
|
|
29089
|
+
const id = uuid();
|
|
29090
|
+
const timestamp = now();
|
|
29091
|
+
d.run(`INSERT OR IGNORE INTO kg_edges (id, source_id, source_type, target_id, target_type, relation_type, weight, metadata, created_at)
|
|
29092
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [id, sourceId, sourceType, targetId, targetType, relationType, weight, JSON.stringify(metadata || {}), timestamp]);
|
|
29093
|
+
return { id, source_id: sourceId, source_type: sourceType, target_id: targetId, target_type: targetType, relation_type: relationType, weight, metadata: metadata || {}, created_at: timestamp };
|
|
29094
|
+
}
|
|
29095
|
+
function removeKgEdges(sourceId, targetId, relationType, db) {
|
|
29096
|
+
const d = db || getDatabase();
|
|
29097
|
+
if (relationType) {
|
|
29098
|
+
return d.run("DELETE FROM kg_edges WHERE source_id = ? AND target_id = ? AND relation_type = ?", [sourceId, targetId, relationType]).changes;
|
|
29099
|
+
}
|
|
29100
|
+
return d.run("DELETE FROM kg_edges WHERE source_id = ? AND target_id = ?", [sourceId, targetId]).changes;
|
|
29101
|
+
}
|
|
29102
|
+
var init_kg = __esm(() => {
|
|
29103
|
+
init_database();
|
|
29104
|
+
});
|
|
29105
|
+
|
|
29106
|
+
// src/db/project-agent-roles.ts
|
|
29107
|
+
var exports_project_agent_roles = {};
|
|
29108
|
+
__export(exports_project_agent_roles, {
|
|
29109
|
+
setProjectAgentRole: () => setProjectAgentRole,
|
|
29110
|
+
removeProjectAgentRole: () => removeProjectAgentRole,
|
|
29111
|
+
listProjectAgentRoles: () => listProjectAgentRoles,
|
|
29112
|
+
getProjectOrgChart: () => getProjectOrgChart,
|
|
29113
|
+
getAgentProjectRoles: () => getAgentProjectRoles
|
|
29114
|
+
});
|
|
29115
|
+
function rowToRole(row) {
|
|
29116
|
+
return { ...row, is_lead: row.is_lead === 1 };
|
|
29117
|
+
}
|
|
29118
|
+
function setProjectAgentRole(projectId, agentId, role, isLead = false, db) {
|
|
29119
|
+
const d = db || getDatabase();
|
|
29120
|
+
const existing = d.query("SELECT * FROM project_agent_roles WHERE project_id = ? AND agent_id = ? AND role = ?").get(projectId, agentId, role);
|
|
29121
|
+
if (existing) {
|
|
29122
|
+
d.run("UPDATE project_agent_roles SET is_lead = ? WHERE id = ?", [isLead ? 1 : 0, existing.id]);
|
|
29123
|
+
return rowToRole(d.query("SELECT * FROM project_agent_roles WHERE id = ?").get(existing.id));
|
|
29124
|
+
}
|
|
29125
|
+
const id = uuid();
|
|
29126
|
+
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()]);
|
|
29127
|
+
return rowToRole(d.query("SELECT * FROM project_agent_roles WHERE id = ?").get(id));
|
|
29128
|
+
}
|
|
29129
|
+
function removeProjectAgentRole(projectId, agentId, role, db) {
|
|
29130
|
+
const d = db || getDatabase();
|
|
29131
|
+
if (role) {
|
|
29132
|
+
return d.run("DELETE FROM project_agent_roles WHERE project_id = ? AND agent_id = ? AND role = ?", [projectId, agentId, role]).changes;
|
|
29133
|
+
}
|
|
29134
|
+
return d.run("DELETE FROM project_agent_roles WHERE project_id = ? AND agent_id = ?", [projectId, agentId]).changes;
|
|
29135
|
+
}
|
|
29136
|
+
function listProjectAgentRoles(projectId, db) {
|
|
29137
|
+
const d = db || getDatabase();
|
|
29138
|
+
return d.query("SELECT * FROM project_agent_roles WHERE project_id = ? ORDER BY role, created_at").all(projectId).map(rowToRole);
|
|
29139
|
+
}
|
|
29140
|
+
function getAgentProjectRoles(agentId, db) {
|
|
29141
|
+
const d = db || getDatabase();
|
|
29142
|
+
return d.query("SELECT * FROM project_agent_roles WHERE agent_id = ? ORDER BY project_id, role").all(agentId).map(rowToRole);
|
|
29143
|
+
}
|
|
29144
|
+
function getProjectOrgChart(projectId, opts, db) {
|
|
29145
|
+
const d = db || getDatabase();
|
|
29146
|
+
const globalTree = getOrgChart(d);
|
|
29147
|
+
const projectRoles = listProjectAgentRoles(projectId, d);
|
|
29148
|
+
const rolesByAgent = new Map;
|
|
29149
|
+
for (const pr of projectRoles) {
|
|
29150
|
+
if (!rolesByAgent.has(pr.agent_id))
|
|
29151
|
+
rolesByAgent.set(pr.agent_id, { roles: [], isLead: false });
|
|
29152
|
+
const entry = rolesByAgent.get(pr.agent_id);
|
|
29153
|
+
entry.roles.push(pr.role);
|
|
29154
|
+
if (pr.is_lead)
|
|
29155
|
+
entry.isLead = true;
|
|
29156
|
+
}
|
|
29157
|
+
function augmentTree(nodes) {
|
|
29158
|
+
return nodes.map((n) => {
|
|
29159
|
+
const override = rolesByAgent.get(n.agent.id);
|
|
29160
|
+
return {
|
|
29161
|
+
...n,
|
|
29162
|
+
reports: augmentTree(n.reports),
|
|
29163
|
+
project_roles: override?.roles ?? [],
|
|
29164
|
+
is_project_lead: override?.isLead ?? false
|
|
29165
|
+
};
|
|
29166
|
+
}).filter((n) => {
|
|
29167
|
+
if (!opts?.filter_to_project)
|
|
29168
|
+
return true;
|
|
29169
|
+
const hasRole = n.project_roles.length > 0;
|
|
29170
|
+
const hasDescendant = n.reports.length > 0;
|
|
29171
|
+
return hasRole || hasDescendant;
|
|
29172
|
+
});
|
|
29173
|
+
}
|
|
29174
|
+
return augmentTree(globalTree);
|
|
29175
|
+
}
|
|
29176
|
+
var init_project_agent_roles = __esm(() => {
|
|
29177
|
+
init_database();
|
|
29178
|
+
init_agents();
|
|
29179
|
+
});
|
|
29180
|
+
|
|
29181
|
+
// src/db/patrol.ts
|
|
29182
|
+
var exports_patrol = {};
|
|
29183
|
+
__export(exports_patrol, {
|
|
29184
|
+
patrolTasks: () => patrolTasks,
|
|
29185
|
+
getReviewQueue: () => getReviewQueue
|
|
29186
|
+
});
|
|
29187
|
+
function rowToTask3(row) {
|
|
29188
|
+
return {
|
|
29189
|
+
...row,
|
|
29190
|
+
status: row.status,
|
|
29191
|
+
priority: row.priority,
|
|
29192
|
+
tags: JSON.parse(row.tags || "[]"),
|
|
29193
|
+
metadata: JSON.parse(row.metadata || "{}"),
|
|
29194
|
+
requires_approval: Boolean(row.requires_approval)
|
|
29195
|
+
};
|
|
29196
|
+
}
|
|
29197
|
+
function patrolTasks(opts, db) {
|
|
29198
|
+
const d = db || getDatabase();
|
|
29199
|
+
const stuckMinutes = opts?.stuck_minutes || 60;
|
|
29200
|
+
const confidenceThreshold = opts?.confidence_threshold || 0.5;
|
|
29201
|
+
const issues = [];
|
|
29202
|
+
let projectFilter = "";
|
|
29203
|
+
const projectParams = [];
|
|
29204
|
+
if (opts?.project_id) {
|
|
29205
|
+
projectFilter = " AND project_id = ?";
|
|
29206
|
+
projectParams.push(opts.project_id);
|
|
29207
|
+
}
|
|
29208
|
+
const stuckCutoff = new Date(Date.now() - stuckMinutes * 60 * 1000).toISOString();
|
|
29209
|
+
const stuckRows = d.query(`SELECT * FROM tasks WHERE status = 'in_progress' AND updated_at < ?${projectFilter} ORDER BY updated_at ASC`).all(stuckCutoff, ...projectParams);
|
|
29210
|
+
for (const row of stuckRows) {
|
|
29211
|
+
const task = rowToTask3(row);
|
|
29212
|
+
const minutesStuck = Math.round((Date.now() - new Date(task.updated_at).getTime()) / 60000);
|
|
29213
|
+
issues.push({
|
|
29214
|
+
type: "stuck",
|
|
29215
|
+
task_id: task.id,
|
|
29216
|
+
task_title: task.title,
|
|
29217
|
+
severity: minutesStuck > 480 ? "critical" : minutesStuck > 120 ? "high" : "medium",
|
|
29218
|
+
detail: `In progress for ${minutesStuck} minutes without update`
|
|
29219
|
+
});
|
|
29220
|
+
}
|
|
29221
|
+
const lowConfRows = d.query(`SELECT * FROM tasks WHERE status = 'completed' AND confidence IS NOT NULL AND confidence < ?${projectFilter} ORDER BY confidence ASC`).all(confidenceThreshold, ...projectParams);
|
|
29222
|
+
for (const row of lowConfRows) {
|
|
29223
|
+
const task = rowToTask3(row);
|
|
29224
|
+
issues.push({
|
|
29225
|
+
type: "low_confidence",
|
|
29226
|
+
task_id: task.id,
|
|
29227
|
+
task_title: task.title,
|
|
29228
|
+
severity: (task.confidence ?? 0) < 0.3 ? "high" : "medium",
|
|
29229
|
+
detail: `Completed with confidence ${task.confidence} (threshold: ${confidenceThreshold})`
|
|
29230
|
+
});
|
|
29231
|
+
}
|
|
29232
|
+
const orphanedRows = d.query(`SELECT * FROM tasks WHERE status = 'pending' AND project_id IS NULL AND agent_id IS NULL AND assigned_to IS NULL ORDER BY created_at ASC`).all();
|
|
29233
|
+
for (const row of orphanedRows) {
|
|
29234
|
+
const task = rowToTask3(row);
|
|
29235
|
+
issues.push({
|
|
29236
|
+
type: "orphaned",
|
|
29237
|
+
task_id: task.id,
|
|
29238
|
+
task_title: task.title,
|
|
29239
|
+
severity: "low",
|
|
29240
|
+
detail: "Pending task with no project, no agent, and no assignee"
|
|
29241
|
+
});
|
|
29242
|
+
}
|
|
29243
|
+
const needsReviewRows = d.query(`SELECT * FROM tasks WHERE status = 'completed' AND requires_approval = 1 AND approved_by IS NULL${projectFilter} ORDER BY completed_at DESC`).all(...projectParams);
|
|
29244
|
+
for (const row of needsReviewRows) {
|
|
29245
|
+
const task = rowToTask3(row);
|
|
29246
|
+
issues.push({
|
|
29247
|
+
type: "needs_review",
|
|
29248
|
+
task_id: task.id,
|
|
29249
|
+
task_title: task.title,
|
|
29250
|
+
severity: "medium",
|
|
29251
|
+
detail: "Completed but requires approval \u2014 not yet reviewed"
|
|
29252
|
+
});
|
|
29253
|
+
}
|
|
29254
|
+
const pendingWithDeps = d.query(`SELECT t.* FROM tasks t
|
|
29255
|
+
WHERE t.status = 'pending'${projectFilter}
|
|
29256
|
+
AND t.id IN (SELECT task_id FROM task_dependencies)`).all(...projectParams);
|
|
29257
|
+
for (const row of pendingWithDeps) {
|
|
29258
|
+
const deps = d.query(`SELECT d.depends_on, t.status FROM task_dependencies d
|
|
29259
|
+
JOIN tasks t ON t.id = d.depends_on
|
|
29260
|
+
WHERE d.task_id = ?`).all(row.id);
|
|
29261
|
+
if (deps.length > 0 && deps.every((dep) => dep.status === "failed" || dep.status === "cancelled")) {
|
|
29262
|
+
const task = rowToTask3(row);
|
|
29263
|
+
issues.push({
|
|
29264
|
+
type: "zombie_blocked",
|
|
29265
|
+
task_id: task.id,
|
|
29266
|
+
task_title: task.title,
|
|
29267
|
+
severity: "high",
|
|
29268
|
+
detail: `Blocked by ${deps.length} task(s) that are all failed/cancelled \u2014 will never unblock`
|
|
29269
|
+
});
|
|
29270
|
+
}
|
|
29271
|
+
}
|
|
29272
|
+
const severityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
29273
|
+
issues.sort((a, b) => severityOrder[a.severity] - severityOrder[b.severity]);
|
|
29274
|
+
return {
|
|
29275
|
+
issues,
|
|
29276
|
+
total_issues: issues.length,
|
|
29277
|
+
scanned_at: new Date().toISOString()
|
|
29278
|
+
};
|
|
29279
|
+
}
|
|
29280
|
+
function getReviewQueue(opts, db) {
|
|
29281
|
+
const d = db || getDatabase();
|
|
29282
|
+
let sql = `SELECT * FROM tasks WHERE status = 'completed' AND (
|
|
29283
|
+
(requires_approval = 1 AND approved_by IS NULL)
|
|
29284
|
+
OR (confidence IS NOT NULL AND confidence < 0.5)
|
|
29285
|
+
)`;
|
|
29286
|
+
const params = [];
|
|
29287
|
+
if (opts?.project_id) {
|
|
29288
|
+
sql += " AND project_id = ?";
|
|
29289
|
+
params.push(opts.project_id);
|
|
29290
|
+
}
|
|
29291
|
+
sql += " ORDER BY CASE WHEN requires_approval = 1 AND approved_by IS NULL THEN 0 ELSE 1 END, confidence ASC";
|
|
29292
|
+
if (opts?.limit) {
|
|
29293
|
+
sql += " LIMIT ?";
|
|
29294
|
+
params.push(opts.limit);
|
|
29295
|
+
}
|
|
29296
|
+
return d.query(sql).all(...params).map(rowToTask3);
|
|
29297
|
+
}
|
|
29298
|
+
var init_patrol = __esm(() => {
|
|
29299
|
+
init_database();
|
|
29300
|
+
});
|
|
29301
|
+
|
|
29302
|
+
// src/db/agent-metrics.ts
|
|
29303
|
+
var exports_agent_metrics = {};
|
|
29304
|
+
__export(exports_agent_metrics, {
|
|
29305
|
+
scoreTask: () => scoreTask,
|
|
29306
|
+
getLeaderboard: () => getLeaderboard,
|
|
29307
|
+
getAgentMetrics: () => getAgentMetrics
|
|
29308
|
+
});
|
|
29309
|
+
function getAgentMetrics(agentId, opts, db) {
|
|
29310
|
+
const d = db || getDatabase();
|
|
29311
|
+
const agent = d.query("SELECT id, name FROM agents WHERE id = ? OR LOWER(name) = LOWER(?)").get(agentId, agentId);
|
|
29312
|
+
if (!agent)
|
|
29313
|
+
return null;
|
|
29314
|
+
let projectFilter = "";
|
|
29315
|
+
const params = [agent.id, agent.id];
|
|
29316
|
+
if (opts?.project_id) {
|
|
29317
|
+
projectFilter = " AND project_id = ?";
|
|
29318
|
+
params.push(opts.project_id);
|
|
29319
|
+
}
|
|
29320
|
+
const completed = d.query(`SELECT COUNT(*) as count FROM tasks WHERE (agent_id = ? OR assigned_to = ?) AND status = 'completed'${projectFilter}`).get(...params).count;
|
|
29321
|
+
const failed = d.query(`SELECT COUNT(*) as count FROM tasks WHERE (agent_id = ? OR assigned_to = ?) AND status = 'failed'${projectFilter}`).get(...params).count;
|
|
29322
|
+
const inProgress = d.query(`SELECT COUNT(*) as count FROM tasks WHERE (agent_id = ? OR assigned_to = ?) AND status = 'in_progress'${projectFilter}`).get(...params).count;
|
|
29323
|
+
const total = completed + failed;
|
|
29324
|
+
const completionRate = total > 0 ? completed / total : 0;
|
|
29325
|
+
const avgTime = d.query(`SELECT AVG(
|
|
29326
|
+
(julianday(completed_at) - julianday(created_at)) * 24 * 60
|
|
29327
|
+
) as avg_minutes
|
|
29328
|
+
FROM tasks
|
|
29329
|
+
WHERE (agent_id = ? OR assigned_to = ?) AND status = 'completed' AND completed_at IS NOT NULL${projectFilter}`).get(...params);
|
|
29330
|
+
const avgConf = d.query(`SELECT AVG(confidence) as avg_confidence
|
|
29331
|
+
FROM tasks
|
|
29332
|
+
WHERE (agent_id = ? OR assigned_to = ?) AND status = 'completed' AND confidence IS NOT NULL${projectFilter}`).get(...params);
|
|
29333
|
+
const reviewTasks = d.query(`SELECT metadata FROM tasks
|
|
29334
|
+
WHERE (agent_id = ? OR assigned_to = ?) AND status = 'completed'${projectFilter}
|
|
29335
|
+
AND metadata LIKE '%_review_score%'`).all(...params);
|
|
29336
|
+
let reviewScoreAvg = null;
|
|
29337
|
+
if (reviewTasks.length > 0) {
|
|
29338
|
+
let total2 = 0;
|
|
29339
|
+
let count = 0;
|
|
29340
|
+
for (const row of reviewTasks) {
|
|
29341
|
+
try {
|
|
29342
|
+
const meta = JSON.parse(row.metadata);
|
|
29343
|
+
if (typeof meta._review_score === "number") {
|
|
29344
|
+
total2 += meta._review_score;
|
|
29345
|
+
count++;
|
|
29346
|
+
}
|
|
29347
|
+
} catch {}
|
|
29348
|
+
}
|
|
29349
|
+
if (count > 0)
|
|
29350
|
+
reviewScoreAvg = total2 / count;
|
|
29351
|
+
}
|
|
29352
|
+
const speedScore = avgTime?.avg_minutes != null ? Math.max(0, 1 - avgTime.avg_minutes / (60 * 24)) : 0.5;
|
|
29353
|
+
const confidenceScore = avgConf?.avg_confidence ?? 0.5;
|
|
29354
|
+
const volumeScore = Math.min(1, completed / 50);
|
|
29355
|
+
const compositeScore = completionRate * 0.3 + speedScore * 0.2 + confidenceScore * 0.3 + volumeScore * 0.2;
|
|
29356
|
+
return {
|
|
29357
|
+
agent_id: agent.id,
|
|
29358
|
+
agent_name: agent.name,
|
|
29359
|
+
tasks_completed: completed,
|
|
29360
|
+
tasks_failed: failed,
|
|
29361
|
+
tasks_in_progress: inProgress,
|
|
29362
|
+
completion_rate: Math.round(completionRate * 1000) / 1000,
|
|
29363
|
+
avg_completion_minutes: avgTime?.avg_minutes != null ? Math.round(avgTime.avg_minutes * 10) / 10 : null,
|
|
29364
|
+
avg_confidence: avgConf?.avg_confidence != null ? Math.round(avgConf.avg_confidence * 1000) / 1000 : null,
|
|
29365
|
+
review_score_avg: reviewScoreAvg != null ? Math.round(reviewScoreAvg * 1000) / 1000 : null,
|
|
29366
|
+
composite_score: Math.round(compositeScore * 1000) / 1000
|
|
29367
|
+
};
|
|
29368
|
+
}
|
|
29369
|
+
function getLeaderboard(opts, db) {
|
|
29370
|
+
const d = db || getDatabase();
|
|
29371
|
+
const agents = d.query("SELECT id FROM agents ORDER BY name").all();
|
|
29372
|
+
const entries = [];
|
|
29373
|
+
for (const agent of agents) {
|
|
29374
|
+
const metrics = getAgentMetrics(agent.id, { project_id: opts?.project_id }, d);
|
|
29375
|
+
if (metrics && (metrics.tasks_completed > 0 || metrics.tasks_failed > 0 || metrics.tasks_in_progress > 0)) {
|
|
29376
|
+
entries.push(metrics);
|
|
29377
|
+
}
|
|
29378
|
+
}
|
|
29379
|
+
entries.sort((a, b) => b.composite_score - a.composite_score);
|
|
29380
|
+
const limit = opts?.limit || 20;
|
|
29381
|
+
return entries.slice(0, limit).map((entry, idx) => ({
|
|
29382
|
+
...entry,
|
|
29383
|
+
rank: idx + 1
|
|
29384
|
+
}));
|
|
29385
|
+
}
|
|
29386
|
+
function scoreTask(taskId, score, reviewerId, db) {
|
|
29387
|
+
const d = db || getDatabase();
|
|
29388
|
+
if (score < 0 || score > 1)
|
|
29389
|
+
throw new Error("Score must be between 0 and 1");
|
|
29390
|
+
const task = d.query("SELECT metadata FROM tasks WHERE id = ?").get(taskId);
|
|
29391
|
+
if (!task)
|
|
29392
|
+
throw new Error(`Task not found: ${taskId}`);
|
|
29393
|
+
const metadata = JSON.parse(task.metadata || "{}");
|
|
29394
|
+
metadata._review_score = score;
|
|
29395
|
+
if (reviewerId)
|
|
29396
|
+
metadata._reviewed_by = reviewerId;
|
|
29397
|
+
metadata._reviewed_at = now();
|
|
29398
|
+
d.run("UPDATE tasks SET metadata = ?, updated_at = ? WHERE id = ?", [JSON.stringify(metadata), now(), taskId]);
|
|
29399
|
+
}
|
|
29400
|
+
var init_agent_metrics = __esm(() => {
|
|
29401
|
+
init_database();
|
|
29402
|
+
});
|
|
29403
|
+
|
|
28448
29404
|
// src/mcp/tools/task-rel-tools.ts
|
|
28449
29405
|
function registerTaskRelTools(server, ctx) {
|
|
28450
29406
|
const { shouldRegisterTool, resolveId, formatError, formatTask } = ctx;
|
|
@@ -28459,7 +29415,7 @@ function registerTaskRelTools(server, ctx) {
|
|
|
28459
29415
|
next_steps: exports_external2.array(exports_external2.string()).optional().describe("Recommended next actions")
|
|
28460
29416
|
}, async ({ agent_id, project_id, summary, completed, in_progress, blockers, next_steps }) => {
|
|
28461
29417
|
try {
|
|
28462
|
-
const { createHandoff: createHandoff2 } = (()
|
|
29418
|
+
const { createHandoff: createHandoff2 } = (init_handoffs(), __toCommonJS(exports_handoffs));
|
|
28463
29419
|
const handoff = createHandoff2({
|
|
28464
29420
|
agent_id,
|
|
28465
29421
|
project_id: project_id ? resolveId(project_id, "projects") : undefined,
|
|
@@ -28481,7 +29437,7 @@ function registerTaskRelTools(server, ctx) {
|
|
|
28481
29437
|
project_id: exports_external2.string().optional().describe("Filter by project")
|
|
28482
29438
|
}, async ({ agent_id, project_id }) => {
|
|
28483
29439
|
try {
|
|
28484
|
-
const { getLatestHandoff: getLatestHandoff2 } = (()
|
|
29440
|
+
const { getLatestHandoff: getLatestHandoff2 } = (init_handoffs(), __toCommonJS(exports_handoffs));
|
|
28485
29441
|
const handoff = getLatestHandoff2(agent_id, project_id ? resolveId(project_id, "projects") : undefined);
|
|
28486
29442
|
if (!handoff)
|
|
28487
29443
|
return { content: [{ type: "text", text: "No handoffs found." }] };
|
|
@@ -28512,7 +29468,7 @@ function registerTaskRelTools(server, ctx) {
|
|
|
28512
29468
|
created_by: exports_external2.string().optional().describe("Agent ID who created this relationship")
|
|
28513
29469
|
}, async ({ source_task_id, target_task_id, relationship_type, created_by }) => {
|
|
28514
29470
|
try {
|
|
28515
|
-
const { addTaskRelationship: addTaskRelationship2 } = (()
|
|
29471
|
+
const { addTaskRelationship: addTaskRelationship2 } = (init_task_relationships(), __toCommonJS(exports_task_relationships));
|
|
28516
29472
|
const rel = addTaskRelationship2({
|
|
28517
29473
|
source_task_id: resolveId(source_task_id),
|
|
28518
29474
|
target_task_id: resolveId(target_task_id),
|
|
@@ -28533,7 +29489,7 @@ function registerTaskRelTools(server, ctx) {
|
|
|
28533
29489
|
relationship_type: exports_external2.enum(["related_to", "conflicts_with", "similar_to", "duplicates", "supersedes", "modifies_same_file"]).optional()
|
|
28534
29490
|
}, async ({ id, source_task_id, target_task_id, relationship_type }) => {
|
|
28535
29491
|
try {
|
|
28536
|
-
const { removeTaskRelationship: removeTaskRelationship2, removeTaskRelationshipByPair: removeTaskRelationshipByPair2 } = (()
|
|
29492
|
+
const { removeTaskRelationship: removeTaskRelationship2, removeTaskRelationshipByPair: removeTaskRelationshipByPair2 } = (init_task_relationships(), __toCommonJS(exports_task_relationships));
|
|
28537
29493
|
let removed = false;
|
|
28538
29494
|
if (id) {
|
|
28539
29495
|
removed = removeTaskRelationship2(id);
|
|
@@ -28554,7 +29510,7 @@ function registerTaskRelTools(server, ctx) {
|
|
|
28554
29510
|
relationship_type: exports_external2.enum(["related_to", "conflicts_with", "similar_to", "duplicates", "supersedes", "modifies_same_file"]).optional()
|
|
28555
29511
|
}, async ({ task_id, relationship_type }) => {
|
|
28556
29512
|
try {
|
|
28557
|
-
const { getTaskRelationships: getTaskRelationships2 } = (()
|
|
29513
|
+
const { getTaskRelationships: getTaskRelationships2 } = (init_task_relationships(), __toCommonJS(exports_task_relationships));
|
|
28558
29514
|
const rels = getTaskRelationships2(resolveId(task_id), relationship_type);
|
|
28559
29515
|
if (rels.length === 0)
|
|
28560
29516
|
return { content: [{ type: "text", text: "No relationships found." }] };
|
|
@@ -28571,7 +29527,7 @@ function registerTaskRelTools(server, ctx) {
|
|
|
28571
29527
|
task_id: exports_external2.string().describe("Task ID to detect file relationships for")
|
|
28572
29528
|
}, async ({ task_id }) => {
|
|
28573
29529
|
try {
|
|
28574
|
-
const { autoDetectFileRelationships: autoDetectFileRelationships2 } = (()
|
|
29530
|
+
const { autoDetectFileRelationships: autoDetectFileRelationships2 } = (init_task_relationships(), __toCommonJS(exports_task_relationships));
|
|
28575
29531
|
const created = autoDetectFileRelationships2(resolveId(task_id));
|
|
28576
29532
|
return { content: [{ type: "text", text: created.length > 0 ? `Created ${created.length} file relationship(s).` : "No file overlaps detected." }] };
|
|
28577
29533
|
} catch (e) {
|
|
@@ -28582,8 +29538,8 @@ function registerTaskRelTools(server, ctx) {
|
|
|
28582
29538
|
if (shouldRegisterTool("sync_kg")) {
|
|
28583
29539
|
server.tool("sync_kg", "Sync all existing relationships into the knowledge graph edges table. Idempotent.", {}, async () => {
|
|
28584
29540
|
try {
|
|
28585
|
-
const { syncKgEdges } = (()
|
|
28586
|
-
const result =
|
|
29541
|
+
const { syncKgEdges: syncKgEdges2 } = (init_kg(), __toCommonJS(exports_kg));
|
|
29542
|
+
const result = syncKgEdges2();
|
|
28587
29543
|
return { content: [{ type: "text", text: `Knowledge graph synced: ${result.synced} edge(s) processed.` }] };
|
|
28588
29544
|
} catch (e) {
|
|
28589
29545
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -28599,8 +29555,8 @@ function registerTaskRelTools(server, ctx) {
|
|
|
28599
29555
|
limit: exports_external2.number().optional().describe("Max results")
|
|
28600
29556
|
}, async ({ entity_id, relation_type, entity_type, direction, limit }) => {
|
|
28601
29557
|
try {
|
|
28602
|
-
const { getRelated } = (()
|
|
28603
|
-
const edges =
|
|
29558
|
+
const { getRelated: getRelated2 } = (init_kg(), __toCommonJS(exports_kg));
|
|
29559
|
+
const edges = getRelated2(entity_id, { relation_type, entity_type, direction, limit });
|
|
28604
29560
|
if (edges.length === 0)
|
|
28605
29561
|
return { content: [{ type: "text", text: "No related entities found." }] };
|
|
28606
29562
|
const lines = edges.map((e) => `${e.source_id.slice(0, 12)}(${e.source_type}) --[${e.relation_type}]--> ${e.target_id.slice(0, 12)}(${e.target_type})`);
|
|
@@ -28619,8 +29575,8 @@ function registerTaskRelTools(server, ctx) {
|
|
|
28619
29575
|
relation_types: exports_external2.array(exports_external2.string()).optional().describe("Filter by relation types")
|
|
28620
29576
|
}, async ({ source_id, target_id, max_depth, relation_types }) => {
|
|
28621
29577
|
try {
|
|
28622
|
-
const { findPath } = (()
|
|
28623
|
-
const paths =
|
|
29578
|
+
const { findPath: findPath2 } = (init_kg(), __toCommonJS(exports_kg));
|
|
29579
|
+
const paths = findPath2(source_id, target_id, { max_depth, relation_types });
|
|
28624
29580
|
if (paths.length === 0)
|
|
28625
29581
|
return { content: [{ type: "text", text: "No path found." }] };
|
|
28626
29582
|
const lines = paths.map((path, i) => {
|
|
@@ -28644,8 +29600,8 @@ function registerTaskRelTools(server, ctx) {
|
|
|
28644
29600
|
relation_types: exports_external2.array(exports_external2.string()).optional().describe("Filter by relation types")
|
|
28645
29601
|
}, async ({ entity_id, max_depth, relation_types }) => {
|
|
28646
29602
|
try {
|
|
28647
|
-
const { getImpactAnalysis } = (()
|
|
28648
|
-
const impact =
|
|
29603
|
+
const { getImpactAnalysis: getImpactAnalysis2 } = (init_kg(), __toCommonJS(exports_kg));
|
|
29604
|
+
const impact = getImpactAnalysis2(entity_id, { max_depth, relation_types });
|
|
28649
29605
|
if (impact.length === 0)
|
|
28650
29606
|
return { content: [{ type: "text", text: "No downstream impact detected." }] };
|
|
28651
29607
|
const byDepth = new Map;
|
|
@@ -28675,8 +29631,8 @@ Depth ${depth}:`);
|
|
|
28675
29631
|
limit: exports_external2.number().optional().describe("Max results (default: 20)")
|
|
28676
29632
|
}, async ({ project_id, limit }) => {
|
|
28677
29633
|
try {
|
|
28678
|
-
const { getCriticalPath } = (()
|
|
28679
|
-
const result =
|
|
29634
|
+
const { getCriticalPath: getCriticalPath2 } = (init_kg(), __toCommonJS(exports_kg));
|
|
29635
|
+
const result = getCriticalPath2({ project_id: project_id ? resolveId(project_id, "projects") : undefined, limit });
|
|
28680
29636
|
if (result.length === 0)
|
|
28681
29637
|
return { content: [{ type: "text", text: "No critical path data. Run sync_kg first to populate the knowledge graph." }] };
|
|
28682
29638
|
const lines = result.map((r, i) => `${i + 1}. ${r.task_id.slice(0, 8)} blocks ${r.blocking_count} task(s), max depth ${r.depth}`);
|
|
@@ -28696,13 +29652,13 @@ ${lines.join(`
|
|
|
28696
29652
|
is_lead: exports_external2.coerce.boolean().optional().describe("Whether this agent is the project lead for this role")
|
|
28697
29653
|
}, async ({ project_id, agent_name, role, is_lead }) => {
|
|
28698
29654
|
try {
|
|
28699
|
-
const { setProjectAgentRole } = (()
|
|
28700
|
-
const { getAgentByName: getAgentByName2 } = (()
|
|
29655
|
+
const { setProjectAgentRole: setProjectAgentRole2 } = (init_project_agent_roles(), __toCommonJS(exports_project_agent_roles));
|
|
29656
|
+
const { getAgentByName: getAgentByName2 } = (init_agents(), __toCommonJS(exports_agents));
|
|
28701
29657
|
const agent = getAgentByName2(agent_name);
|
|
28702
29658
|
if (!agent)
|
|
28703
29659
|
return { content: [{ type: "text", text: `Agent not found: ${agent_name}` }], isError: true };
|
|
28704
29660
|
const pid = resolveId(project_id, "projects");
|
|
28705
|
-
const result =
|
|
29661
|
+
const result = setProjectAgentRole2(pid, agent.id, role, is_lead ?? false);
|
|
28706
29662
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
28707
29663
|
} catch (e) {
|
|
28708
29664
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -28732,9 +29688,9 @@ ${lines.join(`
|
|
|
28732
29688
|
}).join(`
|
|
28733
29689
|
`);
|
|
28734
29690
|
};
|
|
28735
|
-
const { getProjectOrgChart } = (()
|
|
29691
|
+
const { getProjectOrgChart: getProjectOrgChart2 } = (init_project_agent_roles(), __toCommonJS(exports_project_agent_roles));
|
|
28736
29692
|
const pid = resolveId(project_id, "projects");
|
|
28737
|
-
const tree =
|
|
29693
|
+
const tree = getProjectOrgChart2(pid, { filter_to_project });
|
|
28738
29694
|
if (format === "json") {
|
|
28739
29695
|
return { content: [{ type: "text", text: JSON.stringify(tree, null, 2) }] };
|
|
28740
29696
|
}
|
|
@@ -28752,9 +29708,9 @@ ${lines.join(`
|
|
|
28752
29708
|
project_id: exports_external2.string().describe("Project ID")
|
|
28753
29709
|
}, async ({ project_id }) => {
|
|
28754
29710
|
try {
|
|
28755
|
-
const { listProjectAgentRoles } = (()
|
|
29711
|
+
const { listProjectAgentRoles: listProjectAgentRoles2 } = (init_project_agent_roles(), __toCommonJS(exports_project_agent_roles));
|
|
28756
29712
|
const pid = resolveId(project_id, "projects");
|
|
28757
|
-
const roles =
|
|
29713
|
+
const roles = listProjectAgentRoles2(pid);
|
|
28758
29714
|
return { content: [{ type: "text", text: JSON.stringify(roles, null, 2) }] };
|
|
28759
29715
|
} catch (e) {
|
|
28760
29716
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -28768,7 +29724,7 @@ ${lines.join(`
|
|
|
28768
29724
|
limit: exports_external2.number().optional().describe("Max results")
|
|
28769
29725
|
}, async ({ capabilities, min_score, limit }) => {
|
|
28770
29726
|
try {
|
|
28771
|
-
const { getCapableAgents: getCapableAgents2 } = (()
|
|
29727
|
+
const { getCapableAgents: getCapableAgents2 } = (init_agents(), __toCommonJS(exports_agents));
|
|
28772
29728
|
const results = getCapableAgents2(capabilities, { min_score, limit });
|
|
28773
29729
|
if (results.length === 0)
|
|
28774
29730
|
return { content: [{ type: "text", text: "No agents match the given capabilities." }] };
|
|
@@ -28787,8 +29743,8 @@ ${lines.join(`
|
|
|
28787
29743
|
project_id: exports_external2.string().optional().describe("Filter by project")
|
|
28788
29744
|
}, async ({ stuck_minutes, confidence_threshold, project_id }) => {
|
|
28789
29745
|
try {
|
|
28790
|
-
const { patrolTasks } = (()
|
|
28791
|
-
const result =
|
|
29746
|
+
const { patrolTasks: patrolTasks2 } = (init_patrol(), __toCommonJS(exports_patrol));
|
|
29747
|
+
const result = patrolTasks2({
|
|
28792
29748
|
stuck_minutes,
|
|
28793
29749
|
confidence_threshold,
|
|
28794
29750
|
project_id: project_id ? resolveId(project_id, "projects") : undefined
|
|
@@ -28814,8 +29770,8 @@ ${lines.join(`
|
|
|
28814
29770
|
limit: exports_external2.number().optional().describe("Max results (default: all)")
|
|
28815
29771
|
}, async ({ project_id, limit }) => {
|
|
28816
29772
|
try {
|
|
28817
|
-
const { getReviewQueue } = (()
|
|
28818
|
-
const tasks =
|
|
29773
|
+
const { getReviewQueue: getReviewQueue2 } = (init_patrol(), __toCommonJS(exports_patrol));
|
|
29774
|
+
const tasks = getReviewQueue2({
|
|
28819
29775
|
project_id: project_id ? resolveId(project_id, "projects") : undefined,
|
|
28820
29776
|
limit
|
|
28821
29777
|
});
|
|
@@ -28841,8 +29797,8 @@ ${lines.join(`
|
|
|
28841
29797
|
reviewer_id: exports_external2.string().optional().describe("Agent ID of reviewer")
|
|
28842
29798
|
}, async ({ task_id, score, reviewer_id }) => {
|
|
28843
29799
|
try {
|
|
28844
|
-
const { scoreTask } = (()
|
|
28845
|
-
|
|
29800
|
+
const { scoreTask: scoreTask2 } = (init_agent_metrics(), __toCommonJS(exports_agent_metrics));
|
|
29801
|
+
scoreTask2(resolveId(task_id), score, reviewer_id);
|
|
28846
29802
|
return { content: [{ type: "text", text: `Task ${task_id.slice(0, 8)} scored: ${score}` }] };
|
|
28847
29803
|
} catch (e) {
|
|
28848
29804
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -28859,7 +29815,7 @@ ${lines.join(`
|
|
|
28859
29815
|
notes: exports_external2.string().optional().describe("Notes about what was done")
|
|
28860
29816
|
}, async ({ task_id, minutes, agent_id, started_at, ended_at, notes }) => {
|
|
28861
29817
|
try {
|
|
28862
|
-
const { logTime: logTime2 } = (()
|
|
29818
|
+
const { logTime: logTime2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
28863
29819
|
logTime2({ task_id: resolveId(task_id), minutes, agent_id, started_at, ended_at, notes });
|
|
28864
29820
|
return { content: [{ type: "text", text: `Logged ${minutes} min on task ${task_id.slice(0, 8)}` }] };
|
|
28865
29821
|
} catch (e) {
|
|
@@ -28874,7 +29830,7 @@ ${lines.join(`
|
|
|
28874
29830
|
since: exports_external2.string().optional().describe("ISO date \u2014 only tasks completed after this date")
|
|
28875
29831
|
}, async ({ project_id, agent_id, since }) => {
|
|
28876
29832
|
try {
|
|
28877
|
-
const { getTimeReport: getTimeReport2 } = (()
|
|
29833
|
+
const { getTimeReport: getTimeReport2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
28878
29834
|
const report = getTimeReport2({ project_id: project_id ? resolveId(project_id, "projects") : undefined, agent_id, since });
|
|
28879
29835
|
if (report.length === 0)
|
|
28880
29836
|
return { content: [{ type: "text", text: "No completed tasks found." }] };
|
|
@@ -28898,7 +29854,7 @@ ${lines.join(`
|
|
|
28898
29854
|
agent_id: exports_external2.string().optional().describe("Agent subscribing (defaults to context agent)")
|
|
28899
29855
|
}, async ({ task_id, agent_id }) => {
|
|
28900
29856
|
try {
|
|
28901
|
-
const { watchTask: watchTask2 } = (()
|
|
29857
|
+
const { watchTask: watchTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
28902
29858
|
watchTask2(resolveId(task_id), agent_id || "");
|
|
28903
29859
|
return { content: [{ type: "text", text: `Now watching task ${task_id.slice(0, 8)}` }] };
|
|
28904
29860
|
} catch (e) {
|
|
@@ -28912,7 +29868,7 @@ ${lines.join(`
|
|
|
28912
29868
|
agent_id: exports_external2.string().optional().describe("Agent unsubscribing (defaults to context agent)")
|
|
28913
29869
|
}, async ({ task_id, agent_id }) => {
|
|
28914
29870
|
try {
|
|
28915
|
-
const { unwatchTask: unwatchTask2 } = (()
|
|
29871
|
+
const { unwatchTask: unwatchTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
28916
29872
|
unwatchTask2(resolveId(task_id), agent_id || "");
|
|
28917
29873
|
return { content: [{ type: "text", text: `Stopped watching task ${task_id.slice(0, 8)}` }] };
|
|
28918
29874
|
} catch (e) {
|
|
@@ -28925,7 +29881,7 @@ ${lines.join(`
|
|
|
28925
29881
|
task_id: exports_external2.string().describe("Task ID")
|
|
28926
29882
|
}, async ({ task_id }) => {
|
|
28927
29883
|
try {
|
|
28928
|
-
const { getTaskWatchers: getTaskWatchers2 } = (()
|
|
29884
|
+
const { getTaskWatchers: getTaskWatchers2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
28929
29885
|
const watchers = getTaskWatchers2(resolveId(task_id));
|
|
28930
29886
|
if (watchers.length === 0)
|
|
28931
29887
|
return { content: [{ type: "text", text: "No watchers." }] };
|
|
@@ -28944,15 +29900,15 @@ ${lines.join(`
|
|
|
28944
29900
|
agent_id: exports_external2.string().optional().describe("Filter by assignee")
|
|
28945
29901
|
}, async ({ project_id, plan_id, task_list_id, since, agent_id }) => {
|
|
28946
29902
|
try {
|
|
28947
|
-
const { listTasks: listTasks3 } = (()
|
|
28948
|
-
const { patrolTasks } = (()
|
|
29903
|
+
const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
29904
|
+
const { patrolTasks: patrolTasks2 } = (init_patrol(), __toCommonJS(exports_patrol));
|
|
28949
29905
|
const completed = listTasks3({ status: "completed", project_id, plan_id, task_list_id, assigned_to: agent_id, limit: 500 }, undefined);
|
|
28950
29906
|
const filtered = since ? completed.filter((t) => t.completed_at && t.completed_at >= since) : completed;
|
|
28951
29907
|
const total = filtered.length;
|
|
28952
29908
|
const lowConf = filtered.filter((t) => t.confidence != null && t.confidence < 0.7).length;
|
|
28953
29909
|
const withEstimate = filtered.filter((t) => t.estimated_minutes != null && t.actual_minutes != null);
|
|
28954
29910
|
const avgDiff = withEstimate.length > 0 ? withEstimate.reduce((acc, t) => acc + (t.actual_minutes - t.estimated_minutes), 0) / withEstimate.length : 0;
|
|
28955
|
-
const patrolResult =
|
|
29911
|
+
const patrolResult = patrolTasks2({ project_id: project_id ? resolveId(project_id, "projects") : undefined });
|
|
28956
29912
|
const stuck = patrolResult.issues.filter((i) => i.type === "stuck").length;
|
|
28957
29913
|
const lines = [
|
|
28958
29914
|
`Retro (${total} completed tasks${since ? ` since ${since}` : ""})`,
|
|
@@ -28973,7 +29929,7 @@ ${lines.join(`
|
|
|
28973
29929
|
limit: exports_external2.number().optional().describe("Max results (default: 20)")
|
|
28974
29930
|
}, async ({ project_id, limit }) => {
|
|
28975
29931
|
try {
|
|
28976
|
-
const { listTasks: listTasks3 } = (()
|
|
29932
|
+
const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
28977
29933
|
const tasks = listTasks3({ status: "pending", project_id, assigned_to: "", limit: limit || 20 }, undefined);
|
|
28978
29934
|
if (tasks.length === 0)
|
|
28979
29935
|
return { content: [{ type: "text", text: "Inbox is empty." }] };
|
|
@@ -28992,8 +29948,8 @@ ${lines.join(`
|
|
|
28992
29948
|
project_id: exports_external2.string().optional().describe("Filter by project")
|
|
28993
29949
|
}, async ({ agent_id, project_id }) => {
|
|
28994
29950
|
try {
|
|
28995
|
-
const { getAgentMetrics } = (()
|
|
28996
|
-
const metrics =
|
|
29951
|
+
const { getAgentMetrics: getAgentMetrics2 } = (init_agent_metrics(), __toCommonJS(exports_agent_metrics));
|
|
29952
|
+
const metrics = getAgentMetrics2(agent_id, {
|
|
28997
29953
|
project_id: project_id ? resolveId(project_id, "projects") : undefined
|
|
28998
29954
|
});
|
|
28999
29955
|
if (!metrics)
|
|
@@ -29020,8 +29976,8 @@ ${lines.join(`
|
|
|
29020
29976
|
limit: exports_external2.number().optional().describe("Max entries (default: 20)")
|
|
29021
29977
|
}, async ({ project_id, limit }) => {
|
|
29022
29978
|
try {
|
|
29023
|
-
const { getLeaderboard } = (()
|
|
29024
|
-
const entries =
|
|
29979
|
+
const { getLeaderboard: getLeaderboard2 } = (init_agent_metrics(), __toCommonJS(exports_agent_metrics));
|
|
29980
|
+
const entries = getLeaderboard2({
|
|
29025
29981
|
project_id: project_id ? resolveId(project_id, "projects") : undefined,
|
|
29026
29982
|
limit
|
|
29027
29983
|
});
|
|
@@ -29981,6 +30937,361 @@ var init_machines2 = __esm(() => {
|
|
|
29981
30937
|
init_database();
|
|
29982
30938
|
});
|
|
29983
30939
|
|
|
30940
|
+
// src/mcp/tools/agents.ts
|
|
30941
|
+
function registerAgentTools(server, { shouldRegisterTool, resolveId, formatError, agentFocusMap, getAgentFocus }) {
|
|
30942
|
+
if (shouldRegisterTool("set_focus")) {
|
|
30943
|
+
server.tool("set_focus", "Focus this agent on a project. All list/search/status tools will default to this project.", {
|
|
30944
|
+
agent_id: exports_external2.string().describe("Agent ID or name"),
|
|
30945
|
+
project_id: exports_external2.string().optional().describe("Project to focus on. Omit to clear."),
|
|
30946
|
+
task_list_id: exports_external2.string().optional().describe("Task list to focus on")
|
|
30947
|
+
}, async ({ agent_id, project_id, task_list_id }) => {
|
|
30948
|
+
try {
|
|
30949
|
+
const resolvedProject = project_id ? resolveId(project_id, "projects") : undefined;
|
|
30950
|
+
const focus = { agent_id, project_id: resolvedProject, task_list_id };
|
|
30951
|
+
agentFocusMap.set(agent_id, focus);
|
|
30952
|
+
try {
|
|
30953
|
+
const agent = getAgentByName(agent_id) || getAgent(agent_id);
|
|
30954
|
+
if (agent) {
|
|
30955
|
+
const db = getDatabase();
|
|
30956
|
+
db.run("UPDATE agents SET active_project_id = ? WHERE id = ?", [resolvedProject || null, agent.id]);
|
|
30957
|
+
}
|
|
30958
|
+
} catch {}
|
|
30959
|
+
const projectName = resolvedProject ? ` (${resolvedProject.slice(0, 8)})` : "";
|
|
30960
|
+
return { content: [{ type: "text", text: `Focused on project${projectName}. Read tools will default to this scope. Pass explicit project_id to override.` }] };
|
|
30961
|
+
} catch (e) {
|
|
30962
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
30963
|
+
}
|
|
30964
|
+
});
|
|
30965
|
+
}
|
|
30966
|
+
if (shouldRegisterTool("get_focus")) {
|
|
30967
|
+
server.tool("get_focus", "Get the current focus for an agent.", { agent_id: exports_external2.string().describe("Agent ID or name") }, async ({ agent_id }) => {
|
|
30968
|
+
const focus = getAgentFocus(agent_id);
|
|
30969
|
+
if (!focus?.project_id) {
|
|
30970
|
+
return { content: [{ type: "text", text: "No focus set. Showing all projects." }] };
|
|
30971
|
+
}
|
|
30972
|
+
return { content: [{ type: "text", text: `Focused on project: ${focus.project_id}${focus.task_list_id ? `, task list: ${focus.task_list_id}` : ""}` }] };
|
|
30973
|
+
});
|
|
30974
|
+
}
|
|
30975
|
+
if (shouldRegisterTool("unfocus")) {
|
|
30976
|
+
server.tool("unfocus", "Clear focus \u2014 show all projects and tasks.", { agent_id: exports_external2.string().describe("Agent ID or name") }, async ({ agent_id }) => {
|
|
30977
|
+
agentFocusMap.delete(agent_id);
|
|
30978
|
+
try {
|
|
30979
|
+
const agent = getAgentByName(agent_id) || getAgent(agent_id);
|
|
30980
|
+
if (agent) {
|
|
30981
|
+
const db = getDatabase();
|
|
30982
|
+
db.run("UPDATE agents SET active_project_id = NULL WHERE id = ?", [agent.id]);
|
|
30983
|
+
}
|
|
30984
|
+
} catch {}
|
|
30985
|
+
return { content: [{ type: "text", text: "Focus cleared. Showing all projects." }] };
|
|
30986
|
+
});
|
|
30987
|
+
}
|
|
30988
|
+
if (shouldRegisterTool("register_agent")) {
|
|
30989
|
+
server.tool("register_agent", "Register an agent. Any name is allowed \u2014 the configured pool is advisory, not enforced. Returns a conflict error if the name is held by a recently-active agent.", {
|
|
30990
|
+
name: exports_external2.string().describe("Agent name \u2014 any name is allowed. Use suggest_agent_name to see pool suggestions and avoid conflicts."),
|
|
30991
|
+
description: exports_external2.string().optional(),
|
|
30992
|
+
role: exports_external2.string().optional(),
|
|
30993
|
+
title: exports_external2.string().optional(),
|
|
30994
|
+
level: exports_external2.string().optional(),
|
|
30995
|
+
permissions: exports_external2.array(exports_external2.string()).optional(),
|
|
30996
|
+
capabilities: exports_external2.array(exports_external2.string()).optional().describe("Agent capabilities/skills for task routing (e.g. ['typescript', 'testing', 'devops'])"),
|
|
30997
|
+
session_id: exports_external2.string().optional().describe("Unique ID for this coding session (e.g. process PID + timestamp, or env var). Used to detect name collisions across sessions. Store it and pass on every register_agent call."),
|
|
30998
|
+
working_dir: exports_external2.string().optional().describe("Working directory of this session \u2014 used to look up the project's agent pool and identify who holds the name in a conflict"),
|
|
30999
|
+
force: exports_external2.boolean().optional().describe("Force takeover of an active agent's name. Use with caution \u2014 only when you know the previous session is dead.")
|
|
31000
|
+
}, async ({ name, description, role, title, level, permissions, capabilities, session_id, working_dir, force }) => {
|
|
31001
|
+
try {
|
|
31002
|
+
const pool = getAgentPoolForProject(working_dir);
|
|
31003
|
+
const result = registerAgent({ name, description, role, title, level, permissions, capabilities, session_id, working_dir, force, pool: pool || undefined });
|
|
31004
|
+
if (isAgentConflict(result)) {
|
|
31005
|
+
const suggestLine = result.suggestions && result.suggestions.length > 0 ? `
|
|
31006
|
+
Available names: ${result.suggestions.join(", ")}` : "";
|
|
31007
|
+
const hint = `CONFLICT: ${result.message}${suggestLine}`;
|
|
31008
|
+
return {
|
|
31009
|
+
content: [{ type: "text", text: hint }],
|
|
31010
|
+
isError: true
|
|
31011
|
+
};
|
|
31012
|
+
}
|
|
31013
|
+
const agent = result;
|
|
31014
|
+
const poolLine = pool ? `
|
|
31015
|
+
Pool: [${pool.join(", ")}]` : "";
|
|
31016
|
+
return {
|
|
31017
|
+
content: [{
|
|
31018
|
+
type: "text",
|
|
31019
|
+
text: `Agent registered:
|
|
31020
|
+
ID: ${agent.id}
|
|
31021
|
+
Name: ${agent.name}${agent.description ? `
|
|
31022
|
+
Description: ${agent.description}` : ""}
|
|
31023
|
+
Session: ${agent.session_id ?? "unbound"}${poolLine}
|
|
31024
|
+
Created: ${agent.created_at}
|
|
31025
|
+
Last seen: ${agent.last_seen_at}`
|
|
31026
|
+
}]
|
|
31027
|
+
};
|
|
31028
|
+
} catch (e) {
|
|
31029
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
31030
|
+
}
|
|
31031
|
+
});
|
|
31032
|
+
}
|
|
31033
|
+
if (shouldRegisterTool("suggest_agent_name")) {
|
|
31034
|
+
server.tool("suggest_agent_name", "Get available agent names for a project. Shows configured pool, active agents, and suggestions. If no pool is configured, any name is allowed.", {
|
|
31035
|
+
working_dir: exports_external2.string().optional().describe("Your working directory \u2014 used to look up the project's allowed name pool from config")
|
|
31036
|
+
}, async ({ working_dir }) => {
|
|
31037
|
+
try {
|
|
31038
|
+
const pool = getAgentPoolForProject(working_dir);
|
|
31039
|
+
const cutoff = new Date(Date.now() - 30 * 60 * 1000).toISOString();
|
|
31040
|
+
const allActive = listAgents().filter((a) => a.last_seen_at > cutoff);
|
|
31041
|
+
if (!pool) {
|
|
31042
|
+
const lines2 = [
|
|
31043
|
+
"No agent pool configured \u2014 any name is allowed.",
|
|
31044
|
+
allActive.length > 0 ? `Active agents (avoid these names): ${allActive.map((a) => `${a.name} (seen ${Math.round((Date.now() - new Date(a.last_seen_at).getTime()) / 60000)}m ago)`).join(", ")}` : "No active agents.",
|
|
31045
|
+
`
|
|
31046
|
+
To restrict names, configure agent_pool or project_pools in ~/.hasna/todos/config.json`
|
|
31047
|
+
];
|
|
31048
|
+
return { content: [{ type: "text", text: lines2.join(`
|
|
31049
|
+
`) }] };
|
|
31050
|
+
}
|
|
31051
|
+
const available = getAvailableNamesFromPool(pool, getDatabase());
|
|
31052
|
+
const activeInPool = allActive.filter((a) => pool.map((n) => n.toLowerCase()).includes(a.name));
|
|
31053
|
+
const lines = [
|
|
31054
|
+
`Project pool: ${pool.join(", ")}`,
|
|
31055
|
+
`Available now (${available.length}): ${available.length > 0 ? available.join(", ") : "none \u2014 all names in use"}`,
|
|
31056
|
+
activeInPool.length > 0 ? `Active agents: ${activeInPool.map((a) => `${a.name} (seen ${Math.round((Date.now() - new Date(a.last_seen_at).getTime()) / 60000)}m ago)`).join(", ")}` : "Active agents: none",
|
|
31057
|
+
available.length > 0 ? `
|
|
31058
|
+
Suggested: ${available[0]}` : `
|
|
31059
|
+
No names available. Wait for an active agent to go stale (30min timeout).`
|
|
31060
|
+
];
|
|
31061
|
+
return { content: [{ type: "text", text: lines.join(`
|
|
31062
|
+
`) }] };
|
|
31063
|
+
} catch (e) {
|
|
31064
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
31065
|
+
}
|
|
31066
|
+
});
|
|
31067
|
+
}
|
|
31068
|
+
if (shouldRegisterTool("list_agents")) {
|
|
31069
|
+
server.tool("list_agents", "List all registered agents. By default shows only active agents \u2014 set include_archived to see archived ones too.", {
|
|
31070
|
+
include_archived: exports_external2.boolean().optional().describe("Include archived agents in the list (default: false)")
|
|
31071
|
+
}, async ({ include_archived }) => {
|
|
31072
|
+
try {
|
|
31073
|
+
const agents = listAgents({ include_archived: include_archived ?? false });
|
|
31074
|
+
if (agents.length === 0) {
|
|
31075
|
+
return { content: [{ type: "text", text: "No agents registered." }] };
|
|
31076
|
+
}
|
|
31077
|
+
const text = agents.map((a) => {
|
|
31078
|
+
const statusTag = a.status === "archived" ? " [archived]" : "";
|
|
31079
|
+
return `${a.id} | ${a.name}${statusTag}${a.description ? ` - ${a.description}` : ""} (last seen: ${a.last_seen_at})`;
|
|
31080
|
+
}).join(`
|
|
31081
|
+
`);
|
|
31082
|
+
return { content: [{ type: "text", text: `${agents.length} agent(s):
|
|
31083
|
+
${text}` }] };
|
|
31084
|
+
} catch (e) {
|
|
31085
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
31086
|
+
}
|
|
31087
|
+
});
|
|
31088
|
+
}
|
|
31089
|
+
if (shouldRegisterTool("get_agent")) {
|
|
31090
|
+
server.tool("get_agent", "Get agent details by ID or name. Provide one of id or name.", {
|
|
31091
|
+
agent_id: exports_external2.string().optional(),
|
|
31092
|
+
id: exports_external2.string().optional(),
|
|
31093
|
+
name: exports_external2.string().optional()
|
|
31094
|
+
}, async ({ agent_id, id, name }) => {
|
|
31095
|
+
try {
|
|
31096
|
+
const identifier = agent_id || id || name;
|
|
31097
|
+
if (!identifier) {
|
|
31098
|
+
return { content: [{ type: "text", text: "Provide agent_id, id, or name." }], isError: true };
|
|
31099
|
+
}
|
|
31100
|
+
const agent = getAgent(identifier) || getAgentByName(identifier);
|
|
31101
|
+
if (!agent) {
|
|
31102
|
+
return { content: [{ type: "text", text: `Agent not found: ${identifier}` }], isError: true };
|
|
31103
|
+
}
|
|
31104
|
+
const parts = [
|
|
31105
|
+
`ID: ${agent.id}`,
|
|
31106
|
+
`Name: ${agent.name}`
|
|
31107
|
+
];
|
|
31108
|
+
if (agent.description)
|
|
31109
|
+
parts.push(`Description: ${agent.description}`);
|
|
31110
|
+
if (Object.keys(agent.metadata).length > 0)
|
|
31111
|
+
parts.push(`Metadata: ${JSON.stringify(agent.metadata)}`);
|
|
31112
|
+
parts.push(`Created: ${agent.created_at}`);
|
|
31113
|
+
parts.push(`Last seen: ${agent.last_seen_at}`);
|
|
31114
|
+
return { content: [{ type: "text", text: parts.join(`
|
|
31115
|
+
`) }] };
|
|
31116
|
+
} catch (e) {
|
|
31117
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
31118
|
+
}
|
|
31119
|
+
});
|
|
31120
|
+
}
|
|
31121
|
+
if (shouldRegisterTool("rename_agent")) {
|
|
31122
|
+
server.tool("rename_agent", "Rename an agent. Resolve by id or current name.", {
|
|
31123
|
+
id: exports_external2.string().optional(),
|
|
31124
|
+
name: exports_external2.string().optional(),
|
|
31125
|
+
new_name: exports_external2.string()
|
|
31126
|
+
}, async ({ id, name, new_name }) => {
|
|
31127
|
+
try {
|
|
31128
|
+
if (!id && !name) {
|
|
31129
|
+
return { content: [{ type: "text", text: "Provide either id or name." }], isError: true };
|
|
31130
|
+
}
|
|
31131
|
+
const agent = id ? getAgent(id) : getAgentByName(name);
|
|
31132
|
+
if (!agent) {
|
|
31133
|
+
return { content: [{ type: "text", text: `Agent not found: ${id || name}` }], isError: true };
|
|
31134
|
+
}
|
|
31135
|
+
const oldName = agent.name;
|
|
31136
|
+
const updated = updateAgent(agent.id, { name: new_name });
|
|
31137
|
+
const db = getDatabase();
|
|
31138
|
+
const tasksResult = db.run("UPDATE tasks SET assigned_to = ? WHERE assigned_to = ?", [new_name, oldName]);
|
|
31139
|
+
const taskNote = tasksResult.changes > 0 ? `
|
|
31140
|
+
Updated assigned_to on ${tasksResult.changes} task(s).` : "";
|
|
31141
|
+
return {
|
|
31142
|
+
content: [{
|
|
31143
|
+
type: "text",
|
|
31144
|
+
text: `Agent renamed: ${oldName} -> ${updated.name}
|
|
31145
|
+
ID: ${updated.id}${taskNote}`
|
|
31146
|
+
}]
|
|
31147
|
+
};
|
|
31148
|
+
} catch (e) {
|
|
31149
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
31150
|
+
}
|
|
31151
|
+
});
|
|
31152
|
+
}
|
|
31153
|
+
if (shouldRegisterTool("update_agent")) {
|
|
31154
|
+
server.tool("update_agent", "Update an agent's description, role, title, or other metadata. Resolve by id or name.", {
|
|
31155
|
+
agent_id: exports_external2.string().optional(),
|
|
31156
|
+
id: exports_external2.string().optional(),
|
|
31157
|
+
name: exports_external2.string().optional(),
|
|
31158
|
+
description: exports_external2.string().optional(),
|
|
31159
|
+
role: exports_external2.string().optional(),
|
|
31160
|
+
title: exports_external2.string().optional(),
|
|
31161
|
+
level: exports_external2.string().optional(),
|
|
31162
|
+
capabilities: exports_external2.array(exports_external2.string()).optional(),
|
|
31163
|
+
permissions: exports_external2.array(exports_external2.string()).optional(),
|
|
31164
|
+
metadata: exports_external2.record(exports_external2.unknown()).optional()
|
|
31165
|
+
}, async ({ agent_id, id, name, ...updates }) => {
|
|
31166
|
+
try {
|
|
31167
|
+
const identifier = agent_id || id || name;
|
|
31168
|
+
if (!identifier) {
|
|
31169
|
+
return { content: [{ type: "text", text: "Provide agent_id, id, or name." }], isError: true };
|
|
31170
|
+
}
|
|
31171
|
+
const agent = getAgent(identifier) || getAgentByName(identifier);
|
|
31172
|
+
if (!agent) {
|
|
31173
|
+
return { content: [{ type: "text", text: `Agent not found: ${identifier}` }], isError: true };
|
|
31174
|
+
}
|
|
31175
|
+
const updated = updateAgent(agent.id, updates);
|
|
31176
|
+
return { content: [{ type: "text", text: `Agent updated: ${updated.name} (${updated.id.slice(0, 8)})` }] };
|
|
31177
|
+
} catch (e) {
|
|
31178
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
31179
|
+
}
|
|
31180
|
+
});
|
|
31181
|
+
}
|
|
31182
|
+
if (shouldRegisterTool("delete_agent")) {
|
|
31183
|
+
server.tool("delete_agent", "Archive an agent (soft delete). The agent is hidden from list_agents but preserved for task history. Use unarchive_agent to restore. Resolve by id or name.", {
|
|
31184
|
+
agent_id: exports_external2.string().optional(),
|
|
31185
|
+
id: exports_external2.string().optional(),
|
|
31186
|
+
name: exports_external2.string().optional()
|
|
31187
|
+
}, async ({ agent_id, id, name }) => {
|
|
31188
|
+
try {
|
|
31189
|
+
const identifier = agent_id || id || name;
|
|
31190
|
+
if (!identifier) {
|
|
31191
|
+
return { content: [{ type: "text", text: "Provide agent_id, id, or name." }], isError: true };
|
|
31192
|
+
}
|
|
31193
|
+
const agent = getAgent(identifier) || getAgentByName(identifier);
|
|
31194
|
+
if (!agent) {
|
|
31195
|
+
return { content: [{ type: "text", text: `Agent not found: ${identifier}` }], isError: true };
|
|
31196
|
+
}
|
|
31197
|
+
const archived = archiveAgent(agent.id);
|
|
31198
|
+
return {
|
|
31199
|
+
content: [{
|
|
31200
|
+
type: "text",
|
|
31201
|
+
text: archived ? `Agent archived: ${agent.name} (${agent.id}). Use unarchive_agent to restore.` : `Failed to archive agent: ${agent.name}`
|
|
31202
|
+
}],
|
|
31203
|
+
isError: !archived
|
|
31204
|
+
};
|
|
31205
|
+
} catch (e) {
|
|
31206
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
31207
|
+
}
|
|
31208
|
+
});
|
|
31209
|
+
}
|
|
31210
|
+
if (shouldRegisterTool("unarchive_agent")) {
|
|
31211
|
+
server.tool("unarchive_agent", "Restore an archived agent back to active status. Resolve by id or name.", {
|
|
31212
|
+
agent_id: exports_external2.string().optional(),
|
|
31213
|
+
id: exports_external2.string().optional(),
|
|
31214
|
+
name: exports_external2.string().optional()
|
|
31215
|
+
}, async ({ agent_id, id, name }) => {
|
|
31216
|
+
try {
|
|
31217
|
+
const identifier = agent_id || id || name;
|
|
31218
|
+
if (!identifier) {
|
|
31219
|
+
return { content: [{ type: "text", text: "Provide agent_id, id, or name." }], isError: true };
|
|
31220
|
+
}
|
|
31221
|
+
const agent = getAgent(identifier) || getAgentByName(identifier);
|
|
31222
|
+
if (!agent) {
|
|
31223
|
+
return { content: [{ type: "text", text: `Agent not found: ${identifier}` }], isError: true };
|
|
31224
|
+
}
|
|
31225
|
+
if (agent.status === "active") {
|
|
31226
|
+
return { content: [{ type: "text", text: `Agent ${agent.name} is already active.` }] };
|
|
31227
|
+
}
|
|
31228
|
+
const restored = unarchiveAgent(agent.id);
|
|
31229
|
+
return {
|
|
31230
|
+
content: [{
|
|
31231
|
+
type: "text",
|
|
31232
|
+
text: restored ? `Agent restored: ${agent.name} (${agent.id}) is now active.` : `Failed to restore agent: ${agent.name}`
|
|
31233
|
+
}],
|
|
31234
|
+
isError: !restored
|
|
31235
|
+
};
|
|
31236
|
+
} catch (e) {
|
|
31237
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
31238
|
+
}
|
|
31239
|
+
});
|
|
31240
|
+
}
|
|
31241
|
+
if (shouldRegisterTool("heartbeat")) {
|
|
31242
|
+
server.tool("heartbeat", "Update your last_seen_at timestamp to signal you're still active. Call periodically during long tasks to prevent being marked stale.", {
|
|
31243
|
+
agent_id: exports_external2.string().describe("Your agent ID or name.")
|
|
31244
|
+
}, async ({ agent_id }) => {
|
|
31245
|
+
try {
|
|
31246
|
+
const agent = getAgent(agent_id) || getAgentByName(agent_id);
|
|
31247
|
+
if (!agent) {
|
|
31248
|
+
return { content: [{ type: "text", text: `Agent not found: ${agent_id}` }], isError: true };
|
|
31249
|
+
}
|
|
31250
|
+
updateAgentActivity(agent.id);
|
|
31251
|
+
return {
|
|
31252
|
+
content: [{
|
|
31253
|
+
type: "text",
|
|
31254
|
+
text: `Heartbeat: ${agent.name} (${agent.id}) \u2014 last_seen_at updated to ${new Date().toISOString()}`
|
|
31255
|
+
}]
|
|
31256
|
+
};
|
|
31257
|
+
} catch (e) {
|
|
31258
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
31259
|
+
}
|
|
31260
|
+
});
|
|
31261
|
+
}
|
|
31262
|
+
if (shouldRegisterTool("release_agent")) {
|
|
31263
|
+
server.tool("release_agent", "Explicitly release/logout an agent \u2014 clears session binding and makes the name immediately available. Call this when your session ends instead of waiting for the 30-minute stale timeout.", {
|
|
31264
|
+
agent_id: exports_external2.string().describe("Your agent ID or name."),
|
|
31265
|
+
session_id: exports_external2.string().optional().describe("Your session ID \u2014 if provided, release only succeeds if it matches (prevents other sessions from releasing your agent).")
|
|
31266
|
+
}, async ({ agent_id, session_id }) => {
|
|
31267
|
+
try {
|
|
31268
|
+
const agent = getAgent(agent_id) || getAgentByName(agent_id);
|
|
31269
|
+
if (!agent) {
|
|
31270
|
+
return { content: [{ type: "text", text: `Agent not found: ${agent_id}` }], isError: true };
|
|
31271
|
+
}
|
|
31272
|
+
const released = releaseAgent(agent.id, session_id);
|
|
31273
|
+
if (!released) {
|
|
31274
|
+
return { content: [{ type: "text", text: `Release denied: session_id does not match agent's current session.` }], isError: true };
|
|
31275
|
+
}
|
|
31276
|
+
return {
|
|
31277
|
+
content: [{
|
|
31278
|
+
type: "text",
|
|
31279
|
+
text: `Agent released: ${agent.name} (${agent.id}) \u2014 session cleared, name is now available.`
|
|
31280
|
+
}]
|
|
31281
|
+
};
|
|
31282
|
+
} catch (e) {
|
|
31283
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
31284
|
+
}
|
|
31285
|
+
});
|
|
31286
|
+
}
|
|
31287
|
+
}
|
|
31288
|
+
var init_agents2 = __esm(() => {
|
|
31289
|
+
init_zod2();
|
|
31290
|
+
init_agents();
|
|
31291
|
+
init_config();
|
|
31292
|
+
init_database();
|
|
31293
|
+
});
|
|
31294
|
+
|
|
29984
31295
|
// src/mcp/index.ts
|
|
29985
31296
|
var exports_mcp = {};
|
|
29986
31297
|
__export(exports_mcp, {
|
|
@@ -29988,17 +31299,23 @@ __export(exports_mcp, {
|
|
|
29988
31299
|
});
|
|
29989
31300
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
29990
31301
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
29991
|
-
import { readFileSync as readFileSync7 } from "fs";
|
|
31302
|
+
import { existsSync as existsSync9, readFileSync as readFileSync7 } from "fs";
|
|
29992
31303
|
import { join as join12, dirname as dirname6 } from "path";
|
|
29993
31304
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
29994
31305
|
function getMcpVersion() {
|
|
29995
31306
|
try {
|
|
29996
|
-
|
|
29997
|
-
|
|
29998
|
-
|
|
31307
|
+
let dir = dirname6(fileURLToPath3(import.meta.url));
|
|
31308
|
+
for (let i = 0;i < 4; i++) {
|
|
31309
|
+
const pkgPath = join12(dir, "package.json");
|
|
31310
|
+
if (existsSync9(pkgPath)) {
|
|
31311
|
+
return JSON.parse(readFileSync7(pkgPath, "utf-8")).version || "0.0.0";
|
|
31312
|
+
}
|
|
31313
|
+
dir = dirname6(dir);
|
|
31314
|
+
}
|
|
29999
31315
|
} catch {
|
|
30000
31316
|
return "0.0.0";
|
|
30001
31317
|
}
|
|
31318
|
+
return "0.0.0";
|
|
30002
31319
|
}
|
|
30003
31320
|
function shouldRegisterTool(name) {
|
|
30004
31321
|
if (TODOS_PROFILE === "minimal")
|
|
@@ -30159,6 +31476,7 @@ var init_mcp = __esm(() => {
|
|
|
30159
31476
|
init_task_rel_tools();
|
|
30160
31477
|
init_code_tools();
|
|
30161
31478
|
init_machines2();
|
|
31479
|
+
init_agents2();
|
|
30162
31480
|
server = new McpServer({
|
|
30163
31481
|
name: "todos",
|
|
30164
31482
|
version: getMcpVersion()
|
|
@@ -30176,6 +31494,7 @@ var init_mcp = __esm(() => {
|
|
|
30176
31494
|
"get_next_task",
|
|
30177
31495
|
"bootstrap",
|
|
30178
31496
|
"get_tasks_changed_since",
|
|
31497
|
+
"get_health",
|
|
30179
31498
|
"heartbeat",
|
|
30180
31499
|
"release_agent"
|
|
30181
31500
|
]);
|
|
@@ -30216,6 +31535,7 @@ var init_mcp = __esm(() => {
|
|
|
30216
31535
|
registerTaskResources(server, toolContext);
|
|
30217
31536
|
registerTaskRelTools(server, toolContext);
|
|
30218
31537
|
registerCodeTools(server, toolContext);
|
|
31538
|
+
registerAgentTools(server, { ...toolContext, agentFocusMap });
|
|
30219
31539
|
registerMachineTools(server, { shouldRegisterTool, formatError });
|
|
30220
31540
|
registerDispatchTools(server, { shouldRegisterTool, resolveId, formatError });
|
|
30221
31541
|
registerCloudSyncTools(server, { shouldRegisterTool, formatError });
|
|
@@ -30766,7 +32086,7 @@ ${chalk2.cyan(sid)} ${statusColor(task.status)} ${prioColor(task.priority)} ${ta
|
|
|
30766
32086
|
opts.tags = opts.tags || opts.tag;
|
|
30767
32087
|
opts.list = opts.list || opts.taskList;
|
|
30768
32088
|
const resolvedId = resolveTaskId(id);
|
|
30769
|
-
const current =
|
|
32089
|
+
const current = getTask(resolvedId);
|
|
30770
32090
|
if (!current) {
|
|
30771
32091
|
console.error(chalk2.red(`Task not found: ${id}`));
|
|
30772
32092
|
process.exit(1);
|
|
@@ -30827,7 +32147,7 @@ ${chalk2.cyan(sid)} ${statusColor(task.status)} ${prioColor(task.priority)} ${ta
|
|
|
30827
32147
|
program2.command("approve <id>").description("Approve a task that requires approval").action((id) => {
|
|
30828
32148
|
const globalOpts = program2.opts();
|
|
30829
32149
|
const resolvedId = resolveTaskId(id);
|
|
30830
|
-
const task =
|
|
32150
|
+
const task = getTask(resolvedId);
|
|
30831
32151
|
if (!task) {
|
|
30832
32152
|
console.error(chalk2.red(`Task not found: ${id}`));
|
|
30833
32153
|
process.exit(1);
|
|
@@ -31429,7 +32749,7 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
|
|
|
31429
32749
|
updated.activeForm = existing.task.activeForm;
|
|
31430
32750
|
writeClaudeTask(dir, updated);
|
|
31431
32751
|
if (recordConflict) {
|
|
31432
|
-
const latest =
|
|
32752
|
+
const latest = getTask(task.id);
|
|
31433
32753
|
if (latest) {
|
|
31434
32754
|
const conflict = {
|
|
31435
32755
|
agent: "claude",
|
|
@@ -31452,7 +32772,7 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
|
|
|
31452
32772
|
ct.subject = formatPrefixedSubject(task.title, prefixConfig.prefix, prefixCounter);
|
|
31453
32773
|
}
|
|
31454
32774
|
writeClaudeTask(dir, ct);
|
|
31455
|
-
const current =
|
|
32775
|
+
const current = getTask(task.id);
|
|
31456
32776
|
if (current) {
|
|
31457
32777
|
const newMeta = { ...current.metadata, claude_task_id: claudeId };
|
|
31458
32778
|
updateTask(task.id, { version: current.version, metadata: newMeta });
|
|
@@ -31654,7 +32974,7 @@ function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
|
|
|
31654
32974
|
const updated = taskToAgentTask(task, existing.task.id, existing.task.metadata);
|
|
31655
32975
|
writeAgentTask(dir, updated);
|
|
31656
32976
|
if (recordConflict) {
|
|
31657
|
-
const latest =
|
|
32977
|
+
const latest = getTask(task.id);
|
|
31658
32978
|
if (latest) {
|
|
31659
32979
|
const conflict = {
|
|
31660
32980
|
agent,
|
|
@@ -31673,7 +32993,7 @@ function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
|
|
|
31673
32993
|
hwm++;
|
|
31674
32994
|
const at = taskToAgentTask(task, externalId);
|
|
31675
32995
|
writeAgentTask(dir, at);
|
|
31676
|
-
const current =
|
|
32996
|
+
const current = getTask(task.id);
|
|
31677
32997
|
if (current) {
|
|
31678
32998
|
const newMeta = { ...current.metadata, [metaKey]: externalId };
|
|
31679
32999
|
updateTask(task.id, { version: current.version, metadata: newMeta });
|
|
@@ -32756,7 +34076,7 @@ ${tasks.length} task(s) shown`));
|
|
|
32756
34076
|
program2.command("blame <file>").description("Show which tasks/agents touched a file and why \u2014 combines task_files + task_commits").action(async (filePath) => {
|
|
32757
34077
|
const globalOpts = program2.opts();
|
|
32758
34078
|
const { findTasksByFile: findTasksByFile2 } = await Promise.resolve().then(() => (init_task_files(), exports_task_files));
|
|
32759
|
-
const { getTask:
|
|
34079
|
+
const { getTask: getTask2 } = await Promise.resolve().then(() => (init_tasks(), exports_tasks));
|
|
32760
34080
|
const db = getDatabase();
|
|
32761
34081
|
const taskFiles = findTasksByFile2(filePath, db);
|
|
32762
34082
|
const commitRows = db.query("SELECT tc.*, t.title, t.short_id FROM task_commits tc JOIN tasks t ON t.id = tc.task_id WHERE tc.files_changed LIKE ? ORDER BY tc.committed_at DESC").all(`%${filePath}%`);
|
|
@@ -32770,7 +34090,7 @@ Blame: ${filePath}
|
|
|
32770
34090
|
if (taskFiles.length > 0) {
|
|
32771
34091
|
console.log(chalk6.bold("Task File Links:"));
|
|
32772
34092
|
for (const tf of taskFiles) {
|
|
32773
|
-
const task =
|
|
34093
|
+
const task = getTask2(tf.task_id, db);
|
|
32774
34094
|
const title = task ? task.title : "unknown";
|
|
32775
34095
|
const sid = task?.short_id || tf.task_id.slice(0, 8);
|
|
32776
34096
|
console.log(` ${chalk6.cyan(sid)} ${title} \u2014 ${chalk6.dim(tf.role || "file")} ${chalk6.dim(tf.updated_at)}`);
|
|
@@ -32804,74 +34124,10 @@ Commit Links (${commitRows.length}):`));
|
|
|
32804
34124
|
init_database();
|
|
32805
34125
|
init_tasks();
|
|
32806
34126
|
init_audit();
|
|
32807
|
-
|
|
32808
|
-
|
|
32809
|
-
// src/db/handoffs.ts
|
|
32810
|
-
init_database();
|
|
32811
|
-
function createHandoff(input, db) {
|
|
32812
|
-
const d = db || getDatabase();
|
|
32813
|
-
const id = uuid();
|
|
32814
|
-
const timestamp = now();
|
|
32815
|
-
d.run(`INSERT INTO handoffs (id, agent_id, project_id, summary, completed, in_progress, blockers, next_steps, created_at)
|
|
32816
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
32817
|
-
id,
|
|
32818
|
-
input.agent_id || null,
|
|
32819
|
-
input.project_id || null,
|
|
32820
|
-
input.summary,
|
|
32821
|
-
input.completed ? JSON.stringify(input.completed) : null,
|
|
32822
|
-
input.in_progress ? JSON.stringify(input.in_progress) : null,
|
|
32823
|
-
input.blockers ? JSON.stringify(input.blockers) : null,
|
|
32824
|
-
input.next_steps ? JSON.stringify(input.next_steps) : null,
|
|
32825
|
-
timestamp
|
|
32826
|
-
]);
|
|
32827
|
-
return {
|
|
32828
|
-
id,
|
|
32829
|
-
agent_id: input.agent_id || null,
|
|
32830
|
-
project_id: input.project_id || null,
|
|
32831
|
-
summary: input.summary,
|
|
32832
|
-
completed: input.completed || null,
|
|
32833
|
-
in_progress: input.in_progress || null,
|
|
32834
|
-
blockers: input.blockers || null,
|
|
32835
|
-
next_steps: input.next_steps || null,
|
|
32836
|
-
created_at: timestamp
|
|
32837
|
-
};
|
|
32838
|
-
}
|
|
32839
|
-
function rowToHandoff(row) {
|
|
32840
|
-
return {
|
|
32841
|
-
...row,
|
|
32842
|
-
completed: row.completed ? JSON.parse(row.completed) : null,
|
|
32843
|
-
in_progress: row.in_progress ? JSON.parse(row.in_progress) : null,
|
|
32844
|
-
blockers: row.blockers ? JSON.parse(row.blockers) : null,
|
|
32845
|
-
next_steps: row.next_steps ? JSON.parse(row.next_steps) : null
|
|
32846
|
-
};
|
|
32847
|
-
}
|
|
32848
|
-
function listHandoffs(projectId, limit = 10, db) {
|
|
32849
|
-
const d = db || getDatabase();
|
|
32850
|
-
if (projectId) {
|
|
32851
|
-
return d.query("SELECT * FROM handoffs WHERE project_id = ? ORDER BY rowid DESC LIMIT ?").all(projectId, limit).map(rowToHandoff);
|
|
32852
|
-
}
|
|
32853
|
-
return d.query("SELECT * FROM handoffs ORDER BY rowid DESC LIMIT ?").all(limit).map(rowToHandoff);
|
|
32854
|
-
}
|
|
32855
|
-
function getLatestHandoff(agentId, projectId, db) {
|
|
32856
|
-
const d = db || getDatabase();
|
|
32857
|
-
let query = "SELECT * FROM handoffs WHERE 1=1";
|
|
32858
|
-
const params = [];
|
|
32859
|
-
if (agentId) {
|
|
32860
|
-
query += " AND agent_id = ?";
|
|
32861
|
-
params.push(agentId);
|
|
32862
|
-
}
|
|
32863
|
-
if (projectId) {
|
|
32864
|
-
query += " AND project_id = ?";
|
|
32865
|
-
params.push(projectId);
|
|
32866
|
-
}
|
|
32867
|
-
query += " ORDER BY rowid DESC LIMIT 1";
|
|
32868
|
-
const row = d.query(query).get(...params);
|
|
32869
|
-
return row ? rowToHandoff(row) : null;
|
|
32870
|
-
}
|
|
32871
|
-
|
|
32872
|
-
// src/cli/commands/query-commands.ts
|
|
34127
|
+
init_handoffs();
|
|
32873
34128
|
init_recurrence();
|
|
32874
34129
|
init_helpers();
|
|
34130
|
+
import chalk7 from "chalk";
|
|
32875
34131
|
function registerQueryCommands(program2) {
|
|
32876
34132
|
program2.command("next").description("Show the best pending task to work on next").option("--agent <id>", "Prefer tasks assigned to this agent").option("--project <id>", "Filter to project").option("-j, --json", "Output as JSON").action(async (opts) => {
|
|
32877
34133
|
const globalOpts = program2.opts();
|
|
@@ -33160,7 +34416,7 @@ No task claimed (nothing available).`));
|
|
|
33160
34416
|
const globalOpts = program2.opts();
|
|
33161
34417
|
const resolvedId = resolveTaskId(id);
|
|
33162
34418
|
const db = getDatabase();
|
|
33163
|
-
const task =
|
|
34419
|
+
const task = getTask(resolvedId, db);
|
|
33164
34420
|
if (!task) {
|
|
33165
34421
|
console.error(chalk7.red(`Task not found: ${id}`));
|
|
33166
34422
|
process.exit(1);
|
|
@@ -33180,7 +34436,7 @@ No task claimed (nothing available).`));
|
|
|
33180
34436
|
const globalOpts = program2.opts();
|
|
33181
34437
|
const resolvedId = resolveTaskId(id);
|
|
33182
34438
|
const db = getDatabase();
|
|
33183
|
-
const task =
|
|
34439
|
+
const task = getTask(resolvedId, db);
|
|
33184
34440
|
if (!task) {
|
|
33185
34441
|
console.error(chalk7.red(`Task not found: ${id}`));
|
|
33186
34442
|
process.exit(1);
|
|
@@ -33200,7 +34456,7 @@ No task claimed (nothing available).`));
|
|
|
33200
34456
|
const globalOpts = program2.opts();
|
|
33201
34457
|
const resolvedId = resolveTaskId(id);
|
|
33202
34458
|
const db = getDatabase();
|
|
33203
|
-
const task =
|
|
34459
|
+
const task = getTask(resolvedId, db);
|
|
33204
34460
|
if (!task) {
|
|
33205
34461
|
console.error(chalk7.red(`Task not found: ${id}`));
|
|
33206
34462
|
process.exit(1);
|
|
@@ -33221,7 +34477,7 @@ No task claimed (nothing available).`));
|
|
|
33221
34477
|
const globalOpts = program2.opts();
|
|
33222
34478
|
const resolvedId = resolveTaskId(id);
|
|
33223
34479
|
const db = getDatabase();
|
|
33224
|
-
const task =
|
|
34480
|
+
const task = getTask(resolvedId, db);
|
|
33225
34481
|
if (!task) {
|
|
33226
34482
|
console.error(chalk7.red(`Task not found: ${id}`));
|
|
33227
34483
|
process.exit(1);
|
|
@@ -34384,7 +35640,7 @@ init_tasks();
|
|
|
34384
35640
|
init_helpers();
|
|
34385
35641
|
import chalk9 from "chalk";
|
|
34386
35642
|
import { execSync as execSync3 } from "child_process";
|
|
34387
|
-
import { existsSync as
|
|
35643
|
+
import { existsSync as existsSync10, readFileSync as readFileSync8, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, chmodSync } from "fs";
|
|
34388
35644
|
import { dirname as dirname7, join as join13 } from "path";
|
|
34389
35645
|
var HOME2 = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
34390
35646
|
function getMcpBinaryPath() {
|
|
@@ -34394,12 +35650,12 @@ function getMcpBinaryPath() {
|
|
|
34394
35650
|
return p;
|
|
34395
35651
|
} catch {}
|
|
34396
35652
|
const bunBin = join13(HOME2, ".bun", "bin", "todos-mcp");
|
|
34397
|
-
if (
|
|
35653
|
+
if (existsSync10(bunBin))
|
|
34398
35654
|
return bunBin;
|
|
34399
35655
|
return "todos-mcp";
|
|
34400
35656
|
}
|
|
34401
35657
|
function readJsonFile2(path) {
|
|
34402
|
-
if (!
|
|
35658
|
+
if (!existsSync10(path))
|
|
34403
35659
|
return {};
|
|
34404
35660
|
try {
|
|
34405
35661
|
return JSON.parse(readFileSync8(path, "utf-8"));
|
|
@@ -34409,19 +35665,19 @@ function readJsonFile2(path) {
|
|
|
34409
35665
|
}
|
|
34410
35666
|
function writeJsonFile2(path, data) {
|
|
34411
35667
|
const dir = dirname7(path);
|
|
34412
|
-
if (!
|
|
35668
|
+
if (!existsSync10(dir))
|
|
34413
35669
|
mkdirSync5(dir, { recursive: true });
|
|
34414
35670
|
writeFileSync5(path, JSON.stringify(data, null, 2) + `
|
|
34415
35671
|
`);
|
|
34416
35672
|
}
|
|
34417
35673
|
function readTomlFile(path) {
|
|
34418
|
-
if (!
|
|
35674
|
+
if (!existsSync10(path))
|
|
34419
35675
|
return "";
|
|
34420
35676
|
return readFileSync8(path, "utf-8");
|
|
34421
35677
|
}
|
|
34422
35678
|
function writeTomlFile(path, content) {
|
|
34423
35679
|
const dir = dirname7(path);
|
|
34424
|
-
if (!
|
|
35680
|
+
if (!existsSync10(dir))
|
|
34425
35681
|
mkdirSync5(dir, { recursive: true });
|
|
34426
35682
|
writeFileSync5(path, content);
|
|
34427
35683
|
}
|
|
@@ -34565,7 +35821,7 @@ function registerMcpHooksCommands(program2) {
|
|
|
34565
35821
|
todosBin = p;
|
|
34566
35822
|
} catch {}
|
|
34567
35823
|
const hooksDir = join13(process.cwd(), ".claude", "hooks");
|
|
34568
|
-
if (!
|
|
35824
|
+
if (!existsSync10(hooksDir))
|
|
34569
35825
|
mkdirSync5(hooksDir, { recursive: true });
|
|
34570
35826
|
const hookScript = `#!/usr/bin/env bash
|
|
34571
35827
|
# Auto-generated by: todos hooks install
|
|
@@ -34687,7 +35943,7 @@ exit 0
|
|
|
34687
35943
|
const gitDir = execSync3("git rev-parse --git-dir", { encoding: "utf-8" }).trim();
|
|
34688
35944
|
const hookPath = `${gitDir}/hooks/post-commit`;
|
|
34689
35945
|
const marker = "# todos-auto-link";
|
|
34690
|
-
if (
|
|
35946
|
+
if (existsSync10(hookPath)) {
|
|
34691
35947
|
const existing = readFileSync8(hookPath, "utf-8");
|
|
34692
35948
|
if (existing.includes(marker)) {
|
|
34693
35949
|
console.log(chalk9.yellow("Hook already installed."));
|
|
@@ -34715,7 +35971,7 @@ $(dirname "$0")/../../scripts/post-commit-hook.sh
|
|
|
34715
35971
|
const gitDir = execSync3("git rev-parse --git-dir", { encoding: "utf-8" }).trim();
|
|
34716
35972
|
const hookPath = `${gitDir}/hooks/post-commit`;
|
|
34717
35973
|
const marker = "# todos-auto-link";
|
|
34718
|
-
if (!
|
|
35974
|
+
if (!existsSync10(hookPath)) {
|
|
34719
35975
|
console.log(chalk9.dim("No post-commit hook found."));
|
|
34720
35976
|
return;
|
|
34721
35977
|
}
|