fix(coding-agents): don't let one stray file abort the Claude import, and give Prime Agent the companion skill (#3771, #3772) - #3812
Merged
Conversation
… and give Prime Agent the skill `importLocalHistory` documents that it never throws, but the Claude reader did: `jsonlFiles` gated on `existsSync`, which is TRUE for a regular file sitting where a project directory was expected, and the `readdirSync` behind it threw ENOTDIR straight out of `--import-conversations` — one junk entry killed the whole run instead of costing that entry. A `listDir` helper now owns "there is nothing here to list", which also closes the same hole one level up on `~/.claude/projects` itself. (#3771) Prime Agent documents the same skill-discovery roots the other skills-capable hosts use but never received the packaged companion skill, so it only ever picked one up by accident from the shared `~/.agents` root. It now installs into its own `~/.prime/agent/skills`: `uninstallSkill` removes a fixed directory name, so writing to the shared root would make `uninstall prime-agent` delete Codex's and dsh's copy. (#3772) Closes #3771 Closes #3772 Claude-Session: https://claude.ai/code/session_01AHvpDBNP3CXfVL88WnazmC
Strix Security ReviewWarning This pull request has 1 commit after the last Strix review ( No security issues found. Updated for Reviewed by Strix |
…gent skill line Claude-Session: https://claude.ai/code/session_01AHvpDBNP3CXfVL88WnazmC
feniix
added a commit
to feniix/hindsight
that referenced
this pull request
Aug 28, 2026
…bort the history import dshHistory guarded its root with existsSync, which is true for a regular file, and then called readdirSync on it unguarded: a file at ~/.dsh/sessions threw ENOTDIR out of importLocalHistory, which documents that it never throws, and whose caller in installer.ts does not catch. Route the root listing through the module's listDir helper (which vectorize-io#3812 introduced for the Claude reader) and let the per-project loop use it too, dropping its private try/catch. Regression test seeds a regular file at the sessions root and fails with the reported ENOTDIR without the change. A second test covers the per-project case, which had no importLocalHistory coverage for dsh. Closes vectorize-io#3771.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two independent coding-agents fixes, both small enough to share a PR.
#3771 — a stray file aborted the whole Claude history import.
claudeHistoryprefilters~/.claude/projectscandidates by encoded NAME and hands each match tojsonlFiles, which gated onexistsSync— TRUE for a regular file — and thenreaddirSync, which throwsENOTDIR. That escapedimportLocalHistory, which documents that it never throws, and whose caller (importConversationsininstaller.ts) has no catch of its own: one junk entry killed the entire--import-conversationsrun instead of costing that entry.The guard belongs in the listing rather than at the call site the issue suggested —
jsonlFilesalready owned "there is nothing here to list" viaexistsSync, and a stray file is that same case reached through a different errno. AlistDirhelper now holds it, which also closes the identical hole one level up (a regular file at~/.claude/projectswas equally fatal) and sheds astatper candidate directory plus its TOCTOU window. The dsh reader already handles its own case with atry/catchand is left alone.#3772 — Prime Agent never received the companion skill. It documents the same discovery roots the other skills-capable hosts use, so it can load the skill and today only does so by accident, when the user also runs Codex or dsh and it picks ours up from the shared
~/.agentsroot.installSkill/uninstallSkillare now wired into its adapter, into Prime Agent's own~/.prime/agent/skills:uninstallSkillremoves a fixed directory name, so installing to the shared root would makeuninstall prime-agentdelete Codex's and dsh's copy. Uninstall's skill removal sits outside thesettings.jsonguard so a hand-deleted settings file can't strand the skill.Closes #3771.
Closes #3772.
Type of change
Test plan
src/core/history.test.ts— two new Claude regression tests: a stray file named like the project dir of a subdirectory (so the prefix filter hands it to the listing), and a regular file where~/.claude/projectsitself belongs. Both were verified to fail on the pre-fix listing with the exact reported error (ENOTDIR: not a directory, scandir '…/.claude/projects/-Users-x-dev-myrepo-sub'), and the first still imports the real session alongside the junk entry.src/installer.test.ts— prime-agent joins the cross-host skill install/uninstall matrix, plus a test that installing Codex and Prime Agent and then uninstalling Prime Agent leaves the shared~/.agentscopy intact.npx vitest run --exclude 'src/e2e/**'→ 658 passed, 23 skipped../scripts/hooks/lint.shclean,./scripts/hooks/check-unused.shshows nothing new.npm run skill:build+sync-coding-agents-doc.mjsafter linting; only the docs page moved, the skill regions are untouched.Notes for the reviewer
pias a new harness). This PR takes the samelistDirshape as fix(coding-agents): don't let a stray file at the dsh sessions root abort the history import #3778 for the first half, and does the Prime Agent half without the new harness. Close whichever set you don't want.SKILL_DIRSincore/skill-sync.ts: that self-update path runs from the stdin hooksession-start, which persistent-plugin harnesses (prime-agent, dsh, opencode, Kilo, Cline) never reach — the known dsh adapter: daemon mode never starts the daemon (ensureDaemon parity missing) #3524-shaped gap, unchanged here. Prime Agent's copy refreshes on re-install like dsh's does.codexHistory's walk is guarded at the opposite altitude (one unstattable entry discards every file found so far), andimportLocalHistorystill has no top-level backstop for its never-throws contract.🤖 Generated with Claude Code
https://claude.ai/code/session_01AHvpDBNP3CXfVL88WnazmC