@open-tomato/rafa 0.32.0 → 0.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/README.md +134 -4
  2. package/dist/bundled/agents/build-error-resolver.md +22 -14
  3. package/dist/bundled/agents/code-reviewer.md +22 -14
  4. package/dist/bundled/agents/loop-implementer.md +22 -8
  5. package/dist/bundled/agents/tdd-guide.md +23 -15
  6. package/dist/bundled/operators/agents/rafa-stretch-engineer.md +177 -0
  7. package/dist/bundled/operators/agents/rafa-stretch-watchtower.md +91 -0
  8. package/dist/bundled/operators/skills/rafa-stretch-gap-log/SKILL.md +72 -0
  9. package/dist/bundled/operators/skills/rafa-stretch-pit-stop/SKILL.md +77 -0
  10. package/dist/bundled/operators/skills/rafa-stretch-sweep/SKILL.md +111 -0
  11. package/dist/bundled/skills/dev-planner/SKILL.md +22 -4
  12. package/dist/bundled/skills/git-workflow/SKILL.md +1 -1
  13. package/dist/cli.js +4495 -1789
  14. package/dist/{describe-d31jy6dk.js → describe-5hqfwbjc.js} +3 -3
  15. package/dist/effort/store/index.js +24 -4
  16. package/dist/{index-mt6hpsry.js → index-3mzw3kzh.js} +2 -2
  17. package/dist/{index-qgayjcym.js → index-4b3m7s0z.js} +7148 -4603
  18. package/dist/{index-22stnzde.js → index-b8t7q5hc.js} +666 -1137
  19. package/dist/{index-wvm2s4pr.js → index-bvfbfa63.js} +699 -132
  20. package/dist/{index-rs79dq9e.js → index-fzhd3d4y.js} +22 -4
  21. package/dist/{index-5tftj69y.js → index-k6sd4ym5.js} +2 -2
  22. package/dist/{index-pbeyv281.js → index-v8d7c6sw.js} +2 -2
  23. package/dist/{index-9aph6ft9.js → index-vp0xghk2.js} +167 -5
  24. package/dist/index.js +27 -7
  25. package/dist/plan/index.js +3 -3
  26. package/dist/plan-prompt.md +12 -0
  27. package/package.json +5 -2
