Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 126 additions & 30 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,51 +1,147 @@
# Veryfront Renderer
# Veryfront — Agent Guide

Deno-based React meta-framework with SSR/RSC and AI-native capabilities.
Veryfront is a full-stack web framework with CLI tooling for dev, build, deploy, and AI features. Runtime: **Deno**.

## Commands
## Quick Start

See `deno.json` for all tasks. Key ones:
```bash
# Command discovery
veryfront schema --json

# MCP server (for Claude Code, Cursor, etc.)
veryfront mcp

# Run tests
VF_DISABLE_LRU_INTERVAL=1 SSR_TRANSFORM_PER_PROJECT_LIMIT=0 REVALIDATION_PER_PROJECT_LIMIT=0 \
NODE_ENV=production LOG_FORMAT=text \
deno test --no-check --allow-all --unstable-worker-options --unstable-net

# Unit tests only (parallel, excludes integration)
deno test --no-check --allow-all --parallel \
'--ignore=tests,src/ai/workflow/__tests__,src/cli/commands/*.integration.test.ts'
```

## CLI Usage

### Structured Output

All commands support `--json` for machine-readable output:

```bash
deno task dev # Development server
deno task test # Run tests
deno task verify # Full CI check
veryfront deploy --json
veryfront doctor --json
veryfront whoami --json
```

**Success envelope:**
```json
{ "success": true, "command": "deploy", "data": { ... }, "timing": { "duration_ms": 3200 } }
```

**Error envelope:**
```json
{ "success": false, "command": "deploy", "error": { "code": "PERMISSION_ERROR", "slug": "deploy-not-authorized", "message": "...", "context": {} } }
```

### Global Flags

| Flag | Short | Description |
|------|-------|-------------|
| `--json` | `-j` | Structured JSON output |
| `--output <path>` | `-o` | Write JSON output to file |
| `--yes` | `-y` | Skip confirmation prompts (auto-detected in CI) |
| `--quiet` | `-q` | Suppress non-essential output |
| `--verbose` | | Enable debug logging |
| `--no-color` | | Disable color output |

### Exit Codes

| Code | Meaning |
|------|---------|
| 0 | Success |
| 1 | Error |
| 2 | Usage error |
| 130 | Interrupted (SIGINT) |

## MCP Connection

**stdio (local editors):**
```bash
veryfront mcp
```

**HTTP (remote access):**
MCP auto-starts on port 9999 with the dev server (`veryfront dev`).

**Key tools:** `vf_list_templates`, `vf_create_project`, `vf_scaffold`, `vf_get_errors`, `vf_get_project_context`, `vf_list_routes`, `vf_get_conventions`, `vf_get_component_tree`, `vf_hot_reload`

**Resources:** `veryfront://skill`, `veryfront://errors`, `veryfront://logs`, `veryfront://schema`, `veryfront://agents-md`

## Architecture

See `src/README.md` for module documentation.
```
src/ # Core framework modules
├── ai/ # AI workflows, tool definitions
├── build/ # Build pipeline
├── config/ # Configuration resolution
├── errors/ # VeryfrontError registry
├── mcp/ # MCP protocol types
├── platform/ # Deno/Node platform abstraction
├── provider/ # AI model providers
└── ...

cli/ # CLI layer (see cli/AGENTS.md for details)
├── router.ts # Command dispatch
├── commands/ # Individual commands (handler.ts + command.ts + command-help.ts)
├── mcp/ # MCP server + tools
├── help/ # Help system with categories
├── shared/ # Args, JSON output, interactive mode
├── auth/ # login, logout, whoami
├── ui/ # Colors, spinners
└── utils/ # Logger, VERSION, prompts
```

### Hash Imports

## Imports
- `#veryfront/` — src modules (e.g., `#veryfront/errors`, `#veryfront/config`)
- `#cli/` — CLI modules (e.g., `#cli/shared/args`, `#cli/utils`)
- `veryfront/` — public API surface (e.g., `veryfront/platform`, `veryfront/mcp`)

Import map defined in `deno.json`.

## Contributing Conventions

### Error Handling

Use the `VeryfrontError` registry pattern:

Use `#veryfront/*` for internal imports:
```typescript
import { foo } from "#veryfront/utils";
import { defineError } from "#veryfront/errors";
const MY_ERROR = defineError("my-error-slug", ErrorCode.CONFIG_ERROR, 400);
throw MY_ERROR.create("Something went wrong", { detail: "extra context" });
```

