leancode 1.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/LICENSE ADDED
File without changes
package/README.md ADDED
File without changes
package/bin/index.js ADDED
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env node
2
+
3
+ const init = require("../cli/init.command");
4
+ const sync = require("../cli/sync.command");
5
+
6
+ const command = process.argv[2];
7
+
8
+ switch (command) {
9
+ case "init":
10
+ init();
11
+ break;
12
+ case "sync":
13
+ sync();
14
+ break;
15
+ default:
16
+ console.log(`
17
+ LeanCode CLI
18
+
19
+ Commands:
20
+ leancode init Initialize LeanCode
21
+ leancode sync Refresh AI context
22
+ `);
23
+ }
@@ -0,0 +1,14 @@
1
+
2
+ const { generateLeanCode } = require("../generators/leancode.generator");
3
+ const { scanRepository } = require("../engine/repo.scanner");
4
+
5
+ module.exports = function init() {
6
+ console.log("⚡ Initializing LeanCode...");
7
+
8
+ const root = process.cwd();
9
+
10
+ generateLeanCode(root);
11
+ scanRepository(root);
12
+ // seedArchitecture(root); // Removed to prevent architecture.json creation
13
+ console.log(" LeanCode ready with AI context");
14
+ };
@@ -0,0 +1,10 @@
1
+ const { scanRepository } = require("../engine/repo.scanner");
2
+
3
+ module.exports = function sync() {
4
+ console.log(" Syncing AI context...");
5
+
6
+ const root = process.cwd();
7
+ scanRepository(root);
8
+ console.log(" Calculating bloat score...");
9
+ console.log(" AI context updated");
10
+ };
@@ -0,0 +1,12 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+
4
+ function loadArchitecture(root) {
5
+ const file = path.join(root, ".leancode/architecture.json");
6
+
7
+ if (!fs.existsSync(file)) return null;
8
+
9
+ return JSON.parse(fs.readFileSync(file, "utf8"));
10
+ }
11
+
12
+ module.exports = { loadArchitecture };
@@ -0,0 +1,30 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+
4
+ async function seedArchitecture(root) {
5
+ const dir = path.join(root, ".leancode");
6
+
7
+ if (!fs.existsSync(dir)) {
8
+ fs.mkdirSync(dir);
9
+ }
10
+
11
+ const configPath = path.join(dir, "architecture.json");
12
+
13
+ if (fs.existsSync(configPath)) return;
14
+
15
+ const architecture = {
16
+ version: 1,
17
+ mode: "learning",
18
+ structure: "auto",
19
+ rules: {
20
+ maxDepth: 3,
21
+ allowUtilsSprawl: false,
22
+ enforceDomains: false
23
+ },
24
+ domains: []
25
+ };
26
+
27
+ fs.writeFileSync(configPath, JSON.stringify(architecture, null, 2));
28
+ }
29
+
30
+ // module.exports = { seedArchitecture }; // Disabled to prevent architecture.json creation
@@ -0,0 +1,71 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+ const { loadArchitecture } = require("./architecture.loader");
4
+
5
+
6
+ const IGNORE = ["node_modules", ".git", ".leancode"];
7
+
8
+
9
+ function analyzeBloat(root, issues = []) {
10
+ let fileCount = 0;
11
+ let smallFiles = 0;
12
+ let maxDepth = 0;
13
+ let duplicateNames = {};
14
+ let duplicates = 0;
15
+
16
+ function walk(dir, depth = 0) {
17
+ maxDepth = Math.max(maxDepth, depth);
18
+
19
+ const files = fs.readdirSync(dir);
20
+
21
+ files.forEach((file) => {
22
+ if (IGNORE.includes(file)) return;
23
+
24
+ const full = path.join(dir, file);
25
+ const stat = fs.statSync(full);
26
+
27
+ if (stat.isDirectory()) {
28
+ walk(full, depth + 1);
29
+ } else {
30
+ fileCount++;
31
+
32
+ if (stat.size < 300) smallFiles++;
33
+
34
+ duplicateNames[file] =
35
+ (duplicateNames[file] || 0) + 1;
36
+ }
37
+ });
38
+ }
39
+
40
+ walk(root);
41
+
42
+ Object.values(duplicateNames).forEach((count) => {
43
+ if (count > 1) duplicates += count - 1;
44
+ });
45
+
46
+ // ---- Metrics ----
47
+
48
+ const smallFileRatio = smallFiles / (fileCount || 1);
49
+ const depthScore = Math.min(maxDepth / 10, 1);
50
+ const duplicateScore = duplicates / (fileCount || 1);
51
+ const densityScore = Math.min(fileCount / 150, 1);
52
+
53
+ const finalScore =
54
+ (smallFileRatio +
55
+ depthScore +
56
+ duplicateScore +
57
+ densityScore) /
58
+ 4;
59
+
60
+ return {
61
+ score: Math.round(finalScore * 100),
62
+ stats: {
63
+ fileCount,
64
+ smallFiles,
65
+ maxDepth,
66
+ duplicates,
67
+ },
68
+ };
69
+ }
70
+
71
+ module.exports = { analyzeBloat };
@@ -0,0 +1,20 @@
1
+ function detectDrift(files, architecture) {
2
+ const warnings = [];
3
+
4
+ const featureFolders = new Set(
5
+ files.map(f => f.split("/")[1])
6
+ );
7
+
8
+ featureFolders.forEach(folder => {
9
+ if (!architecture.domains.includes(folder)) {
10
+ warnings.push({
11
+ type: "drift",
12
+ message: `New domain emerging: ${folder}`
13
+ });
14
+ }
15
+ });
16
+
17
+ return warnings;
18
+ }
19
+
20
+ module.exports = { detectDrift };
@@ -0,0 +1,65 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+ const { buildTree } = require("../utils/tree.utils");
4
+ const { analyzeBloat } = require("./bloat.analyzer");
5
+ const { loadArchitecture } = require("./architecture.loader");
6
+ const { validateStructure } = require("./structure.guardian");
7
+
8
+
9
+
10
+ function scanRepository(root) {
11
+ const lcDir = path.join(root, ".leancode");
12
+
13
+ if (!fs.existsSync(lcDir)) {
14
+ console.log(" LeanCode not initialized");
15
+ return;
16
+ }
17
+
18
+ const tree = buildTree(root);
19
+ const bloat = analyzeBloat(root);
20
+ const architecture = loadArchitecture(root);
21
+ const summary = `
22
+ ## Repository Structure (Auto Generated)
23
+
24
+ \`\`\`
25
+ ${tree}
26
+ \`\`\`
27
+
28
+ ## Code Bloat Analysis
29
+
30
+ Bloat Score: **${bloat.score}%**
31
+
32
+ ### Metrics
33
+ - Files: ${bloat.stats.fileCount}
34
+ - Small Files: ${bloat.stats.smallFiles}
35
+ - Max Depth: ${bloat.stats.maxDepth}
36
+ - Duplicate Names: ${bloat.stats.duplicates}
37
+
38
+ ### Interpretation
39
+ ${
40
+ bloat.score < 30
41
+ ? "✅ Lean architecture"
42
+ : bloat.score < 60
43
+ ? "⚠️ Moderate complexity"
44
+ : "🚨 High code bloat detected"
45
+ }
46
+
47
+ ### AI Optimization Instructions
48
+ - Prefer modifying existing modules
49
+ - Reduce file fragmentation
50
+ - Merge tiny utilities when possible
51
+ `;
52
+
53
+ const baseTemplate = fs.readFileSync(
54
+ path.join(__dirname, "..", "templates", "AI_CONTEXT.base.md"),
55
+ "utf8"
56
+ );
57
+
58
+ fs.writeFileSync(
59
+ path.join(lcDir, "AI_CONTEXT.md"),
60
+ baseTemplate + summary
61
+
62
+ );
63
+ }
64
+
65
+ module.exports = { scanRepository };
@@ -0,0 +1,27 @@
1
+
2
+ function validateStructure(filePath, architecture) {
3
+ const warnings = [];
4
+
5
+ if (!architecture) return warnings;
6
+
7
+ // Rule 1: utils folder warning
8
+ if (filePath.includes("/utils/")) {
9
+ warnings.push({
10
+ type: "architecture",
11
+ message: "Utils folder detected — consider domain placement"
12
+ });
13
+ }
14
+
15
+ // Rule 2: deep nesting
16
+ const depth = filePath.split("/").length;
17
+ if (depth > architecture.rules.maxDepth + 3) {
18
+ warnings.push({
19
+ type: "architecture",
20
+ message: "File nesting too deep"
21
+ });
22
+ }
23
+
24
+ return warnings;
25
+ }
26
+
27
+ module.exports = { validateStructure };
@@ -0,0 +1,35 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+ const { copyTemplate } = require("../utils/file.utils");
4
+
5
+ function generateLeanCode(root) {
6
+ const lcDir = path.join(root, ".leancode");
7
+
8
+ // create .leancode folder
9
+ if (!fs.existsSync(lcDir)) {
10
+ fs.mkdirSync(lcDir);
11
+ }
12
+
13
+ const templates = path.join(__dirname, "..", "templates");
14
+
15
+ // ---- create internal rule files ----
16
+ copyTemplate(
17
+ path.join(templates, "ARCHITECTURE.md"),
18
+ path.join(lcDir, "ARCHITECTURE.md")
19
+ );
20
+
21
+ copyTemplate(
22
+ path.join(templates, "RULES.md"),
23
+ path.join(lcDir, "RULES.md")
24
+ );
25
+
26
+ // ---- create ROOT AI ENTRY FILE ---- ⭐ NEW
27
+ copyTemplate(
28
+ path.join(templates, "LEANCODE.md"),
29
+ path.join(root, "LEANCODE.md")
30
+ );
31
+
32
+ console.log("📁 LeanCode files created");
33
+ }
34
+
35
+ module.exports = { generateLeanCode };
package/package.json ADDED
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "leancode",
3
+ "version": "1.1.0",
4
+ "description": "LeanCode - AI anti code bloat framework",
5
+ "bin": {
6
+ "leancode": "./bin/index.js"
7
+ },
8
+ "type": "commonjs",
9
+ "license": "MIT"
10
+ }
@@ -0,0 +1,5 @@
1
+ # LeanCode AI Context
2
+
3
+ This repository uses LeanCode anti-bloat framework.
4
+
5
+ AI must analyze project structure before generating code.
@@ -0,0 +1,5 @@
1
+ # Architecture Guidelines
2
+
3
+ - Prefer reuse over creation
4
+ - Maintain shallow folder depth
5
+ - Single responsibility modules
@@ -0,0 +1,21 @@
1
+ # LeanCode Enabled Repository
2
+
3
+ This repository uses the LeanCode framework to reduce code bloat
4
+ and maintain clean architecture.
5
+
6
+ ## Instructions for AI Assistants
7
+
8
+ Before generating or modifying code, ALWAYS follow:
9
+
10
+ - `.leancode/AI_CONTEXT.md`
11
+ - `.leancode/ARCHITECTURE.md`
12
+ - `.leancode/RULES.md`
13
+
14
+ ## Core Principles
15
+
16
+ - Prefer modifying existing files over creating new ones
17
+ - Avoid duplicate logic
18
+ - Maintain minimal architecture
19
+ - Respect current project structure
20
+
21
+ LeanCode ensures AI-generated code remains lean and maintainable.
@@ -0,0 +1,7 @@
1
+ # LeanCode Rules
2
+
3
+ AI must:
4
+
5
+ - Modify existing files first
6
+ - Avoid duplicate logic
7
+ - Reduce unnecessary abstractions
@@ -0,0 +1,10 @@
1
+ const fs = require("fs");
2
+
3
+ function copyTemplate(src, dest) {
4
+ if (fs.existsSync(dest)) return;
5
+
6
+ const content = fs.readFileSync(src, "utf8");
7
+ fs.writeFileSync(dest, content);
8
+ }
9
+
10
+ module.exports = { copyTemplate };
@@ -0,0 +1,30 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+
4
+ const IGNORE = ["node_modules", ".git", ".leancode"];
5
+
6
+ function buildTree(dir, prefix = "") {
7
+ let output = "";
8
+
9
+ const files = fs.readdirSync(dir);
10
+
11
+ files.forEach((file, index) => {
12
+ if (IGNORE.includes(file)) return;
13
+
14
+ const full = path.join(dir, file);
15
+ const isLast = index === files.length - 1;
16
+
17
+ output += `${prefix}${isLast ? "└──" : "├──"} ${file}\n`;
18
+
19
+ if (fs.statSync(full).isDirectory()) {
20
+ output += buildTree(
21
+ full,
22
+ prefix + (isLast ? " " : "│ ")
23
+ );
24
+ }
25
+ });
26
+
27
+ return output;
28
+ }
29
+
30
+ module.exports = { buildTree };