@kw-yeh/vibeflow 4.0.1 → 4.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,146 +1,165 @@
1
1
  # VibeFlow
2
2
 
3
- VibeFlow 是一款專為本地開發設計的「意圖驅動」看板平台。它將 Claude Code CLI 的強大生成能力與 Git Worktree 的多工隔離機制結合,讓開發者能透過視覺化看板同時管理多個開發任務,並在獨立的工作區中安全地進行代碼實驗與自動化實作。
3
+ **讓多個 coding agent 同時開工的本機看板。**
4
4
 
5
- ## 🛠 核心技術棧 (Tech Stack)
5
+ 每張卡片是一個任務。卡片一開始,VibeFlow 就幫它開一條獨立的 Git 分支與 worktree,在裡面啟動 Claude Code 或 Codex,並把即時終端機放在卡片旁邊。好幾張卡可以同時跑,彼此不會改到同一份檔案,你的專案資料夾本身也不會被切換分支。
6
6
 
7
- * **核心**: `packages/core` — 任務、git worktree、agent 指令組裝、session 管理,不依賴任何 UI
8
- * **前端**: 本機 Web UI(Next.js 靜態輸出,由 core 提供)· 終端機 TUI(Ink)
9
- * **看板**: `@hello-pangea/dnd`
10
- * **終端**: `xterm.js` ↔ WebSocket ↔ core;session 由 tmux(macOS / Linux)或 `node-pty` 持有
11
- * **儲存**: core 自有的 JSON store(`packages/core/src/json-store.ts`,沿用舊版桌面 App 的 electron-store 檔案,舊看板會直接接上)
7
+ ```bash
8
+ npm i -g @kw-yeh/vibeflow@latest
9
+ vibeflow
10
+ ```
12
11
 
13
- ## 📦 安裝與執行
12
+ 一切都在你的電腦上執行:不需要另外註冊帳號,看板與程式碼都留在本機。
14
13
 
15
- | 方式 | 指令 | 適合對象 |
16
- |---|---|---|
17
- | 免安裝試用 | `npx @kw-yeh/vibeflow` | 第一次接觸的人 |
18
- | 全域安裝 | `npm i -g @kw-yeh/vibeflow`,之後執行 `vibeflow` | 日常使用者 |
19
- | 從原始碼執行 | `git clone` → `npm install` → `npm start` | 貢獻者、想改程式的人 |
20
- | 從原始碼一鍵啟動 | macOS / Linux:雙擊 `start.command`(或執行 `./start.command`);Windows:雙擊 `start.cmd` | 用 clone 下來的版本日常使用:自動安裝相依、建置 Web UI 並開啟網頁 |
14
+ ---
15
+
16
+ ## 它能幫你做什麼
17
+
18
+ - **一卡一分支一 worktree**:建卡時自動建立分支與 worktree、複製 `.env` 等被忽略的檔案,相依套件也在背景複製好,worktree 建好就能直接跑。
19
+ - **看板式管理多個 agent**:Backlog → In Progress → Done。把卡片拖進 In Progress,agent 就在它的 worktree 裡開始工作。
20
+ - **即時終端機**:每張卡都有自己的互動式終端機,agent 要你確認時直接在畫面上回答。
21
+ - **關掉也不會中斷**:裝了 tmux 時,agent 跑在背景 session 裡;關掉瀏覽器甚至 VibeFlow 本身,任務都會繼續,重新開啟就接回來。
22
+ - **看得到 agent 做了什麼**:每張卡有 diff 檢視、artifacts,以及一份會保留下來的決策紀錄(agent 做了哪些決定、為什麼)。
23
+ - **選你要的 agent**:Claude Code 或 Codex,每張卡可以各自指定 model、推理強度(effort)與 Auto Mode(是否免確認執行)。
24
+ - **Web UI 與終端機 UI 共用同一個看板**:習慣瀏覽器用 Web UI,習慣終端機用 `vibeflow tui`。
25
+ - **也能用指令操作**:`vibeflow task create` 不開 UI 直接建卡;agent 自己也能在看板上建立後續的卡片。
26
+
27
+ ---
28
+
29
+ ## 快速開始
21
30
 
