add-skill-kit 3.2.2 → 3.2.4

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 (72) hide show
  1. package/bin/lib/commands/install.js +67 -45
  2. package/lib/agent-cli/README.md +21 -0
  3. package/lib/agent-cli/bin/ag-smart.js +158 -0
  4. package/lib/agent-cli/lib/audit.js +154 -0
  5. package/lib/agent-cli/lib/audit.test.js +100 -0
  6. package/lib/agent-cli/lib/auto-learn.js +319 -0
  7. package/lib/agent-cli/lib/auto_preview.py +148 -0
  8. package/lib/agent-cli/lib/backup.js +138 -0
  9. package/lib/agent-cli/lib/backup.test.js +78 -0
  10. package/lib/agent-cli/lib/checklist.py +222 -0
  11. package/lib/agent-cli/lib/cognitive-lesson.js +476 -0
  12. package/lib/agent-cli/lib/completion.js +149 -0
  13. package/lib/agent-cli/lib/config.js +35 -0
  14. package/lib/agent-cli/lib/eslint-fix.js +238 -0
  15. package/lib/agent-cli/lib/evolution-signal.js +215 -0
  16. package/lib/agent-cli/lib/export.js +86 -0
  17. package/lib/agent-cli/lib/export.test.js +65 -0
  18. package/lib/agent-cli/lib/fix.js +337 -0
  19. package/lib/agent-cli/lib/fix.test.js +80 -0
  20. package/lib/agent-cli/lib/gemini-export.js +83 -0
  21. package/lib/agent-cli/lib/generate-registry.js +42 -0
  22. package/lib/agent-cli/lib/hooks/install-hooks.js +152 -0
  23. package/lib/agent-cli/lib/hooks/lint-learn.js +172 -0
  24. package/lib/agent-cli/lib/ignore.js +116 -0
  25. package/lib/agent-cli/lib/ignore.test.js +58 -0
  26. package/lib/agent-cli/lib/init.js +124 -0
  27. package/lib/agent-cli/lib/learn.js +255 -0
  28. package/lib/agent-cli/lib/learn.test.js +70 -0
  29. package/lib/agent-cli/lib/migrate-to-v4.js +322 -0
  30. package/lib/agent-cli/lib/proposals.js +199 -0
  31. package/lib/agent-cli/lib/proposals.test.js +56 -0
  32. package/lib/agent-cli/lib/recall.js +820 -0
  33. package/lib/agent-cli/lib/recall.test.js +107 -0
  34. package/lib/agent-cli/lib/selfevolution-bridge.js +167 -0
  35. package/lib/agent-cli/lib/session_manager.py +120 -0
  36. package/lib/agent-cli/lib/settings.js +203 -0
  37. package/lib/agent-cli/lib/skill-learn.js +296 -0
  38. package/lib/agent-cli/lib/stats.js +132 -0
  39. package/lib/agent-cli/lib/stats.test.js +94 -0
  40. package/lib/agent-cli/lib/types.js +33 -0
  41. package/lib/agent-cli/lib/ui/audit-ui.js +146 -0
  42. package/lib/agent-cli/lib/ui/backup-ui.js +107 -0
  43. package/lib/agent-cli/lib/ui/clack-helpers.js +317 -0
  44. package/lib/agent-cli/lib/ui/common.js +83 -0
  45. package/lib/agent-cli/lib/ui/completion-ui.js +126 -0
  46. package/lib/agent-cli/lib/ui/custom-select.js +69 -0
  47. package/lib/agent-cli/lib/ui/dashboard-ui.js +123 -0
  48. package/lib/agent-cli/lib/ui/evolution-signals-ui.js +107 -0
  49. package/lib/agent-cli/lib/ui/export-ui.js +94 -0
  50. package/lib/agent-cli/lib/ui/fix-all-ui.js +191 -0
  51. package/lib/agent-cli/lib/ui/help-ui.js +49 -0
  52. package/lib/agent-cli/lib/ui/index.js +169 -0
  53. package/lib/agent-cli/lib/ui/init-ui.js +56 -0
  54. package/lib/agent-cli/lib/ui/knowledge-ui.js +55 -0
  55. package/lib/agent-cli/lib/ui/learn-ui.js +706 -0
  56. package/lib/agent-cli/lib/ui/lessons-ui.js +148 -0
  57. package/lib/agent-cli/lib/ui/pretty.js +145 -0
  58. package/lib/agent-cli/lib/ui/proposals-ui.js +99 -0
  59. package/lib/agent-cli/lib/ui/recall-ui.js +342 -0
  60. package/lib/agent-cli/lib/ui/routing-demo.js +79 -0
  61. package/lib/agent-cli/lib/ui/routing-ui.js +325 -0
  62. package/lib/agent-cli/lib/ui/settings-ui.js +381 -0
  63. package/lib/agent-cli/lib/ui/stats-ui.js +123 -0
  64. package/lib/agent-cli/lib/ui/watch-ui.js +236 -0
  65. package/lib/agent-cli/lib/verify_all.py +327 -0
  66. package/lib/agent-cli/lib/watcher.js +181 -0
  67. package/lib/agent-cli/lib/watcher.test.js +85 -0
  68. package/lib/agent-cli/package.json +51 -0
  69. package/lib/agentskillskit-cli/README.md +21 -0
  70. package/lib/agentskillskit-cli/ag-smart.js +158 -0
  71. package/lib/agentskillskit-cli/package.json +51 -0
  72. package/package.json +11 -6
