@vovy-ai/go 0.2.0 → 0.3.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 +4 -3
  2. package/dist/index.js +85 -8
  3. package/package.json +36 -4
package/README.md CHANGED
@@ -15,19 +15,20 @@ npx @vovy-ai/go uninstall # remove everything Vovy wrote, cleanly
15
15
 
16
16
  Installed globally (`npm i -g @vovy-ai/go`), the command is just `vovy`.
17
17
 
18
- ## Why this is free, forever
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
19
 
20
- Vovy never runs its own AI model and never calls a Vovy-hosted server. It only writes markdown skill files that your existing AI coding tool reads for free, using the model you already pay for. No account, no API key, no cost, ever.
20
+ It's MIT-licensed and runs entirely on your machine no account, no API key, nothing phoning home.
21
21
 
22
22
  ## What it installs
23
23
 
24
- Three skills that make AI coding assistants safer and better-scoped for non-technical founders:
24
+ Four skills plus a local MCP server that give AI coding assistants deterministic project context and safety rails:
25
25
 
26
26
  | Skill | What it does |
27
27
  |---|---|
28
28
  | **Prompt Rescoper** | Rewrites vague, oversized requests into a small, reviewable spec before any code is written. |
29
29
  | **Project Skill Drafter** | Analyzes your actual project and drafts a project-specific skill so future requests already know your stack. |
30
30
  | **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. |
31
32
 
