@kw-yeh/vibeflow 4.11.13 → 4.11.16

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
@@ -514,16 +514,6 @@ function agentArtifactsDir(worktreePath, workspacePath) {
514
514
  const ws = pathBasename(worktreePath);
515
515
  return `${dir}/${ws}${ARTIFACTS_DIR_SUFFIX}`;
516
516
  }
517
- function buildArtifactPrompt(artifactsDir = ARTIFACTS_FALLBACK_DIR) {
518
- return [
519
- "Artifact \u8A2D\u5B9A\uFF08\u672C\u6B21 session \u5168\u7A0B\u9069\u7528\uFF09\uFF1A",
520
- `1. \u672C\u4EFB\u52D9\u7684 Artifact \u8CC7\u6599\u593E\u662F ${artifactsDir}/\uFF1B\u9700\u8981\u4FDD\u5B58\u7D66\u4F7F\u7528\u8005\u7684\u9A57\u8B49\u8B49\u64DA\u6216\u5831\u544A\u6642\uFF0C\u8ACB\u5BEB\u5165\u6B64\u8CC7\u6599\u593E\uFF08\u4E0D\u5B58\u5728\u8ACB\u5EFA\u7ACB\uFF09\u3002`,
521
- `2. \u4F7F\u7528\u8005\u6703\u5728 VibeFlow \u7684 Artifacts \u5206\u9801\u6AA2\u8996\u6839\u76EE\u9304\u5167\u5BB9\uFF1B\u53EA\u653E\u4F60\u6703\u4E3B\u52D5\u8ACB\u4F7F\u7528\u8005\u904E\u76EE\u7684\u6A94\u6848\u3002`,
522
- `3. \u4F60\u81EA\u5DF1\u7684\u5DE5\u4F5C\u66AB\u5B58\uFF08\u4E00\u6B21\u6027 script\u3001log\u3001\u4E2D\u9593\u8F38\u51FA\u3001debug \u6A94\uFF09\u653E\u5728 ${artifactsDir}/${SCRATCH_DIR_NAME}/\u3002`,
523
- `4. \u82E5\u5DE5\u5177\u5148\u8F38\u51FA\u5230 /tmp\u3001/private/tmp\u3001$TMPDIR \u6216 ~/Downloads\uFF0C\u8ACB\u628A\u6700\u7D42\u8981\u5448\u73FE\u7684\u6A94\u6848\u8907\u88FD\u5230 ${artifactsDir}/ \u4E26\u78BA\u8A8D\u5B58\u5728\u3002`,
524
- "5. Artifact \u8CC7\u6599\u593E\u4F4D\u65BC worktree \u4E4B\u5916\uFF1B\u4E0D\u8981\u628A\u5176\u4E2D\u5167\u5BB9\u52A0\u5165 git commit\uFF0C\u4E5F\u4E0D\u8981\u628A\u6700\u7D42\u4EA4\u4ED8\u7269\u53EA\u7559\u5728\u9019\u88E1\u3002"
525
- ].join("\n");
526
- }
527
517
  function claudePermissionFlag(autoMode) {
528
518
  return autoMode ? ` --permission-mode ${AUTO_PERMISSION_MODE}` : "";
529
519
  }
