savenow
Use this skill when the user wants to persist the current conversation into the active agent's daily memory file.
The command name is /savenow.
Scope: OpenClaw + Telegram only. Relies on sessions_list / sessions_history, the runtime MessageThreadId, and Telegram inline keyboards. Vanilla Claude Code, CLI, and web surfaces are not supported by design.
Supported forms:
/savenow— preview only. Extract candidates, compare semantically against today's memory, show markdown diff + Apply/Cancel inline buttons. Writes nothing./savenow apply— write the most recent pending preview (within the 30-minute TTL and from the same session/topic)./savenow cancel— discard the pending preview without writing./savenow auto— extract + compare + write directly. Skip preview and buttons./savenow auto <sessionKey>— auto on an explicit session./savenow list— show same-topic candidate sessions without writing./savenow <sessionKey>— preview against an explicit session.
Goal
Write only durable, high-value notes to memory/YYYY-MM-DD.md in the current agent workspace.
Do not patch built-in commands. Do not change Telegram settings. Do not write MEMORY.md.
What counts as durable
Keep:
- resolved root causes and fixes
- stable workflow rules and conventions
- preferences and routing rules
- important system mappings, ids, and file locations
- decisions that will matter again later
Skip:
- chit-chat and acknowledgements
- temporary plans or one-off status updates
- repetitive back-and-forth with no lasting value
- content already saved today in the same or very similar form
Routing
Inspect the raw command argument first and pick a branch:
| Arg | Branch | Writes? |
|---|---|---|
| (empty) | preview | no |
list | list (unchanged) | no |
apply | apply | yes, from pending |
cancel | cancel | no, deletes pending |
auto | auto on resolved session | yes |
auto <key> | auto on explicit key | yes |
| other token | explicit-session preview | no |
Preview branch — /savenow (and explicit-session preview)
-
Resolve the target session.
- If runtime exposes
CommandTargetSessionKey, use it. - If the raw arg is a non-empty value other than
list/apply/cancel/auto, treat it as the explicitsessionKey. - Else read the current
MessageThreadIdfrom runtime, callsessions_list, filter strictly:- same topic/thread only
- exclude keys containing
:slash: - exclude
cron,hook,nodekinds - exclude command-only or internal helper sessions
- If exactly one clear candidate remains, use it. If multiple, pick the newest non-slash real chat session from the same thread.
- If thread is missing, no same-topic candidate exists, or it stays ambiguous, fail closed and ask the user to rerun
/savenow <sessionKey>. Do not write.
- If runtime exposes
-
Read the transcript. Call
sessions_historyagainst the resolved target session withincludeTools: false,limit: 120..180. -
Read today's memory file (new step). Use the Read tool on
memory/YYYY-MM-DD.mdso the agent can perform semantic comparison. If the file does not exist yet, proceed as if empty. -
Extract
0..5candidate memory entries. Each entry must be specific, reusable, short, self-contained. For each candidate, perform semantic comparison against existing sections in the memory file and assign anaction:"add"— net-new durable note"merge"— substantial overlap with an existing section; adds new bullets to it (setmerge_target_titleto the exact existing title)"skip"— semantically already covered, or not durable enough
JSON shape (write to
temp/savenow-entries.json):[ { "candidate_index": 0, "title": "Gateway token mismatch fix", "bullets": [ "Resolved `unauthorized: gateway token mismatch` by updating `gateway.cmd` and the related env variables.", "Next time a similar error appears, check token and env alignment first." ], "action": "add", "reason": "" }, { "candidate_index": 1, "title": "Telegram inline button rules", "bullets": ["Inline keyboards use a single-row, 3-button layout."], "action": "merge", "merge_target_title": "Telegram UI conventions", "reason": "New rule belongs to the same existing topic." } ]Backward-compat:
{ "title": "...", "bullets": [...] }withoutactionis treated as"add". -
Run the preview script from the workspace root:
node "{baseDir}/scripts/preview-diff.mjs" \ --entries-file "temp/savenow-entries.json" \ --memory-path "memory/YYYY-MM-DD.md" \ --pending-file "temp/savenow-pending.json" \ --session-key "<resolved sessionKey>" \ --message-thread-id "<MessageThreadId>" \ --ttl-minutes 30 -
Pipe the script's stdout to the chat as the user-facing reply.
-
Render a Telegram inline keyboard below the markdown reply, two buttons in a single row:
✅ Apply→ callback/savenow apply❌ Cancel→ callback/savenow cancel
If the script reports 0 candidates, do not render the keyboard (nothing to apply).
Apply branch — /savenow apply
-
Read
temp/savenow-pending.json. Reject with a one-liner if:- file is missing →
"No pending preview, run /savenow first." expiresAt < now→"Preview expired (X min ago), rerun /savenow."sessionKeyormessageThreadIdmismatches the current runtime →"Pending preview is from a different topic. Rerun /savenow here."
- file is missing →
-
Run the merge script using
entriesFilefrom the pending JSON:node "{baseDir}/scripts/merge-daily-memory.mjs" --entries-file "<pending.entriesFile>" -
On success delete
temp/savenow-pending.json(and the entries file if you wish to clean up). -
Reply briefly (no buttons):
Saved to memory/2026-05-15.md: Gateway token mismatch fix (+2 bullets to "Telegram UI conventions"). Mini summary: saved gateway auth fix and a Telegram button rule.Mention added titles, merged titles (with bullet counts), and any
fallbackAdded/fallbackSkippedevents from the script's JSON output.
Cancel branch — /savenow cancel
- If
temp/savenow-pending.jsonexists, delete it. Also deletetemp/savenow-entries.jsonif it exists. - Reply:
Pending preview cancelled. Nothing written. - If there was no pending file, reply:
No pending preview to cancel.(not an error.)
Auto branch — /savenow auto [sessionKey]
-
Run steps 1–4 from the preview branch (resolve session, pull transcript, read today's memory, extract candidates with actions).
-
Skip the preview script and pending file.
-
Run the merge script directly:
node "{baseDir}/scripts/merge-daily-memory.mjs" --entries-file "temp/savenow-entries.json" -
Reply with the same "Saved to memory/… + mini summary" format. No buttons.
List branch — /savenow list
Call sessions_list and return same-topic candidate sessions only. Do not write.
Memory file format
# YYYY-MM-DD
## HH:MM - Short title
- bullet
- bullet
For merges, the script appends new bullets in-place to the matched section and adds a trailing - (merged HH:MM) marker bullet. A second merge into the same section replaces the previous marker rather than stacking.
Important constraints
- Default
/savenownever writes — it previews and shows Apply/Cancel buttons. - Only auto-resolve within the current topic/thread. If unresolvable, fail closed.
- Do not fall back to a global "most recent session