yapout 0.5.1 → 0.7.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 +310 -116
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -28,6 +28,75 @@ import {
28
28
  writeFileSync,
29
29
  unlinkSync
30
30
  } from "fs";
31
+
32
+ // src/lib/git.ts
33
+ import { execSync } from "child_process";
34
+ import { dirname, isAbsolute, resolve } from "path";
35
+ function git(args, cwd) {
36
+ return execSync(`git ${args}`, { cwd, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
37
+ }
38
+ function resolveRepoRoot(cwd) {
39
+ try {
40
+ const commonDir = git("rev-parse --git-common-dir", cwd);
41
+ const abs = isAbsolute(commonDir) ? commonDir : resolve(cwd, commonDir);
42
+ return dirname(abs);
43
+ } catch {
44
+ return cwd;
45
+ }
46
+ }
47
+ function getRepoFullName(cwd) {
48
+ const url = git("remote get-url origin", cwd);
49
+ const sshMatch = url.match(/git@github\.com:(.+?)(?:\.git)?$/);
50
+ if (sshMatch) return sshMatch[1];
51
+ const httpsMatch = url.match(/github\.com\/(.+?)(?:\.git)?$/);
52
+ if (httpsMatch) return httpsMatch[1];
53
+ throw new Error(`Could not parse GitHub repo from remote URL: ${url}`);
54
+ }
55
+ function getDefaultBranch(cwd) {
56
+ try {
57
+ const ref = git("rev-parse --abbrev-ref origin/HEAD", cwd);
58
+ return ref.replace("origin/", "");
59
+ } catch {
60
+ try {
61
+ git("rev-parse --verify origin/main", cwd);
62
+ return "main";
63
+ } catch {
64
+ return "master";
65
+ }
66
+ }
67
+ }
68
+ function getCurrentBranch(cwd) {
69
+ return git("branch --show-current", cwd);
70
+ }
71
+ function fetchOrigin(cwd) {
72
+ git("fetch origin", cwd);
73
+ }
74
+ function checkoutNewBranch(name, base, cwd) {
75
+ git(`checkout -b ${name} origin/${base}`, cwd);
76
+ }
77
+ function stageAll(cwd) {
78
+ git("add -A", cwd);
79
+ try {
80
+ git("reset HEAD -- .yapout/", cwd);
81
+ } catch {
82
+ }
83
+ }
84
+ function commit(message, cwd) {
85
+ git(`commit -m "${message.replace(/"/g, '\\"')}"`, cwd);
86
+ return git("rev-parse HEAD", cwd);
87
+ }
88
+ function push(branch, cwd) {
89
+ git(`push -u origin ${branch}`, cwd);
90
+ }
91
+ function getDiffStats(base, head, cwd) {
92
+ try {
93
+ return git(`diff --stat origin/${base}...${head}`, cwd);
94
+ } catch {
95
+ return "(could not compute diff stats)";
96
+ }
97
+ }
98
+
99
+ // src/lib/config.ts
31
100
  import { parse as yamlParse } from "yaml";
32
101
  var YAPOUT_DIR = join(homedir(), ".yapout");
33
102
  function ensureYapoutDir() {
@@ -72,19 +141,46 @@ function writeProjectMappings(mappings) {
72
141
  writeFileSync(PROJECTS_PATH, JSON.stringify(mappings, null, 2));
73
142
  }
74
143
  function getProjectMapping(dir) {
144
+ const root = resolveRepoRoot(dir);
75
145
  const mappings = readProjectMappings();
76
- return mappings[dir] || null;
146
+ return mappings[root] || mappings[dir] || null;
77
147
  }
78
148
  function setProjectMapping(dir, mapping) {
79
149
  const mappings = readProjectMappings();
80
- mappings[dir] = mapping;
150
+ mappings[resolveRepoRoot(dir)] = mapping;
81
151
  writeProjectMappings(mappings);
82
152
  }
83
153
  function removeProjectMapping(dir) {
154
+ const root = resolveRepoRoot(dir);
84
155
  const mappings = readProjectMappings();
85
- delete mappings[dir];
156
+ delete mappings[root];
157
+ if (root !== dir) delete mappings[dir];
86
158
  writeProjectMappings(mappings);
87
159
  }
160
+ var DEVICE_PATH = join(YAPOUT_DIR, "device.json");
161
+ function readDeviceIdentity() {
162
+ if (!existsSync(DEVICE_PATH)) return null;
163
+ try {
164
+ return JSON.parse(readFileSync(DEVICE_PATH, "utf-8"));
165
+ } catch {
166
+ return null;
167
+ }
168
+ }
169
+ function writeDeviceIdentity(identity) {
170
+ ensureYapoutDir();
171
+ writeFileSync(DEVICE_PATH, JSON.stringify(identity, null, 2), { mode: 384 });
172
+ }
173
+ function getOrCreateDeviceIdentity(defaultName) {
174
+ const existing = readDeviceIdentity();
175
+ if (existing) return existing;
176
+ const identity = {
177
+ deviceId: typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `dev-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`,
178
+ name: defaultName,
179
+ createdAt: Date.now()
180
+ };
181
+ writeDeviceIdentity(identity);
182
+ return identity;
183
+ }
88
184
  var WATCH_DEFAULTS = {
89
185
  auto_enrich: true,
90
186
  auto_implement: true,
@@ -97,7 +193,7 @@ var CONFIG_DEFAULTS = {
97
193
  watch: { ...WATCH_DEFAULTS }
98
194
  };
99
195
  function readYapoutConfig(cwd) {
100
- const configPath = join(cwd, ".yapout", "config.yml");
196
+ const configPath = join(resolveRepoRoot(cwd), ".yapout", "config.yml");
101
197
  if (!existsSync(configPath)) return { ...CONFIG_DEFAULTS };
102
198
  try {
103
199
  const raw = yamlParse(readFileSync(configPath, "utf-8"));
@@ -179,10 +275,10 @@ function createConvexClient(token) {
179
275
  }
180
276
 
181
277
  // src/lib/protocol.ts
182
- import { execSync, spawnSync } from "child_process";
278
+ import { execSync as execSync2, spawnSync } from "child_process";
183
279
  import { platform, homedir as homedir2 } from "os";
184
280
  import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2, unlinkSync as unlinkSync2 } from "fs";
185
- import { join as join2, dirname } from "path";
281
+ import { join as join2, dirname as dirname2 } from "path";
186
282
  function getYapoutBinPath() {
187
283
  const os = platform();
188
284
  try {
@@ -195,7 +291,7 @@ function getYapoutBinPath() {
195
291
  }
196
292
  } catch {
197
293
  throw new Error(
198
- "Could not find `yapout` on PATH. Make sure @yapout/cli is installed globally."
294
+ "Could not find `yapout` on PATH. Make sure yapout is installed globally (npm install -g yapout)."
199
295
  );
200
296
  }
201
297
  }
@@ -208,7 +304,7 @@ function registerWindows(yapoutPath) {
208
304
  `reg add "${key}\\shell\\open\\command" /ve /d "${handler}" /f`
209
305
  ];
210
306
  for (const cmd of commands) {
211
- execSync(cmd, { stdio: "pipe" });
307
+ execSync2(cmd, { stdio: "pipe" });
212
308
  }
213
309
  }
214
310
  function registerMacOS(_yapoutPath) {
@@ -361,11 +457,11 @@ on idle
361
457
  end idle`;
362
458
  writeFileSync2(tmpScript, scriptContent);
363
459
  try {
364
- execSync(`rm -rf "${appPath}"`, { stdio: "pipe" });
460
+ execSync2(`rm -rf "${appPath}"`, { stdio: "pipe" });
365
461
  } catch {
366
462
  }
367
- mkdirSync2(dirname(appPath), { recursive: true });
368
- execSync(`osacompile -s -o "${appPath}" "${tmpScript}"`, { stdio: "pipe" });
463
+ mkdirSync2(dirname2(appPath), { recursive: true });
464
+ execSync2(`osacompile -s -o "${appPath}" "${tmpScript}"`, { stdio: "pipe" });
369
465
  try {
370
466
  unlinkSync2(tmpScript);
371
467
  } catch {
@@ -381,12 +477,12 @@ end idle`;
381
477
  ];
382
478
  for (const cmd of plistCommands) {
383
479
  try {
384
- execSync(`/usr/libexec/PlistBuddy -c '${cmd}' "${plistPath}"`, { stdio: "pipe" });
480
+ execSync2(`/usr/libexec/PlistBuddy -c '${cmd}' "${plistPath}"`, { stdio: "pipe" });
385
481
  } catch {
386
482
  }
387
483
  }
388
484
  try {
389
- execSync(
485
+ execSync2(
390
486
  `/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister -R "${appPath}"`,
391
487
  { stdio: "pipe" }
392
488
  );
@@ -406,7 +502,7 @@ MimeType=x-scheme-handler/yapout;
406
502
  `;
407
503
  writeFileSync2(join2(appsDir, "yapout-handler.desktop"), desktop);
408
504
  try {
409
- execSync(
505
+ execSync2(
410
506
  `xdg-mime default yapout-handler.desktop x-scheme-handler/yapout`,
411
507
  { stdio: "pipe" }
412
508
  );
@@ -426,7 +522,7 @@ function registerProtocolHandler() {
426
522
  }
427
523
 
428
524
  // src/commands/login.ts
429
- var CLI_VERSION = "0.5.1";
525
+ var CLI_VERSION = "0.7.0";
430
526
  function safeReturnTo(raw) {
431
527
  if (!raw) return null;
432
528
  try {
@@ -438,7 +534,7 @@ function safeReturnTo(raw) {
438
534
  }
439
535
  }
440
536
  function startCallbackServer() {
441
- return new Promise((resolve11) => {
537
+ return new Promise((resolve12) => {
442
538
  let resolveData;
443
539
  let rejectData;
444
540
  const dataPromise = new Promise((res, rej) => {
@@ -487,7 +583,7 @@ function startCallbackServer() {
487
583
  server.listen(0, () => {
488
584
  const address = server.address();
489
585
  const port = typeof address === "object" && address ? address.port : 0;
490
- resolve11({ port, data: dataPromise });
586
+ resolve12({ port, data: dataPromise });
491
587
  });
492
588
  setTimeout(() => {
493
589
  server.close();
@@ -558,7 +654,7 @@ var logoutCommand = new Command2("logout").description("Log out of yapout").acti
558
654
 
559
655
  // src/commands/link.ts
560
656
  import { Command as Command3 } from "commander";
561
- import { resolve, join as join3 } from "path";
657
+ import { resolve as resolve2, join as join3 } from "path";
562
658
  import {
563
659
  existsSync as existsSync2,
564
660
  mkdirSync as mkdirSync3,
@@ -566,6 +662,7 @@ import {
566
662
  writeFileSync as writeFileSync3,
567
663
  appendFileSync
568
664
  } from "fs";
665
+ import { hostname as hostname2 } from "os";
569
666
  import chalk4 from "chalk";
570
667
 
571
668
  // src/lib/auth.ts
@@ -592,9 +689,22 @@ import { select } from "@inquirer/prompts";
592
689
  async function pickProject(projects) {
593
690
  return await select({
594
691
  message: "Select a project to link to this directory:",
595
- choices: projects.map((p) => ({
596
- name: p.githubRepoFullName ? `${p.name} (${p.githubRepoFullName})` : p.name,
597
- value: p
692
+ choices: projects.map((p) => {
693
+ const repo = p.githubRepoFullName ? ` (${p.githubRepoFullName})` : "";
694
+ const orgLabel = p.org ? ` [${p.org.name}]` : "";
695
+ return {
696
+ name: `${p.name}${repo}${orgLabel}`,
697
+ value: p
698
+ };
699
+ })
700
+ });
701
+ }
702
+ async function pickOrg(orgs, message = "Which org does this project belong to?") {
703
+ return await select({
704
+ message,
705
+ choices: orgs.map((o) => ({
706
+ name: `${o.name} (${o.slug}, ${o.role})`,
707
+ value: o
598
708
  }))
599
709
  });
600
710
  }
@@ -626,7 +736,7 @@ branch_prefix: feat
626
736
  `;
627
737
  var linkCommand = new Command3("link").description("Link the current directory to a yapout project").action(async () => {
628
738
  const creds = requireAuth();
629
- const cwd = resolve(process.cwd());
739
+ const cwd = resolveRepoRoot(resolve2(process.cwd()));
630
740
  const client = createConvexClient(creds.token);
631
741
  let projects;
632
742
  try {
@@ -648,6 +758,25 @@ var linkCommand = new Command3("link").description("Link the current directory t
648
758
  process.exit(1);
649
759
  }
650
760
  const selected = await pickProject(projects);
761
+ const device = getOrCreateDeviceIdentity(hostname2());
762
+ try {
763
+ await client.mutation(anyApi.functions.devices.registerDevice, {
764
+ deviceId: device.deviceId,
765
+ name: device.name,
766
+ cliVersion: getCliVersion(),
767
+ machineHostname: hostname2()
768
+ });
769
+ await client.mutation(anyApi.functions.projectCheckouts.linkCheckout, {
770
+ projectId: selected.id,
771
+ deviceId: device.deviceId,
772
+ localPath: cwd
773
+ });
774
+ } catch (err) {
775
+ console.warn(
776
+ chalk4.yellow("Warning: failed to record this checkout \u2014 "),
777
+ err.message
778
+ );
779
+ }
651
780
  setProjectMapping(cwd, {
652
781
  projectId: selected.id,
653
782
  projectName: selected.name,
@@ -688,23 +817,50 @@ var linkCommand = new Command3("link").description("Link the current directory t
688
817
  };
689
818
  writeFileSync3(mcpPath, JSON.stringify(mcpConfig, null, 2) + "\n");
690
819
  const label = selected.githubRepoFullName ? `${selected.name} (${selected.githubRepoFullName})` : selected.name;
820
+ const orgSuffix = selected.org ? ` in ${selected.org.name}` : "";
691
821
  console.log(
692
- chalk4.green(`Linked to ${label}.`) + " Claude Code will discover yapout tools automatically."
822
+ chalk4.green(`Linked to ${label}${orgSuffix}.`) + " Claude Code will discover yapout tools automatically."
693
823
  );
694
824
  });
825
+ function getCliVersion() {
826
+ try {
827
+ const pkg = JSON.parse(
828
+ readFileSync2(join3(import.meta.dirname, "..", "package.json"), "utf-8")
829
+ );
830
+ return pkg.version ?? "unknown";
831
+ } catch {
832
+ return "unknown";
833
+ }
834
+ }
695
835
 
696
836
  // src/commands/unlink.ts
697
837
  import { Command as Command4 } from "commander";
698
- import { resolve as resolve2, join as join4 } from "path";
838
+ import { resolve as resolve3, join as join4 } from "path";
699
839
  import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync4, rmSync } from "fs";
700
840
  import chalk5 from "chalk";
701
- var unlinkCommand = new Command4("unlink").description("Unlink the current directory from its yapout project").action(() => {
702
- const cwd = resolve2(process.cwd());
841
+ var unlinkCommand = new Command4("unlink").description("Unlink the current directory from its yapout project").action(async () => {
842
+ const cwd = resolve3(process.cwd());
703
843
  const mapping = getProjectMapping(cwd);
704
844
  if (!mapping) {
705
845
  console.error(chalk5.yellow("No project linked to this directory."));
706
846
  process.exit(1);
707
847
  }
848
+ const device = readDeviceIdentity();
849
+ const creds = readCredentials();
850
+ if (device && creds) {
851
+ try {
852
+ const client = createConvexClient(creds.token);
853
+ await client.mutation(anyApi.functions.projectCheckouts.unlinkCheckout, {
854
+ projectId: mapping.projectId,
855
+ deviceId: device.deviceId
856
+ });
857
+ } catch (err) {
858
+ console.warn(
859
+ chalk5.yellow("Warning: failed to update server \u2014 "),
860
+ err.message
861
+ );
862
+ }
863
+ }
708
864
  removeProjectMapping(cwd);
709
865
  const yapoutDir = join4(cwd, ".yapout");
710
866
  if (existsSync3(yapoutDir)) {
@@ -733,7 +889,7 @@ var unlinkCommand = new Command4("unlink").description("Unlink the current direc
733
889
 
734
890
  // src/commands/status.ts
735
891
  import { Command as Command5 } from "commander";
736
- import { resolve as resolve3 } from "path";
892
+ import { resolve as resolve4 } from "path";
737
893
  import chalk6 from "chalk";
738
894
  var statusCommand = new Command5("status").description("Show yapout status for this directory").action(() => {
739
895
  console.log(chalk6.bold("yapout status\n"));
@@ -755,7 +911,7 @@ var statusCommand = new Command5("status").description("Show yapout status for t
755
911
  ` Auth: ${chalk6.green(creds.email)} (expires in ${daysLeft} day${daysLeft === 1 ? "" : "s"})`
756
912
  );
757
913
  }
758
- const cwd = resolve3(process.cwd());
914
+ const cwd = resolve4(process.cwd());
759
915
  const mapping = getProjectMapping(cwd);
760
916
  if (!mapping) {
761
917
  console.log(
@@ -771,7 +927,7 @@ var statusCommand = new Command5("status").description("Show yapout status for t
771
927
 
772
928
  // src/commands/init.ts
773
929
  import { Command as Command6 } from "commander";
774
- import { resolve as resolve4, join as join5 } from "path";
930
+ import { resolve as resolve5, join as join5 } from "path";
775
931
  import {
776
932
  existsSync as existsSync4,
777
933
  mkdirSync as mkdirSync4,
@@ -779,66 +935,8 @@ import {
779
935
  readFileSync as readFileSync4,
780
936
  appendFileSync as appendFileSync2
781
937
  } from "fs";
938
+ import { hostname as hostname3 } from "os";
782
939
  import chalk7 from "chalk";
783
-
784
- // src/lib/git.ts
785
- import { execSync as execSync2 } from "child_process";
786
- function git(args, cwd) {
787
- return execSync2(`git ${args}`, { cwd, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
788
- }
789
- function getRepoFullName(cwd) {
790
- const url = git("remote get-url origin", cwd);
791
- const sshMatch = url.match(/git@github\.com:(.+?)(?:\.git)?$/);
792
- if (sshMatch) return sshMatch[1];
793
- const httpsMatch = url.match(/github\.com\/(.+?)(?:\.git)?$/);
794
- if (httpsMatch) return httpsMatch[1];
795
- throw new Error(`Could not parse GitHub repo from remote URL: ${url}`);
796
- }
797
- function getDefaultBranch(cwd) {
798
- try {
799
- const ref = git("rev-parse --abbrev-ref origin/HEAD", cwd);
800
- return ref.replace("origin/", "");
801
- } catch {
802
- try {
803
- git("rev-parse --verify origin/main", cwd);
804
- return "main";
805
- } catch {
806
- return "master";
807
- }
808
- }
809
- }
810
- function getCurrentBranch(cwd) {
811
- return git("branch --show-current", cwd);
812
- }
813
- function fetchOrigin(cwd) {
814
- git("fetch origin", cwd);
815
- }
816
- function checkoutNewBranch(name, base, cwd) {
817
- git(`checkout -b ${name} origin/${base}`, cwd);
818
- }
819
- function stageAll(cwd) {
820
- git("add -A", cwd);
821
- try {
822
- git("reset HEAD -- .yapout/", cwd);
823
- } catch {
824
- }
825
- }
826
- function commit(message, cwd) {
827
- git(`commit -m "${message.replace(/"/g, '\\"')}"`, cwd);
828
- return git("rev-parse HEAD", cwd);
829
- }
830
- function push(branch, cwd) {
831
- git(`push -u origin ${branch}`, cwd);
832
- }
833
- function getDiffStats(base, head, cwd) {
834
- try {
835
- return git(`diff --stat origin/${base}...${head}`, cwd);
836
- } catch {
837
- return "(could not compute diff stats)";
838
- }
839
- }
840
-
841
- // src/commands/init.ts
842
940
  var CONFIG_YAML_CONTENT2 = `# yapout local configuration
843
941
  # See: https://docs.yapout.dev/cli/config
844
942
 
@@ -863,9 +961,9 @@ branch_prefix: feat
863
961
  # {{ticket.linearTicketId}}, {{ticket.id}}
864
962
  # commit_template: "{{ticket.type}}({{ticket.linearTicketId}}): {{ticket.title}}"
865
963
  `;
866
- 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)").action(async (name) => {
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) => {
867
965
  const creds = requireAuth();
868
- const cwd = resolve4(process.cwd());
966
+ const cwd = resolveRepoRoot(resolve5(process.cwd()));
869
967
  let repoFullName;
870
968
  let defaultBranch;
871
969
  try {
@@ -880,11 +978,82 @@ var initCommand = new Command6("init").description("Create a yapout project from
880
978
  }
881
979
  const projectName = name || repoFullName.split("/")[1] || "unnamed";
882
980
  const client = createConvexClient(creds.token);
981
+ let orgs;
982
+ try {
983
+ orgs = await client.query(
984
+ anyApi.functions.orgMembers.getMyOrgs,
985
+ {}
986
+ );
987
+ } catch (err) {
988
+ console.error(
989
+ chalk7.red("Failed to load your orgs."),
990
+ err.message
991
+ );
992
+ process.exit(1);
993
+ }
994
+ if (!orgs || orgs.length === 0) {
995
+ console.error(
996
+ chalk7.red(
997
+ "You aren't a member of any org. Sign in to the web app once to create your personal org, then re-run."
998
+ )
999
+ );
1000
+ process.exit(1);
1001
+ }
1002
+ let chosenOrgId;
1003
+ let chosenOrgName;
1004
+ if (options?.org) {
1005
+ const match = orgs.find((o) => o.org.slug === options.org);
1006
+ if (!match) {
1007
+ console.error(
1008
+ chalk7.red(`Org "${options.org}" not found among your memberships.`)
1009
+ );
1010
+ console.error(
1011
+ chalk7.dim(
1012
+ "Available: " + orgs.map((o) => o.org.slug).join(", ")
1013
+ )
1014
+ );
1015
+ process.exit(1);
1016
+ }
1017
+ chosenOrgId = match.org._id;
1018
+ chosenOrgName = match.org.name;
1019
+ } else if (orgs.length === 1) {
1020
+ chosenOrgId = orgs[0].org._id;
1021
+ chosenOrgName = orgs[0].org.name;
1022
+ console.log(
1023
+ chalk7.dim(`Creating in `) + chalk7.cyan(chosenOrgName)
1024
+ );
1025
+ } else {
1026
+ const picked = await pickOrg(
1027
+ orgs.map((o) => ({
1028
+ id: o.org._id,
1029
+ name: o.org.name,
1030
+ slug: o.org.slug,
1031
+ role: o.role
1032
+ }))
1033
+ );
1034
+ chosenOrgId = picked.id;
1035
+ chosenOrgName = picked.name;
1036
+ }
1037
+ const device = getOrCreateDeviceIdentity(hostname3());
1038
+ try {
1039
+ await client.mutation(anyApi.functions.devices.registerDevice, {
1040
+ deviceId: device.deviceId,
1041
+ name: device.name,
1042
+ cliVersion: getCliVersion2(),
1043
+ machineHostname: hostname3()
1044
+ });
1045
+ } catch (err) {
1046
+ console.warn(
1047
+ chalk7.yellow("Warning: device registration failed \u2014 "),
1048
+ err.message
1049
+ );
1050
+ }
883
1051
  let result;
884
1052
  try {
885
1053
  result = await client.mutation(
886
1054
  anyApi.functions.projects.createProjectFromCli,
887
1055
  {
1056
+ orgId: chosenOrgId,
888
1057
  name: projectName,
889
1058
  githubRepoFullName: repoFullName,
890
1059
  githubDefaultBranch: defaultBranch
@@ -897,6 +1066,18 @@ var initCommand = new Command6("init").description("Create a yapout project from
897
1066
  );
898
1067
  process.exit(1);
899
1068
  }
1069
+ try {
1070
+ await client.mutation(anyApi.functions.projectCheckouts.linkCheckout, {
1071
+ projectId: result.projectId,
1072
+ deviceId: device.deviceId,
1073
+ localPath: cwd
1074
+ });
1075
+ } catch (err) {
1076
+ console.warn(
1077
+ chalk7.yellow("Warning: failed to record project checkout \u2014 "),
1078
+ err.message
1079
+ );
1080
+ }
900
1081
  setProjectMapping(cwd, {
901
1082
  projectId: result.projectId,
902
1083
  projectName: result.projectName,
@@ -933,12 +1114,24 @@ var initCommand = new Command6("init").description("Create a yapout project from
933
1114
  };
934
1115
  writeFileSync5(mcpPath, JSON.stringify(mcpConfig, null, 2) + "\n");
935
1116
  console.log(
936
- chalk7.green(`Created project "${result.projectName}"`) + chalk7.dim(` (${repoFullName}, branch: ${defaultBranch})`)
1117
+ chalk7.green(`Created project "${result.projectName}"`) + chalk7.dim(
1118
+ ` in ${chosenOrgName} (${repoFullName}, branch: ${defaultBranch})`
1119
+ )
937
1120
  );
938
1121
  console.log(
939
1122
  chalk7.dim("Run ") + chalk7.cyan("yapout_compact") + chalk7.dim(" in Claude Code to generate project context.")
940
1123
  );
941
1124
  });
1125
+ function getCliVersion2() {
1126
+ try {
1127
+ const pkg = JSON.parse(
1128
+ readFileSync4(join5(import.meta.dirname, "..", "package.json"), "utf-8")
1129
+ );
1130
+ return pkg.version ?? "unknown";
1131
+ } catch {
1132
+ return "unknown";
1133
+ }
1134
+ }
942
1135
 
943
1136
  // src/commands/mcp-server.ts
944
1137
  import { Command as Command7 } from "commander";
@@ -963,8 +1156,9 @@ function registerInitTool(server, ctx) {
963
1156
  linearTeamId: z.string().optional().describe("Linear team ID to associate")
964
1157
  },
965
1158
  async (args) => {
966
- const repoFullName = getRepoFullName(ctx.cwd);
967
- const defaultBranch = getDefaultBranch(ctx.cwd);
1159
+ const repoRoot = resolveRepoRoot(ctx.cwd);
1160
+ const repoFullName = getRepoFullName(repoRoot);
1161
+ const defaultBranch = getDefaultBranch(repoRoot);
968
1162
  const projectName = args.name || repoFullName.split("/")[1] || "unnamed";
969
1163
  const result = await ctx.client.mutation(
970
1164
  anyApi3.functions.projects.createProjectFromCli,
@@ -977,12 +1171,12 @@ function registerInitTool(server, ctx) {
977
1171
  );
978
1172
  ctx.projectId = result.projectId;
979
1173
  ctx.projectName = result.projectName;
980
- setProjectMapping(ctx.cwd, {
1174
+ setProjectMapping(repoRoot, {
981
1175
  projectId: result.projectId,
982
1176
  projectName: result.projectName,
983
1177
  linkedAt: Date.now()
984
1178
  });
985
- const yapoutDir = join6(ctx.cwd, ".yapout");
1179
+ const yapoutDir = join6(repoRoot, ".yapout");
986
1180
  if (!existsSync5(yapoutDir)) mkdirSync5(yapoutDir, { recursive: true });
987
1181
  const configPath = join6(yapoutDir, "config.yml");
988
1182
  if (!existsSync5(configPath)) {
@@ -2125,7 +2319,7 @@ function truncate(text) {
2125
2319
  ` + lines.slice(-MAX_OUTPUT_LINES).join("\n");
2126
2320
  }
2127
2321
  function runCommand(command, cwd) {
2128
- return new Promise((resolve11) => {
2322
+ return new Promise((resolve12) => {
2129
2323
  const start = Date.now();
2130
2324
  const child = exec(command, {
2131
2325
  cwd,
@@ -2133,7 +2327,7 @@ function runCommand(command, cwd) {
2133
2327
  maxBuffer: 10 * 1024 * 1024
2134
2328
  // 10MB
2135
2329
  }, (error, stdout, stderr) => {
2136
- resolve11({
2330
+ resolve12({
2137
2331
  exitCode: error?.code ?? (error ? 1 : 0),
2138
2332
  stdout: truncate(stdout),
2139
2333
  stderr: truncate(stderr),
@@ -2141,7 +2335,7 @@ function runCommand(command, cwd) {
2141
2335
  });
2142
2336
  });
2143
2337
  child.on("error", () => {
2144
- resolve11({
2338
+ resolve12({
2145
2339
  exitCode: 1,
2146
2340
  stdout: "",
2147
2341
  stderr: `Command timed out after ${COMMAND_TIMEOUT_MS / 1e3}s`,
@@ -3183,7 +3377,7 @@ and issue counts so you can ask the user for confirmation before creating a new
3183
3377
  }
3184
3378
  const projects = await ctx.client.action(
3185
3379
  anyApi3.functions.linearProjectsMutations.fetchProjectsDetailed,
3186
- { teamId: project.linearTeamId }
3380
+ { projectId: ctx.projectId, teamId: project.linearTeamId }
3187
3381
  );
3188
3382
  return {
3189
3383
  content: [
@@ -3573,7 +3767,7 @@ async function startMcpServer() {
3573
3767
  };
3574
3768
  const server = new McpServer({
3575
3769
  name: "yapout",
3576
- version: "0.5.1"
3770
+ version: "0.7.0"
3577
3771
  });
3578
3772
  registerInitTool(server, ctx);
3579
3773
  registerCompactTool(server, ctx);
@@ -3611,9 +3805,9 @@ var mcpServerCommand = new Command7("mcp-server").description("Start the MCP ser
3611
3805
  // src/commands/worktrees.ts
3612
3806
  import { Command as Command8 } from "commander";
3613
3807
  import chalk8 from "chalk";
3614
- import { resolve as resolve5 } from "path";
3808
+ import { resolve as resolve6 } from "path";
3615
3809
  var worktreesCommand = new Command8("worktrees").description("List active yapout worktrees").action(() => {
3616
- const cwd = resolve5(process.cwd());
3810
+ const cwd = resolve6(process.cwd());
3617
3811
  const worktrees = listWorktrees(cwd);
3618
3812
  if (worktrees.length === 0) {
3619
3813
  console.log(chalk8.dim("No active yapout worktrees."));
@@ -3631,10 +3825,10 @@ var worktreesCommand = new Command8("worktrees").description("List active yapout
3631
3825
  // src/commands/clean.ts
3632
3826
  import { Command as Command9 } from "commander";
3633
3827
  import chalk9 from "chalk";
3634
- import { resolve as resolve6 } from "path";
3828
+ import { resolve as resolve7 } from "path";
3635
3829
  var cleanCommand = new Command9("clean").description("Remove worktrees for completed or failed tickets").action(async () => {
3636
3830
  const creds = requireAuth();
3637
- const cwd = resolve6(process.cwd());
3831
+ const cwd = resolve7(process.cwd());
3638
3832
  const worktrees = listWorktrees(cwd);
3639
3833
  if (worktrees.length === 0) {
3640
3834
  console.log(chalk9.dim("No worktrees to clean."));
@@ -3674,7 +3868,7 @@ var cleanCommand = new Command9("clean").description("Remove worktrees for compl
3674
3868
 
3675
3869
  // src/commands/watch.ts
3676
3870
  import { Command as Command10 } from "commander";
3677
- import { resolve as resolve7 } from "path";
3871
+ import { resolve as resolve8 } from "path";
3678
3872
  import {
3679
3873
  readFileSync as readFileSync6,
3680
3874
  writeFileSync as writeFileSync9,
@@ -3909,10 +4103,10 @@ var Spawner = class {
3909
4103
  if (this.agents.size === 0) return;
3910
4104
  const timeout = 10 * 60 * 1e3;
3911
4105
  const start = Date.now();
3912
- await new Promise((resolve11) => {
4106
+ await new Promise((resolve12) => {
3913
4107
  const check = () => {
3914
4108
  if (this.agents.size === 0 || Date.now() - start > timeout) {
3915
- resolve11();
4109
+ resolve12();
3916
4110
  return;
3917
4111
  }
3918
4112
  setTimeout(check, 2e3);
@@ -4322,7 +4516,7 @@ var watchCommand = new Command10("watch").description("Watch for work and spawn
4322
4516
  return;
4323
4517
  }
4324
4518
  const creds = requireAuth();
4325
- const cwd = resolve7(process.cwd());
4519
+ const cwd = resolve8(process.cwd());
4326
4520
  const mapping = getProjectMapping(cwd);
4327
4521
  if (!mapping) {
4328
4522
  console.error(
@@ -4339,7 +4533,7 @@ var watchCommand = new Command10("watch").description("Watch for work and spawn
4339
4533
  chalk11.green("Watcher started in background") + chalk11.dim(` (PID ${process.pid}, log: ${LOG_FILE})`)
4340
4534
  );
4341
4535
  }
4342
- console.log(chalk11.bold(`yapout watch v${"0.5.1"}`));
4536
+ console.log(chalk11.bold(`yapout watch v${"0.7.0"}`));
4343
4537
  console.log(
4344
4538
  `Project: ${chalk11.green(mapping.projectName)} (${mapping.projectId})`
4345
4539
  );
@@ -4394,11 +4588,11 @@ function isProcessRunning(pid) {
4394
4588
 
4395
4589
  // src/commands/queue.ts
4396
4590
  import { Command as Command11 } from "commander";
4397
- import { resolve as resolve8 } from "path";
4591
+ import { resolve as resolve9 } from "path";
4398
4592
  import chalk12 from "chalk";
4399
4593
  var queueCommand = new Command11("queue").description("Show pipeline state \u2014 what's ready, blocked, and pending").action(async () => {
4400
4594
  const creds = requireAuth();
4401
- const cwd = resolve8(process.cwd());
4595
+ const cwd = resolve9(process.cwd());
4402
4596
  const mapping = getProjectMapping(cwd);
4403
4597
  if (!mapping) {
4404
4598
  console.error(
@@ -4491,13 +4685,13 @@ function formatAgo(ms) {
4491
4685
 
4492
4686
  // src/commands/next.ts
4493
4687
  import { Command as Command12 } from "commander";
4494
- import { resolve as resolve9 } from "path";
4688
+ import { resolve as resolve10 } from "path";
4495
4689
  import { writeFileSync as writeFileSync10 } from "fs";
4496
4690
  import { join as join12 } from "path";
4497
4691
  import chalk13 from "chalk";
4498
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) => {
4499
4693
  const creds = requireAuth();
4500
- const cwd = resolve9(process.cwd());
4694
+ const cwd = resolve10(process.cwd());
4501
4695
  const mapping = getProjectMapping(cwd);
4502
4696
  if (!mapping) {
4503
4697
  console.error(
@@ -4596,11 +4790,11 @@ function formatBrief(ref, ticket, brief) {
4596
4790
 
4597
4791
  // src/commands/recap.ts
4598
4792
  import { Command as Command13 } from "commander";
4599
- import { resolve as resolve10 } from "path";
4793
+ import { resolve as resolve11 } from "path";
4600
4794
  import chalk14 from "chalk";
4601
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) => {
4602
4796
  const creds = requireAuth();
4603
- const cwd = resolve10(process.cwd());
4797
+ const cwd = resolve11(process.cwd());
4604
4798
  const mapping = getProjectMapping(cwd);
4605
4799
  if (!mapping) {
4606
4800
  console.error(
@@ -4892,7 +5086,7 @@ var handleUriCommand = new Command15("handle-uri").description("Handle a yapout:
4892
5086
 
4893
5087
  // src/index.ts
4894
5088
  var program = new Command16();
4895
- program.name("yapout").description("yapout \u2014 from meeting transcript to merged PR").version("0.5.1");
5089
+ program.name("yapout").description("yapout \u2014 from meeting transcript to merged PR").version("0.7.0");
4896
5090
  program.addCommand(loginCommand);
4897
5091
  program.addCommand(logoutCommand);
4898
5092
  program.addCommand(initCommand);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yapout",
3
- "version": "0.5.1",
3
+ "version": "0.7.0",
4
4
  "description": "yapout CLI — link local repos, authenticate, and manage projects",
5
5
  "type": "module",
6
6
  "bin": {