@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.
- package/LICENSE +20 -0
- package/README.md +394 -93
- package/bin/naraya-native.mjs +4 -0
- package/bin/naraya.mjs +1 -142
- package/bin/undici-timeout.mjs +1 -0
- package/dist/assets.pack.gz +0 -0
- package/dist/mcp/config-loader.js +32 -0
- package/dist/mcp/lifecycle.js +90 -0
- package/dist/mcp/tool-mapper.js +31 -0
- package/dist/mcp/transport.js +30 -0
- package/dist/pentest/catalog/catalog-loader.js +45 -0
- package/dist/pentest/catalog/index.js +1 -0
- package/dist/pentest/cli.js +117 -0
- package/dist/pentest/command-builder/command-builder.js +90 -0
- package/dist/pentest/command-builder/index.js +1 -0
- package/dist/pentest/index.js +10 -0
- package/dist/pentest/installer/index.js +1 -0
- package/dist/pentest/installer/tool-installer.js +90 -0
- package/dist/pentest/manager.js +125 -0
- package/dist/pentest/mode/index.js +1 -0
- package/dist/pentest/mode/mode-selector.js +127 -0
- package/dist/pentest/selector/index.js +1 -0
- package/dist/pentest/selector/tool-selector.js +66 -0
- package/dist/pentest/skill-bridge/index.js +1 -0
- package/dist/pentest/skill-bridge/skill-bridge.js +66 -0
- package/dist/pentest/skills/generator/index.js +1 -0
- package/dist/pentest/skills/generator/skill-generator.js +310 -0
- package/dist/pentest/skills/index.js +3 -0
- package/dist/pentest/skills/loader/index.js +1 -0
- package/dist/pentest/skills/loader/skill-loader.js +167 -0
- package/dist/pentest/skills/register/index.js +1 -0
- package/dist/pentest/skills/register/skill-register.js +162 -0
- package/dist/pentest/skills/types.js +1 -0
- package/dist/pentest/types.js +90 -0
- package/package.json +42 -14
- package/src/assets-pack.mjs +1 -0
- package/src/banner.mjs +5 -0
- package/src/clipboard.mjs +1 -0
- package/src/config.mjs +1 -40
- package/src/goodbye.mjs +7 -0
- package/src/login.mjs +7 -49
- package/src/mcp/config-loader.ts +50 -0
- package/src/mcp/lifecycle.ts +113 -0
- package/src/mcp/tool-mapper.ts +42 -0
- package/src/mcp/transport.ts +38 -0
- package/src/mcp-cli.mjs +5 -0
- package/src/pentest/catalog/catalog-loader.ts +55 -0
- package/src/pentest/catalog/index.ts +1 -0
- package/src/pentest/cli.ts +130 -0
- package/src/pentest/command-builder/command-builder.ts +109 -0
- package/src/pentest/command-builder/index.ts +1 -0
- package/src/pentest/index.ts +11 -0
- package/src/pentest/installer/index.ts +1 -0
- package/src/pentest/installer/tool-installer.ts +107 -0
- package/src/pentest/manager.ts +167 -0
- package/src/pentest/mode/index.ts +1 -0
- package/src/pentest/mode/mode-selector.ts +159 -0
- package/src/pentest/selector/index.ts +1 -0
- package/src/pentest/selector/tool-selector.ts +87 -0
- package/src/pentest/skill-bridge/index.ts +1 -0
- package/src/pentest/skill-bridge/skill-bridge.ts +86 -0
- package/src/pentest/skills/generator/index.ts +1 -0
- package/src/pentest/skills/generator/skill-generator.ts +373 -0
- package/src/pentest/skills/index.ts +4 -0
- package/src/pentest/skills/loader/index.ts +1 -0
- package/src/pentest/skills/loader/skill-loader.ts +206 -0
- package/src/pentest/skills/register/index.ts +1 -0
- package/src/pentest/skills/register/skill-register.ts +196 -0
- package/src/pentest/skills/types.ts +66 -0
- package/src/pentest/types.ts +341 -0
- package/src/seed.mjs +1 -36
- package/src/splash.mjs +4 -0
- package/src/status.mjs +2 -71
- package/assets/APPEND-SYSTEM.md +0 -9
- package/assets/extensions/naraya-brand.ts +0 -251
- package/assets/extensions/naraya-gate.ts +0 -23
- package/assets/naraya-logo.txt +0 -5
- package/assets/skills/narabuild/SKILL.md +0 -156
- package/assets/skills/naradroid/SKILL.md +0 -118
- package/assets/skills/naraexplore/SKILL.md +0 -71
- package/assets/skills/narafe/SKILL.md +0 -94
- package/assets/skills/naraplan/SKILL.md +0 -47
- package/assets/skills/narasearch/SKILL.md +0 -141
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import type { PentestSkill, PentestSkillManifest, SkillLoadResult } from "../types.js"
|
|
2
|
+
import type { PentestPhase, ToolCategory } from "../../types.js"
|
|
3
|
+
import { existsSync, readFileSync, readdirSync, statSync } from "fs"
|
|
4
|
+
import { join } from "path"
|
|
5
|
+
|
|
6
|
+
const SKILL_DIRS = [
|
|
7
|
+
".agents/skills",
|
|
8
|
+
".opencode/skills",
|
|
9
|
+
".claude/skills",
|
|
10
|
+
"packages/pentest-skills/skills",
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
const SKILL_FILENAME = "SKILL.md"
|
|
14
|
+
|
|
15
|
+
export function resolveSkillPath(skillName: string, baseDir: string = process.cwd()): string | null {
|
|
16
|
+
for (const dir of SKILL_DIRS) {
|
|
17
|
+
const skillPath = join(baseDir, dir, skillName, SKILL_FILENAME)
|
|
18
|
+
if (existsSync(skillPath)) {
|
|
19
|
+
return skillPath
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return null
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function discoverSkills(baseDir: string = process.cwd()): PentestSkillManifest[] {
|
|
26
|
+
const manifests: PentestSkillManifest[] = []
|
|
27
|
+
|
|
28
|
+
for (const dir of SKILL_DIRS) {
|
|
29
|
+
const skillsDir = join(baseDir, dir)
|
|
30
|
+
if (!existsSync(skillsDir)) continue
|
|
31
|
+
|
|
32
|
+
const entries = readdirSync(skillsDir)
|
|
33
|
+
for (const entry of entries) {
|
|
34
|
+
const skillDir = join(skillsDir, entry)
|
|
35
|
+
if (!statSync(skillDir).isDirectory()) continue
|
|
36
|
+
|
|
37
|
+
const skillFile = join(skillDir, SKILL_FILENAME)
|
|
38
|
+
if (!existsSync(skillFile)) continue
|
|
39
|
+
|
|
40
|
+
const manifest = parseSkillManifest(skillFile, entry, skillDir)
|
|
41
|
+
if (manifest) {
|
|
42
|
+
manifests.push(manifest)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return manifests
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function loadSkill(skillName: string, baseDir: string = process.cwd()): SkillLoadResult {
|
|
51
|
+
const skillPath = resolveSkillPath(skillName, baseDir)
|
|
52
|
+
|
|
53
|
+
if (!skillPath) {
|
|
54
|
+
return {
|
|
55
|
+
name: skillName,
|
|
56
|
+
loaded: false,
|
|
57
|
+
error: `Skill not found: ${skillName}`,
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
const content = readFileSync(skillPath, "utf-8")
|
|
63
|
+
const skill = parseSkillContent(content, skillName, skillPath)
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
name: skillName,
|
|
67
|
+
loaded: true,
|
|
68
|
+
skill,
|
|
69
|
+
}
|
|
70
|
+
} catch (error) {
|
|
71
|
+
return {
|
|
72
|
+
name: skillName,
|
|
73
|
+
loaded: false,
|
|
74
|
+
error: error instanceof Error ? error.message : "Failed to load skill",
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function loadAllSkills(baseDir: string = process.cwd()): SkillLoadResult[] {
|
|
80
|
+
const manifests = discoverSkills(baseDir)
|
|
81
|
+
return manifests.map(m => loadSkill(m.name, baseDir))
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function loadSkillsByPhase(
|
|
85
|
+
phase: string,
|
|
86
|
+
baseDir: string = process.cwd(),
|
|
87
|
+
): SkillLoadResult[] {
|
|
88
|
+
const manifests = discoverSkills(baseDir)
|
|
89
|
+
const filtered = manifests.filter(m => m.phase.includes(phase as never))
|
|
90
|
+
return filtered.map(m => loadSkill(m.name, baseDir))
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function loadSkillsByTools(
|
|
94
|
+
toolNames: readonly string[],
|
|
95
|
+
baseDir: string = process.cwd(),
|
|
96
|
+
): SkillLoadResult[] {
|
|
97
|
+
const manifests = discoverSkills(baseDir)
|
|
98
|
+
const toolSet = new Set(toolNames)
|
|
99
|
+
const filtered = manifests.filter(m => m.tools.some((t: string) => toolSet.has(t)))
|
|
100
|
+
return filtered.map(m => loadSkill(m.name, baseDir))
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function getSkillsForTool(
|
|
104
|
+
toolName: string,
|
|
105
|
+
baseDir: string = process.cwd(),
|
|
106
|
+
): SkillLoadResult[] {
|
|
107
|
+
return loadSkillsByTools([toolName], baseDir)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function parseSkillManifest(
|
|
111
|
+
skillFile: string,
|
|
112
|
+
skillName: string,
|
|
113
|
+
_skillDir: string,
|
|
114
|
+
): PentestSkillManifest | null {
|
|
115
|
+
try {
|
|
116
|
+
const content = readFileSync(skillFile, "utf-8")
|
|
117
|
+
const frontmatter = extractFrontmatter(content)
|
|
118
|
+
|
|
119
|
+
if (!frontmatter) {
|
|
120
|
+
return {
|
|
121
|
+
name: skillName,
|
|
122
|
+
description: "",
|
|
123
|
+
version: "0.0.0",
|
|
124
|
+
phase: [] as PentestPhase[],
|
|
125
|
+
category: [] as ToolCategory[],
|
|
126
|
+
tools: [],
|
|
127
|
+
tags: [],
|
|
128
|
+
skill_path: skillFile,
|
|
129
|
+
loaded: false,
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
name: (frontmatter.name as string) ?? skillName,
|
|
135
|
+
description: (frontmatter.description as string) ?? "",
|
|
136
|
+
version: (frontmatter.version as string) ?? "0.0.0",
|
|
137
|
+
phase: parseStringArray(frontmatter.phase) as PentestPhase[],
|
|
138
|
+
category: parseStringArray(frontmatter.category) as ToolCategory[],
|
|
139
|
+
tools: parseStringArray(frontmatter.tools),
|
|
140
|
+
tags: parseStringArray(frontmatter.tags),
|
|
141
|
+
skill_path: skillFile,
|
|
142
|
+
loaded: true,
|
|
143
|
+
}
|
|
144
|
+
} catch {
|
|
145
|
+
return null
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function parseSkillContent(
|
|
150
|
+
content: string,
|
|
151
|
+
skillName: string,
|
|
152
|
+
_skillPath: string,
|
|
153
|
+
): PentestSkill {
|
|
154
|
+
const frontmatter = extractFrontmatter(content) ?? {}
|
|
155
|
+
|
|
156
|
+
return {
|
|
157
|
+
name: (frontmatter.name as string) ?? skillName,
|
|
158
|
+
description: (frontmatter.description as string) ?? "",
|
|
159
|
+
version: (frontmatter.version as string) ?? "0.0.0",
|
|
160
|
+
phase: parseStringArray(frontmatter.phase) as PentestPhase[],
|
|
161
|
+
category: parseStringArray(frontmatter.category) as ToolCategory[],
|
|
162
|
+
tools: parseStringArray(frontmatter.tools),
|
|
163
|
+
author: frontmatter.author as string | undefined,
|
|
164
|
+
homepage: frontmatter.homepage as string | undefined,
|
|
165
|
+
tags: parseStringArray(frontmatter.tags),
|
|
166
|
+
template: content,
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function extractFrontmatter(content: string): Record<string, unknown> | null {
|
|
171
|
+
const match = /^---\s*\n([\s\S]*?)\n---/.exec(content)
|
|
172
|
+
if (!match) return null
|
|
173
|
+
|
|
174
|
+
const yaml = match[1]
|
|
175
|
+
const result: Record<string, unknown> = {}
|
|
176
|
+
|
|
177
|
+
for (const line of yaml.split("\n")) {
|
|
178
|
+
const colonIndex = line.indexOf(":")
|
|
179
|
+
if (colonIndex === -1) continue
|
|
180
|
+
|
|
181
|
+
const key = line.slice(0, colonIndex).trim()
|
|
182
|
+
const value = line.slice(colonIndex + 1).trim()
|
|
183
|
+
|
|
184
|
+
if (value.startsWith("[") && value.endsWith("]")) {
|
|
185
|
+
result[key] = value
|
|
186
|
+
.slice(1, -1)
|
|
187
|
+
.split(",")
|
|
188
|
+
.map(s => s.trim().replace(/^["']|["']$/g, ""))
|
|
189
|
+
} else {
|
|
190
|
+
result[key] = value.replace(/^["']|["']$/g, "")
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return result
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function parseStringArray(value: unknown): string[] {
|
|
198
|
+
if (Array.isArray(value)) return value.map(String)
|
|
199
|
+
if (typeof value === "string") {
|
|
200
|
+
if (value.startsWith("[") && value.endsWith("]")) {
|
|
201
|
+
return value.slice(1, -1).split(",").map(s => s.trim().replace(/^["']|["']$/g, ""))
|
|
202
|
+
}
|
|
203
|
+
return [value]
|
|
204
|
+
}
|
|
205
|
+
return []
|
|
206
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./skill-register.js"
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import type { PentestSkill, SkillRegisterEntry, SkillRegisterConfig } from "../types.js"
|
|
2
|
+
import { existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync } from "fs"
|
|
3
|
+
import { join, dirname } from "path"
|
|
4
|
+
import { loadSkill } from "../loader/skill-loader.js"
|
|
5
|
+
|
|
6
|
+
const DEFAULT_CONFIG: SkillRegisterConfig = {
|
|
7
|
+
skills_dir: ".agents/skills",
|
|
8
|
+
auto_discover: true,
|
|
9
|
+
search_paths: [".agents/skills", ".opencode/skills", ".claude/skills"],
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export class SkillRegister {
|
|
13
|
+
private entries: Map<string, SkillRegisterEntry> = new Map()
|
|
14
|
+
private config: SkillRegisterConfig
|
|
15
|
+
|
|
16
|
+
constructor(config: Partial<SkillRegisterConfig> = {}) {
|
|
17
|
+
this.config = { ...DEFAULT_CONFIG, ...config }
|
|
18
|
+
|
|
19
|
+
if (this.config.auto_discover) {
|
|
20
|
+
this.discoverAndRegister()
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
register(skill: PentestSkill): SkillRegisterEntry {
|
|
25
|
+
const entry: SkillRegisterEntry = {
|
|
26
|
+
name: skill.name,
|
|
27
|
+
skill,
|
|
28
|
+
registered_at: new Date().toISOString(),
|
|
29
|
+
enabled: true,
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
this.entries.set(skill.name, entry)
|
|
33
|
+
return entry
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
registerFromFile(skillName: string, baseDir?: string): SkillRegisterEntry | null {
|
|
37
|
+
const result = loadSkill(skillName, baseDir)
|
|
38
|
+
|
|
39
|
+
if (!result.loaded || !result.skill) {
|
|
40
|
+
return null
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return this.register(result.skill)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
registerMany(skills: readonly PentestSkill[]): SkillRegisterEntry[] {
|
|
47
|
+
return skills.map(skill => this.register(skill))
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
registerFromFiles(skillNames: readonly string[], baseDir?: string): SkillRegisterEntry[] {
|
|
51
|
+
const entries: SkillRegisterEntry[] = []
|
|
52
|
+
|
|
53
|
+
for (const name of skillNames) {
|
|
54
|
+
const entry = this.registerFromFile(name, baseDir)
|
|
55
|
+
if (entry) entries.push(entry)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return entries
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
unregister(skillName: string): boolean {
|
|
62
|
+
return this.entries.delete(skillName)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
enable(skillName: string): boolean {
|
|
66
|
+
const entry = this.entries.get(skillName)
|
|
67
|
+
if (!entry) return false
|
|
68
|
+
;(entry as { enabled: boolean }).enabled = true
|
|
69
|
+
return true
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
disable(skillName: string): boolean {
|
|
73
|
+
const entry = this.entries.get(skillName)
|
|
74
|
+
if (!entry) return false
|
|
75
|
+
;(entry as { enabled: boolean }).enabled = false
|
|
76
|
+
return true
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
get(skillName: string): SkillRegisterEntry | undefined {
|
|
80
|
+
return this.entries.get(skillName)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
getEnabled(): SkillRegisterEntry[] {
|
|
84
|
+
return [...this.entries.values()].filter(e => e.enabled)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
getAll(): SkillRegisterEntry[] {
|
|
88
|
+
return [...this.entries.values()]
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
getByPhase(phase: string): SkillRegisterEntry[] {
|
|
92
|
+
return this.getEnabled().filter(e => e.skill.phase.includes(phase as never))
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
getByCategory(category: string): SkillRegisterEntry[] {
|
|
96
|
+
return this.getEnabled().filter(e => e.skill.category.includes(category as never))
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
getByTool(toolName: string): SkillRegisterEntry[] {
|
|
100
|
+
return this.getEnabled().filter(e => e.skill.tools.includes(toolName))
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
getByTag(tag: string): SkillRegisterEntry[] {
|
|
104
|
+
return this.getEnabled().filter(e => e.skill.tags.includes(tag))
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
has(skillName: string): boolean {
|
|
108
|
+
return this.entries.has(skillName)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
count(): number {
|
|
112
|
+
return this.entries.size
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
enabledCount(): number {
|
|
116
|
+
return this.getEnabled().length
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
clear(): void {
|
|
120
|
+
this.entries.clear()
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
saveToFile(outputPath?: string): string {
|
|
124
|
+
const path = outputPath ?? join(this.config.skills_dir, ".skill-register.json")
|
|
125
|
+
const dir = dirname(path)
|
|
126
|
+
|
|
127
|
+
if (!existsSync(dir)) {
|
|
128
|
+
mkdirSync(dir, { recursive: true })
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const data = {
|
|
132
|
+
version: "1.0.0",
|
|
133
|
+
saved_at: new Date().toISOString(),
|
|
134
|
+
entries: this.getAll().map(e => ({
|
|
135
|
+
name: e.name,
|
|
136
|
+
description: e.skill.description,
|
|
137
|
+
version: e.skill.version,
|
|
138
|
+
phase: e.skill.phase,
|
|
139
|
+
category: e.skill.category,
|
|
140
|
+
tools: e.skill.tools,
|
|
141
|
+
tags: e.skill.tags,
|
|
142
|
+
enabled: e.enabled,
|
|
143
|
+
registered_at: e.registered_at,
|
|
144
|
+
})),
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
writeFileSync(path, JSON.stringify(data, null, 2))
|
|
148
|
+
return path
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
loadFromFile(inputPath?: string): number {
|
|
152
|
+
const path = inputPath ?? join(this.config.skills_dir, ".skill-register.json")
|
|
153
|
+
|
|
154
|
+
if (!existsSync(path)) return 0
|
|
155
|
+
|
|
156
|
+
try {
|
|
157
|
+
const content = readFileSync(path, "utf-8")
|
|
158
|
+
const data = JSON.parse(content) as { entries: Array<{ name: string; enabled: boolean }> }
|
|
159
|
+
|
|
160
|
+
let loaded = 0
|
|
161
|
+
for (const entry of data.entries ?? []) {
|
|
162
|
+
const result = loadSkill(entry.name)
|
|
163
|
+
if (result.loaded && result.skill) {
|
|
164
|
+
const registered = this.register(result.skill)
|
|
165
|
+
;(registered as { enabled: boolean }).enabled = entry.enabled
|
|
166
|
+
loaded++
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return loaded
|
|
171
|
+
} catch {
|
|
172
|
+
return 0
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
private discoverAndRegister(): void {
|
|
177
|
+
for (const searchPath of this.config.search_paths) {
|
|
178
|
+
if (!existsSync(searchPath)) continue
|
|
179
|
+
|
|
180
|
+
const entries = readdirSync(searchPath)
|
|
181
|
+
for (const entry of entries) {
|
|
182
|
+
const skillPath = join(searchPath, entry, "SKILL.md")
|
|
183
|
+
if (existsSync(skillPath)) {
|
|
184
|
+
const result = loadSkill(entry, searchPath)
|
|
185
|
+
if (result.loaded && result.skill) {
|
|
186
|
+
this.register(result.skill)
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export function createSkillRegister(config?: Partial<SkillRegisterConfig>): SkillRegister {
|
|
195
|
+
return new SkillRegister(config)
|
|
196
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { PentestPhase, ToolCategory } from "../types.js"
|
|
2
|
+
|
|
3
|
+
export interface PentestSkill {
|
|
4
|
+
readonly name: string
|
|
5
|
+
readonly description: string
|
|
6
|
+
readonly version: string
|
|
7
|
+
readonly phase: readonly PentestPhase[]
|
|
8
|
+
readonly category: readonly ToolCategory[]
|
|
9
|
+
readonly tools: readonly string[]
|
|
10
|
+
readonly author?: string
|
|
11
|
+
readonly homepage?: string
|
|
12
|
+
readonly tags: readonly string[]
|
|
13
|
+
readonly template: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface PentestSkillManifest {
|
|
17
|
+
readonly name: string
|
|
18
|
+
readonly description: string
|
|
19
|
+
readonly version: string
|
|
20
|
+
readonly phase: readonly PentestPhase[]
|
|
21
|
+
readonly category: readonly ToolCategory[]
|
|
22
|
+
readonly tools: readonly string[]
|
|
23
|
+
readonly tags: readonly string[]
|
|
24
|
+
readonly skill_path: string
|
|
25
|
+
readonly loaded: boolean
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface SkillRegisterEntry {
|
|
29
|
+
readonly name: string
|
|
30
|
+
readonly skill: PentestSkill
|
|
31
|
+
readonly registered_at: string
|
|
32
|
+
readonly enabled: boolean
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface SkillRegisterConfig {
|
|
36
|
+
readonly skills_dir: string
|
|
37
|
+
readonly auto_discover: boolean
|
|
38
|
+
readonly search_paths: readonly string[]
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface GeneratedSkill {
|
|
42
|
+
readonly name: string
|
|
43
|
+
readonly content: string
|
|
44
|
+
readonly path: string
|
|
45
|
+
readonly tools_referenced: readonly string[]
|
|
46
|
+
readonly phase: readonly PentestPhase[]
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface SkillGenerationOptions {
|
|
50
|
+
readonly name: string
|
|
51
|
+
readonly description: string
|
|
52
|
+
readonly phase: readonly PentestPhase[]
|
|
53
|
+
readonly category: readonly ToolCategory[]
|
|
54
|
+
readonly tools: readonly string[]
|
|
55
|
+
readonly author?: string
|
|
56
|
+
readonly tags?: readonly string[]
|
|
57
|
+
readonly output_dir?: string
|
|
58
|
+
readonly template?: "basic" | "recon" | "exploitation" | "reporting" | "custom"
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface SkillLoadResult {
|
|
62
|
+
readonly name: string
|
|
63
|
+
readonly loaded: boolean
|
|
64
|
+
readonly skill?: PentestSkill
|
|
65
|
+
readonly error?: string
|
|
66
|
+
}
|