32
33
  Full docs, architecture, and source: **[github.com/Vovy-AI/vovy-cli](https://github.com/Vovy-AI/vovy-cli)**
33
34
 
package/dist/index.js CHANGED
@@ -7,6 +7,8 @@ import { parseArgs } from "util";
7
7
  import { ADAPTERS as ADAPTERS2 } from "@vovy-ai/host-detect";
8
8
 
9
9
  // src/commands/doctor.ts
10
+ import { createRequire } from "module";
11
+ import { join } from "path";
10
12
  import { getAllSkills as getAllSkills2 } from "@vovy-ai/skills";
11
13
 
12
14
  // src/commands/install.ts
@@ -14,7 +16,7 @@ import {
14
16
  writeMcpConfig,
15
17
  writeSkillFile
16
18
  } from "@vovy-ai/host-detect";
17
- import { getAllSkills } from "@vovy-ai/skills";
19
+ import { detectProjectContext, getAllSkills } from "@vovy-ai/skills";
18
20
 
19
21
  // src/targets.ts
20
22
  import { ADAPTERS, getAdapter } from "@vovy-ai/host-detect";
@@ -28,7 +30,7 @@ var VOVY_MCP_ENTRY = { id: "vovy", command: "npx", args: ["-y", "@vovy-ai/mcp-se
28
30
  function runInstall(opts) {
29
31
  const scope = opts.scope ?? "user";
30
32
  const targets = resolveTargets(opts.env, opts.hosts);
31
- const skills = getAllSkills();
33
+ const skills = getAllSkills(scope === "project" ? detectProjectContext(opts.env.cwd) : void 0);
32
34
  return targets.map((adapter) => {
33
35
  const skillResults = skills.map((skill) => ({
34
36
  skillId: skill.id,
@@ -62,7 +64,12 @@ var MCP_TOOL_METADATA = [
62
64
  {
63
65
  name: "search_codebase",
64
66
  title: "Search Codebase",
65
- description: `Deterministic, non-LLM symbol search over a JS/TS/JSX/TSX project via tree-sitter (no embeddings, no network access). Use this BEFORE reading whole files to answer a 'where is X handled' / 'how does Y work' question \u2014 it finds the exact symbol or file first so you read only what's relevant, not entire files. Actions: "overview" (top-level functions/classes/interfaces/exports in one file, needs filePath), "find_symbol" (declaration sites of a name across the project, needs query), "find_references" (identifier-boundary-aware usage sites of a name \u2014 more precise than grep since it never matches inside a string/comment, needs query), "pattern" (plain regex/text search fallback for non-code content, needs query).`
67
+ description: `Deterministic, non-LLM symbol search over a JS/TS/JSX/TSX project (no embeddings, no network access). Use this BEFORE reading whole files to answer a 'where is X handled' / 'what calls Y' / 'is it safe to change Z' question \u2014 it finds the exact symbol, method, or file first, so you read one function instead of one whole file. Covers class methods, interface members, and object-literal methods, not just top-level declarations. Actions: "overview" (declarations and their members in one file, needs filePath), "find_symbol" (declaration sites of a name across the project, needs query), "find_references" (usage sites of a name, excluding declarations, needs query), "impact" (transitive blast radius: who references this symbol, who references THOSE callers, and so on, depth-tagged \u2014 the 'what breaks if I change this' answer; needs query, optional maxDepth default 3), "pattern" (plain regex/text search fallback for non-code content, needs query). Every response names the \`backend\` that answered: "typescript" means results were resolved through the real type checker and each reference carries the declaration it resolves to; "tree-sitter" means results are identifier-name matches (never inside a string or comment, unlike grep) that cannot distinguish two same-named symbols in different scopes \u2014 treat those as candidates, not proof, and treat "impact" tails as over-approximate.`
68
+ },
69
+ {
70
+ name: "project_memory",
71
+ title: "Project Memory",
72
+ description: `Records and recalls this project's decisions, mistakes, and constraints as plain markdown under .vovy/memory/, committed to git \u2014 so rationale survives across sessions, tools, and teammates with no server or account. Actions: "record" (save an entry; needs type: "decision" | "mistake" | "constraint", title, body \u2014 for decisions include what was REJECTED and why, for mistakes include why it happened and how to avoid it, for constraints include the why behind the rule), "recall" (deterministic keyword search over saved entries; needs query \u2014 use BEFORE starting non-trivial work and before revisiting any past choice), "list" (every entry's title and type, no bodies). Never pass secrets, API keys, or passwords in a body \u2014 entries are committed to git, and record refuses content that looks like a credential.`
66
73
  }
67
74
  ];
68
75
  function estimateTokens(text) {
@@ -83,6 +90,26 @@ function computeTokenFootprint() {
83
90
  totalEstTokens: skillsEstTokens + toolsEstTokens
84
91
  };
85
92
  }
93
+ function probeContextBackend(cwd) {
94
+ if (process.env.VOVY_CONTEXT_BACKEND === "tree-sitter") {
95
+ return {
96
+ backend: "tree-sitter",
97
+ reason: "forced by VOVY_CONTEXT_BACKEND \u2014 name-matching only, unset to restore scope-aware search"
98
+ };
99
+ }
100
+ try {
101
+ createRequire(join(cwd, "package.json")).resolve("typescript");
102
+ return {
103
+ backend: "typescript",
104
+ reason: "this project's own TypeScript \u2014 scope-aware, references name the declaration they resolve to"
105
+ };
106
+ } catch {
107
+ return {
108
+ backend: "tree-sitter",
109
+ reason: "no typescript resolvable from this project \u2014 name matches only; `npm i -D typescript` upgrades search_codebase to scope-aware results"
110
+ };
111
+ }
112
+ }
86
113
  function runDoctor(env, hosts, scope) {
87
114
  const dryRunResults = runInstall({ env, hosts, scope, dryRun: true });
88
115
  const totalSkillCount = getAllSkills2().length;
@@ -103,11 +130,42 @@ function runDoctor(env, hosts, scope) {
103
130
  healthy: entries.length === totalSkillCount && entries.every((e) => e.status === "ok") && (mcp === null || mcp.status === "ok")
104
131
  };
105
132
  });
106
- return { reports, tokenFootprint: computeTokenFootprint() };
133
+ return {
134
+ reports,
135
+ tokenFootprint: computeTokenFootprint(),
136
+ contextBackend: probeContextBackend(env.cwd)
137
+ };
138
+ }
139
+
140
+ // src/commands/statusline.ts
141
+ import { existsSync, readdirSync } from "fs";
142
+ import { join as join2 } from "path";
143
+ function buildStatusline(env) {
144
+ const { reports, contextBackend } = runDoctor(env);
145
+ const healthy = reports.filter((r) => r.healthy).length;
146
+ const skills = reports.length === 0 ? "not installed" : `skills ${healthy}/${reports.length} host${reports.length === 1 ? "" : "s"} ok`;
147
+ const parts = [`[vovy] ${skills}`, `engine:${contextBackend.backend}`];
148
+ const memoryCount = countMemoryEntries(env.cwd);
149
+ if (memoryCount > 0) parts.push(`memory:${memoryCount}`);
150
+ return parts.join(" \xB7 ");
151
+ }
152
+ function countMemoryEntries(cwd) {
153
+ const memoryDir = join2(cwd, ".vovy", "memory");
154
+ if (!existsSync(memoryDir)) return 0;
155
+ let count = 0;
156
+ for (const typeDir of ["decisions", "mistakes", "constraints"]) {
157
+ const dir = join2(memoryDir, typeDir);
158
+ if (!existsSync(dir)) continue;
159
+ try {
160
+ count += readdirSync(dir).filter((f) => f.endsWith(".md")).length;
161
+ } catch {
162
+ }
163
+ }
164
+ return count;
107
165
  }
108
166
 
109
167
  // src/commands/uninstall.ts
110
- import { existsSync, readFileSync, rmSync, writeFileSync } from "fs";
168
+ import { existsSync as existsSync2, readFileSync, rmSync, writeFileSync } from "fs";
111
169
  import {
112
170
  removeCodexMcpEntry,
113
171
  removeJsonMcpEntry
@@ -122,14 +180,14 @@ function runUninstall(opts) {
122
180
  const removedSkillPaths = [];
123
181
  for (const skill of SKILL_MANIFEST) {
124
182
  const path = adapter.skillFilePath(opts.env, scope, skill.id);
125
- if (existsSync(path)) {
183
+ if (existsSync2(path)) {
126
184
  removedSkillPaths.push(path);
127
185
  if (!dryRun) rmSync(path, { force: true });
128
186
  }
129
187
  }
130
188
  let removedMcpEntry = null;
131
189
  const mcpPath = adapter.mcpConfigPath(opts.env, scope);
132
- if (mcpPath && existsSync(mcpPath)) {
190
+ if (mcpPath && existsSync2(mcpPath)) {
133
191
  const existing = readFileSync(mcpPath, "utf8");
134
192
  const next = adapter.id === "codex" ? removeCodexMcpEntry(existing, VOVY_ID) : removeJsonMcpEntry(existing, VOVY_ID);
135
193
  if (next !== null) {
@@ -154,6 +212,7 @@ Usage:
154
212
  npx @vovy-ai/go install [options] Write Vovy's skills into detected (or specified) host tools
155
213
  npx @vovy-ai/go doctor [options] Check whether Vovy is correctly installed, without changing anything
156
214
  npx @vovy-ai/go uninstall [options] Remove everything Vovy's installer wrote
215
+ npx @vovy-ai/go statusline One-line status for a host's status bar (engine, skills, memory)
157
216
 
158
217
  Options:
159
218
  --host <ids> Comma-separated host ids to target instead of auto-detecting.
@@ -220,12 +279,24 @@ ${adapter.label}:`);
220
279
  console.log(
221
280
  flags.dryRun ? "\nDry run only \u2014 nothing was written. Re-run without --dry-run to apply." : "\nDone. Vovy's skills are now available in your tool(s) above."
222
281
  );
282
+ if (!flags.dryRun && reports.some((r) => r.adapter.id === "claude-code")) {
283
+ console.log(
284
+ `
285
+ Optional: a Vovy status-bar badge for Claude Code (shows engine + memory at a glance).
286
+ Add to ~/.claude/settings.json:
287
+ "statusLine": { "type": "command", "command": "npx -y @vovy-ai/go statusline" }
288
+ (Faster variant: npm i -g @vovy-ai/go, then use "vovy statusline" as the command.)`
289
+ );
290
+ }
291
+ }
292
+ function cmdStatusline() {
293
+ console.log(buildStatusline(realEnv()));
223
294
  }
224
295
  function cmdDoctor(argv) {
225
296
  const flags = parseCommonFlags(argv);
226
297
  if (flags.help) return console.log(HELP);
227
298
  const env = realEnv();
228
- const { reports, tokenFootprint } = runDoctor(env, flags.hosts, flags.scope);
299
+ const { reports, tokenFootprint, contextBackend } = runDoctor(env, flags.hosts, flags.scope);
229
300
  if (reports.length === 0) {
230
301
  console.log("No supported host tools detected on this machine \u2014 nothing to check.");
231
302
  return;
@@ -244,6 +315,10 @@ ${adapter.label}: ${healthy ? "OK" : "needs `npx @vovy-ai/go install`"}`);
244
315
  }
245
316
  console.log(
246
317
  `
318
+ search_codebase backend for this directory: ${contextBackend.backend} (${contextBackend.reason})`
319
+ );
320
+ console.log(
321
+ `
247
322
  Estimated always-on token footprint: ~${tokenFootprint.totalEstTokens} tokens (${tokenFootprint.skillCount} skill files ~${tokenFootprint.skillsEstTokens}, ${tokenFootprint.toolCount} MCP tool definitions ~${tokenFootprint.toolsEstTokens}) \u2014 what every session pays whether or not a skill fires. chars/4 estimate, not an exact tokenizer count.`
248
323
  );
249
324
  process.exitCode = allHealthy ? 0 : 1;
@@ -284,6 +359,8 @@ async function main() {
284
359
  return cmdDoctor(rest);
285
360
  case "uninstall":
286
361
  return cmdUninstall(rest);
362
+ case "statusline":
363
+ return cmdStatusline();
287
364
  case "--help":
288
365
  case "-h":
289
366
  case void 0:
package/package.json CHANGED
@@ -1,8 +1,39 @@
1
1
  {
2
2
  "name": "@vovy-ai/go",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Free, forever, drop-in MCP server + skill pack that teaches non-technical founders to vibe code safely. npx @vovy-ai/go install",
5
+ "keywords": [
6
+ "vibe-coding",
7
+ "ai-coding-assistant",
8
+ "claude-code",
9
+ "codex-cli",
10
+ "cursor",
11
+ "cline",
12
+ "windsurf",
13
+ "mcp",
14
+ "mcp-server",
15
+ "model-context-protocol",
16
+ "claude",
17
+ "llm-agent",
18
+ "ai-agent",
19
+ "coding-agent",
20
+ "prompt-engineering",
21
+ "tree-sitter",
22
+ "code-search",
23
+ "developer-tools",
24
+ "cli"
25
+ ],
5
26
  "license": "MIT",
27
+ "homepage": "https://github.com/Vovy-AI/vovy-cli#readme",
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "git+https://github.com/Vovy-AI/vovy-cli.git",
31
+ "directory": "packages/cli"
32
+ },
33
+ "bugs": {
34
+ "url": "https://github.com/Vovy-AI/vovy-cli/issues"
35
+ },
36
+ "author": "Vovy AI",
6
37
  "type": "module",
7
38
  "bin": {
8
39
  "vovy": "./dist/index.js"
@@ -11,13 +42,14 @@
11
42
  "dist"
12
43
  ],
13
44
  "dependencies": {
14
- "@vovy-ai/skills": "0.3.0",
15
- "@vovy-ai/host-detect": "0.1.1"
45
+ "@vovy-ai/host-detect": "0.1.1",
46
+ "@vovy-ai/skills": "0.4.0"
16
47
  },
17
48
  "devDependencies": {
18
49
  "tsup": "^8.3.5",
19
50
  "typescript": "^5.7.2",
20
- "vitest": "^2.1.8"
51
+ "vitest": "^2.1.8",
52
+ "@vovy-ai/mcp-server": "0.3.0"
21
53
  },
22
54
  "scripts": {
23
55
  "build": "tsup src/index.ts --format esm --dts --clean",