import-claude-context
Skill396 starsChanged 6 months agoMITView on GitHub
Bring the owner's Claude Code history into Sutando: index, extract and haiku-summarise the stock ~/.claude/projects transcripts, stage the result, show the owner a digest and — only on their 'bring it in' — land it in core memory, notes/claude-import/ and People payloads. Triggers: 'import my Claude history', 'read my Claude Code sessions', 'bring my Claude context along', 'what did I work on in Claude Code', and the review replies 'bring it in' / 'bring in <slug>' / 'forget <slug>' / 'include <date>' / 'include personal' / 'hold <date>' / 'forget <date>'. Personal sessions are held back and never quoted; people the store already has are updated, never overwritten. Read-only on ~/.claude; conversation text only; transcripts are processed by Anthropic's Claude API, the provider the Sutando already runs on, which does not train on them. Nothing is uploaded to AG2 Space except the people you approve in the digest, which are saved to your People store.
- Installs packages
In this repository73 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- agent-activitySkill
- agent-registrySkill
- agent-room-opsSkill
- answer-formatSkill
- audio-transcribeSkill
- bee-actionsSkill
- bot2bot-postSkill
- call-diagnosticsSkill
- claude-codexSkill
- claude-geminiSkill
- claude-piSkill
- claude-routerSkill
- collaboration-intelligenceSkill
- connect-appsSkill
- context-dropSkill
- context-reconstructSkill
- db-browserSkill
- deal-finderSkill
- doc-ingestSkill
- electron-overlay-dimmingSkill
- email-findSkill
- engine-conflict-resolveSkill
- gemini-ttsSkill
- image-generationSkill
- local-workspace-serverSkill
- macos-toolsSkill
- macos-useSkill
- make-viral-videoSkill
- marketplaceSkill
- media-transcriptSkill
- meeting-prepSkill
- meeting-schedulerSkill
- mention-gateSkill
- model-switchSkill
- morning-briefingSkill
- ms365-connectSkill
- observeSkill
- obsidian-vaultSkill
- openai-ttsSkill
- open-sutando-refSkill
- overlay-appsSkill
- phone-conversationSkill
- proactive-loopSkill
- pr-shepherd-contractSkill
- quota-trackerSkill
- regression-searchSkill
- relaySkill
- releaseSkill
- report-feedbackSkill
- review-preflightSkill
- room-collabSkill
- schedule-cronsSkill
- screen-companionSkill
- screen-recordSkill
- self-diagnoseSkill
- self-upgradeSkill
- session-recapSkill
- skill-usage-reportSkill
- The first 60 shown.
The file
We split this skill into the rules it gives an agent
6 rules. About 0 are common, 2 are distinctive, and 4 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈2
Distinctive · estimate
This author's own choices. Worth a look.
4
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 25
Run only after the onboarding card's Import button or an explicit owner request.
- Distinctive: line 64
When the tool is not connected (or the digest said "Store not checked"), note "People sync pending" and leave it for the next run.
- Very specific: line 89
Write, move, rename or lock anything under ~/.claude — the tree is opened read-only.
- Very specific: line 90
Read subagents/ or any nested transcript, or sidechain sessions (first message isSidechain: true) — index.py skips them.
- Very specific: line 92
Put transcript text on disk before it has been noise-stripped and run through secret_scanner.scan_and_redact.
- Very specific: line 130
Every script also takes --root DIR (another projects dir), --out-dir/--data-dir, --workspace and finalize --memory-dir for tests and …
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Import Claude context
Sutando starts out knowing the owner's projects, decisions, open threads and people by reading the conversations they already had with Claude Code. The transcripts under the stock ~/.claude/projects/ (claude_home_path("projects", vanilla=True) — never Sutando's relocated .claude-sutando) are indexed without a model, the dialog is cleaned and redacted on disk, cheap-model subagents summarise it, the result is staged and shown to the owner as a digest, and only their "bring it in" moves it into the Sutando-owned sinks.
Usage: /import-claude-context [--projects a,b] [--session uuid] [--since 30d] [--dry-run] [--counts-only] [--json] [--new] [--cloud] [--stage [--known-people file]] [--commit] [--discard] [--forget slug] [--include date|uuid] [--include-personal] [--hold date|uuid] [--forget-session date|uuid] [--purge-dumps]
When it runs
- Owner ask (any install, standalone or desktop) — the owner says "import my Claude history", "read my Claude Code sessions", the Settings button's sentence "Import my Claude Code history, consented in Settings at
<ISO>.", or invokes/import-claude-context. Same procedure with--run-kind user. - AG2 Space desktop onboarding — the owner's first-contact DM message carries the import request when they consented on the onboarding card: "Hi — I'm all set up, say hello. Then import my Claude Code history: I consented on the onboarding card at
<ISO>, found N conversations across M projects." The trigger is an owner message containing "import my Claude Code history" together with "consented on the onboarding card", both case-insensitively — the standalone form below opens the sentence, so it capitalises "Import" (classify.py'sIMPORT_TRIGGER_SENTENCESalready matches case-insensitively; this keeps the documented rule and the classifier in step). Match the phrase, not theat: the consent time follows it asat <ISO>only when the client recorded one, and as(time not recorded)when it did not — a consent captured before the client stored a timestamp is stated, never invented, so a trigger keyed onatstops matching exactly those installs. The counts clause is optional the same way. The request also arrives standalone, without a greeting, when the card consent was given but its first-contact message already went out: "Import my Claude Code history, consented on the onboarding card at<ISO>, found N conversations across M projects." — same case, same--run-kind onboarding. Run the procedure below with--run-kind onboarding; the message's counts are the card's file count, the acknowledgement uses the index'sconversations(step 2). Legacy / fallback: an earlier desktop wrote the same request as its own task file —channel_id: onboarding-wizard,user_id: onboarding-wizard,access_tier: owner,priority: low, body "Run the import-claude-context skill (onboarding run). Consent given on the onboarding card at<ISO>…" — and installed clients may still do so; treat that task exactly as this case (it is consented;priority: loworders it after a pending greeting and defers nothing — seesrc/task_priority.py). Its result is[no-send](the wizard channel has no delivery path). A queued import task is never left intasks/for a later turn:/task-orphan-checkstep 3a keeps a started one out of the recovery DM (a started run whosestatus.jsonhas not moved for an hour is listed there as stalled, and still resumes on the next sweep), but only this procedure starts it. - Review reply — while
$DATA/staged/review.mdexists, an owner message saying "bring it in", "bring in<slug>", "forget<slug>", "include<date>", "include personal", "hold<date>" or "forget<date>" answers the pending review: run the matching command from step 10 and nothing else. Any other message is not an answer; the set stays staged. The owner may ask what a held session was about: answer in the owner DM only, on request, from the summary — answering does not include it.
This skill is part of the engine and needs nothing from AG2 Space: a standalone Sutando (an OSS install with no desktop app) has cases 1 and 3, where the owner asks in whatever channel they talk to their Sutando in — "DM" below means that owner channel. Case 2 is only how the AG2 Space desktop's onboarding card and Settings button reach the same procedure with the same consent.
In cases 1 and 2 the import is a background job: the triggering message gets its reply (case 2: the hello plus one line that the import has started; case 1: the acknowledgement text of step 2 as the reply itself, no separate proactive file), and everything after that — the digest, a failure note — is a proactive message to the owner DM (results/proactive-<ts>.txt). The legacy task file's own result is [no-send], with the acknowledgement as a proactive message. The owner's answer (case 3) arrives as a later task.
Consent contract
- Run only after the onboarding card's Import button or an explicit owner request. A mention of Claude Code in passing is not consent; the health check, proactive loop and crons never start this on their own.
- That consent covers reading, summarising and staging. Landing the result is a second, separate yes: nothing reaches core memory,
notes/or the People store until the owner has read the digest (staged/review.md) and replied "bring it in" (or "bring in<slug>") in the same conversation. - An incremental re-run (
--new: only sessions changed since the last import) needs no new import consent — the first consent covers keeping the import current — but its result is staged and reviewed like the first. Say what you are doing when you do it. --cloud(uploading the summaries to cloud-recall, corpusclaude-import) is a separate, explicit yes every time. Never in the onboarding run; never inferred from the first consent.- Before the first full run, when the owner is present, state the counts (
index.py --counts-only --json— a file count, so "about N transcripts across M projects"; the exact number the owner is told is the index'sconversations, step 2) and that a digest will follow for approval, then proceed. The onboarding run has the import consent already; do not ask again in the DM — the acknowledgement (step 2) and then the digest are the next things the owner sees.
Procedure
Set DATA=<workspace>/data/claude-import (per host, not vault-synced) and S=skills/import-claude-context/scripts. Every script resolves the workspace through bash scripts/sutando-config.sh workspace.
The slow part (summaries, roll-ups, entities, staging) never runs in the core's task loop: the main task indexes, extracts, acknowledges, spawns ONE background subagent and returns [no-send] — "at background" means spawn a subagent (CLAUDE.md), and the core stays free for other tasks. status.json phases indexed → extracted → summarizing → rolling-up → staged → done let the desktop show progress; from step 5 on it is kept current by progress.py, never by hand. Every status also carries the run's identity — task_id (the task that started it) and run_id — read from state.json's run, which index.py --task-id stores at run start; the orphan check uses it to tell this task's run from an older one.
Main task (seconds):
- Greeting and import in the same turn. When the import request rides on the owner's first-contact message (case 2), the reply is one message: a short hello first, then one line that the import has started (the step-2 acknowledgement is that line — do not send it twice). When a separate greeting or owner message is waiting alongside a legacy import task, answer it first — a short hello, nothing about the import — then immediately run steps 1–4 of this procedure before ending the turn: index, acknowledge, extract, spawn the coordinator, write the task's
[no-send]result. The import is a background job, but starting it is not deferrable: nothing re-delivers a task the core has already been told about — the watcher emitsTASK_FILE:once,priority: lowis ordering metadata, not a hold, and the startup ceremony (registration, boot recap,/startup complete) is no reason to stop. Never leave the import for a later turn, for the proactive loop, or for a nudge from the owner; a task left intasks/after this turn is a bug (2026-09-11: the core answered the hello, reported startup, went idle, and the import sat for six minutes until the owner typed "run the import"). - Index (no model):
python3 $S/index.py --task-id <id> --json→$DATA/index.json+$DATA/claude-import-index.md(per project: recoveredcwd, session count, date range; one[ ]row per session with title + date). Phaseindexed.<id>is the task this run answers — theid:header of the task file (task-claude-import-<ms>for the legacy form) or, for a message-triggered run (cases 1–2), the task id of the owner message that carried the request (theTASK_FILE:the core was handed); always pass it. The full pass mints arun_idand storesrun: {task_id, run_id, started_at}instate.json;extract.py,progress.pyandfinalize.pycarry both ids into everystatus.jsonthey write./task-orphan-checkmatches a status to a task only by thattask_id— a run started without one can never mark its task done (the task is kept and listed in the recovery DM), so omitting the flag costs a spurious recovery line, never the import.--dry-runprints the counts and writes nothing;--counts-onlyis readdir+stat only — a file count that cannot tell a sidechain or a never-answered session apart, good for a fast estimate only. The full index reportssessions,empty(sessions with no assistant message: aborted or never answered — nothing gets summarised for them) andconversations=sessions − empty;conversationsis the number the owner is told. The reportednewis the number of sessions changed since the last index. Acknowledge immediately — with
conversationsandprojectsfrom the index (NandMbelow) and nothing else; append(K empty ones skipped)after "M projects" only whenemptyis > 0. When the request came as an owner message (cases 1 and 2), this text is the reply to that message — after the hello in case 2 — written as the task's result in step 4; only the legacy task file (no delivery path) gets it asresults/proactive-<ts>.txt(owner DM). One acknowledgement, never both:Importing your Claude Code history: N conversations across M projects. I'll message you here when it's ready to review — usually 10–20 minutes. Your transcripts are processed by Anthropic's Claude API, the provider your Sutando already runs on, which does not train on them. Nothing is uploaded to AG2 Space except the people you approve in the digest, which are saved to your People store.
- Extract:
python3 $S/extract.py --json(add--newon re-runs,--projects/--sessionto narrow,--max-chars-totalto cap; default 8,000,000 chars, newest sessions first). Produces$DATA/dumps/<slug>/<uuid>.<n>.txt— dialog only, harness noise stripped, secrets redacted, ≤120k chars per chunk, 0600 files in a 0700 dir. A session whose cleaned dialog has no turn or fewer than 400 chars (--min-chars) gets no dump: it isskipped_emptyin the counts andskipped_empty: trueinstate.json(a--newre-run leaves it alone until the file changes).extractedcounts only the sessions that got a dump, soextracted+skipped_empty+errorsis what was looked at. Phaseextracted. - Spawn the import coordinator — ONE Agent-tool subagent, run in the background, briefed with steps 5–9,
$DATA,$S, the run kind and the slugs to cover. It may run on the default model (it only orchestrates and reads counts) but must never paste transcript text; its per-session workers aremodel: haiku. Then end the task: for a message-triggered run the result is the reply (case 2: the hello, then the step-2 acknowledgement; case 1: the acknowledgement), for the legacy task file it is[no-send]. Do not wait for the subagent; do not summarise inline; do not hand the work back to a later session or a later turn.
Import coordinator (background subagent):
- Session summaries — one Agent-tool subagent per session with
model: haiku(owner rule, session-recap: never spend core-model quota on transcripts), prompt =prompts/session-summary.mdwith the slug/uuid/chunk paths filled in. The subagent Reads the chunk files and Writes$DATA/summaries/<slug>/<uuid>.jsonitself; do not paste dump text through the coordinator. Fan out in parallel batches; skip sessions whosesummaries/<slug>/<uuid>.jsonis newer than theirextracted_atinstate.json(resumable) and sessions markedskipped_empty: truethere (they have no chunk files — no worker). After each batch runpython3 $S/progress.py(no model): it recounts from disk — sessions minusskipped_empty, summaries present, roll-ups, entities, staged manifest — and rewritesstatus.json(phasesummarizingwithsummarized/sessions,rolling-uponce every session is in). Never writestatus.jsonby hand. A subagent that answersSTOPmarks a missing or unreadable input — fix or skip, never retry blindly. Each summary carriespersonal(true when the session was mainly about the owner's private life; the prompt says when) andpersonal_reason(a generic category, ≤ 6 words): a personal session is held — it feeds nothing downstream, the owner sees only its date and reason, and the owner's own--include/--hold(step 10) outrank the classifier.python3 $S/finalize.py --held-jsonlists the held sessions (project, uuid, date, reason — no titles) so the next two steps can leave them out. - Project roll-ups — one haiku subagent per project, prompt
prompts/project.md; reads that project's non-personal session JSONs (list only those — never a held one; a project whose sessions are all held gets no roll-up), writes$DATA/projects/<slug>.json(roll-up +note_markdown+sessions, the uuids it was written from). Incremental runs pass the previous roll-up so facts carry forward. Thenpython3 $S/progress.py. - Entities — one haiku subagent over all non-personal summaries, prompt
prompts/entities.md, writes$DATA/entities.json(people, companies, deals, decisions, open threads, every entry cited). Thenpython3 $S/progress.py. - Stage (nothing lands yet). First, if the station's People tool is connected, run
people__list_peopleand write$DATA/known-people.json— a JSON array with onlyid,slug,name,emailandidentifiers.emailsper person (no dossiers). Thenpython3 $S/finalize.py --stage --run-kind onboarding|user --known-people $DATA/known-people.json --json(drop the flag when the tool is not connected: the digest then says "Store not checked" and step 10 upserts nobody). It de-duplicates the entities in memory (people sharing an email are one person; a bare first name folds into its unique full name; companies by name —people_merged/companies_mergedin the JSON;entities.jsonitself is not rewritten), drops every citation of a held session, matches each remaining person against the store (any email first, then the normalised name — diacritics folded), and renders everything under$DATA/staged/:memory/claude_import.md(≤ 2,000 B),notes/claude-import/<slug>.md+overview.md,people.json(people with ≥ 2 citations: every one the store already has as an UPDATE payload, plus ≤ 25 new ones — the published set, computed the way--commitwill publish it: the selected projects' live citations and fields unioned with every other landed project's approved citations and fields, so a person who clears the two-citation floor only through the union, or whose fields change, is shown before it lands),known-people.json(the listing as used — re-stages and the commit reuse it),review.md(the digest) andmanifest.json(held,stale_rollups,changed_since_approval,people_changed_since_approval,known_people: checked|not checked,people_ambiguous);status.jsonphasestaged. It touches neithermemory_dir(), nor<workspace>/notes, norMEMORY.md. The memory file andoverview.mdare single files over every landed project: they are previewed (and, at the commit, rebuilt) from the approved snapshots —$DATA/approved/<slug>.json, one per landed project, written by--commit— plus the staged set, never from a landed project's currentprojects/<slug>.json. A landed project whose roll-up changed since its approval (a re-run nobody reviewed) is listed under Memory in the digest as "changed since approval — bring in<slug>to refresh" and keeps its approved text until the owner brings it in again: stage it (--stage --projects <slug>, shown as an update to its note), re-post the digest, commit on their yes. The approved People export is the same kind of shared file: a landed project whose people changed since its approval (an entities re-run added a person or a citation nobody reviewed —$DATA/approved/people-inputs.jsonkeeps apeople_hashesfingerprint per project) is listed the same way under People and keeps its approved citations in$DATA/people.jsonuntil it is brought in again. The digest's People section also shows, under "Updates to people already in your export", the concrete field diff for each shared person the union changes (e.g.role: CTO → …, with the citation count) — compared on the values the dossier publishes (role/company at 80 chars, relationship at 200), never on a shorter preview; a value over 120 chars is shown as its shared prefix elided (…) plus both tails, so a changed suffix is always visible — a person whose published fields equal their approved entry has nothing to review and is not listed.--stageis the default: a barefinalize.pycannot write into a sink. A project markedstale_rollupsin the manifest (its roll-up was written with a held session, without an included one, or lists a forgotten one) is staged with a placeholder body and cannot be committed: re-run that project's roll-up (step 6) and stage again before posting the digest. Thenpython3 $S/finalize.py --purge-dumpsunless the owner asked to keep the dumps (the review and the commit read the summaries, not the dumps). - Digest — write
results/proactive-<ts>.txtfor the owner DM only containing$DATA/staged/review.mdverbatim; it ends with the reply instructions ("Reply 'bring it in' to save this to your Sutando, 'bring in<slug>' for one project, or 'forget<slug>' to drop one."). This is the ONE place transcript-derived text is shown to the owner: one paragraph per project, its open threads and decisions, the people it would add — "Already in your People store" (name, matched on email or name, citations), "New" and "Needs your call" (a name two store entries share), and under them any landed project whose people "changed since approval" — the size of the memory summary (and under it any landed project whose roll-up "changed since approval", with the reply that refreshes it), and "Held back as personal (N):<date>·<reason>" with the include/hold instructions; a held session is never quoted, not even its title. If any step 5–8 fails part-way, write a short proactive message instead — what was done, as counts ("summarised K of N sessions"), and "say 'import my Claude history' to continue"; the run is resumable and the owner is never left without a message.
Later task (the owner's reply, case 3 above):
- Review reply — the commit happens only here, on the owner's explicit yes, never in the run that produced the digest:
- "bring it in" →
python3 $S/finalize.py --commit --json: moves the staged set into the sinks with the usual guards — memory file ≤ 2,000 B, theMEMORY.mdrow only whenmemory-index-budget.pyallows, notes with a dated## Updatesection on re-runs — snapshots each landed roll-up as approved ($DATA/approved/<slug>.json), rebuilds the memory file andoverview.mdfrom the approved snapshots only, writes the approved People export ($DATA/people.json, with its inputs in$DATA/approved/people-inputs.json) — a full commit publishes the staged people set verbatim (exactly what the digest showed, fingerprint-guarded like the rest of the staged set; a--projectssubset publishes only people from that same staged set, cut to the landed projects' citations — see "bring in<slug>"), so what lands inpeople.jsonis exactly what was reviewed and no field is recomputed from a laterentities.jsonat commit time —, setssummarized_atper session (never for a held one) and phasedone, prints the counts (changed_since_approvalandpeople_changed_since_approvalcount the landed projects still waiting for a "bring in<slug>": a roll-up, or people, that changed since they were brought in). Thenpython3 $S/finalize.py --people-json(the approved copy, matched against the store listing taken at staging) and, when the station's People tool is connected, handle each payload by itsexistingfield:existing: null— a new person:people__upsert_personwith the payload as it is (name,doc,email/identifiers,source: claude-import,last_interaction_at).existing: {id, slug, matched_on}— the store has them:people__get_person(id)→ write itsdocto a file and the payload'sdoc_appendto another →python3 $S/finalize.py --people-doc-merge --existing-doc <file> --append <file>→people__upsert_personwith the sameslug/id, the merged markdown asdoc, the payload'sidentifiers,sourceandlast_interaction_at. Never a fresh doc, never a second record.- a name under "Needs your call" (
people_ambiguousin the manifest) is upserted for nobody: ask the owner in the completion message which store entry it is (or that it is someone new), then fixknown-people.jsonaccordingly (keep the entry they named, or add the person's email to it) and run--people-json --projects <slug> --known-people $DATA/known-people.jsonfor that one. When the tool is not connected (or the digest said "Store not checked"), note "People sync pending" and leave it for the next run. People are upserted only after the commit, never from the staged list.
- "bring in
<slug>" →python3 $S/finalize.py --commit --projects <slug> --json(an exact slug or a unique part of one; ambiguous is refused). The rest stays staged andreview.mdis re-rendered for it. It publishes topeople.jsononly what the current digest showed for that project: the people instaged/people.json, each cut to the landed projects' citations (this project's, plus the approved citations of projects landed earlier) and kept only while those still clear the two-citation floor — nobody is added or re-ranked, so a person the 25 cap hid behind other projects' names in the combined digest is not published by "bring in<slug>". When the owner wants that project's own view (the people the cap hid), re-stage it alone —--stage --projects <slug>— re-post that digest and commit on its yes. Thenpython3 $S/finalize.py --people-json --projects <slug>(a landed project's payloads come from the approved inputs, restricted to the peoplepeople.jsonpublished; it reuses the staged store listing — pass--known-peoplewhen nothing is staged any more) and handle those as above — only people cited at least twice inside the approved project. - "forget
<slug>" →python3 $S/finalize.py --discard --projects <slug>while it is still staged;python3 $S/finalize.py --forget <slug>once it has been committed — an exact known slug or a unique part of one, never a path:../x,/abs,a/b,..,~, an empty, unknown or ambiguous value is refused and deletes nothing, and a target that is a symlink or resolves outsidenotes/claude-import/,data/claude-import/or the memory dir refuses the whole command before anything is touched. It removes the project's note, roll-up, summaries, dumps, state, entity citations and approved snapshot, and drops its citations from the approved People export (people_revokedin the JSON: people that fall under the 2-citation floor go with them; tell the owner which names, so they canpeople__delete_personthe ones only that project justified). A plain--discarddrops the whole pending set. - "include
<date>" →python3 $S/finalize.py --include <date> --json(a uuid, or its first 8+ characters, when the date is shared — the refusal lists the uuids); "include personal" →--include-personal. The session re-joins its project and the pending review is re-rendered; when the JSON saysstale_rollups> 0 the project's roll-up was written without it — re-run that roll-up (step 6, now listing the session), stage again and re-post the digest before any commit. - "hold
<date>" →python3 $S/finalize.py --hold <date> --json: holds a session the classifier let through (a landed session is refused — the note is on disk); the approved People export loses its citations (people_revoked). Its roll-up is now stale: re-run it without the session, stage again, re-post. - "forget
<date>" →python3 $S/finalize.py --forget-session <date> --json: deletes that session's summary, dumps, state entry and entity citations, and drops it from the approved People export (people_revoked); the index row (Claude Code's own metadata) stays until the next index run, and a later--newrun summarises the session again unless the transcript is gone or the owner holds it. A roll-up that listed it is stale: re-run, stage, re-post. - Anything else, or no reply, leaves the set staged. Never commit on silence, on a re-worded question, or on the original import consent.
--commitis refused when nothing is staged, when the summaries changed since staging (stale), or when a chosen project's roll-up is stale: fix, stage again and re-post the digest.--commitnever lands a held session.
- "bring it in" →
Completion message (after the commit; counts only, plus the People names so they can be undone), as the reply to the owner's message:
Saved your Claude Code history: N sessions across M projects (K new; H held back as personal). Wrote
notes/claude-import/overview.md+ M project notes and a 2 KB memory summary (MEMORY.md row: added | skipped — index full). People added to your store: A, B, C (say "forget<name>" to undo); updated with new interactions: D, E. [Needs your call: F — is that <store entry 1> or <store entry 2>, or someone new?] Your transcripts are processed by Anthropic's Claude API, the provider your Sutando already runs on, which does not train on them. Nothing is uploaded to AG2 Space except the people you approve in the digest, which are saved to your People store. Say "import my Claude history" any time to pick up new sessions;/import-claude-context --forget <slug>removes one project.Never quote transcript text, titles of private sessions, or paths under
~/.claudein the acknowledgement, the completion message or a failure note; the digest is the only message that carries derived text, and only in the owner DM. A held session is named by date and reason there and nowhere else.
Never do
- Commit (
finalize.py --commit) without the owner's explicit yes — "bring it in" or "bring in<slug>" — in the same conversation as the digest. Silence, the original import consent, a yes from anyone but the owner, or a stale staged set is not a yes. Upsert People only after that commit. - Overwrite an existing person's dossier or create a second record for someone the store already has — existing people only gain an appended section and merged identifiers (
existingpayloads: fetch,--people-doc-merge, upsert with the same slug/id). Never upsert a "Needs your call" name until the owner has said who it is, and never upsert anyone when the store could not be listed. - Quote, title, summarise or land a held (personal) session anywhere but the owner DM on the owner's direct question: the digest, the notes, the memory file, the People payloads, the manifest and the completion message carry its date and reason at most. Including it is the owner's call ("include
<date>"), never the coordinator's. - Refresh the memory file,
overview.mdor the approved People export ($DATA/people.json) from a roll-up or an entities pass the owner has not brought in.--commitrebuilds the first two from the approved snapshots ($DATA/approved/<slug>.json) and never from a changedprojects/<slug>.json, and takes the export's people for every project outside the commit from$DATA/approved/people-inputs.json, never from a changedentities.json: a landed project the digest lists as "changed since approval" (under Memory, or under People) keeps its approved text and citations until "bring in<slug>" — committing another project does not refresh it, and neither do you by hand. - Publish anything to
people.jsonthat the digest did not show.--stagecomputes the published set — the union of the selected projects' live people with every landed project's approved people — and a full--commitpublishes that staged set verbatim; if a person's field change (a role, a company, a merged email) is not rendered inreview.md, their approved fields stay byte-for-byte after the commit. Never work around the fingerprint guard that refuses a stale staged set, and never recompute the export from liveentities.jsonat commit time. - Pass anything but a known slug (or a unique part of one) to
--forget, or build a deletion path from the owner's words yourself. A path (../x,/abs,a/b),..,~, an empty or unknown value is refused; a target that is a symlink or resolves outsidenotes/claude-import/,data/claude-import/or the memory dir refuses the whole command before anything is deleted. Nothing the import deletes ever lies outside those three places. - Index a title, prompt, summary or agent name without the importer's redaction:
index.pyruns_common.redact_text(secret_scannerplus the Google/Anthropic key shapes — the same objectextract.pyuses on the dumps) on every metadata string before it is cut to length; the generic scanner alone let ansk-ant-…key into the index, and a cut before the redaction can leave half a key. Do not add a second scanner or a shortcut around it. - Keep an approved People export (
$DATA/people.json) that cites a forgotten project or session, or a held one:--forget,--forget-sessionand--holdshrink it to the citations still approved (people under the 2-citation floor go) starting from the published set — never a re-ranking ofapproved/people-inputs.json, so a person the cap hid from the digest is never surfaced by a removal — and it never grows without a new commit; do not hand-edit it and do not re-upsert from an old copy. - Block the task loop on summarisation, or defer the start. The main task returns its reply (or
[no-send]for the legacy task file) right after spawning the background coordinator; summaries, roll-ups, entities and staging run in that subagent, never inline in the core task and never "later". Steps 1–4 run in the turn that received the request — a greeting to answer, the startup ceremony, a boot recap orpriority: lowon the legacy task is not a reason to end the turn with the import unstarted. - Post the digest (
staged/review.md) anywhere but the owner DM (results/proactive-<ts>.txt) — not in a channel, a task result, a log or a note. - Write, move, rename or lock anything under
~/.claude— the tree is opened read-only;index.py/extract.pyrefuse an out-dir inside it andfinalize.pyrefuses a memory dir under it. Do not runfinalize.pyoutside the core without--memory-dir. - Read
subagents/**or any nested transcript, or sidechain sessions (first messageisSidechain: true) — index.py skips them; do not go around it. - Let tool I/O, attachments, images or thinking blocks into a dump, a prompt or a note. The extractor takes user + assistant text only; keep it that way.
- Put transcript text on disk before it has been noise-stripped and run through
secret_scanner.scan_and_redact. Keep[STORED-IN-KEYCHAIN-…]placeholders verbatim; never reconstruct a secret. - Log content.
status.json, stdout of the scripts (--json), the app log and the completion message carry counts only — no titles, prompts, quotes or paths. The digest (staged/review.md) is the one exception and goes to the owner DM only. - Send content anywhere but the summarising haiku subagents (Anthropic's Claude API, the provider this Sutando already runs on, which does not train on them) without
--cloudand a fresh explicit yes. No cloud-recall upload, no telemetry with content, no sharing of notes, nothing to AG2 Space. - Summarise with anything but haiku; never paste dumps into the core session.
- Install anything to run this skill — no
pip install, no--break-system-packages, no venv, no brew — and never offer to in the DM. Whensecret_scannerprintsmode: DEGRADED(thedetect-secretspackage is absent on this Mac), the repo-local rules still run and the run proceeds: keep the acknowledgement exactly as in step 2 and add one line at the end of the digest — "Note: secret redaction used the built-in rules only on this Mac." — nothing more. - Touch
user_profile.md(read by literal name insrc/voice-context.ts), or write theMEMORY.mdrow whenmemory-index-budget.pyrefuses — the file stays, the row is logged as skipped. - Re-run on a schedule, auto-import for a non-owner, or import for a different user's home (
$SOURCE_CLAUDE_CONFIG_DIRis honoured only because migration scripts already use it).
CLI reference
| flag | script | effect |
--task-id <id> | index | the task this run answers (task header id:; for a message-triggered run the owner message's task id). Stored with a fresh uuid4 run_id under run in state.json; every status.json write carries both. Omitted → task_id: null, and the orphan check keeps the task rather than archiving it |
--projects a,b | index, extract, finalize | index/extract/--stage: slugs matching exactly or as a case-insensitive substring of the opaque slug (e.g. gtm); --commit/--discard/--people-json: an exact slug or a unique part of one — no match or an ambiguous one is refused |
--session uuid | extract | one session (prefix allowed) |
--since 30d | index | only transcripts modified in the window (12h, 2w, YYYY-MM-DD also) |
--dry-run | index | full pass, writes nothing, prints counts |
--counts-only | index | readdir + stat only; opens no transcript; a file count for the pre-run estimate — the acknowledgement uses the full index's conversations (= sessions − empty) |
--json | all | counts as JSON on stdout (counts only) |
--new | index, extract | index: list only sessions changed since the last index; extract: only sessions changed since their last extraction |
--cloud | (skill) | upload the finished summaries to cloud-recall corpus claude-import — separate explicit consent; not implemented in the scripts yet, do it by hand only when asked |
--stage | finalize | the default: de-duplicate the entities and render the review set under $DATA/staged/ (memory file, notes, people.json, review.md, manifest.json); writes no sink, phase staged |
--commit | finalize | move the staged set into the sinks — only on the owner's yes; refused (exit 1) when nothing is staged or the staged set is stale; with --projects lands that subset and re-stages the rest (phase done, or staged while something is pending); snapshots each landed roll-up as approved ($DATA/approved/<slug>.json) and rebuilds the memory file and overview.md from the snapshots only — the JSON's changed_since_approval counts the landed projects whose roll-up changed since they were brought in; the approved People export takes the landed projects' people from the reviewed entities and every other landed project's from $DATA/approved/people-inputs.json — people_changed_since_approval counts the landed projects whose people changed since they were brought in; a full commit publishes staged/people.json verbatim, a --projects subset only the people that file lists, cut to the landed projects' citations (never a recompute over the narrowed projects: a person the cap hid from the digest is never published unseen) |
--discard | finalize | drop the staged set (or the --projects subset) without touching the sinks |
--people-json | finalize | print the People payloads (≥ 2 citations; every existing person + ≤ 25 new) — the staged copy while a review is pending, else the last commit's approved copy ($DATA/people.json), else computed; --projects restricts to people cited inside those projects (from the approved inputs when every one of them has landed — and then only people $DATA/people.json published —, else recomputed; matched against --known-people, else the staged listing). Approved only by --commit |
--known-people FILE | finalize --stage, --people-json | the station's people__list_people output (JSON array: id, slug, name, email, identifiers.emails); matches by email then normalised name; without it the manifest says known_people: "not checked" and every payload is existing: null |
--people-doc-merge --existing-doc F --append F | finalize | print the existing dossier with the doc_append section merged in — a section under the same heading is replaced, otherwise appended (idempotent) |
--held-json | finalize | the held sessions: project, uuid, date, reason — never a title |
--include date|uuid, --include-personal | finalize | lift the hold on one session (a uuid or its first 8+ characters when a date is shared; ambiguous is refused with the uuids) or on all; recorded in state.json, a pending review is re-rendered; stale_rollups in the JSON says the roll-up must be re-run |
--hold date|uuid | finalize | hold a summarised session the classifier missed (refused once it has landed); the review is re-rendered, the roll-up is stale until re-run, the approved People export loses its citations (people_revoked) |
--forget-session date|uuid | finalize | delete that session's summary (and partials), dumps, state entry and entity citations, and drop it from the approved People export (people_revoked); a roll-up that lists it is stale until re-run |
--forget slug | finalize | an exact known slug or a unique part of one — never a path (../x, /abs, a/b, .., ~, empty, unknown or ambiguous is refused, exit 1, nothing deleted; a symlink or a target outside notes/claude-import/, data/claude-import/ or the memory dir refuses the whole command): removes exactly that project's note, summaries, dumps, roll-up, approved snapshot, memory line, state, entity citations, staged copy and its citations in the approved People export (people_revoked; a pending review is re-rendered without it) |
--purge-dumps | finalize | delete $DATA/dumps/ (alone, or together with --stage/--forget) |
--max-chars-total N | extract | soft cap for one run (default 8,000,000 chars, newest first) |
--min-chars N | extract | a session with less cleaned dialog than this (default 400 chars), or none at all, is skipped: no dump, skipped_empty in the counts and in state.json, never counted as extracted |
| (none) | progress | recount from disk and rewrite status.json — phase summarizing / rolling-up / staged with sessions (minus skipped_empty), summarized, rolled_up, entities, staged; run after every coordinator batch |
--run-kind onboarding|user | finalize --stage | the last field of the note header line; --commit reuses the staged run kind |
Every script also takes --root DIR (another projects dir), --out-dir/--data-dir, --workspace and finalize --memory-dir for tests and unusual installs. Slugs start with - (-Users-o-Projects-x); --projects/--forget accept them as typed (--forget -Users-o-Projects-x) as well as --forget=-Users-o-Projects-x.
Files it writes
| what | where |
| index, state, status, dumps, summaries, roll-ups, entities | <workspace>/data/claude-import/… (dumps 0600 in 0700). state.json run = {task_id, run_id, started_at} of the run in progress (set by index.py --task-id); status.json = phase, task_id, run_id, counts — nothing else |
| staged review set (nothing landed yet) | <workspace>/data/claude-import/staged/ (0700): review.md — the digest —, memory/claude_import.md, notes/claude-import/<slug>.md + overview.md, people.json, known-people.json (the store listing as used, 0600), manifest.json (staged_at, run kind, pending slugs, input fingerprint, held rows — date + reason only —, stale_rollups, known_people, people_ambiguous); removed by --commit/--discard |
| the coordinator's store listing; the owner's include/hold calls; the last commit's approved People payloads | <workspace>/data/claude-import/known-people.json (ids, names, emails only); state.json personal_override per session; <workspace>/data/claude-import/people.json (0600) |
approved snapshots (only by --commit; removed by --forget) | <workspace>/data/claude-import/approved/<slug>.json — each landed roll-up as the owner approved it (rollup_hash, approved_at), the source of the memory file and overview.md; approved/people-inputs.json — the entities (approved projects' citations only), store listing and per-project people_hashes people.json was computed from, shrunk by --forget/--forget-session/--hold (each starts from people.json and only removes: no re-ranking, no recomputed cap), grown only by --commit and only with the projects it lands (0600 in 0700) |
narrative (only by --commit) | <workspace>/notes/claude-import/<slug>.md, overview.md — header *[imported, claude-code] — import-claude-context | <first> → <last> | onboarding|user* |
facts the core loads at start (only by --commit) | memory_dir()/claude_import.md (≤ 2,000 B) + one MEMORY.md row when the budget allows |
people (only after --commit) | people__upsert_person payloads, source: claude-import |
Tests: tests/import-claude-context-{common,index,extract,progress,finalize,stage}.test.py. Parsing lives in skills/session-recap/scripts/extract.py (--root) and src/context_resume.py; this skill adds none of its own.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

