feat(cli): add ctx7 update to refresh installed skills and rules - #2951
Open
fahreddinozcan wants to merge 3 commits into
Open
feat(cli): add ctx7 update to refresh installed skills and rules#2951fahreddinozcan wants to merge 3 commits into
fahreddinozcan wants to merge 3 commits into
Conversation
Skills and rules are versioned by a checked-in content manifest served from raw.githubusercontent.com, and installs are tracked in the CLI state file, so content changes reach existing installs without a CLI release.
The update notice was TTY-gated, so agent-driven runs (npx ctx7@latest docs, always piped) never saw it and never refreshed. Out-of-date skills and rules are now rewritten silently in the preAction hook, guarded by a stale-tolerant lock so concurrent agent invocations cannot drop install records. Locally modified and version-blocked items are still left alone; the interactive notice now reports only those, since they need a human decision.
Refresh reused installSkill, which falls back to the legacy GitHub download and records revision 0. When the manifest was reachable but its content was not, that rewrote files and downgraded the record below what was installed, so the same item looked outdated on every subsequent command and re-downloaded forever. Refresh now resolves strictly from the manifest and refuses to write anything below the target revision; the fallback stays on the setup path where it belongs. A one-hour backoff stops a broken mirror being retried per command. Also: serialize state writes through a re-entrant, stale-tolerant lock with a 250ms cap so no command stalls behind another process, write state atomically via temp-and-rename, and quarantine an unparseable state file instead of silently disabling install tracking forever.
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.
Keeps skills and rules installed by
ctx7 setupcurrent as their upstream content changes, refreshing them automatically during ordinary commands and addingctx7 updatefor manual control.skills/manifest.json, generated byscripts/generate-content-manifest.mjs, giving each skill and rule arevision, aminCliVersion, and per-file content hashes. A CI step regenerates it and fails on a dirty diff.raw.githubusercontent.comagainst that manifest, verifying every file hash before accepting it. Falls back to the existing GitHub API download for skills and to the built-in constants for rules when unreachable.cli-state.jsonbehind a 24h TTL and records each install there by absolute path, with its revision, content hash, and file list.preActionhook, soctx7 docsandctx7 libraryself-heal. Guarded by a stale-tolerant lock file, since agents run these concurrently.ctx7 update --forceoverwrites.minCliVersionexceeds the running CLI and points atctx7 upgrade.ctx7 remove.Content freshness is decoupled from the npm release cycle: a merge to master reaches existing installs on their next check regardless of the installed CLI version.
Why auto-refresh
The rules and skills tell agents to invoke
npx ctx7@latest docs ..., which always runs with stdout piped. A TTY-gated notice is invisible on that path, so the overwhelmingly common usage would never have been told to update and would never have updated. Refreshing in the natural flow is what actually closes the loop;ctx7 updateexists for the cases auto-refresh deliberately will not touch.Auto-refresh is silent, never fails the host command, and skips
setup,update,remove, andupgrade.CTX7_NO_AUTO_UPDATE=1disables it. Cost on the common path is one state-file read: on-disk hashing only happens for records whose revision already differs from the cached manifest, and the manifest itself is fetched at most once per day.The interactive notice now reports only what auto-refresh will not do on its own, locally modified files and version-blocked revisions, since both need a human decision.
State file
cli-state.json(in$XDG_STATE_HOME/context7/) gains two things: aninstallsmap keyed by absolute path, and a TTL'dcontentManifestcache. The existing npm update-check fields are unchanged.After
ctx7 setupGlobal CLI mode for Claude Code and Cursor, plus MCP mode at project scope. One record per installed artifact. Note the two Cursor and Claude rule records share a source file but differ in
hash, because Cursor's copy carries thealwaysApplyfrontmatter.{ "installs": { "~/.claude/skills/find-docs": { "kind": "skill", "name": "find-docs", "agent": "claude", "scope": "global", "revision": 4, "hash": "3a69a6f34d4f", "files": ["SKILL.md"] }, "~/.claude/rules/context7.md": { "kind": "rule", "name": "context7-cli.md", "agent": "claude", "scope": "global", "mode": "cli", "revision": 2, "hash": "1934a46b1e43" }, "~/.cursor/skills/find-docs": { "kind": "skill", "name": "find-docs", "agent": "cursor", "scope": "global", "revision": 4, "hash": "3a69a6f34d4f", "files": ["SKILL.md"] }, "~/.cursor/rules/context7.mdc": { "kind": "rule", "name": "context7-cli.md", "agent": "cursor", "scope": "global", "mode": "cli", "revision": 2, "hash": "5410731229e2" }, "/repo/.claude/skills/context7-mcp": { "kind": "skill", "name": "context7-mcp", "agent": "claude", "scope": "project", "revision": 3, "hash": "ad156f258515", "files": ["SKILL.md"] }, "/repo/.claude/rules/context7.md": { "kind": "rule", "name": "context7-mcp.md", "agent": "claude", "scope": "project", "mode": "mcp", "revision": 2, "hash": "6266214a08c5" } } }Manifest cache
Written on the first check and reused for 24h. Also serves as the last-known-good copy when the network is unavailable, so an offline
ctx7 updatereports against the cache instead of failing.{ "contentManifest": { "fetchedAt": 1753699200000, "manifest": { "schema": 1, "skills": { "find-docs": { "revision": 4, "minCliVersion": "0.0.0", "files": [{ "path": "SKILL.md", "hash": "5bf22997630f" }] }, "context7-cli": { "revision": 3, "minCliVersion": "0.6.0", "files": [ { "path": "SKILL.md", "hash": "d7b70ba126d9" }, { "path": "references/docs.md", "hash": "144d463d5aa6" } ] } }, "rules": { "context7-cli.md": { "revision": 2, "minCliVersion": "0.0.0", "hash": "9716d0bcec92" } } } }, "contentNotifiedAt": 1753699200000 }Fallback install
When the manifest is unreachable,
setupstill installs via the existing GitHub API path and recordsrevision: 0. The next successful check sees any real revision as newer and offers the update.{ "installs": { "~/.claude/skills/find-docs": { "kind": "skill", "name": "find-docs", "agent": "claude", "scope": "global", "revision": 0, "hash": "3a69a6f34d4f", "files": ["SKILL.md"] } } }Output
Auto-refresh during an agent-style invocation. Nothing is printed; only the state file moves:
ctx7 update --check, covering all three outcomes:ctx7 update --jsonfor the same state:[ { "path": "~/.claude/skills/find-docs", "kind": "skill", "name": "find-docs", "agent": "claude", "scope": "global", "installedRevision": 1, "latestRevision": 2, "edited": false, "blockedBy": null }, { "path": "~/.cursor/skills/find-docs", "kind": "skill", "name": "find-docs", "agent": "cursor", "scope": "global", "installedRevision": 1, "latestRevision": 2, "edited": true, "blockedBy": null }, { "path": "/repo/.claude/skills/context7-mcp", "kind": "skill", "name": "context7-mcp", "agent": "claude", "scope": "project", "installedRevision": 1, "latestRevision": 3, "edited": false, "blockedBy": "99.0.0" } ]Notes for review
revision: 0until the manifest lands on master, so the firstctx7 updateafter this ships will report everything as outdated. That is the intended one-time migration.~/.agents/skills, so that path's install record reflects whichever agent ran last and the other will read as locally modified. Pre-existing and not addressed here, but now visible.