From 741e2262740227938fd4052bdaa7e5f0f19e8ffc Mon Sep 17 00:00:00 2001 From: Rhuan Barreto Date: Sun, 10 May 2026 22:25:47 +0200 Subject: [PATCH 01/16] =?UTF-8?q?feat:=20legal=20compliance=20remediation?= =?UTF-8?q?=20=E2=80=94=20SPDX=20headers,=20first-run=20notice,=20license?= =?UTF-8?q?=20scanning?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 1: Critical legal compliance - Add SPDX-License-Identifier + copyright header to all 156 source files - Implement first-run privacy disclosure (one-time notice on first TTY session) - Add ToS notice to login flow (shown before GitHub OAuth) - Add LIA URL to `archgate telemetry status` output Phase 2: Documentation - Update telemetry.mdx with legal basis, retention table, data subject rights - Update privacy-policy.mdx with controller info, retention, ToS link, CCPA - Switch docs site PostHog to cookieless tracking (persistence: "memory") Phase 3: Engineering - Add scripts/check-licenses.ts — automated dependency license audit - Add scripts/add-spdx-headers.ts — one-time SPDX header insertion tool - Add "license:check" script to package.json All 738 tests pass, all 24 ADR rules pass, build compiles. Signed-off-by: Rhuan Barreto --- docs/src/components/Analytics.astro | 1 + .../content/docs/reference/privacy-policy.mdx | 28 +++- docs/src/content/docs/reference/telemetry.mdx | 25 +++- package.json | 1 + scripts/add-spdx-headers.ts | 52 ++++++++ scripts/check-licenses.ts | 126 ++++++++++++++++++ src/cli.ts | 6 + src/commands/adr/create.ts | 2 + src/commands/adr/domain/add.ts | 2 + src/commands/adr/domain/index.ts | 2 + src/commands/adr/domain/list.ts | 2 + src/commands/adr/domain/remove.ts | 2 + src/commands/adr/index.ts | 2 + src/commands/adr/list.ts | 2 + src/commands/adr/show.ts | 2 + src/commands/adr/update.ts | 2 + src/commands/check.ts | 2 + src/commands/clean.ts | 2 + src/commands/doctor.ts | 2 + src/commands/init.ts | 2 + src/commands/login.ts | 2 + src/commands/plugin/index.ts | 2 + src/commands/plugin/install.ts | 2 + src/commands/plugin/url.ts | 2 + src/commands/review-context.ts | 2 + src/commands/session-context/claude-code.ts | 2 + src/commands/session-context/copilot.ts | 2 + src/commands/session-context/cursor.ts | 2 + src/commands/session-context/index.ts | 2 + src/commands/session-context/opencode.ts | 2 + src/commands/telemetry.ts | 5 + src/commands/upgrade.ts | 2 + src/engine/context.ts | 2 + src/engine/git-files.ts | 2 + src/engine/loader.ts | 2 + src/engine/reporter.ts | 2 + src/engine/rule-scanner.ts | 2 + src/engine/runner.ts | 2 + src/engine/source-positions.ts | 2 + src/formats/adr.ts | 2 + src/formats/project-config.ts | 2 + src/formats/rules.ts | 2 + src/helpers/adr-templates.ts | 2 + src/helpers/adr-writer.ts | 2 + src/helpers/auth.ts | 2 + src/helpers/binary-upgrade.ts | 2 + src/helpers/claude-settings.ts | 2 + src/helpers/copilot-settings.ts | 2 + src/helpers/credential-store.ts | 2 + src/helpers/cursor-settings.ts | 2 + src/helpers/doctor.ts | 2 + src/helpers/editor-detect.ts | 2 + src/helpers/exit.ts | 2 + src/helpers/git.ts | 2 + src/helpers/init-project.ts | 2 + src/helpers/install-info.ts | 2 + src/helpers/log.ts | 2 + src/helpers/login-flow.ts | 5 + src/helpers/output.ts | 2 + src/helpers/paths.ts | 2 + src/helpers/platform.ts | 2 + src/helpers/plugin-install.ts | 2 + src/helpers/project-config.ts | 2 + src/helpers/repo-probe.ts | 2 + src/helpers/repo.ts | 2 + src/helpers/rules-shim.ts | 2 + src/helpers/sentry.ts | 2 + src/helpers/session-context-copilot.ts | 2 + src/helpers/session-context-opencode.ts | 2 + src/helpers/session-context.ts | 2 + src/helpers/signup.ts | 2 + src/helpers/telemetry-config.ts | 35 +++++ src/helpers/telemetry.ts | 2 + src/helpers/tls.ts | 2 + src/helpers/update-check.ts | 2 + src/helpers/vscode-settings.ts | 2 + tests/commands/adr.test.ts | 2 + tests/commands/adr/create.test.ts | 2 + tests/commands/adr/domain/add.test.ts | 2 + tests/commands/adr/domain/list.test.ts | 2 + tests/commands/adr/domain/remove.test.ts | 2 + tests/commands/adr/index.test.ts | 2 + tests/commands/adr/list.test.ts | 2 + tests/commands/adr/show.test.ts | 2 + tests/commands/adr/update.test.ts | 2 + tests/commands/check-security.test.ts | 2 + tests/commands/check.test.ts | 2 + tests/commands/clean.test.ts | 2 + tests/commands/init.test.ts | 2 + tests/commands/login.test.ts | 2 + tests/commands/plugin/index.test.ts | 2 + tests/commands/plugin/install.test.ts | 2 + tests/commands/plugin/url.test.ts | 2 + tests/commands/review-context.test.ts | 2 + tests/commands/session-context.test.ts | 2 + .../session-context/claude-code.test.ts | 2 + .../commands/session-context/copilot.test.ts | 2 + tests/commands/session-context/cursor.test.ts | 2 + .../commands/session-context/opencode.test.ts | 2 + tests/commands/upgrade.test.ts | 2 + tests/engine/context.test.ts | 2 + tests/engine/git-files.test.ts | 2 + tests/engine/loader-security.test.ts | 2 + tests/engine/loader.test.ts | 2 + tests/engine/reporter.test.ts | 2 + tests/engine/rule-scanner-adversarial.test.ts | 2 + tests/engine/rule-scanner-positions.test.ts | 2 + tests/engine/rule-scanner.test.ts | 2 + tests/engine/runner-security.test.ts | 2 + tests/engine/runner.test.ts | 2 + tests/engine/source-positions.test.ts | 2 + tests/fixtures/rules/TEST-001-sample.rules.ts | 2 + tests/formats/adr-fuzz.test.ts | 2 + tests/formats/adr.test.ts | 2 + tests/formats/project-config-fuzz.test.ts | 2 + tests/formats/rules.test.ts | 2 + tests/helpers/adr-templates.test.ts | 2 + tests/helpers/adr-writer.test.ts | 2 + tests/helpers/auth-poll.test.ts | 2 + tests/helpers/auth.test.ts | 2 + tests/helpers/binary-upgrade.test.ts | 2 + tests/helpers/claude-settings.test.ts | 2 + tests/helpers/copilot-settings.test.ts | 2 + tests/helpers/credential-store.test.ts | 2 + tests/helpers/cursor-settings.test.ts | 2 + tests/helpers/doctor.test.ts | 2 + tests/helpers/editor-detect.test.ts | 2 + tests/helpers/exit.test.ts | 2 + tests/helpers/git.test.ts | 2 + tests/helpers/init-project.test.ts | 2 + tests/helpers/install-info.test.ts | 2 + tests/helpers/log.test.ts | 2 + tests/helpers/login-flow.test.ts | 2 + tests/helpers/output.test.ts | 2 + tests/helpers/paths.test.ts | 2 + tests/helpers/platform.test.ts | 2 + tests/helpers/plugin-install.test.ts | 2 + tests/helpers/project-config.test.ts | 2 + tests/helpers/repo-probe.test.ts | 2 + tests/helpers/repo.test.ts | 2 + tests/helpers/rules-shim.test.ts | 2 + tests/helpers/sentry.test.ts | 2 + tests/helpers/session-context-copilot.test.ts | 2 + tests/helpers/session-context-cursor.test.ts | 2 + .../helpers/session-context-opencode.test.ts | 2 + tests/helpers/session-context.test.ts | 2 + tests/helpers/signup.test.ts | 2 + tests/helpers/telemetry-config.test.ts | 2 + tests/helpers/telemetry.test.ts | 2 + tests/helpers/tls.test.ts | 2 + tests/helpers/update-check.test.ts | 2 + tests/helpers/vscode-settings.test.ts | 2 + tests/integration/adr-show-update.test.ts | 2 + tests/integration/adr.test.ts | 2 + tests/integration/check.test.ts | 2 + tests/integration/clean.test.ts | 2 + tests/integration/cli-harness.ts | 2 + tests/integration/cli-perf.test.ts | 2 + tests/integration/init.test.ts | 2 + tests/integration/review-context.test.ts | 2 + tests/preload.ts | 2 + tests/test-utils.ts | 2 + 162 files changed, 584 insertions(+), 4 deletions(-) create mode 100644 scripts/add-spdx-headers.ts create mode 100644 scripts/check-licenses.ts diff --git a/docs/src/components/Analytics.astro b/docs/src/components/Analytics.astro index c51eb288..7afaa33c 100644 --- a/docs/src/components/Analytics.astro +++ b/docs/src/components/Analytics.astro @@ -5,5 +5,6 @@ ui_host: "https://eu.posthog.com", defaults: "2026-01-30", person_profiles: "identified_only", + persistence: "memory", }); diff --git a/docs/src/content/docs/reference/privacy-policy.mdx b/docs/src/content/docs/reference/privacy-policy.mdx index dafee47a..fa5c5f7c 100644 --- a/docs/src/content/docs/reference/privacy-policy.mdx +++ b/docs/src/content/docs/reference/privacy-policy.mdx @@ -9,15 +9,31 @@ For the full Archgate Privacy Policy, please visit: The sections below summarize the most relevant points for CLI users. The canonical policy on the website is the legally binding version. +**Data controller:** Dasolve AS (Org.nr 936 035 019), Lillogata 5P, 0484 Oslo, Norway. Contact: [privacy@archgate.dev](mailto:privacy@archgate.dev). + ## Summary for CLI users ### What the CLI collects Archgate collects **anonymous usage analytics** (via PostHog) and **crash reports** (via Sentry) to improve the tool. No personal information, source code, file content, or AI prompts are ever collected by the CLI itself. See the [Telemetry](/reference/telemetry/) page for a detailed breakdown of every data point. +**Legal basis:** Legitimate interest under GDPR Article 6(1)(f). See our [Legitimate Interest Assessment](https://archgate.dev/legitimate-interest-assessment). + +### Data storage and retention + +| Service | Data | Region | Retention | +| ------------- | ---------------------------- | -------------- | ------------------------ | +| PostHog Cloud | Anonymous usage analytics | EU (Frankfurt) | 1 year | +| Sentry Cloud | Crash reports | EU (Frankfurt) | 90 days | +| Turso | Plugins Service account data | EU | Until deletion requested | + +Data is transmitted via reverse proxies at `n.archgate.dev` (analytics) and `s.archgate.dev` (errors) — transparent forwarders operated by Dasolve AS on Cloudflare, with no logging or storage. + ### Archgate Plugins Service -When you sign up via `archgate login`, the **Plugins Service** (`plugins.archgate.dev`) collects personal information: your **email address**, **GitHub username**, **editor choice**, and a **use case description**. This data is used to provision your account and send a welcome email. Authentication tokens are stored as SHA-256 hashes on our servers; on your machine, credentials are kept in your OS credential manager (never as plain-text files). See the [full privacy policy](https://archgate.dev/privacy-policy) for complete details. +When you sign up via `archgate login`, the **Plugins Service** (`plugins.archgate.dev`) collects personal information: your **email address**, **GitHub username**, **editor choice**, and a **use case description**. This data is used to provision your account and send a welcome email. Authentication tokens are stored as SHA-256 hashes on our servers; on your machine, credentials are kept in your OS credential manager (never as plain-text files). + +By creating an account, you agree to the [Terms of Service](https://archgate.dev/terms-of-service). See the [full privacy policy](https://archgate.dev/privacy-policy) for complete details. ### How to opt out @@ -31,6 +47,16 @@ archgate telemetry disable Note: telemetry opt-out disables CLI analytics and crash reporting. It does not affect the Plugins Service account data, which is required for plugin access. To delete your account data, contact [privacy@archgate.dev](mailto:privacy@archgate.dev). +### Your rights + +- **Access:** Request a copy of your data — email [privacy@archgate.dev](mailto:privacy@archgate.dev) with your install ID (from `~/.archgate/config.json` or `archgate telemetry status`). +- **Erasure:** Request deletion of historical data — email with your install ID. Completed within 30 days. +- **Object:** Disable telemetry at any time via `archgate telemetry disable`. +- **Portability:** Request data export in JSON or CSV format. +- **Complaint:** Contact the Norwegian Data Protection Authority ([Datatilsynet](https://www.datatilsynet.no)). + ### What the docs site collects This documentation site (`cli.archgate.dev`) uses [Cloudflare Web Analytics](https://www.cloudflare.com/web-analytics/), a privacy-first analytics service. Cloudflare Web Analytics does not use cookies, does not track individual visitors, and does not collect personal information. It provides aggregate page-view and performance metrics only. + +PostHog is also used on this site with **cookieless, memory-only tracking** (`persistence: "memory"`). No cookies are set, no localStorage is written, and no data persists between page loads. diff --git a/docs/src/content/docs/reference/telemetry.mdx b/docs/src/content/docs/reference/telemetry.mdx index 2396ca8d..776fdcdf 100644 --- a/docs/src/content/docs/reference/telemetry.mdx +++ b/docs/src/content/docs/reference/telemetry.mdx @@ -112,11 +112,30 @@ archgate telemetry status The environment variable takes precedence over the CLI setting. If `ARCHGATE_TELEMETRY=0` is set, telemetry is disabled regardless of the CLI config. +## Legal basis + +Archgate CLI telemetry operates on an **opt-out basis** under GDPR Article 6(1)(f) — legitimate interests of the controller. We have published a formal [Legitimate Interest Assessment](https://archgate.dev/legitimate-interest-assessment) documenting why this is proportionate and lawful. + +In summary: the data is anonymous (random UUID, no PII), the impact on users is minimal, robust safeguards are in place (IP anonymization, EU storage, limited retention, transparency), and users retain full control via an easy, permanent opt-out. + ## Where data is stored -- **Analytics**: PostHog Cloud (EU region). Data retained per PostHog's standard retention policy. -- **Error tracking**: Sentry Cloud (EU region). Error events retained for 90 days. -- **Local config**: `~/.archgate/config.json` stores your telemetry preference and anonymous install ID. +| Service | Data | Region | Retention | +| ------------- | --------------------------------- | -------------- | ------------------- | +| PostHog Cloud | Anonymous usage analytics | EU (Frankfurt) | 1 year | +| Sentry Cloud | Crash reports | EU (Frankfurt) | 90 days | +| Local config | Telemetry preference + install ID | Your machine | Until you delete it | + +Analytics events are routed through `n.archgate.dev` and error reports through `s.archgate.dev`. These are transparent reverse proxies operated by Dasolve AS on Cloudflare infrastructure — they forward requests without logging, storing, or inspecting payloads. + +## Your rights + +- **Right of access**: Request a copy of all data associated with your install ID. Email [privacy@archgate.dev](mailto:privacy@archgate.dev) with your install ID (found via `archgate telemetry status` or in `~/.archgate/config.json`). Response within 30 days. +- **Right to erasure**: Request deletion of historical analytics and crash data. Disabling telemetry stops future collection but does not delete past events. Email [privacy@archgate.dev](mailto:privacy@archgate.dev) with your install ID for deletion. +- **Right to object**: Disable telemetry at any time via `archgate telemetry disable` or `ARCHGATE_TELEMETRY=0`. +- **Right to lodge a complaint**: Contact the Norwegian Data Protection Authority ([Datatilsynet](https://www.datatilsynet.no)). + +**Data controller:** Dasolve AS (Org.nr 936 035 019), Lillogata 5P, 0484 Oslo, Norway. Contact: [privacy@archgate.dev](mailto:privacy@archgate.dev). ## Open source diff --git a/package.json b/package.json index b0d8bbc7..cc14d801 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "format": "oxfmt --write .", "format:check": "oxfmt --check .", "knip": "knip", + "license:check": "bun run scripts/check-licenses.ts", "lint": "oxlint --deny-warnings .", "postinstall": "node scripts/postinstall.cjs", "test": "bun test --timeout 60000", diff --git a/scripts/add-spdx-headers.ts b/scripts/add-spdx-headers.ts new file mode 100644 index 00000000..bccae9ad --- /dev/null +++ b/scripts/add-spdx-headers.ts @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate +/** + * add-spdx-headers.ts — One-time script to add SPDX license headers to all + * TypeScript source files that don't already have them. + * + * Run: bun run scripts/add-spdx-headers.ts + */ + +import { readFileSync, writeFileSync } from "node:fs"; +import { join } from "node:path"; + +import { Glob } from "bun"; + +const HEADER = `// SPDX-License-Identifier: Apache-2.0\n// Copyright 2026 Archgate\n`; +const ROOT = join(import.meta.dir, ".."); + +const patterns = ["src/**/*.ts", "tests/**/*.ts"]; +let updated = 0; +let skipped = 0; + +for (const pattern of patterns) { + const glob = new Glob(pattern); + for (const match of glob.scanSync({ cwd: ROOT, absolute: true })) { + const content = readFileSync(match, "utf-8"); + + // Skip if already has SPDX header + if (content.includes("SPDX-License-Identifier")) { + skipped++; + continue; + } + + // Handle shebang lines — insert after them + let newContent: string; + if (content.startsWith("#!")) { + const firstNewline = content.indexOf("\n"); + newContent = + content.slice(0, firstNewline + 1) + + HEADER + + content.slice(firstNewline + 1); + } else { + newContent = HEADER + content; + } + + writeFileSync(match, newContent); + updated++; + } +} + +console.log( + `Added SPDX headers to ${updated} files. Skipped ${skipped} (already had headers).` +); diff --git a/scripts/check-licenses.ts b/scripts/check-licenses.ts new file mode 100644 index 00000000..d0fa37fc --- /dev/null +++ b/scripts/check-licenses.ts @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate +/** + * check-licenses.ts — Verify all dependency licenses are compatible with Apache-2.0. + * + * Run: bun run license:check + * + * Scans top-level node_modules packages and checks their `license` field against + * an allowlist of permissive licenses. Fails with exit code 1 if any package uses + * a copyleft or unknown license. + */ + +import { readdirSync, readFileSync, existsSync } from "node:fs"; +import { join } from "node:path"; +import { styleText } from "node:util"; + +const ALLOWED_LICENSES = new Set([ + "MIT", + "Apache-2.0", + "ISC", + "BSD-2-Clause", + "BSD-3-Clause", + "0BSD", + "CC0-1.0", + "Unlicense", + "BlueOak-1.0.0", + "CC-BY-4.0", + "CC-BY-3.0", + "Python-2.0", + "(MIT OR Apache-2.0)", + "MIT OR Apache-2.0", + "(MIT AND Zlib)", + "(BSD-2-Clause OR MIT OR Apache-2.0)", + "(MIT OR CC0-1.0)", + "BlueOak-1.0.0 OR MIT OR Apache-2.0", +]); + +/** + * Normalize license expressions — handles SPDX expressions and common variants. + */ +function normalizeLicense(raw: string): string { + return raw.trim().replace(/^\(/u, "").replace(/\)$/u, ""); +} + +function isAllowed(license: string | undefined): boolean { + if (!license) return false; + if (ALLOWED_LICENSES.has(license)) return true; + + // Handle OR expressions: "MIT OR Apache-2.0" — all alternatives must be allowed + const normalized = normalizeLicense(license); + if (ALLOWED_LICENSES.has(normalized)) return true; + if (ALLOWED_LICENSES.has(`(${normalized})`)) return true; + + // If it's an OR expression, check if at least one option is allowed + if (normalized.includes(" OR ")) { + return normalized.split(" OR ").some((l) => ALLOWED_LICENSES.has(l.trim())); + } + + return false; +} + +const nodeModulesPath = join(import.meta.dir, "..", "node_modules"); +const violations: { name: string; license: string | undefined }[] = []; +let scanned = 0; + +const entries = readdirSync(nodeModulesPath, { withFileTypes: true }); + +for (const entry of entries) { + if (!entry.isDirectory()) continue; + + // Handle scoped packages (@scope/package) + if (entry.name.startsWith("@")) { + const scopePath = join(nodeModulesPath, entry.name); + const scopedEntries = readdirSync(scopePath, { withFileTypes: true }); + for (const scopedEntry of scopedEntries) { + if (!scopedEntry.isDirectory()) continue; + const pkgJsonPath = join(scopePath, scopedEntry.name, "package.json"); + if (!existsSync(pkgJsonPath)) continue; + const pkgJson = JSON.parse(readFileSync(pkgJsonPath, "utf-8")); + const name = `${entry.name}/${scopedEntry.name}`; + const license = pkgJson.license; + scanned++; + if (!isAllowed(license)) { + violations.push({ name, license }); + } + } + } else { + // Skip hidden dirs and .package-lock.json etc. + if (entry.name.startsWith(".")) continue; + const pkgJsonPath = join(nodeModulesPath, entry.name, "package.json"); + if (!existsSync(pkgJsonPath)) continue; + const pkgJson = JSON.parse(readFileSync(pkgJsonPath, "utf-8")); + const license = pkgJson.license; + scanned++; + if (!isAllowed(license)) { + violations.push({ name: entry.name, license }); + } + } +} + +console.log(`Scanned ${scanned} packages.`); + +if (violations.length > 0) { + console.log( + styleText( + "red", + `\n${violations.length} package(s) with disallowed or missing licenses:\n` + ) + ); + for (const v of violations) { + console.log( + ` ${styleText("bold", v.name)}: ${v.license ?? "(no license field)"}` + ); + } + console.log( + "\nIf a license is permissive but not in the allowlist, add it to scripts/check-licenses.ts." + ); + process.exit(1); +} else { + console.log( + styleText( + "green", + "All dependency licenses are compatible with Apache-2.0." + ) + ); +} diff --git a/src/cli.ts b/src/cli.ts index c7791547..9072778e 100755 --- a/src/cli.ts +++ b/src/cli.ts @@ -1,4 +1,6 @@ #!/usr/bin/env bun +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { Command, Option } from "@commander-js/extra-typings"; import { semver } from "bun"; @@ -31,6 +33,7 @@ import { initTelemetry, trackCommand, } from "./helpers/telemetry"; +import { showFirstRunNoticeIfNeeded } from "./helpers/telemetry-config"; import { checkForUpdatesIfNeeded } from "./helpers/update-check"; // Pre-main environment guards — these are user-facing errors (exit 1), not bugs. @@ -94,6 +97,9 @@ async function main() { // the second arg is the actual subcommand being executed. We use the action // command so `adr create` etc. resolves correctly instead of always "root". program.hook("preAction", async (_hookedCommand, actionCommand) => { + // Show one-time privacy disclosure before the first real command. + showFirstRunNoticeIfNeeded(); + // Ensure telemetry SDKs are initialized before emitting any events. // By the time a real command's preAction fires, the init promises have // usually already resolved (they started before command registration), diff --git a/src/commands/adr/create.ts b/src/commands/adr/create.ts index 05665612..5547a3d4 100644 --- a/src/commands/adr/create.ts +++ b/src/commands/adr/create.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { cursorTo } from "node:readline"; import type { Command } from "@commander-js/extra-typings"; diff --git a/src/commands/adr/domain/add.ts b/src/commands/adr/domain/add.ts index d3588888..8da3c0cb 100644 --- a/src/commands/adr/domain/add.ts +++ b/src/commands/adr/domain/add.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import type { Command } from "@commander-js/extra-typings"; import { exitWith } from "../../../helpers/exit"; diff --git a/src/commands/adr/domain/index.ts b/src/commands/adr/domain/index.ts index e305eb35..273b1bfe 100644 --- a/src/commands/adr/domain/index.ts +++ b/src/commands/adr/domain/index.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import type { Command } from "@commander-js/extra-typings"; import { registerDomainAddCommand } from "./add"; diff --git a/src/commands/adr/domain/list.ts b/src/commands/adr/domain/list.ts index ef1f227e..77bf5850 100644 --- a/src/commands/adr/domain/list.ts +++ b/src/commands/adr/domain/list.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { styleText } from "node:util"; import type { Command } from "@commander-js/extra-typings"; diff --git a/src/commands/adr/domain/remove.ts b/src/commands/adr/domain/remove.ts index e3a9cd4f..8ed4bb41 100644 --- a/src/commands/adr/domain/remove.ts +++ b/src/commands/adr/domain/remove.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import type { Command } from "@commander-js/extra-typings"; import { exitWith } from "../../../helpers/exit"; diff --git a/src/commands/adr/index.ts b/src/commands/adr/index.ts index 4f802e56..f2e1d113 100644 --- a/src/commands/adr/index.ts +++ b/src/commands/adr/index.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import type { Command } from "@commander-js/extra-typings"; import { registerAdrCreateCommand } from "./create"; diff --git a/src/commands/adr/list.ts b/src/commands/adr/list.ts index e6d8cc7d..a69d9b6a 100644 --- a/src/commands/adr/list.ts +++ b/src/commands/adr/list.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { existsSync } from "node:fs"; import { styleText } from "node:util"; diff --git a/src/commands/adr/show.ts b/src/commands/adr/show.ts index ba1810de..e580a569 100644 --- a/src/commands/adr/show.ts +++ b/src/commands/adr/show.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import type { Command } from "@commander-js/extra-typings"; import { findAdrFileById } from "../../helpers/adr-writer"; diff --git a/src/commands/adr/update.ts b/src/commands/adr/update.ts index 121e6d4d..72b3dee6 100644 --- a/src/commands/adr/update.ts +++ b/src/commands/adr/update.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import type { Command } from "@commander-js/extra-typings"; import { updateAdrFile } from "../../helpers/adr-writer"; diff --git a/src/commands/check.ts b/src/commands/check.ts index 30b55a18..d909ac33 100644 --- a/src/commands/check.ts +++ b/src/commands/check.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import type { Command } from "@commander-js/extra-typings"; import { loadRuleAdrs } from "../engine/loader"; diff --git a/src/commands/clean.ts b/src/commands/clean.ts index b18fbe82..cc423272 100644 --- a/src/commands/clean.ts +++ b/src/commands/clean.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { existsSync, readdirSync, rmSync } from "node:fs"; import { join } from "node:path"; diff --git a/src/commands/doctor.ts b/src/commands/doctor.ts index 924bc678..c84c1969 100644 --- a/src/commands/doctor.ts +++ b/src/commands/doctor.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { styleText } from "node:util"; import type { Command } from "@commander-js/extra-typings"; diff --git a/src/commands/init.ts b/src/commands/init.ts index 33033405..535f8497 100644 --- a/src/commands/init.ts +++ b/src/commands/init.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { existsSync } from "node:fs"; import { join } from "node:path"; import { cursorTo } from "node:readline"; diff --git a/src/commands/login.ts b/src/commands/login.ts index aa80bf38..4a06a467 100644 --- a/src/commands/login.ts +++ b/src/commands/login.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { styleText } from "node:util"; import type { Command } from "@commander-js/extra-typings"; diff --git a/src/commands/plugin/index.ts b/src/commands/plugin/index.ts index caab5c6d..73af4e0b 100644 --- a/src/commands/plugin/index.ts +++ b/src/commands/plugin/index.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import type { Command } from "@commander-js/extra-typings"; import { registerPluginInstallCommand } from "./install"; diff --git a/src/commands/plugin/install.ts b/src/commands/plugin/install.ts index 8817702a..a8e456e7 100644 --- a/src/commands/plugin/install.ts +++ b/src/commands/plugin/install.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { styleText } from "node:util"; import type { Command } from "@commander-js/extra-typings"; diff --git a/src/commands/plugin/url.ts b/src/commands/plugin/url.ts index 89e2ca85..0ef10284 100644 --- a/src/commands/plugin/url.ts +++ b/src/commands/plugin/url.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import type { Command } from "@commander-js/extra-typings"; import { Option } from "@commander-js/extra-typings"; diff --git a/src/commands/review-context.ts b/src/commands/review-context.ts index 413affe2..83d2ee02 100644 --- a/src/commands/review-context.ts +++ b/src/commands/review-context.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import type { Command } from "@commander-js/extra-typings"; import { buildReviewContext } from "../engine/context"; diff --git a/src/commands/session-context/claude-code.ts b/src/commands/session-context/claude-code.ts index 19be7c8b..7e7fea01 100644 --- a/src/commands/session-context/claude-code.ts +++ b/src/commands/session-context/claude-code.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import type { Command } from "@commander-js/extra-typings"; import { Option } from "@commander-js/extra-typings"; diff --git a/src/commands/session-context/copilot.ts b/src/commands/session-context/copilot.ts index 108886f4..bb99881e 100644 --- a/src/commands/session-context/copilot.ts +++ b/src/commands/session-context/copilot.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import type { Command } from "@commander-js/extra-typings"; import { Option } from "@commander-js/extra-typings"; diff --git a/src/commands/session-context/cursor.ts b/src/commands/session-context/cursor.ts index 205c7f51..31e11383 100644 --- a/src/commands/session-context/cursor.ts +++ b/src/commands/session-context/cursor.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import type { Command } from "@commander-js/extra-typings"; import { Option } from "@commander-js/extra-typings"; diff --git a/src/commands/session-context/index.ts b/src/commands/session-context/index.ts index da14d7cc..66d527ef 100644 --- a/src/commands/session-context/index.ts +++ b/src/commands/session-context/index.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import type { Command } from "@commander-js/extra-typings"; import { registerClaudeCodeSessionContextCommand } from "./claude-code"; diff --git a/src/commands/session-context/opencode.ts b/src/commands/session-context/opencode.ts index 60705cb9..f8e98e9a 100644 --- a/src/commands/session-context/opencode.ts +++ b/src/commands/session-context/opencode.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import type { Command } from "@commander-js/extra-typings"; import { Option } from "@commander-js/extra-typings"; diff --git a/src/commands/telemetry.ts b/src/commands/telemetry.ts index 183fefa7..5d138101 100644 --- a/src/commands/telemetry.ts +++ b/src/commands/telemetry.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import type { Command } from "@commander-js/extra-typings"; import { exitWith } from "../helpers/exit"; @@ -37,6 +39,9 @@ export function registerTelemetryCommand(program: Command) { console.log( "\nTo disable: `archgate telemetry disable` or set ARCHGATE_TELEMETRY=0" ); + console.log( + "\nLegal basis: https://archgate.dev/legitimate-interest-assessment" + ); console.log("Learn more: https://cli.archgate.dev/reference/telemetry"); } else { console.log("Telemetry is disabled."); diff --git a/src/commands/upgrade.ts b/src/commands/upgrade.ts index cee36d67..1a5e2adb 100644 --- a/src/commands/upgrade.ts +++ b/src/commands/upgrade.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { existsSync } from "node:fs"; import { dirname, join } from "node:path"; import { clearLine, cursorTo } from "node:readline"; diff --git a/src/engine/context.ts b/src/engine/context.ts index 810092df..0c8b4bde 100644 --- a/src/engine/context.ts +++ b/src/engine/context.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import type { AdrDocument, AdrDomain } from "../formats/adr"; import { getChangedFiles, getStagedFiles } from "./git-files"; import { loadRuleAdrs, parseAllAdrs } from "./loader"; diff --git a/src/engine/git-files.ts b/src/engine/git-files.ts index 1fcf2ab5..6475ed23 100644 --- a/src/engine/git-files.ts +++ b/src/engine/git-files.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate /** Git file-listing utilities for ADR scope resolution and change detection. */ import { logDebug } from "../helpers/log"; diff --git a/src/engine/loader.ts b/src/engine/loader.ts index acaa2934..db48bf89 100644 --- a/src/engine/loader.ts +++ b/src/engine/loader.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { readdirSync } from "node:fs"; import { join, basename } from "node:path"; import { pathToFileURL } from "node:url"; diff --git a/src/engine/reporter.ts b/src/engine/reporter.ts index cd3ad82d..d7b4f249 100644 --- a/src/engine/reporter.ts +++ b/src/engine/reporter.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { styleText } from "node:util"; import type { Severity } from "../formats/rules"; diff --git a/src/engine/rule-scanner.ts b/src/engine/rule-scanner.ts index f6fba1ca..ba6283ed 100644 --- a/src/engine/rule-scanner.ts +++ b/src/engine/rule-scanner.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { parseModule } from "meriyah"; /** diff --git a/src/engine/runner.ts b/src/engine/runner.ts index 2cd03a20..dc176e09 100644 --- a/src/engine/runner.ts +++ b/src/engine/runner.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { lstatSync } from "node:fs"; import { relative, resolve, isAbsolute } from "node:path"; diff --git a/src/engine/source-positions.ts b/src/engine/source-positions.ts index 19fa968d..672d329c 100644 --- a/src/engine/source-positions.ts +++ b/src/engine/source-positions.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate /** * Utilities for mapping violation positions from transpiled JS back to * original TypeScript source, skipping matches in comments and strings. diff --git a/src/formats/adr.ts b/src/formats/adr.ts index e666a171..5c72656d 100644 --- a/src/formats/adr.ts +++ b/src/formats/adr.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { z } from "zod"; import { DOMAIN_NAME_PATTERN } from "./project-config"; diff --git a/src/formats/project-config.ts b/src/formats/project-config.ts index eeb74ccc..6ec1b3f8 100644 --- a/src/formats/project-config.ts +++ b/src/formats/project-config.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { z } from "zod"; export const DOMAIN_NAME_PATTERN = /^[a-z][a-z0-9-]*$/u; diff --git a/src/formats/rules.ts b/src/formats/rules.ts index c36cc375..45cc26bc 100644 --- a/src/formats/rules.ts +++ b/src/formats/rules.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate // --- Severity --- export type Severity = "error" | "warning" | "info"; diff --git a/src/helpers/adr-templates.ts b/src/helpers/adr-templates.ts index 0f35b874..410464d8 100644 --- a/src/helpers/adr-templates.ts +++ b/src/helpers/adr-templates.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import type { AdrDomain } from "../formats/adr"; export function generateExampleAdr(projectName: string): string { diff --git a/src/helpers/adr-writer.ts b/src/helpers/adr-writer.ts index 636c84be..515489cf 100644 --- a/src/helpers/adr-writer.ts +++ b/src/helpers/adr-writer.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { existsSync, readdirSync } from "node:fs"; import { join, basename } from "node:path"; diff --git a/src/helpers/auth.ts b/src/helpers/auth.ts index 8fe09ec8..7b1766f7 100644 --- a/src/helpers/auth.ts +++ b/src/helpers/auth.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate /** * auth.ts — GitHub Device Flow authentication and archgate token management. * diff --git a/src/helpers/binary-upgrade.ts b/src/helpers/binary-upgrade.ts index 8909a4cb..e6ff7a6e 100644 --- a/src/helpers/binary-upgrade.ts +++ b/src/helpers/binary-upgrade.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { createHash } from "node:crypto"; import { chmodSync, mkdtempSync, renameSync, unlinkSync } from "node:fs"; import { unlink } from "node:fs/promises"; diff --git a/src/helpers/claude-settings.ts b/src/helpers/claude-settings.ts index 5d8a7bc5..203e88c0 100644 --- a/src/helpers/claude-settings.ts +++ b/src/helpers/claude-settings.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { existsSync, mkdirSync } from "node:fs"; import { join } from "node:path"; diff --git a/src/helpers/copilot-settings.ts b/src/helpers/copilot-settings.ts index b625af86..7e3aafda 100644 --- a/src/helpers/copilot-settings.ts +++ b/src/helpers/copilot-settings.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { existsSync, mkdirSync } from "node:fs"; import { join } from "node:path"; diff --git a/src/helpers/credential-store.ts b/src/helpers/credential-store.ts index 1b81e1dc..849b5dc1 100644 --- a/src/helpers/credential-store.ts +++ b/src/helpers/credential-store.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate /** * credential-store.ts — Secure credential storage using git's native credential helpers. * diff --git a/src/helpers/cursor-settings.ts b/src/helpers/cursor-settings.ts index d9ab0058..a5b18693 100644 --- a/src/helpers/cursor-settings.ts +++ b/src/helpers/cursor-settings.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate /** * Cursor editor integration. * diff --git a/src/helpers/doctor.ts b/src/helpers/doctor.ts index b4bcaf2c..a85dc863 100644 --- a/src/helpers/doctor.ts +++ b/src/helpers/doctor.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate /** * doctor.ts — Gathers system diagnostic information for debugging. * diff --git a/src/helpers/editor-detect.ts b/src/helpers/editor-detect.ts index a800176d..52e2d367 100644 --- a/src/helpers/editor-detect.ts +++ b/src/helpers/editor-detect.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate /** * editor-detect.ts — Detects available editor CLIs and prompts the user to select. * diff --git a/src/helpers/exit.ts b/src/helpers/exit.ts index b16d6994..61b97f01 100644 --- a/src/helpers/exit.ts +++ b/src/helpers/exit.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate /** * exit.ts — Centralized process-exit helper for the CLI. * diff --git a/src/helpers/git.ts b/src/helpers/git.ts index 10b7c200..abbcdbe2 100644 --- a/src/helpers/git.ts +++ b/src/helpers/git.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { logDebug, logInfo } from "./log"; import { isWindows, isMacOS, resolveCommand } from "./platform"; diff --git a/src/helpers/init-project.ts b/src/helpers/init-project.ts index ef955864..62285f47 100644 --- a/src/helpers/init-project.ts +++ b/src/helpers/init-project.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { existsSync, readdirSync } from "node:fs"; import { basename, join } from "node:path"; diff --git a/src/helpers/install-info.ts b/src/helpers/install-info.ts index 910afa0c..541b3bd5 100644 --- a/src/helpers/install-info.ts +++ b/src/helpers/install-info.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate /** * install-info.ts — Detects the CLI installation method and project context. * diff --git a/src/helpers/log.ts b/src/helpers/log.ts index 65e83288..2bfc8034 100644 --- a/src/helpers/log.ts +++ b/src/helpers/log.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { styleText } from "node:util"; // --------------------------------------------------------------------------- diff --git a/src/helpers/login-flow.ts b/src/helpers/login-flow.ts index 6a674ff2..743f5772 100644 --- a/src/helpers/login-flow.ts +++ b/src/helpers/login-flow.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate /** * login-flow.ts — Shared GitHub device flow + signup logic * used by both `login` and `init` commands. @@ -49,6 +51,9 @@ export interface LoginFlowResult { export async function runLoginFlow( options?: LoginFlowOptions ): Promise { + console.log("By signing up, you agree to the Archgate Terms of Service:"); + console.log("https://archgate.dev/terms-of-service\n"); + logInfo("Authenticating with GitHub...\n"); logDebug("Starting login flow"); diff --git a/src/helpers/output.ts b/src/helpers/output.ts index 9c0f7033..c4e84cf7 100644 --- a/src/helpers/output.ts +++ b/src/helpers/output.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate /** * Output format detection for agent vs human contexts. * diff --git a/src/helpers/paths.ts b/src/helpers/paths.ts index 828a20e0..5ca65b88 100644 --- a/src/helpers/paths.ts +++ b/src/helpers/paths.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { existsSync, mkdirSync } from "node:fs"; import { homedir } from "node:os"; import { join, dirname } from "node:path"; diff --git a/src/helpers/platform.ts b/src/helpers/platform.ts index 284dcb32..46c03a9b 100644 --- a/src/helpers/platform.ts +++ b/src/helpers/platform.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { readFileSync } from "node:fs"; // --------------------------------------------------------------------------- diff --git a/src/helpers/plugin-install.ts b/src/helpers/plugin-install.ts index 44477f9a..b6afb944 100644 --- a/src/helpers/plugin-install.ts +++ b/src/helpers/plugin-install.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate /** Download and install the archgate plugin for supported editors. */ import { mkdirSync, unlinkSync } from "node:fs"; diff --git a/src/helpers/project-config.ts b/src/helpers/project-config.ts index 0ad52df9..1436a674 100644 --- a/src/helpers/project-config.ts +++ b/src/helpers/project-config.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate /** * project-config.ts — Read/write `.archgate/config.json` at project root. * diff --git a/src/helpers/repo-probe.ts b/src/helpers/repo-probe.ts index 198e39f8..5addec49 100644 --- a/src/helpers/repo-probe.ts +++ b/src/helpers/repo-probe.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate /** * repo-probe.ts — Unauthenticated API probes to determine whether a Git * repository is public on its host (GitHub, GitLab, Bitbucket, Azure DevOps). diff --git a/src/helpers/repo.ts b/src/helpers/repo.ts index 52d63f81..5338783f 100644 --- a/src/helpers/repo.ts +++ b/src/helpers/repo.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate /** * repo.ts — Detect the git repository context for telemetry enrichment. * diff --git a/src/helpers/rules-shim.ts b/src/helpers/rules-shim.ts index 2deb0260..4a933e14 100644 --- a/src/helpers/rules-shim.ts +++ b/src/helpers/rules-shim.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { logDebug } from "./log"; import { projectPath } from "./paths"; diff --git a/src/helpers/sentry.ts b/src/helpers/sentry.ts index 2fd4e569..959efbb7 100644 --- a/src/helpers/sentry.ts +++ b/src/helpers/sentry.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate /** * sentry.ts — Error tracking via @sentry/node-core light mode. * diff --git a/src/helpers/session-context-copilot.ts b/src/helpers/session-context-copilot.ts index 04cb96c6..73e926f8 100644 --- a/src/helpers/session-context-copilot.ts +++ b/src/helpers/session-context-copilot.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { readdirSync, statSync } from "node:fs"; import { join, resolve } from "node:path"; diff --git a/src/helpers/session-context-opencode.ts b/src/helpers/session-context-opencode.ts index 52f1cef5..fbc0d9fd 100644 --- a/src/helpers/session-context-opencode.ts +++ b/src/helpers/session-context-opencode.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { Database } from "bun:sqlite"; import { existsSync } from "node:fs"; import { resolve } from "node:path"; diff --git a/src/helpers/session-context.ts b/src/helpers/session-context.ts index 293a8778..9cbe44e8 100644 --- a/src/helpers/session-context.ts +++ b/src/helpers/session-context.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { readdirSync, statSync } from "node:fs"; import { homedir } from "node:os"; import { basename, join } from "node:path"; diff --git a/src/helpers/signup.ts b/src/helpers/signup.ts index 2e95a325..ca23acc8 100644 --- a/src/helpers/signup.ts +++ b/src/helpers/signup.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate /** * signup.ts — Archgate plugins platform signup for unregistered users. */ diff --git a/src/helpers/telemetry-config.ts b/src/helpers/telemetry-config.ts index ec08e46f..7d233402 100644 --- a/src/helpers/telemetry-config.ts +++ b/src/helpers/telemetry-config.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate /** * telemetry-config.ts — Manages telemetry preferences in ~/.archgate/config.json. * @@ -26,6 +28,8 @@ export interface TelemetryConfig { installId: string; /** ISO date of first telemetry config creation. */ createdAt: string; + /** Whether the first-run privacy notice has been shown. */ + noticeShown?: boolean; } // --------------------------------------------------------------------------- @@ -145,6 +149,37 @@ function saveTelemetryConfigAsync(config: TelemetryConfig): void { }); } +// --------------------------------------------------------------------------- +// First-run privacy notice +// --------------------------------------------------------------------------- + +/** + * Show a one-time privacy notice on first interactive CLI session. + * Only displays when: TTY, not CI, telemetry enabled, notice not yet shown. + * Non-blocking — saves the "shown" flag asynchronously. + */ +export function showFirstRunNoticeIfNeeded(): void { + if (!process.stdout.isTTY) return; + if (Bun.env.CI) return; + if (isEnvTelemetryDisabled()) return; + + const config = loadTelemetryConfig(); + if (!config.telemetry) return; + if (config.noticeShown) return; + + logDebug("Showing first-run privacy notice"); + process.stdout.write( + "\nArchgate collects anonymous usage data to improve the tool.\n" + + "Details: https://cli.archgate.dev/reference/telemetry\n" + + "Disable: archgate telemetry disable\n\n" + ); + + // Persist the flag so the notice is shown only once + config.noticeShown = true; + cachedConfig = config; + saveTelemetryConfigAsync(config); +} + // --------------------------------------------------------------------------- // Testing helpers // --------------------------------------------------------------------------- diff --git a/src/helpers/telemetry.ts b/src/helpers/telemetry.ts index c3ae8573..8fbf4c0a 100644 --- a/src/helpers/telemetry.ts +++ b/src/helpers/telemetry.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate /** * telemetry.ts — Anonymous usage analytics via PostHog Node SDK. * diff --git a/src/helpers/tls.ts b/src/helpers/tls.ts index 230870f1..157406ff 100644 --- a/src/helpers/tls.ts +++ b/src/helpers/tls.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate /** * tls.ts — Detect TLS/SSL interception errors common in corporate environments * and provide actionable guidance to the user. diff --git a/src/helpers/update-check.ts b/src/helpers/update-check.ts index a5362e75..9fc99811 100644 --- a/src/helpers/update-check.ts +++ b/src/helpers/update-check.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { semver } from "bun"; import { fetchLatestGitHubVersion } from "./binary-upgrade"; diff --git a/src/helpers/vscode-settings.ts b/src/helpers/vscode-settings.ts index f130f9f7..b9c6c379 100644 --- a/src/helpers/vscode-settings.ts +++ b/src/helpers/vscode-settings.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { existsSync, mkdirSync } from "node:fs"; import { homedir } from "node:os"; import { join } from "node:path"; diff --git a/tests/commands/adr.test.ts b/tests/commands/adr.test.ts index 3b6e6e96..06bbf811 100644 --- a/tests/commands/adr.test.ts +++ b/tests/commands/adr.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, beforeEach, afterEach } from "bun:test"; import { mkdtempSync, rmSync, readdirSync, existsSync } from "node:fs"; import { tmpdir } from "node:os"; diff --git a/tests/commands/adr/create.test.ts b/tests/commands/adr/create.test.ts index c5fded77..3d138a81 100644 --- a/tests/commands/adr/create.test.ts +++ b/tests/commands/adr/create.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, beforeEach, afterEach, spyOn } from "bun:test"; import { mkdtempSync, rmSync, mkdirSync, existsSync } from "node:fs"; import { tmpdir } from "node:os"; diff --git a/tests/commands/adr/domain/add.test.ts b/tests/commands/adr/domain/add.test.ts index 7f6d51fa..ce9432cb 100644 --- a/tests/commands/adr/domain/add.test.ts +++ b/tests/commands/adr/domain/add.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, beforeEach, afterEach, spyOn } from "bun:test"; import { existsSync, mkdirSync, mkdtempSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; diff --git a/tests/commands/adr/domain/list.test.ts b/tests/commands/adr/domain/list.test.ts index a38f1f88..d0df2910 100644 --- a/tests/commands/adr/domain/list.test.ts +++ b/tests/commands/adr/domain/list.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, beforeEach, afterEach, spyOn } from "bun:test"; import { mkdirSync, mkdtempSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; diff --git a/tests/commands/adr/domain/remove.test.ts b/tests/commands/adr/domain/remove.test.ts index 8f022951..f6785038 100644 --- a/tests/commands/adr/domain/remove.test.ts +++ b/tests/commands/adr/domain/remove.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, beforeEach, afterEach, spyOn } from "bun:test"; import { mkdirSync, mkdtempSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; diff --git a/tests/commands/adr/index.test.ts b/tests/commands/adr/index.test.ts index 9cfb5de0..615b8c24 100644 --- a/tests/commands/adr/index.test.ts +++ b/tests/commands/adr/index.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test } from "bun:test"; import { Command } from "@commander-js/extra-typings"; diff --git a/tests/commands/adr/list.test.ts b/tests/commands/adr/list.test.ts index 07f9cc2c..5eb152e1 100644 --- a/tests/commands/adr/list.test.ts +++ b/tests/commands/adr/list.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, beforeEach, afterEach, spyOn } from "bun:test"; import { mkdtempSync, rmSync, mkdirSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; diff --git a/tests/commands/adr/show.test.ts b/tests/commands/adr/show.test.ts index 11aaa096..d6b347e5 100644 --- a/tests/commands/adr/show.test.ts +++ b/tests/commands/adr/show.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, beforeEach, afterEach, spyOn } from "bun:test"; import { mkdtempSync, rmSync, mkdirSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; diff --git a/tests/commands/adr/update.test.ts b/tests/commands/adr/update.test.ts index 8dbf73bd..92c6e8ac 100644 --- a/tests/commands/adr/update.test.ts +++ b/tests/commands/adr/update.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, beforeEach, afterEach, spyOn } from "bun:test"; import { mkdtempSync, rmSync, mkdirSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; diff --git a/tests/commands/check-security.test.ts b/tests/commands/check-security.test.ts index f85b764c..1debcded 100644 --- a/tests/commands/check-security.test.ts +++ b/tests/commands/check-security.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, beforeEach, afterEach } from "bun:test"; import { mkdtempSync, diff --git a/tests/commands/check.test.ts b/tests/commands/check.test.ts index 5e9d0466..cd1fed85 100644 --- a/tests/commands/check.test.ts +++ b/tests/commands/check.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, beforeEach, afterEach } from "bun:test"; import { mkdtempSync, rmSync, mkdirSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; diff --git a/tests/commands/clean.test.ts b/tests/commands/clean.test.ts index 788f2f3a..931a1e6f 100644 --- a/tests/commands/clean.test.ts +++ b/tests/commands/clean.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, beforeEach, afterEach, spyOn } from "bun:test"; import { mkdtempSync, diff --git a/tests/commands/init.test.ts b/tests/commands/init.test.ts index 8c4fcbc6..57aa779d 100644 --- a/tests/commands/init.test.ts +++ b/tests/commands/init.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, beforeEach, afterEach } from "bun:test"; import { mkdtempSync, rmSync, existsSync } from "node:fs"; import { tmpdir } from "node:os"; diff --git a/tests/commands/login.test.ts b/tests/commands/login.test.ts index 3f5d0cc5..58fbc202 100644 --- a/tests/commands/login.test.ts +++ b/tests/commands/login.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test } from "bun:test"; import { Command } from "@commander-js/extra-typings"; diff --git a/tests/commands/plugin/index.test.ts b/tests/commands/plugin/index.test.ts index 737fc688..54c71abe 100644 --- a/tests/commands/plugin/index.test.ts +++ b/tests/commands/plugin/index.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test } from "bun:test"; import { Command } from "@commander-js/extra-typings"; diff --git a/tests/commands/plugin/install.test.ts b/tests/commands/plugin/install.test.ts index 6916b7bd..e4b63b8b 100644 --- a/tests/commands/plugin/install.test.ts +++ b/tests/commands/plugin/install.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test } from "bun:test"; import { Command } from "@commander-js/extra-typings"; diff --git a/tests/commands/plugin/url.test.ts b/tests/commands/plugin/url.test.ts index 47847b9a..b942fd45 100644 --- a/tests/commands/plugin/url.test.ts +++ b/tests/commands/plugin/url.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test } from "bun:test"; import { Command } from "@commander-js/extra-typings"; diff --git a/tests/commands/review-context.test.ts b/tests/commands/review-context.test.ts index 966e6f9b..33f0c203 100644 --- a/tests/commands/review-context.test.ts +++ b/tests/commands/review-context.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test } from "bun:test"; import { Command } from "@commander-js/extra-typings"; diff --git a/tests/commands/session-context.test.ts b/tests/commands/session-context.test.ts index 95cd683e..f4158f3f 100644 --- a/tests/commands/session-context.test.ts +++ b/tests/commands/session-context.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test } from "bun:test"; import { Command } from "@commander-js/extra-typings"; diff --git a/tests/commands/session-context/claude-code.test.ts b/tests/commands/session-context/claude-code.test.ts index 97196fb6..2faf4961 100644 --- a/tests/commands/session-context/claude-code.test.ts +++ b/tests/commands/session-context/claude-code.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test } from "bun:test"; import { Command } from "@commander-js/extra-typings"; diff --git a/tests/commands/session-context/copilot.test.ts b/tests/commands/session-context/copilot.test.ts index 5ba2ad17..d4a3b7d9 100644 --- a/tests/commands/session-context/copilot.test.ts +++ b/tests/commands/session-context/copilot.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test } from "bun:test"; import { Command } from "@commander-js/extra-typings"; diff --git a/tests/commands/session-context/cursor.test.ts b/tests/commands/session-context/cursor.test.ts index 3b715158..c31fa89f 100644 --- a/tests/commands/session-context/cursor.test.ts +++ b/tests/commands/session-context/cursor.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test } from "bun:test"; import { Command } from "@commander-js/extra-typings"; diff --git a/tests/commands/session-context/opencode.test.ts b/tests/commands/session-context/opencode.test.ts index da3c1560..af12145a 100644 --- a/tests/commands/session-context/opencode.test.ts +++ b/tests/commands/session-context/opencode.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test } from "bun:test"; import { Command } from "@commander-js/extra-typings"; diff --git a/tests/commands/upgrade.test.ts b/tests/commands/upgrade.test.ts index 033d87b9..77acccc9 100644 --- a/tests/commands/upgrade.test.ts +++ b/tests/commands/upgrade.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, beforeEach, afterEach } from "bun:test"; import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; diff --git a/tests/engine/context.test.ts b/tests/engine/context.test.ts index f0a14000..568aea8b 100644 --- a/tests/engine/context.test.ts +++ b/tests/engine/context.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, beforeEach, afterEach } from "bun:test"; import { mkdtempSync, rmSync, mkdirSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; diff --git a/tests/engine/git-files.test.ts b/tests/engine/git-files.test.ts index 5133a484..71a66711 100644 --- a/tests/engine/git-files.test.ts +++ b/tests/engine/git-files.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, beforeEach, afterEach } from "bun:test"; import { mkdtempSync, mkdirSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; diff --git a/tests/engine/loader-security.test.ts b/tests/engine/loader-security.test.ts index 6100d68d..dd122064 100644 --- a/tests/engine/loader-security.test.ts +++ b/tests/engine/loader-security.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, beforeEach, afterEach } from "bun:test"; import { mkdtempSync, rmSync, mkdirSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; diff --git a/tests/engine/loader.test.ts b/tests/engine/loader.test.ts index e72e78b4..9f2dc3fb 100644 --- a/tests/engine/loader.test.ts +++ b/tests/engine/loader.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, beforeEach, afterEach } from "bun:test"; import { mkdtempSync, diff --git a/tests/engine/reporter.test.ts b/tests/engine/reporter.test.ts index 23206040..d953345b 100644 --- a/tests/engine/reporter.test.ts +++ b/tests/engine/reporter.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, beforeEach, afterEach, spyOn } from "bun:test"; import { diff --git a/tests/engine/rule-scanner-adversarial.test.ts b/tests/engine/rule-scanner-adversarial.test.ts index cd9e61c2..bfdd6801 100644 --- a/tests/engine/rule-scanner-adversarial.test.ts +++ b/tests/engine/rule-scanner-adversarial.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test } from "bun:test"; import { scanRuleSource } from "../../src/engine/rule-scanner"; diff --git a/tests/engine/rule-scanner-positions.test.ts b/tests/engine/rule-scanner-positions.test.ts index cc018f88..3a1b82d3 100644 --- a/tests/engine/rule-scanner-positions.test.ts +++ b/tests/engine/rule-scanner-positions.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test } from "bun:test"; import { scanRuleSource } from "../../src/engine/rule-scanner"; diff --git a/tests/engine/rule-scanner.test.ts b/tests/engine/rule-scanner.test.ts index d841d591..c4220232 100644 --- a/tests/engine/rule-scanner.test.ts +++ b/tests/engine/rule-scanner.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test } from "bun:test"; import { scanRuleSource } from "../../src/engine/rule-scanner"; diff --git a/tests/engine/runner-security.test.ts b/tests/engine/runner-security.test.ts index 2dd05339..fc4d5ff1 100644 --- a/tests/engine/runner-security.test.ts +++ b/tests/engine/runner-security.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, beforeEach, afterEach } from "bun:test"; import { mkdtempSync, rmSync, mkdirSync } from "node:fs"; import { tmpdir } from "node:os"; diff --git a/tests/engine/runner.test.ts b/tests/engine/runner.test.ts index b0943b30..c14afd93 100644 --- a/tests/engine/runner.test.ts +++ b/tests/engine/runner.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, beforeEach, afterEach } from "bun:test"; import { mkdtempSync, rmSync, mkdirSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; diff --git a/tests/engine/source-positions.test.ts b/tests/engine/source-positions.test.ts index 7d68526e..b169d7c9 100644 --- a/tests/engine/source-positions.test.ts +++ b/tests/engine/source-positions.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test } from "bun:test"; import { diff --git a/tests/fixtures/rules/TEST-001-sample.rules.ts b/tests/fixtures/rules/TEST-001-sample.rules.ts index 4a261c72..23e37593 100644 --- a/tests/fixtures/rules/TEST-001-sample.rules.ts +++ b/tests/fixtures/rules/TEST-001-sample.rules.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import type { RuleSet } from "../../../src/formats/rules"; export default { diff --git a/tests/formats/adr-fuzz.test.ts b/tests/formats/adr-fuzz.test.ts index 372e7a6a..2c564497 100644 --- a/tests/formats/adr-fuzz.test.ts +++ b/tests/formats/adr-fuzz.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test } from "bun:test"; import fc from "fast-check"; diff --git a/tests/formats/adr.test.ts b/tests/formats/adr.test.ts index b6430b81..c2b70cb0 100644 --- a/tests/formats/adr.test.ts +++ b/tests/formats/adr.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test } from "bun:test"; import { join } from "node:path"; diff --git a/tests/formats/project-config-fuzz.test.ts b/tests/formats/project-config-fuzz.test.ts index 9dae50b8..3583dfff 100644 --- a/tests/formats/project-config-fuzz.test.ts +++ b/tests/formats/project-config-fuzz.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test } from "bun:test"; import fc from "fast-check"; diff --git a/tests/formats/rules.test.ts b/tests/formats/rules.test.ts index cdb6ced0..845267ae 100644 --- a/tests/formats/rules.test.ts +++ b/tests/formats/rules.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test } from "bun:test"; import type { RuleSet } from "../../src/formats/rules"; diff --git a/tests/helpers/adr-templates.test.ts b/tests/helpers/adr-templates.test.ts index 710a286c..869a84bb 100644 --- a/tests/helpers/adr-templates.test.ts +++ b/tests/helpers/adr-templates.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test } from "bun:test"; import { parseAdr } from "../../src/formats/adr"; diff --git a/tests/helpers/adr-writer.test.ts b/tests/helpers/adr-writer.test.ts index 7c5f9655..58e79a5c 100644 --- a/tests/helpers/adr-writer.test.ts +++ b/tests/helpers/adr-writer.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, beforeEach, afterEach } from "bun:test"; import { mkdtempSync, diff --git a/tests/helpers/auth-poll.test.ts b/tests/helpers/auth-poll.test.ts index d6476a1a..cf45aefd 100644 --- a/tests/helpers/auth-poll.test.ts +++ b/tests/helpers/auth-poll.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, mock } from "bun:test"; /** Type-safe fetch mock — Bun's fetch type includes `preconnect` which mock() doesn't provide. */ diff --git a/tests/helpers/auth.test.ts b/tests/helpers/auth.test.ts index a56da959..a83f9687 100644 --- a/tests/helpers/auth.test.ts +++ b/tests/helpers/auth.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, beforeEach, afterEach, mock } from "bun:test"; import { mkdtempSync, rmSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; diff --git a/tests/helpers/binary-upgrade.test.ts b/tests/helpers/binary-upgrade.test.ts index 60ab1265..bd3cee4c 100644 --- a/tests/helpers/binary-upgrade.test.ts +++ b/tests/helpers/binary-upgrade.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, mock, beforeEach, afterEach } from "bun:test"; import { existsSync, diff --git a/tests/helpers/claude-settings.test.ts b/tests/helpers/claude-settings.test.ts index 36ba425f..7288558f 100644 --- a/tests/helpers/claude-settings.test.ts +++ b/tests/helpers/claude-settings.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, beforeEach, afterEach } from "bun:test"; import { mkdtempSync, rmSync, existsSync, mkdirSync } from "node:fs"; import { tmpdir } from "node:os"; diff --git a/tests/helpers/copilot-settings.test.ts b/tests/helpers/copilot-settings.test.ts index 0a5f67af..86eaf044 100644 --- a/tests/helpers/copilot-settings.test.ts +++ b/tests/helpers/copilot-settings.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, beforeEach, afterEach } from "bun:test"; import { mkdtempSync, rmSync, existsSync } from "node:fs"; import { tmpdir } from "node:os"; diff --git a/tests/helpers/credential-store.test.ts b/tests/helpers/credential-store.test.ts index 50a7e61a..4f8a8eea 100644 --- a/tests/helpers/credential-store.test.ts +++ b/tests/helpers/credential-store.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, beforeEach, afterEach } from "bun:test"; import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; diff --git a/tests/helpers/cursor-settings.test.ts b/tests/helpers/cursor-settings.test.ts index 7c48b8b8..d6b018e6 100644 --- a/tests/helpers/cursor-settings.test.ts +++ b/tests/helpers/cursor-settings.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, beforeEach, afterEach } from "bun:test"; import { mkdtempSync, rmSync, existsSync } from "node:fs"; import { tmpdir } from "node:os"; diff --git a/tests/helpers/doctor.test.ts b/tests/helpers/doctor.test.ts index 7aaebc5f..ba559604 100644 --- a/tests/helpers/doctor.test.ts +++ b/tests/helpers/doctor.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test } from "bun:test"; import { runDoctor } from "../../src/helpers/doctor"; diff --git a/tests/helpers/editor-detect.test.ts b/tests/helpers/editor-detect.test.ts index 41c6cc94..8ba818f3 100644 --- a/tests/helpers/editor-detect.test.ts +++ b/tests/helpers/editor-detect.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test"; // --------------------------------------------------------------------------- diff --git a/tests/helpers/exit.test.ts b/tests/helpers/exit.test.ts index c834c3ba..02ab86d7 100644 --- a/tests/helpers/exit.test.ts +++ b/tests/helpers/exit.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, beforeEach, afterEach } from "bun:test"; import { diff --git a/tests/helpers/git.test.ts b/tests/helpers/git.test.ts index 7aca5829..ac7997bb 100644 --- a/tests/helpers/git.test.ts +++ b/tests/helpers/git.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test } from "bun:test"; import { installGit } from "../../src/helpers/git"; diff --git a/tests/helpers/init-project.test.ts b/tests/helpers/init-project.test.ts index 0a69ff34..1eab1dde 100644 --- a/tests/helpers/init-project.test.ts +++ b/tests/helpers/init-project.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, beforeEach, afterEach } from "bun:test"; import { mkdtempSync, rmSync, existsSync, mkdirSync } from "node:fs"; import { tmpdir } from "node:os"; diff --git a/tests/helpers/install-info.test.ts b/tests/helpers/install-info.test.ts index c78e803b..057b6bb4 100644 --- a/tests/helpers/install-info.test.ts +++ b/tests/helpers/install-info.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, afterEach } from "bun:test"; import { diff --git a/tests/helpers/log.test.ts b/tests/helpers/log.test.ts index 5372d13b..afefbbf3 100644 --- a/tests/helpers/log.test.ts +++ b/tests/helpers/log.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, beforeEach, afterEach, spyOn } from "bun:test"; import { logDebug, logInfo, logError, logWarn } from "../../src/helpers/log"; diff --git a/tests/helpers/login-flow.test.ts b/tests/helpers/login-flow.test.ts index 67bd2ba3..975d3a79 100644 --- a/tests/helpers/login-flow.test.ts +++ b/tests/helpers/login-flow.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test } from "bun:test"; import { runLoginFlow } from "../../src/helpers/login-flow"; diff --git a/tests/helpers/output.test.ts b/tests/helpers/output.test.ts index b1f133be..b05fffcb 100644 --- a/tests/helpers/output.test.ts +++ b/tests/helpers/output.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, beforeEach, afterEach } from "bun:test"; import { isAgentContext, formatJSON } from "../../src/helpers/output"; diff --git a/tests/helpers/paths.test.ts b/tests/helpers/paths.test.ts index 2e24e35d..798ba1d9 100644 --- a/tests/helpers/paths.test.ts +++ b/tests/helpers/paths.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, beforeEach, afterEach } from "bun:test"; import { mkdtempSync, rmSync, mkdirSync } from "node:fs"; import { tmpdir } from "node:os"; diff --git a/tests/helpers/platform.test.ts b/tests/helpers/platform.test.ts index 05d3a3df..c7944001 100644 --- a/tests/helpers/platform.test.ts +++ b/tests/helpers/platform.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, beforeEach, afterEach } from "bun:test"; import { diff --git a/tests/helpers/plugin-install.test.ts b/tests/helpers/plugin-install.test.ts index 709a02d6..1a314a67 100644 --- a/tests/helpers/plugin-install.test.ts +++ b/tests/helpers/plugin-install.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test } from "bun:test"; import { diff --git a/tests/helpers/project-config.test.ts b/tests/helpers/project-config.test.ts index 7a6159b1..056e9983 100644 --- a/tests/helpers/project-config.test.ts +++ b/tests/helpers/project-config.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, beforeEach, afterEach } from "bun:test"; import { existsSync, mkdtempSync, mkdirSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; diff --git a/tests/helpers/repo-probe.test.ts b/tests/helpers/repo-probe.test.ts index 26e6d8c3..cbc63b41 100644 --- a/tests/helpers/repo-probe.test.ts +++ b/tests/helpers/repo-probe.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, afterEach, beforeEach } from "bun:test"; import { diff --git a/tests/helpers/repo.test.ts b/tests/helpers/repo.test.ts index f4dffa3a..785ff5d1 100644 --- a/tests/helpers/repo.test.ts +++ b/tests/helpers/repo.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, afterEach, beforeEach } from "bun:test"; import { mkdtempSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; diff --git a/tests/helpers/rules-shim.test.ts b/tests/helpers/rules-shim.test.ts index 92739a4a..87f4fbb6 100644 --- a/tests/helpers/rules-shim.test.ts +++ b/tests/helpers/rules-shim.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, beforeEach, afterEach } from "bun:test"; import { mkdtempSync, rmSync, mkdirSync, existsSync } from "node:fs"; import { tmpdir } from "node:os"; diff --git a/tests/helpers/sentry.test.ts b/tests/helpers/sentry.test.ts index 78336e4b..1f5c8e32 100644 --- a/tests/helpers/sentry.test.ts +++ b/tests/helpers/sentry.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, test, beforeEach, afterEach, mock } from "bun:test"; import { mkdtempSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; diff --git a/tests/helpers/session-context-copilot.test.ts b/tests/helpers/session-context-copilot.test.ts index 8fc8f0e8..d35abd99 100644 --- a/tests/helpers/session-context-copilot.test.ts +++ b/tests/helpers/session-context-copilot.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { afterEach, beforeEach, describe, expect, test } from "bun:test"; import { mkdirSync, rmSync, writeFileSync } from "node:fs"; import { homedir } from "node:os"; diff --git a/tests/helpers/session-context-cursor.test.ts b/tests/helpers/session-context-cursor.test.ts index 95f4d1d1..5493d194 100644 --- a/tests/helpers/session-context-cursor.test.ts +++ b/tests/helpers/session-context-cursor.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { afterEach, beforeEach, describe, expect, test } from "bun:test"; import { mkdirSync, rmSync, writeFileSync } from "node:fs"; import { homedir } from "node:os"; diff --git a/tests/helpers/session-context-opencode.test.ts b/tests/helpers/session-context-opencode.test.ts index 816d7b18..54c4e37c 100644 --- a/tests/helpers/session-context-opencode.test.ts +++ b/tests/helpers/session-context-opencode.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { Database } from "bun:sqlite"; import { afterEach, beforeEach, describe, expect, test } from "bun:test"; import { mkdirSync, rmSync } from "node:fs"; diff --git a/tests/helpers/session-context.test.ts b/tests/helpers/session-context.test.ts index 31cb2985..78fd87a0 100644 --- a/tests/helpers/session-context.test.ts +++ b/tests/helpers/session-context.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { afterEach, beforeEach, describe, expect, test } from "bun:test"; import { mkdirSync, rmSync, writeFileSync } from "node:fs"; import { homedir } from "node:os"; diff --git a/tests/helpers/signup.test.ts b/tests/helpers/signup.test.ts index baf28b97..e34fbaa1 100644 --- a/tests/helpers/signup.test.ts +++ b/tests/helpers/signup.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, mock } from "bun:test"; import { diff --git a/tests/helpers/telemetry-config.test.ts b/tests/helpers/telemetry-config.test.ts index 966d0fb1..f3f1fc80 100644 --- a/tests/helpers/telemetry-config.test.ts +++ b/tests/helpers/telemetry-config.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, beforeEach, afterEach } from "bun:test"; import { mkdtempSync, rmSync, readFileSync } from "node:fs"; import { tmpdir } from "node:os"; diff --git a/tests/helpers/telemetry.test.ts b/tests/helpers/telemetry.test.ts index 556e542c..2be1cd05 100644 --- a/tests/helpers/telemetry.test.ts +++ b/tests/helpers/telemetry.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, beforeEach, afterEach } from "bun:test"; import { mkdtempSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; diff --git a/tests/helpers/tls.test.ts b/tests/helpers/tls.test.ts index 9f0c097d..09a767cd 100644 --- a/tests/helpers/tls.test.ts +++ b/tests/helpers/tls.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test } from "bun:test"; import { isTlsError, tlsHintMessage } from "../../src/helpers/tls"; diff --git a/tests/helpers/update-check.test.ts b/tests/helpers/update-check.test.ts index d011d690..88c31753 100644 --- a/tests/helpers/update-check.test.ts +++ b/tests/helpers/update-check.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, beforeEach, afterEach, mock } from "bun:test"; import { mkdtempSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; diff --git a/tests/helpers/vscode-settings.test.ts b/tests/helpers/vscode-settings.test.ts index 5cbf1497..baf5f539 100644 --- a/tests/helpers/vscode-settings.test.ts +++ b/tests/helpers/vscode-settings.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, beforeEach, afterEach } from "bun:test"; import { mkdtempSync, rmSync, existsSync, mkdirSync } from "node:fs"; import { homedir } from "node:os"; diff --git a/tests/integration/adr-show-update.test.ts b/tests/integration/adr-show-update.test.ts index dac1b76a..34bc6bb4 100644 --- a/tests/integration/adr-show-update.test.ts +++ b/tests/integration/adr-show-update.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, beforeEach, afterEach } from "bun:test"; import { join } from "node:path"; diff --git a/tests/integration/adr.test.ts b/tests/integration/adr.test.ts index 90fb14d3..fe73274e 100644 --- a/tests/integration/adr.test.ts +++ b/tests/integration/adr.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, beforeEach, afterEach } from "bun:test"; import { readdirSync } from "node:fs"; import { join } from "node:path"; diff --git a/tests/integration/check.test.ts b/tests/integration/check.test.ts index 841f0f31..1b439a38 100644 --- a/tests/integration/check.test.ts +++ b/tests/integration/check.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, beforeEach, afterEach } from "bun:test"; import { mkdirSync, writeFileSync } from "node:fs"; import { join } from "node:path"; diff --git a/tests/integration/clean.test.ts b/tests/integration/clean.test.ts index 267ffab1..bb5ee319 100644 --- a/tests/integration/clean.test.ts +++ b/tests/integration/clean.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, beforeEach, afterEach } from "bun:test"; import { mkdirSync, writeFileSync } from "node:fs"; import { existsSync } from "node:fs"; diff --git a/tests/integration/cli-harness.ts b/tests/integration/cli-harness.ts index 50f579b6..dbfcd6e4 100644 --- a/tests/integration/cli-harness.ts +++ b/tests/integration/cli-harness.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate /** * Integration test harness — runs the real CLI via Bun.spawn * against isolated temp project directories. diff --git a/tests/integration/cli-perf.test.ts b/tests/integration/cli-perf.test.ts index ad6a31eb..65506660 100644 --- a/tests/integration/cli-perf.test.ts +++ b/tests/integration/cli-perf.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate /** * Performance regression tests for CLI startup and exit latency. * diff --git a/tests/integration/init.test.ts b/tests/integration/init.test.ts index 4e2d8a21..a7722b90 100644 --- a/tests/integration/init.test.ts +++ b/tests/integration/init.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, beforeEach, afterEach } from "bun:test"; import { existsSync, mkdirSync, readdirSync, writeFileSync } from "node:fs"; import { join } from "node:path"; diff --git a/tests/integration/review-context.test.ts b/tests/integration/review-context.test.ts index 8409ff8e..8d683379 100644 --- a/tests/integration/review-context.test.ts +++ b/tests/integration/review-context.test.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { describe, expect, test, beforeEach, afterEach } from "bun:test"; import { mkdirSync, writeFileSync } from "node:fs"; import { mkdtempSync } from "node:fs"; diff --git a/tests/preload.ts b/tests/preload.ts index e98b5c5a..a21e8c41 100644 --- a/tests/preload.ts +++ b/tests/preload.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate /** * Test preload — sets environment for all test runs. * diff --git a/tests/test-utils.ts b/tests/test-utils.ts index d53483a0..cfa78f66 100644 --- a/tests/test-utils.ts +++ b/tests/test-utils.ts @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate import { rmSync } from "node:fs"; /** From 82ed06195fa5ca799f24f7b5c9514e2eac402ebf Mon Sep 17 00:00:00 2001 From: rhuanbarreto <283004+rhuanbarreto@users.noreply.github.com> Date: Sun, 10 May 2026 20:27:14 +0000 Subject: [PATCH 02/16] docs: regenerate llms-full.txt Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- docs/public/llms-full.txt | 53 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 4 deletions(-) diff --git a/docs/public/llms-full.txt b/docs/public/llms-full.txt index 2958048b..97ff806a 100644 --- a/docs/public/llms-full.txt +++ b/docs/public/llms-full.txt @@ -4039,15 +4039,31 @@ For the full Archgate Privacy Policy, please visit: The sections below summarize the most relevant points for CLI users. The canonical policy on the website is the legally binding version. +**Data controller:** Dasolve AS (Org.nr 936 035 019), Lillogata 5P, 0484 Oslo, Norway. Contact: [privacy@archgate.dev](mailto:privacy@archgate.dev). + ## Summary for CLI users ### What the CLI collects Archgate collects **anonymous usage analytics** (via PostHog) and **crash reports** (via Sentry) to improve the tool. No personal information, source code, file content, or AI prompts are ever collected by the CLI itself. See the [Telemetry](/reference/telemetry/) page for a detailed breakdown of every data point. +**Legal basis:** Legitimate interest under GDPR Article 6(1)(f). See our [Legitimate Interest Assessment](https://archgate.dev/legitimate-interest-assessment). + +### Data storage and retention + +| Service | Data | Region | Retention | +| ------------- | ---------------------------- | -------------- | ------------------------ | +| PostHog Cloud | Anonymous usage analytics | EU (Frankfurt) | 1 year | +| Sentry Cloud | Crash reports | EU (Frankfurt) | 90 days | +| Turso | Plugins Service account data | EU | Until deletion requested | + +Data is transmitted via reverse proxies at `n.archgate.dev` (analytics) and `s.archgate.dev` (errors) — transparent forwarders operated by Dasolve AS on Cloudflare, with no logging or storage. + ### Archgate Plugins Service -When you sign up via `archgate login`, the **Plugins Service** (`plugins.archgate.dev`) collects personal information: your **email address**, **GitHub username**, **editor choice**, and a **use case description**. This data is used to provision your account and send a welcome email. Authentication tokens are stored as SHA-256 hashes on our servers; on your machine, credentials are kept in your OS credential manager (never as plain-text files). See the [full privacy policy](https://archgate.dev/privacy-policy) for complete details. +When you sign up via `archgate login`, the **Plugins Service** (`plugins.archgate.dev`) collects personal information: your **email address**, **GitHub username**, **editor choice**, and a **use case description**. This data is used to provision your account and send a welcome email. Authentication tokens are stored as SHA-256 hashes on our servers; on your machine, credentials are kept in your OS credential manager (never as plain-text files). + +By creating an account, you agree to the [Terms of Service](https://archgate.dev/terms-of-service). See the [full privacy policy](https://archgate.dev/privacy-policy) for complete details. ### How to opt out @@ -4061,10 +4077,20 @@ archgate telemetry disable Note: telemetry opt-out disables CLI analytics and crash reporting. It does not affect the Plugins Service account data, which is required for plugin access. To delete your account data, contact [privacy@archgate.dev](mailto:privacy@archgate.dev). +### Your rights + +- **Access:** Request a copy of your data — email [privacy@archgate.dev](mailto:privacy@archgate.dev) with your install ID (from `~/.archgate/config.json` or `archgate telemetry status`). +- **Erasure:** Request deletion of historical data — email with your install ID. Completed within 30 days. +- **Object:** Disable telemetry at any time via `archgate telemetry disable`. +- **Portability:** Request data export in JSON or CSV format. +- **Complaint:** Contact the Norwegian Data Protection Authority ([Datatilsynet](https://www.datatilsynet.no)). + ### What the docs site collects This documentation site (`cli.archgate.dev`) uses [Cloudflare Web Analytics](https://www.cloudflare.com/web-analytics/), a privacy-first analytics service. Cloudflare Web Analytics does not use cookies, does not track individual visitors, and does not collect personal information. It provides aggregate page-view and performance metrics only. +PostHog is also used on this site with **cookieless, memory-only tracking** (`persistence: "memory"`). No cookies are set, no localStorage is written, and no data persists between page loads. + --- ## Reference: Rule API @@ -4492,11 +4518,30 @@ archgate telemetry status The environment variable takes precedence over the CLI setting. If `ARCHGATE_TELEMETRY=0` is set, telemetry is disabled regardless of the CLI config. +## Legal basis + +Archgate CLI telemetry operates on an **opt-out basis** under GDPR Article 6(1)(f) — legitimate interests of the controller. We have published a formal [Legitimate Interest Assessment](https://archgate.dev/legitimate-interest-assessment) documenting why this is proportionate and lawful. + +In summary: the data is anonymous (random UUID, no PII), the impact on users is minimal, robust safeguards are in place (IP anonymization, EU storage, limited retention, transparency), and users retain full control via an easy, permanent opt-out. + ## Where data is stored -- **Analytics**: PostHog Cloud (EU region). Data retained per PostHog's standard retention policy. -- **Error tracking**: Sentry Cloud (EU region). Error events retained for 90 days. -- **Local config**: `~/.archgate/config.json` stores your telemetry preference and anonymous install ID. +| Service | Data | Region | Retention | +| ------------- | --------------------------------- | -------------- | ------------------- | +| PostHog Cloud | Anonymous usage analytics | EU (Frankfurt) | 1 year | +| Sentry Cloud | Crash reports | EU (Frankfurt) | 90 days | +| Local config | Telemetry preference + install ID | Your machine | Until you delete it | + +Analytics events are routed through `n.archgate.dev` and error reports through `s.archgate.dev`. These are transparent reverse proxies operated by Dasolve AS on Cloudflare infrastructure — they forward requests without logging, storing, or inspecting payloads. + +## Your rights + +- **Right of access**: Request a copy of all data associated with your install ID. Email [privacy@archgate.dev](mailto:privacy@archgate.dev) with your install ID (found via `archgate telemetry status` or in `~/.archgate/config.json`). Response within 30 days. +- **Right to erasure**: Request deletion of historical analytics and crash data. Disabling telemetry stops future collection but does not delete past events. Email [privacy@archgate.dev](mailto:privacy@archgate.dev) with your install ID for deletion. +- **Right to object**: Disable telemetry at any time via `archgate telemetry disable` or `ARCHGATE_TELEMETRY=0`. +- **Right to lodge a complaint**: Contact the Norwegian Data Protection Authority ([Datatilsynet](https://www.datatilsynet.no)). + +**Data controller:** Dasolve AS (Org.nr 936 035 019), Lillogata 5P, 0484 Oslo, Norway. Contact: [privacy@archgate.dev](mailto:privacy@archgate.dev). ## Open source From ceedfce3715d29b3a6623301799ef30e98471cc6 Mon Sep 17 00:00:00 2001 From: Rhuan Barreto Date: Sun, 10 May 2026 22:41:24 +0200 Subject: [PATCH 03/16] docs: add LGPD references to telemetry documentation - Cite LGPD Art. 7, IX c/c Art. 10 alongside GDPR Art. 6(1)(f) as legal basis - Add ANPD as complaint authority for Brazilian users - Link to PT-BR privacy policy for full LGPD rights (Art. 18) Signed-off-by: Rhuan Barreto --- docs/src/content/docs/reference/telemetry.mdx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/src/content/docs/reference/telemetry.mdx b/docs/src/content/docs/reference/telemetry.mdx index 776fdcdf..dab0338f 100644 --- a/docs/src/content/docs/reference/telemetry.mdx +++ b/docs/src/content/docs/reference/telemetry.mdx @@ -114,7 +114,7 @@ The environment variable takes precedence over the CLI setting. If `ARCHGATE_TEL ## Legal basis -Archgate CLI telemetry operates on an **opt-out basis** under GDPR Article 6(1)(f) — legitimate interests of the controller. We have published a formal [Legitimate Interest Assessment](https://archgate.dev/legitimate-interest-assessment) documenting why this is proportionate and lawful. +Archgate CLI telemetry operates on an **opt-out basis** under GDPR Article 6(1)(f) and LGPD Article 7, IX c/c Article 10 — legitimate interests of the controller. We have published a formal [Legitimate Interest Assessment](https://archgate.dev/legitimate-interest-assessment) documenting why this is proportionate and lawful. In summary: the data is anonymous (random UUID, no PII), the impact on users is minimal, robust safeguards are in place (IP anonymization, EU storage, limited retention, transparency), and users retain full control via an easy, permanent opt-out. @@ -133,10 +133,12 @@ Analytics events are routed through `n.archgate.dev` and error reports through ` - **Right of access**: Request a copy of all data associated with your install ID. Email [privacy@archgate.dev](mailto:privacy@archgate.dev) with your install ID (found via `archgate telemetry status` or in `~/.archgate/config.json`). Response within 30 days. - **Right to erasure**: Request deletion of historical analytics and crash data. Disabling telemetry stops future collection but does not delete past events. Email [privacy@archgate.dev](mailto:privacy@archgate.dev) with your install ID for deletion. - **Right to object**: Disable telemetry at any time via `archgate telemetry disable` or `ARCHGATE_TELEMETRY=0`. -- **Right to lodge a complaint**: Contact the Norwegian Data Protection Authority ([Datatilsynet](https://www.datatilsynet.no)). +- **Right to lodge a complaint**: Contact the Norwegian Data Protection Authority ([Datatilsynet](https://www.datatilsynet.no)) or, for Brazilian users, the ANPD ([www.gov.br/anpd](https://www.gov.br/anpd)). **Data controller:** Dasolve AS (Org.nr 936 035 019), Lillogata 5P, 0484 Oslo, Norway. Contact: [privacy@archgate.dev](mailto:privacy@archgate.dev). +**Brazilian users (LGPD):** For LGPD-specific rights (Art. 18), international transfer details (Art. 33), and ANPD contact information, see the [Portuguese privacy policy](https://archgate.dev/pt-br/privacy-policy). + ## Open source The telemetry implementation is fully open source. You can inspect exactly what data is collected by reading: From 8ae4e156ab5fedc566f32a58b3a6f007aaa15703 Mon Sep 17 00:00:00 2001 From: rhuanbarreto <283004+rhuanbarreto@users.noreply.github.com> Date: Sun, 10 May 2026 20:42:32 +0000 Subject: [PATCH 04/16] docs: regenerate llms-full.txt Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- docs/public/llms-full.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/public/llms-full.txt b/docs/public/llms-full.txt index 97ff806a..49347f74 100644 --- a/docs/public/llms-full.txt +++ b/docs/public/llms-full.txt @@ -4520,7 +4520,7 @@ The environment variable takes precedence over the CLI setting. If `ARCHGATE_TEL ## Legal basis -Archgate CLI telemetry operates on an **opt-out basis** under GDPR Article 6(1)(f) — legitimate interests of the controller. We have published a formal [Legitimate Interest Assessment](https://archgate.dev/legitimate-interest-assessment) documenting why this is proportionate and lawful. +Archgate CLI telemetry operates on an **opt-out basis** under GDPR Article 6(1)(f) and LGPD Article 7, IX c/c Article 10 — legitimate interests of the controller. We have published a formal [Legitimate Interest Assessment](https://archgate.dev/legitimate-interest-assessment) documenting why this is proportionate and lawful. In summary: the data is anonymous (random UUID, no PII), the impact on users is minimal, robust safeguards are in place (IP anonymization, EU storage, limited retention, transparency), and users retain full control via an easy, permanent opt-out. @@ -4539,10 +4539,12 @@ Analytics events are routed through `n.archgate.dev` and error reports through ` - **Right of access**: Request a copy of all data associated with your install ID. Email [privacy@archgate.dev](mailto:privacy@archgate.dev) with your install ID (found via `archgate telemetry status` or in `~/.archgate/config.json`). Response within 30 days. - **Right to erasure**: Request deletion of historical analytics and crash data. Disabling telemetry stops future collection but does not delete past events. Email [privacy@archgate.dev](mailto:privacy@archgate.dev) with your install ID for deletion. - **Right to object**: Disable telemetry at any time via `archgate telemetry disable` or `ARCHGATE_TELEMETRY=0`. -- **Right to lodge a complaint**: Contact the Norwegian Data Protection Authority ([Datatilsynet](https://www.datatilsynet.no)). +- **Right to lodge a complaint**: Contact the Norwegian Data Protection Authority ([Datatilsynet](https://www.datatilsynet.no)) or, for Brazilian users, the ANPD ([www.gov.br/anpd](https://www.gov.br/anpd)). **Data controller:** Dasolve AS (Org.nr 936 035 019), Lillogata 5P, 0484 Oslo, Norway. Contact: [privacy@archgate.dev](mailto:privacy@archgate.dev). +**Brazilian users (LGPD):** For LGPD-specific rights (Art. 18), international transfer details (Art. 33), and ANPD contact information, see the [Portuguese privacy policy](https://archgate.dev/pt-br/privacy-policy). + ## Open source The telemetry implementation is fully open source. You can inspect exactly what data is collected by reading: From 1a738a6f0c4a3d9b85cc7a8bf3d9d68fbc867c78 Mon Sep 17 00:00:00 2001 From: Rhuan Barreto Date: Sun, 10 May 2026 23:05:29 +0200 Subject: [PATCH 05/16] feat(adrs): add LEGAL-001 (SPDX headers) and LEGAL-002 (license compatibility) ADRs with rules - LEGAL-001: Enforces SPDX-License-Identifier header in all src/ and tests/ .ts files - LEGAL-002: Verifies all dependencies use Apache-2.0-compatible licenses Both ADRs include companion .rules.ts files for automated CI enforcement. The `legal` domain now governs open-source licensing compliance. Signed-off-by: Rhuan Barreto --- .../adrs/LEGAL-001-spdx-license-headers.md | 89 +++++++++++++++++ .../LEGAL-001-spdx-license-headers.rules.ts | 38 +++++++ ...AL-002-dependency-license-compatibility.md | 98 +++++++++++++++++++ ...-dependency-license-compatibility.rules.ts | 85 ++++++++++++++++ 4 files changed, 310 insertions(+) create mode 100644 .archgate/adrs/LEGAL-001-spdx-license-headers.md create mode 100644 .archgate/adrs/LEGAL-001-spdx-license-headers.rules.ts create mode 100644 .archgate/adrs/LEGAL-002-dependency-license-compatibility.md create mode 100644 .archgate/adrs/LEGAL-002-dependency-license-compatibility.rules.ts diff --git a/.archgate/adrs/LEGAL-001-spdx-license-headers.md b/.archgate/adrs/LEGAL-001-spdx-license-headers.md new file mode 100644 index 00000000..ffa2cebe --- /dev/null +++ b/.archgate/adrs/LEGAL-001-spdx-license-headers.md @@ -0,0 +1,89 @@ +--- +id: LEGAL-001 +title: SPDX License Headers +domain: legal +rules: true +files: ["src/**/*.ts", "tests/**/*.ts"] +--- + +## Context + +The Archgate CLI is licensed under Apache-2.0. SPDX (Software Package Data Exchange) license identifiers provide a machine-readable, unambiguous way to declare the license of each source file. This eliminates ambiguity about which license applies to any given file and enables automated compliance tooling to verify license declarations at scale. + +Without per-file license identifiers, downstream consumers (enterprises, redistributors, compliance scanners) must infer the license from the root LICENSE.md file — a fragile assumption that breaks when files are extracted, copied, or bundled outside the original repository context. + +**Alternatives considered:** + +- **Root LICENSE.md only** — Covers the project as a whole but provides no per-file signal. Files extracted in isolation (e.g., copy-pasted utilities, bundled snippets) lose their license provenance. +- **Full license header blocks** — Includes the entire license notice in every file. Verbose, creates noise, and adds maintenance burden if the copyright year or holder changes. +- **REUSE 3.0 specification** — A comprehensive approach using `.reuse/dep5` files for bulk declarations. More complex than needed for a single-license project where all files share the same license. + +SPDX-License-Identifier comments are the lightest-weight solution that provides per-file legal clarity, is recognized by all major compliance scanners (FOSSA, Snyk, Black Duck, npm license-checker), and is trivial to maintain. + +## Decision + +Every TypeScript source file in `src/` and `tests/` must begin with an SPDX license identifier and copyright notice: + +```typescript +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate +``` + +For files with a shebang line (`#!/usr/bin/env bun`), the SPDX header must appear immediately after the shebang: + +```typescript +#!/usr/bin/env bun +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate +``` + +## Do's and Don'ts + +### Do + +- Add the SPDX header as the first two lines of every new `.ts` file in `src/` or `tests/` +- Place the header after the shebang line when one is present (only `src/cli.ts`) +- Use the exact format: `// SPDX-License-Identifier: Apache-2.0` followed by `// Copyright 2026 Archgate` +- Run `bun run scripts/add-spdx-headers.ts` to bulk-add headers to new files + +### Don't + +- Don't use block comments (`/* */`) for the SPDX header — scanners expect `//` single-line format +- Don't add SPDX headers to non-TypeScript files (JSON, YAML, Markdown) — they are covered by the root LICENSE.md +- Don't modify the copyright year without a project-wide decision +- Don't add additional license identifiers (e.g., dual licensing) without updating this ADR + +## Consequences + +### Positive + +- **Unambiguous per-file licensing** — Every source file declares its license in a machine-readable format +- **Compliance scanner compatibility** — FOSSA, Snyk, and npm license-checker recognize SPDX identifiers automatically +- **Downstream safety** — Files extracted or bundled independently retain their license provenance +- **Apache-2.0 Section 4(a) alignment** — "You must give any other recipients of the Work a copy of this License" — per-file identifiers serve as a lightweight form of notice + +### Negative + +- **Two extra lines per file** — Minor visual noise; mitigated by being at the very top (above imports) +- **Year maintenance** — If copyright year changes, all files need updating (mitigated by the bulk script) + +### Risks + +- **Developers forget on new files** — The `LEGAL-001/spdx-header-present` rule catches this automatically in CI. + - **Mitigation:** Automated rule blocks PRs that add `.ts` files without the header. + +## Compliance and Enforcement + +### Automated Enforcement + +- **Archgate rule** `LEGAL-001/spdx-header-present`: Scans all scoped `.ts` files and verifies the SPDX identifier is present in the first 3 lines. Severity: `error` (hard blocker). + +### Manual Enforcement + +None required — fully automated. + +## References + +- [SPDX License List](https://spdx.org/licenses/) +- [REUSE Software recommendations](https://reuse.software/spec/) +- [Apache-2.0 License, Section 4](https://www.apache.org/licenses/LICENSE-2.0#redistribution) diff --git a/.archgate/adrs/LEGAL-001-spdx-license-headers.rules.ts b/.archgate/adrs/LEGAL-001-spdx-license-headers.rules.ts new file mode 100644 index 00000000..db3acf0b --- /dev/null +++ b/.archgate/adrs/LEGAL-001-spdx-license-headers.rules.ts @@ -0,0 +1,38 @@ +/// + +export default { + rules: { + "spdx-header-present": { + description: + "All TypeScript source files must have an SPDX-License-Identifier header", + async check(ctx) { + const results = await Promise.all( + ctx.scopedFiles.map(async (file) => { + const content = await ctx.readFile(file); + return { file, content }; + }) + ); + + for (const { file, content } of results) { + if (content === null) continue; + + // Check first 5 lines for the SPDX identifier (allows for shebang) + const lines = content.split("\n").slice(0, 5); + const hasSpdx = lines.some((line) => + line.includes("SPDX-License-Identifier: Apache-2.0") + ); + + if (!hasSpdx) { + ctx.report.violation({ + message: + "Missing SPDX-License-Identifier header. Add `// SPDX-License-Identifier: Apache-2.0` as the first line.", + file, + line: 1, + fix: 'Add "// SPDX-License-Identifier: Apache-2.0\\n// Copyright 2026 Archgate\\n" at the top of the file', + }); + } + } + }, + }, + }, +} satisfies RuleSet; diff --git a/.archgate/adrs/LEGAL-002-dependency-license-compatibility.md b/.archgate/adrs/LEGAL-002-dependency-license-compatibility.md new file mode 100644 index 00000000..9c8962ff --- /dev/null +++ b/.archgate/adrs/LEGAL-002-dependency-license-compatibility.md @@ -0,0 +1,98 @@ +--- +id: LEGAL-002 +title: Dependency License Compatibility +domain: legal +rules: true +files: ["package.json"] +--- + +## Context + +The Archgate CLI is licensed under Apache-2.0, a permissive open-source license. When the CLI is compiled into a binary via `bun build --compile`, all runtime dependencies are bundled into the executable. This means the binary is a combined work that must comply with the license terms of every bundled dependency. + +Copyleft licenses (GPL, AGPL, LGPL) impose "share-alike" requirements that could force the entire CLI to be distributed under copyleft terms — incompatible with the project's Apache-2.0 licensing. Even for devDependencies (which are not bundled), copyleft test frameworks or build tools could create legal ambiguity about the project's overall license posture. + +**Alternatives considered:** + +- **No automated checking** — Rely on manual review during dependency additions. Error-prone; a single copyleft transitive dependency could slip in unnoticed. +- **FOSSA or Snyk integration** — Third-party SaaS license scanners. Adds external dependency, cost, and requires API tokens in CI. Overkill for a project with only 3 production dependencies. +- **npm license-checker package** — Adds a devDependency for something achievable with a simple script. Counter to ARCH-006 (minimize dependencies). + +A lightweight, self-contained script that reads `node_modules/*/package.json` license fields provides the same coverage without external dependencies or API tokens. + +## Decision + +All dependencies (production and development) must use licenses compatible with Apache-2.0. The project maintains an allowlist of approved permissive licenses: + +| License | SPDX Identifier | +| ---------------------------- | -------------------- | +| MIT License | MIT | +| Apache License 2.0 | Apache-2.0 | +| ISC License | ISC | +| BSD 2-Clause | BSD-2-Clause | +| BSD 3-Clause | BSD-3-Clause | +| Zero-Clause BSD | 0BSD | +| Creative Commons Zero | CC0-1.0 | +| The Unlicense | Unlicense | +| Blue Oak Model License | BlueOak-1.0.0 | +| Creative Commons Attribution | CC-BY-4.0, CC-BY-3.0 | +| Python Software Foundation | Python-2.0 | + +SPDX OR expressions (e.g., `MIT OR Apache-2.0`) are allowed if at least one alternative is on the allowlist. + +**Prohibited licenses include:** GPL-2.0, GPL-3.0, AGPL-3.0, LGPL-2.1, LGPL-3.0, SSPL-1.0, and any other copyleft or source-available license. + +## Do's and Don'ts + +### Do + +- Run `bun run license:check` before adding any new dependency +- Verify transitive dependencies — a permissively-licensed package may pull in a copyleft transitive +- Add newly-encountered permissive licenses to the allowlist in `scripts/check-licenses.ts` with a comment +- Prefer dependencies with clear SPDX license identifiers in their `package.json` + +### Don't + +- Don't add dependencies with GPL, AGPL, LGPL, or SSPL licenses +- Don't add dependencies with no license field (`UNLICENSED` or missing) — these are "all rights reserved" by default +- Don't assume a package is permissive based on its README — always check the `license` field in `package.json` +- Don't add packages with `Custom` or proprietary license fields without explicit legal review + +## Consequences + +### Positive + +- **Legal certainty** — Every dependency in the compiled binary is confirmed Apache-2.0-compatible +- **Distribution safety** — Users, enterprises, and downstream redistributors can rely on the project's Apache-2.0 license without hidden copyleft obligations +- **Automated enforcement** — License violations are caught in CI before merge + +### Negative + +- **May reject useful packages** — Some high-quality libraries use copyleft licenses (e.g., readline-sync is GPL). These cannot be used regardless of utility. +- **Allowlist maintenance** — Rare or exotic permissive licenses require manual addition to the allowlist + +### Risks + +- **Transitive dependency license change** — A previously-permissive dependency may change its license in a new version (e.g., the Elasticsearch SSPL relicensing). + - **Mitigation:** `bun run license:check` runs on the installed `node_modules` tree, catching license changes on any version update. +- **Missing license field in package.json** — Some packages declare their license only in a LICENSE file, not in the `license` field. The scanner may flag these as "no license." + - **Mitigation:** If a package is clearly permissive (LICENSE file exists) but lacks a `package.json` license field, add it to the allowlist with a comment explaining the override. + +## Compliance and Enforcement + +### Automated Enforcement + +- **Archgate rule** `LEGAL-002/no-copyleft-deps`: Reads `package.json` devDependencies and dependencies, cross-references against the allowlist in `scripts/check-licenses.ts`. Severity: `error` (hard blocker). +- **Script**: `bun run license:check` — scans all installed `node_modules` packages for comprehensive coverage including transitives. + +### Manual Enforcement + +- Dependency additions in PRs should include a note confirming license compatibility +- The `bun run license:check` script should be run after any `bun install` or lockfile update + +## References + +- [SPDX License List](https://spdx.org/licenses/) +- [Apache-2.0 License Compatibility](https://www.apache.org/legal/resolved.html) +- [ARCH-006 — Dependency Policy](./ARCH-006-dependency-policy.md) — Governs which dependencies are allowed; this ADR governs their license compatibility +- [`scripts/check-licenses.ts`](../../scripts/check-licenses.ts) — The license scanner implementation diff --git a/.archgate/adrs/LEGAL-002-dependency-license-compatibility.rules.ts b/.archgate/adrs/LEGAL-002-dependency-license-compatibility.rules.ts new file mode 100644 index 00000000..de78190a --- /dev/null +++ b/.archgate/adrs/LEGAL-002-dependency-license-compatibility.rules.ts @@ -0,0 +1,85 @@ +/// + +const ALLOWED_LICENSES = new Set([ + "MIT", + "Apache-2.0", + "ISC", + "BSD-2-Clause", + "BSD-3-Clause", + "0BSD", + "CC0-1.0", + "Unlicense", + "BlueOak-1.0.0", + "CC-BY-4.0", + "CC-BY-3.0", + "Python-2.0", + "(MIT OR Apache-2.0)", + "MIT OR Apache-2.0", + "(MIT AND Zlib)", + "(BSD-2-Clause OR MIT OR Apache-2.0)", + "(MIT OR CC0-1.0)", + "BlueOak-1.0.0 OR MIT OR Apache-2.0", +]); + +function isAllowed(license: string | undefined): boolean { + if (!license) return false; + if (ALLOWED_LICENSES.has(license)) return true; + + const normalized = license.trim().replace(/^\(/u, "").replace(/\)$/u, ""); + if (ALLOWED_LICENSES.has(normalized)) return true; + if (ALLOWED_LICENSES.has(`(${normalized})`)) return true; + + // OR expressions: at least one alternative must be allowed + if (normalized.includes(" OR ")) { + return normalized.split(" OR ").some((l) => ALLOWED_LICENSES.has(l.trim())); + } + + return false; +} + +export default { + rules: { + "no-copyleft-deps": { + description: + "All dependencies must use Apache-2.0-compatible (permissive) licenses", + async check(ctx) { + let pkg; + try { + pkg = await ctx.readJSON("package.json"); + } catch { + return; + } + + const allDeps = [ + ...Object.keys(pkg.dependencies ?? {}), + ...Object.keys(pkg.devDependencies ?? {}), + ]; + + const depResults = await Promise.all( + allDeps.map(async (dep) => { + try { + const depPkg = await ctx.readJSON( + `node_modules/${dep}/package.json` + ); + return { dep, license: depPkg.license as string | undefined }; + } catch { + // Scoped packages or missing — skip (covered by full scan) + return null; + } + }) + ); + + for (const result of depResults) { + if (result === null) continue; + if (!isAllowed(result.license)) { + ctx.report.violation({ + message: `Dependency "${result.dep}" has disallowed license: "${result.license ?? "(none)"}". Only permissive licenses (MIT, Apache-2.0, ISC, BSD, etc.) are allowed.`, + file: "package.json", + fix: `Remove "${result.dep}" or find an alternative with a permissive license. Run \`bun run license:check\` for full details.`, + }); + } + } + }, + }, + }, +} satisfies RuleSet; From 822684b4c9ca2028a6cf70800d416598a8ca646f Mon Sep 17 00:00:00 2001 From: Rhuan Barreto Date: Sun, 10 May 2026 23:09:19 +0200 Subject: [PATCH 06/16] docs(examples): add spdx-license-headers and license-compatibility example rules Add reusable example documentation for the two LEGAL-domain rules so other Archgate users can adopt them in their own projects: - spdx-license-headers: enforce per-file SPDX-License-Identifier headers - license-compatibility: verify dependency licenses against a permissive allowlist Both examples include EN + PT-BR translations with customization guidance. Signed-off-by: Rhuan Barreto --- .../docs/examples/license-compatibility.mdx | 127 ++++++++++++++++++ .../docs/examples/spdx-license-headers.mdx | 99 ++++++++++++++ .../pt-br/examples/license-compatibility.mdx | 127 ++++++++++++++++++ .../pt-br/examples/spdx-license-headers.mdx | 99 ++++++++++++++ 4 files changed, 452 insertions(+) create mode 100644 docs/src/content/docs/examples/license-compatibility.mdx create mode 100644 docs/src/content/docs/examples/spdx-license-headers.mdx create mode 100644 docs/src/content/docs/pt-br/examples/license-compatibility.mdx create mode 100644 docs/src/content/docs/pt-br/examples/spdx-license-headers.mdx diff --git a/docs/src/content/docs/examples/license-compatibility.mdx b/docs/src/content/docs/examples/license-compatibility.mdx new file mode 100644 index 00000000..5c54b7ab --- /dev/null +++ b/docs/src/content/docs/examples/license-compatibility.mdx @@ -0,0 +1,127 @@ +--- +title: license-compatibility +description: Verify all dependencies use licenses compatible with your project's license to prevent copyleft contamination. +--- + +Prevent copyleft or incompatible licenses from entering your dependency tree. + +## Rule details + +When you compile or bundle dependencies into your application, their license terms apply to the combined work. A single GPL dependency in a permissive (MIT/Apache-2.0) project can force the entire project to adopt copyleft terms. This rule checks all direct dependencies against a permissive-license allowlist. + +## Examples of **incorrect** code + +```json title="package.json" +{ + "dependencies": { "zod": "^3.23.0" }, + "devDependencies": { "readline-sync": "^1.4.10" } +} +``` + +If `readline-sync` uses GPL-3.0, it triggers a violation even as a devDependency. + +## Examples of **correct** code + +```json title="package.json" +{ + "dependencies": { "zod": "^3.23.0" }, + "devDependencies": { "fast-check": "^4.7.0" } +} +``` + +Both `zod` (MIT) and `fast-check` (MIT) are on the permissive allowlist. + +## Rule implementation + +```typescript +/// + +const ALLOWED_LICENSES = new Set([ + "MIT", + "Apache-2.0", + "ISC", + "BSD-2-Clause", + "BSD-3-Clause", + "0BSD", + "CC0-1.0", + "Unlicense", + "BlueOak-1.0.0", +]); + +function isAllowed(license: string | undefined): boolean { + if (!license) return false; + if (ALLOWED_LICENSES.has(license)) return true; + + // Handle SPDX OR expressions — at least one option must be allowed + const normalized = license.trim().replace(/^\(/u, "").replace(/\)$/u, ""); + if (ALLOWED_LICENSES.has(normalized)) return true; + + if (normalized.includes(" OR ")) { + return normalized.split(" OR ").some((l) => ALLOWED_LICENSES.has(l.trim())); + } + + return false; +} + +export default { + rules: { + "no-copyleft-deps": { + description: "All dependencies must use permissive licenses", + async check(ctx) { + let pkg: { + dependencies?: Record; + devDependencies?: Record; + }; + try { + pkg = (await ctx.readJSON("package.json")) as typeof pkg; + } catch { + return; + } + + const allDeps = [ + ...Object.keys(pkg.dependencies ?? {}), + ...Object.keys(pkg.devDependencies ?? {}), + ]; + + const depResults = await Promise.all( + allDeps.map(async (dep) => { + try { + const depPkg = (await ctx.readJSON( + `node_modules/${dep}/package.json` + )) as { license?: string }; + return { dep, license: depPkg.license }; + } catch { + return null; // Missing or scoped — skip + } + }) + ); + + for (const result of depResults) { + if (result === null) continue; + if (!isAllowed(result.license)) { + ctx.report.violation({ + message: `Dependency "${result.dep}" has disallowed license: "${result.license ?? "(none)"}".`, + file: "package.json", + fix: `Remove "${result.dep}" or find an alternative with a permissive license.`, + }); + } + } + }, + }, + }, +} satisfies RuleSet; +``` + +## Customization + +- **Change the allowlist**: Add or remove license identifiers based on your project's license. GPL projects can allow GPL dependencies; Apache-2.0 projects should block them. +- **Check only production deps**: Remove `devDependencies` from `allDeps` if you only care about bundled/shipped code. +- **Add transitive scanning**: For deeper coverage, scan all `node_modules/*/package.json` instead of only direct dependencies (see a dedicated script approach for this). + +## When to use it + +When your project uses a permissive license (MIT, Apache-2.0, ISC, BSD) and you want to prevent copyleft contamination, especially in compiled/bundled distributions. + +## When not to use it + +In GPL-licensed projects (where copyleft dependencies are compatible), or when you have a dedicated license-scanning SaaS tool (FOSSA, Snyk) that already gates your CI pipeline. diff --git a/docs/src/content/docs/examples/spdx-license-headers.mdx b/docs/src/content/docs/examples/spdx-license-headers.mdx new file mode 100644 index 00000000..3dd1c007 --- /dev/null +++ b/docs/src/content/docs/examples/spdx-license-headers.mdx @@ -0,0 +1,99 @@ +--- +title: spdx-license-headers +description: Enforce SPDX-License-Identifier headers in all source files for per-file license clarity and compliance scanner compatibility. +--- + +Ensure every source file declares its license with a machine-readable SPDX identifier. + +## Rule details + +Open-source projects benefit from per-file license declarations — they survive file extraction, bundling, and copy-paste scenarios where the root LICENSE file is not present. This rule verifies that every TypeScript source file starts with the standard SPDX header comment. + +## Examples of **incorrect** code + +```typescript title="src/helpers/utils.ts" +import { join } from "node:path"; + +export function resolvePath(base: string, rel: string): string { + return join(base, rel); +} +``` + +File is missing the SPDX license identifier header. + +## Examples of **correct** code + +```typescript title="src/helpers/utils.ts" +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate +import { join } from "node:path"; + +export function resolvePath(base: string, rel: string): string { + return join(base, rel); +} +``` + +For files with a shebang: + +```typescript title="src/cli.ts" +#!/usr/bin/env bun +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate +import { Command } from "commander"; +``` + +## Rule implementation + +```typescript +/// + +export default { + rules: { + "spdx-header-present": { + description: + "All TypeScript source files must have an SPDX-License-Identifier header", + async check(ctx) { + const results = await Promise.all( + ctx.scopedFiles.map(async (file) => { + const content = await ctx.readFile(file); + return { file, content }; + }) + ); + + for (const { file, content } of results) { + if (content === null) continue; + + // Check first 5 lines for the SPDX identifier (allows for shebang) + const lines = content.split("\n").slice(0, 5); + const hasSpdx = lines.some((line) => + line.includes("SPDX-License-Identifier: Apache-2.0") + ); + + if (!hasSpdx) { + ctx.report.violation({ + message: "Missing SPDX-License-Identifier header.", + file, + line: 1, + fix: 'Add "// SPDX-License-Identifier: Apache-2.0" as the first line of the file', + }); + } + } + }, + }, + }, +} satisfies RuleSet; +``` + +## Customization + +- **Change the license**: Replace `Apache-2.0` with your project's SPDX identifier (e.g., `MIT`, `BSD-3-Clause`) +- **Change the scope**: Adjust the `files` glob in the ADR frontmatter to match your source directories +- **Add copyright check**: Extend the rule to also verify the copyright line format + +## When to use it + +When your project is open-source and you want unambiguous per-file license declarations that are recognized by compliance scanners (FOSSA, Snyk, Black Duck, npm license-checker). + +## When not to use it + +In proprietary/closed-source projects where all files are implicitly "all rights reserved," or when your organization uses a different license-declaration mechanism like REUSE 3.0 `.dep5` files. diff --git a/docs/src/content/docs/pt-br/examples/license-compatibility.mdx b/docs/src/content/docs/pt-br/examples/license-compatibility.mdx new file mode 100644 index 00000000..2e0cc55d --- /dev/null +++ b/docs/src/content/docs/pt-br/examples/license-compatibility.mdx @@ -0,0 +1,127 @@ +--- +title: license-compatibility +description: Verificar se todas as dependências usam licenças compatíveis com a licença do seu projeto para prevenir contaminação copyleft. +--- + +Previna que licenças copyleft ou incompatíveis entrem na sua árvore de dependências. + +## Detalhes da regra + +Quando você compila ou empacota dependências na sua aplicação, os termos de licença delas se aplicam à obra combinada. Uma única dependência GPL em um projeto permissivo (MIT/Apache-2.0) pode forçar o projeto inteiro a adotar termos copyleft. Esta regra verifica todas as dependências diretas contra uma allowlist de licenças permissivas. + +## Exemplos de código **incorreto** + +```json title="package.json" +{ + "dependencies": { "zod": "^3.23.0" }, + "devDependencies": { "readline-sync": "^1.4.10" } +} +``` + +Se `readline-sync` usa GPL-3.0, dispara uma violação mesmo como devDependency. + +## Exemplos de código **correto** + +```json title="package.json" +{ + "dependencies": { "zod": "^3.23.0" }, + "devDependencies": { "fast-check": "^4.7.0" } +} +``` + +Tanto `zod` (MIT) quanto `fast-check` (MIT) estão na allowlist de licenças permissivas. + +## Implementação da regra + +```typescript +/// + +const ALLOWED_LICENSES = new Set([ + "MIT", + "Apache-2.0", + "ISC", + "BSD-2-Clause", + "BSD-3-Clause", + "0BSD", + "CC0-1.0", + "Unlicense", + "BlueOak-1.0.0", +]); + +function isAllowed(license: string | undefined): boolean { + if (!license) return false; + if (ALLOWED_LICENSES.has(license)) return true; + + // Handle SPDX OR expressions — at least one option must be allowed + const normalized = license.trim().replace(/^\(/u, "").replace(/\)$/u, ""); + if (ALLOWED_LICENSES.has(normalized)) return true; + + if (normalized.includes(" OR ")) { + return normalized.split(" OR ").some((l) => ALLOWED_LICENSES.has(l.trim())); + } + + return false; +} + +export default { + rules: { + "no-copyleft-deps": { + description: "All dependencies must use permissive licenses", + async check(ctx) { + let pkg: { + dependencies?: Record; + devDependencies?: Record; + }; + try { + pkg = (await ctx.readJSON("package.json")) as typeof pkg; + } catch { + return; + } + + const allDeps = [ + ...Object.keys(pkg.dependencies ?? {}), + ...Object.keys(pkg.devDependencies ?? {}), + ]; + + const depResults = await Promise.all( + allDeps.map(async (dep) => { + try { + const depPkg = (await ctx.readJSON( + `node_modules/${dep}/package.json` + )) as { license?: string }; + return { dep, license: depPkg.license }; + } catch { + return null; // Missing or scoped — skip + } + }) + ); + + for (const result of depResults) { + if (result === null) continue; + if (!isAllowed(result.license)) { + ctx.report.violation({ + message: `Dependency "${result.dep}" has disallowed license: "${result.license ?? "(none)"}".`, + file: "package.json", + fix: `Remove "${result.dep}" or find an alternative with a permissive license.`, + }); + } + } + }, + }, + }, +} satisfies RuleSet; +``` + +## Personalização + +- **Alterar a allowlist**: Adicione ou remova identificadores de licença baseado na licença do seu projeto. Projetos GPL podem permitir dependências GPL; projetos Apache-2.0 devem bloqueá-las. +- **Verificar apenas deps de produção**: Remova `devDependencies` de `allDeps` se você só se preocupa com código empacotado/distribuído. +- **Adicionar scan transitivo**: Para cobertura mais profunda, escaneie todos os `node_modules/*/package.json` ao invés de apenas dependências diretas (veja uma abordagem com script dedicado para isso). + +## Quando usar + +Quando seu projeto usa uma licença permissiva (MIT, Apache-2.0, ISC, BSD) e você quer prevenir contaminação copyleft, especialmente em distribuições compiladas/empacotadas. + +## Quando não usar + +Em projetos licenciados sob GPL (onde dependências copyleft são compatíveis), ou quando você tem uma ferramenta SaaS dedicada de scanning de licenças (FOSSA, Snyk) que já bloqueia seu pipeline de CI. diff --git a/docs/src/content/docs/pt-br/examples/spdx-license-headers.mdx b/docs/src/content/docs/pt-br/examples/spdx-license-headers.mdx new file mode 100644 index 00000000..a267ae7e --- /dev/null +++ b/docs/src/content/docs/pt-br/examples/spdx-license-headers.mdx @@ -0,0 +1,99 @@ +--- +title: spdx-license-headers +description: Garantir cabeçalhos SPDX-License-Identifier em todos os arquivos fonte para clareza de licença por arquivo e compatibilidade com scanners de conformidade. +--- + +Garanta que cada arquivo fonte declare sua licença com um identificador SPDX legível por máquina. + +## Detalhes da regra + +Projetos open-source se beneficiam de declarações de licença por arquivo — elas sobrevivem à extração de arquivos, bundling e cenários de cópia onde o arquivo LICENSE raiz não está presente. Esta regra verifica que cada arquivo TypeScript fonte começa com o comentário de cabeçalho SPDX padrão. + +## Exemplos de código **incorreto** + +```typescript title="src/helpers/utils.ts" +import { join } from "node:path"; + +export function resolvePath(base: string, rel: string): string { + return join(base, rel); +} +``` + +O arquivo está sem o cabeçalho de identificador de licença SPDX. + +## Exemplos de código **correto** + +```typescript title="src/helpers/utils.ts" +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate +import { join } from "node:path"; + +export function resolvePath(base: string, rel: string): string { + return join(base, rel); +} +``` + +Para arquivos com shebang: + +```typescript title="src/cli.ts" +#!/usr/bin/env bun +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate +import { Command } from "commander"; +``` + +## Implementação da regra + +```typescript +/// + +export default { + rules: { + "spdx-header-present": { + description: + "All TypeScript source files must have an SPDX-License-Identifier header", + async check(ctx) { + const results = await Promise.all( + ctx.scopedFiles.map(async (file) => { + const content = await ctx.readFile(file); + return { file, content }; + }) + ); + + for (const { file, content } of results) { + if (content === null) continue; + + // Check first 5 lines for the SPDX identifier (allows for shebang) + const lines = content.split("\n").slice(0, 5); + const hasSpdx = lines.some((line) => + line.includes("SPDX-License-Identifier: Apache-2.0") + ); + + if (!hasSpdx) { + ctx.report.violation({ + message: "Missing SPDX-License-Identifier header.", + file, + line: 1, + fix: 'Add "// SPDX-License-Identifier: Apache-2.0" as the first line of the file', + }); + } + } + }, + }, + }, +} satisfies RuleSet; +``` + +## Personalização + +- **Alterar a licença**: Substitua `Apache-2.0` pelo identificador SPDX do seu projeto (ex: `MIT`, `BSD-3-Clause`) +- **Alterar o escopo**: Ajuste o glob `files` no frontmatter do ADR para corresponder aos seus diretórios fonte +- **Adicionar verificação de copyright**: Estenda a regra para também verificar o formato da linha de copyright + +## Quando usar + +Quando seu projeto é open-source e você deseja declarações de licença por arquivo inequívocas reconhecidas por scanners de conformidade (FOSSA, Snyk, Black Duck, npm license-checker). + +## Quando não usar + +Em projetos proprietários/closed-source onde todos os arquivos são implicitamente "todos os direitos reservados", ou quando sua organização usa um mecanismo diferente de declaração de licença como arquivos `.dep5` do REUSE 3.0. From 4f141c2748ac01b8fc04429771b7ecddcbf8f124 Mon Sep 17 00:00:00 2001 From: rhuanbarreto <283004+rhuanbarreto@users.noreply.github.com> Date: Sun, 10 May 2026 21:10:18 +0000 Subject: [PATCH 07/16] docs: regenerate llms-full.txt Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- docs/public/llms-full.txt | 228 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 228 insertions(+) diff --git a/docs/public/llms-full.txt b/docs/public/llms-full.txt index 49347f74..2f37cff3 100644 --- a/docs/public/llms-full.txt +++ b/docs/public/llms-full.txt @@ -5029,6 +5029,135 @@ When your project intentionally mixes naming conventions (e.g., PascalCase for R --- +## Examples: license-compatibility + +Source: https://cli.archgate.dev/examples/license-compatibility/ + +Prevent copyleft or incompatible licenses from entering your dependency tree. + +## Rule details + +When you compile or bundle dependencies into your application, their license terms apply to the combined work. A single GPL dependency in a permissive (MIT/Apache-2.0) project can force the entire project to adopt copyleft terms. This rule checks all direct dependencies against a permissive-license allowlist. + +## Examples of **incorrect** code + +```json title="package.json" +{ + "dependencies": { "zod": "^3.23.0" }, + "devDependencies": { "readline-sync": "^1.4.10" } +} +``` + +If `readline-sync` uses GPL-3.0, it triggers a violation even as a devDependency. + +## Examples of **correct** code + +```json title="package.json" +{ + "dependencies": { "zod": "^3.23.0" }, + "devDependencies": { "fast-check": "^4.7.0" } +} +``` + +Both `zod` (MIT) and `fast-check` (MIT) are on the permissive allowlist. + +## Rule implementation + +```typescript +/// + +const ALLOWED_LICENSES = new Set([ + "MIT", + "Apache-2.0", + "ISC", + "BSD-2-Clause", + "BSD-3-Clause", + "0BSD", + "CC0-1.0", + "Unlicense", + "BlueOak-1.0.0", +]); + +function isAllowed(license: string | undefined): boolean { + if (!license) return false; + if (ALLOWED_LICENSES.has(license)) return true; + + // Handle SPDX OR expressions — at least one option must be allowed + const normalized = license.trim().replace(/^\(/u, "").replace(/\)$/u, ""); + if (ALLOWED_LICENSES.has(normalized)) return true; + + if (normalized.includes(" OR ")) { + return normalized.split(" OR ").some((l) => ALLOWED_LICENSES.has(l.trim())); + } + + return false; +} + +export default { + rules: { + "no-copyleft-deps": { + description: "All dependencies must use permissive licenses", + async check(ctx) { + let pkg: { + dependencies?: Record; + devDependencies?: Record; + }; + try { + pkg = (await ctx.readJSON("package.json")) as typeof pkg; + } catch { + return; + } + + const allDeps = [ + ...Object.keys(pkg.dependencies ?? {}), + ...Object.keys(pkg.devDependencies ?? {}), + ]; + + const depResults = await Promise.all( + allDeps.map(async (dep) => { + try { + const depPkg = (await ctx.readJSON( + `node_modules/${dep}/package.json` + )) as { license?: string }; + return { dep, license: depPkg.license }; + } catch { + return null; // Missing or scoped — skip + } + }) + ); + + for (const result of depResults) { + if (result === null) continue; + if (!isAllowed(result.license)) { + ctx.report.violation({ + message: `Dependency "${result.dep}" has disallowed license: "${result.license ?? "(none)"}".`, + file: "package.json", + fix: `Remove "${result.dep}" or find an alternative with a permissive license.`, + }); + } + } + }, + }, + }, +} satisfies RuleSet; +``` + +## Customization + +- **Change the allowlist**: Add or remove license identifiers based on your project's license. GPL projects can allow GPL dependencies; Apache-2.0 projects should block them. +- **Check only production deps**: Remove `devDependencies` from `allDeps` if you only care about bundled/shipped code. +- **Add transitive scanning**: For deeper coverage, scan all `node_modules/*/package.json` instead of only direct dependencies (see a dedicated script approach for this). + +## When to use it + +When your project uses a permissive license (MIT, Apache-2.0, ISC, BSD) and you want to prevent copyleft contamination, especially in compiled/bundled distributions. + +## When not to use it + +In GPL-licensed projects (where copyleft dependencies are compatible), or when you have a dedicated license-scanning SaaS tool (FOSSA, Snyk) that already gates your CI pipeline. + +--- + ## Examples: max-file-length Source: https://cli.archgate.dev/examples/max-file-length/ @@ -6062,6 +6191,105 @@ When the directory contains mixed file types that do not all need to follow the --- +## Examples: spdx-license-headers + +Source: https://cli.archgate.dev/examples/spdx-license-headers/ + +Ensure every source file declares its license with a machine-readable SPDX identifier. + +## Rule details + +Open-source projects benefit from per-file license declarations — they survive file extraction, bundling, and copy-paste scenarios where the root LICENSE file is not present. This rule verifies that every TypeScript source file starts with the standard SPDX header comment. + +## Examples of **incorrect** code + +```typescript title="src/helpers/utils.ts" + +export function resolvePath(base: string, rel: string): string { + return join(base, rel); +} +``` + +File is missing the SPDX license identifier header. + +## Examples of **correct** code + +```typescript title="src/helpers/utils.ts" +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate + +export function resolvePath(base: string, rel: string): string { + return join(base, rel); +} +``` + +For files with a shebang: + +```typescript title="src/cli.ts" +#!/usr/bin/env bun +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 Archgate + +``` + +## Rule implementation + +```typescript +/// + +export default { + rules: { + "spdx-header-present": { + description: + "All TypeScript source files must have an SPDX-License-Identifier header", + async check(ctx) { + const results = await Promise.all( + ctx.scopedFiles.map(async (file) => { + const content = await ctx.readFile(file); + return { file, content }; + }) + ); + + for (const { file, content } of results) { + if (content === null) continue; + + // Check first 5 lines for the SPDX identifier (allows for shebang) + const lines = content.split("\n").slice(0, 5); + const hasSpdx = lines.some((line) => + line.includes("SPDX-License-Identifier: Apache-2.0") + ); + + if (!hasSpdx) { + ctx.report.violation({ + message: "Missing SPDX-License-Identifier header.", + file, + line: 1, + fix: 'Add "// SPDX-License-Identifier: Apache-2.0" as the first line of the file', + }); + } + } + }, + }, + }, +} satisfies RuleSet; +``` + +## Customization + +- **Change the license**: Replace `Apache-2.0` with your project's SPDX identifier (e.g., `MIT`, `BSD-3-Clause`) +- **Change the scope**: Adjust the `files` glob in the ADR frontmatter to match your source directories +- **Add copyright check**: Extend the rule to also verify the copyright line format + +## When to use it + +When your project is open-source and you want unambiguous per-file license declarations that are recognized by compliance scanners (FOSSA, Snyk, Black Duck, npm license-checker). + +## When not to use it + +In proprietary/closed-source projects where all files are implicitly "all rights reserved," or when your organization uses a different license-declaration mechanism like REUSE 3.0 `.dep5` files. + +--- + ## Examples: test-file-coverage Source: https://cli.archgate.dev/examples/test-file-coverage/ From 5591279c1379509d26ab90026b466d751a82f7c7 Mon Sep 17 00:00:00 2001 From: Rhuan Barreto Date: Sun, 10 May 2026 23:11:09 +0200 Subject: [PATCH 08/16] feat(adrs): LEGAL-002 now scans all transitive dependencies recursively The no-copyleft-deps rule now globs node_modules/*/package.json and node_modules/@*/*/package.json to check ALL installed packages (direct and transitive), not just the ones listed in package.json. This catches copyleft licenses that may be introduced deep in the dependency tree. Signed-off-by: Rhuan Barreto --- ...AL-002-dependency-license-compatibility.md | 4 +- ...-dependency-license-compatibility.rules.ts | 46 ++++++++++++------- 2 files changed, 31 insertions(+), 19 deletions(-) diff --git a/.archgate/adrs/LEGAL-002-dependency-license-compatibility.md b/.archgate/adrs/LEGAL-002-dependency-license-compatibility.md index 9c8962ff..23da8c1f 100644 --- a/.archgate/adrs/LEGAL-002-dependency-license-compatibility.md +++ b/.archgate/adrs/LEGAL-002-dependency-license-compatibility.md @@ -82,8 +82,8 @@ SPDX OR expressions (e.g., `MIT OR Apache-2.0`) are allowed if at least one alte ### Automated Enforcement -- **Archgate rule** `LEGAL-002/no-copyleft-deps`: Reads `package.json` devDependencies and dependencies, cross-references against the allowlist in `scripts/check-licenses.ts`. Severity: `error` (hard blocker). -- **Script**: `bun run license:check` — scans all installed `node_modules` packages for comprehensive coverage including transitives. +- **Archgate rule** `LEGAL-002/no-copyleft-deps`: Scans **all** packages in `node_modules/` (direct and transitive) via glob, reads each `package.json` license field, and flags any package not on the permissive allowlist. Severity: `error` (hard blocker). +- **Script**: `bun run license:check` — standalone script for the same check (useful for local verification outside of `archgate check`). ### Manual Enforcement diff --git a/.archgate/adrs/LEGAL-002-dependency-license-compatibility.rules.ts b/.archgate/adrs/LEGAL-002-dependency-license-compatibility.rules.ts index de78190a..eb35587f 100644 --- a/.archgate/adrs/LEGAL-002-dependency-license-compatibility.rules.ts +++ b/.archgate/adrs/LEGAL-002-dependency-license-compatibility.rules.ts @@ -37,33 +37,45 @@ function isAllowed(license: string | undefined): boolean { return false; } +/** + * Extract the package name from a node_modules package.json path. + * Handles both regular (node_modules/foo/package.json) and scoped + * (node_modules/@scope/foo/package.json) packages. + */ +function extractPackageName(path: string): string { + const parts = path.replaceAll("\\", "/").split("/"); + const nmIdx = parts.lastIndexOf("node_modules"); + if (nmIdx === -1) return path; + const afterNm = parts.slice(nmIdx + 1); + // Scoped package: @scope/name + if (afterNm[0]?.startsWith("@") && afterNm.length >= 2) { + return `${afterNm[0]}/${afterNm[1]}`; + } + return afterNm[0] ?? path; +} + export default { rules: { "no-copyleft-deps": { description: - "All dependencies must use Apache-2.0-compatible (permissive) licenses", + "All dependencies (including transitive) must use Apache-2.0-compatible (permissive) licenses", async check(ctx) { - let pkg; - try { - pkg = await ctx.readJSON("package.json"); - } catch { - return; - } - - const allDeps = [ - ...Object.keys(pkg.dependencies ?? {}), - ...Object.keys(pkg.devDependencies ?? {}), + // Scan ALL packages in node_modules — direct AND transitive + const pkgFiles = [ + ...(await ctx.glob("node_modules/*/package.json")), + ...(await ctx.glob("node_modules/@*/*/package.json")), ]; const depResults = await Promise.all( - allDeps.map(async (dep) => { + pkgFiles.map(async (pkgPath) => { try { - const depPkg = await ctx.readJSON( - `node_modules/${dep}/package.json` - ); - return { dep, license: depPkg.license as string | undefined }; + const depPkg = await ctx.readJSON(pkgPath); + const name = extractPackageName(pkgPath); + return { + dep: name, + license: depPkg.license as string | undefined, + }; } catch { - // Scoped packages or missing — skip (covered by full scan) return null; } }) From 53d09c94f27d5274882578436fef9776f86625a5 Mon Sep 17 00:00:00 2001 From: Rhuan Barreto Date: Sun, 10 May 2026 23:16:48 +0200 Subject: [PATCH 09/16] =?UTF-8?q?refactor:=20remove=20redundant=20license:?= =?UTF-8?q?check=20script=20=E2=80=94=20LEGAL-002=20rule=20covers=20it?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The LEGAL-002 archgate rule now recursively scans all node_modules packages (direct + transitive), making scripts/check-licenses.ts redundant. Removed the script and the package.json license:check entry. Also updated ADR text and example docs to reflect the rule-only approach. Signed-off-by: Rhuan Barreto --- ...AL-002-dependency-license-compatibility.md | 9 +- .../docs/examples/license-compatibility.mdx | 2 +- .../pt-br/examples/license-compatibility.mdx | 2 +- package.json | 1 - scripts/check-licenses.ts | 126 ------------------ 5 files changed, 5 insertions(+), 135 deletions(-) delete mode 100644 scripts/check-licenses.ts diff --git a/.archgate/adrs/LEGAL-002-dependency-license-compatibility.md b/.archgate/adrs/LEGAL-002-dependency-license-compatibility.md index 23da8c1f..d2658723 100644 --- a/.archgate/adrs/LEGAL-002-dependency-license-compatibility.md +++ b/.archgate/adrs/LEGAL-002-dependency-license-compatibility.md @@ -46,9 +46,9 @@ SPDX OR expressions (e.g., `MIT OR Apache-2.0`) are allowed if at least one alte ### Do -- Run `bun run license:check` before adding any new dependency +- Run `archgate check` (or `bun run validate`) before adding any new dependency - Verify transitive dependencies — a permissively-licensed package may pull in a copyleft transitive -- Add newly-encountered permissive licenses to the allowlist in `scripts/check-licenses.ts` with a comment +- Add newly-encountered permissive licenses to the allowlist in the LEGAL-002 `.rules.ts` file - Prefer dependencies with clear SPDX license identifiers in their `package.json` ### Don't @@ -82,17 +82,14 @@ SPDX OR expressions (e.g., `MIT OR Apache-2.0`) are allowed if at least one alte ### Automated Enforcement -- **Archgate rule** `LEGAL-002/no-copyleft-deps`: Scans **all** packages in `node_modules/` (direct and transitive) via glob, reads each `package.json` license field, and flags any package not on the permissive allowlist. Severity: `error` (hard blocker). -- **Script**: `bun run license:check` — standalone script for the same check (useful for local verification outside of `archgate check`). +- **Archgate rule** `LEGAL-002/no-copyleft-deps`: Scans **all** packages in `node_modules/` (direct and transitive) via glob, reads each `package.json` license field, and flags any package not on the permissive allowlist. Severity: `error` (hard blocker). Runs as part of `archgate check` (included in `bun run validate`). ### Manual Enforcement - Dependency additions in PRs should include a note confirming license compatibility -- The `bun run license:check` script should be run after any `bun install` or lockfile update ## References - [SPDX License List](https://spdx.org/licenses/) - [Apache-2.0 License Compatibility](https://www.apache.org/legal/resolved.html) - [ARCH-006 — Dependency Policy](./ARCH-006-dependency-policy.md) — Governs which dependencies are allowed; this ADR governs their license compatibility -- [`scripts/check-licenses.ts`](../../scripts/check-licenses.ts) — The license scanner implementation diff --git a/docs/src/content/docs/examples/license-compatibility.mdx b/docs/src/content/docs/examples/license-compatibility.mdx index 5c54b7ab..61df2d6d 100644 --- a/docs/src/content/docs/examples/license-compatibility.mdx +++ b/docs/src/content/docs/examples/license-compatibility.mdx @@ -116,7 +116,7 @@ export default { - **Change the allowlist**: Add or remove license identifiers based on your project's license. GPL projects can allow GPL dependencies; Apache-2.0 projects should block them. - **Check only production deps**: Remove `devDependencies` from `allDeps` if you only care about bundled/shipped code. -- **Add transitive scanning**: For deeper coverage, scan all `node_modules/*/package.json` instead of only direct dependencies (see a dedicated script approach for this). +- **Scan transitives**: Use `ctx.glob("node_modules/*/package.json")` and `ctx.glob("node_modules/@*/*/package.json")` to scan all installed packages recursively, not just direct dependencies. ## When to use it diff --git a/docs/src/content/docs/pt-br/examples/license-compatibility.mdx b/docs/src/content/docs/pt-br/examples/license-compatibility.mdx index 2e0cc55d..4346e6fe 100644 --- a/docs/src/content/docs/pt-br/examples/license-compatibility.mdx +++ b/docs/src/content/docs/pt-br/examples/license-compatibility.mdx @@ -116,7 +116,7 @@ export default { - **Alterar a allowlist**: Adicione ou remova identificadores de licença baseado na licença do seu projeto. Projetos GPL podem permitir dependências GPL; projetos Apache-2.0 devem bloqueá-las. - **Verificar apenas deps de produção**: Remova `devDependencies` de `allDeps` se você só se preocupa com código empacotado/distribuído. -- **Adicionar scan transitivo**: Para cobertura mais profunda, escaneie todos os `node_modules/*/package.json` ao invés de apenas dependências diretas (veja uma abordagem com script dedicado para isso). +- **Escanear transitivas**: Use `ctx.glob("node_modules/*/package.json")` e `ctx.glob("node_modules/@*/*/package.json")` para escanear todos os pacotes instalados recursivamente, não apenas dependências diretas. ## Quando usar diff --git a/package.json b/package.json index cc14d801..b0d8bbc7 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,6 @@ "format": "oxfmt --write .", "format:check": "oxfmt --check .", "knip": "knip", - "license:check": "bun run scripts/check-licenses.ts", "lint": "oxlint --deny-warnings .", "postinstall": "node scripts/postinstall.cjs", "test": "bun test --timeout 60000", diff --git a/scripts/check-licenses.ts b/scripts/check-licenses.ts deleted file mode 100644 index d0fa37fc..00000000 --- a/scripts/check-licenses.ts +++ /dev/null @@ -1,126 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright 2026 Archgate -/** - * check-licenses.ts — Verify all dependency licenses are compatible with Apache-2.0. - * - * Run: bun run license:check - * - * Scans top-level node_modules packages and checks their `license` field against - * an allowlist of permissive licenses. Fails with exit code 1 if any package uses - * a copyleft or unknown license. - */ - -import { readdirSync, readFileSync, existsSync } from "node:fs"; -import { join } from "node:path"; -import { styleText } from "node:util"; - -const ALLOWED_LICENSES = new Set([ - "MIT", - "Apache-2.0", - "ISC", - "BSD-2-Clause", - "BSD-3-Clause", - "0BSD", - "CC0-1.0", - "Unlicense", - "BlueOak-1.0.0", - "CC-BY-4.0", - "CC-BY-3.0", - "Python-2.0", - "(MIT OR Apache-2.0)", - "MIT OR Apache-2.0", - "(MIT AND Zlib)", - "(BSD-2-Clause OR MIT OR Apache-2.0)", - "(MIT OR CC0-1.0)", - "BlueOak-1.0.0 OR MIT OR Apache-2.0", -]); - -/** - * Normalize license expressions — handles SPDX expressions and common variants. - */ -function normalizeLicense(raw: string): string { - return raw.trim().replace(/^\(/u, "").replace(/\)$/u, ""); -} - -function isAllowed(license: string | undefined): boolean { - if (!license) return false; - if (ALLOWED_LICENSES.has(license)) return true; - - // Handle OR expressions: "MIT OR Apache-2.0" — all alternatives must be allowed - const normalized = normalizeLicense(license); - if (ALLOWED_LICENSES.has(normalized)) return true; - if (ALLOWED_LICENSES.has(`(${normalized})`)) return true; - - // If it's an OR expression, check if at least one option is allowed - if (normalized.includes(" OR ")) { - return normalized.split(" OR ").some((l) => ALLOWED_LICENSES.has(l.trim())); - } - - return false; -} - -const nodeModulesPath = join(import.meta.dir, "..", "node_modules"); -const violations: { name: string; license: string | undefined }[] = []; -let scanned = 0; - -const entries = readdirSync(nodeModulesPath, { withFileTypes: true }); - -for (const entry of entries) { - if (!entry.isDirectory()) continue; - - // Handle scoped packages (@scope/package) - if (entry.name.startsWith("@")) { - const scopePath = join(nodeModulesPath, entry.name); - const scopedEntries = readdirSync(scopePath, { withFileTypes: true }); - for (const scopedEntry of scopedEntries) { - if (!scopedEntry.isDirectory()) continue; - const pkgJsonPath = join(scopePath, scopedEntry.name, "package.json"); - if (!existsSync(pkgJsonPath)) continue; - const pkgJson = JSON.parse(readFileSync(pkgJsonPath, "utf-8")); - const name = `${entry.name}/${scopedEntry.name}`; - const license = pkgJson.license; - scanned++; - if (!isAllowed(license)) { - violations.push({ name, license }); - } - } - } else { - // Skip hidden dirs and .package-lock.json etc. - if (entry.name.startsWith(".")) continue; - const pkgJsonPath = join(nodeModulesPath, entry.name, "package.json"); - if (!existsSync(pkgJsonPath)) continue; - const pkgJson = JSON.parse(readFileSync(pkgJsonPath, "utf-8")); - const license = pkgJson.license; - scanned++; - if (!isAllowed(license)) { - violations.push({ name: entry.name, license }); - } - } -} - -console.log(`Scanned ${scanned} packages.`); - -if (violations.length > 0) { - console.log( - styleText( - "red", - `\n${violations.length} package(s) with disallowed or missing licenses:\n` - ) - ); - for (const v of violations) { - console.log( - ` ${styleText("bold", v.name)}: ${v.license ?? "(no license field)"}` - ); - } - console.log( - "\nIf a license is permissive but not in the allowlist, add it to scripts/check-licenses.ts." - ); - process.exit(1); -} else { - console.log( - styleText( - "green", - "All dependency licenses are compatible with Apache-2.0." - ) - ); -} From 56c8783564430b59a425cabdb2933d8b909b8f2a Mon Sep 17 00:00:00 2001 From: rhuanbarreto <283004+rhuanbarreto@users.noreply.github.com> Date: Sun, 10 May 2026 21:17:45 +0000 Subject: [PATCH 10/16] docs: regenerate llms-full.txt Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- docs/public/llms-full.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/public/llms-full.txt b/docs/public/llms-full.txt index 2f37cff3..e471b106 100644 --- a/docs/public/llms-full.txt +++ b/docs/public/llms-full.txt @@ -5146,7 +5146,7 @@ export default { - **Change the allowlist**: Add or remove license identifiers based on your project's license. GPL projects can allow GPL dependencies; Apache-2.0 projects should block them. - **Check only production deps**: Remove `devDependencies` from `allDeps` if you only care about bundled/shipped code. -- **Add transitive scanning**: For deeper coverage, scan all `node_modules/*/package.json` instead of only direct dependencies (see a dedicated script approach for this). +- **Scan transitives**: Use `ctx.glob("node_modules/*/package.json")` and `ctx.glob("node_modules/@*/*/package.json")` to scan all installed packages recursively, not just direct dependencies. ## When to use it From 6c045b7b37ebd7922c597f03addc06089844df3e Mon Sep 17 00:00:00 2001 From: Rhuan Barreto Date: Sun, 10 May 2026 23:20:47 +0200 Subject: [PATCH 11/16] fix: remove stale bun run license:check references from ADR and rule Signed-off-by: Rhuan Barreto --- .archgate/adrs/LEGAL-002-dependency-license-compatibility.md | 2 +- .../adrs/LEGAL-002-dependency-license-compatibility.rules.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.archgate/adrs/LEGAL-002-dependency-license-compatibility.md b/.archgate/adrs/LEGAL-002-dependency-license-compatibility.md index d2658723..1930b7f9 100644 --- a/.archgate/adrs/LEGAL-002-dependency-license-compatibility.md +++ b/.archgate/adrs/LEGAL-002-dependency-license-compatibility.md @@ -74,7 +74,7 @@ SPDX OR expressions (e.g., `MIT OR Apache-2.0`) are allowed if at least one alte ### Risks - **Transitive dependency license change** — A previously-permissive dependency may change its license in a new version (e.g., the Elasticsearch SSPL relicensing). - - **Mitigation:** `bun run license:check` runs on the installed `node_modules` tree, catching license changes on any version update. + - **Mitigation:** The `LEGAL-002/no-copyleft-deps` rule scans the installed `node_modules` tree on every `archgate check`, catching license changes on any version update. - **Missing license field in package.json** — Some packages declare their license only in a LICENSE file, not in the `license` field. The scanner may flag these as "no license." - **Mitigation:** If a package is clearly permissive (LICENSE file exists) but lacks a `package.json` license field, add it to the allowlist with a comment explaining the override. diff --git a/.archgate/adrs/LEGAL-002-dependency-license-compatibility.rules.ts b/.archgate/adrs/LEGAL-002-dependency-license-compatibility.rules.ts index eb35587f..529df735 100644 --- a/.archgate/adrs/LEGAL-002-dependency-license-compatibility.rules.ts +++ b/.archgate/adrs/LEGAL-002-dependency-license-compatibility.rules.ts @@ -87,7 +87,7 @@ export default { ctx.report.violation({ message: `Dependency "${result.dep}" has disallowed license: "${result.license ?? "(none)"}". Only permissive licenses (MIT, Apache-2.0, ISC, BSD, etc.) are allowed.`, file: "package.json", - fix: `Remove "${result.dep}" or find an alternative with a permissive license. Run \`bun run license:check\` for full details.`, + fix: `Remove "${result.dep}" or find an alternative with a permissive license. See LEGAL-002 for the approved license list.`, }); } } From 389187a6a6483b7c11cd3a24d52210d728b44ba3 Mon Sep 17 00:00:00 2001 From: Rhuan Barreto Date: Sun, 10 May 2026 23:21:53 +0200 Subject: [PATCH 12/16] refactor: simplify LEGAL-002 to single glob with brace expansion Signed-off-by: Rhuan Barreto --- .../LEGAL-002-dependency-license-compatibility.rules.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.archgate/adrs/LEGAL-002-dependency-license-compatibility.rules.ts b/.archgate/adrs/LEGAL-002-dependency-license-compatibility.rules.ts index 529df735..2ed7fae3 100644 --- a/.archgate/adrs/LEGAL-002-dependency-license-compatibility.rules.ts +++ b/.archgate/adrs/LEGAL-002-dependency-license-compatibility.rules.ts @@ -60,11 +60,9 @@ export default { description: "All dependencies (including transitive) must use Apache-2.0-compatible (permissive) licenses", async check(ctx) { - // Scan ALL packages in node_modules — direct AND transitive - const pkgFiles = [ - ...(await ctx.glob("node_modules/*/package.json")), - ...(await ctx.glob("node_modules/@*/*/package.json")), - ]; + // Scan ALL packages in node_modules — direct AND transitive. + // Brace expansion covers both regular (zod) and scoped (@sentry/node-core) packages. + const pkgFiles = await ctx.glob("node_modules/{*,@*/*}/package.json"); const depResults = await Promise.all( pkgFiles.map(async (pkgPath) => { From 24b21aa3f99a1106bcf12364f49e61e491b599f9 Mon Sep 17 00:00:00 2001 From: Rhuan Barreto Date: Sun, 10 May 2026 23:22:45 +0200 Subject: [PATCH 13/16] docs: update example to use single brace-expansion glob Signed-off-by: Rhuan Barreto --- docs/src/content/docs/examples/license-compatibility.mdx | 2 +- docs/src/content/docs/pt-br/examples/license-compatibility.mdx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/src/content/docs/examples/license-compatibility.mdx b/docs/src/content/docs/examples/license-compatibility.mdx index 61df2d6d..fe97cec7 100644 --- a/docs/src/content/docs/examples/license-compatibility.mdx +++ b/docs/src/content/docs/examples/license-compatibility.mdx @@ -116,7 +116,7 @@ export default { - **Change the allowlist**: Add or remove license identifiers based on your project's license. GPL projects can allow GPL dependencies; Apache-2.0 projects should block them. - **Check only production deps**: Remove `devDependencies` from `allDeps` if you only care about bundled/shipped code. -- **Scan transitives**: Use `ctx.glob("node_modules/*/package.json")` and `ctx.glob("node_modules/@*/*/package.json")` to scan all installed packages recursively, not just direct dependencies. +- **Scan transitives**: Use `ctx.glob("node_modules/{*,@*/*}/package.json")` to scan all installed packages (including scoped) recursively, not just direct dependencies. ## When to use it diff --git a/docs/src/content/docs/pt-br/examples/license-compatibility.mdx b/docs/src/content/docs/pt-br/examples/license-compatibility.mdx index 4346e6fe..e5d5ad98 100644 --- a/docs/src/content/docs/pt-br/examples/license-compatibility.mdx +++ b/docs/src/content/docs/pt-br/examples/license-compatibility.mdx @@ -116,7 +116,7 @@ export default { - **Alterar a allowlist**: Adicione ou remova identificadores de licença baseado na licença do seu projeto. Projetos GPL podem permitir dependências GPL; projetos Apache-2.0 devem bloqueá-las. - **Verificar apenas deps de produção**: Remova `devDependencies` de `allDeps` se você só se preocupa com código empacotado/distribuído. -- **Escanear transitivas**: Use `ctx.glob("node_modules/*/package.json")` e `ctx.glob("node_modules/@*/*/package.json")` para escanear todos os pacotes instalados recursivamente, não apenas dependências diretas. +- **Escanear transitivas**: Use `ctx.glob("node_modules/{*,@*/*}/package.json")` para escanear todos os pacotes instalados (incluindo escopados) recursivamente, não apenas dependências diretas. ## Quando usar From 71982c7829f252e4716dae06d59ee03f38099992 Mon Sep 17 00:00:00 2001 From: rhuanbarreto <283004+rhuanbarreto@users.noreply.github.com> Date: Sun, 10 May 2026 21:23:53 +0000 Subject: [PATCH 14/16] docs: regenerate llms-full.txt Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- docs/public/llms-full.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/public/llms-full.txt b/docs/public/llms-full.txt index e471b106..84c1a802 100644 --- a/docs/public/llms-full.txt +++ b/docs/public/llms-full.txt @@ -5146,7 +5146,7 @@ export default { - **Change the allowlist**: Add or remove license identifiers based on your project's license. GPL projects can allow GPL dependencies; Apache-2.0 projects should block them. - **Check only production deps**: Remove `devDependencies` from `allDeps` if you only care about bundled/shipped code. -- **Scan transitives**: Use `ctx.glob("node_modules/*/package.json")` and `ctx.glob("node_modules/@*/*/package.json")` to scan all installed packages recursively, not just direct dependencies. +- **Scan transitives**: Use `ctx.glob("node_modules/{*,@*/*}/package.json")` to scan all installed packages (including scoped) recursively, not just direct dependencies. ## When to use it From cb78f39b229939bb9d227117594d66bea5adc4c2 Mon Sep 17 00:00:00 2001 From: Rhuan Barreto Date: Sun, 10 May 2026 23:24:52 +0200 Subject: [PATCH 15/16] docs(examples): update license-compatibility code to recursive glob implementation Signed-off-by: Rhuan Barreto --- .../docs/examples/license-compatibility.mdx | 50 +++++++++++-------- .../pt-br/examples/license-compatibility.mdx | 50 +++++++++++-------- 2 files changed, 58 insertions(+), 42 deletions(-) diff --git a/docs/src/content/docs/examples/license-compatibility.mdx b/docs/src/content/docs/examples/license-compatibility.mdx index fe97cec7..1dfe3ca5 100644 --- a/docs/src/content/docs/examples/license-compatibility.mdx +++ b/docs/src/content/docs/examples/license-compatibility.mdx @@ -63,35 +63,43 @@ function isAllowed(license: string | undefined): boolean { return false; } +/** + * Extract package name from a node_modules path. + * Handles both regular and scoped (@scope/name) packages. + */ +function extractPackageName(path: string): string { + const parts = path.replaceAll("\\", "/").split("/"); + const nmIdx = parts.lastIndexOf("node_modules"); + if (nmIdx === -1) return path; + const afterNm = parts.slice(nmIdx + 1); + if (afterNm[0]?.startsWith("@") && afterNm.length >= 2) { + return `${afterNm[0]}/${afterNm[1]}`; + } + return afterNm[0] ?? path; +} + export default { rules: { "no-copyleft-deps": { - description: "All dependencies must use permissive licenses", + description: + "All dependencies (including transitive) must use permissive licenses", async check(ctx) { - let pkg: { - dependencies?: Record; - devDependencies?: Record; - }; - try { - pkg = (await ctx.readJSON("package.json")) as typeof pkg; - } catch { - return; - } - - const allDeps = [ - ...Object.keys(pkg.dependencies ?? {}), - ...Object.keys(pkg.devDependencies ?? {}), - ]; + // Scan ALL packages in node_modules — direct AND transitive. + // Brace expansion covers both regular and scoped packages. + const pkgFiles = await ctx.glob("node_modules/{*,@*/*}/package.json"); const depResults = await Promise.all( - allDeps.map(async (dep) => { + pkgFiles.map(async (pkgPath) => { try { - const depPkg = (await ctx.readJSON( - `node_modules/${dep}/package.json` - )) as { license?: string }; - return { dep, license: depPkg.license }; + const depPkg = (await ctx.readJSON(pkgPath)) as { + license?: string; + }; + return { + dep: extractPackageName(pkgPath), + license: depPkg.license, + }; } catch { - return null; // Missing or scoped — skip + return null; } }) ); diff --git a/docs/src/content/docs/pt-br/examples/license-compatibility.mdx b/docs/src/content/docs/pt-br/examples/license-compatibility.mdx index e5d5ad98..531110d4 100644 --- a/docs/src/content/docs/pt-br/examples/license-compatibility.mdx +++ b/docs/src/content/docs/pt-br/examples/license-compatibility.mdx @@ -63,35 +63,43 @@ function isAllowed(license: string | undefined): boolean { return false; } +/** + * Extrai o nome do pacote de um caminho node_modules. + * Trata pacotes regulares e escopados (@scope/name). + */ +function extractPackageName(path: string): string { + const parts = path.replaceAll("\\", "/").split("/"); + const nmIdx = parts.lastIndexOf("node_modules"); + if (nmIdx === -1) return path; + const afterNm = parts.slice(nmIdx + 1); + if (afterNm[0]?.startsWith("@") && afterNm.length >= 2) { + return `${afterNm[0]}/${afterNm[1]}`; + } + return afterNm[0] ?? path; +} + export default { rules: { "no-copyleft-deps": { - description: "All dependencies must use permissive licenses", + description: + "All dependencies (including transitive) must use permissive licenses", async check(ctx) { - let pkg: { - dependencies?: Record; - devDependencies?: Record; - }; - try { - pkg = (await ctx.readJSON("package.json")) as typeof pkg; - } catch { - return; - } - - const allDeps = [ - ...Object.keys(pkg.dependencies ?? {}), - ...Object.keys(pkg.devDependencies ?? {}), - ]; + // Escaneia TODOS os pacotes em node_modules — diretos E transitivos. + // Brace expansion cobre pacotes regulares e escopados. + const pkgFiles = await ctx.glob("node_modules/{*,@*/*}/package.json"); const depResults = await Promise.all( - allDeps.map(async (dep) => { + pkgFiles.map(async (pkgPath) => { try { - const depPkg = (await ctx.readJSON( - `node_modules/${dep}/package.json` - )) as { license?: string }; - return { dep, license: depPkg.license }; + const depPkg = (await ctx.readJSON(pkgPath)) as { + license?: string; + }; + return { + dep: extractPackageName(pkgPath), + license: depPkg.license, + }; } catch { - return null; // Missing or scoped — skip + return null; } }) ); From b72e86c5a78eb852c65c7dcfeb2576b4de82bed5 Mon Sep 17 00:00:00 2001 From: rhuanbarreto <283004+rhuanbarreto@users.noreply.github.com> Date: Sun, 10 May 2026 21:26:49 +0000 Subject: [PATCH 16/16] docs: regenerate llms-full.txt Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- docs/public/llms-full.txt | 50 +++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/docs/public/llms-full.txt b/docs/public/llms-full.txt index 84c1a802..c8e004bd 100644 --- a/docs/public/llms-full.txt +++ b/docs/public/llms-full.txt @@ -5093,35 +5093,43 @@ function isAllowed(license: string | undefined): boolean { return false; } +/** + * Extract package name from a node_modules path. + * Handles both regular and scoped (@scope/name) packages. + */ +function extractPackageName(path: string): string { + const parts = path.replaceAll("\\", "/").split("/"); + const nmIdx = parts.lastIndexOf("node_modules"); + if (nmIdx === -1) return path; + const afterNm = parts.slice(nmIdx + 1); + if (afterNm[0]?.startsWith("@") && afterNm.length >= 2) { + return `${afterNm[0]}/${afterNm[1]}`; + } + return afterNm[0] ?? path; +} + export default { rules: { "no-copyleft-deps": { - description: "All dependencies must use permissive licenses", + description: + "All dependencies (including transitive) must use permissive licenses", async check(ctx) { - let pkg: { - dependencies?: Record; - devDependencies?: Record; - }; - try { - pkg = (await ctx.readJSON("package.json")) as typeof pkg; - } catch { - return; - } - - const allDeps = [ - ...Object.keys(pkg.dependencies ?? {}), - ...Object.keys(pkg.devDependencies ?? {}), - ]; + // Scan ALL packages in node_modules — direct AND transitive. + // Brace expansion covers both regular and scoped packages. + const pkgFiles = await ctx.glob("node_modules/{*,@*/*}/package.json"); const depResults = await Promise.all( - allDeps.map(async (dep) => { + pkgFiles.map(async (pkgPath) => { try { - const depPkg = (await ctx.readJSON( - `node_modules/${dep}/package.json` - )) as { license?: string }; - return { dep, license: depPkg.license }; + const depPkg = (await ctx.readJSON(pkgPath)) as { + license?: string; + }; + return { + dep: extractPackageName(pkgPath), + license: depPkg.license, + }; } catch { - return null; // Missing or scoped — skip + return null; } }) );