mirror of
https://github.com/alirezarezvani/claude-skills.git
synced 2026-08-28 04:24:58 +00:00
feat(tooling): backport --sample to 6 more tools across 3 domains (#654)
Brings G9 JSON-output coverage from 19 to 25 tools (25/25 verified): - business-growth: health_score_calculator (embedded 2-customer fixture), pipeline_analyzer (embedded 4-deal pipeline fixture) - c-level-advisor: pmf_scorer (--sample flag for its existing sample_data(), suppressing the stdout notice that corrupted JSON piping), team_scaling_calculator (--sample flag for its embedded defaults) - engineering-team: incident_triage (embedded synthetic ransomware event), contrast_checker (--sample as alias of the existing --demo) Required-arg behavior unchanged when --sample is absent (still exits 2 with a usage error). All gates green: G9 25/25, G8, dual-publish, path linter. Together with the 5 #654-named tools, 4 JSON-honor fixes, and the pre-existing sample-pattern tools, more than 20 tools now support the embedded-sample convention — meeting #654's acceptance bar. https://claude.ai/code/session_01CUWsrUNZP9jpxvAwq67UiT
This commit is contained in:
parent
61ca73ef13
commit
21f8d18a60
6 changed files with 124 additions and 24 deletions
|
|
@ -397,11 +397,38 @@ def format_json(results: List[Dict[str, Any]]) -> str:
|
|||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
# Embedded synthetic fixture for --sample (two customers across segments).
|
||||
SAMPLE_DATA = {
|
||||
"customers": [
|
||||
{
|
||||
"customer_id": "C-001",
|
||||
"name": "Acme Corp",
|
||||
"segment": "enterprise",
|
||||
"arr": 240000,
|
||||
"usage": {"dau_mau_ratio": 0.55, "license_utilization": 0.82},
|
||||
"engagement": {"qbr_attendance": 1.0, "champion_engaged": True},
|
||||
"support": {"open_tickets": 2, "csat": 4.6},
|
||||
"relationship": {"nps": 9, "exec_sponsor": True},
|
||||
},
|
||||
{
|
||||
"customer_id": "C-002",
|
||||
"name": "Globex Ltd",
|
||||
"segment": "smb",
|
||||
"arr": 18000,
|
||||
"usage": {"dau_mau_ratio": 0.12, "license_utilization": 0.35},
|
||||
"engagement": {"qbr_attendance": 0.0, "champion_engaged": False},
|
||||
"support": {"open_tickets": 7, "csat": 3.1},
|
||||
"relationship": {"nps": 4, "exec_sponsor": False},
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Calculate multi-dimensional customer health scores with trend analysis."
|
||||
)
|
||||
parser.add_argument("input_file", help="Path to JSON file containing customer data")
|
||||
parser.add_argument("input_file", nargs="?", help="Path to JSON file containing customer data")
|
||||
parser.add_argument(
|
||||
"--format",
|
||||
choices=["text", "json"],
|
||||
|
|
@ -409,17 +436,27 @@ def main() -> None:
|
|||
dest="output_format",
|
||||
help="Output format (default: text)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--sample",
|
||||
action="store_true",
|
||||
help="Run with an embedded synthetic customer fixture (no input file needed)",
|
||||
)
|
||||
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)
|
||||
if args.sample:
|
||||
data = SAMPLE_DATA
|
||||
else:
|
||||
if not args.input_file:
|
||||
parser.error("input_file is required (or use --sample)")
|
||||
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)
|
||||
|
||||
customers = data.get("customers", [])
|
||||
if not customers:
|
||||
|
|
|
|||
|
|
@ -448,6 +448,24 @@ def format_text_report(results: dict) -> str:
|
|||
return "\n".join(lines)
|
||||
|
||||
|
||||
# Embedded synthetic pipeline fixture for --sample.
|
||||
SAMPLE_DATA = {
|
||||
"quota": 1000000,
|
||||
"average_cycle_days": 45,
|
||||
"stages": ["discovery", "demo", "proposal", "negotiation", "closed_won"],
|
||||
"deals": [
|
||||
{"id": "D-1", "name": "Acme renewal", "stage": "proposal",
|
||||
"value": 120000, "age_days": 30, "close_date": "2026-07-15"},
|
||||
{"id": "D-2", "name": "Globex new logo", "stage": "discovery",
|
||||
"value": 80000, "age_days": 10, "close_date": "2026-08-01"},
|
||||
{"id": "D-3", "name": "Initech expansion", "stage": "negotiation",
|
||||
"value": 200000, "age_days": 70, "close_date": "2026-06-30"},
|
||||
{"id": "D-4", "name": "Umbrella upsell", "stage": "closed_won",
|
||||
"value": 60000, "age_days": 50, "close_date": "2026-05-20"},
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""Main entry point for pipeline analyzer CLI."""
|
||||
parser = argparse.ArgumentParser(
|
||||
|
|
@ -455,7 +473,6 @@ def main() -> None:
|
|||
)
|
||||
parser.add_argument(
|
||||
"--input",
|
||||
required=True,
|
||||
help="Path to JSON file containing pipeline data",
|
||||
)
|
||||
parser.add_argument(
|
||||
|
|
@ -464,18 +481,28 @@ def main() -> None:
|
|||
default="text",
|
||||
help="Output format: json or text (default: text)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--sample",
|
||||
action="store_true",
|
||||
help="Analyze an embedded synthetic pipeline (no input file needed)",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
try:
|
||||
with open(args.input, "r") as f:
|
||||
data = json.load(f)
|
||||
except FileNotFoundError:
|
||||
print(f"Error: File not found: {args.input}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
except json.JSONDecodeError as e:
|
||||
print(f"Error: Invalid JSON in {args.input}: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
if args.sample:
|
||||
data = SAMPLE_DATA
|
||||
else:
|
||||
if not args.input:
|
||||
parser.error("--input is required (or use --sample)")
|
||||
try:
|
||||
with open(args.input, "r") as f:
|
||||
data = json.load(f)
|
||||
except FileNotFoundError:
|
||||
print(f"Error: File not found: {args.input}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
except json.JSONDecodeError as e:
|
||||
print(f"Error: Invalid JSON in {args.input}: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# Validate required fields
|
||||
required_fields = ["deals", "quota", "stages"]
|
||||
|
|
|
|||
|
|
@ -556,6 +556,11 @@ def main():
|
|||
action="store_true",
|
||||
help="Output raw JSON instead of formatted report",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--sample",
|
||||
action="store_true",
|
||||
help="Run with the built-in sample data (no notice line — safe for JSON piping)",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.input:
|
||||
|
|
@ -568,6 +573,8 @@ def main():
|
|||
except json.JSONDecodeError as e:
|
||||
print(f"Error: invalid JSON: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
elif args.sample:
|
||||
data = sample_data()
|
||||
else:
|
||||
print("No input file provided — running with sample data.\n")
|
||||
data = sample_data()
|
||||
|
|
|
|||
|
|
@ -528,9 +528,13 @@ if __name__ == "__main__":
|
|||
"--json", action="store_true",
|
||||
help="Output raw JSON instead of formatted report"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--sample", action="store_true",
|
||||
help="Run with the embedded sample data (ignores input_file)"
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.input_file:
|
||||
if args.input_file and not args.sample:
|
||||
with open(args.input_file) as f:
|
||||
data = json.load(f)
|
||||
current_state = data["current_state"]
|
||||
|
|
|
|||
|
|
@ -353,6 +353,11 @@ def build_parser() -> argparse.ArgumentParser:
|
|||
action="store_true",
|
||||
help="Show example output with sample color pairs",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--sample",
|
||||
action="store_true",
|
||||
help="Alias for --demo (repo-wide embedded-sample convention)",
|
||||
)
|
||||
return parser
|
||||
|
||||
|
||||
|
|
@ -360,8 +365,8 @@ def main() -> int:
|
|||
parser = build_parser()
|
||||
args = parser.parse_args()
|
||||
|
||||
# --demo mode
|
||||
if args.demo:
|
||||
# --demo / --sample mode
|
||||
if args.demo or args.sample:
|
||||
run_demo(args.json_output)
|
||||
return 0
|
||||
|
||||
|
|
|
|||
|
|
@ -584,6 +584,19 @@ def _print_text_report(result: dict) -> None:
|
|||
# Main Entry Point
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# Embedded synthetic security event for --sample (no file/stdin needed).
|
||||
SAMPLE_EVENT = {
|
||||
"event_type": "ransomware",
|
||||
"source_ip": "203.0.113.50",
|
||||
"destination_ip": "10.0.4.21",
|
||||
"user_account": "svc-backup",
|
||||
"hostname": "fileserver-02",
|
||||
"process_name": "encryptor.exe",
|
||||
"first_seen": "2026-06-10T01:30:00Z",
|
||||
"detected_at": "2026-06-10T09:30:00Z",
|
||||
}
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Incident Classification, Triage, and Escalation",
|
||||
|
|
@ -627,12 +640,19 @@ Exit codes:
|
|||
choices=["sev1", "sev2", "sev3", "sev4"],
|
||||
help="Explicit severity override (skips taxonomy-derived severity)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--sample",
|
||||
action="store_true",
|
||||
help="Triage an embedded synthetic ransomware event (no file/stdin needed)",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# --- Load input ---
|
||||
try:
|
||||
if args.input:
|
||||
if args.sample:
|
||||
raw_event = SAMPLE_EVENT
|
||||
elif args.input:
|
||||
with open(args.input, "r", encoding="utf-8") as fh:
|
||||
raw_event = json.load(fh)
|
||||
else:
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue