Skip to content

jot and doctor: read the plugin entry the sessions use - #103

Merged
gaius-codius merged 7 commits into
mainfrom
gh-99-plugin-entry
Oct 25, 2026
Merged

gaius-codius merged 7 commits into
mainfrom
gh-99-plugin-entry

Conversation

@gaius-codius

Copy link
Copy Markdown
Owner

ostrakon jot and ostrakon doctor now read the plugin's options from the settings entry that Claude Code sessions actually read. Before, they took the first of ostrakon, ostrakon@inline and ostrakon@ostrakon that had a value. So with a marketplace install, an ostrakon entry left over from install.sh won: jot saved to that entry's folder and posted to that entry's hub, and doctor checked them too.

What changed

Which entry is read (server/jot.ts, bin/doctor)

  • pluginConfigs["ostrakon@ostrakon"] only, when enabledPlugins["ostrakon@ostrakon"] is set and no folder copy of the mod is loaded.
  • Otherwise ostrakon, then ostrakon@inline.
  • A folder copy counts when CLAUDE_CODE_PLUGIN_DIRS names a folder that holds the mod. It is read as Claude Code 2.1.310 reads it:
    • the list is split on :;
    • the settings' env value wins over the shell's, even when it is empty;
    • a leading ~ means the home folder;
    • a relative entry loads nothing.
  • When the mod is both installed and loaded from a folder, Claude Code loads only the folder's copy (its log says Plugin "ostrakon" from --plugin-dir overrides installed version). Doctor now says that, and names the entries it read. Before, it said the mod loads twice.

An empty CLAUDE_CONFIG_DIR

  • Claude Code keeps an empty value as it is, so it reads settings.json and themes/ from the working directory.
  • jot, doctor and install.sh now do the same; before, they treated an empty value as unset.
  • The mod reads a custom theme from $.session.cwd(); before, it read /themes/<name>.json.
  • DESIGN 5.0.1 records the theme change.

Settings that can't be used

  • Settings that are valid JSON but not an object ([1], null) are reported as such. Before, they were read as empty, so doctor said "no hubUrl set".
  • Doctor also reports a settings.json it can't open (a folder, or no permission).
  • In either case, doctor skips the hub check.

Docs: SPEC §2.1 records the Claude Code behaviour above, and §9.5 the entry rule. The CHANGELOG has the doctor, mod and install changes.

Acceptance criteria

  • Marketplace install with no folder copy: jot and doctor read only ostrakon@ostrakon.
    • server/jot.test.ts "installed from the marketplace and loaded from no folder, reads only ostrakon@ostrakon"
    • server/doctor.test.ts "installed from the marketplace and loaded from no folder, checks only what ostrakon@ostrakon says"
  • Loaded from a folder: they read ostrakon, then ostrakon@inline, and never ostrakon@ostrakon.
    • "loaded from a folder, …" in both test files
    • "the settings' plugin folders go over the shell's, an empty list included" in both
    • "reads a ~ in the plugin folders as home, and a relative folder as none" in both
  • Both set up: doctor says so and names the entries it read. Covered by server/doctor.test.ts "loaded from a folder, checks ostrakon then ostrakon@inline, and says when the mod is also installed". What a session does in this case was checked against Claude Code 2.1.310 and is recorded in SPEC §2.1 and §9.5.
  • Doctor's settings reading has tests: the new server/doctor.test.ts runs bin/doctor with stub claude, curl and systemctl. It covers the entry choice, the hub URL and token taken as a pair from one entry, CLAUDE_CONFIG_DIR (unset, set, set but empty), a missing settings file, and invalid or unreadable settings.
  • An empty CLAUDE_CONFIG_DIR behaves the same in jot, doctor and the mod, as in Claude Code:
    • jot: "with CLAUDE_CONFIG_DIR set but empty, reads settings.json in the working directory, as Claude Code does"
    • doctor: "reads the settings from CLAUDE_CONFIG_DIR when set, and from the working directory when it is set but empty"
    • the mod: plugin/tests/hooks.test.ts "with CLAUDE_CONFIG_DIR set but empty, a custom theme is read from the working directory, as Claude Code reads it"
  • Settings that parse but aren't an object (from the issue's comment): jot "says the settings aren't a JSON object when they parse as something else", and doctor "says the settings aren't valid when they aren't JSON, or are JSON but not an object".

