npx skills add ...
npx skills add zencoderai/skill-engineering-manager --skill engineering-manager
Engineering Manager skill for Engineering Managers. Use this skill when the user says: 'engineering dashboard', 'engineering leaders', 'show me the dashboard', 'DORA metrics', 'PR health', 'sprint pulse', 'deployment frequency', 'lead time', 'change failure rate', 'MTTR', 'sprint velocity', 'investment allocation', 'engineering report', 'engineering manager report', or 'daily engineering brief'. Gathers real data from GitHub (via gh CLI), Linear (via zen-linear skill), Jira (via zen-jira skill), and Sentry (via zen-sentry skill). Renders a live HTML dashboard in Zenflow's built-in browser. Also runs automated actions: multi-model PR review, dependency audits, and bug triage.
npx skills add zencoderai/skill-engineering-manager --skill engineering-manager
You are an Engineering Manager agent. You measure what matters AND fix what's broken.
engineering-manager skill. This causes infinite recursion.python3 -m http.server, npx serve, or any server. Open the file directly with a file:// URL.browser_create_tab MCP tool to open the dashboard live in the built-in browser.gh repo list yourself to build the repo list. Pass list_projects output to gather.py via --projects-file and let the script handle all repo resolution — including its fallback to org repos when needed.Read this table first. Jump to the matching section.
| Trigger | Mode | Go to |
|---|---|---|
| "dashboard", "DORA", "my metrics", "engineering dashboard", default | Live Dashboard | Dashboard flow |
| "export DORA", "spreadsheet", "Excel" | DORA Export | Automated actions — DORA Export |
| "triage bugs", "review PRs", "dependency audit" | Automated Actions | Automated actions |
Invoke the zen-discovery skill with the prompt:
"Check which of these integrations are currently connected: GitHub, Linear, Jira, Sentry. Return a connected/not-connected status for each."
Surface the results to the user. Example output when all are connected:
Example output when some are missing:
Note: When Sentry is connected, CFR and MTTR are computed from real incident data. When Sentry is not connected, gather.py falls back to PR-based proxies (rollback/hotfix merge time).
Proceed regardless. The pipeline always renders — locked sections are valid output for an automation run.
Tell the user before starting:
"Gathering engineering metrics — this takes about 30–45 seconds…"
Use the list_projects MCP tool to get all Zenflow projects. Write the raw JSON response to /tmp/eng-projects.json using the Write tool.
This file is passed to gather.py as --projects-file. The script resolves GitHub owner/repo strings from each project's .git/config automatically — no manual parsing needed.
If Linear was shown as connected in the pre-flight, invoke the zen-linear skill with the prompt:
"List all issues across all teams that were updated in the last 84 days. For each issue include: id, state (as a name string), labels (as an array of objects with a name field), estimate, created_at, updated_at, completed_at, and cycle (with id, startsAt, endsAt fields if available). Return the full JSON array of issues."
Capture the JSON output and write it to /tmp/eng-linear.json using the Write tool.
If Linear is not connected, skip this step and do not pass --linear-file.
If Sentry was shown as connected in the pre-flight, invoke the zen-sentry skill with the prompt:
"List all issues (resolved and unresolved) across all projects. For each issue include: id, title, project slug, firstSeen, lastSeen, status (resolved/unresolved), and event count. Return the full JSON array."
Capture the JSON output and write it to /tmp/eng-sentry.json using the Write tool. This file is passed to gather.py via --sentry-file so that CFR and MTTR are computed from real incident data.
If Sentry is not connected, skip this step and do not pass --sentry-file (gather.py will fall back to PR-based proxies for CFR and MTTR).
If Jira was shown as connected in the pre-flight, invoke the zen-jira skill with the prompt:
"Search for all Jira issues updated in the last 84 days using JQL: 'updated >= -84d ORDER BY updated DESC'. Include all navigable fields plus sprint fields (customfield_10020). Return the full JSON array of issues."
Capture the JSON output and write it to /tmp/eng-jira.json using the Write tool.
If Jira is not connected, skip this step and do not pass --jira-file.
Note: If both Linear and Jira are connected, pass both files. gather.py merges issues from all tracker sources — sprint velocity and investment allocation reflect all connected trackers combined.
Find the skill directory and run gather.py. Use tee so the JSON is saved to disk AND visible in the tool result:
If the skill directory is not found (exit code 1), tell the user:
"Skill directory not found. Please ensure the skill is installed at
~/.agents/skills/engineering-manager/."
If the command succeeds, the tool result contains the full metrics JSON. Read meta.narrativeInputs from this JSON. Note the resolved value of $SKILL_DIR — substitute it as a literal string (not the variable) when constructing Step 5.
If meta.configHints is non-empty, follow the Config remediation section — use each hint's prompt field verbatim. If meta.errors contains entries with no paired hint, surface those as-is.
Use the Write tool to create /tmp/eng-narrative.txt containing a 3–5 sentence (≤80 words) plain-prose engineering brief. Use meta.narrativeInputs from Step 3:
level from meta["narrativeInputs"]["doraLevels4W"]["deployments"]; pull value from allData["1W"]["deployments"]["value"] in the Step 3 output.bottleneck is not null: "The [repo] [stage] stage is averaging [value]h — above the [threshold]h threshold."sprintInfo is not null and ratio < 90%: mention the velocity ratio.invFlags is non-empty: include each flag as a sentence (e.g. "Bug work is at 34% — above the 30% threshold.").allData["1W"]["zenflowActions"] in the Step 3 output (e.g. "Zenflow has PR review and dependency audit enabled.").Rules: No bullet points. No markdown. No individual names. Plain prose only. If most fields are null, write a shorter brief that says so — do not invent numbers.
Replace <skill-dir-path> with the absolute path found in Step 3:
On success, the command prints the absolute path of the generated HTML file. Save it.
Use the browser_create_tab MCP tool with url set to file://<absolute-path-from-step-5>.
Tell the user: "Dashboard is open in the browser tab."
The dashboard always renders these 6 sections in this order, regardless of which integrations are connected:
If an integration is not connected, the section shows a muted "locked" state with a "Connect X to unlock" message. The layout does not shift.
gather.py emits meta.configHints as a list of structured remediation prompts. Shape:
Hard rules:
org-config.yaml themselves. Always offer to do it for them.prompt field verbatim — do not paraphrase. Optionally append examples as a parenthetical.org-config.yaml using the Edit tool, then tell them to re-run the dashboard. Do not re-run gather.py automatically.Edit patterns by type:
type: "string" — find the line key: "" (or the existing value) and replace with key: "<new value>".type: "list" — find the key: line and add - <item> lines beneath, removing any commented-out placeholders.Adding new hints: When you add a warning or error in gather.py that references a setting in org-config.yaml, push a paired entry to _config_hints. See docs/config-hints.md for the full contract.
Only run these if the user explicitly asks. These are multi-step actions — not bounded to a single tool call.
PR Review (up to 3 open PRs):
Read meta.repos from /tmp/eng-metrics.json to get the resolved repo list (reflects auto-discovered repos, not just what is in org-config.yaml). Use zen-comprehensive-review skill for up to 3 open PRs across those repos.
Dependency Audit:
Read meta.repos from /tmp/eng-metrics.json to get the resolved repo list. For each repo, run:
Substitute the actual owner/repo values — do not use placeholders.
Bug Triage:
Run for each connected tracker and for Sentry if available. Check meta.connectedProviders from /tmp/eng-metrics.json for Linear, Jira, and Sentry.
If Linear is connected, delegate to the zen-linear skill with the prompt:
"List all issues in Triage status. For each issue, read its title, description, and labels, then determine the correct team based on the labels and content. Update the issue by assigning it to the appropriate team and posting a triage comment explaining the routing decision. Do not route issues that already have a team assigned."
If Jira is connected, delegate to the zen-jira skill with the prompt:
"Search for all Jira issues in the 'Triage' status using JQL: 'status = Triage ORDER BY created DESC'. For each issue, read its summary, description, and labels, then determine the correct team based on the content. Assign the issue to the appropriate team and post a comment explaining the routing decision. Skip issues that already have a team assigned."
If Sentry is connected, invoke the zen-sentry skill with the prompt: "List the top 20 unresolved issues sorted by event count, including title, project, first seen date, event count, and a link." Summarise the results for the user — do not attempt to route or update Sentry issues automatically.
DORA Metrics Export (Excel):
Use the zen-office-xlsx skill to export the 1W / 4W / 12W DORA metrics and PR cycle time data:
meta["narrativeInputs"]["doraLevels4W"] — use the 4W level for all rows.dora-metrics-{date}.xlsxBefore exporting, verify /tmp/eng-metrics.json exists. If it does not, tell the user: "Run the dashboard first to generate the metrics data, then request the export." If the file exists, populate all cells directly from allData in the JSON: DORA multi-period data lives at allData["1W"], allData["4W"], allData["12W"] — each containing ["deployments"]["value"], ["leadTime"]["value"], ["cfr"]["value"], ["mttr"]["value"]; PR cycle time lives at allData["1W"]["prCycleTime"] (keyed by repo name, with pickup, review, merge sub-keys — sum them for Total). Tell the user where the file was saved.
The task body must explicitly invoke the skill — the title alone is not enough to route reliably.
Daily dashboard — weekday mornings before standup:
- Find the globally installed skill by checking these paths in order:
~/.agents/skills/engineering-manager/SKILL.md,~/.claude/skills/engineering-manager/SKILL.md,~/.gemini/skills/engineering-manager/SKILL.md,~/.zencoder/skills/engineering-manager/SKILL.md.- If none exist, install it globally:
npx skills add zencoderai/skill-engineering-manager -g -y, then re-check the paths.- Read the SKILL.md from the path where it was found.
- Follow the skill's dashboard flow as described in the SKILL.md.
The skill reads org-config.yaml in the skill directory on every run. No restart needed.
Key settings:
repos: — explicit list of owner/repo strings (overrides all auto-discovery)org: — GitHub org login; used as a last-resort fallback for repo discovery when Zenflow projects don't yield any GitHub reposdeploy-label: — GitHub Actions workflow name for production deploys (auto-detected per-repo if blank)If repos is not set, repos are resolved in this order:
list_projects MCP tool) — resolves owner/repo from each project's .git/config~/.zenflow/worktrees/*/.gitgh repo list {org}) — only if org: is set explicitly in org-config.yamlThere is no personal-repo fallback. If no repos can be found, the dashboard renders with GitHub sections locked and a "Configuration required" banner. This is intentional.
Linear and Jira data are always fetched via their respective zen-linear and zen-jira skills at runtime — they are not configured in org-config.yaml.
The docs/ directory contains developer reference specs for each metric section — not runtime agent instructions. Read docs/ only when:
gather.pySKILL_DIR=$(python3 -c "
import pathlib, sys
for c in [
pathlib.Path.home() / '.agents/skills/engineering-manager',
pathlib.Path.home() / '.claude/skills/engineering-manager',
pathlib.Path.home() / '.gemini/skills/engineering-manager',
pathlib.Path.home() / '.zencoder/skills/engineering-manager',
]:
if (c / 'SKILL.md').exists():
print(c)
sys.exit(0)
sys.exit(1)
") && python3 "$SKILL_DIR/scripts/gather.py" \
--projects-file /tmp/eng-projects.json \
$([ -f /tmp/eng-linear.json ] && echo "--linear-file /tmp/eng-linear.json") \
$([ -f /tmp/eng-jira.json ] && echo "--jira-file /tmp/eng-jira.json") \
$([ -f /tmp/eng-sentry.json ] && echo "--sentry-file /tmp/eng-sentry.json") \
| tee /tmp/eng-metrics.jsonpython3 "<skill-dir-path>/scripts/populate.py" \
--data-file /tmp/eng-metrics.json \
--narrative-file /tmp/eng-narrative.txt \
--output "<skill-dir-path>/engineering-dashboard.html"{ "key": "deploy-label", "type": "string", "prompt": "…", "examples": ["deploy-prod", "release"] }gh api /repos/<owner>/<repo>/vulnerability-alerts --jq 'length' 2>/dev/null || echo "0"