@makerx/yoke 0.14.0 → 0.16.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/README.md +1 -0
- package/dist/cli.js +132 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -30,6 +30,7 @@ yoke sync --user # ~/.claude/skills
|
|
|
30
30
|
|
|
31
31
|
```bash
|
|
32
32
|
yoke sync [--user] [--force] # write yoke's skills
|
|
33
|
+
yoke update # update yoke and re-sync its skills where they were synced
|
|
33
34
|
yoke review [number] # review a PR (used by /review)
|
|
34
35
|
yoke review-ci # add the CI review workflow (used by /review-ci)
|
|
35
36
|
yoke review-ci-gate # require the review / gate check on the default branch (used by /review-ci-gate)
|
package/dist/cli.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
|
-
import { relative } from "path";
|
|
4
|
+
import { relative as relative2 } from "path";
|
|
5
5
|
import { Command } from "commander";
|
|
6
6
|
|
|
7
7
|
// package.json
|
|
8
8
|
var package_default = {
|
|
9
9
|
name: "@makerx/yoke",
|
|
10
|
-
version: "0.
|
|
10
|
+
version: "0.16.0",
|
|
11
11
|
description: "Lean, user-invoked Claude Code skills for MakerX AI-assisted delivery, plus the CLI they call.",
|
|
12
12
|
license: "MIT",
|
|
13
13
|
author: "MakerX",
|
|
@@ -486,9 +486,9 @@ var ReviewError = class extends Error {
|
|
|
486
486
|
}
|
|
487
487
|
};
|
|
488
488
|
var maxBuffer = 256 * 1024 * 1024;
|
|
489
|
-
var run = (command, args, cwd, input) => new Promise((
|
|
489
|
+
var run = (command, args, cwd, input) => new Promise((resolve3, reject) => {
|
|
490
490
|
const child = execFile(command, args, { cwd, encoding: "utf8", maxBuffer }, (error, stdout, stderr) => {
|
|
491
|
-
if (!error) return
|
|
491
|
+
if (!error) return resolve3(stdout);
|
|
492
492
|
const detail = stderr.trim();
|
|
493
493
|
reject(new ReviewError(`\`${command} ${args.join(" ")}\` failed${detail ? `: ${detail}` : ""}`));
|
|
494
494
|
});
|
|
@@ -1904,7 +1904,7 @@ function exitError(harness, code, output) {
|
|
|
1904
1904
|
function hasReview(path) {
|
|
1905
1905
|
return existsSync3(path) && readFileSync4(path, "utf8").trim().length > 0;
|
|
1906
1906
|
}
|
|
1907
|
-
var runHarness = (run2) => new Promise((
|
|
1907
|
+
var runHarness = (run2) => new Promise((resolve3) => {
|
|
1908
1908
|
const shell = process.platform === "win32";
|
|
1909
1909
|
const args = harnessArgs(run2);
|
|
1910
1910
|
const child = spawn(run2.harness, shell ? args.map(windowsQuote) : args, {
|
|
@@ -1941,14 +1941,14 @@ var runHarness = (run2) => new Promise((resolve) => {
|
|
|
1941
1941
|
child.stdin.on("error", () => {
|
|
1942
1942
|
});
|
|
1943
1943
|
child.stdin.end(run2.prompt);
|
|
1944
|
-
child.on("error", (error) =>
|
|
1944
|
+
child.on("error", (error) => resolve3({ ok: false, error: spawnError(run2.harness, error) }));
|
|
1945
1945
|
child.on("close", (code) => {
|
|
1946
1946
|
onLine(pending);
|
|
1947
1947
|
if (code !== 0)
|
|
1948
|
-
return
|
|
1948
|
+
return resolve3({ ok: false, error: exitError(run2.harness, code, [stderr || finalText, lastError].filter(Boolean).join("\n")) });
|
|
1949
1949
|
if (run2.harness === "claude") writeFileSync3(run2.outputPath, finalText);
|
|
1950
|
-
if (!hasReview(run2.outputPath)) return
|
|
1951
|
-
|
|
1950
|
+
if (!hasReview(run2.outputPath)) return resolve3({ ok: false, error: `${run2.harness} returned an empty review` });
|
|
1951
|
+
resolve3({ ok: true, ...model ? { model } : {}, ...usage ? { usage } : {} });
|
|
1952
1952
|
});
|
|
1953
1953
|
});
|
|
1954
1954
|
|
|
@@ -2288,9 +2288,9 @@ var commandTimeout = 3e4;
|
|
|
2288
2288
|
var probeTimeout = 6e4;
|
|
2289
2289
|
var maxBodyChars2 = 300;
|
|
2290
2290
|
function exec(harness, args) {
|
|
2291
|
-
return new Promise((
|
|
2291
|
+
return new Promise((resolve3) => {
|
|
2292
2292
|
const options = { encoding: "utf8", timeout: commandTimeout, shell: process.platform === "win32" };
|
|
2293
|
-
execFile2(harness, args, options, (error, stdout) =>
|
|
2293
|
+
execFile2(harness, args, options, (error, stdout) => resolve3({ ok: !error, stdout }));
|
|
2294
2294
|
});
|
|
2295
2295
|
}
|
|
2296
2296
|
async function claudeSignedIn() {
|
|
@@ -2621,17 +2621,137 @@ function syncSkills(destination, userDestination, force = false, skills = bundle
|
|
|
2621
2621
|
return result;
|
|
2622
2622
|
}
|
|
2623
2623
|
|
|
2624
|
+
// src/features/update/install.ts
|
|
2625
|
+
import { execSync } from "child_process";
|
|
2626
|
+
import { dirname as dirname3, isAbsolute, relative, resolve } from "path";
|
|
2627
|
+
import { fileURLToPath } from "url";
|
|
2628
|
+
function installDir(moduleUrl = import.meta.url) {
|
|
2629
|
+
return resolve(dirname3(fileURLToPath(moduleUrl)), "..");
|
|
2630
|
+
}
|
|
2631
|
+
function npmGlobalRoot() {
|
|
2632
|
+
return execSync("npm root -g", { stdio: "pipe" }).toString().trim();
|
|
2633
|
+
}
|
|
2634
|
+
function gitToplevel(dir) {
|
|
2635
|
+
return execSync("git rev-parse --show-toplevel", { cwd: dir, stdio: "pipe" }).toString().trim();
|
|
2636
|
+
}
|
|
2637
|
+
function isGitClone(dir, toplevel = gitToplevel) {
|
|
2638
|
+
try {
|
|
2639
|
+
return resolve(toplevel(dir)) === resolve(dir);
|
|
2640
|
+
} catch {
|
|
2641
|
+
return false;
|
|
2642
|
+
}
|
|
2643
|
+
}
|
|
2644
|
+
function isInside(parent, child, platform = process.platform) {
|
|
2645
|
+
const fold = (path) => platform === "win32" ? resolve(path).toLowerCase() : resolve(path);
|
|
2646
|
+
const rel = relative(fold(parent), fold(child));
|
|
2647
|
+
return rel !== "" && !rel.startsWith("..") && !isAbsolute(rel);
|
|
2648
|
+
}
|
|
2649
|
+
function isGlobalNpmInstall(dir, globalRoot = npmGlobalRoot, platform = process.platform) {
|
|
2650
|
+
try {
|
|
2651
|
+
return isInside(globalRoot(), dir, platform);
|
|
2652
|
+
} catch {
|
|
2653
|
+
return false;
|
|
2654
|
+
}
|
|
2655
|
+
}
|
|
2656
|
+
function detectInstall(dir, globalRoot = npmGlobalRoot, toplevel = gitToplevel) {
|
|
2657
|
+
if (isGitClone(dir, toplevel)) return "git";
|
|
2658
|
+
return isGlobalNpmInstall(dir, globalRoot) ? "npm" : "unknown";
|
|
2659
|
+
}
|
|
2660
|
+
|
|
2661
|
+
// src/features/update/targets.ts
|
|
2662
|
+
import { existsSync as existsSync9, readFileSync as readFileSync10 } from "fs";
|
|
2663
|
+
import { homedir as homedir4 } from "os";
|
|
2664
|
+
import { join as join10, resolve as resolve2 } from "path";
|
|
2665
|
+
function holdsYokeSkill(root, skills) {
|
|
2666
|
+
return skills.some((skill) => {
|
|
2667
|
+
if (skill.userLevel) return false;
|
|
2668
|
+
const file = join10(root, skill.name, "SKILL.md");
|
|
2669
|
+
return existsSync9(file) && readFileSync10(file, "utf8").includes(ownerMarker);
|
|
2670
|
+
});
|
|
2671
|
+
}
|
|
2672
|
+
function syncTargets(cwd = process.cwd(), home = homedir4(), skills = bundledSkills) {
|
|
2673
|
+
const userRoot = skillsRoot(true, cwd, home);
|
|
2674
|
+
const repoRoot2 = skillsRoot(false, cwd, home);
|
|
2675
|
+
const targets = [];
|
|
2676
|
+
if (holdsYokeSkill(userRoot, skills)) targets.push("user");
|
|
2677
|
+
if (resolve2(repoRoot2) !== resolve2(userRoot) && holdsYokeSkill(repoRoot2, skills)) targets.push("repo");
|
|
2678
|
+
return targets;
|
|
2679
|
+
}
|
|
2680
|
+
|
|
2681
|
+
// src/features/update/update.ts
|
|
2682
|
+
import { execSync as execSync2 } from "child_process";
|
|
2683
|
+
var packageName = "@makerx/yoke";
|
|
2684
|
+
var syncCommands = { user: "yoke sync --user", repo: "yoke sync" };
|
|
2685
|
+
var noTargetsMessage = "No yoke skills were synced here before, so none were re-synced. Run `yoke sync` for this repo's .claude/skills, or `yoke sync --user` for ~/.claude/skills.";
|
|
2686
|
+
function runInherited(command, cwd) {
|
|
2687
|
+
execSync2(command, { cwd, stdio: "inherit" });
|
|
2688
|
+
}
|
|
2689
|
+
function installLatest(install, dir, run2, log) {
|
|
2690
|
+
if (install === "npm") {
|
|
2691
|
+
log("Updating the global npm install...");
|
|
2692
|
+
run2(`npm i -g ${packageName}@latest`);
|
|
2693
|
+
return;
|
|
2694
|
+
}
|
|
2695
|
+
log("Pulling the latest yoke...");
|
|
2696
|
+
run2("git pull", dir);
|
|
2697
|
+
log("Installing dependencies...");
|
|
2698
|
+
run2("npm ci", dir);
|
|
2699
|
+
log("Building...");
|
|
2700
|
+
run2("npm run build", dir);
|
|
2701
|
+
}
|
|
2702
|
+
function updateYoke({ dir, install, targets, run: run2 = runInherited, log, error }) {
|
|
2703
|
+
log(`yoke is installed at ${dir}`);
|
|
2704
|
+
if (install === "unknown") {
|
|
2705
|
+
error(
|
|
2706
|
+
`Could not tell how yoke was installed at ${dir}. Expected a git clone or a global npm install; reinstall with \`npm i -g ${packageName}@latest\`.`
|
|
2707
|
+
);
|
|
2708
|
+
return 1;
|
|
2709
|
+
}
|
|
2710
|
+
try {
|
|
2711
|
+
installLatest(install, dir, run2, log);
|
|
2712
|
+
} catch {
|
|
2713
|
+
error("Updating yoke failed, so no skills were re-synced.");
|
|
2714
|
+
return 1;
|
|
2715
|
+
}
|
|
2716
|
+
const found = targets();
|
|
2717
|
+
if (found.length === 0) {
|
|
2718
|
+
log(noTargetsMessage);
|
|
2719
|
+
return 0;
|
|
2720
|
+
}
|
|
2721
|
+
for (const target of found) {
|
|
2722
|
+
const command = syncCommands[target];
|
|
2723
|
+
log(`Running ${command}...`);
|
|
2724
|
+
try {
|
|
2725
|
+
run2(command);
|
|
2726
|
+
} catch {
|
|
2727
|
+
error(`${command} failed.`);
|
|
2728
|
+
return 1;
|
|
2729
|
+
}
|
|
2730
|
+
}
|
|
2731
|
+
return 0;
|
|
2732
|
+
}
|
|
2733
|
+
|
|
2624
2734
|
// src/cli.ts
|
|
2625
2735
|
var { version } = package_default;
|
|
2626
2736
|
var program = new Command("yoke").description("Lean Claude Code skills and the CLI they call.").version(version);
|
|
2627
2737
|
program.command("sync").description("Write yoke's skills into this repo's .claude/skills, or ~/.claude/skills with --user").option("--user", "write to ~/.claude/skills instead, for every repo").option("--force", "overwrite same-named skills that yoke didn't write").action((options) => {
|
|
2628
2738
|
const user = options.user ?? false;
|
|
2629
2739
|
const cwd = process.cwd();
|
|
2630
|
-
const display = (path) => path.startsWith(cwd) ?
|
|
2740
|
+
const display = (path) => path.startsWith(cwd) ? relative2(cwd, path) : path;
|
|
2631
2741
|
const { written, skipped } = syncSkills(skillsRoot(user), skillsRoot(true), options.force ?? false);
|
|
2632
2742
|
for (const file of written) console.log(`wrote ${display(file)}`);
|
|
2633
2743
|
for (const dir of skipped) console.log(`skipped ${display(dir)} (not yoke's skill; rerun with --force to overwrite it)`);
|
|
2634
2744
|
});
|
|
2745
|
+
program.command("update").description("Update yoke to the latest version, then re-sync its skills wherever they were synced before").action(() => {
|
|
2746
|
+
const dir = installDir();
|
|
2747
|
+
process.exitCode = updateYoke({
|
|
2748
|
+
dir,
|
|
2749
|
+
install: detectInstall(dir),
|
|
2750
|
+
targets: () => syncTargets(),
|
|
2751
|
+
log: console.log,
|
|
2752
|
+
error: console.error
|
|
2753
|
+
});
|
|
2754
|
+
});
|
|
2635
2755
|
var pr = program.command("pr").description("Pull request commands");
|
|
2636
2756
|
pr.command("raise").description("Validate a PR title and body, then create the PR with gh").requiredOption("--title <title>", "single-line PR title").requiredOption("--body <body>", "PR body with ## What, ## Why and optionally ## How").option("--base <branch>", "base branch (defaults to the repo default)").option("--no-draft", "create a ready-for-review PR instead of a draft").addHelpText("after", raiseGuide).action((options) => {
|
|
2637
2757
|
try {
|