aitasks 1.3.1 → 1.3.3

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 +8 -5
  2. package/dist/index.js +142 -46
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -174,14 +174,17 @@ aitasks init --with-review
174
174
  aitasks init --with-review # safe to re-run; updates DB setting and rewrites agent file
175
175
  ```
176
176
 
177
+ **A task is only complete when its status is `done`.** No other status — verified criteria, `review`, `in_progress` — counts as complete. Agents must reach `done` to consider a task finished.
178
+
177
179
  **How it works:**
178
180
 
179
- 1. Agent completes work and checks all acceptance criteria
181
+ 1. Agent completes work and verifies all acceptance criteria with evidence
180
182
  2. Agent submits for review — task moves to `review` status:
181
183
  ```sh
182
184
  aitasks review TASK-001 --agent $AITASKS_AGENT_ID
183
185
  ```
184
- 3. A separate review sub-agent inspects the implementation and either:
186
+ The command output explicitly instructs the agent to immediately spawn a review sub-agent. The task is **not complete** at this point.
187
+ 3. The agent **immediately spawns a review sub-agent** to inspect the implementation:
185
188
  - **Approves** — moves task to done:
186
189
  ```sh
187
190
  aitasks done TASK-001 --agent review-agent
@@ -190,11 +193,11 @@ aitasks init --with-review # safe to re-run; updates DB setting and rewrites ag
190
193
  ```sh
191
194
  aitasks reject TASK-001 --reason "Missing error handling for 404 case" --agent review-agent
192
195
  ```
193
- 4. If rejected, the agent addresses feedback, re-verifies criteria, and repeats from step 2.
196
+ 4. If rejected, the original agent addresses the feedback, re-verifies criteria, and repeats from step 2.
194
197
 
195
- **Enforcement:** When review is required, `aitasks done` and `aitasks update --status done` both block if the task is not already in `review` status. The gate cannot be bypassed.
198
+ **Enforcement:** `aitasks done` and `aitasks update --status done` both block if the task isn't already in `review` status. The gate cannot be bypassed.
196
199
 
197
- **Board:** Tasks in `review` status appear in the **IN PROGRESS** section with a `◈` magenta indicator so they're visually distinct from actively worked tasks.
200
+ **Board:** Tasks in `review` status appear in the **IN PROGRESS** section with a `◈` magenta indicator so they're visually distinct from actively-worked tasks.
198
201
 
199
202
  ---
200
203
 
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.1",
1893
+ version: "1.3.3",
1894
1894
  description: "CLI task management tool built for AI agents",
1895
1895
  type: "module",
