返回目录
其他 插件

dsh-plugin-ponytail

BorisLoveDev/dsh-plugin-ponytail

ponytail (lazy senior dev mode) as a native DeepSeek Harness plugin: per-project rules, skills and a /ponytail level command.

Stars
0
Forks
0
Issues
0
更新
今天

PROJECT TOPICS

项目标签

INSTALL REFERENCE

安装参考

未验证
dsh plugin --profile web add github:BorisLoveDev/dsh-plugin-ponytail

该命令指向仓库当前默认分支;尚无绑定当前 commit 的完整验证结果。

PROJECT README

README

dsh-plugin-ponytail

ponytail — "lazy senior dev mode" — as a native DeepSeek Harness plugin.

Upstream ponytail ships adapters for Claude Code, Codex, Cursor, OpenCode, Kiro, Windsurf and others, and none for DSH: its always-on layer is a UserPromptSubmit lifecycle hook, which DSH agents do not have. This package rebuilds that layer on the DSH extension points that do exist, so you get the reference behaviour without patching upstream and without planting generated files in your repositories.

What it contributes

Contribution DSH extension point
The always-on rules, recomputed for every prompt systemPrompt.context()
The six ponytail* skills, listed only while the mode is on skills.registerProvider()
/ponytail lite\|full\|ultra\|off\|status commands.register()

Nothing is written into your project: no AGENTS.local.md, no symlinks in .agents/skills. The only artifact is the .ponytail/ directory you already have.

Relationship to the other DSH ponytail plugins

Two other ports already exist, and they cover different slices:

  • dsh-ponytail (ccll) vendors @dietrichgebert/ponytail as a dependency, injects the ruleset as a global system-prompt section (reaching subagents too), adds the six skills as runtime skills, and detects the "stop ponytail" / "normal mode" deactivation. Global and per-session.
  • dsh-ponytail-skills (gongyijie85, repository gongyijie85/dsh-ponytail) ships the six skills inside the package and registers only a ctx.skills provider. Skills only: it injects no rules, so the style is adopted when the model chooses to load the skill rather than being enforced on every prompt.
  • This package: always-on rules plus skills plus the command, scoped to a project by the presence of .ponytail/.
dsh-ponytail dsh-ponytail-skills this package
Always-on rules yes (system-prompt section) no yes (runtime context, or a section)
Skills 6, registered at mount 6, provider from the package whatever is in .ponytail/skills/
/ponytail command yes no yes
Deactivation gesture yes no yes
Which projects get it all all only those with a .ponytail/ above the cwd
Mode storage in-process, per session n/a .ponytail/state, on disk, per project
Mode after resuming a session back to the default n/a preserved
Upstream text required at runtime shipped in the package vendored into .ponytail/ by your own tooling
Name/invocation frontmatter rules n/a follows the built-in provider follows the built-in provider

Ideas borrowed from dsh-ponytail, because they were right: optional per-feature injection instead of a hard inject (a profile without a command surface must not lose the rules), loud rejection of unknown config keys, the whole-message deactivation gesture, a /ponytail default command, and shipped types.

Ideas taken from dsh-ponytail-skills: honouring the built-in provider's frontmatter contract — the disable-model-invocation / user-invocable invocation policy and the kebab-case name grammar, so a skill placed in .ponytail/skills/ behaves exactly as it would in .agents/skills/.

Ideas this package contributes: per-project opt-in by walking up from the cwd (which crosses nested repository roots), mode state on disk so it survives resume and restart, and systemPrompt.context as the default placement so a level change does not perturb the system-prompt prefix.

Install any one of them, not two: dsh-ponytail and this package each register a /ponytail command, and the second registration fails.

If you want ponytail everywhere with no per-project setup, use dsh-ponytail. If you only want the skills available and are happy for the model to decide when to use them, dsh-ponytail-skills is the smaller install. If you want to opt in per project, enforce the rules on every prompt, and keep the mode next to the code, use this.

Install

dsh plugin --profile web add dsh-plugin-ponytail

The manifest declares dsh.bundle.patch, so the package also joins dsh.profile.bundles and the loader applies its patch as one more layer. Restart the profile so the new layer boots.

For a checkout instead of the registry:

dsh plugin --profile web add /path/to/dsh-plugin-ponytail

Mounting into a running host

DSH reads dsh.profile.bundles from the profile's package.json at boot, so a bundle normally takes effect on the next start. A profile with patchReload: live does watch its patch files continuously, so a running host can pick the row up without a restart. Leave the package in dependencies, remove it from dsh.profile.bundles, and add a pure insert to the profile's own patch layer (~/.dsh/profiles/web/cordis.patch.yml):

