Skip to content

Commit f73c142

Browse files
authored
Merge pull request #3 from yavydev/develop
feat: add yavy project create command
2 parents fa198a3 + 9dcf6cf commit f73c142

29 files changed

+2442
-26
lines changed

.github/workflows/tests.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,5 @@ jobs:
2222

2323
- run: npm ci
2424
- run: npm run typecheck
25+
- run: npm run lint
2526
- run: npm run test

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ dist
44
.omc
55
*.log
66
*.tgz
7+
.claude

CLAUDE.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# @yavydev/cli
2+
3+
CLI for searching, managing, and configuring AI-ready documentation on Yavy.
4+
5+
## Commands
6+
7+
```bash
8+
npm install # Install dependencies
9+
npm run build # Build with tsup
10+
npm run dev # Build in watch mode
11+
npm run check # Run all checks (typecheck + lint + format + test)
12+
npm run typecheck # TypeScript strict type checking
13+
npm run lint # ESLint with typescript-eslint
14+
npm run lint:fix # ESLint auto-fix
15+
npm run test # Run tests (vitest)
16+
npm run format:check # Check formatting (prettier)
17+
npm run format # Fix formatting
18+
```
19+
20+
## Architecture
21+
22+
The CLI is built on Commander.js with a modular command structure. Each command is a factory function returning a `Command` instance, registered in the entry point.
23+
24+
- **Commands** - one directory or file per command group. Each exports a factory.
25+
- **API Client** - token-based HTTP client with retry logic and exponential backoff.
26+
- **Auth** - OAuth2 PKCE flow with local callback server; credentials stored in `~/.yavy/`.
27+
- **Prompts** - interactive flows using @clack/prompts for multi-select and @inquirer/prompts for input/select.
28+
29+
See [docs/architecture.md](docs/architecture.md) for details.
30+
31+
## Key Design Decisions
32+
33+
- `@/` path aliases throughout (configured in tsconfig, tsup, vitest).
34+
- Strict TypeScript: `strict`, `noUncheckedIndexedAccess`, `noUnusedLocals`, `noUnusedParameters`, `noFallthroughCasesInSwitch`.
35+
- ESLint with typescript-eslint: `consistent-type-imports`, `no-floating-promises`, `no-explicit-any`.
36+
- Commands set `process.exitCode` instead of calling `process.exit()` directly - keeps code testable.
37+
- Two auth patterns coexist: OAuth (login flow) and token-based (API token via env or config file).
38+
- Interactive mode activates when required CLI flags are missing; flags always take precedence.
39+
40+
## After Changing Commands
41+
42+
- Register new commands in the entry point.
43+
- Add the command to README.md under the Commands section.
44+
- If adding a new command group, create a directory under `src/commands/`.
45+
46+
## After Changing the API Client
47+
48+
- Update tests that mock fetch globally.
49+
- If adding new response types, define them alongside existing API interfaces in the client module.
50+
51+
## Documentation
52+
53+
- [Architecture](docs/architecture.md) - layers, data flow, design decisions
54+
- [README](README.md) - install, quick start, command reference

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,22 @@ Lists all projects you have access to across your organizations.
6363
| -------- | -------------- |
6464
| `--json` | Output as JSON |
6565

66+
### `yavy project create`
67+
68+
Create a new documentation project. Runs interactively when `--url` or `--github` is omitted.
69+
70+
| Flag | Description |
71+
| ----------------- | ---------------------------------------- |
72+
| `--url <url>` | Documentation URL (web crawl source) |
73+
| `--github <repo>` | GitHub repository (e.g. laravel/docs) |
74+
| `--org <slug>` | Organization slug |
75+
| `--name <name>` | Project name (auto-generated if omitted) |
76+
| `--public` | Make project public (default) |
77+
| `--private` | Make project private |
78+
| `--branch <name>` | GitHub branch override |
79+
| `--docs-path <p>` | GitHub docs path |
80+
| `--no-sync` | Skip initial auto-sync |
81+
6682
### `yavy generate <org/project>`
6783

