nas1112/self-evolve: Automatically review after task completion, writing experience into memory and skills

Introduction

Running a long task in DSH often involves dozens of tool calls, where the direction has long since drifted, yet every step looks “reasonable” in isolation. Changing the writing style of the same shell command repeatedly fails, without changing the approach. Hard-earned multi-step processes have to be started from scratch for similar tasks next time. self-evolve (maintainer NAS1112, community directory classification: Memory) is built for this: it reviews the task only after it is fully complete, writes truly worth-remembering experience into local memory or reusable skills, and the desktop pet in the bottom right corner announces this evolution. It doesn’t write a summary after every conversation ends, nor does it interrupt mid-stream.

What is this

One-sentence positioning: DSH plugin, Host half + Client half. The Host half is responsible for triggering judgment and persistence when the task ends, while the Client half is responsible for the desktop pet animation and card in the bottom right corner of the session page.

Its three hard constraints are clearly written in the README:

  • No interruption mid-way: Review happens only when the task ends, not a single extra word in the conversation flow.
  • Write by default (No): Only experience that will “make your future self do things differently” is persisted, max one per turn.
  • Data is all local: Memory goes into AGENTS.md, skills go into ~/.agents/skills/, your handwritten content is never overwritten.

Maintainer is NAS1112. The version in package.json in the README is 0.5.2, but the zip in INSTALL.md is self-evolve-0.3.0.zip. When installing, rely on the actual downloaded directory.

Trigger Conditions: Length is not equal to Value

Based on DESIGN.md. The core judgment is “a normal development task can easily run over 15 steps”. If triggered by step count, the result is a review every round, filling memory with noise. Therefore, “how many steps ran” does not participate in triggering by default. Only three scenarios are evaluated once at the end of the task:

Signal Default Threshold
User explicitly asks to remember Trigger on hit (remember / jot it down / from now on / I’m used to / I prefer / remember that / from now on)
A problem actually occurred Any tool fails 2 times; bash / shell peak consecutive failure reaches 3 times
A complex workflow succeeded 25 steps and 4 distinct tools

A few details:

  1. Failure criteria covers isError, [exit code: N] (N≠0), [timed out after ...], [killed by signal: ...]; failure of edit due to file changes also counts.
  2. Link counting is reset when the user sends a new message; thresholds measure “this round”; the same turn uses the turn number as an idempotent key, triggering only once.
  3. When user says “Remember X”, preference types directly point to scope: "global", while repository conventions remain at project level.
  4. To restore triggering by step count, set triggerOnLengthOnly to true; triggers at full chainSteps as well.
  5. Turns ending after ask_user_question or exit_plan_mode are stopping to wait for the user mid-process; the plugin skips triggering and waits for the turn where the task truly finishes to evolve.

Trigger signals themselves produce no messages. Every step is evaluated in agent/pre-step; if conditions are met, mark that agent as “ready this round”. The protocol body is written into the system prompt via ctx.systemPrompt.section(), and the ready status is written into the dynamically refreshed context via ctx.systemPrompt.context()——only the model can see it, no extra text or messages appear on the user interface. DESIGN.md also notes the origin: earlier used agent.steer() to append a specific review step at turn end; that step’s reply became the “last assistant message of this round”, pushing the real conclusion into the collapsed “Called Tools” group, effectively hiding the answer.

What to Write, How Much

The protocol tone is default to writing nothing, and explicitly lists what not to record: what the task achieved, the original error text, another way of saying what is already in memory. If there is no qualified experience, end the round directly, without explaining the protocol to the user or marking it.

“Max one per turn” is written not just in the protocol but is also hard-limited on the tool side (maxRecordsPerTurn, default 1); excess calls return skipped directly without persisting.

Deduplication and merging are handed to the judgment model (optional). When a key is provided, besides judging if it’s worth writing, it also answers merge_v1 (is this repeating an existing entry), relation_v1 (is this updating an entry, can cross-level hit and replace), stale_v1 (which existing entry’s premise no longer holds after this write, mark “Premise Expired”). When judging, the title + first 160 chars of body + level of existing entries are passed to the model (max 12), so changing wording won’t stack another one. When no key is provided, judgment degrades to direct write (fail-open), and the plugin works normally.

