Skip to content

Latest commit

 

History

History
604 lines (463 loc) · 30.3 KB

File metadata and controls

604 lines (463 loc) · 30.3 KB

Second Brain

Most people who study seriously already keep their thinking somewhere: an Obsidian vault, a visual planner, a notebook. StudyLoop can publish what it knows — your plan, today's next action, what is due for review — into that place, so you are not holding two half-pictures in your head.

This is off by default and stays completely silent until you choose a provider. With no second_brain: section, StudyLoop imports no code for it, writes no files, and never mentions it.

What a second brain means here

StudyLoop publishes projections, not a synchronised copy.

Your plan lives as a Markdown document that StudyLoop owns and you can read (studyloop plan path). That document is the single source of truth. A projection is a rendering of it into your second brain — regenerated whenever you publish, and never read back. Nothing in a backend, the CLI, or an agent protocol writes to your plan file.

flowchart LR
    P["Plan Markdown<br/>(source of truth)"] --> R[Render projection]
    R --> V["Your vault<br/>Study/Plans/&lt;plan-id&gt;.md"]
    N["Study/Plans/&lt;plan-id&gt;.notes.md<br/>(yours)"] -. "only on studyloop brain pull" .-> A[You and your mentor]
    A --> P
Loading

The dashed line is the only path back, it is manual, and it stops at you — not at the plan file. When you ask for your notes, StudyLoop hands them over; you and your mentor decide what belongs in the plan and change it with studyloop plan ….

The reasoning behind this is recorded in ADR-0010, and the full contract with the test that proves each clause is in the repository at docs/architecture/second-brain.md.

Choose one

Provider Cost Where your study text goes Publishing
none (default) nowhere
obsidian free, local files in your own vault, on your own disk studyloop brain publish
xtiles free plan connects; editing an existing project is paid your assistant's model service, then xTiles' cloud your assistant, on request

Obsidian is the free, local option and it is complete: nothing in StudyLoop needs a paid service. xTiles is there because some learners already live in it, and it is served through your assistant rather than by StudyLoop itself.

What leaves your machine

Provider What leaves To whom
none nothing
obsidian nothing. StudyLoop writes files into a folder on your disk. nobody
xtiles the plan text, today's action, due reviews, the session's learning record — only when you run one of the prompts your assistant's model service, and xTiles' cloud

Two honest caveats. For Obsidian, "nothing leaves" is a statement about StudyLoop: if you use Obsidian Sync, or a plugin that phones home, that is your vault's arrangement and not something StudyLoop can speak for. For xTiles, your sign-in lives in your assistant's own configuration — StudyLoop stores no credential for this feature, and there is nothing for it to leak.

Obsidian

The workflow, end to end

What you actually do, once. Steps 4 and 5 are the loop you repeat; everything above them happens on the first day only.

flowchart TD
    S1["1. studyloop brain enable obsidian --vault ~/MyVault"] --> S2["2. studyloop brain publish --dry-run<br/><i>lists what it would write, writes nothing</i>"]
    S2 --> S3["3. studyloop brain publish<br/><i>today's note + every active plan</i>"]
    S3 --> S4["4. Read it in Obsidian<br/>Study/Today.md · Study/Plans/&lt;plan-id&gt;.md"]
    S4 --> S5["5. Think, and write in your OWN file<br/>Study/Plans/&lt;plan-id&gt;.notes.md"]
    S5 --> S6["6. studyloop brain pull &lt;plan-id&gt;<br/><i>hands your notes back to you</i>"]
    S6 --> S7["7. You decide what belongs in the plan<br/>studyloop plan milestone / status,<br/>or edit the plan document itself"]
    S7 --> S3

    S3 -.->|"a projection you hand-edited"| W["Replaced on the next publish,<br/>with a warning naming the file"]
    S3 -.->|"a file StudyLoop does not own"| R["Refused. Publish stops and says which file."]

    classDef you fill:#eceaff,stroke:#4c3fbb,color:#231d63
    classDef danger fill:#fff1dc,stroke:#a9741a,color:#673e05
    class S4,S5,S7 you
    class W,R danger
Loading

The purple steps are the ones only you can do. Nothing on this diagram sends anything anywhere: every arrow is a file on your own disk.

Where notes go

Everything lives under one folder you name (Study by default):

