Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 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
3 changes: 3 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@ coverage/
pnpm-lock.yaml
.superpowers/
.codegraph/
*.toml
tests/fixtures/setup/claude-code/malformed.json
tests/fixtures/setup/metadata/malformed.json
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Relay

Safe installed setup is documented in [setup and configuration](docs/setup-and-configuration.md). Use an explicit absolute `--config-file` and preview before `--apply`; generic MCP is snippet-only.

For Linux-only Claude Desktop MCPB evaluation, see [the MCPB guide](integrations/claude-desktop/README.md) and [verification record](docs/claude-desktop-mcpb-verification.md).

**Testing Relay for the first time?** Follow the [source-checkout installation and usage guide](docs/source-checkout-guide.md) to clone, run, connect an AI client, and complete a safe smoke test.
Expand Down
8 changes: 5 additions & 3 deletions docs/agent-integration.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# Agent Integration

## Supported source-checkout model
## Supported installed model

Relay integrations run the built entries from an absolute source checkout: `node __RELAY_CHECKOUT__/dist/mcp/main.js` or `node __RELAY_CHECKOUT__/dist/cli/main.js`. `relay mcp` is a future packaged command owned by Epic #18 and is not available.
Installed integrations invoke the stable command `relay mcp`. Use `relay setup --client codex --config-file <absolute-path>` or the equivalent Claude Code command to preview a change, then add `--apply` only after reviewing the exact target, operation, and snippet. Generic MCP remains snippet-only.

For source development only, run the built entries from an absolute source checkout: `node __RELAY_CHECKOUT__/dist/mcp/main.js` or `node __RELAY_CHECKOUT__/dist/cli/main.js`. These source-checkout examples are intentionally separate from installed templates.

For source development only, run `pnpm dev:mcp` from the checkout; vendor configuration should use the built Node entry so it is independent of the current working directory.

Expand All @@ -25,7 +27,7 @@ Set `RELAY_DB_PATH` to `__RELAY_CHECKOUT__/.relay-validation/relay.db` for first

## Canonical MCP and CLI entry points

See [generic MCP](../integrations/generic-mcp/README.md) and [generic CLI](../integrations/generic-cli/README.md). The canonical MCP server is `node __RELAY_CHECKOUT__/dist/mcp/main.js`; `relay mcp` remains future-only and unavailable until Epic #18.
See [generic MCP](../integrations/generic-mcp/README.md) and [generic CLI](../integrations/generic-cli/README.md). Installed configuration uses `relay mcp`; source-checkout validation uses the explicit built Node entry above.

## Session and provenance example

Expand Down
5 changes: 5 additions & 0 deletions docs/distribution/npm-package.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ configuration, and cache paths are independent of the current directory.
platform default, and whitespace or relative values fail with usage/validation
exit code 2. CLI, MCP, and UI use the same effective database path.

After installation, initialize Relay with `relay setup`. To configure Codex or
Claude Code, preview with an explicit absolute `--config-file` and add
`--apply` only after reviewing the exact entry; see [safe setup and
configuration](../setup-and-configuration.md). Generic MCP is snippet-only.

## Supported runtime

The initial release claim is Node.js 24 on Windows x64, macOS arm64, and
Expand Down
47 changes: 47 additions & 0 deletions docs/setup-and-configuration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Safe setup and agent configuration

`relay setup` initializes Relay's data and configuration roots and opens the canonical database runtime so forward migrations run. It never replaces, resets, or deletes an existing database. Re-running it is safe.

Mutable client setup is preview-first: it mutates only when `--apply` is supplied.

```text
relay setup --client codex --config-file <absolute-path>
relay setup --client codex --config-file <absolute-path> --apply
relay setup --client claude-code --config-file <absolute-path> [--apply]
relay setup --client generic-mcp
```

Codex and Claude Code require an explicit absolute configuration path. Relay never scans home directories or infers a client file. Generic MCP produces a reviewed snippet only.