@@ -10,11 +10,12 @@ import {
10
10
  activeOutputMode,
11
11
  configFilePath,
12
12
  createTextOutput,
13
+ hubContextOf,
13
14
  isStampableStub,
14
15
  parseConfigText,
15
16
  readRafaBlocks,
16
17
  resolveConfig
17
- } from "./index-9aph6ft9.js";
18
+ } from "./index-vp0xghk2.js";
18
19
  import {
19
20
  bless,
20
21
  merge
@@ -22,21 +23,23 @@ import {
22
23
  import {
23
24
  CommandExit,
24
25
  commandSpelling
25
- } from "./index-mt6hpsry.js";
26
+ } from "./index-3mzw3kzh.js";
26
27
  import {
27
28
  BUSY_TIMEOUT_MAX_MS,
28
29
  BUSY_TIMEOUT_MIN_MS,
29
30
  RAFA_INSTINCTS_PATH,
30
31
  STORE_BACKENDS,
31
32
  SYNC_STRATEGIES,
33
+ TEST_SCOPES,
32
34
  bundledBinDirectory,
33
35
  describeValue,
34
36
  isBusyTimeoutMs,
37
+ isGitHubLogin,
35
38
  isMapping,
36
39
  messageOf,
37
40
  resolveInstinctScopes,
38
41
  tierExists
39
- } from "./index-rs79dq9e.js";
42
+ } from "./index-fzhd3d4y.js";
40
43
  import {
41
44
  actionHash,
42
45
  triggerKey
@@ -158,7 +161,7 @@ function appendRows(path, rows, keyOf) {
158
161
  // src/effort/store/location.ts
159
162
  import { existsSync as existsSync2, realpathSync } from "fs";
160
163
  import { tmpdir } from "os";
161
- import { isAbsolute, join as join2, relative, resolve, sep } from "path";
164
+ import { basename, dirname as dirname2, isAbsolute, join as join2, relative, resolve, sep } from "path";
162
165
  var EFFORT_DIR_VARIABLE = "RAFA_EFFORT_DIR";
163
166
  var TEST_PROCESS_VARIABLE = "RAFA_TEST";
164
167
  var TEST_ENTRY_SUFFIX = ".test.ts";
@@ -166,6 +169,13 @@ function canonical(path) {
166
169
  const resolved = resolve(path);
167
170
  return existsSync2(resolved) ? realpathSync(resolved) : resolved;
168
171
  }
172
+ function realPathOf(path) {
173
+ const resolved = resolve(path);
174
+ if (existsSync2(resolved))
175
+ return realpathSync(resolved);
176
+ const parent = dirname2(resolved);
177
+ return parent === resolved ? resolved : join2(realPathOf(parent), basename(resolved));
178
+ }
169
179
  function isWithin(path, dir) {
170
180
  const between = relative(resolve(dir), resolve(path));
171
181
  const climbsOut = between === ".." || between.startsWith(`..${sep}`);
@@ -190,7 +200,7 @@ function isTestProcess(main = Bun.main, env = process.env) {
190
200
  return main.endsWith(TEST_ENTRY_SUFFIX) || env[TEST_PROCESS_VARIABLE] === "1";
191
201
  }
192
202
  function isUnderTempDir(path, tempDir = tmpdir()) {
193
- return isWithin(path, tempDir) || isWithin(path, canonical(tempDir));
203
+ return isWithin(path, tempDir) || isWithin(realPathOf(path), realPathOf(tempDir));
194
204
  }
195
205
  function currentProbe() {
196
206
  return { main: Bun.main, env: process.env, tempDir: tmpdir() };
@@ -244,12 +254,12 @@ function openNdjsonStore(repoRoot) {
244
254
 
245
255
  // src/effort/store/sqlite.ts
246
256
  import { existsSync as existsSync5, mkdirSync as mkdirSync5 } from "fs";
247
- import { dirname as dirname9, join as join9 } from "path";
257
+ import { dirname as dirname10, join as join9 } from "path";
248
258
  import { Database } from "bun:sqlite";
249
259
 
250
260
  // package.json
251
261
  var name = "@open-tomato/rafa";
252
- var version = "0.32.0";
262
+ var version = "0.34.0";
253
263
 
254
264
  // src/cli/version.ts
255
265
  var RAFA_VERSION = version;
@@ -259,7 +269,7 @@ function versionLine() {
259
269
 
260
270
  // src/effort/store/development-build.ts
261
271
  import { tmpdir as tmpdir2 } from "os";
262
- import { basename as basename2, dirname as dirname7, isAbsolute as isAbsolute5 } from "path";
272
+ import { basename as basename3, dirname as dirname8, isAbsolute as isAbsolute5 } from "path";
263
273
 
264
274
  // src/loop/sessions.ts
265
275
  import {
@@ -271,21 +281,11 @@ import {
271
281
  rmSync as rmSync3,
272
282
  writeFileSync as writeFileSync3
273
283
  } from "fs";
274
- import { basename, dirname as dirname5, isAbsolute as isAbsolute4, join as join7 } from "path";
275
-
276
- // src/next/hop-record.ts
277
- import { randomUUID as randomUUID2 } from "crypto";
278
- import { mkdirSync as mkdirSync3, readFileSync as readFileSync3, renameSync as renameSync2, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
279
- import { dirname as dirname4, join as join6 } from "path";
280
-
281
- // src/project/position.ts
282
- import { randomUUID } from "crypto";
283
- import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, renameSync, rmSync, writeFileSync } from "fs";
284
- import { dirname as dirname3, join as join5 } from "path";
284
+ import { dirname as dirname6, join as join7 } from "path";
285
285
 
286
286
  // src/project/scope.ts
287
287
  import { existsSync as existsSync3, realpathSync as realpathSync2 } from "fs";
288
- import { dirname as dirname2, isAbsolute as isAbsolute3, join as join4 } from "path";
288
+ import { dirname as dirname3, isAbsolute as isAbsolute3, join as join4 } from "path";
289
289
 
290
290
  // src/project/worktree-root.ts
291
291
  import { isAbsolute as isAbsolute2 } from "path";
@@ -352,7 +352,7 @@ function mainCheckoutOf(dir, seams = {}) {
352
352
  }
353
353
 
354
354
  // src/project/scope.ts
355
- var SCOPE_DIR = dirname2(CONFIG_FILE);
355
+ var SCOPE_DIR = dirname3(CONFIG_FILE);
356
356
  var INIT_COMMAND = "rafa init";
357
357
  var DISK_FILE_SYSTEM = Object.freeze({
358
358
  exists: (path) => existsSync3(path),
@@ -400,19 +400,23 @@ function realHomeOf(home, fs) {
400
400
  }
401
401
  }
402
402
  function selfAndAncestors(path) {
403
- const parent = dirname2(path);
403
+ const parent = dirname3(path);
404
404
  return parent === path ? [path] : [path, ...selfAndAncestors(parent)];
405
405
  }
406
- function walkUp(realStart, realHome, fs) {
406
+ var GIT_MARKER = ".git";
407
+ function walkUp(dirs, realHome, fs, stopAtTopLevel) {
407
408
  let passedUserConfig = false;
408
- for (const dir of selfAndAncestors(realStart)) {
409
- if (!fs.exists(configFilePath(dir)))
410
- continue;
411
- if (dir !== realHome)
412
- return { root: dir, passedUserConfig };
413
- passedUserConfig = true;
409
+ for (const dir of dirs) {
410
+ if (fs.exists(configFilePath(dir))) {
411
+ if (dir !== realHome)
412
+ return { root: dir, passedUserConfig, topLevel: null };
413
+ passedUserConfig = true;
414
+ }
415
+ if (stopAtTopLevel && fs.exists(join4(dir, GIT_MARKER))) {
416
+ return { root: null, passedUserConfig, topLevel: dir };
417
+ }
414
418
  }
415
- return { root: null, passedUserConfig };
419
+ return { root: null, passedUserConfig, topLevel: null };
416
420
  }
417
421
  function mainCheckoutAt(realStart, mainCheckout) {
418
422
  try {
@@ -430,6 +434,13 @@ function fallBack(realStart, realHome, fs, mainCheckout) {
430
434
  }
431
435
  return fs.exists(configFilePath(main)) ? { root: main, probed: main } : { root: null, probed: main };
432
436
  }
437
+ function walkAbove(lower, realHome, fs) {
438
+ if (lower.topLevel === null)
439
+ return lower;
440
+ const above = selfAndAncestors(lower.topLevel).slice(1);
441
+ const upper = walkUp(above, realHome, fs, false);
442
+ return { ...upper, passedUserConfig: lower.passedUserConfig || upper.passedUserConfig };
443
+ }
433
444
  function resolveScope(start, seams) {
434
445
  const { home, fs = DISK_FILE_SYSTEM, mainCheckout = mainCheckoutOf } = seams;
435
446
  requireAbsolute("start directory", start);
@@ -437,16 +448,29 @@ function resolveScope(start, seams) {
437
448
  const user = scopeAt(home);
438
449
  const realStart = realStartOf(start, fs);
439
450
  const realHome = realHomeOf(home, fs);
440
- const walk = walkUp(realStart, realHome, fs);
441
- const fallback = walk.root === null ? fallBack(realStart, realHome, fs, mainCheckout) : { root: walk.root, probed: null };
442
- if (fallback.root === null) {
451
+ const lower = walkUp(selfAndAncestors(realStart), realHome, fs, true);
452
+ const fallback = lower.root === null ? fallBack(realStart, realHome, fs, mainCheckout) : { root: lower.root, probed: null };
453
+ const walk = fallback.root === null ? walkAbove(lower, realHome, fs) : lower;
454
+ const root = fallback.root ?? walk.root;
455
+ if (root === null) {
443
456
  const passed = walk.passedUserConfig ? user.configFile : null;
444
457
  return { found: false, start, home, user, hint: initHint(start, passed, fallback.probed) };
445
458
  }
446
- return { found: true, root: fallback.root, home, project: scopeAt(fallback.root), user };
459
+ return { found: true, root, home, project: scopeAt(root), user };
447
460
  }
448
461
 
462
+ // src/loop/session-record-parse.ts
463
+ import { basename as basename2, isAbsolute as isAbsolute4 } from "path";
464
+
465
+ // src/next/hop-record.ts
466
+ import { randomUUID as randomUUID2 } from "crypto";
467
+ import { mkdirSync as mkdirSync3, readFileSync as readFileSync3, renameSync as renameSync2, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
468
+ import { dirname as dirname5, join as join6 } from "path";
469
+
449
470
  // src/project/position.ts
471
+ import { randomUUID } from "crypto";
472
+ import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, renameSync, rmSync, writeFileSync } from "fs";
473
+ import { dirname as dirname4, join as join5 } from "path";
450
474
  var POSITION_FILE = "position.json";
451
475
  function positionFilePath(root) {
452
476
  return join5(scopeAt(root).dir, POSITION_FILE);
@@ -496,7 +520,7 @@ function readPositionFile(root) {
496
520
  }
497
521
  function writePositionFile(root, position) {
498
522
  const file = positionFilePath(root);
499
- mkdirSync2(dirname3(file), { recursive: true });
523
+ mkdirSync2(dirname4(file), { recursive: true });
500
524
  const temporary = `${file}.${String(process.pid)}.${randomUUID()}.tmp`;
501
525
  const { current, previous, home } = position;
502
526
  try {
@@ -610,7 +634,7 @@ function ownKeys(record) {
610
634
  }
611
635
  function writeHopRecord(root, record) {
612
636
  const file = hopFilePath(root);
613
- mkdirSync3(dirname4(file), { recursive: true });
637
+ mkdirSync3(dirname5(file), { recursive: true });
614
638
  const temporary = `${file}.${String(process.pid)}.${randomUUID2()}.tmp`;
615
639
  try {
616
640
  writeFileSync2(temporary, `${JSON.stringify(ownKeys(record), null, 2)}
@@ -625,11 +649,13 @@ function staleAgainst(record, position) {
625
649
  return position.home.board !== record.home.board || position.home.epic !== record.home.epic;
626
650
  }
627
651
 
628
- // src/loop/sessions.ts
652
+ // src/loop/session-record-parse.ts
629
653
  var SESSION_STATES = Object.freeze(["running", "paused", "stopped", "done"]);
630
- var RUNS_DIR = "runs";
654
+ var SESSION_PHASES = Object.freeze(["task", "wrap-up", "pull-request", "ci", "repair"]);
655
+ var DEFAULT_PHASE = "task";
631
656
  var RECORD_EXTENSION = ".json";
632
657
  var SESSION_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
658
+ var SESSION_STEP_KINDS = Object.freeze(["baseline", "task", "stage", "pre-wrap-up"]);
633
659
 
634
660
  class SessionRecordError extends Error {
635
661
  file;
@@ -639,40 +665,9 @@ class SessionRecordError extends Error {
639
665
  this.file = file;
640
666
  }
641
667
  }
642
-
643
- class SessionConflictError extends Error {
644
- conflicts;
645
- constructor(conflicts) {
646
- const named = conflicts.map((conflict) => `${conflict.record.sessionId} (${conflict.reason})`);
647
- super(`the plan's sessions refuse the run: ${named.join(", ")}`);
648
- this.name = "SessionConflictError";
649
- this.conflicts = conflicts;
650
- }
651
- }
652
-
653
- class SessionStateError extends Error {
654
- file;
655
- state;
656
- constructor(file, state, onlyFrom) {
657
- const acted = onlyFrom.length === 0 ? "no state" : onlyFrom.join(" or ");
658
- super(`session record ${file}: stores ${state}, and the change acts on ${acted} alone`);
659
- this.name = "SessionStateError";
660
- this.file = file;
661
- this.state = state;
662
- }
663
- }
664
668
  function isSessionId(value) {
665
669
  return SESSION_ID_PATTERN.test(value);
666
670
  }
667
- function runsDir(root) {
668
- return join7(scopeAt(root).dir, RUNS_DIR);
669
- }
670
- function sessionFilePath(root, sessionId) {
671
- if (!isSessionId(sessionId)) {
672
- throw new Error(`session record: unusable session id ${JSON.stringify(sessionId)}`);
673
- }
674
- return join7(runsDir(root), `${sessionId}${RECORD_EXTENSION}`);
675
- }
676
671
  function describeValue2(value) {
677
672
  return value === undefined ? "missing" : JSON.stringify(value) ?? String(value);
678
673
  }
@@ -710,11 +705,68 @@ function worktreeProblem(fields) {
710
705
  const worktree = field(fields, "worktree");
711
706
  return isText(worktree) && isAbsolute4(worktree) ? null : `worktree is ${describeValue2(worktree)}, expected an absolute path`;
712
707
  }
708
+ function isTextList(value) {
709
+ return Array.isArray(value) && value.every(isText);
710
+ }
711
+ function isFailure(value) {
712
+ return isObject(value) && isText(field(value, "file")) && isText(field(value, "name"));
713
+ }
714
+ function isFailureList(value) {
715
+ return Array.isArray(value) && value.every(isFailure);
716
+ }
717
+ function failureKey(failure) {
718
+ return JSON.stringify([failure.file, failure.name]);
719
+ }
720
+ function stepFailuresProblem(step, at) {
721
+ const failures = field(step, "failures");
722
+ const newFailures = field(step, "newFailures");
723
+ if (!isFailureList(failures))
724
+ return `${at}.failures is ${describeValue2(failures)}, expected a list of failing tests`;
725
+ if (!isFailureList(newFailures)) {
726
+ return `${at}.newFailures is ${describeValue2(newFailures)}, expected a list of failing tests`;
727
+ }
728
+ const known = new Set(failures.map(failureKey));
729
+ const stray = newFailures.find((failure) => !known.has(failureKey(failure)));
730
+ return stray === undefined ? null : `${at}.newFailures names ${describeValue2(stray)}, which ${at}.failures does not`;
731
+ }
732
+ function stepInterruptedProblem(step, at) {
733
+ if (!Object.hasOwn(step, "interrupted"))
734
+ return null;
735
+ const interrupted = field(step, "interrupted");
736
+ return interrupted === true ? null : `${at}.interrupted is ${describeValue2(interrupted)}, expected true or no key`;
737
+ }
738
+ function stepProblems(step, at) {
739
+ if (!isObject(step))
740
+ return [`${at} is ${describeValue2(step)}, expected a step`];
741
+ const kind = field(step, "kind");
742
+ const scope = field(step, "scope");
743
+ const command = field(step, "command");
744
+ const exitCode = field(step, "exitCode");
745
+ const summary = field(step, "summary");
746
+ const problems = [
747
+ SESSION_STEP_KINDS.includes(kind) ? null : `${at}.kind is ${describeValue2(kind)}, expected one of ${SESSION_STEP_KINDS.join(", ")}`,
748
+ TEST_SCOPES.includes(scope) || isTextList(scope) ? null : `${at}.scope is ${describeValue2(scope)}, expected one of ${TEST_SCOPES.join(", ")} or a list of paths`,
749
+ isTextList(command) && command.length > 0 ? null : `${at}.command is ${describeValue2(command)}, expected a non-empty list of arguments`,
750
+ typeof exitCode === "number" && Number.isSafeInteger(exitCode) ? null : `${at}.exitCode is ${describeValue2(exitCode)}, expected a whole number`,
751
+ summary === null || typeof summary === "string" ? null : `${at}.summary is ${describeValue2(summary)}, expected null or a string`,
752
+ stepFailuresProblem(step, at),
753
+ stepInterruptedProblem(step, at)
754
+ ];
755
+ return problems.filter((problem) => problem !== null);
756
+ }
757
+ function stepsProblems(fields) {
758
+ if (!Object.hasOwn(fields, "steps"))
759
+ return [];
760
+ const steps = field(fields, "steps");
761
+ if (!Array.isArray(steps))
762
+ return [`steps is ${describeValue2(steps)}, expected a list of steps`];
763
+ return steps.flatMap((step, index) => stepProblems(step, `steps[${index}]`));
764
+ }
713
765
  function sessionIdProblem(sessionId, file) {
714
766
  if (typeof sessionId !== "string" || !isSessionId(sessionId)) {
715
767
  return `sessionId is ${describeValue2(sessionId)}, expected a plain file name`;
716
768
  }
717
- return basename(file) === `${sessionId}${RECORD_EXTENSION}` ? null : `sessionId ${JSON.stringify(sessionId)} is not the file's name`;
769
+ return basename2(file) === `${sessionId}${RECORD_EXTENSION}` ? null : `sessionId ${JSON.stringify(sessionId)} is not the file's name`;
718
770
  }
719
771
  function planStubProblem(planStub) {
720
772
  return planStub === null || typeof planStub === "string" && isStampableStub(planStub) ? null : `planStub is ${describeValue2(planStub)}, expected null or a plan stub`;
@@ -735,14 +787,41 @@ function recordProblems(fields, file) {
735
787
  SESSION_STATES.includes(state) ? null : `state is ${describeValue2(state)}, expected one of ${SESSION_STATES.join(", ")}`,
736
788
  taskProblem(field(fields, "task")),
737
789
  hopProblem(fields),
738
- worktreeProblem(fields)
790
+ worktreeProblem(fields),
791
+ ...stepsProblems(fields)
739
792
  ];
740
793
  return problems.filter((problem) => problem !== null);
741
794
  }
795
+ function isSessionPhase(value) {
796
+ return SESSION_PHASES.includes(value);
797
+ }
798
+ function phaseEntry(phase) {
799
+ return isSessionPhase(phase) ? { phase } : {};
800
+ }
742
801
  function freezeHop(value) {
743
802
  const hop2 = asHopRecord(value);
744
803
  return Object.freeze({ ...hop2, home: Object.freeze(hop2.home), from: Object.freeze(hop2.from) });
745
804
  }
805
+ function freezeFailures(failures) {
806
+ return Object.freeze(failures.map((failure) => Object.freeze({ file: failure.file, name: failure.name })));
807
+ }
808
+ function freezeStep(step) {
809
+ return Object.freeze({
810
+ kind: step.kind,
811
+ scope: typeof step.scope === "string" ? step.scope : Object.freeze([...step.scope]),
812
+ command: Object.freeze([...step.command]),
813
+ exitCode: step.exitCode,
814
+ summary: step.summary,
815
+ failures: freezeFailures(step.failures),
816
+ newFailures: freezeFailures(step.newFailures),
817
+ ...step.interrupted === true ? { interrupted: true } : {}
818
+ });
819
+ }
820
+ function stepsEntry(steps) {
821
+ if (!Array.isArray(steps) || steps.length === 0)
822
+ return {};
823
+ return { steps: Object.freeze(steps.map(freezeStep)) };
824
+ }
746
825
  function freezeRecord(fields) {
747
826
  const task = field(fields, "task");
748
827
  const hop2 = field(fields, "hop");
@@ -756,10 +835,18 @@ function freezeRecord(fields) {
756
835
  startedAt: field(fields, "startedAt"),
757
836
  state: field(fields, "state"),
758
837
  task: isObject(task) ? Object.freeze({ line: field(task, "line"), text: field(task, "text") }) : null,
838
+ ...phaseEntry(field(fields, "phase")),
759
839
  ...hop2 === undefined ? {} : { hop: freezeHop(hop2) },
760
- ...worktree === undefined ? {} : { worktree }
840
+ ...worktree === undefined ? {} : { worktree },
841
+ ...stepsEntry(field(fields, "steps"))
761
842
  });
762
843
  }
844
+ function sessionSteps(record) {
845
+ return record.steps ?? [];
846
+ }
847
+ function sessionPhase(record) {
848
+ return record.phase ?? DEFAULT_PHASE;
849
+ }
763
850
  function parseSessionRecord(text, file) {
764
851
  let parsed;
765
852
  try {
@@ -774,6 +861,40 @@ function parseSessionRecord(text, file) {
774
861
  throw new SessionRecordError(file, problems.join("; "));
775
862
  return freezeRecord(parsed);
776
863
  }
864
+
865
+ // src/loop/sessions.ts
866
+ var RUNS_DIR = "runs";
867
+
868
+ class SessionConflictError extends Error {
869
+ conflicts;
870
+ constructor(conflicts) {
871
+ const named = conflicts.map((conflict) => `${conflict.record.sessionId} (${conflict.reason})`);
872
+ super(`the plan's sessions refuse the run: ${named.join(", ")}`);
873
+ this.name = "SessionConflictError";
874
+ this.conflicts = conflicts;
875
+ }
876
+ }
877
+
878
+ class SessionStateError extends Error {
879
+ file;
880
+ state;
881
+ constructor(file, state, onlyFrom) {
882
+ const acted = onlyFrom.length === 0 ? "no state" : onlyFrom.join(" or ");
883
+ super(`session record ${file}: stores ${state}, and the change acts on ${acted} alone`);
884
+ this.name = "SessionStateError";
885
+ this.file = file;
886
+ this.state = state;
887
+ }
888
+ }
889
+ function runsDir(root) {
890
+ return join7(scopeAt(root).dir, RUNS_DIR);
891
+ }
892
+ function sessionFilePath(root, sessionId) {
893
+ if (!isSessionId(sessionId)) {
894
+ throw new Error(`session record: unusable session id ${JSON.stringify(sessionId)}`);
895
+ }
896
+ return join7(runsDir(root), `${sessionId}${RECORD_EXTENSION}`);
897
+ }
777
898
  function errorCode(error) {
778
899
  return typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" ? error.code : null;
779
900
  }
@@ -842,7 +963,7 @@ function checkRecord(record, file) {
842
963
  }
843
964
  function writeRecordFile(file, record, isNew) {
844
965
  checkRecord(record, file);
845
- mkdirSync4(dirname5(file), { recursive: true });
966
+ mkdirSync4(dirname6(file), { recursive: true });
846
967
  const temporary = `${file}.${process.pid}.tmp`;
847
968
  writeFileSync3(temporary, `${JSON.stringify(record, null, 2)}
848
969
  `);
@@ -870,10 +991,15 @@ function updateSession(root, sessionId, change) {
870
991
  if (change.onlyFrom !== undefined && !change.onlyFrom.includes(stored.state)) {
871
992
  throw new SessionStateError(file, stored.state, change.onlyFrom);
872
993
  }
994
+ const appended = change.appendStep === undefined ? [] : stepProblems(change.appendStep, `steps[${sessionSteps(stored).length}]`);
995
+ if (appended.length > 0)
996
+ throw new SessionRecordError(file, `not written: ${appended.join("; ")}`);
873
997
  const record = freezeRecord({
874
998
  ...stored,
875
999
  state: change.state ?? stored.state,
876
- task: change.task === undefined ? stored.task : change.task
1000
+ task: change.task === undefined ? stored.task : change.task,
1001
+ phase: change.phase ?? stored.phase,
1002
+ steps: change.appendStep === undefined ? sessionSteps(stored) : [...sessionSteps(stored), change.appendStep]
877
1003
  });
878
1004
  writeRecordFile(file, record, false);
879
1005
  return record;
@@ -881,7 +1007,7 @@ function updateSession(root, sessionId, change) {
881
1007
 
882
1008
  // src/runtime/identity.ts
883
1009
  import { existsSync as existsSync4, readFileSync as readFileSync5, realpathSync as realpathSync3 } from "fs";
884
- import { dirname as dirname6, join as join8, resolve as resolve2 } from "path";
1010
+ import { dirname as dirname7, join as join8, resolve as resolve2 } from "path";
885
1011
  var MANIFEST = "package.json";
886
1012
  var CHECKOUT_ENTRY = join8("src", "rafa.ts");
887
1013
  function realOrResolved(path) {
@@ -906,7 +1032,7 @@ function nearestRafaPackage(start) {
906
1032
  const manifest = join8(dir, MANIFEST);
907
1033
  if (existsSync4(manifest) && manifestName(manifest) === name)
908
1034
  return dir;
909
- const parent = dirname6(dir);
1035
+ const parent = dirname7(dir);
910
1036
  if (parent === dir)
911
1037
  return null;
912
1038
  dir = parent;
@@ -914,7 +1040,7 @@ function nearestRafaPackage(start) {
914
1040
  }
915
1041
  function readRuntimeIdentity(entry = Bun.main) {
916
1042
  const real = realOrResolved(entry);
917
- const packageDir = nearestRafaPackage(dirname6(real));
1043
+ const packageDir = nearestRafaPackage(dirname7(real));
918
1044
  if (packageDir !== null && existsSync4(join8(packageDir, CHECKOUT_ENTRY))) {
919
1045
  return { kind: "development", entry: real, checkout: packageDir };
920
1046
  }
@@ -941,11 +1067,11 @@ class DevelopmentBuildRefusedError extends Error {
941
1067
  }
942
1068
  }
943
1069
  function storeProjectRoot(path) {
944
- const storeDir = dirname7(path);
945
- const scopeDir = dirname7(storeDir);
946
- if (basename2(storeDir) !== EFFORT_DIR_NAME || basename2(scopeDir) !== SCOPE_DIR)
1070
+ const storeDir = dirname8(path);
1071
+ const scopeDir = dirname8(storeDir);
1072
+ if (basename3(storeDir) !== EFFORT_DIR_NAME || basename3(scopeDir) !== SCOPE_DIR)
947
1073
  return null;
948
- return dirname7(scopeDir);
1074
+ return dirname8(scopeDir);
949
1075
  }
950
1076
  function isOwnedStore(path, env, tempDir) {
951
1077
  if (isUnderTempDir(path, tempDir))
@@ -1575,7 +1701,7 @@ function activeStoreSettings() {
1575
1701
 
1576
1702
  // src/effort/store/store-meta.ts
1577
1703
  import { randomUUID as randomUUID3 } from "crypto";
1578
- import { dirname as dirname8 } from "path";
1704
+ import { dirname as dirname9 } from "path";
1579
1705
 
1580
1706
  // src/effort/store/store-identity.ts
1581
1707
  import { spawnSync as spawnSync3 } from "child_process";
@@ -1717,7 +1843,7 @@ function readStoreMeta(db) {
1717
1843
  };
1718
1844
  }
1719
1845
  function projectFor(storePath, recorded, seams) {
1720
- const read = (seams.readProject ?? readProjectIdentity)(dirname8(storePath));
1846
+ const read = (seams.readProject ?? readProjectIdentity)(dirname9(storePath));
1721
1847
  if (read.rootCommit !== null)
1722
1848
  return { rootCommit: read.rootCommit, remote: read.remote };
1723
1849
  if (recorded === null)
@@ -1788,7 +1914,7 @@ function batchEntries(kind, rows) {
1788
1914
  function withSqliteStore(path, access, create, use, identity = {}) {
1789
1915
  guardTestProcess(path);
1790
1916
  if (create)
1791
- mkdirSync5(dirname9(path), { recursive: true });
1917
+ mkdirSync5(dirname10(path), { recursive: true });
1792
1918
  const db = new Database(path, { readwrite: true, create });
1793
1919
  try {
1794
1920
  db.run(`PRAGMA busy_timeout = ${String(activeStoreSettings().busyTimeoutMs)}`);
@@ -2448,7 +2574,7 @@ function copyEffortStore(request) {
2448
2574
  import { randomUUID as randomUUID4 } from "crypto";
2449
2575
  import { existsSync as existsSync11, mkdtempSync, rmSync as rmSync6 } from "fs";
2450
2576
  import { tmpdir as tmpdir3 } from "os";
2451
- import { dirname as dirname10, join as join11 } from "path";
2577
+ import { dirname as dirname11, join as join11 } from "path";
2452
2578
  import { Database as Database5, SQLiteError as SQLiteError2 } from "bun:sqlite";
2453
2579
 
2454
2580
  // src/effort/commits.ts
@@ -2865,7 +2991,7 @@ var LIVE2 = "live";
2865
2991
  function asidePaths2(path, first, stamp) {
2866
2992
  return { parallelPath: `${path}.migrate-${stamp}`, backupPath: `${path}.before-${first}-${stamp}.bak` };
2867
2993
  }
2868
- function liveLoop(path, options, nothingDone = "Nothing was migrated.") {
2994
+ function liveLoop(path, options, nothingDone = "Nothing was migrated.", ownSessionId = null) {
2869
2995
  const root = storeProjectRoot(path);
2870
2996
  if (root === null)
2871
2997
  return;
@@ -2875,7 +3001,7 @@ function liveLoop(path, options, nothingDone = "Nothing was migrated.") {
2875
3001
  } catch (error) {
2876
3002
  throw new RebuildRefusal(`the loop's run records cannot be read, so a live loop on ${path} cannot be ruled out` + ` (${messageOf(error)}). ${nothingDone}`);
2877
3003
  }
2878
- return records.find((record) => record.state === "running" || record.state === "paused");
3004
+ return records.find((record) => (record.state === "running" || record.state === "paused") && record.sessionId !== ownSessionId);
2879
3005
  }
2880
3006
  function refuseLiveLoop2(path, breaking, options) {
2881
3007
  const loop = liveLoop(path, options);
@@ -3004,15 +3130,15 @@ function recordedMeta(path) {
3004
3130
  }
3005
3131
  function refuseOtherProject(options) {
3006
3132
  const other = recordedMeta(options.otherPath);
3007
- const here = recordedMeta(options.path)?.projectRootCommit ?? (options.readProject ?? readProjectIdentity)(dirname10(options.path)).rootCommit;
3133
+ const here = recordedMeta(options.path)?.projectRootCommit ?? (options.readProject ?? readProjectIdentity)(dirname11(options.path)).rootCommit;
3008
3134
  const there = other?.projectRootCommit ?? null;
3009
3135
  if (here !== null && there !== null && here !== there) {
3010
3136
  throw new MergeRefusal("other-project", `REFUSED \u2014 ${options.otherPath} is the effort store of another project (root commit ${there});` + ` this store's project has root commit ${here}. ${NOTHING_MERGED}`);
3011
3137
  }
3012
3138
  return other?.storeId ?? null;
3013
3139
  }
3014
- function refuseLiveLoop3(path, options) {
3015
- const loop = liveLoop(path, options, NOTHING_MERGED);
3140
+ function refuseLiveLoop3(path, options, sessionId) {
3141
+ const loop = liveLoop(path, options, NOTHING_MERGED, sessionId);
3016
3142
  if (loop === undefined)
3017
3143
  return;
3018
3144
  throw new MergeRefusal("live-loop", `REFUSED \u2014 loop ${loop.sessionId} (pid ${String(loop.pid)}, plan ${loop.planStub ?? loop.plan}) is running on this store,` + ` and swapping the merged store in under it would lose what it writes. ${NOTHING_MERGED}` + " Finish or stop that loop, then run it again.");
@@ -3103,7 +3229,7 @@ function refuseUnmergeable(options, migrations) {
3103
3229
  const otherStore = refuseOtherProject(options);
3104
3230
  if (!options.dryRun) {
3105
3231
  refuseUnownedDevelopmentWrite(path, [...writeNames(localPlan), MERGES_TABLE], options);
3106
- refuseLiveLoop3(path, options);
3232
+ refuseLiveLoop3(path, options, options.sessionId ?? null);
3107
3233
  }
3108
3234
  return { otherPlan, otherStore };
3109
3235
  }
@@ -3171,10 +3297,11 @@ function createFileSync(options) {
3171
3297
  const copied = copyEffortStore({ source, target });
3172
3298
  return Object.freeze({ status: "pushed", path: join12(copied.directory, SQLITE_STORE_FILE_NAME) });
3173
3299
  };
3174
- const pull = async ({ from, dryRun }) => {
3300
+ const pull = async ({ from, dryRun, sessionId = null }) => {
3175
3301
  const otherPath = resolve5(repoRoot, requirePath("from", from));
3176
3302
  const path = join12(storeDir(), SQLITE_STORE_FILE_NAME);
3177
- const merge2 = mergeStore({ ...mergeSeams(options), path, otherPath, backend, dryRun, stamp: fileStamp(now()), now });
3303
+ const stamp = fileStamp(now());
3304
+ const merge2 = mergeStore({ ...mergeSeams(options), path, otherPath, backend, dryRun, stamp, now, sessionId });
3178
3305
  return Object.freeze({ status: "pulled", merge: merge2 });
3179
3306
  };
3180
3307
  return Object.freeze({ kind: "file", push, pull });
@@ -3218,12 +3345,19 @@ class SyncModuleMissing extends Error {
3218
3345
  function isModuleStrategy(kind) {
3219
3346
  return MODULE_SYNC_STRATEGIES.includes(kind);
3220
3347
  }
3348
+ function contextOf(repoRoot, config) {
3349
+ const hub = hubContextOf(config);
3350
+ return {
3351
+ repoRoot,
3352
+ ...config.store === undefined ? {} : { store: config.store },
3353
+ ...hub === undefined ? {} : { hub }
3354
+ };
3355
+ }
3221
3356
  function selectSync(repoRoot, config, registry = CORE_ADAPTER_REGISTRY) {
3222
3357
  const kind = config.effortSync;
3223
3358
  const adapter = typeof kind === "string" ? registry.find("sync", kind) : undefined;
3224
- if (adapter !== undefined) {
3225
- return adapter.create(config.store === undefined ? { repoRoot } : { repoRoot, store: config.store });
3226
- }
3359
+ if (adapter !== undefined)
3360
+ return adapter.create(contextOf(repoRoot, config));
3227
3361
  const registered = registry.kinds("sync");
3228
3362
  if (isModuleStrategy(kind))
3229
3363
  throw new SyncModuleMissing(kind, registered);
@@ -4335,6 +4469,34 @@ function createLocalLearning(options) {
4335
4469
  return Object.freeze(learning);
4336
4470
  }
4337
4471
 
4472
+ // src/adapters/output/events.ts
4473
+ var EVENT_PREFIX = "rafa\xB7 ";
4474
+ var KIND_COLUMN = 17;
4475
+ function padKind(kind) {
4476
+ return `${kind} `.padEnd(KIND_COLUMN);
4477
+ }
4478
+ function oneLine(message) {
4479
+ return message.split(`
4480
+ `).map((part) => part.trim()).filter((part) => part !== "").join(" ");
4481
+ }
4482
+ function createEventsOutput({ stream }) {
4483
+ const writeLine = (line) => {
4484
+ stream.write(`${EVENT_PREFIX}${line}
4485
+ `);
4486
+ };
4487
+ const error = (message) => {
4488
+ writeLine(`${padKind("error")}${oneLine(message)}`);
4489
+ };
4490
+ const silent = () => {};
4491
+ const emit = (event) => {
4492
+ if (event.type === "event")
4493
+ writeLine(oneLine(event.summary));
4494
+ if (event.type === "log" && event.level === "error")
4495
+ error(event.message);
4496
+ };
4497
+ return Object.freeze({ info: silent, warn: silent, debug: silent, error, emit, result: silent });
4498
+ }
4499
+
4338
4500
  // src/adapters/output/json.ts
4339
4501
  var SECOND_RESULT_REFUSAL = "json output: refused a second terminal result event; a command emits exactly one";
4340
4502
  function createJsonOutput({
@@ -4643,7 +4805,7 @@ function guardSpend() {
4643
4805
  }
4644
4806
  async function spawnClaude(args, prompt, options = {}) {
4645
4807
  guardSpend();
4646
- if (activeOutputMode() === "json") {
4808
+ if (activeOutputMode() !== "text") {
4647
4809
  const { exitCode } = await spawnCaptured(args, prompt, options);
4648
4810
  return exitCode;
4649
4811
  }
@@ -4677,22 +4839,22 @@ function writeWholeLines(text, write) {
4677
4839
  }
4678
4840
  async function teeToOperator(stream) {
4679
4841
  const decoder = new TextDecoder;
4680
- const jsonOutput = activeOutputMode() === "json" ? activeOutput() : null;
4842
+ const routed = activeOutputMode() === "text" ? null : activeOutput();
4681
4843
  const writeLine = (line) => {
4682
- jsonOutput?.info(line);
4844
+ routed?.info(line);
4683
4845
  };
4684
4846
  let text = "";
4685
4847
  let pending = "";
4686
4848
  for await (const chunk of stream) {
4687
- if (jsonOutput === null)
4849
+ if (routed === null)
4688
4850
  process.stdout.write(chunk);
4689
4851
  const decoded = decoder.decode(chunk, { stream: true });
4690
4852
  text += decoded;
4691
- if (jsonOutput !== null)
4853
+ if (routed !== null)
4692
4854
  pending = writeWholeLines(pending + decoded, writeLine);
4693
4855
  }
4694
4856
  const tail = decoder.decode();
4695
- if (jsonOutput !== null) {
4857
+ if (routed !== null) {
4696
4858
  const rest = writeWholeLines(pending + tail, writeLine);
4697
4859
  if (rest !== "")
4698
4860
  writeLine(rest);
@@ -4810,9 +4972,12 @@ var GITHUB_LABELS = Object.freeze({
4810
4972
  blockedByPrefix: "blocked-by:OPT-"
4811
4973
  });
4812
4974
  var VIEW_FIELDS = "number,title,body,state,stateReason,labels,url";
4975
+ var LIST_COMMAND = "gh issue list";
4813
4976
  var LIST_FIELDS = "number,url,labels";
4977
+ var OPEN_LIST_FIELDS = "number,url,labels,title,body";
4978
+ var OPEN_ISSUES_LIMIT = 1000;
4814
4979
  var DEFAULT_FIND_LIMIT = 30;
4815
- var NOT_PLANNED = "NOT_PLANNED";
4980
+ var CANCELLED_REASONS = new Set(["NOT_PLANNED", "DUPLICATE"]);
4816
4981
  var ISSUE_NUMBER = /^[1-9]\d*$/;
4817
4982
  var REPO_PART = /^[\w.-]+$/;
4818
4983
  var CREATED_ISSUE_URL = /^https:\/\/[^\s/]+\/[\w.-]+\/[\w.-]+\/issues\/([1-9]\d*)$/;
@@ -4984,28 +5149,49 @@ function viewedIssue(stdout, number) {
4984
5149
  const checked = payload;
4985
5150
  return { ...checked, labels: labelNames(checked.labels) ?? [] };
4986
5151
  }
4987
- function listedIssues(stdout) {
4988
- const command = "gh issue list";
4989
- const payload = parseJson(stdout, command);
5152
+ function listedRows(stdout) {
5153
+ const payload = parseJson(stdout, LIST_COMMAND);
4990
5154
  if (!Array.isArray(payload)) {
4991
- throw new Error(`${PREFIX2}: ${command} answered ${describeValue(payload)}, expected a list`);
4992
- }
4993
- return payload.map((row, index) => {
4994
- const refuse2 = (problem) => new Error(`${PREFIX2}: ${command} answered row ${index} ${problem}`);
4995
- if (!isMapping(row))
4996
- throw refuse2(`${describeValue(row)}, expected a mapping`);
4997
- const { number, url } = row;
4998
- const labels = labelNames(row["labels"]);
4999
- if (typeof number !== "number" || !isIssueNumber3(String(number))) {
5000
- throw refuse2(`with number ${describeValue(number)}, expected an issue number`);
5001
- }
5002
- if (typeof url !== "string")
5003
- throw refuse2(`with url ${describeValue(url)}, expected a string`);
5004
- if (labels === null)
5005
- throw refuse2("with labels that are not a list of named labels");
5006
- return { number, url, labels };
5155
+ throw new Error(`${PREFIX2}: ${LIST_COMMAND} answered ${describeValue(payload)}, expected a list`);
5156
+ }
5157
+ return payload;
5158
+ }
5159
+ function rowRefusal(index, problem) {
5160
+ return new Error(`${PREFIX2}: ${LIST_COMMAND} answered row ${index} ${problem}`);
5161
+ }
5162
+ function listedRow(row, index) {
5163
+ if (!isMapping(row))
5164
+ throw rowRefusal(index, `${describeValue(row)}, expected a mapping`);
5165
+ const { number, url } = row;
5166
+ const labels = labelNames(row["labels"]);
5167
+ if (typeof number !== "number" || !isIssueNumber3(String(number))) {
5168
+ throw rowRefusal(index, `with number ${describeValue(number)}, expected an issue number`);
5169
+ }
5170
+ if (typeof url !== "string")
5171
+ throw rowRefusal(index, `with url ${describeValue(url)}, expected a string`);
5172
+ if (labels === null)
5173
+ throw rowRefusal(index, "with labels that are not a list of named labels");
5174
+ return { number, url, labels };
5175
+ }
5176
+ function listedIssues(stdout) {
5177
+ return listedRows(stdout).map(listedRow);
5178
+ }
5179
+ function listedOpenIssues(stdout) {
5180
+ return listedRows(stdout).map((row, index) => {
5181
+ const listed2 = listedRow(row, index);
5182
+ const { title, body } = row;
5183
+ if (typeof title !== "string")
5184
+ throw rowRefusal(index, `with title ${describeValue(title)}, expected a string`);
5185
+ if (typeof body !== "string")
5186
+ throw rowRefusal(index, `with body ${describeValue(body)}, expected a string`);
5187
+ return { ...listed2, title, body };
5007
5188
  });
5008
5189
  }
5190
+ function refOfRow(row) {
5191
+ const module = labelValue(row.labels, GITHUB_LABELS.modulePrefix);
5192
+ const ref = { opt: 0, kind: "github", externalId: String(row.number), url: row.url };
5193
+ return module === undefined ? ref : { ...ref, module };
5194
+ }
5009
5195
  function labelValue(labels, prefix) {
5010
5196
  return labels.find((label) => label.startsWith(prefix))?.slice(prefix.length);
5011
5197
  }
@@ -5019,7 +5205,7 @@ function moduleOfLabels(labels) {
5019
5205
  function stateOf(viewed) {
5020
5206
  if (viewed.state === "OPEN")
5021
5207
  return "todo";
5022
- return viewed.stateReason === NOT_PLANNED ? "cancelled" : "done";
5208
+ return viewed.stateReason !== null && CANCELLED_REASONS.has(viewed.stateReason) ? "cancelled" : "done";
5023
5209
  }
5024
5210
  function issueOf(ref, viewed) {
5025
5211
  const priority = labelValue(viewed.labels, GITHUB_LABELS.priorityPrefix);
@@ -5130,12 +5316,26 @@ function createGithubTracker(options) {
5130
5316
  ...optionalFlag("--label", moduleLabel),
5131
5317
  ...optionalFlag("--label", typeLabel),
5132
5318
  ...optionalFlag("--search", query.text)
5133
- ], "gh issue list");
5134
- return listedIssues(stdout).map((row) => {
5135
- const module = labelValue(row.labels, GITHUB_LABELS.modulePrefix);
5136
- const ref = { opt: 0, kind: "github", externalId: String(row.number), url: row.url };
5137
- return module === undefined ? ref : { ...ref, module };
5138
- });
5319
+ ], LIST_COMMAND);
5320
+ return listedIssues(stdout).map(refOfRow);
5321
+ },
5322
+ openIssues: async (type) => {
5323
+ if (!isOneOf(ISSUE_TYPES, type)) {
5324
+ throw new TypeError(`${PREFIX2}: openIssues refused type ${describeValue(type)}, expected one of: ${ISSUE_TYPES.join(", ")}`);
5325
+ }
5326
+ const stdout = await run([
5327
+ "issue",
5328
+ "list",
5329
+ "--state",
5330
+ "open",
5331
+ "--json",
5332
+ OPEN_LIST_FIELDS,
5333
+ "--limit",
5334
+ String(OPEN_ISSUES_LIMIT),
5335
+ "--label",
5336
+ `${GITHUB_LABELS.typePrefix}${type}`
5337
+ ], LIST_COMMAND);
5338
+ return listedOpenIssues(stdout).map((row) => ({ ref: refOfRow(row), title: row.title, body: row.body }));
5139
5339
  },
5140
5340
  comment: async (ref, body) => {
5141
5341
  const number = issueNumberOfRef(ref);
@@ -5173,6 +5373,7 @@ import { join as join16 } from "path";
5173
5373
  var PREFIX3 = "local tracker";
5174
5374
  var FRONTMATTER = /^---\n([\s\S]*?)\n---\n([\s\S]*)$/;
5175
5375
  var ISSUE_NUMBER2 = /^[1-9]\d*$/;
5376
+ var CLOSED_STATES = new Set(["done", "released", "cancelled"]);
5176
5377
  var ISSUE_EXTENSION = ".md";
5177
5378
  function isString2(value) {
5178
5379
  return typeof value === "string";
@@ -5304,7 +5505,7 @@ function parseLocalIssue(contents, sourcePath) {
5304
5505
  function warnThroughActiveOutput2(message) {
5305
5506
  activeOutput().warn(message);
5306
5507
  }
5307
- async function listLocalIssues(issuesDir, warn = warnThroughActiveOutput2) {
5508
+ async function listLocalIssues(issuesDir, warn = warnThroughActiveOutput2, reader = "find") {
5308
5509
  const numbers = await issueNumbers(issuesDir);
5309
5510
  const settled = await Promise.allSettled(numbers.map(async (number) => {
5310
5511
  const path = issuePath(issuesDir, number);
@@ -5319,7 +5520,7 @@ async function listLocalIssues(issuesDir, warn = warnThroughActiveOutput2) {
5319
5520
  return settled.flatMap((result) => {
5320
5521
  if (result.status === "fulfilled")
5321
5522
  return [result.value];
5322
- warn(`${messageOf(result.reason)}; find skipped it`);
5523
+ warn(`${messageOf(result.reason)}; ${reader} skipped it`);
5323
5524
  return [];
5324
5525
  });
5325
5526
  }
@@ -5405,6 +5606,14 @@ function createLocalTracker(options) {
5405
5606
  const matched = (await listLocalIssues(issuesDir, warn)).filter(({ record }) => matchesQuery(record, query));
5406
5607
  return matched.slice(0, query.limit ?? matched.length).map(({ number, record }) => refFor(number, record.draft.opt));
5407
5608
  },
5609
+ openIssues: async (type) => {
5610
+ const open = (await listLocalIssues(issuesDir, warn, "openIssues")).filter(({ record }) => record.draft.type === type && !CLOSED_STATES.has(record.state));
5611
+ return open.map(({ number, record }) => ({
5612
+ ref: refFor(number, record.draft.opt),
5613
+ title: record.draft.title,
5614
+ body: record.draft.body
5615
+ }));
5616
+ },
5408
5617
  comment: async (ref, body) => {
5409
5618
  const { number, record } = await read(ref);
5410
5619
  await rewrite(number, { ...record, comments: [...record.comments, `${now()}: ${body}`] });
@@ -5513,6 +5722,12 @@ var CORE_ADAPTERS = [
5513
5722
  portVersion: PORT_VERSIONS.output,
5514
5723
  create: ({ stream = process.stdout }) => createJsonOutput({ stream })
5515
5724
  },
5725
+ {
5726
+ port: "output",
5727
+ kind: "events",
5728
+ portVersion: PORT_VERSIONS.output,
5729
+ create: ({ stream = process.stdout }) => createEventsOutput({ stream })
5730
+ },
5516
5731
  {
5517
5732
  port: "tracker",
5518
5733
  kind: "local",
@@ -5579,6 +5794,358 @@ var CORE_ADAPTERS = [
5579
5794
  ];
5580
5795
  var CORE_ADAPTER_REGISTRY = createAdapterRegistry(CORE_ADAPTERS);
5581
5796
 
5797
+ // src/board/trust.ts
5798
+ var REPO_PATH = "{owner}/{repo}";
5799
+ var TRUSTED_PERMISSIONS = ["admin", "maintain", "write"];
5800
+ var TRUST_REFUSAL_EXIT = 2;
5801
+ var REMEDY = Object.freeze({
5802
+ issue: "a member must open the spec",
5803
+ "pull request": "a member must open the pull request"
5804
+ });
5805
+ var UNREPORTED = "the lookup wrote nothing";
5806
+ function failureDetail(reading) {
5807
+ const detail = reading.permission?.detail ?? "";
5808
+ return detail === "" ? UNREPORTED : detail;
5809
+ }
5810
+ function isTrustedPermission(value) {
5811
+ return TRUSTED_PERMISSIONS.some((permission) => permission === value);
5812
+ }
5813
+ function stringOrNull(value) {
5814
+ return typeof value === "string" ? value : null;
5815
+ }
5816
+ function lookupFailed(login, detail) {
5817
+ return { login, permission: null, roleName: null, detail };
5818
+ }
5819
+ function detailOf2(result, command) {
5820
+ const written = result.stderr.trim() || result.stdout.trim();
5821
+ return written === "" ? `${command} failed and wrote nothing` : `${command} failed: ${written}`;
5822
+ }
5823
+ function createGhPermissions(options) {
5824
+ const { gh } = options;
5825
+ return async (login) => {
5826
+ if (!isGitHubLogin(login)) {
5827
+ return lookupFailed(login, `${describeValue(login)} is not a GitHub login, so no permission was read`);
5828
+ }
5829
+ const path = `repos/${REPO_PATH}/collaborators/${login}/permission`;
5830
+ const command = `gh api ${path}`;
5831
+ const result = await gh(["api", path]);
5832
+ if (!result.ok)
5833
+ return lookupFailed(login, detailOf2(result, command));
5834
+ let payload;
5835
+ try {
5836
+ payload = JSON.parse(result.stdout);
5837
+ } catch (error) {
5838
+ return lookupFailed(login, `${command} wrote output that is not JSON: ${messageOf(error)}`);
5839
+ }
5840
+ if (!isMapping(payload)) {
5841
+ return lookupFailed(login, `${command} answered ${describeValue(payload)}, expected a mapping`);
5842
+ }
5843
+ const permission = stringOrNull(payload["permission"]);
5844
+ const roleName = stringOrNull(payload["role_name"]);
5845
+ return permission === null && roleName === null ? lookupFailed(login, `${command} answered neither a permission nor a role_name`) : { login, permission, roleName, detail: "" };
5846
+ };
5847
+ }
5848
+ async function readAuthorTrust(options) {
5849
+ const { login, permissions, trustedAuthors } = options;
5850
+ if (!isGitHubLogin(login)) {
5851
+ const detail = `${describeValue(login)} is not a GitHub login, so no permission was read`;
5852
+ return {
5853
+ login,
5854
+ trusted: false,
5855
+ source: null,
5856
+ refusal: "lookup-failed",
5857
+ permission: lookupFailed(login, detail)
5858
+ };
5859
+ }
5860
+ const wanted = login.toLowerCase();
5861
+ const listed2 = trustedAuthors.some((author) => author.toLowerCase() === wanted);
5862
+ if (listed2) {
5863
+ return { login, trusted: true, source: "allow-list", refusal: null, permission: null };
5864
+ }
5865
+ const permission = await permissions(login);
5866
+ if (permission.detail !== "") {
5867
+ return { login, trusted: false, source: null, refusal: "lookup-failed", permission };
5868
+ }
5869
+ const trusted = isTrustedPermission(permission.permission) || isTrustedPermission(permission.roleName);
5870
+ return {
5871
+ login,
5872
+ trusted,
5873
+ source: trusted ? "permission" : null,
5874
+ refusal: trusted ? null : "no-write-access",
5875
+ permission
5876
+ };
5877
+ }
5878
+ function ghBoardTrust(options) {
5879
+ return {
5880
+ permissions: createGhPermissions({ gh: options.gh }),
5881
+ trustedAuthors: options.trustedAuthors,
5882
+ repo: options.repo
5883
+ };
5884
+ }
5885
+ function readBoardTrust(trust, login) {
5886
+ return readAuthorTrust({
5887
+ login,
5888
+ permissions: trust.permissions,
5889
+ trustedAuthors: trust.trustedAuthors
5890
+ });
5891
+ }
5892
+ async function requireTrustedBoardAuthor(item, trust, login) {
5893
+ const reading = await readBoardTrust(trust, login);
5894
+ requireTrustedAuthor({ kind: item.kind, number: item.number, repo: trust.repo }, reading);
5895
+ return reading;
5896
+ }
5897
+ function trustRefusalClause(repo, reading) {
5898
+ if (reading.trusted) {
5899
+ throw new TypeError(`board trust: ${reading.login} is trusted, and has no refusal to name`);
5900
+ }
5901
+ return reading.refusal === "lookup-failed" ? `whose write access to ${repo} could not be read (${failureDetail(reading)})` : `who has no write access to ${repo}`;
5902
+ }
5903
+ function trustRefusalMessage(item, reading) {
5904
+ const because = trustRefusalClause(item.repo, reading);
5905
+ const opened = `${item.kind} #${String(item.number)} was opened by ${reading.login}`;
5906
+ return `${opened}, ${because}; ${REMEDY[item.kind]}`;
5907
+ }
5908
+ function requireTrustedAuthor(item, reading) {
5909
+ if (reading.trusted)
5910
+ return;
5911
+ throw new CommandExit(TRUST_REFUSAL_EXIT, trustRefusalMessage(item, reading));
5912
+ }
5913
+ // src/effort/sync/wire.ts
5914
+ import { existsSync as existsSync14, mkdtempSync as mkdtempSync2, rmSync as rmSync7 } from "fs";
5915
+ import { tmpdir as tmpdir4 } from "os";
5916
+ import { join as join17 } from "path";
5917
+ import { Database as Database6, SQLiteError as SQLiteError3 } from "bun:sqlite";
5918
+ var WIRE_FORMAT = "rafa-effort-wire";
5919
+ var WIRE_VERSION = 1;
5920
+
5921
+ class WireFormatError extends Error {
5922
+ name = "WireFormatError";
5923
+ }
5924
+
5925
+ class WireExportRefusal extends Error {
5926
+ name = "WireExportRefusal";
5927
+ }
5928
+ var MERGED_TABLES = Object.entries(MERGE_RULES).filter(([, rule]) => rule.scope === "merged").map(([table]) => table);
5929
+ var ORIGIN_COLUMNS = ["origin_store", "origin_seq"];
5930
+ function isRecord4(value) {
5931
+ return typeof value === "object" && value !== null && !Array.isArray(value);
5932
+ }
5933
+ function isCount(value) {
5934
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
5935
+ }
5936
+ function isMergedTable(name2) {
5937
+ return MERGED_TABLES.includes(name2);
5938
+ }
5939
+ function checkCursor(value) {
5940
+ if (!isRecord4(value))
5941
+ throw new WireFormatError("wire: cursor is not an object");
5942
+ const entries = Object.entries(value).map(([table, seq]) => {
5943
+ if (!isMergedTable(table))
5944
+ throw new WireFormatError(`wire: cursor names ${JSON.stringify(table)}, which is no merged table`);
5945
+ if (!isCount(seq))
5946
+ throw new WireFormatError(`wire: cursor for ${table} is ${JSON.stringify(seq)}, expected a whole number of 0 or more`);
5947
+ return [table, seq];
5948
+ });
5949
+ return Object.freeze(Object.fromEntries(entries));
5950
+ }
5951
+ function checkRow(table, value, index) {
5952
+ const where = `wire: ${table} row ${String(index)}`;
5953
+ if (!isRecord4(value))
5954
+ throw new WireFormatError(`${where} is not an object`);
5955
+ for (const [column, cell] of Object.entries(value)) {
5956
+ const carried = cell === null || typeof cell === "string" || typeof cell === "number" && Number.isFinite(cell);
5957
+ if (!carried)
5958
+ throw new WireFormatError(`${where} holds ${JSON.stringify(cell) ?? typeof cell} in ${column}, expected a string, a number or null`);
5959
+ }
5960
+ const seq = value.seq;
5961
+ if (!isCount(seq) || seq === 0)
5962
+ throw new WireFormatError(`${where} has seq ${JSON.stringify(seq) ?? "undefined"}, expected a whole number above 0`);
5963
+ return Object.freeze({ ...value });
5964
+ }
5965
+ function checkMigrations(value) {
5966
+ if (!Array.isArray(value))
5967
+ throw new WireFormatError("wire: migrations is not a list");
5968
+ const ids = value.map((id) => {
5969
+ if (typeof id !== "string" || id === "")
5970
+ throw new WireFormatError(`wire: migration id ${JSON.stringify(id)} is not a name`);
5971
+ return id;
5972
+ });
5973
+ const twice = ids.filter((id, index) => ids.indexOf(id) !== index);
5974
+ if (twice.length > 0)
5975
+ throw new WireFormatError(`wire: migrations name ${twice.join(", ")} more than once`);
5976
+ return Object.freeze(ids);
5977
+ }
5978
+ function checkTables(value) {
5979
+ if (!isRecord4(value))
5980
+ throw new WireFormatError("wire: tables is not an object");
5981
+ const entries = Object.entries(value).map(([table, rows]) => {
5982
+ if (!isMergedTable(table))
5983
+ throw new WireFormatError(`wire: tables names ${JSON.stringify(table)}, which is no merged table`);
5984
+ if (!Array.isArray(rows))
5985
+ throw new WireFormatError(`wire: tables.${table} is not a list`);
5986
+ return [table, Object.freeze(rows.map((row, index) => checkRow(table, row, index)))];
5987
+ });
5988
+ return Object.freeze(Object.fromEntries(entries));
5989
+ }
5990
+ function checkPayload(value) {
5991
+ if (!isRecord4(value))
5992
+ throw new WireFormatError("wire: the payload is not an object");
5993
+ if (value.format !== WIRE_FORMAT)
5994
+ throw new WireFormatError(`wire: format is ${JSON.stringify(value.format) ?? "undefined"}, expected ${JSON.stringify(WIRE_FORMAT)}`);
5995
+ if (value.version !== WIRE_VERSION)
5996
+ throw new WireFormatError(`wire: version is ${JSON.stringify(value.version) ?? "undefined"}; this rafa reads version ${String(WIRE_VERSION)}`);
5997
+ return Object.freeze({
5998
+ format: WIRE_FORMAT,
5999
+ version: WIRE_VERSION,
6000
+ migrations: checkMigrations(value.migrations),
6001
+ tables: checkTables(value.tables),
6002
+ cursor: checkCursor(value.cursor)
6003
+ });
6004
+ }
6005
+ function encodeWirePayload(payload) {
6006
+ return JSON.stringify(payload);
6007
+ }
6008
+ function decodeWirePayload(text) {
6009
+ let parsed;
6010
+ try {
6011
+ parsed = JSON.parse(text);
6012
+ } catch (error) {
6013
+ const reason = error instanceof Error ? error.message : String(error);
6014
+ throw new WireFormatError(`wire: the payload is not JSON (${reason})`);
6015
+ }
6016
+ return checkPayload(parsed);
6017
+ }
6018
+ function readLoggedIds(db) {
6019
+ const hasLog = db.query("SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?").get(MIGRATION_LOG_TABLE) !== null;
6020
+ if (!hasLog)
6021
+ return null;
6022
+ return db.query(`SELECT id FROM ${MIGRATION_LOG_TABLE} ORDER BY seq`).all().map(({ id }) => id);
6023
+ }
6024
+ function columnsOf(db, table) {
6025
+ return db.query(`PRAGMA table_info(${quoted(table)})`).all().map(({ name: name2 }) => name2);
6026
+ }
6027
+ function wireValue(value, where) {
6028
+ if (typeof value === "bigint") {
6029
+ const number = Number(value);
6030
+ if (Number.isSafeInteger(number))
6031
+ return number;
6032
+ throw new WireExportRefusal(`wire: ${where} holds ${String(value)}, past the whole numbers JSON carries exactly`);
6033
+ }
6034
+ if (value instanceof Uint8Array)
6035
+ throw new WireExportRefusal(`wire: ${where} holds a BLOB, which the wire does not carry`);
6036
+ return value;
6037
+ }
6038
+ function readRows(db, table, columns, after, path) {
6039
+ const named = columns.map(quoted).join(", ");
6040
+ return db.query(`SELECT ${named} FROM ${quoted(table)} WHERE seq > ? ORDER BY seq`).all(after).map((row) => Object.freeze(Object.fromEntries(Object.entries(row).map(([column, value]) => [
6041
+ column,
6042
+ wireValue(value, `${path} ${table}.${column} at seq ${String(row.seq)}`)
6043
+ ]))));
6044
+ }
6045
+ function readPayload(db, path, cursor) {
6046
+ const migrations = readLoggedIds(db);
6047
+ if (migrations === null) {
6048
+ throw new WireExportRefusal(`wire: ${path} has no migration log; any rafa command that writes the store brings it forward`);
6049
+ }
6050
+ const tables = {};
6051
+ const next = { ...cursor };
6052
+ for (const table of MERGED_TABLES) {
6053
+ const columns = columnsOf(db, table);
6054
+ if (columns.length === 0)
6055
+ continue;
6056
+ const missing = ORIGIN_COLUMNS.filter((column) => !columns.includes(column));
6057
+ if (missing.length > 0) {
6058
+ throw new WireExportRefusal(`wire: ${path} holds no ${missing.map((column) => `${table}.${column}`).join(", ")}, so its rows carry no origin pair`);
6059
+ }
6060
+ const rows = readRows(db, table, columns, cursor[table] ?? 0, path);
6061
+ tables[table] = Object.freeze(rows);
6062
+ const last = rows[rows.length - 1];
6063
+ next[table] = last === undefined ? cursor[table] ?? 0 : Number(last.seq);
6064
+ }
6065
+ return Object.freeze({
6066
+ format: WIRE_FORMAT,
6067
+ version: WIRE_VERSION,
6068
+ migrations: Object.freeze([...migrations]),
6069
+ tables: Object.freeze(tables),
6070
+ cursor: Object.freeze(next)
6071
+ });
6072
+ }
6073
+ function exportWirePayload(options) {
6074
+ const { path } = options;
6075
+ const cursor = checkCursor(options.cursor ?? {});
6076
+ guardTestProcess(path);
6077
+ if (!existsSync14(path))
6078
+ throw new WireExportRefusal(`wire: ${path} is not there`);
6079
+ const db = new Database6(path, { readonly: true, safeIntegers: true });
6080
+ try {
6081
+ db.run(`PRAGMA busy_timeout = ${String(activeStoreSettings().busyTimeoutMs)}`);
6082
+ return db.transaction(() => readPayload(db, path, cursor))();
6083
+ } finally {
6084
+ db.close();
6085
+ }
6086
+ }
6087
+ function namedMigrations(ids, catalogue) {
6088
+ const known = new Set(catalogue.map(({ id }) => id));
6089
+ const unknown = ids.filter((id) => !known.has(id));
6090
+ if (unknown.length > 0) {
6091
+ throw new WireFormatError(`wire: the payload's store holds migration ${unknown.join(", ")}, which this rafa does not know; update rafa to take its rows`);
6092
+ }
6093
+ return catalogue.filter(({ id }) => ids.includes(id));
6094
+ }
6095
+ function insertRows(db, table, rows) {
6096
+ const columns = columnsOf(db, table);
6097
+ if (columns.length === 0)
6098
+ throw new WireFormatError(`wire: the payload carries ${table}, which its migrations do not make`);
6099
+ const statement = db.query(`INSERT INTO ${quoted(table)} (${columns.map(quoted).join(", ")}) VALUES (${columns.map(() => "?").join(", ")})`);
6100
+ const expected = [...columns].sort().join(",");
6101
+ rows.forEach((row, index) => {
6102
+ const where = `wire: ${table} row ${String(index)}`;
6103
+ const held = Object.keys(row);
6104
+ if ([...held].sort().join(",") !== expected) {
6105
+ throw new WireFormatError(`${where} holds columns ${held.join(", ")}; its migrations make ${columns.join(", ")}`);
6106
+ }
6107
+ try {
6108
+ statement.run(...columns.map((column) => row[column] ?? null));
6109
+ } catch (error) {
6110
+ if (!(error instanceof SQLiteError3))
6111
+ throw error;
6112
+ throw new WireFormatError(`${where} (seq ${String(row.seq)}) is refused by the table: ${error.message}`);
6113
+ }
6114
+ });
6115
+ }
6116
+ function buildStore(path, payload, options) {
6117
+ const migrations = namedMigrations(payload.migrations, options.migrations ?? SQLITE_MIGRATIONS);
6118
+ const db = new Database6(path, { create: true, readwrite: true });
6119
+ try {
6120
+ bringForward(db, path, "write", "open", {
6121
+ migrations,
6122
+ appliedBy: appliedByName(options.identity ?? readRuntimeIdentity()),
6123
+ now: options.now,
6124
+ builtAside: true
6125
+ });
6126
+ db.transaction(() => {
6127
+ for (const [table, rows] of Object.entries(payload.tables))
6128
+ insertRows(db, table, rows);
6129
+ })();
6130
+ } finally {
6131
+ db.close();
6132
+ }
6133
+ }
6134
+ function materialiseWirePayload(payload, options = {}) {
6135
+ const checked = checkPayload(payload);
6136
+ const directory = mkdtempSync2(join17(options.tempDir ?? tmpdir4(), "rafa-wire-"));
6137
+ const release = () => rmSync7(directory, { recursive: true, force: true });
6138
+ const path = join17(directory, SQLITE_STORE_FILE_NAME);
6139
+ try {
6140
+ guardTestProcess(path);
6141
+ buildStore(path, checked, options);
6142
+ } catch (error) {
6143
+ release();
6144
+ throw error;
6145
+ }
6146
+ return Object.freeze({ directory, path, release });
6147
+ }
6148
+
5582
6149
  // src/effort/store/index.ts
5583
6150
  function describeValue4(value) {
5584
6151
  if (typeof value === "string")
@@ -5596,4 +6163,4 @@ function selectEffortStore(repoRoot, config) {
5596
6163
  return adapter.create({ repoRoot });
5597
6164
  }
5598
6165
 
5599
- export { EFFORT_STORE_DIR, effortStoreFileName, EFFORT_DIR_VARIABLE, effortStoreDir, guardTestProcess, EFFORT_KEY_PROJECTIONS, openNdjsonStore, name, version, RAFA_VERSION, versionLine, createGitRunner, gitSaid, NOT_A_REPOSITORY, mainCheckoutOf, SCOPE_DIR, INIT_COMMAND, DISK_FILE_SYSTEM, ScopeError, scopeAt, initHint, selfAndAncestors, resolveScope, POSITION_FILE, positionFilePath, readPositionFile, writePositionFile, positionAt, rehome, hop, goHome, HOP_FILE, hopFilePath, readHopRecord, writeHopRecord, staleAgainst, SessionRecordError, SessionConflictError, SessionStateError, isSessionId, runsDir, sessionFilePath, errorCode, isPidAlive, readSessions, readSession, beginSession, updateSession, DEVELOPMENT_NEXT_STEP, DEVELOPMENT_MARK, DevelopmentBuildRefusedError, refuseUnownedDevelopmentWrite, LEGACY_GATE_OPEN, LEGACY_GATE_CLOSED, SQLITE_MIGRATIONS, sqliteCatalogue, planSchema, SchemaRefusedError, readStoreSchema, writeNames, ACROSS_STORES_ORDER, STAMPED_COLUMNS, stampedValues, activeStoreSettings, normalizeRemote, gitRemoteUrl, readStoreMeta, SQLITE_STORE_FILE_NAME, sqliteStorePath, LONE_SURROGATE, withSqliteStore, writeSqliteStore, openSqliteStore, RebuildRefusal, readConfigFile, loadConfig, plansDirAt, requireProject, resolvePlansDir, resolveProjectConfig, countTasks, formatCounts, checkbox, planFileName, stubOfPlanFile, isFile, issueLine, plural, expectNoArgument, expectOneArgument, expectAtMostOneArgument, readSwitch, fileStamp, fix_schema_default, EffortCopyRefusal, EffortCopyFailure, copyEffortStore, minutesBetween, readCommitLog, UnionSchemaMismatch, migrateStore, MOVE_TO_SQLITE, MergeRefusal, mergeStore, createFileSync, SyncModuleMissing, selectSync, SKILL_FILE, MARKDOWN_EXTENSION, scanLayout, declaredNameIssue, readFrontmatterDocument, readFrontmatter, renderFrontmatter, mergeFrontmatter, writeFrontmatter, ACTION_HEADING, CAUSE_HEADING, parseInstinct, writeInstinct, readScopes, allRecords, findRecords, instinctProject, toHeldRecords, sessionSpawnEnv, getClaudeUsagePercent, checkUsage, CLAUDE_BIN, claudeArgs, interruptClaudeSessions, spawnClaude, runClaude, spawnClaudeCaptured, runClaudeCaptured, planFilePath, ClaudePlannerError, ISSUE_TYPES, ISSUE_PRIORITIES, ISSUE_STATES, GITHUB_LABELS, createGhRunner, typeOfLabels, moduleOfLabels, createLocalTracker, PORT_VERSIONS, createAdapterRegistry, CORE_ADAPTER_REGISTRY, selectEffortStore };
6166
+ export { EFFORT_STORE_DIR, effortStoreFileName, EFFORT_DIR_VARIABLE, effortStoreDir, guardTestProcess, EFFORT_KEY_PROJECTIONS, openNdjsonStore, name, version, RAFA_VERSION, versionLine, createGitRunner, gitSaid, NOT_A_REPOSITORY, mainCheckoutOf, SCOPE_DIR, INIT_COMMAND, DISK_FILE_SYSTEM, ScopeError, scopeAt, initHint, selfAndAncestors, resolveScope, POSITION_FILE, positionFilePath, readPositionFile, writePositionFile, positionAt, rehome, hop, goHome, HOP_FILE, hopFilePath, readHopRecord, writeHopRecord, staleAgainst, SessionRecordError, isSessionId, sessionPhase, SessionConflictError, SessionStateError, runsDir, sessionFilePath, errorCode, isPidAlive, readSessions, readSession, beginSession, updateSession, DEVELOPMENT_NEXT_STEP, DEVELOPMENT_MARK, DevelopmentBuildRefusedError, refuseUnownedDevelopmentWrite, LEGACY_GATE_OPEN, LEGACY_GATE_CLOSED, SQLITE_MIGRATIONS, sqliteCatalogue, planSchema, SchemaRefusedError, readStoreSchema, writeNames, ACROSS_STORES_ORDER, STAMPED_COLUMNS, stampedValues, activeStoreSettings, normalizeRemote, gitRemoteUrl, readStoreMeta, SQLITE_STORE_FILE_NAME, sqliteStorePath, LONE_SURROGATE, withSqliteStore, writeSqliteStore, openSqliteStore, RebuildRefusal, readConfigFile, loadConfig, plansDirAt, requireProject, resolvePlansDir, resolveProjectConfig, countTasks, formatCounts, checkbox, planFileName, stubOfPlanFile, isFile, issueLine, plural, expectNoArgument, expectOneArgument, expectAtMostOneArgument, readSwitch, fileStamp, fix_schema_default, EffortCopyRefusal, EffortCopyFailure, copyEffortStore, minutesBetween, readCommitLog, UnionSchemaMismatch, migrateStore, MOVE_TO_SQLITE, MergeRefusal, mergeStore, createFileSync, SyncModuleMissing, selectSync, SKILL_FILE, MARKDOWN_EXTENSION, scanLayout, declaredNameIssue, readFrontmatterDocument, readFrontmatter, renderFrontmatter, mergeFrontmatter, writeFrontmatter, ACTION_HEADING, CAUSE_HEADING, parseInstinct, writeInstinct, readScopes, allRecords, findRecords, instinctProject, toHeldRecords, padKind, oneLine, sessionSpawnEnv, getClaudeUsagePercent, checkUsage, CLAUDE_BIN, claudeArgs, interruptClaudeSessions, spawnClaude, runClaude, spawnClaudeCaptured, runClaudeCaptured, planFilePath, ClaudePlannerError, ISSUE_TYPES, ISSUE_PRIORITIES, ISSUE_STATES, GITHUB_LABELS, createGhRunner, typeOfLabels, moduleOfLabels, createLocalTracker, PORT_VERSIONS, createAdapterRegistry, CORE_ADAPTER_REGISTRY, TRUSTED_PERMISSIONS, createGhPermissions, readAuthorTrust, ghBoardTrust, readBoardTrust, requireTrustedBoardAuthor, trustRefusalClause, requireTrustedAuthor, WIRE_FORMAT, WIRE_VERSION, WireFormatError, WireExportRefusal, encodeWirePayload, decodeWirePayload, exportWirePayload, materialiseWirePayload, selectEffortStore };