@wingman-ai/gateway 0.4.1 → 0.4.2

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.
Files changed (48) hide show
  1. package/dist/cli/commands/init.cjs +135 -1
  2. package/dist/cli/commands/init.js +136 -2
  3. package/dist/cli/commands/skill.cjs +7 -3
  4. package/dist/cli/commands/skill.js +7 -3
  5. package/dist/cli/config/loader.cjs +7 -3
  6. package/dist/cli/config/loader.js +7 -3
  7. package/dist/cli/config/schema.cjs +27 -9
  8. package/dist/cli/config/schema.d.ts +18 -4
  9. package/dist/cli/config/schema.js +23 -8
  10. package/dist/cli/core/agentInvoker.cjs +49 -11
  11. package/dist/cli/core/agentInvoker.js +49 -11
  12. package/dist/cli/services/skillRepository.cjs +155 -69
  13. package/dist/cli/services/skillRepository.d.ts +7 -2
  14. package/dist/cli/services/skillRepository.js +155 -69
  15. package/dist/cli/services/skillService.cjs +93 -26
  16. package/dist/cli/services/skillService.d.ts +7 -0
  17. package/dist/cli/services/skillService.js +96 -29
  18. package/dist/cli/types/skill.d.ts +8 -3
  19. package/dist/skills/activation.cjs +92 -0
  20. package/dist/skills/activation.d.ts +12 -0
  21. package/dist/skills/activation.js +58 -0
  22. package/dist/skills/bin-requirements.cjs +63 -0
  23. package/dist/skills/bin-requirements.d.ts +3 -0
  24. package/dist/skills/bin-requirements.js +26 -0
  25. package/dist/skills/metadata.cjs +141 -0
  26. package/dist/skills/metadata.d.ts +29 -0
  27. package/dist/skills/metadata.js +104 -0
  28. package/dist/skills/overlay.cjs +75 -0
  29. package/dist/skills/overlay.d.ts +2 -0
  30. package/dist/skills/overlay.js +38 -0
  31. package/dist/tests/cli-config-loader.test.cjs +7 -3
  32. package/dist/tests/cli-config-loader.test.js +7 -3
  33. package/dist/tests/cli-init.test.cjs +54 -0
  34. package/dist/tests/cli-init.test.js +54 -0
  35. package/dist/tests/config-json-schema.test.cjs +12 -0
  36. package/dist/tests/config-json-schema.test.js +12 -0
  37. package/dist/tests/skill-activation.test.cjs +86 -0
  38. package/dist/tests/skill-activation.test.d.ts +1 -0
  39. package/dist/tests/skill-activation.test.js +80 -0
  40. package/dist/tests/skill-metadata.test.cjs +119 -0
  41. package/dist/tests/skill-metadata.test.d.ts +1 -0
  42. package/dist/tests/skill-metadata.test.js +113 -0
  43. package/dist/tests/skill-repository.test.cjs +363 -0
  44. package/dist/tests/skill-repository.test.js +363 -0
  45. package/package.json +4 -4
  46. package/skills/gog/SKILL.md +1 -1
  47. package/skills/weather/SKILL.md +1 -1
  48. package/skills/ui-registry/SKILL.md +0 -35
@@ -62,6 +62,11 @@ const DEFAULT_TOOLS = [
62
62
  "think"
63
63
  ];
64
64
  const DEFAULT_FS_ROOT = ".";
65
+ const DEFAULT_BROWSER_PROFILE_ID = "default";
66
+ const DEFAULT_BROWSER_PROFILES_DIR = ".wingman/browser-profiles";
67
+ const DEFAULT_BROWSER_EXTENSIONS_DIR = ".wingman/browser-extensions";
68
+ const DEFAULT_BUNDLED_EXTENSION_ID = "wingman";
69
+ const DEFAULT_BROWSER_TRANSPORT = "auto";
65
70
  const DEFAULT_MODELS = {
66
71
  anthropic: "anthropic:claude-sonnet-4-5",
67
72
  openai: "openai:gpt-4o",
@@ -318,7 +323,10 @@ async function handleConfigSetup(input) {
318
323
  recursive: true
319
324
  });
320
325
  (0, external_node_fs_namespaceObject.writeFileSync)(configPath, JSON.stringify(nextConfig, null, 2));
326
+ const browserBootstrap = bootstrapBrowserDefaults(configRoot, nextConfig);
321
327
  writeLine(outputManager, `Saved config to ${configPath}`);
328
+ if (browserBootstrap.profileDirectory) writeLine(outputManager, `Prepared default browser profile at ${browserBootstrap.profileDirectory}`);
329
+ if (browserBootstrap.extensionDirectory) writeLine(outputManager, `Installed bundled browser extension at ${browserBootstrap.extensionDirectory}`);
322
330
  }