1896
1896
  bin: {
@@ -29290,7 +29290,12 @@ function getAgentInstructions(version, opts = {}) {
29290
29290
  return `${INSTRUCTIONS_START_MARKER}
29291
29291
 
29292
29292
  ## AITasks \u2014 Agent Task Protocol (v${version})
29293
-
29293
+ ${reviewRequired ? `
29294
+ > \u26A0\uFE0F **REVIEW ENFORCEMENT IS ENABLED ON THIS PROJECT**
29295
+ > You cannot mark any task done directly. Every task requires a review step:
29296
+ > \`aitasks review\` \u2192 spawn review sub-agent \u2192 \`aitasks done\`
29297
+ > Tasks are only complete when their status is \`done\`. All other statuses mean work is still in progress.
29298
+ ` : ""}
29294
29299
  You have access to the \`aitasks\` CLI. This is your single source of truth for
29295
29300
  all work in this project. Follow this protocol without exception.
29296
29301
 
@@ -29347,7 +29352,7 @@ This finds the best task, claims it, and starts it in one command.
29347
29352
  aitasks start TASK-001 --agent $AITASKS_AGENT_ID
29348
29353
  \`\`\`
29349
29354
 
29350
- **Bulk operations:** You can claim, start, review, or complete multiple tasks at once:
29355
+ **Bulk operations:** You can claim, start, or complete multiple tasks at once:
29351
29356
  \`\`\`bash
29352
29357
  aitasks claim TASK-001 TASK-002 TASK-003 --agent $AITASKS_AGENT_ID
29353
29358
  aitasks start TASK-001 TASK-002 --agent $AITASKS_AGENT_ID
@@ -29395,6 +29400,8 @@ aitasks deps TASK-001 # Shows what this task is blocked by and what it blocks
29395
29400
 
29396
29401
  ### Completing a Task${reviewRequired ? " \u26A0\uFE0F REVIEW REQUIRED" : ""}
29397
29402
 
29403
+ > **A task is only complete when its status is \`done\`. Verified criteria, implementation notes, and \`review\` status do NOT mean the task is done. You have not finished a task until \`aitasks done\` has succeeded.**
29404
+
29398
29405
  You MUST verify every acceptance criterion before marking done.
29399
29406
 
29400
29407
  1. View all criteria:
@@ -29413,26 +29420,31 @@ ${reviewRequired ? `3. Submit for review \u2014 you CANNOT mark done directly:
29413
29420
  \`\`\`bash
29414
29421
  aitasks review TASK-001 --agent $AITASKS_AGENT_ID
29415
29422
  \`\`\`
29423
+ **STOP HERE. Do not run \`aitasks done\` yourself.** The system will block self-approval.
29416
29424
 
29417
- 4. Spawn a review sub-agent to inspect the implementation and either:
29418
- - **Approve** (moves task to done):
29419
- \`\`\`bash
29420
- aitasks done TASK-001 --agent <review-agent-id>
29421
- \`\`\`
29422
- - **Reject** (sends task back to in_progress with feedback):
29423
- \`\`\`bash
29424
- aitasks reject TASK-001 --reason "<specific feedback>" --agent <review-agent-id>
29425
- \`\`\`
29426
-
29427
- 5. If rejected: address the feedback, re-check criteria, and repeat from step 3.` : `3. Mark done (will FAIL if any criterion is unchecked):
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:
29428
29427
  \`\`\`bash
29429
- aitasks done TASK-001 --agent $AITASKS_AGENT_ID
29428
+ # Review sub-agent registers itself
29429
+ aitasks heartbeat --agent <review-agent-id>
29430
+
29431
+ # Approve (moves task to done):
29432
+ aitasks done TASK-001 --agent <review-agent-id>
29433
+
29434
+ # OR reject (sends task back to in_progress with feedback):
29435
+ aitasks reject TASK-001 --reason "<specific feedback>" --agent <review-agent-id>
29430
29436
  \`\`\`
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.**
29439
+
29440
+ 5. If rejected: address the feedback, re-check criteria, and repeat from step 3.
29431
29441
 
29432
- If review is needed:
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):
29433
29443
  \`\`\`bash
29434
- aitasks review TASK-001 --agent $AITASKS_AGENT_ID
29435
- \`\`\``}
29444
+ aitasks done TASK-001 --agent $AITASKS_AGENT_ID
29445
+ \`\`\`
29446
+
29447
+ > The task is only done when \`aitasks done\` completes successfully. Do not treat a task as finished until you see the done confirmation.`}
29436
29448
 
29437
29449
  ---
29438
29450
 
@@ -29446,8 +29458,8 @@ aitasks undo TASK-001 # Undoes the last action (claim, start, done, check, no
29446
29458
  Undoable actions:
29447
29459
  - claimed \u2192 unclaims the task
29448
29460
  - started \u2192 reverts to ready status
29449
- - completed \u2192 reverts to in_progress
29450
- - review_requested \u2192 reverts to in_progress
29461
+ - completed \u2192 reverts to in_progress${reviewRequired ? `
29462
+ - review_requested \u2192 reverts to in_progress` : ""}
29451
29463
  - criterion_checked \u2192 removes the verification
