Skip to content
Agentmart
Submit a tool

daily-brief

Build the user's Daily Brief across personal work and every permitted company.

Skill by indigoai-us

IndexedMIT
Write a review

Chalk Labs

Need help deploying daily-brief?

Chalk Labs sets up agents like this inside real teams: connected to your tools, tested on your data, with a human in the loop where it matters.

Book a call

Install

Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.

npx agentmarket install skill/indigoai-us-daily-brief

Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.

Get the weekly diff

The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.

We use your email only for this newsletter. Privacy

About

Build the user's Daily Brief across personal work and every permitted company. Reads the last brief and the user's brief preferences, sweeps sessions, PRs, CI and DMs (plus meeting signals, fleet and connected integrations when a company is bound), writes brief-data.json and brief.json with a launch prompt for every action, and reports status so the desktop app can notify the user. The person's first ready brief adds a welcome edition. Run by the main local bot when the user presses Create brief, or on a schedule.

Use cases: devops, data, productivity, agent-building

No reviews yet. Installed it? Reviews come from verified installers only. How reviews work

Permissions

What this skill can do and what it asks for, from its SKILL.md. Checked Oct 25, 2026.

Tools
No tools found
Secrets
None asked for
Install scripts
None found
Runs as
Python package

Network hosts it talks to 1

  • codeload.github.com · found in install command

Files it touches

  • ~/.claude/skills/daily-brief

Tools it pre-approves 6

From allowed-tools in SKILL.md: the agent may use these without asking you each time.

  • Read,
  • Write,
  • Edit,
  • Grep,
  • Glob,
  • Bash

What's inside

Skill name
daily-brief
Description
Build the user's Daily Brief across personal work and every permitted company. Reads the last brief and the user's brief preferences, sweeps sessions, PRs, CI and DMs (plus meeting signals, fleet and connected integrations when a company is bound), writes brief-data.json and brief.json with a launch prompt for every action, and reports status so the desktop app can notify the user. The person's first ready brief adds a welcome edition. Run by the main local bot when the user presses Create brief, or on a schedule.
License
MIT
Allowed tools
Read,, Write,, Edit,, Grep,, Glob,, Bash
Instructions
426 lines in SKILL.md · view on GitHub

Before installing

  • Ships executable scripts
  • Pre-approves any shell command

Files (11)

  • SKILL.md
  • openai.yaml · agents/
  • brief-sweep.py · scripts/
  • brief_rank.py · scripts/
  • dm-inbox-no-company.json · scripts/fixtures/
  • housekeeping.py · scripts/
  • local_sessions.py · scripts/
  • test_brief_additions.py · scripts/
  • test_brief_helpers.py · scripts/
  • test_local_sessions.py · scripts/
  • test_person_brief.py · scripts/

SKILL.md (first lines)

# /daily-brief

Builds one person's Daily Brief across personal work and every permitted company by default. The HQ desktop app starts this through the main local bot when the user presses **Create brief**. A person can also run it directly with `/daily-brief [--all|company|--personal] [--welcome] [--today YYYY-MM-DD]`.

An explicit personal brief (`--personal`) reads only what belongs to the person on this machine and in GitHub: work sessions, PRs, main-branch CI and personal DM messages (messages with no company). The company-only sources (meetings, meeting signals, fleet agents and integrations) are skipped and recorded as skipped, and housekeeping skips the doctor and sync checks. A new user with no company gets a useful first brief this way.

The output is a set of files the desktop app reads. This skill never posts, sends, merges or deploys anything.

## Inputs

| Input | Default |
|---|---|
| `--all` | Default: personal work plus every membership permitted by the session lock |
| `company` | Explicit first argument selects only that company |
| `--personal` | Build a brief with no company, even when the session has one bound |
| `--welcome` | Add the welcome edition (Welcome edition, below). On by default for the person’s first ready run in any mode |
| `today` | Today's local date |
| `run_id` | `--run-id <id>`, passed by the app. If missing, generate one as `brief-<YYYYMMDD-HHMMSS>` in local time |