## Environment Variables
Match errors with: `error instanceof VeryfrontError && error.slug === "my-error-slug"`

```bash
VERYFRONT_DEBUG=1 # Enable debug logging
PROXY_MODE=1 # Enable proxy mode
VERYFRONT_API_TOKEN=vf_... # API token
VERYFRONT_PROJECT_SLUG=... # Project slug
PRODUCTION_MODE=1 # Production mode
```
### Tests

## Testing
- BDD style: `describe()` / `it()` from `#veryfront/testing/bdd.ts`
- Assertions from `#veryfront/testing/assert.ts`
- Test files: colocated as `*.test.ts` next to source

### Arg Parsing

```typescript
// Use withTestContext for automatic cleanup
await withTestContext("my-test", async (ctx) => {
const server = await ctx.startDevServer({ port });
});
import { z } from "zod";
import { createArgParser, CommonArgs } from "#cli/shared/args";

const Schema = z.object({ force: z.boolean().default(false) });
const parseArgs = createArgParser(Schema, { force: CommonArgs.force });
```

## Troubleshooting
### Command Structure

| Issue | Fix |
|-------|-----|
| Module not found | Clear `.cache/` |
| Test timeout | Check for hanging promises |
| Port in use | `lsof -i :3001` |
Each command lives in `cli/commands/{name}/` with:
- `handler.ts` — entry point, signature: `(args: ParsedArgs) => Promise<void>`
- `command.ts` — Zod schema, arg parser, business logic
- `command-help.ts` — `CommandHelp` object with name, category, description, usage, options, examples
109 changes: 109 additions & 0 deletions cli/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# CLI Module — Agent Guide

The `cli/` directory is the entry layer for the Veryfront CLI. All user-facing commands, help output, MCP server, and shared utilities live here.

## Directory Structure

```
cli/
├── router.ts # Command dispatch — maps command names to handlers
├── main.ts # CLI entry point — parses argv, calls routeCommand()
├── shared/
│ ├── args.ts # Arg parsing: createArgParser(), CommonArgs, parseCliArgs()
│ ├── types.ts # ParsedArgs interface
│ ├── json-output.ts # JSON envelope utilities (isJsonMode, outputJson, etc.)
│ ├── interactive.ts # --yes / CI detection (isInteractive, detectCI)
│ ├── config.ts # Project config resolution + API client
│ └── slug.ts # Project slug utilities
├── commands/
│ └── {name}/
│ ├── handler.ts # Entry: parseArgs → call command function
│ ├── command.ts # Business logic + Zod schema
│ ├── command-help.ts # Help definition (CommandHelp object)
│ └── handler.test.ts # Tests
├── help/
│ ├── types.ts # CommandHelp, CommandCategory, CommandOption
│ ├── command-definitions.ts # Central COMMANDS registry
│ ├── main-help.ts # Main help output (grouped by category)
│ └── formatters.ts # Terminal formatting utilities
├── mcp/
│ ├── server.ts # MCP server (stdio + HTTP)
│ ├── tools.ts # Core MCP tools
│ └── advanced-tools.ts # Extended MCP tools registry
├── auth/ # login, logout, whoami
├── ui/ # Colors, spinners, box drawing
└── utils/ # Logger, VERSION, confirmPrompt, exitProcess
```

## Adding a New Command

1. **Create command directory:** `cli/commands/{name}/`
2. **Define help** in `command-help.ts`:
```typescript
import type { CommandHelp } from "../../help/types.ts";
export const fooHelp: CommandHelp = {
name: "foo",
category: "development", // development | deploy | project | files | ai | auth
description: "...",
usage: "veryfront foo [options]",
options: [{ flag: "--bar", description: "..." }],
examples: ["veryfront foo --bar"],
};
```
3. **Define args + logic** in `command.ts` using Zod + `createArgParser`:
```typescript
const FooSchema = z.object({ bar: z.boolean().default(false) });
export const parseFooArgs = createArgParser(FooSchema, {
bar: { keys: ["bar"], type: "boolean" },
});
```
4. **Create handler** in `handler.ts`:
```typescript
export async function handleFooCommand(args: ParsedArgs): Promise<void> {
const opts = parseArgsOrThrow(parseFooArgs, "foo", args);
// ... command logic
}
```
5. **Register** in `cli/router.ts`: import + add to `commands` record
6. **Register help** in `cli/help/command-definitions.ts`: import + add to `COMMANDS`

## Global Flags

