scaffoldrite 2.0.2 → 2.0.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.
- package/dist/library/ai/ait.js +129 -0
- package/dist/library/ai/generateStructure.js +28 -0
- package/dist/library/ai/generateStructureWithGroq.js +75 -0
- package/dist/library/checkpage/checkPackageType.js +25 -0
- package/dist/library/checkpage/checkpackage.js +59 -0
- package/dist/library/commandHandler.js +7 -3
- package/dist/library/sam.js +2 -0
- package/dist/utils/index.js +2 -0
- package/package.json +7 -1
- package/readme.md +54 -8
|
@@ -0,0 +1,129 @@
|
|
|
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;
|
|
@@ -0,0 +1,28 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
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.checkAndReportPackages = checkAndReportPackages;
|
|
7
|
+
// ./checkpage/checkpackage.ts
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const depcheck_1 = __importDefault(require("depcheck"));
|
|
11
|
+
const data_1 = require("../../data");
|
|
12
|
+
const TS_TYPE_REGEX = /^@types\//;
|
|
13
|
+
async function checkAndReportPackages() {
|
|
14
|
+
console.log(data_1.theme.primary.bold(`${data_1.icons.folder} Checking project dependencies...\n`));
|
|
15
|
+
const packageJsonPath = path_1.default.join(process.cwd(), "package.json");
|
|
16
|
+
if (!fs_1.default.existsSync(packageJsonPath)) {
|
|
17
|
+
console.error(data_1.theme.error(`${data_1.icons.error} package.json was not found in current directory.`));
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const packageJson = JSON.parse(fs_1.default.readFileSync(packageJsonPath, "utf-8"));
|
|
21
|
+
const allDeps = { ...packageJson.dependencies, ...packageJson.devDependencies };
|
|
22
|
+
const missingDeps = [];
|
|
23
|
+
// 1️⃣ Check runtime-resolvable deps
|
|
24
|
+
for (const dep of Object.keys(allDeps)) {
|
|
25
|
+
try {
|
|
26
|
+
require.resolve(dep);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
missingDeps.push(dep);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
// 2️⃣ Run depcheck to catch imported-but-not-listed packages
|
|
33
|
+
const options = { ignoreDirs: ["dist", "build", "node_modules"] };
|
|
34
|
+
const result = await (0, depcheck_1.default)(process.cwd(), options);
|
|
35
|
+
// Combine depcheck missing deps (filter duplicates)
|
|
36
|
+
const depcheckMissing = Object.keys(result.missing || {}).filter((dep) => !missingDeps.includes(dep));
|
|
37
|
+
const finalMissing = [...missingDeps, ...depcheckMissing];
|
|
38
|
+
if (finalMissing.length === 0) {
|
|
39
|
+
console.log(data_1.theme.success.bold(`${data_1.icons.check} All packages are installed!`));
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
// Separate runtime deps vs TypeScript types
|
|
43
|
+
const runtimeDeps = [];
|
|
44
|
+
const typeDeps = [];
|
|
45
|
+
finalMissing.forEach((pkg) => {
|
|
46
|
+
if (TS_TYPE_REGEX.test(pkg))
|
|
47
|
+
typeDeps.push(pkg);
|
|
48
|
+
else
|
|
49
|
+
runtimeDeps.push(pkg);
|
|
50
|
+
});
|
|
51
|
+
console.log(data_1.theme.error.bold(`${data_1.icons.error} Missing packages:`));
|
|
52
|
+
runtimeDeps.forEach((pkg) => console.log(data_1.theme.warning(` - ${pkg} (runtime)`)));
|
|
53
|
+
typeDeps.forEach((pkg) => console.log(data_1.theme.warning(` - ${pkg} (TypeScript type)`)));
|
|
54
|
+
console.log(); // newline
|
|
55
|
+
if (runtimeDeps.length)
|
|
56
|
+
console.log(data_1.theme.info(`Install runtime packages: npm install ${runtimeDeps.join(" ")}`));
|
|
57
|
+
if (typeDeps.length)
|
|
58
|
+
console.log(data_1.theme.info(`Install TypeScript types as dev deps: npm install -D ${typeDeps.join(" ")}`));
|
|
59
|
+
}
|
|
@@ -28,6 +28,7 @@ const index_7 = require("../utils/index");
|
|
|
28
28
|
const index_8 = require("../utils/index");
|
|
29
29
|
const index_9 = require("../utils/index");
|
|
30
30
|
const index_10 = require("../utils/index");
|
|
31
|
+
const checkpackage_1 = require("./checkpage/checkpackage");
|
|
31
32
|
const args = process.argv.slice(3).filter((a) => !a.startsWith("--"));
|
|
32
33
|
const arg3 = args[0];
|
|
33
34
|
const arg4 = args[1];
|
|
@@ -64,9 +65,11 @@ if (!index_9.command || index_9.command === "--help" || index_9.command === "-h"
|
|
|
64
65
|
(0, index_3.printUsage)();
|
|
65
66
|
(0, index_8.exit)(0);
|
|
66
67
|
}
|
|
67
|
-
if (!
|
|
68
|
-
console.
|
|
69
|
-
(
|
|
68
|
+
if (!index_3.ALLOWED_FLAGS.hasOwnProperty(index_9.command)) {
|
|
69
|
+
console.log(allowedFlags, 'wati');
|
|
70
|
+
console.error(`✗ Unknown command: ${index_9.command}`);
|
|
71
|
+
(0, index_3.printUsage)();
|
|
72
|
+
process.exit(1);
|
|
70
73
|
}
|
|
71
74
|
const invalidFlags = passedFlags?.filter((flag) => !allowedFlags.includes(flag));
|
|
72
75
|
if (invalidFlags?.length > 0) {
|
|
@@ -128,6 +131,7 @@ exports.commandHandlers = {
|
|
|
128
131
|
// return;
|
|
129
132
|
// }
|
|
130
133
|
// },
|
|
134
|
+
"check-packages": checkpackage_1.checkAndReportPackages,
|
|
131
135
|
init: async () => {
|
|
132
136
|
const shouldOverwrite = force;
|
|
133
137
|
const sDir = path_1.default.join(index_7.baseDir, ".scaffoldrite");
|
package/dist/utils/index.js
CHANGED
|
@@ -240,6 +240,7 @@ exports.ALLOWED_FLAGS = {
|
|
|
240
240
|
rename: ["--yes", "--dry-run", "--verbose", "--summary"],
|
|
241
241
|
list: ["--structure", "--sr", "--fs", "--diff", "--with-icon"],
|
|
242
242
|
version: [],
|
|
243
|
+
"check-packages": ["--validate"]
|
|
243
244
|
};
|
|
244
245
|
function printUsage(cmd) {
|
|
245
246
|
if (cmd && exports.ALLOWED_FLAGS[cmd]) {
|
|
@@ -254,6 +255,7 @@ function printUsage(cmd) {
|
|
|
254
255
|
delete: "<path> [--yes | -y] [--dry-run] [--verbose | --summary]",
|
|
255
256
|
rename: "<path> <newName> [--yes | -y] [--dry-run] [--verbose | --summary]",
|
|
256
257
|
version: "",
|
|
258
|
+
"check-packages": "[--validate]"
|
|
257
259
|
};
|
|
258
260
|
const args = argsMap[cmd] ? ` ${argsMap[cmd]}` : "";
|
|
259
261
|
console.log(data_1.theme.primary.bold(`Usage for '${cmd}':`) +
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "scaffoldrite",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.4",
|
|
4
4
|
"description": "A project structure validator and generator CLI tool.",
|
|
5
5
|
"author": "Isaac Anasonye",
|
|
6
6
|
"license": "MIT",
|
|
@@ -51,5 +51,11 @@
|
|
|
51
51
|
},
|
|
52
52
|
"engines": {
|
|
53
53
|
"node": ">=17"
|
|
54
|
+
},
|
|
55
|
+
"dependencies": {
|
|
56
|
+
"chalk": "^5.6.2",
|
|
57
|
+
"depcheck": "^1.4.7",
|
|
58
|
+
"dotenv": "^17.2.3",
|
|
59
|
+
"node-fetch": "^3.3.2"
|
|
54
60
|
}
|
|
55
61
|
}
|
package/readme.md
CHANGED
|
@@ -621,7 +621,6 @@ Sometimes you need exceptions. That's where `.scaffoldignore` comes in:
|
|
|
621
621
|
node_modules/ # Ignore dependencies
|
|
622
622
|
dist/ # Ignore build output
|
|
623
623
|
.temp/ # Ignore temporary files
|
|
624
|
-
|
|
625
624
|
```
|
|
626
625
|
|
|
627
626
|
**Used when:**
|
|
@@ -819,17 +818,64 @@ When generating to **output directories**, you can use the `--copy` flag to pres
|
|
|
819
818
|
# Generate structure with empty files (default)
|
|
820
819
|
sr generate ./output
|
|
821
820
|
|
|
822
|
-
|
|
821
|
+
|
|
822
|
+
## 💾 Preserving Content & Mitigating Risks
|
|
823
|
+
|
|
824
|
+
Scaffoldrite focuses on **structure, not file content**. By default, `sr generate` creates missing files or folders **without preserving existing file content**. To make this safer, follow these best practices:
|
|
825
|
+
|
|
826
|
+
### 1️⃣ Use `--copy` when generating to a different directory
|
|
827
|
+
|
|
828
|
+
```bash
|
|
823
829
|
sr generate ./output --copy
|
|
824
830
|
```
|
|
825
831
|
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
832
|
+
* Copies existing file contents from source to output
|
|
833
|
+
* Maintains templates or boilerplate if defined
|
|
834
|
+
* Great for creating project templates or starter kits
|
|
835
|
+
|
|
836
|
+
**Notes:**
|
|
837
|
+
|
|
838
|
+
* `--copy` **does not work in-place**; use Git or manual backup for regenerating in the same directory.
|
|
839
|
+
* Cannot be combined with `--ignore-tooling`.
|
|
840
|
+
|
|
841
|
+
### 2️⃣ Commit changes before regenerating
|
|
842
|
+
|
|
843
|
+
Always commit your work before running `sr generate`:
|
|
844
|
+
|
|
845
|
+
```bash
|
|
846
|
+
git add .
|
|
847
|
+
git commit -m "Save work before sr generate"
|
|
848
|
+
```
|
|
849
|
+
|
|
850
|
+
This ensures you can **restore deleted or modified files** if anything goes wrong.
|
|
851
|
+
|
|
852
|
+
### 3️⃣ Validate first
|
|
853
|
+
|
|
854
|
+
Preview what Scaffoldrite would change without affecting your files:
|
|
855
|
+
|
|
856
|
+
```bash
|
|
857
|
+
sr validate --allow-extra
|
|
858
|
+
```
|
|
859
|
+
|
|
860
|
+
This shows missing, extra, or misaligned files, so you can make informed decisions.
|
|
861
|
+
|
|
862
|
+
### 4️⃣ Rename carefully
|
|
863
|
+
|
|
864
|
+
Instead of renaming in `structure.sr` first:
|
|
865
|
+
|
|
866
|
+
1. Rename the file in your filesystem.
|
|
867
|
+
2. Sync your `structure.sr`:
|
|
868
|
+
|
|
869
|
+
```bash
|
|
870
|
+
sr update --from-fs .
|
|
871
|
+
```
|
|
872
|
+
|
|
873
|
+
This preserves content because the filesystem rename happens before Scaffoldrite updates the structure.
|
|
874
|
+
|
|
875
|
+
### Warning
|
|
876
|
+
|
|
877
|
+
> ⚠ Renaming a file in `structure.sr` **will delete the old file in the filesystem**. Commit or back up your work first.
|
|
831
878
|
|
|
832
|
-
**Note:** `--copy` cannot be used with `--ignore-tooling` as they're mutually exclusive flags.
|
|
833
879
|
|
|
834
880
|
### Real-World Examples
|
|
835
881
|
|