Where Data Lands

Content Path
Project Memory <workspace>/AGENTS.md (within the <!-- self-evolve:begin --> managed section)
Global Memory $DSH_HOME/AGENTS.md
Skills written by this plugin ~/.agents/skills/<name>/SKILL.md
Long experience details $DSH_HOME/self-evolve/details/
Judgment log $DSH_HOME/self-evolve/judgements.jsonl
Last precipitation $DSH_HOME/self-evolve/last-record.json

The plugin only modifies the managed section it owns, not your handwritten content; your own written SKILL.md (without ownership markers) won’t be taken over. When self-built skills are written, they carry a line <!-- self-evolve:skill -->, and old files fall back to identification via the managed title in the body—this is also the “cumulative self-built skills” stance, other skills you wrote in the directory do not participate in the count.

Installation and Enablement

There is no official command like dsh plugin add; it uses the link: method attached to a profile. Taking $DSH_HOME/profiles/desktop as an example, other profiles just change the path.

  1. Unzip the plugin directory to a fixed location:
unzip self-evolve-0.3.0.zip -d ~/plugins
# Result: ~/plugins/self-evolve/

The directory should contain index.js (Host), client.js (Client), package.json, cordis.patch.yml, assets/pet/*.gif, locale/.

  1. Edit profiles/desktop/package.json, add @local/self-evolve with link: to dependencies, and put it in dsh.profile.bundles (This line is mandatory, or the plugin won’t load with the profile):
{
  "dependencies": {
    "@local/self-evolve": "link:/Users/you/plugins/self-evolve"
  },
  "dsh": {
    "profile": {
      "bundles": [
        "@local/self-evolve"
      ]
    }
  }
}
  1. Install dependencies or create symlink directly:
cd "$DSH_HOME/profiles/desktop"
pnpm install          # Use this if you have pnpm
# If no pnpm, create the link directly:
ln -sfn ~/plugins/self-evolve node_modules/@local/self-evolve
  1. Reload: restart dsh web, then refresh the page.

Optional configuration. Override section in profiles/desktop/cordis.patch.yml:

- id: self-evolve
  name: "@local/self-evolve"
  config:
    shellFailureThreshold: 3
    toolFailureThreshold: 2
    triggerOnLengthOnly: false
    chainSteps: 15
    scope: auto
    workflowMinSteps: 25
    workflowMinDistinctTools: 4
    maxRecordsPerTurn: 1
    decision:
      enabled: true
      shadow: true
      model: doubao-seed-2-1-lite-260915
      baseURL: https://ark.cn-beijing.volces.com/api/v1
      apiKey: ''            # Leave empty = use key configured in UI, or read env var
      apiKeyEnv: A_API_KEY
      timeoutMs: 900
      worthThreshold: 0.5
      scopeMinConfidence: 0.6
  disabled: false

It works without this section, using built-in defaults. The judgment model (Volcano Ark System One) is not strictly required: if no key is configured, judgment returns degraded, and the plugin writes normally. To enable judgment, fill in the key in the Experience Library ⚙ page, or write it into apiKey, or put it in an environment variable.

  1. Verify:
Settings → Plugins: self-evolve appears in the list
Desktop pet appears in the bottom right corner of the page
Click desktop pet → opens Experience Library, all four pages (Memory / Skills / Judgment Log / Settings) are accessible

Probe the route via command line:

curl -s -o /dev/null -w '%{http_code}\n' \
  -H 'x-self-evolve: 1' \
  "http://127.0.0.1:62100/api/self-evolve/pet/idle.gif"
# Expect 200
  1. Regression test (optional, plugin needs to be running):
cd self-evolve/test
pnpm install
pnpm test

test-routes.mjs defaults to http://127.0.0.1:62100; change the port using SELF_EVOLVE_BASE=http://127.0.0.1:<port>.

Daily Usage

After installation, nothing is needed. Usually it is completely invisible, only popping up a small card at the bottom right when the task ends:

  1. Click desktop pet to open Experience Library: view memory/skills, apply or discard skill proposals, edit/delete in place, use “Soup of Forgetfulness” to batch clear.
  2. Telling the model “Remember…” triggers a recording immediately, no need to wait for failure or long chains.
  3. Model side provides three tools: self_evolve_record, self_evolve_propose, self_evolve_status. self_evolve_propose target relaxed from “only self-built skills” to any skill under the skills directory, including your own; the model can only submit proposals, persisting requires a person to click “Apply” in the Experience Library, and rewriting cannot secretly add ownership markers.

A few things to know about the interface shape: The desktop pet in the bottom right corner is always present; a purple badge in the top right shows cumulative evolution count for this session; starting from the 2nd evolution in the same session, it only appears as a small card automatically, not interrupting reading; clicking the small card upgrades to a full-screen ledger. The dock card has three stages—Burst (precipitating) → Flash (completion moment) → Settled (written); the ledger shows the trigger reason, judgment value, write target, and history for this session. The 1st evolution won’t interrupt a streaming output answer with a full-screen card—the answer would be covered—animation timing is controlled by the running gate, nothing is displayed while this round is still running.

Suitable Scenarios and Notes

Suitable for this approach: review only after task completion, default write nothing only write useful stuff, experience falls in local repo or home directory not cloud, want a non-interrupting reminder form. If you prefer manual summary every time, or collect experience in a centralized service, this might not fit.

A few boundaries that must be known:

  • Run with current dsh process permissions. The plugin can read/write the workspace and home directory; before installing, check source code and license (README’s package.json is marked MIT) to decide whether to attach it to the profile.
  • Global write relies on the approval channel. If the current combination has no available approval answerer, boundary-crossing writes will fail closed and return FS_SANDBOX_DENIED; at this point, you can only switch scope back to workspace, or explicitly set the file strategy to danger-full-access.
  • “New workflow” is a heuristic judgment (steps + tool types), no semantic understanding. A long chain that is indeed repetitive labor for the same thing might be judged as a workflow; the prompt text already requires the model to skip and explain during “one-time processes”, so the cost of a misjudgment is a harmless self-explanation.
  • Counting is stored in memory (WeakMap), starting from zero after process restart or session switch. This is an indicator of “whether this round went off track”, and does not need to be persisted by design.
  • Review happens at agent/turn-stopping; if the user cancels this round mid-process, this round won’t be reviewed—consistent with the semantics of “evolution only after task completion”, but work that was cancelled won’t accumulate experience.
  • If a skill is written wrongly (invalid frontmatter), it will be ignored by dsh-skill-filesystem and warned in logs; this plugin only appends valid content, doesn’t modify existing frontmatter.
  • When installed via link:, modifying index.js might not hot-reload immediately. Call self_evolve_status before verifying to confirm the environment field exists—lack of this field means old code is running, need to turn the plugin off and on in the plugin page.
  • Client half (animation) loading timing differs from Host half: changes to dsh.client require reinstalling the bundle and refreshing the page to take effect. Just reloading the plugin is not enough for new registered slots to appear.

Trigger thresholds, implementation details of evolution animations, memory levels and deduplication, Experience Library, self-optimization proposals, judgment model, full configuration table, design trade-offs, and verification methods are all in DESIGN.md in the repository.

Conclusion

self-evolve does a narrow thing: review once after the task ends, write one truly useful experience into local memory or skill, then tell you via the desktop pet what this evolution is. It doesn’t rewrite the conversation flow, doesn’t overwrite your handwritten content, and doesn’t send experience elsewhere. If this is exactly what you lack, start here:

  • Directory page: https://www.skillhub.cn/plugins/NAS1112/self-evolve
  • GitHub: https://github.com/NAS1112/self-evolve
羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

Xiaoye