Conversation
BREAKING (runtime v3). Replace the per-repo hydrate.sh/health.sh and the
per-work hooks/ with a single runtime-wide hook hub at <runtime>/hooks/ —
one executable per event, where the user branches on MX_* context inside.
Events: pre/post-worktree-create (post = the old hydrate), pre/post-
worktree-remove, pre/post-work-archive, pre/post-work-unarchive,
pre/post-repo-fetch, and repo-health. pre-* non-zero aborts the op
(HOOK_FAILED); post-* warns; repo-health stdout feeds `mx repo health`.
Hooks are any-language executables (shebang), stamp-if-missing so user
logic is never clobbered; delete a file to disable an event.
Repos now carry repo.json ({ "name": … }, extensible) and no scripts.
The CLI fires hooks around worktree add/rm, archive/unarchive, repo fetch
(hooks.ts replaces hydrate.ts + workhooks.ts); HYDRATE_FAILED folded into
HOOK_FAILED.
Migration v2→v3 (mx migrate): stamps the hooks hub, writes each repo.json,
and retires old scripts — deletes defaults, keeps customized ones with a
warning so the user can fold the logic into the central hooks. --dry-run
previews the plan + warnings.
Core: HOOK_EVENTS / hookScript / runtimeHooksDir, repoConfigFile /
read/writeRepoConfig, stampRuntimeHooks; RUNTIME_VERSION = 3. Tests, all
docs, templates, and the runtime CLAUDE.md updated. 69 tests green.
Multi-worktree: each worktree gains a `name` (its wt/<name> directory and the rm/port/unarchive selector), defaulting to the repo name. Pass a distinct positional name to add a second worktree of the same repo: mx work -n feat worktree add app app-pr2 --branch fix Selectors and ports are keyed by worktree name (independent ports per worktree), workspace folders use wt/<name>, and inferContext resolves the wt/<name> segment to its repo via work.json. work.json now ALWAYS records `name` (uniform shape, new + migrated). Back-compatible: a name-less entry defaults to repo. New worktreeName / findWorktreeByName in @mx/core; hooks gain $MX_WORKTREE_NAME. Migration: the v2->v3 step backfills `name` (= repo) into existing work.json worktrees so old and new manifests match. Dropped `mx work worktree hydrate` and `--no-hydrate` — with hooks as the source of truth they're redundant; post-worktree-create always fires on add (no-op the hook to skip). 7 new edge-case tests (distinct-name add, default-name collision, rm by name, independent ports, archive/unarchive round-trip, invalid name, v2-style name-less back-compat) + migration backfill assertion; 76 total. Docs, templates, READMEs, and runtime CLAUDE.md updated. Part of 3.0.0.
mx work archive now clears each worktree's ports (the worktrees/servers are gone, so the numbers become reusable while archived). mx work unarchive re-creates the worktrees and fires post-worktree-create for each one, so the hook re-allocates ports and re-hydrates exactly like a fresh worktree add. Updated tests (ports freed + allocatedPorts empty after archive; restored ports empty), docs, and history. Part of 3.0.0.
…archive selector, inferContext mechanism)
…used by' work names
…dex hook Health - New mx work health / mx work -n <name> health: local work-folder audit (stray non-mx-native root files, worktree presence vs work.json, cross-work port collisions from hand-edits, archive invariants), plus a central work-health hook captured as the extra row. - New mx health [--all]: whole-runtime overview, every repo block followed by every active work block (--all adds archived). - Health views show only metric rows (each carries a tick/warn); info-only fields (default branch, last fetched detail, worktrees-in-works, work status, active port count) stay in mx info. last fetched is a metric for remote repos with a 24h freshness rule. The repo/work name carries an aggregate marker for quick scanning. Failing checks render inline as the row hint (no separate issues block). - repo-health / work-health hooks follow a silent-when-healthy convention: empty output or a bare "ok" renders the extra row as OK with a tick, any other output renders a warning with the message and flags the block. Context-index loading - Remove the per-work .claude/settings.json SessionStart hook. Claude Code caps hook output (~2KB) so a real INDEX.json was truncated. mx no longer stamps it; the v2 to v3 migration removes a default-stamped one (a customized settings.json is kept with a warning). Loading the whole index is now the session's job, with a "load the mx ctx index as whole" trigger. Adds workHealth / listWorkHealth / WorkHealth to @mx/core, the work-health HOOK_EVENTS entry, commands/health.ts, and isDefaultClaudeSettings() in the v2 to v3 migration. Docs, help text, and templates updated. 85 tests pass.
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.
Breaking — runtime v3. A large redesign bundling several changes (run
mx migrateonce per runtime after upgrading).1. Central hook hub (
<runtime>/hooks/)Per-repo
hydrate.sh/health.shand per-workhooks/are gone. All lifecycle hooks live in one directory, one executable per event, where you branch onMX_*inside.Events:
pre/post-worktree-create(post = the old "hydrate"),pre/post-worktree-remove,pre/post-work-archive,pre/post-work-unarchive,pre/post-repo-fetch,repo-health.pre-*non-zero aborts (HOOK_FAILED);post-*warns;repo-healthstdout feedsmx repo health. Any language (shebang); stamp-if-missing (never clobbers your logic); delete a file to disable.2. Multiple worktrees of one repo per work
Each worktree has a
name— itswt/<name>directory and the selector forrm/port/unarchive— defaulting to the repo name. Pass a distinct positional name to hold several worktrees of one repo:work.jsonnow always recordsname(uniform shape;mx migratebackfills= repointo existing manifests). Back-compatible: a name-less entry defaults torepo. Hooks gain$MX_WORKTREE_NAME.3. Repos carry
repo.json, no scripts{ "name": … }(extensible).mx repo add/newwrite it.4. Dropped
worktree hydrate+--no-hydrateRedundant with hooks:
post-worktree-createalways fires onworktree add(no-op the hook to skip).5. Archive frees ports; unarchive re-hydrates
mx work archivenow clears each worktree'sports(the worktrees and their servers are gone, so the numbers are reusable while archived).mx work unarchivere-creates the worktrees and firespost-worktree-createper worktree, where the hook re-allocates ports and re-hydrates just like a freshworktree add.Migration (v2 → v3)
mx migratestamps thehooks/hub, writes eachrepo.json, backfillswork.jsonworktreenames, and retires the old scripts — deleting defaults, keeping customized ones with a warning so you can fold the logic into the central hooks.--dry-runpreviews plan + warnings.Code
HOOK_EVENTS/hookScript/runtimeHooksDir,repoConfigFile/read|writeRepoConfig,stampRuntimeHooks,worktreeName/findWorktreeByName,RUNTIME_VERSION = 3; multi-worktree threading throughworks.ts/ports.ts/inferContext; v2→v3 migration (default-vs-customized script detection + name backfill).hooks.tsrunner (replaceshydrate.ts+workhooks.ts); positional worktree name; name-keyedrm/port/unarchive;hydratesubcommand +--no-hydrateremoved;HYDRATE_FAILEDfolded intoHOOK_FAILED.templates/hooks/<event>(11 no-ops); removedtemplates/repo/.CLAUDE.mdupdated;npm/package.json→ 3.0.0.Verification
typecheck / lint / test / build green. Smoke-tested end-to-end: every hook fires with correct
MX_*context (incl.MX_WORKTREE_NAME), pre-veto aborts,repo-healthcaptured; two worktrees of one repo (wt/app+wt/app-2) with independent ports and uniformwork.json;mx migratev2→v3 stamps hooks, writesrepo.json, backfills names, removes a defaulthydrate.shand keeps a customizedhealth.shwith a warning.6. Health:
mx work health+mx health(added after the original PR)New
mx work health/mx work -n <name> healthaudits a work folder the waymx repo healthaudits a clone, purely local:work.jsonbut missing on disk (and the inverse for archived works)work.json)New
mx health [--all]: whole-runtime overview, every repo block followed by every active work block (--alladds archived works).Health views now show only metric rows (each carries a ✓/⚠). Info-only fields (default branch, worktrees-in-works, work status, active port count) stay in
mx info.last fetchedis a metric for remote repos with a 24h freshness rule. The repo/work name carries an aggregate ✓/⚠ for quick scanning, and each failing check renders inline as the row hint. Therepo-health/work-healthhooks follow a silent-when-healthy convention: empty output or a bareokrenders theextrarow asOK ✓, any other output renders ⚠ with the message.7. Removed the per-work
SessionStartcontext-index hookv2 stamped
works/<feature>/.claude/settings.jsonwith aSessionStarthook that printedcontext/INDEX.json, but Claude Code caps hook output (~2KB) so a real index was truncated. v3 drops it:mx work new/mx syncno longer stamp it, andmx migrateremoves a default-stamped one (a customizedsettings.jsonis kept with a warning). Loading the whole index is now the session's job, with a "load the mx ctx index as whole" trigger documented in the runtimeCLAUDE.md.Code (additions)
workHealth/listWorkHealth/WorkHealthinworkhealth.ts;work-healthadded toHOOK_EVENTS;isDefaultClaudeSettings()in the v2→v3 migration (removes the default SessionStart hook).commands/health.ts;renderWorkHealthDetail; metric-only rendering with name-level aggregate + inline hints.templates/hooks/work-health; both health hooks document the silent-when-healthy convention.