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.
- package/dist/index.js +310 -116
- 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[
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
460
|
+
execSync2(`rm -rf "${appPath}"`, { stdio: "pipe" });
|
|
365
461
|
} catch {
|
|
366
462
|
}
|
|
367
|
-
mkdirSync2(
|
|
368
|
-
|
|
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
|
-
|
|
480
|
+
execSync2(`/usr/libexec/PlistBuddy -c '${cmd}' "${plistPath}"`, { stdio: "pipe" });
|
|
385
481
|
} catch {
|
|
386
482
|
}
|
|
387
483
|
}
|
|
388
484
|
try {
|
|
389
|
-
|
|
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
|
-
|
|
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.
|
|
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((
|
|
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
|
-
|
|
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
|
-
|
|
597
|
-
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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(
|
|
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
|
|
967
|
-
const
|
|
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(
|
|
1174
|
+
setProjectMapping(repoRoot, {
|
|
981
1175
|
projectId: result.projectId,
|
|
982
1176
|
projectName: result.projectName,
|
|
983
1177
|
linkedAt: Date.now()
|
|
984
1178
|
});
|
|
985
|
-
const yapoutDir = join6(
|
|
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((
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
3808
|
+
import { resolve as resolve6 } from "path";
|
|
3615
3809
|
var worktreesCommand = new Command8("worktrees").description("List active yapout worktrees").action(() => {
|
|
3616
|
-
const 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
|
|
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 =
|
|
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
|
|
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((
|
|
4106
|
+
await new Promise((resolve12) => {
|
|
3913
4107
|
const check = () => {
|
|
3914
4108
|
if (this.agents.size === 0 || Date.now() - start > timeout) {
|
|
3915
|
-
|
|
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 =
|
|
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.
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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.
|
|
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);
|