ferc/pruneguard

GitHub: ferc/pruneguard

基于 Rust 的高性能 JS/TS monorepo 死代码检测与架构治理工具,支持安全删除分析和 CI 集成。

Stars: 1 | Forks: 0

# pruneguard Pruneguard 审查 JS/TS 仓库,告诉你哪些代码未使用、哪些违反了架构规则、哪些可以安全删除,以及你的分支中发生了哪些变化。 它以编译好的 Rust 二进制文件形式发布,并带有一层轻薄的 JS 封装。无需 Rust 工具链,无需编译,无需原生插件 —— 只需 `npm install` 即可开始使用。 ## 快速开始 ``` npm install pruneguard ``` 在 `package.json` 中添加脚本: ``` { "scripts": { "review": "pruneguard", "scan": "pruneguard scan", "prune:delete": "pruneguard safe-delete" } } ``` 运行: ``` # 审查你的 repo (默认命令) npx pruneguard # 仅审查你 branch 上的更改 npx pruneguard --changed-since origin/main # 完整详细扫描 npx pruneguard scan # 检查 file 是否可安全删除 npx pruneguard safe-delete src/legacy/old-widget.ts # 获取修复计划 npx pruneguard fix-plan src/legacy/old-widget.ts ``` 需要 Node.js >= 18。支持的平台:macOS (ARM64, x64), Linux (x64/ARM64, glibc 和 musl), Windows (x64, ARM64)。 有关从安装到获取首个结果的完整演练,请参阅 [docs/getting-started.md](docs/getting-started.md)。 ## 工作原理 pruneguard 为每个支持的平台发布一个编译好的 Rust 二进制文件。JS API 和 CLI 都在后台调用该二进制文件。在你的本地机器上,daemon 会将图保存在内存中保持温热,以实现亚毫秒级的查询。在 CI 中(或当你传递 `--daemon off` 时),每次调用都是一次全新的一次性运行。 ``` npm install pruneguard | v @pruneguard/cli- <-- native binary, auto-selected by OS+arch | v pruneguard (JS wrapper) <-- spawns the binary, parses JSON output | +-- CLI: npx pruneguard +-- JS API: import { review } from "pruneguard" ``` | 上下文 | 模式 | 原因 | |---------------------|----------|-------------------------------------------| | 本地终端 | daemon | 保持图温热,即时 `review` 和 `impact` | | CI / `--daemon off` | 一次性运行 | 确定性,无残留进程 | ## CLI ### 命令 #### 日常使用 ``` pruneguard # Review your repo or branch (default command) pruneguard scan [paths...] # Full repo scan with detailed findings pruneguard safe-delete # Check if files or exports are safe to remove pruneguard fix-plan # Generate a remediation plan ``` #### 调查 ``` pruneguard impact # Analyze blast radius for a target pruneguard explain # Explain a finding with proof chain ``` #### 策略与治理 ``` pruneguard suggest-rules # Auto-suggest governance rules from graph analysis ``` #### 设置 ``` pruneguard init # Generate pruneguard.json with schema reference pruneguard print-config # Print resolved config ``` #### 调试与迁移 ``` pruneguard debug resolve --from # Trace module resolution pruneguard debug entrypoints # List detected entrypoints pruneguard debug runtime # Print binary/platform info pruneguard daemon start|stop|status # Manage the background daemon ``` ### 全局标志 ``` -c, --config Config file path [default: pruneguard.json] --format text | json | sarif | dot --profile production | development | all --changed-since Only report findings for changed files --focus Filter findings to matching paths --severity Minimum severity: error | warn | info --no-cache Disable incremental cache --no-baseline Disable baseline suppression --max-findings Cap reported findings --require-full-scope Fail if scan would be partial-scope --daemon auto | off | required ``` ### 常见 CLI 工作流 ``` # 审查你的 branch (日常命令) pruneguard --changed-since origin/main # 不受 baseline 影响的完整扫描 (确定性 CI) pruneguard --no-baseline --no-cache scan # 聚焦到 repo 的一个切片 (完整分析,过滤输出) pruneguard --focus "src/**" scan # 如果扫描是部分范围的则失败 pruneguard --require-full-scope scan # 用于 CI pipelines 的 JSON 输出 pruneguard --format json # 用于 GitHub Code Scanning 的 SARIF pruneguard --format sarif scan > results.sarif # Graphviz DOT 输出 pruneguard --format dot scan | dot -Tsvg -o graph.svg # file 的 Blast radius pruneguard impact src/utils/helpers.ts # 解释特定的 finding pruneguard explain unused-export:packages/core:src/old.ts#deprecatedFn # 检查 files 是否可安全删除 pruneguard safe-delete src/utils/old-helper.ts src/legacy/widget.ts # Debug module resolution pruneguard debug resolve ./utils --from src/index.ts ``` ## JS API 每个函数都会调用原生二进制文件并返回解析后的类型化结果。 完整 API 参考请参阅 [docs/js-api.md](docs/js-api.md)。 ### review ``` import { review } from "pruneguard"; const result = await review({ baseRef: "origin/main", noCache: true, }); console.log("Blocking:", result.blockingFindings.length); console.log("Advisory:", result.advisoryFindings.length); console.log("Trust:", JSON.stringify(result.trust)); if (result.blockingFindings.length > 0) { for (const f of result.blockingFindings) { console.error(` [${f.confidence}] ${f.code}: ${f.message}`); } process.exit(1); } ``` ### scan ``` import { scan } from "pruneguard"; const report = await scan({ cwd: "/path/to/repo", // optional, defaults to process.cwd() profile: "production", // optional: "production" | "development" | "all" changedSince: "origin/main", // optional focus: "packages/core/**", // optional noCache: true, // optional noBaseline: true, // optional requireFullScope: true, // optional paths: ["src/lib"], // optional, partial-scope scan }); console.log(report.summary.totalFindings); console.log(report.findings[0].id, report.findings[0].confidence); ``` ### safeDelete ``` import { safeDelete } from "pruneguard"; const result = await safeDelete({ targets: ["src/legacy/old-widget.ts", "src/utils/deprecated-helper.ts"], }); console.log("Safe:", result.safe.map(e => e.target)); console.log("Blocked:", result.blocked.map(e => `${e.target}: ${e.reasons.join(", ")}`)); console.log("Deletion order:", result.deletionOrder); ``` ### fixPlan ``` import { fixPlan } from "pruneguard"; const plan = await fixPlan({ targets: ["unused-export:packages/core:src/old.ts#deprecatedFn"], }); for (const action of plan.actions) { console.log(`${action.kind}: ${action.targets.join(", ")} (${action.risk} risk)`); for (const step of action.steps) { console.log(` - ${step.description}`); } } ``` ### run ``` import { run } from "pruneguard"; // Run arbitrary CLI args const result = await run(["--format", "json", "--no-cache", "scan"]); console.log(result.exitCode); console.log(result.stdout); console.log(result.durationMs); ``` ### binaryPath ``` import { binaryPath } from "pruneguard"; // Resolve the native binary path (for custom integrations) console.log(binaryPath()); // => /path/to/node_modules/@pruneguard/cli-darwin-arm64/bin/pruneguard ``` ### 其他 API 函数 ``` import { impact, explain, suggestRules, loadConfig, schemaPath, scanDot, resolutionInfo, debugResolve, debugEntrypoints, } from "pruneguard"; // Blast radius const blast = await impact({ target: "src/utils/helpers.ts" }); console.log(blast.affectedEntrypoints, blast.affectedFiles); // Proof chain const proof = await explain({ query: "src/old.ts#deprecatedFn" }); console.log(proof.proofs); // Suggest governance rules from graph analysis const rules = await suggestRules(); console.log(rules.suggestedRules); // Load resolved config const config = await loadConfig(); // Path to the bundled JSON schema console.log(schemaPath()); // Graphviz DOT output const dot = await scanDot(); // Binary resolution diagnostics const info = resolutionInfo(); console.log(info.source, info.platform); ``` ### 完整 API 参考 | 函数 | 签名 | 描述 | |---|---|---| | `review` | `(options?) => Promise` | 审查你的仓库或分支 | | `scan` | `(options?) => Promise` | 带有详细发现的全仓库扫描 | | `safeDelete` | `(options) => Promise` | 检查文件或导出是否可以安全移除 | | `fixPlan` | `(options) => Promise` | 生成修复计划 | | `impact` | `(options) => Promise` | 分析目标的波及范围 | | `explain` | `(options) => Promise` | 附带证据链解释发现 | | `suggestRules` | `(options?) => Promise` | 自动建议治理规则 | | `loadConfig` | `(options?) => Promise` | 加载已解析的配置 | | `schemaPath` | `() => string` | 捆绑的配置 JSON schema 路径 | | `binaryPath` | `() => string` | 已解析的原生二进制文件路径 | | `run` | `(args, options?) => Promise` | 运行任意 CLI 参数 | | `scanDot` | `(options?) => Promise` | Graphviz DOT 输出 | ### 错误处理 所有 API 函数在失败时都会抛出 `PruneguardExecutionError`。该错误包含一个 `code` 字段,用于程序化处理: | 代码 | 含义 | |---|---| | `PRUNEGUARD_BINARY_NOT_FOUND` | 无法定位原生二进制文件 | | `PRUNEGUARD_EXECUTION_FAILED` | 二进制文件以意外代码退出 | | `PRUNEGUARD_JSON_PARSE_FAILED` | 二进制文件输出无效 JSON | ``` import { scan, PruneguardExecutionError } from "pruneguard"; try { await scan(); } catch (err) { if (err instanceof PruneguardExecutionError) { console.error(err.code, err.message); console.error("stderr:", err.stderr); } } ``` ## GitHub Actions pruneguard 包含一个可复用的 [GitHub Action](.github/actions/pruneguard/) 用于 CI 集成。有关完整的设置指南,包括基线工作流、SARIF 和 monorepo 策略,请参阅 [docs/ci-integration.md](docs/ci-integration.md)。 ### 分支审查关卡 ``` name: pruneguard on: [pull_request] jobs: review: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 with: fetch-depth: 0 - uses: actions/setup-node@v6 with: node-version: 24 - run: npm install pruneguard - name: Branch review run: npx pruneguard --changed-since origin/main --format json ``` 退出代码 0 表示没有阻断性发现;退出代码 1 表示存在阻断性发现。JSON 输出包含 `blockingFindings` 和 `advisoryFindings` 数组,用于进一步处理。 ### 基线门控 CI 通过在 `main` 分支上保存基线并仅针对新发现进行失败处理,来增量式地采用 pruneguard。 ``` name: pruneguard-baseline on: push: branches: [main] pull_request: jobs: scan: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 with: fetch-depth: 0 - uses: actions/setup-node@v6 with: node-version: 24 - run: npm install pruneguard # On main: save baseline - name: Save baseline if: github.ref == 'refs/heads/main' run: npx pruneguard --no-cache --no-baseline --format json scan > baseline.json - uses: actions/upload-artifact@v6 if: github.ref == 'refs/heads/main' with: name: pruneguard-baseline path: baseline.json # On PRs: compare against baseline - uses: actions/download-artifact@v7 if: github.event_name == 'pull_request' with: name: pruneguard-baseline continue-on-error: true - name: Check for new findings if: github.event_name == 'pull_request' run: | npx pruneguard --no-cache --no-baseline --format json scan > current.json node -e " const fs = require('fs'); if (!fs.existsSync('baseline.json')) { console.log('No baseline found, skipping comparison'); process.exit(0); } const baseline = JSON.parse(fs.readFileSync('baseline.json', 'utf-8')); const current = JSON.parse(fs.readFileSync('current.json', 'utf-8')); const baseIds = new Set(baseline.findings.map(f => f.id)); const newFindings = current.findings.filter(f => !baseIds.has(f.id)); if (newFindings.length > 0) { console.error(newFindings.length + ' new finding(s):'); newFindings.forEach(f => console.error(' ' + f.id + ': ' + f.message)); process.exit(1); } console.log('No new findings relative to baseline.'); " ``` ### 安全删除审查 在自动清理 PR 合并之前,验证标记为移除的候选项是否确实可以安全删除。 ``` name: safe-delete-check on: pull_request: paths: - "scripts/cleanup-*.mjs" jobs: check: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 with: fetch-depth: 0 - uses: actions/setup-node@v6 with: node-version: 24 - run: npm install pruneguard - name: Check deletion safety run: | # Find files deleted in this PR DELETED=$(git diff --name-only --diff-filter=D origin/main...HEAD | grep -E '\.(ts|tsx|js|jsx|mts|mjs)$' || true) if [ -z "$DELETED" ]; then echo "No source files deleted in this PR." exit 0 fi echo "Checking deletion safety for:" echo "$DELETED" npx pruneguard --format json safe-delete $DELETED ``` ## 信任模型 - 全仓库 `scan` 是删除决策的可信模式。 - `--focus` 在完整分析后过滤报告的发现。 - 位置参数 `scan ` 会缩小分析的文件集范围,并作为部分范围/建议性进行报告。 - `--require-full-scope` 将建议性的部分范围死代码扫描转变为硬性失败(退出代码 2)。 - `--no-baseline` 禁用基线自动发现,以实现确定性的 CI、奇偶校验和基准测试。 - 在包含许多未解析说明符的仓库中删除代码前,请使用 `impact` 和 `explain`。 - 发现结果带有 `confidence`(高、中、低)以指示可信度。 ## 配置 大多数仓库无需配置文件即可工作。运行 `pruneguard init` 生成一个仅包含 `$schema` 引用的最小配置,以便编辑器自动补全。 对于需要自定义的仓库: ``` { "$schema": "./node_modules/pruneguard/configuration_schema.json", "workspaces": { "packageManager": "pnpm", "roots": ["apps/*", "packages/*"] }, "analysis": { "unusedExports": "error", "unusedFiles": "warn", "unusedDependencies": "error", "cycles": "warn" } } ``` 完整配置参考请参阅 [docs/config.md](docs/config.md)。 ## 文档 | 指南 | 描述 | |---|---| | [入门指南](docs/getting-started.md) | 从安装到获取首个结果的演练 | | [配置](docs/config.md) | 完整配置参考 | | [CI 集成](docs/ci-integration.md) | GitHub Actions, 基线工作流, SARIF | | [JS API 参考](docs/js-api.md) | 完整的类型化 API 文档 | | [Agent 集成](docs/agent-integration.md) | AI Agent 工作流(review + safe-delete + fix-plan) | | [配方](docs/recipes.md) | 复制粘贴自动化示例 | | [迁移](docs/migration.md) | 从其他工具迁移 | | [架构](docs/architecture.md) | 内部设计和流水线阶段 | | [性能](docs/performance.md) | 性能模型、缓存行为、基准测试 | | [基准测试](docs/benchmarks.md) | 目标延迟和基准测试方法论 | ## 开发 依赖:Rust (stable), Node.js, pnpm, just ``` just build-js # Build the JS wrapper just stage-release # Stage npm packages into .release/ just pack-smoke # End-to-end package install smoke test just smoke-repos # Opt-in real-repo smoke tests just parity # Real-repo parity checks just benchmark CASE=../../repo # Benchmark a single corpus just benchmark-repos # Benchmark all configured corpora ``` 其他有用的命令: ``` just ready # fmt + check + test + lint just build # Release binary just run scan # Run against current directory just schemas # Regenerate shipped schemas just schemas-check # Verify schemas are committed just ci # Full CI pipeline locally ``` ## 许可证 MIT
标签:Agent-first, CMS安全, GNU通用公共许可证, JavaScript, MITM代理, Node.js, Rust, TypeScript, WebSocket, 二进制工具, 云安全监控, 代码审查, 代码清理, 依赖分析, 可视化界面, 守护进程, 安全删除, 安全插件, 开发效率, 影响分析, 数据可视化, 暗色界面, 架构治理, 死代码检测, 网络可观测性, 网络流量审计, 通知系统, 静态分析