farmwork 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,180 @@
1
+ # Farmwork
2
+
3
+ > Agentic development harness for AI-assisted projects
4
+
5
+ Farmwork is a framework that transforms AI coding assistants from reactive tools into proactive development partners. The name is a play on "framework" - because building software should feel like tending a well-organized farm.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install -g farmwork
11
+ ```
12
+
13
+ Or run directly with npx:
14
+
15
+ ```bash
16
+ npx farmwork init
17
+ ```
18
+
19
+ ## Quick Start
20
+
21
+ ```bash
22
+ # Initialize in your project
23
+ cd your-project
24
+ farmwork init
25
+
26
+ # Check your setup
27
+ farmwork doctor
28
+
29
+ # View framework status
30
+ farmwork status
31
+ ```
32
+
33
+ ## Commands
34
+
35
+ ### `farmwork init`
36
+
37
+ Initialize the Farmwork framework in your current directory. Runs an interactive setup wizard to configure your project for any tech stack.
38
+
39
+ ```bash
40
+ farmwork init # Interactive setup wizard
41
+ farmwork init -f # Force overwrite existing files
42
+ ```
43
+
44
+ **Options:**
45
+ - `-f, --force` - Overwrite existing files
46
+
47
+ **Storybook Support:**
48
+ If you enable Storybook (for React/Vue projects), the wizard will also ask for:
49
+ - Storybook URL (e.g., storybook.yoursite.com)
50
+ - Netlify Auth Token (for deployment)
51
+ - Netlify Site ID
52
+ - Password protection preference (recommended)
53
+
54
+ **Creates:**
55
+ - `CLAUDE.md` - Main instructions and phrase commands
56
+ - `.claude/` - Claude Code configuration directory
57
+ - `settings.json` - Project settings
58
+ - `agents/` - Specialized subagents
59
+ - `commands/` - User-invocable skills
60
+ - `_AUDIT/` - Living audit documents
61
+ - `FARMHOUSE.md` - Framework command center
62
+ - `_PLANS/` - Implementation plans directory
63
+ - `justfile` - Navigation and task commands
64
+
65
+ ### `farmwork add <type> <name>`
66
+
67
+ Add a new component to your Farmwork setup.
68
+
69
+ ```bash
70
+ farmwork add agent code-reviewer # Add a new agent
71
+ farmwork add command deploy # Add a new command
72
+ farmwork add audit performance # Add a new audit document
73
+ ```
74
+
75
+ **Types:**
76
+ - `agent` - Creates `.claude/agents/<name>.md`
77
+ - `command` - Creates `.claude/commands/<name>.md`
78
+ - `audit` - Creates `_AUDIT/<NAME>.md`
79
+
80
+ ### `farmwork status`
81
+
82
+ Display Farmwork status and metrics.
83
+
84
+ ```bash
85
+ farmwork status
86
+ ```
87
+
88
+ **Shows:**
89
+ - Component counts (agents, commands, audits, plans)
90
+ - Issue tracking status (if beads is configured)
91
+ - FARMHOUSE score and open items
92
+ - Configuration file status
93
+ - Project metrics (tests, stories)
94
+
95
+ ### `farmwork doctor`
96
+
97
+ Check your Farmwork setup and diagnose issues.
98
+
99
+ ```bash
100
+ farmwork doctor
101
+ ```
102
+
103
+ **Checks:**
104
+ - Core files (CLAUDE.md, .claude/, settings)
105
+ - Agents and commands configuration
106
+ - Audit system (_AUDIT/, FARMHOUSE.md, _PLANS/)
107
+ - Navigation (justfile, just command)
108
+ - Issue tracking (beads)
109
+ - Security (.gitignore settings)
110
+
111
+ ## The Farmwork Method
112
+
113
+ ### Core Concepts
114
+
115
+ 1. **FARMHOUSE.md** - Central command for tracking framework metrics
116
+ 2. **Phrase Commands** - Natural language triggers for workflows
117
+ 3. **Agents** - Specialized AI subagents for specific tasks
118
+ 4. **Commands** - User-invocable skills (triggered with `/command`)
119
+ 5. **Issue Tracking** - Using beads (`bd`) for full visibility
120
+ 6. **Living Audits** - Documents that track ongoing concerns
121
+
122
+ ### Phrase Commands
123
+
124
+ **Farmwork Phrases** (Development Workflow):
125
+ - `till the land` - Audit systems, update metrics
126
+ - `inspect the farm` - Full inspection (code review, performance, security, quality)
127
+ - `go to market` - i18n translation check
128
+ - `harvest crops` - Full push workflow
129
+
130
+ **Plan Phrases**:
131
+ - `make a plan for...` - Create implementation plan
132
+ - `let's implement...` - Execute plan with issue tracking
133
+
134
+ ### Recommended Workflow
135
+
136
+ 1. **Start Session**: Run `till the land` to audit current state
137
+ 2. **Plan Work**: Use `make a plan for...` for new features
138
+ 3. **Implement**: Use `let's implement...` to execute with tracking
139
+ 4. **Quality Check**: Run `inspect the farm`
140
+ 5. **Ship**: Run `harvest crops` to push changes
141
+
142
+ ## Directory Structure
143
+
144
+ ```
145
+ your-project/
146
+ ā”œā”€ā”€ CLAUDE.md # Main instructions & phrase commands
147
+ ā”œā”€ā”€ .claude/ # Claude Code configuration
148
+ │ ā”œā”€ā”€ settings.json # Project settings
149
+ │ ā”œā”€ā”€ agents/ # Specialized subagents
150
+ │ │ ā”œā”€ā”€ code-reviewer.md
151
+ │ │ ā”œā”€ā”€ security-auditor.md
152
+ │ │ └── ...
153
+ │ └── commands/ # User-invocable skills
154
+ │ ā”œā”€ā”€ push.md
155
+ │ └── ...
156
+ ā”œā”€ā”€ _AUDIT/ # Living audit documents
157
+ │ ā”œā”€ā”€ FARMHOUSE.md # Framework command center
158
+ │ ā”œā”€ā”€ SECURITY.md
159
+ │ ā”œā”€ā”€ PERFORMANCE.md
160
+ │ └── ...
161
+ ā”œā”€ā”€ _PLANS/ # Implementation plans
162
+ │ └── FEATURE_NAME.md
163
+ ā”œā”€ā”€ .beads/ # Issue tracking (optional)
164
+ └── justfile # Navigation commands
165
+ ```
166
+
167
+ ## Requirements
168
+
169
+ - Node.js 18+
170
+ - [just](https://github.com/casey/just) (recommended for navigation)
171
+ - [beads](https://github.com/steveyegge/beads) (optional, for issue tracking)
172
+
173
+ ## License
174
+
175
+ MIT
176
+
177
+ ## Links
178
+
179
+ - [Farmwork Documentation](https://farmwork.wynter.ai)
180
+ - [GitHub Repository](https://github.com/wynterjones/farmwork)
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { program } from 'commander';
4
+ import { init } from '../src/init.js';
5
+ import { add } from '../src/add.js';
6
+ import { status } from '../src/status.js';
7
+ import { doctor } from '../src/doctor.js';
8
+ import chalk from 'chalk';
9
+
10
+ const VERSION = '1.0.0';
11
+
12
+ console.log(chalk.green(`
13
+ ╔═════════════════════════════════════╗
14
+ ā•‘ 🌾 Farmwork CLI v${VERSION} ā•‘
15
+ ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•
16
+ `));
17
+
18
+ program
19
+ .name('farmwork')
20
+ .description('Farmwork - Agentic development harness for AI-assisted projects')
21
+ .version(VERSION);
22
+
23
+ program
24
+ .command('init')
25
+ .description('Initialize Farmwork in current directory')
26
+ .option('-f, --force', 'Overwrite existing files')
27
+ .action(init);
28
+
29
+ program
30
+ .command('add <type> <name>')
31
+ .description('Add a component (agent, command, audit)')
32
+ .action(add);
33
+
34
+ program
35
+ .command('status')
36
+ .description('Show Farmwork status and metrics')
37
+ .action(status);
38
+
39
+ program
40
+ .command('doctor')
41
+ .description('Check Farmwork setup and diagnose issues')
42
+ .action(doctor);
43
+
44
+ program.parse();
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "farmwork",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "description": "Farmwork - Agentic development harness for AI-assisted projects",
6
+ "main": "src/index.js",
7
+ "bin": {
8
+ "farmwork": "./bin/farmwork.js"
9
+ },
10
+ "scripts": {
11
+ "test": "node --test",
12
+ "lint": "eslint src/"
13
+ },
14
+ "keywords": [
15
+ "cli",
16
+ "ai",
17
+ "claude",
18
+ "development",
19
+ "workflow",
20
+ "automation",
21
+ "agentic"
22
+ ],
23
+ "author": "Wynter Jones",
24
+ "license": "MIT",
25
+ "dependencies": {
26
+ "chalk": "^5.3.0",
27
+ "commander": "^12.1.0",
28
+ "inquirer": "^9.2.12",
29
+ "ora": "^8.0.1",
30
+ "fs-extra": "^11.2.0"
31
+ },
32
+ "engines": {
33
+ "node": ">=18.0.0"
34
+ },
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "https://github.com/wynterjones/farmwork"
38
+ },
39
+ "homepage": "https://farmwork.wynter.ai"
40
+ }
package/src/add.js ADDED
@@ -0,0 +1,194 @@
1
+ import fs from "fs-extra";
2
+ import path from "path";
3
+ import chalk from "chalk";
4
+ import ora from "ora";
5
+
6
+ const TEMPLATES = {
7
+ agent: (name, description) => `# ${name} Agent
8
+
9
+ > ${description}
10
+
11
+ ## Purpose
12
+
13
+ [Describe what this agent does and when to use it]
14
+
15
+ ## Triggers
16
+
17
+ This agent is launched via the Task tool with \`subagent_type="${name}"\`.
18
+
19
+ ## Workflow
20
+
21
+ 1. [First step]
22
+ 2. [Second step]
23
+ 3. [Third step]
24
+
25
+ ## Output
26
+
27
+ [Describe what the agent returns or produces]
28
+
29
+ ## Examples
30
+
31
+ \`\`\`
32
+ # Example usage
33
+ Task tool with subagent_type="${name}"
34
+ Prompt: "[Example prompt]"
35
+ \`\`\`
36
+ `,
37
+
38
+ command: (
39
+ name,
40
+ description,
41
+ ) => `# ${name.charAt(0).toUpperCase() + name.slice(1)} Command
42
+
43
+ ${description}
44
+
45
+ ## Workflow
46
+
47
+ Execute these steps in order. **Stop immediately if any step fails.**
48
+
49
+ ### Step 1: [First Step]
50
+
51
+ \`\`\`bash
52
+ # Command to run
53
+ \`\`\`
54
+
55
+ ### Step 2: [Second Step]
56
+
57
+ \`\`\`bash
58
+ # Command to run
59
+ \`\`\`
60
+
61
+ ### Step 3: Report Results
62
+
63
+ Show a summary of what was accomplished.
64
+ `,
65
+
66
+ audit: (name) => `# ${name.charAt(0).toUpperCase() + name.slice(1)} Audit
67
+
68
+ > [One-line description of this audit area]
69
+
70
+ **Last Updated:** ${new Date().toISOString().split("T")[0]}
71
+ **Score:** 0.0/10
72
+ **Status:** 0 open items
73
+
74
+ ---
75
+
76
+ ## How to get 10/10
77
+
78
+ [One paragraph explaining what perfect looks like for this area]
79
+
80
+ ---
81
+
82
+ ## Constraints
83
+
84
+ | Constraint | Reason | Impact |
85
+ |------------|--------|--------|
86
+ | [Constraint 1] | [Why] | [What happens] |
87
+
88
+ ---
89
+
90
+ ## Open Items
91
+
92
+ _None currently_
93
+
94
+ ---
95
+
96
+ ## [Area-Specific Section]
97
+
98
+ [Content specific to this audit area]
99
+
100
+ ---
101
+
102
+ ## Audit History
103
+
104
+ | Date | Changes |
105
+ |------|---------|
106
+ | ${new Date().toISOString().split("T")[0]} | Initial audit document |
107
+ `,
108
+ };
109
+
110
+ export async function add(type, name, options) {
111
+ const cwd = process.cwd();
112
+ const spinner = ora();
113
+
114
+ const validTypes = ["agent", "command", "audit"];
115
+ if (!validTypes.includes(type)) {
116
+ console.log(chalk.red(`\nāŒ Invalid type: ${type}`));
117
+ console.log(chalk.gray(` Valid types: ${validTypes.join(", ")}`));
118
+ return;
119
+ }
120
+
121
+ if (!name) {
122
+ console.log(chalk.red("\nāŒ Name is required"));
123
+ console.log(chalk.gray(` Usage: produce add ${type} <name>`));
124
+ return;
125
+ }
126
+
127
+ const claudeDir = path.join(cwd, ".claude");
128
+ if (!fs.existsSync(claudeDir)) {
129
+ console.log(chalk.red("\nāŒ Farmwork not initialized"));
130
+ console.log(chalk.gray(" Run: produce init"));
131
+ return;
132
+ }
133
+
134
+ spinner.start(`Adding ${type}: ${name}`);
135
+
136
+ try {
137
+ let filePath;
138
+ let content;
139
+ let description = options?.description || `[Description for ${name}]`;
140
+
141
+ switch (type) {
142
+ case "agent":
143
+ filePath = path.join(claudeDir, "agents", `${name}.md`);
144
+ content = TEMPLATES.agent(name, description);
145
+ break;
146
+
147
+ case "command":
148
+ filePath = path.join(claudeDir, "commands", `${name}.md`);
149
+ content = TEMPLATES.command(name, description);
150
+ break;
151
+
152
+ case "audit":
153
+ const auditDir = path.join(cwd, "_AUDIT");
154
+ if (!fs.existsSync(auditDir)) {
155
+ fs.mkdirSync(auditDir, { recursive: true });
156
+ }
157
+ filePath = path.join(auditDir, `${name.toUpperCase()}.md`);
158
+ content = TEMPLATES.audit(name);
159
+ break;
160
+ }
161
+
162
+ if (fs.existsSync(filePath)) {
163
+ spinner.fail(`${type} already exists: ${name}`);
164
+ console.log(chalk.gray(` File: ${filePath}`));
165
+ return;
166
+ }
167
+
168
+ const dir = path.dirname(filePath);
169
+ if (!fs.existsSync(dir)) {
170
+ fs.mkdirSync(dir, { recursive: true });
171
+ }
172
+
173
+ fs.writeFileSync(filePath, content);
174
+ spinner.succeed(`Added ${type}: ${name}`);
175
+
176
+ console.log(chalk.gray(`\n File: ${filePath}`));
177
+ console.log(chalk.cyan(`\n Edit the file to customize the ${type}.`));
178
+
179
+ if (type === "agent") {
180
+ console.log(
181
+ chalk.gray(`\n Launch with: Task tool, subagent_type="${name}"`),
182
+ );
183
+ } else if (type === "command") {
184
+ console.log(chalk.gray(`\n Invoke with: /${name}`));
185
+ } else if (type === "audit") {
186
+ console.log(
187
+ chalk.gray(`\n Update FARMHOUSE.md to reference this audit.`),
188
+ );
189
+ }
190
+ } catch (error) {
191
+ spinner.fail(`Failed to add ${type}`);
192
+ console.log(chalk.red(` ${error.message}`));
193
+ }
194
+ }
package/src/doctor.js ADDED
@@ -0,0 +1,244 @@
1
+ import fs from "fs-extra";
2
+ import path from "path";
3
+ import chalk from "chalk";
4
+ import { execSync } from "child_process";
5
+
6
+ function checkExists(filePath, description) {
7
+ const exists = fs.existsSync(filePath);
8
+ return {
9
+ passed: exists,
10
+ message: description,
11
+ details: exists ? null : `Missing: ${filePath}`,
12
+ };
13
+ }
14
+
15
+ function checkCommand(command, description) {
16
+ try {
17
+ execSync(`which ${command}`, { encoding: "utf8", stdio: "pipe" });
18
+ return { passed: true, message: description, details: null };
19
+ } catch {
20
+ return {
21
+ passed: false,
22
+ message: description,
23
+ details: `Command not found: ${command}`,
24
+ };
25
+ }
26
+ }
27
+
28
+ function checkDirectoryNotEmpty(dir, description) {
29
+ if (!fs.existsSync(dir)) {
30
+ return {
31
+ passed: false,
32
+ message: description,
33
+ details: `Directory missing: ${dir}`,
34
+ };
35
+ }
36
+ const files = fs.readdirSync(dir);
37
+ const hasContent = files.length > 0;
38
+ return {
39
+ passed: hasContent,
40
+ message: description,
41
+ details: hasContent ? null : `Directory empty: ${dir}`,
42
+ };
43
+ }
44
+
45
+ function checkClaudeMdSections(claudeMdPath) {
46
+ if (!fs.existsSync(claudeMdPath)) {
47
+ return {
48
+ passed: false,
49
+ message: "CLAUDE.md has required sections",
50
+ details: "File missing",
51
+ };
52
+ }
53
+
54
+ const content = fs.readFileSync(claudeMdPath, "utf8");
55
+ const requiredSections = [
56
+ "Phrase Commands",
57
+ "Issue Tracking",
58
+ "Claude Code Commands",
59
+ ];
60
+
61
+ const missing = requiredSections.filter(
62
+ (section) => !content.includes(section),
63
+ );
64
+
65
+ return {
66
+ passed: missing.length === 0,
67
+ message: "CLAUDE.md has required sections",
68
+ details:
69
+ missing.length > 0 ? `Missing sections: ${missing.join(", ")}` : null,
70
+ };
71
+ }
72
+
73
+ function checkFarmhouseFormat(farmhousePath) {
74
+ if (!fs.existsSync(farmhousePath)) {
75
+ return {
76
+ passed: false,
77
+ message: "FARMHOUSE.md follows format",
78
+ details: "File missing",
79
+ };
80
+ }
81
+
82
+ const content = fs.readFileSync(farmhousePath, "utf8");
83
+ const requiredFields = ["**Last Updated:**", "**Score:**", "**Status:**"];
84
+
85
+ const missing = requiredFields.filter((field) => !content.includes(field));
86
+
87
+ return {
88
+ passed: missing.length === 0,
89
+ message: "FARMHOUSE.md follows format",
90
+ details:
91
+ missing.length > 0 ? `Missing fields: ${missing.join(", ")}` : null,
92
+ };
93
+ }
94
+
95
+ function checkGitignore(cwd) {
96
+ const gitignorePath = path.join(cwd, ".gitignore");
97
+ if (!fs.existsSync(gitignorePath)) {
98
+ return {
99
+ passed: false,
100
+ message: ".gitignore includes local settings",
101
+ details: "File missing",
102
+ };
103
+ }
104
+
105
+ const content = fs.readFileSync(gitignorePath, "utf8");
106
+ const hasLocalSettings = content.includes("settings.local.json");
107
+
108
+ return {
109
+ passed: hasLocalSettings,
110
+ message: ".gitignore includes local settings",
111
+ details: hasLocalSettings ? null : "Add: .claude/settings.local.json",
112
+ };
113
+ }
114
+
115
+ export async function doctor() {
116
+ const cwd = process.cwd();
117
+
118
+ console.log(chalk.cyan("\n🩺 Farmwork Doctor\n"));
119
+ console.log(chalk.gray("Checking your Farmwork setup...\n"));
120
+
121
+ const checks = [];
122
+
123
+ checks.push({ category: "Core Files", items: [] });
124
+ checks[0].items.push(
125
+ checkExists(path.join(cwd, "CLAUDE.md"), "CLAUDE.md exists"),
126
+ );
127
+ checks[0].items.push(
128
+ checkExists(path.join(cwd, ".claude"), ".claude/ directory exists"),
129
+ );
130
+ checks[0].items.push(
131
+ checkExists(
132
+ path.join(cwd, ".claude", "settings.json"),
133
+ "settings.json exists",
134
+ ),
135
+ );
136
+ checks[0].items.push(checkClaudeMdSections(path.join(cwd, "CLAUDE.md")));
137
+
138
+ checks.push({ category: "Agents & Commands", items: [] });
139
+ checks[1].items.push(
140
+ checkDirectoryNotEmpty(
141
+ path.join(cwd, ".claude", "agents"),
142
+ "Has agents defined",
143
+ ),
144
+ );
145
+ checks[1].items.push(
146
+ checkDirectoryNotEmpty(
147
+ path.join(cwd, ".claude", "commands"),
148
+ "Has commands defined",
149
+ ),
150
+ );
151
+
152
+ checks.push({ category: "Audit System", items: [] });
153
+ checks[2].items.push(
154
+ checkExists(path.join(cwd, "_AUDIT"), "_AUDIT/ directory exists"),
155
+ );
156
+ checks[2].items.push(
157
+ checkExists(
158
+ path.join(cwd, "_AUDIT", "FARMHOUSE.md"),
159
+ "FARMHOUSE.md exists",
160
+ ),
161
+ );
162
+ checks[2].items.push(
163
+ checkFarmhouseFormat(path.join(cwd, "_AUDIT", "FARMHOUSE.md")),
164
+ );
165
+ checks[2].items.push(
166
+ checkExists(path.join(cwd, "_PLANS"), "_PLANS/ directory exists"),
167
+ );
168
+
169
+ checks.push({ category: "Navigation", items: [] });
170
+ checks[3].items.push(
171
+ checkExists(path.join(cwd, "justfile"), "justfile exists"),
172
+ );
173
+ checks[3].items.push(checkCommand("just", "just command available"));
174
+
175
+ checks.push({ category: "Issue Tracking", items: [] });
176
+ checks[4].items.push(
177
+ checkExists(path.join(cwd, ".beads"), ".beads/ directory exists"),
178
+ );
179
+ checks[4].items.push(checkCommand("bd", "bd (beads) command available"));
180
+
181
+ checks.push({ category: "Security", items: [] });
182
+ checks[5].items.push(checkGitignore(cwd));
183
+
184
+ let totalPassed = 0;
185
+ let totalFailed = 0;
186
+ let totalWarnings = 0;
187
+
188
+ for (const category of checks) {
189
+ console.log(chalk.bold(`${category.category}`));
190
+
191
+ for (const check of category.items) {
192
+ if (check.passed) {
193
+ console.log(chalk.green(` 🌱 ${check.message}`));
194
+ totalPassed++;
195
+ } else {
196
+ if (check.message.includes("beads") || check.message.includes("bd ")) {
197
+ console.log(chalk.yellow(` šŸ‹ ${check.message}`));
198
+ if (check.details) console.log(chalk.gray(` ${check.details}`));
199
+ totalWarnings++;
200
+ } else {
201
+ console.log(chalk.red(` šŸ‚ ${check.message}`));
202
+ if (check.details) console.log(chalk.gray(` ${check.details}`));
203
+ totalFailed++;
204
+ }
205
+ }
206
+ }
207
+ console.log();
208
+ }
209
+
210
+ console.log(chalk.bold("Summary"));
211
+ console.log(chalk.green(` 🌱 ${totalPassed} passed`));
212
+ if (totalWarnings > 0) {
213
+ console.log(chalk.yellow(` šŸ‹ ${totalWarnings} warnings (optional)`));
214
+ }
215
+ if (totalFailed > 0) {
216
+ console.log(chalk.red(` šŸ‚ ${totalFailed} failed`));
217
+ }
218
+
219
+ const health =
220
+ totalFailed === 0
221
+ ? totalWarnings === 0
222
+ ? "Excellent"
223
+ : "Good"
224
+ : totalFailed <= 2
225
+ ? "Needs Attention"
226
+ : "Critical";
227
+
228
+ const healthColor =
229
+ health === "Excellent"
230
+ ? chalk.green
231
+ : health === "Good"
232
+ ? chalk.cyan
233
+ : health === "Needs Attention"
234
+ ? chalk.yellow
235
+ : chalk.red;
236
+
237
+ console.log(`\n${chalk.bold("Health:")} ${healthColor(health)}`);
238
+
239
+ if (totalFailed > 0) {
240
+ console.log(chalk.gray("\nRun `farmwork init` to fix missing components."));
241
+ }
242
+
243
+ console.log();
244
+ }
package/src/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { init } from './init.js';
2
+ export { add } from './add.js';
3
+ export { status } from './status.js';
4
+ export { doctor } from './doctor.js';