323
331
  function mergeConfigFile(configPath, agentId, fsRoot) {
324
332
  const raw = (0, external_node_fs_namespaceObject.readFileSync)(configPath, "utf-8");
@@ -357,6 +365,8 @@ function mergeConfigValues(config, agentId, fsRoot) {
357
365
  ...gateway,
358
366
  fsRoots
359
367
  };
368
+ const browserMerge = applyRecommendedBrowserConfig(nextConfig);
369
+ if (browserMerge.changed) changed = true;
360
370
  return {
361
371
  config: nextConfig,
362
372
  changed
@@ -371,7 +381,131 @@ function buildDefaultConfig(agentId, fsRoot) {
371
381
  fsRoot
372
382
  ]
373
383
  };
374
- return config;
384
+ const nextConfig = config;
385
+ applyRecommendedBrowserConfig(nextConfig);
386
+ return nextConfig;
387
+ }
388
+ function isObjectRecord(value) {
389
+ return Boolean(value) && "object" == typeof value && !Array.isArray(value);
390
+ }
391
+ function readStringRecord(value) {
392
+ if (!isObjectRecord(value)) return {};
393
+ const record = {};
394
+ for (const [key, entry] of Object.entries(value))if ("string" == typeof entry && entry.trim()) record[key] = entry.trim();
395
+ return record;
396
+ }
397
+ function readStringArray(value) {
398
+ if (!Array.isArray(value)) return [];
399
+ return value.filter((entry)=>"string" == typeof entry).map((entry)=>entry.trim()).filter((entry)=>entry.length > 0);
400
+ }
401
+ function normalizePathForConfig(pathValue) {
402
+ return pathValue.replaceAll("\\", "/");
403
+ }
404
+ function applyRecommendedBrowserConfig(config) {
405
+ let changed = false;
406
+ const browserRaw = config.browser;
407
+ const browser = isObjectRecord(browserRaw) ? {
408
+ ...browserRaw
409
+ } : {};
410
+ if (!isObjectRecord(browserRaw)) changed = true;
411
+ const profilesDir = "string" == typeof browser.profilesDir && browser.profilesDir.trim() ? browser.profilesDir.trim() : DEFAULT_BROWSER_PROFILES_DIR;
412
+ if (browser.profilesDir !== profilesDir) {
413
+ browser.profilesDir = profilesDir;
414
+ changed = true;
415
+ }
416
+ const defaultProfile = "string" == typeof browser.defaultProfile && browser.defaultProfile.trim() ? browser.defaultProfile.trim() : DEFAULT_BROWSER_PROFILE_ID;
417
+ if (browser.defaultProfile !== defaultProfile) {
418
+ browser.defaultProfile = defaultProfile;
419
+ changed = true;
420
+ }
421
+ const profiles = readStringRecord(browser.profiles);
422
+ const defaultProfilePath = normalizePathForConfig((0, external_node_path_namespaceObject.join)(profilesDir, defaultProfile));
423
+ if (!profiles[defaultProfile]) {
424
+ profiles[defaultProfile] = defaultProfilePath;
425
+ changed = true;
426
+ }
427
+ if (!isObjectRecord(browser.profiles) || Object.keys(profiles).length !== Object.keys(browser.profiles).length) changed = true;
428
+ browser.profiles = profiles;
429
+ const extensionsDir = "string" == typeof browser.extensionsDir && browser.extensionsDir.trim() ? browser.extensionsDir.trim() : DEFAULT_BROWSER_EXTENSIONS_DIR;
430
+ if (browser.extensionsDir !== extensionsDir) {
431
+ browser.extensionsDir = extensionsDir;
432
+ changed = true;
433
+ }
434
+ const extensions = readStringRecord(browser.extensions);
435
+ const defaultExtensionPath = normalizePathForConfig((0, external_node_path_namespaceObject.join)(extensionsDir, DEFAULT_BUNDLED_EXTENSION_ID));
436
+ if (!extensions[DEFAULT_BUNDLED_EXTENSION_ID]) {
437
+ extensions[DEFAULT_BUNDLED_EXTENSION_ID] = defaultExtensionPath;
438
+ changed = true;
439
+ }
440
+ if (!isObjectRecord(browser.extensions) || Object.keys(extensions).length !== Object.keys(browser.extensions).length) changed = true;
441
+ browser.extensions = extensions;
442
+ const defaultExtensions = new Set(readStringArray(browser.defaultExtensions));
443
+ if (!defaultExtensions.has(DEFAULT_BUNDLED_EXTENSION_ID)) {
444
+ defaultExtensions.add(DEFAULT_BUNDLED_EXTENSION_ID);
445
+ changed = true;
446
+ }
447
+ const nextDefaultExtensions = Array.from(defaultExtensions);
448
+ if (!Array.isArray(browser.defaultExtensions) || nextDefaultExtensions.length !== browser.defaultExtensions.length) changed = true;
449
+ browser.defaultExtensions = nextDefaultExtensions;
450
+ const transport = browser.transport;
451
+ if ("auto" !== transport && "playwright" !== transport && "relay" !== transport) {
452
+ browser.transport = DEFAULT_BROWSER_TRANSPORT;
453
+ changed = true;
454
+ }
455
+ config.browser = browser;
456
+ return {
457
+ config,
458
+ changed
459
+ };
460
+ }
461
+ function resolveBundledBrowserExtensionPath() {
462
+ const candidates = [
463
+ new URL("../../../../extensions/wingman-browser-extension", __rslib_import_meta_url__),
464
+ new URL("../../../extensions/wingman-browser-extension", __rslib_import_meta_url__)
465
+ ];
466
+ for (const candidate of candidates){
467
+ const resolved = (0, external_node_url_namespaceObject.fileURLToPath)(candidate);
468
+ if ((0, external_node_fs_namespaceObject.existsSync)(resolved) && (0, external_node_fs_namespaceObject.statSync)(resolved).isDirectory()) return resolved;
469
+ }
470
+ const cwdFallback = (0, external_node_path_namespaceObject.join)(process.cwd(), "extensions", "wingman-browser-extension");
471
+ if ((0, external_node_fs_namespaceObject.existsSync)(cwdFallback) && (0, external_node_fs_namespaceObject.statSync)(cwdFallback).isDirectory()) return cwdFallback;
472
+ return null;
473
+ }
474
+ function resolveWorkspacePath(workspace, configuredPath) {
475
+ return (0, external_node_path_namespaceObject.isAbsolute)(configuredPath) ? configuredPath : (0, external_node_path_namespaceObject.join)(workspace, configuredPath);
476
+ }
477
+ function bootstrapBrowserDefaults(configRoot, config) {
478
+ if (!isObjectRecord(config.browser)) return {};
479
+ const browser = config.browser;
480
+ const workspace = (0, external_node_path_namespaceObject.dirname)(configRoot);
481
+ let profileDirectory;
482
+ let extensionDirectory;
483
+ const defaultProfile = "string" == typeof browser.defaultProfile ? browser.defaultProfile.trim() : "";
484
+ if (defaultProfile) {
485
+ const profiles = readStringRecord(browser.profiles);
486
+ const profilesDir = "string" == typeof browser.profilesDir && browser.profilesDir.trim() ? browser.profilesDir.trim() : DEFAULT_BROWSER_PROFILES_DIR;
487
+ const profilePath = profiles[defaultProfile] || normalizePathForConfig((0, external_node_path_namespaceObject.join)(profilesDir, defaultProfile));
488
+ const absoluteProfilePath = resolveWorkspacePath(workspace, profilePath);
489
+ if (!(0, external_node_fs_namespaceObject.existsSync)(absoluteProfilePath)) profileDirectory = absoluteProfilePath;
490
+ (0, external_node_fs_namespaceObject.mkdirSync)(absoluteProfilePath, {
491
+ recursive: true
492
+ });
493
+ }
494
+ const extensions = readStringRecord(browser.extensions);
495
+ const bundledExtensionPath = extensions[DEFAULT_BUNDLED_EXTENSION_ID];
496
+ if (bundledExtensionPath) {
497
+ const absoluteExtensionPath = resolveWorkspacePath(workspace, bundledExtensionPath);
498
+ const bundledExtensionSource = resolveBundledBrowserExtensionPath();
499
+ const manifestPath = (0, external_node_path_namespaceObject.join)(absoluteExtensionPath, "manifest.json");
500
+ if (bundledExtensionSource && !(0, external_node_fs_namespaceObject.existsSync)(manifestPath)) {
501
+ copyDirectory(bundledExtensionSource, absoluteExtensionPath);
502
+ extensionDirectory = absoluteExtensionPath;
503
+ }
504
+ }
505
+ return {
506
+ profileDirectory,
507
+ extensionDirectory
508
+ };
375
509
  }