Handled in `routeCommand()` in `router.ts`:
- `--json` / `-j` → `setJsonMode(true)` — enables structured JSON output
- `--output` / `-o` → `setOutputPath(path)` — write JSON to file
- `--yes` / `-y` → `setNonInteractive(true)` — skip prompts (auto-detected in CI)
- `--quiet` / `-q`, `--verbose`, `--no-color`, `--color`, `--help`, `--version`

## JSON Output Pattern

Commands that support `--json` use the envelope format:
```typescript
import { isJsonMode, outputJson, createSuccessEnvelope } from "../../shared/json-output.ts";

if (isJsonMode()) {
await outputJson(createSuccessEnvelope("command-name", { key: "value" }));
return;
}
```

## Arg Parsing

- Single-char aliases defined in `parseCliArgs()` in `shared/args.ts`
- Reusable specs in `CommonArgs` (force, dryRun, branch, env, projectSlug, etc.)
- New aliases: add to the `alias` object in `parseCliArgs()`

## Testing

- Test file: `cli/commands/{name}/handler.test.ts`
- Run single: `VF_DISABLE_LRU_INTERVAL=1 deno test --no-check --allow-all cli/commands/{name}/`
- Run all CLI: `VF_DISABLE_LRU_INTERVAL=1 deno test --no-check --allow-all --parallel '--ignore=tests,src/ai/workflow/__tests__,src/cli/commands/*.integration.test.ts' cli/`
- BDD style: `describe()` / `it()` from `#veryfront/testing/bdd.ts`
- Assertions: `assertEquals`, `assertRejects`, etc. from `#veryfront/testing/assert.ts`

## Common Mistakes

- Forgetting to register in **both** `router.ts` AND `command-definitions.ts`
- Using wrong import path for shared modules (use `#cli/shared/args` across module boundaries, relative within `cli/`)
- Missing `category` field in command-help (required by `CommandHelp` interface)
- Not handling JSON mode in new commands (check `isJsonMode()` before human-readable output)
14 changes: 14 additions & 0 deletions cli/auth/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { canOpenBrowser, openBrowser } from "./browser.ts";
import { isTTY, promptUser } from "../utils/index.ts";
import { brand, dim, error, muted, success, warning } from "../ui/colors.ts";
import { DEFAULT_LOGIN_TIMEOUT_MS, getApiUrl } from "../shared/constants.ts";
import { createSuccessEnvelope, isJsonMode, outputJson } from "../shared/json-output.ts";

export type AuthMethod = "google" | "github" | "microsoft" | "token";

Expand Down Expand Up @@ -250,6 +251,10 @@ export async function whoami(
if (env.apiToken) {
const userInfo = await validateToken(env.apiToken);
if (userInfo) {
if (isJsonMode()) {
await outputJson(createSuccessEnvelope("whoami", { ...userInfo, source: "env" }));
return userInfo;
}
console.log();
console.log(" " + success("✓") + " Logged in as " + brand(userInfo.email));
console.log(" " + dim("(via VERYFRONT_API_TOKEN)"));
Expand All @@ -261,13 +266,22 @@ export async function whoami(
if (storedToken) {
const userInfo = await validateToken(storedToken);
if (userInfo) {
if (isJsonMode()) {
await outputJson(createSuccessEnvelope("whoami", { ...userInfo, source: "token-store" }));
return userInfo;
}
console.log();
console.log(" " + success("✓") + " Logged in as " + brand(userInfo.email));
console.log(" " + dim(`Token stored at: ${getTokenLocation()}`));
return userInfo;
}
}

if (isJsonMode()) {
await outputJson(createSuccessEnvelope("whoami", { authenticated: false }));
return null;
}

console.log();
console.log(" " + warning("✗") + " Not logged in");
console.log(" " + dim("Run 'veryfront login' to authenticate"));
Expand Down
1 change: 1 addition & 0 deletions cli/commands/analyze-chunks/command-help.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { CommandHelp } from "../../help/types.ts";

export const analyzeChunksHelp: CommandHelp = {
name: "analyze-chunks",
category: "development",
description: "Analyze bundle chunks and sizes",
usage: "veryfront analyze-chunks [options]",
options: [
Expand Down
1 change: 1 addition & 0 deletions cli/commands/build/command-help.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { CommandHelp } from "../../help/types.ts";

export const buildHelp: CommandHelp = {
name: "build",
category: "development",
description: "Build your application for production",
usage: "veryfront build [options]",
options: [
Expand Down
Loading
Loading