Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 27 additions & 6 deletions .agents/skills/daily-plan/AGENT_INSTRUCTIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,27 @@ context. Do not rely on hook-driven side effects for any write you make.

## Phase 1: Context Gathering

Gather ALL of the following, in parallel where possible. If any source fails or
an optional integration is not set up, skip it silently and note the skipped
source in your final report. Never error to the user.
Gather ALL of the following. If any source fails or an optional integration is
not set up, skip it silently and note the skipped source in your final report.
Never error to the user.

**Issue the steps in three batches, each as one turn of parallel tool calls.**
The numbered steps below are a checklist, not an order of execution. Running
them one after another is what made this brief take minutes; nothing in batch
A depends on anything else in batch A.

- **Batch A (independent reads, one turn):** 1.1 week progress, 1.2 calendar,
1.4 commitments and feeds, 1.5 tasks, 1.6 work summary, 1.7 completed
reminders, 1.8 Dex Inbox, 1.9 email connection check, 1.10 chat connection
check, 1.12 ideas, and the profile read plus note listing for 1.13.
- **Batch B (needs batch A's results, one turn):** 1.3 meeting context for
every meeting, 1.9 and 1.10 queries for connected sources, 1.11 semantic
queries, and the `lookup_person` calls for 1.13.
- **Batch C (writes):** the 1.7 task status updates, the 1.13 person-page
updates, then Phase 3.

Every step's own rules, gates, and post-conditions still apply exactly as
written; batching changes when a call is issued, never whether it is made.

### 1.1 Week Progress

Expand Down Expand Up @@ -133,9 +151,12 @@ from them; triage is an interactive step in the main conversation.
### 1.9 Email Intelligence (if connected)

Check `System/integrations/config.yaml`. Also treat a registered `apple-mail-mcp`
server as connected. Before querying any connected email source, run
`python3 core/utils/doctor.py --deep`; Apple Mail search is usable only when the
`mail.apple-search` check reports `OK` / `feature_status: ok`.
server as connected. Before querying Apple Mail, run
`python3 core/utils/doctor.py --deep --only mail.apple-search`; Apple Mail search
is usable only when the `mail.apple-search` check reports `OK` /
`feature_status: ok`. `--only` runs that one probe, not the whole live checkup.
Google Workspace needs no local probe: its own tool response carries the health
status.

If the source is connected and healthy:

Expand Down
40 changes: 30 additions & 10 deletions .agents/skills/daily-plan/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,11 @@ Agent tool, using the self-contained prompt in this skill's
1. Read `.agents/skills/daily-plan/AGENT_INSTRUCTIONS.md`.
2. Substitute its placeholders (`{{TARGET_DATE}}`, `{{TARGET_DATE_PLUS_1}}`,
`{{DAY_NAME}}`, `{{MONTH}}`, `{{DD}}`).
3. Call the Agent tool with `subagent_type: "general-purpose"`, that prompt, and
a short description.
3. Call the Agent tool with `subagent_type: "general-purpose"`,
`model: "sonnet"`, that prompt, and a short description. The brief is
mechanical gathering, so it runs on the fast tier declared by this skill's
`model_routing.steps.data-gathering`; the judgement calls stay in this
conversation on the default model.
4. Verify it wrote the draft plan to `00-Inbox/Daily_Plans/YYYY-MM-DD.md`, then run
the remaining interactive steps from its findings and present the plan.
5. **Close out every `<!-- NEEDS TASK -->` line in the draft.** The subagent
Expand Down Expand Up @@ -92,13 +95,27 @@ Before executing this command, read `System/user-profile.yaml` → `communicatio

---

## Step 0: Process Unprocessed Meetings
## Step 0: Process Unprocessed Meetings (only when something is waiting)

Before gathering context, ensure recent meetings are in the vault by running `/process-meetings`. This pulls any unprocessed meetings from the meeting source (Otter.ai, Granola, etc.), creates meeting notes, updates person/company pages, and extracts tasks — so the daily plan has complete data from yesterday and any earlier gaps.
Before gathering context, ask the session-start sweep how many meeting records
are still waiting, without re-scanning the vault yourself. The count mode
prints one integer, bypasses the sweep's once-per-30-minutes notice throttle,
and writes nothing, so it is safe to call every morning:

- If no new meetings are found, continue silently
- If meetings are processed, note the count and use the extracted context in the plan
- Do NOT ask for a skill rating after this sub-step — save that for the end of the full plan
Ask the host for its count of waiting meeting records if it keeps one; if it does not, treat the count as unknown and run the meeting pass.

Branch on the answer:

- **`0`:** skip this step entirely and say nothing. Yesterday evening's review
already ran the catch-up pass, so on most mornings this is the answer, and
re-running a full seven-day `/process-meetings` here found nothing new while
costing minutes.
- **Above `0`:** run `/process-meetings`. This creates meeting notes, updates
person/company pages, and extracts tasks, so the plan has complete data.
Note the count and use the extracted context in the plan. Do NOT ask for a
skill rating after this sub-step — save that for the end of the full plan.
- **The command fails or prints nothing:** treat that as unknown, not as zero:
run `/process-meetings` as before.

## Step 0.5: Dex Inbox Check (Phone Captures)

Expand Down Expand Up @@ -343,9 +360,12 @@ For each completed item:
### 5.8 Email Intelligence (if connected)

Check `System/integrations/config.yaml` for `google-workspace.enabled: true`. Also treat a
registered `apple-mail-mcp` server as a connected source. Before querying a connected email
source, run `python3 core/utils/doctor.py --deep`; Apple Mail search is usable only when the
`mail.apple-search` check reports `OK` / `feature_status: ok`.
registered `apple-mail-mcp` server as a connected source. Before querying Apple Mail, run
`python3 core/utils/doctor.py --deep --only mail.apple-search`; Apple Mail search is usable
only when the `mail.apple-search` check reports `OK` / `feature_status: ok`. `--only` runs
that one probe instead of the whole live checkup (smoke journeys, search index, connected
tools), which is what a full `--deep` run does and what made this step slow every morning.
Google Workspace needs no local probe: its own tool response carries the health status.

If connected and healthy:
1. Use an attention-inbox count for the headline, never a provider-wide unread total.
Expand Down
22 changes: 18 additions & 4 deletions .agents/skills/daily-review/AGENT_INSTRUCTIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@ any section gracefully if a tool fails; never error out.
**Note:** PostToolUse hooks from the parent skill do not fire in this subagent
context. Do not rely on hook-driven side effects for any write you make.

**Issue the steps in batches, each as one turn of parallel tool calls.** Step 0
runs first because it changes the notes the rest reads. After it, everything in
Steps 1 through 3 and 5 through 6 is independent: the file discovery, the task
file read, `get_week_progress()` and `get_week_priorities()`, the meeting note
reads, `calendar_get_today()`, the Reminders calls, the email check, today's
plan file, and tomorrow's calendar all go out in one turn. Step 2.4's semantic
queries follow in a second turn once you know today's tasks and meetings. Then
write. Running the steps one after another is what made this brief slow; each
step's own rules and gates still apply exactly as written.

---

## Step 0: Meeting Catch-Up (not same-day only)
Expand All @@ -32,10 +42,14 @@ Why the window matters: a same-day filter loses meetings permanently on any day
the review does not run.

Process local candidates following the process-meetings instructions, including
provider-neutral discovery and updating person pages directly rather than
counting on a hook. Note the actual path of every meeting processed. If the
profile is missing or malformed, report that once and use the safe local
fallback. If nothing is unprocessed, skip silently.
its batching rules, provider-neutral discovery and updating person pages
directly rather than counting on a hook. Note the actual path of every meeting
processed. If the profile is missing or malformed, report that once and use the
safe local fallback. If nothing is unprocessed, skip silently.

This is the only meeting pass of the evening. The main conversation no longer
runs `/process-meetings` itself, so a meeting you skip here is not caught later
today; the catch-up window above is what makes that safe.

---

Expand Down
25 changes: 17 additions & 8 deletions .agents/skills/daily-review/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,10 @@ Agent tool, using the self-contained prompt in this skill's
1. Read `.agents/skills/daily-review/AGENT_INSTRUCTIONS.md`.
2. Substitute its placeholders (`{{TARGET_DATE}}`, `{{TOMORROW_DATE}}`,
`{{TOMORROW_DATE_PLUS_1}}`, `{{DAY_NAME}}`, `{{MONTH}}`, `{{DD}}`, `{{YYYY}}`).
3. Call the Agent tool with `subagent_type: "general-purpose"`, that prompt, and
a short description.
3. Call the Agent tool with `subagent_type: "general-purpose"`,
`model: "sonnet"`, that prompt, and a short description. The brief is
mechanical gathering, so it runs on the fast tier; the judgement calls stay
in this conversation on the default model.
4. Verify it wrote the draft to `07-Archives/Reviews/Daily_Review_YYYY-MM-DD.md`,
then run the interactive steps from its findings and complete the placeholder
sections.
Expand Down Expand Up @@ -93,13 +95,20 @@ find . -type f -name "*.md" -newermt "$TODAY 00:00:00" ! -newermt "$TODAY 23:59:

---

## Step 1.5: Process Today's Meetings
## Step 1.5: Today's Meetings Are Already Processed

Before gathering context, ensure today's meetings are in the vault by running `/process-meetings today`. This pulls any unprocessed meetings from the meeting source (Otter.ai, Granola, etc.), creates meeting notes, updates person/company pages, and extracts tasks — so the rest of the review has complete data.
Meeting catch-up happens once, inside the gathering helper (its Step 0 processes
every unprocessed meeting since the last one that was, following
`process-meetings/AGENT_INSTRUCTIONS.md`). Do NOT also run `/process-meetings`
from this conversation: that ran the whole meeting pass twice per evening, and
the second pass found nothing new.

- If no new meetings are found, continue silently
- If meetings are processed, note the count for the review summary
- Do NOT ask for a skill rating after this sub-step — save that for the end of the full review
- Read the helper's "Meetings processed in catch-up" line and carry the count
into the review summary
- If the helper reported meetings left unstamped or an older backlog, surface
those lines; do not re-run the pass to check them
- Only if the helper failed and the inline fallback is running does this
conversation process meetings itself, following the same brief

---

Expand Down Expand Up @@ -551,7 +560,7 @@ Add one line at the end of the review output:

| Integration | MCP Server | Tools Used |
|-------------|------------|------------|
| Meetings | Meeting source MCP (via `/process-meetings today`) | Fetches and processes unprocessed meetings |
| Meetings | Local meeting notes (helper Step 0 catch-up, following `process-meetings/AGENT_INSTRUCTIONS.md`) | Processes unprocessed meetings once per review |
| Work | work-mcp | `list_tasks`, `get_week_progress`, `get_commitments_due`, `analyze_calendar_capacity` |
| Calendar | calendar-mcp | `calendar_get_today` |
| Reminders | calendar-mcp | `reminders_list_completed`, `reminders_find_and_complete`, `reminders_clear_completed`, `reminders_list_items` |
28 changes: 28 additions & 0 deletions .agents/skills/process-meetings/AGENT_INSTRUCTIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,24 @@ hook does also run, because both skip a page that already lists the meeting.
- `--people-only`: only update person/company pages (skip tasks)
- `--no-todos`: create notes but do not extract tasks

**Work in batches, not one meeting at a time.** Every step below is written per
meeting for clarity, but the tool calls are independent across meetings, so
issue them together:

1. **Read everything first, in one turn:** the profile, the state file, the
directory listings, and every candidate note. Do not read one note, process
it, then read the next.
2. **Look up everyone at once:** one turn of `lookup_person` calls covering
every participant across every meeting, plus the calendar fetches (one per
date) and `detect_soft_commitments` for every meeting.
3. **Then write:** person and company page updates, `create_task` calls, and
markers. Writes to the same file stay sequential; writes to different files
can share a turn.

Sequential per-meeting processing is what made this brief take minutes for a
handful of meetings. The verification rules in Step 6 are unchanged: batching
changes when calls are issued, never whether their results are checked.

---

## Step 1: Resolve the Local Meeting Sources
Expand Down Expand Up @@ -331,9 +349,19 @@ Processing complete.
|------|---------|---------|--------------|
| ... | ... | ... | ... |

### Touched

- Tasks created: [every `task-YYYYMMDD-XXX` ID returned by `create_task`, or none]
- Notes stamped: [every vault-relative note path you appended the marker to, or none]
- Pages created: [every vault-relative person or company page path you created, or none]

[Any warnings or issues encountered]
```

The `Touched` block is what the conversation verifies before repeating your
counts, so it must list every item exactly, one targeted check each. A count
without a matching `Touched` entry is treated as unverified.

---

## Important Notes
Expand Down
29 changes: 20 additions & 9 deletions .agents/skills/process-meetings/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,11 @@ subagent via the Agent tool, using the self-contained prompt in this skill's

1. Read `.agents/skills/process-meetings/AGENT_INSTRUCTIONS.md`.
2. Substitute its placeholders (`{{ARGS}}`, the arguments passed to this skill).
3. Call the Agent tool with `subagent_type: "general-purpose"`, that prompt, and
a short description.
3. Call the Agent tool with `subagent_type: "general-purpose"`,
`model: "sonnet"`, that prompt, and a short description. The brief is
mechanical processing with its own verification steps, so it runs on the
fast tier; anything that needs a judgement call comes back to this
conversation.
4. Display its summary report.

The subagent inherits MCP connections, runs in its own context, and that context
Expand Down Expand Up @@ -71,13 +74,21 @@ until someone resolves it.

**The report is a claim, not evidence — check it before repeating it.** This
subagent writes to the vault, and its summary states counts the user will act
on. Before displaying it, verify the claims cheaply against the vault:

- Every task it says it created: confirm the ID appears in `03-Tasks/Tasks.md`
(`list_tasks`, or read the file).
- Every meeting it says it stamped: confirm the `tasks-extracted` marker is
actually in that note.
- Every person or company page it says it created: confirm the file exists.
on. Its report ends with a `Touched` block naming every task ID it created,
every note it stamped, and every page it created. Verify exactly those items,
and nothing wider:

- Each listed task ID: confirm it appears in `03-Tasks/Tasks.md` with one
targeted search per ID (`grep`, or `list_tasks` filtered to that ID). Do not
read the whole task file.
- Each listed stamped note: confirm the `tasks-extracted` marker is actually in
that one file.
- Each listed created page: confirm the file exists.

Do not re-read every meeting note or the full task list to look for claims the
report did not make; the full sweep was what made this step slow, and it never
found anything the `Touched` block did not name. If the report has no `Touched`
block, treat every count in it as unverified and say so.

If a claim does not hold, say so plainly in the summary you present and treat
that meeting as unprocessed. Never pass an unverified count to the user as fact,
Expand Down
25 changes: 21 additions & 4 deletions .claude/hooks/meeting-queue-check.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -277,11 +277,19 @@ function noticeLines(count) {
];
}