- insert:
    - id: ponytail
      name: dsh-plugin-ponytail

Keep it out of dsh.profile.bundles while that insert exists. applyEntryPatches does not deduplicate inserts by id, so the row would mount twice — once from the bundle patch and once from the profile layer.

Set up a project

The plugin contributes nothing until it finds a .ponytail/ directory. Presence is the opt-in.

mkdir -p .ponytail/skills
# rules.md            — the static rules (upstream AGENTS.md)
# skills/*/SKILL.md   — the vendor skills
# state               — off | lite | full | ultra
printf 'full\n' > .ponytail/state

Copying upstream's skills/ and AGENTS.md into .ponytail/ is enough:

git clone --depth 1 https://github.com/DietrichGebert/ponytail /tmp/ponytail
mkdir -p .ponytail/skills
cp /tmp/ponytail/AGENTS.md .ponytail/rules.md
cp -R /tmp/ponytail/skills/. .ponytail/skills/
printf 'full\n' > .ponytail/state

Skills you add yourself follow the built-in filesystem provider's contract, so they behave exactly as they would in .agents/skills/: name must be kebab-case, and disable-model-invocation: true / user-invocable: false control who may invoke them. A skill whose name or invocation frontmatter is invalid is skipped rather than half-registered.

How the directory is found

The plugin walks up from the session's working directory until it finds .ponytail/, and stops at the filesystem root.

That one rule is the whole scoping model, and it behaves differently from a file planted in a single project tree:

  • A session started in a nested repository still finds .ponytail/. If the tree holding .ponytail/ contains a checkout that is itself a git repository, a session started inside that checkout is covered too, because the walk crosses project roots instead of stopping at the nearest one.
  • Every descendant directory inherits the mode, with no per-project installation.
  • Sibling projects are unaffected: no .ponytail/ above them means no rules and no skills.

Levels

/ponytail                        # show the current level, directory and skill count
/ponytail lite                   # name the lazier alternative in one line
/ponytail full                   # the ladder enforced (default)
/ponytail ultra                  # YAGNI extremist, deletion before addition
/ponytail off                    # withdraw the rules and the skills
/ponytail default lite           # persist the user-level default across projects

/ponytail <level> writes .ponytail/state. The rules are recomputed on the next prompt, so off takes effect immediately and nothing has to be regenerated.

Upstream's whole-message deactivation gesture also works: a message that is exactly stop ponytail or normal mode switches that project to off. A message that merely mentions the phrase ("add a normal mode toggle") does not.

/ponytail default <mode> writes upstream's own $XDG_CONFIG_HOME/ponytail/config.json (or ~/.config/ponytail/config.json, %APPDATA%\ponytail\config.json), which is the same file the reference adapters and this plugin's own resolution order read. It is the level used when no .ponytail/state exists.

The level is resolved exactly as upstream resolves it: PONYTAIL_DEFAULT_MODE, then defaultMode from that config file, then this row's defaultMode, then full. A per-project .ponytail/state wins over all of them.

ponytail-review is a skill, not a level; /ponytail review says so.

What the model sees

Upstream's own payload, byte for byte:

PONYTAIL MODE ACTIVE — level: <mode>

<the mode-filtered body of skills/ponytail/SKILL.md>

filterSkillBodyForMode is a faithful port of upstream hooks/ponytail-instructions.js, including its two subtleties: only the intensity table rows and the quoted worked examples are level-specific, and an ordinary rule bullet whose label happens to start with a level word survives verbatim. When skills/ponytail/SKILL.md is missing, .ponytail/rules.md is used instead.

Verified against upstream on the vendored SKILL.md: identical for lite, full and ultra, modulo a trailing newline.

Context, cache and cost

The rules are recomputed for every prompt, so it is fair to ask whether that churns the prompt prefix and costs cache hits. Measured:

  • The system prompt is never touched. The default placement: context is a durable user-role snapshot, not a system-prompt section. The cacheable prefix — system prompt and tool schemas — contains no ponytail text.
  • The payload is byte-stable. Rendering the same unchanged files three times, including from a fresh mount, produces identical bytes, and it carries no timestamp, path, digest, or process id. That is asserted by a test, so a future edit cannot quietly make it nondeterministic.
  • An unchanged contribution is not re-sent. The harness compares the rendered runtime context with the snapshot it already holds and emits a message only when the text differs; when it does differ, the new one replaces the old rather than accumulating. Measured on a live 209-step session using this same seam: exactly one snapshot message.
  • Skill order cannot flap. This provider reads readdirSync, whose order is not guaranteed, but ctx.skills.snapshot() sorts, and the catalog digest is a hash of [name, description] pairs only, so neither order nor metadata can trigger a re-emit.
  • What it adds, once per session: the rules payload (5228 characters at full) plus six skill-catalog entries (2368 characters, each description capped at 500) — roughly 2.1k tokens, cached from then on. /ponytail adds nothing to the model context; commands are human-facing and never enter the prompt.

