Landscape
Shared AI agent skills
npx -y skills add flurdy/agent-skills --skill landscapeAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 6 stars6 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Morning catch-up view β assigned Jira tickets and recent discussion, open PRs, current working copy state, and (if present) in-progress and ready beads in one glance. Run at session start to orient.
SKILL.md
21.3 KB, as published. Nobody here has run it
Landscape β Morning Catch-up
Show a consolidated landscape of where you are and what to do next, pulling from multiple sources at once. Designed for the start of a work session (especially Monday mornings) to quickly orient.
Usage
/landscape # Full landscape
/landscape quick # Skip PR details (faster, Jira + working-copy + beads-if-present only)
What It Shows
Separate blocks, rendered from broadest context to most immediate. Order matters β the last block is the most load-bearing for "what am I doing right now":
- π Jira β tickets assigned to you, not Done (with sprint and latest discussion)
- π PRs β org-wide open PRs, recently closed, unresolved threads
- π― Beads β in-progress and top ready beads in this repo (skipped if
bdnot installed) - π Working copy β current branch, uncommitted/unpushed work (plus, in a multi-repo workspace, a roll-up of sibling service repos with unsaved/unpushed state)
- Next β single-sentence suggestion for the most load-bearing action
Each block is independent β if one source fails, the others still render.
Instructions
MUST re-fetch on every invocation. Each
/landscaperun MUST execute every fetch from scratch βdate, the Jira MCP query, thegh-pr-list-*andgh-pr-details.shscripts,beads.sh, andworking-copy.sh. NEVER reuse output from a previous run in the same session and NEVER extrapolate timestamps. State changes (PR merges, new approvals, ticket transitions) happen between runs; reusing stale tables has caused real merges to be missed in/pr-statusand the same risk applies here.MUST use the dedicated helper scripts. Never construct ad-hoc
bd β¦orgit β¦shell pipelines for this skill. Specifically: do NOT chaincommand -v bdprobes withbd list β¦ && β¦orβ¦ || bd list --readyinside a single Bash call. Always invoke~/.agents/skills/landscape/scripts/beads.shinstead β it handles probing, repo gating, and listing internally. Likewise, do NOT hand-walk sibling service repos with your ownfor β¦ git -C β¦loop β always go through~/.agents/skills/wrap-up/scripts/multirepo.sh(Β§4b), which handles workspace detection and per-repo state. Inline chaining bypasses the per-script permission allowlist and produces noisy permission prompts.
Render the blocks in the order listed below. Some data fetches can run in parallel at the top.
0. Header
date '+%A %Y-%m-%d %H:%M'
Output:
## Landscape β {Weekday} {YYYY-MM-DD} {HH:MM}
If the weekday is Monday, add a subtitle: _Monday β extra catch-up across the weekend._
1. π Jira β assigned to you
Query Jira for open tickets assigned to the current user, including sprint membership. The sprint field is the Jira Cloud default custom field customfield_10020. Use the MCP tool:
mcp__jira__jira_get
path: /rest/api/3/search/jql
queryParams:
jql: assignee = currentUser() AND statusCategory != Done ORDER BY cf[10020] ASC, priority DESC, updated DESC
fields: summary,status,priority,issuetype,updated,customfield_10020
maxResults: 20
jq: issues[*].{key: key, summary: fields.summary, type: fields.issuetype.name, status: fields.status.name, priority: fields.priority.name, updated: fields.updated, sprint: fields.customfield_10020}
The sprint field is an array of sprint objects. Extract the active sprint's name (first sprint where state == "active"), or the most recent if none are active. If the array is empty or null, show β (ticket not in a sprint β possibly backlog).
Sort the rows before rendering. JQL's cf[10020] sort is unreliable across sprint states, so re-sort client-side:
- Bucket by sprint state: active sprints first, then future sprints (by
startDateascending), then no-sprint / backlog last. - Within each bucket, sort by priority
P1 β P2 β P3 β P4 β P5(treat missing/unknown priority as lowest). - Within equal priority, preserve the JQL
updated DESCorder.
Keep it as a single table β the Sprint column makes the group boundary visible without subheadings.
Render:
### π Jira β assigned to you
| Key | Sprint | Type | Pri | Status | Updated | Discussion | Summary |
|-----|--------|------|-----|--------|---------|------------|---------|
| [AB-649](https://.../browse/AB-649) | Sprint 42 | Task | P1 | In Progress | 2h | π¬ 3 Β· Jane Β· 40m | Stabilise identity cookies |
- Key: markdown link to the Jira issue. Use the Jira base URL from the issue's
selffield, or a site-configured base (e.g.https://yourorg.atlassian.net/browse/{key}). - Sprint: active sprint name. Truncate numeric-only names to
S{N}if the column gets wide.βif none. - Type: issuetype name (Task / Story / Bug / Sub-task).
- Pri: shorten long names β
P1 CriticalβP1,P2 HighβP2, etc. - Status: status name (In Progress / Code Review / Ready for QA / β¦).
- Updated: relative time since
updated(e.g.2h,4d). - Discussion: filled by the latest-discussion lookup below.
- Summary: truncate to ~50 chars.
If no tickets are assigned, show _No open Jira tickets assigned to you._
If the Jira API returns an error, show _Jira unavailable: {error}_ and move on β do not fail the whole skill.
After the table, note whether the tickets span one sprint or multiple. Example: _All 6 in Sprint 42._ or _Spans 2 sprints: Sprint 42 (4), Sprint 43 (2)._ This answers "am I focused or scattered?" at a glance.
Latest Jira discussion
After the assigned-ticket search succeeds, fetch the newest comment for every returned ticket. These calls may run in parallel, but do not skip tickets just because their updated value is old:
mcp__jira__jira_get
path: /rest/api/3/issue/{key}/comment
queryParams:
orderBy: -created
maxResults: 1
jq: '{total: .total, latest: (.comments[0] // null | if . == null then null else {author: .author.displayName, accountId: .author.accountId, created: .created} end)}'
Add a Discussion column to the assigned-ticket table:
| Key | Sprint | Type | Pri | Status | Updated | Discussion | Summary |
|-----|--------|------|-----|--------|---------|------------|---------|
| [AB-649](https://.../browse/AB-649) | Sprint 42 | Task | P1 | In Progress | 2h | π¬ 3 Β· Jane Β· 40m | Stabilise identity cookies |
- Discussion:
βwhentotalis zero. Otherwise showπ¬ {total} Β· {latest author first name or @accountId} Β· {relative age}. Do not render comment bodies in this compact table. - If a per-ticket comment request fails, show
?for that row and append_Some Jira discussion could not be fetched._after the sprint note. A failed comment lookup must not hide the assigned-ticket table. - Keep the existing row sort; comments are awareness signals, not a reprioritisation rule.
2. π PRs β delegate to pr-status logic
If /landscape quick was invoked, skip this section entirely and add a one-line note: _PR section skipped (quick mode). Run /pr-status for full view._
Otherwise, follow the pr-status skill's instructions as-is (see ~/.agents/skills/pr-status/SKILL.md). Reuse its scripts directly β do NOT re-invoke the slash command:
- List open PRs org-wide via
gh-pr-list-open.sh - List recently closed via
gh-pr-list-closed.sh - Fetch details via
gh-pr-details.sh(grouped by owner/repo) - Render the same tables
Head this section ### π PRs instead of pr-status's own headings.
3. π― Beads β in-progress + ready work
Run the beads.sh helper. It probes for bd, checks for .beads/ in the repo, and emits in-progress + ready listings as delimited sections. Do not call bd directly from this skill β always go through this script:
~/.agents/skills/landscape/scripts/beads.sh
Output sections (delimited by ---<NAME>---):
---STATUS---βOK,NO_BD(bd not installed), orNO_BEADS_IN_REPO(no.beads/here)---IN-PROGRESS---β output ofbd list --status=in_progress(only ifSTATUS=OK)---READY---β JSON array fromnext-bd --json(or plain text frombd list --readyfallback) (only ifSTATUS=OK)
If STATUS is NO_BD, render _Beads not installed β skipping._ and stop.
If STATUS is NO_BEADS_IN_REPO, render _No beads in this repo._ and stop.
Otherwise render the tables below.
In-progress beads
**In progress ({count})**
| ID | Pri | Type | Labels | Title |
|----|-----|------|--------|-------|
- Include a Labels column. Show
βif none. - If no in-progress beads: show
_No in-progress beads._
Ready beads β sorted by sprint
The ---READY--- section is a JSON array (from next-bd --json). Each item has id, title, priority, issue_type, and rank.
Step 1 β Extract Jira keys. For each bead, scan title for the first match of [A-Z]+-\d+. If no match, the bead has no Jira link.
Step 2 β Batch Jira sprint lookup. If any keys were found, call:
mcp__jira__jira_get
path: /rest/api/3/search/jql
queryParams:
jql: key in ({comma-separated keys})
fields: summary,status,issuetype,priority,customfield_10020
maxResults: 50
jq: issues[*].{key: key, status: fields.status.name, sprint: fields.customfield_10020}
For each ticket's sprint array, pick the active sprint (first with state=="active"). If none active, pick the earliest future sprint (lowest startDate with state=="future"). If neither, treat as no-sprint.
Step 3 β Sort and cap. Bucket order: active sprint(s) β future sprints (by startDate) β no-sprint (has Jira key) β no Jira link. Within each bucket preserve the rank order from next-bd. Cap the total at 5 beads. If more exist: _+{N} more β run /next sprint to see all._
Step 4 β Render. One table:
**Ready ({shown} of {total})**
| # | ID | Pri | Type | Jira | Sprint | Status | Title |
|---|----|-----|------|------|--------|--------|-------|
#is the picker index1-N.Jiracolumn: markdown link[KEY](https://yourorg.atlassian.net/browse/KEY). Showβif no Jira key.Sprintcolumn: number + state suffix only (31 (active),32 (future)) β strip the project prefix from sprint names like"PROJ Sprint 31". Showβif no sprint or no Jira.Statuscolumn: Jira status. Showβif no Jira.- If no ready beads at all:
_No ready beads. Run /triage to add work._ - If the
---READY---section is not valid JSON (fallback plain text): render it as-is, capped at 5 lines. - If the Jira call fails: render the table without Jira/Sprint/Status columns; add a footnote
_Jira unavailable._
4. π Working copy β current branch
Rendered LAST because it's the most immediate context β the branch you're sitting on right now, what needs committing/pushing, and whether it's in sync.
Run the working-copy.sh helper, which emits delimited sections for branch, dirty status, ahead/behind, last commit, and on-branch stash count:
~/.agents/skills/landscape/scripts/working-copy.sh
Output is grouped by ---SECTION--- markers. Parse and render from that.
Render:
### π Working copy
| Field | Value |
|-------|-------|
| Branch | fix/AB-649-device-cookie-combined |
| Dirty | clean _(or: 3 modified, 1 untracked)_ |
| vs upstream | β
in sync _(or: β¬ 2 ahead, β¬ 1 behind)_ |
| Last commit | `abc1234` commit subject (2h ago) |
Notes:
-
If
@{u}fails (no upstream), showno upstream tracking. -
If output is empty for dirty, show
clean. -
Stashes: do NOT include a stash row in the table. Only surface stashes if there are stashes on the current branch. If non-empty, add a one-line footnote below the table:
β οΈ {N} stash(es) on this branch β run `git stash list` to review.Global stash count is not interesting β omit it.
-
Other worktrees: the
OTHER-WORKTREES-UNSAFEsection lists only worktrees (excluding the current one) that have uncommitted changes or unpushed commits. If empty, render nothing β worktrees that are clean and pushed are not interesting. If non-empty, add a footnote below the table:β οΈ Other worktrees with unsaved work: - `/path/to/other` on `fix/X` β 3 modified, 2 unpushedOmit the dirty/unpushed parts that are zero (e.g.
3 modifiedalone, or2 unpushedalone). -
Recent handoffs for this repo: probe
~/.claude/handoffs/via:~/.agents/skills/handoffs/scripts/list.sh --summary-onlyParse from the output:
---SUMMARY---βcurrent_repo_recent_liveβ recent handoffs for this repo that are still live work: not superseded and not finished (uses the same Monβ3 / Tueβ4 / elseβ3 weekend buffer as the closed-PR list). "Finished" here means all referenced beads are closed β a localbdcheck that runs even on this offline (--summary-only, no--check-branches) call, so a handoff whose task shipped no longer counts as a live thread to resume. Re-wraps of the same branch still collapse to one. (Merged-PR / Jira-Done detection needs the network and only applies under/handoffs's--check-branches; landscape's offline count catches the bead-closed case.)---CURRENT-REPO-LATEST---β a single{slug}|{branch}|{date}line for the newest current-repo handoff (the "last session"), or empty if none.---CURRENT-REPO-LIVE---β one{slug}|{branch}|{date}|{time}line per recent live (non-superseded, non-finished) current-repo handoff, newest first (these are the threads behind thecurrent_repo_recent_livecount). The first line equals---CURRENT-REPO-LATEST---when the newest handoff is itself still live; if the newest one is finished it's absent here, so the first line is the most recent resumable thread instead.
If
current_repo_recent_live > 0, render its own table (N=current_repo_recent_live) β a footnote got lost among the other tables, so give it the same### {emoji} {Name}+ table shape as the sections above:### π₯ Recent handoffs ({N} live, last {RECENT-WINDOW-DAYS}d) | Handoff | Branch | When | |---------|--------|------| | `{slug}` π | `{branch}` | {age} | | `{slug}` | `{branch}` | {age} | _`/handoffs` to browse and resume._- One row per
---CURRENT-REPO-LIVE---line, newest first. - Handoff: the
{slug}. Appendπto the row whose{slug}matches the---CURRENT-REPO-LATEST---line β that's the last session, the most likely resume target. When the newest handoff is already finished it won't appear here; the first row is then simply the most recent resumable thread, left unmarked. - Branch: the
{branch}; renderβwhen it is?or empty. - When: relative age of
{date}(e.g.today,2d). - Cap at 5 rows (newest first). When
N > 5, render the first 5 and replace the caption with_+{N β 5} more β/handoffsto browse._
Suppress the section entirely when
current_repo_recent_live == 0β silence is shorter. Older, superseded, or finished handoffs are still browsable via/handoffs; this table is just a fresh-work hint, deliberately offline (no--check-branches, so no branch-staleness here). This call can run in parallel withworking-copy.sh.
4b. ποΈ Other repos in this workspace
working-copy.sh (Β§4) inspects only the cwd repo (plus its own worktrees). In a multi-repo workspace β mgit services (.mgit.conf) or git submodules (.gitmodules) β that silently misses uncommitted/unpushed state in sibling service repos. This is the single biggest blind spot at session start: you orient on the root repo and never notice that, say, dispatch was left with unpushed commits last night. Roll them up:
~/.agents/skills/wrap-up/scripts/multirepo.sh
It emits ---MARKER--- (mgit | submodules | none), ---ROOT---, and ---REPOS--- lines:
{name}|{branch}|{ahead}|{behind}|{upstream}|{modified}|{untracked} (ahead/behind are - with no upstream; the root repo appears as its own row). This call can run in parallel with working-copy.sh β it's the same roll-up /wrap-up Β§3b uses.
---MARKER---isnoneβ single repo; skip this whole section silently (Β§4 already covered it).- Otherwise render only the members with something to report (ahead>0, behind>0, or modified+untracked>0); a clean+pushed member is noise. Skip the table entirely if every member is clean β in a healthy workspace this section shows nothing, which is the point.
### ποΈ Other repos in this workspace
| Repo | Branch | Unpushed | Behind | Uncommitted |
|------|--------|----------|--------|-------------|
- Unpushed:
{ahead}, orno upstreamwhen upstream=no (local-only, never pushed). - Behind: show only when >0. A member that's diverged (ahead>0 AND behind>0) needs a rebase/pull before it can push β flag it explicitly:
β οΈ diverged β N ahead / M behind. - Uncommitted:
{modified} modified / {untracked} untracked(omit zero parts).
Carry the most urgent member (diverged, then unpushed, then uncommitted) into the Β§5 Next suggestion β a sibling repo left in that state is exactly what a fresh session forgets.
5. Next step suggestion
After all blocks render, add a short footer with a concrete next step, picked from what's visible. Prefer the most load-bearing single action:
- If the current branch's PR is approved, CI green, 0 threads, clean merge state β suggest merging it (this unblocks stacked PRs).
- If the current branch's PR has unresolved review threads β suggest
/review-comments. - If the current branch's PR is behind main β suggest
/rebase-main. - If there is uncommitted work β suggest committing or stashing.
- If a sibling service repo (Β§4b) is diverged β suggest rebasing/pulling it before it can block a push (name the repo).
- If a sibling service repo has unpushed commits or uncommitted work β suggest pushing/committing it (name the repo).
Once those "finish / unblock" actions are clear, the suggestion becomes a pick-what-to-work-on decision. This is the morning "resume a handoff or start fresh?" question β arbitrate it here rather than leaving the user to choose between /handoffs and /next. Use the handoff signals from Β§4 (current_repo_recent_live and the ---CURRENT-REPO-LATEST--- {slug}|{branch}|{date} line):
- If a recent live handoff is on the current branch (its
{branch}equals the Β§4 working-copy branch) β strongest resume signal: you're already sitting on its branch and it carries the open threads + suggested next step the bare branch doesn't. SuggestResume {slug} β /handoffs. - If exactly one in-progress bead and no current-branch handoff β suggest resuming the bead (show the ID).
- If nothing decisive above but a recent live handoff exists (
current_repo_recent_live > 0) β lead with the handoff and name the fallback in one sentence, e.g.Resume {slug} (last session's thread) via /handoffs β or /next safe for fresh work.A warm thread beats a cold start, but the user keeps the choice. - If no live handoff and nothing in progress but ready beads exist β suggest
/next. - Otherwise β suggest
/triageor pulling a Jira ticket.
Format as one line:
---
**Next:** _{suggestion}_
Single sentence. Pick one action β don't list a menu. The one exception is the handoff-fallback branch above, which deliberately names a single fallback (Resume {slug} β¦ β or /next safe β¦) so the morning "resume or start fresh?" choice stays visible in one sentence.
Failure modes
Each block is independent β a failure in one must not prevent the others from rendering.
- Not in a git repo: skip the Working-copy block, print
_Not in a git repository._in its place. - No Jira MCP configured: skip the Jira block, print
_Jira MCP not configured._. - gh not authenticated: skip the PR block, print
_GitHub CLI not authenticated (run \gh auth login`)._`. - No beads in repo: skip the Beads block, print
_No beads in this repo._. - Single repo (no
.mgit.conf/.gitmodules): the Β§4b roll-up emitsMARKER=noneβ skip that block silently; Β§4 already covers the one repo.
Performance notes
- Run the git commands in parallel (single Bash call, or parallel tool calls).
- The PR section dominates runtime β that's why
/landscape quickskips it. - Don't re-render pr-status's "deltas since last check" β landscape is a snapshot, not a diff.