feat(skills): carry devfive-frontend and let a skill be more than one file - #63
Merged
Merged
Conversation
… file
devup_figma_export returns one screen's TSX, but a project is a tree of
routes, and knowing devup-ui does not tell an agent which file that TSX
becomes.
The gap is not hypothetical. A vinext project created from service-template
was implemented as a Vite SPA - a main.tsx swapping screens on local state,
with authored CSS files beside code that has globalCss() - by an agent that
had been given the devup-ui skill and nothing else. devup-ui is a styling
skill and correctly says nothing about project structure, so the rules that
would have caught it live in devfive-frontend, which no bare machine had.
devfive-frontend is five files, so carrying it meant a skill could no longer
be one document.
A third origin carries it. An `embedded` skill is copied from another DevFive
repository and pins the commit it copied, so refresh-skills.mjs can move it
forward. An `own` skill is authored here, has no second copy to drift from,
and pins no commit - printing "at unknown" would read as a lost revision
rather than one that never existed. Making that an origin rather than a
special case keeps both on the same install path.
The manifest now records documents[{path,bytes,sha256}] per skill instead of
one digest per skill, and every origin uses skills/<name>/<relpath>, so a
document's source path and its install path are the same string and cannot
disagree. install() stages a skill's documents in one OutputTransaction: a
SKILL.md that survived while its four references did not is the failure this
exists to avoid, because it looks installed and its links go nowhere. Only
the entry document is annotated, so the manifest digest stays true of every
reference that lands on disk.
Two things surfaced while building it.
.gitattributes matched skills/*.md, and a git pattern containing a slash does
not let * cross one. Moving the documents into per-skill directories silently
dropped the -text attribute that the file's own comment says exists to stop a
Windows checkout from failing the integrity check on that platform alone. It
now matches skills/**/*.md, confirmed with git check-attr.
refresh-skills.mjs skipped own skills entirely, which left no way to update a
digest after editing a document this repository authors - the trap that makes
someone write a SHA-256 by hand. For an own skill the direction reverses: the
file on disk is the truth and the script reseals the manifest from it.
The integration tests spelled out how many skills exist, so adding one failed
four tests that had nothing to say about it. They derive the counts now.
devfive-frontend also gained the two rules the SPA incident needed - that
vite.config.ts does not mean Vite, because vinext runs Next App Router on
Vite, and that no .css or .scss belongs in application source - plus the
extraction rule from devup-ui issue 663, checked against the extractor
itself: an inline object literal indexed at a style prop extracts to static
classes, an external object referenced by name becomes a CSS variable, and an
external object of css() results is neither, because css() already extracted
at its own call site and className is never a style-extraction source.
#64) A binary's skill copy ages independently of the repository it describes. Installing that copy without saying where it came from leaves the agent applying old rules with no reason to question them. Fetch current documents on an explicit MCP install, but keep offline delivery a first-class path: the bare machine this feature exists for is also where a download is least likely to work. Reuse the CLI's ETag-aware upstream through a URL-taking trait. MCP URLs come from each manifest repository and document path; the CLI still fetches devup-ui and writes its existing HOME targets. A four-second budget covers all fetching in one MCP install, and every write still goes through the server's OutputPolicy and one OutputTransaction. If any reference fails, use the whole embedded skill rather than mixing documents from two copies. Report fetched or embedded for every installed skill, with a fallback reason. A 404 additionally warns that the manifest may point at a moved file. Fetched documents have no pinned commit, so their entry note reuses the CLI provenance fields: URL, ETag, fetched time and SHA-256 of upstream bytes. Keep that note after frontmatter so the skill loader still sees it. Own skills stay in the binary; external skills are never fetched or written. Read DEVUP_MCP_SKILLS_OFFLINE once and leave HTTP initialization on the install path, unreachable from self-check, status or resource reads. Run MCP integration tests in child processes with offline configuration set before startup. Injected upstreams cover fresh documents, every fetch failure, 404 warnings, nested references, the shared deadline, and rollback without opening a socket. Preserve all four CLI behavior tests.
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.
Why
devup_figma_exportreturns one screen's TSX, but a project is a tree of routes, and knowing devup-ui does not tell an agent which file that TSX becomes.The gap is not hypothetical. A
vinextproject created fromservice-templatewas implemented as a Vite SPA — amain.tsxswapping screens on local state, with authored.cssfiles beside code that hasglobalCss()— by an agent that had been given the devup-ui skill and nothing else.devup-ui is a styling skill and correctly says nothing about project structure. The rules that would have caught it live in devfive-frontend, which no bare machine had. And devfive-frontend is five files, so carrying it meant a skill could no longer be one document.
What changed
A third origin:
ownembeddedown(new)externalAn
ownskill pins no commit because it has no second copy to drift from — printingat unknownwould read as a lost revision rather than one that never existed. Making it an origin rather than a special case keeps both carried origins on the same install path.Multi-document skills
manifest.jsonrecordsdocuments[{path, bytes, sha256}]per skill instead of one digest per skill, so a five-document skill whose fourth reference was edited fails on that reference, naming it.skills/<name>/<relpath>for every origin, so a document's source path and its install path are the same string and cannot disagree. The three existing skills were moved withgit mv(0 content change).install()stages a skill's documents in oneOutputTransaction. ASKILL.mdthat survived while its four references did not is the failure this exists to avoid: it looks installed and its links go nowhere.Two things this surfaced
.gitattributessilently stopped applying. It matchedskills/*.md, and a git pattern containing a slash does not let*cross one. Moving the documents into per-skill directories dropped the-textattribute that the file's own comment says exists to stop a Windows checkout from failing the integrity check on that platform alone. Nowskills/**/*.md, confirmed withgit check-attr.refresh-skills.mjshad no way to reseal anownskill. It skipped them entirely, so editing a document this repository authors broke the integrity test with no tool offering to fix it — the trap that makes someone write a SHA-256 by hand. For anownskill the direction reverses: the file on disk is the truth and the script reseals the manifest from it. Demonstrated:Skill content
devfive-frontend gained the two rules the SPA incident needed:
vite.config.tsdoes not mean Vite.vinextruns Next App Router on Vite, so a normal App Router project has avite.config.tsand nonext.config.ts. Readpackage.json. There is nomain.tsxand noindex.htmlin such a project..css/.scssin application source.resetCss()+globalCss()+ style props, with the@devup-ui/reset-cssbuild recipe (include/optimizeDeps.exclude/ssr.noExternal) that previously had to be reverse-engineered out ofnode_modules.It also records the extraction rule from dev-five-git/devup-ui#663, checked against the extractor rather than assumed (
extract_style_from_member_expression.rs):mem.objectArrayExpression/ObjectExpression(inline literal)Identifier(external object by name)dynamic_style()→ CSS variable…and the exception that resolves an internal contradiction in the skill: an external object of
css()results is fine, becausecss()already extracted at its own call site andclassNameis never a style-extraction source (it appears nowhere inextract_style_from_jsx.rs;prop_modify_utils.rsonly merges it).Tests
The integration tests spelled out how many skills exist, so adding one failed four tests that had nothing to say about it. They derive the counts from the report now. Added coverage for the multi-document case: every reference
SKILL.mdlinks to must be a file the install writes.Verification
cargo clippy -p devup-mcp --all-targets --all-features -- -D warningscargo test -p devup-mcp --all-featuresnode scripts/refresh-skills.mjs --checkgit check-attr texton all three depthstext: unsetFollow-up (not in this PR)
Fetch-first with embedded fallback for
devup_skills install. Two design points to settle first: the provenance note a fetched document carries (it has no commit), and whether the CLI path (--install-skills, which writes to$HOME) unifies with the MCP path (which writes project-locally). Keeping the CLI contract unchanged is the smaller correct change.