// `countOnly` answers "how many meeting records are waiting?" for a skill that
// is about to decide whether to run a meeting pass at all (daily-plan Step 0).
// It bypasses the 30-minute notice throttle and never writes the throttle
// marker: the throttle exists so concurrent sessions do not pile on the same
// notice, and a count is not a notice. Without this, a skill asking right after
// session start would always be told zero, because session start had just
// spent the throttle window.
function checkMeetingQueue(options = {}) {
try {
const {
vaultRoot = process.cwd(),
now = Date.now(),
countOnly = false,
} = options || {};
const nowMilliseconds = Number(now);
if (typeof vaultRoot !== 'string' || !Number.isFinite(nowMilliseconds)) {
Expand All @@ -293,7 +301,7 @@ function checkMeetingQueue(options = {}) {

const markerPath = path.join(paths.systemDir, '.last-meeting-queue-notice');
const nowSeconds = Math.floor(nowMilliseconds / 1000);
if (isThrottled(markerPath, nowSeconds)) return emptyResult();
if (!countOnly && isThrottled(markerPath, nowSeconds)) return emptyResult();

let meetingsDirectory;
try {
Expand All @@ -310,6 +318,7 @@ function checkMeetingQueue(options = {}) {
existingGranolaIds,
) + countQueueFiles(paths.meetingsDir, existingGranolaIds);
if (count === 0) return emptyResult();
if (countOnly) return { count, lines: [] };

try {
fs.mkdirSync(paths.systemDir, { recursive: true });
Expand All @@ -328,10 +337,18 @@ module.exports = { checkMeetingQueue };

if (require.main === module) {
try {
const result = checkMeetingQueue({ vaultRoot: process.argv[2] || process.cwd() });
if (result.count > 0) process.stdout.write(`${result.lines.join('\n')}\n`);
const args = process.argv.slice(2);
const countOnly = args.includes('--count');
const vaultRoot = args.find((arg) => !arg.startsWith('--')) || process.cwd();
const result = checkMeetingQueue({ vaultRoot, countOnly });
if (countOnly) {
// One integer, always, so a skill can branch on it without parsing prose.
process.stdout.write(`${result.count}\n`);
} else if (result.count > 0) {
process.stdout.write(`${result.lines.join('\n')}\n`);
}
} catch (error) {
// Silent by design.
// Silent by design. In --count mode the caller treats no output as unknown.
}
process.exitCode = 0;
}
Loading
Loading