Skip to content
26 changes: 26 additions & 0 deletions .archgate/lint/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Linter Rules

This directory hosts linter-specific rules that enforce your ADRs at the linter level.

## Convention

Place linter plugin files here, named by tool:

- `oxlint.js` — Custom oxlint rules (JavaScript plugin)
- `eslint.js` — Custom ESLint rules
- `biome.js` — Custom Biome rules

## Usage with oxlint

1. Create `.archgate/lint/oxlint.js` exporting your plugin rules.
2. Reference it in your oxlint config:

```json
{
"plugins": [".archgate/lint/oxlint.js"]
}
```

## Why here?

Archgate standardizes `.archgate/lint/` as the location for linter rules that complement ADR checks. This keeps governance artifacts together — ADRs in `adrs/`, linter rules in `lint/`.
13 changes: 13 additions & 0 deletions .claude/settings.local.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"agent": "archgate:developer",
"enableAllProjectMcpServers": true,
"enabledMcpjsonServers": ["archgate"],
"permissions": {
"allow": [
"mcp__plugin_archgate_archgate__*",
"Skill(archgate:architect)",
"Skill(archgate:quality-manager)",
"Skill(archgate:adr-author)"
]
}
}
8 changes: 8 additions & 0 deletions .cursor/mcp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"mcpServers": {
"archgate": {
"command": "archgate",
"args": ["mcp"]
}
}
}
29 changes: 29 additions & 0 deletions .cursor/rules/archgate-governance.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
description: Archgate ADR governance — enforces architecture decision records
globs:
alwaysApply: true
---

# Archgate Governance

This project uses Archgate to enforce Architecture Decision Records (ADRs).

## Before writing code

- Use the `review_context` MCP tool to get applicable ADR briefings for changed files
- Review the Decision and Do's/Don'ts sections of each applicable ADR

## After writing code

- Run the `check` MCP tool to validate compliance with all ADR rules
- Fix any violations before considering work complete

## ADR commands

- `list_adrs` — List all active ADRs with metadata
- `check` — Run automated compliance checks (use `staged: true` for pre-commit)
- `review_context` — Get changed files grouped by domain with ADR briefings

## Key principle

Architectural decisions are enforced, not suggested. If `check` reports violations, they must be fixed.
72 changes: 62 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ Archgate has two layers:

When a rule is violated, `archgate check` reports the file, line, and which ADR was broken. Exit code 1 means violations — wire it into CI and it blocks merges automatically.

**The AI integration layer** is a Claude Code plugin that gives AI agents live access to your ADRs through the MCP server (`archgate mcp`). Agents read the decisions before writing code, validate changes after, and capture new patterns into the governance base. Archgate ships as its own governance subject — its own development is governed by the ADRs in `.archgate/adrs/`.
**The CLI is free and open source.** Writing ADRs, enforcing rules, running checks in CI, and wiring up pre-commit hooks all work without an account or subscription.

