kramscan 0.1.0 → 0.2.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 (105) hide show
  1. package/README.md +392 -87
  2. package/dist/agent/confirmation.d.ts +38 -0
  3. package/dist/agent/confirmation.js +210 -0
  4. package/dist/agent/context.d.ts +81 -0
  5. package/dist/agent/context.js +227 -0
  6. package/dist/agent/index.d.ts +10 -0
  7. package/dist/agent/index.js +32 -0
  8. package/dist/agent/orchestrator.d.ts +63 -0
  9. package/dist/agent/orchestrator.js +370 -0
  10. package/dist/agent/prompts/system.d.ts +6 -0
  11. package/dist/agent/prompts/system.js +116 -0
  12. package/dist/agent/skill-registry.d.ts +78 -0
  13. package/dist/agent/skill-registry.js +202 -0
  14. package/dist/agent/skills/analyze-findings.d.ts +22 -0
  15. package/dist/agent/skills/analyze-findings.js +191 -0
  16. package/dist/agent/skills/generate-report.d.ts +26 -0
  17. package/dist/agent/skills/generate-report.js +436 -0
  18. package/dist/agent/skills/health-check.d.ts +28 -0
  19. package/dist/agent/skills/health-check.js +344 -0
  20. package/dist/agent/skills/index.d.ts +9 -0
  21. package/dist/agent/skills/index.js +17 -0
  22. package/dist/agent/skills/verify-finding.d.ts +17 -0
  23. package/dist/agent/skills/verify-finding.js +91 -0
  24. package/dist/agent/skills/web-scan.d.ts +22 -0
  25. package/dist/agent/skills/web-scan.js +203 -0
  26. package/dist/agent/types.d.ts +141 -0
  27. package/dist/agent/types.js +16 -0
  28. package/dist/cli.d.ts +3 -0
  29. package/dist/cli.js +176 -139
  30. package/dist/commands/agent.d.ts +6 -0
  31. package/dist/commands/agent.js +250 -0
  32. package/dist/commands/ai.d.ts +2 -0
  33. package/dist/commands/ai.js +112 -0
  34. package/dist/commands/analyze.js +104 -55
  35. package/dist/commands/config.js +63 -37
  36. package/dist/commands/doctor.js +22 -17
  37. package/dist/commands/onboard.js +190 -125
  38. package/dist/commands/report.js +69 -77
  39. package/dist/commands/scan.js +261 -81
  40. package/dist/commands/scans.d.ts +2 -0
  41. package/dist/commands/scans.js +51 -0
  42. package/dist/core/ai-client.d.ts +7 -2
  43. package/dist/core/ai-client.js +231 -20
  44. package/dist/core/ai-payloads.d.ts +17 -0
  45. package/dist/core/ai-payloads.js +54 -0
  46. package/dist/core/config-schema.d.ts +197 -0
  47. package/dist/core/config-schema.js +68 -0
  48. package/dist/core/config-schema.test.d.ts +1 -0
  49. package/dist/core/config-schema.test.js +151 -0
  50. package/dist/core/config.d.ts +17 -36
  51. package/dist/core/config.js +261 -20
  52. package/dist/core/errors.d.ts +71 -0
  53. package/dist/core/errors.js +162 -0
  54. package/dist/core/scan-index.d.ts +19 -0
  55. package/dist/core/scan-index.js +52 -0
  56. package/dist/core/scan-storage.d.ts +11 -0
  57. package/dist/core/scan-storage.js +69 -0
  58. package/dist/core/scanner.d.ts +101 -4
  59. package/dist/core/scanner.js +432 -63
  60. package/dist/core/vulnerability-detector.d.ts +18 -2
  61. package/dist/core/vulnerability-detector.js +349 -38
  62. package/dist/core/vulnerability-detector.test.d.ts +1 -0
  63. package/dist/core/vulnerability-detector.test.js +210 -0
  64. package/dist/index.js +3 -0
  65. package/dist/plugins/PluginManager.d.ts +27 -0
  66. package/dist/plugins/PluginManager.js +166 -0
  67. package/dist/plugins/index.d.ts +7 -0
  68. package/dist/plugins/index.js +19 -0
  69. package/dist/plugins/types.d.ts +55 -0
  70. package/dist/plugins/types.js +25 -0
  71. package/dist/plugins/vulnerabilities/CSRFPlugin.d.ts +8 -0
  72. package/dist/plugins/vulnerabilities/CSRFPlugin.js +34 -0
  73. package/dist/plugins/vulnerabilities/SQLInjectionPlugin.d.ts +11 -0
  74. package/dist/plugins/vulnerabilities/SQLInjectionPlugin.js +109 -0
  75. package/dist/plugins/vulnerabilities/SecurityHeadersPlugin.d.ts +11 -0
  76. package/dist/plugins/vulnerabilities/SecurityHeadersPlugin.js +63 -0
  77. package/dist/plugins/vulnerabilities/SensitiveDataPlugin.d.ts +9 -0
  78. package/dist/plugins/vulnerabilities/SensitiveDataPlugin.js +32 -0
  79. package/dist/plugins/vulnerabilities/XSSPlugin.d.ts +15 -0
  80. package/dist/plugins/vulnerabilities/XSSPlugin.js +81 -0
  81. package/dist/reports/PdfGenerator.d.ts +36 -0
  82. package/dist/reports/PdfGenerator.js +379 -0
  83. package/dist/utils/logger.d.ts +33 -1
  84. package/dist/utils/logger.js +127 -8
  85. package/dist/utils/theme.d.ts +55 -0
  86. package/dist/utils/theme.js +195 -0
  87. package/package.json +27 -6
  88. package/dist/core/executor.d.ts +0 -2
  89. package/dist/core/executor.js +0 -74
  90. package/dist/core/logger.d.ts +0 -12
  91. package/dist/core/logger.js +0 -51
  92. package/dist/core/registry.d.ts +0 -3
  93. package/dist/core/registry.js +0 -35
  94. package/dist/core/storage.d.ts +0 -4
  95. package/dist/core/storage.js +0 -39
  96. package/dist/core/types.d.ts +0 -24
  97. package/dist/core/types.js +0 -2
  98. package/dist/skills/base.d.ts +0 -8
  99. package/dist/skills/base.js +0 -6
  100. package/dist/skills/builtin.d.ts +0 -4
  101. package/dist/skills/builtin.js +0 -71
  102. package/dist/skills/loader.d.ts +0 -2
  103. package/dist/skills/loader.js +0 -27
  104. package/dist/skills/types.d.ts +0 -46
  105. package/dist/skills/types.js +0 -2
