@kw-yeh/vibeflow 4.5.0 → 4.5.2

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/README.md CHANGED
@@ -19,11 +19,11 @@ vibeflow
19
19
 
20
20
  ## 它能幫你做什麼
21
21
 
22
- - **一卡一分支一 worktree**:建卡時自動建立分支與 worktree、複製 `.env` 等被忽略的檔案,相依套件也在背景複製好,worktree 建好就能直接跑。
22
+ - **一卡一分支一 worktree**:卡片開始執行時,從當下最新的基準分支自動建立分支與 worktree、複製 `.env` 等被忽略的檔案,相依套件也在背景複製好,worktree 建好就能直接跑。
23
23
  - **看板式管理多個 agent**:Backlog → In Progress → Done。把卡片拖進 In Progress,agent 就在它的 worktree 裡開始工作。
24
24
  - **即時終端機**:每張卡都有自己的互動式終端機,agent 要你確認時直接在畫面上回答。
25
25
  - **關掉也不會中斷**:裝了 tmux 時,agent 跑在背景 session 裡;關掉瀏覽器甚至 VibeFlow 本身,任務都會繼續,重新開啟就接回來。
26
- - **看得到 agent 做了什麼**:每張卡有 diff 檢視、artifacts,以及一份會保留下來的決策紀錄(agent 做了哪些決定、為什麼)。
26
+ - **看得到 agent 做了什麼**:每張卡有 diff 檢視、artifacts,以及「決策」分頁:顯示這張卡的分支新增或修改的 `spec.md`(agent 做了哪些決定、為什麼)。卡片完成後打開時預設顯示決策;本地分支刪掉後,會改從 `origin/<branch>` 或 GitHub PR 的 merge commit 讀回合併當時的內容(squash/rebase merge 的卡片讀不回來)。
27
27
  - **選你要的 agent**:Claude Code 或 Codex,每張卡可以各自指定 model、推理強度(effort)與 Auto Mode(是否免確認執行)。
28
28
  - **Web UI 與終端機 UI 共用同一個看板**:習慣瀏覽器用 Web UI,習慣終端機用 `vibeflow tui`。
29
29
  - **內建 skill,可自行調整**:每個人的 Library(設定 → Library)都附帶 `visual-parity`、`pr` 兩個 skill,兩個 agent 都會載入。可以直接編輯、停用或刪除,隨時能還原成 VibeFlow 出貨的版本;沒改過的會跟著 VibeFlow 升級更新。`visual-parity` 需要 Python 與 `pip install playwright && playwright install chromium`。
@@ -59,7 +59,7 @@ VibeFlow 會在本機啟動,並自動用瀏覽器開啟看板。第一次啟
59
59
  3. 寫下標題與你要 agent 做的事,選 agent,按建立。
60
60
  4. 把卡片拖到 **In Progress**,agent 就開始工作;點開卡片就能看到它的即時終端機。
61
61
  5. 做完之後在卡片上檢查 diff;要留下的變更請讓 agent commit、push 或開 PR。
62
- 6. 把卡片移到 **Done**:VibeFlow 會刪掉這張卡的 worktree 與本地分支。**尚未 commit 的變更會一起消失**,移動前會再跟你確認一次。
62
+ 6. 把卡片移到 **Done**:VibeFlow 會刪掉這張卡的 worktree 與本地分支。**尚未 commit 的變更會一起消失**,移動前會再跟你確認一次。已 push 的 `spec.md` 之後仍會顯示在卡片的「決策」分頁。
63
63
 
64
64
  有問題時先跑:
65
65
 
package/dist/vibeflow.mjs CHANGED
@@ -3465,7 +3465,8 @@ async function provisionTaskWorktree(task) {
3465
3465
  branch: result.branch,
3466
3466
  worktreePath: result.worktreePath,
3467
3467
  baseBranch: result.baseBranch,
3468
- pushed: result.pushed
3468
+ pushed: result.pushed,
3469
+ provisionedAt: Date.now()
3469
3470
  };
