@open-tomato/rafa 0.34.1 → 0.36.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -18
- package/dist/bundled/agents/doc-updater.md +1 -1
- package/dist/bundled/operators/.claude-plugin/plugin.json +5 -0
- package/dist/bundled/operators/agents/rafa-stretch-engineer.md +40 -19
- package/dist/bundled/operators/agents/rafa-stretch-watchtower.md +9 -9
- package/dist/bundled/operators/skills/rafa-stretch-pit-stop/SKILL.md +2 -2
- package/dist/cli.js +3748 -1682
- package/dist/effort/store/index.js +2 -2
- package/dist/{index-s8ff86xt.js → index-2n2nedrk.js} +1857 -860
- package/dist/{index-v8d7c6sw.js → index-7bb0fdb7.js} +40 -2
- package/dist/{index-nr4hdb35.js → index-ddzzmcaz.js} +1127 -346
- package/dist/{index-vp0xghk2.js → index-jfas157k.js} +6 -3
- package/dist/{index-bebv92ha.js → index-qvbeqrx3.js} +401 -201
- package/dist/index.js +4 -6
- package/dist/plan/index.js +2 -2
- package/package.json +1 -1
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
parseConfigText,
|
|
16
16
|
readRafaBlocks,
|
|
17
17
|
resolveConfig
|
|
18
|
-
} from "./index-
|
|
18
|
+
} from "./index-jfas157k.js";
|
|
19
19
|
import {
|
|
20
20
|
bless,
|
|
21
21
|
merge
|
|
@@ -255,11 +255,11 @@ function openNdjsonStore(repoRoot) {
|
|
|
255
255
|
// src/effort/store/sqlite.ts
|
|
256
256
|
import { existsSync as existsSync5, mkdirSync as mkdirSync5 } from "fs";
|
|
257
257
|
import { dirname as dirname10, join as join9 } from "path";
|
|
258
|
-
import { Database } from "bun:sqlite";
|
|
258
|
+
import { Database as Database2 } from "bun:sqlite";
|
|
259
259
|
|
|
260
260
|
// package.json
|
|
261
261
|
var name = "@open-tomato/rafa";
|
|
262
|
-
var version = "0.
|
|
262
|
+
var version = "0.36.0";
|
|
263
263
|
|
|
264
264
|
// src/cli/version.ts
|
|
265
265
|
var RAFA_VERSION = version;
|
|
@@ -656,6 +656,7 @@ var DEFAULT_PHASE = "task";
|
|
|
656
656
|
var RECORD_EXTENSION = ".json";
|
|
657
657
|
var SESSION_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
658
658
|
var SESSION_STEP_KINDS = Object.freeze(["baseline", "task", "stage", "pre-wrap-up"]);
|
|
659
|
+
var SESSION_STEP_REASONS = Object.freeze(["declared", "trigger", "fallback", "no-module-tests", "stage"]);
|
|
659
660
|
|
|
660
661
|
class SessionRecordError extends Error {
|
|
661
662
|
file;
|
|
@@ -805,10 +806,17 @@ function freezeHop(value) {
|
|
|
805
806
|
function freezeFailures(failures) {
|
|
806
807
|
return Object.freeze(failures.map((failure) => Object.freeze({ file: failure.file, name: failure.name })));
|
|
807
808
|
}
|
|
809
|
+
function isSessionStepReason(value) {
|
|
810
|
+
return SESSION_STEP_REASONS.includes(value);
|
|
811
|
+
}
|
|
812
|
+
function reasonEntry(reason) {
|
|
813
|
+
return isSessionStepReason(reason) ? { reason } : {};
|
|
814
|
+
}
|
|
808
815
|
function freezeStep(step) {
|
|
809
816
|
return Object.freeze({
|
|
810
817
|
kind: step.kind,
|
|
811
818
|
scope: typeof step.scope === "string" ? step.scope : Object.freeze([...step.scope]),
|
|
819
|
+
...reasonEntry(field(step, "reason")),
|
|
812
820
|
command: Object.freeze([...step.command]),
|
|
813
821
|
exitCode: step.exitCode,
|
|
814
822
|
summary: step.summary,
|
|
@@ -1469,6 +1477,20 @@ var SQLITE_MIGRATIONS = [
|
|
|
1469
1477
|
recorded_at TEXT NOT NULL CHECK (recorded_at <> '')
|
|
1470
1478
|
);
|
|
1471
1479
|
`
|
|
1480
|
+
},
|
|
1481
|
+
{
|
|
1482
|
+
id: "store-meta-generation",
|
|
1483
|
+
breaks: [],
|
|
1484
|
+
sql: `
|
|
1485
|
+
ALTER TABLE store_meta ADD COLUMN generation TEXT CHECK (generation <> '');
|
|
1486
|
+
`
|
|
1487
|
+
},
|
|
1488
|
+
{
|
|
1489
|
+
id: "session-worktree",
|
|
1490
|
+
breaks: [],
|
|
1491
|
+
sql: `
|
|
1492
|
+
ALTER TABLE sessions ADD COLUMN worktree TEXT;
|
|
1493
|
+
`
|
|
1472
1494
|
}
|
|
1473
1495
|
];
|
|
1474
1496
|
var SQLITE_SCHEMA_VERSION = SQLITE_MIGRATIONS.length;
|
|
@@ -1700,13 +1722,55 @@ function activeStoreSettings() {
|
|
|
1700
1722
|
}
|
|
1701
1723
|
|
|
1702
1724
|
// src/effort/store/store-meta.ts
|
|
1703
|
-
import { randomUUID as
|
|
1725
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
1704
1726
|
import { dirname as dirname9 } from "path";
|
|
1727
|
+
import { Database } from "bun:sqlite";
|
|
1728
|
+
|
|
1729
|
+
// src/effort/store/store-generation.ts
|
|
1730
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
1731
|
+
import { readFileSync as readFileSync6, renameSync as renameSync4, rmSync as rmSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
1732
|
+
var STORE_GENERATION_SUFFIX = ".generation";
|
|
1733
|
+
function storeGenerationPath(storePath) {
|
|
1734
|
+
return `${storePath}${STORE_GENERATION_SUFFIX}`;
|
|
1735
|
+
}
|
|
1736
|
+
function isMissingFile(error) {
|
|
1737
|
+
return error instanceof Error && "code" in error && error.code === "ENOENT";
|
|
1738
|
+
}
|
|
1739
|
+
function readStoreGeneration(storePath) {
|
|
1740
|
+
let text;
|
|
1741
|
+
try {
|
|
1742
|
+
text = readFileSync6(storeGenerationPath(storePath), "utf8");
|
|
1743
|
+
} catch (error) {
|
|
1744
|
+
if (isMissingFile(error))
|
|
1745
|
+
return null;
|
|
1746
|
+
throw error;
|
|
1747
|
+
}
|
|
1748
|
+
const generation = text.trim();
|
|
1749
|
+
return generation === "" ? null : generation;
|
|
1750
|
+
}
|
|
1751
|
+
function assertWritableGeneration(generation) {
|
|
1752
|
+
if (generation === "" || generation !== generation.trim() || /[\r\n]/.test(generation)) {
|
|
1753
|
+
throw new Error(`store generation must be non-empty, unpadded and on one line, got ${JSON.stringify(generation)}`);
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
function writeStoreGeneration(storePath, generation) {
|
|
1757
|
+
assertWritableGeneration(generation);
|
|
1758
|
+
const file = storeGenerationPath(storePath);
|
|
1759
|
+
const temporary = `${file}.${String(process.pid)}.${randomUUID3()}.tmp`;
|
|
1760
|
+
try {
|
|
1761
|
+
writeFileSync4(temporary, `${generation}
|
|
1762
|
+
`, { encoding: "utf8", flag: "wx" });
|
|
1763
|
+
renameSync4(temporary, file);
|
|
1764
|
+
} catch (error) {
|
|
1765
|
+
rmSync4(temporary, { force: true });
|
|
1766
|
+
throw error;
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1705
1769
|
|
|
1706
1770
|
// src/effort/store/store-identity.ts
|
|
1707
1771
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
1708
1772
|
import { createHmac } from "crypto";
|
|
1709
|
-
import { readFileSync as
|
|
1773
|
+
import { readFileSync as readFileSync7, realpathSync as realpathSync4, statSync } from "fs";
|
|
1710
1774
|
import { hostname, platform as currentPlatform } from "os";
|
|
1711
1775
|
|
|
1712
1776
|
// src/schema/project-id.ts
|
|
@@ -1744,7 +1808,7 @@ function hostIdFrom(source, value) {
|
|
|
1744
1808
|
}
|
|
1745
1809
|
function readMachineIdFile() {
|
|
1746
1810
|
try {
|
|
1747
|
-
const value =
|
|
1811
|
+
const value = readFileSync7(MACHINE_ID_PATH, "utf8").trim();
|
|
1748
1812
|
return value === "" ? null : value;
|
|
1749
1813
|
} catch {
|
|
1750
1814
|
return null;
|
|
@@ -1780,14 +1844,19 @@ function readStoreFileFacts(path) {
|
|
|
1780
1844
|
return { storePath, fileDev: stats.dev, fileIno: stats.ino };
|
|
1781
1845
|
}
|
|
1782
1846
|
function observeStore(path, readHost = readHostId) {
|
|
1783
|
-
|
|
1847
|
+
const fileFacts = readStoreFileFacts(path);
|
|
1848
|
+
return { hostId: readHost(), ...fileFacts, generation: readStoreGeneration(fileFacts.storePath) };
|
|
1849
|
+
}
|
|
1850
|
+
function staleGeneration(recorded, facts) {
|
|
1851
|
+
return recorded.generation !== null && recorded.generation !== facts.generation;
|
|
1784
1852
|
}
|
|
1785
1853
|
function movedFacts(recorded, facts) {
|
|
1786
1854
|
const sameFile = BigInt(recorded.fileDev) === facts.fileDev && BigInt(recorded.fileIno) === facts.fileIno;
|
|
1787
1855
|
const moved = [
|
|
1788
1856
|
["host", recorded.hostId !== facts.hostId],
|
|
1789
1857
|
["path", recorded.storePath !== facts.storePath],
|
|
1790
|
-
["file", !sameFile]
|
|
1858
|
+
["file", !sameFile],
|
|
1859
|
+
["generation", staleGeneration(recorded, facts)]
|
|
1791
1860
|
];
|
|
1792
1861
|
return moved.filter(([, differs]) => differs).map(([reason]) => reason);
|
|
1793
1862
|
}
|
|
@@ -1827,8 +1896,12 @@ function toStoredInteger(value) {
|
|
|
1827
1896
|
function fromStoredInteger(text) {
|
|
1828
1897
|
return BigInt.asUintN(STORED_INTEGER_BITS, BigInt(text));
|
|
1829
1898
|
}
|
|
1899
|
+
function hasGenerationColumn(db) {
|
|
1900
|
+
return db.query("SELECT count(*) AS n FROM pragma_table_info('store_meta') WHERE name = 'generation'").get()?.n === 1;
|
|
1901
|
+
}
|
|
1830
1902
|
function readStoreMeta(db) {
|
|
1831
|
-
const
|
|
1903
|
+
const generation = hasGenerationColumn(db) ? "generation" : "NULL AS generation";
|
|
1904
|
+
const row = db.query("SELECT store_id, project_root_commit, project_remote, host_id, store_path," + " CAST(file_dev AS TEXT) AS file_dev, CAST(file_ino AS TEXT) AS file_ino, minted_at," + ` ${generation} FROM store_meta WHERE id = 1`).get();
|
|
1832
1905
|
if (row === null)
|
|
1833
1906
|
return null;
|
|
1834
1907
|
return {
|
|
@@ -1839,7 +1912,8 @@ function readStoreMeta(db) {
|
|
|
1839
1912
|
storePath: row.store_path,
|
|
1840
1913
|
fileDev: fromStoredInteger(row.file_dev),
|
|
1841
1914
|
fileIno: fromStoredInteger(row.file_ino),
|
|
1842
|
-
mintedAt: row.minted_at
|
|
1915
|
+
mintedAt: row.minted_at,
|
|
1916
|
+
generation: row.generation
|
|
1843
1917
|
};
|
|
1844
1918
|
}
|
|
1845
1919
|
function projectFor(storePath, recorded, seams) {
|
|
@@ -1850,14 +1924,30 @@ function projectFor(storePath, recorded, seams) {
|
|
|
1850
1924
|
return null;
|
|
1851
1925
|
return { rootCommit: recorded.projectRootCommit, remote: recorded.projectRemote };
|
|
1852
1926
|
}
|
|
1853
|
-
function writeStoreMeta(db, facts, project, seams) {
|
|
1854
|
-
const storeId = (seams.newStoreId ??
|
|
1927
|
+
function writeStoreMeta(db, facts, project, generation, seams) {
|
|
1928
|
+
const storeId = (seams.newStoreId ?? randomUUID4)();
|
|
1855
1929
|
const mintedAt = (seams.now?.() ?? new Date).toISOString();
|
|
1856
|
-
db.query("INSERT OR REPLACE INTO store_meta (id, store_id, project_root_commit, project_remote," + " host_id, store_path, file_dev, file_ino, minted_at) VALUES (1, ?, ?, ?, ?, ?, ?, ?, ?)").run(storeId, project.rootCommit, project.remote, facts.hostId, facts.storePath, toStoredInteger(facts.fileDev), toStoredInteger(facts.fileIno), mintedAt);
|
|
1930
|
+
db.query("INSERT OR REPLACE INTO store_meta (id, store_id, project_root_commit, project_remote," + " host_id, store_path, file_dev, file_ino, minted_at, generation) VALUES (1, ?, ?, ?, ?, ?, ?, ?, ?, ?)").run(storeId, project.rootCommit, project.remote, facts.hostId, facts.storePath, toStoredInteger(facts.fileDev), toStoredInteger(facts.fileIno), mintedAt, generation);
|
|
1857
1931
|
return storeId;
|
|
1858
1932
|
}
|
|
1859
|
-
function
|
|
1860
|
-
|
|
1933
|
+
function settleUnderLock(db, observe, prefetched, seams) {
|
|
1934
|
+
const current = readStoreMeta(db);
|
|
1935
|
+
const facts = observe();
|
|
1936
|
+
const decision = decideStoreIdentity("write", current, () => facts);
|
|
1937
|
+
if (decision.action === "none")
|
|
1938
|
+
return { action: "none" };
|
|
1939
|
+
const generation = (seams.newGeneration ?? randomUUID4)();
|
|
1940
|
+
if (decision.action === "keep") {
|
|
1941
|
+
db.query("UPDATE store_meta SET generation = ? WHERE id = 1").run(generation);
|
|
1942
|
+
writeStoreGeneration(facts.storePath, generation);
|
|
1943
|
+
return { action: "keep", storeId: decision.storeId };
|
|
1944
|
+
}
|
|
1945
|
+
const project = prefetched ?? projectFor(facts.storePath, current, seams);
|
|
1946
|
+
if (project === null)
|
|
1947
|
+
return { action: "no-project", reasons: decision.reasons };
|
|
1948
|
+
const storeId = writeStoreMeta(db, facts, project, generation, seams);
|
|
1949
|
+
writeStoreGeneration(facts.storePath, generation);
|
|
1950
|
+
return { action: "mint", storeId, reasons: decision.reasons };
|
|
1861
1951
|
}
|
|
1862
1952
|
function settleStoreIdentity(db, path, access, seams = {}) {
|
|
1863
1953
|
if (access === "read")
|
|
@@ -1865,19 +1955,72 @@ function settleStoreIdentity(db, path, access, seams = {}) {
|
|
|
1865
1955
|
const observe = () => observeStore(path, seams.readHostId ?? readHostId);
|
|
1866
1956
|
const recorded = readStoreMeta(db);
|
|
1867
1957
|
const first = decideStoreIdentity(access, recorded, observe);
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
const project = projectFor(first.facts.storePath, recorded, seams);
|
|
1871
|
-
if (project === null)
|
|
1958
|
+
const prefetched = first.action === "mint" ? projectFor(first.facts.storePath, recorded, seams) : undefined;
|
|
1959
|
+
if (first.action === "mint" && prefetched === null)
|
|
1872
1960
|
return { action: "no-project", reasons: first.reasons };
|
|
1873
|
-
const
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1961
|
+
const underLock = db.transaction(() => settleUnderLock(db, observe, prefetched ?? undefined, seams));
|
|
1962
|
+
return underLock.immediate();
|
|
1963
|
+
}
|
|
1964
|
+
function hasStoreMetaTable(db) {
|
|
1965
|
+
return db.query("SELECT count(*) AS n FROM sqlite_master WHERE type = 'table' AND name = 'store_meta'").get()?.n === 1;
|
|
1966
|
+
}
|
|
1967
|
+
function decideLiveWrite(livePath, readHost) {
|
|
1968
|
+
const live = new Database(livePath, { readonly: true });
|
|
1969
|
+
let recorded;
|
|
1970
|
+
try {
|
|
1971
|
+
if (!hasStoreMetaTable(live))
|
|
1972
|
+
return { action: "no-table" };
|
|
1973
|
+
recorded = readStoreMeta(live);
|
|
1974
|
+
} finally {
|
|
1975
|
+
live.close();
|
|
1976
|
+
}
|
|
1977
|
+
if (recorded === null)
|
|
1978
|
+
return { action: "no-row" };
|
|
1979
|
+
const facts = observeStore(livePath, readHost);
|
|
1980
|
+
const decision = decideStoreIdentity("write", recorded, () => facts);
|
|
1981
|
+
if (decision.action === "keep")
|
|
1982
|
+
return { action: "keep", storeId: decision.storeId };
|
|
1983
|
+
const reasons = decision.action === "mint" ? decision.reasons : [];
|
|
1984
|
+
return { action: "mint", reasons };
|
|
1985
|
+
}
|
|
1986
|
+
function holdsRowOf(db, storeId) {
|
|
1987
|
+
return hasStoreMetaTable(db) && readStoreMeta(db)?.storeId === storeId;
|
|
1988
|
+
}
|
|
1989
|
+
function spoilGeneration(db, newGeneration) {
|
|
1990
|
+
if (!hasStoreMetaTable(db) || !hasGenerationColumn(db))
|
|
1991
|
+
return null;
|
|
1992
|
+
const rows = db.query("SELECT count(*) AS n FROM store_meta WHERE id = 1").get()?.n;
|
|
1993
|
+
if (rows !== 1)
|
|
1994
|
+
return null;
|
|
1995
|
+
const generation = newGeneration();
|
|
1996
|
+
db.query("UPDATE store_meta SET generation = ? WHERE id = 1").run(generation);
|
|
1997
|
+
return generation;
|
|
1998
|
+
}
|
|
1999
|
+
function writeCarried(db, storeId, parallel, newGeneration) {
|
|
2000
|
+
const dev = toStoredInteger(parallel.fileDev);
|
|
2001
|
+
const ino = toStoredInteger(parallel.fileIno);
|
|
2002
|
+
if (hasGenerationColumn(db)) {
|
|
2003
|
+
const generation = newGeneration();
|
|
2004
|
+
db.query("UPDATE store_meta SET file_dev = ?, file_ino = ?, generation = ? WHERE id = 1").run(dev, ino, generation);
|
|
2005
|
+
return { action: "carried", storeId, generation };
|
|
2006
|
+
}
|
|
2007
|
+
db.query("UPDATE store_meta SET file_dev = ?, file_ino = ? WHERE id = 1").run(dev, ino);
|
|
2008
|
+
return { action: "carried", storeId, generation: null };
|
|
2009
|
+
}
|
|
2010
|
+
function carryStoreIdentity(livePath, parallelPath, seams = {}) {
|
|
2011
|
+
const parallel = readStoreFileFacts(parallelPath);
|
|
2012
|
+
const live = decideLiveWrite(livePath, seams.readHostId ?? readHostId);
|
|
2013
|
+
const newGeneration = seams.newGeneration ?? randomUUID4;
|
|
2014
|
+
const db = new Database(parallelPath, { readwrite: true });
|
|
2015
|
+
try {
|
|
2016
|
+
if (live.action === "keep" && holdsRowOf(db, live.storeId)) {
|
|
2017
|
+
return writeCarried(db, live.storeId, parallel, newGeneration);
|
|
2018
|
+
}
|
|
2019
|
+
const spoiled = spoilGeneration(db, newGeneration);
|
|
2020
|
+
return live.action === "keep" ? { action: "other-row", spoiled } : { ...live, spoiled };
|
|
2021
|
+
} finally {
|
|
2022
|
+
db.close();
|
|
2023
|
+
}
|
|
1881
2024
|
}
|
|
1882
2025
|
|
|
1883
2026
|
// src/effort/store/sqlite.ts
|
|
@@ -1886,8 +2029,12 @@ function sqliteStorePath(repoRoot) {
|
|
|
1886
2029
|
return join9(effortStoreDir(repoRoot), SQLITE_STORE_FILE_NAME);
|
|
1887
2030
|
}
|
|
1888
2031
|
var KIND_TABLES = {
|
|
1889
|
-
sessions: {
|
|
1890
|
-
|
|
2032
|
+
sessions: {
|
|
2033
|
+
name: "sessions",
|
|
2034
|
+
keyColumn: "session_id",
|
|
2035
|
+
fieldColumns: [{ column: "worktree", value: (row) => row.worktree ?? null }]
|
|
2036
|
+
},
|
|
2037
|
+
commits: { name: "commits", keyColumn: "sha", fieldColumns: [] }
|
|
1891
2038
|
};
|
|
1892
2039
|
var LONE_SURROGATE = /[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/;
|
|
1893
2040
|
function hasKey(entry) {
|
|
@@ -1898,6 +2045,7 @@ function refused(index, length, reason) {
|
|
|
1898
2045
|
}
|
|
1899
2046
|
function batchEntries(kind, rows) {
|
|
1900
2047
|
const keyOf = EFFORT_KEY_PROJECTIONS[kind];
|
|
2048
|
+
const fieldColumns = KIND_TABLES[kind].fieldColumns;
|
|
1901
2049
|
const projected = rows.map((row) => ({ key: keyOf(row), row }));
|
|
1902
2050
|
const keyed = projected.filter(hasKey);
|
|
1903
2051
|
if (keyed.length < projected.length) {
|
|
@@ -1909,13 +2057,17 @@ function batchEntries(kind, rows) {
|
|
|
1909
2057
|
const reason = "carries a key with a lone UTF-16 surrogate," + " which SQLite text cannot hold";
|
|
1910
2058
|
throw refused(unholdableAt, rows.length, reason);
|
|
1911
2059
|
}
|
|
1912
|
-
return keyed.map(({ key, row }) => ({
|
|
2060
|
+
return keyed.map(({ key, row }) => ({
|
|
2061
|
+
key,
|
|
2062
|
+
body: JSON.stringify(row),
|
|
2063
|
+
fields: fieldColumns.map(({ value }) => value(row))
|
|
2064
|
+
}));
|
|
1913
2065
|
}
|
|
1914
2066
|
function withSqliteStore(path, access, create, use, identity = {}) {
|
|
1915
2067
|
guardTestProcess(path);
|
|
1916
2068
|
if (create)
|
|
1917
2069
|
mkdirSync5(dirname10(path), { recursive: true });
|
|
1918
|
-
const db = new
|
|
2070
|
+
const db = new Database2(path, { readwrite: true, create });
|
|
1919
2071
|
try {
|
|
1920
2072
|
db.run(`PRAGMA busy_timeout = ${String(activeStoreSettings().busyTimeoutMs)}`);
|
|
1921
2073
|
bringForward(db, path, access, "open");
|
|
@@ -1926,8 +2078,11 @@ function withSqliteStore(path, access, create, use, identity = {}) {
|
|
|
1926
2078
|
}
|
|
1927
2079
|
}
|
|
1928
2080
|
function insertBatch(db, table, entries) {
|
|
1929
|
-
const
|
|
1930
|
-
const
|
|
2081
|
+
const fieldNames = table.fieldColumns.map(({ column }) => column);
|
|
2082
|
+
const columns = [table.keyColumn, "row_json", ...fieldNames].join(", ");
|
|
2083
|
+
const placeholders = ["?", "?", ...fieldNames.map(() => "?")].join(", ");
|
|
2084
|
+
const insert = db.query(`INSERT INTO ${table.name} (${columns}, ${STAMPED_COLUMNS})` + ` VALUES (${placeholders}, ${stampedValues(table.name)})` + ` ON CONFLICT (${table.keyColumn}) DO NOTHING`);
|
|
2085
|
+
const insertAll = db.transaction(() => entries.reduce((appended, { key, body, fields }) => appended + insert.run(key, body, ...fields).changes, 0));
|
|
1931
2086
|
return insertAll.immediate();
|
|
1932
2087
|
}
|
|
1933
2088
|
function writeSqliteStore(path, rowCount, empty, insert) {
|
|
@@ -1990,15 +2145,105 @@ import { existsSync as existsSync12 } from "fs";
|
|
|
1990
2145
|
import { join as join12, resolve as resolve5 } from "path";
|
|
1991
2146
|
|
|
1992
2147
|
// src/effort/store/fix-schema.ts
|
|
1993
|
-
import { existsSync as
|
|
1994
|
-
import { Database as
|
|
2148
|
+
import { existsSync as existsSync8 } from "fs";
|
|
2149
|
+
import { Database as Database4 } from "bun:sqlite";
|
|
1995
2150
|
|
|
1996
2151
|
// src/effort/store/rebuild-aside.ts
|
|
1997
|
-
import { existsSync as
|
|
2152
|
+
import { closeSync, existsSync as existsSync7, fsyncSync, openSync, renameSync as renameSync5, rmSync as rmSync6 } from "fs";
|
|
2153
|
+
|
|
2154
|
+
// src/effort/store/copy.ts
|
|
2155
|
+
import { copyFileSync, constants, existsSync as existsSync6, mkdirSync as mkdirSync6, readdirSync as readdirSync2, rmSync as rmSync5, statSync as statSync2 } from "fs";
|
|
2156
|
+
import { join as join10, resolve as resolve3 } from "path";
|
|
2157
|
+
import { Database as Database3 } from "bun:sqlite";
|
|
2158
|
+
var NDJSON_KINDS = { sessions: true, commits: true };
|
|
2159
|
+
var EFFORT_COPY_FILE_NAMES = [
|
|
2160
|
+
SQLITE_STORE_FILE_NAME,
|
|
2161
|
+
...Object.keys(NDJSON_KINDS).map((kind) => effortStoreFileName(kind))
|
|
2162
|
+
];
|
|
2163
|
+
|
|
2164
|
+
class EffortCopyRefusal extends Error {
|
|
2165
|
+
name = "EffortCopyRefusal";
|
|
2166
|
+
}
|
|
1998
2167
|
|
|
2168
|
+
class EffortCopyFailure extends Error {
|
|
2169
|
+
name = "EffortCopyFailure";
|
|
2170
|
+
}
|
|
2171
|
+
function refuseUsedTarget(target) {
|
|
2172
|
+
if (!existsSync6(target))
|
|
2173
|
+
return;
|
|
2174
|
+
let entries;
|
|
2175
|
+
try {
|
|
2176
|
+
if (!statSync2(target).isDirectory()) {
|
|
2177
|
+
throw new EffortCopyRefusal(`${target} is a file, not a directory; name an empty or absent directory with --to=<dir>.`);
|
|
2178
|
+
}
|
|
2179
|
+
entries = readdirSync2(target);
|
|
2180
|
+
} catch (error) {
|
|
2181
|
+
if (error instanceof EffortCopyRefusal)
|
|
2182
|
+
throw error;
|
|
2183
|
+
throw new EffortCopyFailure(`${target} cannot be read: ${String(error)}`);
|
|
2184
|
+
}
|
|
2185
|
+
if (entries.length === 0)
|
|
2186
|
+
return;
|
|
2187
|
+
throw new EffortCopyRefusal(`${target} is not empty (${String(entries.length)} entries); nothing was copied.` + " Name an empty or absent directory with --to=<dir>.");
|
|
2188
|
+
}
|
|
2189
|
+
function vacuumInto(source, target) {
|
|
2190
|
+
const db = new Database3(source, { readonly: true });
|
|
2191
|
+
try {
|
|
2192
|
+
db.run(`PRAGMA busy_timeout = ${String(activeStoreSettings().busyTimeoutMs)}`);
|
|
2193
|
+
db.run("VACUUM INTO ?", [target]);
|
|
2194
|
+
} finally {
|
|
2195
|
+
db.close();
|
|
2196
|
+
}
|
|
2197
|
+
}
|
|
2198
|
+
function copyOne(name2, source, target) {
|
|
2199
|
+
if (name2 === SQLITE_STORE_FILE_NAME) {
|
|
2200
|
+
vacuumInto(join10(source, name2), join10(target, name2));
|
|
2201
|
+
return;
|
|
2202
|
+
}
|
|
2203
|
+
copyFileSync(join10(source, name2), join10(target, name2), constants.COPYFILE_EXCL);
|
|
2204
|
+
}
|
|
2205
|
+
function undo(target, written, madeDir) {
|
|
2206
|
+
try {
|
|
2207
|
+
if (madeDir === undefined) {
|
|
2208
|
+
for (const name2 of written)
|
|
2209
|
+
rmSync5(join10(target, name2), { force: true });
|
|
2210
|
+
} else {
|
|
2211
|
+
rmSync5(madeDir, { recursive: true, force: true });
|
|
2212
|
+
}
|
|
2213
|
+
return "";
|
|
2214
|
+
} catch (error) {
|
|
2215
|
+
return ` Removing what it made failed too: ${String(error)}`;
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
2218
|
+
function copyEffortStore(request) {
|
|
2219
|
+
const source = resolve3(request.source);
|
|
2220
|
+
const target = resolve3(request.target);
|
|
2221
|
+
guardTestProcess(source);
|
|
2222
|
+
guardTestProcess(target);
|
|
2223
|
+
const files = EFFORT_COPY_FILE_NAMES.filter((name2) => existsSync6(join10(source, name2)));
|
|
2224
|
+
if (files.length === 0) {
|
|
2225
|
+
throw new EffortCopyRefusal(`no effort store at ${source}: it holds none of ${EFFORT_COPY_FILE_NAMES.join(", ")}. Nothing to copy.`);
|
|
2226
|
+
}
|
|
2227
|
+
refuseUsedTarget(target);
|
|
2228
|
+
const written = [];
|
|
2229
|
+
let madeDir;
|
|
2230
|
+
try {
|
|
2231
|
+
madeDir = mkdirSync6(target, { recursive: true });
|
|
2232
|
+
for (const name2 of files) {
|
|
2233
|
+
written.push(name2);
|
|
2234
|
+
copyOne(name2, source, target);
|
|
2235
|
+
}
|
|
2236
|
+
} catch (error) {
|
|
2237
|
+
const left = undo(target, written, madeDir);
|
|
2238
|
+
throw new EffortCopyFailure(`copying ${source} to ${target} failed: ${String(error)}.${left}`);
|
|
2239
|
+
}
|
|
2240
|
+
return Object.freeze({ source, directory: target, files: Object.freeze(files) });
|
|
2241
|
+
}
|
|
2242
|
+
|
|
2243
|
+
// src/effort/store/rebuild-aside.ts
|
|
1999
2244
|
class RebuildRefusal extends Error {
|
|
2000
|
-
constructor(message) {
|
|
2001
|
-
super(message);
|
|
2245
|
+
constructor(message, options) {
|
|
2246
|
+
super(message, options);
|
|
2002
2247
|
this.name = "RebuildRefusal";
|
|
2003
2248
|
}
|
|
2004
2249
|
}
|
|
@@ -2010,7 +2255,7 @@ function count(db, sql) {
|
|
|
2010
2255
|
}
|
|
2011
2256
|
function refuseInFlight(path) {
|
|
2012
2257
|
for (const suffix of ["-journal", "-wal"]) {
|
|
2013
|
-
if (
|
|
2258
|
+
if (existsSync7(`${path}${suffix}`)) {
|
|
2014
2259
|
throw new RebuildRefusal(`${path}${suffix} is there, so a write is in flight or was interrupted; stop whatever uses the store` + " and open it once with rafa before repairing it");
|
|
2015
2260
|
}
|
|
2016
2261
|
}
|
|
@@ -2033,21 +2278,78 @@ function refuseCorrupt(db) {
|
|
|
2033
2278
|
throw new RebuildRefusal(`the rebuilt store failed integrity_check: ${answer ?? "no answer"}`);
|
|
2034
2279
|
}
|
|
2035
2280
|
function removeParallel(parallelPath) {
|
|
2036
|
-
|
|
2037
|
-
|
|
2281
|
+
rmSync6(parallelPath, { force: true });
|
|
2282
|
+
rmSync6(`${parallelPath}-journal`, { force: true });
|
|
2283
|
+
}
|
|
2284
|
+
var STEP_TEXT = {
|
|
2285
|
+
backup: ({ path, backupPath }) => ({
|
|
2286
|
+
doing: `writing ${path} out to the backup ${backupPath}`,
|
|
2287
|
+
backup: `any part of the backup written at ${backupPath}`
|
|
2288
|
+
}),
|
|
2289
|
+
carry: ({ path, parallelPath, backupPath }) => ({
|
|
2290
|
+
doing: `carrying the store id of ${path} onto ${parallelPath}`,
|
|
2291
|
+
backup: `the whole backup at ${backupPath}`
|
|
2292
|
+
}),
|
|
2293
|
+
record: ({ path, parallelPath, backupPath }) => ({
|
|
2294
|
+
doing: `writing the generation carried onto ${parallelPath} to the side record of ${path}`,
|
|
2295
|
+
backup: `the whole backup at ${backupPath}`
|
|
2296
|
+
}),
|
|
2297
|
+
rename: ({ path, parallelPath, backupPath }) => ({
|
|
2298
|
+
doing: `renaming ${parallelPath} over ${path}`,
|
|
2299
|
+
backup: `the whole backup at ${backupPath}`
|
|
2300
|
+
})
|
|
2301
|
+
};
|
|
2302
|
+
|
|
2303
|
+
class SwapFailure extends RebuildRefusal {
|
|
2304
|
+
step;
|
|
2305
|
+
parallelPath;
|
|
2306
|
+
backupPath;
|
|
2307
|
+
constructor(step, files, cause) {
|
|
2308
|
+
const { doing, backup } = STEP_TEXT[step](files);
|
|
2309
|
+
const reason = cause instanceof Error ? cause.message : String(cause);
|
|
2310
|
+
super(`${doing} failed (${reason}), so ${files.path} was not replaced and is unchanged.` + ` The rebuilt store is left at ${files.parallelPath}, and ${backup}.`, { cause });
|
|
2311
|
+
this.name = "SwapFailure";
|
|
2312
|
+
this.step = step;
|
|
2313
|
+
this.parallelPath = files.parallelPath;
|
|
2314
|
+
this.backupPath = files.backupPath;
|
|
2315
|
+
}
|
|
2316
|
+
}
|
|
2317
|
+
function writeBackup(path, backupPath) {
|
|
2318
|
+
vacuumInto(path, backupPath);
|
|
2319
|
+
const backup = openSync(backupPath, "r");
|
|
2320
|
+
try {
|
|
2321
|
+
fsyncSync(backup);
|
|
2322
|
+
} finally {
|
|
2323
|
+
closeSync(backup);
|
|
2324
|
+
}
|
|
2038
2325
|
}
|
|
2039
|
-
|
|
2040
|
-
|
|
2326
|
+
var SWAP_STEPS = {
|
|
2327
|
+
backup: writeBackup,
|
|
2328
|
+
carry: (path, parallelPath) => carryStoreIdentity(path, parallelPath),
|
|
2329
|
+
record: writeStoreGeneration,
|
|
2330
|
+
rename: renameSync5
|
|
2331
|
+
};
|
|
2332
|
+
function runStep(step, files, act) {
|
|
2041
2333
|
try {
|
|
2042
|
-
|
|
2334
|
+
return act();
|
|
2043
2335
|
} catch (error) {
|
|
2044
|
-
|
|
2045
|
-
|
|
2336
|
+
throw new SwapFailure(step, files, error);
|
|
2337
|
+
}
|
|
2338
|
+
}
|
|
2339
|
+
function swapIn(path, parallelPath, backupPath, steps = {}) {
|
|
2340
|
+
const { backup, carry, record, rename } = { ...SWAP_STEPS, ...steps };
|
|
2341
|
+
const files = { path, parallelPath, backupPath };
|
|
2342
|
+
runStep("backup", files, () => backup(path, backupPath));
|
|
2343
|
+
const carried = runStep("carry", files, () => carry(path, parallelPath));
|
|
2344
|
+
if (carried.action === "carried" && carried.generation !== null) {
|
|
2345
|
+
const { generation } = carried;
|
|
2346
|
+
runStep("record", files, () => record(path, generation));
|
|
2046
2347
|
}
|
|
2348
|
+
runStep("rename", files, () => rename(parallelPath, path));
|
|
2047
2349
|
}
|
|
2048
2350
|
function refuseTaken(paths) {
|
|
2049
2351
|
for (const taken of paths) {
|
|
2050
|
-
if (
|
|
2352
|
+
if (existsSync7(taken))
|
|
2051
2353
|
throw new RebuildRefusal(`${taken} is already there; move it aside or pass another stamp`);
|
|
2052
2354
|
}
|
|
2053
2355
|
}
|
|
@@ -2086,7 +2388,7 @@ function columnNames(db, schema, table) {
|
|
|
2086
2388
|
return db.query("SELECT name FROM pragma_table_info(?, ?)").all(table, schema).map((row) => row.name);
|
|
2087
2389
|
}
|
|
2088
2390
|
function readOnlySchema(path) {
|
|
2089
|
-
const db = new
|
|
2391
|
+
const db = new Database4(path, { readonly: true });
|
|
2090
2392
|
try {
|
|
2091
2393
|
return readStoreSchema(db, path);
|
|
2092
2394
|
} finally {
|
|
@@ -2134,7 +2436,7 @@ function refuseUnusable(db, parallelPath, migrations) {
|
|
|
2134
2436
|
throw new RebuildRefusal(`the rebuilt store still needs ${needs.join(", ")}`);
|
|
2135
2437
|
}
|
|
2136
2438
|
function buildParallel(path, parallelPath, migrations, stamp) {
|
|
2137
|
-
const db = new
|
|
2439
|
+
const db = new Database4(parallelPath, { create: true, readwrite: true });
|
|
2138
2440
|
try {
|
|
2139
2441
|
bringForward(db, parallelPath, "write", "open", { migrations, ...stamp, builtAside: true });
|
|
2140
2442
|
db.run(`ATTACH DATABASE ? AS ${LIVE}`, [path]);
|
|
@@ -2171,7 +2473,7 @@ function fixStoreSchema(options) {
|
|
|
2171
2473
|
const appliedBy = appliedByName(identity);
|
|
2172
2474
|
const known = migrations.map(({ id }) => id);
|
|
2173
2475
|
const nothing = { known, appliedBy, pending: [], kept: [], leftTables: [], leftColumns: [], backupPath: null };
|
|
2174
|
-
if (!
|
|
2476
|
+
if (!existsSync8(path)) {
|
|
2175
2477
|
return { path, status: "missing", storeVersion: null, logged: false, reason: null, refusal: null, unknown: [], ...nothing };
|
|
2176
2478
|
}
|
|
2177
2479
|
refuseInFlight(path);
|
|
@@ -2213,19 +2515,19 @@ function withoutWayOut(message, nextStep) {
|
|
|
2213
2515
|
}
|
|
2214
2516
|
|
|
2215
2517
|
// src/commands/plan/plan-files.ts
|
|
2216
|
-
import { statSync as
|
|
2217
|
-
import { resolve as
|
|
2518
|
+
import { statSync as statSync3 } from "fs";
|
|
2519
|
+
import { resolve as resolve4 } from "path";
|
|
2218
2520
|
|
|
2219
2521
|
// src/config-load.ts
|
|
2220
|
-
import { existsSync as
|
|
2522
|
+
import { existsSync as existsSync9, readFileSync as readFileSync8, realpathSync as realpathSync5 } from "fs";
|
|
2221
2523
|
import { isAbsolute as isAbsolute6 } from "path";
|
|
2222
2524
|
function readConfigFile(root) {
|
|
2223
2525
|
const path = configFilePath(root);
|
|
2224
|
-
if (!
|
|
2526
|
+
if (!existsSync9(path))
|
|
2225
2527
|
return null;
|
|
2226
2528
|
let text;
|
|
2227
2529
|
try {
|
|
2228
|
-
text =
|
|
2530
|
+
text = readFileSync8(path, "utf8");
|
|
2229
2531
|
} catch (error) {
|
|
2230
2532
|
const problem = `${path}: cannot be read (${messageOf(error)})`;
|
|
2231
2533
|
throw new ConfigError([problem], { cause: error });
|
|
@@ -2262,7 +2564,7 @@ function loadConfig(roots, cli = {}, warn = printWarning) {
|
|
|
2262
2564
|
|
|
2263
2565
|
// src/commands/plan/plan-files.ts
|
|
2264
2566
|
function plansDirAt(root, planDir) {
|
|
2265
|
-
return { label: planDir, path:
|
|
2567
|
+
return { label: planDir, path: resolve4(root, planDir) };
|
|
2266
2568
|
}
|
|
2267
2569
|
function requireProject(context, command) {
|
|
2268
2570
|
if (context.project === null)
|
|
@@ -2309,7 +2611,7 @@ function stubOfPlanFile(name2) {
|
|
|
2309
2611
|
return stub !== undefined && isStampableStub(stub) ? stub : null;
|
|
2310
2612
|
}
|
|
2311
2613
|
function isFile(path) {
|
|
2312
|
-
return
|
|
2614
|
+
return statSync3(path, { throwIfNoEntry: false })?.isFile() ?? false;
|
|
2313
2615
|
}
|
|
2314
2616
|
function issueLine(file, issue) {
|
|
2315
2617
|
return `${file}:${issue.line}: ${issue.reason}: ${issue.text}`;
|
|
@@ -2355,6 +2657,9 @@ var DRY_RUN_FLAG = "dry-run";
|
|
|
2355
2657
|
function fileStamp(date) {
|
|
2356
2658
|
return date.toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z");
|
|
2357
2659
|
}
|
|
2660
|
+
function keepsIdLine(swapped) {
|
|
2661
|
+
return `The ${swapped} keeps the store's id; the backup is a copy, so renamed back it takes a new id on its` + " next write.";
|
|
2662
|
+
}
|
|
2358
2663
|
function refuseLiveLoop(root, isAlive) {
|
|
2359
2664
|
let records;
|
|
2360
2665
|
try {
|
|
@@ -2412,7 +2717,7 @@ function renderFixSchema(result) {
|
|
|
2412
2717
|
case "rebuilt":
|
|
2413
2718
|
return [
|
|
2414
2719
|
...rebuildLines(result),
|
|
2415
|
-
`\u2705 Rebuilt at this rafa's migrations. The original is kept whole at ${String(result.backupPath)};` +
|
|
2720
|
+
`\u2705 Rebuilt at this rafa's migrations. The original is kept whole at ${String(result.backupPath)};` + ` rename it back to undo. ${keepsIdLine("rebuild")}`
|
|
2416
2721
|
];
|
|
2417
2722
|
}
|
|
2418
2723
|
}
|
|
@@ -2452,7 +2757,7 @@ function createFixSchemaCommand(seams = {}) {
|
|
|
2452
2757
|
subject: "effort",
|
|
2453
2758
|
action: "fix-schema",
|
|
2454
2759
|
summary: "rebuild an effort store this rafa refuses at the migrations it knows, keeping the original",
|
|
2455
|
-
description: "Repairs `.rafa/effort/effort.sqlite`, or the store under `RAFA_EFFORT_DIR`, when this rafa refuses" + " it for a reason a rebuild repairs: no migration log past the legacy entries (pre-log-unreleased), a log" + " with a schema version it does not match (gate-mismatch), a logged checksum this rafa holds otherwise" + " (edited), or a logged migration this rafa does not know that breaks older readers or writers" + " (unknown-breaks-readers, unknown-breaks-writers). Installing a rafa that knows the store's migrations" + " comes first where one exists. It builds a parallel store beside it at this rafa's migrations, with a" + " migration log naming this rafa, copies every table and column this rafa knows, checks the row counts," + " SQLite's integrity_check and the schema plan, and lists what only the newer schema holds, unknown" + " migrations included. Then it
|
|
2760
|
+
description: "Repairs `.rafa/effort/effort.sqlite`, or the store under `RAFA_EFFORT_DIR`, when this rafa refuses" + " it for a reason a rebuild repairs: no migration log past the legacy entries (pre-log-unreleased), a log" + " with a schema version it does not match (gate-mismatch), a logged checksum this rafa holds otherwise" + " (edited), or a logged migration this rafa does not know that breaks older readers or writers" + " (unknown-breaks-readers, unknown-breaks-writers). Installing a rafa that knows the store's migrations" + " comes first where one exists. It builds a parallel store beside it at this rafa's migrations, with a" + " migration log naming this rafa, copies every table and column this rafa knows, checks the row counts," + " SQLite's integrity_check and the schema plan, and lists what only the newer schema holds, unknown" + " migrations included. Then it writes the original to `effort.sqlite.v<user_version>-<stamp>.bak`," + " whole, and moves the rebuild into its place. The rebuild keeps the store's id; the backup is a copy," + " so renamed back it takes a new id on its next write. A store this rafa uses, current or behind, or no store, is" + " left alone. It refuses, changing nothing, while a loop session under the project is running or paused," + " from a development build, while a journal beside the store shows a write in flight, when the newer" + " schema lacks a table or column this rafa writes, and on a refusal a rebuild does not repair. With" + " `--output=json` the outcome is the data of the terminal result event. Starts no session.",
|
|
2456
2761
|
args: [],
|
|
2457
2762
|
flags: [
|
|
2458
2763
|
{
|
|
@@ -2481,101 +2786,12 @@ function createFixSchemaCommand(seams = {}) {
|
|
|
2481
2786
|
}
|
|
2482
2787
|
var fix_schema_default = createFixSchemaCommand();
|
|
2483
2788
|
|
|
2484
|
-
// src/effort/store/copy.ts
|
|
2485
|
-
import { copyFileSync, constants, existsSync as existsSync9, mkdirSync as mkdirSync6, readdirSync as readdirSync2, rmSync as rmSync5, statSync as statSync3 } from "fs";
|
|
2486
|
-
import { join as join10, resolve as resolve4 } from "path";
|
|
2487
|
-
import { Database as Database3 } from "bun:sqlite";
|
|
2488
|
-
var NDJSON_KINDS = { sessions: true, commits: true };
|
|
2489
|
-
var EFFORT_COPY_FILE_NAMES = [
|
|
2490
|
-
SQLITE_STORE_FILE_NAME,
|
|
2491
|
-
...Object.keys(NDJSON_KINDS).map((kind) => effortStoreFileName(kind))
|
|
2492
|
-
];
|
|
2493
|
-
|
|
2494
|
-
class EffortCopyRefusal extends Error {
|
|
2495
|
-
name = "EffortCopyRefusal";
|
|
2496
|
-
}
|
|
2497
|
-
|
|
2498
|
-
class EffortCopyFailure extends Error {
|
|
2499
|
-
name = "EffortCopyFailure";
|
|
2500
|
-
}
|
|
2501
|
-
function refuseUsedTarget(target) {
|
|
2502
|
-
if (!existsSync9(target))
|
|
2503
|
-
return;
|
|
2504
|
-
let entries;
|
|
2505
|
-
try {
|
|
2506
|
-
if (!statSync3(target).isDirectory()) {
|
|
2507
|
-
throw new EffortCopyRefusal(`${target} is a file, not a directory; name an empty or absent directory with --to=<dir>.`);
|
|
2508
|
-
}
|
|
2509
|
-
entries = readdirSync2(target);
|
|
2510
|
-
} catch (error) {
|
|
2511
|
-
if (error instanceof EffortCopyRefusal)
|
|
2512
|
-
throw error;
|
|
2513
|
-
throw new EffortCopyFailure(`${target} cannot be read: ${String(error)}`);
|
|
2514
|
-
}
|
|
2515
|
-
if (entries.length === 0)
|
|
2516
|
-
return;
|
|
2517
|
-
throw new EffortCopyRefusal(`${target} is not empty (${String(entries.length)} entries); nothing was copied.` + " Name an empty or absent directory with --to=<dir>.");
|
|
2518
|
-
}
|
|
2519
|
-
function vacuumInto(source, target) {
|
|
2520
|
-
const db = new Database3(source, { readonly: true });
|
|
2521
|
-
try {
|
|
2522
|
-
db.run(`PRAGMA busy_timeout = ${String(activeStoreSettings().busyTimeoutMs)}`);
|
|
2523
|
-
db.run("VACUUM INTO ?", [target]);
|
|
2524
|
-
} finally {
|
|
2525
|
-
db.close();
|
|
2526
|
-
}
|
|
2527
|
-
}
|
|
2528
|
-
function copyOne(name2, source, target) {
|
|
2529
|
-
if (name2 === SQLITE_STORE_FILE_NAME) {
|
|
2530
|
-
vacuumInto(join10(source, name2), join10(target, name2));
|
|
2531
|
-
return;
|
|
2532
|
-
}
|
|
2533
|
-
copyFileSync(join10(source, name2), join10(target, name2), constants.COPYFILE_EXCL);
|
|
2534
|
-
}
|
|
2535
|
-
function undo(target, written, madeDir) {
|
|
2536
|
-
try {
|
|
2537
|
-
if (madeDir === undefined) {
|
|
2538
|
-
for (const name2 of written)
|
|
2539
|
-
rmSync5(join10(target, name2), { force: true });
|
|
2540
|
-
} else {
|
|
2541
|
-
rmSync5(madeDir, { recursive: true, force: true });
|
|
2542
|
-
}
|
|
2543
|
-
return "";
|
|
2544
|
-
} catch (error) {
|
|
2545
|
-
return ` Removing what it made failed too: ${String(error)}`;
|
|
2546
|
-
}
|
|
2547
|
-
}
|
|
2548
|
-
function copyEffortStore(request) {
|
|
2549
|
-
const source = resolve4(request.source);
|
|
2550
|
-
const target = resolve4(request.target);
|
|
2551
|
-
guardTestProcess(source);
|
|
2552
|
-
guardTestProcess(target);
|
|
2553
|
-
const files = EFFORT_COPY_FILE_NAMES.filter((name2) => existsSync9(join10(source, name2)));
|
|
2554
|
-
if (files.length === 0) {
|
|
2555
|
-
throw new EffortCopyRefusal(`no effort store at ${source}: it holds none of ${EFFORT_COPY_FILE_NAMES.join(", ")}. Nothing to copy.`);
|
|
2556
|
-
}
|
|
2557
|
-
refuseUsedTarget(target);
|
|
2558
|
-
const written = [];
|
|
2559
|
-
let madeDir;
|
|
2560
|
-
try {
|
|
2561
|
-
madeDir = mkdirSync6(target, { recursive: true });
|
|
2562
|
-
for (const name2 of files) {
|
|
2563
|
-
written.push(name2);
|
|
2564
|
-
copyOne(name2, source, target);
|
|
2565
|
-
}
|
|
2566
|
-
} catch (error) {
|
|
2567
|
-
const left = undo(target, written, madeDir);
|
|
2568
|
-
throw new EffortCopyFailure(`copying ${source} to ${target} failed: ${String(error)}.${left}`);
|
|
2569
|
-
}
|
|
2570
|
-
return Object.freeze({ source, directory: target, files: Object.freeze(files) });
|
|
2571
|
-
}
|
|
2572
|
-
|
|
2573
2789
|
// src/effort/store/merge-store.ts
|
|
2574
|
-
import { randomUUID as
|
|
2575
|
-
import { existsSync as existsSync11, mkdtempSync, rmSync as
|
|
2790
|
+
import { randomUUID as randomUUID5 } from "crypto";
|
|
2791
|
+
import { existsSync as existsSync11, mkdtempSync, rmSync as rmSync7 } from "fs";
|
|
2576
2792
|
import { tmpdir as tmpdir3 } from "os";
|
|
2577
2793
|
import { dirname as dirname11, join as join11 } from "path";
|
|
2578
|
-
import { Database as
|
|
2794
|
+
import { Database as Database6, SQLiteError as SQLiteError2 } from "bun:sqlite";
|
|
2579
2795
|
|
|
2580
2796
|
// src/effort/commits.ts
|
|
2581
2797
|
import { execFileSync } from "child_process";
|
|
@@ -2986,7 +3202,7 @@ function settleMatches(local, other, unions, trail) {
|
|
|
2986
3202
|
|
|
2987
3203
|
// src/effort/store/migrate.ts
|
|
2988
3204
|
import { existsSync as existsSync10 } from "fs";
|
|
2989
|
-
import { Database as
|
|
3205
|
+
import { Database as Database5 } from "bun:sqlite";
|
|
2990
3206
|
var LIVE2 = "live";
|
|
2991
3207
|
function asidePaths2(path, first, stamp) {
|
|
2992
3208
|
return { parallelPath: `${path}.migrate-${stamp}`, backupPath: `${path}.before-${first}-${stamp}.bak` };
|
|
@@ -3014,7 +3230,7 @@ function refuseLiveLoop2(path, breaking, options) {
|
|
|
3014
3230
|
}
|
|
3015
3231
|
function buildMigrated(path, parallelPath, migrations, stamp) {
|
|
3016
3232
|
vacuumInto(path, parallelPath);
|
|
3017
|
-
const db = new
|
|
3233
|
+
const db = new Database5(parallelPath, { readwrite: true });
|
|
3018
3234
|
try {
|
|
3019
3235
|
const brought = bringForward(db, parallelPath, "write", "migrate", { migrations, ...stamp, builtAside: true });
|
|
3020
3236
|
db.run(`ATTACH DATABASE ? AS ${LIVE2}`, [path]);
|
|
@@ -3098,7 +3314,7 @@ function refuseMissing(path, which) {
|
|
|
3098
3314
|
function refuseDamaged(path) {
|
|
3099
3315
|
let answers;
|
|
3100
3316
|
try {
|
|
3101
|
-
const db = new
|
|
3317
|
+
const db = new Database6(path, { readonly: true });
|
|
3102
3318
|
try {
|
|
3103
3319
|
answers = db.query("PRAGMA integrity_check").all().map((row) => row.integrity_check);
|
|
3104
3320
|
} finally {
|
|
@@ -3120,7 +3336,7 @@ function writablePlan(path, migrations) {
|
|
|
3120
3336
|
return plan;
|
|
3121
3337
|
}
|
|
3122
3338
|
function recordedMeta(path) {
|
|
3123
|
-
const db = new
|
|
3339
|
+
const db = new Database6(path, { readonly: true });
|
|
3124
3340
|
try {
|
|
3125
3341
|
const hasTable = db.query("SELECT count(*) AS n FROM sqlite_master WHERE type = 'table' AND name = 'store_meta'").get()?.n === 1;
|
|
3126
3342
|
return hasTable ? readStoreMeta(db) : null;
|
|
@@ -3146,18 +3362,18 @@ function refuseLiveLoop3(path, options, sessionId) {
|
|
|
3146
3362
|
function openOther(otherPath, plan, stamp) {
|
|
3147
3363
|
const needs = writeNames(plan);
|
|
3148
3364
|
if (needs.length === 0) {
|
|
3149
|
-
const db = new
|
|
3365
|
+
const db = new Database6(otherPath, { readonly: true });
|
|
3150
3366
|
return { db, broughtForward: [], release: () => db.close() };
|
|
3151
3367
|
}
|
|
3152
3368
|
const dir = mkdtempSync(join11(tmpdir3(), "rafa-merge-other-"));
|
|
3153
3369
|
const release = (db) => {
|
|
3154
3370
|
db?.close();
|
|
3155
|
-
|
|
3371
|
+
rmSync7(dir, { recursive: true, force: true });
|
|
3156
3372
|
};
|
|
3157
3373
|
try {
|
|
3158
3374
|
const copyPath = join11(dir, "other.sqlite");
|
|
3159
3375
|
vacuumInto(otherPath, copyPath);
|
|
3160
|
-
const db = new
|
|
3376
|
+
const db = new Database6(copyPath, { readwrite: true });
|
|
3161
3377
|
try {
|
|
3162
3378
|
bringForward(db, copyPath, "write", "open", { ...stamp, builtAside: true });
|
|
3163
3379
|
} catch (error) {
|
|
@@ -3201,7 +3417,7 @@ function addedRows(merged) {
|
|
|
3201
3417
|
}
|
|
3202
3418
|
function buildMerged(path, parallelPath, other, merge2) {
|
|
3203
3419
|
vacuumInto(path, parallelPath);
|
|
3204
|
-
const db = new
|
|
3420
|
+
const db = new Database6(parallelPath, { readwrite: true });
|
|
3205
3421
|
try {
|
|
3206
3422
|
bringForward(db, parallelPath, "write", "open", { ...merge2.stamp, builtAside: true });
|
|
3207
3423
|
const merged = mergeInto(db, other, merge2.id, merge2.otherStore, merge2.stamp.now().toISOString());
|
|
@@ -3244,7 +3460,7 @@ function mergeStore(options) {
|
|
|
3244
3460
|
appliedBy: appliedByName(options.identity ?? readRuntimeIdentity()),
|
|
3245
3461
|
now: options.now ?? (() => new Date)
|
|
3246
3462
|
};
|
|
3247
|
-
const mergeId = (options.newMergeId ??
|
|
3463
|
+
const mergeId = (options.newMergeId ?? randomUUID5)();
|
|
3248
3464
|
const other = openOther(otherPath, otherPlan, mergeStamp);
|
|
3249
3465
|
try {
|
|
3250
3466
|
const { built, backupPath } = rebuildAside({
|
|
@@ -3371,7 +3587,7 @@ import { appendFile, mkdir, readFile, rm, writeFile } from "fs/promises";
|
|
|
3371
3587
|
import { join as join15 } from "path";
|
|
3372
3588
|
|
|
3373
3589
|
// src/commands/instinct/instinct-records.ts
|
|
3374
|
-
import { readdirSync as readdirSync4, readFileSync as
|
|
3590
|
+
import { readdirSync as readdirSync4, readFileSync as readFileSync9, statSync as statSync5 } from "fs";
|
|
3375
3591
|
import { join as join14 } from "path";
|
|
3376
3592
|
|
|
3377
3593
|
// src/check/layout.ts
|
|
@@ -4024,7 +4240,7 @@ function unreadableIssue(message) {
|
|
|
4024
4240
|
function readRecord(path, scope, id) {
|
|
4025
4241
|
let text;
|
|
4026
4242
|
try {
|
|
4027
|
-
text =
|
|
4243
|
+
text = readFileSync9(path, "utf8");
|
|
4028
4244
|
} catch (error) {
|
|
4029
4245
|
const message = error instanceof Error ? error.message : String(error);
|
|
4030
4246
|
return { scope, path, id, instinct: null, issues: [unreadableIssue(message)] };
|
|
@@ -4725,35 +4941,6 @@ function sessionSpawnEnv(base, binDir = bundledBinDirectory()) {
|
|
|
4725
4941
|
}
|
|
4726
4942
|
|
|
4727
4943
|
// src/utils/claude.ts
|
|
4728
|
-
async function getClaudeUsagePercent() {
|
|
4729
|
-
const envPct = process.env["CLAUDE_USAGE_PERCENT"];
|
|
4730
|
-
if (envPct !== undefined) {
|
|
4731
|
-
const pct = Number(envPct);
|
|
4732
|
-
return Number.isFinite(pct) ? Math.max(0, Math.min(100, pct)) : null;
|
|
4733
|
-
}
|
|
4734
|
-
return null;
|
|
4735
|
-
}
|
|
4736
|
-
async function checkUsage(context) {
|
|
4737
|
-
const pct = await getClaudeUsagePercent();
|
|
4738
|
-
if (pct === null)
|
|
4739
|
-
return false;
|
|
4740
|
-
if (context === "task" && pct >= 90) {
|
|
4741
|
-
activeOutput().warn(`
|
|
4742
|
-
Claude usage at ${pct.toFixed(0)}% (>=90%). Pausing after current task to avoid hitting the limit.`);
|
|
4743
|
-
return true;
|
|
4744
|
-
}
|
|
4745
|
-
if (pct >= 80) {
|
|
4746
|
-
activeOutput().warn(`
|
|
4747
|
-
Claude usage at ${pct.toFixed(0)}% (>=80%). Monitor closely \u2014 tasks may be interrupted.`);
|
|
4748
|
-
} else if (context === "issue" && pct >= 70) {
|
|
4749
|
-
activeOutput().warn(`
|
|
4750
|
-
Claude usage at ${pct.toFixed(0)}% (>=70%). Consider whether to start the next issue.`);
|
|
4751
|
-
} else {
|
|
4752
|
-
activeOutput().info(`
|
|
4753
|
-
Claude usage: ${pct.toFixed(0)}%`);
|
|
4754
|
-
}
|
|
4755
|
-
return false;
|
|
4756
|
-
}
|
|
4757
4944
|
var CLAUDE_BIN = "claude";
|
|
4758
4945
|
var CLAUDE_BASE_ARGS = [
|
|
4759
4946
|
"-p",
|
|
@@ -4941,7 +5128,8 @@ var TYPE_MEMBERS = {
|
|
|
4941
5128
|
adr: true,
|
|
4942
5129
|
chore: true,
|
|
4943
5130
|
"package-api": true,
|
|
4944
|
-
epic: true
|
|
5131
|
+
epic: true,
|
|
5132
|
+
spec: true
|
|
4945
5133
|
};
|
|
4946
5134
|
var PRIORITY_MEMBERS = {
|
|
4947
5135
|
urgent: true,
|
|
@@ -4969,7 +5157,8 @@ var GITHUB_LABELS = Object.freeze({
|
|
|
4969
5157
|
typePrefix: "type:",
|
|
4970
5158
|
priorityPrefix: "priority:",
|
|
4971
5159
|
needsTriage: "needs-triage",
|
|
4972
|
-
blockedByPrefix: "blocked-by:OPT-"
|
|
5160
|
+
blockedByPrefix: "blocked-by:OPT-",
|
|
5161
|
+
specBlocked: "spec:blocked"
|
|
4973
5162
|
});
|
|
4974
5163
|
var VIEW_FIELDS = "number,title,body,state,stateReason,labels,url";
|
|
4975
5164
|
var LIST_COMMAND = "gh issue list";
|
|
@@ -5078,7 +5267,8 @@ var DRAFT_CHECKS = [
|
|
|
5078
5267
|
"blockedBy",
|
|
5079
5268
|
(value) => Array.isArray(value) && value.every((item) => Number.isSafeInteger(item) && item > 0),
|
|
5080
5269
|
"a list of positive whole numbers"
|
|
5081
|
-
]
|
|
5270
|
+
],
|
|
5271
|
+
["specBlocked", (value) => value === undefined || typeof value === "boolean", "absent or a boolean"]
|
|
5082
5272
|
];
|
|
5083
5273
|
function draftProblem(draft) {
|
|
5084
5274
|
if (!isMapping(draft))
|
|
@@ -5092,6 +5282,7 @@ function labelsFor(draft) {
|
|
|
5092
5282
|
`${GITHUB_LABELS.modulePrefix}${draft.module}`,
|
|
5093
5283
|
`${GITHUB_LABELS.typePrefix}${draft.type}`,
|
|
5094
5284
|
...draft.blockedBy.map((opt) => `${GITHUB_LABELS.blockedByPrefix}${opt}`),
|
|
5285
|
+
...draft.specBlocked === true ? [GITHUB_LABELS.specBlocked] : [],
|
|
5095
5286
|
priority
|
|
5096
5287
|
];
|
|
5097
5288
|
}
|
|
@@ -5387,6 +5578,12 @@ function oneOf2(members) {
|
|
|
5387
5578
|
function orNull(accepts) {
|
|
5388
5579
|
return (value) => value === null || accepts(value);
|
|
5389
5580
|
}
|
|
5581
|
+
function orAbsent(accepts) {
|
|
5582
|
+
return (value) => value === undefined || accepts(value);
|
|
5583
|
+
}
|
|
5584
|
+
function isBoolean(value) {
|
|
5585
|
+
return typeof value === "boolean";
|
|
5586
|
+
}
|
|
5390
5587
|
function listOf(accepts) {
|
|
5391
5588
|
return (value) => Array.isArray(value) && value.every(accepts);
|
|
5392
5589
|
}
|
|
@@ -5398,6 +5595,7 @@ var FIELD_CHECKS = {
|
|
|
5398
5595
|
project: [orNull(isString2), "a string or null"],
|
|
5399
5596
|
title: [isString2, "a string"],
|
|
5400
5597
|
blockedBy: [listOf(isFiniteNumber), "a list of numbers"],
|
|
5598
|
+
specBlocked: [orAbsent(isBoolean), "absent or a boolean"],
|
|
5401
5599
|
state: [oneOf2(ISSUE_STATES), `one of: ${ISSUE_STATES.join(", ")}`],
|
|
5402
5600
|
fallbackReason: [orNull(isString2), "a string or null"],
|
|
5403
5601
|
capturedAt: [isString2, "a string"],
|
|
@@ -5453,6 +5651,7 @@ function renderLocalIssue(record) {
|
|
|
5453
5651
|
project: draft.project,
|
|
5454
5652
|
title: draft.title,
|
|
5455
5653
|
blockedBy: draft.blockedBy,
|
|
5654
|
+
...draft.specBlocked === undefined ? {} : { specBlocked: draft.specBlocked },
|
|
5456
5655
|
state: record.state,
|
|
5457
5656
|
fallbackReason: record.fallbackReason,
|
|
5458
5657
|
capturedAt: record.capturedAt,
|
|
@@ -5494,7 +5693,8 @@ function parseLocalIssue(contents, sourcePath) {
|
|
|
5494
5693
|
module: fields.module,
|
|
5495
5694
|
priority: fields.priority,
|
|
5496
5695
|
project: fields.project,
|
|
5497
|
-
blockedBy: fields.blockedBy
|
|
5696
|
+
blockedBy: fields.blockedBy,
|
|
5697
|
+
...fields.specBlocked === undefined ? {} : { specBlocked: fields.specBlocked }
|
|
5498
5698
|
},
|
|
5499
5699
|
fallbackReason: fields.fallbackReason,
|
|
5500
5700
|
capturedAt: fields.capturedAt,
|
|
@@ -5911,10 +6111,10 @@ function requireTrustedAuthor(item, reading) {
|
|
|
5911
6111
|
throw new CommandExit(TRUST_REFUSAL_EXIT, trustRefusalMessage(item, reading));
|
|
5912
6112
|
}
|
|
5913
6113
|
// src/effort/sync/wire.ts
|
|
5914
|
-
import { existsSync as existsSync14, mkdtempSync as mkdtempSync2, rmSync as
|
|
6114
|
+
import { existsSync as existsSync14, mkdtempSync as mkdtempSync2, rmSync as rmSync8 } from "fs";
|
|
5915
6115
|
import { tmpdir as tmpdir4 } from "os";
|
|
5916
6116
|
import { join as join17 } from "path";
|
|
5917
|
-
import { Database as
|
|
6117
|
+
import { Database as Database7, SQLiteError as SQLiteError3 } from "bun:sqlite";
|
|
5918
6118
|
var WIRE_FORMAT = "rafa-effort-wire";
|
|
5919
6119
|
var WIRE_VERSION = 1;
|
|
5920
6120
|
|
|
@@ -6076,7 +6276,7 @@ function exportWirePayload(options) {
|
|
|
6076
6276
|
guardTestProcess(path);
|
|
6077
6277
|
if (!existsSync14(path))
|
|
6078
6278
|
throw new WireExportRefusal(`wire: ${path} is not there`);
|
|
6079
|
-
const db = new
|
|
6279
|
+
const db = new Database7(path, { readonly: true, safeIntegers: true });
|
|
6080
6280
|
try {
|
|
6081
6281
|
db.run(`PRAGMA busy_timeout = ${String(activeStoreSettings().busyTimeoutMs)}`);
|
|
6082
6282
|
return db.transaction(() => readPayload(db, path, cursor))();
|
|
@@ -6115,7 +6315,7 @@ function insertRows(db, table, rows) {
|
|
|
6115
6315
|
}
|
|
6116
6316
|
function buildStore(path, payload, options) {
|
|
6117
6317
|
const migrations = namedMigrations(payload.migrations, options.migrations ?? SQLITE_MIGRATIONS);
|
|
6118
|
-
const db = new
|
|
6318
|
+
const db = new Database7(path, { create: true, readwrite: true });
|
|
6119
6319
|
try {
|
|
6120
6320
|
bringForward(db, path, "write", "open", {
|
|
6121
6321
|
migrations,
|
|
@@ -6134,7 +6334,7 @@ function buildStore(path, payload, options) {
|
|
|
6134
6334
|
function materialiseWirePayload(payload, options = {}) {
|
|
6135
6335
|
const checked = checkPayload(payload);
|
|
6136
6336
|
const directory = mkdtempSync2(join17(options.tempDir ?? tmpdir4(), "rafa-wire-"));
|
|
6137
|
-
const release = () =>
|
|
6337
|
+
const release = () => rmSync8(directory, { recursive: true, force: true });
|
|
6138
6338
|
const path = join17(directory, SQLITE_STORE_FILE_NAME);
|
|
6139
6339
|
try {
|
|
6140
6340
|
guardTestProcess(path);
|
|
@@ -6163,4 +6363,4 @@ function selectEffortStore(repoRoot, config) {
|
|
|
6163
6363
|
return adapter.create({ repoRoot });
|
|
6164
6364
|
}
|
|
6165
6365
|
|
|
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,
|
|
6366
|
+
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, EffortCopyRefusal, EffortCopyFailure, copyEffortStore, RebuildRefusal, readConfigFile, loadConfig, plansDirAt, requireProject, resolvePlansDir, resolveProjectConfig, countTasks, formatCounts, checkbox, planFileName, stubOfPlanFile, isFile, issueLine, plural, expectNoArgument, expectOneArgument, expectAtMostOneArgument, readSwitch, fileStamp, keepsIdLine, fix_schema_default, 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, EVENT_PREFIX, padKind, oneLine, sessionSpawnEnv, 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 };
|