Skip to content

Commit f8bb3ba

Browse files
tnunamakclaude
andcommitted
fix: restore custom heartbeat bloom spinner, replace clack with inquirer
Restore the hand-crafted connect renderer that was accidentally overwritten by an agent in commit 5029900. The original renderer (from ae3a674) features a custom heartbeat bloom spinner (· → ✧ → ✦ double-beat with dark pause) with ANSI cursor control for in-place repaint — no ora dependency. Adapted the restored renderer's interface to match current call sites (title, scopeActive, scopeDone, success, detail, next, fail, bell, cleanup, pauseForPrompt, resumeAfterPrompt). Replaced @clack/prompts with @inquirer/prompts for credential inputs and source picker. Clack's visual chrome (vertical bars, diamond symbols) violated the design skill's principle of owning our visual identity. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 00d0b27 commit f8bb3ba

6 files changed

Lines changed: 473 additions & 184 deletions

File tree

docs/CLI-README-launch.md

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
# vana
2+
3+
**Portable personal data, from the terminal.**
4+
5+
[![Release](https://img.shields.io/github/v/release/vana-com/cli)](https://github.com/vana-com/cli/releases)
6+
![macOS · Linux · Windows](https://img.shields.io/badge/platform-macOS%20·%20Linux%20·%20Windows-blue)
7+
[![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE)
8+
9+
<p align="center">
10+
<img src="docs/assets/demo.gif" width="720" />
11+
<br />
12+
<sub>Connecting GitHub and inspecting the results. Credentials never leave your machine.</sub>
13+
</p>
14+
15+
`vana` collects your data from platforms you use. You log in through a browser
16+
on your machine, and the CLI saves it locally as JSON.
17+
18+
### Highlights
19+
20+
- **Fully local**: credentials and collected data never leave your machine
21+
- **Any platform**: connects through a browser session, not a restricted API
22+
- **Inspectable**: collected data is JSON you can summarize, query, or pipe
23+
- **Agent-ready**: `--json` and `--no-input` flags for scripts and AI agents
24+
- **Session caching**: log in once, reconnect faster next time
25+
- **Extensible**: connectors are standalone modules; add new platforms without touching the core
26+
27+
## Install
28+
29+
macOS (Homebrew):
30+
31+
```bash
32+
brew install vana-com/tap/vana
33+
```
34+
35+
macOS and Linux:
36+
37+
```bash
38+
curl -fsSL https://cli.vana.com/install.sh | sh
39+
```
40+
41+
Windows (PowerShell):
42+
43+
```powershell
44+
irm https://cli.vana.com/install.ps1 | iex
45+
```
46+
47+
Verify with `vana --version`.
48+
49+
## Quick start
50+
51+
```console
52+
$ vana connect github
53+
54+
Connect GitHub
55+
56+
→ Connecting
57+
Checking GitHub login...
58+
Login confirmed. Collecting data in background...
59+
Profile (1/3): Fetching profile...
60+
Repositories (2/3): Fetched 2 repositories
61+
Starred (3/3): Fetched 0 starred repositories
62+
✓ Connected GitHub. Saved locally.
63+
Path: ~/.dataconnect/last-result.json
64+
```
65+
66+
```console
67+
$ vana data show github
68+
69+
GitHub data
70+
71+
→ Summary
72+
Profile: tnunamak
73+
Repositories: 2
74+
Latest repos: vana-connect, data-connectors
75+
Starred: 0
76+
77+
Path: ~/.dataconnect/last-result.json
78+
Updated: Mar 14, 2026, 8:10 AM
79+
```
80+
81+
Your data is on disk at `~/.dataconnect/`.
82+
83+
```bash
84+
vana data show github --json | jq '.summary'
85+
```
86+
87+
Explore further:
88+
89+
```bash
90+
vana sources # See all available platforms
91+
vana connect # Interactive source picker
92+
vana status # What's connected, what needs attention
93+
```
94+
95+
## How it works
96+
97+
1. `vana connect <source>` launches a browser on your machine
98+
2. You log in (credentials stay on your machine)
99+
3. The CLI collects your data and saves it to `~/.dataconnect/`
100+
4. `vana data show <source>` summarizes what was collected
101+
102+
Sessions are cached, so reconnecting is faster next time. Your data is files
103+
on disk. Inspect, move, or delete them whenever you want.
104+
105+
## Commands
106+
107+
| Command | What it does |
108+
| ------------------------- | --------------------------------------------------------- |
109+
| `vana connect [source]` | Connect a platform and export your data |
110+
| `vana sources` | List available platforms |
111+
| `vana data list` | Show all collected datasets |
112+
| `vana data show <source>` | Summarize a collected dataset |
113+
| `vana data path <source>` | Print the file path for a dataset |
114+
| `vana status` | Overview of connections and anything that needs attention |
115+
| `vana doctor` | Diagnose installation and runtime health |
116+
| `vana logs [source]` | View run logs |
117+
| `vana setup` | Install or repair the browser runtime |
118+
119+
Run `vana <command> --help` for detailed usage.
120+
121+
### For scripts and AI agents
122+
123+
Commands support structured output:
124+
125+
```bash
126+
vana connect github --json --no-input # Machine-safe — never prompts
127+
vana data show github --json | jq # Pipe collected data anywhere
128+
vana sources --json # Discover platforms programmatically
129+
```
130+
131+
`--json` writes structured output to stdout. `--no-input` guarantees no
132+
interactive prompts. The CLI exits `1` if input is needed. See the
133+
[exit code reference](docs/exit-codes.md) for the full contract.
134+
135+
## Sources
136+
137+
`vana` connects to any platform that has a web login. Connectors handle the
138+
automation for each source.
139+
140+
The CLI shares its connector format with
141+
[DataConnect](https://github.com/vana-com/data-connect) and the
142+
[data-connectors](https://github.com/vana-com/data-connectors) repository.
143+
144+
Tested in the CLI: **GitHub**, **Spotify**.
145+
Available in the connector ecosystem: **ChatGPT**, **Instagram**, **LinkedIn**,
146+
**YouTube**, **Shop**, and more.
147+
148+
Run `vana sources` to see what's available on your install.
149+
150+
Missing a platform?
151+
[Request one](https://github.com/vana-com/cli/issues/new?template=source-request.yml)
152+
· [Build a connector](docs/building-connectors.md)
153+
154+
## Ecosystem
155+
156+
`vana` is the CLI for [Vana](https://vana.org)'s data portability network. It
157+
shares connectors and local storage with
158+
[DataConnect](https://github.com/vana-com/data-connect), the desktop app. For
159+
building apps that request user data, see the
160+
[Connect SDK](https://github.com/vana-com/vana-connect).
161+
162+
## Privacy
163+
164+
**Credentials**: You log in through a browser on your machine. Vana never
165+
sees your password, token, or session cookie.
166+
167+
**Collected data**: Saved to `~/.dataconnect/` as local files. Nothing is
168+
uploaded.
169+
170+
**Browser sessions**: Cached in `~/.dataconnect/browser-profiles/` for faster
171+
reconnects. Delete them any time.
172+
173+
**Telemetry**: None.
174+
175+
## Troubleshooting
176+
177+
```bash
178+
vana doctor # Runtime, browser, and state health
179+
vana logs <source> # Latest run log for a source
180+
```
181+
182+
| Problem | Fix |
183+
| ----------------------- | ------------------------------------------ |
184+
| Browser runtime missing | `vana setup` |
185+
| Login expired | `vana connect <source>` to re-authenticate |
186+
| Connector fails | `vana logs <source>` for details |
187+
188+
## Uninstall
189+
190+
Remove the CLI:
191+
192+
```bash
193+
brew uninstall vana # Homebrew
194+
rm -f ~/.local/bin/vana # Script install (macOS / Linux)
195+
```
196+
197+
Remove collected data and state:
198+
199+
```bash
200+
rm -rf ~/.dataconnect
201+
```
202+
203+
## Documentation
204+
205+
- [Building connectors](docs/building-connectors.md)
206+
- [Exit code reference](docs/exit-codes.md)
207+
- [Architecture](docs/architecture.md)
208+
209+
## Community
210+
211+
- [Issues](https://github.com/vana-com/cli/issues): bugs and source requests
212+
- [Discussions](https://github.com/vana-com/cli/discussions): questions and ideas
213+
- [Discord](https://discord.gg/vana): chat with the team
214+
- [Contributing](CONTRIBUTING.md)
215+
216+
## License
217+
218+
MIT

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,6 @@
9999
"node": ">=20.0.0"
100100
},
101101
"dependencies": {
102-
"@clack/prompts": "^1.1.0",
103102
"@inquirer/prompts": "8.3.0",
104103
"chromium-bidi": "15.0.0",
105104
"commander": "14.0.3",

pnpm-lock.yaml

Lines changed: 0 additions & 24 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/cli/index.ts

Lines changed: 23 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,7 @@ import fsp from "node:fs/promises";
33
import path from "node:path";
44
import { createRequire } from "node:module";
55

6-
import {
7-
text as clackText,
8-
password as clackPassword,
9-
select as clackSelect,
10-
confirm as clackConfirm,
11-
isCancel,
12-
} from "@clack/prompts";
6+
import { confirm, input, password, select } from "@inquirer/prompts";
137
import { Command, CommanderError } from "commander";
148

159
import {
@@ -496,11 +490,11 @@ async function runConnect(
496490
process.stderr.write(" \u2022 Local files under ~/.dataconnect/\n\n");
497491
process.stderr.write("Your credentials stay on this machine.\n\n");
498492

499-
const shouldContinue = await clackConfirm({
493+
const shouldContinue = await confirm({
500494
message: "Continue?",
501-
initialValue: true,
495+
default: true,
502496
});
503-
if (isCancel(shouldContinue) || !shouldContinue) {
497+
if (!shouldContinue) {
504498
renderer?.fail("Cancelled.");
505499
emit.event({
506500
type: "outcome",
@@ -709,30 +703,16 @@ async function runConnect(
709703
for (const field of needInput.fields) {
710704
const isPasswordField = field.toLowerCase().includes("password");
711705
if (isPasswordField) {
712-
const result = await clackPassword({
706+
values[field] = await password({
713707
message: humanizeField(field),
714708
});
715-
if (isCancel(result)) {
716-
throw new Error("__vana_prompt_cancelled__");
717-
}
718-
values[field] = result;
719709
} else {
720-
const result = await clackText({
710+
values[field] = await input({
721711
message: humanizeField(field),
722712
});
723-
if (isCancel(result)) {
724-
throw new Error("__vana_prompt_cancelled__");
725-
}
726-
values[field] = result;
727713
}
728714
}
729715
} catch (error) {
730-
if (
731-
error instanceof Error &&
732-
error.message === "__vana_prompt_cancelled__"
733-
) {
734-
throw error;
735-
}
736716
if (isPromptCancelled(error)) {
737717
throw new Error("__vana_prompt_cancelled__");
738718
}
@@ -1088,8 +1068,8 @@ async function runConnectEntry(options: GlobalOptions): Promise<number> {
10881068
return 1;
10891069
}
10901070

1091-
// Build clack-compatible options from enriched sources
1092-
const clackOptions = enrichedSources.map((item) => {
1071+
// Build inquirer-compatible choices from enriched sources
1072+
const choices = enrichedSources.map((item) => {
10931073
const connected = hasCollectedData(item.dataState);
10941074
const hint = connected
10951075
? "connected"
@@ -1098,23 +1078,26 @@ async function runConnectEntry(options: GlobalOptions): Promise<number> {
10981078
: undefined;
10991079
return {
11001080
value: item.id,
1101-
label: item.name,
1102-
hint,
1081+
name: item.name,
1082+
description: hint,
11031083
};
11041084
});
11051085

1106-
const source = await clackSelect({
1107-
message: "Choose a source to connect.",
1108-
options: clackOptions,
1109-
initialValue: suggestedSource?.id,
1110-
});
1086+
try {
1087+
const source = await select({
1088+
message: "Choose a source to connect.",
1089+
choices,
1090+
default: suggestedSource?.id,
1091+
});
11111092

1112-
if (isCancel(source)) {
1113-
emit.info("Cancelled.");
1114-
return 1;
1093+
return runConnect(source as string, options);
1094+
} catch (error) {
1095+
if (isPromptCancelled(error)) {
1096+
emit.info("Cancelled.");
1097+
return 1;
1098+
}
1099+
throw error;
11151100
}
1116-
1117-
return runConnect(source as string, options);
11181101
}
11191102

11201103
async function runList(options: GlobalOptions): Promise<number> {

0 commit comments

Comments
 (0)