@khiem_enhance/ai-doc-agent 0.1.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/README.md ADDED
@@ -0,0 +1,32 @@
1
+ # ai-doc-agent
2
+
3
+ AI-powered documentation generator that analyzes your source code and generates
4
+ technical documentation directly from it.
5
+
6
+ > The codebase is the single source of truth.
7
+
8
+ ---
9
+
10
+ ## ✨ Features
11
+
12
+ - Generate **Architecture Overview**
13
+ - Generate **Module / Feature documentation**
14
+ - Generate **API documentation** (from FE usage or BE code)
15
+ - Generate **Component / Service documentation**
16
+ - Output structured **Markdown docs**
17
+
18
+ ---
19
+
20
+ ## 📦 Requirements
21
+
22
+ - Node.js >= 18
23
+ - An OpenAI API key
24
+
25
+ ---
26
+
27
+ ## 🔐 Setup
28
+
29
+ Set your OpenAI API key as an environment variable:
30
+
31
+ ```bash
32
+ export OPENAI_API_KEY=sk-xxxxxxxx
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateArchitectureDoc = generateArchitectureDoc;
4
+ const openaiClient_1 = require("../llm/openaiClient");
5
+ async function generateArchitectureDoc(tree, files) {
6
+ const prompt = `
7
+ You are an AI Documentation Agent.
8
+
9
+ RULES:
10
+ - Code is the single source of truth
11
+ - Do NOT invent features
12
+ - If unclear, mark assumptions clearly
13
+
14
+ GOAL:
15
+ Generate Architecture Overview documentation.
16
+
17
+ PROJECT STRUCTURE:
18
+ ${tree}
19
+
20
+ SOURCE CODE:
21
+ ${files}
22
+
23
+ OUTPUT:
24
+ Markdown only.
25
+ `;
26
+ return (0, openaiClient_1.askLLM)(prompt);
27
+ }
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateModuleDocs = generateModuleDocs;
4
+ const openaiClient_1 = require("../llm/openaiClient");
5
+ async function generateModuleDocs(moduleName, fileList, sourceCode) {
6
+ const prompt = `
7
+ You are an AI Documentation Agent.
8
+
9
+ RULES:
10
+ - Code is the single source of truth
11
+ - Do NOT invent features
12
+ - If unclear, explicitly mark assumptions
13
+
14
+ GOAL:
15
+ Generate documentation for ONE module / feature.
16
+
17
+ MODULE NAME:
18
+ ${moduleName}
19
+
20
+ FILES:
21
+ ${fileList}
22
+
23
+ SOURCE CODE:
24
+ ${sourceCode}
25
+
26
+ DOCUMENT STRUCTURE:
27
+ # ${moduleName}
28
+
29
+ ## Purpose
30
+ ## Responsibilities
31
+ ## Main Files
32
+ ## Execution Flow
33
+ ## Edge Cases / Constraints
34
+
35
+ OUTPUT:
36
+ Markdown only.
37
+ `;
38
+ return (0, openaiClient_1.askLLM)(prompt);
39
+ }
package/dist/cli.js ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const commander_1 = require("commander");
5
+ const generate_1 = require("./commands/generate");
6
+ const program = new commander_1.Command();
7
+ program
8
+ .name("ai-doc-agent")
9
+ .description("AI-powered documentation generator")
10
+ .version("0.1.0");
11
+ program
12
+ .command("generate")
13
+ .option("--since <commit>", "Only analyze changes since commit")
14
+ .option("--output <dir>", "Docs output directory", "docs")
15
+ .action(generate_1.generateDocs);
16
+ program.parse();
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.generateDocs = generateDocs;
7
+ const path_1 = __importDefault(require("path"));
8
+ const fileScanner_1 = require("../scanner/fileScanner");
9
+ const contentReader_1 = require("../scanner/contentReader");
10
+ const architecture_1 = require("../analyzers/architecture");
11
+ const modules_1 = require("../analyzers/modules");
12
+ const markdownWriter_1 = require("../writers/markdownWriter");
13
+ const gitUtils_1 = require("../git/gitUtils");
14
+ const moduleDetector_1 = require("../scanner/moduleDetector");
15
+ async function generateDocs(options) {
16
+ const root = process.cwd();
17
+ const files = options.since
18
+ ? (0, gitUtils_1.getChangedFiles)(options.since).map(f => path_1.default.join(root, f))
19
+ : await (0, fileScanner_1.scanProject)(root);
20
+ // ---------- Architecture ----------
21
+ const tree = files
22
+ .map(f => path_1.default.relative(root, f))
23
+ .join("\n");
24
+ const architectureSource = files
25
+ .slice(0, 25)
26
+ .map(f => `FILE: ${f}\n${(0, contentReader_1.readFile)(f)}`)
27
+ .join("\n\n");
28
+ const architecture = await (0, architecture_1.generateArchitectureDoc)(tree, architectureSource);
29
+ (0, markdownWriter_1.writeDoc)(options.output, "architecture.md", architecture);
30
+ // ---------- Modules ----------
31
+ const modules = (0, moduleDetector_1.detectModules)(files, root);
32
+ for (const [moduleName, moduleFiles] of Object.entries(modules)) {
33
+ const fileList = moduleFiles
34
+ .map(f => path_1.default.relative(root, f))
35
+ .join("\n");
36
+ const source = moduleFiles
37
+ .slice(0, 20)
38
+ .map(f => `FILE: ${f}\n${(0, contentReader_1.readFile)(f)}`)
39
+ .join("\n\n");
40
+ const doc = await (0, modules_1.generateModuleDocs)(moduleName, fileList, source);
41
+ (0, markdownWriter_1.writeDoc)(path_1.default.join(options.output, "modules"), `${moduleName}.md`, doc);
42
+ console.log(`📄 Module doc generated: ${moduleName}`);
43
+ }
44
+ console.log("✅ Docs generation completed");
45
+ }
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.env = void 0;
4
+ require("dotenv/config");
5
+ exports.env = {
6
+ openaiKey: process.env.OPENAI_API_KEY,
7
+ model: process.env.MODEL || "gpt-4.1-mini"
8
+ };
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getChangedFiles = getChangedFiles;
4
+ const child_process_1 = require("child_process");
5
+ function getChangedFiles(since) {
6
+ if (!since)
7
+ return [];
8
+ const output = (0, child_process_1.execSync)(`git diff --name-only ${since}`, { encoding: "utf-8" });
9
+ return output
10
+ .split("\n")
11
+ .filter(Boolean)
12
+ .filter(f => /\.(ts|tsx|js|jsx)$/.test(f));
13
+ }
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.openai = void 0;
7
+ exports.askLLM = askLLM;
8
+ const openai_1 = __importDefault(require("openai"));
9
+ const env_1 = require("../config/env");
10
+ exports.openai = new openai_1.default({
11
+ apiKey: env_1.env.openaiKey
12
+ });
13
+ async function askLLM(prompt) {
14
+ const res = await exports.openai.chat.completions.create({
15
+ model: env_1.env.model,
16
+ messages: [{ role: "user", content: prompt }]
17
+ });
18
+ return res.choices[0].message.content;
19
+ }
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.readFile = readFile;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ function readFile(file) {
9
+ return fs_1.default.readFileSync(file, "utf-8");
10
+ }
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.scanProject = scanProject;
7
+ const fast_glob_1 = __importDefault(require("fast-glob"));
8
+ async function scanProject(root) {
9
+ return (0, fast_glob_1.default)([
10
+ "**/*.{ts,tsx,js,jsx}",
11
+ "!**/node_modules/**",
12
+ "!**/dist/**"
13
+ ], {
14
+ cwd: root,
15
+ absolute: true
16
+ });
17
+ }
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.detectModules = detectModules;
7
+ const path_1 = __importDefault(require("path"));
8
+ function detectModules(files, root) {
9
+ const modules = {};
10
+ for (const file of files) {
11
+ const relative = path_1.default.relative(root, file);
12
+ const parts = relative.split(path_1.default.sep);
13
+ // src/<module>/...
14
+ const moduleName = parts[0] === "src" && parts[1]
15
+ ? parts[1]
16
+ : "shared";
17
+ if (!modules[moduleName]) {
18
+ modules[moduleName] = [];
19
+ }
20
+ modules[moduleName].push(file);
21
+ }
22
+ return modules;
23
+ }
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.writeDoc = writeDoc;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ function writeDoc(dir, fileName, content) {
10
+ fs_1.default.mkdirSync(dir, { recursive: true });
11
+ fs_1.default.writeFileSync(path_1.default.join(dir, fileName), content);
12
+ }
package/package.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "@khiem_enhance/ai-doc-agent",
3
+ "version": "0.1.0",
4
+ "description": "AI-powered documentation generator from source code",
5
+ "license": "MIT",
6
+ "bin": {
7
+ "ai-doc-agent": "dist/cli.js"
8
+ },
9
+ "main": "dist/cli.js",
10
+ "files": ["dist", "README.md", "LICENSE"],
11
+ "scripts": {
12
+ "build": "tsc",
13
+ "prepublishOnly": "npm run build"
14
+ },
15
+ "engines": {
16
+ "node": ">=18"
17
+ },
18
+ "dependencies": {
19
+ "commander": "^11.0.0",
20
+ "dotenv": "^16.0.0",
21
+ "fast-glob": "^3.3.0",
22
+ "openai": "^4.0.0"
23
+ }
24
+ }