contract-clause-analyzer
Analyze construction contract clauses. Identify risks, obligations, and key terms using NLP.
What this skill does
# Contract Clause Analyzer
## Business Case
### Problem Statement
Contract review is time-consuming and error-prone:
- Important clauses missed
- Risk provisions overlooked
- Inconsistent interpretation
- Long review cycles
### Solution
AI-assisted contract clause analysis that identifies key provisions, flags risks, and extracts critical terms.
## Technical Implementation
```python
import pandas as pd
from datetime import datetime, date
from typing import Dict, Any, List, Optional
from dataclasses import dataclass, field
from enum import Enum
import re
class ClauseType(Enum):
SCOPE = "scope"
PAYMENT = "payment"
SCHEDULE = "schedule"
CHANGE_ORDER = "change_order"
TERMINATION = "termination"
INDEMNIFICATION = "indemnification"
INSURANCE = "insurance"
WARRANTY = "warranty"
DISPUTE = "dispute"
LIABILITY = "liability"
FORCE_MAJEURE = "force_majeure"
SAFETY = "safety"
COMPLIANCE = "compliance"
OTHER = "other"
class RiskLevel(Enum):
HIGH = "high"
MEDIUM = "medium"
LOW = "low"
INFO = "info"
@dataclass
class ContractClause:
clause_id: str
section: str
title: str
text: str
clause_type: ClauseType
risk_level: RiskLevel
key_terms: List[str] = field(default_factory=list)
obligations: List[str] = field(default_factory=list)
deadlines: List[str] = field(default_factory=list)
amounts: List[str] = field(default_factory=list)
notes: str = ""
@dataclass
class AnalysisResult:
contract_name: str
analyzed_date: datetime
total_clauses: int
clauses: List[ContractClause]
risk_summary: Dict[str, int]
key_dates: List[Dict[str, str]]
key_amounts: List[Dict[str, str]]
class ContractClauseAnalyzer:
"""Analyze construction contract clauses."""
RISK_KEYWORDS = {
'high': ['indemnify', 'sole discretion', 'waive', 'forfeit', 'liquidated damages',
'consequential', 'unlimited liability', 'hold harmless', 'no limit'],
'medium': ['shall', 'must', 'required', 'obligated', 'responsible', 'liable',
'penalty', 'default', 'breach'],
'low': ['may', 'should', 'reasonable', 'mutual', 'consent', 'approval']
}
CLAUSE_PATTERNS = {
ClauseType.PAYMENT: ['payment', 'invoice', 'retainage', 'progress payment'],
ClauseType.SCHEDULE: ['schedule', 'completion date', 'milestone', 'time is of the essence'],
ClauseType.CHANGE_ORDER: ['change order', 'modification', 'additional work', 'variation'],
ClauseType.TERMINATION: ['termination', 'terminate', 'cancellation'],
ClauseType.INDEMNIFICATION: ['indemnif', 'hold harmless', 'defend'],
ClauseType.INSURANCE: ['insurance', 'coverage', 'policy', 'insured'],
ClauseType.WARRANTY: ['warranty', 'guarantee', 'defect', 'workmanship'],
ClauseType.DISPUTE: ['dispute', 'arbitration', 'mediation', 'litigation'],
ClauseType.LIABILITY: ['liability', 'damages', 'limitation'],
ClauseType.FORCE_MAJEURE: ['force majeure', 'act of god', 'unforeseen'],
}
def __init__(self):
self.clauses: List[ContractClause] = []
def analyze_text(self, contract_name: str, text: str) -> AnalysisResult:
"""Analyze contract text."""
self.clauses = []
# Split into sections/clauses
sections = self._split_into_sections(text)
for i, section in enumerate(sections):
clause = self._analyze_clause(f"CL-{i+1:03d}", section)
self.clauses.append(clause)
# Generate summary
risk_summary = {
'high': sum(1 for c in self.clauses if c.risk_level == RiskLevel.HIGH),
'medium': sum(1 for c in self.clauses if c.risk_level == RiskLevel.MEDIUM),
'low': sum(1 for c in self.clauses if c.risk_level == RiskLevel.LOW)
}
key_dates = []
key_amounts = []
for clause in self.clauses:
for d in clause.deadlines:
key_dates.append({'clause': clause.clause_id, 'date': d})
for a in clause.amounts:
key_amounts.append({'clause': clause.clause_id, 'amount': a})
return AnalysisResult(
contract_name=contract_name,
analyzed_date=datetime.now(),
total_clauses=len(self.clauses),
clauses=self.clauses,
risk_summary=risk_summary,
key_dates=key_dates,
key_amounts=key_amounts
)
def _split_into_sections(self, text: str) -> List[Dict[str, str]]:
"""Split contract into sections."""
sections = []
# Simple split by numbered sections
pattern = r'(\d+\.[\d\.]*\s+[A-Z][^\.]+)'
parts = re.split(pattern, text)
current_title = ""
for i, part in enumerate(parts):
if re.match(r'\d+\.[\d\.]*\s+[A-Z]', part):
current_title = part.strip()
elif part.strip() and current_title:
sections.append({
'title': current_title,
'text': part.strip()
})
current_title = ""
# If no sections found, treat whole text as one
if not sections and text.strip():
sections.append({'title': 'Contract Text', 'text': text.strip()})
return sections
def _analyze_clause(self, clause_id: str, section: Dict[str, str]) -> ContractClause:
"""Analyze single clause."""
text = section.get('text', '')
title = section.get('title', '')
text_lower = text.lower()
# Determine clause type
clause_type = self._determine_type(text_lower)
# Assess risk level
risk_level = self._assess_risk(text_lower)
# Extract key terms
key_terms = self._extract_key_terms(text)
# Extract obligations
obligations = self._extract_obligations(text)
# Extract dates
deadlines = self._extract_dates(text)
# Extract amounts
amounts = self._extract_amounts(text)
return ContractClause(
clause_id=clause_id,
section=clause_id,
title=title,
text=text[:500] + "..." if len(text) > 500 else text,
clause_type=clause_type,
risk_level=risk_level,
key_terms=key_terms,
obligations=obligations,
deadlines=deadlines,
amounts=amounts
)
def _determine_type(self, text: str) -> ClauseType:
"""Determine clause type from content."""
for clause_type, keywords in self.CLAUSE_PATTERNS.items():
if any(kw in text for kw in keywords):
return clause_type
return ClauseType.OTHER
def _assess_risk(self, text: str) -> RiskLevel:
"""Assess risk level of clause."""
high_count = sum(1 for kw in self.RISK_KEYWORDS['high'] if kw in text)
medium_count = sum(1 for kw in self.RISK_KEYWORDS['medium'] if kw in text)
if high_count >= 2:
return RiskLevel.HIGH
elif high_count >= 1 or medium_count >= 3:
return RiskLevel.MEDIUM
elif medium_count >= 1:
return RiskLevel.LOW
return RiskLevel.INFO
def _extract_key_terms(self, text: str) -> List[str]:
"""Extract key defined terms."""
# Look for quoted terms or capitalized multi-word phrases
patterns = [
r'"([^"]+)"',
r"'([^']+)'",
r'\b([A-Z][a-z]+(?:\s+[A-Z][a-z]+)+)\b'
]
terms = []
for pattern in patterns:
matches = re.findall(pattern, text)
terms.extend(matches[:5])
return list(set(terms))[:10]
def _extract_obligations(self, text: str) -> List[str]:
"""Extract obligation statements."""
patterns = [
r'(?:contractor|owner|party)\s+shall\s+([^\.]+)',
r'(?:contractor|owner|party)\s+must\s+([^\.]+)',
Related in General
modeling-omnistudio-epc-catalog
IncludedSalesforce Industries CME EPC product-modeling skill for Product2-based catalog creation. Use when creating EPC products, configuring product attributes, building offer bundles with Product Child Items, or reviewing EPC DataPack JSON metadata for product catalog changes. TRIGGER when: user creates or updates Product2 EPC records, AttributeAssignment payloads, AttributeMetadata/AttributeDefaultValues, Offer bundles, or ProductChildItem relationships. DO NOT TRIGGER when: designing OmniScripts/FlexCards/Integration Procedures (use building-omnistudio-omniscript, building-omnistudio-flexcard, or building-omnistudio-integration-procedure), implementing Apex business logic (use generating-apex), or troubleshooting deployment pipelines (use deploying-metadata).
relationship-science-coach
IncludedUse this skill for direct, practical adult relationship coaching: couples conflict, repair, trust, marriage, dating, flirting, attachment patterns, emotional connection, sex, desire differences, eroticism, kink negotiation, affection, love languages, breakups, and long-term passion. Draw on Gottman, EFT and Hold Me Tight, attachment science, modern sex research, Perel, Nagoski, Kerner, Schnarch, Love and Stosny, and flexible love-language tools. Be concrete and low-hedge. Redirect only for imminent danger, abuse, coercive control, minors, non-consent, self-harm, stalking, or medical/legal/psychiatric decisions.
building-sf-integrations
IncludedSalesforce integration architecture and runtime plumbing with 120-point scoring. Use this skill to set up Named Credentials, External Credentials, External Services, REST/SOAP callout patterns, Platform Events, and Change Data Capture. TRIGGER when: user sets up Named Credentials, External Services, REST/SOAP callouts, Platform Events, CDC, or touches .namedCredential-meta.xml files. DO NOT TRIGGER when: Connected App/OAuth config (use configuring-connected-apps), Apex-only logic (use generating-apex), or data import/export (use handling-sf-data).
venue-templates
IncludedAccess comprehensive LaTeX templates, formatting requirements, and submission guidelines for major scientific publication venues (Nature, Science, PLOS, IEEE, ACM), academic conferences (NeurIPS, ICML, CVPR, CHI), research posters, and grant proposals (NSF, NIH, DOE, DARPA). This skill should be used when preparing manuscripts for journal submission, conference papers, research posters, or grant proposals and need venue-specific formatting requirements and templates.
let-fate-decide
IncludedDraws the 12 Houses of the Zodiac Tarot spread to inject entropy into planning when prompts are vague, ambiguous, or casually delegated. Interprets the spread to guide next steps. Use when the user says 'let fate decide', 'YOLO', 'whatever', 'idk', or other nonchalant phrases, makes Yu-Gi-Oh references, or when you are about to arbitrarily pick between multiple reasonable approaches. Prefer over ask-questions-if-underspecified when the user's tone is casual or playful rather than precision-seeking.
net-ops
IncludedCross-platform network troubleshooting (Windows, macOS, Linux) via local or remote shell. Use for: DNS broken, can't resolve hostnames, nslookup/dig works but apps fail, NRPT, WFP, scutil, /etc/resolver, systemd-resolved, /etc/resolv.conf, NetworkManager, VPN DNS leak residue (ProtonVPN/Mullvad/WireGuard/AnyConnect), AV/firewall blocking DNS or DoH, Tailscale DNS interaction, intermittent connectivity, remote diagnostics over SSH.