yapout 0.7.0 → 0.9.0

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 (2) hide show
  1. package/dist/index.js +710 -352
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -7,16 +7,16 @@ import {
7
7
  } from "./chunk-DLHFRTYU.js";
8
8
 
9
9
  // src/index.ts
10
- import { Command as Command16 } from "commander";
10
+ import { Command as Command17 } from "commander";
11
11
 
12
12
  // src/commands/login.ts
13
- import { Command } from "commander";
14
- import http from "http";
15
- import { createHash } from "crypto";
16
- import { hostname, userInfo } from "os";
17
- import chalk from "chalk";
13
+ import { Command as Command2 } from "commander";
14
+ import http2 from "http";
15
+ import { createHash as createHash2 } from "crypto";
16
+ import { hostname as hostname2, userInfo as userInfo2 } from "os";
17
+ import chalk3 from "chalk";
18
18
  import open from "open";
19
- import { anyApi as anyApi2 } from "convex/server";
19
+ import { anyApi as anyApi3 } from "convex/server";
20
20
 
21
21
  // src/lib/config.ts
22
22
  import { homedir } from "os";
@@ -521,8 +521,267 @@ function registerProtocolHandler() {
521
521
  }
522
522
  }
523
523
 
524
+ // src/commands/serve.ts
525
+ import { Command } from "commander";
526
+ import http from "http";
527
+ import { createHash } from "crypto";
528
+ import { hostname, userInfo, platform as platform2, type } from "os";
529
+ import { join as join3 } from "path";
530
+ import {
531
+ readFileSync as readFileSync2,
532
+ writeFileSync as writeFileSync3,
533
+ existsSync as existsSync2,
534
+ unlinkSync as unlinkSync3,
535
+ openSync
536
+ } from "fs";
537
+ import { spawn } from "child_process";
538
+ import chalk2 from "chalk";
539
+ import { anyApi as anyApi2 } from "convex/server";
540
+
541
+ // src/lib/auth.ts
542
+ import chalk from "chalk";
543
+ function requireAuth() {
544
+ const creds = readCredentials();
545
+ if (!creds) {
546
+ console.error(
547
+ chalk.red("Not logged in.") + " Run " + chalk.cyan("yapout login") + " first."
548
+ );
549
+ process.exit(1);
550
+ }
551
+ if (Date.now() > creds.expiresAt) {
552
+ console.error(
553
+ chalk.red("Session expired.") + " Run " + chalk.cyan("yapout login") + " to re-authenticate."
554
+ );
555
+ process.exit(1);
556
+ }
557
+ return creds;
558
+ }
559
+
560
+ // src/commands/serve.ts
561
+ var CLI_VERSION = "0.9.0";
562
+ var DEFAULT_PORT = 7777;
563
+ var PORT_RANGE = 10;
564
+ var HEARTBEAT_MS = 3e4;
565
+ var PID_FILE = join3(getYapoutDir(), "serve.pid");
566
+ var PORT_FILE = join3(getYapoutDir(), "serve.port");
567
+ var LOG_FILE = join3(getYapoutDir(), "serve.log");
568
+ var ALLOWED_ORIGINS = [
569
+ "https://yapout.com",
570
+ "https://www.yapout.com",
571
+ "https://yapout.vercel.app",
572
+ // Local dev / sandbox ports — wide range so any worktree's auto-allocated
573
+ // Next.js port works without configuration.
574
+ ...Array.from({ length: 20 }, (_, i) => `http://localhost:${3e3 + i}`),
575
+ ...Array.from({ length: 20 }, (_, i) => `http://127.0.0.1:${3e3 + i}`)
576
+ ];
577
+ function computeDeviceId() {
578
+ return createHash("sha256").update(hostname() + userInfo().username).digest("hex").slice(0, 16);
579
+ }
580
+ function corsHeadersFor(origin) {
581
+ const allowOrigin = origin && ALLOWED_ORIGINS.includes(origin) ? origin : ALLOWED_ORIGINS[0];
582
+ return {
583
+ "Access-Control-Allow-Origin": allowOrigin,
584
+ "Access-Control-Allow-Methods": "GET, OPTIONS",
585
+ "Access-Control-Allow-Headers": "Content-Type",
586
+ "Access-Control-Allow-Private-Network": "true",
587
+ Vary: "Origin"
588
+ };
589
+ }
590
+ function startServer(payload) {
591
+ const server = http.createServer((req, res) => {
592
+ const origin = req.headers.origin;
593
+ const headers = corsHeadersFor(origin);
594
+ if (req.method === "OPTIONS") {
595
+ res.writeHead(204, headers);
596
+ res.end();
597
+ return;
598
+ }
599
+ if (req.method === "GET" && req.url === "/device") {
600
+ res.writeHead(200, {
601
+ ...headers,
602
+ "Content-Type": "application/json",
603
+ "Cache-Control": "no-store"
604
+ });
605
+ res.end(JSON.stringify(payload));
606
+ return;
607
+ }
608
+ res.writeHead(404, headers);
609
+ res.end();
610
+ });
611
+ return new Promise((resolve12, reject) => {
612
+ let attempt = 0;
613
+ const tryListen = () => {
614
+ const port = DEFAULT_PORT + attempt;
615
+ const onError = (err) => {
616
+ server.removeListener("error", onError);
617
+ if (err.code === "EADDRINUSE" && attempt < PORT_RANGE - 1) {
618
+ attempt++;
619
+ tryListen();
620
+ } else {
621
+ reject(err);
622
+ }
623
+ };
624
+ server.once("error", onError);
625
+ server.listen(port, "127.0.0.1", () => {
626
+ server.removeListener("error", onError);
627
+ resolve12({
628
+ port,
629
+ close: () => server.close()
630
+ });
631
+ });
632
+ };
633
+ tryListen();
634
+ });
635
+ }
636
+ async function runForeground() {
637
+ const creds = requireAuth();
638
+ const deviceId = computeDeviceId();
639
+ const payload = {
640
+ deviceId,
641
+ name: hostname(),
642
+ hostname: hostname(),
643
+ platform: `${type()} ${platform2()}`,
644
+ cliVersion: CLI_VERSION
645
+ };
646
+ const { port } = await startServer(payload);
647
+ writeFileSync3(PORT_FILE, String(port));
648
+ writeFileSync3(PID_FILE, String(process.pid));
649
+ console.log(chalk2.green(`yapout serve listening on http://127.0.0.1:${port}`));
650
+ console.log(chalk2.dim(`Device: ${payload.name} (${deviceId})`));
651
+ console.log(chalk2.dim("Press Ctrl-C to stop."));
652
+ const client = createConvexClient(creds.token);
653
+ const heartbeat = async () => {
654
+ try {
655
+ await client.mutation(anyApi2.functions.devices.heartbeatDevice, {
656
+ deviceId
657
+ });
658
+ } catch {
659
+ }
660
+ };
661
+ client.mutation(anyApi2.functions.devices.registerDevice, {
662
+ deviceId,
663
+ name: payload.name,
664
+ cliVersion: CLI_VERSION,
665
+ machineHostname: payload.hostname
666
+ }).catch(() => {
667
+ });
668
+ void heartbeat();
669
+ const interval = setInterval(heartbeat, HEARTBEAT_MS);
670
+ const shutdown = async () => {
671
+ clearInterval(interval);
672
+ try {
673
+ await client.mutation(anyApi2.functions.devices.markDeviceOffline, {
674
+ deviceId
675
+ });
676
+ } catch {
677
+ }
678
+ try {
679
+ if (existsSync2(PID_FILE)) unlinkSync3(PID_FILE);
680
+ } catch {
681
+ }
682
+ try {
683
+ if (existsSync2(PORT_FILE)) unlinkSync3(PORT_FILE);
684
+ } catch {
685
+ }
686
+ process.exit(0);
687
+ };
688
+ process.on("SIGINT", () => {
689
+ void shutdown();
690
+ });
691
+ process.on("SIGTERM", () => {
692
+ void shutdown();
693
+ });
694
+ await new Promise(() => {
695
+ });
696
+ }
697
+ function isProcessRunning(pid) {
698
+ try {
699
+ process.kill(pid, 0);
700
+ return true;
701
+ } catch {
702
+ return false;
703
+ }
704
+ }
705
+ function readPid() {
706
+ if (!existsSync2(PID_FILE)) return null;
707
+ const raw = readFileSync2(PID_FILE, "utf-8").trim();
708
+ const pid = parseInt(raw, 10);
709
+ return Number.isFinite(pid) ? pid : null;
710
+ }
711
+ function readPort() {
712
+ if (!existsSync2(PORT_FILE)) return null;
713
+ const raw = readFileSync2(PORT_FILE, "utf-8").trim();
714
+ const port = parseInt(raw, 10);
715
+ return Number.isFinite(port) ? port : null;
716
+ }
717
+ function spawnServeDaemon(argv0) {
718
+ const existingPid = readPid();
719
+ if (existingPid && isProcessRunning(existingPid)) return;
720
+ const logFd = openSync(LOG_FILE, "a");
721
+ const child = spawn(process.execPath, [argv0, "serve"], {
722
+ detached: true,
723
+ stdio: ["ignore", logFd, logFd],
724
+ env: process.env
725
+ });
726
+ child.unref();
727
+ }
728
+ var serveCommand = new Command("serve").description(
729
+ "Run the local device discovery server (auto-started by `yapout login`)"
730
+ ).option("--status", "Check if the server is running").option("--stop", "Stop the running server").action(async (opts) => {
731
+ if (opts.status) {
732
+ const pid = readPid();
733
+ const port = readPort();
734
+ if (pid && isProcessRunning(pid)) {
735
+ console.log(
736
+ chalk2.green(`yapout serve is running`) + chalk2.dim(` (PID ${pid}, port ${port ?? "?"})`)
737
+ );
738
+ } else {
739
+ if (pid) unlinkSync3(PID_FILE);
740
+ console.log(chalk2.dim("yapout serve is not running"));
741
+ }
742
+ return;
743
+ }
744
+ if (opts.stop) {
745
+ const pid = readPid();
746
+ if (!pid) {
747
+ console.log(chalk2.dim("yapout serve is not running"));
748
+ return;
749
+ }
750
+ try {
751
+ process.kill(pid, "SIGTERM");
752
+ console.log(chalk2.green(`Stopped yapout serve`) + chalk2.dim(` (PID ${pid})`));
753
+ } catch {
754
+ console.log(chalk2.dim("Server already stopped"));
755
+ }
756
+ try {
757
+ unlinkSync3(PID_FILE);
758
+ } catch {
759
+ }
760
+ try {
761
+ unlinkSync3(PORT_FILE);
762
+ } catch {
763
+ }
764
+ return;
765
+ }
766
+ const existingPid = readPid();
767
+ if (existingPid && !isProcessRunning(existingPid)) {
768
+ try {
769
+ unlinkSync3(PID_FILE);
770
+ } catch {
771
+ }
772
+ } else if (existingPid && isProcessRunning(existingPid)) {
773
+ console.log(
774
+ chalk2.dim(
775
+ `yapout serve is already running (PID ${existingPid}). Use --stop first.`
776
+ )
777
+ );
778
+ process.exit(1);
779
+ }
780
+ await runForeground();
781
+ });
782
+
524
783
  // src/commands/login.ts
