aitasks 1.3.0 → 1.3.2
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 +47 -5
- package/dist/index.js +212 -67
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -72,6 +72,7 @@ export AITASKS_JSON=true
|
|
|
72
72
|
| Command | Description |
|
|
73
73
|
|---|---|
|
|
74
74
|
| `aitasks init` | Initialize a task database in the current project |
|
|
75
|
+
| `aitasks init --with-review` | Initialize with review enforcement (agents cannot mark done without a passing review) |
|
|
75
76
|
| `aitasks onboard` | Print or inject agent protocol instructions into CLAUDE.md / AGENTS.md |
|
|
76
77
|
|
|
77
78
|
### Task Discovery
|
|
@@ -79,7 +80,7 @@ export AITASKS_JSON=true
|
|
|
79
80
|
| Command | Description |
|
|
80
81
|
|---|---|
|
|
81
82
|
| `aitasks list` | List all tasks, sorted by priority |
|
|
82
|
-
| `aitasks list --status ready` | Filter by status (`ready`, `in_progress`, `
|
|
83
|
+
| `aitasks list --status ready` | Filter by status (`ready`, `in_progress`, `blocked`, `review`, `done`) |
|
|
83
84
|
| `aitasks next` | Show the highest-priority unblocked ready task |
|
|
84
85
|
| `aitasks next --claim --agent <id>` | Auto-claim and start the best task |
|
|
85
86
|
| `aitasks show <id>` | Full detail on a specific task (includes time tracking) |
|
|
@@ -96,9 +97,9 @@ export AITASKS_JSON=true
|
|
|
96
97
|
| `aitasks start <id...> --agent <id>` | Begin active work on task(s) |
|
|
97
98
|
| `aitasks note <id> <text>` | Add an implementation note |
|
|
98
99
|
| `aitasks check <id> <n> --evidence <text>` | Verify acceptance criterion n |
|
|
99
|
-
| `aitasks done <id...> --agent <id>` | Mark task(s) complete (all criteria must be verified) |
|
|
100
|
-
| `aitasks review <id...> --agent <id>` | Submit for
|
|
101
|
-
| `aitasks reject <id> --reason <text>` | Reject
|
|
100
|
+
| `aitasks done <id...> --agent <id>` | Mark task(s) complete (all criteria must be verified; must be in `review` status if enforcement is on) |
|
|
101
|
+
| `aitasks review <id...> --agent <id>` | Submit task(s) for review (moves to `review` status) |
|
|
102
|
+
| `aitasks reject <id> --reason <text>` | Reject a task in review, send it back to `in_progress` with feedback |
|
|
102
103
|
| `aitasks unclaim <id> --agent <id>` | Release a task back to the pool |
|
|
103
104
|
| `aitasks undo <id>` | Undo the last action on a task |
|
|
104
105
|
| `aitasks delete <id...>` | Delete task(s) - does not require claiming first |
|
|
@@ -151,12 +152,53 @@ When you run `aitasks init`, it automatically injects a full agent protocol into
|
|
|
151
152
|
You can also inject/view it manually:
|
|
152
153
|
|
|
153
154
|
```sh
|
|
154
|
-
aitasks onboard # print to stdout
|
|
155
|
+
aitasks onboard # print to stdout (reflects current review_required setting)
|
|
155
156
|
aitasks onboard --append # append to detected agent file
|
|
156
157
|
aitasks onboard --file MY.md # append to a specific file
|
|
157
158
|
aitasks onboard --json # output as JSON string
|
|
158
159
|
```
|
|
159
160
|
|
|
161
|
+
The injected instructions automatically adapt to the project's review enforcement setting — if `--with-review` is enabled, agents receive the full review workflow (with `aitasks review`, sub-agent approval, and `aitasks reject`) instead of the standard completion flow.
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## Review Enforcement
|
|
166
|
+
|
|
167
|
+
Enable a mandatory review gate so agents cannot mark tasks done without an explicit approval step:
|
|
168
|
+
|
|
169
|
+
```sh
|
|
170
|
+
# Enable at init time
|
|
171
|
+
aitasks init --with-review
|
|
172
|
+
|
|
173
|
+
# Enable on an existing project (also replaces the agent instructions file with review-aware version)
|
|
174
|
+
aitasks init --with-review # safe to re-run; updates DB setting and rewrites agent file
|
|
175
|
+
```
|
|
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
|
+
|
|
179
|
+
**How it works:**
|
|
180
|
+
|
|
181
|
+
1. Agent completes work and verifies all acceptance criteria with evidence
|
|
182
|
+
2. Agent submits for review — task moves to `review` status:
|
|
183
|
+
```sh
|
|
184
|
+
aitasks review TASK-001 --agent $AITASKS_AGENT_ID
|
|
185
|
+
```
|
|
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:
|
|
188
|
+
- **Approves** — moves task to done:
|
|
189
|
+
```sh
|
|
190
|
+
aitasks done TASK-001 --agent review-agent
|
|
191
|
+
```
|
|
192
|
+
- **Rejects** — sends it back to `in_progress` with feedback:
|
|
193
|
+
```sh
|
|
194
|
+
aitasks reject TASK-001 --reason "Missing error handling for 404 case" --agent review-agent
|
|
195
|
+
```
|
|
196
|
+
4. If rejected, the original agent addresses the feedback, re-verifies criteria, and repeats from step 2.
|
|
197
|
+
|
|
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.
|
|
199
|
+
|
|
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.
|
|
201
|
+
|
|
160
202
|
---
|
|
161
203
|
|
|
162
204
|
## Data Storage
|
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.2",
|
|
1894
1894
|
description: "CLI task management tool built for AI agents",
|
|
1895
1895
|
type: "module",
|
|
1896
1896
|
bin: {
|
|
@@ -29071,7 +29071,16 @@ 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 = 2;
|
|
29075
|
+
function runMigrations(db) {
|
|
29076
|
+
const row = db.query(`SELECT value FROM meta WHERE key = 'schema_version'`).get();
|
|
29077
|
+
const current = row ? parseInt(row.value, 10) : 1;
|
|
29078
|
+
if (current < 2) {
|
|
29079
|
+
db.exec(`UPDATE tasks SET status = 'review' WHERE status = 'needs_review'`);
|
|
29080
|
+
db.exec(`INSERT OR IGNORE INTO meta (key, value) VALUES ('review_required', 'false')`);
|
|
29081
|
+
db.exec(`UPDATE meta SET value = '2' WHERE key = 'schema_version'`);
|
|
29082
|
+
}
|
|
29083
|
+
}
|
|
29075
29084
|
function initializeSchema(db) {
|
|
29076
29085
|
db.exec(`
|
|
29077
29086
|
CREATE TABLE IF NOT EXISTS meta (
|
|
@@ -29126,6 +29135,7 @@ function initializeSchema(db) {
|
|
|
29126
29135
|
insert.run("last_task_number", "0");
|
|
29127
29136
|
insert.run("schema_version", String(SCHEMA_VERSION));
|
|
29128
29137
|
insert.run("initialized_at", String(Date.now()));
|
|
29138
|
+
insert.run("review_required", "false");
|
|
29129
29139
|
}
|
|
29130
29140
|
|
|
29131
29141
|
// src/db/backup.ts
|
|
@@ -29240,10 +29250,20 @@ function getDb() {
|
|
|
29240
29250
|
const db = new Database(dbPath);
|
|
29241
29251
|
applyPragmas(db);
|
|
29242
29252
|
checkIntegrity(db, taskieDir);
|
|
29253
|
+
runMigrations(db);
|
|
29243
29254
|
registerCleanup();
|
|
29244
29255
|
_db = db;
|
|
29245
29256
|
return db;
|
|
29246
29257
|
}
|
|
29258
|
+
function getReviewRequired() {
|
|
29259
|
+
const db = getDb();
|
|
29260
|
+
const row = db.query(`SELECT value FROM meta WHERE key = 'review_required'`).get();
|
|
29261
|
+
return row?.value === "true";
|
|
29262
|
+
}
|
|
29263
|
+
function setReviewRequired(enabled) {
|
|
29264
|
+
const db = getDb();
|
|
29265
|
+
db.run(`INSERT OR REPLACE INTO meta (key, value) VALUES ('review_required', ?)`, [enabled ? "true" : "false"]);
|
|
29266
|
+
}
|
|
29247
29267
|
function createFreshDb(taskieDir) {
|
|
29248
29268
|
const dbPath = join3(taskieDir, "db.sqlite");
|
|
29249
29269
|
const db = new Database(dbPath);
|
|
@@ -29265,11 +29285,17 @@ import { join as join4 } from "path";
|
|
|
29265
29285
|
// src/utils/instructions.ts
|
|
29266
29286
|
var INSTRUCTIONS_START_MARKER = "<!-- aitasks:instructions -->";
|
|
29267
29287
|
var INSTRUCTIONS_END_MARKER = "<!-- aitasks:instructions:end -->";
|
|
29268
|
-
function getAgentInstructions(version) {
|
|
29288
|
+
function getAgentInstructions(version, opts = {}) {
|
|
29289
|
+
const reviewRequired = opts.reviewRequired ?? false;
|
|
29269
29290
|
return `${INSTRUCTIONS_START_MARKER}
|
|
29270
29291
|
|
|
29271
29292
|
## AITasks \u2014 Agent Task Protocol (v${version})
|
|
29272
|
-
|
|
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
|
+
` : ""}
|
|
29273
29299
|
You have access to the \`aitasks\` CLI. This is your single source of truth for
|
|
29274
29300
|
all work in this project. Follow this protocol without exception.
|
|
29275
29301
|
|
|
@@ -29326,7 +29352,7 @@ This finds the best task, claims it, and starts it in one command.
|
|
|
29326
29352
|
aitasks start TASK-001 --agent $AITASKS_AGENT_ID
|
|
29327
29353
|
\`\`\`
|
|
29328
29354
|
|
|
29329
|
-
**Bulk operations:** You can claim, start,
|
|
29355
|
+
**Bulk operations:** You can claim, start, or complete multiple tasks at once:
|
|
29330
29356
|
\`\`\`bash
|
|
29331
29357
|
aitasks claim TASK-001 TASK-002 TASK-003 --agent $AITASKS_AGENT_ID
|
|
29332
29358
|
aitasks start TASK-001 TASK-002 --agent $AITASKS_AGENT_ID
|
|
@@ -29372,7 +29398,9 @@ aitasks deps TASK-001 # Shows what this task is blocked by and what it blocks
|
|
|
29372
29398
|
|
|
29373
29399
|
---
|
|
29374
29400
|
|
|
29375
|
-
### Completing a Task
|
|
29401
|
+
### Completing a Task${reviewRequired ? " \u26A0\uFE0F REVIEW REQUIRED" : ""}
|
|
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.**
|
|
29376
29404
|
|
|
29377
29405
|
You MUST verify every acceptance criterion before marking done.
|
|
29378
29406
|
|
|
@@ -29388,16 +29416,31 @@ You MUST verify every acceptance criterion before marking done.
|
|
|
29388
29416
|
aitasks check TASK-001 2 --evidence "integration test suite passes: 12/12 green"
|
|
29389
29417
|
\`\`\`
|
|
29390
29418
|
|
|
29391
|
-
3.
|
|
29419
|
+
${reviewRequired ? `3. Submit for review \u2014 you CANNOT mark done directly:
|
|
29392
29420
|
\`\`\`bash
|
|
29393
|
-
aitasks
|
|
29421
|
+
aitasks review TASK-001 --agent $AITASKS_AGENT_ID
|
|
29394
29422
|
\`\`\`
|
|
29395
29423
|
|
|
29396
|
-
|
|
29424
|
+
4. **IMMEDIATELY spawn a review sub-agent** \u2014 do NOT move to other tasks until review resolves.
|
|
29425
|
+
The task is still incomplete. The review sub-agent must either:
|
|
29426
|
+
- **Approve** (moves task to done):
|
|
29427
|
+
\`\`\`bash
|
|
29428
|
+
aitasks done TASK-001 --agent <review-agent-id>
|
|
29429
|
+
\`\`\`
|
|
29430
|
+
- **Reject** (sends task back to in_progress with feedback):
|
|
29431
|
+
\`\`\`bash
|
|
29432
|
+
aitasks reject TASK-001 --reason "<specific feedback>" --agent <review-agent-id>
|
|
29433
|
+
\`\`\`
|
|
29434
|
+
|
|
29435
|
+
5. If rejected: address the feedback, re-check criteria, and repeat from step 3.
|
|
29436
|
+
|
|
29437
|
+
> 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):
|
|
29397
29438
|
\`\`\`bash
|
|
29398
|
-
aitasks
|
|
29439
|
+
aitasks done TASK-001 --agent $AITASKS_AGENT_ID
|
|
29399
29440
|
\`\`\`
|
|
29400
29441
|
|
|
29442
|
+
> The task is only done when \`aitasks done\` completes successfully. Do not treat a task as finished until you see the done confirmation.`}
|
|
29443
|
+
|
|
29401
29444
|
---
|
|
29402
29445
|
|
|
29403
29446
|
### Undoing Mistakes
|
|
@@ -29410,8 +29453,8 @@ aitasks undo TASK-001 # Undoes the last action (claim, start, done, check, no
|
|
|
29410
29453
|
Undoable actions:
|
|
29411
29454
|
- claimed \u2192 unclaims the task
|
|
29412
29455
|
- started \u2192 reverts to ready status
|
|
29413
|
-
- completed \u2192 reverts to in_progress
|
|
29414
|
-
- review_requested \u2192 reverts to in_progress
|
|
29456
|
+
- completed \u2192 reverts to in_progress${reviewRequired ? `
|
|
29457
|
+
- review_requested \u2192 reverts to in_progress` : ""}
|
|
29415
29458
|
- criterion_checked \u2192 removes the verification
|
|
29416
29459
|
- note_added \u2192 removes the implementation note
|
|
29417
29460
|
|
|
@@ -29428,13 +29471,15 @@ aitasks unclaim TASK-001 --agent $AITASKS_AGENT_ID --reason "Blocked on missing
|
|
|
29428
29471
|
|
|
29429
29472
|
### Rules
|
|
29430
29473
|
|
|
29431
|
-
1.
|
|
29432
|
-
2. Never
|
|
29433
|
-
3. Never
|
|
29434
|
-
4.
|
|
29435
|
-
5.
|
|
29436
|
-
6.
|
|
29437
|
-
7.
|
|
29474
|
+
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.
|
|
29475
|
+
2. Never mark a task done without checking EVERY acceptance criterion with evidence.
|
|
29476
|
+
3. Never start a task you haven't claimed.
|
|
29477
|
+
4. Never silently abandon a task \u2014 always unclaim with a reason.
|
|
29478
|
+
5. Add implementation notes continuously, not just at the end.
|
|
29479
|
+
6. If a task needs splitting, create subtasks BEFORE marking parent done.
|
|
29480
|
+
7. Your evidence strings must be concrete and verifiable \u2014 not vague affirmations.
|
|
29481
|
+
8. Always provide --desc and at least one --ac when creating a task. Both are required.${reviewRequired ? `
|
|
29482
|
+
9. NEVER move a task to done directly. Always submit for review first with \`aitasks review\`, then IMMEDIATELY spawn a review sub-agent. Do NOT work on other tasks until the review resolves. A task is only complete when its status is \`done\`.` : ""}
|
|
29438
29483
|
|
|
29439
29484
|
---
|
|
29440
29485
|
|
|
@@ -29451,8 +29496,9 @@ aitasks claim <id...> --agent <id> Claim task(s) - supports patterns li
|
|
|
29451
29496
|
aitasks start <id...> --agent <id> Begin work on task(s)
|
|
29452
29497
|
aitasks note <id> <text> --agent <id> Add implementation note
|
|
29453
29498
|
aitasks check <id> <n> --evidence <text> Verify acceptance criterion n
|
|
29454
|
-
aitasks done <id...> --agent <id> Mark task(s) complete
|
|
29455
|
-
aitasks review <id...> --agent <id>
|
|
29499
|
+
aitasks done <id...> --agent <id> Mark task(s) complete (only valid completion)${reviewRequired ? `
|
|
29500
|
+
aitasks review <id...> --agent <id> Submit for review \u2014 then spawn review sub-agent immediately
|
|
29501
|
+
aitasks reject <id> --reason <r> Reject review \u2014 sends back to in_progress` : ""}
|
|
29456
29502
|
aitasks block <id> --on <id,...> Mark as blocked
|
|
29457
29503
|
aitasks unblock <id> --from <id> Remove a blocker
|
|
29458
29504
|
aitasks unclaim <id> --agent <id> Release task
|
|
@@ -29481,13 +29527,17 @@ function findExistingAgentFile(projectRoot) {
|
|
|
29481
29527
|
}
|
|
29482
29528
|
return null;
|
|
29483
29529
|
}
|
|
29484
|
-
function injectOrCreateAgentFile(projectRoot, version) {
|
|
29485
|
-
const instructions = getAgentInstructions(version);
|
|
29530
|
+
function injectOrCreateAgentFile(projectRoot, version, reviewRequired = false, force = false) {
|
|
29531
|
+
const instructions = getAgentInstructions(version, { reviewRequired });
|
|
29486
29532
|
const existing = findExistingAgentFile(projectRoot);
|
|
29487
29533
|
if (existing) {
|
|
29488
29534
|
const content = readFileSync2(existing, "utf8");
|
|
29489
29535
|
if (instructionsAlreadyPresent(content)) {
|
|
29490
|
-
|
|
29536
|
+
if (!force)
|
|
29537
|
+
return { filePath: existing, action: "skipped" };
|
|
29538
|
+
const replaced = replaceInstructionsBlock(content, instructions);
|
|
29539
|
+
writeFileSync2(existing, replaced, "utf8");
|
|
29540
|
+
return { filePath: existing, action: "appended" };
|
|
29491
29541
|
}
|
|
29492
29542
|
const separator = content.endsWith(`
|
|
29493
29543
|
`) ? `
|
|
@@ -29503,8 +29553,25 @@ function injectOrCreateAgentFile(projectRoot, version) {
|
|
|
29503
29553
|
`, "utf8");
|
|
29504
29554
|
return { filePath: newPath, action: "created" };
|
|
29505
29555
|
}
|
|
29506
|
-
function
|
|
29507
|
-
const
|
|
29556
|
+
function replaceInstructionsBlock(content, newInstructions) {
|
|
29557
|
+
const start = content.indexOf(INSTRUCTIONS_START_MARKER);
|
|
29558
|
+
const end = content.indexOf(INSTRUCTIONS_END_MARKER);
|
|
29559
|
+
if (start === -1 || end === -1)
|
|
29560
|
+
return content;
|
|
29561
|
+
const before = content.slice(0, start).replace(/\n+$/, "");
|
|
29562
|
+
const after = content.slice(end + INSTRUCTIONS_END_MARKER.length).replace(/^\n+/, "");
|
|
29563
|
+
const sep = before.length > 0 ? `
|
|
29564
|
+
|
|
29565
|
+
` : "";
|
|
29566
|
+
const tail = after.length > 0 ? `
|
|
29567
|
+
|
|
29568
|
+
` + after : `
|
|
29569
|
+
`;
|
|
29570
|
+
return before + sep + newInstructions + `
|
|
29571
|
+
` + tail;
|
|
29572
|
+
}
|
|
29573
|
+
function appendToSpecificFile(filePath, version, reviewRequired = false) {
|
|
29574
|
+
const instructions = getAgentInstructions(version, { reviewRequired });
|
|
29508
29575
|
if (!existsSync4(filePath)) {
|
|
29509
29576
|
writeFileSync2(filePath, instructions + `
|
|
29510
29577
|
`, "utf8");
|
|
@@ -29525,27 +29592,51 @@ function appendToSpecificFile(filePath, version) {
|
|
|
29525
29592
|
}
|
|
29526
29593
|
|
|
29527
29594
|
// src/commands/init.ts
|
|
29528
|
-
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").
|
|
29595
|
+
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", `
|
|
29596
|
+
Examples:
|
|
29597
|
+
$ aitasks init
|
|
29598
|
+
Initialize in current directory, inject agent instructions into CLAUDE.md/AGENTS.md.
|
|
29599
|
+
|
|
29600
|
+
$ aitasks init --with-review
|
|
29601
|
+
Same as above, but enable the review enforcement gate. Agents must run
|
|
29602
|
+
\`aitasks review\` and spawn a review sub-agent before any task can be
|
|
29603
|
+
marked done. Running this on an existing project also updates the agent
|
|
29604
|
+
instructions file with the review workflow.
|
|
29605
|
+
|
|
29606
|
+
$ aitasks init --skip-agent-file
|
|
29607
|
+
Initialize without touching any CLAUDE.md / AGENTS.md file.`).action(async (opts) => {
|
|
29529
29608
|
const root = findProjectRoot();
|
|
29530
29609
|
const taskieDir = join5(root, ".aitasks");
|
|
29531
29610
|
if (existsSync5(join5(taskieDir, "db.sqlite"))) {
|
|
29532
29611
|
console.log(source_default.yellow(" AITasks is already initialized in this project."));
|
|
29533
29612
|
console.log(source_default.dim(` DB: ${join5(taskieDir, "db.sqlite")}`));
|
|
29613
|
+
if (opts.withReview) {
|
|
29614
|
+
setReviewRequired(true);
|
|
29615
|
+
console.log(source_default.green(" \u2713") + " Review enforcement enabled.");
|
|
29616
|
+
}
|
|
29534
29617
|
if (!opts.skipAgentFile) {
|
|
29535
|
-
const result = injectOrCreateAgentFile(root, getVersion());
|
|
29618
|
+
const result = injectOrCreateAgentFile(root, getVersion(), !!opts.withReview, !!opts.withReview);
|
|
29536
29619
|
printAgentFileResult(result);
|
|
29537
29620
|
}
|
|
29538
29621
|
return;
|
|
29539
29622
|
}
|
|
29540
29623
|
mkdirSync(taskieDir, { recursive: true });
|
|
29541
29624
|
createFreshDb(taskieDir);
|
|
29625
|
+
if (opts.withReview) {
|
|
29626
|
+
setReviewRequired(true);
|
|
29627
|
+
}
|
|
29542
29628
|
console.log("");
|
|
29543
29629
|
console.log(source_default.green(" \u2713") + source_default.bold(" AITasks initialized"));
|
|
29544
29630
|
console.log(source_default.dim(` Project root : ${root}`));
|
|
29545
29631
|
console.log(source_default.dim(` Database : ${join5(taskieDir, "db.sqlite")}`));
|
|
29632
|
+
if (opts.withReview) {
|
|
29633
|
+
console.log(source_default.magenta(" \u25C8") + source_default.bold(" Review enforcement enabled"));
|
|
29634
|
+
console.log(source_default.dim(" Agents must submit tasks for review before marking done."));
|
|
29635
|
+
console.log(source_default.dim(" Use: aitasks review <id> \u2192 review sub-agent \u2192 aitasks done <id>"));
|
|
29636
|
+
}
|
|
29546
29637
|
console.log("");
|
|
29547
29638
|
if (!opts.skipAgentFile) {
|
|
29548
|
-
const result = injectOrCreateAgentFile(root, getVersion());
|
|
29639
|
+
const result = injectOrCreateAgentFile(root, getVersion(), !!opts.withReview);
|
|
29549
29640
|
printAgentFileResult(result);
|
|
29550
29641
|
}
|
|
29551
29642
|
console.log(source_default.dim(" Run `aitasks create` to add your first task."));
|
|
@@ -30156,7 +30247,7 @@ var PRIORITY_ORDER = `CASE priority
|
|
|
30156
30247
|
WHEN 'low' THEN 3 END`;
|
|
30157
30248
|
var STATUS_ORDER = `CASE status
|
|
30158
30249
|
WHEN 'in_progress' THEN 0
|
|
30159
|
-
WHEN '
|
|
30250
|
+
WHEN 'review' THEN 1
|
|
30160
30251
|
WHEN 'blocked' THEN 2
|
|
30161
30252
|
WHEN 'ready' THEN 3
|
|
30162
30253
|
WHEN 'backlog' THEN 4
|
|
@@ -30357,6 +30448,17 @@ function completeTask(taskId, agentId) {
|
|
|
30357
30448
|
if (unchecked.length > 0) {
|
|
30358
30449
|
return { task: null, error: "Not all acceptance criteria are verified", unchecked };
|
|
30359
30450
|
}
|
|
30451
|
+
if (getReviewRequired() && task.status !== "review") {
|
|
30452
|
+
return {
|
|
30453
|
+
task: null,
|
|
30454
|
+
error: `Review required: submit this task for review first.
|
|
30455
|
+
` + ` 1. aitasks review <taskId> --agent $AITASKS_AGENT_ID
|
|
30456
|
+
` + ` 2. Spawn a review sub-agent to inspect the work
|
|
30457
|
+
` + ` 3. Review agent approves: aitasks done <taskId> --agent <review-agent-id>
|
|
30458
|
+
` + ` Review agent rejects: aitasks reject <taskId> --reason "<feedback>"
|
|
30459
|
+
` + " Tasks cannot be moved to Done without a passing review."
|
|
30460
|
+
};
|
|
30461
|
+
}
|
|
30360
30462
|
const updated = updateTask(taskId, { status: "done", completed_at: Date.now() });
|
|
30361
30463
|
logEvent({ task_id: taskId, agent_id: agentId, event_type: "completed", payload: {} });
|
|
30362
30464
|
const pendingRows = db.query(`SELECT id, blocked_by FROM tasks WHERE status != 'done'`).all();
|
|
@@ -30442,7 +30544,7 @@ function reviewTask(taskId, agentId) {
|
|
|
30442
30544
|
if (task.status !== "in_progress") {
|
|
30443
30545
|
return { task: null, error: "Task must be in_progress to request review" };
|
|
30444
30546
|
}
|
|
30445
|
-
const updated = updateTask(taskId, { status: "
|
|
30547
|
+
const updated = updateTask(taskId, { status: "review" });
|
|
30446
30548
|
logEvent({ task_id: taskId, agent_id: agentId, event_type: "review_requested", payload: {} });
|
|
30447
30549
|
return { task: updated };
|
|
30448
30550
|
}
|
|
@@ -30450,8 +30552,8 @@ function rejectTask(taskId, reason, agentId) {
|
|
|
30450
30552
|
const task = getTask(taskId);
|
|
30451
30553
|
if (!task)
|
|
30452
30554
|
return { task: null, error: "Task not found" };
|
|
30453
|
-
if (task.status !== "
|
|
30454
|
-
return { task: null, error: "Task must be in
|
|
30555
|
+
if (task.status !== "review") {
|
|
30556
|
+
return { task: null, error: "Task must be in review status to reject" };
|
|
30455
30557
|
}
|
|
30456
30558
|
updateTask(taskId, { status: "in_progress" });
|
|
30457
30559
|
addImplementationNote(taskId, `REVIEW REJECTED: ${reason}`, agentId ?? "human");
|
|
@@ -38533,7 +38635,7 @@ var STATUS_ICON = {
|
|
|
38533
38635
|
ready: "\u25CE",
|
|
38534
38636
|
in_progress: "\u25B6",
|
|
38535
38637
|
blocked: "\u2298",
|
|
38536
|
-
|
|
38638
|
+
review: "\u25C8",
|
|
38537
38639
|
done: "\u2713"
|
|
38538
38640
|
};
|
|
38539
38641
|
|
|
@@ -40114,7 +40216,7 @@ var STATUS_COLORS = {
|
|
|
40114
40216
|
ready: "blue",
|
|
40115
40217
|
in_progress: "yellow",
|
|
40116
40218
|
blocked: "#FF5C5C",
|
|
40117
|
-
|
|
40219
|
+
review: "magenta",
|
|
40118
40220
|
done: "green"
|
|
40119
40221
|
};
|
|
40120
40222
|
var PRIORITY_COLORS = {
|
|
@@ -40526,7 +40628,7 @@ var STATUS_COLORS2 = {
|
|
|
40526
40628
|
ready: "blue",
|
|
40527
40629
|
in_progress: "yellow",
|
|
40528
40630
|
blocked: "#FF5C5C",
|
|
40529
|
-
|
|
40631
|
+
review: "magenta",
|
|
40530
40632
|
done: "green"
|
|
40531
40633
|
};
|
|
40532
40634
|
var PRIORITY_COLORS2 = {
|
|
@@ -41001,6 +41103,7 @@ var startCommand = new Command("start").description("Start working on task(s) (t
|
|
|
41001
41103
|
console.error("");
|
|
41002
41104
|
process.exit(1);
|
|
41003
41105
|
}
|
|
41106
|
+
const reviewRequired = getReviewRequired();
|
|
41004
41107
|
const results = [];
|
|
41005
41108
|
let allSuccess = true;
|
|
41006
41109
|
for (const taskId of resolvedIds) {
|
|
@@ -41014,18 +41117,21 @@ var startCommand = new Command("start").description("Start working on task(s) (t
|
|
|
41014
41117
|
}
|
|
41015
41118
|
continue;
|
|
41016
41119
|
}
|
|
41017
|
-
results.push({ id: taskId, success: true });
|
|
41120
|
+
results.push({ id: taskId, success: true, review_required: reviewRequired });
|
|
41018
41121
|
if (!json) {
|
|
41019
41122
|
console.log("");
|
|
41020
41123
|
console.log(source_default.green(" \u25B6") + ` Started ${source_default.bold(task.id)} \u2014 ${task.title}`);
|
|
41021
41124
|
if (task.acceptance_criteria.length > 0) {
|
|
41022
41125
|
console.log(source_default.dim(` ${task.acceptance_criteria.length} acceptance criteria to verify before done`));
|
|
41023
41126
|
}
|
|
41127
|
+
if (reviewRequired) {
|
|
41128
|
+
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}`));
|
|
41129
|
+
}
|
|
41024
41130
|
console.log(source_default.dim(` Add notes as you go: aitasks note ${task.id} "<your note>"`));
|
|
41025
41131
|
}
|
|
41026
41132
|
}
|
|
41027
41133
|
if (json) {
|
|
41028
|
-
return jsonOut(allSuccess, { results });
|
|
41134
|
+
return jsonOut(allSuccess, { results, review_required: reviewRequired });
|
|
41029
41135
|
}
|
|
41030
41136
|
console.log("");
|
|
41031
41137
|
if (!allSuccess) {
|
|
@@ -41197,6 +41303,7 @@ var reviewCommand = new Command("review").description("Request human review for
|
|
|
41197
41303
|
console.error("");
|
|
41198
41304
|
process.exit(1);
|
|
41199
41305
|
}
|
|
41306
|
+
const reviewRequired = getReviewRequired();
|
|
41200
41307
|
const results = [];
|
|
41201
41308
|
let allSuccess = true;
|
|
41202
41309
|
for (const taskId of resolvedIds) {
|
|
@@ -41210,12 +41317,37 @@ var reviewCommand = new Command("review").description("Request human review for
|
|
|
41210
41317
|
}
|
|
41211
41318
|
continue;
|
|
41212
41319
|
}
|
|
41213
|
-
|
|
41320
|
+
if (reviewRequired) {
|
|
41321
|
+
results.push({
|
|
41322
|
+
id: taskId,
|
|
41323
|
+
success: true,
|
|
41324
|
+
status: "review",
|
|
41325
|
+
next_action: "REQUIRED: Spawn a review sub-agent immediately. This task is NOT complete until the review agent approves it.",
|
|
41326
|
+
review_commands: {
|
|
41327
|
+
approve: `aitasks done ${taskId} --agent <review-agent-id>`,
|
|
41328
|
+
reject: `aitasks reject ${taskId} --reason "<specific feedback>" --agent <review-agent-id>`
|
|
41329
|
+
}
|
|
41330
|
+
});
|
|
41331
|
+
} else {
|
|
41332
|
+
results.push({ id: taskId, success: true, status: "review" });
|
|
41333
|
+
}
|
|
41214
41334
|
if (!json) {
|
|
41215
41335
|
console.log("");
|
|
41216
41336
|
console.log(source_default.magenta(" \u25C8") + ` ${source_default.bold(task.id)} submitted for review`);
|
|
41217
|
-
|
|
41218
|
-
|
|
41337
|
+
if (reviewRequired) {
|
|
41338
|
+
console.log(source_default.yellow(" \u26A0 ") + source_default.bold(" This task is NOT complete yet."));
|
|
41339
|
+
console.log("");
|
|
41340
|
+
console.log(` You MUST ${source_default.bold("immediately spawn a review sub-agent")} to inspect the implementation.`);
|
|
41341
|
+
console.log(source_default.dim(" The task remains incomplete until the review agent moves it to done."));
|
|
41342
|
+
console.log("");
|
|
41343
|
+
console.log(source_default.dim(" Review sub-agent steps:"));
|
|
41344
|
+
console.log(source_default.dim(` 1. Examine implementation and verify all acceptance criteria`));
|
|
41345
|
+
console.log(source_default.dim(` 2. Approve: aitasks done ${task.id} --agent <review-agent-id>`));
|
|
41346
|
+
console.log(source_default.dim(` Reject: aitasks reject ${task.id} --reason "<feedback>" --agent <review-agent-id>`));
|
|
41347
|
+
} else {
|
|
41348
|
+
console.log(source_default.dim(` Approve: aitasks done ${task.id} --agent <review-agent-id>`));
|
|
41349
|
+
console.log(source_default.dim(` Reject: aitasks reject ${task.id} --reason "<feedback>"`));
|
|
41350
|
+
}
|
|
41219
41351
|
}
|
|
41220
41352
|
}
|
|
41221
41353
|
if (json) {
|
|
@@ -41296,9 +41428,10 @@ var nextCommand = new Command("next").description("Show the next best task to wo
|
|
|
41296
41428
|
}
|
|
41297
41429
|
const aid = opts.claim ? requireAgentId(opts.agent, "next --claim") : agentId(opts.agent) ?? undefined;
|
|
41298
41430
|
const task = getNextTask(aid);
|
|
41431
|
+
const reviewRequired = getReviewRequired();
|
|
41299
41432
|
if (!task) {
|
|
41300
41433
|
if (json)
|
|
41301
|
-
return jsonOut(true, null);
|
|
41434
|
+
return jsonOut(true, { task: null, review_required: reviewRequired });
|
|
41302
41435
|
console.log("");
|
|
41303
41436
|
console.log(source_default.dim(" No ready tasks available. Check backlog with: aitasks list"));
|
|
41304
41437
|
console.log("");
|
|
@@ -41321,17 +41454,24 @@ var nextCommand = new Command("next").description("Show the next best task to wo
|
|
|
41321
41454
|
}
|
|
41322
41455
|
const updatedTask = startResult.task;
|
|
41323
41456
|
if (json)
|
|
41324
|
-
return jsonOut(true, updatedTask);
|
|
41457
|
+
return jsonOut(true, { ...updatedTask, review_required: reviewRequired });
|
|
41325
41458
|
console.log("");
|
|
41326
41459
|
console.log(source_default.green(" \u2713") + ` Claimed and started ${source_default.bold(updatedTask.id)}: ${updatedTask.title}`);
|
|
41327
41460
|
console.log(source_default.dim(` Priority: ${updatedTask.priority} \xB7 Type: ${updatedTask.type}`));
|
|
41328
41461
|
console.log(source_default.dim(` Agent: ${aid}`));
|
|
41462
|
+
if (reviewRequired) {
|
|
41463
|
+
console.log("");
|
|
41464
|
+
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"));
|
|
41465
|
+
}
|
|
41329
41466
|
console.log("");
|
|
41330
41467
|
return;
|
|
41331
41468
|
}
|
|
41332
41469
|
if (json)
|
|
41333
|
-
return jsonOut(true, task);
|
|
41470
|
+
return jsonOut(true, { ...task, review_required: reviewRequired });
|
|
41334
41471
|
console.log(renderTaskDetail(task));
|
|
41472
|
+
if (reviewRequired) {
|
|
41473
|
+
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"));
|
|
41474
|
+
}
|
|
41335
41475
|
console.log(source_default.dim(` Claim it: aitasks claim ${task.id} --agent <your-id>`));
|
|
41336
41476
|
console.log("");
|
|
41337
41477
|
});
|
|
@@ -41364,6 +41504,9 @@ var updateCommand = new Command("update").description("Update task fields").argu
|
|
|
41364
41504
|
if (Object.keys(changes).length === 0) {
|
|
41365
41505
|
exitError("No changes specified. Use --help to see available options.", json);
|
|
41366
41506
|
}
|
|
41507
|
+
if (opts.status === "done" && getReviewRequired() && task.status !== "review") {
|
|
41508
|
+
exitError("Review required: use `aitasks done` which enforces the review gate.\n" + " Submit for review first: aitasks review <taskId> --agent $AITASKS_AGENT_ID", json);
|
|
41509
|
+
}
|
|
41367
41510
|
const updated = updateTask(id, changes);
|
|
41368
41511
|
if (json)
|
|
41369
41512
|
return jsonOut(true, updated);
|
|
@@ -41387,7 +41530,7 @@ var STATUS_COLORS3 = {
|
|
|
41387
41530
|
ready: "blue",
|
|
41388
41531
|
in_progress: "yellow",
|
|
41389
41532
|
blocked: "#FF5C5C",
|
|
41390
|
-
|
|
41533
|
+
review: "magenta",
|
|
41391
41534
|
done: "green"
|
|
41392
41535
|
};
|
|
41393
41536
|
var PRIORITY_COLORS3 = {
|
|
@@ -41398,11 +41541,11 @@ var PRIORITY_COLORS3 = {
|
|
|
41398
41541
|
};
|
|
41399
41542
|
function buildTree(tasks) {
|
|
41400
41543
|
const sorted = [...tasks].sort((a2, b2) => a2.id.localeCompare(b2.id));
|
|
41401
|
-
const inProgressIds = new Set(sorted.filter((t) => t.status === "in_progress").map((t) => t.id));
|
|
41544
|
+
const inProgressIds = new Set(sorted.filter((t) => t.status === "in_progress" || t.status === "review").map((t) => t.id));
|
|
41402
41545
|
const doneIds = new Set(sorted.filter((t) => t.status === "done").map((t) => t.id));
|
|
41403
41546
|
const shownIds = new Set;
|
|
41404
41547
|
const items = [];
|
|
41405
|
-
const ipCount = sorted.filter((t) => t.status === "in_progress").length;
|
|
41548
|
+
const ipCount = sorted.filter((t) => t.status === "in_progress" || t.status === "review").length;
|
|
41406
41549
|
const doneCount = sorted.filter((t) => t.status === "done").length;
|
|
41407
41550
|
function push(task, indent, isLastSibling, section, showHeader) {
|
|
41408
41551
|
items.push({
|
|
@@ -41416,13 +41559,13 @@ function buildTree(tasks) {
|
|
|
41416
41559
|
shownIds.add(task.id);
|
|
41417
41560
|
}
|
|
41418
41561
|
let firstIp = true;
|
|
41419
|
-
for (const task of sorted.filter((t) => t.status === "in_progress" && !t.parent_id)) {
|
|
41562
|
+
for (const task of sorted.filter((t) => (t.status === "in_progress" || t.status === "review") && !t.parent_id)) {
|
|
41420
41563
|
push(task, 0, false, "in_progress", firstIp);
|
|
41421
41564
|
firstIp = false;
|
|
41422
41565
|
const subs = sorted.filter((t) => t.parent_id === task.id);
|
|
41423
41566
|
subs.forEach((sub, i) => push(sub, 1, i === subs.length - 1, "in_progress", false));
|
|
41424
41567
|
}
|
|
41425
|
-
for (const task of sorted.filter((t) => t.status === "in_progress" && !!t.parent_id)) {
|
|
41568
|
+
for (const task of sorted.filter((t) => (t.status === "in_progress" || t.status === "review") && !!t.parent_id)) {
|
|
41426
41569
|
if (!shownIds.has(task.id)) {
|
|
41427
41570
|
push(task, 0, false, "in_progress", firstIp);
|
|
41428
41571
|
firstIp = false;
|
|
@@ -41666,7 +41809,7 @@ var RightPane = ({ task, width, height, scrollOffset, metricsRef }) => {
|
|
|
41666
41809
|
}, i, true, undefined, this))
|
|
41667
41810
|
}, undefined, false, undefined, this);
|
|
41668
41811
|
};
|
|
41669
|
-
var PICKER_STATUSES = ["backlog", "ready", "in_progress", "blocked", "
|
|
41812
|
+
var PICKER_STATUSES = ["backlog", "ready", "in_progress", "blocked", "review", "done"];
|
|
41670
41813
|
var StatusPicker = ({ task }) => /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
|
|
41671
41814
|
flexDirection: "column",
|
|
41672
41815
|
paddingLeft: 2,
|
|
@@ -41877,7 +42020,7 @@ var TreeBoardComponent = ({ getTasks }) => {
|
|
|
41877
42020
|
"2": "ready",
|
|
41878
42021
|
"3": "in_progress",
|
|
41879
42022
|
"4": "blocked",
|
|
41880
|
-
"5": "
|
|
42023
|
+
"5": "review",
|
|
41881
42024
|
"6": "done"
|
|
41882
42025
|
};
|
|
41883
42026
|
const newStatus = statusMap[input];
|
|
@@ -42274,10 +42417,10 @@ var STATUS_LABELS = {
|
|
|
42274
42417
|
ready: "READY",
|
|
42275
42418
|
in_progress: "IN PROGRESS",
|
|
42276
42419
|
blocked: "BLOCKED",
|
|
42277
|
-
|
|
42420
|
+
review: "REVIEW",
|
|
42278
42421
|
done: "DONE"
|
|
42279
42422
|
};
|
|
42280
|
-
var ALL_STATUSES = ["in_progress", "
|
|
42423
|
+
var ALL_STATUSES = ["in_progress", "review", "ready", "blocked", "backlog", "done"];
|
|
42281
42424
|
var StaticCard = ({ task }) => {
|
|
42282
42425
|
const priColor = PRIORITY_COLORS3[task.priority] ?? "gray";
|
|
42283
42426
|
return /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
|
|
@@ -42544,13 +42687,14 @@ var exportCommand = new Command("export").description("Export all task data").op
|
|
|
42544
42687
|
// src/commands/onboard.ts
|
|
42545
42688
|
var VERSION = "1.0.0";
|
|
42546
42689
|
var onboardCommand = new Command("onboard").description("Print or inject agent protocol instructions").option("--append", "Auto-detect and append to CLAUDE.md/AGENTS.md/GEMINI.md").option("--file <path>", "Append to a specific file").option("--json", "Output the instructions as a JSON string").action((opts) => {
|
|
42547
|
-
const
|
|
42690
|
+
const reviewRequired = isInitialized() ? getReviewRequired() : false;
|
|
42691
|
+
const instructions = getAgentInstructions(VERSION, { reviewRequired });
|
|
42548
42692
|
if (opts.json) {
|
|
42549
42693
|
console.log(JSON.stringify({ instructions }, null, 2));
|
|
42550
42694
|
return;
|
|
42551
42695
|
}
|
|
42552
42696
|
if (opts.file) {
|
|
42553
|
-
const result2 = appendToSpecificFile(opts.file, VERSION);
|
|
42697
|
+
const result2 = appendToSpecificFile(opts.file, VERSION, reviewRequired);
|
|
42554
42698
|
const rel = opts.file.replace(process.cwd() + "/", "");
|
|
42555
42699
|
switch (result2.action) {
|
|
42556
42700
|
case "created":
|
|
@@ -42567,7 +42711,7 @@ var onboardCommand = new Command("onboard").description("Print or inject agent p
|
|
|
42567
42711
|
}
|
|
42568
42712
|
if (opts.append) {
|
|
42569
42713
|
const root = findProjectRoot();
|
|
42570
|
-
const result2 = injectOrCreateAgentFile(root, VERSION);
|
|
42714
|
+
const result2 = injectOrCreateAgentFile(root, VERSION, reviewRequired);
|
|
42571
42715
|
const rel = result2.filePath.replace(process.cwd() + "/", "");
|
|
42572
42716
|
switch (result2.action) {
|
|
42573
42717
|
case "created":
|
|
@@ -42738,7 +42882,7 @@ function getStatusIcon(status) {
|
|
|
42738
42882
|
ready: "\u25D0",
|
|
42739
42883
|
in_progress: "\u25B6",
|
|
42740
42884
|
blocked: "\u2298",
|
|
42741
|
-
|
|
42885
|
+
review: "\u25C8",
|
|
42742
42886
|
done: "\u2713"
|
|
42743
42887
|
};
|
|
42744
42888
|
return icons[status] || "\u25CB";
|
|
@@ -42778,7 +42922,7 @@ var searchCommand = new Command("search").description("Full-text search across t
|
|
|
42778
42922
|
const statusIcon = getStatusIcon2(task.status);
|
|
42779
42923
|
const priColor = getPriorityColor(task.priority);
|
|
42780
42924
|
console.log("");
|
|
42781
|
-
console.log(`${source_default.bold.white(task.id)} ` + `${priColor(task.priority[0].toUpperCase())} ` + `${statusIcon} ` + highlightMatches(task.title, searchTerms));
|
|
42925
|
+
console.log(`${source_default.bold.white(task.id)} ` + `${priColor((task.priority[0] ?? "?").toUpperCase())} ` + `${statusIcon} ` + highlightMatches(task.title, searchTerms));
|
|
42782
42926
|
const context = getSearchContext(task, searchTerms);
|
|
42783
42927
|
if (context) {
|
|
42784
42928
|
console.log(source_default.dim(` ${context}`));
|
|
@@ -42837,7 +42981,7 @@ function getStatusIcon2(status) {
|
|
|
42837
42981
|
ready: source_default.blue("\u25D0"),
|
|
42838
42982
|
in_progress: source_default.yellow("\u25B6"),
|
|
42839
42983
|
blocked: source_default.hex("#FF5C5C")("\u2298"),
|
|
42840
|
-
|
|
42984
|
+
review: source_default.magenta("\u25C8"),
|
|
42841
42985
|
done: source_default.green("\u2713")
|
|
42842
42986
|
};
|
|
42843
42987
|
return icons[status] || "\u25CB";
|
|
@@ -42927,25 +43071,26 @@ var undoCommand = new Command("undo").description("Undo the last action on a tas
|
|
|
42927
43071
|
}
|
|
42928
43072
|
});
|
|
42929
43073
|
function undoComplete(task) {
|
|
43074
|
+
const newStatus = getReviewRequired() ? "review" : "in_progress";
|
|
42930
43075
|
updateTask(task.id, {
|
|
42931
|
-
status:
|
|
43076
|
+
status: newStatus,
|
|
42932
43077
|
completed_at: null
|
|
42933
43078
|
});
|
|
42934
43079
|
return {
|
|
42935
43080
|
success: true,
|
|
42936
|
-
message: `${task.id} reverted from done to
|
|
42937
|
-
data: { task: task.id, previousStatus: "done", newStatus
|
|
43081
|
+
message: `${task.id} reverted from done to ${newStatus}`,
|
|
43082
|
+
data: { task: task.id, previousStatus: "done", newStatus }
|
|
42938
43083
|
};
|
|
42939
43084
|
}
|
|
42940
43085
|
function undoReviewRequested(task) {
|
|
42941
|
-
if (task.status !== "
|
|
42942
|
-
return { success: false, message: `${task.id} is not in
|
|
43086
|
+
if (task.status !== "review") {
|
|
43087
|
+
return { success: false, message: `${task.id} is not in review status` };
|
|
42943
43088
|
}
|
|
42944
43089
|
updateTask(task.id, { status: "in_progress" });
|
|
42945
43090
|
return {
|
|
42946
43091
|
success: true,
|
|
42947
|
-
message: `${task.id} reverted from
|
|
42948
|
-
data: { task: task.id, previousStatus: "
|
|
43092
|
+
message: `${task.id} reverted from review to in_progress`,
|
|
43093
|
+
data: { task: task.id, previousStatus: "review", newStatus: "in_progress" }
|
|
42949
43094
|
};
|
|
42950
43095
|
}
|
|
42951
43096
|
function undoStarted(task) {
|
|
@@ -43135,4 +43280,4 @@ program2.parseAsync(process.argv).catch((err) => {
|
|
|
43135
43280
|
process.exit(1);
|
|
43136
43281
|
});
|
|
43137
43282
|
|
|
43138
|
-
//# debugId=
|
|
43283
|
+
//# debugId=3AB57D11F115EBBB64756E2164756E21
|