@naraya/cli 0.1.0 → 0.4.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.
Files changed (83) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +394 -93
  3. package/bin/naraya-native.mjs +4 -0
  4. package/bin/naraya.mjs +1 -142
  5. package/bin/undici-timeout.mjs +1 -0
  6. package/dist/assets.pack.gz +0 -0
  7. package/dist/mcp/config-loader.js +32 -0
  8. package/dist/mcp/lifecycle.js +90 -0
  9. package/dist/mcp/tool-mapper.js +31 -0
  10. package/dist/mcp/transport.js +30 -0
  11. package/dist/pentest/catalog/catalog-loader.js +45 -0
  12. package/dist/pentest/catalog/index.js +1 -0
  13. package/dist/pentest/cli.js +117 -0
  14. package/dist/pentest/command-builder/command-builder.js +90 -0
  15. package/dist/pentest/command-builder/index.js +1 -0
  16. package/dist/pentest/index.js +10 -0
  17. package/dist/pentest/installer/index.js +1 -0
  18. package/dist/pentest/installer/tool-installer.js +90 -0
  19. package/dist/pentest/manager.js +125 -0
  20. package/dist/pentest/mode/index.js +1 -0
  21. package/dist/pentest/mode/mode-selector.js +127 -0
  22. package/dist/pentest/selector/index.js +1 -0
  23. package/dist/pentest/selector/tool-selector.js +66 -0
  24. package/dist/pentest/skill-bridge/index.js +1 -0
  25. package/dist/pentest/skill-bridge/skill-bridge.js +66 -0
  26. package/dist/pentest/skills/generator/index.js +1 -0
  27. package/dist/pentest/skills/generator/skill-generator.js +310 -0
  28. package/dist/pentest/skills/index.js +3 -0
  29. package/dist/pentest/skills/loader/index.js +1 -0
  30. package/dist/pentest/skills/loader/skill-loader.js +167 -0
  31. package/dist/pentest/skills/register/index.js +1 -0
  32. package/dist/pentest/skills/register/skill-register.js +162 -0
  33. package/dist/pentest/skills/types.js +1 -0
  34. package/dist/pentest/types.js +90 -0
  35. package/package.json +42 -14
  36. package/src/assets-pack.mjs +1 -0
  37. package/src/banner.mjs +5 -0
  38. package/src/clipboard.mjs +1 -0
  39. package/src/config.mjs +1 -40
  40. package/src/goodbye.mjs +7 -0
  41. package/src/login.mjs +7 -49
  42. package/src/mcp/config-loader.ts +50 -0
  43. package/src/mcp/lifecycle.ts +113 -0
  44. package/src/mcp/tool-mapper.ts +42 -0
  45. package/src/mcp/transport.ts +38 -0
  46. package/src/mcp-cli.mjs +5 -0
  47. package/src/pentest/catalog/catalog-loader.ts +55 -0
  48. package/src/pentest/catalog/index.ts +1 -0
  49. package/src/pentest/cli.ts +130 -0
  50. package/src/pentest/command-builder/command-builder.ts +109 -0
  51. package/src/pentest/command-builder/index.ts +1 -0
  52. package/src/pentest/index.ts +11 -0
  53. package/src/pentest/installer/index.ts +1 -0
  54. package/src/pentest/installer/tool-installer.ts +107 -0
  55. package/src/pentest/manager.ts +167 -0
  56. package/src/pentest/mode/index.ts +1 -0
  57. package/src/pentest/mode/mode-selector.ts +159 -0
  58. package/src/pentest/selector/index.ts +1 -0
  59. package/src/pentest/selector/tool-selector.ts +87 -0
  60. package/src/pentest/skill-bridge/index.ts +1 -0
  61. package/src/pentest/skill-bridge/skill-bridge.ts +86 -0
  62. package/src/pentest/skills/generator/index.ts +1 -0
  63. package/src/pentest/skills/generator/skill-generator.ts +373 -0
  64. package/src/pentest/skills/index.ts +4 -0
  65. package/src/pentest/skills/loader/index.ts +1 -0
  66. package/src/pentest/skills/loader/skill-loader.ts +206 -0
  67. package/src/pentest/skills/register/index.ts +1 -0
  68. package/src/pentest/skills/register/skill-register.ts +196 -0
  69. package/src/pentest/skills/types.ts +66 -0
  70. package/src/pentest/types.ts +341 -0
  71. package/src/seed.mjs +1 -36
  72. package/src/splash.mjs +4 -0
  73. package/src/status.mjs +2 -71
  74. package/assets/APPEND-SYSTEM.md +0 -9
  75. package/assets/extensions/naraya-brand.ts +0 -251
  76. package/assets/extensions/naraya-gate.ts +0 -23
  77. package/assets/naraya-logo.txt +0 -5
  78. package/assets/skills/narabuild/SKILL.md +0 -156
  79. package/assets/skills/naradroid/SKILL.md +0 -118
  80. package/assets/skills/naraexplore/SKILL.md +0 -71
  81. package/assets/skills/narafe/SKILL.md +0 -94
  82. package/assets/skills/naraplan/SKILL.md +0 -47
  83. package/assets/skills/narasearch/SKILL.md +0 -141
