Skip to content

Commit 26d5797

Browse files
authored
feat(engine): upgrade agentdb to 1.2.0, async reads + array indexes — v2.1.0 (#15)
Adapt to agentdb 1.2.0 breaking change: Collection read methods (findOne, find, findAll, count) are now async. Add await to all 10 call sites. findTask() becomes async, cascading to all callers. Add arrayIndexes on tags and depends for O(1) $contains lookups.
1 parent e502f21 commit 26d5797

9 files changed

Lines changed: 85 additions & 51 deletions

File tree

.claude-plugin/marketplace.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"name": "backlog",
1313
"source": "./",
1414
"description": "Persistent, cross-session task management for Claude Code. 24 tools, 7 skills, agent coordination, doc-driven development.",
15-
"version": "2.0.2",
15+
"version": "2.1.0",
1616
"author": {
1717
"name": "BacklogHQ"
1818
},

.claude-plugin/plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "backlog",
3-
"version": "2.0.2",
3+
"version": "2.1.0",
44
"description": "Persistent, cross-session task management for Claude Code. Tasks survive sessions so work started by one agent can be picked up by another.",
55
"author": {
66
"name": "BacklogHQ"

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
# Changelog
22

3+
## 2.1.0 (2026-04-11)
4+
5+
### Changed
6+
- **Upgraded `@backloghq/agentdb` from 1.1.1 to 1.2.0** — adapts to async Collection read methods (`findOne`, `find`, `findAll`, `count`). All engine read paths now `await` these calls, enabling future disk-backed storage mode.
7+
- **`findTask()` is now async** — cascades `await` to all callers: `taskCommand`, `writeDoc`, `readDoc`, `deleteDoc`, `duplicateTask`.
8+
9+
### Added
10+
- **Array indexes on `tags` and `depends`** — uses agentdb's new `arrayIndexes` schema option for O(1) `$contains` lookups. Tag filters (`+bug`, `-old`) and dependency checks are now index-accelerated instead of full-scan.
11+
312
## 2.0.2 (2026-04-11)
413

514
### Fixed

dist/engine/index.js

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ async function drainSyncQueue() {
118118
});
119119
}
120120
else if (entry.completed) {
121-
const matches = c.find({ filter: { status: "pending", description: entry.completed } });
121+
const matches = await c.find({ filter: { status: "pending", description: entry.completed } });
122122
if (matches.records.length === 1) {
123123
const match = matches.records[0];
124124
await c.update({ _id: match._id }, { $set: { status: "completed", end: now(), modified: now() } });
@@ -130,7 +130,7 @@ async function drainSyncQueue() {
130130
else if (entry.subagent_start) {
131131
const agentName = entry.subagent_start;
132132
// Query once, not per-entry — avoid O(n²) if multiple subagent_start entries
133-
for (const task of c.findAll()) {
133+
for (const task of await c.findAll()) {
134134
if (task.status === "pending" && !task.agent) {
135135
await c.update({ _id: task._id }, { $set: { agent: agentName, modified: now() } });
136136
}
@@ -214,16 +214,16 @@ function toTask(record) {
214214
const { _id, _version, ...rest } = record;
215215
return { uuid: _id, ...rest };
216216
}
217-
function findTask(id) {
217+
async function findTask(id) {
218218
const c = getCol();
219219
// Try as UUID/_id
220-
const byId = c.findOne(id);
220+
const byId = await c.findOne(id);
221221
if (byId)
222222
return byId;
223223
// Try as numeric ID
224224
const numId = parseInt(id, 10);
225225
if (!isNaN(numId)) {
226-
const result = c.find({ filter: { id: numId }, limit: 1 });
226+
const result = await c.find({ filter: { id: numId }, limit: 1 });
227227
return result.records[0];
228228
}
229229
return undefined;
@@ -233,7 +233,7 @@ export async function exportTasks(_config, filter) {
233233
await drainSyncQueue();
234234
const c = getCol();
235235
// Generate recurring task instances
236-
const allRecords = c.findAll();
236+
const allRecords = await c.findAll();
237237
const allTasks = allRecords.map(toTask);
238238
let idCounter = 0;
239239
for (const t of allTasks) {
@@ -259,7 +259,7 @@ export async function exportTasks(_config, filter) {
259259
const filterObj = compileFilter(filter);
260260
const filtered = Object.keys(filterObj).length === 0
261261
? allRecords
262-
: c.find({ filter: filterObj, limit: 10000 }).records;
262+
: (await c.find({ filter: filterObj, limit: 10000 })).records;
263263
// Ensure urgency on filtered results (find() returns fresh records without urgency)
264264
if (Object.keys(filterObj).length > 0) {
265265
for (const record of filtered) {
@@ -314,8 +314,8 @@ export async function modifyTask(_config, filter, attrs, extraArgs = []) {
314314
const c = getCol();
315315
const filterObj = compileFilter(filter);
316316
const matches = Object.keys(filterObj).length === 0
317-
? c.findAll()
318-
: c.find({ filter: filterObj, limit: 10000 }).records;
317+
? await c.findAll()
318+
: (await c.find({ filter: filterObj, limit: 10000 })).records;
319319
if (matches.length === 0)
320320
return "No matching tasks.";
321321
let modified = 0;
@@ -381,7 +381,7 @@ export async function modifyTask(_config, filter, attrs, extraArgs = []) {
381381
}
382382
export async function taskCommand(_config, id, command, extraArgs = []) {
383383
const c = getCol();
384-
const record = findTask(id);
384+
const record = await findTask(id);
385385
if (!record)
386386
return `No task found matching '${id}'.`;
387387
const taskId = record._id;
@@ -458,7 +458,7 @@ export async function logTask(_config, description, attrs, extraArgs = []) {
458458
return "Task logged.";
459459
}
460460
export async function duplicateTask(_config, id, attrs, extraArgs = []) {
461-
const record = findTask(id);
461+
const record = await findTask(id);
462462
if (!record)
463463
return `No task found matching '${id}'.`;
464464
const c = getCol();
@@ -546,7 +546,7 @@ export async function getUnique(_config, attribute) {
546546
await drainSyncQueue();
547547
const c = getCol();
548548
const values = new Set();
549-
const records = c.findAll();
549+
const records = await c.findAll();
550550
for (const record of records) {
551551
if (record.status !== "pending" && record.status !== "recurring")
552552
continue;
@@ -562,7 +562,7 @@ export async function getUnique(_config, attribute) {
562562
}
563563
// --- Doc operations (blob API) ---
564564
export async function writeDoc(_config, id, content) {
565-
const record = findTask(id);
565+
const record = await findTask(id);
566566
if (!record)
567567
throw new Error(`No task found matching '${id}'`);
568568
const c = getCol();
@@ -575,7 +575,7 @@ export async function writeDoc(_config, id, content) {
575575
return `Doc written for task ${taskId}.`;
576576
}
577577
export async function readDoc(_config, id) {
578-
const record = findTask(id);
578+
const record = await findTask(id);
579579
if (!record)
580580
throw new Error(`No task found matching '${id}'`);
581581
const c = getCol();
@@ -588,7 +588,7 @@ export async function readDoc(_config, id) {
588588
}
589589
}
590590
export async function deleteDoc(_config, id) {
591-
const record = findTask(id);
591+
const record = await findTask(id);
592592
if (!record)
593593
throw new Error(`No task found matching '${id}'`);
594594
const c = getCol();
@@ -606,7 +606,7 @@ export async function archiveTasks(_config, olderThanDays = 90) {
606606
const c = getCol();
607607
const cutoffISO = new Date(Date.now() - olderThanDays * 86400000).toISOString();
608608
// Use predicate-based archive to handle complex date+status filter
609-
const allRecords = c.findAll();
609+
const allRecords = await c.findAll();
610610
const toArchive = [];
611611
for (const r of allRecords) {
612612
if ((r.status === "completed" || r.status === "deleted") && r.end && new Date(r.end).getTime() <= new Date(cutoffISO).getTime()) {

dist/engine/task-schema.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export const taskSchema = defineSchema({
3636
has_doc: { type: "boolean" },
3737
},
3838
indexes: ["status", "project", "priority"],
39+
arrayIndexes: ["tags", "depends"],
3940
virtualFilters: {
4041
"+OVERDUE": (t) => t.status === "pending" && !!t.due && isOverdue(t.due),
4142
"+ACTIVE": (t) => t.status === "pending" && !!t.start,

package-lock.json

Lines changed: 38 additions & 15 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "agent-teams-task-mcp",
3-
"version": "2.0.2",
3+
"version": "2.1.0",
44
"description": "Persistent task management plugin for Claude Code and agent teams",
55
"main": "dist/index.js",
66
"scripts": {
@@ -20,7 +20,7 @@
2020
"license": "MIT",
2121
"type": "module",
2222
"dependencies": {
23-
"@backloghq/agentdb": "^1.1.1",
23+
"@backloghq/agentdb": "^1.2.0",
2424
"@cfworker/json-schema": "^4.1.1",
2525
"@modelcontextprotocol/server": "^2.0.0-alpha.2",
2626
"zod": "^4.3.6"

0 commit comments

Comments
 (0)