3470
3471
  } catch (err) {
3471
3472
  throw Object.assign(
@@ -4546,29 +4547,51 @@ async function readWorktreeSpecs(worktreePath, baseBranch) {
4546
4547
  }
4547
4548
  return specs.sort((a, b) => a.path.localeCompare(b.path));
4548
4549
  }
4549
- async function readBranchSpecs(projectPath, branch, baseBranch, changedPaths) {
4550
- const ref = `refs/heads/${branch}`;
4550
+ async function readBranchSpecs(projectPath, branch, baseBranch, changedPaths, launchedAt) {
4551
+ for (const ref of [`refs/heads/${branch}`, `refs/remotes/origin/${branch}`]) {
4552
+ if (!await refExists(projectPath, ref)) continue;
4553
+ const paths = changedPaths ?? await diffNames(projectPath, `${await resolveBaseRef(projectPath, baseBranch)}...${ref}`);
4554
+ const specs = await readSpecsAt(projectPath, ref, paths);
4555
+ if (specs.length > 0) return specs;
4556
+ }
4557
+ const merge = await findPullRequestMerge(projectPath, branch, baseBranch, launchedAt);
4558
+ if (!merge) return [];
4559
+ return readSpecsAt(projectPath, `${merge}^2`, await diffNames(projectPath, `${merge}^1`, merge));
4560
+ }
4561
+ async function refExists(cwd, ref) {
4551
4562
  try {
4552
- await git(projectPath, ["rev-parse", "--verify", ref]);
4563
+ await git(cwd, ["rev-parse", "--verify", "--quiet", ref]);
4564
+ return true;
4553
4565
  } catch {
4554
- return [];
4566
+ return false;
4555
4567
  }
4556
- let paths = changedPaths;
4557
- if (!paths) {
4558
- const baseRef = await resolveBaseRef(projectPath, baseBranch);
4559
- const names = await git(projectPath, [
4560
- "diff",
4561
- "--name-only",
4562
- "--diff-filter=d",
4563
- `${baseRef}...${ref}`
4564
- ]).catch(() => "");
4565
- paths = names.split("\n").map((l) => l.trim());
4568
+ }
4569
+ async function diffNames(cwd, ...range) {
4570
+ const names = await git(cwd, ["diff", "--name-only", "--diff-filter=d", ...range]).catch(() => "");
4571
+ return names.split("\n").map((l) => l.trim());
4572
+ }
4573
+ async function findPullRequestMerge(cwd, branch, baseBranch, launchedAt) {
4574
+ const baseRef = await resolveBaseRef(cwd, baseBranch);
4575
+ const log = await git(cwd, [
4576
+ "log",
4577
+ "--merges",
4578
+ "--first-parent",
4579
+ "--format=%H %s",
4580
+ ...launchedAt ? [`--since=${new Date(launchedAt).toISOString()}`] : [],
4581
+ baseRef
4582
+ ]).catch(() => "");
4583
+ for (const line of log.split("\n")) {
4584
+ const match = /^(\S+) Merge pull request #\d+ from [^/\s]+\/(\S+)$/.exec(line.trim());
4585
+ if (match && match[2] === branch) return match[1];
4566
4586
  }
4587
+ return null;
4588
+ }
4589
+ async function readSpecsAt(cwd, ref, paths) {
4567
4590
  const specs = [];
4568
4591
  for (const p of paths.filter(isSpecPath)) {
4569
4592
  try {
4570
- const content = await git(projectPath, ["show", `${ref}:${p}`]);
4571
- const ct = await git(projectPath, ["log", "-1", "--format=%ct", ref, "--", p]);
4593
+ const content = await git(cwd, ["show", `${ref}:${p}`]);
4594
+ const ct = await git(cwd, ["log", "-1", "--format=%ct", ref, "--", p]);
4572
4595
  specs.push({
4573
4596
  path: p,
4574
4597
  ...clip2(Buffer.from(content, "utf8")),
@@ -5317,6 +5340,7 @@ function createCore({ sessions, bus, version }) {
5317
5340
  ...branchChanged ? { branch: nextBranch, branchExplicit: true } : {},
5318
5341
  worktreePath: void 0,
5319
5342
  pushed: void 0,
5343
+ provisionedAt: void 0,
5320
5344
  launchedAt: void 0
5321
5345
  };
5322
5346
  }
@@ -5446,9 +5470,10 @@ function createCore({ sessions, bus, version }) {
5446
5470
  const task = requireTask(taskId);
5447
5471
  const base = task.baseBranch ?? "main";
5448
5472
  if (task.worktreePath) return readWorktreeSpecs(task.worktreePath, base);
5449
- if (!task.projectPath || !task.launchedAt) return [];
5473
+ const since = task.provisionedAt ?? task.launchedAt ?? (task.pushed !== void 0 ? task.createdAt : void 0);
5474
+ if (!task.projectPath || !since) return [];
5450
5475
  const changed = task.outcome?.files.filter((f) => f.status !== "D").map((f) => f.path);
5451
- return readBranchSpecs(task.projectPath, task.branch, base, changed);
5476
+ return readBranchSpecs(task.projectPath, task.branch, base, changed, since);
5452
5477
  },
5453
5478
  "board:getCliLaunchInfo": () => boardCliLaunchInfo(),
5454
5479
  "library:list": async () => listLibrary(await libraryRoot(), builtinSkillsDir()),
@@ -1 +1 @@
1
- <!DOCTYPE html><html><head><meta charSet="utf-8" data-next-head=""/><meta name="viewport" content="width=device-width" data-next-head=""/><link rel="icon" href="/favicon.ico" sizes="any" data-next-head=""/><link rel="icon" href="/logo.svg" type="image/svg+xml" data-next-head=""/><link rel="apple-touch-icon" href="/apple-touch-icon.png" data-next-head=""/><title data-next-head="">404: This page could not be found</title><link rel="preload" href="/_next/static/css/24b804d01aa3af3c.css" as="style"/><link rel="stylesheet" href="/_next/static/css/24b804d01aa3af3c.css" data-n-g=""/><noscript data-n-css=""></noscript><script defer="" noModule="" src="/_next/static/chunks/polyfills-42372ed130431b0a.js"></script><script src="/_next/static/chunks/webpack-c8aa5df0425f6b7b.js" defer=""></script><script src="/_next/static/chunks/framework-b92a367cf1ad4bf7.js" defer=""></script><script src="/_next/static/chunks/main-ec7fd4c371d06729.js" defer=""></script><script src="/_next/static/chunks/pages/_app-2a4ae532bdcab6f2.js" defer=""></script><script src="/_next/static/chunks/pages/_error-8f65b0f8749f42d2.js" defer=""></script><script src="/_next/static/GVhAr0pZILduxKDBurk9J/_buildManifest.js" defer=""></script><script src="/_next/static/GVhAr0pZILduxKDBurk9J/_ssgManifest.js" defer=""></script></head><body><div id="__next"><div style="font-family:system-ui,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div style="line-height:48px"><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding-right:23px;font-size:24px;font-weight:500;vertical-align:top">404</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:28px">This page could not be found<!-- -->.</h2></div></div></div></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{"statusCode":404}},"page":"/_error","query":{},"buildId":"GVhAr0pZILduxKDBurk9J","nextExport":true,"isFallback":false,"gip":true,"scriptLoader":[]}</script></body></html>
1
+ <!DOCTYPE html><html><head><meta charSet="utf-8" data-next-head=""/><meta name="viewport" content="width=device-width" data-next-head=""/><link rel="icon" href="/favicon.ico" sizes="any" data-next-head=""/><link rel="icon" href="/logo.svg" type="image/svg+xml" data-next-head=""/><link rel="apple-touch-icon" href="/apple-touch-icon.png" data-next-head=""/><title data-next-head="">404: This page could not be found</title><link rel="preload" href="/_next/static/css/24b804d01aa3af3c.css" as="style"/><link rel="stylesheet" href="/_next/static/css/24b804d01aa3af3c.css" data-n-g=""/><noscript data-n-css=""></noscript><script defer="" noModule="" src="/_next/static/chunks/polyfills-42372ed130431b0a.js"></script><script src="/_next/static/chunks/webpack-c8aa5df0425f6b7b.js" defer=""></script><script src="/_next/static/chunks/framework-b92a367cf1ad4bf7.js" defer=""></script><script src="/_next/static/chunks/main-ec7fd4c371d06729.js" defer=""></script><script src="/_next/static/chunks/pages/_app-2a4ae532bdcab6f2.js" defer=""></script><script src="/_next/static/chunks/pages/_error-8f65b0f8749f42d2.js" defer=""></script><script src="/_next/static/5P4nrPXSK7r5MwLNDxF-J/_buildManifest.js" defer=""></script><script src="/_next/static/5P4nrPXSK7r5MwLNDxF-J/_ssgManifest.js" defer=""></script></head><body><div id="__next"><div style="font-family:system-ui,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div style="line-height:48px"><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding-right:23px;font-size:24px;font-weight:500;vertical-align:top">404</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:28px">This page could not be found<!-- -->.</h2></div></div></div></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{"statusCode":404}},"page":"/_error","query":{},"buildId":"5P4nrPXSK7r5MwLNDxF-J","nextExport":true,"isFallback":false,"gip":true,"scriptLoader":[]}</script></body></html>
package/dist/web/404.html CHANGED
@@ -1 +1 @@
1
- <!DOCTYPE html><html><head><meta charSet="utf-8" data-next-head=""/><meta name="viewport" content="width=device-width" data-next-head=""/><link rel="icon" href="/favicon.ico" sizes="any" data-next-head=""/><link rel="icon" href="/logo.svg" type="image/svg+xml" data-next-head=""/><link rel="apple-touch-icon" href="/apple-touch-icon.png" data-next-head=""/><title data-next-head="">404: This page could not be found</title><link rel="preload" href="/_next/static/css/24b804d01aa3af3c.css" as="style"/><link rel="stylesheet" href="/_next/static/css/24b804d01aa3af3c.css" data-n-g=""/><noscript data-n-css=""></noscript><script defer="" noModule="" src="/_next/static/chunks/polyfills-42372ed130431b0a.js"></script><script src="/_next/static/chunks/webpack-c8aa5df0425f6b7b.js" defer=""></script><script src="/_next/static/chunks/framework-b92a367cf1ad4bf7.js" defer=""></script><script src="/_next/static/chunks/main-ec7fd4c371d06729.js" defer=""></script><script src="/_next/static/chunks/pages/_app-2a4ae532bdcab6f2.js" defer=""></script><script src="/_next/static/chunks/pages/_error-8f65b0f8749f42d2.js" defer=""></script><script src="/_next/static/GVhAr0pZILduxKDBurk9J/_buildManifest.js" defer=""></script><script src="/_next/static/GVhAr0pZILduxKDBurk9J/_ssgManifest.js" defer=""></script></head><body><div id="__next"><div style="font-family:system-ui,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div style="line-height:48px"><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding-right:23px;font-size:24px;font-weight:500;vertical-align:top">404</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:28px">This page could not be found<!-- -->.</h2></div></div></div></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{"statusCode":404}},"page":"/_error","query":{},"buildId":"GVhAr0pZILduxKDBurk9J","nextExport":true,"isFallback":false,"gip":true,"scriptLoader":[]}</script></body></html>
1
+ <!DOCTYPE html><html><head><meta charSet="utf-8" data-next-head=""/><meta name="viewport" content="width=device-width" data-next-head=""/><link rel="icon" href="/favicon.ico" sizes="any" data-next-head=""/><link rel="icon" href="/logo.svg" type="image/svg+xml" data-next-head=""/><link rel="apple-touch-icon" href="/apple-touch-icon.png" data-next-head=""/><title data-next-head="">404: This page could not be found</title><link rel="preload" href="/_next/static/css/24b804d01aa3af3c.css" as="style"/><link rel="stylesheet" href="/_next/static/css/24b804d01aa3af3c.css" data-n-g=""/><noscript data-n-css=""></noscript><script defer="" noModule="" src="/_next/static/chunks/polyfills-42372ed130431b0a.js"></script><script src="/_next/static/chunks/webpack-c8aa5df0425f6b7b.js" defer=""></script><script src="/_next/static/chunks/framework-b92a367cf1ad4bf7.js" defer=""></script><script src="/_next/static/chunks/main-ec7fd4c371d06729.js" defer=""></script><script src="/_next/static/chunks/pages/_app-2a4ae532bdcab6f2.js" defer=""></script><script src="/_next/static/chunks/pages/_error-8f65b0f8749f42d2.js" defer=""></script><script src="/_next/static/5P4nrPXSK7r5MwLNDxF-J/_buildManifest.js" defer=""></script><script src="/_next/static/5P4nrPXSK7r5MwLNDxF-J/_ssgManifest.js" defer=""></script></head><body><div id="__next"><div style="font-family:system-ui,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div style="line-height:48px"><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding-right:23px;font-size:24px;font-weight:500;vertical-align:top">404</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:28px">This page could not be found<!-- -->.</h2></div></div></div></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{"statusCode":404}},"page":"/_error","query":{},"buildId":"5P4nrPXSK7r5MwLNDxF-J","nextExport":true,"isFallback":false,"gip":true,"scriptLoader":[]}</script></body></html>
@@ -1 +1 @@
1
- <!DOCTYPE html><html><head><meta charSet="utf-8" data-next-head=""/><meta name="viewport" content="width=device-width" data-next-head=""/><link rel="icon" href="/favicon.ico" sizes="any" data-next-head=""/><link rel="icon" href="/logo.svg" type="image/svg+xml" data-next-head=""/><link rel="apple-touch-icon" href="/apple-touch-icon.png" data-next-head=""/><title data-next-head="">VibeFlow</title><link rel="preload" href="/_next/static/css/24b804d01aa3af3c.css" as="style"/><link rel="stylesheet" href="/_next/static/css/24b804d01aa3af3c.css" data-n-g=""/><noscript data-n-css=""></noscript><script defer="" noModule="" src="/_next/static/chunks/polyfills-42372ed130431b0a.js"></script><script src="/_next/static/chunks/webpack-c8aa5df0425f6b7b.js" defer=""></script><script src="/_next/static/chunks/framework-b92a367cf1ad4bf7.js" defer=""></script><script src="/_next/static/chunks/main-ec7fd4c371d06729.js" defer=""></script><script src="/_next/static/chunks/pages/_app-2a4ae532bdcab6f2.js" defer=""></script><script src="/_next/static/chunks/2524-abaf3a55023f6066.js" defer=""></script><script src="/_next/static/chunks/pages/home-a511856e480ddb33.js" defer=""></script><script src="/_next/static/GVhAr0pZILduxKDBurk9J/_buildManifest.js" defer=""></script><script src="/_next/static/GVhAr0pZILduxKDBurk9J/_ssgManifest.js" defer=""></script></head><body><div id="__next"><div class="bg-background text-foreground"><div class="flex min-h-screen items-center justify-center bg-background text-base text-muted-foreground">載入中…</div></div></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{}},"page":"/home","query":{},"buildId":"GVhAr0pZILduxKDBurk9J","nextExport":true,"autoExport":true,"isFallback":false,"scriptLoader":[]}</script></body></html>
1
+ <!DOCTYPE html><html><head><meta charSet="utf-8" data-next-head=""/><meta name="viewport" content="width=device-width" data-next-head=""/><link rel="icon" href="/favicon.ico" sizes="any" data-next-head=""/><link rel="icon" href="/logo.svg" type="image/svg+xml" data-next-head=""/><link rel="apple-touch-icon" href="/apple-touch-icon.png" data-next-head=""/><title data-next-head="">VibeFlow</title><link rel="preload" href="/_next/static/css/24b804d01aa3af3c.css" as="style"/><link rel="stylesheet" href="/_next/static/css/24b804d01aa3af3c.css" data-n-g=""/><noscript data-n-css=""></noscript><script defer="" noModule="" src="/_next/static/chunks/polyfills-42372ed130431b0a.js"></script><script src="/_next/static/chunks/webpack-c8aa5df0425f6b7b.js" defer=""></script><script src="/_next/static/chunks/framework-b92a367cf1ad4bf7.js" defer=""></script><script src="/_next/static/chunks/main-ec7fd4c371d06729.js" defer=""></script><script src="/_next/static/chunks/pages/_app-2a4ae532bdcab6f2.js" defer=""></script><script src="/_next/static/chunks/2524-abaf3a55023f6066.js" defer=""></script><script src="/_next/static/chunks/pages/home-a511856e480ddb33.js" defer=""></script><script src="/_next/static/5P4nrPXSK7r5MwLNDxF-J/_buildManifest.js" defer=""></script><script src="/_next/static/5P4nrPXSK7r5MwLNDxF-J/_ssgManifest.js" defer=""></script></head><body><div id="__next"><div class="bg-background text-foreground"><div class="flex min-h-screen items-center justify-center bg-background text-base text-muted-foreground">載入中…</div></div></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{}},"page":"/home","query":{},"buildId":"5P4nrPXSK7r5MwLNDxF-J","nextExport":true,"autoExport":true,"isFallback":false,"scriptLoader":[]}</script></body></html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kw-yeh/vibeflow",
3
- "version": "4.5.0",
3
+ "version": "4.5.2",
4
4
  "description": "Intent-driven local kanban for coding agents: git worktree per card, Web UI and TUI.",
5
5
  "license": "MIT",
6
6
  "author": "KW-Yeh",