@@ -131,7 +131,7 @@ export async function run(spec) {
131
131
  for (const e of fs.readdirSync(skillsDir)) {
132
132
  const sp = path.join(skillsDir, e);
133
133
  if (fs.statSync(sp).isDirectory()) {
134
- // Check if this directory has SKILL.md (top-level skill)
134
+ // Check if this directory has SKILL.md (top-level skill only)
135
135
  if (fs.existsSync(path.join(sp, "SKILL.md"))) {
136
136
  const m = parseSkillMdFrontmatter(path.join(sp, "SKILL.md"));
137
137
  skillsInRepo.push({
@@ -142,22 +142,8 @@ export async function run(spec) {
142
142
  _path: sp
143
143
  });
144
144
  }
145
-
146
- // Also scan nested directories (2 levels deep) for sub-skills
147
- for (const sub of fs.readdirSync(sp)) {
148
- const subPath = path.join(sp, sub);
149
- if (fs.statSync(subPath).isDirectory() && fs.existsSync(path.join(subPath, "SKILL.md"))) {
150
- const m = parseSkillMdFrontmatter(path.join(subPath, "SKILL.md"));
151
- const skillName = `${e}/${sub}`; // e.g. "game-development/2d-games"
152
- skillsInRepo.push({
153
- title: skillName,
154
- value: skillName,
155
- description: m.description || "",
156
- selected: singleSkill ? skillName === singleSkill : true,
157
- _path: subPath
158
- });
159
- }
160
- }
145
+ // NOTE: Sub-folders are NOT counted as separate skills
146
+ // They are part of the parent skill (e.g. game-development/2d-games)
161
147
  }
162
148
  }
163
149
  }
@@ -187,31 +173,45 @@ export async function run(spec) {
187
173
  stepLine();
188
174
  step(`Auto-selected: ${c.cyan(singleSkill)}`);
189
175
  } else {
190
- // Group skills by category - 5 main categories (all skills covered, no Other)
191
- // NOTE: Order matters! Mobile is before Security so mobile-security-coder goes to Mobile
176
+ // FAANG-Grade Categories (8 balanced categories)
177
+ // NOTE: Order matters! Specialized categories FIRST, Core is fallback
192
178
  const CATEGORY_KEYWORDS = {
193
- "⚙️ Backend & API": [
194
- // Core backend
195
- "backend", "api", "nodejs", "server", "database", "prisma", "mcp", "python", "cache",
196
- // Architecture & patterns
197
- "architecture", "pattern", "app-builder", "performance", "profiling",
198
- // Testing & quality
199
- "testing", "test", "tdd", "lint", "validate", "debugging", "systematic", "debug",
200
- "code-review", "code-quality", "e2e", "integration", "webapp", "checklist", "reviewer", "clean",
201
- // Documentation & tools
202
- "documentation", "plan", "writing", "geo", "seo", "i18n", "localization", "template",
203
- // CLI & scripting
204
- "bash", "linux", "powershell", "windows", "shell", "script",
205
- // AI & automation
206
- "agent", "routing", "brainstorm", "behavioral", "intelligent", "parallel", "modes",
207
- // Other skills
208
- "typescript", "problem", "workflow", "builder", "conventions", "fundamentals",
209
- "best-practices", "procedures", "management"
179
+ // Specialized domains
180
+ "🎨 Frontend & UI": [
181
+ "frontend", "nextjs", "tailwind", "css", "ui", "ux", "visual",
182
+ "studio", "web-core", "design-system", "react-architect", "react"
183
+ ],
184
+ "🎮 Game Development": [
185
+ "game", "development", "engine", "unity", "unreal", "godot", "phaser"
186
+ ],
187
+ "📱 Mobile": [
188
+ "mobile", "first", "developer", "react-native", "flutter",
189
+ "ios", "android", "swift", "kotlin"
190
+ ],
191
+ "🔒 Security & DevOps": [
192
+ "security", "vulnerability", "offensive", "scanner", "red-team", "governance",
193
+ "cicd", "pipeline", "gitops", "docker", "deploy", "server-ops"
194
+ ],
195
+ // Technical domains
196
+ "🧪 Testing & Quality": [
197
+ "test", "testing", "tdd", "e2e", "debug", "quality", "review",
198
+ "lint", "validate", "automation", "problem", "checker"
199
+ ],
200
+ "🤖 AI & Agents": [
201
+ "agent", "pattern", "auto-learn", "execution", "self-evolution",
202
+ "lifecycle", "skill-forge", "intelligent", "routing"
210
203
  ],
211
- "🎨 Frontend & UI": ["frontend", "nextjs", "tailwind", "css", "ui", "ux", "visual", "studio", "web-core", "design-system", "react-architect", "react"],
212
- "🎮 Game Development": ["game", "development", "engine", "unity", "unreal", "godot", "phaser", "game-dev"],
213
- "📱 Mobile": ["mobile", "first", "developer", "react-native", "flutter", "ios", "android", "swift", "kotlin"],
214
- "🔒 Security & DevOps": ["security", "vulnerability", "deploy", "git", "docker", "red-team", "governance", "offensive", "gitops", "cicd", "pipeline", "incident", "chaos", "scanner"]
204
+ "📚 Docs & Planning": [
205
+ "doc", "template", "plan", "project", "idea", "brainstorm",
206
+ "geo", "seo", "i18n", "writing"
207
+ ],
208
+ // Fallback (core backend/infra)
209
+ "⚙️ Backend & Core": [
210
+ "backend", "api", "nodejs", "python", "server", "database",
211
+ "prisma", "mcp", "data", "architect", "scaffold", "system",
212
+ "typescript", "shell", "bash", "powershell", "git", "code-craft",
213
+ "code-constitution", "observability", "perf", "state", "rollback"
214
+ ]
215
215
  };
216
216
 
217
217
  function categorizeSkill(skillName) {
@@ -221,12 +221,18 @@ export async function run(spec) {
221
221
  return category;
222
222
  }
223
223
  }
224
- return "⚙️ Backend & API"; // Default fallback (no "Other" category)
224
+ return "⚙️ Backend & Core"; // Default fallback (no "Other" category)
225
225
  }
226
226
 
227
+ // REQUIRED SKILLS - Always installed, not shown in selection
228
+ const REQUIRED_SKILLS = ["auto-learner"];
229
+
230
+ // Filter out required skills from selection list
231
+ const selectableSkills = skillsInRepo.filter(s => !REQUIRED_SKILLS.includes(s.value));
232
+
227
233
  // Group skills by category
228
234
  const grouped = {};
229
- for (const skill of skillsInRepo) {
235
+ for (const skill of selectableSkills) {
230
236
  const cat = categorizeSkill(skill.value);
231
237
  if (!grouped[cat]) grouped[cat] = [];
232
238
  grouped[cat].push(skill);
@@ -263,13 +269,22 @@ export async function run(spec) {
263
269
 
264
270
  // Get all skills from selected categories
265
271
  selectedSkills = selectedCategories.flatMap(cat => grouped[cat].map(s => s.value));
266
- }
267
272
 
273
+ // Add required system skills
274
+ const requiredInRepo = skillsInRepo.filter(s => REQUIRED_SKILLS.includes(s.value)).map(s => s.value);
275
+ selectedSkills = [...new Set([...selectedSkills, ...requiredInRepo])];
276
+ }
268
277
 
278
+ // Check for required skills and show info
279
+ const CORE_REQUIRED = ["auto-learner"];
280
+ const installedRequired = selectedSkills.filter(s => CORE_REQUIRED.includes(s));
269
281
 
270
282
  stepLine();
271
283
  step("Select skills to install");
272
- console.log(`${c.gray(S.branch)} ${c.dim(selectedSkills.join(", "))}`);
284
+ console.log(`${c.gray(S.branch)} ${c.dim(selectedSkills.filter(s => !CORE_REQUIRED.includes(s)).join(", "))}`);
285
+ if (installedRequired.length > 0) {
286
+ console.log(`${c.gray(S.branch)} ${c.cyan("+ System required:")} ${c.green(installedRequired.join(", "))}`);
287
+ }
273
288
 
274
289
  // --- Detect installed agents ---
275
290
  stepLine();
@@ -490,7 +505,7 @@ export async function run(spec) {
490
505
  archInstalled = true;
491
506
  }
492
507
 
493
- // Install knowledge if it exists
508
+ // Install knowledge if it exists (required for Agent CLI)
494
509
  const knowledgeDir = path.join(baseAgentDir, "knowledge");
495
510
  const targetKnowledgeDir = path.join(WORKSPACE, "..", "knowledge");
496
511
  let knowledgeInstalled = false;
@@ -499,6 +514,13 @@ export async function run(spec) {
499
514
  fs.cpSync(knowledgeDir, targetKnowledgeDir, { recursive: true });
500
515
  step("Installed knowledge/");
501
516
  knowledgeInstalled = true;
517
+ } else if (!fs.existsSync(targetKnowledgeDir)) {
518
+ // Create empty knowledge folder for Agent CLI
519
+ fs.mkdirSync(targetKnowledgeDir, { recursive: true });
520
+ // Create minimal structure for Agent CLI
521
+ fs.writeFileSync(path.join(targetKnowledgeDir, "lessons-learned.yaml"), "# Lessons learned by AI Agent\nlessons: []\n");
522
+ step("Created knowledge/ (Agent CLI ready)");
523
+ knowledgeInstalled = true;
502
524
  }
503
525
 
504
526
  // Install rules if they exist
@@ -0,0 +1,21 @@
1
+ # Agent Skill Kit CLI
2
+
3
+ The intelligent CLI engine for Agent Skill Kit, featuring:
4
+ - **Routing Engine**: Semantic routing validation
5
+ - **Audit System**: Skill usage auditing and metrics
6
+ - **Interactive Mode**: Smart interactive agent interface
7
+
8
+ ## Installation
9
+
10
+ This package is typically managed by the `add-skill-kit` installer.
11
+
12
+ ```bash
13
+ # Global install (managed by installer)
14
+ npm install -g @agentskillskit/cli
15
+
16
+ # Usage
17
+ agent
18
+ ```
19
+
20
+ ## Documentation
21
+ See the main [Agent Skill Kit](https://github.com/agentskillskit/agent-skills) repository.
@@ -0,0 +1,158 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Smart Agent CLI - ESM Version (Production-Ready)
4
+ *
5
+ * The main interface for humans to interact with the Smart Agent Skills system.
6
+ *
7
+ * Commands:
8
+ * learn Add new lessons to memory
9
+ * recall Check files against memory
10
+ * audit Full compliance audit
11
+ * watch Real-time file monitoring
12
+ * stats Knowledge base statistics
13
+ * install-hooks Install git pre-commit hook
14
+ * lint-learn Auto-learn from ESLint output
15
+ */
16
+
17
+ import { spawn } from "child_process";
18
+ import path from "path";
19
+ import { fileURLToPath } from "url";
20
+ import { VERSION } from "../lib/config.js";
21
+
22
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
23
+ const ARGS = process.argv.slice(2);
24
+ const COMMAND = ARGS[0];
25
+ const SCRIPTS_DIR = path.join(__dirname, "..", "lib");
26
+ const HOOKS_DIR = path.join(SCRIPTS_DIR, "hooks");
27
+
28
+ /**
29
+ * Run a script with given arguments
30
+ * @param {string} script - Script filename (relative to lib/)
31
+ * @param {string[]} args - Arguments to pass
32
+ * @param {string} baseDir - Base directory for script
33
+ */
34
+ function run(script, args = [], baseDir = SCRIPTS_DIR) {
35
+ const scriptPath = path.join(baseDir, script);
36
+ const child = spawn("node", [scriptPath, ...args], {
37
+ stdio: "inherit",
38
+ shell: true
39
+ });
40
+
41
+ child.on("close", (code) => {
42
+ process.exit(code || 0);
43
+ });
44
+
45
+ child.on("error", (err) => {
46
+ console.error(`❌ Failed to run ${script}:`, err.message);
47
+ process.exit(1);
48
+ });
49
+ }
50
+
51
+ function printHelp() {
52
+ console.log(`
53
+ 🤖 Agent Skill Kit CLI v${VERSION}
54
+
55
+ Usage: ag-smart <command> [options]
56
+
57
+ ${"─".repeat(50)}
58
+
59
+ 📚 CORE COMMANDS:
60
+
61
+ learn Teach a new lesson to the memory
62
+ ag-smart learn --add --pattern "var " --message "Use let/const"
63
+ ag-smart learn --list
64
+ ag-smart learn --remove LEARN-001
65
+
66
+ recall Check file(s) against learned patterns
67
+ ag-smart recall src/app.js
68
+ ag-smart recall ./src
69
+
70
+ audit Run full compliance audit
71
+ ag-smart audit [directory]
72
+
73
+ ${"─".repeat(50)}
74
+
75
+ 🚀 PRODUCTION FEATURES:
76
+
77
+ watch Real-time file monitoring
78
+ ag-smart watch [directory]
79
+
80
+ stats Knowledge base statistics
81
+ ag-smart stats
82
+
83
+ install-hooks Install git pre-commit hook
84
+ ag-smart install-hooks
85
+ ag-smart install-hooks --remove
86
+
87
+ lint-learn Auto-learn from ESLint JSON output
88
+ npx eslint . --format json | ag-smart lint-learn
89
+
90
+ fix 🆕 Auto-fix violations
91
+ ag-smart fix <file|dir> [--mode safe|aggressive]
92
+
93
+ sync-skills 🆕 Sync hot patterns to SKILL.md
94
+ ag-smart sync-skills
95
+
96
+ ${"─".repeat(50)}
97
+
98
+ 📖 HELP:
99
+
100
+ help, --help Show this help message
101
+ --version Show version number
102
+
103
+ 💡 Docs: https://github.com/agentskillkit/agent-skills
104
+ `);
105
+ }
106
+
107
+ // Command routing
108
+ switch (COMMAND) {
109
+ // Core commands (v2 versions)
110
+ case "learn":
111
+ run("learn.js", ARGS.slice(1));
112
+ break;
113
+ case "recall":
114
+ run("recall.js", ARGS.slice(1));
115
+ break;
116
+ case "audit":
117
+ run("audit.js", ARGS.slice(1));
118
+ break;
119
+
120
+ // Production features
121
+ case "watch":
122
+ run("watcher.js", ARGS.slice(1));
123
+ break;
124
+ case "stats":
125
+ run("stats.js", ARGS.slice(1));
126
+ break;
127
+ case "install-hooks":
128
+ run("install-hooks.js", ARGS.slice(1), HOOKS_DIR);
129
+ break;
130
+ case "lint-learn":
131
+ run("lint-learn.js", ARGS.slice(1), HOOKS_DIR);
132
+ break;
133
+ case "fix":
134
+ run("fix.js", ARGS.slice(1));
135
+ break;
136
+ case "sync-skills":
137
+ run("skill-learn.js", ARGS.slice(1));
138
+ break;
139
+
140
+ // Meta
141
+ case "--version":
142
+ case "-v":
143
+ console.log(VERSION);
144
+ break;
145
+ case "help":
146
+ case "--help":
147
+ case "-h":
148
+ printHelp();
149
+ break;
150
+ case undefined:
151
+ // No command = show interactive Clack menu
152
+ import("../lib/ui/index.js").then(m => m.showMainMenu()).catch(console.error);
153
+ break;
154
+ default:
155
+ console.log(`❌ Unknown command: ${COMMAND}`);
156
+ console.log(" Run 'ag-smart help' for available commands.\n");
157
+ process.exit(1);
158
+ }
@@ -0,0 +1,154 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Smart Audit Script (Production-Ready)
4
+ *
5
+ * The "Judge" - Orchestrates all compliance checks:
6
+ * 1. Memory Recall (Past Mistakes)
7
+ * 2. Constitution Checks (Governance)
8
+ * 3. Real-time Analysis
9
+ *
10
+ * Usage: ag-smart audit [directory]
11
+ */
12
+
13
+ import fs from "fs";
14
+ import path from "path";
15
+ import { fileURLToPath } from "url";
16
+ import { scanDirectory, loadKnowledge, saveKnowledge, printResults } from "./recall.js";
17
+ import { AGENT_DIR, VERSION } from "./config.js";
18
+ import * as p from "@clack/prompts";
19
+ import pc from "picocolors";
20
+
21
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
22
+
23
+ // ============================================================================
24
+ // AUDIT CONFIGURATION
25
+ // ============================================================================
26
+
27
+ const SCAN_EXTENSIONS = [".js", ".ts", ".tsx", ".jsx", ".mjs"];
28
+
29
+ // ============================================================================
30
+ // GOVERNANCE CHECKS
31
+ // ============================================================================
32
+
33
+ /**
34
+ * Check if governance files exist
35
+ * @param {string} projectRoot
36
+ * @returns {{ passed: boolean, details: string[] }}
37
+ */
38
+ function checkGovernance(projectRoot) {
39
+ const details = [];
40
+ let passed = true;
41
+
42
+ const governanceFiles = [
43
+ { path: path.join(projectRoot, ".agent", "GEMINI.md"), name: "GEMINI.md" },
44
+ { path: path.join(projectRoot, ".agent", "ARCHITECTURE.md"), name: "ARCHITECTURE.md" }
45
+ ];
46
+
47
+ governanceFiles.forEach(file => {
48
+ if (fs.existsSync(file.path)) {
49
+ details.push(`${pc.green("✓")} ${file.name} found`);
50
+ } else {
51
+ details.push(`${pc.yellow("⚠")} ${file.name} not found (optional)`);
52
+ }
53
+ });
54
+
55
+ // Check for skills
56
+ const skillsDir = path.join(projectRoot, ".agent", "skills");
57
+ if (fs.existsSync(skillsDir)) {
58
+ const skills = fs.readdirSync(skillsDir).filter(f =>
59
+ fs.statSync(path.join(skillsDir, f)).isDirectory()
60
+ );
61
+ details.push(`${pc.green("✓")} ${skills.length} skill(s) loaded`);
62
+ }
63
+
64
+ return { passed, details };
65
+ }
66
+
67
+ // ============================================================================
68
+ // MAIN AUDIT
69
+ // ============================================================================
70
+
71
+ /**
72
+ * Run full audit on a project
73
+ * @param {string} projectRoot
74
+ */
75
+ async function runAudit(projectRoot) {
76
+ p.intro(pc.cyan(`⚖️ SMART AUDIT v${VERSION}`));
77
+
78
+ let exitCode = 0;
79
+ const startTime = Date.now();
80
+
81
+ // Phase 1: Memory Recall
82
+ const s1 = p.spinner();
83
+ s1.start("Phase 1: Memory Recall");
84
+
85
+ const db = loadKnowledge();
86
+
87
+ if (db.lessons.length === 0) {
88
+ s1.stop("Phase 1: No lessons learned yet");
89
+ } else {
90
+ const { results } = scanDirectory(projectRoot, db, SCAN_EXTENSIONS);
91
+
92
+ if (results.length > 0) {
93
+ const stats = printResults(results);
94
+ saveKnowledge(db);
95
+
96
+ if (stats.errors > 0) {
97
+ exitCode = 1;
98
+ }
99
+ s1.stop(`Phase 1: Found ${stats.total} violation(s)`);
100
+ } else {
101
+ s1.stop("Phase 1: No violations found");
102
+ }
103
+ }
104
+
105
+ // Phase 2: Governance
106
+ const s2 = p.spinner();
107
+ s2.start("Phase 2: Governance Check");
108
+
109
+ const govResult = checkGovernance(projectRoot);
110
+ s2.stop("Phase 2: Governance checked");
111
+
112
+ p.note(govResult.details.join("\n"), pc.dim("Governance"));
113
+
114
+ // Phase 3: Summary
115
+ const duration = ((Date.now() - startTime) / 1000).toFixed(2);
116
+ const totalHits = db.lessons.reduce((sum, l) => sum + (l.hitCount || 0), 0);
117
+
118
+ const summaryLines = [
119
+ `⏱️ Completed in ${duration}s`,
120
+ `📊 Lessons in memory: ${db.lessons.length}`,
121
+ `🎯 Total pattern hits: ${totalHits}`
122
+ ];
123
+ p.note(summaryLines.join("\n"), pc.dim("Summary"));
124
+
125
+ if (exitCode === 0) {
126
+ p.outro(pc.green("✅ AUDIT PASSED: Code is smart and compliant"));
127
+ } else {
128
+ p.outro(pc.red("❌ AUDIT FAILED: Please fix ERROR violations"));
129
+ }
130
+
131
+ process.exit(exitCode);
132
+ }
133
+
134
+ // ============================================================================
135
+ // CLI
136
+ // ============================================================================
137
+
138
+ const args = process.argv.slice(2);
139
+ const projectRoot = args[0] || process.cwd();
140
+
141
+ if (args.includes("--help")) {
142
+ console.log(`
143
+ ⚖️ Smart Audit - Compliance Checker
144
+
145
+ Usage:
146
+ ag-smart audit [directory]
147
+
148
+ Options:
149
+ --help Show this help
150
+ `);
151
+ process.exit(0);
152
+ }
153
+
154
+ runAudit(projectRoot);
@@ -0,0 +1,100 @@
1
+ /**
2
+ * @fileoverview Tests for audit.js functionality
3
+ */
4
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
5
+ import fs from "fs";
6
+ import path from "path";
7
+ import os from "os";
8
+
9
+ const TEST_DIR = path.join(os.tmpdir(), "agent-skill-kit-audit-test");
10
+ const AGENT_DIR = path.join(TEST_DIR, ".agent");
11
+
12
+ describe("Audit - Governance Check", () => {
13
+ beforeEach(() => {
14
+ fs.mkdirSync(AGENT_DIR, { recursive: true });
15
+ });
16
+
17
+ afterEach(() => {
18
+ fs.rmSync(TEST_DIR, { recursive: true, force: true });
19
+ });
20
+
21
+ it("detects GEMINI.md presence", () => {
22
+ const geminiPath = path.join(AGENT_DIR, "GEMINI.md");
23
+
24
+ // Before creation
25
+ expect(fs.existsSync(geminiPath)).toBe(false);
26
+
27
+ // After creation
28
+ fs.writeFileSync(geminiPath, "# Test", "utf8");
29
+ expect(fs.existsSync(geminiPath)).toBe(true);
30
+ });
31
+
32
+ it("detects ARCHITECTURE.md presence", () => {
33
+ const archPath = path.join(AGENT_DIR, "ARCHITECTURE.md");
34
+ fs.writeFileSync(archPath, "# Architecture", "utf8");
35
+
36
+ expect(fs.existsSync(archPath)).toBe(true);
37
+ });
38
+
39
+ it("counts skills in skills directory", () => {
40
+ const skillsDir = path.join(AGENT_DIR, "skills");
41
+ fs.mkdirSync(skillsDir, { recursive: true });
42
+
43
+ // Create mock skills
44
+ fs.mkdirSync(path.join(skillsDir, "skill-1"));
45
+ fs.mkdirSync(path.join(skillsDir, "skill-2"));
46
+
47
+ const skills = fs.readdirSync(skillsDir).filter(f =>
48
+ fs.statSync(path.join(skillsDir, f)).isDirectory()
49
+ );
50
+
51
+ expect(skills.length).toBe(2);
52
+ });
53
+ });
54
+
55
+ describe("Audit - Compliance Results", () => {
56
+ it("calculates pass/fail status correctly", () => {
57
+ const results = {
58
+ errors: 0,
59
+ warnings: 2,
60
+ total: 2
61
+ };
62
+
63
+ // Errors = 0 means pass
64
+ expect(results.errors === 0).toBe(true);
65
+ });
66
+
67
+ it("fails when errors exist", () => {
68
+ const results = {
69
+ errors: 1,
70
+ warnings: 0,
71
+ total: 1
72
+ };
73
+
74
+ expect(results.errors > 0).toBe(true);
75
+ });
76
+
77
+ it("calculates total correctly", () => {
78
+ const violations = [
79
+ { lesson: { severity: "ERROR" }, matches: [1, 2] },
80
+ { lesson: { severity: "WARNING" }, matches: [1] }
81
+ ];
82
+
83
+ let total = 0;
84
+ let errors = 0;
85
+ let warnings = 0;
86
+
87
+ violations.forEach(v => {
88
+ total += v.matches.length;
89
+ if (v.lesson.severity === "ERROR") {
90
+ errors += v.matches.length;
91
+ } else {
92
+ warnings += v.matches.length;
93
+ }
94
+ });
95
+
96
+ expect(total).toBe(3);
97
+ expect(errors).toBe(2);
98
+ expect(warnings).toBe(1);
99
+ });
100
+ });