@vibeflow-tools/cli 0.10.0 → 0.12.0
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/CHANGELOG.md +121 -23
- package/dist/chunk-22ZK3AD6.js +6 -0
- package/dist/chunk-2CI62NHU.js +8 -0
- package/dist/chunk-2FTOVVDE.js +10 -0
- package/dist/chunk-2HKRXAUP.js +10 -0
- package/dist/chunk-2L7GDWKA.js +8 -0
- package/dist/chunk-32Z32W7T.js +2 -0
- package/dist/chunk-3V7HLRIS.js +6 -0
- package/dist/chunk-4KWRHSXU.js +10 -0
- package/dist/chunk-ACDVAO4P.js +6 -0
- package/dist/chunk-BBIA4IIO.js +2 -0
- package/dist/chunk-BOPGIKZN.js +8 -0
- package/dist/chunk-BUQI2W5H.js +2 -0
- package/dist/chunk-BZDBSBRV.js +2 -0
- package/dist/chunk-CKNX42Z3.js +10 -0
- package/dist/chunk-CQ524DVH.js +2 -0
- package/dist/chunk-D4FZTZHO.js +8 -0
- package/dist/chunk-EAYTSVCN.js +2 -0
- package/dist/chunk-EDJNSJXJ.js +10 -0
- package/dist/chunk-ES3JTCZ7.js +8 -0
- package/dist/chunk-EZX6HYC3.js +8 -0
- package/dist/chunk-FYRJDWDC.js +2 -0
- package/dist/chunk-GN436FFO.js +10 -0
- package/dist/chunk-GQQIUROY.js +2 -0
- package/dist/chunk-GTZDS3U3.js +6 -0
- package/dist/chunk-I3WFKPN7.js +8 -0
- package/dist/chunk-ICO6AZHJ.js +10 -0
- package/dist/chunk-IJ67OEKX.js +2 -0
- package/dist/chunk-ILUMCOF2.js +2 -0
- package/dist/chunk-JAWQQLJC.js +8 -0
- package/dist/chunk-JQTURVVN.js +2 -0
- package/dist/chunk-KZZ4ADHY.js +6 -0
- package/dist/chunk-L6EJ7QTO.js +6 -0
- package/dist/chunk-LYJHU4LP.js +10 -0
- package/dist/chunk-N5PXJMOU.js +6 -0
- package/dist/chunk-NHVKGOSV.js +10 -0
- package/dist/chunk-O4LGEPMQ.js +8 -0
- package/dist/chunk-P63ZVL36.js +2 -0
- package/dist/chunk-PFOVGF7M.js +10 -0
- package/dist/chunk-PGGJRQCO.js +2 -0
- package/dist/chunk-PN5C7EHL.js +8 -0
- package/dist/chunk-PWP474PZ.js +2 -0
- package/dist/chunk-PYMUW2HM.js +8 -0
- package/dist/chunk-Q7MEW67Z.js +8 -0
- package/dist/chunk-QJMBWTAZ.js +8 -0
- package/dist/chunk-R5NQALFI.js +2 -0
- package/dist/chunk-RBBUTBE3.js +6 -0
- package/dist/chunk-RRQ5P63N.js +2 -0
- package/dist/chunk-SQPWTIQO.js +2 -0
- package/dist/chunk-TCTUG3QV.js +8 -0
- package/dist/chunk-TTWEWSD3.js +6 -0
- package/dist/chunk-WARORREB.js +2 -0
- package/dist/chunk-WHXU37AT.js +10 -0
- package/dist/chunk-WKEMBLJR.js +2 -0
- package/dist/chunk-WVRMKUU5.js +6 -0
- package/dist/chunk-WZ2QQDYW.js +2 -0
- package/dist/chunk-X3VLCXWS.js +6 -0
- package/dist/chunk-X4CQRMD4.js +6 -0
- package/dist/chunk-X4RGF6JJ.js +2 -0
- package/dist/chunk-XHZXHACL.js +2 -0
- package/dist/chunk-XJ35INCK.js +10 -0
- package/dist/chunk-XYZECVJ4.js +8 -0
- package/dist/chunk-YMBPKWZ4.js +2 -0
- package/dist/chunk-ZHA4Y2KB.js +8 -0
- package/dist/cli/chunk-22ZK3AD6.js +6 -0
- package/dist/cli/chunk-2CI62NHU.js +8 -0
- package/dist/cli/chunk-2FTOVVDE.js +10 -0
- package/dist/cli/chunk-2HKRXAUP.js +10 -0
- package/dist/cli/chunk-2L7GDWKA.js +8 -0
- package/dist/cli/chunk-32Z32W7T.js +2 -0
- package/dist/cli/chunk-3V7HLRIS.js +6 -0
- package/dist/cli/chunk-4KWRHSXU.js +10 -0
- package/dist/cli/chunk-ACDVAO4P.js +6 -0
- package/dist/cli/chunk-BBIA4IIO.js +2 -0
- package/dist/cli/chunk-BOPGIKZN.js +8 -0
- package/dist/cli/chunk-BUQI2W5H.js +2 -0
- package/dist/cli/chunk-BZDBSBRV.js +2 -0
- package/dist/cli/chunk-CKNX42Z3.js +10 -0
- package/dist/cli/chunk-CQ524DVH.js +2 -0
- package/dist/cli/chunk-D4FZTZHO.js +8 -0
- package/dist/cli/chunk-EAYTSVCN.js +2 -0
- package/dist/cli/chunk-EDJNSJXJ.js +10 -0
- package/dist/cli/chunk-ES3JTCZ7.js +8 -0
- package/dist/cli/chunk-EZX6HYC3.js +8 -0
- package/dist/cli/chunk-FYRJDWDC.js +2 -0
- package/dist/cli/chunk-GN436FFO.js +10 -0
- package/dist/cli/chunk-GQQIUROY.js +2 -0
- package/dist/cli/chunk-GTZDS3U3.js +6 -0
- package/dist/cli/chunk-I3WFKPN7.js +8 -0
- package/dist/cli/chunk-ICO6AZHJ.js +10 -0
- package/dist/cli/chunk-IJ67OEKX.js +2 -0
- package/dist/cli/chunk-ILUMCOF2.js +2 -0
- package/dist/cli/chunk-JAWQQLJC.js +8 -0
- package/dist/cli/chunk-JQTURVVN.js +2 -0
- package/dist/cli/chunk-KZZ4ADHY.js +6 -0
- package/dist/cli/chunk-L6EJ7QTO.js +6 -0
- package/dist/cli/chunk-LYJHU4LP.js +10 -0
- package/dist/cli/chunk-N5PXJMOU.js +6 -0
- package/dist/cli/chunk-NHVKGOSV.js +10 -0
- package/dist/cli/chunk-O4LGEPMQ.js +8 -0
- package/dist/cli/chunk-P63ZVL36.js +2 -0
- package/dist/cli/chunk-PFOVGF7M.js +10 -0
- package/dist/cli/chunk-PGGJRQCO.js +2 -0
- package/dist/cli/chunk-PN5C7EHL.js +8 -0
- package/dist/cli/chunk-PWP474PZ.js +2 -0
- package/dist/cli/chunk-PYMUW2HM.js +8 -0
- package/dist/cli/chunk-Q7MEW67Z.js +8 -0
- package/dist/cli/chunk-QJMBWTAZ.js +8 -0
- package/dist/cli/chunk-R5NQALFI.js +2 -0
- package/dist/cli/chunk-RBBUTBE3.js +6 -0
- package/dist/cli/chunk-RRQ5P63N.js +2 -0
- package/dist/cli/chunk-SQPWTIQO.js +2 -0
- package/dist/cli/chunk-TCTUG3QV.js +8 -0
- package/dist/cli/chunk-TTWEWSD3.js +6 -0
- package/dist/cli/chunk-WARORREB.js +2 -0
- package/dist/cli/chunk-WHXU37AT.js +10 -0
- package/dist/cli/chunk-WKEMBLJR.js +2 -0
- package/dist/cli/chunk-WVRMKUU5.js +6 -0
- package/dist/cli/chunk-WZ2QQDYW.js +2 -0
- package/dist/cli/chunk-X3VLCXWS.js +6 -0
- package/dist/cli/chunk-X4CQRMD4.js +6 -0
- package/dist/cli/chunk-X4RGF6JJ.js +2 -0
- package/dist/cli/chunk-XHZXHACL.js +2 -0
- package/dist/cli/chunk-XJ35INCK.js +10 -0
- package/dist/cli/chunk-XYZECVJ4.js +8 -0
- package/dist/cli/chunk-YMBPKWZ4.js +2 -0
- package/dist/cli/chunk-ZHA4Y2KB.js +8 -0
- package/dist/cli/comments-5LRGOYLT.js +2 -0
- package/dist/cli/comments-D3SKM2F4.js +2 -0
- package/dist/cli/comments-GGE2X2EE.js +2 -0
- package/dist/cli/comments-GZ6HQMHL.js +2 -0
- package/dist/cli/comments-LHSMBKJ3.js +2 -0
- package/dist/cli/comments-M5HDNHXR.js +2 -0
- package/dist/cli/comments-MJP5KLEE.js +2 -0
- package/dist/cli/comments-TQHWMRB4.js +2 -0
- package/dist/cli/comments-VSWJW47E.js +2 -0
- package/dist/cli/files-3GGL6AHA.js +2 -0
- package/dist/cli/files-4M7AX2IR.js +2 -0
- package/dist/cli/files-4O3JKNKY.js +2 -0
- package/dist/cli/files-7GFBKISD.js +2 -0
- package/dist/cli/files-7H73XU4Z.js +2 -0
- package/dist/cli/files-BMMCSJZ3.js +2 -0
- package/dist/cli/files-CZEVX3KX.js +2 -0
- package/dist/cli/files-F2EISWAF.js +2 -0
- package/dist/cli/files-FL23FKMK.js +2 -0
- package/dist/cli/files-FW2FWYFX.js +2 -0
- package/dist/cli/files-GQPP5SSP.js +2 -0
- package/dist/cli/files-QO35EIJI.js +2 -0
- package/dist/cli/files-R6BRCQQX.js +2 -0
- package/dist/cli/files-RNEVXCMZ.js +2 -0
- package/dist/cli/files-TGBQRRBH.js +2 -0
- package/dist/cli/files-WH4W5IPG.js +2 -0
- package/dist/cli/files-XKVCQQMI.js +2 -0
- package/dist/cli/git-NH6PGXOM.js +2 -0
- package/dist/cli/index.js +67 -55
- package/dist/cli/push-25YMX4RZ.js +2 -0
- package/dist/cli/push-26JSGYHL.js +2 -0
- package/dist/cli/push-5NNA77B2.js +2 -0
- package/dist/cli/push-7M34G4OW.js +2 -0
- package/dist/cli/push-AARMGHSL.js +2 -0
- package/dist/cli/push-EVBLKP53.js +2 -0
- package/dist/cli/push-HG6EPV6P.js +2 -0
- package/dist/cli/push-RFYHMZBR.js +2 -0
- package/dist/cli/push-RQJQM7U7.js +2 -0
- package/dist/cli/push-S4G36KHW.js +2 -0
- package/dist/cli/push-S5XSREE5.js +2 -0
- package/dist/cli/push-SWN2KN2R.js +2 -0
- package/dist/cli/review-gate-QR2E4REN.js +2 -0
- package/dist/cli/settings-KKHPB344.js +2 -0
- package/dist/cli/settings-PYRZJJ4H.js +2 -0
- package/dist/cli/tasks-3HXDHCPS.js +2 -0
- package/dist/cli/tasks-5X5AAK2N.js +2 -0
- package/dist/cli/tasks-MUL5SYJQ.js +2 -0
- package/dist/cli/tasks-N3AEGAEB.js +2 -0
- package/dist/cli/tasks-NSLXFEXZ.js +2 -0
- package/dist/cli/tasks-OJTUAWAN.js +2 -0
- package/dist/cli/tasks-SV7ZVS7W.js +2 -0
- package/dist/cli/tasks-VCU3JVNJ.js +2 -0
- package/dist/cli/verify-44MRXSPT.js +2 -0
- package/dist/cli/verify-AXMQ27UT.js +2 -0
- package/dist/cli/verify-BJ62CQI3.js +2 -0
- package/dist/cli/verify-DEG2BZOB.js +2 -0
- package/dist/cli/verify-DGL7A4WD.js +2 -0
- package/dist/cli/verify-GP427VCE.js +2 -0
- package/dist/cli/verify-QUCASFYP.js +2 -0
- package/dist/cli/verify-RY7B6C2P.js +2 -0
- package/dist/cli/verify-TTAW3GG6.js +2 -0
- package/dist/cli/verify-VLVCMBBW.js +2 -0
- package/dist/cli/verify-XL7JI7LN.js +2 -0
- package/dist/cli/verify-ZOAIUNRD.js +2 -0
- package/dist/cli/workspace-AJAVENKA.js +2 -0
- package/dist/client/kanban-browser.js +17 -11
- package/dist/client/kanban.css +1 -1
- package/dist/client/overlay-browser.js +6 -6
- package/dist/files-3GGL6AHA.js +2 -0
- package/dist/files-4M7AX2IR.js +2 -0
- package/dist/files-4O3JKNKY.js +2 -0
- package/dist/files-7GFBKISD.js +2 -0
- package/dist/files-7H73XU4Z.js +2 -0
- package/dist/files-BMMCSJZ3.js +2 -0
- package/dist/files-CZEVX3KX.js +2 -0
- package/dist/files-F2EISWAF.js +2 -0
- package/dist/files-FL23FKMK.js +2 -0
- package/dist/files-FW2FWYFX.js +2 -0
- package/dist/files-GQPP5SSP.js +2 -0
- package/dist/files-QO35EIJI.js +2 -0
- package/dist/files-R6BRCQQX.js +2 -0
- package/dist/files-RNEVXCMZ.js +2 -0
- package/dist/files-TGBQRRBH.js +2 -0
- package/dist/files-WH4W5IPG.js +2 -0
- package/dist/files-XKVCQQMI.js +2 -0
- package/dist/index.js +67 -55
- package/dist/workspace-AJAVENKA.js +2 -0
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,106 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.12.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 148cd3c: Kanban: new Compact view mode (Board | Compact | List). Compact shows one-line rows across all lanes — no tags, no comment/file counts. Done lane fits cards to screen height with "+N more" indicator. Overlay: add-task dialog now has an Advanced section with tag chips and priority selector.
|
|
8
|
+
- c57b423: Verify gate: optional enforcement in Settings > Enforcement. When ON, agents must run `vibeflow verify` before setting status to review. Auto-skips for non-UI tasks. `--skip-verify` flag available as bypass.
|
|
9
|
+
|
|
10
|
+
### Patch Changes
|
|
11
|
+
|
|
12
|
+
- ef67d56: Comment edits now broadcast to live UI. File uploads reject reserved filenames (.env, .git\*, .linked.json). Task schema limits enforced (titles 500 chars, descriptions 10K, URLs 2K).
|
|
13
|
+
- 7f96921: File upload validation hardened: rejects filenames with leading dots, enforces max length (255 chars), and validates extensions against an allowlist. 24 new tests.
|
|
14
|
+
- 074e376: Concurrent task writes now safely serialized via cross-process lock. GET endpoints are side-effect-free. Unified priority sorting across CLI and API.
|
|
15
|
+
- 16112b3: Atomic task claim: `--next` now re-checks status inside a lock, preventing double-claims under concurrent access.
|
|
16
|
+
- 803ca05: Review gate shared between CLI and REST. PATCH route now filters unexpected fields. Comment and verify requirements enforced.
|
|
17
|
+
- cf9dc82: Push dry-run mode, orphan task cleanup, verify cancellation via AbortSignal, serve deduplication.
|
|
18
|
+
- 1c3385d: Schema validation now enforces max lengths on task titles, descriptions, URLs, and selectors. Oversized fields are rejected with a clear error instead of being silently stored.
|
|
19
|
+
- 405b3f8: Fix file count badge display and changelog button styling in the kanban UI.
|
|
20
|
+
|
|
21
|
+
## 0.11.0
|
|
22
|
+
|
|
23
|
+
### Minor Changes
|
|
24
|
+
|
|
25
|
+
- abbda12: feat(cli): startup banner always shows a localhost URL alongside the LAN IP
|
|
26
|
+
|
|
27
|
+
When the server is bound to 0.0.0.0, every user-facing URL in the serve and
|
|
28
|
+
kanban startup banners (Kanban board, Task API, /inject guide, overlay script
|
|
29
|
+
tag, File lines, "Kanban board ready") now prints an aligned localhost
|
|
30
|
+
continuation line. Fixes kanban "request timeouts" when the displayed LAN IP
|
|
31
|
+
is unreachable from the local browser. Output is unchanged when bound to a
|
|
32
|
+
single host.
|
|
33
|
+
|
|
34
|
+
- 76d664c: feat(verify): page-wide baseline capture and diff
|
|
35
|
+
|
|
36
|
+
> ⚠️ The `verify` command is experimental and may change without notice.
|
|
37
|
+
|
|
38
|
+
- Capture page-wide baseline at annotation time, store as baseline-page.json
|
|
39
|
+
- Remove baseline from task.json (too large), use file reference instead
|
|
40
|
+
- Enable page-wide diff at verify time (verify-page-diff.json)
|
|
41
|
+
- Add html_query tool for structural HTML changes
|
|
42
|
+
- Back-fill baseline values in verify-all-styles.json for accurate diffs
|
|
43
|
+
|
|
44
|
+
- c3c3fd7: Show the changelog after updates and add a `vibeflow changelog` command.
|
|
45
|
+
|
|
46
|
+
- The update notice in `vibeflow kanban` now prints the newest CHANGELOG section below it; suppress with `--no-changelog`.
|
|
47
|
+
- New `vibeflow changelog [--all]` command prints the latest (or full) changelog to the terminal.
|
|
48
|
+
- Kanban board shows a one-time "What's New" modal after a CLI update and a header **Changelog** button to browse the full changelog at any time.
|
|
49
|
+
|
|
50
|
+
- adac672: feat(verify): page-wide query system with progressive exploration
|
|
51
|
+
|
|
52
|
+
> ⚠️ The `verify` command is experimental and may change without notice.
|
|
53
|
+
|
|
54
|
+
Adds page-wide element capture and query tools for agents to explore verify evidence:
|
|
55
|
+
|
|
56
|
+
- `vibeflow verify style_diff <id>` — summary of all style changes across the page
|
|
57
|
+
- `vibeflow verify style_query <id> <prop>` — ALL elements where a property changed
|
|
58
|
+
- `vibeflow verify html_query <id> children|text|attributes` — structural changes
|
|
59
|
+
- `vibeflow verify element_info <id>` — element details
|
|
60
|
+
|
|
61
|
+
Captures `verify-all-styles.json` with element tree, styles, and structure for every element with classes or data-attributes.
|
|
62
|
+
|
|
63
|
+
### Patch Changes
|
|
64
|
+
|
|
65
|
+
- 5881018: Add verified property to tasks with green VERIFIED badge in kanban UI.
|
|
66
|
+
- 857d597: feat(verify): add agent evidence tools for targeted style/HTML queries
|
|
67
|
+
|
|
68
|
+
> ⚠️ The `verify` command is experimental and may change without notice.
|
|
69
|
+
|
|
70
|
+
Adds four new CLI tools that agents can call to explore verify evidence
|
|
71
|
+
without reading all files (~34K tokens → ~7K tokens):
|
|
72
|
+
|
|
73
|
+
- `vibeflow verify style_query <task-id> <property>` — query specific CSS property
|
|
74
|
+
- `vibeflow verify style_diff <task-id> [--filter <pattern>]` — get changed properties
|
|
75
|
+
- `vibeflow verify element_info <task-id>` — get element details
|
|
76
|
+
- `vibeflow verify html_diff <task-id>` — get HTML changes
|
|
77
|
+
|
|
78
|
+
Also adds tool hints in verify output so agents know what's available.
|
|
79
|
+
|
|
80
|
+
- d0eceb5: fix(kanban): add vertical scrollbar to lanes except Done
|
|
81
|
+
|
|
82
|
+
Lanes now have `overflow-y: auto` so tasks are scrollable when they exceed the viewport height. The Done lane keeps `overflow-y: hidden` since it already limits visible cards to 20.
|
|
83
|
+
|
|
84
|
+
- fix(kanban): What's New changelog modal scrollbar not working
|
|
85
|
+
|
|
86
|
+
The full changelog view (8000+ pixels of content) overflowed the modal without a scrollbar because the scrollable child wasn't constrained by the modal's maxHeight. Fixed by making modal-box a flex column container so children shrink within bounds.
|
|
87
|
+
|
|
88
|
+
- 9085faf: Add Playwright page HTML, screenshot, and element HTML to verify evidence artifacts.
|
|
89
|
+
|
|
90
|
+
> ⚠️ The `verify` command is experimental and may change without notice.
|
|
91
|
+
|
|
92
|
+
- fix(cli): security hardening — XSS, SSRF, open redirects, config crash protection
|
|
93
|
+
|
|
94
|
+
- Sanitize all user data in kanban-template.html with `esc()` and `safeHttpUrl()` helpers; replace `innerHTML=''` clears with `replaceChildren()`
|
|
95
|
+
- Add same-origin guard on kanban file preview to prevent open redirects
|
|
96
|
+
- Validate outbound URL scheme (http/https only) in SaaS client to prevent SSRF
|
|
97
|
+
- Wrap config JSON.parse in try/catch to prevent CLI crash on corrupted config
|
|
98
|
+
- Add SAFETY comments on verified-safe innerHTML usages (renderMarkdown, SVG namespaces)
|
|
99
|
+
|
|
100
|
+
- 3405e3b: Improve verify command output: show evidence file paths and agent instructions instead of generic "Verification passed". Add verify step to agent workflow instructions.
|
|
101
|
+
|
|
102
|
+
> ⚠️ The `verify` command is experimental and may change without notice.
|
|
103
|
+
|
|
3
104
|
## 0.10.0
|
|
4
105
|
|
|
5
106
|
### Minor Changes
|
|
@@ -208,29 +309,26 @@
|
|
|
208
309
|
|
|
209
310
|
### Patch Changes
|
|
210
311
|
|
|
211
|
-
-
|
|
212
|
-
-
|
|
213
|
-
-
|
|
214
|
-
-
|
|
215
|
-
-
|
|
216
|
-
-
|
|
217
|
-
-
|
|
218
|
-
-
|
|
219
|
-
-
|
|
220
|
-
-
|
|
221
|
-
-
|
|
222
|
-
-
|
|
223
|
-
-
|
|
224
|
-
-
|
|
225
|
-
-
|
|
226
|
-
-
|
|
227
|
-
-
|
|
228
|
-
-
|
|
229
|
-
-
|
|
230
|
-
-
|
|
231
|
-
- 9cd0d82: Validate workspace URL against an allowlist of production domains before using it as the SaaS API base URL, preventing SSRF (SEC-08).
|
|
232
|
-
- 9cd0d82: Serve screenshots through a route handler that validates task ID format instead of an unauthenticated static directory (SEC-05).
|
|
233
|
-
- 9cd0d82: Only trust X-Forwarded-For header for rate limiting when TRUSTED_PROXY=1 env var is set, preventing IP spoofing (SEC-01).
|
|
312
|
+
- Fix task file corruption when multiple processes write simultaneously.
|
|
313
|
+
- Fix potential HTML injection via malformed workspace ID.
|
|
314
|
+
- Fix soft-deleted comments being permanently lost when edited.
|
|
315
|
+
- Fix copilot auth failing silently on some systems.
|
|
316
|
+
- Fix logout not fully clearing workspace data.
|
|
317
|
+
- Reject invalid task status values instead of silently accepting.
|
|
318
|
+
- Fix push command reading tasks from wrong directory in some cases.
|
|
319
|
+
- Fix commit hash display showing wrong commits when multiple projects are open.
|
|
320
|
+
- Fix stale commit data persisting in task objects.
|
|
321
|
+
- Reject unknown settings keys to prevent accidental config corruption.
|
|
322
|
+
- Fix XSS vulnerability in markdown link rendering.
|
|
323
|
+
- Fix comment ID collisions when multiple users comment simultaneously.
|
|
324
|
+
- Fix CLI not running cleanup handlers on login errors.
|
|
325
|
+
- Faster task listing by computing counts from memory instead of disk.
|
|
326
|
+
- Fix copilot config file readable by other users on shared systems.
|
|
327
|
+
- Reject unsupported file types on upload.
|
|
328
|
+
- Block cross-origin mutation requests for security.
|
|
329
|
+
- Fix potential SSRF via malformed workspace URL.
|
|
330
|
+
- Fix screenshots accessible without authentication.
|
|
331
|
+
- Fix rate limiting bypass via spoofed X-Forwarded-For header.
|
|
234
332
|
|
|
235
333
|
## 0.4.1
|
|
236
334
|
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{a as w}from"./chunk-BZDBSBRV.js";import{a as B,d as U}from"./chunk-32Z32W7T.js";import{c as P,d as E,u as N}from"./chunk-ZHA4Y2KB.js";import{a as A,b as _}from"./chunk-KJ5LYG6A.js";import g from"chalk";import{unlinkSync as ye,readFileSync as ke,existsSync as we}from"fs";import{resolve as Se,join as $e}from"path";import I from"fs/promises";import oe from"os";import F from"path";var re=F.join(oe.homedir(),".vibeflow"),T=F.join(re,"token");async function b(){try{return(await I.readFile(T,"utf8")).trim()||null}catch{return null}}async function j(e){await I.mkdir(F.dirname(T),{recursive:!0}),await I.writeFile(T,e,{mode:384})}async function Ae(){try{await I.unlink(T)}catch{}}import i from"chalk";import ie from"open";import V from"readline";import ae from"@inquirer/select";import{existsSync as L,readFileSync as D,writeFileSync as W,mkdirSync as M,renameSync as z}from"fs";import{join as x}from"path";import G from"os";var ne="settings.json";function H(){return x(G.homedir(),".vibeflow","settings.json")}function J(e){return x(e,P,ne)}function O(){try{return JSON.parse(D(H(),"utf-8"))}catch{return{}}}function q(e){let r=x(G.homedir(),".vibeflow");L(r)||M(r,{recursive:!0});let o=H(),n=o+".tmp";W(n,JSON.stringify(e,null,2),"utf-8"),z(n,o)}function se(e){let r=O(),o=J(e);if(!L(o))return r;try{let n=JSON.parse(D(o,"utf-8"));return{...r,...n}}catch{return r}}function Ne(e,r){let o=x(e,P);L(o)||M(o,{recursive:!0});let n=J(e),c={...se(e),...r},p=n+".tmp";return W(p,JSON.stringify(c,null,2),"utf-8"),z(p,n),c}import{existsSync as ce,readdirSync as K,readFileSync as le}from"fs";import{join as R,resolve as de}from"path";var pe="https://app.vibeflow.tools",fe=300*1e3;function Y(){return process.env.VIBEFLOW_API_URL??pe}async function ue(e){return new Promise(r=>setTimeout(r,e))}async function ge(e){return e.length===1?(console.log(i.dim(` Using board: ${e[0].icon??""} ${e[0].name}`)),e[0]):await ae({message:"Select a board to use:",choices:e.map(o=>({name:`${o.icon??""} ${o.name}`,value:o}))})}async function Q(e,r){try{let o=await fetch(`${e}/api/cli/settings`,{headers:{Authorization:`Bearer ${r}`}});if(!o.ok)return;let n=await o.json();n.settings&&Object.keys(n.settings).length>0&&q({...n.settings,_settingsRefreshedAt:Date.now()})}catch{}}async function Ve(){try{let e=await b();if(!e)return;let o=O()._settingsRefreshedAt??0;if(Date.now()-o<fe)return;let n=Y();Q(n,e)}catch{}}async function me(e,r){try{let[o,n]=await Promise.all([fetch(`${e}/api/cli/workspaces`,{headers:{Authorization:`Bearer ${r}`}}),fetch(`${e}/api/cli/profile`,{headers:{Authorization:`Bearer ${r}`}})]);if(!o.ok)return;let a=await o.json();if(!a.workspaces?.length){console.log(i.yellow(" No boards found. Create a board in the web app first."));return}let c=n.ok?await n.json():null,p=await ge(a.workspaces);await _({...p,email:c?.email??null}),console.log(i.green(` Board set: ${p.icon??""} ${p.name}`)),console.log(i.dim(` Open at: ${p.url}`))}catch{}}async function he(e,r,o,n){let a=R(e,P,E);if(!ce(a))return;let c=[];try{for(let s of K(a,{withFileTypes:!0}))if(s.isDirectory()){let f=R(a,s.name);for(let m of K(f).filter(d=>d.endsWith(".json")))c.push(R(f,m))}else s.isFile()&&s.name.endsWith(".json")&&c.push(R(a,s.name))}catch{return}if(c.length===0)return;let p=c.flatMap(s=>{try{return[JSON.parse(le(s,"utf-8"))]}catch{return[]}}),y=new Set;try{let s=await fetch(`${r}/api/cli/tasks?boardId=${n.id}`,{headers:{Authorization:`Bearer ${o}`}}).catch(()=>null);if(s?.ok){let f=await s.json();y=new Set((f.tasks??[]).flatMap(m=>[m.id,...m.cliId?[m.cliId]:[]]))}}catch{}let l=p.filter(s=>!y.has(s.id));if(l.length===0)return;console.log(),console.log(i.bold(` ${l.length} local task${l.length!==1?"s are":" is"} not yet synced to the board:`));let h=l.slice(0,10);for(let s of h){let f=s.status?i.dim(` [${s.status}]`):"";console.log(i.dim(` \u2022 ${s.title??s.id}${f}`))}return l.length>10&&console.log(i.dim(` \u2026 and ${l.length-10} more`)),new Promise(s=>{let f=V.createInterface({input:process.stdin,output:process.stdout});f.question(i.cyan(`
|
|
3
|
+
Push them to your board now? [Y/n]: `),async m=>{if(f.close(),m.trim().toLowerCase()==="n"){console.log(i.dim(" Skipped. You can push later with: vibeflow push")),s();return}let d=V.createInterface({input:process.stdin,output:process.stdout});d.question(i.cyan(" Delete local task files after push? [y/N]: "),async k=>{d.close();let S=k.trim().toLowerCase()!=="y";try{let t=await fetch(`${r}/api/cli/import`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${o}`},body:JSON.stringify({boardId:n.id,tasks:l,keepLocalFiles:S})}),u={};try{u=await t.json()}catch{}t.ok?(console.log(i.green(` \u2713 Pushed ${u.imported??0} task${(u.imported??0)!==1?"s":""} to ${n.name}`)),(u.skipped??0)>0&&console.log(i.dim(` (${u.skipped} already existed and were skipped)`))):console.log(i.red(` Push failed: ${u.error??`HTTP ${t.status}`}`))}catch(t){console.log(i.red(` Push failed: ${t instanceof Error?t.message:"Network error"}`))}s()})})})}async function X(e=de(".")){let r=Y();console.log(i.cyan(" Initiating device login flow..."));let o;try{o=await fetch(`${r}/api/cli/auth/device-init`,{method:"POST",headers:{"Content-Type":"application/json"}})}catch(h){console.error(i.red(` Failed to connect to login server: ${h instanceof Error?h.message:"Network error"}`)),process.exitCode=w.AUTH;return}if(!o.ok){console.error(i.red(` Failed to initiate login: ${o.status} ${o.statusText}`)),process.exitCode=w.AUTH;return}let{deviceCode:n,userCode:a,verificationUrl:c,expiresIn:p}=await o.json();console.log(),console.log(i.bold(" Open this URL in your browser to authorize:")),console.log(i.underline(` ${c}`)),console.log();try{await ie(c),console.log(i.dim(" (Browser opened automatically)"))}catch{}let y=Date.now()+p*1e3,l=3e3;for(;Date.now()<y;){await ue(l);let h=null;try{h=await fetch(`${r}/api/cli/auth/device-poll`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({deviceCode:n})})}catch{console.error(i.red(" Poll request failed (network error). Retrying..."));continue}if(!h.ok){console.error(i.red(" Poll request failed. Retrying..."));continue}let s=await h.json();if("token"in s){await j(s.token),console.log(),console.log(i.green(" Logged in successfully. Now operating in SaaS mode.")),await Promise.all([me(r,s.token),Q(r,s.token)]);let{readWorkspace:f}=await import("./workspace-AJAVENKA.js"),m=await f();m&&await he(e,r,s.token,m);return}if("expired"in s){console.error(i.red(" Device code expired. Please try again.")),process.exitCode=w.AUTH;return}process.stdout.write(i.dim("."))}console.error(i.red(`
|
|
4
|
+
Login timed out. Please try again.`)),process.exitCode=w.AUTH}var Pe="https://app.vibeflow.tools";function be(){return process.env.VIBEFLOW_API_URL??Pe}function Ie(e){return e<1024?`${e} B`:e<1024*1024?`${(e/1024).toFixed(1)} KB`:`${(e/(1024*1024)).toFixed(1)} MB`}function Z(e,r,o=24){let n=Math.round(e/r*o);return`[${"\u2588".repeat(n)+"\u2591".repeat(o-n)}]`}async function Te(e,r,o,n){let a=[];for(let{localId:d,remoteId:k}of n){let S=U(o,d);for(let t of S){let u=t.linkedPath??$e(B(o,d),t.name);we(u)&&a.push({remoteId:k,name:t.name,filePath:u,size:t.size})}}if(a.length===0)return{uploaded:0,failed:0,failedFiles:[]};let c=a.length,p=a.reduce((d,k)=>d+k.size,0);console.log(g.cyan(`
|
|
5
|
+
Uploading ${c} file(s) (${Ie(p)})...`));let y=0,l=0,h=0,s=Date.now(),f=[];for(let d of a){let k=Math.round(y/c*100),S=Z(y,c),t=(Date.now()-s)/1e3,u=h/Math.max(t,.01),ee=p-h,v=u>0?Math.ceil(ee/u):0,te=v>60?`${Math.ceil(v/60)}m`:`${v}s`;process.stdout.write(`\r ${S} ${k}% ${d.name.slice(0,28)} ETA: ${te} `);try{let $=ke(d.filePath),C=await fetch(`${e}/api/tasks/${encodeURIComponent(d.remoteId)}/files`,{method:"POST",headers:{Authorization:`Bearer ${r}`,"Content-Type":"application/octet-stream","x-filename":encodeURIComponent(d.name)},body:$});C.ok?(y++,h+=d.size):(l++,f.push({name:d.name,reason:`HTTP ${C.status}`}))}catch($){l++,f.push({name:d.name,reason:$ instanceof Error?$.message:String($)})}}let m=Z(c,c);return process.stdout.write(`\r ${m} 100% Done${" ".repeat(40)}
|
|
6
|
+
`),{uploaded:y,failed:l,failedFiles:f}}async function it(e,r){let o=Se(e),n=await b();if(!n&&(console.log(g.yellow(" Not logged in. Starting login flow...")),await X(),n=await b(),!n)){console.error(g.red(" Login failed. Please run `vibeflow login` and try again.")),process.exitCode=w.AUTH;return}let a=N(o);if(a.length===0){console.log(g.dim(" No local tasks found. Nothing to push."));return}console.log(g.cyan(` Found ${a.length} local task(s). Pushing to SaaS...`));let c=be(),p=r.workspace;if(!p){let t=await A();t&&(p=t.id,console.log(g.dim(` Using board: ${t.icon??""} ${t.name}`)))}let y={tasks:a};p&&(y.workspaceId=p);let l;try{let t=await fetch(`${c}/api/cli/import`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${n}`},body:JSON.stringify(y)});if(!t.ok){let u=await t.json().catch(()=>({}));console.error(g.red(` Import failed: ${t.status} ${u.error??t.statusText}`)),process.exitCode=w.GENERAL;return}l=await t.json()}catch(t){let u=t instanceof Error?t.message:String(t);console.error(g.red(` Network error: ${u}`)),process.exitCode=w.GENERAL;return}console.log(g.green(` \u2713 Pushed ${l.imported} task(s) to SaaS`)),l.skipped&&l.skipped>0&&console.log(g.dim(` Skipped ${l.skipped} already-imported task(s).`));let h=(await A())?.name??l.workspaceId;console.log(g.dim(` Board: ${h}`));let s=l.idMap??{},f=a.filter(t=>t.id&&s[t.id]).map(t=>({localId:t.id,remoteId:s[t.id]})),{uploaded:m,failed:d,failedFiles:k}=await Te(c,n,o,f);if(m>0&&console.log(g.green(` \u2713 Uploaded ${m} file(s) to storage`)),d>0){console.log(g.yellow(` \u26A0 ${d} file(s) failed to upload`));for(let t of k)console.log(g.dim(` - ${t.name}: ${t.reason}`))}if(r.keepLocalFiles){console.log(g.dim(" Local task files kept (--keep-local-files)."));return}let S=0;for(let t of a)try{ye(t.filePath),S++}catch{}console.log(g.dim(` Deleted ${S} local task file(s).`))}export{b as a,Ae as b,se as c,Ne as d,Ve as e,X as f,Ie as g,Z as h,it as i};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{a as B}from"./chunk-X4RGF6JJ.js";import{existsSync as l,readFileSync as I,writeFileSync as F,mkdirSync as d,readdirSync as f,unlinkSync as h,renameSync as k,openSync as H,statSync as J}from"fs";import{join as u,extname as S}from"path";import{randomBytes as V}from"crypto";var b=["backlog","todo","in-progress","review","done"];function v(e){let i=(e??"Medium").trim().toLowerCase();return i==="critical"?0:i==="high"?1:i==="low"?3:2}function Y(e,i){let n=v(e.priority),s=v(i.priority);return n!==s?n-s:new Date(e.created).getTime()-new Date(i.created).getTime()}var m=".vibeflow",w="tasks",x="tasks/files",y="tasks/screenshots",Q="config.json";import{openSync as j,statSync as _,unlinkSync as D,mkdirSync as E,existsSync as M}from"fs";import{join as A}from"path";var $=new Map;function R(e,i){let n=A(e,m,"tasks",".locks");try{M(n)||E(n,{recursive:!0})}catch{}return A(n,`${i}.lock`)}function K(e,i,n){let s=Date.now()+i,{dirname:r}=B("path");for(;;)try{j(e,"wx");return}catch(t){if(t.code==="ENOENT"){try{E(r(e),{recursive:!0})}catch{}continue}if(t.code!=="EEXIST")throw t;try{let c=_(e);if(Date.now()-c.mtimeMs>n){try{D(e)}catch{}continue}}catch{continue}if(Date.now()>=s)throw new Error(`Failed to acquire lock ${e} within ${i}ms`);if(s-Date.now()<=0)throw new Error(`Failed to acquire lock ${e} within ${i}ms`);let a=Date.now()+Math.min(10,s-Date.now());for(;Date.now()<a;);}}function q(e){try{D(e)}catch{}}async function ie(e,i,n){let s=n?.timeoutMs??5e3,r=n?.staleMs??1e4,o=($.get(e)??Promise.resolve()).then(async()=>{K(e,s,r);try{return await i()}finally{q(e)}});return $.set(e,o.catch(()=>{})),o}function W(){return V(15).toString("hex")}function p(e){return u(e,m,w)}function z(e){let i=u(e,m),n=u(i,"files"),s=u(i,x),r=u(i,"screenshots"),t=u(i,y);if(l(n)){d(s,{recursive:!0});for(let o of f(n)){let a=u(n,o),c=u(s,o);if(!l(c))try{k(a,c)}catch{}}}if(l(r)){d(t,{recursive:!0});for(let o of f(r)){let a=u(r,o),c=u(t,o);if(!l(c))try{k(a,c)}catch{}}}}function le(e){z(e),d(p(e),{recursive:!0}),d(u(e,m,y),{recursive:!0})}function T(e){return e.slice(0,10)}function U(e,i,n){let s=p(e);return n?u(s,T(n),`${i}.json`):u(s,`${i}.json`)}function O(e,i){let n=p(e);if(!l(n))return null;for(let s of f(n,{withFileTypes:!0}))if(s.isDirectory()){let r=u(n,s.name,`${i}.json`);if(l(r))return r}else if(s.name===`${i}.json`)return u(n,s.name);return null}function G(e){return{...e,text:e.text??e.content??""}}function X(e){let i=(()=>{if(typeof e.type!="string")return;let n=e.type.trim();if(!(!n||n==="[object Object]"))return n})();return{id:String(e.id??""),title:String(e.title??"Untitled"),description:String(e.description??""),status:b.includes(e.status)?e.status:"todo",url:e.url?String(e.url):void 0,selector:(()=>{let n=String(e.selector??"/");return e.file&&!e.cssSelector&&n.startsWith(String(e.file))?e.url?String(e.url):"/":n})(),cssSelector:e.cssSelector&&String(e.cssSelector)!==String(e.selector??"/")?String(e.cssSelector):void 0,file:e.file?String(e.file):void 0,line:e.line==null?void 0:Number(e.line),col:e.col==null?void 0:Number(e.col),component:e.component?String(e.component):void 0,type:i,priority:e.priority?String(e.priority):void 0,...e.reportBack===!0&&{reportBack:!0},agent:e.agent?String(e.agent):void 0,model:e.model?String(e.model):void 0,author:e.author?String(e.author):void 0,commits:Array.isArray(e.commits)?e.commits.map(n=>({sha:String(n.sha??""),message:String(n.message??""),timestamp:String(n.timestamp??new Date().toISOString())})):void 0,created:String(e.created??new Date().toISOString()),updated:e.updated?String(e.updated):void 0,comments:Array.isArray(e.comments)?e.comments.map(n=>G(n)):[],files:Array.isArray(e.files)?e.files.map(n=>typeof n=="string"?{name:n,addedAt:new Date().toISOString()}:{name:String(n.name??""),addedAt:String(n.addedAt??new Date().toISOString()),linkedPath:n.linkedPath?String(n.linkedPath):void 0,mimeType:n.mimeType?String(n.mimeType):void 0}).filter(n=>n.name):[],screenshot:e.screenshot?String(e.screenshot):void 0,annotatedElementText:e.annotatedElementText?String(e.annotatedElementText):void 0,tags:Array.isArray(e.tags)?e.tags.filter(n=>typeof n=="string"&&n.length>0):void 0,sortKey:e.sortKey?String(e.sortKey):void 0,verified:e.verified===!0,branchName:e.branchName?String(e.branchName):void 0,baseline:e.baseline&&typeof e.baseline=="object"?e.baseline:void 0,authStateEnc:e.authStateEnc?String(e.authStateEnc):void 0}}function N(e,i){let n=u(p(e),T(i.created));d(n,{recursive:!0});let s=u(n,`${i.id}.json`),r=s+".tmp";F(r,JSON.stringify(i,null,2),"utf-8"),k(r,s)}function C(e){return e.replace(/\\(n|t|r|\\)/g,(i,n)=>{switch(n){case"n":return`
|
|
3
|
+
`;case"t":return" ";case"r":return"\r";case"\\":return"\\";default:return n}})}function me(e,i){let n=(()=>{let r=(i.priority??"").trim().toLowerCase();return r==="critical"?"Critical":r==="high"?"High":r==="low"?"Low":"Medium"})(),s={...i,title:C(i.title??"").trim(),description:C(i.description??"").trim(),priority:n,id:W(),created:new Date().toISOString(),comments:[],files:[]};return N(e,s),s}function g(e){try{let i=I(e,"utf-8");if(e.endsWith(".json")){let n=JSON.parse(i);return!n||typeof n!="object"||!("id"in n)?null:X(n)}return null}catch{return null}}function P(e){let i=p(e);if(!l(i))return[];let n=[];for(let s of f(i,{withFileTypes:!0}))if(s.isDirectory()){let r=u(i,s.name);for(let t of f(r))if(S(t)===".json"){let o=u(r,t),a=g(o);a&&n.push({task:a,filePath:o})}}else if(S(s.name)===".json"){let r=u(i,s.name),t=g(r);t&&n.push({task:t,filePath:r})}return n}function de(e){return P(e).map(({task:i})=>i)}function fe(e){return P(e).map(({task:i,filePath:n})=>({...i,filePath:n}))}function pe(e,i,n){let s=R(e,i),r=null,t=Date.now()+5e3;for(;Date.now()<t;)try{H(s,"wx");break}catch(o){if(o.code!=="EEXIST")throw o;try{let c=J(s);if(Date.now()-c.mtimeMs>1e4){try{h(s)}catch{}continue}}catch{continue}let a=Date.now();for(;Date.now()-a<10&&Date.now()<t;);}try{let o=O(e,i),a=o?g(o):null;if(!a)return null;let c={...a,...n,updated:new Date().toISOString()};if(N(e,c),o&&o!==U(e,i,c.created))try{h(o)}catch{}r=c}finally{try{h(s)}catch{}}return r}function he(e,i){let n=O(e,i);return n?(h(n),!0):!1}function ge(e,i,n){return{id:e.id,status:e.status,title:e.title,description:e.description,...e.url&&{url:e.url},selector:e.selector,...e.file&&{file:e.file},...e.line!=null&&{line:e.line},...e.col!=null&&{col:e.col},...e.component&&{component:e.component},...e.type&&{type:e.type},...e.priority&&{priority:e.priority},...i&&i.length>0&&{structuredComments:i},...n&&n.length>0&&{linkedFiles:n},...e.reportBack&&{reportBack:!0},created:e.created}}function ye(e,i,n,s,r){let t=[];if(t.push(`[${e.status}] ${e.title}`),t.push(` id: ${e.id}`),t.push(` file: ${i}`),e.file&&t.push(` source: ${e.file}${e.line==null?"":`:${e.line}`}${e.col==null?"":`:${e.col}`}`),e.component&&t.push(` component: ${e.component}`),t.push(` selector: ${e.selector??"/"}`),e.cssSelector&&t.push(` css: ${e.cssSelector}`),e.url&&t.push(` url: ${e.url}`),e.commits&&e.commits.length>0)if(e.commits.length===1)t.push(` commit: ${e.commits[0].sha}`);else{t.push(` commits (${e.commits.length}):`);for(let o of e.commits)t.push(` ${o.sha.slice(0,8)} ${o.timestamp} ${o.message.slice(0,60)}`)}if(t.push(` created: ${e.created}`),e.type&&t.push(` type: ${e.type}`),e.priority&&t.push(` priority: ${e.priority}`),e.author&&t.push(` author: ${e.author}`),e.description){t.push(" description:");for(let o of e.description.split(`
|
|
4
|
+
`))t.push(` ${o}`)}if(e.annotatedElementText&&t.push(` element text: ${e.annotatedElementText}`),n.length>0){t.push(` comments (${n.length}):`);for(let o of n){let a=o.updatedAt?` (edited ${o.updatedAt})`:"";t.push(` [${o.author??"user"}] ${o.createdAt}${a}`);for(let c of o.text.split(`
|
|
5
|
+
`))t.push(` ${c}`)}}if(s.length>0){t.push(` linked files (${s.length}):`);for(let o of s){t.push(` - ${o.name} ${o.url}`);let a=o.linkedPath??u(r,".vibeflow","files",e.id,o.name);if(/\.(md|txt)$/i.test(o.name)&&o.size<1e5&&l(a))try{let c=I(a,"utf-8");t.push(" \u250C\u2500\u2500 content \u2500\u2500");for(let L of c.split(`
|
|
6
|
+
`))t.push(` \u2502 ${L}`);t.push(" \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")}catch{}}}return t.join(`
|
|
7
|
+
`)}function ke(e){let{autoCommit:i,autoPush:n,autoComment:s,createBranch:r}=e,t=[];if(t.push("Agent instructions (concise):"),t.push(" Discover: vibeflow tasks --status todo | vibeflow tasks --type Research | vibeflow tasks --user <email> | vibeflow tasks --tag <tag>"),t.push(" Auto-claim: vibeflow tasks --next (picks highest-priority todo task and sets it in-progress automatically)"),t.push(" Details: vibeflow tasks --get <id> (full task info with comments and files)"),t.push(' Create: vibeflow tasks --add --title "..." --description "..."'),t.push(""),t.push(" Workflow:"),r?(t.push(" \u26A0 Create a branch FIRST, before any implementation:"),t.push(" 1. git checkout -b <short-name> (e.g. fix/annotation-errors, feat/eye-toggle, chore/cleanup-extension)"),t.push(" Branch name rules: lowercase, kebab-case, 2-5 words, prefix fix/feat/chore/docs."),t.push(" Describe the WORK done (not dates). Bad: agent/2026-04-16. Good: fix/bug-errors-visibility."),t.push(" 2. vibeflow tasks --edit <id> --set-status in-progress \u2190 CLAIM TASK"),t.push(" 3. <implement the change>")):(t.push(" \u26A0 IMMEDIATELY set in-progress BEFORE any implementation work:"),t.push(" 1. vibeflow tasks --edit <id> --set-status in-progress \u2190 DO THIS FIRST"),t.push(" (if you used --next: task is already in-progress \u2014 skip to step 2)"),t.push(" 2. <implement the change>")),e.requireVerifyBeforeReview)if(i){t.push(r?" 4. git add <files> (stage your changes first)":" 3. git add <files> (stage your changes first)"),t.push(` ${r?"5":"4"}. vibeflow verify <id> (verify the fix \u2014 check the evidences it produces)`),t.push(" If verification fails, set task back to in-progress and fix.");let a=["--set-status review"];i&&a.push('--commit-message "<one-line summary>"'),s&&a.push('--comment "<report>"'),t.push(` ${r?"6":"5"}. vibeflow tasks --edit <id> ${a.join(" ")}`),t.push(" CLI will commit staged changes and link the commit SHA automatically.")}else{t.push(r?' 4. git add <files> && vibeflow tasks --commit --task <id> --message "<one-line summary>"':' 3. git add <files> && vibeflow tasks --commit --task <id> --message "<one-line summary>"'),t.push(` ${r?"5":"4"}. vibeflow verify <id> (verify the fix \u2014 check the evidences it produces)`),t.push(" If verification fails, set task back to in-progress and fix.");let a=["--set-status review"];s&&a.push('--comment "<report>"'),t.push(` ${r?"6":"5"}. vibeflow tasks --edit <id> ${a.join(" ")}`)}else{if(i){t.push(r?" 4. git add <files> (stage your changes first)":" 3. git add <files> (stage your changes first)");let c=["--set-status review"];i&&c.push('--commit-message "<one-line summary>"'),s&&c.push('--comment "<report>"'),t.push(` ${r?"5":"4"}. vibeflow tasks --edit <id> ${c.join(" ")}`),t.push(" CLI will commit staged changes and link the commit SHA automatically.")}else{t.push(r?' 4. git add <files> && vibeflow tasks --commit --task <id> --message "<one-line summary>"':' 3. git add <files> && vibeflow tasks --commit --task <id> --message "<one-line summary>"');let c=["--set-status review"];s&&c.push('--comment "<report>"'),t.push(` ${r?"5":"4"}. vibeflow tasks --edit <id> ${c.join(" ")}`)}let a=r?"6":"5";t.push(` ${a}. vibeflow verify <id> (verify the fix \u2014 check the evidences it produces)`),t.push(" If verification fails, set task back to in-progress and fix.")}return s&&(t.push(""),t.push(" Comment format (--comment):"),t.push(" \xB7 Plain text for concise one-liners."),t.push(" \xB7 Markdown for multi-section reports. Use **bold**, bullet lists, code fences."),t.push(" \xB7 Must cover: what changed, why, key decisions, anything future agents should know."),t.push(" \xB7 For long reports, attach a .md file and reference it in the comment.")),t.push(""),i&&t.push(" [setting] Auto-commit ON: provide --commit-message when setting status to review; CLI commits."),n&&t.push(" [setting] Auto-push ON: CLI pushes after the commit automatically."),s&&t.push(" [setting] Auto-comment ON: --comment is required when setting status to review."),r&&t.push(" [setting] Create branch ON: all work goes on a dedicated branch created before implementation."),e.requireVerifyBeforeReview&&t.push(" [setting] Verify gate ON: run vibeflow verify before setting status to review (skipped for tasks without URL/selector)."),e.hasResearchTasks&&(t.push(" Research tasks: NEVER generate code \u2014 research only."),t.push(" Attach a .md report before marking Research tasks as review."),t.push(" CLI ENFORCES: cannot mark Research as review without an attached .md report."),t.push(" Create the report file locally first, then upload when marking as review:"),t.push(' vibeflow tasks --edit <id> --set-status review --report-file ./report.md --comment "..."'),t.push(" The file is saved next to the task and deleted from the original path automatically."),t.push(" Report must include: findings, options considered (with pros/cons), recommendation, sources.")),e.hasBugTasks&&(t.push(" Bug tasks: Reproduce the bug first, then include in the comment:"),t.push(" \xB7 Symptom: what the user sees"),t.push(" \xB7 Trigger: exact steps to reproduce"),t.push(" \xB7 Root cause: the specific code/logic that caused it"),t.push(" \xB7 Fix: what was changed and why it works"),t.push(" \xB7 Evidence: paste the relevant error message or stack trace")),t.push(" BLOCKED? If a task is unclear or missing context:"),t.push(' vibeflow tasks --edit <id> --comment "Blocked: <reason>. Need: <what is needed>."'),t.push(" Then pick the next task: vibeflow tasks --next"),t.push(" Do not guess at unclear requirements \u2014 leave a comment and move on."),t.push(" CRITICAL: NEVER edit .vibeflow/ task files directly."),t.push(" All task operations (status, comments, commits) must go through CLI commands."),t.push(" CRITICAL: Set in-progress BEFORE reading/planning. Other agents may pick the same task."),t.push(" The in-progress transition signals ownership. Skip it and another agent may duplicate your work."),t.push(" Sequence: discover tasks \u2192 pick one \u2192 set in-progress \u2192 THEN read details and implement."),t.push(' CRITICAL: NEVER set a task status to "done".'),t.push(' When your implementation is complete, set the status to "review" \u2014 not "done".'),t.push(" Only humans can mark tasks as done after reviewing the work."),t.push(" The CLI will warn you and still allow the change \u2014 but agents must not use it."),t.push(""),t.join(`
|
|
8
|
+
`)}function Se(e){let i=p(e);if(!l(i))return 0;let n=0;for(let s of f(i,{withFileTypes:!0})){if(s.isDirectory()||S(s.name)!==".json")continue;let r=u(i,s.name),t=g(r);if(!t)continue;let o=u(i,T(t.created)),a=u(o,s.name);r!==a&&(d(o,{recursive:!0}),F(a,JSON.stringify(t,null,2),"utf-8"),h(r),n++)}return n}export{b as a,v as b,Y as c,m as d,w as e,x as f,y as g,Q as h,R as i,ie as j,W as k,p as l,le as m,U as n,O as o,N as p,C as q,me as r,g as s,de as t,fe as u,pe as v,he as w,ge as x,ye as y,ke as z,Se as A};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{c as st}from"./chunk-SQPWTIQO.js";import{a as U}from"./chunk-BZDBSBRV.js";import{a as b,e as R,g as I}from"./chunk-GQQIUROY.js";import{c as tt,g as et,n as ot,r as nt,u as rt}from"./chunk-QJMBWTAZ.js";import c from"chalk";import{resolve as wt,join as N}from"path";import{readFileSync as z}from"fs";import{statSync as Nt,readdirSync as Tt,unlinkSync as At}from"fs";import M from"crypto";var vt="vibeflow-auth-v1",xt=1440*60*1e3;function it(t){return M.scryptSync(t,vt,32)}function jt(t,e){let o=it(e),n=M.randomBytes(16),r=M.createCipheriv("aes-256-gcm",o,n),s=JSON.stringify(t),i=Buffer.concat([r.update(s),r.final()]),y=r.getAuthTag();return{version:1,createdAt:new Date().toISOString(),expiresAt:new Date(Date.now()+xt).toISOString(),iv:n.toString("hex"),tag:y.toString("hex"),data:i.toString("hex")}}function at(t,e){if(new Date(t.expiresAt)<new Date)return null;try{let o=it(e),n=M.createDecipheriv("aes-256-gcm",o,Buffer.from(t.iv,"hex"));n.setAuthTag(Buffer.from(t.tag,"hex"));let r=Buffer.concat([n.update(Buffer.from(t.data,"hex")),n.final()]);return JSON.parse(r.toString())}catch{return null}}function bt(t,e){return Math.abs(t.x-e.x)>2||Math.abs(t.y-e.y)>2||Math.abs(t.width-e.width)>2||Math.abs(t.height-e.height)>2}function H(t,e){let o=!!e.outerHTML,n=t.outerHTML!==e.outerHTML,r={},s=new Set([...Object.keys(t.computedStyles),...Object.keys(e.computedStyles)]);for(let l of s){let E=t.computedStyles[l],x=e.computedStyles[l];E!==x&&(r[l]=[E??"",x??""])}let i=bt(t.position.boundingBox,e.position.boundingBox),y=new Set(t.consoleErrors),g=e.consoleErrors.filter(l=>!y.has(l));return{selectorResolves:o,htmlChanged:n,stylesChanged:r,positionChanged:i,newConsoleErrors:g}}function ct(t,e){let o=[];if(t.selectorResolves){t.htmlChanged&&o.push("HTML changed");let n=Object.keys(t.stylesChanged).length;if(n>0){let r=Object.keys(t.stylesChanged).slice(0,5).join(", "),s=n>5?` (+${n-5} more)`:"";o.push(`${n} style property change(s): ${r}${s}`)}t.positionChanged&&o.push("position shifted"),t.newConsoleErrors.length>0&&o.push(`${t.newConsoleErrors.length} new console error(s)`),o.length===0&&o.push("no structural changes detected")}else o.push(`Selector "${e}" no longer resolves`);return o.join("; ")}import{readFileSync as lt,writeFileSync as Ht,mkdirSync as Jt,existsSync as ft}from"fs";import{join as gt,basename as Rt}from"path";import{execSync as Ct}from"child_process";var G={port:3700};function Ot(t){return gt(t,tt,et)}function ut(t){let e=Ot(t);if(!ft(e))return{...G};let o=lt(e,"utf-8"),n;try{n=JSON.parse(o)}catch{return{...G}}return{...G,...n}}function Wt(t){let e=gt(t,"package.json");if(ft(e))try{let o=JSON.parse(lt(e,"utf-8"));if(o.name)return o.name}catch{}return Rt(t)}function zt(t){try{return Ct("git branch --show-current",{cwd:t,encoding:"utf-8",stdio:["pipe","pipe","pipe"]}).trim()||null}catch{return null}}var W=["display","visibility","opacity","position","overflow","overflow-x","overflow-y","width","min-width","max-width","height","min-height","max-height","margin","margin-top","margin-right","margin-bottom","margin-left","padding","padding-top","padding-right","padding-bottom","padding-left","border","border-width","border-style","border-color","border-radius","background-color","background-image","color","font-size","font-weight","font-style","font-family","line-height","letter-spacing","text-align","text-decoration","text-overflow","white-space","flex-direction","flex-wrap","flex","justify-content","align-items","gap","row-gap","column-gap","grid-template-columns","grid-template-rows","z-index","box-shadow","transform","transition","cursor","user-select","pointer-events","top","left","right","bottom"];function dt(t){let e=[],o=t;for(;o&&o!==document.documentElement;){let n=o.parentElement;if(!n)break;let r=Array.from(n.children).indexOf(o);e.unshift(r),o=n}return e.join("/")}function ht(t){let e=t.tagName.toLowerCase(),o=t.getAttribute("data-task-id");if(o)return`${e}.task-card[data-task-id=${o}]`;let n=t.getAttribute("data-status");if(n&&t.classList.contains("column-scroll"))return`${e}.column-scroll[data-status=${n}]`;let r=t.getAttribute("data-column-id");if(r)return`${e}[data-column-id=${r}]`;let s=Array.from(t.classList).slice(0,2),i=e;s.length&&(i+="."+s.join("."));let y=t.parentElement;if(y){let g=Array.from(y.children).filter(l=>l.tagName===t.tagName);if(g.length>1){let l=g.indexOf(t)+1;i+=`:nth-of-type(${l})`}}return i}function mt(t,e=W){let o=window.getComputedStyle(t),n={};for(let r of e)n[r]=o.getPropertyValue(r);return n}function pt(t,e=200){return t.replace(/\s+/g," ").trim().slice(0,e)}function yt(t){let e=new Map;for(let o of t){let n=o.tagName.toLowerCase(),r=Array.from(o.classList).slice(0,2).join("."),s=r?`${n}.${r}`:n;e.set(s,(e.get(s)||0)+1)}return Array.from(e.entries()).map(([o,n])=>`${o} \xD7${n}`).slice(0,10)}var p=class extends Error{constructor(o,n,r){super(n);this.code=o;this.suggestion=r;this.name="VerifyError"}code;suggestion};async function Pt(){try{return await import("playwright")}catch{throw new p("E_PLAYWRIGHT_MISSING","Playwright is not installed.","Run: npx playwright install chromium")}}async function kt(t,e,o={}){let n=wt(t),r=ot(n,e);if(!r)throw new p("E_NOT_FOUND",`Task not found: ${e}`,"Run 'vibeflow tasks' to see available task IDs.");let s=nt(r);if(!s)throw new p("E_NOT_FOUND",`Task not found: ${e}`);let i,y=s.baselineElementFile||"baseline-element.json",g=I(n,e,y);if(g)try{i=JSON.parse(z(g,"utf-8"))}catch{}if(!i&&s.baseline&&(i=s.baseline),!i)throw new p("E_NO_BASELINE","Task has no baseline. Re-annotate to capture one.");let l=[],E={},x={};if(s.authStateEnc&&s.author)try{let m=JSON.parse(s.authStateEnc),f=at(m,s.author);if(!f)throw new p("E_AUTH_EXPIRED","Auth state expired. Re-annotate to capture fresh cookies.");l=f.cookies,E=f.localStorage,x=f.sessionStorage}catch(m){throw m instanceof p?m:new p("E_AUTH_CORRUPT","Auth state corrupted. Re-annotate.")}let v=o.url??s.url;if(!v)throw new p("E_NO_URL","Task has no URL. Cannot verify without a target URL.","Re-annotate the element with a URL, or use --url to override.");let C=ut(n).port??3700,A=v.startsWith("http")?v:`http://localhost:${C}${v.startsWith("/")?v:"/"+v}`,S=s.cssSelector??s.selector;if(!S||S==="/")throw new p("E_NO_SELECTOR","Task has no CSS selector. Cannot verify without a selector.");let j=await Pt(),$;try{$=await j.chromium.launch({headless:!0})}catch(m){let f=m instanceof Error?m.message:String(m);throw f.includes("not found")||f.includes("not installed")||f.includes("Executable doesn't exist")?new p("E_PLAYWRIGHT_MISSING","Chromium is not installed.","Run: npx playwright install chromium"):new p("E_PLAYWRIGHT_CRASH",`Verification failed: ${f}`,"Try again.")}let k;try{let m=i.position?.viewport;k=await $.newContext({viewport:m?{width:m.width,height:m.height}:void 0,deviceScaleFactor:m?.dpr,userAgent:i.browser||void 0}),l.length>0&&await k.addCookies(l.map(a=>({name:a.name,value:a.value,domain:a.domain,path:a.path,expires:a.expires,httpOnly:a.httpOnly,secure:a.secure,sameSite:a.sameSite})));let f=await k.newPage(),T=[];f.on("console",a=>{a.type()==="error"&&T.push(a.text())}),f.on("pageerror",a=>{T.push(a.message)});try{await f.goto(A,{waitUntil:"networkidle",timeout:15e3})}catch(a){let u=a instanceof Error?a.message:String(a);throw u.includes("ERR_CONNECTION_REFUSED")||u.includes("ECONNREFUSED")?new p("E_APP_NOT_RUNNING",`Cannot connect to ${A}. Is the dev server running?`):new p("E_NAVIGATION_FAILED",`Failed to navigate to ${A}: ${u}`)}try{Object.keys(E).length>0&&await f.evaluate(a=>{for(let[u,d]of Object.entries(a))window.localStorage.setItem(u,d)},E),Object.keys(x).length>0&&await f.evaluate(a=>{for(let[u,d]of Object.entries(a))window.sessionStorage.setItem(u,d)},x)}catch{}let P=0;try{await f.waitForSelector(S,{timeout:1e4}),P=await f.locator(S).count()}catch{let a={outerHTML:"",computedStyles:{},selector:S,position:i.position,browser:i.browser,consoleErrors:T,capturedAt:new Date().toISOString()},u=H(i,a);return K(s.id,s.description,i,a,u,[],S)}if(P>1){let a={outerHTML:"",computedStyles:{},selector:S,position:i.position,browser:i.browser,consoleErrors:T,capturedAt:new Date().toISOString()},u=H(i,a),d=`Selector matches ${P} elements. The selector may need to be more specific.`;return K(s.id,s.description,i,a,u,[],S,d)}let L=await Lt(f,S,i,T),_=null,B=s.baselineFile||"baseline-page.json",F=I(n,e,B);if(F)try{_=JSON.parse(z(F,"utf-8"))}catch{}let h=H(i,L),O=await _t(n,e,i,L,h,T,f,S);if(_&&_.elements){let a=I(n,e,"verify-all-styles.json");if(a)try{let u=JSON.parse(z(a,"utf-8"));if(u&&u.elements){let d={},St=Object.entries(u.elements);for(let[Y,X]of St){let J=_.elements[Y]?.after;if(J){X.baseline=J;let V={};for(let[q,Q]of Object.entries(X.after)){let Z=J[q];Z!==Q&&(V[q]=[Z,Q])}Object.keys(V).length>0&&(d[Y]=V)}}R(n,e,"verify-all-styles.json",Buffer.from(JSON.stringify(u,null,2)));let Et=JSON.stringify({totalChanged:Object.keys(d).length,elements:d},null,2);R(n,e,"verify-page-diff.json",Buffer.from(Et)),O.push(N(b(n,e),"verify-page-diff.json"))}}catch{}}let D=K(s.id,s.description,i,L,h,O,S);return rt(n,e,{verified:D.ok}),D}finally{await k?.close(),await $?.close()}}async function Lt(t,e,o,n){let r=t.locator(e).first(),s=await r.evaluate(l=>l.outerHTML).catch(()=>""),i=await r.evaluate(l=>{let E=window.getComputedStyle(l),x={};for(let v=0;v<E.length;v++){let w=E[v];x[w]=E.getPropertyValue(w)}return x}).catch(()=>({})),g={boundingBox:await r.boundingBox().catch(()=>null)??o.position.boundingBox,scrollPosition:await t.evaluate(()=>({x:window.scrollX,y:window.scrollY})),viewport:await t.evaluate(()=>({width:window.innerWidth,height:window.innerHeight,dpr:window.devicePixelRatio})),stackingContext:o.position.stackingContext};return{outerHTML:s,computedStyles:i,selector:e,position:g,browser:o.browser,consoleErrors:n,capturedAt:new Date().toISOString()}}async function _t(t,e,o,n,r,s,i,y){let g=[],l=b(t,e);try{let w=Tt(l);for(let C of w)(C.startsWith("verify-")||C==="baseline.json")&&At(N(l,C))}catch{}let E=JSON.stringify(n,null,2);R(t,e,"verify-after.json",Buffer.from(E)),g.push(N(b(t,e),"verify-after.json"));let x=JSON.stringify(r,null,2);R(t,e,"verify-diff.json",Buffer.from(x)),g.push(N(b(t,e),"verify-diff.json"));let v=s.length>0?s.join(`
|
|
3
|
+
`):"(no console errors)";if(R(t,e,"verify-console.txt",Buffer.from(v)),g.push(N(b(t,e),"verify-console.txt")),i){try{let w=await i.content();R(t,e,"verify-page.html",Buffer.from(w)),g.push(N(b(t,e),"verify-page.html"))}catch{}try{let C=await i.evaluate(({buildKeyFn:A,buildSelectorFn:S,filterStylesFn:j,childSignatureFn:$,normalizeTextFn:k,styles:m,maxElements:f})=>{let T={},P=0,L=!1,_=document.createTreeWalker(document.body,NodeFilter.SHOW_ELEMENT,{acceptNode(h){if(P>=f)return NodeFilter.FILTER_REJECT;let O=h;return O.classList.length>0||O.hasAttribute("data-task-id")||O.hasAttribute("data-status")||O.hasAttribute("data-column-id")?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_SKIP}}),B=[],F;for(;F=_.nextNode();)B.push(F);for(let h of B){if(P>=f){L=!0;break}let O=A(h),D=Array.from(h.children),a={};for(let d of Array.from(h.attributes))d.name.startsWith("data-")&&(a[d.name.slice(5)]=d.value);let u={x:0,y:0,width:0,height:0};try{let d=h.getBoundingClientRect();u={x:Math.round(d.x),y:Math.round(d.y),width:Math.round(d.width),height:Math.round(d.height)}}catch{}T[O]={key:O,selector:S(h),tag:h.tagName.toLowerCase(),classes:Array.from(h.classList),dataAttrs:a,parentKey:h.parentElement?A(h.parentElement):"",childCount:D.length,childSignature:$(D),text:k(h.textContent??""),position:u,baseline:j(h,m),after:j(h,m)},P++}return{version:1,capturedAt:new Date().toISOString(),truncated:L,elements:T}},{buildKeyFn:dt,buildSelectorFn:ht,filterStylesFn:mt,childSignatureFn:yt,normalizeTextFn:pt,styles:W,maxElements:1e3});if(C){let A=JSON.stringify(C,null,2);R(t,e,"verify-all-styles.json",Buffer.from(A)),g.push(N(b(t,e),"verify-all-styles.json"))}}catch{}try{let w=await i.screenshot({fullPage:!1});R(t,e,"verify-screenshot.png",w),g.push(N(b(t,e),"verify-screenshot.png"))}catch{}if(y)try{let w=await i.locator(y).first().evaluate(C=>C.outerHTML);R(t,e,"verify-element.html",Buffer.from(w)),g.push(N(b(t,e),"verify-element.html"))}catch{}}if(o){let w=JSON.stringify(o,null,2);R(t,e,"baseline.json",Buffer.from(w)),g.push(N(b(t,e),"baseline.json"))}return g}function K(t,e,o,n,r,s,i,y){let g=r.selectorResolves&&r.newConsoleErrors.length===0,l=y??ct(r,i);return{taskId:t,ok:g,taskDescription:e,baseline:{selector:o.selector,url:"",capturedAt:o.capturedAt,snapshot:o},after:{snapshot:n,consoleErrors:n.consoleErrors},diff:r,evidenceFiles:s,verdict:l}}async function ce(t,e,o){let n=wt(t);try{let r=await kt(n,e,o);await $t(n,e,r),o.json?console.log(JSON.stringify(r,null,2)):Ft(r)}catch(r){if(r instanceof p)o.json?process.stderr.write(JSON.stringify({ok:!1,error:{code:r.code,message:r.message,suggestion:r.suggestion}})+`
|
|
4
|
+
`):(process.stderr.write(c.red(`\u2717 ${r.message}
|
|
5
|
+
`)),r.suggestion&&process.stderr.write(c.dim(` ${r.suggestion}
|
|
6
|
+
`))),process.exitCode=U.GENERAL;else{let s=r instanceof Error?r.message:String(r);o.json?process.stderr.write(JSON.stringify({ok:!1,error:{code:"E_UNKNOWN",message:s}})+`
|
|
7
|
+
`):process.stderr.write(c.red(`\u2717 Verification failed: ${s}
|
|
8
|
+
`)),process.exitCode=U.GENERAL}}}async function $t(t,e,o){let n=`**Verification ${o.ok?"\u2705 passed":"\u26A0\uFE0F issues detected"}**
|
|
9
|
+
|
|
10
|
+
${o.verdict}`;st(t,e,"agent",n,void 0,"system")}function Ft(t){let e=t.ok?c.green("\u2705"):c.yellow("\u26A0\uFE0F");console.log(),console.log(` ${e} Evidences collected for task ${t.taskId}`),console.log(c.dim("\u2500".repeat(60))),console.log(c.dim(` Verdict: ${t.verdict}`)),console.log();let o=t.diff;if(o.selectorResolves){o.htmlChanged?console.log(c.cyan(" \u2713 HTML changed")):console.log(c.dim(" \xB7 HTML unchanged"));let n=Object.keys(o.stylesChanged).length;if(n>0){console.log(c.cyan(` \u2713 ${n} style property change(s):`));for(let[r,[s,i]]of Object.entries(o.stylesChanged).slice(0,8))console.log(c.dim(` ${r}: ${s} \u2192 ${i}`));n>8&&console.log(c.dim(` ... and ${n-8} more`))}if(o.positionChanged&&console.log(c.cyan(" \u2713 Position shifted")),o.newConsoleErrors.length>0){console.log(c.yellow(` \u26A0 ${o.newConsoleErrors.length} new console error(s):`));for(let r of o.newConsoleErrors.slice(0,3))console.log(c.dim(` ${r.slice(0,120)}`))}}else console.log(c.red(" \u26A0 Target element not found \u2014 the fix may have renamed/removed it."));if(t.evidenceFiles.length>0){console.log(c.cyan(` Evidence files (${t.evidenceFiles.length}):`));for(let n of t.evidenceFiles){let r=n.split("/").pop()??n;try{let i=(Nt(n).size/1024).toFixed(1);console.log(c.dim(` ${r} (${i} KB)`))}catch{console.log(c.dim(` ${r}`))}}}console.log(),console.log(c.cyan(" Explore evidence (agent tools):")),console.log(c.dim(` vibeflow verify style_query ${t.taskId} <property>`)),console.log(c.dim(` vibeflow verify style_diff ${t.taskId} [--filter <pattern>]`)),console.log(c.dim(` vibeflow verify element_info ${t.taskId}`)),console.log(c.dim(` vibeflow verify html_diff ${t.taskId}`)),console.log(),console.log(c.cyan(" Review evidences:")),console.log(c.dim(" 1. Check if the fix is confirmed (styles match expectations)")),console.log(c.dim(` 2. If confirmed \u2192 vibeflow tasks --edit ${t.taskId} --set-status review --comment "Verified: <what you confirmed>"`)),console.log(c.dim(" 3. If not sure \u2192 leave a comment explaining uncertainty")),console.log(c.dim(` 4. If wrong \u2192 vibeflow tasks --edit ${t.taskId} --set-status in-progress`)),console.log(),t.ok||(console.log(c.yellow(" \u26A0 Verification failed \u2014 move to in-progress and fix:")),console.log(c.dim(` vibeflow tasks --edit ${t.taskId} --set-status in-progress`)))}export{jt as a,ut as b,Wt as c,zt as d,p as e,kt as f,ce as g,$t as h};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{c as st}from"./chunk-CQ524DVH.js";import{a as U}from"./chunk-BZDBSBRV.js";import{g as b,k as R,m as I}from"./chunk-IJ67OEKX.js";import{c as tt,g as et,n as ot,s as nt,v as rt}from"./chunk-PN5C7EHL.js";import c from"chalk";import{resolve as wt,join as N}from"path";import{readFileSync as z}from"fs";import{statSync as Nt,readdirSync as Tt,unlinkSync as At}from"fs";import M from"crypto";var vt="vibeflow-auth-v1",xt=1440*60*1e3;function it(t){return M.scryptSync(t,vt,32)}function jt(t,e){let o=it(e),n=M.randomBytes(16),r=M.createCipheriv("aes-256-gcm",o,n),s=JSON.stringify(t),i=Buffer.concat([r.update(s),r.final()]),y=r.getAuthTag();return{version:1,createdAt:new Date().toISOString(),expiresAt:new Date(Date.now()+xt).toISOString(),iv:n.toString("hex"),tag:y.toString("hex"),data:i.toString("hex")}}function at(t,e){if(new Date(t.expiresAt)<new Date)return null;try{let o=it(e),n=M.createDecipheriv("aes-256-gcm",o,Buffer.from(t.iv,"hex"));n.setAuthTag(Buffer.from(t.tag,"hex"));let r=Buffer.concat([n.update(Buffer.from(t.data,"hex")),n.final()]);return JSON.parse(r.toString())}catch{return null}}function bt(t,e){return Math.abs(t.x-e.x)>2||Math.abs(t.y-e.y)>2||Math.abs(t.width-e.width)>2||Math.abs(t.height-e.height)>2}function H(t,e){let o=!!e.outerHTML,n=t.outerHTML!==e.outerHTML,r={},s=new Set([...Object.keys(t.computedStyles),...Object.keys(e.computedStyles)]);for(let l of s){let E=t.computedStyles[l],x=e.computedStyles[l];E!==x&&(r[l]=[E??"",x??""])}let i=bt(t.position.boundingBox,e.position.boundingBox),y=new Set(t.consoleErrors),g=e.consoleErrors.filter(l=>!y.has(l));return{selectorResolves:o,htmlChanged:n,stylesChanged:r,positionChanged:i,newConsoleErrors:g}}function ct(t,e){let o=[];if(t.selectorResolves){t.htmlChanged&&o.push("HTML changed");let n=Object.keys(t.stylesChanged).length;if(n>0){let r=Object.keys(t.stylesChanged).slice(0,5).join(", "),s=n>5?` (+${n-5} more)`:"";o.push(`${n} style property change(s): ${r}${s}`)}t.positionChanged&&o.push("position shifted"),t.newConsoleErrors.length>0&&o.push(`${t.newConsoleErrors.length} new console error(s)`),o.length===0&&o.push("no structural changes detected")}else o.push(`Selector "${e}" no longer resolves`);return o.join("; ")}import{readFileSync as lt,writeFileSync as Ht,mkdirSync as Jt,existsSync as ft}from"fs";import{join as gt,basename as Rt}from"path";import{execSync as Ct}from"child_process";var G={port:3700};function Ot(t){return gt(t,tt,et)}function ut(t){let e=Ot(t);if(!ft(e))return{...G};let o=lt(e,"utf-8"),n;try{n=JSON.parse(o)}catch{return{...G}}return{...G,...n}}function Wt(t){let e=gt(t,"package.json");if(ft(e))try{let o=JSON.parse(lt(e,"utf-8"));if(o.name)return o.name}catch{}return Rt(t)}function zt(t){try{return Ct("git branch --show-current",{cwd:t,encoding:"utf-8",stdio:["pipe","pipe","pipe"]}).trim()||null}catch{return null}}var W=["display","visibility","opacity","position","overflow","overflow-x","overflow-y","width","min-width","max-width","height","min-height","max-height","margin","margin-top","margin-right","margin-bottom","margin-left","padding","padding-top","padding-right","padding-bottom","padding-left","border","border-width","border-style","border-color","border-radius","background-color","background-image","color","font-size","font-weight","font-style","font-family","line-height","letter-spacing","text-align","text-decoration","text-overflow","white-space","flex-direction","flex-wrap","flex","justify-content","align-items","gap","row-gap","column-gap","grid-template-columns","grid-template-rows","z-index","box-shadow","transform","transition","cursor","user-select","pointer-events","top","left","right","bottom"];function dt(t){let e=[],o=t;for(;o&&o!==document.documentElement;){let n=o.parentElement;if(!n)break;let r=Array.from(n.children).indexOf(o);e.unshift(r),o=n}return e.join("/")}function ht(t){let e=t.tagName.toLowerCase(),o=t.getAttribute("data-task-id");if(o)return`${e}.task-card[data-task-id=${o}]`;let n=t.getAttribute("data-status");if(n&&t.classList.contains("column-scroll"))return`${e}.column-scroll[data-status=${n}]`;let r=t.getAttribute("data-column-id");if(r)return`${e}[data-column-id=${r}]`;let s=Array.from(t.classList).slice(0,2),i=e;s.length&&(i+="."+s.join("."));let y=t.parentElement;if(y){let g=Array.from(y.children).filter(l=>l.tagName===t.tagName);if(g.length>1){let l=g.indexOf(t)+1;i+=`:nth-of-type(${l})`}}return i}function mt(t,e=W){let o=window.getComputedStyle(t),n={};for(let r of e)n[r]=o.getPropertyValue(r);return n}function pt(t,e=200){return t.replace(/\s+/g," ").trim().slice(0,e)}function yt(t){let e=new Map;for(let o of t){let n=o.tagName.toLowerCase(),r=Array.from(o.classList).slice(0,2).join("."),s=r?`${n}.${r}`:n;e.set(s,(e.get(s)||0)+1)}return Array.from(e.entries()).map(([o,n])=>`${o} \xD7${n}`).slice(0,10)}var p=class extends Error{constructor(o,n,r){super(n);this.code=o;this.suggestion=r;this.name="VerifyError"}code;suggestion};async function Pt(){try{return await import("playwright")}catch{throw new p("E_PLAYWRIGHT_MISSING","Playwright is not installed.","Run: npx playwright install chromium")}}async function kt(t,e,o={}){let n=wt(t),r=ot(n,e);if(!r)throw new p("E_NOT_FOUND",`Task not found: ${e}`,"Run 'vibeflow tasks' to see available task IDs.");let s=nt(r);if(!s)throw new p("E_NOT_FOUND",`Task not found: ${e}`);let i,y=s.baselineElementFile||"baseline-element.json",g=I(n,e,y);if(g)try{i=JSON.parse(z(g,"utf-8"))}catch{}if(!i&&s.baseline&&(i=s.baseline),!i)throw new p("E_NO_BASELINE","Task has no baseline. Re-annotate to capture one.");let l=[],E={},x={};if(s.authStateEnc&&s.author)try{let m=JSON.parse(s.authStateEnc),f=at(m,s.author);if(!f)throw new p("E_AUTH_EXPIRED","Auth state expired. Re-annotate to capture fresh cookies.");l=f.cookies,E=f.localStorage,x=f.sessionStorage}catch(m){throw m instanceof p?m:new p("E_AUTH_CORRUPT","Auth state corrupted. Re-annotate.")}let v=o.url??s.url;if(!v)throw new p("E_NO_URL","Task has no URL. Cannot verify without a target URL.","Re-annotate the element with a URL, or use --url to override.");let C=ut(n).port??3700,A=v.startsWith("http")?v:`http://localhost:${C}${v.startsWith("/")?v:"/"+v}`,S=s.cssSelector??s.selector;if(!S||S==="/")throw new p("E_NO_SELECTOR","Task has no CSS selector. Cannot verify without a selector.");let j=await Pt(),$;try{$=await j.chromium.launch({headless:!0})}catch(m){let f=m instanceof Error?m.message:String(m);throw f.includes("not found")||f.includes("not installed")||f.includes("Executable doesn't exist")?new p("E_PLAYWRIGHT_MISSING","Chromium is not installed.","Run: npx playwright install chromium"):new p("E_PLAYWRIGHT_CRASH",`Verification failed: ${f}`,"Try again.")}let k;try{let m=i.position?.viewport;k=await $.newContext({viewport:m?{width:m.width,height:m.height}:void 0,deviceScaleFactor:m?.dpr,userAgent:i.browser||void 0}),l.length>0&&await k.addCookies(l.map(a=>({name:a.name,value:a.value,domain:a.domain,path:a.path,expires:a.expires,httpOnly:a.httpOnly,secure:a.secure,sameSite:a.sameSite})));let f=await k.newPage(),T=[];f.on("console",a=>{a.type()==="error"&&T.push(a.text())}),f.on("pageerror",a=>{T.push(a.message)});try{await f.goto(A,{waitUntil:"networkidle",timeout:15e3})}catch(a){let u=a instanceof Error?a.message:String(a);throw u.includes("ERR_CONNECTION_REFUSED")||u.includes("ECONNREFUSED")?new p("E_APP_NOT_RUNNING",`Cannot connect to ${A}. Is the dev server running?`):new p("E_NAVIGATION_FAILED",`Failed to navigate to ${A}: ${u}`)}try{Object.keys(E).length>0&&await f.evaluate(a=>{for(let[u,d]of Object.entries(a))window.localStorage.setItem(u,d)},E),Object.keys(x).length>0&&await f.evaluate(a=>{for(let[u,d]of Object.entries(a))window.sessionStorage.setItem(u,d)},x)}catch{}let P=0;try{await f.waitForSelector(S,{timeout:1e4}),P=await f.locator(S).count()}catch{let a={outerHTML:"",computedStyles:{},selector:S,position:i.position,browser:i.browser,consoleErrors:T,capturedAt:new Date().toISOString()},u=H(i,a);return K(s.id,s.description,i,a,u,[],S)}if(P>1){let a={outerHTML:"",computedStyles:{},selector:S,position:i.position,browser:i.browser,consoleErrors:T,capturedAt:new Date().toISOString()},u=H(i,a),d=`Selector matches ${P} elements. The selector may need to be more specific.`;return K(s.id,s.description,i,a,u,[],S,d)}let L=await Lt(f,S,i,T),_=null,B=s.baselineFile||"baseline-page.json",F=I(n,e,B);if(F)try{_=JSON.parse(z(F,"utf-8"))}catch{}let h=H(i,L),O=await _t(n,e,i,L,h,T,f,S);if(_&&_.elements){let a=I(n,e,"verify-all-styles.json");if(a)try{let u=JSON.parse(z(a,"utf-8"));if(u&&u.elements){let d={},St=Object.entries(u.elements);for(let[Y,X]of St){let J=_.elements[Y]?.after;if(J){X.baseline=J;let V={};for(let[q,Q]of Object.entries(X.after)){let Z=J[q];Z!==Q&&(V[q]=[Z,Q])}Object.keys(V).length>0&&(d[Y]=V)}}R(n,e,"verify-all-styles.json",Buffer.from(JSON.stringify(u,null,2)));let Et=JSON.stringify({totalChanged:Object.keys(d).length,elements:d},null,2);R(n,e,"verify-page-diff.json",Buffer.from(Et)),O.push(N(b(n,e),"verify-page-diff.json"))}}catch{}}let D=K(s.id,s.description,i,L,h,O,S);return rt(n,e,{verified:D.ok}),D}finally{await k?.close(),await $?.close()}}async function Lt(t,e,o,n){let r=t.locator(e).first(),s=await r.evaluate(l=>l.outerHTML).catch(()=>""),i=await r.evaluate(l=>{let E=window.getComputedStyle(l),x={};for(let v=0;v<E.length;v++){let w=E[v];x[w]=E.getPropertyValue(w)}return x}).catch(()=>({})),g={boundingBox:await r.boundingBox().catch(()=>null)??o.position.boundingBox,scrollPosition:await t.evaluate(()=>({x:window.scrollX,y:window.scrollY})),viewport:await t.evaluate(()=>({width:window.innerWidth,height:window.innerHeight,dpr:window.devicePixelRatio})),stackingContext:o.position.stackingContext};return{outerHTML:s,computedStyles:i,selector:e,position:g,browser:o.browser,consoleErrors:n,capturedAt:new Date().toISOString()}}async function _t(t,e,o,n,r,s,i,y){let g=[],l=b(t,e);try{let w=Tt(l);for(let C of w)(C.startsWith("verify-")||C==="baseline.json")&&At(N(l,C))}catch{}let E=JSON.stringify(n,null,2);R(t,e,"verify-after.json",Buffer.from(E)),g.push(N(b(t,e),"verify-after.json"));let x=JSON.stringify(r,null,2);R(t,e,"verify-diff.json",Buffer.from(x)),g.push(N(b(t,e),"verify-diff.json"));let v=s.length>0?s.join(`
|
|
3
|
+
`):"(no console errors)";if(R(t,e,"verify-console.txt",Buffer.from(v)),g.push(N(b(t,e),"verify-console.txt")),i){try{let w=await i.content();R(t,e,"verify-page.html",Buffer.from(w)),g.push(N(b(t,e),"verify-page.html"))}catch{}try{let C=await i.evaluate(({buildKeyFn:A,buildSelectorFn:S,filterStylesFn:j,childSignatureFn:$,normalizeTextFn:k,styles:m,maxElements:f})=>{let T={},P=0,L=!1,_=document.createTreeWalker(document.body,NodeFilter.SHOW_ELEMENT,{acceptNode(h){if(P>=f)return NodeFilter.FILTER_REJECT;let O=h;return O.classList.length>0||O.hasAttribute("data-task-id")||O.hasAttribute("data-status")||O.hasAttribute("data-column-id")?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_SKIP}}),B=[],F;for(;F=_.nextNode();)B.push(F);for(let h of B){if(P>=f){L=!0;break}let O=A(h),D=Array.from(h.children),a={};for(let d of Array.from(h.attributes))d.name.startsWith("data-")&&(a[d.name.slice(5)]=d.value);let u={x:0,y:0,width:0,height:0};try{let d=h.getBoundingClientRect();u={x:Math.round(d.x),y:Math.round(d.y),width:Math.round(d.width),height:Math.round(d.height)}}catch{}T[O]={key:O,selector:S(h),tag:h.tagName.toLowerCase(),classes:Array.from(h.classList),dataAttrs:a,parentKey:h.parentElement?A(h.parentElement):"",childCount:D.length,childSignature:$(D),text:k(h.textContent??""),position:u,baseline:j(h,m),after:j(h,m)},P++}return{version:1,capturedAt:new Date().toISOString(),truncated:L,elements:T}},{buildKeyFn:dt,buildSelectorFn:ht,filterStylesFn:mt,childSignatureFn:yt,normalizeTextFn:pt,styles:W,maxElements:1e3});if(C){let A=JSON.stringify(C,null,2);R(t,e,"verify-all-styles.json",Buffer.from(A)),g.push(N(b(t,e),"verify-all-styles.json"))}}catch{}try{let w=await i.screenshot({fullPage:!1});R(t,e,"verify-screenshot.png",w),g.push(N(b(t,e),"verify-screenshot.png"))}catch{}if(y)try{let w=await i.locator(y).first().evaluate(C=>C.outerHTML);R(t,e,"verify-element.html",Buffer.from(w)),g.push(N(b(t,e),"verify-element.html"))}catch{}}if(o){let w=JSON.stringify(o,null,2);R(t,e,"baseline.json",Buffer.from(w)),g.push(N(b(t,e),"baseline.json"))}return g}function K(t,e,o,n,r,s,i,y){let g=r.selectorResolves&&r.newConsoleErrors.length===0,l=y??ct(r,i);return{taskId:t,ok:g,taskDescription:e,baseline:{selector:o.selector,url:"",capturedAt:o.capturedAt,snapshot:o},after:{snapshot:n,consoleErrors:n.consoleErrors},diff:r,evidenceFiles:s,verdict:l}}async function ce(t,e,o){let n=wt(t);try{let r=await kt(n,e,o);await $t(n,e,r),o.json?console.log(JSON.stringify(r,null,2)):Ft(r)}catch(r){if(r instanceof p)o.json?process.stderr.write(JSON.stringify({ok:!1,error:{code:r.code,message:r.message,suggestion:r.suggestion}})+`
|
|
4
|
+
`):(process.stderr.write(c.red(`\u2717 ${r.message}
|
|
5
|
+
`)),r.suggestion&&process.stderr.write(c.dim(` ${r.suggestion}
|
|
6
|
+
`))),process.exitCode=U.GENERAL;else{let s=r instanceof Error?r.message:String(r);o.json?process.stderr.write(JSON.stringify({ok:!1,error:{code:"E_UNKNOWN",message:s}})+`
|
|
7
|
+
`):process.stderr.write(c.red(`\u2717 Verification failed: ${s}
|
|
8
|
+
`)),process.exitCode=U.GENERAL}}}async function $t(t,e,o){let n=`**Verification ${o.ok?"\u2705 passed":"\u26A0\uFE0F issues detected"}**
|
|
9
|
+
|
|
10
|
+
${o.verdict}`;st(t,e,"agent",n,void 0,"system")}function Ft(t){let e=t.ok?c.green("\u2705"):c.yellow("\u26A0\uFE0F");console.log(),console.log(` ${e} Evidences collected for task ${t.taskId}`),console.log(c.dim("\u2500".repeat(60))),console.log(c.dim(` Verdict: ${t.verdict}`)),console.log();let o=t.diff;if(o.selectorResolves){o.htmlChanged?console.log(c.cyan(" \u2713 HTML changed")):console.log(c.dim(" \xB7 HTML unchanged"));let n=Object.keys(o.stylesChanged).length;if(n>0){console.log(c.cyan(` \u2713 ${n} style property change(s):`));for(let[r,[s,i]]of Object.entries(o.stylesChanged).slice(0,8))console.log(c.dim(` ${r}: ${s} \u2192 ${i}`));n>8&&console.log(c.dim(` ... and ${n-8} more`))}if(o.positionChanged&&console.log(c.cyan(" \u2713 Position shifted")),o.newConsoleErrors.length>0){console.log(c.yellow(` \u26A0 ${o.newConsoleErrors.length} new console error(s):`));for(let r of o.newConsoleErrors.slice(0,3))console.log(c.dim(` ${r.slice(0,120)}`))}}else console.log(c.red(" \u26A0 Target element not found \u2014 the fix may have renamed/removed it."));if(t.evidenceFiles.length>0){console.log(c.cyan(` Evidence files (${t.evidenceFiles.length}):`));for(let n of t.evidenceFiles){let r=n.split("/").pop()??n;try{let i=(Nt(n).size/1024).toFixed(1);console.log(c.dim(` ${r} (${i} KB)`))}catch{console.log(c.dim(` ${r}`))}}}console.log(),console.log(c.cyan(" Explore evidence (agent tools):")),console.log(c.dim(` vibeflow verify style_query ${t.taskId} <property>`)),console.log(c.dim(` vibeflow verify style_diff ${t.taskId} [--filter <pattern>]`)),console.log(c.dim(` vibeflow verify element_info ${t.taskId}`)),console.log(c.dim(` vibeflow verify html_diff ${t.taskId}`)),console.log(),console.log(c.cyan(" Review evidences:")),console.log(c.dim(" 1. Check if the fix is confirmed (styles match expectations)")),console.log(c.dim(` 2. If confirmed \u2192 vibeflow tasks --edit ${t.taskId} --set-status review --comment "Verified: <what you confirmed>"`)),console.log(c.dim(" 3. If not sure \u2192 leave a comment explaining uncertainty")),console.log(c.dim(` 4. If wrong \u2192 vibeflow tasks --edit ${t.taskId} --set-status in-progress`)),console.log(),t.ok||(console.log(c.yellow(" \u26A0 Verification failed \u2014 move to in-progress and fix:")),console.log(c.dim(` vibeflow tasks --edit ${t.taskId} --set-status in-progress`)))}export{jt as a,ut as b,Wt as c,zt as d,p as e,kt as f,ce as g,$t as h};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{readdirSync as q,writeFileSync as V,readFileSync as G,unlinkSync as b,statSync as F,mkdirSync as Y,existsSync as l}from"fs";import{join as m,basename as R}from"path";var g=".vibeflow",P="tasks",S="tasks/files",v="tasks/screenshots",te="config.json";import{existsSync as f,readFileSync as E,writeFileSync as z,mkdirSync as p,readdirSync as h,unlinkSync as w,renameSync as A}from"fs";import{join as u,extname as I}from"path";import{randomBytes as j}from"crypto";var K=["backlog","todo","in-progress","review","done"];function M(){return j(15).toString("hex")}function k(e){return u(e,g,P)}function H(e){let s=u(e,g),n=u(s,"files"),i=u(s,S),r=u(s,"screenshots"),t=u(s,v);if(f(n)){p(i,{recursive:!0});for(let o of h(n)){let a=u(n,o),c=u(i,o);if(!f(c))try{A(a,c)}catch{}}}if(f(r)){p(t,{recursive:!0});for(let o of h(r)){let a=u(r,o),c=u(t,o);if(!f(c))try{A(a,c)}catch{}}}}function ae(e){H(e),p(k(e),{recursive:!0}),p(u(e,g,v),{recursive:!0})}function D(e){return e.slice(0,10)}function U(e,s,n){let i=k(e);return n?u(i,D(n),`${s}.json`):u(i,`${s}.json`)}function T(e,s){let n=k(e);if(!f(n))return null;for(let i of h(n,{withFileTypes:!0}))if(i.isDirectory()){let r=u(n,i.name,`${s}.json`);if(f(r))return r}else if(i.name===`${s}.json`)return u(n,i.name);return null}function W(e){return{...e,text:e.text??e.content??""}}function J(e){let s=(()=>{if(typeof e.type!="string")return;let n=e.type.trim();if(!(!n||n==="[object Object]"))return n})();return{id:String(e.id??""),title:String(e.title??"Untitled"),description:String(e.description??""),status:K.includes(e.status)?e.status:"todo",url:e.url?String(e.url):void 0,selector:(()=>{let n=String(e.selector??"/");return e.file&&!e.cssSelector&&n.startsWith(String(e.file))?e.url?String(e.url):"/":n})(),cssSelector:e.cssSelector&&String(e.cssSelector)!==String(e.selector??"/")?String(e.cssSelector):void 0,file:e.file?String(e.file):void 0,line:e.line==null?void 0:Number(e.line),col:e.col==null?void 0:Number(e.col),component:e.component?String(e.component):void 0,type:s,priority:e.priority?String(e.priority):void 0,...e.reportBack===!0&&{reportBack:!0},agent:e.agent?String(e.agent):void 0,model:e.model?String(e.model):void 0,author:e.author?String(e.author):void 0,commits:Array.isArray(e.commits)?e.commits.map(n=>({sha:String(n.sha??""),message:String(n.message??""),timestamp:String(n.timestamp??new Date().toISOString())})):void 0,created:String(e.created??new Date().toISOString()),updated:e.updated?String(e.updated):void 0,comments:Array.isArray(e.comments)?e.comments.map(n=>W(n)):[],files:Array.isArray(e.files)?e.files.map(n=>typeof n=="string"?{name:n,addedAt:new Date().toISOString()}:{name:String(n.name??""),addedAt:String(n.addedAt??new Date().toISOString()),linkedPath:n.linkedPath?String(n.linkedPath):void 0,mimeType:n.mimeType?String(n.mimeType):void 0}).filter(n=>n.name):[],screenshot:e.screenshot?String(e.screenshot):void 0,annotatedElementText:e.annotatedElementText?String(e.annotatedElementText):void 0,tags:Array.isArray(e.tags)?e.tags.filter(n=>typeof n=="string"&&n.length>0):void 0,sortKey:e.sortKey?String(e.sortKey):void 0,branchName:e.branchName?String(e.branchName):void 0,baseline:e.baseline&&typeof e.baseline=="object"?e.baseline:void 0,authStateEnc:e.authStateEnc?String(e.authStateEnc):void 0}}function N(e,s){let n=u(k(e),D(s.created));p(n,{recursive:!0});let i=u(n,`${s.id}.json`),r=i+".tmp";z(r,JSON.stringify(s,null,2),"utf-8"),A(r,i)}function O(e){return e.replace(/\\(n|t|r|\\)/g,(s,n)=>{switch(n){case"n":return`
|
|
3
|
+
`;case"t":return" ";case"r":return"\r";case"\\":return"\\";default:return n}})}function ue(e,s){let n=(()=>{let r=(s.priority??"").trim().toLowerCase();return r==="critical"?"Critical":r==="high"?"High":r==="low"?"Low":"Medium"})(),i={...s,title:O(s.title??"").trim(),description:O(s.description??"").trim(),priority:n,id:M(),created:new Date().toISOString(),comments:[],files:[]};return N(e,i),i}function y(e){try{let s=E(e,"utf-8");if(e.endsWith(".json")){let n=JSON.parse(s);return!n||typeof n!="object"||!("id"in n)?null:J(n)}return null}catch{return null}}function L(e){let s=k(e);if(!f(s))return[];let n=[];for(let i of h(s,{withFileTypes:!0}))if(i.isDirectory()){let r=u(s,i.name);for(let t of h(r))if(I(t)===".json"){let o=u(r,t),a=y(o);a&&n.push({task:a,filePath:o})}}else if(I(i.name)===".json"){let r=u(s,i.name),t=y(r);t&&n.push({task:t,filePath:r})}return n}function ce(e){return L(e).map(({task:s})=>s)}function le(e){return L(e).map(({task:s,filePath:n})=>({...s,filePath:n}))}function B(e,s,n){let i=T(e,s),r=i?y(i):null;if(!r)return null;let t={...r,...n,updated:new Date().toISOString()};if(N(e,t),i&&i!==U(e,s,t.created))try{w(i)}catch{}return t}function me(e,s){let n=T(e,s);return n?(w(n),!0):!1}function fe(e,s,n){return{id:e.id,status:e.status,title:e.title,description:e.description,...e.url&&{url:e.url},selector:e.selector,...e.file&&{file:e.file},...e.line!=null&&{line:e.line},...e.col!=null&&{col:e.col},...e.component&&{component:e.component},...e.type&&{type:e.type},...e.priority&&{priority:e.priority},...s&&s.length>0&&{structuredComments:s},...n&&n.length>0&&{linkedFiles:n},...e.reportBack&&{reportBack:!0},created:e.created}}function de(e,s,n,i,r){let t=[];if(t.push(`[${e.status}] ${e.title}`),t.push(` id: ${e.id}`),t.push(` file: ${s}`),e.file&&t.push(` source: ${e.file}${e.line==null?"":`:${e.line}`}${e.col==null?"":`:${e.col}`}`),e.component&&t.push(` component: ${e.component}`),t.push(` selector: ${e.selector??"/"}`),e.cssSelector&&t.push(` css: ${e.cssSelector}`),e.url&&t.push(` url: ${e.url}`),e.commits&&e.commits.length>0)if(e.commits.length===1)t.push(` commit: ${e.commits[0].sha}`);else{t.push(` commits (${e.commits.length}):`);for(let o of e.commits)t.push(` ${o.sha.slice(0,8)} ${o.timestamp} ${o.message.slice(0,60)}`)}if(t.push(` created: ${e.created}`),e.type&&t.push(` type: ${e.type}`),e.priority&&t.push(` priority: ${e.priority}`),e.author&&t.push(` author: ${e.author}`),e.description){t.push(" description:");for(let o of e.description.split(`
|
|
4
|
+
`))t.push(` ${o}`)}if(e.annotatedElementText&&t.push(` element text: ${e.annotatedElementText}`),n.length>0){t.push(` comments (${n.length}):`);for(let o of n){let a=o.updatedAt?` (edited ${o.updatedAt})`:"";t.push(` [${o.author??"user"}] ${o.createdAt}${a}`);for(let c of o.text.split(`
|
|
5
|
+
`))t.push(` ${c}`)}}if(i.length>0){t.push(` linked files (${i.length}):`);for(let o of i){t.push(` - ${o.name} ${o.url}`);let a=o.linkedPath??u(r,".vibeflow","files",e.id,o.name);if(/\.(md|txt)$/i.test(o.name)&&o.size<1e5&&f(a))try{let c=E(a,"utf-8");t.push(" \u250C\u2500\u2500 content \u2500\u2500");for(let _ of c.split(`
|
|
6
|
+
`))t.push(` \u2502 ${_}`);t.push(" \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")}catch{}}}return t.join(`
|
|
7
|
+
`)}function ge(e){let{autoCommit:s,autoPush:n,autoComment:i,createBranch:r}=e,t=[];if(t.push("Agent instructions (concise):"),t.push(" Discover: vibeflow tasks --status todo | vibeflow tasks --type Research | vibeflow tasks --user <email> | vibeflow tasks --tag <tag>"),t.push(" Auto-claim: vibeflow tasks --next (picks highest-priority todo task and sets it in-progress automatically)"),t.push(" Details: vibeflow tasks --get <id> (full task info with comments and files)"),t.push(' Create: vibeflow tasks --add --title "..." --description "..."'),t.push(""),t.push(" Workflow:"),r?(t.push(" \u26A0 Create a branch FIRST, before any implementation:"),t.push(" 1. git checkout -b <short-name> (e.g. fix/annotation-errors, feat/eye-toggle, chore/cleanup-extension)"),t.push(" Branch name rules: lowercase, kebab-case, 2-5 words, prefix fix/feat/chore/docs."),t.push(" Describe the WORK done (not dates). Bad: agent/2026-04-16. Good: fix/bug-errors-visibility."),t.push(" 2. vibeflow tasks --edit <id> --set-status in-progress \u2190 CLAIM TASK"),t.push(" 3. <implement the change>")):(t.push(" \u26A0 IMMEDIATELY set in-progress BEFORE any implementation work:"),t.push(" 1. vibeflow tasks --edit <id> --set-status in-progress \u2190 DO THIS FIRST"),t.push(" (if you used --next: task is already in-progress \u2014 skip to step 2)"),t.push(" 2. <implement the change>")),s){t.push(r?" 4. git add <files> (stage your changes first)":" 3. git add <files> (stage your changes first)");let a=["--set-status review"];s&&a.push('--commit-message "<one-line summary>"'),i&&a.push('--comment "<report>"'),t.push(` ${r?"5":"4"}. vibeflow tasks --edit <id> ${a.join(" ")}`),t.push(" CLI will commit staged changes and link the commit SHA automatically.")}else{t.push(r?' 4. git add <files> && vibeflow tasks --commit --task <id> --message "<one-line summary>"':' 3. git add <files> && vibeflow tasks --commit --task <id> --message "<one-line summary>"');let a=["--set-status review"];i&&a.push('--comment "<report>"'),t.push(` ${r?"5":"4"}. vibeflow tasks --edit <id> ${a.join(" ")}`)}let o=r?"6":"5";return t.push(` ${o}. vibeflow verify <id> (verify the fix \u2014 check the evidences it produces)`),t.push(" If verification fails, set task back to in-progress and fix."),i&&(t.push(""),t.push(" Comment format (--comment):"),t.push(" \xB7 Plain text for concise one-liners."),t.push(" \xB7 Markdown for multi-section reports. Use **bold**, bullet lists, code fences."),t.push(" \xB7 Must cover: what changed, why, key decisions, anything future agents should know."),t.push(" \xB7 For long reports, attach a .md file and reference it in the comment.")),t.push(""),s&&t.push(" [setting] Auto-commit ON: provide --commit-message when setting status to review; CLI commits."),n&&t.push(" [setting] Auto-push ON: CLI pushes after the commit automatically."),i&&t.push(" [setting] Auto-comment ON: --comment is required when setting status to review."),r&&t.push(" [setting] Create branch ON: all work goes on a dedicated branch created before implementation."),e.hasResearchTasks&&(t.push(" Research tasks: NEVER generate code \u2014 research only."),t.push(" Attach a .md report before marking Research tasks as review."),t.push(" CLI ENFORCES: cannot mark Research as review without an attached .md report."),t.push(" Create the report file locally first, then upload when marking as review:"),t.push(' vibeflow tasks --edit <id> --set-status review --report-file ./report.md --comment "..."'),t.push(" The file is saved next to the task and deleted from the original path automatically."),t.push(" Report must include: findings, options considered (with pros/cons), recommendation, sources.")),e.hasBugTasks&&(t.push(" Bug tasks: Reproduce the bug first, then include in the comment:"),t.push(" \xB7 Symptom: what the user sees"),t.push(" \xB7 Trigger: exact steps to reproduce"),t.push(" \xB7 Root cause: the specific code/logic that caused it"),t.push(" \xB7 Fix: what was changed and why it works"),t.push(" \xB7 Evidence: paste the relevant error message or stack trace")),t.push(" BLOCKED? If a task is unclear or missing context:"),t.push(' vibeflow tasks --edit <id> --comment "Blocked: <reason>. Need: <what is needed>."'),t.push(" Then pick the next task: vibeflow tasks --next"),t.push(" Do not guess at unclear requirements \u2014 leave a comment and move on."),t.push(" CRITICAL: NEVER edit .vibeflow/ task files directly."),t.push(" All task operations (status, comments, commits) must go through CLI commands."),t.push(" CRITICAL: Set in-progress BEFORE reading/planning. Other agents may pick the same task."),t.push(" The in-progress transition signals ownership. Skip it and another agent may duplicate your work."),t.push(" Sequence: discover tasks \u2192 pick one \u2192 set in-progress \u2192 THEN read details and implement."),t.push(' CRITICAL: NEVER set a task status to "done".'),t.push(' When your implementation is complete, set the status to "review" \u2014 not "done".'),t.push(" Only humans can mark tasks as done after reviewing the work."),t.push(" The CLI will warn you and still allow the change \u2014 but agents must not use it."),t.push(""),t.join(`
|
|
8
|
+
`)}var $=".linked.json";function d(e,s){return m(e,g,S,s)}function Q(e,s){Y(d(e,s),{recursive:!0})}function X(e,s){let n=m(d(e,s),$);if(!l(n))return[];try{return JSON.parse(G(n,"utf-8"))}catch{return[]}}function Z(e,s){let n=T(e,s),i=n?y(n):null;return!i?.files||i.files.length===0?[]:i.files}function C(e,s,n){B(e,s,{files:n})}function x(e,s){let n=Z(e,s),i=m(d(e,s),$);if(!l(i))return n;let r=X(e,s);if(r.length===0){try{b(i)}catch{}return n}let t=n.slice(),o=!1;for(let a of r)t.find(c=>c.linkedPath===a.path||c.name===a.name&&c.linkedPath)||(t.push({name:a.name,linkedPath:a.path,addedAt:new Date().toISOString()}),o=!0);o&&C(e,s,t);try{b(i)}catch{}return t}function ee(e,s){let n=d(e,s),i=x(e,s),r=new Map;for(let t of i){if(t.linkedPath&&l(t.linkedPath)){let a=F(t.linkedPath);r.set(t.name,{name:t.name,size:a.size,url:`/api/tasks/${s}/files/${encodeURIComponent(t.name)}`,linkedPath:t.linkedPath,createdAt:a.mtime.toISOString()});continue}let o=m(n,t.name);if(l(o)){let a=F(o);r.set(t.name,{name:t.name,size:a.size,url:`/api/tasks/${s}/files/${encodeURIComponent(t.name)}`,createdAt:a.mtime.toISOString()})}}if(l(n))for(let t of q(n,{withFileTypes:!0})){if(!t.isFile()||t.name===$||r.has(t.name))continue;let o=m(n,t.name),a=F(o);r.set(t.name,{name:t.name,size:a.size,url:`/api/tasks/${s}/files/${encodeURIComponent(t.name)}`,createdAt:a.mtime.toISOString()})}return Array.from(r.values())}function be(e,s,n,i){let r=R(n);Q(e,s),V(m(d(e,s),r),i);let t=x(e,s);return t.find(o=>o.name===r&&!o.linkedPath)||(t.push({name:r,addedAt:new Date().toISOString()}),C(e,s,t)),{name:r,size:i.length,url:`/api/tasks/${s}/files/${encodeURIComponent(r)}`}}function xe(e,s,n){let i=R(n),r=x(e,s),t=r.findIndex(a=>a.name===i);if(t!==-1){let[a]=r.splice(t,1);if(C(e,s,r),a&&!a.linkedPath){let c=m(d(e,s),i);l(c)&&b(c)}return!0}let o=m(d(e,s),i);return l(o)?(b(o),!0):!1}function ve(e,s,n){let i=R(n),r=x(e,s).find(o=>o.name===i&&o.linkedPath);if(r?.linkedPath&&l(r.linkedPath))return r.linkedPath;let t=m(d(e,s),i);return l(t)?t:null}function Ae(e,s){return ee(e,s).length}export{g as a,P as b,S as c,v as d,te as e,M as f,ae as g,U as h,T as i,O as j,ue as k,y as l,ce as m,le as n,B as o,me as p,fe as q,de as r,ge as s,d as t,Q as u,ee as v,be as w,xe as x,ve as y,Ae as z};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{c as y,e as S,n as R,s as T,v as x}from"./chunk-ZHA4Y2KB.js";import{readdirSync as A,writeFileSync as z,readFileSync as O,unlinkSync as m,statSync as g,mkdirSync as $,existsSync as f}from"fs";import{join as l,basename as d}from"path";var h=".linked.json";function c(e,n){return l(e,y,S,n)}function b(e,n){$(c(e,n),{recursive:!0})}function L(e,n){let s=l(c(e,n),h);if(!f(s))return[];try{return JSON.parse(O(s,"utf-8"))}catch{return[]}}function F(e,n){let s=R(e,n),r=s?T(s):null;return!r?.files||r.files.length===0?[]:r.files}function P(e,n){return F(e,n)}function k(e,n,s){x(e,n,{files:s})}function p(e,n){let s=F(e,n),r=l(c(e,n),h);if(!f(r))return s;let i=L(e,n);if(i.length===0){try{m(r)}catch{}return s}let t=s.slice(),o=!1;for(let a of i)t.find(u=>u.linkedPath===a.path||u.name===a.name&&u.linkedPath)||(t.push({name:a.name,linkedPath:a.path,addedAt:new Date().toISOString()}),o=!0);o&&k(e,n,t);try{m(r)}catch{}return t}function v(e,n){let s=c(e,n),r=P(e,n),i=new Map;for(let t of r){if(t.linkedPath&&f(t.linkedPath)){let a=g(t.linkedPath);i.set(t.name,{name:t.name,size:a.size,url:`/api/tasks/${n}/files/${encodeURIComponent(t.name)}`,linkedPath:t.linkedPath,createdAt:a.mtime.toISOString()});continue}let o=l(s,t.name);if(f(o)){let a=g(o);i.set(t.name,{name:t.name,size:a.size,url:`/api/tasks/${n}/files/${encodeURIComponent(t.name)}`,createdAt:a.mtime.toISOString()})}}if(f(s))for(let t of A(s,{withFileTypes:!0})){if(!t.isFile()||t.name===h||i.has(t.name))continue;let o=l(s,t.name),a=g(o);i.set(t.name,{name:t.name,size:a.size,url:`/api/tasks/${n}/files/${encodeURIComponent(t.name)}`,createdAt:a.mtime.toISOString()})}return Array.from(i.values())}function U(e,n,s,r){let i=d(s);b(e,n),z(l(c(e,n),i),r);let t=p(e,n);return t.find(o=>o.name===i&&!o.linkedPath)||(t.push({name:i,addedAt:new Date().toISOString()}),k(e,n,t)),{name:i,size:r.length,url:`/api/tasks/${n}/files/${encodeURIComponent(i)}`}}function E(e,n,s){let r=d(s),i=p(e,n),t=i.findIndex(a=>a.name===r);if(t!==-1){let[a]=i.splice(t,1);if(k(e,n,i),a&&!a.linkedPath){let u=l(c(e,n),r);f(u)&&m(u)}return!0}let o=l(c(e,n),r);return f(o)?(m(o),!0):!1}function _(e,n,s){let r=d(s),i=P(e,n).find(o=>o.name===r&&o.linkedPath);if(i?.linkedPath&&f(i.linkedPath))return i.linkedPath;let t=l(c(e,n),r);return f(t)?t:null}function M(e,n){return v(e,n).length}async function B(e){let{listTasks:n}=await import("./tasks-N3AEGAEB.js"),s=n(e),r=0;for(let i of s)try{let t=P(e,i.id);p(e,i.id),F(e,i.id).length!==t.length&&r++}catch{}return r}export{c as a,b,P as c,v as d,U as e,E as f,_ as g,M as h,B as i};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{a as C,b as M}from"./chunk-KJ5LYG6A.js";import{a as _,b as W}from"./chunk-FYRJDWDC.js";import{a as P}from"./chunk-BZDBSBRV.js";import{g as F,j as L}from"./chunk-BBIA4IIO.js";import{n as N}from"./chunk-Q7MEW67Z.js";import{d as T,e as j,g as B}from"./chunk-XHZXHACL.js";import u from"chalk";import{unlinkSync as K,readFileSync as de,existsSync as E}from"fs";import{resolve as pe,join as O}from"path";import A from"fs/promises";import Q from"os";import D from"path";var X=D.join(Q.homedir(),".vibeflow"),x=D.join(X,"token");async function R(){try{return(await A.readFile(x,"utf8")).trim()||null}catch{return null}}async function z(t){await A.mkdir(D.dirname(x),{recursive:!0}),await A.writeFile(x,t,{mode:384})}async function we(){try{await A.unlink(x)}catch{}}import n from"chalk";import Z from"open";import H from"readline";import ee from"@inquirer/select";import{existsSync as te,readdirSync as q,readFileSync as oe}from"fs";import{join as v,resolve as re}from"path";var se="https://app.vibeflow.tools",ne=300*1e3;function G(){return process.env.VIBEFLOW_API_URL??se}async function ie(t){return new Promise(i=>setTimeout(i,t))}async function ae(t){return t.length===1?(console.log(n.dim(` Using board: ${t[0].icon??""} ${t[0].name}`)),t[0]):await ee({message:"Select a board to use:",choices:t.map(o=>({name:`${o.icon??""} ${o.name}`,value:o}))})}async function J(t,i){try{let o=await fetch(`${t}/api/cli/settings`,{headers:{Authorization:`Bearer ${i}`}});if(!o.ok)return;let s=await o.json();s.settings&&Object.keys(s.settings).length>0&&W({...s.settings,_settingsRefreshedAt:Date.now()})}catch{}}async function Ce(){try{let t=await R();if(!t)return;let o=_()._settingsRefreshedAt??0;if(Date.now()-o<ne)return;let s=G();J(s,t)}catch{}}async function le(t,i){try{let[o,s]=await Promise.all([fetch(`${t}/api/cli/workspaces`,{headers:{Authorization:`Bearer ${i}`}}),fetch(`${t}/api/cli/profile`,{headers:{Authorization:`Bearer ${i}`}})]);if(!o.ok)return;let l=await o.json();if(!l.workspaces?.length){console.log(n.yellow(" No boards found. Create a board in the web app first."));return}let a=s.ok?await s.json():null,m=await ae(l.workspaces);await M({...m,email:a?.email??null}),console.log(n.green(` Board set: ${m.icon??""} ${m.name}`)),console.log(n.dim(` Open at: ${m.url}`))}catch{}}async function ce(t,i,o,s){let l=v(t,T,j);if(!te(l))return;let a=[];try{for(let e of q(l,{withFileTypes:!0}))if(e.isDirectory()){let d=v(l,e.name);for(let k of q(d).filter(c=>c.endsWith(".json")))a.push(v(d,k))}else e.isFile()&&e.name.endsWith(".json")&&a.push(v(l,e.name))}catch{return}if(a.length===0)return;let m=a.flatMap(e=>{try{return[JSON.parse(oe(e,"utf-8"))]}catch{return[]}}),h=new Set;try{let e=await fetch(`${i}/api/cli/tasks?boardId=${s.id}`,{headers:{Authorization:`Bearer ${o}`}}).catch(()=>null);if(e?.ok){let d=await e.json();h=new Set((d.tasks??[]).flatMap(k=>[k.id,...k.cliId?[k.cliId]:[]]))}}catch{}let g=m.filter(e=>!h.has(e.id));if(g.length===0)return;console.log(),console.log(n.bold(` ${g.length} local task${g.length!==1?"s are":" is"} not yet synced to the board:`));let y=g.slice(0,10);for(let e of y){let d=e.status?n.dim(` [${e.status}]`):"";console.log(n.dim(` \u2022 ${e.title??e.id}${d}`))}return g.length>10&&console.log(n.dim(` \u2026 and ${g.length-10} more`)),new Promise(e=>{let d=H.createInterface({input:process.stdin,output:process.stdout});d.question(n.cyan(`
|
|
3
|
+
Push them to your board now? [Y/n]: `),async k=>{if(d.close(),k.trim().toLowerCase()==="n"){console.log(n.dim(" Skipped. You can push later with: vibeflow push")),e();return}let c=H.createInterface({input:process.stdin,output:process.stdout});c.question(n.cyan(" Delete local task files after push? [y/N]: "),async $=>{c.close();let b=$.trim().toLowerCase()!=="y";try{let p=await fetch(`${i}/api/cli/import`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${o}`},body:JSON.stringify({boardId:s.id,tasks:g,keepLocalFiles:b})}),f={};try{f=await p.json()}catch{}p.ok?(console.log(n.green(` \u2713 Pushed ${f.imported??0} task${(f.imported??0)!==1?"s":""} to ${s.name}`)),(f.skipped??0)>0&&console.log(n.dim(` (${f.skipped} already existed and were skipped)`))):console.log(n.red(` Push failed: ${f.error??`HTTP ${p.status}`}`))}catch(p){console.log(n.red(` Push failed: ${p instanceof Error?p.message:"Network error"}`))}e()})})})}async function V(t=re(".")){let i=G();console.log(n.cyan(" Initiating device login flow..."));let o;try{o=await fetch(`${i}/api/cli/auth/device-init`,{method:"POST",headers:{"Content-Type":"application/json"}})}catch(y){console.error(n.red(` Failed to connect to login server: ${y instanceof Error?y.message:"Network error"}`)),process.exitCode=P.AUTH;return}if(!o.ok){console.error(n.red(` Failed to initiate login: ${o.status} ${o.statusText}`)),process.exitCode=P.AUTH;return}let{deviceCode:s,userCode:l,verificationUrl:a,expiresIn:m}=await o.json();console.log(),console.log(n.bold(" Open this URL in your browser to authorize:")),console.log(n.underline(` ${a}`)),console.log();try{await Z(a),console.log(n.dim(" (Browser opened automatically)"))}catch{}let h=Date.now()+m*1e3,g=3e3;for(;Date.now()<h;){await ie(g);let y=null;try{y=await fetch(`${i}/api/cli/auth/device-poll`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({deviceCode:s})})}catch{console.error(n.red(" Poll request failed (network error). Retrying..."));continue}if(!y.ok){console.error(n.red(" Poll request failed. Retrying..."));continue}let e=await y.json();if("token"in e){await z(e.token),console.log(),console.log(n.green(" Logged in successfully. Now operating in SaaS mode.")),await Promise.all([le(i,e.token),J(i,e.token)]);let{readWorkspace:d}=await import("./workspace-KGOQ67YV.js"),k=await d();k&&await ce(t,i,e.token,k);return}if("expired"in e){console.error(n.red(" Device code expired. Please try again.")),process.exitCode=P.AUTH;return}process.stdout.write(n.dim("."))}console.error(n.red(`
|
|
4
|
+
Login timed out. Please try again.`)),process.exitCode=P.AUTH}var fe="https://app.vibeflow.tools";function ue(){return process.env.VIBEFLOW_API_URL??fe}function me(t){return t<1024?`${t} B`:t<1024*1024?`${(t/1024).toFixed(1)} KB`:`${(t/(1024*1024)).toFixed(1)} MB`}function Y(t,i,o=24){let s=Math.round(t/i*o);return`[${"\u2588".repeat(s)+"\u2591".repeat(o-s)}]`}async function ge(t,i,o,s){let l=[];for(let{localId:c,remoteId:$}of s){let b=L(o,c);for(let p of b){let f=p.linkedPath??O(F(o,c),p.name);E(f)&&l.push({remoteId:$,name:p.name,filePath:f,size:p.size})}}if(l.length===0)return{uploaded:0,failed:0,failedFiles:[]};let a=l.length,m=l.reduce((c,$)=>c+$.size,0);console.log(u.cyan(`
|
|
5
|
+
Uploading ${a} file(s) (${me(m)})...`));let h=0,g=0,y=0,e=Date.now(),d=[];for(let c of l){let $=Math.round(h/a*100),b=Y(h,a),p=(Date.now()-e)/1e3,f=y/Math.max(p,.01),r=m-y,w=f>0?Math.ceil(r/f):0,I=w>60?`${Math.ceil(w/60)}m`:`${w}s`;process.stdout.write(`\r ${b} ${$}% ${c.name.slice(0,28)} ETA: ${I} `);try{let S=de(c.filePath),U=await fetch(`${t}/api/tasks/${encodeURIComponent(c.remoteId)}/files`,{method:"POST",headers:{Authorization:`Bearer ${i}`,"Content-Type":"application/octet-stream","x-filename":encodeURIComponent(c.name)},body:S});U.ok?(h++,y+=c.size):(g++,d.push({name:c.name,reason:`HTTP ${U.status}`}))}catch(S){g++,d.push({name:c.name,reason:S instanceof Error?S.message:String(S)})}}let k=Y(a,a);return process.stdout.write(`\r ${k} 100% Done${" ".repeat(40)}
|
|
6
|
+
`),{uploaded:h,failed:g,failedFiles:d}}async function He(t,i){let o=pe(t),s=N(o);if(s.length===0)return console.log(u.dim(" No local tasks found. Nothing to push.")),{dryRun:!!i.dryRun,tasks:0,files:0,steps:["No tasks"]};let l=0;for(let r of s){let w=L(o,r.id);l+=w.length}let a=i.workspace;if(!a){let r=await C().catch(()=>null);r&&(a=r.id)}let m=a?(await C().catch(()=>null))?.name??a:"(no board selected)";if(i.dryRun)return console.log(u.cyan(` Dry run: ${s.length} task(s), ${l} file(s), board: ${m}`)),{dryRun:!0,tasks:s.length,files:l,board:m,steps:["Dry run: no changes made"]};let h=await R();if(!h&&(console.log(u.yellow(" Not logged in. Starting login flow...")),await V(),h=await R(),!h)){console.error(u.red(" Login failed. Please run `vibeflow login` and try again.")),process.exitCode=P.AUTH;return}console.log(u.cyan(` Found ${s.length} local task(s). Pushing to SaaS...`));let g=ue(),y={tasks:s};a&&(y.workspaceId=a);let e;try{let r=await fetch(`${g}/api/cli/import`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${h}`},body:JSON.stringify(y)});if(!r.ok){let w=await r.json().catch(()=>({}));console.error(u.red(` Import failed: ${r.status} ${w.error??r.statusText}`)),process.exitCode=P.GENERAL;return}e=await r.json()}catch(r){let w=r instanceof Error?r.message:String(r);console.error(u.red(` Network error: ${w}`)),process.exitCode=P.GENERAL;return}console.log(u.green(` \u2713 Pushed ${e.imported} task(s) to SaaS`)),e.skipped&&e.skipped>0&&console.log(u.dim(` Skipped ${e.skipped} already-imported task(s).`)),console.log(u.dim(` Board: ${m}`));let d=e.idMap??{},k=s.filter(r=>r.id&&d[r.id]).map(r=>({localId:r.id,remoteId:d[r.id]})),{uploaded:c,failed:$,failedFiles:b}=await ge(g,h,o,k);if(c>0&&console.log(u.green(` \u2713 Uploaded ${c} file(s) to storage`)),$>0){console.log(u.yellow(` \u26A0 ${$} file(s) failed to upload`));for(let r of b)console.log(u.dim(` - ${r.name}: ${r.reason}`))}if(i.keepLocalFiles){console.log(u.dim(" Local task files kept (--keep-local-files)."));return}let p=0,f=0;for(let r of s){try{K(r.filePath),p++}catch{}try{let w=F(o,r.id);if(E(w)){let{rmSync:I}=await import("fs");I(w,{recursive:!0,force:!0}),f++}}catch{}try{let w=O(o,T,B),I=O(w,`${r.id}.png`);E(I)&&(K(I),f++)}catch{}}return console.log(u.dim(` Deleted ${p} local task file(s).`)),f>0&&console.log(u.dim(` Cleaned ${f} orphaned asset dir(s).`)),{dryRun:!1,tasks:p,files:l,board:m,steps:["Push completed"]}}export{R as a,we as b,Ce as c,V as d,me as e,Y as f,He as g};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{c as st}from"./chunk-EAYTSVCN.js";import{a as U}from"./chunk-BZDBSBRV.js";import{a as b,e as R,g as I}from"./chunk-PWP474PZ.js";import{d as tt,h as et,o as ot,s as nt,v as rt}from"./chunk-2CI62NHU.js";import c from"chalk";import{resolve as wt,join as N}from"path";import{readFileSync as z}from"fs";import{statSync as Nt,readdirSync as Tt,unlinkSync as At}from"fs";import M from"crypto";var vt="vibeflow-auth-v1",xt=1440*60*1e3;function it(t){return M.scryptSync(t,vt,32)}function jt(t,e){let o=it(e),n=M.randomBytes(16),r=M.createCipheriv("aes-256-gcm",o,n),s=JSON.stringify(t),i=Buffer.concat([r.update(s),r.final()]),y=r.getAuthTag();return{version:1,createdAt:new Date().toISOString(),expiresAt:new Date(Date.now()+xt).toISOString(),iv:n.toString("hex"),tag:y.toString("hex"),data:i.toString("hex")}}function at(t,e){if(new Date(t.expiresAt)<new Date)return null;try{let o=it(e),n=M.createDecipheriv("aes-256-gcm",o,Buffer.from(t.iv,"hex"));n.setAuthTag(Buffer.from(t.tag,"hex"));let r=Buffer.concat([n.update(Buffer.from(t.data,"hex")),n.final()]);return JSON.parse(r.toString())}catch{return null}}function bt(t,e){return Math.abs(t.x-e.x)>2||Math.abs(t.y-e.y)>2||Math.abs(t.width-e.width)>2||Math.abs(t.height-e.height)>2}function H(t,e){let o=!!e.outerHTML,n=t.outerHTML!==e.outerHTML,r={},s=new Set([...Object.keys(t.computedStyles),...Object.keys(e.computedStyles)]);for(let l of s){let E=t.computedStyles[l],x=e.computedStyles[l];E!==x&&(r[l]=[E??"",x??""])}let i=bt(t.position.boundingBox,e.position.boundingBox),y=new Set(t.consoleErrors),g=e.consoleErrors.filter(l=>!y.has(l));return{selectorResolves:o,htmlChanged:n,stylesChanged:r,positionChanged:i,newConsoleErrors:g}}function ct(t,e){let o=[];if(t.selectorResolves){t.htmlChanged&&o.push("HTML changed");let n=Object.keys(t.stylesChanged).length;if(n>0){let r=Object.keys(t.stylesChanged).slice(0,5).join(", "),s=n>5?` (+${n-5} more)`:"";o.push(`${n} style property change(s): ${r}${s}`)}t.positionChanged&&o.push("position shifted"),t.newConsoleErrors.length>0&&o.push(`${t.newConsoleErrors.length} new console error(s)`),o.length===0&&o.push("no structural changes detected")}else o.push(`Selector "${e}" no longer resolves`);return o.join("; ")}import{readFileSync as lt,writeFileSync as Ht,mkdirSync as Jt,existsSync as ft}from"fs";import{join as gt,basename as Rt}from"path";import{execSync as Ct}from"child_process";var G={port:3700};function Ot(t){return gt(t,tt,et)}function ut(t){let e=Ot(t);if(!ft(e))return{...G};let o=lt(e,"utf-8"),n;try{n=JSON.parse(o)}catch{return{...G}}return{...G,...n}}function Wt(t){let e=gt(t,"package.json");if(ft(e))try{let o=JSON.parse(lt(e,"utf-8"));if(o.name)return o.name}catch{}return Rt(t)}function zt(t){try{return Ct("git branch --show-current",{cwd:t,encoding:"utf-8",stdio:["pipe","pipe","pipe"]}).trim()||null}catch{return null}}var W=["display","visibility","opacity","position","overflow","overflow-x","overflow-y","width","min-width","max-width","height","min-height","max-height","margin","margin-top","margin-right","margin-bottom","margin-left","padding","padding-top","padding-right","padding-bottom","padding-left","border","border-width","border-style","border-color","border-radius","background-color","background-image","color","font-size","font-weight","font-style","font-family","line-height","letter-spacing","text-align","text-decoration","text-overflow","white-space","flex-direction","flex-wrap","flex","justify-content","align-items","gap","row-gap","column-gap","grid-template-columns","grid-template-rows","z-index","box-shadow","transform","transition","cursor","user-select","pointer-events","top","left","right","bottom"];function dt(t){let e=[],o=t;for(;o&&o!==document.documentElement;){let n=o.parentElement;if(!n)break;let r=Array.from(n.children).indexOf(o);e.unshift(r),o=n}return e.join("/")}function ht(t){let e=t.tagName.toLowerCase(),o=t.getAttribute("data-task-id");if(o)return`${e}.task-card[data-task-id=${o}]`;let n=t.getAttribute("data-status");if(n&&t.classList.contains("column-scroll"))return`${e}.column-scroll[data-status=${n}]`;let r=t.getAttribute("data-column-id");if(r)return`${e}[data-column-id=${r}]`;let s=Array.from(t.classList).slice(0,2),i=e;s.length&&(i+="."+s.join("."));let y=t.parentElement;if(y){let g=Array.from(y.children).filter(l=>l.tagName===t.tagName);if(g.length>1){let l=g.indexOf(t)+1;i+=`:nth-of-type(${l})`}}return i}function mt(t,e=W){let o=window.getComputedStyle(t),n={};for(let r of e)n[r]=o.getPropertyValue(r);return n}function pt(t,e=200){return t.replace(/\s+/g," ").trim().slice(0,e)}function yt(t){let e=new Map;for(let o of t){let n=o.tagName.toLowerCase(),r=Array.from(o.classList).slice(0,2).join("."),s=r?`${n}.${r}`:n;e.set(s,(e.get(s)||0)+1)}return Array.from(e.entries()).map(([o,n])=>`${o} \xD7${n}`).slice(0,10)}var p=class extends Error{constructor(o,n,r){super(n);this.code=o;this.suggestion=r;this.name="VerifyError"}code;suggestion};async function Pt(){try{return await import("playwright")}catch{throw new p("E_PLAYWRIGHT_MISSING","Playwright is not installed.","Run: npx playwright install chromium")}}async function kt(t,e,o={}){let n=wt(t),r=ot(n,e);if(!r)throw new p("E_NOT_FOUND",`Task not found: ${e}`,"Run 'vibeflow tasks' to see available task IDs.");let s=nt(r);if(!s)throw new p("E_NOT_FOUND",`Task not found: ${e}`);let i,y=s.baselineElementFile||"baseline-element.json",g=I(n,e,y);if(g)try{i=JSON.parse(z(g,"utf-8"))}catch{}if(!i&&s.baseline&&(i=s.baseline),!i)throw new p("E_NO_BASELINE","Task has no baseline. Re-annotate to capture one.");let l=[],E={},x={};if(s.authStateEnc&&s.author)try{let m=JSON.parse(s.authStateEnc),f=at(m,s.author);if(!f)throw new p("E_AUTH_EXPIRED","Auth state expired. Re-annotate to capture fresh cookies.");l=f.cookies,E=f.localStorage,x=f.sessionStorage}catch(m){throw m instanceof p?m:new p("E_AUTH_CORRUPT","Auth state corrupted. Re-annotate.")}let v=o.url??s.url;if(!v)throw new p("E_NO_URL","Task has no URL. Cannot verify without a target URL.","Re-annotate the element with a URL, or use --url to override.");let C=ut(n).port??3700,A=v.startsWith("http")?v:`http://localhost:${C}${v.startsWith("/")?v:"/"+v}`,S=s.cssSelector??s.selector;if(!S||S==="/")throw new p("E_NO_SELECTOR","Task has no CSS selector. Cannot verify without a selector.");let j=await Pt(),$;try{$=await j.chromium.launch({headless:!0})}catch(m){let f=m instanceof Error?m.message:String(m);throw f.includes("not found")||f.includes("not installed")||f.includes("Executable doesn't exist")?new p("E_PLAYWRIGHT_MISSING","Chromium is not installed.","Run: npx playwright install chromium"):new p("E_PLAYWRIGHT_CRASH",`Verification failed: ${f}`,"Try again.")}let k;try{let m=i.position?.viewport;k=await $.newContext({viewport:m?{width:m.width,height:m.height}:void 0,deviceScaleFactor:m?.dpr,userAgent:i.browser||void 0}),l.length>0&&await k.addCookies(l.map(a=>({name:a.name,value:a.value,domain:a.domain,path:a.path,expires:a.expires,httpOnly:a.httpOnly,secure:a.secure,sameSite:a.sameSite})));let f=await k.newPage(),T=[];f.on("console",a=>{a.type()==="error"&&T.push(a.text())}),f.on("pageerror",a=>{T.push(a.message)});try{await f.goto(A,{waitUntil:"networkidle",timeout:15e3})}catch(a){let u=a instanceof Error?a.message:String(a);throw u.includes("ERR_CONNECTION_REFUSED")||u.includes("ECONNREFUSED")?new p("E_APP_NOT_RUNNING",`Cannot connect to ${A}. Is the dev server running?`):new p("E_NAVIGATION_FAILED",`Failed to navigate to ${A}: ${u}`)}try{Object.keys(E).length>0&&await f.evaluate(a=>{for(let[u,d]of Object.entries(a))window.localStorage.setItem(u,d)},E),Object.keys(x).length>0&&await f.evaluate(a=>{for(let[u,d]of Object.entries(a))window.sessionStorage.setItem(u,d)},x)}catch{}let P=0;try{await f.waitForSelector(S,{timeout:1e4}),P=await f.locator(S).count()}catch{let a={outerHTML:"",computedStyles:{},selector:S,position:i.position,browser:i.browser,consoleErrors:T,capturedAt:new Date().toISOString()},u=H(i,a);return K(s.id,s.description,i,a,u,[],S)}if(P>1){let a={outerHTML:"",computedStyles:{},selector:S,position:i.position,browser:i.browser,consoleErrors:T,capturedAt:new Date().toISOString()},u=H(i,a),d=`Selector matches ${P} elements. The selector may need to be more specific.`;return K(s.id,s.description,i,a,u,[],S,d)}let L=await Lt(f,S,i,T),_=null,B=s.baselineFile||"baseline-page.json",F=I(n,e,B);if(F)try{_=JSON.parse(z(F,"utf-8"))}catch{}let h=H(i,L),O=await _t(n,e,i,L,h,T,f,S);if(_&&_.elements){let a=I(n,e,"verify-all-styles.json");if(a)try{let u=JSON.parse(z(a,"utf-8"));if(u&&u.elements){let d={},St=Object.entries(u.elements);for(let[Y,X]of St){let J=_.elements[Y]?.after;if(J){X.baseline=J;let V={};for(let[q,Q]of Object.entries(X.after)){let Z=J[q];Z!==Q&&(V[q]=[Z,Q])}Object.keys(V).length>0&&(d[Y]=V)}}R(n,e,"verify-all-styles.json",Buffer.from(JSON.stringify(u,null,2)));let Et=JSON.stringify({totalChanged:Object.keys(d).length,elements:d},null,2);R(n,e,"verify-page-diff.json",Buffer.from(Et)),O.push(N(b(n,e),"verify-page-diff.json"))}}catch{}}let D=K(s.id,s.description,i,L,h,O,S);return rt(n,e,{verified:D.ok}),D}finally{await k?.close(),await $?.close()}}async function Lt(t,e,o,n){let r=t.locator(e).first(),s=await r.evaluate(l=>l.outerHTML).catch(()=>""),i=await r.evaluate(l=>{let E=window.getComputedStyle(l),x={};for(let v=0;v<E.length;v++){let w=E[v];x[w]=E.getPropertyValue(w)}return x}).catch(()=>({})),g={boundingBox:await r.boundingBox().catch(()=>null)??o.position.boundingBox,scrollPosition:await t.evaluate(()=>({x:window.scrollX,y:window.scrollY})),viewport:await t.evaluate(()=>({width:window.innerWidth,height:window.innerHeight,dpr:window.devicePixelRatio})),stackingContext:o.position.stackingContext};return{outerHTML:s,computedStyles:i,selector:e,position:g,browser:o.browser,consoleErrors:n,capturedAt:new Date().toISOString()}}async function _t(t,e,o,n,r,s,i,y){let g=[],l=b(t,e);try{let w=Tt(l);for(let C of w)(C.startsWith("verify-")||C==="baseline.json")&&At(N(l,C))}catch{}let E=JSON.stringify(n,null,2);R(t,e,"verify-after.json",Buffer.from(E)),g.push(N(b(t,e),"verify-after.json"));let x=JSON.stringify(r,null,2);R(t,e,"verify-diff.json",Buffer.from(x)),g.push(N(b(t,e),"verify-diff.json"));let v=s.length>0?s.join(`
|
|
3
|
+
`):"(no console errors)";if(R(t,e,"verify-console.txt",Buffer.from(v)),g.push(N(b(t,e),"verify-console.txt")),i){try{let w=await i.content();R(t,e,"verify-page.html",Buffer.from(w)),g.push(N(b(t,e),"verify-page.html"))}catch{}try{let C=await i.evaluate(({buildKeyFn:A,buildSelectorFn:S,filterStylesFn:j,childSignatureFn:$,normalizeTextFn:k,styles:m,maxElements:f})=>{let T={},P=0,L=!1,_=document.createTreeWalker(document.body,NodeFilter.SHOW_ELEMENT,{acceptNode(h){if(P>=f)return NodeFilter.FILTER_REJECT;let O=h;return O.classList.length>0||O.hasAttribute("data-task-id")||O.hasAttribute("data-status")||O.hasAttribute("data-column-id")?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_SKIP}}),B=[],F;for(;F=_.nextNode();)B.push(F);for(let h of B){if(P>=f){L=!0;break}let O=A(h),D=Array.from(h.children),a={};for(let d of Array.from(h.attributes))d.name.startsWith("data-")&&(a[d.name.slice(5)]=d.value);let u={x:0,y:0,width:0,height:0};try{let d=h.getBoundingClientRect();u={x:Math.round(d.x),y:Math.round(d.y),width:Math.round(d.width),height:Math.round(d.height)}}catch{}T[O]={key:O,selector:S(h),tag:h.tagName.toLowerCase(),classes:Array.from(h.classList),dataAttrs:a,parentKey:h.parentElement?A(h.parentElement):"",childCount:D.length,childSignature:$(D),text:k(h.textContent??""),position:u,baseline:j(h,m),after:j(h,m)},P++}return{version:1,capturedAt:new Date().toISOString(),truncated:L,elements:T}},{buildKeyFn:dt,buildSelectorFn:ht,filterStylesFn:mt,childSignatureFn:yt,normalizeTextFn:pt,styles:W,maxElements:1e3});if(C){let A=JSON.stringify(C,null,2);R(t,e,"verify-all-styles.json",Buffer.from(A)),g.push(N(b(t,e),"verify-all-styles.json"))}}catch{}try{let w=await i.screenshot({fullPage:!1});R(t,e,"verify-screenshot.png",w),g.push(N(b(t,e),"verify-screenshot.png"))}catch{}if(y)try{let w=await i.locator(y).first().evaluate(C=>C.outerHTML);R(t,e,"verify-element.html",Buffer.from(w)),g.push(N(b(t,e),"verify-element.html"))}catch{}}if(o){let w=JSON.stringify(o,null,2);R(t,e,"baseline.json",Buffer.from(w)),g.push(N(b(t,e),"baseline.json"))}return g}function K(t,e,o,n,r,s,i,y){let g=r.selectorResolves&&r.newConsoleErrors.length===0,l=y??ct(r,i);return{taskId:t,ok:g,taskDescription:e,baseline:{selector:o.selector,url:"",capturedAt:o.capturedAt,snapshot:o},after:{snapshot:n,consoleErrors:n.consoleErrors},diff:r,evidenceFiles:s,verdict:l}}async function ce(t,e,o){let n=wt(t);try{let r=await kt(n,e,o);await $t(n,e,r),o.json?console.log(JSON.stringify(r,null,2)):Ft(r)}catch(r){if(r instanceof p)o.json?process.stderr.write(JSON.stringify({ok:!1,error:{code:r.code,message:r.message,suggestion:r.suggestion}})+`
|
|
4
|
+
`):(process.stderr.write(c.red(`\u2717 ${r.message}
|
|
5
|
+
`)),r.suggestion&&process.stderr.write(c.dim(` ${r.suggestion}
|
|
6
|
+
`))),process.exitCode=U.GENERAL;else{let s=r instanceof Error?r.message:String(r);o.json?process.stderr.write(JSON.stringify({ok:!1,error:{code:"E_UNKNOWN",message:s}})+`
|
|
7
|
+
`):process.stderr.write(c.red(`\u2717 Verification failed: ${s}
|
|
8
|
+
`)),process.exitCode=U.GENERAL}}}async function $t(t,e,o){let n=`**Verification ${o.ok?"\u2705 passed":"\u26A0\uFE0F issues detected"}**
|
|
9
|
+
|
|
10
|
+
${o.verdict}`;st(t,e,"agent",n,void 0,"system")}function Ft(t){let e=t.ok?c.green("\u2705"):c.yellow("\u26A0\uFE0F");console.log(),console.log(` ${e} Evidences collected for task ${t.taskId}`),console.log(c.dim("\u2500".repeat(60))),console.log(c.dim(` Verdict: ${t.verdict}`)),console.log();let o=t.diff;if(o.selectorResolves){o.htmlChanged?console.log(c.cyan(" \u2713 HTML changed")):console.log(c.dim(" \xB7 HTML unchanged"));let n=Object.keys(o.stylesChanged).length;if(n>0){console.log(c.cyan(` \u2713 ${n} style property change(s):`));for(let[r,[s,i]]of Object.entries(o.stylesChanged).slice(0,8))console.log(c.dim(` ${r}: ${s} \u2192 ${i}`));n>8&&console.log(c.dim(` ... and ${n-8} more`))}if(o.positionChanged&&console.log(c.cyan(" \u2713 Position shifted")),o.newConsoleErrors.length>0){console.log(c.yellow(` \u26A0 ${o.newConsoleErrors.length} new console error(s):`));for(let r of o.newConsoleErrors.slice(0,3))console.log(c.dim(` ${r.slice(0,120)}`))}}else console.log(c.red(" \u26A0 Target element not found \u2014 the fix may have renamed/removed it."));if(t.evidenceFiles.length>0){console.log(c.cyan(` Evidence files (${t.evidenceFiles.length}):`));for(let n of t.evidenceFiles){let r=n.split("/").pop()??n;try{let i=(Nt(n).size/1024).toFixed(1);console.log(c.dim(` ${r} (${i} KB)`))}catch{console.log(c.dim(` ${r}`))}}}console.log(),console.log(c.cyan(" Explore evidence (agent tools):")),console.log(c.dim(` vibeflow verify style_query ${t.taskId} <property>`)),console.log(c.dim(` vibeflow verify style_diff ${t.taskId} [--filter <pattern>]`)),console.log(c.dim(` vibeflow verify element_info ${t.taskId}`)),console.log(c.dim(` vibeflow verify html_diff ${t.taskId}`)),console.log(),console.log(c.cyan(" Review evidences:")),console.log(c.dim(" 1. Check if the fix is confirmed (styles match expectations)")),console.log(c.dim(` 2. If confirmed \u2192 vibeflow tasks --edit ${t.taskId} --set-status review --comment "Verified: <what you confirmed>"`)),console.log(c.dim(" 3. If not sure \u2192 leave a comment explaining uncertainty")),console.log(c.dim(` 4. If wrong \u2192 vibeflow tasks --edit ${t.taskId} --set-status in-progress`)),console.log(),t.ok||(console.log(c.yellow(" \u26A0 Verification failed \u2014 move to in-progress and fix:")),console.log(c.dim(` vibeflow tasks --edit ${t.taskId} --set-status in-progress`)))}export{jt as a,ut as b,Wt as c,zt as d,p as e,kt as f,ce as g,$t as h};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{a as w}from"./chunk-BZDBSBRV.js";import{a as B,d as U}from"./chunk-PWP474PZ.js";import{d as P,e as E,u as N}from"./chunk-2CI62NHU.js";import{a as A,b as _}from"./chunk-KJ5LYG6A.js";import g from"chalk";import{unlinkSync as ye,readFileSync as ke,existsSync as we}from"fs";import{resolve as Se,join as $e}from"path";import I from"fs/promises";import oe from"os";import F from"path";var re=F.join(oe.homedir(),".vibeflow"),T=F.join(re,"token");async function b(){try{return(await I.readFile(T,"utf8")).trim()||null}catch{return null}}async function j(e){await I.mkdir(F.dirname(T),{recursive:!0}),await I.writeFile(T,e,{mode:384})}async function Ae(){try{await I.unlink(T)}catch{}}import i from"chalk";import ie from"open";import V from"readline";import ae from"@inquirer/select";import{existsSync as L,readFileSync as D,writeFileSync as W,mkdirSync as M,renameSync as z}from"fs";import{join as x}from"path";import G from"os";var ne="settings.json";function H(){return x(G.homedir(),".vibeflow","settings.json")}function J(e){return x(e,P,ne)}function O(){try{return JSON.parse(D(H(),"utf-8"))}catch{return{}}}function q(e){let r=x(G.homedir(),".vibeflow");L(r)||M(r,{recursive:!0});let o=H(),n=o+".tmp";W(n,JSON.stringify(e,null,2),"utf-8"),z(n,o)}function se(e){let r=O(),o=J(e);if(!L(o))return r;try{let n=JSON.parse(D(o,"utf-8"));return{...r,...n}}catch{return r}}function Ne(e,r){let o=x(e,P);L(o)||M(o,{recursive:!0});let n=J(e),c={...se(e),...r},p=n+".tmp";return W(p,JSON.stringify(c,null,2),"utf-8"),z(p,n),c}import{existsSync as ce,readdirSync as K,readFileSync as le}from"fs";import{join as R,resolve as de}from"path";var pe="https://app.vibeflow.tools",fe=300*1e3;function Y(){return process.env.VIBEFLOW_API_URL??pe}async function ue(e){return new Promise(r=>setTimeout(r,e))}async function ge(e){return e.length===1?(console.log(i.dim(` Using board: ${e[0].icon??""} ${e[0].name}`)),e[0]):await ae({message:"Select a board to use:",choices:e.map(o=>({name:`${o.icon??""} ${o.name}`,value:o}))})}async function Q(e,r){try{let o=await fetch(`${e}/api/cli/settings`,{headers:{Authorization:`Bearer ${r}`}});if(!o.ok)return;let n=await o.json();n.settings&&Object.keys(n.settings).length>0&&q({...n.settings,_settingsRefreshedAt:Date.now()})}catch{}}async function Ve(){try{let e=await b();if(!e)return;let o=O()._settingsRefreshedAt??0;if(Date.now()-o<fe)return;let n=Y();Q(n,e)}catch{}}async function me(e,r){try{let[o,n]=await Promise.all([fetch(`${e}/api/cli/workspaces`,{headers:{Authorization:`Bearer ${r}`}}),fetch(`${e}/api/cli/profile`,{headers:{Authorization:`Bearer ${r}`}})]);if(!o.ok)return;let a=await o.json();if(!a.workspaces?.length){console.log(i.yellow(" No boards found. Create a board in the web app first."));return}let c=n.ok?await n.json():null,p=await ge(a.workspaces);await _({...p,email:c?.email??null}),console.log(i.green(` Board set: ${p.icon??""} ${p.name}`)),console.log(i.dim(` Open at: ${p.url}`))}catch{}}async function he(e,r,o,n){let a=R(e,P,E);if(!ce(a))return;let c=[];try{for(let s of K(a,{withFileTypes:!0}))if(s.isDirectory()){let f=R(a,s.name);for(let m of K(f).filter(d=>d.endsWith(".json")))c.push(R(f,m))}else s.isFile()&&s.name.endsWith(".json")&&c.push(R(a,s.name))}catch{return}if(c.length===0)return;let p=c.flatMap(s=>{try{return[JSON.parse(le(s,"utf-8"))]}catch{return[]}}),y=new Set;try{let s=await fetch(`${r}/api/cli/tasks?boardId=${n.id}`,{headers:{Authorization:`Bearer ${o}`}}).catch(()=>null);if(s?.ok){let f=await s.json();y=new Set((f.tasks??[]).flatMap(m=>[m.id,...m.cliId?[m.cliId]:[]]))}}catch{}let l=p.filter(s=>!y.has(s.id));if(l.length===0)return;console.log(),console.log(i.bold(` ${l.length} local task${l.length!==1?"s are":" is"} not yet synced to the board:`));let h=l.slice(0,10);for(let s of h){let f=s.status?i.dim(` [${s.status}]`):"";console.log(i.dim(` \u2022 ${s.title??s.id}${f}`))}return l.length>10&&console.log(i.dim(` \u2026 and ${l.length-10} more`)),new Promise(s=>{let f=V.createInterface({input:process.stdin,output:process.stdout});f.question(i.cyan(`
|
|
3
|
+
Push them to your board now? [Y/n]: `),async m=>{if(f.close(),m.trim().toLowerCase()==="n"){console.log(i.dim(" Skipped. You can push later with: vibeflow push")),s();return}let d=V.createInterface({input:process.stdin,output:process.stdout});d.question(i.cyan(" Delete local task files after push? [y/N]: "),async k=>{d.close();let S=k.trim().toLowerCase()!=="y";try{let t=await fetch(`${r}/api/cli/import`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${o}`},body:JSON.stringify({boardId:n.id,tasks:l,keepLocalFiles:S})}),u={};try{u=await t.json()}catch{}t.ok?(console.log(i.green(` \u2713 Pushed ${u.imported??0} task${(u.imported??0)!==1?"s":""} to ${n.name}`)),(u.skipped??0)>0&&console.log(i.dim(` (${u.skipped} already existed and were skipped)`))):console.log(i.red(` Push failed: ${u.error??`HTTP ${t.status}`}`))}catch(t){console.log(i.red(` Push failed: ${t instanceof Error?t.message:"Network error"}`))}s()})})})}async function X(e=de(".")){let r=Y();console.log(i.cyan(" Initiating device login flow..."));let o;try{o=await fetch(`${r}/api/cli/auth/device-init`,{method:"POST",headers:{"Content-Type":"application/json"}})}catch(h){console.error(i.red(` Failed to connect to login server: ${h instanceof Error?h.message:"Network error"}`)),process.exitCode=w.AUTH;return}if(!o.ok){console.error(i.red(` Failed to initiate login: ${o.status} ${o.statusText}`)),process.exitCode=w.AUTH;return}let{deviceCode:n,userCode:a,verificationUrl:c,expiresIn:p}=await o.json();console.log(),console.log(i.bold(" Open this URL in your browser to authorize:")),console.log(i.underline(` ${c}`)),console.log();try{await ie(c),console.log(i.dim(" (Browser opened automatically)"))}catch{}let y=Date.now()+p*1e3,l=3e3;for(;Date.now()<y;){await ue(l);let h=null;try{h=await fetch(`${r}/api/cli/auth/device-poll`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({deviceCode:n})})}catch{console.error(i.red(" Poll request failed (network error). Retrying..."));continue}if(!h.ok){console.error(i.red(" Poll request failed. Retrying..."));continue}let s=await h.json();if("token"in s){await j(s.token),console.log(),console.log(i.green(" Logged in successfully. Now operating in SaaS mode.")),await Promise.all([me(r,s.token),Q(r,s.token)]);let{readWorkspace:f}=await import("./workspace-AJAVENKA.js"),m=await f();m&&await he(e,r,s.token,m);return}if("expired"in s){console.error(i.red(" Device code expired. Please try again.")),process.exitCode=w.AUTH;return}process.stdout.write(i.dim("."))}console.error(i.red(`
|
|
4
|
+
Login timed out. Please try again.`)),process.exitCode=w.AUTH}var Pe="https://app.vibeflow.tools";function be(){return process.env.VIBEFLOW_API_URL??Pe}function Ie(e){return e<1024?`${e} B`:e<1024*1024?`${(e/1024).toFixed(1)} KB`:`${(e/(1024*1024)).toFixed(1)} MB`}function Z(e,r,o=24){let n=Math.round(e/r*o);return`[${"\u2588".repeat(n)+"\u2591".repeat(o-n)}]`}async function Te(e,r,o,n){let a=[];for(let{localId:d,remoteId:k}of n){let S=U(o,d);for(let t of S){let u=t.linkedPath??$e(B(o,d),t.name);we(u)&&a.push({remoteId:k,name:t.name,filePath:u,size:t.size})}}if(a.length===0)return{uploaded:0,failed:0,failedFiles:[]};let c=a.length,p=a.reduce((d,k)=>d+k.size,0);console.log(g.cyan(`
|
|
5
|
+
Uploading ${c} file(s) (${Ie(p)})...`));let y=0,l=0,h=0,s=Date.now(),f=[];for(let d of a){let k=Math.round(y/c*100),S=Z(y,c),t=(Date.now()-s)/1e3,u=h/Math.max(t,.01),ee=p-h,v=u>0?Math.ceil(ee/u):0,te=v>60?`${Math.ceil(v/60)}m`:`${v}s`;process.stdout.write(`\r ${S} ${k}% ${d.name.slice(0,28)} ETA: ${te} `);try{let $=ke(d.filePath),C=await fetch(`${e}/api/tasks/${encodeURIComponent(d.remoteId)}/files`,{method:"POST",headers:{Authorization:`Bearer ${r}`,"Content-Type":"application/octet-stream","x-filename":encodeURIComponent(d.name)},body:$});C.ok?(y++,h+=d.size):(l++,f.push({name:d.name,reason:`HTTP ${C.status}`}))}catch($){l++,f.push({name:d.name,reason:$ instanceof Error?$.message:String($)})}}let m=Z(c,c);return process.stdout.write(`\r ${m} 100% Done${" ".repeat(40)}
|
|
6
|
+
`),{uploaded:y,failed:l,failedFiles:f}}async function it(e,r){let o=Se(e),n=await b();if(!n&&(console.log(g.yellow(" Not logged in. Starting login flow...")),await X(),n=await b(),!n)){console.error(g.red(" Login failed. Please run `vibeflow login` and try again.")),process.exitCode=w.AUTH;return}let a=N(o);if(a.length===0){console.log(g.dim(" No local tasks found. Nothing to push."));return}console.log(g.cyan(` Found ${a.length} local task(s). Pushing to SaaS...`));let c=be(),p=r.workspace;if(!p){let t=await A();t&&(p=t.id,console.log(g.dim(` Using board: ${t.icon??""} ${t.name}`)))}let y={tasks:a};p&&(y.workspaceId=p);let l;try{let t=await fetch(`${c}/api/cli/import`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${n}`},body:JSON.stringify(y)});if(!t.ok){let u=await t.json().catch(()=>({}));console.error(g.red(` Import failed: ${t.status} ${u.error??t.statusText}`)),process.exitCode=w.GENERAL;return}l=await t.json()}catch(t){let u=t instanceof Error?t.message:String(t);console.error(g.red(` Network error: ${u}`)),process.exitCode=w.GENERAL;return}console.log(g.green(` \u2713 Pushed ${l.imported} task(s) to SaaS`)),l.skipped&&l.skipped>0&&console.log(g.dim(` Skipped ${l.skipped} already-imported task(s).`));let h=(await A())?.name??l.workspaceId;console.log(g.dim(` Board: ${h}`));let s=l.idMap??{},f=a.filter(t=>t.id&&s[t.id]).map(t=>({localId:t.id,remoteId:s[t.id]})),{uploaded:m,failed:d,failedFiles:k}=await Te(c,n,o,f);if(m>0&&console.log(g.green(` \u2713 Uploaded ${m} file(s) to storage`)),d>0){console.log(g.yellow(` \u26A0 ${d} file(s) failed to upload`));for(let t of k)console.log(g.dim(` - ${t.name}: ${t.reason}`))}if(r.keepLocalFiles){console.log(g.dim(" Local task files kept (--keep-local-files)."));return}let S=0;for(let t of a)try{ye(t.filePath),S++}catch{}console.log(g.dim(` Deleted ${S} local task file(s).`))}export{b as a,Ae as b,se as c,Ne as d,Ve as e,X as f,Ie as g,Z as h,it as i};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{g as S,l as A,o as R}from"./chunk-Q7MEW67Z.js";import{d as y,f as k}from"./chunk-XHZXHACL.js";import{readdirSync as I,writeFileSync as $,readFileSync as w,unlinkSync as g,statSync as d,mkdirSync as C,existsSync as l}from"fs";import{join as f,basename as m,extname as L}from"path";var E=255,T=50*1024*1024,v=new Set([".png",".jpg",".jpeg",".gif",".webp",".pdf",".txt",".md",".json",".csv",".svg",".mp4",".mov",".zip"]);function N(e){if(!e||e.length===0||e.length>E||e.startsWith(".")||e.includes("/")||e.includes("\\")||e==="."||e===".."||e.includes("..")||e.includes("\0"))return!1;for(let t=0;t<e.length;t++)if(e.charCodeAt(t)<32)return!1;return!0}function O(e){let t=L(e).toLowerCase();return v.has(t)}function W(e,t){return N(e)?O(e)?t!==void 0&&t>T?{valid:!1,errorCode:"VALIDATION",errorMessage:`File too large: ${t} bytes (max ${T})`}:{valid:!0}:{valid:!1,errorCode:"UNSUPPORTED_FILE_TYPE",errorMessage:`Unsupported file type "${L(e).toLowerCase()}". Allowed: ${[...v].join(", ")}`}:{valid:!1,errorCode:"INVALID_FILENAME",errorMessage:`Invalid filename: empty, too long (>${E} chars), path separator, control character, or hidden file`}}var h=".linked.json";function u(e,t){return f(e,y,k,t)}function _(e,t){C(u(e,t),{recursive:!0})}function b(e,t){let r=f(u(e,t),h);if(!l(r))return[];try{return JSON.parse(w(r,"utf-8"))}catch{return[]}}function p(e,t){let r=S(e,t),s=r?A(r):null;return!s?.files||s.files.length===0?[]:s.files}function F(e,t){return p(e,t)}function P(e,t,r){R(e,t,{files:r})}function x(e,t){let r=p(e,t),s=f(u(e,t),h);if(!l(s))return r;let i=b(e,t);if(i.length===0){try{g(s)}catch{}return r}let n=r.slice(),a=!1;for(let o of i)n.find(c=>c.linkedPath===o.path||c.name===o.name&&c.linkedPath)||(n.push({name:o.name,linkedPath:o.path,addedAt:new Date().toISOString()}),a=!0);a&&P(e,t,n);try{g(s)}catch{}return n}function M(e,t){let r=u(e,t),s=F(e,t),i=new Map;for(let n of s){if(n.linkedPath&&l(n.linkedPath)){let o=d(n.linkedPath);i.set(n.name,{name:n.name,size:o.size,url:`/api/tasks/${t}/files/${encodeURIComponent(n.name)}`,linkedPath:n.linkedPath,createdAt:o.mtime.toISOString()});continue}let a=f(r,n.name);if(l(a)){let o=d(a);i.set(n.name,{name:n.name,size:o.size,url:`/api/tasks/${t}/files/${encodeURIComponent(n.name)}`,createdAt:o.mtime.toISOString()})}}if(l(r))for(let n of I(r,{withFileTypes:!0})){if(!n.isFile()||n.name===h||i.has(n.name))continue;let a=f(r,n.name),o=d(a);i.set(n.name,{name:n.name,size:o.size,url:`/api/tasks/${t}/files/${encodeURIComponent(n.name)}`,createdAt:o.mtime.toISOString()})}return Array.from(i.values())}function B(e,t,r,s){let i=m(r);_(e,t),$(f(u(e,t),i),s);let n=x(e,t);return n.find(a=>a.name===i&&!a.linkedPath)||(n.push({name:i,addedAt:new Date().toISOString()}),P(e,t,n)),{name:i,size:s.length,url:`/api/tasks/${t}/files/${encodeURIComponent(i)}`}}function G(e,t,r){let s=m(r),i=x(e,t),n=i.findIndex(o=>o.name===s);if(n!==-1){let[o]=i.splice(n,1);if(P(e,t,i),o&&!o.linkedPath){let c=f(u(e,t),s);l(c)&&g(c)}return!0}let a=f(u(e,t),s);return l(a)?(g(a),!0):!1}function H(e,t,r){let s=m(r),i=F(e,t).find(a=>a.name===s&&a.linkedPath);if(i?.linkedPath&&l(i.linkedPath))return i.linkedPath;let n=f(u(e,t),s);return l(n)?n:null}function J(e,t){return M(e,t).length}async function K(e){let{listTasks:t}=await import("./tasks-5X5AAK2N.js"),r=t(e),s=0;for(let i of r)try{let n=F(e,i.id);x(e,i.id),p(e,i.id).length!==n.length&&s++}catch{}return s}export{E as a,T as b,v as c,N as d,O as e,W as f,u as g,_ as h,F as i,M as j,B as k,G as l,H as m,J as n,K as o};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{readdirSync as W,writeFileSync as J,readFileSync as G,unlinkSync as v,statSync as R,mkdirSync as Y,existsSync as l}from"fs";import{join as f,basename as F}from"path";var g=".vibeflow",C="tasks",S="tasks/files",x="tasks/screenshots",te="config.json";import{existsSync as m,readFileSync as O,writeFileSync as j,mkdirSync as p,readdirSync as h,unlinkSync as E,renameSync as A}from"fs";import{join as c,extname as P}from"path";import{randomBytes as z}from"crypto";var K=["backlog","todo","in-progress","review","done"];function H(){return z(15).toString("hex")}function k(e){return c(e,g,C)}function M(e){let i=c(e,g),n=c(i,"files"),r=c(i,S),s=c(i,"screenshots"),t=c(i,x);if(m(n)){p(r,{recursive:!0});for(let o of h(n)){let a=c(n,o),u=c(r,o);if(!m(u))try{A(a,u)}catch{}}}if(m(s)){p(t,{recursive:!0});for(let o of h(s)){let a=c(s,o),u=c(t,o);if(!m(u))try{A(a,u)}catch{}}}}function ae(e){M(e),p(k(e),{recursive:!0}),p(c(e,g,x),{recursive:!0})}function D(e){return e.slice(0,10)}function U(e,i,n){let r=k(e);return n?c(r,D(n),`${i}.json`):c(r,`${i}.json`)}function T(e,i){let n=k(e);if(!m(n))return null;for(let r of h(n,{withFileTypes:!0}))if(r.isDirectory()){let s=c(n,r.name,`${i}.json`);if(m(s))return s}else if(r.name===`${i}.json`)return c(n,r.name);return null}function V(e){return{...e,text:e.text??e.content??""}}function q(e){let i=(()=>{if(typeof e.type!="string")return;let n=e.type.trim();if(!(!n||n==="[object Object]"))return n})();return{id:String(e.id??""),title:String(e.title??"Untitled"),description:String(e.description??""),status:K.includes(e.status)?e.status:"todo",url:e.url?String(e.url):void 0,selector:(()=>{let n=String(e.selector??"/");return e.file&&!e.cssSelector&&n.startsWith(String(e.file))?e.url?String(e.url):"/":n})(),cssSelector:e.cssSelector&&String(e.cssSelector)!==String(e.selector??"/")?String(e.cssSelector):void 0,file:e.file?String(e.file):void 0,line:e.line==null?void 0:Number(e.line),col:e.col==null?void 0:Number(e.col),component:e.component?String(e.component):void 0,type:i,priority:e.priority?String(e.priority):void 0,...e.reportBack===!0&&{reportBack:!0},agent:e.agent?String(e.agent):void 0,model:e.model?String(e.model):void 0,author:e.author?String(e.author):void 0,commits:Array.isArray(e.commits)?e.commits.map(n=>({sha:String(n.sha??""),message:String(n.message??""),timestamp:String(n.timestamp??new Date().toISOString())})):void 0,created:String(e.created??new Date().toISOString()),updated:e.updated?String(e.updated):void 0,comments:Array.isArray(e.comments)?e.comments.map(n=>V(n)):[],files:Array.isArray(e.files)?e.files.map(n=>typeof n=="string"?{name:n,addedAt:new Date().toISOString()}:{name:String(n.name??""),addedAt:String(n.addedAt??new Date().toISOString()),linkedPath:n.linkedPath?String(n.linkedPath):void 0,mimeType:n.mimeType?String(n.mimeType):void 0}).filter(n=>n.name):[],screenshot:e.screenshot?String(e.screenshot):void 0,annotatedElementText:e.annotatedElementText?String(e.annotatedElementText):void 0,tags:Array.isArray(e.tags)?e.tags.filter(n=>typeof n=="string"&&n.length>0):void 0,sortKey:e.sortKey?String(e.sortKey):void 0,verified:e.verified===!0,branchName:e.branchName?String(e.branchName):void 0,baseline:e.baseline&&typeof e.baseline=="object"?e.baseline:void 0,authStateEnc:e.authStateEnc?String(e.authStateEnc):void 0}}function N(e,i){let n=c(k(e),D(i.created));p(n,{recursive:!0});let r=c(n,`${i.id}.json`),s=r+".tmp";j(s,JSON.stringify(i,null,2),"utf-8"),A(s,r)}function I(e){return e.replace(/\\(n|t|r|\\)/g,(i,n)=>{switch(n){case"n":return`
|
|
3
|
+
`;case"t":return" ";case"r":return"\r";case"\\":return"\\";default:return n}})}function ue(e,i){let n=(()=>{let s=(i.priority??"").trim().toLowerCase();return s==="critical"?"Critical":s==="high"?"High":s==="low"?"Low":"Medium"})(),r={...i,title:I(i.title??"").trim(),description:I(i.description??"").trim(),priority:n,id:H(),created:new Date().toISOString(),comments:[],files:[]};return N(e,r),r}function y(e){try{let i=O(e,"utf-8");if(e.endsWith(".json")){let n=JSON.parse(i);return!n||typeof n!="object"||!("id"in n)?null:q(n)}return null}catch{return null}}function L(e){let i=k(e);if(!m(i))return[];let n=[];for(let r of h(i,{withFileTypes:!0}))if(r.isDirectory()){let s=c(i,r.name);for(let t of h(s))if(P(t)===".json"){let o=c(s,t),a=y(o);a&&n.push({task:a,filePath:o})}}else if(P(r.name)===".json"){let s=c(i,r.name),t=y(s);t&&n.push({task:t,filePath:s})}return n}function ce(e){return L(e).map(({task:i})=>i)}function le(e){return L(e).map(({task:i,filePath:n})=>({...i,filePath:n}))}function B(e,i,n){let r=T(e,i),s=r?y(r):null;if(!s)return null;let t={...s,...n,updated:new Date().toISOString()};if(N(e,t),r&&r!==U(e,i,t.created))try{E(r)}catch{}return t}function fe(e,i){let n=T(e,i);return n?(E(n),!0):!1}function me(e,i,n){return{id:e.id,status:e.status,title:e.title,description:e.description,...e.url&&{url:e.url},selector:e.selector,...e.file&&{file:e.file},...e.line!=null&&{line:e.line},...e.col!=null&&{col:e.col},...e.component&&{component:e.component},...e.type&&{type:e.type},...e.priority&&{priority:e.priority},...i&&i.length>0&&{structuredComments:i},...n&&n.length>0&&{linkedFiles:n},...e.reportBack&&{reportBack:!0},created:e.created}}function de(e,i,n,r,s){let t=[];if(t.push(`[${e.status}] ${e.title}`),t.push(` id: ${e.id}`),t.push(` file: ${i}`),e.file&&t.push(` source: ${e.file}${e.line==null?"":`:${e.line}`}${e.col==null?"":`:${e.col}`}`),e.component&&t.push(` component: ${e.component}`),t.push(` selector: ${e.selector??"/"}`),e.cssSelector&&t.push(` css: ${e.cssSelector}`),e.url&&t.push(` url: ${e.url}`),e.commits&&e.commits.length>0)if(e.commits.length===1)t.push(` commit: ${e.commits[0].sha}`);else{t.push(` commits (${e.commits.length}):`);for(let o of e.commits)t.push(` ${o.sha.slice(0,8)} ${o.timestamp} ${o.message.slice(0,60)}`)}if(t.push(` created: ${e.created}`),e.type&&t.push(` type: ${e.type}`),e.priority&&t.push(` priority: ${e.priority}`),e.author&&t.push(` author: ${e.author}`),e.description){t.push(" description:");for(let o of e.description.split(`
|
|
4
|
+
`))t.push(` ${o}`)}if(e.annotatedElementText&&t.push(` element text: ${e.annotatedElementText}`),n.length>0){t.push(` comments (${n.length}):`);for(let o of n){let a=o.updatedAt?` (edited ${o.updatedAt})`:"";t.push(` [${o.author??"user"}] ${o.createdAt}${a}`);for(let u of o.text.split(`
|
|
5
|
+
`))t.push(` ${u}`)}}if(r.length>0){t.push(` linked files (${r.length}):`);for(let o of r){t.push(` - ${o.name} ${o.url}`);let a=o.linkedPath??c(s,".vibeflow","files",e.id,o.name);if(/\.(md|txt)$/i.test(o.name)&&o.size<1e5&&m(a))try{let u=O(a,"utf-8");t.push(" \u250C\u2500\u2500 content \u2500\u2500");for(let _ of u.split(`
|
|
6
|
+
`))t.push(` \u2502 ${_}`);t.push(" \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")}catch{}}}return t.join(`
|
|
7
|
+
`)}function ge(e){let{autoCommit:i,autoPush:n,autoComment:r,createBranch:s}=e,t=[];if(t.push("Agent instructions (concise):"),t.push(" Discover: vibeflow tasks --status todo | vibeflow tasks --type Research | vibeflow tasks --user <email> | vibeflow tasks --tag <tag>"),t.push(" Auto-claim: vibeflow tasks --next (picks highest-priority todo task and sets it in-progress automatically)"),t.push(" Details: vibeflow tasks --get <id> (full task info with comments and files)"),t.push(' Create: vibeflow tasks --add --title "..." --description "..."'),t.push(""),t.push(" Workflow:"),s?(t.push(" \u26A0 Create a branch FIRST, before any implementation:"),t.push(" 1. git checkout -b <short-name> (e.g. fix/annotation-errors, feat/eye-toggle, chore/cleanup-extension)"),t.push(" Branch name rules: lowercase, kebab-case, 2-5 words, prefix fix/feat/chore/docs."),t.push(" Describe the WORK done (not dates). Bad: agent/2026-04-16. Good: fix/bug-errors-visibility."),t.push(" 2. vibeflow tasks --edit <id> --set-status in-progress \u2190 CLAIM TASK"),t.push(" 3. <implement the change>")):(t.push(" \u26A0 IMMEDIATELY set in-progress BEFORE any implementation work:"),t.push(" 1. vibeflow tasks --edit <id> --set-status in-progress \u2190 DO THIS FIRST"),t.push(" (if you used --next: task is already in-progress \u2014 skip to step 2)"),t.push(" 2. <implement the change>")),e.requireVerifyBeforeReview)if(i){t.push(s?" 4. git add <files> (stage your changes first)":" 3. git add <files> (stage your changes first)"),t.push(` ${s?"5":"4"}. vibeflow verify <id> (verify the fix \u2014 check the evidences it produces)`),t.push(" If verification fails, set task back to in-progress and fix.");let a=["--set-status review"];i&&a.push('--commit-message "<one-line summary>"'),r&&a.push('--comment "<report>"'),t.push(` ${s?"6":"5"}. vibeflow tasks --edit <id> ${a.join(" ")}`),t.push(" CLI will commit staged changes and link the commit SHA automatically.")}else{t.push(s?' 4. git add <files> && vibeflow tasks --commit --task <id> --message "<one-line summary>"':' 3. git add <files> && vibeflow tasks --commit --task <id> --message "<one-line summary>"'),t.push(` ${s?"5":"4"}. vibeflow verify <id> (verify the fix \u2014 check the evidences it produces)`),t.push(" If verification fails, set task back to in-progress and fix.");let a=["--set-status review"];r&&a.push('--comment "<report>"'),t.push(` ${s?"6":"5"}. vibeflow tasks --edit <id> ${a.join(" ")}`)}else{if(i){t.push(s?" 4. git add <files> (stage your changes first)":" 3. git add <files> (stage your changes first)");let u=["--set-status review"];i&&u.push('--commit-message "<one-line summary>"'),r&&u.push('--comment "<report>"'),t.push(` ${s?"5":"4"}. vibeflow tasks --edit <id> ${u.join(" ")}`),t.push(" CLI will commit staged changes and link the commit SHA automatically.")}else{t.push(s?' 4. git add <files> && vibeflow tasks --commit --task <id> --message "<one-line summary>"':' 3. git add <files> && vibeflow tasks --commit --task <id> --message "<one-line summary>"');let u=["--set-status review"];r&&u.push('--comment "<report>"'),t.push(` ${s?"5":"4"}. vibeflow tasks --edit <id> ${u.join(" ")}`)}let a=s?"6":"5";t.push(` ${a}. vibeflow verify <id> (verify the fix \u2014 check the evidences it produces)`),t.push(" If verification fails, set task back to in-progress and fix.")}return r&&(t.push(""),t.push(" Comment format (--comment):"),t.push(" \xB7 Plain text for concise one-liners."),t.push(" \xB7 Markdown for multi-section reports. Use **bold**, bullet lists, code fences."),t.push(" \xB7 Must cover: what changed, why, key decisions, anything future agents should know."),t.push(" \xB7 For long reports, attach a .md file and reference it in the comment.")),t.push(""),i&&t.push(" [setting] Auto-commit ON: provide --commit-message when setting status to review; CLI commits."),n&&t.push(" [setting] Auto-push ON: CLI pushes after the commit automatically."),r&&t.push(" [setting] Auto-comment ON: --comment is required when setting status to review."),s&&t.push(" [setting] Create branch ON: all work goes on a dedicated branch created before implementation."),e.requireVerifyBeforeReview&&t.push(" [setting] Verify gate ON: run vibeflow verify before setting status to review (skipped for tasks without URL/selector)."),e.hasResearchTasks&&(t.push(" Research tasks: NEVER generate code \u2014 research only."),t.push(" Attach a .md report before marking Research tasks as review."),t.push(" CLI ENFORCES: cannot mark Research as review without an attached .md report."),t.push(" Create the report file locally first, then upload when marking as review:"),t.push(' vibeflow tasks --edit <id> --set-status review --report-file ./report.md --comment "..."'),t.push(" The file is saved next to the task and deleted from the original path automatically."),t.push(" Report must include: findings, options considered (with pros/cons), recommendation, sources.")),e.hasBugTasks&&(t.push(" Bug tasks: Reproduce the bug first, then include in the comment:"),t.push(" \xB7 Symptom: what the user sees"),t.push(" \xB7 Trigger: exact steps to reproduce"),t.push(" \xB7 Root cause: the specific code/logic that caused it"),t.push(" \xB7 Fix: what was changed and why it works"),t.push(" \xB7 Evidence: paste the relevant error message or stack trace")),t.push(" BLOCKED? If a task is unclear or missing context:"),t.push(' vibeflow tasks --edit <id> --comment "Blocked: <reason>. Need: <what is needed>."'),t.push(" Then pick the next task: vibeflow tasks --next"),t.push(" Do not guess at unclear requirements \u2014 leave a comment and move on."),t.push(" CRITICAL: NEVER edit .vibeflow/ task files directly."),t.push(" All task operations (status, comments, commits) must go through CLI commands."),t.push(" CRITICAL: Set in-progress BEFORE reading/planning. Other agents may pick the same task."),t.push(" The in-progress transition signals ownership. Skip it and another agent may duplicate your work."),t.push(" Sequence: discover tasks \u2192 pick one \u2192 set in-progress \u2192 THEN read details and implement."),t.push(' CRITICAL: NEVER set a task status to "done".'),t.push(' When your implementation is complete, set the status to "review" \u2014 not "done".'),t.push(" Only humans can mark tasks as done after reviewing the work."),t.push(" The CLI will warn you and still allow the change \u2014 but agents must not use it."),t.push(""),t.join(`
|
|
8
|
+
`)}var $=".linked.json";function d(e,i){return f(e,g,S,i)}function Q(e,i){Y(d(e,i),{recursive:!0})}function X(e,i){let n=f(d(e,i),$);if(!l(n))return[];try{return JSON.parse(G(n,"utf-8"))}catch{return[]}}function Z(e,i){let n=T(e,i),r=n?y(n):null;return!r?.files||r.files.length===0?[]:r.files}function w(e,i,n){B(e,i,{files:n})}function b(e,i){let n=Z(e,i),r=f(d(e,i),$);if(!l(r))return n;let s=X(e,i);if(s.length===0){try{v(r)}catch{}return n}let t=n.slice(),o=!1;for(let a of s)t.find(u=>u.linkedPath===a.path||u.name===a.name&&u.linkedPath)||(t.push({name:a.name,linkedPath:a.path,addedAt:new Date().toISOString()}),o=!0);o&&w(e,i,t);try{v(r)}catch{}return t}function ee(e,i){let n=d(e,i),r=b(e,i),s=new Map;for(let t of r){if(t.linkedPath&&l(t.linkedPath)){let a=R(t.linkedPath);s.set(t.name,{name:t.name,size:a.size,url:`/api/tasks/${i}/files/${encodeURIComponent(t.name)}`,linkedPath:t.linkedPath,createdAt:a.mtime.toISOString()});continue}let o=f(n,t.name);if(l(o)){let a=R(o);s.set(t.name,{name:t.name,size:a.size,url:`/api/tasks/${i}/files/${encodeURIComponent(t.name)}`,createdAt:a.mtime.toISOString()})}}if(l(n))for(let t of W(n,{withFileTypes:!0})){if(!t.isFile()||t.name===$||s.has(t.name))continue;let o=f(n,t.name),a=R(o);s.set(t.name,{name:t.name,size:a.size,url:`/api/tasks/${i}/files/${encodeURIComponent(t.name)}`,createdAt:a.mtime.toISOString()})}return Array.from(s.values())}function ve(e,i,n,r){let s=F(n);Q(e,i),J(f(d(e,i),s),r);let t=b(e,i);return t.find(o=>o.name===s&&!o.linkedPath)||(t.push({name:s,addedAt:new Date().toISOString()}),w(e,i,t)),{name:s,size:r.length,url:`/api/tasks/${i}/files/${encodeURIComponent(s)}`}}function be(e,i,n){let r=F(n),s=b(e,i),t=s.findIndex(a=>a.name===r);if(t!==-1){let[a]=s.splice(t,1);if(w(e,i,s),a&&!a.linkedPath){let u=f(d(e,i),r);l(u)&&v(u)}return!0}let o=f(d(e,i),r);return l(o)?(v(o),!0):!1}function xe(e,i,n){let r=F(n),s=b(e,i).find(o=>o.name===r&&o.linkedPath);if(s?.linkedPath&&l(s.linkedPath))return s.linkedPath;let t=f(d(e,i),r);return l(t)?t:null}function Ae(e,i){return ee(e,i).length}export{g as a,C as b,x as c,te as d,H as e,ae as f,U as g,T as h,I as i,ue as j,y as k,ce as l,le as m,B as n,fe as o,me as p,de as q,ge as r,d as s,Q as t,ee as u,ve as v,be as w,xe as x,Ae as y};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{d as y,f as S,o as R,s as T,v as x}from"./chunk-O4LGEPMQ.js";import{readdirSync as A,writeFileSync as z,readFileSync as O,unlinkSync as m,statSync as g,mkdirSync as $,existsSync as f}from"fs";import{join as l,basename as d}from"path";var h=".linked.json";function c(e,n){return l(e,y,S,n)}function b(e,n){$(c(e,n),{recursive:!0})}function L(e,n){let s=l(c(e,n),h);if(!f(s))return[];try{return JSON.parse(O(s,"utf-8"))}catch{return[]}}function F(e,n){let s=R(e,n),r=s?T(s):null;return!r?.files||r.files.length===0?[]:r.files}function P(e,n){return F(e,n)}function k(e,n,s){x(e,n,{files:s})}function p(e,n){let s=F(e,n),r=l(c(e,n),h);if(!f(r))return s;let i=L(e,n);if(i.length===0){try{m(r)}catch{}return s}let t=s.slice(),o=!1;for(let a of i)t.find(u=>u.linkedPath===a.path||u.name===a.name&&u.linkedPath)||(t.push({name:a.name,linkedPath:a.path,addedAt:new Date().toISOString()}),o=!0);o&&k(e,n,t);try{m(r)}catch{}return t}function v(e,n){let s=c(e,n),r=P(e,n),i=new Map;for(let t of r){if(t.linkedPath&&f(t.linkedPath)){let a=g(t.linkedPath);i.set(t.name,{name:t.name,size:a.size,url:`/api/tasks/${n}/files/${encodeURIComponent(t.name)}`,linkedPath:t.linkedPath,createdAt:a.mtime.toISOString()});continue}let o=l(s,t.name);if(f(o)){let a=g(o);i.set(t.name,{name:t.name,size:a.size,url:`/api/tasks/${n}/files/${encodeURIComponent(t.name)}`,createdAt:a.mtime.toISOString()})}}if(f(s))for(let t of A(s,{withFileTypes:!0})){if(!t.isFile()||t.name===h||i.has(t.name))continue;let o=l(s,t.name),a=g(o);i.set(t.name,{name:t.name,size:a.size,url:`/api/tasks/${n}/files/${encodeURIComponent(t.name)}`,createdAt:a.mtime.toISOString()})}return Array.from(i.values())}function U(e,n,s,r){let i=d(s);b(e,n),z(l(c(e,n),i),r);let t=p(e,n);return t.find(o=>o.name===i&&!o.linkedPath)||(t.push({name:i,addedAt:new Date().toISOString()}),k(e,n,t)),{name:i,size:r.length,url:`/api/tasks/${n}/files/${encodeURIComponent(i)}`}}function E(e,n,s){let r=d(s),i=p(e,n),t=i.findIndex(a=>a.name===r);if(t!==-1){let[a]=i.splice(t,1);if(k(e,n,i),a&&!a.linkedPath){let u=l(c(e,n),r);f(u)&&m(u)}return!0}let o=l(c(e,n),r);return f(o)?(m(o),!0):!1}function _(e,n,s){let r=d(s),i=P(e,n).find(o=>o.name===r&&o.linkedPath);if(i?.linkedPath&&f(i.linkedPath))return i.linkedPath;let t=l(c(e,n),r);return f(t)?t:null}function M(e,n){return v(e,n).length}async function B(e){let{listTasks:n}=await import("./tasks-SV7ZVS7W.js"),s=n(e),r=0;for(let i of s)try{let t=P(e,i.id);p(e,i.id),F(e,i.id).length!==t.length&&r++}catch{}return r}export{c as a,b,P as c,v as d,U as e,E as f,_ as g,M as h,B as i};
|