<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Plugins on Trevor's Blog</title><link>http://mings.in/en/tags/plugins/</link><description>Recent content in Plugins on Trevor's Blog</description><generator>Hugo</generator><language>en-US</language><lastBuildDate>Tue, 03 Nov 2026 19:40:00 +0800</lastBuildDate><atom:link href="http://mings.in/en/tags/plugins/index.xml" rel="self" type="application/rss+xml"/><item><title>I Had dsh Write Its Own Plugin. It Took Nine Minutes.</title><link>http://mings.in/en/posts/dsh-creator/</link><pubDate>Tue, 03 Nov 2026 19:40:00 +0800</pubDate><guid>http://mings.in/en/posts/dsh-creator/</guid><description>I asked dsh&amp;rsquo;s Creator mode for a plugin. It inspected the runtime, read source, wrote the package, requested install approval, and nine minutes later the tool was sitting in the plugin manager. A fresh session called it in ten seconds.</description><content:encoded><![CDATA[<p>The news item going around today is that DeepSeek shipped Creator Mode, described in English coverage as something mystical where you build plugins by chatting.</p>
<p>I run dsh daily, on 0.2.2-alpha.2 released October 28. My first instinct was not to paraphrase it. It was to try it.</p>
<p>So I opened a Creator mode session in my own web profile with a one-line request: write the smallest possible plugin, register a tool called <code>profile_ping</code>, and have it return the current profile name, the dsh version, and which plugins are installed in that profile.</p>
<p>Nine minutes later the tool was sitting in the plugin manager. I opened a new session, called it, and had the answer in ten seconds.</p>
<h2 id="dsh-has-four-modes">dsh has four modes</h2>
<p>Open the web UI and there is a mode selector above the input box. It expands to four:</p>
<ul>
<li>Standard: writes code, looks things up, runs commands, good for most tasks</li>
<li>PTC: everything Standard does, plus batches of tool calls you then filter, dedupe, count, or summarize</li>
<li>Minimal: terminal and image reading only, for testing baseline capability</li>
<li>Creator: in the official wording, customize dsh through conversation, letting the agent write plugins that add features or UI, or combine tools and prompts into a mode of your own</li>
</ul>
<p>I picked the fourth.</p>
<p><img alt="Four modes, and the fourth is the one that rewrites dsh itself" loading="lazy" src="/images/dsh-creator/fig1.png"></p>
<h2 id="it-did-not-start-by-writing-code">It did not start by writing code</h2>
<p>That was the most surprising part.</p>
<p>After receiving the request it loaded a skill package called <code>cordis-plugin-development</code>, then started inspecting the runtime.</p>
<p>The Trajectory panel on the right shows the sequence. It ran inspect providers to see what services exist, then query runtime to ask for the exact method signatures on <code>Service.listService</code>, and worked through the contracts for <code>pluginManager</code>, <code>profileContext</code>, and <code>defineTool</code>.</p>
<p>It even went and read the source of <code>@deepseek-ai/dsh-app-boot</code> to decide whether a plugin could import that package. It needed the dsh version, and the version only exists in the install directory&rsquo;s <code>package.json</code>.</p>
<p>That reasoning step is worth more than the result. It first established that a profile-installed plugin cannot resolve packages from the install directory, then switched to the <code>profileContext</code> the runtime already exposes, reading the version itself from the <code>installAnchor</code> path.</p>
<h2 id="what-it-produced">What it produced</h2>
<p>The output is a standard bundle package in the session workspace: manifest, patch, entry point, two locale files, an icon, and a README.</p>
<p>The line in <code>package.json</code>, <code>&quot;dsh&quot;: { &quot;bundle&quot;: { &quot;patch&quot;: &quot;./cordis.patch.yml&quot; } }</code>, is how it introduces itself to dsh. The patch file is three lines, inserting a row with the id <code>profile-ping</code>.</p>
<p><code>index.js</code> is 6.9 KB: it registers the tool, defines the parameter and output schemas, and writes a render function. The Chinese display name, &ldquo;Profile 探针&rdquo; (profile probe), it picked on its own.</p>
<p><img alt="It did not generate a file, it generated a package dsh can install" loading="lazy" src="/images/dsh-creator/fig3.png"></p>
<h2 id="i-rejected-it-once">I rejected it once</h2>
<p>The install step calls <code>plugin_manager</code> with <code>install_bundle</code>, which raises an approval dialog stating that this writes changes into the profile and that installed Host code runs outside the sandbox.</p>
<p>I clicked Allow once.</p>
<p>Later it wanted to copy the packed tgz out of <code>/tmp</code> into Documents for safekeeping, and asked for approval again. I clicked Reject, because this was a temporary artifact I planned to clean up.</p>
<p>Its reaction is worth recording: it stopped, and said explicitly that the user declined the escalation, so it should not work around it and would just explain.</p>
<p>That rule is written in its skill package, not improvised by the model. The text: do not hand-write the profile&rsquo;s <code>package.json</code>, do not run pnpm in the profile directory, <code>install_bundle</code> does those things.</p>
<p><img alt="The eighty-three steps it walked on its own" loading="lazy" src="/images/dsh-creator/fig2.png"></p>
<h2 id="it-works">It works</h2>
<p>In a new session I said: call <code>profile_ping</code> directly and paste the raw output.</p>
<p>Ten seconds later it came back with <code>Profile: web</code>, <code>dsh version: 0.2.2-alpha.2</code>, <code>Plugins (209):</code>, followed by 209 package names. The one at the end of that list is the tool it just wrote.</p>
<p>The plugin manager&rsquo;s installed list also gained a row: Profile ping, with icon, description, and its toggle switched on.</p>
<p><img alt="Top: the call result. Bottom: the same plugin in the plugin manager" loading="lazy" src="/images/dsh-creator/fig4.png"></p>
<h2 id="the-rules-are-written-into-the-skill-package">The rules are written into the skill package</h2>
<p>I only read the skill document it loaded after the run, and then I understood why it moved the way it did. The document writes that the workflow has five steps, and the first is &ldquo;deliver a working version first.&rdquo;</p>
<p>The opening of the doc says: ship something that runs, then talk about how it looks. If a visual request names no destination, the default is the current web UI. Do not build a preview page, do not make several design variants, do not write screenshot scripts.</p>
<p>Then three prohibitions: do not hand-write the profile&rsquo;s <code>package.json</code>, do not run pnpm in the profile directory, do not create packages under <code>$DSH_HOME</code>. Those all go through <code>install_bundle</code>, and a hand write costs an approval each time.</p>
<p>There is also a verification rule: after install, read <code>application</code> and <code>warnings</code> from the returned result, not logs, not the process list, not whether the page came up.</p>
<p>I did not interfere with a single technical decision. The only thing I did was two approval clicks, one yes and one no. It walked the whole thing inside that rulebook.</p>
<h2 id="three-rough-edges-i-have-to-mention">Three rough edges I have to mention</h2>
<p>First, the workspace directory it created has a literal backslash in its name. On macOS dsh built the path as <code>deepseek-harness\creator-lab</code>, and that backslash is a real character, not an escape. ESM cannot load such a path, so the plugin simply could not be imported. It found this itself and worked around it by packing a tgz and installing from that.</p>
<p>Second, in the same turn the tool could not be called. The tool list is fixed when a turn begins, so a freshly registered tool only appears next turn. It spun in the Trajectory several times, writing in its own reasoning that it could probably only emit tools from the static list. I had to open a new session to verify it.</p>
<p>Third, the install record says <code>file:/tmp/dsh-plugin-build/xxx.tgz</code>. Clear the temp directory and this plugin never installs again. That is the problem it wanted to fix, and that is the request I rejected. For anything long-lived, the package belongs in a workspace and should be installed by pointing <code>install_bundle</code> at the directory.</p>
<h2 id="what-it-can-build-and-what-it-cannot">What it can build, and what it cannot</h2>
<p>It can build tool plugins, which add a capability to the agent&rsquo;s toolbox, like my <code>profile_ping</code>; configuration plugins, which change the behavior of some host service; and UI plugins, which mount into a slot in the web UI. The skill package ships templates and reference docs for all three.</p>
<p>It cannot build plugins that need a new host service. Its own documentation says this plugin is not a kernel: no new services, no new session event types, no new sandbox backends. Cross-profile changes are also out, one profile at a time.</p>
<p>The more practical limit is tokens. Nine minutes of work burned 8.9 million tokens, and a 99% cache hit rate is what kept the cost bearable. If you want it doing serious work, decide first whether it is worth that.</p>
<h2 id="my-take">My take</h2>
<p>&ldquo;Build plugins by chatting&rdquo; makes it sound like a generator: you describe, it emits code.</p>
<p>It is not that. It behaves like an engineer who knows this framework: inspect what the runtime offers, read source to confirm the constraints, follow the skill package&rsquo;s procedure, request approval to install, and verify the result itself.</p>
<p>What is actually scarce here is the procedure. The skill package writes down &ldquo;deliver something runnable before anything pretty,&rdquo; &ldquo;an unspecified visual request lands in the current UI,&rdquo; and &ldquo;read <code>application</code>, not logs.&rdquo; Models change. This rulebook does not.</p>
<p>One boundary worth stating: 8.9 million tokens for nine minutes, 99% cache hit. This is not free magic, it is real construction work.</p>
<p>What tool of yours do you have to dig through config every time just to learn its state? That one is worth having a plugin written for it.</p>
]]></content:encoded></item></channel></rss>