Conversation
…r the duplicate hooks `update` walks the template tree twice — once to report what it will do, once to do it — and only the apply loop asked whether the install was in plugin or marketplace mode. Every marketplace user was shown the whole skill and agent set as "New Files in This Version", confirmed adding them, and then saw none appear. Because the list was never empty, `All files are up to date!` could not fire for a marketplace install however current it was. Both loops now share `isPluginManagedPath()`. The second one has been running silently since the plugin migration. Moving a project to the plugin registered it and left devtronic's old inline hooks in `.claude/settings.json`, so both sets ran: the same SessionStart prompt fired twice per session, and the unfiltered `npx eslint --fix` linted every markdown write alongside the plugin's filtered `auto-lint.sh`. `registerGitHubPlugin()` now strips them and reports which events it cleaned. Matching is by signature and narrow on purpose. An unrecognised hook is one the user added, and removing it takes work they cannot get back — over half of `stripDevtronicHooks`'s tests assert exactly that. Found by running `devtronic update --dry-run` against this repository, which proposed 50 files it would not have written.
Everything needed to know this has existed since the manifest did: the manifest records the version that last wrote the project's files, and the CLI knows its own. Nothing ran the comparison unless you typed `devtronic info`, so this repository sat on 1.3.0 through two minor releases without a word. A `SessionStart` hook now runs `version-check.sh`. It reads two version strings, orders them with `sort -V`, and prints one line naming the command that fixes the gap — `devtronic update` when the CLI is ahead, `npm i -g devtronic@latest` when the project is. Silent when they agree, local, and always exits 0: a session must never fail to start because of this check. Detection only. `devtronic update` retires files and asks about the ones you made yours, so applying it unattended is how work gets lost. The automatable half is knowing you need to. The script is one text in two places, so a test asserts the generated and bundled copies are identical — the guard that hooks.json earned the hard way.
`docs/plugins.md` said the `PostToolUse` lint filter was a set of per-handler `if:` conditions (`Edit(**/*.ts)`, …). That implementation was written and then reverted during the 1.5.0 review — the filter lives in `auto-lint.sh`, which reads the real `tool_input.file_path` — but the documentation kept the version that never shipped. The `SessionStart` section now covers the version check, with the line it prints and why it reports rather than applies. Both READMEs name it, and the overview box in the root README is aligned to its own border again.
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.
Three changes, all found by trying to run
devtronic updateagainst this repository.updateannounced ~50 files it was never going to writeupdatewalks the template tree twice — once to report, once to apply — and only the applyloop asked whether the install was in plugin or marketplace mode. Every marketplace user was
shown the whole skill and agent set as "New Files in This Version", confirmed adding them, and
saw none appear. Because the list was never empty,
All files are up to date!could not firefor a marketplace install however current it was.
Both loops now share
isPluginManagedPath().Migrating to the plugin left the old inline hooks behind
The plugin supplies the same hooks, so both sets ran: the SessionStart prompt fired twice per
session, and the unfiltered
npx eslint --fixlinted every markdown write alongside theplugin's filtered
auto-lint.sh. Running since the plugin migration, in every migratedproject.
registerGitHubPlugin()now strips them and reports which events it cleaned. Matching is bysignature and deliberately narrow: an unrecognised hook is one the user added, and removing it
takes work they cannot get back. Over half of
stripDevtronicHooks()'s tests assert exactlythat.
The session now says when you're behind
A
SessionStarthook runsversion-check.sh: it compares the version in.ai-template/manifest.jsonagainst the installed CLI and prints one line when they differ,naming
devtronic updateornpm i -g devtronic@latestdepending on which side is older.Every part of this has existed since the manifest did. Nothing ran the comparison unless you
typed
devtronic info— which is why this repository sat on 1.3.0 through two minor releaseswithout a word.
Detection only, on purpose.
updateretires files and asks about the ones you made yours;applying that unattended is how work gets lost.
Verification
typecheck,lint,buildclean. 1120 tests, 53 files.46 tests cover the three changes, all mutation-checked:
sort -V→sort1.10.0vs1.9.0)