@makerx/yoke 0.11.0 → 0.12.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 (3) hide show
  1. package/README.md +1 -1
  2. package/dist/cli.js +51 -21
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -23,7 +23,7 @@ yoke sync --user # ~/.claude/skills
23
23
 
24
24
  - `/pr` raises a PR with a short What/Why/(How) description.
25
25
  - `/review [number]` reviews a PR with Claude and Codex, combines their findings and offers to post them. [Configure](src/features/review/README.md#settings).
26
- - `/review-ci` adds a workflow that reviews each PR in CI and helps set its variable and secret. [Details](src/features/review/README.md#ci).
26
+ - `/review-ci` makes CI review each PR, adding a workflow or switching the repo's existing LLM review workflow over, and helps set its variable and secret. [Details](src/features/review/README.md#ci).
27
27
 
28
28
  ## CLI
29
29
 
package/dist/cli.js CHANGED
@@ -7,7 +7,7 @@ import { Command } from "commander";
7
7
  // package.json
8
8
  var package_default = {
9
9
  name: "@makerx/yoke",
10
- version: "0.11.0",
10
+ version: "0.12.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",
@@ -756,11 +756,15 @@ function repoRoot(cwd = process.cwd()) {
756
756
  throw new ReviewError("Not in a git repository; run `yoke review-ci` from the repo to review.");
757
757
  }
758
758
  }
759
- function writeCallerWorkflow(root, content, force) {
759
+ var editInPlaceMessage = [
760
+ `${callerWorkflowPath} already exists and differs from yoke's version, so it was left as it is.`,
761
+ "Edit it in place to run yoke's review, keeping its provider, models and names. `yoke review-ci --print` shows yoke's version without saving it."
762
+ ].join("\n");
763
+ function writeCallerWorkflow(root, content) {
760
764
  const path = join3(root, callerWorkflowPath);
761
765
  if (existsSync2(path)) {
762
766
  if (readFileSync2(path, "utf8") === content) return "unchanged";
763
- if (!force) throw new ReviewError(`${callerWorkflowPath} already exists and differs; pass --force to overwrite it.`);
767
+ throw new ReviewError(editInPlaceMessage);
764
768
  }
765
769
  mkdirSync2(dirname2(path), { recursive: true });
766
770
  writeFileSync2(path, content);
@@ -770,7 +774,7 @@ function entraSetup({ clientId, environment }, repo) {
770
774
  const credential = federatedCredential(repo, environment);
771
775
  return [
772
776
  "",
773
- `Instead of REVIEW_CI_API_KEY, the job signs in to Foundry as Entra app ${clientId} with its GitHub OIDC token. On that app, create a federated credential with:`,
777
+ `Instead of an API key, the review signs in to Foundry as Entra app ${clientId}. So that the app trusts this repo's review runs, add a federated credential to it with:`,
774
778
  ` issuer: ${credential.issuer}`,
775
779
  ` audience: ${credential.audience}`,
776
780
  ` subject: ${credential.subject}`,
@@ -780,17 +784,28 @@ function entraSetup({ clientId, environment }, repo) {
780
784
  ...environment ? [`The job runs in the ${environment} environment; create it on the repo if it does not exist.`] : []
781
785
  ];
782
786
  }
787
+ function reportHeading(outcome, workflow) {
788
+ if (outcome === "printed")
789
+ return [`yoke's version of ${callerWorkflowPath}, not saved to the repo:`, "", renderCallerWorkflow(workflow).trimEnd()];
790
+ if (outcome === "unchanged") return [`${callerWorkflowPath} already matches yoke's version; nothing changed.`];
791
+ return [`Added ${callerWorkflowPath}, which reviews each PR opened from this repo.`];
792
+ }
793
+ function variableAndSecretSetup(settings, workflow) {
794
+ const hasSecret = workflowSecrets(workflow).length > 0;
795
+ return [
796
+ hasSecret ? `The review reads the model server's address from the repo variable ${baseUrlVariable} and its API key from the secret ${apiKeySecret}. Set them with:` : `The review reads the model server's address from the repo variable ${baseUrlVariable}. Set it with:`,
797
+ ...setupCommands(settings, workflow).map((command) => ` ${command}`),
798
+ ...hasSecret ? [`Skip the secret if ${apiKeySecret} is already set for your organisation.`] : []
799
+ ];
800
+ }
783
801
  function reviewCiReport(outcome, settings, workflow, repo) {
784
- const action = outcome === "wrote" ? "wrote" : "kept";
785
- const reads = [`variable ${baseUrlVariable}`, ...workflowSecrets(workflow).map((secret2) => `secret ${secret2}`)];
786
802
  return [
787
- `${action} ${callerWorkflowPath}`,
803
+ ...reportHeading(outcome, workflow),
788
804
  "",
789
- `The workflow reads ${reads.join(" and ")}. Set ${reads.length > 1 ? "them" : "it"} on the repo (or the org) with:`,
790
- ...setupCommands(settings, workflow).map((command) => ` ${command}`),
805
+ ...variableAndSecretSetup(settings, workflow),
791
806
  ...workflow.entra ? entraSetup(workflow.entra, repo()) : [],
792
807
  "",
793
- "Commit and push the workflow; each PR opened from this repo then gets a review."
808
+ outcome === "printed" ? "Once the repo's review workflow runs this review, commit and push it; each PR opened from this repo then gets a review." : `Commit and push ${callerWorkflowPath}; each PR opened from this repo then gets a review.`
794
809
  ].join("\n");
795
810
  }
796
811
 
@@ -2185,23 +2200,38 @@ If \`yoke\` is not found, run \`npm i -g @makerx/yoke\` and retry.
2185
2200
  // src/features/review/ciSkill.ts
2186
2201
  var reviewCiSkill = {
2187
2202
  name: "review-ci",
2188
- description: "Add yoke's adversarial PR review workflow to this repo and set the variable and secret it needs.",
2203
+ description: "Add yoke's adversarial PR review workflow to this repo, or switch its existing LLM review workflow to it, and set the variable and secret it needs.",
2189
2204
  disableModelInvocation: true,
2190
2205
  userLevel: true,
2191
2206
  body: `
2192
- Run \`yoke review-ci $ARGUMENTS\`. If it reports that a model is required, run \`yoke models\` to list what the provider serves, ask the user which model each reviewer should run, and rerun with \`--reviewer-1-model\` and \`--reviewer-2-model\`. If it reports the workflow already exists and differs, show the user the difference and ask before rerunning with \`--force\`.
2207
+ Read \`.github/workflows/\`, recent review runs (\`gh run list\`, \`gh run view --log\`) and \`yoke config list\` before asking anything. Only ask what they don't answer, in plain words about the repo, not flags.
2193
2208
 
2194
- With \`--provider foundry\`, if the Foundry resource has local (API key) auth disabled or the user prefers Entra, rerun with \`--azure-client-id\` and \`--azure-tenant-id\` of the Entra app the job signs in as, plus \`--environment\` if the job should run in a GitHub environment. Ask the user for these values; don't guess them.
2209
+ ## Workflow
2195
2210
 
2196
- Then set the variable and secret it printed. Run \`gh variable list\` and \`gh secret list\` first; if one is missing, ask whether it is already set at the org level before setting it on the repo.
2197
- - \`REVIEW_CI_BASE_URL\` is a variable, not a secret: confirm the URL with the user and run the printed \`gh variable set\` command.
2198
- - \`REVIEW_CI_API_KEY\` must never enter this conversation: ask the user to run \`gh secret set REVIEW_CI_API_KEY\` in their own terminal, outside this session, and paste the key at its prompt. With Entra auth there is no API key to set.
2211
+ A workflow is an LLM review if it has a model review PRs, whatever tool it uses.
2199
2212
 
2200
- With Entra auth, show the user the federated credential it printed (issuer, audience, subject) and the \`az ad app federated-credential create\` command, and ask whether to run it or whether they will add the credential in the Azure portal. The app also needs the Cognitive Services User role on the Foundry resource, and the environment, if given, must exist on the repo.
2213
+ - **None:** run \`yoke review-ci $ARGUMENTS\`.
2214
+ - **One, not yoke's:** run \`yoke review-ci $ARGUMENTS --print\` with its provider and models, then edit that file in place to run yoke's review. Keep its name, triggers, provider, models, base URL, variable and secret names.
2215
+ - **Already yoke's** (calls MakerXStudio/yoke's \`review.yml\`, or \`./.github/workflows/review.yml\` in yoke itself): compare with \`--print\` and add only what is missing.
2201
2216
 
2202
- Finish by running \`gh variable list\` and \`gh secret list\` and telling the user to commit and push the workflow file.
2217
+ Never ask whether to overwrite a workflow. If \`review-pr.yml\` exists and differs, edit it in place.
2203
2218
 
2204
- If \`yoke\` is not found, run \`npm i -g @makerx/yoke\` and retry.
2219
+ If no workflow or setting names the models, ask which model each reviewer runs. For LiteLLM, offer \`yoke models\`. For Foundry, ask for deployment names.
2220
+
2221
+ For Foundry with Entra, reuse the client and tenant IDs from the existing workflow; ask only if none is there.
2222
+
2223
+ ## Variable and secret
2224
+
2225
+ Never ask whether they are set for the organisation, and don't query org settings.
2226
+
2227
+ - \`REVIEW_CI_BASE_URL\` not in \`gh variable list\`: set it on the repo with the base URL from the workflow or the printed command.
2228
+ - \`REVIEW_CI_API_KEY\` not in \`gh secret list\`: tell the user to run \`gh secret set REVIEW_CI_API_KEY\` in their own terminal, and to skip it if the secret is already set for the organisation. Never take the key in this conversation.
2229
+
2230
+ With Entra, check \`az ad app federated-credential list --id <client-id>\` and show the printed create command if the credential is missing.
2231
+
2232
+ Finish with what changed and what's left: commit and push the workflow.
2233
+
2234
+ If \`yoke\` is not found, run \`npm i -g @makerx/yoke\`.
2205
2235
  `
2206
2236
  };
2207
2237
 
@@ -2310,14 +2340,14 @@ addSettingOptions(
2310
2340
  ).option("--provider <provider>", "litellm or foundry; every slot uses it in CI. Default: review.reviewer1.provider, else litellm").option(
2311
2341
  "--azure-client-id <id>",
2312
2342
  "Entra app (client) ID to sign in to Foundry with GitHub OIDC instead of an API key; needs --provider foundry"
2313
- ).option("--azure-tenant-id <id>", "Entra tenant ID of that app").option("--environment <name>", "GitHub environment the review job runs in, matching the app's federated credential subject").option("--force", `overwrite an existing ${callerWorkflowPath} that differs`),
2343
+ ).option("--azure-tenant-id <id>", "Entra tenant ID of that app").option("--environment <name>", "GitHub environment the review job runs in, matching the app's federated credential subject").option("--print", `print the workflow and setup commands without writing ${callerWorkflowPath}`),
2314
2344
  ciKeys
2315
2345
  ).addHelpText("after", settingsHelp).action((options) => {
2316
2346
  try {
2317
2347
  const settings = resolveSettings(flagSettings(options, ciKeys), process.env, readUserConfig(userConfigPath()));
2318
2348
  const workflow = callerWorkflow(settings, options.provider, options);
2319
2349
  const root = repoRoot();
2320
- const outcome = writeCallerWorkflow(root, renderCallerWorkflow(workflow), options.force ?? false);
2350
+ const outcome = options.print ? "printed" : writeCallerWorkflow(root, renderCallerWorkflow(workflow));
2321
2351
  console.log(reviewCiReport(outcome, settings, workflow, () => githubRepo(root)));
2322
2352
  } catch (error) {
2323
2353
  if (!(error instanceof ReviewError || error instanceof ConfigError)) throw error;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@makerx/yoke",
3
- "version": "0.11.0",
3
+ "version": "0.12.0",
4
4
  "description": "Lean, user-invoked Claude Code skills for MakerX AI-assisted delivery, plus the CLI they call.",
5
5
  "license": "MIT",
6
6
  "author": "MakerX",