flavor-code 1.4.3-beta.2 → 1.4.3

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 (33) hide show
  1. package/README.md +25 -0
  2. package/README.zh-CN.md +25 -0
  3. package/dist/agent/loop.d.ts +1 -0
  4. package/dist/{app-G35YBHPR.js → app-TVY2HM5L.js} +342 -112
  5. package/dist/{chunk-FAJ6YDZK.js → chunk-36IDJRLQ.js} +1 -1
  6. package/dist/{chunk-HGTCIHWA.js → chunk-5BDKRA6U.js} +30 -1
  7. package/dist/{chunk-T6RE76D2.js → chunk-IKGT4MDT.js} +35 -23
  8. package/dist/{chunk-K7UGGWJV.js → chunk-JQM2L2YM.js} +1 -1
  9. package/dist/{chunk-6X62CGMA.js → chunk-ONRY3C2L.js} +3 -0
  10. package/dist/{chunk-IDJAHEG6.js → chunk-QACFKPZP.js} +391 -188
  11. package/dist/{chunk-SJUIIHIA.js → chunk-X6U6BO5D.js} +1 -1
  12. package/dist/{claude-ink-BE56DB6N.js → claude-ink-F554I3GJ.js} +1 -1
  13. package/dist/{cli-3MF6EL2H.js → cli-MAWCV2YR.js} +7 -7
  14. package/dist/cli-main.js +16 -16
  15. package/dist/config/protected-file.d.ts +2 -0
  16. package/dist/config/schema.d.ts +3 -0
  17. package/dist/context/global-instructions.d.ts +12 -0
  18. package/dist/context/manager.d.ts +2 -0
  19. package/dist/desktop/main.js +651 -419
  20. package/dist/desktop-renderer/assets/{index-DCqIrF1S.js → index-CjC0Ibiq.js} +2 -2
  21. package/dist/desktop-renderer/assets/{interactive-terminal-B6GbpzIn.js → interactive-terminal-IjIT3Xoa.js} +1 -1
  22. package/dist/desktop-renderer/index.html +1 -1
  23. package/dist/{doctor-OHCGK52S.js → doctor-SEL32R7M.js} +3 -3
  24. package/dist/harness/local.d.ts +1 -0
  25. package/dist/{load-QK6CMLDF.js → load-RHWXNUCS.js} +2 -2
  26. package/dist/{manager-5SI4MPES.js → manager-IYBNNKUP.js} +2 -2
  27. package/dist/{production-6DIZ6YLQ.js → production-TQROFEPV.js} +6 -6
  28. package/dist/sdk/index.js +6 -6
  29. package/dist/{store-ZIHN6363.js → store-MZZERW7K.js} +2 -2
  30. package/dist/ui/commands.d.ts +11 -3
  31. package/dist/ui/session.d.ts +2 -1
  32. package/package.json +1 -1
  33. package//346/212/200/346/234/257/346/226/271/346/241/210/346/212/245/345/221/212.md +649 -1
@@ -1,8 +1,8 @@
1
1
  // src/desktop/main.ts
2
2
  import { appendFileSync } from "fs";
3
3
  import { createHash as createHash29, randomUUID as randomUUID29 } from "crypto";
4
- import { readFile as readFile41, stat as stat20, writeFile as writeFile19 } from "fs/promises";
5
- import { basename as basename12, dirname as dirname23, join as join51, resolve as resolve43 } from "path";
4
+ import { readFile as readFile42, stat as stat20, writeFile as writeFile19 } from "fs/promises";
5
+ import { basename as basename12, dirname as dirname23, join as join52, resolve as resolve43 } from "path";
6
6
  import { fileURLToPath as fileURLToPath3 } from "url";
7
7
  import { app, BrowserWindow as BrowserWindow2, clipboard, dialog, ipcMain, Menu, nativeImage, Notification, shell } from "electron";
8
8
 