Before applying, inspect the target, operation, entry identifier, and snippet. Relay proves ownership using the exact `relay` entry, the `relay` command, `['mcp']` arguments, client, and normalized path. Unknown or conflicting entries fail closed. A changed client file receives a collision-safe sibling backup and a validated atomic replacement; Relay ownership metadata is updated only after the replacement is reparsed successfully.

Use `relay config paths` to inspect effective paths. Before destructive configuration actions, run `relay config integrations` to inspect Relay-owned records. `relay config disable` removes an exact owned entry while retaining disabled ownership; setup can safely re-enable it. `relay config remove` removes only the exact owned entry and ownership record. Both commands require `--apply`. These operations retain the database, tasks, backups, and unrelated configuration.

The complete inspection and mutation surface is:

```text
relay config paths
relay config paths --output json
relay config integrations
relay config integrations --output json
relay config snippet --client codex
relay config snippet --client claude-code
relay config snippet --client generic-mcp
relay config disable --client codex --config-file <absolute-path> --apply
relay config remove --client codex --config-file <absolute-path> --apply
```

The client configuration path is always explicit and absolute. Setup is preview-first and mutates only when `--apply` is supplied. `relay config disable` and `relay config remove` always require `--apply`; omitting it is a usage error with exit code 2, not a preview. Generic MCP is snippet-only and has no mutation mode.

For the human safety gate, use this checklist with a disposable absolute path and an isolated `RELAY_DB_PATH`:

1. Copy a real Codex or Claude Code configuration to a disposable file; never start with the only live configuration copy.
2. Run the setup preview and record the reported operation and exact snippet.
3. Apply the setup and verify the configured entry is exact.
4. Compare unrelated configuration bytes before and after.
5. Compare the backup with the original bytes.
6. Rerun setup and verify it reports unchanged and creates no new backup.
7. Disable the owned entry and verify unrelated content remains.
8. Re-enable it with setup and verify the entry returns.
9. Remove it and verify the ownership record is gone while the database remains.
10. Query a task created before setup and confirm it still exists.
Original file line number Diff line number Diff line change
Expand Up @@ -132,12 +132,7 @@ export interface RelayIntegrationOwnership {
readonly lastBackupPath?: string;
}

export type IntegrationOperation =
| 'created'
| 'updated'
| 'unchanged'
| 'disabled'
| 'removed';
export type IntegrationOperation = 'created' | 'updated' | 'unchanged' | 'disabled' | 'removed';

export interface IntegrationChangePlan {
readonly client: MutableIntegrationClient;
Expand Down Expand Up @@ -584,14 +579,27 @@ git commit -m "feat: apply owned configuration changes atomically"

```ts
export type OperationalCommand =
| { readonly kind: 'setup'; readonly client?: IntegrationClient; readonly configFile?: string; readonly apply: boolean }
| {
readonly kind: 'setup';
readonly client?: IntegrationClient;
readonly configFile?: string;
readonly apply: boolean;
}
| { readonly kind: 'config-paths' }
| { readonly kind: 'config-integrations' }
| { readonly kind: 'config-snippet'; readonly client: IntegrationClient }
| { readonly kind: 'config-disable' | 'config-remove'; readonly client: MutableIntegrationClient; readonly configFile: string; readonly apply: true };
| {
readonly kind: 'config-disable' | 'config-remove';
readonly client: MutableIntegrationClient;
readonly configFile: string;
readonly apply: true;
};

export function parseOperationalCommand(argv: readonly string[]): OperationalCommand;
export async function runOperationalCommand(command: OperationalCommand, dependencies: OperationalDependencies): Promise<number>;
export async function runOperationalCommand(
command: OperationalCommand,
dependencies: OperationalDependencies,
): Promise<number>;
```

- [ ] **Step 1: Write failing parser tests for the locked command grammar.**
Expand Down
Loading
Loading