@hasna/todos 0.11.33 → 0.11.35
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/README.md +12 -0
- package/dist/cli/commands/agent-commands.d.ts.map +1 -1
- package/dist/cli/commands/api-key-commands.d.ts +3 -0
- package/dist/cli/commands/api-key-commands.d.ts.map +1 -0
- package/dist/cli/commands/config-serve-commands.d.ts.map +1 -1
- package/dist/cli/index.js +2343 -607
- package/dist/db/agent-names.d.ts +23 -0
- package/dist/db/agent-names.d.ts.map +1 -0
- package/dist/db/agents.d.ts +2 -0
- package/dist/db/agents.d.ts.map +1 -1
- package/dist/db/api-keys.d.ts +28 -0
- package/dist/db/api-keys.d.ts.map +1 -0
- 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/schema.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.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +400 -13
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +2585 -536
- 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 +352 -42
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/serve.d.ts.map +1 -1
- package/dist/types/index.d.ts +21 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/mcp/index.js
CHANGED
|
@@ -810,6 +810,42 @@ var init_migrations = __esm(() => {
|
|
|
810
810
|
ALTER TABLE tasks ADD COLUMN current_step TEXT;
|
|
811
811
|
ALTER TABLE tasks ADD COLUMN total_steps INTEGER;
|
|
812
812
|
INSERT OR IGNORE INTO _migrations (id) VALUES (48);
|
|
813
|
+
`,
|
|
814
|
+
`
|
|
815
|
+
CREATE TABLE IF NOT EXISTS cycles (
|
|
816
|
+
id TEXT PRIMARY KEY,
|
|
817
|
+
project_id TEXT REFERENCES projects(id) ON DELETE CASCADE,
|
|
818
|
+
number INTEGER NOT NULL,
|
|
819
|
+
start_date TEXT NOT NULL,
|
|
820
|
+
end_date TEXT NOT NULL,
|
|
821
|
+
duration_weeks INTEGER NOT NULL DEFAULT 1,
|
|
822
|
+
status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active', 'completed', 'archived')),
|
|
823
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
824
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
825
|
+
);
|
|
826
|
+
CREATE INDEX IF NOT EXISTS idx_cycles_project ON cycles(project_id);
|
|
827
|
+
CREATE INDEX IF NOT EXISTS idx_cycles_number ON cycles(number);
|
|
828
|
+
CREATE INDEX IF NOT EXISTS idx_cycles_status ON cycles(status);
|
|
829
|
+
CREATE INDEX IF NOT EXISTS idx_cycles_dates ON cycles(start_date, end_date);
|
|
830
|
+
ALTER TABLE tasks ADD COLUMN cycle_id TEXT REFERENCES cycles(id) ON DELETE SET NULL;
|
|
831
|
+
CREATE INDEX IF NOT EXISTS idx_tasks_cycle ON tasks(cycle_id) WHERE cycle_id IS NOT NULL;
|
|
832
|
+
INSERT OR IGNORE INTO _migrations (id) VALUES (49);
|
|
833
|
+
`,
|
|
834
|
+
`
|
|
835
|
+
CREATE TABLE IF NOT EXISTS api_keys (
|
|
836
|
+
id TEXT PRIMARY KEY,
|
|
837
|
+
name TEXT NOT NULL,
|
|
838
|
+
key_hash TEXT NOT NULL UNIQUE,
|
|
839
|
+
prefix TEXT NOT NULL UNIQUE,
|
|
840
|
+
permissions TEXT NOT NULL DEFAULT '["*"]',
|
|
841
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
842
|
+
last_used_at TEXT,
|
|
843
|
+
expires_at TEXT,
|
|
844
|
+
revoked_at TEXT
|
|
845
|
+
);
|
|
846
|
+
CREATE INDEX IF NOT EXISTS idx_api_keys_prefix ON api_keys(prefix);
|
|
847
|
+
CREATE INDEX IF NOT EXISTS idx_api_keys_active ON api_keys(revoked_at, expires_at);
|
|
848
|
+
INSERT OR IGNORE INTO _migrations (id) VALUES (50);
|
|
813
849
|
`
|
|
814
850
|
];
|
|
815
851
|
});
|
|
@@ -1208,6 +1244,20 @@ function ensureSchema(db) {
|
|
|
1208
1244
|
ensureIndex("CREATE INDEX IF NOT EXISTS idx_cycles_dates ON cycles(start_date, end_date)");
|
|
1209
1245
|
ensureColumn("tasks", "cycle_id", "TEXT REFERENCES cycles(id) ON DELETE SET NULL");
|
|
1210
1246
|
ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_cycle ON tasks(cycle_id) WHERE cycle_id IS NOT NULL");
|
|
1247
|
+
ensureTable("api_keys", `
|
|
1248
|
+
CREATE TABLE api_keys (
|
|
1249
|
+
id TEXT PRIMARY KEY,
|
|
1250
|
+
name TEXT NOT NULL,
|
|
1251
|
+
key_hash TEXT NOT NULL UNIQUE,
|
|
1252
|
+
prefix TEXT NOT NULL UNIQUE,
|
|
1253
|
+
permissions TEXT NOT NULL DEFAULT '["*"]',
|
|
1254
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
1255
|
+
last_used_at TEXT,
|
|
1256
|
+
expires_at TEXT,
|
|
1257
|
+
revoked_at TEXT
|
|
1258
|
+
)`);
|
|
1259
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_api_keys_prefix ON api_keys(prefix)");
|
|
1260
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_api_keys_active ON api_keys(revoked_at, expires_at)");
|
|
1211
1261
|
}
|
|
1212
1262
|
function backfillTaskTags(db) {
|
|
1213
1263
|
try {
|
|
@@ -12635,14 +12685,224 @@ See https://www.postgresql.org/docs/current/libpq-ssl.html for libpq SSL mode de
|
|
|
12635
12685
|
init_adapter();
|
|
12636
12686
|
});
|
|
12637
12687
|
|
|
12688
|
+
// src/db/agent-names.ts
|
|
12689
|
+
function normalizeAgentNameInput(name) {
|
|
12690
|
+
return name.trim().toLowerCase();
|
|
12691
|
+
}
|
|
12692
|
+
function hasGeneratedNumericSuffix(name) {
|
|
12693
|
+
return NUMERIC_SUFFIX_RE.test(normalizeAgentNameInput(name));
|
|
12694
|
+
}
|
|
12695
|
+
function isGenericAgentName(name) {
|
|
12696
|
+
const normalized = normalizeAgentNameInput(name);
|
|
12697
|
+
if (RESERVED_GENERIC_NAMES.has(normalized))
|
|
12698
|
+
return true;
|
|
12699
|
+
for (const generic of RESERVED_GENERIC_NAMES) {
|
|
12700
|
+
if (normalized === `${generic}s`)
|
|
12701
|
+
return true;
|
|
12702
|
+
if (normalized.match(new RegExp(`^${generic}\\d+$`)))
|
|
12703
|
+
return true;
|
|
12704
|
+
if (normalized.match(new RegExp(`^${generic}[-_]\\d+$`)))
|
|
12705
|
+
return true;
|
|
12706
|
+
}
|
|
12707
|
+
return false;
|
|
12708
|
+
}
|
|
12709
|
+
function isBlockedAgentName(name) {
|
|
12710
|
+
const normalized = normalizeAgentNameInput(name);
|
|
12711
|
+
return isGenericAgentName(normalized) || hasGeneratedNumericSuffix(normalized) || !ONE_WORD_NAME_RE.test(normalized);
|
|
12712
|
+
}
|
|
12713
|
+
function suggestAgentNames(existingNames = []) {
|
|
12714
|
+
const existing = new Set([...existingNames].map(normalizeAgentNameInput));
|
|
12715
|
+
return PREFERRED_AGENT_NAMES.filter((name) => !existing.has(name));
|
|
12716
|
+
}
|
|
12717
|
+
function validateAgentName(name, existingNames = []) {
|
|
12718
|
+
const normalized = normalizeAgentNameInput(name);
|
|
12719
|
+
const suggestions = suggestAgentNames(existingNames).slice(0, 5);
|
|
12720
|
+
if (!normalized) {
|
|
12721
|
+
throw new InvalidAgentNameError(name, "choose a real one-word name instead of an empty value", suggestions);
|
|
12722
|
+
}
|
|
12723
|
+
if (/\s/.test(normalized)) {
|
|
12724
|
+
throw new InvalidAgentNameError(name, "use a single word, preferably a Roman or Greek name", suggestions);
|
|
12725
|
+
}
|
|
12726
|
+
if (normalized.length < 3) {
|
|
12727
|
+
throw new InvalidAgentNameError(name, "use a more distinctive name with at least three characters", suggestions);
|
|
12728
|
+
}
|
|
12729
|
+
if (isGenericAgentName(normalized)) {
|
|
12730
|
+
throw new InvalidAgentNameError(name, "generic names like agent, agent-1, assistant, or worker-2 are reserved", suggestions);
|
|
12731
|
+
}
|
|
12732
|
+
if (hasGeneratedNumericSuffix(normalized)) {
|
|
12733
|
+
throw new InvalidAgentNameError(name, "numbered suffix names are not allowed; pick a distinct human-readable name", suggestions);
|
|
12734
|
+
}
|
|
12735
|
+
if (!ONE_WORD_NAME_RE.test(normalized)) {
|
|
12736
|
+
throw new InvalidAgentNameError(name, "use one word made of letters only, preferably a Roman or Greek name", suggestions);
|
|
12737
|
+
}
|
|
12738
|
+
return normalized;
|
|
12739
|
+
}
|
|
12740
|
+
function tableHasColumn(db, table, column) {
|
|
12741
|
+
try {
|
|
12742
|
+
return db.query(`PRAGMA table_info(${table})`).all().some((row) => row.name === column);
|
|
12743
|
+
} catch {
|
|
12744
|
+
return false;
|
|
12745
|
+
}
|
|
12746
|
+
}
|
|
12747
|
+
function updateReferences(db, oldName, newName) {
|
|
12748
|
+
const refs = [
|
|
12749
|
+
["tasks", "assigned_to"],
|
|
12750
|
+
["tasks", "agent_id"],
|
|
12751
|
+
["tasks", "locked_by"],
|
|
12752
|
+
["tasks", "assigned_by"],
|
|
12753
|
+
["plans", "agent_id"],
|
|
12754
|
+
["sessions", "agent_id"],
|
|
12755
|
+
["task_comments", "agent_id"],
|
|
12756
|
+
["task_history", "agent_id"],
|
|
12757
|
+
["webhooks", "agent_id"],
|
|
12758
|
+
["task_files", "agent_id"],
|
|
12759
|
+
["task_time_logs", "agent_id"],
|
|
12760
|
+
["task_watchers", "agent_id"],
|
|
12761
|
+
["task_checkpoints", "agent_id"],
|
|
12762
|
+
["task_heartbeats", "agent_id"],
|
|
12763
|
+
["project_agent_roles", "agent_id"]
|
|
12764
|
+
];
|
|
12765
|
+
let changed = 0;
|
|
12766
|
+
for (const [table, column] of refs) {
|
|
12767
|
+
if (!tableHasColumn(db, table, column))
|
|
12768
|
+
continue;
|
|
12769
|
+
try {
|
|
12770
|
+
changed += db.run(`UPDATE ${table} SET ${column} = ? WHERE LOWER(${column}) = ?`, [newName, oldName]).changes;
|
|
12771
|
+
} catch {}
|
|
12772
|
+
}
|
|
12773
|
+
return changed;
|
|
12774
|
+
}
|
|
12775
|
+
function normalizeGeneratedAgentNames(db) {
|
|
12776
|
+
const rows = db.query("SELECT * FROM agents ORDER BY created_at, id").all();
|
|
12777
|
+
const existing = new Set(rows.map((agent) => normalizeAgentNameInput(agent.name)));
|
|
12778
|
+
const renamed = [];
|
|
12779
|
+
for (const agent of rows) {
|
|
12780
|
+
const oldName = normalizeAgentNameInput(agent.name);
|
|
12781
|
+
if (!isBlockedAgentName(oldName))
|
|
12782
|
+
continue;
|
|
12783
|
+
const candidates = suggestAgentNames(existing);
|
|
12784
|
+
const replacement = candidates[0];
|
|
12785
|
+
if (!replacement) {
|
|
12786
|
+
throw new Error("No safe agent names are available for normalization");
|
|
12787
|
+
}
|
|
12788
|
+
existing.delete(oldName);
|
|
12789
|
+
existing.add(replacement);
|
|
12790
|
+
db.run("UPDATE agents SET name = ?, last_seen_at = ? WHERE id = ?", [replacement, now(), agent.id]);
|
|
12791
|
+
const referenceUpdates = updateReferences(db, oldName, replacement);
|
|
12792
|
+
renamed.push({
|
|
12793
|
+
id: agent.id,
|
|
12794
|
+
old_name: oldName,
|
|
12795
|
+
new_name: replacement,
|
|
12796
|
+
reference_updates: referenceUpdates
|
|
12797
|
+
});
|
|
12798
|
+
}
|
|
12799
|
+
return renamed;
|
|
12800
|
+
}
|
|
12801
|
+
var InvalidAgentNameError, ROMAN_AGENT_NAMES, GREEK_AGENT_NAMES, NICE_AGENT_NAMES, PREFERRED_AGENT_NAMES, RESERVED_GENERIC_NAMES, NUMERIC_SUFFIX_RE, ONE_WORD_NAME_RE;
|
|
12802
|
+
var init_agent_names = __esm(() => {
|
|
12803
|
+
init_database();
|
|
12804
|
+
InvalidAgentNameError = class InvalidAgentNameError extends Error {
|
|
12805
|
+
suggestions;
|
|
12806
|
+
constructor(name, reason, suggestions = []) {
|
|
12807
|
+
super(`Invalid agent name "${name}": ${reason}${suggestions.length > 0 ? `. Try: ${suggestions.join(", ")}` : ""}`);
|
|
12808
|
+
this.name = "InvalidAgentNameError";
|
|
12809
|
+
this.suggestions = suggestions;
|
|
12810
|
+
}
|
|
12811
|
+
};
|
|
12812
|
+
ROMAN_AGENT_NAMES = [
|
|
12813
|
+
"caesar",
|
|
12814
|
+
"augustus",
|
|
12815
|
+
"marcus",
|
|
12816
|
+
"brutus",
|
|
12817
|
+
"cicero",
|
|
12818
|
+
"cato",
|
|
12819
|
+
"nero",
|
|
12820
|
+
"claudius",
|
|
12821
|
+
"tiberius",
|
|
12822
|
+
"hadrian",
|
|
12823
|
+
"trajan",
|
|
12824
|
+
"vespasian",
|
|
12825
|
+
"domitian",
|
|
12826
|
+
"caligula",
|
|
12827
|
+
"commodus",
|
|
12828
|
+
"livia",
|
|
12829
|
+
"julia",
|
|
12830
|
+
"octavia",
|
|
12831
|
+
"claudia",
|
|
12832
|
+
"agrippina",
|
|
12833
|
+
"cornelia",
|
|
12834
|
+
"valeria",
|
|
12835
|
+
"fulvia",
|
|
12836
|
+
"hortensia",
|
|
12837
|
+
"fabia"
|
|
12838
|
+
];
|
|
12839
|
+
GREEK_AGENT_NAMES = [
|
|
12840
|
+
"athena",
|
|
12841
|
+
"apollo",
|
|
12842
|
+
"artemis",
|
|
12843
|
+
"hera",
|
|
12844
|
+
"iris",
|
|
12845
|
+
"hector",
|
|
12846
|
+
"achilles",
|
|
12847
|
+
"odysseus",
|
|
12848
|
+
"theseus",
|
|
12849
|
+
"pericles",
|
|
12850
|
+
"solon",
|
|
12851
|
+
"sophia",
|
|
12852
|
+
"thalia",
|
|
12853
|
+
"calliope",
|
|
12854
|
+
"clio",
|
|
12855
|
+
"phoebe",
|
|
12856
|
+
"daphne",
|
|
12857
|
+
"leonidas",
|
|
12858
|
+
"andromeda",
|
|
12859
|
+
"cassander"
|
|
12860
|
+
];
|
|
12861
|
+
NICE_AGENT_NAMES = [
|
|
12862
|
+
"atlas",
|
|
12863
|
+
"aurora",
|
|
12864
|
+
"ember",
|
|
12865
|
+
"nova",
|
|
12866
|
+
"orion",
|
|
12867
|
+
"rhea",
|
|
12868
|
+
"selene",
|
|
12869
|
+
"sirius",
|
|
12870
|
+
"vesper",
|
|
12871
|
+
"zephyr"
|
|
12872
|
+
];
|
|
12873
|
+
PREFERRED_AGENT_NAMES = [
|
|
12874
|
+
...ROMAN_AGENT_NAMES,
|
|
12875
|
+
...GREEK_AGENT_NAMES,
|
|
12876
|
+
...NICE_AGENT_NAMES
|
|
12877
|
+
];
|
|
12878
|
+
RESERVED_GENERIC_NAMES = new Set([
|
|
12879
|
+
"agent",
|
|
12880
|
+
"agents",
|
|
12881
|
+
"ai",
|
|
12882
|
+
"assistant",
|
|
12883
|
+
"bot",
|
|
12884
|
+
"coder",
|
|
12885
|
+
"default",
|
|
12886
|
+
"helper",
|
|
12887
|
+
"model",
|
|
12888
|
+
"system",
|
|
12889
|
+
"user",
|
|
12890
|
+
"worker"
|
|
12891
|
+
]);
|
|
12892
|
+
NUMERIC_SUFFIX_RE = /[-_]\d+$/;
|
|
12893
|
+
ONE_WORD_NAME_RE = /^[a-z]+$/;
|
|
12894
|
+
});
|
|
12895
|
+
|
|
12638
12896
|
// src/db/agents.ts
|
|
12639
12897
|
var exports_agents = {};
|
|
12640
12898
|
__export(exports_agents, {
|
|
12641
12899
|
updateAgentActivity: () => updateAgentActivity,
|
|
12642
12900
|
updateAgent: () => updateAgent,
|
|
12643
12901
|
unarchiveAgent: () => unarchiveAgent,
|
|
12902
|
+
suggestAgentNames: () => suggestAgentNames,
|
|
12644
12903
|
releaseAgent: () => releaseAgent,
|
|
12645
12904
|
registerAgent: () => registerAgent,
|
|
12905
|
+
normalizeGeneratedAgentNames: () => normalizeGeneratedAgentNames,
|
|
12646
12906
|
matchCapabilities: () => matchCapabilities,
|
|
12647
12907
|
listAgents: () => listAgents,
|
|
12648
12908
|
isAgentConflict: () => isAgentConflict,
|
|
@@ -12654,7 +12914,8 @@ __export(exports_agents, {
|
|
|
12654
12914
|
getAgent: () => getAgent,
|
|
12655
12915
|
deleteAgent: () => deleteAgent,
|
|
12656
12916
|
autoReleaseStaleAgents: () => autoReleaseStaleAgents,
|
|
12657
|
-
archiveAgent: () => archiveAgent
|
|
12917
|
+
archiveAgent: () => archiveAgent,
|
|
12918
|
+
InvalidAgentNameError: () => InvalidAgentNameError
|
|
12658
12919
|
});
|
|
12659
12920
|
function getActiveWindowMs() {
|
|
12660
12921
|
const env = process.env["TODOS_AGENT_TIMEOUT_MS"];
|
|
@@ -12677,7 +12938,7 @@ function getAvailableNamesFromPool(pool, db) {
|
|
|
12677
12938
|
autoReleaseStaleAgents(db);
|
|
12678
12939
|
const cutoff = new Date(Date.now() - getActiveWindowMs()).toISOString();
|
|
12679
12940
|
const activeNames = new Set(db.query("SELECT name FROM agents WHERE status = 'active' AND last_seen_at > ?").all(cutoff).map((r) => r.name.toLowerCase()));
|
|
12680
|
-
return pool.filter((name) => !activeNames.has(name
|
|
12941
|
+
return pool.map(normalizeAgentNameInput).filter((name) => !activeNames.has(name));
|
|
12681
12942
|
}
|
|
12682
12943
|
function shortUuid() {
|
|
12683
12944
|
return crypto.randomUUID().slice(0, 8);
|
|
@@ -12693,7 +12954,8 @@ function rowToAgent(row) {
|
|
|
12693
12954
|
}
|
|
12694
12955
|
function registerAgent(input, db) {
|
|
12695
12956
|
const d = db || getDatabase();
|
|
12696
|
-
const
|
|
12957
|
+
const existingNames = d.query("SELECT name FROM agents").all().map((row) => row.name);
|
|
12958
|
+
const normalizedName = validateAgentName(input.name, existingNames);
|
|
12697
12959
|
const existing = getAgentByName(normalizedName, d);
|
|
12698
12960
|
if (existing) {
|
|
12699
12961
|
const lastSeenMs = new Date(existing.last_seen_at).getTime();
|
|
@@ -12826,7 +13088,8 @@ function updateAgent(id, input, db) {
|
|
|
12826
13088
|
const sets = ["last_seen_at = ?"];
|
|
12827
13089
|
const params = [now()];
|
|
12828
13090
|
if (input.name !== undefined) {
|
|
12829
|
-
const
|
|
13091
|
+
const existingNames = d.query("SELECT name FROM agents WHERE id != ?").all(id).map((row) => row.name);
|
|
13092
|
+
const newName = validateAgentName(input.name, existingNames);
|
|
12830
13093
|
const holder = getAgentByName(newName, d);
|
|
12831
13094
|
if (holder && holder.id !== id) {
|
|
12832
13095
|
const lastSeenMs = new Date(holder.last_seen_at).getTime();
|
|
@@ -12937,6 +13200,7 @@ function getCapableAgents(capabilities, opts, db) {
|
|
|
12937
13200
|
}
|
|
12938
13201
|
var init_agents = __esm(() => {
|
|
12939
13202
|
init_database();
|
|
13203
|
+
init_agent_names();
|
|
12940
13204
|
});
|
|
12941
13205
|
|
|
12942
13206
|
// src/types/index.ts
|
|
@@ -13203,6 +13467,23 @@ function loadConfig() {
|
|
|
13203
13467
|
cached = config;
|
|
13204
13468
|
return cached;
|
|
13205
13469
|
}
|
|
13470
|
+
function getAgentPoolForProject(workingDir) {
|
|
13471
|
+
const config = loadConfig();
|
|
13472
|
+
if (workingDir && config.project_pools) {
|
|
13473
|
+
let bestKey = null;
|
|
13474
|
+
let bestLen = 0;
|
|
13475
|
+
for (const key of Object.keys(config.project_pools)) {
|
|
13476
|
+
if (workingDir.startsWith(key) && key.length > bestLen) {
|
|
13477
|
+
bestKey = key;
|
|
13478
|
+
bestLen = key.length;
|
|
13479
|
+
}
|
|
13480
|
+
}
|
|
13481
|
+
if (bestKey && config.project_pools[bestKey]) {
|
|
13482
|
+
return config.project_pools[bestKey];
|
|
13483
|
+
}
|
|
13484
|
+
}
|
|
13485
|
+
return config.agent_pool || null;
|
|
13486
|
+
}
|
|
13206
13487
|
function getCompletionGuardConfig(projectPath) {
|
|
13207
13488
|
const config = loadConfig();
|
|
13208
13489
|
const global2 = { ...GUARD_DEFAULTS, ...config.completion_guard };
|
|
@@ -13224,6 +13505,27 @@ var init_config2 = __esm(() => {
|
|
|
13224
13505
|
});
|
|
13225
13506
|
|
|
13226
13507
|
// src/db/projects.ts
|
|
13508
|
+
var exports_projects = {};
|
|
13509
|
+
__export(exports_projects, {
|
|
13510
|
+
updateProject: () => updateProject,
|
|
13511
|
+
slugify: () => slugify,
|
|
13512
|
+
setMachineLocalPath: () => setMachineLocalPath,
|
|
13513
|
+
renameProject: () => renameProject,
|
|
13514
|
+
removeProjectSource: () => removeProjectSource,
|
|
13515
|
+
removeMachineLocalPath: () => removeMachineLocalPath,
|
|
13516
|
+
nextTaskShortId: () => nextTaskShortId,
|
|
13517
|
+
listProjects: () => listProjects,
|
|
13518
|
+
listProjectSources: () => listProjectSources,
|
|
13519
|
+
listMachineLocalPaths: () => listMachineLocalPaths,
|
|
13520
|
+
getProjectWithSources: () => getProjectWithSources,
|
|
13521
|
+
getProjectByPath: () => getProjectByPath,
|
|
13522
|
+
getProject: () => getProject,
|
|
13523
|
+
getMachineLocalPath: () => getMachineLocalPath,
|
|
13524
|
+
ensureProject: () => ensureProject,
|
|
13525
|
+
deleteProject: () => deleteProject,
|
|
13526
|
+
createProject: () => createProject,
|
|
13527
|
+
addProjectSource: () => addProjectSource
|
|
13528
|
+
});
|
|
13227
13529
|
function slugify(name) {
|
|
13228
13530
|
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
13229
13531
|
}
|
|
@@ -13262,6 +13564,18 @@ function getProject(id, db) {
|
|
|
13262
13564
|
const row = d.query("SELECT * FROM projects WHERE id = ?").get(id);
|
|
13263
13565
|
return row;
|
|
13264
13566
|
}
|
|
13567
|
+
function getProjectByPath(path, db) {
|
|
13568
|
+
const d = db || getDatabase();
|
|
13569
|
+
try {
|
|
13570
|
+
const machineId = getMachineId(d);
|
|
13571
|
+
const machineRow = d.query(`SELECT p.* FROM projects p
|
|
13572
|
+
JOIN project_machine_paths pmp ON pmp.project_id = p.id
|
|
13573
|
+
WHERE pmp.machine_id = ? AND pmp.path = ?`).get(machineId, path);
|
|
13574
|
+
if (machineRow)
|
|
13575
|
+
return machineRow;
|
|
13576
|
+
} catch {}
|
|
13577
|
+
return d.query("SELECT * FROM projects WHERE path = ?").get(path);
|
|
13578
|
+
}
|
|
13265
13579
|
function listProjects(db) {
|
|
13266
13580
|
const d = db || getDatabase();
|
|
13267
13581
|
return d.query("SELECT * FROM projects ORDER BY name").all();
|
|
@@ -13293,11 +13607,141 @@ function updateProject(id, input, db) {
|
|
|
13293
13607
|
d.run(`UPDATE projects SET ${sets.join(", ")} WHERE id = ?`, params);
|
|
13294
13608
|
return getProject(id, d);
|
|
13295
13609
|
}
|
|
13610
|
+
function renameProject(id, input, db) {
|
|
13611
|
+
const d = db || getDatabase();
|
|
13612
|
+
const project = getProject(id, d);
|
|
13613
|
+
if (!project)
|
|
13614
|
+
throw new ProjectNotFoundError(id);
|
|
13615
|
+
let taskListsUpdated = 0;
|
|
13616
|
+
const ts = now();
|
|
13617
|
+
if (input.new_slug !== undefined) {
|
|
13618
|
+
const normalised = input.new_slug.toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-|-$/g, "");
|
|
13619
|
+
if (!normalised)
|
|
13620
|
+
throw new Error("Invalid slug \u2014 must be non-empty kebab-case");
|
|
13621
|
+
const conflict = d.query("SELECT id FROM projects WHERE task_list_id = ? AND id != ?").get(normalised, id);
|
|
13622
|
+
if (conflict)
|
|
13623
|
+
throw new Error(`Slug "${normalised}" is already used by another project`);
|
|
13624
|
+
const oldSlug = project.task_list_id;
|
|
13625
|
+
d.run("UPDATE projects SET task_list_id = ?, updated_at = ? WHERE id = ?", [normalised, ts, id]);
|
|
13626
|
+
if (oldSlug) {
|
|
13627
|
+
const result = d.run("UPDATE task_lists SET slug = ?, name = COALESCE(?, name), updated_at = ? WHERE project_id = ? AND slug = ?", [normalised, input.name ?? null, ts, id, oldSlug]);
|
|
13628
|
+
taskListsUpdated = result.changes;
|
|
13629
|
+
}
|
|
13630
|
+
}
|
|
13631
|
+
if (input.name !== undefined) {
|
|
13632
|
+
d.run("UPDATE projects SET name = ?, updated_at = ? WHERE id = ?", [input.name, ts, id]);
|
|
13633
|
+
}
|
|
13634
|
+
return { project: getProject(id, d), task_lists_updated: taskListsUpdated };
|
|
13635
|
+
}
|
|
13296
13636
|
function deleteProject(id, db) {
|
|
13297
13637
|
const d = db || getDatabase();
|
|
13298
13638
|
const result = d.run("DELETE FROM projects WHERE id = ?", [id]);
|
|
13299
13639
|
return result.changes > 0;
|
|
13300
13640
|
}
|
|
13641
|
+
function rowToSource(row) {
|
|
13642
|
+
return {
|
|
13643
|
+
...row,
|
|
13644
|
+
metadata: row.metadata ? JSON.parse(row.metadata) : {}
|
|
13645
|
+
};
|
|
13646
|
+
}
|
|
13647
|
+
function addProjectSource(input, db) {
|
|
13648
|
+
const d = db || getDatabase();
|
|
13649
|
+
const id = uuid();
|
|
13650
|
+
const timestamp = now();
|
|
13651
|
+
d.run(`INSERT INTO project_sources (id, project_id, type, name, uri, description, metadata, created_at, updated_at)
|
|
13652
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
13653
|
+
id,
|
|
13654
|
+
input.project_id,
|
|
13655
|
+
input.type,
|
|
13656
|
+
input.name,
|
|
13657
|
+
input.uri,
|
|
13658
|
+
input.description || null,
|
|
13659
|
+
JSON.stringify(input.metadata || {}),
|
|
13660
|
+
timestamp,
|
|
13661
|
+
timestamp
|
|
13662
|
+
]);
|
|
13663
|
+
return rowToSource(d.query("SELECT * FROM project_sources WHERE id = ?").get(id));
|
|
13664
|
+
}
|
|
13665
|
+
function removeProjectSource(id, db) {
|
|
13666
|
+
const d = db || getDatabase();
|
|
13667
|
+
const result = d.run("DELETE FROM project_sources WHERE id = ?", [id]);
|
|
13668
|
+
return result.changes > 0;
|
|
13669
|
+
}
|
|
13670
|
+
function listProjectSources(projectId, db) {
|
|
13671
|
+
const d = db || getDatabase();
|
|
13672
|
+
const rows = d.query("SELECT * FROM project_sources WHERE project_id = ? ORDER BY name").all(projectId);
|
|
13673
|
+
return rows.map(rowToSource);
|
|
13674
|
+
}
|
|
13675
|
+
function getProjectWithSources(id, db) {
|
|
13676
|
+
const d = db || getDatabase();
|
|
13677
|
+
const project = getProject(id, d);
|
|
13678
|
+
if (!project)
|
|
13679
|
+
return null;
|
|
13680
|
+
project.sources = listProjectSources(id, d);
|
|
13681
|
+
return project;
|
|
13682
|
+
}
|
|
13683
|
+
function nextTaskShortId(projectId, db) {
|
|
13684
|
+
const d = db || getDatabase();
|
|
13685
|
+
const project = getProject(projectId, d);
|
|
13686
|
+
if (!project || !project.task_prefix)
|
|
13687
|
+
return null;
|
|
13688
|
+
d.run("UPDATE projects SET task_counter = task_counter + 1, updated_at = ? WHERE id = ?", [now(), projectId]);
|
|
13689
|
+
const updated = getProject(projectId, d);
|
|
13690
|
+
const padded = String(updated.task_counter).padStart(5, "0");
|
|
13691
|
+
return `${updated.task_prefix}-${padded}`;
|
|
13692
|
+
}
|
|
13693
|
+
function ensureProject(name, path, db) {
|
|
13694
|
+
const d = db || getDatabase();
|
|
13695
|
+
const existing = getProjectByPath(path, d);
|
|
13696
|
+
if (existing) {
|
|
13697
|
+
if (!existing.task_prefix) {
|
|
13698
|
+
const prefix = generatePrefix(existing.name, d);
|
|
13699
|
+
d.run("UPDATE projects SET task_prefix = ?, updated_at = ? WHERE id = ?", [prefix, now(), existing.id]);
|
|
13700
|
+
return getProject(existing.id, d);
|
|
13701
|
+
}
|
|
13702
|
+
setMachineLocalPath(existing.id, path, d);
|
|
13703
|
+
return existing;
|
|
13704
|
+
}
|
|
13705
|
+
const project = createProject({ name, path }, d);
|
|
13706
|
+
setMachineLocalPath(project.id, path, d);
|
|
13707
|
+
return project;
|
|
13708
|
+
}
|
|
13709
|
+
function setMachineLocalPath(projectId, path, db) {
|
|
13710
|
+
const d = db || getDatabase();
|
|
13711
|
+
const machineId = getMachineId(d);
|
|
13712
|
+
const ts = now();
|
|
13713
|
+
const existing = d.query("SELECT * FROM project_machine_paths WHERE project_id = ? AND machine_id = ?").get(projectId, machineId);
|
|
13714
|
+
if (existing) {
|
|
13715
|
+
if (existing.path !== path) {
|
|
13716
|
+
d.run("UPDATE project_machine_paths SET path = ?, updated_at = ? WHERE id = ?", [path, ts, existing.id]);
|
|
13717
|
+
}
|
|
13718
|
+
return d.query("SELECT * FROM project_machine_paths WHERE id = ?").get(existing.id);
|
|
13719
|
+
}
|
|
13720
|
+
const id = uuid();
|
|
13721
|
+
d.run("INSERT INTO project_machine_paths (id, project_id, machine_id, path, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)", [id, projectId, machineId, path, ts, ts]);
|
|
13722
|
+
return d.query("SELECT * FROM project_machine_paths WHERE id = ?").get(id);
|
|
13723
|
+
}
|
|
13724
|
+
function getMachineLocalPath(projectId, db) {
|
|
13725
|
+
const d = db || getDatabase();
|
|
13726
|
+
try {
|
|
13727
|
+
const machineId = getMachineId(d);
|
|
13728
|
+
const row = d.query("SELECT path FROM project_machine_paths WHERE project_id = ? AND machine_id = ?").get(projectId, machineId);
|
|
13729
|
+
if (row)
|
|
13730
|
+
return row.path;
|
|
13731
|
+
} catch {}
|
|
13732
|
+
const project = getProject(projectId, d);
|
|
13733
|
+
return project?.path ?? null;
|
|
13734
|
+
}
|
|
13735
|
+
function listMachineLocalPaths(projectId, db) {
|
|
13736
|
+
const d = db || getDatabase();
|
|
13737
|
+
return d.query("SELECT * FROM project_machine_paths WHERE project_id = ? ORDER BY machine_id").all(projectId);
|
|
13738
|
+
}
|
|
13739
|
+
function removeMachineLocalPath(projectId, machineId, db) {
|
|
13740
|
+
const d = db || getDatabase();
|
|
13741
|
+
const mid = machineId ?? getMachineId(d);
|
|
13742
|
+
const result = d.run("DELETE FROM project_machine_paths WHERE project_id = ? AND machine_id = ?", [projectId, mid]);
|
|
13743
|
+
return result.changes > 0;
|
|
13744
|
+
}
|
|
13301
13745
|
var init_projects = __esm(() => {
|
|
13302
13746
|
init_types2();
|
|
13303
13747
|
init_database();
|
|
@@ -13558,8 +14002,8 @@ function createTask(input, db) {
|
|
|
13558
14002
|
let id = uuid();
|
|
13559
14003
|
for (let attempt = 0;attempt < 3; attempt++) {
|
|
13560
14004
|
try {
|
|
13561
|
-
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)
|
|
13562
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
14005
|
+
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)
|
|
14006
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
13563
14007
|
id,
|
|
13564
14008
|
null,
|
|
13565
14009
|
input.project_id || null,
|
|
@@ -13581,6 +14025,10 @@ function createTask(input, db) {
|
|
|
13581
14025
|
timestamp,
|
|
13582
14026
|
input.due_at || null,
|
|
13583
14027
|
input.estimated_minutes || null,
|
|
14028
|
+
input.confidence ?? null,
|
|
14029
|
+
input.retry_count ?? 0,
|
|
14030
|
+
input.max_retries ?? 3,
|
|
14031
|
+
input.retry_after ?? null,
|
|
13584
14032
|
input.requires_approval ? 1 : 0,
|
|
13585
14033
|
null,
|
|
13586
14034
|
null,
|
|
@@ -13605,11 +14053,11 @@ function createTask(input, db) {
|
|
|
13605
14053
|
if (tags.length > 0) {
|
|
13606
14054
|
insertTaskTags(id, tags, d);
|
|
13607
14055
|
}
|
|
13608
|
-
const task =
|
|
14056
|
+
const task = getTask(id, d);
|
|
13609
14057
|
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(() => {});
|
|
13610
14058
|
return task;
|
|
13611
14059
|
}
|
|
13612
|
-
function
|
|
14060
|
+
function getTask(id, db) {
|
|
13613
14061
|
const d = db || getDatabase();
|
|
13614
14062
|
const row = d.query("SELECT * FROM tasks WHERE id = ?").get(id);
|
|
13615
14063
|
if (!row)
|
|
@@ -13618,7 +14066,7 @@ function getTask2(id, db) {
|
|
|
13618
14066
|
}
|
|
13619
14067
|
function getTaskWithRelations(id, db) {
|
|
13620
14068
|
const d = db || getDatabase();
|
|
13621
|
-
const task =
|
|
14069
|
+
const task = getTask(id, d);
|
|
13622
14070
|
if (!task)
|
|
13623
14071
|
return null;
|
|
13624
14072
|
const subtaskRows = d.query("SELECT * FROM tasks WHERE parent_id = ? ORDER BY created_at").all(id);
|
|
@@ -13632,7 +14080,7 @@ function getTaskWithRelations(id, db) {
|
|
|
13632
14080
|
WHERE td.depends_on = ?`).all(id);
|
|
13633
14081
|
const blocked_by = blockedByRows.map(rowToTask);
|
|
13634
14082
|
const comments = d.query("SELECT * FROM task_comments WHERE task_id = ? ORDER BY created_at").all(id);
|
|
13635
|
-
const parent = task.parent_id ?
|
|
14083
|
+
const parent = task.parent_id ? getTask(task.parent_id, d) : null;
|
|
13636
14084
|
const checklist = getChecklist(id, d);
|
|
13637
14085
|
return {
|
|
13638
14086
|
...task,
|
|
@@ -13819,14 +14267,16 @@ function countTasks(filter = {}, db) {
|
|
|
13819
14267
|
}
|
|
13820
14268
|
function updateTask(id, input, db) {
|
|
13821
14269
|
const d = db || getDatabase();
|
|
13822
|
-
const task =
|
|
14270
|
+
const task = getTask(id, d);
|
|
13823
14271
|
if (!task)
|
|
13824
14272
|
throw new TaskNotFoundError(id);
|
|
13825
14273
|
if (task.version !== input.version) {
|
|
13826
14274
|
throw new VersionConflictError(id, input.version, task.version);
|
|
13827
14275
|
}
|
|
14276
|
+
const timestamp = now();
|
|
14277
|
+
const completionTimestamp = input.completed_at ?? timestamp;
|
|
13828
14278
|
const sets = ["version = version + 1", "updated_at = ?"];
|
|
13829
|
-
const params = [
|
|
14279
|
+
const params = [timestamp];
|
|
13830
14280
|
if (input.title !== undefined) {
|
|
13831
14281
|
sets.push("title = ?");
|
|
13832
14282
|
params.push(input.title);
|
|
@@ -13843,13 +14293,17 @@ function updateTask(id, input, db) {
|
|
|
13843
14293
|
params.push(input.status);
|
|
13844
14294
|
if (input.status === "completed") {
|
|
13845
14295
|
sets.push("completed_at = ?");
|
|
13846
|
-
params.push(
|
|
14296
|
+
params.push(completionTimestamp);
|
|
13847
14297
|
}
|
|
13848
14298
|
}
|
|
13849
14299
|
if (input.priority !== undefined) {
|
|
13850
14300
|
sets.push("priority = ?");
|
|
13851
14301
|
params.push(input.priority);
|
|
13852
14302
|
}
|
|
14303
|
+
if (input.project_id !== undefined) {
|
|
14304
|
+
sets.push("project_id = ?");
|
|
14305
|
+
params.push(input.project_id);
|
|
14306
|
+
}
|
|
13853
14307
|
if (input.assigned_to !== undefined) {
|
|
13854
14308
|
sets.push("assigned_to = ?");
|
|
13855
14309
|
params.push(input.assigned_to);
|
|
@@ -13878,6 +14332,30 @@ function updateTask(id, input, db) {
|
|
|
13878
14332
|
sets.push("estimated_minutes = ?");
|
|
13879
14333
|
params.push(input.estimated_minutes);
|
|
13880
14334
|
}
|
|
14335
|
+
if (input.actual_minutes !== undefined) {
|
|
14336
|
+
sets.push("actual_minutes = ?");
|
|
14337
|
+
params.push(input.actual_minutes);
|
|
14338
|
+
}
|
|
14339
|
+
if (input.completed_at !== undefined && input.status !== "completed") {
|
|
14340
|
+
sets.push("completed_at = ?");
|
|
14341
|
+
params.push(input.completed_at);
|
|
14342
|
+
}
|
|
14343
|
+
if (input.confidence !== undefined) {
|
|
14344
|
+
sets.push("confidence = ?");
|
|
14345
|
+
params.push(input.confidence);
|
|
14346
|
+
}
|
|
14347
|
+
if (input.retry_count !== undefined) {
|
|
14348
|
+
sets.push("retry_count = ?");
|
|
14349
|
+
params.push(input.retry_count);
|
|
14350
|
+
}
|
|
14351
|
+
if (input.max_retries !== undefined) {
|
|
14352
|
+
sets.push("max_retries = ?");
|
|
14353
|
+
params.push(input.max_retries);
|
|
14354
|
+
}
|
|
14355
|
+
if (input.retry_after !== undefined) {
|
|
14356
|
+
sets.push("retry_after = ?");
|
|
14357
|
+
params.push(input.retry_after);
|
|
14358
|
+
}
|
|
13881
14359
|
if (input.requires_approval !== undefined) {
|
|
13882
14360
|
sets.push("requires_approval = ?");
|
|
13883
14361
|
params.push(input.requires_approval ? 1 : 0);
|
|
@@ -13899,7 +14377,7 @@ function updateTask(id, input, db) {
|
|
|
13899
14377
|
params.push(id, input.version);
|
|
13900
14378
|
const result = d.run(`UPDATE tasks SET ${sets.join(", ")} WHERE id = ? AND version = ?`, params);
|
|
13901
14379
|
if (result.changes === 0) {
|
|
13902
|
-
const current =
|
|
14380
|
+
const current = getTask(id, d);
|
|
13903
14381
|
throw new VersionConflictError(id, input.version, current?.version ?? -1);
|
|
13904
14382
|
}
|
|
13905
14383
|
if (input.tags !== undefined) {
|
|
@@ -13928,11 +14406,16 @@ function updateTask(id, input, db) {
|
|
|
13928
14406
|
tags: input.tags ?? task.tags,
|
|
13929
14407
|
metadata: input.metadata ?? task.metadata,
|
|
13930
14408
|
version: task.version + 1,
|
|
13931
|
-
updated_at:
|
|
13932
|
-
completed_at: input.status === "completed" ?
|
|
14409
|
+
updated_at: timestamp,
|
|
14410
|
+
completed_at: input.status === "completed" ? completionTimestamp : input.completed_at !== undefined ? input.completed_at : task.completed_at,
|
|
14411
|
+
actual_minutes: input.actual_minutes ?? task.actual_minutes,
|
|
14412
|
+
confidence: input.confidence !== undefined ? input.confidence : task.confidence,
|
|
14413
|
+
retry_count: input.retry_count ?? task.retry_count,
|
|
14414
|
+
max_retries: input.max_retries ?? task.max_retries,
|
|
14415
|
+
retry_after: input.retry_after !== undefined ? input.retry_after : task.retry_after,
|
|
13933
14416
|
requires_approval: input.requires_approval !== undefined ? input.requires_approval : task.requires_approval,
|
|
13934
14417
|
approved_by: input.approved_by ?? task.approved_by,
|
|
13935
|
-
approved_at: input.approved_by ?
|
|
14418
|
+
approved_at: input.approved_by ? timestamp : task.approved_at
|
|
13936
14419
|
};
|
|
13937
14420
|
}
|
|
13938
14421
|
function deleteTask(id, db) {
|
|
@@ -14085,9 +14568,9 @@ var init_templates = __esm(() => {
|
|
|
14085
14568
|
// src/db/task-graph.ts
|
|
14086
14569
|
function addDependency(taskId, dependsOn, db) {
|
|
14087
14570
|
const d = db || getDatabase();
|
|
14088
|
-
if (!
|
|
14571
|
+
if (!getTask(taskId, d))
|
|
14089
14572
|
throw new TaskNotFoundError(taskId);
|
|
14090
|
-
if (!
|
|
14573
|
+
if (!getTask(dependsOn, d))
|
|
14091
14574
|
throw new TaskNotFoundError(dependsOn);
|
|
14092
14575
|
if (wouldCreateCycle(taskId, dependsOn, d)) {
|
|
14093
14576
|
throw new DependencyCycleError(taskId, dependsOn);
|
|
@@ -14109,7 +14592,7 @@ function getTaskDependents(taskId, db) {
|
|
|
14109
14592
|
}
|
|
14110
14593
|
function cloneTask(taskId, overrides, db) {
|
|
14111
14594
|
const d = db || getDatabase();
|
|
14112
|
-
const source =
|
|
14595
|
+
const source = getTask(taskId, d);
|
|
14113
14596
|
if (!source)
|
|
14114
14597
|
throw new TaskNotFoundError(taskId);
|
|
14115
14598
|
const input = {
|
|
@@ -14132,13 +14615,13 @@ function cloneTask(taskId, overrides, db) {
|
|
|
14132
14615
|
}
|
|
14133
14616
|
function getTaskGraph(taskId, direction = "both", db) {
|
|
14134
14617
|
const d = db || getDatabase();
|
|
14135
|
-
const task =
|
|
14618
|
+
const task = getTask(taskId, d);
|
|
14136
14619
|
if (!task)
|
|
14137
14620
|
throw new TaskNotFoundError(taskId);
|
|
14138
14621
|
function toNode(t) {
|
|
14139
14622
|
const deps = getTaskDependencies(t.id, d);
|
|
14140
14623
|
const hasUnfinishedDeps = deps.some((dep) => {
|
|
14141
|
-
const depTask =
|
|
14624
|
+
const depTask = getTask(dep.depends_on, d);
|
|
14142
14625
|
return depTask && depTask.status !== "completed";
|
|
14143
14626
|
});
|
|
14144
14627
|
return { id: t.id, short_id: t.short_id, title: t.title, status: t.status, priority: t.priority, is_blocked: hasUnfinishedDeps };
|
|
@@ -14149,7 +14632,7 @@ function getTaskGraph(taskId, direction = "both", db) {
|
|
|
14149
14632
|
visited.add(id);
|
|
14150
14633
|
const deps = d.query("SELECT depends_on FROM task_dependencies WHERE task_id = ?").all(id);
|
|
14151
14634
|
return deps.map((dep) => {
|
|
14152
|
-
const depTask =
|
|
14635
|
+
const depTask = getTask(dep.depends_on, d);
|
|
14153
14636
|
if (!depTask)
|
|
14154
14637
|
return null;
|
|
14155
14638
|
return { task: toNode(depTask), depends_on: buildUp(dep.depends_on, visited), blocks: [] };
|
|
@@ -14161,7 +14644,7 @@ function getTaskGraph(taskId, direction = "both", db) {
|
|
|
14161
14644
|
visited.add(id);
|
|
14162
14645
|
const dependents = d.query("SELECT task_id FROM task_dependencies WHERE depends_on = ?").all(id);
|
|
14163
14646
|
return dependents.map((dep) => {
|
|
14164
|
-
const depTask =
|
|
14647
|
+
const depTask = getTask(dep.task_id, d);
|
|
14165
14648
|
if (!depTask)
|
|
14166
14649
|
return null;
|
|
14167
14650
|
return { task: toNode(depTask), depends_on: [], blocks: buildDown(dep.task_id, visited) };
|
|
@@ -14174,7 +14657,7 @@ function getTaskGraph(taskId, direction = "both", db) {
|
|
|
14174
14657
|
}
|
|
14175
14658
|
function moveTask(taskId, target, db) {
|
|
14176
14659
|
const d = db || getDatabase();
|
|
14177
|
-
const task =
|
|
14660
|
+
const task = getTask(taskId, d);
|
|
14178
14661
|
if (!task)
|
|
14179
14662
|
throw new TaskNotFoundError(taskId);
|
|
14180
14663
|
const sets = ["updated_at = ?", "version = version + 1"];
|
|
@@ -14193,7 +14676,7 @@ function moveTask(taskId, target, db) {
|
|
|
14193
14676
|
}
|
|
14194
14677
|
params.push(taskId);
|
|
14195
14678
|
d.run(`UPDATE tasks SET ${sets.join(", ")} WHERE id = ?`, params);
|
|
14196
|
-
return
|
|
14679
|
+
return getTask(taskId, d);
|
|
14197
14680
|
}
|
|
14198
14681
|
function wouldCreateCycle(taskId, dependsOn, db) {
|
|
14199
14682
|
const visited = new Set;
|
|
@@ -14226,7 +14709,7 @@ function getBlockingDeps(id, db) {
|
|
|
14226
14709
|
return [];
|
|
14227
14710
|
const blocking = [];
|
|
14228
14711
|
for (const dep of deps) {
|
|
14229
|
-
const task =
|
|
14712
|
+
const task = getTask(dep.depends_on, d);
|
|
14230
14713
|
if (task && task.status !== "completed")
|
|
14231
14714
|
blocking.push(task);
|
|
14232
14715
|
}
|
|
@@ -14234,7 +14717,7 @@ function getBlockingDeps(id, db) {
|
|
|
14234
14717
|
}
|
|
14235
14718
|
function startTask(id, agentId, db) {
|
|
14236
14719
|
const d = db || getDatabase();
|
|
14237
|
-
const task =
|
|
14720
|
+
const task = getTask(id, d);
|
|
14238
14721
|
if (!task)
|
|
14239
14722
|
throw new TaskNotFoundError(id);
|
|
14240
14723
|
const blocking = getBlockingDeps(id, d);
|
|
@@ -14257,7 +14740,7 @@ function startTask(id, agentId, db) {
|
|
|
14257
14740
|
}
|
|
14258
14741
|
function completeTask(id, agentId, db, options) {
|
|
14259
14742
|
const d = db || getDatabase();
|
|
14260
|
-
const task =
|
|
14743
|
+
const task = getTask(id, d);
|
|
14261
14744
|
if (!task)
|
|
14262
14745
|
throw new TaskNotFoundError(id);
|
|
14263
14746
|
if (agentId && task.locked_by && task.locked_by !== agentId && !isLockExpired(task.locked_at)) {
|
|
@@ -14273,14 +14756,14 @@ function completeTask(id, agentId, db, options) {
|
|
|
14273
14756
|
completionMeta._completion = { confidence: options.confidence };
|
|
14274
14757
|
}
|
|
14275
14758
|
const hasMeta = Object.keys(completionMeta).length > 0;
|
|
14276
|
-
const timestamp = now();
|
|
14759
|
+
const timestamp = options?.completed_at || now();
|
|
14277
14760
|
const confidence = options?.confidence !== undefined ? options.confidence : null;
|
|
14278
14761
|
const tx = d.transaction(() => {
|
|
14279
14762
|
if (hasMeta) {
|
|
14280
14763
|
const meta2 = { ...task.metadata, ...completionMeta };
|
|
14281
14764
|
const metaResult = d.run("UPDATE tasks SET metadata = ?, version = version + 1, updated_at = ? WHERE id = ? AND version = ?", [JSON.stringify(meta2), timestamp, id, task.version]);
|
|
14282
14765
|
if (metaResult.changes === 0) {
|
|
14283
|
-
const current =
|
|
14766
|
+
const current = getTask(id, d);
|
|
14284
14767
|
throw new VersionConflictError(id, task.version, current?.version ?? -1);
|
|
14285
14768
|
}
|
|
14286
14769
|
}
|
|
@@ -14337,7 +14820,7 @@ function completeTask(id, agentId, db, options) {
|
|
|
14337
14820
|
}
|
|
14338
14821
|
function lockTask(id, agentId, db) {
|
|
14339
14822
|
const d = db || getDatabase();
|
|
14340
|
-
const task =
|
|
14823
|
+
const task = getTask(id, d);
|
|
14341
14824
|
if (!task)
|
|
14342
14825
|
throw new TaskNotFoundError(id);
|
|
14343
14826
|
if (task.locked_by === agentId && !isLockExpired(task.locked_at)) {
|
|
@@ -14348,7 +14831,7 @@ function lockTask(id, agentId, db) {
|
|
|
14348
14831
|
const result = d.run(`UPDATE tasks SET locked_by = ?, locked_at = ?, version = version + 1, updated_at = ?
|
|
14349
14832
|
WHERE id = ? AND (locked_by IS NULL OR locked_by = ? OR locked_at < ?)`, [agentId, timestamp, timestamp, id, agentId, cutoff]);
|
|
14350
14833
|
if (result.changes === 0) {
|
|
14351
|
-
const current =
|
|
14834
|
+
const current = getTask(id, d);
|
|
14352
14835
|
if (!current)
|
|
14353
14836
|
throw new TaskNotFoundError(id);
|
|
14354
14837
|
if (current.locked_by && !isLockExpired(current.locked_at)) {
|
|
@@ -14364,7 +14847,7 @@ function lockTask(id, agentId, db) {
|
|
|
14364
14847
|
}
|
|
14365
14848
|
function unlockTask(id, agentId, db) {
|
|
14366
14849
|
const d = db || getDatabase();
|
|
14367
|
-
const task =
|
|
14850
|
+
const task = getTask(id, d);
|
|
14368
14851
|
if (!task)
|
|
14369
14852
|
throw new TaskNotFoundError(id);
|
|
14370
14853
|
if (agentId && task.locked_by && task.locked_by !== agentId) {
|
|
@@ -14465,7 +14948,7 @@ function getTasksChangedSince(since, filters, db) {
|
|
|
14465
14948
|
}
|
|
14466
14949
|
function failTask(id, agentId, reason, options, db) {
|
|
14467
14950
|
const d = db || getDatabase();
|
|
14468
|
-
const task =
|
|
14951
|
+
const task = getTask(id, d);
|
|
14469
14952
|
if (!task)
|
|
14470
14953
|
throw new TaskNotFoundError(id);
|
|
14471
14954
|
const meta = {
|
|
@@ -14664,7 +15147,7 @@ function getStatus(filters, agentId, options, db) {
|
|
|
14664
15147
|
}
|
|
14665
15148
|
function decomposeTasks(parentId, subtasks, options, db) {
|
|
14666
15149
|
const d = db || getDatabase();
|
|
14667
|
-
const parent =
|
|
15150
|
+
const parent = getTask(parentId, d);
|
|
14668
15151
|
if (!parent)
|
|
14669
15152
|
throw new TaskNotFoundError(parentId);
|
|
14670
15153
|
const created = [];
|
|
@@ -14695,7 +15178,7 @@ function decomposeTasks(parentId, subtasks, options, db) {
|
|
|
14695
15178
|
function setTaskStatus(id, status, _agentId, db) {
|
|
14696
15179
|
const d = db || getDatabase();
|
|
14697
15180
|
for (let attempt = 0;attempt < 3; attempt++) {
|
|
14698
|
-
const task =
|
|
15181
|
+
const task = getTask(id, d);
|
|
14699
15182
|
if (!task)
|
|
14700
15183
|
throw new TaskNotFoundError(id);
|
|
14701
15184
|
if (task.status === status)
|
|
@@ -14713,7 +15196,7 @@ function setTaskStatus(id, status, _agentId, db) {
|
|
|
14713
15196
|
function setTaskPriority(id, priority, _agentId, db) {
|
|
14714
15197
|
const d = db || getDatabase();
|
|
14715
15198
|
for (let attempt = 0;attempt < 3; attempt++) {
|
|
14716
|
-
const task =
|
|
15199
|
+
const task = getTask(id, d);
|
|
14717
15200
|
if (!task)
|
|
14718
15201
|
throw new TaskNotFoundError(id);
|
|
14719
15202
|
if (task.priority === priority)
|
|
@@ -14821,7 +15304,7 @@ function bulkUpdateTasks(taskIds, updates, db) {
|
|
|
14821
15304
|
const tx = d.transaction(() => {
|
|
14822
15305
|
for (const id of taskIds) {
|
|
14823
15306
|
try {
|
|
14824
|
-
const task =
|
|
15307
|
+
const task = getTask(id, d);
|
|
14825
15308
|
if (!task) {
|
|
14826
15309
|
failed.push({ id, error: "Task not found" });
|
|
14827
15310
|
continue;
|
|
@@ -14836,6 +15319,30 @@ function bulkUpdateTasks(taskIds, updates, db) {
|
|
|
14836
15319
|
tx();
|
|
14837
15320
|
return { updated, failed };
|
|
14838
15321
|
}
|
|
15322
|
+
function bulkDeleteTasks(taskIds, force = false, db) {
|
|
15323
|
+
const d = db || getDatabase();
|
|
15324
|
+
let deleted = 0;
|
|
15325
|
+
let skipped = 0;
|
|
15326
|
+
const failed = [];
|
|
15327
|
+
const tx = d.transaction(() => {
|
|
15328
|
+
for (const id of taskIds) {
|
|
15329
|
+
try {
|
|
15330
|
+
const childCount = d.query("SELECT COUNT(*) as count FROM tasks WHERE parent_id = ?").get(id);
|
|
15331
|
+
if (!force && childCount.count > 0) {
|
|
15332
|
+
skipped++;
|
|
15333
|
+
continue;
|
|
15334
|
+
}
|
|
15335
|
+
const result = d.run("DELETE FROM tasks WHERE id = ?", [id]);
|
|
15336
|
+
if (result.changes > 0)
|
|
15337
|
+
deleted++;
|
|
15338
|
+
} catch (e) {
|
|
15339
|
+
failed.push({ id, error: e instanceof Error ? e.message : String(e) });
|
|
15340
|
+
}
|
|
15341
|
+
}
|
|
15342
|
+
});
|
|
15343
|
+
tx();
|
|
15344
|
+
return { deleted, skipped, failed };
|
|
15345
|
+
}
|
|
14839
15346
|
function archiveTasks(options, db) {
|
|
14840
15347
|
const d = db || getDatabase();
|
|
14841
15348
|
const conditions = ["archived_at IS NULL"];
|
|
@@ -14860,10 +15367,33 @@ function archiveTasks(options, db) {
|
|
|
14860
15367
|
const result = d.run(`UPDATE tasks SET archived_at = ? WHERE ${conditions.join(" AND ")}`, [ts, ...params]);
|
|
14861
15368
|
return { archived: result.changes };
|
|
14862
15369
|
}
|
|
15370
|
+
function archiveCompletedTasks(days = 7, projectId, db) {
|
|
15371
|
+
return archiveTasks({
|
|
15372
|
+
project_id: projectId,
|
|
15373
|
+
older_than_days: days,
|
|
15374
|
+
status: ["completed"]
|
|
15375
|
+
}, db).archived;
|
|
15376
|
+
}
|
|
15377
|
+
function getArchivedTasks(opts = {}, db) {
|
|
15378
|
+
const d = db || getDatabase();
|
|
15379
|
+
const conditions = ["archived_at IS NOT NULL"];
|
|
15380
|
+
const params = [];
|
|
15381
|
+
if (opts.project_id) {
|
|
15382
|
+
conditions.push("project_id = ?");
|
|
15383
|
+
params.push(opts.project_id);
|
|
15384
|
+
}
|
|
15385
|
+
let limitClause = "";
|
|
15386
|
+
if (opts.limit) {
|
|
15387
|
+
limitClause = " LIMIT ?";
|
|
15388
|
+
params.push(opts.limit);
|
|
15389
|
+
}
|
|
15390
|
+
const rows = d.query(`SELECT * FROM tasks WHERE ${conditions.join(" AND ")} ORDER BY archived_at DESC${limitClause}`).all(...params);
|
|
15391
|
+
return rows.map(rowToTask);
|
|
15392
|
+
}
|
|
14863
15393
|
function unarchiveTask(id, db) {
|
|
14864
15394
|
const d = db || getDatabase();
|
|
14865
15395
|
d.run("UPDATE tasks SET archived_at = NULL WHERE id = ?", [id]);
|
|
14866
|
-
return
|
|
15396
|
+
return getTask(id, d);
|
|
14867
15397
|
}
|
|
14868
15398
|
function getOverdueTasks(projectId, db) {
|
|
14869
15399
|
const d = db || getDatabase();
|
|
@@ -14878,6 +15408,80 @@ function getOverdueTasks(projectId, db) {
|
|
|
14878
15408
|
const rows = d.query(query).all(...params);
|
|
14879
15409
|
return rows.map(rowToTask);
|
|
14880
15410
|
}
|
|
15411
|
+
function notifyUpcomingDeadlines(opts = {}, db) {
|
|
15412
|
+
const d = db || getDatabase();
|
|
15413
|
+
const hours = opts.hours ?? 24;
|
|
15414
|
+
const start = new Date().toISOString();
|
|
15415
|
+
const end = new Date(Date.now() + hours * 60 * 60 * 1000).toISOString();
|
|
15416
|
+
const conditions = [
|
|
15417
|
+
"archived_at IS NULL",
|
|
15418
|
+
"due_at IS NOT NULL",
|
|
15419
|
+
"due_at >= ?",
|
|
15420
|
+
"due_at <= ?",
|
|
15421
|
+
"status NOT IN ('completed', 'cancelled', 'failed')"
|
|
15422
|
+
];
|
|
15423
|
+
const params = [start, end];
|
|
15424
|
+
if (opts.project_id) {
|
|
15425
|
+
conditions.push("project_id = ?");
|
|
15426
|
+
params.push(opts.project_id);
|
|
15427
|
+
}
|
|
15428
|
+
if (opts.agent_id) {
|
|
15429
|
+
conditions.push("assigned_to = ?");
|
|
15430
|
+
params.push(opts.agent_id);
|
|
15431
|
+
}
|
|
15432
|
+
const rows = d.query(`SELECT * FROM tasks WHERE ${conditions.join(" AND ")} ORDER BY due_at ASC`).all(...params);
|
|
15433
|
+
return rows.map(rowToTask);
|
|
15434
|
+
}
|
|
15435
|
+
function getBlockedTasks(projectId, db) {
|
|
15436
|
+
const d = db || getDatabase();
|
|
15437
|
+
const params = [];
|
|
15438
|
+
let projectClause = "";
|
|
15439
|
+
if (projectId) {
|
|
15440
|
+
projectClause = "AND t.project_id = ?";
|
|
15441
|
+
params.push(projectId);
|
|
15442
|
+
}
|
|
15443
|
+
const rows = d.query(`
|
|
15444
|
+
SELECT t.*, GROUP_CONCAT(dep.id) AS blocked_by_ids
|
|
15445
|
+
FROM tasks t
|
|
15446
|
+
JOIN task_dependencies td ON td.task_id = t.id
|
|
15447
|
+
JOIN tasks dep ON dep.id = td.depends_on
|
|
15448
|
+
WHERE t.archived_at IS NULL
|
|
15449
|
+
AND t.status NOT IN ('completed', 'cancelled', 'failed')
|
|
15450
|
+
AND dep.status NOT IN ('completed', 'cancelled')
|
|
15451
|
+
${projectClause}
|
|
15452
|
+
GROUP BY t.id
|
|
15453
|
+
ORDER BY t.priority DESC, t.created_at DESC
|
|
15454
|
+
`).all(...params);
|
|
15455
|
+
return rows.map((row) => ({
|
|
15456
|
+
...rowToTask(row),
|
|
15457
|
+
blocked_by: row.blocked_by_ids ? row.blocked_by_ids.split(",") : []
|
|
15458
|
+
}));
|
|
15459
|
+
}
|
|
15460
|
+
function getBlockingTasks(projectId, db) {
|
|
15461
|
+
const d = db || getDatabase();
|
|
15462
|
+
const params = [];
|
|
15463
|
+
let projectClause = "";
|
|
15464
|
+
if (projectId) {
|
|
15465
|
+
projectClause = "AND blocker.project_id = ?";
|
|
15466
|
+
params.push(projectId);
|
|
15467
|
+
}
|
|
15468
|
+
const rows = d.query(`
|
|
15469
|
+
SELECT blocker.*, COUNT(DISTINCT blocked.id) AS blocking_count
|
|
15470
|
+
FROM tasks blocker
|
|
15471
|
+
JOIN task_dependencies td ON td.depends_on = blocker.id
|
|
15472
|
+
JOIN tasks blocked ON blocked.id = td.task_id
|
|
15473
|
+
WHERE blocker.archived_at IS NULL
|
|
15474
|
+
AND blocker.status NOT IN ('completed', 'cancelled', 'failed')
|
|
15475
|
+
AND blocked.status NOT IN ('completed', 'cancelled', 'failed')
|
|
15476
|
+
${projectClause}
|
|
15477
|
+
GROUP BY blocker.id
|
|
15478
|
+
ORDER BY blocking_count DESC, blocker.created_at DESC
|
|
15479
|
+
`).all(...params);
|
|
15480
|
+
return rows.map((row) => ({
|
|
15481
|
+
...rowToTask(row),
|
|
15482
|
+
blocking_count: row.blocking_count
|
|
15483
|
+
}));
|
|
15484
|
+
}
|
|
14881
15485
|
function logTime(input, db) {
|
|
14882
15486
|
const d = db || getDatabase();
|
|
14883
15487
|
const id = uuid();
|
|
@@ -14958,6 +15562,7 @@ __export(exports_tasks, {
|
|
|
14958
15562
|
removeDependency: () => removeDependency,
|
|
14959
15563
|
redistributeStaleTasks: () => redistributeStaleTasks,
|
|
14960
15564
|
notifyWatchers: () => notifyWatchers,
|
|
15565
|
+
notifyUpcomingDeadlines: () => notifyUpcomingDeadlines,
|
|
14961
15566
|
moveTask: () => moveTask,
|
|
14962
15567
|
logTime: () => logTime,
|
|
14963
15568
|
logCost: () => logCost,
|
|
@@ -14973,12 +15578,15 @@ __export(exports_tasks, {
|
|
|
14973
15578
|
getTaskGraph: () => getTaskGraph,
|
|
14974
15579
|
getTaskDependents: () => getTaskDependents,
|
|
14975
15580
|
getTaskDependencies: () => getTaskDependencies,
|
|
14976
|
-
getTask: () =>
|
|
15581
|
+
getTask: () => getTask,
|
|
14977
15582
|
getStatus: () => getStatus,
|
|
14978
15583
|
getStaleTasks: () => getStaleTasks,
|
|
14979
15584
|
getOverdueTasks: () => getOverdueTasks,
|
|
14980
15585
|
getNextTask: () => getNextTask,
|
|
15586
|
+
getBlockingTasks: () => getBlockingTasks,
|
|
14981
15587
|
getBlockingDeps: () => getBlockingDeps,
|
|
15588
|
+
getBlockedTasks: () => getBlockedTasks,
|
|
15589
|
+
getArchivedTasks: () => getArchivedTasks,
|
|
14982
15590
|
getActiveWork: () => getActiveWork,
|
|
14983
15591
|
failTask: () => failTask,
|
|
14984
15592
|
deleteTask: () => deleteTask,
|
|
@@ -14990,8 +15598,10 @@ __export(exports_tasks, {
|
|
|
14990
15598
|
claimOrSteal: () => claimOrSteal,
|
|
14991
15599
|
claimNextTask: () => claimNextTask,
|
|
14992
15600
|
bulkUpdateTasks: () => bulkUpdateTasks,
|
|
15601
|
+
bulkDeleteTasks: () => bulkDeleteTasks,
|
|
14993
15602
|
bulkCreateTasks: () => bulkCreateTasks,
|
|
14994
15603
|
archiveTasks: () => archiveTasks,
|
|
15604
|
+
archiveCompletedTasks: () => archiveCompletedTasks,
|
|
14995
15605
|
addDependency: () => addDependency
|
|
14996
15606
|
});
|
|
14997
15607
|
var init_tasks = __esm(() => {
|
|
@@ -15169,54 +15779,459 @@ var init_dispatch = __esm(() => {
|
|
|
15169
15779
|
init_dispatch_formatter();
|
|
15170
15780
|
});
|
|
15171
15781
|
|
|
15172
|
-
// src/db/
|
|
15173
|
-
var
|
|
15174
|
-
__export(
|
|
15175
|
-
|
|
15176
|
-
|
|
15177
|
-
|
|
15178
|
-
|
|
15179
|
-
|
|
15180
|
-
|
|
15181
|
-
findTasksByFile: () => findTasksByFile,
|
|
15182
|
-
detectFileConflicts: () => detectFileConflicts,
|
|
15183
|
-
bulkFindTasksByFiles: () => bulkFindTasksByFiles,
|
|
15184
|
-
bulkAddTaskFiles: () => bulkAddTaskFiles,
|
|
15185
|
-
addTaskFile: () => addTaskFile
|
|
15782
|
+
// src/db/comments.ts
|
|
15783
|
+
var exports_comments = {};
|
|
15784
|
+
__export(exports_comments, {
|
|
15785
|
+
updateComment: () => updateComment,
|
|
15786
|
+
logProgress: () => logProgress,
|
|
15787
|
+
listComments: () => listComments,
|
|
15788
|
+
getComment: () => getComment,
|
|
15789
|
+
deleteComment: () => deleteComment,
|
|
15790
|
+
addComment: () => addComment
|
|
15186
15791
|
});
|
|
15187
|
-
function
|
|
15792
|
+
function addComment(input, db) {
|
|
15188
15793
|
const d = db || getDatabase();
|
|
15794
|
+
if (!getTask(input.task_id, d)) {
|
|
15795
|
+
throw new TaskNotFoundError(input.task_id);
|
|
15796
|
+
}
|
|
15189
15797
|
const id = uuid();
|
|
15190
15798
|
const timestamp = now();
|
|
15191
|
-
|
|
15192
|
-
|
|
15193
|
-
|
|
15194
|
-
|
|
15195
|
-
|
|
15196
|
-
|
|
15197
|
-
|
|
15198
|
-
|
|
15799
|
+
d.run(`INSERT INTO task_comments (id, task_id, agent_id, session_id, content, type, progress_pct, created_at)
|
|
15800
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
15801
|
+
id,
|
|
15802
|
+
input.task_id,
|
|
15803
|
+
input.agent_id || null,
|
|
15804
|
+
input.session_id || null,
|
|
15805
|
+
input.content,
|
|
15806
|
+
input.type || "comment",
|
|
15807
|
+
input.progress_pct ?? null,
|
|
15808
|
+
timestamp
|
|
15809
|
+
]);
|
|
15810
|
+
return getComment(id, d);
|
|
15199
15811
|
}
|
|
15200
|
-
function
|
|
15201
|
-
|
|
15202
|
-
return d.query("SELECT * FROM task_files WHERE id = ?").get(id);
|
|
15812
|
+
function logProgress(taskId, message, pct, agentId, db) {
|
|
15813
|
+
return addComment({ task_id: taskId, content: message, type: "progress", progress_pct: pct, agent_id: agentId }, db);
|
|
15203
15814
|
}
|
|
15204
|
-
function
|
|
15815
|
+
function getComment(id, db) {
|
|
15205
15816
|
const d = db || getDatabase();
|
|
15206
|
-
return d.query("SELECT * FROM
|
|
15817
|
+
return d.query("SELECT * FROM task_comments WHERE id = ?").get(id);
|
|
15207
15818
|
}
|
|
15208
|
-
function
|
|
15819
|
+
function listComments(taskId, db) {
|
|
15209
15820
|
const d = db || getDatabase();
|
|
15210
|
-
return d.query("SELECT * FROM
|
|
15821
|
+
return d.query("SELECT * FROM task_comments WHERE task_id = ? ORDER BY created_at").all(taskId);
|
|
15211
15822
|
}
|
|
15212
|
-
function
|
|
15823
|
+
function updateComment(id, input, db) {
|
|
15213
15824
|
const d = db || getDatabase();
|
|
15214
|
-
|
|
15215
|
-
|
|
15216
|
-
|
|
15217
|
-
|
|
15825
|
+
d.run("UPDATE task_comments SET content = ? WHERE id = ?", [input.content, id]);
|
|
15826
|
+
const comment = getComment(id, d);
|
|
15827
|
+
if (!comment) {
|
|
15828
|
+
throw new Error(`Comment not found: ${id}`);
|
|
15829
|
+
}
|
|
15830
|
+
return comment;
|
|
15218
15831
|
}
|
|
15219
|
-
function
|
|
15832
|
+
function deleteComment(id, db) {
|
|
15833
|
+
const d = db || getDatabase();
|
|
15834
|
+
const result = d.run("DELETE FROM task_comments WHERE id = ?", [id]);
|
|
15835
|
+
return result.changes > 0;
|
|
15836
|
+
}
|
|
15837
|
+
var init_comments = __esm(() => {
|
|
15838
|
+
init_types2();
|
|
15839
|
+
init_database();
|
|
15840
|
+
init_tasks();
|
|
15841
|
+
});
|
|
15842
|
+
|
|
15843
|
+
// src/lib/search.ts
|
|
15844
|
+
var exports_search = {};
|
|
15845
|
+
__export(exports_search, {
|
|
15846
|
+
searchTasks: () => searchTasks
|
|
15847
|
+
});
|
|
15848
|
+
function rowToTask2(row) {
|
|
15849
|
+
return {
|
|
15850
|
+
...row,
|
|
15851
|
+
tags: JSON.parse(row.tags || "[]"),
|
|
15852
|
+
metadata: JSON.parse(row.metadata || "{}"),
|
|
15853
|
+
status: row.status,
|
|
15854
|
+
priority: row.priority,
|
|
15855
|
+
requires_approval: Boolean(row.requires_approval)
|
|
15856
|
+
};
|
|
15857
|
+
}
|
|
15858
|
+
function hasFts(db) {
|
|
15859
|
+
try {
|
|
15860
|
+
const result = db.query("SELECT 1 FROM sqlite_master WHERE type='table' AND name='tasks_fts'").get();
|
|
15861
|
+
return result !== null;
|
|
15862
|
+
} catch {
|
|
15863
|
+
return false;
|
|
15864
|
+
}
|
|
15865
|
+
}
|
|
15866
|
+
function escapeFtsQuery(q) {
|
|
15867
|
+
return q.replace(/["*^()]/g, " ").trim().split(/\s+/).filter(Boolean).map((token) => `"${token}"*`).join(" ");
|
|
15868
|
+
}
|
|
15869
|
+
function searchTasks(options, projectId, taskListId, db) {
|
|
15870
|
+
const opts = typeof options === "string" ? { query: options || undefined, project_id: projectId, task_list_id: taskListId } : options;
|
|
15871
|
+
const d = db || getDatabase();
|
|
15872
|
+
clearExpiredLocks(d);
|
|
15873
|
+
const params = [];
|
|
15874
|
+
let sql;
|
|
15875
|
+
const raw = opts.query?.trim() ?? "";
|
|
15876
|
+
const q = raw === "*" ? "" : raw;
|
|
15877
|
+
if (hasFts(d) && q) {
|
|
15878
|
+
const ftsQuery = escapeFtsQuery(q);
|
|
15879
|
+
sql = `SELECT t.* FROM tasks t
|
|
15880
|
+
INNER JOIN tasks_fts fts ON fts.rowid = t.rowid
|
|
15881
|
+
WHERE tasks_fts MATCH ?`;
|
|
15882
|
+
params.push(ftsQuery);
|
|
15883
|
+
} else if (q) {
|
|
15884
|
+
const pattern = `%${q}%`;
|
|
15885
|
+
sql = `SELECT * FROM tasks t WHERE (t.title LIKE ? OR t.description LIKE ? OR EXISTS (SELECT 1 FROM task_tags WHERE task_tags.task_id = t.id AND tag LIKE ?))`;
|
|
15886
|
+
params.push(pattern, pattern, pattern);
|
|
15887
|
+
} else {
|
|
15888
|
+
sql = `SELECT * FROM tasks t WHERE 1=1`;
|
|
15889
|
+
}
|
|
15890
|
+
if (opts.project_id) {
|
|
15891
|
+
sql += " AND t.project_id = ?";
|
|
15892
|
+
params.push(opts.project_id);
|
|
15893
|
+
}
|
|
15894
|
+
if (opts.task_list_id) {
|
|
15895
|
+
sql += " AND t.task_list_id = ?";
|
|
15896
|
+
params.push(opts.task_list_id);
|
|
15897
|
+
}
|
|
15898
|
+
if (opts.status) {
|
|
15899
|
+
if (Array.isArray(opts.status)) {
|
|
15900
|
+
sql += ` AND t.status IN (${opts.status.map(() => "?").join(",")})`;
|
|
15901
|
+
params.push(...opts.status);
|
|
15902
|
+
} else {
|
|
15903
|
+
sql += " AND t.status = ?";
|
|
15904
|
+
params.push(opts.status);
|
|
15905
|
+
}
|
|
15906
|
+
}
|
|
15907
|
+
if (opts.priority) {
|
|
15908
|
+
if (Array.isArray(opts.priority)) {
|
|
15909
|
+
sql += ` AND t.priority IN (${opts.priority.map(() => "?").join(",")})`;
|
|
15910
|
+
params.push(...opts.priority);
|
|
15911
|
+
} else {
|
|
15912
|
+
sql += " AND t.priority = ?";
|
|
15913
|
+
params.push(opts.priority);
|
|
15914
|
+
}
|
|
15915
|
+
}
|
|
15916
|
+
if (opts.assigned_to) {
|
|
15917
|
+
sql += " AND t.assigned_to = ?";
|
|
15918
|
+
params.push(opts.assigned_to);
|
|
15919
|
+
}
|
|
15920
|
+
if (opts.agent_id) {
|
|
15921
|
+
sql += " AND t.agent_id = ?";
|
|
15922
|
+
params.push(opts.agent_id);
|
|
15923
|
+
}
|
|
15924
|
+
if (opts.created_after) {
|
|
15925
|
+
sql += " AND t.created_at > ?";
|
|
15926
|
+
params.push(opts.created_after);
|
|
15927
|
+
}
|
|
15928
|
+
if (opts.updated_after) {
|
|
15929
|
+
sql += " AND t.updated_at > ?";
|
|
15930
|
+
params.push(opts.updated_after);
|
|
15931
|
+
}
|
|
15932
|
+
if (opts.has_dependencies === true) {
|
|
15933
|
+
sql += " AND t.id IN (SELECT task_id FROM task_dependencies)";
|
|
15934
|
+
} else if (opts.has_dependencies === false) {
|
|
15935
|
+
sql += " AND t.id NOT IN (SELECT task_id FROM task_dependencies)";
|
|
15936
|
+
}
|
|
15937
|
+
if (opts.is_blocked === true) {
|
|
15938
|
+
sql += " AND t.id IN (SELECT td.task_id FROM task_dependencies td JOIN tasks dep ON dep.id = td.depends_on WHERE dep.status != 'completed')";
|
|
15939
|
+
} else if (opts.is_blocked === false) {
|
|
15940
|
+
sql += " AND t.id NOT IN (SELECT td.task_id FROM task_dependencies td JOIN tasks dep ON dep.id = td.depends_on WHERE dep.status != 'completed')";
|
|
15941
|
+
}
|
|
15942
|
+
if (hasFts(d) && q) {
|
|
15943
|
+
sql += ` ORDER BY bm25(tasks_fts),
|
|
15944
|
+
CASE t.priority WHEN 'critical' THEN 0 WHEN 'high' THEN 1 WHEN 'medium' THEN 2 WHEN 'low' THEN 3 END,
|
|
15945
|
+
t.created_at DESC`;
|
|
15946
|
+
} else {
|
|
15947
|
+
sql += ` ORDER BY
|
|
15948
|
+
CASE t.priority WHEN 'critical' THEN 0 WHEN 'high' THEN 1 WHEN 'medium' THEN 2 WHEN 'low' THEN 3 END,
|
|
15949
|
+
t.created_at DESC`;
|
|
15950
|
+
}
|
|
15951
|
+
const rows = d.query(sql).all(...params);
|
|
15952
|
+
return rows.map(rowToTask2);
|
|
15953
|
+
}
|
|
15954
|
+
var init_search = __esm(() => {
|
|
15955
|
+
init_database();
|
|
15956
|
+
});
|
|
15957
|
+
|
|
15958
|
+
// src/db/handoffs.ts
|
|
15959
|
+
var exports_handoffs = {};
|
|
15960
|
+
__export(exports_handoffs, {
|
|
15961
|
+
listHandoffs: () => listHandoffs,
|
|
15962
|
+
getLatestHandoff: () => getLatestHandoff,
|
|
15963
|
+
createHandoff: () => createHandoff
|
|
15964
|
+
});
|
|
15965
|
+
function createHandoff(input, db) {
|
|
15966
|
+
const d = db || getDatabase();
|
|
15967
|
+
const id = uuid();
|
|
15968
|
+
const timestamp = now();
|
|
15969
|
+
d.run(`INSERT INTO handoffs (id, agent_id, project_id, summary, completed, in_progress, blockers, next_steps, created_at)
|
|
15970
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
15971
|
+
id,
|
|
15972
|
+
input.agent_id || null,
|
|
15973
|
+
input.project_id || null,
|
|
15974
|
+
input.summary,
|
|
15975
|
+
input.completed ? JSON.stringify(input.completed) : null,
|
|
15976
|
+
input.in_progress ? JSON.stringify(input.in_progress) : null,
|
|
15977
|
+
input.blockers ? JSON.stringify(input.blockers) : null,
|
|
15978
|
+
input.next_steps ? JSON.stringify(input.next_steps) : null,
|
|
15979
|
+
timestamp
|
|
15980
|
+
]);
|
|
15981
|
+
return {
|
|
15982
|
+
id,
|
|
15983
|
+
agent_id: input.agent_id || null,
|
|
15984
|
+
project_id: input.project_id || null,
|
|
15985
|
+
summary: input.summary,
|
|
15986
|
+
completed: input.completed || null,
|
|
15987
|
+
in_progress: input.in_progress || null,
|
|
15988
|
+
blockers: input.blockers || null,
|
|
15989
|
+
next_steps: input.next_steps || null,
|
|
15990
|
+
created_at: timestamp
|
|
15991
|
+
};
|
|
15992
|
+
}
|
|
15993
|
+
function rowToHandoff(row) {
|
|
15994
|
+
return {
|
|
15995
|
+
...row,
|
|
15996
|
+
completed: row.completed ? JSON.parse(row.completed) : null,
|
|
15997
|
+
in_progress: row.in_progress ? JSON.parse(row.in_progress) : null,
|
|
15998
|
+
blockers: row.blockers ? JSON.parse(row.blockers) : null,
|
|
15999
|
+
next_steps: row.next_steps ? JSON.parse(row.next_steps) : null
|
|
16000
|
+
};
|
|
16001
|
+
}
|
|
16002
|
+
function listHandoffs(projectId, limit = 10, db) {
|
|
16003
|
+
const d = db || getDatabase();
|
|
16004
|
+
if (projectId) {
|
|
16005
|
+
return d.query("SELECT * FROM handoffs WHERE project_id = ? ORDER BY rowid DESC LIMIT ?").all(projectId, limit).map(rowToHandoff);
|
|
16006
|
+
}
|
|
16007
|
+
return d.query("SELECT * FROM handoffs ORDER BY rowid DESC LIMIT ?").all(limit).map(rowToHandoff);
|
|
16008
|
+
}
|
|
16009
|
+
function getLatestHandoff(agentId, projectId, db) {
|
|
16010
|
+
const d = db || getDatabase();
|
|
16011
|
+
let query = "SELECT * FROM handoffs WHERE 1=1";
|
|
16012
|
+
const params = [];
|
|
16013
|
+
if (agentId) {
|
|
16014
|
+
query += " AND agent_id = ?";
|
|
16015
|
+
params.push(agentId);
|
|
16016
|
+
}
|
|
16017
|
+
if (projectId) {
|
|
16018
|
+
query += " AND project_id = ?";
|
|
16019
|
+
params.push(projectId);
|
|
16020
|
+
}
|
|
16021
|
+
query += " ORDER BY rowid DESC LIMIT 1";
|
|
16022
|
+
const row = d.query(query).get(...params);
|
|
16023
|
+
return row ? rowToHandoff(row) : null;
|
|
16024
|
+
}
|
|
16025
|
+
var init_handoffs = __esm(() => {
|
|
16026
|
+
init_database();
|
|
16027
|
+
});
|
|
16028
|
+
|
|
16029
|
+
// src/lib/auto-assign.ts
|
|
16030
|
+
var exports_auto_assign = {};
|
|
16031
|
+
__export(exports_auto_assign, {
|
|
16032
|
+
findBestAgent: () => findBestAgent,
|
|
16033
|
+
autoAssignTask: () => autoAssignTask
|
|
16034
|
+
});
|
|
16035
|
+
function findBestAgent(_task, db) {
|
|
16036
|
+
const d = db || getDatabase();
|
|
16037
|
+
const agents = listAgents(d).filter((a) => (a.role || "agent") === "agent");
|
|
16038
|
+
if (agents.length === 0)
|
|
16039
|
+
return null;
|
|
16040
|
+
const inProgressTasks = listTasks({ status: "in_progress" }, d);
|
|
16041
|
+
const idToName = new Map;
|
|
16042
|
+
const load = new Map;
|
|
16043
|
+
for (const a of agents) {
|
|
16044
|
+
idToName.set(a.id, a.name);
|
|
16045
|
+
load.set(a.id, 0);
|
|
16046
|
+
}
|
|
16047
|
+
for (const t of inProgressTasks) {
|
|
16048
|
+
const agentId = t.assigned_to || t.agent_id;
|
|
16049
|
+
if (agentId && load.has(agentId)) {
|
|
16050
|
+
load.set(agentId, (load.get(agentId) || 0) + 1);
|
|
16051
|
+
}
|
|
16052
|
+
}
|
|
16053
|
+
let bestAgent = agents[0].name;
|
|
16054
|
+
let bestLoad = load.get(agents[0].id) ?? 0;
|
|
16055
|
+
for (const a of agents) {
|
|
16056
|
+
const l = load.get(a.id) ?? 0;
|
|
16057
|
+
if (l < bestLoad) {
|
|
16058
|
+
bestAgent = a.name;
|
|
16059
|
+
bestLoad = l;
|
|
16060
|
+
}
|
|
16061
|
+
}
|
|
16062
|
+
return bestAgent;
|
|
16063
|
+
}
|
|
16064
|
+
function getAgentWorkloads(d) {
|
|
16065
|
+
const rows = d.query("SELECT assigned_to, COUNT(*) as count FROM tasks WHERE status = 'in_progress' AND assigned_to IS NOT NULL GROUP BY assigned_to").all();
|
|
16066
|
+
return new Map(rows.map((r) => [r.assigned_to, r.count]));
|
|
16067
|
+
}
|
|
16068
|
+
function buildPrompt(task, agents) {
|
|
16069
|
+
const agentList = agents.map((a) => `- ${a.name} (role: ${a.role}, caps: [${a.capabilities.join(", ")}], active_tasks: ${a.in_progress_tasks})`).join(`
|
|
16070
|
+
`);
|
|
16071
|
+
return `You are a task routing assistant. Given a task and available agents, choose the SINGLE best agent.
|
|
16072
|
+
|
|
16073
|
+
TASK:
|
|
16074
|
+
Title: ${task.title}
|
|
16075
|
+
Priority: ${task.priority}
|
|
16076
|
+
Tags: ${task.tags.join(", ") || "none"}
|
|
16077
|
+
Description: ${task.description?.slice(0, 300) || "none"}
|
|
16078
|
+
|
|
16079
|
+
AVAILABLE AGENTS:
|
|
16080
|
+
${agentList}
|
|
16081
|
+
|
|
16082
|
+
Rules:
|
|
16083
|
+
- Match task tags/content to agent capabilities
|
|
16084
|
+
- Prefer agents with fewer active tasks
|
|
16085
|
+
- Prefer agents whose role fits the task (lead for critical, developer for features, qa for testing)
|
|
16086
|
+
- If no clear match, pick the agent with fewest active tasks
|
|
16087
|
+
|
|
16088
|
+
Respond with ONLY a JSON object: {"agent_name": "<name>", "reason": "<one sentence>"}`;
|
|
16089
|
+
}
|
|
16090
|
+
async function callCerebras(prompt, apiKey) {
|
|
16091
|
+
try {
|
|
16092
|
+
const resp = await fetch(CEREBRAS_API_URL, {
|
|
16093
|
+
method: "POST",
|
|
16094
|
+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` },
|
|
16095
|
+
body: JSON.stringify({
|
|
16096
|
+
model: CEREBRAS_MODEL,
|
|
16097
|
+
messages: [{ role: "user", content: prompt }],
|
|
16098
|
+
max_tokens: 150,
|
|
16099
|
+
temperature: 0
|
|
16100
|
+
}),
|
|
16101
|
+
signal: AbortSignal.timeout(1e4)
|
|
16102
|
+
});
|
|
16103
|
+
if (!resp.ok)
|
|
16104
|
+
return null;
|
|
16105
|
+
const data = await resp.json();
|
|
16106
|
+
const content = data?.choices?.[0]?.message?.content?.trim();
|
|
16107
|
+
if (!content)
|
|
16108
|
+
return null;
|
|
16109
|
+
const match = content.match(/\{[^}]+\}/s);
|
|
16110
|
+
if (!match)
|
|
16111
|
+
return null;
|
|
16112
|
+
return JSON.parse(match[0]);
|
|
16113
|
+
} catch {
|
|
16114
|
+
return null;
|
|
16115
|
+
}
|
|
16116
|
+
}
|
|
16117
|
+
async function autoAssignTask(taskId, db) {
|
|
16118
|
+
const d = db || getDatabase();
|
|
16119
|
+
const task = getTask(taskId, d);
|
|
16120
|
+
if (!task)
|
|
16121
|
+
throw new Error(`Task ${taskId} not found`);
|
|
16122
|
+
const agents = listAgents(d).filter((a) => a.status === "active");
|
|
16123
|
+
if (agents.length === 0) {
|
|
16124
|
+
return { task_id: taskId, assigned_to: null, agent_name: null, method: "no_agents" };
|
|
16125
|
+
}
|
|
16126
|
+
const workloads = getAgentWorkloads(d);
|
|
16127
|
+
const apiKey = process.env["CEREBRAS_API_KEY"];
|
|
16128
|
+
let selectedAgent = null;
|
|
16129
|
+
let method = "capability_match";
|
|
16130
|
+
let reason;
|
|
16131
|
+
if (apiKey) {
|
|
16132
|
+
const agentData = agents.map((a) => ({
|
|
16133
|
+
id: a.id,
|
|
16134
|
+
name: a.name,
|
|
16135
|
+
role: a.role || "agent",
|
|
16136
|
+
capabilities: a.capabilities || [],
|
|
16137
|
+
in_progress_tasks: workloads.get(a.id) ?? 0
|
|
16138
|
+
}));
|
|
16139
|
+
const result = await callCerebras(buildPrompt({
|
|
16140
|
+
title: task.title,
|
|
16141
|
+
description: task.description,
|
|
16142
|
+
priority: task.priority,
|
|
16143
|
+
tags: task.tags || []
|
|
16144
|
+
}, agentData), apiKey);
|
|
16145
|
+
if (result?.agent_name) {
|
|
16146
|
+
selectedAgent = agents.find((a) => a.name === result.agent_name) ?? null;
|
|
16147
|
+
if (selectedAgent) {
|
|
16148
|
+
method = "cerebras";
|
|
16149
|
+
reason = result.reason;
|
|
16150
|
+
}
|
|
16151
|
+
}
|
|
16152
|
+
}
|
|
16153
|
+
if (!selectedAgent) {
|
|
16154
|
+
const taskTags = task.tags || [];
|
|
16155
|
+
const capable = getCapableAgents(taskTags, { min_score: 0, limit: 10 }, d);
|
|
16156
|
+
if (capable.length > 0) {
|
|
16157
|
+
const sorted = capable.sort((a, b) => {
|
|
16158
|
+
if (b.score !== a.score)
|
|
16159
|
+
return b.score - a.score;
|
|
16160
|
+
return (workloads.get(a.agent.id) ?? 0) - (workloads.get(b.agent.id) ?? 0);
|
|
16161
|
+
});
|
|
16162
|
+
selectedAgent = sorted[0].agent;
|
|
16163
|
+
reason = `Capability match (score: ${sorted[0].score.toFixed(2)})`;
|
|
16164
|
+
} else {
|
|
16165
|
+
selectedAgent = agents.slice().sort((a, b) => (workloads.get(a.id) ?? 0) - (workloads.get(b.id) ?? 0))[0];
|
|
16166
|
+
reason = `Least busy agent (${workloads.get(selectedAgent.id) ?? 0} active tasks)`;
|
|
16167
|
+
}
|
|
16168
|
+
}
|
|
16169
|
+
if (selectedAgent) {
|
|
16170
|
+
updateTask(taskId, { assigned_to: selectedAgent.id, version: task.version }, d);
|
|
16171
|
+
}
|
|
16172
|
+
return {
|
|
16173
|
+
task_id: taskId,
|
|
16174
|
+
assigned_to: selectedAgent?.id ?? null,
|
|
16175
|
+
agent_name: selectedAgent?.name ?? null,
|
|
16176
|
+
method,
|
|
16177
|
+
reason
|
|
16178
|
+
};
|
|
16179
|
+
}
|
|
16180
|
+
var CEREBRAS_API_URL = "https://api.cerebras.ai/v1/chat/completions", CEREBRAS_MODEL = "llama-3.3-70b";
|
|
16181
|
+
var init_auto_assign = __esm(() => {
|
|
16182
|
+
init_database();
|
|
16183
|
+
init_tasks();
|
|
16184
|
+
init_agents();
|
|
16185
|
+
});
|
|
16186
|
+
|
|
16187
|
+
// src/db/task-files.ts
|
|
16188
|
+
var exports_task_files = {};
|
|
16189
|
+
__export(exports_task_files, {
|
|
16190
|
+
updateTaskFileStatus: () => updateTaskFileStatus,
|
|
16191
|
+
removeTaskFile: () => removeTaskFile,
|
|
16192
|
+
listTaskFiles: () => listTaskFiles,
|
|
16193
|
+
listActiveFiles: () => listActiveFiles,
|
|
16194
|
+
getTaskFile: () => getTaskFile,
|
|
16195
|
+
getFileHeatMap: () => getFileHeatMap,
|
|
16196
|
+
findTasksByFile: () => findTasksByFile,
|
|
16197
|
+
detectFileConflicts: () => detectFileConflicts,
|
|
16198
|
+
bulkFindTasksByFiles: () => bulkFindTasksByFiles,
|
|
16199
|
+
bulkAddTaskFiles: () => bulkAddTaskFiles,
|
|
16200
|
+
addTaskFile: () => addTaskFile
|
|
16201
|
+
});
|
|
16202
|
+
function addTaskFile(input, db) {
|
|
16203
|
+
const d = db || getDatabase();
|
|
16204
|
+
const id = uuid();
|
|
16205
|
+
const timestamp = now();
|
|
16206
|
+
const existing = d.query("SELECT id FROM task_files WHERE task_id = ? AND path = ?").get(input.task_id, input.path);
|
|
16207
|
+
if (existing) {
|
|
16208
|
+
d.run("UPDATE task_files SET status = ?, agent_id = ?, note = ?, updated_at = ? WHERE id = ?", [input.status || "active", input.agent_id || null, input.note || null, timestamp, existing.id]);
|
|
16209
|
+
return getTaskFile(existing.id, d);
|
|
16210
|
+
}
|
|
16211
|
+
d.run(`INSERT INTO task_files (id, task_id, path, status, agent_id, note, created_at, updated_at)
|
|
16212
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [id, input.task_id, input.path, input.status || "active", input.agent_id || null, input.note || null, timestamp, timestamp]);
|
|
16213
|
+
return getTaskFile(id, d);
|
|
16214
|
+
}
|
|
16215
|
+
function getTaskFile(id, db) {
|
|
16216
|
+
const d = db || getDatabase();
|
|
16217
|
+
return d.query("SELECT * FROM task_files WHERE id = ?").get(id);
|
|
16218
|
+
}
|
|
16219
|
+
function listTaskFiles(taskId, db) {
|
|
16220
|
+
const d = db || getDatabase();
|
|
16221
|
+
return d.query("SELECT * FROM task_files WHERE task_id = ? ORDER BY path").all(taskId);
|
|
16222
|
+
}
|
|
16223
|
+
function findTasksByFile(path, db) {
|
|
16224
|
+
const d = db || getDatabase();
|
|
16225
|
+
return d.query("SELECT * FROM task_files WHERE path = ? AND status != 'removed' ORDER BY updated_at DESC").all(path);
|
|
16226
|
+
}
|
|
16227
|
+
function updateTaskFileStatus(taskId, path, status, agentId, db) {
|
|
16228
|
+
const d = db || getDatabase();
|
|
16229
|
+
const timestamp = now();
|
|
16230
|
+
d.run("UPDATE task_files SET status = ?, agent_id = COALESCE(?, agent_id), updated_at = ? WHERE task_id = ? AND path = ?", [status, agentId || null, timestamp, taskId, path]);
|
|
16231
|
+
const row = d.query("SELECT * FROM task_files WHERE task_id = ? AND path = ?").get(taskId, path);
|
|
16232
|
+
return row;
|
|
16233
|
+
}
|
|
16234
|
+
function removeTaskFile(taskId, path, db) {
|
|
15220
16235
|
const d = db || getDatabase();
|
|
15221
16236
|
const result = d.run("DELETE FROM task_files WHERE task_id = ? AND path = ?", [taskId, path]);
|
|
15222
16237
|
return result.changes > 0;
|
|
@@ -15451,14 +16466,653 @@ function autoDetectFileRelationships(taskId, db) {
|
|
|
15451
16466
|
var RELATIONSHIP_TYPES;
|
|
15452
16467
|
var init_task_relationships = __esm(() => {
|
|
15453
16468
|
init_database();
|
|
15454
|
-
RELATIONSHIP_TYPES = [
|
|
15455
|
-
"related_to",
|
|
15456
|
-
"conflicts_with",
|
|
15457
|
-
"similar_to",
|
|
15458
|
-
"duplicates",
|
|
15459
|
-
"supersedes",
|
|
15460
|
-
"modifies_same_file"
|
|
15461
|
-
];
|
|
16469
|
+
RELATIONSHIP_TYPES = [
|
|
16470
|
+
"related_to",
|
|
16471
|
+
"conflicts_with",
|
|
16472
|
+
"similar_to",
|
|
16473
|
+
"duplicates",
|
|
16474
|
+
"supersedes",
|
|
16475
|
+
"modifies_same_file"
|
|
16476
|
+
];
|
|
16477
|
+
});
|
|
16478
|
+
|
|
16479
|
+
// src/db/task-commits.ts
|
|
16480
|
+
var exports_task_commits = {};
|
|
16481
|
+
__export(exports_task_commits, {
|
|
16482
|
+
unlinkTaskCommit: () => unlinkTaskCommit,
|
|
16483
|
+
linkTaskToCommit: () => linkTaskToCommit,
|
|
16484
|
+
getTaskCommits: () => getTaskCommits,
|
|
16485
|
+
findTaskByCommit: () => findTaskByCommit
|
|
16486
|
+
});
|
|
16487
|
+
function rowToCommit(row) {
|
|
16488
|
+
return {
|
|
16489
|
+
...row,
|
|
16490
|
+
files_changed: row.files_changed ? JSON.parse(row.files_changed) : null
|
|
16491
|
+
};
|
|
16492
|
+
}
|
|
16493
|
+
function linkTaskToCommit(input, db) {
|
|
16494
|
+
const d = db || getDatabase();
|
|
16495
|
+
const existing = d.query("SELECT * FROM task_commits WHERE task_id = ? AND sha = ?").get(input.task_id, input.sha);
|
|
16496
|
+
if (existing) {
|
|
16497
|
+
d.run("UPDATE task_commits SET message = COALESCE(?, message), author = COALESCE(?, author), files_changed = COALESCE(?, files_changed), committed_at = COALESCE(?, committed_at) WHERE id = ?", [input.message ?? null, input.author ?? null, input.files_changed ? JSON.stringify(input.files_changed) : null, input.committed_at ?? null, existing.id]);
|
|
16498
|
+
return rowToCommit(d.query("SELECT * FROM task_commits WHERE id = ?").get(existing.id));
|
|
16499
|
+
}
|
|
16500
|
+
const id = uuid();
|
|
16501
|
+
d.run("INSERT INTO task_commits (id, task_id, sha, message, author, files_changed, committed_at, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", [id, input.task_id, input.sha, input.message ?? null, input.author ?? null, input.files_changed ? JSON.stringify(input.files_changed) : null, input.committed_at ?? null, now()]);
|
|
16502
|
+
return rowToCommit(d.query("SELECT * FROM task_commits WHERE id = ?").get(id));
|
|
16503
|
+
}
|
|
16504
|
+
function getTaskCommits(taskId, db) {
|
|
16505
|
+
const d = db || getDatabase();
|
|
16506
|
+
return d.query("SELECT * FROM task_commits WHERE task_id = ? ORDER BY committed_at DESC, created_at DESC").all(taskId).map(rowToCommit);
|
|
16507
|
+
}
|
|
16508
|
+
function findTaskByCommit(sha, db) {
|
|
16509
|
+
const d = db || getDatabase();
|
|
16510
|
+
const row = d.query("SELECT * FROM task_commits WHERE sha = ? OR sha LIKE ? LIMIT 1").get(sha, `${sha}%`);
|
|
16511
|
+
if (!row)
|
|
16512
|
+
return null;
|
|
16513
|
+
return { task_id: row.task_id, commit: rowToCommit(row) };
|
|
16514
|
+
}
|
|
16515
|
+
function unlinkTaskCommit(taskId, sha, db) {
|
|
16516
|
+
const d = db || getDatabase();
|
|
16517
|
+
return d.run("DELETE FROM task_commits WHERE task_id = ? AND (sha = ? OR sha LIKE ?)", [taskId, sha, `${sha}%`]).changes > 0;
|
|
16518
|
+
}
|
|
16519
|
+
var init_task_commits = __esm(() => {
|
|
16520
|
+
init_database();
|
|
16521
|
+
});
|
|
16522
|
+
|
|
16523
|
+
// src/db/file-locks.ts
|
|
16524
|
+
var exports_file_locks = {};
|
|
16525
|
+
__export(exports_file_locks, {
|
|
16526
|
+
unlockFile: () => unlockFile,
|
|
16527
|
+
lockFile: () => lockFile,
|
|
16528
|
+
listFileLocks: () => listFileLocks,
|
|
16529
|
+
forceUnlockFile: () => forceUnlockFile,
|
|
16530
|
+
cleanExpiredFileLocks: () => cleanExpiredFileLocks,
|
|
16531
|
+
checkFileLock: () => checkFileLock,
|
|
16532
|
+
FILE_LOCK_DEFAULT_TTL_SECONDS: () => FILE_LOCK_DEFAULT_TTL_SECONDS
|
|
16533
|
+
});
|
|
16534
|
+
function expiresAt(ttlSeconds) {
|
|
16535
|
+
return new Date(Date.now() + ttlSeconds * 1000).toISOString();
|
|
16536
|
+
}
|
|
16537
|
+
function cleanExpiredFileLocks(db) {
|
|
16538
|
+
const d = db || getDatabase();
|
|
16539
|
+
const result = d.run("DELETE FROM file_locks WHERE expires_at <= ?", [now()]);
|
|
16540
|
+
return result.changes;
|
|
16541
|
+
}
|
|
16542
|
+
function lockFile(input, db) {
|
|
16543
|
+
const d = db || getDatabase();
|
|
16544
|
+
const ttl = input.ttl_seconds ?? FILE_LOCK_DEFAULT_TTL_SECONDS;
|
|
16545
|
+
const expiry = expiresAt(ttl);
|
|
16546
|
+
const timestamp = now();
|
|
16547
|
+
cleanExpiredFileLocks(d);
|
|
16548
|
+
const existing = d.query("SELECT * FROM file_locks WHERE path = ?").get(input.path);
|
|
16549
|
+
if (existing) {
|
|
16550
|
+
if (existing.agent_id === input.agent_id) {
|
|
16551
|
+
d.run("UPDATE file_locks SET expires_at = ?, task_id = COALESCE(?, task_id) WHERE id = ?", [expiry, input.task_id ?? null, existing.id]);
|
|
16552
|
+
return d.query("SELECT * FROM file_locks WHERE id = ?").get(existing.id);
|
|
16553
|
+
}
|
|
16554
|
+
throw new LockError(input.path, existing.agent_id);
|
|
16555
|
+
}
|
|
16556
|
+
const id = uuid();
|
|
16557
|
+
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]);
|
|
16558
|
+
return d.query("SELECT * FROM file_locks WHERE id = ?").get(id);
|
|
16559
|
+
}
|
|
16560
|
+
function unlockFile(path, agentId, db) {
|
|
16561
|
+
const d = db || getDatabase();
|
|
16562
|
+
cleanExpiredFileLocks(d);
|
|
16563
|
+
const result = d.run("DELETE FROM file_locks WHERE path = ? AND agent_id = ?", [path, agentId]);
|
|
16564
|
+
return result.changes > 0;
|
|
16565
|
+
}
|
|
16566
|
+
function checkFileLock(path, db) {
|
|
16567
|
+
const d = db || getDatabase();
|
|
16568
|
+
cleanExpiredFileLocks(d);
|
|
16569
|
+
return d.query("SELECT * FROM file_locks WHERE path = ?").get(path);
|
|
16570
|
+
}
|
|
16571
|
+
function listFileLocks(agentId, db) {
|
|
16572
|
+
const d = db || getDatabase();
|
|
16573
|
+
cleanExpiredFileLocks(d);
|
|
16574
|
+
if (agentId) {
|
|
16575
|
+
return d.query("SELECT * FROM file_locks WHERE agent_id = ? ORDER BY created_at DESC").all(agentId);
|
|
16576
|
+
}
|
|
16577
|
+
return d.query("SELECT * FROM file_locks ORDER BY created_at DESC").all();
|
|
16578
|
+
}
|
|
16579
|
+
function forceUnlockFile(path, db) {
|
|
16580
|
+
const d = db || getDatabase();
|
|
16581
|
+
const result = d.run("DELETE FROM file_locks WHERE path = ?", [path]);
|
|
16582
|
+
return result.changes > 0;
|
|
16583
|
+
}
|
|
16584
|
+
var FILE_LOCK_DEFAULT_TTL_SECONDS;
|
|
16585
|
+
var init_file_locks = __esm(() => {
|
|
16586
|
+
init_database();
|
|
16587
|
+
init_types2();
|
|
16588
|
+
FILE_LOCK_DEFAULT_TTL_SECONDS = 30 * 60;
|
|
16589
|
+
});
|
|
16590
|
+
|
|
16591
|
+
// src/db/kg.ts
|
|
16592
|
+
var exports_kg = {};
|
|
16593
|
+
__export(exports_kg, {
|
|
16594
|
+
syncKgEdges: () => syncKgEdges,
|
|
16595
|
+
removeKgEdges: () => removeKgEdges,
|
|
16596
|
+
getRelated: () => getRelated,
|
|
16597
|
+
getImpactAnalysis: () => getImpactAnalysis,
|
|
16598
|
+
getCriticalPath: () => getCriticalPath,
|
|
16599
|
+
findPath: () => findPath,
|
|
16600
|
+
addKgEdge: () => addKgEdge
|
|
16601
|
+
});
|
|
16602
|
+
function rowToEdge(row) {
|
|
16603
|
+
return {
|
|
16604
|
+
...row,
|
|
16605
|
+
metadata: JSON.parse(row.metadata || "{}")
|
|
16606
|
+
};
|
|
16607
|
+
}
|
|
16608
|
+
function syncKgEdges(db) {
|
|
16609
|
+
const d = db || getDatabase();
|
|
16610
|
+
let synced = 0;
|
|
16611
|
+
const tx = d.transaction(() => {
|
|
16612
|
+
const deps = d.query("SELECT task_id, depends_on FROM task_dependencies").all();
|
|
16613
|
+
for (const dep of deps) {
|
|
16614
|
+
synced += upsertEdge(d, dep.task_id, "task", dep.depends_on, "task", "depends_on");
|
|
16615
|
+
}
|
|
16616
|
+
const assignments = d.query("SELECT id, assigned_to FROM tasks WHERE assigned_to IS NOT NULL").all();
|
|
16617
|
+
for (const a of assignments) {
|
|
16618
|
+
synced += upsertEdge(d, a.id, "task", a.assigned_to, "agent", "assigned_to");
|
|
16619
|
+
}
|
|
16620
|
+
const agents = d.query("SELECT id, reports_to FROM agents WHERE reports_to IS NOT NULL").all();
|
|
16621
|
+
for (const a of agents) {
|
|
16622
|
+
synced += upsertEdge(d, a.id, "agent", a.reports_to, "agent", "reports_to");
|
|
16623
|
+
}
|
|
16624
|
+
const files = d.query("SELECT task_id, path FROM task_files WHERE status != 'removed'").all();
|
|
16625
|
+
for (const f of files) {
|
|
16626
|
+
synced += upsertEdge(d, f.task_id, "task", f.path, "file", "references_file");
|
|
16627
|
+
}
|
|
16628
|
+
const taskProjects = d.query("SELECT id, project_id FROM tasks WHERE project_id IS NOT NULL").all();
|
|
16629
|
+
for (const tp of taskProjects) {
|
|
16630
|
+
synced += upsertEdge(d, tp.id, "task", tp.project_id, "project", "in_project");
|
|
16631
|
+
}
|
|
16632
|
+
const taskPlans = d.query("SELECT id, plan_id FROM tasks WHERE plan_id IS NOT NULL").all();
|
|
16633
|
+
for (const tp of taskPlans) {
|
|
16634
|
+
synced += upsertEdge(d, tp.id, "task", tp.plan_id, "plan", "in_plan");
|
|
16635
|
+
}
|
|
16636
|
+
try {
|
|
16637
|
+
const rels = d.query("SELECT source_task_id, target_task_id, relationship_type FROM task_relationships").all();
|
|
16638
|
+
for (const r of rels) {
|
|
16639
|
+
synced += upsertEdge(d, r.source_task_id, "task", r.target_task_id, "task", r.relationship_type);
|
|
16640
|
+
}
|
|
16641
|
+
} catch {}
|
|
16642
|
+
});
|
|
16643
|
+
tx();
|
|
16644
|
+
return { synced };
|
|
16645
|
+
}
|
|
16646
|
+
function upsertEdge(d, sourceId, sourceType, targetId, targetType, relationType, weight = 1) {
|
|
16647
|
+
try {
|
|
16648
|
+
d.run(`INSERT OR IGNORE INTO kg_edges (id, source_id, source_type, target_id, target_type, relation_type, weight, metadata, created_at)
|
|
16649
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, '{}', ?)`, [uuid(), sourceId, sourceType, targetId, targetType, relationType, weight, now()]);
|
|
16650
|
+
return 1;
|
|
16651
|
+
} catch {
|
|
16652
|
+
return 0;
|
|
16653
|
+
}
|
|
16654
|
+
}
|
|
16655
|
+
function getRelated(entityId, opts, db) {
|
|
16656
|
+
const d = db || getDatabase();
|
|
16657
|
+
const direction = opts?.direction || "both";
|
|
16658
|
+
const conditions = [];
|
|
16659
|
+
const params = [];
|
|
16660
|
+
if (direction === "outgoing" || direction === "both") {
|
|
16661
|
+
conditions.push("source_id = ?");
|
|
16662
|
+
params.push(entityId);
|
|
16663
|
+
}
|
|
16664
|
+
if (direction === "incoming" || direction === "both") {
|
|
16665
|
+
conditions.push("target_id = ?");
|
|
16666
|
+
params.push(entityId);
|
|
16667
|
+
}
|
|
16668
|
+
let sql = `SELECT * FROM kg_edges WHERE (${conditions.join(" OR ")})`;
|
|
16669
|
+
if (opts?.relation_type) {
|
|
16670
|
+
sql += " AND relation_type = ?";
|
|
16671
|
+
params.push(opts.relation_type);
|
|
16672
|
+
}
|
|
16673
|
+
if (opts?.entity_type) {
|
|
16674
|
+
sql += " AND (source_type = ? OR target_type = ?)";
|
|
16675
|
+
params.push(opts.entity_type, opts.entity_type);
|
|
16676
|
+
}
|
|
16677
|
+
sql += " ORDER BY weight DESC, created_at DESC";
|
|
16678
|
+
if (opts?.limit) {
|
|
16679
|
+
sql += " LIMIT ?";
|
|
16680
|
+
params.push(opts.limit);
|
|
16681
|
+
}
|
|
16682
|
+
return d.query(sql).all(...params).map(rowToEdge);
|
|
16683
|
+
}
|
|
16684
|
+
function findPath(sourceId, targetId, opts, db) {
|
|
16685
|
+
const d = db || getDatabase();
|
|
16686
|
+
const maxDepth = opts?.max_depth || 5;
|
|
16687
|
+
const visited = new Set;
|
|
16688
|
+
const queue = [{ id: sourceId, path: [] }];
|
|
16689
|
+
const results = [];
|
|
16690
|
+
visited.add(sourceId);
|
|
16691
|
+
while (queue.length > 0) {
|
|
16692
|
+
const current = queue.shift();
|
|
16693
|
+
if (current.path.length >= maxDepth)
|
|
16694
|
+
continue;
|
|
16695
|
+
let sql = "SELECT * FROM kg_edges WHERE source_id = ?";
|
|
16696
|
+
const params = [current.id];
|
|
16697
|
+
if (opts?.relation_types && opts.relation_types.length > 0) {
|
|
16698
|
+
const placeholders = opts.relation_types.map(() => "?").join(",");
|
|
16699
|
+
sql += ` AND relation_type IN (${placeholders})`;
|
|
16700
|
+
params.push(...opts.relation_types);
|
|
16701
|
+
}
|
|
16702
|
+
const edges = d.query(sql).all(...params).map(rowToEdge);
|
|
16703
|
+
for (const edge of edges) {
|
|
16704
|
+
const nextId = edge.target_id;
|
|
16705
|
+
const newPath = [...current.path, edge];
|
|
16706
|
+
if (nextId === targetId) {
|
|
16707
|
+
results.push(newPath);
|
|
16708
|
+
if (results.length >= 3)
|
|
16709
|
+
return results;
|
|
16710
|
+
continue;
|
|
16711
|
+
}
|
|
16712
|
+
if (!visited.has(nextId)) {
|
|
16713
|
+
visited.add(nextId);
|
|
16714
|
+
queue.push({ id: nextId, path: newPath });
|
|
16715
|
+
}
|
|
16716
|
+
}
|
|
16717
|
+
}
|
|
16718
|
+
return results;
|
|
16719
|
+
}
|
|
16720
|
+
function getImpactAnalysis(entityId, opts, db) {
|
|
16721
|
+
const d = db || getDatabase();
|
|
16722
|
+
const maxDepth = opts?.max_depth || 3;
|
|
16723
|
+
const results = [];
|
|
16724
|
+
const visited = new Set;
|
|
16725
|
+
visited.add(entityId);
|
|
16726
|
+
const queue = [{ id: entityId, depth: 0 }];
|
|
16727
|
+
while (queue.length > 0) {
|
|
16728
|
+
const current = queue.shift();
|
|
16729
|
+
if (current.depth >= maxDepth)
|
|
16730
|
+
continue;
|
|
16731
|
+
let sql = "SELECT * FROM kg_edges WHERE source_id = ?";
|
|
16732
|
+
const params = [current.id];
|
|
16733
|
+
if (opts?.relation_types && opts.relation_types.length > 0) {
|
|
16734
|
+
const placeholders = opts.relation_types.map(() => "?").join(",");
|
|
16735
|
+
sql += ` AND relation_type IN (${placeholders})`;
|
|
16736
|
+
params.push(...opts.relation_types);
|
|
16737
|
+
}
|
|
16738
|
+
const edges = d.query(sql).all(...params).map(rowToEdge);
|
|
16739
|
+
for (const edge of edges) {
|
|
16740
|
+
if (!visited.has(edge.target_id)) {
|
|
16741
|
+
visited.add(edge.target_id);
|
|
16742
|
+
results.push({
|
|
16743
|
+
entity_id: edge.target_id,
|
|
16744
|
+
entity_type: edge.target_type,
|
|
16745
|
+
depth: current.depth + 1,
|
|
16746
|
+
relation: edge.relation_type
|
|
16747
|
+
});
|
|
16748
|
+
queue.push({ id: edge.target_id, depth: current.depth + 1 });
|
|
16749
|
+
}
|
|
16750
|
+
}
|
|
16751
|
+
}
|
|
16752
|
+
return results;
|
|
16753
|
+
}
|
|
16754
|
+
function getCriticalPath(opts, db) {
|
|
16755
|
+
const d = db || getDatabase();
|
|
16756
|
+
let sql = `SELECT source_id, target_id FROM kg_edges WHERE relation_type = 'depends_on'`;
|
|
16757
|
+
const params = [];
|
|
16758
|
+
if (opts?.project_id) {
|
|
16759
|
+
sql += ` AND source_id IN (SELECT id FROM tasks WHERE project_id = ?)`;
|
|
16760
|
+
params.push(opts.project_id);
|
|
16761
|
+
}
|
|
16762
|
+
const edges = d.query(sql).all(...params);
|
|
16763
|
+
const blocks = new Map;
|
|
16764
|
+
for (const e of edges) {
|
|
16765
|
+
if (!blocks.has(e.target_id))
|
|
16766
|
+
blocks.set(e.target_id, new Set);
|
|
16767
|
+
blocks.get(e.target_id).add(e.source_id);
|
|
16768
|
+
}
|
|
16769
|
+
const results = [];
|
|
16770
|
+
for (const [taskId] of blocks) {
|
|
16771
|
+
const visited = new Set;
|
|
16772
|
+
const q = [taskId];
|
|
16773
|
+
let maxDepth = 0;
|
|
16774
|
+
let depth = 0;
|
|
16775
|
+
let levelSize = q.length;
|
|
16776
|
+
while (q.length > 0) {
|
|
16777
|
+
const node = q.shift();
|
|
16778
|
+
levelSize--;
|
|
16779
|
+
const downstream = blocks.get(node);
|
|
16780
|
+
if (downstream) {
|
|
16781
|
+
for (const d2 of downstream) {
|
|
16782
|
+
if (!visited.has(d2)) {
|
|
16783
|
+
visited.add(d2);
|
|
16784
|
+
q.push(d2);
|
|
16785
|
+
}
|
|
16786
|
+
}
|
|
16787
|
+
}
|
|
16788
|
+
if (levelSize === 0) {
|
|
16789
|
+
depth++;
|
|
16790
|
+
maxDepth = Math.max(maxDepth, depth);
|
|
16791
|
+
levelSize = q.length;
|
|
16792
|
+
}
|
|
16793
|
+
}
|
|
16794
|
+
if (visited.size > 0) {
|
|
16795
|
+
results.push({ task_id: taskId, blocking_count: visited.size, depth: maxDepth });
|
|
16796
|
+
}
|
|
16797
|
+
}
|
|
16798
|
+
results.sort((a, b) => b.blocking_count - a.blocking_count);
|
|
16799
|
+
return results.slice(0, opts?.limit || 20);
|
|
16800
|
+
}
|
|
16801
|
+
function addKgEdge(sourceId, sourceType, targetId, targetType, relationType, weight = 1, metadata, db) {
|
|
16802
|
+
const d = db || getDatabase();
|
|
16803
|
+
const id = uuid();
|
|
16804
|
+
const timestamp = now();
|
|
16805
|
+
d.run(`INSERT OR IGNORE INTO kg_edges (id, source_id, source_type, target_id, target_type, relation_type, weight, metadata, created_at)
|
|
16806
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [id, sourceId, sourceType, targetId, targetType, relationType, weight, JSON.stringify(metadata || {}), timestamp]);
|
|
16807
|
+
return { id, source_id: sourceId, source_type: sourceType, target_id: targetId, target_type: targetType, relation_type: relationType, weight, metadata: metadata || {}, created_at: timestamp };
|
|
16808
|
+
}
|
|
16809
|
+
function removeKgEdges(sourceId, targetId, relationType, db) {
|
|
16810
|
+
const d = db || getDatabase();
|
|
16811
|
+
if (relationType) {
|
|
16812
|
+
return d.run("DELETE FROM kg_edges WHERE source_id = ? AND target_id = ? AND relation_type = ?", [sourceId, targetId, relationType]).changes;
|
|
16813
|
+
}
|
|
16814
|
+
return d.run("DELETE FROM kg_edges WHERE source_id = ? AND target_id = ?", [sourceId, targetId]).changes;
|
|
16815
|
+
}
|
|
16816
|
+
var init_kg = __esm(() => {
|
|
16817
|
+
init_database();
|
|
16818
|
+
});
|
|
16819
|
+
|
|
16820
|
+
// src/db/project-agent-roles.ts
|
|
16821
|
+
var exports_project_agent_roles = {};
|
|
16822
|
+
__export(exports_project_agent_roles, {
|
|
16823
|
+
setProjectAgentRole: () => setProjectAgentRole,
|
|
16824
|
+
removeProjectAgentRole: () => removeProjectAgentRole,
|
|
16825
|
+
listProjectAgentRoles: () => listProjectAgentRoles,
|
|
16826
|
+
getProjectOrgChart: () => getProjectOrgChart,
|
|
16827
|
+
getAgentProjectRoles: () => getAgentProjectRoles
|
|
16828
|
+
});
|
|
16829
|
+
function rowToRole(row) {
|
|
16830
|
+
return { ...row, is_lead: row.is_lead === 1 };
|
|
16831
|
+
}
|
|
16832
|
+
function setProjectAgentRole(projectId, agentId, role, isLead = false, db) {
|
|
16833
|
+
const d = db || getDatabase();
|
|
16834
|
+
const existing = d.query("SELECT * FROM project_agent_roles WHERE project_id = ? AND agent_id = ? AND role = ?").get(projectId, agentId, role);
|
|
16835
|
+
if (existing) {
|
|
16836
|
+
d.run("UPDATE project_agent_roles SET is_lead = ? WHERE id = ?", [isLead ? 1 : 0, existing.id]);
|
|
16837
|
+
return rowToRole(d.query("SELECT * FROM project_agent_roles WHERE id = ?").get(existing.id));
|
|
16838
|
+
}
|
|
16839
|
+
const id = uuid();
|
|
16840
|
+
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()]);
|
|
16841
|
+
return rowToRole(d.query("SELECT * FROM project_agent_roles WHERE id = ?").get(id));
|
|
16842
|
+
}
|
|
16843
|
+
function removeProjectAgentRole(projectId, agentId, role, db) {
|
|
16844
|
+
const d = db || getDatabase();
|
|
16845
|
+
if (role) {
|
|
16846
|
+
return d.run("DELETE FROM project_agent_roles WHERE project_id = ? AND agent_id = ? AND role = ?", [projectId, agentId, role]).changes;
|
|
16847
|
+
}
|
|
16848
|
+
return d.run("DELETE FROM project_agent_roles WHERE project_id = ? AND agent_id = ?", [projectId, agentId]).changes;
|
|
16849
|
+
}
|
|
16850
|
+
function listProjectAgentRoles(projectId, db) {
|
|
16851
|
+
const d = db || getDatabase();
|
|
16852
|
+
return d.query("SELECT * FROM project_agent_roles WHERE project_id = ? ORDER BY role, created_at").all(projectId).map(rowToRole);
|
|
16853
|
+
}
|
|
16854
|
+
function getAgentProjectRoles(agentId, db) {
|
|
16855
|
+
const d = db || getDatabase();
|
|
16856
|
+
return d.query("SELECT * FROM project_agent_roles WHERE agent_id = ? ORDER BY project_id, role").all(agentId).map(rowToRole);
|
|
16857
|
+
}
|
|
16858
|
+
function getProjectOrgChart(projectId, opts, db) {
|
|
16859
|
+
const d = db || getDatabase();
|
|
16860
|
+
const globalTree = getOrgChart(d);
|
|
16861
|
+
const projectRoles = listProjectAgentRoles(projectId, d);
|
|
16862
|
+
const rolesByAgent = new Map;
|
|
16863
|
+
for (const pr of projectRoles) {
|
|
16864
|
+
if (!rolesByAgent.has(pr.agent_id))
|
|
16865
|
+
rolesByAgent.set(pr.agent_id, { roles: [], isLead: false });
|
|
16866
|
+
const entry = rolesByAgent.get(pr.agent_id);
|
|
16867
|
+
entry.roles.push(pr.role);
|
|
16868
|
+
if (pr.is_lead)
|
|
16869
|
+
entry.isLead = true;
|
|
16870
|
+
}
|
|
16871
|
+
function augmentTree(nodes) {
|
|
16872
|
+
return nodes.map((n) => {
|
|
16873
|
+
const override = rolesByAgent.get(n.agent.id);
|
|
16874
|
+
return {
|
|
16875
|
+
...n,
|
|
16876
|
+
reports: augmentTree(n.reports),
|
|
16877
|
+
project_roles: override?.roles ?? [],
|
|
16878
|
+
is_project_lead: override?.isLead ?? false
|
|
16879
|
+
};
|
|
16880
|
+
}).filter((n) => {
|
|
16881
|
+
if (!opts?.filter_to_project)
|
|
16882
|
+
return true;
|
|
16883
|
+
const hasRole = n.project_roles.length > 0;
|
|
16884
|
+
const hasDescendant = n.reports.length > 0;
|
|
16885
|
+
return hasRole || hasDescendant;
|
|
16886
|
+
});
|
|
16887
|
+
}
|
|
16888
|
+
return augmentTree(globalTree);
|
|
16889
|
+
}
|
|
16890
|
+
var init_project_agent_roles = __esm(() => {
|
|
16891
|
+
init_database();
|
|
16892
|
+
init_agents();
|
|
16893
|
+
});
|
|
16894
|
+
|
|
16895
|
+
// src/db/patrol.ts
|
|
16896
|
+
var exports_patrol = {};
|
|
16897
|
+
__export(exports_patrol, {
|
|
16898
|
+
patrolTasks: () => patrolTasks,
|
|
16899
|
+
getReviewQueue: () => getReviewQueue
|
|
16900
|
+
});
|
|
16901
|
+
function rowToTask3(row) {
|
|
16902
|
+
return {
|
|
16903
|
+
...row,
|
|
16904
|
+
status: row.status,
|
|
16905
|
+
priority: row.priority,
|
|
16906
|
+
tags: JSON.parse(row.tags || "[]"),
|
|
16907
|
+
metadata: JSON.parse(row.metadata || "{}"),
|
|
16908
|
+
requires_approval: Boolean(row.requires_approval)
|
|
16909
|
+
};
|
|
16910
|
+
}
|
|
16911
|
+
function patrolTasks(opts, db) {
|
|
16912
|
+
const d = db || getDatabase();
|
|
16913
|
+
const stuckMinutes = opts?.stuck_minutes || 60;
|
|
16914
|
+
const confidenceThreshold = opts?.confidence_threshold || 0.5;
|
|
16915
|
+
const issues = [];
|
|
16916
|
+
let projectFilter = "";
|
|
16917
|
+
const projectParams = [];
|
|
16918
|
+
if (opts?.project_id) {
|
|
16919
|
+
projectFilter = " AND project_id = ?";
|
|
16920
|
+
projectParams.push(opts.project_id);
|
|
16921
|
+
}
|
|
16922
|
+
const stuckCutoff = new Date(Date.now() - stuckMinutes * 60 * 1000).toISOString();
|
|
16923
|
+
const stuckRows = d.query(`SELECT * FROM tasks WHERE status = 'in_progress' AND updated_at < ?${projectFilter} ORDER BY updated_at ASC`).all(stuckCutoff, ...projectParams);
|
|
16924
|
+
for (const row of stuckRows) {
|
|
16925
|
+
const task = rowToTask3(row);
|
|
16926
|
+
const minutesStuck = Math.round((Date.now() - new Date(task.updated_at).getTime()) / 60000);
|
|
16927
|
+
issues.push({
|
|
16928
|
+
type: "stuck",
|
|
16929
|
+
task_id: task.id,
|
|
16930
|
+
task_title: task.title,
|
|
16931
|
+
severity: minutesStuck > 480 ? "critical" : minutesStuck > 120 ? "high" : "medium",
|
|
16932
|
+
detail: `In progress for ${minutesStuck} minutes without update`
|
|
16933
|
+
});
|
|
16934
|
+
}
|
|
16935
|
+
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);
|
|
16936
|
+
for (const row of lowConfRows) {
|
|
16937
|
+
const task = rowToTask3(row);
|
|
16938
|
+
issues.push({
|
|
16939
|
+
type: "low_confidence",
|
|
16940
|
+
task_id: task.id,
|
|
16941
|
+
task_title: task.title,
|
|
16942
|
+
severity: (task.confidence ?? 0) < 0.3 ? "high" : "medium",
|
|
16943
|
+
detail: `Completed with confidence ${task.confidence} (threshold: ${confidenceThreshold})`
|
|
16944
|
+
});
|
|
16945
|
+
}
|
|
16946
|
+
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();
|
|
16947
|
+
for (const row of orphanedRows) {
|
|
16948
|
+
const task = rowToTask3(row);
|
|
16949
|
+
issues.push({
|
|
16950
|
+
type: "orphaned",
|
|
16951
|
+
task_id: task.id,
|
|
16952
|
+
task_title: task.title,
|
|
16953
|
+
severity: "low",
|
|
16954
|
+
detail: "Pending task with no project, no agent, and no assignee"
|
|
16955
|
+
});
|
|
16956
|
+
}
|
|
16957
|
+
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);
|
|
16958
|
+
for (const row of needsReviewRows) {
|
|
16959
|
+
const task = rowToTask3(row);
|
|
16960
|
+
issues.push({
|
|
16961
|
+
type: "needs_review",
|
|
16962
|
+
task_id: task.id,
|
|
16963
|
+
task_title: task.title,
|
|
16964
|
+
severity: "medium",
|
|
16965
|
+
detail: "Completed but requires approval \u2014 not yet reviewed"
|
|
16966
|
+
});
|
|
16967
|
+
}
|
|
16968
|
+
const pendingWithDeps = d.query(`SELECT t.* FROM tasks t
|
|
16969
|
+
WHERE t.status = 'pending'${projectFilter}
|
|
16970
|
+
AND t.id IN (SELECT task_id FROM task_dependencies)`).all(...projectParams);
|
|
16971
|
+
for (const row of pendingWithDeps) {
|
|
16972
|
+
const deps = d.query(`SELECT d.depends_on, t.status FROM task_dependencies d
|
|
16973
|
+
JOIN tasks t ON t.id = d.depends_on
|
|
16974
|
+
WHERE d.task_id = ?`).all(row.id);
|
|
16975
|
+
if (deps.length > 0 && deps.every((dep) => dep.status === "failed" || dep.status === "cancelled")) {
|
|
16976
|
+
const task = rowToTask3(row);
|
|
16977
|
+
issues.push({
|
|
16978
|
+
type: "zombie_blocked",
|
|
16979
|
+
task_id: task.id,
|
|
16980
|
+
task_title: task.title,
|
|
16981
|
+
severity: "high",
|
|
16982
|
+
detail: `Blocked by ${deps.length} task(s) that are all failed/cancelled \u2014 will never unblock`
|
|
16983
|
+
});
|
|
16984
|
+
}
|
|
16985
|
+
}
|
|
16986
|
+
const severityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
16987
|
+
issues.sort((a, b) => severityOrder[a.severity] - severityOrder[b.severity]);
|
|
16988
|
+
return {
|
|
16989
|
+
issues,
|
|
16990
|
+
total_issues: issues.length,
|
|
16991
|
+
scanned_at: new Date().toISOString()
|
|
16992
|
+
};
|
|
16993
|
+
}
|
|
16994
|
+
function getReviewQueue(opts, db) {
|
|
16995
|
+
const d = db || getDatabase();
|
|
16996
|
+
let sql = `SELECT * FROM tasks WHERE status = 'completed' AND (
|
|
16997
|
+
(requires_approval = 1 AND approved_by IS NULL)
|
|
16998
|
+
OR (confidence IS NOT NULL AND confidence < 0.5)
|
|
16999
|
+
)`;
|
|
17000
|
+
const params = [];
|
|
17001
|
+
if (opts?.project_id) {
|
|
17002
|
+
sql += " AND project_id = ?";
|
|
17003
|
+
params.push(opts.project_id);
|
|
17004
|
+
}
|
|
17005
|
+
sql += " ORDER BY CASE WHEN requires_approval = 1 AND approved_by IS NULL THEN 0 ELSE 1 END, confidence ASC";
|
|
17006
|
+
if (opts?.limit) {
|
|
17007
|
+
sql += " LIMIT ?";
|
|
17008
|
+
params.push(opts.limit);
|
|
17009
|
+
}
|
|
17010
|
+
return d.query(sql).all(...params).map(rowToTask3);
|
|
17011
|
+
}
|
|
17012
|
+
var init_patrol = __esm(() => {
|
|
17013
|
+
init_database();
|
|
17014
|
+
});
|
|
17015
|
+
|
|
17016
|
+
// src/db/agent-metrics.ts
|
|
17017
|
+
var exports_agent_metrics = {};
|
|
17018
|
+
__export(exports_agent_metrics, {
|
|
17019
|
+
scoreTask: () => scoreTask,
|
|
17020
|
+
getLeaderboard: () => getLeaderboard,
|
|
17021
|
+
getAgentMetrics: () => getAgentMetrics
|
|
17022
|
+
});
|
|
17023
|
+
function getAgentMetrics(agentId, opts, db) {
|
|
17024
|
+
const d = db || getDatabase();
|
|
17025
|
+
const agent = d.query("SELECT id, name FROM agents WHERE id = ? OR LOWER(name) = LOWER(?)").get(agentId, agentId);
|
|
17026
|
+
if (!agent)
|
|
17027
|
+
return null;
|
|
17028
|
+
let projectFilter = "";
|
|
17029
|
+
const params = [agent.id, agent.id];
|
|
17030
|
+
if (opts?.project_id) {
|
|
17031
|
+
projectFilter = " AND project_id = ?";
|
|
17032
|
+
params.push(opts.project_id);
|
|
17033
|
+
}
|
|
17034
|
+
const completed = d.query(`SELECT COUNT(*) as count FROM tasks WHERE (agent_id = ? OR assigned_to = ?) AND status = 'completed'${projectFilter}`).get(...params).count;
|
|
17035
|
+
const failed = d.query(`SELECT COUNT(*) as count FROM tasks WHERE (agent_id = ? OR assigned_to = ?) AND status = 'failed'${projectFilter}`).get(...params).count;
|
|
17036
|
+
const inProgress = d.query(`SELECT COUNT(*) as count FROM tasks WHERE (agent_id = ? OR assigned_to = ?) AND status = 'in_progress'${projectFilter}`).get(...params).count;
|
|
17037
|
+
const total = completed + failed;
|
|
17038
|
+
const completionRate = total > 0 ? completed / total : 0;
|
|
17039
|
+
const avgTime = d.query(`SELECT AVG(
|
|
17040
|
+
(julianday(completed_at) - julianday(created_at)) * 24 * 60
|
|
17041
|
+
) as avg_minutes
|
|
17042
|
+
FROM tasks
|
|
17043
|
+
WHERE (agent_id = ? OR assigned_to = ?) AND status = 'completed' AND completed_at IS NOT NULL${projectFilter}`).get(...params);
|
|
17044
|
+
const avgConf = d.query(`SELECT AVG(confidence) as avg_confidence
|
|
17045
|
+
FROM tasks
|
|
17046
|
+
WHERE (agent_id = ? OR assigned_to = ?) AND status = 'completed' AND confidence IS NOT NULL${projectFilter}`).get(...params);
|
|
17047
|
+
const reviewTasks = d.query(`SELECT metadata FROM tasks
|
|
17048
|
+
WHERE (agent_id = ? OR assigned_to = ?) AND status = 'completed'${projectFilter}
|
|
17049
|
+
AND metadata LIKE '%_review_score%'`).all(...params);
|
|
17050
|
+
let reviewScoreAvg = null;
|
|
17051
|
+
if (reviewTasks.length > 0) {
|
|
17052
|
+
let total2 = 0;
|
|
17053
|
+
let count = 0;
|
|
17054
|
+
for (const row of reviewTasks) {
|
|
17055
|
+
try {
|
|
17056
|
+
const meta = JSON.parse(row.metadata);
|
|
17057
|
+
if (typeof meta._review_score === "number") {
|
|
17058
|
+
total2 += meta._review_score;
|
|
17059
|
+
count++;
|
|
17060
|
+
}
|
|
17061
|
+
} catch {}
|
|
17062
|
+
}
|
|
17063
|
+
if (count > 0)
|
|
17064
|
+
reviewScoreAvg = total2 / count;
|
|
17065
|
+
}
|
|
17066
|
+
const speedScore = avgTime?.avg_minutes != null ? Math.max(0, 1 - avgTime.avg_minutes / (60 * 24)) : 0.5;
|
|
17067
|
+
const confidenceScore = avgConf?.avg_confidence ?? 0.5;
|
|
17068
|
+
const volumeScore = Math.min(1, completed / 50);
|
|
17069
|
+
const compositeScore = completionRate * 0.3 + speedScore * 0.2 + confidenceScore * 0.3 + volumeScore * 0.2;
|
|
17070
|
+
return {
|
|
17071
|
+
agent_id: agent.id,
|
|
17072
|
+
agent_name: agent.name,
|
|
17073
|
+
tasks_completed: completed,
|
|
17074
|
+
tasks_failed: failed,
|
|
17075
|
+
tasks_in_progress: inProgress,
|
|
17076
|
+
completion_rate: Math.round(completionRate * 1000) / 1000,
|
|
17077
|
+
avg_completion_minutes: avgTime?.avg_minutes != null ? Math.round(avgTime.avg_minutes * 10) / 10 : null,
|
|
17078
|
+
avg_confidence: avgConf?.avg_confidence != null ? Math.round(avgConf.avg_confidence * 1000) / 1000 : null,
|
|
17079
|
+
review_score_avg: reviewScoreAvg != null ? Math.round(reviewScoreAvg * 1000) / 1000 : null,
|
|
17080
|
+
composite_score: Math.round(compositeScore * 1000) / 1000
|
|
17081
|
+
};
|
|
17082
|
+
}
|
|
17083
|
+
function getLeaderboard(opts, db) {
|
|
17084
|
+
const d = db || getDatabase();
|
|
17085
|
+
const agents = d.query("SELECT id FROM agents ORDER BY name").all();
|
|
17086
|
+
const entries = [];
|
|
17087
|
+
for (const agent of agents) {
|
|
17088
|
+
const metrics = getAgentMetrics(agent.id, { project_id: opts?.project_id }, d);
|
|
17089
|
+
if (metrics && (metrics.tasks_completed > 0 || metrics.tasks_failed > 0 || metrics.tasks_in_progress > 0)) {
|
|
17090
|
+
entries.push(metrics);
|
|
17091
|
+
}
|
|
17092
|
+
}
|
|
17093
|
+
entries.sort((a, b) => b.composite_score - a.composite_score);
|
|
17094
|
+
const limit = opts?.limit || 20;
|
|
17095
|
+
return entries.slice(0, limit).map((entry, idx) => ({
|
|
17096
|
+
...entry,
|
|
17097
|
+
rank: idx + 1
|
|
17098
|
+
}));
|
|
17099
|
+
}
|
|
17100
|
+
function scoreTask(taskId, score, reviewerId, db) {
|
|
17101
|
+
const d = db || getDatabase();
|
|
17102
|
+
if (score < 0 || score > 1)
|
|
17103
|
+
throw new Error("Score must be between 0 and 1");
|
|
17104
|
+
const task = d.query("SELECT metadata FROM tasks WHERE id = ?").get(taskId);
|
|
17105
|
+
if (!task)
|
|
17106
|
+
throw new Error(`Task not found: ${taskId}`);
|
|
17107
|
+
const metadata = JSON.parse(task.metadata || "{}");
|
|
17108
|
+
metadata._review_score = score;
|
|
17109
|
+
if (reviewerId)
|
|
17110
|
+
metadata._reviewed_by = reviewerId;
|
|
17111
|
+
metadata._reviewed_at = now();
|
|
17112
|
+
d.run("UPDATE tasks SET metadata = ?, updated_at = ? WHERE id = ?", [JSON.stringify(metadata), now(), taskId]);
|
|
17113
|
+
}
|
|
17114
|
+
var init_agent_metrics = __esm(() => {
|
|
17115
|
+
init_database();
|
|
15462
17116
|
});
|
|
15463
17117
|
|
|
15464
17118
|
// src/lib/extract.ts
|
|
@@ -20707,7 +22361,7 @@ async function logError(message, opts) {
|
|
|
20707
22361
|
|
|
20708
22362
|
// src/mcp/index.ts
|
|
20709
22363
|
init_types2();
|
|
20710
|
-
import { readFileSync as readFileSync6 } from "fs";
|
|
22364
|
+
import { existsSync as existsSync10, readFileSync as readFileSync6 } from "fs";
|
|
20711
22365
|
import { join as join10, dirname as dirname3 } from "path";
|
|
20712
22366
|
import { fileURLToPath } from "url";
|
|
20713
22367
|
|
|
@@ -20950,14 +22604,24 @@ ${lines.join(`
|
|
|
20950
22604
|
|
|
20951
22605
|
// src/mcp/tools/task-crud.ts
|
|
20952
22606
|
init_tasks();
|
|
22607
|
+
init_types2();
|
|
20953
22608
|
function registerTaskCrudTools(server, ctx) {
|
|
20954
22609
|
const { shouldRegisterTool, resolveId, formatError, formatTask } = ctx;
|
|
22610
|
+
function versionFor(taskId, version) {
|
|
22611
|
+
const current = getTask(taskId);
|
|
22612
|
+
if (!current)
|
|
22613
|
+
throw new TaskNotFoundError(taskId);
|
|
22614
|
+
if (version !== undefined && current.version !== version) {
|
|
22615
|
+
throw new VersionConflictError(taskId, version, current.version);
|
|
22616
|
+
}
|
|
22617
|
+
return current.version;
|
|
22618
|
+
}
|
|
20955
22619
|
if (shouldRegisterTool("create_task")) {
|
|
20956
22620
|
server.tool("create_task", "Create a new task in a project. Pass short_id=null to auto-generate.", {
|
|
20957
22621
|
title: exports_external.string().describe("Task title"),
|
|
20958
22622
|
description: exports_external.string().optional().describe("Task description (markdown)"),
|
|
20959
|
-
status: exports_external.enum(["pending", "in_progress", "completed", "cancelled"]).optional().describe("Initial status (default: pending)"),
|
|
20960
|
-
priority: exports_external.enum(["low", "medium", "high", "
|
|
22623
|
+
status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional().describe("Initial status (default: pending)"),
|
|
22624
|
+
priority: exports_external.enum(["low", "medium", "high", "critical"]).optional().describe("Priority (default: medium)"),
|
|
20961
22625
|
project_id: exports_external.string().optional().describe("Project ID"),
|
|
20962
22626
|
task_list_id: exports_external.string().optional().describe("Task list ID"),
|
|
20963
22627
|
assigned_to: exports_external.string().optional().describe("Agent ID or name to assign to"),
|
|
@@ -20987,9 +22651,9 @@ function registerTaskCrudTools(server, ctx) {
|
|
|
20987
22651
|
if (confidence !== undefined)
|
|
20988
22652
|
resolved.confidence = confidence;
|
|
20989
22653
|
if (retry_count !== undefined)
|
|
20990
|
-
resolved.
|
|
22654
|
+
resolved.max_retries = retry_count;
|
|
20991
22655
|
if (deadline)
|
|
20992
|
-
resolved.
|
|
22656
|
+
resolved.due_at = deadline;
|
|
20993
22657
|
const task = createTask(resolved);
|
|
20994
22658
|
return { content: [{ type: "text", text: formatTask(task) }] };
|
|
20995
22659
|
} catch (e) {
|
|
@@ -20999,8 +22663,8 @@ function registerTaskCrudTools(server, ctx) {
|
|
|
20999
22663
|
}
|
|
21000
22664
|
if (shouldRegisterTool("list_tasks")) {
|
|
21001
22665
|
server.tool("list_tasks", "List tasks with optional filters. Pass empty arrays for multi-value filters (e.g. status=[] shows all).", {
|
|
21002
|
-
status: exports_external.union([exports_external.enum(["pending", "in_progress", "completed", "cancelled"]), exports_external.array(exports_external.enum(["pending", "in_progress", "completed", "cancelled"]))]).optional().describe("Filter by status"),
|
|
21003
|
-
priority: exports_external.union([exports_external.enum(["low", "medium", "high", "
|
|
22666
|
+
status: exports_external.union([exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]), exports_external.array(exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]))]).optional().describe("Filter by status"),
|
|
22667
|
+
priority: exports_external.union([exports_external.enum(["low", "medium", "high", "critical"]), exports_external.array(exports_external.enum(["low", "medium", "high", "critical"]))]).optional().describe("Filter by priority"),
|
|
21004
22668
|
project_id: exports_external.string().optional().describe("Filter by project"),
|
|
21005
22669
|
task_list_id: exports_external.string().optional().describe("Filter by task list"),
|
|
21006
22670
|
assigned_to: exports_external.string().optional().describe("Filter by assignee (agent ID or name, empty string = unassigned)"),
|
|
@@ -21035,9 +22699,9 @@ function registerTaskCrudTools(server, ctx) {
|
|
|
21035
22699
|
}, async ({ task_id }) => {
|
|
21036
22700
|
try {
|
|
21037
22701
|
const resolvedId = resolveId(task_id);
|
|
21038
|
-
const task =
|
|
22702
|
+
const task = getTask(resolvedId);
|
|
21039
22703
|
if (!task)
|
|
21040
|
-
throw new
|
|
22704
|
+
throw new TaskNotFoundError(task_id);
|
|
21041
22705
|
const focus = ctx.getAgentFocus(task.assigned_to || "");
|
|
21042
22706
|
const lines = [
|
|
21043
22707
|
`ID: ${task.id}`,
|
|
@@ -21051,7 +22715,7 @@ function registerTaskCrudTools(server, ctx) {
|
|
|
21051
22715
|
task.estimated_minutes != null ? `Estimate: ${task.estimated_minutes} min` : null,
|
|
21052
22716
|
task.actual_minutes != null ? `Actual: ${task.actual_minutes} min` : null,
|
|
21053
22717
|
task.confidence != null ? `Confidence: ${task.confidence}` : null,
|
|
21054
|
-
task.
|
|
22718
|
+
task.due_at ? `Due: ${task.due_at}` : null,
|
|
21055
22719
|
task.completed_at ? `Completed: ${task.completed_at}` : null,
|
|
21056
22720
|
focus ? `Focus: agent=${focus.agent_id} project=${focus.project_id || "(global)"}` : null,
|
|
21057
22721
|
task.created_at ? `Created: ${task.created_at}` : null,
|
|
@@ -21073,8 +22737,8 @@ ${task.description}` : null
|
|
|
21073
22737
|
task_id: exports_external.string().describe("Task ID"),
|
|
21074
22738
|
title: exports_external.string().optional(),
|
|
21075
22739
|
description: exports_external.string().optional(),
|
|
21076
|
-
status: exports_external.enum(["pending", "in_progress", "completed", "cancelled"]).optional(),
|
|
21077
|
-
priority: exports_external.enum(["low", "medium", "high", "
|
|
22740
|
+
status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
|
|
22741
|
+
priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
|
|
21078
22742
|
assigned_to: exports_external.string().nullable().optional().describe("Agent ID or name, null to unassign"),
|
|
21079
22743
|
project_id: exports_external.string().nullable().optional(),
|
|
21080
22744
|
task_list_id: exports_external.string().nullable().optional(),
|
|
@@ -21103,9 +22767,15 @@ ${task.description}` : null
|
|
|
21103
22767
|
resolved.task_list_id = resolveId(resolved.task_list_id, "task_lists");
|
|
21104
22768
|
if (resolved.depends_on && Array.isArray(resolved.depends_on))
|
|
21105
22769
|
resolved.depends_on = resolved.depends_on.map(resolveId);
|
|
21106
|
-
if (resolved.estimate !== undefined)
|
|
22770
|
+
if (resolved.estimate !== undefined) {
|
|
21107
22771
|
resolved.estimated_minutes = resolved.estimate;
|
|
21108
|
-
|
|
22772
|
+
delete resolved.estimate;
|
|
22773
|
+
}
|
|
22774
|
+
if (resolved.deadline !== undefined) {
|
|
22775
|
+
resolved.due_at = resolved.deadline;
|
|
22776
|
+
delete resolved.deadline;
|
|
22777
|
+
}
|
|
22778
|
+
const task = updateTask(resolvedId, { ...resolved, version: versionFor(resolvedId, version) });
|
|
21109
22779
|
return { content: [{ type: "text", text: formatTask(task) }] };
|
|
21110
22780
|
} catch (e) {
|
|
21111
22781
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -21202,48 +22872,23 @@ function deletePlan(id, db) {
|
|
|
21202
22872
|
return result.changes > 0;
|
|
21203
22873
|
}
|
|
21204
22874
|
|
|
21205
|
-
// src/db/comments.ts
|
|
21206
|
-
init_types2();
|
|
21207
|
-
init_database();
|
|
21208
|
-
init_tasks();
|
|
21209
|
-
function addComment(input, db) {
|
|
21210
|
-
const d = db || getDatabase();
|
|
21211
|
-
if (!getTask2(input.task_id, d)) {
|
|
21212
|
-
throw new TaskNotFoundError(input.task_id);
|
|
21213
|
-
}
|
|
21214
|
-
const id = uuid();
|
|
21215
|
-
const timestamp = now();
|
|
21216
|
-
d.run(`INSERT INTO task_comments (id, task_id, agent_id, session_id, content, type, progress_pct, created_at)
|
|
21217
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
21218
|
-
id,
|
|
21219
|
-
input.task_id,
|
|
21220
|
-
input.agent_id || null,
|
|
21221
|
-
input.session_id || null,
|
|
21222
|
-
input.content,
|
|
21223
|
-
input.type || "comment",
|
|
21224
|
-
input.progress_pct ?? null,
|
|
21225
|
-
timestamp
|
|
21226
|
-
]);
|
|
21227
|
-
return getComment(id, d);
|
|
21228
|
-
}
|
|
21229
|
-
function getComment(id, db) {
|
|
21230
|
-
const d = db || getDatabase();
|
|
21231
|
-
return d.query("SELECT * FROM task_comments WHERE id = ?").get(id);
|
|
21232
|
-
}
|
|
21233
|
-
function listComments(taskId, db) {
|
|
21234
|
-
const d = db || getDatabase();
|
|
21235
|
-
return d.query("SELECT * FROM task_comments WHERE task_id = ? ORDER BY created_at").all(taskId);
|
|
21236
|
-
}
|
|
21237
|
-
function deleteComment(id, db) {
|
|
21238
|
-
const d = db || getDatabase();
|
|
21239
|
-
const result = d.run("DELETE FROM task_comments WHERE id = ?", [id]);
|
|
21240
|
-
return result.changes > 0;
|
|
21241
|
-
}
|
|
21242
|
-
|
|
21243
22875
|
// src/mcp/tools/task-project-tools.ts
|
|
22876
|
+
init_comments();
|
|
21244
22877
|
init_types2();
|
|
21245
22878
|
function registerTaskProjectTools(server, ctx) {
|
|
21246
22879
|
const { shouldRegisterTool, resolveId, formatError, formatTask } = ctx;
|
|
22880
|
+
function versionFor(taskId, version) {
|
|
22881
|
+
const current = getTask(taskId);
|
|
22882
|
+
if (!current)
|
|
22883
|
+
throw new TaskNotFoundError(taskId);
|
|
22884
|
+
if (version !== undefined && current.version !== version) {
|
|
22885
|
+
throw new VersionConflictError(taskId, version, current.version);
|
|
22886
|
+
}
|
|
22887
|
+
return current.version;
|
|
22888
|
+
}
|
|
22889
|
+
function updateWithOptionalVersion(taskId, updates, version) {
|
|
22890
|
+
return updateTask(taskId, { ...updates, version: versionFor(taskId, version) });
|
|
22891
|
+
}
|
|
21247
22892
|
if (shouldRegisterTool("start_task")) {
|
|
21248
22893
|
server.tool("start_task", "Mark a task as in_progress. Uses optimistic locking via version if provided.", {
|
|
21249
22894
|
task_id: exports_external.string().describe("Task ID"),
|
|
@@ -21251,7 +22896,12 @@ function registerTaskProjectTools(server, ctx) {
|
|
|
21251
22896
|
}, async ({ task_id, version }) => {
|
|
21252
22897
|
try {
|
|
21253
22898
|
const resolvedId = resolveId(task_id);
|
|
21254
|
-
|
|
22899
|
+
if (version !== undefined)
|
|
22900
|
+
versionFor(resolvedId, version);
|
|
22901
|
+
const current = getTask(resolvedId);
|
|
22902
|
+
if (!current)
|
|
22903
|
+
throw new TaskNotFoundError(resolvedId);
|
|
22904
|
+
const task = startTask(resolvedId, current.assigned_to || current.agent_id || "mcp");
|
|
21255
22905
|
return { content: [{ type: "text", text: formatTask(task) }] };
|
|
21256
22906
|
} catch (e) {
|
|
21257
22907
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -21267,7 +22917,12 @@ function registerTaskProjectTools(server, ctx) {
|
|
|
21267
22917
|
}, async ({ task_id, confidence, completed_at, version }) => {
|
|
21268
22918
|
try {
|
|
21269
22919
|
const resolvedId = resolveId(task_id);
|
|
21270
|
-
|
|
22920
|
+
if (version !== undefined)
|
|
22921
|
+
versionFor(resolvedId, version);
|
|
22922
|
+
const current = getTask(resolvedId);
|
|
22923
|
+
if (!current)
|
|
22924
|
+
throw new TaskNotFoundError(resolvedId);
|
|
22925
|
+
const task = completeTask(resolvedId, current.assigned_to || current.agent_id || undefined, undefined, { confidence, completed_at });
|
|
21271
22926
|
return { content: [{ type: "text", text: formatTask(task) }] };
|
|
21272
22927
|
} catch (e) {
|
|
21273
22928
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -21281,7 +22936,7 @@ function registerTaskProjectTools(server, ctx) {
|
|
|
21281
22936
|
}, async ({ task_id, version }) => {
|
|
21282
22937
|
try {
|
|
21283
22938
|
const resolvedId = resolveId(task_id);
|
|
21284
|
-
const task =
|
|
22939
|
+
const task = version === undefined ? setTaskStatus(resolvedId, "cancelled") : updateWithOptionalVersion(resolvedId, { status: "cancelled" }, version);
|
|
21285
22940
|
return { content: [{ type: "text", text: formatTask(task) }] };
|
|
21286
22941
|
} catch (e) {
|
|
21287
22942
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -21297,7 +22952,7 @@ function registerTaskProjectTools(server, ctx) {
|
|
|
21297
22952
|
try {
|
|
21298
22953
|
const resolvedId = resolveId(task_id);
|
|
21299
22954
|
const resolvedAssignee = resolveId(new_assignee, "agents");
|
|
21300
|
-
const task =
|
|
22955
|
+
const task = updateWithOptionalVersion(resolvedId, { assigned_to: resolvedAssignee }, version);
|
|
21301
22956
|
return { content: [{ type: "text", text: formatTask(task) }] };
|
|
21302
22957
|
} catch (e) {
|
|
21303
22958
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -21312,7 +22967,7 @@ function registerTaskProjectTools(server, ctx) {
|
|
|
21312
22967
|
}, async ({ task_id, deadline, version }) => {
|
|
21313
22968
|
try {
|
|
21314
22969
|
const resolvedId = resolveId(task_id);
|
|
21315
|
-
const task =
|
|
22970
|
+
const task = updateWithOptionalVersion(resolvedId, { due_at: deadline }, version);
|
|
21316
22971
|
return { content: [{ type: "text", text: formatTask(task) }] };
|
|
21317
22972
|
} catch (e) {
|
|
21318
22973
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -21322,12 +22977,12 @@ function registerTaskProjectTools(server, ctx) {
|
|
|
21322
22977
|
if (shouldRegisterTool("prioritize_task")) {
|
|
21323
22978
|
server.tool("prioritize_task", "Set a task's priority.", {
|
|
21324
22979
|
task_id: exports_external.string().describe("Task ID"),
|
|
21325
|
-
priority: exports_external.enum(["low", "medium", "high", "
|
|
22980
|
+
priority: exports_external.enum(["low", "medium", "high", "critical"]).describe("New priority"),
|
|
21326
22981
|
version: exports_external.number().optional().describe("Expected version for optimistic locking")
|
|
21327
22982
|
}, async ({ task_id, priority, version }) => {
|
|
21328
22983
|
try {
|
|
21329
22984
|
const resolvedId = resolveId(task_id);
|
|
21330
|
-
const task =
|
|
22985
|
+
const task = version === undefined ? setTaskPriority(resolvedId, priority) : updateWithOptionalVersion(resolvedId, { priority }, version);
|
|
21331
22986
|
return { content: [{ type: "text", text: formatTask(task) }] };
|
|
21332
22987
|
} catch (e) {
|
|
21333
22988
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -21383,18 +23038,18 @@ function registerTaskProjectTools(server, ctx) {
|
|
|
21383
23038
|
if (shouldRegisterTool("bulk_update_tasks")) {
|
|
21384
23039
|
server.tool("bulk_update_tasks", "Update multiple tasks at once. All tasks must pass the dependency check.", {
|
|
21385
23040
|
task_ids: exports_external.array(exports_external.string()).describe("Array of task IDs to update"),
|
|
21386
|
-
status: exports_external.enum(["pending", "in_progress", "completed", "cancelled"]).optional(),
|
|
21387
|
-
priority: exports_external.enum(["low", "medium", "high", "
|
|
23041
|
+
status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
|
|
23042
|
+
priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
|
|
21388
23043
|
assigned_to: exports_external.string().nullable().optional().describe("Agent ID or name, null to unassign")
|
|
21389
23044
|
}, async ({ task_ids, status, priority, assigned_to }) => {
|
|
21390
23045
|
try {
|
|
21391
|
-
const { bulkUpdateTasks: bulkUpdateTasks2 } = (()
|
|
23046
|
+
const { bulkUpdateTasks: bulkUpdateTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
21392
23047
|
const resolved = task_ids.map(resolveId);
|
|
21393
23048
|
let resolvedAssignee = assigned_to;
|
|
21394
23049
|
if (resolvedAssignee && typeof resolvedAssignee === "string")
|
|
21395
23050
|
resolvedAssignee = resolveId(resolvedAssignee, "agents");
|
|
21396
23051
|
const result = bulkUpdateTasks2(resolved, { status, priority, assigned_to: resolvedAssignee });
|
|
21397
|
-
return { content: [{ type: "text", text: `${result.updated} task(s) updated, ${result.
|
|
23052
|
+
return { content: [{ type: "text", text: `${result.updated} task(s) updated, ${result.failed.length} failed.` }] };
|
|
21398
23053
|
} catch (e) {
|
|
21399
23054
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
21400
23055
|
}
|
|
@@ -21405,8 +23060,8 @@ function registerTaskProjectTools(server, ctx) {
|
|
|
21405
23060
|
tasks: exports_external.array(exports_external.object({
|
|
21406
23061
|
title: exports_external.string(),
|
|
21407
23062
|
description: exports_external.string().optional(),
|
|
21408
|
-
status: exports_external.enum(["pending", "in_progress", "completed", "cancelled"]).optional(),
|
|
21409
|
-
priority: exports_external.enum(["low", "medium", "high", "
|
|
23063
|
+
status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
|
|
23064
|
+
priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
|
|
21410
23065
|
project_id: exports_external.string().optional(),
|
|
21411
23066
|
task_list_id: exports_external.string().optional(),
|
|
21412
23067
|
assigned_to: exports_external.string().optional(),
|
|
@@ -21417,7 +23072,7 @@ function registerTaskProjectTools(server, ctx) {
|
|
|
21417
23072
|
})).describe("Array of task objects")
|
|
21418
23073
|
}, async ({ tasks }) => {
|
|
21419
23074
|
try {
|
|
21420
|
-
const { bulkCreateTasks: bulkCreateTasks2 } = (()
|
|
23075
|
+
const { bulkCreateTasks: bulkCreateTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
21421
23076
|
const resolved = tasks.map((t) => {
|
|
21422
23077
|
const r = { ...t };
|
|
21423
23078
|
if (r.project_id)
|
|
@@ -21431,7 +23086,7 @@ function registerTaskProjectTools(server, ctx) {
|
|
|
21431
23086
|
return r;
|
|
21432
23087
|
});
|
|
21433
23088
|
const result = bulkCreateTasks2(resolved);
|
|
21434
|
-
return { content: [{ type: "text", text: `${result.created} task(s) created
|
|
23089
|
+
return { content: [{ type: "text", text: `${result.created.length} task(s) created.` }] };
|
|
21435
23090
|
} catch (e) {
|
|
21436
23091
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
21437
23092
|
}
|
|
@@ -21443,9 +23098,9 @@ function registerTaskProjectTools(server, ctx) {
|
|
|
21443
23098
|
force: exports_external.boolean().optional().describe("Skip child check for all tasks (dangerous)")
|
|
21444
23099
|
}, async ({ task_ids, force }) => {
|
|
21445
23100
|
try {
|
|
21446
|
-
const { bulkDeleteTasks } = (()
|
|
23101
|
+
const { bulkDeleteTasks: bulkDeleteTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
21447
23102
|
const resolved = task_ids.map(resolveId);
|
|
21448
|
-
const result =
|
|
23103
|
+
const result = bulkDeleteTasks2(resolved, force);
|
|
21449
23104
|
return { content: [{ type: "text", text: `${result.deleted} task(s) deleted, ${result.skipped} skipped (has children).` }] };
|
|
21450
23105
|
} catch (e) {
|
|
21451
23106
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -21494,7 +23149,7 @@ function registerTaskProjectTools(server, ctx) {
|
|
|
21494
23149
|
const project = getProject(resolvedId);
|
|
21495
23150
|
if (!project)
|
|
21496
23151
|
throw new TaskNotFoundError(`Project not found: ${project_id}`);
|
|
21497
|
-
const { listTasks: listTasks3 } = (()
|
|
23152
|
+
const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
21498
23153
|
const tasks = listTasks3({ project_id: resolvedId, limit: 100 }, undefined);
|
|
21499
23154
|
const lines = [
|
|
21500
23155
|
`ID: ${project.id}`,
|
|
@@ -21595,7 +23250,7 @@ function registerTaskProjectTools(server, ctx) {
|
|
|
21595
23250
|
throw new TaskNotFoundError(`Task list not found: ${task_list_id}`);
|
|
21596
23251
|
let tasks = [];
|
|
21597
23252
|
if (include_tasks) {
|
|
21598
|
-
const { listTasks: listTasks3 } = (()
|
|
23253
|
+
const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
21599
23254
|
tasks = listTasks3({ task_list_id: resolvedId, limit: 200 }, undefined);
|
|
21600
23255
|
}
|
|
21601
23256
|
const lines = [
|
|
@@ -21695,7 +23350,7 @@ Tasks:` : null,
|
|
|
21695
23350
|
throw new TaskNotFoundError(`Plan not found: ${plan_id}`);
|
|
21696
23351
|
let tasks = [];
|
|
21697
23352
|
if (include_tasks) {
|
|
21698
|
-
const { listTasks: listTasks3 } = (()
|
|
23353
|
+
const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
21699
23354
|
tasks = listTasks3({ plan_id: resolvedId, limit: 200 }, undefined);
|
|
21700
23355
|
}
|
|
21701
23356
|
const lines = [
|
|
@@ -21784,7 +23439,7 @@ Tasks:` : null,
|
|
|
21784
23439
|
const tag = getTag(tag_id);
|
|
21785
23440
|
if (!tag)
|
|
21786
23441
|
throw new TaskNotFoundError(`Tag not found: ${tag_id}`);
|
|
21787
|
-
const { listTasks: listTasks3 } = (()
|
|
23442
|
+
const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
21788
23443
|
const tasks = listTasks3({ tags: [tag.name], limit: 100 }, undefined);
|
|
21789
23444
|
const lines = [
|
|
21790
23445
|
`Tag: ${tag.name}${tag.color ? ` (${tag.color})` : ""}`,
|
|
@@ -21863,7 +23518,7 @@ Tasks:` : null,
|
|
|
21863
23518
|
const label = getLabel(label_id);
|
|
21864
23519
|
if (!label)
|
|
21865
23520
|
throw new TaskNotFoundError(`Label not found: ${label_id}`);
|
|
21866
|
-
const { listTasks: listTasks3 } = (()
|
|
23521
|
+
const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
21867
23522
|
const tasks = listTasks3({ tags: [label.name], limit: 100 }, undefined);
|
|
21868
23523
|
const lines = [
|
|
21869
23524
|
`Label: ${label.name}${label.color ? ` (${label.color})` : ""}`,
|
|
@@ -21914,7 +23569,7 @@ Tasks:` : null,
|
|
|
21914
23569
|
try {
|
|
21915
23570
|
const resolvedId = resolveId(task_id);
|
|
21916
23571
|
const resolvedAuthor = author ? resolveId(author, "agents") : undefined;
|
|
21917
|
-
const comment = addComment({ task_id: resolvedId, body,
|
|
23572
|
+
const comment = addComment({ task_id: resolvedId, content: body, agent_id: resolvedAuthor });
|
|
21918
23573
|
return { content: [{ type: "text", text: `Comment added to ${task_id.slice(0, 8)}: ${body.slice(0, 50)}${body.length > 50 ? "..." : ""}` }] };
|
|
21919
23574
|
} catch (e) {
|
|
21920
23575
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -21930,8 +23585,8 @@ Tasks:` : null,
|
|
|
21930
23585
|
const comments = listComments(resolvedId);
|
|
21931
23586
|
if (comments.length === 0)
|
|
21932
23587
|
return { content: [{ type: "text", text: "No comments." }] };
|
|
21933
|
-
const lines = comments.map((c) => `[${c.
|
|
21934
|
-
${c.
|
|
23588
|
+
const lines = comments.map((c) => `[${c.agent_id || "unknown"}] ${c.created_at?.slice(0, 16)}:
|
|
23589
|
+
${c.content}`);
|
|
21935
23590
|
return { content: [{ type: "text", text: lines.join(`
|
|
21936
23591
|
|
|
21937
23592
|
`) }] };
|
|
@@ -21946,7 +23601,7 @@ Tasks:` : null,
|
|
|
21946
23601
|
body: exports_external.string().describe("New comment body")
|
|
21947
23602
|
}, async ({ comment_id, body }) => {
|
|
21948
23603
|
try {
|
|
21949
|
-
const comment = updateComment(comment_id, { body });
|
|
23604
|
+
const comment = updateComment(comment_id, { content: body });
|
|
21950
23605
|
return { content: [{ type: "text", text: `Comment ${comment_id.slice(0, 8)} updated.` }] };
|
|
21951
23606
|
} catch (e) {
|
|
21952
23607
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -21969,17 +23624,17 @@ Tasks:` : null,
|
|
|
21969
23624
|
server.tool("search_tasks", "Full-text search across task titles and descriptions.", {
|
|
21970
23625
|
query: exports_external.string().describe("Search query"),
|
|
21971
23626
|
project_id: exports_external.string().optional().describe("Filter by project"),
|
|
21972
|
-
status: exports_external.enum(["pending", "in_progress", "completed", "cancelled"]).optional(),
|
|
23627
|
+
status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
|
|
21973
23628
|
limit: exports_external.number().optional().describe("Max results (default: 20)")
|
|
21974
23629
|
}, async ({ query, project_id, status, limit }) => {
|
|
21975
23630
|
try {
|
|
21976
|
-
const { searchTasks } = (()
|
|
23631
|
+
const { searchTasks: searchTasks2 } = (init_search(), __toCommonJS(exports_search));
|
|
21977
23632
|
const resolved = { query, limit };
|
|
21978
23633
|
if (project_id)
|
|
21979
23634
|
resolved.project_id = resolveId(project_id, "projects");
|
|
21980
23635
|
if (status)
|
|
21981
23636
|
resolved.status = status;
|
|
21982
|
-
const results =
|
|
23637
|
+
const results = searchTasks2(resolved);
|
|
21983
23638
|
if (results.length === 0)
|
|
21984
23639
|
return { content: [{ type: "text", text: `No results for: ${query}` }] };
|
|
21985
23640
|
const lines = results.map((t) => `${t.short_id || t.id.slice(0, 8)} [${t.status}] ${t.title}`);
|
|
@@ -21991,34 +23646,22 @@ ${lines.join(`
|
|
|
21991
23646
|
}
|
|
21992
23647
|
});
|
|
21993
23648
|
}
|
|
21994
|
-
if (shouldRegisterTool("sync")) {
|
|
21995
|
-
server.tool("sync", "Sync tasks from a GitHub PR or external source into the project.", {
|
|
21996
|
-
source: exports_external.enum(["github_pr", "linear", "asana"]).describe("Source type"),
|
|
21997
|
-
source_id: exports_external.string().describe("PR number, Linear issue ID, or Asana task ID"),
|
|
21998
|
-
project_id: exports_external.string().optional().describe("Project ID to import into"),
|
|
21999
|
-
options: exports_external.record(exports_external.unknown()).optional().describe("Source-specific options")
|
|
22000
|
-
}, async ({ source, source_id, project_id, options }) => {
|
|
22001
|
-
try {
|
|
22002
|
-
const { syncFromGithubPR, syncFromLinear, syncFromAsana } = (()=>{throw new Error("Cannot require module "+"../lib/sync.js");})();
|
|
22003
|
-
let result;
|
|
22004
|
-
if (source === "github_pr") {
|
|
22005
|
-
result = await syncFromGithubPR({ prNumber: parseInt(source_id), project_id: project_id ? resolveId(project_id, "projects") : undefined, ...options });
|
|
22006
|
-
} else if (source === "linear") {
|
|
22007
|
-
result = await syncFromLinear({ issueId: source_id, project_id: project_id ? resolveId(project_id, "projects") : undefined, ...options });
|
|
22008
|
-
} else {
|
|
22009
|
-
result = await syncFromAsana({ taskId: source_id, project_id: project_id ? resolveId(project_id, "projects") : undefined, ...options });
|
|
22010
|
-
}
|
|
22011
|
-
return { content: [{ type: "text", text: `Synced from ${source}: ${result.task?.title || source_id}` }] };
|
|
22012
|
-
} catch (e) {
|
|
22013
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
22014
|
-
}
|
|
22015
|
-
});
|
|
22016
|
-
}
|
|
22017
23649
|
}
|
|
22018
23650
|
|
|
22019
23651
|
// src/mcp/tools/task-workflow-tools.ts
|
|
23652
|
+
init_types2();
|
|
22020
23653
|
function registerTaskWorkflowTools(server, ctx) {
|
|
22021
23654
|
const { shouldRegisterTool, resolveId, formatError, formatTask } = ctx;
|
|
23655
|
+
function versionFor(taskId, version) {
|
|
23656
|
+
const { getTask: getTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
23657
|
+
const current = getTask2(taskId);
|
|
23658
|
+
if (!current)
|
|
23659
|
+
throw new TaskNotFoundError(taskId);
|
|
23660
|
+
if (version !== undefined && current.version !== version) {
|
|
23661
|
+
throw new VersionConflictError(taskId, version, current.version);
|
|
23662
|
+
}
|
|
23663
|
+
return current.version;
|
|
23664
|
+
}
|
|
22022
23665
|
if (shouldRegisterTool("approve_task")) {
|
|
22023
23666
|
server.tool("approve_task", "Approve a task that requires_approval. Records who approved it.", {
|
|
22024
23667
|
task_id: exports_external.string().describe("Task ID"),
|
|
@@ -22027,13 +23670,14 @@ function registerTaskWorkflowTools(server, ctx) {
|
|
|
22027
23670
|
version: exports_external.number().optional().describe("Expected version for optimistic locking")
|
|
22028
23671
|
}, async ({ task_id, approved_by, notes, version }) => {
|
|
22029
23672
|
try {
|
|
22030
|
-
const { updateTask: updateTask2 } = (()
|
|
23673
|
+
const { updateTask: updateTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
22031
23674
|
const resolvedId = resolveId(task_id);
|
|
22032
23675
|
const resolvedApprover = approved_by ? resolveId(approved_by, "agents") : undefined;
|
|
22033
23676
|
const task = updateTask2(resolvedId, {
|
|
22034
23677
|
approved_by: resolvedApprover,
|
|
22035
|
-
metadata: notes ? { approval_notes: notes } : {}
|
|
22036
|
-
|
|
23678
|
+
metadata: notes ? { approval_notes: notes } : {},
|
|
23679
|
+
version: versionFor(resolvedId, version)
|
|
23680
|
+
});
|
|
22037
23681
|
return { content: [{ type: "text", text: `Task ${task_id.slice(0, 8)} approved by ${resolvedApprover || "unknown"}` }] };
|
|
22038
23682
|
} catch (e) {
|
|
22039
23683
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -22048,15 +23692,11 @@ function registerTaskWorkflowTools(server, ctx) {
|
|
|
22048
23692
|
version: exports_external.number().optional().describe("Expected version for optimistic locking")
|
|
22049
23693
|
}, async ({ task_id, reason, agent_id, version }) => {
|
|
22050
23694
|
try {
|
|
22051
|
-
const {
|
|
23695
|
+
const { failTask: failTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
22052
23696
|
const resolvedId = resolveId(task_id);
|
|
22053
|
-
|
|
22054
|
-
|
|
22055
|
-
|
|
22056
|
-
back_on_board: false,
|
|
22057
|
-
metadata: reason ? { failure_reason: reason } : {}
|
|
22058
|
-
}, version);
|
|
22059
|
-
return { content: [{ type: "text", text: `Task ${task_id.slice(0, 8)} marked failed. Retry count: ${task.retry_count}` }] };
|
|
23697
|
+
versionFor(resolvedId, version);
|
|
23698
|
+
const result = failTask2(resolvedId, agent_id, reason);
|
|
23699
|
+
return { content: [{ type: "text", text: `Task ${task_id.slice(0, 8)} marked failed. Retry count: ${result.task.retry_count}` }] };
|
|
22060
23700
|
} catch (e) {
|
|
22061
23701
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
22062
23702
|
}
|
|
@@ -22065,12 +23705,12 @@ function registerTaskWorkflowTools(server, ctx) {
|
|
|
22065
23705
|
if (shouldRegisterTool("get_my_tasks")) {
|
|
22066
23706
|
server.tool("get_my_tasks", "Get tasks assigned to the calling agent. Supports focus mode scoping.", {
|
|
22067
23707
|
agent_id: exports_external.string().optional().describe("Agent ID (defaults to context agent)"),
|
|
22068
|
-
status: exports_external.enum(["pending", "in_progress", "completed", "cancelled"]).optional().describe("Filter by status"),
|
|
23708
|
+
status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional().describe("Filter by status"),
|
|
22069
23709
|
project_id: exports_external.string().optional().describe("Filter by project (respects focus mode)"),
|
|
22070
23710
|
limit: exports_external.number().optional().describe("Max results (default: 50)")
|
|
22071
23711
|
}, async ({ agent_id, status, project_id, limit }) => {
|
|
22072
23712
|
try {
|
|
22073
|
-
const { listTasks: listTasks3 } = (()
|
|
23713
|
+
const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
22074
23714
|
const focus = ctx.getAgentFocus(agent_id || "");
|
|
22075
23715
|
const effectiveAgentId = focus ? focus.agent_id : agent_id || "";
|
|
22076
23716
|
const effectiveProjectId = focus?.project_id || project_id;
|
|
@@ -22090,6 +23730,122 @@ function registerTaskWorkflowTools(server, ctx) {
|
|
|
22090
23730
|
}
|
|
22091
23731
|
});
|
|
22092
23732
|
}
|
|
23733
|
+
if (shouldRegisterTool("get_next_task")) {
|
|
23734
|
+
server.tool("get_next_task", "Get the best available pending task without claiming it.", {
|
|
23735
|
+
agent_id: exports_external.string().optional().describe("Agent ID or name for assignment affinity"),
|
|
23736
|
+
project_id: exports_external.string().optional().describe("Filter by project"),
|
|
23737
|
+
task_list_id: exports_external.string().optional().describe("Filter by task list"),
|
|
23738
|
+
plan_id: exports_external.string().optional().describe("Filter by plan"),
|
|
23739
|
+
tags: exports_external.array(exports_external.string()).optional().describe("Filter by tags")
|
|
23740
|
+
}, async ({ agent_id, project_id, task_list_id, plan_id, tags }) => {
|
|
23741
|
+
try {
|
|
23742
|
+
const { getNextTask: getNextTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
23743
|
+
const filters = {};
|
|
23744
|
+
if (project_id)
|
|
23745
|
+
filters.project_id = resolveId(project_id, "projects");
|
|
23746
|
+
if (task_list_id)
|
|
23747
|
+
filters.task_list_id = resolveId(task_list_id, "task_lists");
|
|
23748
|
+
if (plan_id)
|
|
23749
|
+
filters.plan_id = resolveId(plan_id, "plans");
|
|
23750
|
+
if (tags)
|
|
23751
|
+
filters.tags = tags;
|
|
23752
|
+
const task = getNextTask2(agent_id, filters);
|
|
23753
|
+
return { content: [{ type: "text", text: task ? formatTask(task) : "No available task." }] };
|
|
23754
|
+
} catch (e) {
|
|
23755
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
23756
|
+
}
|
|
23757
|
+
});
|
|
23758
|
+
}
|
|
23759
|
+
if (shouldRegisterTool("claim_next_task")) {
|
|
23760
|
+
server.tool("claim_next_task", "Atomically claim and start the best available pending task for an agent.", {
|
|
23761
|
+
agent_id: exports_external.string().describe("Agent ID or name claiming the task"),
|
|
23762
|
+
project_id: exports_external.string().optional().describe("Filter by project"),
|
|
23763
|
+
task_list_id: exports_external.string().optional().describe("Filter by task list"),
|
|
23764
|
+
plan_id: exports_external.string().optional().describe("Filter by plan"),
|
|
23765
|
+
tags: exports_external.array(exports_external.string()).optional().describe("Filter by tags")
|
|
23766
|
+
}, async ({ agent_id, project_id, task_list_id, plan_id, tags }) => {
|
|
23767
|
+
try {
|
|
23768
|
+
const { claimNextTask: claimNextTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
23769
|
+
const filters = {};
|
|
23770
|
+
if (project_id)
|
|
23771
|
+
filters.project_id = resolveId(project_id, "projects");
|
|
23772
|
+
if (task_list_id)
|
|
23773
|
+
filters.task_list_id = resolveId(task_list_id, "task_lists");
|
|
23774
|
+
if (plan_id)
|
|
23775
|
+
filters.plan_id = resolveId(plan_id, "plans");
|
|
23776
|
+
if (tags)
|
|
23777
|
+
filters.tags = tags;
|
|
23778
|
+
const task = claimNextTask2(agent_id, filters);
|
|
23779
|
+
return { content: [{ type: "text", text: task ? formatTask(task) : "No available task to claim." }] };
|
|
23780
|
+
} catch (e) {
|
|
23781
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
23782
|
+
}
|
|
23783
|
+
});
|
|
23784
|
+
}
|
|
23785
|
+
if (shouldRegisterTool("get_tasks_changed_since")) {
|
|
23786
|
+
server.tool("get_tasks_changed_since", "List tasks changed since an ISO timestamp.", {
|
|
23787
|
+
since: exports_external.string().describe("ISO timestamp"),
|
|
23788
|
+
project_id: exports_external.string().optional().describe("Filter by project"),
|
|
23789
|
+
task_list_id: exports_external.string().optional().describe("Filter by task list"),
|
|
23790
|
+
limit: exports_external.number().optional().describe("Maximum tasks to return")
|
|
23791
|
+
}, async ({ since, project_id, task_list_id, limit }) => {
|
|
23792
|
+
try {
|
|
23793
|
+
const { getTasksChangedSince: getTasksChangedSince2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
23794
|
+
const filters = {};
|
|
23795
|
+
if (project_id)
|
|
23796
|
+
filters.project_id = resolveId(project_id, "projects");
|
|
23797
|
+
if (task_list_id)
|
|
23798
|
+
filters.task_list_id = resolveId(task_list_id, "task_lists");
|
|
23799
|
+
const tasks = getTasksChangedSince2(since, filters).slice(0, limit || 50);
|
|
23800
|
+
if (tasks.length === 0)
|
|
23801
|
+
return { content: [{ type: "text", text: "No changed tasks." }] };
|
|
23802
|
+
return { content: [{ type: "text", text: tasks.map(formatTask).join(`
|
|
23803
|
+
`) }] };
|
|
23804
|
+
} catch (e) {
|
|
23805
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
23806
|
+
}
|
|
23807
|
+
});
|
|
23808
|
+
}
|
|
23809
|
+
function registerContextTool(name, description) {
|
|
23810
|
+
if (!shouldRegisterTool(name))
|
|
23811
|
+
return;
|
|
23812
|
+
server.tool(name, description, {
|
|
23813
|
+
agent_id: exports_external.string().optional().describe("Agent ID or name"),
|
|
23814
|
+
project_id: exports_external.string().optional().describe("Filter by project"),
|
|
23815
|
+
task_list_id: exports_external.string().optional().describe("Filter by task list"),
|
|
23816
|
+
explain_blocked: exports_external.boolean().optional().describe("Include blocked task details")
|
|
23817
|
+
}, async ({ agent_id, project_id, task_list_id, explain_blocked }) => {
|
|
23818
|
+
try {
|
|
23819
|
+
const { getStatus: getStatus2, getNextTask: getNextTask2, getOverdueTasks: getOverdueTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
23820
|
+
const { getLatestHandoff: getLatestHandoff2 } = (init_handoffs(), __toCommonJS(exports_handoffs));
|
|
23821
|
+
const filters = {};
|
|
23822
|
+
if (project_id)
|
|
23823
|
+
filters.project_id = resolveId(project_id, "projects");
|
|
23824
|
+
if (task_list_id)
|
|
23825
|
+
filters.task_list_id = resolveId(task_list_id, "task_lists");
|
|
23826
|
+
const status = getStatus2(filters, agent_id, { explain_blocked });
|
|
23827
|
+
const next_task = getNextTask2(agent_id, filters);
|
|
23828
|
+
const overdue = getOverdueTasks2(filters.project_id);
|
|
23829
|
+
const latest_handoff = getLatestHandoff2(agent_id, filters.project_id);
|
|
23830
|
+
return {
|
|
23831
|
+
content: [{
|
|
23832
|
+
type: "text",
|
|
23833
|
+
text: JSON.stringify({
|
|
23834
|
+
status,
|
|
23835
|
+
next_task,
|
|
23836
|
+
overdue_count: overdue.length,
|
|
23837
|
+
latest_handoff,
|
|
23838
|
+
as_of: new Date().toISOString()
|
|
23839
|
+
}, null, 2)
|
|
23840
|
+
}]
|
|
23841
|
+
};
|
|
23842
|
+
} catch (e) {
|
|
23843
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
23844
|
+
}
|
|
23845
|
+
});
|
|
23846
|
+
}
|
|
23847
|
+
registerContextTool("get_context", "Get session start context: queue status, next task, overdue count, and latest handoff.");
|
|
23848
|
+
registerContextTool("bootstrap", "Bootstrap an agent session with queue context and the next available task.");
|
|
22093
23849
|
if (shouldRegisterTool("get_org_chart")) {
|
|
22094
23850
|
server.tool("get_org_chart", "Get the global org chart (agent hierarchy + titles).", {
|
|
22095
23851
|
format: exports_external.enum(["text", "json"]).optional().describe("Output format (default: text)")
|
|
@@ -22110,7 +23866,7 @@ function registerTaskWorkflowTools(server, ctx) {
|
|
|
22110
23866
|
}).join(`
|
|
22111
23867
|
`);
|
|
22112
23868
|
};
|
|
22113
|
-
const { getOrgChart: getOrgChart2 } = (()
|
|
23869
|
+
const { getOrgChart: getOrgChart2 } = (init_agents(), __toCommonJS(exports_agents));
|
|
22114
23870
|
const tree = getOrgChart2();
|
|
22115
23871
|
if (format === "json") {
|
|
22116
23872
|
return { content: [{ type: "text", text: JSON.stringify(tree, null, 2) }] };
|
|
@@ -22130,10 +23886,10 @@ function registerTaskWorkflowTools(server, ctx) {
|
|
|
22130
23886
|
reports_to: exports_external.string().describe("Manager agent ID or name")
|
|
22131
23887
|
}, async ({ agent_id, reports_to }) => {
|
|
22132
23888
|
try {
|
|
22133
|
-
const {
|
|
23889
|
+
const { updateAgent: updateAgent2 } = (init_agents(), __toCommonJS(exports_agents));
|
|
22134
23890
|
const resolvedAgent = resolveId(agent_id, "agents");
|
|
22135
23891
|
const resolvedManager = resolveId(reports_to, "agents");
|
|
22136
|
-
|
|
23892
|
+
updateAgent2(resolvedAgent, { reports_to: resolvedManager });
|
|
22137
23893
|
return { content: [{ type: "text", text: `${agent_id} now reports to ${reports_to}` }] };
|
|
22138
23894
|
} catch (e) {
|
|
22139
23895
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -22151,9 +23907,9 @@ function registerTaskAutoTools(server, ctx) {
|
|
|
22151
23907
|
project_id: exports_external.string().optional().describe("Scope to a project")
|
|
22152
23908
|
}, async ({ days = 7, project_id }) => {
|
|
22153
23909
|
try {
|
|
22154
|
-
const { archiveCompletedTasks } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
23910
|
+
const { archiveCompletedTasks: archiveCompletedTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
22155
23911
|
const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
|
|
22156
|
-
const count =
|
|
23912
|
+
const count = archiveCompletedTasks2(days, resolvedProjectId);
|
|
22157
23913
|
return { content: [{ type: "text", text: `Archived ${count} completed task(s) older than ${days} days.` }] };
|
|
22158
23914
|
} catch (e) {
|
|
22159
23915
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -22179,9 +23935,9 @@ function registerTaskAutoTools(server, ctx) {
|
|
|
22179
23935
|
limit: exports_external.number().optional().describe("Max results (default: 50)")
|
|
22180
23936
|
}, async ({ project_id, limit }) => {
|
|
22181
23937
|
try {
|
|
22182
|
-
const { getArchivedTasks } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
23938
|
+
const { getArchivedTasks: getArchivedTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
22183
23939
|
const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
|
|
22184
|
-
const tasks =
|
|
23940
|
+
const tasks = getArchivedTasks2({ project_id: resolvedProjectId, limit: limit || 50 });
|
|
22185
23941
|
if (tasks.length === 0)
|
|
22186
23942
|
return { content: [{ type: "text", text: "No archived tasks." }] };
|
|
22187
23943
|
const lines = tasks.map((t) => `${t.short_id || t.id.slice(0, 8)} ${t.title} archived ${t.archived_at}`);
|
|
@@ -22197,14 +23953,13 @@ function registerTaskAutoTools(server, ctx) {
|
|
|
22197
23953
|
task_id: exports_external.string().describe("Task ID")
|
|
22198
23954
|
}, async ({ task_id }) => {
|
|
22199
23955
|
try {
|
|
22200
|
-
const { autoAssign } = (init_agents(), __toCommonJS(exports_agents));
|
|
22201
23956
|
const resolvedId = resolveId(task_id);
|
|
22202
|
-
const
|
|
22203
|
-
|
|
23957
|
+
const { autoAssignTask: autoAssignTask2 } = (init_auto_assign(), __toCommonJS(exports_auto_assign));
|
|
23958
|
+
const assignment = await autoAssignTask2(resolvedId);
|
|
23959
|
+
if (!assignment.assigned_to)
|
|
22204
23960
|
return { content: [{ type: "text", text: "No suitable agent found for this task." }] };
|
|
22205
|
-
const
|
|
22206
|
-
|
|
22207
|
-
return { content: [{ type: "text", text: `Task ${task_id.slice(0, 8)} assigned to ${assignment.agent_name} (score: ${assignment.score.toFixed(2)})` }] };
|
|
23961
|
+
const reason = assignment.reason ? ` \u2014 ${assignment.reason}` : "";
|
|
23962
|
+
return { content: [{ type: "text", text: `Task ${task_id.slice(0, 8)} assigned to ${assignment.agent_name || assignment.assigned_to} via ${assignment.method}${reason}` }] };
|
|
22208
23963
|
} catch (e) {
|
|
22209
23964
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
22210
23965
|
}
|
|
@@ -22215,10 +23970,23 @@ function registerTaskAutoTools(server, ctx) {
|
|
|
22215
23970
|
agent_id: exports_external.string().optional().describe("Agent ID (defaults to context agent)")
|
|
22216
23971
|
}, async ({ agent_id }) => {
|
|
22217
23972
|
try {
|
|
22218
|
-
const {
|
|
23973
|
+
const { listTasks: listTasks3, getBlockedTasks: getBlockedTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
22219
23974
|
const focus = ctx.getAgentFocus(agent_id || "");
|
|
22220
23975
|
const effectiveAgentId = focus ? focus.agent_id : agent_id || "";
|
|
22221
|
-
|
|
23976
|
+
if (!effectiveAgentId) {
|
|
23977
|
+
return { content: [{ type: "text", text: "No agent_id provided and no agent focus is active." }], isError: true };
|
|
23978
|
+
}
|
|
23979
|
+
const assigned = listTasks3({ assigned_to: effectiveAgentId, limit: 500 }, undefined);
|
|
23980
|
+
const now2 = Date.now();
|
|
23981
|
+
const dueSoonCutoff = now2 + 24 * 60 * 60 * 1000;
|
|
23982
|
+
const blocked = getBlockedTasks2().filter((t) => t.assigned_to === effectiveAgentId);
|
|
23983
|
+
const workload = {
|
|
23984
|
+
in_progress: assigned.filter((t) => t.status === "in_progress").length,
|
|
23985
|
+
pending: assigned.filter((t) => t.status === "pending").length,
|
|
23986
|
+
completed_recent: assigned.filter((t) => t.status === "completed" && t.completed_at && now2 - new Date(t.completed_at).getTime() <= 7 * 24 * 60 * 60 * 1000).length,
|
|
23987
|
+
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,
|
|
23988
|
+
blocked: blocked.length
|
|
23989
|
+
};
|
|
22222
23990
|
const lines = [
|
|
22223
23991
|
`Agent: ${effectiveAgentId}`,
|
|
22224
23992
|
`In Progress: ${workload.in_progress}`,
|
|
@@ -22240,10 +24008,29 @@ function registerTaskAutoTools(server, ctx) {
|
|
|
22240
24008
|
max_per_agent: exports_external.number().optional().describe("Max tasks per agent (default: 5)")
|
|
22241
24009
|
}, async ({ project_id, max_per_agent }) => {
|
|
22242
24010
|
try {
|
|
22243
|
-
const {
|
|
24011
|
+
const { listAgents: listAgents2 } = (init_agents(), __toCommonJS(exports_agents));
|
|
24012
|
+
const { listTasks: listTasks3, updateTask: updateTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
22244
24013
|
const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
|
|
22245
|
-
const
|
|
22246
|
-
|
|
24014
|
+
const limit = max_per_agent || 5;
|
|
24015
|
+
const agents = listAgents2().filter((agent) => agent.status === "active");
|
|
24016
|
+
if (agents.length === 0)
|
|
24017
|
+
return { content: [{ type: "text", text: "No active agents available for rebalancing." }] };
|
|
24018
|
+
const activeTasks = listTasks3({ project_id: resolvedProjectId, status: ["pending", "in_progress"], limit: 1000 }, undefined);
|
|
24019
|
+
const load = new Map(agents.map((agent) => [agent.id, activeTasks.filter((t) => t.assigned_to === agent.id).length]));
|
|
24020
|
+
let moved = 0;
|
|
24021
|
+
let skipped = 0;
|
|
24022
|
+
for (const task of activeTasks.filter((t) => t.status === "pending" && t.assigned_to && (load.get(t.assigned_to) ?? 0) > limit)) {
|
|
24023
|
+
const target = agents.filter((agent) => agent.id !== task.assigned_to).sort((a, b) => (load.get(a.id) ?? 0) - (load.get(b.id) ?? 0))[0];
|
|
24024
|
+
if (!target || (load.get(target.id) ?? 0) >= limit) {
|
|
24025
|
+
skipped++;
|
|
24026
|
+
continue;
|
|
24027
|
+
}
|
|
24028
|
+
updateTask2(task.id, { assigned_to: target.id, version: task.version });
|
|
24029
|
+
load.set(task.assigned_to, (load.get(task.assigned_to) ?? 1) - 1);
|
|
24030
|
+
load.set(target.id, (load.get(target.id) ?? 0) + 1);
|
|
24031
|
+
moved++;
|
|
24032
|
+
}
|
|
24033
|
+
return { content: [{ type: "text", text: `Rebalanced: moved ${moved} task(s), ${skipped} skipped.` }] };
|
|
22247
24034
|
} catch (e) {
|
|
22248
24035
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
22249
24036
|
}
|
|
@@ -22256,13 +24043,13 @@ function registerTaskAutoTools(server, ctx) {
|
|
|
22256
24043
|
agent_id: exports_external.string().optional().describe("Filter by assignee")
|
|
22257
24044
|
}, async ({ hours = 24, project_id, agent_id }) => {
|
|
22258
24045
|
try {
|
|
22259
|
-
const { notifyUpcomingDeadlines } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
24046
|
+
const { notifyUpcomingDeadlines: notifyUpcomingDeadlines2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
22260
24047
|
const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
|
|
22261
24048
|
const resolvedAgentId = agent_id ? resolveId(agent_id, "agents") : undefined;
|
|
22262
|
-
const tasks =
|
|
24049
|
+
const tasks = notifyUpcomingDeadlines2({ hours, project_id: resolvedProjectId, agent_id: resolvedAgentId });
|
|
22263
24050
|
if (tasks.length === 0)
|
|
22264
24051
|
return { content: [{ type: "text", text: "No deadlines approaching." }] };
|
|
22265
|
-
const lines = tasks.map((t) => `${t.short_id || t.id.slice(0, 8)} ${t.title} due ${t.
|
|
24052
|
+
const lines = tasks.map((t) => `${t.short_id || t.id.slice(0, 8)} ${t.title} due ${t.due_at}`);
|
|
22266
24053
|
return { content: [{ type: "text", text: `${tasks.length} task(s) due within ${hours}h:
|
|
22267
24054
|
${lines.join(`
|
|
22268
24055
|
`)}` }] };
|
|
@@ -22296,9 +24083,9 @@ ${lines.join(`
|
|
|
22296
24083
|
project_id: exports_external.string().optional().describe("Filter by project")
|
|
22297
24084
|
}, async ({ project_id }) => {
|
|
22298
24085
|
try {
|
|
22299
|
-
const { getBlockedTasks } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
24086
|
+
const { getBlockedTasks: getBlockedTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
22300
24087
|
const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
|
|
22301
|
-
const tasks =
|
|
24088
|
+
const tasks = getBlockedTasks2(resolvedProjectId);
|
|
22302
24089
|
if (tasks.length === 0)
|
|
22303
24090
|
return { content: [{ type: "text", text: "No blocked tasks." }] };
|
|
22304
24091
|
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(", ")}`);
|
|
@@ -22315,9 +24102,9 @@ ${lines.join(`
|
|
|
22315
24102
|
project_id: exports_external.string().optional().describe("Filter by project")
|
|
22316
24103
|
}, async ({ project_id }) => {
|
|
22317
24104
|
try {
|
|
22318
|
-
const { getBlockingTasks } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
24105
|
+
const { getBlockingTasks: getBlockingTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
22319
24106
|
const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
|
|
22320
|
-
const tasks =
|
|
24107
|
+
const tasks = getBlockingTasks2(resolvedProjectId);
|
|
22321
24108
|
if (tasks.length === 0)
|
|
22322
24109
|
return { content: [{ type: "text", text: "No tasks blocking others." }] };
|
|
22323
24110
|
const lines = tasks.map((t) => `${t.short_id || t.id.slice(0, 8)} [${t.status}] ${t.title} \u2014 blocking ${t.blocking_count} task(s)`);
|
|
@@ -22332,20 +24119,18 @@ ${lines.join(`
|
|
|
22332
24119
|
if (shouldRegisterTool("get_health")) {
|
|
22333
24120
|
server.tool("get_health", "Get system health: task counts by status, active agents, project summary.", async () => {
|
|
22334
24121
|
try {
|
|
22335
|
-
const {
|
|
22336
|
-
const { listProjects: listProjects2 } = (
|
|
24122
|
+
const { countTasks: countTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
24123
|
+
const { listProjects: listProjects2 } = (init_projects(), __toCommonJS(exports_projects));
|
|
22337
24124
|
const { listAgents: listAgents2 } = (init_agents(), __toCommonJS(exports_agents));
|
|
22338
|
-
const
|
|
22339
|
-
|
|
22340
|
-
|
|
22341
|
-
|
|
22342
|
-
|
|
22343
|
-
|
|
22344
|
-
const projects = listProjects2({ limit: 100 });
|
|
22345
|
-
const agents = listAgents2({ limit: 100 });
|
|
24125
|
+
const pending = countTasks2({ status: "pending" });
|
|
24126
|
+
const inProgress = countTasks2({ status: "in_progress" });
|
|
24127
|
+
const completed = countTasks2({ status: "completed" });
|
|
24128
|
+
const cancelled = countTasks2({ status: "cancelled" });
|
|
24129
|
+
const projects = listProjects2();
|
|
24130
|
+
const agents = listAgents2();
|
|
22346
24131
|
const lines = [
|
|
22347
24132
|
`=== System Health ===`,
|
|
22348
|
-
`Tasks: ${pending
|
|
24133
|
+
`Tasks: ${pending} pending | ${inProgress} in progress | ${completed} completed | ${cancelled} cancelled`,
|
|
22349
24134
|
`Projects: ${projects.length} total`,
|
|
22350
24135
|
`Agents: ${agents.length} registered`
|
|
22351
24136
|
];
|
|
@@ -22362,17 +24147,31 @@ ${lines.join(`
|
|
|
22362
24147
|
function registerTaskAdvTools(server, ctx) {
|
|
22363
24148
|
const { shouldRegisterTool, resolveId, formatError, formatTask, formatTaskDetail } = ctx;
|
|
22364
24149
|
if (shouldRegisterTool("get_status")) {
|
|
22365
|
-
server.tool("get_status", "Get
|
|
22366
|
-
task_id: exports_external.string().describe("Task ID")
|
|
22367
|
-
|
|
24150
|
+
server.tool("get_status", "Get queue status summary, or pass task_id for a task's detailed status.", {
|
|
24151
|
+
task_id: exports_external.string().optional().describe("Task ID for task-specific status"),
|
|
24152
|
+
project_id: exports_external.string().optional().describe("Filter summary by project"),
|
|
24153
|
+
task_list_id: exports_external.string().optional().describe("Filter summary by task list"),
|
|
24154
|
+
agent_id: exports_external.string().optional().describe("Agent for next-task affinity"),
|
|
24155
|
+
explain_blocked: exports_external.boolean().optional().describe("Include blocked task explanations in summary")
|
|
24156
|
+
}, async ({ task_id, project_id, task_list_id, agent_id, explain_blocked }) => {
|
|
22368
24157
|
try {
|
|
24158
|
+
if (!task_id) {
|
|
24159
|
+
const { getStatus: getStatus2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
24160
|
+
const filters = {};
|
|
24161
|
+
if (project_id)
|
|
24162
|
+
filters.project_id = resolveId(project_id, "projects");
|
|
24163
|
+
if (task_list_id)
|
|
24164
|
+
filters.task_list_id = resolveId(task_list_id, "task_lists");
|
|
24165
|
+
const status = getStatus2(filters, agent_id, { explain_blocked });
|
|
24166
|
+
return { content: [{ type: "text", text: JSON.stringify(status, null, 2) }] };
|
|
24167
|
+
}
|
|
22369
24168
|
const resolvedId = resolveId(task_id);
|
|
22370
|
-
const { getTask:
|
|
22371
|
-
const task =
|
|
24169
|
+
const { getTask: getTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
24170
|
+
const task = getTask2(resolvedId);
|
|
22372
24171
|
if (!task)
|
|
22373
24172
|
throw new Error(`Task not found: ${task_id}`);
|
|
22374
24173
|
const { getTaskDependencies: getTaskDependencies3 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
22375
|
-
const { listComments: listComments2 } = (
|
|
24174
|
+
const { listComments: listComments2 } = (init_comments(), __toCommonJS(exports_comments));
|
|
22376
24175
|
const { listTaskFiles: listTaskFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
|
|
22377
24176
|
const [deps, comments, files] = await Promise.all([
|
|
22378
24177
|
Promise.resolve(getTaskDependencies3(resolvedId, "both")),
|
|
@@ -22382,14 +24181,14 @@ function registerTaskAdvTools(server, ctx) {
|
|
|
22382
24181
|
const lines = [
|
|
22383
24182
|
`Status: ${task.status} | Priority: ${task.priority}`,
|
|
22384
24183
|
task.assigned_to ? `Assigned: ${task.assigned_to}` : "Unassigned",
|
|
22385
|
-
task.
|
|
24184
|
+
task.due_at ? `Due: ${task.due_at}` : null,
|
|
22386
24185
|
task.confidence != null ? `Confidence: ${task.confidence}` : null,
|
|
22387
24186
|
deps.length > 0 ? `
|
|
22388
24187
|
Dependencies (${deps.length}):` : null,
|
|
22389
24188
|
...deps.map((d) => ` [${d.direction}] ${d.task_id.slice(0, 8)} (${d.status})`),
|
|
22390
24189
|
comments.length > 0 ? `
|
|
22391
24190
|
Comments (${comments.length}):` : null,
|
|
22392
|
-
...comments.map((c) => ` [${c.
|
|
24191
|
+
...comments.map((c) => ` [${c.agent_id || "?"}] ${c.created_at?.slice(0, 16)}: ${c.content.slice(0, 80)}`),
|
|
22393
24192
|
files.length > 0 ? `
|
|
22394
24193
|
Files (${files.length}):` : null,
|
|
22395
24194
|
...files.map((f) => ` ${f.status} ${f.path}`)
|
|
@@ -22407,22 +24206,22 @@ Files (${files.length}):` : null,
|
|
|
22407
24206
|
}, async ({ task_id }) => {
|
|
22408
24207
|
try {
|
|
22409
24208
|
const resolvedId = resolveId(task_id);
|
|
22410
|
-
const { getTask:
|
|
22411
|
-
const task =
|
|
24209
|
+
const { getTask: getTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
24210
|
+
const task = getTask2(resolvedId);
|
|
22412
24211
|
if (!task)
|
|
22413
24212
|
throw new Error(`Task not found: ${task_id}`);
|
|
22414
24213
|
const { getTaskDependencies: getTaskDependencies3 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
22415
24214
|
const { getTaskRelationships: getTaskRelationships2 } = (init_task_relationships(), __toCommonJS(exports_task_relationships));
|
|
22416
|
-
const { listComments: listComments2 } = (
|
|
24215
|
+
const { listComments: listComments2 } = (init_comments(), __toCommonJS(exports_comments));
|
|
22417
24216
|
const { listTaskFiles: listTaskFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
|
|
22418
|
-
const { getTaskCommits } = (
|
|
24217
|
+
const { getTaskCommits: getTaskCommits2 } = (init_task_commits(), __toCommonJS(exports_task_commits));
|
|
22419
24218
|
const { getTaskWatchers: getTaskWatchers2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
22420
24219
|
const [deps, rels, comments, files, commits, watchers] = await Promise.all([
|
|
22421
24220
|
Promise.resolve(getTaskDependencies3(resolvedId, "both")),
|
|
22422
24221
|
Promise.resolve(getTaskRelationships2(resolvedId)),
|
|
22423
24222
|
Promise.resolve(listComments2(resolvedId)),
|
|
22424
24223
|
Promise.resolve(listTaskFiles2(resolvedId)),
|
|
22425
|
-
Promise.resolve(
|
|
24224
|
+
Promise.resolve(getTaskCommits2(resolvedId)),
|
|
22426
24225
|
Promise.resolve(getTaskWatchers2(resolvedId))
|
|
22427
24226
|
]);
|
|
22428
24227
|
const lines = [
|
|
@@ -22436,7 +24235,7 @@ Files (${files.length}):` : null,
|
|
|
22436
24235
|
task.tags?.length ? `Tags: ${task.tags.join(", ")}` : null,
|
|
22437
24236
|
task.created_at ? `Created: ${task.created_at}` : null,
|
|
22438
24237
|
task.updated_at ? `Updated: ${task.updated_at}` : null,
|
|
22439
|
-
task.
|
|
24238
|
+
task.due_at ? `Due: ${task.due_at}` : null,
|
|
22440
24239
|
task.completed_at ? `Completed: ${task.completed_at}` : null,
|
|
22441
24240
|
deps.length > 0 ? `
|
|
22442
24241
|
--- Dependencies (${deps.length}) ---` : null,
|
|
@@ -22446,7 +24245,7 @@ Files (${files.length}):` : null,
|
|
|
22446
24245
|
...rels.map((r) => ` ${r.source_task_id.slice(0, 8)} --[${r.relationship_type}]--> ${r.target_task_id.slice(0, 8)}`),
|
|
22447
24246
|
comments.length > 0 ? `
|
|
22448
24247
|
--- Comments (${comments.length}) ---` : null,
|
|
22449
|
-
...comments.map((c) => ` [${c.
|
|
24248
|
+
...comments.map((c) => ` [${c.agent_id || "?"}] ${c.created_at?.slice(0, 16)}: ${c.content.slice(0, 120)}`),
|
|
22450
24249
|
files.length > 0 ? `
|
|
22451
24250
|
--- Files (${files.length}) ---` : null,
|
|
22452
24251
|
...files.map((f) => ` [${f.status}] ${f.path}`),
|
|
@@ -22495,8 +24294,8 @@ ${JSON.stringify(task.metadata, null, 2)}` : null
|
|
|
22495
24294
|
limit: 20
|
|
22496
24295
|
}, undefined);
|
|
22497
24296
|
const completedYesterday = completed.filter((t) => t.completed_at && t.completed_at.startsWith(yesterdayStr));
|
|
22498
|
-
const { getBlockedTasks } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
22499
|
-
const blocked =
|
|
24297
|
+
const { getBlockedTasks: getBlockedTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
24298
|
+
const blocked = getBlockedTasks2(effectiveProjectId ? resolveId(effectiveProjectId, "projects") : undefined).filter((t) => t.assigned_to === effectiveAgentId);
|
|
22500
24299
|
const lines = [
|
|
22501
24300
|
`Standup for ${effectiveAgentId} (${effectiveProjectId ? `project: ${effectiveProjectId.slice(0, 8)}` : "all projects"})`,
|
|
22502
24301
|
inProgress.length > 0 ? `
|
|
@@ -22525,11 +24324,11 @@ No blocked tasks.`,
|
|
|
22525
24324
|
agent_id: exports_external.string().optional().describe("Agent claiming (defaults to context)")
|
|
22526
24325
|
}, async ({ task_id, agent_id }) => {
|
|
22527
24326
|
try {
|
|
22528
|
-
const {
|
|
24327
|
+
const { startTask: startTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
22529
24328
|
const resolvedId = resolveId(task_id);
|
|
22530
24329
|
const focus = ctx.getAgentFocus(agent_id || "");
|
|
22531
|
-
const effectiveAgent = focus ? focus.agent_id : agent_id || "";
|
|
22532
|
-
const task =
|
|
24330
|
+
const effectiveAgent = focus ? focus.agent_id : agent_id || "mcp";
|
|
24331
|
+
const task = startTask2(resolvedId, effectiveAgent);
|
|
22533
24332
|
return { content: [{ type: "text", text: formatTask(task) }] };
|
|
22534
24333
|
} catch (e) {
|
|
22535
24334
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -22541,211 +24340,95 @@ No blocked tasks.`,
|
|
|
22541
24340
|
task_id: exports_external.string().describe("Task ID")
|
|
22542
24341
|
}, async ({ task_id }) => {
|
|
22543
24342
|
try {
|
|
22544
|
-
const { updateTask: updateTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
22545
|
-
const task = updateTask2(resolveId(task_id), { status: "pending", assigned_to: null });
|
|
22546
|
-
return { content: [{ type: "text", text: formatTask(task) }] };
|
|
22547
|
-
} catch (e) {
|
|
22548
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
22549
|
-
}
|
|
22550
|
-
});
|
|
22551
|
-
}
|
|
22552
|
-
if (shouldRegisterTool("extend_task")) {
|
|
22553
|
-
server.tool("extend_task", "Add more time to a task's estimate.", {
|
|
22554
|
-
task_id: exports_external.string().describe("Task ID"),
|
|
22555
|
-
minutes: exports_external.number().describe("Additional minutes to add to estimate")
|
|
22556
|
-
}, async ({ task_id, minutes }) => {
|
|
22557
|
-
try {
|
|
22558
|
-
const { getTask: getTask4, updateTask: updateTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
24343
|
+
const { getTask: getTask2, updateTask: updateTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
22559
24344
|
const resolvedId = resolveId(task_id);
|
|
22560
|
-
const
|
|
22561
|
-
if (!
|
|
24345
|
+
const current = getTask2(resolvedId);
|
|
24346
|
+
if (!current)
|
|
22562
24347
|
throw new Error(`Task not found: ${task_id}`);
|
|
22563
|
-
const
|
|
22564
|
-
|
|
22565
|
-
return { content: [{ type: "text", text: `Estimate updated: ${currentEstimate} \u2192 ${updated.estimated_minutes} min (+${minutes})` }] };
|
|
22566
|
-
} catch (e) {
|
|
22567
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
22568
|
-
}
|
|
22569
|
-
});
|
|
22570
|
-
}
|
|
22571
|
-
if (shouldRegisterTool("add_comment")) {
|
|
22572
|
-
server.tool("add_comment", "Alias for create_comment.", {
|
|
22573
|
-
task_id: exports_external.string().describe("Task ID"),
|
|
22574
|
-
body: exports_external.string().describe("Comment body"),
|
|
22575
|
-
author: exports_external.string().optional().describe("Author agent ID or name")
|
|
22576
|
-
}, async ({ task_id, body, author }) => {
|
|
22577
|
-
try {
|
|
22578
|
-
const { createComment } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
22579
|
-
const resolvedId = resolveId(task_id);
|
|
22580
|
-
const resolvedAuthor = author ? resolveId(author, "agents") : undefined;
|
|
22581
|
-
createComment({ task_id: resolvedId, body, author: resolvedAuthor });
|
|
22582
|
-
return { content: [{ type: "text", text: `Comment added to ${task_id.slice(0, 8)}` }] };
|
|
22583
|
-
} catch (e) {
|
|
22584
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
22585
|
-
}
|
|
22586
|
-
});
|
|
22587
|
-
}
|
|
22588
|
-
if (shouldRegisterTool("get_comments")) {
|
|
22589
|
-
server.tool("get_comments", "Alias for list_comments.", {
|
|
22590
|
-
task_id: exports_external.string().describe("Task ID")
|
|
22591
|
-
}, async ({ task_id }) => {
|
|
22592
|
-
try {
|
|
22593
|
-
const { listComments: listComments2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
22594
|
-
const comments = listComments2(resolveId(task_id));
|
|
22595
|
-
if (comments.length === 0)
|
|
22596
|
-
return { content: [{ type: "text", text: "No comments." }] };
|
|
22597
|
-
const lines = comments.map((c) => `[${c.author || "?"}] ${c.created_at?.slice(0, 16)}: ${c.body}`);
|
|
22598
|
-
return { content: [{ type: "text", text: lines.join(`
|
|
22599
|
-
|
|
22600
|
-
`) }] };
|
|
22601
|
-
} catch (e) {
|
|
22602
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
22603
|
-
}
|
|
22604
|
-
});
|
|
22605
|
-
}
|
|
22606
|
-
if (shouldRegisterTool("list_my_tasks")) {
|
|
22607
|
-
server.tool("list_my_tasks", "Alias for get_my_tasks.", {
|
|
22608
|
-
agent_id: exports_external.string().optional().describe("Agent ID (defaults to context agent)"),
|
|
22609
|
-
status: exports_external.enum(["pending", "in_progress", "completed", "cancelled"]).optional(),
|
|
22610
|
-
project_id: exports_external.string().optional().describe("Filter by project"),
|
|
22611
|
-
limit: exports_external.number().optional()
|
|
22612
|
-
}, async ({ agent_id, status, project_id, limit }) => {
|
|
22613
|
-
try {
|
|
22614
|
-
const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
22615
|
-
const focus = ctx.getAgentFocus(agent_id || "");
|
|
22616
|
-
const effectiveAgentId = focus ? focus.agent_id : agent_id || "";
|
|
22617
|
-
const effectiveProjectId = focus?.project_id || project_id;
|
|
22618
|
-
const tasks = listTasks3({
|
|
22619
|
-
assigned_to: effectiveAgentId,
|
|
22620
|
-
status,
|
|
22621
|
-
project_id: effectiveProjectId ? resolveId(effectiveProjectId, "projects") : undefined,
|
|
22622
|
-
limit: limit || 50
|
|
22623
|
-
}, undefined);
|
|
22624
|
-
if (tasks.length === 0)
|
|
22625
|
-
return { content: [{ type: "text", text: "No tasks found." }] };
|
|
22626
|
-
const lines = tasks.map(formatTask);
|
|
22627
|
-
return { content: [{ type: "text", text: lines.join(`
|
|
22628
|
-
`) }] };
|
|
22629
|
-
} catch (e) {
|
|
22630
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
22631
|
-
}
|
|
22632
|
-
});
|
|
22633
|
-
}
|
|
22634
|
-
if (shouldRegisterTool("list_agents")) {
|
|
22635
|
-
server.tool("list_agents", "List all registered agents.", {
|
|
22636
|
-
project_id: exports_external.string().optional().describe("Filter by project"),
|
|
22637
|
-
role: exports_external.string().optional().describe("Filter by global role"),
|
|
22638
|
-
capabilities: exports_external.array(exports_external.string()).optional().describe("Filter by capabilities")
|
|
22639
|
-
}, async ({ project_id, role, capabilities }) => {
|
|
22640
|
-
try {
|
|
22641
|
-
const { listAgents: listAgents2 } = (init_agents(), __toCommonJS(exports_agents));
|
|
22642
|
-
const resolved = {};
|
|
22643
|
-
if (project_id)
|
|
22644
|
-
resolved.project_id = resolveId(project_id, "projects");
|
|
22645
|
-
if (role)
|
|
22646
|
-
resolved.role = role;
|
|
22647
|
-
if (capabilities)
|
|
22648
|
-
resolved.capabilities = capabilities;
|
|
22649
|
-
const agents = listAgents2(resolved);
|
|
22650
|
-
if (agents.length === 0)
|
|
22651
|
-
return { content: [{ type: "text", text: "No agents found." }] };
|
|
22652
|
-
const lines = agents.map((a) => `\u25CF ${a.name} [${a.role || "no role"}]${a.capabilities?.length ? ` caps:[${a.capabilities.join(",")}]` : ""}`);
|
|
22653
|
-
return { content: [{ type: "text", text: lines.join(`
|
|
22654
|
-
`) }] };
|
|
22655
|
-
} catch (e) {
|
|
22656
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
22657
|
-
}
|
|
22658
|
-
});
|
|
22659
|
-
}
|
|
22660
|
-
if (shouldRegisterTool("get_agent")) {
|
|
22661
|
-
server.tool("get_agent", "Get full details for an agent.", {
|
|
22662
|
-
agent_id: exports_external.string().describe("Agent ID or name")
|
|
22663
|
-
}, async ({ agent_id }) => {
|
|
22664
|
-
try {
|
|
22665
|
-
const { getAgentByName: getAgentByName2, getAgent: getAgent2 } = (init_agents(), __toCommonJS(exports_agents));
|
|
22666
|
-
const resolvedId = resolveId(agent_id, "agents");
|
|
22667
|
-
const agent = agent_id.includes("@") || !resolvedId.startsWith("agent_") ? getAgentByName2(agent_id) : getAgent2(resolvedId);
|
|
22668
|
-
if (!agent)
|
|
22669
|
-
return { content: [{ type: "text", text: `Agent not found: ${agent_id}` }], isError: true };
|
|
22670
|
-
const lines = [
|
|
22671
|
-
`ID: ${agent.id}`,
|
|
22672
|
-
`Name: ${agent.name}`,
|
|
22673
|
-
agent.email ? `Email: ${agent.email}` : null,
|
|
22674
|
-
agent.title ? `Title: ${agent.title}` : null,
|
|
22675
|
-
agent.role ? `Role: ${agent.role}` : null,
|
|
22676
|
-
agent.capabilities?.length ? `Capabilities: ${agent.capabilities.join(", ")}` : null,
|
|
22677
|
-
agent.last_seen_at ? `Last Seen: ${agent.last_seen_at}` : null
|
|
22678
|
-
].filter(Boolean);
|
|
22679
|
-
return { content: [{ type: "text", text: lines.join(`
|
|
22680
|
-
`) }] };
|
|
24348
|
+
const task = updateTask2(resolvedId, { status: "pending", assigned_to: null, version: current.version });
|
|
24349
|
+
return { content: [{ type: "text", text: formatTask(task) }] };
|
|
22681
24350
|
} catch (e) {
|
|
22682
24351
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
22683
24352
|
}
|
|
22684
|
-
});
|
|
22685
|
-
}
|
|
22686
|
-
if (shouldRegisterTool("
|
|
22687
|
-
server.tool("
|
|
22688
|
-
|
|
22689
|
-
|
|
22690
|
-
|
|
22691
|
-
title: exports_external.string().optional(),
|
|
22692
|
-
role: exports_external.string().optional(),
|
|
22693
|
-
capabilities: exports_external.array(exports_external.string()).optional()
|
|
22694
|
-
}, async ({ agent_id, ...updates }) => {
|
|
24353
|
+
});
|
|
24354
|
+
}
|
|
24355
|
+
if (shouldRegisterTool("extend_task")) {
|
|
24356
|
+
server.tool("extend_task", "Add more time to a task's estimate.", {
|
|
24357
|
+
task_id: exports_external.string().describe("Task ID"),
|
|
24358
|
+
minutes: exports_external.number().describe("Additional minutes to add to estimate")
|
|
24359
|
+
}, async ({ task_id, minutes }) => {
|
|
22695
24360
|
try {
|
|
22696
|
-
const {
|
|
22697
|
-
const resolvedId = resolveId(
|
|
22698
|
-
const
|
|
22699
|
-
|
|
22700
|
-
|
|
24361
|
+
const { getTask: getTask2, updateTask: updateTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
24362
|
+
const resolvedId = resolveId(task_id);
|
|
24363
|
+
const task = getTask2(resolvedId);
|
|
24364
|
+
if (!task)
|
|
24365
|
+
throw new Error(`Task not found: ${task_id}`);
|
|
24366
|
+
const currentEstimate = task.estimated_minutes || 0;
|
|
24367
|
+
const updated = updateTask2(resolvedId, { estimated_minutes: currentEstimate + minutes, version: task.version });
|
|
24368
|
+
return { content: [{ type: "text", text: `Estimate updated: ${currentEstimate} \u2192 ${updated.estimated_minutes} min (+${minutes})` }] };
|
|
22701
24369
|
} catch (e) {
|
|
22702
24370
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
22703
24371
|
}
|
|
22704
24372
|
});
|
|
22705
24373
|
}
|
|
22706
|
-
if (shouldRegisterTool("
|
|
22707
|
-
server.tool("
|
|
22708
|
-
|
|
22709
|
-
|
|
24374
|
+
if (shouldRegisterTool("add_comment")) {
|
|
24375
|
+
server.tool("add_comment", "Alias for create_comment.", {
|
|
24376
|
+
task_id: exports_external.string().describe("Task ID"),
|
|
24377
|
+
body: exports_external.string().describe("Comment body"),
|
|
24378
|
+
author: exports_external.string().optional().describe("Author agent ID or name")
|
|
24379
|
+
}, async ({ task_id, body, author }) => {
|
|
22710
24380
|
try {
|
|
22711
|
-
const {
|
|
22712
|
-
const resolvedId = resolveId(
|
|
22713
|
-
const
|
|
22714
|
-
|
|
22715
|
-
|
|
22716
|
-
deleteAgent2(agent.id);
|
|
22717
|
-
return { content: [{ type: "text", text: `Agent ${agent_id} deleted.` }] };
|
|
24381
|
+
const { addComment: addComment2 } = (init_comments(), __toCommonJS(exports_comments));
|
|
24382
|
+
const resolvedId = resolveId(task_id);
|
|
24383
|
+
const resolvedAuthor = author ? resolveId(author, "agents") : undefined;
|
|
24384
|
+
addComment2({ task_id: resolvedId, content: body, agent_id: resolvedAuthor });
|
|
24385
|
+
return { content: [{ type: "text", text: `Comment added to ${task_id.slice(0, 8)}` }] };
|
|
22718
24386
|
} catch (e) {
|
|
22719
24387
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
22720
24388
|
}
|
|
22721
24389
|
});
|
|
22722
24390
|
}
|
|
22723
|
-
if (shouldRegisterTool("
|
|
22724
|
-
server.tool("
|
|
22725
|
-
|
|
22726
|
-
|
|
22727
|
-
title: exports_external.string().optional(),
|
|
22728
|
-
role: exports_external.string().optional(),
|
|
22729
|
-
capabilities: exports_external.array(exports_external.string()).optional()
|
|
22730
|
-
}, async ({ name, email, title, role, capabilities }) => {
|
|
24391
|
+
if (shouldRegisterTool("get_comments")) {
|
|
24392
|
+
server.tool("get_comments", "Alias for list_comments.", {
|
|
24393
|
+
task_id: exports_external.string().describe("Task ID")
|
|
24394
|
+
}, async ({ task_id }) => {
|
|
22731
24395
|
try {
|
|
22732
|
-
const {
|
|
22733
|
-
const
|
|
22734
|
-
|
|
24396
|
+
const { listComments: listComments2 } = (init_comments(), __toCommonJS(exports_comments));
|
|
24397
|
+
const comments = listComments2(resolveId(task_id));
|
|
24398
|
+
if (comments.length === 0)
|
|
24399
|
+
return { content: [{ type: "text", text: "No comments." }] };
|
|
24400
|
+
const lines = comments.map((c) => `[${c.agent_id || "?"}] ${c.created_at?.slice(0, 16)}: ${c.content}`);
|
|
24401
|
+
return { content: [{ type: "text", text: lines.join(`
|
|
24402
|
+
|
|
24403
|
+
`) }] };
|
|
22735
24404
|
} catch (e) {
|
|
22736
24405
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
22737
24406
|
}
|
|
22738
24407
|
});
|
|
22739
24408
|
}
|
|
22740
|
-
if (shouldRegisterTool("
|
|
22741
|
-
server.tool("
|
|
22742
|
-
agent_id: exports_external.string().describe("Agent ID"),
|
|
22743
|
-
status: exports_external.
|
|
22744
|
-
|
|
24409
|
+
if (shouldRegisterTool("list_my_tasks")) {
|
|
24410
|
+
server.tool("list_my_tasks", "Alias for get_my_tasks.", {
|
|
24411
|
+
agent_id: exports_external.string().optional().describe("Agent ID (defaults to context agent)"),
|
|
24412
|
+
status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
|
|
24413
|
+
project_id: exports_external.string().optional().describe("Filter by project"),
|
|
24414
|
+
limit: exports_external.number().optional()
|
|
24415
|
+
}, async ({ agent_id, status, project_id, limit }) => {
|
|
22745
24416
|
try {
|
|
22746
|
-
const {
|
|
22747
|
-
|
|
22748
|
-
|
|
24417
|
+
const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
24418
|
+
const focus = ctx.getAgentFocus(agent_id || "");
|
|
24419
|
+
const effectiveAgentId = focus ? focus.agent_id : agent_id || "";
|
|
24420
|
+
const effectiveProjectId = focus?.project_id || project_id;
|
|
24421
|
+
const tasks = listTasks3({
|
|
24422
|
+
assigned_to: effectiveAgentId,
|
|
24423
|
+
status,
|
|
24424
|
+
project_id: effectiveProjectId ? resolveId(effectiveProjectId, "projects") : undefined,
|
|
24425
|
+
limit: limit || 50
|
|
24426
|
+
}, undefined);
|
|
24427
|
+
if (tasks.length === 0)
|
|
24428
|
+
return { content: [{ type: "text", text: "No tasks found." }] };
|
|
24429
|
+
const lines = tasks.map(formatTask);
|
|
24430
|
+
return { content: [{ type: "text", text: lines.join(`
|
|
24431
|
+
`) }] };
|
|
22749
24432
|
} catch (e) {
|
|
22750
24433
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
22751
24434
|
}
|
|
@@ -22792,6 +24475,11 @@ function registerTaskMetaTools(server, ctx) {
|
|
|
22792
24475
|
prioritize_task: "prioritize_task \u2014 Set priority. Params: task_id, priority, version",
|
|
22793
24476
|
search_tasks: "search_tasks \u2014 Full-text search. Params: query, project_id, status, limit",
|
|
22794
24477
|
get_my_tasks: "get_my_tasks \u2014 Get tasks for calling agent. Params: agent_id, status, project_id, limit",
|
|
24478
|
+
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",
|
|
24479
|
+
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",
|
|
24480
|
+
get_tasks_changed_since: "get_tasks_changed_since \u2014 List tasks changed since an ISO timestamp. Params: since, project_id, task_list_id, limit",
|
|
24481
|
+
get_context: "get_context \u2014 Get session start context. Params: agent_id, project_id, task_list_id, explain_blocked",
|
|
24482
|
+
bootstrap: "bootstrap \u2014 Bootstrap an agent session with queue context. Params: agent_id, project_id, task_list_id, explain_blocked",
|
|
22795
24483
|
standup: "standup \u2014 Get standup report. Params: agent_id, project_id",
|
|
22796
24484
|
patrol_tasks: "patrol_tasks \u2014 Scan for task issues. Params: stuck_minutes, confidence_threshold, project_id",
|
|
22797
24485
|
get_review_queue: "get_review_queue \u2014 Get tasks needing review. Params: project_id, limit",
|
|
@@ -22849,9 +24537,20 @@ function registerTaskMetaTools(server, ctx) {
|
|
|
22849
24537
|
get_health: "get_health \u2014 Get system health stats",
|
|
22850
24538
|
approve_task: "approve_task \u2014 Approve a task. Params: task_id, approved_by, notes, version",
|
|
22851
24539
|
fail_task: "fail_task \u2014 Mark task failed. Params: task_id, reason, agent_id, version",
|
|
24540
|
+
register_agent: "register_agent \u2014 Register an agent. Params: name, description, role, title, capabilities, session_id, working_dir, force",
|
|
24541
|
+
list_agents: "list_agents \u2014 List registered agents. Params: include_archived",
|
|
24542
|
+
get_agent: "get_agent \u2014 Get agent details. Params: agent_id, id, name",
|
|
24543
|
+
update_agent: "update_agent \u2014 Update an agent. Params: agent_id, id, name, description, role, title, level, capabilities, permissions, metadata",
|
|
24544
|
+
delete_agent: "delete_agent \u2014 Archive an agent. Params: agent_id, id, name",
|
|
24545
|
+
unarchive_agent: "unarchive_agent \u2014 Restore an archived agent. Params: agent_id, id, name",
|
|
24546
|
+
heartbeat: "heartbeat \u2014 Update an agent heartbeat. Params: agent_id",
|
|
24547
|
+
release_agent: "release_agent \u2014 Release an agent session/name. Params: agent_id, session_id",
|
|
24548
|
+
set_focus: "set_focus \u2014 Focus an agent on a project. Params: agent_id, project_id, task_list_id",
|
|
24549
|
+
get_focus: "get_focus \u2014 Get current agent focus. Params: agent_id",
|
|
24550
|
+
unfocus: "unfocus \u2014 Clear agent focus. Params: agent_id",
|
|
24551
|
+
suggest_agent_name: "suggest_agent_name \u2014 Suggest available agent names. Params: working_dir",
|
|
22852
24552
|
get_org_chart: "get_org_chart \u2014 Get global org chart. Params: format",
|
|
22853
24553
|
set_reports_to: "set_reports_to \u2014 Set org hierarchy. Params: agent_id, reports_to",
|
|
22854
|
-
sync: "sync \u2014 Sync from external source. Params: source, source_id, project_id, options",
|
|
22855
24554
|
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",
|
|
22856
24555
|
migrate_pg: "migrate_pg \u2014 Apply PostgreSQL migrations. Params: connection_string"
|
|
22857
24556
|
};
|
|
@@ -22870,39 +24569,7 @@ function registerTaskMetaTools(server, ctx) {
|
|
|
22870
24569
|
init_tasks();
|
|
22871
24570
|
init_projects();
|
|
22872
24571
|
init_agents();
|
|
22873
|
-
|
|
22874
|
-
// src/db/task-commits.ts
|
|
22875
|
-
init_database();
|
|
22876
|
-
function rowToCommit(row) {
|
|
22877
|
-
return {
|
|
22878
|
-
...row,
|
|
22879
|
-
files_changed: row.files_changed ? JSON.parse(row.files_changed) : null
|
|
22880
|
-
};
|
|
22881
|
-
}
|
|
22882
|
-
function linkTaskToCommit(input, db) {
|
|
22883
|
-
const d = db || getDatabase();
|
|
22884
|
-
const existing = d.query("SELECT * FROM task_commits WHERE task_id = ? AND sha = ?").get(input.task_id, input.sha);
|
|
22885
|
-
if (existing) {
|
|
22886
|
-
d.run("UPDATE task_commits SET message = COALESCE(?, message), author = COALESCE(?, author), files_changed = COALESCE(?, files_changed), committed_at = COALESCE(?, committed_at) WHERE id = ?", [input.message ?? null, input.author ?? null, input.files_changed ? JSON.stringify(input.files_changed) : null, input.committed_at ?? null, existing.id]);
|
|
22887
|
-
return rowToCommit(d.query("SELECT * FROM task_commits WHERE id = ?").get(existing.id));
|
|
22888
|
-
}
|
|
22889
|
-
const id = uuid();
|
|
22890
|
-
d.run("INSERT INTO task_commits (id, task_id, sha, message, author, files_changed, committed_at, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", [id, input.task_id, input.sha, input.message ?? null, input.author ?? null, input.files_changed ? JSON.stringify(input.files_changed) : null, input.committed_at ?? null, now()]);
|
|
22891
|
-
return rowToCommit(d.query("SELECT * FROM task_commits WHERE id = ?").get(id));
|
|
22892
|
-
}
|
|
22893
|
-
function getTaskCommits(taskId, db) {
|
|
22894
|
-
const d = db || getDatabase();
|
|
22895
|
-
return d.query("SELECT * FROM task_commits WHERE task_id = ? ORDER BY committed_at DESC, created_at DESC").all(taskId).map(rowToCommit);
|
|
22896
|
-
}
|
|
22897
|
-
function findTaskByCommit(sha, db) {
|
|
22898
|
-
const d = db || getDatabase();
|
|
22899
|
-
const row = d.query("SELECT * FROM task_commits WHERE sha = ? OR sha LIKE ? LIMIT 1").get(sha, `${sha}%`);
|
|
22900
|
-
if (!row)
|
|
22901
|
-
return null;
|
|
22902
|
-
return { task_id: row.task_id, commit: rowToCommit(row) };
|
|
22903
|
-
}
|
|
22904
|
-
|
|
22905
|
-
// src/mcp/tools/task-resources.ts
|
|
24572
|
+
init_task_commits();
|
|
22906
24573
|
function registerTaskResources(server, ctx) {
|
|
22907
24574
|
const { shouldRegisterTool, resolveId, formatError } = ctx;
|
|
22908
24575
|
server.resource("tasks", "todos://tasks", { description: "All active tasks", mimeType: "application/json" }, async () => {
|
|
@@ -22927,7 +24594,7 @@ function registerTaskResources(server, ctx) {
|
|
|
22927
24594
|
note: exports_external.string().optional().describe("Note about why this file is linked")
|
|
22928
24595
|
}, async ({ task_id, path, paths: multiplePaths, status, agent_id, note }) => {
|
|
22929
24596
|
try {
|
|
22930
|
-
const { addTaskFile: addTaskFile2, bulkAddTaskFiles: bulkAddTaskFiles2, detectFileConflicts: detectFileConflicts2 } = (()
|
|
24597
|
+
const { addTaskFile: addTaskFile2, bulkAddTaskFiles: bulkAddTaskFiles2, detectFileConflicts: detectFileConflicts2 } = (init_task_files(), __toCommonJS(exports_task_files));
|
|
22931
24598
|
const resolvedId = resolveId(task_id);
|
|
22932
24599
|
let addedFiles;
|
|
22933
24600
|
if (multiplePaths && multiplePaths.length > 0) {
|
|
@@ -22971,7 +24638,7 @@ function registerTaskResources(server, ctx) {
|
|
|
22971
24638
|
if (shouldRegisterTool("list_task_files")) {
|
|
22972
24639
|
server.tool("list_task_files", "List all files linked to a task.", { task_id: exports_external.string().describe("Task ID") }, async ({ task_id }) => {
|
|
22973
24640
|
try {
|
|
22974
|
-
const { listTaskFiles: listTaskFiles2 } = (()
|
|
24641
|
+
const { listTaskFiles: listTaskFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
|
|
22975
24642
|
const resolvedId = resolveId(task_id);
|
|
22976
24643
|
const files = listTaskFiles2(resolvedId);
|
|
22977
24644
|
if (files.length === 0)
|
|
@@ -22988,7 +24655,7 @@ ${lines.join(`
|
|
|
22988
24655
|
if (shouldRegisterTool("find_tasks_by_file")) {
|
|
22989
24656
|
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_external.string().describe("File path to search for") }, async ({ path }) => {
|
|
22990
24657
|
try {
|
|
22991
|
-
const { findTasksByFile: findTasksByFile2 } = (()
|
|
24658
|
+
const { findTasksByFile: findTasksByFile2 } = (init_task_files(), __toCommonJS(exports_task_files));
|
|
22992
24659
|
const files = findTasksByFile2(path);
|
|
22993
24660
|
if (files.length === 0)
|
|
22994
24661
|
return { content: [{ type: "text", text: `No tasks linked to ${path}` }] };
|
|
@@ -23008,7 +24675,7 @@ ${lines.join(`
|
|
|
23008
24675
|
min_edits: exports_external.number().optional().describe("Minimum edit count to include (default: 1)")
|
|
23009
24676
|
}, async ({ limit, project_id, min_edits }) => {
|
|
23010
24677
|
try {
|
|
23011
|
-
const { getFileHeatMap: getFileHeatMap2 } = (()
|
|
24678
|
+
const { getFileHeatMap: getFileHeatMap2 } = (init_task_files(), __toCommonJS(exports_task_files));
|
|
23012
24679
|
const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
|
|
23013
24680
|
const results = getFileHeatMap2({ limit, project_id: resolvedProjectId, min_edits });
|
|
23014
24681
|
return { content: [{ type: "text", text: JSON.stringify(results, null, 2) }] };
|
|
@@ -23022,7 +24689,7 @@ ${lines.join(`
|
|
|
23022
24689
|
paths: exports_external.array(exports_external.string()).describe("Array of file paths to check")
|
|
23023
24690
|
}, async ({ paths }) => {
|
|
23024
24691
|
try {
|
|
23025
|
-
const { bulkFindTasksByFiles: bulkFindTasksByFiles2 } = (()
|
|
24692
|
+
const { bulkFindTasksByFiles: bulkFindTasksByFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
|
|
23026
24693
|
const results = bulkFindTasksByFiles2(paths);
|
|
23027
24694
|
return { content: [{ type: "text", text: JSON.stringify(results, null, 2) }] };
|
|
23028
24695
|
} catch (e) {
|
|
@@ -23035,11 +24702,11 @@ ${lines.join(`
|
|
|
23035
24702
|
project_id: exports_external.string().optional().describe("Filter by project")
|
|
23036
24703
|
}, async ({ project_id }) => {
|
|
23037
24704
|
try {
|
|
23038
|
-
const { listActiveFiles: listActiveFiles2 } = (()
|
|
24705
|
+
const { listActiveFiles: listActiveFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
|
|
23039
24706
|
let files = listActiveFiles2();
|
|
23040
24707
|
if (project_id) {
|
|
23041
24708
|
const pid = resolveId(project_id, "projects");
|
|
23042
|
-
const db = (()
|
|
24709
|
+
const db = (init_database(), __toCommonJS(exports_database)).getDatabase();
|
|
23043
24710
|
files = db.query(`
|
|
23044
24711
|
SELECT
|
|
23045
24712
|
tf.path,
|
|
@@ -23121,8 +24788,8 @@ ${lines.join(`
|
|
|
23121
24788
|
ttl_seconds: exports_external.number().optional().describe("Lock TTL in seconds (default: 1800 = 30 min)")
|
|
23122
24789
|
}, async ({ path, agent_id, task_id, ttl_seconds }) => {
|
|
23123
24790
|
try {
|
|
23124
|
-
const { lockFile } = (()
|
|
23125
|
-
const lock =
|
|
24791
|
+
const { lockFile: lockFile2 } = (init_file_locks(), __toCommonJS(exports_file_locks));
|
|
24792
|
+
const lock = lockFile2({ path, agent_id, task_id, ttl_seconds });
|
|
23126
24793
|
return { content: [{ type: "text", text: JSON.stringify(lock, null, 2) }] };
|
|
23127
24794
|
} catch (e) {
|
|
23128
24795
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -23135,8 +24802,8 @@ ${lines.join(`
|
|
|
23135
24802
|
agent_id: exports_external.string().describe("Agent releasing the lock (must be the lock holder)")
|
|
23136
24803
|
}, async ({ path, agent_id }) => {
|
|
23137
24804
|
try {
|
|
23138
|
-
const { unlockFile } = (()
|
|
23139
|
-
const released =
|
|
24805
|
+
const { unlockFile: unlockFile2 } = (init_file_locks(), __toCommonJS(exports_file_locks));
|
|
24806
|
+
const released = unlockFile2(path, agent_id);
|
|
23140
24807
|
return { content: [{ type: "text", text: JSON.stringify({ released, path }) }] };
|
|
23141
24808
|
} catch (e) {
|
|
23142
24809
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -23148,8 +24815,8 @@ ${lines.join(`
|
|
|
23148
24815
|
path: exports_external.string().describe("File path to check")
|
|
23149
24816
|
}, async ({ path }) => {
|
|
23150
24817
|
try {
|
|
23151
|
-
const { checkFileLock } = (()
|
|
23152
|
-
const lock =
|
|
24818
|
+
const { checkFileLock: checkFileLock2 } = (init_file_locks(), __toCommonJS(exports_file_locks));
|
|
24819
|
+
const lock = checkFileLock2(path);
|
|
23153
24820
|
if (!lock)
|
|
23154
24821
|
return { content: [{ type: "text", text: JSON.stringify({ path, locked: false }) }] };
|
|
23155
24822
|
return { content: [{ type: "text", text: JSON.stringify({ path, locked: true, ...lock }) }] };
|
|
@@ -23163,8 +24830,8 @@ ${lines.join(`
|
|
|
23163
24830
|
agent_id: exports_external.string().optional().describe("Filter locks by agent")
|
|
23164
24831
|
}, async ({ agent_id }) => {
|
|
23165
24832
|
try {
|
|
23166
|
-
const { listFileLocks } = (()
|
|
23167
|
-
const locks =
|
|
24833
|
+
const { listFileLocks: listFileLocks2 } = (init_file_locks(), __toCommonJS(exports_file_locks));
|
|
24834
|
+
const locks = listFileLocks2(agent_id);
|
|
23168
24835
|
return { content: [{ type: "text", text: JSON.stringify(locks, null, 2) }] };
|
|
23169
24836
|
} catch (e) {
|
|
23170
24837
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -23187,8 +24854,8 @@ function registerTaskRelTools(server, ctx) {
|
|
|
23187
24854
|
next_steps: exports_external.array(exports_external.string()).optional().describe("Recommended next actions")
|
|
23188
24855
|
}, async ({ agent_id, project_id, summary, completed, in_progress, blockers, next_steps }) => {
|
|
23189
24856
|
try {
|
|
23190
|
-
const { createHandoff } = (()
|
|
23191
|
-
const handoff =
|
|
24857
|
+
const { createHandoff: createHandoff2 } = (init_handoffs(), __toCommonJS(exports_handoffs));
|
|
24858
|
+
const handoff = createHandoff2({
|
|
23192
24859
|
agent_id,
|
|
23193
24860
|
project_id: project_id ? resolveId(project_id, "projects") : undefined,
|
|
23194
24861
|
summary,
|
|
@@ -23209,8 +24876,8 @@ function registerTaskRelTools(server, ctx) {
|
|
|
23209
24876
|
project_id: exports_external.string().optional().describe("Filter by project")
|
|
23210
24877
|
}, async ({ agent_id, project_id }) => {
|
|
23211
24878
|
try {
|
|
23212
|
-
const { getLatestHandoff } = (()
|
|
23213
|
-
const handoff =
|
|
24879
|
+
const { getLatestHandoff: getLatestHandoff2 } = (init_handoffs(), __toCommonJS(exports_handoffs));
|
|
24880
|
+
const handoff = getLatestHandoff2(agent_id, project_id ? resolveId(project_id, "projects") : undefined);
|
|
23214
24881
|
if (!handoff)
|
|
23215
24882
|
return { content: [{ type: "text", text: "No handoffs found." }] };
|
|
23216
24883
|
const lines = [
|
|
@@ -23240,7 +24907,7 @@ function registerTaskRelTools(server, ctx) {
|
|
|
23240
24907
|
created_by: exports_external.string().optional().describe("Agent ID who created this relationship")
|
|
23241
24908
|
}, async ({ source_task_id, target_task_id, relationship_type, created_by }) => {
|
|
23242
24909
|
try {
|
|
23243
|
-
const { addTaskRelationship: addTaskRelationship2 } = (()
|
|
24910
|
+
const { addTaskRelationship: addTaskRelationship2 } = (init_task_relationships(), __toCommonJS(exports_task_relationships));
|
|
23244
24911
|
const rel = addTaskRelationship2({
|
|
23245
24912
|
source_task_id: resolveId(source_task_id),
|
|
23246
24913
|
target_task_id: resolveId(target_task_id),
|
|
@@ -23261,7 +24928,7 @@ function registerTaskRelTools(server, ctx) {
|
|
|
23261
24928
|
relationship_type: exports_external.enum(["related_to", "conflicts_with", "similar_to", "duplicates", "supersedes", "modifies_same_file"]).optional()
|
|
23262
24929
|
}, async ({ id, source_task_id, target_task_id, relationship_type }) => {
|
|
23263
24930
|
try {
|
|
23264
|
-
const { removeTaskRelationship: removeTaskRelationship2, removeTaskRelationshipByPair: removeTaskRelationshipByPair2 } = (()
|
|
24931
|
+
const { removeTaskRelationship: removeTaskRelationship2, removeTaskRelationshipByPair: removeTaskRelationshipByPair2 } = (init_task_relationships(), __toCommonJS(exports_task_relationships));
|
|
23265
24932
|
let removed = false;
|
|
23266
24933
|
if (id) {
|
|
23267
24934
|
removed = removeTaskRelationship2(id);
|
|
@@ -23282,7 +24949,7 @@ function registerTaskRelTools(server, ctx) {
|
|
|
23282
24949
|
relationship_type: exports_external.enum(["related_to", "conflicts_with", "similar_to", "duplicates", "supersedes", "modifies_same_file"]).optional()
|
|
23283
24950
|
}, async ({ task_id, relationship_type }) => {
|
|
23284
24951
|
try {
|
|
23285
|
-
const { getTaskRelationships: getTaskRelationships2 } = (()
|
|
24952
|
+
const { getTaskRelationships: getTaskRelationships2 } = (init_task_relationships(), __toCommonJS(exports_task_relationships));
|
|
23286
24953
|
const rels = getTaskRelationships2(resolveId(task_id), relationship_type);
|
|
23287
24954
|
if (rels.length === 0)
|
|
23288
24955
|
return { content: [{ type: "text", text: "No relationships found." }] };
|
|
@@ -23299,7 +24966,7 @@ function registerTaskRelTools(server, ctx) {
|
|
|
23299
24966
|
task_id: exports_external.string().describe("Task ID to detect file relationships for")
|
|
23300
24967
|
}, async ({ task_id }) => {
|
|
23301
24968
|
try {
|
|
23302
|
-
const { autoDetectFileRelationships: autoDetectFileRelationships2 } = (()
|
|
24969
|
+
const { autoDetectFileRelationships: autoDetectFileRelationships2 } = (init_task_relationships(), __toCommonJS(exports_task_relationships));
|
|
23303
24970
|
const created = autoDetectFileRelationships2(resolveId(task_id));
|
|
23304
24971
|
return { content: [{ type: "text", text: created.length > 0 ? `Created ${created.length} file relationship(s).` : "No file overlaps detected." }] };
|
|
23305
24972
|
} catch (e) {
|
|
@@ -23310,8 +24977,8 @@ function registerTaskRelTools(server, ctx) {
|
|
|
23310
24977
|
if (shouldRegisterTool("sync_kg")) {
|
|
23311
24978
|
server.tool("sync_kg", "Sync all existing relationships into the knowledge graph edges table. Idempotent.", {}, async () => {
|
|
23312
24979
|
try {
|
|
23313
|
-
const { syncKgEdges } = (()
|
|
23314
|
-
const result =
|
|
24980
|
+
const { syncKgEdges: syncKgEdges2 } = (init_kg(), __toCommonJS(exports_kg));
|
|
24981
|
+
const result = syncKgEdges2();
|
|
23315
24982
|
return { content: [{ type: "text", text: `Knowledge graph synced: ${result.synced} edge(s) processed.` }] };
|
|
23316
24983
|
} catch (e) {
|
|
23317
24984
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -23327,8 +24994,8 @@ function registerTaskRelTools(server, ctx) {
|
|
|
23327
24994
|
limit: exports_external.number().optional().describe("Max results")
|
|
23328
24995
|
}, async ({ entity_id, relation_type, entity_type, direction, limit }) => {
|
|
23329
24996
|
try {
|
|
23330
|
-
const { getRelated } = (()
|
|
23331
|
-
const edges =
|
|
24997
|
+
const { getRelated: getRelated2 } = (init_kg(), __toCommonJS(exports_kg));
|
|
24998
|
+
const edges = getRelated2(entity_id, { relation_type, entity_type, direction, limit });
|
|
23332
24999
|
if (edges.length === 0)
|
|
23333
25000
|
return { content: [{ type: "text", text: "No related entities found." }] };
|
|
23334
25001
|
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})`);
|
|
@@ -23347,8 +25014,8 @@ function registerTaskRelTools(server, ctx) {
|
|
|
23347
25014
|
relation_types: exports_external.array(exports_external.string()).optional().describe("Filter by relation types")
|
|
23348
25015
|
}, async ({ source_id, target_id, max_depth, relation_types }) => {
|
|
23349
25016
|
try {
|
|
23350
|
-
const { findPath } = (()
|
|
23351
|
-
const paths =
|
|
25017
|
+
const { findPath: findPath2 } = (init_kg(), __toCommonJS(exports_kg));
|
|
25018
|
+
const paths = findPath2(source_id, target_id, { max_depth, relation_types });
|
|
23352
25019
|
if (paths.length === 0)
|
|
23353
25020
|
return { content: [{ type: "text", text: "No path found." }] };
|
|
23354
25021
|
const lines = paths.map((path, i) => {
|
|
@@ -23372,8 +25039,8 @@ function registerTaskRelTools(server, ctx) {
|
|
|
23372
25039
|
relation_types: exports_external.array(exports_external.string()).optional().describe("Filter by relation types")
|
|
23373
25040
|
}, async ({ entity_id, max_depth, relation_types }) => {
|
|
23374
25041
|
try {
|
|
23375
|
-
const { getImpactAnalysis } = (()
|
|
23376
|
-
const impact =
|
|
25042
|
+
const { getImpactAnalysis: getImpactAnalysis2 } = (init_kg(), __toCommonJS(exports_kg));
|
|
25043
|
+
const impact = getImpactAnalysis2(entity_id, { max_depth, relation_types });
|
|
23377
25044
|
if (impact.length === 0)
|
|
23378
25045
|
return { content: [{ type: "text", text: "No downstream impact detected." }] };
|
|
23379
25046
|
const byDepth = new Map;
|
|
@@ -23403,8 +25070,8 @@ Depth ${depth}:`);
|
|
|
23403
25070
|
limit: exports_external.number().optional().describe("Max results (default: 20)")
|
|
23404
25071
|
}, async ({ project_id, limit }) => {
|
|
23405
25072
|
try {
|
|
23406
|
-
const { getCriticalPath } = (()
|
|
23407
|
-
const result =
|
|
25073
|
+
const { getCriticalPath: getCriticalPath2 } = (init_kg(), __toCommonJS(exports_kg));
|
|
25074
|
+
const result = getCriticalPath2({ project_id: project_id ? resolveId(project_id, "projects") : undefined, limit });
|
|
23408
25075
|
if (result.length === 0)
|
|
23409
25076
|
return { content: [{ type: "text", text: "No critical path data. Run sync_kg first to populate the knowledge graph." }] };
|
|
23410
25077
|
const lines = result.map((r, i) => `${i + 1}. ${r.task_id.slice(0, 8)} blocks ${r.blocking_count} task(s), max depth ${r.depth}`);
|
|
@@ -23424,13 +25091,13 @@ ${lines.join(`
|
|
|
23424
25091
|
is_lead: exports_external.coerce.boolean().optional().describe("Whether this agent is the project lead for this role")
|
|
23425
25092
|
}, async ({ project_id, agent_name, role, is_lead }) => {
|
|
23426
25093
|
try {
|
|
23427
|
-
const { setProjectAgentRole } = (()
|
|
23428
|
-
const { getAgentByName: getAgentByName2 } = (()
|
|
25094
|
+
const { setProjectAgentRole: setProjectAgentRole2 } = (init_project_agent_roles(), __toCommonJS(exports_project_agent_roles));
|
|
25095
|
+
const { getAgentByName: getAgentByName2 } = (init_agents(), __toCommonJS(exports_agents));
|
|
23429
25096
|
const agent = getAgentByName2(agent_name);
|
|
23430
25097
|
if (!agent)
|
|
23431
25098
|
return { content: [{ type: "text", text: `Agent not found: ${agent_name}` }], isError: true };
|
|
23432
25099
|
const pid = resolveId(project_id, "projects");
|
|
23433
|
-
const result =
|
|
25100
|
+
const result = setProjectAgentRole2(pid, agent.id, role, is_lead ?? false);
|
|
23434
25101
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
23435
25102
|
} catch (e) {
|
|
23436
25103
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -23460,9 +25127,9 @@ ${lines.join(`
|
|
|
23460
25127
|
}).join(`
|
|
23461
25128
|
`);
|
|
23462
25129
|
};
|
|
23463
|
-
const { getProjectOrgChart } = (()
|
|
25130
|
+
const { getProjectOrgChart: getProjectOrgChart2 } = (init_project_agent_roles(), __toCommonJS(exports_project_agent_roles));
|
|
23464
25131
|
const pid = resolveId(project_id, "projects");
|
|
23465
|
-
const tree =
|
|
25132
|
+
const tree = getProjectOrgChart2(pid, { filter_to_project });
|
|
23466
25133
|
if (format === "json") {
|
|
23467
25134
|
return { content: [{ type: "text", text: JSON.stringify(tree, null, 2) }] };
|
|
23468
25135
|
}
|
|
@@ -23480,9 +25147,9 @@ ${lines.join(`
|
|
|
23480
25147
|
project_id: exports_external.string().describe("Project ID")
|
|
23481
25148
|
}, async ({ project_id }) => {
|
|
23482
25149
|
try {
|
|
23483
|
-
const { listProjectAgentRoles } = (()
|
|
25150
|
+
const { listProjectAgentRoles: listProjectAgentRoles2 } = (init_project_agent_roles(), __toCommonJS(exports_project_agent_roles));
|
|
23484
25151
|
const pid = resolveId(project_id, "projects");
|
|
23485
|
-
const roles =
|
|
25152
|
+
const roles = listProjectAgentRoles2(pid);
|
|
23486
25153
|
return { content: [{ type: "text", text: JSON.stringify(roles, null, 2) }] };
|
|
23487
25154
|
} catch (e) {
|
|
23488
25155
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -23496,7 +25163,7 @@ ${lines.join(`
|
|
|
23496
25163
|
limit: exports_external.number().optional().describe("Max results")
|
|
23497
25164
|
}, async ({ capabilities, min_score, limit }) => {
|
|
23498
25165
|
try {
|
|
23499
|
-
const { getCapableAgents: getCapableAgents2 } = (()
|
|
25166
|
+
const { getCapableAgents: getCapableAgents2 } = (init_agents(), __toCommonJS(exports_agents));
|
|
23500
25167
|
const results = getCapableAgents2(capabilities, { min_score, limit });
|
|
23501
25168
|
if (results.length === 0)
|
|
23502
25169
|
return { content: [{ type: "text", text: "No agents match the given capabilities." }] };
|
|
@@ -23515,8 +25182,8 @@ ${lines.join(`
|
|
|
23515
25182
|
project_id: exports_external.string().optional().describe("Filter by project")
|
|
23516
25183
|
}, async ({ stuck_minutes, confidence_threshold, project_id }) => {
|
|
23517
25184
|
try {
|
|
23518
|
-
const { patrolTasks } = (()
|
|
23519
|
-
const result =
|
|
25185
|
+
const { patrolTasks: patrolTasks2 } = (init_patrol(), __toCommonJS(exports_patrol));
|
|
25186
|
+
const result = patrolTasks2({
|
|
23520
25187
|
stuck_minutes,
|
|
23521
25188
|
confidence_threshold,
|
|
23522
25189
|
project_id: project_id ? resolveId(project_id, "projects") : undefined
|
|
@@ -23542,8 +25209,8 @@ ${lines.join(`
|
|
|
23542
25209
|
limit: exports_external.number().optional().describe("Max results (default: all)")
|
|
23543
25210
|
}, async ({ project_id, limit }) => {
|
|
23544
25211
|
try {
|
|
23545
|
-
const { getReviewQueue } = (()
|
|
23546
|
-
const tasks =
|
|
25212
|
+
const { getReviewQueue: getReviewQueue2 } = (init_patrol(), __toCommonJS(exports_patrol));
|
|
25213
|
+
const tasks = getReviewQueue2({
|
|
23547
25214
|
project_id: project_id ? resolveId(project_id, "projects") : undefined,
|
|
23548
25215
|
limit
|
|
23549
25216
|
});
|
|
@@ -23569,8 +25236,8 @@ ${lines.join(`
|
|
|
23569
25236
|
reviewer_id: exports_external.string().optional().describe("Agent ID of reviewer")
|
|
23570
25237
|
}, async ({ task_id, score, reviewer_id }) => {
|
|
23571
25238
|
try {
|
|
23572
|
-
const { scoreTask } = (()
|
|
23573
|
-
|
|
25239
|
+
const { scoreTask: scoreTask2 } = (init_agent_metrics(), __toCommonJS(exports_agent_metrics));
|
|
25240
|
+
scoreTask2(resolveId(task_id), score, reviewer_id);
|
|
23574
25241
|
return { content: [{ type: "text", text: `Task ${task_id.slice(0, 8)} scored: ${score}` }] };
|
|
23575
25242
|
} catch (e) {
|
|
23576
25243
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -23587,7 +25254,7 @@ ${lines.join(`
|
|
|
23587
25254
|
notes: exports_external.string().optional().describe("Notes about what was done")
|
|
23588
25255
|
}, async ({ task_id, minutes, agent_id, started_at, ended_at, notes }) => {
|
|
23589
25256
|
try {
|
|
23590
|
-
const { logTime: logTime2 } = (()
|
|
25257
|
+
const { logTime: logTime2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
23591
25258
|
logTime2({ task_id: resolveId(task_id), minutes, agent_id, started_at, ended_at, notes });
|
|
23592
25259
|
return { content: [{ type: "text", text: `Logged ${minutes} min on task ${task_id.slice(0, 8)}` }] };
|
|
23593
25260
|
} catch (e) {
|
|
@@ -23602,7 +25269,7 @@ ${lines.join(`
|
|
|
23602
25269
|
since: exports_external.string().optional().describe("ISO date \u2014 only tasks completed after this date")
|
|
23603
25270
|
}, async ({ project_id, agent_id, since }) => {
|
|
23604
25271
|
try {
|
|
23605
|
-
const { getTimeReport: getTimeReport2 } = (()
|
|
25272
|
+
const { getTimeReport: getTimeReport2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
23606
25273
|
const report = getTimeReport2({ project_id: project_id ? resolveId(project_id, "projects") : undefined, agent_id, since });
|
|
23607
25274
|
if (report.length === 0)
|
|
23608
25275
|
return { content: [{ type: "text", text: "No completed tasks found." }] };
|
|
@@ -23626,7 +25293,7 @@ ${lines.join(`
|
|
|
23626
25293
|
agent_id: exports_external.string().optional().describe("Agent subscribing (defaults to context agent)")
|
|
23627
25294
|
}, async ({ task_id, agent_id }) => {
|
|
23628
25295
|
try {
|
|
23629
|
-
const { watchTask: watchTask2 } = (()
|
|
25296
|
+
const { watchTask: watchTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
23630
25297
|
watchTask2(resolveId(task_id), agent_id || "");
|
|
23631
25298
|
return { content: [{ type: "text", text: `Now watching task ${task_id.slice(0, 8)}` }] };
|
|
23632
25299
|
} catch (e) {
|
|
@@ -23640,7 +25307,7 @@ ${lines.join(`
|
|
|
23640
25307
|
agent_id: exports_external.string().optional().describe("Agent unsubscribing (defaults to context agent)")
|
|
23641
25308
|
}, async ({ task_id, agent_id }) => {
|
|
23642
25309
|
try {
|
|
23643
|
-
const { unwatchTask: unwatchTask2 } = (()
|
|
25310
|
+
const { unwatchTask: unwatchTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
23644
25311
|
unwatchTask2(resolveId(task_id), agent_id || "");
|
|
23645
25312
|
return { content: [{ type: "text", text: `Stopped watching task ${task_id.slice(0, 8)}` }] };
|
|
23646
25313
|
} catch (e) {
|
|
@@ -23653,7 +25320,7 @@ ${lines.join(`
|
|
|
23653
25320
|
task_id: exports_external.string().describe("Task ID")
|
|
23654
25321
|
}, async ({ task_id }) => {
|
|
23655
25322
|
try {
|
|
23656
|
-
const { getTaskWatchers: getTaskWatchers2 } = (()
|
|
25323
|
+
const { getTaskWatchers: getTaskWatchers2 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
23657
25324
|
const watchers = getTaskWatchers2(resolveId(task_id));
|
|
23658
25325
|
if (watchers.length === 0)
|
|
23659
25326
|
return { content: [{ type: "text", text: "No watchers." }] };
|
|
@@ -23672,15 +25339,15 @@ ${lines.join(`
|
|
|
23672
25339
|
agent_id: exports_external.string().optional().describe("Filter by assignee")
|
|
23673
25340
|
}, async ({ project_id, plan_id, task_list_id, since, agent_id }) => {
|
|
23674
25341
|
try {
|
|
23675
|
-
const { listTasks: listTasks3 } = (()
|
|
23676
|
-
const { patrolTasks } = (()
|
|
25342
|
+
const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
25343
|
+
const { patrolTasks: patrolTasks2 } = (init_patrol(), __toCommonJS(exports_patrol));
|
|
23677
25344
|
const completed = listTasks3({ status: "completed", project_id, plan_id, task_list_id, assigned_to: agent_id, limit: 500 }, undefined);
|
|
23678
25345
|
const filtered = since ? completed.filter((t) => t.completed_at && t.completed_at >= since) : completed;
|
|
23679
25346
|
const total = filtered.length;
|
|
23680
25347
|
const lowConf = filtered.filter((t) => t.confidence != null && t.confidence < 0.7).length;
|
|
23681
25348
|
const withEstimate = filtered.filter((t) => t.estimated_minutes != null && t.actual_minutes != null);
|
|
23682
25349
|
const avgDiff = withEstimate.length > 0 ? withEstimate.reduce((acc, t) => acc + (t.actual_minutes - t.estimated_minutes), 0) / withEstimate.length : 0;
|
|
23683
|
-
const patrolResult =
|
|
25350
|
+
const patrolResult = patrolTasks2({ project_id: project_id ? resolveId(project_id, "projects") : undefined });
|
|
23684
25351
|
const stuck = patrolResult.issues.filter((i) => i.type === "stuck").length;
|
|
23685
25352
|
const lines = [
|
|
23686
25353
|
`Retro (${total} completed tasks${since ? ` since ${since}` : ""})`,
|
|
@@ -23701,7 +25368,7 @@ ${lines.join(`
|
|
|
23701
25368
|
limit: exports_external.number().optional().describe("Max results (default: 20)")
|
|
23702
25369
|
}, async ({ project_id, limit }) => {
|
|
23703
25370
|
try {
|
|
23704
|
-
const { listTasks: listTasks3 } = (()
|
|
25371
|
+
const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
|
|
23705
25372
|
const tasks = listTasks3({ status: "pending", project_id, assigned_to: "", limit: limit || 20 }, undefined);
|
|
23706
25373
|
if (tasks.length === 0)
|
|
23707
25374
|
return { content: [{ type: "text", text: "Inbox is empty." }] };
|
|
@@ -23720,8 +25387,8 @@ ${lines.join(`
|
|
|
23720
25387
|
project_id: exports_external.string().optional().describe("Filter by project")
|
|
23721
25388
|
}, async ({ agent_id, project_id }) => {
|
|
23722
25389
|
try {
|
|
23723
|
-
const { getAgentMetrics } = (()
|
|
23724
|
-
const metrics =
|
|
25390
|
+
const { getAgentMetrics: getAgentMetrics2 } = (init_agent_metrics(), __toCommonJS(exports_agent_metrics));
|
|
25391
|
+
const metrics = getAgentMetrics2(agent_id, {
|
|
23725
25392
|
project_id: project_id ? resolveId(project_id, "projects") : undefined
|
|
23726
25393
|
});
|
|
23727
25394
|
if (!metrics)
|
|
@@ -23748,8 +25415,8 @@ ${lines.join(`
|
|
|
23748
25415
|
limit: exports_external.number().optional().describe("Max entries (default: 20)")
|
|
23749
25416
|
}, async ({ project_id, limit }) => {
|
|
23750
25417
|
try {
|
|
23751
|
-
const { getLeaderboard } = (()
|
|
23752
|
-
const entries =
|
|
25418
|
+
const { getLeaderboard: getLeaderboard2 } = (init_agent_metrics(), __toCommonJS(exports_agent_metrics));
|
|
25419
|
+
const entries = getLeaderboard2({
|
|
23753
25420
|
project_id: project_id ? resolveId(project_id, "projects") : undefined,
|
|
23754
25421
|
limit
|
|
23755
25422
|
});
|
|
@@ -23999,15 +25666,395 @@ ${lines.join(`
|
|
|
23999
25666
|
}
|
|
24000
25667
|
}
|
|
24001
25668
|
|
|
25669
|
+
// src/mcp/tools/agents.ts
|
|
25670
|
+
init_agents();
|
|
25671
|
+
init_config2();
|
|
25672
|
+
init_database();
|
|
25673
|
+
function registerAgentTools(server, { shouldRegisterTool, resolveId, formatError, agentFocusMap, getAgentFocus }) {
|
|
25674
|
+
if (shouldRegisterTool("set_focus")) {
|
|
25675
|
+
server.tool("set_focus", "Focus this agent on a project. All list/search/status tools will default to this project.", {
|
|
25676
|
+
agent_id: exports_external.string().describe("Agent ID or name"),
|
|
25677
|
+
project_id: exports_external.string().optional().describe("Project to focus on. Omit to clear."),
|
|
25678
|
+
task_list_id: exports_external.string().optional().describe("Task list to focus on")
|
|
25679
|
+
}, async ({ agent_id, project_id, task_list_id }) => {
|
|
25680
|
+
try {
|
|
25681
|
+
const resolvedProject = project_id ? resolveId(project_id, "projects") : undefined;
|
|
25682
|
+
const focus = { agent_id, project_id: resolvedProject, task_list_id };
|
|
25683
|
+
agentFocusMap.set(agent_id, focus);
|
|
25684
|
+
try {
|
|
25685
|
+
const agent = getAgentByName(agent_id) || getAgent(agent_id);
|
|
25686
|
+
if (agent) {
|
|
25687
|
+
const db = getDatabase();
|
|
25688
|
+
db.run("UPDATE agents SET active_project_id = ? WHERE id = ?", [resolvedProject || null, agent.id]);
|
|
25689
|
+
}
|
|
25690
|
+
} catch {}
|
|
25691
|
+
const projectName = resolvedProject ? ` (${resolvedProject.slice(0, 8)})` : "";
|
|
25692
|
+
return { content: [{ type: "text", text: `Focused on project${projectName}. Read tools will default to this scope. Pass explicit project_id to override.` }] };
|
|
25693
|
+
} catch (e) {
|
|
25694
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
25695
|
+
}
|
|
25696
|
+
});
|
|
25697
|
+
}
|
|
25698
|
+
if (shouldRegisterTool("get_focus")) {
|
|
25699
|
+
server.tool("get_focus", "Get the current focus for an agent.", { agent_id: exports_external.string().describe("Agent ID or name") }, async ({ agent_id }) => {
|
|
25700
|
+
const focus = getAgentFocus(agent_id);
|
|
25701
|
+
if (!focus?.project_id) {
|
|
25702
|
+
return { content: [{ type: "text", text: "No focus set. Showing all projects." }] };
|
|
25703
|
+
}
|
|
25704
|
+
return { content: [{ type: "text", text: `Focused on project: ${focus.project_id}${focus.task_list_id ? `, task list: ${focus.task_list_id}` : ""}` }] };
|
|
25705
|
+
});
|
|
25706
|
+
}
|
|
25707
|
+
if (shouldRegisterTool("unfocus")) {
|
|
25708
|
+
server.tool("unfocus", "Clear focus \u2014 show all projects and tasks.", { agent_id: exports_external.string().describe("Agent ID or name") }, async ({ agent_id }) => {
|
|
25709
|
+
agentFocusMap.delete(agent_id);
|
|
25710
|
+
try {
|
|
25711
|
+
const agent = getAgentByName(agent_id) || getAgent(agent_id);
|
|
25712
|
+
if (agent) {
|
|
25713
|
+
const db = getDatabase();
|
|
25714
|
+
db.run("UPDATE agents SET active_project_id = NULL WHERE id = ?", [agent.id]);
|
|
25715
|
+
}
|
|
25716
|
+
} catch {}
|
|
25717
|
+
return { content: [{ type: "text", text: "Focus cleared. Showing all projects." }] };
|
|
25718
|
+
});
|
|
25719
|
+
}
|
|
25720
|
+
if (shouldRegisterTool("register_agent")) {
|
|
25721
|
+
server.tool("register_agent", "Register an agent with a distinctive one-word name. Generic/generated names like agent, agent-1, assistant, or worker-2 are rejected. Prefer Roman or Greek names from suggest_agent_name.", {
|
|
25722
|
+
name: exports_external.string().describe("Distinctive one-word agent name. Use suggest_agent_name first; avoid agent, agent-1, and other numbered generated names."),
|
|
25723
|
+
description: exports_external.string().optional(),
|
|
25724
|
+
role: exports_external.string().optional(),
|
|
25725
|
+
title: exports_external.string().optional(),
|
|
25726
|
+
level: exports_external.string().optional(),
|
|
25727
|
+
permissions: exports_external.array(exports_external.string()).optional(),
|
|
25728
|
+
capabilities: exports_external.array(exports_external.string()).optional().describe("Agent capabilities/skills for task routing (e.g. ['typescript', 'testing', 'devops'])"),
|
|
25729
|
+
session_id: exports_external.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."),
|
|
25730
|
+
working_dir: exports_external.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"),
|
|
25731
|
+
force: exports_external.boolean().optional().describe("Force takeover of an active agent's name. Use with caution \u2014 only when you know the previous session is dead.")
|
|
25732
|
+
}, async ({ name, description, role, title, level, permissions, capabilities, session_id, working_dir, force }) => {
|
|
25733
|
+
try {
|
|
25734
|
+
const pool = getAgentPoolForProject(working_dir);
|
|
25735
|
+
const result = registerAgent({ name, description, role, title, level, permissions, capabilities, session_id, working_dir, force, pool: pool || undefined });
|
|
25736
|
+
if (isAgentConflict(result)) {
|
|
25737
|
+
const suggestLine = result.suggestions && result.suggestions.length > 0 ? `
|
|
25738
|
+
Available names: ${result.suggestions.join(", ")}` : "";
|
|
25739
|
+
const hint = `CONFLICT: ${result.message}${suggestLine}`;
|
|
25740
|
+
return {
|
|
25741
|
+
content: [{ type: "text", text: hint }],
|
|
25742
|
+
isError: true
|
|
25743
|
+
};
|
|
25744
|
+
}
|
|
25745
|
+
const agent = result;
|
|
25746
|
+
const poolLine = pool ? `
|
|
25747
|
+
Pool: [${pool.join(", ")}]` : "";
|
|
25748
|
+
return {
|
|
25749
|
+
content: [{
|
|
25750
|
+
type: "text",
|
|
25751
|
+
text: `Agent registered:
|
|
25752
|
+
ID: ${agent.id}
|
|
25753
|
+
Name: ${agent.name}${agent.description ? `
|
|
25754
|
+
Description: ${agent.description}` : ""}
|
|
25755
|
+
Session: ${agent.session_id ?? "unbound"}${poolLine}
|
|
25756
|
+
Created: ${agent.created_at}
|
|
25757
|
+
Last seen: ${agent.last_seen_at}`
|
|
25758
|
+
}]
|
|
25759
|
+
};
|
|
25760
|
+
} catch (e) {
|
|
25761
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
25762
|
+
}
|
|
25763
|
+
});
|
|
25764
|
+
}
|
|
25765
|
+
if (shouldRegisterTool("suggest_agent_name")) {
|
|
25766
|
+
server.tool("suggest_agent_name", "Get available Roman/Greek-style agent names for a project. Use these instead of generic generated names.", {
|
|
25767
|
+
working_dir: exports_external.string().optional().describe("Your working directory \u2014 used to look up the project's allowed name pool from config")
|
|
25768
|
+
}, async ({ working_dir }) => {
|
|
25769
|
+
try {
|
|
25770
|
+
const pool = getAgentPoolForProject(working_dir);
|
|
25771
|
+
const cutoff = new Date(Date.now() - 30 * 60 * 1000).toISOString();
|
|
25772
|
+
const allActive = listAgents().filter((a) => a.last_seen_at > cutoff);
|
|
25773
|
+
if (!pool) {
|
|
25774
|
+
const suggestions = getAvailableNamesFromPool([
|
|
25775
|
+
"caesar",
|
|
25776
|
+
"augustus",
|
|
25777
|
+
"marcus",
|
|
25778
|
+
"brutus",
|
|
25779
|
+
"cicero",
|
|
25780
|
+
"cato",
|
|
25781
|
+
"nero",
|
|
25782
|
+
"claudius",
|
|
25783
|
+
"tiberius",
|
|
25784
|
+
"hadrian",
|
|
25785
|
+
"athena",
|
|
25786
|
+
"apollo",
|
|
25787
|
+
"artemis",
|
|
25788
|
+
"iris",
|
|
25789
|
+
"hector",
|
|
25790
|
+
"sophia",
|
|
25791
|
+
"thalia",
|
|
25792
|
+
"phoebe",
|
|
25793
|
+
"daphne"
|
|
25794
|
+
], getDatabase());
|
|
25795
|
+
const lines2 = [
|
|
25796
|
+
"No project pool configured. Use a distinctive one-word name; generic generated names are blocked.",
|
|
25797
|
+
`Suggested names: ${suggestions.slice(0, 8).join(", ")}`,
|
|
25798
|
+
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.",
|
|
25799
|
+
`
|
|
25800
|
+
To restrict names, configure agent_pool or project_pools in ~/.hasna/todos/config.json`
|
|
25801
|
+
];
|
|
25802
|
+
return { content: [{ type: "text", text: lines2.join(`
|
|
25803
|
+
`) }] };
|
|
25804
|
+
}
|
|
25805
|
+
const available = getAvailableNamesFromPool(pool, getDatabase());
|
|
25806
|
+
const activeInPool = allActive.filter((a) => pool.map((n) => n.toLowerCase()).includes(a.name));
|
|
25807
|
+
const lines = [
|
|
25808
|
+
`Project pool: ${pool.join(", ")}`,
|
|
25809
|
+
`Available now (${available.length}): ${available.length > 0 ? available.join(", ") : "none \u2014 all names in use"}`,
|
|
25810
|
+
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",
|
|
25811
|
+
available.length > 0 ? `
|
|
25812
|
+
Suggested: ${available[0]}` : `
|
|
25813
|
+
No names available. Wait for an active agent to go stale (30min timeout).`
|
|
25814
|
+
];
|
|
25815
|
+
return { content: [{ type: "text", text: lines.join(`
|
|
25816
|
+
`) }] };
|
|
25817
|
+
} catch (e) {
|
|
25818
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
25819
|
+
}
|
|
25820
|
+
});
|
|
25821
|
+
}
|
|
25822
|
+
if (shouldRegisterTool("list_agents")) {
|
|
25823
|
+
server.tool("list_agents", "List all registered agents. By default shows only active agents \u2014 set include_archived to see archived ones too.", {
|
|
25824
|
+
include_archived: exports_external.boolean().optional().describe("Include archived agents in the list (default: false)")
|
|
25825
|
+
}, async ({ include_archived }) => {
|
|
25826
|
+
try {
|
|
25827
|
+
const agents = listAgents({ include_archived: include_archived ?? false });
|
|
25828
|
+
if (agents.length === 0) {
|
|
25829
|
+
return { content: [{ type: "text", text: "No agents registered." }] };
|
|
25830
|
+
}
|
|
25831
|
+
const text = agents.map((a) => {
|
|
25832
|
+
const statusTag = a.status === "archived" ? " [archived]" : "";
|
|
25833
|
+
return `${a.id} | ${a.name}${statusTag}${a.description ? ` - ${a.description}` : ""} (last seen: ${a.last_seen_at})`;
|
|
25834
|
+
}).join(`
|
|
25835
|
+
`);
|
|
25836
|
+
return { content: [{ type: "text", text: `${agents.length} agent(s):
|
|
25837
|
+
${text}` }] };
|
|
25838
|
+
} catch (e) {
|
|
25839
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
25840
|
+
}
|
|
25841
|
+
});
|
|
25842
|
+
}
|
|
25843
|
+
if (shouldRegisterTool("get_agent")) {
|
|
25844
|
+
server.tool("get_agent", "Get agent details by ID or name. Provide one of id or name.", {
|
|
25845
|
+
agent_id: exports_external.string().optional(),
|
|
25846
|
+
id: exports_external.string().optional(),
|
|
25847
|
+
name: exports_external.string().optional()
|
|
25848
|
+
}, async ({ agent_id, id, name }) => {
|
|
25849
|
+
try {
|
|
25850
|
+
const identifier = agent_id || id || name;
|
|
25851
|
+
if (!identifier) {
|
|
25852
|
+
return { content: [{ type: "text", text: "Provide agent_id, id, or name." }], isError: true };
|
|
25853
|
+
}
|
|
25854
|
+
const agent = getAgent(identifier) || getAgentByName(identifier);
|
|
25855
|
+
if (!agent) {
|
|
25856
|
+
return { content: [{ type: "text", text: `Agent not found: ${identifier}` }], isError: true };
|
|
25857
|
+
}
|
|
25858
|
+
const parts = [
|
|
25859
|
+
`ID: ${agent.id}`,
|
|
25860
|
+
`Name: ${agent.name}`
|
|
25861
|
+
];
|
|
25862
|
+
if (agent.description)
|
|
25863
|
+
parts.push(`Description: ${agent.description}`);
|
|
25864
|
+
if (Object.keys(agent.metadata).length > 0)
|
|
25865
|
+
parts.push(`Metadata: ${JSON.stringify(agent.metadata)}`);
|
|
25866
|
+
parts.push(`Created: ${agent.created_at}`);
|
|
25867
|
+
parts.push(`Last seen: ${agent.last_seen_at}`);
|
|
25868
|
+
return { content: [{ type: "text", text: parts.join(`
|
|
25869
|
+
`) }] };
|
|
25870
|
+
} catch (e) {
|
|
25871
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
25872
|
+
}
|
|
25873
|
+
});
|
|
25874
|
+
}
|
|
25875
|
+
if (shouldRegisterTool("rename_agent")) {
|
|
25876
|
+
server.tool("rename_agent", "Rename an agent. Resolve by id or current name.", {
|
|
25877
|
+
id: exports_external.string().optional(),
|
|
25878
|
+
name: exports_external.string().optional(),
|
|
25879
|
+
new_name: exports_external.string()
|
|
25880
|
+
}, async ({ id, name, new_name }) => {
|
|
25881
|
+
try {
|
|
25882
|
+
if (!id && !name) {
|
|
25883
|
+
return { content: [{ type: "text", text: "Provide either id or name." }], isError: true };
|
|
25884
|
+
}
|
|
25885
|
+
const agent = id ? getAgent(id) : getAgentByName(name);
|
|
25886
|
+
if (!agent) {
|
|
25887
|
+
return { content: [{ type: "text", text: `Agent not found: ${id || name}` }], isError: true };
|
|
25888
|
+
}
|
|
25889
|
+
const oldName = agent.name;
|
|
25890
|
+
const updated = updateAgent(agent.id, { name: new_name });
|
|
25891
|
+
const db = getDatabase();
|
|
25892
|
+
const tasksResult = db.run("UPDATE tasks SET assigned_to = ? WHERE assigned_to = ?", [new_name, oldName]);
|
|
25893
|
+
const taskNote = tasksResult.changes > 0 ? `
|
|
25894
|
+
Updated assigned_to on ${tasksResult.changes} task(s).` : "";
|
|
25895
|
+
return {
|
|
25896
|
+
content: [{
|
|
25897
|
+
type: "text",
|
|
25898
|
+
text: `Agent renamed: ${oldName} -> ${updated.name}
|
|
25899
|
+
ID: ${updated.id}${taskNote}`
|
|
25900
|
+
}]
|
|
25901
|
+
};
|
|
25902
|
+
} catch (e) {
|
|
25903
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
25904
|
+
}
|
|
25905
|
+
});
|
|
25906
|
+
}
|
|
25907
|
+
if (shouldRegisterTool("update_agent")) {
|
|
25908
|
+
server.tool("update_agent", "Update an agent's description, role, title, or other metadata. Resolve by id or name.", {
|
|
25909
|
+
agent_id: exports_external.string().optional(),
|
|
25910
|
+
id: exports_external.string().optional(),
|
|
25911
|
+
name: exports_external.string().optional(),
|
|
25912
|
+
description: exports_external.string().optional(),
|
|
25913
|
+
role: exports_external.string().optional(),
|
|
25914
|
+
title: exports_external.string().optional(),
|
|
25915
|
+
level: exports_external.string().optional(),
|
|
25916
|
+
capabilities: exports_external.array(exports_external.string()).optional(),
|
|
25917
|
+
permissions: exports_external.array(exports_external.string()).optional(),
|
|
25918
|
+
metadata: exports_external.record(exports_external.unknown()).optional()
|
|
25919
|
+
}, async ({ agent_id, id, name, ...updates }) => {
|
|
25920
|
+
try {
|
|
25921
|
+
const identifier = agent_id || id || name;
|
|
25922
|
+
if (!identifier) {
|
|
25923
|
+
return { content: [{ type: "text", text: "Provide agent_id, id, or name." }], isError: true };
|
|
25924
|
+
}
|
|
25925
|
+
const agent = getAgent(identifier) || getAgentByName(identifier);
|
|
25926
|
+
if (!agent) {
|
|
25927
|
+
return { content: [{ type: "text", text: `Agent not found: ${identifier}` }], isError: true };
|
|
25928
|
+
}
|
|
25929
|
+
const updated = updateAgent(agent.id, updates);
|
|
25930
|
+
return { content: [{ type: "text", text: `Agent updated: ${updated.name} (${updated.id.slice(0, 8)})` }] };
|
|
25931
|
+
} catch (e) {
|
|
25932
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
25933
|
+
}
|
|
25934
|
+
});
|
|
25935
|
+
}
|
|
25936
|
+
if (shouldRegisterTool("delete_agent")) {
|
|
25937
|
+
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.", {
|
|
25938
|
+
agent_id: exports_external.string().optional(),
|
|
25939
|
+
id: exports_external.string().optional(),
|
|
25940
|
+
name: exports_external.string().optional()
|
|
25941
|
+
}, async ({ agent_id, id, name }) => {
|
|
25942
|
+
try {
|
|
25943
|
+
const identifier = agent_id || id || name;
|
|
25944
|
+
if (!identifier) {
|
|
25945
|
+
return { content: [{ type: "text", text: "Provide agent_id, id, or name." }], isError: true };
|
|
25946
|
+
}
|
|
25947
|
+
const agent = getAgent(identifier) || getAgentByName(identifier);
|
|
25948
|
+
if (!agent) {
|
|
25949
|
+
return { content: [{ type: "text", text: `Agent not found: ${identifier}` }], isError: true };
|
|
25950
|
+
}
|
|
25951
|
+
const archived = archiveAgent(agent.id);
|
|
25952
|
+
return {
|
|
25953
|
+
content: [{
|
|
25954
|
+
type: "text",
|
|
25955
|
+
text: archived ? `Agent archived: ${agent.name} (${agent.id}). Use unarchive_agent to restore.` : `Failed to archive agent: ${agent.name}`
|
|
25956
|
+
}],
|
|
25957
|
+
isError: !archived
|
|
25958
|
+
};
|
|
25959
|
+
} catch (e) {
|
|
25960
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
25961
|
+
}
|
|
25962
|
+
});
|
|
25963
|
+
}
|
|
25964
|
+
if (shouldRegisterTool("unarchive_agent")) {
|
|
25965
|
+
server.tool("unarchive_agent", "Restore an archived agent back to active status. Resolve by id or name.", {
|
|
25966
|
+
agent_id: exports_external.string().optional(),
|
|
25967
|
+
id: exports_external.string().optional(),
|
|
25968
|
+
name: exports_external.string().optional()
|
|
25969
|
+
}, async ({ agent_id, id, name }) => {
|
|
25970
|
+
try {
|
|
25971
|
+
const identifier = agent_id || id || name;
|
|
25972
|
+
if (!identifier) {
|
|
25973
|
+
return { content: [{ type: "text", text: "Provide agent_id, id, or name." }], isError: true };
|
|
25974
|
+
}
|
|
25975
|
+
const agent = getAgent(identifier) || getAgentByName(identifier);
|
|
25976
|
+
if (!agent) {
|
|
25977
|
+
return { content: [{ type: "text", text: `Agent not found: ${identifier}` }], isError: true };
|
|
25978
|
+
}
|
|
25979
|
+
if (agent.status === "active") {
|
|
25980
|
+
return { content: [{ type: "text", text: `Agent ${agent.name} is already active.` }] };
|
|
25981
|
+
}
|
|
25982
|
+
const restored = unarchiveAgent(agent.id);
|
|
25983
|
+
return {
|
|
25984
|
+
content: [{
|
|
25985
|
+
type: "text",
|
|
25986
|
+
text: restored ? `Agent restored: ${agent.name} (${agent.id}) is now active.` : `Failed to restore agent: ${agent.name}`
|
|
25987
|
+
}],
|
|
25988
|
+
isError: !restored
|
|
25989
|
+
};
|
|
25990
|
+
} catch (e) {
|
|
25991
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
25992
|
+
}
|
|
25993
|
+
});
|
|
25994
|
+
}
|
|
25995
|
+
if (shouldRegisterTool("heartbeat")) {
|
|
25996
|
+
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.", {
|
|
25997
|
+
agent_id: exports_external.string().describe("Your agent ID or name.")
|
|
25998
|
+
}, async ({ agent_id }) => {
|
|
25999
|
+
try {
|
|
26000
|
+
const agent = getAgent(agent_id) || getAgentByName(agent_id);
|
|
26001
|
+
if (!agent) {
|
|
26002
|
+
return { content: [{ type: "text", text: `Agent not found: ${agent_id}` }], isError: true };
|
|
26003
|
+
}
|
|
26004
|
+
updateAgentActivity(agent.id);
|
|
26005
|
+
return {
|
|
26006
|
+
content: [{
|
|
26007
|
+
type: "text",
|
|
26008
|
+
text: `Heartbeat: ${agent.name} (${agent.id}) \u2014 last_seen_at updated to ${new Date().toISOString()}`
|
|
26009
|
+
}]
|
|
26010
|
+
};
|
|
26011
|
+
} catch (e) {
|
|
26012
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
26013
|
+
}
|
|
26014
|
+
});
|
|
26015
|
+
}
|
|
26016
|
+
if (shouldRegisterTool("release_agent")) {
|
|
26017
|
+
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.", {
|
|
26018
|
+
agent_id: exports_external.string().describe("Your agent ID or name."),
|
|
26019
|
+
session_id: exports_external.string().optional().describe("Your session ID \u2014 if provided, release only succeeds if it matches (prevents other sessions from releasing your agent).")
|
|
26020
|
+
}, async ({ agent_id, session_id }) => {
|
|
26021
|
+
try {
|
|
26022
|
+
const agent = getAgent(agent_id) || getAgentByName(agent_id);
|
|
26023
|
+
if (!agent) {
|
|
26024
|
+
return { content: [{ type: "text", text: `Agent not found: ${agent_id}` }], isError: true };
|
|
26025
|
+
}
|
|
26026
|
+
const released = releaseAgent(agent.id, session_id);
|
|
26027
|
+
if (!released) {
|
|
26028
|
+
return { content: [{ type: "text", text: `Release denied: session_id does not match agent's current session.` }], isError: true };
|
|
26029
|
+
}
|
|
26030
|
+
return {
|
|
26031
|
+
content: [{
|
|
26032
|
+
type: "text",
|
|
26033
|
+
text: `Agent released: ${agent.name} (${agent.id}) \u2014 session cleared, name is now available.`
|
|
26034
|
+
}]
|
|
26035
|
+
};
|
|
26036
|
+
} catch (e) {
|
|
26037
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
26038
|
+
}
|
|
26039
|
+
});
|
|
26040
|
+
}
|
|
26041
|
+
}
|
|
26042
|
+
|
|
24002
26043
|
// src/mcp/index.ts
|
|
24003
26044
|
function getMcpVersion() {
|
|
24004
26045
|
try {
|
|
24005
|
-
|
|
24006
|
-
|
|
24007
|
-
|
|
26046
|
+
let dir = dirname3(fileURLToPath(import.meta.url));
|
|
26047
|
+
for (let i = 0;i < 4; i++) {
|
|
26048
|
+
const pkgPath = join10(dir, "package.json");
|
|
26049
|
+
if (existsSync10(pkgPath)) {
|
|
26050
|
+
return JSON.parse(readFileSync6(pkgPath, "utf-8")).version || "0.0.0";
|
|
26051
|
+
}
|
|
26052
|
+
dir = dirname3(dir);
|
|
26053
|
+
}
|
|
24008
26054
|
} catch {
|
|
24009
26055
|
return "0.0.0";
|
|
24010
26056
|
}
|
|
26057
|
+
return "0.0.0";
|
|
24011
26058
|
}
|
|
24012
26059
|
var server = new McpServer({
|
|
24013
26060
|
name: "todos",
|
|
@@ -24026,6 +26073,7 @@ var MINIMAL_TOOLS = new Set([
|
|
|
24026
26073
|
"get_next_task",
|
|
24027
26074
|
"bootstrap",
|
|
24028
26075
|
"get_tasks_changed_since",
|
|
26076
|
+
"get_health",
|
|
24029
26077
|
"heartbeat",
|
|
24030
26078
|
"release_agent"
|
|
24031
26079
|
]);
|
|
@@ -24204,6 +26252,7 @@ registerTaskMetaTools(server, toolContext);
|
|
|
24204
26252
|
registerTaskResources(server, toolContext);
|
|
24205
26253
|
registerTaskRelTools(server, toolContext);
|
|
24206
26254
|
registerCodeTools(server, toolContext);
|
|
26255
|
+
registerAgentTools(server, { ...toolContext, agentFocusMap });
|
|
24207
26256
|
registerMachineTools(server, { shouldRegisterTool, formatError });
|
|
24208
26257
|
registerDispatchTools(server, { shouldRegisterTool, resolveId, formatError });
|
|
24209
26258
|
registerCloudSyncTools(server, { shouldRegisterTool, formatError });
|