@open-tomato/rafa 0.9.2 → 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.
package/dist/index.js CHANGED
@@ -30,10 +30,12 @@ import {
30
30
  import {
31
31
  CORE_ADAPTER_REGISTRY,
32
32
  ClaudePlannerError,
33
+ CommandExit,
33
34
  EFFORT_KEY_PROJECTIONS,
34
35
  LONE_SURROGATE,
35
36
  PORT_VERSIONS,
36
37
  checkUsage,
38
+ commandSpelling,
37
39
  createAdapterRegistry,
38
40
  createGhRunner,
39
41
  createLocalTracker,
@@ -48,7 +50,7 @@ import {
48
50
  sqliteStorePath,
49
51
  withSqliteStore,
50
52
  writeSqliteStore
51
- } from "./index-eqsm86n7.js";
53
+ } from "./index-srdtw7w1.js";
52
54
  import {
53
55
  BUDGET_FLAG,
54
56
  CLAUDE_SETTING_SOURCES,
@@ -132,27 +134,6 @@ import { existsSync as existsSync2, readdirSync, statSync } from "fs";
132
134
  import { homedir } from "os";
133
135
  import { join, resolve } from "path";
134
136
 
135
- // src/cli/command.ts
136
- var MAX_EXIT_CODE = 255;
137
-
138
- class CommandExit extends Error {
139
- exitCode;
140
- constructor(exitCode, message = "") {
141
- super(message);
142
- if (!Number.isInteger(exitCode) || exitCode < 0 || exitCode > MAX_EXIT_CODE) {
143
- throw new TypeError(`CommandExit: exit code is ${describeValue(exitCode)}, expected a whole number from 0 to ${MAX_EXIT_CODE}`);
144
- }
145
- this.name = "CommandExit";
146
- this.exitCode = exitCode;
147
- }
148
- }
149
- function isTopLevel(command) {
150
- return command.subject === command.action;
151
- }
152
- function commandSpelling(command) {
153
- return isTopLevel(command) ? command.subject : `${command.subject} ${command.action}`;
154
- }
155
-
156
137
  // src/effort/session-log.ts
157
138
  import { createReadStream } from "fs";
158
139
  import { basename } from "path";
@@ -1914,7 +1895,7 @@ async function report(args, repoRoot) {
1914
1895
  }
1915
1896
  }
1916
1897
  // package.json
1917
- var version = "0.9.2";
1898
+ var version = "0.11.0";
1918
1899
 
1919
1900
  // src/modules/manifest.ts
1920
1901
  var MANIFEST_VERSION = 1;
@@ -2204,9 +2185,11 @@ function requireProject(context, command) {
2204
2185
  return context.project;
2205
2186
  }
2206
2187
  function resolvePlansDir(project, command, warn) {
2188
+ return plansDirAt(project.root, resolveProjectConfig(project, command, warn).planDir);
2189
+ }
2190
+ function resolveProjectConfig(project, command, warn) {
2207
2191
  try {
2208
- const { config } = loadConfig({ root: project.root, home: project.home }, {}, warn);
2209
- return plansDirAt(project.root, config.planDir);
2192
+ return loadConfig({ root: project.root, home: project.home }, {}, warn).config;
2210
2193
  } catch (error) {
2211
2194
  if (!(error instanceof ConfigError))
2212
2195
  throw error;
@@ -9708,12 +9691,768 @@ async function finishRelease(input, seams = {}) {
9708
9691
  return { outcome: "released", version: version2, subject, sha, sentence: null, body: null };
9709
9692
  }
9710
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
+
9711
10450
  // src/start/session.ts
9712
10451
  import { randomUUID as randomUUID8 } from "crypto";
9713
- import { relative as relative2 } from "path";
10452
+ import { relative as relative3 } from "path";
9714
10453
  var NOTHING_DISPATCHED = " Nothing was checked and nothing was dispatched.";
9715
10454
  function recordLabel(repoRoot, sessionId) {
9716
- return relative2(repoRoot, sessionFilePath(repoRoot, sessionId));
10455
+ return relative3(repoRoot, sessionFilePath(repoRoot, sessionId));
9717
10456
  }
9718
10457
  function conflictLine(repoRoot, conflict) {
9719
10458
  const { record } = conflict;
@@ -9743,7 +10482,7 @@ function openRefusal(repoRoot, draft, error) {
9743
10482
  if (!(error instanceof SessionRecordError) && !isSystemError)
9744
10483
  return null;
9745
10484
  return [
9746
- `\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.`,
9747
10486
  ` ${messageOf(error)}`,
9748
10487
  NOTHING_DISPATCHED
9749
10488
  ].join(`
@@ -9779,7 +10518,7 @@ function openRunSession(options) {
9779
10518
  const draft = {
9780
10519
  sessionId: (seams.newSessionId ?? randomUUID8)(),
9781
10520
  planStub: options.planStub,
9782
- plan: relative2(repoRoot, options.planPath),
10521
+ plan: relative3(repoRoot, options.planPath),
9783
10522
  branch: options.branch,
9784
10523
  pid: seams.pid ?? process.pid,
9785
10524
  startedAt: (seams.now ?? (() => new Date))().toISOString()
@@ -9856,11 +10595,11 @@ function announcePlanIssues(planContent) {
9856
10595
  }
9857
10596
 
9858
10597
  // src/start/runtime.ts
9859
- 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";
9860
10599
  import { homedir as homedir6 } from "os";
9861
- 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";
9862
10601
  var RUNTIME_FLAG = "--runtime";
9863
- var RUNTIME_SUBDIR = join12(".rafa", "runtime");
10602
+ var RUNTIME_SUBDIR = join14(".rafa", "runtime");
9864
10603
  var RUNTIME_ENTRY = "cli.js";
9865
10604
  var SOURCE_DIR = "src";
9866
10605
  var RUNTIME_COMMAND = ["start"];
@@ -9877,10 +10616,10 @@ function resolveRuntime(value, places) {
9877
10616
  throw refusal3([`\u274C Refusing to start: \`--runtime\` names no runtime. Spell it ${SPELLING}.`]);
9878
10617
  }
9879
10618
  const isVersion = VERSION_SHAPE2.test(value);
9880
- 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);
9881
10620
  refuseInsideSource(value, named, places);
9882
10621
  const entry = entryOf(value, named, isVersion, places.home);
9883
- const real = realpathSync3(entry);
10622
+ const real = realpathSync4(entry);
9884
10623
  refuseInsideSource(value, real, places);
9885
10624
  return real;
9886
10625
  }
@@ -9888,7 +10627,7 @@ function entryOf(value, named, isVersion, home) {
9888
10627
  if (isVersion) {
9889
10628
  if (isFile2(named))
9890
10629
  return named;
9891
- const root = join12(home, RUNTIME_SUBDIR);
10630
+ const root = join14(home, RUNTIME_SUBDIR);
9892
10631
  const installed = installedVersions(root);
9893
10632
  throw refusal3([
9894
10633
  `\u274C Refusing to start: \`--runtime=${value}\` names no installed runtime: ${named} is no file.`,
@@ -9897,7 +10636,7 @@ function entryOf(value, named, isVersion, home) {
9897
10636
  }
9898
10637
  if (isFile2(named))
9899
10638
  return named;
9900
- const inside = join12(named, RUNTIME_ENTRY);
10639
+ const inside = join14(named, RUNTIME_ENTRY);
9901
10640
  if (isDirectory(named) && isFile2(inside))
9902
10641
  return inside;
9903
10642
  throw refusal3([
@@ -9915,17 +10654,17 @@ function refuseInsideSource(value, path5, places) {
9915
10654
  ]);
9916
10655
  }
9917
10656
  function sourceDirs({ cwd, root }) {
9918
- const dirs = [join12(cwd, SOURCE_DIR), join12(root, SOURCE_DIR)];
9919
- 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));
9920
10659
  return [...new Set([...dirs, ...real])];
9921
10660
  }
9922
10661
  function isInside(path5, dir) {
9923
- const rel = relative3(dir, path5);
9924
- return rel === "" || !isAbsolute3(rel) && rel !== ".." && !rel.startsWith(`..${sep}`);
10662
+ const rel = relative4(dir, path5);
10663
+ return rel === "" || !isAbsolute4(rel) && rel !== ".." && !rel.startsWith(`..${sep}`);
9925
10664
  }
9926
10665
  function installedVersions(root) {
9927
10666
  try {
9928
- 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));
9929
10668
  } catch {
9930
10669
  return [];
9931
10670
  }
@@ -9968,7 +10707,7 @@ async function runFromSelectedRuntime(seams) {
9968
10707
  }
9969
10708
  function realOrResolved(path5) {
9970
10709
  try {
9971
- return realpathSync3(path5);
10710
+ return realpathSync4(path5);
9972
10711
  } catch {
9973
10712
  return resolve10(path5);
9974
10713
  }
@@ -10056,11 +10795,11 @@ function eventOf(line) {
10056
10795
  }
10057
10796
 
10058
10797
  // src/triage/triage.ts
10059
- import { basename as basename6, join as join13 } from "path";
10798
+ import { basename as basename6, join as join15 } from "path";
10060
10799
 
10061
10800
  // src/effort/store/tracker-refs.ts
10062
10801
  import { randomUUID as randomUUID9 } from "crypto";
10063
- import { existsSync as existsSync17 } from "fs";
10802
+ import { existsSync as existsSync18 } from "fs";
10064
10803
  var SELECT_HELD = `
10065
10804
  SELECT seq, tracker_ref FROM findings
10066
10805
  WHERE session_id = ? AND artifact = ?
@@ -10183,7 +10922,7 @@ function writeTrackerRef(repoRoot, write, seams = {}) {
10183
10922
  wanted.json,
10184
10923
  collectedAt
10185
10924
  ];
10186
- const [action, stored] = withSqliteStore(path5, !existsSync17(path5), (db) => {
10925
+ const [action, stored] = withSqliteStore(path5, !existsSync18(path5), (db) => {
10187
10926
  const place = db.transaction(() => placeRef(db, path5, write, wanted, insertValues));
10188
10927
  return place.immediate();
10189
10928
  });
@@ -10192,14 +10931,14 @@ function writeTrackerRef(repoRoot, write, seams = {}) {
10192
10931
  function readTrackerRef(repoRoot, artifact) {
10193
10932
  checkArtifact("read", artifact);
10194
10933
  const path5 = sqliteStorePath(repoRoot);
10195
- if (!existsSync17(path5))
10934
+ if (!existsSync18(path5))
10196
10935
  return null;
10197
10936
  const oldest = withSqliteStore(path5, false, (db) => db.query(SELECT_OLDEST).get(artifact));
10198
10937
  return oldest === null ? null : heldRefOf(path5, oldest.seq, oldest.tracker_ref).ref;
10199
10938
  }
10200
10939
 
10201
10940
  // src/triage/triage.ts
10202
- var PRIVATE_TRIAGE_DIR = join13(".rafa", "triage", "private");
10941
+ var PRIVATE_TRIAGE_DIR = join15(".rafa", "triage", "private");
10203
10942
  var SECRET_ENV_NAMES = ["GITHUB_TOKEN", "ANTHROPIC_API_KEY", "LINEAR_API_KEY"];
10204
10943
  var TRIAGE_MODULE = "unassigned";
10205
10944
  var BLOCKER_SEPARATOR = "; ";
@@ -10210,7 +10949,7 @@ var MIN_FENCE_LENGTH = 3;
10210
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.";
10211
10950
  var COMMENT_OPENING = "Reported again by a rafa task session.";
10212
10951
  function privateTriageDir(repoRoot) {
10213
- return join13(repoRoot, PRIVATE_TRIAGE_DIR);
10952
+ return join15(repoRoot, PRIVATE_TRIAGE_DIR);
10214
10953
  }
10215
10954
  function createPrivateTriageTracker(repoRoot, options = {}) {
10216
10955
  return createLocalTracker({
@@ -10787,6 +11526,13 @@ async function start(args, repoRoot) {
10787
11526
  args
10788
11527
  });
10789
11528
  guardRunBranch(planStub2, branch, args);
11529
+ await announceRiskTotal({
11530
+ repoRoot,
11531
+ home: homedir7(),
11532
+ planPath,
11533
+ config: runConfig.config,
11534
+ environment: process.env
11535
+ });
10790
11536
  await requireNoticesAnswered();
10791
11537
  setActivePlanStub(planStub2);
10792
11538
  const session = openRunSession({ repoRoot, planPath, planStub: planStub2, branch });