sidecar-cli 0.1.5-beta.2 → 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
@@ -79,6 +79,18 @@ Run locally in dev mode:
79
79
  npm run dev -- --help
80
80
  ```
81
81
 
82
+ ## Release channels
83
+
84
+ Sidecar ships features through `beta` → `rc` → `stable` as version bumps — there are no per-feature channel flags. Install `@beta` to try new features early; install default (stable) for the latest promoted release.
85
+
86
+ | Channel | Current version | Notes |
87
+ | --- | --- | --- |
88
+ | stable (`latest`) | `0.1.4` | Promoted releases. Homebrew tracks this channel. |
89
+ | rc | _none newer than stable_ | Release candidates published only when preparing a stable cut. |
90
+ | beta | `0.1.5-beta.2` | Dual-runner pipelines, run replay, ambient capture via Claude Code hooks, typed validation + auto-approve, freestanding prompt specs, `sidecar demo`. |
91
+
92
+ Per-version release notes live at [github.com/karlhills/sidecar-cli/releases](https://github.com/karlhills/sidecar-cli/releases).
93
+
82
94
  ## Quick start
83
95
 
84
96
  1. Initialize in a project directory:
@@ -188,9 +200,9 @@ Notes, decisions, worklogs:
188
200
 
189
201
  Tasks:
190
202
 
191
- - `sidecar task add "<title>" [--description <text>] [--priority low|medium|high] [--by human|agent] [--json]`
192
- - `sidecar task done <task-id> [--by human|agent] [--json]`
193
- - `sidecar task list [--status open|done|all] [--format table|json] [--json]`
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]`
194
206
 
195
207
  Sessions:
196
208
 
@@ -422,7 +434,7 @@ sidecar context --format markdown
422
434
  sidecar session start --actor agent --name codex
423
435
  sidecar decision record --title "Use SQLite" --summary "Local-first persistence"
424
436
  sidecar worklog record --goal "init flow" --done "Implemented schema and command surface" --files src/cli.ts,src/db/schema.ts
425
- sidecar task add "Add integration tests" --priority medium --by agent
437
+ sidecar task create --title "Add integration tests" --summary "Add integration coverage for init flow" --goal "Ensure init flow has regression coverage" --priority medium
426
438
  sidecar summary refresh
427
439
  sidecar session end --summary "Initialization and recording flow implemented"
428
440
  ```
@@ -437,7 +449,7 @@ Required minimum for any code change:
437
449
  1. `sidecar context --format markdown`
438
450
  2. `sidecar worklog record --done "<what changed>" --files <paths> --by agent`
439
451
  3. if behavior/design changed: `sidecar decision record ...`
440
- 4. if follow-up exists: `sidecar task add ...`
452
+ 4. if follow-up exists: `sidecar task create ...`
441
453
  5. `sidecar summary refresh`
442
454
 
443
455
  Optional local enforcement:
@@ -477,7 +489,7 @@ When changes are made in this repo, document them in Sidecar:
477
489
  1. `sidecar context --format markdown`
478
490
  2. `sidecar worklog record --done "<what changed>" --files <paths> --by human|agent`
479
491
  3. `sidecar decision record ...` when behavior/design changes
480
- 4. `sidecar task add ...` for follow-up work
492
+ 4. `sidecar task create ...` for follow-up work
481
493
  5. `sidecar summary refresh`
482
494
 
483
495
  ## Local storage details
@@ -567,7 +579,7 @@ Standard JSON envelope:
567
579
  {
568
580
  "ok": true,
569
581
  "version": "1.0",
570
- "command": "task add",
582
+ "command": "task create",
571
583
  "data": {},
572
584
  "errors": []
573
585
  }
@@ -579,7 +591,7 @@ Failure envelope:
579
591
  {
580
592
  "ok": false,
581
593
  "version": "1.0",
582
- "command": "task add",
594
+ "command": "task create",
583
595
  "data": null,
584
596
  "errors": ["..."]
585
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 open\n $ sidecar task list --json')
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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sidecar-cli",
3
- "version": "0.1.5-beta.2",
3
+ "version": "0.1.6-beta.1",
4
4
  "description": "Local-first project memory and recording tool",
5
5
  "scripts": {
6
6
  "build": "npm run clean && tsc -p tsconfig.json",