@vovy-ai/go 0.3.0 → 0.4.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 +10 -6
  2. package/dist/index.js +126 -8
  3. package/package.json +5 -5
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @vovy-ai/go
2
2
 
3
- Free, forever, drop-in skills for vibe coding safely the `vovy` CLI installer.
3
+ The `vovy` CLI — one command drops project-aware skills, scope-aware code search, and git-committed project memory into the AI coding tool you already use. Free forever.
4
4
 
5
5
  ```
6
6
  npx @vovy-ai/go install
@@ -11,24 +11,28 @@ That's it. It detects which AI coding tool you have installed — [Claude Code](
11
11
  ```
12
12
  npx @vovy-ai/go doctor # check everything is installed correctly
13
13
  npx @vovy-ai/go uninstall # remove everything Vovy wrote, cleanly
14
+ npx @vovy-ai/go statusline # one-line badge for your tool's status bar
14
15
  ```
15
16
 
16
17
  Installed globally (`npm i -g @vovy-ai/go`), the command is just `vovy`.
17
18
 
18
- `npx @vovy-ai/go doctor` also reports a deterministic "always-on token footprint" — every installed skill file plus every registered MCP tool definition, the tokens a session pays whether or not any of it ever fires.
19
+ `npx @vovy-ai/go doctor` also reports a deterministic "always-on token footprint" — every installed skill file plus every registered MCP tool definition, the tokens a session pays whether or not any of it ever fires — and tells you which Context Engine backend your project gets: `typescript` (scope-aware, resolved through your project's own TypeScript) or `tree-sitter` (name-matching fallback), with the one-line upgrade if you're on the fallback.
19
20
 
20
- It's MIT-licensed and runs entirely on your machine — no account, no API key, nothing phoning home.
21
+ It's MIT-licensed and runs entirely on your machine — no account, no API key, no background telemetry. The only network call in the CLI is a one-time, two-question install survey that sends answers **only if you type them**; skip it (or set `VOVY_NO_SURVEY=1`) and nothing is ever sent.
21
22
 
22
23
  ## What it installs
23
24
 
24
- Four skills plus a local MCP server that give AI coding assistants deterministic project context and safety rails:
25
+ Five skills plus a local MCP server that give AI coding assistants deterministic project context, durable memory, and safety rails:
25
26
 
26
27
  | Skill | What it does |
27
28
  |---|---|
28
- | **Prompt Rescoper** | Rewrites vague, oversized requests into a small, reviewable spec before any code is written. |
29
+ | **Prompt Rescoper** | Rewrites vague, oversized requests into a small, reviewable spec — including how the change will be verified and what imperfections are acceptable — before any code is written. |
29
30
  | **Project Skill Drafter** | Analyzes your actual project and drafts a project-specific skill so future requests already know your stack. |
30
31
  | **Founder Explainer** | Explains destructive/high-stakes actions in plain English before they happen, and flags common vibe-coding security mistakes. |
31
- | **Context Scoper** | Calls a tree-sitter-backed Context Engine to find the exact symbol or file before reading whole files — fewer tokens spent, fewer same-named false matches. |
32
+ | **Context Scoper** | Finds the exact symbol, method, or file before reading whole files — via your project's own TypeScript when available (scope-aware, so two same-named functions don't get confused), tree-sitter otherwise. Includes `impact`: "what breaks if I change this", walked transitively. |
33
+ | **Memory Keeper** | Records decisions (with what was rejected and why), mistakes (with how to avoid repeating them), and constraints (with the why) into `.vovy/memory/` — plain markdown, committed to git, so rationale survives across sessions, machines, teammates, and AI tools. |
34
+
35
+ With `--scope project`, skill descriptions are tailored to your detected stack (a Next.js + TypeScript project gets descriptions that say so), which helps them trigger at the right moments at zero extra token cost.
32
36
 
33
37
  Full docs, architecture, and source: **[github.com/Vovy-AI/vovy-cli](https://github.com/Vovy-AI/vovy-cli)**
34
38
 
package/dist/index.js CHANGED
@@ -1,7 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { realpathSync } from "fs";
4
+ import { readFileSync as readFileSync3, realpathSync } from "fs";
5
+ import { dirname as dirname2, join as join4 } from "path";
6
+ import { createInterface } from "readline/promises";
5
7
  import { fileURLToPath } from "url";
6
8
  import { parseArgs } from "util";
7
9
  import { ADAPTERS as ADAPTERS2 } from "@vovy-ai/host-detect";
@@ -164,8 +166,109 @@ function countMemoryEntries(cwd) {
164
166
  return count;
165
167
  }
166
168
 
169
+ // src/commands/survey.ts
170
+ import { mkdirSync, readFileSync, writeFileSync } from "fs";
171
+ import { dirname, join as join3 } from "path";
172
+ var SURVEY_URL = "https://swrrttvhfnsiesmkbwia.supabase.co/rest/v1/vovy_cli_survey";
173
+ var SURVEY_ANON_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InN3cnJ0dHZoZm5zaWVzbWtid2lhIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDQ0ODI1MjQsImV4cCI6MjA2MDA1ODUyNH0.5BMG6KY-49J_Gz8Qg02Hf3DEHgiy3evtiXqP77non_A";
174
+ var SOURCES = [
175
+ { key: "1", value: "x_twitter", label: "X / Twitter" },
176
+ { key: "2", value: "github", label: "GitHub" },
177
+ { key: "3", value: "friend", label: "A friend or colleague" },
178
+ { key: "4", value: "search", label: "Search" },
179
+ { key: "5", value: "youtube", label: "YouTube" },
180
+ { key: "6", value: "other", label: "Other" }
181
+ ];
182
+ function statePath(env) {
183
+ return join3(env.home, ".vovy", "cli-state.json");
184
+ }
185
+ function readState(env) {
186
+ try {
187
+ return JSON.parse(readFileSync(statePath(env), "utf8"));
188
+ } catch {
189
+ return {};
190
+ }
191
+ }
192
+ function markSurveyOffered(env) {
193
+ const path = statePath(env);
194
+ mkdirSync(dirname(path), { recursive: true });
195
+ writeFileSync(path, `${JSON.stringify({ ...readState(env), surveyOffered: true }, null, 2)}
196
+ `);
197
+ }
198
+ function shouldOfferSurvey(env, isTty) {
199
+ if (!isTty) return false;
200
+ if (process.env.VOVY_NO_SURVEY) return false;
201
+ if (process.env.CI) return false;
202
+ if (readState(env).surveyOffered) return false;
203
+ return true;
204
+ }
205
+ function buildSurveyPayload(answers, cliVersion2) {
206
+ if (answers.source === void 0 && answers.rating === void 0) return null;
207
+ const payload = { cli_version: cliVersion2.slice(0, 20) };
208
+ if (answers.source !== void 0) payload.source = answers.source;
209
+ if (answers.source === "other" && answers.sourceOther) {
210
+ payload.source_other = answers.sourceOther.slice(0, 120);
211
+ }
212
+ if (answers.rating !== void 0) payload.rating = answers.rating;
213
+ return payload;
214
+ }
215
+ async function askSurveyQuestions(rl) {
216
+ const answers = {};
217
+ const sourceMenu = SOURCES.map((s) => ` [${s.key}] ${s.label}`).join("\n");
218
+ const sourceInput = (await rl.question(`
219
+ How did you hear about Vovy?
220
+ ${sourceMenu}
221
+ > `)).trim();
222
+ const source = SOURCES.find((s) => s.key === sourceInput);
223
+ if (source) {
224
+ answers.source = source.value;
225
+ if (source.value === "other") {
226
+ const detail = (await rl.question("Where? (optional) > ")).trim();
227
+ if (detail) answers.sourceOther = detail;
228
+ }
229
+ }
230
+ const ratingInput = (await rl.question("\nExperience so far, 1 (rough) to 5 (great)? > ")).trim();
231
+ const rating = Number.parseInt(ratingInput, 10);
232
+ if (Number.isInteger(rating) && rating >= 1 && rating <= 5) answers.rating = rating;
233
+ return answers;
234
+ }
235
+ async function sendSurvey(payload, url = SURVEY_URL) {
236
+ try {
237
+ const response = await fetch(url, {
238
+ method: "POST",
239
+ headers: { apikey: SURVEY_ANON_KEY, "Content-Type": "application/json" },
240
+ body: JSON.stringify(payload),
241
+ signal: AbortSignal.timeout(2500)
242
+ });
243
+ return response.ok;
244
+ } catch {
245
+ return false;
246
+ }
247
+ }
248
+ async function offerSurvey(env, cliVersion2, makeInterface) {
249
+ markSurveyOffered(env);
250
+ console.log(
251
+ "\nOne-time question (never asked again \u2014 press Enter to skip either one).\nIf you answer, the answers and the CLI version are sent to the Vovy team; skipping sends nothing."
252
+ );
253
+ const rl = makeInterface();
254
+ try {
255
+ const answers = await askSurveyQuestions(rl);
256
+ const payload = buildSurveyPayload(answers, cliVersion2);
257
+ if (!payload) {
258
+ console.log("Skipped \u2014 nothing was sent.");
259
+ return;
260
+ }
261
+ const sent = await sendSurvey(payload);
262
+ console.log(
263
+ sent ? "Thanks \u2014 that helps more than you'd think." : "Thanks! (Couldn't reach the survey endpoint \u2014 nothing else to do.)"
264
+ );
265
+ } finally {
266
+ rl.close();
267
+ }
268
+ }
269
+
167
270
  // src/commands/uninstall.ts
168
- import { existsSync as existsSync2, readFileSync, rmSync, writeFileSync } from "fs";
271
+ import { existsSync as existsSync3, readFileSync as readFileSync2, rmSync, writeFileSync as writeFileSync2 } from "fs";
169
272
  import {
170
273
  removeCodexMcpEntry,
171
274
  removeJsonMcpEntry
@@ -180,19 +283,19 @@ function runUninstall(opts) {
180
283
  const removedSkillPaths = [];
181
284
  for (const skill of SKILL_MANIFEST) {
182
285
  const path = adapter.skillFilePath(opts.env, scope, skill.id);
183
- if (existsSync2(path)) {
286
+ if (existsSync3(path)) {
184
287
  removedSkillPaths.push(path);
185
288
  if (!dryRun) rmSync(path, { force: true });
186
289
  }
187
290
  }
188
291
  let removedMcpEntry = null;
189
292
  const mcpPath = adapter.mcpConfigPath(opts.env, scope);
190
- if (mcpPath && existsSync2(mcpPath)) {
191
- const existing = readFileSync(mcpPath, "utf8");
293
+ if (mcpPath && existsSync3(mcpPath)) {
294
+ const existing = readFileSync2(mcpPath, "utf8");
192
295
  const next = adapter.id === "codex" ? removeCodexMcpEntry(existing, VOVY_ID) : removeJsonMcpEntry(existing, VOVY_ID);
193
296
  if (next !== null) {
194
297
  removedMcpEntry = mcpPath;
195
- if (!dryRun) writeFileSync(mcpPath, next, "utf8");
298
+ if (!dryRun) writeFileSync2(mcpPath, next, "utf8");
196
299
  }
197
300
  }
198
301
  return { adapter, removedSkillPaths, removedMcpEntry };
@@ -206,7 +309,15 @@ function realEnv() {
206
309
  }
207
310
 
208
311
  // src/index.ts
209
- var HELP = `Vovy \u2014 free, forever, drop-in skills for vibe coding safely. Run via \`npx @vovy-ai/go\` (or \`vovy\` if installed globally).
312
+ function cliVersion() {
313
+ try {
314
+ const pkgPath = join4(dirname2(fileURLToPath(import.meta.url)), "..", "package.json");
315
+ return JSON.parse(readFileSync3(pkgPath, "utf8")).version ?? "unknown";
316
+ } catch {
317
+ return "unknown";
318
+ }
319
+ }
320
+ var HELP = `Vovy \u2014 drop-in skills for vibe coding safely with the AI tool you already use. Run via \`npx @vovy-ai/go\` (or \`vovy\` if installed globally).
210
321
 
211
322
  Usage:
212
323
  npx @vovy-ai/go install [options] Write Vovy's skills into detected (or specified) host tools
@@ -251,7 +362,7 @@ function actionSymbol(action) {
251
362
  if (action === "created") return "+";
252
363
  return "~";
253
364
  }
254
- function cmdInstall(argv) {
365
+ async function cmdInstall(argv) {
255
366
  const flags = parseCommonFlags(argv);
256
367
  if (flags.help) return console.log(HELP);
257
368
  const env = realEnv();
@@ -288,6 +399,13 @@ Add to ~/.claude/settings.json:
288
399
  (Faster variant: npm i -g @vovy-ai/go, then use "vovy statusline" as the command.)`