The only thing that moves the text is a change you make:

  • /ponytail lite|full|ultra (or editing .ponytail/state) changes it once; the prefix up to that point stays cached, so it is one ordinary cache miss.
  • payload: rules uses rules.md (2590 characters) instead of upstream's mode-filtered SKILL.md body (5228) for about half the always-on text, at the cost of the per-level filtering.
  • Re-enabling a file-based layer (bash .ponytail/switch on) delivers the rules a second time as workspace instructions, doubling the text.

The doubled-ruleset warning

That last configuration is the one real trap, so the plugin detects it. When an AGENTS.md, CLAUDE.md, AGENTS.local.md, or CLAUDE.local.md anywhere from the ponytail root down to the session cwd carries an upstream ponytail marker, the agent is warned in the prompt, and /ponytail status reports it:

  duplicate : /path/to/AGENTS.local.md — rules also arrive as workspace instructions

The warning is its own prompt entry, so a healthy project contributes nothing and the rules payload stays byte-identical to upstream's. It requires an upstream phrase (PONYTAIL MODE ACTIVE, "lazy senior dev", "best code is the code never written"), not merely the word "ponytail", so a project that mentions the name is not flagged. Set warnOnDuplicateInstructions: false to turn it off.

Configuration

The row takes an optional config. Every field is optional.

- insert:
    - id: ponytail
      name: dsh-plugin-ponytail
      config:
        dirName: .ponytail # directory searched for, upward from the cwd
        placement: context # context (default) | section
        payload: skill # skill (default, upstream payload) | rules
        skills: true # serve .ponytail/skills/
        skillRank: 260 # precedence vs the built-in roots (project 100/200, custom 300)
        command: true # register /ponytail
        commandName: ponytail
        contextOrder: 130 # placement after the harness policy contexts (110-120)
        contextName: ponytail
        defaultMode: full # used when state is absent and upstream resolves nothing
        warnOnDuplicateInstructions: true # warn the agent when the rules arrive twice

placement: section registers a system-prompt section instead of a runtime context. Use it only if you need the rules pinned into the system prompt: it changes the prompt prefix whenever the level changes, and the default context placement is the same durable user-role snapshot shape the harness uses for workspace instructions.

Caveats

  • The minimal agent preset calls suppressRuntimeContext(), which switches off every dynamic context in that scope. With the default placement: context the rules are silent there; use placement: section if you need them on minimal.
  • A catalog read lists skills but does not load their bodies; each body is read from disk when the skill is actually used.
  • The plugin serves whatever is in .ponytail/skills/. Keeping that directory current is upstream's problem, not this package's.

Migrating from a file-based install

If you had ponytail wired through a generated AGENTS.local.md plus symlinks in .agents/skills/, turn that layer off and leave .ponytail/ in place:

bash .ponytail/switch off # removes AGENTS.local.md and the symlinks

The plugin reads the same .ponytail/rules.md, .ponytail/skills/ and .ponytail/state, so nothing else has to move. Leaving both layers on would show the rules twice.

Discovery and listing

DSH has no first-party plugin registry: dsh plugin forwards to pnpm inside the profile, so npm is the distribution channel. The community catalog is DSH Plugin Hub, which indexes plugins by the npm keyword dsh-plugin and the GitHub topic dsh-plugin.

  • npm: npm search dsh-plugin
  • from inside an agent: install dsh-find-plugin and search the dsh-plugin topic
  • in the GUI: dsh plugin --profile web add dsh-plugin adds a plugin marketplace to Settings

To list a plugin: publish to npm with the dsh-plugin keyword, give the repository the dsh-plugin topic, then submit it at https://dsh-plugin.org for review.

Development

pnpm install
pnpm test

test/selfcheck.test.mjs drives the plugin against stubs of the three host registries, so discovery, level resolution, mode filtering, the skills provider and the /ponytail handler are all exercised without a running harness.

License

MIT. ponytail itself is a separate project by Dietrich Gebert, under its own license; this package only adapts it.

CLASSIFICATION EVIDENCE

分类依据

项目类型插件
功能分类其他
规则置信度

系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。当前命中: 无有效分类标签。