dsh-plugins
AGENTS.md3 starsChanged 36 days agoMITView on GitHub
- Before finishing a task, run the checks relevant to the changed scope. - For code changes, run the available tests, linter, type checker, build, and package or integration verification when applicable. Prefer the repository's combined check command when it covers the affected code. - Do not claim that a task is complete while a relevant check is failing. If a check cannot be run, state which check was skipped and why. - Re-counting the head of a branch after…
Where files are headingNew AGENTS.md files have more rules about keeping secrets out of the code, and fewer about code style.
Of the AGENTS.md files we read, the 31,700 first seen in the last six months, against the older ones: the kind of rule that gained most share, and the kind that lost most. About files like this in general, not this one, and an estimate, since a kind is a pattern in the words.
In this repository6 other files it gives its agents.
- create-pluginSkill
- leak-guardSkill
- qa-stand-runSkill
- release-pluginsSkill
- session-checkpointSkill
- shared-checkoutSkill
The file
We split this AGENTS.md into the rules it gives an agent
12 rules. About 0 are common, 5 are distinctive, and 7 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈5
Distinctive · estimate
This author's own choices. Worth a look.
7
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 5
Before finishing a task, run the checks relevant to the changed scope.
- Distinctive: line 9
Do not claim that a task is complete while a relevant check is failing.
- Distinctive: line 10
If a check cannot be run, state which check was skipped and why.
- Distinctive: line 29
Do not restructure a matrix, a job split, or an artifact name to work around a Gitea Actions limitation, even when the mirror run is red or …
- Very specific: line 60
Every user-visible @yadsh/dsh-qa-surface change that adds or updates an Nx version plan must also update …
- Very specific: line 68
Every package that declares dsh.client must ship a classic browser bundle that registers itself through window.__ModuleLoader__.load({ id, …
- Very specific: line 92
Do not register a configuration card in the Settings dialog.
- Very specific: line 116
Use settings.section for a page that is mounted inside the native settings tree, as dsh-preset-persona-editor does.
- Very specific: line 122
Use a Plugins-page seat — plugins.row.config (keyed <package name>#<row id>) for one row's own configuration, plugins.bundle.config (keyed …
- Distinctive: line 187
Keep the shell rules identical in every self-contained client bundle that owns its shell.
- Very specific: line 243
Test fixtures ship in public git history even when they do not ship in tarballs.
- Very specific: line 248
Before committing anything naming a project, host, task or person, grep for marker classes: ticket keys [A-Z]{3,10}-[0-9]+, corporate …
≈ “Common” is an estimate: at least 1 in 5 of the 40,246 AGENTS.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Repository instructions
Verification before completion
- Before finishing a task, run the checks relevant to the changed scope.
- For code changes, run the available tests, linter, type checker, build, and package or integration verification when applicable. Prefer the repository's combined check command when it covers the affected code.
- Do not claim that a task is complete while a relevant check is failing. If a check cannot be run, state which check was skipped and why.
- Re-counting the head of a branch after a batch of merges includes lint of the
whole line —
pnpm lint:workspaceandNX_DAEMON=false pnpm nx run-many -t lint --skip-nx-cache— because a tooling package of this repository declares alinttarget only, so a set of build/test/verify passes over it without running a task and still exits 0. Judge that run by itsSuccessfully ran target lint for N projectsline, not by its exit code. The set, its reason and the ban on widening theno-useless-escapeexemption are indocs/VERIFICATION.md, §Re-counting the line after a batch of merges. - Report the exact verification commands and their results in the final handoff.
CI workflows stay GitHub-shaped
- The public GitHub Actions run is the release pipeline: it publishes the packages and gates the wave. A Gitea Actions mirror run is a convenience copy on a self-hosted runner and has no authority over the shipped artifact.
- Therefore the shape of
.github/workflows/*is defined by what GitHub needs. Do not restructure a matrix, a job split, or an artifact name to work around a Gitea Actions limitation, even when the mirror run is red or oversized because of it. Fix the mirror on the mirror's side (runner capacity, the status the board assigns to mirror runs) or ask the owner. - Adding a step, or making a step's condition depend on the event type, stays
within GitHub's shape and is fine. Replacing
matrix: ${{ fromJSON(needs.prepare.outputs.matrix) }}with a fixed bucket list, so that a host which cannot expand it still fans out, is not. - A mirror run that fails for reasons the changed code does not cause is not a
blocker for the pull request: mark the pull request
ci-externaland say which run and job were examined. Do not "fix" such redness by weakening the workflow's checks on main.
File size budget
- A source file under
plugins/*/src,packages/*/src,plugins/*/scriptsorpackages/*/scriptsmust stay within 1400 lines (1200 warns) and a file underplugins/*/testsorpackages/*/testswithin 900 (700 warns); a generated bundle under a package'slib/is held to a runaway line limit only, and no budget is measured in bytes. The repository root'sscripts/is outside the measured scope until a card splits it. The thresholds, the scope and the reason of every exemption are indocs/VERIFICATION.md. scripts/check-file-budget.mjs, run aspnpm check:files, carries the files that were already over budget when the gate landed, each with its reason on the same line, and that list only shrinks: split the oversized file instead of raising a threshold or adding a path to it. Growing an allowlisted file is neither an error nor a warning — the refactor cards own those paths — but every entry is printed asallowlisted: <path> — <reason>in each run, so an exemption is never invisible.
QA surface release notes
- Every user-visible
@yadsh/dsh-qa-surfacechange that adds or updates an Nx version plan must also updateplugins/dsh-qa-surface/src/client/components/QaChangelog.tsxfor the planned version in the same change. Do not bumppackage.jsonmanually; Nx materializes the version during release.
Browser client module identity
- Every package that declares
dsh.clientmust ship a classic browser bundle that registers itself throughwindow.__ModuleLoader__.load({ id, factory }). - The registration
idmust exactly equal the package's fullpackage.json.name, including its npm scope. Do not use the Cordis patch id, the plugin directory name, or an unscoped shorthand. - Client bundle and packed-package verification must assert the full package
name. When an integration check fetches a bundle, use
/plugins/<full-package-name>/client.js, preserving the scoped path.
Plugin configuration card UI
Choosing the registration point
- A plugin's configuration card registers in the Host's Plugins panel, on
the
plugins.row.configseat of this bundle's own row. The seat is keyed<package name>#<row id>, where the row id is theidthe bundle'scordis.patch.ymldeclares. That row id is also the settings namespace the Host resolves the volatile Config under, so a seat move never orphans a saved value. - A feature-owned page — backed by custom Remote services or account/session
state rather than by this bundle's settings namespace — takes the panel's
bundle-level seat,
plugins.bundle.config, keyed by the package name. That seat renders no form at all, which is why a configuration card uses the row seat and resolves its own form. - Do not register a configuration card in the Settings dialog. The
settings.plugin.itemslot was deleted in0.1.7, andsettings.plugins.tab— a tab of the native Plugins settings section — is not a registration point a plugin of this repository adds to, even though the Host still ships that slot. plugins.itemis neither: it is the Host's own list of companion packages, fed from inside the Plugins page (ui-plugin-managerrenders its entries itself), and not a seat a plugin of this repository registers into.- The Plugins panel is not the settings directory, so a card seated there keeps
answering from a non-loopback browser, where the settings directory is
intentionally unavailable. Read
state.statusandstate.writableoff the form and disable the write controls instead of hiding the card. - The seat hands its registrant two views:
view: 'page'is the card, and the same entry is seated asview: 'summary'wherever the page wants a one-liner for a row that declares no description. Answer the summary with that sentence and mount the card only forpage— a card inside a line of text draws a page within a line and polls its Remote twice. - Alongside the injected face the seat passes its own owner prop named
form: aConfigPageFormof{ state, mutate }only, which can neither be subscribed to nor written field by field, and which isundefinedwhen the Config declares no.volatile()field — a page that edits nothing says so, it does not invent a field to make a surface appear. The renderer spreads the owner props after the face, so a card that needs the fullConfigFormresolves it throughctx.configForms.get<T>(namespace)and passes it in its face under a name other thanform. - Use
settings.sectionfor a page that is mounted inside the native settings tree, asdsh-preset-persona-editordoes. It is a placement choice, not an availability guarantee: a page that must keep working without the native settings surface belongs on the Plugins panel. A page registered here that reuses the standard card shell keeps its<li>root inside a plugin-owned<ul>. - Use a Plugins-page seat —
plugins.row.config(keyed<package name>#<row id>) for one row's own configuration,plugins.bundle.config(keyed by the package name) for the bundle's — when the card edits that bundle's own Host Config and nothing else, asdsh-documentsdoes. This is a placement on the other surface, not a rename of the settings namespace: the row id is the namespace the Host serves the form under, so moving a card here keeps every stored value readable. Two consequences follow: the page reports itself unavailable on a Host without a managed profile and reads its inventory throughapi-remotes, so a card that must stay reachable from a non-loopback browser belongs insettings.plugins.tab, not here; and the page draws the row's own title and description from the package's exported display metadata (<package>/locale/en.json, exported at./locale/en.json), not from alabelthe registrant supplies — without that file the row is named by its full package name. A card registered here that reuses the standard card shell keeps its<li>root inside a plugin-owned<ul>, since the configuration section supplies no list. - That naming fallback is the Host's behaviour; the requirement is this
repository's:
pnpm verify:packagesdemandslocale/en.jsonof every plugin package, with a non-emptymeta.titleandmeta.description, exported at./locale/en.jsonand covered byfiles. Shipping the file is what makes a row read as a phrase an operator recognises instead of a package specifier, and thefileshalf is what makes it survive the tarball — a locale directory the package omits falls back just the same on an installed deployment. en.jsonis the English dictionary and the discovery anchor: the Host enumerates every*.jsonin that directory and re-resolves each through the exports map, so a second locale needs the"./locale/*.json"glob export, and a non-language filename left inlocale/is a metadata diagnostic for the whole row. Until a locale other thanenis served, write the copy in English — the panel titles rows in English whatever language the card body speaks.
Two kinds of card: who owns the chrome
- On the Plugins panel,
plugins.row.configseats the card inside the Host's own row-detail page. The page draws the card surface (a--dsw-radius-xl, 20 px radius), the row title, the row id, the module name and the description line, and only then mounts the registrant'spageview under the page's configuration section. A card seated here renders the body only: no outer border, no background of its own, no<li>root, none of thedsh-plugin-card*classes and no chevron of ours. Repeating the chrome inside the Host's card draws a second frame and a second heading next to the first-party rows, which is what this rule exists to prevent. - The same bundle still answers the
summaryview of its own seat, and that answer lands inside the Host's description paragraph: one plain sentence, never a card. - Focus treatment on this surface is the Host's, from its ring tokens:
outline: var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary)). Both tokens need a fallback: where--dsw-focus-ring-widthis undeclared, the wholeoutlineshorthand is invalid and the ring disappears instead of degrading. A:focus-visiblerule of our own with a hard-coded outline loses to the Host'sfocus.css(specificity 0-3-2 against 0-2-0) under pointer modality, so raising our specificity is the wrong repair — take the tokens. - The seat key
<package name>#<row id>and the settings namespace it resolves are not part of the chrome. Moving them orphans the values a user already saved, so a chrome change never touches them. - A page mounted in the native settings tree (
settings.section), or in a tab of the native settings surface (settings.plugins.tab) — a placement this repository is moving away from, not one a new card chooses — still owns its whole card: nothing around it draws a frame. Such a card's root is a<li>rendered inside a plugin-owned<ul>— not an<article>and not a permanently expanded custom panel. - For a card that owns its shell, use the shared BEM class contract:
dsh-plugin-card,dsh-plugin-card--open,dsh-plugin-card__header,dsh-plugin-card__head-text,dsh-plugin-card__name,dsh-plugin-card__description,dsh-plugin-card__badge,dsh-plugin-card__chevron, anddsh-plugin-card__body. Keep plugin-specific class names inside the body. Keep the shell rules identical in every self-contained client bundle that owns its shell. Do not add a plugin-specific border, shadow, gradient, header icon, title size, or hover treatment. The canonical shell CSS is:
.dsh-plugin-card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s} .dsh-plugin-card:hover{border-color:var(--dsw-alias-label-dimmed)} .dsh-plugin-card--open{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)} .dsh-plugin-card__header{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex} .dsh-plugin-card__header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px} .dsh-plugin-card__head-text{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex} .dsh-plugin-card__name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4} .dsh-plugin-card__description{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5} .dsh-plugin-card__badge{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px} .dsh-plugin-card__chevron{width:14px;height:14px;color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s} .dsh-plugin-card--open .dsh-plugin-card__chevron{transform:rotate(180deg)} .dsh-plugin-card__body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:8px}- The header of a card that owns its shell is a full-width
<button type="button">witharia-expanded, an accessible show/hide label, the title and description stack, an optional status badge, and the chevron in that order. Render the body only while open. While the row's namespace answersunavailable, render no card. - A card that owns its shell uses a 14 by 14 inline SVG chevron with
viewBox="0 0 14 14"and the pathm3.5 5.25 3.5 3.5 3.5-3.5, stroked withcurrentColor, round caps, and round joins. Do not use font glyphs such as⌄or▾; their shape and baseline vary by font and encoding. - Build plugin-specific controls from
--dsw-alias-*design tokens so light, dark, and system themes stay coherent. Hard-coded colors may communicate a narrow semantic state, but must not define the card surface or typography. - Package verification for a configuration card is decided by the seat the bundle
registers on, and the seat is read off the built bundle rather than declared by the
plugin. A card that owns its shell (
settings.section,settings.plugins.tab) must show the standard shell class and the SVG path in the built client bundle, and must reject legacy outer-shell classes, font chevrons and non-standard shell tokens. A card seated on the Plugins panel row (plugins.row.config) must show neither — anydsh-plugin-card*class or our chevron path there is the second frame this rule forbids. A registration is the strongest statement of a seat, and where the bundle carries none — the seat reached as an option, or the registration is positional — the contract reads the seat names the bundle quotes, so a mention in a tooltip or a surviving comment can reach the decision after all. Name the seat at the registration and keep seat strings out of prose. A package that publishes the shell for others to inline, and registers nothing itself, is held to the canonical shell half directly (verifyCanonicalShell). When a local DSH web app is available, visually compare the card with a first-party one: closed, hovered, focused and open for a card that owns its shell, and the row's own states — collapsed, expanded, focused after a mouse click — for a card that does not.
No internal identifiers in public content
- The repository is public and every package publishes to npm. Examples,
fixtures, specs, tool descriptions, error messages and READMEs must use only
synthetic placeholders:
PROJ-123/PROJ-456,jira.example.corp, «Демо-продукт». Never real task numbers under a renamed prefix, internal hosts, LAN addresses, project keys, product names, or people's names. - Test fixtures ship in public git history even when they do not ship in
tarballs;
lib/*.jsstrings and anything in the packagefileslist ship to npm. - Release notes must not reveal that a leak existed ("no longer names an internal Jira project" is itself a disclosure).
- Before committing anything naming a project, host, task or person, grep for
marker classes: ticket keys
[A-Z]{3,10}-[0-9]+, corporate hosts, RFC1918 addresses with real ports, person names and logins. - Exempt:
qa-deploy/,qa-deploy-docker/,.portable/kits. - The audit method behind these rules — what counts as a leak, which surfaces a sweep
covers, how the marker dictionary and the allowlist interact, and what to do after
something shipped — is written up in
.agents/skills/leak-guard/SKILL.md. The report of an actual sweep stays out of the public tree: the method is reproducible, the findings are not a public artifact.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

