surf-cli 2.13.1 → 2.15.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 +65 -34
- package/agents/gpt-pro.md +19 -0
- package/dist/content/index.js +116 -0
- package/dist/content/index.js.map +1 -0
- package/dist/icons/icon-128.png +0 -0
- package/dist/icons/icon-16.png +0 -0
- package/dist/icons/icon-48.png +0 -0
- package/dist/manifest.json +2 -11
- package/dist/options/options.js +3 -3
- package/dist/options/options.js.map +1 -1
- package/dist/service-worker/index.js +261 -61
- package/dist/service-worker/index.js.map +1 -1
- package/native/browser-scheduler.cjs +348 -0
- package/native/browser-session-store.cjs +271 -0
- package/native/chatgpt-client-selection.cjs +20 -10
- package/native/chatgpt-client-ui.cjs +35 -7
- package/native/cli.cjs +333 -62
- package/native/do-executor.cjs +5 -0
- package/native/host-helpers.cjs +19 -3
- package/native/host-sessions.cjs +8 -1
- package/native/host.cjs +766 -19
- package/native/mcp-server.cjs +1 -1
- package/native/oracle-cli.cjs +2 -2
- package/native/oracle-jobs.cjs +25 -3
- package/native/playbook-cli.cjs +16 -3
- package/native/surf-error.cjs +47 -0
- package/native/tool-scope.cjs +107 -0
- package/native/workflow-definition.cjs +7 -0
- package/package.json +8 -2
- package/pi-extension/surf.ts +275 -2
- package/skills/surf/SKILL.md +52 -23
- package/dist/content/accessibility-tree.js +0 -11
- package/dist/content/accessibility-tree.js.map +0 -1
- package/dist/content/visual-indicator.js +0 -111
- package/dist/content/visual-indicator.js.map +0 -1
package/native/mcp-server.cjs
CHANGED
|
@@ -269,7 +269,7 @@ const TOOL_SCHEMAS = {
|
|
|
269
269
|
desc: "Ask ChatGPT through the browser session",
|
|
270
270
|
schema: {
|
|
271
271
|
query: z.string().describe("Question or prompt"),
|
|
272
|
-
model: z.string().optional().describe("ChatGPT model"),
|
|
272
|
+
model: z.string().optional().describe("ChatGPT model: instant, thinking, pro, gpt-5.5, gpt-5.6-sol, or a visible model label"),
|
|
273
273
|
"with-page": z.boolean().optional().describe("Include current page context"),
|
|
274
274
|
file: z.string().optional().describe("One attachment path"),
|
|
275
275
|
timeout: z.number().optional().describe("Timeout in seconds")
|
package/native/oracle-cli.cjs
CHANGED
|
@@ -30,8 +30,8 @@ Commands:
|
|
|
30
30
|
|
|
31
31
|
Ask/follow options:
|
|
32
32
|
--files <glob> Add context files (repeatable)
|
|
33
|
-
--model <model> Select
|
|
34
|
-
--effort <effort> Select
|
|
33
|
+
--model <model> Select model: instant, thinking, pro, gpt-5.5, gpt-5.6-sol
|
|
34
|
+
--effort <effort> Select effort: light, standard, extended, heavy, pro
|
|
35
35
|
--detach Return after dispatch
|
|
36
36
|
--allow-sensitive Allow deny-listed context files
|
|
37
37
|
|
package/native/oracle-jobs.cjs
CHANGED
|
@@ -35,6 +35,24 @@ function codedError(code, message, details = {}) {
|
|
|
35
35
|
return error;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
function promptDigest(prompt) {
|
|
39
|
+
return `sha256:${crypto.createHash("sha256").update(prompt).digest("hex")}`;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function hydrateJobMetadata(job, root = getPrivateStateRoot()) {
|
|
43
|
+
const prompt = job.promptDigest ? null : readPrivateFile(path.join(jobDirectory(job.id, root), "request.md"), {
|
|
44
|
+
root,
|
|
45
|
+
encoding: "utf8",
|
|
46
|
+
});
|
|
47
|
+
const legacyModel = job.modelRequested === undefined && job.modelVerified === undefined;
|
|
48
|
+
return {
|
|
49
|
+
...job,
|
|
50
|
+
modelRequested: job.modelRequested ?? (legacyModel && job.state === "created" ? job.model ?? null : null),
|
|
51
|
+
modelVerified: job.modelVerified ?? (legacyModel && job.state !== "created" ? job.model ?? null : null),
|
|
52
|
+
promptDigest: job.promptDigest ?? promptDigest(prompt),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
38
56
|
function readJobs(root = getPrivateStateRoot()) {
|
|
39
57
|
const base = oracleRoot(root);
|
|
40
58
|
if (!fs.existsSync(base)) return [];
|
|
@@ -44,7 +62,8 @@ function readJobs(root = getPrivateStateRoot()) {
|
|
|
44
62
|
.filter((id) => JOB_ID_PATTERN.test(id))
|
|
45
63
|
.sort((a, b) => b.localeCompare(a))
|
|
46
64
|
.map((id) => readPrivateJson(path.join(base, id, "job.json"), null, { root }))
|
|
47
|
-
.filter(Boolean)
|
|
65
|
+
.filter(Boolean)
|
|
66
|
+
.map((job) => hydrateJobMetadata(job, root));
|
|
48
67
|
}
|
|
49
68
|
|
|
50
69
|
function createJob({ prompt, contextManifest = {}, model = null, effortRequested = null, follow = null }) {
|
|
@@ -84,8 +103,11 @@ function createJob({ prompt, contextManifest = {}, model = null, effortRequested
|
|
|
84
103
|
id,
|
|
85
104
|
state: "created",
|
|
86
105
|
model,
|
|
106
|
+
modelRequested: model,
|
|
107
|
+
modelVerified: null,
|
|
87
108
|
effortRequested,
|
|
88
109
|
effortVerified: null,
|
|
110
|
+
promptDigest: promptDigest(prompt),
|
|
89
111
|
createdAt: now.toISOString(),
|
|
90
112
|
dispatchedAt: null,
|
|
91
113
|
awaitingAt: null,
|
|
@@ -110,7 +132,7 @@ function getJob(id) {
|
|
|
110
132
|
const root = getPrivateStateRoot();
|
|
111
133
|
const job = readPrivateJson(path.join(jobDirectory(id, root), "job.json"), null, { root });
|
|
112
134
|
if (!job) throw codedError("not_found", `oracle job not found: ${id}`);
|
|
113
|
-
return job;
|
|
135
|
+
return hydrateJobMetadata(job, root);
|
|
114
136
|
}
|
|
115
137
|
|
|
116
138
|
function getResponse(id) {
|
|
@@ -141,7 +163,7 @@ function markDispatched(id, { tabId, promptEcho, modelVerified, effortVerified }
|
|
|
141
163
|
dispatchedAt: new Date().toISOString(),
|
|
142
164
|
tabId,
|
|
143
165
|
...(promptEcho ? { promptEcho } : {}),
|
|
144
|
-
...(modelVerified ? { model: modelVerified } : {}),
|
|
166
|
+
...(modelVerified ? { model: modelVerified, modelVerified } : {}),
|
|
145
167
|
...(effortVerified ? { effortVerified } : {}),
|
|
146
168
|
});
|
|
147
169
|
}
|
package/native/playbook-cli.cjs
CHANGED
|
@@ -36,6 +36,11 @@ async function requestHost(endpoint, tool, args, options = {}) {
|
|
|
36
36
|
try {
|
|
37
37
|
const request = { type: "tool_request", method: "execute_tool", params: { tool, args }, id: `playbook-${Date.now()}-${Math.random()}` };
|
|
38
38
|
if (options.tabId) request.tabId = options.tabId;
|
|
39
|
+
if (options.session) {
|
|
40
|
+
request.session = options.session;
|
|
41
|
+
request.sessionSource = options.sessionSource || "environment";
|
|
42
|
+
}
|
|
43
|
+
if (options.admission) request.admission = options.admission;
|
|
39
44
|
return unwrapResponse(await transport.request(request, options.timeoutMs || 11 * 60 * 1000));
|
|
40
45
|
} finally {
|
|
41
46
|
await transport.close();
|
|
@@ -48,7 +53,7 @@ function runSpec(argv) {
|
|
|
48
53
|
const parsed = parseCommandArgs(argv.slice(offset));
|
|
49
54
|
const [playbook, op] = parsed.positional;
|
|
50
55
|
if (!playbook || !op) throw new Error(direct ? "Usage: surf use <playbook> <op> [--arg value]" : "Usage: surf pb run <playbook> <op> [--arg value]");
|
|
51
|
-
const reserved = new Set(["json", "no-lock", "tab-id", "write", "repeat", "retry-attempt", "override-in-doubt", "pin-built-in", "allow-script"]);
|
|
56
|
+
const reserved = new Set(["json", "no-lock", "no-wait", "session", "tab-id", "write", "repeat", "retry-attempt", "override-in-doubt", "pin-built-in", "allow-script"]);
|
|
52
57
|
const args = Object.fromEntries(Object.entries(parsed.options).filter(([name]) => !reserved.has(name)));
|
|
53
58
|
return { playbook, op, args, options: parsed.options };
|
|
54
59
|
}
|
|
@@ -73,7 +78,7 @@ function playbookCommandNeedsBrowser(argv) {
|
|
|
73
78
|
return subcommand === "record" && ["start", "stop", "discard"].includes(argv[2]);
|
|
74
79
|
}
|
|
75
80
|
|
|
76
|
-
async function handlePlaybookCli(argv, { endpoint, cwd = process.cwd() }) {
|
|
81
|
+
async function handlePlaybookCli(argv, { endpoint, cwd = process.cwd(), session, sessionSource, admission } = {}) {
|
|
77
82
|
if (!["playbook", "pb", "use"].includes(argv[0])) return { handled: false };
|
|
78
83
|
if (argv[0] === "use" || argv[1] === "run") {
|
|
79
84
|
const spec = runSpec(argv);
|
|
@@ -93,6 +98,9 @@ async function handlePlaybookCli(argv, { endpoint, cwd = process.cwd() }) {
|
|
|
93
98
|
};
|
|
94
99
|
const value = await requestHost(endpoint, "playbook.run", args, {
|
|
95
100
|
tabId: spec.options["tab-id"],
|
|
101
|
+
session: spec.options.session || session,
|
|
102
|
+
sessionSource: spec.options.session ? "explicit" : sessionSource,
|
|
103
|
+
admission: spec.options["no-wait"] === true ? { wait: false } : admission,
|
|
96
104
|
timeoutMs: resolveRequestDeadlineMs("playbook.run", args),
|
|
97
105
|
});
|
|
98
106
|
return { handled: true, value, json: spec.options.json === true };
|
|
@@ -118,7 +126,12 @@ async function handlePlaybookCli(argv, { endpoint, cwd = process.cwd() }) {
|
|
|
118
126
|
else if (action === "mark") args = { label: parsed.positional.slice(1).join(" ") };
|
|
119
127
|
else if (action === "stop") args = { draft: parsed.options.draft === true };
|
|
120
128
|
else if (!["status", "pause", "resume", "discard"].includes(action)) throw new Error("Unknown record command");
|
|
121
|
-
const value = await requestHost(endpoint, tool, args, {
|
|
129
|
+
const value = await requestHost(endpoint, tool, args, {
|
|
130
|
+
tabId: parsed.options["tab-id"],
|
|
131
|
+
session: parsed.options.session || session,
|
|
132
|
+
sessionSource: parsed.options.session ? "explicit" : sessionSource,
|
|
133
|
+
admission: parsed.options["no-wait"] === true ? { wait: false } : admission,
|
|
134
|
+
});
|
|
122
135
|
return { handled: true, value, json: parsed.options.json === true };
|
|
123
136
|
}
|
|
124
137
|
if (command === "suggest") return { handled: true, value: suggestions({ since: parsed.options.since || "1h" }), json: parsed.options.json === true };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
class SurfError extends Error {
|
|
2
|
+
constructor(code, message, details = {}) {
|
|
3
|
+
super(message);
|
|
4
|
+
this.name = "SurfError";
|
|
5
|
+
this.code = code;
|
|
6
|
+
Object.assign(this, details);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
toJSON() {
|
|
10
|
+
const value = { code: this.code, message: this.message };
|
|
11
|
+
for (const key of [
|
|
12
|
+
"session", "target", "lastUrl", "laneKey", "resourceKeys", "retryable", "recoveryCommand",
|
|
13
|
+
"queue", "reason", "browserEpoch", "expectedBrowserEpoch",
|
|
14
|
+
]) {
|
|
15
|
+
if (this[key] !== undefined) value[key] = this[key];
|
|
16
|
+
}
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function surfError(code, message, details = {}) {
|
|
22
|
+
return new SurfError(code, message, details);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function isSurfError(error) {
|
|
26
|
+
return Boolean(error && typeof error === "object" && typeof error.code === "string");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function fromExtensionError(result, fallbackCode = "browser_error") {
|
|
30
|
+
if (!result?.error) return null;
|
|
31
|
+
return surfError(result.errorCode || fallbackCode, result.error, result.errorDetails || {});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function recoveryFor(error) {
|
|
35
|
+
if (!error || typeof error !== "object") return null;
|
|
36
|
+
if (typeof error.recoveryCommand === "string" && error.recoveryCommand) return error.recoveryCommand;
|
|
37
|
+
if ((error.code === "tab_gone" || error.code === "session_epoch_stale") && error.session) {
|
|
38
|
+
return `surf session.reopen ${error.session}`;
|
|
39
|
+
}
|
|
40
|
+
if ((error.code === "tab_busy" || error.code === "browser_busy" || error.code === "resource_busy") && error.session) {
|
|
41
|
+
return `surf session.info ${error.session}`;
|
|
42
|
+
}
|
|
43
|
+
if (error.code === "browser_busy") return "surf session.list --refresh";
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
module.exports = { SurfError, surfError, isSurfError, fromExtensionError, recoveryFor };
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
const path = require("path");
|
|
2
|
+
|
|
3
|
+
const PROVIDER_TOOLS = new Set([
|
|
4
|
+
"chatgpt", "gemini", "perplexity", "grok", "kimi", "aistudio", "aistudio.build",
|
|
5
|
+
"oracle.ask", "oracle.result", "oracle.cancel",
|
|
6
|
+
]);
|
|
7
|
+
|
|
8
|
+
const HOST_TOOLS = new Set([
|
|
9
|
+
"wait",
|
|
10
|
+
"session.list",
|
|
11
|
+
"session.info",
|
|
12
|
+
"tab.unname", "tabs_unregister", "tab.named", "tabs_list_named",
|
|
13
|
+
]);
|
|
14
|
+
|
|
15
|
+
const BROWSER_READ_TOOLS = new Set([
|
|
16
|
+
"tab.list", "tabs_context", "list_tabs",
|
|
17
|
+
"window.list",
|
|
18
|
+
"history.list", "history.search",
|
|
19
|
+
"bookmark.list",
|
|
20
|
+
"downloads.search",
|
|
21
|
+
]);
|
|
22
|
+
|
|
23
|
+
const BROWSER_WRITE_TOOLS = new Set([
|
|
24
|
+
"session.new", "session.ensure", "session.close", "session.rebind", "session.reopen",
|
|
25
|
+
"tab.new", "new_tab", "tabs_create",
|
|
26
|
+
"tab.move", "tab.switch", "switch_tab",
|
|
27
|
+
"tab.group", "tab.ungroup",
|
|
28
|
+
"window.new", "window.close", "window.focus", "window.resize",
|
|
29
|
+
"smoke",
|
|
30
|
+
]);
|
|
31
|
+
|
|
32
|
+
const BROWSER_WRITE_TARGETED_TOOLS = new Set([
|
|
33
|
+
"cookie.set", "cookie.clear", "cookie.clear-all",
|
|
34
|
+
"bookmark.add", "bookmark.remove",
|
|
35
|
+
"playbook.run",
|
|
36
|
+
]);
|
|
37
|
+
|
|
38
|
+
const TAB_TOOLS = new Set([
|
|
39
|
+
"ai", "computer", "batch", "record", "animate-audit", "perf-audit",
|
|
40
|
+
"navigate", "go", "back", "forward", "reload", "tab.reload",
|
|
41
|
+
"screenshot", "snap", "resize",
|
|
42
|
+
"page.read", "read_page", "page.text", "get_page_text", "page.html", "page.save", "page.state",
|
|
43
|
+
"click", "left_click", "right_click", "double_click", "triple_click", "drag", "hover", "key", "submit",
|
|
44
|
+
"type", "smart_type", "find_and_type", "form_input", "form.fill", "select", "upload", "upload_image",
|
|
45
|
+
"scroll", "scroll.top", "scroll.bottom", "scroll.to", "scroll.info", "scroll_to_position",
|
|
46
|
+
"search", "locate.role", "locate.text", "locate.label", "element.styles",
|
|
47
|
+
"js", "javascript_tool", "eval",
|
|
48
|
+
"wait.element", "wait.url", "wait.network", "wait.dom", "wait.load", "health",
|
|
49
|
+
"frame.list", "frame.switch", "frame.main", "frame.js",
|
|
50
|
+
"dialog.accept", "dialog.dismiss", "dialog.info",
|
|
51
|
+
"console", "network", "network.get", "network.body", "network.curl", "network.path",
|
|
52
|
+
"network.origins", "network.clear", "network.stats", "network.export",
|
|
53
|
+
"emulate.network", "emulate.cpu", "emulate.geo", "emulate.device", "emulate.viewport", "emulate.touch",
|
|
54
|
+
"perf.start", "perf.stop", "perf.metrics",
|
|
55
|
+
"zoom", "cookie.list", "cookie.get",
|
|
56
|
+
"tab.name", "tabs_register",
|
|
57
|
+
"playbook.record.start", "playbook.record.stop", "playbook.record.status", "playbook.record.mark",
|
|
58
|
+
"playbook.record.pause", "playbook.record.resume", "playbook.record.discard",
|
|
59
|
+
]);
|
|
60
|
+
|
|
61
|
+
function hasExplicitTabCloseTarget(args = {}) {
|
|
62
|
+
return [args.id, args.tab_id, args.tabId, args.ids, args.tab_ids, args.tabIds]
|
|
63
|
+
.some((value) => value !== undefined && value !== null && value !== "");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function classifyTool(tool, args = {}) {
|
|
67
|
+
if (PROVIDER_TOOLS.has(tool) || tool.startsWith("oracle.")) {
|
|
68
|
+
const hostOnly = tool === "oracle.result" || tool === "oracle.cancel" || tool === "oracle.status" || tool === "oracle.list";
|
|
69
|
+
return hostOnly
|
|
70
|
+
? { scope: "host", targetUse: "host" }
|
|
71
|
+
: { scope: "provider", targetUse: "default-tab" };
|
|
72
|
+
}
|
|
73
|
+
if (tool === "session.list") {
|
|
74
|
+
return { scope: args.refresh ? "browser-read" : "host", targetUse: "host" };
|
|
75
|
+
}
|
|
76
|
+
if (tool === "session.info") {
|
|
77
|
+
return { scope: args.refresh ? "browser-read" : "host", targetUse: "host" };
|
|
78
|
+
}
|
|
79
|
+
if (tool === "tab.close" || tool === "close_tab") {
|
|
80
|
+
return hasExplicitTabCloseTarget(args)
|
|
81
|
+
? { scope: "browser-write", targetUse: "browser" }
|
|
82
|
+
: { scope: "browser-write", targetUse: "default-tab" };
|
|
83
|
+
}
|
|
84
|
+
if (HOST_TOOLS.has(tool)) return { scope: "host", targetUse: "host" };
|
|
85
|
+
if (BROWSER_READ_TOOLS.has(tool)) return { scope: "browser-read", targetUse: "browser" };
|
|
86
|
+
if (BROWSER_WRITE_TARGETED_TOOLS.has(tool)) {
|
|
87
|
+
return { scope: "browser-write", targetUse: "default-tab" };
|
|
88
|
+
}
|
|
89
|
+
if (BROWSER_WRITE_TOOLS.has(tool)) return { scope: "browser-write", targetUse: "browser" };
|
|
90
|
+
if (TAB_TOOLS.has(tool)) {
|
|
91
|
+
const resourceKeys = [];
|
|
92
|
+
if (tool === "network.export" && typeof args.output === "string") resourceKeys.push(`file:${path.resolve(args.output)}`);
|
|
93
|
+
if (tool.startsWith("playbook.record.")) resourceKeys.push("playbook-recorder");
|
|
94
|
+
return { scope: "tab", targetUse: "default-tab", resourceKeys };
|
|
95
|
+
}
|
|
96
|
+
return { scope: "browser-write", targetUse: "browser", conservative: true };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
module.exports = {
|
|
100
|
+
BROWSER_READ_TOOLS,
|
|
101
|
+
BROWSER_WRITE_TARGETED_TOOLS,
|
|
102
|
+
BROWSER_WRITE_TOOLS,
|
|
103
|
+
HOST_TOOLS,
|
|
104
|
+
PROVIDER_TOOLS,
|
|
105
|
+
TAB_TOOLS,
|
|
106
|
+
classifyTool,
|
|
107
|
+
};
|
|
@@ -36,6 +36,13 @@ const COMMANDS = {
|
|
|
36
36
|
"tab.close": { primaryArg: "id", effect: "page-write", argKinds: { id: "tab-id" } },
|
|
37
37
|
"tab.name": { primaryArg: "name", effect: "page-write", argKinds: { name: "name" } },
|
|
38
38
|
"tab.unname": { primaryArg: "name", effect: "page-write", argKinds: { name: "name" } },
|
|
39
|
+
"session.new": { primaryArg: "name", effect: "navigation", recordable: false, argKinds: { name: "name", url: "url" } },
|
|
40
|
+
"session.ensure": { primaryArg: "name", effect: "navigation", recordable: false, argKinds: { name: "name", url: "url" } },
|
|
41
|
+
"session.list": { effect: "read", recordable: false },
|
|
42
|
+
"session.info": { primaryArg: "name", effect: "read", recordable: false, argKinds: { name: "name" } },
|
|
43
|
+
"session.close": { primaryArg: "name", effect: "page-write", recordable: false, argKinds: { name: "name" } },
|
|
44
|
+
"session.rebind": { primaryArg: "name", effect: "page-write", recordable: false, argKinds: { name: "name", tabId: "tab-id" } },
|
|
45
|
+
"session.reopen": { primaryArg: "name", effect: "navigation", recordable: false, argKinds: { name: "name", url: "url" } },
|
|
39
46
|
scroll_to_position: { primaryArg: "position", effect: "page-write", argKinds: { position: "position" } },
|
|
40
47
|
type: { primaryArg: "text", effect: "page-write", argKinds: { selector: "selector", text: "user-input" }, sensitiveArgs: ["text"] },
|
|
41
48
|
smart_type: { primaryArg: "text", effect: "page-write", argKinds: { selector: "selector", text: "user-input" }, sensitiveArgs: ["text"] },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "surf-cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.15.0",
|
|
4
4
|
"description": "CLI for AI agents to control Chrome. Zero config, agent-agnostic, battle-tested.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"chrome",
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
"playbooks/",
|
|
34
34
|
"scripts/",
|
|
35
35
|
"dist/",
|
|
36
|
+
"agents/",
|
|
36
37
|
"skills/",
|
|
37
38
|
"README.md",
|
|
38
39
|
"LICENSE"
|
|
@@ -81,7 +82,12 @@
|
|
|
81
82
|
],
|
|
82
83
|
"skills": [
|
|
83
84
|
"./skills"
|
|
84
|
-
]
|
|
85
|
+
],
|
|
86
|
+
"subagents": {
|
|
87
|
+
"agents": [
|
|
88
|
+
"./agents"
|
|
89
|
+
]
|
|
90
|
+
}
|
|
85
91
|
},
|
|
86
92
|
"peerDependencies": {
|
|
87
93
|
"typebox": "*"
|