@robota-sdk/agent-sdk 3.0.0-beta.24 → 3.0.0-beta.25

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.
@@ -1,6 +1,113 @@
1
1
  // src/types.ts
2
2
  import { TRUST_TO_MODE } from "@robota-sdk/agent-core";
3
3
 
4
+ // src/hooks/prompt-executor.ts
5
+ function extractJson(raw) {
6
+ const codeBlockMatch = /```(?:json)?\s*\n?([\s\S]*?)\n?\s*```/.exec(raw);
7
+ if (codeBlockMatch) {
8
+ return codeBlockMatch[1].trim();
9
+ }
10
+ return raw.trim();
11
+ }
12
+ var PromptExecutor = class {
13
+ type = "prompt";
14
+ providerFactory;
15
+ defaultModel;
16
+ constructor(options) {
17
+ this.providerFactory = options.providerFactory;
18
+ this.defaultModel = options.defaultModel;
19
+ }
20
+ async execute(definition, input) {
21
+ const promptDef = definition;
22
+ const model = promptDef.model ?? this.defaultModel;
23
+ try {
24
+ const provider = this.providerFactory(model);
25
+ const prompt = `${promptDef.prompt}
26
+
27
+ Context:
28
+ ${JSON.stringify(input)}
29
+
30
+ Respond with JSON: { "ok": boolean, "reason"?: string }`;
31
+ const rawResponse = await provider.complete(prompt);
32
+ const jsonStr = extractJson(rawResponse);
33
+ let parsed;
34
+ try {
35
+ parsed = JSON.parse(jsonStr);
36
+ } catch {
37
+ return {
38
+ exitCode: 1,
39
+ stdout: "",
40
+ stderr: `Failed to parse AI response as JSON: ${rawResponse}`
41
+ };
42
+ }
43
+ if (parsed.ok) {
44
+ return { exitCode: 0, stdout: JSON.stringify(parsed), stderr: "" };
45
+ }
46
+ return {
47
+ exitCode: 2,
48
+ stdout: "",
49
+ stderr: parsed.reason ?? "Blocked by prompt hook"
50
+ };
51
+ } catch (err) {
52
+ const message = err instanceof Error ? err.message : String(err);
53
+ return { exitCode: 1, stdout: "", stderr: message };
54
+ }
55
+ }
56
+ };
57
+
58
+ // src/hooks/agent-executor.ts
59
+ var DEFAULT_MAX_TURNS = 50;
60
+ var DEFAULT_TIMEOUT_SECONDS = 60;
61
+ function extractJson2(raw) {
62
+ const codeBlockMatch = /```(?:json)?\s*\n?([\s\S]*?)\n?\s*```/.exec(raw);
63
+ if (codeBlockMatch) {
64
+ return codeBlockMatch[1].trim();
65
+ }
66
+ return raw.trim();
67
+ }
68
+ var AgentExecutor = class {
69
+ type = "agent";
70
+ sessionFactory;
71
+ constructor(options) {
72
+ this.sessionFactory = options.sessionFactory;
73
+ }
74
+ async execute(definition, input) {
75
+ const agentDef = definition;
76
+ const maxTurns = agentDef.maxTurns ?? DEFAULT_MAX_TURNS;
77
+ const timeout = agentDef.timeout ?? DEFAULT_TIMEOUT_SECONDS;
78
+ try {
79
+ const session = this.sessionFactory({ maxTurns, timeout });
80
+ const prompt = `Hook input:
81
+ ${JSON.stringify(input)}
82
+
83
+ Respond with JSON: { "ok": boolean, "reason"?: string }`;
84
+ const rawResponse = await session.run(prompt);
85
+ const jsonStr = extractJson2(rawResponse);
86
+ let parsed;
87
+ try {
88
+ parsed = JSON.parse(jsonStr);
89
+ } catch {
90
+ return {
91
+ exitCode: 1,
92
+ stdout: "",
93
+ stderr: `Failed to parse agent response as JSON: ${rawResponse}`
94
+ };
95
+ }
96
+ if (parsed.ok) {
97
+ return { exitCode: 0, stdout: JSON.stringify(parsed), stderr: "" };
98
+ }
99
+ return {
100
+ exitCode: 2,
101
+ stdout: "",
102
+ stderr: parsed.reason ?? "Blocked by agent hook"
103
+ };
104
+ } catch (err) {
105
+ const message = err instanceof Error ? err.message : String(err);
106
+ return { exitCode: 1, stdout: "", stderr: message };
107
+ }
108
+ }
109
+ };
110
+
4
111
  // src/assembly/create-session.ts
5
112
  import { Session } from "@robota-sdk/agent-sessions";
6
113
 
@@ -33,6 +140,19 @@ function buildToolsSection(descriptions) {
33
140
  const lines = ["## Available Tools", ...descriptions.map((d) => `- ${d}`)];
34
141
  return lines.join("\n");
35
142
  }