Briefs are manually triggered, so several can exist per day. Each run gets its own folder, `workspace/brief/<run_id>/`. The `run_id` is `brief-<YYYYMMDD-HHMMSS>` in local time.

| File | Written when | What it is |
|---|---|---|
| `status.json` | At every step | Progress the app polls |
| `brief-data.json` | Step 3 | Raw normalized sweep output |
| `brief.json` | Step 4 | Synthesized items with prompts, read by the generator |
| `housekeeping.json` | Step 5 | Cleanup results and proposed cleanup actions |

When a run finishes, ready or failed, append one line to `workspace/brief/index.jsonl`. The desktop app's history navigator reads this file:

```json
{"run_id":"…","created_at":"…","finished_at":"…","duration_s":0,"window":{"from":"…","to":"…"},"state":"ready|failed","mode":"company|personal","company":"acme|null","catch_up":false,"lead":"…","counts":{"decisions":0,"shipped":0,"incidents":0,"carry":0,"recovered":0},"landed":false,"headline":"one plain line"}
```

`counts.shipped` is `brief-data.json.prs_summary.merged`, the same number the loader and the brief show. `counts.carry` and `counts.recovered` are the lengths of `items.carry` and `items.recovered`; `landed` is true when the run saw at least one merged PR or a release. `brief_rank.py --check` prints `index_counts` and `landed` ready to copy.

`status.json` shape:

```json
{"run_id":"…","state":"running|ready|failed","step":"…","progress":0.0,"started_at":"…","updated_at":"…","error":null,"sources":{}}
```

Write `status.json` before each step, and again after every source and integration check, so the app's progress bar stays truthful and keeps moving. Also keep a rolling `activity` list in `status.json`, newest last, capped at 12 entries. The app shows it under the sunrise so the user can see the run is alive:

```json
"activity":[{"at":"…","label":"Read 42 work sessions","state":"done","detail":"Oct 4–9"},{"at":"…","label":"Checking GitHub PRs","state":"running"}]
```

As each source lands, also update `findings` and `samples` in `status.json`. The app animates these as live counters and preview cards:

```json
"findings":{"signals":{"decision":0,"action_item":0,"risk":0,"commitment":0,"key_point":0},"sessions":0,"meetings":0,"dms":{"total":0,"need_you":0},"prs":{"merged":0,"opened":0,"open":0,"review_requested":0},"ci_red":0,"agents":{"total":0,"attention":0},"integrations":{}},
"samples":[{"kind":"decision","title":"Approve release-gate fix","company":"acme","at":"…"}]
```

`samples` holds at most 8 items, all from the active company only (`company` is null on a personal brief). Titles are short and plain. Never include DM body text: use the sender name and a summary instead.

`state` is `running`, `done`, `skipped` or `failed`. Labels are plain, user-facing language with real counts ("Found 31 DMs, 4 need you"), never commands or file paths. For other-company items, give counts only. Spread progress evenly across the sweep (0.25 → 0.7) by source, and across the integration checks. Read every timestamp from the clock in the same command that writes it.

## Step 1. Start

Write `status.json` with `state: running`, `step: "Checking your setup"` and `progress: 0.05`.

Check the session's company (`bash core/scripts/hq-session.sh current`). Never sweep a company the session is not locked to. The sweep reads the lock with `bash core/scripts/hq-session.sh get company_slugs` and refuses `--company` when that slug is outside the locked set.

