
Contract And Proposal Writer
- 174 installs
- 451 repo stars
- Updated July 21, 2026
- borghei/claude-skills
Draft client SOWs, MSAs, and project proposals with clear deliverables, timelines, assumptions, and pricing tables before signing engagements.
About
Guides Claude to produce polished contracts and client proposals with structured scope, legal-ready clauses, milestone plans, and pricing sections tailored to software and consulting engagements.
- SOW and MSA drafting
- Deliverable and milestone tables
- Assumption and exclusion clauses
- Pricing and payment term blocks
- Revision-ready proposal templates
Contract And Proposal Writer by the numbers
- 174 all-time installs (skills.sh)
- Ranked #271 of 688 Office & Documents skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/borghei/claude-skills --skill contract-and-proposal-writerAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 174 |
|---|---|
| repo stars | ★ 451 |
| Last updated | July 21, 2026 |
| Repository | borghei/claude-skills ↗ |
What it does
Draft client SOWs, MSAs, and project proposals with clear deliverables, timelines, assumptions, and pricing tables before signing engagements.
Files
Contract & Proposal Writer
Tier: POWERFUL Category: Business Growth Tags: contracts, proposals, SOW, NDA, MSA, GDPR, legal templates, freelance
Overview
Generate professional, jurisdiction-aware business documents: freelance contracts, project proposals, statements of work, NDAs, and master service agreements. Outputs structured Markdown with conversion instructions for DOCX and PDF. Covers US (Delaware), EU (GDPR), UK, and DACH (German law) jurisdictions with clause libraries for each.
This is not a substitute for legal counsel. Use these templates as strong starting points. Review with an attorney for engagements over $50K or involving complex IP, equity, or regulatory requirements.
---
Core Capabilities
- Fixed-price and hourly development contracts
- Monthly consulting retainer agreements
- Project proposals with timeline and budget breakdown
- Statements of Work (SOW) with deliverables matrix and acceptance criteria
- NDAs (mutual and one-way)
- Master Service Agreements (MSA) with SOW attachment framework
- SaaS partnership agreements (reseller, referral, white-label, integration)
- GDPR Data Processing Addenda (Art. 28) for EU/DACH
- Jurisdiction-specific clause library (US, EU, UK, DACH)
- Change order and scope management clauses
---
Workflow
Step 1: Requirements Gathering
Gather before drafting:
| Question | Why It Matters |
|---|---|
| Document type? | Contract, proposal, SOW, NDA, MSA |
| Jurisdiction? | US-Delaware, EU, UK, DACH |
| Engagement model? | Fixed-price, hourly, retainer, revenue-share |
| Parties? | Legal names, roles, registered addresses |
| Scope summary? | 1-3 sentences describing the work |
| Total value or rate? | Drives payment terms and liability caps |
| Timeline? | Start date, end date or duration, milestones |
| Special requirements? | IP assignment, white-label, subcontractors, non-compete |
| Personal data involved? | Triggers GDPR DPA requirement in EU/DACH |
Step 2: Template Selection
| Document Type | Engagement Model | Template |
|---|---|---|
| Dev contract | Fixed-price | Template A: Fixed-Price Development |
| Dev contract | Hourly/Retainer | Template B: Consulting Retainer |
| Partnership | Revenue-share | Template C: SaaS Partnership |
| NDA | Mutual | Template NDA-M |
| NDA | One-way (discloser/recipient) | Template NDA-OW |
| SOW | Any | Template SOW (attaches to MSA or standalone) |
| Proposal | Any | Template P: Project Proposal |
Step 3: Generate & Fill
Fill all [BRACKETED] placeholders. Flag missing information as [REQUIRED - description]. Never leave blanks -- an incomplete contract is more dangerous than no contract.
Step 4: Review Checklist
Before sending any generated document:
- [ ] All
[BRACKETED]placeholders filled - [ ] Correct jurisdiction selected and consistent throughout
- [ ] Payment terms match engagement model
- [ ] IP clause matches jurisdiction requirements
- [ ] Liability cap is reasonable (typically 1x-3x contract value)
- [ ] Termination clauses include both for-cause and for-convenience
- [ ] DPA included if personal data is processed (EU/DACH mandatory)
- [ ] Force majeure clause included for engagements over 3 months
- [ ] Change order process defined for fixed-price contracts
- [ ] Acceptance criteria defined for each deliverable
---
Clause Library
Payment Terms
| Model | Standard Terms | Risk Notes |
|---|---|---|
| Fixed-price | 50% upfront, 25% at beta, 25% at acceptance | Best for defined scope |
| Hourly | Net-30, monthly invoicing | Requires time tracking |
| Retainer | Monthly prepaid, 1st of month | Include overflow rate |
| Milestone | Per-milestone invoicing | Define milestones precisely |
| Revenue-share | Net-30 after month close, minimum threshold | Requires audit rights |
Late payment: 1.5% per month (US standard), up to statutory maximum in EU/DACH.
Intellectual Property
| Jurisdiction | Default IP Ownership | Key Requirement |
|---|---|---|
| US (Delaware) | Work-for-hire doctrine | Must be in writing, 9 qualifying categories |
| EU | Author retains moral rights | Separate written assignment needed |
| UK | Employer owns (if employee) | Contractor: explicit assignment required |
| DACH (Germany) | Author retains Urheberrecht permanently | Must transfer Nutzungsrechte (usage rights) explicitly |
Pre-existing IP: Always carve out pre-existing tools, libraries, and frameworks. Grant client a perpetual, royalty-free license to use pre-existing IP as embedded in deliverables.
Portfolio rights: Developer retains right to display work in portfolio unless client requests confidentiality in writing within 30 days.
Liability
| Risk Level | Cap | When to Use |
|---|---|---|
| Standard | 1x total fees paid | Most projects |
| High-risk | 3x total fees paid | Critical infrastructure, regulated industries |
| Uncapped (mutual) | No cap, mutual indemnification | Enterprise partnerships |
Always exclude: Indirect, incidental, and consequential damages (both parties).
Termination
| Type | Notice Period | Financial Treatment |
|---|---|---|
| For cause | 14-day cure period | Pay for work completed |
| For convenience (client) | 30 days written notice | Pay for work completed + 10-20% of remaining value |
| For convenience (either) | 30-60 days | Pay for work completed |
| Immediate (material breach uncured) | 7 days post-notice | Pro-rata payment |
Confidentiality
- Standard term: 3 years post-termination
- Trade secrets: Perpetual (as long as information remains a trade secret)
- Return/destruction: All confidential materials returned or certified destroyed within 30 days of termination
- Exceptions: Publicly known, independently developed, received from third party, required by law
Dispute Resolution
| Jurisdiction | Recommended Forum | Rules |
|---|---|---|
| US | Binding arbitration | AAA Commercial Rules, Delaware venue |
| EU | ICC arbitration or local courts | ICC Rules, venue in capital of governing law |
| UK | LCIA arbitration, London | LCIA Rules, English law |
| DACH | DIS arbitration or Landgericht | DIS Rules, German law |
---
Jurisdiction-Specific Requirements
US (Delaware)
- Governing law: State of Delaware (most business-friendly)
- Work-for-hire doctrine applies (Copyright Act 101)
- Non-compete: Enforceable with reasonable scope/duration/geography
- Electronic signatures: Valid under ESIGN Act and UETA
EU (GDPR)
- Data Processing Addendum required if handling personal data
- IP assignment may require separate written deed in some member states
- Consumer protection laws may override contract terms for B2C
- Right to withdraw within 14 days for distance contracts (B2C)
UK (Post-Brexit)
- Governed by English law (most common choice)
- IP: Patents Act 1977, CDPA 1988
- UK GDPR (post-Brexit equivalent) applies for data processing
- Electronic signatures: Valid under Electronic Communications Act 2000
DACH (Germany / Austria / Switzerland)
- BGB (Buergerliches Gesetzbuch) governs contracts
- Schriftform (written form) required for certain clauses (para 126 BGB)
- Author always retains moral rights (Urheberpersoernlichkeitsrecht) -- cannot be transferred
- Must explicitly transfer Nutzungsrechte (usage rights) with scope and duration
- Non-competes: Maximum 2 years, compensation required (para 74 HGB)
- DSGVO (German GDPR implementation) mandatory for personal data
- Kuendigungsfristen: Statutory notice periods apply and cannot be shortened below minimum
---
GDPR Data Processing Addendum (Template Block)
Required for any EU/DACH engagement involving personal data:
## DATA PROCESSING ADDENDUM (Art. 28 GDPR/DSGVO)
Controller: [CLIENT LEGAL NAME]
Processor: [SERVICE PROVIDER LEGAL NAME]
### Processing Scope
Processor processes personal data solely to perform services under the Agreement.
### Categories of Data Subjects
[End users / Employees / Customers of Controller]
### Categories of Personal Data
[Names, email addresses, usage data, IP addresses, payment information]
### Processing Duration
Term of the Agreement. Deletion within [30] days of termination.
### Processor Obligations
1. Process only on Controller's documented instructions
2. Ensure authorized persons committed to confidentiality
3. Implement Art. 32 technical and organizational measures
4. Assist with data subject rights requests within [10] business days
5. Notify Controller of personal data breach within [72] hours
6. No sub-processors without prior written consent
7. Delete or return all personal data upon termination
8. Make available information to demonstrate compliance
### Current Sub-Processors
| Sub-Processor | Location | Purpose |
|--------------|----------|---------|
| [AWS/GCP/Azure] | [Region] | Cloud infrastructure |
| [Stripe] | [US/EU] | Payment processing |
### Cross-Border Transfers
Transfers outside EEA: [ ] Standard Contractual Clauses [ ] Adequacy Decision [ ] BCRs---
Project Proposal Template (Template P)
# PROJECT PROPOSAL
**Prepared for:** [Client Name]
**Prepared by:** [Your Name / Company]
**Date:** [Date]
**Valid until:** [Date + 30 days]
---
## Executive Summary
[2-3 sentences: what you will build, the business problem it solves, and the expected outcome]
## Understanding of Requirements
[Demonstrate you understand the client's problem. Reference their specific situation, not generic boilerplate]
## Proposed Solution
[Technical approach, architecture overview, technology choices with rationale]
## Scope of Work
### In Scope
- [Deliverable 1: specific description]
- [Deliverable 2: specific description]
- [Deliverable 3: specific description]
### Out of Scope
- [Explicitly list what is NOT included -- prevents scope creep]
### Assumptions
- [Client provides X by Y date]
- [Access to Z system will be available]
## Timeline
| Phase | Deliverables | Duration | Dates |
|-------|-------------|----------|-------|
| Discovery | Requirements document, architecture plan | 1 week | [Dates] |
| Development | Core features, API integration | 4 weeks | [Dates] |
| Testing | QA, UAT, bug fixes | 1 week | [Dates] |
| Launch | Deployment, monitoring, handoff | 1 week | [Dates] |
## Investment
| Item | Cost |
|------|------|
| Discovery & Planning | [Amount] |
| Development | [Amount] |
| Testing & QA | [Amount] |
| Project Management | [Amount] |
| **Total** | **[Amount]** |
### Payment Schedule
- 50% upon contract signing
- 25% at beta delivery
- 25% upon final acceptance
## Why Us
[2-3 concrete differentiators. Reference relevant experience, not just claims]
## Next Steps
1. Review and approve this proposal
2. Sign agreement (attached)
3. Kick-off meeting within [5] business days---
Document Conversion
# Markdown to DOCX (basic)
pandoc contract.md -o contract.docx --reference-doc=template.docx
# With numbered sections (legal style)
pandoc contract.md -o contract.docx --number-sections -V fontsize=11pt
# Markdown to PDF (via LaTeX)
pandoc contract.md -o contract.pdf -V geometry:margin=1in -V fontsize=11pt
# Batch convert all contracts
for f in contracts/*.md; do
pandoc "$f" -o "${f%.md}.docx" --reference-doc=template.docx
done---
Common Pitfalls
| Pitfall | Consequence | Prevention |
|---|---|---|
| Missing IP assignment language | Unclear ownership, disputes | Always include explicit IP clause per jurisdiction |
| Vague acceptance criteria | Endless revision cycles | Define "accepted" = written sign-off within X days |
| No change order process | Scope creep on fixed-price | Include change order clause with pricing mechanism |
| Jurisdiction mismatch | Unenforceable clauses | Match governing law to where parties operate |
| Missing liability cap | Unlimited exposure | Always cap liability at 1-3x contract value |
| Oral amendments | Unenforceable modifications | Require written amendments signed by both parties |
| No DPA for EU data | GDPR violation, up to 4% global revenue fine | Always include DPA when processing EU personal data |
| Missing force majeure | No protection against unforeseeable events | Include for engagements over 3 months |
---
Best Practices
1. Use milestone payments over net-30 for projects over $10K -- reduces cash flow risk for both parties 2. Always include a change order clause in fixed-price contracts 3. For DACH: include Schriftformklausel (written form clause) explicitly 4. Define response time SLAs in retainer agreements (e.g., 4h urgent / 24h normal) 5. Keep templates in version control; review annually as laws change 6. For NDAs: always specify return/destruction of confidential materials on termination 7. Include a survival clause -- specify which clauses survive termination (confidentiality, IP, liability) 8. For EU/DACH: check if consumer protection laws apply (B2C engagements have additional requirements)
---
Related Skills
| Skill | Use When |
|---|---|
| ceo-advisor | Strategic decisions about partnerships and business models |
| cfo-advisor | Financial terms, pricing strategy, revenue recognition |
| launch-strategy | Contract timing around product launches |
---
Tool Reference
1. contract_clause_checker.py
Purpose: Validate a contract document (as structured JSON) against required clauses for a given jurisdiction and engagement type.
python scripts/contract_clause_checker.py contract.json --jurisdiction us-delaware
python scripts/contract_clause_checker.py contract.json --jurisdiction eu --json| Flag | Required | Description |
|---|---|---|
contract.json | Yes | JSON file with contract clauses and metadata |
--jurisdiction | No | Jurisdiction to check against: us-delaware, eu, uk, dach (default: us-delaware) |
--type | No | Contract type: fixed-price, hourly, retainer, nda, msa (default: fixed-price) |
--json | No | Output results as JSON |
2. proposal_cost_estimator.py
Purpose: Generate a project cost estimate with phase breakdown, payment schedule, and margin analysis.
python scripts/proposal_cost_estimator.py --hourly-rate 150 --hours 200 --phases 4
python scripts/proposal_cost_estimator.py --hourly-rate 150 --hours 200 --phases 4 --json| Flag | Required | Description |
|---|---|---|
--hourly-rate | Yes | Hourly rate in dollars |
--hours | Yes | Estimated total hours |
--phases | No | Number of project phases (default: 3) |
--margin | No | Desired profit margin percentage (default: 20) |
--currency | No | Currency code (default: USD) |
--json | No | Output results as JSON |
3. contract_comparison_analyzer.py
Purpose: Compare two contract versions and identify differences in key clauses, payment terms, and risk areas.
python scripts/contract_comparison_analyzer.py contract_v1.json contract_v2.json
python scripts/contract_comparison_analyzer.py contract_v1.json contract_v2.json --json| Flag | Required | Description |
|---|---|---|
contract_v1.json | Yes | JSON file with first contract version |
contract_v2.json | Yes | JSON file with second contract version |
--json | No | Output results as JSON |
---
Troubleshooting
| Problem | Likely Cause | Solution |
|---|---|---|
| Placeholders left in final document | Rushed filling process | Use contract_clause_checker.py to scan for unfilled [BRACKETED] placeholders before sending |
| IP clause is unenforceable in EU/DACH | Using US work-for-hire language in EU context | Switch to explicit Nutzungsrechte transfer for DACH; use separate written assignment deed for EU |
| Client disputes scope after signing | Vague acceptance criteria or missing change order process | Define "accepted" = written sign-off within X business days; include change order clause with pricing mechanism |
| Payment disputes on hourly contracts | No time tracking requirement or unclear invoicing terms | Specify time tracking tool, invoicing frequency (monthly), and payment terms (net-30) in the contract |
| GDPR non-compliance penalty risk | Missing DPA for EU/DACH engagements involving personal data | Always include Art. 28 DPA when processing EU personal data; use the template block in this skill |
| Contract fails legal review | Jurisdiction mismatch or missing mandatory clauses | Run contract_clause_checker.py against the target jurisdiction before legal review |
---
Success Criteria
- All [BRACKETED] placeholders filled before document delivery
- Correct jurisdiction selected and consistent throughout (verified by contract_clause_checker.py)
- Payment terms match engagement model with clear invoicing cadence
- IP clause matches jurisdiction requirements (work-for-hire for US, Nutzungsrechte for DACH)
- Liability cap set at 1-3x contract value with consequential damages excluded
- DPA included for all EU/DACH engagements involving personal data
- Change order process defined for all fixed-price contracts
---
Scope & Limitations
- In scope: Contract templates, proposal generation, clause libraries, jurisdiction-specific compliance, document comparison, cost estimation
- Out of scope: Legal advice, contract negotiation strategy, litigation support, regulatory filings
- Not legal counsel: These templates are starting points; review with an attorney for engagements over $50K or involving complex IP, equity, or regulatory requirements
- Jurisdiction coverage: US (Delaware), EU (general), UK, DACH (Germany/Austria/Switzerland); other jurisdictions may require additional legal review
- Currency: Cost estimator defaults to USD; adjust for local currency in international engagements
---
Integration Points
- ceo-advisor -- Strategic decisions about partnership structures and business models that drive contract type selection
- cfo-advisor -- Financial terms, revenue recognition, and pricing strategy that inform payment schedule and margin targets
- customer-success-manager -- SOW and MSA structures for customer engagements; renewal terms feed into CS workflows
- pricing-strategy -- When proposal pricing needs strategic positioning against competitors or market rates
- revenue-operations -- Contract values and payment schedules feed into pipeline forecasting and revenue recognition
#!/usr/bin/env python3
"""
Contract Clause Checker
Validate a contract document against required clauses for a given
jurisdiction and engagement type. Identifies missing clauses, incomplete
sections, and jurisdiction-specific compliance gaps.
Usage:
python contract_clause_checker.py contract.json --jurisdiction us-delaware
python contract_clause_checker.py contract.json --jurisdiction eu --json
"""
import argparse
import json
import sys
REQUIRED_CLAUSES = {
"fixed-price": [
{"clause": "parties", "label": "Parties and legal names", "all_jurisdictions": True},
{"clause": "scope_of_work", "label": "Scope of Work / Deliverables", "all_jurisdictions": True},
{"clause": "acceptance_criteria", "label": "Acceptance criteria with sign-off process", "all_jurisdictions": True},
{"clause": "payment_terms", "label": "Payment terms and schedule", "all_jurisdictions": True},
{"clause": "ip_assignment", "label": "Intellectual property assignment", "all_jurisdictions": True},
{"clause": "pre_existing_ip", "label": "Pre-existing IP carve-out", "all_jurisdictions": True},
{"clause": "confidentiality", "label": "Confidentiality / NDA clause", "all_jurisdictions": True},
{"clause": "liability_cap", "label": "Liability limitation", "all_jurisdictions": True},
{"clause": "termination", "label": "Termination for cause and convenience", "all_jurisdictions": True},
{"clause": "change_order", "label": "Change order process", "all_jurisdictions": True},
{"clause": "governing_law", "label": "Governing law and dispute resolution", "all_jurisdictions": True},
{"clause": "force_majeure", "label": "Force majeure", "all_jurisdictions": True},
{"clause": "warranties", "label": "Warranties and representations", "all_jurisdictions": True},
],
"hourly": [
{"clause": "parties", "label": "Parties and legal names", "all_jurisdictions": True},
{"clause": "scope_of_work", "label": "Scope of Work / Services description", "all_jurisdictions": True},
{"clause": "rate_schedule", "label": "Rate schedule and invoicing terms", "all_jurisdictions": True},
{"clause": "time_tracking", "label": "Time tracking requirements", "all_jurisdictions": True},
{"clause": "payment_terms", "label": "Payment terms (net-30 etc.)", "all_jurisdictions": True},
{"clause": "ip_assignment", "label": "IP assignment", "all_jurisdictions": True},
{"clause": "confidentiality", "label": "Confidentiality", "all_jurisdictions": True},
{"clause": "liability_cap", "label": "Liability limitation", "all_jurisdictions": True},
{"clause": "termination", "label": "Termination with notice period", "all_jurisdictions": True},
{"clause": "governing_law", "label": "Governing law", "all_jurisdictions": True},
],
"retainer": [
{"clause": "parties", "label": "Parties and legal names", "all_jurisdictions": True},
{"clause": "services_description", "label": "Services description", "all_jurisdictions": True},
{"clause": "retainer_amount", "label": "Monthly retainer amount", "all_jurisdictions": True},
{"clause": "hours_included", "label": "Hours included and overflow rate", "all_jurisdictions": True},
{"clause": "payment_terms", "label": "Payment terms (prepaid monthly)", "all_jurisdictions": True},
{"clause": "sla", "label": "Response time SLAs", "all_jurisdictions": True},
{"clause": "ip_assignment", "label": "IP assignment", "all_jurisdictions": True},
{"clause": "confidentiality", "label": "Confidentiality", "all_jurisdictions": True},
{"clause": "termination", "label": "Termination with notice period", "all_jurisdictions": True},
{"clause": "governing_law", "label": "Governing law", "all_jurisdictions": True},
],
"nda": [
{"clause": "parties", "label": "Parties and legal names", "all_jurisdictions": True},
{"clause": "definition", "label": "Definition of confidential information", "all_jurisdictions": True},
{"clause": "obligations", "label": "Obligations of receiving party", "all_jurisdictions": True},
{"clause": "exceptions", "label": "Exceptions to confidentiality", "all_jurisdictions": True},
{"clause": "term", "label": "Term and duration", "all_jurisdictions": True},
{"clause": "return_destruction", "label": "Return or destruction of materials", "all_jurisdictions": True},
{"clause": "governing_law", "label": "Governing law", "all_jurisdictions": True},
],
"msa": [
{"clause": "parties", "label": "Parties and legal names", "all_jurisdictions": True},
{"clause": "master_terms", "label": "Master terms and conditions", "all_jurisdictions": True},
{"clause": "sow_framework", "label": "SOW attachment framework", "all_jurisdictions": True},
{"clause": "payment_terms", "label": "General payment terms", "all_jurisdictions": True},
{"clause": "ip_assignment", "label": "IP assignment (default for all SOWs)", "all_jurisdictions": True},
{"clause": "confidentiality", "label": "Confidentiality", "all_jurisdictions": True},
{"clause": "liability_cap", "label": "Liability limitation", "all_jurisdictions": True},
{"clause": "termination", "label": "Termination of master and individual SOWs", "all_jurisdictions": True},
{"clause": "governing_law", "label": "Governing law", "all_jurisdictions": True},
{"clause": "amendments", "label": "Amendment process (written only)", "all_jurisdictions": True},
],
}
JURISDICTION_EXTRAS = {
"eu": [
{"clause": "dpa", "label": "GDPR Data Processing Addendum (Art. 28)", "condition": "personal_data"},
{"clause": "right_to_withdraw", "label": "Right to withdraw (14-day, B2C only)", "condition": "b2c"},
{"clause": "ip_deed", "label": "Separate IP assignment deed", "condition": "always"},
],
"dach": [
{"clause": "dpa", "label": "DSGVO Data Processing Addendum", "condition": "personal_data"},
{"clause": "nutzungsrechte", "label": "Explicit Nutzungsrechte (usage rights) transfer", "condition": "always"},
{"clause": "schriftformklausel", "label": "Schriftformklausel (written form clause)", "condition": "always"},
{"clause": "kuendigungsfristen", "label": "Statutory notice periods (Kuendigungsfristen)", "condition": "always"},
{"clause": "moral_rights", "label": "Acknowledgment of Urheberpersoernlichkeitsrecht (cannot be transferred)", "condition": "always"},
],
"uk": [
{"clause": "uk_gdpr", "label": "UK GDPR compliance for data processing", "condition": "personal_data"},
{"clause": "cdpa", "label": "CDPA 1988 IP provisions", "condition": "always"},
],
"us-delaware": [
{"clause": "work_for_hire", "label": "Work-for-hire declaration (Copyright Act 101)", "condition": "always"},
{"clause": "esign_compliance", "label": "ESIGN Act / UETA compliance for e-signatures", "condition": "always"},
],
}
def check_contract(data: dict, jurisdiction: str, contract_type: str) -> dict:
"""Check contract against required clauses."""
clauses_present = data.get("clauses", {})
metadata = data.get("metadata", {})
involves_personal_data = metadata.get("personal_data", False)
is_b2c = metadata.get("b2c", False)
# Check base clauses
base_required = REQUIRED_CLAUSES.get(contract_type, REQUIRED_CLAUSES["fixed-price"])
results = []
passed = 0
for req in base_required:
present = bool(clauses_present.get(req["clause"]))
if present:
passed += 1
results.append({
"clause": req["clause"],
"label": req["label"],
"present": present,
"required": True,
"source": "base",
})
# Check jurisdiction extras
extras = JURISDICTION_EXTRAS.get(jurisdiction, [])
for extra in extras:
applicable = True
if extra["condition"] == "personal_data" and not involves_personal_data:
applicable = False
if extra["condition"] == "b2c" and not is_b2c:
applicable = False
if applicable:
present = bool(clauses_present.get(extra["clause"]))
if present:
passed += 1
results.append({
"clause": extra["clause"],
"label": extra["label"],
"present": present,
"required": True,
"source": f"jurisdiction:{jurisdiction}",
})
# Check for unfilled placeholders
placeholder_warnings = []
for key, value in clauses_present.items():
if isinstance(value, str) and "[" in value and "]" in value:
placeholder_warnings.append({
"clause": key,
"warning": f"Contains unfilled placeholder: {value[:80]}",
})
total_required = len(results)
missing = [r for r in results if not r["present"]]
compliance_pct = (passed / total_required * 100) if total_required > 0 else 0
if compliance_pct >= 95:
status = "COMPLIANT"
elif compliance_pct >= 75:
status = "NEEDS_REVIEW"
else:
status = "NON_COMPLIANT"
return {
"contract_type": contract_type,
"jurisdiction": jurisdiction,
"status": status,
"compliance_pct": round(compliance_pct, 1),
"clauses_checked": total_required,
"clauses_present": passed,
"clauses_missing": len(missing),
"results": results,
"missing_clauses": missing,
"placeholder_warnings": placeholder_warnings,
"recommendations": _generate_recommendations(missing, jurisdiction, placeholder_warnings),
}
def _generate_recommendations(missing: list, jurisdiction: str, placeholders: list) -> list:
"""Generate recommendations."""
recs = []
for m in missing:
recs.append({
"priority": "HIGH",
"action": f"Add missing clause: {m['label']}",
"source": m["source"],
})
if placeholders:
recs.append({
"priority": "CRITICAL",
"action": f"{len(placeholders)} unfilled placeholder(s) found. Fill all [BRACKETED] values before sending.",
"source": "validation",
})
return recs
def format_text(result: dict) -> str:
"""Format results as human-readable text."""
lines = []
lines.append("=" * 60)
lines.append(f"CONTRACT CLAUSE CHECKER")
lines.append("=" * 60)
lines.append(f"\nType: {result['contract_type']}")
lines.append(f"Jurisdiction: {result['jurisdiction']}")
lines.append(f"Status: {result['status']}")
lines.append(f"Compliance: {result['compliance_pct']}% ({result['clauses_present']}/{result['clauses_checked']})")
lines.append(f"\n--- Clause Checklist ---")
for r in result["results"]:
status = "[OK] " if r["present"] else "[MISS]"
lines.append(f" {status} {r['label']}")
if result["placeholder_warnings"]:
lines.append(f"\n--- Placeholder Warnings ---")
for pw in result["placeholder_warnings"]:
lines.append(f" [WARN] {pw['clause']}: {pw['warning']}")
if result["recommendations"]:
lines.append(f"\n--- Recommendations ---")
for r in result["recommendations"]:
lines.append(f"[{r['priority']}] {r['action']}")
lines.append("")
return "\n".join(lines)
def main():
parser = argparse.ArgumentParser(
description="Validate contract against required clauses for jurisdiction and type."
)
parser.add_argument("input_file", help="JSON file with contract clauses and metadata")
parser.add_argument("--jurisdiction", choices=["us-delaware", "eu", "uk", "dach"],
default="us-delaware", help="Jurisdiction (default: us-delaware)")
parser.add_argument("--type", choices=["fixed-price", "hourly", "retainer", "nda", "msa"],
default="fixed-price", help="Contract type (default: fixed-price)")
parser.add_argument("--json", action="store_true", help="Output results as JSON")
args = parser.parse_args()
try:
with open(args.input_file, "r") as f:
data = json.load(f)
except FileNotFoundError:
print(f"Error: File not found: {args.input_file}", file=sys.stderr)
sys.exit(1)
except json.JSONDecodeError as e:
print(f"Error: Invalid JSON in {args.input_file}: {e}", file=sys.stderr)
sys.exit(1)
result = check_contract(data, args.jurisdiction, args.type)
if args.json:
print(json.dumps(result, indent=2))
else:
print(format_text(result))
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
Contract Comparison Analyzer
Compare two contract versions and identify differences in key clauses,
payment terms, and risk areas. Useful for reviewing redlines and
tracking changes between negotiation rounds.
Usage:
python contract_comparison_analyzer.py contract_v1.json contract_v2.json
python contract_comparison_analyzer.py contract_v1.json contract_v2.json --json
"""
import argparse
import json
import sys
KEY_CLAUSES = [
"parties", "scope_of_work", "deliverables", "acceptance_criteria",
"payment_terms", "payment_schedule", "ip_assignment", "pre_existing_ip",
"confidentiality", "confidentiality_term", "liability_cap",
"termination_for_cause", "termination_for_convenience", "notice_period",
"governing_law", "dispute_resolution", "force_majeure",
"change_order", "warranties", "indemnification", "non_compete",
"non_solicitation", "dpa", "insurance",
]
RISK_INDICATORS = {
"liability_cap": {"field": "liability_cap", "risk": "Liability cap changed -- review exposure"},
"indemnification": {"field": "indemnification", "risk": "Indemnification terms changed -- may shift risk"},
"termination_for_convenience": {"field": "termination_for_convenience", "risk": "Termination convenience changed -- check notice and financial terms"},
"ip_assignment": {"field": "ip_assignment", "risk": "IP assignment changed -- verify ownership is clear"},
"non_compete": {"field": "non_compete", "risk": "Non-compete added or modified -- check scope and duration"},
"governing_law": {"field": "governing_law", "risk": "Governing law changed -- may affect enforceability"},
}
def compare_contracts(v1: dict, v2: dict) -> dict:
"""Compare two contract versions."""
v1_clauses = v1.get("clauses", {})
v2_clauses = v2.get("clauses", {})
v1_meta = v1.get("metadata", {})
v2_meta = v2.get("metadata", {})
changes = []
additions = []
removals = []
unchanged = []
all_keys = set(list(v1_clauses.keys()) + list(v2_clauses.keys()))
for key in sorted(all_keys):
v1_val = v1_clauses.get(key)
v2_val = v2_clauses.get(key)
if v1_val is None and v2_val is not None:
additions.append({
"clause": key,
"new_value": _summarize(v2_val),
"is_key_clause": key in KEY_CLAUSES,
})
elif v1_val is not None and v2_val is None:
removals.append({
"clause": key,
"old_value": _summarize(v1_val),
"is_key_clause": key in KEY_CLAUSES,
})
elif v1_val != v2_val:
changes.append({
"clause": key,
"old_value": _summarize(v1_val),
"new_value": _summarize(v2_val),
"is_key_clause": key in KEY_CLAUSES,
})
else:
unchanged.append(key)
# Risk assessment
risk_flags = []
for change in changes:
if change["clause"] in RISK_INDICATORS:
ri = RISK_INDICATORS[change["clause"]]
risk_flags.append({
"clause": change["clause"],
"risk": ri["risk"],
"old_value": change["old_value"],
"new_value": change["new_value"],
"severity": "HIGH",
})
for removal in removals:
if removal["is_key_clause"]:
risk_flags.append({
"clause": removal["clause"],
"risk": f"Key clause '{removal['clause']}' was removed -- this may create exposure",
"old_value": removal["old_value"],
"new_value": "(removed)",
"severity": "HIGH",
})
for addition in additions:
if addition["clause"] in ["non_compete", "non_solicitation", "indemnification"]:
risk_flags.append({
"clause": addition["clause"],
"risk": f"New clause '{addition['clause']}' was added -- review carefully",
"old_value": "(not present)",
"new_value": addition["new_value"],
"severity": "MEDIUM",
})
return {
"version_1": v1_meta.get("version", "v1"),
"version_2": v2_meta.get("version", "v2"),
"summary": {
"total_clauses_compared": len(all_keys),
"changes": len(changes),
"additions": len(additions),
"removals": len(removals),
"unchanged": len(unchanged),
"risk_flags": len(risk_flags),
},
"changes": changes,
"additions": additions,
"removals": removals,
"risk_flags": risk_flags,
"recommendations": _generate_recommendations(changes, additions, removals, risk_flags),
}
def _summarize(value) -> str:
"""Summarize a clause value for display."""
if isinstance(value, str):
return value[:100] + "..." if len(value) > 100 else value
elif isinstance(value, dict):
return json.dumps(value)[:100]
elif isinstance(value, list):
return f"[{len(value)} items]"
return str(value)
def _generate_recommendations(changes: list, additions: list,
removals: list, risks: list) -> list:
"""Generate recommendations."""
recs = []
if risks:
recs.append({
"priority": "HIGH",
"recommendation": f"{len(risks)} risk flag(s) detected. Review each flagged clause before signing.",
})
if removals:
key_removals = [r for r in removals if r["is_key_clause"]]
if key_removals:
recs.append({
"priority": "HIGH",
"recommendation": f"{len(key_removals)} key clause(s) removed: {', '.join(r['clause'] for r in key_removals)}. Ensure this is intentional.",
})
if not changes and not additions and not removals:
recs.append({
"priority": "INFO",
"recommendation": "No differences found between versions.",
})
return recs
def format_text(result: dict) -> str:
"""Format results as human-readable text."""
lines = []
lines.append("=" * 60)
lines.append(f"CONTRACT COMPARISON: {result['version_1']} vs {result['version_2']}")
lines.append("=" * 60)
s = result["summary"]
lines.append(f"\nClauses Compared: {s['total_clauses_compared']}")
lines.append(f"Changes: {s['changes']}, Additions: {s['additions']}, Removals: {s['removals']}, Unchanged: {s['unchanged']}")
lines.append(f"Risk Flags: {s['risk_flags']}")
if result["changes"]:
lines.append(f"\n--- Changes ---")
for c in result["changes"]:
key_marker = " [KEY]" if c["is_key_clause"] else ""
lines.append(f" {c['clause']}{key_marker}:")
lines.append(f" OLD: {c['old_value']}")
lines.append(f" NEW: {c['new_value']}")
if result["additions"]:
lines.append(f"\n--- Additions ---")
for a in result["additions"]:
key_marker = " [KEY]" if a["is_key_clause"] else ""
lines.append(f" + {a['clause']}{key_marker}: {a['new_value']}")
if result["removals"]:
lines.append(f"\n--- Removals ---")
for r in result["removals"]:
key_marker = " [KEY]" if r["is_key_clause"] else ""
lines.append(f" - {r['clause']}{key_marker}: {r['old_value']}")
if result["risk_flags"]:
lines.append(f"\n--- Risk Flags ---")
for rf in result["risk_flags"]:
lines.append(f" [{rf['severity']}] {rf['clause']}: {rf['risk']}")
if result["recommendations"]:
lines.append(f"\n--- Recommendations ---")
for r in result["recommendations"]:
lines.append(f"[{r['priority']}] {r['recommendation']}")
lines.append("")
return "\n".join(lines)
def main():
parser = argparse.ArgumentParser(
description="Compare two contract versions and identify differences and risk areas."
)
parser.add_argument("file_v1", help="JSON file with first contract version")
parser.add_argument("file_v2", help="JSON file with second contract version")
parser.add_argument("--json", action="store_true", help="Output results as JSON")
args = parser.parse_args()
try:
with open(args.file_v1, "r") as f:
v1 = json.load(f)
with open(args.file_v2, "r") as f:
v2 = json.load(f)
except FileNotFoundError as e:
print(f"Error: {e}", file=sys.stderr)
sys.exit(1)
except json.JSONDecodeError as e:
print(f"Error: Invalid JSON: {e}", file=sys.stderr)
sys.exit(1)
result = compare_contracts(v1, v2)
if args.json:
print(json.dumps(result, indent=2))
else:
print(format_text(result))
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
Proposal Cost Estimator
Generate a project cost estimate with phase breakdown, payment schedule,
and margin analysis. Supports multiple billing models and currencies.
Usage:
python proposal_cost_estimator.py --hourly-rate 150 --hours 200 --phases 4
python proposal_cost_estimator.py --hourly-rate 150 --hours 200 --phases 4 --json
"""
import argparse
import json
import math
import sys
DEFAULT_PHASE_NAMES = [
"Discovery & Planning",
"Development",
"Testing & QA",
"Launch & Handoff",
"Post-Launch Support",
]
DEFAULT_PHASE_WEIGHTS = [0.10, 0.50, 0.20, 0.15, 0.05]
PAYMENT_SCHEDULES = {
"milestone": {
"description": "Milestone-based (recommended for >$10K)",
"schedule": [
{"milestone": "Contract signing", "percentage": 50},
{"milestone": "Beta delivery", "percentage": 25},
{"milestone": "Final acceptance", "percentage": 25},
],
},
"monthly": {
"description": "Monthly invoicing (recommended for hourly/retainer)",
"schedule": [
{"milestone": "Monthly invoice", "percentage": "pro-rata"},
],
},
"phased": {
"description": "Per-phase payment (recommended for multi-phase projects)",
"schedule": [], # Generated dynamically
},
}
def estimate_cost(hourly_rate: float, hours: float, num_phases: int,
margin: float, currency: str) -> dict:
"""Generate project cost estimate."""
base_cost = hourly_rate * hours
margin_amount = base_cost * (margin / 100.0)
total_cost = base_cost + margin_amount
# Phase breakdown
phases = []
phase_names = DEFAULT_PHASE_NAMES[:num_phases]
phase_weights = DEFAULT_PHASE_WEIGHTS[:num_phases]
# Normalize weights
weight_sum = sum(phase_weights)
phase_weights = [w / weight_sum for w in phase_weights]
for i, (name, weight) in enumerate(zip(phase_names, phase_weights)):
phase_hours = round(hours * weight, 1)
phase_cost = round(total_cost * weight, 2)
phases.append({
"phase": i + 1,
"name": name,
"weight_pct": round(weight * 100, 1),
"estimated_hours": phase_hours,
"estimated_cost": phase_cost,
})
# Payment schedule options
milestone_schedule = []
milestone_schedule.append({
"event": "Contract signing (50%)",
"amount": round(total_cost * 0.50, 2),
})
if num_phases >= 3:
milestone_schedule.append({
"event": "Beta delivery (25%)",
"amount": round(total_cost * 0.25, 2),
})
milestone_schedule.append({
"event": "Final acceptance (25%)",
"amount": round(total_cost * 0.25, 2),
})
else:
milestone_schedule.append({
"event": "Final delivery (50%)",
"amount": round(total_cost * 0.50, 2),
})
phased_schedule = []
for p in phases:
phased_schedule.append({
"event": f"{p['name']} completion",
"amount": p["estimated_cost"],
})
# Estimated duration (assuming 6 productive hours per day, 5 days per week)
productive_hours_per_week = 30
estimated_weeks = math.ceil(hours / productive_hours_per_week)
# Risk buffer
risk_buffer_hours = round(hours * 0.15, 1)
risk_buffer_cost = round(risk_buffer_hours * hourly_rate, 2)
return {
"inputs": {
"hourly_rate": hourly_rate,
"total_hours": hours,
"num_phases": num_phases,
"margin_pct": margin,
"currency": currency,
},
"cost_summary": {
"base_cost": round(base_cost, 2),
"margin_amount": round(margin_amount, 2),
"total_project_cost": round(total_cost, 2),
"effective_hourly_rate": round(total_cost / hours, 2),
"currency": currency,
},
"phase_breakdown": phases,
"timeline": {
"estimated_weeks": estimated_weeks,
"productive_hours_per_week": productive_hours_per_week,
"total_hours": hours,
},
"payment_options": {
"milestone": milestone_schedule,
"phased": phased_schedule,
},
"risk_buffer": {
"buffer_hours": risk_buffer_hours,
"buffer_cost": risk_buffer_cost,
"total_with_buffer": round(total_cost + risk_buffer_cost, 2),
"note": "15% contingency for scope changes and unforeseen complexity",
},
"recommendations": _generate_recommendations(total_cost, hours, num_phases),
}
def _generate_recommendations(total_cost: float, hours: float, phases: int) -> list:
"""Generate recommendations for proposal."""
recs = []
if total_cost > 50000:
recs.append({
"priority": "HIGH",
"recommendation": "Project over $50K -- use milestone-based payments and include legal review.",
})
if total_cost > 10000:
recs.append({
"priority": "MEDIUM",
"recommendation": "Use milestone payments over net-30 to reduce cash flow risk.",
})
if hours > 500:
recs.append({
"priority": "MEDIUM",
"recommendation": "Large project -- consider phased contracting with go/no-go gates between phases.",
})
recs.append({
"priority": "MEDIUM",
"recommendation": "Include a change order clause for scope changes with pricing mechanism.",
})
recs.append({
"priority": "LOW",
"recommendation": "Present the client with 2-3 pricing options (basic, recommended, premium) to avoid a yes/no decision.",
})
return recs
def format_text(result: dict) -> str:
"""Format results as human-readable text."""
lines = []
cur = result["inputs"]["currency"]
lines.append("=" * 60)
lines.append("PROPOSAL COST ESTIMATOR")
lines.append("=" * 60)
cs = result["cost_summary"]
lines.append(f"\n--- Cost Summary ---")
lines.append(f"Hourly Rate: {cur} {result['inputs']['hourly_rate']:>10,.2f}")
lines.append(f"Total Hours: {result['inputs']['total_hours']:>10,.1f}")
lines.append(f"Base Cost: {cur} {cs['base_cost']:>10,.2f}")
lines.append(f"Margin ({result['inputs']['margin_pct']}%): {cur} {cs['margin_amount']:>10,.2f}")
lines.append(f"Total Project Cost: {cur} {cs['total_project_cost']:>10,.2f}")
lines.append(f"Effective Rate: {cur} {cs['effective_hourly_rate']:>10,.2f}/hr")
lines.append(f"\n--- Phase Breakdown ---")
lines.append(f"{'Phase':<25} {'Hours':>8} {'Cost':>12} {'%':>6}")
for p in result["phase_breakdown"]:
lines.append(f"{p['name']:<25} {p['estimated_hours']:>8.1f} {cur} {p['estimated_cost']:>8,.2f} {p['weight_pct']:>5.1f}%")
tl = result["timeline"]
lines.append(f"\n--- Timeline ---")
lines.append(f"Estimated Duration: {tl['estimated_weeks']} weeks")
lines.append(f"Productive Hours/Week: {tl['productive_hours_per_week']}")
rb = result["risk_buffer"]
lines.append(f"\n--- Risk Buffer ---")
lines.append(f"Buffer Hours: {rb['buffer_hours']}")
lines.append(f"Buffer Cost: {cur} {rb['buffer_cost']:,.2f}")
lines.append(f"Total with Buffer: {cur} {rb['total_with_buffer']:,.2f}")
lines.append(f"\n--- Payment Options ---")
lines.append(f"\n Milestone-based:")
for m in result["payment_options"]["milestone"]:
lines.append(f" {m['event']:<35} {cur} {m['amount']:>10,.2f}")
lines.append(f"\n Per-phase:")
for m in result["payment_options"]["phased"]:
lines.append(f" {m['event']:<35} {cur} {m['amount']:>10,.2f}")
lines.append(f"\n--- Recommendations ---")
for r in result["recommendations"]:
lines.append(f"[{r['priority']}] {r['recommendation']}")
lines.append("")
return "\n".join(lines)
def main():
parser = argparse.ArgumentParser(
description="Generate project cost estimate with phase breakdown and payment schedule."
)
parser.add_argument("--hourly-rate", type=float, required=True,
help="Hourly rate in dollars")
parser.add_argument("--hours", type=float, required=True,
help="Estimated total hours")
parser.add_argument("--phases", type=int, default=3,
help="Number of project phases (default: 3, max: 5)")
parser.add_argument("--margin", type=float, default=20,
help="Desired profit margin percentage (default: 20)")
parser.add_argument("--currency", default="USD",
help="Currency code (default: USD)")
parser.add_argument("--json", action="store_true",
help="Output results as JSON")
args = parser.parse_args()
if args.hourly_rate <= 0:
print("Error: --hourly-rate must be positive.", file=sys.stderr)
sys.exit(1)
if args.hours <= 0:
print("Error: --hours must be positive.", file=sys.stderr)
sys.exit(1)
num_phases = max(1, min(5, args.phases))
result = estimate_cost(args.hourly_rate, args.hours, num_phases,
args.margin, args.currency)
if args.json:
print(json.dumps(result, indent=2))
else:
print(format_text(result))
if __name__ == "__main__":
main()