Skip to content

Commit d6aaad3

Browse files
committed
fix(#152): harden cli - limit validation, error isolation, stale metadata
1 parent 4c18c5e commit d6aaad3

File tree

8 files changed

+59
-23
lines changed

8 files changed

+59
-23
lines changed

README.md

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,52 @@
11
# Yavy CLI
22

3-
Generate AI skills from your indexed documentation on [Yavy](https://yavy.dev).
3+
Search and manage your AI-ready documentation on [Yavy](https://yavy.dev).
44

55
## Installation
66

77
```bash
88
npm install -g @yavydev/cli
99
```
1010

11-
Requires Node.js >= 18.
11+
Requires Node.js >= 20.
1212

1313
## Quick Start
1414

1515
```bash
16-
# Authenticate with your Yavy account
16+
# Interactive setup: authenticate, select projects, configure AI tools
17+
yavy init
18+
19+
# Or authenticate manually
1720
yavy login
1821

22+
# Search your indexed documentation
23+
yavy search "how do I get started?"
24+
1925
# List your projects
2026
yavy projects
21-
22-
# Generate a skill for a project
23-
yavy generate my-org/my-project
2427
```
2528

2629
## Commands
2730

31+
### `yavy init`
32+
33+
Interactive setup wizard that authenticates, selects projects, and configures your AI tools (skills + MCP config) in one step.
34+
35+
| Flag | Description |
36+
| --------------- | ------------------------------------------------------- |
37+
| `--tool <name>` | Configure a specific tool only |
38+
| `--yes` | Non-interactive mode: all detected tools + all projects |
39+
40+
### `yavy search <query>`
41+
42+
Search your indexed documentation directly from the terminal.
43+
44+
| Flag | Description |
45+
| ------------------------- | ---------------------------------- |
46+
| `--project <org/project>` | Scope search to a specific project |
47+
| `--limit <number>` | Maximum results (1-20, default 10) |
48+
| `--json` | Output as JSON |
49+
2850
### `yavy login`
2951

3052
Opens your browser to authenticate with your Yavy account using OAuth (PKCE). Credentials are stored in `~/.yavy/credentials.json`.
@@ -57,8 +79,8 @@ By default, skills are saved to `.claude/skills/<project>/` in the current direc
5779
## How It Works
5880

5981
1. Yavy indexes your documentation sources (websites, GitHub repos, Confluence, Notion)
60-
2. The CLI calls the Yavy API to download a skill using the indexed content
61-
3. The skill file is saved locally for your AI coding tools to discover
82+
2. The CLI calls the Yavy API to search or download skills using the indexed content
83+
3. Skills and MCP configs are saved locally for your AI coding tools to discover
6284
4. AI coding assistants automatically activate the skill when working with relevant code
6385

6486
## Configuration
@@ -71,7 +93,7 @@ By default, skills are saved to `.claude/skills/<project>/` in the current direc
7193
## Related
7294

7395
- [Yavy Claude Code Plugin](https://github.com/yavydev/claude-code) — Claude Code plugin with interactive setup
74-
- [Yavy](https://yavy.dev) — Index documentation, generate AI skills
96+
- [Yavy](https://yavy.dev) — Index documentation, search with AI
7597

7698
## License
7799

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@yavydev/cli",
33
"version": "0.1.0",
4-
"description": "Generate AI skills from your indexed documentation on Yavy",
4+
"description": "Search and manage your AI-ready documentation on Yavy",
55
"type": "module",
66
"bin": {
77
"yavy": "./bin/yavy.js"
@@ -13,8 +13,8 @@
1313
"typecheck": "tsc --noEmit",
1414
"test": "vitest run",
1515
"test:watch": "vitest",
16-
"format": "prettier --write \"src/**/*.{ts,js}\" \"*.{json,md}\"",
17-
"format:check": "prettier --check \"src/**/*.{ts,js}\" \"*.{json,md}\""
16+
"format": "prettier --write \"src/**/*.{ts,js}\" \"tests/**/*.{ts,js}\" \"*.{json,md}\"",
17+
"format:check": "prettier --check \"src/**/*.{ts,js}\" \"tests/**/*.{ts,js}\" \"*.{json,md}\""
1818
},
1919
"keywords": [
2020
"yavy",

src/commands/init.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import chalk from 'chalk';
33
import { Command } from 'commander';
44
import { type ApiProject, YavyApiClient } from '@/api/client';
55
import { getAccessToken } from '@/auth/store';
6-
import { error } from '@/utils';
7-
import { configureTool } from '@/commands/init/configure-tool';
6+
import { error, warn } from '@/utils';
7+
import { configureTool, type ConfigureResult } from '@/commands/init/configure-tool';
88
import { resolveToolFromFlag, scanForTools } from '@/commands/init/scan-tools';
99
import { AiTool, TOOL_CONFIGS, type InitOptions } from '@/commands/init/types';
1010

@@ -53,7 +53,14 @@ async function runInit(options: InitOptions): Promise<void> {
5353
const s = p.spinner();
5454
s.start('Configuring tools...');
5555

56-
const results = selectedTools.map((tool) => configureTool(tool, selectedProjects, process.cwd()));
56+
const results: ConfigureResult[] = [];
57+
for (const tool of selectedTools) {
58+
try {
59+
results.push(configureTool(tool, selectedProjects, process.cwd()));
60+
} catch (err) {
61+
warn(`Failed to configure ${TOOL_CONFIGS[tool].name}: ${err instanceof Error ? err.message : String(err)}`);
62+
}
63+
}
5764

5865
s.stop('Tools configured.');
5966

src/commands/init/configure-tool.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type { ApiProject } from '@/api/client';
44
import { YAVY_BASE_URL } from '@/config';
55
import { generateProjectContent, generateSkillContent, slugify } from '@/commands/init/generate-skill';
66
import { type AiTool, TOOL_CONFIGS } from '@/commands/init/types';
7+
import { warn } from '@/utils';
78

89
export interface ConfigureResult {
910
tool: AiTool;
@@ -50,8 +51,8 @@ function buildMcpUrl(projects: ApiProject[]): string {
5051
const orgSlugs = [...new Set(projects.map((proj) => proj.organization.slug))];
5152

5253
if (orgSlugs.length > 1) {
53-
console.warn(
54-
`Warning: Projects span multiple organizations (${orgSlugs.join(', ')}). ` +
54+
warn(
55+
`Projects span multiple organizations (${orgSlugs.join(', ')}). ` +
5556
`MCP will be configured for "${orgSlugs[0]}" only. Run yavy init per organization for full coverage.`,
5657
);
5758
}

src/commands/search.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ export function searchCommand(): Command {
1313
.option('--json', 'Output as JSON')
1414
.action(async (query: string, options: { project?: string; limit: string; json?: boolean }) => {
1515
const limit = parseInt(options.limit, 10);
16+
17+
if (isNaN(limit) || limit < 1 || limit > 20) {
18+
error('--limit must be a number between 1 and 20');
19+
process.exit(1);
20+
}
21+
1622
const spinner = options.json ? null : ora('Searching...').start();
1723

1824
try {

src/config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
export const YAVY_BASE_URL = process.env.YAVY_BASE_URL ?? 'https://yavy.dev';
22
export const YAVY_CLIENT_ID = process.env.YAVY_CLIENT_ID ?? '01965e6a-0000-7000-8000-000000000001';
3-
export const YAVY_USER_AGENT = `@yavydev/cli`;
3+
export const YAVY_USER_AGENT = `@yavydev/cli/${process.env.npm_package_version ?? 'unknown'}`;
44
export const REQUEST_TIMEOUT_MS = 30_000;
55
export const MAX_RETRIES = 3;

tests/commands/init/configure-tool.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ describe('configureTool', () => {
177177
});
178178

179179
it('warns when projects span multiple organizations', () => {
180-
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
180+
const stderrSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
181181

182182
const projects = [
183183
makeProject({ organization: { name: 'Org A', slug: 'org-a' } }),
@@ -186,9 +186,9 @@ describe('configureTool', () => {
186186

187187
configureTool(AiTool.Cursor, projects, '/project');
188188

189-
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('multiple organizations'));
190-
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('org-a'));
189+
expect(stderrSpy).toHaveBeenCalledWith(expect.stringContaining('multiple organizations'));
190+
expect(stderrSpy).toHaveBeenCalledWith(expect.stringContaining('org-a'));
191191

192-
warnSpy.mockRestore();
192+
stderrSpy.mockRestore();
193193
});
194194
});

tsup.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { defineConfig } from 'tsup';
33
export default defineConfig({
44
entry: ['src/index.ts'],
55
format: ['esm'],
6-
target: 'node18',
6+
target: 'node20',
77
clean: true,
88
dts: true,
99
sourcemap: true,

0 commit comments

Comments
 (0)