@open-tomato/rafa 0.34.0 → 0.34.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -820,6 +820,20 @@ is the old one removed, so no reader meets a half-replaced runtime. The
820
820
  link lands by a rename too, so a shell meets the old link or the new one
821
821
  and never none.
822
822
 
823
+ ## Bringing a project up to the installed rafa
824
+
825
+ `rafa self-update` moves the rafa every project on the device runs, and
826
+ leaves what each project holds as it was set up. `rafa update current`,
827
+ run inside a project, brings that project to the installed rafa: it
828
+ creates the `.rafa/` folders and the board labels newer versions expect,
829
+ and records the version in `rafa.lock` at the repository root, a small
830
+ JSON file meant to be committed. It works within a patch range: the
831
+ installed version must share the major and minor of the one the lock
832
+ records, and a project with no lock is adopted. It prints every change
833
+ first, and `--dry-run` stops there; otherwise it asks once, or takes
834
+ `--yes`. The rest of `rafa update` (`self`, `project`, `board`, `next`,
835
+ `latest`) is in development and says so (#713).
836
+
823
837
  ## Runtime
824
838
 
825
839
  The build targets bun, and `engines` names `bun` alone: there is no
@@ -0,0 +1,53 @@
1
+ ---
2
+ name: rafa-stretch-analyst
3
+ description: Operator agent, never a loop task executor. Read-only. Turns a stretch's raw data (run records, loop logs, effort store, kernel log, board) into checked insights. Takes the person's hunch, tests it against the data, and names the alternative explanation and the check that separates them. Also called da2 or da². Start it with `bun run stretch analyst` in the project's main checkout, or as the third window of `bun run stretch start`.
4
+ tools: Read, Bash
5
+ provenance: first-party
6
+ source: rafa
7
+ stage: alpha
8
+ ---
9
+
10
+ Alpha: tested on rafa's own development, may become a feature.
11
+
12
+ ## Mission
13
+
14
+ The person works on hunches and curiosity. Your job is to say which hunch the
15
+ data supports, which it does not, and which it cannot answer yet. Never
16
+ change anything: no writes to the board, branches, config, or the stretch
17
+ folder except `analysis.md`.
18
+
19
+ ## Vision
20
+
21
+ A finding is only as good as its input. Before any number, check the input:
22
+
23
+ - time that is not work (machine asleep, waiting on the person, a paused loop);
24
+ - units that do not compare (tokens with input against output-only tokens);
25
+ - small samples (a p90 over 7 tasks is one task);
26
+ - a cause counted twice (one failure filed as several bugs);
27
+ - the loudest signal that is not the costliest one.
28
+
29
+ ## Sources
30
+
31
+ | Source | What it holds | How to read it |
32
+ |---|---|---|
33
+ | `.rafa/runs/<id>.json` | test steps: kind, scope, failures, `[Ns]` duration | `python3`/`jq` over `steps` |
34
+ | `.rafa/stretch/<n>/loop-*.log` | `rafa·` lines: task done minutes and tokens | grep the `done` lines |
35
+ | `.rafa/effort/effort.sqlite` | sessions, task reports, dispatches | `sqlite3 -readonly` |
36
+ | `journalctl -k` (`pmset -g log` on macOS) | suspend and resume times | subtract from wall time |
37
+ | `gh issue list --search created:>=…` | filings per loop | group by cause |
38
+ | `bucket.md`, `pit-stops.md`, `watch.md` | plan, decisions, what was seen | timestamps of each change |
39
+
40
+ ## Method, for each question
41
+
42
+ 1. Restate the hunch as a claim that can be false.
43
+ 2. Measure it: the number, its source, and the command that produced it.
44
+ 3. Name at least one other mechanism that fits the same numbers, and the
45
+ check that tells them apart. Run the check if the data allows it.
46
+ 4. Verdict: supported, not supported, or open (with what data would close it).
47
+
48
+ ## Report
49
+
50
+ Write to `.rafa/stretch/<n>/analysis.md` and answer the person in the same
51
+ shape: one line per finding as claim → measurement → source → verdict. Put
52
+ the one finding that changes a decision first. Leave out findings that
53
+ change nothing.
@@ -31,6 +31,11 @@ steps: a helper, never a hinderer. Hold every step you take to it:
31
31
  written; a flag you always add is a flag rafa may not need. Note both
32
32
  in the report.
33
33
  - Stop and ask only where there is a risk. Everything else is yours.
34
+ - Move an issue between epics with `rafa epic move <issue> --to=<epic>`,
35
+ never by adding a second `epic:` label: where the project installs the
36
+ epic guard, a second label added beside the first is removed, and its
37
+ comment lands on the issue. Turning the guard on or off is the
38
+ person's, never yours.
34
39
 
35
40
  ## Two stops, everything else unattended
36
41
 
package/dist/cli.js CHANGED
@@ -2233,7 +2233,7 @@ function restoreRunningCommand(previous) {
2233
2233
  var running = null;
2234
2234
 
2235
2235
  // package.json
2236
- var name = "@open-tomato/rafa", version = "0.34.0";
2236
+ var name = "@open-tomato/rafa", version = "0.34.1";
2237
2237
  var init_package = () => {};
2238
2238
 
2239
2239
  // src/cli/version.ts
@@ -64612,6 +64612,504 @@ var init_status4 = __esm(() => {
64612
64612
  status_default3 = createStatusCommand();
64613
64613
  });
64614
64614
 
64615
+ // src/commands/update/stub.ts
64616
+ function stubMessage(action, issue5) {
64617
+ return `rafa update ${action} is a feature in development, tracked in ${ISSUES_URL}/${String(issue5)}.` + " Nothing was changed.";
64618
+ }
64619
+ function createUpdateStub(spec) {
64620
+ const command = {
64621
+ name: `update ${spec.action}`,
64622
+ subject: "update",
64623
+ action: spec.action,
64624
+ summary: `${spec.summary} (in development, #${String(spec.issue)})`,
64625
+ description: `${spec.summary}. In development: it refuses with exit code 1, naming #${String(spec.issue)},` + " and changes nothing.",
64626
+ args: [],
64627
+ flags: [],
64628
+ examples: [{ cmd: `rafa update ${spec.action}`, note: `Refuses, naming #${String(spec.issue)}; changes nothing.` }],
64629
+ outputs: ["text", "json"],
64630
+ needsProject: false,
64631
+ ...spec.hidden === true ? { hidden: true } : {},
64632
+ run: () => Promise.reject(new CommandExit(1, stubMessage(spec.action, spec.issue)))
64633
+ };
64634
+ return Object.freeze(command);
64635
+ }
64636
+ var ISSUES_URL = "https://github.com/open-tomato/rafa/issues";
64637
+ var init_stub = __esm(() => {
64638
+ init_command();
64639
+ });
64640
+
64641
+ // src/commands/update/board.ts
64642
+ var board_default;
64643
+ var init_board = __esm(() => {
64644
+ init_stub();
64645
+ board_default = createUpdateStub({ action: "board", summary: "apply the board's newest setup and its migrations", issue: 717 });
64646
+ });
64647
+
64648
+ // src/project/lock.ts
64649
+ import { existsSync as existsSync63, lstatSync as lstatSync3, readFileSync as readFileSync79, writeFileSync as writeFileSync31 } from "fs";
64650
+ import { join as join98 } from "path";
64651
+ function lockPath(root) {
64652
+ return join98(root, LOCK_FILE);
64653
+ }
64654
+ function projectLockText(version2) {
64655
+ const lock = { lockfileVersion: LOCKFILE_VERSION, rafa: version2 };
64656
+ return `${JSON.stringify(lock, null, 2)}
64657
+ `;
64658
+ }
64659
+ function isLink(path9) {
64660
+ try {
64661
+ return lstatSync3(path9).isSymbolicLink();
64662
+ } catch {
64663
+ return false;
64664
+ }
64665
+ }
64666
+ function refuse16(problem2) {
64667
+ throw new ProjectLockError(`${LOCK_FILE} ${problem2}`);
64668
+ }
64669
+ function lockOf(value) {
64670
+ if (typeof value !== "object" || value === null || Array.isArray(value))
64671
+ refuse16("holds no JSON object");
64672
+ const record = value;
64673
+ const format = record["lockfileVersion"];
64674
+ if (format !== LOCKFILE_VERSION) {
64675
+ refuse16(`is in lockfileVersion ${String(format)}, and this rafa reads ${String(LOCKFILE_VERSION)}: a newer rafa wrote it`);
64676
+ }
64677
+ const rafa = record["rafa"];
64678
+ if (typeof rafa !== "string" || parseSemanticVersion(rafa) === null) {
64679
+ refuse16(`names rafa ${JSON.stringify(rafa)}, which is no version`);
64680
+ }
64681
+ return { lockfileVersion: LOCKFILE_VERSION, rafa };
64682
+ }
64683
+ function readProjectLock(root) {
64684
+ const path9 = lockPath(root);
64685
+ if (!existsSync63(path9)) {
64686
+ if (isLink(path9))
64687
+ refuse16("is a link to nothing");
64688
+ return null;
64689
+ }
64690
+ let text2;
64691
+ try {
64692
+ text2 = readFileSync79(path9, "utf8");
64693
+ } catch (error) {
64694
+ refuse16(`could not be read: ${messageOf(error)}`);
64695
+ }
64696
+ let value;
64697
+ try {
64698
+ value = JSON.parse(text2);
64699
+ } catch {
64700
+ refuse16("is not JSON");
64701
+ }
64702
+ return lockOf(value);
64703
+ }
64704
+ function writeProjectLock(root, version2) {
64705
+ writeFileSync31(lockPath(root), projectLockText(version2), "utf8");
64706
+ }
64707
+ var LOCK_FILE = "rafa.lock", LOCKFILE_VERSION = 1, ProjectLockError;
64708
+ var init_lock = __esm(() => {
64709
+ init_config_sections();
64710
+ init_version2();
64711
+ ProjectLockError = class ProjectLockError extends Error {
64712
+ name = "ProjectLockError";
64713
+ };
64714
+ });
64715
+
64716
+ // src/project/update-current.ts
64717
+ import { existsSync as existsSync64 } from "fs";
64718
+ import { join as join99 } from "path";
64719
+ function missingFolders(root) {
64720
+ return PROJECT_TREE.map((name2) => join99(SCOPE_DIR, name2)).filter((relative21) => !existsSync64(join99(root, relative21)));
64721
+ }
64722
+ async function readBoard3(access) {
64723
+ if (access.gh === null)
64724
+ return { kind: "skipped", reason: access.reason };
64725
+ try {
64726
+ const held = await listBoardLabels(access.gh);
64727
+ return { kind: "labels", missing: missingBoardLabels(held).map((label) => label.name) };
64728
+ } catch (error) {
64729
+ return { kind: "skipped", reason: messageOf(error) };
64730
+ }
64731
+ }
64732
+ function lockChangeOf(range) {
64733
+ if (range.kind === "adopt")
64734
+ return "created";
64735
+ return range.kind === "patch" ? "updated" : "unchanged";
64736
+ }
64737
+ function fromOf(range) {
64738
+ if (range.kind === "patch")
64739
+ return range.from;
64740
+ if (range.kind === "same")
64741
+ return range.to;
64742
+ return null;
64743
+ }
64744
+ async function readUpdatePlan(roots, range, board) {
64745
+ const { root, lockRoot } = roots;
64746
+ return {
64747
+ root,
64748
+ lockRoot,
64749
+ from: fromOf(range),
64750
+ to: range.to,
64751
+ folders: missingFolders(root),
64752
+ board: await readBoard3(board),
64753
+ deprecations: DEPRECATIONS,
64754
+ lock: lockChangeOf(range)
64755
+ };
64756
+ }
64757
+ function planChanges(plan2) {
64758
+ const labels = plan2.board.kind === "labels" ? plan2.board.missing.length : 0;
64759
+ return plan2.folders.length > 0 || labels > 0 || plan2.deprecations.length > 0 || plan2.lock !== "unchanged";
64760
+ }
64761
+ function foldersNowThere(plan2) {
64762
+ return plan2.folders.filter((relative21) => existsSync64(join99(plan2.root, relative21)));
64763
+ }
64764
+ function writeFolders(plan2) {
64765
+ if (plan2.folders.length === 0)
64766
+ return [];
64767
+ try {
64768
+ return writeProjectScope(plan2.root).filter((write) => write.change === "created").map((write) => write.path);
64769
+ } catch (error) {
64770
+ throw new UpdateStepError("folders", messageOf(error), foldersNowThere(plan2));
64771
+ }
64772
+ }
64773
+ function createdLabels(parts) {
64774
+ return parts.filter((part) => part.outcome === "created").map((part) => `label ${part.name}`);
64775
+ }
64776
+ async function applyUpdatePlan(plan2, gh) {
64777
+ const folders = writeFolders(plan2);
64778
+ const labels = gh !== null && plan2.board.kind === "labels" && plan2.board.missing.length > 0 ? await setUpLabels(gh) : [];
64779
+ if (plan2.lock !== "unchanged") {
64780
+ try {
64781
+ writeProjectLock(plan2.lockRoot, plan2.to);
64782
+ } catch (error) {
64783
+ throw new UpdateStepError("lock", messageOf(error), [...folders, ...createdLabels(labels)]);
64784
+ }
64785
+ }
64786
+ return { folders, labels, lock: plan2.lock };
64787
+ }
64788
+ var DEPRECATIONS, UpdateStepError;
64789
+ var init_update_current = __esm(() => {
64790
+ init_setup();
64791
+ init_config_sections();
64792
+ init_lock();
64793
+ init_scaffold();
64794
+ init_scope();
64795
+ DEPRECATIONS = Object.freeze([]);
64796
+ UpdateStepError = class UpdateStepError extends Error {
64797
+ step;
64798
+ written;
64799
+ name = "UpdateStepError";
64800
+ constructor(step2, message, written3) {
64801
+ super(message);
64802
+ this.step = step2;
64803
+ this.written = written3;
64804
+ }
64805
+ };
64806
+ });
64807
+
64808
+ // src/project/update-range.ts
64809
+ function refused6(reason, message) {
64810
+ return { kind: "refused", reason, message };
64811
+ }
64812
+ function readCurrentRange(recorded, installed) {
64813
+ const to = parseSemanticVersion(installed);
64814
+ if (to === null)
64815
+ return refused6("unreadable", `the installed rafa answers "${installed}", which is no version`);
64816
+ if (recorded === null)
64817
+ return { kind: "adopt", to: installed };
64818
+ const from = parseSemanticVersion(recorded);
64819
+ if (from === null)
64820
+ return refused6("unreadable", `the project records rafa "${recorded}", which is no version`);
64821
+ const order = compareVersions(to, from);
64822
+ if (order < 0) {
64823
+ return refused6("older", `the installed rafa ${installed} is older than the project's ${recorded}, and no downgrade runs before 1.0.0`);
64824
+ }
64825
+ if (to.major !== from.major) {
64826
+ return refused6("newer-major", `the installed rafa ${installed} is a newer major than the project's ${recorded}; that move is rafa update latest`);
64827
+ }
64828
+ if (to.minor !== from.minor) {
64829
+ return refused6("newer-minor", `the installed rafa ${installed} is a newer minor than the project's ${recorded}; that move is rafa update next`);
64830
+ }
64831
+ return order === 0 ? { kind: "same", to: installed } : { kind: "patch", from: recorded, to: installed };
64832
+ }
64833
+ var init_update_range = __esm(() => {
64834
+ init_status();
64835
+ init_version2();
64836
+ });
64837
+
64838
+ // src/commands/update/current.ts
64839
+ function checkoutLockRoot(root, dir) {
64840
+ const run = Bun.spawnSync(["git", "rev-parse", "--show-toplevel"], { cwd: dir, stdout: "pipe", stderr: "pipe" });
64841
+ if (run.exitCode !== 0)
64842
+ return root;
64843
+ const top = run.stdout.toString().trim();
64844
+ return top !== "" && mainCheckoutOf(top) === root ? top : root;
64845
+ }
64846
+ function readSwitch4(context, name2) {
64847
+ const value = context.flags[name2];
64848
+ if (value === undefined || value === false || value === "false")
64849
+ return false;
64850
+ if (value === true || value === "true")
64851
+ return true;
64852
+ throw new CommandExit(1, `\u274C --${name2} takes no value, and read "${String(value)}" as one
64853
+ Usage: ${USAGE47}`);
64854
+ }
64855
+ function couldNotRun(problem2) {
64856
+ return new CommandExit(2, `FAIL \u2014 ${problem2}
64857
+ nothing was changed.`);
64858
+ }
64859
+ function projectOf17(context) {
64860
+ if (context.project === null)
64861
+ throw new Error("rafa update current runs inside a project, and was handed none");
64862
+ return context.project;
64863
+ }
64864
+ function boardAccess(project, seams) {
64865
+ let configured;
64866
+ try {
64867
+ configured = loadConfig({ root: project.root, home: project.home }, {}, () => {}).config.prProvider;
64868
+ } catch (error) {
64869
+ throw couldNotRun(`config: ${messageOf(error)}`);
64870
+ }
64871
+ const provider = resolvePrProvider({ configured, dir: project.root, readRemote: seams.readRemote }).provider;
64872
+ return provider === "gh" ? { gh: seams.openGh(project.root), reason: "" } : { gh: null, reason: `pr.provider is ${provider}, so there is no GitHub board` };
64873
+ }
64874
+ function foldersText(plan2) {
64875
+ if (plan2.folders.length === 0)
64876
+ return "nothing missing";
64877
+ return `create ${plan2.folders.join(", ")}`;
64878
+ }
64879
+ function boardText2(plan2) {
64880
+ if (plan2.board.kind === "skipped")
64881
+ return `skipped: ${plan2.board.reason}`;
64882
+ if (plan2.board.missing.length === 0)
64883
+ return "every label present";
64884
+ return `create labels ${plan2.board.missing.join(", ")}`;
64885
+ }
64886
+ function deprecationsText(plan2) {
64887
+ if (plan2.deprecations.length === 0)
64888
+ return "none registered yet (#718)";
64889
+ return `${String(plan2.deprecations.length)} to run`;
64890
+ }
64891
+ function lockText(plan2) {
64892
+ const where = plan2.lockRoot === plan2.root ? "" : ` in ${plan2.lockRoot}`;
64893
+ if (plan2.lock === "unchanged")
64894
+ return `unchanged at ${plan2.to}${where}`;
64895
+ if (plan2.lock === "created")
64896
+ return `create at ${plan2.to}${where}`;
64897
+ return `update ${String(plan2.from)} \u2192 ${plan2.to}${where}`;
64898
+ }
64899
+ function planLines2(plan2) {
64900
+ const from = plan2.from ?? `no ${LOCK_FILE}`;
64901
+ return [
64902
+ `rafa update current: ${plan2.root}, from ${from} to ${plan2.to}`,
64903
+ ` folders ${foldersText(plan2)}`,
64904
+ ` board ${boardText2(plan2)}`,
64905
+ ` deprecations ${deprecationsText(plan2)}`,
64906
+ ` ${LOCK_FILE} ${lockText(plan2)}`
64907
+ ];
64908
+ }
64909
+ function appliedLines(applied, plan2, context) {
64910
+ for (const path9 of applied.folders)
64911
+ context.output.info(`created ${path9}`);
64912
+ for (const part of applied.labels) {
64913
+ if (part.outcome === "created")
64914
+ context.output.info(`created label ${part.name}`);
64915
+ if (part.outcome === "refused")
64916
+ context.output.warn(`label ${part.name} was not created: ${part.detail}`);
64917
+ }
64918
+ if (applied.lock !== "unchanged")
64919
+ context.output.info(`wrote ${LOCK_FILE} at ${plan2.to}`);
64920
+ }
64921
+ async function confirmed3(yes, seams) {
64922
+ if (yes)
64923
+ return true;
64924
+ if (!seams.isTerminal()) {
64925
+ throw new CommandExit(1, "REFUSED \u2014 rafa update current asks before it changes anything, and there is no" + " terminal to ask on. Run it with --dry-run to see the changes, then with --yes to apply them." + `
64926
+ nothing was changed.`);
64927
+ }
64928
+ const prompter = seams.openPrompter();
64929
+ try {
64930
+ const answer5 = await prompter.ask(APPLY_QUESTION);
64931
+ return answer5 !== null && YES_ANSWERS7.includes(answer5.trim().toLowerCase());
64932
+ } finally {
64933
+ prompter.close();
64934
+ }
64935
+ }
64936
+ function readRange2(root, installed) {
64937
+ let recorded;
64938
+ try {
64939
+ recorded = readProjectLock(root)?.rafa ?? null;
64940
+ } catch (error) {
64941
+ throw couldNotRun(messageOf(error));
64942
+ }
64943
+ const range = readCurrentRange(recorded, installed);
64944
+ if (range.kind === "refused")
64945
+ throw new CommandExit(1, `REFUSED \u2014 ${range.message}
64946
+ nothing was changed.`);
64947
+ return range;
64948
+ }
64949
+ function refuseConflicts(project) {
64950
+ const conflicts2 = scaffoldConflicts(project.root, project.home);
64951
+ if (conflicts2.length === 0)
64952
+ return;
64953
+ throw couldNotRun(`the project scope cannot be written:
64954
+ ${conflicts2.map((conflict) => ` ${conflict}`).join(`
64955
+ `)}`);
64956
+ }
64957
+ function stoppedPartWay(error) {
64958
+ const kept = error.written.length === 0 ? "Nothing was written before it." : `Already written, and kept: ${error.written.join(", ")}.`;
64959
+ return new CommandExit(2, `FAIL \u2014 ${error.step}: ${error.message}
64960
+ stopped part-way. ${kept}` + `
64961
+ Fix the cause and run rafa update current again: each step writes only what is missing.`);
64962
+ }
64963
+ async function applyAndReport(plan2, gh, context) {
64964
+ let applied;
64965
+ try {
64966
+ applied = await applyUpdatePlan(plan2, gh);
64967
+ } catch (error) {
64968
+ if (error instanceof UpdateStepError)
64969
+ throw stoppedPartWay(error);
64970
+ throw error;
64971
+ }
64972
+ appliedLines(applied, plan2, context);
64973
+ return applied;
64974
+ }
64975
+ function refusedLabels(applied) {
64976
+ const refused7 = applied.labels.filter((part) => part.outcome === "refused");
64977
+ if (refused7.length === 0)
64978
+ return null;
64979
+ return new CommandExit(1, `${String(refused7.length)} board label(s) were not created; everything else was applied.` + " Run rafa update current again once gh can create them.");
64980
+ }
64981
+ function stopLine2(plan2, dryRun) {
64982
+ if (!planChanges(plan2))
64983
+ return "Nothing to change.";
64984
+ if (dryRun)
64985
+ return "Dry run: nothing was written. Run rafa update current to apply it.";
64986
+ return null;
64987
+ }
64988
+ async function runUpdateCurrent(context, seams) {
64989
+ expectNoArgument(context.args, USAGE47);
64990
+ const dryRun = readSwitch4(context, "dry-run");
64991
+ const yes = readSwitch4(context, "yes");
64992
+ const project = projectOf17(context);
64993
+ const lockRoot = seams.lockRootOf(project.root);
64994
+ const range = readRange2(lockRoot, seams.installed);
64995
+ refuseConflicts(project);
64996
+ const access = boardAccess(project, seams);
64997
+ const plan2 = await readUpdatePlan({ root: project.root, lockRoot }, range, access);
64998
+ for (const line of planLines2(plan2))
64999
+ context.output.info(line);
65000
+ const finish2 = (applied2) => {
65001
+ if (context.outputMode === "json")
65002
+ context.output.result({ plan: plan2, applied: applied2 });
65003
+ };
65004
+ const stop = stopLine2(plan2, dryRun);
65005
+ if (stop !== null) {
65006
+ context.output.info(stop);
65007
+ finish2(null);
65008
+ return;
65009
+ }
65010
+ context.output.warn("rafa cannot roll these changes back. Run it with --dry-run first to only see them.");
65011
+ if (!await confirmed3(yes, seams)) {
65012
+ context.output.info("Nothing was changed.");
65013
+ finish2(null);
65014
+ return;
65015
+ }
65016
+ const applied = await applyAndReport(plan2, access.gh, context);
65017
+ finish2(applied);
65018
+ const refusal16 = refusedLabels(applied);
65019
+ if (refusal16 !== null)
65020
+ throw refusal16;
65021
+ }
65022
+ function createUpdateCurrentCommand(seams = DEFAULT_UPDATE_CURRENT_SEAMS) {
65023
+ const command = {
65024
+ name: "update current",
65025
+ subject: "update",
65026
+ action: "current",
65027
+ summary: "bring this project to the installed rafa, within its patch range",
65028
+ description: "Brings the project to the installed rafa when the installed version shares the major and" + ` minor of the version \`${LOCK_FILE}\` records at the project root, and is the same patch or newer.` + " A project with no lock is adopted at the installed version. It creates the missing `.rafa/` folders" + " and the missing board labels on a GitHub board, runs the deprecations step (none registered yet)," + ` and writes \`${LOCK_FILE}\`. It prints every change first; \`--dry-run\` stops there. Otherwise it` + " warns that the changes cannot be rolled back and asks once, `--yes` answering; with no terminal and" + " no `--yes` it refuses with exit code 1. A newer minor or major, or an installed rafa older than the" + " lock, refuses with exit code 1, and so does a board label `gh` would not create, once everything" + " else is applied. A lock or config that cannot be read, or a `.rafa/` path holding something else," + " exits 2 before anything is asked; a folder or the lock that cannot be written exits 2 naming the" + " step and what was already written. In a linked worktree the lock is the worktree's, on its branch." + " With `--output=json` the plan and what was applied are the data of the terminal result event.",
65029
+ args: [],
65030
+ flags: [
65031
+ { name: "dry-run", description: "Print every change and write nothing.", type: "boolean" },
65032
+ { name: "yes", description: "Apply the changes without asking.", type: "boolean" }
65033
+ ],
65034
+ examples: [
65035
+ { cmd: "rafa update current --dry-run", note: "Lists the folders, labels and lock it would write, and writes nothing." },
65036
+ { cmd: "rafa update current --yes", note: "Applies them without asking, as a script or a loop host runs it." }
65037
+ ],
65038
+ outputs: ["text", "json"],
65039
+ run: async (context) => runUpdateCurrent(context, seams)
65040
+ };
65041
+ return Object.freeze(command);
65042
+ }
65043
+ var USAGE47 = "rafa update current [--dry-run] [--yes]", APPLY_QUESTION = "Apply these changes? [y/N] ", YES_ANSWERS7, DEFAULT_UPDATE_CURRENT_SEAMS, current_default2;
65044
+ var init_current2 = __esm(() => {
65045
+ init_github();
65046
+ init_command();
65047
+ init_confirm();
65048
+ init_version();
65049
+ init_config_load();
65050
+ init_config_sections();
65051
+ init_provider();
65052
+ init_lock();
65053
+ init_scaffold();
65054
+ init_update_current();
65055
+ init_update_range();
65056
+ init_worktree_root();
65057
+ init_project_id();
65058
+ init_plan_files();
65059
+ YES_ANSWERS7 = ["y", "yes"];
65060
+ DEFAULT_UPDATE_CURRENT_SEAMS = Object.freeze({
65061
+ installed: RAFA_VERSION,
65062
+ openGh: (root) => createGhRunner({ cwd: root }),
65063
+ readRemote: gitRemoteUrl,
65064
+ isTerminal: () => process.stdin.isTTY === true,
65065
+ openPrompter: () => createLinePrompter(process.stdin, process.stderr),
65066
+ lockRootOf: (root) => checkoutLockRoot(root, process.cwd())
65067
+ });
65068
+ current_default2 = createUpdateCurrentCommand();
65069
+ });
65070
+
65071
+ // src/commands/update/latest.ts
65072
+ var latest_default;
65073
+ var init_latest = __esm(() => {
65074
+ init_stub();
65075
+ latest_default = createUpdateStub({ action: "latest", summary: "move this project to the newest version", issue: 716 });
65076
+ });
65077
+
65078
+ // src/commands/update/next.ts
65079
+ var next_default2;
65080
+ var init_next2 = __esm(() => {
65081
+ init_stub();
65082
+ next_default2 = createUpdateStub({ action: "next", summary: "move this project to the newest minor of its major", issue: 716 });
65083
+ });
65084
+
65085
+ // src/commands/update/port.ts
65086
+ var port_default;
65087
+ var init_port = __esm(() => {
65088
+ init_stub();
65089
+ port_default = createUpdateStub({ action: "port", summary: "move this project to a version, as update project does", issue: 716, hidden: true });
65090
+ });
65091
+
65092
+ // src/commands/update/project.ts
65093
+ var project_default;
65094
+ var init_project = __esm(() => {
65095
+ init_stub();
65096
+ project_default = createUpdateStub({ action: "project", summary: "move this project to a version, with its migrations", issue: 716 });
65097
+ });
65098
+
65099
+ // src/commands/update/rafa.ts
65100
+ var rafa_default;
65101
+ var init_rafa = __esm(() => {
65102
+ init_stub();
65103
+ rafa_default = createUpdateStub({ action: "rafa", summary: "move this project to a version, as update project does", issue: 716, hidden: true });
65104
+ });
65105
+
65106
+ // src/commands/update/self.ts
65107
+ var self_default;
65108
+ var init_self = __esm(() => {
65109
+ init_stub();
65110
+ self_default = createUpdateStub({ action: "self", summary: "update rafa itself, from npm, a pinned version or another source", issue: 715 });
65111
+ });
65112
+
64615
65113
  // src/usage.ts
64616
65114
  async function usage(_args) {
64617
65115
  const pct = await getClaudeUsagePercent();
@@ -64743,6 +65241,14 @@ var init_commands2 = __esm(() => {
64743
65241
  init_show8();
64744
65242
  init_status4();
64745
65243
  init_switch();
65244
+ init_board();
65245
+ init_current2();
65246
+ init_latest();
65247
+ init_next2();
65248
+ init_port();
65249
+ init_project();
65250
+ init_rafa();
65251
+ init_self();
64746
65252
  init_usage2();
64747
65253
  CORE_SUBJECTS = Object.freeze([
64748
65254
  { name: "plan", summary: "create plans from specs; list, show and validate them; read their risk and needs" },
@@ -64757,7 +65263,8 @@ var init_commands2 = __esm(() => {
64757
65263
  { name: "release", summary: "read the release state of the project; settle the waiting fragments into a version; tag the commit that set it" },
64758
65264
  { name: "board", summary: "list the boards with their owner, epic count, and which is current and home" },
64759
65265
  { name: "epic", summary: "show one epic's issues as the Roadmap table; create an epic; defer or promote it; move an issue to it; close it through the gate or cancel it" },
64760
- { name: "claim", summary: "give up this device's claim on an issue; hand it to another store or withdraw the offer; accept a handover; take over a stale claim" }
65266
+ { name: "claim", summary: "give up this device's claim on an issue; hand it to another store or withdraw the offer; accept a handover; take over a stale claim" },
65267
+ { name: "update", summary: "bring this project to the installed rafa; the other updates are in development" }
64761
65268
  ]);
64762
65269
  CORE_COMMANDS = Object.freeze([
64763
65270
  create_default2,
@@ -64829,6 +65336,14 @@ var init_commands2 = __esm(() => {
64829
65336
  hand_default,
64830
65337
  accept_default,
64831
65338
  take_default,
65339
+ current_default2,
65340
+ self_default,
65341
+ project_default,
65342
+ rafa_default,
65343
+ port_default,
65344
+ board_default,
65345
+ next_default2,
65346
+ latest_default,
64832
65347
  status_default3,
64833
65348
  next_default,
64834
65349
  roadmap_default,
@@ -65459,12 +65974,12 @@ init_config_sections();
65459
65974
  init_sessions();
65460
65975
  init_scope();
65461
65976
  init_sections2();
65462
- import { mkdirSync as mkdirSync30, readFileSync as readFileSync79, renameSync as renameSync12, writeFileSync as writeFileSync31 } from "fs";
65463
- import { dirname as dirname39, join as join98 } from "path";
65977
+ import { mkdirSync as mkdirSync30, readFileSync as readFileSync80, renameSync as renameSync12, writeFileSync as writeFileSync32 } from "fs";
65978
+ import { dirname as dirname39, join as join100 } from "path";
65464
65979
  var SEEN_FILE = "status-seen.json";
65465
65980
  var SEEN_VERSION = 1;
65466
65981
  function seenFilePath(root) {
65467
- return join98(scopeAt(root).dir, SEEN_FILE);
65982
+ return join100(scopeAt(root).dir, SEEN_FILE);
65468
65983
  }
65469
65984
  function readSeenSessions(root, isAlive) {
65470
65985
  const sessions = {};
@@ -65519,7 +66034,7 @@ function asSnapshot(value) {
65519
66034
  function readSeenFile(root) {
65520
66035
  let text2;
65521
66036
  try {
65522
- text2 = readFileSync79(seenFilePath(root), "utf8");
66037
+ text2 = readFileSync80(seenFilePath(root), "utf8");
65523
66038
  } catch {
65524
66039
  return null;
65525
66040
  }
@@ -65533,7 +66048,7 @@ function writeSeenFile(root, snapshot) {
65533
66048
  const file = seenFilePath(root);
65534
66049
  mkdirSync30(dirname39(file), { recursive: true });
65535
66050
  const temporary = `${file}.${String(process.pid)}.tmp`;
65536
- writeFileSync31(temporary, `${JSON.stringify(snapshot, null, 2)}
66051
+ writeFileSync32(temporary, `${JSON.stringify(snapshot, null, 2)}
65537
66052
  `);
65538
66053
  renameSync12(temporary, file);
65539
66054
  }
@@ -14,7 +14,7 @@ import {
14
14
  openNdjsonStore,
15
15
  openSqliteStore,
16
16
  selectEffortStore
17
- } from "../../index-bvfbfa63.js";
17
+ } from "../../index-bebv92ha.js";
18
18
  import"../../index-vp0xghk2.js";
19
19
  import"../../index-d6d0942q.js";
20
20
  import"../../index-3mzw3kzh.js";
@@ -259,7 +259,7 @@ import { Database } from "bun:sqlite";
259
259
 
260
260
  // package.json
261
261
  var name = "@open-tomato/rafa";
262
- var version = "0.34.0";
262
+ var version = "0.34.1";
263
263
 
264
264
  // src/cli/version.ts
265
265
  var RAFA_VERSION = version;
@@ -321,6 +321,7 @@ import {
321
321
  sessionLine,
322
322
  sessionLogDir,
323
323
  setUpBoard,
324
+ setUpLabels,
324
325
  severalRoadmapsMessage,
325
326
  shellQuote,
326
327
  show_default,
@@ -356,7 +357,7 @@ import {
356
357
  worktreeAt,
357
358
  writeSession,
358
359
  writeShippedFile
359
- } from "./index-4b3m7s0z.js";
360
+ } from "./index-s8ff86xt.js";
360
361
  import {
361
362
  PROBE_TIMEOUT_MS,
362
363
  loadPlanPrerequisites,
@@ -388,6 +389,7 @@ import {
388
389
  MOVE_TO_SQLITE,
389
390
  MergeRefusal,
390
391
  POSITION_FILE,
392
+ RAFA_VERSION,
391
393
  RebuildRefusal,
392
394
  SCOPE_DIR,
393
395
  SKILL_FILE,
@@ -421,6 +423,7 @@ import {
421
423
  isFile,
422
424
  issueLine,
423
425
  loadConfig,
426
+ mainCheckoutOf,
424
427
  mergeFrontmatter,
425
428
  mergeStore,
426
429
  migrateStore,
@@ -461,7 +464,7 @@ import {
461
464
  versionLine,
462
465
  writeFrontmatter,
463
466
  writePositionFile
464
- } from "./index-bvfbfa63.js";
467
+ } from "./index-bebv92ha.js";
465
468
  import {
466
469
  CONFIG_DEFAULTS,
467
470
  CONFIG_FILE,
@@ -22078,6 +22081,445 @@ function createStatusCommand(seams = DEFAULT_STATUS_SEAMS) {
22078
22081
  }
22079
22082
  var status_default3 = createStatusCommand();
22080
22083
 
22084
+ // src/commands/update/stub.ts
22085
+ var ISSUES_URL = "https://github.com/open-tomato/rafa/issues";
22086
+ function stubMessage(action, issue2) {
22087
+ return `rafa update ${action} is a feature in development, tracked in ${ISSUES_URL}/${String(issue2)}.` + " Nothing was changed.";
22088
+ }
22089
+ function createUpdateStub(spec) {
22090
+ const command = {
22091
+ name: `update ${spec.action}`,
22092
+ subject: "update",
22093
+ action: spec.action,
22094
+ summary: `${spec.summary} (in development, #${String(spec.issue)})`,
22095
+ description: `${spec.summary}. In development: it refuses with exit code 1, naming #${String(spec.issue)},` + " and changes nothing.",
22096
+ args: [],
22097
+ flags: [],
22098
+ examples: [{ cmd: `rafa update ${spec.action}`, note: `Refuses, naming #${String(spec.issue)}; changes nothing.` }],
22099
+ outputs: ["text", "json"],
22100
+ needsProject: false,
22101
+ ...spec.hidden === true ? { hidden: true } : {},
22102
+ run: () => Promise.reject(new CommandExit(1, stubMessage(spec.action, spec.issue)))
22103
+ };
22104
+ return Object.freeze(command);
22105
+ }
22106
+
22107
+ // src/commands/update/board.ts
22108
+ var board_default = createUpdateStub({ action: "board", summary: "apply the board's newest setup and its migrations", issue: 717 });
22109
+
22110
+ // src/project/lock.ts
22111
+ import { existsSync as existsSync15, lstatSync as lstatSync2, readFileSync as readFileSync28, writeFileSync as writeFileSync12 } from "fs";
22112
+ import { join as join40 } from "path";
22113
+ var LOCK_FILE = "rafa.lock";
22114
+ var LOCKFILE_VERSION = 1;
22115
+
22116
+ class ProjectLockError extends Error {
22117
+ name = "ProjectLockError";
22118
+ }
22119
+ function lockPath(root) {
22120
+ return join40(root, LOCK_FILE);
22121
+ }
22122
+ function projectLockText(version2) {
22123
+ const lock = { lockfileVersion: LOCKFILE_VERSION, rafa: version2 };
22124
+ return `${JSON.stringify(lock, null, 2)}
22125
+ `;
22126
+ }
22127
+ function isLink(path) {
22128
+ try {
22129
+ return lstatSync2(path).isSymbolicLink();
22130
+ } catch {
22131
+ return false;
22132
+ }
22133
+ }
22134
+ function refuse8(problem2) {
22135
+ throw new ProjectLockError(`${LOCK_FILE} ${problem2}`);
22136
+ }
22137
+ function lockOf(value) {
22138
+ if (typeof value !== "object" || value === null || Array.isArray(value))
22139
+ refuse8("holds no JSON object");
22140
+ const record = value;
22141
+ const format = record["lockfileVersion"];
22142
+ if (format !== LOCKFILE_VERSION) {
22143
+ refuse8(`is in lockfileVersion ${String(format)}, and this rafa reads ${String(LOCKFILE_VERSION)}: a newer rafa wrote it`);
22144
+ }
22145
+ const rafa = record["rafa"];
22146
+ if (typeof rafa !== "string" || parseSemanticVersion(rafa) === null) {
22147
+ refuse8(`names rafa ${JSON.stringify(rafa)}, which is no version`);
22148
+ }
22149
+ return { lockfileVersion: LOCKFILE_VERSION, rafa };
22150
+ }
22151
+ function readProjectLock(root) {
22152
+ const path = lockPath(root);
22153
+ if (!existsSync15(path)) {
22154
+ if (isLink(path))
22155
+ refuse8("is a link to nothing");
22156
+ return null;
22157
+ }
22158
+ let text;
22159
+ try {
22160
+ text = readFileSync28(path, "utf8");
22161
+ } catch (error) {
22162
+ refuse8(`could not be read: ${messageOf(error)}`);
22163
+ }
22164
+ let value;
22165
+ try {
22166
+ value = JSON.parse(text);
22167
+ } catch {
22168
+ refuse8("is not JSON");
22169
+ }
22170
+ return lockOf(value);
22171
+ }
22172
+ function writeProjectLock(root, version2) {
22173
+ writeFileSync12(lockPath(root), projectLockText(version2), "utf8");
22174
+ }
22175
+
22176
+ // src/project/update-current.ts
22177
+ import { existsSync as existsSync16 } from "fs";
22178
+ import { join as join41 } from "path";
22179
+ var DEPRECATIONS = Object.freeze([]);
22180
+ function missingFolders(root) {
22181
+ return PROJECT_TREE.map((name) => join41(SCOPE_DIR, name)).filter((relative9) => !existsSync16(join41(root, relative9)));
22182
+ }
22183
+ async function readBoard3(access) {
22184
+ if (access.gh === null)
22185
+ return { kind: "skipped", reason: access.reason };
22186
+ try {
22187
+ const held = await listBoardLabels(access.gh);
22188
+ return { kind: "labels", missing: missingBoardLabels(held).map((label) => label.name) };
22189
+ } catch (error) {
22190
+ return { kind: "skipped", reason: messageOf(error) };
22191
+ }
22192
+ }
22193
+ function lockChangeOf(range) {
22194
+ if (range.kind === "adopt")
22195
+ return "created";
22196
+ return range.kind === "patch" ? "updated" : "unchanged";
22197
+ }
22198
+ function fromOf(range) {
22199
+ if (range.kind === "patch")
22200
+ return range.from;
22201
+ if (range.kind === "same")
22202
+ return range.to;
22203
+ return null;
22204
+ }
22205
+ async function readUpdatePlan(roots, range, board) {
22206
+ const { root, lockRoot } = roots;
22207
+ return {
22208
+ root,
22209
+ lockRoot,
22210
+ from: fromOf(range),
22211
+ to: range.to,
22212
+ folders: missingFolders(root),
22213
+ board: await readBoard3(board),
22214
+ deprecations: DEPRECATIONS,
22215
+ lock: lockChangeOf(range)
22216
+ };
22217
+ }
22218
+ function planChanges(plan2) {
22219
+ const labels = plan2.board.kind === "labels" ? plan2.board.missing.length : 0;
22220
+ return plan2.folders.length > 0 || labels > 0 || plan2.deprecations.length > 0 || plan2.lock !== "unchanged";
22221
+ }
22222
+
22223
+ class UpdateStepError extends Error {
22224
+ step;
22225
+ written;
22226
+ name = "UpdateStepError";
22227
+ constructor(step, message, written) {
22228
+ super(message);
22229
+ this.step = step;
22230
+ this.written = written;
22231
+ }
22232
+ }
22233
+ function foldersNowThere(plan2) {
22234
+ return plan2.folders.filter((relative9) => existsSync16(join41(plan2.root, relative9)));
22235
+ }
22236
+ function writeFolders(plan2) {
22237
+ if (plan2.folders.length === 0)
22238
+ return [];
22239
+ try {
22240
+ return writeProjectScope(plan2.root).filter((write) => write.change === "created").map((write) => write.path);
22241
+ } catch (error) {
22242
+ throw new UpdateStepError("folders", messageOf(error), foldersNowThere(plan2));
22243
+ }
22244
+ }
22245
+ function createdLabels(parts) {
22246
+ return parts.filter((part) => part.outcome === "created").map((part) => `label ${part.name}`);
22247
+ }
22248
+ async function applyUpdatePlan(plan2, gh) {
22249
+ const folders = writeFolders(plan2);
22250
+ const labels = gh !== null && plan2.board.kind === "labels" && plan2.board.missing.length > 0 ? await setUpLabels(gh) : [];
22251
+ if (plan2.lock !== "unchanged") {
22252
+ try {
22253
+ writeProjectLock(plan2.lockRoot, plan2.to);
22254
+ } catch (error) {
22255
+ throw new UpdateStepError("lock", messageOf(error), [...folders, ...createdLabels(labels)]);
22256
+ }
22257
+ }
22258
+ return { folders, labels, lock: plan2.lock };
22259
+ }
22260
+
22261
+ // src/project/update-range.ts
22262
+ function refused2(reason, message) {
22263
+ return { kind: "refused", reason, message };
22264
+ }
22265
+ function readCurrentRange(recorded, installed) {
22266
+ const to = parseSemanticVersion(installed);
22267
+ if (to === null)
22268
+ return refused2("unreadable", `the installed rafa answers "${installed}", which is no version`);
22269
+ if (recorded === null)
22270
+ return { kind: "adopt", to: installed };
22271
+ const from = parseSemanticVersion(recorded);
22272
+ if (from === null)
22273
+ return refused2("unreadable", `the project records rafa "${recorded}", which is no version`);
22274
+ const order = compareVersions(to, from);
22275
+ if (order < 0) {
22276
+ return refused2("older", `the installed rafa ${installed} is older than the project's ${recorded}, and no downgrade runs before 1.0.0`);
22277
+ }
22278
+ if (to.major !== from.major) {
22279
+ return refused2("newer-major", `the installed rafa ${installed} is a newer major than the project's ${recorded}; that move is rafa update latest`);
22280
+ }
22281
+ if (to.minor !== from.minor) {
22282
+ return refused2("newer-minor", `the installed rafa ${installed} is a newer minor than the project's ${recorded}; that move is rafa update next`);
22283
+ }
22284
+ return order === 0 ? { kind: "same", to: installed } : { kind: "patch", from: recorded, to: installed };
22285
+ }
22286
+
22287
+ // src/commands/update/current.ts
22288
+ var USAGE42 = "rafa update current [--dry-run] [--yes]";
22289
+ var APPLY_QUESTION = "Apply these changes? [y/N] ";
22290
+ var YES_ANSWERS5 = ["y", "yes"];
22291
+ function checkoutLockRoot(root, dir) {
22292
+ const run = Bun.spawnSync(["git", "rev-parse", "--show-toplevel"], { cwd: dir, stdout: "pipe", stderr: "pipe" });
22293
+ if (run.exitCode !== 0)
22294
+ return root;
22295
+ const top = run.stdout.toString().trim();
22296
+ return top !== "" && mainCheckoutOf(top) === root ? top : root;
22297
+ }
22298
+ var DEFAULT_UPDATE_CURRENT_SEAMS = Object.freeze({
22299
+ installed: RAFA_VERSION,
22300
+ openGh: (root) => createGhRunner({ cwd: root }),
22301
+ readRemote: gitRemoteUrl,
22302
+ isTerminal: () => process.stdin.isTTY === true,
22303
+ openPrompter: () => createLinePrompter(process.stdin, process.stderr),
22304
+ lockRootOf: (root) => checkoutLockRoot(root, process.cwd())
22305
+ });
22306
+ function readSwitch4(context, name) {
22307
+ const value = context.flags[name];
22308
+ if (value === undefined || value === false || value === "false")
22309
+ return false;
22310
+ if (value === true || value === "true")
22311
+ return true;
22312
+ throw new CommandExit(1, `\u274C --${name} takes no value, and read "${String(value)}" as one
22313
+ Usage: ${USAGE42}`);
22314
+ }
22315
+ function couldNotRun(problem2) {
22316
+ return new CommandExit(2, `FAIL \u2014 ${problem2}
22317
+ nothing was changed.`);
22318
+ }
22319
+ function projectOf15(context) {
22320
+ if (context.project === null)
22321
+ throw new Error("rafa update current runs inside a project, and was handed none");
22322
+ return context.project;
22323
+ }
22324
+ function boardAccess(project, seams) {
22325
+ let configured;
22326
+ try {
22327
+ configured = loadConfig({ root: project.root, home: project.home }, {}, () => {}).config.prProvider;
22328
+ } catch (error) {
22329
+ throw couldNotRun(`config: ${messageOf(error)}`);
22330
+ }
22331
+ const provider = resolvePrProvider({ configured, dir: project.root, readRemote: seams.readRemote }).provider;
22332
+ return provider === "gh" ? { gh: seams.openGh(project.root), reason: "" } : { gh: null, reason: `pr.provider is ${provider}, so there is no GitHub board` };
22333
+ }
22334
+ function foldersText(plan2) {
22335
+ if (plan2.folders.length === 0)
22336
+ return "nothing missing";
22337
+ return `create ${plan2.folders.join(", ")}`;
22338
+ }
22339
+ function boardText2(plan2) {
22340
+ if (plan2.board.kind === "skipped")
22341
+ return `skipped: ${plan2.board.reason}`;
22342
+ if (plan2.board.missing.length === 0)
22343
+ return "every label present";
22344
+ return `create labels ${plan2.board.missing.join(", ")}`;
22345
+ }
22346
+ function deprecationsText(plan2) {
22347
+ if (plan2.deprecations.length === 0)
22348
+ return "none registered yet (#718)";
22349
+ return `${String(plan2.deprecations.length)} to run`;
22350
+ }
22351
+ function lockText(plan2) {
22352
+ const where = plan2.lockRoot === plan2.root ? "" : ` in ${plan2.lockRoot}`;
22353
+ if (plan2.lock === "unchanged")
22354
+ return `unchanged at ${plan2.to}${where}`;
22355
+ if (plan2.lock === "created")
22356
+ return `create at ${plan2.to}${where}`;
22357
+ return `update ${String(plan2.from)} \u2192 ${plan2.to}${where}`;
22358
+ }
22359
+ function planLines2(plan2) {
22360
+ const from = plan2.from ?? `no ${LOCK_FILE}`;
22361
+ return [
22362
+ `rafa update current: ${plan2.root}, from ${from} to ${plan2.to}`,
22363
+ ` folders ${foldersText(plan2)}`,
22364
+ ` board ${boardText2(plan2)}`,
22365
+ ` deprecations ${deprecationsText(plan2)}`,
22366
+ ` ${LOCK_FILE} ${lockText(plan2)}`
22367
+ ];
22368
+ }
22369
+ function appliedLines(applied, plan2, context) {
22370
+ for (const path of applied.folders)
22371
+ context.output.info(`created ${path}`);
22372
+ for (const part of applied.labels) {
22373
+ if (part.outcome === "created")
22374
+ context.output.info(`created label ${part.name}`);
22375
+ if (part.outcome === "refused")
22376
+ context.output.warn(`label ${part.name} was not created: ${part.detail}`);
22377
+ }
22378
+ if (applied.lock !== "unchanged")
22379
+ context.output.info(`wrote ${LOCK_FILE} at ${plan2.to}`);
22380
+ }
22381
+ async function confirmed2(yes, seams) {
22382
+ if (yes)
22383
+ return true;
22384
+ if (!seams.isTerminal()) {
22385
+ throw new CommandExit(1, "REFUSED \u2014 rafa update current asks before it changes anything, and there is no" + " terminal to ask on. Run it with --dry-run to see the changes, then with --yes to apply them." + `
22386
+ nothing was changed.`);
22387
+ }
22388
+ const prompter = seams.openPrompter();
22389
+ try {
22390
+ const answer2 = await prompter.ask(APPLY_QUESTION);
22391
+ return answer2 !== null && YES_ANSWERS5.includes(answer2.trim().toLowerCase());
22392
+ } finally {
22393
+ prompter.close();
22394
+ }
22395
+ }
22396
+ function readRange(root, installed) {
22397
+ let recorded;
22398
+ try {
22399
+ recorded = readProjectLock(root)?.rafa ?? null;
22400
+ } catch (error) {
22401
+ throw couldNotRun(messageOf(error));
22402
+ }
22403
+ const range = readCurrentRange(recorded, installed);
22404
+ if (range.kind === "refused")
22405
+ throw new CommandExit(1, `REFUSED \u2014 ${range.message}
22406
+ nothing was changed.`);
22407
+ return range;
22408
+ }
22409
+ function refuseConflicts(project) {
22410
+ const conflicts2 = scaffoldConflicts(project.root, project.home);
22411
+ if (conflicts2.length === 0)
22412
+ return;
22413
+ throw couldNotRun(`the project scope cannot be written:
22414
+ ${conflicts2.map((conflict) => ` ${conflict}`).join(`
22415
+ `)}`);
22416
+ }
22417
+ function stoppedPartWay(error) {
22418
+ const kept = error.written.length === 0 ? "Nothing was written before it." : `Already written, and kept: ${error.written.join(", ")}.`;
22419
+ return new CommandExit(2, `FAIL \u2014 ${error.step}: ${error.message}
22420
+ stopped part-way. ${kept}` + `
22421
+ Fix the cause and run rafa update current again: each step writes only what is missing.`);
22422
+ }
22423
+ async function applyAndReport(plan2, gh, context) {
22424
+ let applied;
22425
+ try {
22426
+ applied = await applyUpdatePlan(plan2, gh);
22427
+ } catch (error) {
22428
+ if (error instanceof UpdateStepError)
22429
+ throw stoppedPartWay(error);
22430
+ throw error;
22431
+ }
22432
+ appliedLines(applied, plan2, context);
22433
+ return applied;
22434
+ }
22435
+ function refusedLabels(applied) {
22436
+ const refused3 = applied.labels.filter((part) => part.outcome === "refused");
22437
+ if (refused3.length === 0)
22438
+ return null;
22439
+ return new CommandExit(1, `${String(refused3.length)} board label(s) were not created; everything else was applied.` + " Run rafa update current again once gh can create them.");
22440
+ }
22441
+ function stopLine2(plan2, dryRun) {
22442
+ if (!planChanges(plan2))
22443
+ return "Nothing to change.";
22444
+ if (dryRun)
22445
+ return "Dry run: nothing was written. Run rafa update current to apply it.";
22446
+ return null;
22447
+ }
22448
+ async function runUpdateCurrent(context, seams) {
22449
+ expectNoArgument(context.args, USAGE42);
22450
+ const dryRun = readSwitch4(context, "dry-run");
22451
+ const yes = readSwitch4(context, "yes");
22452
+ const project = projectOf15(context);
22453
+ const lockRoot = seams.lockRootOf(project.root);
22454
+ const range = readRange(lockRoot, seams.installed);
22455
+ refuseConflicts(project);
22456
+ const access = boardAccess(project, seams);
22457
+ const plan2 = await readUpdatePlan({ root: project.root, lockRoot }, range, access);
22458
+ for (const line of planLines2(plan2))
22459
+ context.output.info(line);
22460
+ const finish = (applied2) => {
22461
+ if (context.outputMode === "json")
22462
+ context.output.result({ plan: plan2, applied: applied2 });
22463
+ };
22464
+ const stop = stopLine2(plan2, dryRun);
22465
+ if (stop !== null) {
22466
+ context.output.info(stop);
22467
+ finish(null);
22468
+ return;
22469
+ }
22470
+ context.output.warn("rafa cannot roll these changes back. Run it with --dry-run first to only see them.");
22471
+ if (!await confirmed2(yes, seams)) {
22472
+ context.output.info("Nothing was changed.");
22473
+ finish(null);
22474
+ return;
22475
+ }
22476
+ const applied = await applyAndReport(plan2, access.gh, context);
22477
+ finish(applied);
22478
+ const refusal13 = refusedLabels(applied);
22479
+ if (refusal13 !== null)
22480
+ throw refusal13;
22481
+ }
22482
+ function createUpdateCurrentCommand(seams = DEFAULT_UPDATE_CURRENT_SEAMS) {
22483
+ const command = {
22484
+ name: "update current",
22485
+ subject: "update",
22486
+ action: "current",
22487
+ summary: "bring this project to the installed rafa, within its patch range",
22488
+ description: "Brings the project to the installed rafa when the installed version shares the major and" + ` minor of the version \`${LOCK_FILE}\` records at the project root, and is the same patch or newer.` + " A project with no lock is adopted at the installed version. It creates the missing `.rafa/` folders" + " and the missing board labels on a GitHub board, runs the deprecations step (none registered yet)," + ` and writes \`${LOCK_FILE}\`. It prints every change first; \`--dry-run\` stops there. Otherwise it` + " warns that the changes cannot be rolled back and asks once, `--yes` answering; with no terminal and" + " no `--yes` it refuses with exit code 1. A newer minor or major, or an installed rafa older than the" + " lock, refuses with exit code 1, and so does a board label `gh` would not create, once everything" + " else is applied. A lock or config that cannot be read, or a `.rafa/` path holding something else," + " exits 2 before anything is asked; a folder or the lock that cannot be written exits 2 naming the" + " step and what was already written. In a linked worktree the lock is the worktree's, on its branch." + " With `--output=json` the plan and what was applied are the data of the terminal result event.",
22489
+ args: [],
22490
+ flags: [
22491
+ { name: "dry-run", description: "Print every change and write nothing.", type: "boolean" },
22492
+ { name: "yes", description: "Apply the changes without asking.", type: "boolean" }
22493
+ ],
22494
+ examples: [
22495
+ { cmd: "rafa update current --dry-run", note: "Lists the folders, labels and lock it would write, and writes nothing." },
22496
+ { cmd: "rafa update current --yes", note: "Applies them without asking, as a script or a loop host runs it." }
22497
+ ],
22498
+ outputs: ["text", "json"],
22499
+ run: async (context) => runUpdateCurrent(context, seams)
22500
+ };
22501
+ return Object.freeze(command);
22502
+ }
22503
+ var current_default2 = createUpdateCurrentCommand();
22504
+
22505
+ // src/commands/update/latest.ts
22506
+ var latest_default = createUpdateStub({ action: "latest", summary: "move this project to the newest version", issue: 716 });
22507
+
22508
+ // src/commands/update/next.ts
22509
+ var next_default2 = createUpdateStub({ action: "next", summary: "move this project to the newest minor of its major", issue: 716 });
22510
+
22511
+ // src/commands/update/port.ts
22512
+ var port_default = createUpdateStub({ action: "port", summary: "move this project to a version, as update project does", issue: 716, hidden: true });
22513
+
22514
+ // src/commands/update/project.ts
22515
+ var project_default = createUpdateStub({ action: "project", summary: "move this project to a version, with its migrations", issue: 716 });
22516
+
22517
+ // src/commands/update/rafa.ts
22518
+ var rafa_default = createUpdateStub({ action: "rafa", summary: "move this project to a version, as update project does", issue: 716, hidden: true });
22519
+
22520
+ // src/commands/update/self.ts
22521
+ var self_default = createUpdateStub({ action: "self", summary: "update rafa itself, from npm, a pinned version or another source", issue: 715 });
22522
+
22081
22523
  // src/commands/usage.ts
22082
22524
  var usage_default = wrapPhaseZeroCommand({
22083
22525
  name: "usage",
@@ -22110,7 +22552,8 @@ var CORE_SUBJECTS = Object.freeze([
22110
22552
  { name: "release", summary: "read the release state of the project; settle the waiting fragments into a version; tag the commit that set it" },
22111
22553
  { name: "board", summary: "list the boards with their owner, epic count, and which is current and home" },
22112
22554
  { name: "epic", summary: "show one epic's issues as the Roadmap table; create an epic; defer or promote it; move an issue to it; close it through the gate or cancel it" },
22113
- { name: "claim", summary: "give up this device's claim on an issue; hand it to another store or withdraw the offer; accept a handover; take over a stale claim" }
22555
+ { name: "claim", summary: "give up this device's claim on an issue; hand it to another store or withdraw the offer; accept a handover; take over a stale claim" },
22556
+ { name: "update", summary: "bring this project to the installed rafa; the other updates are in development" }
22114
22557
  ]);
22115
22558
  var CORE_COMMANDS = Object.freeze([
22116
22559
  create_default2,
@@ -22182,6 +22625,14 @@ var CORE_COMMANDS = Object.freeze([
22182
22625
  hand_default,
22183
22626
  accept_default,
22184
22627
  take_default,
22628
+ current_default2,
22629
+ self_default,
22630
+ project_default,
22631
+ rafa_default,
22632
+ port_default,
22633
+ board_default,
22634
+ next_default2,
22635
+ latest_default,
22185
22636
  status_default3,
22186
22637
  next_default,
22187
22638
  roadmap_default,
@@ -127,7 +127,7 @@ import {
127
127
  writeNames,
128
128
  writePositionFile,
129
129
  writeSqliteStore
130
- } from "./index-bvfbfa63.js";
130
+ } from "./index-bebv92ha.js";
131
131
  import {
132
132
  CHANGE_LEVELS,
133
133
  CONFIG_DEFAULTS,
@@ -14334,7 +14334,7 @@ function withOutlineCache(outline, root) {
14334
14334
  import path2 from "path";
14335
14335
  async function coreRoster() {
14336
14336
  const [{ CORE_REGISTRY }, { describeRegistry }] = await Promise.all([
14337
- import("./index-b8t7q5hc.js"),
14337
+ import("./index-nr4hdb35.js"),
14338
14338
  import("./describe-5hqfwbjc.js")
14339
14339
  ]);
14340
14340
  return describeRegistry(CORE_REGISTRY, version);
@@ -31282,4 +31282,4 @@ async function plan(args, repoRoot, registry = CORE_ADAPTER_REGISTRY) {
31282
31282
  }
31283
31283
  }
31284
31284
 
31285
- export { SERVE_CLI_VERSION, PROVENANCE_FIELD, SKILL_FILE2 as SKILL_FILE, provenanceBlock, serveVerdict, SKILL_USE_CLI_VERSION, MANIFEST_VERSION, FEATURE_TYPES, OUTPUT_CHANNELS, RUNNING_MANIFEST_SEAMS, validateManifest, loadModules, moduleSettings, pullBeforeRead, sessionLogDir, collectSessionRow, collect, AGENT_DEFINITION_DIR, VENDOR_COMMAND, readAgentDirectory, resolveAgentRoster, missingPlanAgents, isStackList, stackPaths, stacksFromEccSkillName, DESCRIPTION_LIMIT, LISTING_LIMIT, SKILL_SIGNALS, countCharacters, toolOf, pathDirectories, checkReferences, checkFile, checkDirectory, bundledAgentsDirectory, sourceItem, byName, skillItem, readTrees, parseReportArgs, formatCount, alignRows, verdictOf, failingRows, formatRows, waitForChecks, recordPlanCi, formatTokens, formatTrendSegment, formatLoopsSegment, readTrendInputs, trendReportOf, buildReport, readSkillsReport, report, SPEC_EXTENSION, boardId, notesFileName, shellQuote, ghMissingMessage, ghUnauthenticatedMessage, ghHostOf, ghPreflightItems, parseWorkingTree, parseWorktrees, readMergeRefusal, resolvePrProvider, requireGhProvider, createGhPullRequests, ghPullRequestsIn, REMOTE, BRANCH_PREFIX2 as BRANCH_PREFIX, parseBaseStanding, hasDiverged, parseClaimMessage, claimBranchIssue, makeOwnershipCommit, fetchClaimBranches, readClaimBranch, pushOwnershipCommit, readClaimState, idleText, ROADMAP_TITLE, ROADMAP_SETTING, parseRoadmapBody, createGhRoadmapSearch, severalRoadmapsMessage, createGhOpenPullRequests, closedIssuesIn, branchClaims, scanClaimBranches, readEpicBody, SPEC_BLOCKED_LABEL, hasSpecBlockedLabel, readBlockedBy, EPIC_LABEL_PREFIX, epicSlugsOf, isNotPlanned, groupByEpicLabel, localDay, readEpics, BOARD_LISTING_LIMIT, createGhBoardListing, validate_default, RefsBlockError, fingerprintText, PREVIOUS_COPY_WARN_ABOVE, previousDir, countPreviousCopies, createGhSpecIssueReader, readRoadmapSetting, ROADMAP_LABEL, BOARD_LABELS, LABEL_LIST_LIMIT, SPEC_TEMPLATE_PATH, TEMPLATES_DIRNAME, boardChanged, listBoardLabels, missingBoardLabels, anythingAt, writeShippedFile, setUpBoard, resolveDefaultBoard, unlabelledRoadmapMessage, createGhRoadmapBody, tickSentence, appendLine, tickLine, editChecklist, noBoardListsLine, epicTickSentence, tickRoadmapAfterMerge, createGhIssueBoard, createLinePrompter, readBlockedIssues, renderBlockedIssues, resolveTracker, DEFAULT_ISSUE_SEAMS, lineRefusal, expectTwoArguments, readTextFlag, readNonBlankFlag, readRequiredFlag, readChoice, readChoiceFlag, issueProject, issueSubjectConfig, resolveIssueTracker, issueRef, issueName, urlLines, onTracker, unblock_default, unblockAfterMerge, HORIZON_LABEL_PREFIX, readEpicProblems, epicProblemMessage, isWaiting, blockerToken, addBlockerToBody, labelsLineBlockersOf, LABELS_READS, createLabelsRelations, EPIC_TYPE_LABEL, STAGE_LABELS, unlabelReleased, readRefsText, readCopyRefs, restampCopyRefs, RefVerifyError, blockerArg, createNativeRelations, selectBoardRelations, NATIVE_MODE, NATIVE_RETRY_HINT, nativeEpicLeft, nativeAlreadyMessage, nativeParentLine, readBoardRepository, configuredMoveRelations, readConfiguredRelations, blockedLineSentence, readDoctorRefs, renderDoctorRefs, readEpicDependents, REASON_FLAG, renderHorizonComment, renderParkedPullRequestComment, renderMoveComment, renderCloseComment, renderCancelComment, cancelMoveReason, renderDependentComment, renderUnblockNote, readReason, unaskedReasonMessage, blankReasonMessage, HORIZONS, horizonOf, readListedEpics, invalidateRows, runIssueList, ISSUE_LIST_FLAGS, list_default, firstNowEpic, show_default, samePlace, resolvePlace, createPlanDirNames, hasPlanFor, memoiseVerifier, LEARNED_DIRECTORY, ORIGIN_FIELD, DEMOTION_VERDICTS, WHEN_TO_USE_HEADING, bodySections, classifySkill, firstSentence, convertToInstinct, rankCandidates, readRankCandidate, NO_NEXT_STEP, readSchemaReport, gateMeaning, unknownAdditive, unknownAdditiveWarning, developmentApplied, isFirstDispatch, createStartPreflightClaim, checkDrift, driftLines, readDoctorEffortSync, renderDoctorEffortSync, effortSyncRefusal, SESSION_ID_FLAG2 as SESSION_ID_FLAG, readBoardBody, openBoards, boardOfEpic, createOwnerResolver, resolveLoopSeams, sessionIdFlag, refusal3 as refusal, isLive, planLabel, sessionLine, projectRoot2 as projectRoot, readRecords, pickSession, writeSession, readSessionChecklist, phaseNote, phasedCounts, checkboxAt, estimateEta, readSessionFinishes, formatDuration, etaLine, RELEASE_REMOTE, RELEASE_BASE_BRANCH, parseSemanticVersion, readManifestVersion, replaceManifestVersion, gitPathOf, receiptProblem, readReceiptVerdict, FRAGMENT_PLAN_ID_PATTERN, serializeFragment, allocateFragmentName, directoryPrefix, releaseCommitSubject, readSettle, buildSettle, waitingSettingsOf, readWaiting, forecastPhrase, DEFAULT_RELEASE_SEAMS, compareVersions, changelogVersions, readTags, status_default, epicEndLines, watchDryEpic, nextOwnerGate, resolveReleaseEnabled, refuseMisplacedRuntime, readBinPath, self_update_default, versionTag, INDENT4 as INDENT, cleanUpAfterMerge, reportFollowUps, RELEASE_BLOCK_OPEN, RELEASE_BLOCK_CLOSE, localPlanNotes, readGuard, guardLines, mergeGuardSettings, readMergeGuard, guardReaction, followsMerge, readSettleAfterMerge, DEFAULT_BASE_BRANCH, ghNextBoard, openNextSources, WORKTREES_SUBDIR, resolveWorktreePath, crossRepositoryRefusal, addResolveWorktree, removeResolveWorktree, pushResolved, worktreeAt, cleanupSteps, dryRunLines, runCleanupSteps, defaultCleanupSeams, readCleanup, cleanupCounts, openNextRelations, list_default2 as list_default1, readBranch2 as readBranch, readPlans, readOpenPull, readBranchPlan, readNextState, readHomeAfterLoop, commandWords, nextQuestion, NEXT_COMMAND_ACTIONS, LABELS_ONLY_ACTIONS, actionInvocation, runAction, HINT_FLAG_SPEC, actionOutput, endWithNextStep, endingWith, lazyPrompter2 as lazyPrompter, ready_default, DEFAULT_PLAN_FILE, resolvePlanPath, assessPlanRisk, renderRiskText, announceRiskTotal, resolveCreateSpec, TRIAGE_MODULE, namedSecrets, triageReport, unresolvedTracker, DISK_ROOTS_FILE_SYSTEM, gitToplevel, rootRefusal, rootCandidates, DEFAULT_CI_TIMEOUT_MIN, CI_POLL_INTERVAL_MS, DEFAULT_CI_ATTEMPTS, MECHANICAL_CONFLICT_SENTENCE, start, usage, createPlanRefsVerifier, plan };
31285
+ export { SERVE_CLI_VERSION, PROVENANCE_FIELD, SKILL_FILE2 as SKILL_FILE, provenanceBlock, serveVerdict, SKILL_USE_CLI_VERSION, MANIFEST_VERSION, FEATURE_TYPES, OUTPUT_CHANNELS, RUNNING_MANIFEST_SEAMS, validateManifest, loadModules, moduleSettings, pullBeforeRead, sessionLogDir, collectSessionRow, collect, AGENT_DEFINITION_DIR, VENDOR_COMMAND, readAgentDirectory, resolveAgentRoster, missingPlanAgents, isStackList, stackPaths, stacksFromEccSkillName, DESCRIPTION_LIMIT, LISTING_LIMIT, SKILL_SIGNALS, countCharacters, toolOf, pathDirectories, checkReferences, checkFile, checkDirectory, bundledAgentsDirectory, sourceItem, byName, skillItem, readTrees, parseReportArgs, formatCount, alignRows, verdictOf, failingRows, formatRows, waitForChecks, recordPlanCi, formatTokens, formatTrendSegment, formatLoopsSegment, readTrendInputs, trendReportOf, buildReport, readSkillsReport, report, SPEC_EXTENSION, boardId, notesFileName, shellQuote, ghMissingMessage, ghUnauthenticatedMessage, ghHostOf, ghPreflightItems, parseWorkingTree, parseWorktrees, readMergeRefusal, resolvePrProvider, requireGhProvider, createGhPullRequests, ghPullRequestsIn, REMOTE, BRANCH_PREFIX2 as BRANCH_PREFIX, parseBaseStanding, hasDiverged, parseClaimMessage, claimBranchIssue, makeOwnershipCommit, fetchClaimBranches, readClaimBranch, pushOwnershipCommit, readClaimState, idleText, ROADMAP_TITLE, ROADMAP_SETTING, parseRoadmapBody, createGhRoadmapSearch, severalRoadmapsMessage, createGhOpenPullRequests, closedIssuesIn, branchClaims, scanClaimBranches, readEpicBody, SPEC_BLOCKED_LABEL, hasSpecBlockedLabel, readBlockedBy, EPIC_LABEL_PREFIX, epicSlugsOf, isNotPlanned, groupByEpicLabel, localDay, readEpics, BOARD_LISTING_LIMIT, createGhBoardListing, validate_default, RefsBlockError, fingerprintText, PREVIOUS_COPY_WARN_ABOVE, previousDir, countPreviousCopies, createGhSpecIssueReader, readRoadmapSetting, ROADMAP_LABEL, BOARD_LABELS, LABEL_LIST_LIMIT, SPEC_TEMPLATE_PATH, TEMPLATES_DIRNAME, boardChanged, listBoardLabels, missingBoardLabels, setUpLabels, anythingAt, writeShippedFile, setUpBoard, resolveDefaultBoard, unlabelledRoadmapMessage, createGhRoadmapBody, tickSentence, appendLine, tickLine, editChecklist, noBoardListsLine, epicTickSentence, tickRoadmapAfterMerge, createGhIssueBoard, createLinePrompter, readBlockedIssues, renderBlockedIssues, resolveTracker, DEFAULT_ISSUE_SEAMS, lineRefusal, expectTwoArguments, readTextFlag, readNonBlankFlag, readRequiredFlag, readChoice, readChoiceFlag, issueProject, issueSubjectConfig, resolveIssueTracker, issueRef, issueName, urlLines, onTracker, unblock_default, unblockAfterMerge, HORIZON_LABEL_PREFIX, readEpicProblems, epicProblemMessage, isWaiting, blockerToken, addBlockerToBody, labelsLineBlockersOf, LABELS_READS, createLabelsRelations, EPIC_TYPE_LABEL, STAGE_LABELS, unlabelReleased, readRefsText, readCopyRefs, restampCopyRefs, RefVerifyError, blockerArg, createNativeRelations, selectBoardRelations, NATIVE_MODE, NATIVE_RETRY_HINT, nativeEpicLeft, nativeAlreadyMessage, nativeParentLine, readBoardRepository, configuredMoveRelations, readConfiguredRelations, blockedLineSentence, readDoctorRefs, renderDoctorRefs, readEpicDependents, REASON_FLAG, renderHorizonComment, renderParkedPullRequestComment, renderMoveComment, renderCloseComment, renderCancelComment, cancelMoveReason, renderDependentComment, renderUnblockNote, readReason, unaskedReasonMessage, blankReasonMessage, HORIZONS, horizonOf, readListedEpics, invalidateRows, runIssueList, ISSUE_LIST_FLAGS, list_default, firstNowEpic, show_default, samePlace, resolvePlace, createPlanDirNames, hasPlanFor, memoiseVerifier, LEARNED_DIRECTORY, ORIGIN_FIELD, DEMOTION_VERDICTS, WHEN_TO_USE_HEADING, bodySections, classifySkill, firstSentence, convertToInstinct, rankCandidates, readRankCandidate, NO_NEXT_STEP, readSchemaReport, gateMeaning, unknownAdditive, unknownAdditiveWarning, developmentApplied, isFirstDispatch, createStartPreflightClaim, checkDrift, driftLines, readDoctorEffortSync, renderDoctorEffortSync, effortSyncRefusal, SESSION_ID_FLAG2 as SESSION_ID_FLAG, readBoardBody, openBoards, boardOfEpic, createOwnerResolver, resolveLoopSeams, sessionIdFlag, refusal3 as refusal, isLive, planLabel, sessionLine, projectRoot2 as projectRoot, readRecords, pickSession, writeSession, readSessionChecklist, phaseNote, phasedCounts, checkboxAt, estimateEta, readSessionFinishes, formatDuration, etaLine, RELEASE_REMOTE, RELEASE_BASE_BRANCH, parseSemanticVersion, readManifestVersion, replaceManifestVersion, gitPathOf, receiptProblem, readReceiptVerdict, FRAGMENT_PLAN_ID_PATTERN, serializeFragment, allocateFragmentName, directoryPrefix, releaseCommitSubject, readSettle, buildSettle, waitingSettingsOf, readWaiting, forecastPhrase, DEFAULT_RELEASE_SEAMS, compareVersions, changelogVersions, readTags, status_default, epicEndLines, watchDryEpic, nextOwnerGate, resolveReleaseEnabled, refuseMisplacedRuntime, readBinPath, self_update_default, versionTag, INDENT4 as INDENT, cleanUpAfterMerge, reportFollowUps, RELEASE_BLOCK_OPEN, RELEASE_BLOCK_CLOSE, localPlanNotes, readGuard, guardLines, mergeGuardSettings, readMergeGuard, guardReaction, followsMerge, readSettleAfterMerge, DEFAULT_BASE_BRANCH, ghNextBoard, openNextSources, WORKTREES_SUBDIR, resolveWorktreePath, crossRepositoryRefusal, addResolveWorktree, removeResolveWorktree, pushResolved, worktreeAt, cleanupSteps, dryRunLines, runCleanupSteps, defaultCleanupSeams, readCleanup, cleanupCounts, openNextRelations, list_default2 as list_default1, readBranch2 as readBranch, readPlans, readOpenPull, readBranchPlan, readNextState, readHomeAfterLoop, commandWords, nextQuestion, NEXT_COMMAND_ACTIONS, LABELS_ONLY_ACTIONS, actionInvocation, runAction, HINT_FLAG_SPEC, actionOutput, endWithNextStep, endingWith, lazyPrompter2 as lazyPrompter, ready_default, DEFAULT_PLAN_FILE, resolvePlanPath, assessPlanRisk, renderRiskText, announceRiskTotal, resolveCreateSpec, TRIAGE_MODULE, namedSecrets, triageReport, unresolvedTracker, DISK_ROOTS_FILE_SYSTEM, gitToplevel, rootRefusal, rootCandidates, DEFAULT_CI_TIMEOUT_MIN, CI_POLL_INTERVAL_MS, DEFAULT_CI_ATTEMPTS, MECHANICAL_CONFLICT_SENTENCE, start, usage, createPlanRefsVerifier, plan };
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  start,
11
11
  usage,
12
12
  validateManifest
13
- } from "./index-4b3m7s0z.js";
13
+ } from "./index-s8ff86xt.js";
14
14
  import {
15
15
  PLAN_BLOCK_KINDS,
16
16
  PLAN_HEADER_FIELDS,
@@ -53,7 +53,7 @@ import {
53
53
  scopeAt,
54
54
  selectEffortStore,
55
55
  selfAndAncestors
56
- } from "./index-bvfbfa63.js";
56
+ } from "./index-bebv92ha.js";
57
57
  import {
58
58
  CONFIG_DEFAULTS,
59
59
  CONFIG_FILE,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-tomato/rafa",
3
- "version": "0.34.0",
3
+ "version": "0.34.1",
4
4
  "description": "Alpha. Runs a plan through the ralph loop: one Claude Code session per task, one commit per task, then a pull request and a CI wait.",
5
5
  "private": false,
6
6
  "publishConfig": {
@@ -57,7 +57,9 @@
57
57
  "lint": "eslint .",
58
58
  "check-types": "tsc --noEmit && bun run --filter './packages/*' check-types",
59
59
  "gate:control-bytes": "bun scripts/control-byte-gate/control-byte-gate.ts",
60
- "snapshot": "bun scripts/snapshot-runtime.ts"
60
+ "snapshot": "bun scripts/snapshot-runtime.ts",
61
+ "stretch": "bash scripts/stretch/stretch.sh",
62
+ "device:check": "bash scripts/device/check.sh"
61
63
  },
62
64
  "devDependencies": {
63
65
  "@eslint/js": "^9.39.4",