@greenarmor/ges-core
Advanced tools
| export interface AIRecommendation { | ||
| id: string; | ||
| timestamp: string; | ||
| category: "security" | "compliance" | "architecture" | "performance" | "best-practice" | "bug" | "improvement"; | ||
| title: string; | ||
| description: string; | ||
| severity: "info" | "low" | "medium" | "high"; | ||
| affected_controls?: string[]; | ||
| affected_files?: string[]; | ||
| suggested_action: string; | ||
| status: "open" | "acknowledged" | "implemented" | "dismissed"; | ||
| } | ||
| export declare function recordAIRecommendation(projectPath: string, opts: { | ||
| category: AIRecommendation["category"]; | ||
| title: string; | ||
| description: string; | ||
| severity?: AIRecommendation["severity"]; | ||
| affected_controls?: string[]; | ||
| affected_files?: string[]; | ||
| suggested_action: string; | ||
| }): AIRecommendation; | ||
| export declare function loadAIRecommendations(projectPath: string): AIRecommendation[]; |
| import * as fs from "node:fs"; | ||
| import * as path from "node:path"; | ||
| let recCounter = 0; | ||
| export function recordAIRecommendation(projectPath, opts) { | ||
| recCounter++; | ||
| const recommendation = { | ||
| id: `ai-rec-${Date.now()}-${recCounter}`, | ||
| timestamp: new Date().toISOString(), | ||
| category: opts.category, | ||
| title: opts.title, | ||
| description: opts.description, | ||
| severity: opts.severity || "info", | ||
| affected_controls: opts.affected_controls || [], | ||
| affected_files: opts.affected_files || [], | ||
| suggested_action: opts.suggested_action, | ||
| status: "open", | ||
| }; | ||
| const devLogsDir = path.join(projectPath, ".dev-logs", "ai-recommendations"); | ||
| if (!fs.existsSync(devLogsDir)) { | ||
| fs.mkdirSync(devLogsDir, { recursive: true }); | ||
| } | ||
| const dateStr = new Date().toISOString().split("T")[0]; | ||
| const fileName = `${dateStr}-${recommendation.id}.md`; | ||
| const md = [ | ||
| `# AI Recommendation: ${recommendation.title}`, | ||
| ``, | ||
| `**ID**: ${recommendation.id}`, | ||
| `**Date**: ${recommendation.timestamp}`, | ||
| `**Category**: ${recommendation.category}`, | ||
| `**Severity**: ${recommendation.severity}`, | ||
| `**Status**: ${recommendation.status}`, | ||
| ``, | ||
| `## Description`, | ||
| ``, | ||
| recommendation.description, | ||
| ``, | ||
| ]; | ||
| if (recommendation.affected_controls && recommendation.affected_controls.length > 0) { | ||
| md.push(`## Affected Controls`); | ||
| md.push(""); | ||
| for (const c of recommendation.affected_controls) { | ||
| md.push(`- ${c}`); | ||
| } | ||
| md.push(""); | ||
| } | ||
| if (recommendation.affected_files && recommendation.affected_files.length > 0) { | ||
| md.push(`## Affected Files`); | ||
| md.push(""); | ||
| for (const f of recommendation.affected_files) { | ||
| md.push(`- ${f}`); | ||
| } | ||
| md.push(""); | ||
| } | ||
| md.push(`## Suggested Action`); | ||
| md.push(""); | ||
| md.push(recommendation.suggested_action); | ||
| md.push(""); | ||
| md.push(`---`); | ||
| md.push(`*This recommendation was generated by an AI assistant using the GESF MCP server. It is logged here for developer review and is NOT automatically applied to the project.*`); | ||
| md.push(""); | ||
| fs.writeFileSync(path.join(devLogsDir, fileName), md.join("\n"), "utf-8"); | ||
| return recommendation; | ||
| } | ||
| export function loadAIRecommendations(projectPath) { | ||
| const recDir = path.join(projectPath, ".dev-logs", "ai-recommendations"); | ||
| const results = []; | ||
| try { | ||
| const entries = fs.readdirSync(recDir); | ||
| for (const entry of entries) { | ||
| if (!entry.endsWith(".md")) | ||
| continue; | ||
| try { | ||
| const content = fs.readFileSync(path.join(recDir, entry), "utf-8"); | ||
| const idMatch = content.match(/\*\*ID\*\*:\s*(.+)/); | ||
| const titleMatch = content.match(/^# AI Recommendation:\s*(.+)/m); | ||
| const catMatch = content.match(/\*\*Category\*\*:\s*(.+)/); | ||
| const sevMatch = content.match(/\*\*Severity\*\*:\s*(.+)/); | ||
| const dateMatch = content.match(/\*\*Date\*\*:\s*(.+)/); | ||
| const statusMatch = content.match(/\*\*Status\*\*:\s*(.+)/); | ||
| if (idMatch && titleMatch) { | ||
| results.push({ | ||
| id: idMatch[1].trim(), | ||
| timestamp: dateMatch ? dateMatch[1].trim() : "", | ||
| category: (catMatch ? catMatch[1].trim() : "improvement"), | ||
| title: titleMatch[1].trim(), | ||
| severity: (sevMatch ? sevMatch[1].trim() : "info"), | ||
| description: "", | ||
| suggested_action: "", | ||
| status: (statusMatch ? statusMatch[1].trim() : "open"), | ||
| }); | ||
| } | ||
| } | ||
| catch { | ||
| // skip malformed files | ||
| } | ||
| } | ||
| } | ||
| catch { | ||
| // dir doesn't exist | ||
| } | ||
| return results.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()); | ||
| } |
+1
-0
@@ -7,1 +7,2 @@ export * from "./types/index.js"; | ||
| export * from "./activity-log/index.js"; | ||
| export * from "./recommendations/index.js"; |
+1
-0
@@ -7,1 +7,2 @@ export * from "./types/index.js"; | ||
| export * from "./activity-log/index.js"; | ||
| export * from "./recommendations/index.js"; |
+1
-1
@@ -27,3 +27,3 @@ { | ||
| "types": "./dist/index.d.ts", | ||
| "version": "1.2.5", | ||
| "version": "1.2.6", | ||
| "scripts": { | ||
@@ -30,0 +30,0 @@ "build": "tsc", |
62356
8.81%19
11.76%1563
8.77%