29452
29464
  - note_added \u2192 removes the implementation note
29453
29465
 
@@ -29464,14 +29476,15 @@ aitasks unclaim TASK-001 --agent $AITASKS_AGENT_ID --reason "Blocked on missing
29464
29476
 
29465
29477
  ### Rules
29466
29478
 
29467
- 1. Never mark a task done without checking EVERY acceptance criterion with evidence.
29468
- 2. Never start a task you haven't claimed.
29469
- 3. Never silently abandon a task \u2014 always unclaim with a reason.
29470
- 4. Add implementation notes continuously, not just at the end.
29471
- 5. If a task needs splitting, create subtasks BEFORE marking parent done.
29472
- 6. Your evidence strings must be concrete and verifiable \u2014 not vague affirmations.
29473
- 7. Always provide --desc and at least one --ac when creating a task. Both are required.${reviewRequired ? `
29474
- 8. NEVER move a task to done directly. Always submit for review first with \`aitasks review\`, then spawn a review sub-agent to approve or reject. Tasks cannot be marked done without a passing review.` : ""}
29479
+ 1. **A task is only complete when its status is \`done\`.** No other status \u2014 not criteria-verified, not \`review\`, not \`in_progress\` \u2014 counts as complete. Your work on a task is not finished until \`aitasks done\` succeeds.
29480
+ 2. Never mark a task done without checking EVERY acceptance criterion with evidence.
29481
+ 3. Never start a task you haven't claimed.
29482
+ 4. Never silently abandon a task \u2014 always unclaim with a reason.
29483
+ 5. Add implementation notes continuously, not just at the end.
29484
+ 6. If a task needs splitting, create subtasks BEFORE marking parent done.
29485
+ 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.` : ""}
29475
29488
 
29476
29489
  ---
29477
29490
 
@@ -29488,8 +29501,9 @@ aitasks claim <id...> --agent <id> Claim task(s) - supports patterns li
29488
29501
  aitasks start <id...> --agent <id> Begin work on task(s)
29489
29502
  aitasks note <id> <text> --agent <id> Add implementation note
29490
29503
  aitasks check <id> <n> --evidence <text> Verify acceptance criterion n
29491
- aitasks done <id...> --agent <id> Mark task(s) complete
29492
- aitasks review <id...> --agent <id> Request human review
29504
+ aitasks done <id...> --agent <id> Mark task(s) complete (only valid completion)${reviewRequired ? `
29505
+ aitasks review <id...> --agent <id> Submit for review \u2014 then spawn review sub-agent immediately
29506
+ aitasks reject <id> --reason <r> Reject review \u2014 sends back to in_progress` : ""}
29493
29507
  aitasks block <id> --on <id,...> Mark as blocked
29494
29508
  aitasks unblock <id> --from <id> Remove a blocker
29495
29509
  aitasks unclaim <id> --agent <id> Release task
@@ -29583,7 +29597,19 @@ function appendToSpecificFile(filePath, version, reviewRequired = false) {
29583
29597
  }
29584
29598
 
29585
29599
  // src/commands/init.ts
