runtrim 0.1.6 → 0.1.8

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 +854 -179
  2. package/package.json +3 -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,
@@ -3920,7 +4180,7 @@ program.command("init").description("Initialize RunTrim in the current project")
3920
4180
  if (!initResult.ok) return;
3921
4181
  const baseline = (_a2 = loadProjectAudit(cwd)) != null ? _a2 : performBaselineProjectAudit(cwd, null);
3922
4182
  const scriptNames = Object.keys(baseline.scripts);
3923
- const starterCreated = fs9.existsSync(path9.join(getConfigDir(cwd), "latest-prompt.md"));
4183
+ const starterCreated = fs10.existsSync(path10.join(getConfigDir(cwd), "latest-prompt.md"));
3924
4184
  console.log(ACCENT.bold(" RunTrim init"));
3925
4185
  console.log("");
3926
4186
  console.log(DIM(" Project detected"));
@@ -4538,8 +4798,8 @@ program.command("run <task>").description("Guard then run configured local agent
4538
4798
  console.log("");
4539
4799
  return;
4540
4800
  }
4541
- const outputPath = path9.join(getRunsDir(cwd), `${run.id}.output.txt`);
4542
- fs9.writeFileSync(outputPath, `# stdout
4801
+ const outputPath = path10.join(getRunsDir(cwd), `${run.id}.output.txt`);
4802
+ fs10.writeFileSync(outputPath, `# stdout
4543
4803
  ${stdout}
4544
4804
 
4545
4805
  # stderr
@@ -4616,7 +4876,8 @@ program.command("prepare <task>").description("Prepare a guarded prompt without
4616
4876
  });
4617
4877
  }
4618
4878
  );
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) => {
4879
+ 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;
4620
4881
  const cwd = process.cwd();
4621
4882
  const allowed = await ensureRepoAllowedForFree(cwd);
4622
4883
  if (!allowed) return;
@@ -4624,45 +4885,148 @@ program.command("go <task>").description("Daily shortcut: initialize if needed,
4624
4885
  const initResult = await initializeRunTrim(cwd, { allowOverwritePrompt: false });
4625
4886
  if (!initResult.ok) return;
4626
4887
  }
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
4888
  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);
