Skip to content
14 changes: 12 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ A 24-tool MCP server for Claude Code that catches ambiguous instructions before
[![npm](https://img.shields.io/npm/v/preflight-dev)](https://www.npmjs.com/package/preflight-dev)
[![Node 18+](https://img.shields.io/badge/node-18%2B-brightgreen?logo=node.js&logoColor=white)](https://nodejs.org/)

[Quick Start](#quick-start) · [How It Works](#how-it-works) · [Tool Reference](#tool-reference) · [Configuration](#configuration) · [Scoring](#the-12-category-scorecard)
[Quick Start](#quick-start) · [How It Works](#how-it-works) · [Usage Examples](examples/USAGE-EXAMPLES.md) · [Tool Reference](#tool-reference) · [Configuration](#configuration) · [Scoring](#the-12-category-scorecard)

</div>

Expand Down Expand Up @@ -119,9 +119,11 @@ Restart Claude Code. The tools activate automatically.

```bash
npm install -g preflight-dev
claude mcp add preflight -- preflight-dev
claude mcp add preflight -- preflight-dev serve
```

> **Tip:** Running `preflight-dev` in a terminal (TTY) launches the interactive setup wizard. When piped (like `claude mcp add`), it auto-detects and starts the MCP server. You can also force either mode with `preflight-dev init` or `preflight-dev serve`.

---

## How It Works
Expand Down Expand Up @@ -372,6 +374,8 @@ Preflight understands that microservices share contracts. When your prompt menti

### Setup

> 💡 **Want a ready-to-use example?** Copy `examples/.preflight/` into your project root — it includes annotated `config.yml`, `triage.yml`, and sample contracts. See [`examples/.preflight/README.md`](examples/.preflight/README.md).

**Option 1: `.preflight/config.yml`** (recommended — committed to repo)

```yaml
Expand Down Expand Up @@ -562,6 +566,12 @@ flowchart TB

---

## Troubleshooting

Having issues? See **[TROUBLESHOOTING.md](TROUBLESHOOTING.md)** for solutions to common problems — LanceDB setup, missing projects, config loading, and more.

---

## Contributing

This project is young and there's plenty to do. Check the [issues](https://github.com/TerminalGravity/preflight/issues) — several are tagged `good first issue`.
Expand Down
172 changes: 172 additions & 0 deletions TROUBLESHOOTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
# Troubleshooting

Common issues and how to fix them.

---

## Server won't start

**Symptom:** `claude mcp add` succeeds but tools don't appear, or you see errors in Claude Code's MCP log.

**Fixes:**

1. **Check Node version.** Preflight requires Node 20+.
```bash
node --version # Must be v20.x or higher
```

2. **Missing dependencies.** If running from a clone:
```bash
cd /path/to/preflight && npm install
```

3. **Wrong path.** The `tsx` command needs the path to `src/index.ts` (dev) or use the npm binary:
```bash
# From source
claude mcp add preflight -- npx tsx /absolute/path/to/preflight/src/index.ts

# From npm
npm install -g preflight-dev
claude mcp add preflight -- preflight-dev
```

4. **Restart Claude Code** after adding or changing MCP config. Tools won't appear until restart.

---

## "No projects found" on search/timeline

**Symptom:** `search_history` or `timeline_view` returns "No projects found for scope."

**Cause:** `CLAUDE_PROJECT_DIR` isn't set, and no projects have been onboarded.

**Fix:** Set the env var in your `.mcp.json`:

```json
{
"mcpServers": {
"preflight": {
"command": "npx",
"args": ["tsx", "/path/to/preflight/src/index.ts"],
"env": {
"CLAUDE_PROJECT_DIR": "/path/to/your/project"
}
}
}
}
```

Or run the `onboard_project` tool first to register a project.

---

## LanceDB / vector search errors

**Symptom:** Errors mentioning LanceDB, `@lancedb/lancedb`, or embeddings when using `search_history`.

**Fixes:**

1. **Native dependency build.** LanceDB includes native code. If `npm install` fails on your platform:
```bash
# Make sure you have build tools
# macOS:
xcode-select --install
# Linux:
sudo apt install build-essential python3
```

2. **First-time indexing.** The timeline DB is created on first use. Run `onboard_project` to trigger initial indexing — this may take a moment for large session histories.

3. **Disk space.** LanceDB stores vectors in `~/.preflight/timeline/`. Check disk space if indexing fails silently.

---

## Embedding provider issues

**Symptom:** Search returns no results or poor matches.

**Default behavior:** Preflight uses a local embedding provider (no API key needed). For better quality, you can use OpenAI:

```bash
claude mcp add preflight \
-e EMBEDDING_PROVIDER=openai \
-e OPENAI_API_KEY=sk-... \
-- npx tsx /path/to/preflight/src/index.ts
```

Or set in `.preflight/config.yml`:

```yaml
embeddings:
provider: openai
model: text-embedding-3-small
```

---

## Tools appear but do nothing

**Symptom:** Tools are listed but `preflight_check` returns empty or generic responses.

**Likely cause:** The project directory doesn't have meaningful git history or session data yet.

**What to try:**

1. Make sure you're in a git repo with commits.
2. Use `preflight_check` with a real prompt — it needs actual text to triage.
3. Run `session_stats` to verify session data is accessible.

---

## `.preflight/` config not loading

**Symptom:** Custom triage rules or thresholds in `.preflight/config.yml` are ignored.

**Fixes:**

1. The `.preflight/` directory must be in your **project root** (where `CLAUDE_PROJECT_DIR` points).
2. File must be valid YAML. Validate with:
```bash
npx yaml < .preflight/config.yml
```
3. Check the [config example](/.preflight/) in this repo for correct structure.

---

## High token usage from preflight itself

**Symptom:** Preflight tools are consuming lots of tokens with long outputs.

**Fix:** Switch to the `minimal` profile:

```bash
claude mcp add preflight \
-e PROMPT_DISCIPLINE_PROFILE=minimal \
-- npx tsx /path/to/preflight/src/index.ts
```

Profiles: `strict` (most checks), `standard` (default), `minimal` (lightweight).

---

## Init wizard launches instead of MCP server

**Symptom:** Running `preflight-dev` via `claude mcp add` opens the interactive setup wizard instead of starting the MCP server.

**Cause:** Older versions of the bin entry always ran the init wizard regardless of context.

**Fix:** Update to the latest version and use `serve` to force server mode:

```bash
npm install -g preflight-dev@latest
claude mcp add preflight -- preflight-dev serve
```

The binary auto-detects TTY vs piped stdin, but `serve` makes it explicit. You can also force the wizard with `preflight-dev init`.

---

## Still stuck?

- Check [GitHub Issues](https://github.com/TerminalGravity/preflight/issues) for known bugs
- Open a new issue with your Node version, OS, and the error message
19 changes: 15 additions & 4 deletions bin/cli.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,22 @@
#!/usr/bin/env node
// This is a shim that loads the compiled TypeScript CLI
// Dual-mode entry point:
// - Interactive TTY → run the init wizard (preflight-dev init)
// - Piped / non-TTY → start the MCP server (used by `claude mcp add`)
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

// Load the compiled CLI
const cliPath = join(__dirname, '../dist/cli/init.js');
await import(cliPath);
const forceInit = process.argv.includes('init');
const forceServe = process.argv.includes('serve');

if (forceInit || (!forceServe && process.stdin.isTTY)) {
// Interactive: run setup wizard
const cliPath = join(__dirname, '../dist/cli/init.js');
await import(cliPath);
} else {
// Non-interactive (MCP stdio): start the server
const serverPath = join(__dirname, '../dist/index.js');
await import(serverPath);
}
43 changes: 43 additions & 0 deletions examples/.preflight/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# `.preflight/` Config Directory

Copy this directory to your project root to configure preflight for your team.

```
your-project/
├── .preflight/
│ ├── config.yml # Main config (profile, thresholds, related projects)
│ ├── triage.yml # Triage rules (keywords, strictness)
│ └── contracts/ # Manual contract definitions
│ └── api.yml # Example: shared API types and routes
├── src/
└── ...
```

## Getting Started

```bash
# From your project root:
cp -r /path/to/preflight/examples/.preflight .preflight

# Edit config.yml with your related project paths:
vim .preflight/config.yml

# Commit to share with your team:
git add .preflight && git commit -m "add preflight config"
```

## Files

| File | Purpose | Required? |
|------|---------|-----------|
| `config.yml` | Profile, related projects, thresholds, embedding config | No (sensible defaults) |
| `triage.yml` | Keyword rules and strictness for prompt classification | No (sensible defaults) |
| `contracts/*.yml` | Manual type/route/interface definitions | No (auto-extracted from code) |

All files are optional. Preflight works with zero config — these files let you tune it.

## Tips

- **Start minimal.** Drop in just `config.yml` with your `related_projects`. Add triage rules later as you see which prompts get misclassified.
- **Contracts are supplements.** Preflight auto-extracts types and routes from your code. Only add manual contracts for external services or planned interfaces.
- **Commit `.preflight/`.** The whole point is team-shareable configuration.
38 changes: 38 additions & 0 deletions examples/.preflight/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# .preflight/config.yml — Example configuration
# Copy this directory to your project root and customize.
#
# Every field is optional. Defaults are sensible for solo projects.
# For teams, commit this to your repo so everyone shares the same triage rules.

# Profile controls how much detail preflight adds to responses.
# "minimal" — only flags ambiguous+ prompts, skips clarification detail
# "standard" — balanced (default)
# "full" — maximum detail on every non-trivial prompt
profile: standard

# Related projects for cross-service contract awareness.
# Preflight indexes these and surfaces relevant types/routes when your
# prompt touches shared boundaries.
related_projects:
- path: /Users/you/projects/api-gateway
alias: api-gateway
- path: /Users/you/projects/shared-types
alias: shared-types

# Behavioral thresholds — tune these to your workflow.
thresholds:
# Warn if no session activity for this many minutes
session_stale_minutes: 30

# Suggest a checkpoint after this many tool calls
max_tool_calls_before_checkpoint: 100

# Minimum corrections before preflight forms a "you keep doing this" pattern
correction_pattern_threshold: 3

# Embedding provider for semantic search across session history.
# "local" — zero config, uses Xenova transformers (~50 events/sec, 384d)
# "openai" — faster + higher quality, requires OPENAI_API_KEY (~200 events/sec, 1536d)
embeddings:
provider: local
# openai_api_key: sk-... # uncomment if using openai provider
50 changes: 50 additions & 0 deletions examples/.preflight/contracts/api.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# .preflight/contracts/api.yml — Manual contract definitions
# These supplement auto-extracted contracts from your codebase.
# Manual definitions win on name conflicts with auto-extracted ones.
#
# Use these when:
# - A contract lives in a service you don't have locally
# - Auto-extraction misses something important
# - You want to document planned/in-progress interfaces

- name: User
kind: interface
description: Core user record shared across services
fields:
- name: id
type: string
required: true
- name: email
type: string
required: true
- name: role
type: "'admin' | 'member' | 'viewer'"
required: true
- name: createdAt
type: Date
required: true

- name: CreateUserRequest
kind: interface
description: POST /api/users request body
fields:
- name: email
type: string
required: true
- name: role
type: "'admin' | 'member' | 'viewer'"
required: false
- name: inviteCode
type: string
required: false

- name: "POST /api/users"
kind: route
description: Creates a new user account
fields:
- name: body
type: CreateUserRequest
required: true
- name: returns
type: "{ user: User, token: string }"
required: true
Loading
Loading