agentwheel 0.6.0 → 0.7.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/README.md +17 -2
- package/dist/index.js +176 -68
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -33,10 +33,10 @@ No lock-in. No central gatekeeper. Your packages live in plain git repos, your c
|
|
|
33
33
|
|
|
34
34
|
---
|
|
35
35
|
|
|
36
|
-
> **Status: early (v0.
|
|
36
|
+
> **Status: early (v0.7).** The lifecycle core is real and tested — local/git/skillkit/vercel
|
|
37
37
|
> sources, optional registry discovery, plan/sync/update/drift/uninstall, overlays, eject/remember,
|
|
38
38
|
> profiles, runtime auto-detection, fleet targeting, asset-includes, selective installs,
|
|
39
|
-
> update notifications, rich JSON merge, and pluggable adapters.
|
|
39
|
+
> update notifications, full Claude/Codex adapters, rich JSON/TOML merge, and pluggable adapters.
|
|
40
40
|
> Expect sharp edges.
|
|
41
41
|
|
|
42
42
|
## What it does
|
|
@@ -260,6 +260,20 @@ agentwheel sync ./my-pack --adapter-config ./myco-internal.jsonc
|
|
|
260
260
|
Built-in adapters ship for common runtimes; declarative adapters need no code and stay private.
|
|
261
261
|
Programmatic adapters, for private runtime logic beyond file placement, require explicit `--allow-adapter-code`.
|
|
262
262
|
|
|
263
|
+
Built-in runtime targets:
|
|
264
|
+
|
|
265
|
+
| Runtime | Main targets |
|
|
266
|
+
|---|---|
|
|
267
|
+
| **OpenClaw** | `.openclaw/AGENTS.md`, `.openclaw/skills`, `.openclaw/rules`, `.openclaw/commands`, MCP/hooks/settings, semantic plugin planning |
|
|
268
|
+
| **Claude Code** | `.claude/CLAUDE.md`, `.claude/skills`, `.claude/commands`, `.claude/agents`, `.claude/rules`, `.claude/.mcp.json`, `.claude/settings.json` |
|
|
269
|
+
| **Codex CLI** | `.codex/AGENTS.md`, `.codex/skills`, `.codex/commands`, `.codex/agents`, `.codex/rules`, `.codex/config.toml`, `.codex/hooks.json` |
|
|
270
|
+
| **Hermes** | `.hermes/AGENTS.md`, `.hermes/skills`, `.hermes/rules`, `.hermes/commands`, MCP/hooks/settings |
|
|
271
|
+
| **GitHub Copilot** | `.github/copilot-instructions.md`, `.github/instructions`, `.github/prompts` |
|
|
272
|
+
|
|
273
|
+
Claude MCP and hooks/settings are merged as JSON. Codex MCP entries are merged into
|
|
274
|
+
`[mcp_servers]` in `.codex/config.toml` without deleting unrelated user config; hooks use
|
|
275
|
+
`.codex/hooks.json`.
|
|
276
|
+
|
|
263
277
|
Copilot support is intentionally file-drop only: instructions, rules, and prompt/command files are
|
|
264
278
|
placed in GitHub-native locations, while raw `SKILL.md` directories stay disabled until there is a
|
|
265
279
|
clear conversion format.
|
|
@@ -272,6 +286,7 @@ clear conversion format.
|
|
|
272
286
|
- [x] **v0.4** — runtime auto-detection; no `--target-root` needed for normal use; fleet config with named agents; global + project config merge; `--agent` and `--all`.
|
|
273
287
|
- [x] **v0.5** — asset-includes compose shared files into skills at install time; executable bits preserved; hashes include composed assets.
|
|
274
288
|
- [x] **v0.6** — selective installs with `--select`/`--skill`; required artifacts; cached npm update notifier.
|
|
289
|
+
- [x] **v0.7** — full Claude/Codex adapters; Codex TOML MCP merge; subagent enumeration; `--skill` selector fix.
|
|
275
290
|
|
|
276
291
|
## Design docs
|
|
277
292
|
|
package/dist/index.js
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
} from "./chunk-N2LZY7LO.js";
|
|
9
9
|
|
|
10
10
|
// src/cli/index.ts
|
|
11
|
-
import { mkdir as
|
|
11
|
+
import { mkdir as mkdir9, rm as rm8, writeFile as writeFile6 } from "fs/promises";
|
|
12
12
|
import { join as join20 } from "path";
|
|
13
13
|
import { Command } from "commander";
|
|
14
14
|
|
|
@@ -59,7 +59,7 @@ var targetMappingSchema = z2.object({
|
|
|
59
59
|
dest: z2.string().min(1),
|
|
60
60
|
enabled: z2.boolean().default(true),
|
|
61
61
|
semantic: z2.enum(["openclaw-plugin"]).optional(),
|
|
62
|
-
merge: z2.enum(["json-deep"]).optional()
|
|
62
|
+
merge: z2.enum(["json-deep", "codex-toml-mcp"]).optional()
|
|
63
63
|
});
|
|
64
64
|
var adapterSchema = z2.object({
|
|
65
65
|
name: z2.string().min(1),
|
|
@@ -86,8 +86,17 @@ async function loadAdapterConfig(path) {
|
|
|
86
86
|
// src/adapters/claude.ts
|
|
87
87
|
var claudeAdapter = {
|
|
88
88
|
name: "claude",
|
|
89
|
-
displayName: "Claude",
|
|
90
|
-
targets: {
|
|
89
|
+
displayName: "Claude Code",
|
|
90
|
+
targets: {
|
|
91
|
+
instructions: { enabled: true, dest: ".claude/CLAUDE.md" },
|
|
92
|
+
rules: { enabled: true, dest: ".claude/rules" },
|
|
93
|
+
skills: { enabled: true, dest: ".claude/skills" },
|
|
94
|
+
commands: { enabled: true, dest: ".claude/commands" },
|
|
95
|
+
subagents: { enabled: true, dest: ".claude/agents" },
|
|
96
|
+
mcp: { enabled: true, dest: ".claude/.mcp.json", merge: "json-deep" },
|
|
97
|
+
hooks: { enabled: true, dest: ".claude/settings.json", merge: "json-deep" },
|
|
98
|
+
settings: { enabled: true, dest: ".claude/settings.json", merge: "json-deep" }
|
|
99
|
+
}
|
|
91
100
|
};
|
|
92
101
|
|
|
93
102
|
// src/adapters/copilot.ts
|
|
@@ -107,8 +116,17 @@ var copilotAdapter = {
|
|
|
107
116
|
// src/adapters/codex.ts
|
|
108
117
|
var codexAdapter = {
|
|
109
118
|
name: "codex",
|
|
110
|
-
displayName: "Codex",
|
|
111
|
-
targets: {
|
|
119
|
+
displayName: "Codex CLI",
|
|
120
|
+
targets: {
|
|
121
|
+
instructions: { enabled: true, dest: ".codex/AGENTS.md" },
|
|
122
|
+
rules: { enabled: true, dest: ".codex/rules" },
|
|
123
|
+
skills: { enabled: true, dest: ".codex/skills" },
|
|
124
|
+
commands: { enabled: true, dest: ".codex/commands" },
|
|
125
|
+
subagents: { enabled: true, dest: ".codex/agents" },
|
|
126
|
+
mcp: { enabled: true, dest: ".codex/config.toml", merge: "codex-toml-mcp" },
|
|
127
|
+
hooks: { enabled: true, dest: ".codex/hooks.json", merge: "json-deep" },
|
|
128
|
+
settings: { enabled: true, dest: ".codex/settings.json", merge: "json-deep" }
|
|
129
|
+
}
|
|
112
130
|
};
|
|
113
131
|
|
|
114
132
|
// src/adapters/hermes.ts
|
|
@@ -282,7 +300,7 @@ var manifestEntrySchema = z3.object({
|
|
|
282
300
|
packageName: z3.string().min(1).optional(),
|
|
283
301
|
semanticCommand: z3.array(z3.string()).optional(),
|
|
284
302
|
executed: z3.boolean().optional(),
|
|
285
|
-
mergeStrategy: z3.enum(["json-deep"]).optional()
|
|
303
|
+
mergeStrategy: z3.enum(["json-deep", "codex-toml-mcp"]).optional()
|
|
286
304
|
});
|
|
287
305
|
var installManifestSchema = z3.object({
|
|
288
306
|
version: z3.literal(1),
|
|
@@ -355,6 +373,94 @@ function normalizeTargetRoot(path) {
|
|
|
355
373
|
return resolve3(path);
|
|
356
374
|
}
|
|
357
375
|
|
|
376
|
+
// src/install/toml-merge.ts
|
|
377
|
+
import { mkdir as mkdir2, readFile as readFile5, writeFile as writeFile3 } from "fs/promises";
|
|
378
|
+
import { dirname as dirname2 } from "path";
|
|
379
|
+
async function mergeCodexTomlMcp(sourcePath, destPath) {
|
|
380
|
+
const source = JSON.parse(await readFile5(sourcePath, "utf8"));
|
|
381
|
+
const servers = extractMcpServers(source);
|
|
382
|
+
const current = await pathExists(destPath) ? await readFile5(destPath, "utf8") : "";
|
|
383
|
+
const withoutManaged = removeManagedMcpSections(current, Object.keys(servers));
|
|
384
|
+
const merged = appendMcpServers(withoutManaged, servers);
|
|
385
|
+
await mkdir2(dirname2(destPath), { recursive: true });
|
|
386
|
+
await writeFile3(destPath, merged, "utf8");
|
|
387
|
+
}
|
|
388
|
+
function extractMcpServers(source) {
|
|
389
|
+
const raw = isRecord2(source.mcpServers) ? source.mcpServers : source;
|
|
390
|
+
const servers = {};
|
|
391
|
+
for (const [name, value] of Object.entries(raw)) {
|
|
392
|
+
if (!isRecord2(value)) continue;
|
|
393
|
+
servers[name] = value;
|
|
394
|
+
}
|
|
395
|
+
if (Object.keys(servers).length === 0) {
|
|
396
|
+
throw new Error("Codex MCP TOML merge needs a JSON object with mcpServers");
|
|
397
|
+
}
|
|
398
|
+
return servers;
|
|
399
|
+
}
|
|
400
|
+
function removeManagedMcpSections(content, serverNames) {
|
|
401
|
+
if (serverNames.length === 0 || content.trim() === "") return content;
|
|
402
|
+
const names = new Set(serverNames);
|
|
403
|
+
const lines = content.split(/\r?\n/);
|
|
404
|
+
const kept = [];
|
|
405
|
+
let skipping = false;
|
|
406
|
+
for (const line of lines) {
|
|
407
|
+
const section = line.match(/^\s*\[([^\]]+)]\s*$/)?.[1];
|
|
408
|
+
if (section) {
|
|
409
|
+
const match = section.match(/^mcp_servers\.([^.\]]+)(?:\.|$)/);
|
|
410
|
+
skipping = match ? names.has(unquoteTomlKey(match[1] ?? "")) : false;
|
|
411
|
+
}
|
|
412
|
+
if (!skipping) kept.push(line);
|
|
413
|
+
}
|
|
414
|
+
return kept.join("\n").replace(/\n{3,}$/g, "\n\n");
|
|
415
|
+
}
|
|
416
|
+
function appendMcpServers(content, servers) {
|
|
417
|
+
const blocks = Object.entries(servers).sort(([a], [b]) => a.localeCompare(b)).map(([name, server]) => formatMcpServer(name, server));
|
|
418
|
+
const prefix = content.trimEnd();
|
|
419
|
+
return `${prefix ? `${prefix}
|
|
420
|
+
|
|
421
|
+
` : ""}${blocks.join("\n\n")}
|
|
422
|
+
`;
|
|
423
|
+
}
|
|
424
|
+
function formatMcpServer(name, server) {
|
|
425
|
+
const env = isRecord2(server.env) ? server.env : void 0;
|
|
426
|
+
const lines = [`[mcp_servers.${quoteTomlKey(name)}]`];
|
|
427
|
+
for (const [key, value] of Object.entries(server)) {
|
|
428
|
+
if (key === "env" || value === void 0) continue;
|
|
429
|
+
lines.push(`${quoteTomlKey(key)} = ${formatTomlValue(value)}`);
|
|
430
|
+
}
|
|
431
|
+
if (env && Object.keys(env).length > 0) {
|
|
432
|
+
lines.push("", `[mcp_servers.${quoteTomlKey(name)}.env]`);
|
|
433
|
+
for (const [key, value] of Object.entries(env)) {
|
|
434
|
+
lines.push(`${quoteTomlKey(key)} = ${formatTomlValue(value)}`);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
return lines.join("\n");
|
|
438
|
+
}
|
|
439
|
+
function formatTomlValue(value) {
|
|
440
|
+
if (typeof value === "string") return JSON.stringify(value);
|
|
441
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
442
|
+
if (Array.isArray(value)) return `[${value.map(formatTomlValue).join(", ")}]`;
|
|
443
|
+
if (isRecord2(value)) {
|
|
444
|
+
const entries = Object.entries(value).map(([key, child]) => `${quoteTomlKey(key)} = ${formatTomlValue(child)}`);
|
|
445
|
+
return `{ ${entries.join(", ")} }`;
|
|
446
|
+
}
|
|
447
|
+
return '""';
|
|
448
|
+
}
|
|
449
|
+
function quoteTomlKey(key) {
|
|
450
|
+
return /^[A-Za-z0-9_-]+$/.test(key) ? key : JSON.stringify(key);
|
|
451
|
+
}
|
|
452
|
+
function unquoteTomlKey(key) {
|
|
453
|
+
if (!key.startsWith('"')) return key;
|
|
454
|
+
try {
|
|
455
|
+
return JSON.parse(key);
|
|
456
|
+
} catch {
|
|
457
|
+
return key;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
function isRecord2(value) {
|
|
461
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
462
|
+
}
|
|
463
|
+
|
|
358
464
|
// src/install/apply.ts
|
|
359
465
|
var execFileAsync = promisify(execFile);
|
|
360
466
|
async function applyInstallPlan(plan, sourceLock, options = {}) {
|
|
@@ -420,6 +526,8 @@ async function applyInstallPlan(plan, sourceLock, options = {}) {
|
|
|
420
526
|
}
|
|
421
527
|
if (operation.mergeStrategy === "json-deep") {
|
|
422
528
|
await mergeJsonFile(operation.sourcePath, operation.destPath);
|
|
529
|
+
} else if (operation.mergeStrategy === "codex-toml-mcp") {
|
|
530
|
+
await mergeCodexTomlMcp(operation.sourcePath, operation.destPath);
|
|
423
531
|
} else {
|
|
424
532
|
await atomicCopy(operation.sourcePath, operation.destPath, operation.kind);
|
|
425
533
|
}
|
|
@@ -445,7 +553,7 @@ async function applyInstallPlan(plan, sourceLock, options = {}) {
|
|
|
445
553
|
artifactType: operation.artifactType,
|
|
446
554
|
artifactName: operation.artifactName,
|
|
447
555
|
kind: operation.kind,
|
|
448
|
-
hash: operation.mergeStrategy
|
|
556
|
+
hash: operation.mergeStrategy && operation.currentHash ? operation.currentHash : operation.desiredHash,
|
|
449
557
|
sourceHash: operation.desiredHash,
|
|
450
558
|
updatedAt: now,
|
|
451
559
|
channel: operation.channel,
|
|
@@ -574,7 +682,7 @@ async function createInstallPlan(bundle, adapter, targetRoot, manifest) {
|
|
|
574
682
|
}
|
|
575
683
|
continue;
|
|
576
684
|
}
|
|
577
|
-
if (op.mergeStrategy
|
|
685
|
+
if (op.mergeStrategy) {
|
|
578
686
|
const existing2 = manifestByPath.get(op.relativeDestPath);
|
|
579
687
|
const exists2 = await pathExists(op.destPath);
|
|
580
688
|
if (!exists2) {
|
|
@@ -682,7 +790,7 @@ function operationForArtifact(artifact, adapter, targetRoot) {
|
|
|
682
790
|
semanticCommand: openClawPluginInstallCommand({ path: sourcePath, dryRun: true })
|
|
683
791
|
};
|
|
684
792
|
}
|
|
685
|
-
const destPath = artifact.type === "instructions" || artifact.type === "settings" ? join3(targetRoot, target.dest) : join3(targetRoot, target.dest, artifact.name);
|
|
793
|
+
const destPath = artifact.type === "instructions" || artifact.type === "settings" || isFileTarget(target.dest) ? join3(targetRoot, target.dest) : join3(targetRoot, target.dest, artifact.name);
|
|
686
794
|
return {
|
|
687
795
|
action: "create",
|
|
688
796
|
artifactType: artifact.type,
|
|
@@ -698,6 +806,9 @@ function operationForArtifact(artifact, adapter, targetRoot) {
|
|
|
698
806
|
mergeStrategy: target.merge
|
|
699
807
|
};
|
|
700
808
|
}
|
|
809
|
+
function isFileTarget(dest) {
|
|
810
|
+
return /\.(json|jsonc|toml|md)$/i.test(dest);
|
|
811
|
+
}
|
|
701
812
|
function summarizePlan(plan) {
|
|
702
813
|
const summary = {
|
|
703
814
|
create: 0,
|
|
@@ -805,13 +916,13 @@ function formatPlan(plan) {
|
|
|
805
916
|
|
|
806
917
|
// src/source/git.ts
|
|
807
918
|
import { execFile as execFile2 } from "child_process";
|
|
808
|
-
import { mkdir as
|
|
919
|
+
import { mkdir as mkdir3, rm as rm3 } from "fs/promises";
|
|
809
920
|
import { homedir } from "os";
|
|
810
921
|
import { basename as basename2, join as join7, resolve as resolve5 } from "path";
|
|
811
922
|
import { promisify as promisify2 } from "util";
|
|
812
923
|
|
|
813
924
|
// src/model/package.ts
|
|
814
|
-
import { readFile as
|
|
925
|
+
import { readFile as readFile6 } from "fs/promises";
|
|
815
926
|
import { join as join5 } from "path";
|
|
816
927
|
import { parse as parse2, printParseErrorCode as printParseErrorCode2 } from "jsonc-parser";
|
|
817
928
|
import { z as z4 } from "zod";
|
|
@@ -837,7 +948,7 @@ async function findPackageManifestPath(root) {
|
|
|
837
948
|
async function readPackageManifest(root) {
|
|
838
949
|
const path = await findPackageManifestPath(root);
|
|
839
950
|
if (!path) return void 0;
|
|
840
|
-
const content = await
|
|
951
|
+
const content = await readFile6(path, "utf8");
|
|
841
952
|
const errors = [];
|
|
842
953
|
const parsed = parse2(content, errors, { allowTrailingComma: true, disallowComments: false });
|
|
843
954
|
if (errors.length > 0) {
|
|
@@ -939,7 +1050,7 @@ var LocalSourceDriver = class {
|
|
|
939
1050
|
}
|
|
940
1051
|
}
|
|
941
1052
|
}
|
|
942
|
-
for (const type of ["commands", "mcp", "hooks", "settings", "plugins"]) {
|
|
1053
|
+
for (const type of ["commands", "subagents", "mcp", "hooks", "settings", "plugins"]) {
|
|
943
1054
|
const dir = join6(root, type);
|
|
944
1055
|
if (!await pathExists(dir)) continue;
|
|
945
1056
|
artifacts.push(...await listGenericArtifacts(type, dir, type, resolved.packageName));
|
|
@@ -995,9 +1106,7 @@ async function listFromManifest(root, packageName) {
|
|
|
995
1106
|
if (stats.isDirectory()) {
|
|
996
1107
|
for (const entry of await sortedDirEntries(full)) {
|
|
997
1108
|
const child = join6(full, entry.name);
|
|
998
|
-
if (provide.type === "skills" && entry.isDirectory()) {
|
|
999
|
-
artifacts.push(await artifactForDir(provide.type, entry.name, child, join6(provide.path, entry.name), packageName, provide.assets, provide.required));
|
|
1000
|
-
} else if (provide.type === "plugins" && entry.isDirectory()) {
|
|
1109
|
+
if ((provide.type === "skills" || provide.type === "plugins" || provide.type === "subagents") && entry.isDirectory()) {
|
|
1001
1110
|
artifacts.push(await artifactForDir(provide.type, entry.name, child, join6(provide.path, entry.name), packageName, provide.assets, provide.required));
|
|
1002
1111
|
} else if (entry.isFile()) {
|
|
1003
1112
|
const name = provide.type === "rules" && entry.name.endsWith(".md") ? entry.name : entry.name;
|
|
@@ -1070,7 +1179,7 @@ var GitSourceDriver = class {
|
|
|
1070
1179
|
}
|
|
1071
1180
|
async fetch(resolved) {
|
|
1072
1181
|
const parsed = parseGitSource(resolved.source);
|
|
1073
|
-
await
|
|
1182
|
+
await mkdir3(resolve5(resolved.resolvedPath, ".."), { recursive: true });
|
|
1074
1183
|
if (!await pathExists(join7(resolved.resolvedPath, ".git"))) {
|
|
1075
1184
|
await rm3(resolved.resolvedPath, { recursive: true, force: true });
|
|
1076
1185
|
await git(["clone", "--no-tags", parsed.url, resolved.resolvedPath]);
|
|
@@ -1141,14 +1250,14 @@ async function git(args) {
|
|
|
1141
1250
|
}
|
|
1142
1251
|
|
|
1143
1252
|
// src/source/skillkit.ts
|
|
1144
|
-
import { cp, mkdir as
|
|
1253
|
+
import { cp, mkdir as mkdir4, readFile as readFile7, rm as rm4 } from "fs/promises";
|
|
1145
1254
|
import { homedir as homedir2 } from "os";
|
|
1146
|
-
import { basename as basename4, dirname as
|
|
1255
|
+
import { basename as basename4, dirname as dirname4, join as join9, resolve as resolve6 } from "path";
|
|
1147
1256
|
import * as defaultSkillKit from "@skillkit/core";
|
|
1148
1257
|
|
|
1149
1258
|
// src/source/skill-artifacts.ts
|
|
1150
1259
|
import { readdir as readdir2, stat as stat3 } from "fs/promises";
|
|
1151
|
-
import { basename as basename3, dirname as
|
|
1260
|
+
import { basename as basename3, dirname as dirname3, extname as extname2, join as join8 } from "path";
|
|
1152
1261
|
async function artifactsFromSkillPaths(paths, packageName) {
|
|
1153
1262
|
const artifacts = [];
|
|
1154
1263
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -1185,7 +1294,7 @@ async function artifactFromSkillPath(item, packageName) {
|
|
|
1185
1294
|
};
|
|
1186
1295
|
}
|
|
1187
1296
|
if (stats.isFile() && basename3(item.path).toLowerCase() === "skill.md") {
|
|
1188
|
-
const dir =
|
|
1297
|
+
const dir = dirname3(item.path);
|
|
1189
1298
|
const name = sanitizeSkillName(item.name ?? basename3(dir));
|
|
1190
1299
|
return {
|
|
1191
1300
|
type: "skills",
|
|
@@ -1268,7 +1377,7 @@ var SkillKitSourceDriver = class {
|
|
|
1268
1377
|
if (!provider?.clone) {
|
|
1269
1378
|
throw new Error("SkillKit provider API unavailable or cannot resolve source. Expected @skillkit/core detectProvider().clone().");
|
|
1270
1379
|
}
|
|
1271
|
-
await
|
|
1380
|
+
await mkdir4(dirname4(resolved.resolvedPath), { recursive: true });
|
|
1272
1381
|
const result = await provider.clone(providerSpec, resolved.resolvedPath, {});
|
|
1273
1382
|
if (!result.success || !result.path) {
|
|
1274
1383
|
throw new Error(`SkillKit provider failed to fetch ${spec}: ${result.error ?? "unknown error"}`);
|
|
@@ -1314,7 +1423,7 @@ var SkillKitSourceDriver = class {
|
|
|
1314
1423
|
for (const skill of this.discover(resolved.resolvedPath)) {
|
|
1315
1424
|
const skillMd = join9(skill.path, "SKILL.md");
|
|
1316
1425
|
if (await pathExists(skillMd)) {
|
|
1317
|
-
this.core.translateSkill(await
|
|
1426
|
+
this.core.translateSkill(await readFile7(skillMd, "utf8"), "openclaw", { sourceFilename: "SKILL.md" });
|
|
1318
1427
|
}
|
|
1319
1428
|
}
|
|
1320
1429
|
return resolved;
|
|
@@ -1482,13 +1591,13 @@ function getSourceDriver(name = "local") {
|
|
|
1482
1591
|
}
|
|
1483
1592
|
|
|
1484
1593
|
// src/staging/staging.ts
|
|
1485
|
-
import { chmod, cp as cp3, mkdir as
|
|
1486
|
-
import { basename as basename7, dirname as
|
|
1594
|
+
import { chmod, cp as cp3, mkdir as mkdir6, mkdtemp, readdir as readdir4, stat as stat6 } from "fs/promises";
|
|
1595
|
+
import { basename as basename7, dirname as dirname6, join as join12, relative as relative2, resolve as resolve8, sep } from "path";
|
|
1487
1596
|
import { tmpdir as tmpdir2 } from "os";
|
|
1488
1597
|
|
|
1489
1598
|
// src/staging/customize.ts
|
|
1490
|
-
import { cp as cp2, mkdir as
|
|
1491
|
-
import { dirname as
|
|
1599
|
+
import { cp as cp2, mkdir as mkdir5, readdir as readdir3, readFile as readFile8, writeFile as writeFile4 } from "fs/promises";
|
|
1600
|
+
import { dirname as dirname5, join as join11 } from "path";
|
|
1492
1601
|
async function applyCustomizations(artifacts, options) {
|
|
1493
1602
|
let next = [...artifacts];
|
|
1494
1603
|
next = await applyReplacements(next, options, "override");
|
|
@@ -1503,11 +1612,11 @@ async function applyInstructionOverlay(artifacts, options) {
|
|
|
1503
1612
|
const index = artifacts.findIndex((artifact2) => artifact2.type === "instructions");
|
|
1504
1613
|
if (index < 0) return artifacts;
|
|
1505
1614
|
const artifact = artifacts[index];
|
|
1506
|
-
const managed = await
|
|
1507
|
-
const local = await
|
|
1615
|
+
const managed = await readFile8(artifact.stagedPath ?? artifact.sourcePath, "utf8");
|
|
1616
|
+
const local = await readFile8(overlayPath, "utf8");
|
|
1508
1617
|
const composedPath = join11(options.stageRoot, ".agentwheel-composed", "instructions", "AGENTS.md");
|
|
1509
|
-
await
|
|
1510
|
-
await
|
|
1618
|
+
await mkdir5(dirname5(composedPath), { recursive: true });
|
|
1619
|
+
await writeFile4(
|
|
1511
1620
|
composedPath,
|
|
1512
1621
|
[
|
|
1513
1622
|
"<!-- BEGIN agentwheel managed: upstream -->",
|
|
@@ -1568,7 +1677,7 @@ async function applyReplacements(artifacts, options, channel) {
|
|
|
1568
1677
|
const kind = entry.isDirectory() ? "dir" : "file";
|
|
1569
1678
|
const existing = byKey.get(`${type}:${entry.name}`);
|
|
1570
1679
|
const stagedPath = join11(options.stageRoot, ".agentwheel-composed", channel, type, entry.name);
|
|
1571
|
-
await
|
|
1680
|
+
await mkdir5(dirname5(stagedPath), { recursive: true });
|
|
1572
1681
|
await cp2(full, stagedPath, { recursive: kind === "dir", dereference: true });
|
|
1573
1682
|
byKey.set(`${type}:${entry.name}`, {
|
|
1574
1683
|
type,
|
|
@@ -1640,7 +1749,7 @@ async function stageSource(driver, source, options = {}) {
|
|
|
1640
1749
|
const stagedArtifacts = [];
|
|
1641
1750
|
for (const artifact of artifacts) {
|
|
1642
1751
|
const stagedPath = join12(root, artifact.relativePath);
|
|
1643
|
-
await
|
|
1752
|
+
await mkdir6(dirname6(stagedPath), { recursive: true });
|
|
1644
1753
|
await cp3(artifact.sourcePath, stagedPath, { recursive: artifact.kind === "dir", dereference: true });
|
|
1645
1754
|
await composeAssets(artifact, resolved.resolvedPath, stagedPath);
|
|
1646
1755
|
stagedArtifacts.push({
|
|
@@ -1699,7 +1808,7 @@ async function copyAsset(asset, source, dest) {
|
|
|
1699
1808
|
const sourceStats = await stat6(source);
|
|
1700
1809
|
if (sourceStats.isFile()) {
|
|
1701
1810
|
if (matchesAny(basename7(source), asset.include)) {
|
|
1702
|
-
await
|
|
1811
|
+
await mkdir6(dest, { recursive: true });
|
|
1703
1812
|
await copyAssetFile(source, join12(dest, basename7(source)), asset);
|
|
1704
1813
|
}
|
|
1705
1814
|
return;
|
|
@@ -1708,7 +1817,7 @@ async function copyAsset(asset, source, dest) {
|
|
|
1708
1817
|
throw new Error(`Asset include source is not a file or directory: ${source}`);
|
|
1709
1818
|
}
|
|
1710
1819
|
if (!asset.include?.length) {
|
|
1711
|
-
await
|
|
1820
|
+
await mkdir6(dirname6(dest), { recursive: true });
|
|
1712
1821
|
await cp3(source, dest, { recursive: true, dereference: true });
|
|
1713
1822
|
if (asset.mode === "copy") await normalizeCopiedModes(dest);
|
|
1714
1823
|
return;
|
|
@@ -1720,7 +1829,7 @@ async function copyAsset(asset, source, dest) {
|
|
|
1720
1829
|
}
|
|
1721
1830
|
}
|
|
1722
1831
|
async function copyAssetFile(source, dest, asset) {
|
|
1723
|
-
await
|
|
1832
|
+
await mkdir6(dirname6(dest), { recursive: true });
|
|
1724
1833
|
await cp3(source, dest, { dereference: true });
|
|
1725
1834
|
if (asset.mode === "copy") await chmod(dest, 420);
|
|
1726
1835
|
}
|
|
@@ -1768,9 +1877,9 @@ function matchesGlob(path, pattern) {
|
|
|
1768
1877
|
}
|
|
1769
1878
|
|
|
1770
1879
|
// src/model/workspace.ts
|
|
1771
|
-
import { readFile as
|
|
1880
|
+
import { readFile as readFile9 } from "fs/promises";
|
|
1772
1881
|
import { homedir as homedir3 } from "os";
|
|
1773
|
-
import { dirname as
|
|
1882
|
+
import { dirname as dirname7, join as join13, resolve as resolve9 } from "path";
|
|
1774
1883
|
import { z as z5 } from "zod";
|
|
1775
1884
|
var workspacePackageSchema = z5.object({
|
|
1776
1885
|
name: z5.string().min(1),
|
|
@@ -1817,7 +1926,7 @@ function workspaceConfigPath(workspaceRoot) {
|
|
|
1817
1926
|
async function readWorkspaceConfig(workspaceRoot) {
|
|
1818
1927
|
const path = workspaceConfigPath(workspaceRoot);
|
|
1819
1928
|
if (!await pathExists(path)) return emptyWorkspaceConfig();
|
|
1820
|
-
return workspaceConfigSchema.parse(JSON.parse(await
|
|
1929
|
+
return workspaceConfigSchema.parse(JSON.parse(await readFile9(path, "utf8")));
|
|
1821
1930
|
}
|
|
1822
1931
|
async function writeWorkspaceConfig(workspaceRoot, config) {
|
|
1823
1932
|
await writeJsonAtomic(workspaceConfigPath(workspaceRoot), workspaceConfigSchema.parse(config));
|
|
@@ -1835,7 +1944,7 @@ async function findWorkspaceRoot(start = process.cwd()) {
|
|
|
1835
1944
|
let current = resolve9(start);
|
|
1836
1945
|
while (true) {
|
|
1837
1946
|
if (await pathExists(workspaceConfigPath(current))) return current;
|
|
1838
|
-
const parent =
|
|
1947
|
+
const parent = dirname7(current);
|
|
1839
1948
|
if (parent === current) return resolve9(start);
|
|
1840
1949
|
current = parent;
|
|
1841
1950
|
}
|
|
@@ -1869,15 +1978,15 @@ function emptyWorkspaceConfig() {
|
|
|
1869
1978
|
}
|
|
1870
1979
|
async function readConfigPath(path) {
|
|
1871
1980
|
if (!await pathExists(path)) return emptyWorkspaceConfig();
|
|
1872
|
-
return workspaceConfigSchema.parse(JSON.parse(await
|
|
1981
|
+
return workspaceConfigSchema.parse(JSON.parse(await readFile9(path, "utf8")));
|
|
1873
1982
|
}
|
|
1874
1983
|
|
|
1875
1984
|
// src/lifecycle/customization.ts
|
|
1876
|
-
import { appendFile, cp as cp4, mkdir as
|
|
1877
|
-
import { dirname as
|
|
1985
|
+
import { appendFile, cp as cp4, mkdir as mkdir7, rm as rm5 } from "fs/promises";
|
|
1986
|
+
import { dirname as dirname8, join as join14 } from "path";
|
|
1878
1987
|
async function remember(workspaceRoot, runtime, text) {
|
|
1879
1988
|
const overlayPath = join14(workspaceRoot, ".agentwheel", "overlays", runtime, "instructions.local.md");
|
|
1880
|
-
await
|
|
1989
|
+
await mkdir7(dirname8(overlayPath), { recursive: true });
|
|
1881
1990
|
await appendFile(overlayPath, `${text.trim()}
|
|
1882
1991
|
`, "utf8");
|
|
1883
1992
|
return { overlayPath };
|
|
@@ -1902,7 +2011,7 @@ async function ejectArtifact(workspaceRoot, item) {
|
|
|
1902
2011
|
throw new Error(`Artifact not found: ${item}`);
|
|
1903
2012
|
}
|
|
1904
2013
|
const ejectedPath = join14(workspaceRoot, ".agentwheel", "ejected", ...parsed.packageName.split("/"), parsed.type, parsed.name);
|
|
1905
|
-
await
|
|
2014
|
+
await mkdir7(dirname8(ejectedPath), { recursive: true });
|
|
1906
2015
|
await rm5(ejectedPath, { recursive: true, force: true });
|
|
1907
2016
|
await cp4(artifact.stagedPath ?? artifact.sourcePath, ejectedPath, { recursive: artifact.kind === "dir", dereference: true });
|
|
1908
2017
|
return { ...parsed, ejectedPath };
|
|
@@ -1926,9 +2035,9 @@ import { rm as rm7 } from "fs/promises";
|
|
|
1926
2035
|
import { join as join16 } from "path";
|
|
1927
2036
|
|
|
1928
2037
|
// src/registry/client.ts
|
|
1929
|
-
import { readFile as
|
|
2038
|
+
import { readFile as readFile10, rm as rm6, stat as stat7 } from "fs/promises";
|
|
1930
2039
|
import { homedir as homedir4 } from "os";
|
|
1931
|
-
import { dirname as
|
|
2040
|
+
import { dirname as dirname9, join as join15, resolve as resolve10 } from "path";
|
|
1932
2041
|
import { fileURLToPath } from "url";
|
|
1933
2042
|
|
|
1934
2043
|
// src/model/registry.ts
|
|
@@ -2014,7 +2123,7 @@ var RegistryClient = class {
|
|
|
2014
2123
|
}
|
|
2015
2124
|
async readCache() {
|
|
2016
2125
|
if (!await pathExists(this.cachePath)) return void 0;
|
|
2017
|
-
return registryCacheSchema.parse(JSON.parse(await
|
|
2126
|
+
return registryCacheSchema.parse(JSON.parse(await readFile10(this.cachePath, "utf8")));
|
|
2018
2127
|
}
|
|
2019
2128
|
isExpired(cache, ttlMs) {
|
|
2020
2129
|
return this.now().getTime() - new Date(cache.fetchedAt).getTime() > ttlMs;
|
|
@@ -2033,10 +2142,10 @@ var RegistryClient = class {
|
|
|
2033
2142
|
if (await pathExists(filePath)) {
|
|
2034
2143
|
const fullPath = resolve10(filePath);
|
|
2035
2144
|
const stats = await stat7(fullPath);
|
|
2036
|
-
return
|
|
2145
|
+
return readFile10(stats.isDirectory() ? join15(fullPath, "index.json") : fullPath, "utf8");
|
|
2037
2146
|
}
|
|
2038
|
-
const resolved = await this.git.fetch(await this.git.resolve(source, { cacheRoot: join15(
|
|
2039
|
-
return
|
|
2147
|
+
const resolved = await this.git.fetch(await this.git.resolve(source, { cacheRoot: join15(dirname9(this.cachePath), "registry-repos") }));
|
|
2148
|
+
return readFile10(join15(resolved.resolvedPath, "index.json"), "utf8");
|
|
2040
2149
|
}
|
|
2041
2150
|
};
|
|
2042
2151
|
async function resolvePackageSource(source, workspaceRoot) {
|
|
@@ -2172,7 +2281,7 @@ function shouldUpdatePackage(pkg, lock) {
|
|
|
2172
2281
|
}
|
|
2173
2282
|
|
|
2174
2283
|
// src/runtime/target.ts
|
|
2175
|
-
import { basename as basename8, dirname as
|
|
2284
|
+
import { basename as basename8, dirname as dirname10, join as join18, resolve as resolve11 } from "path";
|
|
2176
2285
|
var runtimeMarkers = [
|
|
2177
2286
|
{ adapter: "openclaw", dirs: [".openclaw", ".clawdbot", ".moltbot"] },
|
|
2178
2287
|
{ adapter: "claude", dirs: [".claude"] },
|
|
@@ -2226,7 +2335,7 @@ async function detectRuntimeTarget(cwd = process.cwd(), adapterFilter) {
|
|
|
2226
2335
|
if (adapterFilter && marker.adapter !== adapterFilter) continue;
|
|
2227
2336
|
for (const dir of marker.dirs) {
|
|
2228
2337
|
if (basename8(root) === dir) {
|
|
2229
|
-
matches.push({ adapter: marker.adapter, targetRoot:
|
|
2338
|
+
matches.push({ adapter: marker.adapter, targetRoot: dirname10(root) });
|
|
2230
2339
|
} else if (await pathExists(join18(root, dir))) {
|
|
2231
2340
|
matches.push({ adapter: marker.adapter, targetRoot: root });
|
|
2232
2341
|
}
|
|
@@ -2260,9 +2369,9 @@ function dedupeTargets(matches) {
|
|
|
2260
2369
|
}
|
|
2261
2370
|
|
|
2262
2371
|
// src/cli/update-check.ts
|
|
2263
|
-
import { mkdir as
|
|
2372
|
+
import { mkdir as mkdir8, readFile as readFile11, writeFile as writeFile5 } from "fs/promises";
|
|
2264
2373
|
import { homedir as homedir5 } from "os";
|
|
2265
|
-
import { dirname as
|
|
2374
|
+
import { dirname as dirname11, join as join19 } from "path";
|
|
2266
2375
|
var DEFAULT_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
2267
2376
|
var DEFAULT_TIMEOUT_MS = 300;
|
|
2268
2377
|
var REGISTRY_URL = "https://registry.npmjs.org/agentwheel";
|
|
@@ -2306,7 +2415,7 @@ async function fetchLatestVersion(fetchImpl, timeoutMs) {
|
|
|
2306
2415
|
}
|
|
2307
2416
|
async function readCache(path) {
|
|
2308
2417
|
try {
|
|
2309
|
-
const parsed = JSON.parse(await
|
|
2418
|
+
const parsed = JSON.parse(await readFile11(path, "utf8"));
|
|
2310
2419
|
if (typeof parsed.checkedAt !== "string" || typeof parsed.latest !== "string") return void 0;
|
|
2311
2420
|
return { checkedAt: parsed.checkedAt, latest: parsed.latest };
|
|
2312
2421
|
} catch {
|
|
@@ -2314,8 +2423,8 @@ async function readCache(path) {
|
|
|
2314
2423
|
}
|
|
2315
2424
|
}
|
|
2316
2425
|
async function writeCache(path, cache) {
|
|
2317
|
-
await
|
|
2318
|
-
await
|
|
2426
|
+
await mkdir8(dirname11(path), { recursive: true });
|
|
2427
|
+
await writeFile5(path, `${JSON.stringify(cache, null, 2)}
|
|
2319
2428
|
`, "utf8");
|
|
2320
2429
|
}
|
|
2321
2430
|
function warnIfNewer(latest, current, stderr = process.stderr) {
|
|
@@ -2338,7 +2447,7 @@ function normalizeVersion(version) {
|
|
|
2338
2447
|
|
|
2339
2448
|
// src/cli/index.ts
|
|
2340
2449
|
var program = new Command();
|
|
2341
|
-
program.name("agentwheel").description("Multi-runtime agent artifact orchestrator").version("0.
|
|
2450
|
+
program.name("agentwheel").description("Multi-runtime agent artifact orchestrator").version("0.7.0").option("--no-update-check", "disable npm version update check", false);
|
|
2342
2451
|
program.command("init").argument("[kind]", "workspace or package", "workspace").option("--target-root <path>", "workspace root", process.cwd()).action(async (kind, options) => {
|
|
2343
2452
|
const root = normalizeTargetRoot(options.targetRoot);
|
|
2344
2453
|
if (kind === "package") {
|
|
@@ -2528,8 +2637,7 @@ async function buildPlan(source, target, options) {
|
|
|
2528
2637
|
adapter,
|
|
2529
2638
|
driver: options.driver,
|
|
2530
2639
|
mode: options.mode,
|
|
2531
|
-
select:
|
|
2532
|
-
skills: options.skills
|
|
2640
|
+
select: selectedArtifactsFromOptions(options)
|
|
2533
2641
|
});
|
|
2534
2642
|
return { plan: result.plan, bundle: result.bundle };
|
|
2535
2643
|
}
|
|
@@ -2624,9 +2732,9 @@ function filterUninstallPlanBySelection(plan, selected) {
|
|
|
2624
2732
|
};
|
|
2625
2733
|
}
|
|
2626
2734
|
async function initPackage(root) {
|
|
2627
|
-
await
|
|
2628
|
-
await
|
|
2629
|
-
await
|
|
2735
|
+
await mkdir9(join20(root, "instructions"), { recursive: true });
|
|
2736
|
+
await mkdir9(join20(root, "rules"), { recursive: true });
|
|
2737
|
+
await mkdir9(join20(root, "skills"), { recursive: true });
|
|
2630
2738
|
const manifestPath = join20(root, "agentwheel.json");
|
|
2631
2739
|
const manifest = {
|
|
2632
2740
|
schemaVersion: 1,
|
|
@@ -2638,9 +2746,9 @@ async function initPackage(root) {
|
|
|
2638
2746
|
{ type: "skills", path: "skills" }
|
|
2639
2747
|
]
|
|
2640
2748
|
};
|
|
2641
|
-
await
|
|
2749
|
+
await writeFile6(manifestPath, `${JSON.stringify(manifest, null, 2)}
|
|
2642
2750
|
`, "utf8");
|
|
2643
|
-
await
|
|
2751
|
+
await writeFile6(join20(root, "instructions", "AGENTS.md"), "# Agent Instructions\n", "utf8");
|
|
2644
2752
|
}
|
|
2645
2753
|
function formatUninstallResult(result) {
|
|
2646
2754
|
const removedLabel = result.removed === 1 ? "managed file" : "managed files";
|
|
@@ -2660,7 +2768,7 @@ function printRegistryEntries(entries) {
|
|
|
2660
2768
|
}
|
|
2661
2769
|
async function main() {
|
|
2662
2770
|
await maybeCheckForUpdate({
|
|
2663
|
-
currentVersion: "0.
|
|
2771
|
+
currentVersion: "0.7.0",
|
|
2664
2772
|
argv: process.argv,
|
|
2665
2773
|
env: process.env,
|
|
2666
2774
|
isTTY: process.stderr.isTTY === true
|