@@ -262,6 +262,9 @@ var FlavorConfigSchema = z2.object({
262
262
  skills: z2.object({
263
263
  disabled: z2.array(SkillNameSchema).max(1e3).default([])
264
264
  }).prefault({}),
265
+ globalInstructions: z2.object({
266
+ enabled: z2.boolean().default(true)
267
+ }).prefault({}),
265
268
  agents: z2.object({
266
269
  main: z2.object({ model: z2.string() }).optional(),
267
270
  subagent: z2.object({ model: z2.string() }).optional()
@@ -2326,6 +2329,34 @@ async function updateProtectedFile(options) {
2326
2329
  return next;
2327
2330
  }, options.lockTimeoutMs ?? LOCK_TIMEOUT_MS, options.staleLockMs ?? STALE_LOCK_MS);
2328
2331
  }
2332
+ async function updatePlainTextFile(path, update, maxBytes) {
2333
+ await mkdir2(dirname(path), { recursive: true, mode: 448 });
2334
+ return withFileLock(path, async () => {
2335
+ const current = await readOptionalPlainText(path, maxBytes);
2336
+ const next = await update(current);
2337
+ if (next === void 0 || next === current) return current;
2338
+ if (await readOptionalPlainText(path, maxBytes) !== current) {
2339
+ throw new Error(`Text file changed while the update was prepared: ${path}`);
2340
+ }
2341
+ await writeAtomic(path, next, defaultFileOperations);
2342
+ return next;
2343
+ }, LOCK_TIMEOUT_MS, STALE_LOCK_MS);
2344
+ }
2345
+ async function readOptionalPlainText(path, maxBytes) {
2346
+ try {
2347
+ if (maxBytes !== void 0 && (await stat(path)).size > maxBytes) {
2348
+ throw new Error(`Text file exceeds ${maxBytes} bytes: ${path}`);
2349
+ }
2350
+ const bytes = await readFile(path);
2351
+ if (maxBytes !== void 0 && bytes.length > maxBytes) {
2352
+ throw new Error(`Text file exceeds ${maxBytes} bytes: ${path}`);
2353
+ }
2354
+ return new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes);
2355
+ } catch (error) {
2356
+ if (isCode2(error, "ENOENT")) return void 0;
2357
+ throw error;
2358
+ }
2359
+ }
2329
2360
  async function withFileLock(path, operation, lockTimeoutMs, staleLockMs) {
2330
2361
  const lockPath = `${path}.lock`;
2331
2362
  const token = JSON.stringify({ pid: process.pid, nonce: randomUUID3() });
@@ -7132,11 +7163,11 @@ function createElectronBrowserHost(options) {
7132
7163
  }
7133
7164
 
7134
7165
  // src/production.ts
7135
- import { readFile as readFile29 } from "fs/promises";
7166
+ import { readFile as readFile30 } from "fs/promises";
7136
7167
  import { mkdirSync as mkdirSync4, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync as unlinkSync3, writeFileSync as writeFileSync3 } from "fs";
7137
7168
  import { createHash as createHash22, randomUUID as randomUUID25 } from "crypto";
7138
7169
  import { homedir as homedir3, release as osRelease, tmpdir as tmpdir4, version as osVersion } from "os";
7139
- import { basename as basename9, dirname as dirname22, join as join38, relative as relative25, resolve as resolve36, sep as sep25 } from "path";
7170
+ import { basename as basename9, dirname as dirname22, join as join39, relative as relative25, resolve as resolve36, sep as sep25 } from "path";
7140
7171
  import { performance } from "perf_hooks";
7141
7172
 
7142
7173
  // src/agent/subagents.ts
@@ -8267,6 +8298,7 @@ var ContextManager = class _ContextManager {
8267
8298
  #workspaceInstructions;
8268
8299
  #memory;
8269
8300
  #userMemory;
8301
+ #globalInstructions;
8270
8302
  #compactAtChars;
8271
8303
  #toolOutputChars;
8272
8304
  #recentTurns;
@@ -8311,6 +8343,7 @@ var ContextManager = class _ContextManager {
8311
8343
  this.#workspaceInstructions = options.workspaceInstructions;
8312
8344
  this.#memory = options.memory;
8313
8345
  this.#userMemory = options.userMemory;
8346
+ this.#globalInstructions = options.globalInstructions;
8314
8347
  this.#taskState = options.taskState;
8315
8348
  this.#compactAtChars = options.compactAtChars ?? Number.POSITIVE_INFINITY;
8316
8349
  this.#toolOutputChars = options.toolOutputChars;
@@ -8350,6 +8383,7 @@ var ContextManager = class _ContextManager {
8350
8383
  ...this.#memory === void 0 ? {} : { memory: this.#memory },
8351
8384
  ...this.#taskState === void 0 ? {} : { taskState: this.#taskState },
8352
8385
  ...userMemory === void 0 ? {} : { userMemory },
8386
+ ...this.#globalInstructions === void 0 ? {} : { globalInstructions: this.#globalInstructions },
8353
8387
  compactAtChars: this.#compactAtChars,
8354
8388
  toolOutputChars: this.#toolOutputChars,
8355
8389
  compaction: this.#compaction,
@@ -8431,12 +8465,14 @@ var ContextManager = class _ContextManager {
8431
8465
  for (const key of DYNAMIC_SOURCE_ORDER) {
8432
8466
  const value = next[key];
8433
8467
  if (value === void 0 || value === this.#epoch.sources[key]) continue;
8434
- updates.push(contextUpdateMessage(key, deltaSourceValue(this.#epoch.sources[key], displaySourceValue(key, value))));
8468
+ const display = key === "global-instructions" ? this.#epoch.sources[key] === void 0 ? value : `These replace all earlier global instructions.
8469
+ ${value}` : deltaSourceValue(this.#epoch.sources[key], displaySourceValue(key, value));
8470
+ updates.push(contextUpdateMessage(key, display));
8435
8471
  this.#epoch.sources[key] = value;
8436
8472
  }
8437
8473
  for (const key of DYNAMIC_SOURCE_ORDER) {
8438
8474
  if (key in next || !(key in this.#epoch.sources)) continue;
8439
- updates.push(contextUpdateMessage(key, "(removed)"));
8475
+ updates.push(contextUpdateMessage(key, key === "global-instructions" ? "Global instructions were removed; disregard all earlier global instructions." : "(removed)"));
8440
8476
  delete this.#epoch.sources[key];
8441
8477
  }
8442
8478
  if (updates.length === 0) return false;
@@ -8456,9 +8492,15 @@ var ContextManager = class _ContextManager {
8456
8492
  const legacySummary = snapshot4.summary?.role === "system" && snapshot4.summary.content.startsWith("Conversation summary\n") ? snapshot4.summary.content.slice("Conversation summary\n".length) : void 0;
8457
8493
  this.#compact = snapshot4.compact === void 0 ? legacySummary === void 0 ? void 0 : { summary: legacySummary, compactedAt: (/* @__PURE__ */ new Date(0)).toISOString() } : { ...snapshot4.compact };
8458
8494
  if (snapshot4.epoch !== void 0) this.#epoch = cloneEpoch(snapshot4.epoch);
8495
+ const globalInstructions = tryResolveOptionalSystemSections(this.#globalInstructions);
8496
+ const globalInstructionsAbsent = globalInstructions.available && (globalInstructions.sections?.length ?? 0) === 0;
8497
+ if (globalInstructionsAbsent) {
8498
+ delete this.#epoch.sources["global-instructions"];
8499
+ if (this.#epoch.pinnedSources !== void 0) delete this.#epoch.pinnedSources["global-instructions"];
8500
+ }
8459
8501
  this.#visibilityLog = boundVisibilityLog(snapshot4.visibilityLog);
8460
8502
  this.#activeTransientSystem.clear();
8461
- this.#messages = messages.map((message2) => message2.role === "tool" ? { ...message2, content: truncateToolOutput(message2.content, this.#toolOutputChars) } : cloneMessage2(message2));
8503
+ this.#messages = (globalInstructionsAbsent ? messages.filter((message2) => message2.role !== "system" || !modelContentText(message2.content).startsWith("Context update [global-instructions]\n")) : messages).map((message2) => message2.role === "tool" ? { ...message2, content: truncateToolOutput(message2.content, this.#toolOutputChars) } : cloneMessage2(message2));
8462
8504
  this.#lastRecordedInputTokens = void 0;
8463
8505
  this.#estimatedTokensAtLastRecordedUsage = void 0;
8464
8506
  this.#consecutiveAutoCompactFailures = 0;
@@ -8674,6 +8716,12 @@ ${userMemory}`,
8674
8716
  }
8675
8717
  #resolvedDynamicSources(previous = {}) {
8676
8718
  const result = {};
8719
+ const globalInstructions = tryResolveOptionalSystemSections(this.#globalInstructions);
8720
+ if (!globalInstructions.available && previous["global-instructions"] !== void 0) {
8721
+ result["global-instructions"] = previous["global-instructions"];
8722
+ } else if (globalInstructions.sections !== void 0 && globalInstructions.sections.length > 0) {
8723
+ result["global-instructions"] = globalInstructions.sections.join("\n\n");
8724
+ }
8677
8725
  if (this.#memory !== void 0) result["long-term-memory"] = this.#memory;
8678
8726
  if (this.#taskState !== void 0) result["task-state"] = this.#taskState;
8679
8727
  const volatile = tryResolveOptionalSystemSections(this.#volatileSystem);
@@ -8682,7 +8730,7 @@ ${userMemory}`,
8682
8730
  return result;
8683
8731
  }
8684
8732
  };
8685
- var DYNAMIC_SOURCE_ORDER = ["long-term-memory", "task-state", "runtime"];
8733
+ var DYNAMIC_SOURCE_ORDER = ["global-instructions", "long-term-memory", "task-state", "runtime"];
8686
8734
  function sourceMessages(sources) {
8687
8735
  return DYNAMIC_SOURCE_ORDER.flatMap((key) => {
8688
8736
  const content = sources[key];
@@ -8696,7 +8744,7 @@ function sourceMessages(sources) {
8696
8744
  } catch {
8697
8745
  }
8698
8746
  }
8699
- const label = key === "long-term-memory" ? "Long-term memory" : key === "task-state" ? "Task state" : "Runtime";
8747
+ const label = key === "global-instructions" ? "Global instructions" : key === "long-term-memory" ? "Long-term memory" : key === "task-state" ? "Task state" : "Runtime";
8700
8748
  return [{
8701
8749
  role: "system",
8702
8750
  content: `${label}
@@ -8852,9 +8900,122 @@ function providerValidMessages(input) {
8852
8900
  return output;
8853
8901
  }
8854
8902
 
8903
+ // src/context/global-instructions.ts
8904
+ import { lstat as lstat2, readFile as readFile6 } from "fs/promises";
8905
+ import { join as join7 } from "path";
8906
+ var GlobalInstructions = class _GlobalInstructions {
8907
+ static MAX_BYTES = 64 * 1024;
8908
+ static MAX_RULE_BYTES = 4 * 1024;
8909
+ path;
8910
+ #content;
8911
+ constructor(home) {
8912
+ this.path = join7(home, ".flavor-code", "GLOBAL.md");
8913
+ }
8914
+ get content() {
8915
+ return this.#content;
8916
+ }
8917
+ async refresh() {
8918
+ let info;
8919
+ try {
8920
+ info = await lstat2(this.path);
8921
+ } catch (error) {
8922
+ if (isMissing(error)) {
8923
+ this.#content = void 0;
8924
+ return;
8925
+ }
8926
+ throw error;
8927
+ }
8928
+ if (!info.isFile()) throw new Error(`Global instructions must be a regular file: ${this.path}`);
8929
+ if (info.size > _GlobalInstructions.MAX_BYTES) {
8930
+ throw new Error(`Global instructions exceed ${_GlobalInstructions.MAX_BYTES} bytes: ${this.path}`);
8931
+ }
8932
+ const bytes = await readFile6(this.path);
8933
+ if (bytes.length > _GlobalInstructions.MAX_BYTES) {
8934
+ throw new Error(`Global instructions exceed ${_GlobalInstructions.MAX_BYTES} bytes: ${this.path}`);
8935
+ }
8936
+ const content = new TextDecoder("utf-8", { fatal: true }).decode(bytes).trim();
8937
+ this.#content = content || void 0;
8938
+ }
8939
+ async remember(input) {
8940
+ const rule = input.trim();
8941
+ if (!rule || /[\r\n]/u.test(rule)) throw new Error("Global rules must be one non-empty line.");
8942
+ if (Buffer.byteLength(rule, "utf8") > _GlobalInstructions.MAX_RULE_BYTES) {
8943
+ throw new Error(`Global rule exceeds ${_GlobalInstructions.MAX_RULE_BYTES} bytes.`);
8944
+ }
8945
+ let added = false;
8946
+ const next = await updatePlainTextFile(this.path, async (current) => {
8947
+ const exists4 = await regularFileExists(this.path);
8948
+ const base = exists4 ? current ?? "" : "";
8949
+ validateDocument(base);
8950
+ if (ruleLines(base).some((item) => item.text.toLocaleLowerCase() === rule.toLocaleLowerCase())) return base;
8951
+ const eol = base.includes("\r\n") ? "\r\n" : "\n";
8952
+ const nextContent = base.length === 0 ? `# Global instructions${eol}${eol}- ${rule}${eol}` : `${base}${base.endsWith(eol) ? "" : eol}${eol}- ${rule}${eol}`;
8953
+ validateDocument(nextContent);
8954
+ added = true;
8955
+ return nextContent;
8956
+ }, _GlobalInstructions.MAX_BYTES);
8957
+ this.#content = next?.trim() || void 0;
8958
+ return added ? "added" : "already-present";
8959
+ }
8960
+ async forget(input) {
8961
+ const query = input.trim().toLocaleLowerCase();
8962
+ if (!query || /[\r\n]/u.test(query)) throw new Error("Specify one global rule to forget.");
8963
+ if (!await regularFileExists(this.path)) return "not-found";
8964
+ let result = "not-found";
8965
+ const next = await updatePlainTextFile(this.path, async (current) => {
8966
+ if (!await regularFileExists(this.path)) return void 0;
8967
+ const base = current ?? "";
8968
+ validateDocument(base);
8969
+ const entries = ruleLines(base);
8970
+ const exact = entries.filter((entry2) => entry2.text.toLocaleLowerCase() === query);
8971
+ const matches = exact.length > 0 ? exact : entries.filter((entry2) => entry2.text.toLocaleLowerCase().includes(query));
8972
+ if (matches.length === 0) return base;
8973
+ if (matches.length > 1) {
8974
+ result = "ambiguous";
8975
+ return base;
8976
+ }
8977
+ const match = matches[0];
8978
+ result = "removed";
8979
+ const remaining = `${base.slice(0, match.start)}${base.slice(match.end)}`;
8980
+ return remaining.trim() === "# Global instructions" ? "" : remaining;
8981
+ }, _GlobalInstructions.MAX_BYTES);
8982
+ this.#content = next?.trim() || void 0;
8983
+ return result;
8984
+ }
8985
+ };
8986
+ function ruleLines(content) {
8987
+ return [...content.matchAll(/^[ \t]*[-*+][ \t]+([^\r\n]+)(?:\r?\n|$)/gmu)].map((match) => ({
8988
+ text: match[1].trim(),
8989
+ start: match.index,
8990
+ end: match.index + match[0].length
8991
+ }));
8992
+ }
8993
+ function validateDocument(content) {
8994
+ if (Buffer.byteLength(content, "utf8") > GlobalInstructions.MAX_BYTES) {
8995
+ throw new Error(`Global instructions exceed ${GlobalInstructions.MAX_BYTES} bytes.`);
8996
+ }
8997
+ return content;
8998
+ }
8999
+ async function regularFileExists(path) {
9000
+ try {
9001
+ const info = await lstat2(path);
9002
+ if (!info.isFile()) throw new Error(`Global instructions must be a regular file: ${path}`);
9003
+ if (info.size > GlobalInstructions.MAX_BYTES) {
9004
+ throw new Error(`Global instructions exceed ${GlobalInstructions.MAX_BYTES} bytes: ${path}`);
9005
+ }
9006
+ return true;
9007
+ } catch (error) {
9008
+ if (isMissing(error)) return false;
9009
+ throw error;
9010
+ }
9011
+ }
9012
+ function isMissing(error) {
9013
+ return error instanceof Error && "code" in error && error.code === "ENOENT";
9014
+ }
9015
+
8855
9016
  // src/context/workspace-instructions.ts
8856
9017
  import { createHash as createHash5 } from "crypto";
8857
- import { lstat as lstat2, readFile as readFile6, realpath as realpath4 } from "fs/promises";
9018
+ import { lstat as lstat3, readFile as readFile7, realpath as realpath4 } from "fs/promises";
8858
9019
  import { dirname as dirname5, isAbsolute as isAbsolute4, relative as relative4, resolve as resolve5, sep as sep4 } from "path";
8859
9020
  var NAMES = ["AGENTS.md", "CLAUDE.md", "AGENTS.local.md", "CLAUDE.local.md"];
8860
9021
  var ROOT_NAMES = ["AGENTS.md", "CLAUDE.md", "AGENTS.local.md", "CLAUDE.local.md"];
@@ -8914,11 +9075,11 @@ var WorkspaceInstructions = class {
8914
9075
  for (const name of names) {
8915
9076
  const path = resolve5(directory.path, name);
8916
9077
  try {
8917
- const info = await lstat2(path);
9078
+ const info = await lstat3(path);
8918
9079
  if (!info.isFile() && !info.isSymbolicLink()) continue;
8919
9080
  const physical = await realpath4(path);
8920
9081
  if (!within(physicalRoot, physical)) continue;
8921
- const bytes = await readFile6(path);
9082
+ const bytes = await readFile7(path);
8922
9083
  const clipped = bytes.subarray(0, this.#maxFileBytes);
8923
9084
  const suffix = bytes.length > clipped.length ? `
8924
9085
 
@@ -8957,7 +9118,7 @@ ${document.content}
8957
9118
  };
8958
9119
  async function directoryForPath(path) {
8959
9120
  try {
8960
- return (await lstat2(path)).isDirectory() ? path : dirname5(path);
9121
+ return (await lstat3(path)).isDirectory() ? path : dirname5(path);
8961
9122
  } catch {
8962
9123
  return dirname5(path);
8963
9124
  }
@@ -9754,6 +9915,12 @@ var AgentLoop = class {
9754
9915
  }
9755
9916
  }
9756
9917
  async *#runInternal(request) {
9918
+ try {
9919
+ await this.#options.beforeModelCall?.(request.signal);
9920
+ } catch (error) {
9921
+ yield { type: "error", error: normalizeProviderError(error) };
9922
+ return;
9923
+ }
9757
9924
  this.#options.context.refreshContextSources();
9758
9925
  this.#options.context.append(request.initialUserMessage ?? { role: "user", content: request.prompt });
9759
9926
  let totalInputTokens = 0;
@@ -9814,6 +9981,7 @@ var AgentLoop = class {
9814
9981
  return;
9815
9982
  }
9816
9983
  try {
9984
+ await this.#options.beforeModelCall?.(request.signal);
9817
9985
  if (await this.#options.context.prepareForModelCall(request.signal)) yield { type: "compacted" };
9818
9986
  } catch (error) {
9819
9987
  yield { type: "error", error: normalizeProviderError(error) };
@@ -10826,7 +10994,7 @@ function redactClassifierText(value) {
10826
10994
  // src/tools/runtime.ts
10827
10995
  import { randomUUID as randomUUID7 } from "crypto";
10828
10996
  import { mkdir as mkdir6, writeFile as writeFile3 } from "fs/promises";
10829
- import { basename as basename3, join as join7, resolve as resolve7 } from "path";
10997
+ import { basename as basename3, join as join8, resolve as resolve7 } from "path";
10830
10998
  import { z as z16 } from "zod";
10831
10999
  var DEFAULT_TOOL_OUTPUT_LIMITS = Object.freeze({
10832
11000
  perToolChars: 5e4,
@@ -11148,9 +11316,9 @@ var ToolRuntime = class {
11148
11316
  const reason = perToolExceeded && turnExceeded ? "per_tool_and_turn_limit" : perToolExceeded ? "per_tool_limit" : "turn_limit";
11149
11317
  const preview = headTailPreview(serialized, previewChars);
11150
11318
  this.#turnOutputChars += preview.length;
11151
- const directory = join7(this.#workspace, ".flavor", "tool-results");
11319
+ const directory = join8(this.#workspace, ".flavor", "tool-results");
11152
11320
  const filename2 = `${(/* @__PURE__ */ new Date()).toISOString().replaceAll(":", "-")}-${randomUUID7()}-${safeFilename(tool)}.txt`;
11153
- const savedTo = join7(directory, filename2);
11321
+ const savedTo = join8(directory, filename2);
11154
11322
  let storage;
11155
11323
  try {
11156
11324
  await mkdir6(directory, { recursive: true });
@@ -11369,6 +11537,7 @@ var LocalHarness = class {
11369
11537
  ...this.#options.softLimitFactor === void 0 ? {} : { softLimitFactor: this.#options.softLimitFactor },
11370
11538
  ...this.#options.extendIterations === void 0 ? {} : { extendIterations: this.#options.extendIterations },
11371
11539
  ...isMain && this.#options.hasActiveProgress !== void 0 ? { hasActiveProgress: this.#options.hasActiveProgress } : {},
11540
+ ...this.#options.beforeModelCall === void 0 ? {} : { beforeModelCall: this.#options.beforeModelCall },
11372
11541
  ...isMain && this.#options.hallucinationGuard !== void 0 ? { hallucinationGuard: this.#options.hallucinationGuard } : {},
11373
11542
  ...this.#options.modelJournal === void 0 ? {} : { modelJournal: this.#options.modelJournal }
11374
11543
  });
@@ -11398,7 +11567,7 @@ function toModelTool(tool) {
11398
11567
  // src/harness/journal.ts
11399
11568
  import { createHash as createHash6, randomUUID as randomUUID8 } from "crypto";
11400
11569
  import { closeSync, constants as constants2, existsSync as existsSync2, fsyncSync, mkdirSync, openSync, readFileSync, realpathSync as realpathSync2, renameSync, statSync, truncateSync, unlinkSync, writeSync } from "fs";
11401
- import { join as join8, relative as relative6, resolve as resolve8, sep as sep6 } from "path";
11570
+ import { join as join9, relative as relative6, resolve as resolve8, sep as sep6 } from "path";
11402
11571
  import { z as z17 } from "zod";
11403
11572
 
11404
11573
  // src/harness/invariants.ts
@@ -11488,14 +11657,14 @@ var HarnessJournal = class {
11488
11657
  #records = [];
11489
11658
  constructor(options) {
11490
11659
  const workspace = resolve8(options.workspace);
11491
- const directory = join8(workspace, ".flavor", "sessions");
11660
+ const directory = join9(workspace, ".flavor", "sessions");
11492
11661
  assertSessionId(options.sessionId);
11493
11662
  mkdirSync(directory, { recursive: true, mode: 448 });
11494
11663
  assertWithin(workspace, directory);
11495
11664
  const canonicalWorkspace = realpathSync2.native(workspace);
11496
11665
  const canonicalDirectory = realpathSync2.native(directory);
11497
11666
  if (!isWithin2(canonicalWorkspace, canonicalDirectory)) throw new Error("Harness journal directory escapes the workspace");
11498
- this.#path = join8(canonicalDirectory, `${options.sessionId}.events.jsonl`);
11667
+ this.#path = join9(canonicalDirectory, `${options.sessionId}.events.jsonl`);
11499
11668
  this.#now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
11500
11669
  this.#maxBytes = options.maxBytes ?? MAX_JOURNAL_BYTES;
11501
11670
  if (!Number.isSafeInteger(this.#maxBytes) || this.#maxBytes < 1024) throw new Error("maxBytes must be an integer of at least 1024");
@@ -12300,7 +12469,7 @@ function createIncidentReporter(options) {
12300
12469
  import { createHash as createHash7, randomUUID as randomUUID10 } from "crypto";
12301
12470
  import { chmod, unlink as unlink2 } from "fs/promises";
12302
12471
  import { tmpdir } from "os";
12303
- import { join as join9 } from "path";
12472
+ import { join as join10 } from "path";
12304
12473
  import { createServer as createServer2 } from "net";
12305
12474
  var MAX_REQUEST_BYTES = 64 * 1024;
12306
12475
  var SOCKET_TIMEOUT_MS = 1e4;
@@ -12308,7 +12477,7 @@ function islandControlEndpoint(sessionId, platform = process.platform) {
12308
12477
  const digest = createHash7("sha256").update(`${process.pid}\0${sessionId}`).digest("hex").slice(0, 20);
12309
12478
  if (platform === "win32") return `\\\\.\\pipe\\flavor-island-control-${digest}`;
12310
12479
  const uid = typeof process.getuid === "function" ? process.getuid() : 0;
12311
- return join9(tmpdir(), `flavor-island-control-${uid}-${digest}.sock`);
12480
+ return join10(tmpdir(), `flavor-island-control-${uid}-${digest}.sock`);
12312
12481
  }
12313
12482
  async function createIslandControlServer(options) {
12314
12483
  const platform = options.platform ?? process.platform;
@@ -12435,18 +12604,18 @@ function closeServer(server) {
12435
12604
  // src/evolve/service.ts
12436
12605
  import { spawn as spawn2 } from "child_process";
12437
12606
  import { writeFile as writeFile7 } from "fs/promises";
12438
- import { join as join12 } from "path";
12607
+ import { join as join13 } from "path";
12439
12608
  import { z as z21 } from "zod";
12440
12609
 
12441
12610
  // src/evolve/loader.ts
12442
12611
  import { cp as cp2, mkdir as mkdir7, mkdtemp, readdir as readdir3, rm as rm4, stat as stat5, writeFile as writeFile5 } from "fs/promises";
12443
12612
  import { tmpdir as tmpdir2 } from "os";
12444
- import { join as join10, resolve as resolve11 } from "path";
12613
+ import { join as join11, resolve as resolve11 } from "path";
12445
12614
 
12446
12615
  // src/plugins/host.ts
12447
12616
  import { createHash as createHash8 } from "crypto";
12448
12617
  import { lstatSync, realpathSync as realpathSync3 } from "fs";
12449
- import { lstat as lstat3, open as open4, readFile as readFile7, readdir as readdir2, realpath as realpath6, stat as stat4, writeFile as writeFile4 } from "fs/promises";
12618
+ import { lstat as lstat4, open as open4, readFile as readFile8, readdir as readdir2, realpath as realpath6, stat as stat4, writeFile as writeFile4 } from "fs/promises";
12450
12619
  import { isAbsolute as isAbsolute7, relative as relative7, resolve as resolve10, sep as sep7 } from "path";
12451
12620
  import { pathToFileURL as pathToFileURL2 } from "url";
12452
12621
  import { z as z20 } from "zod";
@@ -12795,7 +12964,7 @@ var PluginHost = class {
12795
12964
  async #readCandidate(inputRoot, source, label = inputRoot) {
12796
12965
  let diagnosticLabel = label;
12797
12966
  try {
12798
- const rootInfo = await lstat3(inputRoot);
12967
+ const rootInfo = await lstat4(inputRoot);
12799
12968
  if (!rootInfo.isDirectory() || rootInfo.isSymbolicLink()) throw new Error("Plugin root must be a static directory");
12800
12969
  const root = await realpath6(inputRoot);
12801
12970
  const rawManifest = await readManifest2(resolve10(root, MANIFEST));
@@ -12805,7 +12974,7 @@ var PluginHost = class {
12805
12974
  const manifest = PluginManifestSchema.parse(rawManifest);
12806
12975
  const entry2 = resolveContained(root, manifest.main, "Plugin entry");
12807
12976
  const entrySnapshot = await snapshotEntry(root, entry2);
12808
- const fingerprint2 = createHash8("sha256").update(JSON.stringify(manifest)).update(await readFile7(entrySnapshot.physicalPath)).digest("hex");
12977
+ const fingerprint2 = createHash8("sha256").update(JSON.stringify(manifest)).update(await readFile8(entrySnapshot.physicalPath)).digest("hex");
12809
12978
  return { manifest, root, source, entry: entry2, entrySnapshot, fingerprint: fingerprint2 };
12810
12979
  } catch (error) {
12811
12980
  this.#diagnose(diagnosticLabel, error, inputRoot);
@@ -13052,7 +13221,7 @@ var PluginHost = class {
13052
13221
  }
13053
13222
  };
13054
13223
  async function readManifest2(path) {
13055
- const lexical = await lstat3(path, { bigint: true });
13224
+ const lexical = await lstat4(path, { bigint: true });
13056
13225
  if (!lexical.isFile() || lexical.isSymbolicLink()) throw new Error("Plugin manifest must be a static regular file");
13057
13226
  const handle = await open4(path, "r");
13058
13227
  try {
@@ -13069,7 +13238,7 @@ async function readManifest2(path) {
13069
13238
  if (bytesRead > MAX_MANIFEST_BYTES) throw new Error(`Plugin manifest exceeds ${MAX_MANIFEST_BYTES} bytes`);
13070
13239
  const after = fromStats(await handle.stat({ bigint: true }));
13071
13240
  assertSnapshot(before, after, "Plugin manifest");
13072
- const current = fromStats(await lstat3(path, { bigint: true }));
13241
+ const current = fromStats(await lstat4(path, { bigint: true }));
13073
13242
  assertSnapshot(before, current, "Plugin manifest path");
13074
13243
  return JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(buffer.subarray(0, bytesRead)));
13075
13244
  } finally {
@@ -13077,7 +13246,7 @@ async function readManifest2(path) {
13077
13246
  }
13078
13247
  }
13079
13248
  async function snapshotEntry(root, entry2) {
13080
- const lexicalStats = await lstat3(entry2, { bigint: true });
13249
+ const lexicalStats = await lstat4(entry2, { bigint: true });
13081
13250
  if (!lexicalStats.isFile() && !lexicalStats.isSymbolicLink()) throw new Error("Plugin entry must be a file");
13082
13251
  const physicalPath = await realpath6(entry2);
13083
13252
  assertContained2(root, physicalPath, "Plugin entry symlink");
@@ -13165,8 +13334,8 @@ function compare(left, right) {
13165
13334
  }
13166
13335
 
13167
13336
  // src/evolve/loader.ts
13168
- var PLUGIN_DIR = join10(".flavor", "plugins");
13169
- var VERSIONS_DIR = join10(".flavor", "plugins", ".versions");
13337
+ var PLUGIN_DIR = join11(".flavor", "plugins");
13338
+ var VERSIONS_DIR = join11(".flavor", "plugins", ".versions");
13170
13339
  function sanitizePluginName(tool) {
13171
13340
  const base = String(tool ?? "fix").replace(/[^a-z0-9-]/gi, "-").toLowerCase();
13172
13341
  return base.startsWith("fix-") ? base : `fix-${base}`;
@@ -13210,8 +13379,8 @@ export function activate(ctx) {
13210
13379
  async function scaffoldFixPlugin(workspace, name) {
13211
13380
  const dir = fixPluginDir(workspace, name);
13212
13381
  await mkdir7(dir, { recursive: true });
13213
- const manifestPath = join10(dir, "flavor-plugin.json");
13214
- const entryPath = join10(dir, "index.js");
13382
+ const manifestPath = join11(dir, "flavor-plugin.json");
13383
+ const entryPath = join11(dir, "index.js");
13215
13384
  try {
13216
13385
  await writeFile5(manifestPath, SCAFFOLD_MANIFEST(name), { encoding: "utf8", flag: "wx" });
13217
13386
  await writeFile5(entryPath, SCAFFOLD_ENTRY(name), { encoding: "utf8", flag: "wx" });
@@ -13231,9 +13400,9 @@ async function verifyFixPlugin(workspace, name) {
13231
13400
  return { ok: false, provided: [], tools: [], commands: [], error: `Plugin "${name}" not found.` };
13232
13401
  }
13233
13402
  const collected = { tools: [], commands: [] };
13234
- const sandbox = await mkdtemp(join10(tmpdir2(), "flavor-evolve-verify-"));
13403
+ const sandbox = await mkdtemp(join11(tmpdir2(), "flavor-evolve-verify-"));
13235
13404
  try {
13236
- await cp2(pluginRoot, join10(sandbox, name), { recursive: true });
13405
+ await cp2(pluginRoot, join11(sandbox, name), { recursive: true });
13237
13406
  const host = new PluginHost({
13238
13407
  projectPluginDirs: [sandbox],
13239
13408
  sandbox: true,
@@ -13284,7 +13453,7 @@ async function verifyFixPlugin(workspace, name) {
13284
13453
  }
13285
13454
  async function snapshotFixPlugin(workspace, name) {
13286
13455
  const pluginRoot = fixPluginDir(workspace, name);
13287
- const snapshotRoot = join10(versionsDirFor(workspace, name), timestamp());
13456
+ const snapshotRoot = join11(versionsDirFor(workspace, name), timestamp());
13288
13457
  await mkdir7(snapshotRoot, { recursive: true });
13289
13458
  await cp2(pluginRoot, snapshotRoot, { recursive: true });
13290
13459
  return snapshotRoot;
@@ -13305,7 +13474,7 @@ async function revertFixPlugin(workspace, name) {
13305
13474
  }
13306
13475
  const snapshots = entries.filter((entry2) => !entry2.startsWith(".")).sort();
13307
13476
  if (snapshots.length === 0) throw new Error(`Plugin "${name}" has no snapshot to restore.`);
13308
- const latest = join10(versionsRoot, snapshots[snapshots.length - 1]);
13477
+ const latest = join11(versionsRoot, snapshots[snapshots.length - 1]);
13309
13478
  await rm4(pluginRoot, { recursive: true, force: true });
13310
13479
  await mkdir7(pluginRoot, { recursive: true });
13311
13480
  await cp2(latest, pluginRoot, { recursive: true });
@@ -13314,8 +13483,8 @@ async function revertFixPlugin(workspace, name) {
13314
13483
 
13315
13484
  // src/evolve/store.ts
13316
13485
  import { createHash as createHash9 } from "crypto";
13317
- import { appendFile, mkdir as mkdir8, readFile as readFile8, rm as rm5, writeFile as writeFile6 } from "fs/promises";
13318
- import { dirname as dirname8, join as join11 } from "path";
13486
+ import { appendFile, mkdir as mkdir8, readFile as readFile9, rm as rm5, writeFile as writeFile6 } from "fs/promises";
13487
+ import { dirname as dirname8, join as join12 } from "path";
13319
13488
  function normalizeError(message2) {
13320
13489
  return String(message2 ?? "").replace(/\s+/g, " ").replace(/"[^"]*"/g, '"\u2026"').replace(/`[^`]*`/g, '"\u2026"').trim().slice(0, 160);
13321
13490
  }
@@ -13328,7 +13497,7 @@ function argKeys(args) {
13328
13497
  }
13329
13498
  async function readJsonLines(file) {
13330
13499
  try {
13331
- const text = await readFile8(file, "utf8");
13500
+ const text = await readFile9(file, "utf8");
13332
13501
  const records = [];
13333
13502
  for (const line of text.split("\n")) {
13334
13503
  const trimmed = line.trim();
@@ -13351,7 +13520,7 @@ async function writeJsonLines(file, records) {
13351
13520
  }
13352
13521
  async function readJsonArray(file) {
13353
13522
  try {
13354
- const parsed = JSON.parse(await readFile8(file, "utf8"));
13523
+ const parsed = JSON.parse(await readFile9(file, "utf8"));
13355
13524
  return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string") : [];
13356
13525
  } catch {
13357
13526
  return [];
@@ -13359,7 +13528,7 @@ async function readJsonArray(file) {
13359
13528
  }
13360
13529
  async function readRules(file) {
13361
13530
  try {
13362
- const parsed = JSON.parse(await readFile8(file, "utf8"));
13531
+ const parsed = JSON.parse(await readFile9(file, "utf8"));
13363
13532
  if (!Array.isArray(parsed)) return [];
13364
13533
  return parsed.filter((item) => item !== null && typeof item === "object").filter((item) => typeof item.id === "string" && typeof item.text === "string").map((item) => ({
13365
13534
  id: item.id,
@@ -13388,12 +13557,12 @@ var EvolveStore = class {
13388
13557
  #queue = Promise.resolve();
13389
13558
  constructor(options) {
13390
13559
  const { workspace, maxSignals = 400, maxRules = 20 } = options;
13391
- this.dir = join11(workspace, ".flavor", "evolve");
13392
- this.signalsFile = join11(this.dir, "signals.jsonl");
13393
- this.reflectionsFile = join11(this.dir, "reflections.jsonl");
13394
- this.doneFile = join11(this.dir, "done.json");
13395
- this.verifiedFile = join11(this.dir, "verified.json");
13396
- this.rulesFile = join11(this.dir, "rules.json");
13560
+ this.dir = join12(workspace, ".flavor", "evolve");
13561
+ this.signalsFile = join12(this.dir, "signals.jsonl");
13562
+ this.reflectionsFile = join12(this.dir, "reflections.jsonl");
13563
+ this.doneFile = join12(this.dir, "done.json");
13564
+ this.verifiedFile = join12(this.dir, "verified.json");
13565
+ this.rulesFile = join12(this.dir, "rules.json");
13397
13566
  this.maxSignals = maxSignals;
13398
13567
  this.maxRules = maxRules;
13399
13568
  }
@@ -13936,7 +14105,7 @@ function createEvolveService(options) {
13936
14105
  if (suggestion === void 0) return `No open suggestion with id "${suggestionId}". Use /evolve suggest to list them.`;
13937
14106
  const name = sanitizePluginName(suggestion.tool);
13938
14107
  const dir = await scaffoldFixPlugin(workspace, name);
13939
- await writeFile7(join12(dir, "PLAN.md"), buildPlan(suggestion, name, "See the repeated failure below; describe your fix in PLAN.md."), "utf8");
14108
+ await writeFile7(join13(dir, "PLAN.md"), buildPlan(suggestion, name, "See the repeated failure below; describe your fix in PLAN.md."), "utf8");
13940
14109
  await snapshotFixPlugin(workspace, name);
13941
14110
  return [
13942
14111
  `suggestion ${suggestion.id}: ${suggestion.tool} x${suggestion.count} \u2014 ${suggestion.error}`,
@@ -14009,7 +14178,7 @@ ${result.stderr.slice(-4e3)}` : ""}`;
14009
14178
  name: "evolve_improve",
14010
14179
  description: "Implement a fix for one repeated tool failure. Default kind=plugin scaffolds the fix-<tool>/ plugin dir and writes PLAN.md with instructions for implementing, verifying, reloading, and testing it. kind=prompt_rule instead stores a concise guardrail rule (from `implementation`) that is injected into future system prompts and closes the suggestion. Use when the model proposes a concrete fix for a suggestion in the system prompt.",
14011
14180
  inputSchema,
14012
- paths: (input) => [join12(workspace, ".flavor", "evolve"), ...input.kind === "prompt_rule" ? [] : [join12(workspace, ".flavor", "plugins")]],
14181
+ paths: (input) => [join13(workspace, ".flavor", "evolve"), ...input.kind === "prompt_rule" ? [] : [join13(workspace, ".flavor", "plugins")]],
14013
14182
  execute: async (input, signal) => {
14014
14183
  signal.throwIfAborted();
14015
14184
  const { suggestionId, implementation, kind } = input;
@@ -14029,7 +14198,7 @@ ${result.stderr.slice(-4e3)}` : ""}`;
14029
14198
  }
14030
14199
  const name = sanitizePluginName(suggestion.tool);
14031
14200
  const dir = await scaffoldFixPlugin(workspace, name);
14032
- await writeFile7(join12(dir, "PLAN.md"), buildPlan(suggestion, name, implementation), "utf8");
14201
+ await writeFile7(join13(dir, "PLAN.md"), buildPlan(suggestion, name, implementation), "utf8");
14033
14202
  await snapshotFixPlugin(workspace, name);
14034
14203
  return [
14035
14204
  `Scaffolded fix plugin at ${dir} for suggestion [${suggestion.id}] (${suggestion.tool} x${suggestion.count}).`,
@@ -14052,8 +14221,8 @@ ${result.stderr.slice(-4e3)}` : ""}`;
14052
14221
  }
14053
14222
 
14054
14223
  // src/init/project.ts
14055
- import { lstat as lstat4, mkdir as mkdir9, readdir as readdir4, readFile as readFile9, realpath as realpath7, writeFile as writeFile8 } from "fs/promises";
14056
- import { basename as basename4, isAbsolute as isAbsolute8, join as join13, relative as relative8, sep as sep8 } from "path";
14224
+ import { lstat as lstat5, mkdir as mkdir9, readdir as readdir4, readFile as readFile10, realpath as realpath7, writeFile as writeFile8 } from "fs/promises";
14225
+ import { basename as basename4, isAbsolute as isAbsolute8, join as join14, relative as relative8, sep as sep8 } from "path";
14057
14226
  var GENERATED_START = "<!-- flavor-code:start -->";
14058
14227
  var GENERATED_END = "<!-- flavor-code:end -->";
14059
14228
  var MAX_MANIFEST_BYTES2 = 1e6;
@@ -14106,7 +14275,7 @@ function isContainedPath(root, candidate) {
14106
14275
  }
14107
14276
  async function pathKind(path, root) {
14108
14277
  try {
14109
- const details = await lstat4(path);
14278
+ const details = await lstat5(path);
14110
14279
  if (details.isSymbolicLink() || !isContainedPath(root, await realpath7(path))) return void 0;
14111
14280
  if (details.isFile()) return "file";
14112
14281
  if (details.isDirectory()) return "directory";
@@ -14120,14 +14289,14 @@ function isMissingPathError(error) {
14120
14289
  return error instanceof Error && "code" in error && error.code === "ENOENT";
14121
14290
  }
14122
14291
  async function readTextIfSmall(path, root) {
14123
- const details = await lstat4(path).catch((error) => {
14292
+ const details = await lstat5(path).catch((error) => {
14124
14293
  if (isMissingPathError(error)) return void 0;
14125
14294
  throw error;
14126
14295
  });
14127
14296
  if (details === void 0 || !details.isFile() || details.isSymbolicLink() || details.size > MAX_MANIFEST_BYTES2 || !isContainedPath(root, await realpath7(path))) {
14128
14297
  return void 0;
14129
14298
  }
14130
- return readFile9(path, "utf8");
14299
+ return readFile10(path, "utf8");
14131
14300
  }
14132
14301
  function isRecord2(value) {
14133
14302
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -14174,7 +14343,7 @@ async function sampleSourceExtensions(cwd, root, directories) {
14174
14343
  const extensions = /* @__PURE__ */ new Set();
14175
14344
  const sampleDirectories = ["", ...directories].slice(0, 12);
14176
14345
  for (const relativeDirectory of sampleDirectories) {
14177
- const absoluteDirectory = join13(cwd, relativeDirectory);
14346
+ const absoluteDirectory = join14(cwd, relativeDirectory);
14178
14347
  if (!isContainedPath(root, await realpath7(absoluteDirectory))) continue;
14179
14348
  let entries;
14180
14349
  try {
@@ -14195,15 +14364,15 @@ async function inspectProject(cwd) {
14195
14364
  const root = await realpath7(cwd);
14196
14365
  const sourceDirectories = [];
14197
14366
  for (const directory of SOURCE_DIRECTORIES) {
14198
- if (await pathKind(join13(cwd, directory), root) === "directory") sourceDirectories.push(directory);
14367
+ if (await pathKind(join14(cwd, directory), root) === "directory") sourceDirectories.push(directory);
14199
14368
  }
14200
14369
  const instructionFiles = [];
14201
14370
  for (const file of INSTRUCTION_FILES) {
14202
- if (await pathKind(join13(cwd, file), root) === "file") instructionFiles.push(file);
14371
+ if (await pathKind(join14(cwd, file), root) === "file") instructionFiles.push(file);
14203
14372
  }
14204
14373
  const configFiles = [];
14205
14374
  for (const file of CONFIG_FILES) {
14206
- if (await pathKind(join13(cwd, file), root) === "file") configFiles.push(file);
14375
+ if (await pathKind(join14(cwd, file), root) === "file") configFiles.push(file);
14207
14376
  }
14208
14377
  const manifestNames = [
14209
14378
  "package.json",
@@ -14216,7 +14385,7 @@ async function inspectProject(cwd) {
14216
14385
  ];
14217
14386
  const present = /* @__PURE__ */ new Set();
14218
14387
  for (const manifest of manifestNames) {
14219
- if (await pathKind(join13(cwd, manifest), root) === "file") present.add(manifest);
14388
+ if (await pathKind(join14(cwd, manifest), root) === "file") present.add(manifest);
14220
14389
  }
14221
14390
  const detectedLockfiles = JAVASCRIPT_LOCKFILES.filter(({ name }) => present.has(name));
14222
14391
  const detectedJsManagers = [
@@ -14242,7 +14411,7 @@ async function inspectProject(cwd) {
14242
14411
  let projectName = sanitizeProjectName(basename4(cwd)) ?? "project";
14243
14412
  let declaredJsManager;
14244
14413
  if (present.has("package.json")) {
14245
- const text = await readTextIfSmall(join13(cwd, "package.json"), root);
14414
+ const text = await readTextIfSmall(join14(cwd, "package.json"), root);
14246
14415
  if (text === void 0) {
14247
14416
  cautions.push("package.json was too large to inspect; its scripts were ignored.");
14248
14417
  } else {
@@ -14296,8 +14465,8 @@ async function inspectProject(cwd) {
14296
14465
  addScriptCommand(testCommands, scripts, jsManager, ["test"]);
14297
14466
  addScriptCommand(lintCommands, scripts, jsManager, ["lint"]);
14298
14467
  addScriptCommand(formatCommands, scripts, jsManager, ["format", "format:check"]);
14299
- const pyproject = present.has("pyproject.toml") ? await readTextIfSmall(join13(cwd, "pyproject.toml"), root) : void 0;
14300
- const requirements = present.has("requirements.txt") ? await readTextIfSmall(join13(cwd, "requirements.txt"), root) : void 0;
14468
+ const pyproject = present.has("pyproject.toml") ? await readTextIfSmall(join14(cwd, "pyproject.toml"), root) : void 0;
14469
+ const requirements = present.has("requirements.txt") ? await readTextIfSmall(join14(cwd, "requirements.txt"), root) : void 0;
14301
14470
  const pythonTools = detectPythonTools(pyproject, requirements, configFiles);
14302
14471
  const pythonManager = pythonManagers[0];
14303
14472
  const pythonPrefix = pythonManager === "uv" ? "uv run " : pythonManager === "poetry" ? "poetry run " : pythonManager === "pipenv" ? "pipenv run " : "";
@@ -14315,8 +14484,8 @@ async function inspectProject(cwd) {
14315
14484
  if (present.has("pyproject.toml") || present.has("requirements.txt") || extensions.has("py")) {
14316
14485
  languages.add("Python");
14317
14486
  }
14318
- const gitignorePath = await pathKind(join13(cwd, ".gitignore"), root) === "file" ? ".gitignore" : void 0;
14319
- const hasCodeGraph = await pathKind(join13(cwd, ".flavor", "astgraph", "index.db"), root) === "file";
14487
+ const gitignorePath = await pathKind(join14(cwd, ".gitignore"), root) === "file" ? ".gitignore" : void 0;
14488
+ const hasCodeGraph = await pathKind(join14(cwd, ".flavor", "astgraph", "index.db"), root) === "file";
14320
14489
  return {
14321
14490
  cwd,
14322
14491
  projectName,
@@ -14425,12 +14594,12 @@ function isEntryIgnored(lines, entry2) {
14425
14594
  });
14426
14595
  }
14427
14596
  async function addSessionsToGitignore(cwd) {
14428
- const path = join13(cwd, ".gitignore");
14597
+ const path = join14(cwd, ".gitignore");
14429
14598
  const root = await realpath7(cwd);
14430
14599
  await assertSafeManagedPath(path, root);
14431
14600
  let existing;
14432
14601
  try {
14433
- existing = await readFile9(path, "utf8");
14602
+ existing = await readFile10(path, "utf8");
14434
14603
  } catch (error) {
14435
14604
  if (!isMissingPathError(error)) throw error;
14436
14605
  existing = "";
@@ -14445,7 +14614,7 @@ async function addSessionsToGitignore(cwd) {
14445
14614
  }
14446
14615
  async function assertSafeManagedPath(path, root) {
14447
14616
  try {
14448
- const details = await lstat4(path);
14617
+ const details = await lstat5(path);
14449
14618
  if (details.isSymbolicLink()) {
14450
14619
  throw new Error(`Refusing to overwrite managed file symbolic link: ${path}`);
14451
14620
  }
@@ -14459,9 +14628,9 @@ async function assertSafeManagedPath(path, root) {
14459
14628
  }
14460
14629
  async function ensureFlavorDirectories(cwd) {
14461
14630
  const root = await realpath7(cwd);
14462
- const sessionsDir = join13(cwd, ".flavor", "sessions");
14463
- const skillsDir = join13(cwd, ".flavor", "skills");
14464
- const pluginsDir = join13(cwd, ".flavor", "plugins");
14631
+ const sessionsDir = join14(cwd, ".flavor", "sessions");
14632
+ const skillsDir = join14(cwd, ".flavor", "skills");
14633
+ const pluginsDir = join14(cwd, ".flavor", "plugins");
14465
14634
  await mkdir9(sessionsDir, { recursive: true, mode: 448 });
14466
14635
  await mkdir9(skillsDir, { recursive: true, mode: 448 });
14467
14636
  await mkdir9(pluginsDir, { recursive: true, mode: 448 });
@@ -14474,11 +14643,11 @@ async function ensureFlavorDirectories(cwd) {
14474
14643
  }
14475
14644
  }
14476
14645
  async function createExampleFlavorConfig(cwd) {
14477
- const configPath = join13(cwd, ".flavor", "flavor.json");
14646
+ const configPath = join14(cwd, ".flavor", "flavor.json");
14478
14647
  const root = await realpath7(cwd);
14479
14648
  await assertSafeManagedPath(configPath, root);
14480
14649
  try {
14481
- await lstat4(configPath);
14650
+ await lstat5(configPath);
14482
14651
  return;
14483
14652
  } catch (error) {
14484
14653
  if (!isMissingPathError(error)) throw error;
@@ -14509,12 +14678,12 @@ async function initializeFlavor(cwd) {
14509
14678
  await ensureFlavorDirectories(cwd);
14510
14679
  await createExampleFlavorConfig(cwd);
14511
14680
  const facts = await inspectProject(cwd);
14512
- const path = join13(cwd, "FLAVOR.md");
14681
+ const path = join14(cwd, "FLAVOR.md");
14513
14682
  const root = await realpath7(cwd);
14514
14683
  await assertSafeManagedPath(path, root);
14515
14684
  let existing;
14516
14685
  try {
14517
- existing = await readFile9(path, "utf8");
14686
+ existing = await readFile10(path, "utf8");
14518
14687
  } catch (error) {
14519
14688
  if (!isMissingPathError(error)) throw error;
14520
14689
  }
@@ -14527,7 +14696,7 @@ async function initializeFlavor(cwd) {
14527
14696
  }
14528
14697
 
14529
14698
  // src/utils/memory-restart.ts
14530
- import { join as join14 } from "path";
14699
+ import { join as join15 } from "path";
14531
14700
  var MEMORY_RESTART_EXIT_CODE = 75;
14532
14701
  var MEMORY_RESTART_MARKER_FILE = "memory-restart.json";
14533
14702
  var MEMORY_RESTART_WINDOW_MS = 60 * 60 * 1e3;
@@ -14544,7 +14713,7 @@ function clearMemoryRotation() {
14544
14713
  rotationActive = false;
14545
14714
  }
14546
14715
  function memoryRestartMarkerPath(workspace) {
14547
- return join14(workspace, ".flavor", "tmp", MEMORY_RESTART_MARKER_FILE);
14716
+ return join15(workspace, ".flavor", "tmp", MEMORY_RESTART_MARKER_FILE);
14548
14717
  }
14549
14718
  function nextMemoryRestartMarker(previous, sessionId, now, continuation) {
14550
14719
  const previousWindowStart = previous?.windowStartedAt ?? previous?.requestedAt;
@@ -15078,7 +15247,7 @@ ${cycle.verification.summary}` !== signature) break;
15078
15247
  // src/goal/orchestrator.ts
15079
15248
  import { createHash as createHash11 } from "crypto";
15080
15249
  import { writeFile as writeFile9, mkdir as mkdir10 } from "fs/promises";
15081
- import { join as join16 } from "path";
15250
+ import { join as join17 } from "path";
15082
15251
 
15083
15252
  // src/goal/types.ts
15084
15253
  import { z as z23 } from "zod";
@@ -15249,8 +15418,8 @@ function extractJson(text) {
15249
15418
 
15250
15419
  // src/goal/classifier.ts
15251
15420
  import { createHash as createHash10 } from "crypto";
15252
- import { readFile as readFile10 } from "fs/promises";
15253
- import { join as join15 } from "path";
15421
+ import { readFile as readFile11 } from "fs/promises";
15422
+ import { join as join16 } from "path";
15254
15423
  function buildSkepticPrompt(evidence, plan, index) {
15255
15424
  const criteriaList = plan.criteria.map((c) => ` ${c.id}. [${c.type}] ${c.description}`).join("\n");
15256
15425
  return [
@@ -15429,10 +15598,10 @@ function computeFingerprint(gaps) {
15429
15598
  }
15430
15599
  async function collectEvidence(workspace, objective, finalResponse, priorGaps, contractHash2 = createHash10("sha256").update(objective).digest("hex"), hostVerification) {
15431
15600
  const workspaceState = await collectWorkspaceState(workspace);
15432
- const planPath2 = join15(workspace, ".flavor", "goal-plan.md");
15601
+ const planPath2 = join16(workspace, ".flavor", "goal-plan.md");
15433
15602
  let planFile = null;
15434
15603
  try {
15435
- planFile = await readFile10(planPath2, "utf8");
15604
+ planFile = await readFile11(planPath2, "utf8");
15436
15605
  } catch {
15437
15606
  }
15438
15607
  return {
@@ -15801,8 +15970,8 @@ function waitForStageRetry(delayMs, signal) {
15801
15970
  });
15802
15971
  }
15803
15972
  async function writePlanFile(workspace, plan) {
15804
- const dir = join16(workspace, ".flavor");
15805
- const planPath2 = join16(dir, "goal-plan.md");
15973
+ const dir = join17(workspace, ".flavor");
15974
+ const planPath2 = join17(dir, "goal-plan.md");
15806
15975
  await mkdir10(dir, { recursive: true });
15807
15976
  const criteriaText = plan.criteria.map((c) => `- [${c.type}] AC-${c.id}: ${c.description}`).join("\n");
15808
15977
  const content = [
@@ -15879,8 +16048,8 @@ function formatPriorGaps(gaps) {
15879
16048
  // src/goal/store.ts
15880
16049
  import { constants as constants4 } from "fs";
15881
16050
  import { randomUUID as randomUUID11 } from "crypto";
15882
- import { lstat as lstat5, mkdir as mkdir11, open as open5, readFile as readFile11, rename as rename4, rm as rm6, stat as stat6 } from "fs/promises";
15883
- import { dirname as dirname9, join as join17, relative as relative9, resolve as resolve12, sep as sep9 } from "path";
16051
+ import { lstat as lstat6, mkdir as mkdir11, open as open5, readFile as readFile12, rename as rename4, rm as rm6, stat as stat6 } from "fs/promises";
16052
+ import { dirname as dirname9, join as join18, relative as relative9, resolve as resolve12, sep as sep9 } from "path";
15884
16053
  var DEFAULT_MAX_BYTES = 1024 * 1024;
15885
16054
  var GoalStore = class {
15886
16055
  #workspace;
@@ -15888,7 +16057,7 @@ var GoalStore = class {
15888
16057
  #maxBytes;
15889
16058
  constructor(options) {
15890
16059
  this.#workspace = resolve12(options.workspace);
15891
- this.#root = join17(this.#workspace, ".flavor", "goals");
16060
+ this.#root = join18(this.#workspace, ".flavor", "goals");
15892
16061
  this.#maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;
15893
16062
  }
15894
16063
  async save(input) {
@@ -15896,13 +16065,13 @@ var GoalStore = class {
15896
16065
  await assertNoSymlink(this.#workspace, dirname9(this.#root));
15897
16066
  await mkdir11(this.#root, { recursive: true, mode: 448 });
15898
16067
  await assertNoSymlink(this.#workspace, this.#root);
15899
- const target = join17(this.#root, `${state.id}.json`);
16068
+ const target = join18(this.#root, `${state.id}.json`);
15900
16069
  const body = `${JSON.stringify(state, null, 2)}
15901
16070
  `;
15902
16071
  if (Buffer.byteLength(body) > this.#maxBytes) {
15903
16072
  throw new Error(`Goal state exceeds maximum size of ${this.#maxBytes} bytes`);
15904
16073
  }
15905
- const temporary = join17(this.#root, `.${state.id}.${process.pid}.${randomUUID11()}.tmp`);
16074
+ const temporary = join18(this.#root, `.${state.id}.${process.pid}.${randomUUID11()}.tmp`);
15906
16075
  let handle;
15907
16076
  try {
15908
16077
  handle = await open5(temporary, constants4.O_CREAT | constants4.O_EXCL | constants4.O_WRONLY, 384);
@@ -15923,13 +16092,13 @@ var GoalStore = class {
15923
16092
  }
15924
16093
  await assertNoSymlink(this.#workspace, dirname9(this.#root));
15925
16094
  await assertNoSymlink(this.#workspace, this.#root);
15926
- const target = join17(this.#root, `${goalId}.json`);
16095
+ const target = join18(this.#root, `${goalId}.json`);
15927
16096
  try {
15928
16097
  const metadata = await stat6(target);
15929
16098
  if (!metadata.isFile() || metadata.size > this.#maxBytes) {
15930
16099
  throw new Error("Goal state is not a bounded regular file");
15931
16100
  }
15932
- const raw = await readFile11(target, "utf8");
16101
+ const raw = await readFile12(target, "utf8");
15933
16102
  return GoalStateSchema.parse(JSON.parse(raw));
15934
16103
  } catch (error) {
15935
16104
  if (isCode4(error, "ENOENT")) throw new Error(`Goal "${goalId}" was not found`);
@@ -15953,7 +16122,7 @@ async function assertNoSymlink(root, target) {
15953
16122
  }
15954
16123
  for (const path of chain.reverse()) {
15955
16124
  try {
15956
- if ((await lstat5(path)).isSymbolicLink()) throw new Error(`Goal path contains a symbolic link: ${path}`);
16125
+ if ((await lstat6(path)).isSymbolicLink()) throw new Error(`Goal path contains a symbolic link: ${path}`);
15957
16126
  } catch (error) {
15958
16127
  if (!isCode4(error, "ENOENT")) throw error;
15959
16128
  }
@@ -15964,8 +16133,8 @@ function isCode4(error, code) {
15964
16133
  }
15965
16134
 
15966
16135
  // src/loop/isolation.ts
15967
- import { appendFile as appendFile2, mkdir as mkdir12, readFile as readFile12 } from "fs/promises";
15968
- import { join as join18, resolve as resolve13 } from "path";
16136
+ import { appendFile as appendFile2, mkdir as mkdir12, readFile as readFile13 } from "fs/promises";
16137
+ import { join as join19, resolve as resolve13 } from "path";
15969
16138
  var CODE_CHANGE = /\b(?:fix|implement|add|update|change|modify|refactor|build|create|remove|delete|write|migrate)\b|修复|实现|添加|更新|修改|重构|创建|删除|迁移/i;
15970
16139
  var READ_ONLY = /\b(?:review|analy[sz]e|inspect|explain|research|investigate|audit|summari[sz]e|report)\b|调研|分析|审查|检查|解释|研究|总结|报告/i;
15971
16140
  function inferGoalIntent(goal) {
@@ -15988,8 +16157,8 @@ async function prepareLoopWorkspace(options) {
15988
16157
  return { kind: "needs_human", reason: "The workspace has uncommitted changes that a new worktree would omit." };
15989
16158
  }
15990
16159
  options.signal?.throwIfAborted();
15991
- const worktrees = join18(root, ".worktrees");
15992
- const path = join18(worktrees, `loop-${options.loopId}`);
16160
+ const worktrees = join19(root, ".worktrees");
16161
+ const path = join19(worktrees, `loop-${options.loopId}`);
15993
16162
  const branch = `loop/${options.loopId}`;
15994
16163
  try {
15995
16164
  await ensureWorktreeDirectoryIgnored(root);
@@ -16019,12 +16188,12 @@ async function ensureWorktreeDirectoryIgnored(root) {
16019
16188
  const commonDirectory = await git(root, ["rev-parse", "--git-common-dir"]);
16020
16189
  if (commonDirectory.code !== 0) throw new Error(commonDirectory.stderr.trim() || "Git common directory is unavailable");
16021
16190
  const gitDirectory = resolve13(root, commonDirectory.stdout.trim());
16022
- const infoDirectory = join18(gitDirectory, "info");
16023
- const excludePath = join18(infoDirectory, "exclude");
16191
+ const infoDirectory = join19(gitDirectory, "info");
16192
+ const excludePath = join19(infoDirectory, "exclude");
16024
16193
  await mkdir12(infoDirectory, { recursive: true });
16025
16194
  let existing = "";
16026
16195
  try {
16027
- existing = await readFile12(excludePath, "utf8");
16196
+ existing = await readFile13(excludePath, "utf8");
16028
16197
  } catch (error) {
16029
16198
  if (!(typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT")) throw error;
16030
16199
  }
@@ -16035,8 +16204,8 @@ async function ensureWorktreeDirectoryIgnored(root) {
16035
16204
 
16036
16205
  // src/loop/store.ts
16037
16206
  import { constants as constants5 } from "fs";
16038
- import { lstat as lstat6, mkdir as mkdir13, open as open6, readFile as readFile13, realpath as realpath8, rename as rename5, rm as rm7, stat as stat7 } from "fs/promises";
16039
- import { dirname as dirname10, join as join19, relative as relative10, resolve as resolve14, sep as sep10 } from "path";
16207
+ import { lstat as lstat7, mkdir as mkdir13, open as open6, readFile as readFile14, realpath as realpath8, rename as rename5, rm as rm7, stat as stat7 } from "fs/promises";
16208
+ import { dirname as dirname10, join as join20, relative as relative10, resolve as resolve14, sep as sep10 } from "path";
16040
16209
  import { randomUUID as randomUUID12 } from "crypto";
16041
16210
  var LOOP_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
16042
16211
  var DEFAULT_MAX_BYTES2 = 5 * 1024 * 1024;
@@ -16047,7 +16216,7 @@ var LoopStore = class {
16047
16216
  #appendTails = /* @__PURE__ */ new Map();
16048
16217
  constructor(options) {
16049
16218
  this.#workspace = resolve14(options.workspace);
16050
- this.#root = join19(this.#workspace, ".flavor", "loops");
16219
+ this.#root = join20(this.#workspace, ".flavor", "loops");
16051
16220
  this.#maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES2;
16052
16221
  }
16053
16222
  async create(state) {
@@ -16064,11 +16233,11 @@ var LoopStore = class {
16064
16233
  const state = LoopStateSchema.parse(input);
16065
16234
  await this.#assertWorkspace(state.workspace);
16066
16235
  const directory = await this.#prepare(state.loopId);
16067
- const target = join19(directory, "state.json");
16236
+ const target = join20(directory, "state.json");
16068
16237
  const body = `${JSON.stringify(state, null, 2)}
16069
16238
  `;
16070
16239
  if (Buffer.byteLength(body) > this.#maxBytes) throw new Error(`Loop state exceeds maximum size of ${this.#maxBytes} bytes`);
16071
- const temporary = join19(directory, `.state.${process.pid}.${randomUUID12()}.tmp`);
16240
+ const temporary = join20(directory, `.state.${process.pid}.${randomUUID12()}.tmp`);
16072
16241
  let handle;
16073
16242
  try {
16074
16243
  handle = await open6(temporary, constants5.O_CREAT | constants5.O_EXCL | constants5.O_WRONLY, 384);
@@ -16084,11 +16253,11 @@ var LoopStore = class {
16084
16253
  }
16085
16254
  async load(loopId) {
16086
16255
  const directory = await this.#safeExisting(loopId);
16087
- const target = join19(directory, "state.json");
16256
+ const target = join20(directory, "state.json");
16088
16257
  try {
16089
16258
  const metadata = await stat7(target);
16090
16259
  if (!metadata.isFile() || metadata.size > this.#maxBytes) throw new Error("Loop state is not a bounded regular file");
16091
- const raw = await readFile13(target, "utf8");
16260
+ const raw = await readFile14(target, "utf8");
16092
16261
  const state = LoopStateSchema.parse(JSON.parse(raw));
16093
16262
  await this.#assertWorkspace(state.workspace);
16094
16263
  return state;
@@ -16104,7 +16273,7 @@ var LoopStore = class {
16104
16273
  const previous = this.#appendTails.get(event.loopId) ?? Promise.resolve();
16105
16274
  const next = previous.catch(() => void 0).then(async () => {
16106
16275
  const directory = await this.#prepare(event.loopId);
16107
- const path = join19(directory, "events.jsonl");
16276
+ const path = join20(directory, "events.jsonl");
16108
16277
  const handle = await open6(path, constants5.O_CREAT | constants5.O_APPEND | constants5.O_WRONLY, 384);
16109
16278
  try {
16110
16279
  await handle.writeFile(`${JSON.stringify(event)}
@@ -16121,7 +16290,7 @@ var LoopStore = class {
16121
16290
  await assertNoSymlink2(this.#workspace, dirname10(this.#root));
16122
16291
  await mkdir13(this.#root, { recursive: true, mode: 448 });
16123
16292
  await assertNoSymlink2(this.#workspace, this.#root);
16124
- const directory = join19(this.#root, loopId);
16293
+ const directory = join20(this.#root, loopId);
16125
16294
  await mkdir13(directory, { recursive: true, mode: 448 });
16126
16295
  await assertNoSymlink2(this.#workspace, directory);
16127
16296
  return directory;
@@ -16129,7 +16298,7 @@ var LoopStore = class {
16129
16298
  async #safeExisting(loopId) {
16130
16299
  validateId(loopId);
16131
16300
  await assertNoSymlink2(this.#workspace, this.#root);
16132
- const directory = join19(this.#root, loopId);
16301
+ const directory = join20(this.#root, loopId);
16133
16302
  await assertNoSymlink2(this.#workspace, directory);
16134
16303
  return directory;
16135
16304
  }
@@ -16155,7 +16324,7 @@ async function assertNoSymlink2(root, target) {
16155
16324
  }
16156
16325
  for (const path of chain.reverse()) {
16157
16326
  try {
16158
- if ((await lstat6(path)).isSymbolicLink()) throw new Error(`Loop path contains a symbolic link: ${path}`);
16327
+ if ((await lstat7(path)).isSymbolicLink()) throw new Error(`Loop path contains a symbolic link: ${path}`);
16159
16328
  } catch (error) {
16160
16329
  if (!isCode5(error, "ENOENT")) throw error;
16161
16330
  }
@@ -16177,13 +16346,13 @@ function isCode5(error, code) {
16177
16346
  }
16178
16347
 
16179
16348
  // src/loop/verifier.ts
16180
- import { readFile as readFile14 } from "fs/promises";
16181
- import { join as join21 } from "path";
16349
+ import { readFile as readFile15 } from "fs/promises";
16350
+ import { join as join22 } from "path";
16182
16351
 
16183
16352
  // src/tools/shell.ts
16184
16353
  import { spawn as spawn4 } from "child_process";
16185
16354
  import { existsSync as existsSync4 } from "fs";
16186
- import { basename as basename5, delimiter as delimiter2, isAbsolute as isAbsolute9, join as join20, relative as relative11, resolve as resolve15, sep as sep11 } from "path";
16355
+ import { basename as basename5, delimiter as delimiter2, isAbsolute as isAbsolute9, join as join21, relative as relative11, resolve as resolve15, sep as sep11 } from "path";
16187
16356
  import { z as z25 } from "zod";
16188
16357
 
16189
16358
  // src/tools/jobs.ts
@@ -16846,7 +17015,7 @@ function decodeOutput(buffer) {
16846
17015
  return new TextDecoder(encoding).decode(buffer);
16847
17016
  }
16848
17017
  function shellEnvironment(root, environment) {
16849
- const localBin = join20(root, "node_modules", ".bin");
17018
+ const localBin = join21(root, "node_modules", ".bin");
16850
17019
  if (!existsSync4(localBin)) return environment;
16851
17020
  const pathKey = Object.keys(environment).find((key) => key.toLowerCase() === "path") ?? "PATH";
16852
17021
  const current = environment[pathKey] ?? "";
@@ -16950,13 +17119,13 @@ var MAX_PERSISTED_VERIFICATION_STREAM_BYTES = 1024;
16950
17119
  async function inferVerificationPlan(workspace) {
16951
17120
  const commands = [];
16952
17121
  const seen = /* @__PURE__ */ new Set();
16953
- const packageJson = await optionalJson(join21(workspace, "package.json"));
17122
+ const packageJson = await optionalJson(join22(workspace, "package.json"));
16954
17123
  const scripts = isRecord3(packageJson?.scripts) ? packageJson.scripts : {};
16955
17124
  for (const name of SCRIPT_PRIORITY) {
16956
17125
  if (typeof scripts[name] !== "string" || isUnconditionalPassThrough(scripts[name])) continue;
16957
17126
  add(commands, seen, npmScript(name));
16958
17127
  }
16959
- const flavor = await optionalText(join21(workspace, "FLAVOR.md"));
17128
+ const flavor = await optionalText(join22(workspace, "FLAVOR.md"));
16960
17129
  if (flavor !== void 0) {
16961
17130
  for (const match of flavor.matchAll(/^- `([^`]+)`\s*$/gm)) {
16962
17131
  const parsed = parseTrustedCommand(match[1] ?? "");
@@ -17031,7 +17200,7 @@ function isUnconditionalPassThrough(script) {
17031
17200
  }
17032
17201
  async function optionalText(path) {
17033
17202
  try {
17034
- return await readFile14(path, "utf8");
17203
+ return await readFile15(path, "utf8");
17035
17204
  } catch (error) {
17036
17205
  if (isCode6(error, "ENOENT")) return void 0;
17037
17206
  throw error;
@@ -17052,7 +17221,7 @@ function isCode6(error, code) {
17052
17221
 
17053
17222
  // src/models/anthropic.ts
17054
17223
  import Anthropic from "@anthropic-ai/sdk";
17055
- import { readFile as readFile15 } from "fs/promises";
17224
+ import { readFile as readFile16 } from "fs/promises";
17056
17225
 
17057
17226
  // src/utils/json.ts
17058
17227
  function safeParseJson(input) {
@@ -17101,11 +17270,11 @@ function isEnvTruthy(value) {
17101
17270
 
17102
17271
  // src/utils/log.ts
17103
17272
  import { appendFile as appendFile3, mkdir as mkdir14 } from "fs/promises";
17104
- import { dirname as dirname11, join as join22, resolve as resolve16 } from "path";
17273
+ import { dirname as dirname11, join as join23, resolve as resolve16 } from "path";
17105
17274
  var pending = Promise.resolve();
17106
17275
  var usagePending = Promise.resolve();
17107
17276
  function usageLogPath() {
17108
- return process.env.FLAVOR_USAGE_FILE ?? join22(process.cwd(), ".flavor", "usage.jsonl");
17277
+ return process.env.FLAVOR_USAGE_FILE ?? join23(process.cwd(), ".flavor", "usage.jsonl");
17109
17278
  }
17110
17279
  var usageSessionId = "unknown";
17111
17280
  var truncateOnNextAppend = false;
@@ -17134,7 +17303,7 @@ var AuditLogger = class {
17134
17303
  #path;
17135
17304
  #closed = false;
17136
17305
  constructor(workspace) {
17137
- this.#path = join22(resolve16(workspace), ".flavor", "audit.jsonl");
17306
+ this.#path = join23(resolve16(workspace), ".flavor", "audit.jsonl");
17138
17307
  }
17139
17308
  get path() {
17140
17309
  return this.#path;
@@ -17391,7 +17560,7 @@ ${modelContentText(message2.content)}
17391
17560
  source: {
17392
17561
  type: "base64",
17393
17562
  media_type: block.mediaType,
17394
- data: (await readFile15(block.source.path)).toString("base64")
17563
+ data: (await readFile16(block.source.path)).toString("base64")
17395
17564
  }
17396
17565
  }));
17397
17566
  if (message2.cacheBreakpoint && Array.isArray(content) && content.length > 0) {
@@ -17613,7 +17782,7 @@ function resolveCacheProfile(input) {
17613
17782
  // src/models/openai.ts
17614
17783
  import OpenAI from "openai";
17615
17784
  import { createHash as createHash12 } from "crypto";
17616
- import { readFile as readFile16 } from "fs/promises";
17785
+ import { readFile as readFile17 } from "fs/promises";
17617
17786
  var DEFAULT_THINKING_EFFORT = "high";
17618
17787
  var EXPLICIT_CACHE_BREAKPOINT = { mode: "explicit" };
17619
17788
  var MAX_EXPLICIT_CACHE_BREAKPOINTS = 3;
@@ -17636,7 +17805,7 @@ async function toInput(message2, cacheBreakpoint = false) {
17636
17805
  prompt_cache_breakpoint: EXPLICIT_CACHE_BREAKPOINT
17637
17806
  }] : message2.content : await Promise.all(message2.content.map(async (block) => block.type === "text" ? { type: "input_text", text: block.text } : {
17638
17807
  type: "input_image",
17639
- image_url: `data:${block.mediaType};base64,${(await readFile16(block.source.path)).toString("base64")}`,
17808
+ image_url: `data:${block.mediaType};base64,${(await readFile17(block.source.path)).toString("base64")}`,
17640
17809
  detail: "auto"
17641
17810
  }));
17642
17811
  if (cacheBreakpoint && Array.isArray(content) && content.length > 0) {
@@ -17880,16 +18049,28 @@ function selectExplicitBreakpointIndexes(messages) {
17880
18049
  const indexes = messages.map((message2, index) => message2.cacheBreakpoint ? index : -1).filter((index) => index >= 0);
17881
18050
  const tail = messages.length - 1;
17882
18051
  if (tail >= 0 && indexes.at(-1) !== tail) indexes.push(tail);
17883
- return new Set(indexes.slice(-MAX_EXPLICIT_CACHE_BREAKPOINTS));
18052
+ const firstNonSystem = messages.findIndex((message2) => message2.role !== "system");
18053
+ const leadingSystemEnd = firstNonSystem < 0 ? tail : firstNonSystem - 1;
18054
+ const stable = indexes.filter((index) => index <= leadingSystemEnd).at(-1);
18055
+ if (stable === void 0) return new Set(indexes.slice(-MAX_EXPLICIT_CACHE_BREAKPOINTS));
18056
+ return /* @__PURE__ */ new Set([
18057
+ stable,
18058
+ ...indexes.filter((index) => index !== stable).slice(-(MAX_EXPLICIT_CACHE_BREAKPOINTS - 1))
18059
+ ]);
17884
18060
  }
17885
18061
  function promptCacheKey(request, sortedTools) {
17886
- let stableEnd = -1;
17887
- request.messages.forEach((message2, index) => {
17888
- if (message2.cacheBreakpoint) stableEnd = index;
17889
- });
17890
18062
  const firstNonSystem = request.messages.findIndex((message2) => message2.role !== "system");
17891
18063
  const leadingSystemEnd = firstNonSystem < 0 ? request.messages.length - 1 : firstNonSystem - 1;
17892
- stableEnd = Math.max(stableEnd, leadingSystemEnd);
18064
+ let stableEnd = -1;
18065
+ for (let index = 0; index <= leadingSystemEnd; index += 1) {
18066
+ if (request.messages[index]?.cacheBreakpoint) stableEnd = index;
18067
+ }
18068
+ if (stableEnd < 0) {
18069
+ request.messages.forEach((message2, index) => {
18070
+ if (message2.cacheBreakpoint) stableEnd = index;
18071
+ });
18072
+ stableEnd = Math.max(stableEnd, leadingSystemEnd);
18073
+ }
17893
18074
  const stableMessages = stableEnd < 0 ? [] : request.messages.slice(0, stableEnd + 1);
17894
18075
  const digest = createHash12("sha256").update(JSON.stringify({ model: request.model, messages: stableMessages, tools: sortedTools })).digest("hex").slice(0, 56);
17895
18076
  return `flavor-${digest}`;
@@ -18130,7 +18311,7 @@ import {
18130
18311
 
18131
18312
  // src/utils/version.ts
18132
18313
  import { readFileSync as readFileSync2 } from "fs";
18133
- import { dirname as dirname12, join as join23 } from "path";
18314
+ import { dirname as dirname12, join as join24 } from "path";
18134
18315
  import { fileURLToPath } from "url";
18135
18316
  var cached;
18136
18317
  function packageVersion() {
@@ -18138,7 +18319,7 @@ function packageVersion() {
18138
18319
  let directory = dirname12(fileURLToPath(import.meta.url));
18139
18320
  for (; ; ) {
18140
18321
  try {
18141
- const manifest = JSON.parse(readFileSync2(join23(directory, "package.json"), "utf8"));
18322
+ const manifest = JSON.parse(readFileSync2(join24(directory, "package.json"), "utf8"));
18142
18323
  if (typeof manifest.version === "string" && manifest.version.length > 0) {
18143
18324
  cached = manifest.version;
18144
18325
  return cached;
@@ -18565,8 +18746,8 @@ function isStoredToken(value) {
18565
18746
  }
18566
18747
 
18567
18748
  // src/permissions/policy.ts
18568
- import { readFile as readFile17 } from "fs/promises";
18569
- import { join as join24, resolve as resolve18 } from "path";
18749
+ import { readFile as readFile18 } from "fs/promises";
18750
+ import { join as join25, resolve as resolve18 } from "path";
18570
18751
  import { z as z27 } from "zod";
18571
18752
  var DecisionSchema2 = z27.enum(["allow", "ask", "deny"]);
18572
18753
  var PrefixSchema = z27.array(z27.string().min(1).max(1024)).min(1).max(64);
@@ -18603,15 +18784,15 @@ var CompiledPermissionPolicy = class {
18603
18784
  async function loadPermissionPolicy(options) {
18604
18785
  const sources = [
18605
18786
  ...options.managedPath === void 0 ? [] : [{ tier: "managed", path: resolve18(options.managedPath) }],
18606
- { tier: "user", path: join24(resolve18(options.home), ".flavor-code", "permissions.json") },
18607
- { tier: "project", path: join24(resolve18(options.workspace), ".flavor", "permissions.json") },
18608
- { tier: "local", path: join24(resolve18(options.workspace), ".flavor", "permissions.local.json") }
18787
+ { tier: "user", path: join25(resolve18(options.home), ".flavor-code", "permissions.json") },
18788
+ { tier: "project", path: join25(resolve18(options.workspace), ".flavor", "permissions.json") },
18789
+ { tier: "local", path: join25(resolve18(options.workspace), ".flavor", "permissions.local.json") }
18609
18790
  ];
18610
18791
  const rules = [];
18611
18792
  for (const source of sources) {
18612
18793
  let raw;
18613
18794
  try {
18614
- raw = await readFile17(source.path, "utf8");
18795
+ raw = await readFile18(source.path, "utf8");
18615
18796
  } catch (error) {
18616
18797
  if (isCode7(error, "ENOENT")) continue;
18617
18798
  throw error;
@@ -18826,7 +19007,7 @@ function data(value) {
18826
19007
 
18827
19008
  // src/skills/registry.ts
18828
19009
  import { constants as constants6 } from "fs";
18829
- import { lstat as lstat7, open as defaultOpen, readdir as readdir5, realpath as realpath9 } from "fs/promises";
19010
+ import { lstat as lstat8, open as defaultOpen, readdir as readdir5, realpath as realpath9 } from "fs/promises";
18830
19011
  import { isAbsolute as isAbsolute10, relative as relative12, resolve as resolve19, sep as sep12 } from "path";
18831
19012
  import { marked } from "marked";
18832
19013
  import { parseDocument } from "yaml";
@@ -18991,12 +19172,12 @@ var SkillRegistry = class {
18991
19172
  let entries;
18992
19173
  let physicalRoot = root;
18993
19174
  try {
18994
- const rootInfo = await lstat7(root);
19175
+ const rootInfo = await lstat8(root);
18995
19176
  if (rootInfo.isSymbolicLink() || !rootInfo.isDirectory()) throw new Error("Skill root must be a real directory");
18996
19177
  physicalRoot = await realpath9(root);
18997
19178
  entries = await readdir5(root, { withFileTypes: true });
18998
19179
  } catch (error) {
18999
- if (isMissing(error)) continue;
19180
+ if (isMissing2(error)) continue;
19000
19181
  this.#diagnose(root, error);
19001
19182
  continue;
19002
19183
  }
@@ -19145,7 +19326,7 @@ async function openFile(path, flags) {
19145
19326
  return defaultOpen(path, flags);
19146
19327
  }
19147
19328
  async function openVerifiedFile(path, root, label, opener, expected) {
19148
- const initial = await lstat7(path, { bigint: true });
19329
+ const initial = await lstat8(path, { bigint: true });
19149
19330
  if (initial.isSymbolicLink()) throw new Error(`${label} symlinks are not allowed`);
19150
19331
  assertRegular(initial, label);
19151
19332
  const canonical3 = await realpath9(path);
@@ -19212,7 +19393,7 @@ function isWithin4(root, candidate) {
19212
19393
  const delta = relative12(root, candidate);
19213
19394
  return delta === "" || delta !== ".." && !delta.startsWith(`..${sep12}`) && !isAbsolute10(delta);
19214
19395
  }
19215
- function isMissing(error) {
19396
+ function isMissing2(error) {
19216
19397
  return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
19217
19398
  }
19218
19399
  function normalizeReference(reference) {
@@ -19377,8 +19558,8 @@ function createSkillResourceTool(registry) {
19377
19558
 
19378
19559
  // src/session/store.ts
19379
19560
  import { constants as constants7 } from "fs";
19380
- import { lstat as lstat8, mkdir as mkdir15, open as open7, readFile as readFile18, readdir as readdir6, realpath as realpath10, rename as rename6, rm as rm8, stat as stat8 } from "fs/promises";
19381
- import { basename as basename6, dirname as dirname14, join as join25, relative as relative13, resolve as resolve20, sep as sep13 } from "path";
19561
+ import { lstat as lstat9, mkdir as mkdir15, open as open7, readFile as readFile19, readdir as readdir6, realpath as realpath10, rename as rename6, rm as rm8, stat as stat8 } from "fs/promises";
19562
+ import { basename as basename6, dirname as dirname14, join as join26, relative as relative13, resolve as resolve20, sep as sep13 } from "path";
19382
19563
  import { randomUUID as randomUUID13 } from "crypto";
19383
19564
  import { z as z29 } from "zod";
19384
19565
 
@@ -20161,7 +20342,7 @@ var SessionStore = class {
20161
20342
  #maxSessions;
20162
20343
  constructor(options) {
20163
20344
  this.#workspace = resolve20(options.workspace);
20164
- this.#sessions = join25(this.#workspace, ".flavor", "sessions");
20345
+ this.#sessions = join26(this.#workspace, ".flavor", "sessions");
20165
20346
  this.#maxBytes = options.maxBytes ?? DEFAULT_MAX_SESSION_BYTES;
20166
20347
  if (!Number.isSafeInteger(this.#maxBytes) || this.#maxBytes < 256) throw new Error("maxBytes must be an integer of at least 256");
20167
20348
  this.#maxSessions = options.maxSessions ?? 50;
@@ -20193,7 +20374,7 @@ var SessionStore = class {
20193
20374
  const body = `${lines.join("\n")}
20194
20375
  `;
20195
20376
  if (Buffer.byteLength(body) > this.#maxBytes) throw new Error(`Session exceeds maximum size of ${this.#maxBytes} bytes`);
20196
- const temporary = join25(this.#sessions, `.${document.sessionId}.${process.pid}.${randomUUID13()}.tmp`);
20377
+ const temporary = join26(this.#sessions, `.${document.sessionId}.${process.pid}.${randomUUID13()}.tmp`);
20197
20378
  let handle;
20198
20379
  try {
20199
20380
  handle = await open7(temporary, constants7.O_CREAT | constants7.O_EXCL | constants7.O_WRONLY, 384);
@@ -20212,7 +20393,7 @@ var SessionStore = class {
20212
20393
  async acquireLease(sessionId) {
20213
20394
  SessionIdSchema.parse(sessionId);
20214
20395
  await this.#prepareDirectory();
20215
- const path = join25(this.#sessions, `.${sessionId}.lock`);
20396
+ const path = join26(this.#sessions, `.${sessionId}.lock`);
20216
20397
  const token = JSON.stringify({ pid: process.pid, nonce: randomUUID13(), createdAt: (/* @__PURE__ */ new Date()).toISOString() });
20217
20398
  for (let attempt = 0; attempt < 3; attempt += 1) {
20218
20399
  let handle;
@@ -20230,7 +20411,7 @@ var SessionStore = class {
20230
20411
  release: async () => {
20231
20412
  if (released) return;
20232
20413
  try {
20233
- if (await readFile18(path, "utf8") === token) await rm8(path, { force: true });
20414
+ if (await readFile19(path, "utf8") === token) await rm8(path, { force: true });
20234
20415
  } catch (error) {
20235
20416
  if (!isCode8(error, "ENOENT")) throw error;
20236
20417
  }
@@ -20346,14 +20527,14 @@ var SessionStore = class {
20346
20527
  if (isCode8(error, "ENOENT")) return;
20347
20528
  throw error;
20348
20529
  }
20349
- const owner2 = await reclaimStaleSessionLock(join25(this.#sessions, `.${sessionId}.lock`));
20530
+ const owner2 = await reclaimStaleSessionLock(join26(this.#sessions, `.${sessionId}.lock`));
20350
20531
  if (owner2 !== void 0) throw new Error(`Session "${sessionId}" is active in process ${owner2} and cannot be deleted`);
20351
20532
  const path = this.#path(sessionId);
20352
20533
  try {
20353
- const metadata = await lstat8(path);
20534
+ const metadata = await lstat9(path);
20354
20535
  if (!metadata.isFile()) throw new Error("Session path is not a regular file");
20355
20536
  await rm8(path);
20356
- await rm8(join25(this.#sessions, `${sessionId}.events.jsonl`), { force: true }).catch(() => void 0);
20537
+ await rm8(join26(this.#sessions, `${sessionId}.events.jsonl`), { force: true }).catch(() => void 0);
20357
20538
  } catch (error) {
20358
20539
  if (!isCode8(error, "ENOENT")) throw error;
20359
20540
  }
@@ -20369,14 +20550,14 @@ var SessionStore = class {
20369
20550
  if (entries.length <= this.#maxSessions) return;
20370
20551
  let retained = 0;
20371
20552
  for (const entry2 of entries) {
20372
- const owner2 = await reclaimStaleSessionLock(join25(this.#sessions, `.${entry2.sessionId}.lock`));
20553
+ const owner2 = await reclaimStaleSessionLock(join26(this.#sessions, `.${entry2.sessionId}.lock`));
20373
20554
  if (owner2 !== void 0) continue;
20374
20555
  if (retained < this.#maxSessions) {
20375
20556
  retained += 1;
20376
20557
  continue;
20377
20558
  }
20378
20559
  await rm8(this.#path(entry2.sessionId), { force: true }).catch(() => void 0);
20379
- await rm8(join25(this.#sessions, `${entry2.sessionId}.events.jsonl`), { force: true }).catch(() => void 0);
20560
+ await rm8(join26(this.#sessions, `${entry2.sessionId}.events.jsonl`), { force: true }).catch(() => void 0);
20380
20561
  }
20381
20562
  }
20382
20563
  async #assertWorkspace(stored) {
@@ -20424,14 +20605,14 @@ function storedSessionVersion(raw) {
20424
20605
  }
20425
20606
  async function reclaimStaleSessionLock(path) {
20426
20607
  try {
20427
- const token = await readFile18(path, "utf8");
20608
+ const token = await readFile19(path, "utf8");
20428
20609
  const metadata = await stat8(path);
20429
20610
  const owner2 = parseSessionLockOwner(token);
20430
20611
  if (owner2 !== void 0 && isProcessAlive2(owner2)) return owner2;
20431
20612
  if (owner2 === void 0 && Date.now() - metadata.mtimeMs <= INVALID_SESSION_LOCK_STALE_MS) {
20432
20613
  throw new Error(`Session writer lock ${path} is incomplete or invalid; retry after the owning process exits`);
20433
20614
  }
20434
- if (await readFile18(path, "utf8") !== token) return void 0;
20615
+ if (await readFile19(path, "utf8") !== token) return void 0;
20435
20616
  const stale = `${path}.stale-${process.pid}-${randomUUID13()}`;
20436
20617
  try {
20437
20618
  await rename6(path, stale);
@@ -20467,7 +20648,7 @@ async function boundedRead(path, maxBytes) {
20467
20648
  const metadata = await stat8(path);
20468
20649
  if (!metadata.isFile()) throw new Error("Session path is not a regular file");
20469
20650
  if (metadata.size > maxBytes) throw new Error(`Session exceeds maximum read size of ${maxBytes} bytes`);
20470
- const value = await readFile18(path);
20651
+ const value = await readFile19(path);
20471
20652
  if (value.byteLength > maxBytes) throw new Error(`Session exceeds maximum read size of ${maxBytes} bytes`);
20472
20653
  return value.toString("utf8");
20473
20654
  }
@@ -20619,7 +20800,7 @@ async function assertNoSymlink3(root, target) {
20619
20800
  }
20620
20801
  for (const path of chain.reverse()) {
20621
20802
  try {
20622
- if ((await lstat8(path)).isSymbolicLink()) throw new Error(`Session path contains a symbolic link: ${path}`);
20803
+ if ((await lstat9(path)).isSymbolicLink()) throw new Error(`Session path contains a symbolic link: ${path}`);
20623
20804
  } catch (error) {
20624
20805
  if (!isCode8(error, "ENOENT")) throw error;
20625
20806
  }
@@ -20659,14 +20840,14 @@ function sanitize2(input, seen = /* @__PURE__ */ new WeakSet()) {
20659
20840
 
20660
20841
  // src/session/tree.ts
20661
20842
  import { randomUUID as randomUUID15 } from "crypto";
20662
- import { mkdir as mkdir17, readFile as readFile20, rename as rename8, writeFile as writeFile11 } from "fs/promises";
20663
- import { dirname as dirname16, join as join27, resolve as resolve22 } from "path";
20843
+ import { mkdir as mkdir17, readFile as readFile21, rename as rename8, writeFile as writeFile11 } from "fs/promises";
20844
+ import { dirname as dirname16, join as join28, resolve as resolve22 } from "path";
20664
20845
 
20665
20846
  // src/session/checkpoint.ts
20666
20847
  import { createHash as createHash16, randomUUID as randomUUID14 } from "crypto";
20667
20848
  import { execFile as execFile2 } from "child_process";
20668
- import { lstat as lstat9, mkdir as mkdir16, open as open8, readFile as readFile19, readdir as readdir7, rename as rename7, rm as rm9, writeFile as writeFile10 } from "fs/promises";
20669
- import { dirname as dirname15, join as join26, relative as relative14, resolve as resolve21, sep as sep14 } from "path";
20849
+ import { lstat as lstat10, mkdir as mkdir16, open as open8, readFile as readFile20, readdir as readdir7, rename as rename7, rm as rm9, writeFile as writeFile10 } from "fs/promises";
20850
+ import { dirname as dirname15, join as join27, relative as relative14, resolve as resolve21, sep as sep14 } from "path";
20670
20851
  var EXCLUDED = /* @__PURE__ */ new Set([".git", ".flavor", "node_modules", "dist", "build", "release", ".worktrees"]);
20671
20852
  var MAX_FILE_BYTES = 16 * 1024 * 1024;
20672
20853
  var MAX_TOTAL_BYTES = 128 * 1024 * 1024;
@@ -20679,9 +20860,9 @@ var WorkspaceCheckpointStore = class {
20679
20860
  #maxTotalBytes;
20680
20861
  constructor(options) {
20681
20862
  this.#workspace = resolve21(options.workspace);
20682
- this.#root = join26(this.#workspace, ".flavor", "checkpoints");
20683
- this.#objects = join26(this.#root, "objects");
20684
- this.#manifests = join26(this.#root, "manifests");
20863
+ this.#root = join27(this.#workspace, ".flavor", "checkpoints");
20864
+ this.#objects = join27(this.#root, "objects");
20865
+ this.#manifests = join27(this.#root, "manifests");
20685
20866
  this.#maxFileBytes = options.maxFileBytes ?? MAX_FILE_BYTES;
20686
20867
  this.#maxTotalBytes = options.maxTotalBytes ?? MAX_TOTAL_BYTES;
20687
20868
  }
@@ -20693,15 +20874,15 @@ var WorkspaceCheckpointStore = class {
20693
20874
  await mkdir16(this.#manifests, { recursive: true });
20694
20875
  for (const path of paths) {
20695
20876
  const absolute = contained(this.#workspace, path);
20696
- const info = await lstat9(absolute);
20877
+ const info = await lstat10(absolute);
20697
20878
  if (info.isSymbolicLink()) throw new Error(`Checkpoint refuses symbolic link: ${path}`);
20698
20879
  if (!info.isFile()) continue;
20699
20880
  if (info.size > this.#maxFileBytes) throw new Error(`Checkpoint file exceeds size limit: ${path}`);
20700
20881
  total += info.size;
20701
20882
  if (total > this.#maxTotalBytes) throw new Error("Checkpoint exceeds total size limit");
20702
- const content = await readFile19(absolute);
20883
+ const content = await readFile20(absolute);
20703
20884
  const digest = createHash16("sha256").update(content).digest("hex");
20704
- const objectPath = join26(this.#objects, digest);
20885
+ const objectPath = join27(this.#objects, digest);
20705
20886
  try {
20706
20887
  const handle = await open8(objectPath, "wx");
20707
20888
  try {
@@ -20721,12 +20902,12 @@ var WorkspaceCheckpointStore = class {
20721
20902
  ...label === void 0 || label.trim().length === 0 ? {} : { label: label.trim() },
20722
20903
  files
20723
20904
  };
20724
- await atomicJson(join26(this.#manifests, `${checkpoint.id}.json`), checkpoint);
20905
+ await atomicJson(join27(this.#manifests, `${checkpoint.id}.json`), checkpoint);
20725
20906
  return checkpoint;
20726
20907
  }
20727
20908
  async load(id) {
20728
20909
  if (!/^checkpoint-[A-Za-z0-9-]+$/.test(id)) throw new Error("Invalid checkpoint id");
20729
- const value = JSON.parse(await readFile19(join26(this.#manifests, `${id}.json`), "utf8"));
20910
+ const value = JSON.parse(await readFile20(join27(this.#manifests, `${id}.json`), "utf8"));
20730
20911
  if (value.version !== 1 || value.id !== id || !Array.isArray(value.files)) throw new Error("Invalid checkpoint manifest");
20731
20912
  for (const file of value.files) {
20732
20913
  contained(this.#workspace, file.path);
@@ -20738,7 +20919,7 @@ var WorkspaceCheckpointStore = class {
20738
20919
  const checkpoint = await this.load(id);
20739
20920
  const objects = /* @__PURE__ */ new Map();
20740
20921
  for (const file of checkpoint.files) {
20741
- const object = await readFile19(join26(this.#objects, file.digest));
20922
+ const object = await readFile20(join27(this.#objects, file.digest));
20742
20923
  if (object.length !== file.size || createHash16("sha256").update(object).digest("hex") !== file.digest) {
20743
20924
  throw new Error(`Checkpoint object is corrupt: ${file.digest}`);
20744
20925
  }
@@ -20761,7 +20942,7 @@ var WorkspaceCheckpointStore = class {
20761
20942
  }
20762
20943
  };
20763
20944
  async function discoverFiles(workspace) {
20764
- if (await exists2(join26(workspace, ".git"))) {
20945
+ if (await exists2(join27(workspace, ".git"))) {
20765
20946
  const listed = await gitFiles(workspace);
20766
20947
  return listed.filter((path) => !EXCLUDED.has(path.split("/")[0] ?? "")).sort();
20767
20948
  }
@@ -20770,7 +20951,7 @@ async function discoverFiles(workspace) {
20770
20951
  const entries = await readdir7(directory, { withFileTypes: true });
20771
20952
  for (const entry2 of entries) {
20772
20953
  if (EXCLUDED.has(entry2.name)) continue;
20773
- const absolute = join26(directory, entry2.name);
20954
+ const absolute = join27(directory, entry2.name);
20774
20955
  const path = normalizeRelative(relative14(workspace, absolute));
20775
20956
  if (entry2.isSymbolicLink()) throw new Error(`Checkpoint refuses symbolic link: ${path}`);
20776
20957
  if (entry2.isDirectory()) await walk(absolute);
@@ -20804,7 +20985,7 @@ async function gitFiles(workspace) {
20804
20985
  }
20805
20986
  async function exists2(path) {
20806
20987
  try {
20807
- await lstat9(path);
20988
+ await lstat10(path);
20808
20989
  return true;
20809
20990
  } catch (error) {
20810
20991
  if (isCode9(error, "ENOENT")) return false;
@@ -20845,17 +21026,17 @@ var SessionHistory = class _SessionHistory {
20845
21026
  #leafId;
20846
21027
  #unrevert;
20847
21028
  constructor(options, document) {
20848
- this.#path = join27(resolve22(options.workspace), ".flavor", "session-trees", options.sessionId, "tree.json");
21029
+ this.#path = join28(resolve22(options.workspace), ".flavor", "session-trees", options.sessionId, "tree.json");
20849
21030
  this.#checkpoints = new WorkspaceCheckpointStore({ workspace: options.workspace });
20850
21031
  this.#restoreContext = options.restoreContext;
20851
21032
  this.#nodes = boundedNodes(document.nodes);
20852
21033
  this.#leafId = document.leafId !== null && this.#nodes.some((node) => node.id === document.leafId) ? document.leafId : this.#nodes.at(-1)?.id ?? null;
20853
21034
  }
20854
21035
  static async open(options) {
20855
- const path = join27(resolve22(options.workspace), ".flavor", "session-trees", options.sessionId, "tree.json");
21036
+ const path = join28(resolve22(options.workspace), ".flavor", "session-trees", options.sessionId, "tree.json");
20856
21037
  let document = { version: 1, sessionId: options.sessionId, leafId: null, nodes: [] };
20857
21038
  try {
20858
- const parsed = JSON.parse(await readFile20(path, "utf8"));
21039
+ const parsed = JSON.parse(await readFile21(path, "utf8"));
20859
21040
  validate(parsed, options.sessionId);
20860
21041
  document = parsed;
20861
21042
  } catch (error) {
@@ -20988,7 +21169,7 @@ function isCode10(error, code) {
20988
21169
 
20989
21170
  // src/utils/rotation-census.ts
20990
21171
  import { mkdirSync as mkdirSync2, readdirSync, unlinkSync as unlinkSync2, writeFileSync } from "fs";
20991
- import { join as join28 } from "path";
21172
+ import { join as join29 } from "path";
20992
21173
  import { writeHeapSnapshot } from "v8";
20993
21174
  var CENSUS_PREFIX = "rotation-census-";
20994
21175
  var SNAPSHOT_PREFIX = "heap-";
@@ -21012,9 +21193,9 @@ function censusHeapBlock(reading) {
21012
21193
  }
21013
21194
  function writeRotationCensus(workspace, census) {
21014
21195
  try {
21015
- const directory = join28(workspace, ".flavor", "tmp");
21196
+ const directory = join29(workspace, ".flavor", "tmp");
21016
21197
  mkdirSync2(directory, { recursive: true });
21017
- const path = join28(directory, `${CENSUS_PREFIX}${Date.now()}.json`);
21198
+ const path = join29(directory, `${CENSUS_PREFIX}${Date.now()}.json`);
21018
21199
  writeFileSync(path, `${JSON.stringify(census, null, 2)}
21019
21200
  `, { encoding: "utf8", mode: 384 });
21020
21201
  prune(directory, CENSUS_PREFIX, ".json", KEEP_CENSUS_FILES);
@@ -21025,9 +21206,9 @@ function writeRotationCensus(workspace, census) {
21025
21206
  }
21026
21207
  function writeRotationHeapSnapshot(workspace) {
21027
21208
  try {
21028
- const directory = join28(workspace, ".flavor", "tmp");
21209
+ const directory = join29(workspace, ".flavor", "tmp");
21029
21210
  mkdirSync2(directory, { recursive: true });
21030
- const path = join28(directory, `${SNAPSHOT_PREFIX}${Date.now()}.heapsnapshot`);
21211
+ const path = join29(directory, `${SNAPSHOT_PREFIX}${Date.now()}.heapsnapshot`);
21031
21212
  writeHeapSnapshot(path);
21032
21213
  prune(directory, SNAPSHOT_PREFIX, ".heapsnapshot", KEEP_SNAPSHOT_FILES);
21033
21214
  return path;
@@ -21039,7 +21220,7 @@ function prune(directory, prefix, suffix, keep) {
21039
21220
  const stale = readdirSync(directory).filter((name) => name.startsWith(prefix) && name.endsWith(suffix)).sort().slice(0, -keep);
21040
21221
  for (const name of stale) {
21041
21222
  try {
21042
- unlinkSync2(join28(directory, name));
21223
+ unlinkSync2(join29(directory, name));
21043
21224
  } catch {
21044
21225
  }
21045
21226
  }
@@ -21048,8 +21229,8 @@ function prune(directory, prefix, suffix, keep) {
21048
21229
  // src/sleep/organizer.ts
21049
21230
  import { constants as constants8 } from "fs";
21050
21231
  import { randomUUID as randomUUID16 } from "crypto";
21051
- import { lstat as lstat10, mkdir as mkdir18, open as open9, readdir as readdir8, realpath as realpath11, rename as rename9, rm as rm10 } from "fs/promises";
21052
- import { isAbsolute as isAbsolute11, join as join29, relative as relative15, resolve as resolve23, sep as sep15 } from "path";
21232
+ import { lstat as lstat11, mkdir as mkdir18, open as open9, readdir as readdir8, realpath as realpath11, rename as rename9, rm as rm10 } from "fs/promises";
21233
+ import { isAbsolute as isAbsolute11, join as join30, relative as relative15, resolve as resolve23, sep as sep15 } from "path";
21053
21234
  import { z as z30 } from "zod";
21054
21235
  var DateKeySchema = z30.string().regex(/^\d{4}-\d{2}-\d{2}$/);
21055
21236
  var ReviewSchema = z30.object({
@@ -21104,7 +21285,7 @@ var ProjectSleepOrganizer = class {
21104
21285
  #generate;
21105
21286
  constructor(options) {
21106
21287
  this.#workspace = resolve23(options.workspace);
21107
- this.#sleepDirectory = join29(this.#workspace, ".flavor", "sleep");
21288
+ this.#sleepDirectory = join30(this.#workspace, ".flavor", "sleep");
21108
21289
  this.#sessions = options.sessions ?? new SessionStore({ workspace: this.#workspace });
21109
21290
  this.#generate = options.generate;
21110
21291
  }
@@ -21113,7 +21294,7 @@ var ProjectSleepOrganizer = class {
21113
21294
  signal.throwIfAborted();
21114
21295
  await this.#prepareDirectory();
21115
21296
  if (await this.#hasReport(date)) return { status: "exists", date };
21116
- const lockPath = join29(this.#sleepDirectory, `.${date}.lock`);
21297
+ const lockPath = join30(this.#sleepDirectory, `.${date}.lock`);
21117
21298
  let lock;
21118
21299
  try {
21119
21300
  lock = await open9(lockPath, constants8.O_CREAT | constants8.O_EXCL | constants8.O_WRONLY, 384);
@@ -21148,7 +21329,7 @@ Previous output was invalid. Repair it and return strict JSON only.`;
21148
21329
  }
21149
21330
  if (review === void 0) throw lastError;
21150
21331
  const filename2 = `${date}-${filenameSummary(review.title)}.md`;
21151
- const path = join29(this.#sleepDirectory, filename2);
21332
+ const path = join30(this.#sleepDirectory, filename2);
21152
21333
  const markdown = renderSleepReport(date, review, documents);
21153
21334
  await this.#atomicWrite(path, markdown);
21154
21335
  return { status: "written", date, path, sessionCount: documents.length };
@@ -21190,7 +21371,7 @@ Previous output was invalid. Repair it and return strict JSON only.`;
21190
21371
  });
21191
21372
  }
21192
21373
  async #prepareDirectory() {
21193
- const flavorDirectory = join29(this.#workspace, ".flavor");
21374
+ const flavorDirectory = join30(this.#workspace, ".flavor");
21194
21375
  await assertNotSymlink(flavorDirectory);
21195
21376
  await mkdir18(flavorDirectory, { recursive: true, mode: 448 });
21196
21377
  await assertNotSymlink(this.#sleepDirectory);
@@ -21204,7 +21385,7 @@ Previous output was invalid. Repair it and return strict JSON only.`;
21204
21385
  return (await readdir8(this.#sleepDirectory, { withFileTypes: true })).some((entry2) => entry2.isFile() && entry2.name.startsWith(prefix) && entry2.name.endsWith(".md"));
21205
21386
  }
21206
21387
  async #atomicWrite(path, content) {
21207
- const temporary = join29(this.#sleepDirectory, `.${randomUUID16()}.tmp`);
21388
+ const temporary = join30(this.#sleepDirectory, `.${randomUUID16()}.tmp`);
21208
21389
  let handle;
21209
21390
  try {
21210
21391
  handle = await open9(temporary, constants8.O_CREAT | constants8.O_EXCL | constants8.O_WRONLY, 384);
@@ -21520,7 +21701,7 @@ function assertDateKey(value) {
21520
21701
  }
21521
21702
  async function assertNotSymlink(path) {
21522
21703
  try {
21523
- if ((await lstat10(path)).isSymbolicLink()) throw new Error(`Refusing to use symbolic link: ${path}`);
21704
+ if ((await lstat11(path)).isSymbolicLink()) throw new Error(`Refusing to use symbolic link: ${path}`);
21524
21705
  } catch (error) {
21525
21706
  if (!hasCode(error, "ENOENT")) throw error;
21526
21707
  }
@@ -21578,7 +21759,7 @@ function computeSessionStats(documents) {
21578
21759
 
21579
21760
  // src/tools/files.ts
21580
21761
  import { constants as constants9 } from "fs";
21581
- import { mkdir as mkdir19, open as open10, readFile as readFile21, realpath as realpath12, rename as rename10, rm as rm11, stat as stat9 } from "fs/promises";
21762
+ import { mkdir as mkdir19, open as open10, readFile as readFile22, realpath as realpath12, rename as rename10, rm as rm11, stat as stat9 } from "fs/promises";
21582
21763
  import { dirname as dirname17, isAbsolute as isAbsolute12, relative as relative16, resolve as resolve24, sep as sep16 } from "path";
21583
21764
  import { randomUUID as randomUUID17 } from "crypto";
21584
21765
  import { z as z31 } from "zod";
@@ -21924,7 +22105,7 @@ function createPathGuard(workspace) {
21924
22105
  if (!within2(physicalRoot, physical)) throw new Error("Path escapes the workspace through a symlink");
21925
22106
  break;
21926
22107
  } catch (error) {
21927
- if (!isMissing2(error)) throw error;
22108
+ if (!isMissing3(error)) throw error;
21928
22109
  const parent = dirname17(ancestor);
21929
22110
  if (parent === ancestor) throw error;
21930
22111
  ancestor = parent;
@@ -21942,7 +22123,7 @@ async function atomicWrite(path, content, signal, expectedVersion) {
21942
22123
  try {
21943
22124
  mode = (await stat9(path)).mode & 511;
21944
22125
  } catch (error) {
21945
- if (!isMissing2(error)) throw error;
22126
+ if (!isMissing3(error)) throw error;
21946
22127
  }
21947
22128
  try {
21948
22129
  const handle = await open10(temporary, constants9.O_CREAT | constants9.O_EXCL | constants9.O_WRONLY, 384);
@@ -21963,7 +22144,7 @@ async function atomicWrite(path, content, signal, expectedVersion) {
21963
22144
  }
21964
22145
  }
21965
22146
  async function readText(path) {
21966
- const contents = await readFile21(path);
22147
+ const contents = await readFile22(path);
21967
22148
  if (isBinary(contents)) throw new Error("Cannot edit binary file as text");
21968
22149
  return decodeFileText(contents);
21969
22150
  }
@@ -21991,7 +22172,7 @@ async function fileVersion(path) {
21991
22172
  try {
21992
22173
  return versionOfStat(await stat9(path));
21993
22174
  } catch (error) {
21994
- if (isMissing2(error)) return ABSENT_VERSION;
22175
+ if (isMissing3(error)) return ABSENT_VERSION;
21995
22176
  throw error;
21996
22177
  }
21997
22178
  }
@@ -22000,10 +22181,10 @@ function versionOfStat(info) {
22000
22181
  }
22001
22182
  async function readOptionalPresentationText(path) {
22002
22183
  try {
22003
- const contents = await readFile21(path);
22184
+ const contents = await readFile22(path);
22004
22185
  return isBinary(contents) ? { exists: true } : { exists: true, text: contents.toString("utf8") };
22005
22186
  } catch (error) {
22006
- return isMissing2(error) ? { exists: false } : { exists: true };
22187
+ return isMissing3(error) ? { exists: false } : { exists: true };
22007
22188
  }
22008
22189
  }
22009
22190
  function isBinary(contents) {
@@ -22112,7 +22293,7 @@ function mergeRange(ranges, start, end) {
22112
22293
  function abortIfNeeded(signal) {
22113
22294
  if (signal.aborted) throw signal.reason;
22114
22295
  }
22115
- function isMissing2(error) {
22296
+ function isMissing3(error) {
22116
22297
  return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
22117
22298
  }
22118
22299
  var PATCH_SEARCH_RADIUS = 100;
@@ -22197,7 +22378,7 @@ async function requireAbsent(guard, path) {
22197
22378
  try {
22198
22379
  await guard.existing(path);
22199
22380
  } catch (error) {
22200
- if (isMissing2(error)) return "";
22381
+ if (isMissing3(error)) return "";
22201
22382
  throw error;
22202
22383
  }
22203
22384
  throw new Error("Patch creation destination already exists");
@@ -23033,7 +23214,7 @@ async function readOptionalIgnoreFile(path, maxBytes, signal) {
23033
23214
  try {
23034
23215
  handle = await open11(path, "r");
23035
23216
  } catch (error) {
23036
- if (isMissing3(error)) return void 0;
23217
+ if (isMissing4(error)) return void 0;
23037
23218
  throw error;
23038
23219
  }
23039
23220
  const buffer = Buffer.allocUnsafe(maxBytes + 1);
@@ -23051,7 +23232,7 @@ async function readOptionalIgnoreFile(path, maxBytes, signal) {
23051
23232
  if (offset > maxBytes) throw new Error(`Ignore file byte limit of ${maxBytes} exceeded: ${path}`);
23052
23233
  return buffer.subarray(0, offset);
23053
23234
  }
23054
- function isMissing3(error) {
23235
+ function isMissing4(error) {
23055
23236
  return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
23056
23237
  }
23057
23238
 
@@ -23385,7 +23566,7 @@ function charset(contentType) {
23385
23566
  // src/tools/lsp.ts
23386
23567
  import { spawn as spawn6 } from "child_process";
23387
23568
  import { existsSync as existsSync5 } from "fs";
23388
- import { access as access2, constants as constants10, readFile as readFile22 } from "fs/promises";
23569
+ import { access as access2, constants as constants10, readFile as readFile23 } from "fs/promises";
23389
23570
  import { basename as basename7, dirname as dirname18, extname as extname3, isAbsolute as isAbsolute14, relative as relative18, resolve as resolve26, sep as sep18 } from "path";
23390
23571
  import { fileURLToPath as fileURLToPath2, pathToFileURL as pathToFileURL3 } from "url";
23391
23572
  import { z as z34 } from "zod";
@@ -23581,7 +23762,7 @@ var LspConnection = class {
23581
23762
  const url = new URL(uri);
23582
23763
  if (url.protocol !== "file:") return;
23583
23764
  const filePath = fileURLToPath2(url);
23584
- const text = await readFile22(filePath, "utf8");
23765
+ const text = await readFile23(filePath, "utf8");
23585
23766
  const current = this.#documents.get(uri);
23586
23767
  if (current === void 0) {
23587
23768
  this.#documents.set(uri, { text, version: 1 });
@@ -24315,12 +24496,12 @@ ${explanation}` : "";
24315
24496
  // src/desktop/astgraph-service.ts
24316
24497
  import { stat as stat11 } from "fs/promises";
24317
24498
  import { createRequire as nodeCreateRequire } from "module";
24318
- import { join as join30, resolve as resolve27 } from "path";
24499
+ import { join as join31, resolve as resolve27 } from "path";
24319
24500
  var FIELDS = "id, kind, name, qualified_name, file_path, language, start_line, end_line, signature";
24320
24501
  var DesktopAstGraphService = class {
24321
24502
  #path;
24322
24503
  constructor(workspace) {
24323
- this.#path = join30(resolve27(workspace), ".flavor", "astgraph", "index.db");
24504
+ this.#path = join31(resolve27(workspace), ".flavor", "astgraph", "index.db");
24324
24505
  }
24325
24506
  async status() {
24326
24507
  if ((await stat11(this.#path).catch(() => void 0))?.isFile() !== true) {
@@ -24603,8 +24784,8 @@ function createTodoWriteTool() {
24603
24784
 
24604
24785
  // src/tools/managed.ts
24605
24786
  import { randomUUID as randomUUID18 } from "crypto";
24606
- import { link, lstat as lstat11, mkdir as mkdir20, readFile as readFile23, readdir as readdir9, unlink as unlink3, writeFile as writeFile12 } from "fs/promises";
24607
- import { dirname as dirname19, join as join31, resolve as resolve29 } from "path";
24787
+ import { link, lstat as lstat12, mkdir as mkdir20, readFile as readFile24, readdir as readdir9, unlink as unlink3, writeFile as writeFile12 } from "fs/promises";
24788
+ import { dirname as dirname19, join as join32, resolve as resolve29 } from "path";
24608
24789
  import { z as z39 } from "zod";
24609
24790
  var MANAGED_TOOL_VERSION = 1;
24610
24791
  var MAX_RECORD_BYTES = 256 * 1024;
@@ -24656,8 +24837,8 @@ var ManagedToolStore = class {
24656
24837
  #diagnostics = [];
24657
24838
  constructor(options) {
24658
24839
  this.#workspace = resolve29(options.workspace);
24659
- this.#projectRoot = join31(this.#workspace, ".flavor", "tools");
24660
- this.#globalRoot = join31(resolve29(options.home), ".flavor-code", "tools");
24840
+ this.#projectRoot = join32(this.#workspace, ".flavor", "tools");
24841
+ this.#globalRoot = join32(resolve29(options.home), ".flavor-code", "tools");
24661
24842
  this.#now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
24662
24843
  }
24663
24844
  get diagnostics() {
@@ -24704,7 +24885,7 @@ var ManagedToolStore = class {
24704
24885
  version: MANAGED_TOOL_VERSION,
24705
24886
  createdAt: this.#now()
24706
24887
  });
24707
- const path = join31(this.directory(input.scope), filename(input.name));
24888
+ const path = join32(this.directory(input.scope), filename(input.name));
24708
24889
  const definition = compileDefinition(record3, input.scope, this.#workspace);
24709
24890
  await writeExclusive(path, `${JSON.stringify(record3, void 0, 2)}
24710
24891
  `);
@@ -24718,7 +24899,7 @@ var ManagedToolStore = class {
24718
24899
  if (matches.length === 0) throw new Error(`Managed tool "${input.name}" was not found`);
24719
24900
  if (matches.length > 1) throw new Error(`Managed tool "${input.name}" exists in multiple scopes; specify scope`);
24720
24901
  const entry2 = matches[0];
24721
- const info = await lstat11(entry2.path);
24902
+ const info = await lstat12(entry2.path);
24722
24903
  if (!info.isFile() || info.isSymbolicLink()) throw new Error(`Managed tool path is no longer a regular file: ${entry2.path}`);
24723
24904
  await unlink3(entry2.path);
24724
24905
  this.#entries.delete(scopedKey(entry2.scope, entry2.record.name));
@@ -24729,7 +24910,7 @@ var ManagedToolStore = class {
24729
24910
  if (!parsed.success) return [];
24730
24911
  const matches = this.#matching(parsed.data.name).filter((entry2) => parsed.data.scope === void 0 || entry2.scope === parsed.data.scope);
24731
24912
  if (matches.length > 0) return matches.map((entry2) => entry2.path);
24732
- return [join31(this.directory(parsed.data.scope ?? "project"), filename(parsed.data.name))];
24913
+ return [join32(this.directory(parsed.data.scope ?? "project"), filename(parsed.data.name))];
24733
24914
  }
24734
24915
  async #loadDirectory(scope2) {
24735
24916
  const root = this.directory(scope2);
@@ -24737,17 +24918,17 @@ var ManagedToolStore = class {
24737
24918
  try {
24738
24919
  names = (await readdir9(root, { withFileTypes: true })).filter((entry2) => entry2.name.endsWith(".json")).sort((left, right) => compare2(left.name, right.name)).map((entry2) => entry2.name);
24739
24920
  } catch (error) {
24740
- if (isMissing4(error)) return;
24921
+ if (isMissing5(error)) return;
24741
24922
  this.#diagnostics.push(`${root}: ${message(error)}`);
24742
24923
  return;
24743
24924
  }
24744
24925
  for (const name of names) {
24745
- const path = join31(root, name);
24926
+ const path = join32(root, name);
24746
24927
  try {
24747
- const info = await lstat11(path);
24928
+ const info = await lstat12(path);
24748
24929
  if (!info.isFile() || info.isSymbolicLink()) throw new Error("Managed tool record must be a regular file");
24749
24930
  if (info.size > MAX_RECORD_BYTES) throw new Error(`Managed tool record exceeds ${MAX_RECORD_BYTES} bytes`);
24750
- const record3 = ManagedToolRecordSchema.parse(JSON.parse(await readFile23(path, "utf8")));
24931
+ const record3 = ManagedToolRecordSchema.parse(JSON.parse(await readFile24(path, "utf8")));
24751
24932
  if (record3.scope !== scope2) throw new Error(`Managed tool record scope must match its ${scope2} directory`);
24752
24933
  const key = scopedKey(scope2, record3.name);
24753
24934
  if (this.#entries.has(key)) throw new Error(`Duplicate managed tool name in ${scope2} scope: ${record3.name}`);
@@ -24940,7 +25121,7 @@ function compare2(left, right) {
24940
25121
  function compareScope(left, right) {
24941
25122
  return left === right ? 0 : left === "global" ? -1 : 1;
24942
25123
  }
24943
- function isMissing4(error) {
25124
+ function isMissing5(error) {
24944
25125
  return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
24945
25126
  }
24946
25127
 
@@ -25341,6 +25522,7 @@ var MVP_COMMANDS = [
25341
25522
  "remember",
25342
25523
  "forget",
25343
25524
  "forget-cold",
25525
+ "global",
25344
25526
  "checkpoint",
25345
25527
  "tree",
25346
25528
  "rewind",
@@ -25439,6 +25621,13 @@ function parseSlashCommand(input, dynamicCommands = [], skillCommands = [], mana
25439
25621
  const query = args.join(" ").trim();
25440
25622
  return query.length === 0 ? { name: "invalid", command: name, message: "Use /forget <text-or-id>." } : { name, query };
25441
25623
  }
25624
+ if (name === "global") {
25625
+ const [action, ...parts] = args;
25626
+ if (action === void 0) return { name, action: "show" };
25627
+ const text = parts.join(" ").trim();
25628
+ if ((action === "remember" || action === "forget") && text.length > 0) return { name, action, text };
25629
+ return { name: "invalid", command: name, message: "Use /global [remember <rule>|forget <rule>]." };
25630
+ }
25442
25631
  if (name === "checkpoint") {
25443
25632
  const label = args.join(" ").trim();
25444
25633
  return label.length === 0 ? { name } : { name, label };
@@ -25603,6 +25792,7 @@ var HELP = [
25603
25792
  "/logout clear stored OAuth credentials",
25604
25793
  "/init /config /skills /plugins /hooks /tasks /doctor",
25605
25794
  "/memory /remember [type] <text> /forget <text-or-id> /forget-cold /finish",
25795
+ "/global /global remember <rule> /global forget <rule>",
25606
25796
  "/checkpoint [label] /tree /rewind <node> /unrevert /fork <node>",
25607
25797
  "/compact /clear /paste-image /help /exit",
25608
25798
  "/loop <goal> run a verified autonomous loop",
@@ -26073,6 +26263,8 @@ Address this now (for example run the required verification or fix), then finish
26073
26263
  this.#notice(await required(this.#services.ide, "ide")());
26074
26264
  } else if (command2.name === "memory") {
26075
26265
  this.#notice(await this.#services.memory());
26266
+ } else if (command2.name === "global") {
26267
+ this.#notice(await required(this.#services.global, "global")(command2));
26076
26268
  } else if (command2.name === "remember") {
26077
26269
  this.#notice(await this.#services.remember(command2.type, command2.text));
26078
26270
  } else if (command2.name === "forget") {
@@ -26358,9 +26550,9 @@ function requiredPals(service) {
26358
26550
 
26359
26551
  // src/doctor.ts
26360
26552
  import { constants as constants11 } from "fs";
26361
- import { access as access3, lstat as lstat12, readFile as readFile24, readdir as readdir10, stat as stat12 } from "fs/promises";
26553
+ import { access as access3, lstat as lstat13, readFile as readFile25, readdir as readdir10, stat as stat12 } from "fs/promises";
26362
26554
  import { homedir } from "os";
26363
- import { isAbsolute as isAbsolute16, join as join32, relative as relative20, resolve as resolve30, sep as sep20 } from "path";
26555
+ import { isAbsolute as isAbsolute16, join as join33, relative as relative20, resolve as resolve30, sep as sep20 } from "path";
26364
26556
  import { rgPath as bundledRgPath2 } from "@vscode/ripgrep";
26365
26557
 
26366
26558
  // src/utils/semver.ts
@@ -26407,7 +26599,7 @@ async function runDoctor(options = {}, dependencies2 = {}) {
26407
26599
  const checks = [];
26408
26600
  checks.push(runtimeCheck(nodeVersion, platform, arch));
26409
26601
  checks.push(await directoryCheck("workspace", workspace, false));
26410
- checks.push(await directoryCheck("state directory", join32(home, ".flavor-code"), true, home));
26602
+ checks.push(await directoryCheck("state directory", join33(home, ".flavor-code"), true, home));
26411
26603
  let loaded;
26412
26604
  try {
26413
26605
  loaded = await (dependencies2.loadConfig ?? loadConfig)({
@@ -26433,8 +26625,8 @@ async function runDoctor(options = {}, dependencies2 = {}) {
26433
26625
  checks.push({ name: "shell", status: "fail", message: errorMessage3(error) });
26434
26626
  }
26435
26627
  checks.push(await pluginCheck([
26436
- join32(home, ".flavor-code", "plugins"),
26437
- join32(workspace, ".flavor", "plugins")
26628
+ join33(home, ".flavor-code", "plugins"),
26629
+ join33(workspace, ".flavor", "plugins")
26438
26630
  ]));
26439
26631
  try {
26440
26632
  const latest = await (dependencies2.fetchLatestVersion ?? fetchLatestVersion)({
@@ -26549,13 +26741,13 @@ async function pluginCheck(directories) {
26549
26741
  if (!entry2.isDirectory() || entry2.name.startsWith(".")) continue;
26550
26742
  const root = resolve30(directory, entry2.name);
26551
26743
  try {
26552
- const rootInfo = await lstat12(root);
26744
+ const rootInfo = await lstat13(root);
26553
26745
  if (rootInfo.isSymbolicLink()) throw new Error("plugin directory must not be a symbolic link");
26554
- const manifest = PluginManifestSchema.parse(JSON.parse(await readFile24(join32(root, "flavor-plugin.json"), "utf8")));
26746
+ const manifest = PluginManifestSchema.parse(JSON.parse(await readFile25(join33(root, "flavor-plugin.json"), "utf8")));
26555
26747
  const main = resolve30(root, manifest.main);
26556
26748
  const delta = relative20(root, main);
26557
26749
  if (delta === ".." || delta.startsWith(`..${sep20}`) || isAbsolute16(delta)) throw new Error("plugin entry escapes its root");
26558
- const mainInfo = await lstat12(main);
26750
+ const mainInfo = await lstat13(main);
26559
26751
  if (!mainInfo.isFile() || mainInfo.isSymbolicLink()) throw new Error("plugin entry must be a regular file");
26560
26752
  valid += 1;
26561
26753
  } catch (error) {
@@ -26619,7 +26811,7 @@ var LANG_LABELS = /* @__PURE__ */ new Map([
26619
26811
 
26620
26812
  // src/utils/crash-guard.ts
26621
26813
  import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync2 } from "fs";
26622
- import { join as join33 } from "path";
26814
+ import { join as join34 } from "path";
26623
26815
  var installed = false;
26624
26816
  var crashing = false;
26625
26817
  var crashCleanups = /* @__PURE__ */ new Set();
@@ -26650,7 +26842,7 @@ function installCrashGuard(options = {}) {
26650
26842
  installed = true;
26651
26843
  const crashLogDir = () => {
26652
26844
  try {
26653
- return join33((options.workspace ?? process.cwd)(), ".flavor");
26845
+ return join34((options.workspace ?? process.cwd)(), ".flavor");
26654
26846
  } catch {
26655
26847
  return process.cwd();
26656
26848
  }
@@ -26659,7 +26851,7 @@ function installCrashGuard(options = {}) {
26659
26851
  try {
26660
26852
  const dir = crashLogDir();
26661
26853
  mkdirSync3(dir, { recursive: true });
26662
- const path = join33(dir, `crash-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "")}-${process.pid}.log`);
26854
+ const path = join34(dir, `crash-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "")}-${process.pid}.log`);
26663
26855
  writeFileSync2(path, [
26664
26856
  `kind: ${kind}`,
26665
26857
  `version: ${process.version}`,
@@ -27430,8 +27622,8 @@ ${total}`;
27430
27622
 
27431
27623
  // src/memory/store.ts
27432
27624
  import { createHash as createHash18 } from "crypto";
27433
- import { readFile as readFile25, rm as rm12 } from "fs/promises";
27434
- import { resolve as resolve31, join as join34, relative as relative21, sep as sep21 } from "path";
27625
+ import { readFile as readFile26, rm as rm12 } from "fs/promises";
27626
+ import { resolve as resolve31, join as join35, relative as relative21, sep as sep21 } from "path";
27435
27627
 
27436
27628
  // src/memory/similarity.ts
27437
27629
  function normalizeForSimilarity(value) {
@@ -27534,9 +27726,9 @@ var MemoryStore = class {
27534
27726
  if (!Number.isSafeInteger(options.maxEntries) || options.maxEntries < 1) throw new Error("maxEntries must be an integer of at least 1");
27535
27727
  if (!Number.isSafeInteger(options.maxEntryChars) || options.maxEntryChars < 1) throw new Error("maxEntryChars must be an integer of at least 1");
27536
27728
  this.workspace = resolve31(options.workspace);
27537
- this.#memoryRoot = join34(this.workspace, ".flavor", "memory");
27538
- this.path = join34(this.#memoryRoot, "MEMORY.md");
27539
- this.#behaviorPath = join34(this.#memoryRoot, "behavior.json");
27729
+ this.#memoryRoot = join35(this.workspace, ".flavor", "memory");
27730
+ this.path = join35(this.#memoryRoot, "MEMORY.md");
27731
+ this.#behaviorPath = join35(this.#memoryRoot, "behavior.json");
27540
27732
  this.#maxEntries = options.maxEntries;
27541
27733
  this.#maxEntryChars = options.maxEntryChars;
27542
27734
  }
@@ -27754,7 +27946,7 @@ var MemoryStore = class {
27754
27946
  });
27755
27947
  }
27756
27948
  async #readTaskItem(reference) {
27757
- const document = decodeTask(await readFile25(this.#resolveContentPath(reference.contentPath), "utf8"));
27949
+ const document = decodeTask(await readFile26(this.#resolveContentPath(reference.contentPath), "utf8"));
27758
27950
  return document.items.find((item) => item.id === reference.id)?.content ?? reference.summary;
27759
27951
  }
27760
27952
  #resolveContentPath(contentPath) {
@@ -28394,8 +28586,8 @@ function createExecutionEnvironment(workspace, config) {
28394
28586
  }
28395
28587
 
28396
28588
  // src/ide/client.ts
28397
- import { readFile as readFile26, readdir as readdir11, stat as stat13 } from "fs/promises";
28398
- import { basename as basename8, join as join35, relative as relative23, resolve as resolve33, sep as sep23 } from "path";
28589
+ import { readFile as readFile27, readdir as readdir11, stat as stat13 } from "fs/promises";
28590
+ import { basename as basename8, join as join36, relative as relative23, resolve as resolve33, sep as sep23 } from "path";
28399
28591
  var FlavorIdeClient = class {
28400
28592
  #workspace;
28401
28593
  #home;
@@ -28491,7 +28683,7 @@ var FlavorIdeClient = class {
28491
28683
  };
28492
28684
  if (await this.#healthy(configured)) return configured;
28493
28685
  }
28494
- const directory = join35(this.#home, ".flavor-code", "ide");
28686
+ const directory = join36(this.#home, ".flavor-code", "ide");
28495
28687
  let entries;
28496
28688
  try {
28497
28689
  entries = (await readdir11(directory)).filter((name) => name.endsWith(".lock"));
@@ -28499,9 +28691,9 @@ var FlavorIdeClient = class {
28499
28691
  return void 0;
28500
28692
  }
28501
28693
  const candidates = (await Promise.all(entries.map(async (name) => {
28502
- const path = join35(directory, name);
28694
+ const path = join36(directory, name);
28503
28695
  try {
28504
- const [contents, info] = await Promise.all([readFile26(path, "utf8"), stat13(path)]);
28696
+ const [contents, info] = await Promise.all([readFile27(path, "utf8"), stat13(path)]);
28505
28697
  const lock = parseLockfile(contents);
28506
28698
  return lock === void 0 ? void 0 : { lock, modified: info.mtimeMs };
28507
28699
  } catch {
@@ -29041,29 +29233,29 @@ function palSocketAddress(options) {
29041
29233
  import { createHash as createHash21 } from "crypto";
29042
29234
  import { Socket as Socket2 } from "net";
29043
29235
  import { tmpdir as tmpdir3 } from "os";
29044
- import { join as join37 } from "path";
29236
+ import { join as join38 } from "path";
29045
29237
  import { spawn as spawn8 } from "child_process";
29046
29238
 
29047
29239
  // src/pals/broker.ts
29048
29240
  import { createHash as createHash20, randomUUID as randomUUID23, timingSafeEqual } from "crypto";
29049
- import { chmod as chmod3, lstat as lstat13, mkdir as mkdir23, unlink as unlink6 } from "fs/promises";
29241
+ import { chmod as chmod3, lstat as lstat14, mkdir as mkdir23, unlink as unlink6 } from "fs/promises";
29050
29242
  import { createServer as createServer4, Socket } from "net";
29051
29243
  import { dirname as dirname21 } from "path";
29052
29244
 
29053
29245
  // src/pals/auth.ts
29054
29246
  import { randomBytes as randomBytes3, randomUUID as randomUUID21 } from "crypto";
29055
- import { chmod as chmod2, link as link2, mkdir as mkdir21, open as open12, readFile as readFile27, unlink as unlink4 } from "fs/promises";
29247
+ import { chmod as chmod2, link as link2, mkdir as mkdir21, open as open12, readFile as readFile28, unlink as unlink4 } from "fs/promises";
29056
29248
  import { homedir as homedir2 } from "os";
29057
- import { join as join36 } from "path";
29249
+ import { join as join37 } from "path";
29058
29250
  var PAL_AUTH_TOKEN_BYTES = 32;
29059
29251
  var TOKEN_PATTERN = /^[a-f0-9]{64}$/;
29060
29252
  async function loadOrCreatePalAuthToken(options = {}) {
29061
- const directory = join36(options.home ?? homedir2(), ".flavor-code", "pals");
29062
- const tokenPath = join36(directory, "auth-token");
29253
+ const directory = join37(options.home ?? homedir2(), ".flavor-code", "pals");
29254
+ const tokenPath = join37(directory, "auth-token");
29063
29255
  await mkdir21(directory, { recursive: true, mode: 448 });
29064
29256
  await chmod2(directory, 448);
29065
29257
  const token = (options.random ?? randomBytes3)(PAL_AUTH_TOKEN_BYTES).toString("hex");
29066
- const temporaryPath = join36(directory, `.auth-token-${process.pid}-${randomUUID21()}.tmp`);
29258
+ const temporaryPath = join37(directory, `.auth-token-${process.pid}-${randomUUID21()}.tmp`);
29067
29259
  let handle;
29068
29260
  try {
29069
29261
  handle = await open12(temporaryPath, "wx", 384);
@@ -29086,7 +29278,7 @@ async function loadOrCreatePalAuthToken(options = {}) {
29086
29278
  if (error.code !== "ENOENT") throw error;
29087
29279
  });
29088
29280
  }
29089
- const stored = (await readFile27(tokenPath, "utf8")).trim();
29281
+ const stored = (await readFile28(tokenPath, "utf8")).trim();
29090
29282
  if (!TOKEN_PATTERN.test(stored)) throw new Error("Pals authentication token store is invalid");
29091
29283
  await chmod2(tokenPath, 384);
29092
29284
  return stored;
@@ -29098,7 +29290,7 @@ function validatePalAuthToken(token) {
29098
29290
 
29099
29291
  // src/pals/lifecycle.ts
29100
29292
  import { randomUUID as randomUUID22 } from "crypto";
29101
- import { mkdir as mkdir22, open as open13, readFile as readFile28, rename as rename11, stat as stat14, unlink as unlink5 } from "fs/promises";
29293
+ import { mkdir as mkdir22, open as open13, readFile as readFile29, rename as rename11, stat as stat14, unlink as unlink5 } from "fs/promises";
29102
29294
  import { dirname as dirname20 } from "path";
29103
29295
  function defaultProcessAlive(pid) {
29104
29296
  if (!Number.isSafeInteger(pid) || pid <= 0) return true;
@@ -29130,7 +29322,7 @@ async function acquirePalFileLock(options) {
29130
29322
  handle = await open13(options.path, "wx", 384);
29131
29323
  } catch (error) {
29132
29324
  if (error.code !== "EEXIST") throw error;
29133
- const [contents, info] = await Promise.all([readFile28(options.path, "utf8").catch(() => ""), stat14(options.path).catch(() => void 0)]);
29325
+ const [contents, info] = await Promise.all([readFile29(options.path, "utf8").catch(() => ""), stat14(options.path).catch(() => void 0)]);
29134
29326
  if (await options.endpointLive()) return void 0;
29135
29327
  const existing = parseMetadata(contents);
29136
29328
  if (existing !== void 0 && (options.processAlive ?? defaultProcessAlive)(existing.pid)) return void 0;
@@ -29160,7 +29352,7 @@ async function acquirePalFileLock(options) {
29160
29352
  await handle.close();
29161
29353
  }
29162
29354
  const release = async () => {
29163
- const current = parseMetadata(await readFile28(options.path, "utf8").catch(() => ""));
29355
+ const current = parseMetadata(await readFile29(options.path, "utf8").catch(() => ""));
29164
29356
  if (current?.nonce !== nonce) return;
29165
29357
  await unlink5(options.path).catch((error) => {
29166
29358
  if (error.code !== "ENOENT") throw error;
@@ -29193,7 +29385,7 @@ function canConnect(address) {
29193
29385
  }
29194
29386
  function startupLockPath(address) {
29195
29387
  const digest = createHash21("sha256").update(address, "utf8").digest("hex").slice(0, 24);
29196
- return join37(tmpdir3(), `flavor-code-pals-${digest}.startup.lock`);
29388
+ return join38(tmpdir3(), `flavor-code-pals-${digest}.startup.lock`);
29197
29389
  }
29198
29390
  async function spawnProductionBroker(address) {
29199
29391
  const entry2 = process.argv[1];
@@ -29900,6 +30092,8 @@ async function createProductionRuntime(options) {
29900
30092
  const gitRepository = detectGitRepository(workspace);
29901
30093
  const loaded = await loadConfig({ cwd: workspace, home, environment });
29902
30094
  const config = loaded.config;
30095
+ const globalInstructions = config.globalInstructions.enabled ? new GlobalInstructions(home) : void 0;
30096
+ await globalInstructions?.refresh();
29903
30097
  const mcpReady = connectMcpServers({
29904
30098
  servers: config.mcpServers,
29905
30099
  workspace,
@@ -30034,6 +30228,18 @@ async function createProductionRuntime(options) {
30034
30228
  );
30035
30229
  const registry = new ModelRegistry();
30036
30230
  const diagnostics = [];
30231
+ let lastGlobalInstructionError;
30232
+ const refreshGlobalInstructions = async () => {
30233
+ if (globalInstructions === void 0) return;
30234
+ try {
30235
+ await globalInstructions.refresh();
30236
+ lastGlobalInstructionError = void 0;
30237
+ } catch (error) {
30238
+ const diagnostic = `Global instructions could not be refreshed: ${message(error)}`;
30239
+ if (diagnostic !== lastGlobalInstructionError) diagnostics.push(diagnostic);
30240
+ lastGlobalInstructionError = diagnostic;
30241
+ }
30242
+ };
30037
30243
  const permissionPolicy = await loadPermissionPolicy({
30038
30244
  workspace,
30039
30245
  home,
@@ -30137,8 +30343,8 @@ async function createProductionRuntime(options) {
30137
30343
  const registeredProviders = registration.registered;
30138
30344
  let effectiveLlm = registration.effectiveLlm;
30139
30345
  const pluginHost = new PluginHost({
30140
- globalPluginDirs: [join38(home, ".flavor-code", "plugins")],
30141
- projectPluginDirs: [join38(workspace, ".flavor", "plugins")],
30346
+ globalPluginDirs: [join39(home, ".flavor-code", "plugins")],
30347
+ projectPluginDirs: [join39(workspace, ".flavor", "plugins")],
30142
30348
  config,
30143
30349
  // Existing project plugins (including astgraph and superharness) use Node.js
30144
30350
  // built-ins. The Worker/vm sandbox deliberately blocks those imports, so it
@@ -30247,7 +30453,7 @@ async function createProductionRuntime(options) {
30247
30453
  const address = palSocketAddress({
30248
30454
  platform: process.platform,
30249
30455
  userScope: home,
30250
- runtimeDir: join38(tmpdir4(), `flavor-code-pals-${createHash22("sha256").update(home).digest("hex").slice(0, 16)}`)
30456
+ runtimeDir: join39(tmpdir4(), `flavor-code-pals-${createHash22("sha256").update(home).digest("hex").slice(0, 16)}`)
30251
30457
  });
30252
30458
  const baseCollaborationClient = collaboration.client ?? collaboration.createClient?.({
30253
30459
  instanceId: collaboration.instanceId,
@@ -30358,8 +30564,8 @@ async function createProductionRuntime(options) {
30358
30564
  diagnostics.push(`MCP servers could not start: ${message(error)}`);
30359
30565
  });
30360
30566
  const skills = new SkillRegistry({
30361
- globalRoots: [join38(home, ".flavor-code", "skills")],
30362
- projectRoots: [join38(workspace, ".flavor", "skills"), ...pluginSkillRoots],
30567
+ globalRoots: [join39(home, ".flavor-code", "skills")],
30568
+ projectRoots: [join39(workspace, ".flavor", "skills"), ...pluginSkillRoots],
30363
30569
  authorizeResource: async () => true,
30364
30570
  disabledNames: config.skills.disabled
30365
30571
  });
@@ -30367,7 +30573,7 @@ async function createProductionRuntime(options) {
30367
30573
  skillsReady.catch(() => void 0);
30368
30574
  tools.push(createSkillTool(skills), createSkillResourceTool(skills));
30369
30575
  if (options.extraTools !== void 0) tools.push(...options.extraTools);
30370
- const flavor = await optionalText2(join38(workspace, "FLAVOR.md"));
30576
+ const flavor = await optionalText2(join39(workspace, "FLAVOR.md"));
30371
30577
  const instructionBaseline = await workspaceInstructions.baseline();
30372
30578
  let memoryContext;
30373
30579
  if (memoryStore !== void 0) {
@@ -30607,6 +30813,7 @@ async function createProductionRuntime(options) {
30607
30813
  ...memoryContext === void 0 ? {} : { memory: memoryContext },
30608
30814
  ...taskState === void 0 ? {} : { taskState },
30609
30815
  userMemory: () => userMemoryContext ?? "",
30816
+ ...globalInstructions === void 0 ? {} : { globalInstructions: () => globalInstructions.content ?? "" },
30610
30817
  ...compactAtChars === void 0 ? {} : { compactAtChars },
30611
30818
  toolOutputChars,
30612
30819
  compaction,
@@ -30644,6 +30851,7 @@ async function createProductionRuntime(options) {
30644
30851
  softLimitFactor: config.maxIterations.softLimitFactor,
30645
30852
  extendIterations: config.maxIterations.extendBy,
30646
30853
  hasActiveProgress,
30854
+ ...globalInstructions === void 0 ? {} : { beforeModelCall: refreshGlobalInstructions },
30647
30855
  afterToolSuccess: async (_tool, paths, _input, _output, context) => workspaceInstructions.discover(paths, context.ownerId ?? context.agent),
30648
30856
  toolJournal: {
30649
30857
  start: (tool, input, retrySafe) => harnessJournal.startTool(tool, input, retrySafe),
@@ -30964,7 +31172,7 @@ async function createProductionRuntime(options) {
30964
31172
  evolveService.toolDefinition(),
30965
31173
  ...mcpTools
30966
31174
  ];
30967
- const loopFlavor = await optionalText2(join38(input.workspace, "FLAVOR.md"));
31175
+ const loopFlavor = await optionalText2(join39(input.workspace, "FLAVOR.md"));
30968
31176
  const loopInstructionBaseline = await loopInstructions.baseline();
30969
31177
  const loopEnvironment = createPromptEnvironment({
30970
31178
  now: /* @__PURE__ */ new Date(),
@@ -31004,6 +31212,7 @@ async function createProductionRuntime(options) {
31004
31212
  ...loopInstructionBaseline === "" ? {} : { workspaceInstructions: loopInstructionBaseline },
31005
31213
  ...memoryContext === void 0 ? {} : { memory: memoryContext },
31006
31214
  userMemory: () => userMemoryContext ?? "",
31215
+ ...globalInstructions === void 0 ? {} : { globalInstructions: () => globalInstructions.content ?? "" },
31007
31216
  ...compactAtChars === void 0 ? {} : { compactAtChars },
31008
31217
  toolOutputChars,
31009
31218
  compaction,
@@ -31037,6 +31246,7 @@ async function createProductionRuntime(options) {
31037
31246
  softLimitFactor: config.maxIterations.softLimitFactor,
31038
31247
  extendIterations: config.maxIterations.extendBy,
31039
31248
  loopMode: true,
31249
+ ...globalInstructions === void 0 ? {} : { beforeModelCall: refreshGlobalInstructions },
31040
31250
  afterToolSuccess: async (_tool, paths, _input, _output, context) => loopInstructions.discover(paths, context.ownerId ?? context.agent),
31041
31251
  toolJournal: {
31042
31252
  start: (tool, input2, retrySafe) => harnessJournal.startTool(tool, input2, retrySafe),
@@ -31422,7 +31632,7 @@ async function createProductionRuntime(options) {
31422
31632
  const root = resolve36(workspace);
31423
31633
  const abs = resolve36(root, path);
31424
31634
  if (abs !== root && !abs.startsWith(root + sep25)) throw new Error(`path outside workspace: ${path}`);
31425
- return readFile29(abs, "utf8");
31635
+ return readFile30(abs, "utf8");
31426
31636
  },
31427
31637
  history: async (path, limit) => fileHistory(workspace, path, limit),
31428
31638
  questions,
@@ -31434,7 +31644,7 @@ async function createProductionRuntime(options) {
31434
31644
  }, query, focus, signal),
31435
31645
  audit: async (toolFilter) => {
31436
31646
  try {
31437
- const raw = await readFile29(auditLogger.path, "utf8");
31647
+ const raw = await readFile30(auditLogger.path, "utf8");
31438
31648
  const lines = raw.trim().split("\n").filter((line) => line.length > 0);
31439
31649
  const entries = lines.map((line) => {
31440
31650
  try {
@@ -31474,7 +31684,7 @@ ${summary2}`;
31474
31684
  doctor: async () => formatDoctorReport(await runDoctor({ workspace, home, environment })),
31475
31685
  usage: async () => {
31476
31686
  try {
31477
- const raw = await readFile29(usageLogPath(), "utf8");
31687
+ const raw = await readFile30(usageLogPath(), "utf8");
31478
31688
  return formatUsageSummary(summarizeUsage(parseUsageEntries(raw)));
31479
31689
  } catch (error) {
31480
31690
  if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") {
@@ -31553,6 +31763,28 @@ ${summary2}`;
31553
31763
  Path: ${memoryStore.path}` : `Path: ${memoryStore.path}
31554
31764
 
31555
31765
  ${renderMemoryDocument(entries)}`;
31766
+ },
31767
+ global: async (command2) => {
31768
+ const path = join39(home, ".flavor-code", "GLOBAL.md");
31769
+ if (globalInstructions === void 0) return `Global instructions are disabled.
31770
+ Path: ${path}`;
31771
+ if (command2.action === "show") {
31772
+ await globalInstructions.refresh();
31773
+ return globalInstructions.content === void 0 ? `No global instructions stored.
31774
+ Path: ${path}` : `Global instructions (enabled)
31775
+ Path: ${path}
31776
+
31777
+ ${globalInstructions.content}`;
31778
+ }
31779
+ if (command2.action === "remember") {
31780
+ const result2 = await globalInstructions.remember(command2.text);
31781
+ return result2 === "added" ? `Remembered global rule.
31782
+ Path: ${path}` : `Global rule already exists.
31783
+ Path: ${path}`;
31784
+ }
31785
+ const result = await globalInstructions.forget(command2.text);
31786
+ return result === "removed" ? `Forgot global rule.
31787
+ Path: ${path}` : result === "ambiguous" ? "Multiple global rules match. Use the full text of one rule." : "No matching global rule found.";
31556
31788
  },
31557
31789
  refreshMemory: refreshMemoryState,
31558
31790
  remember: async (type, text) => {
@@ -31627,7 +31859,7 @@ ${renderMemoryDocument(entries)}`;
31627
31859
  return `Provider "${providerName}" is missing authorizationUrl, tokenUrl, or clientId.`;
31628
31860
  }
31629
31861
  try {
31630
- const tokenStore = createFileTokenStore(join38(home, ".flavor-code", "auth.json"));
31862
+ const tokenStore = createFileTokenStore(join39(home, ".flavor-code", "auth.json"));
31631
31863
  const credentialId = oauthCredentialId(oauthConfig.tokenUrl, oauthConfig.clientId);
31632
31864
  await migrateLegacyOAuthToken(tokenStore, providerName, credentialId);
31633
31865
  const oauth = new OAuthCallbackAuthProvider({
@@ -31675,7 +31907,7 @@ ${renderMemoryDocument(entries)}`;
31675
31907
  }
31676
31908
  },
31677
31909
  async logout() {
31678
- const tokenStore = createFileTokenStore(join38(home, ".flavor-code", "auth.json"));
31910
+ const tokenStore = createFileTokenStore(join39(home, ".flavor-code", "auth.json"));
31679
31911
  const tokens2 = await tokenStore.load();
31680
31912
  const hadCredentials = Object.keys(tokens2).length > 0 || effectiveLlm !== void 0;
31681
31913
  if (!hadCredentials) {
@@ -31986,7 +32218,7 @@ async function workspaceFingerprint(workspace) {
31986
32218
  hash.update(name);
31987
32219
  if (remaining <= 0) continue;
31988
32220
  try {
31989
- const content = await readFile29(path);
32221
+ const content = await readFile30(path);
31990
32222
  const slice = content.subarray(0, remaining);
31991
32223
  hash.update(slice);
31992
32224
  remaining -= slice.length;
@@ -32257,7 +32489,7 @@ async function registerConfiguredAdapters(providers, registry, environment, diag
32257
32489
  const configured = { ...providers };
32258
32490
  if (configured.openai === void 0 && environment.OPENAI_API_KEY) configured.openai = { type: "openai", apiKey: environment.OPENAI_API_KEY };
32259
32491
  if (configured.anthropic === void 0 && environment.ANTHROPIC_API_KEY) configured.anthropic = { type: "anthropic", apiKey: environment.ANTHROPIC_API_KEY };
32260
- const oauthTokenStore = createFileTokenStore(join38(home, ".flavor-code", "auth.json"));
32492
+ const oauthTokenStore = createFileTokenStore(join39(home, ".flavor-code", "auth.json"));
32261
32493
  const registered = [];
32262
32494
  let effectiveLlm;
32263
32495
  for (const [name, provider] of Object.entries(configured)) {
@@ -32566,7 +32798,7 @@ async function generateSleepReview(registry, modelId, prompt, signal) {
32566
32798
  }
32567
32799
  async function optionalText2(path) {
32568
32800
  try {
32569
- return await readFile29(path, "utf8");
32801
+ return await readFile30(path, "utf8");
32570
32802
  } catch (error) {
32571
32803
  if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") return void 0;
32572
32804
  throw error;
@@ -32747,13 +32979,13 @@ function createMemoryTaskId() {
32747
32979
 
32748
32980
  // src/desktop/runtime-controller.ts
32749
32981
  import { createHash as createHash28 } from "crypto";
32750
- import { mkdir as mkdir27, readFile as readFile37, readdir as readdir13, rm as rm15, stat as stat17, writeFile as writeFile17 } from "fs/promises";
32982
+ import { mkdir as mkdir27, readFile as readFile38, readdir as readdir13, rm as rm15, stat as stat17, writeFile as writeFile17 } from "fs/promises";
32751
32983
  import { homedir as homedir5 } from "os";
32752
- import { basename as basename10, join as join48, resolve as resolve39 } from "path";
32984
+ import { basename as basename10, join as join49, resolve as resolve39 } from "path";
32753
32985
 
32754
32986
  // src/d2c/workflow.ts
32755
32987
  import { createHash as createHash23 } from "crypto";
32756
- import { join as join39 } from "path";
32988
+ import { join as join40 } from "path";
32757
32989
  import { z as z44 } from "zod";
32758
32990
 
32759
32991
  // src/d2c/workflow-shared.ts
@@ -32899,7 +33131,7 @@ function reviewsFor(report, previous) {
32899
33131
  function implementationDirectory(report) {
32900
33132
  const source = report.implementation.source;
32901
33133
  if (!/^https?:\/\//i.test(source)) return source;
32902
- return join39("src", "d2c-output", report.task);
33134
+ return join40("src", "d2c-output", report.task);
32903
33135
  }
32904
33136
  function createWorkflow(report, framework) {
32905
33137
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -33023,7 +33255,7 @@ function applyQualityIssueDecision(workflow, issueId2, decision) {
33023
33255
  };
33024
33256
  }
33025
33257
  function workflowPath(workspace, task) {
33026
- return join39(taskDir(workspace, task), "workflow.json");
33258
+ return join40(taskDir(workspace, task), "workflow.json");
33027
33259
  }
33028
33260
  async function readWorkflow(workspace, task) {
33029
33261
  return (await readRecoverableFile(workflowPath(workspace, task), decodeWorkflow))?.value;
@@ -33276,8 +33508,8 @@ function confirmApiMapping(mapping, operationKey, operations) {
33276
33508
  }
33277
33509
 
33278
33510
  // src/d2c/modules.ts
33279
- import { readFile as readFile30 } from "fs/promises";
33280
- import { isAbsolute as isAbsolute18, join as join40, normalize as normalize2, posix, sep as sep26, win32 as win322 } from "path";
33511
+ import { readFile as readFile31 } from "fs/promises";
33512
+ import { isAbsolute as isAbsolute18, join as join41, normalize as normalize2, posix, sep as sep26, win32 as win322 } from "path";
33281
33513
  import { z as z45 } from "zod";
33282
33514
  var ModuleSchema = z45.object({
33283
33515
  id: z45.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/),
@@ -33296,13 +33528,13 @@ function safeSource(path) {
33296
33528
  }
33297
33529
  function d2cOutputDirectory(workspace, task) {
33298
33530
  if (!D2C_TASK_PATTERN.test(task)) throw new Error(`Invalid D2C task: ${task}`);
33299
- return join40(workspace, "src", "d2c-output", task);
33531
+ return join41(workspace, "src", "d2c-output", task);
33300
33532
  }
33301
33533
  async function readD2cModules(workspace, task) {
33302
33534
  const project = d2cOutputDirectory(workspace, task);
33303
33535
  let raw;
33304
33536
  try {
33305
- raw = await readFile30(join40(project, "d2c.modules.json"), "utf8");
33537
+ raw = await readFile31(join41(project, "d2c.modules.json"), "utf8");
33306
33538
  } catch {
33307
33539
  return [{ id: "page", label: task, sourceFiles: ["src/App.vue", "src/App.jsx"], keywords: [task] }];
33308
33540
  }
@@ -33322,8 +33554,8 @@ async function readD2cModules(workspace, task) {
33322
33554
  }
33323
33555
 
33324
33556
  // src/d2c/integration.ts
33325
- import { mkdir as mkdir24, readFile as readFile31, writeFile as writeFile13 } from "fs/promises";
33326
- import { join as join41 } from "path";
33557
+ import { mkdir as mkdir24, readFile as readFile32, writeFile as writeFile13 } from "fs/promises";
33558
+ import { join as join42 } from "path";
33327
33559
  function record2(value) {
33328
33560
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
33329
33561
  }
@@ -33459,19 +33691,19 @@ async def reset_acceptance(request: Request):
33459
33691
  }
33460
33692
  async function generateIntegrationArtifacts(projectDir, document, mappings, options = {}) {
33461
33693
  if (mappings.some((mapping) => mapping.status === "needs-confirmation")) throw new Error("Confirm every uncertain OpenAPI mapping before generating integration code");
33462
- const packagePath = join41(projectDir, "package.json");
33694
+ const packagePath = join42(projectDir, "package.json");
33463
33695
  let pkg;
33464
33696
  try {
33465
- pkg = JSON.parse(await readFile31(packagePath, "utf8"));
33697
+ pkg = JSON.parse(await readFile32(packagePath, "utf8"));
33466
33698
  } catch {
33467
33699
  throw new Error(`D2C integration requires a valid package.json at ${packagePath}`);
33468
33700
  }
33469
33701
  const scripts = options.pythonServer ? { ...record2(pkg.scripts) ?? {} } : { ...record2(pkg.scripts) ?? {}, mock: "node mock/server.mjs" };
33470
33702
  const dependencies2 = options.pythonServer ? { ...record2(pkg.dependencies) ?? {}, axios: "^1.7.9" } : { ...record2(pkg.dependencies) ?? {}, axios: "^1.7.9", express: "^5.2.1" };
33471
33703
  const selected = [...new Set(mappings.map((mapping) => mapping.operationKey))].map((key) => document.operations.find((operation) => operation.id === key)).filter((operation) => operation !== void 0);
33472
- const apiDir = join41(projectDir, "src", "api");
33473
- const mockDir = join41(projectDir, "mock");
33474
- const serverDir = join41(projectDir, "server");
33704
+ const apiDir = join42(projectDir, "src", "api");
33705
+ const mockDir = join42(projectDir, "mock");
33706
+ const serverDir = join42(projectDir, "server");
33475
33707
  await Promise.all([
33476
33708
  mkdir24(apiDir, { recursive: true }),
33477
33709
  ...options.pythonServer ? [] : [mkdir24(mockDir, { recursive: true })],
@@ -33481,23 +33713,23 @@ async function generateIntegrationArtifacts(projectDir, document, mappings, opti
33481
33713
  if (options.pythonServer) files.push("server/main.py", "server/requirements.txt", "server/README.md", "server/flavor-runtime.json");
33482
33714
  else files.push("mock/server.mjs");
33483
33715
  await Promise.all([
33484
- writeFile13(join41(apiDir, "http.js"), `import axios from "axios";
33716
+ writeFile13(join42(apiDir, "http.js"), `import axios from "axios";
33485
33717
 
33486
33718
  export const http = axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL || ${JSON.stringify(document.baseUrl ?? "")}, timeout: 15000 });
33487
33719
 
33488
33720
  http.interceptors.response.use((response) => response, (error) => Promise.reject({ name: "ApiError", status: error.response?.status, message: error.response?.data?.message || error.message, cause: error }));
33489
33721
  `),
33490
- writeFile13(join41(apiDir, "d2c-api.js"), apiModule(selected)),
33491
- writeFile13(join41(apiDir, "d2c-bindings.json"), `${JSON.stringify({ schema: 1, generatedAt: (/* @__PURE__ */ new Date()).toISOString(), mappings }, null, 2)}
33722
+ writeFile13(join42(apiDir, "d2c-api.js"), apiModule(selected)),
33723
+ writeFile13(join42(apiDir, "d2c-bindings.json"), `${JSON.stringify({ schema: 1, generatedAt: (/* @__PURE__ */ new Date()).toISOString(), mappings }, null, 2)}
33492
33724
  `),
33493
- ...options.pythonServer ? [] : [writeFile13(join41(mockDir, "server.mjs"), mockModule({ ...document, operations: selected }))],
33725
+ ...options.pythonServer ? [] : [writeFile13(join42(mockDir, "server.mjs"), mockModule({ ...document, operations: selected }))],
33494
33726
  writeFile13(packagePath, `${JSON.stringify({ ...pkg, scripts, dependencies: dependencies2 }, null, 2)}
33495
33727
  `),
33496
33728
  ...options.pythonServer ? [
33497
- writeFile13(join41(serverDir, "main.py"), pythonServerModule({ ...document, operations: selected })),
33498
- writeFile13(join41(serverDir, "requirements.txt"), "fastapi>=0.116,<1\nuvicorn[standard]>=0.35,<1\nsqlalchemy>=2.0,<3\n"),
33499
- writeFile13(join41(serverDir, "README.md"), "# Python API service\n\nThis is the real integration service, not a mock. Development defaults to SQLite through `DATABASE_URL=sqlite:///./data/app.db`. Set the same environment variable to a SQLAlchemy MySQL or PostgreSQL URL for deployment; keep schema changes in migrations instead of database-specific SQL. Flavor Code starts the service automatically for acceptance.\n"),
33500
- writeFile13(join41(serverDir, "flavor-runtime.json"), `${JSON.stringify({
33729
+ writeFile13(join42(serverDir, "main.py"), pythonServerModule({ ...document, operations: selected })),
33730
+ writeFile13(join42(serverDir, "requirements.txt"), "fastapi>=0.116,<1\nuvicorn[standard]>=0.35,<1\nsqlalchemy>=2.0,<3\n"),
33731
+ writeFile13(join42(serverDir, "README.md"), "# Python API service\n\nThis is the real integration service, not a mock. Development defaults to SQLite through `DATABASE_URL=sqlite:///./data/app.db`. Set the same environment variable to a SQLAlchemy MySQL or PostgreSQL URL for deployment; keep schema changes in migrations instead of database-specific SQL. Flavor Code starts the service automatically for acceptance.\n"),
33732
+ writeFile13(join42(serverDir, "flavor-runtime.json"), `${JSON.stringify({
33501
33733
  schema: 1,
33502
33734
  kind: "python-fastapi",
33503
33735
  cwd: "server",
@@ -33516,9 +33748,9 @@ http.interceptors.response.use((response) => response, (error) => Promise.reject
33516
33748
 
33517
33749
  // src/d2c/mock-runner.ts
33518
33750
  import { spawn as spawn9 } from "child_process";
33519
- import { access as access4, readFile as readFile32 } from "fs/promises";
33751
+ import { access as access4, readFile as readFile33 } from "fs/promises";
33520
33752
  import { createServer as createServer5 } from "net";
33521
- import { join as join42 } from "path";
33753
+ import { join as join43 } from "path";
33522
33754
  async function exists3(path) {
33523
33755
  try {
33524
33756
  await access4(path);
@@ -33626,7 +33858,7 @@ ${output.trim()}` : ""}`);
33626
33858
  }
33627
33859
  }
33628
33860
  async function runPythonBackend(projectDir, options) {
33629
- const serverDir = join42(projectDir, "server");
33861
+ const serverDir = join43(projectDir, "server");
33630
33862
  if (options.installDependencies !== false) await installPython(serverDir, options.signal);
33631
33863
  const selectedPort = await port();
33632
33864
  const url = `http://127.0.0.1:${selectedPort}`;
@@ -33672,20 +33904,20 @@ ${managed.output().slice(-4e3)}`);
33672
33904
  };
33673
33905
  }
33674
33906
  async function runD2cMockServer(projectDir, options = {}) {
33675
- const runtimePath = join42(projectDir, "server", "flavor-runtime.json");
33907
+ const runtimePath = join43(projectDir, "server", "flavor-runtime.json");
33676
33908
  if (await exists3(runtimePath)) {
33677
33909
  let runtime;
33678
33910
  try {
33679
- runtime = JSON.parse(await readFile32(runtimePath, "utf8"));
33911
+ runtime = JSON.parse(await readFile33(runtimePath, "utf8"));
33680
33912
  } catch {
33681
33913
  throw new Error(`E2E backend runtime manifest is invalid at ${runtimePath}`);
33682
33914
  }
33683
33915
  if (runtime.schema !== 1 || runtime.kind !== "python-fastapi") throw new Error("Unsupported E2E backend runtime manifest");
33684
33916
  return runPythonBackend(projectDir, options);
33685
33917
  }
33686
- const serverPath = join42(projectDir, "mock", "server.mjs");
33918
+ const serverPath = join43(projectDir, "mock", "server.mjs");
33687
33919
  if (!await exists3(serverPath)) throw new Error(`D2C mock server is not generated at ${serverPath}`);
33688
- if (options.installDependencies === false && !await exists3(join42(projectDir, "node_modules", "express", "package.json"))) {
33920
+ if (options.installDependencies === false && !await exists3(join43(projectDir, "node_modules", "express", "package.json"))) {
33689
33921
  throw new Error("D2C mock server dependencies are not installed");
33690
33922
  }
33691
33923
  if (options.installDependencies !== false) await install(projectDir, options.signal);
@@ -33727,13 +33959,13 @@ ${managed.output().slice(-4e3)}`);
33727
33959
 
33728
33960
  // src/d2c/product.ts
33729
33961
  import { createHash as createHash25, randomUUID as randomUUID27 } from "crypto";
33730
- import { copyFile as copyFile2, mkdir as mkdir25, readFile as readFile34, rename as rename13, stat as stat15, unlink as unlink7, writeFile as writeFile15 } from "fs/promises";
33731
- import { join as join43 } from "path";
33962
+ import { copyFile as copyFile2, mkdir as mkdir25, readFile as readFile35, rename as rename13, stat as stat15, unlink as unlink7, writeFile as writeFile15 } from "fs/promises";
33963
+ import { join as join44 } from "path";
33732
33964
  import { z as z46 } from "zod";
33733
33965
 
33734
33966
  // src/e2e/prd-governance.ts
33735
33967
  import { createHash as createHash24, randomUUID as randomUUID26 } from "crypto";
33736
- import { readFile as readFile33, rename as rename12, rm as rm13, writeFile as writeFile14 } from "fs/promises";
33968
+ import { readFile as readFile34, rename as rename12, rm as rm13, writeFile as writeFile14 } from "fs/promises";
33737
33969
  function hashPrd(markdown) {
33738
33970
  return createHash24("sha256").update(markdown, "utf8").digest("hex");
33739
33971
  }
@@ -33810,14 +34042,14 @@ function approvePrd(markdown, now = /* @__PURE__ */ new Date()) {
33810
34042
  return { hash: hashPrd(markdown), approvedAt: now.toISOString(), criteria };
33811
34043
  }
33812
34044
  async function assertApprovedPrd(path, approved) {
33813
- const current = await readFile33(path, "utf8");
34045
+ const current = await readFile34(path, "utf8");
33814
34046
  if (hashPrd(current) !== approved.hash) {
33815
34047
  throw new Error("PRD_LOCK_VIOLATION: confirmed PRD content changed after approval");
33816
34048
  }
33817
34049
  return approved;
33818
34050
  }
33819
34051
  async function updatePrdSectionFile(path, input) {
33820
- const current = await readFile33(path, "utf8");
34052
+ const current = await readFile34(path, "utf8");
33821
34053
  if (hashPrd(current) !== input.expectedHash) {
33822
34054
  throw new Error("PRD_EDIT_CONFLICT: the PRD changed; refresh before saving this section");
33823
34055
  }
@@ -33883,13 +34115,13 @@ var PlanSchema2 = z46.object({
33883
34115
  }).strict();
33884
34116
  function d2cProductDirectory(workspace, task) {
33885
34117
  if (!D2C_TASK_PATTERN.test(task)) throw new Error("Invalid D2C task name");
33886
- return join43(taskDir(workspace, task), "product");
34118
+ return join44(taskDir(workspace, task), "product");
33887
34119
  }
33888
34120
  function d2cProductPrototypeDirectory(workspace, task) {
33889
- return join43(d2cProductDirectory(workspace, task), "prototype");
34121
+ return join44(d2cProductDirectory(workspace, task), "prototype");
33890
34122
  }
33891
34123
  function planPath(workspace, task) {
33892
- return join43(taskDir(workspace, task), "product-plan.json");
34124
+ return join44(taskDir(workspace, task), "product-plan.json");
33893
34125
  }
33894
34126
  function resolveD2cProductTechnology(requirement, fallbackFramework = "vue") {
33895
34127
  const frontendCandidates = [
@@ -33981,14 +34213,14 @@ async function fileTimestamp(path) {
33981
34213
  return info?.isFile() ? info.mtime.toISOString() : void 0;
33982
34214
  }
33983
34215
  async function fileHash(path) {
33984
- const buffer = await readFile34(path).catch((error) => {
34216
+ const buffer = await readFile35(path).catch((error) => {
33985
34217
  if (error.code === "ENOENT") return void 0;
33986
34218
  throw error;
33987
34219
  });
33988
34220
  return buffer === void 0 ? void 0 : createHash25("sha256").update(buffer).digest("hex");
33989
34221
  }
33990
34222
  async function validateAndNormalizePrototypeManifest(path) {
33991
- const raw = await readFile34(path, "utf8").catch((error) => {
34223
+ const raw = await readFile35(path, "utf8").catch((error) => {
33992
34224
  if (error.code === "ENOENT") return void 0;
33993
34225
  throw error;
33994
34226
  });
@@ -34064,7 +34296,7 @@ async function createD2cProductPlan(workspace, input, now = () => /* @__PURE__ *
34064
34296
  });
34065
34297
  }
34066
34298
  async function readD2cProductPlan(workspace, task) {
34067
- const raw = await readFile34(planPath(workspace, task), "utf8").catch((error) => {
34299
+ const raw = await readFile35(planPath(workspace, task), "utf8").catch((error) => {
34068
34300
  if (error.code === "ENOENT") return void 0;
34069
34301
  throw error;
34070
34302
  });
@@ -34072,16 +34304,16 @@ async function readD2cProductPlan(workspace, task) {
34072
34304
  }
34073
34305
  async function discoverD2cProductArtifacts(workspace, plan) {
34074
34306
  const product = d2cProductDirectory(workspace, plan.task);
34075
- const prdUpdatedAt = await fileTimestamp(join43(product, "prd.md"));
34076
- const prdHash = await fileHash(join43(product, "prd.md"));
34077
- const prototypeUpdatedAt = await fileTimestamp(join43(product, "prototype", "index.html"));
34078
- const prototypeHash = await fileHash(join43(product, "prototype", "index.html"));
34079
- const expectedManifest = join43(product, "prototype", "interaction-manifest.json");
34307
+ const prdUpdatedAt = await fileTimestamp(join44(product, "prd.md"));
34308
+ const prdHash = await fileHash(join44(product, "prd.md"));
34309
+ const prototypeUpdatedAt = await fileTimestamp(join44(product, "prototype", "index.html"));
34310
+ const prototypeHash = await fileHash(join44(product, "prototype", "index.html"));
34311
+ const expectedManifest = join44(product, "prototype", "interaction-manifest.json");
34080
34312
  let manifestUpdatedAt = await fileTimestamp(expectedManifest);
34081
34313
  if (manifestUpdatedAt === void 0) {
34082
- const misplacedManifest = join43(product, "interaction-manifest.json");
34314
+ const misplacedManifest = join44(product, "interaction-manifest.json");
34083
34315
  if (await fileTimestamp(misplacedManifest) !== void 0) {
34084
- await mkdir25(join43(product, "prototype"), { recursive: true });
34316
+ await mkdir25(join44(product, "prototype"), { recursive: true });
34085
34317
  await copyFile2(misplacedManifest, expectedManifest);
34086
34318
  manifestUpdatedAt = await fileTimestamp(expectedManifest);
34087
34319
  }
@@ -34114,8 +34346,8 @@ async function readD2cProductPlanView(workspace, task) {
34114
34346
  if (stored === void 0) return void 0;
34115
34347
  const plan = await discoverD2cProductArtifacts(workspace, stored);
34116
34348
  if (plan.revision !== stored.revision) await writeD2cProductPlan(workspace, plan);
34117
- const prdMarkdown = plan.prd === void 0 ? void 0 : await readFile34(join43(taskDir(workspace, task), plan.prd.path), "utf8");
34118
- const manifestPath = join43(d2cProductDirectory(workspace, task), "prototype", "interaction-manifest.json");
34349
+ const prdMarkdown = plan.prd === void 0 ? void 0 : await readFile35(join44(taskDir(workspace, task), plan.prd.path), "utf8");
34350
+ const manifestPath = join44(d2cProductDirectory(workspace, task), "prototype", "interaction-manifest.json");
34119
34351
  const validationError = plan.phase === "design-generating" && await fileTimestamp(manifestPath) !== void 0 ? await validateAndNormalizePrototypeManifest(manifestPath) : void 0;
34120
34352
  return {
34121
34353
  plan,
@@ -34205,9 +34437,9 @@ ${prdMarkdown.slice(0, 2e4)}`
34205
34437
 
34206
34438
  // src/e2e/acceptance-baseline.ts
34207
34439
  import { createHash as createHash26 } from "crypto";
34208
- import { readFile as readFile35 } from "fs/promises";
34440
+ import { readFile as readFile36 } from "fs/promises";
34209
34441
  async function fileArtifact(path) {
34210
- const content = await readFile35(path);
34442
+ const content = await readFile36(path);
34211
34443
  return { path, hash: createHash26("sha256").update(content).digest("hex"), bytes: content.byteLength };
34212
34444
  }
34213
34445
  async function verifyAcceptanceArtifact(path, expected, label) {
@@ -34274,7 +34506,7 @@ function createAcceptanceEvidence(coverage, run, now = /* @__PURE__ */ new Date(
34274
34506
 
34275
34507
  // src/e2e/delivery-run.ts
34276
34508
  import { createHash as createHash27 } from "crypto";
34277
- import { join as join44 } from "path";
34509
+ import { join as join45 } from "path";
34278
34510
  import { z as z47 } from "zod";
34279
34511
  var DELIVERY_NODE_IDS = ["requirement", "prd", "design", "d2c", "api", "acceptance", "delivery"];
34280
34512
  var ArtifactSchema = z47.object({
@@ -34391,7 +34623,7 @@ function completeDeliveryNode(run, node, outputs, now = /* @__PURE__ */ new Date
34391
34623
  return { ...run, nodes, updatedAt: timestamp2 };
34392
34624
  }
34393
34625
  function deliveryRunPath(workspace, task) {
34394
- return join44(taskDir(workspace, task), "delivery-run.json");
34626
+ return join45(taskDir(workspace, task), "delivery-run.json");
34395
34627
  }
34396
34628
  function decodeDeliveryRun(raw) {
34397
34629
  return DeliveryRunSchema.parse(JSON.parse(raw));
@@ -34432,9 +34664,9 @@ async function updateDeliveryRun(workspace, task, expectedRevision, mutation) {
34432
34664
  }
34433
34665
 
34434
34666
  // src/skills/manager.ts
34435
- import { lstat as lstat14, mkdir as mkdir26, readFile as readFile36, readdir as readdir12, realpath as realpath13, rm as rm14, writeFile as writeFile16 } from "fs/promises";
34667
+ import { lstat as lstat15, mkdir as mkdir26, readFile as readFile37, readdir as readdir12, realpath as realpath13, rm as rm14, writeFile as writeFile16 } from "fs/promises";
34436
34668
  import { homedir as homedir4 } from "os";
34437
- import { isAbsolute as isAbsolute19, join as join45, relative as relative26, resolve as resolve37, sep as sep27 } from "path";
34669
+ import { isAbsolute as isAbsolute19, join as join46, relative as relative26, resolve as resolve37, sep as sep27 } from "path";
34438
34670
  import { stringify } from "yaml";
34439
34671
  var SkillManager = class {
34440
34672
  #workspace;
@@ -34457,7 +34689,7 @@ var SkillManager = class {
34457
34689
  async create(draft) {
34458
34690
  const value = parseDraft(draft);
34459
34691
  const projectRoot = this.#projectRoot();
34460
- const directory = join45(projectRoot, value.name);
34692
+ const directory = join46(projectRoot, value.name);
34461
34693
  await mkdir26(projectRoot, { recursive: true, mode: 448 });
34462
34694
  try {
34463
34695
  await mkdir26(directory, { mode: 448 });
@@ -34466,7 +34698,7 @@ var SkillManager = class {
34466
34698
  throw error;
34467
34699
  }
34468
34700
  try {
34469
- await writeFile16(join45(directory, "SKILL.md"), renderSkill(value), { encoding: "utf8", flag: "wx", mode: 384 });
34701
+ await writeFile16(join46(directory, "SKILL.md"), renderSkill(value), { encoding: "utf8", flag: "wx", mode: 384 });
34470
34702
  await this.#persistEnabled(value.name, true);
34471
34703
  return this.get(value.name);
34472
34704
  } catch (error) {
@@ -34479,13 +34711,13 @@ var SkillManager = class {
34479
34711
  const value = parseDraft(draft);
34480
34712
  if (value.name !== validName) throw new Error("Rename is not supported; create a new skill instead");
34481
34713
  const directory = this.#projectSkillDirectory(validName);
34482
- const info = await lstat14(directory).catch((error) => {
34714
+ const info = await lstat15(directory).catch((error) => {
34483
34715
  if (isCode11(error, "ENOENT")) throw new Error(`Project skill not found: ${validName}`);
34484
34716
  throw error;
34485
34717
  });
34486
34718
  if (!info.isDirectory() || info.isSymbolicLink()) throw new Error(`Project skill path is not a real directory: ${validName}`);
34487
- const skillFile = join45(directory, "SKILL.md");
34488
- const fileInfo = await lstat14(skillFile);
34719
+ const skillFile = join46(directory, "SKILL.md");
34720
+ const fileInfo = await lstat15(skillFile);
34489
34721
  if (!fileInfo.isFile() || fileInfo.isSymbolicLink()) throw new Error(`Project skill file is not a real file: ${validName}`);
34490
34722
  await writeFile16(skillFile, renderSkill(value), { encoding: "utf8", mode: 384 });
34491
34723
  return this.get(validName);
@@ -34493,7 +34725,7 @@ var SkillManager = class {
34493
34725
  async delete(name) {
34494
34726
  const validName = parseName(name);
34495
34727
  const directory = this.#projectSkillDirectory(validName);
34496
- const info = await lstat14(directory).catch((error) => {
34728
+ const info = await lstat15(directory).catch((error) => {
34497
34729
  if (isCode11(error, "ENOENT")) throw new Error(`Project skill not found: ${validName}`);
34498
34730
  throw error;
34499
34731
  });
@@ -34516,12 +34748,12 @@ var SkillManager = class {
34516
34748
  async #registry() {
34517
34749
  const loaded = await loadConfig({ cwd: this.#workspace, home: this.#home });
34518
34750
  const [globalPluginRoots, projectPluginRoots] = await Promise.all([
34519
- discoverPluginSkillRoots(join45(this.#home, ".flavor-code", "plugins")),
34520
- discoverPluginSkillRoots(join45(this.#workspace, ".flavor", "plugins"))
34751
+ discoverPluginSkillRoots(join46(this.#home, ".flavor-code", "plugins")),
34752
+ discoverPluginSkillRoots(join46(this.#workspace, ".flavor", "plugins"))
34521
34753
  ]);
34522
34754
  return {
34523
34755
  registry: new SkillRegistry({
34524
- globalRoots: [join45(this.#home, ".flavor-code", "skills"), ...globalPluginRoots],
34756
+ globalRoots: [join46(this.#home, ".flavor-code", "skills"), ...globalPluginRoots],
34525
34757
  projectRoots: [this.#projectRoot(), ...projectPluginRoots]
34526
34758
  }),
34527
34759
  disabled: new Set(loaded.config.skills.disabled)
@@ -34537,10 +34769,10 @@ var SkillManager = class {
34537
34769
  };
34538
34770
  }
34539
34771
  #projectRoot() {
34540
- return join45(this.#workspace, ".flavor", "skills");
34772
+ return join46(this.#workspace, ".flavor", "skills");
34541
34773
  }
34542
34774
  #projectSkillDirectory(name) {
34543
- return join45(this.#projectRoot(), name);
34775
+ return join46(this.#projectRoot(), name);
34544
34776
  }
34545
34777
  };
34546
34778
  function parseName(value) {
@@ -34581,12 +34813,12 @@ async function discoverPluginSkillRoots(directory) {
34581
34813
  const roots = [];
34582
34814
  for (const entry2 of entries) {
34583
34815
  if (!entry2.isDirectory() || entry2.isSymbolicLink()) continue;
34584
- const pluginRoot = await realpath13(join45(directory, entry2.name));
34816
+ const pluginRoot = await realpath13(join46(directory, entry2.name));
34585
34817
  try {
34586
- const manifest = PluginManifestSchema.parse(JSON.parse(await readFile36(join45(pluginRoot, "flavor-plugin.json"), "utf8")));
34818
+ const manifest = PluginManifestSchema.parse(JSON.parse(await readFile37(join46(pluginRoot, "flavor-plugin.json"), "utf8")));
34587
34819
  for (const contribution of manifest.contributes.skillRoots) {
34588
34820
  const candidate = resolve37(pluginRoot, contribution.path);
34589
- const info = await lstat14(candidate);
34821
+ const info = await lstat15(candidate);
34590
34822
  if (!info.isDirectory() || info.isSymbolicLink()) continue;
34591
34823
  const physical = await realpath13(candidate);
34592
34824
  const relation = relative26(pluginRoot, physical);
@@ -34658,11 +34890,11 @@ async function createProjectMemoryManager(options) {
34658
34890
  }
34659
34891
 
34660
34892
  // src/mcp/config-manager.ts
34661
- import { join as join46 } from "path";
34893
+ import { join as join47 } from "path";
34662
34894
  var ProjectMcpConfigManager = class {
34663
34895
  path;
34664
34896
  constructor(workspace) {
34665
- this.path = join46(workspace, ".flavor", "flavor.json");
34897
+ this.path = join47(workspace, ".flavor", "flavor.json");
34666
34898
  }
34667
34899
  async list() {
34668
34900
  const document = await readRecoverableFile(this.path, (raw) => parseDocument2(this.path, raw));
@@ -34759,7 +34991,7 @@ function isRecord7(value) {
34759
34991
  }
34760
34992
 
34761
34993
  // src/desktop/model-config.ts
34762
- import { join as join47 } from "path";
34994
+ import { join as join48 } from "path";
34763
34995
  var DEFAULT_DESKTOP_MODELS = [
34764
34996
  {
34765
34997
  id: "anthropic:deepseek-v4-pro",
@@ -34780,7 +35012,7 @@ var DEFAULT_DESKTOP_MODELS = [
34780
35012
  ];
34781
35013
  async function loadDesktopModels(workspace, home) {
34782
35014
  try {
34783
- const tokens2 = await createFileTokenStore(join47(home, ".flavor-code", "auth.json")).load();
35015
+ const tokens2 = await createFileTokenStore(join48(home, ".flavor-code", "auth.json")).load();
34784
35016
  const pkceModels = [];
34785
35017
  const knownPkce = /* @__PURE__ */ new Set();
34786
35018
  for (const token of Object.values(tokens2)) {
@@ -34925,33 +35157,33 @@ function pngDataUrl(png) {
34925
35157
  }
34926
35158
  async function detectD2cFramework(workspace, task) {
34927
35159
  try {
34928
- const pkg = JSON.parse(await readFile37(join48(d2cOutputDirectory(workspace, task), "package.json"), "utf8"));
35160
+ const pkg = JSON.parse(await readFile38(join49(d2cOutputDirectory(workspace, task), "package.json"), "utf8"));
34929
35161
  return pkg.dependencies?.react === void 0 ? "vue" : "react";
34930
35162
  } catch {
34931
35163
  return "vue";
34932
35164
  }
34933
35165
  }
34934
35166
  function integrationDirectory(workspace, task) {
34935
- return join48(workspace, ".flavor", "d2c", task, "integration");
35167
+ return join49(workspace, ".flavor", "d2c", task, "integration");
34936
35168
  }
34937
35169
  var D2C_BACKEND_SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([".cfg", ".env", ".ini", ".js", ".json", ".lock", ".mjs", ".py", ".toml", ".txt", ".yaml", ".yml"]);
34938
35170
  var D2C_BACKEND_IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([".git", ".venv", "__pycache__", "data", "node_modules", "venv"]);
34939
35171
  async function d2cBackendSourceFingerprint(project) {
34940
- const runtimeManifest = join48(project, "server", "flavor-runtime.json");
35172
+ const runtimeManifest = join49(project, "server", "flavor-runtime.json");
34941
35173
  const realBackend = (await stat17(runtimeManifest).catch(() => void 0))?.isFile() === true;
34942
- const root = realBackend ? join48(project, "server") : join48(project, "mock");
35174
+ const root = realBackend ? join49(project, "server") : join49(project, "mock");
34943
35175
  if ((await stat17(root).catch(() => void 0))?.isDirectory() !== true) return void 0;
34944
35176
  const files = [];
34945
35177
  const visit = async (directory) => {
34946
35178
  const entries = await readdir13(directory, { withFileTypes: true });
34947
35179
  for (const entry2 of entries) {
34948
35180
  if (entry2.isDirectory()) {
34949
- if (!D2C_BACKEND_IGNORED_DIRECTORIES.has(entry2.name)) await visit(join48(directory, entry2.name));
35181
+ if (!D2C_BACKEND_IGNORED_DIRECTORIES.has(entry2.name)) await visit(join49(directory, entry2.name));
34950
35182
  continue;
34951
35183
  }
34952
35184
  if (!entry2.isFile()) continue;
34953
35185
  const extension2 = entry2.name.includes(".") ? `.${entry2.name.split(".").pop().toLowerCase()}` : "";
34954
- if (D2C_BACKEND_SOURCE_EXTENSIONS.has(extension2)) files.push(join48(directory, entry2.name));
35186
+ if (D2C_BACKEND_SOURCE_EXTENSIONS.has(extension2)) files.push(join49(directory, entry2.name));
34955
35187
  }
34956
35188
  };
34957
35189
  await visit(root);
@@ -34959,7 +35191,7 @@ async function d2cBackendSourceFingerprint(project) {
34959
35191
  for (const path of files.sort()) {
34960
35192
  hash.update(path.slice(root.length));
34961
35193
  hash.update("\0");
34962
- hash.update(await readFile37(path));
35194
+ hash.update(await readFile38(path));
34963
35195
  hash.update("\0");
34964
35196
  }
34965
35197
  return hash.digest("hex");
@@ -35507,7 +35739,7 @@ var DesktopRuntimeController = class {
35507
35739
  if (workflow === void 0 || !reviewProgress(workflow).complete || workflow.stage === "visual-review") {
35508
35740
  throw new Error("Complete visual review before importing OpenAPI");
35509
35741
  }
35510
- const moduleManifest = join48(d2cOutputDirectory(workspace, task), "d2c.modules.json");
35742
+ const moduleManifest = join49(d2cOutputDirectory(workspace, task), "d2c.modules.json");
35511
35743
  const moduleManifestInfo = await stat17(moduleManifest).catch(() => void 0);
35512
35744
  if (moduleManifestInfo === void 0 || !moduleManifestInfo.isFile()) {
35513
35745
  throw new Error("D2C implementation must write d2c.modules.json before API mapping; ask the agent to split the project into modules first");
@@ -35520,8 +35752,8 @@ var DesktopRuntimeController = class {
35520
35752
  await mkdir27(dir, { recursive: true });
35521
35753
  const hash = createHash28("sha256").update(raw).digest("hex");
35522
35754
  await Promise.all([
35523
- writeFile17(join48(dir, "swagger.json"), raw),
35524
- writeFile17(join48(dir, "openapi.normalized.json"), `${JSON.stringify(document, null, 2)}
35755
+ writeFile17(join49(dir, "swagger.json"), raw),
35756
+ writeFile17(join49(dir, "openapi.normalized.json"), `${JSON.stringify(document, null, 2)}
35525
35757
  `)
35526
35758
  ]);
35527
35759
  const next = {
@@ -35547,7 +35779,7 @@ var DesktopRuntimeController = class {
35547
35779
  await this.#assertProductPrdLocked(workspace, task);
35548
35780
  const info = await stat17(sourcePath);
35549
35781
  if (!info.isFile() || info.size > 8 * 1024 * 1024) throw new Error("OpenAPI JSON must be a file no larger than 8 MiB");
35550
- return this.#storeD2cOpenApi(workspace, task, basename10(sourcePath), await readFile37(sourcePath, "utf8"), false);
35782
+ return this.#storeD2cOpenApi(workspace, task, basename10(sourcePath), await readFile38(sourcePath, "utf8"), false);
35551
35783
  }
35552
35784
  async getD2cIntegration(task) {
35553
35785
  const workspace = this.#workspace;
@@ -35556,8 +35788,8 @@ var DesktopRuntimeController = class {
35556
35788
  let workflow = await readWorkflow(workspace, task);
35557
35789
  if (workflow?.openapi === void 0 && workflow !== void 0 && reviewProgress(workflow).complete && await readD2cProductPlanView(workspace, task) !== void 0) {
35558
35790
  const modules = await readD2cModules(workspace, task);
35559
- const productOpenApiPath = join48(taskDir(workspace, task), "product", "openapi.json");
35560
- const authored = await readFile37(productOpenApiPath, "utf8").catch(() => void 0);
35791
+ const productOpenApiPath = join49(taskDir(workspace, task), "product", "openapi.json");
35792
+ const authored = await readFile38(productOpenApiPath, "utf8").catch(() => void 0);
35561
35793
  const generated = authored === void 0;
35562
35794
  const prepared = await this.#storeD2cOpenApi(
35563
35795
  workspace,
@@ -35569,7 +35801,7 @@ var DesktopRuntimeController = class {
35569
35801
  return prepared;
35570
35802
  }
35571
35803
  if (workflow?.openapi === void 0 || workflow.mappings === void 0) return void 0;
35572
- const document = JSON.parse(await readFile37(join48(integrationDirectory(workspace, task), "openapi.normalized.json"), "utf8"));
35804
+ const document = JSON.parse(await readFile38(join49(integrationDirectory(workspace, task), "openapi.normalized.json"), "utf8"));
35573
35805
  return { workflow, document, mappings: workflow.mappings };
35574
35806
  }
35575
35807
  async confirmD2cMapping(task, moduleId, operationKey) {
@@ -35636,7 +35868,7 @@ var DesktopRuntimeController = class {
35636
35868
  ].join("\n");
35637
35869
  const delivery = await readDeliveryRun(workspace, task);
35638
35870
  if (delivery?.nodes.api.status === "running") {
35639
- const apiArtifacts = await Promise.all(generated.files.map(async (file) => artifactRef(file, await readFile37(join48(project, file)))));
35871
+ const apiArtifacts = await Promise.all(generated.files.map(async (file) => artifactRef(file, await readFile38(join49(project, file)))));
35640
35872
  const completed = await updateDeliveryRun(
35641
35873
  workspace,
35642
35874
  task,
@@ -35663,8 +35895,8 @@ var DesktopRuntimeController = class {
35663
35895
  try {
35664
35896
  const fingerprint2 = await d2cBackendSourceFingerprint(project);
35665
35897
  if (fingerprint2 !== void 0) this.#d2cMockSourceFingerprints.set(task, fingerprint2);
35666
- const environmentPath = join48(project, ".env.local");
35667
- const existingEnvironment = await readFile37(environmentPath, "utf8").catch(() => "");
35898
+ const environmentPath = join49(project, ".env.local");
35899
+ const existingEnvironment = await readFile38(environmentPath, "utf8").catch(() => "");
35668
35900
  const environment = `${existingEnvironment.split(/\r?\n/).filter((line) => !/^\s*VITE_API_BASE_URL\s*=/.test(line)).join("\n").trim()}
35669
35901
  VITE_API_BASE_URL=${running.url}
35670
35902
  `.replace(/^\n/, "");
@@ -35744,8 +35976,8 @@ VITE_API_BASE_URL=${running.url}
35744
35976
  const readyPreview = this.#d2cPreviews.get(task);
35745
35977
  const readyMock = this.#d2cMocks.get(task);
35746
35978
  if (readyPreview === void 0 || readyMock === void 0) throw new Error("D2C preview or backend service is unavailable after synchronization");
35747
- const manifestPath = join48(taskDir(workspace, task), "design", "interaction-manifest.json");
35748
- const manifest = parseInteractionManifest(await readFile37(manifestPath, "utf8"));
35979
+ const manifestPath = join49(taskDir(workspace, task), "design", "interaction-manifest.json");
35980
+ const manifest = parseInteractionManifest(await readFile38(manifestPath, "utf8"));
35749
35981
  const baseline = await this.#verifyAcceptanceBaseline(workspace, task);
35750
35982
  const coverage = baseline === void 0 ? void 0 : verifyRequirementCoverage(baseline.approvedPrd.criteria, manifest);
35751
35983
  let executableManifest = manifest;
@@ -35756,7 +35988,7 @@ VITE_API_BASE_URL=${running.url}
35756
35988
  await mkdir27(planDir, { recursive: true });
35757
35989
  try {
35758
35990
  const observations = await this.#observeD2cPages(manifest, readyPreview.url);
35759
- const prd = await readFile37(join48(taskDir(workspace, task), "product", "prd.md"), "utf8").catch(() => void 0);
35991
+ const prd = await readFile38(join49(taskDir(workspace, task), "product", "prd.md"), "utf8").catch(() => void 0);
35760
35992
  const apiContext = JSON.stringify({ openapi: workflow.openapi, mappings: workflow.mappings }, null, 2);
35761
35993
  const plan = await this.#d2cJudge.planInteractions({ task, seed: manifest, observations, ...prd === void 0 ? {} : { prd }, apiContext });
35762
35994
  executableManifest = mergeInteractionManifests(manifest, plan.manifest);
@@ -35767,9 +35999,9 @@ VITE_API_BASE_URL=${running.url}
35767
35999
  plannedScenarios: executableManifest.pages.reduce((total, page) => total + page.scenarios.length, 0),
35768
36000
  pageAnalyses: plan.pageAnalyses
35769
36001
  };
35770
- await writeFile17(join48(planDir, "autonomous-interaction-plan.json"), `${JSON.stringify({ ...plan, manifest: executableManifest }, null, 2)}
36002
+ await writeFile17(join49(planDir, "autonomous-interaction-plan.json"), `${JSON.stringify({ ...plan, manifest: executableManifest }, null, 2)}
35771
36003
  `, { encoding: "utf8", mode: 384 });
35772
- await rm15(join48(planDir, "autonomous-interaction-diagnostic.json"), { force: true });
36004
+ await rm15(join49(planDir, "autonomous-interaction-diagnostic.json"), { force: true });
35773
36005
  } catch (cause) {
35774
36006
  const detail = message(cause);
35775
36007
  review = {
@@ -35777,8 +36009,8 @@ VITE_API_BASE_URL=${running.url}
35777
36009
  plannedScenarios: manifest.pages.reduce((total, page) => total + page.scenarios.length, 0),
35778
36010
  warning: `\u591A\u6A21\u6001\u81EA\u4E3B\u89C4\u5212\u5931\u8D25\uFF0C\u5DF2\u7EE7\u7EED\u6267\u884C\u5DF2\u786E\u8BA4\u7684\u8BBE\u8BA1\u5951\u7EA6\uFF1A${detail}`
35779
36011
  };
35780
- await rm15(join48(planDir, "autonomous-interaction-plan.json"), { force: true });
35781
- await writeFile17(join48(planDir, "autonomous-interaction-diagnostic.json"), `${JSON.stringify({
36012
+ await rm15(join49(planDir, "autonomous-interaction-plan.json"), { force: true });
36013
+ await writeFile17(join49(planDir, "autonomous-interaction-diagnostic.json"), `${JSON.stringify({
35782
36014
  schema: 1,
35783
36015
  stage: "planning",
35784
36016
  occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -35792,13 +36024,13 @@ VITE_API_BASE_URL=${running.url}
35792
36024
  let result = { ...rawResult, evidenceMode: review.mode };
35793
36025
  if (coverage !== void 0) {
35794
36026
  const evidence = createAcceptanceEvidence(coverage, result);
35795
- await writeFile17(join48(taskDir(workspace, task), "acceptance-evidence.json"), `${JSON.stringify({ ...evidence, baseline }, null, 2)}
36027
+ await writeFile17(join49(taskDir(workspace, task), "acceptance-evidence.json"), `${JSON.stringify({ ...evidence, baseline }, null, 2)}
35796
36028
  `, { encoding: "utf8", mode: 384 });
35797
36029
  if (!evidence.passed) result = { ...result, passed: false, failures: Math.max(1, result.failures) };
35798
36030
  }
35799
36031
  const dir = integrationDirectory(workspace, task);
35800
36032
  await mkdir27(dir, { recursive: true });
35801
- await writeFile17(join48(dir, "interaction-results.json"), `${JSON.stringify(result, null, 2)}
36033
+ await writeFile17(join49(dir, "interaction-results.json"), `${JSON.stringify(result, null, 2)}
35802
36034
  `);
35803
36035
  const next = await updateWorkflow(workspace, task, workflow.revision, (current) => applyInteractionRun(current, result));
35804
36036
  return { workflow: next, result, review };
@@ -35811,7 +36043,7 @@ VITE_API_BASE_URL=${running.url}
35811
36043
  if (mock === void 0 || !mock.exited() || result.failures === 0) return result;
35812
36044
  const output = mock.output().trim();
35813
36045
  try {
35814
- const dir = join48(d2cOutputDirectory(workspace, task), "mock-server.log");
36046
+ const dir = join49(d2cOutputDirectory(workspace, task), "mock-server.log");
35815
36047
  await writeFile17(dir, `${output}
35816
36048
  `);
35817
36049
  } catch {
@@ -35906,7 +36138,7 @@ VITE_API_BASE_URL=${running.url}
35906
36138
  if (current === void 0) throw new Error("D2C product plan is unavailable");
35907
36139
  if (current.plan.approvedPrd !== void 0) throw new Error("The approved PRD is immutable and cannot be edited");
35908
36140
  if (current.plan.phase !== "prd-review") throw new Error("PRD sections can only be edited during PRD review");
35909
- const path = join48(taskDir(workspace, task), "product", "prd.md");
36141
+ const path = join49(taskDir(workspace, task), "product", "prd.md");
35910
36142
  const updated = await updatePrdSectionFile(path, { sectionId, body, expectedHash });
35911
36143
  const now = (/* @__PURE__ */ new Date()).toISOString();
35912
36144
  await writeD2cProductPlan(workspace, {
@@ -35926,15 +36158,15 @@ VITE_API_BASE_URL=${running.url}
35926
36158
  async #assertProductPrdLocked(workspace, task) {
35927
36159
  const current = await readD2cProductPlanView(workspace, task);
35928
36160
  if (current?.plan.approvedPrd === void 0) return;
35929
- await assertApprovedPrd(join48(taskDir(workspace, task), "product", "prd.md"), current.plan.approvedPrd);
36161
+ await assertApprovedPrd(join49(taskDir(workspace, task), "product", "prd.md"), current.plan.approvedPrd);
35930
36162
  }
35931
36163
  async #acceptancePaths(workspace, task) {
35932
- const product = join48(taskDir(workspace, task), "product");
35933
- const openapi = join48(product, "openapi.json");
36164
+ const product = join49(taskDir(workspace, task), "product");
36165
+ const openapi = join49(product, "openapi.json");
35934
36166
  return {
35935
- prd: join48(product, "prd.md"),
35936
- prototype: join48(product, "prototype", "index.html"),
35937
- interaction: join48(product, "prototype", "interaction-manifest.json"),
36167
+ prd: join49(product, "prd.md"),
36168
+ prototype: join49(product, "prototype", "index.html"),
36169
+ interaction: join49(product, "prototype", "interaction-manifest.json"),
35938
36170
  ...await stat17(openapi).catch(() => void 0) === void 0 ? {} : { openapi }
35939
36171
  };
35940
36172
  }
@@ -35942,17 +36174,17 @@ VITE_API_BASE_URL=${running.url}
35942
36174
  const product = await readD2cProductPlanView(workspace, task);
35943
36175
  if (product?.plan.approvedPrd === void 0) return void 0;
35944
36176
  await this.#assertProductPrdLocked(workspace, task);
35945
- const baseline = JSON.parse(await readFile37(join48(taskDir(workspace, task), "acceptance-baseline.json"), "utf8"));
36177
+ const baseline = JSON.parse(await readFile38(join49(taskDir(workspace, task), "acceptance-baseline.json"), "utf8"));
35946
36178
  await verifyAcceptanceBaseline(await this.#acceptancePaths(workspace, task), baseline);
35947
- const design = join48(taskDir(workspace, task), "design");
36179
+ const design = join49(taskDir(workspace, task), "design");
35948
36180
  await Promise.all([
35949
- verifyAcceptanceArtifact(join48(design, "index.html"), baseline.artifacts.prototype, "runtime prototype"),
35950
- verifyAcceptanceArtifact(join48(design, "interaction-manifest.json"), baseline.artifacts.interaction, "runtime interaction contract")
36181
+ verifyAcceptanceArtifact(join49(design, "index.html"), baseline.artifacts.prototype, "runtime prototype"),
36182
+ verifyAcceptanceArtifact(join49(design, "interaction-manifest.json"), baseline.artifacts.interaction, "runtime interaction contract")
35951
36183
  ]);
35952
36184
  const workflow = await readWorkflow(workspace, task);
35953
36185
  if (workflow?.openapi !== void 0) {
35954
36186
  await verifyAcceptanceArtifact(
35955
- join48(taskDir(workspace, task), "integration", "swagger.json"),
36187
+ join49(taskDir(workspace, task), "integration", "swagger.json"),
35956
36188
  { hash: workflow.openapi.hash },
35957
36189
  "runtime OpenAPI contract"
35958
36190
  );
@@ -35973,14 +36205,14 @@ VITE_API_BASE_URL=${running.url}
35973
36205
  throw new Error("D2C PRD is empty and cannot be accepted");
35974
36206
  }
35975
36207
  if (stage === "design" && accepted) {
35976
- const manifest = join48(d2cProductPrototypeDirectory(workspace, task), "interaction-manifest.json");
35977
- const normalizedManifest = parseInteractionManifest(await readFile37(manifest, "utf8"));
36208
+ const manifest = join49(d2cProductPrototypeDirectory(workspace, task), "interaction-manifest.json");
36209
+ const normalizedManifest = parseInteractionManifest(await readFile38(manifest, "utf8"));
35978
36210
  if (current.plan.approvedPrd === void 0) throw new Error("Approve the PRD before confirming the design");
35979
36211
  verifyRequirementCoverage(current.plan.approvedPrd.criteria, normalizedManifest);
35980
36212
  await writeFile17(manifest, `${JSON.stringify(normalizedManifest, null, 2)}
35981
36213
  `, { encoding: "utf8", mode: 384 });
35982
36214
  const baseline = await captureAcceptanceBaseline(await this.#acceptancePaths(workspace, task), current.plan.approvedPrd);
35983
- await writeFile17(join48(taskDir(workspace, task), "acceptance-baseline.json"), `${JSON.stringify(baseline, null, 2)}
36215
+ await writeFile17(join49(taskDir(workspace, task), "acceptance-baseline.json"), `${JSON.stringify(baseline, null, 2)}
35984
36216
  `, { encoding: "utf8", mode: 384 });
35985
36217
  }
35986
36218
  const plan = await writeD2cProductPlan(
@@ -36014,8 +36246,8 @@ VITE_API_BASE_URL=${running.url}
36014
36246
  const imported = await this.importD2cDesign(task, d2cProductPrototypeDirectory(workspace, task));
36015
36247
  const delivery = await readDeliveryRun(workspace, task);
36016
36248
  if (delivery !== void 0) {
36017
- const prototype = await readFile37(join48(d2cProductPrototypeDirectory(workspace, task), "index.html"), "utf8");
36018
- const interaction = await readFile37(join48(d2cProductPrototypeDirectory(workspace, task), "interaction-manifest.json"), "utf8");
36249
+ const prototype = await readFile38(join49(d2cProductPrototypeDirectory(workspace, task), "index.html"), "utf8");
36250
+ const interaction = await readFile38(join49(d2cProductPrototypeDirectory(workspace, task), "interaction-manifest.json"), "utf8");
36019
36251
  const completed = await updateDeliveryRun(workspace, task, delivery.revision, (run) => completeDeliveryNode(run, "design", [
36020
36252
  artifactRef("product/prototype/index.html", prototype),
36021
36253
  artifactRef("product/prototype/interaction-manifest.json", interaction)
@@ -36099,10 +36331,10 @@ VITE_API_BASE_URL=${running.url}
36099
36331
  const next = await updateWorkflow(workspace, task, workflow.revision, (current) => applyQualityJudgment(current, judgment));
36100
36332
  const qualityContent = `${JSON.stringify(judgment, null, 2)}
36101
36333
  `;
36102
- await writeFile17(join48(taskDir(workspace, task), "quality-judge.json"), qualityContent, { encoding: "utf8", mode: 384 });
36334
+ await writeFile17(join49(taskDir(workspace, task), "quality-judge.json"), qualityContent, { encoding: "utf8", mode: 384 });
36103
36335
  const delivery = await readDeliveryRun(workspace, task);
36104
36336
  if (next.stage === "completed" && delivery?.nodes.acceptance.status === "running") {
36105
- const interactionContent = await readFile37(join48(integrationDirectory(workspace, task), "interaction-results.json"), "utf8");
36337
+ const interactionContent = await readFile38(join49(integrationDirectory(workspace, task), "interaction-results.json"), "utf8");
36106
36338
  const accepted = await updateDeliveryRun(workspace, task, delivery.revision, (run) => completeDeliveryNode(run, "acceptance", [
36107
36339
  artifactRef("integration/interaction-results.json", interactionContent),
36108
36340
  artifactRef("quality-judge.json", qualityContent)
@@ -36334,7 +36566,7 @@ async function loginShellEnvironment(environment, platform = process.platform, d
36334
36566
  }
36335
36567
 
36336
36568
  // src/desktop/git-manager.ts
36337
- import { readFile as readFile38, rm as rm16 } from "fs/promises";
36569
+ import { readFile as readFile39, rm as rm16 } from "fs/promises";
36338
36570
  import { isAbsolute as isAbsolute21, relative as relative28, resolve as resolve40 } from "path";
36339
36571
  function failure(action, stderr, stdout) {
36340
36572
  return new Error(`${action}\u5931\u8D25\uFF1A${(stderr.trim() || stdout.trim() || "\u672A\u77E5 Git \u9519\u8BEF").split("\n")[0]}`);
@@ -36366,7 +36598,7 @@ async function desktopGitDiff(workspace, path, staged = false) {
36366
36598
  if (file === void 0) throw new Error("\u8BE5\u6587\u4EF6\u5DF2\u4E0D\u5728\u53D8\u66F4\u5217\u8868\u4E2D");
36367
36599
  if (file.untracked) {
36368
36600
  const target = safeWorkspacePath(workspace, path);
36369
- const value = await readFile38(target, "utf8");
36601
+ const value = await readFile39(target, "utf8");
36370
36602
  return value.length > 12e4 ? `${value.slice(0, 12e4)}
36371
36603
  \u2026[\u5185\u5BB9\u5DF2\u622A\u65AD]` : value;
36372
36604
  }
@@ -36428,13 +36660,13 @@ async function desktopLastTurnDiff(workspace, sessionId, paths = []) {
36428
36660
  return path.replaceAll("\\", "/");
36429
36661
  }));
36430
36662
  try {
36431
- const tree = JSON.parse(await readFile38(resolve40(workspace, ".flavor", "session-trees", sessionId, "tree.json"), "utf8"));
36663
+ const tree = JSON.parse(await readFile39(resolve40(workspace, ".flavor", "session-trees", sessionId, "tree.json"), "utf8"));
36432
36664
  const leaf = tree.nodes?.find((node) => node.id === tree.leafId);
36433
36665
  const parent = leaf?.parentId == null ? void 0 : tree.nodes?.find((node) => node.id === leaf.parentId);
36434
36666
  if (leaf === void 0 || parent === void 0) return desktopGitReviewDiff(workspace, { scope: "last-turn", paths });
36435
36667
  const load = async (id) => {
36436
36668
  if (!/^checkpoint-[A-Za-z0-9-]+$/.test(id)) throw new Error("Invalid checkpoint id");
36437
- return JSON.parse(await readFile38(resolve40(workspace, ".flavor", "checkpoints", "manifests", `${id}.json`), "utf8"));
36669
+ return JSON.parse(await readFile39(resolve40(workspace, ".flavor", "checkpoints", "manifests", `${id}.json`), "utf8"));
36438
36670
  };
36439
36671
  const [before, after] = await Promise.all([load(parent.checkpointId), load(leaf.checkpointId)]);
36440
36672
  const oldFiles = new Map(before.files.map((file) => [file.path, file.digest]));
@@ -36443,7 +36675,7 @@ async function desktopLastTurnDiff(workspace, sessionId, paths = []) {
36443
36675
  const object = async (digest) => {
36444
36676
  if (digest === void 0) return "";
36445
36677
  if (!/^[a-f0-9]{64}$/.test(digest)) throw new Error("Invalid checkpoint object");
36446
- const value = await readFile38(resolve40(workspace, ".flavor", "checkpoints", "objects", digest));
36678
+ const value = await readFile39(resolve40(workspace, ".flavor", "checkpoints", "objects", digest));
36447
36679
  return value.includes(0) ? "[binary content]" : value.toString("utf8");
36448
36680
  };
36449
36681
  const chunks = await Promise.all(changed.map(async (path) => wholeFileDiff(path, await object(oldFiles.get(path)), await object(newFiles.get(path)))));
@@ -36482,8 +36714,8 @@ function safeGitTarget(value) {
36482
36714
 
36483
36715
  // src/desktop/worktree-manager.ts
36484
36716
  import { randomUUID as randomUUID28 } from "crypto";
36485
- import { mkdir as mkdir28, readFile as readFile39, readdir as readdir14, rm as rm17, stat as stat18, writeFile as writeFile18 } from "fs/promises";
36486
- import { basename as basename11, isAbsolute as isAbsolute22, join as join49, relative as relative29, resolve as resolve41, sep as sep29 } from "path";
36717
+ import { mkdir as mkdir28, readFile as readFile40, readdir as readdir14, rm as rm17, stat as stat18, writeFile as writeFile18 } from "fs/promises";
36718
+ import { basename as basename11, isAbsolute as isAbsolute22, join as join50, relative as relative29, resolve as resolve41, sep as sep29 } from "path";
36487
36719
  var DesktopWorktreeManager = class {
36488
36720
  #repository;
36489
36721
  #storage;
@@ -36529,7 +36761,7 @@ var DesktopWorktreeManager = class {
36529
36761
  }
36530
36762
  const values = await Promise.all(names.filter((name) => name.endsWith(".json")).map(async (name) => {
36531
36763
  try {
36532
- const stored = validateStored(JSON.parse(await readFile39(join49(this.#storage, name), "utf8")), this.#repository, this.#storage);
36764
+ const stored = validateStored(JSON.parse(await readFile40(join50(this.#storage, name), "utf8")), this.#repository, this.#storage);
36533
36765
  if ((await stat18(stored.path).catch(() => void 0))?.isDirectory() !== true) return void 0;
36534
36766
  const status = await git2(stored.path, ["status", "--porcelain"]);
36535
36767
  const merged = await git2(this.#repository, ["merge-base", "--is-ancestor", stored.branch, "HEAD"]);
@@ -36563,7 +36795,7 @@ var DesktopWorktreeManager = class {
36563
36795
  }
36564
36796
  async #load(id) {
36565
36797
  const path = this.#metadataPath(id);
36566
- return validateStored(JSON.parse(await readFile39(path, "utf8")), this.#repository, this.#storage);
36798
+ return validateStored(JSON.parse(await readFile40(path, "utf8")), this.#repository, this.#storage);
36567
36799
  }
36568
36800
  #metadataPath(id) {
36569
36801
  if (!/^worktree-[a-z0-9._-]{1,80}$/.test(id)) throw new Error("Invalid worktree id");
@@ -36591,9 +36823,9 @@ function validateStored(value, repository, storage) {
36591
36823
  }
36592
36824
 
36593
36825
  // src/desktop/workbench-service.ts
36594
- import { readFile as readFile40, readdir as readdir15, stat as stat19 } from "fs/promises";
36826
+ import { readFile as readFile41, readdir as readdir15, stat as stat19 } from "fs/promises";
36595
36827
  import { homedir as homedir6 } from "os";
36596
- import { join as join50, resolve as resolve42 } from "path";
36828
+ import { join as join51, resolve as resolve42 } from "path";
36597
36829
  var MAX_TEXT = 128e3;
36598
36830
  var MAX_ITEMS = 200;
36599
36831
  var DesktopWorkbenchService = class {
@@ -36626,11 +36858,11 @@ var DesktopWorkbenchService = class {
36626
36858
  return url.toString().replace(/\/$/, input.trim().endsWith("/") ? "/" : "");
36627
36859
  }
36628
36860
  async #goals() {
36629
- const root = join50(this.#workspace, ".flavor", "goals");
36861
+ const root = join51(this.#workspace, ".flavor", "goals");
36630
36862
  const names = await readdir15(root).catch((error) => error.code === "ENOENT" ? [] : Promise.reject(error));
36631
36863
  const values = await Promise.all(names.filter((name) => /^goal-[A-Za-z0-9._-]+\.json$/.test(name)).slice(0, MAX_ITEMS).map(async (name) => {
36632
36864
  try {
36633
- return GoalStateSchema.parse(JSON.parse(await boundedRead2(join50(root, name))));
36865
+ return GoalStateSchema.parse(JSON.parse(await boundedRead2(join51(root, name))));
36634
36866
  } catch {
36635
36867
  return void 0;
36636
36868
  }
@@ -36640,7 +36872,7 @@ var DesktopWorkbenchService = class {
36640
36872
  async #instructions() {
36641
36873
  const names = ["FLAVOR.md", "AGENTS.md", "CLAUDE.md", "AGENTS.local.md", "CLAUDE.local.md"];
36642
36874
  const items = await Promise.all(names.map(async (name) => {
36643
- const path = join50(this.#workspace, name);
36875
+ const path = join51(this.#workspace, name);
36644
36876
  try {
36645
36877
  return { name, path, content: redactErrorText(await boundedRead2(path)) };
36646
36878
  } catch (error) {
@@ -36653,9 +36885,9 @@ var DesktopWorkbenchService = class {
36653
36885
  async #permissionFiles() {
36654
36886
  const candidates = [
36655
36887
  ...process.env.FLAVOR_MANAGED_PERMISSIONS ? [{ tier: "managed", path: resolve42(process.env.FLAVOR_MANAGED_PERMISSIONS), content: "" }] : [],
36656
- { tier: "user", path: join50(this.#home, ".flavor-code", "permissions.json"), content: "" },
36657
- { tier: "project", path: join50(this.#workspace, ".flavor", "permissions.json"), content: "" },
36658
- { tier: "local", path: join50(this.#workspace, ".flavor", "permissions.local.json"), content: "" }
36888
+ { tier: "user", path: join51(this.#home, ".flavor-code", "permissions.json"), content: "" },
36889
+ { tier: "project", path: join51(this.#workspace, ".flavor", "permissions.json"), content: "" },
36890
+ { tier: "local", path: join51(this.#workspace, ".flavor", "permissions.local.json"), content: "" }
36659
36891
  ];
36660
36892
  const values = await Promise.all(candidates.map(async (item) => {
36661
36893
  try {
@@ -36670,7 +36902,7 @@ var DesktopWorkbenchService = class {
36670
36902
  async #audit() {
36671
36903
  let raw;
36672
36904
  try {
36673
- raw = await boundedRead2(join50(this.#workspace, ".flavor", "audit.jsonl"));
36905
+ raw = await boundedRead2(join51(this.#workspace, ".flavor", "audit.jsonl"));
36674
36906
  } catch (error) {
36675
36907
  if (error.code === "ENOENT") return [];
36676
36908
  throw error;
@@ -36685,7 +36917,7 @@ var DesktopWorkbenchService = class {
36685
36917
  }).reverse();
36686
36918
  }
36687
36919
  async #context(sessionId) {
36688
- const usage = await readJsonLines2(join50(this.#workspace, ".flavor", "usage.jsonl"));
36920
+ const usage = await readJsonLines2(join51(this.#workspace, ".flavor", "usage.jsonl"));
36689
36921
  if (sessionId === void 0) return { visibility: [], usage };
36690
36922
  try {
36691
36923
  const document = await new SessionStore({ workspace: this.#workspace }).load(sessionId);
@@ -36699,7 +36931,7 @@ async function boundedRead2(path) {
36699
36931
  const info = await stat19(path);
36700
36932
  if (!info.isFile()) throw new Error(`Expected a file: ${path}`);
36701
36933
  if (info.size > MAX_TEXT) throw new Error(`Workbench file exceeds ${MAX_TEXT} bytes: ${path}`);
36702
- return readFile40(path, "utf8");
36934
+ return readFile41(path, "utf8");
36703
36935
  }
36704
36936
  async function readJsonLines2(path) {
36705
36937
  try {
@@ -36760,7 +36992,7 @@ function ensureBrowserUiWriteAllowed(spaceId) {
36760
36992
  }
36761
36993
  function logStartup(step, detail) {
36762
36994
  try {
36763
- const logPath = join51(dirname23(process.execPath), "flavor-code-startup.log");
36995
+ const logPath = join52(dirname23(process.execPath), "flavor-code-startup.log");
36764
36996
  const ts = (/* @__PURE__ */ new Date()).toISOString();
36765
36997
  appendFileSync(logPath, `[${ts}] ${step}${detail ? ` | ${detail}` : ""}
36766
36998
  `);
@@ -36785,7 +37017,7 @@ var d2cEmbedded = createEmbeddedD2cAutomation(() => {
36785
37017
  };
36786
37018
  });
36787
37019
  var d2cJudgeClient = createD2cJudgeClient();
36788
- var judgeStore = () => createD2cJudgeConfigStore(join51(app.getPath("userData"), "d2c-judge.json"));
37020
+ var judgeStore = () => createD2cJudgeConfigStore(join52(app.getPath("userData"), "d2c-judge.json"));
36789
37021
  function judgeImage(png) {
36790
37022
  const image = nativeImage.createFromBuffer(png);
36791
37023
  const size = image.getSize();
@@ -36811,7 +37043,7 @@ function sessionMetaKey(workspace, sessionId) {
36811
37043
  }
36812
37044
  function worktreeManager(workspace) {
36813
37045
  const key = createHash29("sha256").update(resolve43(workspace)).digest("hex").slice(0, 20);
36814
- return new DesktopWorktreeManager({ repository: workspace, storage: join51(app.getPath("userData"), "task-worktrees", key) });
37046
+ return new DesktopWorktreeManager({ repository: workspace, storage: join52(app.getPath("userData"), "task-worktrees", key) });
36815
37047
  }
36816
37048
  function activeWorkingDirectory() {
36817
37049
  if (activeWorkspace === void 0) throw new Error("\u8BF7\u5148\u6253\u5F00\u9879\u76EE");
@@ -36956,7 +37188,7 @@ function createDesktopController(workspace, owner2) {
36956
37188
  alias: `desktop-${basename12(workspace ?? runtimeOptions.workspace ?? "flavor")}`.slice(0, 64),
36957
37189
  optional: true,
36958
37190
  startBroker: (address) => {
36959
- const entry2 = app.isPackaged ? join51(process.resourcesPath, "app.asar.unpacked", "dist", "desktop", "pals-broker-entry.js") : join51(moduleDirectory, "pals-broker-entry.js");
37191
+ const entry2 = app.isPackaged ? join52(process.resourcesPath, "app.asar.unpacked", "dist", "desktop", "pals-broker-entry.js") : join52(moduleDirectory, "pals-broker-entry.js");
36960
37192
  return spawnPalBrokerProcess({
36961
37193
  address,
36962
37194
  executable: process.execPath,
@@ -36985,11 +37217,11 @@ function createDesktopController(workspace, owner2) {
36985
37217
  var detachedController = createDesktopController();
36986
37218
  var controller = detachedController;
36987
37219
  function statePath() {
36988
- return join51(app.getPath("userData"), "desktop-state.json");
37220
+ return join52(app.getPath("userData"), "desktop-state.json");
36989
37221
  }
36990
37222
  async function loadPersistedProjects() {
36991
37223
  try {
36992
- const raw = await readFile41(statePath(), "utf8");
37224
+ const raw = await readFile42(statePath(), "utf8");
36993
37225
  if (raw.length > 1e5) return { projects: [] };
36994
37226
  const parsed = JSON.parse(raw);
36995
37227
  Object.assign(workbench, {
@@ -37166,7 +37398,7 @@ async function chooseAndOpenWorkspace() {
37166
37398
  var cachedAppIconDataUrl;
37167
37399
  function appIconDataUrl() {
37168
37400
  if (cachedAppIconDataUrl !== void 0) return cachedAppIconDataUrl;
37169
- const icon = nativeImage.createFromPath(join51(app.getAppPath(), "assets", "icon.png"));
37401
+ const icon = nativeImage.createFromPath(join52(app.getAppPath(), "assets", "icon.png"));
37170
37402
  if (icon.isEmpty()) return void 0;
37171
37403
  cachedAppIconDataUrl = icon.toDataURL();
37172
37404
  return cachedAppIconDataUrl;
@@ -37642,7 +37874,7 @@ function applicationMenu() {
37642
37874
  ]);
37643
37875
  }
37644
37876
  async function createWindow() {
37645
- const rendererPath = join51(app.getAppPath(), "dist", "desktop-renderer", "index.html");
37877
+ const rendererPath = join52(app.getAppPath(), "dist", "desktop-renderer", "index.html");
37646
37878
  mainWindow = new BrowserWindow2({
37647
37879
  title: "Flavor Code",
37648
37880
  width: 1280,
@@ -37653,14 +37885,14 @@ async function createWindow() {
37653
37885
  show: false,
37654
37886
  ...desktopWindowChrome(),
37655
37887
  webPreferences: {
37656
- preload: join51(moduleDirectory, "preload.cjs"),
37888
+ preload: join52(moduleDirectory, "preload.cjs"),
37657
37889
  contextIsolation: true,
37658
37890
  nodeIntegration: false,
37659
37891
  sandbox: false,
37660
37892
  webSecurity: true
37661
37893
  }
37662
37894
  });
37663
- const iconPath = join51(app.getAppPath(), "assets", "icon.png");
37895
+ const iconPath = join52(app.getAppPath(), "assets", "icon.png");
37664
37896
  const icon = nativeImage.createFromPath(iconPath);
37665
37897
  if (!icon.isEmpty()) {
37666
37898
  mainWindow.setIcon(icon);