@rbbtsn0w/adg 0.1.0-alpha.1 → 0.1.0-beta.1
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/dist/bin/adg.js +703 -0
- package/dist/src/adapters/anthropic.js +54 -0
- package/dist/src/adapters/index.js +10 -0
- package/dist/src/adapters/openai.js +30 -0
- package/dist/src/adapters/reverse.js +53 -0
- package/dist/src/agents/claude.js +118 -0
- package/dist/src/agents/codex.js +61 -0
- package/{src/agents/index.ts → dist/src/agents/index.js} +6 -8
- package/dist/src/agents/registry.js +24 -0
- package/dist/src/agents/types.js +1 -0
- package/dist/src/commands/adapt.js +26 -0
- package/dist/src/commands/import.js +51 -0
- package/dist/src/commands/init.js +104 -0
- package/dist/src/commands/install.js +257 -0
- package/dist/src/commands/link.js +34 -0
- package/dist/src/commands/list.js +19 -0
- package/dist/src/commands/marketplace.js +124 -0
- package/dist/src/commands/migrate.js +60 -0
- package/dist/src/commands/multiselect-skills.js +103 -0
- package/dist/src/commands/remove.js +102 -0
- package/dist/src/commands/select-agents.js +40 -0
- package/dist/src/commands/select-components.js +61 -0
- package/dist/src/commands/select-plugins.js +25 -0
- package/dist/src/commands/select-scope.js +20 -0
- package/dist/src/commands/update.js +50 -0
- package/dist/src/commands/validate.js +50 -0
- package/dist/src/components.js +90 -0
- package/dist/src/deps.js +46 -0
- package/dist/src/fsutil.js +32 -0
- package/dist/src/hash.js +51 -0
- package/dist/src/lock.js +51 -0
- package/dist/src/manifest.js +110 -0
- package/dist/src/marketplace.js +39 -0
- package/{src/package.ts → dist/src/package.js} +37 -42
- package/{src/paths.ts → dist/src/paths.js} +54 -60
- package/dist/src/semver.js +55 -0
- package/dist/src/skills.js +79 -0
- package/dist/src/sources.js +122 -0
- package/dist/src/types.js +19 -0
- package/dist/vendor/skills/package.json +143 -0
- package/dist/vendor/skills/src/add.js +1663 -0
- package/dist/vendor/skills/src/agents.js +729 -0
- package/dist/vendor/skills/src/blob.js +436 -0
- package/dist/vendor/skills/src/cli.js +340 -0
- package/dist/vendor/skills/src/constants.js +3 -0
- package/dist/vendor/skills/src/detect-agent.js +56 -0
- package/dist/vendor/skills/src/find.js +294 -0
- package/dist/vendor/skills/src/frontmatter.js +13 -0
- package/dist/vendor/skills/src/git-tree.js +32 -0
- package/dist/vendor/skills/src/git.js +235 -0
- package/dist/vendor/skills/src/install.js +75 -0
- package/dist/vendor/skills/src/installer.js +924 -0
- package/dist/vendor/skills/src/list.js +201 -0
- package/dist/vendor/skills/src/local-lock.js +109 -0
- package/dist/vendor/skills/src/plugin-manifest.js +152 -0
- package/dist/vendor/skills/src/prompts/search-multiselect.js +312 -0
- package/dist/vendor/skills/src/providers/index.js +4 -0
- package/dist/vendor/skills/src/providers/registry.js +42 -0
- package/dist/vendor/skills/src/providers/types.js +1 -0
- package/dist/vendor/skills/src/providers/wellknown.js +625 -0
- package/dist/vendor/skills/src/remove.js +263 -0
- package/dist/vendor/skills/src/sanitize.js +57 -0
- package/dist/vendor/skills/src/self-cli.js +15 -0
- package/dist/vendor/skills/src/skill-lock.js +237 -0
- package/dist/vendor/skills/src/skills.js +264 -0
- package/dist/vendor/skills/src/source-parser.js +367 -0
- package/dist/vendor/skills/src/sync.js +404 -0
- package/dist/vendor/skills/src/telemetry.js +101 -0
- package/dist/vendor/skills/src/test-utils.js +59 -0
- package/dist/vendor/skills/src/types.js +1 -0
- package/dist/vendor/skills/src/update-source.js +76 -0
- package/dist/vendor/skills/src/update.js +590 -0
- package/dist/vendor/skills/src/use.js +505 -0
- package/package.json +15 -7
- package/bin/adg.ts +0 -758
- package/src/adapters/anthropic.ts +0 -54
- package/src/adapters/index.ts +0 -24
- package/src/adapters/openai.ts +0 -37
- package/src/adapters/reverse.ts +0 -60
- package/src/agents/claude.ts +0 -124
- package/src/agents/codex.ts +0 -67
- package/src/agents/registry.ts +0 -30
- package/src/agents/types.ts +0 -47
- package/src/commands/adapt.ts +0 -36
- package/src/commands/import.ts +0 -69
- package/src/commands/init.ts +0 -146
- package/src/commands/install.ts +0 -411
- package/src/commands/link.ts +0 -61
- package/src/commands/list.ts +0 -28
- package/src/commands/marketplace.ts +0 -198
- package/src/commands/migrate.ts +0 -84
- package/src/commands/multiselect-skills.ts +0 -137
- package/src/commands/remove.ts +0 -136
- package/src/commands/select-agents.ts +0 -45
- package/src/commands/select-components.ts +0 -66
- package/src/commands/select-plugins.ts +0 -28
- package/src/commands/select-scope.ts +0 -21
- package/src/commands/update.ts +0 -85
- package/src/commands/validate.ts +0 -57
- package/src/components.ts +0 -90
- package/src/deps.ts +0 -64
- package/src/fsutil.ts +0 -38
- package/src/hash.ts +0 -61
- package/src/lock.ts +0 -57
- package/src/manifest.ts +0 -113
- package/src/marketplace.ts +0 -41
- package/src/semver.ts +0 -67
- package/src/skills.ts +0 -88
- package/src/sources.ts +0 -159
- package/src/types.ts +0 -140
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { resolveSkills } from "../skills.js";
|
|
3
|
+
import { isExposed } from "../components.js";
|
|
4
|
+
/**
|
|
5
|
+
* Generate a Claude (.claude-plugin/plugin.json) manifest from an ADG manifest.
|
|
6
|
+
*
|
|
7
|
+
* Maps the universal fields onto Claude's plugin shape. When `strict` is false,
|
|
8
|
+
* skills are listed explicitly so a skill-bundle marketplace entry can be built.
|
|
9
|
+
* An optional `selection` narrows what is exposed (partial install): categories
|
|
10
|
+
* outside it are dropped and skills are pinned to an explicit subset list.
|
|
11
|
+
*/
|
|
12
|
+
export function toAnthropicManifest(pluginDir, manifest, selection) {
|
|
13
|
+
const out = {
|
|
14
|
+
name: manifest.name,
|
|
15
|
+
version: manifest.version,
|
|
16
|
+
description: manifest.description,
|
|
17
|
+
};
|
|
18
|
+
if (manifest.author)
|
|
19
|
+
out.author = manifest.author;
|
|
20
|
+
if (manifest.homepage)
|
|
21
|
+
out.homepage = manifest.homepage;
|
|
22
|
+
if (manifest.license)
|
|
23
|
+
out.license = manifest.license;
|
|
24
|
+
if (manifest.category)
|
|
25
|
+
out.category = manifest.category;
|
|
26
|
+
if (manifest.commands && isExposed(selection, "commands"))
|
|
27
|
+
out.commands = manifest.commands;
|
|
28
|
+
if (manifest.agents && isExposed(selection, "agents"))
|
|
29
|
+
out.agents = manifest.agents;
|
|
30
|
+
if (manifest.hooks && isExposed(selection, "hooks"))
|
|
31
|
+
out.hooks = manifest.hooks;
|
|
32
|
+
if (manifest.mcp && isExposed(selection, "mcp"))
|
|
33
|
+
out.mcp = manifest.mcp;
|
|
34
|
+
if (selection) {
|
|
35
|
+
// Partial install: always an explicit (possibly empty) skill list.
|
|
36
|
+
out.strict = false;
|
|
37
|
+
const names = isExposed(selection, "skills")
|
|
38
|
+
? selection.skills ?? resolveSkills(pluginDir, manifest)
|
|
39
|
+
: [];
|
|
40
|
+
out.skills = names.map((name) => `./skills/${name}`);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
const strict = manifest.strict !== false;
|
|
44
|
+
if (strict && manifest.skills !== undefined) {
|
|
45
|
+
out.skills = manifest.skills;
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
// skill-bundle form: explicit list, strict:false
|
|
49
|
+
out.strict = false;
|
|
50
|
+
out.skills = resolveSkills(pluginDir, manifest).map((name) => `./skills/${name}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return { defaultPath: join(".claude-plugin", "plugin.json"), manifest: out };
|
|
54
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { toAnthropicManifest } from "./anthropic.js";
|
|
2
|
+
import { toCodexManifest } from "./openai.js";
|
|
3
|
+
export const ADAPTERS = {
|
|
4
|
+
claude: toAnthropicManifest,
|
|
5
|
+
anthropic: toAnthropicManifest,
|
|
6
|
+
codex: toCodexManifest,
|
|
7
|
+
openai: toCodexManifest,
|
|
8
|
+
};
|
|
9
|
+
export const ADAPTER_TARGETS = ["claude", "codex"];
|
|
10
|
+
export { toAnthropicManifest, toCodexManifest };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { resolveSkills } from "../skills.js";
|
|
3
|
+
import { isExposed } from "../components.js";
|
|
4
|
+
/**
|
|
5
|
+
* Generate a Codex (.codex-plugin/plugin.json) manifest from an ADG manifest.
|
|
6
|
+
*
|
|
7
|
+
* Codex's minimal manifest requires name, version, description and skills. The
|
|
8
|
+
* skills field is emitted as an explicit array of skill identifiers. An optional
|
|
9
|
+
* `selection` narrows the exposed skills (Codex only consumes skills).
|
|
10
|
+
*/
|
|
11
|
+
export function toCodexManifest(pluginDir, manifest, selection) {
|
|
12
|
+
const skills = !selection
|
|
13
|
+
? resolveSkills(pluginDir, manifest)
|
|
14
|
+
: isExposed(selection, "skills")
|
|
15
|
+
? selection.skills ?? resolveSkills(pluginDir, manifest)
|
|
16
|
+
: [];
|
|
17
|
+
const out = {
|
|
18
|
+
name: manifest.name,
|
|
19
|
+
version: manifest.version,
|
|
20
|
+
description: manifest.description,
|
|
21
|
+
skills,
|
|
22
|
+
};
|
|
23
|
+
if (manifest.author)
|
|
24
|
+
out.author = manifest.author;
|
|
25
|
+
if (manifest.homepage)
|
|
26
|
+
out.homepage = manifest.homepage;
|
|
27
|
+
if (manifest.license)
|
|
28
|
+
out.license = manifest.license;
|
|
29
|
+
return { defaultPath: join(".codex-plugin", "plugin.json"), manifest: out };
|
|
30
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { ADG_SCHEMA_VERSION } from "../types.js";
|
|
2
|
+
import { validateManifest } from "../manifest.js";
|
|
3
|
+
/**
|
|
4
|
+
* Reverse-adapt a runtime-native manifest (.claude-plugin/plugin.json or
|
|
5
|
+
* .codex-plugin/plugin.json) into a canonical ADG manifest. This is the inverse
|
|
6
|
+
* of the forward adapters and is used when importing existing plugins.
|
|
7
|
+
*
|
|
8
|
+
* Missing `version` falls back to 0.0.0 (callers may override with a git SHA);
|
|
9
|
+
* skills normalize to the manifest's array/string or the default ./skills/.
|
|
10
|
+
*/
|
|
11
|
+
export function fromNativeManifest(raw, _kind) {
|
|
12
|
+
if (typeof raw !== "object" || raw === null) {
|
|
13
|
+
throw new Error("native manifest must be a JSON object");
|
|
14
|
+
}
|
|
15
|
+
const n = raw;
|
|
16
|
+
if (typeof n.name !== "string")
|
|
17
|
+
throw new Error("native manifest missing string `name`");
|
|
18
|
+
const manifest = {
|
|
19
|
+
schemaVersion: ADG_SCHEMA_VERSION,
|
|
20
|
+
name: n.name,
|
|
21
|
+
version: typeof n.version === "string" ? n.version : "0.0.0",
|
|
22
|
+
description: typeof n.description === "string" && n.description ? n.description : `${n.name} plugin.`,
|
|
23
|
+
};
|
|
24
|
+
const out = manifest;
|
|
25
|
+
copyIfString(n, out, "license");
|
|
26
|
+
copyIfString(n, out, "category");
|
|
27
|
+
copyIfString(n, out, "homepage");
|
|
28
|
+
copyIfString(n, out, "commands");
|
|
29
|
+
copyIfString(n, out, "agents");
|
|
30
|
+
copyIfString(n, out, "hooks");
|
|
31
|
+
copyIfString(n, out, "mcp");
|
|
32
|
+
if (typeof n.author === "object" && n.author !== null) {
|
|
33
|
+
manifest.author = n.author;
|
|
34
|
+
}
|
|
35
|
+
else if (typeof n.author === "string") {
|
|
36
|
+
manifest.author = { name: n.author };
|
|
37
|
+
}
|
|
38
|
+
if (typeof n.skills === "string" || isStringArray(n.skills)) {
|
|
39
|
+
manifest.skills = n.skills;
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
manifest.skills = "./skills/";
|
|
43
|
+
}
|
|
44
|
+
manifest.strict = typeof n.strict === "boolean" ? n.strict : true;
|
|
45
|
+
return validateManifest(manifest);
|
|
46
|
+
}
|
|
47
|
+
function copyIfString(src, dst, key) {
|
|
48
|
+
if (typeof src[key] === "string")
|
|
49
|
+
dst[key] = src[key];
|
|
50
|
+
}
|
|
51
|
+
function isStringArray(v) {
|
|
52
|
+
return Array.isArray(v) && v.every((x) => typeof x === "string");
|
|
53
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { join, relative } from "node:path";
|
|
5
|
+
import { writeJson } from "../fsutil.js";
|
|
6
|
+
import { readManifest } from "../manifest.js";
|
|
7
|
+
import { installedPluginDir, lockPath } from "../paths.js";
|
|
8
|
+
import { readLock } from "../lock.js";
|
|
9
|
+
/**
|
|
10
|
+
* Claude Code agent.
|
|
11
|
+
*
|
|
12
|
+
* Claude consumes plugins through its own marketplace system, so we emit a
|
|
13
|
+
* Claude-shaped catalog at `<pluginsDir>/.claude-plugin/marketplace.json` and
|
|
14
|
+
* drive everything through the `claude plugin` CLI (which owns ~/.claude across
|
|
15
|
+
* versions) rather than hand-editing Claude's internal state.
|
|
16
|
+
*/
|
|
17
|
+
const MARKETPLACE = "adg";
|
|
18
|
+
function toPosix(p) {
|
|
19
|
+
return p.split("\\").join("/");
|
|
20
|
+
}
|
|
21
|
+
function claudeHome(env) {
|
|
22
|
+
return env.CLAUDE_CONFIG_DIR?.trim() || join(homedir(), ".claude");
|
|
23
|
+
}
|
|
24
|
+
function available() {
|
|
25
|
+
return spawnSync("claude", ["plugin", "--help"], { stdio: "ignore" }).status === 0;
|
|
26
|
+
}
|
|
27
|
+
function run(args) {
|
|
28
|
+
const r = spawnSync("claude", args, { encoding: "utf8" });
|
|
29
|
+
return { ok: r.status === 0, out: `${r.stdout ?? ""}${r.stderr ?? ""}` };
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Write a Claude marketplace catalog listing every installed plugin, each
|
|
33
|
+
* `source` pointing at its on-disk directory (relative to the catalog).
|
|
34
|
+
*/
|
|
35
|
+
export function writeClaudeCatalog(pluginsDir, name = MARKETPLACE) {
|
|
36
|
+
const lock = readLock(lockPath(pluginsDir));
|
|
37
|
+
const plugins = [];
|
|
38
|
+
for (const [pname, entry] of Object.entries(lock.plugins)) {
|
|
39
|
+
const dir = installedPluginDir(pluginsDir, pname, entry.origin);
|
|
40
|
+
let description = "";
|
|
41
|
+
let author;
|
|
42
|
+
let category;
|
|
43
|
+
try {
|
|
44
|
+
const m = readManifest(dir);
|
|
45
|
+
description = m.description;
|
|
46
|
+
author = m.author;
|
|
47
|
+
category = m.category;
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// no manifest on disk — list it minimally so the catalog stays complete
|
|
51
|
+
}
|
|
52
|
+
const rel = toPosix(relative(pluginsDir, dir)) || pname;
|
|
53
|
+
plugins.push({
|
|
54
|
+
name: pname,
|
|
55
|
+
description,
|
|
56
|
+
source: `./${rel}`,
|
|
57
|
+
...(author ? { author } : {}),
|
|
58
|
+
...(category ? { category } : {}),
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
const catalog = {
|
|
62
|
+
$schema: "https://anthropic.com/claude-code/marketplace.schema.json",
|
|
63
|
+
name,
|
|
64
|
+
description: "ADG-managed plugins",
|
|
65
|
+
owner: { name: "ADG" },
|
|
66
|
+
plugins,
|
|
67
|
+
};
|
|
68
|
+
const file = join(pluginsDir, ".claude-plugin", "marketplace.json");
|
|
69
|
+
writeJson(file, catalog);
|
|
70
|
+
return { file, name };
|
|
71
|
+
}
|
|
72
|
+
/** Register the ADG store as a Claude marketplace (add, or update if present). */
|
|
73
|
+
function syncMarketplace(pluginsDir) {
|
|
74
|
+
const list = run(["plugin", "marketplace", "list"]);
|
|
75
|
+
if (list.ok && list.out.includes(MARKETPLACE))
|
|
76
|
+
run(["plugin", "marketplace", "update", MARKETPLACE]);
|
|
77
|
+
else
|
|
78
|
+
run(["plugin", "marketplace", "add", pluginsDir]);
|
|
79
|
+
}
|
|
80
|
+
export const claudeAgent = {
|
|
81
|
+
id: "claude",
|
|
82
|
+
displayName: "Claude Code",
|
|
83
|
+
adaptTarget: "claude",
|
|
84
|
+
detect: (env = process.env) => existsSync(claudeHome(env)),
|
|
85
|
+
available,
|
|
86
|
+
activate(ctx) {
|
|
87
|
+
if (!available())
|
|
88
|
+
return { agent: "claude", affected: [], skipped: true };
|
|
89
|
+
writeClaudeCatalog(ctx.pluginsDir);
|
|
90
|
+
syncMarketplace(ctx.pluginsDir);
|
|
91
|
+
const affected = [];
|
|
92
|
+
for (const p of ctx.plugins) {
|
|
93
|
+
if (run(["plugin", "install", `${p}@${MARKETPLACE}`, "--scope", ctx.scope]).ok)
|
|
94
|
+
affected.push(p);
|
|
95
|
+
}
|
|
96
|
+
return { agent: "claude", affected, skipped: false };
|
|
97
|
+
},
|
|
98
|
+
deactivate(ctx) {
|
|
99
|
+
if (!available())
|
|
100
|
+
return { agent: "claude", affected: [], skipped: true };
|
|
101
|
+
const affected = [];
|
|
102
|
+
for (const p of ctx.plugins) {
|
|
103
|
+
if (run(["plugin", "uninstall", p, "--scope", ctx.scope]).ok)
|
|
104
|
+
affected.push(p);
|
|
105
|
+
}
|
|
106
|
+
return { agent: "claude", affected, skipped: false };
|
|
107
|
+
},
|
|
108
|
+
refresh(ctx) {
|
|
109
|
+
if (!available())
|
|
110
|
+
return { agent: "claude", affected: [], skipped: true };
|
|
111
|
+
// Claude caches a copy on install and won't re-pull from a local marketplace,
|
|
112
|
+
// so uninstall (keeping data) then re-install to force a fresh copy.
|
|
113
|
+
for (const p of ctx.plugins)
|
|
114
|
+
run(["plugin", "uninstall", p, "--scope", ctx.scope, "--keep-data"]);
|
|
115
|
+
const act = claudeAgent.activate(ctx);
|
|
116
|
+
return { agent: "claude", affected: act.affected, skipped: act.skipped };
|
|
117
|
+
},
|
|
118
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { marketplacePath } from "../paths.js";
|
|
6
|
+
import { readMarketplace } from "../marketplace.js";
|
|
7
|
+
/**
|
|
8
|
+
* Codex agent.
|
|
9
|
+
*
|
|
10
|
+
* Codex natively discovers the `.agents/plugins/marketplace.json` ADG writes, so
|
|
11
|
+
* plugins show up as *available* — but aren't usable until installed with
|
|
12
|
+
* `codex plugin add`. We drive that via the `codex` CLI (which owns ~/.codex).
|
|
13
|
+
*/
|
|
14
|
+
function codexHome(env) {
|
|
15
|
+
return env.CODEX_HOME?.trim() || join(homedir(), ".codex");
|
|
16
|
+
}
|
|
17
|
+
function available() {
|
|
18
|
+
return spawnSync("codex", ["plugin", "--help"], { stdio: "ignore" }).status === 0;
|
|
19
|
+
}
|
|
20
|
+
function run(args) {
|
|
21
|
+
const r = spawnSync("codex", args, { encoding: "utf8" });
|
|
22
|
+
return { ok: r.status === 0, out: `${r.stdout ?? ""}${r.stderr ?? ""}` };
|
|
23
|
+
}
|
|
24
|
+
/** The marketplace name Codex sees, read from the generated marketplace.json. */
|
|
25
|
+
function marketplaceName(pluginsDir) {
|
|
26
|
+
return readMarketplace(marketplacePath(pluginsDir), "").name;
|
|
27
|
+
}
|
|
28
|
+
export const codexAgent = {
|
|
29
|
+
id: "codex",
|
|
30
|
+
displayName: "Codex",
|
|
31
|
+
adaptTarget: "codex",
|
|
32
|
+
detect: (env = process.env) => existsSync(codexHome(env)) || existsSync("/etc/codex"),
|
|
33
|
+
available,
|
|
34
|
+
activate(ctx) {
|
|
35
|
+
const mp = marketplaceName(ctx.pluginsDir);
|
|
36
|
+
if (!available())
|
|
37
|
+
return { agent: "codex", affected: [], skipped: true };
|
|
38
|
+
const affected = [];
|
|
39
|
+
for (const p of ctx.plugins) {
|
|
40
|
+
if (run(["plugin", "add", `${p}@${mp}`]).ok)
|
|
41
|
+
affected.push(p);
|
|
42
|
+
}
|
|
43
|
+
return { agent: "codex", affected, skipped: false };
|
|
44
|
+
},
|
|
45
|
+
deactivate(ctx) {
|
|
46
|
+
const mp = marketplaceName(ctx.pluginsDir);
|
|
47
|
+
if (!available())
|
|
48
|
+
return { agent: "codex", affected: [], skipped: true };
|
|
49
|
+
const affected = [];
|
|
50
|
+
for (const p of ctx.plugins) {
|
|
51
|
+
if (run(["plugin", "remove", `${p}@${mp}`]).ok)
|
|
52
|
+
affected.push(p);
|
|
53
|
+
}
|
|
54
|
+
return { agent: "codex", affected, skipped: false };
|
|
55
|
+
},
|
|
56
|
+
// `codex plugin add` is idempotent and re-copies into the cache, so re-adding
|
|
57
|
+
// is the refresh.
|
|
58
|
+
refresh(ctx) {
|
|
59
|
+
return codexAgent.activate(ctx);
|
|
60
|
+
},
|
|
61
|
+
};
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
import { registerAgent } from "./registry.
|
|
2
|
-
import { claudeAgent } from "./claude.
|
|
3
|
-
import { codexAgent } from "./codex.
|
|
4
|
-
|
|
1
|
+
import { registerAgent } from "./registry.js";
|
|
2
|
+
import { claudeAgent } from "./claude.js";
|
|
3
|
+
import { codexAgent } from "./codex.js";
|
|
5
4
|
// Built-in agents register on import. Third-party agents can `registerAgent()`
|
|
6
5
|
// their own implementation (stage 2: discover from config without core edits).
|
|
7
6
|
registerAgent(claudeAgent);
|
|
8
7
|
registerAgent(codexAgent);
|
|
9
|
-
|
|
10
|
-
export
|
|
11
|
-
export {
|
|
12
|
-
export { writeClaudeCatalog } from "./claude.ts";
|
|
8
|
+
export * from "./types.js";
|
|
9
|
+
export { registerAgent, getAgent, allAgents, detectedAgents, resolveAgents } from "./registry.js";
|
|
10
|
+
export { writeClaudeCatalog } from "./claude.js";
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The agent registry — the "factory": construction/lookup only. Orchestration
|
|
3
|
+
* (looping, dependency order, store writes) stays in the command layer.
|
|
4
|
+
*/
|
|
5
|
+
const REGISTRY = new Map();
|
|
6
|
+
export function registerAgent(agent) {
|
|
7
|
+
REGISTRY.set(agent.id, agent);
|
|
8
|
+
}
|
|
9
|
+
export function getAgent(id) {
|
|
10
|
+
return REGISTRY.get(id);
|
|
11
|
+
}
|
|
12
|
+
export function allAgents() {
|
|
13
|
+
return [...REGISTRY.values()];
|
|
14
|
+
}
|
|
15
|
+
/** Agents that appear installed on this machine. */
|
|
16
|
+
export function detectedAgents(env) {
|
|
17
|
+
return allAgents().filter((a) => a.detect(env));
|
|
18
|
+
}
|
|
19
|
+
/** Agents matching the given ids, or every registered agent when none are given. */
|
|
20
|
+
export function resolveAgents(targets) {
|
|
21
|
+
if (!targets)
|
|
22
|
+
return allAgents();
|
|
23
|
+
return targets.map((t) => getAgent(t)).filter((a) => a !== undefined);
|
|
24
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { ADAPTERS } from "../adapters/index.js";
|
|
3
|
+
import { readManifest } from "../manifest.js";
|
|
4
|
+
import { writeJson } from "../fsutil.js";
|
|
5
|
+
/**
|
|
6
|
+
* Generate runtime-specific manifests for the given targets from a plugin's
|
|
7
|
+
* .agents/.plugin.json. The output path honors the manifest's `adapters`
|
|
8
|
+
* mapping when present, otherwise falls back to the adapter's default path.
|
|
9
|
+
* An optional `selection` narrows what the generated manifests expose.
|
|
10
|
+
*/
|
|
11
|
+
export function adaptPlugin(pluginDir, targets, selection) {
|
|
12
|
+
const manifest = readManifest(pluginDir);
|
|
13
|
+
const results = [];
|
|
14
|
+
for (const target of targets) {
|
|
15
|
+
const adapter = ADAPTERS[target];
|
|
16
|
+
if (!adapter)
|
|
17
|
+
throw new Error(`unknown adapter target: ${target}`);
|
|
18
|
+
const { defaultPath, manifest: out } = adapter(pluginDir, manifest, selection);
|
|
19
|
+
// Output paths are ADG-internal conventions, not producer-configurable: each
|
|
20
|
+
// runtime mandates its own (.claude-plugin/ , .codex-plugin/).
|
|
21
|
+
const file = join(pluginDir, defaultPath);
|
|
22
|
+
writeJson(file, out);
|
|
23
|
+
results.push({ target, file });
|
|
24
|
+
}
|
|
25
|
+
return results;
|
|
26
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { cpSync, existsSync, mkdtempSync, readdirSync, rmSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join, resolve } from "node:path";
|
|
4
|
+
import { ADG_MANIFEST_PATH } from "../manifest.js";
|
|
5
|
+
import { writeJson, writeText } from "../fsutil.js";
|
|
6
|
+
import { installPlugin } from "./install.js";
|
|
7
|
+
import { ADG_SCHEMA_VERSION } from "../types.js";
|
|
8
|
+
/**
|
|
9
|
+
* Wrap a flat directory of `<name>/SKILL.md` skills into a single ADG plugin and
|
|
10
|
+
* install it. Skill folders are copied verbatim under the new plugin's skills/.
|
|
11
|
+
*/
|
|
12
|
+
export function importSkills(opts) {
|
|
13
|
+
const src = resolve(opts.skillsDir);
|
|
14
|
+
const names = readdirSync(src, { withFileTypes: true })
|
|
15
|
+
.filter((e) => e.isDirectory() && existsSync(join(src, e.name, "SKILL.md")))
|
|
16
|
+
.map((e) => e.name)
|
|
17
|
+
.filter((name) => !opts.prefix || name.startsWith(opts.prefix))
|
|
18
|
+
.sort();
|
|
19
|
+
if (names.length === 0) {
|
|
20
|
+
throw new Error(`no SKILL.md skills found in ${src}${opts.prefix ? ` with prefix "${opts.prefix}"` : ""}`);
|
|
21
|
+
}
|
|
22
|
+
const staging = mkdtempSync(join(tmpdir(), "adg-skills-"));
|
|
23
|
+
try {
|
|
24
|
+
const manifest = {
|
|
25
|
+
schemaVersion: ADG_SCHEMA_VERSION,
|
|
26
|
+
name: opts.as,
|
|
27
|
+
version: opts.version ?? "0.1.0",
|
|
28
|
+
description: opts.description ?? `Imported skills bundle (${names.length}).`,
|
|
29
|
+
skills: "./skills/",
|
|
30
|
+
strict: false,
|
|
31
|
+
};
|
|
32
|
+
writeJson(join(staging, ADG_MANIFEST_PATH), manifest);
|
|
33
|
+
for (const name of names) {
|
|
34
|
+
copySkill(join(src, name), join(staging, "skills", name));
|
|
35
|
+
}
|
|
36
|
+
writeText(join(staging, "README.md"), `# ${opts.as}\n\n${manifest.description}\n`);
|
|
37
|
+
return installPlugin({
|
|
38
|
+
source: staging,
|
|
39
|
+
pluginsDir: opts.pluginsDir,
|
|
40
|
+
origin: { type: "local", path: `./${opts.as}` },
|
|
41
|
+
marketplaceName: opts.marketplaceName,
|
|
42
|
+
now: opts.now,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
finally {
|
|
46
|
+
rmSync(staging, { recursive: true, force: true });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function copySkill(from, to) {
|
|
50
|
+
cpSync(from, to, { recursive: true });
|
|
51
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { ADG_MANIFEST_PATH, ADG_MARKETPLACE_PATH } from "../manifest.js";
|
|
4
|
+
import { writeJson, writeText } from "../fsutil.js";
|
|
5
|
+
import { ADG_SCHEMA_VERSION } from "../types.js";
|
|
6
|
+
const NAME_RE = /^[a-z0-9]+(-[a-z0-9]+)*$/;
|
|
7
|
+
/**
|
|
8
|
+
* Dispatch on the authoring scenario: a plugin (`.agents/.plugin.json`), a
|
|
9
|
+
* marketplace catalog (`.agents/.marketplace.json`), or `all` — a catalog root
|
|
10
|
+
* with one starter member plugin in a subdirectory.
|
|
11
|
+
*/
|
|
12
|
+
export function initScaffold(opts) {
|
|
13
|
+
switch (opts.type ?? "plugin") {
|
|
14
|
+
case "plugin":
|
|
15
|
+
return initPlugin(opts);
|
|
16
|
+
case "marketplace":
|
|
17
|
+
return initMarketplace(opts);
|
|
18
|
+
case "all":
|
|
19
|
+
return initAll(opts);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Scaffold a new ADG plugin directory: .agents/.plugin.json, a starter
|
|
24
|
+
* skill (SKILL.md) and README.md.
|
|
25
|
+
*/
|
|
26
|
+
export function initPlugin(opts) {
|
|
27
|
+
if (!NAME_RE.test(opts.name)) {
|
|
28
|
+
throw new Error(`plugin name must be kebab-case, got "${opts.name}"`);
|
|
29
|
+
}
|
|
30
|
+
const pluginDir = join(opts.dir, opts.name);
|
|
31
|
+
const manifestFile = join(pluginDir, ADG_MANIFEST_PATH);
|
|
32
|
+
if (existsSync(manifestFile)) {
|
|
33
|
+
throw new Error(`${manifestFile} already exists; refusing to overwrite`);
|
|
34
|
+
}
|
|
35
|
+
const skillName = opts.skill ?? "getting-started";
|
|
36
|
+
const manifest = {
|
|
37
|
+
schemaVersion: ADG_SCHEMA_VERSION,
|
|
38
|
+
name: opts.name,
|
|
39
|
+
version: "0.1.0",
|
|
40
|
+
description: opts.description ?? `${opts.name} plugin.`,
|
|
41
|
+
author: { name: opts.author ?? "Agent Directory Group" },
|
|
42
|
+
license: "Apache-2.0",
|
|
43
|
+
skills: "./skills/",
|
|
44
|
+
strict: true,
|
|
45
|
+
};
|
|
46
|
+
const created = [];
|
|
47
|
+
writeJson(manifestFile, manifest);
|
|
48
|
+
created.push(manifestFile);
|
|
49
|
+
const skillFile = join(pluginDir, "skills", skillName, "SKILL.md");
|
|
50
|
+
writeText(skillFile, `---\nname: ${skillName}\ndescription: Describe when this skill should trigger.\n---\n\n# ${skillName}\n\nDocument the skill's behavior here.\n`);
|
|
51
|
+
created.push(skillFile);
|
|
52
|
+
const readme = join(pluginDir, "README.md");
|
|
53
|
+
writeText(readme, `# ${opts.name}\n\n${manifest.description}\n`);
|
|
54
|
+
created.push(readme);
|
|
55
|
+
return { pluginDir, created };
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Scaffold a marketplace catalog: .agents/.marketplace.json (empty member list)
|
|
59
|
+
* and README.md. Members are added later, or scaffold them with `--type all`.
|
|
60
|
+
*/
|
|
61
|
+
export function initMarketplace(opts) {
|
|
62
|
+
if (!NAME_RE.test(opts.name)) {
|
|
63
|
+
throw new Error(`marketplace name must be kebab-case, got "${opts.name}"`);
|
|
64
|
+
}
|
|
65
|
+
const catalogDir = join(opts.dir, opts.name);
|
|
66
|
+
const catalogFile = join(catalogDir, ADG_MARKETPLACE_PATH);
|
|
67
|
+
if (existsSync(catalogFile)) {
|
|
68
|
+
throw new Error(`${catalogFile} already exists; refusing to overwrite`);
|
|
69
|
+
}
|
|
70
|
+
const description = opts.description ?? `${opts.name} marketplace.`;
|
|
71
|
+
const catalog = {
|
|
72
|
+
name: opts.name,
|
|
73
|
+
description,
|
|
74
|
+
...(opts.author ? { owner: { name: opts.author } } : {}),
|
|
75
|
+
plugins: [],
|
|
76
|
+
};
|
|
77
|
+
const created = [];
|
|
78
|
+
writeJson(catalogFile, catalog);
|
|
79
|
+
created.push(catalogFile);
|
|
80
|
+
const readme = join(catalogDir, "README.md");
|
|
81
|
+
writeText(readme, `# ${opts.name}\n\n${description}\n`);
|
|
82
|
+
created.push(readme);
|
|
83
|
+
return { pluginDir: catalogDir, created };
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Scaffold both: a catalog root plus one starter member plugin in a
|
|
87
|
+
* subdirectory (a plugin and a marketplace cannot share one `.agents/` dir, so
|
|
88
|
+
* the member lives under `<name>/<name>/`). The catalog lists the member.
|
|
89
|
+
*/
|
|
90
|
+
function initAll(opts) {
|
|
91
|
+
const market = initMarketplace(opts);
|
|
92
|
+
const member = initPlugin({ ...opts, dir: join(opts.dir, opts.name) });
|
|
93
|
+
// Link the starter member into the catalog (source relative to the catalog's
|
|
94
|
+
// grandparent, i.e. the repo root that holds `.agents/`).
|
|
95
|
+
const catalogFile = join(opts.dir, opts.name, ADG_MARKETPLACE_PATH);
|
|
96
|
+
const catalog = {
|
|
97
|
+
name: opts.name,
|
|
98
|
+
description: opts.description ?? `${opts.name} marketplace.`,
|
|
99
|
+
...(opts.author ? { owner: { name: opts.author } } : {}),
|
|
100
|
+
plugins: [{ name: opts.name, source: { source: "local", path: `./${opts.name}` } }],
|
|
101
|
+
};
|
|
102
|
+
writeJson(catalogFile, catalog);
|
|
103
|
+
return { pluginDir: join(opts.dir, opts.name), created: [...market.created, ...member.created] };
|
|
104
|
+
}
|