6884
Downloads a skill from a project's indexed documentation.

docs/architecture.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Architecture
2+
3+
How the CLI is structured, where things live, and why.
4+
5+
## Layers
6+
7+
### Entry Point
8+
9+
`bin/yavy.js` is the shebang entry that loads the built output. The source entry registers all commands with Commander.js and calls `parseAsync()`. Top-level error handler catches unhandled rejections and exits with code 1.
10+
11+
### Commands
12+
13+
Each command is a factory function that returns a configured `Command`. Commands live in `src/commands/` - either as single files (login, logout, search) or directories when they have submodules (init, project).
14+
15+
Command factories wire up flags, descriptions, and an async action handler. The action handler orchestrates: validate inputs, call API, format output. No business logic lives in the action - it delegates to dedicated modules.
16+
17+
### API Client
18+
19+
Single HTTP client class handles all Yavy API communication. Key behaviors:
20+
21+
- Bearer token auth (loaded from credential store or environment)
22+
- Retry with exponential backoff on 429/502/503/504
23+
- Request timeout with AbortController
24+
- Typed response parsing
25+
26+
### Authentication
27+
28+
Two paths:
29+
30+
1. **OAuth PKCE** - `yavy login` opens browser, spawns local HTTP server for callback, exchanges code for token. Credentials persisted to `~/.yavy/credentials.json` with 0600 permissions. Auto-refresh when token nears expiry.
31+
2. **Token-based** - `YAVY_API_TOKEN` env variable or `~/.yavy/config.json`. Used by the project creation flow and CI environments.
32+
33+
### Interactive Prompts
34+
35+
When required CLI flags are missing, commands fall back to interactive mode. Prompts collect the missing values, then merge with any flags that were provided. Two prompt libraries are in use: @clack/prompts (multi-select, spinners) and @inquirer/prompts (input, select).
36+
37+
### Utilities
38+
39+
- **Output** - colored terminal helpers (success, error, warn, info) using chalk
40+
- **Paths** - skill output directories, zip-slip prevention, safe directory creation
41+
- **Errors** - API error formatting that maps HTTP status codes to actionable messages
42+
43+
## Data Flow
44+
45+
```
46+
User runs command
47+
-> Commander parses flags
48+
-> Command action handler runs
49+
-> If missing flags: interactive prompts fill them in
50+
-> API client makes request (with retry)
51+
-> Response formatted and printed to stdout
52+
-> Errors caught, formatted, printed to stderr
53+
```
54+
55+
## Build & Distribution
56+
57+
tsup bundles to ESM targeting Node 20+. The dist includes declarations and source maps. Published to npm as `@yavydev/cli` with `dist/` and `bin/` in the package.
58+
59+
## Testing Strategy
60+
61+
Vitest with globals enabled. Tests mock at module boundaries - the API client, prompts, and filesystem are mocked; pure functions (payload builders, error formatters, org extractors) are tested directly. Console output is verified via spies on console.log/console.error.

eslint.config.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import eslint from '@eslint/js';
2+
import tseslint from 'typescript-eslint';
3+
4+
export default tseslint.config(
5+
eslint.configs.recommended,
6+
...tseslint.configs.recommended,
7+
{
8+
languageOptions: {
9+
parserOptions: {
10+
projectService: true,
11+
tsconfigRootDir: import.meta.dirname,
12+
},
13+
},
14+
rules: {
15+
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
16+
'@typescript-eslint/no-explicit-any': 'error',
17+
'@typescript-eslint/consistent-type-imports': 'error',
18+
'@typescript-eslint/no-floating-promises': 'error',
19+
},
20+
},
21+
{
22+
ignores: ['dist/', 'node_modules/', '*.config.*'],
23+
},
24+
);

0 commit comments

Comments
 (0)