@kw-yeh/vibeflow 4.11.29 → 4.11.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/vibeflow.mjs CHANGED
@@ -1107,9 +1107,9 @@ var init_progress = __esm({
1107
1107
 
1108
1108
  // packages/tui/src/task-form.ts
1109
1109
  import { spawnSync as spawnSync2 } from "child_process";
1110
- import fs26 from "fs";
1111
- import os8 from "os";
1112
- import path29 from "path";
1110
+ import fs27 from "fs";
1111
+ import os9 from "os";
1112
+ import path30 from "path";
1113
1113
  import React, { useEffect, useRef, useState } from "react";
1114
1114
  import { Box, Text, render as render2, useApp, useInput } from "ink";
1115
1115
  function newFormState(defaultAutoMode, agents) {
@@ -1235,17 +1235,17 @@ function editorCommand(env, platform) {
1235
1235
  return env.VISUAL?.trim() || env.EDITOR?.trim() || (platform === "win32" ? "notepad" : "vi");
1236
1236
  }
1237
1237
  function editInEditor(text) {
1238
- const dir = fs26.mkdtempSync(path29.join(os8.tmpdir(), "vibeflow-"));
1239
- const file = path29.join(dir, "description.md");
1238
+ const dir = fs27.mkdtempSync(path30.join(os9.tmpdir(), "vibeflow-"));
1239
+ const file = path30.join(dir, "description.md");
1240
1240
  try {
1241
- fs26.writeFileSync(file, text);
1241
+ fs27.writeFileSync(file, text);
1242
1242
  const cmd = editorCommand(process.env, process.platform);
1243
1243
  const r = spawnSync2(`${cmd} "${file}"`, { stdio: "inherit", shell: true });
1244
1244
  if (r.error) return { error: `\u7121\u6CD5\u555F\u52D5\u7DE8\u8F2F\u5668\uFF08${cmd}\uFF09\uFF1A${r.error.message}` };
1245
1245
  if (r.status !== 0) return { error: `\u7DE8\u8F2F\u5668\uFF08${cmd}\uFF09\u7D50\u675F\u78BC ${r.status}\uFF0C\u63CF\u8FF0\u672A\u8B8A\u66F4` };
1246
- return { text: fs26.readFileSync(file, "utf8").replace(/\r?\n$/, "") };
1246
+ return { text: fs27.readFileSync(file, "utf8").replace(/\r?\n$/, "") };
1247
1247
  } finally {
1248
- fs26.rmSync(dir, { recursive: true, force: true });
1248
+ fs27.rmSync(dir, { recursive: true, force: true });
1249
1249
  }
1250
1250
  }
1251
1251
  function cycle(list, current, step) {
@@ -1316,10 +1316,10 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
1316
1316
  if (editing === "title") setS((prev) => ({ ...prev, title: draft }));
1317
1317
  else if (editing === "branch") setS((prev) => ({ ...prev, branch: value2 }));
1318
1318
  else if (editing === "project") {
1319
- if (value2) setProject(path29.resolve(value2.replace(/^~(?=$|\/)/, os8.homedir())));
1319
+ if (value2) setProject(path30.resolve(value2.replace(/^~(?=$|\/)/, os9.homedir())));
1320
1320
  } else if (editing === "attachments" && value2) {
1321
- const file = path29.resolve(value2.replace(/^~(?=$|\/)/, os8.homedir()));
1322
- if (!fs26.existsSync(file) || !fs26.statSync(file).isFile()) {
1321
+ const file = path30.resolve(value2.replace(/^~(?=$|\/)/, os9.homedir()));
1322
+ if (!fs27.existsSync(file) || !fs27.statSync(file).isFile()) {
1323
1323
  setMessage(`\u627E\u4E0D\u5230\u6A94\u6848\uFF1A${file}`);
1324
1324
  return;
1325
1325
  }
@@ -1402,7 +1402,7 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
1402
1402
  case "branch":
1403
1403
  return s.branch || "\uFF08\u7559\u7A7A\u5247\u81EA\u52D5\u547D\u540D\uFF0C\u4F8B\u5982 feature/login-fix\uFF09";
1404
1404
  case "attachments":
1405
- return s.attachments.length ? s.attachments.map((a) => path29.basename(a)).join("\u3001") : "\uFF08Enter \u8F38\u5165\u6A94\u6848\u8DEF\u5F91\u52A0\u5165\uFF09";
1405
+ return s.attachments.length ? s.attachments.map((a) => path30.basename(a)).join("\u3001") : "\uFF08Enter \u8F38\u5165\u6A94\u6848\u8DEF\u5F91\u52A0\u5165\uFF09";
1406
1406
  case "description": {
1407
1407
  if (!s.description) return "\uFF08Enter \u7528\u7DE8\u8F2F\u5668\u64B0\u5BEB\uFF09";
1408
1408
  const lines = s.description.split("\n");
@@ -1437,7 +1437,7 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
1437
1437
  if (info && !info.isRepo) return "\u9019\u500B\u8CC7\u6599\u593E\u4E0D\u662F Git repository\u3002\u8ACB\u6539\u9078\u4E00\u500B Git \u5C08\u6848\uFF0C\u6216\u5728\u4E0B\u65B9\u521D\u59CB\u5316\u3002";
1438
1438
  if (s.mode === "edit" && projectChanged(s)) return "\u5206\u652F\u6703\u5728\u65B0\u5C08\u6848\u3001\u5361\u7247\u958B\u59CB\u57F7\u884C\u6642\u624D\u5EFA\u7ACB\u3002";
1439
1439
  if (info?.isRepo && !info.hasRemote) return `\u6B64 repository \u6C92\u6709 remote\uFF0C\u5C07\u4EE5\u76EE\u524D\u5206\u652F (${info.currentBranch ?? "HEAD"}) \u70BA\u57FA\u6E96\u5EFA\u7ACB\u672C\u5730 worktree\u3002`;
1440
- if (info?.isRepo && s.projectPath) return `worktree \u6703\u5EFA\u5728 ${ctx.workstationPath || "~/Desktop"}/${path29.basename(s.projectPath)}`;
1440
+ if (info?.isRepo && s.projectPath) return `worktree \u6703\u5EFA\u5728 ${ctx.workstationPath || "~/Desktop"}/${path30.basename(s.projectPath)}`;
1441
1441
  }
1442
1442
  if (field === "branch") {
1443
1443
  const when = info?.hasRemote ? "\u958B\u59CB\u57F7\u884C\u6642\u6703\u5EFA\u7ACB\u540C\u540D\u5206\u652F\u4E26\u63A8\u5230 origin\uFF1B\u82E5 remote \u5DF2\u6709\u9019\u500B\u5206\u652F\uFF0C\u5247\u76F4\u63A5\u53D6\u56DE\u4E26\u63A5\u7E8C\u4E0A\u9762\u7684\u5DE5\u4F5C\u3002" : "\u958B\u59CB\u57F7\u884C\u6642\u6703\u5EFA\u7ACB\u540C\u540D\u5206\u652F\u3002";
@@ -1566,7 +1566,7 @@ __export(src_exports, {
1566
1566
  showDiff: () => showDiff
1567
1567
  });
1568
1568
  import { spawnSync as spawnSync3 } from "child_process";
1569
- import path30 from "path";
1569
+ import path31 from "path";
1570
1570
  import React2, { useEffect as useEffect2, useState as useState2 } from "react";
1571
1571
  import { Box as Box2, Text as Text2, render as render3, useApp as useApp2, useInput as useInput2, useStdout } from "ink";
1572
1572
  function findDetachKey(text) {
@@ -1862,7 +1862,7 @@ function showDiff(task) {
1862
1862
  });
1863
1863
  }
1864
1864
  function tmuxAttach(opts, sessionName) {
1865
- const conf = path30.join(opts.userDataDir, "tmux.conf");
1865
+ const conf = path31.join(opts.userDataDir, "tmux.conf");
1866
1866
  const inOurServer = (process.env.TMUX ?? "").split(",")[0].endsWith(`/${TMUX_SOCKET}`);
1867
1867
  const args = inOurServer ? ["-L", TMUX_SOCKET, "-f", conf, "switch-client", "-t", `=${sessionName}`] : ["-L", TMUX_SOCKET, "-f", conf, "attach-session", "-t", `=${sessionName}`];
1868
1868
  spawnSync3("tmux", args, { stdio: "inherit", env: { ...process.env, TMUX: inOurServer ? process.env.TMUX : "" } });
@@ -1993,9 +1993,9 @@ var init_src = __esm({
1993
1993
  });
1994
1994
 
1995
1995
  // packages/cli/src/main.ts
1996
- import fs27 from "fs";
1997
- import path31 from "path";
1998
- import { fileURLToPath } from "url";
1996
+ import fs28 from "fs";
1997
+ import path32 from "path";
1998
+ import { fileURLToPath as fileURLToPath2 } from "url";
1999
1999
  import { parseArgs as parseArgs2 } from "node:util";
2000
2000
  import { spawn as spawn9 } from "node:child_process";
2001
2001
 
@@ -2601,6 +2601,9 @@ function createBridge(t) {
2601
2601
  },
2602
2602
  // Interactive terminal bridge (Phase 3).
2603
2603
  term: {
2604
+ readFile: (sessionKey, path33) => t.invoke("terminal:readFile", { sessionKey, path: path33 }),
2605
+ existingFiles: (sessionKey, paths) => t.invoke("terminal:existingFiles", { sessionKey, paths }),
2606
+ openFile: (sessionKey, path33) => t.invoke("terminal:openFile", { sessionKey, path: path33 }),
2604
2607
  /**
2605
2608
  * Start a terminal for a task: its agent when `launch` is given, else an
2606
2609
  * interactive shell. Core resolves the cwd and builds the command.
@@ -4714,8 +4717,8 @@ async function taskCommand(argv) {
4714
4717
  }
4715
4718
 
4716
4719
  // packages/cli/src/host.ts
4717
- import fs24 from "fs";
4718
- import path27 from "path";
4720
+ import fs25 from "fs";
4721
+ import path28 from "path";
4719
4722
  import { randomBytes as randomBytes2 } from "crypto";
4720
4723
 
4721
4724
  // packages/core/src/events.ts
@@ -5122,21 +5125,109 @@ async function syncBuiltinsAtStartup() {
5122
5125
  }
5123
5126
 
5124
5127
  // packages/core/src/service.ts
5125
- import fs20 from "fs";
5126
- import os7 from "os";
5127
- import path23 from "path";
5128
+ import fs21 from "fs";
5129
+
5130
+ // packages/core/src/terminal-files.ts
5131
+ import { constants } from "fs";
5132
+ import fs13 from "fs/promises";
5133
+ import os3 from "os";
5134
+ import path16 from "path";
5135
+ import { fileURLToPath } from "url";
5136
+ var MAX_PREVIEW_IMAGE_BYTES = 20 * 1024 * 1024;
5137
+ var MAX_TEXT_BYTES2 = 256 * 1024;
5138
+ var IMAGE_MIME2 = {
5139
+ ".png": "image/png",
5140
+ ".jpg": "image/jpeg",
5141
+ ".jpeg": "image/jpeg",
5142
+ ".gif": "image/gif",
5143
+ ".webp": "image/webp",
5144
+ ".avif": "image/avif",
5145
+ ".bmp": "image/bmp",
5146
+ ".svg": "image/svg+xml"
5147
+ };
5148
+ function resolveTerminalFile(cwd, input) {
5149
+ if (!input || input.includes("\0") || input.length > 32768) throw new Error("\u6A94\u6848\u8DEF\u5F91\u7121\u6548\u3002");
5150
+ let target = input;
5151
+ if (/^file:/i.test(target)) {
5152
+ const url = new URL(target);
5153
+ if (url.hostname && url.hostname !== "localhost") throw new Error("\u53EA\u652F\u63F4\u672C\u6A5F\u6A94\u6848\u3002");
5154
+ target = fileURLToPath(url);
5155
+ } else if (/^[a-z][a-z\d+.-]*:/i.test(target) && !/^[a-z]:[\\/]/i.test(target)) {
5156
+ throw new Error("\u53EA\u652F\u63F4\u672C\u6A5F\u6A94\u6848\u8DEF\u5F91\u3002");
5157
+ }
5158
+ if (target.startsWith("~/") || target.startsWith("~\\")) target = path16.join(os3.homedir(), target.slice(2));
5159
+ if (target.includes("\0")) throw new Error("\u6A94\u6848\u8DEF\u5F91\u7121\u6548\u3002");
5160
+ return path16.resolve(cwd, target);
5161
+ }
5162
+ var MAX_FILE_CANDIDATES = 128;
5163
+ async function existingTerminalFiles(cwd, inputs) {
5164
+ const found2 = await Promise.all(inputs.slice(0, MAX_FILE_CANDIDATES).map(async (input) => {
5165
+ try {
5166
+ return (await fs13.stat(resolveTerminalFile(cwd, input))).isFile();
5167
+ } catch {
5168
+ return false;
5169
+ }
5170
+ }));
5171
+ return inputs.filter((_, i) => found2[i]);
5172
+ }
5173
+ async function readTerminalFile(cwd, input, metadataOnly = false) {
5174
+ let target;
5175
+ try {
5176
+ target = resolveTerminalFile(cwd, input);
5177
+ } catch (error) {
5178
+ return { ok: false, error: "invalid_path", message: error instanceof Error ? error.message : "\u6A94\u6848\u8DEF\u5F91\u7121\u6548\u3002" };
5179
+ }
5180
+ let handle;
5181
+ try {
5182
+ if (!(await fs13.stat(target)).isFile()) return { ok: false, error: "not_file", message: "\u9019\u500B\u8DEF\u5F91\u4E0D\u662F\u4E00\u822C\u6A94\u6848\u3002" };
5183
+ handle = await fs13.open(target, constants.O_RDONLY | constants.O_NONBLOCK);
5184
+ const stat = await handle.stat();
5185
+ if (!stat.isFile()) return { ok: false, error: "not_file", message: "\u9019\u500B\u8DEF\u5F91\u4E0D\u662F\u4E00\u822C\u6A94\u6848\u3002" };
5186
+ if (metadataOnly) return { ok: true, path: target, kind: "binary", reason: "" };
5187
+ const mime = IMAGE_MIME2[path16.extname(target).toLowerCase()];
5188
+ if (mime && stat.size > MAX_PREVIEW_IMAGE_BYTES) {
5189
+ return { ok: true, path: target, kind: "binary", reason: "\u5716\u7247\u8D85\u904E 20 MB \u9810\u89BD\u4E0A\u9650\u3002" };
5190
+ }
5191
+ const limit = mime ? MAX_PREVIEW_IMAGE_BYTES : MAX_TEXT_BYTES2;
5192
+ const buffer = Buffer.alloc(Math.min(stat.size, limit + 1));
5193
+ let bytes = 0;
5194
+ while (bytes < buffer.length) {
5195
+ const { bytesRead } = await handle.read(buffer, bytes, buffer.length - bytes, bytes);
5196
+ if (!bytesRead) break;
5197
+ bytes += bytesRead;
5198
+ }
5199
+ if (mime) {
5200
+ if (bytes > limit) return { ok: true, path: target, kind: "binary", reason: "\u5716\u7247\u8D85\u904E 20 MB \u9810\u89BD\u4E0A\u9650\u3002" };
5201
+ return { ok: true, path: target, kind: "image", dataUrl: `data:${mime};base64,${buffer.subarray(0, bytes).toString("base64")}` };
5202
+ }
5203
+ const sample = buffer.subarray(0, Math.min(bytes, 8192));
5204
+ if (/\.(pdf|zip|gz|tar|exe|dll|woff2?|mp4|mov|webm|mp3|wav)$/i.test(target) || sample.some((b) => b === 0 || (b < 9 || b > 13 && b < 32))) {
5205
+ return { ok: true, path: target, kind: "binary", reason: "\u6B64\u683C\u5F0F\u7121\u6CD5\u5728 VibeFlow \u5167\u9810\u89BD\u3002" };
5206
+ }
5207
+ return { ok: true, path: target, kind: "text", text: buffer.subarray(0, Math.min(bytes, limit)).toString("utf8"), truncated: bytes > limit };
5208
+ } catch (error) {
5209
+ const code2 = error.code;
5210
+ return code2 === "ENOENT" || code2 === "ENOTDIR" ? { ok: false, error: "not_found", message: "\u627E\u4E0D\u5230\u9019\u500B\u6A94\u6848\uFF0C\u53EF\u80FD\u5DF2\u88AB\u79FB\u52D5\u6216\u522A\u9664\u3002" } : { ok: false, error: "unreadable", message: "\u7121\u6CD5\u8B80\u53D6\u9019\u500B\u6A94\u6848\uFF0C\u8ACB\u78BA\u8A8D\u5B58\u53D6\u6B0A\u9650\u3002" };
5211
+ } finally {
5212
+ await handle?.close();
5213
+ }
5214
+ }
5215
+
5216
+ // packages/core/src/service.ts
5217
+ import os8 from "os";
5218
+ import path24 from "path";
5128
5219
  import { randomUUID as randomUUID5 } from "crypto";
5129
5220
 
5130
5221
  // packages/core/src/agent-models.ts
5131
- import fs14 from "fs";
5132
- import os5 from "os";
5133
- import path17 from "path";
5222
+ import fs15 from "fs";
5223
+ import os6 from "os";
5224
+ import path18 from "path";
5134
5225
 
5135
5226
  // packages/core/src/claude-models.ts
5136
5227
  init_effort();
5137
- import fs13 from "fs";
5138
- import os3 from "os";
5139
- import path16 from "path";
5228
+ import fs14 from "fs";
5229
+ import os4 from "os";
5230
+ import path17 from "path";
5140
5231
 
5141
5232
  // packages/core/src/json-lines-process.ts
5142
5233
  import { spawn as spawn4 } from "child_process";
@@ -5240,11 +5331,11 @@ var CLAUDE_MODELS_TIMEOUT_MS = 15e3;
5240
5331
  var REQUEST_ID = "vibeflow-models";
5241
5332
  var CLAUDE_ARGS = ["-p", "--input-format", "stream-json", "--output-format", "stream-json", "--verbose"];
5242
5333
  function openClaude() {
5243
- const cwd = fs13.mkdtempSync(path16.join(os3.tmpdir(), "vf-claude-models-"));
5334
+ const cwd = fs14.mkdtempSync(path17.join(os4.tmpdir(), "vf-claude-models-"));
5244
5335
  return spawnJsonLines("claude", CLAUDE_ARGS, {
5245
5336
  cwd,
5246
5337
  timeoutMs: CLAUDE_MODELS_TIMEOUT_MS,
5247
- onExit: () => fs13.rmSync(cwd, { recursive: true, force: true })
5338
+ onExit: () => fs14.rmSync(cwd, { recursive: true, force: true })
5248
5339
  });
5249
5340
  }
5250
5341
  function isRecord(value) {
@@ -5291,7 +5382,7 @@ async function fetchClaudeModels(open = openClaude) {
5291
5382
  // packages/core/src/codex-models.ts
5292
5383
  init_effort();
5293
5384
  init_env();
5294
- import os4 from "os";
5385
+ import os5 from "os";
5295
5386
  var CODEX_MODELS_TIMEOUT_MS = 2e4;
5296
5387
  var MAX_PAGES = 20;
5297
5388
  var CodexLoginRequiredError = class extends Error {
@@ -5304,7 +5395,7 @@ function codexAppServerCommand() {
5304
5395
  return {
5305
5396
  bin: "codex",
5306
5397
  args: ["app-server", "--listen", "stdio://"],
5307
- cwd: os4.homedir(),
5398
+ cwd: os5.homedir(),
5308
5399
  env: execEnv()
5309
5400
  };
5310
5401
  }
@@ -5393,7 +5484,7 @@ function builtinModels(id) {
5393
5484
  }
5394
5485
  function defaultCodexHome() {
5395
5486
  const configured = process.env.CODEX_HOME?.trim();
5396
- return configured ? configured : path17.join(os5.homedir(), ".codex");
5487
+ return configured ? configured : path18.join(os6.homedir(), ".codex");
5397
5488
  }
5398
5489
  function parseCodexCatalog(raw) {
5399
5490
  const body = JSON.parse(raw);
@@ -5419,9 +5510,9 @@ function errorMessage(err) {
5419
5510
  return err instanceof Error ? err.message : String(err);
5420
5511
  }
5421
5512
  async function readCodexCache(codexHome) {
5422
- const cachePath = path17.join(codexHome, "models_cache.json");
5513
+ const cachePath = path18.join(codexHome, "models_cache.json");
5423
5514
  try {
5424
- const raw = await fs14.promises.readFile(cachePath, "utf8");
5515
+ const raw = await fs15.promises.readFile(cachePath, "utf8");
5425
5516
  return { models: parseCodexCatalog(raw), source: "codex-cache" };
5426
5517
  } catch (err) {
5427
5518
  const missing = err.code === "ENOENT";
@@ -5543,11 +5634,11 @@ function effortDescription(level) {
5543
5634
  }
5544
5635
 
5545
5636
  // packages/core/src/jev-credentials.ts
5546
- import fs15 from "fs";
5547
- import path18 from "path";
5637
+ import fs16 from "fs";
5638
+ import path19 from "path";
5548
5639
  import { randomUUID as randomUUID3 } from "crypto";
5549
5640
  function keyPath(dir) {
5550
- return path18.join(dir, "jev-api-key");
5641
+ return path19.join(dir, "jev-api-key");
5551
5642
  }
5552
5643
  function dataDir() {
5553
5644
  return getPlatform().userDataDir();
@@ -5555,8 +5646,8 @@ function dataDir() {
5555
5646
  function readJevApiKey(dir = dataDir()) {
5556
5647
  try {
5557
5648
  const file = keyPath(dir);
5558
- if (fs15.lstatSync(file).isSymbolicLink()) throw new Error("Jev key file must not be a symlink");
5559
- const saved = fs15.readFileSync(file, "utf8").trim();
5649
+ if (fs16.lstatSync(file).isSymbolicLink()) throw new Error("Jev key file must not be a symlink");
5650
+ const saved = fs16.readFileSync(file, "utf8").trim();
5560
5651
  if (saved) return saved;
5561
5652
  } catch (err) {
5562
5653
  if (err.code !== "ENOENT") throw err;
@@ -5566,8 +5657,8 @@ function readJevApiKey(dir = dataDir()) {
5566
5657
  function jevKeyStatus(dir = dataDir()) {
5567
5658
  try {
5568
5659
  const file = keyPath(dir);
5569
- if (fs15.lstatSync(file).isSymbolicLink()) throw new Error("Jev key file must not be a symlink");
5570
- if (fs15.readFileSync(file, "utf8").trim()) return { configured: true, source: "saved" };
5660
+ if (fs16.lstatSync(file).isSymbolicLink()) throw new Error("Jev key file must not be a symlink");
5661
+ if (fs16.readFileSync(file, "utf8").trim()) return { configured: true, source: "saved" };
5571
5662
  } catch (err) {
5572
5663
  if (err.code !== "ENOENT") throw err;
5573
5664
  }
@@ -5578,20 +5669,20 @@ function saveJevApiKey(value, dir = dataDir()) {
5578
5669
  if (!key || key.length > 4096 || !/^[\x21-\x7e]+$/.test(key)) {
5579
5670
  throw new Error("Jev API key \u683C\u5F0F\u7121\u6548");
5580
5671
  }
5581
- fs15.mkdirSync(dir, { recursive: true, mode: 448 });
5672
+ fs16.mkdirSync(dir, { recursive: true, mode: 448 });
5582
5673
  const file = keyPath(dir);
5583
5674
  const tmp = `${file}.${randomUUID3()}.tmp`;
5584
5675
  try {
5585
- fs15.writeFileSync(tmp, key, { mode: 384, flag: "wx" });
5586
- fs15.renameSync(tmp, file);
5587
- if (process.platform !== "win32") fs15.chmodSync(file, 384);
5676
+ fs16.writeFileSync(tmp, key, { mode: 384, flag: "wx" });
5677
+ fs16.renameSync(tmp, file);
5678
+ if (process.platform !== "win32") fs16.chmodSync(file, 384);
5588
5679
  } finally {
5589
- fs15.rmSync(tmp, { force: true });
5680
+ fs16.rmSync(tmp, { force: true });
5590
5681
  }
5591
5682
  return { configured: true, source: "saved" };
5592
5683
  }
5593
5684
  function removeJevApiKey(dir = dataDir()) {
5594
- fs15.rmSync(keyPath(dir), { force: true });
5685
+ fs16.rmSync(keyPath(dir), { force: true });
5595
5686
  return jevKeyStatus(dir);
5596
5687
  }
5597
5688
 
@@ -6103,45 +6194,45 @@ function createJiraService(options) {
6103
6194
  }
6104
6195
 
6105
6196
  // packages/core/src/board-cli.ts
6106
- import fs16 from "fs";
6107
- import path19 from "path";
6197
+ import fs17 from "fs";
6198
+ import path20 from "path";
6108
6199
  import { pathToFileURL } from "url";
6109
6200
  async function boardCliLaunchInfo() {
6110
- const storeDir = path19.dirname(getStorePath());
6201
+ const storeDir = path20.dirname(getStorePath());
6111
6202
  const entry = getPlatform().cliEntry?.();
6112
- if (entry && fs16.existsSync(entry)) return { storeDir, cliPath: entry };
6203
+ if (entry && fs17.existsSync(entry)) return { storeDir, cliPath: entry };
6113
6204
  const root = getPlatform().sourceRoot();
6114
6205
  if (!root) return { storeDir };
6115
- const cliPath = path19.join(root, "scripts", "vibeflow.mjs");
6116
- const loaderPath = path19.join(root, "test", "support", "register.mjs");
6117
- if (!fs16.existsSync(cliPath) || !fs16.existsSync(loaderPath)) return { storeDir };
6206
+ const cliPath = path20.join(root, "scripts", "vibeflow.mjs");
6207
+ const loaderPath = path20.join(root, "test", "support", "register.mjs");
6208
+ if (!fs17.existsSync(cliPath) || !fs17.existsSync(loaderPath)) return { storeDir };
6118
6209
  return { storeDir, cliPath, loaderPath: pathToFileURL(loaderPath).href };
6119
6210
  }
6120
6211
 
6121
6212
  // packages/core/src/decisions.ts
6122
- import fs17 from "fs";
6123
- import path20 from "path";
6213
+ import fs18 from "fs";
6214
+ import path21 from "path";
6124
6215
  var DECISIONS_FILE_SUFFIX = ".DECISIONS.md";
6125
6216
  function decisionsKey(worktreePath, branch) {
6126
- return worktreePath ? path20.basename(worktreePath) : branch.replace(/\//g, "-");
6217
+ return worktreePath ? path21.basename(worktreePath) : branch.replace(/\//g, "-");
6127
6218
  }
6128
6219
  function decisionsPath(workspacePath, key) {
6129
- return path20.join(workspacePath, `${key}${DECISIONS_FILE_SUFFIX}`);
6220
+ return path21.join(workspacePath, `${key}${DECISIONS_FILE_SUFFIX}`);
6130
6221
  }
6131
6222
  function deleteDecisions(workspacePath, key) {
6132
6223
  try {
6133
- fs17.rmSync(decisionsPath(workspacePath, key), { force: true });
6224
+ fs18.rmSync(decisionsPath(workspacePath, key), { force: true });
6134
6225
  } catch {
6135
6226
  }
6136
6227
  }
6137
6228
 
6138
6229
  // packages/core/src/specs.ts
6139
- import fs18 from "fs";
6140
- import path21 from "path";
6230
+ import fs19 from "fs";
6231
+ import path22 from "path";
6141
6232
  var SPEC_FILE_NAME = "spec.md";
6142
6233
  var MAX_SPEC_BYTES = 256 * 1024;
6143
6234
  function isSpecPath(p) {
6144
- return path21.posix.basename(p) === SPEC_FILE_NAME;
6235
+ return path22.posix.basename(p) === SPEC_FILE_NAME;
6145
6236
  }
6146
6237
  function clip2(buf) {
6147
6238
  return {
@@ -6156,10 +6247,10 @@ async function readWorktreeSpecs(worktreePath, baseBranch) {
6156
6247
  for (const entry of entries) {
6157
6248
  if (entry.status === "D" || !isSpecPath(entry.path)) continue;
6158
6249
  try {
6159
- const full = path21.join(worktreePath, entry.path);
6160
- const stat = fs18.statSync(full);
6250
+ const full = path22.join(worktreePath, entry.path);
6251
+ const stat = fs19.statSync(full);
6161
6252
  if (!stat.isFile()) continue;
6162
- specs.push({ path: entry.path, ...clip2(fs18.readFileSync(full)), updatedAt: stat.mtimeMs });
6253
+ specs.push({ path: entry.path, ...clip2(fs19.readFileSync(full)), updatedAt: stat.mtimeMs });
6163
6254
  } catch {
6164
6255
  }
6165
6256
  }
@@ -6511,17 +6602,17 @@ init_launch();
6511
6602
  // packages/core/src/progress-tracker.ts
6512
6603
  init_progress();
6513
6604
  init_launch();
6514
- import fs19 from "fs";
6515
- import os6 from "os";
6516
- import path22 from "path";
6605
+ import fs20 from "fs";
6606
+ import os7 from "os";
6607
+ import path23 from "path";
6517
6608
  var SINCE_SLACK_MS = 5e3;
6518
6609
  function claudeProjectDir(home, cwd) {
6519
- return path22.join(home, ".claude", "projects", cwd.replace(/[^a-zA-Z0-9]/g, "-"));
6610
+ return path23.join(home, ".claude", "projects", cwd.replace(/[^a-zA-Z0-9]/g, "-"));
6520
6611
  }
6521
6612
  function pathAliases(p) {
6522
- const resolved = path22.resolve(p);
6613
+ const resolved = path23.resolve(p);
6523
6614
  try {
6524
- const real = fs19.realpathSync(resolved);
6615
+ const real = fs20.realpathSync(resolved);
6525
6616
  return real === resolved ? [resolved] : [resolved, real];
6526
6617
  } catch {
6527
6618
  return [resolved];
@@ -6537,7 +6628,7 @@ function samePath(a, b) {
6537
6628
  }
6538
6629
  function listFiles(dir, test) {
6539
6630
  try {
6540
- return fs19.readdirSync(dir).filter(test).map((name) => path22.join(dir, name));
6631
+ return fs20.readdirSync(dir).filter(test).map((name) => path23.join(dir, name));
6541
6632
  } catch {
6542
6633
  return [];
6543
6634
  }
@@ -6545,22 +6636,22 @@ function listFiles(dir, test) {
6545
6636
  function firstLine(file) {
6546
6637
  let fd;
6547
6638
  try {
6548
- fd = fs19.openSync(file, "r");
6639
+ fd = fs20.openSync(file, "r");
6549
6640
  const buf = Buffer.alloc(64 * 1024);
6550
- const n = fs19.readSync(fd, buf, 0, buf.length, 0);
6641
+ const n = fs20.readSync(fd, buf, 0, buf.length, 0);
6551
6642
  const text = buf.subarray(0, n).toString("utf8");
6552
6643
  const nl = text.indexOf("\n");
6553
6644
  return nl === -1 ? null : text.slice(0, nl);
6554
6645
  } catch {
6555
6646
  return null;
6556
6647
  } finally {
6557
- if (fd !== void 0) fs19.closeSync(fd);
6648
+ if (fd !== void 0) fs20.closeSync(fd);
6558
6649
  }
6559
6650
  }
6560
6651
  function advance(file, cursor, reduce, initial) {
6561
6652
  let size;
6562
6653
  try {
6563
- size = fs19.statSync(file).size;
6654
+ size = fs20.statSync(file).size;
6564
6655
  } catch {
6565
6656
  return;
6566
6657
  }
@@ -6572,9 +6663,9 @@ function advance(file, cursor, reduce, initial) {
6572
6663
  if (size === cursor.offset) return;
6573
6664
  let fd;
6574
6665
  try {
6575
- fd = fs19.openSync(file, "r");
6666
+ fd = fs20.openSync(file, "r");
6576
6667
  const chunk = Buffer.alloc(size - cursor.offset);
6577
- const n = fs19.readSync(fd, chunk, 0, chunk.length, cursor.offset);
6668
+ const n = fs20.readSync(fd, chunk, 0, chunk.length, cursor.offset);
6578
6669
  cursor.offset += n;
6579
6670
  const data = Buffer.concat([cursor.rest, chunk.subarray(0, n)]);
6580
6671
  const nl = data.lastIndexOf(10);
@@ -6588,7 +6679,7 @@ function advance(file, cursor, reduce, initial) {
6588
6679
  }
6589
6680
  } catch {
6590
6681
  } finally {
6591
- if (fd !== void 0) fs19.closeSync(fd);
6682
+ if (fd !== void 0) fs20.closeSync(fd);
6592
6683
  }
6593
6684
  }
6594
6685
  function newCursor(state) {
@@ -6607,14 +6698,14 @@ function codexDayDirs(sessionsRoot, since) {
6607
6698
  const cur = new Date(start.getFullYear(), start.getMonth(), start.getDate());
6608
6699
  while (cur <= end) {
6609
6700
  const pad = (n) => String(n).padStart(2, "0");
6610
- days.push(path22.join(sessionsRoot, String(cur.getFullYear()), pad(cur.getMonth() + 1), pad(cur.getDate())));
6701
+ days.push(path23.join(sessionsRoot, String(cur.getFullYear()), pad(cur.getMonth() + 1), pad(cur.getDate())));
6611
6702
  cur.setDate(cur.getDate() + 1);
6612
6703
  }
6613
6704
  return days;
6614
6705
  }
6615
6706
  function createProgressTracker(opts) {
6616
- const home = opts.homeDir ?? os6.homedir();
6617
- const codexHomes = opts.codexHomes ?? (() => [path22.join(home, ".codex")]);
6707
+ const home = opts.homeDir ?? os7.homedir();
6708
+ const codexHomes = opts.codexHomes ?? (() => [path23.join(home, ".codex")]);
6618
6709
  const pollMs = opts.pollMs ?? 2e3;
6619
6710
  const entries = /* @__PURE__ */ new Map();
6620
6711
  const belongsToRun = (target, startedAt) => target.since == null || startedAt != null && startedAt >= target.since - SINCE_SLACK_MS;
@@ -6623,7 +6714,7 @@ function createProgressTracker(opts) {
6623
6714
  const dirs = pathAliases(target.worktreePath).map((p) => claudeProjectDir(home, p));
6624
6715
  for (const file of dirs.flatMap((dir) => listFiles(dir, (n) => n.endsWith(".jsonl")))) {
6625
6716
  if (entry.claude.has(file) || entry.rejected.has(file)) continue;
6626
- const session = path22.basename(file, ".jsonl");
6717
+ const session = path23.basename(file, ".jsonl");
6627
6718
  if (session !== target.sessionId) {
6628
6719
  const head = firstLine(file);
6629
6720
  if (head == null) continue;
@@ -6636,8 +6727,8 @@ function createProgressTracker(opts) {
6636
6727
  entry.claude.set(file, newCursor(initialClaudeState()));
6637
6728
  }
6638
6729
  for (const file of Array.from(entry.claude.keys())) {
6639
- if (file.includes(`${path22.sep}subagents${path22.sep}`)) continue;
6640
- const sub = path22.join(file.slice(0, -".jsonl".length), "subagents");
6730
+ if (file.includes(`${path23.sep}subagents${path23.sep}`)) continue;
6731
+ const sub = path23.join(file.slice(0, -".jsonl".length), "subagents");
6641
6732
  for (const f of listFiles(sub, (n) => n.endsWith(".jsonl"))) {
6642
6733
  if (!entry.claude.has(f)) entry.claude.set(f, newCursor(initialClaudeState()));
6643
6734
  }
@@ -6646,7 +6737,7 @@ function createProgressTracker(opts) {
6646
6737
  function discoverCodex(entry) {
6647
6738
  const { target } = entry;
6648
6739
  for (const codexHome of codexHomes()) {
6649
- for (const day of codexDayDirs(path22.join(codexHome, "sessions"), target.since)) {
6740
+ for (const day of codexDayDirs(path23.join(codexHome, "sessions"), target.since)) {
6650
6741
  for (const file of listFiles(day, (n) => n.startsWith("rollout-") && n.endsWith(".jsonl"))) {
6651
6742
  if (entry.codex.has(file) || entry.rejected.has(file)) continue;
6652
6743
  const head = firstLine(file);
@@ -6663,11 +6754,11 @@ function createProgressTracker(opts) {
6663
6754
  }
6664
6755
  }
6665
6756
  function drainHooks(entry) {
6666
- const dir = path22.join(entry.target.worktreePath, PROGRESS_EVENTS_DIR);
6757
+ const dir = path23.join(entry.target.worktreePath, PROGRESS_EVENTS_DIR);
6667
6758
  for (const file of listFiles(dir, (n) => n.endsWith(".json")).sort()) {
6668
6759
  try {
6669
- const at = fs19.statSync(file).mtimeMs;
6670
- const event = JSON.parse(fs19.readFileSync(file, "utf8"));
6760
+ const at = fs20.statSync(file).mtimeMs;
6761
+ const event = JSON.parse(fs20.readFileSync(file, "utf8"));
6671
6762
  const foreign = entry.target.sessionId && typeof event.session_id === "string" && event.session_id !== entry.target.sessionId;
6672
6763
  const activity = foreign ? null : activityFromHook(event, at);
6673
6764
  if (activity) entry.hookActivity = latestActivity(entry.hookActivity ?? activity, activity);
@@ -6675,7 +6766,7 @@ function createProgressTracker(opts) {
6675
6766
  continue;
6676
6767
  }
6677
6768
  try {
6678
- fs19.unlinkSync(file);
6769
+ fs20.unlinkSync(file);
6679
6770
  } catch {
6680
6771
  }
6681
6772
  }
@@ -6687,8 +6778,8 @@ function createProgressTracker(opts) {
6687
6778
  if (target.agent === "claude") {
6688
6779
  for (const [file, cursor] of entry.claude) {
6689
6780
  runUsage = addUsage(runUsage, claudeUsageTotal(cursor.state));
6690
- if (file.includes(`${path22.sep}subagents${path22.sep}`)) continue;
6691
- const isExecutor = path22.basename(file, ".jsonl") === target.sessionId;
6781
+ if (file.includes(`${path23.sep}subagents${path23.sep}`)) continue;
6782
+ const isExecutor = path23.basename(file, ".jsonl") === target.sessionId;
6692
6783
  const s = cursor.state;
6693
6784
  if (isExecutor || !target.sessionId && (!primary || (s.startedAt ?? 0) > (primary.startedAt ?? 0))) {
6694
6785
  primary = { todos: s.todos, contextTokens: s.contextTokens, activity: s.activity, startedAt: s.startedAt };
@@ -6767,10 +6858,10 @@ function createProgressTracker(opts) {
6767
6858
  };
6768
6859
  entry.timer.unref?.();
6769
6860
  entries.set(target.taskId, entry);
6770
- const eventsDir = path22.join(target.worktreePath, PROGRESS_EVENTS_DIR);
6861
+ const eventsDir = path23.join(target.worktreePath, PROGRESS_EVENTS_DIR);
6771
6862
  try {
6772
- fs19.mkdirSync(eventsDir, { recursive: true });
6773
- entry.watcher = fs19.watch(eventsDir, () => {
6863
+ fs20.mkdirSync(eventsDir, { recursive: true });
6864
+ entry.watcher = fs20.watch(eventsDir, () => {
6774
6865
  if (entry.pending) return;
6775
6866
  entry.pending = setTimeout(() => {
6776
6867
  entry.pending = null;
@@ -6862,15 +6953,16 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
6862
6953
  const router = jevRouter ?? createJevRouter({ getApiKey: readJevApiKey });
6863
6954
  const routing = /* @__PURE__ */ new Map();
6864
6955
  const standaloneTerminals = /* @__PURE__ */ new Map();
6956
+ const terminalDirectories = /* @__PURE__ */ new Map();
6865
6957
  let libraryCodexHome = null;
6866
6958
  void libraryRoot().then((root) => {
6867
6959
  libraryCodexHome = libraryPaths(root).codexHome;
6868
6960
  }).catch(() => {
6869
6961
  });
6870
- const home = homeDir ?? os7.homedir();
6962
+ const home = homeDir ?? os8.homedir();
6871
6963
  const progress = createProgressTracker({
6872
6964
  homeDir: home,
6873
- codexHomes: () => [path23.join(home, ".codex"), ...libraryCodexHome ? [libraryCodexHome] : []],
6965
+ codexHomes: () => [path24.join(home, ".codex"), ...libraryCodexHome ? [libraryCodexHome] : []],
6874
6966
  onUpdate: (taskId, value) => bus.emit("progress:update", { taskId, progress: value }),
6875
6967
  onNotify: (taskId, list) => {
6876
6968
  const prefs = resolveNotificationSettings(getSettings());
@@ -6923,6 +7015,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
6923
7015
  removeTerminals([key]);
6924
7016
  }
6925
7017
  standaloneTerminals.delete(key);
7018
+ terminalDirectories.delete(key);
6926
7019
  unwatchSubAgents(key);
6927
7020
  }
6928
7021
  function isStandaloneKey(key) {
@@ -6944,6 +7037,21 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
6944
7037
  requireTask(key.split(":")[0]);
6945
7038
  return key;
6946
7039
  }
7040
+ function terminalDirectory(sessionKey) {
7041
+ const key = requireSessionKey(sessionKey);
7042
+ const known = standaloneTerminals.get(key) ?? terminalDirectories.get(key);
7043
+ if (known) return known;
7044
+ if (isStandaloneKey(key)) {
7045
+ const saved = listTerminals().find((item) => item.sessionKey === key);
7046
+ if (saved?.taskId) {
7047
+ const task2 = requireTask(saved.taskId);
7048
+ return str(task2.worktreePath ?? task2.projectPath, "directory");
7049
+ }
7050
+ return saved?.projectPath ?? os8.homedir();
7051
+ }
7052
+ const task = requireTask(key.split(":")[0]);
7053
+ return str(task.worktreePath ?? task.projectPath, "directory");
7054
+ }
6947
7055
  function launchEffort(task, route) {
6948
7056
  const effort = task.effort === "auto" ? route?.effort : task.effort;
6949
7057
  const model = models.findModel(taskAgent(task), taskModel(task));
@@ -6988,13 +7096,13 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
6988
7096
  }
6989
7097
  function claudeSessionFile(cwd, sessionId) {
6990
7098
  const munged = cwd.replace(/[^a-zA-Z0-9]/g, "-");
6991
- return path23.join(os7.homedir(), ".claude", "projects", munged, `${sessionId}.jsonl`);
7099
+ return path24.join(os8.homedir(), ".claude", "projects", munged, `${sessionId}.jsonl`);
6992
7100
  }
6993
7101
  async function removeTaskWorktree(task) {
6994
7102
  if (!task.projectPath || !task.worktreePath) return;
6995
7103
  const branch = task.branch || fallbackBranchName(task.id);
6996
7104
  await removeWorktree(task.projectPath, task.worktreePath);
6997
- deleteArtifacts(task.workspacePath ?? path23.dirname(task.worktreePath), task.worktreePath);
7105
+ deleteArtifacts(task.workspacePath ?? path24.dirname(task.worktreePath), task.worktreePath);
6998
7106
  await deleteBranch(task.projectPath, branch);
6999
7107
  }
7000
7108
  function columnOf(taskId) {
@@ -7139,8 +7247,8 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
7139
7247
  "projects:listRecent": () => listRecentProjects(getStore()),
7140
7248
  // `vibeflow open <path>` while a host runs: only the host writes the store.
7141
7249
  "projects:record": (projectPath) => {
7142
- const p = path23.resolve(str(projectPath, "projectPath"));
7143
- if (!fs20.existsSync(p) || !fs20.statSync(p).isDirectory()) invalid(`not a directory: ${p}`);
7250
+ const p = path24.resolve(str(projectPath, "projectPath"));
7251
+ if (!fs21.existsSync(p) || !fs21.statSync(p).isDirectory()) invalid(`not a directory: ${p}`);
7144
7252
  recordRecentProject(getStore(), p);
7145
7253
  return listRecentProjects(getStore());
7146
7254
  },
@@ -7193,7 +7301,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
7193
7301
  if (!task.baseBranch) throw new Error("\u4EFB\u52D9\u6C92\u6709\u8A18\u9304 base branch\uFF0C\u7121\u6CD5\u9084\u539F worktree");
7194
7302
  await teardownTask(task.id);
7195
7303
  await resetWorktreeToBase(task.worktreePath, task.baseBranch);
7196
- const workspacePath = task.workspacePath ?? path23.dirname(task.worktreePath);
7304
+ const workspacePath = task.workspacePath ?? path24.dirname(task.worktreePath);
7197
7305
  deleteArtifacts(workspacePath, task.worktreePath);
7198
7306
  deleteDecisions(workspacePath, decisionsKey(task.worktreePath, task.branch));
7199
7307
  resetSubAgents(task.worktreePath);
@@ -7237,7 +7345,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
7237
7345
  }
7238
7346
  const info = await getGitInfo(nextProject);
7239
7347
  if (!info.isRepo) throw new Error("\u76EE\u6A19\u8CC7\u6599\u593E\u4E0D\u662F git repository");
7240
- const projectName = path23.basename(nextProject);
7348
+ const projectName = path24.basename(nextProject);
7241
7349
  const workspacePath = projectWorkstationPath(resolveWorkstationPath(getSettings()), projectName);
7242
7350
  await teardownTask(existing.id);
7243
7351
  await removeTaskWorktree(existing);
@@ -7299,6 +7407,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
7299
7407
  // Session ended (natural exit included), so stop its watcher.
7300
7408
  onExit: () => unwatchSubAgents(key)
7301
7409
  });
7410
+ terminalDirectories.set(key, cwd);
7302
7411
  watchSubAgents(key, cwd, (subAgents) => {
7303
7412
  bus.emit("subagents:update", { taskId: task.id, subAgents });
7304
7413
  });
@@ -7329,12 +7438,12 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
7329
7438
  cwd = str(p.projectPath, "projectPath");
7330
7439
  projectPath = cwd;
7331
7440
  } else {
7332
- cwd = os7.homedir();
7441
+ cwd = os8.homedir();
7333
7442
  }
7334
- if (!path23.isAbsolute(cwd) || !fs20.existsSync(cwd) || !fs20.statSync(cwd).isDirectory()) {
7443
+ if (!path24.isAbsolute(cwd) || !fs21.existsSync(cwd) || !fs21.statSync(cwd).isDirectory()) {
7335
7444
  invalid("terminal directory must be an existing absolute directory");
7336
7445
  }
7337
- const title = (typeof p.title === "string" ? p.title.trim() : "") || saved?.title || (projectPath ? path23.basename(projectPath) : "~");
7446
+ const title = (typeof p.title === "string" ? p.title.trim() : "") || saved?.title || (projectPath ? path24.basename(projectPath) : "~");
7338
7447
  saveTerminal({
7339
7448
  sessionKey: key,
7340
7449
  title,
@@ -7351,6 +7460,26 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
7351
7460
  }
7352
7461
  return sessions.start(key, { cwd, cols, rows });
7353
7462
  },
7463
+ "terminal:readFile": async (payload) => {
7464
+ const p = obj(payload, "payload");
7465
+ return readTerminalFile(terminalDirectory(p.sessionKey), str(p.path, "path"));
7466
+ },
7467
+ "terminal:existingFiles": async (payload) => {
7468
+ const p = obj(payload, "payload");
7469
+ if (!Array.isArray(p.paths)) invalid("paths must be an array");
7470
+ return existingTerminalFiles(terminalDirectory(p.sessionKey), p.paths.map((value) => str(value, "path")));
7471
+ },
7472
+ "terminal:openFile": async (payload) => {
7473
+ const p = obj(payload, "payload");
7474
+ const file = await readTerminalFile(terminalDirectory(p.sessionKey), str(p.path, "path"), true);
7475
+ if (!file.ok) return { ok: false, message: file.message };
7476
+ try {
7477
+ const message = await getPlatform().openPath(file.path);
7478
+ return message ? { ok: false, message } : { ok: true };
7479
+ } catch (error) {
7480
+ return { ok: false, message: error instanceof Error ? error.message : "\u7121\u6CD5\u958B\u555F\u6A94\u6848\u3002" };
7481
+ }
7482
+ },
7354
7483
  "terminal:rename": (payload) => {
7355
7484
  const p = obj(payload, "payload");
7356
7485
  const key = str(p.sessionKey, "sessionKey");
@@ -7389,7 +7518,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
7389
7518
  "claude:sessionExists": (taskId) => {
7390
7519
  const task = requireTask(taskId);
7391
7520
  if (!task.worktreePath) return false;
7392
- return fs20.existsSync(claudeSessionFile(task.worktreePath, executorSessionId(task.id, task.runId)));
7521
+ return fs21.existsSync(claudeSessionFile(task.worktreePath, executorSessionId(task.id, task.runId)));
7393
7522
  },
7394
7523
  "sessions:list": async () => {
7395
7524
  const keys = await sessions.list();
@@ -7436,7 +7565,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
7436
7565
  const task = requireTask(taskId);
7437
7566
  if (!task.worktreePath || !task.workspacePath) return "task has no worktree";
7438
7567
  const dir = agentArtifactsPath(task.workspacePath, task.worktreePath);
7439
- if (!fs20.existsSync(dir)) return "artifacts directory does not exist yet";
7568
+ if (!fs21.existsSync(dir)) return "artifacts directory does not exist yet";
7440
7569
  return getPlatform().openPath(dir);
7441
7570
  },
7442
7571
  "task:getSpecs": async (taskId) => {
@@ -7571,7 +7700,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
7571
7700
  }
7572
7701
  await removeTaskWorktree(task);
7573
7702
  if (task.workspacePath) {
7574
- fs20.rmSync(taskAttachmentStagingPath(task.workspacePath, task.id), { recursive: true, force: true });
7703
+ fs21.rmSync(taskAttachmentStagingPath(task.workspacePath, task.id), { recursive: true, force: true });
7575
7704
  }
7576
7705
  clearConversation(task.id);
7577
7706
  removeTask(task.id);
@@ -7624,9 +7753,9 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
7624
7753
  // Watch the containing directory, not the file: JsonStore renames a temp
7625
7754
  // file over the target and so replaces the inode on every save.
7626
7755
  watchStore() {
7627
- const file = path23.basename(getStorePath());
7756
+ const file = path24.basename(getStorePath());
7628
7757
  let debounce = null;
7629
- storeWatcher = fs20.watch(path23.dirname(getStorePath()), (_event, filename) => {
7758
+ storeWatcher = fs21.watch(path24.dirname(getStorePath()), (_event, filename) => {
7630
7759
  if (filename !== file) return;
7631
7760
  if (debounce) clearTimeout(debounce);
7632
7761
  debounce = setTimeout(() => {
@@ -7663,9 +7792,9 @@ function createSessionBackend(sink, choice) {
7663
7792
  }
7664
7793
 
7665
7794
  // packages/core/src/web-server.ts
7666
- import fs21 from "fs";
7795
+ import fs22 from "fs";
7667
7796
  import http from "http";
7668
- import path24 from "path";
7797
+ import path25 from "path";
7669
7798
  import { timingSafeEqual } from "crypto";
7670
7799
  import { WebSocketServer } from "ws";
7671
7800
  var TOKEN_COOKIE = "vf_token";
@@ -7720,14 +7849,14 @@ function resolveStatic(root, urlPath) {
7720
7849
  return null;
7721
7850
  }
7722
7851
  if (decoded.includes("\0")) return null;
7723
- const rel = path24.posix.normalize(decoded).replace(/^\/+/, "");
7724
- const base2 = path24.resolve(root);
7725
- const candidates = [rel, path24.posix.join(rel, "index.html"), `${rel}.html`];
7852
+ const rel = path25.posix.normalize(decoded).replace(/^\/+/, "");
7853
+ const base2 = path25.resolve(root);
7854
+ const candidates = [rel, path25.posix.join(rel, "index.html"), `${rel}.html`];
7726
7855
  for (const c of candidates) {
7727
- const full = path24.resolve(base2, c);
7728
- if (full !== base2 && !full.startsWith(base2 + path24.sep)) return null;
7856
+ const full = path25.resolve(base2, c);
7857
+ if (full !== base2 && !full.startsWith(base2 + path25.sep)) return null;
7729
7858
  try {
7730
- if (fs21.statSync(full).isFile()) return full;
7859
+ if (fs22.statSync(full).isFile()) return full;
7731
7860
  } catch {
7732
7861
  }
7733
7862
  }
@@ -7788,7 +7917,7 @@ async function startWebServer(options) {
7788
7917
  send(res, 404, "Not found");
7789
7918
  return;
7790
7919
  }
7791
- const ext = path24.extname(file).toLowerCase();
7920
+ const ext = path25.extname(file).toLowerCase();
7792
7921
  const immutable = url.pathname.startsWith("/_next/static/");
7793
7922
  res.writeHead(200, {
7794
7923
  "Content-Type": CONTENT_TYPES[ext] ?? "application/octet-stream",
@@ -7801,7 +7930,7 @@ async function startWebServer(options) {
7801
7930
  res.end();
7802
7931
  return;
7803
7932
  }
7804
- fs21.createReadStream(file).pipe(res);
7933
+ fs22.createReadStream(file).pipe(res);
7805
7934
  }
7806
7935
  const wss = new WebSocketServer({ noServer: true, maxPayload: MAX_PAYLOAD });
7807
7936
  function reject(socket, status, reason) {
@@ -7900,8 +8029,8 @@ Content-Length: 0\r
7900
8029
  }
7901
8030
 
7902
8031
  // packages/cli/src/update-command.ts
7903
- import fs22 from "node:fs";
7904
- import path25 from "node:path";
8032
+ import fs23 from "node:fs";
8033
+ import path26 from "node:path";
7905
8034
  import { spawnSync } from "node:child_process";
7906
8035
  function npmRunner(args, inherit) {
7907
8036
  const spawnOpts = {
@@ -7913,7 +8042,7 @@ function npmRunner(args, inherit) {
7913
8042
  }
7914
8043
  function packageName(packageRoot) {
7915
8044
  try {
7916
- const pkg = JSON.parse(fs22.readFileSync(path25.join(packageRoot, "package.json"), "utf8"));
8045
+ const pkg = JSON.parse(fs23.readFileSync(path26.join(packageRoot, "package.json"), "utf8"));
7917
8046
  if (typeof pkg.name !== "string") return null;
7918
8047
  return /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/i.test(pkg.name) ? pkg.name : null;
7919
8048
  } catch {
@@ -7928,7 +8057,7 @@ function runUpdate(options) {
7928
8057
  error("vibeflow update \u53EA\u9069\u7528\u65BC\u5168\u57DF npm \u5B89\u88DD\uFF1B\u76EE\u524D\u662F\u539F\u59CB\u78BC\u7248\u672C\u3002");
7929
8058
  return 1;
7930
8059
  }
7931
- const packageRoot = path25.dirname(path25.dirname(options.cliEntry));
8060
+ const packageRoot = path26.dirname(path26.dirname(options.cliEntry));
7932
8061
  const name = packageName(packageRoot);
7933
8062
  if (!name) {
7934
8063
  error("\u7121\u6CD5\u8FA8\u8B58\u76EE\u524D VibeFlow \u7684 npm \u5957\u4EF6\u540D\u7A31\uFF0C\u5DF2\u53D6\u6D88\u66F4\u65B0\u3002");
@@ -7940,8 +8069,8 @@ function runUpdate(options) {
7940
8069
  return 1;
7941
8070
  }
7942
8071
  try {
7943
- const globalPackage = path25.join(String(root.stdout).trim(), ...name.split("/"));
7944
- if (fs22.lstatSync(globalPackage).isSymbolicLink() || fs22.realpathSync(globalPackage) !== fs22.realpathSync(packageRoot)) {
8072
+ const globalPackage = path26.join(String(root.stdout).trim(), ...name.split("/"));
8073
+ if (fs23.lstatSync(globalPackage).isSymbolicLink() || fs23.realpathSync(globalPackage) !== fs23.realpathSync(packageRoot)) {
7945
8074
  error("vibeflow update \u53EA\u9069\u7528\u65BC\u5168\u57DF npm \u5B89\u88DD\uFF1B\u76EE\u524D\u57F7\u884C\u7684\u5957\u4EF6\u4F86\u81EA\u5176\u4ED6\u4F4D\u7F6E\u3002");
7946
8075
  return 1;
7947
8076
  }
@@ -8086,8 +8215,8 @@ async function openUpdateTerminal(platform = process.platform, run3 = launch) {
8086
8215
  }
8087
8216
 
8088
8217
  // packages/cli/src/self-update.ts
8089
- import fs23 from "node:fs";
8090
- import path26 from "node:path";
8218
+ import fs24 from "node:fs";
8219
+ import path27 from "node:path";
8091
8220
  import { execFile as execFile10 } from "node:child_process";
8092
8221
  import { promisify as promisify7 } from "node:util";
8093
8222
  var execFileAsync2 = promisify7(execFile10);
@@ -8096,7 +8225,7 @@ var PROBE_RETRY_MS = 3e4;
8096
8225
  var HOST_TOKEN_ENV = "VIBEFLOW_HOST_TOKEN";
8097
8226
  function readInstalledVersion(packageRoot) {
8098
8227
  try {
8099
- const pkg = JSON.parse(fs23.readFileSync(path26.join(packageRoot, "package.json"), "utf8"));
8228
+ const pkg = JSON.parse(fs24.readFileSync(path27.join(packageRoot, "package.json"), "utf8"));
8100
8229
  return typeof pkg.version === "string" ? pkg.version : null;
8101
8230
  } catch {
8102
8231
  return null;
@@ -8192,7 +8321,7 @@ async function startHost(options) {
8192
8321
  const core = createCore({ sessions, bus, version: options.version });
8193
8322
  let updateTimer = null;
8194
8323
  let installTimer = null;
8195
- const packageRoot = options.cliEntry ? path27.dirname(path27.dirname(options.cliEntry)) : null;
8324
+ const packageRoot = options.cliEntry ? path28.dirname(path28.dirname(options.cliEntry)) : null;
8196
8325
  const updates = createUpdateMonitor({
8197
8326
  packageName: options.sourceRoot || !packageRoot ? null : packageName(packageRoot),
8198
8327
  currentVersion: options.version,
@@ -8280,7 +8409,7 @@ function loginUrl(lock, pathname = "/home/") {
8280
8409
  return `http://127.0.0.1:${lock.port}${pathname}?token=${encodeURIComponent(lock.token)}`;
8281
8410
  }
8282
8411
  function isWebDir(dir) {
8283
- return fs24.existsSync(path27.join(dir, "home", "index.html"));
8412
+ return fs25.existsSync(path28.join(dir, "home", "index.html"));
8284
8413
  }
8285
8414
 
8286
8415
  // packages/cli/src/remote.ts
@@ -8397,8 +8526,8 @@ function connectToHost(lock) {
8397
8526
  }
8398
8527
 
8399
8528
  // packages/cli/src/doctor.ts
8400
- import fs25 from "fs";
8401
- import path28 from "path";
8529
+ import fs26 from "fs";
8530
+ import path29 from "path";
8402
8531
  import { execFile as execFile11 } from "child_process";
8403
8532
  import { promisify as promisify8 } from "util";
8404
8533
  init_env();
@@ -8538,10 +8667,10 @@ async function runDoctor(options) {
8538
8667
  });
8539
8668
  }
8540
8669
  try {
8541
- fs25.mkdirSync(options.userDataDir, { recursive: true });
8542
- const probe = path28.join(options.userDataDir, `.doctor-${process.pid}`);
8543
- fs25.writeFileSync(probe, "");
8544
- fs25.rmSync(probe);
8670
+ fs26.mkdirSync(options.userDataDir, { recursive: true });
8671
+ const probe = path29.join(options.userDataDir, `.doctor-${process.pid}`);
8672
+ fs26.writeFileSync(probe, "");
8673
+ fs26.rmSync(probe);
8545
8674
  checks.push({ id: "data", name: "\u8CC7\u6599\u76EE\u9304", level: "ok", detail: options.userDataDir });
8546
8675
  } catch (err) {
8547
8676
  checks.push({
@@ -8595,25 +8724,25 @@ Options:
8595
8724
  -h, --help Show this help
8596
8725
  `.trim();
8597
8726
  function detectLayout() {
8598
- const file = fileURLToPath(import.meta.url);
8599
- const here = path31.dirname(file);
8600
- const bundledWeb = path31.join(here, "web");
8601
- if (isWebDir(bundledWeb) || path31.basename(here) === "dist") {
8727
+ const file = fileURLToPath2(import.meta.url);
8728
+ const here = path32.dirname(file);
8729
+ const bundledWeb = path32.join(here, "web");
8730
+ if (isWebDir(bundledWeb) || path32.basename(here) === "dist") {
8602
8731
  return {
8603
- version: readVersion(path31.join(here, "..", "package.json")),
8732
+ version: readVersion(path32.join(here, "..", "package.json")),
8604
8733
  webDir: isWebDir(bundledWeb) ? bundledWeb : null,
8605
8734
  sourceRoot: null,
8606
8735
  cliEntry: file
8607
8736
  };
8608
8737
  }
8609
- const root = path31.resolve(here, "..", "..", "..");
8738
+ const root = path32.resolve(here, "..", "..", "..");
8610
8739
  const override = process.env.VIBEFLOW_WEB_DIR;
8611
- const webDir = override && isWebDir(override) ? override : isWebDir(path31.join(root, "app")) ? path31.join(root, "app") : null;
8612
- return { version: readVersion(path31.join(root, "package.json")), webDir, sourceRoot: root, cliEntry: null };
8740
+ const webDir = override && isWebDir(override) ? override : isWebDir(path32.join(root, "app")) ? path32.join(root, "app") : null;
8741
+ return { version: readVersion(path32.join(root, "package.json")), webDir, sourceRoot: root, cliEntry: null };
8613
8742
  }
8614
8743
  function readVersion(pkgPath) {
8615
8744
  try {
8616
- return JSON.parse(fs27.readFileSync(pkgPath, "utf8")).version;
8745
+ return JSON.parse(fs28.readFileSync(pkgPath, "utf8")).version;
8617
8746
  } catch {
8618
8747
  return "0.0.0";
8619
8748
  }
@@ -8851,8 +8980,8 @@ async function cmdOpen(values, target, layout, userDataDir) {
8851
8980
  console.error("\u7528\u6CD5\uFF1Avibeflow open <path>");
8852
8981
  return 1;
8853
8982
  }
8854
- const dir = path31.resolve(target);
8855
- if (!fs27.existsSync(dir) || !fs27.statSync(dir).isDirectory()) {
8983
+ const dir = path32.resolve(target);
8984
+ if (!fs28.existsSync(dir) || !fs28.statSync(dir).isDirectory()) {
8856
8985
  console.error(`\u4E0D\u662F\u8CC7\u6599\u593E\uFF1A${dir}`);
8857
8986
  return 1;
8858
8987
  }