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 +0 -0
- package/README.md +0 -0
- package/bin/index.js +23 -0
- package/cli/init.command.js +14 -0
- package/cli/sync.command.js +10 -0
- package/engine/architecture.loader.js +12 -0
- package/engine/architecture.seeder.js +30 -0
- package/engine/bloat.analyzer.js +71 -0
- package/engine/drift.detector.js +20 -0
- package/engine/repo.scanner.js +65 -0
- package/engine/structure.guardian.js +27 -0
- package/generators/leancode.generator.js +35 -0
- package/package.json +10 -0
- package/templates/AI_CONTEXT.base.md +5 -0
- package/templates/ARCHITECTURE.md +5 -0
- package/templates/LEANCODE.md +21 -0
- package/templates/RULES.md +7 -0
- package/utils/file.utils.js +10 -0
- package/utils/tree.utils.js +30 -0
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,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,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 };
|