525
- var CLI_VERSION = "0.7.0";
784
+ var CLI_VERSION2 = "0.9.0";
526
785
  function safeReturnTo(raw) {
527
786
  if (!raw) return null;
528
787
  try {
@@ -541,7 +800,7 @@ function startCallbackServer() {
541
800
  resolveData = res;
542
801
  rejectData = rej;
543
802
  });
544
- const server = http.createServer((req, res) => {
803
+ const server = http2.createServer((req, res) => {
545
804
  const url = new URL(req.url, `http://localhost`);
546
805
  if (url.pathname === "/callback") {
547
806
  const token = url.searchParams.get("token");
@@ -593,14 +852,14 @@ function startCallbackServer() {
593
852
  }, 12e4);
594
853
  });
595
854
  }
596
- var loginCommand = new Command("login").description("Authenticate with yapout").action(async () => {
597
- console.log(chalk.dim("Starting authentication..."));
855
+ var loginCommand = new Command2("login").description("Authenticate with yapout").action(async () => {
856
+ console.log(chalk3.dim("Starting authentication..."));
598
857
  const { port, data } = await startCallbackServer();
599
858
  const appUrl = getAppUrl();
600
859
  const authUrl = `${appUrl}/auth/cli?port=${port}`;
601
- console.log(chalk.dim(`Opening browser...`));
860
+ console.log(chalk3.dim(`Opening browser...`));
602
861
  await open(authUrl);
603
- console.log(chalk.dim("Waiting for authentication in browser..."));
862
+ console.log(chalk3.dim("Waiting for authentication in browser..."));
604
863
  try {
605
864
  const result = await data;
606
865
  const creds = {
@@ -615,74 +874,60 @@ var loginCommand = new Command("login").description("Authenticate with yapout").
615
874
  Math.round((result.expiresAt - Date.now()) / 864e5)
616
875
  );
617
876
  console.log(
618
- chalk.green("Logged in as ") + chalk.bold(result.email) + chalk.dim(
877
+ chalk3.green("Logged in as ") + chalk3.bold(result.email) + chalk3.dim(
619
878
  ` (expires in ${daysLeft} day${daysLeft === 1 ? "" : "s"})`
620
879
  )
621
880
  );
622
881
  try {
623
882
  const client = createConvexClient(creds.token);
624
- const deviceId = createHash("sha256").update(hostname() + userInfo().username).digest("hex").slice(0, 16);
625
- await client.mutation(anyApi2.functions.devices.registerDevice, {
883
+ const deviceId = createHash2("sha256").update(hostname2() + userInfo2().username).digest("hex").slice(0, 16);
884
+ await client.mutation(anyApi3.functions.devices.registerDevice, {
626
885
  deviceId,
627
- name: hostname(),
628
- cliVersion: CLI_VERSION
886
+ name: hostname2(),
887
+ cliVersion: CLI_VERSION2
629
888
  });
630
889
  } catch {
631
- console.warn(chalk.dim("Note: Could not register device. This is non-fatal."));
890
+ console.warn(chalk3.dim("Note: Could not register device. This is non-fatal."));
632
891
  }
633
892
  try {
634
893
  registerProtocolHandler();
635
894
  console.log(
636
- chalk.dim("Registered yapout:// protocol handler")
895
+ chalk3.dim("Registered yapout:// protocol handler")
637
896
  );
638
897
  } catch {
639
898
  }
899
+ try {
900
+ spawnServeDaemon(process.argv[1]);
901
+ console.log(chalk3.dim("Started yapout serve in the background"));
902
+ } catch {
903
+ }
640
904
  process.exit(0);
641
905
  } catch (err) {
642
- console.error(chalk.red(err.message));
906
+ console.error(chalk3.red(err.message));
643
907
  process.exit(1);
644
908
  }
645
909
  });
646
910
 
647
911
  // src/commands/logout.ts
648
- import { Command as Command2 } from "commander";
649
- import chalk2 from "chalk";
650
- var logoutCommand = new Command2("logout").description("Log out of yapout").action(() => {
912
+ import { Command as Command3 } from "commander";
913
+ import chalk4 from "chalk";
914
+ var logoutCommand = new Command3("logout").description("Log out of yapout").action(() => {
651
915
  deleteCredentials();
652
- console.log(chalk2.green("Logged out."));
916
+ console.log(chalk4.green("Logged out."));
653
917
  });
654
918
 
655
919
  // src/commands/link.ts
656
- import { Command as Command3 } from "commander";
657
- import { resolve as resolve2, join as join3 } from "path";
920
+ import { Command as Command4 } from "commander";
921
+ import { resolve as resolve2, join as join4 } from "path";
658
922
  import {
659
- existsSync as existsSync2,
923
+ existsSync as existsSync3,
660
924
  mkdirSync as mkdirSync3,
661
- readFileSync as readFileSync2,
662
- writeFileSync as writeFileSync3,
925
+ readFileSync as readFileSync3,
926
+ writeFileSync as writeFileSync4,
663
927
  appendFileSync
664
928
  } from "fs";
665
- import { hostname as hostname2 } from "os";
666
- import chalk4 from "chalk";
667
-
668
- // src/lib/auth.ts
669
- import chalk3 from "chalk";
670
- function requireAuth() {
671
- const creds = readCredentials();
672
- if (!creds) {
673
- console.error(
674
- chalk3.red("Not logged in.") + " Run " + chalk3.cyan("yapout login") + " first."
675
- );
676
- process.exit(1);
677
- }
678
- if (Date.now() > creds.expiresAt) {
679
- console.error(
680
- chalk3.red("Session expired.") + " Run " + chalk3.cyan("yapout login") + " to re-authenticate."
681
- );
682
- process.exit(1);
683
- }
684
- return creds;
685
- }
929
+ import { hostname as hostname3 } from "os";
930
+ import chalk5 from "chalk";
686
931
 
687
932
  // src/lib/prompts.ts
688
933
  import { select } from "@inquirer/prompts";
@@ -734,7 +979,7 @@ branch_prefix: feat
734
979
  # {{ticket.linearTicketId}}, {{ticket.id}}
735
980
  # commit_template: "{{ticket.type}}({{ticket.linearTicketId}}): {{ticket.title}}"
736
981
  `;
737
- var linkCommand = new Command3("link").description("Link the current directory to a yapout project").action(async () => {
982
+ var linkCommand = new Command4("link").description("Link the current directory to a yapout project").action(async () => {
738
983
  const creds = requireAuth();
739
984
  const cwd = resolveRepoRoot(resolve2(process.cwd()));
740
985
  const client = createConvexClient(creds.token);
@@ -746,25 +991,25 @@ var linkCommand = new Command3("link").description("Link the current directory t
746
991
  );
747
992
  } catch (err) {
748
993
  console.error(
749
- chalk4.red("Failed to fetch projects."),
994
+ chalk5.red("Failed to fetch projects."),
750
995
  err.message
751
996
  );
752
997
  process.exit(1);
753
998
  }
754
999
  if (projects.length === 0) {
755
1000
  console.error(
756
- chalk4.yellow("No projects found.") + " Create one at " + chalk4.cyan(`${getAppUrl()}/dashboard`)
1001
+ chalk5.yellow("No projects found.") + " Create one at " + chalk5.cyan(`${getAppUrl()}/dashboard`)
757
1002
  );
758
1003
  process.exit(1);
759
1004
  }
760
1005
  const selected = await pickProject(projects);
761
- const device = getOrCreateDeviceIdentity(hostname2());
1006
+ const device = getOrCreateDeviceIdentity(hostname3());
762
1007
  try {
763
1008
  await client.mutation(anyApi.functions.devices.registerDevice, {
764
1009
  deviceId: device.deviceId,
765
1010
  name: device.name,
766
1011
  cliVersion: getCliVersion(),
767
- machineHostname: hostname2()
1012
+ machineHostname: hostname3()
768
1013
  });
769
1014
  await client.mutation(anyApi.functions.projectCheckouts.linkCheckout, {
770
1015
  projectId: selected.id,
@@ -773,7 +1018,7 @@ var linkCommand = new Command3("link").description("Link the current directory t
773
1018
  });
774
1019
  } catch (err) {
775
1020
  console.warn(
776
- chalk4.yellow("Warning: failed to record this checkout \u2014 "),
1021
+ chalk5.yellow("Warning: failed to record this checkout \u2014 "),
777
1022
  err.message
778
1023
  );
779
1024
  }
@@ -782,17 +1027,17 @@ var linkCommand = new Command3("link").description("Link the current directory t
782
1027
  projectName: selected.name,
783
1028
  linkedAt: Date.now()
784
1029
  });
785
- const yapoutDir = join3(cwd, ".yapout");
786
- if (!existsSync2(yapoutDir)) {
1030
+ const yapoutDir = join4(cwd, ".yapout");
1031
+ if (!existsSync3(yapoutDir)) {
787
1032
  mkdirSync3(yapoutDir, { recursive: true });
788
1033
  }
789
- const configPath = join3(yapoutDir, "config.yml");
790
- if (!existsSync2(configPath)) {
791
- writeFileSync3(configPath, CONFIG_YAML_CONTENT);
1034
+ const configPath = join4(yapoutDir, "config.yml");
1035
+ if (!existsSync3(configPath)) {
1036
+ writeFileSync4(configPath, CONFIG_YAML_CONTENT);
792
1037
  }
793
- const gitignorePath = join3(cwd, ".gitignore");
794
- if (existsSync2(gitignorePath)) {
795
- const content = readFileSync2(gitignorePath, "utf-8");
1038
+ const gitignorePath = join4(cwd, ".gitignore");
1039
+ if (existsSync3(gitignorePath)) {
1040
+ const content = readFileSync3(gitignorePath, "utf-8");
796
1041
  if (!content.includes(".yapout/")) {
797
1042
  appendFileSync(
798
1043
  gitignorePath,
@@ -800,13 +1045,13 @@ var linkCommand = new Command3("link").description("Link the current directory t
800
1045
  );
801
1046
  }
802
1047
  } else {
803
- writeFileSync3(gitignorePath, "# yapout local config\n.yapout/\n");
1048
+ writeFileSync4(gitignorePath, "# yapout local config\n.yapout/\n");
804
1049
  }
805
- const mcpPath = join3(cwd, ".mcp.json");
1050
+ const mcpPath = join4(cwd, ".mcp.json");
806
1051
  let mcpConfig = {};
807
- if (existsSync2(mcpPath)) {
1052
+ if (existsSync3(mcpPath)) {
808
1053
  try {
809
- mcpConfig = JSON.parse(readFileSync2(mcpPath, "utf-8"));
1054
+ mcpConfig = JSON.parse(readFileSync3(mcpPath, "utf-8"));
810
1055
  } catch {
811
1056
  }
812
1057
  }
@@ -815,17 +1060,17 @@ var linkCommand = new Command3("link").description("Link the current directory t
815
1060
  command: "yapout",
816
1061
  args: ["mcp-server"]
817
1062
  };
818
- writeFileSync3(mcpPath, JSON.stringify(mcpConfig, null, 2) + "\n");
1063
+ writeFileSync4(mcpPath, JSON.stringify(mcpConfig, null, 2) + "\n");
819
1064
  const label = selected.githubRepoFullName ? `${selected.name} (${selected.githubRepoFullName})` : selected.name;
820
1065
  const orgSuffix = selected.org ? ` in ${selected.org.name}` : "";
821
1066
  console.log(
822
- chalk4.green(`Linked to ${label}${orgSuffix}.`) + " Claude Code will discover yapout tools automatically."
1067
+ chalk5.green(`Linked to ${label}${orgSuffix}.`) + " Claude Code will discover yapout tools automatically."
823
1068
  );
824
1069
  });
825
1070
  function getCliVersion() {
826
1071
  try {
827
1072
  const pkg = JSON.parse(
828
- readFileSync2(join3(import.meta.dirname, "..", "package.json"), "utf-8")
1073
+ readFileSync3(join4(import.meta.dirname, "..", "package.json"), "utf-8")
829
1074
  );
830
1075
  return pkg.version ?? "unknown";
831
1076
  } catch {
@@ -834,15 +1079,15 @@ function getCliVersion() {
834
1079
  }
835
1080
 
836
1081
  // src/commands/unlink.ts
837
- import { Command as Command4 } from "commander";
838
- import { resolve as resolve3, join as join4 } from "path";
839
- import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync4, rmSync } from "fs";
840
- import chalk5 from "chalk";
841
- var unlinkCommand = new Command4("unlink").description("Unlink the current directory from its yapout project").action(async () => {
1082
+ import { Command as Command5 } from "commander";
1083
+ import { resolve as resolve3, join as join5 } from "path";
1084
+ import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync5, rmSync } from "fs";
1085
+ import chalk6 from "chalk";
1086
+ var unlinkCommand = new Command5("unlink").description("Unlink the current directory from its yapout project").action(async () => {
842
1087
  const cwd = resolve3(process.cwd());
843
1088
  const mapping = getProjectMapping(cwd);
844
1089
  if (!mapping) {
845
- console.error(chalk5.yellow("No project linked to this directory."));
1090
+ console.error(chalk6.yellow("No project linked to this directory."));
846
1091
  process.exit(1);
847
1092
  }
848
1093
  const device = readDeviceIdentity();
@@ -856,26 +1101,26 @@ var unlinkCommand = new Command4("unlink").description("Unlink the current direc
856
1101
  });
857
1102
  } catch (err) {
858
1103
  console.warn(
859
- chalk5.yellow("Warning: failed to update server \u2014 "),
1104
+ chalk6.yellow("Warning: failed to update server \u2014 "),
860
1105
  err.message
861
1106
  );
862
1107
  }
863
1108
  }
864
1109
  removeProjectMapping(cwd);
865
- const yapoutDir = join4(cwd, ".yapout");
866
- if (existsSync3(yapoutDir)) {
1110
+ const yapoutDir = join5(cwd, ".yapout");
1111
+ if (existsSync4(yapoutDir)) {
867
1112
  rmSync(yapoutDir, { recursive: true });
868
1113
  }
869
- const mcpPath = join4(cwd, ".mcp.json");
870
- if (existsSync3(mcpPath)) {
1114
+ const mcpPath = join5(cwd, ".mcp.json");
1115
+ if (existsSync4(mcpPath)) {
871
1116
  try {
872
- const mcpConfig = JSON.parse(readFileSync3(mcpPath, "utf-8"));
1117
+ const mcpConfig = JSON.parse(readFileSync4(mcpPath, "utf-8"));
873
1118
  if (mcpConfig.mcpServers?.yapout) {
874
1119
  delete mcpConfig.mcpServers.yapout;
875
1120
  if (Object.keys(mcpConfig.mcpServers).length === 0) {
876
1121
  rmSync(mcpPath);
877
1122
  } else {
878
- writeFileSync4(
1123
+ writeFileSync5(
879
1124
  mcpPath,
880
1125
  JSON.stringify(mcpConfig, null, 2) + "\n"
881
1126
  );
@@ -884,23 +1129,23 @@ var unlinkCommand = new Command4("unlink").description("Unlink the current direc
884
1129
  } catch {
885
1130
  }
886
1131
  }
887
- console.log(chalk5.green("Unlinked."));
1132
+ console.log(chalk6.green("Unlinked."));
888
1133
  });
889
1134
 
890
1135
  // src/commands/status.ts
891
- import { Command as Command5 } from "commander";
1136
+ import { Command as Command6 } from "commander";
892
1137
  import { resolve as resolve4 } from "path";
893
- import chalk6 from "chalk";
894
- var statusCommand = new Command5("status").description("Show yapout status for this directory").action(() => {
895
- console.log(chalk6.bold("yapout status\n"));
1138
+ import chalk7 from "chalk";
1139
+ var statusCommand = new Command6("status").description("Show yapout status for this directory").action(() => {
1140
+ console.log(chalk7.bold("yapout status\n"));
896
1141
  const creds = readCredentials();
897
1142
  if (!creds) {
898
1143
  console.log(
899
- ` Auth: ${chalk6.red("Not logged in.")} Run ${chalk6.cyan("yapout login")}.`
1144
+ ` Auth: ${chalk7.red("Not logged in.")} Run ${chalk7.cyan("yapout login")}.`
900
1145
  );
901
1146
  } else if (Date.now() > creds.expiresAt) {
902
1147
  console.log(
903
- ` Auth: ${chalk6.red("Session expired.")} Run ${chalk6.cyan("yapout login")}.`
1148
+ ` Auth: ${chalk7.red("Session expired.")} Run ${chalk7.cyan("yapout login")}.`
904
1149
  );
905
1150
  } else {
906
1151
  const daysLeft = Math.max(
@@ -908,35 +1153,35 @@ var statusCommand = new Command5("status").description("Show yapout status for t
908
1153
  Math.round((creds.expiresAt - Date.now()) / 864e5)
909
1154
  );
910
1155
  console.log(
911
- ` Auth: ${chalk6.green(creds.email)} (expires in ${daysLeft} day${daysLeft === 1 ? "" : "s"})`
1156
+ ` Auth: ${chalk7.green(creds.email)} (expires in ${daysLeft} day${daysLeft === 1 ? "" : "s"})`
912
1157
  );
913
1158
  }
914
1159
  const cwd = resolve4(process.cwd());
915
1160
  const mapping = getProjectMapping(cwd);
916
1161
  if (!mapping) {
917
1162
  console.log(
918
- ` Project: ${chalk6.yellow("No project linked.")} Run ${chalk6.cyan("yapout link")} in a repo.`
1163
+ ` Project: ${chalk7.yellow("No project linked.")} Run ${chalk7.cyan("yapout link")} in a repo.`
919
1164
  );
920
1165
  } else {
921
- console.log(` Project: ${chalk6.green(mapping.projectName)}`);
1166
+ console.log(` Project: ${chalk7.green(mapping.projectName)}`);
922
1167
  }
923
- console.log(` Daemon: ${chalk6.dim("not running")}`);
924
- console.log(` Work: ${chalk6.dim("no active tickets")}`);
1168
+ console.log(` Daemon: ${chalk7.dim("not running")}`);
1169
+ console.log(` Work: ${chalk7.dim("no active tickets")}`);
925
1170
  console.log();
926
1171
  });
927
1172
 
928
1173
  // src/commands/init.ts
929
- import { Command as Command6 } from "commander";
930
- import { resolve as resolve5, join as join5 } from "path";
1174
+ import { Command as Command7 } from "commander";
1175
+ import { resolve as resolve5, join as join6 } from "path";
931
1176
  import {
932
- existsSync as existsSync4,
1177
+ existsSync as existsSync5,
933
1178
  mkdirSync as mkdirSync4,
934
- writeFileSync as writeFileSync5,
935
- readFileSync as readFileSync4,
1179
+ writeFileSync as writeFileSync6,
1180
+ readFileSync as readFileSync5,
936
1181
  appendFileSync as appendFileSync2
937
1182
  } from "fs";
938
- import { hostname as hostname3 } from "os";
939
- import chalk7 from "chalk";
1183
+ import { hostname as hostname4 } from "os";
1184
+ import chalk8 from "chalk";
940
1185
  var CONFIG_YAML_CONTENT2 = `# yapout local configuration
941
1186
  # See: https://docs.yapout.dev/cli/config
942
1187
 
@@ -961,7 +1206,7 @@ branch_prefix: feat
961
1206
  # {{ticket.linearTicketId}}, {{ticket.id}}
962
1207
  # commit_template: "{{ticket.type}}({{ticket.linearTicketId}}): {{ticket.title}}"
963
1208
  `;
964
- var initCommand = new Command6("init").description("Create a yapout project from the current repo and link it").argument("[name]", "Project name (defaults to repo name)").option("--org <slug>", "Org slug to create the project in (skips picker)").action(async (name, options) => {
1209
+ var initCommand = new Command7("init").description("Create a yapout project from the current repo and link it").argument("[name]", "Project name (defaults to repo name)").option("--org <slug>", "Org slug to create the project in (skips picker)").action(async (name, options) => {
965
1210
  const creds = requireAuth();
966
1211
  const cwd = resolveRepoRoot(resolve5(process.cwd()));
967
1212
  let repoFullName;
@@ -971,7 +1216,7 @@ var initCommand = new Command6("init").description("Create a yapout project from
971
1216
  defaultBranch = getDefaultBranch(cwd);
972
1217
  } catch (err) {
973
1218
  console.error(
974
- chalk7.red("Not a git repo with a GitHub remote."),
1219
+ chalk8.red("Not a git repo with a GitHub remote."),
975
1220
  err.message
976
1221
  );
977
1222
  process.exit(1);
@@ -986,14 +1231,14 @@ var initCommand = new Command6("init").description("Create a yapout project from
986
1231
  );
987
1232
  } catch (err) {
988
1233
  console.error(
989
- chalk7.red("Failed to load your orgs."),
1234
+ chalk8.red("Failed to load your orgs."),
990
1235
  err.message
991
1236
  );
992
1237
  process.exit(1);
993
1238
  }
994
1239
  if (!orgs || orgs.length === 0) {
995
1240
  console.error(
996
- chalk7.red(
1241
+ chalk8.red(
997
1242
  "You aren't a member of any org. Sign in to the web app once to create your personal org, then re-run."
998
1243
  )
999
1244
  );
@@ -1005,10 +1250,10 @@ var initCommand = new Command6("init").description("Create a yapout project from
1005
1250
  const match = orgs.find((o) => o.org.slug === options.org);
1006
1251
  if (!match) {
1007
1252
  console.error(
1008
- chalk7.red(`Org "${options.org}" not found among your memberships.`)
1253
+ chalk8.red(`Org "${options.org}" not found among your memberships.`)
1009
1254
  );
1010
1255
  console.error(
1011
- chalk7.dim(
1256
+ chalk8.dim(
1012
1257
  "Available: " + orgs.map((o) => o.org.slug).join(", ")
1013
1258
  )
1014
1259
  );
@@ -1020,7 +1265,7 @@ var initCommand = new Command6("init").description("Create a yapout project from
1020
1265
  chosenOrgId = orgs[0].org._id;
1021
1266
  chosenOrgName = orgs[0].org.name;
1022
1267
  console.log(
1023
- chalk7.dim(`Creating in `) + chalk7.cyan(chosenOrgName)
1268
+ chalk8.dim(`Creating in `) + chalk8.cyan(chosenOrgName)
1024
1269
  );
1025
1270
  } else {
1026
1271
  const picked = await pickOrg(
@@ -1034,17 +1279,17 @@ var initCommand = new Command6("init").description("Create a yapout project from
1034
1279
  chosenOrgId = picked.id;
1035
1280
  chosenOrgName = picked.name;
1036
1281
  }
1037
- const device = getOrCreateDeviceIdentity(hostname3());
1282
+ const device = getOrCreateDeviceIdentity(hostname4());
1038
1283
  try {
1039
1284
  await client.mutation(anyApi.functions.devices.registerDevice, {
1040
1285
  deviceId: device.deviceId,
1041
1286
  name: device.name,
1042
1287
  cliVersion: getCliVersion2(),
1043
- machineHostname: hostname3()
1288
+ machineHostname: hostname4()
1044
1289
  });
1045
1290
  } catch (err) {
1046
1291
  console.warn(
1047
- chalk7.yellow("Warning: device registration failed \u2014 "),
1292
+ chalk8.yellow("Warning: device registration failed \u2014 "),
1048
1293
  err.message
1049
1294
  );
1050
1295
  }
@@ -1061,7 +1306,7 @@ var initCommand = new Command6("init").description("Create a yapout project from
1061
1306
  );
1062
1307
  } catch (err) {
1063
1308
  console.error(
1064
- chalk7.red("Failed to create project."),
1309
+ chalk8.red("Failed to create project."),
1065
1310
  err.message
1066
1311
  );
1067
1312
  process.exit(1);
@@ -1074,7 +1319,7 @@ var initCommand = new Command6("init").description("Create a yapout project from
1074
1319
  });
1075
1320
  } catch (err) {
1076
1321
  console.warn(
1077
- chalk7.yellow("Warning: failed to record project checkout \u2014 "),
1322
+ chalk8.yellow("Warning: failed to record project checkout \u2014 "),
1078
1323
  err.message
1079
1324
  );
1080
1325
  }
@@ -1083,15 +1328,15 @@ var initCommand = new Command6("init").description("Create a yapout project from
1083
1328
  projectName: result.projectName,
1084
1329
  linkedAt: Date.now()
1085
1330
  });
1086
- const yapoutDir = join5(cwd, ".yapout");
1087
- if (!existsSync4(yapoutDir)) mkdirSync4(yapoutDir, { recursive: true });
1088
- const configPath = join5(yapoutDir, "config.yml");
1089
- if (!existsSync4(configPath)) {
1090
- writeFileSync5(configPath, CONFIG_YAML_CONTENT2);
1091
- }
1092
- const gitignorePath = join5(cwd, ".gitignore");
1093
- if (existsSync4(gitignorePath)) {
1094
- const content = readFileSync4(gitignorePath, "utf-8");
1331
+ const yapoutDir = join6(cwd, ".yapout");
1332
+ if (!existsSync5(yapoutDir)) mkdirSync4(yapoutDir, { recursive: true });
1333
+ const configPath = join6(yapoutDir, "config.yml");
1334
+ if (!existsSync5(configPath)) {
1335
+ writeFileSync6(configPath, CONFIG_YAML_CONTENT2);
1336
+ }
1337
+ const gitignorePath = join6(cwd, ".gitignore");
1338
+ if (existsSync5(gitignorePath)) {
1339
+ const content = readFileSync5(gitignorePath, "utf-8");
1095
1340
  if (!content.includes(".yapout/")) {
1096
1341
  appendFileSync2(
1097
1342
  gitignorePath,
@@ -1099,11 +1344,11 @@ var initCommand = new Command6("init").description("Create a yapout project from
1099
1344
  );
1100
1345
  }
1101
1346
  }
1102
- const mcpPath = join5(cwd, ".mcp.json");
1347
+ const mcpPath = join6(cwd, ".mcp.json");
1103
1348
  let mcpConfig = {};
1104
- if (existsSync4(mcpPath)) {
1349
+ if (existsSync5(mcpPath)) {
1105
1350
  try {
1106
- mcpConfig = JSON.parse(readFileSync4(mcpPath, "utf-8"));
1351
+ mcpConfig = JSON.parse(readFileSync5(mcpPath, "utf-8"));
1107
1352
  } catch {
1108
1353
  }
1109
1354
  }
@@ -1112,20 +1357,20 @@ var initCommand = new Command6("init").description("Create a yapout project from
1112
1357
  command: "yapout",
1113
1358
  args: ["mcp-server"]
1114
1359
  };
1115
- writeFileSync5(mcpPath, JSON.stringify(mcpConfig, null, 2) + "\n");
1360
+ writeFileSync6(mcpPath, JSON.stringify(mcpConfig, null, 2) + "\n");
1116
1361
  console.log(
1117
- chalk7.green(`Created project "${result.projectName}"`) + chalk7.dim(
1362
+ chalk8.green(`Created project "${result.projectName}"`) + chalk8.dim(
1118
1363
  ` in ${chosenOrgName} (${repoFullName}, branch: ${defaultBranch})`
1119
1364
  )
1120
1365
  );
1121
1366
  console.log(
1122
- chalk7.dim("Run ") + chalk7.cyan("yapout_compact") + chalk7.dim(" in Claude Code to generate project context.")
1367
+ chalk8.dim("Run ") + chalk8.cyan("yapout_compact") + chalk8.dim(" in Claude Code to generate project context.")
1123
1368
  );
1124
1369
  });
1125
1370
  function getCliVersion2() {
1126
1371
  try {
1127
1372
  const pkg = JSON.parse(
1128
- readFileSync4(join5(import.meta.dirname, "..", "package.json"), "utf-8")
1373
+ readFileSync5(join6(import.meta.dirname, "..", "package.json"), "utf-8")
1129
1374
  );
1130
1375
  return pkg.version ?? "unknown";
1131
1376
  } catch {
@@ -1134,18 +1379,18 @@ function getCliVersion2() {
1134
1379
  }
1135
1380
 
1136
1381
  // src/commands/mcp-server.ts
1137
- import { Command as Command7 } from "commander";
1382
+ import { Command as Command8 } from "commander";
1138
1383
 
1139
1384
  // src/mcp/server.ts
1140
1385
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
1141
1386
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
1142
1387
  import { ConvexHttpClient as ConvexHttpClient2 } from "convex/browser";
1143
- import { anyApi as anyApi3 } from "convex/server";
1388
+ import { anyApi as anyApi4 } from "convex/server";
1144
1389
 
1145
1390
  // src/mcp/tools/init.ts
1146
1391
  import { z } from "zod";
1147
- import { join as join6 } from "path";
1148
- import { existsSync as existsSync5, mkdirSync as mkdirSync5, writeFileSync as writeFileSync6 } from "fs";
1392
+ import { join as join7 } from "path";
1393
+ import { existsSync as existsSync6, mkdirSync as mkdirSync5, writeFileSync as writeFileSync7 } from "fs";
1149
1394
  import { stringify as yamlStringify } from "yaml";
1150
1395
  function registerInitTool(server, ctx) {
1151
1396
  server.tool(
@@ -1161,7 +1406,7 @@ function registerInitTool(server, ctx) {
1161
1406
  const defaultBranch = getDefaultBranch(repoRoot);
1162
1407
  const projectName = args.name || repoFullName.split("/")[1] || "unnamed";
1163
1408
  const result = await ctx.client.mutation(
1164
- anyApi3.functions.projects.createProjectFromCli,
1409
+ anyApi4.functions.projects.createProjectFromCli,
1165
1410
  {
1166
1411
  name: projectName,
1167
1412
  githubRepoFullName: repoFullName,
@@ -1176,11 +1421,11 @@ function registerInitTool(server, ctx) {
1176
1421
  projectName: result.projectName,
1177
1422
  linkedAt: Date.now()
1178
1423
  });
1179
- const yapoutDir = join6(repoRoot, ".yapout");
1180
- if (!existsSync5(yapoutDir)) mkdirSync5(yapoutDir, { recursive: true });
1181
- const configPath = join6(yapoutDir, "config.yml");
1182
- if (!existsSync5(configPath)) {
1183
- writeFileSync6(
1424
+ const yapoutDir = join7(repoRoot, ".yapout");
1425
+ if (!existsSync6(yapoutDir)) mkdirSync5(yapoutDir, { recursive: true });
1426
+ const configPath = join7(yapoutDir, "config.yml");
1427
+ if (!existsSync6(configPath)) {
1428
+ writeFileSync7(
1184
1429
  configPath,
1185
1430
  `# yapout local configuration
1186
1431
 
@@ -1249,7 +1494,7 @@ function registerCompactTool(server, ctx) {
1249
1494
  let lastUpdated;
1250
1495
  try {
1251
1496
  const project = await ctx.client.query(
1252
- anyApi3.functions.projects.getProject,
1497
+ anyApi4.functions.projects.getProject,
1253
1498
  { projectId: ctx.projectId }
1254
1499
  );
1255
1500
  currentContext = project?.contextSummary ?? void 0;
@@ -1292,7 +1537,7 @@ function registerUpdateContextTool(server, ctx) {
1292
1537
  };
1293
1538
  }
1294
1539
  await ctx.client.mutation(
1295
- anyApi3.functions.projects.updateProjectContext,
1540
+ anyApi4.functions.projects.updateProjectContext,
1296
1541
  {
1297
1542
  projectId: ctx.projectId,
1298
1543
  summary: args.summary
@@ -1362,7 +1607,7 @@ function registerQueueTool(server, ctx) {
1362
1607
  };
1363
1608
  }
1364
1609
  const data = await ctx.client.query(
1365
- anyApi3.functions.workQueue.getWorkQueue,
1610
+ anyApi4.functions.workQueue.getWorkQueue,
1366
1611
  { projectId: ctx.projectId }
1367
1612
  );
1368
1613
  if (!data) {
@@ -1471,7 +1716,7 @@ function registerGetBriefTool(server, ctx) {
1471
1716
  }
1472
1717
  try {
1473
1718
  const data = await ctx.client.query(
1474
- anyApi3.functions.findings.getFindingBrief,
1719
+ anyApi4.functions.findings.getFindingBrief,
1475
1720
  { findingId: itemId }
1476
1721
  );
1477
1722
  if (data) {
@@ -1485,7 +1730,7 @@ function registerGetBriefTool(server, ctx) {
1485
1730
  }
1486
1731
  try {
1487
1732
  const bundle = await ctx.client.query(
1488
- anyApi3.functions.bundles.getBundle,
1733
+ anyApi4.functions.bundles.getBundle,
1489
1734
  { bundleId: itemId }
1490
1735
  );
1491
1736
  if (bundle) {
@@ -1536,8 +1781,8 @@ function registerGetBriefTool(server, ctx) {
1536
1781
 
1537
1782
  // src/mcp/tools/claim.ts
1538
1783
  import { z as z5 } from "zod";
1539
- import { join as join7 } from "path";
1540
- import { existsSync as existsSync6, mkdirSync as mkdirSync6, writeFileSync as writeFileSync7 } from "fs";
1784
+ import { join as join8 } from "path";
1785
+ import { existsSync as existsSync7, mkdirSync as mkdirSync6, writeFileSync as writeFileSync8 } from "fs";
1541
1786
  function readBranchPrefix(cwd) {
1542
1787
  try {
1543
1788
  const config = readYapoutConfig(cwd);
@@ -1656,7 +1901,7 @@ function formatBundleBrief(bundle, projectContext) {
1656
1901
  async function detectWorkItemKind(client, workItemId) {
1657
1902
  try {
1658
1903
  const bundle = await client.query(
1659
- anyApi3.functions.bundles.getBundle,
1904
+ anyApi4.functions.bundles.getBundle,
1660
1905
  { bundleId: workItemId }
1661
1906
  );
1662
1907
  if (bundle) {
@@ -1710,7 +1955,7 @@ function registerClaimTool(server, ctx) {
1710
1955
  }
1711
1956
  async function claimStandalone(ctx, args, findingId) {
1712
1957
  const briefData = await ctx.client.query(
1713
- anyApi3.functions.findings.getFindingBrief,
1958
+ anyApi4.functions.findings.getFindingBrief,
1714
1959
  { findingId }
1715
1960
  );
1716
1961
  if (!briefData) {
@@ -1731,11 +1976,11 @@ async function claimStandalone(ctx, args, findingId) {
1731
1976
  const slug = slugify(finding.title);
1732
1977
  const branchName = linearIssueId ? `${prefix}/${linearIssueId.toLowerCase()}-${slug}` : `${prefix}/${slug}`;
1733
1978
  const localClaim = await ctx.client.mutation(
1734
- anyApi3.functions.findings.claimFindingLocal,
1979
+ anyApi4.functions.findings.claimFindingLocal,
1735
1980
  { findingId, branchName }
1736
1981
  );
1737
1982
  const claim = await ctx.client.mutation(
1738
- anyApi3.functions.workQueue.claimForImplementation,
1983
+ anyApi4.functions.workQueue.claimForImplementation,
1739
1984
  {
1740
1985
  projectId: ctx.projectId,
1741
1986
  workItemId: findingId,
@@ -1745,7 +1990,7 @@ async function claimStandalone(ctx, args, findingId) {
1745
1990
  if (linearIssueId && ctx.projectId) {
1746
1991
  try {
1747
1992
  await ctx.client.action(
1748
- anyApi3.functions.linearStatusMutations.moveIssueStatus,
1993
+ anyApi4.functions.linearStatusMutations.moveIssueStatus,
1749
1994
  {
1750
1995
  projectId: ctx.projectId,
1751
1996
  linearIssueId,
@@ -1802,11 +2047,11 @@ async function claimBundle(ctx, args, bundleId, bundleData) {
1802
2047
  };
1803
2048
  }
1804
2049
  const localClaim = await ctx.client.mutation(
1805
- anyApi3.functions.findings.claimFindingLocal,
2050
+ anyApi4.functions.findings.claimFindingLocal,
1806
2051
  { findingId: primaryFinding._id, branchName }
1807
2052
  );
1808
2053
  const claim = await ctx.client.mutation(
1809
- anyApi3.functions.workQueue.claimForImplementation,
2054
+ anyApi4.functions.workQueue.claimForImplementation,
1810
2055
  {
1811
2056
  projectId: ctx.projectId,
1812
2057
  workItemId: bundleId,
@@ -1817,7 +2062,7 @@ async function claimBundle(ctx, args, bundleId, bundleData) {
1817
2062
  if (f.linearIssueId && ctx.projectId) {
1818
2063
  try {
1819
2064
  await ctx.client.action(
1820
- anyApi3.functions.linearStatusMutations.moveIssueStatus,
2065
+ anyApi4.functions.linearStatusMutations.moveIssueStatus,
1821
2066
  {
1822
2067
  projectId: ctx.projectId,
1823
2068
  linearIssueId: f.linearIssueId,
@@ -1831,7 +2076,7 @@ async function claimBundle(ctx, args, bundleId, bundleData) {
1831
2076
  let projectContext;
1832
2077
  try {
1833
2078
  const briefData = await ctx.client.query(
1834
- anyApi3.functions.findings.getFindingBrief,
2079
+ anyApi4.functions.findings.getFindingBrief,
1835
2080
  { findingId: primaryFinding._id }
1836
2081
  );
1837
2082
  projectContext = briefData?.projectContext;
@@ -1871,7 +2116,7 @@ async function claimBundle(ctx, args, bundleId, bundleData) {
1871
2116
  async function getDefaultBranchForProject(ctx) {
1872
2117
  try {
1873
2118
  const data = await ctx.client.query(
1874
- anyApi3.functions.projects.getProject,
2119
+ anyApi4.functions.projects.getProject,
1875
2120
  { projectId: ctx.projectId }
1876
2121
  );
1877
2122
  return data?.githubDefaultBranch || "main";
@@ -1890,7 +2135,7 @@ async function setupWorktree(ctx, itemId, branchName, defaultBranch, brief, pipe
1890
2135
  writeBrief(worktreePath, brief);
1891
2136
  try {
1892
2137
  await ctx.client.mutation(
1893
- anyApi3.functions.pipelineRuns.reportDaemonEvent,
2138
+ anyApi4.functions.pipelineRuns.reportDaemonEvent,
1894
2139
  {
1895
2140
  pipelineRunId,
1896
2141
  event: "worktree_created",
@@ -1919,14 +2164,14 @@ async function setupWorktree(ctx, itemId, branchName, defaultBranch, brief, pipe
1919
2164
  };
1920
2165
  }
1921
2166
  function writeBrief(dir, brief) {
1922
- const yapoutDir = join7(dir, ".yapout");
1923
- if (!existsSync6(yapoutDir)) mkdirSync6(yapoutDir, { recursive: true });
1924
- writeFileSync7(join7(yapoutDir, "brief.md"), brief);
2167
+ const yapoutDir = join8(dir, ".yapout");
2168
+ if (!existsSync7(yapoutDir)) mkdirSync6(yapoutDir, { recursive: true });
2169
+ writeFileSync8(join8(yapoutDir, "brief.md"), brief);
1925
2170
  }
1926
2171
  async function reportClaimEvents(ctx, pipelineRunId, title, branchName) {
1927
2172
  try {
1928
2173
  await ctx.client.mutation(
1929
- anyApi3.functions.pipelineRuns.reportDaemonEvent,
2174
+ anyApi4.functions.pipelineRuns.reportDaemonEvent,
1930
2175
  {
1931
2176
  pipelineRunId,
1932
2177
  event: "daemon_claimed",
@@ -1934,7 +2179,7 @@ async function reportClaimEvents(ctx, pipelineRunId, title, branchName) {
1934
2179
  }
1935
2180
  );
1936
2181
  await ctx.client.mutation(
1937
- anyApi3.functions.pipelineRuns.reportDaemonEvent,
2182
+ anyApi4.functions.pipelineRuns.reportDaemonEvent,
1938
2183
  {
1939
2184
  pipelineRunId,
1940
2185
  event: "branch_created",
@@ -1961,7 +2206,7 @@ function registerEventTool(server, ctx) {
1961
2206
  async (args) => {
1962
2207
  try {
1963
2208
  await ctx.client.mutation(
1964
- anyApi3.functions.pipelineRuns.reportDaemonEvent,
2209
+ anyApi4.functions.pipelineRuns.reportDaemonEvent,
1965
2210
  {
1966
2211
  pipelineRunId: args.pipelineRunId,
1967
2212
  event: args.event,
@@ -2053,8 +2298,8 @@ async function createPullRequest(title, body, branch, base, repoFullName, cwd) {
2053
2298
  }
2054
2299
 
2055
2300
  // src/mcp/tools/ship.ts
2056
- import { join as join8 } from "path";
2057
- import { existsSync as existsSync7, readFileSync as readFileSync5 } from "fs";
2301
+ import { join as join9 } from "path";
2302
+ import { existsSync as existsSync8, readFileSync as readFileSync6 } from "fs";
2058
2303
  function buildCommitMessage(message, template, finding, allLinearIds) {
2059
2304
  if (message) return message;
2060
2305
  if (template) {
@@ -2105,9 +2350,9 @@ function registerShipTool(server, ctx) {
2105
2350
  let isBundle = false;
2106
2351
  let bundleTitle;
2107
2352
  try {
2108
- const briefPath = join8(gitCwd, ".yapout", "brief.md");
2109
- if (existsSync7(briefPath)) {
2110
- const brief = readFileSync5(briefPath, "utf-8");
2353
+ const briefPath = join9(gitCwd, ".yapout", "brief.md");
2354
+ if (existsSync8(briefPath)) {
2355
+ const brief = readFileSync6(briefPath, "utf-8");
2111
2356
  const bundleMatch = brief.match(/^# Bundle: (.+)$/m);
2112
2357
  if (bundleMatch) {
2113
2358
  isBundle = true;
@@ -2218,7 +2463,7 @@ function registerShipTool(server, ctx) {
2218
2463
  }
2219
2464
  try {
2220
2465
  await ctx.client.mutation(
2221
- anyApi3.functions.pipelineRuns.reportDaemonEvent,
2466
+ anyApi4.functions.pipelineRuns.reportDaemonEvent,
2222
2467
  {
2223
2468
  pipelineRunId: args.pipelineRunId,
2224
2469
  event: "push_completed",
@@ -2227,7 +2472,7 @@ function registerShipTool(server, ctx) {
2227
2472
  );
2228
2473
  if (prUrl) {
2229
2474
  await ctx.client.mutation(
2230
- anyApi3.functions.pipelineRuns.reportDaemonEvent,
2475
+ anyApi4.functions.pipelineRuns.reportDaemonEvent,
2231
2476
  {
2232
2477
  pipelineRunId: args.pipelineRunId,
2233
2478
  event: "pr_opened",
@@ -2239,7 +2484,7 @@ function registerShipTool(server, ctx) {
2239
2484
  }
2240
2485
  try {
2241
2486
  await ctx.client.mutation(
2242
- anyApi3.functions.pipelineRuns.completePipelineLocal,
2487
+ anyApi4.functions.pipelineRuns.completePipelineLocal,
2243
2488
  {
2244
2489
  pipelineRunId: args.pipelineRunId,
2245
2490
  githubPrNumber: prNumber,
@@ -2255,7 +2500,7 @@ function registerShipTool(server, ctx) {
2255
2500
  if (ctx.projectId) {
2256
2501
  try {
2257
2502
  await ctx.client.action(
2258
- anyApi3.functions.linearStatusMutations.moveIssueStatus,
2503
+ anyApi4.functions.linearStatusMutations.moveIssueStatus,
2259
2504
  {
2260
2505
  projectId: ctx.projectId,
2261
2506
  linearIssueId: linearId,
@@ -2267,7 +2512,7 @@ function registerShipTool(server, ctx) {
2267
2512
  if (prUrl) {
2268
2513
  try {
2269
2514
  await ctx.client.action(
2270
- anyApi3.functions.linearStatusMutations.addLinearComment,
2515
+ anyApi4.functions.linearStatusMutations.addLinearComment,
2271
2516
  {
2272
2517
  projectId: ctx.projectId,
2273
2518
  linearIssueId: linearId,
@@ -2285,7 +2530,7 @@ function registerShipTool(server, ctx) {
2285
2530
  result.worktreeCleaned = true;
2286
2531
  try {
2287
2532
  await ctx.client.mutation(
2288
- anyApi3.functions.pipelineRuns.reportDaemonEvent,
2533
+ anyApi4.functions.pipelineRuns.reportDaemonEvent,
2289
2534
  {
2290
2535
  pipelineRunId: args.pipelineRunId,
2291
2536
  event: "worktree_cleaned",
@@ -2376,7 +2621,7 @@ function registerCheckTool(server, ctx) {
2376
2621
  if (args.pipelineRunId) {
2377
2622
  try {
2378
2623
  await ctx.client.mutation(
2379
- anyApi3.functions.pipelineRuns.reportDaemonEvent,
2624
+ anyApi4.functions.pipelineRuns.reportDaemonEvent,
2380
2625
  {
2381
2626
  pipelineRunId: args.pipelineRunId,
2382
2627
  event: "running_check",
@@ -2396,7 +2641,7 @@ function registerCheckTool(server, ctx) {
2396
2641
  if (args.pipelineRunId) {
2397
2642
  try {
2398
2643
  await ctx.client.mutation(
2399
- anyApi3.functions.pipelineRuns.reportDaemonEvent,
2644
+ anyApi4.functions.pipelineRuns.reportDaemonEvent,
2400
2645
  {
2401
2646
  pipelineRunId: args.pipelineRunId,
2402
2647
  event: passed ? "check_passed" : "check_failed",
@@ -2428,8 +2673,8 @@ function registerCheckTool(server, ctx) {
2428
2673
 
2429
2674
  // src/mcp/tools/bundle.ts
2430
2675
  import { z as z9 } from "zod";
2431
- import { join as join9 } from "path";
2432
- import { existsSync as existsSync8, writeFileSync as writeFileSync8, mkdirSync as mkdirSync7 } from "fs";
2676
+ import { join as join10 } from "path";
2677
+ import { existsSync as existsSync9, writeFileSync as writeFileSync9, mkdirSync as mkdirSync7 } from "fs";
2433
2678
  function registerBundleTool(server, ctx) {
2434
2679
  server.tool(
2435
2680
  "yapout_bundle",
@@ -2440,11 +2685,11 @@ function registerBundleTool(server, ctx) {
2440
2685
  },
2441
2686
  async (args) => {
2442
2687
  const result = await ctx.client.mutation(
2443
- anyApi3.functions.bundles.createBundle,
2688
+ anyApi4.functions.bundles.createBundle,
2444
2689
  { leadFindingId: args.withFinding, joiningFindingId: args.findingId }
2445
2690
  );
2446
2691
  const bundledBrief = await ctx.client.query(
2447
- anyApi3.functions.bundles.getBundledBrief,
2692
+ anyApi4.functions.bundles.getBundledBrief,
2448
2693
  { bundleId: result.bundleId }
2449
2694
  );
2450
2695
  if (!bundledBrief) {
@@ -2482,9 +2727,9 @@ function registerBundleTool(server, ctx) {
2482
2727
  sections.push("## Project Context", "", bundledBrief.projectContext);
2483
2728
  }
2484
2729
  const combinedBrief = sections.join("\n");
2485
- const yapoutDir = join9(ctx.cwd, ".yapout");
2486
- if (!existsSync8(yapoutDir)) mkdirSync7(yapoutDir, { recursive: true });
2487
- writeFileSync8(join9(yapoutDir, "brief.md"), combinedBrief);
2730
+ const yapoutDir = join10(ctx.cwd, ".yapout");
2731
+ if (!existsSync9(yapoutDir)) mkdirSync7(yapoutDir, { recursive: true });
2732
+ writeFileSync9(join10(yapoutDir, "brief.md"), combinedBrief);
2488
2733
  return {
2489
2734
  content: [
2490
2735
  {
@@ -2541,7 +2786,7 @@ After calling this tool, you should:
2541
2786
  }
2542
2787
  try {
2543
2788
  const result = await ctx.client.mutation(
2544
- anyApi3.functions.localPipeline.claimForEnrichment,
2789
+ anyApi4.functions.localPipeline.claimForEnrichment,
2545
2790
  {
2546
2791
  projectId,
2547
2792
  ...args.findingId ? { findingId: args.findingId } : {}
@@ -2601,7 +2846,7 @@ function registerGetExistingFindingsTool(server, ctx) {
2601
2846
  }
2602
2847
  try {
2603
2848
  const findings = await ctx.client.query(
2604
- anyApi3.functions.localPipeline.getExistingFindingTitles,
2849
+ anyApi4.functions.localPipeline.getExistingFindingTitles,
2605
2850
  { projectId }
2606
2851
  );
2607
2852
  if (!findings || findings.length === 0) {
@@ -2697,12 +2942,13 @@ The finding transitions: enriching \u2192 enriched \u2192 ready.`,
2697
2942
  isOversized: z11.boolean().optional().describe("Set to true if this finding is too large for a single PR"),
2698
2943
  suggestedSplit: z11.array(z11.string()).optional().describe("If oversized: suggested sub-finding titles for breaking it down"),
2699
2944
  nature: z11.enum(["implementable", "operational", "spike"]).optional().describe("Override the finding's nature if enrichment reveals it should be reclassified"),
2945
+ cloudSafe: z11.boolean().optional().describe("Set to true ONLY if this is a small mechanical change a cloud agent can ship without sandbox testing \u2014 text/copy edits, classname tweaks, single-named-constant changes, \u226430 lines, single file, no logic/type/dependency changes. Default false."),
2700
2946
  sessionId: z11.string().optional().describe("Bulk enrichment session ID (from yapout_start_enrichment). Updates session stats.")
2701
2947
  },
2702
2948
  async (args) => {
2703
2949
  try {
2704
2950
  await ctx.client.mutation(
2705
- anyApi3.functions.localPipeline.saveLocalEnrichment,
2951
+ anyApi4.functions.localPipeline.saveLocalEnrichment,
2706
2952
  {
2707
2953
  findingId: args.findingId,
2708
2954
  title: args.title,
@@ -2712,15 +2958,16 @@ The finding transitions: enriching \u2192 enriched \u2192 ready.`,
2712
2958
  clarifications: args.clarifications,
2713
2959
  isOversized: args.isOversized,
2714
2960
  suggestedSplit: args.suggestedSplit,
2715
- nature: args.nature
2961
+ nature: args.nature,
2962
+ cloudSafe: args.cloudSafe
2716
2963
  }
2717
2964
  );
2718
2965
  await ctx.client.action(
2719
- anyApi3.functions.localPipeline.syncFindingToLinearLocal,
2966
+ anyApi4.functions.localPipeline.syncFindingToLinearLocal,
2720
2967
  { findingId: args.findingId }
2721
2968
  );
2722
2969
  const finding = await ctx.client.query(
2723
- anyApi3.functions.findings.getFinding,
2970
+ anyApi4.functions.findings.getFinding,
2724
2971
  { findingId: args.findingId }
2725
2972
  );
2726
2973
  if (args.sessionId) {
@@ -2779,7 +3026,7 @@ function registerSyncToLinearTool(server, ctx) {
2779
3026
  async (args) => {
2780
3027
  try {
2781
3028
  await ctx.client.action(
2782
- anyApi3.functions.localPipeline.syncFindingToLinearLocal,
3029
+ anyApi4.functions.localPipeline.syncFindingToLinearLocal,
2783
3030
  { findingId: args.findingId }
2784
3031
  );
2785
3032
  return {
@@ -2837,7 +3084,7 @@ function registerSubmitYapSessionTool(server, ctx) {
2837
3084
  }
2838
3085
  try {
2839
3086
  const captureId = await ctx.client.mutation(
2840
- anyApi3.functions.captures.createFromYapSession,
3087
+ anyApi4.functions.captures.createFromYapSession,
2841
3088
  {
2842
3089
  projectId: ctx.projectId,
2843
3090
  title: args.title,
@@ -3113,7 +3360,7 @@ function registerExtractFromYapTool(server, ctx) {
3113
3360
  }
3114
3361
  try {
3115
3362
  const result = await ctx.client.mutation(
3116
- anyApi3.functions.captures.extractFromYapSession,
3363
+ anyApi4.functions.captures.extractFromYapSession,
3117
3364
  {
3118
3365
  projectId: ctx.projectId,
3119
3366
  title: args.sessionTitle,
@@ -3225,7 +3472,7 @@ full spec \u2014 schema changes, files to modify, edge cases, and acceptance cri
3225
3472
  }
3226
3473
  try {
3227
3474
  const result = await ctx.client.mutation(
3228
- anyApi3.functions.localPipeline.decomposeIntoBundle,
3475
+ anyApi4.functions.localPipeline.decomposeIntoBundle,
3229
3476
  {
3230
3477
  findingId: args.findingId,
3231
3478
  bundleDescription: args.bundleDescription,
@@ -3298,7 +3545,7 @@ The finding must be in "enriching" or "enriched" status. It will be transitioned
3298
3545
  }
3299
3546
  try {
3300
3547
  const result = await ctx.client.mutation(
3301
- anyApi3.functions.findings.markDuplicate,
3548
+ anyApi4.functions.findings.markDuplicate,
3302
3549
  {
3303
3550
  findingId: args.findingId,
3304
3551
  duplicateOfLinearId: args.duplicateOfLinearId,
@@ -3361,7 +3608,7 @@ and issue counts so you can ask the user for confirmation before creating a new
3361
3608
  }
3362
3609
  try {
3363
3610
  const project = await ctx.client.query(
3364
- anyApi3.functions.projects.getProject,
3611
+ anyApi4.functions.projects.getProject,
3365
3612
  { projectId: ctx.projectId }
3366
3613
  );
3367
3614
  if (!project?.linearTeamId) {
@@ -3376,7 +3623,7 @@ and issue counts so you can ask the user for confirmation before creating a new
3376
3623
  };
3377
3624
  }
3378
3625
  const projects = await ctx.client.action(
3379
- anyApi3.functions.linearProjectsMutations.fetchProjectsDetailed,
3626
+ anyApi4.functions.linearProjectsMutations.fetchProjectsDetailed,
3380
3627
  { projectId: ctx.projectId, teamId: project.linearTeamId }
3381
3628
  );
3382
3629
  return {
@@ -3438,7 +3685,7 @@ Optionally filter by tags, capture, or explicit finding IDs.`,
3438
3685
  }
3439
3686
  try {
3440
3687
  const allFindings = await ctx.client.query(
3441
- anyApi3.functions.findings.getProjectFindings,
3688
+ anyApi4.functions.findings.getProjectFindings,
3442
3689
  { projectId }
3443
3690
  );
3444
3691
  let drafts = (allFindings ?? []).filter((f) => f.status === "draft");
@@ -3508,7 +3755,7 @@ When done=true, all findings have been processed.`,
3508
3755
  if (args.skip && args.skipFindingId) {
3509
3756
  try {
3510
3757
  await ctx.client.mutation(
3511
- anyApi3.functions.localPipeline.releaseEnrichmentClaim,
3758
+ anyApi4.functions.localPipeline.releaseEnrichmentClaim,
3512
3759
  { findingId: args.skipFindingId }
3513
3760
  );
3514
3761
  updateSessionStats(args.sessionId, {
@@ -3518,7 +3765,7 @@ When done=true, all findings have been processed.`,
3518
3765
  }
3519
3766
  }
3520
3767
  const allFindings = await ctx.client.query(
3521
- anyApi3.functions.findings.getProjectFindings,
3768
+ anyApi4.functions.findings.getProjectFindings,
3522
3769
  { projectId: session.projectId }
3523
3770
  );
3524
3771
  let drafts = (allFindings ?? []).filter(
@@ -3560,7 +3807,7 @@ When done=true, all findings have been processed.`,
3560
3807
  }
3561
3808
  const next = drafts[0];
3562
3809
  const claimResult = await ctx.client.mutation(
3563
- anyApi3.functions.localPipeline.claimForEnrichment,
3810
+ anyApi4.functions.localPipeline.claimForEnrichment,
3564
3811
  { projectId: session.projectId, findingId: next._id }
3565
3812
  );
3566
3813
  if (!claimResult) {
@@ -3641,7 +3888,7 @@ The bundle and all its findings transition to "enriching" status.`,
3641
3888
  async (args) => {
3642
3889
  try {
3643
3890
  const result = await ctx.client.mutation(
3644
- anyApi3.functions.bundles.claimBundleForEnrichment,
3891
+ anyApi4.functions.bundles.claimBundleForEnrichment,
3645
3892
  { bundleId: args.bundleId }
3646
3893
  );
3647
3894
  if (!result) {
@@ -3688,6 +3935,7 @@ Call yapout_sync_bundle_to_linear afterwards to create the Linear project.`,
3688
3935
  enrichedDescription: z21.string().describe("Bundle-level description \u2014 the cohesive story of what this bundle delivers"),
3689
3936
  acceptanceCriteria: z21.array(z21.string()).describe("Bundle-level acceptance criteria"),
3690
3937
  implementationBrief: z21.string().describe("Bundle-level implementation brief \u2014 overall approach, architecture decisions, key files"),
3938
+ cloudSafe: z21.boolean().optional().describe("Set true ONLY if the entire bundle is small mechanical work (\u226430 lines total, single-file ideally, text/className/constant edits). Default false. If unsure, false."),
3691
3939
  findings: z21.array(z21.object({
3692
3940
  findingId: z21.string().describe("Finding ID"),
3693
3941
  title: z21.string().describe("Refined finding title"),
@@ -3699,13 +3947,14 @@ Call yapout_sync_bundle_to_linear afterwards to create the Linear project.`,
3699
3947
  async (args) => {
3700
3948
  try {
3701
3949
  await ctx.client.mutation(
3702
- anyApi3.functions.bundles.saveBundleEnrichment,
3950
+ anyApi4.functions.bundles.saveBundleEnrichment,
3703
3951
  {
3704
3952
  bundleId: args.bundleId,
3705
3953
  title: args.title,
3706
3954
  enrichedDescription: args.enrichedDescription,
3707
3955
  acceptanceCriteria: args.acceptanceCriteria,
3708
3956
  implementationBrief: args.implementationBrief,
3957
+ cloudSafe: args.cloudSafe,
3709
3958
  findings: args.findings.map((f) => ({
3710
3959
  findingId: f.findingId,
3711
3960
  title: f.title,
@@ -3737,6 +3986,113 @@ Call yapout_sync_bundle_to_linear afterwards to create the Linear project.`,
3737
3986
  );
3738
3987
  }
3739
3988
 
3989
+ // src/mcp/tools/block-enrichment.ts
3990
+ import { z as z22 } from "zod";
3991
+ function registerBlockEnrichmentTool(server, ctx) {
3992
+ server.tool(
3993
+ "yapout_block_enrichment",
3994
+ `Refuse to enrich a finding because the user has not provided enough specifics for an autonomous agent to complete the work end-to-end.
3995
+
3996
+ Use this when the finding lacks any of: target file/component, intended behavior, scope boundaries, or success criteria. Do NOT produce a half-baked enrichment "for review" \u2014 block instead. The bar for "enriched" is "another agent can ship this with zero further input."
3997
+
3998
+ The finding must currently be in "enriching" status (claimed via yapout_get_unenriched_finding).
3999
+
4000
+ Transitions: enriching \u2192 needs_input. The user sees the blockerReason and questions in the UI, adds context, and resubmits.`,
4001
+ {
4002
+ findingId: z22.string().describe("The finding ID to block (currently in 'enriching')"),
4003
+ blockerReason: z22.string().describe("One sentence: why this finding cannot be enriched as written. State the missing piece concretely."),
4004
+ blockerQuestions: z22.array(z22.string()).min(1).describe("2-5 specific questions the user must answer before enrichment can succeed. Each must be answerable in a sentence or two.")
4005
+ },
4006
+ async (args) => {
4007
+ try {
4008
+ await ctx.client.mutation(
4009
+ anyApi4.functions.localPipeline.blockLocalEnrichment,
4010
+ {
4011
+ findingId: args.findingId,
4012
+ blockerReason: args.blockerReason,
4013
+ blockerQuestions: args.blockerQuestions
4014
+ }
4015
+ );
4016
+ return {
4017
+ content: [
4018
+ {
4019
+ type: "text",
4020
+ text: JSON.stringify(
4021
+ {
4022
+ findingId: args.findingId,
4023
+ status: "needs_input",
4024
+ message: "Enrichment blocked. The user will see your questions and resubmit with more context."
4025
+ },
4026
+ null,
4027
+ 2
4028
+ )
4029
+ }
4030
+ ]
4031
+ };
4032
+ } catch (err) {
4033
+ return {
4034
+ content: [
4035
+ {
4036
+ type: "text",
4037
+ text: `Error blocking enrichment: ${err.message}`
4038
+ }
4039
+ ],
4040
+ isError: true
4041
+ };
4042
+ }
4043
+ }
4044
+ );
4045
+ server.tool(
4046
+ "yapout_block_bundle_enrichment",
4047
+ `Refuse to enrich a bundle because the user has not provided enough specifics for an autonomous agent to ship it end-to-end.
4048
+
4049
+ Same semantics as yapout_block_enrichment but for an entire bundle. Bundle status transitions enriching \u2192 needs_input; child findings revert to draft.`,
4050
+ {
4051
+ bundleId: z22.string().describe("The bundle ID to block (currently in 'enriching')"),
4052
+ blockerReason: z22.string().describe("One sentence: why this bundle cannot be enriched."),
4053
+ blockerQuestions: z22.array(z22.string()).min(1).describe("2-5 specific questions the user must answer.")
4054
+ },
4055
+ async (args) => {
4056
+ try {
4057
+ await ctx.client.mutation(
4058
+ anyApi4.functions.bundles.blockBundleEnrichment,
4059
+ {
4060
+ bundleId: args.bundleId,
4061
+ blockerReason: args.blockerReason,
4062
+ blockerQuestions: args.blockerQuestions
4063
+ }
4064
+ );
4065
+ return {
4066
+ content: [
4067
+ {
4068
+ type: "text",
4069
+ text: JSON.stringify(
4070
+ {
4071
+ bundleId: args.bundleId,
4072
+ status: "needs_input",
4073
+ message: "Bundle enrichment blocked. The user will see your questions and resubmit."
4074
+ },
4075
+ null,
4076
+ 2
4077
+ )
4078
+ }
4079
+ ]
4080
+ };
4081
+ } catch (err) {
4082
+ return {
4083
+ content: [
4084
+ {
4085
+ type: "text",
4086
+ text: `Error blocking bundle enrichment: ${err.message}`
4087
+ }
4088
+ ],
4089
+ isError: true
4090
+ };
4091
+ }
4092
+ }
4093
+ );
4094
+ }
4095
+
3740
4096
  // src/mcp/server.ts
3741
4097
  async function startMcpServer() {
3742
4098
  const cwd = process.cwd();
@@ -3767,7 +4123,7 @@ async function startMcpServer() {
3767
4123
  };
3768
4124
  const server = new McpServer({
3769
4125
  name: "yapout",
3770
- version: "0.7.0"
4126
+ version: "0.9.0"
3771
4127
  });
3772
4128
  registerInitTool(server, ctx);
3773
4129
  registerCompactTool(server, ctx);
@@ -3793,45 +4149,46 @@ async function startMcpServer() {
3793
4149
  registerEnrichNextTool(server, ctx);
3794
4150
  registerEnrichBundleTool(server, ctx);
3795
4151
  registerSaveBundleEnrichmentTool(server, ctx);
4152
+ registerBlockEnrichmentTool(server, ctx);
3796
4153
  const transport = new StdioServerTransport();
3797
4154
  await server.connect(transport);
3798
4155
  }
3799
4156
 
3800
4157
  // src/commands/mcp-server.ts
3801
- var mcpServerCommand = new Command7("mcp-server").description("Start the MCP server (used by Claude Code)").action(async () => {
4158
+ var mcpServerCommand = new Command8("mcp-server").description("Start the MCP server (used by Claude Code)").action(async () => {
3802
4159
  await startMcpServer();
3803
4160
  });
3804
4161
 
3805
4162
  // src/commands/worktrees.ts
3806
- import { Command as Command8 } from "commander";
3807
- import chalk8 from "chalk";
4163
+ import { Command as Command9 } from "commander";
4164
+ import chalk9 from "chalk";
3808
4165
  import { resolve as resolve6 } from "path";
3809
- var worktreesCommand = new Command8("worktrees").description("List active yapout worktrees").action(() => {
4166
+ var worktreesCommand = new Command9("worktrees").description("List active yapout worktrees").action(() => {
3810
4167
  const cwd = resolve6(process.cwd());
3811
4168
  const worktrees = listWorktrees(cwd);
3812
4169
  if (worktrees.length === 0) {
3813
- console.log(chalk8.dim("No active yapout worktrees."));
4170
+ console.log(chalk9.dim("No active yapout worktrees."));
3814
4171
  return;
3815
4172
  }
3816
- console.log(chalk8.bold("Active worktrees:\n"));
4173
+ console.log(chalk9.bold("Active worktrees:\n"));
3817
4174
  for (const wt of worktrees) {
3818
4175
  console.log(
3819
- ` ${chalk8.cyan(wt.path)} ${chalk8.green(wt.branch)}` + (wt.ticketId ? ` ${chalk8.dim(`(${wt.ticketId})`)}` : "")
4176
+ ` ${chalk9.cyan(wt.path)} ${chalk9.green(wt.branch)}` + (wt.ticketId ? ` ${chalk9.dim(`(${wt.ticketId})`)}` : "")
3820
4177
  );
3821
4178
  }
3822
4179
  console.log();
3823
4180
  });
3824
4181
 
3825
4182
  // src/commands/clean.ts
3826
- import { Command as Command9 } from "commander";
3827
- import chalk9 from "chalk";
4183
+ import { Command as Command10 } from "commander";
4184
+ import chalk10 from "chalk";
3828
4185
  import { resolve as resolve7 } from "path";
3829
- var cleanCommand = new Command9("clean").description("Remove worktrees for completed or failed tickets").action(async () => {
4186
+ var cleanCommand = new Command10("clean").description("Remove worktrees for completed or failed tickets").action(async () => {
3830
4187
  const creds = requireAuth();
3831
4188
  const cwd = resolve7(process.cwd());
3832
4189
  const worktrees = listWorktrees(cwd);
3833
4190
  if (worktrees.length === 0) {
3834
- console.log(chalk9.dim("No worktrees to clean."));
4191
+ console.log(chalk10.dim("No worktrees to clean."));
3835
4192
  return;
3836
4193
  }
3837
4194
  const client = createConvexClient(creds.token);
@@ -3847,7 +4204,7 @@ var cleanCommand = new Command9("clean").description("Remove worktrees for compl
3847
4204
  if (isStale) {
3848
4205
  const label = ticket ? `${ticket.status}: ${ticket.title}` : "ticket not found";
3849
4206
  console.log(
3850
- chalk9.dim(`Removing worktree for ${wt.ticketId} (${label})...`)
4207
+ chalk10.dim(`Removing worktree for ${wt.ticketId} (${label})...`)
3851
4208
  );
3852
4209
  removeWorktree(cwd, wt.path);
3853
4210
  cleaned++;
@@ -3856,10 +4213,10 @@ var cleanCommand = new Command9("clean").description("Remove worktrees for compl
3856
4213
  }
3857
4214
  }
3858
4215
  if (cleaned === 0) {
3859
- console.log(chalk9.dim("All worktrees are still active."));
4216
+ console.log(chalk10.dim("All worktrees are still active."));
3860
4217
  } else {
3861
4218
  console.log(
3862
- chalk9.green(
4219
+ chalk10.green(
3863
4220
  `Cleaned ${cleaned} worktree${cleaned === 1 ? "" : "s"}.`
3864
4221
  )
3865
4222
  );
@@ -3867,27 +4224,27 @@ var cleanCommand = new Command9("clean").description("Remove worktrees for compl
3867
4224
  });
3868
4225
 
3869
4226
  // src/commands/watch.ts
3870
- import { Command as Command10 } from "commander";
4227
+ import { Command as Command11 } from "commander";
3871
4228
  import { resolve as resolve8 } from "path";
3872
4229
  import {
3873
- readFileSync as readFileSync6,
3874
- writeFileSync as writeFileSync9,
3875
- existsSync as existsSync10,
3876
- unlinkSync as unlinkSync3
4230
+ readFileSync as readFileSync7,
4231
+ writeFileSync as writeFileSync10,
4232
+ existsSync as existsSync11,
4233
+ unlinkSync as unlinkSync4
3877
4234
  } from "fs";
3878
- import { join as join11 } from "path";
3879
- import chalk11 from "chalk";
4235
+ import { join as join12 } from "path";
4236
+ import chalk12 from "chalk";
3880
4237
  import { ConvexHttpClient as ConvexHttpClient3 } from "convex/browser";
3881
4238
 
3882
4239
  // src/daemon/watcher.ts
3883
- import { anyApi as anyApi5 } from "convex/server";
4240
+ import { anyApi as anyApi6 } from "convex/server";
3884
4241
  import { execSync as execSync4 } from "child_process";
3885
- import { existsSync as existsSync9, mkdirSync as mkdirSync8 } from "fs";
3886
- import { join as join10 } from "path";
4242
+ import { existsSync as existsSync10, mkdirSync as mkdirSync8 } from "fs";
4243
+ import { join as join11 } from "path";
3887
4244
  import { hostname as osHostname } from "os";
3888
4245
 
3889
4246
  // src/daemon/heartbeat.ts
3890
- import { anyApi as anyApi4 } from "convex/server";
4247
+ import { anyApi as anyApi5 } from "convex/server";
3891
4248
  var HEARTBEAT_INTERVAL = 3e4;
3892
4249
  var Heartbeat = class {
3893
4250
  client;
@@ -3915,14 +4272,14 @@ var Heartbeat = class {
3915
4272
  if (currentTicketId) args.currentTicketId = currentTicketId;
3916
4273
  if (currentBranch) args.currentBranch = currentBranch;
3917
4274
  if (worktreePath) args.worktreePath = worktreePath;
3918
- await this.client.mutation(anyApi4.functions.agents.agentHeartbeat, args);
4275
+ await this.client.mutation(anyApi5.functions.agents.agentHeartbeat, args);
3919
4276
  } catch {
3920
4277
  }
3921
4278
  }
3922
4279
  };
3923
4280
 
3924
4281
  // src/daemon/spawner.ts
3925
- import { spawn } from "child_process";
4282
+ import { spawn as spawn2 } from "child_process";
3926
4283
 
3927
4284
  // src/daemon/notifications.ts
3928
4285
  var notifier = null;
@@ -4011,7 +4368,7 @@ var Spawner = class {
4011
4368
  ...process.env,
4012
4369
  YAPOUT_PROJECT_ID: this.projectId
4013
4370
  };
4014
- const child = spawn("claude", ["--dangerously-skip-permissions", prompt], {
4371
+ const child = spawn2("claude", ["--dangerously-skip-permissions", prompt], {
4015
4372
  cwd: worktreePath,
4016
4373
  stdio: ["pipe", "pipe", "pipe"],
4017
4374
  env,
@@ -4123,7 +4480,7 @@ var Spawner = class {
4123
4480
  };
4124
4481
 
4125
4482
  // src/daemon/display.ts
4126
- import chalk10 from "chalk";
4483
+ import chalk11 from "chalk";
4127
4484
  function formatElapsed(ms) {
4128
4485
  const seconds = Math.floor(ms / 1e3);
4129
4486
  if (seconds < 60) return `${seconds}s`;
@@ -4152,35 +4509,35 @@ function render(state) {
4152
4509
  if (queuedCount > 0) statusParts.push(`${queuedCount} queued`);
4153
4510
  const statusLine = statusParts.length > 0 ? statusParts.join(" | ") : "idle";
4154
4511
  lines.push(
4155
- `${chalk10.bold("yapout watch")} \u2014 ${state.projectName}`
4512
+ `${chalk11.bold("yapout watch")} \u2014 ${state.projectName}`
4156
4513
  );
4157
- lines.push(`${chalk10.green("Watching")} | ${statusLine}`);
4514
+ lines.push(`${chalk11.green("Watching")} | ${statusLine}`);
4158
4515
  lines.push("");
4159
4516
  for (const agent of state.activeAgents) {
4160
4517
  const elapsed = formatElapsed(now - agent.startedAt);
4161
- const wt = agent.worktree ? chalk10.dim(`worktree/${agent.ticketRef.toLowerCase()}`) : "";
4518
+ const wt = agent.worktree ? chalk11.dim(`worktree/${agent.ticketRef.toLowerCase()}`) : "";
4162
4519
  lines.push(
4163
- ` ${chalk10.green("\u25CF")} ${chalk10.bold(agent.ticketRef)} ${chalk10.dim(truncate2(`"${agent.title}"`, 35))} ${chalk10.yellow(agent.phase)} ${wt} ${chalk10.dim(elapsed)}`
4520
+ ` ${chalk11.green("\u25CF")} ${chalk11.bold(agent.ticketRef)} ${chalk11.dim(truncate2(`"${agent.title}"`, 35))} ${chalk11.yellow(agent.phase)} ${wt} ${chalk11.dim(elapsed)}`
4164
4521
  );
4165
4522
  }
4166
4523
  for (const ticket of state.queuedTickets) {
4167
4524
  lines.push(
4168
- ` ${chalk10.dim("\u25CB")} ${chalk10.bold(ticket.ticketRef)} ${chalk10.dim(truncate2(`"${ticket.title}"`, 35))} ${chalk10.dim("queued")}`
4525
+ ` ${chalk11.dim("\u25CB")} ${chalk11.bold(ticket.ticketRef)} ${chalk11.dim(truncate2(`"${ticket.title}"`, 35))} ${chalk11.dim("queued")}`
4169
4526
  );
4170
4527
  }
4171
4528
  if (state.activeAgents.length > 0 || state.queuedTickets.length > 0) {
4172
4529
  lines.push("");
4173
4530
  }
4174
4531
  if (state.recentEvents.length > 0) {
4175
- lines.push(chalk10.dim("Recent:"));
4532
+ lines.push(chalk11.dim("Recent:"));
4176
4533
  for (const event of state.recentEvents.slice(0, 8)) {
4177
4534
  lines.push(
4178
- ` ${chalk10.dim(formatTime(event.time))} ${event.icon} ${event.message}`
4535
+ ` ${chalk11.dim(formatTime(event.time))} ${event.icon} ${event.message}`
4179
4536
  );
4180
4537
  }
4181
4538
  lines.push("");
4182
4539
  }
4183
- lines.push(chalk10.dim("Ctrl+C to stop (agents will finish)"));
4540
+ lines.push(chalk11.dim("Ctrl+C to stop (agents will finish)"));
4184
4541
  return lines.join("\n");
4185
4542
  }
4186
4543
  function clearAndRender(state) {
@@ -4222,7 +4579,7 @@ var Watcher = class {
4222
4579
  );
4223
4580
  process.exit(1);
4224
4581
  }
4225
- await this.client.mutation(anyApi5.functions.agents.registerAgent, {
4582
+ await this.client.mutation(anyApi6.functions.agents.registerAgent, {
4226
4583
  projectId: this.options.projectId,
4227
4584
  sessionId: this.sessionId,
4228
4585
  machineHostname: this.getHostname()
@@ -4258,7 +4615,7 @@ Waiting for ${this.spawner.activeCount} agent(s) to finish...`
4258
4615
  await this.spawner.gracefulShutdown();
4259
4616
  }
4260
4617
  try {
4261
- await this.client.mutation(anyApi5.functions.agents.unregisterAgent, {
4618
+ await this.client.mutation(anyApi6.functions.agents.unregisterAgent, {
4262
4619
  sessionId: this.sessionId
4263
4620
  });
4264
4621
  } catch {
@@ -4272,7 +4629,7 @@ Waiting for ${this.spawner.activeCount} agent(s) to finish...`
4272
4629
  }
4273
4630
  this.heartbeat.stop();
4274
4631
  this.spawner.forceKill();
4275
- this.client.mutation(anyApi5.functions.agents.unregisterAgent, {
4632
+ this.client.mutation(anyApi6.functions.agents.unregisterAgent, {
4276
4633
  sessionId: this.sessionId
4277
4634
  }).catch(() => {
4278
4635
  });
@@ -4314,7 +4671,7 @@ Waiting for ${this.spawner.activeCount} agent(s) to finish...`
4314
4671
  }
4315
4672
  async checkForEnrichmentWork(maxSlots) {
4316
4673
  const tickets = await this.client.query(
4317
- anyApi5.functions.localPipeline.getUnenrichedTickets,
4674
+ anyApi6.functions.localPipeline.getUnenrichedTickets,
4318
4675
  { projectId: this.options.projectId }
4319
4676
  );
4320
4677
  if (!tickets || tickets.length === 0) return;
@@ -4343,7 +4700,7 @@ Waiting for ${this.spawner.activeCount} agent(s) to finish...`
4343
4700
  }
4344
4701
  async checkForImplementationWork(maxSlots) {
4345
4702
  const data = await this.client.query(
4346
- anyApi5.functions.tickets.getLocalQueuedTickets,
4703
+ anyApi6.functions.tickets.getLocalQueuedTickets,
4347
4704
  { projectId: this.options.projectId }
4348
4705
  );
4349
4706
  if (!data || data.ready.length === 0) return;
@@ -4375,10 +4732,10 @@ Waiting for ${this.spawner.activeCount} agent(s) to finish...`
4375
4732
  }
4376
4733
  ensureEnrichWorktree() {
4377
4734
  const wtDir = getWorktreesDir(this.options.cwd);
4378
- const enrichPath = join10(wtDir, "_enrich");
4379
- if (existsSync9(enrichPath)) return enrichPath;
4735
+ const enrichPath = join11(wtDir, "_enrich");
4736
+ if (existsSync10(enrichPath)) return enrichPath;
4380
4737
  try {
4381
- if (!existsSync9(wtDir)) mkdirSync8(wtDir, { recursive: true });
4738
+ if (!existsSync10(wtDir)) mkdirSync8(wtDir, { recursive: true });
4382
4739
  const defaultBranch = getDefaultBranch(this.options.cwd);
4383
4740
  execSync4(
4384
4741
  `git worktree add "${enrichPath}" origin/${defaultBranch}`,
@@ -4416,7 +4773,7 @@ Waiting for ${this.spawner.activeCount} agent(s) to finish...`
4416
4773
  if (wt.ticketId) {
4417
4774
  try {
4418
4775
  const ticket = await this.client.query(
4419
- anyApi5.functions.tickets.getTicket,
4776
+ anyApi6.functions.tickets.getTicket,
4420
4777
  { ticketId: wt.ticketId }
4421
4778
  );
4422
4779
  if (ticket && (ticket.status === "failed" || ticket.status === "workflow2_done")) {
@@ -4481,38 +4838,38 @@ Waiting for ${this.spawner.activeCount} agent(s) to finish...`
4481
4838
  };
4482
4839
 
4483
4840
  // src/commands/watch.ts
4484
- var PID_FILE = join11(getYapoutDir(), "watch.pid");
4485
- var LOG_FILE = join11(getYapoutDir(), "watch.log");
4486
- var watchCommand = new Command10("watch").description("Watch for work and spawn Claude Code agents").option("--bg", "Run in background (detached)").option("--stop", "Stop the background watcher").option("--status", "Check if watcher is running").option("--force", "Force kill on Ctrl+C (don't wait for agents)").action(async (opts) => {
4841
+ var PID_FILE2 = join12(getYapoutDir(), "watch.pid");
4842
+ var LOG_FILE2 = join12(getYapoutDir(), "watch.log");
4843
+ var watchCommand = new Command11("watch").description("Watch for work and spawn Claude Code agents").option("--bg", "Run in background (detached)").option("--stop", "Stop the background watcher").option("--status", "Check if watcher is running").option("--force", "Force kill on Ctrl+C (don't wait for agents)").action(async (opts) => {
4487
4844
  if (opts.status) {
4488
- if (existsSync10(PID_FILE)) {
4489
- const pid = parseInt(readFileSync6(PID_FILE, "utf-8").trim(), 10);
4490
- if (isProcessRunning(pid)) {
4845
+ if (existsSync11(PID_FILE2)) {
4846
+ const pid = parseInt(readFileSync7(PID_FILE2, "utf-8").trim(), 10);
4847
+ if (isProcessRunning2(pid)) {
4491
4848
  console.log(
4492
- chalk11.green("Watcher is running") + chalk11.dim(` (PID ${pid})`)
4849
+ chalk12.green("Watcher is running") + chalk12.dim(` (PID ${pid})`)
4493
4850
  );
4494
4851
  } else {
4495
- console.log(chalk11.dim("Watcher is not running (stale PID file)"));
4496
- unlinkSync3(PID_FILE);
4852
+ console.log(chalk12.dim("Watcher is not running (stale PID file)"));
4853
+ unlinkSync4(PID_FILE2);
4497
4854
  }
4498
4855
  } else {
4499
- console.log(chalk11.dim("Watcher is not running"));
4856
+ console.log(chalk12.dim("Watcher is not running"));
4500
4857
  }
4501
4858
  return;
4502
4859
  }
4503
4860
  if (opts.stop) {
4504
- if (!existsSync10(PID_FILE)) {
4505
- console.log(chalk11.dim("No watcher running"));
4861
+ if (!existsSync11(PID_FILE2)) {
4862
+ console.log(chalk12.dim("No watcher running"));
4506
4863
  return;
4507
4864
  }
4508
- const pid = parseInt(readFileSync6(PID_FILE, "utf-8").trim(), 10);
4865
+ const pid = parseInt(readFileSync7(PID_FILE2, "utf-8").trim(), 10);
4509
4866
  try {
4510
4867
  process.kill(pid, "SIGTERM");
4511
- console.log(chalk11.green(`Stopped watcher (PID ${pid})`));
4868
+ console.log(chalk12.green(`Stopped watcher (PID ${pid})`));
4512
4869
  } catch {
4513
- console.log(chalk11.dim("Watcher already stopped"));
4870
+ console.log(chalk12.dim("Watcher already stopped"));
4514
4871
  }
4515
- unlinkSync3(PID_FILE);
4872
+ unlinkSync4(PID_FILE2);
4516
4873
  return;
4517
4874
  }
4518
4875
  const creds = requireAuth();
@@ -4520,7 +4877,7 @@ var watchCommand = new Command10("watch").description("Watch for work and spawn
4520
4877
  const mapping = getProjectMapping(cwd);
4521
4878
  if (!mapping) {
4522
4879
  console.error(
4523
- chalk11.red("No project linked.") + " Run " + chalk11.cyan("yapout link") + " in a repo."
4880
+ chalk12.red("No project linked.") + " Run " + chalk12.cyan("yapout link") + " in a repo."
4524
4881
  );
4525
4882
  process.exit(1);
4526
4883
  }
@@ -4528,16 +4885,16 @@ var watchCommand = new Command10("watch").description("Watch for work and spawn
4528
4885
  const client = new ConvexHttpClient3(getConvexUrl());
4529
4886
  client.setAuth(creds.token);
4530
4887
  if (opts.bg) {
4531
- writeFileSync9(PID_FILE, process.pid.toString());
4888
+ writeFileSync10(PID_FILE2, process.pid.toString());
4532
4889
  console.log(
4533
- chalk11.green("Watcher started in background") + chalk11.dim(` (PID ${process.pid}, log: ${LOG_FILE})`)
4890
+ chalk12.green("Watcher started in background") + chalk12.dim(` (PID ${process.pid}, log: ${LOG_FILE2})`)
4534
4891
  );
4535
4892
  }
4536
- console.log(chalk11.bold(`yapout watch v${"0.7.0"}`));
4893
+ console.log(chalk12.bold(`yapout watch v${"0.9.0"}`));
4537
4894
  console.log(
4538
- `Project: ${chalk11.green(mapping.projectName)} (${mapping.projectId})`
4895
+ `Project: ${chalk12.green(mapping.projectName)} (${mapping.projectId})`
4539
4896
  );
4540
- console.log(chalk11.dim("Watching..."));
4897
+ console.log(chalk12.dim("Watching..."));
4541
4898
  console.log();
4542
4899
  const watcher = new Watcher(
4543
4900
  {
@@ -4554,17 +4911,17 @@ var watchCommand = new Command10("watch").description("Watch for work and spawn
4554
4911
  const shutdown = async () => {
4555
4912
  if (shuttingDown) {
4556
4913
  watcher.forceStop();
4557
- if (existsSync10(PID_FILE)) unlinkSync3(PID_FILE);
4914
+ if (existsSync11(PID_FILE2)) unlinkSync4(PID_FILE2);
4558
4915
  process.exit(0);
4559
4916
  }
4560
4917
  shuttingDown = true;
4561
- console.log(chalk11.dim("\nShutting down..."));
4918
+ console.log(chalk12.dim("\nShutting down..."));
4562
4919
  if (opts.force) {
4563
4920
  watcher.forceStop();
4564
4921
  } else {
4565
4922
  await watcher.stop();
4566
4923
  }
4567
- if (existsSync10(PID_FILE)) unlinkSync3(PID_FILE);
4924
+ if (existsSync11(PID_FILE2)) unlinkSync4(PID_FILE2);
4568
4925
  process.exit(0);
4569
4926
  };
4570
4927
  process.on("SIGINT", () => {
@@ -4577,7 +4934,7 @@ var watchCommand = new Command10("watch").description("Watch for work and spawn
4577
4934
  await new Promise(() => {
4578
4935
  });
4579
4936
  });
4580
- function isProcessRunning(pid) {
4937
+ function isProcessRunning2(pid) {
4581
4938
  try {
4582
4939
  process.kill(pid, 0);
4583
4940
  return true;
@@ -4587,88 +4944,88 @@ function isProcessRunning(pid) {
4587
4944
  }
4588
4945
 
4589
4946
  // src/commands/queue.ts
4590
- import { Command as Command11 } from "commander";
4947
+ import { Command as Command12 } from "commander";
4591
4948
  import { resolve as resolve9 } from "path";
4592
- import chalk12 from "chalk";
4593
- var queueCommand = new Command11("queue").description("Show pipeline state \u2014 what's ready, blocked, and pending").action(async () => {
4949
+ import chalk13 from "chalk";
4950
+ var queueCommand = new Command12("queue").description("Show pipeline state \u2014 what's ready, blocked, and pending").action(async () => {
4594
4951
  const creds = requireAuth();
4595
4952
  const cwd = resolve9(process.cwd());
4596
4953
  const mapping = getProjectMapping(cwd);
4597
4954
  if (!mapping) {
4598
4955
  console.error(
4599
- chalk12.red("No project linked.") + " Run " + chalk12.cyan("yapout link") + " in a repo."
4956
+ chalk13.red("No project linked.") + " Run " + chalk13.cyan("yapout link") + " in a repo."
4600
4957
  );
4601
4958
  process.exit(1);
4602
4959
  }
4603
4960
  const client = createConvexClient(creds.token);
4604
- const { anyApi: anyApi6 } = await import("convex/server");
4961
+ const { anyApi: anyApi7 } = await import("convex/server");
4605
4962
  const [queueData, unenriched, pending] = await Promise.all([
4606
- client.query(anyApi6.functions.tickets.getLocalQueuedTickets, {
4963
+ client.query(anyApi7.functions.tickets.getLocalQueuedTickets, {
4607
4964
  projectId: mapping.projectId
4608
4965
  }),
4609
- client.query(anyApi6.functions.localPipeline.getUnenrichedTickets, {
4966
+ client.query(anyApi7.functions.localPipeline.getUnenrichedTickets, {
4610
4967
  projectId: mapping.projectId
4611
4968
  }),
4612
- client.query(anyApi6.functions.localPipeline.getPendingSources, {
4969
+ client.query(anyApi7.functions.localPipeline.getPendingSources, {
4613
4970
  projectId: mapping.projectId
4614
4971
  })
4615
4972
  ]);
4616
4973
  console.log();
4617
4974
  if (queueData?.ready && queueData.ready.length > 0) {
4618
- console.log(chalk12.bold("Ready to implement:"));
4975
+ console.log(chalk13.bold("Ready to implement:"));
4619
4976
  for (const t of queueData.ready) {
4620
4977
  const ref = t.linearTicketId ?? t.ticketId;
4621
4978
  const prio = colorPriority(t.priority);
4622
4979
  console.log(
4623
- ` ${chalk12.bold(ref)} ${t.title.slice(0, 45).padEnd(45)} ${prio} ${chalk12.dim(t.type)}`
4980
+ ` ${chalk13.bold(ref)} ${t.title.slice(0, 45).padEnd(45)} ${prio} ${chalk13.dim(t.type)}`
4624
4981
  );
4625
4982
  }
4626
4983
  console.log();
4627
4984
  }
4628
4985
  if (queueData?.blocked && queueData.blocked.length > 0) {
4629
- console.log(chalk12.bold("Blocked:"));
4986
+ console.log(chalk13.bold("Blocked:"));
4630
4987
  for (const t of queueData.blocked) {
4631
4988
  console.log(
4632
- ` ${chalk12.bold(t.ticketId.slice(-6))} ${t.title.slice(0, 45)} ${chalk12.red("blocked by " + t.blockedBy.map((id) => id.slice(-6)).join(", "))}`
4989
+ ` ${chalk13.bold(t.ticketId.slice(-6))} ${t.title.slice(0, 45)} ${chalk13.red("blocked by " + t.blockedBy.map((id) => id.slice(-6)).join(", "))}`
4633
4990
  );
4634
4991
  }
4635
4992
  console.log();
4636
4993
  }
4637
4994
  if (unenriched && unenriched.length > 0) {
4638
- console.log(chalk12.bold("Needs enrichment:"));
4995
+ console.log(chalk13.bold("Needs enrichment:"));
4639
4996
  for (const t of unenriched) {
4640
4997
  const ref = t.ticketId;
4641
4998
  console.log(
4642
- ` ${chalk12.bold(ref.slice(-6))} ${t.title.slice(0, 45).padEnd(45)} ${chalk12.dim(t.priority)}`
4999
+ ` ${chalk13.bold(ref.slice(-6))} ${t.title.slice(0, 45).padEnd(45)} ${chalk13.dim(t.priority)}`
4643
5000
  );
4644
5001
  }
4645
5002
  console.log();
4646
5003
  }
4647
5004
  if (pending && pending.length > 0) {
4648
- console.log(chalk12.bold("Pending extraction:"));
5005
+ console.log(chalk13.bold("Pending extraction:"));
4649
5006
  for (const t of pending) {
4650
5007
  const ago = formatAgo(Date.now() - t.createdAt);
4651
5008
  console.log(
4652
- ` "${t.meetingTitle ?? "Untitled"}" ${chalk12.dim(`(uploaded ${ago})`)}`
5009
+ ` "${t.meetingTitle ?? "Untitled"}" ${chalk13.dim(`(uploaded ${ago})`)}`
4653
5010
  );
4654
5011
  }
4655
5012
  console.log();
4656
5013
  }
4657
5014
  if ((!queueData?.ready || queueData.ready.length === 0) && (!unenriched || unenriched.length === 0) && (!pending || pending.length === 0)) {
4658
- console.log(chalk12.dim("Queue is empty. Upload a transcript to get started."));
5015
+ console.log(chalk13.dim("Queue is empty. Upload a transcript to get started."));
4659
5016
  console.log();
4660
5017
  }
4661
5018
  });
4662
5019
  function colorPriority(p) {
4663
5020
  switch (p) {
4664
5021
  case "urgent":
4665
- return chalk12.red(p);
5022
+ return chalk13.red(p);
4666
5023
  case "high":
4667
- return chalk12.yellow(p);
5024
+ return chalk13.yellow(p);
4668
5025
  case "medium":
4669
- return chalk12.white(p);
5026
+ return chalk13.white(p);
4670
5027
  case "low":
4671
- return chalk12.dim(p);
5028
+ return chalk13.dim(p);
4672
5029
  default:
4673
5030
  return p;
4674
5031
  }
@@ -4684,29 +5041,29 @@ function formatAgo(ms) {
4684
5041
  }
4685
5042
 
4686
5043
  // src/commands/next.ts
4687
- import { Command as Command12 } from "commander";
5044
+ import { Command as Command13 } from "commander";
4688
5045
  import { resolve as resolve10 } from "path";
4689
- import { writeFileSync as writeFileSync10 } from "fs";
4690
- import { join as join12 } from "path";
4691
- import chalk13 from "chalk";
4692
- var nextCommand = new Command12("next").description("Claim the highest priority ticket and set up for implementation").option("--worktree", "Create a git worktree instead of checking out a branch").action(async (opts) => {
5046
+ import { writeFileSync as writeFileSync11 } from "fs";
5047
+ import { join as join13 } from "path";
5048
+ import chalk14 from "chalk";
5049
+ var nextCommand = new Command13("next").description("Claim the highest priority ticket and set up for implementation").option("--worktree", "Create a git worktree instead of checking out a branch").action(async (opts) => {
4693
5050
  const creds = requireAuth();
4694
5051
  const cwd = resolve10(process.cwd());
4695
5052
  const mapping = getProjectMapping(cwd);
4696
5053
  if (!mapping) {
4697
5054
  console.error(
4698
- chalk13.red("No project linked.") + " Run " + chalk13.cyan("yapout link") + " in a repo."
5055
+ chalk14.red("No project linked.") + " Run " + chalk14.cyan("yapout link") + " in a repo."
4699
5056
  );
4700
5057
  process.exit(1);
4701
5058
  }
4702
5059
  const client = createConvexClient(creds.token);
4703
- const { anyApi: anyApi6 } = await import("convex/server");
5060
+ const { anyApi: anyApi7 } = await import("convex/server");
4704
5061
  const data = await client.query(
4705
- anyApi6.functions.tickets.getLocalQueuedTickets,
5062
+ anyApi7.functions.tickets.getLocalQueuedTickets,
4706
5063
  { projectId: mapping.projectId }
4707
5064
  );
4708
5065
  if (!data?.ready || data.ready.length === 0) {
4709
- console.log(chalk13.dim("No tickets ready for implementation."));
5066
+ console.log(chalk14.dim("No tickets ready for implementation."));
4710
5067
  return;
4711
5068
  }
4712
5069
  const ticket = data.ready[0];
@@ -4714,32 +5071,32 @@ var nextCommand = new Command12("next").description("Claim the highest priority
4714
5071
  const config = readYapoutConfig(cwd);
4715
5072
  const defaultBranch = getDefaultBranch(cwd);
4716
5073
  const branchName = `${config.branch_prefix}/${ref.toLowerCase().replace(/\s+/g, "-")}-${ticket.title.toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 40)}`;
4717
- console.log(chalk13.bold(`
5074
+ console.log(chalk14.bold(`
4718
5075
  Claimed: ${ref} "${ticket.title}"`));
4719
5076
  fetchOrigin(cwd);
4720
5077
  let workDir = cwd;
4721
5078
  if (opts.worktree) {
4722
5079
  const { createWorktree: createWorktree2 } = await import("./worktree-ZZZIL7TK.js");
4723
5080
  workDir = createWorktree2(cwd, ticket.ticketId, branchName, defaultBranch);
4724
- console.log(`Worktree: ${chalk13.cyan(workDir)}`);
5081
+ console.log(`Worktree: ${chalk14.cyan(workDir)}`);
4725
5082
  } else {
4726
5083
  checkoutNewBranch(branchName, defaultBranch, cwd);
4727
5084
  }
4728
- console.log(`Branch: ${chalk13.cyan(branchName)}`);
5085
+ console.log(`Branch: ${chalk14.cyan(branchName)}`);
4729
5086
  const brief = await client.query(
4730
- anyApi6.functions.tickets.getTicketBrief,
5087
+ anyApi7.functions.tickets.getTicketBrief,
4731
5088
  { ticketId: ticket.ticketId }
4732
5089
  );
4733
5090
  if (brief) {
4734
- const briefPath = join12(workDir, ".yapout", "brief.md");
5091
+ const briefPath = join13(workDir, ".yapout", "brief.md");
4735
5092
  const briefContent = formatBrief(ref, ticket, brief);
4736
- writeFileSync10(briefPath, briefContent);
4737
- console.log(`Brief: ${chalk13.cyan(briefPath)}`);
5093
+ writeFileSync11(briefPath, briefContent);
5094
+ console.log(`Brief: ${chalk14.cyan(briefPath)}`);
4738
5095
  }
4739
5096
  console.log();
4740
5097
  console.log("Start Claude Code to implement, or run:");
4741
5098
  console.log(
4742
- chalk13.cyan(
5099
+ chalk14.cyan(
4743
5100
  ` claude --dangerously-skip-permissions "Read .yapout/brief.md, implement it, run yapout_check, then yapout_ship"`
4744
5101
  )
4745
5102
  );
@@ -4789,21 +5146,21 @@ function formatBrief(ref, ticket, brief) {
4789
5146
  }
4790
5147
 
4791
5148
  // src/commands/recap.ts
4792
- import { Command as Command13 } from "commander";
5149
+ import { Command as Command14 } from "commander";
4793
5150
  import { resolve as resolve11 } from "path";
4794
- import chalk14 from "chalk";
4795
- var recapCommand = new Command13("recap").description("Show a summary of recent yapout activity").option("--week", "Show full week summary (default: today)").action(async (opts) => {
5151
+ import chalk15 from "chalk";
5152
+ var recapCommand = new Command14("recap").description("Show a summary of recent yapout activity").option("--week", "Show full week summary (default: today)").action(async (opts) => {
4796
5153
  const creds = requireAuth();
4797
5154
  const cwd = resolve11(process.cwd());
4798
5155
  const mapping = getProjectMapping(cwd);
4799
5156
  if (!mapping) {
4800
5157
  console.error(
4801
- chalk14.red("No project linked.") + " Run " + chalk14.cyan("yapout link") + " in a repo."
5158
+ chalk15.red("No project linked.") + " Run " + chalk15.cyan("yapout link") + " in a repo."
4802
5159
  );
4803
5160
  process.exit(1);
4804
5161
  }
4805
5162
  const client = createConvexClient(creds.token);
4806
- const { anyApi: anyApi6 } = await import("convex/server");
5163
+ const { anyApi: anyApi7 } = await import("convex/server");
4807
5164
  const now = /* @__PURE__ */ new Date();
4808
5165
  const todayStart = new Date(
4809
5166
  now.getFullYear(),
@@ -4813,33 +5170,33 @@ var recapCommand = new Command13("recap").description("Show a summary of recent
4813
5170
  const weekStart = todayStart - 6 * 24 * 60 * 60 * 1e3;
4814
5171
  const since = opts.week ? weekStart : todayStart;
4815
5172
  const data = await client.query(
4816
- anyApi6.functions.localPipeline.getRecentActivity,
5173
+ anyApi7.functions.localPipeline.getRecentActivity,
4817
5174
  { projectId: mapping.projectId, since }
4818
5175
  );
4819
5176
  if (!data) {
4820
- console.log(chalk14.dim("Could not fetch activity data."));
5177
+ console.log(chalk15.dim("Could not fetch activity data."));
4821
5178
  return;
4822
5179
  }
4823
5180
  const period = opts.week ? "This week" : "Today";
4824
5181
  console.log();
4825
- console.log(chalk14.bold(`${period}:`));
5182
+ console.log(chalk15.bold(`${period}:`));
4826
5183
  if (data.recentTickets.length > 0) {
4827
5184
  for (const t of data.recentTickets) {
4828
5185
  const ref = t.linearTicketId ?? "ticket";
4829
- console.log(` ${chalk14.green("\u2713")} ${ref} ${t.title}`);
5186
+ console.log(` ${chalk15.green("\u2713")} ${ref} ${t.title}`);
4830
5187
  }
4831
5188
  }
4832
5189
  if (data.recentPRs.length > 0) {
4833
5190
  for (const p of data.recentPRs) {
4834
5191
  const prRef = p.githubPrNumber ? `PR #${p.githubPrNumber}` : "PR";
4835
- const statusColor = p.status === "opened" ? chalk14.green : p.status === "draft" ? chalk14.dim : chalk14.yellow;
5192
+ const statusColor = p.status === "opened" ? chalk15.green : p.status === "draft" ? chalk15.dim : chalk15.yellow;
4836
5193
  console.log(
4837
5194
  ` ${statusColor("\u2191")} ${p.prTitle.slice(0, 45)} ${statusColor(prRef)} ${statusColor(p.status)}`
4838
5195
  );
4839
5196
  }
4840
5197
  }
4841
5198
  if (data.recentTickets.length === 0 && data.recentPRs.length === 0) {
4842
- console.log(chalk14.dim(" No activity yet."));
5199
+ console.log(chalk15.dim(" No activity yet."));
4843
5200
  }
4844
5201
  console.log();
4845
5202
  console.log(
@@ -4849,12 +5206,12 @@ var recapCommand = new Command13("recap").description("Show a summary of recent
4849
5206
  });
4850
5207
 
4851
5208
  // src/commands/yap.ts
4852
- import { Command as Command14 } from "commander";
4853
- import chalk15 from "chalk";
4854
- var yapCommand = new Command14("yap").description(
5209
+ import { Command as Command15 } from "commander";
5210
+ import chalk16 from "chalk";
5211
+ var yapCommand = new Command15("yap").description(
4855
5212
  "Start a yap session \u2014 brainstorm with an AI that knows your codebase"
4856
5213
  ).action(() => {
4857
- console.log(chalk15.bold("yapout yap sessions"));
5214
+ console.log(chalk16.bold("yapout yap sessions"));
4858
5215
  console.log();
4859
5216
  console.log(
4860
5217
  "Yap sessions run inside Claude \u2014 Desktop, CLI, or web \u2014 anywhere the"
@@ -4863,27 +5220,27 @@ var yapCommand = new Command14("yap").description(
4863
5220
  console.log();
4864
5221
  console.log("Just tell Claude:");
4865
5222
  console.log(
4866
- chalk15.green(` "Let's have a yap session about [topic]"`)
5223
+ chalk16.green(` "Let's have a yap session about [topic]"`)
4867
5224
  );
4868
5225
  console.log(
4869
- chalk15.green(
5226
+ chalk16.green(
4870
5227
  ' "I want to brainstorm [idea] \u2014 be a skeptical QA engineer"'
4871
5228
  )
4872
5229
  );
4873
5230
  console.log();
4874
- console.log(chalk15.dim("Personas: tech lead, qa engineer, product owner, end user, or custom"));
5231
+ console.log(chalk16.dim("Personas: tech lead, qa engineer, product owner, end user, or custom"));
4875
5232
  console.log(
4876
- chalk15.dim(
5233
+ chalk16.dim(
4877
5234
  "Claude will call yapout_start_yap to get instructions and yapout_submit_yap_session when done."
4878
5235
  )
4879
5236
  );
4880
5237
  });
4881
5238
 
4882
5239
  // src/commands/handle-uri.ts
4883
- import { Command as Command15 } from "commander";
4884
- import { spawn as spawn2 } from "child_process";
4885
- import { platform as platform2 } from "os";
4886
- import chalk16 from "chalk";
5240
+ import { Command as Command16 } from "commander";
5241
+ import { spawn as spawn3 } from "child_process";
5242
+ import { platform as platform3 } from "os";
5243
+ import chalk17 from "chalk";
4887
5244
  var VALID_ACTIONS = ["claim", "enrich", "enrich-bulk", "enrich-bundle", "yap", "compact"];
4888
5245
  function parseYapoutUri(raw) {
4889
5246
  const url = new URL(raw);
@@ -4996,11 +5353,11 @@ function findProjectDir() {
4996
5353
  return dirs[0];
4997
5354
  }
4998
5355
  function launchTerminal(cwd, claudeArgs) {
4999
- const os = platform2();
5356
+ const os = platform3();
5000
5357
  const claudeCmd = `claude ${claudeArgs.map(shellEscape).join(" ")}`;
5001
5358
  if (os === "win32") {
5002
5359
  const wtArgs = ["wt", "-d", cwd, "cmd", "/k", claudeCmd];
5003
- const child = spawn2("cmd", ["/c", "start", ...wtArgs], {
5360
+ const child = spawn3("cmd", ["/c", "start", ...wtArgs], {
5004
5361
  cwd,
5005
5362
  stdio: "ignore",
5006
5363
  detached: true,
@@ -5008,7 +5365,7 @@ function launchTerminal(cwd, claudeArgs) {
5008
5365
  });
5009
5366
  child.unref();
5010
5367
  child.on("error", () => {
5011
- const fallback = spawn2(
5368
+ const fallback = spawn3(
5012
5369
  "cmd",
5013
5370
  ["/c", "start", "cmd", "/k", claudeCmd],
5014
5371
  { cwd, stdio: "ignore", detached: true, shell: true }
@@ -5021,7 +5378,7 @@ function launchTerminal(cwd, claudeArgs) {
5021
5378
  activate
5022
5379
  do script "cd ${shellEscape(cwd)} && ${escaped}"
5023
5380
  end tell`;
5024
- const child = spawn2("osascript", ["-e", script], {
5381
+ const child = spawn3("osascript", ["-e", script], {
5025
5382
  stdio: "ignore",
5026
5383
  detached: true
5027
5384
  });
@@ -5036,7 +5393,7 @@ end tell`;
5036
5393
  let launched = false;
5037
5394
  for (const term of terminals) {
5038
5395
  try {
5039
- const child = spawn2(term.cmd, term.args, {
5396
+ const child = spawn3(term.cmd, term.args, {
5040
5397
  stdio: "ignore",
5041
5398
  detached: true
5042
5399
  });
@@ -5049,44 +5406,44 @@ end tell`;
5049
5406
  }
5050
5407
  if (!launched) {
5051
5408
  console.error(
5052
- chalk16.red("Could not find a terminal emulator. Install gnome-terminal, konsole, or xterm.")
5409
+ chalk17.red("Could not find a terminal emulator. Install gnome-terminal, konsole, or xterm.")
5053
5410
  );
5054
5411
  process.exit(1);
5055
5412
  }
5056
5413
  }
5057
5414
  }
5058
5415
  function shellEscape(s) {
5059
- if (platform2() === "win32") {
5416
+ if (platform3() === "win32") {
5060
5417
  return `"${s.replace(/"/g, '""')}"`;
5061
5418
  }
5062
5419
  return `'${s.replace(/'/g, "'\\''")}'`;
5063
5420
  }
5064
- var handleUriCommand = new Command15("handle-uri").description("Handle a yapout:// URI (used by OS protocol handler)").argument("<uri>", "The yapout:// URI to handle").action(async (uri) => {
5421
+ var handleUriCommand = new Command16("handle-uri").description("Handle a yapout:// URI (used by OS protocol handler)").argument("<uri>", "The yapout:// URI to handle").action(async (uri) => {
5065
5422
  try {
5066
5423
  const parsed = parseYapoutUri(uri);
5067
5424
  const prompt = buildPrompt(parsed);
5068
5425
  const cwd = findProjectDir();
5069
5426
  if (!cwd) {
5070
5427
  console.error(
5071
- chalk16.red(
5428
+ chalk17.red(
5072
5429
  "No linked project found. Run `yapout init` or `yapout link` in your repo first."
5073
5430
  )
5074
5431
  );
5075
5432
  process.exit(1);
5076
5433
  }
5077
5434
  const label = parsed.ticketId ? `${parsed.action} ${parsed.ticketId}` : parsed.action;
5078
- console.log(chalk16.dim(`Launching Claude Code to ${label}...`));
5435
+ console.log(chalk17.dim(`Launching Claude Code to ${label}...`));
5079
5436
  launchTerminal(cwd, ["--dangerously-skip-permissions", prompt]);
5080
5437
  setTimeout(() => process.exit(0), 500);
5081
5438
  } catch (err) {
5082
- console.error(chalk16.red(err.message));
5439
+ console.error(chalk17.red(err.message));
5083
5440
  process.exit(1);
5084
5441
  }
5085
5442
  });
5086
5443
 
5087
5444
  // src/index.ts
5088
- var program = new Command16();
5089
- program.name("yapout").description("yapout \u2014 from meeting transcript to merged PR").version("0.7.0");
5445
+ var program = new Command17();
5446
+ program.name("yapout").description("yapout \u2014 from meeting transcript to merged PR").version("0.9.0");
5090
5447
  program.addCommand(loginCommand);
5091
5448
  program.addCommand(logoutCommand);
5092
5449
  program.addCommand(initCommand);
@@ -5102,4 +5459,5 @@ program.addCommand(nextCommand);
5102
5459
  program.addCommand(recapCommand);
5103
5460
  program.addCommand(yapCommand);
5104
5461
  program.addCommand(handleUriCommand);
5462
+ program.addCommand(serveCommand);
5105
5463
  program.parse();