@javierfrancoleal/plugin-authoring 0.0.0-stage → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +22 -2
- package/assets/plugin-authoring.md +176 -0
- package/cordis.patch.yml +4 -0
- package/icon.svg +8 -0
- package/lib/index.js +53 -0
- package/locale/en.json +6 -0
- package/locale/es.json +6 -0
- package/locale/fr.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +44 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 J.Franco
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,23 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @javierfrancoleal/plugin-authoring
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Skill de **autoría de plugins** para DeepSeek Harness. Registra la skill
|
|
4
|
+
`plugin-authoring`, cuyo cuerpo (`assets/plugin-authoring.md`) contiene la guía
|
|
5
|
+
destilada para crear y publicar plugins sin redescubrir la arquitectura.
|
|
6
|
+
|
|
7
|
+
La skill cubre: los tres tipos de plugin (host / cliente / doble cara), la anatomía
|
|
8
|
+
de `package.json` + `cordis.patch.yml`, el half host (skills/comandos/efectos), el
|
|
9
|
+
half browser (`dsh.client` + `window.__ModuleLoader__` + slots), locale, tokens de
|
|
10
|
+
tema, instalación, gotchas y checklist de validación.
|
|
11
|
+
|
|
12
|
+
## Uso
|
|
13
|
+
|
|
14
|
+
- **Agentes**: cargar la skill `plugin-authoring` antes de crear o extender un plugin.
|
|
15
|
+
- **Humanos**: leer `docs/PLUGIN-AUTHORING.md` (fuente canónica), copiar la plantilla
|
|
16
|
+
`templates/dsh-plugin/`, y usar `tools/new-plugin.sh` + `tools/check-plugin.sh`.
|
|
17
|
+
|
|
18
|
+
## Instalación
|
|
19
|
+
|
|
20
|
+
Como cualquier bundle: añadir `@javierfrancoleal/plugin-authoring` a
|
|
21
|
+
`dsh.profile.bundles` (y su dependencia `link:`), `pnpm install`, y reiniciar la app.
|
|
22
|
+
|
|
23
|
+
Creado por **J.Franco**.
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# How to author a DeepSeek Harness plugin (distilled)
|
|
2
|
+
|
|
3
|
+
DSH is a **Cordis** runtime. A plugin is an npm package with one or two halves:
|
|
4
|
+
|
|
5
|
+
- **Host half** `lib/index.js` (Node): skills, commands, services, scheduled work.
|
|
6
|
+
- **Browser half** `lib/client.js` (React): UI in the Web GUI.
|
|
7
|
+
|
|
8
|
+
Activation is via `cordis.patch.yml`, which inserts the plugin's row into the Loader.
|
|
9
|
+
|
|
10
|
+
## Three kinds
|
|
11
|
+
|
|
12
|
+
| Kind | `dsh.bundle.patch` | `dsh.client` |
|
|
13
|
+
|---|---|---|
|
|
14
|
+
| Host-only (skill / command) | yes | no |
|
|
15
|
+
| Client-only (UI) | no | yes |
|
|
16
|
+
| Dual-face | yes | yes |
|
|
17
|
+
|
|
18
|
+
## Files
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
mi-plugin/
|
|
22
|
+
├── package.json # exports + dsh.bundle.patch + dsh.client
|
|
23
|
+
├── cordis.patch.yml # - insert: - id: mi-plugin name: '@scope/mi-plugin'
|
|
24
|
+
├── lib/index.js # host half — export apply(ctx)
|
|
25
|
+
├── lib/client.js # browser half — window.__ModuleLoader__.load(...)
|
|
26
|
+
├── locale/*.json # meta.title / meta.description
|
|
27
|
+
├── icon.svg
|
|
28
|
+
└── README.md
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## package.json (dual-face minimum)
|
|
32
|
+
|
|
33
|
+
```jsonc
|
|
34
|
+
{
|
|
35
|
+
"name": "@javierfrancoleal/mi-plugin",
|
|
36
|
+
"version": "1.0.0",
|
|
37
|
+
"type": "module",
|
|
38
|
+
"main": "lib/index.js",
|
|
39
|
+
"exports": {
|
|
40
|
+
".": "./lib/index.js",
|
|
41
|
+
"./client": "./lib/client.js",
|
|
42
|
+
"./cordis.patch.yml": "./cordis.patch.yml",
|
|
43
|
+
"./package.json": "./package.json",
|
|
44
|
+
"./locale/*.json": "./locale/*.json"
|
|
45
|
+
},
|
|
46
|
+
"engines": { "node": ">=24", "dsh": ">=0.2.1-rc.0" },
|
|
47
|
+
"dsh": {
|
|
48
|
+
"manifestVersion": 1,
|
|
49
|
+
"bundle": { "patch": "./cordis.patch.yml" },
|
|
50
|
+
"client": {
|
|
51
|
+
"platform": "web",
|
|
52
|
+
"inject": ["@deepseek-ai/dsh-client-locale", "@deepseek-ai/dsh-client-ui-layout"]
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"peerDependencies": { "@deepseek-ai/cordis": "~4.0.5" }
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Host half — register a skill (proven pattern)
|
|
60
|
+
|
|
61
|
+
```js
|
|
62
|
+
import { readFile } from "node:fs/promises";
|
|
63
|
+
import { fileURLToPath } from "node:url";
|
|
64
|
+
import { BUNDLED_SKILL_RANK } from "@deepseek-ai/dsh-skill";
|
|
65
|
+
|
|
66
|
+
const PROVIDER_NAME = "javierfrancoleal-mi-plugin";
|
|
67
|
+
const SKILL_BODY_URL = new URL("../assets/mi-skill.md", import.meta.url);
|
|
68
|
+
const RESOURCE_BASE = { kind: "directory", path: fileURLToPath(new URL("../assets/", import.meta.url)) };
|
|
69
|
+
const CANDIDATE = {
|
|
70
|
+
name: "mi-skill",
|
|
71
|
+
description: "…",
|
|
72
|
+
invocation: { modelInvocable: true, userInvocable: true },
|
|
73
|
+
provider: PROVIDER_NAME, source: "bundled",
|
|
74
|
+
resourceBase: RESOURCE_BASE, rank: BUNDLED_SKILL_RANK, locator: SKILL_BODY_URL,
|
|
75
|
+
};
|
|
76
|
+
const provider = {
|
|
77
|
+
name: PROVIDER_NAME,
|
|
78
|
+
list: () => Promise.resolve([CANDIDATE]),
|
|
79
|
+
async get() { return { ...CANDIDATE, content: await readFile(SKILL_BODY_URL, "utf8") }; },
|
|
80
|
+
};
|
|
81
|
+
const name = "mi-plugin";
|
|
82
|
+
const inject = ["skills"];
|
|
83
|
+
function apply(ctx) { ctx.skills.registerProvider(() => provider); }
|
|
84
|
+
export { apply, inject, name };
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Commands: `ctx.get("commands")?.register({ definitionId, name, description, input, handler })`.
|
|
88
|
+
Timers: `ctx.effect(() => { const t = setTimeout(run, ms); return () => clearTimeout(t); }, "label")`.
|
|
89
|
+
Log: `ctx.logger.info/warn(...)`.
|
|
90
|
+
|
|
91
|
+
## Browser half — the recipe (the hard part)
|
|
92
|
+
|
|
93
|
+
```js
|
|
94
|
+
window.__ModuleLoader__.load({
|
|
95
|
+
id: "@javierfrancoleal/mi-plugin",
|
|
96
|
+
factory: (require) => {
|
|
97
|
+
var module = { exports: {} };
|
|
98
|
+
var exports = module.exports;
|
|
99
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
100
|
+
const React = require("react"); // react + react/jsx-runtime are baseline
|
|
101
|
+
|
|
102
|
+
function MiComponente(_props) {
|
|
103
|
+
const t = _props && typeof _props.t === "function" ? _props.t : (k) => k;
|
|
104
|
+
return React.createElement("div", null, t("saludo"));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const inject = ["slots", "locale"];
|
|
108
|
+
function apply(ctx) {
|
|
109
|
+
ctx.effect(() => ctx.locale.register("miNs", { zh, en }), "mi-plugin: locale");
|
|
110
|
+
ctx.slots.inject("shell.bottom", () => ctx.slots.register({
|
|
111
|
+
name: "shell.bottom", id: "mi-plugin", order: 0, locale: "miNs"
|
|
112
|
+
}, MiComponente));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
exports.MiComponente = MiComponente;
|
|
116
|
+
exports.apply = apply;
|
|
117
|
+
exports.inject = inject;
|
|
118
|
+
return module.exports;
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Rules:
|
|
124
|
+
- `exports["./client"]` → `./lib/client.js`.
|
|
125
|
+
- Write `lib/client.js` **already built** (this exact form); no build step for `link:` installs.
|
|
126
|
+
- No JSX — use `React.createElement` or `require("react/jsx-runtime")`.
|
|
127
|
+
- Inject CSS with a `<style data-plugin-css="…">` tag.
|
|
128
|
+
- Client `inject` lists **services** (`slots`, `locale`, `theme`, `remote`, …).
|
|
129
|
+
|
|
130
|
+
## Slots
|
|
131
|
+
|
|
132
|
+
| Slot | Alcance | Para qué |
|
|
133
|
+
|---|---|---|
|
|
134
|
+
| `shell.bottom` | root | **global full-width bottom bar** (empty by default) |
|
|
135
|
+
| `shell.overlay` / `shell.leading` | root | overlay / top-left corner |
|
|
136
|
+
| `sidebar` / `rightbar` / `main` | root | columns / central panel |
|
|
137
|
+
| `conversation.input.dock` | sesión | bar above the composer |
|
|
138
|
+
|
|
139
|
+
`ctx.slots.inject(slot, () => ctx.slots.register(opts, Component))` is deferred.
|
|
140
|
+
|
|
141
|
+
## Locale & theme
|
|
142
|
+
|
|
143
|
+
- `ctx.locale.register(ns, { zh, en })` — **both** dictionaries required.
|
|
144
|
+
- `locale: ns` in `register` → framework injects `t` as a prop.
|
|
145
|
+
- Theme tokens (light/dark aware, no hardcoded hex): `--dsw-alias-label-primary/secondary/tertiary`,
|
|
146
|
+
`--dsw-alias-state-success-primary` (green), `-warn-primary` (amber), `-error-primary` (red),
|
|
147
|
+
`--dsw-specific-menu`, `--dsw-alias-border-l1`, `--dsw-radius-md`, `--dsw-elevation-panel`,
|
|
148
|
+
`--dsw-menu-backdrop-filter`.
|
|
149
|
+
|
|
150
|
+
## Install into the running app
|
|
151
|
+
|
|
152
|
+
1. `~/.dsh/profiles/<perfil>/package.json`: add `"@scope/nombre": "link:/abs/path"` to
|
|
153
|
+
`dependencies` and `"@scope/nombre"` to `dsh.profile.bundles`.
|
|
154
|
+
2. `cd ~/.dsh/profiles/<perfil> && pnpm install`.
|
|
155
|
+
3. **Restart the app** (a new bundle needs a host recomposition).
|
|
156
|
+
|
|
157
|
+
## Gotchas
|
|
158
|
+
|
|
159
|
+
1. The checkout lives inside `app.asar` — use `_dsh-inspect/asar-tool.mjs list|extract`.
|
|
160
|
+
2. The `desktop` profile is Electron-managed: `dsh --profile desktop --dump-config` fails.
|
|
161
|
+
Validate with a throwaway profile.
|
|
162
|
+
3. New bundles are NOT picked up by HMR on external edits — restart the app.
|
|
163
|
+
4. `dsh.bundle.patch` + `dsh.client` can coexist (dual-face).
|
|
164
|
+
5. React / slots / primitives are baseline; don't list them in `dsh.client.inject`.
|
|
165
|
+
|
|
166
|
+
## Validation checklist
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
node --check lib/index.js && node --check lib/client.js
|
|
170
|
+
node tools/check-client.mjs lib/client.js
|
|
171
|
+
dsh --profile testprices --dump-config | grep mi-plugin # throwaway profile, not desktop
|
|
172
|
+
curl -sS http://127.0.0.1:<puerto>/plugins/@scope/mi-plugin/client.js # 200 after restart
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Full reference: `docs/PLUGIN-AUTHORING.md`. Template: `templates/dsh-plugin/`.
|
|
176
|
+
Scaffold/validate: `tools/new-plugin.sh`, `tools/check-plugin.sh`.
|
package/cordis.patch.yml
ADDED
package/icon.svg
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" fill="none">
|
|
2
|
+
<rect width="128" height="128" rx="24" fill="#7C5CFF"/>
|
|
3
|
+
<path d="M38 34 H90 V54 H38 Z" fill="#ffffff" opacity="0.35"/>
|
|
4
|
+
<path d="M38 62 H90 V82 H38 Z" fill="#ffffff" opacity="0.7"/>
|
|
5
|
+
<path d="M38 90 H90 V100 H38 Z" fill="#ffffff"/>
|
|
6
|
+
<circle cx="98" cy="44" r="7" fill="#ffffff"/>
|
|
7
|
+
<path d="M92 44 H104 M98 38 V50" stroke="#ffffff" stroke-width="3" stroke-linecap="round"/>
|
|
8
|
+
</svg>
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// @javierfrancoleal/plugin-authoring — host half.
|
|
2
|
+
// Registers the `plugin-authoring` skill so agents can load the distilled
|
|
3
|
+
// guide for building DeepSeek Harness plugins without re-discovering the
|
|
4
|
+
// architecture. The skill body lives in assets/plugin-authoring.md.
|
|
5
|
+
import { readFile } from "node:fs/promises";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { BUNDLED_SKILL_RANK } from "@deepseek-ai/dsh-skill";
|
|
8
|
+
|
|
9
|
+
const PROVIDER_NAME = "javierfrancoleal-plugin-authoring";
|
|
10
|
+
const SKILL_BODY_URL = new URL("../assets/plugin-authoring.md", import.meta.url);
|
|
11
|
+
const RESOURCE_BASE = {
|
|
12
|
+
kind: "directory",
|
|
13
|
+
path: fileURLToPath(new URL("../assets/", import.meta.url)),
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const CANDIDATE = {
|
|
17
|
+
name: "plugin-authoring",
|
|
18
|
+
description: "Build and publish a DeepSeek Harness plugin (host-only, client-only, or dual-face) without re-discovering the architecture: package.json anatomy, cordis.patch.yml, the browser-half recipe (dsh.client + window.__ModuleLoader__ + slots), locale, theme tokens, install steps, gotchas, and the validation checklist. Load before creating or extending any DSH plugin.",
|
|
19
|
+
invocation: {
|
|
20
|
+
modelInvocable: true,
|
|
21
|
+
userInvocable: true,
|
|
22
|
+
},
|
|
23
|
+
provider: PROVIDER_NAME,
|
|
24
|
+
source: "bundled",
|
|
25
|
+
resourceBase: RESOURCE_BASE,
|
|
26
|
+
rank: BUNDLED_SKILL_RANK,
|
|
27
|
+
locator: SKILL_BODY_URL,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const provider = {
|
|
31
|
+
name: PROVIDER_NAME,
|
|
32
|
+
list: () => Promise.resolve([CANDIDATE]),
|
|
33
|
+
async get(_candidate) {
|
|
34
|
+
return {
|
|
35
|
+
name: CANDIDATE.name,
|
|
36
|
+
description: CANDIDATE.description,
|
|
37
|
+
invocation: CANDIDATE.invocation,
|
|
38
|
+
provider: CANDIDATE.provider,
|
|
39
|
+
source: CANDIDATE.source,
|
|
40
|
+
resourceBase: RESOURCE_BASE,
|
|
41
|
+
content: await readFile(SKILL_BODY_URL, "utf8"),
|
|
42
|
+
};
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const name = "plugin-authoring";
|
|
47
|
+
const inject = ["skills"];
|
|
48
|
+
|
|
49
|
+
function apply(ctx) {
|
|
50
|
+
ctx.skills.registerProvider(() => provider);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export { apply, inject, name };
|
package/locale/en.json
ADDED
package/locale/es.json
ADDED
package/locale/fr.json
ADDED
package/locale/zh.json
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,47 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@javierfrancoleal/plugin-authoring",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Plugin-authoring skill for DeepSeek Harness: the distilled anatomy, client-UI recipe, slots, locale, theme tokens, install steps, gotchas, and validation checklist for building and publishing new DSH plugins.",
|
|
5
|
+
"author": "J.Franco",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "lib/index.js",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": "./lib/index.js",
|
|
11
|
+
"./cordis.patch.yml": "./cordis.patch.yml",
|
|
12
|
+
"./package.json": "./package.json",
|
|
13
|
+
"./locale/*.json": "./locale/*.json"
|
|
14
|
+
},
|
|
15
|
+
"icon": "./icon.svg",
|
|
16
|
+
"files": [
|
|
17
|
+
"icon.svg",
|
|
18
|
+
"locale/*.json",
|
|
19
|
+
"lib/index.js",
|
|
20
|
+
"assets",
|
|
21
|
+
"cordis.patch.yml",
|
|
22
|
+
"README.md"
|
|
23
|
+
],
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=24",
|
|
26
|
+
"dsh": ">=0.2.1-rc.0"
|
|
27
|
+
},
|
|
28
|
+
"keywords": [
|
|
29
|
+
"deepseek-harness",
|
|
30
|
+
"plugin-authoring",
|
|
31
|
+
"scaffold",
|
|
32
|
+
"skill"
|
|
33
|
+
],
|
|
34
|
+
"dsh": {
|
|
35
|
+
"manifestVersion": 1,
|
|
36
|
+
"bundle": {
|
|
37
|
+
"patch": "./cordis.patch.yml"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"publishConfig": {
|
|
41
|
+
"access": "public"
|
|
42
|
+
},
|
|
43
|
+
"peerDependencies": {
|
|
44
|
+
"@deepseek-ai/cordis": "~4.0.5",
|
|
45
|
+
"@deepseek-ai/dsh-skill": ">=0.2.1-rc.5"
|
|
46
|
+
}
|
|
6
47
|
}
|