22
- `vibeflow` 會在本機啟動 core,並用瀏覽器開啟 Web UI。它不是需要簽章的 App,所以不會被 Gatekeeper 或 SmartScreen 攔下。
31
+ ### 1. 先準備好這些
32
+
33
+ | 需要 | 說明 |
34
+ |---|---|
35
+ | **Node.js 22+** | [nodejs.org](https://nodejs.org) |
36
+ | **Git 2.30+** | 要操作的專案必須是 Git repository |
37
+ | **Claude Code 或 Codex CLI,且已登入** | 至少一個。Claude Code:`npm install -g @anthropic-ai/claude-code`(或 `curl -fsSL https://claude.ai/install.sh \| bash`),裝完執行一次 `claude` 完成登入 |
38
+ | tmux(建議) | macOS:`brew install tmux`。沒有也能用,只是關掉 VibeFlow 時 agent 會一起結束 |
39
+ | Git Bash(僅 Windows) | 隨 [Git for Windows](https://git-scm.com/download/win) 安裝 |
40
+
41
+ ### 2. 安裝並啟動
23
42
 
24
43
  ```bash
25
- vibeflow # 啟動(或沿用已在執行的)core,開啟 Web UI
26
- vibeflow tui # 終端機介面,和 Web UI 操作同一份看板與 session
27
- vibeflow status # host、執行中的 session、看板摘要
28
- vibeflow stop <id> | --all # 停止 agent session
29
- vibeflow shutdown # 停止 host(tmux 裡的 agent 會繼續執行)
30
- vibeflow open <path> # 把專案資料夾加入最近使用清單,再開啟 Web UI
31
- vibeflow doctor # 檢查前置需求,並列出修正指令
32
- vibeflow task create … # 不開 UI 直接建卡(vibeflow task --help)
44
+ npm i -g @kw-yeh/vibeflow@latest
45
+ vibeflow
33
46
  ```
34
47
 
35
- 同一台機器同時只會有一個 core(`<appData>/vibeflow/core.lock`)。再次執行 `vibeflow` 或 `vibeflow tui` 時,會連到已在執行的那一個。
48
+ VibeFlow 會在本機啟動,並自動用瀏覽器開啟看板。第一次啟動時如果偵測到缺了什麼,會直接告訴你該怎麼補。
36
49
 
37
- **任務不綁 UI**:有 tmux 時,每張卡片的 agent 跑在 `tmux -L vibeflow` 的 session 裡。關掉瀏覽器、TUI,甚至 core 本身,agent 都會繼續執行,重新開啟即可接回。沒有 tmux 時(包括 Windows),agent 由 core 直接持有,會隨 core 一起結束。
50
+ ### 3. 開第一張卡
38
51
 
39
- ### ✅ 前置需求
52
+ 1. 按側邊欄的 **+**,或 Backlog 欄的 **新建任務**。
53
+ 2. 選一個專案資料夾(必須是 Git repo),選要從哪條分支開始。
54
+ 3. 寫下標題與你要 agent 做的事,選 agent,按建立。
55
+ 4. 把卡片拖到 **In Progress**,agent 就開始工作;點開卡片就能看到它的即時終端機。
56
+ 5. 做完之後在卡片上檢查 diff;要留下的變更請讓 agent commit、push 或開 PR。
57
+ 6. 把卡片移到 **Done**:VibeFlow 會刪掉這張卡的 worktree 與本地分支。**尚未 commit 的變更會一起消失**,移動前會再跟你確認一次。
40
58
 
41
- 1. **Node.js 22 以上**。
42
- 2. **Git 2.30 以上**,且要操作的專案必須是 Git repository。若要使用「Approve & Push」,推送認證(SSH key 或 credential helper)需事先設定好。
43
- 3. **tmux**(建議;macOS 用 `brew install tmux`)。沒有時會自動改用 node-pty,並在 `vibeflow doctor` 裡提示。
44
- 4. **claude 或 codex CLI 至少一個,已登入**:
45
- ```bash
46
- npm install -g @anthropic-ai/claude-code # 或官方 installer:curl -fsSL https://claude.ai/install.sh | bash
47
- ```
48
- 5. **Windows**:需要 Git for Windows 的 Git Bash(agent 的啟動指令是 POSIX sh)。
59
+ 有問題時先跑:
49
60
 
50
- 有問題時先執行 `vibeflow doctor`。第一次啟動若偵測到問題,也會自動檢查一次。
61
+ ```bash
62
+ vibeflow doctor
63
+ ```
51
64
 
52
- ### 🔒 本機伺服器的安全性
65
+ 它會逐項檢查 Node、Git、tmux、終端機模組與 agent CLI,並列出修正指令。
53
66
 
54
- core 的 Web 伺服器能啟動 agent 和寫入終端機,因此:
67
+ ---
55
68
 
56
- - 只綁定 `127.0.0.1` / `::1`,使用隨機 port。
57
- - 必須帶每次啟動時產生的 token 才能進入。`vibeflow` 開啟的網址會帶上 token,載入後換成 HttpOnly、SameSite=Strict 的 cookie,並從網址列移除。
58
- - WebSocket 握手會同時檢查 cookie 與 `Origin`,其他網頁無法從你的瀏覽器連進來。
59
- - `Host` 標頭必須是 loopback,用來防範 DNS rebinding。
60
- - 鎖檔權限為 0600。
61
- - API 只接受 core 認得的 task id,不接受任意路徑或指令。
69
+ ## 安裝方式
62
70
 
63
- ## 🚢 發佈新版本(Maintainers)
71
+ | 方式 | 指令 | 適合 |
72
+ |---|---|---|
73
+ | **全域安裝(建議)** | `npm i -g @kw-yeh/vibeflow@latest`,之後執行 `vibeflow` | 大多數人 |
74
+ | 免安裝試用 | `npx @kw-yeh/vibeflow` | 只想先看看、不想安裝 |
75
+ | 從原始碼一鍵啟動 | clone 後雙擊 `start.command`(macOS/Linux)或 `start.cmd`(Windows) | 想用最新的 `main`,或要改程式 |
64
76
 
65
- CI(`.github/workflows/release.yml`)在每個 PR 上只跑第 1 步;推送 `v*` tag 時會:
77
+ 三種方式讀寫的是同一份看板,可以隨時換。
66
78
 
67
- 1. 在 Ubuntu、macOS、Windows 上跑 typecheck、`npm test`、Web UI 端對端測試與 `vibeflow doctor`。
68
- 2. `npm pack` 後以全域安裝方式驗證能啟動,再執行 `npm publish --provenance`(需要 `NPM_TOKEN` secret)。
79
+ 之後要更新到最新版,再執行一次同一行安裝指令即可:
69
80
 
70
81
  ```bash
71
- # 1. 更新 package.json 的 version 並 commit
72
- # 2. 上 tag 並推送
73
- git tag v4.0.0
74
- git push origin v4.0.0
82
+ npm i -g @kw-yeh/vibeflow@latest
75
83
  ```
76
84
 
77
- 本地驗證:`npm run build:npm && npm pack ./dist-npm`。
78
-
79
- ## 📂 專案資料結構與 Worktree 隔離設計
85
+ ### 安裝時出現 `EACCES: permission denied`
80
86
 
81
- VibeFlow 會在您開啟的專案中建立一個隱藏的暫存空間,確保主目錄程式碼在開發過程中保持乾淨:
87
+ 代表 npm 的全域目錄屬於系統管理員(從 nodejs.org 下載安裝檔裝 Node 時常見)。建議不要用 `sudo`,改成讓全域套件裝在你自己的家目錄:
82
88
 
89
+ ```bash
90
+ mkdir -p ~/.npm-global
91
+ npm config set prefix ~/.npm-global
92
+ echo 'export PATH="$HOME/.npm-global/bin:$PATH"' >> ~/.zshrc
93
+ source ~/.zshrc
94
+ npm i -g @kw-yeh/vibeflow@latest
83
95
  ```
84
- [專案目錄] /
85
- ├── .vibeflow/ <-- 自動加入 .gitignore
86
- │ ├── task-uuid-1/ <-- 卡片 A 的獨立 Worktree (分支: vf-task-1)
87
- │ └── task-uuid-2/ <-- 卡片 B 的獨立 Worktree (分支: vf-task-2)
88
- ├── src/
89
- └── package.json
96
+
97
+ 用 Homebrew(`brew install node`)或 [nvm](https://github.com/nvm-sh/nvm) 裝的 Node 本來就不需要 `sudo`。已經用 `sudo` 裝好也能正常使用;若 `vibeflow doctor` 回報終端機模組沒有執行權限,照它列出的 `sudo chmod +x …` 指令執行一次即可。
98
+
99
+ ---
100
+
101
+ ## 常用指令
102
+
103
+ ```bash
104
+ vibeflow # 啟動(或連到已在執行的)VibeFlow,開啟 Web UI
105
+ vibeflow tui # 終端機介面,和 Web UI 操作同一個看板
106
+ vibeflow status # 目前的 host、執行中的 session、看板摘要
107
+ vibeflow stop <id> | --all # 停止某張卡(或全部)的 agent
108
+ vibeflow shutdown # 關閉 VibeFlow(tmux 裡的 agent 會繼續執行)
109
+ vibeflow open <path> # 把專案資料夾加進最近使用清單並開啟 Web UI
110
+ vibeflow doctor # 檢查前置需求
111
+ vibeflow task create … # 不開 UI 直接建卡,選項見 vibeflow task --help
90
112
  ```
91
113
 
92
- ## 🚀 核心工作流
114
+ 沒有全域安裝、改用 `npx` 時,把 `vibeflow` 換成 `npx @kw-yeh/vibeflow`。
93
115
 
94
- ### 1. 任務初始化 (Task Creation)
116
+ ---
95
117
 
96
- * **路徑偵測**: 使用者選擇本地專案資料夾,App 自動檢查 Git 狀態與 Remote 資訊。
97
- * **分支策略**: 若有 Remote,彈窗讓使用者選擇基準分支(Base Branch)。
98
- * **背景準備**:
99
- 1. 自動將 `.vibeflow/` 寫入 `.gitignore`。
100
- 2. 執行 `git worktree add -b vf-[id] .vibeflow/vf-[id] origin/[選定分支]`。
101
- 3. 執行 `git push -u origin vf-[id]` 將工作分支同步至雲端。
118
+ ## 運作方式
102
119
 
103
- ### 2. 交互執行 (Execution & Live Terminal)
120
+ **檔案放在哪裡**
104
121
 
105
- * **PTY 整合**: 當卡片拖入 `In Progress` 或點擊執行,core 在該 Worktree 路徑啟動 `claude` CLI(tmux session,或沒有 tmux 時的 `node-pty`)。
106
- * **互動模式 (Type A)**:
107
- * 卡片展開為 **互動式終端機**。
108
- * 使用者可直接在畫面上回答 Claude 的提問(例如:`y/n` 或確認指令)。
109
- * **多工並行**: 支援同時啟動多張卡片,每張卡片擁有獨立的 PTY 進程與 Worktree 環境,互不干擾。
122
+ - 每張卡的 worktree 建在「工作站資料夾」底下:`<工作站>/<專案名>/<分支名>`。工作站預設是 `~/Desktop`,可在 **設定** 裡改。
123
+ - 你的專案資料夾本身不會被切換分支,也不會多出 worktree。
124
+ - 專案有 remote 時,新分支會在建卡時 push 到 origin;有設 CI 的話可能會被觸發。
125
+ - 看板資料存在 `~/Library/Application Support/vibeflow/`(macOS)、`%APPDATA%\vibeflow\`(Windows)或 `~/.config/vibeflow/`(Linux)。
110
126
 
111
- ### 3. 審查與清理 (Review & Finalize)
127
+ **同一台電腦只有一個 VibeFlow**
112
128
 
113
- * **視覺化 Diff**: 任務完成後,讀取該 Worktree 的 `git diff` 並以 Side-by-side 模式呈現。
114
- * **一鍵合併**: 使用者點擊 Approve 後,App 在 Worktree 執行 `git commit` 與 `git push`。
115
- * **自動清理**: 卡片移至 `Done` 後,App 自動執行 `git worktree remove`,徹底刪除 `.vibeflow/` 下的暫存資料夾,保持硬碟整潔。
129
+ 再次執行 `vibeflow` 或 `vibeflow tui` 時,會連到已在執行的那一個,不會開出第二份。
116
130
 
117
- ## 📅 開發階段 (Milestones)
131
+ **本機伺服器的安全性**
118
132
 
119
- ### 第一階段:基礎架構 (Day 1-2)
133
+ VibeFlow 的本機伺服器能啟動 agent、寫入終端機,因此:
120
134
 
121
- * [x] 搭建 Electron + Next.js 環境。
122
- * [x] 實作看板拖曳介面。
123
- * [x] 整合 `electron-store` 紀錄本地專案路徑與卡片狀態。
135
+ - 只綁定 `127.0.0.1`/`::1`,使用隨機 port。
136
+ - 每次啟動產生一組 token,網址帶上它才能進入;載入後換成 HttpOnly、SameSite=Strict 的 cookie,並從網址列移除。
137
+ - WebSocket 連線同時檢查 cookie 與 `Origin`,`Host` 必須是 loopback,其他網頁無法從你的瀏覽器連進來。
138
+ - API 只接受 VibeFlow 認得的卡片 id,不接受任意路徑或指令。
124
139
 
125
- ### 第二階段:Git 自動化 (Day 3-4)
140
+ ---
126
141
 
127
- * [x] 實作 `git remote` 偵測與分支選擇彈窗。
128
- * [x] 實作自動建立 Worktree 與分支推送的後端邏輯。
129
- * [x] 確保 `.vibeflow/` 資料夾被正確忽略。
142
+ ## 參與開發
130
143
 
131
- ### 第三階段:互動終端 (Day 5-7)
144
+ ```bash
145
+ git clone https://github.com/KW-Yeh/VibeFlow.git
146
+ cd VibeFlow
147
+ ./start.command # 或:npm install && npm start
148
+ ```
149
+
150
+ 架構、慣例、測試指令與完成標準都在 [AGENTS.md](AGENTS.md)。
132
151
 
133
- * [x] 在主進程封裝 `node-pty` 與 `claude` CLI。
134
- * [x] 前端整合 `xterm.js`,達成雙向串流(輸出顯示 + 鍵盤輸入)。
135
- * [x] 實作多卡片並行的 PTY 進程管理。
152
+ ### 發佈新版本(Maintainers)
136
153
 
137
- ### 第四階段:Review & Cleanup (Day 8-10)
154
+ 推送 `v*` tag 會觸發 [CI](.github/workflows/release.yml):在 Ubuntu、macOS、Windows 上跑 typecheck、測試、端對端測試與 `vibeflow doctor`,打包後實際安裝驗證,再以 npm Trusted Publishing(OIDC,不需要 token)發佈 `@kw-yeh/vibeflow`。
155
+
156
+ ```bash
157
+ npm version <x.y.z> -m "chore: release v%s"
158
+ git push origin main --follow-tags
159
+ ```
138
160
 
139
- * [x] 整合 `react-diff-viewer` 呈現程式碼變更。
140
- * [x] 實作 Approve 後的自動提交與 Worktree 清理機制。
161
+ 本地驗證打包:`npm run build:npm && npm pack ./dist-npm`。
141
162
 
142
- ## ⚠️ 開發提醒
163
+ ## License
143
164
 
144
- 1. **Native Modules**: `node-pty` uses packaged prebuilt binaries where available; use `npm run check:node-pty` to confirm the installed binary loads.
145
- 2. **互動處理**: 確保 `xterm.js` 的輸入能即時傳回 PTY,這對於 Claude Code 的互動式提問(Type A)至關重要。
146
- 3. **環境變數**: core 呼叫 `claude` 時需手動注入使用者的 `PATH`(`packages/core/src/env.ts`),否則會找不到指令。
165
+ [MIT](LICENSE)
@@ -0,0 +1,57 @@
1
+ import { createRequire as __vfCreateRequire } from 'node:module';
2
+ const require = __vfCreateRequire(import.meta.url);
3
+
4
+ // packages/core/src/pty-helper.ts
5
+ import fs from "fs";
6
+ import path from "path";
7
+ import { createRequire } from "module";
8
+ function nodePtyRoot() {
9
+ try {
10
+ return path.dirname(createRequire(import.meta.url).resolve("node-pty/package.json"));
11
+ } catch {
12
+ return null;
13
+ }
14
+ }
15
+ function ensurePtySpawnHelper(root = nodePtyRoot()) {
16
+ if (process.platform === "win32" || !root) return [];
17
+ const candidates = [
18
+ path.join(root, "build", "Release", "spawn-helper"),
19
+ path.join(root, "prebuilds", `${process.platform}-${process.arch}`, "spawn-helper")
20
+ ];
21
+ const failures = [];
22
+ for (const file of candidates) {
23
+ let stat;
24
+ try {
25
+ stat = fs.statSync(file);
26
+ } catch {
27
+ continue;
28
+ }
29
+ try {
30
+ fs.accessSync(file, fs.constants.X_OK);
31
+ continue;
32
+ } catch {
33
+ }
34
+ try {
35
+ fs.chmodSync(file, stat.mode & 4095 | 73);
36
+ } catch (err) {
37
+ const uid = process.getuid?.();
38
+ failures.push({ path: file, error: err.message, ownedByOther: uid !== void 0 && stat.uid !== uid });
39
+ }
40
+ }
41
+ return failures;
42
+ }
43
+ function spawnHelperFixCommand(failures) {
44
+ const sudo = failures.some((f) => f.ownedByOther) ? "sudo " : "";
45
+ return `${sudo}chmod +x ${failures.map((f) => `"${f.path}"`).join(" ")}`;
46
+ }
47
+
48
+ // packages/cli/src/postinstall.ts
49
+ try {
50
+ const failures = ensurePtySpawnHelper();
51
+ if (failures.length > 0) {
52
+ console.warn(`[vibeflow] node-pty spawn-helper \u7121\u6CD5\u8A2D\u70BA\u53EF\u57F7\u884C\uFF0C\u7D42\u7AEF\u6A5F\u6703\u7121\u6CD5\u555F\u52D5\u3002\u8ACB\u57F7\u884C\uFF1A
53
+ ${spawnHelperFixCommand(failures)}`);
54
+ }
55
+ } catch (err) {
56
+ console.warn(`[vibeflow] postinstall \u7565\u904E\uFF1A${err.message}`);
57
+ }
package/dist/vibeflow.mjs CHANGED
@@ -1899,9 +1899,9 @@ function ensurePtySpawnHelper(root = nodePtyRoot()) {
1899
1899
  ];
1900
1900
  const failures = [];
1901
1901
  for (const file of candidates) {
1902
- let mode;
1902
+ let stat;
1903
1903
  try {
1904
- mode = fs4.statSync(file).mode;
1904
+ stat = fs4.statSync(file);
1905
1905
  } catch {
1906
1906
  continue;
1907
1907
  }
@@ -1911,13 +1911,18 @@ function ensurePtySpawnHelper(root = nodePtyRoot()) {
1911
1911
  } catch {
1912
1912
  }
1913
1913
  try {
1914
- fs4.chmodSync(file, mode & 4095 | 73);
1914
+ fs4.chmodSync(file, stat.mode & 4095 | 73);
1915
1915
  } catch (err) {
1916
- failures.push({ path: file, error: err.message });
1916
+ const uid = process.getuid?.();
1917
+ failures.push({ path: file, error: err.message, ownedByOther: uid !== void 0 && stat.uid !== uid });
1917
1918
  }
1918
1919
  }
1919
1920
  return failures;
1920
1921
  }
1922
+ function spawnHelperFixCommand(failures) {
1923
+ const sudo = failures.some((f) => f.ownedByOther) ? "sudo " : "";
1924
+ return `${sudo}chmod +x ${failures.map((f) => `"${f.path}"`).join(" ")}`;
1925
+ }
1921
1926
 
1922
1927
  // packages/cli/src/task-command.ts
1923
1928
  import { parseArgs } from "node:util";
@@ -5641,8 +5646,8 @@ async function checkNodePty() {
5641
5646
  id: "node-pty",
5642
5647
  name: "node-pty",
5643
5648
  level: "fail",
5644
- detail: `spawn-helper \u6C92\u6709\u57F7\u884C\u6B0A\u9650\uFF1A${unrepaired[0].error}`,
5645
- fix: unrepaired.map((f) => `chmod +x "${f.path}"`).join(" && ")
5649
+ detail: unrepaired.some((f) => f.ownedByOther) ? "spawn-helper \u6C92\u6709\u57F7\u884C\u6B0A\u9650\uFF0C\u800C\u4E14\u6A94\u6848\u5C6C\u65BC\u5176\u4ED6\u4F7F\u7528\u8005\uFF08\u901A\u5E38\u662F\u7528 sudo \u5168\u57DF\u5B89\u88DD\uFF09\uFF0CVibeFlow \u7121\u6CD5\u81EA\u884C\u4FEE\u6B63" : `spawn-helper \u6C92\u6709\u57F7\u884C\u6B0A\u9650\uFF1A${unrepaired[0].error}`,
5650
+ fix: spawnHelperFixCommand(unrepaired)
5646
5651
  };
5647
5652
  }
5648
5653
  try {
@@ -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/edcc6abb24dfd492.css" as="style"/><link rel="stylesheet" href="/_next/static/css/edcc6abb24dfd492.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-320afeed07d86516.js" defer=""></script><script src="/_next/static/chunks/pages/_error-8f65b0f8749f42d2.js" defer=""></script><script src="/_next/static/-9MMnQDtlhk8JcEq6dTBS/_buildManifest.js" defer=""></script><script src="/_next/static/-9MMnQDtlhk8JcEq6dTBS/_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":"-9MMnQDtlhk8JcEq6dTBS","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/edcc6abb24dfd492.css" as="style"/><link rel="stylesheet" href="/_next/static/css/edcc6abb24dfd492.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-320afeed07d86516.js" defer=""></script><script src="/_next/static/chunks/pages/_error-8f65b0f8749f42d2.js" defer=""></script><script src="/_next/static/0iT_99y5BhZs3U_2l_mSN/_buildManifest.js" defer=""></script><script src="/_next/static/0iT_99y5BhZs3U_2l_mSN/_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":"0iT_99y5BhZs3U_2l_mSN","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/edcc6abb24dfd492.css" as="style"/><link rel="stylesheet" href="/_next/static/css/edcc6abb24dfd492.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-320afeed07d86516.js" defer=""></script><script src="/_next/static/chunks/pages/_error-8f65b0f8749f42d2.js" defer=""></script><script src="/_next/static/-9MMnQDtlhk8JcEq6dTBS/_buildManifest.js" defer=""></script><script src="/_next/static/-9MMnQDtlhk8JcEq6dTBS/_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":"-9MMnQDtlhk8JcEq6dTBS","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/edcc6abb24dfd492.css" as="style"/><link rel="stylesheet" href="/_next/static/css/edcc6abb24dfd492.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-320afeed07d86516.js" defer=""></script><script src="/_next/static/chunks/pages/_error-8f65b0f8749f42d2.js" defer=""></script><script src="/_next/static/0iT_99y5BhZs3U_2l_mSN/_buildManifest.js" defer=""></script><script src="/_next/static/0iT_99y5BhZs3U_2l_mSN/_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":"0iT_99y5BhZs3U_2l_mSN","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/edcc6abb24dfd492.css" as="style"/><link rel="stylesheet" href="/_next/static/css/edcc6abb24dfd492.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-320afeed07d86516.js" defer=""></script><script src="/_next/static/chunks/4270-6c9c75303137e3a5.js" defer=""></script><script src="/_next/static/chunks/pages/home-efefddd4b88b018d.js" defer=""></script><script src="/_next/static/-9MMnQDtlhk8JcEq6dTBS/_buildManifest.js" defer=""></script><script src="/_next/static/-9MMnQDtlhk8JcEq6dTBS/_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":"-9MMnQDtlhk8JcEq6dTBS","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/edcc6abb24dfd492.css" as="style"/><link rel="stylesheet" href="/_next/static/css/edcc6abb24dfd492.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-320afeed07d86516.js" defer=""></script><script src="/_next/static/chunks/4270-6c9c75303137e3a5.js" defer=""></script><script src="/_next/static/chunks/pages/home-efefddd4b88b018d.js" defer=""></script><script src="/_next/static/0iT_99y5BhZs3U_2l_mSN/_buildManifest.js" defer=""></script><script src="/_next/static/0iT_99y5BhZs3U_2l_mSN/_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":"0iT_99y5BhZs3U_2l_mSN","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.0.1",
3
+ "version": "4.0.3",
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",
@@ -8,6 +8,9 @@
8
8
  "bin": {
9
9
  "vibeflow": "dist/vibeflow.mjs"
10
10
  },
11
+ "scripts": {
12
+ "postinstall": "node dist/postinstall.mjs"
13
+ },
11
14
  "files": [
12
15
  "dist",
13
16
  "README.md",