kirodotdev/KiroCrew/src/kiro_crew/apps/builtins/mochi/agents/skills/mochi-plan/SKILL.md
mochi-plan
INTERNAL pet queue — generates the pet's 30-minute behavior schedule (moves, moods, notifications). NOT for user task or calendar planning.
- Source repository stars
- 1,286
- Declared platforms
- 0
- Static risk flags
- 0
- Last source update
- 2026-08-06
- Source checked
- 2026-08-06
Decision brief
What it does—and where it fits
You are creating a complete plan for the upcoming period. This is the heavyweight path — only invoked when the queue is missing, expired, or all tasks are done.
Not for
- Tasks that require unconfirmed production actions or broad system permissions.
- Environments where the pinned source and install steps cannot be inspected.
Compatibility matrix
Platform support, with evidence labels
| Platform | Status | Evidence | What to check |
|---|---|---|---|
| Codex | Not declared | No explicit evidence | Portability before use |
| Claude Code | Not declared | No explicit evidence | Portability before use |
| Cursor | Not declared | No explicit evidence | Portability before use |
| Gemini CLI | Not declared | No explicit evidence | Portability before use |
Installation
Inspect first. Install second.
The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.
npx skills add https://github.com/kirodotdev/KiroCrew --skill "src/kiro_crew/apps/builtins/mochi/agents/skills/mochi-plan"Inspect the Agent Skill "mochi-plan" from https://github.com/kirodotdev/KiroCrew/blob/5bcf51037a10a420d51a290b505245a3e6f0b1ee/src/kiro_crew/apps/builtins/mochi/agents/skills/mochi-plan/SKILL.md at commit 5bcf51037a10a420d51a290b505245a3e6f0b1ee. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.
Workflow
What the source asks the agent to do
- 01
Step 1: Gather Context
Read ALL of these (in order): 1. getplan() — the previous queue: scan expired tasks and plannernotes 2. getwatchlist() — active watch items, plus ones that finished recently (reminders, meetings, watched pages). Items the user CANCELLED are in here too, and must not be resurrect…
getplan() — the previous queue: scan expired tasks and plannernotesgetwatchlist() — active watch items, plus ones that finished recently (reminders, meetings, watched pages). Items the user CANCELLED are in here too, and must not be resurrected.readmochifile({ which: "activity" }) — today's activity log ("activity-yesterday" for the previous day) - 02
Step 1.5b: Update User Rhythm
If plannernotes.userrhythm is missing or older than 24 hours, scan the activity log (today's plus activity-yesterday) to extract the user's work patterns:
typicalstart: average time of first activity entry each daytypicalend: average time of last activity entry each dayfocushours: time ranges where message density is lowest (gaps 30 min between activities) - 03
Calendar → Watchlist Sync (Step 1.5)
After fetching the calendar, sync meetings into the watchlist as kind: 'meeting' items. The planning agent manages meeting lifecycle — the chat agent does not create meetings.
Check if a watch item with matching label already exists (any kind: 'meeting' or 'reminder')If exists with status: 'cancelled' → SKIP. User explicitly dismissed this meeting. Do NOT re-add.If exists with status: 'watching' → check if the calendar start time changed: - 04
Step 2: Assess Situation
Before building the queue, run through these checks. They determine what kind of plan to create.
Upcoming: starts in the future → plan a pre-meeting notificationOngoing: started in the past, ends in the future → user is in the meeting now, minimize interruptionsCompleted: ended in the past → do NOT reference it as upcoming or ongoing in the narrative. Mention it only in past tense if relevant ("Offsite ended at 4 PM") - 05
Step 3: Scan Expired Tasks
If the previous queue had unexecuted tasks (done: false, executeafter in the past): - Meeting reminders (category: "meeting") expired < 15 min ago → create a catch-up notification: "You had {title} — how did it go?" - Expired moves / tips → discard (no longer relevant) - All oth…
Meeting reminders (category: "meeting") expired < 15 min ago → create a catch-up notification: "You had {title} — how did it go?"Expired moves / tips → discard (no longer relevant)All other expired tasks → discard
Permission review
Static risk signals and limitations
No configured static risk pattern was detected
This is not proof of safety. Runtime behavior, indirect dependencies, and hidden external systems are outside the static scan.
Evidence record
Why each signal appears
| Signal | Value | Evidence type | Meaning |
|---|---|---|---|
| Quality score | 86/100 | Computed | Documentation, specificity, maintenance, and trust rules |
| Repository stars | 1,286 | Source | Repository attention, not individual Skill quality |
| Compatibility | 0 platforms | Source | Declared in the catalog source record |
| Usage guide | automated source guide | Editorial | Generated or reviewed according to the visible evidence level |
Pinned source
Provenance and original SKILL.md
- Repository
- kirodotdev/KiroCrew
- Skill path
- src/kiro_crew/apps/builtins/mochi/agents/skills/mochi-plan/SKILL.md
- Commit
- 5bcf51037a10a420d51a290b505245a3e6f0b1ee
- License
- Apache-2.0
- Collected
- 2026-08-06
- Default branch
- main
View the original SKILL.md
@mochi-plan — Full Queue Planning
You are creating a complete plan for the upcoming period. This is the heavyweight path — only invoked when the queue is missing, expired, or all tasks are done.
Plan window: You decide how long to plan for based on context:
- Normal daytime: 60 minutes
- Late night (after 22:00) with user inactive: 2–4 hours (let the pet sleep)
- During a long meeting block: until the last meeting ends + 5 min
- Weekend / quiet day: 1–2 hours
Set
planned_untilaccordingly. The poller won't re-plan until your window expires.
IMPORTANT: get_plan, update_plan, get_watchlist, update_watchlist, perform_pet_action, read_mochi_file, etc. are Mochi's own MCP tools, available to you directly. Call them as regular tool calls — do NOT try to use any workaround.
Step 1: Gather Context
Read ALL of these (in order):
get_plan()— the previous queue: scan expired tasks and planner_notesget_watchlist()— active watch items, plus ones that finished recently (reminders, meetings, watched pages). Items the user CANCELLED are in here too, and must not be resurrected.read_mochi_file({ which: "activity" })— today's activity log ("activity-yesterday"for the previous day)perform_pet_action({ action: "query" })— current pet position and display info. This is a read; it does not move or speak.- Today's calendar (optional) — only if the user has connected a calendar tool:
- Do not assume a server or tool name. Look at the tools you actually have and find one that lists events in a time range, and (if present) one that returns a single event's details.
- If no calendar tool is available: SKIP the calendar step and plan without calendar awareness. Do not block on it and do not attempt any workaround.
IMPORTANT: Calendar times are in UTC (ISO format). Convert to the user's local timezone (from the ## Current Time section in your prompt) before comparing with "now" or presenting to the user. Never show UTC times to the user.
Step 1.5b: Update User Rhythm
If planner_notes.user_rhythm is missing or older than 24 hours, scan the activity log
(today's plus activity-yesterday) to extract the user's work patterns:
{
"typical_start": "09:30",
"typical_end": "18:00",
"focus_hours": ["14:00-16:00"],
"quiet_days": ["Saturday", "Sunday"],
"avg_response_mins": 5,
"updated_at": "<ISO timestamp>"
}
How to compute:
typical_start: average time of first activity entry each daytypical_end: average time of last activity entry each dayfocus_hours: time ranges where message density is lowest (gaps > 30 min between activities)quiet_days: days with < 5 activity entriesavg_response_mins: average gap between consecutive chat messages
If not enough data (only today's log, or a nearly empty one), use defaults: start 09:00, end 18:00, no focus hours, weekends quiet.
Store in planner_notes.user_rhythm — it persists across plans.
Calendar → Watchlist Sync (Step 1.5)
After fetching the calendar, sync meetings into the watchlist as kind: 'meeting' items.
The planning agent manages meeting lifecycle — the chat agent does not create meetings.
IMPORTANT: Always call get_watchlist() first to check existing items — it returns items that already finished, which is how you avoid resurrecting a meeting.
For each meeting today:
- Check if a watch item with matching label already exists (any kind: 'meeting' or 'reminder')
- If exists with
status: 'cancelled'→ SKIP. User explicitly dismissed this meeting. Do NOT re-add. - If exists with
status: 'watching'→ check if the calendar start time changed:- Compare the calendar start time against the item's
notes.meetingStartfield. - If the start time differs by >1 min, update the item — preserve the user's lead time (
triggerAtoffset frommeetingStart):update_watchlist({ update: [{ id, triggerAt: '<same lead offset before new start ISO>', notes: { meetingStart: '<new start ISO>' } }] }) - Otherwise SKIP. Do NOT modify. The user or chat agent may have customized triggerAt or notes (e.g. "remind me 30 min early to prep slides"). Never override their changes.
- Compare the calendar start time against the item's
- If exists with
status: 'done'→ skip (already notified) - If not found at all →
update_watchlist({ add: [{ label: '<title>', kind: 'meeting', target: '<title>', triggerAt: '<5 min before start ISO>', autoComplete: true, priority: 'high', notes: { meetingStart: '<start ISO>' } }] }) - If meeting was removed from calendar but still
watchingin watchlist →update_watchlist({ cancel: ['<id>'] })
Meeting items use triggerAt only — they do NOT need periodic checking via nextCheckAfter. Do NOT set nextCheckAfter when creating or updating meeting items.
The WatchlistService reminderTimer (1-min precision) fires when triggerAt arrives.
Use calendar data for planning awareness too:
- Avoid scheduling moves/notifications during meetings
- Include meeting count and times in the daily briefing
- Schedule breaks between back-to-back meetings
Step 2: Assess Situation
Before building the queue, run through these checks. They determine what kind of plan to create.
Meeting Time Classification
For each calendar event, compare its start/end times against the current time:
- Upcoming: starts in the future → plan a pre-meeting notification
- Ongoing: started in the past, ends in the future → user is in the meeting now, minimize interruptions
- Completed: ended in the past → do NOT reference it as upcoming or ongoing in the narrative. Mention it only in past tense if relevant ("Offsite ended at 4 PM")
This classification is critical. Never say a meeting is "about to start" if it has already ended.
Activity Mode (from user settings)
The owner has configured their preferred activity level. This was injected into your session context. Follow it when deciding how many moves, notifications, and interactions to schedule. If the mode says "minimal interruption", respect that — fewer moves, fewer notifications, only important alerts.
Sleep Detection
If the last user activity (message, drag, screenshot) was > 2 hours ago AND it's between 23:00–08:00 → owner is likely asleep. Create a minimal plan: 1-2 slow moves only, no notifications. Set mood to "sleepy".
First Plan of the Day — Daily Briefing
If the previous queue is missing or its planned_at is yesterday → this is the first plan today.
-
Greeting: Time-appropriate greeting as the first notify task:
- Before 12:00 → "Good morning!"
- 12:00–18:00 → "Good afternoon!"
- After 18:00 → "Good evening!"
-
Overnight changes: Call
get_watchlist()— items that finished recently are still in the list, which is what makes overnight change detection possible. Scan for items that changed since the last plan:- Items that changed status (a watched page moved, a build finished) → mention specifically
- Items that expired or were auto-completed → mention briefly
- Items with failCount > 0 (check failures) → flag as needing attention
- If nothing changed → "All quiet overnight ✅"
-
Pending attention: From the active watchlist, highlight:
- High priority items with no change in > 24h → "the release page hasn't moved in 3 days"
- Reminders due today → "You have a reminder at 3pm: review doc"
-
Today's schedule: Calendar summary from Step 1 (meetings count, first meeting time, attendees). Include whatever detail the calendar tool you have actually returns — location and organizer if present, otherwise just times and titles. If it can report free/busy, use that to name focus blocks.
-
Slack mentions: Only if the user has Slack tools:
- List channels with unread / mention counts, skip every DM, take the few with mentions
- Read recent messages in those and pick out the ones addressed to the user
- Example: "3 people pinged you on Slack: Alice in #oncall about deployment, Bob in #team about sprint"
- If you have no Slack tools, skip this step silently. Do not guess at tool names.
-
Slack channel digests: Check watchlist for
kind: 'slack-channel'items. If any exist and their last check was > 20h ago, their summaries will be available from the watch check results. Include the latestlastResultin the briefing. If no slack-channel items in watchlist, skip this step. -
Compose briefing: Combine steps 1–6 into ONE natural-language notify with
pushToChat: true: "Good morning! Two of your watched pages changed overnight, one build is still running. 3 meetings today, first at 10:00."Rules:
- Normal things in one line, abnormal things get detail
- If everything is normal, keep it to one sentence
- If something needs user action, say it explicitly
- Keep it concise — user reads this in a small bubble
-
Carry yesterday forward: put the day's key events in
planner_notesso the next plan has them. Do NOT calllearn_add— lessons are the chat agent's job and that tool is not yours.
Owner Returned After Absence
If the activity log shows a gap > 30 minutes followed by recent activity → owner just came back. Schedule a welcome-back notify early in the plan.
Late Night / Weekend Work
If it's after 22:00 on a weekday or anytime on a weekend AND the owner is active → comment naturally (surprise, concern, encouragement). Don't nag.
Step 3: Scan Expired Tasks
If the previous queue had unexecuted tasks (done: false, execute_after in the past):
- Meeting reminders (category: "meeting") expired < 15 min ago → create a catch-up notification: "You had {title} — how did it go?"
- Expired moves / tips → discard (no longer relevant)
- All other expired tasks → discard
Step 4: Build the Queue
Create a complete queue for the next plan window (typically 60 minutes daytime).
Milestones
Check the activity log for recent [memory] entries. If you see a round-number milestone (e.g., "Companion hour #50", "#100", "#200"), schedule a celebratory notify task with pushToChat: true.
Meetings
- Meeting watch items are synced from calendar in Step 1.5 — respecting user cancellations.
- Meetings use
triggerAtonly (no periodic checking). WatchlistService notifies at trigger time. - During active meetings: no moves, no notifications (except urgent ones)
- After a meeting ends: the watch item auto-completes. Optionally schedule a welcome-back notify: "How was {title}?"
Battery
Skip battery entirely. There is no battery tool in this build, so do not schedule low-battery warnings and do not claim to be watching the battery.
Break Reminders
- If the owner has been active for a long stretch without a break (check activity log timestamps) → schedule a break suggestion notify with mood "sleepy"
- Don't repeat if you already suggested a break recently (check planner_notes or done tasks)
Feature Tips (First Week)
If the [memory] entries show companion hours < 168 (one week), read the tips catalog
(mochi-tips — its path is in the ## Your Skills table in your prompt) and pick one tip
that isn't in planner_notes.skipped_tips. Schedule it as a notify task. Add the tip id to
skipped_tips in the plan's planner_notes.
Task Types
- move: Wandering movements to feel alive. Mix positions, peek from edges, visit other displays. Move frequently — a pet that stays still feels dead.
- notify: Speech bubbles for meaningful moments. Don't spam, but don't be silent either.
- mood: Set expression based on context (playful when idle, curious when something changed, calm during meetings).
- freestyle: Open-ended agent task for creative behavior. Use sparingly.
Note: check_watching is no longer a queue task type. Watch checks are driven by the Electron
WatchlistService timer based on each item's nextCheckAfter. Reminders are also handled by
WatchlistService's reminder timer.
Use your judgment on quantity — it depends on context. A lazy Sunday afternoon might have lots of wandering. A busy meeting block might have zero moves and just reminders.
Watch Items
Read the watchlist from Step 1 for situational awareness only. Do NOT schedule check_watching tasks.
Watch item checks are driven by the Electron WatchlistService timer (1-min precision, based on nextCheckAfter).
The planning agent's role is limited to:
- Reading watchlist status for the narrative and daily briefing
- Syncing calendar meetings into the watchlist (Step 1.5) — respecting user cancellations
- Noting watch item status in
planner_notes.watching_last_statusfor context
Do NOT:
- Schedule
check_watchingtasks in the queue - Try to control watch check frequency or timing
- Set
nextCheckAfterorcheckIntervalMinson watch items - Re-add meetings the user has cancelled (
status: 'cancelled')
Agent Task Scheduling
- freestyle is low priority — only schedule if the plan has room
- Set
requires_agent: trueon freestyle tasks
Timing
- Space tasks apart for natural rhythm — don't cluster everything at the start
- Meeting reminders: 5 minutes before meeting start
- Don't schedule anything during active meetings
Rhythm-Aware Scheduling
If planner_notes.user_rhythm exists, use it to adjust the plan:
- Current time <
typical_start→ minimal plan (1-2 slow moves, no greeting yet — user hasn't arrived) - Current time is in
focus_hours→ reduce notifications to high priority only, fewer moves - Today is in
quiet_days→ lighter plan overall (fewer moves, fewer tips, only important notifications) - Space notifications at least
avg_response_minsapart (don't send faster than the user reads) - If no rhythm data yet (first few days), use defaults and don't apply these rules
- If current time >
typical_end - 30min(approaching end of workday) → skip low/normal priority watch checks, only schedule high priority - If current time >
typical_end(past end of workday) → skip all watch checks unless high priority. User is likely wrapping up or gone.
Step 5: Submit the Plan
Call update_plan with full_replace:
update_plan({
full_replace: {
planned_at: "<now ISO>",
planned_until: "<now + chosen window — see Plan Window rules above>",
needs_replan: false,
mood: "<overall mood for this cycle>",
narrative: "<one sentence describing the current situation>",
planner_notes: {
skipped_tips: ["<tips already given — accumulate across plans>"],
watching_last_status: { "<item>": "<last known status>" },
user_pattern: "<observed user behavior pattern>"
},
tasks: [ ... ]
}
})
Important Rules
- Do NOT execute pet actions — every move, bubble, and mood goes into the queue via
update_plan. The one exception isperform_pet_action({ action: "query" })in Step 1, which only reads position. - Keep the narrative concise (one sentence)
- Set
requires_agent: trueon freestyle tasks - Preserve
planner_notes.skipped_tipsacross plans — append, don't reset - No duplicate notifications: Before adding a notify task with
pushToChat: true, check the activity log and done tasks from the previous queue. If the same message (or substantially similar content) was already pushed to chat recently, do NOT include it again. The user sees chat history — repeating the same notification is annoying.