@cairn-tool/cairn 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.markdownlintrc +7 -0
- package/LICENSE +21 -0
- package/README.md +1431 -0
- package/dist/agent/audit/baseline.d.ts +69 -0
- package/dist/agent/audit/baseline.js +128 -0
- package/dist/agent/audit/baseline.js.map +1 -0
- package/dist/agent/audit/index.d.ts +126 -0
- package/dist/agent/audit/index.js +625 -0
- package/dist/agent/audit/index.js.map +1 -0
- package/dist/agent/import/detect.d.ts +49 -0
- package/dist/agent/import/detect.js +159 -0
- package/dist/agent/import/detect.js.map +1 -0
- package/dist/agent/import/normalize.d.ts +72 -0
- package/dist/agent/import/normalize.js +475 -0
- package/dist/agent/import/normalize.js.map +1 -0
- package/dist/agent/install/index.d.ts +211 -0
- package/dist/agent/install/index.js +907 -0
- package/dist/agent/install/index.js.map +1 -0
- package/dist/agent/manifest.d.ts +75 -0
- package/dist/agent/manifest.js +210 -0
- package/dist/agent/manifest.js.map +1 -0
- package/dist/agent/marketplace/index.d.ts +69 -0
- package/dist/agent/marketplace/index.js +212 -0
- package/dist/agent/marketplace/index.js.map +1 -0
- package/dist/agent/marketplace/spec.d.ts +48 -0
- package/dist/agent/marketplace/spec.js +237 -0
- package/dist/agent/marketplace/spec.js.map +1 -0
- package/dist/agent/output.d.ts +36 -0
- package/dist/agent/output.js +56 -0
- package/dist/agent/output.js.map +1 -0
- package/dist/agent/overlays.d.ts +38 -0
- package/dist/agent/overlays.js +223 -0
- package/dist/agent/overlays.js.map +1 -0
- package/dist/agent/package/index.d.ts +105 -0
- package/dist/agent/package/index.js +339 -0
- package/dist/agent/package/index.js.map +1 -0
- package/dist/agent/package/tar.d.ts +36 -0
- package/dist/agent/package/tar.js +137 -0
- package/dist/agent/package/tar.js.map +1 -0
- package/dist/agent/parser.d.ts +13 -0
- package/dist/agent/parser.js +559 -0
- package/dist/agent/parser.js.map +1 -0
- package/dist/agent/render.d.ts +16 -0
- package/dist/agent/render.js +721 -0
- package/dist/agent/render.js.map +1 -0
- package/dist/agent/sarif.d.ts +18 -0
- package/dist/agent/sarif.js +56 -0
- package/dist/agent/sarif.js.map +1 -0
- package/dist/agent/scaffold.d.ts +86 -0
- package/dist/agent/scaffold.js +250 -0
- package/dist/agent/scaffold.js.map +1 -0
- package/dist/agent/targets/antigravity.d.ts +17 -0
- package/dist/agent/targets/antigravity.js +218 -0
- package/dist/agent/targets/antigravity.js.map +1 -0
- package/dist/agent/targets/claude-code.d.ts +2 -0
- package/dist/agent/targets/claude-code.js +220 -0
- package/dist/agent/targets/claude-code.js.map +1 -0
- package/dist/agent/targets/codex.d.ts +2 -0
- package/dist/agent/targets/codex.js +212 -0
- package/dist/agent/targets/codex.js.map +1 -0
- package/dist/agent/targets/cursor.d.ts +2 -0
- package/dist/agent/targets/cursor.js +210 -0
- package/dist/agent/targets/cursor.js.map +1 -0
- package/dist/agent/targets/index.d.ts +31 -0
- package/dist/agent/targets/index.js +64 -0
- package/dist/agent/targets/index.js.map +1 -0
- package/dist/agent/targets/opencode.d.ts +15 -0
- package/dist/agent/targets/opencode.js +215 -0
- package/dist/agent/targets/opencode.js.map +1 -0
- package/dist/agent/targets/schema.d.ts +315 -0
- package/dist/agent/targets/schema.js +179 -0
- package/dist/agent/targets/schema.js.map +1 -0
- package/dist/agent/test/assert.d.ts +46 -0
- package/dist/agent/test/assert.js +288 -0
- package/dist/agent/test/assert.js.map +1 -0
- package/dist/agent/test/index.d.ts +73 -0
- package/dist/agent/test/index.js +181 -0
- package/dist/agent/test/index.js.map +1 -0
- package/dist/agent/test/schema.d.ts +81 -0
- package/dist/agent/test/schema.js +235 -0
- package/dist/agent/test/schema.js.map +1 -0
- package/dist/agent/types.d.ts +204 -0
- package/dist/agent/types.js +11 -0
- package/dist/agent/types.js.map +1 -0
- package/dist/agent/upgrade.d.ts +29 -0
- package/dist/agent/upgrade.js +72 -0
- package/dist/agent/upgrade.js.map +1 -0
- package/dist/agent/writer.d.ts +30 -0
- package/dist/agent/writer.js +73 -0
- package/dist/agent/writer.js.map +1 -0
- package/dist/archive/db.d.ts +20 -0
- package/dist/archive/db.js +98 -0
- package/dist/archive/db.js.map +1 -0
- package/dist/archive/progress.d.ts +62 -0
- package/dist/archive/progress.js +151 -0
- package/dist/archive/progress.js.map +1 -0
- package/dist/archive/query.d.ts +86 -0
- package/dist/archive/query.js +241 -0
- package/dist/archive/query.js.map +1 -0
- package/dist/archive/run.d.ts +91 -0
- package/dist/archive/run.js +331 -0
- package/dist/archive/run.js.map +1 -0
- package/dist/archive/segments.d.ts +89 -0
- package/dist/archive/segments.js +144 -0
- package/dist/archive/segments.js.map +1 -0
- package/dist/archive/sets.d.ts +58 -0
- package/dist/archive/sets.js +344 -0
- package/dist/archive/sets.js.map +1 -0
- package/dist/archive/tar-read.d.ts +35 -0
- package/dist/archive/tar-read.js +90 -0
- package/dist/archive/tar-read.js.map +1 -0
- package/dist/audit-baseline.d.ts +66 -0
- package/dist/audit-baseline.js +124 -0
- package/dist/audit-baseline.js.map +1 -0
- package/dist/automation.d.ts +16 -0
- package/dist/automation.js +38 -0
- package/dist/automation.js.map +1 -0
- package/dist/backlinks.d.ts +15 -0
- package/dist/backlinks.js +30 -0
- package/dist/backlinks.js.map +1 -0
- package/dist/checkers/katex.d.ts +3 -0
- package/dist/checkers/katex.js +71 -0
- package/dist/checkers/katex.js.map +1 -0
- package/dist/checkers/markdown-lint.d.ts +28 -0
- package/dist/checkers/markdown-lint.js +53 -0
- package/dist/checkers/markdown-lint.js.map +1 -0
- package/dist/checkers/mermaid.d.ts +6 -0
- package/dist/checkers/mermaid.js +53 -0
- package/dist/checkers/mermaid.js.map +1 -0
- package/dist/checkers/references.d.ts +3 -0
- package/dist/checkers/references.js +55 -0
- package/dist/checkers/references.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +1117 -0
- package/dist/cli.js.map +1 -0
- package/dist/command-result.d.ts +6 -0
- package/dist/command-result.js +13 -0
- package/dist/command-result.js.map +1 -0
- package/dist/commands/agent-audit.d.ts +21 -0
- package/dist/commands/agent-audit.js +119 -0
- package/dist/commands/agent-audit.js.map +1 -0
- package/dist/commands/agent-doctor.d.ts +25 -0
- package/dist/commands/agent-doctor.js +217 -0
- package/dist/commands/agent-doctor.js.map +1 -0
- package/dist/commands/agent-import.d.ts +21 -0
- package/dist/commands/agent-import.js +212 -0
- package/dist/commands/agent-import.js.map +1 -0
- package/dist/commands/agent-install.d.ts +18 -0
- package/dist/commands/agent-install.js +59 -0
- package/dist/commands/agent-install.js.map +1 -0
- package/dist/commands/agent-installed.d.ts +6 -0
- package/dist/commands/agent-installed.js +17 -0
- package/dist/commands/agent-installed.js.map +1 -0
- package/dist/commands/agent-marketplace.d.ts +11 -0
- package/dist/commands/agent-marketplace.js +178 -0
- package/dist/commands/agent-marketplace.js.map +1 -0
- package/dist/commands/agent-package.d.ts +16 -0
- package/dist/commands/agent-package.js +159 -0
- package/dist/commands/agent-package.js.map +1 -0
- package/dist/commands/agent-scaffold.d.ts +17 -0
- package/dist/commands/agent-scaffold.js +181 -0
- package/dist/commands/agent-scaffold.js.map +1 -0
- package/dist/commands/agent-specs.d.ts +7 -0
- package/dist/commands/agent-specs.js +20 -0
- package/dist/commands/agent-specs.js.map +1 -0
- package/dist/commands/agent-test.d.ts +21 -0
- package/dist/commands/agent-test.js +50 -0
- package/dist/commands/agent-test.js.map +1 -0
- package/dist/commands/agent-uninstall.d.ts +6 -0
- package/dist/commands/agent-uninstall.js +45 -0
- package/dist/commands/agent-uninstall.js.map +1 -0
- package/dist/commands/agent-upgrade.d.ts +5 -0
- package/dist/commands/agent-upgrade.js +105 -0
- package/dist/commands/agent-upgrade.js.map +1 -0
- package/dist/commands/agent.d.ts +27 -0
- package/dist/commands/agent.js +549 -0
- package/dist/commands/agent.js.map +1 -0
- package/dist/commands/archive.d.ts +47 -0
- package/dist/commands/archive.js +311 -0
- package/dist/commands/archive.js.map +1 -0
- package/dist/commands/audit.d.ts +25 -0
- package/dist/commands/audit.js +325 -0
- package/dist/commands/audit.js.map +1 -0
- package/dist/commands/check-snippets.d.ts +58 -0
- package/dist/commands/check-snippets.js +247 -0
- package/dist/commands/check-snippets.js.map +1 -0
- package/dist/commands/check-urls.d.ts +52 -0
- package/dist/commands/check-urls.js +205 -0
- package/dist/commands/check-urls.js.map +1 -0
- package/dist/commands/code-blocks.d.ts +8 -0
- package/dist/commands/code-blocks.js +66 -0
- package/dist/commands/code-blocks.js.map +1 -0
- package/dist/commands/completion.d.ts +15 -0
- package/dist/commands/completion.js +20 -0
- package/dist/commands/completion.js.map +1 -0
- package/dist/commands/context.d.ts +16 -0
- package/dist/commands/context.js +131 -0
- package/dist/commands/context.js.map +1 -0
- package/dist/commands/describe.d.ts +12 -0
- package/dist/commands/describe.js +62 -0
- package/dist/commands/describe.js.map +1 -0
- package/dist/commands/diff.d.ts +11 -0
- package/dist/commands/diff.js +185 -0
- package/dist/commands/diff.js.map +1 -0
- package/dist/commands/fix.d.ts +59 -0
- package/dist/commands/fix.js +173 -0
- package/dist/commands/fix.js.map +1 -0
- package/dist/commands/frontmatter.d.ts +7 -0
- package/dist/commands/frontmatter.js +84 -0
- package/dist/commands/frontmatter.js.map +1 -0
- package/dist/commands/graph.d.ts +12 -0
- package/dist/commands/graph.js +122 -0
- package/dist/commands/graph.js.map +1 -0
- package/dist/commands/headers.d.ts +7 -0
- package/dist/commands/headers.js +48 -0
- package/dist/commands/headers.js.map +1 -0
- package/dist/commands/index.d.ts +8 -0
- package/dist/commands/index.js +32 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/links.d.ts +9 -0
- package/dist/commands/links.js +123 -0
- package/dist/commands/links.js.map +1 -0
- package/dist/commands/lint-dir.d.ts +15 -0
- package/dist/commands/lint-dir.js +156 -0
- package/dist/commands/lint-dir.js.map +1 -0
- package/dist/commands/lint.d.ts +14 -0
- package/dist/commands/lint.js +45 -0
- package/dist/commands/lint.js.map +1 -0
- package/dist/commands/orphans.d.ts +10 -0
- package/dist/commands/orphans.js +74 -0
- package/dist/commands/orphans.js.map +1 -0
- package/dist/commands/outline.d.ts +7 -0
- package/dist/commands/outline.js +54 -0
- package/dist/commands/outline.js.map +1 -0
- package/dist/commands/query.d.ts +18 -0
- package/dist/commands/query.js +341 -0
- package/dist/commands/query.js.map +1 -0
- package/dist/commands/refs-to.d.ts +8 -0
- package/dist/commands/refs-to.js +65 -0
- package/dist/commands/refs-to.js.map +1 -0
- package/dist/commands/refs.d.ts +10 -0
- package/dist/commands/refs.js +103 -0
- package/dist/commands/refs.js.map +1 -0
- package/dist/commands/rename-file.d.ts +16 -0
- package/dist/commands/rename-file.js +215 -0
- package/dist/commands/rename-file.js.map +1 -0
- package/dist/commands/rename-heading.d.ts +10 -0
- package/dist/commands/rename-heading.js +182 -0
- package/dist/commands/rename-heading.js.map +1 -0
- package/dist/commands/schema.d.ts +10 -0
- package/dist/commands/schema.js +34 -0
- package/dist/commands/schema.js.map +1 -0
- package/dist/commands/scripts.d.ts +59 -0
- package/dist/commands/scripts.js +222 -0
- package/dist/commands/scripts.js.map +1 -0
- package/dist/commands/section.d.ts +9 -0
- package/dist/commands/section.js +49 -0
- package/dist/commands/section.js.map +1 -0
- package/dist/commands/serve.d.ts +6 -0
- package/dist/commands/serve.js +48 -0
- package/dist/commands/serve.js.map +1 -0
- package/dist/commands/stats.d.ts +6 -0
- package/dist/commands/stats.js +109 -0
- package/dist/commands/stats.js.map +1 -0
- package/dist/commands/structure.d.ts +6 -0
- package/dist/commands/structure.js +130 -0
- package/dist/commands/structure.js.map +1 -0
- package/dist/commands/tables.d.ts +8 -0
- package/dist/commands/tables.js +77 -0
- package/dist/commands/tables.js.map +1 -0
- package/dist/commands/tasks.d.ts +8 -0
- package/dist/commands/tasks.js +59 -0
- package/dist/commands/tasks.js.map +1 -0
- package/dist/commands/toc.d.ts +18 -0
- package/dist/commands/toc.js +105 -0
- package/dist/commands/toc.js.map +1 -0
- package/dist/commands/update-check.d.ts +36 -0
- package/dist/commands/update-check.js +119 -0
- package/dist/commands/update-check.js.map +1 -0
- package/dist/commands/usage.d.ts +56 -0
- package/dist/commands/usage.js +883 -0
- package/dist/commands/usage.js.map +1 -0
- package/dist/commands/validate-frontmatter.d.ts +13 -0
- package/dist/commands/validate-frontmatter.js +34 -0
- package/dist/commands/validate-frontmatter.js.map +1 -0
- package/dist/completion/model.d.ts +49 -0
- package/dist/completion/model.js +194 -0
- package/dist/completion/model.js.map +1 -0
- package/dist/completion/shells.d.ts +2 -0
- package/dist/completion/shells.js +280 -0
- package/dist/completion/shells.js.map +1 -0
- package/dist/config-schema.d.ts +15 -0
- package/dist/config-schema.js +44 -0
- package/dist/config-schema.js.map +1 -0
- package/dist/config.d.ts +102 -0
- package/dist/config.js +614 -0
- package/dist/config.js.map +1 -0
- package/dist/context.d.ts +106 -0
- package/dist/context.js +202 -0
- package/dist/context.js.map +1 -0
- package/dist/contract/describe.d.ts +84 -0
- package/dist/contract/describe.js +104 -0
- package/dist/contract/describe.js.map +1 -0
- package/dist/contract/registry.d.ts +2 -0
- package/dist/contract/registry.js +622 -0
- package/dist/contract/registry.js.map +1 -0
- package/dist/contract/schemas/agent.d.ts +2 -0
- package/dist/contract/schemas/agent.js +572 -0
- package/dist/contract/schemas/agent.js.map +1 -0
- package/dist/contract/schemas/archive.d.ts +3 -0
- package/dist/contract/schemas/archive.js +227 -0
- package/dist/contract/schemas/archive.js.map +1 -0
- package/dist/contract/schemas/index.d.ts +11 -0
- package/dist/contract/schemas/index.js +49 -0
- package/dist/contract/schemas/index.js.map +1 -0
- package/dist/contract/schemas/markdown.d.ts +15 -0
- package/dist/contract/schemas/markdown.js +1035 -0
- package/dist/contract/schemas/markdown.js.map +1 -0
- package/dist/contract/schemas/meta.d.ts +5 -0
- package/dist/contract/schemas/meta.js +233 -0
- package/dist/contract/schemas/meta.js.map +1 -0
- package/dist/contract/schemas/scripts.d.ts +4 -0
- package/dist/contract/schemas/scripts.js +183 -0
- package/dist/contract/schemas/scripts.js.map +1 -0
- package/dist/contract/schemas/shared.d.ts +11 -0
- package/dist/contract/schemas/shared.js +26 -0
- package/dist/contract/schemas/shared.js.map +1 -0
- package/dist/contract/schemas/usage.d.ts +6 -0
- package/dist/contract/schemas/usage.js +489 -0
- package/dist/contract/schemas/usage.js.map +1 -0
- package/dist/contract/types.d.ts +63 -0
- package/dist/contract/types.js +2 -0
- package/dist/contract/types.js.map +1 -0
- package/dist/contract/version.d.ts +20 -0
- package/dist/contract/version.js +21 -0
- package/dist/contract/version.js.map +1 -0
- package/dist/diff.d.ts +156 -0
- package/dist/diff.js +500 -0
- package/dist/diff.js.map +1 -0
- package/dist/edit-plan.d.ts +133 -0
- package/dist/edit-plan.js +245 -0
- package/dist/edit-plan.js.map +1 -0
- package/dist/fix/markdownlint-fixer.d.ts +34 -0
- package/dist/fix/markdownlint-fixer.js +125 -0
- package/dist/fix/markdownlint-fixer.js.map +1 -0
- package/dist/fix/registry.d.ts +48 -0
- package/dist/fix/registry.js +28 -0
- package/dist/fix/registry.js.map +1 -0
- package/dist/fix/relative-links-fixer.d.ts +21 -0
- package/dist/fix/relative-links-fixer.js +96 -0
- package/dist/fix/relative-links-fixer.js.map +1 -0
- package/dist/fix/snippets-fixer.d.ts +2 -0
- package/dist/fix/snippets-fixer.js +64 -0
- package/dist/fix/snippets-fixer.js.map +1 -0
- package/dist/fix/toc-fixer.d.ts +2 -0
- package/dist/fix/toc-fixer.js +94 -0
- package/dist/fix/toc-fixer.js.map +1 -0
- package/dist/formats.d.ts +22 -0
- package/dist/formats.js +34 -0
- package/dist/formats.js.map +1 -0
- package/dist/formatters.d.ts +11 -0
- package/dist/formatters.js +48 -0
- package/dist/formatters.js.map +1 -0
- package/dist/frontmatter-validation.d.ts +11 -0
- package/dist/frontmatter-validation.js +188 -0
- package/dist/frontmatter-validation.js.map +1 -0
- package/dist/git.d.ts +40 -0
- package/dist/git.js +124 -0
- package/dist/git.js.map +1 -0
- package/dist/graph.d.ts +50 -0
- package/dist/graph.js +200 -0
- package/dist/graph.js.map +1 -0
- package/dist/input-selection.d.ts +9 -0
- package/dist/input-selection.js +122 -0
- package/dist/input-selection.js.map +1 -0
- package/dist/input.d.ts +2 -0
- package/dist/input.js +32 -0
- package/dist/input.js.map +1 -0
- package/dist/link-target.d.ts +39 -0
- package/dist/link-target.js +68 -0
- package/dist/link-target.js.map +1 -0
- package/dist/lint.d.ts +13 -0
- package/dist/lint.js +27 -0
- package/dist/lint.js.map +1 -0
- package/dist/markdown-ast.d.ts +73 -0
- package/dist/markdown-ast.js +187 -0
- package/dist/markdown-ast.js.map +1 -0
- package/dist/object-path.d.ts +14 -0
- package/dist/object-path.js +14 -0
- package/dist/object-path.js.map +1 -0
- package/dist/option-utils.d.ts +13 -0
- package/dist/option-utils.js +25 -0
- package/dist/option-utils.js.map +1 -0
- package/dist/outline.d.ts +16 -0
- package/dist/outline.js +32 -0
- package/dist/outline.js.map +1 -0
- package/dist/query/entities.d.ts +60 -0
- package/dist/query/entities.js +219 -0
- package/dist/query/entities.js.map +1 -0
- package/dist/query/execute.d.ts +22 -0
- package/dist/query/execute.js +0 -0
- package/dist/query/execute.js.map +1 -0
- package/dist/query/plan.d.ts +76 -0
- package/dist/query/plan.js +183 -0
- package/dist/query/plan.js.map +1 -0
- package/dist/query/render.d.ts +3 -0
- package/dist/query/render.js +39 -0
- package/dist/query/render.js.map +1 -0
- package/dist/refs.d.ts +19 -0
- package/dist/refs.js +10 -0
- package/dist/refs.js.map +1 -0
- package/dist/result.d.ts +49 -0
- package/dist/result.js +47 -0
- package/dist/result.js.map +1 -0
- package/dist/runtime.d.ts +35 -0
- package/dist/runtime.js +77 -0
- package/dist/runtime.js.map +1 -0
- package/dist/sarif.d.ts +36 -0
- package/dist/sarif.js +30 -0
- package/dist/sarif.js.map +1 -0
- package/dist/scripts/execute.d.ts +65 -0
- package/dist/scripts/execute.js +235 -0
- package/dist/scripts/execute.js.map +1 -0
- package/dist/scripts/registry.d.ts +55 -0
- package/dist/scripts/registry.js +103 -0
- package/dist/scripts/registry.js.map +1 -0
- package/dist/scripts/resolve.d.ts +90 -0
- package/dist/scripts/resolve.js +288 -0
- package/dist/scripts/resolve.js.map +1 -0
- package/dist/sections.d.ts +53 -0
- package/dist/sections.js +100 -0
- package/dist/sections.js.map +1 -0
- package/dist/serve/errors.d.ts +25 -0
- package/dist/serve/errors.js +45 -0
- package/dist/serve/errors.js.map +1 -0
- package/dist/serve/paths.d.ts +32 -0
- package/dist/serve/paths.js +71 -0
- package/dist/serve/paths.js.map +1 -0
- package/dist/serve/server.d.ts +31 -0
- package/dist/serve/server.js +123 -0
- package/dist/serve/server.js.map +1 -0
- package/dist/serve/tools.d.ts +19 -0
- package/dist/serve/tools.js +597 -0
- package/dist/serve/tools.js.map +1 -0
- package/dist/snippets.d.ts +184 -0
- package/dist/snippets.js +454 -0
- package/dist/snippets.js.map +1 -0
- package/dist/sqlite-store.d.ts +62 -0
- package/dist/sqlite-store.js +104 -0
- package/dist/sqlite-store.js.map +1 -0
- package/dist/sqlite.d.ts +59 -0
- package/dist/sqlite.js +51 -0
- package/dist/sqlite.js.map +1 -0
- package/dist/toc.d.ts +58 -0
- package/dist/toc.js +83 -0
- package/dist/toc.js.map +1 -0
- package/dist/types.d.ts +7 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/update-notifier.d.ts +95 -0
- package/dist/update-notifier.js +301 -0
- package/dist/update-notifier.js.map +1 -0
- package/dist/url-cache.d.ts +14 -0
- package/dist/url-cache.js +70 -0
- package/dist/url-cache.js.map +1 -0
- package/dist/usage/aggregate.d.ts +107 -0
- package/dist/usage/aggregate.js +435 -0
- package/dist/usage/aggregate.js.map +1 -0
- package/dist/usage/db/index.d.ts +49 -0
- package/dist/usage/db/index.js +118 -0
- package/dist/usage/db/index.js.map +1 -0
- package/dist/usage/db/migrations.d.ts +29 -0
- package/dist/usage/db/migrations.js +38 -0
- package/dist/usage/db/migrations.js.map +1 -0
- package/dist/usage/db/open.d.ts +33 -0
- package/dist/usage/db/open.js +37 -0
- package/dist/usage/db/open.js.map +1 -0
- package/dist/usage/db/schema.d.ts +18 -0
- package/dist/usage/db/schema.js +147 -0
- package/dist/usage/db/schema.js.map +1 -0
- package/dist/usage/db/store.d.ts +74 -0
- package/dist/usage/db/store.js +280 -0
- package/dist/usage/db/store.js.map +1 -0
- package/dist/usage/events.d.ts +202 -0
- package/dist/usage/events.js +243 -0
- package/dist/usage/events.js.map +1 -0
- package/dist/usage/filter.d.ts +43 -0
- package/dist/usage/filter.js +97 -0
- package/dist/usage/filter.js.map +1 -0
- package/dist/usage/providers/antigravity.d.ts +2 -0
- package/dist/usage/providers/antigravity.js +431 -0
- package/dist/usage/providers/antigravity.js.map +1 -0
- package/dist/usage/providers/claude-code.d.ts +2 -0
- package/dist/usage/providers/claude-code.js +400 -0
- package/dist/usage/providers/claude-code.js.map +1 -0
- package/dist/usage/providers/codex.d.ts +2 -0
- package/dist/usage/providers/codex.js +387 -0
- package/dist/usage/providers/codex.js.map +1 -0
- package/dist/usage/providers/gemini-cli.d.ts +2 -0
- package/dist/usage/providers/gemini-cli.js +466 -0
- package/dist/usage/providers/gemini-cli.js.map +1 -0
- package/dist/usage/providers/index.d.ts +23 -0
- package/dist/usage/providers/index.js +46 -0
- package/dist/usage/providers/index.js.map +1 -0
- package/dist/usage/providers/opencode.d.ts +2 -0
- package/dist/usage/providers/opencode.js +422 -0
- package/dist/usage/providers/opencode.js.map +1 -0
- package/dist/usage/providers/protobuf.d.ts +35 -0
- package/dist/usage/providers/protobuf.js +125 -0
- package/dist/usage/providers/protobuf.js.map +1 -0
- package/dist/usage/providers/types.d.ts +74 -0
- package/dist/usage/providers/types.js +2 -0
- package/dist/usage/providers/types.js.map +1 -0
- package/dist/usage/scan.d.ts +54 -0
- package/dist/usage/scan.js +216 -0
- package/dist/usage/scan.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.js +8 -0
- package/dist/version.js.map +1 -0
- package/dist/workspace-index.d.ts +39 -0
- package/dist/workspace-index.js +265 -0
- package/dist/workspace-index.js.map +1 -0
- package/dist/workspace.d.ts +89 -0
- package/dist/workspace.js +219 -0
- package/dist/workspace.js.map +1 -0
- package/package.json +92 -0
|
@@ -0,0 +1,625 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { diagnostic, TARGETS } from "../types.js";
|
|
4
|
+
import { allFiles } from "../parser.js";
|
|
5
|
+
import { policyEntries } from "../render.js";
|
|
6
|
+
import { COMPONENT_KEYS } from "../manifest.js";
|
|
7
|
+
import { TARGET_PROFILES } from "../targets/index.js";
|
|
8
|
+
/** A single file larger than this is worth a reviewer's attention. */
|
|
9
|
+
export const MAX_FILE_BYTES = 1024 * 1024;
|
|
10
|
+
/** A bundle larger than this is worth a reviewer's attention. */
|
|
11
|
+
export const MAX_TOTAL_BYTES = 10 * 1024 * 1024;
|
|
12
|
+
/**
|
|
13
|
+
* Directories excluded from the source inventory.
|
|
14
|
+
*
|
|
15
|
+
* These are version-control and dependency infrastructure, not bundle content:
|
|
16
|
+
* the renderer never emits them, so counting them would drown the size and
|
|
17
|
+
* binary checks in noise that can never reach a generated plugin.
|
|
18
|
+
*/
|
|
19
|
+
export const INVENTORY_EXCLUDED = [".git", "node_modules"];
|
|
20
|
+
/**
|
|
21
|
+
* Root variables the targets declare, read from the profiles rather than
|
|
22
|
+
* hardcoded, so a new target's spelling is understood without editing audit.
|
|
23
|
+
*/
|
|
24
|
+
const RENDERED_PLACEHOLDERS = new Set(TARGETS.flatMap((target) => TARGET_PROFILES[target].placeholders.rootVariables));
|
|
25
|
+
/** Source spellings `rewritePlaceholders` consumes. No profile declares these. */
|
|
26
|
+
const SOURCE_PLACEHOLDERS = new Set(["${BUNDLE_ROOT}", "${SKILL_DIR}", "${ARGUMENTS}"]);
|
|
27
|
+
const INTERPRETERS = [
|
|
28
|
+
"sh",
|
|
29
|
+
"bash",
|
|
30
|
+
"zsh",
|
|
31
|
+
"dash",
|
|
32
|
+
"ksh",
|
|
33
|
+
"python",
|
|
34
|
+
"python3",
|
|
35
|
+
"node",
|
|
36
|
+
"perl",
|
|
37
|
+
"ruby",
|
|
38
|
+
"pwsh",
|
|
39
|
+
"powershell",
|
|
40
|
+
];
|
|
41
|
+
/** Interpreters and escalators that make a policy prefix meaningless. */
|
|
42
|
+
const ESCALATORS = [...INTERPRETERS, "env", "eval", "exec", "xargs", "sudo", "doas"];
|
|
43
|
+
/** Commands that fetch and run a package chosen at invocation time. */
|
|
44
|
+
const PACKAGE_RUNNERS = ["npx", "pnpx", "bunx", "uvx", "pipx", "dlx"];
|
|
45
|
+
const SECRET_KEY = /(?:^|[_-])(?:token|secret|password|passwd|apikey|api_key|access_key|credential|private_key|auth)(?:$|[_-])/i;
|
|
46
|
+
/** Provider-issued credential prefixes. Prefix-anchored, never entropy alone. */
|
|
47
|
+
const SECRET_VALUE = /\b(?:sk-[A-Za-z0-9]{16,}|ghp_[A-Za-z0-9]{20,}|github_pat_[A-Za-z0-9_]{20,}|xox[baprs]-[A-Za-z0-9-]{10,}|AKIA[0-9A-Z]{16}|AIza[0-9A-Za-z_-]{20,})\b/;
|
|
48
|
+
/** Keys whose values are structurally long and never credentials. */
|
|
49
|
+
const NOT_SECRET_KEYS = new Set([
|
|
50
|
+
"command",
|
|
51
|
+
"args",
|
|
52
|
+
"cwd",
|
|
53
|
+
"url",
|
|
54
|
+
"description",
|
|
55
|
+
"version",
|
|
56
|
+
"sha256",
|
|
57
|
+
"integrity",
|
|
58
|
+
]);
|
|
59
|
+
/** Extensions whose contents are expected to be binary. */
|
|
60
|
+
const BINARY_EXTENSIONS = /\.(?:png|jpe?g|gif|webp|ico|pdf|woff2?|ttf|otf|zip|gz|tgz|tar|wasm|so|dylib)$/i;
|
|
61
|
+
export const AUDIT_LIMITATIONS = [
|
|
62
|
+
"Static analysis only: no file is executed and no network request is made.",
|
|
63
|
+
"Findings are prompts for human review, not proof that a bundle is malicious.",
|
|
64
|
+
"Heuristics are conservative and readable; an obfuscated command can evade them.",
|
|
65
|
+
];
|
|
66
|
+
/** Every code the source-only checks can emit. */
|
|
67
|
+
export const SOURCE_CHECKS = [
|
|
68
|
+
"AB504",
|
|
69
|
+
"AB505",
|
|
70
|
+
"AB506",
|
|
71
|
+
"AB600",
|
|
72
|
+
"AB601",
|
|
73
|
+
"AB602",
|
|
74
|
+
"AB603",
|
|
75
|
+
"AB604",
|
|
76
|
+
"AB605",
|
|
77
|
+
"AB606",
|
|
78
|
+
"AB607",
|
|
79
|
+
"AB610",
|
|
80
|
+
"AB611",
|
|
81
|
+
"AB612",
|
|
82
|
+
"AB613",
|
|
83
|
+
"AB614",
|
|
84
|
+
"AB620",
|
|
85
|
+
"AB621",
|
|
86
|
+
"AB622",
|
|
87
|
+
"AB623",
|
|
88
|
+
"AB630",
|
|
89
|
+
"AB631",
|
|
90
|
+
"AB632",
|
|
91
|
+
"AB633",
|
|
92
|
+
"AB634",
|
|
93
|
+
"AB640",
|
|
94
|
+
"AB641",
|
|
95
|
+
];
|
|
96
|
+
/** Codes that additionally require `--target`, because they read rendered output. */
|
|
97
|
+
export const RENDERED_CHECKS = ["AB624", "AB642"];
|
|
98
|
+
/** Codes that additionally require `--baseline`. */
|
|
99
|
+
export const BASELINE_CHECKS = ["AB650", "AB651", "AB652", "AB653", "AB654"];
|
|
100
|
+
/**
|
|
101
|
+
* Every code audit itself produces, as opposed to the parse and render
|
|
102
|
+
* diagnostics it forwards. This distinction is what lets a warning about the
|
|
103
|
+
* bundle's review surface fail the command while an approximate mapping — which
|
|
104
|
+
* every codex bundle carries and which says nothing about trust — does not.
|
|
105
|
+
*/
|
|
106
|
+
export const AUDIT_CODES = new Set([
|
|
107
|
+
...SOURCE_CHECKS,
|
|
108
|
+
...RENDERED_CHECKS,
|
|
109
|
+
...BASELINE_CHECKS,
|
|
110
|
+
]);
|
|
111
|
+
function warn(code, message, extra = {}) {
|
|
112
|
+
return diagnostic(code, message, "approximate", extra);
|
|
113
|
+
}
|
|
114
|
+
function note(code, message, extra = {}) {
|
|
115
|
+
return diagnostic(code, message, "exact", extra);
|
|
116
|
+
}
|
|
117
|
+
function fail(code, message, extra = {}) {
|
|
118
|
+
return { ...diagnostic(code, message, "unsupported", extra), severity: "error" };
|
|
119
|
+
}
|
|
120
|
+
function record(value) {
|
|
121
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
122
|
+
? value
|
|
123
|
+
: undefined;
|
|
124
|
+
}
|
|
125
|
+
function posix(value) {
|
|
126
|
+
return value.split(path.sep).join("/");
|
|
127
|
+
}
|
|
128
|
+
/** Splits a command line into tokens, honoring single and double quotes. */
|
|
129
|
+
export function tokenize(command) {
|
|
130
|
+
const tokens = [];
|
|
131
|
+
for (const match of command.matchAll(/"([^"]*)"|'([^']*)'|(\S+)/g))
|
|
132
|
+
tokens.push(match[1] ?? match[2] ?? match[3]);
|
|
133
|
+
return tokens;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Executable-format magic number.
|
|
137
|
+
*
|
|
138
|
+
* Header parsing only, mirroring `imageKind` in the packager: pulling in a
|
|
139
|
+
* binary-format library to answer "is this an executable" would be a dependency
|
|
140
|
+
* for a review nicety.
|
|
141
|
+
*/
|
|
142
|
+
export function binaryKind(content) {
|
|
143
|
+
if (content.length < 4)
|
|
144
|
+
return null;
|
|
145
|
+
if (content.subarray(0, 4).toString("hex") === "7f454c46")
|
|
146
|
+
return "elf";
|
|
147
|
+
if (content[0] === 0x4d && content[1] === 0x5a)
|
|
148
|
+
return "pe";
|
|
149
|
+
const magic = content.subarray(0, 4).toString("hex");
|
|
150
|
+
if (["feedface", "feedfacf", "cefaedfe", "cffaedfe", "cafebabe"].includes(magic))
|
|
151
|
+
return "macho";
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
/** Shannon entropy per character, used only to grade a candidate secret. */
|
|
155
|
+
export function entropy(value) {
|
|
156
|
+
const counts = new Map();
|
|
157
|
+
for (const char of value)
|
|
158
|
+
counts.set(char, (counts.get(char) ?? 0) + 1);
|
|
159
|
+
let total = 0;
|
|
160
|
+
for (const count of counts.values()) {
|
|
161
|
+
const probability = count / value.length;
|
|
162
|
+
total -= probability * Math.log2(probability);
|
|
163
|
+
}
|
|
164
|
+
return total;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Every file in the bundle, with bundle-root-relative POSIX paths.
|
|
168
|
+
*
|
|
169
|
+
* The normalization is not cosmetic: `bundle.hookFiles` paths are relative to
|
|
170
|
+
* the hook directory, so passing them to the packager's `checkExecutables`
|
|
171
|
+
* would miss the `hooks/` prefix its regex looks for and flag every scaffolded
|
|
172
|
+
* hook script.
|
|
173
|
+
*/
|
|
174
|
+
export function buildSourceInventory(bundle) {
|
|
175
|
+
const files = [];
|
|
176
|
+
for (const entry of fs.readdirSync(bundle.root, { withFileTypes: true })) {
|
|
177
|
+
if (INVENTORY_EXCLUDED.includes(entry.name))
|
|
178
|
+
continue;
|
|
179
|
+
const full = path.join(bundle.root, entry.name);
|
|
180
|
+
if (entry.isDirectory())
|
|
181
|
+
files.push(...allFiles(full).map((file) => ({ ...file, path: `${entry.name}/${posix(file.path)}` })));
|
|
182
|
+
else if (entry.isFile() || entry.isSymbolicLink())
|
|
183
|
+
files.push({
|
|
184
|
+
path: entry.name,
|
|
185
|
+
content: fs.readFileSync(full),
|
|
186
|
+
mode: fs.statSync(full).mode & 0o777,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
return files.sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));
|
|
190
|
+
}
|
|
191
|
+
/** The `targets.<target>` override blocks of a structured component value. */
|
|
192
|
+
function overrides(value) {
|
|
193
|
+
const targets = record(value.targets);
|
|
194
|
+
if (!targets)
|
|
195
|
+
return [];
|
|
196
|
+
return TARGETS.flatMap((target) => {
|
|
197
|
+
const override = record(targets[target]);
|
|
198
|
+
return override ? [[target, override]] : [];
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
/** The event map of a hooks document, in any of the spellings the renderer accepts. */
|
|
202
|
+
function hookEvents(value) {
|
|
203
|
+
return (record(value.hooks) ??
|
|
204
|
+
record(value.events) ??
|
|
205
|
+
Object.fromEntries(Object.entries(value).filter(([key]) => key !== "targets")));
|
|
206
|
+
}
|
|
207
|
+
/** Collects the `command` strings out of a handler, flat or Claude-nested. */
|
|
208
|
+
function handlerCommands(handler) {
|
|
209
|
+
const entry = record(handler);
|
|
210
|
+
if (!entry)
|
|
211
|
+
return [];
|
|
212
|
+
const found = [];
|
|
213
|
+
for (const key of ["command", "windowsCommand"])
|
|
214
|
+
if (typeof entry[key] === "string")
|
|
215
|
+
found.push(entry[key]);
|
|
216
|
+
if (Array.isArray(entry.hooks))
|
|
217
|
+
for (const inner of entry.hooks)
|
|
218
|
+
found.push(...handlerCommands(inner));
|
|
219
|
+
return found;
|
|
220
|
+
}
|
|
221
|
+
/** The MCP server map of an mcp document. */
|
|
222
|
+
function mcpServers(value) {
|
|
223
|
+
return record(value.mcpServers) ?? {};
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Every command a hook or MCP server would run, from the base document and from
|
|
227
|
+
* each `targets.<target>` override.
|
|
228
|
+
*
|
|
229
|
+
* Deliberately read from the source rather than from rendered artifacts: the
|
|
230
|
+
* renderer substitutes placeholders but does not change a command's structure,
|
|
231
|
+
* so auditing both would report the same command up to six times.
|
|
232
|
+
*/
|
|
233
|
+
export function collectCommands(bundle) {
|
|
234
|
+
const commands = [];
|
|
235
|
+
if (bundle.hooks) {
|
|
236
|
+
const push = (source, target) => {
|
|
237
|
+
for (const [event, handlers] of Object.entries(hookEvents(source)))
|
|
238
|
+
for (const handler of Array.isArray(handlers) ? handlers : [handlers])
|
|
239
|
+
for (const command of handlerCommands(handler))
|
|
240
|
+
commands.push({
|
|
241
|
+
origin: "hook",
|
|
242
|
+
name: event,
|
|
243
|
+
command,
|
|
244
|
+
...(target ? { target } : {}),
|
|
245
|
+
path: bundle.hooks?.path,
|
|
246
|
+
});
|
|
247
|
+
};
|
|
248
|
+
push(bundle.hooks.value);
|
|
249
|
+
for (const [target, override] of overrides(bundle.hooks.value))
|
|
250
|
+
push(override, target);
|
|
251
|
+
}
|
|
252
|
+
if (bundle.mcp) {
|
|
253
|
+
const push = (source, target) => {
|
|
254
|
+
for (const [name, value] of Object.entries(mcpServers(source))) {
|
|
255
|
+
const server = record(value);
|
|
256
|
+
if (!server || typeof server.command !== "string")
|
|
257
|
+
continue;
|
|
258
|
+
commands.push({
|
|
259
|
+
origin: "mcp",
|
|
260
|
+
name,
|
|
261
|
+
command: server.command,
|
|
262
|
+
...(Array.isArray(server.args) ? { args: server.args.map(String) } : {}),
|
|
263
|
+
...(target ? { target } : {}),
|
|
264
|
+
path: bundle.mcp?.path,
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
push(bundle.mcp.value);
|
|
269
|
+
for (const [target, override] of overrides(bundle.mcp.value))
|
|
270
|
+
push(override, target);
|
|
271
|
+
}
|
|
272
|
+
return commands;
|
|
273
|
+
}
|
|
274
|
+
/** Every MCP server declaration, from the base document and each override. */
|
|
275
|
+
function collectServers(bundle) {
|
|
276
|
+
if (!bundle.mcp)
|
|
277
|
+
return [];
|
|
278
|
+
const found = [];
|
|
279
|
+
const push = (source, target) => {
|
|
280
|
+
for (const [name, value] of Object.entries(mcpServers(source))) {
|
|
281
|
+
const server = record(value);
|
|
282
|
+
if (server)
|
|
283
|
+
found.push({ name, server, ...(target ? { target } : {}) });
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
push(bundle.mcp.value);
|
|
287
|
+
for (const [target, override] of overrides(bundle.mcp.value))
|
|
288
|
+
push(override, target);
|
|
289
|
+
return found;
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Resolves a `${BUNDLE_ROOT}`-anchored path to a bundle-relative one.
|
|
293
|
+
*
|
|
294
|
+
* Only the portable spelling is resolved. `${CLAUDE_PLUGIN_ROOT}` and the other
|
|
295
|
+
* native root variables name the *rendered* tree, whose layout need not mirror
|
|
296
|
+
* the source, so resolving them against the bundle would report files that are
|
|
297
|
+
* present under a different name.
|
|
298
|
+
*/
|
|
299
|
+
function bundleReference(token) {
|
|
300
|
+
const match = token.match(/^(?:\$\{BUNDLE_ROOT\}|\{\{bundleRoot\}\})\/(.+)$/);
|
|
301
|
+
return match ? match[1] : null;
|
|
302
|
+
}
|
|
303
|
+
/** AB600–AB606: what the commands a host would run actually do. */
|
|
304
|
+
export function checkCommands(commands, bundle) {
|
|
305
|
+
const diagnostics = [];
|
|
306
|
+
for (const entry of commands) {
|
|
307
|
+
const where = { component: `${entry.origin}:${entry.name}`, path: entry.path };
|
|
308
|
+
const tokens = [...tokenize(entry.command), ...(entry.args ?? [])];
|
|
309
|
+
const line = [entry.command, ...(entry.args ?? [])].join(" ");
|
|
310
|
+
const first = tokens[0] ?? "";
|
|
311
|
+
const basename = path.posix.basename(first.replace(/\\/g, "/"));
|
|
312
|
+
if (INTERPRETERS.includes(basename)) {
|
|
313
|
+
const flag = tokens.findIndex((token) => ["-c", "-e", "-Command"].includes(token));
|
|
314
|
+
if (flag >= 0 && (tokens[flag + 1] ?? "").length > 0)
|
|
315
|
+
diagnostics.push(warn("AB600", `Runs an inline script through ${basename}: ${entry.name}`, {
|
|
316
|
+
...where,
|
|
317
|
+
remediation: "Move the script into a file this bundle owns, and invoke that.",
|
|
318
|
+
}));
|
|
319
|
+
}
|
|
320
|
+
if (/\$\([^)]*\)|`[^`]*`|(?:^|[^&|])[;|&]|[<>]/.test(line))
|
|
321
|
+
diagnostics.push(warn("AB601", `Command uses shell interpolation or chaining: ${entry.name}`, {
|
|
322
|
+
...where,
|
|
323
|
+
remediation: "Review what the substituted or chained portion can expand to.",
|
|
324
|
+
}));
|
|
325
|
+
for (const match of line.matchAll(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g)) {
|
|
326
|
+
const spelling = `\${${match[1]}}`;
|
|
327
|
+
if (RENDERED_PLACEHOLDERS.has(spelling) || SOURCE_PLACEHOLDERS.has(spelling))
|
|
328
|
+
continue;
|
|
329
|
+
diagnostics.push(note("AB602", `Command reads '${spelling}' from the host environment: ${entry.name}`, {
|
|
330
|
+
...where,
|
|
331
|
+
remediation: "Confirm the host is expected to provide it.",
|
|
332
|
+
}));
|
|
333
|
+
}
|
|
334
|
+
for (const token of tokens)
|
|
335
|
+
if (/^\/(?!\/)/.test(token) || /^[A-Za-z]:[\\/]/.test(token)) {
|
|
336
|
+
diagnostics.push(warn("AB603", `Command uses the absolute path '${token}': ${entry.name}`, {
|
|
337
|
+
...where,
|
|
338
|
+
remediation: "Use ${BUNDLE_ROOT}/… for bundle files, or a bare name resolved on PATH.",
|
|
339
|
+
}));
|
|
340
|
+
break;
|
|
341
|
+
}
|
|
342
|
+
if (/\b(?:curl|wget)\b[^|]*\|\s*(?:sudo\s+)?(?:sh|bash|zsh|python3?)\b/.test(line) ||
|
|
343
|
+
/(?:Invoke-Expression|iex)\s*\(/i.test(line))
|
|
344
|
+
diagnostics.push(warn("AB606", `Command downloads and executes code: ${entry.name}`, {
|
|
345
|
+
...where,
|
|
346
|
+
remediation: "Vendor the script into the bundle, or pin and verify what is fetched.",
|
|
347
|
+
}));
|
|
348
|
+
for (const token of tokens) {
|
|
349
|
+
const reference = bundleReference(token);
|
|
350
|
+
if (reference === null)
|
|
351
|
+
continue;
|
|
352
|
+
const resolved = path.join(bundle.root, reference);
|
|
353
|
+
if (!fs.existsSync(resolved) || !fs.statSync(resolved).isFile()) {
|
|
354
|
+
diagnostics.push(fail("AB604", `Command references a missing bundle file: ${reference}`, {
|
|
355
|
+
...where,
|
|
356
|
+
remediation: "Add the file, or correct the path.",
|
|
357
|
+
}));
|
|
358
|
+
continue;
|
|
359
|
+
}
|
|
360
|
+
const content = fs.readFileSync(resolved);
|
|
361
|
+
const mode = fs.statSync(resolved).mode & 0o777;
|
|
362
|
+
if (content.subarray(0, 2).toString("utf8") === "#!" && (mode & 0o111) === 0)
|
|
363
|
+
diagnostics.push(note("AB605", `Script '${reference}' has a shebang but is not executable`, {
|
|
364
|
+
...where,
|
|
365
|
+
path: resolved,
|
|
366
|
+
remediation: "chmod +x it, or invoke it through its interpreter.",
|
|
367
|
+
}));
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
return diagnostics;
|
|
371
|
+
}
|
|
372
|
+
/** AB610–AB614: what an MCP server connects to and what it is handed. */
|
|
373
|
+
export function checkMcp(bundle) {
|
|
374
|
+
const diagnostics = [];
|
|
375
|
+
for (const { name, server, target } of collectServers(bundle)) {
|
|
376
|
+
const where = {
|
|
377
|
+
component: `mcp:${name}`,
|
|
378
|
+
path: bundle.mcp?.path,
|
|
379
|
+
...(target ? { target } : {}),
|
|
380
|
+
};
|
|
381
|
+
const transport = server.type ?? server.transport;
|
|
382
|
+
if (typeof server.url === "string" ||
|
|
383
|
+
(transport !== undefined && String(transport) !== "stdio"))
|
|
384
|
+
diagnostics.push(note("AB610", `MCP server '${name}' is not a local stdio server` +
|
|
385
|
+
(typeof server.url === "string" ? `: ${server.url}` : ` (${String(transport)})`), { ...where, remediation: "Confirm the endpoint and who operates it." }));
|
|
386
|
+
for (const [key, value] of Object.entries(record(server.env) ?? {})) {
|
|
387
|
+
if (typeof value !== "string" || value === "")
|
|
388
|
+
continue;
|
|
389
|
+
const isReference = /^\$\{[^}]+\}$|^\$[A-Za-z_][A-Za-z0-9_]*$|^\{\{[^}]+\}\}$/.test(value);
|
|
390
|
+
if (SECRET_KEY.test(key) && !isReference)
|
|
391
|
+
diagnostics.push(warn("AB611", `MCP server '${name}' embeds a literal value in env.${key}`, {
|
|
392
|
+
...where,
|
|
393
|
+
remediation: "Reference an environment variable instead of inlining the value.",
|
|
394
|
+
}));
|
|
395
|
+
else if (SECRET_VALUE.test(value))
|
|
396
|
+
diagnostics.push(warn("AB611", `MCP server '${name}' env.${key} looks like an issued credential`, {
|
|
397
|
+
...where,
|
|
398
|
+
remediation: "Rotate it and reference an environment variable instead.",
|
|
399
|
+
}));
|
|
400
|
+
else if (!NOT_SECRET_KEYS.has(key) &&
|
|
401
|
+
value.length >= 32 &&
|
|
402
|
+
/^[A-Za-z0-9+/=_-]+$/.test(value) &&
|
|
403
|
+
entropy(value) >= 3.5)
|
|
404
|
+
diagnostics.push(note("AB612", `MCP server '${name}' env.${key} is a high-entropy literal`, {
|
|
405
|
+
...where,
|
|
406
|
+
remediation: "Confirm it is not a credential.",
|
|
407
|
+
}));
|
|
408
|
+
}
|
|
409
|
+
const inherits = server.inheritEnv === true ||
|
|
410
|
+
server.env === true ||
|
|
411
|
+
server.env === "inherit" ||
|
|
412
|
+
server.env === "*" ||
|
|
413
|
+
(Array.isArray(server.passEnv) && server.passEnv.map(String).includes("*")) ||
|
|
414
|
+
server.envFile !== undefined;
|
|
415
|
+
if (inherits)
|
|
416
|
+
diagnostics.push(warn("AB613", `MCP server '${name}' inherits broad environment state`, {
|
|
417
|
+
...where,
|
|
418
|
+
remediation: "List the variables the server actually needs.",
|
|
419
|
+
}));
|
|
420
|
+
if (typeof server.command === "string" &&
|
|
421
|
+
PACKAGE_RUNNERS.includes(path.posix.basename(server.command.replace(/\\/g, "/"))))
|
|
422
|
+
diagnostics.push(note("AB614", `MCP server '${name}' runs a package fetched at launch`, {
|
|
423
|
+
...where,
|
|
424
|
+
remediation: "Confirm the package and publisher; pin a version.",
|
|
425
|
+
}));
|
|
426
|
+
}
|
|
427
|
+
return diagnostics;
|
|
428
|
+
}
|
|
429
|
+
/** AB620–AB622: how broad a command policy's grants are. */
|
|
430
|
+
export function checkPolicies(bundle) {
|
|
431
|
+
const diagnostics = [];
|
|
432
|
+
for (const entry of policyEntries(bundle)) {
|
|
433
|
+
const action = String(entry.action ?? entry.decision ?? "prompt");
|
|
434
|
+
if (action !== "allow")
|
|
435
|
+
continue;
|
|
436
|
+
const rawPattern = entry.pattern ?? entry.prefix ?? entry.command ?? "";
|
|
437
|
+
const pattern = Array.isArray(rawPattern)
|
|
438
|
+
? rawPattern.flat().map(String).join(" ")
|
|
439
|
+
: String(rawPattern);
|
|
440
|
+
const where = { component: `policy:${pattern || "(empty)"}` };
|
|
441
|
+
const negative = entry.negativeExamples ?? entry.nonMatches;
|
|
442
|
+
if (!Array.isArray(negative) || negative.length === 0)
|
|
443
|
+
diagnostics.push(warn("AB620", `Allow rule '${pattern}' has no negative examples`, {
|
|
444
|
+
...where,
|
|
445
|
+
remediation: "Add the nearest invocation the rule must not permit.",
|
|
446
|
+
}));
|
|
447
|
+
const tokens = pattern.trim().split(/\s+/).filter(Boolean);
|
|
448
|
+
const first = tokens[0] ?? "";
|
|
449
|
+
const basename = path.posix.basename(first.replace(/\\/g, "/"));
|
|
450
|
+
if (first === "" || first === "*" || ESCALATORS.includes(basename))
|
|
451
|
+
diagnostics.push(warn("AB621", `Allow rule '${pattern || "(empty)"}' grants an interpreter or wildcard`, {
|
|
452
|
+
...where,
|
|
453
|
+
remediation: "An interpreter prefix permits any program; narrow it, or use prompt.",
|
|
454
|
+
}));
|
|
455
|
+
else if (tokens.length === 1 && !first.includes("/"))
|
|
456
|
+
diagnostics.push(warn("AB622", `Allow rule '${pattern}' permits every subcommand of ${pattern}`, {
|
|
457
|
+
...where,
|
|
458
|
+
remediation: "Add the subcommand and arguments the rule is meant to permit.",
|
|
459
|
+
}));
|
|
460
|
+
}
|
|
461
|
+
return diagnostics;
|
|
462
|
+
}
|
|
463
|
+
function componentsOf(bundle) {
|
|
464
|
+
return [...bundle.skills, ...bundle.agents, ...bundle.rules];
|
|
465
|
+
}
|
|
466
|
+
/** AB607, AB623, AB641: the capabilities components declare for themselves. */
|
|
467
|
+
export function checkCapabilities(bundle) {
|
|
468
|
+
const diagnostics = [];
|
|
469
|
+
const capabilities = new Set(["read", "write", "shell", "web"]);
|
|
470
|
+
const native = new Set(TARGETS.flatMap((target) => Object.values(TARGET_PROFILES[target].tools.capabilities ?? {}).flat()));
|
|
471
|
+
for (const component of componentsOf(bundle)) {
|
|
472
|
+
const tools = Array.isArray(component.metadata.tools)
|
|
473
|
+
? component.metadata.tools.map(String)
|
|
474
|
+
: [];
|
|
475
|
+
if (!tools.length)
|
|
476
|
+
continue;
|
|
477
|
+
const where = { component: component.name, path: component.path };
|
|
478
|
+
if (tools.includes("web") || tools.some((tool) => tool === "WebFetch" || tool === "WebSearch"))
|
|
479
|
+
diagnostics.push(note("AB607", `Component '${component.name}' is granted network tools`, where));
|
|
480
|
+
if (tools.includes("shell") || tools.includes("Bash"))
|
|
481
|
+
diagnostics.push(note("AB623", `Component '${component.name}' is granted shell access`, where));
|
|
482
|
+
// `mapTools` returns early when an explicit per-target list exists, so a
|
|
483
|
+
// vocabulary complaint would be about a value no target ever reads.
|
|
484
|
+
const targets = record(component.metadata.targets) ?? {};
|
|
485
|
+
if (TARGETS.some((target) => record(targets[target])?.tools !== undefined))
|
|
486
|
+
continue;
|
|
487
|
+
const unknown = tools.filter((tool) => !capabilities.has(tool) && !native.has(tool));
|
|
488
|
+
if (unknown.length)
|
|
489
|
+
diagnostics.push(warn("AB641", `Component '${component.name}' declares unrecognized tools: ${unknown.join(", ")}`, {
|
|
490
|
+
...where,
|
|
491
|
+
remediation: "Use a capability (read, write, shell, web), or a targets.<target>.tools override.",
|
|
492
|
+
}));
|
|
493
|
+
}
|
|
494
|
+
return diagnostics;
|
|
495
|
+
}
|
|
496
|
+
/** AB630–AB634: the shape of the files the bundle carries. */
|
|
497
|
+
export function checkInventory(bundle, files) {
|
|
498
|
+
const diagnostics = [];
|
|
499
|
+
const declaredAssets = new Set([bundle.marketplace?.icon, ...(bundle.marketplace?.screenshots ?? [])].filter((value) => typeof value === "string"));
|
|
500
|
+
let total = 0;
|
|
501
|
+
for (const file of files) {
|
|
502
|
+
total += file.content.length;
|
|
503
|
+
// Everything reaching here points inside the bundle: the parser throws on a
|
|
504
|
+
// symlink that resolves outside a component directory.
|
|
505
|
+
if (fs.lstatSync(path.join(bundle.root, file.path)).isSymbolicLink())
|
|
506
|
+
diagnostics.push(note("AB630", `Symlink in the bundle: ${file.path}`, {
|
|
507
|
+
path: file.path,
|
|
508
|
+
remediation: "Packaging follows it and stores a copy, not a link.",
|
|
509
|
+
}));
|
|
510
|
+
const kind = binaryKind(file.content);
|
|
511
|
+
if (kind)
|
|
512
|
+
diagnostics.push(warn("AB631", `Bundled ${kind} executable: ${file.path}`, {
|
|
513
|
+
path: file.path,
|
|
514
|
+
remediation: "Build it from source at install time, or document its provenance.",
|
|
515
|
+
}));
|
|
516
|
+
else if (file.content.includes(0) &&
|
|
517
|
+
!BINARY_EXTENSIONS.test(file.path) &&
|
|
518
|
+
// The default assets root; a custom one falls through to this notice,
|
|
519
|
+
// which is the right severity for "look at this file".
|
|
520
|
+
!file.path.startsWith("assets/"))
|
|
521
|
+
diagnostics.push(note("AB632", `Unexpected binary content: ${file.path}`, { path: file.path }));
|
|
522
|
+
if (file.content.length > MAX_FILE_BYTES && !declaredAssets.has(path.posix.basename(file.path)))
|
|
523
|
+
diagnostics.push(note("AB633", `File exceeds ${MAX_FILE_BYTES} bytes: ${file.path} (${file.content.length})`, { path: file.path }));
|
|
524
|
+
}
|
|
525
|
+
if (total > MAX_TOTAL_BYTES)
|
|
526
|
+
diagnostics.push(note("AB634", `Bundle exceeds ${MAX_TOTAL_BYTES} bytes (${total})`, {
|
|
527
|
+
remediation: "Consider fetching large resources at install time instead.",
|
|
528
|
+
}));
|
|
529
|
+
return diagnostics;
|
|
530
|
+
}
|
|
531
|
+
/** AB640: manifest component roots that name nothing. */
|
|
532
|
+
export function checkManifestClaims(bundle) {
|
|
533
|
+
if (bundle.legacy)
|
|
534
|
+
return [];
|
|
535
|
+
const declared = record(bundle.manifest.components) ?? {};
|
|
536
|
+
const populated = {
|
|
537
|
+
skills: bundle.skills.length > 0,
|
|
538
|
+
agents: bundle.agents.length > 0,
|
|
539
|
+
rules: bundle.rules.length > 0,
|
|
540
|
+
hooks: bundle.hooks !== undefined,
|
|
541
|
+
policies: bundle.policies.length > 0,
|
|
542
|
+
mcp: bundle.mcp !== undefined,
|
|
543
|
+
assets: bundle.assets.length > 0,
|
|
544
|
+
};
|
|
545
|
+
return COMPONENT_KEYS.filter((key) => (declared[key] !== undefined || bundle.manifest[key] !== undefined) && !populated[key]).map((key) => note("AB640", `The manifest declares a component root '${key}' that holds nothing`, {
|
|
546
|
+
remediation: "Add the component, or drop the declaration.",
|
|
547
|
+
}));
|
|
548
|
+
}
|
|
549
|
+
/** AB624, AB642: what the rendered output grants and claims. */
|
|
550
|
+
export function checkRendered(bundle, artifacts, targets, profiles) {
|
|
551
|
+
const diagnostics = [];
|
|
552
|
+
const unbounded = /^Bash\(\s*[*:]/;
|
|
553
|
+
for (const artifact of artifacts) {
|
|
554
|
+
if (!/(^|\/)\.claude\/settings\.json$/.test(artifact.path))
|
|
555
|
+
continue;
|
|
556
|
+
let parsed;
|
|
557
|
+
try {
|
|
558
|
+
parsed = JSON.parse(artifact.content.toString("utf8"));
|
|
559
|
+
}
|
|
560
|
+
catch {
|
|
561
|
+
continue;
|
|
562
|
+
}
|
|
563
|
+
const allow = record(record(parsed)?.permissions)?.allow;
|
|
564
|
+
for (const grant of Array.isArray(allow) ? allow.map(String) : [])
|
|
565
|
+
if (unbounded.test(grant))
|
|
566
|
+
diagnostics.push(warn("AB624", `Rendered permission grants unrestricted shell: ${grant}`, {
|
|
567
|
+
path: artifact.path,
|
|
568
|
+
remediation: "Give the policy rule a command prefix.",
|
|
569
|
+
}));
|
|
570
|
+
}
|
|
571
|
+
// Frontmatter passes through `metadataFor` untouched, so a hand-written
|
|
572
|
+
// allowed-tools entry reaches the emitted component verbatim.
|
|
573
|
+
for (const component of componentsOf(bundle)) {
|
|
574
|
+
const raw = component.metadata["allowed-tools"] ?? component.metadata.permissions;
|
|
575
|
+
const values = Array.isArray(raw) ? raw.map(String) : typeof raw === "string" ? [raw] : [];
|
|
576
|
+
for (const value of values)
|
|
577
|
+
if (/Bash\(\s*[*:]?\s*\*?\s*\)/.test(value) || unbounded.test(value))
|
|
578
|
+
diagnostics.push(warn("AB624", `Component '${component.name}' grants unrestricted shell: ${value}`, {
|
|
579
|
+
component: component.name,
|
|
580
|
+
path: component.path,
|
|
581
|
+
remediation: "Name the commands the component needs.",
|
|
582
|
+
}));
|
|
583
|
+
}
|
|
584
|
+
// Skipped for a bundle with no components at all: the manifest still names
|
|
585
|
+
// the default roots, and reporting them would fail a bare `agent init`.
|
|
586
|
+
if (!componentsOf(bundle).length && !bundle.hooks && !bundle.mcp)
|
|
587
|
+
return diagnostics;
|
|
588
|
+
for (const target of targets)
|
|
589
|
+
for (const profile of profiles) {
|
|
590
|
+
const prefix = `${target}/${profile}/`;
|
|
591
|
+
const paths = artifacts
|
|
592
|
+
.filter((artifact) => artifact.path.startsWith(prefix))
|
|
593
|
+
.map((artifact) => artifact.path.slice(prefix.length));
|
|
594
|
+
// The manifest location is profile data, never a guess at a filename.
|
|
595
|
+
const spec = TARGET_PROFILES[target].manifest;
|
|
596
|
+
if (spec.directory === null)
|
|
597
|
+
continue;
|
|
598
|
+
const manifestArtifact = artifacts.find((artifact) => artifact.path === `${prefix}${spec.directory}/${spec.file}`);
|
|
599
|
+
if (!manifestArtifact)
|
|
600
|
+
continue;
|
|
601
|
+
let manifest;
|
|
602
|
+
try {
|
|
603
|
+
manifest = record(JSON.parse(manifestArtifact.content.toString("utf8")));
|
|
604
|
+
}
|
|
605
|
+
catch {
|
|
606
|
+
continue;
|
|
607
|
+
}
|
|
608
|
+
for (const key of ["skills", "agents", "hooks", "mcpServers"]) {
|
|
609
|
+
const claim = manifest?.[key];
|
|
610
|
+
if (typeof claim !== "string")
|
|
611
|
+
continue;
|
|
612
|
+
const normalized = claim.replace(/^\.\//, "").replace(/\/$/, "");
|
|
613
|
+
if (!normalized)
|
|
614
|
+
continue;
|
|
615
|
+
if (!paths.some((candidate) => candidate === normalized || candidate.startsWith(`${normalized}/`)))
|
|
616
|
+
diagnostics.push(note("AB642", `The rendered manifest claims '${key}: ${claim}', which has no files`, {
|
|
617
|
+
target,
|
|
618
|
+
profile,
|
|
619
|
+
path: manifestArtifact.path,
|
|
620
|
+
}));
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
return diagnostics;
|
|
624
|
+
}
|
|
625
|
+
//# sourceMappingURL=index.js.map
|