aitasks 1.3.2 → 1.3.4
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 +2 -1
- package/dist/index.js +88 -40
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -140,7 +140,8 @@ aitasks create \
|
|
|
140
140
|
--ac "Returns 200" \ # Acceptance criterion (repeatable, at least one required)
|
|
141
141
|
--priority high \ # critical | high | medium | low
|
|
142
142
|
--type feature \ # feature | bug | chore | spike
|
|
143
|
-
--parent TASK-001
|
|
143
|
+
--parent TASK-001 \ # Parent task ID (optional)
|
|
144
|
+
--agent $AITASKS_AGENT_ID # Agent creating the task (logged in event history)
|
|
144
145
|
```
|
|
145
146
|
|
|
146
147
|
---
|
package/dist/index.js
CHANGED
|
@@ -1890,7 +1890,7 @@ var require_commander = __commonJS((exports) => {
|
|
|
1890
1890
|
var require_package = __commonJS((exports, module) => {
|
|
1891
1891
|
module.exports = {
|
|
1892
1892
|
name: "aitasks",
|
|
1893
|
-
version: "1.3.
|
|
1893
|
+
version: "1.3.4",
|
|
1894
1894
|
description: "CLI task management tool built for AI agents",
|
|
1895
1895
|
type: "module",
|
|
1896
1896
|
bin: {
|
|
@@ -29071,7 +29071,7 @@ import { join as join3 } from "path";
|
|
|
29071
29071
|
import { existsSync as existsSync3 } from "fs";
|
|
29072
29072
|
|
|
29073
29073
|
// src/db/schema.ts
|
|
29074
|
-
var SCHEMA_VERSION =
|
|
29074
|
+
var SCHEMA_VERSION = 3;
|
|
29075
29075
|
function runMigrations(db) {
|
|
29076
29076
|
const row = db.query(`SELECT value FROM meta WHERE key = 'schema_version'`).get();
|
|
29077
29077
|
const current = row ? parseInt(row.value, 10) : 1;
|
|
@@ -29080,6 +29080,11 @@ function runMigrations(db) {
|
|
|
29080
29080
|
db.exec(`INSERT OR IGNORE INTO meta (key, value) VALUES ('review_required', 'false')`);
|
|
29081
29081
|
db.exec(`UPDATE meta SET value = '2' WHERE key = 'schema_version'`);
|
|
29082
29082
|
}
|
|
29083
|
+
if (current < 3) {
|
|
29084
|
+
db.exec(`ALTER TABLE agents ADD COLUMN first_seen INTEGER`);
|
|
29085
|
+
db.exec(`UPDATE agents SET first_seen = last_seen WHERE first_seen IS NULL`);
|
|
29086
|
+
db.exec(`UPDATE meta SET value = '3' WHERE key = 'schema_version'`);
|
|
29087
|
+
}
|
|
29083
29088
|
}
|
|
29084
29089
|
function initializeSchema(db) {
|
|
29085
29090
|
db.exec(`
|
|
@@ -29111,6 +29116,7 @@ function initializeSchema(db) {
|
|
|
29111
29116
|
|
|
29112
29117
|
CREATE TABLE IF NOT EXISTS agents (
|
|
29113
29118
|
id TEXT PRIMARY KEY,
|
|
29119
|
+
first_seen INTEGER NOT NULL,
|
|
29114
29120
|
last_seen INTEGER NOT NULL,
|
|
29115
29121
|
current_task TEXT REFERENCES tasks(id) ON DELETE SET NULL
|
|
29116
29122
|
);
|
|
@@ -29383,7 +29389,7 @@ Always note:
|
|
|
29383
29389
|
|
|
29384
29390
|
Creating subtasks:
|
|
29385
29391
|
\`\`\`bash
|
|
29386
|
-
aitasks create --title "Write unit tests for auth" --desc "Add unit tests covering all auth edge cases" --ac "All tests pass" --ac "Coverage \u2265 90%" --parent TASK-001 --priority high --type chore
|
|
29392
|
+
aitasks create --title "Write unit tests for auth" --desc "Add unit tests covering all auth edge cases" --ac "All tests pass" --ac "Coverage \u2265 90%" --parent TASK-001 --priority high --type chore --agent $AITASKS_AGENT_ID
|
|
29387
29393
|
\`\`\`
|
|
29388
29394
|
|
|
29389
29395
|
If you discover your task is blocked by something:
|
|
@@ -29420,17 +29426,22 @@ ${reviewRequired ? `3. Submit for review \u2014 you CANNOT mark done directly:
|
|
|
29420
29426
|
\`\`\`bash
|
|
29421
29427
|
aitasks review TASK-001 --agent $AITASKS_AGENT_ID
|
|
29422
29428
|
\`\`\`
|
|
29429
|
+
**STOP HERE. Do not run \`aitasks done\` yourself.** The system will block self-approval.
|
|
29430
|
+
|
|
29431
|
+
4. **IMMEDIATELY spawn a review sub-agent with a DIFFERENT agent ID** to inspect the implementation.
|
|
29432
|
+
The review sub-agent must first register itself, then approve or reject:
|
|
29433
|
+
\`\`\`bash
|
|
29434
|
+
# Review sub-agent registers itself
|
|
29435
|
+
aitasks heartbeat --agent <review-agent-id>
|
|
29423
29436
|
|
|
29424
|
-
|
|
29425
|
-
|
|
29426
|
-
|
|
29427
|
-
|
|
29428
|
-
|
|
29429
|
-
|
|
29430
|
-
|
|
29431
|
-
|
|
29432
|
-
aitasks reject TASK-001 --reason "<specific feedback>" --agent <review-agent-id>
|
|
29433
|
-
\`\`\`
|
|
29437
|
+
# Approve (moves task to done):
|
|
29438
|
+
aitasks done TASK-001 --agent <review-agent-id>
|
|
29439
|
+
|
|
29440
|
+
# OR reject (sends task back to in_progress with feedback):
|
|
29441
|
+
aitasks reject TASK-001 --reason "<specific feedback>" --agent <review-agent-id>
|
|
29442
|
+
\`\`\`
|
|
29443
|
+
The task is still incomplete until the review agent approves it.
|
|
29444
|
+
**The system will block approval from any agent that has not registered or that submitted the review itself.**
|
|
29434
29445
|
|
|
29435
29446
|
5. If rejected: address the feedback, re-check criteria, and repeat from step 3.
|
|
29436
29447
|
|
|
@@ -29478,8 +29489,8 @@ aitasks unclaim TASK-001 --agent $AITASKS_AGENT_ID --reason "Blocked on missing
|
|
|
29478
29489
|
5. Add implementation notes continuously, not just at the end.
|
|
29479
29490
|
6. If a task needs splitting, create subtasks BEFORE marking parent done.
|
|
29480
29491
|
7. Your evidence strings must be concrete and verifiable \u2014 not vague affirmations.
|
|
29481
|
-
8. Always provide --desc
|
|
29482
|
-
9. NEVER move a task to done directly. Always submit for review first with \`aitasks review\`, then IMMEDIATELY spawn a review sub-agent
|
|
29492
|
+
8. Always provide --desc, at least one --ac, and --agent when creating a task. All three are required.${reviewRequired ? `
|
|
29493
|
+
9. NEVER move a task to done directly. Always submit for review first with \`aitasks review\`, then IMMEDIATELY spawn a review sub-agent with a DIFFERENT agent ID. Do NOT call \`aitasks done\` yourself after submitting for review \u2014 the system blocks self-approval.` : ""}
|
|
29483
29494
|
|
|
29484
29495
|
---
|
|
29485
29496
|
|
|
@@ -29491,7 +29502,7 @@ aitasks list [--status <s>] [--json] List tasks
|
|
|
29491
29502
|
aitasks show <id> Full task detail (includes time tracking)
|
|
29492
29503
|
aitasks search <query> Search titles, descriptions, notes
|
|
29493
29504
|
aitasks deps <id> Show dependency tree
|
|
29494
|
-
aitasks create --title <t> --desc <d> --ac <c> [--ac <c> ...] Create a task
|
|
29505
|
+
aitasks create --title <t> --desc <d> --ac <c> [--ac <c> ...] --agent <id> Create a task
|
|
29495
29506
|
aitasks claim <id...> --agent <id> Claim task(s) - supports patterns like TASK-0*
|
|
29496
29507
|
aitasks start <id...> --agent <id> Begin work on task(s)
|
|
29497
29508
|
aitasks note <id> <text> --agent <id> Add implementation note
|
|
@@ -30318,7 +30329,7 @@ function createTask(data) {
|
|
|
30318
30329
|
now,
|
|
30319
30330
|
JSON.stringify(data.metadata ?? {})
|
|
30320
30331
|
]);
|
|
30321
|
-
logEvent({ task_id: id, event_type: "created", payload: { title: data.title } });
|
|
30332
|
+
logEvent({ task_id: id, agent_id: data.created_by, event_type: "created", payload: { title: data.title } });
|
|
30322
30333
|
return getTask(id);
|
|
30323
30334
|
}
|
|
30324
30335
|
var JSON_FIELDS = new Set([
|
|
@@ -30404,9 +30415,9 @@ function claimTask(taskId, agentId) {
|
|
|
30404
30415
|
error: `Task is blocked by: ${task.blocked_by.join(", ")}. Complete those first.`
|
|
30405
30416
|
};
|
|
30406
30417
|
}
|
|
30407
|
-
db.run(`INSERT INTO agents (id, last_seen, current_task)
|
|
30408
|
-
VALUES (?, ?, ?)
|
|
30409
|
-
ON CONFLICT(id) DO UPDATE SET last_seen = excluded.last_seen, current_task = excluded.current_task`, [agentId, Date.now(), taskId]);
|
|
30418
|
+
db.run(`INSERT INTO agents (id, first_seen, last_seen, current_task)
|
|
30419
|
+
VALUES (?, ?, ?, ?)
|
|
30420
|
+
ON CONFLICT(id) DO UPDATE SET last_seen = excluded.last_seen, current_task = excluded.current_task`, [agentId, Date.now(), Date.now(), taskId]);
|
|
30410
30421
|
const updated = updateTask(taskId, { assigned_to: agentId, status: "ready" });
|
|
30411
30422
|
logEvent({ task_id: taskId, agent_id: agentId, event_type: "claimed", payload: {} });
|
|
30412
30423
|
return { task: updated };
|
|
@@ -30422,9 +30433,9 @@ function startTask(taskId, agentId) {
|
|
|
30422
30433
|
}
|
|
30423
30434
|
const db = getDb();
|
|
30424
30435
|
if (!task.assigned_to) {
|
|
30425
|
-
db.run(`INSERT INTO agents (id, last_seen, current_task)
|
|
30426
|
-
VALUES (?, ?, ?)
|
|
30427
|
-
ON CONFLICT(id) DO UPDATE SET last_seen = excluded.last_seen, current_task = excluded.current_task`, [agentId, Date.now(), taskId]);
|
|
30436
|
+
db.run(`INSERT INTO agents (id, first_seen, last_seen, current_task)
|
|
30437
|
+
VALUES (?, ?, ?, ?)
|
|
30438
|
+
ON CONFLICT(id) DO UPDATE SET last_seen = excluded.last_seen, current_task = excluded.current_task`, [agentId, Date.now(), Date.now(), taskId]);
|
|
30428
30439
|
}
|
|
30429
30440
|
const updated = updateTask(taskId, {
|
|
30430
30441
|
assigned_to: agentId,
|
|
@@ -30448,16 +30459,45 @@ function completeTask(taskId, agentId) {
|
|
|
30448
30459
|
if (unchecked.length > 0) {
|
|
30449
30460
|
return { task: null, error: "Not all acceptance criteria are verified", unchecked };
|
|
30450
30461
|
}
|
|
30451
|
-
if (getReviewRequired()
|
|
30452
|
-
|
|
30453
|
-
|
|
30454
|
-
|
|
30462
|
+
if (getReviewRequired()) {
|
|
30463
|
+
if (task.status !== "review") {
|
|
30464
|
+
return {
|
|
30465
|
+
task: null,
|
|
30466
|
+
error: `Review required: submit this task for review first.
|
|
30455
30467
|
` + ` 1. aitasks review <taskId> --agent $AITASKS_AGENT_ID
|
|
30456
30468
|
` + ` 2. Spawn a review sub-agent to inspect the work
|
|
30457
30469
|
` + ` 3. Review agent approves: aitasks done <taskId> --agent <review-agent-id>
|
|
30458
30470
|
` + ` Review agent rejects: aitasks reject <taskId> --reason "<feedback>"
|
|
30459
30471
|
` + " Tasks cannot be moved to Done without a passing review."
|
|
30460
|
-
|
|
30472
|
+
};
|
|
30473
|
+
}
|
|
30474
|
+
if (task.assigned_to && task.assigned_to === agentId) {
|
|
30475
|
+
return {
|
|
30476
|
+
task: null,
|
|
30477
|
+
error: `Self-approval blocked: ${agentId} is the implementing agent and cannot approve their own review.
|
|
30478
|
+
` + ` A separate review sub-agent must run: aitasks done ${taskId} --agent <review-agent-id>`
|
|
30479
|
+
};
|
|
30480
|
+
}
|
|
30481
|
+
const events = getTaskEvents(taskId);
|
|
30482
|
+
const reviewEvent = [...events].reverse().find((e2) => e2.event_type === "review_requested");
|
|
30483
|
+
if (reviewEvent?.agent_id && reviewEvent.agent_id === agentId) {
|
|
30484
|
+
return {
|
|
30485
|
+
task: null,
|
|
30486
|
+
error: `Self-approval blocked: ${agentId} submitted this task for review and cannot also approve it.
|
|
30487
|
+
` + ` A separate review sub-agent must run: aitasks done ${taskId} --agent <review-agent-id>`
|
|
30488
|
+
};
|
|
30489
|
+
}
|
|
30490
|
+
const reviewerRow = db.query(`SELECT first_seen FROM agents WHERE id = ?`).get(agentId);
|
|
30491
|
+
const reviewSubmittedAt = reviewEvent?.created_at ?? 0;
|
|
30492
|
+
if (!reviewerRow || reviewerRow.first_seen > reviewSubmittedAt) {
|
|
30493
|
+
return {
|
|
30494
|
+
task: null,
|
|
30495
|
+
error: `Review agent '${agentId}' was not active before this review was submitted.
|
|
30496
|
+
` + ` A real review sub-agent must be independently spawned \u2014 it cannot be registered
|
|
30497
|
+
` + ` moments before approving. The reviewer must have prior activity in the system
|
|
30498
|
+
` + ` before the review was submitted.`
|
|
30499
|
+
};
|
|
30500
|
+
}
|
|
30461
30501
|
}
|
|
30462
30502
|
const updated = updateTask(taskId, { status: "done", completed_at: Date.now() });
|
|
30463
30503
|
logEvent({ task_id: taskId, agent_id: agentId, event_type: "completed", payload: {} });
|
|
@@ -30589,9 +30629,9 @@ function unclaimTask(taskId, agentId, reason) {
|
|
|
30589
30629
|
}
|
|
30590
30630
|
function heartbeat(agentId, taskId) {
|
|
30591
30631
|
const db = getDb();
|
|
30592
|
-
db.run(`INSERT INTO agents (id, last_seen, current_task)
|
|
30593
|
-
VALUES (?, ?, ?)
|
|
30594
|
-
ON CONFLICT(id) DO UPDATE SET last_seen = excluded.last_seen`, [agentId, Date.now(), taskId ?? null]);
|
|
30632
|
+
db.run(`INSERT INTO agents (id, first_seen, last_seen, current_task)
|
|
30633
|
+
VALUES (?, ?, ?, ?)
|
|
30634
|
+
ON CONFLICT(id) DO UPDATE SET last_seen = excluded.last_seen`, [agentId, Date.now(), Date.now(), taskId ?? null]);
|
|
30595
30635
|
}
|
|
30596
30636
|
function listAgents() {
|
|
30597
30637
|
const db = getDb();
|
|
@@ -30689,7 +30729,7 @@ function exitError(msg, json) {
|
|
|
30689
30729
|
}
|
|
30690
30730
|
|
|
30691
30731
|
// src/commands/create.ts
|
|
30692
|
-
var createCommand2 = new Command("create").description("Create a new task").option("-t, --title <title>", "Task title").option("-d, --desc <description>", "Task description").option("-a, --ac <criterion>", "Acceptance criterion (repeatable)", collect, []).option("-p, --priority <priority>", "Priority: critical|high|medium|low", "medium").option("--type <type>", "Type: feature|bug|chore|spike", "feature").option("--parent <taskId>", "Parent task ID for subtasks").option("--json", "Output as JSON").action(async (opts) => {
|
|
30732
|
+
var createCommand2 = new Command("create").description("Create a new task").option("-t, --title <title>", "Task title").option("-d, --desc <description>", "Task description").option("-a, --ac <criterion>", "Acceptance criterion (repeatable)", collect, []).option("-p, --priority <priority>", "Priority: critical|high|medium|low", "medium").option("--type <type>", "Type: feature|bug|chore|spike", "feature").option("--parent <taskId>", "Parent task ID for subtasks").option("--agent <agentId>", "Agent ID creating this task (or set AITASKS_AGENT_ID)").option("--json", "Output as JSON").action(async (opts) => {
|
|
30693
30733
|
requireInitialized();
|
|
30694
30734
|
const json = isJsonMode(opts.json);
|
|
30695
30735
|
if (opts.title) {
|
|
@@ -30714,13 +30754,15 @@ var createCommand2 = new Command("create").description("Create a new task").opti
|
|
|
30714
30754
|
process.exit(1);
|
|
30715
30755
|
}
|
|
30716
30756
|
}
|
|
30757
|
+
const aid = agentId(opts.agent) ?? undefined;
|
|
30717
30758
|
const task2 = createTask({
|
|
30718
30759
|
title: opts.title,
|
|
30719
30760
|
description: opts.desc ?? "",
|
|
30720
30761
|
acceptance_criteria: opts.ac,
|
|
30721
30762
|
priority: opts.priority,
|
|
30722
30763
|
type: opts.type,
|
|
30723
|
-
parent_id: opts.parent
|
|
30764
|
+
parent_id: opts.parent,
|
|
30765
|
+
created_by: aid
|
|
30724
30766
|
});
|
|
30725
30767
|
if (json)
|
|
30726
30768
|
return jsonOut(true, task2);
|
|
@@ -41322,10 +41364,10 @@ var reviewCommand = new Command("review").description("Request human review for
|
|
|
41322
41364
|
id: taskId,
|
|
41323
41365
|
success: true,
|
|
41324
41366
|
status: "review",
|
|
41325
|
-
next_action:
|
|
41367
|
+
next_action: `REQUIRED: Spawn a review sub-agent immediately with a DIFFERENT agent ID (e.g. review-${taskId.toLowerCase()}). Do NOT call aitasks done yourself \u2014 the system will block self-approval.`,
|
|
41326
41368
|
review_commands: {
|
|
41327
|
-
approve: `aitasks done ${taskId} --agent
|
|
41328
|
-
reject: `aitasks reject ${taskId} --reason "<specific feedback>" --agent
|
|
41369
|
+
approve: `aitasks done ${taskId} --agent review-${taskId.toLowerCase()}`,
|
|
41370
|
+
reject: `aitasks reject ${taskId} --reason "<specific feedback>" --agent review-${taskId.toLowerCase()}`
|
|
41329
41371
|
}
|
|
41330
41372
|
});
|
|
41331
41373
|
} else {
|
|
@@ -41340,10 +41382,16 @@ var reviewCommand = new Command("review").description("Request human review for
|
|
|
41340
41382
|
console.log(` You MUST ${source_default.bold("immediately spawn a review sub-agent")} to inspect the implementation.`);
|
|
41341
41383
|
console.log(source_default.dim(" The task remains incomplete until the review agent moves it to done."));
|
|
41342
41384
|
console.log("");
|
|
41385
|
+
console.log(source_default.yellow(" \u2716 ") + source_default.bold("STOP \u2014 do NOT run `aitasks done` yourself."));
|
|
41386
|
+
console.log(source_default.dim(" The system will block self-approval. A separate agent must do the review."));
|
|
41387
|
+
console.log("");
|
|
41343
41388
|
console.log(source_default.dim(" Review sub-agent steps:"));
|
|
41344
|
-
console.log(source_default.dim(` 1.
|
|
41345
|
-
console.log(source_default.dim(` 2.
|
|
41346
|
-
console.log(source_default.dim(`
|
|
41389
|
+
console.log(source_default.dim(` 1. Spawn a sub-agent with a unique agent ID, e.g. review-${task.id.toLowerCase()}`));
|
|
41390
|
+
console.log(source_default.dim(` 2. Sub-agent registers: aitasks heartbeat --agent review-${task.id.toLowerCase()}`));
|
|
41391
|
+
console.log(source_default.dim(` 3. Sub-agent examines implementation and verifies all acceptance criteria`));
|
|
41392
|
+
console.log(source_default.dim(` 4. Approve: aitasks done ${task.id} --agent review-${task.id.toLowerCase()}`));
|
|
41393
|
+
console.log(source_default.dim(` Reject: aitasks reject ${task.id} --reason "<feedback>" --agent review-${task.id.toLowerCase()}`));
|
|
41394
|
+
console.log(source_default.dim(` The system will reject approval from unregistered or self-approving agents.`));
|
|
41347
41395
|
} else {
|
|
41348
41396
|
console.log(source_default.dim(` Approve: aitasks done ${task.id} --agent <review-agent-id>`));
|
|
41349
41397
|
console.log(source_default.dim(` Reject: aitasks reject ${task.id} --reason "<feedback>"`));
|
|
@@ -43280,4 +43328,4 @@ program2.parseAsync(process.argv).catch((err) => {
|
|
|
43280
43328
|
process.exit(1);
|
|
43281
43329
|
});
|
|
43282
43330
|
|
|
43283
|
-
//# debugId=
|
|
43331
|
+
//# debugId=6CE8123AAE8C303564756E2164756E21
|