@web42/cli 0.2.7 → 0.2.9
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/commands/search.js +20 -15
- package/dist/commands/send.js +75 -41
- package/dist/commands/serve.d.ts +1 -1
- package/dist/commands/serve.js +160 -114
- package/dist/index.js +1 -19
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/dist/commands/config.d.ts +0 -2
- package/dist/commands/config.js +0 -27
- package/dist/commands/init.d.ts +0 -2
- package/dist/commands/init.js +0 -451
- package/dist/commands/install.d.ts +0 -3
- package/dist/commands/install.js +0 -231
- package/dist/commands/list.d.ts +0 -3
- package/dist/commands/list.js +0 -22
- package/dist/commands/pack.d.ts +0 -2
- package/dist/commands/pack.js +0 -210
- package/dist/commands/pull.d.ts +0 -2
- package/dist/commands/pull.js +0 -202
- package/dist/commands/push.d.ts +0 -2
- package/dist/commands/push.js +0 -374
- package/dist/commands/remix.d.ts +0 -2
- package/dist/commands/remix.js +0 -49
- package/dist/commands/sync.d.ts +0 -2
- package/dist/commands/sync.js +0 -98
- package/dist/commands/uninstall.d.ts +0 -3
- package/dist/commands/uninstall.js +0 -54
- package/dist/commands/update.d.ts +0 -3
- package/dist/commands/update.js +0 -59
- package/dist/platforms/base.d.ts +0 -82
- package/dist/platforms/base.js +0 -1
- package/dist/platforms/claude/__tests__/adapter.test.d.ts +0 -1
- package/dist/platforms/claude/__tests__/adapter.test.js +0 -257
- package/dist/platforms/claude/__tests__/security.test.d.ts +0 -1
- package/dist/platforms/claude/__tests__/security.test.js +0 -166
- package/dist/platforms/claude/adapter.d.ts +0 -34
- package/dist/platforms/claude/adapter.js +0 -525
- package/dist/platforms/claude/security.d.ts +0 -15
- package/dist/platforms/claude/security.js +0 -67
- package/dist/platforms/claude/templates.d.ts +0 -5
- package/dist/platforms/claude/templates.js +0 -22
- package/dist/platforms/openclaw/adapter.d.ts +0 -12
- package/dist/platforms/openclaw/adapter.js +0 -476
- package/dist/platforms/openclaw/templates.d.ts +0 -7
- package/dist/platforms/openclaw/templates.js +0 -369
- package/dist/platforms/registry.d.ts +0 -6
- package/dist/platforms/registry.js +0 -32
- package/dist/types/sync.d.ts +0 -74
- package/dist/types/sync.js +0 -7
- package/dist/utils/bundled-skills.d.ts +0 -6
- package/dist/utils/bundled-skills.js +0 -29
- package/dist/utils/secrets.d.ts +0 -32
- package/dist/utils/secrets.js +0 -118
- package/dist/utils/skill.d.ts +0 -6
- package/dist/utils/skill.js +0 -42
- package/dist/utils/sync.d.ts +0 -14
- package/dist/utils/sync.js +0 -242
package/dist/platforms/base.d.ts
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
export interface ConfigVariable {
|
|
2
|
-
key: string;
|
|
3
|
-
label: string;
|
|
4
|
-
description?: string;
|
|
5
|
-
required: boolean;
|
|
6
|
-
default?: string;
|
|
7
|
-
}
|
|
8
|
-
export interface PackOptions {
|
|
9
|
-
cwd: string;
|
|
10
|
-
outputDir: string;
|
|
11
|
-
dryRun?: boolean;
|
|
12
|
-
agentName?: string;
|
|
13
|
-
}
|
|
14
|
-
export interface PackedFile {
|
|
15
|
-
path: string;
|
|
16
|
-
content: string;
|
|
17
|
-
hash: string;
|
|
18
|
-
}
|
|
19
|
-
export interface PackResult {
|
|
20
|
-
files: PackedFile[];
|
|
21
|
-
configTemplate: Record<string, unknown> | null;
|
|
22
|
-
configVariables: ConfigVariable[];
|
|
23
|
-
ignorePatterns?: string[];
|
|
24
|
-
}
|
|
25
|
-
export interface InstallOptions {
|
|
26
|
-
agentSlug: string;
|
|
27
|
-
username: string;
|
|
28
|
-
workspacePath: string;
|
|
29
|
-
files: Array<{
|
|
30
|
-
path: string;
|
|
31
|
-
content: string | null;
|
|
32
|
-
content_hash: string;
|
|
33
|
-
}>;
|
|
34
|
-
configTemplate: Record<string, unknown> | null;
|
|
35
|
-
configAnswers: Record<string, string>;
|
|
36
|
-
version?: string;
|
|
37
|
-
}
|
|
38
|
-
export interface InstallResult {
|
|
39
|
-
filesWritten: number;
|
|
40
|
-
agentDir: string;
|
|
41
|
-
}
|
|
42
|
-
export interface UninstallOptions {
|
|
43
|
-
agentName: string;
|
|
44
|
-
workspacePath?: string;
|
|
45
|
-
}
|
|
46
|
-
export interface UninstallResult {
|
|
47
|
-
removed: boolean;
|
|
48
|
-
paths: string[];
|
|
49
|
-
}
|
|
50
|
-
export interface InstalledAgent {
|
|
51
|
-
name: string;
|
|
52
|
-
source?: string;
|
|
53
|
-
workspace: string;
|
|
54
|
-
}
|
|
55
|
-
export interface InitConfig {
|
|
56
|
-
name: string;
|
|
57
|
-
model?: string;
|
|
58
|
-
}
|
|
59
|
-
export interface AgentCandidate {
|
|
60
|
-
name: string;
|
|
61
|
-
description?: string;
|
|
62
|
-
model?: string;
|
|
63
|
-
skills: string[];
|
|
64
|
-
path: string;
|
|
65
|
-
}
|
|
66
|
-
export interface ResolvedSkill {
|
|
67
|
-
name: string;
|
|
68
|
-
sourcePath: string;
|
|
69
|
-
found: boolean;
|
|
70
|
-
}
|
|
71
|
-
export interface PlatformAdapter {
|
|
72
|
-
name: string;
|
|
73
|
-
home: string;
|
|
74
|
-
extractInitConfig(cwd: string): InitConfig | null;
|
|
75
|
-
pack(options: PackOptions): Promise<PackResult>;
|
|
76
|
-
install(options: InstallOptions): Promise<InstallResult>;
|
|
77
|
-
uninstall(options: UninstallOptions): Promise<UninstallResult>;
|
|
78
|
-
listInstalled(): Promise<InstalledAgent[]>;
|
|
79
|
-
discoverAgents?(cwd: string): AgentCandidate[];
|
|
80
|
-
resolveSkills?(skillNames: string[], cwd: string): ResolvedSkill[];
|
|
81
|
-
resolveInstallPath?(localName: string, global?: boolean): string;
|
|
82
|
-
}
|
package/dist/platforms/base.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,257 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
-
import { mkdirSync, writeFileSync, rmSync, existsSync } from "fs";
|
|
3
|
-
import { join } from "path";
|
|
4
|
-
import { tmpdir } from "os";
|
|
5
|
-
import { ClaudeAdapter } from "../adapter.js";
|
|
6
|
-
function createTempDir() {
|
|
7
|
-
const dir = join(tmpdir(), `claude-adapter-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
8
|
-
mkdirSync(dir, { recursive: true });
|
|
9
|
-
return dir;
|
|
10
|
-
}
|
|
11
|
-
describe("ClaudeAdapter", () => {
|
|
12
|
-
let adapter;
|
|
13
|
-
let tempDir;
|
|
14
|
-
beforeEach(() => {
|
|
15
|
-
adapter = new ClaudeAdapter();
|
|
16
|
-
tempDir = createTempDir();
|
|
17
|
-
// Override home so discoverAgents/resolveSkills don't pick up the real
|
|
18
|
-
// ~/.claude directory during tests, making results fully deterministic.
|
|
19
|
-
adapter.home = tempDir;
|
|
20
|
-
});
|
|
21
|
-
afterEach(() => {
|
|
22
|
-
if (existsSync(tempDir)) {
|
|
23
|
-
rmSync(tempDir, { recursive: true, force: true });
|
|
24
|
-
}
|
|
25
|
-
});
|
|
26
|
-
describe("discoverAgents", () => {
|
|
27
|
-
it("finds agents in cwd/agents/", () => {
|
|
28
|
-
const agentsDir = join(tempDir, "agents");
|
|
29
|
-
mkdirSync(agentsDir, { recursive: true });
|
|
30
|
-
writeFileSync(join(agentsDir, "test-agent.md"), `---
|
|
31
|
-
name: test-agent
|
|
32
|
-
description: A test agent
|
|
33
|
-
model: sonnet
|
|
34
|
-
skills:
|
|
35
|
-
- skill-one
|
|
36
|
-
---
|
|
37
|
-
|
|
38
|
-
You are a test agent.
|
|
39
|
-
`);
|
|
40
|
-
const agents = adapter.discoverAgents(tempDir);
|
|
41
|
-
expect(agents).toHaveLength(1);
|
|
42
|
-
expect(agents[0].name).toBe("test-agent");
|
|
43
|
-
expect(agents[0].description).toBe("A test agent");
|
|
44
|
-
expect(agents[0].model).toBe("sonnet");
|
|
45
|
-
expect(agents[0].skills).toEqual(["skill-one"]);
|
|
46
|
-
});
|
|
47
|
-
it("parses frontmatter correctly with multiple skills", () => {
|
|
48
|
-
const agentsDir = join(tempDir, "agents");
|
|
49
|
-
mkdirSync(agentsDir, { recursive: true });
|
|
50
|
-
writeFileSync(join(agentsDir, "multi-skill.md"), `---
|
|
51
|
-
name: multi-skill-agent
|
|
52
|
-
description: Agent with many skills
|
|
53
|
-
model: opus
|
|
54
|
-
skills:
|
|
55
|
-
- flutter-conventions
|
|
56
|
-
- dart-testing
|
|
57
|
-
- a11y-flutter
|
|
58
|
-
tools: Read, Grep, Glob, Bash
|
|
59
|
-
---
|
|
60
|
-
|
|
61
|
-
Body content.
|
|
62
|
-
`);
|
|
63
|
-
const agents = adapter.discoverAgents(tempDir);
|
|
64
|
-
expect(agents).toHaveLength(1);
|
|
65
|
-
expect(agents[0].skills).toEqual([
|
|
66
|
-
"flutter-conventions",
|
|
67
|
-
"dart-testing",
|
|
68
|
-
"a11y-flutter",
|
|
69
|
-
]);
|
|
70
|
-
});
|
|
71
|
-
it("returns empty for no agents", () => {
|
|
72
|
-
const agents = adapter.discoverAgents(tempDir);
|
|
73
|
-
expect(agents).toEqual([]);
|
|
74
|
-
});
|
|
75
|
-
it("uses filename as fallback name when no frontmatter name", () => {
|
|
76
|
-
const agentsDir = join(tempDir, "agents");
|
|
77
|
-
mkdirSync(agentsDir, { recursive: true });
|
|
78
|
-
writeFileSync(join(agentsDir, "my-agent.md"), `---
|
|
79
|
-
description: No name in frontmatter
|
|
80
|
-
---
|
|
81
|
-
|
|
82
|
-
Body.
|
|
83
|
-
`);
|
|
84
|
-
const agents = adapter.discoverAgents(tempDir);
|
|
85
|
-
expect(agents[0].name).toBe("my-agent");
|
|
86
|
-
});
|
|
87
|
-
it("discovers multiple agents", () => {
|
|
88
|
-
const agentsDir = join(tempDir, "agents");
|
|
89
|
-
mkdirSync(agentsDir, { recursive: true });
|
|
90
|
-
writeFileSync(join(agentsDir, "agent-a.md"), "---\nname: agent-a\n---\nBody.");
|
|
91
|
-
writeFileSync(join(agentsDir, "agent-b.md"), "---\nname: agent-b\n---\nBody.");
|
|
92
|
-
const agents = adapter.discoverAgents(tempDir);
|
|
93
|
-
expect(agents).toHaveLength(2);
|
|
94
|
-
const names = agents.map((a) => a.name).sort();
|
|
95
|
-
expect(names).toEqual(["agent-a", "agent-b"]);
|
|
96
|
-
});
|
|
97
|
-
it("skips non-.md files", () => {
|
|
98
|
-
const agentsDir = join(tempDir, "agents");
|
|
99
|
-
mkdirSync(agentsDir, { recursive: true });
|
|
100
|
-
writeFileSync(join(agentsDir, "agent.md"), "---\nname: real-agent\n---\nBody.");
|
|
101
|
-
writeFileSync(join(agentsDir, "notes.txt"), "Just a text file");
|
|
102
|
-
writeFileSync(join(agentsDir, ".DS_Store"), "");
|
|
103
|
-
const agents = adapter.discoverAgents(tempDir);
|
|
104
|
-
expect(agents).toHaveLength(1);
|
|
105
|
-
expect(agents[0].name).toBe("real-agent");
|
|
106
|
-
});
|
|
107
|
-
});
|
|
108
|
-
describe("resolveSkills", () => {
|
|
109
|
-
it("finds skills in cwd/skills/", () => {
|
|
110
|
-
const skillDir = join(tempDir, "skills", "my-skill");
|
|
111
|
-
mkdirSync(skillDir, { recursive: true });
|
|
112
|
-
writeFileSync(join(skillDir, "SKILL.md"), "# My Skill\nDescription here.");
|
|
113
|
-
const resolved = adapter.resolveSkills(["my-skill"], tempDir);
|
|
114
|
-
expect(resolved).toHaveLength(1);
|
|
115
|
-
expect(resolved[0].name).toBe("my-skill");
|
|
116
|
-
expect(resolved[0].found).toBe(true);
|
|
117
|
-
expect(resolved[0].sourcePath).toBe(skillDir);
|
|
118
|
-
});
|
|
119
|
-
it("marks missing skills as not found", () => {
|
|
120
|
-
const resolved = adapter.resolveSkills(["nonexistent-skill"], tempDir);
|
|
121
|
-
expect(resolved).toHaveLength(1);
|
|
122
|
-
expect(resolved[0].found).toBe(false);
|
|
123
|
-
});
|
|
124
|
-
it("resolves multiple skills", () => {
|
|
125
|
-
mkdirSync(join(tempDir, "skills", "skill-a"), { recursive: true });
|
|
126
|
-
writeFileSync(join(tempDir, "skills", "skill-a", "SKILL.md"), "# Skill A");
|
|
127
|
-
mkdirSync(join(tempDir, "skills", "skill-b"), { recursive: true });
|
|
128
|
-
writeFileSync(join(tempDir, "skills", "skill-b", "SKILL.md"), "# Skill B");
|
|
129
|
-
const resolved = adapter.resolveSkills(["skill-a", "skill-b", "missing"], tempDir);
|
|
130
|
-
expect(resolved.filter((s) => s.found)).toHaveLength(2);
|
|
131
|
-
expect(resolved.find((s) => s.name === "missing")?.found).toBe(false);
|
|
132
|
-
});
|
|
133
|
-
});
|
|
134
|
-
describe("extractInitConfig", () => {
|
|
135
|
-
it("returns config when agent exists", () => {
|
|
136
|
-
const agentsDir = join(tempDir, "agents");
|
|
137
|
-
mkdirSync(agentsDir, { recursive: true });
|
|
138
|
-
writeFileSync(join(agentsDir, "test.md"), "---\nname: test-agent\nmodel: sonnet\n---\nBody.");
|
|
139
|
-
const config = adapter.extractInitConfig(tempDir);
|
|
140
|
-
expect(config).not.toBeNull();
|
|
141
|
-
expect(config.name).toBe("test-agent");
|
|
142
|
-
expect(config.model).toBe("sonnet");
|
|
143
|
-
});
|
|
144
|
-
it("returns null when no agents exist", () => {
|
|
145
|
-
const config = adapter.extractInitConfig(tempDir);
|
|
146
|
-
expect(config).toBeNull();
|
|
147
|
-
});
|
|
148
|
-
});
|
|
149
|
-
describe("pack", () => {
|
|
150
|
-
it("collects agent and referenced skills", async () => {
|
|
151
|
-
// Set up agent
|
|
152
|
-
const agentsDir = join(tempDir, "agents");
|
|
153
|
-
mkdirSync(agentsDir, { recursive: true });
|
|
154
|
-
writeFileSync(join(agentsDir, "my-agent.md"), `---
|
|
155
|
-
name: my-agent
|
|
156
|
-
skills:
|
|
157
|
-
- test-skill
|
|
158
|
-
---
|
|
159
|
-
|
|
160
|
-
Agent body.
|
|
161
|
-
`);
|
|
162
|
-
// Set up skill
|
|
163
|
-
const skillDir = join(tempDir, "skills", "test-skill");
|
|
164
|
-
mkdirSync(skillDir, { recursive: true });
|
|
165
|
-
writeFileSync(join(skillDir, "SKILL.md"), "# Test Skill\nSkill description.");
|
|
166
|
-
mkdirSync(join(skillDir, "references"), { recursive: true });
|
|
167
|
-
writeFileSync(join(skillDir, "references", "ref.md"), "Reference content.");
|
|
168
|
-
const result = await adapter.pack({
|
|
169
|
-
cwd: tempDir,
|
|
170
|
-
outputDir: ".web42/my-agent/dist",
|
|
171
|
-
agentName: "my-agent",
|
|
172
|
-
});
|
|
173
|
-
expect(result.files.length).toBeGreaterThanOrEqual(3);
|
|
174
|
-
const paths = result.files.map((f) => f.path);
|
|
175
|
-
expect(paths).toContain("agents/my-agent.md");
|
|
176
|
-
expect(paths).toContain("skills/test-skill/SKILL.md");
|
|
177
|
-
expect(paths).toContain("skills/test-skill/references/ref.md");
|
|
178
|
-
});
|
|
179
|
-
it("excludes .git and node_modules", async () => {
|
|
180
|
-
const agentsDir = join(tempDir, "agents");
|
|
181
|
-
mkdirSync(agentsDir, { recursive: true });
|
|
182
|
-
writeFileSync(join(agentsDir, "test.md"), "---\nname: test\n---\nBody.");
|
|
183
|
-
// Create files that should be excluded
|
|
184
|
-
mkdirSync(join(tempDir, ".git"), { recursive: true });
|
|
185
|
-
writeFileSync(join(tempDir, ".git", "config"), "git config");
|
|
186
|
-
mkdirSync(join(tempDir, "node_modules", "pkg"), { recursive: true });
|
|
187
|
-
writeFileSync(join(tempDir, "node_modules", "pkg", "index.js"), "module.exports = {}");
|
|
188
|
-
const result = await adapter.pack({
|
|
189
|
-
cwd: tempDir,
|
|
190
|
-
outputDir: ".web42/test/dist",
|
|
191
|
-
agentName: "test",
|
|
192
|
-
});
|
|
193
|
-
const paths = result.files.map((f) => f.path);
|
|
194
|
-
expect(paths.some((p) => p.includes(".git"))).toBe(false);
|
|
195
|
-
expect(paths.some((p) => p.includes("node_modules"))).toBe(false);
|
|
196
|
-
});
|
|
197
|
-
it("applies template var sanitization", async () => {
|
|
198
|
-
const agentsDir = join(tempDir, "agents");
|
|
199
|
-
mkdirSync(agentsDir, { recursive: true });
|
|
200
|
-
writeFileSync(join(agentsDir, "test.md"), "---\nname: test\n---\nPath: ~/.claude/agents/test.md");
|
|
201
|
-
const result = await adapter.pack({
|
|
202
|
-
cwd: tempDir,
|
|
203
|
-
outputDir: ".web42/test/dist",
|
|
204
|
-
agentName: "test",
|
|
205
|
-
});
|
|
206
|
-
const agentFile = result.files.find((f) => f.path === "agents/test.md");
|
|
207
|
-
expect(agentFile?.content).toContain("{{CLAUDE_HOME}}");
|
|
208
|
-
expect(agentFile?.content).not.toContain("~/.claude/");
|
|
209
|
-
});
|
|
210
|
-
it("throws when agentName is not provided", async () => {
|
|
211
|
-
await expect(adapter.pack({ cwd: tempDir, outputDir: "dist" })).rejects.toThrow("agentName");
|
|
212
|
-
});
|
|
213
|
-
it("throws when agent file not found", async () => {
|
|
214
|
-
await expect(adapter.pack({ cwd: tempDir, outputDir: "dist", agentName: "nonexistent" })).rejects.toThrow("not found");
|
|
215
|
-
});
|
|
216
|
-
it("collects commands if present", async () => {
|
|
217
|
-
const agentsDir = join(tempDir, "agents");
|
|
218
|
-
mkdirSync(agentsDir, { recursive: true });
|
|
219
|
-
writeFileSync(join(agentsDir, "test.md"), "---\nname: test\n---\nBody.");
|
|
220
|
-
const commandsDir = join(tempDir, "commands");
|
|
221
|
-
mkdirSync(commandsDir, { recursive: true });
|
|
222
|
-
writeFileSync(join(commandsDir, "review.md"), "# Review command");
|
|
223
|
-
const result = await adapter.pack({
|
|
224
|
-
cwd: tempDir,
|
|
225
|
-
outputDir: "dist",
|
|
226
|
-
agentName: "test",
|
|
227
|
-
});
|
|
228
|
-
const paths = result.files.map((f) => f.path);
|
|
229
|
-
expect(paths).toContain("commands/review.md");
|
|
230
|
-
});
|
|
231
|
-
});
|
|
232
|
-
describe("install and uninstall", () => {
|
|
233
|
-
let installDir;
|
|
234
|
-
beforeEach(() => {
|
|
235
|
-
installDir = createTempDir();
|
|
236
|
-
});
|
|
237
|
-
afterEach(() => {
|
|
238
|
-
if (existsSync(installDir)) {
|
|
239
|
-
rmSync(installDir, { recursive: true, force: true });
|
|
240
|
-
}
|
|
241
|
-
});
|
|
242
|
-
it("rejects path traversal", async () => {
|
|
243
|
-
// We can't easily test the actual install since it writes to ~/.claude/
|
|
244
|
-
// but we can test the path traversal check by examining the adapter logic
|
|
245
|
-
const files = [
|
|
246
|
-
{ path: "../../../etc/passwd", content: "bad content", content_hash: "abc" },
|
|
247
|
-
];
|
|
248
|
-
// The adapter checks: resolve(CLAUDE_HOME, file.path).startsWith(resolve(CLAUDE_HOME))
|
|
249
|
-
// A path with ../ would resolve outside CLAUDE_HOME
|
|
250
|
-
const { resolve: pathResolve } = await import("path");
|
|
251
|
-
const { homedir: getHomedir } = await import("os");
|
|
252
|
-
const home = join(getHomedir(), ".claude");
|
|
253
|
-
const resolved = pathResolve(home, "../../../etc/passwd");
|
|
254
|
-
expect(resolved.startsWith(pathResolve(home))).toBe(false);
|
|
255
|
-
});
|
|
256
|
-
});
|
|
257
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { stripForbiddenFrontmatter } from "../security.js";
|
|
3
|
-
describe("stripForbiddenFrontmatter", () => {
|
|
4
|
-
it("strips hooks from frontmatter", () => {
|
|
5
|
-
const input = `---
|
|
6
|
-
name: test-agent
|
|
7
|
-
description: A test agent
|
|
8
|
-
hooks: some-hook-config
|
|
9
|
-
model: sonnet
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
Body content here.
|
|
13
|
-
`;
|
|
14
|
-
const { cleaned, stripped } = stripForbiddenFrontmatter(input);
|
|
15
|
-
expect(stripped).toEqual(["hooks"]);
|
|
16
|
-
expect(cleaned).not.toContain("hooks");
|
|
17
|
-
expect(cleaned).toContain("name: test-agent");
|
|
18
|
-
expect(cleaned).toContain("model: sonnet");
|
|
19
|
-
expect(cleaned).toContain("Body content here.");
|
|
20
|
-
});
|
|
21
|
-
it("strips mcpServers from frontmatter", () => {
|
|
22
|
-
const input = `---
|
|
23
|
-
name: test-agent
|
|
24
|
-
mcpServers: some-server
|
|
25
|
-
---
|
|
26
|
-
|
|
27
|
-
Body.
|
|
28
|
-
`;
|
|
29
|
-
const { cleaned, stripped } = stripForbiddenFrontmatter(input);
|
|
30
|
-
expect(stripped).toEqual(["mcpServers"]);
|
|
31
|
-
expect(cleaned).not.toContain("mcpServers");
|
|
32
|
-
expect(cleaned).toContain("name: test-agent");
|
|
33
|
-
});
|
|
34
|
-
it("strips permissionMode from frontmatter", () => {
|
|
35
|
-
const input = `---
|
|
36
|
-
name: test-agent
|
|
37
|
-
permissionMode: full
|
|
38
|
-
---
|
|
39
|
-
|
|
40
|
-
Body.
|
|
41
|
-
`;
|
|
42
|
-
const { cleaned, stripped } = stripForbiddenFrontmatter(input);
|
|
43
|
-
expect(stripped).toEqual(["permissionMode"]);
|
|
44
|
-
expect(cleaned).not.toContain("permissionMode");
|
|
45
|
-
});
|
|
46
|
-
it("strips multiple forbidden keys at once", () => {
|
|
47
|
-
const input = `---
|
|
48
|
-
name: test-agent
|
|
49
|
-
hooks: PostToolUse
|
|
50
|
-
mcpServers: my-server
|
|
51
|
-
permissionMode: full
|
|
52
|
-
model: sonnet
|
|
53
|
-
---
|
|
54
|
-
|
|
55
|
-
Body.
|
|
56
|
-
`;
|
|
57
|
-
const { cleaned, stripped } = stripForbiddenFrontmatter(input);
|
|
58
|
-
expect(stripped).toContain("hooks");
|
|
59
|
-
expect(stripped).toContain("mcpServers");
|
|
60
|
-
expect(stripped).toContain("permissionMode");
|
|
61
|
-
expect(stripped).toHaveLength(3);
|
|
62
|
-
expect(cleaned).toContain("name: test-agent");
|
|
63
|
-
expect(cleaned).toContain("model: sonnet");
|
|
64
|
-
expect(cleaned).not.toContain("hooks");
|
|
65
|
-
expect(cleaned).not.toContain("mcpServers");
|
|
66
|
-
expect(cleaned).not.toContain("permissionMode");
|
|
67
|
-
});
|
|
68
|
-
it("preserves all safe frontmatter keys", () => {
|
|
69
|
-
const input = `---
|
|
70
|
-
name: test-agent
|
|
71
|
-
description: A test agent
|
|
72
|
-
tools: Read, Grep, Glob, Bash
|
|
73
|
-
model: sonnet
|
|
74
|
-
---
|
|
75
|
-
|
|
76
|
-
Body.
|
|
77
|
-
`;
|
|
78
|
-
const { cleaned, stripped } = stripForbiddenFrontmatter(input);
|
|
79
|
-
expect(stripped).toEqual([]);
|
|
80
|
-
expect(cleaned).toBe(input);
|
|
81
|
-
});
|
|
82
|
-
it("handles content with no frontmatter", () => {
|
|
83
|
-
const input = "Just some markdown content.\nNo frontmatter here.";
|
|
84
|
-
const { cleaned, stripped } = stripForbiddenFrontmatter(input);
|
|
85
|
-
expect(stripped).toEqual([]);
|
|
86
|
-
expect(cleaned).toBe(input);
|
|
87
|
-
});
|
|
88
|
-
it("handles empty frontmatter", () => {
|
|
89
|
-
const input = `---
|
|
90
|
-
---
|
|
91
|
-
|
|
92
|
-
Body content.
|
|
93
|
-
`;
|
|
94
|
-
const { cleaned, stripped } = stripForbiddenFrontmatter(input);
|
|
95
|
-
expect(stripped).toEqual([]);
|
|
96
|
-
expect(cleaned).toBe(input);
|
|
97
|
-
});
|
|
98
|
-
it("returns list of stripped keys", () => {
|
|
99
|
-
const input = `---
|
|
100
|
-
name: test
|
|
101
|
-
hooks: something
|
|
102
|
-
mcpServers: something-else
|
|
103
|
-
---
|
|
104
|
-
|
|
105
|
-
Body.
|
|
106
|
-
`;
|
|
107
|
-
const { stripped } = stripForbiddenFrontmatter(input);
|
|
108
|
-
expect(stripped).toEqual(["hooks", "mcpServers"]);
|
|
109
|
-
});
|
|
110
|
-
it("preserves body content after frontmatter", () => {
|
|
111
|
-
const input = `---
|
|
112
|
-
name: test
|
|
113
|
-
hooks: should-be-removed
|
|
114
|
-
---
|
|
115
|
-
|
|
116
|
-
# Main Heading
|
|
117
|
-
|
|
118
|
-
This is the body content.
|
|
119
|
-
It has multiple lines.
|
|
120
|
-
|
|
121
|
-
## Section 2
|
|
122
|
-
|
|
123
|
-
More content here.
|
|
124
|
-
`;
|
|
125
|
-
const { cleaned } = stripForbiddenFrontmatter(input);
|
|
126
|
-
expect(cleaned).toContain("# Main Heading");
|
|
127
|
-
expect(cleaned).toContain("This is the body content.");
|
|
128
|
-
expect(cleaned).toContain("## Section 2");
|
|
129
|
-
expect(cleaned).toContain("More content here.");
|
|
130
|
-
});
|
|
131
|
-
it("handles multiline frontmatter values (indented lines)", () => {
|
|
132
|
-
const input = `---
|
|
133
|
-
name: test-agent
|
|
134
|
-
hooks:
|
|
135
|
-
PostToolUse:
|
|
136
|
-
- matcher: "Edit|Write"
|
|
137
|
-
PreToolUse:
|
|
138
|
-
- type: command
|
|
139
|
-
model: sonnet
|
|
140
|
-
skills:
|
|
141
|
-
- skill-one
|
|
142
|
-
- skill-two
|
|
143
|
-
---
|
|
144
|
-
|
|
145
|
-
Body.
|
|
146
|
-
`;
|
|
147
|
-
const { cleaned, stripped } = stripForbiddenFrontmatter(input);
|
|
148
|
-
expect(stripped).toEqual(["hooks"]);
|
|
149
|
-
expect(cleaned).toContain("name: test-agent");
|
|
150
|
-
expect(cleaned).toContain("model: sonnet");
|
|
151
|
-
expect(cleaned).toContain("skills:");
|
|
152
|
-
expect(cleaned).toContain(" - skill-one");
|
|
153
|
-
expect(cleaned).not.toContain("PostToolUse");
|
|
154
|
-
expect(cleaned).not.toContain("matcher");
|
|
155
|
-
});
|
|
156
|
-
it("handles malformed frontmatter (no closing ---)", () => {
|
|
157
|
-
const input = `---
|
|
158
|
-
name: test
|
|
159
|
-
hooks: something
|
|
160
|
-
No closing delimiter
|
|
161
|
-
`;
|
|
162
|
-
const { cleaned, stripped } = stripForbiddenFrontmatter(input);
|
|
163
|
-
expect(stripped).toEqual([]);
|
|
164
|
-
expect(cleaned).toBe(input);
|
|
165
|
-
});
|
|
166
|
-
});
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import type { InitConfig, InstalledAgent, InstallOptions, InstallResult, PackOptions, PackResult, PlatformAdapter, UninstallOptions, UninstallResult } from "../base.js";
|
|
2
|
-
declare const HARDCODED_EXCLUDES: string[];
|
|
3
|
-
export interface AgentCandidate {
|
|
4
|
-
name: string;
|
|
5
|
-
description?: string;
|
|
6
|
-
model?: string;
|
|
7
|
-
skills: string[];
|
|
8
|
-
path: string;
|
|
9
|
-
}
|
|
10
|
-
export interface ResolvedSkill {
|
|
11
|
-
name: string;
|
|
12
|
-
sourcePath: string;
|
|
13
|
-
found: boolean;
|
|
14
|
-
}
|
|
15
|
-
export declare class ClaudeAdapter implements PlatformAdapter {
|
|
16
|
-
name: string;
|
|
17
|
-
home: string;
|
|
18
|
-
/**
|
|
19
|
-
* Discover agent .md files in known locations.
|
|
20
|
-
*/
|
|
21
|
-
discoverAgents(cwd: string): AgentCandidate[];
|
|
22
|
-
/**
|
|
23
|
-
* Resolve skill directories from known locations.
|
|
24
|
-
*/
|
|
25
|
-
resolveSkills(skillNames: string[], cwd: string): ResolvedSkill[];
|
|
26
|
-
extractInitConfig(cwd: string): InitConfig | null;
|
|
27
|
-
pack(options: PackOptions): Promise<PackResult>;
|
|
28
|
-
install(options: InstallOptions): Promise<InstallResult>;
|
|
29
|
-
uninstall(options: UninstallOptions): Promise<UninstallResult>;
|
|
30
|
-
listInstalled(workspacePath?: string): Promise<InstalledAgent[]>;
|
|
31
|
-
resolveInstallPath(_localName: string, global?: boolean): string;
|
|
32
|
-
}
|
|
33
|
-
export declare const claudeAdapter: ClaudeAdapter;
|
|
34
|
-
export { HARDCODED_EXCLUDES as CLAUDE_HARDCODED_EXCLUDES };
|