@hasna/todos 0.10.17 → 0.10.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +110 -1
- package/dist/db/database.d.ts.map +1 -1
- package/dist/db/task-commits.d.ts +31 -0
- package/dist/db/task-commits.d.ts.map +1 -0
- package/dist/index.js +17 -1
- package/dist/mcp/index.js +110 -1
- package/dist/server/index.js +17 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -2911,6 +2911,22 @@ var init_database = __esm(() => {
|
|
|
2911
2911
|
CREATE INDEX IF NOT EXISTS idx_project_agent_roles_project ON project_agent_roles(project_id);
|
|
2912
2912
|
CREATE INDEX IF NOT EXISTS idx_project_agent_roles_agent ON project_agent_roles(agent_id);
|
|
2913
2913
|
INSERT OR IGNORE INTO _migrations (id) VALUES (31);
|
|
2914
|
+
`,
|
|
2915
|
+
`
|
|
2916
|
+
CREATE TABLE IF NOT EXISTS task_commits (
|
|
2917
|
+
id TEXT PRIMARY KEY,
|
|
2918
|
+
task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
|
|
2919
|
+
sha TEXT NOT NULL,
|
|
2920
|
+
message TEXT,
|
|
2921
|
+
author TEXT,
|
|
2922
|
+
files_changed TEXT,
|
|
2923
|
+
committed_at TEXT,
|
|
2924
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
2925
|
+
UNIQUE(task_id, sha)
|
|
2926
|
+
);
|
|
2927
|
+
CREATE INDEX IF NOT EXISTS idx_task_commits_task ON task_commits(task_id);
|
|
2928
|
+
CREATE INDEX IF NOT EXISTS idx_task_commits_sha ON task_commits(sha);
|
|
2929
|
+
INSERT OR IGNORE INTO _migrations (id) VALUES (32);
|
|
2914
2930
|
`,
|
|
2915
2931
|
`
|
|
2916
2932
|
CREATE TABLE IF NOT EXISTS file_locks (
|
|
@@ -2924,7 +2940,7 @@ var init_database = __esm(() => {
|
|
|
2924
2940
|
CREATE INDEX IF NOT EXISTS idx_file_locks_path ON file_locks(path);
|
|
2925
2941
|
CREATE INDEX IF NOT EXISTS idx_file_locks_agent ON file_locks(agent_id);
|
|
2926
2942
|
CREATE INDEX IF NOT EXISTS idx_file_locks_expires ON file_locks(expires_at);
|
|
2927
|
-
INSERT OR IGNORE INTO _migrations (id) VALUES (
|
|
2943
|
+
INSERT OR IGNORE INTO _migrations (id) VALUES (33);
|
|
2928
2944
|
`
|
|
2929
2945
|
];
|
|
2930
2946
|
});
|
|
@@ -9817,6 +9833,50 @@ var init_zod = __esm(() => {
|
|
|
9817
9833
|
init_external();
|
|
9818
9834
|
});
|
|
9819
9835
|
|
|
9836
|
+
// src/db/task-commits.ts
|
|
9837
|
+
var exports_task_commits = {};
|
|
9838
|
+
__export(exports_task_commits, {
|
|
9839
|
+
unlinkTaskCommit: () => unlinkTaskCommit,
|
|
9840
|
+
linkTaskToCommit: () => linkTaskToCommit,
|
|
9841
|
+
getTaskCommits: () => getTaskCommits,
|
|
9842
|
+
findTaskByCommit: () => findTaskByCommit
|
|
9843
|
+
});
|
|
9844
|
+
function rowToCommit(row) {
|
|
9845
|
+
return {
|
|
9846
|
+
...row,
|
|
9847
|
+
files_changed: row.files_changed ? JSON.parse(row.files_changed) : null
|
|
9848
|
+
};
|
|
9849
|
+
}
|
|
9850
|
+
function linkTaskToCommit(input, db) {
|
|
9851
|
+
const d = db || getDatabase();
|
|
9852
|
+
const existing = d.query("SELECT * FROM task_commits WHERE task_id = ? AND sha = ?").get(input.task_id, input.sha);
|
|
9853
|
+
if (existing) {
|
|
9854
|
+
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]);
|
|
9855
|
+
return rowToCommit(d.query("SELECT * FROM task_commits WHERE id = ?").get(existing.id));
|
|
9856
|
+
}
|
|
9857
|
+
const id = uuid();
|
|
9858
|
+
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()]);
|
|
9859
|
+
return rowToCommit(d.query("SELECT * FROM task_commits WHERE id = ?").get(id));
|
|
9860
|
+
}
|
|
9861
|
+
function getTaskCommits(taskId, db) {
|
|
9862
|
+
const d = db || getDatabase();
|
|
9863
|
+
return d.query("SELECT * FROM task_commits WHERE task_id = ? ORDER BY committed_at DESC, created_at DESC").all(taskId).map(rowToCommit);
|
|
9864
|
+
}
|
|
9865
|
+
function findTaskByCommit(sha, db) {
|
|
9866
|
+
const d = db || getDatabase();
|
|
9867
|
+
const row = d.query("SELECT * FROM task_commits WHERE sha = ? OR sha LIKE ? LIMIT 1").get(sha, `${sha}%`);
|
|
9868
|
+
if (!row)
|
|
9869
|
+
return null;
|
|
9870
|
+
return { task_id: row.task_id, commit: rowToCommit(row) };
|
|
9871
|
+
}
|
|
9872
|
+
function unlinkTaskCommit(taskId, sha, db) {
|
|
9873
|
+
const d = db || getDatabase();
|
|
9874
|
+
return d.run("DELETE FROM task_commits WHERE task_id = ? AND (sha = ? OR sha LIKE ?)", [taskId, sha, `${sha}%`]).changes > 0;
|
|
9875
|
+
}
|
|
9876
|
+
var init_task_commits = __esm(() => {
|
|
9877
|
+
init_database();
|
|
9878
|
+
});
|
|
9879
|
+
|
|
9820
9880
|
// src/lib/auto-assign.ts
|
|
9821
9881
|
var exports_auto_assign = {};
|
|
9822
9882
|
__export(exports_auto_assign, {
|
|
@@ -11354,6 +11414,12 @@ Checklist (${done}/${task.checklist.length}):`);
|
|
|
11354
11414
|
const resolvedId = resolveId(id);
|
|
11355
11415
|
const evidence = files_changed || test_results || commit_hash || notes || attachment_ids ? { files_changed, test_results, commit_hash, notes, attachment_ids } : undefined;
|
|
11356
11416
|
const task = completeTask(resolvedId, agent_id, undefined, { skip_recurrence, confidence, ...evidence });
|
|
11417
|
+
if (commit_hash) {
|
|
11418
|
+
try {
|
|
11419
|
+
const { linkTaskToCommit: linkTaskToCommit2 } = (init_task_commits(), __toCommonJS(exports_task_commits));
|
|
11420
|
+
linkTaskToCommit2({ task_id: resolvedId, sha: commit_hash, files_changed });
|
|
11421
|
+
} catch {}
|
|
11422
|
+
}
|
|
11357
11423
|
let text = `completed: ${formatTask(task)}`;
|
|
11358
11424
|
if (task.metadata._next_recurrence) {
|
|
11359
11425
|
const next = task.metadata._next_recurrence;
|
|
@@ -13816,6 +13882,49 @@ ${lines.join(`
|
|
|
13816
13882
|
}
|
|
13817
13883
|
});
|
|
13818
13884
|
}
|
|
13885
|
+
if (shouldRegisterTool("link_task_to_commit")) {
|
|
13886
|
+
server.tool("link_task_to_commit", "Link a git commit SHA to a task. Creates an audit trail: task \u2192 commits. Upserts on same task+sha.", {
|
|
13887
|
+
task_id: exports_external.string().describe("Task ID"),
|
|
13888
|
+
sha: exports_external.string().describe("Git commit SHA (full or short)"),
|
|
13889
|
+
message: exports_external.string().optional().describe("Commit message"),
|
|
13890
|
+
author: exports_external.string().optional().describe("Commit author"),
|
|
13891
|
+
files_changed: exports_external.array(exports_external.string()).optional().describe("Files changed in this commit"),
|
|
13892
|
+
committed_at: exports_external.string().optional().describe("ISO timestamp of commit")
|
|
13893
|
+
}, async ({ task_id, sha, message, author, files_changed, committed_at }) => {
|
|
13894
|
+
try {
|
|
13895
|
+
const { linkTaskToCommit: linkTaskToCommit2 } = (init_task_commits(), __toCommonJS(exports_task_commits));
|
|
13896
|
+
const resolvedId = resolveId(task_id);
|
|
13897
|
+
const commit = linkTaskToCommit2({ task_id: resolvedId, sha, message, author, files_changed, committed_at });
|
|
13898
|
+
return { content: [{ type: "text", text: JSON.stringify(commit, null, 2) }] };
|
|
13899
|
+
} catch (e) {
|
|
13900
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
13901
|
+
}
|
|
13902
|
+
});
|
|
13903
|
+
}
|
|
13904
|
+
if (shouldRegisterTool("get_task_commits")) {
|
|
13905
|
+
server.tool("get_task_commits", "Get all git commits linked to a task.", { task_id: exports_external.string().describe("Task ID") }, async ({ task_id }) => {
|
|
13906
|
+
try {
|
|
13907
|
+
const { getTaskCommits: getTaskCommits2 } = (init_task_commits(), __toCommonJS(exports_task_commits));
|
|
13908
|
+
const commits = getTaskCommits2(resolveId(task_id));
|
|
13909
|
+
return { content: [{ type: "text", text: JSON.stringify(commits, null, 2) }] };
|
|
13910
|
+
} catch (e) {
|
|
13911
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
13912
|
+
}
|
|
13913
|
+
});
|
|
13914
|
+
}
|
|
13915
|
+
if (shouldRegisterTool("find_task_by_commit")) {
|
|
13916
|
+
server.tool("find_task_by_commit", "Find which task a git commit SHA is linked to. Supports prefix matching.", { sha: exports_external.string().describe("Git commit SHA (full or short prefix)") }, async ({ sha }) => {
|
|
13917
|
+
try {
|
|
13918
|
+
const { findTaskByCommit: findTaskByCommit2 } = (init_task_commits(), __toCommonJS(exports_task_commits));
|
|
13919
|
+
const result = findTaskByCommit2(sha);
|
|
13920
|
+
if (!result)
|
|
13921
|
+
return { content: [{ type: "text", text: `No task linked to commit ${sha}` }] };
|
|
13922
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
13923
|
+
} catch (e) {
|
|
13924
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
13925
|
+
}
|
|
13926
|
+
});
|
|
13927
|
+
}
|
|
13819
13928
|
if (shouldRegisterTool("lock_file")) {
|
|
13820
13929
|
server.tool("lock_file", "Acquire an exclusive lock on a file path. Throws if another agent holds an active lock. Same agent re-locks refreshes the TTL.", {
|
|
13821
13930
|
path: exports_external.string().describe("File path to lock"),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/db/database.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAItC,eAAO,MAAM,mBAAmB,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/db/database.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAItC,eAAO,MAAM,mBAAmB,KAAK,CAAC;AA6jBtC,wBAAgB,WAAW,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,CAkBrD;AAyRD,wBAAgB,aAAa,IAAI,IAAI,CAKpC;AAED,wBAAgB,aAAa,IAAI,IAAI,CAEpC;AAED,wBAAgB,GAAG,IAAI,MAAM,CAE5B;AAED,wBAAgB,IAAI,IAAI,MAAM,CAE7B;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAK9D;AAED,wBAAgB,gBAAgB,CAAC,KAAK,SAAa,GAAG,MAAM,CAG3D;AAED,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,QAAQ,GAAG,IAAI,CAGpD;AAED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CA0B9F"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Database } from "bun:sqlite";
|
|
2
|
+
export interface TaskCommit {
|
|
3
|
+
id: string;
|
|
4
|
+
task_id: string;
|
|
5
|
+
sha: string;
|
|
6
|
+
message: string | null;
|
|
7
|
+
author: string | null;
|
|
8
|
+
files_changed: string[] | null;
|
|
9
|
+
committed_at: string | null;
|
|
10
|
+
created_at: string;
|
|
11
|
+
}
|
|
12
|
+
export interface LinkTaskToCommitInput {
|
|
13
|
+
task_id: string;
|
|
14
|
+
sha: string;
|
|
15
|
+
message?: string;
|
|
16
|
+
author?: string;
|
|
17
|
+
files_changed?: string[];
|
|
18
|
+
committed_at?: string;
|
|
19
|
+
}
|
|
20
|
+
/** Link a git commit SHA to a task. Upserts on same task+sha. */
|
|
21
|
+
export declare function linkTaskToCommit(input: LinkTaskToCommitInput, db?: Database): TaskCommit;
|
|
22
|
+
/** Get all commits linked to a task. */
|
|
23
|
+
export declare function getTaskCommits(taskId: string, db?: Database): TaskCommit[];
|
|
24
|
+
/** Find which task a commit SHA is linked to. */
|
|
25
|
+
export declare function findTaskByCommit(sha: string, db?: Database): {
|
|
26
|
+
task_id: string;
|
|
27
|
+
commit: TaskCommit;
|
|
28
|
+
} | null;
|
|
29
|
+
/** Remove a commit link. */
|
|
30
|
+
export declare function unlinkTaskCommit(taskId: string, sha: string, db?: Database): boolean;
|
|
31
|
+
//# sourceMappingURL=task-commits.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-commits.d.ts","sourceRoot":"","sources":["../../src/db/task-commits.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAG3C,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,aAAa,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAC/B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;CACpB;AAoBD,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,iEAAiE;AACjE,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,UAAU,CAoBxF;AAED,wCAAwC;AACxC,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,UAAU,EAAE,CAG1E;AAED,iDAAiD;AACjD,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,UAAU,CAAA;CAAE,GAAG,IAAI,CAM3G;AAED,4BAA4B;AAC5B,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,OAAO,CAGpF"}
|
package/dist/index.js
CHANGED
|
@@ -718,6 +718,22 @@ var MIGRATIONS = [
|
|
|
718
718
|
INSERT OR IGNORE INTO _migrations (id) VALUES (31);
|
|
719
719
|
`,
|
|
720
720
|
`
|
|
721
|
+
CREATE TABLE IF NOT EXISTS task_commits (
|
|
722
|
+
id TEXT PRIMARY KEY,
|
|
723
|
+
task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
|
|
724
|
+
sha TEXT NOT NULL,
|
|
725
|
+
message TEXT,
|
|
726
|
+
author TEXT,
|
|
727
|
+
files_changed TEXT,
|
|
728
|
+
committed_at TEXT,
|
|
729
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
730
|
+
UNIQUE(task_id, sha)
|
|
731
|
+
);
|
|
732
|
+
CREATE INDEX IF NOT EXISTS idx_task_commits_task ON task_commits(task_id);
|
|
733
|
+
CREATE INDEX IF NOT EXISTS idx_task_commits_sha ON task_commits(sha);
|
|
734
|
+
INSERT OR IGNORE INTO _migrations (id) VALUES (32);
|
|
735
|
+
`,
|
|
736
|
+
`
|
|
721
737
|
CREATE TABLE IF NOT EXISTS file_locks (
|
|
722
738
|
id TEXT PRIMARY KEY,
|
|
723
739
|
path TEXT NOT NULL UNIQUE,
|
|
@@ -729,7 +745,7 @@ var MIGRATIONS = [
|
|
|
729
745
|
CREATE INDEX IF NOT EXISTS idx_file_locks_path ON file_locks(path);
|
|
730
746
|
CREATE INDEX IF NOT EXISTS idx_file_locks_agent ON file_locks(agent_id);
|
|
731
747
|
CREATE INDEX IF NOT EXISTS idx_file_locks_expires ON file_locks(expires_at);
|
|
732
|
-
INSERT OR IGNORE INTO _migrations (id) VALUES (
|
|
748
|
+
INSERT OR IGNORE INTO _migrations (id) VALUES (33);
|
|
733
749
|
`
|
|
734
750
|
];
|
|
735
751
|
var _db = null;
|
package/dist/mcp/index.js
CHANGED
|
@@ -956,6 +956,22 @@ var init_database = __esm(() => {
|
|
|
956
956
|
CREATE INDEX IF NOT EXISTS idx_project_agent_roles_project ON project_agent_roles(project_id);
|
|
957
957
|
CREATE INDEX IF NOT EXISTS idx_project_agent_roles_agent ON project_agent_roles(agent_id);
|
|
958
958
|
INSERT OR IGNORE INTO _migrations (id) VALUES (31);
|
|
959
|
+
`,
|
|
960
|
+
`
|
|
961
|
+
CREATE TABLE IF NOT EXISTS task_commits (
|
|
962
|
+
id TEXT PRIMARY KEY,
|
|
963
|
+
task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
|
|
964
|
+
sha TEXT NOT NULL,
|
|
965
|
+
message TEXT,
|
|
966
|
+
author TEXT,
|
|
967
|
+
files_changed TEXT,
|
|
968
|
+
committed_at TEXT,
|
|
969
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
970
|
+
UNIQUE(task_id, sha)
|
|
971
|
+
);
|
|
972
|
+
CREATE INDEX IF NOT EXISTS idx_task_commits_task ON task_commits(task_id);
|
|
973
|
+
CREATE INDEX IF NOT EXISTS idx_task_commits_sha ON task_commits(sha);
|
|
974
|
+
INSERT OR IGNORE INTO _migrations (id) VALUES (32);
|
|
959
975
|
`,
|
|
960
976
|
`
|
|
961
977
|
CREATE TABLE IF NOT EXISTS file_locks (
|
|
@@ -969,7 +985,7 @@ var init_database = __esm(() => {
|
|
|
969
985
|
CREATE INDEX IF NOT EXISTS idx_file_locks_path ON file_locks(path);
|
|
970
986
|
CREATE INDEX IF NOT EXISTS idx_file_locks_agent ON file_locks(agent_id);
|
|
971
987
|
CREATE INDEX IF NOT EXISTS idx_file_locks_expires ON file_locks(expires_at);
|
|
972
|
-
INSERT OR IGNORE INTO _migrations (id) VALUES (
|
|
988
|
+
INSERT OR IGNORE INTO _migrations (id) VALUES (33);
|
|
973
989
|
`
|
|
974
990
|
];
|
|
975
991
|
});
|
|
@@ -2868,6 +2884,50 @@ var init_agents = __esm(() => {
|
|
|
2868
2884
|
AGENT_ACTIVE_WINDOW_MS = 30 * 60 * 1000;
|
|
2869
2885
|
});
|
|
2870
2886
|
|
|
2887
|
+
// src/db/task-commits.ts
|
|
2888
|
+
var exports_task_commits = {};
|
|
2889
|
+
__export(exports_task_commits, {
|
|
2890
|
+
unlinkTaskCommit: () => unlinkTaskCommit,
|
|
2891
|
+
linkTaskToCommit: () => linkTaskToCommit,
|
|
2892
|
+
getTaskCommits: () => getTaskCommits,
|
|
2893
|
+
findTaskByCommit: () => findTaskByCommit
|
|
2894
|
+
});
|
|
2895
|
+
function rowToCommit(row) {
|
|
2896
|
+
return {
|
|
2897
|
+
...row,
|
|
2898
|
+
files_changed: row.files_changed ? JSON.parse(row.files_changed) : null
|
|
2899
|
+
};
|
|
2900
|
+
}
|
|
2901
|
+
function linkTaskToCommit(input, db) {
|
|
2902
|
+
const d = db || getDatabase();
|
|
2903
|
+
const existing = d.query("SELECT * FROM task_commits WHERE task_id = ? AND sha = ?").get(input.task_id, input.sha);
|
|
2904
|
+
if (existing) {
|
|
2905
|
+
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]);
|
|
2906
|
+
return rowToCommit(d.query("SELECT * FROM task_commits WHERE id = ?").get(existing.id));
|
|
2907
|
+
}
|
|
2908
|
+
const id = uuid();
|
|
2909
|
+
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()]);
|
|
2910
|
+
return rowToCommit(d.query("SELECT * FROM task_commits WHERE id = ?").get(id));
|
|
2911
|
+
}
|
|
2912
|
+
function getTaskCommits(taskId, db) {
|
|
2913
|
+
const d = db || getDatabase();
|
|
2914
|
+
return d.query("SELECT * FROM task_commits WHERE task_id = ? ORDER BY committed_at DESC, created_at DESC").all(taskId).map(rowToCommit);
|
|
2915
|
+
}
|
|
2916
|
+
function findTaskByCommit(sha, db) {
|
|
2917
|
+
const d = db || getDatabase();
|
|
2918
|
+
const row = d.query("SELECT * FROM task_commits WHERE sha = ? OR sha LIKE ? LIMIT 1").get(sha, `${sha}%`);
|
|
2919
|
+
if (!row)
|
|
2920
|
+
return null;
|
|
2921
|
+
return { task_id: row.task_id, commit: rowToCommit(row) };
|
|
2922
|
+
}
|
|
2923
|
+
function unlinkTaskCommit(taskId, sha, db) {
|
|
2924
|
+
const d = db || getDatabase();
|
|
2925
|
+
return d.run("DELETE FROM task_commits WHERE task_id = ? AND (sha = ? OR sha LIKE ?)", [taskId, sha, `${sha}%`]).changes > 0;
|
|
2926
|
+
}
|
|
2927
|
+
var init_task_commits = __esm(() => {
|
|
2928
|
+
init_database();
|
|
2929
|
+
});
|
|
2930
|
+
|
|
2871
2931
|
// src/lib/auto-assign.ts
|
|
2872
2932
|
var exports_auto_assign = {};
|
|
2873
2933
|
__export(exports_auto_assign, {
|
|
@@ -9166,6 +9226,12 @@ if (shouldRegisterTool("complete_task")) {
|
|
|
9166
9226
|
const resolvedId = resolveId(id);
|
|
9167
9227
|
const evidence = files_changed || test_results || commit_hash || notes || attachment_ids ? { files_changed, test_results, commit_hash, notes, attachment_ids } : undefined;
|
|
9168
9228
|
const task = completeTask(resolvedId, agent_id, undefined, { skip_recurrence, confidence, ...evidence });
|
|
9229
|
+
if (commit_hash) {
|
|
9230
|
+
try {
|
|
9231
|
+
const { linkTaskToCommit: linkTaskToCommit2 } = (init_task_commits(), __toCommonJS(exports_task_commits));
|
|
9232
|
+
linkTaskToCommit2({ task_id: resolvedId, sha: commit_hash, files_changed });
|
|
9233
|
+
} catch {}
|
|
9234
|
+
}
|
|
9169
9235
|
let text = `completed: ${formatTask(task)}`;
|
|
9170
9236
|
if (task.metadata._next_recurrence) {
|
|
9171
9237
|
const next = task.metadata._next_recurrence;
|
|
@@ -11628,6 +11694,49 @@ if (shouldRegisterTool("list_active_files")) {
|
|
|
11628
11694
|
}
|
|
11629
11695
|
});
|
|
11630
11696
|
}
|
|
11697
|
+
if (shouldRegisterTool("link_task_to_commit")) {
|
|
11698
|
+
server.tool("link_task_to_commit", "Link a git commit SHA to a task. Creates an audit trail: task \u2192 commits. Upserts on same task+sha.", {
|
|
11699
|
+
task_id: exports_external.string().describe("Task ID"),
|
|
11700
|
+
sha: exports_external.string().describe("Git commit SHA (full or short)"),
|
|
11701
|
+
message: exports_external.string().optional().describe("Commit message"),
|
|
11702
|
+
author: exports_external.string().optional().describe("Commit author"),
|
|
11703
|
+
files_changed: exports_external.array(exports_external.string()).optional().describe("Files changed in this commit"),
|
|
11704
|
+
committed_at: exports_external.string().optional().describe("ISO timestamp of commit")
|
|
11705
|
+
}, async ({ task_id, sha, message, author, files_changed, committed_at }) => {
|
|
11706
|
+
try {
|
|
11707
|
+
const { linkTaskToCommit: linkTaskToCommit2 } = (init_task_commits(), __toCommonJS(exports_task_commits));
|
|
11708
|
+
const resolvedId = resolveId(task_id);
|
|
11709
|
+
const commit = linkTaskToCommit2({ task_id: resolvedId, sha, message, author, files_changed, committed_at });
|
|
11710
|
+
return { content: [{ type: "text", text: JSON.stringify(commit, null, 2) }] };
|
|
11711
|
+
} catch (e) {
|
|
11712
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
11713
|
+
}
|
|
11714
|
+
});
|
|
11715
|
+
}
|
|
11716
|
+
if (shouldRegisterTool("get_task_commits")) {
|
|
11717
|
+
server.tool("get_task_commits", "Get all git commits linked to a task.", { task_id: exports_external.string().describe("Task ID") }, async ({ task_id }) => {
|
|
11718
|
+
try {
|
|
11719
|
+
const { getTaskCommits: getTaskCommits2 } = (init_task_commits(), __toCommonJS(exports_task_commits));
|
|
11720
|
+
const commits = getTaskCommits2(resolveId(task_id));
|
|
11721
|
+
return { content: [{ type: "text", text: JSON.stringify(commits, null, 2) }] };
|
|
11722
|
+
} catch (e) {
|
|
11723
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
11724
|
+
}
|
|
11725
|
+
});
|
|
11726
|
+
}
|
|
11727
|
+
if (shouldRegisterTool("find_task_by_commit")) {
|
|
11728
|
+
server.tool("find_task_by_commit", "Find which task a git commit SHA is linked to. Supports prefix matching.", { sha: exports_external.string().describe("Git commit SHA (full or short prefix)") }, async ({ sha }) => {
|
|
11729
|
+
try {
|
|
11730
|
+
const { findTaskByCommit: findTaskByCommit2 } = (init_task_commits(), __toCommonJS(exports_task_commits));
|
|
11731
|
+
const result = findTaskByCommit2(sha);
|
|
11732
|
+
if (!result)
|
|
11733
|
+
return { content: [{ type: "text", text: `No task linked to commit ${sha}` }] };
|
|
11734
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
11735
|
+
} catch (e) {
|
|
11736
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
11737
|
+
}
|
|
11738
|
+
});
|
|
11739
|
+
}
|
|
11631
11740
|
if (shouldRegisterTool("lock_file")) {
|
|
11632
11741
|
server.tool("lock_file", "Acquire an exclusive lock on a file path. Throws if another agent holds an active lock. Same agent re-locks refreshes the TTL.", {
|
|
11633
11742
|
path: exports_external.string().describe("File path to lock"),
|
package/dist/server/index.js
CHANGED
|
@@ -872,6 +872,22 @@ var init_database = __esm(() => {
|
|
|
872
872
|
CREATE INDEX IF NOT EXISTS idx_project_agent_roles_project ON project_agent_roles(project_id);
|
|
873
873
|
CREATE INDEX IF NOT EXISTS idx_project_agent_roles_agent ON project_agent_roles(agent_id);
|
|
874
874
|
INSERT OR IGNORE INTO _migrations (id) VALUES (31);
|
|
875
|
+
`,
|
|
876
|
+
`
|
|
877
|
+
CREATE TABLE IF NOT EXISTS task_commits (
|
|
878
|
+
id TEXT PRIMARY KEY,
|
|
879
|
+
task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
|
|
880
|
+
sha TEXT NOT NULL,
|
|
881
|
+
message TEXT,
|
|
882
|
+
author TEXT,
|
|
883
|
+
files_changed TEXT,
|
|
884
|
+
committed_at TEXT,
|
|
885
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
886
|
+
UNIQUE(task_id, sha)
|
|
887
|
+
);
|
|
888
|
+
CREATE INDEX IF NOT EXISTS idx_task_commits_task ON task_commits(task_id);
|
|
889
|
+
CREATE INDEX IF NOT EXISTS idx_task_commits_sha ON task_commits(sha);
|
|
890
|
+
INSERT OR IGNORE INTO _migrations (id) VALUES (32);
|
|
875
891
|
`,
|
|
876
892
|
`
|
|
877
893
|
CREATE TABLE IF NOT EXISTS file_locks (
|
|
@@ -885,7 +901,7 @@ var init_database = __esm(() => {
|
|
|
885
901
|
CREATE INDEX IF NOT EXISTS idx_file_locks_path ON file_locks(path);
|
|
886
902
|
CREATE INDEX IF NOT EXISTS idx_file_locks_agent ON file_locks(agent_id);
|
|
887
903
|
CREATE INDEX IF NOT EXISTS idx_file_locks_expires ON file_locks(expires_at);
|
|
888
|
-
INSERT OR IGNORE INTO _migrations (id) VALUES (
|
|
904
|
+
INSERT OR IGNORE INTO _migrations (id) VALUES (33);
|
|
889
905
|
`
|
|
890
906
|
];
|
|
891
907
|
});
|