sidecar-cli 0.1.5-rc.1 → 0.1.6-beta.1
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
CHANGED
|
@@ -200,9 +200,9 @@ Notes, decisions, worklogs:
|
|
|
200
200
|
|
|
201
201
|
Tasks:
|
|
202
202
|
|
|
203
|
-
- `sidecar task
|
|
204
|
-
- `sidecar task
|
|
205
|
-
- `sidecar task list [--status
|
|
203
|
+
- `sidecar task create --title "<title>" --summary "<summary>" --goal "<goal>" [--type feature|bug|chore|research] [--status draft|ready|queued|running|review|blocked|done] [--priority low|medium|high] [--json]`
|
|
204
|
+
- `sidecar task set-status <task-id> --to draft|ready|queued|running|review|blocked|done --reason "<text>" [--by human|agent] [--session <id>] [--json]`
|
|
205
|
+
- `sidecar task list [--status draft|ready|queued|running|review|blocked|done|all] [--json]`
|
|
206
206
|
|
|
207
207
|
Sessions:
|
|
208
208
|
|
|
@@ -434,7 +434,7 @@ sidecar context --format markdown
|
|
|
434
434
|
sidecar session start --actor agent --name codex
|
|
435
435
|
sidecar decision record --title "Use SQLite" --summary "Local-first persistence"
|
|
436
436
|
sidecar worklog record --goal "init flow" --done "Implemented schema and command surface" --files src/cli.ts,src/db/schema.ts
|
|
437
|
-
sidecar task
|
|
437
|
+
sidecar task create --title "Add integration tests" --summary "Add integration coverage for init flow" --goal "Ensure init flow has regression coverage" --priority medium
|
|
438
438
|
sidecar summary refresh
|
|
439
439
|
sidecar session end --summary "Initialization and recording flow implemented"
|
|
440
440
|
```
|
|
@@ -449,7 +449,7 @@ Required minimum for any code change:
|
|
|
449
449
|
1. `sidecar context --format markdown`
|
|
450
450
|
2. `sidecar worklog record --done "<what changed>" --files <paths> --by agent`
|
|
451
451
|
3. if behavior/design changed: `sidecar decision record ...`
|
|
452
|
-
4. if follow-up exists: `sidecar task
|
|
452
|
+
4. if follow-up exists: `sidecar task create ...`
|
|
453
453
|
5. `sidecar summary refresh`
|
|
454
454
|
|
|
455
455
|
Optional local enforcement:
|
|
@@ -489,7 +489,7 @@ When changes are made in this repo, document them in Sidecar:
|
|
|
489
489
|
1. `sidecar context --format markdown`
|
|
490
490
|
2. `sidecar worklog record --done "<what changed>" --files <paths> --by human|agent`
|
|
491
491
|
3. `sidecar decision record ...` when behavior/design changes
|
|
492
|
-
4. `sidecar task
|
|
492
|
+
4. `sidecar task create ...` for follow-up work
|
|
493
493
|
5. `sidecar summary refresh`
|
|
494
494
|
|
|
495
495
|
## Local storage details
|
|
@@ -579,7 +579,7 @@ Standard JSON envelope:
|
|
|
579
579
|
{
|
|
580
580
|
"ok": true,
|
|
581
581
|
"version": "1.0",
|
|
582
|
-
"command": "task
|
|
582
|
+
"command": "task create",
|
|
583
583
|
"data": {},
|
|
584
584
|
"errors": []
|
|
585
585
|
}
|
|
@@ -591,7 +591,7 @@ Failure envelope:
|
|
|
591
591
|
{
|
|
592
592
|
"ok": false,
|
|
593
593
|
"version": "1.0",
|
|
594
|
-
"command": "task
|
|
594
|
+
"command": "task create",
|
|
595
595
|
"data": null,
|
|
596
596
|
"errors": ["..."]
|
|
597
597
|
}
|
package/dist/cli.js
CHANGED
|
@@ -22,7 +22,7 @@ import { refreshSummaryFile } from './services/summary-service.js';
|
|
|
22
22
|
import { buildContext } from './services/context-service.js';
|
|
23
23
|
import { getCapabilitiesManifest } from './services/capabilities-service.js';
|
|
24
24
|
import { addArtifact, listArtifacts } from './services/artifact-service.js';
|
|
25
|
-
import { addDecision, addNote, addWorklog, getActiveSessionId, listRecentEvents } from './services/event-service.js';
|
|
25
|
+
import { addDecision, addNote, addWorklog, createEvent, getActiveSessionId, listRecentEvents } from './services/event-service.js';
|
|
26
26
|
import { currentSession, endSession, startSession, verifySessionHygiene } from './services/session-service.js';
|
|
27
27
|
import { HOOK_EVENTS, handleHookEvent, hookEventSchema, hookPayloadSchema } from './services/hook-service.js';
|
|
28
28
|
import { loadPromptSpec } from './prompts/prompt-spec.js';
|
|
@@ -39,6 +39,7 @@ import { compileTaskPrompt } from './prompts/prompt-service.js';
|
|
|
39
39
|
import { runPipelineExecution, runTaskExecution } from './services/run-orchestrator-service.js';
|
|
40
40
|
import { loadRunnerPreferences } from './runners/config.js';
|
|
41
41
|
import { assignTask, queueReadyTasks } from './services/task-orchestration-service.js';
|
|
42
|
+
import { transitionTaskStatus } from './services/task-status-service.js';
|
|
42
43
|
import { buildReviewSummary, createFollowupTaskFromRun, reviewRun } from './services/run-review-service.js';
|
|
43
44
|
const pkg = JSON.parse(fs.readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
|
|
44
45
|
const actorSchema = z.enum(['human', 'agent']);
|
|
@@ -1100,7 +1101,7 @@ task
|
|
|
1100
1101
|
.description('List task packets')
|
|
1101
1102
|
.option('--status <status>', 'draft|ready|queued|running|review|blocked|done|all', 'all')
|
|
1102
1103
|
.option('--json', 'Print machine-readable JSON output')
|
|
1103
|
-
.addHelpText('after', '\nExamples:\n $ sidecar task list\n $ sidecar task list --status
|
|
1104
|
+
.addHelpText('after', '\nExamples:\n $ sidecar task list\n $ sidecar task list --status draft\n $ sidecar task list --json')
|
|
1104
1105
|
.action((opts) => {
|
|
1105
1106
|
const command = 'task list';
|
|
1106
1107
|
try {
|
|
@@ -1158,6 +1159,64 @@ task
|
|
|
1158
1159
|
handleCommandError(command, Boolean(opts.json), err);
|
|
1159
1160
|
}
|
|
1160
1161
|
});
|
|
1162
|
+
task
|
|
1163
|
+
.command('set-status <task-id>')
|
|
1164
|
+
.description('Transition task packet status with validation and an audit note')
|
|
1165
|
+
.requiredOption('--to <status>', 'draft|ready|queued|running|review|blocked|done')
|
|
1166
|
+
.requiredOption('--reason <text>', 'Why this manual transition is needed')
|
|
1167
|
+
.option('--by <actor>', 'human|agent', 'human')
|
|
1168
|
+
.option('--session <session-id>', 'Session id override')
|
|
1169
|
+
.option('--json', 'Print machine-readable JSON output')
|
|
1170
|
+
.addHelpText('after', '\nExamples:\n $ sidecar task set-status T-001 --to ready --reason "Ready to queue"\n $ sidecar task set-status T-001 --to done --reason "Administrative cleanup" --by agent --json')
|
|
1171
|
+
.action((taskIdText, opts) => {
|
|
1172
|
+
const command = 'task set-status';
|
|
1173
|
+
try {
|
|
1174
|
+
const taskId = taskIdText.trim().toUpperCase();
|
|
1175
|
+
const toStatus = taskPacketStatusSchema.parse(opts.to);
|
|
1176
|
+
const reason = String(opts.reason ?? '').trim();
|
|
1177
|
+
if (!reason)
|
|
1178
|
+
fail('Reason is required');
|
|
1179
|
+
const by = actorSchema.parse(opts.by);
|
|
1180
|
+
const { db, projectId } = requireInitialized();
|
|
1181
|
+
const sessionId = maybeSessionId(db, projectId, opts.session);
|
|
1182
|
+
const result = transitionTaskStatus(resolveProjectRoot(), taskId, toStatus);
|
|
1183
|
+
const eventId = createEvent(db, {
|
|
1184
|
+
projectId,
|
|
1185
|
+
type: 'note',
|
|
1186
|
+
title: `Task ${result.task_id} status changed`,
|
|
1187
|
+
summary: `${result.from_status} -> ${result.to_status}: ${reason}`,
|
|
1188
|
+
details: {
|
|
1189
|
+
task_id: result.task_id,
|
|
1190
|
+
from_status: result.from_status,
|
|
1191
|
+
to_status: result.to_status,
|
|
1192
|
+
reason,
|
|
1193
|
+
command: 'task set-status',
|
|
1194
|
+
},
|
|
1195
|
+
createdBy: by,
|
|
1196
|
+
sessionId,
|
|
1197
|
+
});
|
|
1198
|
+
db.close();
|
|
1199
|
+
respondSuccess(command, Boolean(opts.json), {
|
|
1200
|
+
task: result,
|
|
1201
|
+
event: {
|
|
1202
|
+
id: eventId,
|
|
1203
|
+
type: 'note',
|
|
1204
|
+
title: `Task ${result.task_id} status changed`,
|
|
1205
|
+
summary: `${result.from_status} -> ${result.to_status}: ${reason}`,
|
|
1206
|
+
created_by: by,
|
|
1207
|
+
session_id: sessionId,
|
|
1208
|
+
created_at: nowIso(),
|
|
1209
|
+
},
|
|
1210
|
+
}, [
|
|
1211
|
+
`Updated ${result.task_id}: ${result.from_status} -> ${result.to_status}.`,
|
|
1212
|
+
`Reason: ${reason}`,
|
|
1213
|
+
`Recorded note event #${eventId}.`,
|
|
1214
|
+
]);
|
|
1215
|
+
}
|
|
1216
|
+
catch (err) {
|
|
1217
|
+
handleCommandError(command, Boolean(opts.json), err);
|
|
1218
|
+
}
|
|
1219
|
+
});
|
|
1161
1220
|
task
|
|
1162
1221
|
.command('create-followup <run-id>')
|
|
1163
1222
|
.description('Create a follow-up task packet from a run report')
|
|
@@ -222,6 +222,13 @@ export function getCapabilitiesManifest(version) {
|
|
|
222
222
|
arguments: ['<task-id>'],
|
|
223
223
|
options: ['--agent-role planner|builder-ui|builder-app|reviewer|tester', '--runner codex|claude', '--json'],
|
|
224
224
|
},
|
|
225
|
+
{
|
|
226
|
+
name: 'set-status',
|
|
227
|
+
description: 'Transition task packet status with validation',
|
|
228
|
+
json_output: true,
|
|
229
|
+
arguments: ['<task-id>'],
|
|
230
|
+
options: ['--to draft|ready|queued|running|review|blocked|done', '--reason <text>', '--by human|agent', '--session <id>', '--json'],
|
|
231
|
+
},
|
|
225
232
|
{
|
|
226
233
|
name: 'create-followup',
|
|
227
234
|
description: 'Create follow-up task from run report',
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { SidecarError } from '../lib/errors.js';
|
|
2
|
+
import { getTaskPacket, saveTaskPacket } from '../tasks/task-service.js';
|
|
3
|
+
const TASK_STATUS_TRANSITIONS = {
|
|
4
|
+
draft: ['ready', 'blocked', 'done'],
|
|
5
|
+
ready: ['draft', 'queued', 'blocked', 'done'],
|
|
6
|
+
queued: ['ready', 'running', 'blocked'],
|
|
7
|
+
running: ['ready', 'review', 'blocked'],
|
|
8
|
+
review: ['ready', 'blocked', 'done'],
|
|
9
|
+
blocked: ['ready', 'done'],
|
|
10
|
+
done: ['review'],
|
|
11
|
+
};
|
|
12
|
+
export function allowedTaskStatusTransitions(fromStatus) {
|
|
13
|
+
return TASK_STATUS_TRANSITIONS[fromStatus];
|
|
14
|
+
}
|
|
15
|
+
export function transitionTaskStatus(rootPath, taskId, toStatus) {
|
|
16
|
+
const task = getTaskPacket(rootPath, taskId);
|
|
17
|
+
const fromStatus = task.status;
|
|
18
|
+
if (fromStatus === toStatus) {
|
|
19
|
+
throw new SidecarError(`Task ${task.task_id} is already '${toStatus}'`);
|
|
20
|
+
}
|
|
21
|
+
const allowed = allowedTaskStatusTransitions(fromStatus);
|
|
22
|
+
if (!allowed.includes(toStatus)) {
|
|
23
|
+
throw new SidecarError(`Invalid status transition for ${task.task_id}: ${fromStatus} -> ${toStatus}. Allowed: ${allowed.join(', ')}`);
|
|
24
|
+
}
|
|
25
|
+
const updated = { ...task, status: toStatus };
|
|
26
|
+
const filePath = saveTaskPacket(rootPath, updated);
|
|
27
|
+
return {
|
|
28
|
+
task_id: task.task_id,
|
|
29
|
+
from_status: fromStatus,
|
|
30
|
+
to_status: toStatus,
|
|
31
|
+
path: filePath,
|
|
32
|
+
};
|
|
33
|
+
}
|