@hasna/todos 0.11.32 → 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 +1717 -463
- 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/lib/logger.d.ts +2 -2
- package/dist/lib/logger.d.ts.map +1 -1
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +2343 -560
- 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");
|
|
@@ -25736,8 +26015,7 @@ var init_cloud = __esm(() => {
|
|
|
25736
26015
|
});
|
|
25737
26016
|
|
|
25738
26017
|
// src/lib/logger.ts
|
|
25739
|
-
|
|
25740
|
-
function getLogger() {
|
|
26018
|
+
async function getLogger() {
|
|
25741
26019
|
if (client)
|
|
25742
26020
|
return client;
|
|
25743
26021
|
const url = process.env.LOGS_URL;
|
|
@@ -25745,14 +26023,15 @@ function getLogger() {
|
|
|
25745
26023
|
const apiKey = process.env.LOGS_API_KEY;
|
|
25746
26024
|
if (!projectId)
|
|
25747
26025
|
return null;
|
|
26026
|
+
const { LogsClient } = await import("@hasna/logs");
|
|
25748
26027
|
client = new LogsClient({ url, projectId, apiKey });
|
|
25749
26028
|
return client;
|
|
25750
26029
|
}
|
|
25751
26030
|
async function logError(message, opts) {
|
|
25752
|
-
const logger = getLogger();
|
|
25753
|
-
if (!logger)
|
|
25754
|
-
return;
|
|
25755
26031
|
try {
|
|
26032
|
+
const logger = await getLogger();
|
|
26033
|
+
if (!logger)
|
|
26034
|
+
return;
|
|
25756
26035
|
await logger.push({
|
|
25757
26036
|
level: "error",
|
|
25758
26037
|
message,
|
|
@@ -25765,7 +26044,6 @@ async function logError(message, opts) {
|
|
|
25765
26044
|
} catch {}
|
|
25766
26045
|
}
|
|
25767
26046
|
var client = null;
|
|
25768
|
-
var init_logger = () => {};
|
|
25769
26047
|
|
|
25770
26048
|
// src/db/dispatches.ts
|
|
25771
26049
|
var exports_dispatches = {};
|
|
@@ -26216,12 +26494,21 @@ var init_dispatch2 = __esm(() => {
|
|
|
26216
26494
|
// src/mcp/tools/task-crud.ts
|
|
26217
26495
|
function registerTaskCrudTools(server, ctx) {
|
|
26218
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
|
+
}
|
|
26219
26506
|
if (shouldRegisterTool("create_task")) {
|
|
26220
26507
|
server.tool("create_task", "Create a new task in a project. Pass short_id=null to auto-generate.", {
|
|
26221
26508
|
title: exports_external2.string().describe("Task title"),
|
|
26222
26509
|
description: exports_external2.string().optional().describe("Task description (markdown)"),
|
|
26223
|
-
status: exports_external2.enum(["pending", "in_progress", "completed", "cancelled"]).optional().describe("Initial status (default: pending)"),
|
|
26224
|
-
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)"),
|
|
26225
26512
|
project_id: exports_external2.string().optional().describe("Project ID"),
|
|
26226
26513
|
task_list_id: exports_external2.string().optional().describe("Task list ID"),
|
|
26227
26514
|
assigned_to: exports_external2.string().optional().describe("Agent ID or name to assign to"),
|
|
@@ -26251,9 +26538,9 @@ function registerTaskCrudTools(server, ctx) {
|
|
|
26251
26538
|
if (confidence !== undefined)
|
|
26252
26539
|
resolved.confidence = confidence;
|
|
26253
26540
|
if (retry_count !== undefined)
|
|
26254
|
-
resolved.
|
|
26541
|
+
resolved.max_retries = retry_count;
|
|
26255
26542
|
if (deadline)
|
|
26256
|
-
resolved.
|
|
26543
|
+
resolved.due_at = deadline;
|
|
26257
26544
|
const task = createTask(resolved);
|
|
26258
26545
|
return { content: [{ type: "text", text: formatTask(task) }] };
|
|
26259
26546
|
} catch (e) {
|
|
@@ -26263,8 +26550,8 @@ function registerTaskCrudTools(server, ctx) {
|
|
|
26263
26550
|
}
|
|
26264
26551
|
if (shouldRegisterTool("list_tasks")) {
|
|
26265
26552
|
server.tool("list_tasks", "List tasks with optional filters. Pass empty arrays for multi-value filters (e.g. status=[] shows all).", {
|
|
26266
|
-
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"),
|
|
26267
|
-
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"),
|
|
26268
26555
|
project_id: exports_external2.string().optional().describe("Filter by project"),
|
|
26269
26556
|
task_list_id: exports_external2.string().optional().describe("Filter by task list"),
|
|
26270
26557
|
assigned_to: exports_external2.string().optional().describe("Filter by assignee (agent ID or name, empty string = unassigned)"),
|
|
@@ -26299,9 +26586,9 @@ function registerTaskCrudTools(server, ctx) {
|
|
|
26299
26586
|
}, async ({ task_id }) => {
|
|
26300
26587
|
try {
|
|
26301
26588
|
const resolvedId = resolveId(task_id);
|
|
26302
|
-
const task =
|
|
26589
|
+
const task = getTask(resolvedId);
|
|
26303
26590
|
if (!task)
|
|
26304
|
-
throw new
|
|
26591
|
+
throw new TaskNotFoundError(task_id);
|
|
26305
26592
|
const focus = ctx.getAgentFocus(task.assigned_to || "");
|
|
26306
26593
|
const lines = [
|
|
26307
26594
|
`ID: ${task.id}`,
|
|
@@ -26315,7 +26602,7 @@ function registerTaskCrudTools(server, ctx) {
|
|
|
26315
26602
|
task.estimated_minutes != null ? `Estimate: ${task.estimated_minutes} min` : null,
|
|
26316
26603
|
task.actual_minutes != null ? `Actual: ${task.actual_minutes} min` : null,
|
|
26317
26604
|
task.confidence != null ? `Confidence: ${task.confidence}` : null,
|
|
26318
|
-
task.
|
|
26605
|
+
task.due_at ? `Due: ${task.due_at}` : null,
|
|
26319
26606
|
task.completed_at ? `Completed: ${task.completed_at}` : null,
|
|
26320
26607
|
focus ? `Focus: agent=${focus.agent_id} project=${focus.project_id || "(global)"}` : null,
|
|
26321
26608
|
task.created_at ? `Created: ${task.created_at}` : null,
|
|
@@ -26337,8 +26624,8 @@ ${task.description}` : null
|
|
|
26337
26624
|
task_id: exports_external2.string().describe("Task ID"),
|
|
26338
26625
|
title: exports_external2.string().optional(),
|
|
26339
26626
|
description: exports_external2.string().optional(),
|
|
26340
|
-
status: exports_external2.enum(["pending", "in_progress", "completed", "cancelled"]).optional(),
|
|
26341
|
-
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(),
|
|
26342
26629
|
assigned_to: exports_external2.string().nullable().optional().describe("Agent ID or name, null to unassign"),
|
|
26343
26630
|
project_id: exports_external2.string().nullable().optional(),
|
|
26344
26631
|
task_list_id: exports_external2.string().nullable().optional(),
|
|
@@ -26367,9 +26654,15 @@ ${task.description}` : null
|
|
|
26367
26654
|
resolved.task_list_id = resolveId(resolved.task_list_id, "task_lists");
|
|
26368
26655
|
if (resolved.depends_on && Array.isArray(resolved.depends_on))
|
|
26369
26656
|
resolved.depends_on = resolved.depends_on.map(resolveId);
|
|
26370
|
-
if (resolved.estimate !== undefined)
|
|
26657
|
+
if (resolved.estimate !== undefined) {
|
|
26371
26658
|
resolved.estimated_minutes = resolved.estimate;
|
|
26372
|
-
|
|
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) });
|
|
26373
26666
|
return { content: [{ type: "text", text: formatTask(task) }] };
|
|
26374
26667
|
} catch (e) {
|
|
26375
26668
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -26394,11 +26687,24 @@ ${task.description}` : null
|
|
|
26394
26687
|
var init_task_crud2 = __esm(() => {
|
|
26395
26688
|
init_zod2();
|
|
26396
26689
|
init_tasks();
|
|
26690
|
+
init_types();
|
|
26397
26691
|
});
|
|
26398
26692
|
|
|
26399
26693
|
// src/mcp/tools/task-project-tools.ts
|
|
26400
26694
|
function registerTaskProjectTools(server, ctx) {
|
|
26401
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
|
+
}
|
|
26402
26708
|
if (shouldRegisterTool("start_task")) {
|
|
26403
26709
|
server.tool("start_task", "Mark a task as in_progress. Uses optimistic locking via version if provided.", {
|
|
26404
26710
|
task_id: exports_external2.string().describe("Task ID"),
|
|
@@ -26406,7 +26712,12 @@ function registerTaskProjectTools(server, ctx) {
|
|
|
26406
26712
|
}, async ({ task_id, version }) => {
|
|
26407
26713
|
try {
|
|
26408
26714
|
const resolvedId = resolveId(task_id);
|
|
26409
|
-
|
|
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");
|
|
26410
26721
|
return { content: [{ type: "text", text: formatTask(task) }] };
|
|
26411
26722
|
} catch (e) {
|
|
26412
26723
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -26422,7 +26733,12 @@ function registerTaskProjectTools(server, ctx) {
|
|
|
26422
26733
|
}, async ({ task_id, confidence, completed_at, version }) => {
|
|
26423
26734
|
try {
|
|
26424
26735
|
const resolvedId = resolveId(task_id);
|
|
26425
|
-
|
|
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 });
|
|
26426
26742
|
return { content: [{ type: "text", text: formatTask(task) }] };
|
|
26427
26743
|
} catch (e) {
|
|
26428
26744
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -26436,7 +26752,7 @@ function registerTaskProjectTools(server, ctx) {
|
|
|
26436
26752
|
}, async ({ task_id, version }) => {
|
|
26437
26753
|
try {
|
|
26438
26754
|
const resolvedId = resolveId(task_id);
|
|
26439
|
-
const task =
|
|
26755
|
+
const task = version === undefined ? setTaskStatus(resolvedId, "cancelled") : updateWithOptionalVersion(resolvedId, { status: "cancelled" }, version);
|
|
26440
26756
|
return { content: [{ type: "text", text: formatTask(task) }] };
|
|
26441
26757
|
} catch (e) {
|
|
26442
26758
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -26452,7 +26768,7 @@ function registerTaskProjectTools(server, ctx) {
|
|
|
26452
26768
|
try {
|
|
26453
26769
|
const resolvedId = resolveId(task_id);
|
|
26454
26770
|
const resolvedAssignee = resolveId(new_assignee, "agents");
|
|
26455
|
-
const task =
|
|
26771
|
+
const task = updateWithOptionalVersion(resolvedId, { assigned_to: resolvedAssignee }, version);
|
|
26456
26772
|
return { content: [{ type: "text", text: formatTask(task) }] };
|
|
26457
26773
|
} catch (e) {
|
|
26458
26774
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -26467,7 +26783,7 @@ function registerTaskProjectTools(server, ctx) {
|
|
|
26467
26783
|
}, async ({ task_id, deadline, version }) => {
|
|
26468
26784
|
try {
|
|
26469
26785
|
const resolvedId = resolveId(task_id);
|
|
26470
|
-
const task =
|
|
26786
|
+
const task = updateWithOptionalVersion(resolvedId, { due_at: deadline }, version);
|
|
26471
26787
|
return { content: [{ type: "text", text: formatTask(task) }] };
|
|
26472
26788
|
} catch (e) {
|
|
26473
26789
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -26477,12 +26793,12 @@ function registerTaskProjectTools(server, ctx) {
|
|
|
26477
26793
|
if (shouldRegisterTool("prioritize_task")) {
|
|
26478
26794
|
server.tool("prioritize_task", "Set a task's priority.", {
|
|
26479
26795
|
task_id: exports_external2.string().describe("Task ID"),
|
|
26480
|
-
priority: exports_external2.enum(["low", "medium", "high", "
|
|
26796
|
+
priority: exports_external2.enum(["low", "medium", "high", "critical"]).describe("New priority"),
|
|
26481
26797
|
version: exports_external2.number().optional().describe("Expected version for optimistic locking")
|
|
26482
26798
|
}, async ({ task_id, priority, version }) => {
|
|
26483
26799
|
try {
|
|
26484
26800
|
const resolvedId = resolveId(task_id);
|
|
26485
|
-
const task =
|
|
26801
|
+
const task = version === undefined ? setTaskPriority(resolvedId, priority) : updateWithOptionalVersion(resolvedId, { priority }, version);
|
|
26486
26802
|
return { content: [{ type: "text", text: formatTask(task) }] };
|
|
26487
26803
|
} catch (e) {
|
|
26488
26804
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -26538,18 +26854,18 @@ function registerTaskProjectTools(server, ctx) {
|
|
|
26538
26854
|
if (shouldRegisterTool("bulk_update_tasks")) {
|
|
26539
26855
|
server.tool("bulk_update_tasks", "Update multiple tasks at once. All tasks must pass the dependency check.", {
|
|
26540
26856
|
task_ids: exports_external2.array(exports_external2.string()).describe("Array of task IDs to update"),
|
|
26541
|
-
status: exports_external2.enum(["pending", "in_progress", "completed", "cancelled"]).optional(),
|
|
26542
|
-
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(),
|
|
26543
26859
|
assigned_to: exports_external2.string().nullable().optional().describe("Agent ID or name, null to unassign")
|
|
26544
26860
|
}, async ({ task_ids, status, priority, assigned_to }) => {
|
|
26545
26861
|
try {
|
|
26546
|
-
const { bulkUpdateTasks: bulkUpdateTasks2 } = (()
|
|
26862
|
+
const { bulkUpdateTasks: bulkUpdateTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
26547
26863
|
const resolved = task_ids.map(resolveId);
|
|
26548
26864
|
let resolvedAssignee = assigned_to;
|
|
26549
26865
|
if (resolvedAssignee && typeof resolvedAssignee === "string")
|
|
26550
26866
|
resolvedAssignee = resolveId(resolvedAssignee, "agents");
|
|
26551
26867
|
const result = bulkUpdateTasks2(resolved, { status, priority, assigned_to: resolvedAssignee });
|
|
26552
|
-
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.` }] };
|
|
26553
26869
|
} catch (e) {
|
|
26554
26870
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
26555
26871
|
}
|
|
@@ -26560,8 +26876,8 @@ function registerTaskProjectTools(server, ctx) {
|
|
|
26560
26876
|
tasks: exports_external2.array(exports_external2.object({
|
|
26561
26877
|
title: exports_external2.string(),
|
|
26562
26878
|
description: exports_external2.string().optional(),
|
|
26563
|
-
status: exports_external2.enum(["pending", "in_progress", "completed", "cancelled"]).optional(),
|
|
26564
|
-
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(),
|
|
26565
26881
|
project_id: exports_external2.string().optional(),
|
|
26566
26882
|
task_list_id: exports_external2.string().optional(),
|
|
26567
26883
|
assigned_to: exports_external2.string().optional(),
|
|
@@ -26572,7 +26888,7 @@ function registerTaskProjectTools(server, ctx) {
|
|
|
26572
26888
|
})).describe("Array of task objects")
|
|
26573
26889
|
}, async ({ tasks }) => {
|
|
26574
26890
|
try {
|
|
26575
|
-
const { bulkCreateTasks: bulkCreateTasks2 } = (()
|
|
26891
|
+
const { bulkCreateTasks: bulkCreateTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
26576
26892
|
const resolved = tasks.map((t) => {
|
|
26577
26893
|
const r = { ...t };
|
|
26578
26894
|
if (r.project_id)
|
|
@@ -26586,7 +26902,7 @@ function registerTaskProjectTools(server, ctx) {
|
|
|
26586
26902
|
return r;
|
|
26587
26903
|
});
|
|
26588
26904
|
const result = bulkCreateTasks2(resolved);
|
|
26589
|
-
return { content: [{ type: "text", text: `${result.created} task(s) created
|
|
26905
|
+
return { content: [{ type: "text", text: `${result.created.length} task(s) created.` }] };
|
|
26590
26906
|
} catch (e) {
|
|
26591
26907
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
26592
26908
|
}
|
|
@@ -26598,9 +26914,9 @@ function registerTaskProjectTools(server, ctx) {
|
|
|
26598
26914
|
force: exports_external2.boolean().optional().describe("Skip child check for all tasks (dangerous)")
|
|
26599
26915
|
}, async ({ task_ids, force }) => {
|
|
26600
26916
|
try {
|
|
26601
|
-
const { bulkDeleteTasks } = (()
|
|
26917
|
+
const { bulkDeleteTasks: bulkDeleteTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
26602
26918
|
const resolved = task_ids.map(resolveId);
|
|
26603
|
-
const result =
|
|
26919
|
+
const result = bulkDeleteTasks2(resolved, force);
|
|
26604
26920
|
return { content: [{ type: "text", text: `${result.deleted} task(s) deleted, ${result.skipped} skipped (has children).` }] };
|
|
26605
26921
|
} catch (e) {
|
|
26606
26922
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -26649,7 +26965,7 @@ function registerTaskProjectTools(server, ctx) {
|
|
|
26649
26965
|
const project = getProject(resolvedId);
|
|
26650
26966
|
if (!project)
|
|
26651
26967
|
throw new TaskNotFoundError(`Project not found: ${project_id}`);
|
|
26652
|
-
const { listTasks: listTasks3 } = (()
|
|
26968
|
+
const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
26653
26969
|
const tasks = listTasks3({ project_id: resolvedId, limit: 100 }, undefined);
|
|
26654
26970
|
const lines = [
|
|
26655
26971
|
`ID: ${project.id}`,
|
|
@@ -26750,7 +27066,7 @@ function registerTaskProjectTools(server, ctx) {
|
|
|
26750
27066
|
throw new TaskNotFoundError(`Task list not found: ${task_list_id}`);
|
|
26751
27067
|
let tasks = [];
|
|
26752
27068
|
if (include_tasks) {
|
|
26753
|
-
const { listTasks: listTasks3 } = (()
|
|
27069
|
+
const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
26754
27070
|
tasks = listTasks3({ task_list_id: resolvedId, limit: 200 }, undefined);
|
|
26755
27071
|
}
|
|
26756
27072
|
const lines = [
|
|
@@ -26850,7 +27166,7 @@ Tasks:` : null,
|
|
|
26850
27166
|
throw new TaskNotFoundError(`Plan not found: ${plan_id}`);
|
|
26851
27167
|
let tasks = [];
|
|
26852
27168
|
if (include_tasks) {
|
|
26853
|
-
const { listTasks: listTasks3 } = (()
|
|
27169
|
+
const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
26854
27170
|
tasks = listTasks3({ plan_id: resolvedId, limit: 200 }, undefined);
|
|
26855
27171
|
}
|
|
26856
27172
|
const lines = [
|
|
@@ -26939,7 +27255,7 @@ Tasks:` : null,
|
|
|
26939
27255
|
const tag = getTag(tag_id);
|
|
26940
27256
|
if (!tag)
|
|
26941
27257
|
throw new TaskNotFoundError(`Tag not found: ${tag_id}`);
|
|
26942
|
-
const { listTasks: listTasks3 } = (()
|
|
27258
|
+
const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
26943
27259
|
const tasks = listTasks3({ tags: [tag.name], limit: 100 }, undefined);
|
|
26944
27260
|
const lines = [
|
|
26945
27261
|
`Tag: ${tag.name}${tag.color ? ` (${tag.color})` : ""}`,
|
|
@@ -27018,7 +27334,7 @@ Tasks:` : null,
|
|
|
27018
27334
|
const label = getLabel(label_id);
|
|
27019
27335
|
if (!label)
|
|
27020
27336
|
throw new TaskNotFoundError(`Label not found: ${label_id}`);
|
|
27021
|
-
const { listTasks: listTasks3 } = (()
|
|
27337
|
+
const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27022
27338
|
const tasks = listTasks3({ tags: [label.name], limit: 100 }, undefined);
|
|
27023
27339
|
const lines = [
|
|
27024
27340
|
`Label: ${label.name}${label.color ? ` (${label.color})` : ""}`,
|
|
@@ -27069,7 +27385,7 @@ Tasks:` : null,
|
|
|
27069
27385
|
try {
|
|
27070
27386
|
const resolvedId = resolveId(task_id);
|
|
27071
27387
|
const resolvedAuthor = author ? resolveId(author, "agents") : undefined;
|
|
27072
|
-
const comment = addComment({ task_id: resolvedId, body,
|
|
27388
|
+
const comment = addComment({ task_id: resolvedId, content: body, agent_id: resolvedAuthor });
|
|
27073
27389
|
return { content: [{ type: "text", text: `Comment added to ${task_id.slice(0, 8)}: ${body.slice(0, 50)}${body.length > 50 ? "..." : ""}` }] };
|
|
27074
27390
|
} catch (e) {
|
|
27075
27391
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -27085,8 +27401,8 @@ Tasks:` : null,
|
|
|
27085
27401
|
const comments = listComments(resolvedId);
|
|
27086
27402
|
if (comments.length === 0)
|
|
27087
27403
|
return { content: [{ type: "text", text: "No comments." }] };
|
|
27088
|
-
const lines = comments.map((c) => `[${c.
|
|
27089
|
-
${c.
|
|
27404
|
+
const lines = comments.map((c) => `[${c.agent_id || "unknown"}] ${c.created_at?.slice(0, 16)}:
|
|
27405
|
+
${c.content}`);
|
|
27090
27406
|
return { content: [{ type: "text", text: lines.join(`
|
|
27091
27407
|
|
|
27092
27408
|
`) }] };
|
|
@@ -27101,7 +27417,7 @@ Tasks:` : null,
|
|
|
27101
27417
|
body: exports_external2.string().describe("New comment body")
|
|
27102
27418
|
}, async ({ comment_id, body }) => {
|
|
27103
27419
|
try {
|
|
27104
|
-
const comment = updateComment(comment_id, { body });
|
|
27420
|
+
const comment = updateComment(comment_id, { content: body });
|
|
27105
27421
|
return { content: [{ type: "text", text: `Comment ${comment_id.slice(0, 8)} updated.` }] };
|
|
27106
27422
|
} catch (e) {
|
|
27107
27423
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -27124,11 +27440,11 @@ Tasks:` : null,
|
|
|
27124
27440
|
server.tool("search_tasks", "Full-text search across task titles and descriptions.", {
|
|
27125
27441
|
query: exports_external2.string().describe("Search query"),
|
|
27126
27442
|
project_id: exports_external2.string().optional().describe("Filter by project"),
|
|
27127
|
-
status: exports_external2.enum(["pending", "in_progress", "completed", "cancelled"]).optional(),
|
|
27443
|
+
status: exports_external2.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
|
|
27128
27444
|
limit: exports_external2.number().optional().describe("Max results (default: 20)")
|
|
27129
27445
|
}, async ({ query, project_id, status, limit }) => {
|
|
27130
27446
|
try {
|
|
27131
|
-
const { searchTasks: searchTasks2 } = (()
|
|
27447
|
+
const { searchTasks: searchTasks2 } = (init_search(), __toCommonJS(exports_search));
|
|
27132
27448
|
const resolved = { query, limit };
|
|
27133
27449
|
if (project_id)
|
|
27134
27450
|
resolved.project_id = resolveId(project_id, "projects");
|
|
@@ -27146,29 +27462,6 @@ ${lines.join(`
|
|
|
27146
27462
|
}
|
|
27147
27463
|
});
|
|
27148
27464
|
}
|
|
27149
|
-
if (shouldRegisterTool("sync")) {
|
|
27150
|
-
server.tool("sync", "Sync tasks from a GitHub PR or external source into the project.", {
|
|
27151
|
-
source: exports_external2.enum(["github_pr", "linear", "asana"]).describe("Source type"),
|
|
27152
|
-
source_id: exports_external2.string().describe("PR number, Linear issue ID, or Asana task ID"),
|
|
27153
|
-
project_id: exports_external2.string().optional().describe("Project ID to import into"),
|
|
27154
|
-
options: exports_external2.record(exports_external2.unknown()).optional().describe("Source-specific options")
|
|
27155
|
-
}, async ({ source, source_id, project_id, options }) => {
|
|
27156
|
-
try {
|
|
27157
|
-
const { syncFromGithubPR, syncFromLinear, syncFromAsana } = (()=>{throw new Error("Cannot require module "+"../lib/sync.js");})();
|
|
27158
|
-
let result;
|
|
27159
|
-
if (source === "github_pr") {
|
|
27160
|
-
result = await syncFromGithubPR({ prNumber: parseInt(source_id), project_id: project_id ? resolveId(project_id, "projects") : undefined, ...options });
|
|
27161
|
-
} else if (source === "linear") {
|
|
27162
|
-
result = await syncFromLinear({ issueId: source_id, project_id: project_id ? resolveId(project_id, "projects") : undefined, ...options });
|
|
27163
|
-
} else {
|
|
27164
|
-
result = await syncFromAsana({ taskId: source_id, project_id: project_id ? resolveId(project_id, "projects") : undefined, ...options });
|
|
27165
|
-
}
|
|
27166
|
-
return { content: [{ type: "text", text: `Synced from ${source}: ${result.task?.title || source_id}` }] };
|
|
27167
|
-
} catch (e) {
|
|
27168
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
27169
|
-
}
|
|
27170
|
-
});
|
|
27171
|
-
}
|
|
27172
27465
|
}
|
|
27173
27466
|
var init_task_project_tools = __esm(() => {
|
|
27174
27467
|
init_zod2();
|
|
@@ -27183,6 +27476,16 @@ var init_task_project_tools = __esm(() => {
|
|
|
27183
27476
|
// src/mcp/tools/task-workflow-tools.ts
|
|
27184
27477
|
function registerTaskWorkflowTools(server, ctx) {
|
|
27185
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
|
+
}
|
|
27186
27489
|
if (shouldRegisterTool("approve_task")) {
|
|
27187
27490
|
server.tool("approve_task", "Approve a task that requires_approval. Records who approved it.", {
|
|
27188
27491
|
task_id: exports_external2.string().describe("Task ID"),
|
|
@@ -27191,13 +27494,14 @@ function registerTaskWorkflowTools(server, ctx) {
|
|
|
27191
27494
|
version: exports_external2.number().optional().describe("Expected version for optimistic locking")
|
|
27192
27495
|
}, async ({ task_id, approved_by, notes, version }) => {
|
|
27193
27496
|
try {
|
|
27194
|
-
const { updateTask: updateTask2 } = (()
|
|
27497
|
+
const { updateTask: updateTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27195
27498
|
const resolvedId = resolveId(task_id);
|
|
27196
27499
|
const resolvedApprover = approved_by ? resolveId(approved_by, "agents") : undefined;
|
|
27197
27500
|
const task = updateTask2(resolvedId, {
|
|
27198
27501
|
approved_by: resolvedApprover,
|
|
27199
|
-
metadata: notes ? { approval_notes: notes } : {}
|
|
27200
|
-
|
|
27502
|
+
metadata: notes ? { approval_notes: notes } : {},
|
|
27503
|
+
version: versionFor(resolvedId, version)
|
|
27504
|
+
});
|
|
27201
27505
|
return { content: [{ type: "text", text: `Task ${task_id.slice(0, 8)} approved by ${resolvedApprover || "unknown"}` }] };
|
|
27202
27506
|
} catch (e) {
|
|
27203
27507
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -27212,15 +27516,11 @@ function registerTaskWorkflowTools(server, ctx) {
|
|
|
27212
27516
|
version: exports_external2.number().optional().describe("Expected version for optimistic locking")
|
|
27213
27517
|
}, async ({ task_id, reason, agent_id, version }) => {
|
|
27214
27518
|
try {
|
|
27215
|
-
const {
|
|
27519
|
+
const { failTask: failTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27216
27520
|
const resolvedId = resolveId(task_id);
|
|
27217
|
-
|
|
27218
|
-
|
|
27219
|
-
|
|
27220
|
-
back_on_board: false,
|
|
27221
|
-
metadata: reason ? { failure_reason: reason } : {}
|
|
27222
|
-
}, version);
|
|
27223
|
-
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}` }] };
|
|
27224
27524
|
} catch (e) {
|
|
27225
27525
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
27226
27526
|
}
|
|
@@ -27229,12 +27529,12 @@ function registerTaskWorkflowTools(server, ctx) {
|
|
|
27229
27529
|
if (shouldRegisterTool("get_my_tasks")) {
|
|
27230
27530
|
server.tool("get_my_tasks", "Get tasks assigned to the calling agent. Supports focus mode scoping.", {
|
|
27231
27531
|
agent_id: exports_external2.string().optional().describe("Agent ID (defaults to context agent)"),
|
|
27232
|
-
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"),
|
|
27233
27533
|
project_id: exports_external2.string().optional().describe("Filter by project (respects focus mode)"),
|
|
27234
27534
|
limit: exports_external2.number().optional().describe("Max results (default: 50)")
|
|
27235
27535
|
}, async ({ agent_id, status, project_id, limit }) => {
|
|
27236
27536
|
try {
|
|
27237
|
-
const { listTasks: listTasks3 } = (()
|
|
27537
|
+
const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27238
27538
|
const focus = ctx.getAgentFocus(agent_id || "");
|
|
27239
27539
|
const effectiveAgentId = focus ? focus.agent_id : agent_id || "";
|
|
27240
27540
|
const effectiveProjectId = focus?.project_id || project_id;
|
|
@@ -27254,6 +27554,122 @@ function registerTaskWorkflowTools(server, ctx) {
|
|
|
27254
27554
|
}
|
|
27255
27555
|
});
|
|
27256
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.");
|
|
27257
27673
|
if (shouldRegisterTool("get_org_chart")) {
|
|
27258
27674
|
server.tool("get_org_chart", "Get the global org chart (agent hierarchy + titles).", {
|
|
27259
27675
|
format: exports_external2.enum(["text", "json"]).optional().describe("Output format (default: text)")
|
|
@@ -27274,7 +27690,7 @@ function registerTaskWorkflowTools(server, ctx) {
|
|
|
27274
27690
|
}).join(`
|
|
27275
27691
|
`);
|
|
27276
27692
|
};
|
|
27277
|
-
const { getOrgChart: getOrgChart2 } = (()
|
|
27693
|
+
const { getOrgChart: getOrgChart2 } = (init_agents(), __toCommonJS(exports_agents));
|
|
27278
27694
|
const tree = getOrgChart2();
|
|
27279
27695
|
if (format === "json") {
|
|
27280
27696
|
return { content: [{ type: "text", text: JSON.stringify(tree, null, 2) }] };
|
|
@@ -27294,10 +27710,10 @@ function registerTaskWorkflowTools(server, ctx) {
|
|
|
27294
27710
|
reports_to: exports_external2.string().describe("Manager agent ID or name")
|
|
27295
27711
|
}, async ({ agent_id, reports_to }) => {
|
|
27296
27712
|
try {
|
|
27297
|
-
const {
|
|
27713
|
+
const { updateAgent: updateAgent2 } = (init_agents(), __toCommonJS(exports_agents));
|
|
27298
27714
|
const resolvedAgent = resolveId(agent_id, "agents");
|
|
27299
27715
|
const resolvedManager = resolveId(reports_to, "agents");
|
|
27300
|
-
|
|
27716
|
+
updateAgent2(resolvedAgent, { reports_to: resolvedManager });
|
|
27301
27717
|
return { content: [{ type: "text", text: `${agent_id} now reports to ${reports_to}` }] };
|
|
27302
27718
|
} catch (e) {
|
|
27303
27719
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -27307,6 +27723,7 @@ function registerTaskWorkflowTools(server, ctx) {
|
|
|
27307
27723
|
}
|
|
27308
27724
|
var init_task_workflow_tools = __esm(() => {
|
|
27309
27725
|
init_zod2();
|
|
27726
|
+
init_types();
|
|
27310
27727
|
});
|
|
27311
27728
|
|
|
27312
27729
|
// src/mcp/tools/task-auto-tools.ts
|
|
@@ -27318,9 +27735,9 @@ function registerTaskAutoTools(server, ctx) {
|
|
|
27318
27735
|
project_id: exports_external2.string().optional().describe("Scope to a project")
|
|
27319
27736
|
}, async ({ days = 7, project_id }) => {
|
|
27320
27737
|
try {
|
|
27321
|
-
const { archiveCompletedTasks } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27738
|
+
const { archiveCompletedTasks: archiveCompletedTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27322
27739
|
const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
|
|
27323
|
-
const count =
|
|
27740
|
+
const count = archiveCompletedTasks2(days, resolvedProjectId);
|
|
27324
27741
|
return { content: [{ type: "text", text: `Archived ${count} completed task(s) older than ${days} days.` }] };
|
|
27325
27742
|
} catch (e) {
|
|
27326
27743
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -27346,9 +27763,9 @@ function registerTaskAutoTools(server, ctx) {
|
|
|
27346
27763
|
limit: exports_external2.number().optional().describe("Max results (default: 50)")
|
|
27347
27764
|
}, async ({ project_id, limit }) => {
|
|
27348
27765
|
try {
|
|
27349
|
-
const { getArchivedTasks } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27766
|
+
const { getArchivedTasks: getArchivedTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27350
27767
|
const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
|
|
27351
|
-
const tasks =
|
|
27768
|
+
const tasks = getArchivedTasks2({ project_id: resolvedProjectId, limit: limit || 50 });
|
|
27352
27769
|
if (tasks.length === 0)
|
|
27353
27770
|
return { content: [{ type: "text", text: "No archived tasks." }] };
|
|
27354
27771
|
const lines = tasks.map((t) => `${t.short_id || t.id.slice(0, 8)} ${t.title} archived ${t.archived_at}`);
|
|
@@ -27364,14 +27781,13 @@ function registerTaskAutoTools(server, ctx) {
|
|
|
27364
27781
|
task_id: exports_external2.string().describe("Task ID")
|
|
27365
27782
|
}, async ({ task_id }) => {
|
|
27366
27783
|
try {
|
|
27367
|
-
const { autoAssign } = (init_agents(), __toCommonJS(exports_agents));
|
|
27368
27784
|
const resolvedId = resolveId(task_id);
|
|
27369
|
-
const
|
|
27370
|
-
|
|
27785
|
+
const { autoAssignTask: autoAssignTask2 } = (init_auto_assign(), __toCommonJS(exports_auto_assign));
|
|
27786
|
+
const assignment = await autoAssignTask2(resolvedId);
|
|
27787
|
+
if (!assignment.assigned_to)
|
|
27371
27788
|
return { content: [{ type: "text", text: "No suitable agent found for this task." }] };
|
|
27372
|
-
const
|
|
27373
|
-
|
|
27374
|
-
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}` }] };
|
|
27375
27791
|
} catch (e) {
|
|
27376
27792
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
27377
27793
|
}
|
|
@@ -27382,10 +27798,23 @@ function registerTaskAutoTools(server, ctx) {
|
|
|
27382
27798
|
agent_id: exports_external2.string().optional().describe("Agent ID (defaults to context agent)")
|
|
27383
27799
|
}, async ({ agent_id }) => {
|
|
27384
27800
|
try {
|
|
27385
|
-
const {
|
|
27801
|
+
const { listTasks: listTasks3, getBlockedTasks: getBlockedTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27386
27802
|
const focus = ctx.getAgentFocus(agent_id || "");
|
|
27387
27803
|
const effectiveAgentId = focus ? focus.agent_id : agent_id || "";
|
|
27388
|
-
|
|
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
|
+
};
|
|
27389
27818
|
const lines = [
|
|
27390
27819
|
`Agent: ${effectiveAgentId}`,
|
|
27391
27820
|
`In Progress: ${workload.in_progress}`,
|
|
@@ -27407,10 +27836,29 @@ function registerTaskAutoTools(server, ctx) {
|
|
|
27407
27836
|
max_per_agent: exports_external2.number().optional().describe("Max tasks per agent (default: 5)")
|
|
27408
27837
|
}, async ({ project_id, max_per_agent }) => {
|
|
27409
27838
|
try {
|
|
27410
|
-
const {
|
|
27839
|
+
const { listAgents: listAgents2 } = (init_agents(), __toCommonJS(exports_agents));
|
|
27840
|
+
const { listTasks: listTasks3, updateTask: updateTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27411
27841
|
const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
|
|
27412
|
-
const
|
|
27413
|
-
|
|
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.` }] };
|
|
27414
27862
|
} catch (e) {
|
|
27415
27863
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
27416
27864
|
}
|
|
@@ -27423,13 +27871,13 @@ function registerTaskAutoTools(server, ctx) {
|
|
|
27423
27871
|
agent_id: exports_external2.string().optional().describe("Filter by assignee")
|
|
27424
27872
|
}, async ({ hours = 24, project_id, agent_id }) => {
|
|
27425
27873
|
try {
|
|
27426
|
-
const { notifyUpcomingDeadlines } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27874
|
+
const { notifyUpcomingDeadlines: notifyUpcomingDeadlines2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27427
27875
|
const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
|
|
27428
27876
|
const resolvedAgentId = agent_id ? resolveId(agent_id, "agents") : undefined;
|
|
27429
|
-
const tasks =
|
|
27877
|
+
const tasks = notifyUpcomingDeadlines2({ hours, project_id: resolvedProjectId, agent_id: resolvedAgentId });
|
|
27430
27878
|
if (tasks.length === 0)
|
|
27431
27879
|
return { content: [{ type: "text", text: "No deadlines approaching." }] };
|
|
27432
|
-
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}`);
|
|
27433
27881
|
return { content: [{ type: "text", text: `${tasks.length} task(s) due within ${hours}h:
|
|
27434
27882
|
${lines.join(`
|
|
27435
27883
|
`)}` }] };
|
|
@@ -27463,9 +27911,9 @@ ${lines.join(`
|
|
|
27463
27911
|
project_id: exports_external2.string().optional().describe("Filter by project")
|
|
27464
27912
|
}, async ({ project_id }) => {
|
|
27465
27913
|
try {
|
|
27466
|
-
const { getBlockedTasks } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27914
|
+
const { getBlockedTasks: getBlockedTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27467
27915
|
const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
|
|
27468
|
-
const tasks =
|
|
27916
|
+
const tasks = getBlockedTasks2(resolvedProjectId);
|
|
27469
27917
|
if (tasks.length === 0)
|
|
27470
27918
|
return { content: [{ type: "text", text: "No blocked tasks." }] };
|
|
27471
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(", ")}`);
|
|
@@ -27482,9 +27930,9 @@ ${lines.join(`
|
|
|
27482
27930
|
project_id: exports_external2.string().optional().describe("Filter by project")
|
|
27483
27931
|
}, async ({ project_id }) => {
|
|
27484
27932
|
try {
|
|
27485
|
-
const { getBlockingTasks } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27933
|
+
const { getBlockingTasks: getBlockingTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27486
27934
|
const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
|
|
27487
|
-
const tasks =
|
|
27935
|
+
const tasks = getBlockingTasks2(resolvedProjectId);
|
|
27488
27936
|
if (tasks.length === 0)
|
|
27489
27937
|
return { content: [{ type: "text", text: "No tasks blocking others." }] };
|
|
27490
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)`);
|
|
@@ -27499,20 +27947,18 @@ ${lines.join(`
|
|
|
27499
27947
|
if (shouldRegisterTool("get_health")) {
|
|
27500
27948
|
server.tool("get_health", "Get system health: task counts by status, active agents, project summary.", async () => {
|
|
27501
27949
|
try {
|
|
27502
|
-
const {
|
|
27503
|
-
const { listProjects: listProjects2 } = (
|
|
27950
|
+
const { countTasks: countTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27951
|
+
const { listProjects: listProjects2 } = (init_projects(), __toCommonJS(exports_projects));
|
|
27504
27952
|
const { listAgents: listAgents2 } = (init_agents(), __toCommonJS(exports_agents));
|
|
27505
|
-
const
|
|
27506
|
-
|
|
27507
|
-
|
|
27508
|
-
|
|
27509
|
-
|
|
27510
|
-
|
|
27511
|
-
const projects = listProjects2({ limit: 100 });
|
|
27512
|
-
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();
|
|
27513
27959
|
const lines = [
|
|
27514
27960
|
`=== System Health ===`,
|
|
27515
|
-
`Tasks: ${pending
|
|
27961
|
+
`Tasks: ${pending} pending | ${inProgress} in progress | ${completed} completed | ${cancelled} cancelled`,
|
|
27516
27962
|
`Projects: ${projects.length} total`,
|
|
27517
27963
|
`Agents: ${agents.length} registered`
|
|
27518
27964
|
];
|
|
@@ -27658,17 +28104,31 @@ var init_task_relationships = __esm(() => {
|
|
|
27658
28104
|
function registerTaskAdvTools(server, ctx) {
|
|
27659
28105
|
const { shouldRegisterTool, resolveId, formatError, formatTask, formatTaskDetail } = ctx;
|
|
27660
28106
|
if (shouldRegisterTool("get_status")) {
|
|
27661
|
-
server.tool("get_status", "Get
|
|
27662
|
-
task_id: exports_external2.string().describe("Task ID")
|
|
27663
|
-
|
|
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 }) => {
|
|
27664
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
|
+
}
|
|
27665
28125
|
const resolvedId = resolveId(task_id);
|
|
27666
|
-
const { getTask:
|
|
27667
|
-
const task =
|
|
28126
|
+
const { getTask: getTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
28127
|
+
const task = getTask2(resolvedId);
|
|
27668
28128
|
if (!task)
|
|
27669
28129
|
throw new Error(`Task not found: ${task_id}`);
|
|
27670
28130
|
const { getTaskDependencies: getTaskDependencies3 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27671
|
-
const { listComments: listComments2 } = (
|
|
28131
|
+
const { listComments: listComments2 } = (init_comments(), __toCommonJS(exports_comments));
|
|
27672
28132
|
const { listTaskFiles: listTaskFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
|
|
27673
28133
|
const [deps, comments, files] = await Promise.all([
|
|
27674
28134
|
Promise.resolve(getTaskDependencies3(resolvedId, "both")),
|
|
@@ -27678,14 +28138,14 @@ function registerTaskAdvTools(server, ctx) {
|
|
|
27678
28138
|
const lines = [
|
|
27679
28139
|
`Status: ${task.status} | Priority: ${task.priority}`,
|
|
27680
28140
|
task.assigned_to ? `Assigned: ${task.assigned_to}` : "Unassigned",
|
|
27681
|
-
task.
|
|
28141
|
+
task.due_at ? `Due: ${task.due_at}` : null,
|
|
27682
28142
|
task.confidence != null ? `Confidence: ${task.confidence}` : null,
|
|
27683
28143
|
deps.length > 0 ? `
|
|
27684
28144
|
Dependencies (${deps.length}):` : null,
|
|
27685
28145
|
...deps.map((d) => ` [${d.direction}] ${d.task_id.slice(0, 8)} (${d.status})`),
|
|
27686
28146
|
comments.length > 0 ? `
|
|
27687
28147
|
Comments (${comments.length}):` : null,
|
|
27688
|
-
...comments.map((c) => ` [${c.
|
|
28148
|
+
...comments.map((c) => ` [${c.agent_id || "?"}] ${c.created_at?.slice(0, 16)}: ${c.content.slice(0, 80)}`),
|
|
27689
28149
|
files.length > 0 ? `
|
|
27690
28150
|
Files (${files.length}):` : null,
|
|
27691
28151
|
...files.map((f) => ` ${f.status} ${f.path}`)
|
|
@@ -27703,15 +28163,15 @@ Files (${files.length}):` : null,
|
|
|
27703
28163
|
}, async ({ task_id }) => {
|
|
27704
28164
|
try {
|
|
27705
28165
|
const resolvedId = resolveId(task_id);
|
|
27706
|
-
const { getTask:
|
|
27707
|
-
const task =
|
|
28166
|
+
const { getTask: getTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
28167
|
+
const task = getTask2(resolvedId);
|
|
27708
28168
|
if (!task)
|
|
27709
28169
|
throw new Error(`Task not found: ${task_id}`);
|
|
27710
28170
|
const { getTaskDependencies: getTaskDependencies3 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27711
28171
|
const { getTaskRelationships: getTaskRelationships2 } = (init_task_relationships(), __toCommonJS(exports_task_relationships));
|
|
27712
|
-
const { listComments: listComments2 } = (
|
|
28172
|
+
const { listComments: listComments2 } = (init_comments(), __toCommonJS(exports_comments));
|
|
27713
28173
|
const { listTaskFiles: listTaskFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
|
|
27714
|
-
const { getTaskCommits: getTaskCommits2 } = (
|
|
28174
|
+
const { getTaskCommits: getTaskCommits2 } = (init_task_commits(), __toCommonJS(exports_task_commits));
|
|
27715
28175
|
const { getTaskWatchers: getTaskWatchers2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27716
28176
|
const [deps, rels, comments, files, commits, watchers] = await Promise.all([
|
|
27717
28177
|
Promise.resolve(getTaskDependencies3(resolvedId, "both")),
|
|
@@ -27732,7 +28192,7 @@ Files (${files.length}):` : null,
|
|
|
27732
28192
|
task.tags?.length ? `Tags: ${task.tags.join(", ")}` : null,
|
|
27733
28193
|
task.created_at ? `Created: ${task.created_at}` : null,
|
|
27734
28194
|
task.updated_at ? `Updated: ${task.updated_at}` : null,
|
|
27735
|
-
task.
|
|
28195
|
+
task.due_at ? `Due: ${task.due_at}` : null,
|
|
27736
28196
|
task.completed_at ? `Completed: ${task.completed_at}` : null,
|
|
27737
28197
|
deps.length > 0 ? `
|
|
27738
28198
|
--- Dependencies (${deps.length}) ---` : null,
|
|
@@ -27742,7 +28202,7 @@ Files (${files.length}):` : null,
|
|
|
27742
28202
|
...rels.map((r) => ` ${r.source_task_id.slice(0, 8)} --[${r.relationship_type}]--> ${r.target_task_id.slice(0, 8)}`),
|
|
27743
28203
|
comments.length > 0 ? `
|
|
27744
28204
|
--- Comments (${comments.length}) ---` : null,
|
|
27745
|
-
...comments.map((c) => ` [${c.
|
|
28205
|
+
...comments.map((c) => ` [${c.agent_id || "?"}] ${c.created_at?.slice(0, 16)}: ${c.content.slice(0, 120)}`),
|
|
27746
28206
|
files.length > 0 ? `
|
|
27747
28207
|
--- Files (${files.length}) ---` : null,
|
|
27748
28208
|
...files.map((f) => ` [${f.status}] ${f.path}`),
|
|
@@ -27791,8 +28251,8 @@ ${JSON.stringify(task.metadata, null, 2)}` : null
|
|
|
27791
28251
|
limit: 20
|
|
27792
28252
|
}, undefined);
|
|
27793
28253
|
const completedYesterday = completed.filter((t) => t.completed_at && t.completed_at.startsWith(yesterdayStr));
|
|
27794
|
-
const { getBlockedTasks } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27795
|
-
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);
|
|
27796
28256
|
const lines = [
|
|
27797
28257
|
`Standup for ${effectiveAgentId} (${effectiveProjectId ? `project: ${effectiveProjectId.slice(0, 8)}` : "all projects"})`,
|
|
27798
28258
|
inProgress.length > 0 ? `
|
|
@@ -27821,11 +28281,11 @@ No blocked tasks.`,
|
|
|
27821
28281
|
agent_id: exports_external2.string().optional().describe("Agent claiming (defaults to context)")
|
|
27822
28282
|
}, async ({ task_id, agent_id }) => {
|
|
27823
28283
|
try {
|
|
27824
|
-
const {
|
|
28284
|
+
const { startTask: startTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27825
28285
|
const resolvedId = resolveId(task_id);
|
|
27826
28286
|
const focus = ctx.getAgentFocus(agent_id || "");
|
|
27827
|
-
const effectiveAgent = focus ? focus.agent_id : agent_id || "";
|
|
27828
|
-
const task =
|
|
28287
|
+
const effectiveAgent = focus ? focus.agent_id : agent_id || "mcp";
|
|
28288
|
+
const task = startTask2(resolvedId, effectiveAgent);
|
|
27829
28289
|
return { content: [{ type: "text", text: formatTask(task) }] };
|
|
27830
28290
|
} catch (e) {
|
|
27831
28291
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -27837,8 +28297,12 @@ No blocked tasks.`,
|
|
|
27837
28297
|
task_id: exports_external2.string().describe("Task ID")
|
|
27838
28298
|
}, async ({ task_id }) => {
|
|
27839
28299
|
try {
|
|
27840
|
-
const { updateTask: updateTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27841
|
-
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 });
|
|
27842
28306
|
return { content: [{ type: "text", text: formatTask(task) }] };
|
|
27843
28307
|
} catch (e) {
|
|
27844
28308
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -27851,13 +28315,13 @@ No blocked tasks.`,
|
|
|
27851
28315
|
minutes: exports_external2.number().describe("Additional minutes to add to estimate")
|
|
27852
28316
|
}, async ({ task_id, minutes }) => {
|
|
27853
28317
|
try {
|
|
27854
|
-
const { getTask:
|
|
28318
|
+
const { getTask: getTask2, updateTask: updateTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
27855
28319
|
const resolvedId = resolveId(task_id);
|
|
27856
|
-
const task =
|
|
28320
|
+
const task = getTask2(resolvedId);
|
|
27857
28321
|
if (!task)
|
|
27858
28322
|
throw new Error(`Task not found: ${task_id}`);
|
|
27859
28323
|
const currentEstimate = task.estimated_minutes || 0;
|
|
27860
|
-
const updated = updateTask2(resolvedId, { estimated_minutes: currentEstimate + minutes });
|
|
28324
|
+
const updated = updateTask2(resolvedId, { estimated_minutes: currentEstimate + minutes, version: task.version });
|
|
27861
28325
|
return { content: [{ type: "text", text: `Estimate updated: ${currentEstimate} \u2192 ${updated.estimated_minutes} min (+${minutes})` }] };
|
|
27862
28326
|
} catch (e) {
|
|
27863
28327
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -27871,10 +28335,10 @@ No blocked tasks.`,
|
|
|
27871
28335
|
author: exports_external2.string().optional().describe("Author agent ID or name")
|
|
27872
28336
|
}, async ({ task_id, body, author }) => {
|
|
27873
28337
|
try {
|
|
27874
|
-
const {
|
|
28338
|
+
const { addComment: addComment2 } = (init_comments(), __toCommonJS(exports_comments));
|
|
27875
28339
|
const resolvedId = resolveId(task_id);
|
|
27876
28340
|
const resolvedAuthor = author ? resolveId(author, "agents") : undefined;
|
|
27877
|
-
|
|
28341
|
+
addComment2({ task_id: resolvedId, content: body, agent_id: resolvedAuthor });
|
|
27878
28342
|
return { content: [{ type: "text", text: `Comment added to ${task_id.slice(0, 8)}` }] };
|
|
27879
28343
|
} catch (e) {
|
|
27880
28344
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -27886,11 +28350,11 @@ No blocked tasks.`,
|
|
|
27886
28350
|
task_id: exports_external2.string().describe("Task ID")
|
|
27887
28351
|
}, async ({ task_id }) => {
|
|
27888
28352
|
try {
|
|
27889
|
-
const { listComments: listComments2 } = (
|
|
28353
|
+
const { listComments: listComments2 } = (init_comments(), __toCommonJS(exports_comments));
|
|
27890
28354
|
const comments = listComments2(resolveId(task_id));
|
|
27891
28355
|
if (comments.length === 0)
|
|
27892
28356
|
return { content: [{ type: "text", text: "No comments." }] };
|
|
27893
|
-
const lines = comments.map((c) => `[${c.
|
|
28357
|
+
const lines = comments.map((c) => `[${c.agent_id || "?"}] ${c.created_at?.slice(0, 16)}: ${c.content}`);
|
|
27894
28358
|
return { content: [{ type: "text", text: lines.join(`
|
|
27895
28359
|
|
|
27896
28360
|
`) }] };
|
|
@@ -27902,7 +28366,7 @@ No blocked tasks.`,
|
|
|
27902
28366
|
if (shouldRegisterTool("list_my_tasks")) {
|
|
27903
28367
|
server.tool("list_my_tasks", "Alias for get_my_tasks.", {
|
|
27904
28368
|
agent_id: exports_external2.string().optional().describe("Agent ID (defaults to context agent)"),
|
|
27905
|
-
status: exports_external2.enum(["pending", "in_progress", "completed", "cancelled"]).optional(),
|
|
28369
|
+
status: exports_external2.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
|
|
27906
28370
|
project_id: exports_external2.string().optional().describe("Filter by project"),
|
|
27907
28371
|
limit: exports_external2.number().optional()
|
|
27908
28372
|
}, async ({ agent_id, status, project_id, limit }) => {
|
|
@@ -27927,126 +28391,6 @@ No blocked tasks.`,
|
|
|
27927
28391
|
}
|
|
27928
28392
|
});
|
|
27929
28393
|
}
|
|
27930
|
-
if (shouldRegisterTool("list_agents")) {
|
|
27931
|
-
server.tool("list_agents", "List all registered agents.", {
|
|
27932
|
-
project_id: exports_external2.string().optional().describe("Filter by project"),
|
|
27933
|
-
role: exports_external2.string().optional().describe("Filter by global role"),
|
|
27934
|
-
capabilities: exports_external2.array(exports_external2.string()).optional().describe("Filter by capabilities")
|
|
27935
|
-
}, async ({ project_id, role, capabilities }) => {
|
|
27936
|
-
try {
|
|
27937
|
-
const { listAgents: listAgents2 } = (init_agents(), __toCommonJS(exports_agents));
|
|
27938
|
-
const resolved = {};
|
|
27939
|
-
if (project_id)
|
|
27940
|
-
resolved.project_id = resolveId(project_id, "projects");
|
|
27941
|
-
if (role)
|
|
27942
|
-
resolved.role = role;
|
|
27943
|
-
if (capabilities)
|
|
27944
|
-
resolved.capabilities = capabilities;
|
|
27945
|
-
const agents = listAgents2(resolved);
|
|
27946
|
-
if (agents.length === 0)
|
|
27947
|
-
return { content: [{ type: "text", text: "No agents found." }] };
|
|
27948
|
-
const lines = agents.map((a) => `\u25CF ${a.name} [${a.role || "no role"}]${a.capabilities?.length ? ` caps:[${a.capabilities.join(",")}]` : ""}`);
|
|
27949
|
-
return { content: [{ type: "text", text: lines.join(`
|
|
27950
|
-
`) }] };
|
|
27951
|
-
} catch (e) {
|
|
27952
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
27953
|
-
}
|
|
27954
|
-
});
|
|
27955
|
-
}
|
|
27956
|
-
if (shouldRegisterTool("get_agent")) {
|
|
27957
|
-
server.tool("get_agent", "Get full details for an agent.", {
|
|
27958
|
-
agent_id: exports_external2.string().describe("Agent ID or name")
|
|
27959
|
-
}, async ({ agent_id }) => {
|
|
27960
|
-
try {
|
|
27961
|
-
const { getAgentByName: getAgentByName2, getAgent: getAgent2 } = (init_agents(), __toCommonJS(exports_agents));
|
|
27962
|
-
const resolvedId = resolveId(agent_id, "agents");
|
|
27963
|
-
const agent = agent_id.includes("@") || !resolvedId.startsWith("agent_") ? getAgentByName2(agent_id) : getAgent2(resolvedId);
|
|
27964
|
-
if (!agent)
|
|
27965
|
-
return { content: [{ type: "text", text: `Agent not found: ${agent_id}` }], isError: true };
|
|
27966
|
-
const lines = [
|
|
27967
|
-
`ID: ${agent.id}`,
|
|
27968
|
-
`Name: ${agent.name}`,
|
|
27969
|
-
agent.email ? `Email: ${agent.email}` : null,
|
|
27970
|
-
agent.title ? `Title: ${agent.title}` : null,
|
|
27971
|
-
agent.role ? `Role: ${agent.role}` : null,
|
|
27972
|
-
agent.capabilities?.length ? `Capabilities: ${agent.capabilities.join(", ")}` : null,
|
|
27973
|
-
agent.last_seen_at ? `Last Seen: ${agent.last_seen_at}` : null
|
|
27974
|
-
].filter(Boolean);
|
|
27975
|
-
return { content: [{ type: "text", text: lines.join(`
|
|
27976
|
-
`) }] };
|
|
27977
|
-
} catch (e) {
|
|
27978
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
27979
|
-
}
|
|
27980
|
-
});
|
|
27981
|
-
}
|
|
27982
|
-
if (shouldRegisterTool("update_agent")) {
|
|
27983
|
-
server.tool("update_agent", "Update an agent's fields.", {
|
|
27984
|
-
agent_id: exports_external2.string().describe("Agent ID or name"),
|
|
27985
|
-
name: exports_external2.string().optional(),
|
|
27986
|
-
email: exports_external2.string().optional(),
|
|
27987
|
-
title: exports_external2.string().optional(),
|
|
27988
|
-
role: exports_external2.string().optional(),
|
|
27989
|
-
capabilities: exports_external2.array(exports_external2.string()).optional()
|
|
27990
|
-
}, async ({ agent_id, ...updates }) => {
|
|
27991
|
-
try {
|
|
27992
|
-
const { getAgentByName: getAgentByName2, updateAgent: updateAgent2 } = (init_agents(), __toCommonJS(exports_agents));
|
|
27993
|
-
const resolvedId = resolveId(agent_id, "agents");
|
|
27994
|
-
const agent = agent_id.includes("@") || !resolvedId.startsWith("agent_") ? getAgentByName2(agent_id) : { id: resolvedId };
|
|
27995
|
-
updateAgent2(agent.id, updates);
|
|
27996
|
-
return { content: [{ type: "text", text: `Agent ${agent.id.slice(0, 8)} updated.` }] };
|
|
27997
|
-
} catch (e) {
|
|
27998
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
27999
|
-
}
|
|
28000
|
-
});
|
|
28001
|
-
}
|
|
28002
|
-
if (shouldRegisterTool("delete_agent")) {
|
|
28003
|
-
server.tool("delete_agent", "Deregister an agent.", {
|
|
28004
|
-
agent_id: exports_external2.string().describe("Agent ID or name")
|
|
28005
|
-
}, async ({ agent_id }) => {
|
|
28006
|
-
try {
|
|
28007
|
-
const { getAgentByName: getAgentByName2, deleteAgent: deleteAgent2 } = (init_agents(), __toCommonJS(exports_agents));
|
|
28008
|
-
const resolvedId = resolveId(agent_id, "agents");
|
|
28009
|
-
const agent = agent_id.includes("@") || !resolvedId.startsWith("agent_") ? getAgentByName2(agent_id) : { id: resolvedId };
|
|
28010
|
-
if (!agent)
|
|
28011
|
-
return { content: [{ type: "text", text: `Agent not found: ${agent_id}` }], isError: true };
|
|
28012
|
-
deleteAgent2(agent.id);
|
|
28013
|
-
return { content: [{ type: "text", text: `Agent ${agent_id} deleted.` }] };
|
|
28014
|
-
} catch (e) {
|
|
28015
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
28016
|
-
}
|
|
28017
|
-
});
|
|
28018
|
-
}
|
|
28019
|
-
if (shouldRegisterTool("register_agent")) {
|
|
28020
|
-
server.tool("register_agent", "Register a new agent.", {
|
|
28021
|
-
name: exports_external2.string().describe("Agent name"),
|
|
28022
|
-
email: exports_external2.string().optional(),
|
|
28023
|
-
title: exports_external2.string().optional(),
|
|
28024
|
-
role: exports_external2.string().optional(),
|
|
28025
|
-
capabilities: exports_external2.array(exports_external2.string()).optional()
|
|
28026
|
-
}, async ({ name, email, title, role, capabilities }) => {
|
|
28027
|
-
try {
|
|
28028
|
-
const { registerAgent: registerAgent2 } = (init_agents(), __toCommonJS(exports_agents));
|
|
28029
|
-
const agent = registerAgent2({ name, email, title, role, capabilities });
|
|
28030
|
-
return { content: [{ type: "text", text: `Agent registered: ${agent.name} (${agent.id})` }] };
|
|
28031
|
-
} catch (e) {
|
|
28032
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
28033
|
-
}
|
|
28034
|
-
});
|
|
28035
|
-
}
|
|
28036
|
-
if (shouldRegisterTool("heartbeat")) {
|
|
28037
|
-
server.tool("heartbeat", "Agent heartbeat \u2014 updates last_seen_at timestamp.", {
|
|
28038
|
-
agent_id: exports_external2.string().describe("Agent ID"),
|
|
28039
|
-
status: exports_external2.string().optional().describe("Current status message")
|
|
28040
|
-
}, async ({ agent_id, status }) => {
|
|
28041
|
-
try {
|
|
28042
|
-
const { heartbeat } = (init_agents(), __toCommonJS(exports_agents));
|
|
28043
|
-
heartbeat(resolveId(agent_id, "agents"), status);
|
|
28044
|
-
return { content: [{ type: "text", text: "Heartbeat received." }] };
|
|
28045
|
-
} catch (e) {
|
|
28046
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
28047
|
-
}
|
|
28048
|
-
});
|
|
28049
|
-
}
|
|
28050
28394
|
}
|
|
28051
28395
|
var init_task_adv_tools = __esm(() => {
|
|
28052
28396
|
init_zod2();
|
|
@@ -28091,6 +28435,11 @@ function registerTaskMetaTools(server, ctx) {
|
|
|
28091
28435
|
prioritize_task: "prioritize_task \u2014 Set priority. Params: task_id, priority, version",
|
|
28092
28436
|
search_tasks: "search_tasks \u2014 Full-text search. Params: query, project_id, status, limit",
|
|
28093
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",
|
|
28094
28443
|
standup: "standup \u2014 Get standup report. Params: agent_id, project_id",
|
|
28095
28444
|
patrol_tasks: "patrol_tasks \u2014 Scan for task issues. Params: stuck_minutes, confidence_threshold, project_id",
|
|
28096
28445
|
get_review_queue: "get_review_queue \u2014 Get tasks needing review. Params: project_id, limit",
|
|
@@ -28148,9 +28497,20 @@ function registerTaskMetaTools(server, ctx) {
|
|
|
28148
28497
|
get_health: "get_health \u2014 Get system health stats",
|
|
28149
28498
|
approve_task: "approve_task \u2014 Approve a task. Params: task_id, approved_by, notes, version",
|
|
28150
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",
|
|
28151
28512
|
get_org_chart: "get_org_chart \u2014 Get global org chart. Params: format",
|
|
28152
28513
|
set_reports_to: "set_reports_to \u2014 Set org hierarchy. Params: agent_id, reports_to",
|
|
28153
|
-
sync: "sync \u2014 Sync from external source. Params: source, source_id, project_id, options",
|
|
28154
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",
|
|
28155
28515
|
migrate_pg: "migrate_pg \u2014 Apply PostgreSQL migrations. Params: connection_string"
|
|
28156
28516
|
};
|
|
@@ -28168,6 +28528,74 @@ var init_task_meta_tools = __esm(() => {
|
|
|
28168
28528
|
init_zod2();
|
|
28169
28529
|
});
|
|
28170
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
|
+
|
|
28171
28599
|
// src/mcp/tools/task-resources.ts
|
|
28172
28600
|
function registerTaskResources(server, ctx) {
|
|
28173
28601
|
const { shouldRegisterTool, resolveId, formatError } = ctx;
|
|
@@ -28193,7 +28621,7 @@ function registerTaskResources(server, ctx) {
|
|
|
28193
28621
|
note: exports_external2.string().optional().describe("Note about why this file is linked")
|
|
28194
28622
|
}, async ({ task_id, path, paths: multiplePaths, status, agent_id, note }) => {
|
|
28195
28623
|
try {
|
|
28196
|
-
const { addTaskFile: addTaskFile2, bulkAddTaskFiles: bulkAddTaskFiles2, detectFileConflicts: detectFileConflicts2 } = (()
|
|
28624
|
+
const { addTaskFile: addTaskFile2, bulkAddTaskFiles: bulkAddTaskFiles2, detectFileConflicts: detectFileConflicts2 } = (init_task_files(), __toCommonJS(exports_task_files));
|
|
28197
28625
|
const resolvedId = resolveId(task_id);
|
|
28198
28626
|
let addedFiles;
|
|
28199
28627
|
if (multiplePaths && multiplePaths.length > 0) {
|
|
@@ -28237,7 +28665,7 @@ function registerTaskResources(server, ctx) {
|
|
|
28237
28665
|
if (shouldRegisterTool("list_task_files")) {
|
|
28238
28666
|
server.tool("list_task_files", "List all files linked to a task.", { task_id: exports_external2.string().describe("Task ID") }, async ({ task_id }) => {
|
|
28239
28667
|
try {
|
|
28240
|
-
const { listTaskFiles: listTaskFiles2 } = (()
|
|
28668
|
+
const { listTaskFiles: listTaskFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
|
|
28241
28669
|
const resolvedId = resolveId(task_id);
|
|
28242
28670
|
const files = listTaskFiles2(resolvedId);
|
|
28243
28671
|
if (files.length === 0)
|
|
@@ -28254,7 +28682,7 @@ ${lines.join(`
|
|
|
28254
28682
|
if (shouldRegisterTool("find_tasks_by_file")) {
|
|
28255
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 }) => {
|
|
28256
28684
|
try {
|
|
28257
|
-
const { findTasksByFile: findTasksByFile2 } = (()
|
|
28685
|
+
const { findTasksByFile: findTasksByFile2 } = (init_task_files(), __toCommonJS(exports_task_files));
|
|
28258
28686
|
const files = findTasksByFile2(path);
|
|
28259
28687
|
if (files.length === 0)
|
|
28260
28688
|
return { content: [{ type: "text", text: `No tasks linked to ${path}` }] };
|
|
@@ -28274,7 +28702,7 @@ ${lines.join(`
|
|
|
28274
28702
|
min_edits: exports_external2.number().optional().describe("Minimum edit count to include (default: 1)")
|
|
28275
28703
|
}, async ({ limit, project_id, min_edits }) => {
|
|
28276
28704
|
try {
|
|
28277
|
-
const { getFileHeatMap: getFileHeatMap2 } = (()
|
|
28705
|
+
const { getFileHeatMap: getFileHeatMap2 } = (init_task_files(), __toCommonJS(exports_task_files));
|
|
28278
28706
|
const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
|
|
28279
28707
|
const results = getFileHeatMap2({ limit, project_id: resolvedProjectId, min_edits });
|
|
28280
28708
|
return { content: [{ type: "text", text: JSON.stringify(results, null, 2) }] };
|
|
@@ -28288,7 +28716,7 @@ ${lines.join(`
|
|
|
28288
28716
|
paths: exports_external2.array(exports_external2.string()).describe("Array of file paths to check")
|
|
28289
28717
|
}, async ({ paths }) => {
|
|
28290
28718
|
try {
|
|
28291
|
-
const { bulkFindTasksByFiles: bulkFindTasksByFiles2 } = (()
|
|
28719
|
+
const { bulkFindTasksByFiles: bulkFindTasksByFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
|
|
28292
28720
|
const results = bulkFindTasksByFiles2(paths);
|
|
28293
28721
|
return { content: [{ type: "text", text: JSON.stringify(results, null, 2) }] };
|
|
28294
28722
|
} catch (e) {
|
|
@@ -28301,11 +28729,11 @@ ${lines.join(`
|
|
|
28301
28729
|
project_id: exports_external2.string().optional().describe("Filter by project")
|
|
28302
28730
|
}, async ({ project_id }) => {
|
|
28303
28731
|
try {
|
|
28304
|
-
const { listActiveFiles: listActiveFiles2 } = (()
|
|
28732
|
+
const { listActiveFiles: listActiveFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
|
|
28305
28733
|
let files = listActiveFiles2();
|
|
28306
28734
|
if (project_id) {
|
|
28307
28735
|
const pid = resolveId(project_id, "projects");
|
|
28308
|
-
const db = (()
|
|
28736
|
+
const db = (init_database(), __toCommonJS(exports_database)).getDatabase();
|
|
28309
28737
|
files = db.query(`
|
|
28310
28738
|
SELECT
|
|
28311
28739
|
tf.path,
|
|
@@ -28387,8 +28815,8 @@ ${lines.join(`
|
|
|
28387
28815
|
ttl_seconds: exports_external2.number().optional().describe("Lock TTL in seconds (default: 1800 = 30 min)")
|
|
28388
28816
|
}, async ({ path, agent_id, task_id, ttl_seconds }) => {
|
|
28389
28817
|
try {
|
|
28390
|
-
const { lockFile } = (()
|
|
28391
|
-
const lock =
|
|
28818
|
+
const { lockFile: lockFile2 } = (init_file_locks(), __toCommonJS(exports_file_locks));
|
|
28819
|
+
const lock = lockFile2({ path, agent_id, task_id, ttl_seconds });
|
|
28392
28820
|
return { content: [{ type: "text", text: JSON.stringify(lock, null, 2) }] };
|
|
28393
28821
|
} catch (e) {
|
|
28394
28822
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -28401,8 +28829,8 @@ ${lines.join(`
|
|
|
28401
28829
|
agent_id: exports_external2.string().describe("Agent releasing the lock (must be the lock holder)")
|
|
28402
28830
|
}, async ({ path, agent_id }) => {
|
|
28403
28831
|
try {
|
|
28404
|
-
const { unlockFile } = (()
|
|
28405
|
-
const released =
|
|
28832
|
+
const { unlockFile: unlockFile2 } = (init_file_locks(), __toCommonJS(exports_file_locks));
|
|
28833
|
+
const released = unlockFile2(path, agent_id);
|
|
28406
28834
|
return { content: [{ type: "text", text: JSON.stringify({ released, path }) }] };
|
|
28407
28835
|
} catch (e) {
|
|
28408
28836
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -28414,8 +28842,8 @@ ${lines.join(`
|
|
|
28414
28842
|
path: exports_external2.string().describe("File path to check")
|
|
28415
28843
|
}, async ({ path }) => {
|
|
28416
28844
|
try {
|
|
28417
|
-
const { checkFileLock } = (()
|
|
28418
|
-
const lock =
|
|
28845
|
+
const { checkFileLock: checkFileLock2 } = (init_file_locks(), __toCommonJS(exports_file_locks));
|
|
28846
|
+
const lock = checkFileLock2(path);
|
|
28419
28847
|
if (!lock)
|
|
28420
28848
|
return { content: [{ type: "text", text: JSON.stringify({ path, locked: false }) }] };
|
|
28421
28849
|
return { content: [{ type: "text", text: JSON.stringify({ path, locked: true, ...lock }) }] };
|
|
@@ -28429,8 +28857,8 @@ ${lines.join(`
|
|
|
28429
28857
|
agent_id: exports_external2.string().optional().describe("Filter locks by agent")
|
|
28430
28858
|
}, async ({ agent_id }) => {
|
|
28431
28859
|
try {
|
|
28432
|
-
const { listFileLocks } = (()
|
|
28433
|
-
const locks =
|
|
28860
|
+
const { listFileLocks: listFileLocks2 } = (init_file_locks(), __toCommonJS(exports_file_locks));
|
|
28861
|
+
const locks = listFileLocks2(agent_id);
|
|
28434
28862
|
return { content: [{ type: "text", text: JSON.stringify(locks, null, 2) }] };
|
|
28435
28863
|
} catch (e) {
|
|
28436
28864
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -28446,6 +28874,533 @@ var init_task_resources = __esm(() => {
|
|
|
28446
28874
|
init_task_commits();
|
|
28447
28875
|
});
|
|
28448
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
|
+
|
|
28449
29404
|
// src/mcp/tools/task-rel-tools.ts
|
|
28450
29405
|
function registerTaskRelTools(server, ctx) {
|
|
28451
29406
|
const { shouldRegisterTool, resolveId, formatError, formatTask } = ctx;
|
|
@@ -28460,7 +29415,7 @@ function registerTaskRelTools(server, ctx) {
|
|
|
28460
29415
|
next_steps: exports_external2.array(exports_external2.string()).optional().describe("Recommended next actions")
|
|
28461
29416
|
}, async ({ agent_id, project_id, summary, completed, in_progress, blockers, next_steps }) => {
|
|
28462
29417
|
try {
|
|
28463
|
-
const { createHandoff: createHandoff2 } = (()
|
|
29418
|
+
const { createHandoff: createHandoff2 } = (init_handoffs(), __toCommonJS(exports_handoffs));
|
|
28464
29419
|
const handoff = createHandoff2({
|
|
28465
29420
|
agent_id,
|
|
28466
29421
|
project_id: project_id ? resolveId(project_id, "projects") : undefined,
|
|
@@ -28482,7 +29437,7 @@ function registerTaskRelTools(server, ctx) {
|
|
|
28482
29437
|
project_id: exports_external2.string().optional().describe("Filter by project")
|
|
28483
29438
|
}, async ({ agent_id, project_id }) => {
|
|
28484
29439
|
try {
|
|
28485
|
-
const { getLatestHandoff: getLatestHandoff2 } = (()
|
|
29440
|
+
const { getLatestHandoff: getLatestHandoff2 } = (init_handoffs(), __toCommonJS(exports_handoffs));
|
|
28486
29441
|
const handoff = getLatestHandoff2(agent_id, project_id ? resolveId(project_id, "projects") : undefined);
|
|
28487
29442
|
if (!handoff)
|
|
28488
29443
|
return { content: [{ type: "text", text: "No handoffs found." }] };
|
|
@@ -28513,7 +29468,7 @@ function registerTaskRelTools(server, ctx) {
|
|
|
28513
29468
|
created_by: exports_external2.string().optional().describe("Agent ID who created this relationship")
|
|
28514
29469
|
}, async ({ source_task_id, target_task_id, relationship_type, created_by }) => {
|
|
28515
29470
|
try {
|
|
28516
|
-
const { addTaskRelationship: addTaskRelationship2 } = (()
|
|
29471
|
+
const { addTaskRelationship: addTaskRelationship2 } = (init_task_relationships(), __toCommonJS(exports_task_relationships));
|
|
28517
29472
|
const rel = addTaskRelationship2({
|
|
28518
29473
|
source_task_id: resolveId(source_task_id),
|
|
28519
29474
|
target_task_id: resolveId(target_task_id),
|
|
@@ -28534,7 +29489,7 @@ function registerTaskRelTools(server, ctx) {
|
|
|
28534
29489
|
relationship_type: exports_external2.enum(["related_to", "conflicts_with", "similar_to", "duplicates", "supersedes", "modifies_same_file"]).optional()
|
|
28535
29490
|
}, async ({ id, source_task_id, target_task_id, relationship_type }) => {
|
|
28536
29491
|
try {
|
|
28537
|
-
const { removeTaskRelationship: removeTaskRelationship2, removeTaskRelationshipByPair: removeTaskRelationshipByPair2 } = (()
|
|
29492
|
+
const { removeTaskRelationship: removeTaskRelationship2, removeTaskRelationshipByPair: removeTaskRelationshipByPair2 } = (init_task_relationships(), __toCommonJS(exports_task_relationships));
|
|
28538
29493
|
let removed = false;
|
|
28539
29494
|
if (id) {
|
|
28540
29495
|
removed = removeTaskRelationship2(id);
|
|
@@ -28555,7 +29510,7 @@ function registerTaskRelTools(server, ctx) {
|
|
|
28555
29510
|
relationship_type: exports_external2.enum(["related_to", "conflicts_with", "similar_to", "duplicates", "supersedes", "modifies_same_file"]).optional()
|
|
28556
29511
|
}, async ({ task_id, relationship_type }) => {
|
|
28557
29512
|
try {
|
|
28558
|
-
const { getTaskRelationships: getTaskRelationships2 } = (()
|
|
29513
|
+
const { getTaskRelationships: getTaskRelationships2 } = (init_task_relationships(), __toCommonJS(exports_task_relationships));
|
|
28559
29514
|
const rels = getTaskRelationships2(resolveId(task_id), relationship_type);
|
|
28560
29515
|
if (rels.length === 0)
|
|
28561
29516
|
return { content: [{ type: "text", text: "No relationships found." }] };
|
|
@@ -28572,7 +29527,7 @@ function registerTaskRelTools(server, ctx) {
|
|
|
28572
29527
|
task_id: exports_external2.string().describe("Task ID to detect file relationships for")
|
|
28573
29528
|
}, async ({ task_id }) => {
|
|
28574
29529
|
try {
|
|
28575
|
-
const { autoDetectFileRelationships: autoDetectFileRelationships2 } = (()
|
|
29530
|
+
const { autoDetectFileRelationships: autoDetectFileRelationships2 } = (init_task_relationships(), __toCommonJS(exports_task_relationships));
|
|
28576
29531
|
const created = autoDetectFileRelationships2(resolveId(task_id));
|
|
28577
29532
|
return { content: [{ type: "text", text: created.length > 0 ? `Created ${created.length} file relationship(s).` : "No file overlaps detected." }] };
|
|
28578
29533
|
} catch (e) {
|
|
@@ -28583,8 +29538,8 @@ function registerTaskRelTools(server, ctx) {
|
|
|
28583
29538
|
if (shouldRegisterTool("sync_kg")) {
|
|
28584
29539
|
server.tool("sync_kg", "Sync all existing relationships into the knowledge graph edges table. Idempotent.", {}, async () => {
|
|
28585
29540
|
try {
|
|
28586
|
-
const { syncKgEdges } = (()
|
|
28587
|
-
const result =
|
|
29541
|
+
const { syncKgEdges: syncKgEdges2 } = (init_kg(), __toCommonJS(exports_kg));
|
|
29542
|
+
const result = syncKgEdges2();
|
|
28588
29543
|
return { content: [{ type: "text", text: `Knowledge graph synced: ${result.synced} edge(s) processed.` }] };
|
|
28589
29544
|
} catch (e) {
|
|
28590
29545
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -28600,8 +29555,8 @@ function registerTaskRelTools(server, ctx) {
|
|
|
28600
29555
|
limit: exports_external2.number().optional().describe("Max results")
|
|
28601
29556
|
}, async ({ entity_id, relation_type, entity_type, direction, limit }) => {
|
|
28602
29557
|
try {
|
|
28603
|
-
const { getRelated } = (()
|
|
28604
|
-
const edges =
|
|
29558
|
+
const { getRelated: getRelated2 } = (init_kg(), __toCommonJS(exports_kg));
|
|
29559
|
+
const edges = getRelated2(entity_id, { relation_type, entity_type, direction, limit });
|
|
28605
29560
|
if (edges.length === 0)
|
|
28606
29561
|
return { content: [{ type: "text", text: "No related entities found." }] };
|
|
28607
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})`);
|
|
@@ -28620,8 +29575,8 @@ function registerTaskRelTools(server, ctx) {
|
|
|
28620
29575
|
relation_types: exports_external2.array(exports_external2.string()).optional().describe("Filter by relation types")
|
|
28621
29576
|
}, async ({ source_id, target_id, max_depth, relation_types }) => {
|
|
28622
29577
|
try {
|
|
28623
|
-
const { findPath } = (()
|
|
28624
|
-
const paths =
|
|
29578
|
+
const { findPath: findPath2 } = (init_kg(), __toCommonJS(exports_kg));
|
|
29579
|
+
const paths = findPath2(source_id, target_id, { max_depth, relation_types });
|
|
28625
29580
|
if (paths.length === 0)
|
|
28626
29581
|
return { content: [{ type: "text", text: "No path found." }] };
|
|
28627
29582
|
const lines = paths.map((path, i) => {
|
|
@@ -28645,8 +29600,8 @@ function registerTaskRelTools(server, ctx) {
|
|
|
28645
29600
|
relation_types: exports_external2.array(exports_external2.string()).optional().describe("Filter by relation types")
|
|
28646
29601
|
}, async ({ entity_id, max_depth, relation_types }) => {
|
|
28647
29602
|
try {
|
|
28648
|
-
const { getImpactAnalysis } = (()
|
|
28649
|
-
const impact =
|
|
29603
|
+
const { getImpactAnalysis: getImpactAnalysis2 } = (init_kg(), __toCommonJS(exports_kg));
|
|
29604
|
+
const impact = getImpactAnalysis2(entity_id, { max_depth, relation_types });
|
|
28650
29605
|
if (impact.length === 0)
|
|
28651
29606
|
return { content: [{ type: "text", text: "No downstream impact detected." }] };
|
|
28652
29607
|
const byDepth = new Map;
|
|
@@ -28676,8 +29631,8 @@ Depth ${depth}:`);
|
|
|
28676
29631
|
limit: exports_external2.number().optional().describe("Max results (default: 20)")
|
|
28677
29632
|
}, async ({ project_id, limit }) => {
|
|
28678
29633
|
try {
|
|
28679
|
-
const { getCriticalPath } = (()
|
|
28680
|
-
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 });
|
|
28681
29636
|
if (result.length === 0)
|
|
28682
29637
|
return { content: [{ type: "text", text: "No critical path data. Run sync_kg first to populate the knowledge graph." }] };
|
|
28683
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}`);
|
|
@@ -28697,13 +29652,13 @@ ${lines.join(`
|
|
|
28697
29652
|
is_lead: exports_external2.coerce.boolean().optional().describe("Whether this agent is the project lead for this role")
|
|
28698
29653
|
}, async ({ project_id, agent_name, role, is_lead }) => {
|
|
28699
29654
|
try {
|
|
28700
|
-
const { setProjectAgentRole } = (()
|
|
28701
|
-
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));
|
|
28702
29657
|
const agent = getAgentByName2(agent_name);
|
|
28703
29658
|
if (!agent)
|
|
28704
29659
|
return { content: [{ type: "text", text: `Agent not found: ${agent_name}` }], isError: true };
|
|
28705
29660
|
const pid = resolveId(project_id, "projects");
|
|
28706
|
-
const result =
|
|
29661
|
+
const result = setProjectAgentRole2(pid, agent.id, role, is_lead ?? false);
|
|
28707
29662
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
28708
29663
|
} catch (e) {
|
|
28709
29664
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -28733,9 +29688,9 @@ ${lines.join(`
|
|
|
28733
29688
|
}).join(`
|
|
28734
29689
|
`);
|
|
28735
29690
|
};
|
|
28736
|
-
const { getProjectOrgChart } = (()
|
|
29691
|
+
const { getProjectOrgChart: getProjectOrgChart2 } = (init_project_agent_roles(), __toCommonJS(exports_project_agent_roles));
|
|
28737
29692
|
const pid = resolveId(project_id, "projects");
|
|
28738
|
-
const tree =
|
|
29693
|
+
const tree = getProjectOrgChart2(pid, { filter_to_project });
|
|
28739
29694
|
if (format === "json") {
|
|
28740
29695
|
return { content: [{ type: "text", text: JSON.stringify(tree, null, 2) }] };
|
|
28741
29696
|
}
|
|
@@ -28753,9 +29708,9 @@ ${lines.join(`
|
|
|
28753
29708
|
project_id: exports_external2.string().describe("Project ID")
|
|
28754
29709
|
}, async ({ project_id }) => {
|
|
28755
29710
|
try {
|
|
28756
|
-
const { listProjectAgentRoles } = (()
|
|
29711
|
+
const { listProjectAgentRoles: listProjectAgentRoles2 } = (init_project_agent_roles(), __toCommonJS(exports_project_agent_roles));
|
|
28757
29712
|
const pid = resolveId(project_id, "projects");
|
|
28758
|
-
const roles =
|
|
29713
|
+
const roles = listProjectAgentRoles2(pid);
|
|
28759
29714
|
return { content: [{ type: "text", text: JSON.stringify(roles, null, 2) }] };
|
|
28760
29715
|
} catch (e) {
|
|
28761
29716
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -28769,7 +29724,7 @@ ${lines.join(`
|
|
|
28769
29724
|
limit: exports_external2.number().optional().describe("Max results")
|
|
28770
29725
|
}, async ({ capabilities, min_score, limit }) => {
|
|
28771
29726
|
try {
|
|
28772
|
-
const { getCapableAgents: getCapableAgents2 } = (()
|
|
29727
|
+
const { getCapableAgents: getCapableAgents2 } = (init_agents(), __toCommonJS(exports_agents));
|
|
28773
29728
|
const results = getCapableAgents2(capabilities, { min_score, limit });
|
|
28774
29729
|
if (results.length === 0)
|
|
28775
29730
|
return { content: [{ type: "text", text: "No agents match the given capabilities." }] };
|
|
@@ -28788,8 +29743,8 @@ ${lines.join(`
|
|
|
28788
29743
|
project_id: exports_external2.string().optional().describe("Filter by project")
|
|
28789
29744
|
}, async ({ stuck_minutes, confidence_threshold, project_id }) => {
|
|
28790
29745
|
try {
|
|
28791
|
-
const { patrolTasks } = (()
|
|
28792
|
-
const result =
|
|
29746
|
+
const { patrolTasks: patrolTasks2 } = (init_patrol(), __toCommonJS(exports_patrol));
|
|
29747
|
+
const result = patrolTasks2({
|
|
28793
29748
|
stuck_minutes,
|
|
28794
29749
|
confidence_threshold,
|
|
28795
29750
|
project_id: project_id ? resolveId(project_id, "projects") : undefined
|
|
@@ -28815,8 +29770,8 @@ ${lines.join(`
|
|
|
28815
29770
|
limit: exports_external2.number().optional().describe("Max results (default: all)")
|
|
28816
29771
|
}, async ({ project_id, limit }) => {
|
|
28817
29772
|
try {
|
|
28818
|
-
const { getReviewQueue } = (()
|
|
28819
|
-
const tasks =
|
|
29773
|
+
const { getReviewQueue: getReviewQueue2 } = (init_patrol(), __toCommonJS(exports_patrol));
|
|
29774
|
+
const tasks = getReviewQueue2({
|
|
28820
29775
|
project_id: project_id ? resolveId(project_id, "projects") : undefined,
|
|
28821
29776
|
limit
|
|
28822
29777
|
});
|
|
@@ -28842,8 +29797,8 @@ ${lines.join(`
|
|
|
28842
29797
|
reviewer_id: exports_external2.string().optional().describe("Agent ID of reviewer")
|
|
28843
29798
|
}, async ({ task_id, score, reviewer_id }) => {
|
|
28844
29799
|
try {
|
|
28845
|
-
const { scoreTask } = (()
|
|
28846
|
-
|
|
29800
|
+
const { scoreTask: scoreTask2 } = (init_agent_metrics(), __toCommonJS(exports_agent_metrics));
|
|
29801
|
+
scoreTask2(resolveId(task_id), score, reviewer_id);
|
|
28847
29802
|
return { content: [{ type: "text", text: `Task ${task_id.slice(0, 8)} scored: ${score}` }] };
|
|
28848
29803
|
} catch (e) {
|
|
28849
29804
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -28860,7 +29815,7 @@ ${lines.join(`
|
|
|
28860
29815
|
notes: exports_external2.string().optional().describe("Notes about what was done")
|
|
28861
29816
|
}, async ({ task_id, minutes, agent_id, started_at, ended_at, notes }) => {
|
|
28862
29817
|
try {
|
|
28863
|
-
const { logTime: logTime2 } = (()
|
|
29818
|
+
const { logTime: logTime2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
28864
29819
|
logTime2({ task_id: resolveId(task_id), minutes, agent_id, started_at, ended_at, notes });
|
|
28865
29820
|
return { content: [{ type: "text", text: `Logged ${minutes} min on task ${task_id.slice(0, 8)}` }] };
|
|
28866
29821
|
} catch (e) {
|
|
@@ -28875,7 +29830,7 @@ ${lines.join(`
|
|
|
28875
29830
|
since: exports_external2.string().optional().describe("ISO date \u2014 only tasks completed after this date")
|
|
28876
29831
|
}, async ({ project_id, agent_id, since }) => {
|
|
28877
29832
|
try {
|
|
28878
|
-
const { getTimeReport: getTimeReport2 } = (()
|
|
29833
|
+
const { getTimeReport: getTimeReport2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
28879
29834
|
const report = getTimeReport2({ project_id: project_id ? resolveId(project_id, "projects") : undefined, agent_id, since });
|
|
28880
29835
|
if (report.length === 0)
|
|
28881
29836
|
return { content: [{ type: "text", text: "No completed tasks found." }] };
|
|
@@ -28899,7 +29854,7 @@ ${lines.join(`
|
|
|
28899
29854
|
agent_id: exports_external2.string().optional().describe("Agent subscribing (defaults to context agent)")
|
|
28900
29855
|
}, async ({ task_id, agent_id }) => {
|
|
28901
29856
|
try {
|
|
28902
|
-
const { watchTask: watchTask2 } = (()
|
|
29857
|
+
const { watchTask: watchTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
28903
29858
|
watchTask2(resolveId(task_id), agent_id || "");
|
|
28904
29859
|
return { content: [{ type: "text", text: `Now watching task ${task_id.slice(0, 8)}` }] };
|
|
28905
29860
|
} catch (e) {
|
|
@@ -28913,7 +29868,7 @@ ${lines.join(`
|
|
|
28913
29868
|
agent_id: exports_external2.string().optional().describe("Agent unsubscribing (defaults to context agent)")
|
|
28914
29869
|
}, async ({ task_id, agent_id }) => {
|
|
28915
29870
|
try {
|
|
28916
|
-
const { unwatchTask: unwatchTask2 } = (()
|
|
29871
|
+
const { unwatchTask: unwatchTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
28917
29872
|
unwatchTask2(resolveId(task_id), agent_id || "");
|
|
28918
29873
|
return { content: [{ type: "text", text: `Stopped watching task ${task_id.slice(0, 8)}` }] };
|
|
28919
29874
|
} catch (e) {
|
|
@@ -28926,7 +29881,7 @@ ${lines.join(`
|
|
|
28926
29881
|
task_id: exports_external2.string().describe("Task ID")
|
|
28927
29882
|
}, async ({ task_id }) => {
|
|
28928
29883
|
try {
|
|
28929
|
-
const { getTaskWatchers: getTaskWatchers2 } = (()
|
|
29884
|
+
const { getTaskWatchers: getTaskWatchers2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
28930
29885
|
const watchers = getTaskWatchers2(resolveId(task_id));
|
|
28931
29886
|
if (watchers.length === 0)
|
|
28932
29887
|
return { content: [{ type: "text", text: "No watchers." }] };
|
|
@@ -28945,15 +29900,15 @@ ${lines.join(`
|
|
|
28945
29900
|
agent_id: exports_external2.string().optional().describe("Filter by assignee")
|
|
28946
29901
|
}, async ({ project_id, plan_id, task_list_id, since, agent_id }) => {
|
|
28947
29902
|
try {
|
|
28948
|
-
const { listTasks: listTasks3 } = (()
|
|
28949
|
-
const { patrolTasks } = (()
|
|
29903
|
+
const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
29904
|
+
const { patrolTasks: patrolTasks2 } = (init_patrol(), __toCommonJS(exports_patrol));
|
|
28950
29905
|
const completed = listTasks3({ status: "completed", project_id, plan_id, task_list_id, assigned_to: agent_id, limit: 500 }, undefined);
|
|
28951
29906
|
const filtered = since ? completed.filter((t) => t.completed_at && t.completed_at >= since) : completed;
|
|
28952
29907
|
const total = filtered.length;
|
|
28953
29908
|
const lowConf = filtered.filter((t) => t.confidence != null && t.confidence < 0.7).length;
|
|
28954
29909
|
const withEstimate = filtered.filter((t) => t.estimated_minutes != null && t.actual_minutes != null);
|
|
28955
29910
|
const avgDiff = withEstimate.length > 0 ? withEstimate.reduce((acc, t) => acc + (t.actual_minutes - t.estimated_minutes), 0) / withEstimate.length : 0;
|
|
28956
|
-
const patrolResult =
|
|
29911
|
+
const patrolResult = patrolTasks2({ project_id: project_id ? resolveId(project_id, "projects") : undefined });
|
|
28957
29912
|
const stuck = patrolResult.issues.filter((i) => i.type === "stuck").length;
|
|
28958
29913
|
const lines = [
|
|
28959
29914
|
`Retro (${total} completed tasks${since ? ` since ${since}` : ""})`,
|
|
@@ -28974,7 +29929,7 @@ ${lines.join(`
|
|
|
28974
29929
|
limit: exports_external2.number().optional().describe("Max results (default: 20)")
|
|
28975
29930
|
}, async ({ project_id, limit }) => {
|
|
28976
29931
|
try {
|
|
28977
|
-
const { listTasks: listTasks3 } = (()
|
|
29932
|
+
const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
28978
29933
|
const tasks = listTasks3({ status: "pending", project_id, assigned_to: "", limit: limit || 20 }, undefined);
|
|
28979
29934
|
if (tasks.length === 0)
|
|
28980
29935
|
return { content: [{ type: "text", text: "Inbox is empty." }] };
|
|
@@ -28993,8 +29948,8 @@ ${lines.join(`
|
|
|
28993
29948
|
project_id: exports_external2.string().optional().describe("Filter by project")
|
|
28994
29949
|
}, async ({ agent_id, project_id }) => {
|
|
28995
29950
|
try {
|
|
28996
|
-
const { getAgentMetrics } = (()
|
|
28997
|
-
const metrics =
|
|
29951
|
+
const { getAgentMetrics: getAgentMetrics2 } = (init_agent_metrics(), __toCommonJS(exports_agent_metrics));
|
|
29952
|
+
const metrics = getAgentMetrics2(agent_id, {
|
|
28998
29953
|
project_id: project_id ? resolveId(project_id, "projects") : undefined
|
|
28999
29954
|
});
|
|
29000
29955
|
if (!metrics)
|
|
@@ -29021,8 +29976,8 @@ ${lines.join(`
|
|
|
29021
29976
|
limit: exports_external2.number().optional().describe("Max entries (default: 20)")
|
|
29022
29977
|
}, async ({ project_id, limit }) => {
|
|
29023
29978
|
try {
|
|
29024
|
-
const { getLeaderboard } = (()
|
|
29025
|
-
const entries =
|
|
29979
|
+
const { getLeaderboard: getLeaderboard2 } = (init_agent_metrics(), __toCommonJS(exports_agent_metrics));
|
|
29980
|
+
const entries = getLeaderboard2({
|
|
29026
29981
|
project_id: project_id ? resolveId(project_id, "projects") : undefined,
|
|
29027
29982
|
limit
|
|
29028
29983
|
});
|
|
@@ -29982,6 +30937,361 @@ var init_machines2 = __esm(() => {
|
|
|
29982
30937
|
init_database();
|
|
29983
30938
|
});
|
|
29984
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
|
+
|
|
29985
31295
|
// src/mcp/index.ts
|
|
29986
31296
|
var exports_mcp = {};
|
|
29987
31297
|
__export(exports_mcp, {
|
|
@@ -29989,17 +31299,23 @@ __export(exports_mcp, {
|
|
|
29989
31299
|
});
|
|
29990
31300
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
29991
31301
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
29992
|
-
import { readFileSync as readFileSync7 } from "fs";
|
|
31302
|
+
import { existsSync as existsSync9, readFileSync as readFileSync7 } from "fs";
|
|
29993
31303
|
import { join as join12, dirname as dirname6 } from "path";
|
|
29994
31304
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
29995
31305
|
function getMcpVersion() {
|
|
29996
31306
|
try {
|
|
29997
|
-
|
|
29998
|
-
|
|
29999
|
-
|
|
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
|
+
}
|
|
30000
31315
|
} catch {
|
|
30001
31316
|
return "0.0.0";
|
|
30002
31317
|
}
|
|
31318
|
+
return "0.0.0";
|
|
30003
31319
|
}
|
|
30004
31320
|
function shouldRegisterTool(name) {
|
|
30005
31321
|
if (TODOS_PROFILE === "minimal")
|
|
@@ -30148,7 +31464,6 @@ var init_mcp = __esm(() => {
|
|
|
30148
31464
|
init_cloud();
|
|
30149
31465
|
init_agents();
|
|
30150
31466
|
init_database();
|
|
30151
|
-
init_logger();
|
|
30152
31467
|
init_types();
|
|
30153
31468
|
init_dispatch2();
|
|
30154
31469
|
init_task_crud2();
|
|
@@ -30161,6 +31476,7 @@ var init_mcp = __esm(() => {
|
|
|
30161
31476
|
init_task_rel_tools();
|
|
30162
31477
|
init_code_tools();
|
|
30163
31478
|
init_machines2();
|
|
31479
|
+
init_agents2();
|
|
30164
31480
|
server = new McpServer({
|
|
30165
31481
|
name: "todos",
|
|
30166
31482
|
version: getMcpVersion()
|
|
@@ -30178,6 +31494,7 @@ var init_mcp = __esm(() => {
|
|
|
30178
31494
|
"get_next_task",
|
|
30179
31495
|
"bootstrap",
|
|
30180
31496
|
"get_tasks_changed_since",
|
|
31497
|
+
"get_health",
|
|
30181
31498
|
"heartbeat",
|
|
30182
31499
|
"release_agent"
|
|
30183
31500
|
]);
|
|
@@ -30218,6 +31535,7 @@ var init_mcp = __esm(() => {
|
|
|
30218
31535
|
registerTaskResources(server, toolContext);
|
|
30219
31536
|
registerTaskRelTools(server, toolContext);
|
|
30220
31537
|
registerCodeTools(server, toolContext);
|
|
31538
|
+
registerAgentTools(server, { ...toolContext, agentFocusMap });
|
|
30221
31539
|
registerMachineTools(server, { shouldRegisterTool, formatError });
|
|
30222
31540
|
registerDispatchTools(server, { shouldRegisterTool, resolveId, formatError });
|
|
30223
31541
|
registerCloudSyncTools(server, { shouldRegisterTool, formatError });
|
|
@@ -30768,7 +32086,7 @@ ${chalk2.cyan(sid)} ${statusColor(task.status)} ${prioColor(task.priority)} ${ta
|
|
|
30768
32086
|
opts.tags = opts.tags || opts.tag;
|
|
30769
32087
|
opts.list = opts.list || opts.taskList;
|
|
30770
32088
|
const resolvedId = resolveTaskId(id);
|
|
30771
|
-
const current =
|
|
32089
|
+
const current = getTask(resolvedId);
|
|
30772
32090
|
if (!current) {
|
|
30773
32091
|
console.error(chalk2.red(`Task not found: ${id}`));
|
|
30774
32092
|
process.exit(1);
|
|
@@ -30829,7 +32147,7 @@ ${chalk2.cyan(sid)} ${statusColor(task.status)} ${prioColor(task.priority)} ${ta
|
|
|
30829
32147
|
program2.command("approve <id>").description("Approve a task that requires approval").action((id) => {
|
|
30830
32148
|
const globalOpts = program2.opts();
|
|
30831
32149
|
const resolvedId = resolveTaskId(id);
|
|
30832
|
-
const task =
|
|
32150
|
+
const task = getTask(resolvedId);
|
|
30833
32151
|
if (!task) {
|
|
30834
32152
|
console.error(chalk2.red(`Task not found: ${id}`));
|
|
30835
32153
|
process.exit(1);
|
|
@@ -31431,7 +32749,7 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
|
|
|
31431
32749
|
updated.activeForm = existing.task.activeForm;
|
|
31432
32750
|
writeClaudeTask(dir, updated);
|
|
31433
32751
|
if (recordConflict) {
|
|
31434
|
-
const latest =
|
|
32752
|
+
const latest = getTask(task.id);
|
|
31435
32753
|
if (latest) {
|
|
31436
32754
|
const conflict = {
|
|
31437
32755
|
agent: "claude",
|
|
@@ -31454,7 +32772,7 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
|
|
|
31454
32772
|
ct.subject = formatPrefixedSubject(task.title, prefixConfig.prefix, prefixCounter);
|
|
31455
32773
|
}
|
|
31456
32774
|
writeClaudeTask(dir, ct);
|
|
31457
|
-
const current =
|
|
32775
|
+
const current = getTask(task.id);
|
|
31458
32776
|
if (current) {
|
|
31459
32777
|
const newMeta = { ...current.metadata, claude_task_id: claudeId };
|
|
31460
32778
|
updateTask(task.id, { version: current.version, metadata: newMeta });
|
|
@@ -31656,7 +32974,7 @@ function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
|
|
|
31656
32974
|
const updated = taskToAgentTask(task, existing.task.id, existing.task.metadata);
|
|
31657
32975
|
writeAgentTask(dir, updated);
|
|
31658
32976
|
if (recordConflict) {
|
|
31659
|
-
const latest =
|
|
32977
|
+
const latest = getTask(task.id);
|
|
31660
32978
|
if (latest) {
|
|
31661
32979
|
const conflict = {
|
|
31662
32980
|
agent,
|
|
@@ -31675,7 +32993,7 @@ function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
|
|
|
31675
32993
|
hwm++;
|
|
31676
32994
|
const at = taskToAgentTask(task, externalId);
|
|
31677
32995
|
writeAgentTask(dir, at);
|
|
31678
|
-
const current =
|
|
32996
|
+
const current = getTask(task.id);
|
|
31679
32997
|
if (current) {
|
|
31680
32998
|
const newMeta = { ...current.metadata, [metaKey]: externalId };
|
|
31681
32999
|
updateTask(task.id, { version: current.version, metadata: newMeta });
|
|
@@ -32758,7 +34076,7 @@ ${tasks.length} task(s) shown`));
|
|
|
32758
34076
|
program2.command("blame <file>").description("Show which tasks/agents touched a file and why \u2014 combines task_files + task_commits").action(async (filePath) => {
|
|
32759
34077
|
const globalOpts = program2.opts();
|
|
32760
34078
|
const { findTasksByFile: findTasksByFile2 } = await Promise.resolve().then(() => (init_task_files(), exports_task_files));
|
|
32761
|
-
const { getTask:
|
|
34079
|
+
const { getTask: getTask2 } = await Promise.resolve().then(() => (init_tasks(), exports_tasks));
|
|
32762
34080
|
const db = getDatabase();
|
|
32763
34081
|
const taskFiles = findTasksByFile2(filePath, db);
|
|
32764
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}%`);
|
|
@@ -32772,7 +34090,7 @@ Blame: ${filePath}
|
|
|
32772
34090
|
if (taskFiles.length > 0) {
|
|
32773
34091
|
console.log(chalk6.bold("Task File Links:"));
|
|
32774
34092
|
for (const tf of taskFiles) {
|
|
32775
|
-
const task =
|
|
34093
|
+
const task = getTask2(tf.task_id, db);
|
|
32776
34094
|
const title = task ? task.title : "unknown";
|
|
32777
34095
|
const sid = task?.short_id || tf.task_id.slice(0, 8);
|
|
32778
34096
|
console.log(` ${chalk6.cyan(sid)} ${title} \u2014 ${chalk6.dim(tf.role || "file")} ${chalk6.dim(tf.updated_at)}`);
|
|
@@ -32806,74 +34124,10 @@ Commit Links (${commitRows.length}):`));
|
|
|
32806
34124
|
init_database();
|
|
32807
34125
|
init_tasks();
|
|
32808
34126
|
init_audit();
|
|
32809
|
-
|
|
32810
|
-
|
|
32811
|
-
// src/db/handoffs.ts
|
|
32812
|
-
init_database();
|
|
32813
|
-
function createHandoff(input, db) {
|
|
32814
|
-
const d = db || getDatabase();
|
|
32815
|
-
const id = uuid();
|
|
32816
|
-
const timestamp = now();
|
|
32817
|
-
d.run(`INSERT INTO handoffs (id, agent_id, project_id, summary, completed, in_progress, blockers, next_steps, created_at)
|
|
32818
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
32819
|
-
id,
|
|
32820
|
-
input.agent_id || null,
|
|
32821
|
-
input.project_id || null,
|
|
32822
|
-
input.summary,
|
|
32823
|
-
input.completed ? JSON.stringify(input.completed) : null,
|
|
32824
|
-
input.in_progress ? JSON.stringify(input.in_progress) : null,
|
|
32825
|
-
input.blockers ? JSON.stringify(input.blockers) : null,
|
|
32826
|
-
input.next_steps ? JSON.stringify(input.next_steps) : null,
|
|
32827
|
-
timestamp
|
|
32828
|
-
]);
|
|
32829
|
-
return {
|
|
32830
|
-
id,
|
|
32831
|
-
agent_id: input.agent_id || null,
|
|
32832
|
-
project_id: input.project_id || null,
|
|
32833
|
-
summary: input.summary,
|
|
32834
|
-
completed: input.completed || null,
|
|
32835
|
-
in_progress: input.in_progress || null,
|
|
32836
|
-
blockers: input.blockers || null,
|
|
32837
|
-
next_steps: input.next_steps || null,
|
|
32838
|
-
created_at: timestamp
|
|
32839
|
-
};
|
|
32840
|
-
}
|
|
32841
|
-
function rowToHandoff(row) {
|
|
32842
|
-
return {
|
|
32843
|
-
...row,
|
|
32844
|
-
completed: row.completed ? JSON.parse(row.completed) : null,
|
|
32845
|
-
in_progress: row.in_progress ? JSON.parse(row.in_progress) : null,
|
|
32846
|
-
blockers: row.blockers ? JSON.parse(row.blockers) : null,
|
|
32847
|
-
next_steps: row.next_steps ? JSON.parse(row.next_steps) : null
|
|
32848
|
-
};
|
|
32849
|
-
}
|
|
32850
|
-
function listHandoffs(projectId, limit = 10, db) {
|
|
32851
|
-
const d = db || getDatabase();
|
|
32852
|
-
if (projectId) {
|
|
32853
|
-
return d.query("SELECT * FROM handoffs WHERE project_id = ? ORDER BY rowid DESC LIMIT ?").all(projectId, limit).map(rowToHandoff);
|
|
32854
|
-
}
|
|
32855
|
-
return d.query("SELECT * FROM handoffs ORDER BY rowid DESC LIMIT ?").all(limit).map(rowToHandoff);
|
|
32856
|
-
}
|
|
32857
|
-
function getLatestHandoff(agentId, projectId, db) {
|
|
32858
|
-
const d = db || getDatabase();
|
|
32859
|
-
let query = "SELECT * FROM handoffs WHERE 1=1";
|
|
32860
|
-
const params = [];
|
|
32861
|
-
if (agentId) {
|
|
32862
|
-
query += " AND agent_id = ?";
|
|
32863
|
-
params.push(agentId);
|
|
32864
|
-
}
|
|
32865
|
-
if (projectId) {
|
|
32866
|
-
query += " AND project_id = ?";
|
|
32867
|
-
params.push(projectId);
|
|
32868
|
-
}
|
|
32869
|
-
query += " ORDER BY rowid DESC LIMIT 1";
|
|
32870
|
-
const row = d.query(query).get(...params);
|
|
32871
|
-
return row ? rowToHandoff(row) : null;
|
|
32872
|
-
}
|
|
32873
|
-
|
|
32874
|
-
// src/cli/commands/query-commands.ts
|
|
34127
|
+
init_handoffs();
|
|
32875
34128
|
init_recurrence();
|
|
32876
34129
|
init_helpers();
|
|
34130
|
+
import chalk7 from "chalk";
|
|
32877
34131
|
function registerQueryCommands(program2) {
|
|
32878
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) => {
|
|
32879
34133
|
const globalOpts = program2.opts();
|
|
@@ -33162,7 +34416,7 @@ No task claimed (nothing available).`));
|
|
|
33162
34416
|
const globalOpts = program2.opts();
|
|
33163
34417
|
const resolvedId = resolveTaskId(id);
|
|
33164
34418
|
const db = getDatabase();
|
|
33165
|
-
const task =
|
|
34419
|
+
const task = getTask(resolvedId, db);
|
|
33166
34420
|
if (!task) {
|
|
33167
34421
|
console.error(chalk7.red(`Task not found: ${id}`));
|
|
33168
34422
|
process.exit(1);
|
|
@@ -33182,7 +34436,7 @@ No task claimed (nothing available).`));
|
|
|
33182
34436
|
const globalOpts = program2.opts();
|
|
33183
34437
|
const resolvedId = resolveTaskId(id);
|
|
33184
34438
|
const db = getDatabase();
|
|
33185
|
-
const task =
|
|
34439
|
+
const task = getTask(resolvedId, db);
|
|
33186
34440
|
if (!task) {
|
|
33187
34441
|
console.error(chalk7.red(`Task not found: ${id}`));
|
|
33188
34442
|
process.exit(1);
|
|
@@ -33202,7 +34456,7 @@ No task claimed (nothing available).`));
|
|
|
33202
34456
|
const globalOpts = program2.opts();
|
|
33203
34457
|
const resolvedId = resolveTaskId(id);
|
|
33204
34458
|
const db = getDatabase();
|
|
33205
|
-
const task =
|
|
34459
|
+
const task = getTask(resolvedId, db);
|
|
33206
34460
|
if (!task) {
|
|
33207
34461
|
console.error(chalk7.red(`Task not found: ${id}`));
|
|
33208
34462
|
process.exit(1);
|
|
@@ -33223,7 +34477,7 @@ No task claimed (nothing available).`));
|
|
|
33223
34477
|
const globalOpts = program2.opts();
|
|
33224
34478
|
const resolvedId = resolveTaskId(id);
|
|
33225
34479
|
const db = getDatabase();
|
|
33226
|
-
const task =
|
|
34480
|
+
const task = getTask(resolvedId, db);
|
|
33227
34481
|
if (!task) {
|
|
33228
34482
|
console.error(chalk7.red(`Task not found: ${id}`));
|
|
33229
34483
|
process.exit(1);
|
|
@@ -34386,7 +35640,7 @@ init_tasks();
|
|
|
34386
35640
|
init_helpers();
|
|
34387
35641
|
import chalk9 from "chalk";
|
|
34388
35642
|
import { execSync as execSync3 } from "child_process";
|
|
34389
|
-
import { existsSync as
|
|
35643
|
+
import { existsSync as existsSync10, readFileSync as readFileSync8, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, chmodSync } from "fs";
|
|
34390
35644
|
import { dirname as dirname7, join as join13 } from "path";
|
|
34391
35645
|
var HOME2 = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
34392
35646
|
function getMcpBinaryPath() {
|
|
@@ -34396,12 +35650,12 @@ function getMcpBinaryPath() {
|
|
|
34396
35650
|
return p;
|
|
34397
35651
|
} catch {}
|
|
34398
35652
|
const bunBin = join13(HOME2, ".bun", "bin", "todos-mcp");
|
|
34399
|
-
if (
|
|
35653
|
+
if (existsSync10(bunBin))
|
|
34400
35654
|
return bunBin;
|
|
34401
35655
|
return "todos-mcp";
|
|
34402
35656
|
}
|
|
34403
35657
|
function readJsonFile2(path) {
|
|
34404
|
-
if (!
|
|
35658
|
+
if (!existsSync10(path))
|
|
34405
35659
|
return {};
|
|
34406
35660
|
try {
|
|
34407
35661
|
return JSON.parse(readFileSync8(path, "utf-8"));
|
|
@@ -34411,19 +35665,19 @@ function readJsonFile2(path) {
|
|
|
34411
35665
|
}
|
|
34412
35666
|
function writeJsonFile2(path, data) {
|
|
34413
35667
|
const dir = dirname7(path);
|
|
34414
|
-
if (!
|
|
35668
|
+
if (!existsSync10(dir))
|
|
34415
35669
|
mkdirSync5(dir, { recursive: true });
|
|
34416
35670
|
writeFileSync5(path, JSON.stringify(data, null, 2) + `
|
|
34417
35671
|
`);
|
|
34418
35672
|
}
|
|
34419
35673
|
function readTomlFile(path) {
|
|
34420
|
-
if (!
|
|
35674
|
+
if (!existsSync10(path))
|
|
34421
35675
|
return "";
|
|
34422
35676
|
return readFileSync8(path, "utf-8");
|
|
34423
35677
|
}
|
|
34424
35678
|
function writeTomlFile(path, content) {
|
|
34425
35679
|
const dir = dirname7(path);
|
|
34426
|
-
if (!
|
|
35680
|
+
if (!existsSync10(dir))
|
|
34427
35681
|
mkdirSync5(dir, { recursive: true });
|
|
34428
35682
|
writeFileSync5(path, content);
|
|
34429
35683
|
}
|
|
@@ -34567,7 +35821,7 @@ function registerMcpHooksCommands(program2) {
|
|
|
34567
35821
|
todosBin = p;
|
|
34568
35822
|
} catch {}
|
|
34569
35823
|
const hooksDir = join13(process.cwd(), ".claude", "hooks");
|
|
34570
|
-
if (!
|
|
35824
|
+
if (!existsSync10(hooksDir))
|
|
34571
35825
|
mkdirSync5(hooksDir, { recursive: true });
|
|
34572
35826
|
const hookScript = `#!/usr/bin/env bash
|
|
34573
35827
|
# Auto-generated by: todos hooks install
|
|
@@ -34689,7 +35943,7 @@ exit 0
|
|
|
34689
35943
|
const gitDir = execSync3("git rev-parse --git-dir", { encoding: "utf-8" }).trim();
|
|
34690
35944
|
const hookPath = `${gitDir}/hooks/post-commit`;
|
|
34691
35945
|
const marker = "# todos-auto-link";
|
|
34692
|
-
if (
|
|
35946
|
+
if (existsSync10(hookPath)) {
|
|
34693
35947
|
const existing = readFileSync8(hookPath, "utf-8");
|
|
34694
35948
|
if (existing.includes(marker)) {
|
|
34695
35949
|
console.log(chalk9.yellow("Hook already installed."));
|
|
@@ -34717,7 +35971,7 @@ $(dirname "$0")/../../scripts/post-commit-hook.sh
|
|
|
34717
35971
|
const gitDir = execSync3("git rev-parse --git-dir", { encoding: "utf-8" }).trim();
|
|
34718
35972
|
const hookPath = `${gitDir}/hooks/post-commit`;
|
|
34719
35973
|
const marker = "# todos-auto-link";
|
|
34720
|
-
if (!
|
|
35974
|
+
if (!existsSync10(hookPath)) {
|
|
34721
35975
|
console.log(chalk9.dim("No post-commit hook found."));
|
|
34722
35976
|
return;
|
|
34723
35977
|
}
|