Study/
├── Today.md                                 StudyLoop's, replaced each publish
├── Plans/
│   ├── python-decorators.md                 StudyLoop's, regenerated
│   └── python-decorators.notes.md           yours; StudyLoop only ever reads it
└── Learning Records/
    └── python-decorators/
        └── LR-0001.md                       StudyLoop's, regenerated

Get started:

studyloop brain enable obsidian --vault ~/Obsidian/Personal
studyloop brain publish

The StudyLoop ownership marker

Every note StudyLoop writes carries a marker in its frontmatter:

studyloop:
  owned: true
  schema: 1
  kind: plan-projection
  plan_id: python-decorators
  content_hash: 9f2c…

That marker is the whole safety mechanism, and it works in the direction that matters: StudyLoop never overwrites a note it does not own. It replaces a file only when the marker is there and names that same projection. A note you wrote by hand, a note made from a template, a note whose frontmatter cannot be parsed — all refused, by name:

Refusing to overwrite 'Study/Plans/python-decorators.md': it is not marked as
StudyLoop-owned. Move or rename that note, then retry.

Every write also has to resolve inside the vault you named. An absolute folder, a .., or a symlinked Study directory pointing elsewhere is refused before anything is written.

Editing or renaming a projection

Editing a projection works, but your edit is replaced on the next publish. The note is a rendering of your plan; there is nowhere in the plan for text you typed into the vault to live. Put your own thinking in the sibling .notes.md instead — StudyLoop never writes that file.

Renaming a projection makes it yours. StudyLoop will not touch a file it cannot find, and the next publish recreates the canonical one under the original name.

Republishing an unchanged plan does nothing at all: no write, no changed timestamp, nothing for a sync client to propagate.

Your own notes, and pulling them back

studyloop brain pull python-decorators

That reads Study/Plans/python-decorators.notes.md and prints it. It never creates that file, never changes it, and reports plainly when there is nothing there yet — which is a normal state, not an error.

The template

StudyLoop ships note templates that mirror the plan document's own sections, so a plan you write by hand in your vault and one StudyLoop published look the same:

studyloop brain template                       # list them
studyloop brain template --print "Study Plan.md"
studyloop brain template --install             # copy into your templates folder

--install copies into <your templates folder>/StudyLoop/ and refuses to overwrite anything already there. The templates deliberately carry no ownership marker, which is what makes a note you create from one permanently yours.

There is also an optional Dataview query page listing your published plans. It needs the community Dataview plugin; without it the page is harmless plain text.

Why there is no Obsidian CLI integration

Obsidian ships an official CLI, and an adapter for it was written for this release and then withdrawn before it shipped. Two reasons, both found in review:

  • It sent notes to whichever vault the running desktop app answered for, and there was no way to tie that vault to the one you configured. With more than one vault open, a publish could put your plan in the wrong one.
  • It passed the whole plan text as a command-line argument, where any other user on the machine could read it out of the process table.

Writing files directly is what this feature always needed — the adapter only added the chance for your own Obsidian template to fire on a note StudyLoop then overwrote anyway. Nothing here runs an external program, and a test asserts that.

If you had use_cli, vault_name, template or daily_note in your config from a pre-release build, StudyLoop now tells you they are gone rather than ignoring them — daily_note in particular wrote into your own daily note, and you should know it no longer does.

Two StudyLoop folders in one vault

If you also use the session-memory export, your vault has two StudyLoop folders and they are for different things. See Obsidian Export for the comparison.

Windows and WSL

Notes are written with a temporary file and an atomic rename, which is safe on Windows too, but a file locked by another program cannot be replaced — StudyLoop reports that and leaves the existing note intact. Under WSL, point vault_path at the path the vault has from the side StudyLoop runs on.

What the projections look like

Obsidian — local file projections

These images render the exact Markdown StudyLoop wrote into a hermetic throwaway vault. They are deliberately labelled not Obsidian inside the pixels: they show the projection files, not an imitation of Obsidian's desktop interface.

Today projection with next action, due reviews and active topics

Study Plan projection with milestones, resources and learning state

Learner-owned sibling notes file that StudyLoop reads but never overwrites

xTiles — live connector output

