From 87b0add6944d66ddbb6ef2669d0e72ea5b039b0c Mon Sep 17 00:00:00 2001 From: Rhuan Barreto Date: Wed, 18 Mar 2026 15:22:12 +0100 Subject: [PATCH 01/20] fix: include bun.lock in release commit changedFiles bun install was regenerating the lockfile but simple-release only stages files tracked in changedFiles. Adding bun.lock ensures the lockfile is included in the release commit. --- .simple-release.js | 1 + 1 file changed, 1 insertion(+) diff --git a/.simple-release.js b/.simple-release.js index 2aae4012..c1bb8884 100644 --- a/.simple-release.js +++ b/.simple-release.js @@ -23,6 +23,7 @@ class ArchgateProject extends NpmProject { if (changed) { writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n"); execSync("bun install", { stdio: "inherit" }); + this.changedFiles.push("bun.lock"); } } From 4986f7efd8080b46db6f2f146bc78f024a48693c Mon Sep 17 00:00:00 2001 From: Rhuan Barreto Date: Wed, 18 Mar 2026 15:24:14 +0100 Subject: [PATCH 02/20] fix: show platform-appropriate shell syntax in TLS error hint On Windows, show PowerShell syntax ($env:NODE_EXTRA_CA_CERTS=...), on Unix show export syntax. --- src/helpers/tls.ts | 8 +++++++- tests/helpers/tls.test.ts | 9 +++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/helpers/tls.ts b/src/helpers/tls.ts index 7ea9db5c..7b1f2eac 100644 --- a/src/helpers/tls.ts +++ b/src/helpers/tls.ts @@ -25,15 +25,21 @@ export function isTlsError(error: unknown): boolean { /** * Human-readable hint explaining the TLS failure and how to fix it. + * Shows the correct shell syntax for the current platform. */ export function tlsHintMessage(): string { + const example = + process.platform === "win32" + ? ' $env:NODE_EXTRA_CA_CERTS="C:\\path\\to\\corporate-ca.pem"' + : ' export NODE_EXTRA_CA_CERTS="/path/to/corporate-ca.pem"'; + return [ "TLS certificate verification failed.", "This typically happens behind a corporate proxy that performs SSL inspection.", "", "To fix this, set the NODE_EXTRA_CA_CERTS environment variable to your corporate CA certificate:", "", - ' export NODE_EXTRA_CA_CERTS="/path/to/corporate-ca.pem"', + example, "", "Then retry the command.", ].join("\n"); diff --git a/tests/helpers/tls.test.ts b/tests/helpers/tls.test.ts index 9ebdf326..8b9d5888 100644 --- a/tests/helpers/tls.test.ts +++ b/tests/helpers/tls.test.ts @@ -47,4 +47,13 @@ describe("tlsHintMessage", () => { test("mentions corporate proxy", () => { expect(tlsHintMessage()).toContain("corporate proxy"); }); + + test("shows PowerShell syntax on Windows", () => { + const msg = tlsHintMessage(); + if (process.platform === "win32") { + expect(msg).toContain("$env:NODE_EXTRA_CA_CERTS="); + } else { + expect(msg).toContain("export NODE_EXTRA_CA_CERTS="); + } + }); }); From 1c7797d503a4cf67cec89134df2da2afbde54714 Mon Sep 17 00:00:00 2001 From: Rhuan Barreto Date: Wed, 18 Mar 2026 15:32:14 +0100 Subject: [PATCH 03/20] feat: add ARCH-009 ADR for centralized platform detection All platform detection must go through src/helpers/platform.ts. Direct process.platform access outside platform.ts is forbidden. Also fixes flaky Windows test failures: - Replace Bun.$ with Bun.spawn in git test helpers (ARCH-007) - Add safeRmSync with retries for Windows EBUSY errors - Increase timeouts for git-commit-heavy tests --- .../ARCH-009-platform-detection-helper.md | 81 +++++++++++++++++++ ...RCH-009-platform-detection-helper.rules.ts | 31 +++++++ src/helpers/tls.ts | 9 ++- tests/engine/git-files.test.ts | 31 +++---- tests/helpers/git.test.ts | 31 ++++--- tests/test-utils.ts | 42 ++++++++++ 6 files changed, 195 insertions(+), 30 deletions(-) create mode 100644 .archgate/adrs/ARCH-009-platform-detection-helper.md create mode 100644 .archgate/adrs/ARCH-009-platform-detection-helper.rules.ts create mode 100644 tests/test-utils.ts diff --git a/.archgate/adrs/ARCH-009-platform-detection-helper.md b/.archgate/adrs/ARCH-009-platform-detection-helper.md new file mode 100644 index 00000000..839b0fa2 --- /dev/null +++ b/.archgate/adrs/ARCH-009-platform-detection-helper.md @@ -0,0 +1,81 @@ +--- +id: ARCH-009 +title: Centralized Platform Detection +domain: architecture +rules: true +files: + - "src/**/*.ts" +--- + +## Context + +The Archgate CLI runs on macOS, Linux, and Windows (including WSL). Platform-specific behavior appears throughout the codebase: shell syntax in user-facing messages, path separators, subprocess resolution, and feature availability checks. + +The `src/helpers/platform.ts` module already provides a centralized, cached API for platform detection (`isWindows()`, `isMacOS()`, `isLinux()`, `isWSL()`, `getPlatformInfo()`). It also exposes a `_resetPlatformCache()` function that allows tests to simulate different platforms without mocking `process.platform` directly. + +The problem is that nothing prevents code from bypassing this module and reading `process.platform` directly. Direct reads: + +- **Scatter platform logic** — Platform checks end up duplicated across modules with inconsistent patterns (`process.platform === "win32"` vs `process.platform !== "linux"`). +- **Cannot be tested** — `process.platform` is read-only in Bun. Code that reads it directly cannot be tested under a different platform without modifying global state. The platform helper's `_resetPlatformCache()` makes cross-platform testing straightforward. +- **Miss WSL** — `process.platform` returns `"linux"` inside WSL. Code that checks for `"win32"` to decide Windows-specific behavior will miss WSL scenarios where Windows paths or tools are relevant. The platform helper accounts for WSL. + +## Decision + +All platform detection in `src/` MUST go through `src/helpers/platform.ts`. Direct access to `process.platform` outside of `platform.ts` is **forbidden**. + +This covers: + +- OS-conditional logic (Windows vs macOS vs Linux) +- WSL detection +- Platform-specific user-facing messages (shell syntax, paths) +- Feature availability checks that depend on the OS + +This does NOT cover: + +- Test files (`tests/**/*.ts`) — tests may inspect `process.platform` for conditional assertions +- Build scripts and configuration files outside `src/` + +## Do's and Don'ts + +### Do + +- **DO** import from `src/helpers/platform.ts` for any platform check: `isWindows()`, `isMacOS()`, `isLinux()`, `isWSL()`, `getPlatformInfo()` +- **DO** use `_resetPlatformCache()` in tests to simulate different platforms +- **DO** consider WSL when implementing Windows-specific behavior — `isWSL()` returns true when running Linux inside WSL, where Windows tools may still be relevant + +### Don't + +- **DON'T** read `process.platform` directly in source files — use the platform helper instead +- **DON'T** duplicate platform detection logic that already exists in `platform.ts` +- **DON'T** assume `"linux"` means a native Linux environment — it could be WSL + +## Consequences + +### Positive + +- **Single source of truth** — All platform detection flows through one module with consistent caching and WSL awareness. +- **Testable** — Cross-platform behavior can be tested on any OS via `_resetPlatformCache()`. +- **WSL-safe** — The helper correctly distinguishes native Linux from WSL, preventing subtle bugs. + +### Negative + +- **Import overhead** — Modules that need a one-off platform check must import from `platform.ts` instead of reading `process.platform` directly. + +## Compliance and Enforcement + +### Automated Enforcement + +- **Archgate rule** `ARCH-009/no-direct-process-platform`: Scans all TypeScript source files (excluding `platform.ts` itself and tests) for direct `process.platform` access. Severity: `error`. + +### Manual Enforcement + +Code reviewers MUST verify: + +1. No `process.platform` reads appear outside `src/helpers/platform.ts` +2. Platform-conditional logic uses the helper functions, not inline checks +3. WSL is considered when the behavior differs between Linux and Windows + +## References + +- [ARCH-007 — Cross-Platform Subprocess Execution](./ARCH-007-cross-platform-subprocess-execution.md) — Related cross-platform concern for subprocess APIs +- `src/helpers/platform.ts` — The canonical platform detection module diff --git a/.archgate/adrs/ARCH-009-platform-detection-helper.rules.ts b/.archgate/adrs/ARCH-009-platform-detection-helper.rules.ts new file mode 100644 index 00000000..c6bfb863 --- /dev/null +++ b/.archgate/adrs/ARCH-009-platform-detection-helper.rules.ts @@ -0,0 +1,31 @@ +import { defineRules } from "../../src/formats/rules"; + +export default defineRules({ + "no-direct-process-platform": { + description: + "Platform detection must use src/helpers/platform.ts, not process.platform directly", + async check(ctx) { + const files = ctx.scopedFiles.filter( + (f) => + !f.includes("tests/") && + !f.includes(".archgate/") && + !f.endsWith("src/helpers/platform.ts") + ); + + const matches = await Promise.all( + files.map((file) => ctx.grep(file, /process\.platform/)) + ); + for (const fileMatches of matches) { + for (const m of fileMatches) { + ctx.report.violation({ + message: + "Do not access process.platform directly — use isWindows(), isMacOS(), isLinux(), or getPlatformInfo() from src/helpers/platform.ts instead.", + file: m.file, + line: m.line, + fix: 'Import { isWindows } from "../helpers/platform" (or the appropriate helper) and use it instead of process.platform', + }); + } + } + }, + }, +}); diff --git a/src/helpers/tls.ts b/src/helpers/tls.ts index 7b1f2eac..92bafb6b 100644 --- a/src/helpers/tls.ts +++ b/src/helpers/tls.ts @@ -3,6 +3,8 @@ * and provide actionable guidance to the user. */ +import { isWindows } from "./platform"; + const TLS_ERROR_PATTERNS = [ "self signed certificate", "unable to get local issuer certificate", @@ -28,10 +30,9 @@ export function isTlsError(error: unknown): boolean { * Shows the correct shell syntax for the current platform. */ export function tlsHintMessage(): string { - const example = - process.platform === "win32" - ? ' $env:NODE_EXTRA_CA_CERTS="C:\\path\\to\\corporate-ca.pem"' - : ' export NODE_EXTRA_CA_CERTS="/path/to/corporate-ca.pem"'; + const example = isWindows() + ? ' $env:NODE_EXTRA_CA_CERTS="C:\\path\\to\\corporate-ca.pem"' + : ' export NODE_EXTRA_CA_CERTS="/path/to/corporate-ca.pem"'; return [ "TLS certificate verification failed.", diff --git a/tests/engine/git-files.test.ts b/tests/engine/git-files.test.ts index 49f6370a..a22e2af9 100644 --- a/tests/engine/git-files.test.ts +++ b/tests/engine/git-files.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test, beforeEach, afterEach } from "bun:test"; -import { mkdtempSync, rmSync, mkdirSync, writeFileSync } from "node:fs"; +import { mkdtempSync, mkdirSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; @@ -9,6 +9,7 @@ import { getChangedFiles, resolveScopedFiles, } from "../../src/engine/git-files"; +import { git, safeRmSync } from "../test-utils"; describe("git-files", () => { let tempDir: string; @@ -18,7 +19,7 @@ describe("git-files", () => { }); afterEach(() => { - rmSync(tempDir, { recursive: true, force: true }); + safeRmSync(tempDir); }); describe("getGitTrackedFiles", () => { @@ -28,9 +29,9 @@ describe("git-files", () => { }); test("returns tracked files in a git repo", async () => { - await Bun.$`git init`.cwd(tempDir).quiet(); + await git(["init"], tempDir); writeFileSync(join(tempDir, "file.ts"), "export const x = 1;"); - await Bun.$`git add file.ts`.cwd(tempDir).quiet(); + await git(["add", "file.ts"], tempDir); const result = await getGitTrackedFiles(tempDir); expect(result).not.toBeNull(); expect(result!.has("file.ts")).toBe(true); @@ -44,9 +45,9 @@ describe("git-files", () => { }); test("returns staged files", async () => { - await Bun.$`git init`.cwd(tempDir).quiet(); + await git(["init"], tempDir); writeFileSync(join(tempDir, "staged.ts"), "export const x = 1;"); - await Bun.$`git add staged.ts`.cwd(tempDir).quiet(); + await git(["add", "staged.ts"], tempDir); const files = await getStagedFiles(tempDir); expect(files).toContain("staged.ts"); }); @@ -59,21 +60,21 @@ describe("git-files", () => { }); test("returns both staged and unstaged changes", async () => { - await Bun.$`git init`.cwd(tempDir).quiet(); - await Bun.$`git config user.email "test@test.com"`.cwd(tempDir).quiet(); - await Bun.$`git config user.name "Test"`.cwd(tempDir).quiet(); + await git(["init"], tempDir); + await git(["config", "user.email", "test@test.com"], tempDir); + await git(["config", "user.name", "Test"], tempDir); writeFileSync(join(tempDir, "a.ts"), "export const a = 1;"); - await Bun.$`git add a.ts`.cwd(tempDir).quiet(); - await Bun.$`git commit -m "init"`.cwd(tempDir).quiet(); + await git(["add", "a.ts"], tempDir); + await git(["commit", "-m", "init"], tempDir); // Stage a new file (staged change) writeFileSync(join(tempDir, "b.ts"), "export const b = 1;"); - await Bun.$`git add b.ts`.cwd(tempDir).quiet(); + await git(["add", "b.ts"], tempDir); // Modify a committed file without staging (unstaged change) writeFileSync(join(tempDir, "a.ts"), "export const a = 2;"); const files = await getChangedFiles(tempDir); expect(files).toContain("a.ts"); expect(files).toContain("b.ts"); - }); + }, 15_000); }); describe("resolveScopedFiles", () => { @@ -83,11 +84,11 @@ describe("git-files", () => { }); test("resolves files matching glob pattern", async () => { - await Bun.$`git init`.cwd(tempDir).quiet(); + await git(["init"], tempDir); mkdirSync(join(tempDir, "src"), { recursive: true }); writeFileSync(join(tempDir, "src", "foo.ts"), "export const x = 1;"); writeFileSync(join(tempDir, "src", "bar.md"), "# Doc"); - await Bun.$`git add .`.cwd(tempDir).quiet(); + await git(["add", "."], tempDir); const files = await resolveScopedFiles(tempDir, ["src/**/*.ts"]); expect(files).toContain("src/foo.ts"); expect(files).not.toContain("src/bar.md"); diff --git a/tests/helpers/git.test.ts b/tests/helpers/git.test.ts index 214983e4..f5acf777 100644 --- a/tests/helpers/git.test.ts +++ b/tests/helpers/git.test.ts @@ -1,27 +1,36 @@ -import { describe, expect, test, beforeEach, afterEach } from "bun:test"; -import { mkdtempSync, rmSync, writeFileSync } from "node:fs"; +import { + describe, + expect, + test, + beforeEach, + afterEach, + setDefaultTimeout, +} from "bun:test"; +import { mkdtempSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; -import { $ } from "bun"; - import { getChangedFiles } from "../../src/helpers/git"; +import { git, safeRmSync } from "../test-utils"; + +setDefaultTimeout(15_000); describe("getChangedFiles", () => { let tempDir: string; beforeEach(async () => { tempDir = mkdtempSync(join(tmpdir(), "archgate-git-test-")); - await $`git init`.cwd(tempDir).quiet(); - await $`git config user.email "test@test.com"`.cwd(tempDir).quiet(); - await $`git config user.name "Test"`.cwd(tempDir).quiet(); + await git(["init"], tempDir); + await git(["config", "user.email", "test@test.com"], tempDir); + await git(["config", "user.name", "Test"], tempDir); // Create an initial commit so HEAD exists writeFileSync(join(tempDir, "README.md"), "init"); - await $`git add . && git commit -m "init"`.cwd(tempDir).quiet(); + await git(["add", "."], tempDir); + await git(["commit", "-m", "init"], tempDir); }); afterEach(() => { - rmSync(tempDir, { recursive: true, force: true }); + safeRmSync(tempDir); }); test("returns empty array when no changes", async () => { @@ -37,14 +46,14 @@ describe("getChangedFiles", () => { test("returns staged files", async () => { writeFileSync(join(tempDir, "new.ts"), "export const x = 1;"); - await $`git add new.ts`.cwd(tempDir).quiet(); + await git(["add", "new.ts"], tempDir); const files = await getChangedFiles(tempDir); expect(files).toContain("new.ts"); }); test("deduplicates files that are both staged and modified", async () => { writeFileSync(join(tempDir, "file.ts"), "v1"); - await $`git add file.ts`.cwd(tempDir).quiet(); + await git(["add", "file.ts"], tempDir); writeFileSync(join(tempDir, "file.ts"), "v2"); const files = await getChangedFiles(tempDir); const count = files.filter((f) => f === "file.ts").length; diff --git a/tests/test-utils.ts b/tests/test-utils.ts new file mode 100644 index 00000000..7a4b46c8 --- /dev/null +++ b/tests/test-utils.ts @@ -0,0 +1,42 @@ +import { rmSync } from "node:fs"; + +/** + * Run a git command in the given directory via Bun.spawn (ARCH-007 compliant). + * Returns stdout as a trimmed string. + */ +export async function git(args: string[], cwd: string): Promise { + const proc = Bun.spawn(["git", ...args], { + cwd, + stdout: "pipe", + stderr: "pipe", + }); + const stdout = await new Response(proc.stdout).text(); + const exitCode = await proc.exited; + if (exitCode !== 0) { + const stderr = await new Response(proc.stderr).text(); + throw new Error( + `git ${args.join(" ")} failed (exit ${exitCode}): ${stderr.trim()}` + ); + } + return stdout.trim(); +} + +/** + * Remove a temp directory with retries to handle Windows EBUSY errors + * caused by git processes that haven't fully released file locks yet. + */ +export function safeRmSync(dir: string, retries = 5): void { + for (let i = 0; i <= retries; i++) { + try { + rmSync(dir, { recursive: true, force: true }); + return; + } catch (err: unknown) { + const code = + err instanceof Error ? (err as NodeJS.ErrnoException).code : undefined; + const isRetryable = + code === "EBUSY" || code === "EPERM" || code === "ENOTEMPTY"; + if (!isRetryable || i === retries) throw err; + Bun.sleepSync(200 * (i + 1)); + } + } +} From 2736a0bab4aafb5fc990f9a2b7969ce11035084b Mon Sep 17 00:00:00 2001 From: Rhuan Barreto Date: Wed, 18 Mar 2026 15:44:47 +0100 Subject: [PATCH 04/20] fix: show all Windows shell variants in TLS error hint isWindows() is true for any shell on Windows, not just PowerShell. Now shows examples for PowerShell, cmd, and Git Bash. --- src/helpers/tls.ts | 12 ++++++++---- tests/helpers/tls.test.ts | 6 ++++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/helpers/tls.ts b/src/helpers/tls.ts index 92bafb6b..230870f1 100644 --- a/src/helpers/tls.ts +++ b/src/helpers/tls.ts @@ -30,9 +30,13 @@ export function isTlsError(error: unknown): boolean { * Shows the correct shell syntax for the current platform. */ export function tlsHintMessage(): string { - const example = isWindows() - ? ' $env:NODE_EXTRA_CA_CERTS="C:\\path\\to\\corporate-ca.pem"' - : ' export NODE_EXTRA_CA_CERTS="/path/to/corporate-ca.pem"'; + const examples = isWindows() + ? [ + ' PowerShell: $env:NODE_EXTRA_CA_CERTS="C:\\path\\to\\corporate-ca.pem"', + " cmd: set NODE_EXTRA_CA_CERTS=C:\\path\\to\\corporate-ca.pem", + ' Git Bash: export NODE_EXTRA_CA_CERTS="/c/path/to/corporate-ca.pem"', + ] + : [' export NODE_EXTRA_CA_CERTS="/path/to/corporate-ca.pem"']; return [ "TLS certificate verification failed.", @@ -40,7 +44,7 @@ export function tlsHintMessage(): string { "", "To fix this, set the NODE_EXTRA_CA_CERTS environment variable to your corporate CA certificate:", "", - example, + ...examples, "", "Then retry the command.", ].join("\n"); diff --git a/tests/helpers/tls.test.ts b/tests/helpers/tls.test.ts index 8b9d5888..9f0c097d 100644 --- a/tests/helpers/tls.test.ts +++ b/tests/helpers/tls.test.ts @@ -48,10 +48,12 @@ describe("tlsHintMessage", () => { expect(tlsHintMessage()).toContain("corporate proxy"); }); - test("shows PowerShell syntax on Windows", () => { + test("shows shell-appropriate syntax per platform", () => { const msg = tlsHintMessage(); if (process.platform === "win32") { - expect(msg).toContain("$env:NODE_EXTRA_CA_CERTS="); + expect(msg).toContain("PowerShell:"); + expect(msg).toContain("cmd:"); + expect(msg).toContain("Git Bash:"); } else { expect(msg).toContain("export NODE_EXTRA_CA_CERTS="); } From 60db9ecfdcc31f592836c0cc96a26c28da34ae31 Mon Sep 17 00:00:00 2001 From: Rhuan Barreto Date: Wed, 18 Mar 2026 15:58:06 +0100 Subject: [PATCH 05/20] feat: interactive signup flow in login command When token claim fails because the user is not registered, the login command now prompts for email and use case, submits a signup request to the plugins API, and immediately retries the token claim (since signups are auto-approved). --- src/commands/login.ts | 57 +++++++++++++++++++++++++++++++++++- src/helpers/auth.ts | 9 ++++++ src/helpers/signup.ts | 49 +++++++++++++++++++++++++++++++ tests/helpers/auth.test.ts | 9 +++--- tests/helpers/signup.test.ts | 45 ++++++++++++++++++++++++++++ 5 files changed, 164 insertions(+), 5 deletions(-) create mode 100644 src/helpers/signup.ts create mode 100644 tests/helpers/signup.test.ts diff --git a/src/commands/login.ts b/src/commands/login.ts index 506a2b37..331785d3 100644 --- a/src/commands/login.ts +++ b/src/commands/login.ts @@ -1,6 +1,7 @@ import { styleText } from "node:util"; import type { Command } from "@commander-js/extra-typings"; +import inquirer from "inquirer"; import { requestDeviceCode, @@ -12,6 +13,7 @@ import { clearCredentials, } from "../helpers/auth"; import { logError, logInfo } from "../helpers/log"; +import { SignupRequiredError, requestSignup } from "../helpers/signup"; import { isTlsError, tlsHintMessage } from "../helpers/tls"; export function registerLoginCommand(program: Command) { @@ -109,9 +111,62 @@ async function runDeviceFlow(): Promise { // Step 4: Exchange GitHub token for archgate plugin token console.log("Claiming archgate plugin token..."); + try { + const archgateToken = await claimArchgateToken(githubToken); + await storeAndFinish(archgateToken, githubUser); + } catch (err) { + if (!(err instanceof SignupRequiredError)) throw err; + + console.log( + `\nYour GitHub account ${styleText("bold", githubUser)} is not yet registered.` + ); + console.log("Let's sign you up now.\n"); + + await runSignupFlow(githubUser, githubToken); + } +} + +async function runSignupFlow( + githubUser: string, + githubToken: string +): Promise { + const answers = await inquirer.prompt([ + { + type: "input", + name: "email", + message: "Email address:", + validate: (v: string) => + /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v) || "Enter a valid email address", + }, + { + type: "input", + name: "useCase", + message: "How do you plan to use archgate?", + validate: (v: string) => + v.trim().length > 0 || "Please describe your use case", + }, + ]); + + console.log("\nSubmitting signup request..."); + const ok = await requestSignup(githubUser, answers.email, answers.useCase); + + if (!ok) { + logError( + "Signup request failed. Please try again or sign up at https://plugins.archgate.dev" + ); + process.exit(1); + } + + // Signup auto-approves — claim the token immediately + console.log("Claiming archgate plugin token..."); const archgateToken = await claimArchgateToken(githubToken); + await storeAndFinish(archgateToken, githubUser); +} - // Step 5: Store credentials +async function storeAndFinish( + archgateToken: string, + githubUser: string +): Promise { await saveCredentials({ token: archgateToken, github_user: githubUser, diff --git a/src/helpers/auth.ts b/src/helpers/auth.ts index bca5a0ad..096a329c 100644 --- a/src/helpers/auth.ts +++ b/src/helpers/auth.ts @@ -9,6 +9,7 @@ import { unlinkSync } from "node:fs"; import { logDebug } from "./log"; import { internalPath, createPathIfNotExists } from "./paths"; +import { SignupRequiredError, isSignupRequiredError } from "./signup"; // --------------------------------------------------------------------------- // Constants @@ -194,6 +195,11 @@ export async function claimArchgateToken(githubToken: string): Promise { const body = (await response.json().catch(() => ({}))) as { error?: string; }; + + if (isSignupRequiredError(body.error)) { + throw new SignupRequiredError(); + } + const message = body.error ?? `Token claim failed (HTTP ${response.status})`; throw new Error(message); @@ -206,6 +212,9 @@ export async function claimArchgateToken(githubToken: string): Promise { return data.token; } +// Re-export for consumers that import from auth.ts +export { SignupRequiredError } from "./signup"; + // --------------------------------------------------------------------------- // Credential Storage // --------------------------------------------------------------------------- diff --git a/src/helpers/signup.ts b/src/helpers/signup.ts new file mode 100644 index 00000000..7feb63f1 --- /dev/null +++ b/src/helpers/signup.ts @@ -0,0 +1,49 @@ +/** + * signup.ts — Archgate plugins platform signup for unregistered users. + */ + +const PLUGINS_API = "https://plugins.archgate.dev"; + +/** + * Sentinel error thrown when the token claim endpoint reports that the + * GitHub account has no approved signup. + */ +export class SignupRequiredError extends Error { + constructor() { + super("No approved signup found for this GitHub account"); + this.name = "SignupRequiredError"; + } +} + +/** + * Returns true if the error message indicates the user needs to sign up. + */ +export function isSignupRequiredError(message?: string): boolean { + if (!message) return false; + const lower = message.toLowerCase(); + return ( + lower.includes("no approved signup") || lower.includes("not registered") + ); +} + +/** + * Submit a signup request to the archgate plugins platform. + * Returns true on success (201), false otherwise. + */ +export async function requestSignup( + github: string, + email: string, + useCase: string +): Promise { + const response = await fetch(`${PLUGINS_API}/api/signup`, { + method: "POST", + headers: { + "Content-Type": "application/json", + "User-Agent": "archgate-cli", + Origin: PLUGINS_API, + }, + body: JSON.stringify({ github, email, useCase }), + signal: AbortSignal.timeout(15_000), + }); + return response.status === 201; +} diff --git a/tests/helpers/auth.test.ts b/tests/helpers/auth.test.ts index 6757e4e7..ecfd64a5 100644 --- a/tests/helpers/auth.test.ts +++ b/tests/helpers/auth.test.ts @@ -189,8 +189,9 @@ describe("auth", () => { } }); - test("throws with error message from service on 403", async () => { - const { claimArchgateToken } = await import("../../src/helpers/auth"); + test("throws SignupRequiredError on 403 with no approved signup", async () => { + const { claimArchgateToken, SignupRequiredError } = + await import("../../src/helpers/auth"); const originalFetch = globalThis.fetch; mockFetch(() => @@ -203,8 +204,8 @@ describe("auth", () => { ); try { - await expect(claimArchgateToken("gho_token")).rejects.toThrow( - "No approved signup" + await expect(claimArchgateToken("gho_token")).rejects.toBeInstanceOf( + SignupRequiredError ); } finally { globalThis.fetch = originalFetch; diff --git a/tests/helpers/signup.test.ts b/tests/helpers/signup.test.ts new file mode 100644 index 00000000..1e6b832e --- /dev/null +++ b/tests/helpers/signup.test.ts @@ -0,0 +1,45 @@ +import { describe, expect, test } from "bun:test"; + +import { + SignupRequiredError, + isSignupRequiredError, +} from "../../src/helpers/signup"; + +describe("SignupRequiredError", () => { + test("is an instance of Error", () => { + const err = new SignupRequiredError(); + expect(err).toBeInstanceOf(Error); + }); + + test("has the correct name", () => { + expect(new SignupRequiredError().name).toBe("SignupRequiredError"); + }); + + test("has a descriptive message", () => { + expect(new SignupRequiredError().message).toContain("No approved signup"); + }); +}); + +describe("isSignupRequiredError", () => { + test("matches 'No approved signup found'", () => { + expect( + isSignupRequiredError("No approved signup found for this GitHub account") + ).toBe(true); + }); + + test("matches 'not registered'", () => { + expect(isSignupRequiredError("User is not registered")).toBe(true); + }); + + test("is case-insensitive", () => { + expect(isSignupRequiredError("NO APPROVED SIGNUP")).toBe(true); + }); + + test("returns false for unrelated messages", () => { + expect(isSignupRequiredError("Token expired")).toBe(false); + }); + + test("returns false for no argument", () => { + expect(isSignupRequiredError()).toBe(false); + }); +}); From cb451898cbf1d3c0935a607d44b1d06bc6a9c0d0 Mon Sep 17 00:00:00 2001 From: Rhuan Barreto Date: Wed, 18 Mar 2026 16:01:21 +0100 Subject: [PATCH 06/20] feat: prefill email from GitHub in signup flow getGitHubUser now returns both login and email from the GitHub API. The signup prompt pre-fills the email field when available. --- src/commands/login.ts | 11 +++++++---- src/helpers/auth.ts | 18 ++++++++++++++---- tests/helpers/auth.test.ts | 9 +++++++-- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/src/commands/login.ts b/src/commands/login.ts index 331785d3..f0e3c8d0 100644 --- a/src/commands/login.ts +++ b/src/commands/login.ts @@ -105,8 +105,9 @@ async function runDeviceFlow(): Promise { deviceCode.expires_in ); - // Step 3: Get GitHub username - const githubUser = await getGitHubUser(githubToken); + // Step 3: Get GitHub user info + const { login: githubUser, email: githubEmail } = + await getGitHubUser(githubToken); logInfo(`GitHub user: ${styleText("bold", githubUser)}`); // Step 4: Exchange GitHub token for archgate plugin token @@ -122,19 +123,21 @@ async function runDeviceFlow(): Promise { ); console.log("Let's sign you up now.\n"); - await runSignupFlow(githubUser, githubToken); + await runSignupFlow(githubUser, githubToken, githubEmail); } } async function runSignupFlow( githubUser: string, - githubToken: string + githubToken: string, + githubEmail: string | null ): Promise { const answers = await inquirer.prompt([ { type: "input", name: "email", message: "Email address:", + default: githubEmail ?? undefined, validate: (v: string) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v) || "Enter a valid email address", }, diff --git a/src/helpers/auth.ts b/src/helpers/auth.ts index 096a329c..bda75eee 100644 --- a/src/helpers/auth.ts +++ b/src/helpers/auth.ts @@ -149,10 +149,17 @@ export async function pollForAccessToken( throw new Error("Device code expired. Please try again."); } +export interface GitHubUserInfo { + login: string; + email: string | null; +} + /** - * Step 3: Get the authenticated GitHub username. + * Step 3: Get the authenticated GitHub user info. */ -export async function getGitHubUser(accessToken: string): Promise { +export async function getGitHubUser( + accessToken: string +): Promise { const response = await fetch("https://api.github.com/user", { headers: { Authorization: `Bearer ${accessToken}`, @@ -165,11 +172,14 @@ export async function getGitHubUser(accessToken: string): Promise { throw new Error(`Failed to fetch GitHub user (HTTP ${response.status})`); } - const data = (await response.json()) as { login?: string }; + const data = (await response.json()) as { + login?: string; + email?: string | null; + }; if (!data.login) { throw new Error("GitHub API did not return a username"); } - return data.login; + return { login: data.login, email: data.email ?? null }; } // --------------------------------------------------------------------------- diff --git a/tests/helpers/auth.test.ts b/tests/helpers/auth.test.ts index ecfd64a5..6f918dc8 100644 --- a/tests/helpers/auth.test.ts +++ b/tests/helpers/auth.test.ts @@ -146,11 +146,16 @@ describe("auth", () => { const { getGitHubUser } = await import("../../src/helpers/auth"); const originalFetch = globalThis.fetch; - mockFetch(() => Promise.resolve(Response.json({ login: "octocat" }))); + mockFetch(() => + Promise.resolve( + Response.json({ login: "octocat", email: "octo@cat.com" }) + ) + ); try { const user = await getGitHubUser("gho_test_token"); - expect(user).toBe("octocat"); + expect(user.login).toBe("octocat"); + expect(user.email).toBe("octo@cat.com"); } finally { globalThis.fetch = originalFetch; } From 781f1415de82a468ae618aa0db14339b90ac0e95 Mon Sep 17 00:00:00 2001 From: Rhuan Barreto Date: Wed, 18 Mar 2026 16:08:16 +0100 Subject: [PATCH 07/20] feat: use signup token directly and ask for editor preference - requestSignup now returns the token from the API response, avoiding a second token claim round-trip and duplicate tokens in the database - Signup flow prompts for editor preference (Claude Code / Cursor) - Falls back to claimArchgateToken if the signup response has no token --- src/commands/login.ts | 28 +++++++++++++++++++++++----- src/helpers/signup.ts | 24 +++++++++++++++++++----- 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/src/commands/login.ts b/src/commands/login.ts index f0e3c8d0..499ecb18 100644 --- a/src/commands/login.ts +++ b/src/commands/login.ts @@ -141,6 +141,15 @@ async function runSignupFlow( validate: (v: string) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v) || "Enter a valid email address", }, + { + type: "list", + name: "editor", + message: "Which editor will you use with archgate?", + choices: [ + { name: "Claude Code", value: "claude-code" }, + { name: "Cursor", value: "cursor" }, + ], + }, { type: "input", name: "useCase", @@ -151,18 +160,27 @@ async function runSignupFlow( ]); console.log("\nSubmitting signup request..."); - const ok = await requestSignup(githubUser, answers.email, answers.useCase); + const result = await requestSignup( + githubUser, + answers.email, + answers.useCase, + answers.editor + ); - if (!ok) { + if (!result.ok) { logError( "Signup request failed. Please try again or sign up at https://plugins.archgate.dev" ); process.exit(1); } - // Signup auto-approves — claim the token immediately - console.log("Claiming archgate plugin token..."); - const archgateToken = await claimArchgateToken(githubToken); + // Use the token from signup if available, otherwise claim separately + let archgateToken = result.token; + if (!archgateToken) { + console.log("Claiming archgate plugin token..."); + archgateToken = await claimArchgateToken(githubToken); + } + await storeAndFinish(archgateToken, githubUser); } diff --git a/src/helpers/signup.ts b/src/helpers/signup.ts index 7feb63f1..3c123b87 100644 --- a/src/helpers/signup.ts +++ b/src/helpers/signup.ts @@ -26,15 +26,23 @@ export function isSignupRequiredError(message?: string): boolean { ); } +export interface SignupResult { + ok: boolean; + /** Token returned by the API when signup is auto-approved. */ + token: string | null; +} + /** * Submit a signup request to the archgate plugins platform. - * Returns true on success (201), false otherwise. + * On auto-approved signups the API returns the token directly, + * avoiding a separate claim round-trip. */ export async function requestSignup( github: string, email: string, - useCase: string -): Promise { + useCase: string, + editor: string = "claude-code" +): Promise { const response = await fetch(`${PLUGINS_API}/api/signup`, { method: "POST", headers: { @@ -42,8 +50,14 @@ export async function requestSignup( "User-Agent": "archgate-cli", Origin: PLUGINS_API, }, - body: JSON.stringify({ github, email, useCase }), + body: JSON.stringify({ github, email, useCase, editor }), signal: AbortSignal.timeout(15_000), }); - return response.status === 201; + + if (response.status !== 201) { + return { ok: false, token: null }; + } + + const data = (await response.json().catch(() => ({}))) as { token?: string }; + return { ok: true, token: data.token ?? null }; } From 1ed7f0ada5cb38eb7d5eb39fe97cba252b75c199 Mon Sep 17 00:00:00 2001 From: Rhuan Barreto Date: Wed, 18 Mar 2026 16:28:28 +0100 Subject: [PATCH 08/20] feat: add all editor options to signup flow Match the frontend's editor choices: Claude Code, VS Code, Copilot CLI, and Cursor. --- src/commands/login.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/commands/login.ts b/src/commands/login.ts index 499ecb18..143d9293 100644 --- a/src/commands/login.ts +++ b/src/commands/login.ts @@ -147,6 +147,8 @@ async function runSignupFlow( message: "Which editor will you use with archgate?", choices: [ { name: "Claude Code", value: "claude-code" }, + { name: "VS Code", value: "vscode" }, + { name: "Copilot CLI", value: "copilot-cli" }, { name: "Cursor", value: "cursor" }, ], }, From 30bb503dae82c80e7c42a9757fe5709a89efb67d Mon Sep 17 00:00:00 2001 From: Rhuan Barreto Date: Wed, 18 Mar 2026 16:33:52 +0100 Subject: [PATCH 09/20] fix: show context-aware hint after login If .archgate/adrs/ exists in the current project, suggest running `archgate check` instead of `archgate init`. --- src/commands/login.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/commands/login.ts b/src/commands/login.ts index 143d9293..bfa2bb16 100644 --- a/src/commands/login.ts +++ b/src/commands/login.ts @@ -13,6 +13,7 @@ import { clearCredentials, } from "../helpers/auth"; import { logError, logInfo } from "../helpers/log"; +import { findProjectRoot } from "../helpers/paths"; import { SignupRequiredError, requestSignup } from "../helpers/signup"; import { isTlsError, tlsHintMessage } from "../helpers/tls"; @@ -199,7 +200,14 @@ async function storeAndFinish( 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." - ); + + if (findProjectRoot()) { + console.log( + "Run `archgate check` to validate your project against its ADRs." + ); + } else { + console.log( + "Run `archgate init` to set up a project with the archgate plugin." + ); + } } From feb8012c4e2401cd17eecc3472d9c4fb3da29935 Mon Sep 17 00:00:00 2001 From: Rhuan Barreto Date: Wed, 18 Mar 2026 16:37:47 +0100 Subject: [PATCH 10/20] docs: update login documentation for interactive signup and TLS handling - Login section now documents automatic signup flow for unregistered users - Added troubleshooting section for TLS/corporate proxy errors - Updated example output to show signup prompts and context-aware hints - Updated beta tips to reflect CLI-based signup (no external site needed) - All changes mirrored in PT-BR documentation --- .../docs/getting-started/installation.mdx | 2 +- .../docs/getting-started/quick-start.mdx | 2 +- .../docs/guides/claude-code-plugin.mdx | 4 +- .../pt-br/getting-started/quick-start.mdx | 2 +- .../docs/pt-br/guides/claude-code-plugin.mdx | 4 +- .../docs/pt-br/reference/cli-commands.mdx | 55 ++++++++++++++++++- .../content/docs/reference/cli-commands.mdx | 55 ++++++++++++++++++- 7 files changed, 111 insertions(+), 13 deletions(-) diff --git a/docs/src/content/docs/getting-started/installation.mdx b/docs/src/content/docs/getting-started/installation.mdx index 729da993..a73d2157 100644 --- a/docs/src/content/docs/getting-started/installation.mdx +++ b/docs/src/content/docs/getting-started/installation.mdx @@ -133,5 +133,5 @@ Restart your shell, then run `npm install -g archgate` again. The `archgate` com Once installed, run `archgate init` in your project to set up governance. See the [Quick Start](/getting-started/quick-start/) guide for a walkthrough. :::tip[Editor plugins (beta)] -Want your AI agent to read ADRs before coding and validate after? The editor plugins for [Claude Code](/guides/claude-code-plugin/) and [Cursor](/guides/cursor-integration/) add a full governance workflow on top of the CLI. [Sign up for beta access](https://plugins.archgate.dev). +Want your AI agent to read ADRs before coding and validate after? The editor plugins for [Claude Code](/guides/claude-code-plugin/) and [Cursor](/guides/cursor-integration/) add a full governance workflow on top of the CLI. Run `archgate login` to sign up and authenticate directly from the CLI. ::: diff --git a/docs/src/content/docs/getting-started/quick-start.mdx b/docs/src/content/docs/getting-started/quick-start.mdx index 1059e2d3..3fdf6101 100644 --- a/docs/src/content/docs/getting-started/quick-start.mdx +++ b/docs/src/content/docs/getting-started/quick-start.mdx @@ -140,5 +140,5 @@ Now that you have a working setup, dive deeper: - [Cursor Integration](/guides/cursor-integration/) — Use Archgate with Cursor IDE for AI-assisted development. :::tip[Editor plugins (beta)] -Want AI agents that automatically read your ADRs before coding? Sign up for the editor plugin beta at [plugins.archgate.dev](https://plugins.archgate.dev), then run `archgate login` and `archgate init --install-plugin`. +Want AI agents that automatically read your ADRs before coding? Run `archgate login` to sign up and authenticate directly from the CLI, then run `archgate init --install-plugin` to set up the plugin. ::: diff --git a/docs/src/content/docs/guides/claude-code-plugin.mdx b/docs/src/content/docs/guides/claude-code-plugin.mdx index df6aa57f..d6650a1e 100644 --- a/docs/src/content/docs/guides/claude-code-plugin.mdx +++ b/docs/src/content/docs/guides/claude-code-plugin.mdx @@ -29,12 +29,12 @@ The `archgate:developer` agent is set as the default agent via `.claude/settings ## Installation :::note[Beta access required] -The Claude Code plugin is currently in beta. [Sign up at plugins.archgate.dev](https://plugins.archgate.dev) to get access before following the steps below. +The Claude Code plugin is currently in beta. Run `archgate login` to sign up and authenticate directly from the CLI -- no separate registration step needed. ::: ### 1. Log in with GitHub -Authenticate with your GitHub account to obtain a plugin token: +Authenticate with your GitHub account to obtain a plugin token. If you are not registered yet, the CLI handles signup interactively -- it prompts for your email, editor preference, and use case, then registers you automatically: ```bash archgate login diff --git a/docs/src/content/docs/pt-br/getting-started/quick-start.mdx b/docs/src/content/docs/pt-br/getting-started/quick-start.mdx index 2994ef62..f05565a1 100644 --- a/docs/src/content/docs/pt-br/getting-started/quick-start.mdx +++ b/docs/src/content/docs/pt-br/getting-started/quick-start.mdx @@ -140,5 +140,5 @@ Agora que você tem uma configuração funcionando, aprofunde-se: - [Integração com Cursor](/pt-br/guides/cursor-integration/) — Use o Archgate com o Cursor IDE para desenvolvimento assistido por IA. :::tip[Plugins para editores (beta)] -Quer agentes de IA que leiam automaticamente seus ADRs antes de programar? Cadastre-se para o beta dos plugins para editores em [plugins.archgate.dev](https://plugins.archgate.dev), depois execute `archgate login` e `archgate init --install-plugin`. +Quer agentes de IA que leiam automaticamente seus ADRs antes de programar? Execute `archgate login` para se cadastrar e autenticar diretamente pela CLI, depois execute `archgate init --install-plugin` para configurar o plugin. ::: diff --git a/docs/src/content/docs/pt-br/guides/claude-code-plugin.mdx b/docs/src/content/docs/pt-br/guides/claude-code-plugin.mdx index 0aa8a7d8..e99ccd97 100644 --- a/docs/src/content/docs/pt-br/guides/claude-code-plugin.mdx +++ b/docs/src/content/docs/pt-br/guides/claude-code-plugin.mdx @@ -29,12 +29,12 @@ O agente `archgate:developer` é definido como agente padrão via `.claude/setti ## Instalação :::note[Acesso beta necessário] -O plugin para Claude Code está atualmente em beta. [Cadastre-se em plugins.archgate.dev](https://plugins.archgate.dev) para obter acesso antes de seguir os passos abaixo. +O plugin para Claude Code está atualmente em beta. Execute `archgate login` para se cadastrar e autenticar diretamente pela CLI -- nenhum passo de registro separado é necessário. ::: ### 1. Faça login com o GitHub -Autentique-se com sua conta do GitHub para obter um token do plugin: +Autentique-se com sua conta do GitHub para obter um token do plugin. Se você ainda não possui cadastro, a CLI cuida do registro de forma interativa -- solicita seu email, preferência de editor e caso de uso, e então realiza o cadastro automaticamente: ```bash archgate login diff --git a/docs/src/content/docs/pt-br/reference/cli-commands.mdx b/docs/src/content/docs/pt-br/reference/cli-commands.mdx index 81a7d2ad..11fabc94 100644 --- a/docs/src/content/docs/pt-br/reference/cli-commands.mdx +++ b/docs/src/content/docs/pt-br/reference/cli-commands.mdx @@ -21,18 +21,18 @@ archgate check --help ## archgate login -Autentique-se com o GitHub para acessar os plugins de editor do Archgate. +Autentique-se com o GitHub para acessar os plugins de editor do Archgate. Se você ainda não possui cadastro, a CLI cuida do registro automaticamente -- solicita seu email, preferência de editor (Claude Code, VS Code, Copilot CLI ou Cursor) e caso de uso, e então realiza o cadastro antes de concluir o login. ```bash archgate login ``` -Inicia um GitHub Device Flow (OAuth). A CLI exibe um código de uso único e uma URL. Abra a URL no seu navegador, insira o código e autorize o Archgate GitHub OAuth App. Após a autorização, a CLI troca sua identidade do GitHub por um token de plugin do Archgate e armazena ambos em `~/.archgate/credentials`. +Inicia um GitHub Device Flow (OAuth). A CLI exibe um código de uso único e uma URL. Abra a URL no seu navegador, insira o código e autorize o Archgate GitHub OAuth App. Após a autorização, a CLI troca sua identidade do GitHub por um token de plugin do Archgate e armazena ambos em `~/.archgate/credentials`. Se o diretório atual já contiver `.archgate/adrs/`, a mensagem pós-login sugere `archgate check` como próximo passo em vez de `archgate init`. As credenciais são necessárias para instalar plugins de editor via `archgate init --install-plugin`. A CLI em si (check, init, etc.) funciona sem login. :::tip[Plugin em beta] -Os plugins de editor estão atualmente em beta. Cadastre-se em [plugins.archgate.dev](https://plugins.archgate.dev) para obter acesso. +Os plugins de editor estão atualmente em beta. Execute `archgate login` para se cadastrar e autenticar diretamente pela CLI -- nenhum passo de registro separado é necessário. ::: ### Subcomandos @@ -60,12 +60,61 @@ and enter the code: ABCD-1234 Waiting for authorization... GitHub user: yourname + +No account found. Let's get you signed up. +Email: you@example.com +Editor: Claude Code +Use case: Enforcing ADRs in our monorepo + +Registering... Claiming archgate plugin token... Authenticated as yourname. Plugin access is now available. Run `archgate init` to set up a project with the archgate plugin. ``` +Se o projeto já possuir `.archgate/adrs/`, a última linha exibe: + +``` +Run `archgate check` to validate your project against its ADRs. +``` + +### Solução de problemas + +#### Erros de TLS/proxy corporativo + +Se `archgate login` falhar com um erro de certificado TLS (comum em redes com proxy corporativo), aponte o runtime para o certificado CA da sua organização usando a variável de ambiente `NODE_EXTRA_CA_CERTS`. + +No macOS/Linux: + +```bash +export NODE_EXTRA_CA_CERTS=/caminho/para/seu-ca-corporativo.pem +archgate login +``` + +No Windows (PowerShell): + +```powershell +$env:NODE_EXTRA_CA_CERTS = "C:\caminho\para\seu-ca-corporativo.pem" +archgate login +``` + +No Windows (cmd): + +```cmd +set NODE_EXTRA_CA_CERTS=C:\caminho\para\seu-ca-corporativo.pem +archgate login +``` + +No Windows (Git Bash): + +```bash +export NODE_EXTRA_CA_CERTS=/c/caminho/para/seu-ca-corporativo.pem +archgate login +``` + +Consulte sua equipe de TI para obter o caminho correto do certificado caso tenha dúvidas. + Verificar o status do login: ```bash diff --git a/docs/src/content/docs/reference/cli-commands.mdx b/docs/src/content/docs/reference/cli-commands.mdx index 3f3b91e7..fb3473af 100644 --- a/docs/src/content/docs/reference/cli-commands.mdx +++ b/docs/src/content/docs/reference/cli-commands.mdx @@ -21,18 +21,18 @@ archgate check --help ## archgate login -Authenticate with GitHub to access Archgate editor plugins. +Authenticate with GitHub to access Archgate editor plugins. If you are not registered yet, the CLI handles signup automatically -- it prompts for your email, editor preference (Claude Code, VS Code, Copilot CLI, or Cursor), and use case, then registers you before completing the login. ```bash archgate login ``` -Starts a GitHub Device Flow (OAuth). The CLI displays a one-time code and a URL. Open the URL in your browser, enter the code, and authorize the Archgate GitHub OAuth App. Once authorized, the CLI exchanges your GitHub identity for an Archgate plugin token and stores both in `~/.archgate/credentials`. +Starts a GitHub Device Flow (OAuth). The CLI displays a one-time code and a URL. Open the URL in your browser, enter the code, and authorize the Archgate GitHub OAuth App. Once authorized, the CLI exchanges your GitHub identity for an Archgate plugin token and stores both in `~/.archgate/credentials`. If the current directory already contains `.archgate/adrs/`, the post-login message shows `archgate check` as the suggested next step instead of `archgate init`. Credentials are required to install editor plugins via `archgate init --install-plugin`. The CLI itself (check, init, etc.) works without login. :::tip[Plugin beta] -Editor plugins are currently in beta. Sign up at [plugins.archgate.dev](https://plugins.archgate.dev) to get access. +Editor plugins are currently in beta. Run `archgate login` to sign up and authenticate directly from the CLI -- no separate registration step needed. ::: ### Subcommands @@ -60,12 +60,61 @@ and enter the code: ABCD-1234 Waiting for authorization... GitHub user: yourname + +No account found. Let's get you signed up. +Email: you@example.com +Editor: Claude Code +Use case: Enforcing ADRs in our monorepo + +Registering... Claiming archgate plugin token... Authenticated as yourname. Plugin access is now available. Run `archgate init` to set up a project with the archgate plugin. ``` +If the project already has `.archgate/adrs/`, the final line reads: + +``` +Run `archgate check` to validate your project against its ADRs. +``` + +### Troubleshooting + +#### TLS/corporate proxy errors + +If `archgate login` fails with a TLS certificate error (common behind corporate proxies), point your runtime at your organization's CA bundle using the `NODE_EXTRA_CA_CERTS` environment variable. + +On macOS/Linux: + +```bash +export NODE_EXTRA_CA_CERTS=/path/to/your-corporate-ca.pem +archgate login +``` + +On Windows (PowerShell): + +```powershell +$env:NODE_EXTRA_CA_CERTS = "C:\path\to\your-corporate-ca.pem" +archgate login +``` + +On Windows (cmd): + +```cmd +set NODE_EXTRA_CA_CERTS=C:\path\to\your-corporate-ca.pem +archgate login +``` + +On Windows (Git Bash): + +```bash +export NODE_EXTRA_CA_CERTS=/c/path/to/your-corporate-ca.pem +archgate login +``` + +Ask your IT team for the correct certificate path if you are unsure. + Check login status: ```bash From 4ac680cd4849326ea62bc50e796354703943d3ed Mon Sep 17 00:00:00 2001 From: Rhuan Barreto Date: Wed, 18 Mar 2026 16:39:36 +0100 Subject: [PATCH 11/20] feat: offer inline login and signup during archgate init When running `archgate init` without credentials, the CLI now asks if the user wants to install the editor plugin. If yes, it runs the full GitHub device flow + signup inline, then continues with plugin installation. Skips interactive prompts in non-TTY environments (agent-driven runs) to avoid blocking MCP servers and onboarding agents. --- src/commands/init.ts | 136 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 132 insertions(+), 4 deletions(-) diff --git a/src/commands/init.ts b/src/commands/init.ts index 59b2dd47..d47abc30 100644 --- a/src/commands/init.ts +++ b/src/commands/init.ts @@ -2,11 +2,21 @@ import { styleText } from "node:util"; import type { Command } from "@commander-js/extra-typings"; import { Option } from "@commander-js/extra-typings"; +import inquirer from "inquirer"; -import { loadCredentials } from "../helpers/auth"; +import { + loadCredentials, + requestDeviceCode, + pollForAccessToken, + getGitHubUser, + claimArchgateToken, + saveCredentials, +} from "../helpers/auth"; import { initProject } from "../helpers/init-project"; import type { EditorTarget } from "../helpers/init-project"; import { logError, logInfo, logWarn } from "../helpers/log"; +import { SignupRequiredError, requestSignup } from "../helpers/signup"; +import { isTlsError, tlsHintMessage } from "../helpers/tls"; const EDITOR_LABELS: Record = { claude: "Claude Code", @@ -37,9 +47,31 @@ export function registerInitCommand(program: Command) { ) .action(async (opts) => { try { - // Auto-detect: install plugin if credentials exist (unless explicitly off) - const installPlugin = - opts.installPlugin ?? (await loadCredentials()) !== null; + let hasCredentials = (await loadCredentials()) !== null; + + // If no credentials and --install-plugin not explicitly set, offer to log in + // Skip interactive prompts in non-TTY environments (agent-driven runs) + if ( + !hasCredentials && + opts.installPlugin === undefined && + process.stdin.isTTY + ) { + const { wantPlugin } = await inquirer.prompt([ + { + type: "confirm", + name: "wantPlugin", + message: + "Would you like to install the Archgate editor plugin? (requires GitHub login)", + default: true, + }, + ]); + + if (wantPlugin) { + hasCredentials = await runInlineLogin(opts.editor); + } + } + + const installPlugin = opts.installPlugin ?? hasCredentials; const result = await initProject(process.cwd(), { editor: opts.editor, @@ -74,12 +106,108 @@ export function registerInitCommand(program: Command) { ); } } catch (err) { + if (isTlsError(err)) { + logError(tlsHintMessage()); + process.exit(1); + } logError(err instanceof Error ? err.message : String(err)); process.exit(1); } }); } +/** Map init editor flags to signup editor identifiers. */ +const SIGNUP_EDITORS: Record = { + claude: "claude-code", + cursor: "cursor", + vscode: "vscode", + copilot: "copilot-cli", +}; + +/** + * Run the GitHub device flow + signup inline during init. + * Returns true if credentials were obtained. + */ +async function runInlineLogin(editor: EditorTarget): Promise { + console.log("\nAuthenticating with GitHub...\n"); + + 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..."); + + const githubToken = await pollForAccessToken( + deviceCode.device_code, + deviceCode.interval, + deviceCode.expires_in + ); + + const { login: githubUser, email: githubEmail } = + await getGitHubUser(githubToken); + logInfo(`GitHub user: ${styleText("bold", githubUser)}`); + + console.log("Claiming archgate plugin token..."); + let archgateToken: string; + try { + archgateToken = await claimArchgateToken(githubToken); + } catch (err) { + if (!(err instanceof SignupRequiredError)) throw err; + + console.log( + `\nYour GitHub account ${styleText("bold", githubUser)} is not yet registered.` + ); + console.log("Let's sign you up now.\n"); + + const answers = await inquirer.prompt([ + { + type: "input", + name: "email", + message: "Email address:", + default: githubEmail ?? undefined, + validate: (v: string) => + /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v) || "Enter a valid email address", + }, + { + type: "input", + name: "useCase", + message: "How do you plan to use archgate?", + validate: (v: string) => + v.trim().length > 0 || "Please describe your use case", + }, + ]); + + console.log("\nSubmitting signup request..."); + const result = await requestSignup( + githubUser, + answers.email, + answers.useCase, + SIGNUP_EDITORS[editor] + ); + + if (!result.ok) { + logError("Signup request failed. Continuing without plugin."); + return false; + } + + archgateToken = result.token ?? (await claimArchgateToken(githubToken)); + } + + await saveCredentials({ + token: archgateToken, + github_user: githubUser, + created_at: new Date().toISOString().split("T")[0], + }); + + logInfo( + `Authenticated as ${styleText("bold", githubUser)}. Continuing with plugin installation.\n` + ); + return true; +} + /** * Print manual plugin installation instructions when the editor CLI is not available. */ From 6fb70a313a82523be76341526353d1c768533ee1 Mon Sep 17 00:00:00 2001 From: Rhuan Barreto Date: Wed, 18 Mar 2026 16:43:15 +0100 Subject: [PATCH 12/20] refactor: extract shared login+signup flow into login-flow.ts The device flow, signup prompt, and credential storage logic was duplicated between login.ts and init.ts. Now both commands use runLoginFlow() from helpers/login-flow.ts. When the editor is already known (init --editor), the signup prompt skips the editor question. --- src/commands/init.ts | 118 +++-------------------- src/commands/login.ts | 144 +++------------------------- src/helpers/login-flow.ts | 157 +++++++++++++++++++++++++++++++ tests/helpers/login-flow.test.ts | 28 ++++++ 4 files changed, 215 insertions(+), 232 deletions(-) create mode 100644 src/helpers/login-flow.ts create mode 100644 tests/helpers/login-flow.test.ts diff --git a/src/commands/init.ts b/src/commands/init.ts index d47abc30..3a8df6d5 100644 --- a/src/commands/init.ts +++ b/src/commands/init.ts @@ -4,18 +4,11 @@ import type { Command } from "@commander-js/extra-typings"; import { Option } from "@commander-js/extra-typings"; import inquirer from "inquirer"; -import { - loadCredentials, - requestDeviceCode, - pollForAccessToken, - getGitHubUser, - claimArchgateToken, - saveCredentials, -} from "../helpers/auth"; +import { loadCredentials } from "../helpers/auth"; import { initProject } from "../helpers/init-project"; import type { EditorTarget } from "../helpers/init-project"; import { logError, logInfo, logWarn } from "../helpers/log"; -import { SignupRequiredError, requestSignup } from "../helpers/signup"; +import { runLoginFlow } from "../helpers/login-flow"; import { isTlsError, tlsHintMessage } from "../helpers/tls"; const EDITOR_LABELS: Record = { @@ -32,6 +25,14 @@ const EDITOR_DIRS: Record = { copilot: ".github/copilot/", }; +/** Map init editor flags to signup editor identifiers. */ +const SIGNUP_EDITORS: Record = { + claude: "claude-code", + cursor: "cursor", + vscode: "vscode", + copilot: "copilot-cli", +}; + const editorOption = new Option("--editor ", "editor integration") .choices(["claude", "cursor", "vscode", "copilot"] as const) .default("claude" as const); @@ -67,7 +68,10 @@ export function registerInitCommand(program: Command) { ]); if (wantPlugin) { - hasCredentials = await runInlineLogin(opts.editor); + const result = await runLoginFlow({ + editor: SIGNUP_EDITORS[opts.editor], + }); + hasCredentials = result.ok; } } @@ -116,98 +120,6 @@ export function registerInitCommand(program: Command) { }); } -/** Map init editor flags to signup editor identifiers. */ -const SIGNUP_EDITORS: Record = { - claude: "claude-code", - cursor: "cursor", - vscode: "vscode", - copilot: "copilot-cli", -}; - -/** - * Run the GitHub device flow + signup inline during init. - * Returns true if credentials were obtained. - */ -async function runInlineLogin(editor: EditorTarget): Promise { - console.log("\nAuthenticating with GitHub...\n"); - - 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..."); - - const githubToken = await pollForAccessToken( - deviceCode.device_code, - deviceCode.interval, - deviceCode.expires_in - ); - - const { login: githubUser, email: githubEmail } = - await getGitHubUser(githubToken); - logInfo(`GitHub user: ${styleText("bold", githubUser)}`); - - console.log("Claiming archgate plugin token..."); - let archgateToken: string; - try { - archgateToken = await claimArchgateToken(githubToken); - } catch (err) { - if (!(err instanceof SignupRequiredError)) throw err; - - console.log( - `\nYour GitHub account ${styleText("bold", githubUser)} is not yet registered.` - ); - console.log("Let's sign you up now.\n"); - - const answers = await inquirer.prompt([ - { - type: "input", - name: "email", - message: "Email address:", - default: githubEmail ?? undefined, - validate: (v: string) => - /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v) || "Enter a valid email address", - }, - { - type: "input", - name: "useCase", - message: "How do you plan to use archgate?", - validate: (v: string) => - v.trim().length > 0 || "Please describe your use case", - }, - ]); - - console.log("\nSubmitting signup request..."); - const result = await requestSignup( - githubUser, - answers.email, - answers.useCase, - SIGNUP_EDITORS[editor] - ); - - if (!result.ok) { - logError("Signup request failed. Continuing without plugin."); - return false; - } - - archgateToken = result.token ?? (await claimArchgateToken(githubToken)); - } - - await saveCredentials({ - token: archgateToken, - github_user: githubUser, - created_at: new Date().toISOString().split("T")[0], - }); - - logInfo( - `Authenticated as ${styleText("bold", githubUser)}. Continuing with plugin installation.\n` - ); - return true; -} - /** * Print manual plugin installation instructions when the editor CLI is not available. */ @@ -227,7 +139,7 @@ function printManualInstructions(editor: EditorTarget, detail?: string): void { console.log(` ${styleText("bold", "copilot plugin install")} ${detail}`); break; default: - // cursor auto-installs always — should not reach here + // cursor/vscode auto-install — should not reach here break; } } diff --git a/src/commands/login.ts b/src/commands/login.ts index bfa2bb16..91a19ca2 100644 --- a/src/commands/login.ts +++ b/src/commands/login.ts @@ -1,20 +1,11 @@ import { styleText } from "node:util"; import type { Command } from "@commander-js/extra-typings"; -import inquirer from "inquirer"; -import { - requestDeviceCode, - pollForAccessToken, - getGitHubUser, - claimArchgateToken, - saveCredentials, - loadCredentials, - clearCredentials, -} from "../helpers/auth"; +import { loadCredentials, clearCredentials } from "../helpers/auth"; import { logError, logInfo } from "../helpers/log"; +import { runLoginFlow } from "../helpers/login-flow"; import { findProjectRoot } from "../helpers/paths"; -import { SignupRequiredError, requestSignup } from "../helpers/signup"; import { isTlsError, tlsHintMessage } from "../helpers/tls"; export function registerLoginCommand(program: Command) { @@ -34,7 +25,12 @@ export function registerLoginCommand(program: Command) { return; } - await runDeviceFlow(); + const result = await runLoginFlow(); + if (result.ok) { + printNextStep(); + } else { + process.exit(1); + } } catch (err) { if (isTlsError(err)) { logError(tlsHintMessage()); @@ -73,7 +69,12 @@ export function registerLoginCommand(program: Command) { .action(async () => { try { await clearCredentials(); - await runDeviceFlow(); + const result = await runLoginFlow(); + if (result.ok) { + printNextStep(); + } else { + process.exit(1); + } } catch (err) { if (isTlsError(err)) { logError(tlsHintMessage()); @@ -85,122 +86,7 @@ export function registerLoginCommand(program: Command) { }); } -async function runDeviceFlow(): Promise { - 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 user info - const { login: githubUser, email: githubEmail } = - await getGitHubUser(githubToken); - logInfo(`GitHub user: ${styleText("bold", githubUser)}`); - - // Step 4: Exchange GitHub token for archgate plugin token - console.log("Claiming archgate plugin token..."); - try { - const archgateToken = await claimArchgateToken(githubToken); - await storeAndFinish(archgateToken, githubUser); - } catch (err) { - if (!(err instanceof SignupRequiredError)) throw err; - - console.log( - `\nYour GitHub account ${styleText("bold", githubUser)} is not yet registered.` - ); - console.log("Let's sign you up now.\n"); - - await runSignupFlow(githubUser, githubToken, githubEmail); - } -} - -async function runSignupFlow( - githubUser: string, - githubToken: string, - githubEmail: string | null -): Promise { - const answers = await inquirer.prompt([ - { - type: "input", - name: "email", - message: "Email address:", - default: githubEmail ?? undefined, - validate: (v: string) => - /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v) || "Enter a valid email address", - }, - { - type: "list", - name: "editor", - message: "Which editor will you use with archgate?", - choices: [ - { name: "Claude Code", value: "claude-code" }, - { name: "VS Code", value: "vscode" }, - { name: "Copilot CLI", value: "copilot-cli" }, - { name: "Cursor", value: "cursor" }, - ], - }, - { - type: "input", - name: "useCase", - message: "How do you plan to use archgate?", - validate: (v: string) => - v.trim().length > 0 || "Please describe your use case", - }, - ]); - - console.log("\nSubmitting signup request..."); - const result = await requestSignup( - githubUser, - answers.email, - answers.useCase, - answers.editor - ); - - if (!result.ok) { - logError( - "Signup request failed. Please try again or sign up at https://plugins.archgate.dev" - ); - process.exit(1); - } - - // Use the token from signup if available, otherwise claim separately - let archgateToken = result.token; - if (!archgateToken) { - console.log("Claiming archgate plugin token..."); - archgateToken = await claimArchgateToken(githubToken); - } - - await storeAndFinish(archgateToken, githubUser); -} - -async function storeAndFinish( - archgateToken: string, - githubUser: string -): Promise { - 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.` - ); - +function printNextStep(): void { if (findProjectRoot()) { console.log( "Run `archgate check` to validate your project against its ADRs." diff --git a/src/helpers/login-flow.ts b/src/helpers/login-flow.ts new file mode 100644 index 00000000..6da3bdeb --- /dev/null +++ b/src/helpers/login-flow.ts @@ -0,0 +1,157 @@ +/** + * login-flow.ts — Shared GitHub device flow + signup logic + * used by both `login` and `init` commands. + */ + +import { styleText } from "node:util"; + +import inquirer from "inquirer"; + +import { + requestDeviceCode, + pollForAccessToken, + getGitHubUser, + claimArchgateToken, + saveCredentials, +} from "./auth"; +import { logError, logInfo } from "./log"; +import { SignupRequiredError, requestSignup } from "./signup"; + +export interface LoginFlowOptions { + /** + * Pre-selected editor for signup (skip the editor prompt). + * When omitted, the user is prompted to choose. + */ + editor?: string; +} + +export interface LoginFlowResult { + /** Whether credentials were obtained. */ + ok: boolean; + /** GitHub username, if login succeeded. */ + githubUser?: string; +} + +/** + * Run the full GitHub device flow: authenticate, claim token (or sign up + * if the user is unregistered), and store credentials. + * + * Returns `{ ok: true }` when credentials are stored, `{ ok: false }` on + * failure (error is already printed). + */ +export async function runLoginFlow( + options?: LoginFlowOptions +): Promise { + console.log("Authenticating with GitHub...\n"); + + 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..."); + + const githubToken = await pollForAccessToken( + deviceCode.device_code, + deviceCode.interval, + deviceCode.expires_in + ); + + const { login: githubUser, email: githubEmail } = + await getGitHubUser(githubToken); + logInfo(`GitHub user: ${styleText("bold", githubUser)}`); + + console.log("Claiming archgate plugin token..."); + let archgateToken: string; + try { + archgateToken = await claimArchgateToken(githubToken); + } catch (err) { + if (!(err instanceof SignupRequiredError)) throw err; + + console.log( + `\nYour GitHub account ${styleText("bold", githubUser)} is not yet registered.` + ); + console.log("Let's sign you up now.\n"); + + const result = await runSignupPrompt( + githubUser, + githubToken, + githubEmail, + options?.editor + ); + if (!result) return { ok: false }; + archgateToken = result; + } + + await saveCredentials({ + token: archgateToken, + github_user: githubUser, + created_at: new Date().toISOString().split("T")[0], + }); + + logInfo( + `Authenticated as ${styleText("bold", githubUser)}. Plugin access is now available.` + ); + return { ok: true, githubUser }; +} + +/** + * Prompt for signup details, submit the request, and return the token. + * Returns null on failure (error is already printed). + */ +async function runSignupPrompt( + githubUser: string, + githubToken: string, + githubEmail: string | null, + preselectedEditor?: string +): Promise { + const { email } = await inquirer.prompt({ + type: "input", + name: "email", + message: "Email address:", + default: githubEmail ?? undefined, + validate: (v: string) => + /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v) || "Enter a valid email address", + }); + + let editor = preselectedEditor; + if (!editor) { + const ans = await inquirer.prompt({ + type: "list", + name: "editor", + message: "Which editor will you use with archgate?", + choices: [ + { name: "Claude Code", value: "claude-code" }, + { name: "VS Code", value: "vscode" }, + { name: "Copilot CLI", value: "copilot-cli" }, + { name: "Cursor", value: "cursor" }, + ], + }); + editor = ans.editor; + } + + const { useCase } = await inquirer.prompt({ + type: "input", + name: "useCase", + message: "How do you plan to use archgate?", + validate: (v: string) => + v.trim().length > 0 || "Please describe your use case", + }); + + console.log("\nSubmitting signup request..."); + const result = await requestSignup(githubUser, email, useCase, editor!); + + if (!result.ok) { + logError( + "Signup request failed. Please try again or sign up at https://plugins.archgate.dev" + ); + return null; + } + + if (result.token) return result.token; + + console.log("Claiming archgate plugin token..."); + return claimArchgateToken(githubToken); +} diff --git a/tests/helpers/login-flow.test.ts b/tests/helpers/login-flow.test.ts new file mode 100644 index 00000000..67bd2ba3 --- /dev/null +++ b/tests/helpers/login-flow.test.ts @@ -0,0 +1,28 @@ +import { describe, expect, test } from "bun:test"; + +import { runLoginFlow } from "../../src/helpers/login-flow"; +import type { + LoginFlowOptions, + LoginFlowResult, +} from "../../src/helpers/login-flow"; + +describe("login-flow", () => { + test("runLoginFlow is exported as a function", () => { + expect(typeof runLoginFlow).toBe("function"); + }); + + test("LoginFlowOptions accepts editor field", () => { + const opts: LoginFlowOptions = { editor: "claude-code" }; + expect(opts.editor).toBe("claude-code"); + }); + + test("LoginFlowResult shape", () => { + const success: LoginFlowResult = { ok: true, githubUser: "octocat" }; + expect(success.ok).toBe(true); + expect(success.githubUser).toBe("octocat"); + + const failure: LoginFlowResult = { ok: false }; + expect(failure.ok).toBe(false); + expect(failure.githubUser).toBeUndefined(); + }); +}); From cc5492189617fbcb5e7cc53d6b00dee480c546ef Mon Sep 17 00:00:00 2001 From: Rhuan Barreto Date: Wed, 18 Mar 2026 16:47:45 +0100 Subject: [PATCH 13/20] docs: remove change-announcement language from login documentation Write docs as if the reader is seeing them for the first time, not announcing what changed. --- docs/src/content/docs/getting-started/installation.mdx | 2 +- docs/src/content/docs/getting-started/quick-start.mdx | 2 +- docs/src/content/docs/guides/claude-code-plugin.mdx | 4 ++-- docs/src/content/docs/pt-br/getting-started/quick-start.mdx | 2 +- docs/src/content/docs/pt-br/guides/claude-code-plugin.mdx | 4 ++-- docs/src/content/docs/pt-br/reference/cli-commands.mdx | 6 ++++-- docs/src/content/docs/reference/cli-commands.mdx | 6 ++++-- 7 files changed, 15 insertions(+), 11 deletions(-) diff --git a/docs/src/content/docs/getting-started/installation.mdx b/docs/src/content/docs/getting-started/installation.mdx index a73d2157..e19f086d 100644 --- a/docs/src/content/docs/getting-started/installation.mdx +++ b/docs/src/content/docs/getting-started/installation.mdx @@ -133,5 +133,5 @@ Restart your shell, then run `npm install -g archgate` again. The `archgate` com Once installed, run `archgate init` in your project to set up governance. See the [Quick Start](/getting-started/quick-start/) guide for a walkthrough. :::tip[Editor plugins (beta)] -Want your AI agent to read ADRs before coding and validate after? The editor plugins for [Claude Code](/guides/claude-code-plugin/) and [Cursor](/guides/cursor-integration/) add a full governance workflow on top of the CLI. Run `archgate login` to sign up and authenticate directly from the CLI. +Want your AI agent to read ADRs before coding and validate after? The editor plugins for [Claude Code](/guides/claude-code-plugin/) and [Cursor](/guides/cursor-integration/) add a full governance workflow on top of the CLI. Run `archgate login` to sign up and get started. ::: diff --git a/docs/src/content/docs/getting-started/quick-start.mdx b/docs/src/content/docs/getting-started/quick-start.mdx index 3fdf6101..6cabb145 100644 --- a/docs/src/content/docs/getting-started/quick-start.mdx +++ b/docs/src/content/docs/getting-started/quick-start.mdx @@ -140,5 +140,5 @@ Now that you have a working setup, dive deeper: - [Cursor Integration](/guides/cursor-integration/) — Use Archgate with Cursor IDE for AI-assisted development. :::tip[Editor plugins (beta)] -Want AI agents that automatically read your ADRs before coding? Run `archgate login` to sign up and authenticate directly from the CLI, then run `archgate init --install-plugin` to set up the plugin. +Want AI agents that automatically read your ADRs before coding? Run `archgate login` to sign up and authenticate, then run `archgate init --install-plugin` to set up the plugin. ::: diff --git a/docs/src/content/docs/guides/claude-code-plugin.mdx b/docs/src/content/docs/guides/claude-code-plugin.mdx index d6650a1e..02b03641 100644 --- a/docs/src/content/docs/guides/claude-code-plugin.mdx +++ b/docs/src/content/docs/guides/claude-code-plugin.mdx @@ -29,12 +29,12 @@ The `archgate:developer` agent is set as the default agent via `.claude/settings ## Installation :::note[Beta access required] -The Claude Code plugin is currently in beta. Run `archgate login` to sign up and authenticate directly from the CLI -- no separate registration step needed. +The Claude Code plugin is currently in beta. Run `archgate login` to sign up and authenticate. ::: ### 1. Log in with GitHub -Authenticate with your GitHub account to obtain a plugin token. If you are not registered yet, the CLI handles signup interactively -- it prompts for your email, editor preference, and use case, then registers you automatically: +Authenticate with your GitHub account to obtain a plugin token: ```bash archgate login diff --git a/docs/src/content/docs/pt-br/getting-started/quick-start.mdx b/docs/src/content/docs/pt-br/getting-started/quick-start.mdx index f05565a1..b495fa72 100644 --- a/docs/src/content/docs/pt-br/getting-started/quick-start.mdx +++ b/docs/src/content/docs/pt-br/getting-started/quick-start.mdx @@ -140,5 +140,5 @@ Agora que você tem uma configuração funcionando, aprofunde-se: - [Integração com Cursor](/pt-br/guides/cursor-integration/) — Use o Archgate com o Cursor IDE para desenvolvimento assistido por IA. :::tip[Plugins para editores (beta)] -Quer agentes de IA que leiam automaticamente seus ADRs antes de programar? Execute `archgate login` para se cadastrar e autenticar diretamente pela CLI, depois execute `archgate init --install-plugin` para configurar o plugin. +Quer agentes de IA que leiam automaticamente seus ADRs antes de programar? Execute `archgate login` para se cadastrar e autenticar, depois execute `archgate init --install-plugin` para configurar o plugin. ::: diff --git a/docs/src/content/docs/pt-br/guides/claude-code-plugin.mdx b/docs/src/content/docs/pt-br/guides/claude-code-plugin.mdx index e99ccd97..e7b252cc 100644 --- a/docs/src/content/docs/pt-br/guides/claude-code-plugin.mdx +++ b/docs/src/content/docs/pt-br/guides/claude-code-plugin.mdx @@ -29,12 +29,12 @@ O agente `archgate:developer` é definido como agente padrão via `.claude/setti ## Instalação :::note[Acesso beta necessário] -O plugin para Claude Code está atualmente em beta. Execute `archgate login` para se cadastrar e autenticar diretamente pela CLI -- nenhum passo de registro separado é necessário. +O plugin para Claude Code está atualmente em beta. Execute `archgate login` para se cadastrar e autenticar. ::: ### 1. Faça login com o GitHub -Autentique-se com sua conta do GitHub para obter um token do plugin. Se você ainda não possui cadastro, a CLI cuida do registro de forma interativa -- solicita seu email, preferência de editor e caso de uso, e então realiza o cadastro automaticamente: +Autentique-se com sua conta do GitHub para obter um token do plugin: ```bash archgate login diff --git a/docs/src/content/docs/pt-br/reference/cli-commands.mdx b/docs/src/content/docs/pt-br/reference/cli-commands.mdx index 11fabc94..76ecae8d 100644 --- a/docs/src/content/docs/pt-br/reference/cli-commands.mdx +++ b/docs/src/content/docs/pt-br/reference/cli-commands.mdx @@ -27,12 +27,14 @@ Autentique-se com o GitHub para acessar os plugins de editor do Archgate. Se voc archgate login ``` -Inicia um GitHub Device Flow (OAuth). A CLI exibe um código de uso único e uma URL. Abra a URL no seu navegador, insira o código e autorize o Archgate GitHub OAuth App. Após a autorização, a CLI troca sua identidade do GitHub por um token de plugin do Archgate e armazena ambos em `~/.archgate/credentials`. Se o diretório atual já contiver `.archgate/adrs/`, a mensagem pós-login sugere `archgate check` como próximo passo em vez de `archgate init`. +Inicia um GitHub Device Flow (OAuth). A CLI exibe um código de uso único e uma URL. Abra a URL no seu navegador, insira o código e autorize o Archgate GitHub OAuth App. Após a autorização, a CLI troca sua identidade do GitHub por um token de plugin do Archgate e armazena ambos em `~/.archgate/credentials`. + +Se sua conta do GitHub ainda não estiver cadastrada, a CLI solicita seu email, editor preferido e caso de uso, e realiza o cadastro automaticamente. As credenciais são necessárias para instalar plugins de editor via `archgate init --install-plugin`. A CLI em si (check, init, etc.) funciona sem login. :::tip[Plugin em beta] -Os plugins de editor estão atualmente em beta. Execute `archgate login` para se cadastrar e autenticar diretamente pela CLI -- nenhum passo de registro separado é necessário. +Os plugins de editor estão atualmente em beta. Execute `archgate login` para se cadastrar e autenticar. ::: ### Subcomandos diff --git a/docs/src/content/docs/reference/cli-commands.mdx b/docs/src/content/docs/reference/cli-commands.mdx index fb3473af..104f3fe5 100644 --- a/docs/src/content/docs/reference/cli-commands.mdx +++ b/docs/src/content/docs/reference/cli-commands.mdx @@ -27,12 +27,14 @@ Authenticate with GitHub to access Archgate editor plugins. If you are not regis archgate login ``` -Starts a GitHub Device Flow (OAuth). The CLI displays a one-time code and a URL. Open the URL in your browser, enter the code, and authorize the Archgate GitHub OAuth App. Once authorized, the CLI exchanges your GitHub identity for an Archgate plugin token and stores both in `~/.archgate/credentials`. If the current directory already contains `.archgate/adrs/`, the post-login message shows `archgate check` as the suggested next step instead of `archgate init`. +Starts a GitHub Device Flow (OAuth). The CLI displays a one-time code and a URL. Open the URL in your browser, enter the code, and authorize the Archgate GitHub OAuth App. Once authorized, the CLI exchanges your GitHub identity for an Archgate plugin token and stores both in `~/.archgate/credentials`. + +If your GitHub account is not yet registered, the CLI prompts for your email, preferred editor, and use case, then signs you up automatically. Credentials are required to install editor plugins via `archgate init --install-plugin`. The CLI itself (check, init, etc.) works without login. :::tip[Plugin beta] -Editor plugins are currently in beta. Run `archgate login` to sign up and authenticate directly from the CLI -- no separate registration step needed. +Editor plugins are currently in beta. Run `archgate login` to sign up and authenticate. ::: ### Subcommands From 819d370f05a7948f731d102849266e40a223a899 Mon Sep 17 00:00:00 2001 From: Rhuan Barreto Date: Wed, 18 Mar 2026 16:53:49 +0100 Subject: [PATCH 14/20] docs: replace plugins.archgate.dev signup links with archgate login All beta banners across editor guides and index pages now point to `archgate login` for signup instead of the external website. --- docs/src/content/docs/guides/copilot-cli-plugin.mdx | 2 +- docs/src/content/docs/guides/cursor-integration.mdx | 2 +- docs/src/content/docs/guides/vscode-plugin.mdx | 2 +- docs/src/content/docs/index.mdx | 2 +- docs/src/content/docs/pt-br/guides/copilot-cli-plugin.mdx | 2 +- docs/src/content/docs/pt-br/guides/cursor-integration.mdx | 2 +- docs/src/content/docs/pt-br/guides/vscode-plugin.mdx | 2 +- docs/src/content/docs/pt-br/index.mdx | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/src/content/docs/guides/copilot-cli-plugin.mdx b/docs/src/content/docs/guides/copilot-cli-plugin.mdx index 71532266..31d6f207 100644 --- a/docs/src/content/docs/guides/copilot-cli-plugin.mdx +++ b/docs/src/content/docs/guides/copilot-cli-plugin.mdx @@ -12,7 +12,7 @@ Copilot CLI supports plugin installation from git repositories using `copilot pl ## Installation :::note[Beta access required] -The Copilot CLI plugin is currently in beta. [Sign up at plugins.archgate.dev](https://plugins.archgate.dev) to get access before following the steps below. +The Copilot CLI plugin is currently in beta. Run `archgate login` to sign up and authenticate before following the steps below. ::: ### 1. Log in with GitHub diff --git a/docs/src/content/docs/guides/cursor-integration.mdx b/docs/src/content/docs/guides/cursor-integration.mdx index f817341f..8c116808 100644 --- a/docs/src/content/docs/guides/cursor-integration.mdx +++ b/docs/src/content/docs/guides/cursor-integration.mdx @@ -16,7 +16,7 @@ archgate init --editor cursor ### With plugin (beta) :::note[Beta access required] -The Cursor plugin is currently in beta. [Sign up at plugins.archgate.dev](https://plugins.archgate.dev) to get access. +The Cursor plugin is currently in beta. Run `archgate login` to sign up and authenticate. ::: If you have logged in via `archgate login`, the init command also downloads and installs the Archgate plugin for Cursor. The plugin provides pre-built agent rules and skills that give Cursor's AI agent a full governance workflow. diff --git a/docs/src/content/docs/guides/vscode-plugin.mdx b/docs/src/content/docs/guides/vscode-plugin.mdx index 23ff5072..77ce062a 100644 --- a/docs/src/content/docs/guides/vscode-plugin.mdx +++ b/docs/src/content/docs/guides/vscode-plugin.mdx @@ -18,7 +18,7 @@ Agent plugins require **VS Code 1.110 (February 2026 release) or later**. Earlie ::: :::note[Beta access required] -The VS Code plugin is currently in beta. [Sign up at plugins.archgate.dev](https://plugins.archgate.dev) to get access before following the steps below. +The VS Code plugin is currently in beta. Run `archgate login` to sign up and authenticate before following the steps below. ::: ### 1. Log in with GitHub diff --git a/docs/src/content/docs/index.mdx b/docs/src/content/docs/index.mdx index e578b631..26e4b2bd 100644 --- a/docs/src/content/docs/index.mdx +++ b/docs/src/content/docs/index.mdx @@ -75,7 +75,7 @@ The Archgate CLI works standalone, but **editor plugins** unlock a full AI gover :::tip[Beta access] -Editor plugins are currently in beta. Sign up at [plugins.archgate.dev](https://plugins.archgate.dev) to get access, then install with `archgate login` and `archgate init --install-plugin`. +Editor plugins are currently in beta. Run `archgate login` to sign up and authenticate, then `archgate init --install-plugin` to set up the plugin. ::: diff --git a/docs/src/content/docs/pt-br/guides/copilot-cli-plugin.mdx b/docs/src/content/docs/pt-br/guides/copilot-cli-plugin.mdx index 6813d4c0..a2048da9 100644 --- a/docs/src/content/docs/pt-br/guides/copilot-cli-plugin.mdx +++ b/docs/src/content/docs/pt-br/guides/copilot-cli-plugin.mdx @@ -12,7 +12,7 @@ O Copilot CLI suporta instalação de plugins a partir de repositórios git usan ## Instalação :::note[Acesso beta necessário] -O plugin para Copilot CLI está atualmente em beta. [Cadastre-se em plugins.archgate.dev](https://plugins.archgate.dev) para obter acesso antes de seguir os passos abaixo. +O plugin para Copilot CLI está atualmente em beta. Execute `archgate login` para se cadastrar e autenticar antes de seguir os passos abaixo. ::: ### 1. Faça login com o GitHub diff --git a/docs/src/content/docs/pt-br/guides/cursor-integration.mdx b/docs/src/content/docs/pt-br/guides/cursor-integration.mdx index 53c7933a..0c0bd8d8 100644 --- a/docs/src/content/docs/pt-br/guides/cursor-integration.mdx +++ b/docs/src/content/docs/pt-br/guides/cursor-integration.mdx @@ -16,7 +16,7 @@ archgate init --editor cursor ### Com plugin (beta) :::note[Acesso beta necessário] -O plugin para Cursor está atualmente em beta. [Cadastre-se em plugins.archgate.dev](https://plugins.archgate.dev) para obter acesso. +O plugin para Cursor está atualmente em beta. Execute `archgate login` para se cadastrar e autenticar. ::: Se você fez login via `archgate login`, o comando init também baixa e instala o plugin Archgate para o Cursor. O plugin fornece regras de agente pré-configuradas e skills que dão ao agente de IA do Cursor um fluxo de governança completo. diff --git a/docs/src/content/docs/pt-br/guides/vscode-plugin.mdx b/docs/src/content/docs/pt-br/guides/vscode-plugin.mdx index f697c911..02303720 100644 --- a/docs/src/content/docs/pt-br/guides/vscode-plugin.mdx +++ b/docs/src/content/docs/pt-br/guides/vscode-plugin.mdx @@ -18,7 +18,7 @@ Plugins de agente requerem **VS Code 1.110 (lançamento de fevereiro de 2026) ou ::: :::note[Acesso beta necessário] -O plugin para VS Code está atualmente em beta. [Cadastre-se em plugins.archgate.dev](https://plugins.archgate.dev) para obter acesso antes de seguir os passos abaixo. +O plugin para VS Code está atualmente em beta. Execute `archgate login` para se cadastrar e autenticar antes de seguir os passos abaixo. ::: ### 1. Faça login com o GitHub diff --git a/docs/src/content/docs/pt-br/index.mdx b/docs/src/content/docs/pt-br/index.mdx index dce25fe3..c8834b12 100644 --- a/docs/src/content/docs/pt-br/index.mdx +++ b/docs/src/content/docs/pt-br/index.mdx @@ -76,7 +76,7 @@ A CLI do Archgate funciona de forma independente, mas os **plugins para editores :::tip[Acesso beta] -Os plugins para editores estão atualmente em beta. Cadastre-se em [plugins.archgate.dev](https://plugins.archgate.dev) para obter acesso, depois instale com `archgate login` e `archgate init --install-plugin`. +Os plugins para editores estão atualmente em beta. Execute `archgate login` para se cadastrar e autenticar, depois `archgate init --install-plugin` para configurar o plugin. ::: From 2d687dafa1bc984d35f4dcac4fb2b5121c0af9ec Mon Sep 17 00:00:00 2001 From: Rhuan Barreto Date: Wed, 18 Mar 2026 16:55:11 +0100 Subject: [PATCH 15/20] docs: add VS Code and Copilot CLI guides to index pages The editor plugin section on both EN and PT-BR index pages now links to all four editor integration guides. Also fixes PT-BR index links to use /pt-br/ prefix. --- docs/src/content/docs/index.mdx | 10 ++++++++++ docs/src/content/docs/pt-br/index.mdx | 14 ++++++++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/docs/src/content/docs/index.mdx b/docs/src/content/docs/index.mdx index 26e4b2bd..307b2912 100644 --- a/docs/src/content/docs/index.mdx +++ b/docs/src/content/docs/index.mdx @@ -84,6 +84,16 @@ Editor plugins are currently in beta. Run `archgate login` to sign up and authen href="/guides/claude-code-plugin/" description="Full setup and usage guide for the Claude Code plugin." /> + + + + From 4a618b8902cacc35f60818e735876a7cf9675b8b Mon Sep 17 00:00:00 2001 From: Rhuan Barreto Date: Wed, 18 Mar 2026 16:58:56 +0100 Subject: [PATCH 16/20] feat: add GEN-002 rule to catch locale-prefixed links in i18n pages Starlight resolves locale routes automatically, so PT-BR pages should use /guides/... not /pt-br/guides/.... The new no-locale-prefix-in-links rule catches this. Also fixes 28 pre-existing violations across PT-BR docs. --- .archgate/adrs/GEN-002-docs-i18n.rules.ts | 35 +++++++++++++++++++ docs/src/content/docs/pt-br/concepts/adrs.mdx | 4 +-- .../src/content/docs/pt-br/concepts/rules.mdx | 2 +- .../pt-br/examples/common-rule-patterns.mdx | 2 +- .../pt-br/getting-started/installation.mdx | 4 +-- .../pt-br/getting-started/quick-start.mdx | 22 ++++++------ .../docs/pt-br/guides/ci-integration.mdx | 4 +-- .../docs/pt-br/guides/copilot-cli-plugin.mdx | 2 +- .../docs/pt-br/guides/cursor-integration.mdx | 2 +- .../docs/pt-br/guides/vscode-plugin.mdx | 2 +- .../docs/pt-br/guides/writing-adrs.mdx | 4 +-- .../docs/pt-br/guides/writing-rules.mdx | 6 ++-- docs/src/content/docs/pt-br/index.mdx | 8 ++--- .../docs/pt-br/reference/adr-schema.mdx | 2 +- 14 files changed, 67 insertions(+), 32 deletions(-) diff --git a/.archgate/adrs/GEN-002-docs-i18n.rules.ts b/.archgate/adrs/GEN-002-docs-i18n.rules.ts index 16e2f759..742b3ab5 100644 --- a/.archgate/adrs/GEN-002-docs-i18n.rules.ts +++ b/.archgate/adrs/GEN-002-docs-i18n.rules.ts @@ -9,7 +9,42 @@ const LOCALES = ["pt-br"]; const CONTENT_ROOT = "docs/src/content/docs"; +/** Patterns that match locale-prefixed internal links in MDX files. */ +const LOCALE_LINK_PATTERNS = LOCALES.map( + (locale) => new RegExp(`(?:href="|\\]\\()/${locale}/`, "g") +); + export default defineRules({ + "no-locale-prefix-in-links": { + description: + "Locale pages must not use locale-prefixed internal links — Starlight resolves them automatically", + severity: "error", + async check(ctx) { + /* oxlint-disable no-await-in-loop -- sequential per-locale is fine for a small list */ + for (const locale of LOCALES) { + const localePrefix = `${CONTENT_ROOT}/${locale}/`; + const localeFiles = (await ctx.glob(`${localePrefix}**/*.mdx`)).filter( + (f) => f.startsWith(localePrefix) + ); + const pattern = LOCALE_LINK_PATTERNS[LOCALES.indexOf(locale)]; + + const matches = await Promise.all( + localeFiles.map((file) => ctx.grep(file, pattern)) + ); + for (const fileMatches of matches) { + for (const m of fileMatches) { + ctx.report.violation({ + message: `Internal link contains locale prefix "/${locale}/". Remove the prefix — Starlight resolves locale routes automatically.`, + file: m.file, + line: m.line, + fix: `Replace "/${locale}/..." with "/..." in the link`, + }); + } + } + } + /* oxlint-enable no-await-in-loop */ + }, + }, "i18n-page-parity": { description: "Every root MDX file must have a corresponding translation in each locale, and vice versa", diff --git a/docs/src/content/docs/pt-br/concepts/adrs.mdx b/docs/src/content/docs/pt-br/concepts/adrs.mdx index e341bd14..e4fc1cbd 100644 --- a/docs/src/content/docs/pt-br/concepts/adrs.mdx +++ b/docs/src/content/docs/pt-br/concepts/adrs.mdx @@ -16,7 +16,7 @@ O documento é um arquivo Markdown com frontmatter YAML armazenado em `.archgate Tanto humanos quanto agentes de IA consomem esse documento. Quando um agente de IA de codificação está prestes a escrever código, ele lê os ADRs relevantes para entender as restrições antes de gerar qualquer coisa. :::tip[Automatize isso com plugins de editor] -Com o plugin do [Claude Code](/pt-br/guides/claude-code-plugin/) ou [Cursor](/pt-br/guides/cursor-integration/), seu agente de IA lê os ADRs aplicáveis automaticamente antes de cada tarefa de codificação -- sem necessidade de copiar e colar manualmente nos prompts. [Inscreva-se para acesso beta](https://plugins.archgate.dev). +Com o plugin do [Claude Code](/guides/claude-code-plugin/) ou [Cursor](/guides/cursor-integration/), seu agente de IA lê os ADRs aplicáveis automaticamente antes de cada tarefa de codificação -- sem necessidade de copiar e colar manualmente nos prompts. [Inscreva-se para acesso beta](https://plugins.archgate.dev). ::: ### ADR como Regras @@ -41,7 +41,7 @@ ARCH-001-command-structure.md ARCH-001-command-structure.rules.ts ``` -O prefixo vem do domínio do ADR (veja [Domínios](/pt-br/concepts/domains/)). O número de sequência é preenchido com zeros até três dígitos e auto-incrementado por `archgate adr create`. +O prefixo vem do domínio do ADR (veja [Domínios](/concepts/domains/)). O número de sequência é preenchido com zeros até três dígitos e auto-incrementado por `archgate adr create`. ## Frontmatter YAML diff --git a/docs/src/content/docs/pt-br/concepts/rules.mdx b/docs/src/content/docs/pt-br/concepts/rules.mdx index cc85a079..59a37bc9 100644 --- a/docs/src/content/docs/pt-br/concepts/rules.mdx +++ b/docs/src/content/docs/pt-br/concepts/rules.mdx @@ -162,5 +162,5 @@ As regras são executadas com as seguintes garantias: - **Arquivos alterados para modo staged** -- Ao executar `archgate check --staged`, `ctx.changedFiles` contém apenas os arquivos staged no git, permitindo que regras pulem arquivos não alterados para feedback mais rápido. :::tip[Execute verificações automaticamente com plugins de editor] -Os plugins de editor para [Claude Code](/pt-br/guides/claude-code-plugin/) e [Cursor](/pt-br/guides/cursor-integration/) executam `archgate check` automaticamente após cada alteração de código. O agente lê os ADRs aplicáveis, escreve código em conformidade e valida -- sem necessidade de executar comandos de verificação manualmente. [Inscreva-se para acesso beta](https://plugins.archgate.dev). +Os plugins de editor para [Claude Code](/guides/claude-code-plugin/) e [Cursor](/guides/cursor-integration/) executam `archgate check` automaticamente após cada alteração de código. O agente lê os ADRs aplicáveis, escreve código em conformidade e valida -- sem necessidade de executar comandos de verificação manualmente. [Inscreva-se para acesso beta](https://plugins.archgate.dev). ::: diff --git a/docs/src/content/docs/pt-br/examples/common-rule-patterns.mdx b/docs/src/content/docs/pt-br/examples/common-rule-patterns.mdx index e592ffdd..25eaa2d7 100644 --- a/docs/src/content/docs/pt-br/examples/common-rule-patterns.mdx +++ b/docs/src/content/docs/pt-br/examples/common-rule-patterns.mdx @@ -269,5 +269,5 @@ const testPath = rel.replace(/\.ts$/, ".test.ts"); ``` :::tip[Deixe agentes de IA escreverem regras para você] -Os plugins de editor para [Claude Code](/pt-br/guides/claude-code-plugin/) e [Cursor](/pt-br/guides/cursor-integration/) incluem uma skill de Quality Manager que identifica padrões recorrentes na sua base de código e propõe novas regras para aplicá-los. [Cadastre-se para acesso beta](https://plugins.archgate.dev). +Os plugins de editor para [Claude Code](/guides/claude-code-plugin/) e [Cursor](/guides/cursor-integration/) incluem uma skill de Quality Manager que identifica padrões recorrentes na sua base de código e propõe novas regras para aplicá-los. [Cadastre-se para acesso beta](https://plugins.archgate.dev). ::: diff --git a/docs/src/content/docs/pt-br/getting-started/installation.mdx b/docs/src/content/docs/pt-br/getting-started/installation.mdx index b20fff7e..9e74c9f0 100644 --- a/docs/src/content/docs/pt-br/getting-started/installation.mdx +++ b/docs/src/content/docs/pt-br/getting-started/installation.mdx @@ -130,8 +130,8 @@ Reinicie seu shell e execute `npm install -g archgate` novamente. O comando `arc ## Próximos passos -Após a instalação, execute `archgate init` no seu projeto para configurar a governança. Veja o guia de [Início Rápido](/pt-br/getting-started/quick-start/) para um passo a passo. +Após a instalação, execute `archgate init` no seu projeto para configurar a governança. Veja o guia de [Início Rápido](/getting-started/quick-start/) para um passo a passo. :::tip[Plugins para editores (beta)] -Quer que seu agente de IA leia ADRs antes de programar e valide depois? Os plugins para editores do [Claude Code](/pt-br/guides/claude-code-plugin/) e [Cursor](/pt-br/guides/cursor-integration/) adicionam um fluxo completo de governança sobre a CLI. [Cadastre-se para acesso beta](https://plugins.archgate.dev). +Quer que seu agente de IA leia ADRs antes de programar e valide depois? Os plugins para editores do [Claude Code](/guides/claude-code-plugin/) e [Cursor](/guides/cursor-integration/) adicionam um fluxo completo de governança sobre a CLI. [Cadastre-se para acesso beta](https://plugins.archgate.dev). ::: diff --git a/docs/src/content/docs/pt-br/getting-started/quick-start.mdx b/docs/src/content/docs/pt-br/getting-started/quick-start.mdx index b495fa72..98329351 100644 --- a/docs/src/content/docs/pt-br/getting-started/quick-start.mdx +++ b/docs/src/content/docs/pt-br/getting-started/quick-start.mdx @@ -15,7 +15,7 @@ curl -fsSL https://raw.githubusercontent.com/archgate/cli/main/install.sh | sh npm install -g archgate ``` -Veja a página de [Instalação](/pt-br/getting-started/installation/) para todas as opções, incluindo Windows e diretórios de instalação personalizados. +Veja a página de [Instalação](/getting-started/installation/) para todas as opções, incluindo Windows e diretórios de instalação personalizados. ## 2. Inicializar seu projeto @@ -122,22 +122,22 @@ Agora que você tem uma configuração funcionando, aprofunde-se: **Entenda os conceitos:** -- [ADRs](/pt-br/concepts/adrs/) — O que são Architecture Decision Records e como o Archgate os utiliza. -- [Regras](/pt-br/concepts/rules/) — Como arquivos complementares `.rules.ts` transformam decisões em verificações automatizadas. -- [Domínios](/pt-br/concepts/domains/) — Como domínios agrupam ADRs relacionados e definem o escopo de correspondência de arquivos. +- [ADRs](/concepts/adrs/) — O que são Architecture Decision Records e como o Archgate os utiliza. +- [Regras](/concepts/rules/) — Como arquivos complementares `.rules.ts` transformam decisões em verificações automatizadas. +- [Domínios](/concepts/domains/) — Como domínios agrupam ADRs relacionados e definem o escopo de correspondência de arquivos. **Escreva os seus:** -- [Escrevendo ADRs](/pt-br/guides/writing-adrs/) — Aprenda o formato completo de ADR e as melhores práticas para escrever decisões eficazes. -- [Escrevendo Regras](/pt-br/guides/writing-rules/) — Explore a API de regras, padrões avançados e como testar suas regras. -- [Padrões Comuns de Regras](/pt-br/examples/common-rule-patterns/) — Padrões prontos para copiar e colar para verificações de dependências, convenções de nomenclatura e mais. +- [Escrevendo ADRs](/guides/writing-adrs/) — Aprenda o formato completo de ADR e as melhores práticas para escrever decisões eficazes. +- [Escrevendo Regras](/guides/writing-rules/) — Explore a API de regras, padrões avançados e como testar suas regras. +- [Padrões Comuns de Regras](/examples/common-rule-patterns/) — Padrões prontos para copiar e colar para verificações de dependências, convenções de nomenclatura e mais. **Integre ao seu fluxo de trabalho:** -- [Integração com CI](/pt-br/guides/ci-integration/) — Conecte `archgate check` ao GitHub Actions, GitLab CI ou qualquer pipeline. -- [Pre-commit Hooks](/pt-br/guides/pre-commit-hooks/) — Execute verificações localmente antes de cada commit. -- [Plugin Claude Code](/pt-br/guides/claude-code-plugin/) — Dê aos agentes de IA um fluxo completo de governança com habilidades baseadas em papéis. -- [Integração com Cursor](/pt-br/guides/cursor-integration/) — Use o Archgate com o Cursor IDE para desenvolvimento assistido por IA. +- [Integração com CI](/guides/ci-integration/) — Conecte `archgate check` ao GitHub Actions, GitLab CI ou qualquer pipeline. +- [Pre-commit Hooks](/guides/pre-commit-hooks/) — Execute verificações localmente antes de cada commit. +- [Plugin Claude Code](/guides/claude-code-plugin/) — Dê aos agentes de IA um fluxo completo de governança com habilidades baseadas em papéis. +- [Integração com Cursor](/guides/cursor-integration/) — Use o Archgate com o Cursor IDE para desenvolvimento assistido por IA. :::tip[Plugins para editores (beta)] Quer agentes de IA que leiam automaticamente seus ADRs antes de programar? Execute `archgate login` para se cadastrar e autenticar, depois execute `archgate init --install-plugin` para configurar o plugin. diff --git a/docs/src/content/docs/pt-br/guides/ci-integration.mdx b/docs/src/content/docs/pt-br/guides/ci-integration.mdx index 54aaadf4..61e7a072 100644 --- a/docs/src/content/docs/pt-br/guides/ci-integration.mdx +++ b/docs/src/content/docs/pt-br/guides/ci-integration.mdx @@ -198,7 +198,7 @@ jobs: O Archgate funciona com qualquer sistema de CI que consiga executar comandos shell. O padrão é sempre o mesmo: -1. Instalar: `npm install -g archgate`, `bun install -g archgate`, ou use o [instalador standalone](/pt-br/getting-started/installation/#instalação-standalone-recomendado) +1. Instalar: `npm install -g archgate`, `bun install -g archgate`, ou use o [instalador standalone](/getting-started/installation/#instalação-standalone-recomendado) 2. Executar: `archgate check` 3. Verificar o código de saída (0 = aprovado, 1 = violações, 2 = erro) @@ -224,5 +224,5 @@ Use `--verbose` para ver as regras aprovadas e informações de tempo junto com ``` :::tip[Adicione governança de IA ao seu fluxo de trabalho] -O CI detecta violações no momento do merge. Plugins de editor detectam no momento da codificação. Com o plugin [Claude Code](/pt-br/guides/claude-code-plugin/) ou [Cursor](/pt-br/guides/cursor-integration/), seu agente de IA lê os ADRs antes de escrever código e valida a conformidade antes mesmo de você commitar. [Inscreva-se para acesso beta](https://plugins.archgate.dev). +O CI detecta violações no momento do merge. Plugins de editor detectam no momento da codificação. Com o plugin [Claude Code](/guides/claude-code-plugin/) ou [Cursor](/guides/cursor-integration/), seu agente de IA lê os ADRs antes de escrever código e valida a conformidade antes mesmo de você commitar. [Inscreva-se para acesso beta](https://plugins.archgate.dev). ::: diff --git a/docs/src/content/docs/pt-br/guides/copilot-cli-plugin.mdx b/docs/src/content/docs/pt-br/guides/copilot-cli-plugin.mdx index a2048da9..beb7013b 100644 --- a/docs/src/content/docs/pt-br/guides/copilot-cli-plugin.mdx +++ b/docs/src/content/docs/pt-br/guides/copilot-cli-plugin.mdx @@ -3,7 +3,7 @@ title: Plugin para Copilot CLI description: Configure o plugin Archgate para GitHub Copilot CLI. Adicione governança arquitetural ao Copilot com conformidade ADR automatizada e aplicação de regras. --- -O plugin Archgate para Copilot CLI oferece aos agentes de IA que trabalham no [GitHub Copilot CLI](https://github.com/features/copilot) um fluxo de governança estruturado. Os agentes leem seus ADRs antes de escrever código, validam depois e capturam novos padrões para a equipe -- o mesmo fluxo disponível no [plugin para Claude Code](/pt-br/guides/claude-code-plugin/). +O plugin Archgate para Copilot CLI oferece aos agentes de IA que trabalham no [GitHub Copilot CLI](https://github.com/features/copilot) um fluxo de governança estruturado. Os agentes leem seus ADRs antes de escrever código, validam depois e capturam novos padrões para a equipe -- o mesmo fluxo disponível no [plugin para Claude Code](/guides/claude-code-plugin/). ## Como funciona diff --git a/docs/src/content/docs/pt-br/guides/cursor-integration.mdx b/docs/src/content/docs/pt-br/guides/cursor-integration.mdx index 0c0bd8d8..b549d495 100644 --- a/docs/src/content/docs/pt-br/guides/cursor-integration.mdx +++ b/docs/src/content/docs/pt-br/guides/cursor-integration.mdx @@ -3,7 +3,7 @@ title: Integração com Cursor description: Integre o Archgate com o Cursor IDE para desenvolvimento assistido por IA com governança arquitetural. Configure regras e skills de agente para conformidade ADR. --- -O Archgate se integra com o [Cursor](https://cursor.com) para oferecer aos agentes de IA um fluxo de governança estruturado. O agente lê seus ADRs antes de escrever código, valida depois e captura novos padrões para a equipe -- o mesmo fluxo disponível no [plugin para Claude Code](/pt-br/guides/claude-code-plugin/). +O Archgate se integra com o [Cursor](https://cursor.com) para oferecer aos agentes de IA um fluxo de governança estruturado. O agente lê seus ADRs antes de escrever código, valida depois e captura novos padrões para a equipe -- o mesmo fluxo disponível no [plugin para Claude Code](/guides/claude-code-plugin/). ## Configuração diff --git a/docs/src/content/docs/pt-br/guides/vscode-plugin.mdx b/docs/src/content/docs/pt-br/guides/vscode-plugin.mdx index 02303720..3fffa101 100644 --- a/docs/src/content/docs/pt-br/guides/vscode-plugin.mdx +++ b/docs/src/content/docs/pt-br/guides/vscode-plugin.mdx @@ -3,7 +3,7 @@ title: Plugin para VS Code description: Instale a extensão Archgate para VS Code com desenvolvimento assistido por IA e governança arquitetural. Verificação de conformidade ADR em tempo real. --- -O plugin Archgate para VS Code oferece aos agentes de IA que trabalham no [VS Code](https://code.visualstudio.com/) um fluxo de governança estruturado. Os agentes leem seus ADRs antes de escrever código, validam depois e capturam novos padrões para a equipe -- o mesmo fluxo disponível no [plugin para Claude Code](/pt-br/guides/claude-code-plugin/). +O plugin Archgate para VS Code oferece aos agentes de IA que trabalham no [VS Code](https://code.visualstudio.com/) um fluxo de governança estruturado. Os agentes leem seus ADRs antes de escrever código, validam depois e capturam novos padrões para a equipe -- o mesmo fluxo disponível no [plugin para Claude Code](/guides/claude-code-plugin/). ## Como funciona diff --git a/docs/src/content/docs/pt-br/guides/writing-adrs.mdx b/docs/src/content/docs/pt-br/guides/writing-adrs.mdx index 0f119484..04edc259 100644 --- a/docs/src/content/docs/pt-br/guides/writing-adrs.mdx +++ b/docs/src/content/docs/pt-br/guides/writing-adrs.mdx @@ -301,10 +301,10 @@ As flags `--id` e `--body` são obrigatórias. Todos os outros campos do frontma 5. **Declare os trade-offs honestamente.** Toda decisão tem consequências negativas. Documentá-las gera confiança e ajuda a equipe a entender o que foi sacrificado. -6. **Escreva regras para decisões que podem ser verificadas automaticamente.** Se uma regra consegue detectar uma violação antes da revisão de código, defina `rules: true` e escreva um arquivo `.rules.ts` complementar. Veja o guia [Escrevendo Regras](/pt-br/guides/writing-rules/). +6. **Escreva regras para decisões que podem ser verificadas automaticamente.** Se uma regra consegue detectar uma violação antes da revisão de código, defina `rules: true` e escreva um arquivo `.rules.ts` complementar. Veja o guia [Escrevendo Regras](/guides/writing-rules/). 7. **Use prefixos de domínio para organizar.** O campo domain (`backend`, `frontend`, `data`, `architecture`, `general`) determina o prefixo do ID e ajuda a filtrar ADRs por área. :::tip[Deixe agentes de IA escreverem ADRs para você] -Os plugins de editor para [Claude Code](/pt-br/guides/claude-code-plugin/) e [Cursor](/pt-br/guides/cursor-integration/) incluem uma skill de ADR Author que cria e atualiza ADRs seguindo as convenções do seu projeto. A skill Quality Manager também propõe novos ADRs quando detecta padrões recorrentes. [Inscreva-se para acesso beta](https://plugins.archgate.dev). +Os plugins de editor para [Claude Code](/guides/claude-code-plugin/) e [Cursor](/guides/cursor-integration/) incluem uma skill de ADR Author que cria e atualiza ADRs seguindo as convenções do seu projeto. A skill Quality Manager também propõe novos ADRs quando detecta padrões recorrentes. [Inscreva-se para acesso beta](https://plugins.archgate.dev). ::: diff --git a/docs/src/content/docs/pt-br/guides/writing-rules.mdx b/docs/src/content/docs/pt-br/guides/writing-rules.mdx index 729af3a2..152522aa 100644 --- a/docs/src/content/docs/pt-br/guides/writing-rules.mdx +++ b/docs/src/content/docs/pt-br/guides/writing-rules.mdx @@ -448,6 +448,6 @@ ARCH-006/no-unapproved-deps ## Próximos passos -- [Padrões Comuns de Regras](/pt-br/examples/common-rule-patterns/) — Padrões prontos para copiar e colar para verificações de dependências, convenções de nomenclatura, restrições de import e mais. -- [Referência da API de Regras](/pt-br/reference/rule-api/) — Referência completa de todos os tipos e funções da API de regras. -- [Integração com CI](/pt-br/guides/ci-integration/) — Integre `archgate check` ao seu pipeline para aplicar regras em cada PR. +- [Padrões Comuns de Regras](/examples/common-rule-patterns/) — Padrões prontos para copiar e colar para verificações de dependências, convenções de nomenclatura, restrições de import e mais. +- [Referência da API de Regras](/reference/rule-api/) — Referência completa de todos os tipos e funções da API de regras. +- [Integração com CI](/guides/ci-integration/) — Integre `archgate check` ao seu pipeline para aplicar regras em cada PR. diff --git a/docs/src/content/docs/pt-br/index.mdx b/docs/src/content/docs/pt-br/index.mdx index 391475f1..77cab2d5 100644 --- a/docs/src/content/docs/pt-br/index.mdx +++ b/docs/src/content/docs/pt-br/index.mdx @@ -82,22 +82,22 @@ Os plugins para editores estão atualmente em beta. Execute `archgate login` par diff --git a/docs/src/content/docs/pt-br/reference/adr-schema.mdx b/docs/src/content/docs/pt-br/reference/adr-schema.mdx index 4918f66d..2d6981e5 100644 --- a/docs/src/content/docs/pt-br/reference/adr-schema.mdx +++ b/docs/src/content/docs/pt-br/reference/adr-schema.mdx @@ -243,7 +243,7 @@ export default defineRules({ }); ``` -Consulte a [API de Regras](/pt-br/reference/rule-api/) para a referência completa da API TypeScript. +Consulte a [API de Regras](/reference/rule-api/) para a referência completa da API TypeScript. --- From 25b9cd3a3496e4cc6c3235f0c27448c9634c7c4b Mon Sep 17 00:00:00 2001 From: Rhuan Barreto Date: Wed, 18 Mar 2026 17:00:37 +0100 Subject: [PATCH 17/20] docs: regenerate llms-full.txt --- docs/public/llms-full.txt | 110 ++++++++++++++++++++++++++++++-------- 1 file changed, 89 insertions(+), 21 deletions(-) diff --git a/docs/public/llms-full.txt b/docs/public/llms-full.txt index f5dad02c..3464f72b 100644 --- a/docs/public/llms-full.txt +++ b/docs/public/llms-full.txt @@ -105,7 +105,7 @@ The Archgate CLI works standalone, but **editor plugins** unlock a full AI gover Cursor's AI agent the same governance workflow as Claude Code. -Editor plugins are currently in beta. Sign up at [plugins.archgate.dev](https://plugins.archgate.dev) to get access, then install with `archgate login` and `archgate init --install-plugin`. +Editor plugins are currently in beta. Run `archgate login` to sign up and authenticate, then `archgate init --install-plugin` to set up the plugin. ## Learn more @@ -130,10 +130,13 @@ irm https://raw.githubusercontent.com/archgate/cli/main/install.ps1 | iex This downloads a pre-built binary for your platform and installs it to `~/.archgate/bin/`. The installer detects your shell profiles and offers to add the directory to your PATH. You can customize the install with environment variables: -- `ARCHGATE_VERSION` — Install a specific version (e.g. `v0.11.2`). Default: latest release. -- `ARCHGATE_INSTALL_DIR` — Custom install directory. Default: `~/.archgate/bin`. -You can also download binaries directly from GitHub Releases: https://github.com/archgate/cli/releases +| Variable | Description | Default | +| ---------------------- | ------------------------------------------- | ----------------- | +| `ARCHGATE_VERSION` | Install a specific version (e.g. `v0.11.2`) | Latest release | +| `ARCHGATE_INSTALL_DIR` | Custom install directory | `~/.archgate/bin` | + +You can also download binaries directly from [GitHub Releases](https://github.com/archgate/cli/releases). ## Install via npm @@ -186,11 +189,7 @@ bun run archgate check Or add a script to your `package.json`: ```json -{ - "scripts": { - "check:adrs": "archgate check" - } -} +{ "scripts": { "check:adrs": "archgate check" } } ``` ```bash @@ -245,7 +244,7 @@ Restart your shell, then run `npm install -g archgate` again. The `archgate` com Once installed, run `archgate init` in your project to set up governance. See the [Quick Start](/getting-started/quick-start/) guide for a walkthrough. -Want your AI agent to read ADRs before coding and validate after? The editor plugins for [Claude Code](/guides/claude-code-plugin/) and [Cursor](/guides/cursor-integration/) add a full governance workflow on top of the CLI. [Sign up for beta access](https://plugins.archgate.dev). +Want your AI agent to read ADRs before coding and validate after? The editor plugins for [Claude Code](/guides/claude-code-plugin/) and [Cursor](/guides/cursor-integration/) add a full governance workflow on top of the CLI. Run `archgate login` to sign up and get started. --- @@ -388,7 +387,7 @@ Now that you have a working setup, dive deeper: - [Claude Code Plugin](/guides/claude-code-plugin/) — Give AI agents a full governance workflow with role-based skills. - [Cursor Integration](/guides/cursor-integration/) — Use Archgate with Cursor IDE for AI-assisted development. -Want AI agents that automatically read your ADRs before coding? Sign up for the editor plugin beta at [plugins.archgate.dev](https://plugins.archgate.dev), then run `archgate login` and `archgate init --install-plugin`. +Want AI agents that automatically read your ADRs before coding? Run `archgate login` to sign up and authenticate, then run `archgate init --install-plugin` to set up the plugin. --- @@ -994,6 +993,26 @@ adr-compliance: - archgate check ``` +## Standalone installer (no Node.js) + +If your CI environment does not have Node.js, use the standalone installer to download a pre-built binary directly from GitHub Releases: + +```yaml +jobs: + check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - run: curl -fsSL https://raw.githubusercontent.com/archgate/cli/main/install.sh | sh + - run: ~/.archgate/bin/archgate check --ci +``` + +This works in any environment with `curl` and `tar` — no runtime dependencies needed. You can pin a version with the `ARCHGATE_VERSION` environment variable: + +```yaml +- run: curl -fsSL https://raw.githubusercontent.com/archgate/cli/main/install.sh | ARCHGATE_VERSION=v0.11.2 sh +``` + ## Bun-based CI If your CI already uses Bun, install Archgate with `bun` instead of `npm`: @@ -1013,7 +1032,7 @@ jobs: Archgate works with any CI system that can run shell commands. The pattern is always the same: -1. Install: `npm install -g archgate` (or `bun install -g archgate`) +1. Install: `npm install -g archgate`, `bun install -g archgate`, or use the [standalone installer](/getting-started/installation/#install-standalone-recommended) 2. Run: `archgate check` 3. Check the exit code (0 = pass, 1 = violations, 2 = error) @@ -1071,7 +1090,7 @@ The `archgate:developer` agent is set as the default agent via `.claude/settings ## Installation -The Claude Code plugin is currently in beta. [Sign up at plugins.archgate.dev](https://plugins.archgate.dev) to get access before following the steps below. +The Claude Code plugin is currently in beta. Run `archgate login` to sign up and authenticate. ### 1. Log in with GitHub @@ -1207,7 +1226,7 @@ Copilot CLI supports plugin installation from git repositories using `copilot pl ## Installation -The Copilot CLI plugin is currently in beta. [Sign up at plugins.archgate.dev](https://plugins.archgate.dev) to get access before following the steps below. +The Copilot CLI plugin is currently in beta. Run `archgate login` to sign up and authenticate before following the steps below. ### 1. Log in with GitHub @@ -1340,7 +1359,7 @@ archgate init --editor cursor ### With plugin (beta) -The Cursor plugin is currently in beta. [Sign up at plugins.archgate.dev](https://plugins.archgate.dev) to get access. +The Cursor plugin is currently in beta. Run `archgate login` to sign up and authenticate. If you have logged in via `archgate login`, the init command also downloads and installs the Archgate plugin for Cursor. The plugin provides pre-built agent rules and skills that give Cursor's AI agent a full governance workflow. @@ -1589,7 +1608,7 @@ The plugin is served in VS Code Copilot's native `.github/plugin/` manifest form Agent plugins require **VS Code 1.110 (February 2026 release) or later**. Earlier versions do not support git-based agent plugin marketplaces. Check your version with `code --version`. -The VS Code plugin is currently in beta. [Sign up at plugins.archgate.dev](https://plugins.archgate.dev) to get access before following the steps below. +The VS Code plugin is currently in beta. Run `archgate login` to sign up and authenticate before following the steps below. ### 1. Log in with GitHub @@ -2781,7 +2800,7 @@ archgate check --help ## archgate login -Authenticate with GitHub to access Archgate editor plugins. +Authenticate with GitHub to access Archgate editor plugins. If you are not registered yet, the CLI handles signup automatically -- it prompts for your email, editor preference (Claude Code, VS Code, Copilot CLI, or Cursor), and use case, then registers you before completing the login. ```bash archgate login @@ -2789,9 +2808,11 @@ archgate login Starts a GitHub Device Flow (OAuth). The CLI displays a one-time code and a URL. Open the URL in your browser, enter the code, and authorize the Archgate GitHub OAuth App. Once authorized, the CLI exchanges your GitHub identity for an Archgate plugin token and stores both in `~/.archgate/credentials`. +If your GitHub account is not yet registered, the CLI prompts for your email, preferred editor, and use case, then signs you up automatically. + Credentials are required to install editor plugins via `archgate init --install-plugin`. The CLI itself (check, init, etc.) works without login. -Editor plugins are currently in beta. Sign up at [plugins.archgate.dev](https://plugins.archgate.dev) to get access. +Editor plugins are currently in beta. Run `archgate login` to sign up and authenticate. ### Subcommands @@ -2818,12 +2839,61 @@ and enter the code: ABCD-1234 Waiting for authorization... GitHub user: yourname + +No account found. Let's get you signed up. +Email: you@example.com +Editor: Claude Code +Use case: Enforcing ADRs in our monorepo + +Registering... Claiming archgate plugin token... Authenticated as yourname. Plugin access is now available. Run `archgate init` to set up a project with the archgate plugin. ``` +If the project already has `.archgate/adrs/`, the final line reads: + +``` +Run `archgate check` to validate your project against its ADRs. +``` + +### Troubleshooting + +#### TLS/corporate proxy errors + +If `archgate login` fails with a TLS certificate error (common behind corporate proxies), point your runtime at your organization's CA bundle using the `NODE_EXTRA_CA_CERTS` environment variable. + +On macOS/Linux: + +```bash +export NODE_EXTRA_CA_CERTS=/path/to/your-corporate-ca.pem +archgate login +``` + +On Windows (PowerShell): + +```powershell +$env:NODE_EXTRA_CA_CERTS = "C:\path\to\your-corporate-ca.pem" +archgate login +``` + +On Windows (cmd): + +```cmd +set NODE_EXTRA_CA_CERTS=C:\path\to\your-corporate-ca.pem +archgate login +``` + +On Windows (Git Bash): + +```bash +export NODE_EXTRA_CA_CERTS=/c/path/to/your-corporate-ca.pem +archgate login +``` + +Ask your IT team for the correct certificate path if you are unsure. + Check login status: ```bash @@ -3561,9 +3631,7 @@ interface ViolationDetail { The return type of `defineRules`. You do not construct this directly. ```typescript -type RuleSet = { - rules: Record; -}; +type RuleSet = { rules: Record }; ``` --- From b75f99a52bb74eec52fb9a13e93b65cb96db508f Mon Sep 17 00:00:00 2001 From: Rhuan Barreto Date: Wed, 18 Mar 2026 17:01:21 +0100 Subject: [PATCH 18/20] ci: check llms-full.txt is up to date with docs Regenerates llms-full.txt during CI and fails if there's a diff, ensuring it stays in sync with documentation changes. --- .github/workflows/code-pull-request.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/code-pull-request.yml b/.github/workflows/code-pull-request.yml index 592b24be..ce7dcc72 100644 --- a/.github/workflows/code-pull-request.yml +++ b/.github/workflows/code-pull-request.yml @@ -44,6 +44,13 @@ jobs: env: PR_TITLE: ${{ github.event.pull_request.title }} run: echo "$PR_TITLE" | bun run commitlint + - name: Check llms-full.txt is up to date + run: | + bun run docs/scripts/generate-llms-full.ts + git diff --exit-code docs/public/llms-full.txt || { + echo "::error::docs/public/llms-full.txt is out of date. Run 'bun run docs/scripts/generate-llms-full.ts' and commit the result." + exit 1 + } - name: Validate id: validate run: bun run validate From c95c53533d6354e29c9f497569158d902e709aec Mon Sep 17 00:00:00 2001 From: Rhuan Barreto Date: Wed, 18 Mar 2026 17:02:28 +0100 Subject: [PATCH 19/20] ci: auto-regenerate llms-full.txt when docs change Adds a workflow that regenerates and auto-commits llms-full.txt when docs content changes in a PR, matching the update-lock pattern. Removes the manual check from the validation workflow. --- .github/workflows/code-pull-request.yml | 7 ---- .github/workflows/update-llms.yaml | 47 +++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 7 deletions(-) create mode 100644 .github/workflows/update-llms.yaml diff --git a/.github/workflows/code-pull-request.yml b/.github/workflows/code-pull-request.yml index ce7dcc72..592b24be 100644 --- a/.github/workflows/code-pull-request.yml +++ b/.github/workflows/code-pull-request.yml @@ -44,13 +44,6 @@ jobs: env: PR_TITLE: ${{ github.event.pull_request.title }} run: echo "$PR_TITLE" | bun run commitlint - - name: Check llms-full.txt is up to date - run: | - bun run docs/scripts/generate-llms-full.ts - git diff --exit-code docs/public/llms-full.txt || { - echo "::error::docs/public/llms-full.txt is out of date. Run 'bun run docs/scripts/generate-llms-full.ts' and commit the result." - exit 1 - } - name: Validate id: validate run: bun run validate diff --git a/.github/workflows/update-llms.yaml b/.github/workflows/update-llms.yaml new file mode 100644 index 00000000..da2373e5 --- /dev/null +++ b/.github/workflows/update-llms.yaml @@ -0,0 +1,47 @@ +name: Update llms-full.txt +on: + pull_request: + branches: + - main + paths: + - "docs/src/content/docs/**" + - "docs/scripts/generate-llms-full.ts" + - ".github/workflows/update-llms.yaml" +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + update-llms: + name: Update llms-full.txt + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: Generate token + id: generate_token + uses: actions/create-github-app-token@v2 + with: + app-id: ${{ secrets.GH_APP_APP_ID }} + private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} + - uses: actions/checkout@v4 + with: + token: ${{ steps.generate_token.outputs.token }} + fetch-depth: 1 + ref: ${{ github.head_ref }} + - uses: moonrepo/setup-toolchain@v0 + with: + auto-install: true + cache: true + cache-base: main + - name: Install dependencies + run: bun install --frozen-lockfile + - name: Regenerate llms-full.txt + run: bun run docs/scripts/generate-llms-full.ts + - name: Commit changes + uses: stefanzweifel/git-auto-commit-action@v6 + with: + commit_message: "docs: regenerate llms-full.txt" + file_pattern: docs/public/llms-full.txt From 3e1f2987bc301fc0984238f72819661bfad5877e Mon Sep 17 00:00:00 2001 From: Rhuan Barreto Date: Wed, 18 Mar 2026 17:03:49 +0100 Subject: [PATCH 20/20] ci: restore llms-full.txt freshness check in validation The auto-commit workflow handles PRs, but the check catches direct pushes or workflow failures. --- .github/workflows/code-pull-request.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/code-pull-request.yml b/.github/workflows/code-pull-request.yml index 592b24be..ce7dcc72 100644 --- a/.github/workflows/code-pull-request.yml +++ b/.github/workflows/code-pull-request.yml @@ -44,6 +44,13 @@ jobs: env: PR_TITLE: ${{ github.event.pull_request.title }} run: echo "$PR_TITLE" | bun run commitlint + - name: Check llms-full.txt is up to date + run: | + bun run docs/scripts/generate-llms-full.ts + git diff --exit-code docs/public/llms-full.txt || { + echo "::error::docs/public/llms-full.txt is out of date. Run 'bun run docs/scripts/generate-llms-full.ts' and commit the result." + exit 1 + } - name: Validate id: validate run: bun run validate