@@ -533,8 +523,9 @@ function shellQuote(s) {
533
523
  function toShellPath(p) {
534
524
  return p.replace(/\\/g, "/");
535
525
  }
536
- function boardEnvPrefix(task, opts) {
526
+ function boardEnvPrefix(task, opts, workspacePath) {
537
527
  const board = opts?.boardCli;
528
+ const artifactsDir = agentArtifactsDir(task.worktreePath, workspacePath) ?? (task.worktreePath ? `${toShellPath(task.worktreePath)}/${ARTIFACTS_FALLBACK_DIR}` : void 0);
538
529
  if (!board) return "";
539
530
  const entries = [
540
531
  ["VIBEFLOW_TASK_ID", task.id],
@@ -544,7 +535,9 @@ function boardEnvPrefix(task, opts) {
544
535
  ["VIBEFLOW_STORE_DIR", board?.storeDir],
545
536
  ["VIBEFLOW_CLI", board?.cliPath],
546
537
  ["VIBEFLOW_CLI_LOADER", board?.loaderPath],
547
- ["VIBEFLOW_AUTO_MODE", opts?.autoMode ? "1" : "0"]
538
+ ["VIBEFLOW_AUTO_MODE", opts?.autoMode ? "1" : "0"],
539
+ ["VIBEFLOW_ARTIFACTS_DIR", artifactsDir],
540
+ ["VIBEFLOW_SCRATCH_DIR", artifactsDir ? `${artifactsDir}/${SCRATCH_DIR_NAME}` : void 0]
548
541
  ];
549
542
  const exports = entries.filter((entry) => Boolean(entry[1])).map(([key, value]) => `${key}=${shellQuote(toShellPath(value))}`);
550
543
  return exports.length ? `export ${exports.join(" ")}; ` : "";
@@ -612,25 +605,22 @@ function assembleCommand(agent, systemPrompt, prompt, model, effort, opts, workt
612
605
  const settings = ` --settings ${shellQuote(buildClaudeSettings(worktreePath))}`;
613
606
  const addDir = workspacePath ? ` --add-dir ${shellQuote(toShellPath(workspacePath))}` : "";
614
607
  const modelFlag = model ? ` --model ${model}` : "";
615
- const effortFlag = effort ? ` --effort ${effort}` : "";
616
- const library = opts?.library;
617
- const libraryFlags = library ? ` --plugin-dir ${shellQuote(toShellPath(library.pluginDir))} --add-dir ${shellQuote(toShellPath(library.libraryDir))}` : "";
618
- const flags = `--chrome${claudePermissionFlag(opts?.autoMode)}${modelFlag}${effortFlag}${settings}${addDir}${libraryFlags}`;
608
+ const effortFlag = effort && effort !== "auto" ? ` --effort ${effort}` : "";
609
+ const flags = `--chrome${claudePermissionFlag(opts?.autoMode)}${modelFlag}${effortFlag}${settings}${addDir}`;
619
610
  const sysFlag = systemPrompt ? ` --append-system-prompt ${shellQuote(systemPrompt)}` : "";
620
611
  const promptArg = prompt ? ` ${shellQuote(prompt)}` : "";
621
612
  const tail = `${flags}${sysFlag}${promptArg}`;
622
613
  cmd = sessionId && opts?.resume ? claudeResumeOrFresh(sessionId, worktreePath, tail) : `claude ${sessionId ? `--session-id ${sessionId} ` : opts?.resume ? "--continue " : ""}${tail}\r`;
623
614
  } else {
624
615
  const combined = [systemPrompt, prompt].filter(Boolean).join("\n\n");
625
- const codexEffortFlag = effort ? `-c ${shellQuote(`model_reasoning_effort="${effort}"`)} ` : "";
626
- const codexHome = opts?.library ? `CODEX_HOME=${shellQuote(toShellPath(opts.library.codexHome))} ` : "";
616
+ const codexEffortFlag = effort && effort !== "auto" ? `-c ${shellQuote(`model_reasoning_effort="${effort}"`)} ` : "";
627
617
  const args = [
628
618
  codexAutoFlag(opts?.autoMode).trim(),
629
619
  codexEffortFlag.trim(),
630
620
  model ? `--model ${model}` : "",
631
621
  combined ? shellQuote(combined) : ""
632
622
  ].filter(Boolean);
633
- cmd = `${codexHome}${["codex", ...args].join(" ")}\r`;
623
+ cmd = `${["codex", ...args].join(" ")}\r`;
634
624
  }
635
625
  if (cmd.length > 2e5) console.warn(`[VibeFlow] launch command is ${cmd.length} bytes \u2014 approaching ARG_MAX`);
636
626
  return cmd;
@@ -647,16 +637,11 @@ function taskModel(task) {
647
637
  function buildAgentCommand(task, systemPrompt, opts, workspacePath) {
648
638
  const agent = taskAgent(task);
649
639
  const model = taskModel(task);
650
- const artifactsDir = agentArtifactsDir(task.worktreePath, workspacePath) ?? ARTIFACTS_FALLBACK_DIR;
651
- const builtInPrompt = buildArtifactPrompt(artifactsDir);
652
- const libraryPrompt = opts?.library?.promptText?.trim();
653
- const customPrompt = resolveSystemPrompt(systemPrompt);
654
- const sys = [builtInPrompt, libraryPrompt, customPrompt].filter(Boolean).join("\n\n");
640
+ const sys = resolveSystemPrompt(systemPrompt);
655
641
  const includeTaskPrompt = opts?.includeTaskPrompt !== false;
656
- const prompt = includeTaskPrompt && !opts?.resume ? [buildPrompt(task), opts?.preflightPlan ? `\u524D\u7F6E\u898F\u5283\uFF08\u4F9B\u53C3\u8003\uFF0C\u8ACB\u5148\u6838\u5C0D\u7A0B\u5F0F\u78BC\u518D\u57F7\u884C\uFF09\uFF1A
657
- ${opts.preflightPlan}` : ""].filter(Boolean).join("\n\n") : "";
642
+ const prompt = includeTaskPrompt && !opts?.resume ? buildPrompt(task) : "";
658
643
  const sessionId = agent === "claude" && includeTaskPrompt ? executorSessionId(task.id, task.runId) : void 0;
659
- return boardEnvPrefix(task, opts) + assembleCommand(
644
+ return boardEnvPrefix(task, opts, workspacePath) + assembleCommand(
660
645
  agent,
661
646
  sys,
662
647
  prompt,
@@ -793,8 +778,8 @@ var init_attachments = __esm({
793
778
  function modelFor(models, modelId) {
794
779
  return modelId ? models?.find((m) => m.id === modelId) : models?.find((m) => m.isDefault);
795
780
  }
796
- function isAgentEffort(value) {
797
- return typeof value === "string" && AGENT_EFFORTS.includes(value);
781
+ function isManualEffort(value) {
782
+ return typeof value === "string" && MANUAL_EFFORTS.includes(value);
798
783
  }
799
784
  function allowedEfforts(model) {
800
785
  return model?.efforts ?? null;
@@ -804,17 +789,19 @@ function selectableEfforts(model) {
804
789
  }
805
790
  function clampEffort(effort, allowed) {
806
791
  if (allowed.length === 0) return void 0;
807
- if (effort === void 0 || allowed.includes(effort)) return effort;
808
- const rank = AGENT_EFFORTS.indexOf(effort);
809
- const ordered = AGENT_EFFORTS.filter((level) => allowed.includes(level));
810
- const notDeeper = ordered.filter((level) => AGENT_EFFORTS.indexOf(level) <= rank);
792
+ if (effort === void 0 || effort === "auto") return void 0;
793
+ if (allowed.includes(effort)) return effort;
794
+ const rank = MANUAL_EFFORTS.indexOf(effort);
795
+ const ordered = MANUAL_EFFORTS.filter((level) => allowed.includes(level));
796
+ const notDeeper = ordered.filter((level) => MANUAL_EFFORTS.indexOf(level) <= rank);
811
797
  return notDeeper.length > 0 ? notDeeper[notDeeper.length - 1] : ordered[0];
812
798
  }
813
- var AGENT_EFFORTS, UNKNOWN_MODEL_EFFORTS;
799
+ var MANUAL_EFFORTS, AGENT_EFFORTS, UNKNOWN_MODEL_EFFORTS;
814
800
  var init_effort = __esm({
815
801
  "packages/core/src/effort.ts"() {
816
802
  "use strict";
817
- AGENT_EFFORTS = ["low", "medium", "high", "xhigh", "max", "ultra"];
803
+ MANUAL_EFFORTS = ["low", "medium", "high", "xhigh", "max", "ultra"];
804
+ AGENT_EFFORTS = ["auto", ...MANUAL_EFFORTS];
818
805
  UNKNOWN_MODEL_EFFORTS = ["low", "medium", "high", "xhigh"];
819
806
  }
820
807
  });
@@ -1104,7 +1091,7 @@ var init_progress = __esm({
1104
1091
  // packages/tui/src/task-form.ts
1105
1092
  import { spawnSync as spawnSync2 } from "child_process";
1106
1093
  import fs24 from "fs";
1107
- import os9 from "os";
1094
+ import os8 from "os";
1108
1095
  import path28 from "path";
1109
1096
  import React, { useEffect, useRef, useState } from "react";
1110
1097
  import { Box, Text, render as render2, useApp, useInput } from "ink";
@@ -1177,10 +1164,10 @@ function modelOptions(ctx, agentCli, current) {
1177
1164
  return options;
1178
1165
  }
1179
1166
  function effortOptions(ctx, agentCli, model) {
1180
- return selectableEfforts(modelFor(ctx.models[agentCli], taskModel({ agentCli, model })));
1167
+ return ["auto", ...selectableEfforts(modelFor(ctx.models[agentCli], taskModel({ agentCli, model })))];
1181
1168
  }
1182
1169
  function fitEffort(ctx, s) {
1183
- const effort = clampEffort(s.effort, effortOptions(ctx, s.agentCli, s.model)) ?? s.effort;
1170
+ const effort = s.effort === "auto" ? "auto" : clampEffort(s.effort, effortOptions(ctx, s.agentCli, s.model).filter(isManualEffort)) ?? s.effort;
1184
1171
  return effort === s.effort ? s : { ...s, effort };
1185
1172
  }
1186
1173
  function blockReason(s, ctx, loadingGit) {
@@ -1231,7 +1218,7 @@ function editorCommand(env, platform) {
1231
1218
  return env.VISUAL?.trim() || env.EDITOR?.trim() || (platform === "win32" ? "notepad" : "vi");
1232
1219
  }
1233
1220
  function editInEditor(text) {
1234
- const dir = fs24.mkdtempSync(path28.join(os9.tmpdir(), "vibeflow-"));
1221
+ const dir = fs24.mkdtempSync(path28.join(os8.tmpdir(), "vibeflow-"));
1235
1222
  const file = path28.join(dir, "description.md");
1236
1223
  try {
1237
1224
  fs24.writeFileSync(file, text);
@@ -1312,9 +1299,9 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
1312
1299
  if (editing === "title") setS((prev) => ({ ...prev, title: draft }));
1313
1300
  else if (editing === "branch") setS((prev) => ({ ...prev, branch: value2 }));
1314
1301
  else if (editing === "project") {
1315
- if (value2) setProject(path28.resolve(value2.replace(/^~(?=$|\/)/, os9.homedir())));
1302
+ if (value2) setProject(path28.resolve(value2.replace(/^~(?=$|\/)/, os8.homedir())));
1316
1303
  } else if (editing === "attachments" && value2) {
1317
- const file = path28.resolve(value2.replace(/^~(?=$|\/)/, os9.homedir()));
1304
+ const file = path28.resolve(value2.replace(/^~(?=$|\/)/, os8.homedir()));
1318
1305
  if (!fs24.existsSync(file) || !fs24.statSync(file).isFile()) {
1319
1306
  setMessage(`\u627E\u4E0D\u5230\u6A94\u6848\uFF1A${file}`);
1320
1307
  return;
@@ -1405,7 +1392,6 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
1405
1392
  return lines.length > 1 ? `${lines[0]} \u2026\uFF08\u5171 ${lines.length} \u884C\uFF09` : lines[0];
1406
1393
  }
1407
1394
  case "effort": {
1408
- if (effortOptions(ctx, s.agentCli, s.model).length === 0) return "\u4E0D\u9069\u7528";
1409
1395
  const e = EFFORTS.find((x) => x.value === s.effort) ?? EFFORTS[0];
1410
1396
  return `\u2039 ${e.label} \xB7 ${e.value} \u203A`;
1411
1397
  }
@@ -1441,7 +1427,7 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
1441
1427
  return s.mode === "edit" && s.worktreePath ? `${when} \u9019\u5F35\u5361\u5DF2\u6709 worktree\uFF1A\u66F4\u6539\u5206\u652F\u3001\u5C08\u6848\u6216\u57FA\u6E96\u5206\u652F\u6703\u79FB\u9664\u5B83\uFF08\u6709\u672A\u5B8C\u6210\u8B8A\u66F4\u6642\u7121\u6CD5\u5132\u5B58\uFF09\u3002` : when;
1442
1428
  }
1443
1429
  if (field === "effort") {
1444
- if (effortOptions(ctx, s.agentCli, s.model).length === 0) return "\u9019\u500B model \u4E0D\u652F\u63F4\u8ABF\u6574\u63A8\u7406\u5F37\u5EA6\uFF0C\u555F\u52D5\u6642\u4E0D\u6703\u6307\u5B9A\u4EFB\u52D9\u8907\u96DC\u5EA6\u3002";
1430
+ if (effortOptions(ctx, s.agentCli, s.model).length === 1) return "\u9019\u500B model \u4E0D\u652F\u63F4\u8ABF\u6574\u63A8\u7406\u5F37\u5EA6\uFF0C\u555F\u52D5\u6642\u4F7F\u7528\u6A21\u578B\u9810\u8A2D\u503C\u3002";
1445
1431
  const e = EFFORTS.find((x) => x.value === s.effort) ?? EFFORTS[0];
1446
1432
  return `${e.description} Claude Code \u8207 Codex \u6703\u5728\u555F\u52D5\u6B64\u4EFB\u52D9\u6642\u5957\u7528\uFF1B\u53EF\u9078\u7684\u7D1A\u8DDD\u4F9D model \u800C\u5B9A\u3002`;
1447
1433
  }
@@ -1521,6 +1507,7 @@ var init_task_form = __esm({
1521
1507
  init_attachments();
1522
1508
  h = React.createElement;
1523
1509
  EFFORTS = [
1510
+ { value: "auto", label: "\u81EA\u52D5", description: "Jev \u4F9D\u4EFB\u52D9\u8207\u76EE\u524D\u6A21\u578B\u9078\u64C7\u63A8\u7406\u5F37\u5EA6\u3002" },
1524
1511
  { value: "low", label: "\u5FEB\u901F", description: "\u9069\u5408\u7BC4\u570D\u660E\u78BA\u3001\u4F4E\u98A8\u96AA\u7684\u5C0F\u4EFB\u52D9\u3002" },
1525
1512
  { value: "medium", label: "\u6A19\u6E96", description: "\u517C\u9867\u901F\u5EA6\u8207\u63A8\u7406\u6DF1\u5EA6\uFF0C\u9069\u5408\u4E00\u822C\u958B\u767C\u4EFB\u52D9\u3002" },
1526
1513
  { value: "high", label: "\u6DF1\u5165", description: "\u9069\u5408\u8DE8\u6A94\u6848\u3001\u9664\u932F\u6216\u9700\u8981\u8F03\u591A\u5224\u65B7\u7684\u4EFB\u52D9\u3002" },
@@ -1528,7 +1515,7 @@ var init_task_form = __esm({
1528
1515
  { value: "max", label: "\u6700\u5927", description: "\u6700\u6DF1\u7684\u63A8\u7406\uFF0C\u9069\u5408\u6700\u56F0\u96E3\u7684\u554F\u984C\u3002" },
1529
1516
  { value: "ultra", label: "\u59D4\u6D3E", description: "\u6700\u6DF1\u63A8\u7406\u4E26\u81EA\u52D5\u59D4\u6D3E\u5B50\u4EFB\u52D9\uFF0C\u8017\u6642\u8207\u7528\u91CF\u53EF\u80FD\u660E\u986F\u589E\u52A0\u3002" }
1530
1517
  ];
1531
- DEFAULT_EFFORT = "medium";
1518
+ DEFAULT_EFFORT = "auto";
1532
1519
  LABELS = {
1533
1520
  title: "\u4EFB\u52D9\u6A19\u984C",
1534
1521
  project: "\u5C08\u6848\u8CC7\u6599\u593E",
@@ -2470,6 +2457,7 @@ function createBridge(t) {
2470
2457
  /** Running app version (package.json version baked into the build). */
2471
2458
  getVersion: () => t.invoke("app:getVersion"),
2472
2459
  getUpdateStatus: () => t.invoke("host:updateStatus"),
2460
+ openUpdateTerminal: () => t.invoke("host:openUpdateTerminal"),
2473
2461
  onUpdateAvailable: (callback) => t.on("update:available", (payload) => callback(payload)),
2474
2462
  /**
2475
2463
  * Fired when an external write (e.g. CLI) changes the store backing file.
@@ -3336,6 +3324,30 @@ var SNIFF_BYTES = 8 * 1024;
3336
3324
  function agentArtifactsPath(baseDir, worktreePath) {
3337
3325
  return path11.join(baseDir, `${path11.basename(worktreePath)}${ARTIFACTS_DIR_SUFFIX2}`);
3338
3326
  }
3327
+ function ensureTaskArtifactDirs(task) {
3328
+ if (!task.worktreePath) throw new Error("\u4EFB\u52D9\u5C1A\u672A\u5EFA\u7ACB worktree");
3329
+ const dir = task.workspacePath ? agentArtifactsPath(task.workspacePath, task.worktreePath) : path11.join(task.worktreePath, ARTIFACTS_FALLBACK_DIR2);
3330
+ fs7.mkdirSync(path11.join(dir, SCRATCH_DIR_NAME2), { recursive: true });
3331
+ return dir;
3332
+ }
3333
+ function addTaskArtifact(task, filePath) {
3334
+ const source = path11.resolve(filePath);
3335
+ const stat = fs7.lstatSync(source);
3336
+ if (!stat.isFile()) throw new Error("Artifact \u4F86\u6E90\u5FC5\u9808\u662F\u4E00\u822C\u6A94\u6848");
3337
+ const dir = ensureTaskArtifactDirs(task);
3338
+ if (path11.dirname(source) === dir) return source;
3339
+ const { name, ext } = path11.parse(source);
3340
+ for (let index = 0; index < 1e3; index += 1) {
3341
+ const destination = path11.join(dir, index === 0 ? `${name}${ext}` : `${name}-${index}${ext}`);
3342
+ try {
3343
+ fs7.copyFileSync(source, destination, fs7.constants.COPYFILE_EXCL);
3344
+ return destination;
3345
+ } catch (error) {
3346
+ if (error.code !== "EEXIST") throw error;
3347
+ }
3348
+ }
3349
+ throw new Error("Artifact \u8CC7\u6599\u593E\u6709\u592A\u591A\u540C\u540D\u6A94\u6848");
3350
+ }
3339
3351
  function mediaKind(filePath, size) {
3340
3352
  const extension = path11.extname(filePath).toLowerCase();
3341
3353
  const imageMime = IMAGE_MIME[extension];
@@ -4271,7 +4283,7 @@ init_effort();
4271
4283
  import { execFile as execFile6 } from "child_process";
4272
4284
  import { promisify as promisify3 } from "util";
4273
4285
  var pexec3 = promisify3(execFile6);
4274
- var DEFAULT_TASK_EFFORT = "medium";
4286
+ var DEFAULT_TASK_EFFORT = "auto";
4275
4287
  var AGENT_CLIS = [
4276
4288
  {
4277
4289
  id: "claude",
@@ -4462,7 +4474,7 @@ function updateTaskFromInput(input) {
4462
4474
  init_attachments();
4463
4475
  var AGENT_IDS = AGENT_CLIS.map((agent) => agent.id);
4464
4476
  var STATUSES = ["backlog", "in_progress", "done"];
4465
- var SUBCOMMANDS = ["create", "update"];
4477
+ var SUBCOMMANDS = ["create", "update", "artifact"];
4466
4478
  var MODES = ["existing", "new"];
4467
4479
  var AUTO_MODES = ["on", "off"];
4468
4480
  var TASK_USAGE = `
@@ -4471,6 +4483,7 @@ VibeFlow task commands
4471
4483
  Usage:
4472
4484
  vibeflow task create [options]
4473
4485
  vibeflow task update [options]
4486
+ vibeflow task artifact add --file <path> [options]
4474
4487
 
4475
4488
  task create options:
4476
4489
  --project <path> Target project directory (required)
@@ -4484,7 +4497,7 @@ task create options:
4484
4497
  --mode <mode> ${MODES.join(" | ")} (default: existing; "new" runs git init first)
4485
4498
  --agent <id> Task agent: ${AGENT_IDS.join(" | ")} (default: claude)
4486
4499
  --model <id> Task model (default: the agent's own default)
4487
- --effort <level> ${AGENT_EFFORTS.join(" | ")} (default: medium, same as the UI)
4500
+ --effort <level> ${AGENT_EFFORTS.join(" | ")} (default: auto, same as the UI)
4488
4501
  --auto-mode <on|off> Let the agent act without asking for approval
4489
4502
  (default: the board-wide setting, same as the UI)
4490
4503
  --attach <path> Attach a file; repeat the flag for several files
@@ -4503,6 +4516,12 @@ task update options:
4503
4516
  At least one of --title / --prompt / --status is required. Branch, worktree
4504
4517
  and project path are provisioned on disk and cannot be changed here.
4505
4518
 
4519
+ task artifact add options:
4520
+ --file <path> Copy a file into the card's user-facing Artifacts folder
4521
+ --task <id> Card ID (defaults to VIBEFLOW_TASK_ID in a task shell)
4522
+ --store-path <dir> Store directory (defaults to VIBEFLOW_STORE_DIR in a task shell)
4523
+ --profile <name> dev | prod \u2014 when no store path is given
4524
+
4506
4525
  Notes:
4507
4526
  A card created with --status in_progress does NOT start running by itself.
4508
4527
  Every launch is started by hand from the app.
@@ -4570,6 +4589,7 @@ async function taskCommand(argv) {
4570
4589
  effort: { type: "string" },
4571
4590
  "auto-mode": { type: "string" },
4572
4591
  attach: { type: "string", multiple: true },
4592
+ file: { type: "string" },
4573
4593
  "store-path": { type: "string" },
4574
4594
  profile: { type: "string" },
4575
4595
  help: { type: "boolean", short: "h" }
@@ -4581,13 +4601,24 @@ async function taskCommand(argv) {
4581
4601
  fail("INVALID_ARGUMENT", err.message);
4582
4602
  }
4583
4603
  const { values, positionals } = parsed;
4584
- const [sub] = positionals;
4604
+ const [sub, action] = positionals;
4585
4605
  if (values.help || !sub) return null;
4586
- const storePath = resolveStorePath(values["store-path"], values.profile);
4606
+ const storePath = resolveStorePath(
4607
+ values["store-path"] ?? (sub === "artifact" ? process.env.VIBEFLOW_STORE_DIR : void 0),
4608
+ values.profile
4609
+ );
4587
4610
  setPlatform(createNodePlatform({ userDataDir: storePath }));
4588
4611
  if (!SUBCOMMANDS.includes(sub)) {
4589
4612
  fail("UNKNOWN_COMMAND", `Unknown command: "task ${sub}". Use: ${SUBCOMMANDS.map((s) => `task ${s}`).join(" | ")}`);
4590
4613
  }
4614
+ if (sub === "artifact") {
4615
+ if (action !== "add") fail("UNKNOWN_COMMAND", "Use: task artifact add --file <path>");
4616
+ const taskId = values.task ?? process.env.VIBEFLOW_TASK_ID;
4617
+ if (!taskId || !values.file) fail("MISSING_ARGUMENT", "task artifact add needs --file and --task (or VIBEFLOW_TASK_ID)");
4618
+ const task2 = findTask(taskId);
4619
+ if (!task2) fail("TASK_NOT_FOUND", `Task not found: ${taskId}`);
4620
+ return { taskId, path: addTaskArtifact(task2, values.file) };
4621
+ }
4591
4622
  if (sub === "update") {
4592
4623
  if (!values.task) fail("MISSING_ARGUMENT", "Missing required arguments: --task");
4593
4624
  requireOneOf("--status", values.status, STATUSES);
@@ -4695,7 +4726,6 @@ import path15 from "path";
4695
4726
  // packages/core/src/library.ts
4696
4727
  import { createHash } from "crypto";
4697
4728
  import fs10 from "fs";
4698
- import os3 from "os";
4699
4729
  import path14 from "path";
4700
4730
  var LIBRARY_DIR = "library";
4701
4731
  var LIBRARY_KINDS = ["skill", "prompt", "script"];
@@ -4712,7 +4742,6 @@ function libraryPaths(root) {
4712
4742
  skills: path14.join(root, KIND_DIR.skill),
4713
4743
  prompts: path14.join(root, KIND_DIR.prompt),
4714
4744
  scripts: path14.join(root, KIND_DIR.script),
4715
- plugin: path14.join(root, "plugin"),
4716
4745
  codexHome: path14.join(root, "codex-home")
4717
4746
  };
4718
4747
  }
@@ -4960,9 +4989,6 @@ function requireEntry(root, kind, name) {
4960
4989
  if (!found2) throw new Error(`\u9805\u76EE\u5BEB\u5165\u5F8C\u8B80\u4E0D\u56DE\u4F86\uFF1A${kind}/${name}`);
4961
4990
  return found2;
4962
4991
  }
4963
- function projectSkillsDir(worktreePath) {
4964
- return path14.join(worktreePath, ".claude", "skills");
4965
- }
4966
4992
  function skillNamesIn(dir) {
4967
4993
  try {
4968
4994
  return fs10.readdirSync(dir, { withFileTypes: true }).filter((d) => !d.name.startsWith(".")).map((d) => d.name).filter((name) => fs10.existsSync(path14.join(dir, name, "SKILL.md"))).sort();
@@ -4970,132 +4996,6 @@ function skillNamesIn(dir) {
4970
4996
  return [];
4971
4997
  }
4972
4998
  }
4973
- var PLUGIN_MANIFEST_DIR = ".claude-plugin";
4974
- function buildPluginDir(root) {
4975
- const paths = libraryPaths(root);
4976
- const enabled = listLibrary(root).filter((e) => e.kind === "skill" && e.enabled);
4977
- fs10.rmSync(path14.join(paths.plugin, "skills"), { recursive: true, force: true });
4978
- fs10.mkdirSync(path14.join(paths.plugin, PLUGIN_MANIFEST_DIR), { recursive: true });
4979
- fs10.mkdirSync(path14.join(paths.plugin, "skills"), { recursive: true });
4980
- fs10.writeFileSync(
4981
- path14.join(paths.plugin, PLUGIN_MANIFEST_DIR, "plugin.json"),
4982
- `${JSON.stringify(
4983
- {
4984
- name: "vibeflow-library",
4985
- version: "1.0.0",
4986
- description: "Skills delivered by VibeFlow for this task."
4987
- },
4988
- null,
4989
- 2
4990
- )}
4991
- `
4992
- );
4993
- for (const entry of enabled) {
4994
- fs10.cpSync(entry.path, path14.join(paths.plugin, "skills", entry.name), {
4995
- recursive: true,
4996
- dereference: true
4997
- });
4998
- }
4999
- return { pluginDir: paths.plugin, skills: enabled.map((e) => e.name) };
5000
- }
5001
- function linkOrSkip(target, linkPath) {
5002
- if (!fs10.existsSync(target)) return false;
5003
- const source = path14.resolve(target);
5004
- const attempts = fs10.statSync(source).isDirectory() ? [
5005
- () => fs10.symlinkSync(source, linkPath, "junction"),
5006
- () => fs10.cpSync(source, linkPath, { recursive: true, dereference: true })
5007
- ] : [
5008
- () => fs10.symlinkSync(source, linkPath),
5009
- () => fs10.linkSync(source, linkPath),
5010
- () => fs10.copyFileSync(source, linkPath)
5011
- ];
5012
- for (const attempt of attempts) {
5013
- try {
5014
- fs10.rmSync(linkPath, { recursive: true, force: true });
5015
- attempt();
5016
- return true;
5017
- } catch {
5018
- }
5019
- }
5020
- return false;
5021
- }
5022
- function buildCodexHome(root, worktreePath, userCodexHome = path14.join(os3.homedir(), ".codex")) {
5023
- const paths = libraryPaths(root);
5024
- const skillsDir = path14.join(paths.codexHome, "skills");
5025
- fs10.rmSync(skillsDir, { recursive: true, force: true });
5026
- fs10.mkdirSync(skillsDir, { recursive: true });
5027
- for (const file of ["auth.json", "config.toml"]) {
5028
- linkOrSkip(path14.join(userCodexHome, file), path14.join(paths.codexHome, file));
5029
- }
5030
- const result = {
5031
- codexHome: paths.codexHome,
5032
- project: [],
5033
- user: [],
5034
- library: [],
5035
- shadowed: []
5036
- };
5037
- const claimed = /* @__PURE__ */ new Map();
5038
- const projectDir = worktreePath ? projectSkillsDir(worktreePath) : void 0;
5039
- if (projectDir) {
5040
- for (const name of skillNamesIn(projectDir)) {
5041
- if (linkOrSkip(path14.join(projectDir, name), path14.join(skillsDir, name))) {
5042
- result.project.push(name);
5043
- claimed.set(name, "project");
5044
- }
5045
- }
5046
- }
5047
- const userDir = path14.join(userCodexHome, "skills");
5048
- for (const name of skillNamesIn(userDir)) {
5049
- const owner = claimed.get(name);
5050
- if (owner) {
5051
- result.shadowed.push({ name, keptFrom: owner, dropped: "user" });
5052
- continue;
5053
- }
5054
- if (linkOrSkip(path14.join(userDir, name), path14.join(skillsDir, name))) {
5055
- result.user.push(name);
5056
- claimed.set(name, "user");
5057
- }
5058
- }
5059
- for (const entry of listLibrary(root)) {
5060
- if (entry.kind !== "skill" || !entry.enabled) continue;
5061
- const owner = claimed.get(entry.name);
5062
- if (owner) {
5063
- result.shadowed.push({ name: entry.name, keptFrom: owner, dropped: "library" });
5064
- continue;
5065
- }
5066
- fs10.cpSync(entry.path, path14.join(skillsDir, entry.name), {
5067
- recursive: true,
5068
- dereference: true
5069
- });
5070
- result.library.push(entry.name);
5071
- }
5072
- return result;
5073
- }
5074
- function libraryLaunchInfoAt(root, worktreePath, userCodexHome) {
5075
- const entries = listLibrary(root).filter((e) => e.enabled);
5076
- if (entries.length === 0) return null;
5077
- const { pluginDir } = buildPluginDir(root);
5078
- const { codexHome } = buildCodexHome(root, worktreePath, userCodexHome);
5079
- const scripts = entries.filter((e) => e.kind === "script").map((e) => ({ name: e.name, path: e.path, description: e.description }));
5080
- const promptBodies = entries.filter((e) => e.kind === "prompt").map((e) => readEntryContent(root, "prompt", e.name).trim()).filter(Boolean);
5081
- const lines = [...promptBodies];
5082
- if (scripts.length > 0) {
5083
- lines.push(
5084
- "\u53EF\u7528\u7684\u5DE5\u5177\u8173\u672C\uFF08\u9700\u8981\u6642\u81EA\u884C\u57F7\u884C\uFF0C\u4E0D\u9700\u8981\u5C31\u5225\u8DD1\uFF09\uFF1A",
5085
- ...scripts.map((s) => `- ${s.path}${s.description ? ` \u2014 ${s.description}` : ""}`)
5086
- );
5087
- }
5088
- return {
5089
- pluginDir,
5090
- codexHome,
5091
- libraryDir: root,
5092
- scripts,
5093
- promptText: lines.join("\n\n")
5094
- };
5095
- }
5096
- async function libraryLaunchInfo(worktreePath) {
5097
- return libraryLaunchInfoAt(await libraryRoot(), worktreePath);
5098
- }
5099
4999
 
5100
5000
  // packages/core/src/library-builtins.ts
5101
5001
  function builtinSkillsDir() {
@@ -5201,19 +5101,19 @@ async function syncBuiltinsAtStartup() {
5201
5101
 
5202
5102
  // packages/core/src/service.ts
5203
5103
  import fs19 from "fs";
5204
- import os8 from "os";
5104
+ import os7 from "os";
5205
5105
  import path23 from "path";
5206
5106
  import { randomUUID as randomUUID5 } from "crypto";
5207
5107
 
5208
5108
  // packages/core/src/agent-models.ts
5209
5109
  import fs13 from "fs";
5210
- import os6 from "os";
5110
+ import os5 from "os";
5211
5111
  import path17 from "path";
5212
5112
 
5213
5113
  // packages/core/src/claude-models.ts
5214
5114
  init_effort();
5215
5115
  import fs12 from "fs";
5216
- import os4 from "os";
5116
+ import os3 from "os";
5217
5117
  import path16 from "path";
5218
5118
 
5219
5119
  // packages/core/src/json-lines-process.ts
@@ -5318,7 +5218,7 @@ var CLAUDE_MODELS_TIMEOUT_MS = 15e3;
5318
5218
  var REQUEST_ID = "vibeflow-models";
5319
5219
  var CLAUDE_ARGS = ["-p", "--input-format", "stream-json", "--output-format", "stream-json", "--verbose"];
5320
5220
  function openClaude() {
5321
- const cwd = fs12.mkdtempSync(path16.join(os4.tmpdir(), "vf-claude-models-"));
5221
+ const cwd = fs12.mkdtempSync(path16.join(os3.tmpdir(), "vf-claude-models-"));
5322
5222
  return spawnJsonLines("claude", CLAUDE_ARGS, {
5323
5223
  cwd,
5324
5224
  timeoutMs: CLAUDE_MODELS_TIMEOUT_MS,
@@ -5348,7 +5248,7 @@ function parseClaudeInitialize(message) {
5348
5248
  seen.add(id);
5349
5249
  const label = typeof entry.displayName === "string" && entry.displayName.trim() ? entry.displayName.trim() : id;
5350
5250
  const description = typeof entry.description === "string" && entry.description.trim() ? entry.description.trim() : void 0;
5351
- const levels = entry.supportsEffort === true && Array.isArray(entry.supportedEffortLevels) ? entry.supportedEffortLevels.filter(isAgentEffort) : [];
5251
+ const levels = entry.supportsEffort === true && Array.isArray(entry.supportedEffortLevels) ? entry.supportedEffortLevels.filter(isManualEffort) : [];
5352
5252
  const isDefault = !defaultMarked && defaultResolved !== void 0 && entry.resolvedModel === defaultResolved;
5353
5253
  if (isDefault) defaultMarked = true;
5354
5254
  models.push({ id, label, ...description ? { description } : {}, efforts: levels, ...isDefault ? { isDefault } : {} });
@@ -5369,7 +5269,7 @@ async function fetchClaudeModels(open = openClaude) {
5369
5269
  // packages/core/src/codex-models.ts
5370
5270
  init_effort();
5371
5271
  init_env();
5372
- import os5 from "os";
5272
+ import os4 from "os";
5373
5273
  var CODEX_MODELS_TIMEOUT_MS = 2e4;
5374
5274
  var MAX_PAGES = 20;
5375
5275
  var CodexLoginRequiredError = class extends Error {
@@ -5382,7 +5282,7 @@ function codexAppServerCommand() {
5382
5282
  return {
5383
5283
  bin: "codex",
5384
5284
  args: ["app-server", "--listen", "stdio://"],
5385
- cwd: os5.homedir(),
5285
+ cwd: os4.homedir(),
5386
5286
  env: execEnv()
5387
5287
  };
5388
5288
  }
@@ -5411,7 +5311,7 @@ function parseCodexModelPage(result) {
5411
5311
  const id = entry.id.trim();
5412
5312
  const label = typeof entry.displayName === "string" && entry.displayName.trim() ? entry.displayName.trim() : id;
5413
5313
  const description = typeof entry.description === "string" && entry.description.trim() ? entry.description.trim() : void 0;
5414
- const efforts = Array.isArray(entry.supportedReasoningEfforts) ? entry.supportedReasoningEfforts.map((e) => isRecord2(e) ? e.reasoningEffort : void 0).filter(isAgentEffort) : void 0;
5314
+ const efforts = Array.isArray(entry.supportedReasoningEfforts) ? entry.supportedReasoningEfforts.map((e) => isRecord2(e) ? e.reasoningEffort : void 0).filter(isManualEffort) : void 0;
5415
5315
  models.push({
5416
5316
  id,
5417
5317
  label,
@@ -5471,7 +5371,7 @@ function builtinModels(id) {
5471
5371
  }
5472
5372
  function defaultCodexHome() {
5473
5373
  const configured = process.env.CODEX_HOME?.trim();
5474
- return configured ? configured : path17.join(os6.homedir(), ".codex");
5374
+ return configured ? configured : path17.join(os5.homedir(), ".codex");
5475
5375
  }
5476
5376
  function parseCodexCatalog(raw) {
5477
5377
  const body = JSON.parse(raw);
@@ -5557,118 +5457,68 @@ function createModelCatalog(opts) {
5557
5457
  }
5558
5458
 
5559
5459
  // packages/core/src/jev-router.ts
5460
+ init_effort();
5560
5461
  init_launch();
5561
5462
  var API_URL = "https://api.typesafe.ai/v1/systemone";
5562
5463
  var MAX_TASK_CHARS = 12e3;
5563
- var MAX_PLAN_CHARS = 8e3;
5564
- function tier(model, agent) {
5565
- const name = `${model.id} ${model.label}`.toLowerCase();
5566
- if (/haiku|mini|nano|luna|flash/.test(name)) return 0;
5567
- if (/opus|astra|pro\b/.test(name)) return 2;
5568
- if (agent === "codex" && /gpt-5\.5|gpt-5\.4(?!-mini)/.test(name)) return 2;
5569
- return 1;
5570
- }
5571
- function routeCandidates(agent, models) {
5572
- const valid = models.filter((m) => /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(m.id));
5573
- const byTier = /* @__PURE__ */ new Map();
5574
- for (const model of valid) {
5575
- const rank = tier(model, agent);
5576
- if (!byTier.has(rank)) byTier.set(rank, model);
5577
- }
5578
- return [...byTier.entries()].sort(([a], [b]) => a - b).map(([, model]) => model);
5579
- }
5580
- function isChoice(value) {
5581
- return !!value && typeof value === "object" && value.type === "choice" && typeof value.choice === "string";
5582
- }
5583
5464
  function createJevRouter(opts = {}) {
5584
5465
  const getApiKey = opts.getApiKey ?? (() => opts.apiKey ?? process.env.TYPESAFE_API_KEY?.trim());
5585
5466
  const fetcher = opts.fetcher ?? fetch;
5586
- const planner = opts.planner ?? (async (agent, model, prompt, cwd) => {
5587
- const args = agent === "claude" ? ["-p", "--model", model, "--permission-mode", "plan"] : ["exec", "--model", model, "--sandbox", "read-only", "--skip-git-repo-check", "-"];
5588
- return (opts.printAgent ?? runAgentPrint)(agent, args, prompt, { cwd, timeout: 12e4, maxBuffer: 128 * 1024 });
5589
- });
5590
- async function ask(state, questions) {
5591
- const apiKey = getApiKey();
5592
- if (!apiKey) throw new Error("\u672A\u8A2D\u5B9A Jev API key\uFF08\u53EF\u5728\u8A2D\u5B9A\u4E2D\u8F38\u5165\u6216\u63D0\u4F9B TYPESAFE_API_KEY\uFF09");
5593
- const response = await fetcher(API_URL, {
5594
- method: "POST",
5595
- headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
5596
- body: JSON.stringify({ model: "jev-latest", state, questions }),
5597
- signal: AbortSignal.timeout(15e3)
5598
- });
5599
- if (!response.ok) throw new Error(`Jev API \u56DE\u61C9 ${response.status}`);
5600
- const body = await response.json();
5601
- if (!body.answers) throw new Error("Jev API \u6C92\u6709\u56DE\u50B3\u5224\u65B7");
5602
- return body.answers;
5603
- }
5604
5467
  return {
5605
5468
  get enabled() {
5606
5469
  return Boolean(getApiKey());
5607
5470
  },
5608
- async route(task, catalog, onPhase) {
5609
- const agent = task.agentCli === "codex" ? "codex" : "claude";
5610
- const candidates = routeCandidates(agent, catalog);
5611
- if (candidates.length === 0) return { status: "fallback", reason: "\u6A21\u578B\u6E05\u55AE\u6C92\u6709\u53EF\u7528\u7684\u6A21\u578B" };
5612
- const expensive = candidates[candidates.length - 1];
5613
- const taskText = buildPrompt(task).slice(0, MAX_TASK_CHARS);
5471
+ async route(task, catalog) {
5472
+ const model = modelFor(catalog, taskModel(task));
5473
+ const levels = selectableEfforts(model);
5474
+ if (levels.length === 0) return { status: "fallback", reason: "\u76EE\u524D\u6A21\u578B\u4E0D\u652F\u63F4\u8ABF\u6574 effort" };
5614
5475
  try {
5615
- onPhase?.("judging");
5616
- const first = await ask({ task: taskText, agent }, {
5617
- complexity: {
5618
- type: "choice",
5619
- instructions: "How difficult is this software task to implement correctly?",
5620
- criteria: {
5621
- simple: "Small, well scoped and routine",
5622
- moderate: "Some design choices or multiple files",
5623
- complex: "Architecture, ambiguous requirements, high risk, or many interacting parts"
5624
- }
5625
- },
5626
- preparation: {
5627
- type: "choice",
5628
- instructions: "Would a separate read-only planning pass materially improve the implementation?",
5629
- criteria: {
5630
- direct: "The request is clear enough to start implementation directly",
5631
- plan: "The request is unclear, broad or complex enough to benefit from a plan first"
5476
+ const apiKey = getApiKey();
5477
+ if (!apiKey) throw new Error("\u672A\u8A2D\u5B9A Jev API key\uFF08\u53EF\u5728\u8A2D\u5B9A\u4E2D\u8F38\u5165\u6216\u63D0\u4F9B TYPESAFE_API_KEY\uFF09");
5478
+ const response = await fetcher(API_URL, {
5479
+ method: "POST",
5480
+ headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
5481
+ body: JSON.stringify({
5482
+ model: "jev-latest",
5483
+ state: {
5484
+ task: buildPrompt(task).slice(0, MAX_TASK_CHARS),
5485
+ agent: task.agentCli === "codex" ? "codex" : "claude",
5486
+ currentModel: model?.id ?? taskModel(task) ?? "CLI default (catalog unavailable)"
5487
+ },
5488
+ questions: {
5489
+ effort: {
5490
+ type: "choice",
5491
+ instructions: "Choose the lowest reasoning effort likely to complete this software task correctly on the current model. Increase effort for complexity, ambiguity, or risk.",
5492
+ criteria: Object.fromEntries(levels.map((level) => [level, effortDescription(level)]))
5493
+ }
5632
5494
  }
5633
- }
5495
+ }),
5496
+ signal: AbortSignal.timeout(15e3)
5634
5497
  });
5635
- if (!isChoice(first.complexity) || !isChoice(first.preparation) || !["simple", "moderate", "complex"].includes(first.complexity.choice) || !["direct", "plan"].includes(first.preparation.choice)) throw new Error("Jev \u56DE\u50B3\u4E0D\u53D7\u652F\u63F4\u7684\u5224\u65B7");
5636
- let plan;
5637
- if (first.preparation.choice === "plan") {
5638
- if (!task.worktreePath) throw new Error("\u898F\u5283\u6642\u627E\u4E0D\u5230 worktree");
5639
- onPhase?.("planning");
5640
- const instruction = `Read the repository and make a concise implementation plan for the following task. Do not edit files. Identify unclear requirements, affected areas, and verification. Return only the plan.
5641
-
5642
- ${taskText}`;
5643
- plan = (await planner(agent, expensive.id, instruction, task.worktreePath)).trim().slice(0, MAX_PLAN_CHARS);
5644
- if (!plan) throw new Error("\u898F\u5283\u6A21\u578B\u6C92\u6709\u56DE\u50B3\u8A08\u756B");
5498
+ if (!response.ok) throw new Error(`Jev API \u56DE\u61C9 ${response.status}`);
5499
+ const body = await response.json();
5500
+ const choice = body.answers?.effort;
5501
+ if (choice?.type !== "choice" || !levels.includes(choice.choice)) {
5502
+ throw new Error("Jev \u9078\u4E86\u76EE\u524D\u6A21\u578B\u4E0D\u652F\u63F4\u7684 effort");
5645
5503
  }
5646
- onPhase?.("selecting");
5647
- const options = Object.fromEntries(candidates.map((m) => [
5648
- m.id,
5649
- `${m.label}; relative cost tier ${tier(m, agent) + 1}/3 (${tier(m, agent) === 2 ? "highest capability" : tier(m, agent) === 0 ? "lowest cost" : "balanced"})`
5650
- ]));
5651
- const second = await ask({ task: taskText, complexity: first.complexity.choice, plan }, {
5652
- model: {
5653
- type: "choice",
5654
- instructions: "Choose the lowest-cost available model likely to implement this task correctly. Prefer more capable models as complexity or uncertainty rises.",
5655
- criteria: options
5656
- }
5657
- });
5658
- if (!isChoice(second.model) || !candidates.some((m) => m.id === second.model.choice)) {
5659
- throw new Error("Jev \u9078\u4E86\u4E0D\u5728\u6A21\u578B\u6E05\u55AE\u4E2D\u7684\u6A21\u578B");
5660
- }
5661
- return {
5662
- status: "routed",
5663
- model: second.model.choice,
5664
- ...plan ? { plannerModel: expensive.id, plan } : {}
5665
- };
5504
+ return { status: "routed", effort: choice.choice };
5666
5505
  } catch (err) {
5667
5506
  return { status: "fallback", reason: err instanceof Error ? err.message : String(err) };
5668
5507
  }
5669
5508
  }
5670
5509
  };
5671
5510
  }
5511
+ function effortDescription(level) {
5512
+ const descriptions = {
5513
+ low: "Routine, small and well scoped work",
5514
+ medium: "Typical development with limited uncertainty",
5515
+ high: "Complex debugging or several interacting files",
5516
+ xhigh: "Difficult design or extensive reasoning",
5517
+ max: "The hardest, highest risk tasks",
5518
+ ultra: "Deepest reasoning with automatic delegation"
5519
+ };
5520
+ return descriptions[level];
5521
+ }
5672
5522
 
5673
5523
  // packages/core/src/jev-credentials.ts
5674
5524
  import fs14 from "fs";
@@ -6634,7 +6484,7 @@ init_launch();
6634
6484
  init_progress();
6635
6485
  init_launch();
6636
6486
  import fs18 from "fs";
6637
- import os7 from "os";
6487
+ import os6 from "os";
6638
6488
  import path22 from "path";
6639
6489
  var SINCE_SLACK_MS = 5e3;
6640
6490
  function claudeProjectDir(home, cwd) {
@@ -6735,7 +6585,7 @@ function codexDayDirs(sessionsRoot, since) {
6735
6585
  return days;
6736
6586
  }
6737
6587
  function createProgressTracker(opts) {
6738
- const home = opts.homeDir ?? os7.homedir();
6588
+ const home = opts.homeDir ?? os6.homedir();
6739
6589
  const codexHomes = opts.codexHomes ?? (() => [path22.join(home, ".codex")]);
6740
6590
  const pollMs = opts.pollMs ?? 2e3;
6741
6591
  const entries = /* @__PURE__ */ new Map();
@@ -6989,7 +6839,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
6989
6839
  libraryCodexHome = libraryPaths(root).codexHome;
6990
6840
  }).catch(() => {
6991
6841
  });
6992
- const home = homeDir ?? os8.homedir();
6842
+ const home = homeDir ?? os7.homedir();
6993
6843
  const progress = createProgressTracker({
6994
6844
  homeDir: home,
6995
6845
  codexHomes: () => [path23.join(home, ".codex"), ...libraryCodexHome ? [libraryCodexHome] : []],
@@ -7066,16 +6916,16 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
7066
6916
  requireTask(key.split(":")[0]);
7067
6917
  return key;
7068
6918
  }
7069
- function launchEffort(task, selectedModel = taskModel(task)) {
7070
- const model = models.findModel(taskAgent(task), selectedModel);
7071
- return model?.efforts ? clampEffort(task.effort, model.efforts) : task.effort;
6919
+ function launchEffort(task, route) {
6920
+ const effort = task.effort === "auto" ? route?.effort : task.effort;
6921
+ const model = models.findModel(taskAgent(task), taskModel(task));
6922
+ return model?.efforts ? clampEffort(effort, model.efforts) : effort;
7072
6923
  }
7073
6924
  async function launchCommand(task, intent) {
7074
6925
  const settings = getSettings();
7075
- const library = await libraryLaunchInfo(task.worktreePath);
7076
6926
  const boardCli = await boardCliLaunchInfo();
7077
- let route = task.model?.trim() || task.jevRoute?.status === "routing" ? void 0 : task.jevRoute;
7078
- if (!task.model?.trim() && !intent.resume && intent.includeTaskPrompt !== false) {
6927
+ let route = task.jevRoute?.status === "routing" ? void 0 : task.jevRoute;
6928
+ if (task.effort === "auto" && !intent.resume && intent.includeTaskPrompt !== false) {
7079
6929
  let pending = routing.get(task.id);
7080
6930
  if (!pending) {
7081
6931
  const setRoute = (jevRoute) => {
@@ -7083,9 +6933,9 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
7083
6933
  bus.emit("state:changed", getState());
7084
6934
  };
7085
6935
  pending = (async () => {
7086
- setRoute({ status: "routing", phase: "judging" });
6936
+ setRoute({ status: "routing" });
7087
6937
  const candidateModels = (await (router.enabled === false ? models.get(taskAgent(task)) : models.refresh(taskAgent(task)))).models;
7088
- const selected = await router.route(task, candidateModels, (phase) => setRoute({ status: "routing", phase }));
6938
+ const selected = await router.route(task, candidateModels);
7089
6939
  setRoute(selected);
7090
6940
  return selected;
7091
6941
  })().catch((err) => {
@@ -7096,24 +6946,21 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
7096
6946
  }
7097
6947
  route = await pending;
7098
6948
  }
7099
- const selectedModel = task.model?.trim() ? taskModel(task) : route?.model;
7100
6949
  return buildAgentCommand(
7101
- { ...task, model: selectedModel, effort: launchEffort(task, selectedModel) },
6950
+ { ...task, model: taskModel(task), effort: launchEffort(task, route) },
7102
6951
  settings.systemPrompt ?? "",
7103
6952
  {
7104
6953
  resume: intent.resume === true,
7105
6954
  includeTaskPrompt: intent.includeTaskPrompt !== false,
7106
- library: library ?? void 0,
7107
6955
  boardCli,
7108
- autoMode: task.autoMode ?? settings.autoMode,
7109
- preflightPlan: !intent.resume ? route?.plan : void 0
6956
+ autoMode: task.autoMode ?? settings.autoMode
7110
6957
  },
7111
6958
  task.workspacePath
7112
6959
  );
7113
6960
  }
7114
6961
  function claudeSessionFile(cwd, sessionId) {
7115
6962
  const munged = cwd.replace(/[^a-zA-Z0-9]/g, "-");
7116
- return path23.join(os8.homedir(), ".claude", "projects", munged, `${sessionId}.jsonl`);
6963
+ return path23.join(os7.homedir(), ".claude", "projects", munged, `${sessionId}.jsonl`);
7117
6964
  }
7118
6965
  async function removeTaskWorktree(task) {
7119
6966
  if (!task.projectPath || !task.worktreePath) return;
@@ -7411,6 +7258,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
7411
7258
  command = (await launchCommand(task, intent)).replace(/\r$/, "");
7412
7259
  }
7413
7260
  }
7261
+ if (p.launch) ensureTaskArtifactDirs(task);
7414
7262
  const result = await sessions.start(key, {
7415
7263
  cwd,
7416
7264
  command,
@@ -8106,6 +7954,66 @@ async function checkForUpdate(packageName2, currentVersion, query = async (name)
8106
7954
  return { currentVersion, latestVersion: latest };
8107
7955
  }
8108
7956
 
7957
+ // packages/cli/src/update-terminal.ts
7958
+ import { spawn as spawn8 } from "node:child_process";
7959
+ function updateTerminalCommands(platform) {
7960
+ if (platform === "win32") {
7961
+ return [{ cmd: "powershell.exe", args: ["-NoProfile", "-Command", "Start-Process cmd.exe -ArgumentList '/k vibeflow update'"], waitForExit: true }];
7962
+ }
7963
+ if (platform === "darwin") {
7964
+ return [{ cmd: "osascript", args: [
7965
+ "-e",
7966
+ 'tell application "Terminal" to activate',
7967
+ "-e",
7968
+ 'tell application "Terminal" to do script "sudo vibeflow update"'
7969
+ ], waitForExit: true }];
7970
+ }
7971
+ const command = "sudo vibeflow update; exec sh";
7972
+ return [
7973
+ { cmd: "gnome-terminal", args: ["--", "sh", "-c", command] },
7974
+ { cmd: "x-terminal-emulator", args: ["-e", "sh", "-c", command] },
7975
+ { cmd: "konsole", args: ["-e", "sh", "-c", command] },
7976
+ { cmd: "xfce4-terminal", args: ["--command", `sh -c '${command}'`] },
7977
+ { cmd: "xterm", args: ["-e", "sh", "-c", command] }
7978
+ ];
7979
+ }
7980
+ function launch(command) {
7981
+ return new Promise((resolve, reject) => {
7982
+ const child = spawn8(command.cmd, command.args, {
7983
+ detached: !command.waitForExit,
7984
+ stdio: command.waitForExit ? ["ignore", "ignore", "pipe"] : "ignore",
7985
+ windowsHide: false
7986
+ });
7987
+ child.once("error", reject);
7988
+ if (command.waitForExit) {
7989
+ let stderr = "";
7990
+ child.stderr?.on("data", (chunk) => {
7991
+ stderr += String(chunk);
7992
+ });
7993
+ child.once("close", (code2) => code2 === 0 ? resolve() : reject(new Error(stderr.trim() || `\u7121\u6CD5\u958B\u555F\u7D42\u7AEF\u6A5F\uFF08\u7D50\u675F\u78BC ${code2 ?? "unknown"}\uFF09`)));
7994
+ } else {
7995
+ child.once("spawn", () => {
7996
+ child.unref();
7997
+ resolve();
7998
+ });
7999
+ }
8000
+ });
8001
+ }
8002
+ async function openUpdateTerminal(platform = process.platform, run3 = launch) {
8003
+ if (platform === "linux" && !process.env.DISPLAY && !process.env.WAYLAND_DISPLAY) {
8004
+ throw new Error("\u627E\u4E0D\u5230\u5716\u5F62\u684C\u9762\uFF0C\u7121\u6CD5\u958B\u555F\u66F4\u65B0\u7D42\u7AEF\u6A5F\u3002");
8005
+ }
8006
+ for (const command of updateTerminalCommands(platform)) {
8007
+ try {
8008
+ await run3(command);
8009
+ return;
8010
+ } catch (err) {
8011
+ if (err.code !== "ENOENT") throw err;
8012
+ }
8013
+ }
8014
+ throw new Error("\u627E\u4E0D\u5230\u53EF\u7528\u7684\u7D42\u7AEF\u6A5F\uFF0C\u8ACB\u624B\u52D5\u57F7\u884C\u66F4\u65B0\u6307\u4EE4\u3002");
8015
+ }
8016
+
8109
8017
  // packages/cli/src/host.ts
8110
8018
  var DEFAULT_HOST_PORT = 47820;
8111
8019
  async function waitForPort(userDataDir, timeoutMs = 1e4) {
@@ -8176,7 +8084,11 @@ async function startHost(options) {
8176
8084
  backend: sessions.kind,
8177
8085
  startedAt: lock.startedAt
8178
8086
  }),
8179
- "host:updateStatus": () => availableUpdate
8087
+ "host:updateStatus": () => availableUpdate,
8088
+ "host:openUpdateTerminal": () => {
8089
+ if (!installedPackage || !availableUpdate) throw new Error("\u76EE\u524D\u6C92\u6709\u53EF\u5F9E\u5168\u57DF npm \u5B89\u88DD\u66F4\u65B0\u7684\u7248\u672C\u3002");
8090
+ return openUpdateTerminal();
8091
+ }
8180
8092
  };
8181
8093
  const server = await startWebServer({
8182
8094
  handlers,
@@ -8514,7 +8426,7 @@ Usage:
8514
8426
  vibeflow doctor Check Node, git, tmux, node-pty and the agent CLIs
8515
8427
  (--skip agents,web,\u2026 leaves checks out, e.g. on CI)
8516
8428
  vibeflow update Install the latest global npm release (restart the host afterward)
8517
- vibeflow task \u2026 Create / update cards (vibeflow task --help)
8429
+ vibeflow task \u2026 Create / update cards; add artifacts (vibeflow task --help)
8518
8430
 
8519
8431
  Options:
8520
8432
  --profile dev|prod Store profile (dev = the "(development)" store npm run dev uses)