**Editor plugins are an optional paid add-on** for teams that want AI agents (Claude Code, Cursor) to read ADRs, validate changes, and capture new patterns automatically. Plugins are distributed from [plugins.archgate.dev](https://plugins.archgate.dev). See [Editor plugins](#editor-plugins) for details.

## Installation

Expand Down Expand Up @@ -76,7 +78,9 @@ archgate init
archgate check
```

`archgate init` creates the `.archgate/adrs/` directory, an example ADR with a companion rules file to show the pattern, and configures the Claude Code plugin if you use it.
`archgate init` creates the `.archgate/adrs/` directory with an example ADR and rules file, and configures editor settings. No account or login is needed — the CLI is fully functional without plugins.

**Want AI agent integration?** See [Editor plugins](#editor-plugins) to add the optional paid plugin for Claude Code or Cursor.

## Writing rules

Expand Down Expand Up @@ -117,15 +121,37 @@ Rules receive the list of files to check (filtered by the ADR's `files` glob if

## Commands

### `archgate login`

Authenticate with GitHub to access the optional paid editor plugins.

```bash
archgate login # authenticate via GitHub Device Flow
archgate login status # show current auth status
archgate login logout # remove stored credentials
archgate login refresh # re-authenticate and claim a new token
```

Opens a browser-based GitHub Device Flow. Once authorized, an archgate plugin token is stored in `~/.archgate/credentials`. This token is used by `archgate init` to install the editor plugin. Not required for CLI-only usage.

### `archgate init`

Initialize governance in the current project.

```bash
archgate init
archgate init # Claude Code (default)
archgate init --editor cursor # Cursor
archgate init --install-plugin # force plugin install attempt
```

Creates `.archgate/adrs/` with an example ADR and rules file, and optionally wires up the Claude Code plugin.
Creates `.archgate/adrs/` with an example ADR and rules file and configures editor settings. Works without an account — plugin installation only happens when you are logged in.

**Plugin install behavior** (optional — requires `archgate login`):

- If you are logged in, init auto-detects your credentials and installs the plugin.
- For **Claude Code**: if the `claude` CLI is on PATH, the plugin is installed automatically via `claude plugin marketplace add` and `claude plugin install`. If not, the manual commands are printed.
- For **Cursor**: the plugin bundle is downloaded from [plugins.archgate.dev](https://plugins.archgate.dev) and extracted into `.cursor/`.
- Use `--install-plugin` to explicitly request plugin installation (useful if auto-detection is skipped).

### `archgate check`

Expand Down Expand Up @@ -230,15 +256,41 @@ pre-commit:
run: archgate check --staged
```

## Claude Code plugin
## Editor plugins

> **Plugins are an optional paid add-on.** The CLI works fully without them. Plugins add AI agent integration — your AI coding agent reads ADRs before writing code, validates changes after implementation, and captures new patterns back into ADRs.

Plugins are distributed from [plugins.archgate.dev](https://plugins.archgate.dev).

### Setup

```bash
# 1. Log in (one-time) — links your GitHub account and issues a plugin token
archgate login

# 2. Initialize a project with the plugin
archgate init # Claude Code (default)
archgate init --editor cursor # or Cursor
```

If you are logged in, `archgate init` auto-detects your credentials and installs the plugin. You can also pass `--install-plugin` explicitly.

### Claude Code

If the `claude` CLI is on your PATH, the plugin is installed automatically. Otherwise, run the printed commands manually:

```bash
claude plugin marketplace add https://<user>:<token>@plugins.archgate.dev/archgate.git
claude plugin install archgate@archgate
```

Once installed, run `archgate:onboard` in Claude Code to initialize governance for your project.

The Claude Code plugin (`archgate:developer`) gives AI agents a full governance workflow:
### Cursor

- Reads applicable ADRs before writing code
- Validates changes after implementation
- Captures new patterns back into ADRs
The Cursor plugin bundle is downloaded from [plugins.archgate.dev](https://plugins.archgate.dev) and extracted into `.cursor/` automatically.

Install the plugin from [archgate/claude-code-plugin](https://github.com/archgate/claude-code-plugin), then run `archgate:onboard` once in your project to initialize governance.
Once installed, run the `ag-onboard` skill in Cursor to initialize governance for your project.

## Contributing

Expand Down
2 changes: 2 additions & 0 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { registerAdrCommand } from "./commands/adr/index";
import { registerUpgradeCommand } from "./commands/upgrade";
import { registerCleanCommand } from "./commands/clean";
import { registerCheckCommand } from "./commands/check";
import { registerLoginCommand } from "./commands/login";
import { registerMcpCommand } from "./commands/mcp";
import { checkForUpdatesIfNeeded } from "./helpers/update-check";
import { logError } from "./helpers/log";
Expand All @@ -35,6 +36,7 @@ async function main() {
.description("AI governance for software development");

registerInitCommand(program);
registerLoginCommand(program);
registerAdrCommand(program);
registerCheckCommand(program);
registerMcpCommand(program);
Expand Down
46 changes: 45 additions & 1 deletion src/commands/init.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import type { Command } from "@commander-js/extra-typings";
import { logError } from "../helpers/log";
import { styleText } from "node:util";
import { logError, logInfo, logWarn } from "../helpers/log";
import { initProject } from "../helpers/init-project";
import type { EditorTarget } from "../helpers/init-project";
import { loadCredentials } from "../helpers/auth";

const VALID_EDITORS = ["claude", "cursor"] as const;

Expand All @@ -14,6 +16,10 @@ export function registerInitCommand(program: Command) {
"editor integration to configure (claude, cursor)",
"claude"
)
.option(
"--install-plugin",
"install the archgate plugin (requires prior `archgate login`)"
)
.action(async (opts) => {
try {
const editor = opts.editor as string;
Expand All @@ -24,9 +30,15 @@ export function registerInitCommand(program: Command) {
process.exit(1);
}

// Auto-detect: install plugin if credentials exist (unless explicitly off)
const installPlugin =
opts.installPlugin ?? (await loadCredentials()) !== null;

const result = await initProject(process.cwd(), {
editor: editor as EditorTarget,
installPlugin,
});

console.log(`Initialized Archgate governance in ${result.projectRoot}`);
console.log(` adrs/ - architecture decision records`);
console.log(` lint/ - linter-specific rules`);
Expand All @@ -35,6 +47,38 @@ export function registerInitCommand(program: Command) {
} else {
console.log(` .claude/ - Claude Code settings configured`);
}

// Plugin install output
if (result.plugin?.installed) {
console.log("");
if (result.plugin.autoInstalled) {
logInfo(
editor === "cursor"
? "Archgate plugin installed for Cursor."
: "Archgate plugin installed for Claude Code."
);
if (result.plugin.detail) {
console.log(` ${result.plugin.detail}`);
}
} else {
// Claude Code — claude CLI not found, show manual commands
logWarn(
"Claude CLI not found. To install the plugin manually, run:"
);
console.log(
` ${styleText("bold", "claude plugin marketplace add")} ${result.plugin.detail}`
);
console.log(
` ${styleText("bold", "claude plugin install")} archgate@archgate`
);
}
} else if (installPlugin) {
// User wanted plugin but no credentials
logWarn(
"Plugin not installed — not logged in.",
"Run `archgate login` first, then re-run `archgate init --install-plugin`."
);
}
} catch (err) {
logError(err instanceof Error ? err.message : String(err));
process.exit(1);
Expand Down
116 changes: 116 additions & 0 deletions src/commands/login.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import type { Command } from "@commander-js/extra-typings";
import { styleText } from "node:util";
import { logError, logInfo } from "../helpers/log";
import {
requestDeviceCode,
pollForAccessToken,
getGitHubUser,
claimArchgateToken,
saveCredentials,
loadCredentials,
clearCredentials,
} from "../helpers/auth";

export function registerLoginCommand(program: Command) {
const login = program
.command("login")
.description("Authenticate with GitHub to access archgate plugins");

login.action(async () => {
try {
// Check if already logged in
const existing = await loadCredentials();
if (existing) {
logInfo(
`Already logged in as ${styleText("bold", existing.github_user)}.`,
"Run `archgate login --refresh` to re-authenticate."
);
return;
}

await runDeviceFlow();
} catch (err) {
logError(err instanceof Error ? err.message : String(err));
process.exit(1);
}
});

login
.command("status")
.description("Show current authentication status")
.action(async () => {
const creds = await loadCredentials();
if (creds) {
console.log(
`Logged in as ${styleText("bold", creds.github_user)} (since ${creds.created_at})`
);
} else {
console.log("Not logged in. Run `archgate login` to authenticate.");
}
});

login
.command("logout")
.description("Remove stored credentials")
.action(async () => {
await clearCredentials();
console.log("Logged out successfully.");
});

login
.command("refresh")
.description("Re-authenticate and claim a new token")
.action(async () => {
try {
await clearCredentials();
await runDeviceFlow();
} catch (err) {
logError(err instanceof Error ? err.message : String(err));
process.exit(1);
}
});
}

async function runDeviceFlow(): Promise<void> {
console.log("Authenticating with GitHub...\n");

// Step 1: Request device code
const deviceCode = await requestDeviceCode();

console.log(
`Open ${styleText("bold", deviceCode.verification_uri)} in your browser`
);
console.log(
`and enter the code: ${styleText(["bold", "green"], deviceCode.user_code)}\n`
);
console.log("Waiting for authorization...");

// Step 2: Poll for access token
const githubToken = await pollForAccessToken(
deviceCode.device_code,
deviceCode.interval,
deviceCode.expires_in
);

// Step 3: Get GitHub username
const githubUser = await getGitHubUser(githubToken);
logInfo(`GitHub user: ${styleText("bold", githubUser)}`);

// Step 4: Exchange GitHub token for archgate plugin token
console.log("Claiming archgate plugin token...");
const archgateToken = await claimArchgateToken(githubToken);

// Step 5: Store credentials
await saveCredentials({
token: archgateToken,
github_user: githubUser,
created_at: new Date().toISOString().split("T")[0],
});

console.log(
`\nAuthenticated as ${styleText("bold", githubUser)}. Plugin access is now available.`
);
console.log(
"Run `archgate init` to set up a project with the archgate plugin."
);
}
Loading