@@ -0,0 +1,90 @@
1
+ import { exec } from "child_process";
2
+ import { promisify } from "util";
3
+ const execAsync = promisify(exec);
4
+ export async function checkToolInstalled(tool) {
5
+ try {
6
+ const { stdout } = await execAsync(tool.check_installed.command, { timeout: 10000 });
7
+ let version;
8
+ if (tool.check_installed.parse_version) {
9
+ const match = stdout.match(new RegExp(tool.check_installed.parse_version));
10
+ version = match?.[1];
11
+ }
12
+ return {
13
+ tools_name: tool.tools_name,
14
+ installed: true,
15
+ version
16
+ };
17
+ }
18
+ catch (error) {
19
+ return {
20
+ tools_name: tool.tools_name,
21
+ installed: false,
22
+ error: error instanceof Error ? error.message : "Unknown error"
23
+ };
24
+ }
25
+ }
26
+ export async function checkAllToolsInstalled(tools) {
27
+ return Promise.all(tools.map(checkToolInstalled));
28
+ }
29
+ export function getInstallCommand(tool, platform = process.platform) {
30
+ const config = tool.installation[platform];
31
+ return config?.command ?? null;
32
+ }
33
+ export function getInstallCommands(tool) {
34
+ const result = {};
35
+ if (tool.installation.linux)
36
+ result.linux = tool.installation.linux;
37
+ if (tool.installation.darwin)
38
+ result.darwin = tool.installation.darwin;
39
+ if (tool.installation.win32)
40
+ result.win32 = tool.installation.win32;
41
+ return result;
42
+ }
43
+ export async function installTool(tool, platform = process.platform) {
44
+ const config = tool.installation[platform];
45
+ if (!config) {
46
+ return {
47
+ success: false,
48
+ message: `No installation command available for platform: ${platform}`
49
+ };
50
+ }
51
+ try {
52
+ const { stdout, stderr } = await execAsync(config.command, { timeout: 300000 });
53
+ return {
54
+ success: true,
55
+ message: stdout || stderr || "Installation completed"
56
+ };
57
+ }
58
+ catch (error) {
59
+ return {
60
+ success: false,
61
+ message: error instanceof Error ? error.message : "Installation failed"
62
+ };
63
+ }
64
+ }
65
+ export function getMissingTools(availability) {
66
+ return availability.filter(a => !a.installed);
67
+ }
68
+ export function getInstalledTools(availability) {
69
+ return availability.filter(a => a.installed);
70
+ }
71
+ export async function ensureToolsInstalled(tools, platform = process.platform) {
72
+ const availability = await checkAllToolsInstalled(tools);
73
+ const missing = getMissingTools(availability);
74
+ const failed = [];
75
+ for (const tool of missing) {
76
+ const toolEntry = tools.find(t => t.tools_name === tool.tools_name);
77
+ if (!toolEntry)
78
+ continue;
79
+ const result = await installTool(toolEntry, platform);
80
+ if (!result.success) {
81
+ failed.push({ tool: tool.tools_name, error: result.message });
82
+ }
83
+ }
84
+ const recheck = await checkAllToolsInstalled(tools);
85
+ return {
86
+ installed: getInstalledTools(recheck),
87
+ missing: getMissingTools(recheck),
88
+ failed
89
+ };
90
+ }
@@ -0,0 +1,125 @@
1
+ import { discoverSkills, loadSkill, loadAllSkills, loadSkillsByPhase } from "./skills/loader/skill-loader.js";
2
+ import { SkillRegister } from "./skills/register/skill-register.js";
3
+ import { selectTools, selectToolsByPhase, selectToolsByCategory } from "./selector/tool-selector.js";
4
+ import { buildCommand, buildCommandWithSudo, validateRequiredFlags } from "./command-builder/command-builder.js";
5
+ import { checkToolInstalled, checkAllToolsInstalled, getInstallCommand, installTool } from "./installer/tool-installer.js";
6
+ import { selectMode, detectMode, getModeConfig, getSkillsForMode, getLoopConfig, getSafetyConfig } from "./mode/mode-selector.js";
7
+ import { existsSync, readFileSync } from "fs";
8
+ import { join } from "path";
9
+ const DEFAULT_SKILL_DIRS = [
10
+ "assets/agents/skills",
11
+ "skills/",
12
+ "packages/pentest-skills/skills",
13
+ ];
14
+ const DEFAULT_CATALOG_PATH = "tools-catalog.json";
15
+ export class PentestManager {
16
+ config;
17
+ cache = {
18
+ manifests: new Map(),
19
+ skills: new Map(),
20
+ catalog: null,
21
+ };
22
+ skillRegister = null;
23
+ constructor(config = {}) {
24
+ this.config = {
25
+ baseDir: config.baseDir ?? process.cwd(),
26
+ skillDirs: config.skillDirs ?? DEFAULT_SKILL_DIRS,
27
+ configPath: config.configPath ?? join(config.baseDir ?? process.cwd(), ".pentestrc"),
28
+ catalogPath: config.catalogPath ?? join(config.baseDir ?? process.cwd(), DEFAULT_CATALOG_PATH),
29
+ };
30
+ }
31
+ // ── Mode Operations ──
32
+ selectMode(options = {}) {
33
+ return selectMode(options);
34
+ }
35
+ detectMode(target) {
36
+ return detectMode(target);
37
+ }
38
+ getModeConfig(mode) {
39
+ return getModeConfig(mode);
40
+ }
41
+ getSkillsForMode(mode) {
42
+ return getSkillsForMode(mode);
43
+ }
44
+ getLoopConfig(mode) {
45
+ return getLoopConfig(mode);
46
+ }
47
+ getSafetyConfig(mode) {
48
+ return getSafetyConfig(mode);
49
+ }
50
+ // ── Skill Operations ──
51
+ discoverSkills() {
52
+ return discoverSkills(this.config.baseDir);
53
+ }
54
+ loadSkill(skillName) {
55
+ return loadSkill(skillName, this.config.baseDir);
56
+ }
57
+ loadAllSkills() {
58
+ return loadAllSkills(this.config.baseDir);
59
+ }
60
+ loadSkillsByPhase(phase) {
61
+ return loadSkillsByPhase(phase, this.config.baseDir);
62
+ }
63
+ // ── Catalog Operations ──
64
+ async loadCatalog() {
65
+ if (this.cache.catalog)
66
+ return this.cache.catalog;
67
+ const catalogPath = this.config.catalogPath;
68
+ if (!existsSync(catalogPath)) {
69
+ throw new Error(`Tools catalog not found: ${catalogPath}`);
70
+ }
71
+ const content = readFileSync(catalogPath, "utf-8");
72
+ this.cache.catalog = JSON.parse(content);
73
+ return this.cache.catalog;
74
+ }
75
+ async selectTools(options) {
76
+ const catalog = await this.loadCatalog();
77
+ return selectTools(catalog, options);
78
+ }
79
+ async selectToolsByPhase(phase) {
80
+ const catalog = await this.loadCatalog();
81
+ return selectToolsByPhase(catalog, phase);
82
+ }
83
+ async selectToolsByCategory(category) {
84
+ const catalog = await this.loadCatalog();
85
+ return selectToolsByCategory(catalog, category);
86
+ }
87
+ // ── Command Operations ──
88
+ buildToolCommand(tool, flags = {}, positional = []) {
89
+ const result = buildCommand(tool, { flags, positional });
90
+ return result.fullCommand;
91
+ }
92
+ buildToolCommandWithSudo(tool, flags = {}, positional = []) {
93
+ return buildCommandWithSudo(tool, { flags, positional });
94
+ }
95
+ validateToolFlags(tool, flags) {
96
+ return validateRequiredFlags(tool, { flags });
97
+ }
98
+ // ── Installation Operations ──
99
+ async checkToolInstalled(tool) {
100
+ return checkToolInstalled(tool);
101
+ }
102
+ async checkAllToolsInstalled(tools) {
103
+ return checkAllToolsInstalled(tools);
104
+ }
105
+ getInstallCommandForTool(tool) {
106
+ return getInstallCommand(tool);
107
+ }
108
+ async installTool(tool) {
109
+ return installTool(tool);
110
+ }
111
+ // ── Skill Register ──
112
+ getRegister() {
113
+ if (!this.skillRegister) {
114
+ this.skillRegister = new SkillRegister({
115
+ skills_dir: join(this.config.baseDir, ".agents/skills"),
116
+ search_paths: [
117
+ join(this.config.baseDir, ".agents/skills"),
118
+ join(this.config.baseDir, ".opencode/skills"),
119
+ join(this.config.baseDir, ".claude/skills"),
120
+ ],
121
+ });
122
+ }
123
+ return this.skillRegister;
124
+ }
125
+ }
@@ -0,0 +1 @@
1
+ export * from "./mode-selector.js";
@@ -0,0 +1,127 @@
1
+ import { MODE_PRESETS } from "../types.js";
2
+ const WEB_EXTENSIONS = [".php", ".asp", ".aspx", ".jsp", ".cgi", ".pl", ".py", ".rb"];
3
+ const NETWORK_PORTS = [22, 23, 3389, 445, 139, 135, 1433, 3306, 5432, 27017];
4
+ const CTF_INDICATORS = ["ctf", "chall", "pwn", "crypto", "forensics", "rev", "misc", "web"];
5
+ const RED_TEAM_INDICATORS = ["ad", "active-directory", "kerberos", "ldap", "smb", "rdp"];
6
+ export function detectMode(target) {
7
+ const lower = target.toLowerCase();
8
+ for (const indicator of CTF_INDICATORS) {
9
+ if (lower.includes(indicator)) {
10
+ return { mode: "ctf", reason: `CTF indicator detected: ${indicator}` };
11
+ }
12
+ }
13
+ for (const indicator of RED_TEAM_INDICATORS) {
14
+ if (lower.includes(indicator)) {
15
+ return { mode: "red-team", reason: `Red team indicator detected: ${indicator}` };
16
+ }
17
+ }
18
+ for (const ext of WEB_EXTENSIONS) {
19
+ if (lower.endsWith(ext)) {
20
+ return { mode: "bug-bounty", reason: `Web extension detected: ${ext}` };
21
+ }
22
+ }
23
+ const portMatch = lower.match(/:(\d+)/);
24
+ if (portMatch) {
25
+ const port = parseInt(portMatch[1], 10);
26
+ if (NETWORK_PORTS.includes(port)) {
27
+ return { mode: "red-team", reason: `Network service port detected: ${port}` };
28
+ }
29
+ }
30
+ if (lower.includes("http://") || lower.includes("https://")) {
31
+ return { mode: "bug-bounty", reason: "Web target detected (HTTP/HTTPS)" };
32
+ }
33
+ const ipMatch = lower.match(/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/);
34
+ if (ipMatch) {
35
+ return { mode: "red-team", reason: `IP address target: ${ipMatch[0]}` };
36
+ }
37
+ return { mode: "auto", reason: "No specific indicators detected, using adaptive mode" };
38
+ }
39
+ export function selectMode(options = {}) {
40
+ if (options.preferred_mode && options.preferred_mode !== "auto") {
41
+ const config = MODE_PRESETS[options.preferred_mode];
42
+ return {
43
+ selected_mode: options.preferred_mode,
44
+ config,
45
+ auto_detected: false,
46
+ detection_reason: `User selected mode: ${options.preferred_mode}`,
47
+ };
48
+ }
49
+ if (options.auto_detect !== false && options.target) {
50
+ const detection = detectMode(options.target);
51
+ if (detection.mode !== "auto") {
52
+ const config = MODE_PRESETS[detection.mode];
53
+ return {
54
+ selected_mode: detection.mode,
55
+ config,
56
+ auto_detected: true,
57
+ detection_reason: detection.reason,
58
+ };
59
+ }
60
+ }
61
+ const config = MODE_PRESETS["auto"];
62
+ return {
63
+ selected_mode: "auto",
64
+ config,
65
+ auto_detected: false,
66
+ detection_reason: "No specific mode detected, using adaptive auto mode",
67
+ };
68
+ }
69
+ export function getModeConfig(mode) {
70
+ return MODE_PRESETS[mode];
71
+ }
72
+ export function getToolsForMode(mode, availableTools) {
73
+ const config = MODE_PRESETS[mode];
74
+ const priority = config.tool_priority;
75
+ const prioritized = [];
76
+ const remaining = [];
77
+ for (const tool of availableTools) {
78
+ const toolCategory = getToolCategory(tool);
79
+ if (toolCategory && priority.includes(toolCategory)) {
80
+ prioritized.push(tool);
81
+ }
82
+ else {
83
+ remaining.push(tool);
84
+ }
85
+ }
86
+ prioritized.sort((a, b) => {
87
+ const catA = getToolCategory(a);
88
+ const catB = getToolCategory(b);
89
+ if (!catA || !catB)
90
+ return 0;
91
+ return priority.indexOf(catA) - priority.indexOf(catB);
92
+ });
93
+ return [...prioritized, ...remaining];
94
+ }
95
+ function getToolCategory(toolName) {
96
+ const categoryMap = {
97
+ subfinder: "recon", amass: "recon", assetfinder: "recon", httpx: "recon",
98
+ naabu: "recon", massdns: "recon", nmap: "enumeration", nuclei: "enumeration",
99
+ ffuf: "enumeration", dirsearch: "enumeration", gobuster: "enumeration",
100
+ feroxbuster: "enumeration", whatweb: "enumeration", wafw00f: "enumeration",
101
+ nikto: "enumeration", burpsuite: "enumeration", owasp_zap: "enumeration",
102
+ sqlmap: "exploitation", commix: "exploitation", hydra: "exploitation",
103
+ hashcat: "exploitation", john: "exploitation", metasploit: "exploitation",
104
+ pwntools: "exploitation",
105
+ curl: "utility", jq: "utility", anew: "utility", grep: "utility",
106
+ sort: "utility", uniq: "utility", notify: "reporting",
107
+ };
108
+ return categoryMap[toolName];
109
+ }
110
+ export function getSkillsForMode(mode) {
111
+ return [...MODE_PRESETS[mode].skill_chain];
112
+ }
113
+ export function getLoopConfig(mode) {
114
+ return MODE_PRESETS[mode].loop_config;
115
+ }
116
+ export function getSafetyConfig(mode) {
117
+ return MODE_PRESETS[mode].safety_constraints;
118
+ }
119
+ export function getReportFormat(mode) {
120
+ return MODE_PRESETS[mode].report_format;
121
+ }
122
+ export function isModeStealth(mode) {
123
+ return MODE_PRESETS[mode].stealth;
124
+ }
125
+ export function getModeParallelism(mode) {
126
+ return MODE_PRESETS[mode].parallelism;
127
+ }
@@ -0,0 +1 @@
1
+ export * from "./tool-selector.js";
@@ -0,0 +1,66 @@
1
+ export function selectTools(catalog, options) {
2
+ let results = [...catalog.tools];
3
+ if (options.category) {
4
+ results = results.filter(t => t.category === options.category);
5
+ }
6
+ if (options.phase) {
7
+ results = results.filter(t => t.phase.includes(options.phase));
8
+ }
9
+ if (options.tags && options.tags.length > 0) {
10
+ results = results.filter(t => options.tags.some(tag => t.tags.includes(tag)));
11
+ }
12
+ if (options.requires_root !== undefined) {
13
+ results = results.filter(t => t.requires_root === options.requires_root);
14
+ }
15
+ return results;
16
+ }
17
+ export function selectToolsByPhase(catalog, phase) {
18
+ return selectTools(catalog, { phase });
19
+ }
20
+ export function selectToolsByCategory(catalog, category) {
21
+ return selectTools(catalog, { category });
22
+ }
23
+ export function selectToolsByTags(catalog, tags) {
24
+ return selectTools(catalog, { tags });
25
+ }
26
+ export function groupToolsByPhase(catalog) {
27
+ const groups = {
28
+ recon: [],
29
+ enumeration: [],
30
+ exploitation: [],
31
+ reporting: [],
32
+ utility: []
33
+ };
34
+ for (const tool of catalog.tools) {
35
+ for (const phase of tool.phase) {
36
+ if (groups[phase]) {
37
+ groups[phase].push(tool);
38
+ }
39
+ }
40
+ }
41
+ return groups;
42
+ }
43
+ export function groupToolsByCategory(catalog) {
44
+ const groups = {};
45
+ for (const category of catalog.categories) {
46
+ groups[category] = [];
47
+ }
48
+ for (const tool of catalog.tools) {
49
+ if (groups[tool.category]) {
50
+ groups[tool.category].push(tool);
51
+ }
52
+ }
53
+ return groups;
54
+ }
55
+ export function findAlternatives(catalog, toolName) {
56
+ const tool = catalog.tools.find(t => t.tools_name === toolName);
57
+ if (!tool?.alternatives?.length)
58
+ return [];
59
+ return catalog.tools.filter(t => tool.alternatives.includes(t.tools_name));
60
+ }
61
+ export function getToolPipeline(catalog, toolName) {
62
+ const tool = catalog.tools.find(t => t.tools_name === toolName);
63
+ if (!tool?.command.pipes?.length)
64
+ return [];
65
+ return catalog.tools.filter(t => tool.command.pipes.includes(t.tools_name));
66
+ }
@@ -0,0 +1 @@
1
+ export * from "./skill-bridge.js";
@@ -0,0 +1,66 @@
1
+ import { existsSync } from "fs";
2
+ import { join } from "path";
3
+ const SKILL_DIRS = [
4
+ ".agents/skills",
5
+ ".opencode/skills",
6
+ ".claude/skills"
7
+ ];
8
+ export function resolveSkillPath(skillName, baseDir = process.cwd()) {
9
+ for (const dir of SKILL_DIRS) {
10
+ const skillPath = join(baseDir, dir, skillName, "SKILL.md");
11
+ if (existsSync(skillPath)) {
12
+ return skillPath;
13
+ }
14
+ }
15
+ return null;
16
+ }
17
+ export function loadToolSkill(tool, baseDir = process.cwd()) {
18
+ const skillPath = resolveSkillPath(tool.skills_loader, baseDir);
19
+ if (!skillPath) {
20
+ return {
21
+ skill_name: tool.skills_loader,
22
+ loaded: false,
23
+ error: `Skill not found: ${tool.skills_loader}`
24
+ };
25
+ }
26
+ return {
27
+ skill_name: tool.skills_loader,
28
+ skill_path: skillPath,
29
+ loaded: true
30
+ };
31
+ }
32
+ export function loadToolSkills(tools, baseDir = process.cwd()) {
33
+ return tools.map(tool => loadToolSkill(tool, baseDir));
34
+ }
35
+ export function getSkillForTool(tools, toolName) {
36
+ const tool = tools.find(t => t.tools_name === toolName);
37
+ if (!tool)
38
+ return null;
39
+ return loadToolSkill(tool);
40
+ }
41
+ export function groupToolsBySkill(tools) {
42
+ const groups = {};
43
+ for (const tool of tools) {
44
+ const skill = tool.skills_loader;
45
+ if (!groups[skill]) {
46
+ groups[skill] = [];
47
+ }
48
+ groups[skill].push(tool);
49
+ }
50
+ return groups;
51
+ }
52
+ export function getToolsForSkill(tools, skillName) {
53
+ return tools.filter(t => t.skills_loader === skillName);
54
+ }
55
+ export function buildSkillManifest(tools, baseDir = process.cwd()) {
56
+ const grouped = groupToolsBySkill(tools);
57
+ return Object.entries(grouped).map(([skillName, skillTools]) => {
58
+ const skillPath = resolveSkillPath(skillName, baseDir);
59
+ return {
60
+ skill_name: skillName,
61
+ skill_path: skillPath,
62
+ tools: skillTools,
63
+ available: skillPath !== null
64
+ };
65
+ });
66
+ }
@@ -0,0 +1 @@
1
+ export * from "./skill-generator.js";