Changes made during review

  • The mod reads the theme from the session's own folder as an absolute path. Before, it handed a relative path to the engine, and the test kit resolves that against the plugin's folder.
  • install.sh uses the same rule for an empty CLAUDE_CONFIG_DIR.
  • jot and doctor read ~ and relative entries in CLAUDE_CODE_PLUGIN_DIRS the way Claude Code does.
  • Doctor reports a settings file it can't open.
  • With no settings file at all, doctor again takes CLAUDE_CODE_PLUGIN_DIRS from the shell. An earlier commit on this branch had stopped doing that. The new test "with no settings file, takes the plugin folders from the shell" covers it.

Checks

tools/check.sh passes at the head: plugin validate, design check, the type-checks, 401 mod tests, 72 hub and jot tests, the phone tests and lint.

Failing test: server/jot.test.ts installed from the marketplace and loaded from no folder, reads only ostrakon@ostrakon
Failing test: server/jot.test.ts reads a ~ in the plugin folders as home, and a relative folder as none, as Claude Code does
Failing test: server/jot.test.ts the settings' plugin folders go over the shell's, an empty list included
Failing test: server/jot.test.ts with CLAUDE_CONFIG_DIR set but empty, reads settings.json in the working directory, as Claude Code does
Failing test: server/jot.test.ts says the settings aren't a JSON object when they parse as something else
Failing test: server/doctor.test.ts installed from the marketplace and loaded from no folder, checks only what ostrakon@ostrakon says
Failing test: server/doctor.test.ts loaded from a folder, checks ostrakon then ostrakon@inline, and says when the mod is also installed
Failing test: server/doctor.test.ts reads a ~ in the plugin folders as home, and a relative folder as none
Failing test: server/doctor.test.ts the settings' plugin folders go over the shell's, an empty list included
Failing test: server/doctor.test.ts reads the settings from CLAUDE_CONFIG_DIR when set, and from the working directory when it is set but empty
Failing test: server/doctor.test.ts says the settings aren't valid when they aren't JSON, or are JSON but not an object
Failing test: plugin/tests/hooks.test.ts -t "with CLAUDE_CONFIG_DIR set but empty, a custom theme is read from the working directory, as Claude Code reads it"

Closes #99

Installed from the marketplace and loaded from no folder, a session reads
pluginConfigs.ostrakon@ostrakon; loaded from a folder, ostrakon then
ostrakon@inline. CLAUDE_CODE_PLUGIN_DIRS comes from the settings' env over
the shell's. An empty CLAUDE_CONFIG_DIR means settings.json in the working
directory, as Claude Code reads it, and settings that parse but aren't an
object are reported as such.
doctor reads pluginConfigs.ostrakon@ostrakon only when the mod is installed
from the marketplace and loaded from no folder, else ostrakon then
ostrakon@inline. With both set up it says sessions use the folder's copy and
names the entries it read. CLAUDE_CODE_PLUGIN_DIRS comes from the settings'
env over the shell's, an empty CLAUDE_CONFIG_DIR means settings.json in the
working directory, and settings that parse but aren't an object are flagged.
Its settings reading now has tests.
…_DIR is empty

Claude Code keeps an empty CLAUDE_CONFIG_DIR as it is, so its folder is the
working directory; the mod read /themes/ instead.
…s, unreadable settings

- The mod reads a custom theme from $.session.cwd() when CLAUDE_CONFIG_DIR is
  set but empty, instead of leaving a relative path to the engine.
- jot and doctor read a leading ~ in CLAUDE_CODE_PLUGIN_DIRS as home and skip
  a relative entry, as Claude Code does.
- doctor reports a settings.json it can't open (a folder, no permission).
- install.sh treats an empty CLAUDE_CONFIG_DIR as Claude Code does.
- DESIGN 5.0.1: the custom theme row covers the empty CLAUDE_CONFIG_DIR.
Run every helper-based case through the stubbed claude and curl.
@gaius-codius
gaius-codius merged commit d250315 into main Oct 25, 2026
9 checks passed
@gaius-codius
gaius-codius deleted the gh-99-plugin-entry branch October 25, 2026 03:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

jot and doctor: read the plugin entry the sessions use

1 participant