- Default to a person brief (`--all`), including personal work and a section for each permitted company. `--company <slug>` and `--personal` remain explicit narrow modes.
- Membership inventory comes from the read-only `hq sync mode --show` table (the caller's memberships), intersected with `hq-session.sh get company_slugs`. Never widen the lock. Show every excluded membership as "not included: open HQ in that company to include it". Inventory/lock failures are visible source failures, never an invented empty membership list.

## Step 2. Load what we know about this person

- Read `personal/brief/brief-prefs.yaml`. If it is missing, create it from the defaults below. Besides `density`, `engine` and the VIP and mute lists, read the widget preferences the app's picker uses: `widget_weights` (kind → -2..2), `pinned_widgets` and `muted_widgets`. Step 4 copies them into `prefs_applied.widgets`.
- Find the last brief: the newest `ready` entry in `workspace/brief/index.jsonl` with `mode: person` for an all-company brief; for explicit narrow modes use the same company and mode. Skip lines that name another company. A line with no `company` field is an older run: use it only when its `brief.json` or `brief-data.json` has the same `company`. Read that run's `brief.json` too: its `items` are the carry-over candidates, and its `carry` rows give `carried_since` and `briefs` for chaining (Step 4, carry).
  - **Deliberate reset.** If `workspace/brief/` is missing or holds no earlier run (no other run folder and no `ready` line in `index.jsonl`) and a sibling `workspace/brief-backup-*` folder exists, the owner reset brief history on purpose. It is not an incident, problem or decision, and never part of the lead. Treat the run as a first brief with no carry-over, label the activity "Starting fresh brief history", and leave the one-line note to housekeeping (Step 5). `brief-data.json.brief_history.reset_on_purpose` records the same check. A missing folder with no backup next to it is also only a housekeeping note, never the lead.
  - **Window:** always the last 24 hours. The brief is a picture of the user's day, not a diff since the last run; a second run an hour later shows the same day again with the new items added. Only a catch-up brief widens the window: to the gap since the last brief, capped at 14 days.
  - **Catch-up:** if the gap since the last brief is 3 days or more, set `catch_up: true`.
  - Carry-over still uses the last brief: an item that was in it and is still open is marked `carry`, never dropped as "nothing new".
- Read the last brief's items. Anything still open becomes a carry-over candidate. Note what was launched or dismissed, from `personal/brief/brief-log.jsonl`.

Write status `step: "Reading your last brief"`, `progress: 0.15`.

## Step 3. Sweep

Run the sweep script. It records each source's success or failure, and never fills a gap with an invented value.

`--hours 24` is the brief's window: sessions, PRs and meetings from the last 24 hours. `--days 7` is only the per-day rollup (`days`) the week arc copies; it never widens the brief. For a catch-up brief (the user has been away 3 or more days) add `--catch-up` and set `--days` to the number of days since the last brief (at most 14), which keeps the meetings from that gap.

```bash
python3 -I .claude/skills/daily-brief/scripts/brief-sweep.py \
  --all --days 7 --hours 24 --today <today> \
  --hq-root "$PWD" --out workspace/brief/<run_id>/brief-data.json \
  --status workspace/brief/<run_id>/status.json
```

For a narrow brief, replace `--all` with `--company <slug>` or `--personal`. Add `--welcome` to force the welcome edition.

On a company brief, PR search is limited to the company's GitHub owner: `--org <owner>`, or else the company's `github_org` in `companies/manifest.yaml`. With neither, and on a personal brief, it covers every owner the user's `gh` login can see. PRs from an owner that `companies/manifest.yaml` maps to another company (to any company, on a personal brief) are dropped and only counted in `prs_other`. The worktree scan treats worktrees the same way: they count in `detail.other` and their paths are never listed. Main-branch CI checks the repos of the user's own PRs in the window (most PRs first, at most 8); `--repos a,b` names them instead, as bare names (owner from `--org` or the PR rows) or owner-qualified names.

The script covers these sources:

| Source | Read through |
|---|---|
| Work sessions | Sessions come from `workspace/sessions` meta files plus the Claude and Codex transcripts (title, and kind you, agent or lane), for the last 24 hours (`--hours`). Other-company sessions are a count only. |
| Recorded meetings, plus each meeting's signals (company only) | `hq meetings list` and `get`. Per meeting, up to 5 decisions and 5 action items carry `text` (the signal's summary line, never transcript text), `owner`, and `mine` (the owner is the user, matched against `--me` or `git config user.name`) |
| DM inbox | `hq dm inbox`, then only this company's messages. A personal brief keeps messages with no company. Another company's messages are dropped and never written. Agent senders, with `agt_` IDs, are labeled. On a company brief, if the inbox page has messages and none carry a company slug field (`company`, `companySlug` or `company_slug` on the message or its channel), the source is skipped with `skipped_reason: "inbox has no company tag"`. `companyUid` is an id, not that field, so a page that only has it is still skipped. An empty inbox is a real empty result, not a skip. |
| Fleet agents (company only) | `hq agents list` |
| The user's PRs | `gh search prs --author @me` (with `author`), plus open PRs where the user is a requested reviewer (`--review-requested @me`, marked `review_requested: true` and `reviewRequests: ["@me"]`). Each row has `product`, `repo`, `repo_full` (the owner-qualified name), `authored_by_me` and `is_release`. Read-only |
| Main-branch CI health | `gh run list` |