Three images are direct live-interface captures using synthetic probe data. The planner card is a documentation rendering of content read back from the live tile because the browser-extension capture clipped xTiles focus mode; the image labels that distinction inside its pixels. The brain-status image is a sanitized rendering of real CLI output, not xTiles UI. The project and planner tile were deleted after capture.

Verified planner-tile content rendered for documentation

Synthetic Study Plan project with page navigation and Mission content

Milestones imported as a table with Status and Concepts columns

Wind-down learning record projected into the synthetic xTiles project

StudyLoop status showing xTiles as a prompt-driven Stage 1 integration

xTiles

xTiles is a visual planner — projects, pages, tiles, a daily planner — and it is the one provider StudyLoop does not write to itself. The pattern is two MCP servers and one assistant: you connect StudyLoop's MCP server and xTiles' hosted connector to the same assistant, then ask it to move today's study into your planner. Obsidian remains the free, local option, and nothing in StudyLoop needs xTiles.

The workflow, end to end

The shape to hold on to: StudyLoop never talks to xTiles. It answers your assistant's questions, and your assistant is the only thing holding both connections. That is why the dashed boundary below matters more than any of the steps — it is the line your study text crosses, and it only ever crosses because you ran a prompt.

flowchart TD
    subgraph LOCAL["Your machine — configuration only"]
        direction TB
        SL["StudyLoop<br/><i>plans, next action, due reviews</i>"]
        CFG["studyloop brain enable xtiles<br/><i>records the choice; stores no credential</i>"]
        ASSIST["Your assistant<br/><i>Claude Code, Kiro, Codex …</i>"]
        SKILL["studyloop install agents<br/><i>wind-down skill, silent unless both gates pass</i>"]
        CFG --- SL
        SKILL --- ASSIST
        SL <-->|"StudyLoop MCP server<br/>get_next_action · get_due_cards"| ASSIST
    end

    SETUP(["claude mcp add … then sign in"]) --> ASSIST
    ASSIST -.->|"setup traffic: attaching the connector<br/>and authorising it in a browser"| XT

    YOU(["You run one of the three prompts"]) --> ASSIST

    ASSIST ==>|"STUDY CONTENT<br/>whatever that prompt includes<br/>or fetches for itself"| MODEL["Your assistant's model service<br/><i>Anthropic, for Claude Code</i>"]
    ASSIST ==>|"STUDY CONTENT<br/>through the xTiles connector"| XT["xTiles' cloud<br/><i>project · pages · planner task</i>"]
    XT --> READ(["You open xTiles and see it"])

    classDef local fill:#eceaff,stroke:#4c3fbb,color:#231d63
    classDef remote fill:#fff1dc,stroke:#a9741a,color:#673e05
    classDef human fill:#e4f7ee,stroke:#1c7a52,color:#0f4530
    class SL,CFG,ASSIST,SKILL local
    class MODEL,XT remote
    class YOU,SETUP,READ human
Loading

Read the arrow weights. Thick arrows carry your study content, and nothing crosses them until you run a prompt. The dashed arrow is setup: attaching the connector and authorising it in a browser is itself traffic to xTiles, so the box is labelled configuration only rather than pretending the network is untouched until later — the honest claim is about study content, not about packets.

Which content crosses depends on the prompt: the planner prompt sends today's action and your due reviews, which it fetches through StudyLoop's MCP server; the project prompt sends the plan Markdown you pasted in. StudyLoop is on neither thick arrow — it cannot see what was sent and cannot verify what arrived. That is also why removing the xtiles server from your assistant's own MCP settings is how you end this, not a StudyLoop command.

What you need

An xTiles account, an assistant that supports remote MCP servers, and StudyLoop's MCP server. xTiles states that MCP works on every plan, Free included, with some limitations there. What its pricing page gates is editing, not connecting: creating projects, pages and tiles is a Free feature, editing an existing project in a personal space starts at Plus, and editing in a shared space at Pro. That distinction decides which of the prompts below works for you, so it is named against each one.

Set up

One URL, one authorisation:

claude mcp add --transport http xtiles https://mcp.xtiles.app/mcp

Then run /mcp inside Claude Code and sign in to xTiles. There is no API key to copy or store, and xTiles also publishes a connector in Claude's Connectors Directory if you would rather not add the URL by hand. Tool names appear only after you have signed in.

