import os, requests, time
LN, MV = os.environ["LINEAR_API_KEY"], os.environ["MAVERA_API_KEY"]
LB, MB = "https://api.linear.app/graphql", "https://app.mavera.io/api/v1"
LH = {"Authorization": LN, "Content-Type": "application/json"}
MH = {"Authorization": f"Bearer {MV}", "Content-Type": "application/json"}
TEAM_KEY = "ENG"
cycle_q = """query($tk: String!) {
cycles(filter: { team: { key: { eq: $tk } } }, first: 12, orderBy: startsAt) {
nodes { number name startsAt endsAt progress completedScopeHistory scopeHistory }
}
}"""
r = requests.post(LB, headers=LH, json={"query": cycle_q, "variables": {"tk": TEAM_KEY}})
if r.status_code == 429:
time.sleep(int(r.headers.get("Retry-After", 60)))
r = requests.post(LB, headers=LH, json={"query": cycle_q, "variables": {"tk": TEAM_KEY}})
r.raise_for_status()
cycles = r.json()["data"]["cycles"]["nodes"]
time.sleep(0.3)
pipe_q = """query($tk: String!) {
issues(filter: { team: { key: { eq: $tk } },
state: { type: { in: ["started", "unstarted"] } } }, first: 100) {
nodes { identifier title estimate project { name } }
}
}"""
pipe_issues = requests.post(LB, headers=LH,
json={"query": pipe_q, "variables": {"tk": TEAM_KEY}}).json()["data"]["issues"]["nodes"]
vel = [{"cycle": c.get("number", "?"),
"dates": f"{(c.get('startsAt') or '')[:10]} → {(c.get('endsAt') or '')[:10]}",
"pts": (c.get("completedScopeHistory") or [0])[-1],
"scope": (c.get("scopeHistory") or [0])[-1],
"pct": round((c.get("progress") or 0) * 100, 1)} for c in cycles]
recent = vel[-6:] if len(vel) >= 6 else vel
avg = sum(v["pts"] for v in recent) / max(len(recent), 1)
trend = "increasing" if len(recent) >= 3 and recent[-1]["pts"] > recent[0]["pts"] else "decreasing"
by_proj, total = {}, 0
for i in pipe_issues:
p = (i.get("project") or {}).get("name", "Unassigned")
pts = i.get("estimate") or 0
by_proj.setdefault(p, {"n": 0, "pts": 0})
by_proj[p]["n"] += 1; by_proj[p]["pts"] += pts; total += pts
vel_txt = "\n".join(f" Cycle {v['cycle']} ({v['dates']}): {v['pts']}/{v['scope']} pts ({v['pct']}%)" for v in vel)
pipe_txt = "\n".join(f" {p}: {d['n']} issues, {d['pts']} pts" for p, d in sorted(by_proj.items(), key=lambda x: -x[1]["pts"]))
print(f"Avg velocity: {avg:.1f} pts/cycle ({trend}), Pipeline: {total} pts")
time.sleep(0.3)
plan = requests.post(f"{MB}/mave/chat", headers=MH, json={
"message": f"Marketing launch planner.\n\nVELOCITY ({len(vel)} cycles):\n{vel_txt}\n\n"
f"AVG: {avg:.1f} pts/cycle ({trend})\n\nPIPELINE:\n{pipe_txt}\nTotal: {total} pts\n\n"
"Produce: 1) Delivery forecast per project 2) Confidence levels 3) Marketing milestone "
"timeline (prep, teaser, launch, follow-up) 4) Risk factors 5) Big bang vs stagger "
"recommendation 6) Content production schedule. Use measured velocity, not optimism.",
}).json()
print(f"\n{'='*60}\nMARKETING LAUNCH TIMELINE\n{'='*60}")
print(plan.get("content", "")[:3000])