@web42/cli 0.1.17 → 0.2.4

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.
@@ -0,0 +1,166 @@
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
+ });
@@ -0,0 +1,34 @@
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 };