fix: bust giget cache on skill update; heading-bounded docs inject#13
fix: bust giget cache on skill update; heading-bounded docs inject#13rgdevme wants to merge 2 commits into
Conversation
agnos skill update appeared to reinstall stale content because giget keeps its own tarball cache at ~/.cache/giget and short-circuits the download on a matching ETag sidecar. When fetchGit is called with noCache: true, also remove the giget tarball + .json sidecar for the source before invoking downloadTemplate. Docs injection now uses the markdown structure as its boundary instead of custom end-marker lines. Drop RULES_BLOCK / INDEX_BLOCK in favor of RULES_HEADING / INDEX_HEADING string constants; replace replaceBetweenMarkers with replaceUnderHeading, which overwrites everything under the heading until the next "## " / "# " heading or EOF. Promote generated index sections from "## " to "### " so they nest correctly under the preserved "## Documentation Index" heading.
Review Summary by QodoFix giget cache busting and heading-bounded docs injection
WalkthroughsDescription• Clear giget's cached tarballs when noCache: true to prevent stale content • Replace custom end-marker lines with markdown heading boundaries for docs injection • Promote generated index sections from ## to ### for proper nesting • Add gigetTarballPath() helper to mirror giget's cache directory logic Diagramflowchart LR
A["skill update<br/>with noCache"] -->|calls fetchGit| B["fetchGit removes<br/>giget tarball"]
B -->|clears cache| C["downloadTemplate<br/>fetches fresh"]
C -->|extracts| D["Updated skill<br/>content"]
E["docs generate"] -->|creates payload| F["replaceUnderHeading<br/>finds ## heading"]
F -->|replaces until<br/>next ## or EOF| G["Bounded injection<br/>no end markers"]
G -->|promotes to ###| H["Proper markdown<br/>nesting"]
File Changes1. packages/core/src/resolver.ts
|
Code Review by Qodo
1.
|
The newly-public gigetTarballPath took ref directly into path.join, so a ref containing ".." or path separators could resolve outside the source's giget cache subdirectory. When noCache is set and fs.rm runs, that meant a crafted ref could delete a sibling source's tarball. Add a containment check: resolve the path against the source-specific subdir and throw when the result escapes it. The check tolerates legitimate refs that contain a single "/" (e.g. release/v1.0) because they stay under the source subdir; it rejects ".." and absolute paths. Also re-export gigetTarballPath from packages/core/src/index.ts so consumers (and tests) don't have to deep-import the internal module. Updated the resolver test to import from the public entry and added a regression test that asserts a malicious ref throws and leaves a sibling tarball untouched.
Summary
Two small fixes the user hit while exercising agnos:
agnos skill update <name>now actually re-fetches upstream.runUpdatealready passednoCache: trueandfetchGitwiped the agnos-side<cacheDir>/repos/<key>dir, but giget keeps its own tarball at~/.cache/giget/<provider>/<owner>-<repo>/<ref>.tar.gzand short-circuits insidedownload()whenever the upstream ETag matches the cached sidecar. With proxies / CDNs / unchanged HEAD this reused the stale tarball. Now whennoCache: trueis set,fetchGitalso removes that tarball and its.jsonsidecar before invokingdownloadTemplate. NewgigetTarballPath()helper mirrors giget'scacheDirectory()(XDG_CACHE_HOME →~/.cache/giget) so the path stays in sync with giget v1.x.Docs injection is now bounded by markdown structure, not custom end markers. The
>__Documentation index end__/>__Documentation rules end__lines are gone.RULES_BLOCK/INDEX_BLOCKcollapse to plain string constantsRULES_HEADING/INDEX_HEADING.replaceBetweenMarkersis replaced byreplaceUnderHeading(text, heading, payload), which finds the## Documentation Index(or## Documentation Rules) line and overwrites everything until the next##/#heading or EOF, keeping a single blank line before the next heading for idempotency.renderIndexBodynow emits### Sectioninstead of## Section, so the injected layout matches the spec:Files
packages/core/src/resolver.ts—gigetTarballPath()+ wipe-on-noCache.packages/core/test/resolver-nocache.test.ts— new test, mocksdownloadTemplate.packages/domain-docs/src/schema.ts—RULES_HEADING/INDEX_HEADING.packages/domain-docs/src/inject/markers.ts—replaceUnderHeading().packages/domain-docs/src/cli/inject.ts— wired to new API.packages/domain-docs/src/cli/generate.ts—###index sections.packages/domain-docs/test/{markers,inject,generate}.test.ts— updated.Test plan
pnpm -F @luxia/core typecheck— cleanpnpm -F @luxia/core test— 119/119pnpm -F @luxia/domain-docs typecheck— cleanpnpm -F @luxia/domain-docs test— 42/42pnpm -F @luxia/domain-docs build— cleanagnos skill update <name>against a repo with a fresh upstream commit → confirm~/.cache/giget/.../<ref>.tar.gzis removed and the new content extracts into.agnos/skills/<name>/.agnos docs generate && agnos docs inject→ confirmAGENTS.mdhas## Documentation Index/## Documentation Rulesheadings with###subsections under the index, no>__Documentation…end__lines, and rerunning is a no-op.