farmwork 1.0.0 โ 1.0.2
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 +118 -92
- package/bin/farmwork.js +0 -6
- package/package.json +9 -3
- package/src/doctor.js +57 -41
- package/src/index.js +0 -1
- package/src/init.js +474 -339
- 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,38 +118,99 @@ 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 =
|
|
130
|
+
fs.existsSync(claudeDir) &&
|
|
131
|
+
(fs.existsSync(farmworkConfig) || fs.existsSync(claudeMd));
|
|
132
|
+
|
|
133
|
+
if (isAlreadyInstalled && !options.force) {
|
|
134
|
+
farmTerm.warn("Farmwork is already installed in this project!");
|
|
135
|
+
farmTerm.nl();
|
|
136
|
+
|
|
137
|
+
// Show what's detected
|
|
138
|
+
farmTerm.gray(" Detected:\n");
|
|
139
|
+
if (fs.existsSync(claudeDir)) farmTerm.gray(" โข .claude/ directory\n");
|
|
140
|
+
if (fs.existsSync(claudeMd)) farmTerm.gray(" โข CLAUDE.md\n");
|
|
141
|
+
if (fs.existsSync(farmworkConfig)) farmTerm.gray(" โข .farmwork.json\n");
|
|
142
|
+
farmTerm.nl();
|
|
143
|
+
|
|
144
|
+
const { continueInit } = await inquirer.prompt([
|
|
145
|
+
{
|
|
146
|
+
type: "list",
|
|
147
|
+
name: "continueInit",
|
|
148
|
+
message: "What would you like to do?",
|
|
149
|
+
choices: [
|
|
150
|
+
{
|
|
151
|
+
name: "๐ด Re-initialize (will backup existing files)",
|
|
152
|
+
value: "reinit",
|
|
153
|
+
},
|
|
154
|
+
{ name: "๐ฎ Run doctor instead (check health)", value: "doctor" },
|
|
155
|
+
{ name: "๐พ Run status instead (view metrics)", value: "status" },
|
|
156
|
+
{ name: "๐ Exit", value: "exit" },
|
|
157
|
+
],
|
|
158
|
+
},
|
|
159
|
+
]);
|
|
160
|
+
|
|
161
|
+
if (continueInit === "exit") {
|
|
162
|
+
farmTerm.info("No changes made. Your farm is safe! ๐พ\n");
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (continueInit === "doctor") {
|
|
167
|
+
farmTerm.nl();
|
|
168
|
+
const { doctor } = await import("./doctor.js");
|
|
169
|
+
await doctor();
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (continueInit === "status") {
|
|
174
|
+
farmTerm.nl();
|
|
175
|
+
const { status } = await import("./status.js");
|
|
176
|
+
await status();
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// continueInit === "reinit" - proceed with force
|
|
181
|
+
options.force = true;
|
|
182
|
+
farmTerm.nl();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
farmTerm.header("FARMWORK INITIALIZATION", "primary");
|
|
186
|
+
farmTerm.info(
|
|
187
|
+
"Let's set up your farm! Answer a few questions to get started.\n",
|
|
188
|
+
);
|
|
125
189
|
|
|
126
190
|
const answers = await inquirer.prompt(QUESTIONS);
|
|
127
191
|
|
|
128
|
-
//
|
|
192
|
+
// Storybook configuration
|
|
129
193
|
if (answers.includeStorybook) {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
),
|
|
194
|
+
farmTerm.nl();
|
|
195
|
+
farmTerm.section("Storybook Deployment", "๐");
|
|
196
|
+
farmTerm.gray(
|
|
197
|
+
" We recommend deploying Storybook to Netlify with password protection.\n",
|
|
135
198
|
);
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
"This keeps your component documentation private while accessible to your team.\n",
|
|
139
|
-
),
|
|
199
|
+
farmTerm.gray(
|
|
200
|
+
" This keeps your component docs private but accessible to your team.\n\n",
|
|
140
201
|
);
|
|
141
202
|
|
|
142
203
|
const storybookAnswers = await inquirer.prompt(STORYBOOK_QUESTIONS);
|
|
143
204
|
Object.assign(answers, storybookAnswers);
|
|
144
205
|
|
|
145
206
|
if (answers.passwordProtect) {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
);
|
|
149
|
-
console.log(
|
|
150
|
-
chalk.gray(" Site settings โ Access control โ Password protection"),
|
|
151
|
-
);
|
|
207
|
+
farmTerm.nl();
|
|
208
|
+
farmTerm.warn("Remember to enable password protection in Netlify:");
|
|
209
|
+
farmTerm.gray(" Site settings โ Access control โ Password protection\n");
|
|
152
210
|
}
|
|
153
211
|
}
|
|
154
212
|
|
|
155
|
-
// Check for existing files
|
|
213
|
+
// Check for existing files
|
|
156
214
|
const existingFiles = [];
|
|
157
215
|
const filesToCheck = [
|
|
158
216
|
{
|
|
@@ -170,11 +228,6 @@ export async function init(options) {
|
|
|
170
228
|
name: ".farmwork.json",
|
|
171
229
|
backup: null,
|
|
172
230
|
},
|
|
173
|
-
{
|
|
174
|
-
path: path.join(cwd, ".claude", "settings.json"),
|
|
175
|
-
name: ".claude/settings.json",
|
|
176
|
-
backup: ".claude/OLD_settings.json",
|
|
177
|
-
},
|
|
178
231
|
{
|
|
179
232
|
path: path.join(cwd, ".claude", "commands"),
|
|
180
233
|
name: ".claude/commands/",
|
|
@@ -204,17 +257,23 @@ export async function init(options) {
|
|
|
204
257
|
let didBackupClaudeMd = false;
|
|
205
258
|
|
|
206
259
|
if (existingFiles.length > 0 && !options.force) {
|
|
207
|
-
|
|
260
|
+
farmTerm.nl();
|
|
261
|
+
farmTerm.warn("The following files/folders already exist:");
|
|
262
|
+
farmTerm.nl();
|
|
263
|
+
|
|
208
264
|
for (const file of existingFiles) {
|
|
209
265
|
if (file.isDir) {
|
|
210
|
-
|
|
266
|
+
farmTerm.gray(` ${file.name}`);
|
|
267
|
+
farmTerm.cyan(" (will add new files)\n");
|
|
211
268
|
} else if (file.backup) {
|
|
212
|
-
|
|
269
|
+
farmTerm.gray(` ${file.name}`);
|
|
270
|
+
farmTerm.yellow(` โ ${file.backup}\n`);
|
|
213
271
|
} else {
|
|
214
|
-
|
|
272
|
+
farmTerm.gray(` ${file.name}`);
|
|
273
|
+
farmTerm.red(" (will overwrite)\n");
|
|
215
274
|
}
|
|
216
275
|
}
|
|
217
|
-
|
|
276
|
+
farmTerm.nl();
|
|
218
277
|
|
|
219
278
|
const { overwriteChoice } = await inquirer.prompt([
|
|
220
279
|
{
|
|
@@ -223,290 +282,223 @@ export async function init(options) {
|
|
|
223
282
|
message: "How would you like to proceed?",
|
|
224
283
|
choices: [
|
|
225
284
|
{
|
|
226
|
-
name: "Continue (backup files, add to existing folders)",
|
|
285
|
+
name: "๐ฑ Continue (backup files, add to existing folders)",
|
|
227
286
|
value: "overwrite",
|
|
228
287
|
},
|
|
229
|
-
{ name: "Cancel installation", value: "cancel" },
|
|
288
|
+
{ name: "๐ Cancel installation", value: "cancel" },
|
|
230
289
|
],
|
|
231
290
|
},
|
|
232
291
|
]);
|
|
233
292
|
|
|
234
293
|
if (overwriteChoice === "cancel") {
|
|
235
|
-
|
|
294
|
+
farmTerm.nl();
|
|
295
|
+
farmTerm.gray(" Installation cancelled.\n\n");
|
|
236
296
|
process.exit(0);
|
|
237
297
|
}
|
|
238
298
|
|
|
239
|
-
// Backup files
|
|
240
|
-
|
|
299
|
+
// Backup files
|
|
300
|
+
farmTerm.nl();
|
|
241
301
|
for (const file of existingFiles) {
|
|
242
302
|
if (file.backup) {
|
|
243
303
|
const backupPath = path.join(cwd, file.backup);
|
|
244
304
|
await fs.copy(file.path, backupPath);
|
|
245
|
-
|
|
305
|
+
farmTerm.status(`Backed up ${file.name} โ ${file.backup}`, "pass");
|
|
246
306
|
if (file.name === "CLAUDE.md") {
|
|
247
307
|
didBackupClaudeMd = true;
|
|
248
308
|
}
|
|
249
309
|
}
|
|
250
310
|
}
|
|
251
|
-
console.log("");
|
|
252
311
|
}
|
|
253
312
|
|
|
254
|
-
// Store for use in final output
|
|
255
313
|
answers._didBackupClaudeMd = didBackupClaudeMd;
|
|
256
314
|
|
|
257
|
-
|
|
315
|
+
// Planting animation
|
|
316
|
+
farmTerm.nl();
|
|
317
|
+
farmTerm.section("Planting Your Farm", emojis.seedling);
|
|
258
318
|
|
|
259
319
|
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
|
-
|
|
282
|
-
|
|
320
|
+
// Create folder structure with animations
|
|
321
|
+
const steps = [
|
|
322
|
+
{
|
|
323
|
+
name: "Creating directories",
|
|
324
|
+
fn: async () => {
|
|
325
|
+
await fs.ensureDir(path.join(cwd, "_AUDIT"));
|
|
326
|
+
await fs.ensureDir(path.join(cwd, "_PLANS"));
|
|
327
|
+
await fs.ensureDir(path.join(cwd, ".claude", "commands"));
|
|
328
|
+
await fs.ensureDir(path.join(cwd, ".claude", "agents"));
|
|
329
|
+
},
|
|
330
|
+
},
|
|
331
|
+
{ name: "Planting CLAUDE.md", fn: () => createClaudeMd(cwd, answers) },
|
|
332
|
+
{
|
|
333
|
+
name: "Building FARMHOUSE.md",
|
|
334
|
+
fn: () => createFarmhouseMd(cwd, answers),
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
name: "Creating audit documents",
|
|
338
|
+
fn: () => createAuditDocs(cwd, answers),
|
|
339
|
+
},
|
|
340
|
+
{ name: "Laying out justfile", fn: () => createJustfile(cwd, answers) },
|
|
341
|
+
{ name: "Training agents", fn: () => createAgents(cwd, answers) },
|
|
342
|
+
{ name: "Setting up commands", fn: () => createCommands(cwd, answers) },
|
|
343
|
+
{ name: "Configuring settings", fn: () => createSettings(cwd, answers) },
|
|
344
|
+
{
|
|
345
|
+
name: "Writing .farmwork.json",
|
|
346
|
+
fn: () => createProduceConfig(cwd, answers),
|
|
347
|
+
},
|
|
348
|
+
];
|
|
283
349
|
|
|
284
|
-
|
|
285
|
-
|
|
350
|
+
for (const step of steps) {
|
|
351
|
+
await farmTerm.spin(step.name, step.fn);
|
|
352
|
+
}
|
|
286
353
|
|
|
287
|
-
|
|
288
|
-
|
|
354
|
+
// Install dependencies
|
|
355
|
+
farmTerm.nl();
|
|
356
|
+
farmTerm.section("Installing Tools", emojis.horse);
|
|
289
357
|
|
|
290
|
-
// Check and install just
|
|
291
|
-
|
|
292
|
-
try {
|
|
358
|
+
// Check and install just
|
|
359
|
+
await farmTerm.spin("Checking for just command runner", async () => {
|
|
293
360
|
const { execSync } = await import("child_process");
|
|
294
361
|
try {
|
|
295
362
|
execSync("which just", { stdio: "ignore" });
|
|
296
363
|
} catch {
|
|
297
|
-
spinner.text = "Installing just...";
|
|
298
364
|
try {
|
|
299
|
-
// Try brew first (macOS), then cargo
|
|
300
365
|
try {
|
|
301
|
-
execSync("brew install just", { stdio: "
|
|
302
|
-
console.log(
|
|
303
|
-
chalk.green("\n๐ฑ Just installed successfully via Homebrew"),
|
|
304
|
-
);
|
|
366
|
+
execSync("brew install just", { stdio: "pipe" });
|
|
305
367
|
} catch {
|
|
306
|
-
execSync("cargo install just", { stdio: "
|
|
307
|
-
console.log(
|
|
308
|
-
chalk.green("\n๐ฑ Just installed successfully via Cargo"),
|
|
309
|
-
);
|
|
368
|
+
execSync("cargo install just", { stdio: "pipe" });
|
|
310
369
|
}
|
|
311
370
|
} 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"));
|
|
371
|
+
farmTerm.warn("Could not install just automatically.");
|
|
372
|
+
farmTerm.gray(" Install manually: brew install just\n");
|
|
317
373
|
}
|
|
318
374
|
}
|
|
319
|
-
}
|
|
320
|
-
// Silently continue if check fails
|
|
321
|
-
}
|
|
375
|
+
});
|
|
322
376
|
|
|
323
|
-
|
|
324
|
-
|
|
377
|
+
// Check and install beads
|
|
378
|
+
await farmTerm.spin("Setting up beads issue tracking", async () => {
|
|
325
379
|
const { execSync } = await import("child_process");
|
|
326
|
-
|
|
327
|
-
// Check if bd is installed
|
|
328
380
|
try {
|
|
329
381
|
execSync("which bd", { stdio: "ignore" });
|
|
330
382
|
} catch {
|
|
331
|
-
// bd not found, try to install it (npm first, then brew, then cargo)
|
|
332
|
-
spinner.text = "Installing beads (bd)...";
|
|
333
383
|
let installed = false;
|
|
334
|
-
|
|
335
|
-
// Try npm first (most common)
|
|
336
384
|
try {
|
|
337
|
-
execSync("npm install -g @beads/bd", { stdio: "
|
|
338
|
-
console.log(chalk.green("\n๐ฑ Beads installed successfully via npm"));
|
|
385
|
+
execSync("npm install -g @beads/bd", { stdio: "pipe" });
|
|
339
386
|
installed = true;
|
|
340
387
|
} catch {
|
|
341
|
-
// Try homebrew
|
|
342
388
|
try {
|
|
343
|
-
execSync("brew install steveyegge/beads/bd", { stdio: "
|
|
344
|
-
console.log(
|
|
345
|
-
chalk.green("\n๐ฑ Beads installed successfully via Homebrew"),
|
|
346
|
-
);
|
|
389
|
+
execSync("brew install steveyegge/beads/bd", { stdio: "pipe" });
|
|
347
390
|
installed = true;
|
|
348
391
|
} catch {
|
|
349
|
-
// Try cargo as last resort
|
|
350
392
|
try {
|
|
351
|
-
execSync("cargo install beads", { stdio: "
|
|
352
|
-
console.log(
|
|
353
|
-
chalk.green("\n๐ฑ Beads installed successfully via Cargo"),
|
|
354
|
-
);
|
|
393
|
+
execSync("cargo install beads", { stdio: "pipe" });
|
|
355
394
|
installed = true;
|
|
356
395
|
} catch {
|
|
357
396
|
// All methods failed
|
|
358
397
|
}
|
|
359
398
|
}
|
|
360
399
|
}
|
|
361
|
-
|
|
362
400
|
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
|
-
);
|
|
401
|
+
farmTerm.warn("Could not install beads automatically.");
|
|
402
|
+
farmTerm.gray(" Install manually: npm install -g @beads/bd\n");
|
|
373
403
|
}
|
|
374
404
|
}
|
|
375
405
|
|
|
376
|
-
// Initialize beads
|
|
377
|
-
spinner.text = "Initializing beads...";
|
|
406
|
+
// Initialize beads
|
|
378
407
|
try {
|
|
379
408
|
execSync("bd init", { cwd, stdio: "ignore" });
|
|
380
409
|
} catch {
|
|
381
|
-
// bd init might fail if already initialized
|
|
410
|
+
// bd init might fail if already initialized
|
|
382
411
|
}
|
|
383
412
|
|
|
384
|
-
// Clean up beads-generated
|
|
385
|
-
spinner.text = "Cleaning up beads defaults...";
|
|
413
|
+
// Clean up beads-generated files
|
|
386
414
|
const beadsAgentFiles = ["AGENTS.md", "@AGENTS.md"];
|
|
387
415
|
for (const file of beadsAgentFiles) {
|
|
388
|
-
const filePath = path.join(cwd, file);
|
|
389
416
|
try {
|
|
390
|
-
await fs.remove(
|
|
417
|
+
await fs.remove(path.join(cwd, file));
|
|
391
418
|
} catch {
|
|
392
|
-
// File doesn't exist
|
|
419
|
+
// File doesn't exist
|
|
393
420
|
}
|
|
394
421
|
}
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
console.log(
|
|
416
|
-
` 1. Run ${chalk.yellow("just --list")} to see available commands`,
|
|
417
|
-
);
|
|
418
|
-
console.log(
|
|
419
|
-
` 2. Say ${chalk.yellow('"till the land"')} to Claude to audit your setup`,
|
|
420
|
-
);
|
|
421
|
-
console.log(
|
|
422
|
-
` 3. Say ${chalk.yellow('"make a plan for <feature>"')} to start planning`,
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
// Success!
|
|
425
|
+
farmTerm.nl();
|
|
426
|
+
farmTerm.divider("โ", 50);
|
|
427
|
+
farmTerm.success("Farmwork initialized successfully!");
|
|
428
|
+
|
|
429
|
+
// Show created structure
|
|
430
|
+
farmTerm.section("Created Structure", emojis.corn);
|
|
431
|
+
await farmTerm.planting(
|
|
432
|
+
[
|
|
433
|
+
"_AUDIT/",
|
|
434
|
+
"_PLANS/",
|
|
435
|
+
".claude/commands/",
|
|
436
|
+
".claude/agents/",
|
|
437
|
+
"CLAUDE.md",
|
|
438
|
+
"justfile",
|
|
439
|
+
".farmwork.json",
|
|
440
|
+
],
|
|
441
|
+
"Files planted",
|
|
423
442
|
);
|
|
424
443
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
);
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
console.log(
|
|
454
|
-
chalk.white(
|
|
455
|
-
" โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ",
|
|
456
|
-
),
|
|
444
|
+
// Next steps
|
|
445
|
+
farmTerm.section("Next Steps", emojis.carrot);
|
|
446
|
+
farmTerm.nl();
|
|
447
|
+
farmTerm.white(" 1. ");
|
|
448
|
+
farmTerm.yellow("just --list");
|
|
449
|
+
farmTerm.gray(" โ See available commands\n");
|
|
450
|
+
farmTerm.white(" 2. ");
|
|
451
|
+
farmTerm.yellow('"open the farm"');
|
|
452
|
+
farmTerm.gray(" โ Audit your setup\n");
|
|
453
|
+
farmTerm.white(" 3. ");
|
|
454
|
+
farmTerm.yellow('"make a plan for <feature>"');
|
|
455
|
+
farmTerm.gray(" โ Start planning\n");
|
|
456
|
+
|
|
457
|
+
// Claude prompt box
|
|
458
|
+
farmTerm.nl();
|
|
459
|
+
farmTerm.section("Get Claude Comfortable", emojis.wheat);
|
|
460
|
+
farmTerm.gray(" Copy and paste this prompt to Claude Code:\n\n");
|
|
461
|
+
|
|
462
|
+
farmTerm.box(
|
|
463
|
+
"Prompt for Claude",
|
|
464
|
+
[
|
|
465
|
+
"Hey Claude, I am using the Farmwork framework,",
|
|
466
|
+
"please go through the justfile and create",
|
|
467
|
+
"project-specific commands, and go through my",
|
|
468
|
+
"app and suggest project-specific subagents",
|
|
469
|
+
"that would work well.",
|
|
470
|
+
],
|
|
471
|
+
"secondary",
|
|
457
472
|
);
|
|
458
473
|
|
|
459
474
|
// Show merge prompt if we backed up CLAUDE.md
|
|
460
475
|
if (answers._didBackupClaudeMd) {
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
),
|
|
466
|
-
);
|
|
467
|
-
console.log(
|
|
468
|
-
chalk.white(
|
|
469
|
-
" โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ",
|
|
470
|
-
),
|
|
471
|
-
);
|
|
472
|
-
console.log(
|
|
473
|
-
chalk.white(" โ") +
|
|
474
|
-
chalk.yellow(
|
|
475
|
-
" Hey Claude, look at my CLAUDE.md file and merge the project-specific ",
|
|
476
|
-
) +
|
|
477
|
-
chalk.white("โ"),
|
|
476
|
+
farmTerm.nl();
|
|
477
|
+
farmTerm.section("Merge Your Old Instructions", "๐ฅฌ");
|
|
478
|
+
farmTerm.gray(
|
|
479
|
+
" Your old CLAUDE.md was backed up. Use this prompt to merge:\n\n",
|
|
478
480
|
);
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
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
|
-
),
|
|
481
|
+
|
|
482
|
+
farmTerm.box(
|
|
483
|
+
"Merge Prompt",
|
|
484
|
+
[
|
|
485
|
+
"Hey Claude, look at my CLAUDE.md file and",
|
|
486
|
+
"merge the project-specific instructions from",
|
|
487
|
+
"OLD_CLAUDE.md into it, so I have one file",
|
|
488
|
+
"with all the Farmwork instructions plus my",
|
|
489
|
+
"original project setup. Then delete the OLD",
|
|
490
|
+
"files when done.",
|
|
491
|
+
],
|
|
492
|
+
"accent",
|
|
504
493
|
);
|
|
505
494
|
}
|
|
506
495
|
|
|
507
|
-
|
|
496
|
+
// Final tractor drive
|
|
497
|
+
farmTerm.nl();
|
|
498
|
+
await farmTerm.tractorAnimation("Your farm is ready!", 1500);
|
|
499
|
+
farmTerm.nl();
|
|
508
500
|
} catch (error) {
|
|
509
|
-
|
|
501
|
+
farmTerm.error("Failed to initialize Farmwork");
|
|
510
502
|
console.error(error);
|
|
511
503
|
process.exit(1);
|
|
512
504
|
}
|
|
@@ -548,11 +540,10 @@ Run these in order for a complete development cycle:
|
|
|
548
540
|
|
|
549
541
|
| Phrase | Action |
|
|
550
542
|
|--------|--------|
|
|
551
|
-
| **
|
|
552
|
-
| **
|
|
553
|
-
| **go to market** | i18n scan +
|
|
554
|
-
| **
|
|
555
|
-
| **open the farm** | Full audit cycle (everything except push), then ask to proceed |
|
|
543
|
+
| **open the farm** | Audit systems, update \`_AUDIT/FARMHOUSE.md\` with current metrics |
|
|
544
|
+
| **count the herd** | Full inspection + dry run: code review, cleanup, performance, security, code quality, accessibility |
|
|
545
|
+
| **go to market** | i18n scan + accessibility audit for missing translations and a11y issues |
|
|
546
|
+
| **close the farm** | Execute \`/push\` (lint, test, build, commit, push) |
|
|
556
547
|
|
|
557
548
|
---
|
|
558
549
|
|
|
@@ -567,28 +558,30 @@ Run these in order for a complete development cycle:
|
|
|
567
558
|
|
|
568
559
|
### Farmwork Phrase Details
|
|
569
560
|
|
|
570
|
-
**
|
|
561
|
+
**open the farm**
|
|
571
562
|
1. Launch \`the-farmer\` agent to audit all systems
|
|
572
563
|
2. Run \`bd list --status closed | wc -l\` to get total completed issues
|
|
573
564
|
3. Updates \`_AUDIT/FARMHOUSE.md\` with current metrics
|
|
574
565
|
|
|
575
|
-
**
|
|
576
|
-
Runs all inspection agents in parallel
|
|
566
|
+
**count the herd** (Full Audit Cycle)
|
|
567
|
+
Runs all inspection agents in parallel, then dry run quality gates. No push.
|
|
568
|
+
|
|
577
569
|
1. **Code Review & Cleanup** - \`code-reviewer\` + \`unused-code-cleaner\`
|
|
578
|
-
2. **Performance Audit** -
|
|
570
|
+
2. **Performance Audit** - \`performance-auditor\`, updates \`_AUDIT/PERFORMANCE.md\`
|
|
579
571
|
3. **Security Audit** - \`security-auditor\` for OWASP Top 10, updates \`_AUDIT/SECURITY.md\`
|
|
580
572
|
4. **Code Quality** - \`code-smell-auditor\` for DRY violations, updates \`_AUDIT/CODE_QUALITY.md\`
|
|
581
|
-
5. **
|
|
573
|
+
5. **Accessibility** - \`accessibility-auditor\` for WCAG 2.1, updates \`_AUDIT/ACCESSIBILITY.md\`
|
|
574
|
+
6. **Dry Run** - lint, tests, build (but NOT commit/push)
|
|
575
|
+
7. **Summary Report** - Consolidate findings, ask user next steps
|
|
582
576
|
|
|
583
|
-
**
|
|
584
|
-
|
|
577
|
+
**go to market**
|
|
578
|
+
1. Scan for hardcoded text not using i18n
|
|
579
|
+
2. Launch \`i18n-locale-translator\` agent
|
|
580
|
+
3. Launch \`accessibility-auditor\` for WCAG 2.1 compliance
|
|
581
|
+
4. Updates \`_AUDIT/ACCESSIBILITY.md\`
|
|
585
582
|
|
|
586
|
-
**
|
|
587
|
-
|
|
588
|
-
2. **Inspect the Farm** - Full inspection (code review, cleanup, performance, security, code quality)
|
|
589
|
-
3. **Dry Run Harvest** - Run lint, tests, build (but NOT commit/push)
|
|
590
|
-
4. **Summary Report** - Present consolidated findings
|
|
591
|
-
5. **Ask User** - Ready to harvest (push)?
|
|
583
|
+
**close the farm**
|
|
584
|
+
- Invoke the \`push\` skill immediately
|
|
592
585
|
|
|
593
586
|
---
|
|
594
587
|
|
|
@@ -645,7 +638,7 @@ async function createFarmhouseMd(cwd, answers) {
|
|
|
645
638
|
const content = `# Farmwork Farmhouse
|
|
646
639
|
|
|
647
640
|
> Central command for the Farmwork agentic harness.
|
|
648
|
-
> Updated automatically by \`the-farmer\` agent during \`/push\` or via "
|
|
641
|
+
> Updated automatically by \`the-farmer\` agent during \`/push\` or via "open the farm" phrase.
|
|
649
642
|
|
|
650
643
|
**Last Updated:** ${today}
|
|
651
644
|
**Score:** 5.0/10
|
|
@@ -658,7 +651,7 @@ async function createFarmhouseMd(cwd, answers) {
|
|
|
658
651
|
| Metric | Count |
|
|
659
652
|
|--------|-------|
|
|
660
653
|
| Commands | 1 |
|
|
661
|
-
| Agents |
|
|
654
|
+
| Agents | 10 |
|
|
662
655
|
| Justfile Recipes | 10 |
|
|
663
656
|
| Unit Tests | 0 |
|
|
664
657
|
| E2E Tests | 0 |
|
|
@@ -676,7 +669,7 @@ All Claude Code commands and agents are documented, phrase triggers are tested a
|
|
|
676
669
|
|
|
677
670
|
| Command | Description |
|
|
678
671
|
|---------|-------------|
|
|
679
|
-
| \`/push\` | Clean, lint, test, build, commit, push |
|
|
672
|
+
| \`/push\` | Clean, lint, test, build, commit, push, update metrics |
|
|
680
673
|
|
|
681
674
|
---
|
|
682
675
|
|
|
@@ -688,6 +681,12 @@ All Claude Code commands and agents are documented, phrase triggers are tested a
|
|
|
688
681
|
| \`code-reviewer\` | Quality & security code review |
|
|
689
682
|
| \`security-auditor\` | OWASP vulnerability scanning |
|
|
690
683
|
| \`performance-auditor\` | Performance anti-patterns |
|
|
684
|
+
| \`code-smell-auditor\` | DRY violations, complexity, naming |
|
|
685
|
+
| \`accessibility-auditor\` | WCAG 2.1 compliance, alt text, contrast |
|
|
686
|
+
| \`unused-code-cleaner\` | Detect and remove dead code |
|
|
687
|
+
| \`code-cleaner\` | Remove comments and console.logs |
|
|
688
|
+
| \`i18n-locale-translator\` | Translate UI text to locales |
|
|
689
|
+
| \`storybook-maintainer\` | Create/update Storybook stories |
|
|
691
690
|
|
|
692
691
|
---
|
|
693
692
|
|
|
@@ -697,11 +696,10 @@ All Claude Code commands and agents are documented, phrase triggers are tested a
|
|
|
697
696
|
|
|
698
697
|
| Phrase | Action |
|
|
699
698
|
|--------|--------|
|
|
700
|
-
| \`
|
|
701
|
-
| \`
|
|
702
|
-
| \`go to market\` | i18n scan +
|
|
703
|
-
| \`
|
|
704
|
-
| \`open the farm\` | Full audit cycle |
|
|
699
|
+
| \`open the farm\` | Audit systems, update FARMHOUSE.md |
|
|
700
|
+
| \`count the herd\` | Full inspection + dry run (no push) |
|
|
701
|
+
| \`go to market\` | i18n scan + accessibility audit |
|
|
702
|
+
| \`close the farm\` | Execute /push |
|
|
705
703
|
|
|
706
704
|
### Plan Phrases
|
|
707
705
|
|
|
@@ -755,6 +753,11 @@ async function createAuditDocs(cwd, answers) {
|
|
|
755
753
|
title: "Performance Audit",
|
|
756
754
|
description: "Performance metrics and optimization tracking",
|
|
757
755
|
},
|
|
756
|
+
{
|
|
757
|
+
name: "ACCESSIBILITY.md",
|
|
758
|
+
title: "Accessibility Audit",
|
|
759
|
+
description: "WCAG 2.1 Level AA compliance tracking",
|
|
760
|
+
},
|
|
758
761
|
{
|
|
759
762
|
name: "CODE_QUALITY.md",
|
|
760
763
|
title: "Code Quality Audit",
|
|
@@ -948,8 +951,8 @@ Reports findings with severity (CRITICAL, HIGH, MEDIUM, LOW) and remediation ste
|
|
|
948
951
|
"security-auditor.md": `---
|
|
949
952
|
name: security-auditor
|
|
950
953
|
description: OWASP security vulnerability scanning
|
|
951
|
-
tools: Read, Grep, Glob
|
|
952
|
-
model:
|
|
954
|
+
tools: Read, Grep, Glob, Edit
|
|
955
|
+
model: haiku
|
|
953
956
|
---
|
|
954
957
|
|
|
955
958
|
# Security Auditor Agent
|
|
@@ -967,8 +970,8 @@ Updates \`_AUDIT/SECURITY.md\` with results.
|
|
|
967
970
|
"performance-auditor.md": `---
|
|
968
971
|
name: performance-auditor
|
|
969
972
|
description: Find memory leaks, unnecessary re-renders, and anti-patterns
|
|
970
|
-
tools: Read, Grep, Glob
|
|
971
|
-
model:
|
|
973
|
+
tools: Read, Grep, Glob, Edit
|
|
974
|
+
model: haiku
|
|
972
975
|
---
|
|
973
976
|
|
|
974
977
|
# Performance Auditor Agent
|
|
@@ -982,6 +985,118 @@ Scans for performance anti-patterns:
|
|
|
982
985
|
|
|
983
986
|
Reports findings with impact assessment.
|
|
984
987
|
Updates \`_AUDIT/PERFORMANCE.md\` with results.
|
|
988
|
+
`,
|
|
989
|
+
"code-smell-auditor.md": `---
|
|
990
|
+
name: code-smell-auditor
|
|
991
|
+
description: Detect DRY violations, complexity issues, naming problems, and technical debt
|
|
992
|
+
tools: Read, Grep, Glob, Edit
|
|
993
|
+
model: haiku
|
|
994
|
+
---
|
|
995
|
+
|
|
996
|
+
# Code Smell Auditor Agent
|
|
997
|
+
|
|
998
|
+
Scans for code quality issues:
|
|
999
|
+
- DRY violations (duplicated code)
|
|
1000
|
+
- Complexity issues (functions > 50 lines, deep nesting)
|
|
1001
|
+
- Naming issues (misleading names, abbreviations)
|
|
1002
|
+
- Magic values (hardcoded numbers/strings)
|
|
1003
|
+
- Technical debt (TODO, FIXME, HACK comments)
|
|
1004
|
+
|
|
1005
|
+
Reports code health as GOOD / FAIR / NEEDS ATTENTION.
|
|
1006
|
+
Updates \`_AUDIT/CODE_QUALITY.md\` with results.
|
|
1007
|
+
`,
|
|
1008
|
+
"accessibility-auditor.md": `---
|
|
1009
|
+
name: accessibility-auditor
|
|
1010
|
+
description: WCAG 2.1 accessibility auditing for React/Next.js applications
|
|
1011
|
+
tools: Read, Grep, Glob, Edit
|
|
1012
|
+
model: haiku
|
|
1013
|
+
---
|
|
1014
|
+
|
|
1015
|
+
# Accessibility Auditor Agent
|
|
1016
|
+
|
|
1017
|
+
Scans for WCAG 2.1 Level AA compliance issues:
|
|
1018
|
+
- Missing or inadequate alt text on images
|
|
1019
|
+
- Color contrast issues
|
|
1020
|
+
- Keyboard navigation problems
|
|
1021
|
+
- Missing ARIA labels and roles
|
|
1022
|
+
- Form accessibility (labels, error messages)
|
|
1023
|
+
- Focus management issues
|
|
1024
|
+
|
|
1025
|
+
Reports findings by severity (CRITICAL, HIGH, MEDIUM, LOW).
|
|
1026
|
+
Updates \`_AUDIT/ACCESSIBILITY.md\` with results.
|
|
1027
|
+
`,
|
|
1028
|
+
"unused-code-cleaner.md": `---
|
|
1029
|
+
name: unused-code-cleaner
|
|
1030
|
+
description: Detect and remove unused code (imports, functions, variables)
|
|
1031
|
+
tools: Read, Write, Edit, Bash, Grep, Glob
|
|
1032
|
+
model: haiku
|
|
1033
|
+
---
|
|
1034
|
+
|
|
1035
|
+
# Unused Code Cleaner Agent
|
|
1036
|
+
|
|
1037
|
+
Detects and removes unused code:
|
|
1038
|
+
- Unused imports
|
|
1039
|
+
- Unused functions and classes
|
|
1040
|
+
- Unused variables
|
|
1041
|
+
- Dead code paths
|
|
1042
|
+
- Console.log statements (optional)
|
|
1043
|
+
- Comments (preserves JSDoc)
|
|
1044
|
+
|
|
1045
|
+
Use after refactoring, when removing features, or before production deployment.
|
|
1046
|
+
`,
|
|
1047
|
+
"code-cleaner.md": `---
|
|
1048
|
+
name: code-cleaner
|
|
1049
|
+
description: Fast removal of comments, console.logs, and debug code while preserving JSDoc
|
|
1050
|
+
tools: Read, Edit, Glob, Grep
|
|
1051
|
+
model: haiku
|
|
1052
|
+
---
|
|
1053
|
+
|
|
1054
|
+
# Code Cleaner Agent
|
|
1055
|
+
|
|
1056
|
+
Fast cleanup of TypeScript/JavaScript files:
|
|
1057
|
+
|
|
1058
|
+
## Removes
|
|
1059
|
+
- Line comments (\`//\`)
|
|
1060
|
+
- Block comments (\`/* */\`)
|
|
1061
|
+
- \`console.log\` statements
|
|
1062
|
+
|
|
1063
|
+
## Preserves
|
|
1064
|
+
- JSDoc comments (\`/** */\`)
|
|
1065
|
+
- \`console.error\`, \`console.warn\`, \`console.info\`
|
|
1066
|
+
`,
|
|
1067
|
+
"i18n-locale-translator.md": `---
|
|
1068
|
+
name: i18n-locale-translator
|
|
1069
|
+
description: Translate UI text content into English (en) and Japanese (jp) using i18n locale system
|
|
1070
|
+
tools: Read, Write, Edit, Glob, Grep
|
|
1071
|
+
model: sonnet
|
|
1072
|
+
---
|
|
1073
|
+
|
|
1074
|
+
# i18n Locale Translator Agent
|
|
1075
|
+
|
|
1076
|
+
Handles internationalization tasks:
|
|
1077
|
+
- Extract hardcoded text from components
|
|
1078
|
+
- Create translation keys in locale files
|
|
1079
|
+
- Translate content to English and Japanese
|
|
1080
|
+
- Update components to use translation hooks
|
|
1081
|
+
|
|
1082
|
+
Use when adding new features or internationalizing existing hardcoded text.
|
|
1083
|
+
`,
|
|
1084
|
+
"storybook-maintainer.md": `---
|
|
1085
|
+
name: storybook-maintainer
|
|
1086
|
+
description: Create and update Storybook stories for UI components
|
|
1087
|
+
tools: Read, Write, Edit, Glob, Grep
|
|
1088
|
+
model: haiku
|
|
1089
|
+
---
|
|
1090
|
+
|
|
1091
|
+
# Storybook Maintainer Agent
|
|
1092
|
+
|
|
1093
|
+
Manages Storybook stories for UI components:
|
|
1094
|
+
- Analyze component props and variants
|
|
1095
|
+
- Create comprehensive story files
|
|
1096
|
+
- Document component usage
|
|
1097
|
+
- Add controls for interactive props
|
|
1098
|
+
|
|
1099
|
+
Use when adding new components or when existing components change significantly.
|
|
985
1100
|
`,
|
|
986
1101
|
};
|
|
987
1102
|
|
|
@@ -993,93 +1108,129 @@ Updates \`_AUDIT/PERFORMANCE.md\` with results.
|
|
|
993
1108
|
async function createCommands(cwd, answers) {
|
|
994
1109
|
const pm = answers.packageManager || "npm";
|
|
995
1110
|
|
|
996
|
-
// Build Storybook deployment steps if enabled
|
|
997
1111
|
const storybookSteps = answers.includeStorybook
|
|
998
1112
|
? `
|
|
999
1113
|
|
|
1000
|
-
### Step
|
|
1114
|
+
### Step 9: Deploy Storybook to Netlify
|
|
1001
1115
|
|
|
1002
|
-
|
|
1003
|
-
\`\`\`bash
|
|
1004
|
-
${pm} run build-storybook
|
|
1005
|
-
\`\`\`
|
|
1006
|
-
|
|
1007
|
-
Deploy to Netlify (requires NETLIFY_AUTH_TOKEN and NETLIFY_STORYBOOK_SITE_ID in .claude/settings.local.json):
|
|
1116
|
+
Deploy the Storybook documentation site:
|
|
1008
1117
|
\`\`\`bash
|
|
1009
1118
|
npx netlify deploy --dir=storybook-static --site=$NETLIFY_STORYBOOK_SITE_ID --prod
|
|
1010
1119
|
\`\`\`
|
|
1011
1120
|
|
|
1121
|
+
Note: Requires \`NETLIFY_AUTH_TOKEN\` and \`NETLIFY_STORYBOOK_SITE_ID\` in \`.claude/settings.local.json\`.
|
|
1122
|
+
If not configured, skip this step and inform the user to add the env vars.
|
|
1123
|
+
|
|
1012
1124
|
Storybook URL: https://${answers.storybookUrl || "storybook.example.com"}
|
|
1013
1125
|
${answers.passwordProtect ? "**Note:** This Storybook is password protected." : ""}
|
|
1014
1126
|
`
|
|
1015
1127
|
: "";
|
|
1016
1128
|
|
|
1017
|
-
const finalStep = answers.includeStorybook
|
|
1018
|
-
? "### Step 7: Report Success"
|
|
1019
|
-
: "### Step 6: Report Success";
|
|
1020
|
-
const reportContent = answers.includeStorybook
|
|
1021
|
-
? `
|
|
1022
|
-
Show summary:
|
|
1023
|
-
- Files changed
|
|
1024
|
-
- Commit hash
|
|
1025
|
-
- Push status
|
|
1026
|
-
- Storybook deploy status
|
|
1027
|
-
`
|
|
1028
|
-
: `
|
|
1029
|
-
Show summary:
|
|
1030
|
-
- Files changed
|
|
1031
|
-
- Commit hash
|
|
1032
|
-
- Push status
|
|
1033
|
-
`;
|
|
1034
|
-
|
|
1035
1129
|
const pushCommand = `---
|
|
1036
|
-
description: Clean, lint, test, build, commit,
|
|
1037
|
-
|
|
1130
|
+
description: Clean, stage, lint, test, build, commit, push, and update metrics
|
|
1131
|
+
argument-hint: [optional: commit message override]
|
|
1132
|
+
allowed-tools: Bash(find:*), Bash(git:*), Bash(${pm}:*), Bash(npx:*)${answers.includeStorybook ? ", Bash(npx netlify:*)" : ""}, Task
|
|
1038
1133
|
---
|
|
1039
1134
|
|
|
1040
1135
|
# Push Command
|
|
1041
1136
|
|
|
1042
|
-
Run quality gates, commit changes, and push to remote
|
|
1137
|
+
Run code cleanup, all quality gates, commit changes, and push to remote.
|
|
1043
1138
|
|
|
1044
1139
|
## Workflow
|
|
1045
1140
|
|
|
1046
1141
|
Execute these steps in order. **Stop immediately if any step fails.**
|
|
1047
1142
|
|
|
1048
|
-
### Step 1:
|
|
1143
|
+
### Step 1: Clean Up System Files
|
|
1144
|
+
Remove any .DS_Store files from the repository:
|
|
1049
1145
|
\`\`\`bash
|
|
1050
|
-
|
|
1146
|
+
find . -name '.DS_Store' -type f -delete
|
|
1147
|
+
\`\`\`
|
|
1148
|
+
|
|
1149
|
+
### Step 2: Sync Packages
|
|
1150
|
+
Clean and reinstall node_modules to ensure package-lock.json stays in sync:
|
|
1151
|
+
\`\`\`bash
|
|
1152
|
+
rm -rf node_modules && ${pm} install
|
|
1051
1153
|
\`\`\`
|
|
1154
|
+
This prevents \`${pm} ci\` failures in CI/CD due to lock file drift.
|
|
1052
1155
|
|
|
1053
|
-
|
|
1054
|
-
Run \`git status\` to verify there are staged changes. If nothing to commit, stop.
|
|
1156
|
+
If package-lock.json was modified, it will be staged in the next step.
|
|
1055
1157
|
|
|
1056
|
-
### Step 3:
|
|
1158
|
+
### Step 3: Stage All Changes
|
|
1159
|
+
\`\`\`bash
|
|
1160
|
+
git add -A
|
|
1161
|
+
\`\`\`
|
|
1057
1162
|
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
3. **Build**: \`${answers.buildCommand}\`
|
|
1163
|
+
### Step 4: Check for Changes
|
|
1164
|
+
Run \`git status\` to verify there are staged changes. If nothing to commit, inform the user and stop.
|
|
1061
1165
|
|
|
1062
|
-
### Step
|
|
1166
|
+
### Step 5: Clean Code
|
|
1063
1167
|
|
|
1064
|
-
|
|
1065
|
-
- Starts with a type (feat, fix, refactor, docs, style, test, chore)
|
|
1066
|
-
- Summarizes the "why" not the "what"
|
|
1067
|
-
- 1-2 sentences maximum
|
|
1168
|
+
Run the code-cleaner agent on staged TypeScript files to remove comments and console.logs.
|
|
1068
1169
|
|
|
1069
|
-
|
|
1170
|
+
This removes:
|
|
1171
|
+
- Line comments (\`//\`) and block comments (\`/* */\`)
|
|
1172
|
+
- \`console.log\` statements
|
|
1070
1173
|
|
|
1071
|
-
|
|
1174
|
+
It preserves:
|
|
1175
|
+
- JSDoc comments (\`/** */\`)
|
|
1176
|
+
- \`console.error\`, \`console.warn\`, \`console.info\`
|
|
1072
1177
|
|
|
1178
|
+
After cleaning, re-stage the modified files:
|
|
1179
|
+
\`\`\`bash
|
|
1180
|
+
git add -A
|
|
1073
1181
|
\`\`\`
|
|
1074
|
-
๐ฝ Generated with [Claude Code](https://claude.com/claude-code)
|
|
1075
1182
|
|
|
1076
|
-
|
|
1183
|
+
### Step 6: Run Quality Gates (in order)
|
|
1184
|
+
|
|
1185
|
+
Run each check. If any fails, stop and report which check failed:
|
|
1186
|
+
|
|
1187
|
+
1. **Lint**: \`${answers.lintCommand}\`${answers.includeStorybook ? `\n2. **Storybook**: \`${pm} run build-storybook\`` : ""}
|
|
1188
|
+
${answers.includeStorybook ? "3" : "2"}. **Unit Tests**: \`${answers.testCommand}\`
|
|
1189
|
+
${answers.includeStorybook ? "4" : "3"}. **Build**: \`${answers.buildCommand}\`
|
|
1190
|
+
|
|
1191
|
+
### Step 7: Generate Commit Message
|
|
1192
|
+
|
|
1193
|
+
If \`$ARGUMENTS\` is provided, use it as the commit message.
|
|
1194
|
+
|
|
1195
|
+
Otherwise, analyze the staged changes:
|
|
1196
|
+
1. Run \`git diff --cached --stat\` to see changed files
|
|
1197
|
+
2. Run \`git diff --cached\` to see actual changes
|
|
1198
|
+
3. Run \`git log -5 --oneline\` to match the repository's commit style
|
|
1199
|
+
4. Generate a concise, descriptive commit message that:
|
|
1200
|
+
- Starts with a type (feat, fix, refactor, docs, style, test, chore)
|
|
1201
|
+
- Summarizes the "why" not the "what"
|
|
1202
|
+
- Is 1-2 sentences maximum
|
|
1203
|
+
|
|
1204
|
+
### Step 8: Commit and Push
|
|
1205
|
+
|
|
1206
|
+
Create the commit with the message, including the standard footer:
|
|
1207
|
+
|
|
1208
|
+
\`\`\`
|
|
1209
|
+
๐ฝ Generated with FARMWORK
|
|
1077
1210
|
\`\`\`
|
|
1078
1211
|
|
|
1079
|
-
Then push
|
|
1212
|
+
Then push to remote:
|
|
1213
|
+
\`\`\`bash
|
|
1214
|
+
git push
|
|
1215
|
+
\`\`\`
|
|
1080
1216
|
${storybookSteps}
|
|
1081
|
-
|
|
1082
|
-
|
|
1217
|
+
### Step 10: Update Farmhouse Metrics
|
|
1218
|
+
|
|
1219
|
+
Run the-farmer agent to update \`_AUDIT/FARMHOUSE.md\` with current metrics:
|
|
1220
|
+
- Commands and agents inventory
|
|
1221
|
+
- Test counts (unit, e2e)
|
|
1222
|
+
- Completed issues count
|
|
1223
|
+
|
|
1224
|
+
This keeps the harness documentation in sync with the codebase.
|
|
1225
|
+
|
|
1226
|
+
### Step 11: Report Success
|
|
1227
|
+
|
|
1228
|
+
Show a summary:
|
|
1229
|
+
- Files changed
|
|
1230
|
+
- Commit hash
|
|
1231
|
+
- Push status${answers.includeStorybook ? "\n- Storybook deploy status (if deployed)" : ""}
|
|
1232
|
+
- Harness metrics updated
|
|
1233
|
+
`;
|
|
1083
1234
|
|
|
1084
1235
|
await fs.writeFile(
|
|
1085
1236
|
path.join(cwd, ".claude", "commands", "push.md"),
|
|
@@ -1088,20 +1239,6 @@ ${reportContent}`;
|
|
|
1088
1239
|
}
|
|
1089
1240
|
|
|
1090
1241
|
async function createSettings(cwd, answers) {
|
|
1091
|
-
const settings = {
|
|
1092
|
-
permissions: {
|
|
1093
|
-
allow: [],
|
|
1094
|
-
deny: [],
|
|
1095
|
-
ask: [],
|
|
1096
|
-
},
|
|
1097
|
-
};
|
|
1098
|
-
|
|
1099
|
-
await fs.writeFile(
|
|
1100
|
-
path.join(cwd, ".claude", "settings.json"),
|
|
1101
|
-
JSON.stringify(settings, null, 2),
|
|
1102
|
-
);
|
|
1103
|
-
|
|
1104
|
-
// Create local settings for sensitive data (gitignored)
|
|
1105
1242
|
if (answers.includeStorybook && answers.netlifyAuthToken) {
|
|
1106
1243
|
const localSettings = {
|
|
1107
1244
|
env: {
|
|
@@ -1115,7 +1252,6 @@ async function createSettings(cwd, answers) {
|
|
|
1115
1252
|
JSON.stringify(localSettings, null, 2),
|
|
1116
1253
|
);
|
|
1117
1254
|
|
|
1118
|
-
// Ensure settings.local.json is gitignored
|
|
1119
1255
|
const gitignorePath = path.join(cwd, ".gitignore");
|
|
1120
1256
|
let gitignoreContent = "";
|
|
1121
1257
|
try {
|
|
@@ -1155,10 +1291,9 @@ async function createProduceConfig(cwd, answers) {
|
|
|
1155
1291
|
storybook: answers.includeStorybook || false,
|
|
1156
1292
|
i18n: answers.includeI18n || false,
|
|
1157
1293
|
},
|
|
1158
|
-
audits: ["FARMHOUSE", "SECURITY", "PERFORMANCE", "CODE_QUALITY", "TESTS"],
|
|
1294
|
+
audits: ["FARMHOUSE", "SECURITY", "PERFORMANCE", "ACCESSIBILITY", "CODE_QUALITY", "TESTS"],
|
|
1159
1295
|
};
|
|
1160
1296
|
|
|
1161
|
-
// Add Storybook configuration if enabled
|
|
1162
1297
|
if (answers.includeStorybook) {
|
|
1163
1298
|
config.storybook = {
|
|
1164
1299
|
url: answers.storybookUrl || null,
|