* feat: C-Suite expansion — 8 new executive advisory roles Add COO, CPO, CMO, CFO, CRO, CISO, CHRO advisors and Executive Mentor. Expands C-level advisory from 2 to 10 roles with 74 total files. Each role includes: - SKILL.md (lean, <5KB, ~1200 tokens for context efficiency) - Reference docs (loaded on demand, not at startup) - Python analysis scripts (stdlib only, runnable CLI) Executive Mentor features /em: slash commands (challenge, board-prep, hard-call, stress-test, postmortem) with devil's advocate agent. 21 Python tools, 24 reference frameworks, 28,379 total lines. All SKILL.md files combined: ~17K tokens (8.5% of 200K context window). Badge: 88 → 116 skills * feat: C-Suite orchestration layer + 18 complementary skills ORCHESTRATION (new): - cs-onboard: Founder interview → company-context.md - chief-of-staff: Routing, synthesis, inter-agent orchestration - board-meeting: 6-phase multi-agent deliberation protocol - decision-logger: Two-layer memory (raw transcripts + approved decisions) - agent-protocol: Inter-agent invocation with loop prevention - context-engine: Company context loading + anonymization CROSS-CUTTING CAPABILITIES (new): - board-deck-builder: Board/investor update assembly - scenario-war-room: Cascading multi-variable what-if modeling - competitive-intel: Systematic competitor tracking + battlecards - org-health-diagnostic: Cross-functional health scoring (8 dimensions) - ma-playbook: M&A strategy (acquiring + being acquired) - intl-expansion: International market entry frameworks CULTURE & COLLABORATION (new): - culture-architect: Values → behaviors, culture code, health assessment - company-os: EOS/Scaling Up operating system selection + implementation - founder-coach: Founder development, delegation, blind spots - strategic-alignment: Strategy cascade, silo detection, alignment scoring - change-management: ADKAR-based change rollout framework - internal-narrative: One story across employees/investors/customers UPGRADES TO EXISTING ROLES: - All 10 roles get reasoning technique directives - All 10 roles get company-context.md integration - All 10 roles get board meeting isolation rules - CEO gets stage-adaptive temporal horizons (seed→C) Key design decisions: - Two-layer memory prevents hallucinated consensus from rejected ideas - Phase 2 isolation: agents think independently before cross-examination - Executive Mentor (The Critic) sees all perspectives, others don't - 25 Python tools total (stdlib only, no dependencies) 52 new files, 10 modified, 10,862 new lines. Total C-suite ecosystem: 134 files, 39,131 lines. * fix: connect all dots — Chief of Staff routes to all 28 skills - Added complementary skills registry to routing-matrix.md - Chief of Staff SKILL.md now lists all 28 skills in ecosystem - Added integration tables to scenario-war-room and competitive-intel - Badge: 116 → 134 skills - README: C-Level Advisory count 10 → 28 Quality audit passed: ✅ All 10 roles: company-context, reasoning, isolation, invocation ✅ All 6 phases in board meeting ✅ Two-layer memory with DO_NOT_RESURFACE ✅ Loop prevention (no self-invoke, max depth 2, no circular) ✅ All /em: commands present ✅ All complementary skills cross-reference roles ✅ Chief of Staff routes to every skill in ecosystem * refactor: CEO + CTO advisors upgraded to C-suite parity Both roles now match the structural standard of all new roles: - CEO: 11.7KB → 6.8KB SKILL.md (heavy content stays in references) - CTO: 10KB → 7.2KB SKILL.md (heavy content stays in references) Added to both: - Integration table (who they work with and when) - Key diagnostic questions - Structured metrics dashboard table - Consistent section ordering (Keywords → Quick Start → Responsibilities → Questions → Metrics → Red Flags → Integration → Reasoning → Context) CEO additions: - Stage-adaptive temporal horizons (seed=3m/6m/12m → B+=1y/3y/5y) - Cross-references to culture-architect and board-deck-builder CTO additions: - Key Questions section (7 diagnostic questions) - Structured metrics table (DORA + debt + team + architecture + cost) - Cross-references to all peer roles All 10 roles now pass structural parity: ✅ Keywords ✅ QuickStart ✅ Questions ✅ Metrics ✅ RedFlags ✅ Integration * feat: add proactive triggers + output artifacts to all 10 roles Every C-suite role now specifies: - Proactive Triggers: 'surface these without being asked' — context-driven early warnings that make advisors proactive, not reactive - Output Artifacts: concrete deliverables per request type (what you ask → what you get) CEO: runway alerts, board prep triggers, strategy review nudges CTO: deploy frequency monitoring, tech debt thresholds, bus factor flags COO: blocker detection, scaling threshold warnings, cadence gaps CPO: retention curve monitoring, portfolio dog detection, research gaps CMO: CAC trend monitoring, positioning gaps, budget staleness CFO: runway forecasting, burn multiple alerts, scenario planning gaps CRO: NRR monitoring, pipeline coverage, pricing review triggers CISO: audit overdue alerts, compliance gaps, vendor risk CHRO: retention risk, comp band gaps, org scaling thresholds Executive Mentor: board prep triggers, groupthink detection, hard call surfacing This transforms the C-suite from reactive advisors into proactive partners. * feat: User Communication Standard — structured output for all roles Defines 3 output formats in agent-protocol/SKILL.md: 1. Standard Output: Bottom Line → What → Why → How to Act → Risks → Your Decision 2. Proactive Alert: What I Noticed → Why It Matters → Action → Urgency (🔴🟡⚪) 3. Board Meeting: Decision Required → Perspectives → Agree/Disagree → Critic → Action Items 10 non-negotiable rules: - Bottom line first, always - Results and decisions only (no process narration) - What + Why + How for every finding - Actions have owners and deadlines ('we should consider' is banned) - Decisions framed as options with trade-offs - Founder is the highest authority — roles recommend, founder decides - Risks are concrete (if X → Y, costs $Z) - Max 5 bullets per section - No jargon without explanation - Silence over fabricated updates All 10 roles reference this standard. Chief of Staff enforces it as a quality gate. Board meeting Phase 4 uses the Board Meeting Output format. * feat: Internal Quality Loop — verification before delivery No role presents to the founder without passing verification: Step 1: Self-Verification (every role, every time) - Source attribution: where did each data point come from? - Assumption audit: [VERIFIED] vs [ASSUMED] tags on every finding - Confidence scoring: 🟢 high / 🟡 medium / 🔴 low per finding - Contradiction check against company-context + decision log - 'So what?' test: every finding needs a business consequence Step 2: Peer Verification (cross-functional) - Financial claims → CFO validates math - Revenue projections → CRO validates pipeline backing - Technical feasibility → CTO validates - People/hiring impact → CHRO validates - Skip for single-domain, low-stakes questions Step 3: Critic Pre-Screen (high-stakes only) - Irreversible decisions, >20% runway impact, strategy changes - Executive Mentor finds weakest point before founder sees it - Suspicious consensus triggers mandatory pre-screen Step 4: Course Correction (after founder feedback) - Approve → log + assign actions - Modify → re-verify changed parts - Reject → DO_NOT_RESURFACE + learn why - 30/60/90 day post-decision review Board meeting contributions now require self-verified format with confidence tags and source attribution on every finding. * fix: resolve PR review issues 1, 4, and minor observation Issue 1: c-level-advisor/CLAUDE.md — completely rewritten - Was: 2 skills (CEO, CTO only), dated Nov 2025 - Now: full 28-skill ecosystem map with architecture diagram, all roles/orchestration/cross-cutting/culture skills listed, design decisions, integration with other domains Issue 4: Root CLAUDE.md — updated all stale counts - 87 → 134 skills across all 3 references - C-Level: 2 → 33 (10 roles + 5 mentor commands + 18 complementary) - Tool count: 160+ → 185+ - Reference count: 200+ → 250+ Minor observation: Documented plugin.json convention - Explained in c-level-advisor/CLAUDE.md that only executive-mentor has plugin.json because only it has slash commands (/em: namespace) - Other skills are invoked by name through Chief of Staff or directly Also fixed: README.md 88+ → 134 in two places (first line + skills section) * fix: update all plugin/index registrations for 28-skill C-suite 1. c-level-advisor/.claude-plugin/plugin.json — v2.0.0 - Was: 2 skills, generic description - Now: all 28 skills listed with descriptions, all 25 scripts, namespace 'cs', full ecosystem description 2. .codex/skills-index.json — added 18 complementary skills - Was: 10 roles only - Now: 28 total c-level entries (10 roles + 6 orchestration + 6 cross-cutting + 6 culture) - Each with full description for skill discovery 3. .claude-plugin/marketplace.json — updated c-level-skills entry - Was: generic 2-skill description - Now: v2.0.0, full 28-skill ecosystem description, skills_count: 28, scripts_count: 25 * feat: add root SKILL.md for c-level-advisor ClawHub package --------- Co-authored-by: Leo <leo@openclaw.ai>
548 lines
20 KiB
Python
548 lines
20 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
Stakeholder Mapper — Executive Mentor Tool
|
||
|
||
Maps stakeholders by influence and alignment.
|
||
Identifies: champions, blockers, swing votes, and hidden risks.
|
||
Outputs: stakeholder grid with engagement strategy per quadrant.
|
||
|
||
Usage:
|
||
python stakeholder_mapper.py # Run with sample data
|
||
python stakeholder_mapper.py --interactive # Interactive mode
|
||
python stakeholder_mapper.py --file data.json # Load from JSON file
|
||
|
||
JSON format:
|
||
{
|
||
"initiative": "Name of the decision or initiative",
|
||
"stakeholders": [
|
||
{
|
||
"name": "Person/Group Name",
|
||
"role": "Their role or title",
|
||
"influence": 8, // 1–10: how much power they have over outcome
|
||
"alignment": 3, // 1–10: how supportive they are (10=champion, 1=blocker)
|
||
"interest": 7, // 1–10: how interested/engaged they are
|
||
"notes": "Optional context — what drives them, hidden concerns, relationships"
|
||
}
|
||
]
|
||
}
|
||
"""
|
||
|
||
import json
|
||
import sys
|
||
import argparse
|
||
from typing import List, Dict, Tuple, Optional
|
||
|
||
# ─────────────────────────────────────────────────────
|
||
# Quadrant classification
|
||
# ─────────────────────────────────────────────────────
|
||
|
||
def classify_stakeholder(influence: float, alignment: float) -> Dict:
|
||
"""
|
||
Classify into strategic quadrant based on influence and alignment.
|
||
|
||
Quadrants:
|
||
- Champions (high influence, high alignment): Your most valuable assets
|
||
- Blockers (high influence, low alignment): Your biggest risks
|
||
- Supporters (low influence, high alignment): Useful but less critical
|
||
- Bystanders (low influence, low alignment): Monitor, low priority
|
||
- Swing Votes (medium influence, medium alignment): Key to persuade
|
||
"""
|
||
mid_influence = 5.5
|
||
mid_alignment = 5.5
|
||
|
||
# Special case: swing votes — medium on both dimensions
|
||
if 4 <= influence <= 7 and 4 <= alignment <= 7:
|
||
return {
|
||
"quadrant": "Swing Vote",
|
||
"symbol": "⚡",
|
||
"priority": "HIGH",
|
||
"strategy": "Persuade — understand concerns, address directly, build relationship"
|
||
}
|
||
|
||
if influence >= mid_influence and alignment >= mid_alignment:
|
||
return {
|
||
"quadrant": "Champion",
|
||
"symbol": "★",
|
||
"priority": "HIGH",
|
||
"strategy": "Leverage — activate them as advocates, give them a role in the initiative"
|
||
}
|
||
elif influence >= mid_influence and alignment < mid_alignment:
|
||
return {
|
||
"quadrant": "Blocker",
|
||
"symbol": "✖",
|
||
"priority": "CRITICAL",
|
||
"strategy": "Address — understand their specific objections, find common ground or neutralize"
|
||
}
|
||
elif influence < mid_influence and alignment >= mid_alignment:
|
||
return {
|
||
"quadrant": "Supporter",
|
||
"symbol": "○",
|
||
"priority": "MEDIUM",
|
||
"strategy": "Maintain — keep informed and engaged, potentially increase their influence"
|
||
}
|
||
else:
|
||
return {
|
||
"quadrant": "Bystander",
|
||
"symbol": "·",
|
||
"priority": "LOW",
|
||
"strategy": "Monitor — minimal investment, keep informed with standard comms"
|
||
}
|
||
|
||
def risk_flags(stakeholder: Dict) -> List[str]:
|
||
"""Identify specific risk signals for a stakeholder."""
|
||
flags = []
|
||
influence = stakeholder["influence"]
|
||
alignment = stakeholder["alignment"]
|
||
interest = stakeholder.get("interest", 5)
|
||
|
||
if influence >= 7 and alignment <= 3:
|
||
flags.append("🔴 HIGH-POWER BLOCKER — can kill this initiative")
|
||
|
||
if influence >= 7 and alignment <= 5 and interest >= 7:
|
||
flags.append("🟡 ENGAGED SKEPTIC — high influence, paying close attention, not convinced")
|
||
|
||
if alignment <= 4 and interest >= 8:
|
||
flags.append("🟡 ACTIVE OPPOSITION — low alignment but highly engaged — may mobilize others")
|
||
|
||
if influence >= 6 and alignment >= 7 and interest <= 3:
|
||
flags.append("🟡 DISENGAGED CHAMPION — strong supporter but not paying attention — needs activation")
|
||
|
||
if influence >= 5 and 4 <= alignment <= 6:
|
||
flags.append("⚡ PERSUADABLE — medium influence, genuinely undecided — high ROI to engage")
|
||
|
||
return flags
|
||
|
||
# ─────────────────────────────────────────────────────
|
||
# Analysis
|
||
# ─────────────────────────────────────────────────────
|
||
|
||
def calculate_overall_alignment(stakeholders: List[Dict]) -> Dict:
|
||
"""Calculate weighted average alignment (weighted by influence)."""
|
||
if not stakeholders:
|
||
return {"score": 0, "verdict": "No data"}
|
||
|
||
total_influence = sum(s["influence"] for s in stakeholders)
|
||
if total_influence == 0:
|
||
return {"score": 0, "verdict": "No influence"}
|
||
|
||
weighted_alignment = sum(
|
||
s["alignment"] * s["influence"] for s in stakeholders
|
||
) / total_influence
|
||
|
||
if weighted_alignment >= 7:
|
||
verdict = "FAVORABLE — strong support among influential stakeholders"
|
||
elif weighted_alignment >= 5:
|
||
verdict = "MIXED — significant opposition needs to be addressed"
|
||
else:
|
||
verdict = "UNFAVORABLE — initiative faces significant headwinds"
|
||
|
||
return {
|
||
"score": round(weighted_alignment, 2),
|
||
"verdict": verdict
|
||
}
|
||
|
||
def find_critical_path(stakeholders: List[Dict]) -> List[Dict]:
|
||
"""
|
||
Identify the minimal set of stakeholders whose alignment is critical.
|
||
These are high-influence stakeholders — their position determines the outcome.
|
||
"""
|
||
high_influence = [s for s in stakeholders if s["influence"] >= 7]
|
||
return sorted(high_influence, key=lambda x: x["influence"], reverse=True)
|
||
|
||
def engagement_sequencing(stakeholders: List[Dict]) -> List[Dict]:
|
||
"""
|
||
Recommend engagement sequence.
|
||
Order: Fix blockers → Activate champions → Persuade swing votes → Maintain rest.
|
||
"""
|
||
classified = []
|
||
for s in stakeholders:
|
||
cls = classify_stakeholder(s["influence"], s["alignment"])
|
||
classified.append({**s, **cls})
|
||
|
||
# Sort by engagement priority
|
||
priority_order = {"CRITICAL": 0, "HIGH": 1, "MEDIUM": 2, "LOW": 3}
|
||
classified.sort(key=lambda x: (priority_order[x["priority"]], -x["influence"]))
|
||
|
||
return classified
|
||
|
||
# ─────────────────────────────────────────────────────
|
||
# ASCII grid visualization
|
||
# ─────────────────────────────────────────────────────
|
||
|
||
def render_grid(stakeholders: List[Dict], width: int = 60) -> str:
|
||
"""
|
||
Render a 2D influence vs alignment grid with stakeholder positions.
|
||
Y-axis: Influence (top = high)
|
||
X-axis: Alignment (left = low, right = high)
|
||
"""
|
||
rows = 10
|
||
cols = 20
|
||
|
||
grid = [[' ' for _ in range(cols)] for _ in range(rows)]
|
||
|
||
for s in stakeholders:
|
||
influence = s["influence"]
|
||
alignment = s["alignment"]
|
||
|
||
# Map scores 1–10 to grid coordinates
|
||
col = int((alignment - 1) / 9 * (cols - 1))
|
||
row = rows - 1 - int((influence - 1) / 9 * (rows - 1))
|
||
|
||
col = max(0, min(cols - 1, col))
|
||
row = max(0, min(rows - 1, row))
|
||
|
||
initial = s["name"][0].upper()
|
||
if grid[row][col] == ' ':
|
||
grid[row][col] = initial
|
||
else:
|
||
grid[row][col] = '+' # Overlap
|
||
|
||
lines = []
|
||
lines.append(" STAKEHOLDER MAP (Influence ↑ | Alignment →)")
|
||
lines.append("")
|
||
lines.append(f" HIGH ┌{'─'*cols}┐")
|
||
|
||
for i, row in enumerate(grid):
|
||
if i == rows // 2:
|
||
prefix = " INFL "
|
||
else:
|
||
prefix = " "
|
||
lines.append(f"{prefix}│{''.join(row)}│")
|
||
|
||
lines.append(f" LOW └{'─'*cols}┘")
|
||
lines.append(f" {'BLOCKER':<12} {'SWING':<8} CHAMPION")
|
||
lines.append(f" Low alignment High alignment")
|
||
lines.append("")
|
||
|
||
# Legend
|
||
lines.append(" Legend (initials):")
|
||
for s in stakeholders:
|
||
cls = classify_stakeholder(s["influence"], s["alignment"])
|
||
lines.append(f" {s['name'][0].upper()} = {s['name']} ({cls['symbol']} {cls['quadrant']})")
|
||
|
||
return "\n".join(lines)
|
||
|
||
# ─────────────────────────────────────────────────────
|
||
# Output formatting
|
||
# ─────────────────────────────────────────────────────
|
||
|
||
def hr(char="─", width=65):
|
||
return char * width
|
||
|
||
def print_report(data: Dict):
|
||
initiative = data.get("initiative", "Unnamed Initiative")
|
||
stakeholders = data["stakeholders"]
|
||
|
||
# Validate and fill defaults
|
||
for s in stakeholders:
|
||
s.setdefault("interest", 5)
|
||
s.setdefault("notes", "")
|
||
s["influence"] = max(1, min(10, float(s["influence"])))
|
||
s["alignment"] = max(1, min(10, float(s["alignment"])))
|
||
s["interest"] = max(1, min(10, float(s["interest"])))
|
||
|
||
print()
|
||
print(hr("═"))
|
||
print(f" STAKEHOLDER ANALYSIS")
|
||
print(f" {initiative}")
|
||
print(hr("═"))
|
||
|
||
# Overall assessment
|
||
overall = calculate_overall_alignment(stakeholders)
|
||
print()
|
||
print("OVERALL ASSESSMENT")
|
||
print(hr())
|
||
print(f" Weighted alignment score: {overall['score']}/10")
|
||
print(f" Verdict: {overall['verdict']}")
|
||
|
||
# Grid visualization
|
||
print()
|
||
print(hr())
|
||
print(render_grid(stakeholders))
|
||
|
||
# Stakeholder profiles by quadrant
|
||
sequenced = engagement_sequencing(stakeholders)
|
||
|
||
# Group by quadrant
|
||
quadrants = {}
|
||
for s in sequenced:
|
||
q = s["quadrant"]
|
||
if q not in quadrants:
|
||
quadrants[q] = []
|
||
quadrants[q].append(s)
|
||
|
||
quadrant_order = ["Blocker", "Swing Vote", "Champion", "Supporter", "Bystander"]
|
||
|
||
print()
|
||
print("STAKEHOLDER PROFILES")
|
||
print(hr())
|
||
|
||
for q_name in quadrant_order:
|
||
if q_name not in quadrants:
|
||
continue
|
||
group = quadrants[q_name]
|
||
first = group[0]
|
||
print()
|
||
print(f" {first['symbol']} {q_name.upper()}S ({len(group)} stakeholder{'s' if len(group)>1 else ''})")
|
||
print(f" Strategy: {first['strategy']}")
|
||
print()
|
||
|
||
for s in group:
|
||
cls = classify_stakeholder(s["influence"], s["alignment"])
|
||
flags = risk_flags(s)
|
||
|
||
print(f" {s['name']}")
|
||
print(f" Role: {s.get('role', 'Not specified')}")
|
||
print(f" Influence: {'█'*int(s['influence']//2)}{'░'*(5-int(s['influence']//2))} {s['influence']:.0f}/10 "
|
||
f"Alignment: {'█'*int(s['alignment']//2)}{'░'*(5-int(s['alignment']//2))} {s['alignment']:.0f}/10 "
|
||
f"Interest: {'█'*int(s['interest']//2)}{'░'*(5-int(s['interest']//2))} {s['interest']:.0f}/10")
|
||
|
||
if flags:
|
||
for flag in flags:
|
||
print(f" {flag}")
|
||
|
||
if s.get("notes"):
|
||
print(f" Notes: {s['notes']}")
|
||
|
||
print()
|
||
|
||
# Engagement plan
|
||
print()
|
||
print("ENGAGEMENT PLAN (sequenced by priority)")
|
||
print(hr())
|
||
print()
|
||
print(f" {'#':<3} {'Name':<22} {'Quadrant':<14} {'Priority':<10} {'First Action'}")
|
||
print(f" {hr('-', 63)}")
|
||
|
||
actions = {
|
||
"Blocker": "Schedule 1:1 — understand specific objections",
|
||
"Swing Vote": "Coffee or informal conversation — listen first",
|
||
"Champion": "Brief them on the initiative — give them a role",
|
||
"Supporter": "Keep informed — monthly update or email",
|
||
"Bystander": "Include in standard comms only"
|
||
}
|
||
|
||
for i, s in enumerate(sequenced, 1):
|
||
action = actions.get(s["quadrant"], "Maintain standard communication")
|
||
print(f" {i:<3} {s['name']:<22} {s['quadrant']:<14} {s['priority']:<10} {action}")
|
||
|
||
# Risk summary
|
||
print()
|
||
print("RISK SUMMARY")
|
||
print(hr())
|
||
|
||
critical_path = find_critical_path(stakeholders)
|
||
if critical_path:
|
||
print()
|
||
print(" High-influence stakeholders (outcome depends on these):")
|
||
for s in critical_path:
|
||
cls = classify_stakeholder(s["influence"], s["alignment"])
|
||
alignment_label = "CHAMPION" if s["alignment"] >= 7 else "BLOCKER" if s["alignment"] <= 4 else "UNDECIDED"
|
||
print(f" {cls['symbol']} {s['name']:<25} influence {s['influence']:.0f}/10 → {alignment_label}")
|
||
|
||
# All risk flags
|
||
all_flags = []
|
||
for s in stakeholders:
|
||
flags = risk_flags(s)
|
||
for flag in flags:
|
||
all_flags.append((s["name"], flag))
|
||
|
||
if all_flags:
|
||
print()
|
||
print(" Risk flags:")
|
||
for name, flag in all_flags:
|
||
print(f" [{name}] {flag}")
|
||
|
||
print()
|
||
print(hr("═"))
|
||
print()
|
||
|
||
# ─────────────────────────────────────────────────────
|
||
# Interactive mode
|
||
# ─────────────────────────────────────────────────────
|
||
|
||
def interactive_mode():
|
||
print()
|
||
print(hr("═"))
|
||
print(" STAKEHOLDER MAPPER — Interactive Mode")
|
||
print(hr("═"))
|
||
|
||
data = {}
|
||
data["initiative"] = input("\nWhat initiative or decision are you mapping?\n> ").strip()
|
||
|
||
print("\nAdd stakeholders one at a time. Empty name to finish.")
|
||
print("Scores: 1=low, 10=high")
|
||
print()
|
||
|
||
stakeholders = []
|
||
while True:
|
||
name = input(f"Stakeholder {len(stakeholders)+1} name (or ENTER to finish): ").strip()
|
||
if not name:
|
||
if len(stakeholders) < 1:
|
||
print(" Need at least 1 stakeholder.")
|
||
continue
|
||
break
|
||
|
||
role = input(f" Role/title: ").strip()
|
||
|
||
def get_score(prompt, default=5):
|
||
while True:
|
||
s = input(f" {prompt} (1–10, default {default}): ").strip()
|
||
if not s:
|
||
return float(default)
|
||
try:
|
||
v = float(s)
|
||
if 1 <= v <= 10:
|
||
return v
|
||
print(" Must be 1–10")
|
||
except ValueError:
|
||
print(" Enter a number")
|
||
|
||
influence = get_score("Influence (power over this decision)")
|
||
alignment = get_score("Alignment (1=opposed, 10=champion)")
|
||
interest = get_score("Interest level (how engaged are they)")
|
||
notes = input(f" Notes (optional): ").strip()
|
||
|
||
stakeholders.append({
|
||
"name": name,
|
||
"role": role,
|
||
"influence": influence,
|
||
"alignment": alignment,
|
||
"interest": interest,
|
||
"notes": notes
|
||
})
|
||
print()
|
||
|
||
data["stakeholders"] = stakeholders
|
||
print_report(data)
|
||
|
||
# ─────────────────────────────────────────────────────
|
||
# Sample data
|
||
# ─────────────────────────────────────────────────────
|
||
|
||
SAMPLE_DATA = {
|
||
"initiative": "Migrate from monolith to microservices (18-month program)",
|
||
"stakeholders": [
|
||
{
|
||
"name": "Sarah Chen (CTO)",
|
||
"role": "Chief Technology Officer",
|
||
"influence": 10,
|
||
"alignment": 9,
|
||
"interest": 9,
|
||
"notes": "Driving force behind the initiative. Will fund and protect the team."
|
||
},
|
||
{
|
||
"name": "Marcus Webb (CFO)",
|
||
"role": "Chief Financial Officer",
|
||
"influence": 9,
|
||
"alignment": 3,
|
||
"interest": 6,
|
||
"notes": "Concerned about 18-month cost with no visible revenue return. Has budget veto."
|
||
},
|
||
{
|
||
"name": "Priya Agarwal (VP Eng)",
|
||
"role": "VP Engineering",
|
||
"influence": 8,
|
||
"alignment": 7,
|
||
"interest": 8,
|
||
"notes": "Supportive in principle, worried about team bandwidth alongside feature delivery."
|
||
},
|
||
{
|
||
"name": "Tom Briggs (VP Product)",
|
||
"role": "VP Product",
|
||
"influence": 7,
|
||
"alignment": 4,
|
||
"interest": 5,
|
||
"notes": "Concerned about roadmap slowdown. Hasn't been in the architecture discussions."
|
||
},
|
||
{
|
||
"name": "Elena Park (CEO)",
|
||
"role": "Chief Executive Officer",
|
||
"influence": 10,
|
||
"alignment": 6,
|
||
"interest": 4,
|
||
"notes": "Trusts the CTO but will back out if CFO and VP Product both push back hard."
|
||
},
|
||
{
|
||
"name": "Raj Patel (Lead Arch)",
|
||
"role": "Lead Architect",
|
||
"influence": 6,
|
||
"alignment": 10,
|
||
"interest": 10,
|
||
"notes": "Deep technical champion. Has proposed detailed migration plan."
|
||
},
|
||
{
|
||
"name": "Dev Team Leads (x4)",
|
||
"role": "Team Leads",
|
||
"influence": 5,
|
||
"alignment": 6,
|
||
"interest": 7,
|
||
"notes": "Mixed. Some excited, some worried about learning curve. Middle ground."
|
||
},
|
||
{
|
||
"name": "Board (investor reps)",
|
||
"role": "Board Directors",
|
||
"influence": 9,
|
||
"alignment": 5,
|
||
"interest": 3,
|
||
"notes": "Not paying attention unless CFO raises flags. Could become blockers if CFO escalates."
|
||
}
|
||
]
|
||
}
|
||
|
||
# ─────────────────────────────────────────────────────
|
||
# Main
|
||
# ─────────────────────────────────────────────────────
|
||
|
||
def main():
|
||
parser = argparse.ArgumentParser(
|
||
description="Stakeholder Mapper — influence, alignment, and engagement strategy"
|
||
)
|
||
parser.add_argument(
|
||
"--interactive", "-i",
|
||
action="store_true",
|
||
help="Interactive mode: enter stakeholder data manually"
|
||
)
|
||
parser.add_argument(
|
||
"--file", "-f",
|
||
type=str,
|
||
help="Load stakeholder data from JSON file"
|
||
)
|
||
parser.add_argument(
|
||
"--sample",
|
||
action="store_true",
|
||
help="Print sample JSON structure and exit"
|
||
)
|
||
|
||
args = parser.parse_args()
|
||
|
||
if args.sample:
|
||
print(json.dumps(SAMPLE_DATA, indent=2))
|
||
return
|
||
|
||
if args.interactive:
|
||
interactive_mode()
|
||
return
|
||
|
||
if args.file:
|
||
try:
|
||
with open(args.file) as f:
|
||
data = json.load(f)
|
||
print_report(data)
|
||
except FileNotFoundError:
|
||
print(f"Error: File '{args.file}' not found.")
|
||
sys.exit(1)
|
||
except json.JSONDecodeError as e:
|
||
print(f"Error: Invalid JSON in '{args.file}': {e}")
|
||
sys.exit(1)
|
||
return
|
||
|
||
# Default: sample data
|
||
print()
|
||
print("Running with sample data. Use --interactive for custom input or --file for JSON.")
|
||
print_report(SAMPLE_DATA)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|