Back to home@Melosic

dsh-invoke

Prompt Vault & Invoker for DeepSeek Harness — 管理、分类、快速调用提示词,支持侧边栏 GUI 与复制粘贴

Stars
1
Language
TypeScript
Created
Aug 14, 2026
Updated
Aug 26, 2026
GitHub repo

Introduction

dsh-invoke

English | 中文

Prompt Vault & Invoker for DeepSeek Harness

A DeepSeek Harness community plugin for managing and invoking prompts — summon your best prompts with one click.

dsh-invoke focuses on prompt management and invocation. It ships with one built-in example prompt as a reference template, and lets you freely add, edit, delete, view, search, and categorize your own prompts.

The plugin runs as a Host + Client two-part plugin: the Host side (Node) registers HTTP routes and DSH commands; the Client side (browser) injects a sidebar entry into Harness and mounts a React panel. The two communicate over the same-origin /api/dsh-invoke/*.

Features

  • Sidebar GUI first: add / edit / delete / view / search / category management, all visual.
  • Quick invoke (copy to clipboard): click "Copy" → fill variables → copy → paste and send. Independent of Harness's internal DOM, 100% compatible.
  • Variable substitution: Mustache-style {{var}} placeholders, filled interactively via a dialog on invoke. (Auto-extraction from the editor selection is planned — the extraction engine is ready, waiting for a selection API from the host.)
  • Category tree + live search: left-hand category filter, top search box with real-time filtering (title / description / tags / body), with matched-keyword highlighting.
  • Hover preview: hover any card or row for 250ms to read the full prompt body in a floating popup — no clicking needed, and the popup never blocks what you're about to click.
  • Compact / comfortable view toggle: switch between the card grid and a dense single-line list from the search bar; the preference is remembered per browser.
  • Light / dark theme: follows Harness's data-ds-dark-theme mechanism automatically, with in-panel manual override.
  • Two-layer storage merge: user-level global storage + project-level storage, project-level wins.
  • Import / export: batch JSON / YAML import (merge / overwrite modes), export backup.
  • Full command-line support: /prompt, /prompt-list, /alias commands, plus /<alias> [content] quick invocation.
  • Alias quick invocation: bind an alias to a prompt, then /<alias> content renders and copies it in one step (variables auto-filled; conflict detection and cascade delete included).
  • Usage stats & smart sorting: ranked by a composite score of usage frequency and recency.

Requirements

  • Node.js >= 22.19 (follows the DeepSeek Harness engine requirement)
  • DeepSeek Harness >= 0.1.0, < 0.2.0

Installation

The plugin mounts as a Cordis plugin. Give cordis.patch.yml to Harness's Cordis loader, or merge its content into your patch config:

- insert:
    - id: dsh-invoke
      name: '@dsh-external/dsh-invoke'
      config:
        enabled: true

Install dependencies:

npm install @dsh-external/dsh-invoke
# or
pnpm add @dsh-external/dsh-invoke

Local Development / From Source

If you want to develop the plugin locally or run it without publishing to npm:

  1. Install DeepSeek Harness globally:

    npm install -g @deepseek-ai/dsh
    
  2. Clone the repository and install dependencies:

    git clone https://github.com/Melosic/dsh-invoke.git
    cd dsh-invoke
    npm install
    
  3. Build the plugin:

    npm run build          # Host build (tsc -p tsconfig.json)
    npm run build:client   # Client build (tsdown / esbuild)
    
  4. Create a DSH profile (skip if you already have one):

    dsh --profile web --help   # creates ~/.dsh/profiles/web/ on first run
    
  5. Link the plugin into the profile: Edit ~/.dsh/profiles/web/package.json and add @dsh-external/dsh-invoke to dependencies:

    "dependencies": {
      "@dsh-external/dsh-invoke": "link:/absolute/path/to/dsh-invoke"
    }
    

    Then install the profile dependencies:

    dsh plugin --profile web install
    
  6. Add the plugin mount entry to ~/.dsh/profiles/web/cordis.patch.yml:

    - insert:
        - id: dsh-invoke
          name: dsh-invoke
          config:
            enabled: true
    
  7. Start Harness with the plugin:

    dsh --profile web --port 8080
    

Open http://127.0.0.1:8080/ in your browser. The Prompt Vault entry button should appear in the sidebar automatically, directly above the Settings button.

Quick Start

  1. Start Harness; the "Prompt Vault" entry button is injected into the sidebar automatically, directly above the Settings button.
  2. Click the entry to open the panel. Browse prompts by clicking a category, or use the search box to locate one quickly.
  3. Use a prompt: click "Copy" on a card → fill variables → click "Copy to clipboard" → paste it into the input and send.
  4. Manage prompts: click "Add" to create a custom prompt, or use "Edit" / "Delete" on cards.

Built-in Example Prompt

The plugin ships with one example prompt, usable directly or as a template:

FieldValue
IDcode-review
Title代码审查 (Code Review)
Description审查代码中的潜在问题,包括逻辑错误、安全漏洞、性能问题
Category开发 (Development)
Tagsreview quality security
Body请审查以下代码,重点关注:1. 逻辑错误 2. 安全漏洞 3. 性能问题(正文以 {{code}} 引用代码)
Variablecode (text input, required)

Command-Line Usage (Optional)

Most operations can be done via the sidebar; the command line targets keyboard-driven users and fallback scenarios. Current commands:

CommandDescription
/promptList all prompts (with category, built-in marker, description)
/prompt-listList all prompts grouped by category
/aliasList all registered aliases and the prompts they point to
/<alias> [content]Invoke the aliased prompt: renders it and copies to the clipboard

Alias System

  • Open the alias dialog via the link-icon action on a prompt card (or by clicking the alias badge on the card). Each prompt can be bound to one alias.
  • Alias rules: lowercase letters / digits / hyphens; must not collide with reserved commands (prompt, prompt-list, alias, help, clear, exit) or existing aliases. Validated server-side.
  • Invocation: /<alias> content — the text after the command fills the template variables. A single-variable prompt receives the whole text; a multi-variable prompt splits it in declaration order using ||. Missing required variables produce a usage hint.
  • On success the rendered prompt is copied to the system clipboard (when the clipboard is unavailable, the body is echoed for manual copy) and the usage counter increments.
  • Deleting a prompt cascades to delete its alias.
  • Alias data lives in the user-level aliases.json (global, not workspace-scoped).

Data Storage

  • User-level (writable): ~/.dsh/prompts.user.json (resolved via @deepseek-ai/dsh-home-paths)
  • Project-level (writable, higher priority): .harness/prompts.json
  • Aliases: user-level aliases.json (global)

Note: how the project root is resolved. In command invocations (/prompt, /prompt-list, /<alias>), project-level storage follows the invoking session's real working directory (agent.session.header.cwd). Over HTTP (/api/dsh-invoke/*), callers may pass an explicit ?cwd= (or cwd in the JSON body); when omitted it falls back to the Host process working directory captured once at plugin load. GET /api/dsh-invoke/workspace reports the resolved root, the project storage path, and whether the directory is a registered dsh workspace. Imports (merge/overwrite) follow the same write-layer policy as creating prompts: project-level when a workspace exists, otherwise user-level.

Prompt IDs: the sidebar UI generates UUIDs (crypto.randomUUID) when creating prompts. The HTTP API does not generate ids — direct POST /api/dsh-invoke/prompts calls must supply a unique id (400 otherwise).

Merge Strategy

Project-level config takes priority over user-level; for a duplicate ID, the project-level prompt wins. When no workspace is open, only user-level storage is loaded.

Example storage format:

{
  "version": 1,
  "categories": ["开发", "测试", "文档", "效率"],
  "customCategories": ["AI辅助"],
  "prompts": [
    {
      "id": "code-review",
      "title": "代码审查",
      "description": "审查代码中的潜在问题",
      "category": "开发",
      "tags": ["review", "quality"],
      "body": "请审查以下代码:\n{{code}}",
      "variables": [{ "name": "code", "type": "text", "required": true }],
      "builtin": true,
      "usageCount": 0,
      "createdAt": "2026-01-15T10:00:00Z",
      "updatedAt": "2026-08-13T14:30:00Z"
    }
  ]
}

Technical Architecture

The plugin uses a Host + Client two-part structure, following DeepSeek Harness community plugin conventions:

             DeepSeek Harness
        ┌────────────────────┐
        │  Host side (Node)  │
        │  src/index.ts       │
        │   ├─ host/routes.ts │◄── HTTP /api/dsh-invoke/*
        │   ├─ commands/*     │◄── DSH commands /prompt /alias
        │   ├─ storage/*      │── two-layer storage merge
        │   └─ engine/*       │── templates / import-export
        └────────┬───────────┘
                 │ same-origin fetch
        ┌────────┴───────────┐
        │  Client (browser)  │
        │  src/client/index.ts│── sidebar injection + panel mount
        │  src/client/api.ts │── fetch wrapper
        │  src/ui/*          │── React panel (light/dark)
        └────────────────────┘

Security Model

All HTTP routes (/api/dsh-invoke/*) pass through three request guards (see src/host/routes.ts):

GuardScopeProtects against
Host allowlist (local/LAN addresses only)All requestsDNS rebinding (attacker domain re-resolving to loopback)
Same-origin check (Sec-Fetch-Site + Origin vs Host)Write operationsCSRF (malicious cross-site POST/PUT/DELETE)
cwd allowlistRequests with explicit ?cwd=Arbitrary directory writes

The cwd allowlist has three tiers by priority: registered dsh workspace when the registry is available (strongest); subtree of the initialized workspace when the registry is unavailable; any existing directory as a documented degradation when neither anchor exists (the HTTP surface remains covered by the first two guards). Request bodies are capped at 10MB, and storage writes use atomic replace with a .bak backup.

Source Layout

dsh-invoke/
├── package.json
├── cordis.patch.yml        # plugin mount patch
├── tsconfig.json           # Host build
├── tsconfig.client.json    # Client build
├── src/
│   ├── index.ts            # Host plugin entry
│   ├── host/
│   │   └── routes.ts       # HTTP route layer (CRUD API)
│   ├── client/
│   │   ├── index.ts        # Browser entry (sidebar injection + mount)
│   │   └── api.ts          # fetch API wrapper
│   ├── storage/
│   │   ├── context.ts      # storage context (workspace/path config)
│   │   ├── manager.ts      # two-layer merge + CRUD + smart sorting
│   │   └── alias-store.ts  # alias storage (CRUD + conflict detection + cascade delete)
│   ├── engine/
│   │   ├── template.ts     # variable substitution ({{var}})
│   │   ├── variable-resolver.ts  # variable extraction (experimental)
│   │   └── import-export.ts  # JSON / YAML import-export
│   ├── commands/
│   │   ├── prompt.ts       # main command registration
│   │   ├── alias.ts        # /alias listing + dynamic /<alias> command registration & invocation
│   │   └── clipboard.ts    # cross-platform clipboard copy (Node child_process)
│   └── ui/
│       ├── theme.ts        # theme adaptation (light/dark)
│       ├── icons.tsx       # Feather Icons inline SVG
│       ├── styles.ts       # design system (CSS variables)
│       ├── WebviewPanel.tsx  # main panel (React 18)
│       └── components/     # cards, forms, category tree, variable/import dialogs
└── tests/                  # Jest unit tests

Development

npm install
npm run build          # Host build (tsc -p tsconfig.json)
npm run build:client   # Client build (tsc -p tsconfig.client.json)
npm run test           # run Jest unit tests

Roadmap

PriorityModuleStatus
P0Core CRUD (add/edit/delete/query)Done
P0Copy to clipboard + variable fillDone
P0Category tree management & live searchDone
P0Light/dark theme auto-adaptationDone
P1Import / exportDone (JSON / YAML)
P12-column grid card layoutDone
P1Variable substitution ({{var}})Done
P1Sidebar GUI (Host + Client)Done
P1Alias system (with conflict detection)Done
P2Project-level auto-load & two-layer mergeDone
P2Usage stats & smart sortingDone
P2AI-assisted prompt generation (experimental)Planned

FAQ

Q: After copying to the clipboard, can it auto-paste into the input box?

A: The current version uses manual paste for stability. Once Harness officially exposes an input-write API, we will support it right away.

Q: Can the built-in example prompt be deleted?

A: Yes. The example prompt supports edit and delete just like user-defined prompts.

Q: If both project-level and user-level exist, which wins?

A: Project-level takes priority; for a duplicate ID, the project-level config wins.

Q: How do I use auto variable extraction? Why does it sometimes not work?

A: Auto-extraction is planned and not yet wired up: the extraction engine is implemented, but the current Harness web client exposes no editor-selection API, so the dialog always asks for manual input. It will light up automatically once the host provides selection access.

Q: Do I have to use the command line?

A: No. All operations can be done through the sidebar GUI; the command line is an optional fallback for keyboard-driven users and degraded scenarios.

Version Compatibility

The v0.2.x series is compatible with DeepSeek Harness >=0.1.0 <0.2.0. When Harness ships a major update, we will adapt promptly — follow the GitHub Releases page.

Contributing

Issues and PRs are welcome:

  1. Fork this repository
  2. Create a feature branch: git checkout -b feature/amazing-feature
  3. Commit your changes: git commit -m 'feat: add amazing feature' (follow Conventional Commits)
  4. Push the branch: git push origin feature/amazing-feature
  5. Open a Pull Request

License

MIT License © 2026