replicas-cli 0.2.424 → 0.2.425

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/index.mjs +95 -43
  2. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -24492,7 +24492,7 @@ function formatTurnElapsed(ms) {
24492
24492
  }
24493
24493
 
24494
24494
  // ../shared/src/cli-version.ts
24495
- var CLI_VERSION = "0.2.424";
24495
+ var CLI_VERSION = "0.2.425";
24496
24496
 
24497
24497
  // ../shared/src/version.ts
24498
24498
  function compareVersions(v1, v2) {
@@ -30365,6 +30365,42 @@ async function serviceLogsCommand(name, options) {
30365
30365
  }
30366
30366
 
30367
30367
  // src/commands/learnings.ts
30368
+ import { createHash as createHash2 } from "crypto";
30369
+ import { mkdirSync as mkdirSync2, readdirSync as readdirSync2, readFileSync as readFileSync2, statSync, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
30370
+ import { join as join2 } from "path";
30371
+ var SESSIONS_DIR = join2(CONFIG_DIR, "learnings-sessions");
30372
+ var REPRINT_AFTER_READS = 50;
30373
+ var SESSION_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
30374
+ function sessionFilePath() {
30375
+ const key = process.env.REPLICAS_CHAT_ID || process.env.CLAUDE_CODE_SESSION_ID;
30376
+ return key ? join2(SESSIONS_DIR, `${key.replace(/[^a-zA-Z0-9_-]/g, "-")}.json`) : null;
30377
+ }
30378
+ function readSession(path6) {
30379
+ const session = { reads: 0, shown: {} };
30380
+ try {
30381
+ const parsed = JSON.parse(readFileSync2(path6, "utf-8"));
30382
+ if (typeof parsed?.reads === "number") session.reads = parsed.reads;
30383
+ const shown = parsed?.shown ?? {};
30384
+ for (const [id, entry] of Object.entries(shown)) {
30385
+ if (typeof entry?.read === "number" && typeof entry?.hash === "string") {
30386
+ session.shown[id] = { read: entry.read, hash: entry.hash };
30387
+ }
30388
+ }
30389
+ } catch {
30390
+ }
30391
+ return session;
30392
+ }
30393
+ function writeSession(path6, session) {
30394
+ try {
30395
+ mkdirSync2(SESSIONS_DIR, { recursive: true, mode: 448 });
30396
+ writeFileSync2(path6, JSON.stringify(session), { mode: 384 });
30397
+ for (const entry of readdirSync2(SESSIONS_DIR)) {
30398
+ const file2 = join2(SESSIONS_DIR, entry);
30399
+ if (Date.now() - statSync(file2).mtimeMs > SESSION_TTL_MS) unlinkSync(file2);
30400
+ }
30401
+ } catch {
30402
+ }
30403
+ }
30368
30404
  function printBlocks(learnings) {
30369
30405
  for (const learning of learnings) {
30370
30406
  const labels = [
@@ -30385,14 +30421,30 @@ async function learningsReadCommand(options) {
30385
30421
  method: "POST",
30386
30422
  body: { query }
30387
30423
  });
30424
+ const path6 = sessionFilePath();
30425
+ const session = path6 ? readSession(path6) : null;
30426
+ if (session) session.reads += 1;
30427
+ const unseen = response.learnings.filter((learning) => {
30428
+ if (!session) return true;
30429
+ const hash2 = createHash2("sha256").update(learning.content).digest("hex").slice(0, 16);
30430
+ const shown = session.shown[learning.id];
30431
+ if (!options.fresh && shown && shown.hash === hash2 && session.reads - shown.read < REPRINT_AFTER_READS) {
30432
+ return false;
30433
+ }
30434
+ session.shown[learning.id] = { read: session.reads, hash: hash2 };
30435
+ return true;
30436
+ });
30388
30437
  if (response.learnings.length === 0) {
30389
30438
  console.log("No learnings matched this query.");
30439
+ } else if (unseen.length === 0) {
30440
+ console.log("No new learnings \u2014 the matching ones were already shown earlier in this session.");
30390
30441
  } else {
30391
- printBlocks(response.learnings);
30442
+ printBlocks(unseen);
30392
30443
  }
30393
30444
  if (response.matching_degraded) {
30394
30445
  console.log("Note: trigger matching was unavailable \u2014 only always-apply learnings are shown.");
30395
30446
  }
30447
+ if (path6 && session) writeSession(path6, session);
30396
30448
  }
30397
30449
  async function propose(request) {
30398
30450
  const { proposal } = await agentFetch("/v1/agent/learnings/proposals", {
@@ -30442,14 +30494,14 @@ async function learningsWithdrawCommand(id) {
30442
30494
 
30443
30495
  // src/commands/computer/index.ts
30444
30496
  import { spawn as spawn5, spawnSync as spawnSync3 } from "child_process";
30445
- import { createHash as createHash2 } from "crypto";
30446
- import { closeSync as closeSync2, copyFileSync as copyFileSync2, existsSync as existsSync3, mkdirSync as mkdirSync4, openSync as openSync2, readFileSync as readFileSync4, readSync, rmSync as rmSync4, writeFileSync as writeFileSync4 } from "fs";
30497
+ import { createHash as createHash3 } from "crypto";
30498
+ import { closeSync as closeSync2, copyFileSync as copyFileSync2, existsSync as existsSync3, mkdirSync as mkdirSync5, openSync as openSync2, readFileSync as readFileSync5, readSync, rmSync as rmSync4, writeFileSync as writeFileSync5 } from "fs";
30447
30499
  import { dirname as dirname3 } from "path";
30448
30500
  import chalk21 from "chalk";
30449
30501
 
30450
30502
  // src/commands/computer/desktop.ts
30451
30503
  import { spawnSync } from "child_process";
30452
- import { existsSync, mkdirSync as mkdirSync2, readFileSync as readFileSync2 } from "fs";
30504
+ import { existsSync, mkdirSync as mkdirSync3, readFileSync as readFileSync3 } from "fs";
30453
30505
  import { dirname, isAbsolute, resolve as resolve2 } from "path";
30454
30506
  var STATE_DIR = process.env.REPLICAS_DESKTOP_STATE_DIR || "/tmp/replicas-computer";
30455
30507
  var DEFAULT_DISPLAY = process.env.REPLICAS_DESKTOP_DISPLAY || ":99";
@@ -30485,7 +30537,7 @@ function desktopStackHealthy() {
30485
30537
  const pids = {};
30486
30538
  for (const name of ["openbox", "tint2", "x11vnc", "novnc"]) {
30487
30539
  try {
30488
- const pid = Number.parseInt(readFileSync2(`${STATE_DIR}/${name}.pid`, "utf8").trim(), 10);
30540
+ const pid = Number.parseInt(readFileSync3(`${STATE_DIR}/${name}.pid`, "utf8").trim(), 10);
30489
30541
  if (!Number.isFinite(pid)) return false;
30490
30542
  process.kill(pid, 0);
30491
30543
  pids[name] = pid;
@@ -30528,7 +30580,7 @@ function runDisplayCmd(bin, args) {
30528
30580
  return r.stdout?.toString() ?? "";
30529
30581
  }
30530
30582
  function runDesktopInputCmd(args) {
30531
- mkdirSync2(dirname(INPUT_LOCK_FILE), { recursive: true });
30583
+ mkdirSync3(dirname(INPUT_LOCK_FILE), { recursive: true });
30532
30584
  return runDisplayCmd("flock", [
30533
30585
  "--exclusive",
30534
30586
  "--wait",
@@ -30603,12 +30655,12 @@ var sleep2 = (ms) => new Promise((resolve3) => setTimeout(resolve3, ms));
30603
30655
 
30604
30656
  // src/commands/computer/recording.ts
30605
30657
  import { spawn as spawn4 } from "child_process";
30606
- import { appendFileSync, existsSync as existsSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync3, rmSync as rmSync3, statSync, writeFileSync as writeFileSync3 } from "fs";
30658
+ import { appendFileSync, existsSync as existsSync2, mkdirSync as mkdirSync4, readFileSync as readFileSync4, rmSync as rmSync3, statSync as statSync2, writeFileSync as writeFileSync4 } from "fs";
30607
30659
  import { dirname as dirname2 } from "path";
30608
30660
 
30609
30661
  // src/commands/computer/recording/render.ts
30610
30662
  import { spawnSync as spawnSync2 } from "child_process";
30611
- import { copyFileSync, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
30663
+ import { copyFileSync, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "fs";
30612
30664
 
30613
30665
  // src/commands/computer/recording/config.ts
30614
30666
  var cameraMotion = {
@@ -31013,7 +31065,7 @@ function renderRecording(rawPath, target, actions, fps, size) {
31013
31065
  }
31014
31066
  const stamp = `${process.pid}-${Date.now()}`;
31015
31067
  const cursor = cursorAssets(stamp);
31016
- writeFileSync2(cursor.path, cursorSvg(cursor.size));
31068
+ writeFileSync3(cursor.path, cursorSvg(cursor.size));
31017
31069
  const spans = renderedSegmentSpans(segments);
31018
31070
  const renderedDuration = spans.length ? spans[spans.length - 1].outputEnd : duration3;
31019
31071
  const renderedActions = actionsOnRenderedTimeline(actions, spans);
@@ -31036,7 +31088,7 @@ function renderRecording(rawPath, target, actions, fps, size) {
31036
31088
  const concatInputs = segments.map((_, index) => `[v${index}]`).join("");
31037
31089
  const filter = `${screenSplitFilter};${filters.join(";")};${concatInputs}concat=n=${segments.length}:v=1:a=0[screen];${cursorFilter}`;
31038
31090
  const filterPath = `/tmp/replicas-recording-filter-${stamp}.ffgraph`;
31039
- writeFileSync2(filterPath, filter);
31091
+ writeFileSync3(filterPath, filter);
31040
31092
  try {
31041
31093
  const r = spawnSync2("ffmpeg", [
31042
31094
  "-y",
@@ -31098,7 +31150,7 @@ function clearRecordingState() {
31098
31150
  }
31099
31151
  function recordingStartedAt() {
31100
31152
  if (!existsSync2(RECORD_STARTED_AT_FILE)) return null;
31101
- const startedAt = Number.parseInt(readFileSync3(RECORD_STARTED_AT_FILE, "utf8").trim(), 10);
31153
+ const startedAt = Number.parseInt(readFileSync4(RECORD_STARTED_AT_FILE, "utf8").trim(), 10);
31102
31154
  return Number.isFinite(startedAt) ? startedAt : null;
31103
31155
  }
31104
31156
  function logRecordingAction(action) {
@@ -31111,7 +31163,7 @@ function logRecordingAction(action) {
31111
31163
  function readRecordingDimensions() {
31112
31164
  if (!existsSync2(RECORD_DIMENSIONS_FILE)) return configuredDesktopDimensions();
31113
31165
  try {
31114
- const dimensions = JSON.parse(readFileSync3(RECORD_DIMENSIONS_FILE, "utf8"));
31166
+ const dimensions = JSON.parse(readFileSync4(RECORD_DIMENSIONS_FILE, "utf8"));
31115
31167
  const width = dimensions?.width;
31116
31168
  const height = dimensions?.height;
31117
31169
  if (typeof width === "number" && Number.isFinite(width) && width > 0 && typeof height === "number" && Number.isFinite(height) && height > 0) {
@@ -31129,7 +31181,7 @@ function isOptionalNumber(value) {
31129
31181
  }
31130
31182
  function readRecordingActions() {
31131
31183
  if (!existsSync2(RECORD_ACTIONS_FILE)) return [];
31132
- return readFileSync3(RECORD_ACTIONS_FILE, "utf8").split("\n").filter(Boolean).flatMap((line) => {
31184
+ return readFileSync4(RECORD_ACTIONS_FILE, "utf8").split("\n").filter(Boolean).flatMap((line) => {
31133
31185
  try {
31134
31186
  const value = JSON.parse(line);
31135
31187
  if (typeof value !== "object" || value === null) return [];
@@ -31151,7 +31203,7 @@ function readRecordingActions() {
31151
31203
  async function computerRecordStartCommand(path6, options) {
31152
31204
  ensureServicesRunning();
31153
31205
  if (existsSync2(RECORD_PID_FILE)) {
31154
- const pid = parseInt(readFileSync3(RECORD_PID_FILE, "utf8").trim(), 10);
31206
+ const pid = parseInt(readFileSync4(RECORD_PID_FILE, "utf8").trim(), 10);
31155
31207
  if (Number.isFinite(pid)) {
31156
31208
  let alive2 = false;
31157
31209
  try {
@@ -31163,10 +31215,10 @@ async function computerRecordStartCommand(path6, options) {
31163
31215
  }
31164
31216
  }
31165
31217
  const target = resolvePath(path6);
31166
- mkdirSync3(dirname2(target), { recursive: true });
31218
+ mkdirSync4(dirname2(target), { recursive: true });
31167
31219
  const fps = options.fps ? parseCoord(options.fps, "--fps") : 60;
31168
31220
  const { width, height } = configuredDesktopDimensions();
31169
- mkdirSync3(STATE_DIR, { recursive: true });
31221
+ mkdirSync4(STATE_DIR, { recursive: true });
31170
31222
  const rawTarget = `${target}.raw-${Date.now()}.mp4`;
31171
31223
  rmSync3(RECORD_ACTIONS_FILE, { force: true });
31172
31224
  const child = spawn4("ffmpeg", [
@@ -31200,17 +31252,17 @@ async function computerRecordStartCommand(path6, options) {
31200
31252
  ], { detached: true, stdio: "ignore" });
31201
31253
  child.unref();
31202
31254
  if (!child.pid) fail("failed to launch ffmpeg");
31203
- writeFileSync3(RECORD_PID_FILE, String(child.pid));
31204
- writeFileSync3(RECORD_PATH_FILE, target);
31205
- writeFileSync3(RECORD_RAW_PATH_FILE, rawTarget);
31206
- writeFileSync3(RECORD_STARTED_AT_FILE, String(Date.now()));
31207
- writeFileSync3(RECORD_FPS_FILE, String(fps));
31208
- writeFileSync3(RECORD_DIMENSIONS_FILE, JSON.stringify({ width, height }));
31255
+ writeFileSync4(RECORD_PID_FILE, String(child.pid));
31256
+ writeFileSync4(RECORD_PATH_FILE, target);
31257
+ writeFileSync4(RECORD_RAW_PATH_FILE, rawTarget);
31258
+ writeFileSync4(RECORD_STARTED_AT_FILE, String(Date.now()));
31259
+ writeFileSync4(RECORD_FPS_FILE, String(fps));
31260
+ writeFileSync4(RECORD_DIMENSIONS_FILE, JSON.stringify({ width, height }));
31209
31261
  const startedAt = Date.now();
31210
31262
  while (Date.now() - startedAt < 5e3) {
31211
31263
  try {
31212
31264
  process.kill(child.pid, 0);
31213
- if (existsSync2(rawTarget) && statSync(rawTarget).size > 0) {
31265
+ if (existsSync2(rawTarget) && statSync2(rawTarget).size > 0) {
31214
31266
  console.log(`${target} (recording ready in ${Date.now() - startedAt}ms)`);
31215
31267
  return;
31216
31268
  }
@@ -31233,7 +31285,7 @@ async function computerRecordStartCommand(path6, options) {
31233
31285
  async function computerRecordStopCommand() {
31234
31286
  if (!existsSync2(RECORD_PID_FILE) && !existsSync2(RECORD_PATH_FILE)) fail("no recording in progress");
31235
31287
  if (existsSync2(RECORD_PID_FILE)) {
31236
- const pid = parseInt(readFileSync3(RECORD_PID_FILE, "utf8").trim(), 10);
31288
+ const pid = parseInt(readFileSync4(RECORD_PID_FILE, "utf8").trim(), 10);
31237
31289
  if (!Number.isFinite(pid)) fail("invalid recording pidfile");
31238
31290
  try {
31239
31291
  process.kill(pid, "SIGINT");
@@ -31252,9 +31304,9 @@ async function computerRecordStopCommand() {
31252
31304
  if (alive) fail(`ffmpeg did not finalize recording within 30 seconds (pid ${pid})`);
31253
31305
  }
31254
31306
  if (existsSync2(RECORD_PATH_FILE)) {
31255
- const target = readFileSync3(RECORD_PATH_FILE, "utf8").trim();
31256
- const rawPath = existsSync2(RECORD_RAW_PATH_FILE) ? readFileSync3(RECORD_RAW_PATH_FILE, "utf8").trim() : target;
31257
- const fps = existsSync2(RECORD_FPS_FILE) ? parseInt(readFileSync3(RECORD_FPS_FILE, "utf8").trim(), 10) : 60;
31307
+ const target = readFileSync4(RECORD_PATH_FILE, "utf8").trim();
31308
+ const rawPath = existsSync2(RECORD_RAW_PATH_FILE) ? readFileSync4(RECORD_RAW_PATH_FILE, "utf8").trim() : target;
31309
+ const fps = existsSync2(RECORD_FPS_FILE) ? parseInt(readFileSync4(RECORD_FPS_FILE, "utf8").trim(), 10) : 60;
31258
31310
  const size = readRecordingDimensions();
31259
31311
  const actions = readRecordingActions();
31260
31312
  if (rawPath !== target) {
@@ -31346,7 +31398,7 @@ function loadBrandSvg(canvasW, canvasH) {
31346
31398
  `Brand wallpaper SVG missing at ${path6}. The workspace image is out of date \u2014 \`desktop/brand-wallpaper.svg\` must be installed at $REPLICAS_DESKTOP_TEMPLATES.`
31347
31399
  );
31348
31400
  }
31349
- return readFileSync4(path6, "utf8").replace(/(<svg[^>]*\s)width="\d+"/, `$1width="${canvasW}"`).replace(/(<svg[^>]*\s)height="\d+"/, `$1height="${canvasH}"`);
31401
+ return readFileSync5(path6, "utf8").replace(/(<svg[^>]*\s)width="\d+"/, `$1width="${canvasW}"`).replace(/(<svg[^>]*\s)height="\d+"/, `$1height="${canvasH}"`);
31350
31402
  }
31351
31403
  var BRAND_PAD_FRACTION = 0.06;
31352
31404
  var SCREENSHOT_CORNER_FRACTION = 0.022;
@@ -31381,7 +31433,7 @@ ${labels.join("\n")}
31381
31433
  </svg>`;
31382
31434
  }
31383
31435
  function overlayGrid(rawPath, target, width, height, gridSize, gridPath) {
31384
- writeFileSync4(gridPath, buildGridSvg(width, height, gridSize));
31436
+ writeFileSync5(gridPath, buildGridSvg(width, height, gridSize));
31385
31437
  const r = spawnSync3(
31386
31438
  "ffmpeg",
31387
31439
  [
@@ -31409,7 +31461,7 @@ function overlayGrid(rawPath, target, width, height, gridSize, gridPath) {
31409
31461
  }
31410
31462
  async function computerScreenshotCommand(path6, options = {}) {
31411
31463
  const target = resolvePath(path6);
31412
- mkdirSync4(dirname3(target), { recursive: true });
31464
+ mkdirSync5(dirname3(target), { recursive: true });
31413
31465
  const stamp = `${process.pid}-${Date.now()}`;
31414
31466
  const rawPath = `/tmp/replicas-screenshot-${stamp}.raw.png`;
31415
31467
  const svgPath = `/tmp/replicas-screenshot-${stamp}.brand.svg`;
@@ -31441,12 +31493,12 @@ async function computerScreenshotCommand(path6, options = {}) {
31441
31493
  const shadowMargin = shadowSigma * 3;
31442
31494
  const shadowW = width + shadowMargin * 2;
31443
31495
  const shadowH = height + shadowMargin * 2;
31444
- writeFileSync4(svgPath, loadBrandSvg(canvasW, canvasH));
31445
- writeFileSync4(
31496
+ writeFileSync5(svgPath, loadBrandSvg(canvasW, canvasH));
31497
+ writeFileSync5(
31446
31498
  maskPath,
31447
31499
  SCREENSHOT_MASK_TEMPLATE.replace(/__W__/g, String(width)).replace(/__H__/g, String(height)).replace(/__R__/g, String(cornerR))
31448
31500
  );
31449
- writeFileSync4(
31501
+ writeFileSync5(
31450
31502
  shadowPath,
31451
31503
  SHADOW_MASK_TEMPLATE.replace(/__SW__/g, String(shadowW)).replace(/__SH__/g, String(shadowH)).replace(/__M__/g, String(shadowMargin)).replace(/__W__/g, String(width)).replace(/__H__/g, String(height)).replace(/__R__/g, String(cornerR))
31452
31504
  );
@@ -31488,7 +31540,7 @@ async function computerScreenshotCommand(path6, options = {}) {
31488
31540
  console.log(target);
31489
31541
  }
31490
31542
  function hashFile(path6) {
31491
- return createHash2("sha256").update(readFileSync4(path6)).digest("hex");
31543
+ return createHash3("sha256").update(readFileSync5(path6)).digest("hex");
31492
31544
  }
31493
31545
  async function captureStableRawScreenshot(target, options) {
31494
31546
  const start = Date.now();
@@ -31539,7 +31591,7 @@ function recordingMousePosition() {
31539
31591
  }
31540
31592
  async function computerObserveCommand(path6, options = {}) {
31541
31593
  const target = resolvePath(path6);
31542
- mkdirSync4(dirname3(target), { recursive: true });
31594
+ mkdirSync5(dirname3(target), { recursive: true });
31543
31595
  const timeoutMs = options.timeout ? parseCoord(options.timeout, "--timeout") : 3e3;
31544
31596
  const stableMs = options.stableMs ? parseCoord(options.stableMs, "--stable-ms") : 600;
31545
31597
  const pollMs = options.pollMs ? parseCoord(options.pollMs, "--poll-ms") : 200;
@@ -31774,7 +31826,7 @@ function browserStateProperties(node) {
31774
31826
  function readBrowserStateCache(path6) {
31775
31827
  let value;
31776
31828
  try {
31777
- value = JSON.parse(readFileSync4(path6, "utf8"));
31829
+ value = JSON.parse(readFileSync5(path6, "utf8"));
31778
31830
  } catch {
31779
31831
  return null;
31780
31832
  }
@@ -32120,7 +32172,7 @@ async function captureBrowserSnapshot(page, options) {
32120
32172
  visibleTextLength += Math.min(name.length, remaining) + 1;
32121
32173
  }
32122
32174
  }
32123
- const revision = createHash2("sha256").update(JSON.stringify([page.url, entries.map(({ key, semantic }) => [key, semantic])])).digest("hex").slice(0, 16);
32175
+ const revision = createHash3("sha256").update(JSON.stringify([page.url, entries.map(({ key, semantic }) => [key, semantic])])).digest("hex").slice(0, 16);
32124
32176
  const snapshot = {
32125
32177
  title: (page.title ?? "").slice(0, 1e3),
32126
32178
  url: (page.url ?? "").slice(0, 2e3),
@@ -32300,7 +32352,7 @@ async function computerBrowserStateCommand(path6, options = {}) {
32300
32352
  const page = await selectChromePage(options);
32301
32353
  const stability = await waitForBrowserStability(page, { timeoutMs, stableMs, pollMs });
32302
32354
  const target = resolvePath(path6);
32303
- mkdirSync4(dirname3(target), { recursive: true });
32355
+ mkdirSync5(dirname3(target), { recursive: true });
32304
32356
  const [{ snapshot, entries }, screenshotResult] = await Promise.all([
32305
32357
  captureBrowserSnapshot(page, { textLimit, elementLimit }),
32306
32358
  sendChromeCommand(page.webSocketDebuggerUrl, "Page.captureScreenshot", {
@@ -32311,7 +32363,7 @@ async function computerBrowserStateCommand(path6, options = {}) {
32311
32363
  ]);
32312
32364
  const data = screenshotResult.data;
32313
32365
  if (typeof data !== "string") fail("Chrome did not return screenshot data");
32314
- writeFileSync4(target, Buffer.from(data, "base64"));
32366
+ writeFileSync5(target, Buffer.from(data, "base64"));
32315
32367
  const screenshot = readPngDimensions(target);
32316
32368
  const targetId = page.id ?? "unknown";
32317
32369
  const cachePath = browserStateCachePath(targetId);
@@ -32327,8 +32379,8 @@ async function computerBrowserStateCommand(path6, options = {}) {
32327
32379
  changes = { added: diff.added, changed: diff.changed, removed: diff.removed };
32328
32380
  }
32329
32381
  }
32330
- mkdirSync4(STATE_DIR, { recursive: true });
32331
- writeFileSync4(cachePath, JSON.stringify({ url: snapshot.url, documentId: snapshot.documentId, entries }));
32382
+ mkdirSync5(STATE_DIR, { recursive: true });
32383
+ writeFileSync5(cachePath, JSON.stringify({ url: snapshot.url, documentId: snapshot.documentId, entries }));
32332
32384
  const stableState = isRecord(stability.state) ? stability.state : {};
32333
32385
  const state = {
32334
32386
  title: snapshot.title,
@@ -37696,7 +37748,7 @@ if (isAgentMode()) {
37696
37748
  }
37697
37749
  });
37698
37750
  const learnings = program.command("learnings").description("Fetch curated org knowledge and propose changes for human review");
37699
- learnings.command("read").description("Fetch learnings relevant to a task. Enrich the query with codebase specifics, not the raw user request.").requiredOption("-q, --query <query>", "Enriched task description to match against learning triggers").action(async (options) => {
37751
+ learnings.command("read").description("Fetch learnings relevant to a task. Enrich the query with codebase specifics, not the raw user request.").requiredOption("-q, --query <query>", "Enriched task description to match against learning triggers").option("-f, --fresh", "Print full text even for learnings already shown in this session").action(async (options) => {
37700
37752
  try {
37701
37753
  await learningsReadCommand(options);
37702
37754
  } catch (error51) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-cli",
3
- "version": "0.2.424",
3
+ "version": "0.2.425",
4
4
  "description": "CLI for managing Replicas workspaces - SSH into cloud dev environments with automatic port forwarding",
5
5
  "main": "dist/index.mjs",
6
6
  "bin": {