aitasks 1.3.3 → 1.3.5

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.
Files changed (3) hide show
  1. package/README.md +2 -1
  2. package/dist/index.js +73 -37
  3. 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 # Parent task ID (optional)
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.3",
1893
+ version: "1.3.5",
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 = 2;
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,12 +29426,13 @@ ${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
  \`\`\`
29423
- **STOP HERE. Do not run \`aitasks done\` yourself.** The system will block self-approval.
29429
+ **Do not run \`aitasks done\` yourself.** The system will block self-approval.
29424
29430
 
29425
- 4. **IMMEDIATELY spawn a review sub-agent with a DIFFERENT agent ID** to inspect the implementation.
29426
- The review sub-agent must first register itself, then approve or reject:
29431
+ 4. **IN THIS SAME SESSION, immediately spawn a review sub-agent** with a DIFFERENT agent ID.
29432
+ Do this NOW \u2014 before writing any summary, before moving to any other task, before anything else.
29433
+ The review sub-agent must register first, then inspect and approve or reject:
29427
29434
  \`\`\`bash
29428
- # Review sub-agent registers itself
29435
+ # Review sub-agent registers itself (BEFORE the review was submitted \u2014 or it will be blocked)
29429
29436
  aitasks heartbeat --agent <review-agent-id>
29430
29437
 
29431
29438
  # Approve (moves task to done):
@@ -29434,12 +29441,15 @@ ${reviewRequired ? `3. Submit for review \u2014 you CANNOT mark done directly:
29434
29441
  # OR reject (sends task back to in_progress with feedback):
29435
29442
  aitasks reject TASK-001 --reason "<specific feedback>" --agent <review-agent-id>
29436
29443
  \`\`\`
29437
- The task is still incomplete until the review agent approves it.
29438
- **The system will block approval from any agent that has not registered or that submitted the review itself.**
29444
+ **Your session is NOT complete after submitting for review. You must remain active and drive the
29445
+ review to completion. The task is still incomplete until \`aitasks done\` succeeds.**
29446
+ The system will block approval from any agent that registered after the review was submitted
29447
+ or that submitted the review itself.
29439
29448
 
29440
29449
  5. If rejected: address the feedback, re-check criteria, and repeat from step 3.
29441
29450
 
29442
- > The task is only done when \`aitasks done\` completes successfully. \`review\` status means awaiting approval \u2014 it is not done.` : `3. Mark done (will FAIL if any criterion is unchecked):
29451
+ > The task is only done when \`aitasks done\` completes successfully. \`review\` status means
29452
+ > awaiting approval \u2014 it is NOT done. Do not write a final summary until all tasks are \`done\`.` : `3. Mark done (will FAIL if any criterion is unchecked):
29443
29453
  \`\`\`bash
29444
29454
  aitasks done TASK-001 --agent $AITASKS_AGENT_ID
29445
29455
  \`\`\`
@@ -29483,10 +29493,29 @@ aitasks unclaim TASK-001 --agent $AITASKS_AGENT_ID --reason "Blocked on missing
29483
29493
  5. Add implementation notes continuously, not just at the end.
29484
29494
  6. If a task needs splitting, create subtasks BEFORE marking parent done.
29485
29495
  7. Your evidence strings must be concrete and verifiable \u2014 not vague affirmations.
29486
- 8. Always provide --desc and at least one --ac when creating a task. Both are required.${reviewRequired ? `
29487
- 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.` : ""}
29496
+ 8. Always provide --desc, at least one --ac, and --agent when creating a task. All three are required.${reviewRequired ? `
29497
+ 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 in the SAME session. Do NOT call \`aitasks done\` yourself after submitting for review \u2014 the system blocks self-approval.
29498
+ 10. **Do not end your session or write a final summary until every task you worked on is in \`done\` status.** If any task is in \`review\`, you must spawn the review sub-agent NOW in this session. Submitting for review and stopping is an incomplete session.` : ""}
29488
29499
 
29489
- ---
29500
+ ${reviewRequired ? `---
29501
+
29502
+ ### Before Ending Your Session
29503
+
29504
+ **Run this check before writing any final summary or concluding your work:**
29505
+
29506
+ \`\`\`bash
29507
+ aitasks list --json # Check that ALL your tasks are in done status
29508
+ \`\`\`
29509
+
29510
+ - Any task in \`done\` \u2192 \u2713 complete
29511
+ - Any task in \`review\` \u2192 you MUST spawn a review sub-agent NOW in this session before stopping
29512
+ - Any task in \`in_progress\` \u2192 continue implementation
29513
+
29514
+ **You are NOT finished until \`aitasks list\` shows every task you worked on as \`done\`.**
29515
+ Submitting tasks for review and then stopping is an incomplete session. The review sub-agent
29516
+ must be spawned and must approve the task in this same session.
29517
+
29518
+ ` : ""}---
29490
29519
 
