@sideboard-ai/cli 0.1.103 → 0.1.110

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +3 -0
  2. package/dist/index.js +108 -5
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -8,6 +8,7 @@ npm i -g @sideboard-ai/cli
8
8
  sideboard ls
9
9
  sideboard send <thread> "…"
10
10
  sideboard mcp # MCP stdio server
11
+ sideboard schedule ls # local jobs that trigger orchestration chats
11
12
  ```
12
13
 
13
14
  Also installs `side` as a short alias. The MCP server is available as `sideboard mcp`, or via `@sideboard-ai/core`'s `sideboard-mcp` bin.
@@ -20,3 +21,5 @@ sideboard slack listen
20
21
  ```
21
22
 
22
23
  How to connect and address Personal / Work Macs: [README — Slack](../../README.md#slack).
24
+
25
+ Schedules (Settings, CLI, or MCP `create_schedule`) fire only while Sideboard.app is running. Overnight: Settings → Advanced → Caffeinate while schedules are enabled. Details: [README — Scheduled orchestration](../../README.md#scheduled-orchestration).
package/dist/index.js CHANGED
@@ -30,7 +30,15 @@ import {
30
30
  disconnectLinear,
31
31
  runSlackListen,
32
32
  SLACK_OAUTH_REDIRECT,
33
- LINEAR_OAUTH_REDIRECT
33
+ LINEAR_OAUTH_REDIRECT,
34
+ createSchedule,
35
+ deleteSchedule,
36
+ fireSchedule,
37
+ formatScheduleWhen,
38
+ getSchedule,
39
+ listSchedules,
40
+ resolveScheduleThreadId,
41
+ updateSchedule
34
42
  } from "@sideboard-ai/core";
35
43
  var VERSION = "0.1.0";
36
44
  async function printUpgradeHint() {
@@ -107,7 +115,7 @@ async function main() {
107
115
  for (const w of s.warnings) console.log(chalk.yellow(` warning: ${w}`));
108
116
  }
109
117
  });
110
- program.command("new").description("Create a thread from branch, PR, or Linear ticket").requiredOption("--from <spec>", "branch:<name>|pr:<n>|ticket:<key>").requiredOption("--agent <agent>", "claude|codex|opencode|brightsy|cursor").option("--repo <path>", "repo path", process.cwd()).option("--title <title>", "thread title").action(async (opts) => {
118
+ program.command("new").description("Create a thread from branch, PR, or Linear ticket").requiredOption("--from <spec>", "branch:<name>|pr:<n>|ticket:<key>").requiredOption("--agent <agent>", "claude|codex|opencode|brightsy|cursor").option("--repo <path>", "repo path", process.cwd()).option("--title <title>", "thread title").option("--cowboy", "work on the project checkout (default branch); requires Settings \u2192 Advanced \u2192 Cowboy mode").action(async (opts) => {
111
119
  await orch.reconcile(opts.repo);
112
120
  const { sourceType, sourceRef } = parseFrom(opts.from);
113
121
  const agent = parseAgent(opts.agent);
@@ -124,7 +132,8 @@ async function main() {
124
132
  sourceRef,
125
133
  agent,
126
134
  repoPath,
127
- title: opts.title
135
+ title: opts.title,
136
+ cowboy: Boolean(opts.cowboy)
128
137
  });
129
138
  console.log(chalk.green(`Created ${thread.id.slice(0, 8)} ${thread.branchName}`));
130
139
  console.log(` worktree: ${thread.worktreePath}`);
@@ -400,14 +409,18 @@ ${preview.diffStat}`);
400
409
  return;
401
410
  }
402
411
  const ok = await confirm(
403
- opts.draft ? "Push and create/update DRAFT PR?" : "Push and create/update PR?"
412
+ preview.cowboy ? `Commit and push directly to ${preview.branch}?` : opts.draft ? "Push and create/update DRAFT PR?" : "Push and create/update PR?"
404
413
  );
405
414
  if (!ok) {
406
415
  console.log("Aborted");
407
416
  return;
408
417
  }
409
418
  const result = await orch.confirmLand(thread, { draft: Boolean(opts.draft) });
410
- console.log(chalk.green(`PR: ${result.prUrl}`));
419
+ if (result.prUrl) {
420
+ console.log(chalk.green(`PR: ${result.prUrl}`));
421
+ } else {
422
+ console.log(chalk.green(`Pushed ${preview.branch}`));
423
+ }
411
424
  });
412
425
  program.command("rm").argument("<thread>", "thread id/ref").option("--purge", "delete record too").option("--delete-branch", "also delete the git branch (with --purge)").action(async (thread, opts) => {
413
426
  if (opts.purge) {
@@ -504,6 +517,96 @@ ${preview.diffStat}`);
504
517
  program.command("mcp").description("Run the Sideboard MCP stdio server").action(async () => {
505
518
  await startMcpServer();
506
519
  });
520
+ const scheduleCmd = program.command("schedule").description(
521
+ "Local jobs that trigger orchestration chats (Sideboard.app must be running to fire)"
522
+ );
523
+ scheduleCmd.command("ls").description("List schedules").action(() => {
524
+ const rows = listSchedules();
525
+ if (rows.length === 0) {
526
+ console.log(chalk.dim("No schedules"));
527
+ return;
528
+ }
529
+ for (const row of rows) {
530
+ const target = row.threadId ? row.threadId.slice(0, 8) : "new chat";
531
+ const on = row.enabled ? chalk.green("on ") : chalk.dim("off");
532
+ console.log(
533
+ `${row.id.slice(0, 8)} ${on} ${formatScheduleWhen(row.when)} next ${row.nextRunAt} ${target} ${row.name}`
534
+ );
535
+ if (row.lastError) console.log(chalk.red(` ${row.lastError}`));
536
+ }
537
+ });
538
+ scheduleCmd.command("add").requiredOption("--prompt <text>", "prompt / goal queued when the job fires").option("--name <name>", "short label").option("--at <iso>", "one-shot ISO datetime").option("--every <duration>", "interval (15m, 1h, 6h, 1d)").option("--cron <expr>", "5-field cron").option("--tz <iana>", "timezone for cron").option(
539
+ "--thread <id>",
540
+ "existing orchestration chat (or self). Omit to create a new Global chat each run"
541
+ ).option("--agent <agent>", "claude|cursor|codex|opencode (new chats only)").action((opts) => {
542
+ const cadence = [opts.at, opts.every, opts.cron].filter(Boolean);
543
+ if (cadence.length !== 1) {
544
+ throw new Error("Pass exactly one of --at, --every, or --cron");
545
+ }
546
+ const when = opts.at ? { kind: "once", at: String(opts.at) } : opts.every ? { kind: "every", every: String(opts.every) } : {
547
+ kind: "cron",
548
+ expr: String(opts.cron),
549
+ tz: opts.tz ? String(opts.tz) : void 0
550
+ };
551
+ const threadId = resolveScheduleThreadId(
552
+ opts.thread ? String(opts.thread) : void 0
553
+ );
554
+ if (String(opts.thread ?? "").trim().toLowerCase() === "self" && !threadId) {
555
+ throw new Error("--thread self needs SIDEBOARD_ORCHESTRATOR_THREAD_ID");
556
+ }
557
+ const agent = opts.agent ? parseAgent(String(opts.agent)) : void 0;
558
+ if (agent === "brightsy") {
559
+ throw new Error("schedules cannot use brightsy \u2014 pick claude, cursor, codex, or opencode");
560
+ }
561
+ const row = createSchedule({
562
+ name: opts.name,
563
+ prompt: String(opts.prompt),
564
+ when,
565
+ threadId,
566
+ agent,
567
+ createdBy: "cli"
568
+ });
569
+ if (!row.threadId && row.when.kind !== "once") {
570
+ console.log(
571
+ chalk.yellow(
572
+ "Recurring schedule has no --thread: each run opens a new orchestration chat."
573
+ )
574
+ );
575
+ }
576
+ console.log(
577
+ chalk.green(
578
+ `Scheduled ${row.id.slice(0, 8)} ${formatScheduleWhen(row.when)} next ${row.nextRunAt}`
579
+ )
580
+ );
581
+ console.log(chalk.dim("Fires while Sideboard.app is running on this Mac."));
582
+ });
583
+ scheduleCmd.command("enable").argument("<id>", "schedule id").action((id) => {
584
+ const row = updateSchedule(id, { enabled: true });
585
+ console.log(chalk.green(`Enabled ${row.id.slice(0, 8)}`));
586
+ });
587
+ scheduleCmd.command("disable").argument("<id>", "schedule id").action((id) => {
588
+ const row = updateSchedule(id, { enabled: false });
589
+ console.log(chalk.yellow(`Disabled ${row.id.slice(0, 8)}`));
590
+ });
591
+ scheduleCmd.command("rm").argument("<id>", "schedule id").action((id) => {
592
+ const row = getSchedule(id);
593
+ if (!row) throw new Error(`Schedule not found: ${id}`);
594
+ deleteSchedule(row.id);
595
+ console.log(chalk.yellow(`Removed ${row.id.slice(0, 8)}`));
596
+ });
597
+ scheduleCmd.command("run").argument("<id>", "schedule id").description("Fire now").action(async (id) => {
598
+ const row = await fireSchedule(id);
599
+ if (row.lastError) {
600
+ console.error(chalk.red(row.lastError));
601
+ process.exitCode = 1;
602
+ return;
603
+ }
604
+ console.log(
605
+ chalk.green(
606
+ `Fired ${row.id.slice(0, 8)} \u2192 ${row.lastThreadId?.slice(0, 8) ?? "thread"}`
607
+ )
608
+ );
609
+ });
507
610
  const brightsyCmd = program.command("brightsy").description(
508
611
  "Brightsy team helpers (wraps `brightsy teams` + Sideboard multi-team MCP state)"
509
612
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sideboard-ai/cli",
3
- "version": "0.1.103",
3
+ "version": "0.1.110",
4
4
  "description": "Sideboard CLI — agent-agnostic worktree orchestration (includes `sideboard mcp`)",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -16,7 +16,7 @@
16
16
  "chalk": "^5.4.1",
17
17
  "commander": "^13.1.0",
18
18
  "ora": "^8.2.0",
19
- "@sideboard-ai/core": "0.1.103"
19
+ "@sideboard-ai/core": "0.1.110"
20
20
  },
21
21
  "devDependencies": {
22
22
  "@types/node": "^22.13.10",