runtrim 0.1.7 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist-cli/runtrim.js +1226 -202
  2. package/package.json +2 -1
@@ -25,8 +25,9 @@ import { Chalk } from "chalk";
25
25
  import ora from "ora";
26
26
  import prompts from "prompts";
27
27
  import clipboard from "clipboardy";
28
- import fs9 from "fs";
29
- import path9 from "path";
28
+ import fs10 from "fs";
29
+ import path10 from "path";
30
+ import os3 from "os";
30
31
  import { execa as execa3 } from "execa";
31
32
 
32
33
  // src/lib/runtrim-config.ts
@@ -1731,6 +1732,16 @@ var SyncRunSchema = z2.object({
1731
1732
  localId: z2.string(),
1732
1733
  task: z2.string(),
1733
1734
  status: z2.string(),
1735
+ // Bridge Mode optional fields
1736
+ goal: z2.string().optional(),
1737
+ allowedScope: z2.array(z2.string()).optional(),
1738
+ forbiddenScope: z2.array(z2.string()).optional(),
1739
+ stopConditions: z2.array(z2.string()).optional(),
1740
+ memoryUsed: z2.boolean().optional(),
1741
+ memorySummary: z2.string().optional(),
1742
+ tokenBudget: z2.number().optional(),
1743
+ scopeDriftStatus: z2.string().optional(),
1744
+ reportSummary: z2.string().optional(),
1734
1745
  createdAt: z2.string(),
1735
1746
  evaluatedAt: z2.string().nullable(),
1736
1747
  riskBefore: z2.string().nullable(),
@@ -1847,7 +1858,7 @@ function buildSyncPayload(input) {
1847
1858
  );
1848
1859
  const memoryNextSafePrompt = readSection(memoryMarkdown, "Next safe prompt");
1849
1860
  const mappedRuns = runs.slice(0, 200).map((run) => {
1850
- var _a3, _b2, _c2, _d2, _e2, _f2, _g2, _h2, _i2, _j2, _k2, _l2, _m2, _n2, _o2, _p2, _q2, _r2, _s2, _t2, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I;
1861
+ var _a3, _b2, _c2, _d2, _e2, _f2, _g2, _h2, _i2, _j2, _k2, _l2, _m2, _n2, _o2, _p2, _q2, _r2, _s2, _t2, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L, _M, _N, _O, _P, _Q, _R, _S, _T, _U, _V, _W, _X, _Y, _Z, __;
1851
1862
  const tokens = Math.round(parseEstimatedNumber(String(run.contract.estimatedTokensTrimmed)));
1852
1863
  const dollars = estimateSavingsFromTokens(tokens);
1853
1864
  const status = (_b2 = (_a3 = run.evaluation) == null ? void 0 : _a3.status) != null ? _b2 : run.status;
@@ -1882,7 +1893,17 @@ function buildSyncPayload(input) {
1882
1893
  nextSafePrompt: (_I = (_H = (_G = (_D = (_B = (_z = run.evaluation) == null ? void 0 : _z.nextPrompt) != null ? _B : (_A = run.evaluation) == null ? void 0 : _A.nextSafePrompt) != null ? _D : (_C = run.evaluation) == null ? void 0 : _C.nextGuardedPrompt) != null ? _G : (_F = (_E = run.contract) == null ? void 0 : _E.splitReport) == null ? void 0 : _F.nextSafePrompt) != null ? _H : fallbackNextPrompt) != null ? _I : null,
1883
1894
  latestPrompt: latestPromptText,
1884
1895
  continuationPrompt: continuationPromptText,
1885
- fallbackNextPrompt
1896
+ fallbackNextPrompt,
1897
+ // Bridge Mode fields
1898
+ goal: ((_K = (_J = run.contract) == null ? void 0 : _J.contract) == null ? void 0 : _K.cleanedObjective) || void 0,
1899
+ allowedScope: ((_N = (_M = (_L = run.contract) == null ? void 0 : _L.contract) == null ? void 0 : _M.relevantScope) == null ? void 0 : _N.length) ? run.contract.contract.relevantScope : void 0,
1900
+ forbiddenScope: ((_Q = (_P = (_O = run.contract) == null ? void 0 : _O.contract) == null ? void 0 : _P.forbiddenScope) == null ? void 0 : _Q.length) ? run.contract.contract.forbiddenScope : void 0,
1901
+ stopConditions: ((_T = (_S = (_R = run.contract) == null ? void 0 : _R.contract) == null ? void 0 : _S.stopRules) == null ? void 0 : _T.length) ? run.contract.contract.stopRules : void 0,
1902
+ memoryUsed: (_U = run.memoryUsed) != null ? _U : void 0,
1903
+ memorySummary: (_X = (_W = run.memorySummary) != null ? _W : (_V = run.evaluation) == null ? void 0 : _V.memorySummary) != null ? _X : void 0,
1904
+ tokenBudget: (_Y = run.tokenBudget) != null ? _Y : void 0,
1905
+ scopeDriftStatus: (_Z = run.scopeDriftStatus) != null ? _Z : void 0,
1906
+ reportSummary: (__ = run.reportSummary) != null ? __ : void 0
1886
1907
  };
1887
1908
  });
1888
1909
  const payload = {
@@ -1921,6 +1942,253 @@ function buildSyncPayload(input) {
1921
1942
  return SyncPayloadSchema.parse(payload);
1922
1943
  }
1923
1944
 
1945
+ // src/lib/bridge.ts
1946
+ import fs7 from "fs";
1947
+ import path7 from "path";
1948
+ var BRIDGE_START = "<!-- RUNTRIM_BRIDGE_START -->";
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
+ var TOKEN_BUDGET_MAP = {
1960
+ low: 1e4,
1961
+ medium: 25e3,
1962
+ high: 4e4,
1963
+ critical: 5e4
1964
+ };
1965
+ function deriveBridgeContext(task, contract, recentRuns, projectName) {
1966
+ var _a2, _b, _c, _d, _e, _f, _g;
1967
+ const c = contract.contract;
1968
+ const riskLevel = (_a2 = contract.wasteRiskAfter) != null ? _a2 : "medium";
1969
+ const tokenBudget = (_b = TOKEN_BUDGET_MAP[riskLevel]) != null ? _b : 25e3;
1970
+ const stopConditions = [
1971
+ ...(_c = c.stopRules) != null ? _c : [],
1972
+ ...((_d = c.whenToAsk) != null ? _d : []).map((s) => `Stop if: ${s}`)
1973
+ ];
1974
+ const verificationSteps = (_e = c.successCriteria) != null ? _e : [];
1975
+ const lines = recentRuns.slice(0, 3).map((r) => {
1976
+ const day = new Date(r.createdAt).toLocaleDateString("en-US", {
1977
+ month: "short",
1978
+ day: "numeric"
1979
+ });
1980
+ return `- ${r.task} (${r.status}, ${day})`;
1981
+ });
1982
+ const memoryContext = lines.length > 0 ? `Recent runs:
1983
+ ${lines.join("\n")}` : "No prior runs. This is the first run for this project.";
1984
+ return {
1985
+ task,
1986
+ goal: c.cleanedObjective || task,
1987
+ riskLevel,
1988
+ tokenBudget,
1989
+ allowedScope: (_f = c.relevantScope) != null ? _f : [],
1990
+ forbiddenScope: (_g = c.forbiddenScope) != null ? _g : [],
1991
+ stopConditions,
1992
+ verificationSteps,
1993
+ memoryContext,
1994
+ projectName
1995
+ };
1996
+ }
1997
+ function writeContractFile(ctx, cwd = process.cwd()) {
1998
+ const dir = path7.join(getConfigDir(cwd), "contracts");
1999
+ if (!fs7.existsSync(dir)) fs7.mkdirSync(dir, { recursive: true });
2000
+ const scopeLines = ctx.allowedScope.length > 0 ? ctx.allowedScope.map((s) => `- ${s}`) : ["- Scope not explicitly defined. Stay close to the task description."];
2001
+ const forbidLines = ctx.forbiddenScope.length > 0 ? ctx.forbiddenScope.map((s) => `- ${s}`) : ["- .env* files", "- auth logic", "- database schema / migrations", "- billing and payment logic"];
2002
+ const stopLines = ctx.stopConditions.length > 0 ? ctx.stopConditions.map((s) => `- ${s}`) : [
2003
+ "- Stop if scope must expand beyond the task.",
2004
+ "- Stop if a forbidden area needs to be touched.",
2005
+ "- Stop if more than 5 files require changes."
2006
+ ];
2007
+ const verifyLines = ctx.verificationSteps.length > 0 ? ctx.verificationSteps.map((s) => `- ${s}`) : ["- Verify the behavior described in the task.", "- Check no unrelated files changed."];
2008
+ const lines = [
2009
+ "# RunTrim Active Contract",
2010
+ "",
2011
+ `Task: ${ctx.task}`,
2012
+ `Goal: ${ctx.goal}`,
2013
+ `Risk: ${ctx.riskLevel}`,
2014
+ `Token budget: ~${ctx.tokenBudget.toLocaleString()}`,
2015
+ `Created: ${(/* @__PURE__ */ new Date()).toISOString()}`,
2016
+ "",
2017
+ "## Allowed scope",
2018
+ ...scopeLines,
2019
+ "",
2020
+ "## Forbidden scope",
2021
+ ...forbidLines,
2022
+ "",
2023
+ "## Stop conditions",
2024
+ ...stopLines,
2025
+ "",
2026
+ "## Verification steps",
2027
+ ...verifyLines,
2028
+ "",
2029
+ "---",
2030
+ "Generated by RunTrim Bridge. Do not edit manually."
2031
+ ];
2032
+ fs7.writeFileSync(path7.join(dir, "latest.md"), lines.join("\n"), "utf-8");
2033
+ }
2034
+ function writeMemoryFile(ctx, cwd = process.cwd()) {
2035
+ const dir = path7.join(getConfigDir(cwd), "memory");
2036
+ if (!fs7.existsSync(dir)) fs7.mkdirSync(dir, { recursive: true });
2037
+ const noTouchLines = ctx.forbiddenScope.length > 0 ? ctx.forbiddenScope.map((s) => `- ${s}`) : ["- .env files", "- auth logic", "- database schema", "- billing and payment logic"];
2038
+ const scopeLines = ctx.allowedScope.length > 0 ? ctx.allowedScope.map((s) => `- ${s}`) : ["- Defined in .runtrim/contracts/latest.md"];
2039
+ const lines = [
2040
+ "# RunTrim Memory Pack",
2041
+ "",
2042
+ `Project: ${ctx.projectName}`,
2043
+ `Current task: ${ctx.task}`,
2044
+ `Updated: ${(/* @__PURE__ */ new Date()).toISOString()}`,
2045
+ "",
2046
+ "## Context",
2047
+ ctx.memoryContext,
2048
+ "",
2049
+ "## Do not touch",
2050
+ ...noTouchLines,
2051
+ "",
2052
+ "## Active task scope",
2053
+ ...scopeLines,
2054
+ "",
2055
+ "---",
2056
+ "Generated by RunTrim Bridge. Do not edit manually."
2057
+ ];
2058
+ fs7.writeFileSync(path7.join(dir, "current.md"), lines.join("\n"), "utf-8");
2059
+ }
2060
+ function writeBridgeInstructions(cwd = process.cwd()) {
2061
+ const dir = path7.join(getConfigDir(cwd), "bridge");
2062
+ if (!fs7.existsSync(dir)) fs7.mkdirSync(dir, { recursive: true });
2063
+ const lines = [
2064
+ "# RunTrim Agent Instructions",
2065
+ "",
2066
+ "You are operating inside a RunTrim-guarded session.",
2067
+ "",
2068
+ "## Before editing",
2069
+ "",
2070
+ "Read these files first:",
2071
+ "- RUNTRIM.md",
2072
+ "- .runtrim/contracts/latest.md",
2073
+ "- .runtrim/memory/current.md",
2074
+ "",
2075
+ "## During editing",
2076
+ "",
2077
+ "- Stay within the allowed scope defined in the contract.",
2078
+ "- If a change requires touching a forbidden area, STOP and tell the user.",
2079
+ "- Make minimal, targeted changes only.",
2080
+ "- Do not refactor, rename, or reorganize outside the task scope.",
2081
+ "- Do not add console.log statements or debug artifacts.",
2082
+ "- Do not install new dependencies without explicit approval.",
2083
+ "",
2084
+ "## After editing",
2085
+ "",
2086
+ "- Tell the user which files you changed and why.",
2087
+ "- The user will run: runtrim finish",
2088
+ "- Do not run runtrim commands yourself unless explicitly asked.",
2089
+ "",
2090
+ "---",
2091
+ "Generated by RunTrim Bridge. Do not edit manually."
2092
+ ];
2093
+ fs7.writeFileSync(path7.join(dir, "agent-instructions.md"), lines.join("\n"), "utf-8");
2094
+ }
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
+ function writeBridgeFiles(ctx, cwd) {
2142
+ const written = [];
2143
+ const managedPaths = [];
2144
+ const track = (relativePath, label) => {
2145
+ managedPaths.push(relativePath);
2146
+ written.push(label != null ? label : relativePath);
2147
+ };
2148
+ writeRootProtocolFile(ctx, cwd);
2149
+ track("RUNTRIM.md");
2150
+ writeContractFile(ctx, cwd);
2151
+ track(".runtrim/contracts/latest.md");
2152
+ writeMemoryFile(ctx, cwd);
2153
+ track(".runtrim/memory/current.md");
2154
+ writeBridgeInstructions(cwd);
2155
+ 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
+ return { written, managedPaths };
2159
+ }
2160
+ function buildBridgePrompt(contractText, ctx) {
2161
+ const allowedList = ctx.allowedScope.length > 0 ? ctx.allowedScope.map((s) => ` - ${s}`).join("\n") : " - Defined in .runtrim/contracts/latest.md";
2162
+ 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 if a forbidden area must be touched.";
2164
+ const header = [
2165
+ "RUNTRIM BRIDGE SESSION",
2166
+ `Task: ${ctx.task}`,
2167
+ `Risk: ${ctx.riskLevel} | Token budget: ~${ctx.tokenBudget.toLocaleString()}`,
2168
+ "",
2169
+ "ALLOWED SCOPE",
2170
+ allowedList,
2171
+ "",
2172
+ "FORBIDDEN SCOPE",
2173
+ forbidList,
2174
+ "",
2175
+ "STOP CONDITIONS",
2176
+ stopList,
2177
+ ""
2178
+ ].join("\n");
2179
+ const footer = [
2180
+ "",
2181
+ "RULES",
2182
+ " - Read RUNTRIM.md and .runtrim/contracts/latest.md before touching any file.",
2183
+ " - Inspect first. List relevant files before opening them.",
2184
+ " - Make the minimal change required. No unrelated refactors.",
2185
+ " - If scope must expand, stop immediately and tell the user.",
2186
+ " - After editing, tell the user which files changed. They will run: runtrim finish",
2187
+ ""
2188
+ ].join("\n");
2189
+ return header + contractText + footer;
2190
+ }
2191
+
1924
2192
  // src/lib/run-watch.ts
1925
2193
  function normalizeScopeKeywords2(scope) {
1926
2194
  var _a2;
@@ -2017,18 +2285,18 @@ function evaluateWatchState(input) {
2017
2285
  }
2018
2286
 
2019
2287
  // src/lib/local-panel-server.ts
2020
- import fs8 from "fs";
2288
+ import fs9 from "fs";
2021
2289
  import http from "http";
2022
2290
  import net from "net";
2023
2291
  import os2 from "os";
2024
- import path8 from "path";
2292
+ import path9 from "path";
2025
2293
  import { spawn } from "child_process";
2026
2294
 
2027
2295
  // src/lib/global-registry.ts
2028
2296
  import crypto from "crypto";
2029
- import fs7 from "fs";
2297
+ import fs8 from "fs";
2030
2298
  import os from "os";
2031
- import path7 from "path";
2299
+ import path8 from "path";
2032
2300
  import { execa as execa2 } from "execa";
2033
2301
  var DEFAULT_REGISTRY = {
2034
2302
  version: 1,
@@ -2040,23 +2308,23 @@ var DEFAULT_REGISTRY = {
2040
2308
  }
2041
2309
  };
2042
2310
  function normalizeRepoPath(input) {
2043
- const resolved = path7.resolve(input);
2311
+ const resolved = path8.resolve(input);
2044
2312
  return process.platform === "win32" ? resolved.toLowerCase() : resolved;
2045
2313
  }
2046
2314
  function hashValue(value) {
2047
2315
  return crypto.createHash("sha256").update(value).digest("hex").slice(0, 16);
2048
2316
  }
2049
2317
  function getGlobalRunTrimDir() {
2050
- return path7.join(os.homedir(), ".runtrim");
2318
+ return path8.join(os.homedir(), ".runtrim");
2051
2319
  }
2052
2320
  function getGlobalRegistryPath() {
2053
- return path7.join(getGlobalRunTrimDir(), "global.json");
2321
+ return path8.join(getGlobalRunTrimDir(), "global.json");
2054
2322
  }
2055
2323
  function loadGlobalRegistry() {
2056
2324
  const registryPath = getGlobalRegistryPath();
2057
- if (!fs7.existsSync(registryPath)) return __spreadValues({}, DEFAULT_REGISTRY);
2325
+ if (!fs8.existsSync(registryPath)) return __spreadValues({}, DEFAULT_REGISTRY);
2058
2326
  try {
2059
- const raw = JSON.parse(fs7.readFileSync(registryPath, "utf-8"));
2327
+ const raw = JSON.parse(fs8.readFileSync(registryPath, "utf-8"));
2060
2328
  return {
2061
2329
  version: 1,
2062
2330
  plan: raw.plan === "free" ? "free" : "free",
@@ -2079,8 +2347,8 @@ function loadGlobalRegistry() {
2079
2347
  }
2080
2348
  function saveGlobalRegistry(registry) {
2081
2349
  const dir = getGlobalRunTrimDir();
2082
- if (!fs7.existsSync(dir)) fs7.mkdirSync(dir, { recursive: true });
2083
- fs7.writeFileSync(getGlobalRegistryPath(), JSON.stringify(registry, null, 2), "utf-8");
2350
+ if (!fs8.existsSync(dir)) fs8.mkdirSync(dir, { recursive: true });
2351
+ fs8.writeFileSync(getGlobalRegistryPath(), JSON.stringify(registry, null, 2), "utf-8");
2084
2352
  }
2085
2353
  async function getCurrentRepoIdentity(cwd = process.cwd()) {
2086
2354
  const normalizedPath = normalizeRepoPath(cwd);
@@ -2098,7 +2366,7 @@ async function getCurrentRepoIdentity(cwd = process.cwd()) {
2098
2366
  const idPrefix = gitRemote ? "remote" : "path";
2099
2367
  return {
2100
2368
  id: `${idPrefix}_${hashValue(idSeed)}`,
2101
- name: path7.basename(normalizedPath),
2369
+ name: path8.basename(normalizedPath),
2102
2370
  path: normalizedPath,
2103
2371
  gitRemote
2104
2372
  };
@@ -2189,9 +2457,9 @@ function truncate2(text, max = 96) {
2189
2457
  return `${text.slice(0, max - 3)}...`;
2190
2458
  }
2191
2459
  function readMemory2(cwd) {
2192
- const p = path8.join(getConfigDir(cwd), "memory.md");
2193
- if (!fs8.existsSync(p)) return null;
2194
- return fs8.readFileSync(p, "utf-8");
2460
+ const p = path9.join(getConfigDir(cwd), "memory.md");
2461
+ if (!fs9.existsSync(p)) return null;
2462
+ return fs9.readFileSync(p, "utf-8");
2195
2463
  }
2196
2464
  function parseMemorySection(memory, title) {
2197
2465
  if (!memory) return "";
@@ -2215,13 +2483,13 @@ function readProjectName(cwd) {
2215
2483
  audit = null;
2216
2484
  }
2217
2485
  if (audit == null ? void 0 : audit.projectName) return audit.projectName;
2218
- const pkgPath = path8.join(cwd, "package.json");
2219
- if (!fs8.existsSync(pkgPath)) return path8.basename(cwd);
2486
+ const pkgPath = path9.join(cwd, "package.json");
2487
+ if (!fs9.existsSync(pkgPath)) return path9.basename(cwd);
2220
2488
  try {
2221
- const pkg = JSON.parse(fs8.readFileSync(pkgPath, "utf-8"));
2222
- return ((_a2 = pkg.name) == null ? void 0 : _a2.trim()) || path8.basename(cwd);
2489
+ const pkg = JSON.parse(fs9.readFileSync(pkgPath, "utf-8"));
2490
+ return ((_a2 = pkg.name) == null ? void 0 : _a2.trim()) || path9.basename(cwd);
2223
2491
  } catch (e) {
2224
- return path8.basename(cwd);
2492
+ return path9.basename(cwd);
2225
2493
  }
2226
2494
  }
2227
2495
  function isoOrEmpty(value) {
@@ -2912,7 +3180,7 @@ async function getPanelState(cwd, monitorMode) {
2912
3180
  warnings,
2913
3181
  initialized,
2914
3182
  projectName: readProjectName(cwd),
2915
- repoPath: path8.resolve(cwd),
3183
+ repoPath: path9.resolve(cwd),
2916
3184
  mode: "local",
2917
3185
  monitor: monitorMode,
2918
3186
  stack: (_a2 = projectAudit == null ? void 0 : projectAudit.detectedStack) != null ? _a2 : [],
@@ -2930,7 +3198,7 @@ async function getPanelState(cwd, monitorMode) {
2930
3198
  changedFilesCount: (_k = (_j = (_h = (_g = latest.evaluation) == null ? void 0 : _g.changedFiles) == null ? void 0 : _h.length) != null ? _j : (_i = latest.watchChangedFiles) == null ? void 0 : _i.length) != null ? _k : 0,
2931
3199
  riskLabel: ((_o = (_n = (_l = latest.evaluation) == null ? void 0 : _l.scopeDriftRisk) != null ? _n : (_m = latest.audit) == null ? void 0 : _m.wasteRiskBefore) != null ? _o : "n/a").replace(/_/g, " "),
2932
3200
  nextSafeAction: (_q = (_p = latest.evaluation) == null ? void 0 : _p.nextSafeAction) != null ? _q : "Run runtrim check to evaluate latest run.",
2933
- promptState: ((_r = latest.contract) == null ? void 0 : _r.isBlocked) ? "split required prompt" : fs8.existsSync(path8.join(getConfigDir(cwd), "latest-prompt.md")) ? "guarded prompt available" : "no continuation prompt yet",
3201
+ promptState: ((_r = latest.contract) == null ? void 0 : _r.isBlocked) ? "split required prompt" : fs9.existsSync(path9.join(getConfigDir(cwd), "latest-prompt.md")) ? "guarded prompt available" : "no continuation prompt yet",
2934
3202
  missingProofCount: (_u = (_t = (_s = latest.evaluation) == null ? void 0 : _s.missingProofItems) == null ? void 0 : _t.length) != null ? _u : 0,
2935
3203
  missingProofLabel: ((_w = (_v = latest.evaluation) == null ? void 0 : _v.missingProofItems) == null ? void 0 : _w.length) ? `${latest.evaluation.missingProofItems.length} verification item(s) still missing` : "No missing proof recorded",
2936
3204
  proofState: ((_y = (_x = latest.evaluation) == null ? void 0 : _x.missingProofItems) == null ? void 0 : _y.length) && latest.evaluation.missingProofItems.length > 0 ? "Proof pending" : latest.status === "guarded" ? "Needs check" : "Proof captured",
@@ -3038,8 +3306,8 @@ async function startLocalPanelServer(options = {}) {
3038
3306
  error: "partial_state",
3039
3307
  warnings: ["state_route_failed"],
3040
3308
  initialized: false,
3041
- projectName: path8.basename(cwd),
3042
- repoPath: path8.resolve(cwd),
3309
+ projectName: path9.basename(cwd),
3310
+ repoPath: path9.resolve(cwd),
3043
3311
  mode: "local",
3044
3312
  monitor,
3045
3313
  stack: [],
@@ -3221,29 +3489,29 @@ function resolveCliLauncherPath() {
3221
3489
  var _a2;
3222
3490
  const invokedPath = (_a2 = process.argv[1]) == null ? void 0 : _a2.trim();
3223
3491
  if (invokedPath) {
3224
- const absolute = path9.resolve(invokedPath);
3225
- if (fs9.existsSync(absolute)) return absolute;
3492
+ const absolute = path10.resolve(invokedPath);
3493
+ if (fs10.existsSync(absolute)) return absolute;
3226
3494
  }
3227
- const localFallback = path9.resolve(process.cwd(), "dist-cli", "runtrim.cjs");
3228
- if (fs9.existsSync(localFallback)) return localFallback;
3495
+ const localFallback = path10.resolve(process.cwd(), "dist-cli", "runtrim.cjs");
3496
+ if (fs10.existsSync(localFallback)) return localFallback;
3229
3497
  return null;
3230
3498
  }
3231
3499
  function resolveCliRuntimeDir() {
3232
3500
  const launcher = resolveCliLauncherPath();
3233
- if (launcher) return path9.dirname(launcher);
3501
+ if (launcher) return path10.dirname(launcher);
3234
3502
  return process.cwd();
3235
3503
  }
3236
3504
  function resolveCliVersion() {
3237
3505
  var _a2, _b;
3238
3506
  const cliDir = resolveCliRuntimeDir();
3239
3507
  const candidates = [
3240
- path9.resolve(cliDir, "..", "package.json"),
3241
- path9.resolve(cliDir, "package.json"),
3242
- path9.resolve(process.cwd(), "package.json")
3508
+ path10.resolve(cliDir, "..", "package.json"),
3509
+ path10.resolve(cliDir, "package.json"),
3510
+ path10.resolve(process.cwd(), "package.json")
3243
3511
  ];
3244
3512
  for (const packageJsonPath of candidates) {
3245
3513
  try {
3246
- const raw = fs9.readFileSync(packageJsonPath, "utf-8");
3514
+ const raw = fs10.readFileSync(packageJsonPath, "utf-8");
3247
3515
  const parsed = JSON.parse(raw);
3248
3516
  if (parsed.version && parsed.version.trim()) return parsed.version.trim();
3249
3517
  } catch (e) {
@@ -3284,13 +3552,13 @@ function parseCommandString(input) {
3284
3552
  function resolvePromptPath(config, cwd) {
3285
3553
  var _a2;
3286
3554
  const configured = ((_a2 = config.lastPromptPath) == null ? void 0 : _a2.trim()) || ".runtrim/latest-prompt.md";
3287
- return path9.isAbsolute(configured) ? configured : path9.join(cwd, configured);
3555
+ return path10.isAbsolute(configured) ? configured : path10.join(cwd, configured);
3288
3556
  }
3289
3557
  function writeLatestPromptFile(content, config, cwd) {
3290
3558
  const promptPath = resolvePromptPath(config, cwd);
3291
- const dir = path9.dirname(promptPath);
3292
- if (!fs9.existsSync(dir)) fs9.mkdirSync(dir, { recursive: true });
3293
- fs9.writeFileSync(promptPath, content, "utf-8");
3559
+ const dir = path10.dirname(promptPath);
3560
+ if (!fs10.existsSync(dir)) fs10.mkdirSync(dir, { recursive: true });
3561
+ fs10.writeFileSync(promptPath, content, "utf-8");
3294
3562
  return promptPath;
3295
3563
  }
3296
3564
  async function openInEditor(editorValue, config, filePath, cwd) {
@@ -3342,14 +3610,6 @@ async function copyToClipboardSafe(value) {
3342
3610
  return false;
3343
3611
  }
3344
3612
  }
3345
- function resolveSyncEndpoint(dashboardUrl) {
3346
- try {
3347
- const u = new URL(dashboardUrl);
3348
- return `${u.origin}/api/sync`;
3349
- } catch (e) {
3350
- return "http://localhost:3000/api/sync";
3351
- }
3352
- }
3353
3613
  function parseMemorySection2(memory, title) {
3354
3614
  const lines = memory.split(/\r?\n/);
3355
3615
  const idx = lines.findIndex((line) => line.trim().toLowerCase() === `${title.toLowerCase()}:`);
@@ -3470,7 +3730,7 @@ function normalizeContinuationAgent(value, fallback) {
3470
3730
  return "custom";
3471
3731
  }
3472
3732
  function resolveContinuationPath(cwd) {
3473
- return path9.join(getConfigDir(cwd), "continuation-prompt.md");
3733
+ return path10.join(getConfigDir(cwd), "continuation-prompt.md");
3474
3734
  }
3475
3735
  function extractMemoryValue(memory, label) {
3476
3736
  var _a2;
@@ -3636,8 +3896,8 @@ async function initializeRunTrim(cwd, options = {}) {
3636
3896
  spinner.stop();
3637
3897
  const configDir = getConfigDir(cwd);
3638
3898
  const runsDir = getRunsDir(cwd);
3639
- if (!fs9.existsSync(configDir)) fs9.mkdirSync(configDir, { recursive: true });
3640
- if (!fs9.existsSync(runsDir)) fs9.mkdirSync(runsDir, { recursive: true });
3899
+ if (!fs10.existsSync(configDir)) fs10.mkdirSync(configDir, { recursive: true });
3900
+ if (!fs10.existsSync(runsDir)) fs10.mkdirSync(runsDir, { recursive: true });
3641
3901
  const existingConfig = hadConfig ? loadConfig(cwd) : null;
3642
3902
  const baseConfig = __spreadValues(__spreadValues({}, DEFAULT_CONFIG), detectProjectInfo(cwd));
3643
3903
  const nextConfig = options.refresh && existingConfig ? __spreadValues({}, existingConfig) : baseConfig;
@@ -3648,20 +3908,20 @@ async function initializeRunTrim(cwd, options = {}) {
3648
3908
  saveConfig(nextConfig, cwd);
3649
3909
  writeProjectAudit(baseline, cwd);
3650
3910
  writeRules(baseline, cwd);
3651
- const memoryPath = path9.join(getConfigDir(cwd), "memory.md");
3911
+ const memoryPath = path10.join(getConfigDir(cwd), "memory.md");
3652
3912
  const hasRuns = loadAllRuns(cwd).length > 0;
3653
3913
  if (hasRuns) {
3654
3914
  const latest = loadLatestRun(cwd);
3655
3915
  if (latest) writeMemoryFromRuns(latest, loadAllRuns(cwd), nextConfig, cwd);
3656
3916
  } else {
3657
- fs9.writeFileSync(memoryPath, buildBaselineMemoryMarkdown(baseline), "utf-8");
3917
+ fs10.writeFileSync(memoryPath, buildBaselineMemoryMarkdown(baseline), "utf-8");
3658
3918
  }
3659
3919
  ensureStarterPromptIfMissing(cwd);
3660
- const gitignorePath = path9.join(cwd, ".gitignore");
3661
- if (fs9.existsSync(gitignorePath)) {
3662
- const content = fs9.readFileSync(gitignorePath, "utf-8");
3920
+ const gitignorePath = path10.join(cwd, ".gitignore");
3921
+ if (fs10.existsSync(gitignorePath)) {
3922
+ const content = fs10.readFileSync(gitignorePath, "utf-8");
3663
3923
  if (!content.includes(".runtrim/runs")) {
3664
- fs9.appendFileSync(gitignorePath, "\n# RunTrim run history\n.runtrim/runs/\n");
3924
+ fs10.appendFileSync(gitignorePath, "\n# RunTrim run history\n.runtrim/runs/\n");
3665
3925
  }
3666
3926
  }
3667
3927
  return { ok: true };
@@ -3758,8 +4018,8 @@ async function runPrepareTask(task, options) {
3758
4018
  }
3759
4019
  async function tryLaunchPanelMonitorDetached(cwd) {
3760
4020
  var _a2, _b;
3761
- const entry = (_a2 = resolveCliLauncherPath()) != null ? _a2 : path9.resolve(cwd, "dist-cli", "runtrim.cjs");
3762
- if (!fs9.existsSync(entry)) return false;
4021
+ const entry = (_a2 = resolveCliLauncherPath()) != null ? _a2 : path10.resolve(cwd, "dist-cli", "runtrim.cjs");
4022
+ if (!fs10.existsSync(entry)) return false;
3763
4023
  try {
3764
4024
  const child = execa3(process.execPath, [entry, "panel", "--monitor"], {
3765
4025
  cwd,
@@ -3905,13 +4165,274 @@ program.command("start").description("Guided RunTrim onboarding and daily loop")
3905
4165
  console.log("");
3906
4166
  }
3907
4167
  });
3908
- program.command("init").description("Initialize RunTrim in the current project").option("--refresh", "Refresh baseline audit/rules/memory without overwriting config").action(async (options) => {
4168
+ var PROTOCOL_BLOCK_START = "<!-- RUNTRIM_PROTOCOL_START -->";
4169
+ var PROTOCOL_BLOCK_END = "<!-- RUNTRIM_PROTOCOL_END -->";
4170
+ var PROTOCOL_POINTER_BLOCK = `
4171
+ ${PROTOCOL_BLOCK_START}
4172
+ This repo uses RunTrim as the guarded AI coding protocol.
4173
+ Before editing code, read RUNTRIM.md.
4174
+ Start every task with: runtrim go "<task>"
4175
+ Stay inside .runtrim/contracts/latest.md.
4176
+ After edits, ask the user to run: runtrim finish
4177
+ ${PROTOCOL_BLOCK_END}
4178
+ `;
4179
+ function upsertProtocolBlock(filePath) {
4180
+ if (!fs10.existsSync(filePath)) return "skipped";
4181
+ const content = fs10.readFileSync(filePath, "utf-8");
4182
+ const startIdx = content.indexOf(PROTOCOL_BLOCK_START);
4183
+ const endIdx = content.indexOf(PROTOCOL_BLOCK_END);
4184
+ if (startIdx !== -1 && endIdx !== -1) {
4185
+ const before = content.slice(0, startIdx);
4186
+ const after = content.slice(endIdx + PROTOCOL_BLOCK_END.length);
4187
+ const newContent = before + PROTOCOL_POINTER_BLOCK.trimStart() + after.replace(/^\n/, "");
4188
+ if (newContent === content) return "unchanged";
4189
+ fs10.writeFileSync(filePath, newContent, "utf-8");
4190
+ return "updated";
4191
+ }
4192
+ fs10.writeFileSync(filePath, content.trimEnd() + "\n" + PROTOCOL_POINTER_BLOCK, "utf-8");
4193
+ return "updated";
4194
+ }
4195
+ function createMinimalAgentPointerFile(filePath, filename) {
4196
+ const label = filename === "CLAUDE.md" ? "Claude Code" : "AI agents";
4197
+ const content = [
4198
+ `# ${label} Instructions`,
4199
+ "",
4200
+ "This repo uses RunTrim as the guarded AI coding protocol.",
4201
+ "Read RUNTRIM.md before editing any code.",
4202
+ "",
4203
+ PROTOCOL_POINTER_BLOCK.trim(),
4204
+ ""
4205
+ ].join("\n");
4206
+ fs10.writeFileSync(filePath, content, "utf-8");
4207
+ }
4208
+ function installProtocol(cwd, baseline, opts = {}) {
4209
+ var _a2, _b, _c, _d, _e, _f, _g;
4210
+ const configDir = getConfigDir(cwd);
4211
+ const now = (/* @__PURE__ */ new Date()).toISOString();
4212
+ const extraFolders = [
4213
+ path10.join(configDir, "contracts"),
4214
+ path10.join(configDir, "memory"),
4215
+ path10.join(configDir, "bridge"),
4216
+ path10.join(configDir, "reports")
4217
+ ];
4218
+ const createdFolders = [];
4219
+ for (const dir of extraFolders) {
4220
+ if (!fs10.existsSync(dir)) {
4221
+ fs10.mkdirSync(dir, { recursive: true });
4222
+ createdFolders.push(dir.replace(cwd + path10.sep, "").replace(/\\/g, "/"));
4223
+ }
4224
+ }
4225
+ const scripts = (_a2 = baseline.scripts) != null ? _a2 : {};
4226
+ const buildCmd = (_d = (_c = (_b = scripts["build"]) != null ? _b : scripts["build:web"]) != null ? _c : scripts["build:all"]) != null ? _d : null;
4227
+ const testCmd = (_f = (_e = scripts["test"]) != null ? _e : scripts["test:run"]) != null ? _f : null;
4228
+ const runtrimMdPath = path10.join(cwd, "RUNTRIM.md");
4229
+ const runtrimMdExists = fs10.existsSync(runtrimMdPath);
4230
+ const runtrimMd = [
4231
+ "# RunTrim Protocol",
4232
+ "",
4233
+ "This repo uses RunTrim as the guarded AI coding control layer.",
4234
+ "",
4235
+ "## Starting an AI coding task",
4236
+ "",
4237
+ "Before any agent touches code, run:",
4238
+ "",
4239
+ "```",
4240
+ 'runtrim go "<describe the task>"',
4241
+ "```",
4242
+ "",
4243
+ "This creates a scoped contract, loads project memory, and generates the guarded prompt for your agent.",
4244
+ "",
4245
+ "## Using your agent",
4246
+ "",
4247
+ "Paste the guarded prompt into Claude Code, Codex, Cursor, or any other AI coding agent.",
4248
+ "The agent receives allowed scope, forbidden areas, stop rules, and verification requirements.",
4249
+ "",
4250
+ "## After edits",
4251
+ "",
4252
+ "Run:",
4253
+ "",
4254
+ "```",
4255
+ "runtrim finish",
4256
+ "```",
4257
+ "",
4258
+ "This checks changed files, detects drift, scores risk, and saves the run report.",
4259
+ "",
4260
+ "## If you are an AI coding agent",
4261
+ "",
4262
+ "1. Read `.runtrim/contracts/latest.md` before touching any file.",
4263
+ "2. Stay inside the allowed scope defined in the contract.",
4264
+ "3. Do not touch forbidden systems or unrelated files.",
4265
+ "4. Stop immediately if scope must expand beyond the contract.",
4266
+ "5. Do not read, write, or reference `.env` files or secrets.",
4267
+ "6. Do not refactor code outside the direct task.",
4268
+ "7. After editing, tell the user to run: `runtrim finish`",
4269
+ "",
4270
+ "## Active contract",
4271
+ "",
4272
+ "If `.runtrim/contracts/latest.md` exists, it contains the active task contract.",
4273
+ "Follow it exactly.",
4274
+ "",
4275
+ "---",
4276
+ `Generated by RunTrim. Updated: ${now}`
4277
+ ].join("\n");
4278
+ fs10.writeFileSync(runtrimMdPath, runtrimMd, "utf-8");
4279
+ const projectJsonPath = path10.join(configDir, "project.json");
4280
+ const projectJsonExists = fs10.existsSync(projectJsonPath);
4281
+ const projectJson = {
4282
+ name: baseline.projectName,
4283
+ stack: baseline.detectedStack,
4284
+ packageManager: baseline.packageManager,
4285
+ buildCommand: buildCmd,
4286
+ testCommand: testCmd,
4287
+ detectedAt: now
4288
+ };
4289
+ fs10.writeFileSync(projectJsonPath, JSON.stringify(projectJson, null, 2), "utf-8");
4290
+ const policiesPath = path10.join(configDir, "policies.json");
4291
+ const policiesJsonExists = fs10.existsSync(policiesPath);
4292
+ const detectedSensitive = ((_g = baseline.riskSurfaces) != null ? _g : []).map((s) => s.type.toLowerCase());
4293
+ const policies = {
4294
+ version: 1,
4295
+ protected: [
4296
+ ".env*",
4297
+ "secrets",
4298
+ "*.key",
4299
+ "*.pem",
4300
+ "auth/**",
4301
+ "middleware.ts",
4302
+ "prisma/schema.prisma",
4303
+ "prisma/migrations/**",
4304
+ "database/migrations/**",
4305
+ "stripe/**",
4306
+ "billing/**",
4307
+ "payment/**",
4308
+ "webhooks/**",
4309
+ "package-lock.json",
4310
+ "pnpm-lock.yaml",
4311
+ "yarn.lock",
4312
+ ".next/**",
4313
+ "dist/**",
4314
+ "build/**",
4315
+ "node_modules/**"
4316
+ ],
4317
+ sensitive: [
4318
+ "auth",
4319
+ "billing",
4320
+ "payment",
4321
+ "middleware",
4322
+ "database",
4323
+ "schema",
4324
+ "migrations",
4325
+ "env",
4326
+ "secrets",
4327
+ "webhooks",
4328
+ ...detectedSensitive.filter((s) => !["auth", "billing", "payment", "middleware", "database", "env", "secrets", "webhooks"].includes(s))
4329
+ ],
4330
+ note: "These areas require explicit task scope before any agent edits.",
4331
+ updatedAt: now
4332
+ };
4333
+ fs10.writeFileSync(policiesPath, JSON.stringify(policies, null, 2), "utf-8");
4334
+ const baselineMdPath = path10.join(configDir, "memory", "baseline.md");
4335
+ const baselineMdExists = fs10.existsSync(baselineMdPath);
4336
+ const protectedList = policies.protected.slice(0, 10).map((p) => `- ${p}`).join("\n");
4337
+ const stackLine = baseline.detectedStack.join(", ") || "unknown";
4338
+ const baselineMd = [
4339
+ "# RunTrim Memory \u2014 Baseline",
4340
+ "",
4341
+ `Project: ${baseline.projectName}`,
4342
+ `Stack: ${stackLine}`,
4343
+ `Package manager: ${baseline.packageManager}`,
4344
+ ...buildCmd ? [`Build: ${buildCmd}`] : [],
4345
+ ...testCmd ? [`Test: ${testCmd}`] : [],
4346
+ "",
4347
+ "## Protected areas",
4348
+ "",
4349
+ protectedList,
4350
+ "",
4351
+ "## Project rules",
4352
+ "",
4353
+ '- Start every AI task with: runtrim go "<task>"',
4354
+ "- Stay inside the scoped contract.",
4355
+ "- Run runtrim finish after agent edits.",
4356
+ "- No unrelated refactors during a task.",
4357
+ "- Never touch .env files.",
4358
+ "",
4359
+ "## Prior agent decisions",
4360
+ "",
4361
+ "No prior runs recorded. This is the baseline for this project.",
4362
+ "",
4363
+ "---",
4364
+ `Created by runtrim init. Updated: ${now}`
4365
+ ].join("\n");
4366
+ fs10.writeFileSync(baselineMdPath, baselineMd, "utf-8");
4367
+ const agentResults = [];
4368
+ const agentTargets = ["CLAUDE.md", "AGENTS.md"];
4369
+ for (const filename of agentTargets) {
4370
+ const filePath = path10.join(cwd, filename);
4371
+ if (fs10.existsSync(filePath)) {
4372
+ const result = upsertProtocolBlock(filePath);
4373
+ if (result !== "skipped") agentResults.push({ file: filename, result });
4374
+ } else if (opts.agentFiles) {
4375
+ createMinimalAgentPointerFile(filePath, filename);
4376
+ agentResults.push({ file: filename, result: "created" });
4377
+ } else {
4378
+ agentResults.push({ file: filename, result: "skipped" });
4379
+ }
4380
+ }
4381
+ let cursorResult = "skipped";
4382
+ const cursorDir = path10.join(cwd, ".cursor");
4383
+ const cursorExists = fs10.existsSync(cursorDir);
4384
+ if (opts.cursor || cursorExists) {
4385
+ const rulesDir = path10.join(cursorDir, "rules");
4386
+ const mdcPath = path10.join(rulesDir, "runtrim.mdc");
4387
+ if (!fs10.existsSync(rulesDir)) fs10.mkdirSync(rulesDir, { recursive: true });
4388
+ const existed = fs10.existsSync(mdcPath);
4389
+ const cursorMdc = [
4390
+ "---",
4391
+ "description: RunTrim guarded AI coding protocol",
4392
+ "alwaysApply: true",
4393
+ "---",
4394
+ "",
4395
+ "# RunTrim Protocol",
4396
+ "",
4397
+ "This repo uses RunTrim as the guarded AI coding control layer.",
4398
+ "",
4399
+ "## Before editing any code",
4400
+ "",
4401
+ "1. Read `RUNTRIM.md` in the repo root.",
4402
+ "2. If `.runtrim/contracts/latest.md` exists, read the active contract.",
4403
+ "",
4404
+ "## Rules",
4405
+ "",
4406
+ "- Stay inside the allowed scope defined in the contract.",
4407
+ "- Do not touch forbidden files or unrelated systems.",
4408
+ "- Stop immediately if scope must expand.",
4409
+ "- Do not read or write `.env` files.",
4410
+ "- Do not refactor outside the task scope.",
4411
+ "",
4412
+ "## After editing",
4413
+ "",
4414
+ "Tell the user to run: `runtrim finish`"
4415
+ ].join("\n");
4416
+ fs10.writeFileSync(mdcPath, cursorMdc, "utf-8");
4417
+ cursorResult = existed ? "updated" : "created";
4418
+ }
4419
+ return {
4420
+ runtrimMd: runtrimMdExists ? "updated" : "created",
4421
+ projectJson: projectJsonExists ? "updated" : "created",
4422
+ policiesJson: policiesJsonExists ? "updated" : "created",
4423
+ baselineMd: baselineMdExists ? "updated" : "created",
4424
+ folders: createdFolders,
4425
+ agentFiles: agentResults,
4426
+ cursorRules: cursorResult
4427
+ };
4428
+ }
4429
+ 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) => {
3909
4430
  var _a2;
3910
4431
  const cwd = process.cwd();
3911
4432
  const allowed = await ensureRepoAllowedForFree(cwd);
3912
4433
  if (!allowed) return;
3913
4434
  console.log("");
3914
- console.log(BOLD("RunTrim") + DIM(" init"));
4435
+ console.log(GO_ACCENT.bold("RunTrim init"));
3915
4436
  console.log("");
3916
4437
  const initResult = await initializeRunTrim(cwd, {
3917
4438
  refresh: options.refresh,
@@ -3919,33 +4440,46 @@ program.command("init").description("Initialize RunTrim in the current project")
3919
4440
  });
3920
4441
  if (!initResult.ok) return;
3921
4442
  const baseline = (_a2 = loadProjectAudit(cwd)) != null ? _a2 : performBaselineProjectAudit(cwd, null);
3922
- const scriptNames = Object.keys(baseline.scripts);
3923
- const starterCreated = fs9.existsSync(path9.join(getConfigDir(cwd), "latest-prompt.md"));
3924
- console.log(ACCENT.bold(" RunTrim init"));
3925
- console.log("");
3926
- console.log(DIM(" Project detected"));
3927
- console.log(DIM(" Name ") + chalk.white(baseline.projectName));
3928
- console.log(DIM(" Stack ") + chalk.white(baseline.detectedStack.join(", ") || "unknown"));
3929
- console.log(DIM(" Package ") + chalk.white(baseline.packageManager));
3930
- console.log("");
3931
- console.log(DIM(" Scripts found"));
3932
- console.log(DIM(" ") + chalk.white(scriptNames.length ? scriptNames.join(", ") : "none"));
4443
+ const protocol = installProtocol(cwd, baseline, {
4444
+ agentFiles: options.agentFiles,
4445
+ cursor: options.cursor
4446
+ });
4447
+ const stackLine = baseline.detectedStack.length ? baseline.detectedStack.join(" + ") : "unknown stack";
4448
+ console.log(DIM(" Project"));
4449
+ console.log(chalk.white(" " + baseline.projectName));
4450
+ console.log(DIM(" " + stackLine));
3933
4451
  console.log("");
3934
- console.log(DIM(" Risk surfaces"));
3935
- for (const s of baseline.riskSurfaces.slice(0, 8)) {
3936
- console.log(DIM(" - ") + chalk.white(s.type));
4452
+ console.log(DIM(" Protocol"));
4453
+ const protocolFiles = [
4454
+ ["RUNTRIM.md", protocol.runtrimMd],
4455
+ [".runtrim/project.json", protocol.projectJson],
4456
+ [".runtrim/policies.json", protocol.policiesJson],
4457
+ [".runtrim/memory/baseline.md", protocol.baselineMd]
4458
+ ];
4459
+ for (const [file, result] of protocolFiles) {
4460
+ console.log(DIM(" ") + chalk.white(file.padEnd(34)) + DIM(result));
3937
4461
  }
3938
4462
  console.log("");
3939
- console.log(DIM(options.refresh ? " Files refreshed" : " Files created"));
3940
- console.log(DIM(" .runtrim/config.json"));
3941
- console.log(DIM(" .runtrim/project-audit.json"));
3942
- console.log(DIM(" .runtrim/rules.md"));
3943
- console.log(DIM(" .runtrim/memory.md"));
3944
- console.log(DIM(" .runtrim/runs/"));
3945
- if (starterCreated) console.log(DIM(" .runtrim/latest-prompt.md"));
4463
+ console.log(DIM(" Agent pointers"));
4464
+ for (const { file, result } of protocol.agentFiles) {
4465
+ const color = result === "skipped" ? DIM : chalk.white;
4466
+ console.log(DIM(" ") + color(file.padEnd(34)) + DIM(result));
4467
+ }
4468
+ if (protocol.cursorRules !== "skipped") {
4469
+ console.log(DIM(" ") + chalk.white(".cursor/rules/runtrim.mdc".padEnd(34)) + DIM(protocol.cursorRules));
4470
+ } else if (options.cursor) {
4471
+ console.log(DIM(" Cursor rules skipped (.cursor/ not found and --cursor not passed)"));
4472
+ }
3946
4473
  console.log("");
4474
+ if (protocol.folders.length > 0) {
4475
+ console.log(DIM(" Folders created"));
4476
+ for (const f of protocol.folders) {
4477
+ console.log(DIM(" " + f + "/"));
4478
+ }
4479
+ console.log("");
4480
+ }
3947
4481
  console.log(DIM(" Next"));
3948
- console.log(chalk.white(' runtrim prepare "describe your next AI coding task"'));
4482
+ console.log(chalk.white(' runtrim go "your first task"'));
3949
4483
  console.log("");
3950
4484
  });
3951
4485
  var agentCommand = program.command("agent").description("Show or configure local agent execution settings");
@@ -4538,8 +5072,8 @@ program.command("run <task>").description("Guard then run configured local agent
4538
5072
  console.log("");
4539
5073
  return;
4540
5074
  }
4541
- const outputPath = path9.join(getRunsDir(cwd), `${run.id}.output.txt`);
4542
- fs9.writeFileSync(outputPath, `# stdout
5075
+ const outputPath = path10.join(getRunsDir(cwd), `${run.id}.output.txt`);
5076
+ fs10.writeFileSync(outputPath, `# stdout
4543
5077
  ${stdout}
4544
5078
 
4545
5079
  # stderr
@@ -4616,7 +5150,8 @@ program.command("prepare <task>").description("Prepare a guarded prompt without
4616
5150
  });
4617
5151
  }
4618
5152
  );
4619
- program.command("go <task>").description("Daily shortcut: initialize if needed, prepare a guarded prompt, and show next steps").option("--monitor", "Open local panel monitor in the background (best effort)").action(async (task, options) => {
5153
+ 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) => {
5154
+ var _a2, _b, _c, _d, _e, _f, _g;
4620
5155
  const cwd = process.cwd();
4621
5156
  const allowed = await ensureRepoAllowedForFree(cwd);
4622
5157
  if (!allowed) return;
@@ -4624,45 +5159,180 @@ program.command("go <task>").description("Daily shortcut: initialize if needed,
4624
5159
  const initResult = await initializeRunTrim(cwd, { allowOverwritePrompt: false });
4625
5160
  if (!initResult.ok) return;
4626
5161
  }
4627
- const originalLog = console.log;
4628
- const originalError = console.error;
4629
- console.log = () => void 0;
4630
- console.error = () => void 0;
4631
- try {
4632
- await runPrepareTask(task, { showHeader: false, copy: true });
4633
- } finally {
4634
- console.log = originalLog;
4635
- console.error = originalError;
4636
- }
4637
- if (!configExists(cwd)) return;
4638
5162
  const config = loadConfig(cwd);
4639
- const promptPath = resolvePromptPath(config, cwd);
4640
- const promptValue = fs9.existsSync(promptPath) ? fs9.readFileSync(promptPath, "utf-8") : "";
4641
- const copied = promptValue ? await copyToClipboardSafe(promptValue) : false;
4642
- if (options.monitor) {
4643
- void tryLaunchPanelMonitorDetached(cwd);
5163
+ const globalAuth = loadGlobalAuth();
5164
+ const rawToken = (_b = (_a2 = globalAuth == null ? void 0 : globalAuth.token) != null ? _a2 : config.syncToken) != null ? _b : null;
5165
+ const apiBase = resolveApiBase(config);
5166
+ if (rawToken == null ? void 0 : rawToken.startsWith("rt_live_")) {
5167
+ let serverResult = null;
5168
+ try {
5169
+ const res = await fetch(`${apiBase}/api/cli/usage/bridge-run`, {
5170
+ method: "POST",
5171
+ headers: { Authorization: `Bearer ${rawToken}` }
5172
+ });
5173
+ if (res.ok) {
5174
+ const body = await res.json();
5175
+ serverResult = {
5176
+ allowed: body.allowed,
5177
+ plan: body.plan,
5178
+ used: (_c = body.usage.bridgeRunsUsed) != null ? _c : 0,
5179
+ limit: body.usage.bridgeRunsLimit
5180
+ };
5181
+ }
5182
+ } catch (e) {
5183
+ }
5184
+ if (serverResult !== null && !serverResult.allowed) {
5185
+ printBridgeLimitMessage(serverResult.used);
5186
+ return;
5187
+ }
5188
+ } else {
5189
+ const check = checkAndIncrementLocalUsage();
5190
+ if (!check.allowed) {
5191
+ printBridgeLimitMessage(check.used);
5192
+ return;
5193
+ }
5194
+ }
5195
+ const auditSpinner = oraFactory({ text: " Auditing task...", color: "blue" }).start();
5196
+ await new Promise((r) => setTimeout(r, 180));
5197
+ const audit = auditTask(task, config, cwd);
5198
+ auditSpinner.stop();
5199
+ const contract = generateContract(task, audit, config);
5200
+ if (contract.isBlocked && contract.splitReport) {
5201
+ const sr = contract.splitReport;
5202
+ updateRun(saveRun(task, audit, contract, cwd).id, { status: "blocked" }, cwd);
5203
+ console.log("");
5204
+ console.log(GO_ACCENT.bold("RunTrim go"));
5205
+ console.log("");
5206
+ console.log(chalk.red.bold(" SPLIT REQUIRED"));
5207
+ console.log("");
5208
+ console.log(DIM(" Task ") + chalk.white(truncate(task, 60)));
5209
+ console.log(DIM(" Risk ") + chalk.red("CRITICAL"));
5210
+ console.log("");
5211
+ console.log(DIM(" This task crosses multiple high-risk systems."));
5212
+ console.log(DIM(" Running it in one agent session would cause scope drift and token waste."));
5213
+ console.log("");
5214
+ console.log(DIM(" Detected: ") + chalk.white(sr.detectedSystems.join(", ")));
5215
+ console.log("");
5216
+ for (const step of sr.recommendedSplit) {
5217
+ console.log(DIM(" ") + chalk.white(step));
5218
+ }
5219
+ console.log("");
5220
+ console.log(DIM(" Estimated waste avoided: ") + ACCENT(sr.estimatedWasteAvoided));
5221
+ console.log("");
5222
+ return;
5223
+ }
5224
+ const runs = loadAllRuns(cwd);
5225
+ const projectAudit = loadProjectAudit(cwd);
5226
+ const projectName = (_d = projectAudit == null ? void 0 : projectAudit.projectName) != null ? _d : path10.basename(cwd);
5227
+ const memoryMarkdown = (() => {
5228
+ try {
5229
+ return readMemory(cwd);
5230
+ } catch (e) {
5231
+ return null;
5232
+ }
5233
+ })();
5234
+ const memoryUsed = Boolean(memoryMarkdown && memoryMarkdown.trim().length > 50);
5235
+ const run = saveRun(task, audit, contract, cwd);
5236
+ updateRun(run.id, {
5237
+ status: "guarded",
5238
+ bridgeMode: true,
5239
+ tokenBudget: deriveBridgeContext(task, contract, runs, projectName).tokenBudget,
5240
+ memoryUsed
5241
+ }, cwd);
5242
+ const bridgeCtx = deriveBridgeContext(task, contract, runs, projectName);
5243
+ let bridgeWritten = [];
5244
+ let bridgeManagedPaths = [];
5245
+ if (options.bridge !== false) {
5246
+ const result = writeBridgeFiles(bridgeCtx, cwd);
5247
+ bridgeWritten = result.written;
5248
+ bridgeManagedPaths = result.managedPaths;
5249
+ }
5250
+ const rawPrompt = contract.contractText;
5251
+ const fullPrompt = options.bridge !== false ? buildBridgePrompt(rawPrompt, bridgeCtx) : rawPrompt;
5252
+ const promptPath = writeLatestPromptFile(fullPrompt, config, cwd);
5253
+ const promptRelative = ".runtrim/latest-prompt.md";
5254
+ if (!bridgeManagedPaths.includes(promptRelative)) {
5255
+ bridgeManagedPaths.push(promptRelative);
5256
+ }
5257
+ const doCopy = options.clipboard !== false;
5258
+ const copied = doCopy ? await copyToClipboardSafe(fullPrompt) : false;
5259
+ if (options.monitor) void tryLaunchPanelMonitorDetached(cwd);
5260
+ updateRun(run.id, { bridgeManagedFiles: bridgeManagedPaths }, cwd);
5261
+ let synced = false;
5262
+ if (options.sync !== false) {
5263
+ const globalAuth2 = loadGlobalAuth();
5264
+ const rawToken2 = (_f = (_e = globalAuth2 == null ? void 0 : globalAuth2.token) != null ? _e : config.syncToken) != null ? _f : null;
5265
+ if (rawToken2 == null ? void 0 : rawToken2.startsWith("rt_live_")) {
5266
+ try {
5267
+ const freshRuns = loadAllRuns(cwd);
5268
+ const payload = buildSyncPayload({
5269
+ cwd,
5270
+ projectName,
5271
+ config,
5272
+ projectAudit: projectAudit != null ? projectAudit : null,
5273
+ memoryMarkdown: memoryMarkdown != null ? memoryMarkdown : "",
5274
+ runs: freshRuns
5275
+ });
5276
+ const apiBase2 = resolveApiBase(config);
5277
+ const r = await fetch(`${apiBase2}/api/sync`, {
5278
+ method: "POST",
5279
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${rawToken2}` },
5280
+ body: JSON.stringify(payload)
5281
+ });
5282
+ synced = r.ok;
5283
+ } catch (e) {
5284
+ }
5285
+ }
4644
5286
  }
5287
+ const riskColor = (_g = { low: chalk.green, medium: chalk.yellow, high: chalk.hex("#FF8C00"), critical: chalk.red }[bridgeCtx.riskLevel]) != null ? _g : chalk.white;
4645
5288
  console.log("");
4646
5289
  console.log(GO_ACCENT.bold("RunTrim go"));
4647
5290
  console.log("");
4648
5291
  console.log(GO_ACCENT.bold("Task"));
4649
- console.log(chalk.white(task));
5292
+ console.log(chalk.white(" " + task));
4650
5293
  console.log("");
4651
- console.log(GO_ACCENT.bold("Guarded prompt"));
4652
- console.log(copied ? chalk.white("Copied to clipboard.") : DIM("Clipboard unavailable. Prompt saved to .runtrim/latest-prompt.md"));
5294
+ console.log(GO_ACCENT.bold("Memory"));
5295
+ console.log(DIM(" " + (memoryUsed ? `Loaded ${runs.length} prior run${runs.length === 1 ? "" : "s"} and project context.` : "No prior runs. Starting from project context.")));
4653
5296
  console.log("");
4654
- console.log(GO_ACCENT.bold("Next"));
4655
- console.log(chalk.white("1. Paste into your preferred coding agent, like Claude, Codex, Cursor, ChatGPT, Kimi, or another agent."));
4656
- console.log(chalk.white("2. Keep the local panel open:"));
4657
- console.log(chalk.white(" runtrim panel --monitor"));
4658
- console.log(chalk.white("3. After edits:"));
4659
- console.log(chalk.white(" runtrim check"));
5297
+ console.log(GO_ACCENT.bold("Contract"));
5298
+ console.log(DIM(" Risk ") + riskColor(bridgeCtx.riskLevel));
5299
+ console.log(DIM(" Token budget ") + chalk.white("~" + bridgeCtx.tokenBudget.toLocaleString()));
5300
+ if (bridgeCtx.allowedScope.length > 0) {
5301
+ console.log(DIM(" Allowed ") + chalk.white(truncate(bridgeCtx.allowedScope.slice(0, 2).join(", "), 60)));
5302
+ }
5303
+ if (bridgeCtx.forbiddenScope.length > 0) {
5304
+ console.log(DIM(" Forbidden ") + chalk.white(truncate(bridgeCtx.forbiddenScope.slice(0, 2).join(", "), 60)));
5305
+ }
5306
+ console.log(DIM(" Run saved ") + chalk.white(`.runtrim/runs/${run.id}.json`));
4660
5307
  console.log("");
4661
- console.log(GO_ACCENT.bold("Why this helps"));
4662
- console.log(DIM("RunTrim keeps each run scoped, remembered, and easier to continue."));
5308
+ if (bridgeWritten.length > 0) {
5309
+ console.log(GO_ACCENT.bold("Bridge"));
5310
+ for (const f of bridgeWritten) {
5311
+ console.log(DIM(" ") + chalk.white(f));
5312
+ }
5313
+ console.log("");
5314
+ }
5315
+ if (options.sync !== false) {
5316
+ console.log(GO_ACCENT.bold("Cloud sync"));
5317
+ console.log(DIM(" ") + (synced ? chalk.white("Synced.") : DIM("Skipped. Run runtrim login to connect your dashboard.")));
5318
+ console.log("");
5319
+ }
5320
+ console.log(GO_ACCENT.bold("Prompt"));
5321
+ if (copied) {
5322
+ console.log(chalk.white(" Copied to clipboard."));
5323
+ } else if (!doCopy) {
5324
+ console.log(chalk.white(" Saved to: " + promptPath));
5325
+ } else {
5326
+ console.log(DIM(" Clipboard unavailable. Saved to: " + promptPath));
5327
+ }
5328
+ if (options.print) {
5329
+ console.log("");
5330
+ console.log(fullPrompt);
5331
+ }
4663
5332
  console.log("");
4664
- console.log(GO_ACCENT.bold("Need more control?"));
4665
- console.log(chalk.white("Run `runtrim --help` for panel, check, memory and continuation commands."));
5333
+ console.log(GO_ACCENT.bold("Next"));
5334
+ console.log(chalk.white(" Paste the guarded prompt into Claude Code, Codex, Cursor, or your agent."));
5335
+ console.log(chalk.white(" After edits are done, run: runtrim finish"));
4666
5336
  console.log("");
4667
5337
  });
4668
5338
  program.command("check").description("Check the latest run and evaluate agent output").option("--json", "Print machine-readable check summary").action(async (options) => {
@@ -4828,80 +5498,6 @@ program.command("check").description("Check the latest run and evaluate agent ou
4828
5498
  console.log(chalk.white(checkSummary.nextSafeAction));
4829
5499
  console.log("");
4830
5500
  });
4831
- program.command("sync").description("Sync local RunTrim metadata to dashboard").action(async () => {
4832
- var _a2, _b;
4833
- const cwd = process.cwd();
4834
- const allowed = await ensureRepoAllowedForFree(cwd);
4835
- if (!allowed) return;
4836
- const cfg = configExists(cwd) ? loadConfig(cwd) : DEFAULT_CONFIG;
4837
- if (!configExists(cwd)) {
4838
- console.log(chalk.yellow(" No config found. Run: runtrim init"));
4839
- console.log("");
4840
- return;
4841
- }
4842
- const config = loadConfig(cwd);
4843
- if (!config.syncToken) {
4844
- console.log(chalk.yellow(" Sync token missing. Run: runtrim auth set <token>"));
4845
- console.log("");
4846
- return;
4847
- }
4848
- const runs = loadAllRuns(cwd);
4849
- const latestRun = (_a2 = runs[0]) != null ? _a2 : null;
4850
- const audit = loadProjectAudit(cwd);
4851
- const inferredProjectName = (audit == null ? void 0 : audit.projectName) || path9.basename(cwd);
4852
- let memory = readMemory(cwd);
4853
- if (!memory) {
4854
- if (latestRun) {
4855
- memory = writeMemoryFromRuns(latestRun, runs, config, cwd);
4856
- } else if (audit) {
4857
- memory = buildBaselineMemoryMarkdown(audit);
4858
- fs9.writeFileSync(path9.join(getConfigDir(cwd), "memory.md"), memory, "utf-8");
4859
- } else {
4860
- memory = "RunTrim Project Memory\n\nCurrent state:\nNo local runs yet.\n";
4861
- }
4862
- }
4863
- const payload = buildSyncPayload({
4864
- cwd,
4865
- projectName: inferredProjectName,
4866
- config,
4867
- projectAudit: audit,
4868
- memoryMarkdown: memory,
4869
- runs
4870
- });
4871
- const endpoint = resolveSyncEndpoint(config.dashboardUrl);
4872
- try {
4873
- const response = await fetch(endpoint, {
4874
- method: "POST",
4875
- headers: {
4876
- "content-type": "application/json",
4877
- "x-runtrim-sync-token": config.syncToken
4878
- },
4879
- body: JSON.stringify(payload)
4880
- });
4881
- const body = await response.json().catch(() => ({}));
4882
- if (!response.ok) {
4883
- console.log(chalk.red(" Sync failed: " + (body.error || `HTTP ${response.status}`)));
4884
- console.log(DIM(" Endpoint: ") + chalk.white(endpoint));
4885
- if (Array.isArray(body.missing) && body.missing.length > 0) {
4886
- console.log(DIM(" Missing: ") + chalk.white(body.missing.join(", ")));
4887
- }
4888
- console.log("");
4889
- return;
4890
- }
4891
- const syncedRuns = (_b = body.syncedRuns) != null ? _b : payload.runs.length;
4892
- console.log(ACCENT.bold(` Synced project memory and ${syncedRuns} runs.`));
4893
- console.log(DIM(" Open dashboard: ") + chalk.white(config.dashboardUrl));
4894
- console.log("");
4895
- } catch (error) {
4896
- const message = error instanceof Error ? error.message : "Unknown network error";
4897
- console.log(chalk.red(" Sync failed: " + message));
4898
- console.log(DIM(" Endpoint: ") + chalk.white(endpoint));
4899
- console.log(
4900
- DIM(" If your dashboard backend is offline, start it first and ensure env vars are set.")
4901
- );
4902
- console.log("");
4903
- }
4904
- });
4905
5501
  program.command("audit").description("Run baseline project audit and refresh local baseline files").action(() => {
4906
5502
  const cwd = process.cwd();
4907
5503
  if (!configExists(cwd)) {
@@ -4925,7 +5521,7 @@ program.command("audit").description("Run baseline project audit and refresh loc
4925
5521
  const latest = loadLatestRun(cwd);
4926
5522
  if (latest) writeMemoryFromRuns(latest, runs, nextConfig, cwd);
4927
5523
  } else {
4928
- fs9.writeFileSync(path9.join(getConfigDir(cwd), "memory.md"), buildBaselineMemoryMarkdown(baseline), "utf-8");
5524
+ fs10.writeFileSync(path10.join(getConfigDir(cwd), "memory.md"), buildBaselineMemoryMarkdown(baseline), "utf-8");
4929
5525
  }
4930
5526
  console.log("");
4931
5527
  console.log(BOLD("RunTrim") + DIM(" audit"));
@@ -5057,15 +5653,15 @@ program.command("memory").description("Show project memory and latest next safe
5057
5653
  console.log("");
5058
5654
  const config = configExists(cwd) ? loadConfig(cwd) : DEFAULT_CONFIG;
5059
5655
  const audit = (_a2 = loadProjectAudit(cwd)) != null ? _a2 : performBaselineProjectAudit(cwd, null);
5060
- const memoryPath = path9.join(getConfigDir(cwd), "memory.md");
5656
+ const memoryPath = path10.join(getConfigDir(cwd), "memory.md");
5061
5657
  const latestRun = loadLatestRun(cwd);
5062
5658
  if (!latestRun) {
5063
5659
  let memory2 = readMemory(cwd);
5064
5660
  if (!memory2) {
5065
- const memoryDir = path9.dirname(memoryPath);
5066
- if (!fs9.existsSync(memoryDir)) fs9.mkdirSync(memoryDir, { recursive: true });
5661
+ const memoryDir = path10.dirname(memoryPath);
5662
+ if (!fs10.existsSync(memoryDir)) fs10.mkdirSync(memoryDir, { recursive: true });
5067
5663
  memory2 = buildBaselineMemoryMarkdown(audit);
5068
- fs9.writeFileSync(memoryPath, memory2, "utf-8");
5664
+ fs10.writeFileSync(memoryPath, memory2, "utf-8");
5069
5665
  }
5070
5666
  const baselinePrompt = 'runtrim go "your task"';
5071
5667
  if (options.prompt) {
@@ -5206,13 +5802,13 @@ program.command("continue").description("Create a safe continuation prompt from
5206
5802
  const nowIso = (/* @__PURE__ */ new Date()).toISOString();
5207
5803
  const continuationPath = resolveContinuationPath(cwd);
5208
5804
  const latestPromptPath = resolvePromptPath(config, cwd);
5209
- const latestPrompt = fs9.existsSync(latestPromptPath) ? fs9.readFileSync(latestPromptPath, "utf-8").trim() : "";
5805
+ const latestPrompt = fs10.existsSync(latestPromptPath) ? fs10.readFileSync(latestPromptPath, "utf-8").trim() : "";
5210
5806
  let memory = readMemory(cwd);
5211
5807
  if (!memory) {
5212
5808
  if (latestRun) memory = writeMemoryFromRuns(latestRun, allRuns, config, cwd);
5213
5809
  else memory = buildBaselineMemoryMarkdown(audit);
5214
5810
  }
5215
- const projectName = audit.projectName || path9.basename(cwd);
5811
+ const projectName = audit.projectName || path10.basename(cwd);
5216
5812
  const stackLine = audit.detectedStack.length > 0 ? audit.detectedStack.join(", ") : "unknown";
5217
5813
  const diffFiles = dedupeFiles(await getGitDiff(cwd));
5218
5814
  const changedFiles = ((_c = (_b = latestRun == null ? void 0 : latestRun.evaluation) == null ? void 0 : _b.changedFiles) == null ? void 0 : _c.length) ? dedupeFiles(latestRun.evaluation.changedFiles) : diffFiles;
@@ -5388,13 +5984,13 @@ program.command("continue").description("Create a safe continuation prompt from
5388
5984
  promptLines.push("");
5389
5985
  }
5390
5986
  const continuationPrompt = promptLines.join("\n");
5391
- const continuationDir = path9.dirname(continuationPath);
5392
- if (!fs9.existsSync(continuationDir)) fs9.mkdirSync(continuationDir, { recursive: true });
5393
- fs9.writeFileSync(continuationPath, continuationPrompt, "utf-8");
5987
+ const continuationDir = path10.dirname(continuationPath);
5988
+ if (!fs10.existsSync(continuationDir)) fs10.mkdirSync(continuationDir, { recursive: true });
5989
+ fs10.writeFileSync(continuationPath, continuationPrompt, "utf-8");
5394
5990
  const copied = await copyToClipboardSafe(continuationPrompt);
5395
5991
  if (memory) {
5396
5992
  const memoryWithContinuation = updateMemoryWithContinuation(memory, reason, continuationPath, nowIso);
5397
- fs9.writeFileSync(path9.join(getConfigDir(cwd), "memory.md"), memoryWithContinuation, "utf-8");
5993
+ fs10.writeFileSync(path10.join(getConfigDir(cwd), "memory.md"), memoryWithContinuation, "utf-8");
5398
5994
  }
5399
5995
  if (hasConfig) {
5400
5996
  const nextConfig = __spreadProps(__spreadValues({}, config), {
@@ -5505,7 +6101,7 @@ program.command("report").description("Show a summary of all local RunTrim runs"
5505
6101
  console.log(DIM(" " + SECTION));
5506
6102
  console.log("");
5507
6103
  const audit = loadProjectAudit(cwd);
5508
- console.log(DIM(" Project ") + chalk.white((_b = audit == null ? void 0 : audit.projectName) != null ? _b : path9.basename(cwd)));
6104
+ console.log(DIM(" Project ") + chalk.white((_b = audit == null ? void 0 : audit.projectName) != null ? _b : path10.basename(cwd)));
5509
6105
  console.log(DIM(" Focus ") + chalk.white(truncate((_d = (_c = latestRun.contract.contract) == null ? void 0 : _c.cleanedObjective) != null ? _d : latestRun.task, 58)));
5510
6106
  console.log(DIM(" Status ") + chalk.white(formatStatus((_e = latestEval == null ? void 0 : latestEval.status) != null ? _e : latestRun.status)));
5511
6107
  console.log(DIM(" Changed ") + chalk.white(`${(_g = (_f = latestEval == null ? void 0 : latestEval.changedFiles) == null ? void 0 : _f.length) != null ? _g : 0} file${((_i = (_h = latestEval == null ? void 0 : latestEval.changedFiles) == null ? void 0 : _h.length) != null ? _i : 0) === 1 ? "" : "s"}`));
@@ -5568,4 +6164,432 @@ var statusColors = {
5568
6164
  drift_detected: chalk.red,
5569
6165
  blocked: chalk.red
5570
6166
  };
6167
+ var GLOBAL_USAGE_FILE = path10.join(os3.homedir(), ".runtrim", "usage.json");
6168
+ var FREE_BRIDGE_LIMIT_LOCAL = 5;
6169
+ function currentUsagePeriod() {
6170
+ const d = /* @__PURE__ */ new Date();
6171
+ return `${d.getUTCFullYear()}-${String(d.getUTCMonth() + 1).padStart(2, "0")}`;
6172
+ }
6173
+ function loadLocalUsageRuns() {
6174
+ var _a2;
6175
+ try {
6176
+ const raw = JSON.parse(fs10.readFileSync(GLOBAL_USAGE_FILE, "utf-8"));
6177
+ return (_a2 = raw.bridgeRuns) != null ? _a2 : {};
6178
+ } catch (e) {
6179
+ return {};
6180
+ }
6181
+ }
6182
+ function saveLocalUsageRuns(bridgeRuns) {
6183
+ const dir = path10.dirname(GLOBAL_USAGE_FILE);
6184
+ if (!fs10.existsSync(dir)) fs10.mkdirSync(dir, { recursive: true });
6185
+ fs10.writeFileSync(GLOBAL_USAGE_FILE, JSON.stringify({ bridgeRuns }, null, 2), "utf-8");
6186
+ }
6187
+ function checkAndIncrementLocalUsage() {
6188
+ var _a2;
6189
+ const period = currentUsagePeriod();
6190
+ const runs = loadLocalUsageRuns();
6191
+ const used = (_a2 = runs[period]) != null ? _a2 : 0;
6192
+ if (used >= FREE_BRIDGE_LIMIT_LOCAL) {
6193
+ return { allowed: false, used };
6194
+ }
6195
+ runs[period] = used + 1;
6196
+ saveLocalUsageRuns(runs);
6197
+ return { allowed: true, used: used + 1 };
6198
+ }
6199
+ function printBridgeLimitMessage(used) {
6200
+ console.log("");
6201
+ console.log(chalk.red.bold(" Free Bridge limit reached."));
6202
+ console.log("");
6203
+ console.log(chalk.white(` You have used ${used} local guarded run${used === 1 ? "" : "s"} this month.`));
6204
+ console.log(DIM(" Upgrade to Pro for unlimited Bridge Mode, cloud sync, project memory,"));
6205
+ console.log(DIM(" reports, and continuation history."));
6206
+ console.log("");
6207
+ console.log(chalk.white(" https://www.runtrim.com/pricing"));
6208
+ console.log("");
6209
+ }
6210
+ var GLOBAL_AUTH_DIR = path10.join(os3.homedir(), ".runtrim");
6211
+ var GLOBAL_AUTH_FILE = path10.join(GLOBAL_AUTH_DIR, "auth.json");
6212
+ function loadGlobalAuth() {
6213
+ if (!fs10.existsSync(GLOBAL_AUTH_FILE)) return null;
6214
+ try {
6215
+ return JSON.parse(fs10.readFileSync(GLOBAL_AUTH_FILE, "utf-8"));
6216
+ } catch (e) {
6217
+ return null;
6218
+ }
6219
+ }
6220
+ function saveGlobalAuth(auth) {
6221
+ if (!fs10.existsSync(GLOBAL_AUTH_DIR)) {
6222
+ fs10.mkdirSync(GLOBAL_AUTH_DIR, { recursive: true });
6223
+ }
6224
+ fs10.writeFileSync(GLOBAL_AUTH_FILE, JSON.stringify(auth, null, 2));
6225
+ }
6226
+ function resolveApiBase(config) {
6227
+ var _a2;
6228
+ const url = (_a2 = config.dashboardUrl) == null ? void 0 : _a2.trim();
6229
+ if (!url || url.startsWith("http://localhost")) {
6230
+ return "https://www.runtrim.com";
6231
+ }
6232
+ try {
6233
+ return new URL(url).origin;
6234
+ } catch (e) {
6235
+ return "https://www.runtrim.com";
6236
+ }
6237
+ }
6238
+ program.command("login").description("Connect this machine to your RunTrim cloud account").option("--token <token>", "CLI token (skip interactive prompt)").action(async (opts) => {
6239
+ var _a2, _b, _c, _d, _e;
6240
+ console.log("");
6241
+ console.log(BOLD("RunTrim") + DIM(" connect to cloud"));
6242
+ console.log("");
6243
+ const cwd = process.cwd();
6244
+ const existing = loadGlobalAuth();
6245
+ if (existing && !opts.token) {
6246
+ console.log(DIM(" Already connected."));
6247
+ if (existing.email) {
6248
+ console.log(DIM(" Account ") + chalk.white(existing.email));
6249
+ }
6250
+ console.log(DIM(" Token ") + chalk.white("rt_live_..."));
6251
+ console.log("");
6252
+ console.log(DIM(" To reconnect, run: ") + GO_ACCENT("runtrim login --token <new-token>"));
6253
+ console.log("");
6254
+ return;
6255
+ }
6256
+ console.log(DIM(" Get your CLI token from:"));
6257
+ console.log(" " + GO_ACCENT("https://www.runtrim.com/app/connect"));
6258
+ console.log("");
6259
+ let rawToken = (_b = (_a2 = opts.token) == null ? void 0 : _a2.trim()) != null ? _b : "";
6260
+ if (!rawToken) {
6261
+ const answer = await prompts({
6262
+ type: "text",
6263
+ name: "token",
6264
+ message: "Paste your CLI token"
6265
+ });
6266
+ rawToken = (_d = (_c = answer.token) == null ? void 0 : _c.trim()) != null ? _d : "";
6267
+ }
6268
+ if (!rawToken || !rawToken.startsWith("rt_live_")) {
6269
+ console.log(chalk.yellow(" Invalid token. Tokens start with rt_live_"));
6270
+ console.log("");
6271
+ return;
6272
+ }
6273
+ const config = configExists(cwd) ? loadConfig(cwd) : DEFAULT_CONFIG;
6274
+ const apiBase = resolveApiBase(config);
6275
+ const verifyUrl = `${apiBase}/api/cli-token/verify`;
6276
+ const spinner = oraFactory({ text: " Verifying token...", color: "blue" }).start();
6277
+ let email;
6278
+ try {
6279
+ const res = await fetch(verifyUrl, {
6280
+ method: "GET",
6281
+ headers: { Authorization: `Bearer ${rawToken}` }
6282
+ });
6283
+ const body = await res.json();
6284
+ if (!res.ok || !body.ok) {
6285
+ spinner.fail(" Invalid token. " + ((_e = body.error) != null ? _e : ""));
6286
+ console.log("");
6287
+ return;
6288
+ }
6289
+ email = body.email;
6290
+ spinner.succeed(" Token verified.");
6291
+ } catch (e) {
6292
+ spinner.fail(" Could not reach RunTrim server. Check your connection.");
6293
+ console.log("");
6294
+ return;
6295
+ }
6296
+ saveGlobalAuth({ token: rawToken, storedAt: (/* @__PURE__ */ new Date()).toISOString(), email });
6297
+ console.log("");
6298
+ if (email) {
6299
+ console.log(ACCENT.bold(" Connected as ") + chalk.white(email));
6300
+ } else {
6301
+ console.log(ACCENT.bold(" Connected to RunTrim cloud."));
6302
+ }
6303
+ console.log(DIM(" Token stored in ") + chalk.white("~/.runtrim/auth.json"));
6304
+ console.log("");
6305
+ console.log(DIM(" Next: navigate to a project and run ") + GO_ACCENT("runtrim sync"));
6306
+ console.log("");
6307
+ });
6308
+ program.command("finish").description("Bridge Mode: evaluate agent output, check scope, mark run completed, and sync").option("--no-sync", "Skip cloud sync even if a CLI token is configured").action(async (options) => {
6309
+ var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o;
6310
+ const cwd = process.cwd();
6311
+ console.log("");
6312
+ console.log(GO_ACCENT.bold("RunTrim finish"));
6313
+ console.log("");
6314
+ const allRuns = loadAllRuns(cwd);
6315
+ const activeRun = allRuns.find((r) => r.status === "guarded" || r.status === "checked");
6316
+ if (!activeRun) {
6317
+ console.log(chalk.yellow(" No active RunTrim session found."));
6318
+ console.log(DIM(' Start a new session with: runtrim go "<task>"'));
6319
+ console.log("");
6320
+ return;
6321
+ }
6322
+ const config = configExists(cwd) ? loadConfig(cwd) : DEFAULT_CONFIG;
6323
+ const projectAudit = loadProjectAudit(cwd);
6324
+ const projectName = (_a2 = projectAudit == null ? void 0 : projectAudit.projectName) != null ? _a2 : path10.basename(cwd);
6325
+ console.log(DIM(" Run ") + chalk.white(truncate(activeRun.task, 60)));
6326
+ console.log(DIM(" Run ID ") + chalk.white(activeRun.id));
6327
+ console.log("");
6328
+ const allChangedFiles = dedupeFiles(await getGitDiff(cwd));
6329
+ const sessionManagedFiles = (_b = activeRun.bridgeManagedFiles) != null ? _b : [];
6330
+ function isRuntrimOwned(f) {
6331
+ const norm = f.replace(/\\/g, "/").toLowerCase();
6332
+ if (norm.startsWith(".runtrim/")) return true;
6333
+ if (norm === "runtrim.md") return true;
6334
+ if (sessionManagedFiles.some(
6335
+ (m) => m.replace(/\\/g, "/").toLowerCase() === norm
6336
+ )) return true;
6337
+ return false;
6338
+ }
6339
+ const runtrimFiles = [];
6340
+ const agentFiles = [];
6341
+ for (const f of allChangedFiles) {
6342
+ if (isRuntrimOwned(f)) {
6343
+ runtrimFiles.push(f);
6344
+ } else {
6345
+ agentFiles.push(f);
6346
+ }
6347
+ }
6348
+ const changedFiles = agentFiles;
6349
+ const maxFiles = inferMaxFilesFromScope(
6350
+ (_d = (_c = activeRun.contract.contract) == null ? void 0 : _c.relevantScope) != null ? _d : [],
6351
+ config.maxFilesPerRun
6352
+ );
6353
+ const scope = evaluateWatchState({
6354
+ changedFiles,
6355
+ run: activeRun,
6356
+ maxFilesPerRun: maxFiles,
6357
+ strict: false
6358
+ });
6359
+ const evaluation = evaluateAgentOutput(null, changedFiles, {
6360
+ task: activeRun.task,
6361
+ relevantScope: (_f = (_e = activeRun.contract.contract) == null ? void 0 : _e.relevantScope) != null ? _f : [],
6362
+ sensitiveScope: (_h = (_g = activeRun.contract.contract) == null ? void 0 : _g.sensitiveScope) != null ? _h : [],
6363
+ forbiddenScope: (_j = (_i = activeRun.contract.contract) == null ? void 0 : _i.forbiddenScope) != null ? _j : [],
6364
+ runStatus: activeRun.status
6365
+ });
6366
+ let scopeDriftStatus = "passed";
6367
+ if (scope.forbiddenFiles.length > 0) scopeDriftStatus = "forbidden_touched";
6368
+ else if (scope.outOfScopeFiles.length > 0) scopeDriftStatus = "out_of_scope";
6369
+ else if (evaluation.scopeDriftRisk === "high" || evaluation.scopeDriftRisk === "medium")
6370
+ scopeDriftStatus = "drift_detected";
6371
+ const forbiddenCount = scope.forbiddenFiles.length;
6372
+ const reportParts = [];
6373
+ if (changedFiles.length === 0) {
6374
+ reportParts.push("No agent changes detected.");
6375
+ } else {
6376
+ reportParts.push(`${changedFiles.length} file${changedFiles.length === 1 ? "" : "s"} changed.`);
6377
+ }
6378
+ if (forbiddenCount > 0) {
6379
+ reportParts.push(`${forbiddenCount} forbidden file${forbiddenCount === 1 ? "" : "s"} touched.`);
6380
+ } else if (changedFiles.length > 0) {
6381
+ reportParts.push("No forbidden systems touched.");
6382
+ }
6383
+ if (scopeDriftStatus === "passed" && changedFiles.length > 0) {
6384
+ reportParts.push("Changes within contract.");
6385
+ }
6386
+ if (evaluation.memorySummary) reportParts.push(evaluation.memorySummary);
6387
+ const reportSummary = reportParts.join(" ");
6388
+ const continuationPack = evaluation.nextGuardedPrompt || null;
6389
+ if (continuationPack) {
6390
+ const contDir = getConfigDir(cwd);
6391
+ if (!fs10.existsSync(contDir)) fs10.mkdirSync(contDir, { recursive: true });
6392
+ fs10.writeFileSync(path10.join(contDir, "continuation-prompt.md"), continuationPack, "utf-8");
6393
+ }
6394
+ const evalRecord = __spreadProps(__spreadValues({}, evaluation), {
6395
+ nextPrompt: evaluation.nextGuardedPrompt,
6396
+ nextSafePrompt: evaluation.nextGuardedPrompt,
6397
+ nextSafeAction: evaluation.nextSafeAction,
6398
+ memorySummary: evaluation.memorySummary,
6399
+ evaluatedAt: evaluation.evaluatedAt
6400
+ });
6401
+ updateRun(activeRun.id, {
6402
+ status: "completed",
6403
+ evaluation: evalRecord,
6404
+ scopeDriftStatus,
6405
+ reportSummary,
6406
+ watchStatus: scope.status,
6407
+ watchWarnings: scope.warnings,
6408
+ watchChangedFiles: agentFiles
6409
+ // only agent changes, not RunTrim protocol files
6410
+ }, cwd);
6411
+ const freshRuns = loadAllRuns(cwd);
6412
+ const updatedRun = (_k = freshRuns.find((r) => r.id === activeRun.id)) != null ? _k : activeRun;
6413
+ writeMemoryFromRuns(updatedRun, freshRuns, config, cwd);
6414
+ let synced = false;
6415
+ if (options.sync !== false) {
6416
+ const globalAuth = loadGlobalAuth();
6417
+ const rawToken = (_m = (_l = globalAuth == null ? void 0 : globalAuth.token) != null ? _l : config.syncToken) != null ? _m : null;
6418
+ if (rawToken == null ? void 0 : rawToken.startsWith("rt_live_")) {
6419
+ try {
6420
+ const memoryMarkdown = (() => {
6421
+ try {
6422
+ return readMemory(cwd);
6423
+ } catch (e) {
6424
+ return null;
6425
+ }
6426
+ })();
6427
+ const payload = buildSyncPayload({
6428
+ cwd,
6429
+ projectName,
6430
+ config,
6431
+ projectAudit: projectAudit != null ? projectAudit : null,
6432
+ memoryMarkdown: memoryMarkdown != null ? memoryMarkdown : "",
6433
+ runs: freshRuns
6434
+ });
6435
+ const apiBase = resolveApiBase(config);
6436
+ const r = await fetch(`${apiBase}/api/sync`, {
6437
+ method: "POST",
6438
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${rawToken}` },
6439
+ body: JSON.stringify(payload)
6440
+ });
6441
+ synced = r.ok;
6442
+ } catch (e) {
6443
+ }
6444
+ }
6445
+ }
6446
+ const scopeColor = scopeDriftStatus === "passed" ? chalk.green : scopeDriftStatus === "forbidden_touched" ? chalk.red : chalk.yellow;
6447
+ const riskAfter = (_n = activeRun.contract.wasteRiskAfter) != null ? _n : "medium";
6448
+ const riskColor = (_o = { low: chalk.green, medium: chalk.yellow, high: chalk.hex("#FF8C00"), critical: chalk.red }[riskAfter]) != null ? _o : chalk.white;
6449
+ console.log(GO_ACCENT.bold("Run"));
6450
+ console.log(chalk.white(" " + truncate(activeRun.task, 70)));
6451
+ console.log("");
6452
+ if (changedFiles.length > 0) {
6453
+ console.log(GO_ACCENT.bold("Changed files"));
6454
+ for (const f of changedFiles.slice(0, 8)) {
6455
+ const isForbidden = scope.forbiddenFiles.includes(f);
6456
+ const isSensitive = scope.sensitiveFiles.includes(f);
6457
+ const marker = isForbidden ? chalk.red(" [forbidden]") : isSensitive ? chalk.yellow(" [sensitive]") : "";
6458
+ console.log(chalk.white(" - " + f) + marker);
6459
+ }
6460
+ if (changedFiles.length > 8) {
6461
+ console.log(DIM(` ... and ${changedFiles.length - 8} more`));
6462
+ }
6463
+ console.log("");
6464
+ } else {
6465
+ console.log(GO_ACCENT.bold("Changed files"));
6466
+ console.log(DIM(" No agent changes detected."));
6467
+ console.log("");
6468
+ }
6469
+ if (runtrimFiles.length > 0) {
6470
+ console.log(GO_ACCENT.bold("RunTrim files"));
6471
+ for (const f of runtrimFiles) {
6472
+ console.log(DIM(" - " + f));
6473
+ }
6474
+ console.log("");
6475
+ }
6476
+ console.log(GO_ACCENT.bold("Scope"));
6477
+ if (changedFiles.length === 0) {
6478
+ console.log(chalk.green(" No agent changes to evaluate."));
6479
+ } else {
6480
+ console.log(scopeColor(" " + (scopeDriftStatus === "passed" ? "Passed" : scopeDriftStatus === "forbidden_touched" ? "Failed \u2014 forbidden files touched" : "Drift detected")));
6481
+ }
6482
+ console.log("");
6483
+ console.log(GO_ACCENT.bold("Risk"));
6484
+ console.log(riskColor(" " + riskAfter));
6485
+ console.log("");
6486
+ console.log(GO_ACCENT.bold("Report"));
6487
+ console.log(chalk.white(" " + reportSummary));
6488
+ console.log("");
6489
+ if (continuationPack) {
6490
+ console.log(GO_ACCENT.bold("Continuation"));
6491
+ console.log(chalk.white(" Saved to .runtrim/continuation-prompt.md"));
6492
+ console.log("");
6493
+ }
6494
+ if (evaluation.nextSafeAction && evaluation.nextSafeAction !== "Run is ready to continue.") {
6495
+ console.log(GO_ACCENT.bold("Next safest step"));
6496
+ console.log(chalk.white(" " + evaluation.nextSafeAction));
6497
+ console.log("");
6498
+ }
6499
+ if (options.sync !== false) {
6500
+ console.log(GO_ACCENT.bold("Cloud sync"));
6501
+ console.log(DIM(" ") + (synced ? chalk.white("Completed.") : DIM("Skipped. Run runtrim login to connect your dashboard.")));
6502
+ console.log("");
6503
+ }
6504
+ });
6505
+ 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) => {
6506
+ var _a2, _b, _c, _d, _e, _f;
6507
+ const cwd = process.cwd();
6508
+ console.log("");
6509
+ console.log(BOLD("RunTrim") + DIM(" cloud sync"));
6510
+ console.log("");
6511
+ const globalAuth = loadGlobalAuth();
6512
+ const config = configExists(cwd) ? loadConfig(cwd) : DEFAULT_CONFIG;
6513
+ const rawToken = (_b = (_a2 = globalAuth == null ? void 0 : globalAuth.token) != null ? _a2 : config.syncToken) != null ? _b : null;
6514
+ if (!rawToken) {
6515
+ console.log(chalk.yellow(" No CLI token found."));
6516
+ console.log(DIM(" Run ") + GO_ACCENT("runtrim login") + DIM(" to connect cloud sync."));
6517
+ console.log(DIM(" Local CLI still works without a token."));
6518
+ console.log("");
6519
+ return;
6520
+ }
6521
+ if (!rawToken.startsWith("rt_live_")) {
6522
+ console.log(chalk.yellow(" Stored token format is invalid. Re-run: runtrim login"));
6523
+ console.log("");
6524
+ return;
6525
+ }
6526
+ const apiBase = resolveApiBase(config);
6527
+ const syncUrl = `${apiBase}/api/sync`;
6528
+ const runs = loadAllRuns(cwd);
6529
+ if (runs.length === 0) {
6530
+ console.log(DIM(" No local runs found in this directory."));
6531
+ console.log(DIM(" Run ") + GO_ACCENT('runtrim go "your task"') + DIM(" first to create runs."));
6532
+ console.log("");
6533
+ return;
6534
+ }
6535
+ const projectAudit = loadProjectAudit(cwd);
6536
+ const memoryMarkdown = (() => {
6537
+ try {
6538
+ return readMemory(cwd);
6539
+ } catch (e) {
6540
+ return "";
6541
+ }
6542
+ })();
6543
+ const payload = buildSyncPayload({
6544
+ cwd,
6545
+ projectName: (_c = projectAudit == null ? void 0 : projectAudit.projectName) != null ? _c : path10.basename(cwd),
6546
+ config,
6547
+ projectAudit: projectAudit != null ? projectAudit : null,
6548
+ memoryMarkdown: memoryMarkdown != null ? memoryMarkdown : "",
6549
+ runs
6550
+ });
6551
+ console.log(DIM(" Project ") + chalk.white(payload.project.name));
6552
+ console.log(DIM(" Runs ") + chalk.white(String(payload.runs.length)));
6553
+ console.log(DIM(" API ") + chalk.white(syncUrl));
6554
+ console.log("");
6555
+ if (opts.dryRun) {
6556
+ console.log(ACCENT.bold(" Dry run \u2014 nothing uploaded."));
6557
+ console.log("");
6558
+ return;
6559
+ }
6560
+ const spinner = oraFactory({ text: " Syncing...", color: "blue" }).start();
6561
+ try {
6562
+ const res = await fetch(syncUrl, {
6563
+ method: "POST",
6564
+ headers: {
6565
+ "Content-Type": "application/json",
6566
+ Authorization: `Bearer ${rawToken}`
6567
+ },
6568
+ body: JSON.stringify(payload)
6569
+ });
6570
+ const body = await res.json();
6571
+ if (!res.ok || !body.ok) {
6572
+ spinner.fail(" Sync failed.");
6573
+ console.log("");
6574
+ if (body.error) {
6575
+ console.log(chalk.red(" Error: ") + chalk.white(body.error));
6576
+ if (res.status === 401) {
6577
+ console.log(DIM(" Token may be invalid or expired. Run: runtrim login"));
6578
+ }
6579
+ }
6580
+ console.log("");
6581
+ return;
6582
+ }
6583
+ spinner.succeed(" Sync complete.");
6584
+ console.log("");
6585
+ console.log(ACCENT.bold(" Synced") + chalk.white(` ${(_d = body.syncedRuns) != null ? _d : payload.runs.length} run${((_e = body.syncedRuns) != null ? _e : payload.runs.length) === 1 ? "" : "s"}`));
6586
+ console.log(DIM(" Project ID ") + chalk.white((_f = body.projectId) != null ? _f : "\u2014"));
6587
+ console.log("");
6588
+ console.log(DIM(" View at ") + GO_ACCENT(`${apiBase}/app`));
6589
+ console.log("");
6590
+ } catch (err) {
6591
+ spinner.fail(" Network error. Check your connection.");
6592
+ console.log("");
6593
+ }
6594
+ });
5571
6595
  program.parse(process.argv);