steroids-cli 0.4.47
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/LICENSE +21 -0
- package/README.md +640 -0
- package/dist/cli/colors.d.ts +110 -0
- package/dist/cli/colors.d.ts.map +1 -0
- package/dist/cli/colors.js +228 -0
- package/dist/cli/colors.js.map +1 -0
- package/dist/cli/env.d.ts +159 -0
- package/dist/cli/env.d.ts.map +1 -0
- package/dist/cli/env.js +227 -0
- package/dist/cli/env.js.map +1 -0
- package/dist/cli/errors.d.ts +166 -0
- package/dist/cli/errors.d.ts.map +1 -0
- package/dist/cli/errors.js +244 -0
- package/dist/cli/errors.js.map +1 -0
- package/dist/cli/flags.d.ts +75 -0
- package/dist/cli/flags.d.ts.map +1 -0
- package/dist/cli/flags.js +232 -0
- package/dist/cli/flags.js.map +1 -0
- package/dist/cli/help.d.ts +97 -0
- package/dist/cli/help.d.ts.map +1 -0
- package/dist/cli/help.js +275 -0
- package/dist/cli/help.js.map +1 -0
- package/dist/cli/index.d.ts +13 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +29 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/interactive.d.ts +58 -0
- package/dist/cli/interactive.d.ts.map +1 -0
- package/dist/cli/interactive.js +127 -0
- package/dist/cli/interactive.js.map +1 -0
- package/dist/cli/output.d.ts +116 -0
- package/dist/cli/output.d.ts.map +1 -0
- package/dist/cli/output.js +178 -0
- package/dist/cli/output.js.map +1 -0
- package/dist/commands/about.d.ts +7 -0
- package/dist/commands/about.d.ts.map +1 -0
- package/dist/commands/about.js +259 -0
- package/dist/commands/about.js.map +1 -0
- package/dist/commands/ai.d.ts +6 -0
- package/dist/commands/ai.d.ts.map +1 -0
- package/dist/commands/ai.js +382 -0
- package/dist/commands/ai.js.map +1 -0
- package/dist/commands/backup.d.ts +3 -0
- package/dist/commands/backup.d.ts.map +1 -0
- package/dist/commands/backup.js +528 -0
- package/dist/commands/backup.js.map +1 -0
- package/dist/commands/completion.d.ts +3 -0
- package/dist/commands/completion.d.ts.map +1 -0
- package/dist/commands/completion.js +405 -0
- package/dist/commands/completion.js.map +1 -0
- package/dist/commands/config.d.ts +3 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +665 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/disputes.d.ts +3 -0
- package/dist/commands/disputes.d.ts.map +1 -0
- package/dist/commands/disputes.js +499 -0
- package/dist/commands/disputes.js.map +1 -0
- package/dist/commands/gc.d.ts +3 -0
- package/dist/commands/gc.d.ts.map +1 -0
- package/dist/commands/gc.js +300 -0
- package/dist/commands/gc.js.map +1 -0
- package/dist/commands/git.d.ts +3 -0
- package/dist/commands/git.d.ts.map +1 -0
- package/dist/commands/git.js +458 -0
- package/dist/commands/git.js.map +1 -0
- package/dist/commands/health.d.ts +3 -0
- package/dist/commands/health.d.ts.map +1 -0
- package/dist/commands/health.js +604 -0
- package/dist/commands/health.js.map +1 -0
- package/dist/commands/hooks.d.ts +6 -0
- package/dist/commands/hooks.d.ts.map +1 -0
- package/dist/commands/hooks.js +529 -0
- package/dist/commands/hooks.js.map +1 -0
- package/dist/commands/init.d.ts +6 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +200 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/llm.d.ts +7 -0
- package/dist/commands/llm.d.ts.map +1 -0
- package/dist/commands/llm.js +285 -0
- package/dist/commands/llm.js.map +1 -0
- package/dist/commands/locks.d.ts +3 -0
- package/dist/commands/locks.d.ts.map +1 -0
- package/dist/commands/locks.js +431 -0
- package/dist/commands/locks.js.map +1 -0
- package/dist/commands/logs.d.ts +3 -0
- package/dist/commands/logs.d.ts.map +1 -0
- package/dist/commands/logs.js +487 -0
- package/dist/commands/logs.js.map +1 -0
- package/dist/commands/loop-phases.d.ts +11 -0
- package/dist/commands/loop-phases.d.ts.map +1 -0
- package/dist/commands/loop-phases.js +204 -0
- package/dist/commands/loop-phases.js.map +1 -0
- package/dist/commands/loop.d.ts +3 -0
- package/dist/commands/loop.d.ts.map +1 -0
- package/dist/commands/loop.js +396 -0
- package/dist/commands/loop.js.map +1 -0
- package/dist/commands/projects.d.ts +6 -0
- package/dist/commands/projects.d.ts.map +1 -0
- package/dist/commands/projects.js +362 -0
- package/dist/commands/projects.js.map +1 -0
- package/dist/commands/purge.d.ts +3 -0
- package/dist/commands/purge.d.ts.map +1 -0
- package/dist/commands/purge.js +516 -0
- package/dist/commands/purge.js.map +1 -0
- package/dist/commands/runners.d.ts +3 -0
- package/dist/commands/runners.d.ts.map +1 -0
- package/dist/commands/runners.js +1076 -0
- package/dist/commands/runners.js.map +1 -0
- package/dist/commands/scan.d.ts +3 -0
- package/dist/commands/scan.d.ts.map +1 -0
- package/dist/commands/scan.js +291 -0
- package/dist/commands/scan.js.map +1 -0
- package/dist/commands/sections-commands.d.ts +9 -0
- package/dist/commands/sections-commands.d.ts.map +1 -0
- package/dist/commands/sections-commands.js +282 -0
- package/dist/commands/sections-commands.js.map +1 -0
- package/dist/commands/sections-graph.d.ts +25 -0
- package/dist/commands/sections-graph.d.ts.map +1 -0
- package/dist/commands/sections-graph.js +180 -0
- package/dist/commands/sections-graph.js.map +1 -0
- package/dist/commands/sections.d.ts +3 -0
- package/dist/commands/sections.d.ts.map +1 -0
- package/dist/commands/sections.js +376 -0
- package/dist/commands/sections.js.map +1 -0
- package/dist/commands/stats.d.ts +6 -0
- package/dist/commands/stats.d.ts.map +1 -0
- package/dist/commands/stats.js +324 -0
- package/dist/commands/stats.js.map +1 -0
- package/dist/commands/tasks.d.ts +3 -0
- package/dist/commands/tasks.d.ts.map +1 -0
- package/dist/commands/tasks.js +1115 -0
- package/dist/commands/tasks.js.map +1 -0
- package/dist/commands/web.d.ts +7 -0
- package/dist/commands/web.d.ts.map +1 -0
- package/dist/commands/web.js +204 -0
- package/dist/commands/web.js.map +1 -0
- package/dist/config/ai-setup.d.ts +27 -0
- package/dist/config/ai-setup.d.ts.map +1 -0
- package/dist/config/ai-setup.js +432 -0
- package/dist/config/ai-setup.js.map +1 -0
- package/dist/config/browser.d.ts +9 -0
- package/dist/config/browser.d.ts.map +1 -0
- package/dist/config/browser.js +200 -0
- package/dist/config/browser.js.map +1 -0
- package/dist/config/json-schema.d.ts +28 -0
- package/dist/config/json-schema.d.ts.map +1 -0
- package/dist/config/json-schema.js +84 -0
- package/dist/config/json-schema.js.map +1 -0
- package/dist/config/loader.d.ts +152 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +270 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/schema.d.ts +34 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +437 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/config/validator.d.ts +32 -0
- package/dist/config/validator.d.ts.map +1 -0
- package/dist/config/validator.js +187 -0
- package/dist/config/validator.js.map +1 -0
- package/dist/database/connection.d.ts +35 -0
- package/dist/database/connection.d.ts.map +1 -0
- package/dist/database/connection.js +208 -0
- package/dist/database/connection.js.map +1 -0
- package/dist/database/queries.d.ts +218 -0
- package/dist/database/queries.d.ts.map +1 -0
- package/dist/database/queries.js +613 -0
- package/dist/database/queries.js.map +1 -0
- package/dist/database/schema.d.ts +8 -0
- package/dist/database/schema.d.ts.map +1 -0
- package/dist/database/schema.js +160 -0
- package/dist/database/schema.js.map +1 -0
- package/dist/disputes/behavior.d.ts +106 -0
- package/dist/disputes/behavior.d.ts.map +1 -0
- package/dist/disputes/behavior.js +150 -0
- package/dist/disputes/behavior.js.map +1 -0
- package/dist/disputes/create.d.ts +59 -0
- package/dist/disputes/create.d.ts.map +1 -0
- package/dist/disputes/create.js +222 -0
- package/dist/disputes/create.js.map +1 -0
- package/dist/disputes/index.d.ts +21 -0
- package/dist/disputes/index.d.ts.map +1 -0
- package/dist/disputes/index.js +76 -0
- package/dist/disputes/index.js.map +1 -0
- package/dist/disputes/markdown.d.ts +41 -0
- package/dist/disputes/markdown.d.ts.map +1 -0
- package/dist/disputes/markdown.js +261 -0
- package/dist/disputes/markdown.js.map +1 -0
- package/dist/disputes/queries.d.ts +83 -0
- package/dist/disputes/queries.d.ts.map +1 -0
- package/dist/disputes/queries.js +180 -0
- package/dist/disputes/queries.js.map +1 -0
- package/dist/disputes/resolve.d.ts +57 -0
- package/dist/disputes/resolve.d.ts.map +1 -0
- package/dist/disputes/resolve.js +171 -0
- package/dist/disputes/resolve.js.map +1 -0
- package/dist/disputes/stale.d.ts +98 -0
- package/dist/disputes/stale.d.ts.map +1 -0
- package/dist/disputes/stale.js +205 -0
- package/dist/disputes/stale.js.map +1 -0
- package/dist/disputes/types.d.ts +92 -0
- package/dist/disputes/types.d.ts.map +1 -0
- package/dist/disputes/types.js +100 -0
- package/dist/disputes/types.js.map +1 -0
- package/dist/git/push.d.ts +26 -0
- package/dist/git/push.d.ts.map +1 -0
- package/dist/git/push.js +97 -0
- package/dist/git/push.js.map +1 -0
- package/dist/git/status.d.ts +61 -0
- package/dist/git/status.d.ts.map +1 -0
- package/dist/git/status.js +251 -0
- package/dist/git/status.js.map +1 -0
- package/dist/hooks/events.d.ts +72 -0
- package/dist/hooks/events.d.ts.map +1 -0
- package/dist/hooks/events.js +120 -0
- package/dist/hooks/events.js.map +1 -0
- package/dist/hooks/index.d.ts +19 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +48 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/integration.d.ts +69 -0
- package/dist/hooks/integration.d.ts.map +1 -0
- package/dist/hooks/integration.js +179 -0
- package/dist/hooks/integration.js.map +1 -0
- package/dist/hooks/merge.d.ts +115 -0
- package/dist/hooks/merge.d.ts.map +1 -0
- package/dist/hooks/merge.js +161 -0
- package/dist/hooks/merge.js.map +1 -0
- package/dist/hooks/orchestrator.d.ts +115 -0
- package/dist/hooks/orchestrator.d.ts.map +1 -0
- package/dist/hooks/orchestrator.js +226 -0
- package/dist/hooks/orchestrator.js.map +1 -0
- package/dist/hooks/payload.d.ts +294 -0
- package/dist/hooks/payload.d.ts.map +1 -0
- package/dist/hooks/payload.js +267 -0
- package/dist/hooks/payload.js.map +1 -0
- package/dist/hooks/script-runner.d.ts +63 -0
- package/dist/hooks/script-runner.d.ts.map +1 -0
- package/dist/hooks/script-runner.js +221 -0
- package/dist/hooks/script-runner.js.map +1 -0
- package/dist/hooks/templates.d.ts +104 -0
- package/dist/hooks/templates.d.ts.map +1 -0
- package/dist/hooks/templates.js +327 -0
- package/dist/hooks/templates.js.map +1 -0
- package/dist/hooks/webhook-runner.d.ts +69 -0
- package/dist/hooks/webhook-runner.d.ts.map +1 -0
- package/dist/hooks/webhook-runner.js +208 -0
- package/dist/hooks/webhook-runner.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +281 -0
- package/dist/index.js.map +1 -0
- package/dist/locking/cleanup.d.ts +70 -0
- package/dist/locking/cleanup.d.ts.map +1 -0
- package/dist/locking/cleanup.js +157 -0
- package/dist/locking/cleanup.js.map +1 -0
- package/dist/locking/queries.d.ts +116 -0
- package/dist/locking/queries.d.ts.map +1 -0
- package/dist/locking/queries.js +255 -0
- package/dist/locking/queries.js.map +1 -0
- package/dist/locking/section-lock.d.ts +74 -0
- package/dist/locking/section-lock.d.ts.map +1 -0
- package/dist/locking/section-lock.js +207 -0
- package/dist/locking/section-lock.js.map +1 -0
- package/dist/locking/task-lock.d.ts +92 -0
- package/dist/locking/task-lock.d.ts.map +1 -0
- package/dist/locking/task-lock.js +246 -0
- package/dist/locking/task-lock.js.map +1 -0
- package/dist/migrations/index.d.ts +7 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/migrations/index.js +37 -0
- package/dist/migrations/index.js.map +1 -0
- package/dist/migrations/manifest.d.ts +92 -0
- package/dist/migrations/manifest.d.ts.map +1 -0
- package/dist/migrations/manifest.js +270 -0
- package/dist/migrations/manifest.js.map +1 -0
- package/dist/migrations/runner.d.ts +84 -0
- package/dist/migrations/runner.d.ts.map +1 -0
- package/dist/migrations/runner.js +351 -0
- package/dist/migrations/runner.js.map +1 -0
- package/dist/orchestrator/coder.d.ts +32 -0
- package/dist/orchestrator/coder.d.ts.map +1 -0
- package/dist/orchestrator/coder.js +174 -0
- package/dist/orchestrator/coder.js.map +1 -0
- package/dist/orchestrator/coordinator.d.ts +28 -0
- package/dist/orchestrator/coordinator.d.ts.map +1 -0
- package/dist/orchestrator/coordinator.js +256 -0
- package/dist/orchestrator/coordinator.js.map +1 -0
- package/dist/orchestrator/reviewer.d.ts +35 -0
- package/dist/orchestrator/reviewer.d.ts.map +1 -0
- package/dist/orchestrator/reviewer.js +241 -0
- package/dist/orchestrator/reviewer.js.map +1 -0
- package/dist/orchestrator/task-selector.d.ts +102 -0
- package/dist/orchestrator/task-selector.d.ts.map +1 -0
- package/dist/orchestrator/task-selector.js +341 -0
- package/dist/orchestrator/task-selector.js.map +1 -0
- package/dist/prompts/coder.d.ts +36 -0
- package/dist/prompts/coder.d.ts.map +1 -0
- package/dist/prompts/coder.js +315 -0
- package/dist/prompts/coder.js.map +1 -0
- package/dist/prompts/prompt-helpers.d.ts +51 -0
- package/dist/prompts/prompt-helpers.d.ts.map +1 -0
- package/dist/prompts/prompt-helpers.js +312 -0
- package/dist/prompts/prompt-helpers.js.map +1 -0
- package/dist/prompts/reviewer.d.ts +40 -0
- package/dist/prompts/reviewer.d.ts.map +1 -0
- package/dist/prompts/reviewer.js +438 -0
- package/dist/prompts/reviewer.js.map +1 -0
- package/dist/providers/api-models.d.ts +65 -0
- package/dist/providers/api-models.d.ts.map +1 -0
- package/dist/providers/api-models.js +323 -0
- package/dist/providers/api-models.js.map +1 -0
- package/dist/providers/claude.d.ts +53 -0
- package/dist/providers/claude.d.ts.map +1 -0
- package/dist/providers/claude.js +229 -0
- package/dist/providers/claude.js.map +1 -0
- package/dist/providers/codex.d.ts +53 -0
- package/dist/providers/codex.d.ts.map +1 -0
- package/dist/providers/codex.js +214 -0
- package/dist/providers/codex.js.map +1 -0
- package/dist/providers/gemini.d.ts +58 -0
- package/dist/providers/gemini.d.ts.map +1 -0
- package/dist/providers/gemini.js +242 -0
- package/dist/providers/gemini.js.map +1 -0
- package/dist/providers/index.d.ts +13 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +49 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/interface.d.ts +173 -0
- package/dist/providers/interface.d.ts.map +1 -0
- package/dist/providers/interface.js +96 -0
- package/dist/providers/interface.js.map +1 -0
- package/dist/providers/invocation-logger.d.ts +114 -0
- package/dist/providers/invocation-logger.d.ts.map +1 -0
- package/dist/providers/invocation-logger.js +298 -0
- package/dist/providers/invocation-logger.js.map +1 -0
- package/dist/providers/openai.d.ts +53 -0
- package/dist/providers/openai.d.ts.map +1 -0
- package/dist/providers/openai.js +232 -0
- package/dist/providers/openai.js.map +1 -0
- package/dist/providers/registry.d.ts +100 -0
- package/dist/providers/registry.d.ts.map +1 -0
- package/dist/providers/registry.js +178 -0
- package/dist/providers/registry.js.map +1 -0
- package/dist/runners/activity-log.d.ts +65 -0
- package/dist/runners/activity-log.d.ts.map +1 -0
- package/dist/runners/activity-log.js +148 -0
- package/dist/runners/activity-log.js.map +1 -0
- package/dist/runners/cron.d.ts +26 -0
- package/dist/runners/cron.d.ts.map +1 -0
- package/dist/runners/cron.js +176 -0
- package/dist/runners/cron.js.map +1 -0
- package/dist/runners/daemon.d.ts +71 -0
- package/dist/runners/daemon.d.ts.map +1 -0
- package/dist/runners/daemon.js +245 -0
- package/dist/runners/daemon.js.map +1 -0
- package/dist/runners/global-db.d.ts +31 -0
- package/dist/runners/global-db.d.ts.map +1 -0
- package/dist/runners/global-db.js +230 -0
- package/dist/runners/global-db.js.map +1 -0
- package/dist/runners/hang-detector.d.ts +38 -0
- package/dist/runners/hang-detector.d.ts.map +1 -0
- package/dist/runners/hang-detector.js +136 -0
- package/dist/runners/hang-detector.js.map +1 -0
- package/dist/runners/heartbeat.d.ts +39 -0
- package/dist/runners/heartbeat.d.ts.map +1 -0
- package/dist/runners/heartbeat.js +79 -0
- package/dist/runners/heartbeat.js.map +1 -0
- package/dist/runners/lock.d.ts +47 -0
- package/dist/runners/lock.d.ts.map +1 -0
- package/dist/runners/lock.js +150 -0
- package/dist/runners/lock.js.map +1 -0
- package/dist/runners/orchestrator-loop.d.ts +20 -0
- package/dist/runners/orchestrator-loop.d.ts.map +1 -0
- package/dist/runners/orchestrator-loop.js +285 -0
- package/dist/runners/orchestrator-loop.js.map +1 -0
- package/dist/runners/projects.d.ts +96 -0
- package/dist/runners/projects.d.ts.map +1 -0
- package/dist/runners/projects.js +255 -0
- package/dist/runners/projects.js.map +1 -0
- package/dist/runners/wakeup.d.ts +34 -0
- package/dist/runners/wakeup.d.ts.map +1 -0
- package/dist/runners/wakeup.js +291 -0
- package/dist/runners/wakeup.js.map +1 -0
- package/migrations/001_initial_schema.sql +106 -0
- package/migrations/002_add_commit_sha.sql +12 -0
- package/migrations/003_add_section_priority.sql +13 -0
- package/migrations/004_add_section_dependencies.sql +18 -0
- package/migrations/005_add_audit_actor_model.sql +10 -0
- package/migrations/006_add_task_invocations.sql +33 -0
- package/migrations/007_add_file_anchor.sql +14 -0
- package/migrations/manifest.json +62 -0
- package/package.json +49 -0
|
@@ -0,0 +1,1115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.tasksCommand = tasksCommand;
|
|
4
|
+
/**
|
|
5
|
+
* steroids tasks - Manage tasks
|
|
6
|
+
*/
|
|
7
|
+
const node_util_1 = require("node:util");
|
|
8
|
+
const node_fs_1 = require("node:fs");
|
|
9
|
+
const node_path_1 = require("node:path");
|
|
10
|
+
const connection_js_1 = require("../database/connection.js");
|
|
11
|
+
const projects_js_1 = require("../runners/projects.js");
|
|
12
|
+
const activity_log_js_1 = require("../runners/activity-log.js");
|
|
13
|
+
const queries_js_1 = require("../database/queries.js");
|
|
14
|
+
const output_js_1 = require("../cli/output.js");
|
|
15
|
+
const errors_js_1 = require("../cli/errors.js");
|
|
16
|
+
const help_js_1 = require("../cli/help.js");
|
|
17
|
+
const integration_js_1 = require("../hooks/integration.js");
|
|
18
|
+
const status_js_1 = require("../git/status.js");
|
|
19
|
+
const HELP = (0, help_js_1.generateHelp)({
|
|
20
|
+
command: 'tasks',
|
|
21
|
+
description: 'Manage tasks in the automated development workflow',
|
|
22
|
+
details: `Tasks are units of work that flow through the coder/reviewer loop.
|
|
23
|
+
Each task has a specification file and tracks progress through various states.
|
|
24
|
+
Use this command to add, update, approve, reject, or skip tasks.`,
|
|
25
|
+
usage: [
|
|
26
|
+
'steroids tasks [options]',
|
|
27
|
+
'steroids tasks <subcommand> [args] [options]',
|
|
28
|
+
],
|
|
29
|
+
subcommands: [
|
|
30
|
+
{ name: 'list', description: 'List tasks (default subcommand)' },
|
|
31
|
+
{ name: 'stats', description: 'Show task counts by status' },
|
|
32
|
+
{ name: 'show', args: '<id|title>', description: 'Show task details with invocation logs' },
|
|
33
|
+
{ name: 'add', args: '<title>', description: 'Add a new task' },
|
|
34
|
+
{ name: 'update', args: '<id|title>', description: 'Update task status' },
|
|
35
|
+
{ name: 'approve', args: '<id|title>', description: 'Approve a task (mark completed)' },
|
|
36
|
+
{ name: 'reject', args: '<id|title>', description: 'Reject a task (back to in_progress)' },
|
|
37
|
+
{ name: 'skip', args: '<id|title>', description: 'Skip a task (external/manual work)' },
|
|
38
|
+
{ name: 'audit', args: '<id|title>', description: 'View task audit trail' },
|
|
39
|
+
],
|
|
40
|
+
options: [
|
|
41
|
+
{ short: 's', long: 'status', description: 'Filter by status', values: 'pending | in_progress | review | completed | disputed | failed | skipped | partial | active | all', default: 'pending' },
|
|
42
|
+
{ short: 'g', long: 'global', description: 'List tasks across ALL registered projects' },
|
|
43
|
+
{ long: 'section', description: 'Filter by section ID (local project only)', values: '<id>' },
|
|
44
|
+
{ long: 'search', description: 'Search in task titles', values: '<query>' },
|
|
45
|
+
{ long: 'reset-rejections', description: 'Reset rejection count to 0 (update subcommand)' },
|
|
46
|
+
{ long: 'actor', description: 'Actor making the change', values: '<name>' },
|
|
47
|
+
{ long: 'model', description: 'Model identifier (for LLM actors)', values: '<model>' },
|
|
48
|
+
{ long: 'notes', description: 'Review notes/comments', values: '<text>' },
|
|
49
|
+
{ long: 'source', description: 'Specification file (add subcommand)', values: '<file>' },
|
|
50
|
+
{ long: 'file', description: 'Anchor task to a committed file (add subcommand)', values: '<path>' },
|
|
51
|
+
{ long: 'line', description: 'Line number in anchored file (requires --file)', values: '<number>' },
|
|
52
|
+
{ long: 'feedback', description: 'Add to "Needs User Input" section (skipped, for human review)' },
|
|
53
|
+
{ long: 'partial', description: 'Mark as partial when skipping' },
|
|
54
|
+
{ long: 'no-hooks', description: 'Skip hook execution (global flag)' },
|
|
55
|
+
],
|
|
56
|
+
examples: [
|
|
57
|
+
{ command: 'steroids tasks', description: 'List pending tasks' },
|
|
58
|
+
{ command: 'steroids tasks --status all', description: 'List all tasks' },
|
|
59
|
+
{ command: 'steroids tasks --status active', description: 'Show active tasks (in_progress + review)' },
|
|
60
|
+
{ command: 'steroids tasks --status skipped', description: 'See what needs manual action' },
|
|
61
|
+
{ command: 'steroids tasks --global --json', description: 'List tasks from all projects as JSON' },
|
|
62
|
+
{ command: 'steroids tasks add "Implement login" --section abc123 --source docs/spec.md', description: 'Add new task' },
|
|
63
|
+
{ command: 'steroids tasks add "Fix bug" --section abc123 --source spec.md --file src/utils.ts --line 42', description: 'Add task anchored to a file' },
|
|
64
|
+
{ command: 'steroids tasks add "Review execSync usage" --feedback', description: 'Add feedback task (skipped section)' },
|
|
65
|
+
{ command: 'steroids tasks update "Implement login" --status review', description: 'Update task status' },
|
|
66
|
+
{ command: 'steroids tasks approve abc123 --model claude-sonnet-4', description: 'Approve a task' },
|
|
67
|
+
{ command: 'steroids tasks approve abc123 --no-hooks', description: 'Approve without triggering hooks' },
|
|
68
|
+
{ command: 'steroids tasks reject abc123 --model codex --notes "Missing tests"', description: 'Reject a task' },
|
|
69
|
+
{ command: 'steroids tasks skip abc123 --notes "Cloud SQL - manual setup"', description: 'Skip a task' },
|
|
70
|
+
{ command: 'steroids tasks audit abc123', description: 'View task history' },
|
|
71
|
+
{ command: 'steroids tasks show abc123 --logs', description: 'Show task with LLM invocation logs' },
|
|
72
|
+
{ command: 'steroids tasks stats --json', description: 'Get task statistics as JSON' },
|
|
73
|
+
],
|
|
74
|
+
related: [
|
|
75
|
+
{ command: 'steroids sections', description: 'Manage task sections' },
|
|
76
|
+
{ command: 'steroids loop', description: 'Run automation on pending tasks' },
|
|
77
|
+
{ command: 'steroids dispute', description: 'View coder/reviewer disputes' },
|
|
78
|
+
],
|
|
79
|
+
sections: [
|
|
80
|
+
{
|
|
81
|
+
title: 'STATUS VALUES',
|
|
82
|
+
content: `pending [ ] Not started
|
|
83
|
+
in_progress [-] Being worked on by coder
|
|
84
|
+
review [o] Ready for reviewer
|
|
85
|
+
completed [x] Approved by reviewer
|
|
86
|
+
disputed [!] Coder/reviewer disagreement
|
|
87
|
+
failed [F] Exceeded 15 rejections (needs human)
|
|
88
|
+
skipped [S] Fully external (nothing to code)
|
|
89
|
+
partial [s] Coded what we could, rest external
|
|
90
|
+
active Combined: in_progress + review`,
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
});
|
|
94
|
+
async function tasksCommand(args, flags) {
|
|
95
|
+
// Check global help flag (parsed by main CLI)
|
|
96
|
+
if (flags.help) {
|
|
97
|
+
console.log(HELP);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (args.length === 0) {
|
|
101
|
+
// Default: list pending tasks
|
|
102
|
+
await listAllTasks([], flags);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
// Check if first arg is a subcommand or a flag
|
|
106
|
+
const subcommand = args[0];
|
|
107
|
+
if (subcommand.startsWith('-')) {
|
|
108
|
+
// It's a flag, so this is a list command
|
|
109
|
+
await listAllTasks(args, flags);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const subArgs = args.slice(1);
|
|
113
|
+
switch (subcommand) {
|
|
114
|
+
case 'stats':
|
|
115
|
+
await showStats(subArgs, flags);
|
|
116
|
+
break;
|
|
117
|
+
case 'show':
|
|
118
|
+
await showTask(subArgs, flags);
|
|
119
|
+
break;
|
|
120
|
+
case 'add':
|
|
121
|
+
await addTask(subArgs, flags);
|
|
122
|
+
break;
|
|
123
|
+
case 'update':
|
|
124
|
+
await updateTask(subArgs, flags);
|
|
125
|
+
break;
|
|
126
|
+
case 'approve':
|
|
127
|
+
await approveTaskCmd(subArgs, flags);
|
|
128
|
+
break;
|
|
129
|
+
case 'reject':
|
|
130
|
+
await rejectTaskCmd(subArgs, flags);
|
|
131
|
+
break;
|
|
132
|
+
case 'skip':
|
|
133
|
+
await skipTaskCmd(subArgs, flags);
|
|
134
|
+
break;
|
|
135
|
+
case 'audit':
|
|
136
|
+
await auditTask(subArgs, flags);
|
|
137
|
+
break;
|
|
138
|
+
case 'list':
|
|
139
|
+
await listAllTasks(subArgs, flags);
|
|
140
|
+
break;
|
|
141
|
+
default:
|
|
142
|
+
if (flags.json) {
|
|
143
|
+
(0, output_js_1.outputJsonError)('tasks', subcommand, errors_js_1.ErrorCode.INVALID_ARGUMENTS, `Unknown subcommand: ${subcommand}`);
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
console.error(`Unknown subcommand: ${subcommand}`);
|
|
147
|
+
console.log(HELP);
|
|
148
|
+
}
|
|
149
|
+
process.exit((0, errors_js_1.getExitCode)(errors_js_1.ErrorCode.INVALID_ARGUMENTS));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
async function listAllTasks(args, globalFlags) {
|
|
153
|
+
// Check for help flag first (parseArgs doesn't always handle -h well)
|
|
154
|
+
if (args.includes('-h') || args.includes('--help')) {
|
|
155
|
+
console.log(HELP);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
const { values } = (0, node_util_1.parseArgs)({
|
|
159
|
+
args,
|
|
160
|
+
options: {
|
|
161
|
+
help: { type: 'boolean', short: 'h', default: false },
|
|
162
|
+
json: { type: 'boolean', short: 'j', default: false },
|
|
163
|
+
status: { type: 'string', short: 's', default: 'pending' },
|
|
164
|
+
section: { type: 'string' },
|
|
165
|
+
search: { type: 'string' },
|
|
166
|
+
global: { type: 'boolean', short: 'g', default: false },
|
|
167
|
+
},
|
|
168
|
+
allowPositionals: false,
|
|
169
|
+
});
|
|
170
|
+
if (values.help) {
|
|
171
|
+
console.log(HELP);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
// Honor global --json flag or local -j/--json flag
|
|
175
|
+
const outputJson = values.json || globalFlags?.json;
|
|
176
|
+
const statusFilter = values.status;
|
|
177
|
+
const isGlobalQuery = values.global;
|
|
178
|
+
// For --global flag, query all registered projects
|
|
179
|
+
if (isGlobalQuery) {
|
|
180
|
+
const allTasks = [];
|
|
181
|
+
const projects = (0, projects_js_1.getRegisteredProjects)(false); // enabled only
|
|
182
|
+
for (const project of projects) {
|
|
183
|
+
const dbPath = `${project.path}/.steroids/steroids.db`;
|
|
184
|
+
if (!(0, node_fs_1.existsSync)(dbPath))
|
|
185
|
+
continue;
|
|
186
|
+
try {
|
|
187
|
+
const { db, close } = (0, connection_js_1.openDatabase)(project.path);
|
|
188
|
+
try {
|
|
189
|
+
let tasks;
|
|
190
|
+
if (statusFilter === 'active') {
|
|
191
|
+
const inProgress = (0, queries_js_1.listTasks)(db, { status: 'in_progress', search: values.search });
|
|
192
|
+
const review = (0, queries_js_1.listTasks)(db, { status: 'review', search: values.search });
|
|
193
|
+
tasks = [...inProgress, ...review];
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
tasks = (0, queries_js_1.listTasks)(db, { status: statusFilter, search: values.search });
|
|
197
|
+
}
|
|
198
|
+
const projectName = project.name || (0, node_path_1.basename)(project.path);
|
|
199
|
+
for (const task of tasks) {
|
|
200
|
+
allTasks.push({
|
|
201
|
+
...task,
|
|
202
|
+
project_path: project.path,
|
|
203
|
+
project_name: projectName,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
finally {
|
|
208
|
+
close();
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
// Skip projects with inaccessible databases
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
if (outputJson) {
|
|
216
|
+
(0, output_js_1.outputJson)('tasks', 'list', {
|
|
217
|
+
tasks: allTasks,
|
|
218
|
+
total: allTasks.length,
|
|
219
|
+
filter: { status: statusFilter, global: true },
|
|
220
|
+
});
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
if (allTasks.length === 0) {
|
|
224
|
+
console.log(`No ${statusFilter} tasks found across all projects.`);
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
const statusLabel = statusFilter.toUpperCase();
|
|
228
|
+
console.log(`${statusLabel} TASKS (All Projects)`);
|
|
229
|
+
console.log('─'.repeat(100));
|
|
230
|
+
console.log('STATUS PROJECT TITLE REJ ID');
|
|
231
|
+
console.log('─'.repeat(100));
|
|
232
|
+
for (const task of allTasks) {
|
|
233
|
+
const marker = queries_js_1.STATUS_MARKERS[task.status];
|
|
234
|
+
const shortId = task.id.substring(0, 8);
|
|
235
|
+
const projectName = (task.project_name || 'unknown').substring(0, 22).padEnd(22);
|
|
236
|
+
const title = task.title.length > 40
|
|
237
|
+
? task.title.substring(0, 37) + '...'
|
|
238
|
+
: task.title.padEnd(40);
|
|
239
|
+
const rej = task.rejection_count > 0
|
|
240
|
+
? String(task.rejection_count).padStart(3)
|
|
241
|
+
: ' -';
|
|
242
|
+
console.log(`${marker} ${projectName} ${title} ${rej} ${shortId}`);
|
|
243
|
+
}
|
|
244
|
+
console.log('─'.repeat(100));
|
|
245
|
+
console.log(`Total: ${allTasks.length} active task(s) across ${projects.length} project(s)`);
|
|
246
|
+
// Multi-project warning
|
|
247
|
+
if (projects.length > 1) {
|
|
248
|
+
const currentProject = process.cwd();
|
|
249
|
+
console.log('');
|
|
250
|
+
console.log('─'.repeat(100));
|
|
251
|
+
console.log('⚠️ MULTI-PROJECT WARNING');
|
|
252
|
+
console.log(` Your current project: ${currentProject}`);
|
|
253
|
+
console.log(' DO NOT modify files in other projects. Each runner works only on its own project.');
|
|
254
|
+
console.log('─'.repeat(100));
|
|
255
|
+
}
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
// Non-global query: use current project only
|
|
259
|
+
const { db, close } = (0, connection_js_1.openDatabase)();
|
|
260
|
+
try {
|
|
261
|
+
let sectionId;
|
|
262
|
+
if (values.section) {
|
|
263
|
+
const section = (0, queries_js_1.getSection)(db, values.section);
|
|
264
|
+
if (!section) {
|
|
265
|
+
if (outputJson) {
|
|
266
|
+
(0, output_js_1.outputJsonError)('tasks', 'list', errors_js_1.ErrorCode.SECTION_NOT_FOUND, `Section not found: ${values.section}`, {
|
|
267
|
+
sectionId: values.section,
|
|
268
|
+
hint: 'Use "steroids sections list" to see available sections.',
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
272
|
+
console.error(`Section not found: ${values.section}`);
|
|
273
|
+
console.error('Use "steroids sections list" to see available sections.');
|
|
274
|
+
}
|
|
275
|
+
process.exit((0, errors_js_1.getExitCode)(errors_js_1.ErrorCode.SECTION_NOT_FOUND));
|
|
276
|
+
}
|
|
277
|
+
sectionId = section.id;
|
|
278
|
+
}
|
|
279
|
+
let tasks;
|
|
280
|
+
if (statusFilter === 'active') {
|
|
281
|
+
const inProgress = (0, queries_js_1.listTasks)(db, { status: 'in_progress', sectionId, search: values.search });
|
|
282
|
+
const review = (0, queries_js_1.listTasks)(db, { status: 'review', sectionId, search: values.search });
|
|
283
|
+
tasks = [...inProgress, ...review];
|
|
284
|
+
}
|
|
285
|
+
else {
|
|
286
|
+
tasks = (0, queries_js_1.listTasks)(db, {
|
|
287
|
+
status: statusFilter,
|
|
288
|
+
sectionId,
|
|
289
|
+
search: values.search,
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
if (outputJson) {
|
|
293
|
+
(0, output_js_1.outputJson)('tasks', 'list', {
|
|
294
|
+
tasks,
|
|
295
|
+
total: tasks.length,
|
|
296
|
+
filter: { status: statusFilter, section: sectionId, search: values.search },
|
|
297
|
+
});
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
if (tasks.length === 0) {
|
|
301
|
+
console.log(`No ${values.status} tasks found.`);
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
console.log(`TASKS (Project: ${process.cwd()})`);
|
|
305
|
+
console.log('─'.repeat(80));
|
|
306
|
+
console.log('STATUS TITLE REJ ID');
|
|
307
|
+
console.log('─'.repeat(80));
|
|
308
|
+
for (const task of tasks) {
|
|
309
|
+
const marker = queries_js_1.STATUS_MARKERS[task.status];
|
|
310
|
+
const shortId = task.id.substring(0, 8);
|
|
311
|
+
const title = task.title.length > 52
|
|
312
|
+
? task.title.substring(0, 49) + '...'
|
|
313
|
+
: task.title.padEnd(52);
|
|
314
|
+
const rej = task.rejection_count > 0
|
|
315
|
+
? String(task.rejection_count).padStart(3)
|
|
316
|
+
: ' -';
|
|
317
|
+
console.log(`${marker} ${title} ${rej} ${shortId}`);
|
|
318
|
+
}
|
|
319
|
+
console.log('─'.repeat(80));
|
|
320
|
+
console.log(`Total: ${tasks.length} tasks`);
|
|
321
|
+
}
|
|
322
|
+
finally {
|
|
323
|
+
close();
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
async function showStats(args, globalFlags) {
|
|
327
|
+
const { values } = (0, node_util_1.parseArgs)({
|
|
328
|
+
args,
|
|
329
|
+
options: {
|
|
330
|
+
help: { type: 'boolean', short: 'h', default: false },
|
|
331
|
+
json: { type: 'boolean', short: 'j', default: false },
|
|
332
|
+
},
|
|
333
|
+
allowPositionals: false,
|
|
334
|
+
});
|
|
335
|
+
if (values.help) {
|
|
336
|
+
console.log(`
|
|
337
|
+
steroids tasks stats - Show task counts by status
|
|
338
|
+
|
|
339
|
+
USAGE:
|
|
340
|
+
steroids tasks stats [options]
|
|
341
|
+
|
|
342
|
+
OPTIONS:
|
|
343
|
+
-j, --json Output as JSON
|
|
344
|
+
-h, --help Show help
|
|
345
|
+
|
|
346
|
+
EXAMPLE:
|
|
347
|
+
steroids tasks stats
|
|
348
|
+
`);
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
const outputJson = values.json || globalFlags?.json;
|
|
352
|
+
const { db, close } = (0, connection_js_1.openDatabase)();
|
|
353
|
+
try {
|
|
354
|
+
const statuses = [
|
|
355
|
+
'pending',
|
|
356
|
+
'in_progress',
|
|
357
|
+
'review',
|
|
358
|
+
'completed',
|
|
359
|
+
'disputed',
|
|
360
|
+
'failed',
|
|
361
|
+
'skipped',
|
|
362
|
+
'partial',
|
|
363
|
+
];
|
|
364
|
+
const counts = {};
|
|
365
|
+
let total = 0;
|
|
366
|
+
for (const status of statuses) {
|
|
367
|
+
const tasks = (0, queries_js_1.listTasks)(db, { status });
|
|
368
|
+
counts[status] = tasks.length;
|
|
369
|
+
total += tasks.length;
|
|
370
|
+
}
|
|
371
|
+
if (outputJson) {
|
|
372
|
+
(0, output_js_1.outputJson)('tasks', 'stats', { counts, total });
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
console.log('TASK STATS');
|
|
376
|
+
console.log('─'.repeat(30));
|
|
377
|
+
for (const status of statuses) {
|
|
378
|
+
const count = counts[status];
|
|
379
|
+
const marker = queries_js_1.STATUS_MARKERS[status];
|
|
380
|
+
console.log(` ${marker} ${status.padEnd(12)}: ${count}`);
|
|
381
|
+
}
|
|
382
|
+
console.log('─'.repeat(30));
|
|
383
|
+
console.log(` Total: ${total}`);
|
|
384
|
+
}
|
|
385
|
+
finally {
|
|
386
|
+
close();
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
async function showTask(args, flags) {
|
|
390
|
+
const out = (0, output_js_1.createOutput)({ command: 'tasks', subcommand: 'show', flags });
|
|
391
|
+
const { values, positionals } = (0, node_util_1.parseArgs)({
|
|
392
|
+
args,
|
|
393
|
+
options: {
|
|
394
|
+
help: { type: 'boolean', short: 'h', default: false },
|
|
395
|
+
logs: { type: 'boolean', default: false },
|
|
396
|
+
'logs-full': { type: 'boolean', default: false },
|
|
397
|
+
limit: { type: 'string', default: '5' },
|
|
398
|
+
},
|
|
399
|
+
allowPositionals: true,
|
|
400
|
+
});
|
|
401
|
+
if (values.help || flags.help || positionals.length === 0) {
|
|
402
|
+
out.log(`
|
|
403
|
+
steroids tasks show <id|title> - Show task details with invocation logs
|
|
404
|
+
|
|
405
|
+
USAGE:
|
|
406
|
+
steroids tasks show <id|title> [options]
|
|
407
|
+
|
|
408
|
+
OPTIONS:
|
|
409
|
+
--logs Show LLM invocation history (prompts/responses)
|
|
410
|
+
--logs-full Show full prompts and responses (verbose)
|
|
411
|
+
--limit <n> Limit number of invocations shown (default: 5)
|
|
412
|
+
-h, --help Show help
|
|
413
|
+
|
|
414
|
+
DESCRIPTION:
|
|
415
|
+
Shows detailed information about a task including:
|
|
416
|
+
- Task metadata (ID, title, status, section, spec file)
|
|
417
|
+
- Rejection count and history
|
|
418
|
+
- LLM invocation logs (with --logs flag)
|
|
419
|
+
|
|
420
|
+
EXAMPLES:
|
|
421
|
+
steroids tasks show abc123 # Basic task info
|
|
422
|
+
steroids tasks show abc123 --logs # Include LLM invocation summary
|
|
423
|
+
steroids tasks show abc123 --logs-full # Full prompts and responses
|
|
424
|
+
steroids tasks show abc123 --logs --limit 10 # Show last 10 invocations
|
|
425
|
+
`);
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
const identifier = positionals[0];
|
|
429
|
+
const showLogs = values.logs || values['logs-full'];
|
|
430
|
+
const showFullLogs = values['logs-full'];
|
|
431
|
+
const limit = parseInt(values.limit, 10) || 5;
|
|
432
|
+
const { db, close } = (0, connection_js_1.openDatabase)();
|
|
433
|
+
try {
|
|
434
|
+
let task = (0, queries_js_1.getTask)(db, identifier);
|
|
435
|
+
if (!task) {
|
|
436
|
+
task = (0, queries_js_1.getTaskByTitle)(db, identifier);
|
|
437
|
+
}
|
|
438
|
+
if (!task) {
|
|
439
|
+
out.error(errors_js_1.ErrorCode.TASK_NOT_FOUND, `Task not found: ${identifier}`, { identifier });
|
|
440
|
+
process.exit((0, errors_js_1.getExitCode)(errors_js_1.ErrorCode.TASK_NOT_FOUND));
|
|
441
|
+
}
|
|
442
|
+
// Get section info
|
|
443
|
+
const section = task.section_id ? (0, queries_js_1.getSection)(db, task.section_id) : null;
|
|
444
|
+
// Get invocation counts
|
|
445
|
+
const invocationCounts = (0, queries_js_1.getInvocationCount)(db, task.id);
|
|
446
|
+
// Get invocations if requested
|
|
447
|
+
let invocations = [];
|
|
448
|
+
if (showLogs) {
|
|
449
|
+
invocations = (0, queries_js_1.getTaskInvocations)(db, task.id).slice(-limit);
|
|
450
|
+
}
|
|
451
|
+
// Get audit trail for rejection history
|
|
452
|
+
const auditEntries = (0, queries_js_1.getTaskAudit)(db, task.id);
|
|
453
|
+
if (flags.json) {
|
|
454
|
+
out.success({
|
|
455
|
+
task,
|
|
456
|
+
section: section ? { id: section.id, name: section.name } : null,
|
|
457
|
+
invocationCounts,
|
|
458
|
+
invocations: showLogs ? invocations : undefined,
|
|
459
|
+
auditTrail: auditEntries,
|
|
460
|
+
});
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
// Display task details
|
|
464
|
+
console.log('─'.repeat(80));
|
|
465
|
+
console.log('TASK DETAILS');
|
|
466
|
+
console.log('─'.repeat(80));
|
|
467
|
+
console.log(`ID: ${task.id}`);
|
|
468
|
+
console.log(`Title: ${task.title}`);
|
|
469
|
+
console.log(`Status: ${queries_js_1.STATUS_MARKERS[task.status]} ${task.status}`);
|
|
470
|
+
console.log(`Section: ${section ? section.name : '(none)'}`);
|
|
471
|
+
console.log(`Spec File: ${task.source_file ?? '(not set)'}`);
|
|
472
|
+
if (task.file_path) {
|
|
473
|
+
const lineStr = task.file_line ? `:${task.file_line}` : '';
|
|
474
|
+
console.log(`File Anchor: ${task.file_path}${lineStr}`);
|
|
475
|
+
console.log(`File Commit: ${task.file_commit_sha?.substring(0, 7) ?? '(unknown)'}`);
|
|
476
|
+
console.log(`Content Hash: ${task.file_content_hash?.substring(0, 12) ?? '(unknown)'}`);
|
|
477
|
+
}
|
|
478
|
+
console.log(`Rejections: ${task.rejection_count}/15`);
|
|
479
|
+
console.log(`Created: ${task.created_at}`);
|
|
480
|
+
console.log(`Updated: ${task.updated_at}`);
|
|
481
|
+
console.log('');
|
|
482
|
+
console.log(`Invocations: ${invocationCounts.coder} coder, ${invocationCounts.reviewer} reviewer (${invocationCounts.total} total)`);
|
|
483
|
+
console.log('─'.repeat(80));
|
|
484
|
+
if (showLogs && invocations.length > 0) {
|
|
485
|
+
console.log('');
|
|
486
|
+
console.log('LLM INVOCATIONS (most recent)');
|
|
487
|
+
console.log('─'.repeat(80));
|
|
488
|
+
for (const inv of invocations) {
|
|
489
|
+
const ts = inv.created_at.substring(0, 19).replace('T', ' ');
|
|
490
|
+
const status = inv.success ? 'OK' : (inv.timed_out ? 'TIMEOUT' : 'FAIL');
|
|
491
|
+
const duration = (inv.duration_ms / 1000).toFixed(1) + 's';
|
|
492
|
+
const rejNum = inv.rejection_number ? ` (rejection #${inv.rejection_number})` : '';
|
|
493
|
+
console.log(`\n[${ts}] ${inv.role.toUpperCase()} - ${inv.provider}/${inv.model} - ${status} - ${duration}${rejNum}`);
|
|
494
|
+
if (showFullLogs) {
|
|
495
|
+
console.log('\n--- PROMPT ---');
|
|
496
|
+
console.log(inv.prompt.substring(0, 5000) + (inv.prompt.length > 5000 ? '\n\n[...truncated...]' : ''));
|
|
497
|
+
console.log('\n--- RESPONSE ---');
|
|
498
|
+
const response = inv.response || '(no response)';
|
|
499
|
+
console.log(response.substring(0, 3000) + (response.length > 3000 ? '\n\n[...truncated...]' : ''));
|
|
500
|
+
if (inv.error) {
|
|
501
|
+
console.log('\n--- ERROR ---');
|
|
502
|
+
console.log(inv.error.substring(0, 1000));
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
else {
|
|
506
|
+
// Summary view
|
|
507
|
+
const promptLines = inv.prompt.split('\n').length;
|
|
508
|
+
const responseLines = (inv.response || '').split('\n').length;
|
|
509
|
+
console.log(` Prompt: ${promptLines} lines, ${inv.prompt.length} chars`);
|
|
510
|
+
console.log(` Response: ${responseLines} lines, ${(inv.response || '').length} chars`);
|
|
511
|
+
if (inv.error) {
|
|
512
|
+
console.log(` Error: ${inv.error.substring(0, 100)}...`);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
console.log('');
|
|
517
|
+
console.log('─'.repeat(80));
|
|
518
|
+
console.log(`Tip: Use --logs-full to see complete prompts and responses`);
|
|
519
|
+
}
|
|
520
|
+
else if (showLogs && invocations.length === 0) {
|
|
521
|
+
console.log('');
|
|
522
|
+
console.log('No invocations recorded for this task yet.');
|
|
523
|
+
console.log('Invocations are recorded when coder/reviewer processes run.');
|
|
524
|
+
}
|
|
525
|
+
if (!showLogs && invocationCounts.total > 0) {
|
|
526
|
+
console.log('');
|
|
527
|
+
console.log(`Tip: Use --logs to see invocation history, --logs-full for full prompts`);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
finally {
|
|
531
|
+
close();
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
async function addTask(args, flags) {
|
|
535
|
+
const out = (0, output_js_1.createOutput)({ command: 'tasks', subcommand: 'add', flags });
|
|
536
|
+
const { values, positionals } = (0, node_util_1.parseArgs)({
|
|
537
|
+
args,
|
|
538
|
+
options: {
|
|
539
|
+
help: { type: 'boolean', short: 'h', default: false },
|
|
540
|
+
section: { type: 'string' },
|
|
541
|
+
source: { type: 'string' },
|
|
542
|
+
file: { type: 'string' },
|
|
543
|
+
line: { type: 'string' },
|
|
544
|
+
feedback: { type: 'boolean', default: false },
|
|
545
|
+
},
|
|
546
|
+
allowPositionals: true,
|
|
547
|
+
});
|
|
548
|
+
if (values.help || flags.help) {
|
|
549
|
+
out.log(`
|
|
550
|
+
steroids tasks add <title> - Add a new task
|
|
551
|
+
|
|
552
|
+
USAGE:
|
|
553
|
+
steroids tasks add <title> --section <id> --source <file> [options]
|
|
554
|
+
steroids tasks add <title> --feedback [--file <path> --line <n>]
|
|
555
|
+
|
|
556
|
+
OPTIONS:
|
|
557
|
+
--section <id> Section ID (required unless --feedback)
|
|
558
|
+
--source <file> Specification file (required unless --feedback)
|
|
559
|
+
--feedback Add to "Needs User Input" section (skipped, for human review)
|
|
560
|
+
--file <path> Anchor task to a committed file
|
|
561
|
+
--line <number> Line number in the anchored file (requires --file)
|
|
562
|
+
-h, --help Show help
|
|
563
|
+
|
|
564
|
+
EXAMPLES:
|
|
565
|
+
steroids tasks add "Implement login" --section abc123 --source docs/login-spec.md
|
|
566
|
+
steroids tasks add "Fix null check" --section abc123 --source spec.md --file src/utils.ts --line 42
|
|
567
|
+
steroids tasks add "Pre-existing execSync in queries.ts needs review" --feedback
|
|
568
|
+
steroids tasks add "Should we use Redis or in-memory cache?" --feedback
|
|
569
|
+
`);
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
if (positionals.length === 0) {
|
|
573
|
+
out.error(errors_js_1.ErrorCode.INVALID_ARGUMENTS, 'task title required', {
|
|
574
|
+
usage: 'steroids tasks add <title> --section <id> --source <file>',
|
|
575
|
+
});
|
|
576
|
+
process.exit(2);
|
|
577
|
+
}
|
|
578
|
+
if (!values.feedback && !values.section) {
|
|
579
|
+
out.error(errors_js_1.ErrorCode.INVALID_ARGUMENTS, '--section <id> is required (or use --feedback)', {
|
|
580
|
+
usage: 'steroids tasks add <title> --section <id> --source <file>',
|
|
581
|
+
hint: 'Every task must belong to a section. Use --feedback for advisory items.',
|
|
582
|
+
});
|
|
583
|
+
process.exit(2);
|
|
584
|
+
}
|
|
585
|
+
if (!values.feedback && !values.source) {
|
|
586
|
+
out.error(errors_js_1.ErrorCode.INVALID_ARGUMENTS, '--source <file> is required (or use --feedback)', {
|
|
587
|
+
usage: 'steroids tasks add <title> --section <id> --source <file>',
|
|
588
|
+
hint: 'Every task must reference a specification file. Use --feedback for advisory items.',
|
|
589
|
+
});
|
|
590
|
+
process.exit(2);
|
|
591
|
+
}
|
|
592
|
+
// Validate --line requires --file
|
|
593
|
+
if (values.line && !values.file) {
|
|
594
|
+
out.error(errors_js_1.ErrorCode.INVALID_ARGUMENTS, '--line requires --file', {
|
|
595
|
+
usage: 'steroids tasks add <title> --file <path> --line <number>',
|
|
596
|
+
hint: 'You cannot specify a line number without a file.',
|
|
597
|
+
});
|
|
598
|
+
process.exit(2);
|
|
599
|
+
}
|
|
600
|
+
// Parse and validate line number
|
|
601
|
+
let fileLine;
|
|
602
|
+
if (values.line) {
|
|
603
|
+
fileLine = parseInt(values.line, 10);
|
|
604
|
+
if (isNaN(fileLine) || fileLine < 1) {
|
|
605
|
+
out.error(errors_js_1.ErrorCode.INVALID_ARGUMENTS, '--line must be a positive integer', {
|
|
606
|
+
provided: values.line,
|
|
607
|
+
});
|
|
608
|
+
process.exit(2);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
// Resolve file anchor if --file is provided
|
|
612
|
+
let filePath;
|
|
613
|
+
let fileCommitSha;
|
|
614
|
+
let fileContentHash;
|
|
615
|
+
if (values.file) {
|
|
616
|
+
const fullPath = (0, node_path_1.resolve)(process.cwd(), values.file);
|
|
617
|
+
const normalizedPath = (0, node_path_1.relative)(process.cwd(), fullPath);
|
|
618
|
+
if (normalizedPath.startsWith('..')) {
|
|
619
|
+
out.error(errors_js_1.ErrorCode.INVALID_ARGUMENTS, `File is outside the project directory: ${values.file}`, {
|
|
620
|
+
file: values.file,
|
|
621
|
+
hint: 'The file must be within the project directory.',
|
|
622
|
+
});
|
|
623
|
+
process.exit(2);
|
|
624
|
+
}
|
|
625
|
+
if (!(0, node_fs_1.existsSync)(fullPath)) {
|
|
626
|
+
out.error(errors_js_1.ErrorCode.INVALID_ARGUMENTS, `File not found: ${normalizedPath}`, {
|
|
627
|
+
file: normalizedPath,
|
|
628
|
+
hint: 'The file must exist in the project directory.',
|
|
629
|
+
});
|
|
630
|
+
process.exit(2);
|
|
631
|
+
}
|
|
632
|
+
if (!(0, status_js_1.isFileTracked)(normalizedPath)) {
|
|
633
|
+
out.error(errors_js_1.ErrorCode.INVALID_ARGUMENTS, `File is not tracked by git: ${normalizedPath}`, {
|
|
634
|
+
file: normalizedPath,
|
|
635
|
+
hint: 'The file must be committed to the repository. Run: git add && git commit',
|
|
636
|
+
});
|
|
637
|
+
process.exit(2);
|
|
638
|
+
}
|
|
639
|
+
if ((0, status_js_1.isFileDirty)(normalizedPath)) {
|
|
640
|
+
out.error(errors_js_1.ErrorCode.INVALID_ARGUMENTS, `File has uncommitted changes: ${normalizedPath}`, {
|
|
641
|
+
file: normalizedPath,
|
|
642
|
+
hint: 'Commit your changes before anchoring a task to this file.',
|
|
643
|
+
});
|
|
644
|
+
process.exit(2);
|
|
645
|
+
}
|
|
646
|
+
filePath = normalizedPath;
|
|
647
|
+
fileCommitSha = (0, status_js_1.getFileLastCommit)(normalizedPath) ?? undefined;
|
|
648
|
+
fileContentHash = (0, status_js_1.getFileContentHash)(normalizedPath) ?? undefined;
|
|
649
|
+
}
|
|
650
|
+
const title = positionals.join(' ');
|
|
651
|
+
const { db, close } = (0, connection_js_1.openDatabase)();
|
|
652
|
+
try {
|
|
653
|
+
let section;
|
|
654
|
+
if (values.feedback) {
|
|
655
|
+
section = (0, queries_js_1.getOrCreateFeedbackSection)(db);
|
|
656
|
+
}
|
|
657
|
+
else {
|
|
658
|
+
section = (0, queries_js_1.getSection)(db, values.section);
|
|
659
|
+
if (!section) {
|
|
660
|
+
out.error(errors_js_1.ErrorCode.SECTION_NOT_FOUND, `Section not found: ${values.section}`, {
|
|
661
|
+
sectionId: values.section,
|
|
662
|
+
hint: 'Use "steroids sections list" to see available sections.',
|
|
663
|
+
});
|
|
664
|
+
process.exit(1);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
const task = (0, queries_js_1.createTask)(db, title, {
|
|
668
|
+
sectionId: section.id,
|
|
669
|
+
sourceFile: values.source ?? undefined,
|
|
670
|
+
filePath,
|
|
671
|
+
fileLine,
|
|
672
|
+
fileCommitSha,
|
|
673
|
+
fileContentHash,
|
|
674
|
+
});
|
|
675
|
+
// Trigger task.created hooks
|
|
676
|
+
if (!(0, integration_js_1.shouldSkipHooks)(flags)) {
|
|
677
|
+
await (0, integration_js_1.triggerHooksSafely)(() => (0, integration_js_1.triggerTaskCreated)(task, { verbose: flags.verbose }), { verbose: flags.verbose });
|
|
678
|
+
}
|
|
679
|
+
out.success({ task, feedback: !!values.feedback });
|
|
680
|
+
if (!flags.json) {
|
|
681
|
+
out.log(`Task created: ${task.title}`);
|
|
682
|
+
out.log(` ID: ${task.id}`);
|
|
683
|
+
out.log(` Status: ${task.status}`);
|
|
684
|
+
if (values.feedback) {
|
|
685
|
+
out.log(` Section: Needs User Input (skipped - for human review)`);
|
|
686
|
+
}
|
|
687
|
+
if (task.source_file) {
|
|
688
|
+
out.log(` Source: ${task.source_file}`);
|
|
689
|
+
}
|
|
690
|
+
if (task.file_path) {
|
|
691
|
+
const lineStr = task.file_line ? `:${task.file_line}` : '';
|
|
692
|
+
out.log(` File: ${task.file_path}${lineStr}`);
|
|
693
|
+
out.log(` Commit: ${task.file_commit_sha?.substring(0, 7) ?? 'unknown'}`);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
finally {
|
|
698
|
+
close();
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
async function updateTask(args, flags) {
|
|
702
|
+
const out = (0, output_js_1.createOutput)({ command: 'tasks', subcommand: 'update', flags });
|
|
703
|
+
const { values, positionals } = (0, node_util_1.parseArgs)({
|
|
704
|
+
args,
|
|
705
|
+
options: {
|
|
706
|
+
help: { type: 'boolean', short: 'h', default: false },
|
|
707
|
+
status: { type: 'string' },
|
|
708
|
+
actor: { type: 'string', default: 'human:cli' },
|
|
709
|
+
model: { type: 'string' },
|
|
710
|
+
notes: { type: 'string' },
|
|
711
|
+
'reset-rejections': { type: 'boolean', default: false },
|
|
712
|
+
},
|
|
713
|
+
allowPositionals: true,
|
|
714
|
+
});
|
|
715
|
+
if (values.help || flags.help || positionals.length === 0) {
|
|
716
|
+
out.log(`
|
|
717
|
+
steroids tasks update <title|id> - Update task status
|
|
718
|
+
|
|
719
|
+
USAGE:
|
|
720
|
+
steroids tasks update <title|id> [options]
|
|
721
|
+
|
|
722
|
+
OPTIONS:
|
|
723
|
+
--status <status> New status: pending | in_progress | review | completed
|
|
724
|
+
--reset-rejections Reset rejection count to 0 (keeps audit history)
|
|
725
|
+
--actor <actor> Actor making the change (default: human:cli)
|
|
726
|
+
--model <model> Model identifier (for LLM actors)
|
|
727
|
+
--notes <text> Notes for the reviewer (useful when submitting for review)
|
|
728
|
+
-h, --help Show help
|
|
729
|
+
|
|
730
|
+
EXAMPLES:
|
|
731
|
+
steroids tasks update abc123 --status review
|
|
732
|
+
steroids tasks update abc123 --status pending --reset-rejections
|
|
733
|
+
steroids tasks update abc123 --status review --notes "Found existing implementation at commit xyz"
|
|
734
|
+
`);
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
if (!values.status && !values['reset-rejections']) {
|
|
738
|
+
if (flags.json) {
|
|
739
|
+
out.error(errors_js_1.ErrorCode.INVALID_ARGUMENTS, '--status or --reset-rejections required');
|
|
740
|
+
}
|
|
741
|
+
else {
|
|
742
|
+
console.error('Error: --status or --reset-rejections required');
|
|
743
|
+
}
|
|
744
|
+
process.exit(1);
|
|
745
|
+
}
|
|
746
|
+
const identifier = positionals.join(' ');
|
|
747
|
+
const { db, close } = (0, connection_js_1.openDatabase)();
|
|
748
|
+
try {
|
|
749
|
+
// Try to find task by ID or title
|
|
750
|
+
let task = (0, queries_js_1.getTask)(db, identifier);
|
|
751
|
+
if (!task) {
|
|
752
|
+
task = (0, queries_js_1.getTaskByTitle)(db, identifier);
|
|
753
|
+
}
|
|
754
|
+
if (!task) {
|
|
755
|
+
if (flags.json) {
|
|
756
|
+
out.error(errors_js_1.ErrorCode.TASK_NOT_FOUND, `Task not found: ${identifier}`, { identifier });
|
|
757
|
+
}
|
|
758
|
+
else {
|
|
759
|
+
console.error(`Task not found: ${identifier}`);
|
|
760
|
+
}
|
|
761
|
+
process.exit((0, errors_js_1.getExitCode)(errors_js_1.ErrorCode.TASK_NOT_FOUND));
|
|
762
|
+
}
|
|
763
|
+
const actor = values.model
|
|
764
|
+
? `model:${values.model}`
|
|
765
|
+
: values.actor ?? 'human:cli';
|
|
766
|
+
let oldRejectionCount;
|
|
767
|
+
// Reset rejections if requested
|
|
768
|
+
if (values['reset-rejections']) {
|
|
769
|
+
oldRejectionCount = (0, queries_js_1.resetRejectionCount)(db, task.id, actor, values.notes);
|
|
770
|
+
}
|
|
771
|
+
// Update status if provided
|
|
772
|
+
const previousStatus = task.status;
|
|
773
|
+
if (values.status) {
|
|
774
|
+
(0, queries_js_1.updateTaskStatus)(db, task.id, values.status, actor, values.notes);
|
|
775
|
+
}
|
|
776
|
+
const updated = (0, queries_js_1.getTask)(db, task.id);
|
|
777
|
+
// Trigger task.updated hooks
|
|
778
|
+
if (!(0, integration_js_1.shouldSkipHooks)(flags) && updated) {
|
|
779
|
+
await (0, integration_js_1.triggerHooksSafely)(() => (0, integration_js_1.triggerTaskUpdated)(updated, previousStatus, { verbose: flags.verbose }), { verbose: flags.verbose });
|
|
780
|
+
}
|
|
781
|
+
if (flags.json) {
|
|
782
|
+
out.success({ task: updated, rejectionReset: oldRejectionCount !== undefined, oldRejectionCount });
|
|
783
|
+
}
|
|
784
|
+
else {
|
|
785
|
+
console.log(`Task updated: ${task.title}`);
|
|
786
|
+
if (values.status) {
|
|
787
|
+
console.log(` Status: ${task.status} → ${values.status}`);
|
|
788
|
+
}
|
|
789
|
+
if (oldRejectionCount !== undefined) {
|
|
790
|
+
console.log(` Rejections: ${oldRejectionCount} → 0 (reset)`);
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
finally {
|
|
795
|
+
close();
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
/**
|
|
799
|
+
* Check if a section is completed after a task is marked complete
|
|
800
|
+
*/
|
|
801
|
+
async function checkSectionCompletion(db, sectionId, flags) {
|
|
802
|
+
if (!sectionId || (0, integration_js_1.shouldSkipHooks)(flags)) {
|
|
803
|
+
return;
|
|
804
|
+
}
|
|
805
|
+
// Get section info
|
|
806
|
+
const section = (0, queries_js_1.getSection)(db, sectionId);
|
|
807
|
+
if (!section) {
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
810
|
+
// Check if all tasks in section are completed
|
|
811
|
+
const sectionTasks = (0, queries_js_1.listTasks)(db, { sectionId });
|
|
812
|
+
const allCompleted = sectionTasks.every((t) => t.status === 'completed');
|
|
813
|
+
if (allCompleted && sectionTasks.length > 0) {
|
|
814
|
+
// Trigger section.completed hooks
|
|
815
|
+
await (0, integration_js_1.triggerHooksSafely)(() => (0, integration_js_1.triggerSectionCompleted)({
|
|
816
|
+
id: section.id,
|
|
817
|
+
name: section.name,
|
|
818
|
+
taskCount: sectionTasks.length,
|
|
819
|
+
}, sectionTasks.map((t) => ({ id: t.id, title: t.title })), { verbose: flags.verbose }), { verbose: flags.verbose });
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
/**
|
|
823
|
+
* Check if the entire project is completed after a task is marked complete
|
|
824
|
+
*/
|
|
825
|
+
async function checkProjectCompletion(db, flags) {
|
|
826
|
+
if ((0, integration_js_1.shouldSkipHooks)(flags)) {
|
|
827
|
+
return;
|
|
828
|
+
}
|
|
829
|
+
// Get all tasks
|
|
830
|
+
const allTasks = (0, queries_js_1.listTasks)(db, { status: 'all' });
|
|
831
|
+
// Check if all tasks are completed
|
|
832
|
+
const allCompleted = allTasks.every((t) => t.status === 'completed');
|
|
833
|
+
if (allCompleted && allTasks.length > 0) {
|
|
834
|
+
// Get sections
|
|
835
|
+
const sections = (0, queries_js_1.listSections)(db);
|
|
836
|
+
// Get unique source files
|
|
837
|
+
const files = Array.from(new Set(allTasks.map((t) => t.source_file).filter(Boolean)));
|
|
838
|
+
// Trigger project.completed hooks
|
|
839
|
+
await (0, integration_js_1.triggerHooksSafely)(() => (0, integration_js_1.triggerProjectCompleted)({
|
|
840
|
+
totalTasks: allTasks.length,
|
|
841
|
+
files,
|
|
842
|
+
sectionCount: sections.length,
|
|
843
|
+
}, { verbose: flags.verbose }), { verbose: flags.verbose });
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
async function approveTaskCmd(args, flags) {
|
|
847
|
+
const out = (0, output_js_1.createOutput)({ command: 'tasks', subcommand: 'approve', flags });
|
|
848
|
+
const { values, positionals } = (0, node_util_1.parseArgs)({
|
|
849
|
+
args,
|
|
850
|
+
options: {
|
|
851
|
+
help: { type: 'boolean', short: 'h', default: false },
|
|
852
|
+
model: { type: 'string' },
|
|
853
|
+
notes: { type: 'string' },
|
|
854
|
+
},
|
|
855
|
+
allowPositionals: true,
|
|
856
|
+
});
|
|
857
|
+
if (values.help || flags.help || positionals.length === 0) {
|
|
858
|
+
out.log(`
|
|
859
|
+
steroids tasks approve <id> - Approve a task
|
|
860
|
+
|
|
861
|
+
USAGE:
|
|
862
|
+
steroids tasks approve <id> [options]
|
|
863
|
+
|
|
864
|
+
OPTIONS:
|
|
865
|
+
--model <model> Model performing the review (required)
|
|
866
|
+
--notes <text> Approval notes
|
|
867
|
+
-h, --help Show help
|
|
868
|
+
`);
|
|
869
|
+
return;
|
|
870
|
+
}
|
|
871
|
+
if (!values.model) {
|
|
872
|
+
out.error(errors_js_1.ErrorCode.INVALID_ARGUMENTS, '--model required');
|
|
873
|
+
process.exit(1);
|
|
874
|
+
}
|
|
875
|
+
const identifier = positionals[0];
|
|
876
|
+
const { db, close } = (0, connection_js_1.openDatabase)();
|
|
877
|
+
try {
|
|
878
|
+
let task = (0, queries_js_1.getTask)(db, identifier);
|
|
879
|
+
if (!task) {
|
|
880
|
+
task = (0, queries_js_1.getTaskByTitle)(db, identifier);
|
|
881
|
+
}
|
|
882
|
+
if (!task) {
|
|
883
|
+
out.error(errors_js_1.ErrorCode.TASK_NOT_FOUND, `Task not found: ${identifier}`, { identifier });
|
|
884
|
+
process.exit((0, errors_js_1.getExitCode)(errors_js_1.ErrorCode.TASK_NOT_FOUND));
|
|
885
|
+
}
|
|
886
|
+
(0, queries_js_1.approveTask)(db, task.id, values.model, values.notes);
|
|
887
|
+
const updated = (0, queries_js_1.getTask)(db, task.id);
|
|
888
|
+
// Trigger task.completed hooks
|
|
889
|
+
if (!(0, integration_js_1.shouldSkipHooks)(flags) && updated) {
|
|
890
|
+
await (0, integration_js_1.triggerHooksSafely)(() => (0, integration_js_1.triggerTaskCompleted)(updated, { verbose: flags.verbose }), { verbose: flags.verbose });
|
|
891
|
+
// Check if section is now complete
|
|
892
|
+
await checkSectionCompletion(db, updated.section_id, flags);
|
|
893
|
+
// Check if entire project is now complete
|
|
894
|
+
await checkProjectCompletion(db, flags);
|
|
895
|
+
}
|
|
896
|
+
out.success({ task: updated });
|
|
897
|
+
if (!flags.json) {
|
|
898
|
+
out.log(`Task approved: ${task.title}`);
|
|
899
|
+
out.log(` Status: completed`);
|
|
900
|
+
out.log(` Reviewer: ${values.model}`);
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
finally {
|
|
904
|
+
close();
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
async function rejectTaskCmd(args, flags) {
|
|
908
|
+
const out = (0, output_js_1.createOutput)({ command: 'tasks', subcommand: 'reject', flags });
|
|
909
|
+
const { values, positionals } = (0, node_util_1.parseArgs)({
|
|
910
|
+
args,
|
|
911
|
+
options: {
|
|
912
|
+
help: { type: 'boolean', short: 'h', default: false },
|
|
913
|
+
model: { type: 'string' },
|
|
914
|
+
notes: { type: 'string' },
|
|
915
|
+
},
|
|
916
|
+
allowPositionals: true,
|
|
917
|
+
});
|
|
918
|
+
if (values.help || flags.help || positionals.length === 0) {
|
|
919
|
+
out.log(`
|
|
920
|
+
steroids tasks reject <id> - Reject a task
|
|
921
|
+
|
|
922
|
+
USAGE:
|
|
923
|
+
steroids tasks reject <id> [options]
|
|
924
|
+
|
|
925
|
+
OPTIONS:
|
|
926
|
+
--model <model> Model performing the review (required)
|
|
927
|
+
--notes <text> Rejection reason (important for coder)
|
|
928
|
+
-h, --help Show help
|
|
929
|
+
`);
|
|
930
|
+
return;
|
|
931
|
+
}
|
|
932
|
+
if (!values.model) {
|
|
933
|
+
out.error(errors_js_1.ErrorCode.INVALID_ARGUMENTS, '--model required');
|
|
934
|
+
process.exit(1);
|
|
935
|
+
}
|
|
936
|
+
const identifier = positionals[0];
|
|
937
|
+
const { db, close } = (0, connection_js_1.openDatabase)();
|
|
938
|
+
try {
|
|
939
|
+
let task = (0, queries_js_1.getTask)(db, identifier);
|
|
940
|
+
if (!task) {
|
|
941
|
+
task = (0, queries_js_1.getTaskByTitle)(db, identifier);
|
|
942
|
+
}
|
|
943
|
+
if (!task) {
|
|
944
|
+
out.error(errors_js_1.ErrorCode.TASK_NOT_FOUND, `Task not found: ${identifier}`, { identifier });
|
|
945
|
+
process.exit((0, errors_js_1.getExitCode)(errors_js_1.ErrorCode.TASK_NOT_FOUND));
|
|
946
|
+
}
|
|
947
|
+
const result = (0, queries_js_1.rejectTask)(db, task.id, values.model, values.notes);
|
|
948
|
+
// Trigger task.failed hooks if task failed
|
|
949
|
+
if (result.status === 'failed') {
|
|
950
|
+
const failedTask = (0, queries_js_1.getTask)(db, task.id);
|
|
951
|
+
if (!(0, integration_js_1.shouldSkipHooks)(flags) && failedTask) {
|
|
952
|
+
await (0, integration_js_1.triggerHooksSafely)(() => (0, integration_js_1.triggerTaskFailed)(failedTask, 15, { verbose: flags.verbose }), { verbose: flags.verbose });
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
out.success({ task, result });
|
|
956
|
+
if (!flags.json) {
|
|
957
|
+
if (result.status === 'failed') {
|
|
958
|
+
out.log(`Task FAILED: ${task.title}`);
|
|
959
|
+
out.log(` Exceeded 15 rejections. Requires human intervention.`);
|
|
960
|
+
}
|
|
961
|
+
else {
|
|
962
|
+
out.log(`Task rejected: ${task.title}`);
|
|
963
|
+
out.log(` Status: in_progress (rejection ${result.rejectionCount}/15)`);
|
|
964
|
+
out.log(` Reviewer: ${values.model}`);
|
|
965
|
+
if (values.notes) {
|
|
966
|
+
out.log(` Notes: ${values.notes}`);
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
finally {
|
|
972
|
+
close();
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
async function skipTaskCmd(args, flags) {
|
|
976
|
+
const out = (0, output_js_1.createOutput)({ command: 'tasks', subcommand: 'skip', flags });
|
|
977
|
+
const { values, positionals } = (0, node_util_1.parseArgs)({
|
|
978
|
+
args,
|
|
979
|
+
options: {
|
|
980
|
+
help: { type: 'boolean', short: 'h', default: false },
|
|
981
|
+
model: { type: 'string' },
|
|
982
|
+
notes: { type: 'string' },
|
|
983
|
+
partial: { type: 'boolean', default: false },
|
|
984
|
+
},
|
|
985
|
+
allowPositionals: true,
|
|
986
|
+
});
|
|
987
|
+
if (values.help || flags.help || positionals.length === 0) {
|
|
988
|
+
out.log(`
|
|
989
|
+
steroids tasks skip <id> - Skip a task (external setup required)
|
|
990
|
+
|
|
991
|
+
USAGE:
|
|
992
|
+
steroids tasks skip <id> [options]
|
|
993
|
+
|
|
994
|
+
OPTIONS:
|
|
995
|
+
--model <model> Model identifying the skip (required for LLM actors)
|
|
996
|
+
--notes <text> Reason for skipping (e.g., "Cloud SQL - marked SKIP in spec")
|
|
997
|
+
--partial Mark as partial (coded what we could, rest is external)
|
|
998
|
+
-h, --help Show help
|
|
999
|
+
|
|
1000
|
+
DESCRIPTION:
|
|
1001
|
+
Use this when a task requires external/manual setup that cannot be automated:
|
|
1002
|
+
- Cloud infrastructure (Cloud SQL, GKE, etc.)
|
|
1003
|
+
- Manual account creation
|
|
1004
|
+
- License procurement
|
|
1005
|
+
- Hardware setup
|
|
1006
|
+
|
|
1007
|
+
The task spec should indicate "SKIP" or "MANUAL" for these tasks.
|
|
1008
|
+
|
|
1009
|
+
--partial: Use when you've implemented what you can, but the rest requires
|
|
1010
|
+
external action (e.g., created deployment YAML but can't provision).
|
|
1011
|
+
|
|
1012
|
+
EXAMPLES:
|
|
1013
|
+
steroids tasks skip abc123 --notes "Cloud SQL - spec says SKIP"
|
|
1014
|
+
steroids tasks skip abc123 --partial --notes "Created deployment, needs manual GKE setup"
|
|
1015
|
+
`);
|
|
1016
|
+
return;
|
|
1017
|
+
}
|
|
1018
|
+
const identifier = positionals[0];
|
|
1019
|
+
const newStatus = values.partial ? 'partial' : 'skipped';
|
|
1020
|
+
const { db, close } = (0, connection_js_1.openDatabase)();
|
|
1021
|
+
try {
|
|
1022
|
+
let task = (0, queries_js_1.getTask)(db, identifier);
|
|
1023
|
+
if (!task) {
|
|
1024
|
+
task = (0, queries_js_1.getTaskByTitle)(db, identifier);
|
|
1025
|
+
}
|
|
1026
|
+
if (!task) {
|
|
1027
|
+
out.error(errors_js_1.ErrorCode.TASK_NOT_FOUND, `Task not found: ${identifier}`, { identifier });
|
|
1028
|
+
process.exit((0, errors_js_1.getExitCode)(errors_js_1.ErrorCode.TASK_NOT_FOUND));
|
|
1029
|
+
}
|
|
1030
|
+
const actor = values.model
|
|
1031
|
+
? `model:${values.model}`
|
|
1032
|
+
: 'human:cli';
|
|
1033
|
+
// Get section name before updating
|
|
1034
|
+
const section = task.section_id ? (0, queries_js_1.getSection)(db, task.section_id) : null;
|
|
1035
|
+
const sectionName = section?.name ?? null;
|
|
1036
|
+
const taskTitle = task.title;
|
|
1037
|
+
(0, queries_js_1.updateTaskStatus)(db, task.id, newStatus, actor, values.notes);
|
|
1038
|
+
// Refresh task to get updated status
|
|
1039
|
+
task = (0, queries_js_1.getTask)(db, task.id);
|
|
1040
|
+
// Log activity for skipped/partial task
|
|
1041
|
+
const projectPath = (0, node_path_1.resolve)(process.cwd());
|
|
1042
|
+
(0, activity_log_js_1.logActivity)(projectPath, 'cli', // CLI operations use 'cli' as runner ID
|
|
1043
|
+
task.id, taskTitle, sectionName, values.partial ? 'partial' : 'skipped');
|
|
1044
|
+
out.success({ task, skipped: true, partial: values.partial });
|
|
1045
|
+
if (!flags.json) {
|
|
1046
|
+
const statusLabel = values.partial ? 'PARTIAL' : 'SKIPPED';
|
|
1047
|
+
out.log(`Task ${statusLabel}: ${task.title}`);
|
|
1048
|
+
out.log(` Status: ${newStatus}`);
|
|
1049
|
+
out.log(` Actor: ${actor}`);
|
|
1050
|
+
if (values.notes) {
|
|
1051
|
+
out.log(` Notes: ${values.notes}`);
|
|
1052
|
+
}
|
|
1053
|
+
out.log('');
|
|
1054
|
+
out.log(' Task will not block the runner. Move on to the next task.');
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
finally {
|
|
1058
|
+
close();
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
async function auditTask(args, flags) {
|
|
1062
|
+
const out = (0, output_js_1.createOutput)({ command: 'tasks', subcommand: 'audit', flags });
|
|
1063
|
+
const { values, positionals } = (0, node_util_1.parseArgs)({
|
|
1064
|
+
args,
|
|
1065
|
+
options: {
|
|
1066
|
+
help: { type: 'boolean', short: 'h', default: false },
|
|
1067
|
+
},
|
|
1068
|
+
allowPositionals: true,
|
|
1069
|
+
});
|
|
1070
|
+
if (values.help || flags.help || positionals.length === 0) {
|
|
1071
|
+
out.log(`
|
|
1072
|
+
steroids tasks audit <id> - View task audit trail
|
|
1073
|
+
|
|
1074
|
+
USAGE:
|
|
1075
|
+
steroids tasks audit <id> [options]
|
|
1076
|
+
|
|
1077
|
+
OPTIONS:
|
|
1078
|
+
-h, --help Show help
|
|
1079
|
+
`);
|
|
1080
|
+
return;
|
|
1081
|
+
}
|
|
1082
|
+
const identifier = positionals[0];
|
|
1083
|
+
const { db, close } = (0, connection_js_1.openDatabase)();
|
|
1084
|
+
try {
|
|
1085
|
+
let task = (0, queries_js_1.getTask)(db, identifier);
|
|
1086
|
+
if (!task) {
|
|
1087
|
+
task = (0, queries_js_1.getTaskByTitle)(db, identifier);
|
|
1088
|
+
}
|
|
1089
|
+
if (!task) {
|
|
1090
|
+
out.error(errors_js_1.ErrorCode.TASK_NOT_FOUND, `Task not found: ${identifier}`, { identifier });
|
|
1091
|
+
process.exit((0, errors_js_1.getExitCode)(errors_js_1.ErrorCode.TASK_NOT_FOUND));
|
|
1092
|
+
}
|
|
1093
|
+
const entries = (0, queries_js_1.getTaskAudit)(db, task.id);
|
|
1094
|
+
out.success({ task: { id: task.id, title: task.title }, auditTrail: entries });
|
|
1095
|
+
if (!flags.json) {
|
|
1096
|
+
out.log(`Audit trail for: ${task.title}`);
|
|
1097
|
+
out.log(`Task ID: ${task.id}`);
|
|
1098
|
+
out.log('─'.repeat(80));
|
|
1099
|
+
out.log('TIMESTAMP FROM TO ACTOR NOTES');
|
|
1100
|
+
out.log('─'.repeat(80));
|
|
1101
|
+
for (const entry of entries) {
|
|
1102
|
+
const ts = entry.created_at.substring(0, 19).replace('T', ' ');
|
|
1103
|
+
const from = (entry.from_status ?? '-').padEnd(12);
|
|
1104
|
+
const to = entry.to_status.padEnd(12);
|
|
1105
|
+
const actor = entry.actor.padEnd(18);
|
|
1106
|
+
const notes = entry.notes ?? '-';
|
|
1107
|
+
out.log(`${ts} ${from} ${to} ${actor} ${notes}`);
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
finally {
|
|
1112
|
+
close();
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
//# sourceMappingURL=tasks.js.map
|