This is written for Claude Code. Exercised end to end by a person on 2026-09-04 with an xTiles Plus account and Kiro CLI 2.21.0 — not yet run in Claude Code. The planner and wind-down prompts wrote what they describe; the project prompt created the project but not the board or the visible page structure it promises (see that prompt's section). StudyLoop installs the wind-down skill into every harness it detects: installation paths are verified against each vendor's documentation, but whether Kiro, Codex, OpenCode, pi or Grok Build can complete xTiles' browser authorisation is not something this page has verified — the skill stays silent unless an xtiles server is actually connected, so an untested harness costs you nothing.

Checking it yourself

Two opt-in checks exist, and neither runs in a normal test run:

just live-obsidian                       # a throwaway vault: publish, read back, remove
just xtiles-auth                         # once, in a real window
just live-xtiles "<url>" "<title prefix>" # is it actually visible in xTiles?

The xTiles one answers the question its connector cannot answer about itself: whether what your assistant wrote is visible in the interface. A write can succeed at the API and still render nowhere, and the interface is where you live. Of the three prompts below, only the planner one returns a URL to check — a planner tile is the one shape this live check has actually validated.

It needs a session captured in a real browser window rather than a stored password, because xtiles.app sign-in is behind reCAPTCHA and fails silently under automation. Every assertion is scoped to a title prefix you supply, and a missing or short prefix is refused rather than skipped — an unscoped check would match your own content.

What leaves your machine

When you run one of these prompts, the plan text, today's next action, the due reviews and the session's learning record go to your assistant's model service — Anthropic, for Claude Code — and through the connector to xTiles' cloud. The next action and the due reviews are your whole study state, not the named plan's: get_next_action and get_due_cards are not plan-scoped, so the title and reason sent to xTiles come from your entire study history even when the prompt names one plan. Nothing is sent unless you run a prompt. xTiles states that its connector asks permission per request, that your assistant only gets what your own account can already see, and that nothing is shared with other users — what you are actually shown per request depends on your assistant, and in a recorded Kiro CLI run no per-write prompt surfaced. Your sign-in lives in your assistant's own MCP settings rather than in StudyLoop, and that is also where you end it: remove the xtiles server there.

One more boundary worth knowing before you write anything: cleanup is uneven. Planner tiles can be removed through the connector (by patching the planner page); pages and projects have no delete tool and have to be deleted in the xTiles interface by hand.

Today into your planner

Creates one planner tile, so it works on any plan including Free. This is the one prompt that returns a URL you can check, and the shape the live UI check has validated.

Using the StudyLoop tools, call get_next_action with energy "medium", time_minutes 25 and modality "recall", and get_due_cards with limit 20. Then, in xTiles, add ONE item to today's planner as a tile built from Markdown, titled "Study: <primary concept>", with the recommendation's reason and estimated minutes, and a checklist of the due reviews, one line per card and at most 20. Do not create a project. Ask me before writing if the planner already has a "Study:" tile today, and tell me the page URL when you are done.

Your plan as a project

Creating the project works on Free, but know the connector's own limits before tier limits: it cannot create a board view, and pages that are collections (tables, boards) cannot be refreshed through it at all — they return an error regardless of what you pay. Normal text pages refresh in place. Expect the project URL to open on the home page; the other pages may not be visible from there in the interface even when the connector reports them created. No MCP tool returns plan Markdown, so paste it in from studyloop plan show <plan-id> --markdown.

Here is my StudyLoop study plan as Markdown, pasted from the CLI. In xTiles, create or refresh a project named "<plan title>" with pages Mission, Milestones, Learning Records, Resources, Checkpoints and Today. Put the Mission text on the home page, the milestones in a table with a Status column (done/not done), the learning records as one page each, the resources in a table, and checkpoints as dated tasks. Refresh normal pages in place; do not try to refresh tables or boards, and do not delete anything. Tell me what you changed, and say which pages could not be refreshed.

Name the project exactly, every time. A renamed project is a project this prompt cannot find, so it creates a second one.

The wind-down record

Adds a page and a task, so this too works on Free.

We are finishing a study session. Summarise what I covered in three bullets and one insight, then: (1) record that summary in StudyLoop first, with record_plan_learning for plan "<plan-id>", titled "<topic>" — the plan document is the source of truth; (2) in xTiles, add the same summary as a new learning record page under my "<plan title>" project, titled "LR — <date> — <topic>"; (3) add ONE planner task titled "Review: <concept>" on the next review date from get_due_cards. If get_due_cards returns no cards, do not invent a date; skip the Review task and the progress writes and say why. If we reviewed cards, record each one in StudyLoop with record_study_progress, passing the card_hash that get_due_cards returned. Ask before writing to xTiles; do not repeat the offer if I decline.

A limit worth knowing: get_due_cards returns cards that are already due — it is not a source of the next review date. With nothing due there is no date to schedule, which is why the prompt tells your assistant to skip rather than invent one. The prompt records into the plan first (via record_plan_learning, or studyloop plan record at a shell) so the xTiles page is a projection of a record the plan already has — never the only copy.

Your mentor offers this last one for you. studyloop install agents installs an opt-in wind-down skill into every harness it finds, and it stays silent unless studyloop brain status --json reports provider: xtiles and an xtiles server is connected in that session.

Launching your second brain from the web app

The Today panel in studyloop web can take you to your second brain with one click. It is a launcher, not a publisher: the button opens your configured provider, and nothing on the page ever writes to it.

flowchart LR
    CFG["second_brain: config"] --> API["GET /api/second-brain/launch-target<br/><i>read-only, never cached</i>"]
    API --> T["Today panel<br/>one action, prefetched state"]
    T -->|"one explicit click"| DEST["Obsidian vault<br/>or your retained xTiles page"]
    API --> S["Settings<br/>explains state, never launches"]

    classDef local fill:#eceaff,stroke:#4c3fbb,color:#231d63
    classDef remote fill:#fff1dc,stroke:#a9741a,color:#673e05
    class CFG,API,T,S local
    class DEST remote
Loading

Three rules hold everywhere on that diagram:

  • Navigation happens only on your click. Loading the page, refreshing it, publishing, or ending a session never opens anything. One click is one navigation: xTiles opens once in a new protected tab, Obsidian hands the current tab to the desktop app's obsidian:// link without leaving an empty tab behind.
  • Disabled means disabled, with the reason. When the action cannot work, the button says why — no provider selected, no retained xTiles destination, a vault that does not exist, or a browser on another device — instead of offering a click that goes nowhere.
  • The server only reports. The launch-state API is read-only: it cannot change your configuration, it never runs a program on the server, and an invalid configuration comes back as a generic disabled state rather than echoing the bad value.

Obsidian opens on the same device only

An obsidian:// link is an instruction to the operating system the browser runs on. If you open the web app from your phone while StudyLoop runs on your desktop, that link would ask your phone for an Obsidian vault it does not have — so the action is enabled only when the browser and StudyLoop are on the same device. From another device the button stays visible but disabled, saying that Obsidian opens only on the device running StudyLoop.

Two honest edges of that rule. StudyLoop decides locality from the direct network peer, so behind a reverse proxy every browser looks remote and the action stays disabled — a safe false negative, not a bug. And when the vault is available, the click opens Study/Today.md inside your vault when that note exists, falling back to the vault itself when it does not.

xTiles opens the page your assistant handed you

StudyLoop never talks to xTiles, so it cannot discover a page to open. The handoff is explicit: your assistant creates or finds a page through the xTiles connector and reports its URL, you review it, and you retain it:

studyloop brain destination set --provider xtiles --url '<connector-returned-url>'
studyloop brain destination clear --provider xtiles

Retaining a destination does not select xTiles as your provider — consent stays with provider: — and the URL is validated before anything is written: HTTPS only, exactly xtiles.app or app.xtiles.app, a real page path, and no credentials, port, query, or fragment. A rejected URL is described by reason only; the value itself is never echoed back or logged. The confirmation shows just the host, and the web app's Settings page never displays the full retained URL either.

What Settings shows

Settings renders one card per provider: the selected provider is active, every other card is muted. An active card's guidance is the same honest reason the launch API reports (empty when the action is ready); a muted card names the CLI command that would select it. There is no save button — changing providers or destinations is a deliberate CLI action, not a web form.

Commands

studyloop brain status --json              # provider, whether it can publish, where notes land
studyloop brain wind-down --json --connector xtiles  # the one offer to make at wind-down, if any
studyloop brain enable obsidian --vault ~/Obsidian/Personal
studyloop brain destination set --provider xtiles --url '<connector-returned-url>'
studyloop brain destination clear --provider xtiles
studyloop brain publish                    # today's note plus every active plan
studyloop brain publish --all              # every plan, whatever its status
studyloop brain publish --today --dry-run  # show what would be written, write nothing
studyloop brain publish --plan python-decorators
studyloop brain pull python-decorators
studyloop brain template --install
studyloop brain enable none                # turn it off again

studyloop brain wind-down --json is what an agent reads at the end of a session: it answers with the channel, whether to offer, the exact sentence to say and why. The publish offer is made only when both configured and supports_publish are true (both visible in brain status --json), which is why a learner on xTiles is never offered a command that cannot work.

At the end of a session your mentor offers this once, and only when a provider that can publish is configured:

Want me to publish today's study record and this plan to your Obsidian vault (Study/Today.md and Study/Plans/.md)? Yes or no — I'll only ask once.

The skill instructs your assistant to offer once and to drop the subject on a no; that behaviour has not yet been observed in a recorded session.

Configuration reference

second_brain:
  provider: obsidian          # none (default) | obsidian | xtiles
  vault_path: ~/Obsidian/Personal
  folder: Study               # the folder inside the vault StudyLoop owns
  backlinks: true             # [[wikilinks]] to your notes, when the matcher is available
  xtiles_destination_url: null  # optional; set with `brain destination set`

Five keys, and that is the whole surface. STUDYLOOP_SECOND_BRAIN_VAULT also exists and overrides the DEFAULT vault location — it is there so the test suite can never reach a real vault, it never selects a provider, and an explicit vault_path always wins over it. If you have it set in a shell profile, unset it.

vault_path is optional: if you already configured a vault for the session-memory export, or an obsidian_base, StudyLoop uses that. Configuring a vault does not switch a provider on — only provider: does.

No environment variable selects a provider. Turning this on authorises writes into your own files, so it has to be a deliberate change to your config.

Troubleshooting

Nothing is written and there is no error. studyloop brain status. If configured is false, no provider is set.

"Vault path does not exist or is not writable". The drive is not mounted, or the vault moved. Mount it, or studyloop brain enable obsidian --vault <new path>.

"Refusing to overwrite … not marked as StudyLoop-owned". A note of yours is already at that path. Move or rename it; StudyLoop will not overwrite it for you.

My edits to a published note keep disappearing. They will. Put your own notes in the sibling .notes.md, which StudyLoop only ever reads.

A note StudyLoop published disappeared from my vault. Nothing here deletes. Check whether you renamed it — a renamed projection becomes yours, and the next publish recreates the canonical one under the original name.

I deleted a plan and its projection is still there. Publishing never deletes, so the note stays until you remove it. That is deliberate: deleting notes is exactly the class of action this feature will not take without being asked.

Backlinks are not appearing. They need the vault topic matcher from agent-session-tools. Without it, publishing continues and logs one warning.

Sources

Every claim about someone else's software, with the date it was checked.

Claim Source Verified on
Dataview is a community plugin, not built in https://github.com/blacksmithgu/obsidian-dataview 2026-09-03
One hosted xTiles MCP server, at https://mcp.xtiles.app/mcp; one browser authorisation, no API key to copy or store https://help.xtiles.app/en/articles/16126651-how-to-connect-xtiles-to-other-ai-tools 2026-09-03
MCP works on every xTiles plan, Free included, with some limitations on Free https://help.xtiles.app/en/articles/16126651-how-to-connect-xtiles-to-other-ai-tools 2026-09-03
Creating projects, pages and tiles is a Free feature; editing an existing project in a personal space starts at Plus, and editing in a shared space at Pro https://xtiles.app/en/pricing/ 2026-09-03
Your assistant only gets what your xTiles account can already see; nothing is shared with other users; you disconnect by removing the server in your AI tool's own MCP settings https://help.xtiles.app/en/articles/16126651-how-to-connect-xtiles-to-other-ai-tools 2026-09-03
A remote HTTP MCP server is added with claude mcp add --transport http, and /mcp manages and authenticates it https://code.claude.com/docs/en/mcp 2026-09-03
xTiles publishes a connector in Claude's Connectors Directory as a quicker alternative to adding the URL by hand https://help.xtiles.app/en/articles/15192396-how-to-connect-xtiles-to-claude 2026-09-03