farmwork 1.0.0 โ 1.0.1
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 +116 -88
- package/bin/farmwork.js +0 -6
- package/package.json +8 -2
- package/src/doctor.js +57 -41
- package/src/index.js +0 -1
- package/src/init.js +429 -328
- package/src/status.js +108 -145
- package/src/terminal.js +649 -0
- package/src/add.js +0 -194
package/src/init.js
CHANGED
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
import inquirer from "inquirer";
|
|
2
|
-
import chalk from "chalk";
|
|
3
|
-
import ora from "ora";
|
|
4
2
|
import fs from "fs-extra";
|
|
5
3
|
import path from "path";
|
|
6
4
|
import { fileURLToPath } from "url";
|
|
5
|
+
import { farmTerm, emojis } from "./terminal.js";
|
|
7
6
|
|
|
8
7
|
const __filename = fileURLToPath(import.meta.url);
|
|
9
8
|
const __dirname = path.dirname(__filename);
|
|
10
|
-
const TEMPLATES_DIR = path.join(__dirname, "..", "templates");
|
|
11
9
|
|
|
12
|
-
// Try to detect package.json info
|
|
13
10
|
function detectPackageJson() {
|
|
14
11
|
try {
|
|
15
12
|
const pkgPath = path.join(process.cwd(), "package.json");
|
|
@@ -32,19 +29,19 @@ const QUESTIONS = [
|
|
|
32
29
|
{
|
|
33
30
|
type: "input",
|
|
34
31
|
name: "projectName",
|
|
35
|
-
message: "Project name:",
|
|
32
|
+
message: "๐ฑ Project name:",
|
|
36
33
|
default: path.basename(process.cwd()),
|
|
37
34
|
},
|
|
38
35
|
{
|
|
39
36
|
type: "list",
|
|
40
37
|
name: "packageManager",
|
|
41
|
-
message: "Package manager:",
|
|
38
|
+
message: "๐งบ Package manager:",
|
|
42
39
|
choices: ["npm", "yarn", "pnpm", "bun"],
|
|
43
40
|
},
|
|
44
41
|
{
|
|
45
42
|
type: "input",
|
|
46
43
|
name: "testCommand",
|
|
47
|
-
message: "Test command:",
|
|
44
|
+
message: "๐ฅ Test command:",
|
|
48
45
|
default: (answers) => {
|
|
49
46
|
const { scripts } = detectPackageJson();
|
|
50
47
|
const pm = answers.packageManager;
|
|
@@ -56,7 +53,7 @@ const QUESTIONS = [
|
|
|
56
53
|
{
|
|
57
54
|
type: "input",
|
|
58
55
|
name: "buildCommand",
|
|
59
|
-
message: "Build command:",
|
|
56
|
+
message: "๐ฝ Build command:",
|
|
60
57
|
default: (answers) => {
|
|
61
58
|
const { scripts } = detectPackageJson();
|
|
62
59
|
const pm = answers.packageManager;
|
|
@@ -67,7 +64,7 @@ const QUESTIONS = [
|
|
|
67
64
|
{
|
|
68
65
|
type: "input",
|
|
69
66
|
name: "lintCommand",
|
|
70
|
-
message: "Lint command:",
|
|
67
|
+
message: "๐ฆ Lint command:",
|
|
71
68
|
default: (answers) => {
|
|
72
69
|
const { scripts } = detectPackageJson();
|
|
73
70
|
const pm = answers.packageManager;
|
|
@@ -78,13 +75,13 @@ const QUESTIONS = [
|
|
|
78
75
|
{
|
|
79
76
|
type: "confirm",
|
|
80
77
|
name: "includeStorybook",
|
|
81
|
-
message: "Include Storybook support?
|
|
78
|
+
message: "๐ Include Storybook support?",
|
|
82
79
|
default: () => detectPackageJson().hasStorybook,
|
|
83
80
|
},
|
|
84
81
|
{
|
|
85
82
|
type: "confirm",
|
|
86
83
|
name: "includeI18n",
|
|
87
|
-
message: "Include i18n support?
|
|
84
|
+
message: "๐ป Include i18n support?",
|
|
88
85
|
default: false,
|
|
89
86
|
},
|
|
90
87
|
];
|
|
@@ -93,27 +90,27 @@ const STORYBOOK_QUESTIONS = [
|
|
|
93
90
|
{
|
|
94
91
|
type: "input",
|
|
95
92
|
name: "storybookUrl",
|
|
96
|
-
message: "Storybook URL
|
|
93
|
+
message: "๐ฟ Storybook URL:",
|
|
97
94
|
default: "storybook.example.com",
|
|
98
95
|
},
|
|
99
96
|
{
|
|
100
97
|
type: "input",
|
|
101
98
|
name: "netlifyAuthToken",
|
|
102
|
-
message: "Netlify Auth Token
|
|
99
|
+
message: "๐๏ธ Netlify Auth Token:",
|
|
103
100
|
validate: (input) =>
|
|
104
101
|
input.length > 0 || "Auth token is required for deployment",
|
|
105
102
|
},
|
|
106
103
|
{
|
|
107
104
|
type: "input",
|
|
108
105
|
name: "netlifySiteId",
|
|
109
|
-
message: "Netlify Site ID
|
|
106
|
+
message: "๐ท๏ธ Netlify Site ID:",
|
|
110
107
|
validate: (input) =>
|
|
111
108
|
input.length > 0 || "Site ID is required for deployment",
|
|
112
109
|
},
|
|
113
110
|
{
|
|
114
111
|
type: "confirm",
|
|
115
112
|
name: "passwordProtect",
|
|
116
|
-
message: "Password protect Storybook?
|
|
113
|
+
message: "๐ Password protect Storybook?",
|
|
117
114
|
default: true,
|
|
118
115
|
},
|
|
119
116
|
];
|
|
@@ -121,78 +118,97 @@ const STORYBOOK_QUESTIONS = [
|
|
|
121
118
|
export async function init(options) {
|
|
122
119
|
const cwd = process.cwd();
|
|
123
120
|
|
|
124
|
-
|
|
121
|
+
// Show animated logo
|
|
122
|
+
await farmTerm.logoAnimated();
|
|
123
|
+
|
|
124
|
+
// Check if farmwork is already installed
|
|
125
|
+
const claudeDir = path.join(cwd, ".claude");
|
|
126
|
+
const farmworkConfig = path.join(cwd, ".farmwork.json");
|
|
127
|
+
const claudeMd = path.join(cwd, "CLAUDE.md");
|
|
128
|
+
|
|
129
|
+
const isAlreadyInstalled = fs.existsSync(claudeDir) &&
|
|
130
|
+
(fs.existsSync(farmworkConfig) || fs.existsSync(claudeMd));
|
|
131
|
+
|
|
132
|
+
if (isAlreadyInstalled && !options.force) {
|
|
133
|
+
farmTerm.warn("Farmwork is already installed in this project!");
|
|
134
|
+
farmTerm.nl();
|
|
135
|
+
|
|
136
|
+
// Show what's detected
|
|
137
|
+
farmTerm.gray(" Detected:\n");
|
|
138
|
+
if (fs.existsSync(claudeDir)) farmTerm.gray(" โข .claude/ directory\n");
|
|
139
|
+
if (fs.existsSync(claudeMd)) farmTerm.gray(" โข CLAUDE.md\n");
|
|
140
|
+
if (fs.existsSync(farmworkConfig)) farmTerm.gray(" โข .farmwork.json\n");
|
|
141
|
+
farmTerm.nl();
|
|
142
|
+
|
|
143
|
+
const { continueInit } = await inquirer.prompt([
|
|
144
|
+
{
|
|
145
|
+
type: "list",
|
|
146
|
+
name: "continueInit",
|
|
147
|
+
message: "What would you like to do?",
|
|
148
|
+
choices: [
|
|
149
|
+
{ name: "๐ด Re-initialize (will backup existing files)", value: "reinit" },
|
|
150
|
+
{ name: "๐ฎ Run doctor instead (check health)", value: "doctor" },
|
|
151
|
+
{ name: "๐พ Run status instead (view metrics)", value: "status" },
|
|
152
|
+
{ name: "๐ Exit", value: "exit" }
|
|
153
|
+
]
|
|
154
|
+
}
|
|
155
|
+
]);
|
|
156
|
+
|
|
157
|
+
if (continueInit === "exit") {
|
|
158
|
+
farmTerm.info("No changes made. Your farm is safe! ๐พ\n");
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (continueInit === "doctor") {
|
|
163
|
+
farmTerm.nl();
|
|
164
|
+
const { doctor } = await import("./doctor.js");
|
|
165
|
+
await doctor();
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (continueInit === "status") {
|
|
170
|
+
farmTerm.nl();
|
|
171
|
+
const { status } = await import("./status.js");
|
|
172
|
+
await status();
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// continueInit === "reinit" - proceed with force
|
|
177
|
+
options.force = true;
|
|
178
|
+
farmTerm.nl();
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
farmTerm.header("FARMWORK INITIALIZATION", "primary");
|
|
182
|
+
farmTerm.info("Let's set up your farm! Answer a few questions to get started.\n");
|
|
125
183
|
|
|
126
184
|
const answers = await inquirer.prompt(QUESTIONS);
|
|
127
185
|
|
|
128
|
-
//
|
|
186
|
+
// Storybook configuration
|
|
129
187
|
if (answers.includeStorybook) {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
),
|
|
135
|
-
);
|
|
136
|
-
console.log(
|
|
137
|
-
chalk.gray(
|
|
138
|
-
"This keeps your component documentation private while accessible to your team.\n",
|
|
139
|
-
),
|
|
140
|
-
);
|
|
188
|
+
farmTerm.nl();
|
|
189
|
+
farmTerm.section("Storybook Deployment", "๐");
|
|
190
|
+
farmTerm.gray(" We recommend deploying Storybook to Netlify with password protection.\n");
|
|
191
|
+
farmTerm.gray(" This keeps your component docs private but accessible to your team.\n\n");
|
|
141
192
|
|
|
142
193
|
const storybookAnswers = await inquirer.prompt(STORYBOOK_QUESTIONS);
|
|
143
194
|
Object.assign(answers, storybookAnswers);
|
|
144
195
|
|
|
145
196
|
if (answers.passwordProtect) {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
);
|
|
149
|
-
console.log(
|
|
150
|
-
chalk.gray(" Site settings โ Access control โ Password protection"),
|
|
151
|
-
);
|
|
197
|
+
farmTerm.nl();
|
|
198
|
+
farmTerm.warn("Remember to enable password protection in Netlify:");
|
|
199
|
+
farmTerm.gray(" Site settings โ Access control โ Password protection\n");
|
|
152
200
|
}
|
|
153
201
|
}
|
|
154
202
|
|
|
155
|
-
// Check for existing files
|
|
203
|
+
// Check for existing files
|
|
156
204
|
const existingFiles = [];
|
|
157
205
|
const filesToCheck = [
|
|
158
|
-
{
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
},
|
|
163
|
-
{
|
|
164
|
-
path: path.join(cwd, "justfile"),
|
|
165
|
-
name: "justfile",
|
|
166
|
-
backup: "OLD_justfile",
|
|
167
|
-
},
|
|
168
|
-
{
|
|
169
|
-
path: path.join(cwd, ".farmwork.json"),
|
|
170
|
-
name: ".farmwork.json",
|
|
171
|
-
backup: null,
|
|
172
|
-
},
|
|
173
|
-
{
|
|
174
|
-
path: path.join(cwd, ".claude", "settings.json"),
|
|
175
|
-
name: ".claude/settings.json",
|
|
176
|
-
backup: ".claude/OLD_settings.json",
|
|
177
|
-
},
|
|
178
|
-
{
|
|
179
|
-
path: path.join(cwd, ".claude", "commands"),
|
|
180
|
-
name: ".claude/commands/",
|
|
181
|
-
backup: null,
|
|
182
|
-
isDir: true,
|
|
183
|
-
},
|
|
184
|
-
{
|
|
185
|
-
path: path.join(cwd, ".claude", "agents"),
|
|
186
|
-
name: ".claude/agents/",
|
|
187
|
-
backup: null,
|
|
188
|
-
isDir: true,
|
|
189
|
-
},
|
|
190
|
-
{
|
|
191
|
-
path: path.join(cwd, "_AUDIT"),
|
|
192
|
-
name: "_AUDIT/",
|
|
193
|
-
backup: null,
|
|
194
|
-
isDir: true,
|
|
195
|
-
},
|
|
206
|
+
{ path: path.join(cwd, "CLAUDE.md"), name: "CLAUDE.md", backup: "OLD_CLAUDE.md" },
|
|
207
|
+
{ path: path.join(cwd, "justfile"), name: "justfile", backup: "OLD_justfile" },
|
|
208
|
+
{ path: path.join(cwd, ".farmwork.json"), name: ".farmwork.json", backup: null },
|
|
209
|
+
{ path: path.join(cwd, ".claude", "commands"), name: ".claude/commands/", backup: null, isDir: true },
|
|
210
|
+
{ path: path.join(cwd, ".claude", "agents"), name: ".claude/agents/", backup: null, isDir: true },
|
|
211
|
+
{ path: path.join(cwd, "_AUDIT"), name: "_AUDIT/", backup: null, isDir: true },
|
|
196
212
|
];
|
|
197
213
|
|
|
198
214
|
for (const file of filesToCheck) {
|
|
@@ -204,17 +220,23 @@ export async function init(options) {
|
|
|
204
220
|
let didBackupClaudeMd = false;
|
|
205
221
|
|
|
206
222
|
if (existingFiles.length > 0 && !options.force) {
|
|
207
|
-
|
|
223
|
+
farmTerm.nl();
|
|
224
|
+
farmTerm.warn("The following files/folders already exist:");
|
|
225
|
+
farmTerm.nl();
|
|
226
|
+
|
|
208
227
|
for (const file of existingFiles) {
|
|
209
228
|
if (file.isDir) {
|
|
210
|
-
|
|
229
|
+
farmTerm.gray(` ${file.name}`);
|
|
230
|
+
farmTerm.cyan(" (will add new files)\n");
|
|
211
231
|
} else if (file.backup) {
|
|
212
|
-
|
|
232
|
+
farmTerm.gray(` ${file.name}`);
|
|
233
|
+
farmTerm.yellow(` โ ${file.backup}\n`);
|
|
213
234
|
} else {
|
|
214
|
-
|
|
235
|
+
farmTerm.gray(` ${file.name}`);
|
|
236
|
+
farmTerm.red(" (will overwrite)\n");
|
|
215
237
|
}
|
|
216
238
|
}
|
|
217
|
-
|
|
239
|
+
farmTerm.nl();
|
|
218
240
|
|
|
219
241
|
const { overwriteChoice } = await inquirer.prompt([
|
|
220
242
|
{
|
|
@@ -222,291 +244,197 @@ export async function init(options) {
|
|
|
222
244
|
name: "overwriteChoice",
|
|
223
245
|
message: "How would you like to proceed?",
|
|
224
246
|
choices: [
|
|
225
|
-
{
|
|
226
|
-
|
|
227
|
-
value: "overwrite",
|
|
228
|
-
},
|
|
229
|
-
{ name: "Cancel installation", value: "cancel" },
|
|
247
|
+
{ name: "๐ฑ Continue (backup files, add to existing folders)", value: "overwrite" },
|
|
248
|
+
{ name: "๐ Cancel installation", value: "cancel" },
|
|
230
249
|
],
|
|
231
250
|
},
|
|
232
251
|
]);
|
|
233
252
|
|
|
234
253
|
if (overwriteChoice === "cancel") {
|
|
235
|
-
|
|
254
|
+
farmTerm.nl();
|
|
255
|
+
farmTerm.gray(" Installation cancelled.\n\n");
|
|
236
256
|
process.exit(0);
|
|
237
257
|
}
|
|
238
258
|
|
|
239
|
-
// Backup files
|
|
240
|
-
|
|
259
|
+
// Backup files
|
|
260
|
+
farmTerm.nl();
|
|
241
261
|
for (const file of existingFiles) {
|
|
242
262
|
if (file.backup) {
|
|
243
263
|
const backupPath = path.join(cwd, file.backup);
|
|
244
264
|
await fs.copy(file.path, backupPath);
|
|
245
|
-
|
|
265
|
+
farmTerm.status(`Backed up ${file.name} โ ${file.backup}`, "pass");
|
|
246
266
|
if (file.name === "CLAUDE.md") {
|
|
247
267
|
didBackupClaudeMd = true;
|
|
248
268
|
}
|
|
249
269
|
}
|
|
250
270
|
}
|
|
251
|
-
console.log("");
|
|
252
271
|
}
|
|
253
272
|
|
|
254
|
-
// Store for use in final output
|
|
255
273
|
answers._didBackupClaudeMd = didBackupClaudeMd;
|
|
256
274
|
|
|
257
|
-
|
|
275
|
+
// Planting animation
|
|
276
|
+
farmTerm.nl();
|
|
277
|
+
farmTerm.section("Planting Your Farm", emojis.seedling);
|
|
258
278
|
|
|
259
279
|
try {
|
|
260
|
-
// Create folder structure
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
spinner.text = "Creating core commands...";
|
|
282
|
-
await createCommands(cwd, answers);
|
|
283
|
-
|
|
284
|
-
spinner.text = "Creating settings...";
|
|
285
|
-
await createSettings(cwd, answers);
|
|
280
|
+
// Create folder structure with animations
|
|
281
|
+
const steps = [
|
|
282
|
+
{ name: "Creating directories", fn: async () => {
|
|
283
|
+
await fs.ensureDir(path.join(cwd, "_AUDIT"));
|
|
284
|
+
await fs.ensureDir(path.join(cwd, "_PLANS"));
|
|
285
|
+
await fs.ensureDir(path.join(cwd, ".claude", "commands"));
|
|
286
|
+
await fs.ensureDir(path.join(cwd, ".claude", "agents"));
|
|
287
|
+
}},
|
|
288
|
+
{ name: "Planting CLAUDE.md", fn: () => createClaudeMd(cwd, answers) },
|
|
289
|
+
{ name: "Building FARMHOUSE.md", fn: () => createFarmhouseMd(cwd, answers) },
|
|
290
|
+
{ name: "Creating audit documents", fn: () => createAuditDocs(cwd, answers) },
|
|
291
|
+
{ name: "Laying out justfile", fn: () => createJustfile(cwd, answers) },
|
|
292
|
+
{ name: "Training agents", fn: () => createAgents(cwd, answers) },
|
|
293
|
+
{ name: "Setting up commands", fn: () => createCommands(cwd, answers) },
|
|
294
|
+
{ name: "Configuring settings", fn: () => createSettings(cwd, answers) },
|
|
295
|
+
{ name: "Writing .farmwork.json", fn: () => createProduceConfig(cwd, answers) },
|
|
296
|
+
];
|
|
297
|
+
|
|
298
|
+
for (const step of steps) {
|
|
299
|
+
await farmTerm.spin(step.name, step.fn);
|
|
300
|
+
}
|
|
286
301
|
|
|
287
|
-
|
|
288
|
-
|
|
302
|
+
// Install dependencies
|
|
303
|
+
farmTerm.nl();
|
|
304
|
+
farmTerm.section("Installing Tools", emojis.horse);
|
|
289
305
|
|
|
290
|
-
// Check and install just
|
|
291
|
-
|
|
292
|
-
try {
|
|
306
|
+
// Check and install just
|
|
307
|
+
await farmTerm.spin("Checking for just command runner", async () => {
|
|
293
308
|
const { execSync } = await import("child_process");
|
|
294
309
|
try {
|
|
295
310
|
execSync("which just", { stdio: "ignore" });
|
|
296
311
|
} catch {
|
|
297
|
-
spinner.text = "Installing just...";
|
|
298
312
|
try {
|
|
299
|
-
// Try brew first (macOS), then cargo
|
|
300
313
|
try {
|
|
301
|
-
execSync("brew install just", { stdio: "
|
|
302
|
-
console.log(
|
|
303
|
-
chalk.green("\n๐ฑ Just installed successfully via Homebrew"),
|
|
304
|
-
);
|
|
314
|
+
execSync("brew install just", { stdio: "pipe" });
|
|
305
315
|
} catch {
|
|
306
|
-
execSync("cargo install just", { stdio: "
|
|
307
|
-
console.log(
|
|
308
|
-
chalk.green("\n๐ฑ Just installed successfully via Cargo"),
|
|
309
|
-
);
|
|
316
|
+
execSync("cargo install just", { stdio: "pipe" });
|
|
310
317
|
}
|
|
311
318
|
} catch {
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
);
|
|
315
|
-
console.log(chalk.gray(" Install manually: brew install just"));
|
|
316
|
-
console.log(chalk.gray(" Or see: https://github.com/casey/just"));
|
|
319
|
+
farmTerm.warn("Could not install just automatically.");
|
|
320
|
+
farmTerm.gray(" Install manually: brew install just\n");
|
|
317
321
|
}
|
|
318
322
|
}
|
|
319
|
-
}
|
|
320
|
-
// Silently continue if check fails
|
|
321
|
-
}
|
|
323
|
+
});
|
|
322
324
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
+
// Check and install beads
|
|
326
|
+
await farmTerm.spin("Setting up beads issue tracking", async () => {
|
|
325
327
|
const { execSync } = await import("child_process");
|
|
326
|
-
|
|
327
|
-
// Check if bd is installed
|
|
328
328
|
try {
|
|
329
329
|
execSync("which bd", { stdio: "ignore" });
|
|
330
330
|
} catch {
|
|
331
|
-
// bd not found, try to install it (npm first, then brew, then cargo)
|
|
332
|
-
spinner.text = "Installing beads (bd)...";
|
|
333
331
|
let installed = false;
|
|
334
|
-
|
|
335
|
-
// Try npm first (most common)
|
|
336
332
|
try {
|
|
337
|
-
execSync("npm install -g @beads/bd", { stdio: "
|
|
338
|
-
console.log(chalk.green("\n๐ฑ Beads installed successfully via npm"));
|
|
333
|
+
execSync("npm install -g @beads/bd", { stdio: "pipe" });
|
|
339
334
|
installed = true;
|
|
340
335
|
} catch {
|
|
341
|
-
// Try homebrew
|
|
342
336
|
try {
|
|
343
|
-
execSync("brew install steveyegge/beads/bd", { stdio: "
|
|
344
|
-
console.log(
|
|
345
|
-
chalk.green("\n๐ฑ Beads installed successfully via Homebrew"),
|
|
346
|
-
);
|
|
337
|
+
execSync("brew install steveyegge/beads/bd", { stdio: "pipe" });
|
|
347
338
|
installed = true;
|
|
348
339
|
} catch {
|
|
349
|
-
// Try cargo as last resort
|
|
350
340
|
try {
|
|
351
|
-
execSync("cargo install beads", { stdio: "
|
|
352
|
-
console.log(
|
|
353
|
-
chalk.green("\n๐ฑ Beads installed successfully via Cargo"),
|
|
354
|
-
);
|
|
341
|
+
execSync("cargo install beads", { stdio: "pipe" });
|
|
355
342
|
installed = true;
|
|
356
343
|
} catch {
|
|
357
344
|
// All methods failed
|
|
358
345
|
}
|
|
359
346
|
}
|
|
360
347
|
}
|
|
361
|
-
|
|
362
348
|
if (!installed) {
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
);
|
|
366
|
-
console.log(
|
|
367
|
-
chalk.gray(" Install manually: npm install -g @beads/bd"),
|
|
368
|
-
);
|
|
369
|
-
console.log(chalk.gray(" Or: brew install steveyegge/beads/bd"));
|
|
370
|
-
console.log(
|
|
371
|
-
chalk.gray(" Or see: https://github.com/steveyegge/beads"),
|
|
372
|
-
);
|
|
349
|
+
farmTerm.warn("Could not install beads automatically.");
|
|
350
|
+
farmTerm.gray(" Install manually: npm install -g @beads/bd\n");
|
|
373
351
|
}
|
|
374
352
|
}
|
|
375
353
|
|
|
376
|
-
// Initialize beads
|
|
377
|
-
spinner.text = "Initializing beads...";
|
|
354
|
+
// Initialize beads
|
|
378
355
|
try {
|
|
379
356
|
execSync("bd init", { cwd, stdio: "ignore" });
|
|
380
357
|
} catch {
|
|
381
|
-
// bd init might fail if already initialized
|
|
358
|
+
// bd init might fail if already initialized
|
|
382
359
|
}
|
|
383
360
|
|
|
384
|
-
// Clean up beads-generated
|
|
385
|
-
spinner.text = "Cleaning up beads defaults...";
|
|
361
|
+
// Clean up beads-generated files
|
|
386
362
|
const beadsAgentFiles = ["AGENTS.md", "@AGENTS.md"];
|
|
387
363
|
for (const file of beadsAgentFiles) {
|
|
388
|
-
const filePath = path.join(cwd, file);
|
|
389
364
|
try {
|
|
390
|
-
await fs.remove(
|
|
365
|
+
await fs.remove(path.join(cwd, file));
|
|
391
366
|
} catch {
|
|
392
|
-
// File doesn't exist
|
|
367
|
+
// File doesn't exist
|
|
393
368
|
}
|
|
394
369
|
}
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
);
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
);
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
);
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
);
|
|
439
|
-
console.log(
|
|
440
|
-
chalk.white(" โ") +
|
|
441
|
-
chalk.yellow(
|
|
442
|
-
" justfile and create project-specific commands, and go through my app ",
|
|
443
|
-
) +
|
|
444
|
-
chalk.white("โ"),
|
|
445
|
-
);
|
|
446
|
-
console.log(
|
|
447
|
-
chalk.white(" โ") +
|
|
448
|
-
chalk.yellow(
|
|
449
|
-
" and suggest any project-specific subagents that would work well. ",
|
|
450
|
-
) +
|
|
451
|
-
chalk.white("โ"),
|
|
452
|
-
);
|
|
453
|
-
console.log(
|
|
454
|
-
chalk.white(
|
|
455
|
-
" โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ",
|
|
456
|
-
),
|
|
457
|
-
);
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
// Success!
|
|
373
|
+
farmTerm.nl();
|
|
374
|
+
farmTerm.divider("โ", 50);
|
|
375
|
+
farmTerm.success("Farmwork initialized successfully!");
|
|
376
|
+
|
|
377
|
+
// Show created structure
|
|
378
|
+
farmTerm.section("Created Structure", emojis.corn);
|
|
379
|
+
await farmTerm.planting([
|
|
380
|
+
"_AUDIT/",
|
|
381
|
+
"_PLANS/",
|
|
382
|
+
".claude/commands/",
|
|
383
|
+
".claude/agents/",
|
|
384
|
+
"CLAUDE.md",
|
|
385
|
+
"justfile",
|
|
386
|
+
".farmwork.json",
|
|
387
|
+
], "Files planted");
|
|
388
|
+
|
|
389
|
+
// Next steps
|
|
390
|
+
farmTerm.section("Next Steps", emojis.carrot);
|
|
391
|
+
farmTerm.nl();
|
|
392
|
+
farmTerm.white(" 1. ");
|
|
393
|
+
farmTerm.yellow("just --list");
|
|
394
|
+
farmTerm.gray(" โ See available commands\n");
|
|
395
|
+
farmTerm.white(" 2. ");
|
|
396
|
+
farmTerm.yellow('"till the land"');
|
|
397
|
+
farmTerm.gray(" โ Audit your setup\n");
|
|
398
|
+
farmTerm.white(" 3. ");
|
|
399
|
+
farmTerm.yellow('"make a plan for <feature>"');
|
|
400
|
+
farmTerm.gray(" โ Start planning\n");
|
|
401
|
+
|
|
402
|
+
// Claude prompt box
|
|
403
|
+
farmTerm.nl();
|
|
404
|
+
farmTerm.section("Get Claude Comfortable", emojis.wheat);
|
|
405
|
+
farmTerm.gray(" Copy and paste this prompt to Claude Code:\n\n");
|
|
406
|
+
|
|
407
|
+
farmTerm.box("Prompt for Claude", [
|
|
408
|
+
"Hey Claude, I am using the Farmwork framework,",
|
|
409
|
+
"please go through the justfile and create",
|
|
410
|
+
"project-specific commands, and go through my",
|
|
411
|
+
"app and suggest project-specific subagents",
|
|
412
|
+
"that would work well.",
|
|
413
|
+
], "secondary");
|
|
458
414
|
|
|
459
415
|
// Show merge prompt if we backed up CLAUDE.md
|
|
460
416
|
if (answers._didBackupClaudeMd) {
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
chalk.white(" โ") +
|
|
474
|
-
chalk.yellow(
|
|
475
|
-
" Hey Claude, look at my CLAUDE.md file and merge the project-specific ",
|
|
476
|
-
) +
|
|
477
|
-
chalk.white("โ"),
|
|
478
|
-
);
|
|
479
|
-
console.log(
|
|
480
|
-
chalk.white(" โ") +
|
|
481
|
-
chalk.yellow(
|
|
482
|
-
" instructions from OLD_CLAUDE.md into it, so I have one file with all ",
|
|
483
|
-
) +
|
|
484
|
-
chalk.white("โ"),
|
|
485
|
-
);
|
|
486
|
-
console.log(
|
|
487
|
-
chalk.white(" โ") +
|
|
488
|
-
chalk.yellow(
|
|
489
|
-
" the Farmwork framework instructions plus my original project setup. ",
|
|
490
|
-
) +
|
|
491
|
-
chalk.white("โ"),
|
|
492
|
-
);
|
|
493
|
-
console.log(
|
|
494
|
-
chalk.white(" โ") +
|
|
495
|
-
chalk.yellow(
|
|
496
|
-
" Then delete OLD_CLAUDE.md when done. Same for OLD_justfile. Thank you. ",
|
|
497
|
-
) +
|
|
498
|
-
chalk.white("โ"),
|
|
499
|
-
);
|
|
500
|
-
console.log(
|
|
501
|
-
chalk.white(
|
|
502
|
-
" โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ",
|
|
503
|
-
),
|
|
504
|
-
);
|
|
417
|
+
farmTerm.nl();
|
|
418
|
+
farmTerm.section("Merge Your Old Instructions", "๐ฅฌ");
|
|
419
|
+
farmTerm.gray(" Your old CLAUDE.md was backed up. Use this prompt to merge:\n\n");
|
|
420
|
+
|
|
421
|
+
farmTerm.box("Merge Prompt", [
|
|
422
|
+
"Hey Claude, look at my CLAUDE.md file and",
|
|
423
|
+
"merge the project-specific instructions from",
|
|
424
|
+
"OLD_CLAUDE.md into it, so I have one file",
|
|
425
|
+
"with all the Farmwork instructions plus my",
|
|
426
|
+
"original project setup. Then delete the OLD",
|
|
427
|
+
"files when done.",
|
|
428
|
+
], "accent");
|
|
505
429
|
}
|
|
506
430
|
|
|
507
|
-
|
|
431
|
+
// Final tractor drive
|
|
432
|
+
farmTerm.nl();
|
|
433
|
+
await farmTerm.tractorAnimation("Your farm is ready!", 1500);
|
|
434
|
+
farmTerm.nl();
|
|
435
|
+
|
|
508
436
|
} catch (error) {
|
|
509
|
-
|
|
437
|
+
farmTerm.error("Failed to initialize Farmwork");
|
|
510
438
|
console.error(error);
|
|
511
439
|
process.exit(1);
|
|
512
440
|
}
|
|
@@ -657,8 +585,8 @@ async function createFarmhouseMd(cwd, answers) {
|
|
|
657
585
|
|
|
658
586
|
| Metric | Count |
|
|
659
587
|
|--------|-------|
|
|
660
|
-
| Commands |
|
|
661
|
-
| Agents |
|
|
588
|
+
| Commands | 2 |
|
|
589
|
+
| Agents | 9 |
|
|
662
590
|
| Justfile Recipes | 10 |
|
|
663
591
|
| Unit Tests | 0 |
|
|
664
592
|
| E2E Tests | 0 |
|
|
@@ -677,6 +605,7 @@ All Claude Code commands and agents are documented, phrase triggers are tested a
|
|
|
677
605
|
| Command | Description |
|
|
678
606
|
|---------|-------------|
|
|
679
607
|
| \`/push\` | Clean, lint, test, build, commit, push |
|
|
608
|
+
| \`/open-the-farm\` | Full audit cycle, then ask user next steps |
|
|
680
609
|
|
|
681
610
|
---
|
|
682
611
|
|
|
@@ -688,6 +617,11 @@ All Claude Code commands and agents are documented, phrase triggers are tested a
|
|
|
688
617
|
| \`code-reviewer\` | Quality & security code review |
|
|
689
618
|
| \`security-auditor\` | OWASP vulnerability scanning |
|
|
690
619
|
| \`performance-auditor\` | Performance anti-patterns |
|
|
620
|
+
| \`code-smell-auditor\` | DRY violations, complexity, naming |
|
|
621
|
+
| \`unused-code-cleaner\` | Detect and remove dead code |
|
|
622
|
+
| \`code-cleaner\` | Remove comments and console.logs |
|
|
623
|
+
| \`i18n-locale-translator\` | Translate UI text to locales |
|
|
624
|
+
| \`storybook-maintainer\` | Create/update Storybook stories |
|
|
691
625
|
|
|
692
626
|
---
|
|
693
627
|
|
|
@@ -745,26 +679,10 @@ async function createAuditDocs(cwd, answers) {
|
|
|
745
679
|
const today = new Date().toISOString().split("T")[0];
|
|
746
680
|
|
|
747
681
|
const audits = [
|
|
748
|
-
{
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
},
|
|
753
|
-
{
|
|
754
|
-
name: "PERFORMANCE.md",
|
|
755
|
-
title: "Performance Audit",
|
|
756
|
-
description: "Performance metrics and optimization tracking",
|
|
757
|
-
},
|
|
758
|
-
{
|
|
759
|
-
name: "CODE_QUALITY.md",
|
|
760
|
-
title: "Code Quality Audit",
|
|
761
|
-
description: "Code quality and standards tracking",
|
|
762
|
-
},
|
|
763
|
-
{
|
|
764
|
-
name: "TESTS.md",
|
|
765
|
-
title: "Test Coverage Audit",
|
|
766
|
-
description: "Test coverage and gaps tracking",
|
|
767
|
-
},
|
|
682
|
+
{ name: "SECURITY.md", title: "Security Audit", description: "Security posture and vulnerability tracking" },
|
|
683
|
+
{ name: "PERFORMANCE.md", title: "Performance Audit", description: "Performance metrics and optimization tracking" },
|
|
684
|
+
{ name: "CODE_QUALITY.md", title: "Code Quality Audit", description: "Code quality and standards tracking" },
|
|
685
|
+
{ name: "TESTS.md", title: "Test Coverage Audit", description: "Test coverage and gaps tracking" },
|
|
768
686
|
];
|
|
769
687
|
|
|
770
688
|
for (const audit of audits) {
|
|
@@ -948,8 +866,8 @@ Reports findings with severity (CRITICAL, HIGH, MEDIUM, LOW) and remediation ste
|
|
|
948
866
|
"security-auditor.md": `---
|
|
949
867
|
name: security-auditor
|
|
950
868
|
description: OWASP security vulnerability scanning
|
|
951
|
-
tools: Read, Grep, Glob
|
|
952
|
-
model:
|
|
869
|
+
tools: Read, Grep, Glob, Edit
|
|
870
|
+
model: haiku
|
|
953
871
|
---
|
|
954
872
|
|
|
955
873
|
# Security Auditor Agent
|
|
@@ -967,8 +885,8 @@ Updates \`_AUDIT/SECURITY.md\` with results.
|
|
|
967
885
|
"performance-auditor.md": `---
|
|
968
886
|
name: performance-auditor
|
|
969
887
|
description: Find memory leaks, unnecessary re-renders, and anti-patterns
|
|
970
|
-
tools: Read, Grep, Glob
|
|
971
|
-
model:
|
|
888
|
+
tools: Read, Grep, Glob, Edit
|
|
889
|
+
model: haiku
|
|
972
890
|
---
|
|
973
891
|
|
|
974
892
|
# Performance Auditor Agent
|
|
@@ -982,6 +900,98 @@ Scans for performance anti-patterns:
|
|
|
982
900
|
|
|
983
901
|
Reports findings with impact assessment.
|
|
984
902
|
Updates \`_AUDIT/PERFORMANCE.md\` with results.
|
|
903
|
+
`,
|
|
904
|
+
"code-smell-auditor.md": `---
|
|
905
|
+
name: code-smell-auditor
|
|
906
|
+
description: Detect DRY violations, complexity issues, naming problems, and technical debt
|
|
907
|
+
tools: Read, Grep, Glob, Edit
|
|
908
|
+
model: haiku
|
|
909
|
+
---
|
|
910
|
+
|
|
911
|
+
# Code Smell Auditor Agent
|
|
912
|
+
|
|
913
|
+
Scans for code quality issues:
|
|
914
|
+
- DRY violations (duplicated code)
|
|
915
|
+
- Complexity issues (functions > 50 lines, deep nesting)
|
|
916
|
+
- Naming issues (misleading names, abbreviations)
|
|
917
|
+
- Magic values (hardcoded numbers/strings)
|
|
918
|
+
- Technical debt (TODO, FIXME, HACK comments)
|
|
919
|
+
|
|
920
|
+
Reports code health as GOOD / FAIR / NEEDS ATTENTION.
|
|
921
|
+
Updates \`_AUDIT/CODE_QUALITY.md\` with results.
|
|
922
|
+
`,
|
|
923
|
+
"unused-code-cleaner.md": `---
|
|
924
|
+
name: unused-code-cleaner
|
|
925
|
+
description: Detect and remove unused code (imports, functions, variables)
|
|
926
|
+
tools: Read, Write, Edit, Bash, Grep, Glob
|
|
927
|
+
model: haiku
|
|
928
|
+
---
|
|
929
|
+
|
|
930
|
+
# Unused Code Cleaner Agent
|
|
931
|
+
|
|
932
|
+
Detects and removes unused code:
|
|
933
|
+
- Unused imports
|
|
934
|
+
- Unused functions and classes
|
|
935
|
+
- Unused variables
|
|
936
|
+
- Dead code paths
|
|
937
|
+
- Console.log statements (optional)
|
|
938
|
+
- Comments (preserves JSDoc)
|
|
939
|
+
|
|
940
|
+
Use after refactoring, when removing features, or before production deployment.
|
|
941
|
+
`,
|
|
942
|
+
"code-cleaner.md": `---
|
|
943
|
+
name: code-cleaner
|
|
944
|
+
description: Fast removal of comments, console.logs, and debug code while preserving JSDoc
|
|
945
|
+
tools: Read, Edit, Glob, Grep
|
|
946
|
+
model: haiku
|
|
947
|
+
---
|
|
948
|
+
|
|
949
|
+
# Code Cleaner Agent
|
|
950
|
+
|
|
951
|
+
Fast cleanup of TypeScript/JavaScript files:
|
|
952
|
+
|
|
953
|
+
## Removes
|
|
954
|
+
- Line comments (\`//\`)
|
|
955
|
+
- Block comments (\`/* */\`)
|
|
956
|
+
- \`console.log\` statements
|
|
957
|
+
|
|
958
|
+
## Preserves
|
|
959
|
+
- JSDoc comments (\`/** */\`)
|
|
960
|
+
- \`console.error\`, \`console.warn\`, \`console.info\`
|
|
961
|
+
`,
|
|
962
|
+
"i18n-locale-translator.md": `---
|
|
963
|
+
name: i18n-locale-translator
|
|
964
|
+
description: Translate UI text content into English (en) and Japanese (jp) using i18n locale system
|
|
965
|
+
tools: Read, Write, Edit, Glob, Grep
|
|
966
|
+
model: sonnet
|
|
967
|
+
---
|
|
968
|
+
|
|
969
|
+
# i18n Locale Translator Agent
|
|
970
|
+
|
|
971
|
+
Handles internationalization tasks:
|
|
972
|
+
- Extract hardcoded text from components
|
|
973
|
+
- Create translation keys in locale files
|
|
974
|
+
- Translate content to English and Japanese
|
|
975
|
+
- Update components to use translation hooks
|
|
976
|
+
|
|
977
|
+
Use when adding new features or internationalizing existing hardcoded text.
|
|
978
|
+
`,
|
|
979
|
+
"storybook-maintainer.md": `---
|
|
980
|
+
name: storybook-maintainer
|
|
981
|
+
description: Create and update Storybook stories for UI components
|
|
982
|
+
tools: Read, Write, Edit, Glob, Grep
|
|
983
|
+
model: haiku
|
|
984
|
+
---
|
|
985
|
+
|
|
986
|
+
# Storybook Maintainer Agent
|
|
987
|
+
|
|
988
|
+
Manages Storybook stories for UI components:
|
|
989
|
+
- Analyze component props and variants
|
|
990
|
+
- Create comprehensive story files
|
|
991
|
+
- Document component usage
|
|
992
|
+
- Add controls for interactive props
|
|
993
|
+
|
|
994
|
+
Use when adding new components or when existing components change significantly.
|
|
985
995
|
`,
|
|
986
996
|
};
|
|
987
997
|
|
|
@@ -993,7 +1003,6 @@ Updates \`_AUDIT/PERFORMANCE.md\` with results.
|
|
|
993
1003
|
async function createCommands(cwd, answers) {
|
|
994
1004
|
const pm = answers.packageManager || "npm";
|
|
995
1005
|
|
|
996
|
-
// Build Storybook deployment steps if enabled
|
|
997
1006
|
const storybookSteps = answers.includeStorybook
|
|
998
1007
|
? `
|
|
999
1008
|
|
|
@@ -1085,23 +1094,117 @@ ${reportContent}`;
|
|
|
1085
1094
|
path.join(cwd, ".claude", "commands", "push.md"),
|
|
1086
1095
|
pushCommand,
|
|
1087
1096
|
);
|
|
1088
|
-
}
|
|
1089
1097
|
|
|
1090
|
-
|
|
1091
|
-
const
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
+
// Create open-the-farm command
|
|
1099
|
+
const openTheFarmCommand = `---
|
|
1100
|
+
description: Full audit cycle - till, inspect, dry run harvest, then ask user next steps
|
|
1101
|
+
allowed-tools: Bash(${pm}:*), Bash(just:*), Task
|
|
1102
|
+
---
|
|
1103
|
+
|
|
1104
|
+
# Open the Farm Command
|
|
1105
|
+
|
|
1106
|
+
Complete development audit workflow. Runs all audits and quality checks without committing, then asks the user what to do next.
|
|
1107
|
+
|
|
1108
|
+
Trigger phrase: "open the farm"
|
|
1109
|
+
|
|
1110
|
+
## Workflow
|
|
1111
|
+
|
|
1112
|
+
Execute these steps in order. Track findings from each step for the final summary.
|
|
1113
|
+
|
|
1114
|
+
### Step 1: Till the Land
|
|
1115
|
+
|
|
1116
|
+
Update the harness documentation with current metrics.
|
|
1117
|
+
|
|
1118
|
+
Launch the \`the-farmer\` agent to audit and update \`_AUDIT/FARMHOUSE.md\`:
|
|
1119
|
+
- Commands and agents inventory
|
|
1120
|
+
- Test counts
|
|
1121
|
+
- Completed issues count
|
|
1122
|
+
|
|
1123
|
+
Record the Farmhouse score for the summary.
|
|
1124
|
+
|
|
1125
|
+
### Step 2: Inspect the Farm
|
|
1126
|
+
|
|
1127
|
+
Run all inspection agents in parallel for comprehensive code quality check.
|
|
1128
|
+
|
|
1129
|
+
#### 2a. Code Review & Cleanup
|
|
1130
|
+
Launch these agents in parallel:
|
|
1131
|
+
- \`code-reviewer\` agent on recently modified files
|
|
1132
|
+
- \`unused-code-cleaner\` to detect dead code, unused imports
|
|
1133
|
+
|
|
1134
|
+
#### 2b. Performance Audit
|
|
1135
|
+
- Launch \`performance-auditor\` agent for anti-patterns
|
|
1136
|
+
|
|
1137
|
+
#### 2c. Security Audit
|
|
1138
|
+
- Launch \`security-auditor\` agent for OWASP Top 10 vulnerabilities
|
|
1139
|
+
- Note findings by severity (CRITICAL, HIGH, MEDIUM, LOW)
|
|
1140
|
+
|
|
1141
|
+
#### 2d. Code Quality Audit
|
|
1142
|
+
- Launch \`code-smell-auditor\` agent for DRY violations, complexity, naming issues
|
|
1143
|
+
- Note code health rating (GOOD / FAIR / NEEDS ATTENTION)
|
|
1144
|
+
|
|
1145
|
+
### Step 3: Dry Run Harvest
|
|
1146
|
+
|
|
1147
|
+
Run quality gates WITHOUT committing or pushing:
|
|
1148
|
+
|
|
1149
|
+
1. **Lint**: \`${answers.lintCommand}\`
|
|
1150
|
+
2. **Tests**: \`${answers.testCommand}\`
|
|
1151
|
+
3. **Build**: \`${answers.buildCommand}\`
|
|
1152
|
+
|
|
1153
|
+
Record pass/fail status for each gate.
|
|
1154
|
+
|
|
1155
|
+
### Step 4: Summary Report
|
|
1156
|
+
|
|
1157
|
+
Present a consolidated report of all findings:
|
|
1158
|
+
|
|
1159
|
+
\`\`\`
|
|
1160
|
+
## Farm Audit Complete
|
|
1161
|
+
|
|
1162
|
+
### Harness Status
|
|
1163
|
+
- Farmhouse Score: X/10
|
|
1164
|
+
- Commands: X | Agents: X | Tests: X
|
|
1165
|
+
|
|
1166
|
+
### Code Quality
|
|
1167
|
+
- Security: X issues (Y critical, Z high)
|
|
1168
|
+
- Performance: X issues
|
|
1169
|
+
- Code Smells: [GOOD/FAIR/NEEDS ATTENTION]
|
|
1170
|
+
- Unused Code: X items flagged
|
|
1171
|
+
|
|
1172
|
+
### Quality Gates
|
|
1173
|
+
- Lint: โ
/โ
|
|
1174
|
+
- Tests: โ
/โ (X passed, Y failed)
|
|
1175
|
+
- Build: โ
/โ
|
|
1176
|
+
|
|
1177
|
+
### Open Items
|
|
1178
|
+
[List top 3-5 issues that should be addressed]
|
|
1179
|
+
\`\`\`
|
|
1180
|
+
|
|
1181
|
+
### Step 5: Ask User
|
|
1182
|
+
|
|
1183
|
+
After presenting the summary, ask:
|
|
1184
|
+
|
|
1185
|
+
> "All audits complete. What would you like to do?"
|
|
1186
|
+
>
|
|
1187
|
+
> Options:
|
|
1188
|
+
> - **harvest crops** - Commit and push all changes
|
|
1189
|
+
> - **review audits** - Show detailed findings from a specific audit
|
|
1190
|
+
> - **fix issues first** - Address the open items before pushing
|
|
1191
|
+
|
|
1192
|
+
Wait for user response before proceeding.
|
|
1193
|
+
|
|
1194
|
+
## Notes
|
|
1195
|
+
|
|
1196
|
+
- This command does NOT modify files or commit changes
|
|
1197
|
+
- All audit agents update their respective \`_AUDIT/*.md\` files
|
|
1198
|
+
- Use this before major releases or after significant development
|
|
1199
|
+
`;
|
|
1098
1200
|
|
|
1099
1201
|
await fs.writeFile(
|
|
1100
|
-
path.join(cwd, ".claude", "
|
|
1101
|
-
|
|
1202
|
+
path.join(cwd, ".claude", "commands", "open-the-farm.md"),
|
|
1203
|
+
openTheFarmCommand,
|
|
1102
1204
|
);
|
|
1205
|
+
}
|
|
1103
1206
|
|
|
1104
|
-
|
|
1207
|
+
async function createSettings(cwd, answers) {
|
|
1105
1208
|
if (answers.includeStorybook && answers.netlifyAuthToken) {
|
|
1106
1209
|
const localSettings = {
|
|
1107
1210
|
env: {
|
|
@@ -1115,7 +1218,6 @@ async function createSettings(cwd, answers) {
|
|
|
1115
1218
|
JSON.stringify(localSettings, null, 2),
|
|
1116
1219
|
);
|
|
1117
1220
|
|
|
1118
|
-
// Ensure settings.local.json is gitignored
|
|
1119
1221
|
const gitignorePath = path.join(cwd, ".gitignore");
|
|
1120
1222
|
let gitignoreContent = "";
|
|
1121
1223
|
try {
|
|
@@ -1158,7 +1260,6 @@ async function createProduceConfig(cwd, answers) {
|
|
|
1158
1260
|
audits: ["FARMHOUSE", "SECURITY", "PERFORMANCE", "CODE_QUALITY", "TESTS"],
|
|
1159
1261
|
};
|
|
1160
1262
|
|
|
1161
|
-
// Add Storybook configuration if enabled
|
|
1162
1263
|
if (answers.includeStorybook) {
|
|
1163
1264
|
config.storybook = {
|
|
1164
1265
|
url: answers.storybookUrl || null,
|