getvibez 0.1.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.
Files changed (3) hide show
  1. package/README.md +32 -0
  2. package/cli.mjs +176 -0
  3. package/package.json +39 -0
package/README.md ADDED
@@ -0,0 +1,32 @@
1
+ # vibez
2
+
3
+ One-command installer for the [Vibez](https://github.com/Peter-Zhao-751/Vibez) agent plugins — push Claude Code and Codex session events (permission prompts, questions, task completion) to your iPhone, and shield your distracting apps until you come back.
4
+
5
+ ```sh
6
+ npx getvibez
7
+ ```
8
+
9
+ Detects which agent CLIs you have (`claude`, `codex`), asks which to install for, and runs each CLI's own plugin manager:
10
+
11
+ - **Claude Code** → `claude plugin marketplace add Peter-Zhao-751/Vibez` + `claude plugin install vibez@plugin`
12
+ - **Codex** → `codex plugin marketplace add Peter-Zhao-751/Vibez` + `codex plugin add vibez@vibez`
13
+
14
+ Safe to re-run; already-installed plugins are detected and skipped.
15
+
16
+ ## Options
17
+
18
+ | Flag | Effect |
19
+ |---|---|
20
+ | `--claude` / `--codex` | Install for one CLI only, no prompt |
21
+ | `-y`, `--yes` | Install for every detected CLI, no prompt |
22
+ | `--dry-run` | Print the commands without running them |
23
+
24
+ ## After installing
25
+
26
+ 1. Get the Vibez iOS app — [free on the App Store](https://apps.apple.com/us/app/ai-coding-focus-vibez/id6775433780).
27
+ 2. Open a new agent session — it prints your private 4-word Vibez ID (in Claude Code, `/vibez:setup` shows it again).
28
+ 3. Enter the ID in the app's Setup card. One ID covers both agents.
29
+
30
+ Requires Node ≥ 18, plus `jq` and `curl` on the machine (the notification hooks use them; the installer warns if they're missing).
31
+
32
+ Full plugin docs: [ClaudePlugin](https://github.com/Peter-Zhao-751/Vibez/tree/main/ClaudePlugin) · [CodexPlugin](https://github.com/Peter-Zhao-751/Vibez/tree/main/CodexPlugin)
package/cli.mjs ADDED
@@ -0,0 +1,176 @@
1
+ #!/usr/bin/env node
2
+ // vibez installer — one command to install the Vibez plugin into every
3
+ // supported agent CLI. Thin wrapper around the official plugin commands;
4
+ // the plugins in ClaudePlugin/ and CodexPlugin/ are the source of truth.
5
+ import { spawnSync } from "node:child_process";
6
+ import { createInterface } from "node:readline/promises";
7
+ import { createRequire } from "node:module";
8
+
9
+ const VERSION = createRequire(import.meta.url)("./package.json").version;
10
+ const REPO = "Peter-Zhao-751/Vibez";
11
+ const APP_STORE = "https://apps.apple.com/us/app/ai-coding-focus-vibez/id6775433780";
12
+
13
+ const TARGETS = [
14
+ {
15
+ flag: "--claude",
16
+ name: "Claude Code",
17
+ bin: "claude",
18
+ installVerb: "install",
19
+ spec: "vibez@plugin",
20
+ installHint: "https://code.claude.com/docs — npm install -g @anthropic-ai/claude-code",
21
+ },
22
+ {
23
+ flag: "--codex",
24
+ name: "Codex",
25
+ bin: "codex",
26
+ installVerb: "add",
27
+ spec: "vibez@vibez",
28
+ installHint: "https://developers.openai.com/codex — npm install -g @openai/codex",
29
+ },
30
+ ];
31
+
32
+ const HELP = `vibez ${VERSION} — install the Vibez plugin for your agent CLIs
33
+
34
+ Usage: npx getvibez [options]
35
+
36
+ Detects Claude Code and Codex on this machine and installs the Vibez
37
+ notification plugin into each via its own plugin manager.
38
+
39
+ Options:
40
+ --claude Install for Claude Code only (skips the prompt)
41
+ --codex Install for Codex only (skips the prompt)
42
+ -y, --yes Install for every detected CLI without prompting
43
+ --dry-run Print the commands that would run, run nothing
44
+ -v, --version Print version
45
+ -h, --help Show this help
46
+
47
+ Pairing: get the Vibez iOS app (${APP_STORE}),
48
+ then open a new agent session — it prints your 4-word Vibez ID to enter in the app.`;
49
+
50
+ function onPath(bin) {
51
+ const probe = process.platform === "win32" ? "where" : "which";
52
+ return spawnSync(probe, [bin], { stdio: "ignore" }).status === 0;
53
+ }
54
+
55
+ function commandsFor(target) {
56
+ return [
57
+ [target.bin, "plugin", "marketplace", "add", REPO],
58
+ [target.bin, "plugin", target.installVerb, target.spec],
59
+ ];
60
+ }
61
+
62
+ // Runs both plugin commands for a target. "Already added/installed" counts
63
+ // as success so re-runs are harmless.
64
+ function install(target) {
65
+ for (const [bin, ...args] of commandsFor(target)) {
66
+ const r = spawnSync(bin, args, { encoding: "utf8" });
67
+ const out = (r.stdout ?? "") + (r.stderr ?? "");
68
+ if (r.status !== 0 && !/already/i.test(out)) {
69
+ return { ok: false, detail: out.trim() || `${bin} ${args.join(" ")} exited ${r.status}` };
70
+ }
71
+ if (/already/i.test(out)) return { ok: true, already: true };
72
+ }
73
+ return { ok: true };
74
+ }
75
+
76
+ // Race the question against readline closing: piped stdin hitting EOF
77
+ // leaves question() pending forever, which would end the process silently.
78
+ async function confirm(rl, closed, question) {
79
+ const answer = await Promise.race([rl.question(`${question} (Y/n) `), closed]);
80
+ if (answer === null) return null;
81
+ const a = answer.trim().toLowerCase();
82
+ return a === "" || a === "y" || a === "yes";
83
+ }
84
+
85
+ async function main() {
86
+ const args = process.argv.slice(2);
87
+ for (const a of args) {
88
+ if (!["--claude", "--codex", "-y", "--yes", "--dry-run", "-v", "--version", "-h", "--help"].includes(a)) {
89
+ console.error(`unknown option: ${a}\n\n${HELP}`);
90
+ process.exit(2);
91
+ }
92
+ }
93
+ if (args.includes("-h") || args.includes("--help")) return console.log(HELP);
94
+ if (args.includes("-v") || args.includes("--version")) return console.log(VERSION);
95
+
96
+ const dryRun = args.includes("--dry-run");
97
+ const yes = args.includes("-y") || args.includes("--yes");
98
+ const narrowed = TARGETS.filter((t) => args.includes(t.flag));
99
+
100
+ const detected = TARGETS.filter((t) => onPath(t.bin));
101
+ const missing = TARGETS.filter((t) => !onPath(t.bin));
102
+
103
+ for (const t of narrowed.filter((t) => !onPath(t.bin))) {
104
+ console.error(`${t.name}: \`${t.bin}\` not found on PATH.\n Install it first: ${t.installHint}`);
105
+ process.exit(1);
106
+ }
107
+ if (detected.length === 0) {
108
+ console.error("No supported agent CLIs found on PATH.\n");
109
+ for (const t of TARGETS) console.error(` ${t.name}: ${t.installHint}`);
110
+ process.exit(1);
111
+ }
112
+
113
+ for (const prereq of ["jq", "curl"]) {
114
+ if (!onPath(prereq)) {
115
+ console.warn(`warning: \`${prereq}\` not found — the Vibez hooks need it to send pushes (brew install ${prereq})`);
116
+ }
117
+ }
118
+
119
+ console.log(`Detected: ${detected.map((t) => t.name).join(", ")}`);
120
+ for (const t of missing) console.log(`Skipping ${t.name} (\`${t.bin}\` not on PATH)`);
121
+ console.log();
122
+
123
+ let selected;
124
+ if (narrowed.length > 0) {
125
+ selected = narrowed;
126
+ } else if (yes || dryRun) {
127
+ selected = detected;
128
+ } else {
129
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
130
+ rl.on("SIGINT", () => {
131
+ rl.close();
132
+ console.log("\naborted — nothing was installed");
133
+ process.exit(130);
134
+ });
135
+ const closed = new Promise((resolve) => rl.once("close", () => resolve(null)));
136
+ selected = [];
137
+ for (const t of detected) {
138
+ const wanted = await confirm(rl, closed, `Install Vibez for ${t.name}?`);
139
+ if (wanted === null) break;
140
+ if (wanted) selected.push(t);
141
+ }
142
+ rl.close();
143
+ if (selected.length === 0) return console.log("Nothing selected — nothing to do.");
144
+ }
145
+
146
+ if (dryRun) {
147
+ console.log("dry-run — would run:");
148
+ for (const t of selected) {
149
+ for (const cmd of commandsFor(t)) console.log(` ${cmd.join(" ")}`);
150
+ }
151
+ return;
152
+ }
153
+
154
+ let failed = false;
155
+ for (const t of selected) {
156
+ process.stdout.write(`Installing for ${t.name}... `);
157
+ const result = install(t);
158
+ if (result.ok) {
159
+ console.log(result.already ? "already installed ✓" : "done ✓");
160
+ } else {
161
+ failed = true;
162
+ console.log("failed ✗");
163
+ console.error(result.detail);
164
+ }
165
+ }
166
+
167
+ console.log(`
168
+ Next steps:
169
+ 1. Get the Vibez iOS app: ${APP_STORE}
170
+ 2. Open a new agent session — it prints your private 4-word Vibez ID.
171
+ (In Claude Code, /vibez:setup shows it again.)
172
+ 3. Enter the ID in the app's Setup card. One ID covers both agents.`);
173
+ if (failed) process.exit(1);
174
+ }
175
+
176
+ main();
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "getvibez",
3
+ "version": "0.1.0",
4
+ "publishConfig": {
5
+ "access": "public"
6
+ },
7
+ "description": "One-command installer for the Vibez plugin — push Claude Code and Codex session events to your iPhone.",
8
+ "bin": {
9
+ "vibez": "cli.mjs"
10
+ },
11
+ "type": "module",
12
+ "files": [
13
+ "cli.mjs",
14
+ "README.md"
15
+ ],
16
+ "scripts": {
17
+ "test": "node test.mjs"
18
+ },
19
+ "engines": {
20
+ "node": ">=18"
21
+ },
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "git+https://github.com/Peter-Zhao-751/Vibez.git",
25
+ "directory": "installer"
26
+ },
27
+ "homepage": "https://github.com/Peter-Zhao-751/Vibez#readme",
28
+ "author": "Peter Zhao",
29
+ "license": "MIT",
30
+ "keywords": [
31
+ "claude-code",
32
+ "codex",
33
+ "notifications",
34
+ "push",
35
+ "fcm",
36
+ "plugin",
37
+ "installer"
38
+ ]
39
+ }