How the i-have-adhd Plugin Persists State Across Conversation Turns
The i-have-adhd plugin persists state across conversation turns by writing a custom session entry via pi.appendEntry and restoring it on every session restart using getSavedState to scan the session branch.
The ayghri/i-have-adhd plugin tracks whether ADHD-friendly mode is active by leveraging Pi's session manager to persist state across conversation turns. Instead of relying on volatile memory, it writes a typed state object into the conversation tree and automatically retrieves the latest value whenever the session restarts or the tree changes. This design guarantees that a user's preference remains active until explicitly disabled.
Using Custom Session Entries to Persist State Across Conversation Turns
The plugin implements a lightweight but durable persistence layer using three core operations: defining a unique entry type, appending state snapshots to the session branch, and scanning that branch to recover the most recent value.
Defining the Custom State Entry Type
At the top of extensions/i-have-adhd.ts, the plugin declares a constant that identifies its persisted data:
const STATE_ENTRY_TYPE = "i-have-adhd-state";
This string acts as a discriminator inside Pi's custom entry system, ensuring the plugin only reads entries it created and ignores unrelated session data.
Saving State with pi.appendEntry
When a user toggles ADHD mode, the setEnabled function updates the in-memory flag and immediately persists it to the session tree:
function setEnabled(nextEnabled: boolean, ctx: ExtensionContext): void {
enabled = nextEnabled;
pi.appendEntry(STATE_ENTRY_TYPE, { enabled } satisfies AdhdModeState);
updateStatus(ctx);
syncContext(ctx);
}
The pi.appendEntry call writes a custom entry into the current session branch. Because Pi's runtime persists the session tree across conversation turns, this entry survives until the user clears the session or overwrites it with a new toggle.
Restoring State from the Session Branch
On every session_start or session_tree event, the plugin invokes restoreState, which fetches the latest saved value or falls back to default flags:
const savedState = getSavedState(ctx);
const enabledByDefault = pi.getFlag("adhd") === true || existsSync(alwaysOnFlag);
enabled = savedState ?? enabledByDefault;
The helper getSavedState iterates over ctx.sessionManager.getBranch() to locate the most recent custom entry:
function getSavedState(ctx: ExtensionContext): boolean | undefined {
for (const entry of ctx.sessionManager.getBranch()) {
if (entry.type === "custom" && entry.customType === STATE_ENTRY_TYPE) {
const data = entry.data as Partial<AdhdModeState>;
if (typeof data?.enabled === "boolean") return data.enabled;
}
}
return undefined;
}
By walking the branch in order, the function naturally returns the latest persisted toggle, making the plugin resilient across restarts and tree mutations.
Synchronizing Context After Restore
After restoring or changing the flag, the plugin calls syncContext to inject the ADHD ruleset from skills/i-have-adhd/SKILL.md into the conversation context when enabled, or withdraw it when disabled. This ensures that the persisted state translates directly into visible behavior on every turn.
Toggling ADHD Mode from Commands
Users interact with persistence through the registered i-have-adhd command. The handler parses arguments and delegates to setEnabled:
pi.registerCommand("i-have-adhd", {
description: "Toggle ADHD-friendly output for this session",
handler: async (args, ctx) => {
const arg = args.trim().toLowerCase();
if (arg === "on") setEnabled(true, ctx);
else if (arg === "off" || arg === "stop") setEnabled(false, ctx);
else setEnabled(!enabled, ctx);
},
});
Issuing stop adhd mode writes a new custom entry with enabled: false, which getSavedState picks up on the next turn.
Summary
- The plugin defines
STATE_ENTRY_TYPE = "i-have-adhd-state"inextensions/i-have-adhd.tsto tag its custom entries. - It persists the boolean flag by calling
pi.appendEntryinsidesetEnabled, writing the value into the session branch. - It restores state by iterating
ctx.sessionManager.getBranch()ingetSavedStateto find the latest matching custom entry. - The restored value drives
syncContext, which adds or removes the ADHD ruleset from the active conversation context. - Default enablement can also come from
pi.getFlag("adhd")or analwaysOnFlagfile if no saved state exists.
Frequently Asked Questions
Where does the i-have-adhd plugin store its enabled state?
The plugin stores its state as a custom entry inside Pi's session manager by calling pi.appendEntry(STATE_ENTRY_TYPE, { enabled }). This writes the data directly into the current session tree, and the Pi runtime automatically persists that tree across conversation turns. No external database or file is required after the initial write.
What happens if no previous state is found in the session branch?
If getSavedState finds no matching custom entry, the plugin evaluates pi.getFlag("adhd") and checks for an alwaysOnFlag file on disk. When none of these sources indicate the mode should be on, the plugin defaults to disabled.
Can the persisted state survive a full session restart?
Yes. Because the entry is stored inside the session tree via ctx.sessionManager, it is automatically restored on session_start and session_tree events. The plugin calls restoreState during these events to recover the latest value.
How does the plugin update the conversation after restoring state?
After determining the correct enabled value, the plugin calls syncContext to either inject the rules from skills/i-have-adhd/SKILL.md or withdraw them. This ensures the assistant's behavior matches the user's persisted preference on every subsequent turn.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →