scaffoldrite 2.0.4 → 2.0.6
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/package.json +1 -1
- package/dist/ast.js +0 -2
- package/dist/commandHandler.js +0 -632
- package/dist/constraints.js +0 -183
- package/dist/fsToAst.js +0 -47
- package/dist/generator.js +0 -152
- package/dist/history.js +0 -21
- package/dist/library/ai/ait.js +0 -129
- package/dist/library/ai/generateStructure.js +0 -28
- package/dist/library/ai/generateStructureWithGroq.js +0 -75
- package/dist/library/checkpage/checkPackageType.js +0 -25
- package/dist/library/sam.js +0 -2
- package/dist/parser.js +0 -103
- package/dist/progress.js +0 -63
- package/dist/structure.js +0 -84
- package/dist/utils/historyhelpers.js +0 -1
- package/dist/validateFS.js +0 -74
- package/dist/validator.js +0 -249
- package/dist/visitor.js +0 -22
package/dist/constraints.js
DELETED
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.parseConstraints = parseConstraints;
|
|
4
|
-
function splitArgs(line) {
|
|
5
|
-
const args = [];
|
|
6
|
-
let current = "";
|
|
7
|
-
let inQuotes = false;
|
|
8
|
-
for (let i = 0; i < line.length; i++) {
|
|
9
|
-
const ch = line[i];
|
|
10
|
-
if (ch === '"' || ch === "'") {
|
|
11
|
-
inQuotes = !inQuotes;
|
|
12
|
-
continue;
|
|
13
|
-
}
|
|
14
|
-
if (ch === " " && !inQuotes) {
|
|
15
|
-
if (current.length > 0) {
|
|
16
|
-
args.push(current);
|
|
17
|
-
current = "";
|
|
18
|
-
}
|
|
19
|
-
continue;
|
|
20
|
-
}
|
|
21
|
-
current += ch;
|
|
22
|
-
}
|
|
23
|
-
if (current.length > 0)
|
|
24
|
-
args.push(current);
|
|
25
|
-
return args;
|
|
26
|
-
}
|
|
27
|
-
function parseConstraints(input) {
|
|
28
|
-
const lines = input.split("\n");
|
|
29
|
-
const constraints = [];
|
|
30
|
-
for (let line of lines) {
|
|
31
|
-
line = line.trim();
|
|
32
|
-
if (!line)
|
|
33
|
-
continue;
|
|
34
|
-
if (line.startsWith("require ")) {
|
|
35
|
-
const path = line.replace("require ", "").trim();
|
|
36
|
-
constraints.push({ type: "require", path });
|
|
37
|
-
continue;
|
|
38
|
-
}
|
|
39
|
-
if (line.startsWith("forbid ")) {
|
|
40
|
-
const path = line.replace("forbid ", "").trim();
|
|
41
|
-
constraints.push({ type: "forbid", path });
|
|
42
|
-
continue;
|
|
43
|
-
}
|
|
44
|
-
if (line.startsWith("maxFilesByExtRecursive ")) {
|
|
45
|
-
const parts = splitArgs(line);
|
|
46
|
-
const ext = parts[1];
|
|
47
|
-
const value = Number(parts[2]);
|
|
48
|
-
const path = parts[3];
|
|
49
|
-
constraints.push({ type: "maxFilesByExtRecursive", path, value, ext });
|
|
50
|
-
continue;
|
|
51
|
-
}
|
|
52
|
-
if (line.startsWith("maxFilesRecursive ")) {
|
|
53
|
-
const parts = splitArgs(line);
|
|
54
|
-
const value = Number(parts[1]);
|
|
55
|
-
const path = parts[2];
|
|
56
|
-
constraints.push({ type: "maxFilesRecursive", path, value });
|
|
57
|
-
continue;
|
|
58
|
-
}
|
|
59
|
-
if (line.startsWith("maxFilesByExt ")) {
|
|
60
|
-
const parts = splitArgs(line);
|
|
61
|
-
const ext = parts[1];
|
|
62
|
-
const value = Number(parts[2]);
|
|
63
|
-
const path = parts[3];
|
|
64
|
-
constraints.push({ type: "maxFilesByExt", path, value, ext });
|
|
65
|
-
continue;
|
|
66
|
-
}
|
|
67
|
-
if (line.startsWith("maxFiles ")) {
|
|
68
|
-
const parts = splitArgs(line);
|
|
69
|
-
const value = Number(parts[1]);
|
|
70
|
-
const path = parts[2];
|
|
71
|
-
constraints.push({ type: "maxFiles", path, value });
|
|
72
|
-
continue;
|
|
73
|
-
}
|
|
74
|
-
if (line.startsWith("maxFoldersRecursive ")) {
|
|
75
|
-
const parts = splitArgs(line);
|
|
76
|
-
const value = Number(parts[1]);
|
|
77
|
-
const path = parts[2];
|
|
78
|
-
constraints.push({ type: "maxFoldersRecursive", path, value });
|
|
79
|
-
continue;
|
|
80
|
-
}
|
|
81
|
-
if (line.startsWith("maxFolders ")) {
|
|
82
|
-
const parts = splitArgs(line);
|
|
83
|
-
const value = Number(parts[1]);
|
|
84
|
-
const path = parts[2];
|
|
85
|
-
constraints.push({ type: "maxFolders", path, value });
|
|
86
|
-
continue;
|
|
87
|
-
}
|
|
88
|
-
if (line.startsWith("minFiles ")) {
|
|
89
|
-
const parts = splitArgs(line);
|
|
90
|
-
const value = Number(parts[1]);
|
|
91
|
-
const path = parts[2];
|
|
92
|
-
constraints.push({ type: "minFiles", path, value });
|
|
93
|
-
continue;
|
|
94
|
-
}
|
|
95
|
-
if (line.startsWith("minFolders ")) {
|
|
96
|
-
const parts = splitArgs(line);
|
|
97
|
-
const value = Number(parts[1]);
|
|
98
|
-
const path = parts[2];
|
|
99
|
-
constraints.push({ type: "minFolders", path, value });
|
|
100
|
-
continue;
|
|
101
|
-
}
|
|
102
|
-
// NEW RULES PARSING (WITH SCOPE)
|
|
103
|
-
if (line.startsWith("eachFolderMustContain ")) {
|
|
104
|
-
const parts = splitArgs(line);
|
|
105
|
-
const scope = parts[1];
|
|
106
|
-
if (scope !== "*" && scope !== "**") {
|
|
107
|
-
throw new Error(`Invalid scope for eachFolderMustContain: ${scope}`);
|
|
108
|
-
}
|
|
109
|
-
const hasPath = parts.length === 4;
|
|
110
|
-
const path = hasPath ? parts[2] : "";
|
|
111
|
-
const value = hasPath ? parts[3] : parts[2];
|
|
112
|
-
constraints.push({ type: "eachFolderMustContain", path, value, scope });
|
|
113
|
-
continue;
|
|
114
|
-
}
|
|
115
|
-
if (line.startsWith("eachFolderMustContainFile ")) {
|
|
116
|
-
const parts = splitArgs(line);
|
|
117
|
-
const scope = parts[1];
|
|
118
|
-
if (scope !== "*" && scope !== "**") {
|
|
119
|
-
throw new Error(`Invalid scope for eachFolderMustContainFile: ${scope}`);
|
|
120
|
-
}
|
|
121
|
-
const hasPath = parts.length === 4;
|
|
122
|
-
const path = hasPath ? parts[2] : "";
|
|
123
|
-
const value = hasPath ? parts[3] : parts[2];
|
|
124
|
-
constraints.push({ type: "eachFolderMustContainFile", path, value, scope });
|
|
125
|
-
continue;
|
|
126
|
-
}
|
|
127
|
-
if (line.startsWith("eachFolderMustContainFolder ")) {
|
|
128
|
-
const parts = splitArgs(line);
|
|
129
|
-
const scope = parts[1];
|
|
130
|
-
if (scope !== "*" && scope !== "**") {
|
|
131
|
-
throw new Error(`Invalid scope for eachFolderMustContainFolder: ${scope}`);
|
|
132
|
-
}
|
|
133
|
-
const hasPath = parts.length === 4;
|
|
134
|
-
const path = hasPath ? parts[2] : "";
|
|
135
|
-
const value = hasPath ? parts[3] : parts[2];
|
|
136
|
-
constraints.push({ type: "eachFolderMustContainFolder", path, value, scope });
|
|
137
|
-
continue;
|
|
138
|
-
}
|
|
139
|
-
if (line.startsWith("eachFolderMustHaveExt ")) {
|
|
140
|
-
const parts = splitArgs(line);
|
|
141
|
-
const scope = parts[1];
|
|
142
|
-
if (scope !== "*" && scope !== "**") {
|
|
143
|
-
throw new Error(`Invalid scope for eachFolderMustHaveExt: ${scope}`);
|
|
144
|
-
}
|
|
145
|
-
const hasPath = parts.length === 4;
|
|
146
|
-
const path = hasPath ? parts[2] : "";
|
|
147
|
-
const ext = hasPath ? parts[3] : parts[2];
|
|
148
|
-
constraints.push({ type: "eachFolderMustHaveExt", path, ext, scope });
|
|
149
|
-
continue;
|
|
150
|
-
}
|
|
151
|
-
// EXISTING RULES
|
|
152
|
-
if (line.startsWith("mustContain ")) {
|
|
153
|
-
const parts = splitArgs(line);
|
|
154
|
-
const path = parts[1];
|
|
155
|
-
const value = parts[2];
|
|
156
|
-
constraints.push({ type: "mustContain", path, value });
|
|
157
|
-
continue;
|
|
158
|
-
}
|
|
159
|
-
if (line.startsWith("fileNameRegex ")) {
|
|
160
|
-
const parts = splitArgs(line);
|
|
161
|
-
const path = parts[1];
|
|
162
|
-
const regex = parts[2];
|
|
163
|
-
constraints.push({ type: "fileNameRegex", path, regex });
|
|
164
|
-
continue;
|
|
165
|
-
}
|
|
166
|
-
if (line.startsWith("maxDepth ")) {
|
|
167
|
-
const parts = splitArgs(line);
|
|
168
|
-
const value = Number(parts[1]);
|
|
169
|
-
const path = parts[2];
|
|
170
|
-
constraints.push({ type: "maxDepth", path, value });
|
|
171
|
-
continue;
|
|
172
|
-
}
|
|
173
|
-
if (line.startsWith("mustHaveFile ")) {
|
|
174
|
-
const parts = splitArgs(line);
|
|
175
|
-
const path = parts[1];
|
|
176
|
-
const value = parts[2];
|
|
177
|
-
constraints.push({ type: "mustHaveFile", path, value });
|
|
178
|
-
continue;
|
|
179
|
-
}
|
|
180
|
-
throw new Error(`Unknown constraint: ${line}`);
|
|
181
|
-
}
|
|
182
|
-
return constraints;
|
|
183
|
-
}
|
package/dist/fsToAst.js
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
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.buildASTFromFS = buildASTFromFS;
|
|
7
|
-
const fs_1 = __importDefault(require("fs"));
|
|
8
|
-
const path_1 = __importDefault(require("path"));
|
|
9
|
-
function buildASTFromFS(dir, ignoreList = []) {
|
|
10
|
-
if (!fs_1.default.existsSync(dir)) {
|
|
11
|
-
throw new Error(`Directory does not exist: ${dir}`);
|
|
12
|
-
}
|
|
13
|
-
const root = {
|
|
14
|
-
type: "folder",
|
|
15
|
-
name: path_1.default.basename(dir),
|
|
16
|
-
children: [],
|
|
17
|
-
};
|
|
18
|
-
function scan(folderPath, node) {
|
|
19
|
-
const items = fs_1.default.readdirSync(folderPath);
|
|
20
|
-
for (const item of items) {
|
|
21
|
-
if (ignoreList.includes(item))
|
|
22
|
-
continue;
|
|
23
|
-
const itemPath = path_1.default.join(folderPath, item);
|
|
24
|
-
if (itemPath.startsWith(path_1.default.join(dir, ".scaffoldrite/history")))
|
|
25
|
-
continue;
|
|
26
|
-
const stat = fs_1.default.statSync(itemPath);
|
|
27
|
-
if (stat.isDirectory()) {
|
|
28
|
-
const childFolder = {
|
|
29
|
-
type: "folder",
|
|
30
|
-
name: item,
|
|
31
|
-
children: [],
|
|
32
|
-
};
|
|
33
|
-
node.children.push(childFolder);
|
|
34
|
-
scan(itemPath, childFolder);
|
|
35
|
-
}
|
|
36
|
-
else {
|
|
37
|
-
const childFile = {
|
|
38
|
-
type: "file",
|
|
39
|
-
name: item,
|
|
40
|
-
};
|
|
41
|
-
node.children.push(childFile);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
scan(dir, root);
|
|
46
|
-
return root;
|
|
47
|
-
}
|
package/dist/generator.js
DELETED
|
@@ -1,152 +0,0 @@
|
|
|
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.generateFS = generateFS;
|
|
7
|
-
const promises_1 = __importDefault(require("fs/promises"));
|
|
8
|
-
const path_1 = __importDefault(require("path"));
|
|
9
|
-
const visitor_1 = require("./visitor");
|
|
10
|
-
// Helper function to check if file exists
|
|
11
|
-
async function fileExists(filePath) {
|
|
12
|
-
try {
|
|
13
|
-
await promises_1.default.access(filePath);
|
|
14
|
-
return true;
|
|
15
|
-
}
|
|
16
|
-
catch {
|
|
17
|
-
return false;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
async function generateFS(ast, outputDir, options) {
|
|
21
|
-
const root = path_1.default.resolve(outputDir);
|
|
22
|
-
const sourceRoot = process.cwd(); // Get source directory for copying
|
|
23
|
-
const ignoreList = options?.ignoreList ?? [];
|
|
24
|
-
const copyContents = options?.copyContents ?? false;
|
|
25
|
-
// ✅ store type info
|
|
26
|
-
const expected = new Map();
|
|
27
|
-
const actual = new Set();
|
|
28
|
-
const ops = [];
|
|
29
|
-
/* Helper: check if path is ignored */
|
|
30
|
-
const isIgnored = (p) => {
|
|
31
|
-
const name = path_1.default.basename(p);
|
|
32
|
-
return ignoreList.includes(name);
|
|
33
|
-
};
|
|
34
|
-
/* 1️⃣ EXPECTED */
|
|
35
|
-
expected.set(root, { type: "folder" });
|
|
36
|
-
// Track source paths for files when copyContents is enabled
|
|
37
|
-
const fileSourcePaths = new Map();
|
|
38
|
-
await (0, visitor_1.visit)(ast, {
|
|
39
|
-
folder: async (_, nodePath) => {
|
|
40
|
-
const fullPath = path_1.default.join(root, nodePath);
|
|
41
|
-
if (!isIgnored(fullPath)) {
|
|
42
|
-
expected.set(fullPath, { type: "folder" });
|
|
43
|
-
}
|
|
44
|
-
},
|
|
45
|
-
file: async (_, nodePath) => {
|
|
46
|
-
const fullPath = path_1.default.join(root, nodePath);
|
|
47
|
-
if (!isIgnored(fullPath)) {
|
|
48
|
-
// Store source path for potential copying
|
|
49
|
-
const sourcePath = path_1.default.join(sourceRoot, nodePath);
|
|
50
|
-
expected.set(fullPath, {
|
|
51
|
-
type: "file",
|
|
52
|
-
sourcePath: sourcePath
|
|
53
|
-
});
|
|
54
|
-
fileSourcePaths.set(fullPath, sourcePath);
|
|
55
|
-
}
|
|
56
|
-
},
|
|
57
|
-
});
|
|
58
|
-
/* 2️⃣ ACTUAL */
|
|
59
|
-
async function scan(dir) {
|
|
60
|
-
if (isIgnored(dir))
|
|
61
|
-
return;
|
|
62
|
-
actual.add(dir);
|
|
63
|
-
const entries = await promises_1.default.readdir(dir, { withFileTypes: true });
|
|
64
|
-
for (const e of entries) {
|
|
65
|
-
const full = path_1.default.join(dir, e.name);
|
|
66
|
-
if (isIgnored(full))
|
|
67
|
-
continue;
|
|
68
|
-
actual.add(full);
|
|
69
|
-
if (e.isDirectory())
|
|
70
|
-
await scan(full);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
try {
|
|
74
|
-
await scan(root);
|
|
75
|
-
}
|
|
76
|
-
catch { }
|
|
77
|
-
/* 3️⃣ PLAN ops (NO FS MUTATION) */
|
|
78
|
-
for (const [p, info] of expected.entries()) {
|
|
79
|
-
if (actual.has(p)) {
|
|
80
|
-
ops.push({ type: "skip", path: p, count: 0 });
|
|
81
|
-
}
|
|
82
|
-
else {
|
|
83
|
-
// Determine operation type
|
|
84
|
-
let operationType = info.type;
|
|
85
|
-
// Check if we should copy contents for this file
|
|
86
|
-
if (info.type === "file" && copyContents && info.sourcePath) {
|
|
87
|
-
const sourceExists = await fileExists(info.sourcePath);
|
|
88
|
-
if (sourceExists) {
|
|
89
|
-
operationType = "copy";
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
ops.push({
|
|
93
|
-
type: operationType,
|
|
94
|
-
path: p,
|
|
95
|
-
count: 0,
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
const extras = [...actual]
|
|
100
|
-
.filter((p) => !expected.has(p))
|
|
101
|
-
.filter((p) => !p.startsWith(path_1.default.join(root, ".scaffoldrite/history")))
|
|
102
|
-
.sort((a, b) => b.length - a.length);
|
|
103
|
-
for (const p of extras) {
|
|
104
|
-
ops.push({ type: "delete", path: p, count: 0 });
|
|
105
|
-
}
|
|
106
|
-
/* 4️⃣ START */
|
|
107
|
-
options?.onStart?.(ops.length);
|
|
108
|
-
/* 5️⃣ APPLY */
|
|
109
|
-
let count = 0;
|
|
110
|
-
for (const op of ops) {
|
|
111
|
-
count++;
|
|
112
|
-
if (!options?.dryRun) {
|
|
113
|
-
if (op.type === "folder") {
|
|
114
|
-
await promises_1.default.mkdir(op.path, { recursive: true });
|
|
115
|
-
}
|
|
116
|
-
else if (op.type === "file") {
|
|
117
|
-
await promises_1.default.mkdir(path_1.default.dirname(op.path), { recursive: true });
|
|
118
|
-
await promises_1.default.writeFile(op.path, "");
|
|
119
|
-
}
|
|
120
|
-
else if (op.type === "copy") {
|
|
121
|
-
// Get source path from expected map
|
|
122
|
-
const info = expected.get(op.path);
|
|
123
|
-
if (info?.sourcePath) {
|
|
124
|
-
try {
|
|
125
|
-
await promises_1.default.mkdir(path_1.default.dirname(op.path), { recursive: true });
|
|
126
|
-
await promises_1.default.copyFile(info.sourcePath, op.path);
|
|
127
|
-
}
|
|
128
|
-
catch (error) {
|
|
129
|
-
// If copy fails, fall back to empty file
|
|
130
|
-
console.warn(`Warning: Could not copy ${info.sourcePath} to ${op.path}, creating empty file instead`);
|
|
131
|
-
await promises_1.default.writeFile(op.path, "");
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
else {
|
|
135
|
-
// Fallback to empty file if no source path
|
|
136
|
-
await promises_1.default.mkdir(path_1.default.dirname(op.path), { recursive: true });
|
|
137
|
-
await promises_1.default.writeFile(op.path, "");
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
else if (op.type === "delete") {
|
|
141
|
-
// KEEP THE ORIGINAL DELETE LOGIC
|
|
142
|
-
if (op.path.startsWith(path_1.default.join(root, ".scaffoldrite/history")))
|
|
143
|
-
continue;
|
|
144
|
-
await promises_1.default.rm(op.path, { recursive: true, force: true });
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
options?.onProgress?.({
|
|
148
|
-
...op,
|
|
149
|
-
count,
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
}
|
package/dist/history.js
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
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.ensureHistoryDir = ensureHistoryDir;
|
|
7
|
-
exports.writeHistory = writeHistory;
|
|
8
|
-
const utils_1 = require("./utils");
|
|
9
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
10
|
-
const fs_1 = __importDefault(require("fs"));
|
|
11
|
-
const HISTORY_DIR = node_path_1.default.join(utils_1.SCAFFOLDRITE_DIR, "history");
|
|
12
|
-
function ensureHistoryDir() {
|
|
13
|
-
if (!fs_1.default.existsSync(HISTORY_DIR)) {
|
|
14
|
-
fs_1.default.mkdirSync(HISTORY_DIR, { recursive: true });
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
function writeHistory(entry) {
|
|
18
|
-
ensureHistoryDir();
|
|
19
|
-
const filename = `${entry.id}-${entry.command}.json`;
|
|
20
|
-
fs_1.default.writeFileSync(node_path_1.default.join(HISTORY_DIR, filename), JSON.stringify(entry, null, 2));
|
|
21
|
-
}
|
package/dist/library/ai/ait.js
DELETED
|
@@ -1,129 +0,0 @@
|
|
|
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.ai = void 0;
|
|
7
|
-
const child_process_1 = require("child_process");
|
|
8
|
-
const data_1 = require("../../data");
|
|
9
|
-
const path_1 = __importDefault(require("path"));
|
|
10
|
-
const fs_1 = __importDefault(require("fs"));
|
|
11
|
-
const generateStructure_1 = require("./generateStructure");
|
|
12
|
-
// ─────────────────────────────────────────────
|
|
13
|
-
// HELPER: readline wrapper
|
|
14
|
-
// ─────────────────────────────────────────────
|
|
15
|
-
function createAsk() {
|
|
16
|
-
const rl = require("readline").createInterface({
|
|
17
|
-
input: process.stdin,
|
|
18
|
-
output: process.stdout,
|
|
19
|
-
});
|
|
20
|
-
const ask = (q) => new Promise((res) => rl.question(q, res));
|
|
21
|
-
return { ask, close: () => rl.close() };
|
|
22
|
-
}
|
|
23
|
-
// ─────────────────────────────────────────────
|
|
24
|
-
// HELPER: sanitize AI output for ScaffoldRite
|
|
25
|
-
// ─────────────────────────────────────────────
|
|
26
|
-
function sanitizeStructureSR(input) {
|
|
27
|
-
return input
|
|
28
|
-
.split("\n")
|
|
29
|
-
.filter((line) => !line.match(/^\s*(STRUCTURE|FORMAT|SYNTAX|RULES)/i))
|
|
30
|
-
.join("\n")
|
|
31
|
-
.trim();
|
|
32
|
-
}
|
|
33
|
-
// ─────────────────────────────────────────────
|
|
34
|
-
// MAIN AI FUNCTION
|
|
35
|
-
// ─────────────────────────────────────────────
|
|
36
|
-
const ai = async () => {
|
|
37
|
-
try {
|
|
38
|
-
// ─────────────────────────────────────────────
|
|
39
|
-
// 1️⃣ INITIAL QUESTIONS
|
|
40
|
-
// ─────────────────────────────────────────────
|
|
41
|
-
let { ask, close } = createAsk();
|
|
42
|
-
const projectName = await ask("Project name: ");
|
|
43
|
-
const framework = await ask("Framework (react, vue, vanilla): ");
|
|
44
|
-
const language = await ask("Language (js, ts): ");
|
|
45
|
-
close(); // ❗ CLOSE BEFORE execSync
|
|
46
|
-
// ─────────────────────────────────────────────
|
|
47
|
-
// 2️⃣ CREATE VITE PROJECT
|
|
48
|
-
// ─────────────────────────────────────────────
|
|
49
|
-
let template = framework.toLowerCase();
|
|
50
|
-
if (framework === "react" && language === "ts")
|
|
51
|
-
template = "react-ts";
|
|
52
|
-
if (framework === "vue" && language === "ts")
|
|
53
|
-
template = "vue-ts";
|
|
54
|
-
if (framework === "vanilla" && language === "ts")
|
|
55
|
-
template = "vanilla-ts";
|
|
56
|
-
(0, child_process_1.execSync)(`npx create-vite@latest "${projectName}" --template ${template} --no-rolldown --no-immediate`, {
|
|
57
|
-
stdio: "inherit",
|
|
58
|
-
shell: process.platform === "win32" ? "cmd.exe" : "/bin/sh",
|
|
59
|
-
});
|
|
60
|
-
const projectPath = path_1.default.resolve(process.cwd(), projectName);
|
|
61
|
-
// ─────────────────────────────────────────────
|
|
62
|
-
// 3️⃣ INIT SCAFFOLDRITE
|
|
63
|
-
// ─────────────────────────────────────────────
|
|
64
|
-
(0, child_process_1.execSync)("sr init --from-fs .", {
|
|
65
|
-
cwd: projectPath,
|
|
66
|
-
stdio: "inherit",
|
|
67
|
-
shell: process.platform === "win32" ? "cmd.exe" : "/bin/sh",
|
|
68
|
-
});
|
|
69
|
-
// ─────────────────────────────────────────────
|
|
70
|
-
// 4️⃣ ASK FOR AI ASSISTANCE
|
|
71
|
-
// ─────────────────────────────────────────────
|
|
72
|
-
({ ask, close } = createAsk());
|
|
73
|
-
const wantAI = await ask("\n🤖 Do you want AI assistance in scaffolding the structure of your app? (yes/no): ");
|
|
74
|
-
if (wantAI.toLowerCase() === "yes") {
|
|
75
|
-
while (true) {
|
|
76
|
-
const description = await ask("\n📝 Describe your project or what you want to add/change:\n");
|
|
77
|
-
const structurePath = path_1.default.join(projectPath, ".scaffoldrite", "structure.sr");
|
|
78
|
-
const existingStructure = fs_1.default.readFileSync(structurePath, "utf-8");
|
|
79
|
-
const result = await (0, generateStructure_1.generateStructure)({
|
|
80
|
-
existingStructure,
|
|
81
|
-
description,
|
|
82
|
-
});
|
|
83
|
-
// 🧠 CLARIFICATION MODE
|
|
84
|
-
if (result.startsWith("CLARIFICATION_REQUIRED")) {
|
|
85
|
-
console.log("\n🤖 I need clarification:\n");
|
|
86
|
-
console.log(result);
|
|
87
|
-
const confirm = await ask("\nIs this what you meant? (yes/no): ");
|
|
88
|
-
if (confirm.toLowerCase() === "yes") {
|
|
89
|
-
await ask("\n✏️ Please rephrase clearly what you want:\n");
|
|
90
|
-
continue;
|
|
91
|
-
}
|
|
92
|
-
else {
|
|
93
|
-
console.log("\n🔁 Okay, please describe what you want again.\n");
|
|
94
|
-
continue;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
// ✅ STRUCTURE MODE
|
|
98
|
-
const clean = sanitizeStructureSR(result);
|
|
99
|
-
fs_1.default.writeFileSync(structurePath, clean);
|
|
100
|
-
// Generate the project structure
|
|
101
|
-
(0, child_process_1.execSync)("sr generate .", {
|
|
102
|
-
cwd: projectPath,
|
|
103
|
-
stdio: "inherit",
|
|
104
|
-
});
|
|
105
|
-
(0, child_process_1.execSync)("sr list --sr --with-icon", {
|
|
106
|
-
cwd: projectPath,
|
|
107
|
-
stdio: "inherit",
|
|
108
|
-
});
|
|
109
|
-
// Ask if satisfied
|
|
110
|
-
({ ask, close } = createAsk());
|
|
111
|
-
const satisfied = await ask("\n✅ Are you satisfied with the structure? (yes/no): ");
|
|
112
|
-
if (satisfied.toLowerCase() === "yes")
|
|
113
|
-
break;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
close();
|
|
117
|
-
// ─────────────────────────────────────────────
|
|
118
|
-
// 5️⃣ FINISH
|
|
119
|
-
// ─────────────────────────────────────────────
|
|
120
|
-
console.log(data_1.theme.success(`\n🎉 Project ${projectName} is ready!\n`));
|
|
121
|
-
console.log(data_1.theme.muted(` cd ${projectName}`));
|
|
122
|
-
console.log(data_1.theme.muted(" npm install"));
|
|
123
|
-
console.log(data_1.theme.muted(" npm run dev"));
|
|
124
|
-
}
|
|
125
|
-
catch (err) {
|
|
126
|
-
console.error(data_1.theme.error(`❌ Failed: ${err.message}`));
|
|
127
|
-
}
|
|
128
|
-
};
|
|
129
|
-
exports.ai = ai;
|
|
@@ -1,28 +0,0 @@
|
|
|
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.generateStructure = generateStructure;
|
|
7
|
-
const node_fetch_1 = __importDefault(require("node-fetch"));
|
|
8
|
-
async function generateStructure(params) {
|
|
9
|
-
try {
|
|
10
|
-
const response = await (0, node_fetch_1.default)("http://localhost:3000/generate-structure", {
|
|
11
|
-
method: "POST",
|
|
12
|
-
headers: { "Content-Type": "application/json" },
|
|
13
|
-
body: JSON.stringify(params),
|
|
14
|
-
});
|
|
15
|
-
if (!response.ok) {
|
|
16
|
-
const text = await response.text();
|
|
17
|
-
throw new Error(`Backend error: ${text}`);
|
|
18
|
-
}
|
|
19
|
-
const data = await response.json();
|
|
20
|
-
if (typeof data?.result !== "string") {
|
|
21
|
-
throw new Error("Invalid backend response: result missing");
|
|
22
|
-
}
|
|
23
|
-
return data.result;
|
|
24
|
-
}
|
|
25
|
-
catch (err) {
|
|
26
|
-
throw new Error(`Failed to generate structure: ${err.message}`);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
@@ -1,75 +0,0 @@
|
|
|
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.generateStructureWithGroq = generateStructureWithGroq;
|
|
7
|
-
const node_fetch_1 = __importDefault(require("node-fetch"));
|
|
8
|
-
async function generateStructureWithGroq(params) {
|
|
9
|
-
const { existingStructure, description } = params;
|
|
10
|
-
const GROQ_API_KEY = process.env.GROQ_API_KEY;
|
|
11
|
-
if (!GROQ_API_KEY) {
|
|
12
|
-
console.error("❌ GROQ_API_KEY is missing.");
|
|
13
|
-
console.error("➡️ Add it to your .env file:");
|
|
14
|
-
console.error(" GROQ_API_KEY=your_key_here");
|
|
15
|
-
process.exit(1);
|
|
16
|
-
}
|
|
17
|
-
const systemPrompt = `
|
|
18
|
-
You are a frontend project structure generator for Scaffoldrite.
|
|
19
|
-
|
|
20
|
-
STRICT RULES:
|
|
21
|
-
- Output ONLY valid structure.sr syntax
|
|
22
|
-
- DO NOT include markdown
|
|
23
|
-
- DO NOT explain anything
|
|
24
|
-
- DO NOT add comments
|
|
25
|
-
- DO NOT wrap output in code blocks
|
|
26
|
-
- Preserve all existing files and folders
|
|
27
|
-
- Only ADD or EXTEND structure where necessary
|
|
28
|
-
- NEVER delete existing entries
|
|
29
|
-
- Frontend-only structure
|
|
30
|
-
- Ignore backend, database, server, API implementation details
|
|
31
|
-
- Backend mentions should be interpreted as frontend needs
|
|
32
|
-
- Valid entities: folder, file
|
|
33
|
-
- Maintain correct indentation
|
|
34
|
-
`;
|
|
35
|
-
const userPrompt = `
|
|
36
|
-
EXISTING STRUCTURE:
|
|
37
|
-
${existingStructure}
|
|
38
|
-
|
|
39
|
-
PROJECT DESCRIPTION:
|
|
40
|
-
${description}
|
|
41
|
-
|
|
42
|
-
TASK:
|
|
43
|
-
Update the existing structure to satisfy the description.
|
|
44
|
-
`;
|
|
45
|
-
const response = await (0, node_fetch_1.default)("https://api.groq.com/openai/v1/chat/completions", {
|
|
46
|
-
method: "POST",
|
|
47
|
-
headers: {
|
|
48
|
-
"Authorization": `Bearer ${GROQ_API_KEY}`,
|
|
49
|
-
"Content-Type": "application/json",
|
|
50
|
-
},
|
|
51
|
-
body: JSON.stringify({
|
|
52
|
-
model: "llama-3.1-70b-versatile",
|
|
53
|
-
temperature: 0.2,
|
|
54
|
-
max_tokens: 1500,
|
|
55
|
-
messages: [
|
|
56
|
-
{ role: "system", content: systemPrompt },
|
|
57
|
-
{ role: "user", content: userPrompt },
|
|
58
|
-
],
|
|
59
|
-
}),
|
|
60
|
-
});
|
|
61
|
-
if (!response.ok) {
|
|
62
|
-
const text = await response.text();
|
|
63
|
-
throw new Error(`Groq API error: ${text}`);
|
|
64
|
-
}
|
|
65
|
-
const data = (await response.json());
|
|
66
|
-
const output = data.choices?.[0]?.message?.content;
|
|
67
|
-
if (!output || typeof output !== "string") {
|
|
68
|
-
throw new Error("Groq returned empty structure");
|
|
69
|
-
}
|
|
70
|
-
// 🔒 Final safety check
|
|
71
|
-
if (!output.includes("folder") && !output.includes("file")) {
|
|
72
|
-
throw new Error("Invalid structure.sr output from Groq");
|
|
73
|
-
}
|
|
74
|
-
return output.trim();
|
|
75
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
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.generateStructure = generateStructure;
|
|
7
|
-
const node_fetch_1 = __importDefault(require("node-fetch"));
|
|
8
|
-
async function generateStructure(params) {
|
|
9
|
-
try {
|
|
10
|
-
const response = await (0, node_fetch_1.default)("https://your-backend.com/generate-structure", {
|
|
11
|
-
method: "POST",
|
|
12
|
-
headers: { "Content-Type": "application/json" },
|
|
13
|
-
body: JSON.stringify(params),
|
|
14
|
-
});
|
|
15
|
-
if (!response.ok) {
|
|
16
|
-
const text = await response.text();
|
|
17
|
-
throw new Error(`Backend error: ${text}`);
|
|
18
|
-
}
|
|
19
|
-
const data = await response.json();
|
|
20
|
-
return data.structure;
|
|
21
|
-
}
|
|
22
|
-
catch (err) {
|
|
23
|
-
throw new Error(`Failed to generate structure: ${err.message}`);
|
|
24
|
-
}
|
|
25
|
-
}
|
package/dist/library/sam.js
DELETED