143
+ function buildSkillsSection(skills) {
144
+ const invocable = skills.filter((s) => s.disableModelInvocation !== true);
145
+ if (invocable.length === 0) {
146
+ return "";
147
+ }
148
+ const lines = [
149
+ "## Skills",
150
+ "The following skills are available:",
151
+ "",
152
+ ...invocable.map((s) => `- ${s.name}: ${s.description}`)
153
+ ];
154
+ return lines.join("\n");
155
+ }
36
156
  function buildSystemPrompt(params) {
37
157
  const { agentsMd, claudeMd, toolDescriptions, trustLevel, projectInfo, cwd, language } = params;
38
158
  const sections = [];
@@ -43,7 +163,9 @@ function buildSystemPrompt(params) {
43
163
  "Always be precise, follow existing code conventions, and prefer minimal changes."
44
164
  ];
45
165
  if (language) {
46
- roleLines.push(`Always respond in ${language}. Use ${language} for all explanations and communications.`);
166
+ roleLines.push(
167
+ `Always respond in ${language}. Use ${language} for all explanations and communications.`
168
+ );
47
169
  }
48
170
  sections.push(roleLines.join("\n"));
49
171
  if (cwd) {
@@ -75,6 +197,12 @@ function buildSystemPrompt(params) {
75
197
  if (toolsSection.length > 0) {
76
198
  sections.push(toolsSection);
77
199
  }
200
+ if (params.skills !== void 0 && params.skills.length > 0) {
201
+ const skillsSection = buildSkillsSection(params.skills);
202
+ if (skillsSection.length > 0) {
203
+ sections.push(skillsSection);
204
+ }
205
+ }
78
206
  return sections.join("\n\n");
79
207
  }
80
208
 
@@ -150,6 +278,25 @@ function createSession(options) {
150
278
  allow: [...defaultAllow, ...options.config.permissions.allow ?? []],
151
279
  deny: options.config.permissions.deny ?? []
152
280
  };
281
+ const hookTypeExecutors = [];
282
+ if (options.providerFactory) {
283
+ hookTypeExecutors.push(
284
+ new PromptExecutor({
285
+ providerFactory: options.providerFactory,
286
+ defaultModel: options.config.provider.model
287
+ })
288
+ );
289
+ }
290
+ if (options.sessionFactory) {
291
+ hookTypeExecutors.push(
292
+ new AgentExecutor({
293
+ sessionFactory: options.sessionFactory
294
+ })
295
+ );
296
+ }
297
+ if (options.additionalHookExecutors) {
298
+ hookTypeExecutors.push(...options.additionalHookExecutors);
299
+ }
153
300
  return new Session({
154
301
  tools,
155
302
  provider,
@@ -168,7 +315,8 @@ function createSession(options) {
168
315
  promptForApproval: options.promptForApproval,
169
316
  onCompact: options.onCompact,
170
317
  compactInstructions: options.compactInstructions ?? options.context.compactInstructions,
171
- sessionLogger: options.sessionLogger
318
+ sessionLogger: options.sessionLogger,
319
+ hookTypeExecutors: hookTypeExecutors.length > 0 ? hookTypeExecutors : void 0
172
320
  });
173
321
  }
174
322
 
@@ -195,10 +343,34 @@ var PermissionsSchema = z.object({
195
343
  deny: z.array(z.string()).optional()
196
344
  });
197
345
  var EnvSchema = z.record(z.string()).optional();
198
- var HookDefinitionSchema = z.object({
346
+ var CommandHookDefinitionSchema = z.object({
199
347
  type: z.literal("command"),
200
- command: z.string()
348
+ command: z.string(),
349
+ timeout: z.number().optional()
350
+ });
351
+ var HttpHookDefinitionSchema = z.object({
352
+ type: z.literal("http"),
353
+ url: z.string(),
354
+ headers: z.record(z.string()).optional(),
355
+ timeout: z.number().optional()
356
+ });
357
+ var PromptHookDefinitionSchema = z.object({
358
+ type: z.literal("prompt"),
359
+ prompt: z.string(),
360
+ model: z.string().optional()
201
361
  });
362
+ var AgentHookDefinitionSchema = z.object({
363
+ type: z.literal("agent"),
364
+ agent: z.string(),
365
+ maxTurns: z.number().optional(),
366
+ timeout: z.number().optional()
367
+ });
368
+ var HookDefinitionSchema = z.discriminatedUnion("type", [
369
+ CommandHookDefinitionSchema,
370
+ HttpHookDefinitionSchema,
371
+ PromptHookDefinitionSchema,
372
+ AgentHookDefinitionSchema
373
+ ]);
202
374
  var HookGroupSchema = z.object({
203
375
  matcher: z.string(),
204
376
  hooks: z.array(HookDefinitionSchema)
@@ -207,8 +379,23 @@ var HooksSchema = z.object({
207
379
  PreToolUse: z.array(HookGroupSchema).optional(),
208
380
  PostToolUse: z.array(HookGroupSchema).optional(),
209
381
  SessionStart: z.array(HookGroupSchema).optional(),
210
- Stop: z.array(HookGroupSchema).optional()
382
+ Stop: z.array(HookGroupSchema).optional(),
383
+ PreCompact: z.array(HookGroupSchema).optional(),
384
+ PostCompact: z.array(HookGroupSchema).optional(),
385
+ UserPromptSubmit: z.array(HookGroupSchema).optional(),
386
+ Notification: z.array(HookGroupSchema).optional()
211
387
  }).optional();
388
+ var EnabledPluginsSchema = z.record(z.boolean()).optional();
389
+ var MarketplaceSourceSchema = z.object({
390
+ source: z.object({
391
+ type: z.enum(["github", "git", "local", "url"]),
392
+ repo: z.string().optional(),
393
+ url: z.string().optional(),
394
+ path: z.string().optional(),
395
+ ref: z.string().optional()
396
+ })
397
+ });
398
+ var ExtraKnownMarketplacesSchema = z.record(MarketplaceSourceSchema).optional();
212
399
  var SettingsSchema = z.object({
213
400
  /** Trust level used when no --permission-mode flag is given */
214
401
  defaultTrustLevel: z.enum(["safe", "moderate", "full"]).optional(),
@@ -217,7 +404,11 @@ var SettingsSchema = z.object({
217
404
  provider: ProviderSchema.optional(),
218
405
  permissions: PermissionsSchema.optional(),
219
406
  env: EnvSchema,
220
- hooks: HooksSchema
407
+ hooks: HooksSchema,
408
+ /** Plugin enablement map: plugin name -> enabled/disabled */
409
+ enabledPlugins: EnabledPluginsSchema,
410
+ /** Extra marketplace URLs for BundlePlugin discovery */
411
+ extraKnownMarketplaces: ExtraKnownMarketplacesSchema
221
412
  });
222
413
 
223
414
  // src/config/config-loader.ts
@@ -284,7 +475,9 @@ function mergeSettings(layers) {
284
475
  env: {
285
476
  ...merged.env ?? {},
286
477
  ...layer.env ?? {}
287
- }
478
+ },
479
+ enabledPlugins: merged.enabledPlugins !== void 0 || layer.enabledPlugins !== void 0 ? { ...merged.enabledPlugins ?? {}, ...layer.enabledPlugins ?? {} } : void 0,
480
+ extraKnownMarketplaces: layer.extraKnownMarketplaces ?? merged.extraKnownMarketplaces
288
481
  };
289
482
  }, {});
290
483
  }
@@ -302,25 +495,39 @@ function toResolvedConfig(merged) {
302
495
  deny: merged.permissions?.deny ?? DEFAULTS.permissions.deny
303
496
  },
304
497
  env: merged.env ?? DEFAULTS.env,
305
- hooks: merged.hooks ?? void 0
498
+ hooks: merged.hooks ?? void 0,
499
+ enabledPlugins: merged.enabledPlugins ?? void 0,
500
+ extraKnownMarketplaces: merged.extraKnownMarketplaces ?? void 0
306
501
  };
307
502
  }
503
+ function getSettingsPaths(cwd) {
504
+ const home = getHomeDir();
505
+ return [
506
+ join(home, ".robota", "settings.json"),
507
+ // 1. user (lowest)
508
+ join(cwd, ".robota", "settings.json"),
509
+ // 2. project
510
+ join(cwd, ".robota", "settings.local.json"),
511
+ // 3. project-local
512
+ join(cwd, ".claude", "settings.json"),
513
+ // 4. project, Claude Code compat
514
+ join(cwd, ".claude", "settings.local.json")
515
+ // 5. project-local (highest)
516
+ ];
517
+ }
308
518
  async function loadConfig(cwd) {
309
- const userSettingsPath = join(getHomeDir(), ".robota", "settings.json");
310
- const projectSettingsPath = join(cwd, ".robota", "settings.json");
311
- const localSettingsPath = join(cwd, ".robota", "settings.local.json");
312
- const rawLayers = [
313
- readJsonFile(userSettingsPath),
314
- readJsonFile(projectSettingsPath),
315
- readJsonFile(localSettingsPath)
316
- ].filter((v) => v !== void 0);
317
- const parsedLayers = rawLayers.map((raw, index) => {
519
+ const allPaths = getSettingsPaths(cwd);
520
+ const rawEntries = [];
521
+ for (const filePath of allPaths) {
522
+ const raw = readJsonFile(filePath);
523
+ if (raw !== void 0) {
524
+ rawEntries.push({ raw, path: filePath });
525
+ }
526
+ }
527
+ const parsedLayers = rawEntries.map(({ raw, path }) => {
318
528
  const result = SettingsSchema.safeParse(raw);
319
529
  if (!result.success) {
320
- const paths = [userSettingsPath, projectSettingsPath, localSettingsPath].filter(
321
- (_, i) => rawLayers[i] !== void 0
322
- );
323
- throw new Error(`Invalid settings in ${paths[index] ?? "unknown"}: ${result.error.message}`);
530
+ throw new Error(`Invalid settings in ${path}: ${result.error.message}`);
324
531
  }
325
532
  return resolveEnvRefs(result.data);
326
533
  });
@@ -534,6 +741,787 @@ function userPaths() {
534
741
  };
535
742
  }
536
743
 
744
+ // src/plugins/plugin-settings-store.ts
745
+ import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync, mkdirSync } from "fs";
746
+ import { dirname as dirname2 } from "path";
747
+ var PluginSettingsStore = class {
748
+ settingsPath;
749
+ constructor(settingsPath) {
750
+ this.settingsPath = settingsPath;
751
+ }
752
+ /** Read the full settings file from disk. */
753
+ readAll() {
754
+ if (!existsSync4(this.settingsPath)) {
755
+ return {};
756
+ }
757
+ try {
758
+ const raw = readFileSync4(this.settingsPath, "utf-8");
759
+ const data = JSON.parse(raw);
760
+ if (typeof data === "object" && data !== null) {
761
+ return data;
762
+ }
763
+ return {};
764
+ } catch {
765
+ return {};
766
+ }
767
+ }
768
+ /** Write the full settings file to disk. */
769
+ writeAll(settings) {
770
+ const dir = dirname2(this.settingsPath);
771
+ if (!existsSync4(dir)) {
772
+ mkdirSync(dir, { recursive: true });
773
+ }
774
+ writeFileSync(this.settingsPath, JSON.stringify(settings, null, 2), "utf-8");
775
+ }
776
+ // --- enabledPlugins ---
777
+ /** Get the enabledPlugins map. */
778
+ getEnabledPlugins() {
779
+ const settings = this.readAll();
780
+ const ep = settings.enabledPlugins;
781
+ if (typeof ep === "object" && ep !== null) {
782
+ return ep;
783
+ }
784
+ return {};
785
+ }
786
+ /** Set a single plugin's enabled state. */
787
+ setPluginEnabled(pluginId, enabled) {
788
+ const settings = this.readAll();
789
+ const ep = this.getEnabledPluginsFrom(settings);
790
+ ep[pluginId] = enabled;
791
+ settings.enabledPlugins = ep;
792
+ this.writeAll(settings);
793
+ }
794
+ /** Remove a plugin from enabledPlugins. */
795
+ removePluginEntry(pluginId) {
796
+ const settings = this.readAll();
797
+ const ep = this.getEnabledPluginsFrom(settings);
798
+ delete ep[pluginId];
799
+ settings.enabledPlugins = ep;
800
+ this.writeAll(settings);
801
+ }
802
+ // --- extraKnownMarketplaces ---
803
+ /** Get all persisted marketplace sources. */
804
+ getMarketplaceSources() {
805
+ const settings = this.readAll();
806
+ const extra = settings.extraKnownMarketplaces;
807
+ if (typeof extra === "object" && extra !== null) {
808
+ return extra;
809
+ }
810
+ return {};
811
+ }
812
+ /** Add or update a marketplace source. */
813
+ setMarketplaceSource(name, source) {
814
+ const settings = this.readAll();
815
+ const extra = this.getMarketplaceSourcesFrom(settings);
816
+ extra[name] = { source };
817
+ settings.extraKnownMarketplaces = extra;
818
+ this.writeAll(settings);
819
+ }
820
+ /** Remove a marketplace source. */
821
+ removeMarketplaceSource(name) {
822
+ const settings = this.readAll();
823
+ const extra = this.getMarketplaceSourcesFrom(settings);
824
+ delete extra[name];
825
+ settings.extraKnownMarketplaces = extra;
826
+ this.writeAll(settings);
827
+ }
828
+ // --- helpers ---
829
+ getEnabledPluginsFrom(settings) {
830
+ const ep = settings.enabledPlugins;
831
+ if (typeof ep === "object" && ep !== null) {
832
+ return ep;
833
+ }
834
+ return {};
835
+ }
836
+ getMarketplaceSourcesFrom(settings) {
837
+ const extra = settings.extraKnownMarketplaces;
838
+ if (typeof extra === "object" && extra !== null) {
839
+ return extra;
840
+ }
841
+ return {};
842
+ }
843
+ };
844
+
845
+ // src/plugins/bundle-plugin-loader.ts
846
+ import { existsSync as existsSync5, readdirSync, readFileSync as readFileSync5 } from "fs";
847
+ import { join as join5 } from "path";
848
+ function parseSkillFrontmatter(raw) {
849
+ const trimmed = raw.trimStart();
850
+ if (!trimmed.startsWith("---")) {
851
+ return { metadata: {}, content: raw };
852
+ }
853
+ const endIndex = trimmed.indexOf("---", 3);
854
+ if (endIndex === -1) {
855
+ return { metadata: {}, content: raw };
856
+ }
857
+ const frontmatterBlock = trimmed.slice(3, endIndex).trim();
858
+ const content = trimmed.slice(endIndex + 3).trimStart();
859
+ const metadata = {};
860
+ for (const line of frontmatterBlock.split("\n")) {
861
+ const colonIndex = line.indexOf(":");
862
+ if (colonIndex === -1) continue;
863
+ const key = line.slice(0, colonIndex).trim();
864
+ let value = line.slice(colonIndex + 1).trim();
865
+ if (typeof value === "string" && value.startsWith("[") && value.endsWith("]")) {
866
+ const inner = value.slice(1, -1);
867
+ value = inner.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
868
+ }
869
+ if (key) {
870
+ metadata[key] = value;
871
+ }
872
+ }
873
+ return { metadata, content };
874
+ }
875
+ function validateManifest(data) {
876
+ if (typeof data !== "object" || data === null) return null;
877
+ const obj = data;
878
+ if (typeof obj.name !== "string") return null;
879
+ if (typeof obj.version !== "string") return null;
880
+ if (typeof obj.description !== "string") return null;
881
+ const features = typeof obj.features === "object" && obj.features !== null ? obj.features : {};
882
+ return {
883
+ name: obj.name,
884
+ version: obj.version,
885
+ description: obj.description,
886
+ features: {
887
+ commands: features.commands === true ? true : void 0,
888
+ agents: features.agents === true ? true : void 0,
889
+ skills: features.skills === true ? true : void 0,
890
+ hooks: features.hooks === true ? true : void 0,
891
+ mcp: features.mcp === true ? true : void 0
892
+ }
893
+ };
894
+ }
895
+ function getSortedSubdirs(dirPath) {
896
+ if (!existsSync5(dirPath)) return [];
897
+ try {
898
+ const entries = readdirSync(dirPath, { withFileTypes: true });
899
+ return entries.filter((e) => e.isDirectory()).map((e) => e.name).sort();
900
+ } catch {
901
+ return [];
902
+ }
903
+ }
904
+ var BundlePluginLoader = class {
905
+ pluginsDir;
906
+ enabledPlugins;
907
+ constructor(pluginsDir, enabledPlugins) {
908
+ this.pluginsDir = pluginsDir;
909
+ this.enabledPlugins = enabledPlugins ?? {};
910
+ }
911
+ /** Load all discovered and enabled bundle plugins (sync). */
912
+ loadPluginsSync() {
913
+ return this.discoverAndLoad();
914
+ }
915
+ /** Load all discovered and enabled bundle plugins (async wrapper). */
916
+ async loadAll() {
917
+ return this.discoverAndLoad();
918
+ }
919
+ /**
920
+ * Discover and load plugins from the cache directory.
921
+ *
922
+ * Directory structure: `<pluginsDir>/cache/<marketplace>/<plugin>/<version>/`
923
+ * For each marketplace/plugin pair, the latest version (lexicographically last) is loaded.
924
+ */
925
+ discoverAndLoad() {
926
+ const cacheDir = join5(this.pluginsDir, "cache");
927
+ if (!existsSync5(cacheDir)) {
928
+ return [];
929
+ }
930
+ const results = [];
931
+ const marketplaces = getSortedSubdirs(cacheDir);
932
+ for (const marketplace of marketplaces) {
933
+ const marketplaceDir = join5(cacheDir, marketplace);
934
+ const plugins = getSortedSubdirs(marketplaceDir);
935
+ for (const pluginName of plugins) {
936
+ const pluginDir = join5(marketplaceDir, pluginName);
937
+ const versions = getSortedSubdirs(pluginDir);
938
+ if (versions.length === 0) continue;
939
+ const latestVersion = versions[versions.length - 1];
940
+ const versionDir = join5(pluginDir, latestVersion);
941
+ const manifestPath = join5(versionDir, ".claude-plugin", "plugin.json");
942
+ if (!existsSync5(manifestPath)) continue;
943
+ const manifest = this.readManifest(manifestPath);
944
+ if (!manifest) continue;
945
+ const pluginId = `${manifest.name}@${marketplace}`;
946
+ if (this.isDisabled(pluginId, manifest.name)) continue;
947
+ const loaded = this.loadPlugin(versionDir, manifest);
948
+ results.push(loaded);
949
+ }
950
+ }
951
+ return results;
952
+ }
953
+ /** Read and validate a plugin.json manifest. Returns null on failure. */
954
+ readManifest(path) {
955
+ try {
956
+ const raw = readFileSync5(path, "utf-8");
957
+ const data = JSON.parse(raw);
958
+ return validateManifest(data);
959
+ } catch {
960
+ return null;
961
+ }
962
+ }
963
+ /**
964
+ * Check if a plugin is explicitly disabled.
965
+ * Checks both `name@marketplace` and `name` keys.
966
+ * Plugins not listed in enabledPlugins are enabled by default.
967
+ */
968
+ isDisabled(pluginId, pluginName) {
969
+ if (pluginId in this.enabledPlugins) {
970
+ return this.enabledPlugins[pluginId] === false;
971
+ }
972
+ if (pluginName in this.enabledPlugins) {
973
+ return this.enabledPlugins[pluginName] === false;
974
+ }
975
+ return false;
976
+ }
977
+ /** Load a single plugin's skills, hooks, agents, and MCP config. */
978
+ loadPlugin(pluginDir, manifest) {
979
+ return {
980
+ manifest,
981
+ skills: this.loadSkills(pluginDir, manifest.name),
982
+ commands: this.loadCommands(pluginDir, manifest.name),
983
+ hooks: this.loadHooks(pluginDir),
984
+ mcpConfig: this.loadMcpConfig(pluginDir),
985
+ agents: this.loadAgents(pluginDir),
986
+ pluginDir
987
+ };
988
+ }
989
+ /** Load skills from the plugin's skills/ directory. */
990
+ loadSkills(pluginDir, pluginName) {
991
+ const skillsDir = join5(pluginDir, "skills");
992
+ if (!existsSync5(skillsDir)) return [];
993
+ const entries = readdirSync(skillsDir, { withFileTypes: true });
994
+ const skills = [];
995
+ for (const entry of entries) {
996
+ if (!entry.isDirectory()) continue;
997
+ const skillFile = join5(skillsDir, entry.name, "SKILL.md");
998
+ if (!existsSync5(skillFile)) continue;
999
+ const raw = readFileSync5(skillFile, "utf-8");
1000
+ const { metadata, content } = parseSkillFrontmatter(raw);
1001
+ const description = typeof metadata.description === "string" ? metadata.description : "";
1002
+ const skill = {
1003
+ name: entry.name,
1004
+ description,
1005
+ skillContent: content,
1006
+ ...metadata
1007
+ };
1008
+ skills.push(skill);
1009
+ }
1010
+ return skills;
1011
+ }
1012
+ /** Load commands from the plugin's commands/ directory (flat .md files). */
1013
+ loadCommands(pluginDir, pluginName) {
1014
+ const commandsDir = join5(pluginDir, "commands");
1015
+ if (!existsSync5(commandsDir)) return [];
1016
+ const entries = readdirSync(commandsDir, { withFileTypes: true });
1017
+ const commands = [];
1018
+ for (const entry of entries) {
1019
+ if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
1020
+ const raw = readFileSync5(join5(commandsDir, entry.name), "utf-8");
1021
+ const { metadata, content } = parseSkillFrontmatter(raw);
1022
+ const name = typeof metadata.name === "string" ? metadata.name : entry.name.replace(/\.md$/, "");
1023
+ const description = typeof metadata.description === "string" ? metadata.description : "";
1024
+ commands.push({
1025
+ ...metadata,
1026
+ name: `${pluginName}:${name}`,
1027
+ description,
1028
+ skillContent: content
1029
+ });
1030
+ }
1031
+ return commands;
1032
+ }
1033
+ /** Load hooks from hooks/hooks.json if present. */
1034
+ loadHooks(pluginDir) {
1035
+ const hooksPath = join5(pluginDir, "hooks", "hooks.json");
1036
+ if (!existsSync5(hooksPath)) return {};
1037
+ try {
1038
+ const raw = readFileSync5(hooksPath, "utf-8");
1039
+ const data = JSON.parse(raw);
1040
+ if (typeof data === "object" && data !== null) {
1041
+ return data;
1042
+ }
1043
+ return {};
1044
+ } catch {
1045
+ return {};
1046
+ }
1047
+ }
1048
+ /** Load MCP server configuration if present. Checks `.mcp.json` at plugin root first. */
1049
+ loadMcpConfig(pluginDir) {
1050
+ const primaryPath = join5(pluginDir, ".mcp.json");
1051
+ const fallbackPath = join5(pluginDir, ".claude-plugin", "mcp.json");
1052
+ const mcpPath = existsSync5(primaryPath) ? primaryPath : fallbackPath;
1053
+ if (!existsSync5(mcpPath)) return void 0;
1054
+ try {
1055
+ const raw = readFileSync5(mcpPath, "utf-8");
1056
+ return JSON.parse(raw);
1057
+ } catch {
1058
+ return void 0;
1059
+ }
1060
+ }
1061
+ /** Load agent definitions from agents/ directory if present. */
1062
+ loadAgents(pluginDir) {
1063
+ const agentsDir = join5(pluginDir, "agents");
1064
+ if (!existsSync5(agentsDir)) return [];
1065
+ try {
1066
+ const entries = readdirSync(agentsDir, { withFileTypes: true });
1067
+ return entries.filter((e) => e.isDirectory() || e.name.endsWith(".md")).map((e) => e.name.replace(/\.md$/, ""));
1068
+ } catch {
1069
+ return [];
1070
+ }
1071
+ }
1072
+ };
1073
+
1074
+ // src/plugins/bundle-plugin-installer.ts
1075
+ import { execSync } from "child_process";
1076
+ import { cpSync, existsSync as existsSync6, mkdirSync as mkdirSync2, readFileSync as readFileSync6, rmSync, writeFileSync as writeFileSync2 } from "fs";
1077
+ import { join as join6, dirname as dirname3 } from "path";
1078
+ var GIT_CLONE_TIMEOUT_MS = 6e4;
1079
+ var BundlePluginInstaller = class {
1080
+ pluginsDir;
1081
+ cacheDir;
1082
+ registryPath;
1083
+ settingsStore;
1084
+ marketplaceClient;
1085
+ exec;
1086
+ constructor(options) {
1087
+ this.pluginsDir = options.pluginsDir;
1088
+ this.cacheDir = join6(this.pluginsDir, "cache");
1089
+ this.registryPath = join6(this.pluginsDir, "installed_plugins.json");
1090
+ this.settingsStore = options.settingsStore;
1091
+ this.marketplaceClient = options.marketplaceClient;
1092
+ this.exec = options.exec ?? this.defaultExec;
1093
+ }
1094
+ /**
1095
+ * Install a plugin from a marketplace.
1096
+ *
1097
+ * 1. Read marketplace manifest to find the plugin entry.
1098
+ * 2. Resolve source (relative path, github, or url).
1099
+ * 3. Copy/clone to `cache/<marketplace>/<plugin>/<version>/`.
1100
+ * 4. Record in `installed_plugins.json`.
1101
+ */
1102
+ async install(pluginName, marketplaceName) {
1103
+ const manifest = this.marketplaceClient.fetchManifest(marketplaceName);
1104
+ const entry = manifest.plugins.find((p) => p.name === pluginName);
1105
+ if (!entry) {
1106
+ throw new Error(`Plugin "${pluginName}" not found in marketplace "${marketplaceName}"`);
1107
+ }
1108
+ const version = this.resolveVersion(entry, marketplaceName);
1109
+ const targetDir = join6(this.cacheDir, marketplaceName, pluginName, version);
1110
+ if (existsSync6(targetDir)) {
1111
+ throw new Error(
1112
+ `Plugin "${pluginName}" version "${version}" is already installed from "${marketplaceName}"`
1113
+ );
1114
+ }
1115
+ this.resolveAndInstall(entry.source, marketplaceName, pluginName, targetDir);
1116
+ const pluginId = `${pluginName}@${marketplaceName}`;
1117
+ const registry = this.readRegistry();
1118
+ registry[pluginId] = {
1119
+ pluginName,
1120
+ marketplace: marketplaceName,
1121
+ version,
1122
+ installPath: targetDir,
1123
+ installedAt: (/* @__PURE__ */ new Date()).toISOString()
1124
+ };
1125
+ this.writeRegistry(registry);
1126
+ }
1127
+ /**
1128
+ * Uninstall a plugin.
1129
+ * Removes from cache and from installed_plugins.json.
1130
+ */
1131
+ async uninstall(pluginId) {
1132
+ const registry = this.readRegistry();
1133
+ const record = registry[pluginId];
1134
+ if (!record) {
1135
+ throw new Error(`Plugin "${pluginId}" is not installed`);
1136
+ }
1137
+ if (existsSync6(record.installPath)) {
1138
+ rmSync(record.installPath, { recursive: true, force: true });
1139
+ }
1140
+ delete registry[pluginId];
1141
+ this.writeRegistry(registry);
1142
+ this.settingsStore.removePluginEntry(pluginId);
1143
+ }
1144
+ /** Enable a plugin by setting its enabledPlugins entry to true. */
1145
+ async enable(pluginId) {
1146
+ this.settingsStore.setPluginEnabled(pluginId, true);
1147
+ }
1148
+ /** Disable a plugin by setting its enabledPlugins entry to false. */
1149
+ async disable(pluginId) {
1150
+ this.settingsStore.setPluginEnabled(pluginId, false);
1151
+ }
1152
+ /** Get all installed plugins. */
1153
+ getInstalledPlugins() {
1154
+ return this.readRegistry();
1155
+ }
1156
+ /** Get plugins installed from a specific marketplace. */
1157
+ getPluginsByMarketplace(marketplaceName) {
1158
+ const registry = this.readRegistry();
1159
+ return Object.values(registry).filter((r) => r.marketplace === marketplaceName);
1160
+ }
1161
+ // --- Private helpers ---
1162
+ /** Resolve the version for a plugin entry. */
1163
+ resolveVersion(entry, marketplaceName) {
1164
+ const entryWithVersion = entry;
1165
+ if (typeof entryWithVersion.version === "string" && entryWithVersion.version) {
1166
+ return entryWithVersion.version;
1167
+ }
1168
+ return this.marketplaceClient.getMarketplaceSha(marketplaceName);
1169
+ }
1170
+ /** Resolve the source and install the plugin. */
1171
+ resolveAndInstall(source, marketplaceName, pluginName, targetDir) {
1172
+ mkdirSync2(targetDir, { recursive: true });
1173
+ if (typeof source === "string") {
1174
+ const marketplaceDir = this.marketplaceClient.getMarketplaceDir(marketplaceName);
1175
+ const sourcePath = join6(marketplaceDir, source);
1176
+ if (!existsSync6(sourcePath)) {
1177
+ rmSync(targetDir, { recursive: true, force: true });
1178
+ throw new Error(
1179
+ `Plugin source path "${source}" not found in marketplace "${marketplaceName}"`
1180
+ );
1181
+ }
1182
+ cpSync(sourcePath, targetDir, { recursive: true });
1183
+ } else if (source.type === "github") {
1184
+ const repoUrl = `https://github.com/${source.repo}.git`;
1185
+ this.cloneToDir(repoUrl, targetDir, pluginName);
1186
+ } else if (source.type === "url") {
1187
+ rmSync(targetDir, { recursive: true, force: true });
1188
+ throw new Error("URL source installation is not yet supported");
1189
+ }
1190
+ }
1191
+ /** Clone a git repository to the target directory. */
1192
+ cloneToDir(repoUrl, targetDir, pluginName) {
1193
+ rmSync(targetDir, { recursive: true, force: true });
1194
+ const command = `git clone --depth 1 ${repoUrl} ${targetDir}`;
1195
+ try {
1196
+ this.exec(command, { timeout: GIT_CLONE_TIMEOUT_MS, stdio: "pipe" });
1197
+ } catch (error) {
1198
+ const message = error instanceof Error ? error.message : String(error);
1199
+ throw new Error(`Failed to clone plugin "${pluginName}": ${message}`);
1200
+ }
1201
+ }
1202
+ /** Read the installed_plugins.json registry. */
1203
+ readRegistry() {
1204
+ if (!existsSync6(this.registryPath)) {
1205
+ return {};
1206
+ }
1207
+ try {
1208
+ const raw = readFileSync6(this.registryPath, "utf-8");
1209
+ const data = JSON.parse(raw);
1210
+ if (typeof data === "object" && data !== null) {
1211
+ return data;
1212
+ }
1213
+ return {};
1214
+ } catch {
1215
+ return {};
1216
+ }
1217
+ }
1218
+ /** Write the installed_plugins.json registry. */
1219
+ writeRegistry(registry) {
1220
+ const dir = dirname3(this.registryPath);
1221
+ if (!existsSync6(dir)) {
1222
+ mkdirSync2(dir, { recursive: true });
1223
+ }
1224
+ writeFileSync2(this.registryPath, JSON.stringify(registry, null, 2), "utf-8");
1225
+ }
1226
+ /** Default exec implementation using child_process. */
1227
+ defaultExec(command, options) {
1228
+ return execSync(command, { timeout: options.timeout, stdio: "pipe" });
1229
+ }
1230
+ };
1231
+
1232
+ // src/plugins/marketplace-client.ts
1233
+ import { execSync as execSync2 } from "child_process";
1234
+ import {
1235
+ cpSync as cpSync2,
1236
+ existsSync as existsSync7,
1237
+ mkdirSync as mkdirSync3,
1238
+ readFileSync as readFileSync7,
1239
+ renameSync,
1240
+ rmSync as rmSync2,
1241
+ writeFileSync as writeFileSync3
1242
+ } from "fs";
1243
+ import { join as join7, dirname as dirname4 } from "path";
1244
+ var GIT_TIMEOUT_MS = 6e4;
1245
+ var MarketplaceClient = class {
1246
+ pluginsDir;
1247
+ exec;
1248
+ marketplacesDir;
1249
+ registryPath;
1250
+ constructor(options) {
1251
+ this.pluginsDir = options.pluginsDir;
1252
+ this.exec = options.exec ?? this.defaultExec;
1253
+ this.marketplacesDir = join7(this.pluginsDir, "marketplaces");
1254
+ this.registryPath = join7(this.pluginsDir, "known_marketplaces.json");
1255
+ }
1256
+ /**
1257
+ * Add a marketplace by cloning its repository.
1258
+ *
1259
+ * 1. Parse source: `owner/repo` string becomes a GitHub source.
1260
+ * 2. Shallow git clone (`--depth 1`) to `marketplaces/<name>/`.
1261
+ * 3. Read `.claude-plugin/marketplace.json` for the `name` field.
1262
+ * 4. Register in `known_marketplaces.json`.
1263
+ *
1264
+ * Returns the registered marketplace name from the manifest.
1265
+ */
1266
+ addMarketplace(source) {
1267
+ const tempName = "temp-" + Date.now().toString(36);
1268
+ const tempDir = join7(this.marketplacesDir, tempName);
1269
+ mkdirSync3(this.marketplacesDir, { recursive: true });
1270
+ if (source.type === "local") {
1271
+ if (!existsSync7(source.path)) {
1272
+ throw new Error(`Local marketplace path does not exist: ${source.path}`);
1273
+ }
1274
+ cpSync2(source.path, tempDir, { recursive: true });
1275
+ } else {
1276
+ const cloneUrl = this.resolveCloneUrl(source);
1277
+ const command = `git clone --depth 1 ${cloneUrl} ${tempDir}`;
1278
+ try {
1279
+ this.exec(command, { timeout: GIT_TIMEOUT_MS, stdio: "pipe" });
1280
+ } catch (error) {
1281
+ const message = error instanceof Error ? error.message : String(error);
1282
+ throw new Error(`Failed to clone marketplace: ${message}`);
1283
+ }
1284
+ }
1285
+ const manifestPath = join7(tempDir, ".claude-plugin", "marketplace.json");
1286
+ if (!existsSync7(manifestPath)) {
1287
+ rmSync2(tempDir, { recursive: true, force: true });
1288
+ throw new Error(
1289
+ source.type === "local" ? "Local directory does not contain .claude-plugin/marketplace.json" : "Cloned repository does not contain .claude-plugin/marketplace.json"
1290
+ );
1291
+ }
1292
+ const manifest = this.readManifestFromPath(manifestPath);
1293
+ const name = manifest.name;
1294
+ if (!name) {
1295
+ rmSync2(tempDir, { recursive: true, force: true });
1296
+ throw new Error('Marketplace manifest does not contain a "name" field');
1297
+ }
1298
+ const registry = this.readRegistry();
1299
+ if (registry[name]) {
1300
+ rmSync2(tempDir, { recursive: true, force: true });
1301
+ throw new Error(`Marketplace "${name}" already exists`);
1302
+ }
1303
+ const finalDir = join7(this.marketplacesDir, name);
1304
+ renameSync(tempDir, finalDir);
1305
+ registry[name] = {
1306
+ source,
1307
+ installLocation: finalDir,
1308
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
1309
+ };
1310
+ this.writeRegistry(registry);
1311
+ return name;
1312
+ }
1313
+ /**
1314
+ * Remove a marketplace.
1315
+ * Uninstalls all plugins from that marketplace, then deletes the clone directory
1316
+ * and removes from the registry.
1317
+ */
1318
+ removeMarketplace(name) {
1319
+ const registry = this.readRegistry();
1320
+ const entry = registry[name];
1321
+ if (!entry) {
1322
+ throw new Error(`Marketplace "${name}" not found`);
1323
+ }
1324
+ this.removeInstalledPluginsForMarketplace(name);
1325
+ if (existsSync7(entry.installLocation)) {
1326
+ rmSync2(entry.installLocation, { recursive: true, force: true });
1327
+ }
1328
+ delete registry[name];
1329
+ this.writeRegistry(registry);
1330
+ }
1331
+ /**
1332
+ * Update a marketplace by running git pull on its clone.
1333
+ * The manifest is re-read from disk on demand (via fetchManifest), so the
1334
+ * updated manifest is automatically available after pull.
1335
+ *
1336
+ * TODO: After pull, detect version changes in installed plugins and offer
1337
+ * to update them (re-install at new version).
1338
+ */
1339
+ updateMarketplace(name) {
1340
+ const registry = this.readRegistry();
1341
+ const entry = registry[name];
1342
+ if (!entry) {
1343
+ throw new Error(`Marketplace "${name}" not found`);
1344
+ }
1345
+ if (!existsSync7(entry.installLocation)) {
1346
+ throw new Error(`Marketplace directory for "${name}" does not exist`);
1347
+ }
1348
+ if (entry.source.type === "local") {
1349
+ const localSource = entry.source;
1350
+ if (!existsSync7(localSource.path)) {
1351
+ throw new Error(`Local marketplace path does not exist: ${localSource.path}`);
1352
+ }
1353
+ rmSync2(entry.installLocation, { recursive: true, force: true });
1354
+ cpSync2(localSource.path, entry.installLocation, { recursive: true });
1355
+ } else {
1356
+ const command = `git -C ${entry.installLocation} pull`;
1357
+ try {
1358
+ this.exec(command, { timeout: GIT_TIMEOUT_MS, stdio: "pipe" });
1359
+ } catch (error) {
1360
+ const message = error instanceof Error ? error.message : String(error);
1361
+ throw new Error(`Failed to update marketplace "${name}": ${message}`);
1362
+ }
1363
+ }
1364
+ entry.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
1365
+ this.writeRegistry(registry);
1366
+ }
1367
+ /** List all registered marketplaces. */
1368
+ listMarketplaces() {
1369
+ const registry = this.readRegistry();
1370
+ return Object.entries(registry).map(([name, entry]) => ({
1371
+ name,
1372
+ source: entry.source,
1373
+ lastUpdated: entry.lastUpdated
1374
+ }));
1375
+ }
1376
+ /**
1377
+ * Read the marketplace manifest from a registered marketplace's clone.
1378
+ */
1379
+ fetchManifest(marketplaceName) {
1380
+ const registry = this.readRegistry();
1381
+ const entry = registry[marketplaceName];
1382
+ if (!entry) {
1383
+ throw new Error(`Marketplace "${marketplaceName}" not found`);
1384
+ }
1385
+ const manifestPath = join7(entry.installLocation, ".claude-plugin", "marketplace.json");
1386
+ if (!existsSync7(manifestPath)) {
1387
+ throw new Error(
1388
+ `Marketplace "${marketplaceName}" does not contain .claude-plugin/marketplace.json`
1389
+ );
1390
+ }
1391
+ return this.readManifestFromPath(manifestPath);
1392
+ }
1393
+ /** Get the clone directory path for a registered marketplace. */
1394
+ getMarketplaceDir(name) {
1395
+ const registry = this.readRegistry();
1396
+ const entry = registry[name];
1397
+ if (!entry) {
1398
+ throw new Error(`Marketplace "${name}" not found`);
1399
+ }
1400
+ return entry.installLocation;
1401
+ }
1402
+ /**
1403
+ * Get the current git SHA (first 12 chars) for a marketplace clone.
1404
+ * Used as a version identifier when plugins lack explicit versions.
1405
+ */
1406
+ getMarketplaceSha(name) {
1407
+ const dir = this.getMarketplaceDir(name);
1408
+ try {
1409
+ const result = this.exec(`git -C ${dir} rev-parse HEAD`, {
1410
+ timeout: GIT_TIMEOUT_MS,
1411
+ stdio: "pipe"
1412
+ });
1413
+ return result.toString().trim().slice(0, 12);
1414
+ } catch {
1415
+ return "unknown";
1416
+ }
1417
+ }
1418
+ /** List all available plugins across all marketplaces. */
1419
+ listAvailablePlugins() {
1420
+ const results = [];
1421
+ const marketplaces = this.listMarketplaces();
1422
+ for (const { name } of marketplaces) {
1423
+ try {
1424
+ const manifest = this.fetchManifest(name);
1425
+ for (const plugin of manifest.plugins) {
1426
+ results.push({ ...plugin, marketplace: name });
1427
+ }
1428
+ } catch {
1429
+ }
1430
+ }
1431
+ return results;
1432
+ }
1433
+ // --- Private helpers ---
1434
+ /** Resolve a marketplace source to a git clone URL. */
1435
+ resolveCloneUrl(source) {
1436
+ switch (source.type) {
1437
+ case "github":
1438
+ return `https://github.com/${source.repo}.git`;
1439
+ case "git":
1440
+ return source.url;
1441
+ case "local":
1442
+ throw new Error("Local source type does not use git cloning");
1443
+ case "url":
1444
+ throw new Error("URL marketplace source is not yet supported");
1445
+ }
1446
+ }
1447
+ /**
1448
+ * Remove all installed plugins that belong to a given marketplace.
1449
+ * Reads installed_plugins.json, deletes cache directories for matching plugins,
1450
+ * and updates the registry.
1451
+ */
1452
+ removeInstalledPluginsForMarketplace(marketplaceName) {
1453
+ const installedPath = join7(this.pluginsDir, "installed_plugins.json");
1454
+ if (!existsSync7(installedPath)) return;
1455
+ let registry;
1456
+ try {
1457
+ const raw = readFileSync7(installedPath, "utf-8");
1458
+ const data = JSON.parse(raw);
1459
+ if (typeof data !== "object" || data === null) return;
1460
+ registry = data;
1461
+ } catch {
1462
+ return;
1463
+ }
1464
+ let changed = false;
1465
+ for (const [pluginId, record] of Object.entries(registry)) {
1466
+ if (record.marketplace === marketplaceName) {
1467
+ if (record.installPath && existsSync7(record.installPath)) {
1468
+ rmSync2(record.installPath, { recursive: true, force: true });
1469
+ }
1470
+ delete registry[pluginId];
1471
+ changed = true;
1472
+ }
1473
+ }
1474
+ if (changed) {
1475
+ const dir = dirname4(installedPath);
1476
+ if (!existsSync7(dir)) {
1477
+ mkdirSync3(dir, { recursive: true });
1478
+ }
1479
+ writeFileSync3(installedPath, JSON.stringify(registry, null, 2), "utf-8");
1480
+ }
1481
+ }
1482
+ /** Read and parse a marketplace.json from a file path. */
1483
+ readManifestFromPath(path) {
1484
+ const raw = readFileSync7(path, "utf-8");
1485
+ const data = JSON.parse(raw);
1486
+ if (typeof data !== "object" || data === null) {
1487
+ throw new Error("Invalid marketplace manifest: not an object");
1488
+ }
1489
+ const obj = data;
1490
+ if (typeof obj.name !== "string") {
1491
+ throw new Error('Invalid marketplace manifest: missing "name" field');
1492
+ }
1493
+ return data;
1494
+ }
1495
+ /** Read the known_marketplaces.json registry. */
1496
+ readRegistry() {
1497
+ if (!existsSync7(this.registryPath)) {
1498
+ return {};
1499
+ }
1500
+ try {
1501
+ const raw = readFileSync7(this.registryPath, "utf-8");
1502
+ const data = JSON.parse(raw);
1503
+ if (typeof data === "object" && data !== null) {
1504
+ return data;
1505
+ }
1506
+ return {};
1507
+ } catch {
1508
+ return {};
1509
+ }
1510
+ }
1511
+ /** Write the known_marketplaces.json registry. */
1512
+ writeRegistry(registry) {
1513
+ const dir = dirname4(this.registryPath);
1514
+ if (!existsSync7(dir)) {
1515
+ mkdirSync3(dir, { recursive: true });
1516
+ }
1517
+ writeFileSync3(this.registryPath, JSON.stringify(registry, null, 2), "utf-8");
1518
+ }
1519
+ /** Default exec implementation using child_process. */
1520
+ defaultExec(command, options) {
1521
+ return execSync2(command, { timeout: options.timeout, stdio: "pipe" });
1522
+ }
1523
+ };
1524
+
537
1525
  // src/tools/agent-tool.ts
538
1526
  import { z as z2 } from "zod";
539
1527
  import { createZodFunctionTool } from "@robota-sdk/agent-tools";
@@ -614,8 +1602,14 @@ import { editTool as editTool2 } from "@robota-sdk/agent-tools";
614
1602
  import { globTool as globTool2 } from "@robota-sdk/agent-tools";
615
1603
  import { grepTool as grepTool2 } from "@robota-sdk/agent-tools";
616
1604
  export {
1605
+ AgentExecutor,
1606
+ BundlePluginInstaller,
1607
+ BundlePluginLoader,
617
1608
  DEFAULT_TOOL_DESCRIPTIONS,
618
1609
  FileSessionLogger,
1610
+ MarketplaceClient,
1611
+ PluginSettingsStore,
1612
+ PromptExecutor,
619
1613
  Session2 as Session,
620
1614
  SessionStore,
621
1615
  SilentSessionLogger,