package/dist/cli.js CHANGED
@@ -32,69 +32,42 @@ var __importStar = (this && this.__importStar) || (function () {
32
32
  return result;
33
33
  };
34
34
  })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
35
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.isVerbose = isVerbose;
40
+ exports.isDebug = isDebug;
41
+ exports.debugLog = debugLog;
36
42
  exports.run = run;
37
43
  const commander_1 = require("commander");
44
+ const inquirer_1 = __importDefault(require("inquirer"));
38
45
  const onboard_1 = require("./commands/onboard");
39
46
  const scan_1 = require("./commands/scan");
40
47
  const analyze_1 = require("./commands/analyze");
41
48
  const report_1 = require("./commands/report");
42
49
  const config_1 = require("./commands/config");
43
50
  const doctor_1 = require("./commands/doctor");
44
- const CLI_VERSION = "0.1.0";
45
- // ─── ANSI Color Helpers ────────────────────────────────────────────
46
- const c = {
47
- reset: "\x1b[0m",
48
- bold: "\x1b[1m",
49
- dim: "\x1b[2m",
50
- red: "\x1b[31m",
51
- green: "\x1b[32m",
52
- yellow: "\x1b[33m",
53
- blue: "\x1b[34m",
54
- magenta: "\x1b[35m",
55
- cyan: "\x1b[36m",
56
- white: "\x1b[37m",
57
- gray: "\x1b[90m",
58
- bgBlue: "\x1b[44m",
59
- bgMagenta: "\x1b[45m",
60
- brightCyan: "\x1b[96m",
61
- brightMagenta: "\x1b[95m",
62
- brightBlue: "\x1b[94m",
63
- brightGreen: "\x1b[92m",
64
- brightYellow: "\x1b[93m",
65
- brightWhite: "\x1b[97m",
66
- };
67
- // ─── ASCII Art Banner ──────────────────────────────────────────────
68
- function printBanner() {
69
- // Sleek ANSI Shadow style — KRAMSCAN
70
- const lines = [
71
- `██╗ ██╗██████╗ █████╗ ███╗ ███╗███████╗ ██████╗ █████╗ ███╗ ██╗`,
72
- `██║ ██╔╝██╔══██╗██╔══██╗████╗ ████║██╔════╝██╔════╝██╔══██╗████╗ ██║`,
73
- `█████╔╝ ██████╔╝███████║██╔████╔██║███████╗██║ ███████║██╔██╗ ██║`,
74
- `██╔═██╗ ██╔══██╗██╔══██║██║╚██╔╝██║╚════██║██║ ██╔══██║██║╚██╗██║`,
75
- `██║ ██╗██║ ██║██║ ██║██║ ╚═╝ ██║███████║╚██████╗██║ ██║██║ ╚████║`,
76
- `╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ ╚═════╝╚═╝ ╚═╝╚═╝ ╚═══╝`,
77
- ];
78
- console.log("");
79
- lines.forEach((line, i) => {
80
- const shade = i % 2 === 0 ? c.brightWhite : c.gray;
81
- console.log(` ${shade}${line}${c.reset}`);
82
- });
83
- console.log("");
51
+ const agent_1 = require("./commands/agent");
52
+ const scans_1 = require("./commands/scans");
53
+ const ai_1 = require("./commands/ai");
54
+ const config_2 = require("./core/config");
55
+ const theme_1 = require("./utils/theme");
56
+ let verboseMode = false;
57
+ let debugMode = false;
58
+ function isVerbose() {
59
+ return verboseMode;
84
60
  }
85
- // ─── Dashboard Info ────────────────────────────────────────────────
86
- function printInfo() {
87
- console.log(` ${c.gray}${c.dim}───────────────────────────────────────────────────────${c.reset}`);
88
- console.log(` ${c.brightWhite}${c.bold} KramScan${c.reset} ${c.gray}v${CLI_VERSION}${c.reset} ${c.dim}${c.gray}|${c.reset} ${c.cyan}AI-Powered Web Security Scanner${c.reset}`);
89
- console.log(` ${c.gray}${c.dim}───────────────────────────────────────────────────────${c.reset}`);
90
- console.log("");
91
- console.log(` ${c.brightYellow}${c.bold}Tips for getting started:${c.reset}`);
92
- console.log(` ${c.white}1.${c.reset} ${c.gray}Run${c.reset} ${c.cyan}kramscan onboard${c.reset} ${c.gray}to configure your API keys.${c.reset}`);
93
- console.log(` ${c.white}2.${c.reset} ${c.gray}Run${c.reset} ${c.cyan}kramscan scan <url>${c.reset} ${c.gray}to scan a target.${c.reset}`);
94
- console.log(` ${c.white}3.${c.reset} ${c.gray}Run${c.reset} ${c.cyan}kramscan --help${c.reset} ${c.gray}for all commands.${c.reset}`);
95
- console.log("");
61
+ function isDebug() {
62
+ return debugMode || (0, config_2.isDebugEnabled)();
63
+ }
64
+ function debugLog(...args) {
65
+ if (debugMode || (0, config_2.isDebugEnabled)()) {
66
+ console.log("[DEBUG]", ...args);
67
+ }
96
68
  }
97
69
  const menuChoices = [
70
+ { label: "Agent", value: "agent", description: "AI-powered interactive security assistant", icon: "🤖", status: "active" },
98
71
  { label: "Onboard", value: "onboard", description: "First-time setup wizard", icon: "⚡", status: "active" },
99
72
  { label: "Scan", value: "scan", description: "Scan a target URL for vulnerabilities", icon: "🔍", status: "active" },
100
73
  { label: "Analyze", value: "analyze", description: "Deep AI analysis of scan results", icon: "🧠", status: "active" },
@@ -104,97 +77,124 @@ const menuChoices = [
104
77
  { label: "Exit", value: "exit", description: "Quit KramScan", icon: "👋", status: "active" },
105
78
  ];
106
79
  async function showInteractiveMenu() {
107
- printBanner();
108
- printInfo();
109
- // Use readline for a simple, CommonJS-compatible interactive menu
110
- const readline = await Promise.resolve().then(() => __importStar(require("readline")));
111
- const rl = readline.createInterface({
112
- input: process.stdin,
113
- output: process.stdout,
114
- });
115
- function renderMenu(selectedIndex) {
116
- // Move cursor up to redraw the menu (clear previous render)
117
- if (selectedIndex >= 0) {
118
- process.stdout.write(`\x1b[${menuChoices.length + 2}A`);
119
- }
120
- console.log(` ${c.brightWhite}${c.bold}What would you like to do?${c.reset}`);
121
- console.log("");
122
- menuChoices.forEach((choice, i) => {
123
- const isSelected = i === selectedIndex;
124
- const statusTag = choice.status === "coming_soon"
125
- ? ` ${c.yellow}[coming soon]${c.reset}`
126
- : "";
127
- if (isSelected) {
128
- console.log(` ${c.brightCyan}${c.bold}❯ ${choice.icon} ${choice.label}${c.reset}${statusTag} ${c.dim}${c.gray}— ${choice.description}${c.reset}`);
129
- }
130
- else {
131
- console.log(` ${choice.icon} ${c.white}${choice.label}${c.reset}${statusTag} ${c.dim}${c.gray}— ${choice.description}${c.reset}`);
132
- }
133
- });
80
+ (0, theme_1.printBanner)();
81
+ (0, theme_1.printInfo)();
82
+ const choices = menuChoices.map((choice) => ({
83
+ name: `${choice.icon} ${choice.label.padEnd(10)} - ${choice.description}${choice.status === "coming_soon" ? " [coming soon]" : ""}`,
84
+ value: choice.value,
85
+ disabled: choice.status === "coming_soon",
86
+ }));
87
+ const { action } = await inquirer_1.default.prompt([
88
+ {
89
+ type: "list",
90
+ name: "action",
91
+ message: theme_1.theme.cyan("What would you like to do?"),
92
+ choices,
93
+ pageSize: 10,
94
+ },
95
+ ]);
96
+ if (action === "exit") {
97
+ console.log(theme_1.theme.gray("\n Goodbye! 👋\n"));
98
+ return;
134
99
  }
135
- return new Promise((resolve) => {
136
- let selectedIndex = 0;
137
- // Enable raw mode for arrow key support
138
- if (process.stdin.isTTY) {
139
- process.stdin.setRawMode(true);
140
- }
141
- process.stdin.resume();
142
- renderMenu(-1); // Initial render (no cursor-up needed)
143
- process.stdin.on("data", async (key) => {
144
- const str = key.toString();
145
- if (str === "\x1b[A") {
146
- // Arrow Up
147
- selectedIndex = (selectedIndex - 1 + menuChoices.length) % menuChoices.length;
148
- renderMenu(selectedIndex);
149
- }
150
- else if (str === "\x1b[B") {
151
- // Arrow Down
152
- selectedIndex = (selectedIndex + 1) % menuChoices.length;
153
- renderMenu(selectedIndex);
154
- }
155
- else if (str === "\r" || str === "\n") {
156
- // Enter
157
- if (process.stdin.isTTY) {
158
- process.stdin.setRawMode(false);
159
- }
160
- process.stdin.pause();
161
- rl.close();
162
- const selected = menuChoices[selectedIndex];
163
- console.log("");
164
- if (selected.value === "exit") {
165
- console.log(` ${c.gray}${c.dim}Goodbye! 👋${c.reset}`);
166
- console.log("");
167
- resolve();
168
- return;
169
- }
170
- if (selected.status === "coming_soon") {
171
- console.log(` ${c.yellow}⚠ ${selected.label}${c.reset} ${c.gray}is coming soon. Stay tuned!${c.reset}`);
172
- console.log(` ${c.gray}Run ${c.cyan}kramscan --help${c.gray} for available commands.${c.reset}`);
173
- console.log("");
174
- resolve();
175
- return;
100
+ const selected = menuChoices.find((c) => c.value === action);
101
+ if (selected && selected.status === "coming_soon") {
102
+ console.log(theme_1.theme.yellow(`\n [!] ${selected.label} is coming soon. Stay tuned!`));
103
+ console.log(theme_1.theme.gray(` Run ${theme_1.theme.cyan("kramscan --help")} for available commands.\n`));
104
+ return;
105
+ }
106
+ let args = [action];
107
+ // Specific handling for commands that need input
108
+ if (action === "scan") {
109
+ const { url } = await inquirer_1.default.prompt([
110
+ {
111
+ type: "input",
112
+ name: "url",
113
+ message: theme_1.theme.cyan("Enter the URL to scan:"),
114
+ validate: (input) => {
115
+ try {
116
+ new URL(input);
117
+ return true;
118
+ }
119
+ catch {
120
+ return "Please enter a valid URL (e.g., https://example.com)";
121
+ }
176
122
  }
177
- // Execute the selected command
178
- console.log(` ${c.brightGreen}▶${c.reset} ${c.bold}Launching ${selected.label}...${c.reset}`);
179
- console.log("");
180
- // Re-run with the command argument
181
- process.argv.push(selected.value);
182
- const program = createProgram();
183
- await program.parseAsync(process.argv);
184
- resolve();
185
123
  }
186
- else if (str === "\x03") {
187
- // Ctrl+C
188
- if (process.stdin.isTTY) {
189
- process.stdin.setRawMode(false);
124
+ ]);
125
+ args.push(url);
126
+ }
127
+ else if (action === "analyze" || action === "report") {
128
+ const { listScans } = await Promise.resolve().then(() => __importStar(require("./core/scan-index")));
129
+ const scans = await listScans(10);
130
+ if (scans.length > 0) {
131
+ const { scanFile } = await inquirer_1.default.prompt([
132
+ {
133
+ type: "list",
134
+ name: "scanFile",
135
+ message: theme_1.theme.cyan(`Select a scan to ${action}:`),
136
+ choices: [
137
+ ...scans.map(s => ({
138
+ name: `${s.timestamp} - ${s.hostname} (${s.summary.total} findings)`,
139
+ value: s.jsonPath
140
+ })),
141
+ { name: "Back to menu", value: "back" }
142
+ ]
190
143
  }
191
- process.stdin.pause();
192
- rl.close();
193
- console.log(`\n ${c.gray}${c.dim}Interrupted. Goodbye! 👋${c.reset}\n`);
194
- process.exit(0);
144
+ ]);
145
+ if (scanFile === "back") {
146
+ return showInteractiveMenu();
195
147
  }
196
- });
197
- });
148
+ args.push(scanFile);
149
+ }
150
+ else {
151
+ console.log(theme_1.theme.yellow(`\n [!] No recent scans found. Please run a scan first.\n`));
152
+ await new Promise(r => setTimeout(r, 1500));
153
+ return showInteractiveMenu();
154
+ }
155
+ }
156
+ console.log(theme_1.theme.green(`\n > Launching ${selected?.label || action}...\n`));
157
+ const program = createProgram();
158
+ try {
159
+ await program.parseAsync(["node", "kramscan", ...args]);
160
+ // After execution, ask if they want to go back to the menu
161
+ const { back } = await inquirer_1.default.prompt([
162
+ {
163
+ type: "confirm",
164
+ name: "back",
165
+ message: theme_1.theme.cyan("Return to main menu?"),
166
+ default: true
167
+ }
168
+ ]);
169
+ if (back) {
170
+ return showInteractiveMenu();
171
+ }
172
+ }
173
+ catch (error) {
174
+ // Error handling is managed by the commands themselves or global handlers
175
+ }
176
+ }
177
+ async function showDirectCommandInput() {
178
+ (0, theme_1.printBanner)();
179
+ (0, theme_1.printInfo)();
180
+ const { command } = await inquirer_1.default.prompt([
181
+ {
182
+ type: "input",
183
+ name: "command",
184
+ message: theme_1.theme.cyan("Enter a command (e.g., 'scan https://example.com'):"),
185
+ filter: (input) => input.trim(),
186
+ },
187
+ ]);
188
+ if (!command) {
189
+ return;
190
+ }
191
+ const tokens = command.match(/(?:[^\s"]+|"[^"]*")+/g)?.map((token) => token.replace(/^"(.*)"$/, "$1")) ?? [];
192
+ const args = tokens[0]?.toLowerCase() === "kramscan" ? tokens.slice(1) : tokens;
193
+ if (args.length > 0) {
194
+ console.log("");
195
+ const program = createProgram();
196
+ await program.parseAsync(["node", "kramscan", ...args]);
197
+ }
198
198
  }
199
199
  // ─── Program Setup ─────────────────────────────────────────────────
200
200
  function createProgram() {
@@ -202,19 +202,56 @@ function createProgram() {
202
202
  program
203
203
  .name("kramscan")
204
204
  .description("KramScan — AI-powered web app security testing")
205
- .version(CLI_VERSION);
205
+ .version(theme_1.CLI_VERSION)
206
+ .option("-v, --verbose", "Enable verbose output")
207
+ .option("--debug", "Enable debug mode")
208
+ .hook("preAction", (thisCommand) => {
209
+ const opts = thisCommand.opts();
210
+ verboseMode = opts.verbose || false;
211
+ debugMode = opts.debug || false;
212
+ })
213
+ .enablePositionalOptions();
206
214
  (0, onboard_1.registerOnboardCommand)(program);
207
215
  (0, scan_1.registerScanCommand)(program);
208
216
  (0, analyze_1.registerAnalyzeCommand)(program);
209
217
  (0, report_1.registerReportCommand)(program);
210
218
  (0, config_1.registerConfigCommand)(program);
211
219
  (0, doctor_1.registerDoctorCommand)(program);
220
+ (0, agent_1.registerAgentCommand)(program);
221
+ (0, scans_1.registerScansCommand)(program);
222
+ (0, ai_1.registerAiCommand)(program);
223
+ // Version subcommand with detailed environment info
224
+ program
225
+ .command("version")
226
+ .description("Show detailed version and environment information")
227
+ .action(async () => {
228
+ const os = await Promise.resolve().then(() => __importStar(require("os")));
229
+ let aiProvider = "not configured";
230
+ try {
231
+ const { getConfig } = await Promise.resolve().then(() => __importStar(require("./core/config")));
232
+ const config = await getConfig();
233
+ if (config.ai.enabled) {
234
+ aiProvider = `${config.ai.provider} (${config.ai.defaultModel})`;
235
+ }
236
+ }
237
+ catch {
238
+ // Config not available
239
+ }
240
+ console.log("");
241
+ console.log(theme_1.theme.brightWhite.bold("KramScan") + " " + theme_1.theme.cyan(`v${theme_1.CLI_VERSION}`));
242
+ console.log(theme_1.theme.gray("─".repeat(40)));
243
+ console.log(theme_1.theme.white(" Node.js: ") + theme_1.theme.cyan(process.version));
244
+ console.log(theme_1.theme.white(" Platform: ") + theme_1.theme.cyan(`${os.platform()} ${os.arch()}`));
245
+ console.log(theme_1.theme.white(" OS: ") + theme_1.theme.cyan(os.release()));
246
+ console.log(theme_1.theme.white(" AI Provider:") + " " + theme_1.theme.cyan(aiProvider));
247
+ console.log("");
248
+ });
212
249
  return program;
213
250
  }
214
251
  // ─── Entry Point ───────────────────────────────────────────────────
215
252
  async function run() {
216
253
  const args = process.argv.slice(2);
217
- // If no command is provided, show the interactive dashboard
254
+ // If no command is provided, show the interactive menu
218
255
  if (args.length === 0) {
219
256
  await showInteractiveMenu();
220
257
  }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Agent Command
3
+ * Interactive AI-powered security assistant CLI
4
+ */
5
+ import { Command } from "commander";
6
+ export declare function registerAgentCommand(program: Command): void;
@@ -0,0 +1,250 @@
1
+ "use strict";
2
+ /**
3
+ * Agent Command
4
+ * Interactive AI-powered security assistant CLI
5
+ */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || (function () {
23
+ var ownKeys = function(o) {
24
+ ownKeys = Object.getOwnPropertyNames || function (o) {
25
+ var ar = [];
26
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
+ return ar;
28
+ };
29
+ return ownKeys(o);
30
+ };
31
+ return function (mod) {
32
+ if (mod && mod.__esModule) return mod;
33
+ var result = {};
34
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
+ __setModuleDefault(result, mod);
36
+ return result;
37
+ };
38
+ })();
39
+ var __importDefault = (this && this.__importDefault) || function (mod) {
40
+ return (mod && mod.__esModule) ? mod : { "default": mod };
41
+ };
42
+ Object.defineProperty(exports, "__esModule", { value: true });
43
+ exports.registerAgentCommand = registerAgentCommand;
44
+ const chalk_1 = __importDefault(require("chalk"));
45
+ const agent_1 = require("../agent");
46
+ const logger_1 = require("../utils/logger");
47
+ const readline = __importStar(require("readline"));
48
+ function registerAgentCommand(program) {
49
+ program
50
+ .command("agent")
51
+ .description("Start interactive AI security assistant")
52
+ .option("-m, --message <text>", "Send a single message and exit")
53
+ .option("--no-confirm", "Skip confirmation prompts for medium risk actions")
54
+ .action(async (options) => {
55
+ // Initialize skill registry with all skills
56
+ const skillRegistry = new agent_1.SkillRegistry();
57
+ skillRegistry.register(new agent_1.WebScanSkill());
58
+ skillRegistry.register(new agent_1.AnalyzeFindingsSkill());
59
+ skillRegistry.register(new agent_1.GenerateReportSkill());
60
+ skillRegistry.register(new agent_1.HealthCheckSkill());
61
+ skillRegistry.register(new agent_1.VerifyFindingSkill());
62
+ // Initialize orchestrator
63
+ const orchestrator = new agent_1.AgentOrchestrator(skillRegistry, {
64
+ enableConfirmation: options.confirm !== false,
65
+ });
66
+ try {
67
+ await orchestrator.initialize();
68
+ if (options.message) {
69
+ // Single message mode
70
+ console.log("");
71
+ console.log(chalk_1.default.bold.cyan("🛡️ KramScan Security Agent"));
72
+ console.log(chalk_1.default.gray("─".repeat(50)));
73
+ console.log("");
74
+ const response = await orchestrator.processUserMessage(options.message);
75
+ console.log(chalk_1.default.bold("Agent:"), response.message);
76
+ console.log("");
77
+ await orchestrator.shutdown();
78
+ return;
79
+ }
80
+ // Interactive mode
81
+ await runInteractiveMode(orchestrator);
82
+ }
83
+ catch (error) {
84
+ logger_1.logger.error(`Failed to start agent: ${error instanceof Error ? error.message : String(error)}`);
85
+ process.exit(1);
86
+ }
87
+ });
88
+ }
89
+ async function runInteractiveMode(orchestrator) {
90
+ // Ensure we aren't in raw mode from other interactive flows.
91
+ if (process.stdin.isTTY) {
92
+ try {
93
+ process.stdin.setRawMode(false);
94
+ }
95
+ catch {
96
+ // ignore
97
+ }
98
+ }
99
+ const rl = readline.createInterface({
100
+ input: process.stdin,
101
+ output: process.stdout,
102
+ });
103
+ printWelcomeBanner();
104
+ printAvailableSkills(orchestrator);
105
+ // Share this readline interface with confirmations to avoid double-reading stdin.
106
+ orchestrator.setReadlineInterface(rl);
107
+ orchestrator.start();
108
+ console.log(chalk_1.default.gray("Type 'help' for commands or 'exit' to quit.\n"));
109
+ let closed = false;
110
+ rl.on('SIGINT', () => rl.close());
111
+ rl.on('close', () => { closed = true; });
112
+ const question = (prompt) => new Promise((resolve) => rl.question(prompt, resolve));
113
+ while (!closed) {
114
+ const input = await question(chalk_1.default.gray('You: '));
115
+ if (closed)
116
+ break;
117
+ const trimmed = input.trim();
118
+ if (!trimmed)
119
+ continue;
120
+ const commandResult = await handleSpecialCommand(trimmed, orchestrator, rl);
121
+ if (commandResult.handled) {
122
+ if (commandResult.shouldExit)
123
+ break;
124
+ continue;
125
+ }
126
+ console.log('');
127
+ try {
128
+ const response = await orchestrator.processUserMessage(trimmed);
129
+ console.log(chalk_1.default.bold.cyan('Agent:'));
130
+ console.log(response.message);
131
+ if (response.toolCalls && response.toolCalls.length > 0) {
132
+ console.log('');
133
+ console.log(chalk_1.default.gray('Tools executed:'));
134
+ response.toolCalls.forEach((call) => {
135
+ const result = response.toolCallResults?.find((r) => r.toolCallId === call.id);
136
+ const icon = result?.success ? chalk_1.default.green('OK') : chalk_1.default.red('X');
137
+ console.log(chalk_1.default.gray(' ' + icon + ' ' + call.name));
138
+ });
139
+ }
140
+ console.log('');
141
+ }
142
+ catch (error) {
143
+ console.log(chalk_1.default.red('Error:'), error instanceof Error ? error.message : String(error));
144
+ console.log('');
145
+ }
146
+ }
147
+ console.log(chalk_1.default.gray('\nGoodbye!\n'));
148
+ await orchestrator.shutdown();
149
+ rl.close();
150
+ }
151
+ async function handleSpecialCommand(input, orchestrator, rl) {
152
+ const command = input.toLowerCase();
153
+ switch (command) {
154
+ case "exit":
155
+ case "quit":
156
+ case "/exit":
157
+ case "/quit":
158
+ rl.close();
159
+ return { handled: true, shouldExit: true };
160
+ case "help":
161
+ case "/help":
162
+ printHelp();
163
+ return { handled: true, shouldExit: false };
164
+ case "clear":
165
+ case "/clear":
166
+ case "/new":
167
+ orchestrator.clearConversation();
168
+ console.log(chalk_1.default.gray("\nConversation history cleared.\n"));
169
+ return { handled: true, shouldExit: false };
170
+ case "status":
171
+ case "/status":
172
+ printStatus(orchestrator);
173
+ return { handled: true, shouldExit: false };
174
+ case "skills":
175
+ case "/skills":
176
+ printAvailableSkills(orchestrator);
177
+ return { handled: true, shouldExit: false };
178
+ default:
179
+ return { handled: false, shouldExit: false };
180
+ }
181
+ }
182
+ function printWelcomeBanner() {
183
+ console.log("");
184
+ console.log(chalk_1.default.bold.cyan(`
185
+ ██╗ ██╗██████╗ █████╗ ███╗ ███╗███████╗ ██████╗ █████╗ ███╗ ██╗
186
+ ██║ ██╔╝██╔══██╗██╔══██╗████╗ ████║██╔════╝██╔════╝██╔══██╗████╗ ██║
187
+ █████╔╝ ██████╔╝███████║██╔████╔██║███████╗██║ ███████║██╔██╗ ██║
188
+ ██╔═██╗ ██╔══██╗██╔══██║██║╚██╔╝██║╚════██║██║ ██╔══██║██║╚██╗██║
189
+ ██║ ██╗██║ ██║██║ ██║██║ ╚═╝ ██║███████║╚██████╗██║ ██║██║ ╚████║
190
+ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ ╚═════╝╚═╝ ╚═╝╚═╝ ╚═══╝
191
+ `));
192
+ console.log(chalk_1.default.gray("─".repeat(70)));
193
+ console.log(chalk_1.default.bold.white(" AI-Powered Security Assistant"), chalk_1.default.gray("|"), chalk_1.default.cyan("v0.1.0"));
194
+ console.log(chalk_1.default.gray("─".repeat(70)));
195
+ console.log("");
196
+ }
197
+ function printAvailableSkills(orchestrator) {
198
+ const skills = orchestrator.getAvailableSkills();
199
+ console.log(chalk_1.default.bold("Available Security Skills:"));
200
+ console.log("");
201
+ skills.forEach((skill) => {
202
+ const riskColor = skill.risk === "high"
203
+ ? chalk_1.default.red
204
+ : skill.risk === "medium"
205
+ ? chalk_1.default.yellow
206
+ : chalk_1.default.green;
207
+ console.log(chalk_1.default.bold.white(` ${skill.name}`));
208
+ console.log(chalk_1.default.gray(` ${skill.description}`));
209
+ console.log(chalk_1.default.gray(` Risk: ${riskColor(skill.risk.toUpperCase())}`), skill.requiresConfirmation ? chalk_1.default.yellow("[Requires Confirmation]") : "");
210
+ console.log("");
211
+ });
212
+ }
213
+ function printHelp() {
214
+ console.log("");
215
+ console.log(chalk_1.default.bold("Commands:"));
216
+ console.log("");
217
+ console.log(chalk_1.default.white(" help "), chalk_1.default.gray("- Show this help message"));
218
+ console.log(chalk_1.default.white(" status "), chalk_1.default.gray("- Show session status"));
219
+ console.log(chalk_1.default.white(" skills "), chalk_1.default.gray("- List available skills"));
220
+ console.log(chalk_1.default.white(" clear "), chalk_1.default.gray("- Clear conversation history"));
221
+ console.log(chalk_1.default.white(" exit "), chalk_1.default.gray("- Exit the agent"));
222
+ console.log("");
223
+ console.log(chalk_1.default.bold("Examples:"));
224
+ console.log("");
225
+ console.log(chalk_1.default.gray(" Scan a website:"));
226
+ console.log(chalk_1.default.cyan(' scan https://example.com'));
227
+ console.log("");
228
+ console.log(chalk_1.default.gray(" Analyze findings:"));
229
+ console.log(chalk_1.default.cyan(' analyze the results'));
230
+ console.log("");
231
+ console.log(chalk_1.default.gray(" Generate report:"));
232
+ console.log(chalk_1.default.cyan(' create a report'));
233
+ console.log("");
234
+ console.log(chalk_1.default.gray(" Check system health:"));
235
+ console.log(chalk_1.default.cyan(' health check'));
236
+ console.log("");
237
+ }
238
+ function printStatus(orchestrator) {
239
+ const summary = orchestrator.getConversationSummary();
240
+ console.log("");
241
+ console.log(chalk_1.default.bold("Session Status:"));
242
+ console.log("");
243
+ console.log(chalk_1.default.gray(` Messages: ${summary.totalMessages}`));
244
+ console.log(chalk_1.default.gray(` Duration: ${summary.sessionDuration}`));
245
+ if (summary.currentTarget) {
246
+ console.log(chalk_1.default.gray(` Target: ${summary.currentTarget}`));
247
+ }
248
+ console.log(chalk_1.default.gray(` Scan Results: ${summary.hasScanResults ? "Yes" : "No"}`));
249
+ console.log("");
250
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function registerAiCommand(program: Command): void;