Every source has an entry in `brief-data.json.sources` with `state`: `ok` (it ran), `failed` (it ran and broke; `note` and `error` hold one plain line such as "Couldn't read DMs."; the exception text goes to stderr only) or `skipped` (left out on purpose; `skipped_reason` says why). On a personal brief the company-only sources use `needs a company`. On a company brief, DMs use `inbox has no company tag` when the inbox page cannot be filtered by company. `ok` is true only for `state: ok`. A skipped source's top-level key is null. The top-level `mode` is `person`, `company` or `personal`. In person mode, `personal` holds personal sources and `companies.<slug>` holds each company sweep; `company` is null at the root. Each section has its own sources, window, derived summaries and deltas. Person history compares only previous `mode: person` runs, matching company sections by slug.

A skipped source is not a failure and not an empty result. The sweep writes its activity entry with `state: skipped`, a plain label (`Skipped DMs` for the inbox) and `detail` set to `skipped_reason`. It does not write `findings.dms` as `{total: 0}`, and it does not say there were no messages. The brief copies that: when `sources.dms.state` is `skipped`, write no DM decisions, waits or people from the inbox, and do not say the inbox was empty. One clause is enough: "DMs were skipped; the inbox has no company tag."

The script also writes `window.today`, `window.hours`, `window.prs_from`/`prs_to`, `window.attended_meetings_from` (= `sessions_from`, or the first catch-up day), and a top-level `prs_summary: {merged, opened, open, review_requested, median_merge_hours, by_product}` for the 24-hour window, counting only the user's own PRs. The loader's PR line uses the same function, so the app and the brief show the same numbers.

Derived data the sweep adds for the app's widgets (all computed, never estimated; every helper has a unit test in `scripts/test_brief_additions.py`):

| Block | What it carries |
|---|---|
| `sessions[]` | `project` (repo from the cwd), `product` (its human name), `cwd`, `last_activity`, `has_handoff` (a handoff thread record exists). Other-company rows keep only times and counts. On a personal brief the person's own rows, the ones bound to no company, carry `company: "personal"`; rows bound to any company count as `other` |

From indigoai-us/hq-pack-core-skills, MIT.

More from indigoai-us/hq-pack-core-skills

Signals

Stars
0
Forks
0
Open issues
0
Last commit (this folder)
Oct 23, 2026 (7 days ago)

From GitHub (fetched Oct 24, 2026).

Details

License
MIT
First seen
Oct 24, 2026
Last checked
Oct 24, 2026

More from indigoai-us

Done for you

Rather not set it up yourself?

Chalk Labs scopes, builds and runs agents for your team: the task audit, the eval harness, the integrations and the weekly ops report. Fixed scope, priced before we start.

See how we build AI agents →Read the case studies

Chalk Labs is a two-founder AI and growth studio. We build AI agents, CRMs and MVPs for Web3 and AI companies, and have shipped 50+ product and token launches.

launches shipped
50+
launches shipped
media placements
500+
media placements
founders on every account
2
founders on every account

Press we've placed: Forbes · CoinDesk · Decrypt · Cointelegraph · The Block

Rahil Jain, Co-founder, Chalk Labs. Ex-Goldman Sachs engineer. You talk to the person who builds it.

Get daily-brief running in your business

Tell us what you want to automate. A founder replies within one working day with a plan, not a pitch deck.

Goes to the Chalk Labs founders, not the newsletter. Privacy

Need help deploying daily-brief?

CallGet help
Cite this page

daily-brief skill. Agent Mart. https://agentmart.dev/skills/indigoai-us-daily-brief (last updated 2026-10-24)