@misar/guards (0.1.0)
Installation
@misar:registry=npm install @misar/guards@0.1.0"@misar/guards": "0.1.0"About this package
@misar/guards
Zero-dependency CI guard CLI for the Misar AI monorepo. One package consolidates
the scattered check-*.mjs guard scripts so a fix lands once and every repo
consumes it. Pure Node.js ESM — Node built-ins only, no install step required to
run.
Install / run
It is published to the private Forgejo registry (@misar scope). In a repo that
already has the @misar scope wired in .npmrc:
pnpm add -D @misar/guards
pnpm misar-guards # blocking mode (exit 1 on findings)
Or run without adding a dep (the bin resolves from the workspace or the registry):
npx @misar/guards --advisory # warn-only ramp
node packages/guards/bin/misar-guards.mjs --help # from the misar-io monorepo
The CLI reads guards.config.json from the current working directory (the
repo root), so run it from the repo root in CI.
CLI contract
misar-guards [options]
--advisory Emit ::warning annotations and ALWAYS exit 0 (ramp mode).
--only=<ids> Run only these guard ids (comma-separated). Bypasses the
config enable/disable — listed guards run with their options.
--json Machine-readable JSON: { ran, counts, advisory, findings }.
--config=<path> Use a config file at <path> instead of ./guards.config.json.
--list List all guard ids + whether each is default-on. Exit 0.
-h, --help Show help.
Exit codes: 0 no findings (or --advisory) · 1 findings in blocking
mode · 2 usage/config error.
Output is Forgejo-Actions annotations, one per finding:
::error file=src/foo.ts,line=42,title=misar-guards/brand-colors::Banned Tailwind color family "purple-*" …
with a one-line summary of counts at the end.
Config (guards.config.json)
{
"$schema": "…/guards.schema.json", // optional
"advisory": false, // optional; same as --advisory
"ignore": ["packages/guards"], // optional; repo-relative path prefixes to skip
"guards": {
"<guard-id>": true | false | { ...options }
}
}
ignore entries are repo-relative path prefixes (also matched as substrings), applied
to every guard's file scan. In the misar-io monorepo, add "packages/guards" to
ignore — the guards package necessarily names the very patterns it detects (VPS
IPs, banned URLs, banned SDK names), so scanning its own source would self-flag.
node_modules, .next, dist, build, .turbo, out, coverage are always skipped.
Resolution:
- A guard listed in
guards:true→ enabled, no options.{ ...options }→ enabled with those options (add"enabled": falseto disable an options block).false→ disabled, even if it is in the default set.
- A guard not listed → enabled iff it is in the default set (below). Opt-in guards must be listed to run.
Copy guards.config.example.json to guards.config.json and trim to taste.
Guards
Default-on (universal — run unless disabled)
| id | checks |
|---|---|
banned-ai-sdk-imports |
Imports of @anthropic-ai/sdk, @google/generative-ai, @mistralai/mistralai (and friends), and openai used with OpenAI directly (OPENAI_API_KEY / api.openai.com) instead of the assisters gateway. |
banned-env-vars |
ANTHROPIC_API_KEY / OPENAI_API_KEY / GOOGLE_API_KEY / GEMINI_API_KEY in source and .env*; ASSISTERS_API_KEY in internal code (should be ASSISTERS_DEV_INTERNAL_KEY); the wrong gateway path …/api/v1 (should be …/v1). |
platform-boundary-imports |
Cross-platform package imports. Option banScope (default @assisters) — set to @misar in the assisters repo. |
brand-colors |
Tailwind purple-*/violet-*/fuchsia-*/indigo-* utilities and the 9 banned hexes. |
api-url-centralization |
www.misar.io/api/*, misar.io/api/*, and product-domain API paths (mail.misar.io/api, misar.dev/api, …). Requires api.misar.io/<slug>/*. |
file-locations |
.md only in docs/** + root README/CLAUDE/MISAR; test files (*.test.*/*.spec.*) only under tests/**; no packages/ dir and no content/docs/** MDX unless isMisarIo: true. |
npmrc-policy |
.npmrc scopes @misar/@assisters to Forgejo, no global registry= line, token var NPM_TOKEN (not FORGEJO_NPM_TOKEN). |
hardcoded-secrets |
Hardcoded VPS IPs (167.114.185.18, 77.42.113.248) and high-confidence secret shapes (sk-…, sk_live_…, GitHub/Slack tokens, PEM keys). |
status-url |
Status/uptime links to any host other than status.misar.io. |
legal-identity-placeholders |
"Pending registration" / "Coming soon" / "Not yet registered" in legal/footer copy (canonical company identity required). |
og-and-matcher |
Ported from check-og-coverage.mjs: root OG card presence, middleware config.matcher must exempt OG routes and webhook/unsubscribe routes, catches the .*og auth-bypass footgun, plus a favicon/icon-set presence check. |
Opt-in (off unless listed — repo-specific)
| id | checks | options |
|---|---|---|
reach-db-routing |
Reach-only tables (reach_*, global_leads, autopilot_runs, linkedin_*, direct_mail_*, social_channel_configs, lead_search_history, privacy_requests) accessed via the mail client (createAdminClient() / NEXT_PUBLIC_SUPABASE_URL) with no reachOwnedAdmin(). |
— |
migration-targets |
Every supabase/migrations/*.sql declares -- misar:target=reach|mail|both. |
dir, headerLines |
sitemap-robots-drift |
App sitemap.ts stays empty; answerBots/trainingBots arrays stay in lockstep between app and landing robots. |
appSitemap, appRobots, landingRobots |
Run misar-guards --list for the live list.
The --advisory ramp
Introducing guards into a repo with pre-existing violations shouldn't wedge the build. Ramp in three steps:
- Observe — run
misar-guards --advisoryin CI. Every finding is a::warningand the job stays green. Read the annotations, size the backlog. - Fix — clear the findings guard-by-guard. Use
--only=<id>locally to focus (misar-guards --only=brand-colors). - Enforce — drop
--advisory(or set"advisory": false). Findings now fail the build (exit 1). Optionally keep a newly-added noisy guard advisory in config while the rest block.
Commit-message hook
misar-commit-msg is a zero-dep validator for .husky/commit-msg. It rejects a
Co-Authored-By trailer, any "Claude"/"AI"/"Anthropic" mention (whole-word,
case-insensitive), and a subject that isn't conventional-commit
type(scope): desc (feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert).
Merge/revert/fixup subjects pass through.
# in a repo with @misar/guards installed
npx husky init # or: mkdir -p .husky
printf '%s\n' 'npx --no -- misar-commit-msg "$1"' > .husky/commit-msg
chmod +x .husky/commit-msg
Git passes the commit-message file path as $1; a violation exits non-zero and
blocks the commit with a helpful message.
Programmatic use
import { GUARDS, GUARDS_BY_ID, DEFAULT_GUARDS } from "@misar/guards";
import { validateCommitMessage } from "@misar/guards/commit-msg";
const { ok, errors } = validateCommitMessage("feat(x): do a thing");
Each guard exports { id, title, run(ctx) -> findings[] }; a finding is
{ guard, file, line, message, severity } (severity is "error" or
"warning").