289
400
  );
290
401
  }
402
+ if (!flags.dryRun && shouldOfferSurvey(env, process.stdout.isTTY === true)) {
403
+ await offerSurvey(
404
+ env,
405
+ cliVersion(),
406
+ () => createInterface({ input: process.stdin, output: process.stdout })
407
+ );
408
+ }
291
409
  }
292
410
  function cmdStatusline() {
293
411
  console.log(buildStatusline(realEnv()));
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vovy-ai/go",
3
- "version": "0.3.0",
4
- "description": "Free, forever, drop-in MCP server + skill pack that teaches non-technical founders to vibe code safely. npx @vovy-ai/go install",
3
+ "version": "0.4.0",
4
+ "description": "Drop-in MCP server + skill pack that teaches non-technical founders to vibe code safely — free forever. npx @vovy-ai/go install",
5
5
  "keywords": [
6
6
  "vibe-coding",
7
7
  "ai-coding-assistant",
@@ -42,14 +42,14 @@
42
42
  "dist"
43
43
  ],
44
44
  "dependencies": {
45
- "@vovy-ai/host-detect": "0.1.1",
46
- "@vovy-ai/skills": "0.4.0"
45
+ "@vovy-ai/host-detect": "0.1.2",
46
+ "@vovy-ai/skills": "0.4.1"
47
47
  },
48
48
  "devDependencies": {
49
49
  "tsup": "^8.3.5",
50
50
  "typescript": "^5.7.2",
51
51
  "vitest": "^2.1.8",
52
- "@vovy-ai/mcp-server": "0.3.0"
52
+ "@vovy-ai/mcp-server": "0.3.1"
53
53
  },
54
54
  "scripts": {
55
55
  "build": "tsup src/index.ts --format esm --dts --clean",