{"_id":"@chughtapan/safer-architecture-lsp","_rev":"3-a01e7a428437abc5f0bf08fa0fbad427","name":"@chughtapan/safer-architecture-lsp","dist-tags":{"latest":"0.3.1"},"versions":{"0.1.0":{"name":"@chughtapan/safer-architecture-lsp","version":"0.1.0","keywords":["architecture","lint","linter","lsp","language-server","typescript","boundaries","cycles","dependencies","ci"],"author":{"name":"Tapan Chugh"},"license":"Apache-2.0","_id":"@chughtapan/safer-architecture-lsp@0.1.0","maintainers":[{"name":"tapanc","email":"chugh.tapan@gmail.com"}],"homepage":"https://github.com/chughtapan/safer-architecture-lsp#readme","bugs":{"url":"https://github.com/chughtapan/safer-architecture-lsp/issues"},"bin":{"safer-lsp-proxy":"dist/proxy/index.js","safer-architecture-lsp":"dist/server/index.js"},"dist":{"shasum":"0cbcab85423176c4c20b841663f00fd79da878f6","tarball":"https://registry.npmjs.org/@chughtapan/safer-architecture-lsp/-/safer-architecture-lsp-0.1.0.tgz","fileCount":250,"integrity":"sha512-qb8bURMwxuPWQUELI951UB40R6ekuFEdpyxhf3DhdcR/frWtHC4q4c+WY6Qbz+sqCA0xFpsaygP0JR9b0T1BRQ==","signatures":[{"sig":"MEQCIAfVYbmTjckJ4uGHpmwNR4XCKiGNAcNFaCFT6/7vKlWRAiAQIeoGo/9xrVH1rP6qw9nky4kxum8OlBybohcb3wIjig==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":577449},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20.19.0"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"scripts":{"lint":"agent-code-guard-knip","test":"npm run build && vitest run","build":"npm run clean && tsc -p tsconfig.json","clean":"rm -rf dist tsconfig.tsbuildinfo","prepack":"npm run clean && tsc -p tsconfig.json"},"_npmUser":{"name":"tapanc","email":"chugh.tapan@gmail.com"},"repository":{"url":"git+https://github.com/chughtapan/safer-architecture-lsp.git","type":"git"},"_npmVersion":"11.16.0","description":"Whole-project TypeScript architecture analyzer: a check CLI for CI and agents, a stdio LSP for editors, and reason-carrying suppression waivers.","directories":{},"_nodeVersion":"26.3.0","dependencies":{"effect":"^3.21.1","chokidar":"^5.0.0","typescript":"^5.4.0","vscode-languageserver":"^9.0.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.2.6","@types/node":"^22.0.0","eslint-plugin-agent-code-guard":"^0.0.14"},"_npmOperationalInternal":{"tmp":"tmp/safer-architecture-lsp_0.1.0_1784765604828_0.9835093614412245","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@chughtapan/safer-architecture-lsp","version":"0.2.0","keywords":["architecture","lint","linter","lsp","language-server","typescript","boundaries","cycles","dependencies","ci"],"author":{"name":"Tapan Chugh"},"license":"Apache-2.0","_id":"@chughtapan/safer-architecture-lsp@0.2.0","maintainers":[{"name":"tapanc","email":"chugh.tapan@gmail.com"}],"homepage":"https://github.com/chughtapan/safer-architecture-lsp#readme","bugs":{"url":"https://github.com/chughtapan/safer-architecture-lsp/issues"},"bin":{"safer-lsp-proxy":"dist/proxy/index.js","safer-architecture-lsp":"dist/server/index.js"},"dist":{"shasum":"bf58f54ddd73f9f40bdcc52fc014fbeb6c8d37bf","tarball":"https://registry.npmjs.org/@chughtapan/safer-architecture-lsp/-/safer-architecture-lsp-0.2.0.tgz","fileCount":254,"integrity":"sha512-7VwZ858M9M9JBG57ms5kWwCtt4XqO8opMJFg4ZpCOZ5uQ1hfHaV8TzBb5qV3jMeUjTj1+f1o0R6PbRfoc2Cy8Q==","signatures":[{"sig":"MEQCIC0IJRpgQD+dVk9UnnpNokJvsEMQw/LVB8QIfW+yZfiGAiAf3ArobZuEebEshOWHfOZFLxd1kaRgk3GFZLgPDg/c7A==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@chughtapan%2fsafer-architecture-lsp@0.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":613114},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20.19.0"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"gitHead":"3028b08df0035b942936963cbfd3bd314356e686","scripts":{"lint":"agent-code-guard-knip","test":"npm run build && vitest run","build":"npm run clean && tsc -p tsconfig.json","clean":"rm -rf dist tsconfig.tsbuildinfo","prepack":"npm run clean && tsc -p tsconfig.json"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:4e7fc84c-4abc-4583-9769-4dcf0d277b22"}},"repository":{"url":"git+https://github.com/chughtapan/safer-architecture-lsp.git","type":"git"},"_npmVersion":"12.0.1","description":"Whole-project TypeScript architecture analyzer: a check CLI for CI and agents, a stdio LSP for editors, and reason-carrying suppression waivers.","directories":{},"_nodeVersion":"22.23.1","dependencies":{"effect":"^3.21.1","chokidar":"^5.0.0","typescript":"^5.4.0","vscode-languageserver":"^9.0.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.2.6","@types/node":"^22.0.0","eslint-plugin-agent-code-guard":"^0.0.14"},"_npmOperationalInternal":{"tmp":"tmp/safer-architecture-lsp_0.2.0_1784877908112_0.14962560684997817","host":"s3://npm-registry-packages-npm-production"}},"0.3.1":{"name":"@chughtapan/safer-architecture-lsp","version":"0.3.1","description":"Whole-project TypeScript architecture analyzer: a check CLI for CI and agents, a stdio LSP for editors, and reason-carrying suppression waivers.","type":"module","license":"Apache-2.0","author":{"name":"Tapan Chugh"},"repository":{"type":"git","url":"git+https://github.com/chughtapan/safer-architecture-lsp.git"},"main":"./dist/index.js","types":"./dist/index.d.ts","bin":{"safer-architecture-lsp":"dist/server/index.js","safer-lsp-proxy":"dist/proxy/index.js"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"publishConfig":{"access":"public"},"scripts":{"prepare":"husky || true","clean":"rm -rf dist tsconfig.tsbuildinfo","build":"npm run clean && tsc -p tsconfig.json","prepack":"npm run clean && tsc -p tsconfig.json","test":"npm run build && vitest run","test:coverage":"npm run build && c8 vitest run && node scripts/coverage-gate.mjs","typecheck":"tsc -p tsconfig.test.json --noEmit","lint":"eslint . --max-warnings 0 && agent-code-guard-knip"},"dependencies":{"chokidar":"^5.0.0","effect":"^3.21.1","typescript":"^5.4.0","vscode-languageserver":"^9.0.1"},"devDependencies":{"@effect/vitest":"^0.30.0","@types/node":"^22.0.0","@vitest/coverage-v8":"^3.2.7","c8":"^12.0.0","eslint":"^9.39.5","eslint-plugin-agent-code-guard":"^0.0.21","globals":"^17.11.0","husky":"^9.1.7","vitest":"^3.2.6"},"engines":{"node":">=20.19.0"},"keywords":["architecture","lint","linter","lsp","language-server","typescript","boundaries","cycles","dependencies","ci"],"gitHead":"f784083f2297ebe107d644a0b6d842040ab70e0a","_id":"@chughtapan/safer-architecture-lsp@0.3.1","bugs":{"url":"https://github.com/chughtapan/safer-architecture-lsp/issues"},"homepage":"https://github.com/chughtapan/safer-architecture-lsp#readme","_nodeVersion":"22.23.2","_npmVersion":"12.0.2","dist":{"integrity":"sha512-EuswnjAt8uIHn8eDq+aZE5EI2thjgRETSCdZ5rxgHFzdAaVootnNvDY5CTJygUopyLTgE4mBVifdanw8GR3QPA==","shasum":"a882997e3f35c5c286693e65853c717699d13dce","tarball":"https://registry.npmjs.org/@chughtapan/safer-architecture-lsp/-/safer-architecture-lsp-0.3.1.tgz","fileCount":292,"unpackedSize":871029,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@chughtapan%2fsafer-architecture-lsp@0.3.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIGz9vBu4cKe1bf9hXX88x11WUuO2J5mQiANpXsk78oDCAiEA4eJeTEanoLb+3R7K23pl05nnCnYtH1t9Hl+DOyUVjzA="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:4e7fc84c-4abc-4583-9769-4dcf0d277b22"}},"directories":{},"maintainers":[{"name":"tapanc","email":"chugh.tapan@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/safer-architecture-lsp_0.3.1_1786744801272_0.5247789892342265"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-23T00:13:24.738Z","modified":"2026-08-14T22:00:01.802Z","0.1.0":"2026-07-23T00:13:25.001Z","0.2.0":"2026-07-24T07:25:08.364Z","0.3.1":"2026-08-14T22:00:01.436Z"},"bugs":{"url":"https://github.com/chughtapan/safer-architecture-lsp/issues"},"author":{"name":"Tapan Chugh"},"license":"Apache-2.0","homepage":"https://github.com/chughtapan/safer-architecture-lsp#readme","keywords":["architecture","lint","linter","lsp","language-server","typescript","boundaries","cycles","dependencies","ci"],"repository":{"type":"git","url":"git+https://github.com/chughtapan/safer-architecture-lsp.git"},"description":"Whole-project TypeScript architecture analyzer: a check CLI for CI and agents, a stdio LSP for editors, and reason-carrying suppression waivers.","maintainers":[{"name":"tapanc","email":"chugh.tapan@gmail.com"}],"readme":"# safer-architecture-lsp\n\nWhole-project TypeScript architecture analysis with a deterministic CI\ngate and live editor diagnostics. It builds a real `ts.Program`, derives\nthe folder-level import graph, and reports structural findings — cycles,\nlayering violations, oversized public surfaces, boundary leaks — with\nevery suppression carrying a written, auditable reason.\n\n**Why this instead of an ESLint rule?** Whole-project analysis doesn't\nfit a per-file lint model, and per-keystroke re-analysis doesn't fit CI\ntools. This package runs one warm analysis core behind two surfaces: a\n`check` command whose exit code CI can trust, and a Language Server that\nstreams the same findings into the diagnostics channel your editor — or\nyour coding agent — already reads. An agent that introduces a cycle sees\nthe diagnostic while authoring, and the escape hatch is one line it must\njustify: that inline feedback loop, with a queryable waiver ledger, is\nthe wedge.\n\n> **Status: 0.x.** Rule heuristics are young; expect to configure. See the 0.x stability\n> policy in [CHANGELOG.md](./CHANGELOG.md).\n\n## CI in two minutes\n\n```bash\nnpm install --save-dev @chughtapan/safer-architecture-lsp\nnpx safer-architecture-lsp check .\n```\n\n`check` analyzes the project once and exits `0` (clean), `1` (findings),\nor `2` (could not analyze — bad root, invalid config, unusable\ntsconfig). It always prints a summary line, so an empty result is\ndistinguishable from a run that never happened:\n\n```\nsafer-architecture check: 0 finding(s) across 0 file(s), 4 waiver(s), 1 project(s), options from file — /repo\n```\n\n`--json` emits the merged report (projects, diagnostics, waivers, and\ndeterministic analysis snapshots) for machine consumption; `--waivers`\nprints the suppression ledger with reasons.\n\n`check` writes a persistent report to\n`node_modules/.cache/safer-architecture-lsp/`, keyed by a content\nwatermark over the project's sources, `package.json`, and `tsconfig`. A\nrepeat run on an unchanged project reuses it instead of rebuilding the\n`ts.Program`, so **cache that directory in CI** to amortize cold cost.\n\nFor a monorepo, add an explicit `safer-architecture.workspace.json` at\nthe root. Paths are literal and relative—there is no implicit discovery\nor glob expansion:\n\n```json\n{ \"packages\": [\"packages/api\", \"packages/web\"] }\n```\n\nEvery listed directory must exist and have a unique, non-empty `package.json`\nname. The CLI and LSP analyze each package with its own config/cache, merge the\nreports, and reject runtime/peer dependency cycles. An invalid manifest blocks\nthe workspace loudly.\n\n## Editor / agent setup\n\nThe `serve` subcommand speaks LSP over stdio:\n\n```jsonc\n// Claude Code plugin.json\n{\n  \"lspServers\": {\n    \"safer-architecture\": {\n      \"command\": \"safer-architecture-lsp\",\n      \"args\": [\"serve\"],\n      \"extensionToLanguage\": { \".ts\": \"typescript\", \".tsx\": \"typescriptreact\" }\n    }\n  }\n}\n```\n\nAny stdio LSP client works the same way (`command: safer-architecture-lsp`,\n`args: [\"serve\"]`). Diagnostics are published for **every** analyzed\nfile, not just open ones, and each finding deep-links its rule\nreference. Hosts that cannot run two servers for one file extension can\nfront this server with the bundled `safer-lsp-proxy` multiplexer\nalongside `typescript-language-server`: the first backend in its config\nis primary, and a sidecar crash never takes the primary down.\nSee the [LSP server guide](./docs/lsp.md) for workspace discovery,\ndiagnostic lifecycle, and teardown behavior.\n\nVerify it's alive: stderr logs one line per workspace registration and\nper publish (`published N finding(s) across M file(s) … in Xms`).\n\n## Configuration\n\nPut `safer-architecture.config.json` at the project root (each listed package\nhas its own file in an explicit workspace). It is schema-validated; an invalid\nfile makes `check` exit without analysis and surfaces as an error diagnostic in\nthe editor, where defaults keep diagnostics available while you repair it.\nEdits hot-reload the affected project. The knobs you'll actually touch first:\n\n```jsonc\n{\n  // Third-party types intentionally part of your public API:\n  \"publicTypePackages\": [\n    { \"package\": \"typescript\", \"reason\": \"our contract is ts.Program-shaped\" }\n  ],\n  // Folders siblings may import freely (your shared kernel):\n  \"sharedFolderNames\": [\n    { \"folder\": \"analyzer\", \"reason\": \"one analysis core, many surfaces\" }\n  ],\n  // Deliberate non-index facade files:\n  \"facadeFiles\": [\n    { \"file\": \"server/workspace-engine.ts\", \"reason\": \"engine contract\" }\n  ],\n  // Public-surface budgets (defaults shown):\n  \"maxPublicExports\": 20,\n  \"maxSubpathExports\": 5\n}\n```\n\nEvery allowance entry requires a `reason` — writing it *is* the\narchitectural decision. The full option list lives in\n[docs/rules.md](./docs/rules.md), mapped rule-by-rule to the options\nthat control it. This repository's own\n[safer-architecture.config.json](./safer-architecture.config.json) is a\nworking example: CI runs `check .` on this codebase and fails on any\nunwaived finding.\n\n### CUPID domain enforcement\n\nDomain enforcement is opt-in and strict once `domains` is non-empty:\n\n```json\n{\n  \"domains\": [{\n    \"name\": \"orders\",\n    \"roots\": [\"src/orders\"],\n    \"entrypoints\": [\"src/orders/index.ts\"],\n    \"reason\": \"owns the order lifecycle\"\n  }],\n  \"domainOwnership\": [{\n    \"path\": \"src/legacy-orders\",\n    \"domain\": \"orders\",\n    \"reason\": \"migration ownership is explicit\"\n  }],\n  \"compositionRoots\": [{\n    \"path\": \"src/bootstrap.ts\",\n    \"reason\": \"application wiring edge\"\n  }]\n}\n```\n\nProduction modules must be owned by an inferred domain root, an explicit\nownership override, or a reason-carrying composition root. Cross-domain imports\nmust target destination entrypoints; domain dependencies must be acyclic; and\nside-effect-only cross-domain imports are restricted to composition roots. See\n[CUPID enforcement](./docs/cupid.md).\n\n## Rules\n\nThe complete reference — what each rule flags, why, its controlling\noptions, and how to fix it — is [docs/rules.md](./docs/rules.md).\nCategories: import topology (cycles, layering, sibling domains), public\nsurface (curation, size budgets, vendor-type leaks), folder shape\n(size, READMEs, explicit APIs), and module shape (accidental\nboundaries, trivial indirection, fat orchestrators).\n\n## Suppressions (waivers)\n\nOne comment line, file-scoped, reason mandatory:\n\n```ts\n// safer-arch-ignore no-trivial-sink-file: deliberate seam; the overlay follow-up grows here.\n```\n\nA missing or empty reason is itself a diagnostic. Granted waivers are\nretained with their reasons and queryable via `check --waivers` — an\nauditable ledger, not a muted warning. Unknown rule ids error. The\nlegacy two-line `@agent-code-guard/architecture-exception` marker from\nthis code's previous life is never honored and always errors, so a\nstale suppression cannot silently stop working.\n\n## Programmatic API\n\n```ts\nimport {\n  analyzeResolvedArchitecture,\n  resolveArchitectureOptions,\n} from \"@chughtapan/safer-architecture-lsp\";\n\nconst options = resolveArchitectureOptions({ projectRoot: process.cwd() });\nconst report = analyzeResolvedArchitecture(options);\nfor (const d of report.diagnostics) console.log(d.ruleId, d.file, d.message);\nfor (const w of report.waivers) console.log(\"waived\", w.ruleId, w.reason);\n```\n\nThe exported report types include deterministic analysis snapshots, and the\nexported option types include domain definitions, ownership overrides, and\ncomposition roots for typed configuration builders.\n\n## Scope and limits (read before adopting)\n\n- Module edges use TypeScript's cached resolver, including `paths` and package\n  `imports` aliases. Workspace package topology comes from declared runtime and\n  peer dependencies; cross-package source/project-reference imports remain\n  scoped to each package's TypeScript project.\n- Analysis is save-time in the editor; live keystroke-level diagnostics\n  are a follow-up.\n- A tsconfig found above the workspace root is scoped to the\n  workspace's files automatically.\n\n## Troubleshooting\n\n- **No diagnostics at all?** Check stderr. A missing/broken tsconfig now\n  surfaces as an `architecture-analysis-unavailable` error diagnostic —\n  if you see literally nothing, your client isn't connected (`serve`\n  missing from args is the common cause; a bare invocation prints help\n  and exits instead of hanging).\n- **\"config INVALID\" in stderr / squiggle on the config file** — the\n  JSON failed schema validation; the message names the offending key.\n- **Findings vanished after an edit?** The engine hot-reloaded with your\n  new config; run `check --waivers` to see what is being suppressed.\n\n## Development\n\n```bash\nnpm install\nnpm run build   # tsc → dist/\nnpm test        # vitest: analyzer fixtures, CLI contract, proxy, full LSP session\nnpm run test:coverage # c8 ratchet + per-directory floors; runs pre-push, not in CI\nnpm run lint    # eslint (agent-code-guard policy) + knip\nnode dist/server/index.js check .   # the dogfood gate CI enforces\n\nCoverage runs pre-push, not in CI: c8 slows the process-spawning suites enough\nto push the slowest property test past vitest's timeout on CI hardware. `npm\ninstall` wires the hook up through husky, so there is nothing to install by\nhand. Bypass it with `SKIP_COVERAGE=1 git push`.\n\nhusky takes over `core.hooksPath`, so anything already in `.git/hooks/pre-push`\nwould stop running; `.husky/pre-push` chains it explicitly.\n```\n\nPublishing: push a `vX.Y.Z` tag matching `package.json` — the publish\nworkflow builds, tests, lints, self-checks, then publishes to npm via\ntrusted publishing.\n","readmeFilename":"README.md"}