29491
29520
  ### Quick Reference
29492
29521
 
@@ -29496,7 +29525,7 @@ aitasks list [--status <s>] [--json] List tasks
29496
29525
  aitasks show <id> Full task detail (includes time tracking)
29497
29526
  aitasks search <query> Search titles, descriptions, notes
29498
29527
  aitasks deps <id> Show dependency tree
29499
- aitasks create --title <t> --desc <d> --ac <c> [--ac <c> ...] Create a task
29528
+ aitasks create --title <t> --desc <d> --ac <c> [--ac <c> ...] --agent <id> Create a task
29500
29529
  aitasks claim <id...> --agent <id> Claim task(s) - supports patterns like TASK-0*
29501
29530
  aitasks start <id...> --agent <id> Begin work on task(s)
29502
29531
  aitasks note <id> <text> --agent <id> Add implementation note
@@ -30323,7 +30352,7 @@ function createTask(data) {
30323
30352
  now,
30324
30353
  JSON.stringify(data.metadata ?? {})
30325
30354
  ]);
30326
- logEvent({ task_id: id, event_type: "created", payload: { title: data.title } });
30355
+ logEvent({ task_id: id, agent_id: data.created_by, event_type: "created", payload: { title: data.title } });
30327
30356
  return getTask(id);
30328
30357
  }
