harness-alchemist 0.1.8 → 0.1.10
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/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +25 -14
- package/alchemy.json +8 -0
- package/alchemy.schema.json +1 -1
- package/dist/deepseek.d.ts +2 -2
- package/dist/deepseek.d.ts.map +1 -1
- package/dist/deepseek.js +7 -20
- package/dist/deepseek.js.map +1 -1
- package/dist/opencode.d.ts +2 -19
- package/dist/opencode.d.ts.map +1 -1
- package/dist/opencode.js +5 -53
- package/dist/opencode.js.map +1 -1
- package/lib/create.mjs +2 -0
- package/lib/validate.mjs +86 -12
- package/mcp.json +19 -0
- package/package.json +6 -1
- package/plugin.json +16 -2
- package/skills/harness-alchemist/SKILL.md +5 -5
- package/skills/harness-alchemist/references/antigravity.md +2 -2
- package/skills/harness-alchemist/references/compatibility.md +10 -8
- package/skills/harness-alchemist/references/deepseek-harness.md +5 -3
- package/skills/harness-alchemist/references/publishing.md +12 -10
- package/skills/harness-alchemist/skill-runtime.json +50 -0
- package/templates/v0.1.0/universal-typescript/.agents/skills/develop-template/SKILL.md.tpl +8 -8
- package/templates/v0.1.0/universal-typescript/.agents/skills/develop-template/references/compatibility.md.tpl +12 -12
- package/templates/v0.1.0/universal-typescript/.agents/skills/develop-template/scripts/check-package.mjs.tpl +3 -0
- package/templates/v0.1.0/universal-typescript/.agents/skills/develop-template/scripts/sync-metadata.mjs.tpl +12 -4
- package/templates/v0.1.0/universal-typescript/.github/workflows/npm-publish.yml.tpl +39 -20
- package/templates/v0.1.0/universal-typescript/AGENTS.md.tpl +4 -2
- package/templates/v0.1.0/universal-typescript/README.md.tpl +13 -9
- package/templates/v0.1.0/universal-typescript/mcp.json.tpl +19 -0
- package/templates/v0.1.0/universal-typescript/package.json.tpl +4 -0
- package/templates/v0.1.0/universal-typescript/plugin.json.tpl +10 -2
- package/templates/v0.1.0/universal-typescript/skills/shared-skill/SKILL.md.tpl +4 -3
- package/templates/v0.1.0/universal-typescript/skills/shared-skill/references/tool-contract.md.tpl +14 -12
- package/templates/v0.1.0/universal-typescript/skills/shared-skill/skill-runtime.json.tpl +50 -0
- package/templates/v0.1.0/universal-typescript/src/deepseek.ts.tpl +7 -26
- package/templates/v0.1.0/universal-typescript/src/opencode.ts.tpl +6 -59
- package/templates/v0.1.0/universal-typescript/tests/runtimes.test.mjs.tpl +44 -9
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
|
|
3
3
|
"name": "harness-alchemist",
|
|
4
4
|
"displayName": "Harness Alchemist",
|
|
5
|
-
"version": "0.1.
|
|
5
|
+
"version": "0.1.10",
|
|
6
6
|
"description": "Scaffold, validate, and publish portable coding-agent plugins across Claude Code, Codex, OpenCode, Antigravity, and DeepSeek Harness.",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "Haochuan Zhang"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "harness-alchemist",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"description": "Scaffold, validate, and publish portable coding-agent plugins across Claude Code, Codex, OpenCode, Antigravity, and DeepSeek Harness.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Haochuan Zhang",
|
package/README.md
CHANGED
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
|
|
29
29
|
---
|
|
30
30
|
|
|
31
|
-
Harness Alchemist scaffolds one TypeScript plugin repository that installs natively into **Claude Code**, **Codex/ChatGPT**, **OpenCode**, **Google Antigravity**, and **DeepSeek Harness/Cordis**.
|
|
31
|
+
Harness Alchemist scaffolds one TypeScript plugin repository that installs natively into **Claude Code**, **Codex/ChatGPT**, **OpenCode**, **Google Antigravity**, and **DeepSeek Harness/Cordis**. Each product skill declares its fixed executable and portable tool schema in `skill-runtime.json`; host entrypoints delegate through `@lunarmoon26/agent-skill-runtime`.
|
|
32
32
|
|
|
33
33
|
## Install
|
|
34
34
|
|
|
@@ -38,7 +38,7 @@ npm install -g harness-alchemist
|
|
|
38
38
|
npx harness-alchemist@latest create my-plugin --help
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
-
Requires Node.js 22.20+ (Bun 1.2+ also supported).
|
|
41
|
+
Requires Node.js 22.20+ (Bun 1.2+ also supported).
|
|
42
42
|
|
|
43
43
|
## Quick start
|
|
44
44
|
|
|
@@ -78,10 +78,10 @@ retains the strict generated single-package layout.
|
|
|
78
78
|
|
|
79
79
|
The optional `runtime` field selects what the adapted package must contain:
|
|
80
80
|
|
|
81
|
-
- `"npm"` (default) — the full generated contract: npm metadata,
|
|
82
|
-
|
|
83
|
-
- `"skills"` — skills and harness manifests only.
|
|
84
|
-
Cordis patch are required, and single-language scripts are allowed, so
|
|
81
|
+
- `"npm"` (default) — the full generated contract: npm metadata, portable
|
|
82
|
+
runtime manifests, OpenCode and Cordis adapters, and the Cordis patch.
|
|
83
|
+
- `"skills"` — skills, portable runtime manifests, and harness manifests only.
|
|
84
|
+
No npm package, adapters, or Cordis patch are required, and single-language scripts are allowed, so
|
|
85
85
|
Python, Go, Rust, Java, C#, or Swift repositories can expose their workflows
|
|
86
86
|
to Claude Code, Codex, Antigravity, and DeepSeek's filesystem skill roots
|
|
87
87
|
without adopting a JavaScript runtime.
|
|
@@ -95,10 +95,10 @@ version.
|
|
|
95
95
|
|
|
96
96
|
| Surface | Purpose |
|
|
97
97
|
| --- | --- |
|
|
98
|
-
| `skills/<name>/` | Agent Skills
|
|
99
|
-
| `src/opencode.ts` | OpenCode plugin
|
|
100
|
-
| `src/deepseek.ts` + `cordis.patch.yml` | Cordis function plugin
|
|
101
|
-
| `.claude-plugin/`, `.codex-plugin/`, `.agents/plugins/`, `plugin.json` | Native manifests for Claude Code, Codex, and
|
|
98
|
+
| `skills/<name>/` | Agent Skills workflow, portable `skill-runtime.json`, fixed executable, optional tested twin, and tool-contract reference |
|
|
99
|
+
| `src/opencode.ts` | OpenCode plugin projecting the portable runtime manifest as native tools |
|
|
100
|
+
| `src/deepseek.ts` + `cordis.patch.yml` | Cordis function plugin registering the same portable tools |
|
|
101
|
+
| `.claude-plugin/`, `.codex-plugin/`, `.agents/plugins/`, `plugin.json`, `mcp.json` | Native and Agent Plugins manifests for Claude Code, Codex, Antigravity, and MCP hosts |
|
|
102
102
|
| `.agents/skills/develop-<name>/` | Repository-maintenance skill so agents can develop the project recursively |
|
|
103
103
|
| `.github/workflows/` | CI plus tag-triggered publish (`vX.Y.Z` → verify → npm provenance) |
|
|
104
104
|
|
|
@@ -112,12 +112,16 @@ Install paths below are verified against real CLIs before shipping in the templa
|
|
|
112
112
|
| Codex/ChatGPT | bundled natively | — | `codex plugin add <name>@<marketplace>` |
|
|
113
113
|
| OpenCode | via `~/.agents/skills/` | npm package hooks | `"plugin": ["<package>"]` in `opencode.json` |
|
|
114
114
|
| Google Antigravity | nested bundle | — | `agy plugin validate . && agy plugin install .` |
|
|
115
|
-
| DeepSeek Harness | profile filesystem roots | Cordis
|
|
115
|
+
| DeepSeek Harness | profile filesystem roots | Cordis tool plugin | `dsh plugin --profile demo add <package-or-path>` |
|
|
116
116
|
|
|
117
117
|
## Skill script contract
|
|
118
118
|
|
|
119
119
|
Product skills own their logic; adapters never do.
|
|
120
120
|
|
|
121
|
+
`skills/<name>/skill-runtime.json` declares the model-facing schema and selects
|
|
122
|
+
one author-controlled entrypoint. Hosts validate the same manifest and never
|
|
123
|
+
accept an executable path from model input.
|
|
124
|
+
|
|
121
125
|
```bash
|
|
122
126
|
echo '{"request": "hello"}' | node skills/<name>/scripts/main.mjs
|
|
123
127
|
# {"ok":true,"plugin":"my-plugin","echo":{"request":"hello"}}
|
|
@@ -125,7 +129,8 @@ echo '{"request": "hello"}' | node skills/<name>/scripts/main.mjs
|
|
|
125
129
|
|
|
126
130
|
- One JSON object on stdin, one JSON result plus newline on stdout.
|
|
127
131
|
- Non-zero exit with a stderr diagnostic on failure.
|
|
128
|
-
- `scripts/main.py` is a stdlib-only behavioral twin
|
|
132
|
+
- `scripts/main.py` is a stdlib-only behavioral twin for direct compatibility
|
|
133
|
+
testing; the starter manifest selects `scripts/main.mjs` for host tools.
|
|
129
134
|
|
|
130
135
|
## Install-level verification
|
|
131
136
|
|
|
@@ -152,14 +157,20 @@ and the DeepSeek Cordis check require npm mode with built adapters
|
|
|
152
157
|
|
|
153
158
|
## Validation tiers
|
|
154
159
|
|
|
155
|
-
`npm run validate` always enforces Agent Skills frontmatter compliance, SKILL.md reference resolution, and twin parity. With the optional `pyodide` devDependency installed, Python
|
|
160
|
+
`npm run validate` always enforces Agent Skills frontmatter compliance, SKILL.md reference resolution, runtime-manifest validation, fixed contained entrypoints, and npm-mode twin parity. With the optional `pyodide` devDependency installed, Python twins are additionally compiled and smoke-executed inside a WebAssembly CPython sandbox; Pyodide is validation-only, not a version 1 execution engine.
|
|
156
161
|
|
|
157
162
|
## Release automation
|
|
158
163
|
|
|
164
|
+
The npm package trusts GitHub Actions workflow `npm-publish.yml` in
|
|
165
|
+
`lunarmoon26/harness-alchemist`. The workflow uses OIDC instead of an
|
|
166
|
+
`NPM_TOKEN`, requires the tag to match the committed package version, and
|
|
167
|
+
accepts only commits contained in `main`.
|
|
168
|
+
|
|
159
169
|
```bash
|
|
160
170
|
# bump package.json version, then:
|
|
161
171
|
npm run sync && npm run verify && git commit -am "Release vX.Y.Z" && git push
|
|
162
|
-
|
|
172
|
+
# after the release commit is merged to main:
|
|
173
|
+
git tag vX.Y.Z && git push origin vX.Y.Z # publishes through npm Trusted Publishing
|
|
163
174
|
```
|
|
164
175
|
|
|
165
176
|
## License
|
package/alchemy.json
ADDED
package/alchemy.schema.json
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"runtime": {
|
|
14
14
|
"enum": ["npm", "skills"],
|
|
15
15
|
"default": "npm",
|
|
16
|
-
"description": "'npm' requires the full generated contract (npm package, OpenCode and Cordis adapters, Cordis patch
|
|
16
|
+
"description": "'npm' requires the full generated contract (npm package, portable runtime manifests, OpenCode and Cordis adapters, and Cordis patch). 'skills' requires skills, portable runtime manifests, and harness manifests only and allows single-language scripts."
|
|
17
17
|
},
|
|
18
18
|
"pluginRoot": {
|
|
19
19
|
"type": "string",
|
package/dist/deepseek.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Context } from "@deepseek-ai/cordis";
|
|
2
2
|
export declare const name = "harness-alchemist";
|
|
3
|
-
export declare
|
|
4
|
-
export declare function apply(context: Context): void
|
|
3
|
+
export declare const inject: string[];
|
|
4
|
+
export declare function apply(context: Context): Promise<void>;
|
|
5
5
|
//# sourceMappingURL=deepseek.d.ts.map
|
package/dist/deepseek.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deepseek.d.ts","sourceRoot":"","sources":["../src/deepseek.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"deepseek.d.ts","sourceRoot":"","sources":["../src/deepseek.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAGlD,eAAO,MAAM,IAAI,sBAAsB,CAAA;AACvC,eAAO,MAAM,MAAM,UAAY,CAAA;AAI/B,wBAAsB,KAAK,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAK3D"}
|
package/dist/deepseek.js
CHANGED
|
@@ -1,26 +1,13 @@
|
|
|
1
|
-
import { spawnSync } from "node:child_process";
|
|
2
1
|
import { dirname, join } from "node:path";
|
|
3
2
|
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { registerDeepSeekTools } from "@lunarmoon26/agent-skill-runtime/deepseek";
|
|
4
4
|
export const name = "harness-alchemist";
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
});
|
|
12
|
-
if (result.error) {
|
|
13
|
-
throw new Error(`Could not run ${script} via ${runtime}: ${result.error.message}`);
|
|
14
|
-
}
|
|
15
|
-
if (result.status !== 0) {
|
|
16
|
-
const diagnostic = result.stderr.trim() || `${script} exited with code ${result.status}`;
|
|
17
|
-
throw new Error(diagnostic);
|
|
18
|
-
}
|
|
19
|
-
return JSON.parse(result.stdout);
|
|
20
|
-
}
|
|
21
|
-
export function apply(context) {
|
|
22
|
-
context.provide(name, {
|
|
23
|
-
run: (payload, runtime = "node") => runSkillScript(runtime === "python" ? "main.py" : "main.mjs", payload),
|
|
5
|
+
export const inject = ["tools"];
|
|
6
|
+
const pluginRoot = join(dirname(fileURLToPath(import.meta.url)), "..");
|
|
7
|
+
export async function apply(context) {
|
|
8
|
+
await registerDeepSeekTools(context, {
|
|
9
|
+
pluginRoot,
|
|
10
|
+
manifestPaths: ["skills/harness-alchemist/skill-runtime.json"],
|
|
24
11
|
});
|
|
25
12
|
}
|
|
26
13
|
//# sourceMappingURL=deepseek.js.map
|
package/dist/deepseek.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deepseek.js","sourceRoot":"","sources":["../src/deepseek.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"deepseek.js","sourceRoot":"","sources":["../src/deepseek.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAGxC,OAAO,EAAE,qBAAqB,EAAE,MAAM,2CAA2C,CAAA;AAEjF,MAAM,CAAC,MAAM,IAAI,GAAG,mBAAmB,CAAA;AACvC,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,OAAO,CAAC,CAAA;AAE/B,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAA;AAEtE,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,OAAgB;IAC1C,MAAM,qBAAqB,CAAC,OAAO,EAAE;QACnC,UAAU;QACV,aAAa,EAAE,CAAC,6CAA6C,CAAC;KAC/D,CAAC,CAAA;AACJ,CAAC"}
|
package/dist/opencode.d.ts
CHANGED
|
@@ -1,21 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
declare const plugin:
|
|
3
|
-
tool: {
|
|
4
|
-
harness_alchemist_run: {
|
|
5
|
-
description: string;
|
|
6
|
-
args: {
|
|
7
|
-
input: import("zod").ZodString;
|
|
8
|
-
runtime: import("zod").ZodOptional<import("zod").ZodEnum<{
|
|
9
|
-
node: "node";
|
|
10
|
-
python: "python";
|
|
11
|
-
}>>;
|
|
12
|
-
};
|
|
13
|
-
execute(args: {
|
|
14
|
-
input: string;
|
|
15
|
-
runtime?: "node" | "python" | undefined;
|
|
16
|
-
}, context: import("@opencode-ai/plugin").ToolContext): Promise<import("@opencode-ai/plugin").ToolResult>;
|
|
17
|
-
};
|
|
18
|
-
};
|
|
19
|
-
}>;
|
|
1
|
+
import type { Plugin } from "@opencode-ai/plugin";
|
|
2
|
+
declare const plugin: Plugin;
|
|
20
3
|
export default plugin;
|
|
21
4
|
//# sourceMappingURL=opencode.d.ts.map
|
package/dist/opencode.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"opencode.d.ts","sourceRoot":"","sources":["../src/opencode.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"opencode.d.ts","sourceRoot":"","sources":["../src/opencode.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAKjD,QAAA,MAAM,MAAM,EAAE,MAGZ,CAAA;AAEF,eAAe,MAAM,CAAA"}
|
package/dist/opencode.js
CHANGED
|
@@ -1,58 +1,10 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
2
1
|
import { dirname, join } from "node:path";
|
|
3
2
|
import { fileURLToPath } from "node:url";
|
|
4
|
-
import {
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const child = spawn(runtime, [join(scriptsDirectory, script)], {
|
|
10
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
11
|
-
});
|
|
12
|
-
let stdout = "";
|
|
13
|
-
let stderr = "";
|
|
14
|
-
child.stdout.on("data", (chunk) => {
|
|
15
|
-
stdout += chunk;
|
|
16
|
-
});
|
|
17
|
-
child.stderr.on("data", (chunk) => {
|
|
18
|
-
stderr += chunk;
|
|
19
|
-
});
|
|
20
|
-
child.on("error", (error) => {
|
|
21
|
-
rejectResult(new Error(`Could not run ${script} via ${runtime}: ${error.message}`));
|
|
22
|
-
});
|
|
23
|
-
child.on("close", (code) => {
|
|
24
|
-
if (code !== 0) {
|
|
25
|
-
rejectResult(new Error(stderr.trim() || `${script} exited with code ${code}`));
|
|
26
|
-
}
|
|
27
|
-
else {
|
|
28
|
-
resolveResult(stdout);
|
|
29
|
-
}
|
|
30
|
-
});
|
|
31
|
-
child.stdin.end(payload);
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
const plugin = (async () => {
|
|
35
|
-
return {
|
|
36
|
-
tool: {
|
|
37
|
-
harness_alchemist_run: tool({
|
|
38
|
-
description: "Run the Harness Alchemist shared-skill workflow. Delegates to skills/harness-alchemist/scripts.",
|
|
39
|
-
args: {
|
|
40
|
-
input: tool.schema
|
|
41
|
-
.string()
|
|
42
|
-
.describe("JSON object passed on stdin to the skill entrypoint"),
|
|
43
|
-
runtime: tool.schema
|
|
44
|
-
.enum(["node", "python"])
|
|
45
|
-
.optional()
|
|
46
|
-
.describe("Entrypoint runtime; defaults to node"),
|
|
47
|
-
},
|
|
48
|
-
execute: async (args) => {
|
|
49
|
-
const script = args.runtime === "python" ? "main.py" : "main.mjs";
|
|
50
|
-
const output = await runSkillScript(script, args.input);
|
|
51
|
-
return JSON.stringify(JSON.parse(output));
|
|
52
|
-
},
|
|
53
|
-
}),
|
|
54
|
-
},
|
|
55
|
-
};
|
|
3
|
+
import { createOpenCodePlugin } from "@lunarmoon26/agent-skill-runtime/opencode";
|
|
4
|
+
const pluginRoot = join(dirname(fileURLToPath(import.meta.url)), "..");
|
|
5
|
+
const plugin = createOpenCodePlugin({
|
|
6
|
+
pluginRoot,
|
|
7
|
+
manifestPaths: ["skills/harness-alchemist/skill-runtime.json"],
|
|
56
8
|
});
|
|
57
9
|
export default plugin;
|
|
58
10
|
//# sourceMappingURL=opencode.js.map
|
package/dist/opencode.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"opencode.js","sourceRoot":"","sources":["../src/opencode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"opencode.js","sourceRoot":"","sources":["../src/opencode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAGxC,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAA;AAEhF,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAA;AAEtE,MAAM,MAAM,GAAW,oBAAoB,CAAC;IAC1C,UAAU;IACV,aAAa,EAAE,CAAC,6CAA6C,CAAC;CAC/D,CAAC,CAAA;AAEF,eAAe,MAAM,CAAA"}
|
package/lib/create.mjs
CHANGED
|
@@ -247,6 +247,8 @@ export async function runCreate(argv) {
|
|
|
247
247
|
const tokens = {
|
|
248
248
|
NAME: options.name,
|
|
249
249
|
NAME_JSON: JSON.stringify(options.name),
|
|
250
|
+
TOOL_NAME: `${options.name.replaceAll("-", "_")}_run`,
|
|
251
|
+
TOOL_NAME_JSON: JSON.stringify(`${options.name.replaceAll("-", "_")}_run`),
|
|
250
252
|
DISPLAY_NAME: options.displayName,
|
|
251
253
|
DISPLAY_NAME_JSON: JSON.stringify(options.displayName),
|
|
252
254
|
DESCRIPTION: options.description,
|
package/lib/validate.mjs
CHANGED
|
@@ -2,10 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
import { existsSync } from "node:fs"
|
|
4
4
|
import { readdir, readFile, realpath } from "node:fs/promises"
|
|
5
|
-
import { basename, dirname, join, resolve, sep } from "node:path"
|
|
5
|
+
import { basename, dirname, join, relative, resolve, sep } from "node:path"
|
|
6
6
|
import { fileURLToPath } from "node:url"
|
|
7
7
|
import { spawnSync } from "node:child_process"
|
|
8
8
|
|
|
9
|
+
import { discoverSkillTools } from "@lunarmoon26/agent-skill-runtime"
|
|
10
|
+
|
|
11
|
+
const AGENT_PLUGIN_SCHEMA = "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json"
|
|
12
|
+
const AGENT_PLUGIN_MCP_SCHEMA = "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json"
|
|
13
|
+
const SKILL_RUNTIME_PACKAGE = "@lunarmoon26/agent-skill-runtime"
|
|
14
|
+
const SKILL_RUNTIME_VERSION = "0.1.0"
|
|
15
|
+
|
|
9
16
|
const PROJECT_REQUIRED_FILES = [
|
|
10
17
|
".agents/plugins/marketplace.json",
|
|
11
18
|
".claude-plugin/marketplace.json",
|
|
@@ -18,6 +25,7 @@ const PROJECT_REQUIRED_FILES = [
|
|
|
18
25
|
const PLUGIN_REQUIRED_FILES = [
|
|
19
26
|
".claude-plugin/plugin.json",
|
|
20
27
|
".codex-plugin/plugin.json",
|
|
28
|
+
"mcp.json",
|
|
21
29
|
"plugin.json",
|
|
22
30
|
]
|
|
23
31
|
|
|
@@ -38,10 +46,10 @@ Validates a universal Claude, Codex, OpenCode, Antigravity, and DeepSeek
|
|
|
38
46
|
plugin scaffold. By default the project is resolved from the current working
|
|
39
47
|
directory or from the script's containing generated project.
|
|
40
48
|
|
|
41
|
-
Product skills are checked against the Agent Skills specification and
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
49
|
+
Product skills are checked against the Agent Skills specification and the
|
|
50
|
+
portable skill runtime manifest. When the optional 'pyodide' devDependency is
|
|
51
|
+
installed, Python twin entrypoints are also syntax-checked and smoke-executed
|
|
52
|
+
in a WebAssembly CPython sandbox.
|
|
45
53
|
|
|
46
54
|
Options:
|
|
47
55
|
--external Run installed platform validators, currently Claude Code.
|
|
@@ -245,6 +253,12 @@ function exportTarget(exports, key, field = "import") {
|
|
|
245
253
|
return undefined
|
|
246
254
|
}
|
|
247
255
|
|
|
256
|
+
function sameValues(actual, expected) {
|
|
257
|
+
return Array.isArray(actual)
|
|
258
|
+
&& actual.length === expected.length
|
|
259
|
+
&& expected.every((value, index) => actual[index] === value)
|
|
260
|
+
}
|
|
261
|
+
|
|
248
262
|
const PYTHON_SMOKE_PROGRAM = `
|
|
249
263
|
import contextlib
|
|
250
264
|
import io
|
|
@@ -289,7 +303,7 @@ async function smokePythonScripts(scripts, errors, warnings, execSmoke = true) {
|
|
|
289
303
|
let output
|
|
290
304
|
try {
|
|
291
305
|
if (execSmoke) {
|
|
292
|
-
instance.globals.set("__ha_payload__", "
|
|
306
|
+
instance.globals.set("__ha_payload__", '{"request":"validation"}')
|
|
293
307
|
output = instance.runPython(PYTHON_SMOKE_PROGRAM)
|
|
294
308
|
} else {
|
|
295
309
|
instance.runPython('compile(__ha_source__, "<skill>", "exec")\nNone')
|
|
@@ -466,7 +480,8 @@ export async function validateProject(projectRoot, options = {}) {
|
|
|
466
480
|
join(root, ".agents/plugins/marketplace.json"),
|
|
467
481
|
errors,
|
|
468
482
|
)
|
|
469
|
-
const
|
|
483
|
+
const agentPlugin = await readJson(join(pluginRoot, "plugin.json"), errors)
|
|
484
|
+
const agentPluginMcp = await readJson(join(pluginRoot, "mcp.json"), errors)
|
|
470
485
|
|
|
471
486
|
if (errors.length > 0) return { root, errors, warnings }
|
|
472
487
|
|
|
@@ -482,11 +497,36 @@ export async function validateProject(projectRoot, options = {}) {
|
|
|
482
497
|
|
|
483
498
|
for (const [label, value] of [
|
|
484
499
|
["Codex plugin", codexPlugin.name],
|
|
485
|
-
["
|
|
500
|
+
["Agent plugin", agentPlugin.name],
|
|
486
501
|
]) {
|
|
487
502
|
if (value !== pluginName) errors.push(`${label} name '${value}' does not match '${pluginName}'`)
|
|
488
503
|
}
|
|
489
504
|
|
|
505
|
+
if (agentPlugin.$schema !== AGENT_PLUGIN_SCHEMA) {
|
|
506
|
+
errors.push(`plugin.json $schema must be '${AGENT_PLUGIN_SCHEMA}'`)
|
|
507
|
+
}
|
|
508
|
+
if (agentPluginMcp.$schema !== AGENT_PLUGIN_MCP_SCHEMA) {
|
|
509
|
+
errors.push(`mcp.json $schema must be '${AGENT_PLUGIN_MCP_SCHEMA}'`)
|
|
510
|
+
}
|
|
511
|
+
const expectedMcpArgs = [
|
|
512
|
+
"-y",
|
|
513
|
+
`${SKILL_RUNTIME_PACKAGE}@${SKILL_RUNTIME_VERSION}`,
|
|
514
|
+
"mcp",
|
|
515
|
+
"--root",
|
|
516
|
+
"${PLUGIN_ROOT}",
|
|
517
|
+
"--manifest",
|
|
518
|
+
`skills/${pluginName}/skill-runtime.json`,
|
|
519
|
+
]
|
|
520
|
+
const mcpServer = agentPluginMcp.mcpServers?.[pluginName]
|
|
521
|
+
if (
|
|
522
|
+
mcpServer?.type !== "stdio" ||
|
|
523
|
+
mcpServer.command !== "npx" ||
|
|
524
|
+
mcpServer.cwd !== "${PLUGIN_ROOT}" ||
|
|
525
|
+
!sameValues(mcpServer.args, expectedMcpArgs)
|
|
526
|
+
) {
|
|
527
|
+
errors.push(`mcp.json must configure '${pluginName}' through ${SKILL_RUNTIME_PACKAGE}@${SKILL_RUNTIME_VERSION}`)
|
|
528
|
+
}
|
|
529
|
+
|
|
490
530
|
if (runtime === "skills") {
|
|
491
531
|
for (const [label, manifest] of [["Codex plugin", codexPlugin]]) {
|
|
492
532
|
if (manifest.version !== claudePlugin.version) {
|
|
@@ -496,8 +536,11 @@ export async function validateProject(projectRoot, options = {}) {
|
|
|
496
536
|
errors.push(`${label} description does not match the Claude plugin`)
|
|
497
537
|
}
|
|
498
538
|
}
|
|
499
|
-
if (
|
|
500
|
-
errors.push("
|
|
539
|
+
if (agentPlugin.version !== claudePlugin.version) {
|
|
540
|
+
errors.push("Agent plugin version does not match the Claude plugin")
|
|
541
|
+
}
|
|
542
|
+
if (agentPlugin.description !== claudePlugin.description) {
|
|
543
|
+
errors.push("Agent plugin description does not match the Claude plugin")
|
|
501
544
|
}
|
|
502
545
|
} else {
|
|
503
546
|
const packageJson = await readJson(join(pluginRoot, "package.json"), errors)
|
|
@@ -519,11 +562,20 @@ export async function validateProject(projectRoot, options = {}) {
|
|
|
519
562
|
errors.push(`${label} description does not match package.json`)
|
|
520
563
|
}
|
|
521
564
|
}
|
|
522
|
-
if (
|
|
523
|
-
errors.push("
|
|
565
|
+
if (agentPlugin.version !== packageJson.version) {
|
|
566
|
+
errors.push("Agent plugin version does not match package.json")
|
|
567
|
+
}
|
|
568
|
+
if (agentPlugin.description !== packageJson.description) {
|
|
569
|
+
errors.push("Agent plugin description does not match package.json")
|
|
570
|
+
}
|
|
571
|
+
if (agentPlugin.license !== packageJson.license) {
|
|
572
|
+
errors.push("Agent plugin license does not match package.json")
|
|
524
573
|
}
|
|
525
574
|
|
|
526
575
|
if (packageJson.type !== "module") errors.push("package.json type must be 'module'")
|
|
576
|
+
if (packageJson.dependencies?.[SKILL_RUNTIME_PACKAGE] !== SKILL_RUNTIME_VERSION) {
|
|
577
|
+
errors.push(`package.json dependencies must pin '${SKILL_RUNTIME_PACKAGE}' to '${SKILL_RUNTIME_VERSION}'`)
|
|
578
|
+
}
|
|
527
579
|
if (opencodeExport === "." && packageJson.engines?.node !== ">=22.20.0") {
|
|
528
580
|
errors.push("package.json engines.node must be '>=22.20.0'")
|
|
529
581
|
}
|
|
@@ -547,6 +599,7 @@ export async function validateProject(projectRoot, options = {}) {
|
|
|
547
599
|
"cordis.patch.yml",
|
|
548
600
|
".claude-plugin/plugin.json",
|
|
549
601
|
".codex-plugin/plugin.json",
|
|
602
|
+
"mcp.json",
|
|
550
603
|
"plugin.json",
|
|
551
604
|
]) {
|
|
552
605
|
if (!packageJson.files?.includes(entry)) errors.push(`package.json files is missing '${entry}'`)
|
|
@@ -599,6 +652,7 @@ export async function validateProject(projectRoot, options = {}) {
|
|
|
599
652
|
if (skillFiles.length < 2) errors.push("Expected a shared skill and a project development skill")
|
|
600
653
|
|
|
601
654
|
const pythonScripts = new Set()
|
|
655
|
+
const runtimeManifestPaths = []
|
|
602
656
|
for (const skillFile of skillFiles) {
|
|
603
657
|
const content = await readFile(skillFile, "utf8")
|
|
604
658
|
const frontmatter = parseFrontmatter(content)
|
|
@@ -613,11 +667,31 @@ export async function validateProject(projectRoot, options = {}) {
|
|
|
613
667
|
errors.push(`${skillFile}: invalid Agent Skill name`)
|
|
614
668
|
}
|
|
615
669
|
if (productSkillFiles.includes(skillFile)) {
|
|
670
|
+
const manifestPath = join(dirname(skillFile), "skill-runtime.json")
|
|
671
|
+
if (!existsSync(manifestPath)) {
|
|
672
|
+
errors.push(`${skillFile}: missing skill-runtime.json`)
|
|
673
|
+
} else {
|
|
674
|
+
runtimeManifestPaths.push(relative(pluginRoot, manifestPath))
|
|
675
|
+
}
|
|
616
676
|
await checkProductSkillRuntime(skillFile, content, frontmatter, errors, pythonScripts, runtime)
|
|
617
677
|
}
|
|
618
678
|
}
|
|
619
679
|
}
|
|
620
680
|
|
|
681
|
+
if (runtimeManifestPaths.length === productSkillFiles.length && runtimeManifestPaths.length > 0) {
|
|
682
|
+
try {
|
|
683
|
+
const tools = await discoverSkillTools({ pluginRoot, manifestPaths: runtimeManifestPaths })
|
|
684
|
+
for (const tool of tools) {
|
|
685
|
+
const directoryName = basename(tool.skillRoot)
|
|
686
|
+
if (tool.manifestName !== directoryName) {
|
|
687
|
+
errors.push(`${tool.manifestPath}: manifest name must match skill directory '${directoryName}'`)
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
} catch (error) {
|
|
691
|
+
errors.push(`Skill runtime validation failed: ${error instanceof Error ? error.message : String(error)}`)
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
|
|
621
695
|
if (pythonScripts.size > 0) {
|
|
622
696
|
await smokePythonScripts([...pythonScripts], errors, warnings, runtime === "npm")
|
|
623
697
|
}
|
package/mcp.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json",
|
|
3
|
+
"mcpServers": {
|
|
4
|
+
"harness-alchemist": {
|
|
5
|
+
"type": "stdio",
|
|
6
|
+
"command": "npx",
|
|
7
|
+
"args": [
|
|
8
|
+
"-y",
|
|
9
|
+
"@lunarmoon26/agent-skill-runtime@0.1.0",
|
|
10
|
+
"mcp",
|
|
11
|
+
"--root",
|
|
12
|
+
"${PLUGIN_ROOT}",
|
|
13
|
+
"--manifest",
|
|
14
|
+
"skills/harness-alchemist/skill-runtime.json"
|
|
15
|
+
],
|
|
16
|
+
"cwd": "${PLUGIN_ROOT}"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "harness-alchemist",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"description": "Scaffold, validate, and publish portable coding-agent plugins across Claude Code, Codex, OpenCode, Antigravity, and DeepSeek Harness.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -25,10 +25,12 @@
|
|
|
25
25
|
"lib",
|
|
26
26
|
"templates",
|
|
27
27
|
"skills",
|
|
28
|
+
"alchemy.json",
|
|
28
29
|
"alchemy.schema.json",
|
|
29
30
|
"cordis.patch.yml",
|
|
30
31
|
".claude-plugin/plugin.json",
|
|
31
32
|
".codex-plugin/plugin.json",
|
|
33
|
+
"mcp.json",
|
|
32
34
|
"plugin.json",
|
|
33
35
|
"README.md",
|
|
34
36
|
"LICENSE"
|
|
@@ -56,6 +58,9 @@
|
|
|
56
58
|
"opencode",
|
|
57
59
|
"scaffolding"
|
|
58
60
|
],
|
|
61
|
+
"dependencies": {
|
|
62
|
+
"@lunarmoon26/agent-skill-runtime": "0.1.0"
|
|
63
|
+
},
|
|
59
64
|
"peerDependencies": {
|
|
60
65
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
61
66
|
"@opencode-ai/plugin": "^1.18.21"
|
package/plugin.json
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$schema": "https://
|
|
2
|
+
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
|
|
3
3
|
"name": "harness-alchemist",
|
|
4
|
-
"
|
|
4
|
+
"version": "0.1.10",
|
|
5
|
+
"description": "Scaffold, validate, and publish portable coding-agent plugins across Claude Code, Codex, OpenCode, Antigravity, and DeepSeek Harness.",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "Haochuan Zhang"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "git+https://github.com/lunarmoon26/harness-alchemist.git",
|
|
10
|
+
"repository": "git+https://github.com/lunarmoon26/harness-alchemist.git",
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"keywords": [
|
|
13
|
+
"agent-skill",
|
|
14
|
+
"scaffolding",
|
|
15
|
+
"mcp",
|
|
16
|
+
"opencode",
|
|
17
|
+
"deepseek"
|
|
18
|
+
]
|
|
5
19
|
}
|
|
@@ -63,9 +63,9 @@ Static validation is structural evidence, not proof that remote services or host
|
|
|
63
63
|
|
|
64
64
|
## Skill Runtime
|
|
65
65
|
|
|
66
|
-
`
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
66
|
+
`skill-runtime.json` declares the fixed `scripts/main.mjs` entrypoint and the
|
|
67
|
+
portable tool schema used by MCP, OpenCode, and DeepSeek. `scripts/main.py`
|
|
68
|
+
(CPython 3.10+) is a direct-test behavioral twin. Both scripts accept one JSON
|
|
69
|
+
object on stdin, return one JSON result plus newline on stdout, and exit non-zero
|
|
70
|
+
with a stderr diagnostic on failure. Keep protocol changes synchronized with
|
|
71
71
|
[references/compatibility.md](references/compatibility.md).
|
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
|
|
5
5
|
```text
|
|
6
6
|
plugin.json
|
|
7
|
+
mcp.json
|
|
7
8
|
skills/<name>/SKILL.md
|
|
8
9
|
rules/<name>.md
|
|
9
10
|
hooks.json
|
|
10
|
-
mcp_config.json
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
`plugin.json` follows Agent Plugins 1.0 and remains separate from the Claude and Codex manifests. `mcp.json` exposes the same executable skill tools to Agent Plugins hosts; current Antigravity CLI releases ignore that file and continue to load the bundled skill.
|
|
14
14
|
|
|
15
15
|
Current Antigravity Agent Skills use the open Agent Skills directory format with nested `<name>/SKILL.md`. This is also the documented plugin skill shape.
|
|
16
16
|
|