29586
- var initCommand = new Command("init").description("Initialize AITasks in the current project").option("--skip-agent-file", "Skip injecting agent instructions into CLAUDE.md/AGENTS.md/GEMINI.md").option("--with-review", "Enforce review gate: agents cannot mark tasks done without a passing review").action(async (opts) => {
29600
+ var initCommand = new Command("init").description("Initialize AITasks in the current project").option("--skip-agent-file", "Skip injecting agent instructions into CLAUDE.md/AGENTS.md/GEMINI.md").option("--with-review", "Enforce review gate: agents cannot mark tasks done without a passing review").addHelpText("after", `
29601
+ Examples:
29602
+ $ aitasks init
29603
+ Initialize in current directory, inject agent instructions into CLAUDE.md/AGENTS.md.
29604
+
29605
+ $ aitasks init --with-review
29606
+ Same as above, but enable the review enforcement gate. Agents must run
29607
+ \`aitasks review\` and spawn a review sub-agent before any task can be
29608
+ marked done. Running this on an existing project also updates the agent
29609
+ instructions file with the review workflow.
29610
+
29611
+ $ aitasks init --skip-agent-file
29612
+ Initialize without touching any CLAUDE.md / AGENTS.md file.`).action(async (opts) => {
29587
29613
  const root = findProjectRoot();
29588
29614
  const taskieDir = join5(root, ".aitasks");
29589
29615
  if (existsSync5(join5(taskieDir, "db.sqlite"))) {
@@ -30427,16 +30453,43 @@ function completeTask(taskId, agentId) {
30427
30453
  if (unchecked.length > 0) {
30428
30454
  return { task: null, error: "Not all acceptance criteria are verified", unchecked };
30429
30455
  }
30430
- if (getReviewRequired() && task.status !== "review") {
30431
- return {
30432
- task: null,
30433
- error: `Review required: submit this task for review first.
30456
+ if (getReviewRequired()) {
30457
+ if (task.status !== "review") {
30458
+ return {
30459
+ task: null,
30460
+ error: `Review required: submit this task for review first.
30434
30461
  ` + ` 1. aitasks review <taskId> --agent $AITASKS_AGENT_ID
30435
30462
  ` + ` 2. Spawn a review sub-agent to inspect the work
30436
30463
  ` + ` 3. Review agent approves: aitasks done <taskId> --agent <review-agent-id>
30437
30464
  ` + ` Review agent rejects: aitasks reject <taskId> --reason "<feedback>"
30438
30465
  ` + " Tasks cannot be moved to Done without a passing review."
30439
- };
30466
+ };
30467
+ }
30468
+ if (task.assigned_to && task.assigned_to === agentId) {
30469
+ return {
30470
+ task: null,
30471
+ error: `Self-approval blocked: ${agentId} is the implementing agent and cannot approve their own review.
30472
+ ` + ` A separate review sub-agent must run: aitasks done ${taskId} --agent <review-agent-id>`
30473
+ };
30474
+ }
30475
+ const events = getTaskEvents(taskId);
30476
+ const reviewEvent = [...events].reverse().find((e2) => e2.event_type === "review_requested");
30477
+ if (reviewEvent?.agent_id && reviewEvent.agent_id === agentId) {
30478
+ return {
30479
+ task: null,
30480
+ error: `Self-approval blocked: ${agentId} submitted this task for review and cannot also approve it.
30481
+ ` + ` A separate review sub-agent must run: aitasks done ${taskId} --agent <review-agent-id>`
30482
+ };
30483
+ }
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
+ };
30492
+ }
30440
30493
  }
30441
30494
  const updated = updateTask(taskId, { status: "done", completed_at: Date.now() });
30442
30495
  logEvent({ task_id: taskId, agent_id: agentId, event_type: "completed", payload: {} });
@@ -41082,6 +41135,7 @@ var startCommand = new Command("start").description("Start working on task(s) (t
41082
41135
  console.error("");
41083
41136
  process.exit(1);
41084
41137
  }
41138
+ const reviewRequired = getReviewRequired();
41085
41139
  const results = [];
41086
41140
  let allSuccess = true;
41087
41141
  for (const taskId of resolvedIds) {
@@ -41095,18 +41149,21 @@ var startCommand = new Command("start").description("Start working on task(s) (t
41095
41149
  }
41096
41150
  continue;
41097
41151
  }
41098
- results.push({ id: taskId, success: true });
41152
+ results.push({ id: taskId, success: true, review_required: reviewRequired });
41099
41153
  if (!json) {
41100
41154
  console.log("");
41101
41155
  console.log(source_default.green(" \u25B6") + ` Started ${source_default.bold(task.id)} \u2014 ${task.title}`);
41102
41156
  if (task.acceptance_criteria.length > 0) {
41103
41157
  console.log(source_default.dim(` ${task.acceptance_criteria.length} acceptance criteria to verify before done`));
41104
41158
  }
41159
+ if (reviewRequired) {
41160
+ console.log(source_default.yellow(" \u26A0 ") + source_default.bold("Review required.") + source_default.dim(` When done: aitasks review ${task.id} \u2192 spawn review sub-agent \u2192 aitasks done ${task.id}`));
41161
+ }
41105
41162
  console.log(source_default.dim(` Add notes as you go: aitasks note ${task.id} "<your note>"`));
41106
41163
  }
41107
41164
  }
41108
41165
  if (json) {
41109
- return jsonOut(allSuccess, { results });
41166
+ return jsonOut(allSuccess, { results, review_required: reviewRequired });
41110
41167
  }
41111
41168
  console.log("");
41112
41169
  if (!allSuccess) {
@@ -41278,6 +41335,7 @@ var reviewCommand = new Command("review").description("Request human review for
41278
41335
  console.error("");
41279
41336
  process.exit(1);
41280
41337
  }
41338
+ const reviewRequired = getReviewRequired();
41281
41339
  const results = [];
41282
41340
  let allSuccess = true;
41283
41341
  for (const taskId of resolvedIds) {
@@ -41291,13 +41349,43 @@ var reviewCommand = new Command("review").description("Request human review for
41291
41349
  }
41292
41350
  continue;
41293
41351
  }
41294
- results.push({ id: taskId, success: true });
41352
+ if (reviewRequired) {
41353
+ results.push({
41354
+ id: taskId,
41355
+ success: true,
41356
+ status: "review",
41357
+ 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.`,
41358
+ review_commands: {
41359
+ approve: `aitasks done ${taskId} --agent review-${taskId.toLowerCase()}`,
41360
+ reject: `aitasks reject ${taskId} --reason "<specific feedback>" --agent review-${taskId.toLowerCase()}`
41361
+ }
41362
+ });
41363
+ } else {
41364
+ results.push({ id: taskId, success: true, status: "review" });
41365
+ }
41295
41366
  if (!json) {
41296
41367
  console.log("");
41297
41368
  console.log(source_default.magenta(" \u25C8") + ` ${source_default.bold(task.id)} submitted for review`);
41298
- console.log(source_default.dim(` Spawn a review sub-agent to inspect the implementation.`));
41299
- console.log(source_default.dim(` Approve: aitasks done ${task.id} --agent <review-agent-id>`));
41300
- console.log(source_default.dim(` Reject: aitasks reject ${task.id} --reason "<feedback>"`));
41369
+ if (reviewRequired) {
41370
+ console.log(source_default.yellow(" \u26A0 ") + source_default.bold(" This task is NOT complete yet."));
41371
+ console.log("");
41372
+ console.log(` You MUST ${source_default.bold("immediately spawn a review sub-agent")} to inspect the implementation.`);
41373
+ console.log(source_default.dim(" The task remains incomplete until the review agent moves it to done."));
41374
+ console.log("");
41375
+ console.log(source_default.yellow(" \u2716 ") + source_default.bold("STOP \u2014 do NOT run `aitasks done` yourself."));
41376
+ console.log(source_default.dim(" The system will block self-approval. A separate agent must do the review."));
41377
+ console.log("");
41378
+ console.log(source_default.dim(" Review sub-agent steps:"));
41379
+ console.log(source_default.dim(` 1. Spawn a sub-agent with a unique agent ID, e.g. review-${task.id.toLowerCase()}`));
41380
+ console.log(source_default.dim(` 2. Sub-agent registers: aitasks heartbeat --agent review-${task.id.toLowerCase()}`));
41381
+ console.log(source_default.dim(` 3. Sub-agent examines implementation and verifies all acceptance criteria`));
41382
+ console.log(source_default.dim(` 4. Approve: aitasks done ${task.id} --agent review-${task.id.toLowerCase()}`));
41383
+ console.log(source_default.dim(` Reject: aitasks reject ${task.id} --reason "<feedback>" --agent review-${task.id.toLowerCase()}`));
41384
+ console.log(source_default.dim(` The system will reject approval from unregistered or self-approving agents.`));
41385
+ } else {
41386
+ console.log(source_default.dim(` Approve: aitasks done ${task.id} --agent <review-agent-id>`));
41387
+ console.log(source_default.dim(` Reject: aitasks reject ${task.id} --reason "<feedback>"`));
41388
+ }
41301
41389
  }
41302
41390
  }
41303
41391
  if (json) {
@@ -41378,9 +41466,10 @@ var nextCommand = new Command("next").description("Show the next best task to wo
41378
41466
  }
41379
41467
  const aid = opts.claim ? requireAgentId(opts.agent, "next --claim") : agentId(opts.agent) ?? undefined;
41380
41468
  const task = getNextTask(aid);
41469
+ const reviewRequired = getReviewRequired();
41381
41470
  if (!task) {
41382
41471
  if (json)
41383
- return jsonOut(true, null);
41472
+ return jsonOut(true, { task: null, review_required: reviewRequired });
41384
41473
  console.log("");
41385
41474
  console.log(source_default.dim(" No ready tasks available. Check backlog with: aitasks list"));
41386
41475
  console.log("");
@@ -41403,17 +41492,24 @@ var nextCommand = new Command("next").description("Show the next best task to wo
41403
41492
  }
41404
41493
  const updatedTask = startResult.task;
41405
41494
  if (json)
41406
- return jsonOut(true, updatedTask);
41495
+ return jsonOut(true, { ...updatedTask, review_required: reviewRequired });
41407
41496
  console.log("");
41408
41497
  console.log(source_default.green(" \u2713") + ` Claimed and started ${source_default.bold(updatedTask.id)}: ${updatedTask.title}`);
41409
41498
  console.log(source_default.dim(` Priority: ${updatedTask.priority} \xB7 Type: ${updatedTask.type}`));
41410
41499
  console.log(source_default.dim(` Agent: ${aid}`));
41500
+ if (reviewRequired) {
41501
+ console.log("");
41502
+ console.log(source_default.yellow(" \u26A0 ") + source_default.bold(" Review enforcement is ON.") + source_default.dim(" When done: aitasks review \u2192 spawn review sub-agent \u2192 aitasks done"));
41503
+ }
41411
41504
  console.log("");
41412
41505
  return;
41413
41506
  }
41414
41507
  if (json)
41415
- return jsonOut(true, task);
41508
+ return jsonOut(true, { ...task, review_required: reviewRequired });
41416
41509
  console.log(renderTaskDetail(task));
41510
+ if (reviewRequired) {
41511
+ console.log(source_default.yellow(" \u26A0 ") + source_default.bold(" Review enforcement is ON.") + source_default.dim(" When done: aitasks review \u2192 spawn review sub-agent \u2192 aitasks done"));
41512
+ }
41417
41513
  console.log(source_default.dim(` Claim it: aitasks claim ${task.id} --agent <your-id>`));
41418
41514
  console.log("");
41419
41515
  });
@@ -43222,4 +43318,4 @@ program2.parseAsync(process.argv).catch((err) => {
43222
43318
  process.exit(1);
43223
43319
  });
43224
43320
 
43225
- //# debugId=B0EAB2DE4FE47CF564756E2164756E21
43321
+ //# debugId=62CCA2B3357C2C9A64756E2164756E21
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aitasks",
3
- "version": "1.3.1",
3
+ "version": "1.3.3",
4
4
  "description": "CLI task management tool built for AI agents",
5
5
  "type": "module",
6
6
  "bin": {