376
510
  async function handleAgentSetup(input) {
377
511
  const { configRoot, agentId, model, force, nonInteractive, outputManager, bundledAgentsPath, copyAgents, ensureDefaultAgent = true } = input;
@@ -1,5 +1,5 @@
1
1
  import { copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
2
- import { join } from "node:path";
2
+ import { dirname, isAbsolute, join } from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
4
  import { cancel, confirm as prompts_confirm, intro, isCancel, multiselect, note, outro, select as prompts_select, spinner, text as prompts_text } from "@clack/prompts";
5
5
  import chalk from "chalk";
@@ -21,6 +21,11 @@ const DEFAULT_TOOLS = [
21
21
  "think"
22
22
  ];
23
23
  const DEFAULT_FS_ROOT = ".";
24
+ const DEFAULT_BROWSER_PROFILE_ID = "default";
25
+ const DEFAULT_BROWSER_PROFILES_DIR = ".wingman/browser-profiles";
26
+ const DEFAULT_BROWSER_EXTENSIONS_DIR = ".wingman/browser-extensions";
27
+ const DEFAULT_BUNDLED_EXTENSION_ID = "wingman";
28
+ const DEFAULT_BROWSER_TRANSPORT = "auto";
24
29
  const DEFAULT_MODELS = {
25
30
  anthropic: "anthropic:claude-sonnet-4-5",
26
31
  openai: "openai:gpt-4o",
@@ -277,7 +282,10 @@ async function handleConfigSetup(input) {
277
282
  recursive: true
278
283
  });
279
284
  writeFileSync(configPath, JSON.stringify(nextConfig, null, 2));
285
+ const browserBootstrap = bootstrapBrowserDefaults(configRoot, nextConfig);
280
286
  writeLine(outputManager, `Saved config to ${configPath}`);
287
+ if (browserBootstrap.profileDirectory) writeLine(outputManager, `Prepared default browser profile at ${browserBootstrap.profileDirectory}`);
288
+ if (browserBootstrap.extensionDirectory) writeLine(outputManager, `Installed bundled browser extension at ${browserBootstrap.extensionDirectory}`);
281
289
  }
282
290
  function mergeConfigFile(configPath, agentId, fsRoot) {
283
291
  const raw = readFileSync(configPath, "utf-8");
@@ -316,6 +324,8 @@ function mergeConfigValues(config, agentId, fsRoot) {
316
324
  ...gateway,
317
325
  fsRoots
318
326
  };
327
+ const browserMerge = applyRecommendedBrowserConfig(nextConfig);
328
+ if (browserMerge.changed) changed = true;
319
329
  return {
320
330
  config: nextConfig,
321
331
  changed
@@ -330,7 +340,131 @@ function buildDefaultConfig(agentId, fsRoot) {
330
340
  fsRoot
331
341
  ]
332
342
  };
333
- return config;
343
+ const nextConfig = config;
344
+ applyRecommendedBrowserConfig(nextConfig);
345
+ return nextConfig;
346
+ }
347
+ function isObjectRecord(value) {
348
+ return Boolean(value) && "object" == typeof value && !Array.isArray(value);
349
+ }
350
+ function readStringRecord(value) {
351
+ if (!isObjectRecord(value)) return {};
352
+ const record = {};
353
+ for (const [key, entry] of Object.entries(value))if ("string" == typeof entry && entry.trim()) record[key] = entry.trim();
354
+ return record;
355
+ }
356
+ function readStringArray(value) {
357
+ if (!Array.isArray(value)) return [];
358
+ return value.filter((entry)=>"string" == typeof entry).map((entry)=>entry.trim()).filter((entry)=>entry.length > 0);
359
+ }
360
+ function normalizePathForConfig(pathValue) {
361
+ return pathValue.replaceAll("\\", "/");
362
+ }
363
+ function applyRecommendedBrowserConfig(config) {
364
+ let changed = false;
365
+ const browserRaw = config.browser;
366
+ const browser = isObjectRecord(browserRaw) ? {
367
+ ...browserRaw
368
+ } : {};
369
+ if (!isObjectRecord(browserRaw)) changed = true;
370
+ const profilesDir = "string" == typeof browser.profilesDir && browser.profilesDir.trim() ? browser.profilesDir.trim() : DEFAULT_BROWSER_PROFILES_DIR;
371
+ if (browser.profilesDir !== profilesDir) {
372
+ browser.profilesDir = profilesDir;
373
+ changed = true;
374
+ }
375
+ const defaultProfile = "string" == typeof browser.defaultProfile && browser.defaultProfile.trim() ? browser.defaultProfile.trim() : DEFAULT_BROWSER_PROFILE_ID;
376
+ if (browser.defaultProfile !== defaultProfile) {
377
+ browser.defaultProfile = defaultProfile;
378
+ changed = true;
379
+ }
380
+ const profiles = readStringRecord(browser.profiles);
381
+ const defaultProfilePath = normalizePathForConfig(join(profilesDir, defaultProfile));
382
+ if (!profiles[defaultProfile]) {
383
+ profiles[defaultProfile] = defaultProfilePath;
384
+ changed = true;
385
+ }
386
+ if (!isObjectRecord(browser.profiles) || Object.keys(profiles).length !== Object.keys(browser.profiles).length) changed = true;
387
+ browser.profiles = profiles;
388
+ const extensionsDir = "string" == typeof browser.extensionsDir && browser.extensionsDir.trim() ? browser.extensionsDir.trim() : DEFAULT_BROWSER_EXTENSIONS_DIR;
389
+ if (browser.extensionsDir !== extensionsDir) {
390
+ browser.extensionsDir = extensionsDir;
391
+ changed = true;
392
+ }
393
+ const extensions = readStringRecord(browser.extensions);
394
+ const defaultExtensionPath = normalizePathForConfig(join(extensionsDir, DEFAULT_BUNDLED_EXTENSION_ID));
395
+ if (!extensions[DEFAULT_BUNDLED_EXTENSION_ID]) {
396
+ extensions[DEFAULT_BUNDLED_EXTENSION_ID] = defaultExtensionPath;
397
+ changed = true;
398
+ }
399
+ if (!isObjectRecord(browser.extensions) || Object.keys(extensions).length !== Object.keys(browser.extensions).length) changed = true;
400
+ browser.extensions = extensions;
401
+ const defaultExtensions = new Set(readStringArray(browser.defaultExtensions));
402
+ if (!defaultExtensions.has(DEFAULT_BUNDLED_EXTENSION_ID)) {
403
+ defaultExtensions.add(DEFAULT_BUNDLED_EXTENSION_ID);
404
+ changed = true;
405
+ }
406
+ const nextDefaultExtensions = Array.from(defaultExtensions);
407
+ if (!Array.isArray(browser.defaultExtensions) || nextDefaultExtensions.length !== browser.defaultExtensions.length) changed = true;
408
+ browser.defaultExtensions = nextDefaultExtensions;
409
+ const transport = browser.transport;
410
+ if ("auto" !== transport && "playwright" !== transport && "relay" !== transport) {
411
+ browser.transport = DEFAULT_BROWSER_TRANSPORT;
412
+ changed = true;
413
+ }
414
+ config.browser = browser;
415
+ return {
416
+ config,
417
+ changed
418
+ };
419
+ }
420
+ function resolveBundledBrowserExtensionPath() {
421
+ const candidates = [
422
+ new URL("../../../../extensions/wingman-browser-extension", import.meta.url),
423
+ new URL("../../../extensions/wingman-browser-extension", import.meta.url)
424
+ ];
425
+ for (const candidate of candidates){
426
+ const resolved = fileURLToPath(candidate);
427
+ if (existsSync(resolved) && statSync(resolved).isDirectory()) return resolved;
428
+ }
429
+ const cwdFallback = join(process.cwd(), "extensions", "wingman-browser-extension");
430
+ if (existsSync(cwdFallback) && statSync(cwdFallback).isDirectory()) return cwdFallback;
431
+ return null;
432
+ }
433
+ function resolveWorkspacePath(workspace, configuredPath) {
434
+ return isAbsolute(configuredPath) ? configuredPath : join(workspace, configuredPath);
435
+ }
436
+ function bootstrapBrowserDefaults(configRoot, config) {
437
+ if (!isObjectRecord(config.browser)) return {};
438
+ const browser = config.browser;
439
+ const workspace = dirname(configRoot);
440
+ let profileDirectory;
441
+ let extensionDirectory;
442
+ const defaultProfile = "string" == typeof browser.defaultProfile ? browser.defaultProfile.trim() : "";
443
+ if (defaultProfile) {
444
+ const profiles = readStringRecord(browser.profiles);
445
+ const profilesDir = "string" == typeof browser.profilesDir && browser.profilesDir.trim() ? browser.profilesDir.trim() : DEFAULT_BROWSER_PROFILES_DIR;
446
+ const profilePath = profiles[defaultProfile] || normalizePathForConfig(join(profilesDir, defaultProfile));
447
+ const absoluteProfilePath = resolveWorkspacePath(workspace, profilePath);
448
+ if (!existsSync(absoluteProfilePath)) profileDirectory = absoluteProfilePath;
449
+ mkdirSync(absoluteProfilePath, {
450
+ recursive: true
451
+ });
452
+ }
453
+ const extensions = readStringRecord(browser.extensions);
454
+ const bundledExtensionPath = extensions[DEFAULT_BUNDLED_EXTENSION_ID];
455
+ if (bundledExtensionPath) {
456
+ const absoluteExtensionPath = resolveWorkspacePath(workspace, bundledExtensionPath);
457
+ const bundledExtensionSource = resolveBundledBrowserExtensionPath();
458
+ const manifestPath = join(absoluteExtensionPath, "manifest.json");
459
+ if (bundledExtensionSource && !existsSync(manifestPath)) {
460
+ copyDirectory(bundledExtensionSource, absoluteExtensionPath);
461
+ extensionDirectory = absoluteExtensionPath;
462
+ }
463
+ }
464
+ return {
465
+ profileDirectory,
466
+ extensionDirectory
467
+ };
334
468
  }
335
469
  async function handleAgentSetup(input) {
336
470
  const { configRoot, agentId, model, force, nonInteractive, outputManager, bundledAgentsPath, copyAgents, ensureDefaultAgent = true } = input;
@@ -42,6 +42,7 @@ async function executeSkillCommand(args, options = {}) {
42
42
  try {
43
43
  const repository = new skillRepository_cjs_namespaceObject.SkillRepository({
44
44
  provider: config.skills?.provider,
45
+ repositories: config.skills?.repositories,
45
46
  repositoryOwner: config.skills?.repositoryOwner,
46
47
  repositoryName: config.skills?.repositoryName,
47
48
  githubToken: config.skills?.githubToken,
@@ -126,9 +127,12 @@ Configuration:
126
127
  Skills can be configured in .wingman/wingman.config.json:
127
128
  {
128
129
  "skills": {
129
- "provider": "github",
130
- "repositoryOwner": "anthropics",
131
- "repositoryName": "skills",
130
+ "provider": "hybrid",
131
+ "repositories": [
132
+ { "owner": "your-org", "name": "your-skills-repo" }
133
+ ],
134
+ "repositoryOwner": "legacy-owner",
135
+ "repositoryName": "legacy-repo",
132
136
  "githubToken": "optional-token",
133
137
  "clawhubBaseUrl": "https://clawhub.ai",
134
138
  "skillsDirectory": "skills",
@@ -14,6 +14,7 @@ async function executeSkillCommand(args, options = {}) {
14
14
  try {
15
15
  const repository = new SkillRepository({
16
16
  provider: config.skills?.provider,
17
+ repositories: config.skills?.repositories,
17
18
  repositoryOwner: config.skills?.repositoryOwner,
18
19
  repositoryName: config.skills?.repositoryName,
19
20
  githubToken: config.skills?.githubToken,
@@ -98,9 +99,12 @@ Configuration:
98
99
  Skills can be configured in .wingman/wingman.config.json:
99
100
  {
100
101
  "skills": {
101
- "provider": "github",
102
- "repositoryOwner": "anthropics",
103
- "repositoryName": "skills",
102
+ "provider": "hybrid",
103
+ "repositories": [
104
+ { "owner": "your-org", "name": "your-skills-repo" }
105
+ ],
106
+ "repositoryOwner": "legacy-owner",
107
+ "repositoryName": "legacy-repo",
104
108
  "githubToken": "optional-token",
105
109
  "clawhubBaseUrl": "https://clawhub.ai",
106
110
  "skillsDirectory": "skills",
@@ -133,9 +133,13 @@ class WingmanConfigLoader {
133
133
  outputMode: "auto"
134
134
  },
135
135
  skills: {
136
- provider: "github",
137
- repositoryOwner: "anthropics",
138
- repositoryName: "skills",
136
+ provider: "hybrid",
137
+ repositories: [
138
+ {
139
+ owner: "RussellCanfield",
140
+ name: "wingman-ai"
141
+ }
142
+ ],
139
143
  clawhubBaseUrl: "https://clawhub.ai",
140
144
  skillsDirectory: "skills",
141
145
  security: {
@@ -105,9 +105,13 @@ class WingmanConfigLoader {
105
105
  outputMode: "auto"
106
106
  },
107
107
  skills: {
108
- provider: "github",
109
- repositoryOwner: "anthropics",
110
- repositoryName: "skills",
108
+ provider: "hybrid",
109
+ repositories: [
110
+ {
111
+ owner: "RussellCanfield",
112
+ name: "wingman-ai"
113
+ }
114
+ ],
111
115
  clawhubBaseUrl: "https://clawhub.ai",
112
116
  skillsDirectory: "skills",
113
117
  security: {
@@ -27,15 +27,16 @@ __webpack_require__.d(__webpack_exports__, {
27
27
  SearchConfigSchema: ()=>SearchConfigSchema,
28
28
  HumanInTheLoopConfigSchema: ()=>HumanInTheLoopConfigSchema,
29
29
  AgentsConfigSchema: ()=>AgentsConfigSchema,
30
+ validateConfig: ()=>validateConfig,
30
31
  GatewayConfigSchema: ()=>GatewayConfigSchema,
31
32
  BrowserTransportSchema: ()=>BrowserTransportSchema,
32
33
  SkillsConfigSchema: ()=>SkillsConfigSchema,
33
34
  SummarizationConfigSchema: ()=>SummarizationConfigSchema,
34
35
  BrowserConfigSchema: ()=>BrowserConfigSchema,
36
+ GitHubSkillRepositorySchema: ()=>GitHubSkillRepositorySchema,
35
37
  ModelRetryConfigSchema: ()=>ModelRetryConfigSchema,
36
- WingmanConfigSchema: ()=>WingmanConfigSchema,
37
38
  ToolRetryConfigSchema: ()=>ToolRetryConfigSchema,
38
- validateConfig: ()=>validateConfig
39
+ WingmanConfigSchema: ()=>WingmanConfigSchema
39
40
  });
40
41
  const external_zod_namespaceObject = require("zod");
41
42
  const types_cjs_namespaceObject = require("../../agent/middleware/hooks/types.cjs");
@@ -49,13 +50,24 @@ const SearchConfigSchema = external_zod_namespaceObject.object({
49
50
  ]).default("duckduckgo").describe("Search provider to use"),
50
51
  maxResults: external_zod_namespaceObject.number().min(1).max(20).optional().default(5).describe("Maximum number of search results to return")
51
52
  });
53
+ const GitHubSkillRepositorySchema = external_zod_namespaceObject.object({
54
+ owner: external_zod_namespaceObject.string().min(1).describe("GitHub repository owner for a skills source"),
55
+ name: external_zod_namespaceObject.string().min(1).describe("GitHub repository name for a skills source")
56
+ });
52
57
  const SkillsConfigSchema = external_zod_namespaceObject.object({
53
58
  provider: external_zod_namespaceObject["enum"]([
54
59
  "github",
55
- "clawhub"
56
- ]).default("github").describe("Skill source provider"),
57
- repositoryOwner: external_zod_namespaceObject.string().default("anthropics").describe("GitHub repository owner for skills"),
58
- repositoryName: external_zod_namespaceObject.string().default("skills").describe("GitHub repository name for skills"),
60
+ "clawhub",
61
+ "hybrid"
62
+ ]).default("hybrid").describe("Skill source provider"),
63
+ repositories: external_zod_namespaceObject.array(GitHubSkillRepositorySchema).optional().default([
64
+ {
65
+ owner: "RussellCanfield",
66
+ name: "wingman-ai"
67
+ }
68
+ ]).describe("Ordered GitHub skill sources. Later repositories override earlier repositories on name conflicts."),
69
+ repositoryOwner: external_zod_namespaceObject.string().optional().describe("Legacy GitHub repository owner fallback when repositories is empty (requires repositoryName)"),
70
+ repositoryName: external_zod_namespaceObject.string().optional().describe("Legacy GitHub repository name fallback when repositories is empty (requires repositoryOwner)"),
59
71
  githubToken: external_zod_namespaceObject.string().optional().describe("GitHub personal access token for higher API rate limits"),
60
72
  clawhubBaseUrl: external_zod_namespaceObject.string().default("https://clawhub.ai").describe("Base URL for ClawHub skill API"),
61
73
  skillsDirectory: external_zod_namespaceObject.string().default("skills").describe("Directory to install skills in"),
@@ -386,9 +398,13 @@ const WingmanConfigSchema = external_zod_namespaceObject.object({
386
398
  outputMode: "auto"
387
399
  }),
388
400
  skills: SkillsConfigSchema.optional().default({
389
- provider: "github",
390
- repositoryOwner: "anthropics",
391
- repositoryName: "skills",
401
+ provider: "hybrid",
402
+ repositories: [
403
+ {
404
+ owner: "RussellCanfield",
405
+ name: "wingman-ai"
406
+ }
407
+ ],
392
408
  clawhubBaseUrl: "https://clawhub.ai",
393
409
  skillsDirectory: "skills",
394
410
  security: {
@@ -482,6 +498,7 @@ exports.AgentsConfigSchema = __webpack_exports__.AgentsConfigSchema;
482
498
  exports.BrowserConfigSchema = __webpack_exports__.BrowserConfigSchema;
483
499
  exports.BrowserTransportSchema = __webpack_exports__.BrowserTransportSchema;
484
500
  exports.GatewayConfigSchema = __webpack_exports__.GatewayConfigSchema;
501
+ exports.GitHubSkillRepositorySchema = __webpack_exports__.GitHubSkillRepositorySchema;
485
502
  exports.HumanInTheLoopConfigSchema = __webpack_exports__.HumanInTheLoopConfigSchema;
486
503
  exports.ModelRetryConfigSchema = __webpack_exports__.ModelRetryConfigSchema;
487
504
  exports.SearchConfigSchema = __webpack_exports__.SearchConfigSchema;
@@ -495,6 +512,7 @@ for(var __rspack_i in __webpack_exports__)if (-1 === [
495
512
  "BrowserConfigSchema",
496
513
  "BrowserTransportSchema",
497
514
  "GatewayConfigSchema",
515
+ "GitHubSkillRepositorySchema",
498
516
  "HumanInTheLoopConfigSchema",
499
517
  "ModelRetryConfigSchema",
500
518
  "SearchConfigSchema",
@@ -7,13 +7,22 @@ export declare const SearchConfigSchema: z.ZodObject<{
7
7
  maxResults: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
8
8
  }, z.core.$strip>;
9
9
  export type SearchConfig = z.infer<typeof SearchConfigSchema>;
10
+ export declare const GitHubSkillRepositorySchema: z.ZodObject<{
11
+ owner: z.ZodString;
12
+ name: z.ZodString;
13
+ }, z.core.$strip>;
10
14
  export declare const SkillsConfigSchema: z.ZodObject<{
11
15
  provider: z.ZodDefault<z.ZodEnum<{
12
16
  github: "github";
13
17
  clawhub: "clawhub";
18
+ hybrid: "hybrid";
14
19
  }>>;
15
- repositoryOwner: z.ZodDefault<z.ZodString>;
16
- repositoryName: z.ZodDefault<z.ZodString>;
20
+ repositories: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodObject<{
21
+ owner: z.ZodString;
22
+ name: z.ZodString;
23
+ }, z.core.$strip>>>>;
24
+ repositoryOwner: z.ZodOptional<z.ZodString>;
25
+ repositoryName: z.ZodOptional<z.ZodString>;
17
26
  githubToken: z.ZodOptional<z.ZodString>;
18
27
  clawhubBaseUrl: z.ZodDefault<z.ZodString>;
19
28
  skillsDirectory: z.ZodDefault<z.ZodString>;
@@ -315,9 +324,14 @@ export declare const WingmanConfigSchema: z.ZodObject<{
315
324
  provider: z.ZodDefault<z.ZodEnum<{
316
325
  github: "github";
317
326
  clawhub: "clawhub";
327
+ hybrid: "hybrid";
318
328
  }>>;
319
- repositoryOwner: z.ZodDefault<z.ZodString>;
320
- repositoryName: z.ZodDefault<z.ZodString>;
329
+ repositories: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodObject<{
330
+ owner: z.ZodString;
331
+ name: z.ZodString;
332
+ }, z.core.$strip>>>>;
333
+ repositoryOwner: z.ZodOptional<z.ZodString>;
334
+ repositoryName: z.ZodOptional<z.ZodString>;
321
335
  githubToken: z.ZodOptional<z.ZodString>;
322
336
  clawhubBaseUrl: z.ZodDefault<z.ZodString>;
323
337
  skillsDirectory: z.ZodDefault<z.ZodString>;
@@ -10,13 +10,24 @@ const SearchConfigSchema = object({
10
10
  ]).default("duckduckgo").describe("Search provider to use"),
11
11
  maxResults: number().min(1).max(20).optional().default(5).describe("Maximum number of search results to return")
12
12
  });
13
+ const GitHubSkillRepositorySchema = object({
14
+ owner: string().min(1).describe("GitHub repository owner for a skills source"),
15
+ name: string().min(1).describe("GitHub repository name for a skills source")
16
+ });
13
17
  const SkillsConfigSchema = object({
14
18
  provider: external_zod_enum([
15
19
  "github",
16
- "clawhub"
17
- ]).default("github").describe("Skill source provider"),
18
- repositoryOwner: string().default("anthropics").describe("GitHub repository owner for skills"),
19
- repositoryName: string().default("skills").describe("GitHub repository name for skills"),
20
+ "clawhub",
21
+ "hybrid"
22
+ ]).default("hybrid").describe("Skill source provider"),
23
+ repositories: array(GitHubSkillRepositorySchema).optional().default([
24
+ {
25
+ owner: "RussellCanfield",
26
+ name: "wingman-ai"
27
+ }
28
+ ]).describe("Ordered GitHub skill sources. Later repositories override earlier repositories on name conflicts."),
29
+ repositoryOwner: string().optional().describe("Legacy GitHub repository owner fallback when repositories is empty (requires repositoryName)"),
30
+ repositoryName: string().optional().describe("Legacy GitHub repository name fallback when repositories is empty (requires repositoryOwner)"),
20
31
  githubToken: string().optional().describe("GitHub personal access token for higher API rate limits"),
21
32
  clawhubBaseUrl: string().default("https://clawhub.ai").describe("Base URL for ClawHub skill API"),
22
33
  skillsDirectory: string().default("skills").describe("Directory to install skills in"),
@@ -347,9 +358,13 @@ const WingmanConfigSchema = object({
347
358
  outputMode: "auto"
348
359
  }),
349
360
  skills: SkillsConfigSchema.optional().default({
350
- provider: "github",
351
- repositoryOwner: "anthropics",
352
- repositoryName: "skills",
361
+ provider: "hybrid",
362
+ repositories: [
363
+ {
364
+ owner: "RussellCanfield",
365
+ name: "wingman-ai"
366
+ }
367
+ ],
353
368
  clawhubBaseUrl: "https://clawhub.ai",
354
369
  skillsDirectory: "skills",
355
370
  security: {
@@ -439,4 +454,4 @@ function validateConfig(data) {
439
454
  };
440
455
  }
441
456
  }
442
- export { AgentsConfigSchema, BrowserConfigSchema, BrowserTransportSchema, GatewayConfigSchema, HumanInTheLoopConfigSchema, ModelRetryConfigSchema, SearchConfigSchema, SkillsConfigSchema, SummarizationConfigSchema, ToolRetryConfigSchema, WingmanConfigSchema, validateConfig };
457
+ export { AgentsConfigSchema, BrowserConfigSchema, BrowserTransportSchema, GatewayConfigSchema, GitHubSkillRepositorySchema, HumanInTheLoopConfigSchema, ModelRetryConfigSchema, SearchConfigSchema, SkillsConfigSchema, SummarizationConfigSchema, ToolRetryConfigSchema, WingmanConfigSchema, validateConfig };