[#251] Add check-updates command, config system, and HookEvent enum#273
Merged
[#251] Add check-updates command, config system, and HookEvent enum#273
Conversation
- Add `mcs check-updates` (pack freshness via git ls-remote, CLI version via git tags) with 7-day cooldown, --force, --json, and context-aware pack filtering - Add `mcs config list/get/set` for user preferences (update-check-packs, update-check-cli) with immediate SessionStart hook management in settings.json - Replace stringly-typed hook events with `Constants.HookEvent` enum (CaseIterable, Codable) for compile-time safety (#269)
- --hook identifies automated SessionStart context (respects cooldown, records timestamp, respects config) - Default (no flag) is user-invoked: always checks, never records timestamp - sync/doctor update checks no longer consume the hook's cooldown window
- Prepends "The following mcs updates are available. Please inform the user:" so Claude proactively relays update notifications
- Single ~/.mcs/update-check.json stores timestamp + results - Hook mode: serves cached results when fresh, does network check when stale - User-invoked checks always do network + update cache (so hook benefits) - mcs pack update invalidates cache so next hook re-checks - CLI version cache self-invalidates when user upgrades (version mismatch)
- Hook stdout is injected into Claude's context; ANSI codes would appear as raw escape sequences - User-invoked mode keeps colored output for terminal readability
- Output additionalContext via hookSpecificOutput JSON (documented Claude Code API) - Plain stdout was implicit; structured format is the official hook output contract
- Call syncHook after first-run config save so the hook is installed immediately - Default nil config values to false in hook mode (match documented defaults) - Eliminate double loadCache() in the hook fast-path (single disk read) - Reference CodingKeys.rawValue in knownKeys instead of duplicating string literals - Fix line length lint violation in buildContextString
- Add VersionCompare.isNewer(candidate:than:) as the single comparison method - Remove UpdateChecker.isNewer (was duplicating VersionCompare) - Simplify parseLatestTag to use VersionCompare.isNewer instead of inline tuple comparison
- Replace try? with do/catch in checkAndPrint and CheckUpdatesCommand (CLAUDE.md rule) - Add plain-text fallback for hook JSON serialization failure - Remove dead isCacheStale() method (logic inlined in performCheck) - Fix doctor summary: always shows fixed=0, add post-fix summary line - Update cache tests to test loadCache directly
- 5 tests for Settings.removeHookEntry (match, no match, cleanup, nil hooks) - 7 tests for UpdateChecker hook lifecycle (addHook, removeHook, idempotency, round-trip, syncHook add/remove)
- Add Settings.addHookEntry/removeHookEntry overloads accepting HookEvent (type safety extends to settings layer) - Replace manual JSONSerialization in printJSON with Codable JSONOutput struct - Add 3 integration tests: hook injection on enable, omission on disable, convergence on re-sync
Critical: - MCSConfig.load: distinguish missing (silent) from corrupt (warn via output param) - saveCache: replace try? with do/catch - relevantPackIDs: use fileExists + do/catch instead of try? - invalidateCache: return success/failure, warn in PackCommand on failure Important: - ConfigurationDiscovery: warn on unknown hook events during export - Global sync: always save settings.json (fixes hook not being stripped when disabled) - Add 3 global sync integration tests (inject, converge, remove) - Add 3 performCheck tests (hook cached, user fresh, cache save) Suggestions: - Rename printHumanReadable to printResult (outputs JSON in hook mode) - Fix removeHookEntry doc comment accuracy
- Track whether hook stripping actually removed entries (group count comparison) - Save when hasContent (packs contributed) OR strippedContent (hooks were removed) - Avoids unnecessary file rewrites when syncing with zero-content packs
- Clarify cache description in docs/cli.md (serves cached OR does fresh check, not both) - Add CaseIterable to MCSConfig.CodingKeys for exhaustive key validation - Replace hardcoded knownKeys test with Set equality against CodingKeys.allCases
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
Users sync once and work for days/weeks with no visibility into pack or CLI staleness. This adds a lightweight
mcs check-updatescommand, amcs configpreferences system, and makes hook event names type-safe with an enum. Closes #251, closes #269.Changes
mcs check-updates: Non-interactive command that checks pack freshness (git ls-remote) and CLI version (git ls-remote --tagson the mcs repo). Supports--hook(SessionStart mode),--json(Codable DTO output), and context-aware pack filtering (global + current project packs only)--hookflag: Identifies the command as running inside a Claude Code SessionStart hook — respects 7-day cooldown, respects config keys, manages cache, and outputs structured JSON (additionalContext) with a strong directive instruction for Claude. Without--hook, checks always run (user-invoked behavior)~/.mcs/update-check.jsonstores timestamp + results. Hook mode serves cached results every session start (no network), refreshes when stale (7 days). User-invoked commands always refresh the cache.mcs pack updateinvalidates the cache (with warning on failure). CLI version cache self-invalidates on upgrademcs config list/get/set: Extensible user preferences at~/.mcs/config.yaml. Two keys:update-check-packsandupdate-check-cli.setimmediately manages the SessionStart hook in~/.claude/settings.jsonmcs syncprompts once whether to enable update notifications, then immediately syncs the hookmcs syncandmcs doctoralways check for updates (no cooldown, ignore config) since they are user-initiated. They also refresh the cache for the hookConstants.HookEventenum: Replaces stringly-typed hook event names withCaseIterable/Codableenum for compile-time safety. Type-safe overloads onSettings.addHookEntry/removeHookEntryacceptHookEventdirectly.ManifestError.invalidHookEventremoved — validation now at decode timeVersionCompare.isNewer: Extracted fromUpdateCheckerintoVersionCompareto eliminate duplicate comparison logicMCSConfig.load(output:): Distinguishes missing (silent) from corrupt (warns via optionalCLIOutputparameter)ConfigurationDiscovery: Warns on unknown hook events during export instead of silently dropping them--fixUX: Summary now shows before the fix confirmation prompt. "0 fixed" hidden from summary; post-fix line shows countUpdateCheckeras single source of truth: Hook parameters, cache management, check helpers (checkAndPrint), hook sync (syncHook), and output formatting centralized as static methodsTest plan
swift testpasses locally (865 tests, 66 new) — clean build verifiedswiftformat --lint .andswiftlintpass (0 violations across 67 source files)mcs check-updatesverified with real packsmcs check-updates --jsonproduces valid JSON via Codable DTOmcs check-updates --hookoutputs structured JSON withadditionalContextmcs config list/get/setverifiedmcs doctor --fixshows summary before fix promptmcs pack updatedeletes cache, next hook re-checksSettings.removeHookEntryfully tested (5 tests)performCheck: hook-mode cached, user-mode fresh, cache save (3 tests)Checklist for engine changes
CLAUDE.md,docs/cli.md,README.md)LifecycleIntegrationTests(project scope) andGlobalConfiguratorTests(global scope)try?violations addressed —do/catchwithoutput.warn()whereCLIOutputavailable; cache ops usedo/catchwith non-fatal fallback