@open-tomato/rafa 0.10.0 → 0.11.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 (4) hide show
  1. package/README.md +4 -1
  2. package/dist/cli.js +1867 -1030
  3. package/dist/index.js +791 -26
  4. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1895,7 +1895,7 @@ async function report(args, repoRoot) {
1895
1895
  }
1896
1896
  }
1897
1897
  // package.json
1898
- var version = "0.10.0";
1898
+ var version = "0.11.0";
1899
1899
 
1900
1900
  // src/modules/manifest.ts
1901
1901
  var MANIFEST_VERSION = 1;
@@ -2185,9 +2185,11 @@ function requireProject(context, command) {
2185
2185
  return context.project;
2186
2186
  }
2187
2187
  function resolvePlansDir(project, command, warn) {
2188
+ return plansDirAt(project.root, resolveProjectConfig(project, command, warn).planDir);
2189
+ }
2190
+ function resolveProjectConfig(project, command, warn) {
2188
2191
  try {
2189
- const { config } = loadConfig({ root: project.root, home: project.home }, {}, warn);
2190
- return plansDirAt(project.root, config.planDir);
2192
+ return loadConfig({ root: project.root, home: project.home }, {}, warn).config;
2191
2193
  } catch (error) {
2192
2194
  if (!(error instanceof ConfigError))
2193
2195
  throw error;
@@ -9689,12 +9691,768 @@ async function finishRelease(input, seams = {}) {
9689
9691
  return { outcome: "released", version: version2, subject, sha, sentence: null, body: null };
9690
9692
  }
9691
9693
 
9694
+ // src/start/risk-total.ts
9695
+ import { readFileSync as readFileSync18 } from "fs";
9696
+
9697
+ // src/plan/risk.ts
9698
+ import { readFileSync as readFileSync17 } from "fs";
9699
+ import { isAbsolute as isAbsolute3, join as join13, relative as relative2 } from "path";
9700
+
9701
+ // src/schema/tiers.ts
9702
+ import { existsSync as existsSync16, realpathSync as realpathSync3 } from "fs";
9703
+ import { dirname as dirname6, join as join12 } from "path";
9704
+ var SKILL_TIERS = ["project", "rafa", "user"];
9705
+ var CLAUDE_SKILLS_PATH = join12(".claude", "skills");
9706
+ var RAFA_INSTINCTS_PATH = join12(".rafa", "instincts");
9707
+ var BUNDLED_SKILLS_DIR = "skills";
9708
+ function realEntry(entry) {
9709
+ try {
9710
+ return realpathSync3(entry);
9711
+ } catch {
9712
+ return entry;
9713
+ }
9714
+ }
9715
+ function bundledSkillsDirectory(entry) {
9716
+ return join12(dirname6(realEntry(entry ?? Bun.main)), BUNDLED_SKILLS_DIR);
9717
+ }
9718
+ function skillTierDirectory(tier, seams) {
9719
+ if (tier === "user")
9720
+ return join12(seams.home, CLAUDE_SKILLS_PATH);
9721
+ if (tier === "rafa")
9722
+ return bundledSkillsDirectory(seams.entry);
9723
+ return seams.projectRoot === null ? null : join12(seams.projectRoot, CLAUDE_SKILLS_PATH);
9724
+ }
9725
+ function resolveSkillTiers(seams) {
9726
+ const located = [];
9727
+ for (const tier of SKILL_TIERS) {
9728
+ const dir = skillTierDirectory(tier, seams);
9729
+ if (dir !== null)
9730
+ located.push({ tier, dir });
9731
+ }
9732
+ return located;
9733
+ }
9734
+
9735
+ // src/plan/risk/accounts.ts
9736
+ var PUSH_REMOTE = "origin";
9737
+ var GITHUB_TRACKER = "github";
9738
+ var URL_USER_INFO = /^([A-Za-z][A-Za-z0-9+.-]*:\/\/)[^@/]+@/;
9739
+ function redactRemote(url) {
9740
+ return url.trim().replace(URL_USER_INFO, "$1***@");
9741
+ }
9742
+ function trackerChain(settings) {
9743
+ return [...new Set([settings.trackerDefault, ...settings.trackerFallback])];
9744
+ }
9745
+ async function readAccounts(settings, seams) {
9746
+ const pushUrl = remoteUrl(seams.git, ["remote", "get-url", "--push", PUSH_REMOTE]);
9747
+ const fetchUrl = remoteUrl(seams.git, ["remote", "get-url", PUSH_REMOTE]);
9748
+ const chain = trackerChain(settings);
9749
+ const reading2 = resolvePrProvider({
9750
+ configured: settings.prProvider,
9751
+ dir: "",
9752
+ readRemote: () => fetchUrl
9753
+ });
9754
+ const needsOwnRepo = chain.includes(GITHUB_TRACKER) || reading2.provider === "gh";
9755
+ const ownRepo = needsOwnRepo ? await viewRepo(seams.gh, []) : null;
9756
+ return [
9757
+ finding("push-remote", pushRemoteText(pushUrl)),
9758
+ finding("visibility", await visibilityText(pushUrl, seams.gh)),
9759
+ finding("issue-tracker", trackerText(chain, ownRepo)),
9760
+ finding("pr-provider", providerText(reading2.provider, reading2.source, ownRepo))
9761
+ ];
9762
+ }
9763
+ function finding(subject, text) {
9764
+ return { level: "note", kind: "account", subject, text };
9765
+ }
9766
+ function remoteUrl(git, args) {
9767
+ const result = git(args);
9768
+ const url = result.stdout.trim();
9769
+ return result.ok && url !== "" ? url : null;
9770
+ }
9771
+ function pushRemoteText(pushUrl) {
9772
+ return pushUrl === null ? `push remote: no ${PUSH_REMOTE} remote, so the loop's push would fail` : `push remote: ${PUSH_REMOTE} \u2192 ${redactRemote(pushUrl)}`;
9773
+ }
9774
+ async function visibilityText(pushUrl, gh) {
9775
+ if (pushUrl === null) {
9776
+ return "visibility: not read, there is no push remote";
9777
+ }
9778
+ if (!isGitHubRemote(pushUrl)) {
9779
+ const host = remoteHost(pushUrl) ?? "a local path";
9780
+ return `visibility: not read, ${host} is not a GitHub host`;
9781
+ }
9782
+ const slug = repoSlug(pushUrl);
9783
+ const view = await viewRepo(gh, [slug]);
9784
+ if (!view.ok) {
9785
+ return `visibility: not read, ${view.reason}`;
9786
+ }
9787
+ const name = view.name ?? slug;
9788
+ return view.visibility === null ? `visibility: not read, gh named no visibility for ${name}` : `visibility: ${name} is ${view.visibility}`;
9789
+ }
9790
+ function repoSlug(url) {
9791
+ return normalizeRemote(url).replace(/^([^/]+):\d+\//, "$1/").replace(/^ssh\.github\.com\//, "github.com/");
9792
+ }
9793
+ function trackerText(chain, ownRepo) {
9794
+ const [first, ...rest] = chain;
9795
+ const fallback = rest.length === 0 ? "" : `, falling back to ${rest.join(", then ")}`;
9796
+ const where = chain.includes(GITHUB_TRACKER) && ownRepo !== null ? `; github files on ${repoPhrase(ownRepo)}` : "";
9797
+ return `issue tracker: ${first ?? "none configured"}${fallback}${where}`;
9798
+ }
9799
+ function providerText(provider, source, ownRepo) {
9800
+ const from = source === "config" ? "set in the config" : `inferred from ${PUSH_REMOTE}`;
9801
+ const effect = provider === "gh" && ownRepo !== null ? `, opening pull requests on ${repoPhrase(ownRepo)}` : "";
9802
+ const none = provider === "none" ? ", so the loop pushes and opens no pull request" : "";
9803
+ return `pull request provider: ${provider} (${from})${effect}${none}`;
9804
+ }
9805
+ function repoPhrase(view) {
9806
+ if (!view.ok) {
9807
+ return `the repository gh resolves, which it could not: ${view.reason}`;
9808
+ }
9809
+ return view.name ?? "the repository gh resolves, which it did not name";
9810
+ }
9811
+ async function viewRepo(gh, target) {
9812
+ const result = await gh(["repo", "view", ...target, "--json", "nameWithOwner,visibility"]);
9813
+ if (!result.ok) {
9814
+ return { ok: false, reason: `gh repo view: ${firstLine(result.stderr) ?? "gh exited non-zero"}` };
9815
+ }
9816
+ const parsed = parseJsonObject(result.stdout);
9817
+ if (parsed === null) {
9818
+ return { ok: false, reason: "gh repo view: answered no JSON object" };
9819
+ }
9820
+ return {
9821
+ ok: true,
9822
+ name: stringField(parsed, "nameWithOwner"),
9823
+ visibility: stringField(parsed, "visibility")?.toLowerCase() ?? null
9824
+ };
9825
+ }
9826
+ function parseJsonObject(text) {
9827
+ try {
9828
+ const value = JSON.parse(text);
9829
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
9830
+ } catch {
9831
+ return null;
9832
+ }
9833
+ }
9834
+ function stringField(object, key) {
9835
+ const value = object[key];
9836
+ return typeof value === "string" && value !== "" ? value : null;
9837
+ }
9838
+ function firstLine(text) {
9839
+ return text.split(`
9840
+ `).map((line) => line.trim()).find((line) => line !== "") ?? null;
9841
+ }
9842
+
9843
+ // src/plan/risk/commands.ts
9844
+ import { posix } from "path";
9845
+
9846
+ // src/check/shell-lines.ts
9847
+ var SHELL_FENCE_LANGUAGES = ["bash", "sh", "shell", "zsh", "console"];
9848
+ var FOREIGN_KEYWORDS = [
9849
+ "async",
9850
+ "await",
9851
+ "class",
9852
+ "const",
9853
+ "def",
9854
+ "enum",
9855
+ "fn",
9856
+ "from",
9857
+ "func",
9858
+ "impl",
9859
+ "import",
9860
+ "interface",
9861
+ "new",
9862
+ "package",
9863
+ "private",
9864
+ "protected",
9865
+ "public",
9866
+ "static",
9867
+ "struct",
9868
+ "throw",
9869
+ "val",
9870
+ "var"
9871
+ ];
9872
+ var HEREDOC = /<<-?\s*['"]?([A-Za-z_][A-Za-z0-9_]*)['"]?/;
9873
+ var CONTINUES = /\\$/;
9874
+ var SPACED_ASSIGNMENT = /^\s*[A-Za-z_$][A-Za-z0-9_$.]*\s+=(?!=)\s+\S/;
9875
+ var FOREIGN_COMMENT = /^\s*(?:\/\/|\/\*|\*\/|\*\s)/;
9876
+ function isShellFence(info) {
9877
+ return SHELL_FENCE_LANGUAGES.includes(info);
9878
+ }
9879
+ function commandOf(line, info) {
9880
+ const text = line.trim();
9881
+ if (text === "" || text.startsWith("#"))
9882
+ return null;
9883
+ if (text.startsWith("$ ") || text === "$")
9884
+ return text.slice(1).trim() || null;
9885
+ if (info === "console")
9886
+ return null;
9887
+ if (text.startsWith("> "))
9888
+ return null;
9889
+ return text;
9890
+ }
9891
+ function heredocTerminator(command) {
9892
+ return HEREDOC.exec(command)?.[1] ?? null;
9893
+ }
9894
+ function continuesLine(command) {
9895
+ return CONTINUES.test(command);
9896
+ }
9897
+ function isForeignCodeLine(line) {
9898
+ const text = line.trim();
9899
+ if (text === "")
9900
+ return false;
9901
+ if (FOREIGN_COMMENT.test(text))
9902
+ return true;
9903
+ if (SPACED_ASSIGNMENT.test(text))
9904
+ return true;
9905
+ return FOREIGN_KEYWORDS.includes(text.split(/\s+/)[0] ?? "");
9906
+ }
9907
+
9908
+ // src/plan/risk/patterns.ts
9909
+ var COMMAND_WRAPPERS = [
9910
+ "command",
9911
+ "do",
9912
+ "else",
9913
+ "env",
9914
+ "exec",
9915
+ "nohup",
9916
+ "sudo",
9917
+ "then",
9918
+ "time",
9919
+ "xargs"
9920
+ ];
9921
+ var OPENER = String.raw`(?:^|[;&|({\x60]|\s-exec(?:dir)?\s)`;
9922
+ var ASSIGNMENTS = String.raw`(?:[A-Za-z_]\w*=\S*\s+)*`;
9923
+ var WRAPPERS = String.raw`(?:(?:${COMMAND_WRAPPERS.join("|")})(?:\s+-\S+)*\s+${ASSIGNMENTS})*`;
9924
+ var AT_COMMAND = String.raw`${OPENER}\s*${ASSIGNMENTS}${WRAPPERS}`;
9925
+ var REST = "[^;&|]*";
9926
+ var GIT_OPTIONS = String.raw`(?:\s+(?:-[Cc]\s+\S+|--?[\w-]+(?:=\S+)?))*`;
9927
+ function hasFlag(short, long) {
9928
+ return String.raw`(?=(?:${REST}\s)?(?:-[A-Za-z]*${short}[A-Za-z]*|--${long})(?=\s|$))`;
9929
+ }
9930
+ function atCommand(name, body) {
9931
+ return { name, expression: new RegExp(`${AT_COMMAND}${body}`) };
9932
+ }
9933
+ var DESTRUCTIVE_PATTERNS = [
9934
+ atCommand("rm -rf", String.raw`rm\s+${hasFlag("[rR]", "recursive")}${hasFlag("f", "force")}`),
9935
+ atCommand("git push --force", String.raw`git${GIT_OPTIONS}\s+push\b(?=${REST}\s(?:--force|-[A-Za-z]*f[A-Za-z]*)(?=\s|$))`),
9936
+ atCommand("git reset --hard", String.raw`git${GIT_OPTIONS}\s+reset\b(?=${REST}\s--hard(?=\s|$))`),
9937
+ atCommand("git clean -f", String.raw`git${GIT_OPTIONS}\s+clean\s+${hasFlag("f", "force")}`),
9938
+ atCommand("sudo", String.raw`sudo(?=\s|$)`),
9939
+ atCommand("brew install", String.raw`brew(?:\s+-\S+)*\s+install\b`),
9940
+ atCommand("npm i -g", String.raw`npm\s+(?:i|install|add)(?=\s)(?=${REST}\s(?:-g|--global)(?=\s|$))`),
9941
+ atCommand("pip install", String.raw`(?:python3?\s+-m\s+)?pip3?\s+install\b`),
9942
+ atCommand("npm publish", String.raw`npm\s+publish\b(?!${REST}--dry-run)`),
9943
+ atCommand("curl \u2026 | sh", String.raw`curl\b[^;&|]*\|(?!\|)\s*${WRAPPERS}(?:ba|z|da)?sh(?=\s|$)`),
9944
+ atCommand("chmod -R", String.raw`chmod\s+${hasFlag("R", "recursive")}`),
9945
+ { name: "DROP TABLE", expression: /\bDROP\s+TABLE\b/i },
9946
+ atCommand("docker system prune", String.raw`docker\s+system\s+prune\b`)
9947
+ ];
9948
+ function destructiveMatches(command) {
9949
+ return DESTRUCTIVE_PATTERNS.filter((pattern) => pattern.expression.test(command));
9950
+ }
9951
+
9952
+ // src/plan/risk/commands.ts
9953
+ var DEVICE_FILES = [
9954
+ "/dev/null",
9955
+ "/dev/stdin",
9956
+ "/dev/stdout",
9957
+ "/dev/stderr",
9958
+ "/dev/tty"
9959
+ ];
9960
+ var WRITES_EVERY_OPERAND = ["mv", "rm", "tee"];
9961
+ var COMMAND_PREFIXES = [...COMMAND_WRAPPERS, "!", "{"];
9962
+ var FENCE_OPEN = /^ {0,3}(`{3,}|~{3,})[ \t]*(\S*)/;
9963
+ var FENCE_CLOSE = /^ {0,3}(`{3,}|~{3,})[ \t]*$/;
9964
+ var CODE_SPAN2 = /`([^`\n]+)`/g;
9965
+ var REDIRECT = /^(?:&>>|&>|>>|>\||>&|<<<|<<-|<<|<&|<>|>|<)/;
9966
+ var OUTPUT_REDIRECTS = /^(?:\d*(?:>>|>\||>&|<>|>)|&>>|&>)$/;
9967
+ var HEREDOC_REDIRECTS = /^\d*<<[-<]?$/;
9968
+ var SEPARATORS2 = ";&|()`";
9969
+ var ASSIGNMENT = /^[A-Za-z_]\w*=/;
9970
+ var SCHEME = /^[A-Za-z][A-Za-z0-9+.-]*:\/\//;
9971
+ function openFence(open) {
9972
+ const run = open[1] ?? "";
9973
+ return {
9974
+ info: (open[2] ?? "").split(/[\s,{]/)[0]?.toLowerCase() ?? "",
9975
+ delimiter: run[0] ?? "`",
9976
+ length: run.length,
9977
+ terminator: null,
9978
+ pending: null,
9979
+ foreign: false,
9980
+ commands: []
9981
+ };
9982
+ }
9983
+ function closesFence(line, fence) {
9984
+ const run = FENCE_CLOSE.exec(line)?.[1] ?? "";
9985
+ return run !== "" && run[0] === fence.delimiter && run.length >= fence.length;
9986
+ }
9987
+ function withoutBackslash(piece) {
9988
+ return piece.replace(/\\$/, "").trim();
9989
+ }
9990
+ function flush(fence) {
9991
+ if (fence.pending !== null)
9992
+ fence.commands.push({ text: fence.pending.text, line: fence.pending.line });
9993
+ fence.pending = null;
9994
+ }
9995
+ function continuationOf(line, info) {
9996
+ const text = line.trim();
9997
+ return info === "console" && text.startsWith("> ") ? text.slice(2) : text;
9998
+ }
9999
+ function readFenceLine(fence, line, number) {
10000
+ const pending = fence.pending;
10001
+ if (pending === null && fence.terminator !== null) {
10002
+ if (line.trim() === fence.terminator)
10003
+ fence.terminator = null;
10004
+ return;
10005
+ }
10006
+ const piece = pending === null ? commandOf(line, fence.info) : continuationOf(line, fence.info);
10007
+ if (piece === null || piece === "")
10008
+ return;
10009
+ if (isForeignCodeLine(piece)) {
10010
+ fence.foreign = true;
10011
+ return;
10012
+ }
10013
+ fence.terminator = heredocTerminator(piece) ?? fence.terminator;
10014
+ if (pending === null)
10015
+ fence.pending = { text: withoutBackslash(piece), line: number };
10016
+ else
10017
+ pending.text = `${pending.text} ${withoutBackslash(piece)}`;
10018
+ if (!continuesLine(piece))
10019
+ flush(fence);
10020
+ }
10021
+ function commandsOf(fence) {
10022
+ flush(fence);
10023
+ return fence.foreign ? [] : fence.commands;
10024
+ }
10025
+ function shellCommandLines(body) {
10026
+ const found = [];
10027
+ let fence = null;
10028
+ for (const [index, line] of body.split(`
10029
+ `).entries()) {
10030
+ if (fence === null) {
10031
+ const open = FENCE_OPEN.exec(line);
10032
+ if (open !== null)
10033
+ fence = openFence(open);
10034
+ continue;
10035
+ }
10036
+ if (closesFence(line, fence)) {
10037
+ found.push(...commandsOf(fence));
10038
+ fence = null;
10039
+ continue;
10040
+ }
10041
+ if (isShellFence(fence.info) && !fence.foreign)
10042
+ readFenceLine(fence, line, index + 1);
10043
+ }
10044
+ if (fence !== null)
10045
+ found.push(...commandsOf(fence));
10046
+ return found;
10047
+ }
10048
+ function taskLineCommands(taskLine, line) {
10049
+ const found = [];
10050
+ for (const match of taskLine.matchAll(CODE_SPAN2)) {
10051
+ const command = commandOf(match[1] ?? "", "sh");
10052
+ if (command !== null && !isForeignCodeLine(command))
10053
+ found.push({ text: command, line });
10054
+ }
10055
+ return found;
10056
+ }
10057
+ function quotedEnd(command, start) {
10058
+ const quote = command[start];
10059
+ let index = start + 1;
10060
+ while (index < command.length && command[index] !== quote) {
10061
+ index += quote === '"' && command[index] === "\\" ? 2 : 1;
10062
+ }
10063
+ return Math.min(index + 1, command.length);
10064
+ }
10065
+ function unquote(run) {
10066
+ const closed = run.length > 1 && run.endsWith(run[0] ?? "");
10067
+ const inner = run.slice(1, closed ? -1 : undefined);
10068
+ return run.startsWith('"') ? inner.replace(/\\(["\\$`])/g, "$1") : inner;
10069
+ }
10070
+ function opensRedirect(rest) {
10071
+ return rest.startsWith(">") || rest.startsWith("<") || rest.startsWith("&>");
10072
+ }
10073
+ function shellTokens(command) {
10074
+ const tokens = [];
10075
+ let word = "";
10076
+ let hasWord = false;
10077
+ const endWord = () => {
10078
+ if (hasWord)
10079
+ tokens.push({ kind: "word", text: word });
10080
+ word = "";
10081
+ hasWord = false;
10082
+ };
10083
+ let index = 0;
10084
+ while (index < command.length) {
10085
+ const char = command[index] ?? "";
10086
+ const rest = command.slice(index);
10087
+ if (opensRedirect(rest)) {
10088
+ const fd = /^\d+$/.test(word) ? word : "";
10089
+ if (fd === "")
10090
+ endWord();
10091
+ word = "";
10092
+ hasWord = false;
10093
+ const operator = REDIRECT.exec(rest)?.[0] ?? char;
10094
+ tokens.push({ kind: "redirect", text: `${fd}${operator}` });
10095
+ index += operator.length;
10096
+ } else if (char === "'" || char === '"') {
10097
+ const end = quotedEnd(command, index);
10098
+ word += unquote(command.slice(index, end));
10099
+ hasWord = true;
10100
+ index = end;
10101
+ } else if (char === "\\") {
10102
+ word += command[index + 1] ?? "";
10103
+ hasWord = true;
10104
+ index += 2;
10105
+ } else if (/\s/.test(char)) {
10106
+ endWord();
10107
+ index += 1;
10108
+ } else if (SEPARATORS2.includes(char)) {
10109
+ endWord();
10110
+ const operator = /^(?:&&|\|\||[;&|()`])/.exec(rest)?.[0] ?? char;
10111
+ tokens.push({ kind: "separator", text: operator });
10112
+ index += operator.length;
10113
+ } else {
10114
+ word += char;
10115
+ hasWord = true;
10116
+ index += 1;
10117
+ }
10118
+ }
10119
+ endWord();
10120
+ return tokens;
10121
+ }
10122
+ function segmentsOf(tokens) {
10123
+ const segments = [[]];
10124
+ for (const token of tokens) {
10125
+ if (token.kind === "separator")
10126
+ segments.push([]);
10127
+ else
10128
+ segments.at(-1)?.push(token);
10129
+ }
10130
+ return segments.filter((segment) => segment.length > 0);
10131
+ }
10132
+ function wordsOf2(segment) {
10133
+ const argv = [];
10134
+ const redirected = [];
10135
+ let operator = null;
10136
+ for (const token of segment) {
10137
+ if (token.kind === "redirect") {
10138
+ operator = token.text;
10139
+ } else if (operator === null) {
10140
+ argv.push(token.text);
10141
+ } else {
10142
+ if (!HEREDOC_REDIRECTS.test(operator)) {
10143
+ redirected.push({ word: token.text, writes: OUTPUT_REDIRECTS.test(operator) });
10144
+ }
10145
+ operator = null;
10146
+ }
10147
+ }
10148
+ return { argv, redirected };
10149
+ }
10150
+ function commandIndex(argv) {
10151
+ let index = 0;
10152
+ let afterWrapper = false;
10153
+ while (index < argv.length) {
10154
+ const word = argv[index] ?? "";
10155
+ const isWrapperFlag = afterWrapper && word.startsWith("-");
10156
+ if (!ASSIGNMENT.test(word) && !COMMAND_PREFIXES.includes(word) && !isWrapperFlag)
10157
+ return index;
10158
+ afterWrapper = COMMAND_PREFIXES.includes(word) || isWrapperFlag;
10159
+ index += 1;
10160
+ }
10161
+ return index;
10162
+ }
10163
+ function operandsOf(args) {
10164
+ const end = args.indexOf("--");
10165
+ const flagged = end === -1 ? args : args.slice(0, end);
10166
+ const after = end === -1 ? [] : args.slice(end + 1);
10167
+ return [...flagged.filter((word) => !word.startsWith("-")), ...after];
10168
+ }
10169
+ function copyTargets(args) {
10170
+ const flag = args.indexOf("-t");
10171
+ if (flag !== -1 && args[flag + 1] !== undefined)
10172
+ return [args[flag + 1] ?? ""];
10173
+ const long = args.find((word) => word.startsWith("--target-directory="));
10174
+ if (long !== undefined)
10175
+ return [long.slice("--target-directory=".length)];
10176
+ return operandsOf(args).slice(-1);
10177
+ }
10178
+ function writtenWords(argv) {
10179
+ const at = commandIndex(argv);
10180
+ const name = posix.basename(argv[at] ?? "");
10181
+ const args = argv.slice(at + 1);
10182
+ if (WRITES_EVERY_OPERAND.includes(name))
10183
+ return new Set(operandsOf(args));
10184
+ if (name === "cp")
10185
+ return new Set(copyTargets(args));
10186
+ return new Set;
10187
+ }
10188
+ function pathCandidate(word) {
10189
+ if (word.startsWith("/") || word.startsWith("~"))
10190
+ return word;
10191
+ const equals = word.indexOf("=");
10192
+ return equals === -1 ? word : word.slice(equals + 1);
10193
+ }
10194
+ function isWithin(root, target) {
10195
+ const step = posix.relative(posix.resolve(root), posix.resolve(target));
10196
+ return !step.startsWith("..") && !posix.isAbsolute(step);
10197
+ }
10198
+ function isOutsidePath(path5, seams) {
10199
+ if (SCHEME.test(path5) || path5.startsWith("//") || DEVICE_FILES.includes(path5))
10200
+ return false;
10201
+ if (path5 === "~" || path5.startsWith("~/"))
10202
+ return !isWithin(seams.repoRoot, posix.join(seams.home, path5.slice(1)));
10203
+ if (path5.startsWith("~"))
10204
+ return true;
10205
+ return path5.startsWith("/") && !isWithin(seams.repoRoot, path5);
10206
+ }
10207
+ function outsidePaths(command, seams) {
10208
+ const found = new Map;
10209
+ const note = (word, writes) => {
10210
+ const path5 = pathCandidate(word);
10211
+ if (isOutsidePath(path5, seams))
10212
+ found.set(path5, (found.get(path5) ?? false) || writes);
10213
+ };
10214
+ for (const segment of segmentsOf(shellTokens(command))) {
10215
+ const { argv, redirected } = wordsOf2(segment);
10216
+ const written = writtenWords(argv);
10217
+ for (const word of argv)
10218
+ note(word, written.has(word) || written.has(pathCandidate(word)));
10219
+ for (const target of redirected)
10220
+ note(target.word, target.writes);
10221
+ }
10222
+ return Array.from(found, ([path5, writes]) => ({ path: path5, writes }));
10223
+ }
10224
+ function findingsOf(command, seams) {
10225
+ const destructive = destructiveMatches(command.text).map((pattern) => ({
10226
+ level: "high",
10227
+ kind: "destructive",
10228
+ text: `${pattern.name} \u2014 ${command.text}`,
10229
+ subject: pattern.name,
10230
+ command: command.text,
10231
+ line: command.line
10232
+ }));
10233
+ const paths = outsidePaths(command.text, seams).map((path5) => ({
10234
+ level: path5.writes ? "high" : "note",
10235
+ kind: "outside-path",
10236
+ text: `${path5.path} \u2014 ${path5.writes ? "written by" : "named in"} ${command.text}`,
10237
+ subject: path5.path,
10238
+ command: command.text,
10239
+ line: command.line
10240
+ }));
10241
+ return [...destructive, ...paths];
10242
+ }
10243
+ function readCommands2(commands, seams) {
10244
+ return commands.flatMap((command) => findingsOf(command, seams));
10245
+ }
10246
+ function readBodyCommands(body, seams) {
10247
+ return readCommands2(shellCommandLines(body), seams);
10248
+ }
10249
+ function readTaskLineCommands(taskLine, line, seams) {
10250
+ return readCommands2(taskLineCommands(taskLine, line), seams);
10251
+ }
10252
+
10253
+ // src/plan/risk/secrets.ts
10254
+ var SECRET_NAME_SHAPES = [
10255
+ { name: "*_TOKEN", expression: /_TOKEN$/ },
10256
+ { name: "*_KEY", expression: /_KEY$/ },
10257
+ { name: "*_SECRET", expression: /_SECRET$/ },
10258
+ { name: "*PASSWORD*", expression: /PASSWORD/ },
10259
+ { name: "AWS_*", expression: /^AWS_/ }
10260
+ ];
10261
+ function secretShapesOf(name) {
10262
+ return SECRET_NAME_SHAPES.filter((shape) => shape.expression.test(name));
10263
+ }
10264
+ function scanSecretNames(environment) {
10265
+ return [...Object.keys(environment)].sort().flatMap((name) => {
10266
+ const shapes = secretShapesOf(name);
10267
+ if (shapes.length === 0) {
10268
+ return [];
10269
+ }
10270
+ const matched = shapes.map((shape) => shape.name).join(", ");
10271
+ return [{ level: "note", kind: "secret", text: `${name} \u2014 matches ${matched}`, subject: name }];
10272
+ });
10273
+ }
10274
+
10275
+ // src/plan/risk.ts
10276
+ var RISK_KINDS = ["tools", "destructive", "outside-path", "account", "secret"];
10277
+ var EVERY_TOOL = "every tool";
10278
+ var SHELL_TOOL = "Bash";
10279
+ function agentTools(frontmatter) {
10280
+ const value = frontmatter["tools"];
10281
+ let names = [];
10282
+ if (typeof value === "string")
10283
+ names = value.split(",");
10284
+ else if (Array.isArray(value))
10285
+ names = value.filter((entry) => typeof entry === "string");
10286
+ const tools = [...new Set(names.map((name) => name.trim()).filter((name) => name !== ""))];
10287
+ return tools.length === 0 ? null : tools;
10288
+ }
10289
+ function holdsShell(tools) {
10290
+ return tools.some((tool) => tool === SHELL_TOOL || tool.startsWith(`${SHELL_TOOL}(`));
10291
+ }
10292
+ function frontmatterString(frontmatter, key) {
10293
+ const value = frontmatter[key];
10294
+ return typeof value === "string" && value.trim() !== "" ? value.trim() : null;
10295
+ }
10296
+ function taskAccess(declaration, seams) {
10297
+ const budget = declaration?.budget ?? null;
10298
+ const agent = declaration?.agent ?? null;
10299
+ if (agent === null) {
10300
+ const tools2 = declaration?.tools ?? null;
10301
+ return {
10302
+ tools: tools2,
10303
+ source: tools2 === null ? "no agent= and no tools=" : "tools=",
10304
+ model: declaration?.model ?? null,
10305
+ budget
10306
+ };
10307
+ }
10308
+ const definition = findAgentDefinition(agent, { repoRoot: seams.repoRoot, home: seams.home }, seams.settingSources);
10309
+ if (definition === null) {
10310
+ return { tools: null, source: `agent=${agent} resolves to no definition`, model: null, budget };
10311
+ }
10312
+ const tools = agentTools(definition.frontmatter);
10313
+ return {
10314
+ tools,
10315
+ source: tools === null ? `agent=${agent} has no tools: line` : `agent=${agent}`,
10316
+ model: frontmatterString(definition.frontmatter, "model"),
10317
+ budget
10318
+ };
10319
+ }
10320
+ function toolsFinding(task, plan2, seams) {
10321
+ const access = taskAccess(task.declaration, seams);
10322
+ const model = `model ${access.model ?? "the session default"}`;
10323
+ const budget = access.budget === null ? "no budget=" : `budget $${String(access.budget)}`;
10324
+ const where = { task: task.text, file: plan2, line: task.lineNum + 1 };
10325
+ if (access.tools === null) {
10326
+ return { level: "high", kind: "tools", text: `${EVERY_TOOL} \u2014 ${access.source}; ${model}; ${budget}`, ...where };
10327
+ }
10328
+ if (holdsShell(access.tools) && access.budget === null) {
10329
+ const listed = access.tools.join(", ");
10330
+ return { level: "note", kind: "tools", text: `${SHELL_TOOL} with no budget= \u2014 ${access.source}: ${listed}; ${model}`, ...where };
10331
+ }
10332
+ return null;
10333
+ }
10334
+ function displayPath(path5, seams) {
10335
+ const absolute = isAbsolute3(path5) ? path5 : join13(seams.repoRoot, path5);
10336
+ const within = (root) => {
10337
+ const step = relative2(root, absolute);
10338
+ return step === "" || step.startsWith("..") || isAbsolute3(step) ? null : step;
10339
+ };
10340
+ const inRepo = within(seams.repoRoot);
10341
+ if (inRepo !== null)
10342
+ return inRepo;
10343
+ const inHome = within(seams.home);
10344
+ return inHome === null ? absolute : `~/${inHome}`;
10345
+ }
10346
+ function located(finding2, file, task) {
10347
+ const base = { level: finding2.level, kind: finding2.kind, text: finding2.text };
10348
+ return task === undefined ? { ...base, file, line: finding2.line } : { ...base, task, file, line: finding2.line };
10349
+ }
10350
+ function readText4(path5) {
10351
+ try {
10352
+ return readFileSync17(path5, "utf8");
10353
+ } catch {
10354
+ return null;
10355
+ }
10356
+ }
10357
+ function findSkillFile(name, seams) {
10358
+ const tiers = resolveSkillTiers({ home: seams.home, projectRoot: seams.repoRoot, entry: seams.entry });
10359
+ for (const { dir } of tiers) {
10360
+ const path5 = join13(dir, name, "SKILL.md");
10361
+ if (readText4(path5) !== null)
10362
+ return path5;
10363
+ }
10364
+ return null;
10365
+ }
10366
+ function taskFiles(declaration, seams) {
10367
+ if (declaration === null)
10368
+ return [];
10369
+ const agent = declaration.agent === null ? null : findAgentDefinition(declaration.agent, { repoRoot: seams.repoRoot, home: seams.home }, seams.settingSources);
10370
+ const skills = (declaration.skills ?? []).map((name) => findSkillFile(name, seams));
10371
+ return [agent?.path ?? null, ...skills].filter((path5) => path5 !== null);
10372
+ }
10373
+ function commandFindings(plan2, display, tasks, seams) {
10374
+ const paths = { repoRoot: seams.repoRoot, home: seams.home };
10375
+ const found = readBodyCommands(plan2.text, paths).map((finding2) => located(finding2, display));
10376
+ const read = new Set;
10377
+ const lines = plan2.text.split(`
10378
+ `);
10379
+ for (const task of tasks) {
10380
+ const spans = readTaskLineCommands(lines[task.lineNum] ?? "", task.lineNum + 1, paths);
10381
+ found.push(...spans.map((finding2) => located(finding2, display, task.text)));
10382
+ for (const file of taskFiles(task.declaration, seams)) {
10383
+ if (read.has(file))
10384
+ continue;
10385
+ read.add(file);
10386
+ const shown = displayPath(file, seams);
10387
+ found.push(...readBodyCommands(readText4(file) ?? "", paths).map((finding2) => located(finding2, shown)));
10388
+ }
10389
+ }
10390
+ return found;
10391
+ }
10392
+ function openTasks(tasks, hidden) {
10393
+ return [...tasks, ...hidden].filter((task) => task.status !== "done").sort((a, b) => a.lineNum - b.lineNum);
10394
+ }
10395
+ function riskTotal(findings) {
10396
+ return {
10397
+ high: findings.filter((finding2) => finding2.level === "high").length,
10398
+ note: findings.filter((finding2) => finding2.level === "note").length
10399
+ };
10400
+ }
10401
+ async function assessPlanRisk(plan2, seams) {
10402
+ const model = parsePlan(plan2.text);
10403
+ const display = displayPath(plan2.path, seams);
10404
+ const tasks = openTasks(model.tasks, model.hiddenTasks);
10405
+ const tools = tasks.flatMap((task) => toolsFinding(task, display, seams) ?? []);
10406
+ const commands = commandFindings(plan2, display, tasks, seams);
10407
+ const accounts = (await readAccounts(seams.accounts, seams.runners)).map(({ level, kind, text }) => ({ level, kind, text }));
10408
+ const secrets = scanSecretNames(seams.environment).map(({ level, kind, text }) => ({ level, kind, text }));
10409
+ const findings = [...tools, ...commands, ...accounts, ...secrets];
10410
+ return { plan: display, total: riskTotal(findings), findings };
10411
+ }
10412
+ function counted2(count, word) {
10413
+ return count === 1 ? `${String(count)} ${word}` : `${String(count)} ${word}s`;
10414
+ }
10415
+ function riskTotalLine(report2) {
10416
+ return `\uD83D\uDEE1 Risk: ${String(report2.total.high)} high, ${counted2(report2.total.note, "note")} \u2014 rafa plan risk ${report2.plan}`;
10417
+ }
10418
+ var KIND_WIDTH = Math.max(...RISK_KINDS.map((kind) => kind.length));
10419
+
10420
+ // src/start/risk-total.ts
10421
+ function spawningRunners(root) {
10422
+ return { git: createGitRunner(root), gh: createGhRunner({ cwd: root }) };
10423
+ }
10424
+ async function readRiskTotalLine(input, seams = {}) {
10425
+ const makeRunners = seams.runners ?? spawningRunners;
10426
+ const report2 = await assessPlanRisk({ path: input.planPath, text: readFileSync18(input.planPath, "utf8") }, {
10427
+ repoRoot: input.repoRoot,
10428
+ home: input.home,
10429
+ settingSources: input.config.settingSources,
10430
+ environment: input.environment,
10431
+ accounts: {
10432
+ prProvider: input.config.prProvider,
10433
+ trackerDefault: input.config.trackerDefault,
10434
+ trackerFallback: input.config.trackerFallback
10435
+ },
10436
+ runners: makeRunners(input.repoRoot)
10437
+ });
10438
+ return riskTotalLine(report2);
10439
+ }
10440
+ async function announceRiskTotal(input, seams = {}) {
10441
+ const output = seams.output ?? activeOutput();
10442
+ try {
10443
+ output.info(await readRiskTotalLine(input, seams));
10444
+ } catch (error) {
10445
+ const said = error instanceof Error ? error.message : String(error);
10446
+ output.warn(`\u26A0\uFE0F Risk: could not read ${input.planPath} \u2014 ${said}`);
10447
+ }
10448
+ }
10449
+
9692
10450
  // src/start/session.ts
9693
10451
  import { randomUUID as randomUUID8 } from "crypto";
9694
- import { relative as relative2 } from "path";
10452
+ import { relative as relative3 } from "path";
9695
10453
  var NOTHING_DISPATCHED = " Nothing was checked and nothing was dispatched.";
9696
10454
  function recordLabel(repoRoot, sessionId) {
9697
- return relative2(repoRoot, sessionFilePath(repoRoot, sessionId));
10455
+ return relative3(repoRoot, sessionFilePath(repoRoot, sessionId));
9698
10456
  }
9699
10457
  function conflictLine(repoRoot, conflict) {
9700
10458
  const { record } = conflict;
@@ -9724,7 +10482,7 @@ function openRefusal(repoRoot, draft, error) {
9724
10482
  if (!(error instanceof SessionRecordError) && !isSystemError)
9725
10483
  return null;
9726
10484
  return [
9727
- `\u274C Refusing to start: the session records under ${relative2(repoRoot, runsDir(repoRoot))}/ cannot be read or written.`,
10485
+ `\u274C Refusing to start: the session records under ${relative3(repoRoot, runsDir(repoRoot))}/ cannot be read or written.`,
9728
10486
  ` ${messageOf(error)}`,
9729
10487
  NOTHING_DISPATCHED
9730
10488
  ].join(`
@@ -9760,7 +10518,7 @@ function openRunSession(options) {
9760
10518
  const draft = {
9761
10519
  sessionId: (seams.newSessionId ?? randomUUID8)(),
9762
10520
  planStub: options.planStub,
9763
- plan: relative2(repoRoot, options.planPath),
10521
+ plan: relative3(repoRoot, options.planPath),
9764
10522
  branch: options.branch,
9765
10523
  pid: seams.pid ?? process.pid,
9766
10524
  startedAt: (seams.now ?? (() => new Date))().toISOString()
@@ -9837,11 +10595,11 @@ function announcePlanIssues(planContent) {
9837
10595
  }
9838
10596
 
9839
10597
  // src/start/runtime.ts
9840
- import { existsSync as existsSync16, readdirSync as readdirSync6, realpathSync as realpathSync3, statSync as statSync9 } from "fs";
10598
+ import { existsSync as existsSync17, readdirSync as readdirSync6, realpathSync as realpathSync4, statSync as statSync9 } from "fs";
9841
10599
  import { homedir as homedir6 } from "os";
9842
- import { isAbsolute as isAbsolute3, join as join12, relative as relative3, resolve as resolve10, sep } from "path";
10600
+ import { isAbsolute as isAbsolute4, join as join14, relative as relative4, resolve as resolve10, sep } from "path";
9843
10601
  var RUNTIME_FLAG = "--runtime";
9844
- var RUNTIME_SUBDIR = join12(".rafa", "runtime");
10602
+ var RUNTIME_SUBDIR = join14(".rafa", "runtime");
9845
10603
  var RUNTIME_ENTRY = "cli.js";
9846
10604
  var SOURCE_DIR = "src";
9847
10605
  var RUNTIME_COMMAND = ["start"];
@@ -9858,10 +10616,10 @@ function resolveRuntime(value, places) {
9858
10616
  throw refusal3([`\u274C Refusing to start: \`--runtime\` names no runtime. Spell it ${SPELLING}.`]);
9859
10617
  }
9860
10618
  const isVersion = VERSION_SHAPE2.test(value);
9861
- const named = isVersion ? join12(places.home, RUNTIME_SUBDIR, value, RUNTIME_ENTRY) : resolve10(places.cwd, value);
10619
+ const named = isVersion ? join14(places.home, RUNTIME_SUBDIR, value, RUNTIME_ENTRY) : resolve10(places.cwd, value);
9862
10620
  refuseInsideSource(value, named, places);
9863
10621
  const entry = entryOf(value, named, isVersion, places.home);
9864
- const real = realpathSync3(entry);
10622
+ const real = realpathSync4(entry);
9865
10623
  refuseInsideSource(value, real, places);
9866
10624
  return real;
9867
10625
  }
@@ -9869,7 +10627,7 @@ function entryOf(value, named, isVersion, home) {
9869
10627
  if (isVersion) {
9870
10628
  if (isFile2(named))
9871
10629
  return named;
9872
- const root = join12(home, RUNTIME_SUBDIR);
10630
+ const root = join14(home, RUNTIME_SUBDIR);
9873
10631
  const installed = installedVersions(root);
9874
10632
  throw refusal3([
9875
10633
  `\u274C Refusing to start: \`--runtime=${value}\` names no installed runtime: ${named} is no file.`,
@@ -9878,7 +10636,7 @@ function entryOf(value, named, isVersion, home) {
9878
10636
  }
9879
10637
  if (isFile2(named))
9880
10638
  return named;
9881
- const inside = join12(named, RUNTIME_ENTRY);
10639
+ const inside = join14(named, RUNTIME_ENTRY);
9882
10640
  if (isDirectory(named) && isFile2(inside))
9883
10641
  return inside;
9884
10642
  throw refusal3([
@@ -9896,17 +10654,17 @@ function refuseInsideSource(value, path5, places) {
9896
10654
  ]);
9897
10655
  }
9898
10656
  function sourceDirs({ cwd, root }) {
9899
- const dirs = [join12(cwd, SOURCE_DIR), join12(root, SOURCE_DIR)];
9900
- const real = dirs.filter((dir) => existsSync16(dir)).map((dir) => realpathSync3(dir));
10657
+ const dirs = [join14(cwd, SOURCE_DIR), join14(root, SOURCE_DIR)];
10658
+ const real = dirs.filter((dir) => existsSync17(dir)).map((dir) => realpathSync4(dir));
9901
10659
  return [...new Set([...dirs, ...real])];
9902
10660
  }
9903
10661
  function isInside(path5, dir) {
9904
- const rel = relative3(dir, path5);
9905
- return rel === "" || !isAbsolute3(rel) && rel !== ".." && !rel.startsWith(`..${sep}`);
10662
+ const rel = relative4(dir, path5);
10663
+ return rel === "" || !isAbsolute4(rel) && rel !== ".." && !rel.startsWith(`..${sep}`);
9906
10664
  }
9907
10665
  function installedVersions(root) {
9908
10666
  try {
9909
- return readdirSync6(root).filter((name) => isFile2(join12(root, name, RUNTIME_ENTRY))).sort((a, b) => a.localeCompare(b));
10667
+ return readdirSync6(root).filter((name) => isFile2(join14(root, name, RUNTIME_ENTRY))).sort((a, b) => a.localeCompare(b));
9910
10668
  } catch {
9911
10669
  return [];
9912
10670
  }
@@ -9949,7 +10707,7 @@ async function runFromSelectedRuntime(seams) {
9949
10707
  }
9950
10708
  function realOrResolved(path5) {
9951
10709
  try {
9952
- return realpathSync3(path5);
10710
+ return realpathSync4(path5);
9953
10711
  } catch {
9954
10712
  return resolve10(path5);
9955
10713
  }
@@ -10037,11 +10795,11 @@ function eventOf(line) {
10037
10795
  }
10038
10796
 
10039
10797
  // src/triage/triage.ts
10040
- import { basename as basename6, join as join13 } from "path";
10798
+ import { basename as basename6, join as join15 } from "path";
10041
10799
 
10042
10800
  // src/effort/store/tracker-refs.ts
10043
10801
  import { randomUUID as randomUUID9 } from "crypto";
10044
- import { existsSync as existsSync17 } from "fs";
10802
+ import { existsSync as existsSync18 } from "fs";
10045
10803
  var SELECT_HELD = `
10046
10804
  SELECT seq, tracker_ref FROM findings
10047
10805
  WHERE session_id = ? AND artifact = ?
@@ -10164,7 +10922,7 @@ function writeTrackerRef(repoRoot, write, seams = {}) {
10164
10922
  wanted.json,
10165
10923
  collectedAt
10166
10924
  ];
10167
- const [action, stored] = withSqliteStore(path5, !existsSync17(path5), (db) => {
10925
+ const [action, stored] = withSqliteStore(path5, !existsSync18(path5), (db) => {
10168
10926
  const place = db.transaction(() => placeRef(db, path5, write, wanted, insertValues));
10169
10927
  return place.immediate();
10170
10928
  });
@@ -10173,14 +10931,14 @@ function writeTrackerRef(repoRoot, write, seams = {}) {
10173
10931
  function readTrackerRef(repoRoot, artifact) {
10174
10932
  checkArtifact("read", artifact);
10175
10933
  const path5 = sqliteStorePath(repoRoot);
10176
- if (!existsSync17(path5))
10934
+ if (!existsSync18(path5))
10177
10935
  return null;
10178
10936
  const oldest = withSqliteStore(path5, false, (db) => db.query(SELECT_OLDEST).get(artifact));
10179
10937
  return oldest === null ? null : heldRefOf(path5, oldest.seq, oldest.tracker_ref).ref;
10180
10938
  }
10181
10939
 
10182
10940
  // src/triage/triage.ts
10183
- var PRIVATE_TRIAGE_DIR = join13(".rafa", "triage", "private");
10941
+ var PRIVATE_TRIAGE_DIR = join15(".rafa", "triage", "private");
10184
10942
  var SECRET_ENV_NAMES = ["GITHUB_TOKEN", "ANTHROPIC_API_KEY", "LINEAR_API_KEY"];
10185
10943
  var TRIAGE_MODULE = "unassigned";
10186
10944
  var BLOCKER_SEPARATOR = "; ";
@@ -10191,7 +10949,7 @@ var MIN_FENCE_LENGTH = 3;
10191
10949
  var ISSUE_OPENING = "An out-of-scope bug a rafa task session reported. The loop filed it and" + " dispatches no task for it: a plan that wants it fixed declares a task.";
10192
10950
  var COMMENT_OPENING = "Reported again by a rafa task session.";
10193
10951
  function privateTriageDir(repoRoot) {
10194
- return join13(repoRoot, PRIVATE_TRIAGE_DIR);
10952
+ return join15(repoRoot, PRIVATE_TRIAGE_DIR);
10195
10953
  }
10196
10954
  function createPrivateTriageTracker(repoRoot, options = {}) {
10197
10955
  return createLocalTracker({
@@ -10768,6 +11526,13 @@ async function start(args, repoRoot) {
10768
11526
  args
10769
11527
  });
10770
11528
  guardRunBranch(planStub2, branch, args);
11529
+ await announceRiskTotal({
11530
+ repoRoot,
11531
+ home: homedir7(),
11532
+ planPath,
11533
+ config: runConfig.config,
11534
+ environment: process.env
11535
+ });
10771
11536
  await requireNoticesAnswered();
10772
11537
  setActivePlanStub(planStub2);
10773
11538
  const session = openRunSession({ repoRoot, planPath, planStub: planStub2, branch });