30329
30358
  var JSON_FIELDS = new Set([
@@ -30409,9 +30438,9 @@ function claimTask(taskId, agentId) {
30409
30438
  error: `Task is blocked by: ${task.blocked_by.join(", ")}. Complete those first.`
30410
30439
  };
30411
30440
  }
30412
- db.run(`INSERT INTO agents (id, last_seen, current_task)
30413
- VALUES (?, ?, ?)
30414
- ON CONFLICT(id) DO UPDATE SET last_seen = excluded.last_seen, current_task = excluded.current_task`, [agentId, Date.now(), taskId]);
30441
+ db.run(`INSERT INTO agents (id, first_seen, last_seen, current_task)
30442
+ VALUES (?, ?, ?, ?)
30443
+ ON CONFLICT(id) DO UPDATE SET last_seen = excluded.last_seen, current_task = excluded.current_task`, [agentId, Date.now(), Date.now(), taskId]);
30415
30444
  const updated = updateTask(taskId, { assigned_to: agentId, status: "ready" });
30416
30445
  logEvent({ task_id: taskId, agent_id: agentId, event_type: "claimed", payload: {} });
30417
30446
  return { task: updated };
@@ -30427,9 +30456,9 @@ function startTask(taskId, agentId) {
30427
30456
  }
30428
30457
  const db = getDb();
30429
30458
  if (!task.assigned_to) {
30430
- db.run(`INSERT INTO agents (id, last_seen, current_task)
30431
- VALUES (?, ?, ?)
30432
- ON CONFLICT(id) DO UPDATE SET last_seen = excluded.last_seen, current_task = excluded.current_task`, [agentId, Date.now(), taskId]);
30459
+ db.run(`INSERT INTO agents (id, first_seen, last_seen, current_task)
30460
+ VALUES (?, ?, ?, ?)
30461
+ ON CONFLICT(id) DO UPDATE SET last_seen = excluded.last_seen, current_task = excluded.current_task`, [agentId, Date.now(), Date.now(), taskId]);
30433
30462
  }
30434
30463
  const updated = updateTask(taskId, {
30435
30464
  assigned_to: agentId,
@@ -30481,14 +30510,18 @@ function completeTask(taskId, agentId) {
30481
30510
  ` + ` A separate review sub-agent must run: aitasks done ${taskId} --agent <review-agent-id>`
30482
30511
  };
30483
30512
  }
30484
- const knownAgent = db.query(`SELECT id FROM agents WHERE id = ?`).get(agentId);
30485
- if (!knownAgent) {
30486
- return {
30487
- task: null,
30488
- error: `Unknown review agent: '${agentId}' has not registered with the system.
30489
- ` + ` A real review sub-agent must register first by running any aitasks command
30490
- ` + ` (e.g. aitasks heartbeat --agent ${agentId}) before it can approve a review.`
30491
- };
30513
+ if (agentId) {
30514
+ const reviewerRow = db.query(`SELECT first_seen FROM agents WHERE id = ?`).get(agentId);
30515
+ const reviewSubmittedAt = reviewEvent?.created_at ?? 0;
30516
+ if (!reviewerRow || reviewerRow.first_seen > reviewSubmittedAt) {
30517
+ return {
30518
+ task: null,
30519
+ error: `Review agent '${agentId}' was not active before this review was submitted.
30520
+ ` + ` A real review sub-agent must be independently spawned \u2014 it cannot be registered
30521
+ ` + ` moments before approving. The reviewer must have prior activity in the system
30522
+ ` + ` before the review was submitted.`
30523
+ };
30524
+ }
30492
30525
  }
30493
30526
  }
30494
30527
  const updated = updateTask(taskId, { status: "done", completed_at: Date.now() });
@@ -30509,7 +30542,8 @@ function completeTask(taskId, agentId) {
30509
30542
  });
30510
30543
  }
30511
30544
  }
30512
- db.run(`UPDATE agents SET current_task = NULL WHERE id = ?`, [agentId]);
30545
+ if (agentId)
30546
+ db.run(`UPDATE agents SET current_task = NULL WHERE id = ?`, [agentId]);
30513
30547
  return { task: updated };
30514
30548
  }
30515
30549
  function blockTask(taskId, blockerIds, agentId) {
@@ -30621,9 +30655,9 @@ function unclaimTask(taskId, agentId, reason) {
30621
30655
  }
30622
30656
  function heartbeat(agentId, taskId) {
30623
30657
  const db = getDb();
30624
- db.run(`INSERT INTO agents (id, last_seen, current_task)
30625
- VALUES (?, ?, ?)
30626
- ON CONFLICT(id) DO UPDATE SET last_seen = excluded.last_seen`, [agentId, Date.now(), taskId ?? null]);
30658
+ db.run(`INSERT INTO agents (id, first_seen, last_seen, current_task)
30659
+ VALUES (?, ?, ?, ?)
30660
+ ON CONFLICT(id) DO UPDATE SET last_seen = excluded.last_seen`, [agentId, Date.now(), Date.now(), taskId ?? null]);
30627
30661
  }
30628
30662
  function listAgents() {
30629
30663
  const db = getDb();
@@ -30721,7 +30755,7 @@ function exitError(msg, json) {
30721
30755
  }
30722
30756
 
30723
30757
  // src/commands/create.ts
30724
- 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) => {
30758
+ 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) => {
30725
30759
  requireInitialized();
30726
30760
  const json = isJsonMode(opts.json);
30727
30761
  if (opts.title) {
@@ -30746,13 +30780,15 @@ var createCommand2 = new Command("create").description("Create a new task").opti
30746
30780
  process.exit(1);
30747
30781
  }
30748
30782
  }
30783
+ const aid = agentId(opts.agent) ?? undefined;
30749
30784
  const task2 = createTask({
30750
30785
  title: opts.title,
30751
30786
  description: opts.desc ?? "",
30752
30787
  acceptance_criteria: opts.ac,
30753
30788
  priority: opts.priority,
30754
30789
  type: opts.type,
30755
- parent_id: opts.parent
30790
+ parent_id: opts.parent,
30791
+ created_by: aid
30756
30792
  });
30757
30793
  if (json)
30758
30794
  return jsonOut(true, task2);
@@ -41226,7 +41262,7 @@ var checkCommand = new Command("check").description("Verify an acceptance criter
41226
41262
  var doneCommand = new Command("done").description("Mark task(s) as complete (all acceptance criteria must be verified) - supports patterns").argument("<taskId...>", "Task ID(s) - can specify multiple or use patterns (e.g., TASK-00*)").option("--agent <agentId>", "Agent ID (or set AITASKS_AGENT_ID)").option("--json", "Output as JSON").action((taskIds, opts) => {
41227
41263
  requireInitialized();
41228
41264
  const json = isJsonMode(opts.json);
41229
- const agent = requireAgentId(opts.agent, "done");
41265
+ const agent = opts.agent ?? process.env.AITASKS_AGENT_ID;
41230
41266
  const allTaskIds = listTasks().map((t) => t.id);
41231
41267
  const resolvedIds = resolveTaskIds(taskIds.map((id) => id.toUpperCase()), allTaskIds);
41232
41268
  if (resolvedIds.length === 0) {
@@ -43318,4 +43354,4 @@ program2.parseAsync(process.argv).catch((err) => {
43318
43354
  process.exit(1);
43319
43355
  });
43320
43356
 
43321
- //# debugId=62CCA2B3357C2C9A64756E2164756E21
43357
+ //# debugId=1AD83EE12EE8B53364756E2164756E21
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aitasks",
3
- "version": "1.3.3",
3
+ "version": "1.3.5",
4
4
  "description": "CLI task management tool built for AI agents",
5
5
  "type": "module",
6
6
  "bin": {