Receber por email
← Voltar para o catálogo

morning-briefing

Token-efficient Claude Code workspace with parallel agents and persistent memory. Research → Plan → Implement → Validate workflow.

18estrelas
Atualizado há 3 meses

Ver no GitHub ↗

Como adicionar

/plugin marketplace add coalesce-labs/catalyst

O comando exato pode variar conforme o repositório. Confira o README no GitHub.

Para o autor da skill

Cole no README do seu repo

Mostra que sua skill está catalogada na Skillteca, gera backlink e tráfego rastreável.

[![Skillteca](https://www.skillteca.com.br/api/badge/morning-briefing-coalesce-labs/svg)](https://www.skillteca.com.br/skills/morning-briefing-coalesce-labs)

Skills relacionadas

Ver todas de Pesquisa e Web →

Alerta por categoria

Receba novas skills de Pesquisa e Web toda segunda

1 email curto, só com as skills novas de Pesquisa e Web. 4 minutos de leitura, sem spam, cancela com 1 clique.

Você confirma o email no primeiro envio. Sem spam. Cancela com 1 clique.

Ler descrição completa

Morning Briefing — canonical markdown + fan-out

When to use

Invoke as /catalyst-dev:morning-briefing to produce today's briefing locally and fan it out to Slack DM, Slack channel, Notion page, and a Loom recording script ([[2026-05-16-catalyst-phase-agent-architecture]] §Initiative 2 Phase 3).

Flags

| Flag | Meaning | |---|---| | --date YYYY-MM-DD | Target date. Default: today (UTC). | | --dry-run | Write to /tmp/morning-briefing-<date>.md instead of thoughts/briefings/. |

Step 1: Prelude — start session, resolve date

SCRIPT_DIR="${CLAUDE_PLUGIN_ROOT:-plugins/dev}/scripts/morning-briefing"
SESSION_SCRIPT="${CLAUDE_PLUGIN_ROOT:-plugins/dev}/scripts/catalyst-session.sh"

CATALYST_SESSION_ID=$("$SESSION_SCRIPT" start --skill "morning-briefing" \
  --ticket "" --workflow "${CATALYST_SESSION_ID:-}")
export CATALYST_SESSION_ID

# Resolve target date + output path. Pass --dry-run / --date through from the user.
OUT_PATH=$(bash "$SCRIPT_DIR/output-path.sh" "$@")
DATE=$(basename "$OUT_PATH" .md | sed 's/^morning-briefing-//')
echo "Target date: $DATE"
echo "Output path: $OUT_PATH"

Step 2: Gather "yesterday" — parallel MCP/CLI queries

Read source: per the linearis skill's "Reading Linear" section, ticket reads go to the replica via direct SQL. The gather-linear.sh helper below uses a filtered issues list (an activity window, not a single-ticket read) — the list-shaped case that has no replica form yet, so it stays on linearis.

Launch the five gather helpers in parallel. Each prints a JSON fragment to its own scratch file; each degrades silently to {} if its credentials are absent so the briefing always renders.

SCRATCH=$(mktemp -d)
trap 'rm -rf "$SCRATCH"' EXIT

bash "$SCRIPT_DIR/gather-linear.sh"   --date "$DATE" > "$SCRATCH/linear.json"   &
bash "$SCRIPT_DIR/gather-github.sh"   --date "$DATE" > "$SCRATCH/github.json"   &
bash "$SCRIPT_DIR/gather-granola.sh"  --date "$DATE" > "$SCRATCH/granola.json"  &
bash "$SCRIPT_DIR/gather-drive.sh"    --date "$DATE" > "$SCRATCH/drive.json"    &
bash "$SCRIPT_DIR/gather-calendar.sh" --date "$DATE" > "$SCRATCH/calendar.json" &
wait

If a richer Linear or Notion query is needed beyond what the CLI/REST helpers expose, use the mcp__linear__* / mcp__notion__* tools directly from this skill — write the result to $SCRATCH/<source>.json in the same shape ({"<source>": [...]}).

Step 3: Gather "decisions"

Populate the decisions: array from four sources:

  1. ADR drift — adr-drift.sh reads ADR code_assertions frontmatter and surfaces patterns

that drift from the codebase. See [ADR-DRIFT.md](./ADR-DRIFT.md).

  1. Blocked PRs — gh search prs --review-requested @me --state open --json … filtered to

PRs with no commit in the last 48h. Each becomes one {type: blocked_pr, …} decision.

  1. Judgment-call Linear tickets — linearis issues list --team <team> --status "Triage,In Progress" --label needs-decision (label name is informational; substitute whatever signal the operator uses).
  2. Pending compound-engineering ADR proposals — the ticket-compound curator queues

APPROVE-gated ADR changes at thoughts/shared/compound/pending/<TICKET>.md. Each pending file becomes one decision the morning ritual can approve via briefing-followup's action-compound.sh. Emitted as type: judgment_call (the frontmatter schema's type enum has no compound_adr value) carrying a pending: path — that field is the discriminator briefing-followup routes on.

Synthesize into a decisions.json fragment:

# ADR drift detection (CTL-459)
bash "$SCRIPT_DIR/adr-drift.sh" --root "$(pwd)" > "$SCRATCH/adr-drift.json"

# Blocked-PR + judgment-call sources are still TODO — start with an empty fragment.
echo '{"decisions": []}' > "$SCRATCH/decisions-other.json"

# Pending compound ADR proposals (CTL-789). Resilient to an absent/empty store:
# the glob below simply yields nothing when thoughts/shared/compound/pending/ is missing.
PENDING_DIR="thoughts/shared/compound/pending"
: > "$SCRATCH/compound-pending.jsonl"
if [[ -d "$PENDING_DIR" ]]; then
  for pf in "$PENDING_DIR"/*.md; do
    [[ -e "$pf" ]] || continue   # no-match glob guard (no nullglob needed)
    PTICKET=$(grep -m1 '^ticket:' "$pf" 2>/dev/null \
      | sed -E 's/^ticket:[[:space:]]*//; s/^"//; s/"$//; s/^'\''//; s/'\''$//')
    [[ -z "$PTICKET" ]] && PTICKET="$(basename "$pf" .md)"
    PTARGET=$(grep -m1 '^target:'  "$pf" 2>/dev/null | sed -E 's/^target:[[:space:]]*//')
    PADRID=$(grep -m1 '^adr_id:'   "$pf" 2>/dev/null | sed -E 's/^adr_id:[[:space:]]*//')
    PRAT=$(grep -m1 '^rationale:'  "$pf" 2>/dev/null | sed -E 's/^rationale:[[:space:]]*//')
    PSUMMARY="ADR proposal (${PTARGET:-new}${PADRID:+ $PADRID}) from ${PTICKET}${PRAT:+: $PRAT}"
    jq -nc \
      --arg id "compound-${PTICKET}" \
      --arg summary "$PSUMMARY" \
      --arg ticket "$PTICKET" \
      --arg pending "$pf" \
      '{id: $id, type: "judgment_call", summary: $summary, status: "open",
        ticket: $ticket, pending: $pending}' >> "$SCRATCH/compound-pending.jsonl"
  done