4889
+ const auditSpinner = oraFactory({ text: " Auditing task...", color: "blue" }).start();
4890
+ await new Promise((r) => setTimeout(r, 180));
4891
+ const audit = auditTask(task, config, cwd);
4892
+ auditSpinner.stop();
4893
+ const contract = generateContract(task, audit, config);
4894
+ if (contract.isBlocked && contract.splitReport) {
4895
+ const sr = contract.splitReport;
4896
+ updateRun(saveRun(task, audit, contract, cwd).id, { status: "blocked" }, cwd);
4897
+ console.log("");
4898
+ console.log(GO_ACCENT.bold("RunTrim go"));
4899
+ console.log("");
4900
+ console.log(chalk.red.bold(" SPLIT REQUIRED"));
4901
+ console.log("");
4902
+ console.log(DIM(" Task ") + chalk.white(truncate(task, 60)));
4903
+ console.log(DIM(" Risk ") + chalk.red("CRITICAL"));
4904
+ console.log("");
4905
+ console.log(DIM(" This task crosses multiple high-risk systems."));
4906
+ console.log(DIM(" Running it in one agent session would cause scope drift and token waste."));
4907
+ console.log("");
4908
+ console.log(DIM(" Detected: ") + chalk.white(sr.detectedSystems.join(", ")));
4909
+ console.log("");
4910
+ for (const step of sr.recommendedSplit) {
4911
+ console.log(DIM(" ") + chalk.white(step));
4912
+ }
4913
+ console.log("");
4914
+ console.log(DIM(" Estimated waste avoided: ") + ACCENT(sr.estimatedWasteAvoided));
4915
+ console.log("");
4916
+ return;
4917
+ }
4918
+ const runs = loadAllRuns(cwd);
4919
+ const projectAudit = loadProjectAudit(cwd);
4920
+ const projectName = (_a2 = projectAudit == null ? void 0 : projectAudit.projectName) != null ? _a2 : path10.basename(cwd);
4921
+ const memoryMarkdown = (() => {
4922
+ try {
4923
+ return readMemory(cwd);
4924
+ } catch (e) {
4925
+ return null;
4926
+ }
4927
+ })();
4928
+ const memoryUsed = Boolean(memoryMarkdown && memoryMarkdown.trim().length > 50);
4929
+ const run = saveRun(task, audit, contract, cwd);
4930
+ updateRun(run.id, {
4931
+ status: "guarded",
4932
+ bridgeMode: true,
4933
+ tokenBudget: deriveBridgeContext(task, contract, runs, projectName).tokenBudget,
4934
+ memoryUsed
4935
+ }, cwd);
4936
+ const bridgeCtx = deriveBridgeContext(task, contract, runs, projectName);
4937
+ let bridgeWritten = [];
4938
+ let bridgeManagedPaths = [];
4939
+ if (options.bridge !== false) {
4940
+ const result = writeBridgeFiles(bridgeCtx, cwd);
4941
+ bridgeWritten = result.written;
4942
+ bridgeManagedPaths = result.managedPaths;
4943
+ }
4944
+ const rawPrompt = contract.contractText;
4945
+ const fullPrompt = options.bridge !== false ? buildBridgePrompt(rawPrompt, bridgeCtx) : rawPrompt;
4946
+ const promptPath = writeLatestPromptFile(fullPrompt, config, cwd);
4947
+ const promptRelative = ".runtrim/latest-prompt.md";
4948
+ if (!bridgeManagedPaths.includes(promptRelative)) {
4949
+ bridgeManagedPaths.push(promptRelative);
4950
+ }
4951
+ const doCopy = options.clipboard !== false;
4952
+ const copied = doCopy ? await copyToClipboardSafe(fullPrompt) : false;
4953
+ if (options.monitor) void tryLaunchPanelMonitorDetached(cwd);
4954
+ updateRun(run.id, { bridgeManagedFiles: bridgeManagedPaths }, cwd);
4955
+ let synced = false;
4956
+ if (options.sync !== false) {
4957
+ const globalAuth = loadGlobalAuth();
4958
+ const rawToken = (_c = (_b = globalAuth == null ? void 0 : globalAuth.token) != null ? _b : config.syncToken) != null ? _c : null;
4959
+ if (rawToken == null ? void 0 : rawToken.startsWith("rt_live_")) {
4960
+ try {
4961
+ const freshRuns = loadAllRuns(cwd);
4962
+ const payload = buildSyncPayload({
4963
+ cwd,
4964
+ projectName,
4965
+ config,
4966
+ projectAudit: projectAudit != null ? projectAudit : null,
4967
+ memoryMarkdown: memoryMarkdown != null ? memoryMarkdown : "",
4968
+ runs: freshRuns
4969
+ });
4970
+ const apiBase = resolveApiBase(config);
4971
+ const r = await fetch(`${apiBase}/api/sync`, {
4972
+ method: "POST",
4973
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${rawToken}` },
4974
+ body: JSON.stringify(payload)
4975
+ });
4976
+ synced = r.ok;
4977
+ } catch (e) {
4978
+ }
4979
+ }
4644
4980
  }
4981
+ const riskColor = (_d = { low: chalk.green, medium: chalk.yellow, high: chalk.hex("#FF8C00"), critical: chalk.red }[bridgeCtx.riskLevel]) != null ? _d : chalk.white;
4645
4982
  console.log("");
4646
4983
  console.log(GO_ACCENT.bold("RunTrim go"));
4647
4984
  console.log("");
4648
4985
  console.log(GO_ACCENT.bold("Task"));
4649
- console.log(chalk.white(task));
4986
+ console.log(chalk.white(" " + task));
4650
4987
  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"));
4988
+ console.log(GO_ACCENT.bold("Memory"));
4989
+ console.log(DIM(" " + (memoryUsed ? `Loaded ${runs.length} prior run${runs.length === 1 ? "" : "s"} and project context.` : "No prior runs. Starting from project context.")));
4653
4990
  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"));
4991
+ console.log(GO_ACCENT.bold("Contract"));
4992
+ console.log(DIM(" Risk ") + riskColor(bridgeCtx.riskLevel));
4993
+ console.log(DIM(" Token budget ") + chalk.white("~" + bridgeCtx.tokenBudget.toLocaleString()));
4994
+ if (bridgeCtx.allowedScope.length > 0) {
4995
+ console.log(DIM(" Allowed ") + chalk.white(truncate(bridgeCtx.allowedScope.slice(0, 2).join(", "), 60)));
4996
+ }
4997
+ if (bridgeCtx.forbiddenScope.length > 0) {
4998
+ console.log(DIM(" Forbidden ") + chalk.white(truncate(bridgeCtx.forbiddenScope.slice(0, 2).join(", "), 60)));
4999
+ }
5000
+ console.log(DIM(" Run saved ") + chalk.white(`.runtrim/runs/${run.id}.json`));
4660
5001
  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."));
5002
+ if (bridgeWritten.length > 0) {
5003
+ console.log(GO_ACCENT.bold("Bridge"));
5004
+ for (const f of bridgeWritten) {
5005
+ console.log(DIM(" ") + chalk.white(f));
5006
+ }
5007
+ console.log("");
5008
+ }
5009
+ if (options.sync !== false) {
5010
+ console.log(GO_ACCENT.bold("Cloud sync"));
5011
+ console.log(DIM(" ") + (synced ? chalk.white("Synced.") : DIM("Skipped. Run runtrim login to connect your dashboard.")));
5012
+ console.log("");
5013
+ }
5014
+ console.log(GO_ACCENT.bold("Prompt"));
5015
+ if (copied) {
5016
+ console.log(chalk.white(" Copied to clipboard."));
5017
+ } else if (!doCopy) {
5018
+ console.log(chalk.white(" Saved to: " + promptPath));
5019
+ } else {
5020
+ console.log(DIM(" Clipboard unavailable. Saved to: " + promptPath));
5021
+ }
5022
+ if (options.print) {
5023
+ console.log("");
5024
+ console.log(fullPrompt);
5025
+ }
4663
5026
  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."));
5027
+ console.log(GO_ACCENT.bold("Next"));
5028
+ console.log(chalk.white(" Paste the guarded prompt into Claude Code, Codex, Cursor, or your agent."));
5029
+ console.log(chalk.white(" After edits are done, run: runtrim finish"));
4666
5030
  console.log("");
4667
5031
  });
4668
5032
  program.command("check").description("Check the latest run and evaluate agent output").option("--json", "Print machine-readable check summary").action(async (options) => {
@@ -4828,80 +5192,6 @@ program.command("check").description("Check the latest run and evaluate agent ou
4828
5192
  console.log(chalk.white(checkSummary.nextSafeAction));
4829
5193
  console.log("");
4830
5194
  });
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
5195
  program.command("audit").description("Run baseline project audit and refresh local baseline files").action(() => {
4906
5196
  const cwd = process.cwd();
4907
5197
  if (!configExists(cwd)) {
@@ -4925,7 +5215,7 @@ program.command("audit").description("Run baseline project audit and refresh loc
4925
5215
  const latest = loadLatestRun(cwd);
4926
5216
  if (latest) writeMemoryFromRuns(latest, runs, nextConfig, cwd);
4927
5217
  } else {
4928
- fs9.writeFileSync(path9.join(getConfigDir(cwd), "memory.md"), buildBaselineMemoryMarkdown(baseline), "utf-8");
5218
+ fs10.writeFileSync(path10.join(getConfigDir(cwd), "memory.md"), buildBaselineMemoryMarkdown(baseline), "utf-8");
4929
5219
  }
4930
5220
  console.log("");
4931
5221
  console.log(BOLD("RunTrim") + DIM(" audit"));
@@ -5057,15 +5347,15 @@ program.command("memory").description("Show project memory and latest next safe
5057
5347
  console.log("");
5058
5348
  const config = configExists(cwd) ? loadConfig(cwd) : DEFAULT_CONFIG;
5059
5349
  const audit = (_a2 = loadProjectAudit(cwd)) != null ? _a2 : performBaselineProjectAudit(cwd, null);
5060
- const memoryPath = path9.join(getConfigDir(cwd), "memory.md");
5350
+ const memoryPath = path10.join(getConfigDir(cwd), "memory.md");
5061
5351
  const latestRun = loadLatestRun(cwd);
5062
5352
  if (!latestRun) {
5063
5353
  let memory2 = readMemory(cwd);
5064
5354
  if (!memory2) {
5065
- const memoryDir = path9.dirname(memoryPath);
5066
- if (!fs9.existsSync(memoryDir)) fs9.mkdirSync(memoryDir, { recursive: true });
5355
+ const memoryDir = path10.dirname(memoryPath);
5356
+ if (!fs10.existsSync(memoryDir)) fs10.mkdirSync(memoryDir, { recursive: true });
5067
5357
  memory2 = buildBaselineMemoryMarkdown(audit);
5068
- fs9.writeFileSync(memoryPath, memory2, "utf-8");
5358
+ fs10.writeFileSync(memoryPath, memory2, "utf-8");
5069
5359
  }
5070
5360
  const baselinePrompt = 'runtrim go "your task"';
5071
5361
  if (options.prompt) {
@@ -5206,13 +5496,13 @@ program.command("continue").description("Create a safe continuation prompt from
5206
5496
  const nowIso = (/* @__PURE__ */ new Date()).toISOString();
5207
5497
  const continuationPath = resolveContinuationPath(cwd);
5208
5498
  const latestPromptPath = resolvePromptPath(config, cwd);
5209
- const latestPrompt = fs9.existsSync(latestPromptPath) ? fs9.readFileSync(latestPromptPath, "utf-8").trim() : "";
5499
+ const latestPrompt = fs10.existsSync(latestPromptPath) ? fs10.readFileSync(latestPromptPath, "utf-8").trim() : "";
5210
5500
  let memory = readMemory(cwd);
5211
5501
  if (!memory) {
5212
5502
  if (latestRun) memory = writeMemoryFromRuns(latestRun, allRuns, config, cwd);
5213
5503
  else memory = buildBaselineMemoryMarkdown(audit);
5214
5504
  }
5215
- const projectName = audit.projectName || path9.basename(cwd);
5505
+ const projectName = audit.projectName || path10.basename(cwd);
5216
5506
  const stackLine = audit.detectedStack.length > 0 ? audit.detectedStack.join(", ") : "unknown";
5217
5507
  const diffFiles = dedupeFiles(await getGitDiff(cwd));
5218
5508
  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 +5678,13 @@ program.command("continue").description("Create a safe continuation prompt from
5388
5678
  promptLines.push("");
5389
5679
  }
5390
5680
  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");
5681
+ const continuationDir = path10.dirname(continuationPath);
5682
+ if (!fs10.existsSync(continuationDir)) fs10.mkdirSync(continuationDir, { recursive: true });
5683
+ fs10.writeFileSync(continuationPath, continuationPrompt, "utf-8");
5394
5684
  const copied = await copyToClipboardSafe(continuationPrompt);
5395
5685
  if (memory) {
5396
5686
  const memoryWithContinuation = updateMemoryWithContinuation(memory, reason, continuationPath, nowIso);
5397
- fs9.writeFileSync(path9.join(getConfigDir(cwd), "memory.md"), memoryWithContinuation, "utf-8");
5687
+ fs10.writeFileSync(path10.join(getConfigDir(cwd), "memory.md"), memoryWithContinuation, "utf-8");
5398
5688
  }
5399
5689
  if (hasConfig) {
5400
5690
  const nextConfig = __spreadProps(__spreadValues({}, config), {
@@ -5505,7 +5795,7 @@ program.command("report").description("Show a summary of all local RunTrim runs"
5505
5795
  console.log(DIM(" " + SECTION));
5506
5796
  console.log("");
5507
5797
  const audit = loadProjectAudit(cwd);
5508
- console.log(DIM(" Project ") + chalk.white((_b = audit == null ? void 0 : audit.projectName) != null ? _b : path9.basename(cwd)));
5798
+ console.log(DIM(" Project ") + chalk.white((_b = audit == null ? void 0 : audit.projectName) != null ? _b : path10.basename(cwd)));
5509
5799
  console.log(DIM(" Focus ") + chalk.white(truncate((_d = (_c = latestRun.contract.contract) == null ? void 0 : _c.cleanedObjective) != null ? _d : latestRun.task, 58)));
5510
5800
  console.log(DIM(" Status ") + chalk.white(formatStatus((_e = latestEval == null ? void 0 : latestEval.status) != null ? _e : latestRun.status)));
5511
5801
  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 +5858,389 @@ var statusColors = {
5568
5858
  drift_detected: chalk.red,
5569
5859
  blocked: chalk.red
5570
5860
  };
5861
+ var GLOBAL_AUTH_DIR = path10.join(os3.homedir(), ".runtrim");
5862
+ var GLOBAL_AUTH_FILE = path10.join(GLOBAL_AUTH_DIR, "auth.json");
5863
+ function loadGlobalAuth() {
5864
+ if (!fs10.existsSync(GLOBAL_AUTH_FILE)) return null;
5865
+ try {
5866
+ return JSON.parse(fs10.readFileSync(GLOBAL_AUTH_FILE, "utf-8"));
5867
+ } catch (e) {
5868
+ return null;
5869
+ }
5870
+ }
5871
+ function saveGlobalAuth(auth) {
5872
+ if (!fs10.existsSync(GLOBAL_AUTH_DIR)) {
5873
+ fs10.mkdirSync(GLOBAL_AUTH_DIR, { recursive: true });
5874
+ }
5875
+ fs10.writeFileSync(GLOBAL_AUTH_FILE, JSON.stringify(auth, null, 2));
5876
+ }
5877
+ function resolveApiBase(config) {
5878
+ var _a2;
5879
+ const url = (_a2 = config.dashboardUrl) == null ? void 0 : _a2.trim();
5880
+ if (!url || url.startsWith("http://localhost")) {
5881
+ return "https://www.runtrim.com";
5882
+ }
5883
+ try {
5884
+ return new URL(url).origin;
5885
+ } catch (e) {
5886
+ return "https://www.runtrim.com";
5887
+ }
5888
+ }
5889
+ program.command("login").description("Connect this machine to your RunTrim cloud account").option("--token <token>", "CLI token (skip interactive prompt)").action(async (opts) => {
5890
+ var _a2, _b, _c, _d, _e;
5891
+ console.log("");
5892
+ console.log(BOLD("RunTrim") + DIM(" connect to cloud"));
5893
+ console.log("");
5894
+ const cwd = process.cwd();
5895
+ const existing = loadGlobalAuth();
5896
+ if (existing && !opts.token) {
5897
+ console.log(DIM(" Already connected."));
5898
+ if (existing.email) {
5899
+ console.log(DIM(" Account ") + chalk.white(existing.email));
5900
+ }
5901
+ console.log(DIM(" Token ") + chalk.white("rt_live_..."));
5902
+ console.log("");
5903
+ console.log(DIM(" To reconnect, run: ") + GO_ACCENT("runtrim login --token <new-token>"));
5904
+ console.log("");
5905
+ return;
5906
+ }
5907
+ console.log(DIM(" Get your CLI token from:"));
5908
+ console.log(" " + GO_ACCENT("https://www.runtrim.com/app/connect"));
5909
+ console.log("");
5910
+ let rawToken = (_b = (_a2 = opts.token) == null ? void 0 : _a2.trim()) != null ? _b : "";
5911
+ if (!rawToken) {
5912
+ const answer = await prompts({
5913
+ type: "text",
5914
+ name: "token",
5915
+ message: "Paste your CLI token"
5916
+ });
5917
+ rawToken = (_d = (_c = answer.token) == null ? void 0 : _c.trim()) != null ? _d : "";
5918
+ }
5919
+ if (!rawToken || !rawToken.startsWith("rt_live_")) {
5920
+ console.log(chalk.yellow(" Invalid token. Tokens start with rt_live_"));
5921
+ console.log("");
5922
+ return;
5923
+ }
5924
+ const config = configExists(cwd) ? loadConfig(cwd) : DEFAULT_CONFIG;
5925
+ const apiBase = resolveApiBase(config);
5926
+ const verifyUrl = `${apiBase}/api/cli-token/verify`;
5927
+ const spinner = oraFactory({ text: " Verifying token...", color: "blue" }).start();
5928
+ let email;
5929
+ try {
5930
+ const res = await fetch(verifyUrl, {
5931
+ method: "GET",
5932
+ headers: { Authorization: `Bearer ${rawToken}` }
5933
+ });
5934
+ const body = await res.json();
5935
+ if (!res.ok || !body.ok) {
5936
+ spinner.fail(" Invalid token. " + ((_e = body.error) != null ? _e : ""));
5937
+ console.log("");
5938
+ return;
5939
+ }
5940
+ email = body.email;
5941
+ spinner.succeed(" Token verified.");
5942
+ } catch (e) {
5943
+ spinner.fail(" Could not reach RunTrim server. Check your connection.");
5944
+ console.log("");
5945
+ return;
5946
+ }
5947
+ saveGlobalAuth({ token: rawToken, storedAt: (/* @__PURE__ */ new Date()).toISOString(), email });
5948
+ console.log("");
5949
+ if (email) {
5950
+ console.log(ACCENT.bold(" Connected as ") + chalk.white(email));
5951
+ } else {
5952
+ console.log(ACCENT.bold(" Connected to RunTrim cloud."));
5953
+ }
5954
+ console.log(DIM(" Token stored in ") + chalk.white("~/.runtrim/auth.json"));
5955
+ console.log("");
5956
+ console.log(DIM(" Next: navigate to a project and run ") + GO_ACCENT("runtrim sync"));
5957
+ console.log("");
5958
+ });
5959
+ 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) => {
5960
+ var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o;
5961
+ const cwd = process.cwd();
5962
+ console.log("");
5963
+ console.log(GO_ACCENT.bold("RunTrim finish"));
5964
+ console.log("");
5965
+ const allRuns = loadAllRuns(cwd);
5966
+ const activeRun = allRuns.find((r) => r.status === "guarded" || r.status === "checked");
5967
+ if (!activeRun) {
5968
+ console.log(chalk.yellow(" No active RunTrim session found."));
5969
+ console.log(DIM(' Start a new session with: runtrim go "<task>"'));
5970
+ console.log("");
5971
+ return;
5972
+ }
5973
+ const config = configExists(cwd) ? loadConfig(cwd) : DEFAULT_CONFIG;
5974
+ const projectAudit = loadProjectAudit(cwd);
5975
+ const projectName = (_a2 = projectAudit == null ? void 0 : projectAudit.projectName) != null ? _a2 : path10.basename(cwd);
5976
+ console.log(DIM(" Run ") + chalk.white(truncate(activeRun.task, 60)));
5977
+ console.log(DIM(" Run ID ") + chalk.white(activeRun.id));
5978
+ console.log("");
5979
+ const allChangedFiles = dedupeFiles(await getGitDiff(cwd));
5980
+ const sessionManagedFiles = (_b = activeRun.bridgeManagedFiles) != null ? _b : [];
5981
+ function isRuntrimOwned(f) {
5982
+ const norm = f.replace(/\\/g, "/").toLowerCase();
5983
+ if (norm.startsWith(".runtrim/")) return true;
5984
+ if (norm === "runtrim.md") return true;
5985
+ if (sessionManagedFiles.some(
5986
+ (m) => m.replace(/\\/g, "/").toLowerCase() === norm
5987
+ )) return true;
5988
+ return false;
5989
+ }
5990
+ const runtrimFiles = [];
5991
+ const agentFiles = [];
5992
+ for (const f of allChangedFiles) {
5993
+ if (isRuntrimOwned(f)) {
5994
+ runtrimFiles.push(f);
5995
+ } else {
5996
+ agentFiles.push(f);
5997
+ }
5998
+ }
5999
+ const changedFiles = agentFiles;
6000
+ const maxFiles = inferMaxFilesFromScope(
6001
+ (_d = (_c = activeRun.contract.contract) == null ? void 0 : _c.relevantScope) != null ? _d : [],
6002
+ config.maxFilesPerRun
6003
+ );
6004
+ const scope = evaluateWatchState({
6005
+ changedFiles,
6006
+ run: activeRun,
6007
+ maxFilesPerRun: maxFiles,
6008
+ strict: false
6009
+ });
6010
+ const evaluation = evaluateAgentOutput(null, changedFiles, {
6011
+ task: activeRun.task,
6012
+ relevantScope: (_f = (_e = activeRun.contract.contract) == null ? void 0 : _e.relevantScope) != null ? _f : [],
6013
+ sensitiveScope: (_h = (_g = activeRun.contract.contract) == null ? void 0 : _g.sensitiveScope) != null ? _h : [],
6014
+ forbiddenScope: (_j = (_i = activeRun.contract.contract) == null ? void 0 : _i.forbiddenScope) != null ? _j : [],
6015
+ runStatus: activeRun.status
6016
+ });
6017
+ let scopeDriftStatus = "passed";
6018
+ if (scope.forbiddenFiles.length > 0) scopeDriftStatus = "forbidden_touched";
6019
+ else if (scope.outOfScopeFiles.length > 0) scopeDriftStatus = "out_of_scope";
6020
+ else if (evaluation.scopeDriftRisk === "high" || evaluation.scopeDriftRisk === "medium")
6021
+ scopeDriftStatus = "drift_detected";
6022
+ const forbiddenCount = scope.forbiddenFiles.length;
6023
+ const reportParts = [];
6024
+ if (changedFiles.length === 0) {
6025
+ reportParts.push("No agent changes detected.");
6026
+ } else {
6027
+ reportParts.push(`${changedFiles.length} file${changedFiles.length === 1 ? "" : "s"} changed.`);
6028
+ }
6029
+ if (forbiddenCount > 0) {
6030
+ reportParts.push(`${forbiddenCount} forbidden file${forbiddenCount === 1 ? "" : "s"} touched.`);
6031
+ } else if (changedFiles.length > 0) {
6032
+ reportParts.push("No forbidden systems touched.");
6033
+ }
6034
+ if (scopeDriftStatus === "passed" && changedFiles.length > 0) {
6035
+ reportParts.push("Changes within contract.");
6036
+ }
6037
+ if (evaluation.memorySummary) reportParts.push(evaluation.memorySummary);
6038
+ const reportSummary = reportParts.join(" ");
6039
+ const continuationPack = evaluation.nextGuardedPrompt || null;
6040
+ if (continuationPack) {
6041
+ const contDir = getConfigDir(cwd);
6042
+ if (!fs10.existsSync(contDir)) fs10.mkdirSync(contDir, { recursive: true });
6043
+ fs10.writeFileSync(path10.join(contDir, "continuation-prompt.md"), continuationPack, "utf-8");
6044
+ }
6045
+ const evalRecord = __spreadProps(__spreadValues({}, evaluation), {
6046
+ nextPrompt: evaluation.nextGuardedPrompt,
6047
+ nextSafePrompt: evaluation.nextGuardedPrompt,
6048
+ nextSafeAction: evaluation.nextSafeAction,
6049
+ memorySummary: evaluation.memorySummary,
6050
+ evaluatedAt: evaluation.evaluatedAt
6051
+ });
6052
+ updateRun(activeRun.id, {
6053
+ status: "completed",
6054
+ evaluation: evalRecord,
6055
+ scopeDriftStatus,
6056
+ reportSummary,
6057
+ watchStatus: scope.status,
6058
+ watchWarnings: scope.warnings,
6059
+ watchChangedFiles: agentFiles
6060
+ // only agent changes, not RunTrim protocol files
6061
+ }, cwd);
6062
+ const freshRuns = loadAllRuns(cwd);
6063
+ const updatedRun = (_k = freshRuns.find((r) => r.id === activeRun.id)) != null ? _k : activeRun;
6064
+ writeMemoryFromRuns(updatedRun, freshRuns, config, cwd);
6065
+ let synced = false;
6066
+ if (options.sync !== false) {
6067
+ const globalAuth = loadGlobalAuth();
6068
+ const rawToken = (_m = (_l = globalAuth == null ? void 0 : globalAuth.token) != null ? _l : config.syncToken) != null ? _m : null;
6069
+ if (rawToken == null ? void 0 : rawToken.startsWith("rt_live_")) {
6070
+ try {
6071
+ const memoryMarkdown = (() => {
6072
+ try {
6073
+ return readMemory(cwd);
6074
+ } catch (e) {
6075
+ return null;
6076
+ }
6077
+ })();
6078
+ const payload = buildSyncPayload({
6079
+ cwd,
6080
+ projectName,
6081
+ config,
6082
+ projectAudit: projectAudit != null ? projectAudit : null,
6083
+ memoryMarkdown: memoryMarkdown != null ? memoryMarkdown : "",
6084
+ runs: freshRuns
6085
+ });
6086
+ const apiBase = resolveApiBase(config);
6087
+ const r = await fetch(`${apiBase}/api/sync`, {
6088
+ method: "POST",
6089
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${rawToken}` },
6090
+ body: JSON.stringify(payload)
6091
+ });
6092
+ synced = r.ok;
6093
+ } catch (e) {
6094
+ }
6095
+ }
6096
+ }
6097
+ const scopeColor = scopeDriftStatus === "passed" ? chalk.green : scopeDriftStatus === "forbidden_touched" ? chalk.red : chalk.yellow;
6098
+ const riskAfter = (_n = activeRun.contract.wasteRiskAfter) != null ? _n : "medium";
6099
+ const riskColor = (_o = { low: chalk.green, medium: chalk.yellow, high: chalk.hex("#FF8C00"), critical: chalk.red }[riskAfter]) != null ? _o : chalk.white;
6100
+ console.log(GO_ACCENT.bold("Run"));
6101
+ console.log(chalk.white(" " + truncate(activeRun.task, 70)));
6102
+ console.log("");
6103
+ if (changedFiles.length > 0) {
6104
+ console.log(GO_ACCENT.bold("Changed files"));
6105
+ for (const f of changedFiles.slice(0, 8)) {
6106
+ const isForbidden = scope.forbiddenFiles.includes(f);
6107
+ const isSensitive = scope.sensitiveFiles.includes(f);
6108
+ const marker = isForbidden ? chalk.red(" [forbidden]") : isSensitive ? chalk.yellow(" [sensitive]") : "";
6109
+ console.log(chalk.white(" - " + f) + marker);
6110
+ }
6111
+ if (changedFiles.length > 8) {
6112
+ console.log(DIM(` ... and ${changedFiles.length - 8} more`));
6113
+ }
6114
+ console.log("");
6115
+ } else {
6116
+ console.log(GO_ACCENT.bold("Changed files"));
6117
+ console.log(DIM(" No agent changes detected."));
6118
+ console.log("");
6119
+ }
6120
+ if (runtrimFiles.length > 0) {
6121
+ console.log(GO_ACCENT.bold("RunTrim files"));
6122
+ for (const f of runtrimFiles) {
6123
+ console.log(DIM(" - " + f));
6124
+ }
6125
+ console.log("");
6126
+ }
6127
+ console.log(GO_ACCENT.bold("Scope"));
6128
+ if (changedFiles.length === 0) {
6129
+ console.log(chalk.green(" No agent changes to evaluate."));
6130
+ } else {
6131
+ console.log(scopeColor(" " + (scopeDriftStatus === "passed" ? "Passed" : scopeDriftStatus === "forbidden_touched" ? "Failed \u2014 forbidden files touched" : "Drift detected")));
6132
+ }
6133
+ console.log("");
6134
+ console.log(GO_ACCENT.bold("Risk"));
6135
+ console.log(riskColor(" " + riskAfter));
6136
+ console.log("");
6137
+ console.log(GO_ACCENT.bold("Report"));
6138
+ console.log(chalk.white(" " + reportSummary));
6139
+ console.log("");
6140
+ if (continuationPack) {
6141
+ console.log(GO_ACCENT.bold("Continuation"));
6142
+ console.log(chalk.white(" Saved to .runtrim/continuation-prompt.md"));
6143
+ console.log("");
6144
+ }
6145
+ if (evaluation.nextSafeAction && evaluation.nextSafeAction !== "Run is ready to continue.") {
6146
+ console.log(GO_ACCENT.bold("Next safest step"));
6147
+ console.log(chalk.white(" " + evaluation.nextSafeAction));
6148
+ console.log("");
6149
+ }
6150
+ if (options.sync !== false) {
6151
+ console.log(GO_ACCENT.bold("Cloud sync"));
6152
+ console.log(DIM(" ") + (synced ? chalk.white("Completed.") : DIM("Skipped. Run runtrim login to connect your dashboard.")));
6153
+ console.log("");
6154
+ }
6155
+ });
6156
+ 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
+ var _a2, _b, _c, _d, _e, _f;
6158
+ const cwd = process.cwd();
6159
+ console.log("");
6160
+ console.log(BOLD("RunTrim") + DIM(" cloud sync"));
6161
+ console.log("");
6162
+ const globalAuth = loadGlobalAuth();
6163
+ const config = configExists(cwd) ? loadConfig(cwd) : DEFAULT_CONFIG;
6164
+ const rawToken = (_b = (_a2 = globalAuth == null ? void 0 : globalAuth.token) != null ? _a2 : config.syncToken) != null ? _b : null;
6165
+ if (!rawToken) {
6166
+ console.log(chalk.yellow(" No CLI token found."));
6167
+ console.log(DIM(" Run ") + GO_ACCENT("runtrim login") + DIM(" to connect cloud sync."));
6168
+ console.log(DIM(" Local CLI still works without a token."));
6169
+ console.log("");
6170
+ return;
6171
+ }
6172
+ if (!rawToken.startsWith("rt_live_")) {
6173
+ console.log(chalk.yellow(" Stored token format is invalid. Re-run: runtrim login"));
6174
+ console.log("");
6175
+ return;
6176
+ }
6177
+ const apiBase = resolveApiBase(config);
6178
+ const syncUrl = `${apiBase}/api/sync`;
6179
+ const runs = loadAllRuns(cwd);
6180
+ if (runs.length === 0) {
6181
+ console.log(DIM(" No local runs found in this directory."));
6182
+ console.log(DIM(" Run ") + GO_ACCENT('runtrim go "your task"') + DIM(" first to create runs."));
6183
+ console.log("");
6184
+ return;
6185
+ }
6186
+ const projectAudit = loadProjectAudit(cwd);
6187
+ const memoryMarkdown = (() => {
6188
+ try {
6189
+ return readMemory(cwd);
6190
+ } catch (e) {
6191
+ return "";
6192
+ }
6193
+ })();
6194
+ const payload = buildSyncPayload({
6195
+ cwd,
6196
+ projectName: (_c = projectAudit == null ? void 0 : projectAudit.projectName) != null ? _c : path10.basename(cwd),
6197
+ config,
6198
+ projectAudit: projectAudit != null ? projectAudit : null,
6199
+ memoryMarkdown: memoryMarkdown != null ? memoryMarkdown : "",
6200
+ runs
6201
+ });
6202
+ console.log(DIM(" Project ") + chalk.white(payload.project.name));
6203
+ console.log(DIM(" Runs ") + chalk.white(String(payload.runs.length)));
6204
+ console.log(DIM(" API ") + chalk.white(syncUrl));
6205
+ console.log("");
6206
+ if (opts.dryRun) {
6207
+ console.log(ACCENT.bold(" Dry run \u2014 nothing uploaded."));
6208
+ console.log("");
6209
+ return;
6210
+ }
6211
+ const spinner = oraFactory({ text: " Syncing...", color: "blue" }).start();
6212
+ try {
6213
+ const res = await fetch(syncUrl, {
6214
+ method: "POST",
6215
+ headers: {
6216
+ "Content-Type": "application/json",
6217
+ Authorization: `Bearer ${rawToken}`
6218
+ },
6219
+ body: JSON.stringify(payload)
6220
+ });
6221
+ const body = await res.json();
6222
+ if (!res.ok || !body.ok) {
6223
+ spinner.fail(" Sync failed.");
6224
+ console.log("");
6225
+ if (body.error) {
6226
+ console.log(chalk.red(" Error: ") + chalk.white(body.error));
6227
+ if (res.status === 401) {
6228
+ console.log(DIM(" Token may be invalid or expired. Run: runtrim login"));
6229
+ }
6230
+ }
6231
+ console.log("");
6232
+ return;
6233
+ }
6234
+ spinner.succeed(" Sync complete.");
6235
+ console.log("");
6236
+ 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"}`));
6237
+ console.log(DIM(" Project ID ") + chalk.white((_f = body.projectId) != null ? _f : "\u2014"));
6238
+ console.log("");
6239
+ console.log(DIM(" View at ") + GO_ACCENT(`${apiBase}/app`));
6240
+ console.log("");
6241
+ } catch (err) {
6242
+ spinner.fail(" Network error. Check your connection.");
6243
+ console.log("");
6244
+ }
6245
+ });
5571
6246
  program.parse(process.argv);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "runtrim",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "CLI guard layer that scopes AI coding runs before they waste tokens.",
5
5
  "license": "MIT",
6
6
  "author": "RunTrim",
@@ -38,6 +38,7 @@
38
38
  "build:all": "npm run build:cli && npm run build:web",
39
39
  "start": "next start",
40
40
  "lint": "eslint",
41
+ "prepublishOnly": "npm run build:cli",
41
42
  "runtrim": "tsx cli/runtrim.ts",
42
43
  "runtrim:build": "npm run build:cli"
43
44
  },
@@ -54,6 +55,7 @@
54
55
  "@radix-ui/react-switch": "^1.2.6",
55
56
  "@radix-ui/react-tabs": "^1.1.13",
56
57
  "@radix-ui/react-tooltip": "^1.2.8",
58
+ "@supabase/ssr": "^0.10.2",
57
59
  "@supabase/supabase-js": "^2.57.4",
58
60
  "chalk": "^5.6.2",
59
61
  "class-variance-authority": "^0.7.1",