From 1553d54ab888a12db9f66d8828d07525093e08fb Mon Sep 17 00:00:00 2001 From: DrBaher Date: Mon, 18 May 2026 18:23:28 +0200 Subject: [PATCH] repo hygiene + docs: issue/PR templates, dependabot, AGENTS MCP section, GETTING_STARTED troubleshooting, examples/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Single PR bundling six small polish items. No code change, no version bump (none of compare-cli's locked surfaces touched). .github/ - ISSUE_TEMPLATE/bug_report.md: structured bug-report template. - ISSUE_TEMPLATE/feature_request.md: structured feature template that reminds reporters of the deterministic-by-design contract and lists sibling-CLI alternatives. - ISSUE_TEMPLATE/config.yml: disables blank issues; surfaces the security-advisory + suite-discussion links. - pull_request_template.md: standardizes the PR body so future contributors and agents alike fill in the same sections (summary, what changes, why this design, test plan, schema impact, version impact). - dependabot.yml: weekly npm bumps grouped by patch/minor for the root package and the mcp/ workspace, plus monthly github-actions bumps. Majors stay separate (pdfjs-dist majors in particular need the CI matrix to validate). AGENTS.md - New "Invoking via MCP" section right above "Failure diagnosis". Documents the three tools, the install incantation, the Claude Desktop / Claude Code config block, and the substantive-drift = success-with- content posture. Cross-cites mcp/README.md and docs/mcp.md. GETTING_STARTED.md - New "Troubleshooting" section above "Next steps". Covers: - The npx-bin-name pitfall (npm 10.x can't auto-resolve a bin when package name and bin name differ; use `-p compare-cli@latest -- compare`). - The scanned-PDF zero-text-extraction case (point users at ocrmypdf; we don't bundle OCR). - --demo's deliberate exit 2 (it's a fixture, not a bug). - The EPEERINVALID warning for compare-cli-mcp@0.1.0 against compare-cli@0.3.0+ (fixed in mcp@0.1.1). - "no agreed round found" — the three-tier resolution. - CI gate patterns and how 0/2/3/4 map to publish-blocking decisions. examples/ - base.md, cand-clean.md, cand-substantive.md, cand-cosmetic.md — four markdown fixtures shaped like a real NDA. Each candidate's exit code matches its filename intent (0 / 2 / 3 respectively). - negotiation.json — canonical nda-review-cli state file shape with status: converged + signoffs populated. Use with --from-negotiation. - README.md — explains the files and shows commands for every public flag against them, including --check, --sarif, --only-clauses, --from-negotiation. Notes that examples/ doesn't ship in the npm tarball (no entry in package.json files). - All four expected exit codes verified locally: compare base.md cand-clean.md → 0 compare base.md cand-substantive.md → 2 compare base.md cand-cosmetic.md → 3 compare --from-negotiation neg cand-clean.md → 0 Tests: 206/206 root + 22/22 mcp (unchanged). Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/ISSUE_TEMPLATE/bug_report.md | 43 ++++++++++++++ .github/ISSUE_TEMPLATE/config.yml | 8 +++ .github/ISSUE_TEMPLATE/feature_request.md | 43 ++++++++++++++ .github/dependabot.yml | 57 ++++++++++++++++++ .github/pull_request_template.md | 56 ++++++++++++++++++ AGENTS.md | 44 ++++++++++++++ GETTING_STARTED.md | 72 +++++++++++++++++++++++ examples/README.md | 68 +++++++++++++++++++++ examples/base.md | 27 +++++++++ examples/cand-clean.md | 27 +++++++++ examples/cand-cosmetic.md | 19 ++++++ examples/cand-substantive.md | 27 +++++++++ examples/negotiation.json | 34 +++++++++++ 13 files changed, 525 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/dependabot.yml create mode 100644 .github/pull_request_template.md create mode 100644 examples/README.md create mode 100644 examples/base.md create mode 100644 examples/cand-clean.md create mode 100644 examples/cand-cosmetic.md create mode 100644 examples/cand-substantive.md create mode 100644 examples/negotiation.json diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..47e86f6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,43 @@ +--- +name: Bug report +about: A reproducible defect in the CLI or its output +title: "bug: " +labels: bug +--- + +## What happened + + + +## What you expected + + + +## Reproduction + + + +```sh +compare path/to/base.docx path/to/cand.pdf --json --why +``` + +```text + +``` + +## Environment + +- compare-cli version: +- Node version: +- OS: +- Install method: + +## Exit code + + + +## Additional context + + diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..44f1ca8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Security vulnerability + url: https://github.com/DrBaher/compare-cli/security/advisories/new + about: Report security issues privately via GitHub's vulnerability reporting (see SECURITY.md). + - name: Suite-level discussion + url: https://cli.drbaher.com + about: The contract-operations CLI suite that compare-cli is part of. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..bb5958d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,43 @@ +--- +name: Feature request +about: A new flag, output mode, or behavior change +title: "feat: " +labels: enhancement +--- + +## The use case + + + +## Proposed shape + + + +```sh +compare base.docx cand.docx --new-flag ARGS +``` + +## Why compare-cli specifically + + + +## Trade-offs + + + +## Related + + diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..2c157c3 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,57 @@ +version: 2 +updates: + # Root compare-cli package — two runtime deps (jszip, pdfjs-dist), no devDeps. + - package-ecosystem: npm + directory: / + schedule: + interval: weekly + day: monday + time: "06:00" + timezone: "UTC" + commit-message: + prefix: deps + labels: + - dependencies + # Group patch + minor bumps so we get one PR per week instead of one per dep. + # Majors stay separate — pdfjs-dist majors in particular need a CI matrix + # check (the Node-engine pin is downstream). + groups: + runtime: + patterns: + - "*" + update-types: + - patch + - minor + + # MCP workspace — runtime dep @modelcontextprotocol/sdk + peer dep compare-cli. + # Peer deps are not auto-bumped by dependabot; the runtime dep is. + - package-ecosystem: npm + directory: /mcp + schedule: + interval: weekly + day: monday + time: "06:00" + timezone: "UTC" + commit-message: + prefix: "deps(mcp)" + labels: + - dependencies + - mcp + groups: + mcp-runtime: + patterns: + - "*" + update-types: + - patch + - minor + + # GitHub Actions versions (uses: actions/...@vX). + - package-ecosystem: github-actions + directory: / + schedule: + interval: monthly + commit-message: + prefix: "ci(actions)" + labels: + - dependencies + - ci diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..d958248 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,56 @@ + + +## Summary + + + +## What changes + + + +- + +## Why this design + + + +## Test plan + +- [ ] `npm test` passes locally +- [ ] `cd mcp && npm test` passes locally (if `mcp/` touched) +- [ ] Manual smoke: +- [ ] CI matrix (Ubuntu × macOS × Node 20/22) green + +## Out of scope + + + +## Schema impact + + + +- [ ] No `--json` shape change +- [ ] AGENTS.md updated +- [ ] COMPARE_SCHEMA.md updated +- [ ] CHANGELOG.md entry added + +## Version impact + + + +- [ ] No bump (infra / docs only) +- [ ] Patch +- [ ] Minor +- [ ] Major (locked-surface change) diff --git a/AGENTS.md b/AGENTS.md index 5c0dc94..d40c8b9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -222,6 +222,50 @@ Useful when the agreement is "byte-perfect after normalization or do nothing" — e.g. financial term sheets where `$1,000` vs `$1000` is in fact meaningful. +## Invoking via MCP + +For agent pipelines that prefer structured tool-calls over shelling out, +`compare-cli-mcp` (separate npm package, lives in `mcp/`) wraps the CLI +as an MCP (Model Context Protocol) server. Three tools, stdio transport, +JSON-first responses. + +```sh +npm install -g compare-cli@^0.3.0 compare-cli-mcp@^0.1.1 +compare-mcp # spawns the server on stdio +``` + +Wire into Claude Desktop / Claude Code: + +```json +{ + "mcpServers": { + "compare-cli": { + "command": "compare-mcp", + "env": { "COMPARE_MCP_BASE_DIR": "/path/to/contract/documents" } + } + } +} +``` + +Tool catalog: + +| MCP tool | CLI equivalent | +|---------------------------|------------------------------------------------------| +| `compare_files` | `compare BASE CANDIDATE [options]` | +| `compare_with_negotiation`| `compare --from-negotiation NEG.json CANDIDATE [...]`| +| `compare_demo` | `compare --demo` | + +`structuredContent` returned by every successful tool call is **byte- +identical** to the `compare --json` shape documented above; agents can +read it without per-transport special-casing. Substantive drift is a +successful tool call (not an MCP error) — same routing logic as the +shell pattern above. Genuine errors (I/O failure, malformed input, no +agreed round) surface as MCP errors with stable codes (`INPUT_NOT_FOUND`, +`PDF_NO_TEXT_LAYER`, `NO_AGREED_ROUND`, etc.). + +Full spec: [`docs/mcp.md`](./docs/mcp.md) (design) and +[`mcp/README.md`](./mcp/README.md) (usage / error catalog). + ## Failure diagnosis | Symptom | Likely cause | Recovery | diff --git a/GETTING_STARTED.md b/GETTING_STARTED.md index 2d10852..de76dd7 100644 --- a/GETTING_STARTED.md +++ b/GETTING_STARTED.md @@ -210,9 +210,81 @@ sign draft.pdf --signer counsel@acme.com Each tool reads stdin / writes stdout / exits with a documented code, so they compose cleanly. +## Troubleshooting + +### `npx compare-cli ... --demo` errors with `sh: compare: command not found` + +The package is `compare-cli`; the installed bin is `compare`. npm 10.x's +`npx` doesn't auto-resolve a bin when the names differ. Use the explicit +form: + +```sh +npx -p compare-cli@latest -- compare --demo +``` + +Or install globally (`npm install -g compare-cli`) and then `compare` is +on `$PATH` directly. Same behavior, less typing. + +### `extracted zero characters from X.pdf` + +The PDF has no text layer — it's a scanned image. compare-cli is +deliberately OCR-free (deterministic + zero-network is the contract; +OCR engines are neither). Run an OCR tool first: + +```sh +ocrmypdf scanned.pdf scanned-ocr.pdf +compare base.docx scanned-ocr.pdf +``` + +`ocrmypdf` is a separate project; install it via your package manager. + +### Exit code 2 from `--demo`, surprised by `--check` exiting non-zero + +`compare --demo` runs against a deliberately-substantive-drift fixture +(the bundled Term clause changed from "two (2) years" to "three (3) +years"). Exit 2 is the contract — the demo demonstrates the gate, not +clean state. Use `compare --version` or `compare --help` if you just +want to confirm the bin runs. + +### `compare-cli-mcp` install: `EPEERINVALID compare-cli@…` + +`compare-cli-mcp@0.1.0` declared `peerDependencies.compare-cli: ^0.2.0` +which doesn't satisfy `compare-cli@0.3.0+`. The warning is cosmetic — +the server functions correctly because the `--json` shape is stable +across compare-cli's v1.x. Upgrading to `compare-cli-mcp@0.1.1+` +widens the peer-dep range and silences the warning. + +### `--from-negotiation: no agreed round found` + +The reader walks every round in `negotiation.json` looking for one of +three signals (in order): + +1. Top-level `status: "converged" | "signed_off" | "finalized"`. +2. A round with `agreed: true`. +3. A round with `clause_status` whose every value is `"agreed"`. + +If none of those is true, the negotiation hasn't converged yet — call +this from compare-cli prematurely and you'll get exit 2 with this +message. Run `nda-review-cli negotiate status` to confirm. + +### CI shows a green `compare` step but my `npm publish` blocks + +If you're using compare-cli inside a publish workflow as a pre-flight +sanity check, remember the exit codes: + +- `0` = safe, publish. +- `2` = substantive drift — usually the right thing to **block** on. +- `3` / `4` = cosmetic / moved — usually safe to publish (the agreement + didn't change), but if you want a strict gate, add `--strict` and + `--strict-cosmetic`. + +The default CI pattern from `AGENTS.md` "Pre-signature gate in a +pipeline" routes these correctly. + ## Next steps - **[COMPARE_SCHEMA.md](./COMPARE_SCHEMA.md)** for the locked v1 contract. - **[AGENTS.md](./AGENTS.md)** for the stable agent-facing surface. - **[ARCHITECTURE.md](./ARCHITECTURE.md)** for how the CLI is shaped. - **[FAQ.md](./FAQ.md)** for common questions. +- **[mcp/README.md](./mcp/README.md)** if you're wiring it into an MCP client. diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..22d3e87 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,68 @@ +# compare-cli examples + +Sample files for trying compare-cli against realistic-shaped contract +inputs without authoring your own. + +> **Not shipped with the npm package** — these live in the repo for +> hands-on exploration, not in the published tarball. Clone the repo or +> grab the raw files from +> [github.com/DrBaher/compare-cli/tree/main/examples](https://github.com/DrBaher/compare-cli/tree/main/examples) +> to use them. + +## Files + +| File | Purpose | +|---|---| +| [`base.md`](./base.md) | The negotiated agreement. Five clauses, plain markdown. | +| [`cand-clean.md`](./cand-clean.md) | Byte-identical to `base.md`. **Exit 0** — safe to sign. | +| [`cand-substantive.md`](./cand-substantive.md) | Term clause changed (two → three years), Confidentiality clause weakened. **Exit 2** — do not sign without review. | +| [`cand-cosmetic.md`](./cand-cosmetic.md) | Same agreement, but with curly quotes, em-dash hyphens, and Unicode non-breaking hyphens swapped in. **Exit 3** — cosmetic-only, the agreement didn't change. | +| [`negotiation.json`](./negotiation.json) | An nda-review-cli state file in the canonical shape (`status: converged`, populated `signoffs`). Use with `--from-negotiation`. | + +## Try it + +After installing the CLI (`npm install -g compare-cli`), from this directory: + +```sh +# Clean — should exit 0 +compare base.md cand-clean.md +echo $? # 0 + +# Substantive drift — exit 2 with intra-clause word diff in the report +compare base.md cand-substantive.md + +# Cosmetic-only — exit 3 +compare base.md cand-cosmetic.md + +# Strict mode — exit 2 even on cosmetic +compare base.md cand-cosmetic.md --strict-cosmetic + +# Reading the agreed text from a negotiation file +compare --from-negotiation negotiation.json cand-clean.md + +# CI-style check (exit code only) +compare base.md cand-substantive.md --check && echo "safe to sign" + +# SARIF output (for GitHub code-scanning) +compare base.md cand-substantive.md --sarif > drift.sarif + +# Filter to specific clauses +compare base.md cand-substantive.md --only-clauses Term --json +compare base.md cand-substantive.md --ignore-clauses Confidentiality +``` + +## Programmatic use (Node) + +```js +import { main, EXIT } from "compare-cli"; + +const code = await main(["examples/base.md", "examples/cand-substantive.md", "--json"]); +process.exit(code); +``` + +## MCP-server use + +If you've installed [`compare-cli-mcp`](../mcp/README.md), the same +files work via tool calls. Set `COMPARE_MCP_BASE_DIR` to this +`examples/` directory to lock the server's read scope down to just these +files. diff --git a/examples/base.md b/examples/base.md new file mode 100644 index 0000000..f89abaf --- /dev/null +++ b/examples/base.md @@ -0,0 +1,27 @@ +## 1. Purpose + +This Mutual Non-Disclosure Agreement (the "Agreement") governs the +exchange of confidential information between Acme Corporation ("Acme") +and Globex Industries ("Globex") for the purpose of evaluating a +potential business transaction. + +## 2. Term + +The term of this Agreement is two (2) years from the Effective Date. + +## 3. Confidentiality Obligations + +Each party shall hold the other's Confidential Information in strict +confidence and shall not disclose it to any third party without the +disclosing party's prior written consent. + +## 4. Notices + +All notices required or permitted under this Agreement shall be in +writing and delivered to the addresses on the cover page. + +## 5. Governing Law + +This Agreement shall be governed by and construed in accordance with +the laws of the State of Delaware, without regard to its +conflict-of-laws principles. diff --git a/examples/cand-clean.md b/examples/cand-clean.md new file mode 100644 index 0000000..f89abaf --- /dev/null +++ b/examples/cand-clean.md @@ -0,0 +1,27 @@ +## 1. Purpose + +This Mutual Non-Disclosure Agreement (the "Agreement") governs the +exchange of confidential information between Acme Corporation ("Acme") +and Globex Industries ("Globex") for the purpose of evaluating a +potential business transaction. + +## 2. Term + +The term of this Agreement is two (2) years from the Effective Date. + +## 3. Confidentiality Obligations + +Each party shall hold the other's Confidential Information in strict +confidence and shall not disclose it to any third party without the +disclosing party's prior written consent. + +## 4. Notices + +All notices required or permitted under this Agreement shall be in +writing and delivered to the addresses on the cover page. + +## 5. Governing Law + +This Agreement shall be governed by and construed in accordance with +the laws of the State of Delaware, without regard to its +conflict-of-laws principles. diff --git a/examples/cand-cosmetic.md b/examples/cand-cosmetic.md new file mode 100644 index 0000000..7e33f08 --- /dev/null +++ b/examples/cand-cosmetic.md @@ -0,0 +1,19 @@ +## 1. Purpose + +This Mutual Non-Disclosure Agreement (the “Agreement”) governs the exchange of confidential information between Acme Corporation (“Acme”) and Globex Industries (“Globex”) for the purpose of evaluating a potential business transaction. + +## 2. Term + +The term of this Agreement is two (2) years from the Effective Date. + +## 3. Confidentiality Obligations + +Each party shall hold the other’s Confidential Information in strict confidence and shall not disclose it to any third party without the disclosing party’s prior written consent. + +## 4. Notices + +All notices required or permitted under this Agreement shall be in writing and delivered to the addresses on the cover page. + +## 5. Governing Law + +This Agreement shall be governed by and construed in accordance with the laws of the State of Delaware, without regard to its conflict-of-laws principles. diff --git a/examples/cand-substantive.md b/examples/cand-substantive.md new file mode 100644 index 0000000..5f274dd --- /dev/null +++ b/examples/cand-substantive.md @@ -0,0 +1,27 @@ +## 1. Purpose + +This Mutual Non-Disclosure Agreement (the "Agreement") governs the +exchange of confidential information between Acme Corporation ("Acme") +and Globex Industries ("Globex") for the purpose of evaluating a +potential business transaction. + +## 2. Term + +The term of this Agreement is three (3) years from the Effective Date. + +## 3. Confidentiality Obligations + +Each party shall hold the other's Confidential Information in strict +confidence and may disclose it to its affiliates and advisors as +reasonably necessary. + +## 4. Notices + +All notices required or permitted under this Agreement shall be in +writing and delivered to the addresses on the cover page. + +## 5. Governing Law + +This Agreement shall be governed by and construed in accordance with +the laws of the State of Delaware, without regard to its +conflict-of-laws principles. diff --git a/examples/negotiation.json b/examples/negotiation.json new file mode 100644 index 0000000..4b596ba --- /dev/null +++ b/examples/negotiation.json @@ -0,0 +1,34 @@ +{ + "negotiation_id": "example-nda-2026-05", + "parties": { + "a": { "name": "Acme Corporation", "address": "1 Acme Way" }, + "b": { "name": "Globex Industries", "address": "100 Globex Plaza" } + }, + "purpose": "Evaluation of a potential business transaction", + "template": "mutual", + "rounds": [ + { + "round": 1, + "proposer": "a", + "text": "## 1. Purpose\n\nThis Mutual Non-Disclosure Agreement (the \"Agreement\") governs the\nexchange of confidential information between Acme Corporation (\"Acme\")\nand Globex Industries (\"Globex\") for the purpose of evaluating a\npotential business transaction.\n\n## 2. Term\n\nThe term of this Agreement is two (2) years from the Effective Date.\n\n## 3. Confidentiality Obligations\n\nEach party shall hold the other's Confidential Information in strict\nconfidence and shall not disclose it to any third party without the\ndisclosing party's prior written consent.\n\n## 4. Notices\n\nAll notices required or permitted under this Agreement shall be in\nwriting and delivered to the addresses on the cover page.\n\n## 5. Governing Law\n\nThis Agreement shall be governed by and construed in accordance with\nthe laws of the State of Delaware, without regard to its\nconflict-of-laws principles.\n", + "text_hash": "sha256:example-hash-1", + "clause_status": { + "purpose": "agreed", + "term": "agreed", + "confidentiality_obligations": "agreed", + "notices": "agreed", + "governing_law": "agreed" + }, + "signed_at": "2026-05-15T14:00:00Z", + "signed_by": "a", + "hash_prev": "", + "hash_self": "sha256:example-hash-1-self" + } + ], + "status": "converged", + "stalemate_counter": 0, + "signoffs": { + "a": "Alice Chen (Acme General Counsel) 2026-05-16T09:30:00Z", + "b": "Bob Martinez (Globex VP Legal) 2026-05-16T11:15:00Z" + } +}