runtrim 0.1.8 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-cli/runtrim.js +503 -105
- package/package.json +1 -1
package/dist-cli/runtrim.js
CHANGED
|
@@ -1947,15 +1947,6 @@ import fs7 from "fs";
|
|
|
1947
1947
|
import path7 from "path";
|
|
1948
1948
|
var BRIDGE_START = "<!-- RUNTRIM_BRIDGE_START -->";
|
|
1949
1949
|
var BRIDGE_END = "<!-- RUNTRIM_BRIDGE_END -->";
|
|
1950
|
-
var BRIDGE_BLOCK = `
|
|
1951
|
-
${BRIDGE_START}
|
|
1952
|
-
Before editing code, read RUNTRIM.md and .runtrim/contracts/latest.md.
|
|
1953
|
-
Stay inside the active scoped contract.
|
|
1954
|
-
Do not touch forbidden files or unrelated systems.
|
|
1955
|
-
If scope expands, stop and ask for a new RunTrim run.
|
|
1956
|
-
After editing, run: runtrim finish
|
|
1957
|
-
${BRIDGE_END}
|
|
1958
|
-
`;
|
|
1959
1950
|
var TOKEN_BUDGET_MAP = {
|
|
1960
1951
|
low: 1e4,
|
|
1961
1952
|
medium: 25e3,
|
|
@@ -1967,10 +1958,9 @@ function deriveBridgeContext(task, contract, recentRuns, projectName) {
|
|
|
1967
1958
|
const c = contract.contract;
|
|
1968
1959
|
const riskLevel = (_a2 = contract.wasteRiskAfter) != null ? _a2 : "medium";
|
|
1969
1960
|
const tokenBudget = (_b = TOKEN_BUDGET_MAP[riskLevel]) != null ? _b : 25e3;
|
|
1970
|
-
const
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
];
|
|
1961
|
+
const stopFromRules = (_c = c.stopRules) != null ? _c : [];
|
|
1962
|
+
const stopFromAsk = ((_d = c.whenToAsk) != null ? _d : []).map((s) => `Stop and ask before: ${s}`);
|
|
1963
|
+
const stopConditions = [...stopFromRules, ...stopFromAsk];
|
|
1974
1964
|
const verificationSteps = (_e = c.successCriteria) != null ? _e : [];
|
|
1975
1965
|
const lines = recentRuns.slice(0, 3).map((r) => {
|
|
1976
1966
|
const day = new Date(r.createdAt).toLocaleDateString("en-US", {
|
|
@@ -1994,6 +1984,126 @@ ${lines.join("\n")}` : "No prior runs. This is the first run for this project.";
|
|
|
1994
1984
|
projectName
|
|
1995
1985
|
};
|
|
1996
1986
|
}
|
|
1987
|
+
function writeCanonicalRuntrimMd(cwd = process.cwd(), projectName) {
|
|
1988
|
+
const lines = [
|
|
1989
|
+
"# RunTrim Protocol",
|
|
1990
|
+
"",
|
|
1991
|
+
...projectName ? [`Project: ${projectName}`, ""] : [],
|
|
1992
|
+
"This repo uses RunTrim as the guarded AI coding control layer.",
|
|
1993
|
+
"",
|
|
1994
|
+
"## How to start an AI coding task",
|
|
1995
|
+
"",
|
|
1996
|
+
"```",
|
|
1997
|
+
'runtrim go "<task>"',
|
|
1998
|
+
"```",
|
|
1999
|
+
"",
|
|
2000
|
+
"RunTrim creates a scoped contract, loads project memory, and generates a guarded prompt.",
|
|
2001
|
+
"",
|
|
2002
|
+
"## How to use your agent",
|
|
2003
|
+
"",
|
|
2004
|
+
"Paste the guarded prompt into Claude Code, Codex, Cursor, or any other coding agent.",
|
|
2005
|
+
"",
|
|
2006
|
+
"## After edits",
|
|
2007
|
+
"",
|
|
2008
|
+
"```",
|
|
2009
|
+
"runtrim finish",
|
|
2010
|
+
"```",
|
|
2011
|
+
"",
|
|
2012
|
+
"RunTrim checks changed files, detects drift, scores risk, and saves the run report.",
|
|
2013
|
+
"",
|
|
2014
|
+
"## If you are an AI coding agent",
|
|
2015
|
+
"",
|
|
2016
|
+
"1. Read `.runtrim/contracts/latest.md`.",
|
|
2017
|
+
" - If `Status: active` \u2014 a live task exists. Follow the contract strictly.",
|
|
2018
|
+
' - If `Status: none` \u2014 no active task. Ask the user to run `runtrim go "<task>"` first.',
|
|
2019
|
+
"2. Do not assume any prior task is still active.",
|
|
2020
|
+
"3. Stay inside the allowed scope defined in the contract.",
|
|
2021
|
+
"4. Stop and ask before touching any forbidden area.",
|
|
2022
|
+
"5. Do not read or write `.env` files or secrets.",
|
|
2023
|
+
"6. After editing, tell the user to run: `runtrim finish`",
|
|
2024
|
+
"",
|
|
2025
|
+
"---",
|
|
2026
|
+
`Protocol: runtrim init. Updated: ${(/* @__PURE__ */ new Date()).toISOString()}`
|
|
2027
|
+
];
|
|
2028
|
+
fs7.writeFileSync(path7.join(cwd, "RUNTRIM.md"), lines.join("\n"), "utf-8");
|
|
2029
|
+
}
|
|
2030
|
+
function writeRestingContract(cwd = process.cwd()) {
|
|
2031
|
+
const dir = path7.join(getConfigDir(cwd), "contracts");
|
|
2032
|
+
if (!fs7.existsSync(dir)) fs7.mkdirSync(dir, { recursive: true });
|
|
2033
|
+
const content = [
|
|
2034
|
+
"# RunTrim Contract",
|
|
2035
|
+
"",
|
|
2036
|
+
"Status: none",
|
|
2037
|
+
"",
|
|
2038
|
+
"No active RunTrim contract.",
|
|
2039
|
+
"",
|
|
2040
|
+
"Start one with:",
|
|
2041
|
+
"",
|
|
2042
|
+
"```",
|
|
2043
|
+
'runtrim go "<your task>"',
|
|
2044
|
+
"```",
|
|
2045
|
+
"",
|
|
2046
|
+
"---",
|
|
2047
|
+
`Reset by runtrim finish. Updated: ${(/* @__PURE__ */ new Date()).toISOString()}`
|
|
2048
|
+
].join("\n");
|
|
2049
|
+
fs7.writeFileSync(path7.join(dir, "latest.md"), content, "utf-8");
|
|
2050
|
+
}
|
|
2051
|
+
function writeRestingMemory(cwd = process.cwd()) {
|
|
2052
|
+
const dir = path7.join(getConfigDir(cwd), "memory");
|
|
2053
|
+
if (!fs7.existsSync(dir)) fs7.mkdirSync(dir, { recursive: true });
|
|
2054
|
+
const content = [
|
|
2055
|
+
"# RunTrim Memory",
|
|
2056
|
+
"",
|
|
2057
|
+
"Status: none",
|
|
2058
|
+
"",
|
|
2059
|
+
"No active RunTrim session.",
|
|
2060
|
+
"",
|
|
2061
|
+
"Project baseline is in `.runtrim/memory/baseline.md`.",
|
|
2062
|
+
"",
|
|
2063
|
+
"Start a new session with:",
|
|
2064
|
+
"",
|
|
2065
|
+
"```",
|
|
2066
|
+
'runtrim go "<your task>"',
|
|
2067
|
+
"```",
|
|
2068
|
+
"",
|
|
2069
|
+
"---",
|
|
2070
|
+
`Reset by runtrim finish. Updated: ${(/* @__PURE__ */ new Date()).toISOString()}`
|
|
2071
|
+
].join("\n");
|
|
2072
|
+
fs7.writeFileSync(path7.join(dir, "current.md"), content, "utf-8");
|
|
2073
|
+
}
|
|
2074
|
+
function archiveContract(cwd, runId) {
|
|
2075
|
+
const contractsDir = path7.join(getConfigDir(cwd), "contracts");
|
|
2076
|
+
const latestPath = path7.join(contractsDir, "latest.md");
|
|
2077
|
+
if (!fs7.existsSync(latestPath)) return;
|
|
2078
|
+
const content = fs7.readFileSync(latestPath, "utf-8");
|
|
2079
|
+
if (content.includes("Status: none")) return;
|
|
2080
|
+
const archiveDir = path7.join(contractsDir, "archive");
|
|
2081
|
+
if (!fs7.existsSync(archiveDir)) fs7.mkdirSync(archiveDir, { recursive: true });
|
|
2082
|
+
fs7.writeFileSync(path7.join(archiveDir, `${runId}.md`), content, "utf-8");
|
|
2083
|
+
}
|
|
2084
|
+
function archiveMemory(cwd, runId) {
|
|
2085
|
+
const memoryDir = path7.join(getConfigDir(cwd), "memory");
|
|
2086
|
+
const currentPath = path7.join(memoryDir, "current.md");
|
|
2087
|
+
if (!fs7.existsSync(currentPath)) return;
|
|
2088
|
+
const content = fs7.readFileSync(currentPath, "utf-8");
|
|
2089
|
+
if (content.includes("Status: none")) return;
|
|
2090
|
+
const archiveDir = path7.join(memoryDir, "archive");
|
|
2091
|
+
if (!fs7.existsSync(archiveDir)) fs7.mkdirSync(archiveDir, { recursive: true });
|
|
2092
|
+
fs7.writeFileSync(path7.join(archiveDir, `${runId}.md`), content, "utf-8");
|
|
2093
|
+
}
|
|
2094
|
+
function removeBridgeBlock(filePath) {
|
|
2095
|
+
if (!fs7.existsSync(filePath)) return false;
|
|
2096
|
+
const content = fs7.readFileSync(filePath, "utf-8");
|
|
2097
|
+
const startIdx = content.indexOf(BRIDGE_START);
|
|
2098
|
+
const endIdx = content.indexOf(BRIDGE_END);
|
|
2099
|
+
if (startIdx === -1 || endIdx === -1) return false;
|
|
2100
|
+
const before = content.slice(0, startIdx).trimEnd();
|
|
2101
|
+
const after = content.slice(endIdx + BRIDGE_END.length).replace(/^\n+/, "\n");
|
|
2102
|
+
const newContent = (before + after).trimEnd() + "\n";
|
|
2103
|
+
if (newContent === content) return false;
|
|
2104
|
+
fs7.writeFileSync(filePath, newContent, "utf-8");
|
|
2105
|
+
return true;
|
|
2106
|
+
}
|
|
1997
2107
|
function writeContractFile(ctx, cwd = process.cwd()) {
|
|
1998
2108
|
const dir = path7.join(getConfigDir(cwd), "contracts");
|
|
1999
2109
|
if (!fs7.existsSync(dir)) fs7.mkdirSync(dir, { recursive: true });
|
|
@@ -2001,13 +2111,15 @@ function writeContractFile(ctx, cwd = process.cwd()) {
|
|
|
2001
2111
|
const forbidLines = ctx.forbiddenScope.length > 0 ? ctx.forbiddenScope.map((s) => `- ${s}`) : ["- .env* files", "- auth logic", "- database schema / migrations", "- billing and payment logic"];
|
|
2002
2112
|
const stopLines = ctx.stopConditions.length > 0 ? ctx.stopConditions.map((s) => `- ${s}`) : [
|
|
2003
2113
|
"- Stop if scope must expand beyond the task.",
|
|
2004
|
-
"- Stop
|
|
2114
|
+
"- Stop and ask before touching any forbidden area.",
|
|
2005
2115
|
"- Stop if more than 5 files require changes."
|
|
2006
2116
|
];
|
|
2007
2117
|
const verifyLines = ctx.verificationSteps.length > 0 ? ctx.verificationSteps.map((s) => `- ${s}`) : ["- Verify the behavior described in the task.", "- Check no unrelated files changed."];
|
|
2008
2118
|
const lines = [
|
|
2009
2119
|
"# RunTrim Active Contract",
|
|
2010
2120
|
"",
|
|
2121
|
+
"Status: active",
|
|
2122
|
+
"",
|
|
2011
2123
|
`Task: ${ctx.task}`,
|
|
2012
2124
|
`Goal: ${ctx.goal}`,
|
|
2013
2125
|
`Risk: ${ctx.riskLevel}`,
|
|
@@ -2039,6 +2151,8 @@ function writeMemoryFile(ctx, cwd = process.cwd()) {
|
|
|
2039
2151
|
const lines = [
|
|
2040
2152
|
"# RunTrim Memory Pack",
|
|
2041
2153
|
"",
|
|
2154
|
+
"Status: active",
|
|
2155
|
+
"",
|
|
2042
2156
|
`Project: ${ctx.projectName}`,
|
|
2043
2157
|
`Current task: ${ctx.task}`,
|
|
2044
2158
|
`Updated: ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
@@ -2067,15 +2181,17 @@ function writeBridgeInstructions(cwd = process.cwd()) {
|
|
|
2067
2181
|
"",
|
|
2068
2182
|
"## Before editing",
|
|
2069
2183
|
"",
|
|
2070
|
-
"Read
|
|
2071
|
-
"
|
|
2072
|
-
"- .
|
|
2073
|
-
|
|
2184
|
+
"1. Read `RUNTRIM.md`.",
|
|
2185
|
+
"2. Read `.runtrim/contracts/latest.md`.",
|
|
2186
|
+
" - If `Status: active` \u2014 follow the contract strictly.",
|
|
2187
|
+
' - If `Status: none` \u2014 stop. Ask the user to run `runtrim go "<task>"` first.',
|
|
2188
|
+
"3. If the contract is active, read `.runtrim/memory/current.md` for session context.",
|
|
2189
|
+
" If no active session, read `.runtrim/memory/baseline.md` for project baseline.",
|
|
2074
2190
|
"",
|
|
2075
2191
|
"## During editing",
|
|
2076
2192
|
"",
|
|
2077
2193
|
"- Stay within the allowed scope defined in the contract.",
|
|
2078
|
-
"-
|
|
2194
|
+
"- Stop and ask the user before touching any forbidden area.",
|
|
2079
2195
|
"- Make minimal, targeted changes only.",
|
|
2080
2196
|
"- Do not refactor, rename, or reorganize outside the task scope.",
|
|
2081
2197
|
"- Do not add console.log statements or debug artifacts.",
|
|
@@ -2088,56 +2204,10 @@ function writeBridgeInstructions(cwd = process.cwd()) {
|
|
|
2088
2204
|
"- Do not run runtrim commands yourself unless explicitly asked.",
|
|
2089
2205
|
"",
|
|
2090
2206
|
"---",
|
|
2091
|
-
"Generated by RunTrim
|
|
2207
|
+
"Generated by RunTrim. Do not edit manually."
|
|
2092
2208
|
];
|
|
2093
2209
|
fs7.writeFileSync(path7.join(dir, "agent-instructions.md"), lines.join("\n"), "utf-8");
|
|
2094
2210
|
}
|
|
2095
|
-
function writeRootProtocolFile(ctx, cwd = process.cwd()) {
|
|
2096
|
-
const lines = [
|
|
2097
|
-
"# RunTrim Protocol",
|
|
2098
|
-
"",
|
|
2099
|
-
"This project is guarded by RunTrim Bridge Mode.",
|
|
2100
|
-
"",
|
|
2101
|
-
"## Active session",
|
|
2102
|
-
"",
|
|
2103
|
-
`Task: ${ctx.task}`,
|
|
2104
|
-
`Risk: ${ctx.riskLevel}`,
|
|
2105
|
-
`Token budget: ~${ctx.tokenBudget.toLocaleString()}`,
|
|
2106
|
-
"",
|
|
2107
|
-
"## Before editing",
|
|
2108
|
-
"",
|
|
2109
|
-
"Read the active contract before making any changes:",
|
|
2110
|
-
"",
|
|
2111
|
-
"```",
|
|
2112
|
-
".runtrim/contracts/latest.md",
|
|
2113
|
-
"```",
|
|
2114
|
-
"",
|
|
2115
|
-
"## Rules",
|
|
2116
|
-
"",
|
|
2117
|
-
"- Stay inside the active scoped contract.",
|
|
2118
|
-
"- Do not touch forbidden files or unrelated systems.",
|
|
2119
|
-
"- If scope must expand, stop and ask the user to start a new RunTrim session.",
|
|
2120
|
-
"- Keep changes minimal and targeted.",
|
|
2121
|
-
"- Do not refactor, reorganize, or rename outside the direct task.",
|
|
2122
|
-
"",
|
|
2123
|
-
"## After editing",
|
|
2124
|
-
"",
|
|
2125
|
-
"The user will run: runtrim finish",
|
|
2126
|
-
"",
|
|
2127
|
-
"Do not attempt to run runtrim commands yourself.",
|
|
2128
|
-
"",
|
|
2129
|
-
"---",
|
|
2130
|
-
`Generated by RunTrim Bridge. Updated: ${(/* @__PURE__ */ new Date()).toISOString()}`
|
|
2131
|
-
];
|
|
2132
|
-
fs7.writeFileSync(path7.join(cwd, "RUNTRIM.md"), lines.join("\n"), "utf-8");
|
|
2133
|
-
}
|
|
2134
|
-
function appendBridgeBlock(filePath) {
|
|
2135
|
-
if (!fs7.existsSync(filePath)) return false;
|
|
2136
|
-
const content = fs7.readFileSync(filePath, "utf-8");
|
|
2137
|
-
if (content.includes(BRIDGE_START)) return false;
|
|
2138
|
-
fs7.writeFileSync(filePath, content.trimEnd() + "\n" + BRIDGE_BLOCK, "utf-8");
|
|
2139
|
-
return true;
|
|
2140
|
-
}
|
|
2141
2211
|
function writeBridgeFiles(ctx, cwd) {
|
|
2142
2212
|
const written = [];
|
|
2143
2213
|
const managedPaths = [];
|
|
@@ -2145,22 +2215,18 @@ function writeBridgeFiles(ctx, cwd) {
|
|
|
2145
2215
|
managedPaths.push(relativePath);
|
|
2146
2216
|
written.push(label != null ? label : relativePath);
|
|
2147
2217
|
};
|
|
2148
|
-
writeRootProtocolFile(ctx, cwd);
|
|
2149
|
-
track("RUNTRIM.md");
|
|
2150
2218
|
writeContractFile(ctx, cwd);
|
|
2151
2219
|
track(".runtrim/contracts/latest.md");
|
|
2152
2220
|
writeMemoryFile(ctx, cwd);
|
|
2153
2221
|
track(".runtrim/memory/current.md");
|
|
2154
2222
|
writeBridgeInstructions(cwd);
|
|
2155
2223
|
track(".runtrim/bridge/agent-instructions.md");
|
|
2156
|
-
if (appendBridgeBlock(path7.join(cwd, "CLAUDE.md"))) track("CLAUDE.md", "CLAUDE.md (bridge block appended)");
|
|
2157
|
-
if (appendBridgeBlock(path7.join(cwd, "AGENTS.md"))) track("AGENTS.md", "AGENTS.md (bridge block appended)");
|
|
2158
2224
|
return { written, managedPaths };
|
|
2159
2225
|
}
|
|
2160
2226
|
function buildBridgePrompt(contractText, ctx) {
|
|
2161
2227
|
const allowedList = ctx.allowedScope.length > 0 ? ctx.allowedScope.map((s) => ` - ${s}`).join("\n") : " - Defined in .runtrim/contracts/latest.md";
|
|
2162
2228
|
const forbidList = ctx.forbiddenScope.length > 0 ? ctx.forbiddenScope.map((s) => ` - ${s}`).join("\n") : " - .env files, auth, database schema, billing, payments";
|
|
2163
|
-
const stopList = ctx.stopConditions.length > 0 ? ctx.stopConditions.slice(0, 4).map((s) => ` - ${s}`).join("\n") : " - Stop if more than 5 files need changes.\n - Stop
|
|
2229
|
+
const stopList = ctx.stopConditions.length > 0 ? ctx.stopConditions.slice(0, 4).map((s) => ` - ${s}`).join("\n") : " - Stop if more than 5 files need changes.\n - Stop and ask before touching any forbidden area.";
|
|
2164
2230
|
const header = [
|
|
2165
2231
|
"RUNTRIM BRIDGE SESSION",
|
|
2166
2232
|
`Task: ${ctx.task}`,
|
|
@@ -2182,7 +2248,7 @@ function buildBridgePrompt(contractText, ctx) {
|
|
|
2182
2248
|
" - Read RUNTRIM.md and .runtrim/contracts/latest.md before touching any file.",
|
|
2183
2249
|
" - Inspect first. List relevant files before opening them.",
|
|
2184
2250
|
" - Make the minimal change required. No unrelated refactors.",
|
|
2185
|
-
" -
|
|
2251
|
+
" - Stop and ask before touching any forbidden area.",
|
|
2186
2252
|
" - After editing, tell the user which files changed. They will run: runtrim finish",
|
|
2187
2253
|
""
|
|
2188
2254
|
].join("\n");
|
|
@@ -4165,13 +4231,239 @@ program.command("start").description("Guided RunTrim onboarding and daily loop")
|
|
|
4165
4231
|
console.log("");
|
|
4166
4232
|
}
|
|
4167
4233
|
});
|
|
4168
|
-
|
|
4234
|
+
var PROTOCOL_BLOCK_START = "<!-- RUNTRIM_PROTOCOL_START -->";
|
|
4235
|
+
var PROTOCOL_BLOCK_END = "<!-- RUNTRIM_PROTOCOL_END -->";
|
|
4236
|
+
var PROTOCOL_POINTER_BLOCK = `
|
|
4237
|
+
${PROTOCOL_BLOCK_START}
|
|
4238
|
+
This repo uses RunTrim as the guarded AI coding protocol.
|
|
4239
|
+
Before editing code, read RUNTRIM.md.
|
|
4240
|
+
Start every task with: runtrim go "<task>"
|
|
4241
|
+
Stay inside .runtrim/contracts/latest.md.
|
|
4242
|
+
After edits, ask the user to run: runtrim finish
|
|
4243
|
+
${PROTOCOL_BLOCK_END}
|
|
4244
|
+
`;
|
|
4245
|
+
function upsertProtocolBlock(filePath) {
|
|
4246
|
+
if (!fs10.existsSync(filePath)) return "skipped";
|
|
4247
|
+
const content = fs10.readFileSync(filePath, "utf-8");
|
|
4248
|
+
const startIdx = content.indexOf(PROTOCOL_BLOCK_START);
|
|
4249
|
+
const endIdx = content.indexOf(PROTOCOL_BLOCK_END);
|
|
4250
|
+
if (startIdx !== -1 && endIdx !== -1) {
|
|
4251
|
+
const before = content.slice(0, startIdx);
|
|
4252
|
+
const after = content.slice(endIdx + PROTOCOL_BLOCK_END.length);
|
|
4253
|
+
const newContent = before + PROTOCOL_POINTER_BLOCK.trimStart() + after.replace(/^\n/, "");
|
|
4254
|
+
if (newContent === content) return "unchanged";
|
|
4255
|
+
fs10.writeFileSync(filePath, newContent, "utf-8");
|
|
4256
|
+
return "updated";
|
|
4257
|
+
}
|
|
4258
|
+
fs10.writeFileSync(filePath, content.trimEnd() + "\n" + PROTOCOL_POINTER_BLOCK, "utf-8");
|
|
4259
|
+
return "updated";
|
|
4260
|
+
}
|
|
4261
|
+
function createMinimalAgentPointerFile(filePath, filename) {
|
|
4262
|
+
const label = filename === "CLAUDE.md" ? "Claude Code" : "AI agents";
|
|
4263
|
+
const content = [
|
|
4264
|
+
`# ${label} Instructions`,
|
|
4265
|
+
"",
|
|
4266
|
+
"This repo uses RunTrim as the guarded AI coding protocol.",
|
|
4267
|
+
"Read RUNTRIM.md before editing any code.",
|
|
4268
|
+
"",
|
|
4269
|
+
PROTOCOL_POINTER_BLOCK.trim(),
|
|
4270
|
+
""
|
|
4271
|
+
].join("\n");
|
|
4272
|
+
fs10.writeFileSync(filePath, content, "utf-8");
|
|
4273
|
+
}
|
|
4274
|
+
function installProtocol(cwd, baseline, opts = {}) {
|
|
4275
|
+
var _a2, _b, _c, _d, _e, _f, _g;
|
|
4276
|
+
const configDir = getConfigDir(cwd);
|
|
4277
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4278
|
+
const extraFolders = [
|
|
4279
|
+
path10.join(configDir, "contracts"),
|
|
4280
|
+
path10.join(configDir, "memory"),
|
|
4281
|
+
path10.join(configDir, "bridge"),
|
|
4282
|
+
path10.join(configDir, "reports")
|
|
4283
|
+
];
|
|
4284
|
+
const createdFolders = [];
|
|
4285
|
+
for (const dir of extraFolders) {
|
|
4286
|
+
if (!fs10.existsSync(dir)) {
|
|
4287
|
+
fs10.mkdirSync(dir, { recursive: true });
|
|
4288
|
+
createdFolders.push(dir.replace(cwd + path10.sep, "").replace(/\\/g, "/"));
|
|
4289
|
+
}
|
|
4290
|
+
}
|
|
4291
|
+
const scripts = (_a2 = baseline.scripts) != null ? _a2 : {};
|
|
4292
|
+
const buildCmd = (_d = (_c = (_b = scripts["build"]) != null ? _b : scripts["build:web"]) != null ? _c : scripts["build:all"]) != null ? _d : null;
|
|
4293
|
+
const testCmd = (_f = (_e = scripts["test"]) != null ? _e : scripts["test:run"]) != null ? _f : null;
|
|
4294
|
+
const runtrimMdPath = path10.join(cwd, "RUNTRIM.md");
|
|
4295
|
+
const runtrimMdExists = fs10.existsSync(runtrimMdPath);
|
|
4296
|
+
writeCanonicalRuntrimMd(cwd, baseline.projectName);
|
|
4297
|
+
const projectJsonPath = path10.join(configDir, "project.json");
|
|
4298
|
+
const projectJsonExists = fs10.existsSync(projectJsonPath);
|
|
4299
|
+
const projectJson = {
|
|
4300
|
+
name: baseline.projectName,
|
|
4301
|
+
stack: baseline.detectedStack,
|
|
4302
|
+
packageManager: baseline.packageManager,
|
|
4303
|
+
buildCommand: buildCmd,
|
|
4304
|
+
testCommand: testCmd,
|
|
4305
|
+
detectedAt: now
|
|
4306
|
+
};
|
|
4307
|
+
fs10.writeFileSync(projectJsonPath, JSON.stringify(projectJson, null, 2), "utf-8");
|
|
4308
|
+
const policiesPath = path10.join(configDir, "policies.json");
|
|
4309
|
+
const policiesJsonExists = fs10.existsSync(policiesPath);
|
|
4310
|
+
const detectedSensitive = ((_g = baseline.riskSurfaces) != null ? _g : []).map((s) => s.type.toLowerCase());
|
|
4311
|
+
const policies = {
|
|
4312
|
+
version: 1,
|
|
4313
|
+
protected: [
|
|
4314
|
+
".env*",
|
|
4315
|
+
"secrets",
|
|
4316
|
+
"*.key",
|
|
4317
|
+
"*.pem",
|
|
4318
|
+
"auth/**",
|
|
4319
|
+
"middleware.ts",
|
|
4320
|
+
"prisma/schema.prisma",
|
|
4321
|
+
"prisma/migrations/**",
|
|
4322
|
+
"database/migrations/**",
|
|
4323
|
+
"stripe/**",
|
|
4324
|
+
"billing/**",
|
|
4325
|
+
"payment/**",
|
|
4326
|
+
"webhooks/**",
|
|
4327
|
+
"package-lock.json",
|
|
4328
|
+
"pnpm-lock.yaml",
|
|
4329
|
+
"yarn.lock",
|
|
4330
|
+
".next/**",
|
|
4331
|
+
"dist/**",
|
|
4332
|
+
"build/**",
|
|
4333
|
+
"node_modules/**"
|
|
4334
|
+
],
|
|
4335
|
+
sensitive: [
|
|
4336
|
+
"auth",
|
|
4337
|
+
"billing",
|
|
4338
|
+
"payment",
|
|
4339
|
+
"middleware",
|
|
4340
|
+
"database",
|
|
4341
|
+
"schema",
|
|
4342
|
+
"migrations",
|
|
4343
|
+
"env",
|
|
4344
|
+
"secrets",
|
|
4345
|
+
"webhooks",
|
|
4346
|
+
...detectedSensitive.filter((s) => !["auth", "billing", "payment", "middleware", "database", "env", "secrets", "webhooks"].includes(s))
|
|
4347
|
+
],
|
|
4348
|
+
note: "These areas require explicit task scope before any agent edits.",
|
|
4349
|
+
updatedAt: now
|
|
4350
|
+
};
|
|
4351
|
+
fs10.writeFileSync(policiesPath, JSON.stringify(policies, null, 2), "utf-8");
|
|
4352
|
+
const baselineMdPath = path10.join(configDir, "memory", "baseline.md");
|
|
4353
|
+
const baselineMdExists = fs10.existsSync(baselineMdPath);
|
|
4354
|
+
const protectedList = policies.protected.slice(0, 10).map((p) => `- ${p}`).join("\n");
|
|
4355
|
+
const stackLine = baseline.detectedStack.join(", ") || "unknown";
|
|
4356
|
+
const baselineMd = [
|
|
4357
|
+
"# RunTrim Memory \u2014 Baseline",
|
|
4358
|
+
"",
|
|
4359
|
+
`Project: ${baseline.projectName}`,
|
|
4360
|
+
`Stack: ${stackLine}`,
|
|
4361
|
+
`Package manager: ${baseline.packageManager}`,
|
|
4362
|
+
...buildCmd ? [`Build: ${buildCmd}`] : [],
|
|
4363
|
+
...testCmd ? [`Test: ${testCmd}`] : [],
|
|
4364
|
+
"",
|
|
4365
|
+
"## Protected areas",
|
|
4366
|
+
"",
|
|
4367
|
+
protectedList,
|
|
4368
|
+
"",
|
|
4369
|
+
"## Project rules",
|
|
4370
|
+
"",
|
|
4371
|
+
'- Start every AI task with: runtrim go "<task>"',
|
|
4372
|
+
"- Stay inside the scoped contract.",
|
|
4373
|
+
"- Run runtrim finish after agent edits.",
|
|
4374
|
+
"- No unrelated refactors during a task.",
|
|
4375
|
+
"- Never touch .env files.",
|
|
4376
|
+
"",
|
|
4377
|
+
"## Prior agent decisions",
|
|
4378
|
+
"",
|
|
4379
|
+
"No prior runs recorded. This is the baseline for this project.",
|
|
4380
|
+
"",
|
|
4381
|
+
"---",
|
|
4382
|
+
`Created by runtrim init. Updated: ${now}`
|
|
4383
|
+
].join("\n");
|
|
4384
|
+
fs10.writeFileSync(baselineMdPath, baselineMd, "utf-8");
|
|
4385
|
+
const agentResults = [];
|
|
4386
|
+
const agentTargets = ["CLAUDE.md", "AGENTS.md"];
|
|
4387
|
+
for (const filename of agentTargets) {
|
|
4388
|
+
const filePath = path10.join(cwd, filename);
|
|
4389
|
+
if (fs10.existsSync(filePath)) {
|
|
4390
|
+
removeBridgeBlock(filePath);
|
|
4391
|
+
const result = upsertProtocolBlock(filePath);
|
|
4392
|
+
agentResults.push({ file: filename, result: result === "skipped" ? "unchanged" : result });
|
|
4393
|
+
} else if (opts.agentFiles) {
|
|
4394
|
+
createMinimalAgentPointerFile(filePath, filename);
|
|
4395
|
+
agentResults.push({ file: filename, result: "created" });
|
|
4396
|
+
} else {
|
|
4397
|
+
agentResults.push({ file: filename, result: "skipped" });
|
|
4398
|
+
}
|
|
4399
|
+
}
|
|
4400
|
+
let cursorResult = "skipped";
|
|
4401
|
+
const cursorDir = path10.join(cwd, ".cursor");
|
|
4402
|
+
const cursorExists = fs10.existsSync(cursorDir);
|
|
4403
|
+
if (opts.cursor || cursorExists) {
|
|
4404
|
+
const rulesDir = path10.join(cursorDir, "rules");
|
|
4405
|
+
const mdcPath = path10.join(rulesDir, "runtrim.mdc");
|
|
4406
|
+
if (!fs10.existsSync(rulesDir)) fs10.mkdirSync(rulesDir, { recursive: true });
|
|
4407
|
+
const existed = fs10.existsSync(mdcPath);
|
|
4408
|
+
const cursorMdc = [
|
|
4409
|
+
"---",
|
|
4410
|
+
"description: RunTrim guarded AI coding protocol",
|
|
4411
|
+
"alwaysApply: true",
|
|
4412
|
+
"---",
|
|
4413
|
+
"",
|
|
4414
|
+
"# RunTrim Protocol",
|
|
4415
|
+
"",
|
|
4416
|
+
"This repo uses RunTrim as the guarded AI coding control layer.",
|
|
4417
|
+
"",
|
|
4418
|
+
"## Before editing any code",
|
|
4419
|
+
"",
|
|
4420
|
+
"1. Read `RUNTRIM.md` in the repo root.",
|
|
4421
|
+
"2. If `.runtrim/contracts/latest.md` exists, read the active contract.",
|
|
4422
|
+
"",
|
|
4423
|
+
"## Rules",
|
|
4424
|
+
"",
|
|
4425
|
+
"- Stay inside the allowed scope defined in the contract.",
|
|
4426
|
+
"- Do not touch forbidden files or unrelated systems.",
|
|
4427
|
+
"- Stop immediately if scope must expand.",
|
|
4428
|
+
"- Do not read or write `.env` files.",
|
|
4429
|
+
"- Do not refactor outside the task scope.",
|
|
4430
|
+
"",
|
|
4431
|
+
"## After editing",
|
|
4432
|
+
"",
|
|
4433
|
+
"Tell the user to run: `runtrim finish`"
|
|
4434
|
+
].join("\n");
|
|
4435
|
+
fs10.writeFileSync(mdcPath, cursorMdc, "utf-8");
|
|
4436
|
+
cursorResult = existed ? "updated" : "created";
|
|
4437
|
+
}
|
|
4438
|
+
const contractsDir = path10.join(configDir, "contracts");
|
|
4439
|
+
const latestContractPath = path10.join(contractsDir, "latest.md");
|
|
4440
|
+
const contractIsStale = fs10.existsSync(latestContractPath) && !fs10.readFileSync(latestContractPath, "utf-8").includes("Status: none");
|
|
4441
|
+
if (!fs10.existsSync(latestContractPath) || contractIsStale) {
|
|
4442
|
+
writeRestingContract(cwd);
|
|
4443
|
+
}
|
|
4444
|
+
const memoryDir = path10.join(configDir, "memory");
|
|
4445
|
+
const currentMemoryPath = path10.join(memoryDir, "current.md");
|
|
4446
|
+
const memoryIsStale = fs10.existsSync(currentMemoryPath) && !fs10.readFileSync(currentMemoryPath, "utf-8").includes("Status: none");
|
|
4447
|
+
if (!fs10.existsSync(currentMemoryPath) || memoryIsStale) {
|
|
4448
|
+
writeRestingMemory(cwd);
|
|
4449
|
+
}
|
|
4450
|
+
return {
|
|
4451
|
+
runtrimMd: runtrimMdExists ? "updated" : "created",
|
|
4452
|
+
projectJson: projectJsonExists ? "updated" : "created",
|
|
4453
|
+
policiesJson: policiesJsonExists ? "updated" : "created",
|
|
4454
|
+
baselineMd: baselineMdExists ? "updated" : "created",
|
|
4455
|
+
folders: createdFolders,
|
|
4456
|
+
agentFiles: agentResults,
|
|
4457
|
+
cursorRules: cursorResult
|
|
4458
|
+
};
|
|
4459
|
+
}
|
|
4460
|
+
program.command("init").description("Install the RunTrim protocol in the current project").option("--refresh", "Refresh baseline audit, rules, and memory without overwriting config").option("--agent-files", "Create CLAUDE.md and AGENTS.md if missing, with RunTrim pointer").option("--cursor", "Create .cursor/rules/runtrim.mdc Cursor agent instructions").action(async (options) => {
|
|
4169
4461
|
var _a2;
|
|
4170
4462
|
const cwd = process.cwd();
|
|
4171
4463
|
const allowed = await ensureRepoAllowedForFree(cwd);
|
|
4172
4464
|
if (!allowed) return;
|
|
4173
4465
|
console.log("");
|
|
4174
|
-
console.log(
|
|
4466
|
+
console.log(GO_ACCENT.bold("RunTrim init"));
|
|
4175
4467
|
console.log("");
|
|
4176
4468
|
const initResult = await initializeRunTrim(cwd, {
|
|
4177
4469
|
refresh: options.refresh,
|
|
@@ -4179,33 +4471,46 @@ program.command("init").description("Initialize RunTrim in the current project")
|
|
|
4179
4471
|
});
|
|
4180
4472
|
if (!initResult.ok) return;
|
|
4181
4473
|
const baseline = (_a2 = loadProjectAudit(cwd)) != null ? _a2 : performBaselineProjectAudit(cwd, null);
|
|
4182
|
-
const
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
|
|
4186
|
-
|
|
4187
|
-
console.log(DIM("
|
|
4188
|
-
console.log(
|
|
4189
|
-
console.log(DIM("
|
|
4190
|
-
console.log("");
|
|
4191
|
-
console.log(DIM(" Scripts found"));
|
|
4192
|
-
console.log(DIM(" ") + chalk.white(scriptNames.length ? scriptNames.join(", ") : "none"));
|
|
4474
|
+
const protocol = installProtocol(cwd, baseline, {
|
|
4475
|
+
agentFiles: options.agentFiles,
|
|
4476
|
+
cursor: options.cursor
|
|
4477
|
+
});
|
|
4478
|
+
const stackLine = baseline.detectedStack.length ? baseline.detectedStack.join(" + ") : "unknown stack";
|
|
4479
|
+
console.log(DIM(" Project"));
|
|
4480
|
+
console.log(chalk.white(" " + baseline.projectName));
|
|
4481
|
+
console.log(DIM(" " + stackLine));
|
|
4193
4482
|
console.log("");
|
|
4194
|
-
console.log(DIM("
|
|
4195
|
-
|
|
4196
|
-
|
|
4483
|
+
console.log(DIM(" Protocol"));
|
|
4484
|
+
const protocolFiles = [
|
|
4485
|
+
["RUNTRIM.md", protocol.runtrimMd],
|
|
4486
|
+
[".runtrim/project.json", protocol.projectJson],
|
|
4487
|
+
[".runtrim/policies.json", protocol.policiesJson],
|
|
4488
|
+
[".runtrim/memory/baseline.md", protocol.baselineMd]
|
|
4489
|
+
];
|
|
4490
|
+
for (const [file, result] of protocolFiles) {
|
|
4491
|
+
console.log(DIM(" ") + chalk.white(file.padEnd(34)) + DIM(result));
|
|
4197
4492
|
}
|
|
4198
4493
|
console.log("");
|
|
4199
|
-
console.log(DIM(
|
|
4200
|
-
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4494
|
+
console.log(DIM(" Agent pointers"));
|
|
4495
|
+
for (const { file, result } of protocol.agentFiles) {
|
|
4496
|
+
const color = result === "skipped" ? DIM : chalk.white;
|
|
4497
|
+
console.log(DIM(" ") + color(file.padEnd(34)) + DIM(result));
|
|
4498
|
+
}
|
|
4499
|
+
if (protocol.cursorRules !== "skipped") {
|
|
4500
|
+
console.log(DIM(" ") + chalk.white(".cursor/rules/runtrim.mdc".padEnd(34)) + DIM(protocol.cursorRules));
|
|
4501
|
+
} else if (options.cursor) {
|
|
4502
|
+
console.log(DIM(" Cursor rules skipped (.cursor/ not found and --cursor not passed)"));
|
|
4503
|
+
}
|
|
4206
4504
|
console.log("");
|
|
4505
|
+
if (protocol.folders.length > 0) {
|
|
4506
|
+
console.log(DIM(" Folders created"));
|
|
4507
|
+
for (const f of protocol.folders) {
|
|
4508
|
+
console.log(DIM(" " + f + "/"));
|
|
4509
|
+
}
|
|
4510
|
+
console.log("");
|
|
4511
|
+
}
|
|
4207
4512
|
console.log(DIM(" Next"));
|
|
4208
|
-
console.log(chalk.white(' runtrim
|
|
4513
|
+
console.log(chalk.white(' runtrim go "your first task"'));
|
|
4209
4514
|
console.log("");
|
|
4210
4515
|
});
|
|
4211
4516
|
var agentCommand = program.command("agent").description("Show or configure local agent execution settings");
|
|
@@ -4877,7 +5182,7 @@ program.command("prepare <task>").description("Prepare a guarded prompt without
|
|
|
4877
5182
|
}
|
|
4878
5183
|
);
|
|
4879
5184
|
program.command("go <task>").description("Bridge Mode: generate a scoped contract, write protocol files, and prepare the guarded prompt").option("--no-clipboard", "Print prompt to terminal instead of copying to clipboard").option("--no-sync", "Skip cloud sync even if a CLI token is configured").option("--no-bridge", "Skip bridge file writing (RUNTRIM.md, contracts, memory)").option("--print", "Always print the prompt to terminal in addition to copying").option("--monitor", "Open local panel monitor in the background (best effort)").action(async (task, options) => {
|
|
4880
|
-
var _a2, _b, _c, _d;
|
|
5185
|
+
var _a2, _b, _c, _d, _e, _f, _g;
|
|
4881
5186
|
const cwd = process.cwd();
|
|
4882
5187
|
const allowed = await ensureRepoAllowedForFree(cwd);
|
|
4883
5188
|
if (!allowed) return;
|
|
@@ -4886,6 +5191,38 @@ program.command("go <task>").description("Bridge Mode: generate a scoped contrac
|
|
|
4886
5191
|
if (!initResult.ok) return;
|
|
4887
5192
|
}
|
|
4888
5193
|
const config = loadConfig(cwd);
|
|
5194
|
+
const globalAuth = loadGlobalAuth();
|
|
5195
|
+
const rawToken = (_b = (_a2 = globalAuth == null ? void 0 : globalAuth.token) != null ? _a2 : config.syncToken) != null ? _b : null;
|
|
5196
|
+
const apiBase = resolveApiBase(config);
|
|
5197
|
+
if (rawToken == null ? void 0 : rawToken.startsWith("rt_live_")) {
|
|
5198
|
+
let serverResult = null;
|
|
5199
|
+
try {
|
|
5200
|
+
const res = await fetch(`${apiBase}/api/cli/usage/bridge-run`, {
|
|
5201
|
+
method: "POST",
|
|
5202
|
+
headers: { Authorization: `Bearer ${rawToken}` }
|
|
5203
|
+
});
|
|
5204
|
+
if (res.ok) {
|
|
5205
|
+
const body = await res.json();
|
|
5206
|
+
serverResult = {
|
|
5207
|
+
allowed: body.allowed,
|
|
5208
|
+
plan: body.plan,
|
|
5209
|
+
used: (_c = body.usage.bridgeRunsUsed) != null ? _c : 0,
|
|
5210
|
+
limit: body.usage.bridgeRunsLimit
|
|
5211
|
+
};
|
|
5212
|
+
}
|
|
5213
|
+
} catch (e) {
|
|
5214
|
+
}
|
|
5215
|
+
if (serverResult !== null && !serverResult.allowed) {
|
|
5216
|
+
printBridgeLimitMessage(serverResult.used);
|
|
5217
|
+
return;
|
|
5218
|
+
}
|
|
5219
|
+
} else {
|
|
5220
|
+
const check = checkAndIncrementLocalUsage();
|
|
5221
|
+
if (!check.allowed) {
|
|
5222
|
+
printBridgeLimitMessage(check.used);
|
|
5223
|
+
return;
|
|
5224
|
+
}
|
|
5225
|
+
}
|
|
4889
5226
|
const auditSpinner = oraFactory({ text: " Auditing task...", color: "blue" }).start();
|
|
4890
5227
|
await new Promise((r) => setTimeout(r, 180));
|
|
4891
5228
|
const audit = auditTask(task, config, cwd);
|
|
@@ -4917,7 +5254,7 @@ program.command("go <task>").description("Bridge Mode: generate a scoped contrac
|
|
|
4917
5254
|
}
|
|
4918
5255
|
const runs = loadAllRuns(cwd);
|
|
4919
5256
|
const projectAudit = loadProjectAudit(cwd);
|
|
4920
|
-
const projectName = (
|
|
5257
|
+
const projectName = (_d = projectAudit == null ? void 0 : projectAudit.projectName) != null ? _d : path10.basename(cwd);
|
|
4921
5258
|
const memoryMarkdown = (() => {
|
|
4922
5259
|
try {
|
|
4923
5260
|
return readMemory(cwd);
|
|
@@ -4954,9 +5291,9 @@ program.command("go <task>").description("Bridge Mode: generate a scoped contrac
|
|
|
4954
5291
|
updateRun(run.id, { bridgeManagedFiles: bridgeManagedPaths }, cwd);
|
|
4955
5292
|
let synced = false;
|
|
4956
5293
|
if (options.sync !== false) {
|
|
4957
|
-
const
|
|
4958
|
-
const
|
|
4959
|
-
if (
|
|
5294
|
+
const globalAuth2 = loadGlobalAuth();
|
|
5295
|
+
const rawToken2 = (_f = (_e = globalAuth2 == null ? void 0 : globalAuth2.token) != null ? _e : config.syncToken) != null ? _f : null;
|
|
5296
|
+
if (rawToken2 == null ? void 0 : rawToken2.startsWith("rt_live_")) {
|
|
4960
5297
|
try {
|
|
4961
5298
|
const freshRuns = loadAllRuns(cwd);
|
|
4962
5299
|
const payload = buildSyncPayload({
|
|
@@ -4967,10 +5304,10 @@ program.command("go <task>").description("Bridge Mode: generate a scoped contrac
|
|
|
4967
5304
|
memoryMarkdown: memoryMarkdown != null ? memoryMarkdown : "",
|
|
4968
5305
|
runs: freshRuns
|
|
4969
5306
|
});
|
|
4970
|
-
const
|
|
4971
|
-
const r = await fetch(`${
|
|
5307
|
+
const apiBase2 = resolveApiBase(config);
|
|
5308
|
+
const r = await fetch(`${apiBase2}/api/sync`, {
|
|
4972
5309
|
method: "POST",
|
|
4973
|
-
headers: { "Content-Type": "application/json", Authorization: `Bearer ${
|
|
5310
|
+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${rawToken2}` },
|
|
4974
5311
|
body: JSON.stringify(payload)
|
|
4975
5312
|
});
|
|
4976
5313
|
synced = r.ok;
|
|
@@ -4978,7 +5315,7 @@ program.command("go <task>").description("Bridge Mode: generate a scoped contrac
|
|
|
4978
5315
|
}
|
|
4979
5316
|
}
|
|
4980
5317
|
}
|
|
4981
|
-
const riskColor = (
|
|
5318
|
+
const riskColor = (_g = { low: chalk.green, medium: chalk.yellow, high: chalk.hex("#FF8C00"), critical: chalk.red }[bridgeCtx.riskLevel]) != null ? _g : chalk.white;
|
|
4982
5319
|
console.log("");
|
|
4983
5320
|
console.log(GO_ACCENT.bold("RunTrim go"));
|
|
4984
5321
|
console.log("");
|
|
@@ -5858,6 +6195,49 @@ var statusColors = {
|
|
|
5858
6195
|
drift_detected: chalk.red,
|
|
5859
6196
|
blocked: chalk.red
|
|
5860
6197
|
};
|
|
6198
|
+
var GLOBAL_USAGE_FILE = path10.join(os3.homedir(), ".runtrim", "usage.json");
|
|
6199
|
+
var FREE_BRIDGE_LIMIT_LOCAL = 5;
|
|
6200
|
+
function currentUsagePeriod() {
|
|
6201
|
+
const d = /* @__PURE__ */ new Date();
|
|
6202
|
+
return `${d.getUTCFullYear()}-${String(d.getUTCMonth() + 1).padStart(2, "0")}`;
|
|
6203
|
+
}
|
|
6204
|
+
function loadLocalUsageRuns() {
|
|
6205
|
+
var _a2;
|
|
6206
|
+
try {
|
|
6207
|
+
const raw = JSON.parse(fs10.readFileSync(GLOBAL_USAGE_FILE, "utf-8"));
|
|
6208
|
+
return (_a2 = raw.bridgeRuns) != null ? _a2 : {};
|
|
6209
|
+
} catch (e) {
|
|
6210
|
+
return {};
|
|
6211
|
+
}
|
|
6212
|
+
}
|
|
6213
|
+
function saveLocalUsageRuns(bridgeRuns) {
|
|
6214
|
+
const dir = path10.dirname(GLOBAL_USAGE_FILE);
|
|
6215
|
+
if (!fs10.existsSync(dir)) fs10.mkdirSync(dir, { recursive: true });
|
|
6216
|
+
fs10.writeFileSync(GLOBAL_USAGE_FILE, JSON.stringify({ bridgeRuns }, null, 2), "utf-8");
|
|
6217
|
+
}
|
|
6218
|
+
function checkAndIncrementLocalUsage() {
|
|
6219
|
+
var _a2;
|
|
6220
|
+
const period = currentUsagePeriod();
|
|
6221
|
+
const runs = loadLocalUsageRuns();
|
|
6222
|
+
const used = (_a2 = runs[period]) != null ? _a2 : 0;
|
|
6223
|
+
if (used >= FREE_BRIDGE_LIMIT_LOCAL) {
|
|
6224
|
+
return { allowed: false, used };
|
|
6225
|
+
}
|
|
6226
|
+
runs[period] = used + 1;
|
|
6227
|
+
saveLocalUsageRuns(runs);
|
|
6228
|
+
return { allowed: true, used: used + 1 };
|
|
6229
|
+
}
|
|
6230
|
+
function printBridgeLimitMessage(used) {
|
|
6231
|
+
console.log("");
|
|
6232
|
+
console.log(chalk.red.bold(" Free Bridge limit reached."));
|
|
6233
|
+
console.log("");
|
|
6234
|
+
console.log(chalk.white(` You have used ${used} local guarded run${used === 1 ? "" : "s"} this month.`));
|
|
6235
|
+
console.log(DIM(" Upgrade to Pro for unlimited Bridge Mode, cloud sync, project memory,"));
|
|
6236
|
+
console.log(DIM(" reports, and continuation history."));
|
|
6237
|
+
console.log("");
|
|
6238
|
+
console.log(chalk.white(" https://www.runtrim.com/pricing"));
|
|
6239
|
+
console.log("");
|
|
6240
|
+
}
|
|
5861
6241
|
var GLOBAL_AUTH_DIR = path10.join(os3.homedir(), ".runtrim");
|
|
5862
6242
|
var GLOBAL_AUTH_FILE = path10.join(GLOBAL_AUTH_DIR, "auth.json");
|
|
5863
6243
|
function loadGlobalAuth() {
|
|
@@ -6062,6 +6442,14 @@ program.command("finish").description("Bridge Mode: evaluate agent output, check
|
|
|
6062
6442
|
const freshRuns = loadAllRuns(cwd);
|
|
6063
6443
|
const updatedRun = (_k = freshRuns.find((r) => r.id === activeRun.id)) != null ? _k : activeRun;
|
|
6064
6444
|
writeMemoryFromRuns(updatedRun, freshRuns, config, cwd);
|
|
6445
|
+
archiveContract(cwd, activeRun.id);
|
|
6446
|
+
archiveMemory(cwd, activeRun.id);
|
|
6447
|
+
writeCanonicalRuntrimMd(cwd, projectName);
|
|
6448
|
+
writeRestingContract(cwd);
|
|
6449
|
+
writeRestingMemory(cwd);
|
|
6450
|
+
const bridgeRemovals = [];
|
|
6451
|
+
if (removeBridgeBlock(path10.join(cwd, "CLAUDE.md"))) bridgeRemovals.push("CLAUDE.md");
|
|
6452
|
+
if (removeBridgeBlock(path10.join(cwd, "AGENTS.md"))) bridgeRemovals.push("AGENTS.md");
|
|
6065
6453
|
let synced = false;
|
|
6066
6454
|
if (options.sync !== false) {
|
|
6067
6455
|
const globalAuth = loadGlobalAuth();
|
|
@@ -6152,6 +6540,16 @@ program.command("finish").description("Bridge Mode: evaluate agent output, check
|
|
|
6152
6540
|
console.log(DIM(" ") + (synced ? chalk.white("Completed.") : DIM("Skipped. Run runtrim login to connect your dashboard.")));
|
|
6153
6541
|
console.log("");
|
|
6154
6542
|
}
|
|
6543
|
+
console.log(GO_ACCENT.bold("Protocol"));
|
|
6544
|
+
console.log(DIM(" ") + chalk.white("RUNTRIM.md restored"));
|
|
6545
|
+
console.log(DIM(" ") + chalk.white("latest contract archived"));
|
|
6546
|
+
console.log(DIM(" ") + chalk.white("current memory reset"));
|
|
6547
|
+
if (bridgeRemovals.length > 0) {
|
|
6548
|
+
for (const f of bridgeRemovals) {
|
|
6549
|
+
console.log(DIM(" ") + chalk.white(`${f} bridge block removed`));
|
|
6550
|
+
}
|
|
6551
|
+
}
|
|
6552
|
+
console.log("");
|
|
6155
6553
|
});
|
|
6156
6554
|
program.command("sync").description("Sync local run history and project memory to your RunTrim dashboard").option("--dry-run", "Show what would be synced without uploading").action(async (opts) => {
|
|
6157
6555
|
var _a2, _b, _c, _d, _e, _f;
|