fi
jq -sc '{decisions: .}' "$SCRATCH/compound-pending.jsonl" > "$SCRATCH/compound-pending.json"

# Merge all decision sources into one fragment
jq -s '{decisions: (
        ((.[0] // {}).decisions // [])
      + ((.[1] // {}).decisions // [])
      + ((.[2] // {}).decisions // []))}' \
  "$SCRATCH/adr-drift.json" "$SCRATCH/decisions-other.json" "$SCRATCH/compound-pending.json" \
  > "$SCRATCH/decisions.json"

Step 3b: Compound digests — "since last briefing" window (CTL-789)

Two compound-engineering digests the daily review scans: Friction since last briefing (the primary one — per-phase friction records the daily review wants to skim) and Learnings since last briefing (new entries in the curated store). Both filter on a since-last-briefing window: midnight of the most recent prior briefing, or — when there is no prior briefing — midnight of the day before $DATE. These render as body sections appended after Step 6; they degrade to a single "_none_" line when their store is empty or absent.

# ── Resolve the window floor (epoch seconds) ────────────────────────────────
# Most recent thoughts/briefings/YYYY-MM-DD.md strictly older than $DATE.
PREV_BRIEFING_DATE=""
if [[ -d thoughts/briefings ]]; then
  for bf in thoughts/briefings/*.md; do
    [[ -e "$bf" ]] || continue
    bd=$(basename "$bf" .md)
    [[ "$bd" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]] || continue
    if [[ "$bd" < "$DATE" ]]; then
      [[ -z "$PREV_BRIEFING_DATE" || "$bd" > "$PREV_BRIEFING_DATE" ]] && PREV_BRIEFING_DATE="$bd"
    fi
  done
fi
# Window floor: prior briefing's midnight, else $DATE minus one day. `date -d`
# (GNU) and `date -j` (BSD/macOS) differ — try both, fall back to "0".
WINDOW_DATE="${PREV_BRIEFING_DATE:-$(date -u -d "$DATE -1 day" +%Y-%m-%d 2>/dev/null \
  || date -j -v-1d -f %Y-%m-%d "$DATE" +%Y-%m-%d 2>/dev/null || echo "$DATE")}"
WINDOW_EPOCH=$(date -u -d "${WINDOW_DATE}T00:00:00Z" +%s 2>/dev/null \
  || date -j -u -f "%Y-%m-%dT%H:%M:%SZ" "${WINDOW_DATE}T00:00:00Z" +%s 2>/dev/null || echo 0)
echo "Compound digest window: since ${WINDOW_DATE} (epoch ${WINDOW_EPOCH})"

# ── Friction digest (PRIMARY) ───────────────────────────────────────────────
# Each record header is the cross-phase contract:
#   ## <phase> · <TICKET> · <ISO-8601 timestamp>
# parse by that timestamp, keep records AFTER the window, render newest-first.
: > "$SCRATCH/friction-records.tsv"   # ticket \t phase \t iso \t one-line
FRICTION_DIR="thoughts/shared/friction"
if [[ -d "$FRICTION_DIR" ]]; then
  for ff in "$FRICTION_DIR"/*.md; do
    [[ -e "$ff" ]] || continue
    python3 - "$ff" "$WINDOW_EPOCH" >> "$SCRATCH/friction-records.tsv" <<'PY'
import sys, re, datetime
path, floor = sys.argv[1], int(sys.argv[2])
hdr = re.compile(r'^##\s+(?P<phase>[^·]+?)\s+·\s+(?P<ticket>[^·]+?)\s+·\s+(?P<ts>\S+)\s*$')
lines = open(path, encoding='utf-8', 
Compartilhar X LinkedIn