forgecraft 1.0.0 → 1.2.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 +66 -14
- package/dist/{chunk-2HFEPXBV.js → chunk-P5CLBIGQ.js} +268 -39
- package/dist/chunk-P5CLBIGQ.js.map +1 -0
- package/dist/cli/index.js +78 -21
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +18 -0
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-2HFEPXBV.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<img src="https://img.shields.io/npm/v/
|
|
2
|
+
<img src="https://img.shields.io/npm/v/forgecraft?style=flat-square&color=000" alt="npm version" />
|
|
3
3
|
<img src="https://img.shields.io/badge/node-%3E%3D18-000?style=flat-square" alt="node version" />
|
|
4
4
|
<img src="https://img.shields.io/github/license/joeljohn159/forgeai?style=flat-square&color=000" alt="license" />
|
|
5
|
-
<img src="https://img.shields.io/badge/frameworks-Next.js%20%7C%20React%20%7C%20Django-000?style=flat-square" alt="frameworks" />
|
|
5
|
+
<img src="https://img.shields.io/badge/frameworks-Next.js%20%7C%20React%20%7C%20Django%20%7C%20Any-000?style=flat-square" alt="frameworks" />
|
|
6
6
|
</p>
|
|
7
7
|
|
|
8
8
|
<h1 align="center">ForgeAI</h1>
|
|
@@ -97,24 +97,33 @@ Each phase has a clear input, a clear output, and a gate between them. Stories t
|
|
|
97
97
|
# Prerequisites: Node 18+, Claude Code CLI (logged in)
|
|
98
98
|
|
|
99
99
|
# Install globally
|
|
100
|
-
npm install -g
|
|
100
|
+
npm install -g forgecraft
|
|
101
101
|
|
|
102
102
|
# Create a new project directory
|
|
103
103
|
mkdir my-app && cd my-app
|
|
104
104
|
|
|
105
|
-
# Initialize (choose Next.js, React+Vite, or
|
|
105
|
+
# Initialize (choose Next.js, React+Vite, Django, or any stack)
|
|
106
106
|
forge init
|
|
107
107
|
|
|
108
108
|
# Build something
|
|
109
109
|
forge auto "a task management app with projects, due dates, and team assignment"
|
|
110
110
|
|
|
111
|
+
# Full auto — skip all prompts
|
|
112
|
+
forge auto "an API with Express and MongoDB" --yes
|
|
113
|
+
|
|
114
|
+
# Attach references — drag & drop files into the terminal
|
|
115
|
+
forge auto "build this app" /path/to/mockup.png /path/to/spec.pdf
|
|
116
|
+
# Attachments:
|
|
117
|
+
# [Image1] mockup.png
|
|
118
|
+
# [Document1] spec.pdf
|
|
119
|
+
|
|
111
120
|
# Start the dev server
|
|
112
121
|
forge start
|
|
113
122
|
|
|
114
123
|
# If interrupted, resume where you left off
|
|
115
124
|
forge resume
|
|
116
125
|
|
|
117
|
-
# Fix a bug (
|
|
126
|
+
# Fix a bug (drag & drop screenshots or use --image)
|
|
118
127
|
forge fix "the sidebar overlaps on mobile" --image screenshot.png
|
|
119
128
|
|
|
120
129
|
# Push to GitHub manually (auto-push happens after forge auto)
|
|
@@ -150,6 +159,32 @@ Type a message at any point during the build. It gets queued and processed at th
|
|
|
150
159
|
"what's the database schema?" → Orchestrator answers directly
|
|
151
160
|
```
|
|
152
161
|
|
|
162
|
+
### Drag & Drop Attachments
|
|
163
|
+
Drag files directly into the terminal to attach mockups, specs, or screenshots. Forge detects the file paths, classifies them, and passes them to the agents as context.
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
# Drag & drop images and documents right into your prompt
|
|
167
|
+
forge auto "build this landing page" /Users/me/mockup.png /Users/me/requirements.pdf
|
|
168
|
+
|
|
169
|
+
Attachments:
|
|
170
|
+
[Image1] mockup.png
|
|
171
|
+
[Document1] requirements.pdf
|
|
172
|
+
|
|
173
|
+
# Also works with forge fix
|
|
174
|
+
forge fix "match this design" /Users/me/screenshot.png
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Supports images (`.png`, `.jpg`, `.svg`, `.webp`), documents (`.pdf`, `.doc`, `.txt`, `.md`, `.csv`, `.json`, `.yaml`), and design files (`.figma`, `.sketch`). Files are copied to `.forge/attachments/` so agents can reference them.
|
|
178
|
+
|
|
179
|
+
### Zero-Prompt Mode (`--yes`)
|
|
180
|
+
Skip every confirmation dialog — Claude Code permission prompts, review gates, resume confirmations. Fully hands-free.
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
forge auto "a blog with auth" --yes
|
|
184
|
+
forge auto "a dashboard" -y --skip-design --quiet
|
|
185
|
+
forge resume -y
|
|
186
|
+
```
|
|
187
|
+
|
|
153
188
|
### Screenshot-Based Bug Fixing
|
|
154
189
|
Pass a screenshot to `forge fix` and Claude reads the image to understand the visual issue:
|
|
155
190
|
|
|
@@ -183,9 +218,18 @@ ForgeAI isn't locked to one framework. Each adapter knows how to scaffold, build
|
|
|
183
218
|
| **Next.js** | TypeScript | Storybook | Stable |
|
|
184
219
|
| **React + Vite** | TypeScript | Storybook | Stable |
|
|
185
220
|
| **Django** | Python | Skipped | Stable |
|
|
186
|
-
|
|
|
221
|
+
| **Any Stack** | Auto-detect | Skipped | Stable |
|
|
187
222
|
|
|
188
|
-
|
|
223
|
+
The **"Other"** option in `forge init` enables a generic adapter that works with any tech stack — Express, Vue, Svelte, Go, Rust, FastAPI, Flutter, Rails, and more. The agent auto-detects your project's language, package manager, build commands, and file structure from existing config files. If starting from scratch, it scaffolds using the standard tooling for the detected stack.
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
# Works with anything
|
|
227
|
+
forge auto "a REST API with Express, Prisma, and PostgreSQL"
|
|
228
|
+
forge auto "a CLI tool in Rust that converts images to WebP"
|
|
229
|
+
forge auto "a FastAPI backend with SQLAlchemy and JWT auth"
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Worker prompts automatically adapt to the framework — Next.js gets App Router instructions, Vite gets SPA routing, Django gets migration commands, and generic projects get smart auto-detection.
|
|
189
233
|
|
|
190
234
|
### Sprint Resume
|
|
191
235
|
If a build gets interrupted (auth expires, network drops, you close the terminal), your progress is saved. Run `forge resume` to pick up exactly where you left off. Blocked stories can be retried.
|
|
@@ -201,10 +245,12 @@ Pass `--deploy` and ForgeAI configures `next.config` for static export and creat
|
|
|
201
245
|
|
|
202
246
|
```bash
|
|
203
247
|
forge auto "description" # Full autonomous pipeline (auto-pushes to GitHub)
|
|
248
|
+
forge auto "description" --yes # Skip all prompts (fully hands-free)
|
|
204
249
|
forge auto "description" --skip-design # Skip Storybook previews (faster)
|
|
205
250
|
forge auto "description" --deploy # Add GitHub Pages deployment
|
|
206
251
|
forge auto "description" --mute # No notification sounds
|
|
207
252
|
forge auto "description" --quiet # Spinners only, no tool details
|
|
253
|
+
forge auto "desc" file.png spec.pdf # Attach reference files (drag & drop)
|
|
208
254
|
```
|
|
209
255
|
|
|
210
256
|
### Step-by-Step Mode
|
|
@@ -286,6 +332,7 @@ src/
|
|
|
286
332
|
│ │ ├── nextjs.ts # Next.js adapter
|
|
287
333
|
│ │ ├── react-vite.ts # React + Vite adapter
|
|
288
334
|
│ │ ├── django.ts # Django adapter
|
|
335
|
+
│ │ ├── generic.ts # Generic adapter (any tech stack)
|
|
289
336
|
│ │ └── index.ts # Adapter registry + factory
|
|
290
337
|
│ │
|
|
291
338
|
│ ├── git/
|
|
@@ -295,6 +342,7 @@ src/
|
|
|
295
342
|
│ │ └── index.ts # GitHub Issues sync (via gh CLI)
|
|
296
343
|
│ │
|
|
297
344
|
│ └── utils/
|
|
345
|
+
│ ├── attachments.ts # Drag & drop file attachment parser
|
|
298
346
|
│ ├── config.ts # Config validation
|
|
299
347
|
│ └── sound.ts # Notification sounds (macOS/Linux/fallback)
|
|
300
348
|
│
|
|
@@ -379,9 +427,10 @@ After `forge init`, your `forge.config.json` controls:
|
|
|
379
427
|
| Git strategy | Manual | Manual | Manual | **Auto commits + tags + push** |
|
|
380
428
|
| Parallelism | N/A | N/A | N/A | **Dependency-grouped parallel** |
|
|
381
429
|
| Human oversight | Full control | None | Approve/reject | **Stage gates + live feedback** |
|
|
382
|
-
| Bug fix with screenshots | N/A | N/A | N/A | **forge fix --image** |
|
|
430
|
+
| Bug fix with screenshots | N/A | N/A | N/A | **forge fix --image + drag & drop** |
|
|
383
431
|
| SEO & assets | You remember | You remember | You remember | **Built-in defaults** |
|
|
384
|
-
| Multi-framework | N/A | Any | Any | **Next.js, React, Django** |
|
|
432
|
+
| Multi-framework | N/A | Any | Any | **Next.js, React, Django, Any** |
|
|
433
|
+
| File attachments | N/A | N/A | N/A | **Drag & drop mockups/specs** |
|
|
385
434
|
| Resume on failure | Start over | N/A | N/A | **Auto-save + resume** |
|
|
386
435
|
|
|
387
436
|
<br/>
|
|
@@ -394,7 +443,7 @@ After `forge init`, your `forge.config.json` controls:
|
|
|
394
443
|
|
|
395
444
|
**v0.3** — React + Vite and Django adapters, `forge map`, `forge resume`
|
|
396
445
|
|
|
397
|
-
**v1.0** —
|
|
446
|
+
**v1.0** — Initial Release
|
|
398
447
|
- 21 CLI commands covering the full development lifecycle
|
|
399
448
|
- 3 framework adapters (Next.js, React + Vite, Django)
|
|
400
449
|
- Framework-aware prompts via pluggable adapter system
|
|
@@ -406,10 +455,13 @@ After `forge init`, your `forge.config.json` controls:
|
|
|
406
455
|
- Config validation with actionable error messages
|
|
407
456
|
- Token expiry handling with auto-retry
|
|
408
457
|
|
|
409
|
-
**v1.1** —
|
|
410
|
-
- **
|
|
411
|
-
-
|
|
412
|
-
- **
|
|
458
|
+
**v1.1** — Current
|
|
459
|
+
- **Any tech stack support** — generic adapter auto-detects language, package manager, build commands, and project structure. Works with Express, Vue, Svelte, Go, Rust, FastAPI, Rails, and more.
|
|
460
|
+
- **`--yes` flag** — skip all confirmation prompts for fully hands-free operation
|
|
461
|
+
- **Drag & drop attachments** — attach mockups, screenshots, specs, and design files by dragging into the terminal. Supports images, PDFs, docs, and design files.
|
|
462
|
+
- Published as `forgecraft` on npm
|
|
463
|
+
|
|
464
|
+
**v1.2** — Next Up
|
|
413
465
|
- **Test generation phase** — auto-generate unit and integration tests after build (Vitest, pytest, Flutter test)
|
|
414
466
|
- **Custom adapter plugin API** — drop a JS file in `.forge/adapters/` to add your own framework
|
|
415
467
|
|
|
@@ -220,11 +220,68 @@ requirements.txt # Python dependencies
|
|
|
220
220
|
`.trim()
|
|
221
221
|
};
|
|
222
222
|
|
|
223
|
+
// src/core/adapters/generic.ts
|
|
224
|
+
var genericAdapter = {
|
|
225
|
+
id: "generic",
|
|
226
|
+
name: "Custom Stack",
|
|
227
|
+
language: "typescript",
|
|
228
|
+
// default, but prompts tell the agent to auto-detect
|
|
229
|
+
scaffoldCommands: [],
|
|
230
|
+
buildCommand: "AUTO_DETECT",
|
|
231
|
+
lintCommand: "AUTO_DETECT",
|
|
232
|
+
typecheckCommand: "AUTO_DETECT",
|
|
233
|
+
devCommand: "npm run dev",
|
|
234
|
+
devPort: 3e3,
|
|
235
|
+
designSupport: false,
|
|
236
|
+
packageManager: "npm",
|
|
237
|
+
requiredFiles: [],
|
|
238
|
+
buildPromptAdditions: `
|
|
239
|
+
CUSTOM TECH STACK \u2014 IMPORTANT:
|
|
240
|
+
This project uses a tech stack chosen by the user. You MUST auto-detect everything.
|
|
241
|
+
|
|
242
|
+
STEP 1 \u2014 DETECT THE STACK:
|
|
243
|
+
Before writing ANY code, read these files (whichever exist):
|
|
244
|
+
- package.json (Node.js/JS/TS projects)
|
|
245
|
+
- requirements.txt / pyproject.toml / Pipfile (Python projects)
|
|
246
|
+
- go.mod (Go projects)
|
|
247
|
+
- Cargo.toml (Rust projects)
|
|
248
|
+
- pom.xml / build.gradle (Java/Kotlin projects)
|
|
249
|
+
- pubspec.yaml (Flutter/Dart projects)
|
|
250
|
+
- Gemfile (Ruby projects)
|
|
251
|
+
- composer.json (PHP projects)
|
|
252
|
+
- Any existing source files to understand patterns
|
|
253
|
+
|
|
254
|
+
STEP 2 \u2014 FOLLOW EXISTING CONVENTIONS:
|
|
255
|
+
- If the project already has code, match its style exactly (naming, file structure, patterns)
|
|
256
|
+
- If starting from scratch, use the standard project structure for the detected tech stack
|
|
257
|
+
- Use the package manager the project already uses (npm, yarn, pnpm, pip, poetry, cargo, go, etc.)
|
|
258
|
+
|
|
259
|
+
STEP 3 \u2014 BUILD VERIFICATION:
|
|
260
|
+
- Detect the correct build/lint/test commands from the project config
|
|
261
|
+
- For Node.js: check package.json "scripts" for build, lint, typecheck commands
|
|
262
|
+
- For Python: use pytest, flake8/ruff, mypy if configured
|
|
263
|
+
- For Go: use go build, go vet, go test
|
|
264
|
+
- For Rust: use cargo build, cargo clippy, cargo test
|
|
265
|
+
- If no build script exists, skip the build step \u2014 do NOT fail
|
|
266
|
+
- If a command does not exist or is not configured, skip it gracefully
|
|
267
|
+
|
|
268
|
+
STEP 4 \u2014 SCAFFOLDING:
|
|
269
|
+
- If the project directory is empty, scaffold the project using the standard tooling for the stack
|
|
270
|
+
(e.g., npm init, cargo init, go mod init, django-admin startproject, etc.)
|
|
271
|
+
- Install dependencies after scaffolding
|
|
272
|
+
`.trim(),
|
|
273
|
+
designPromptAdditions: "",
|
|
274
|
+
fileStructure: `
|
|
275
|
+
(auto-detected \u2014 read the project's existing files to determine structure)
|
|
276
|
+
`.trim()
|
|
277
|
+
};
|
|
278
|
+
|
|
223
279
|
// src/core/adapters/index.ts
|
|
224
280
|
var adapters = {
|
|
225
281
|
nextjs: nextjsAdapter,
|
|
226
282
|
react: reactViteAdapter,
|
|
227
|
-
django: djangoAdapter
|
|
283
|
+
django: djangoAdapter,
|
|
284
|
+
generic: genericAdapter
|
|
228
285
|
};
|
|
229
286
|
function getAdapter(framework) {
|
|
230
287
|
const adapter = adapters[framework];
|
|
@@ -362,12 +419,14 @@ var Orchestrator = class {
|
|
|
362
419
|
// ── Plan Generation ───────────────────────────────────────
|
|
363
420
|
async generatePlan(description) {
|
|
364
421
|
const adapter = getAdapter(this.config.framework);
|
|
422
|
+
const isGeneric = adapter.id === "generic";
|
|
423
|
+
const frameworkHint = isGeneric ? `Tech stack: Detect from the user's description below. The user may specify their own framework, language, and tools.` : `Framework: ${adapter.name} (${this.config.framework})
|
|
424
|
+
Language: ${adapter.language}`;
|
|
365
425
|
const prompt = `
|
|
366
426
|
The user wants to build the following application:
|
|
367
427
|
"${description}"
|
|
368
428
|
|
|
369
|
-
|
|
370
|
-
Language: ${adapter.language}
|
|
429
|
+
${frameworkHint}
|
|
371
430
|
Design support: ${adapter.designSupport ? "yes (Storybook)" : "no"}
|
|
372
431
|
|
|
373
432
|
Break this down into epics and stories. Each story should be:
|
|
@@ -522,11 +581,31 @@ var Orchestrator = class {
|
|
|
522
581
|
`;
|
|
523
582
|
}
|
|
524
583
|
craftBuildPrompt(story, context, adapter) {
|
|
584
|
+
const isGeneric = adapter.id === "generic";
|
|
525
585
|
const designRef = context.designMeta ? `
|
|
526
586
|
Approved design: Follow the design in stories/${story.id}.stories.tsx exactly.` : "";
|
|
527
587
|
const existingRef = context.existingFiles?.length ? `
|
|
528
588
|
Existing files to reference for patterns:
|
|
529
589
|
${context.existingFiles.map((f) => ` - ${f}`).join("\n")}` : "";
|
|
590
|
+
const verifySteps = isGeneric ? `
|
|
591
|
+
After writing code:
|
|
592
|
+
1. Detect the project's build/lint/typecheck commands from its config files (package.json scripts, Makefile, pyproject.toml, etc.)
|
|
593
|
+
2. Run whichever commands exist \u2014 skip any that aren't configured
|
|
594
|
+
3. Fix any errors before finishing
|
|
595
|
+
` : `
|
|
596
|
+
After writing code:
|
|
597
|
+
1. Run: ${adapter.buildCommand} (fix any errors)
|
|
598
|
+
2. Run: ${adapter.lintCommand} (fix any warnings)
|
|
599
|
+
3. Run: ${adapter.typecheckCommand} (fix any type errors)
|
|
600
|
+
`;
|
|
601
|
+
const structureRef = isGeneric ? `
|
|
602
|
+
Project structure:
|
|
603
|
+
Read the existing project files to understand the structure.
|
|
604
|
+
If starting from scratch, use the standard conventions for this tech stack.
|
|
605
|
+
` : `
|
|
606
|
+
Expected project structure:
|
|
607
|
+
${adapter.fileStructure}
|
|
608
|
+
`;
|
|
530
609
|
return `
|
|
531
610
|
Implement: "${story.title}"
|
|
532
611
|
|
|
@@ -535,8 +614,7 @@ ${context.existingFiles.map((f) => ` - ${f}`).join("\n")}` : "";
|
|
|
535
614
|
${designRef}
|
|
536
615
|
${existingRef}
|
|
537
616
|
|
|
538
|
-
|
|
539
|
-
${adapter.fileStructure}
|
|
617
|
+
${structureRef}
|
|
540
618
|
|
|
541
619
|
Technical requirements:
|
|
542
620
|
- Follow existing code patterns in the project
|
|
@@ -544,16 +622,18 @@ ${context.existingFiles.map((f) => ` - ${f}`).join("\n")}` : "";
|
|
|
544
622
|
- Proper error handling
|
|
545
623
|
- Responsive design (mobile-first)
|
|
546
624
|
|
|
547
|
-
|
|
548
|
-
1. Run: ${adapter.buildCommand} (fix any errors)
|
|
549
|
-
2. Run: ${adapter.lintCommand} (fix any warnings)
|
|
550
|
-
3. Run: ${adapter.typecheckCommand} (fix any type errors)
|
|
625
|
+
${verifySteps}
|
|
551
626
|
|
|
552
627
|
If any command fails, read the error, fix it, and re-run.
|
|
553
628
|
Do NOT leave broken code.
|
|
554
629
|
`;
|
|
555
630
|
}
|
|
556
631
|
craftReviewPrompt(story, context, adapter) {
|
|
632
|
+
const isGeneric = adapter.id === "generic";
|
|
633
|
+
const runCmds = isGeneric ? `Run the project's build/lint/test commands (detect from config files). Skip any that aren't configured.` : `Run:
|
|
634
|
+
- ${adapter.buildCommand}
|
|
635
|
+
- ${adapter.lintCommand}
|
|
636
|
+
- ${adapter.typecheckCommand}`;
|
|
557
637
|
return `
|
|
558
638
|
Review the code for: "${story.title}"
|
|
559
639
|
|
|
@@ -568,10 +648,7 @@ ${context.existingFiles.map((f) => ` - ${f}`).join("\n")}` : "";
|
|
|
568
648
|
6. Accessibility \u2014 semantic HTML, ARIA labels
|
|
569
649
|
7. No debug logs, no commented-out code, no TODOs
|
|
570
650
|
|
|
571
|
-
|
|
572
|
-
- ${adapter.buildCommand}
|
|
573
|
-
- ${adapter.lintCommand}
|
|
574
|
-
- ${adapter.typecheckCommand}
|
|
651
|
+
${runCmds}
|
|
575
652
|
|
|
576
653
|
If you find issues:
|
|
577
654
|
- Minor (formatting, missing type): fix them directly
|
|
@@ -581,12 +658,14 @@ ${context.existingFiles.map((f) => ` - ${f}`).join("\n")}` : "";
|
|
|
581
658
|
`;
|
|
582
659
|
}
|
|
583
660
|
craftFixPrompt(story, context, adapter) {
|
|
661
|
+
const isGeneric = adapter.id === "generic";
|
|
662
|
+
const verifyCmds = isGeneric ? "After fixing, run the project's build/test commands to verify (detect from config files)." : `After fixing, run ${adapter.buildCommand} and ${adapter.typecheckCommand} to verify.`;
|
|
584
663
|
return `
|
|
585
664
|
Fix an issue in: "${story.title}"
|
|
586
665
|
Framework: ${adapter.name}
|
|
587
666
|
|
|
588
667
|
Make the smallest possible change. Do not refactor.
|
|
589
|
-
|
|
668
|
+
${verifyCmds}
|
|
590
669
|
`;
|
|
591
670
|
}
|
|
592
671
|
// ── SDK Query Helper ──────────────────────────────────────
|
|
@@ -699,9 +778,20 @@ ${additions}
|
|
|
699
778
|
function getBuildPrompt(framework) {
|
|
700
779
|
const adapter = framework ? getAdapter(framework) : null;
|
|
701
780
|
const additions = adapter?.buildPromptAdditions || "";
|
|
702
|
-
const
|
|
703
|
-
const
|
|
704
|
-
|
|
781
|
+
const isGeneric = adapter?.id === "generic";
|
|
782
|
+
const verifyBlock = isGeneric ? `
|
|
783
|
+
AFTER WRITING CODE:
|
|
784
|
+
1. Detect the project's build/lint/typecheck/test commands from its config files
|
|
785
|
+
(package.json scripts, Makefile, pyproject.toml, Cargo.toml, etc.)
|
|
786
|
+
2. Run whichever commands exist \u2014 skip any that aren't configured
|
|
787
|
+
3. Fix ALL errors before proceeding
|
|
788
|
+
4. If no build system is configured, verify the code is syntactically valid
|
|
789
|
+
` : `
|
|
790
|
+
AFTER WRITING CODE:
|
|
791
|
+
1. Run: ${adapter?.buildCommand || "npm run build"} \u2014 fix ALL errors before proceeding
|
|
792
|
+
2. Run: ${adapter?.lintCommand || "npm run lint"} \u2014 fix warnings
|
|
793
|
+
3. Run: ${adapter?.typecheckCommand || "npx tsc --noEmit"} \u2014 fix type errors
|
|
794
|
+
`;
|
|
705
795
|
return `
|
|
706
796
|
You are a senior fullstack developer working within the Forge development framework.
|
|
707
797
|
Your job is to implement features based on approved designs and story requirements.
|
|
@@ -715,10 +805,7 @@ CODING STANDARDS:
|
|
|
715
805
|
|
|
716
806
|
${additions}
|
|
717
807
|
|
|
718
|
-
|
|
719
|
-
1. Run: ${buildCmd} \u2014 fix ALL errors before proceeding
|
|
720
|
-
2. Run: ${lintCmd} \u2014 fix warnings
|
|
721
|
-
3. Run: ${typecheckCmd} \u2014 fix type errors
|
|
808
|
+
${verifyBlock}
|
|
722
809
|
|
|
723
810
|
If any command fails:
|
|
724
811
|
- Read the full error output
|
|
@@ -736,10 +823,21 @@ GIT:
|
|
|
736
823
|
}
|
|
737
824
|
function getReviewPrompt(framework) {
|
|
738
825
|
const adapter = framework ? getAdapter(framework) : null;
|
|
739
|
-
const
|
|
740
|
-
const lintCmd = adapter?.lintCommand || "npm run lint";
|
|
741
|
-
const typecheckCmd = adapter?.typecheckCommand || "npx tsc --noEmit";
|
|
826
|
+
const isGeneric = adapter?.id === "generic";
|
|
742
827
|
const lang = adapter?.language || "typescript";
|
|
828
|
+
const langCheck = isGeneric ? "3. Code quality \u2014 follows the language's best practices and idioms" : lang === "typescript" ? "3. TypeScript strict compliance (no any, no ts-ignore, no ts-expect-error)" : "3. Python code quality (no bare except, proper type hints where used)";
|
|
829
|
+
const runBlock = isGeneric ? `
|
|
830
|
+
RUN THESE COMMANDS:
|
|
831
|
+
- Detect build/lint/test commands from the project's config files (package.json, Makefile, pyproject.toml, Cargo.toml, etc.)
|
|
832
|
+
- Run whichever exist \u2014 skip any that aren't configured
|
|
833
|
+
- Run tests if a test runner is configured
|
|
834
|
+
` : `
|
|
835
|
+
RUN THESE COMMANDS:
|
|
836
|
+
- ${adapter?.buildCommand || "npm run build"}
|
|
837
|
+
- ${adapter?.lintCommand || "npm run lint"}
|
|
838
|
+
- ${adapter?.typecheckCommand || "npx tsc --noEmit"}
|
|
839
|
+
- npm run test (if tests exist)
|
|
840
|
+
`;
|
|
743
841
|
return `
|
|
744
842
|
You are a QA engineer reviewing code within the Forge development framework.
|
|
745
843
|
Your job is to catch issues before code is merged to main.
|
|
@@ -747,7 +845,7 @@ Your job is to catch issues before code is merged to main.
|
|
|
747
845
|
REVIEW CHECKLIST:
|
|
748
846
|
1. Implementation matches the story description
|
|
749
847
|
2. If design was approved \u2014 implementation matches the design
|
|
750
|
-
${
|
|
848
|
+
${langCheck}
|
|
751
849
|
4. Responsive design works at 375px, 768px, 1440px
|
|
752
850
|
5. Error handling \u2014 what happens when things fail?
|
|
753
851
|
6. Loading states \u2014 what does the user see while waiting?
|
|
@@ -758,11 +856,7 @@ ${lang === "typescript" ? "3. TypeScript strict compliance (no any, no ts-ignore
|
|
|
758
856
|
11. No TODO/FIXME/HACK comments left behind
|
|
759
857
|
12. No hardcoded values that should be configurable
|
|
760
858
|
|
|
761
|
-
|
|
762
|
-
- ${buildCmd}
|
|
763
|
-
- ${lintCmd}
|
|
764
|
-
- ${typecheckCmd}
|
|
765
|
-
- npm run test (if tests exist)
|
|
859
|
+
${runBlock}
|
|
766
860
|
|
|
767
861
|
ISSUE CLASSIFICATION:
|
|
768
862
|
- MINOR: formatting, missing type annotation, unused import
|
|
@@ -781,8 +875,18 @@ Provide a structured review summary:
|
|
|
781
875
|
}
|
|
782
876
|
function getFixPrompt(framework) {
|
|
783
877
|
const adapter = framework ? getAdapter(framework) : null;
|
|
784
|
-
const
|
|
785
|
-
const
|
|
878
|
+
const isGeneric = adapter?.id === "generic";
|
|
879
|
+
const verifyBlock = isGeneric ? `
|
|
880
|
+
AFTER EVERY FIX:
|
|
881
|
+
1. Detect the project's build/test commands from its config files
|
|
882
|
+
2. Run whichever exist to verify nothing is broken
|
|
883
|
+
3. Verify the fix works
|
|
884
|
+
` : `
|
|
885
|
+
AFTER EVERY FIX:
|
|
886
|
+
1. Run: ${adapter?.buildCommand || "npm run build"}
|
|
887
|
+
2. Run: ${adapter?.typecheckCommand || "npx tsc --noEmit"}
|
|
888
|
+
3. Verify the fix works
|
|
889
|
+
`;
|
|
786
890
|
return `
|
|
787
891
|
You are a debugger and problem solver within the Forge development framework.
|
|
788
892
|
Your job is to make targeted fixes without breaking anything else.
|
|
@@ -805,10 +909,7 @@ FOR BUG FIXES:
|
|
|
805
909
|
- Check if the same bug pattern exists elsewhere
|
|
806
910
|
- Verify the fix by running the relevant command
|
|
807
911
|
|
|
808
|
-
|
|
809
|
-
1. Run: ${buildCmd}
|
|
810
|
-
2. Run: ${typecheckCmd}
|
|
811
|
-
3. Verify the fix works
|
|
912
|
+
${verifyBlock}
|
|
812
913
|
|
|
813
914
|
If your fix introduces new errors, undo it and try a different approach.
|
|
814
915
|
`.trim();
|
|
@@ -911,6 +1012,9 @@ var Worker = class {
|
|
|
911
1012
|
cwd: workingDir,
|
|
912
1013
|
maxTurns: MODE_MAX_TURNS[mode]
|
|
913
1014
|
};
|
|
1015
|
+
if (this.sandboxOpts.yes && !this.sandboxOpts.sandbox) {
|
|
1016
|
+
sdkOptions.permissionMode = "bypassPermissions";
|
|
1017
|
+
}
|
|
914
1018
|
if (this.sandboxOpts.sandbox) {
|
|
915
1019
|
sdkOptions.permissionMode = "bypassPermissions";
|
|
916
1020
|
sdkOptions.sandbox = {
|
|
@@ -1904,6 +2008,120 @@ var GitHubSync = class {
|
|
|
1904
2008
|
}
|
|
1905
2009
|
};
|
|
1906
2010
|
|
|
2011
|
+
// src/core/utils/attachments.ts
|
|
2012
|
+
import fs3 from "fs";
|
|
2013
|
+
import path3 from "path";
|
|
2014
|
+
var IMAGE_EXTS = /* @__PURE__ */ new Set([
|
|
2015
|
+
".png",
|
|
2016
|
+
".jpg",
|
|
2017
|
+
".jpeg",
|
|
2018
|
+
".gif",
|
|
2019
|
+
".webp",
|
|
2020
|
+
".svg",
|
|
2021
|
+
".bmp",
|
|
2022
|
+
".ico",
|
|
2023
|
+
".tiff",
|
|
2024
|
+
".avif"
|
|
2025
|
+
]);
|
|
2026
|
+
var DOCUMENT_EXTS = /* @__PURE__ */ new Set([
|
|
2027
|
+
".pdf",
|
|
2028
|
+
".doc",
|
|
2029
|
+
".docx",
|
|
2030
|
+
".txt",
|
|
2031
|
+
".md",
|
|
2032
|
+
".csv",
|
|
2033
|
+
".xls",
|
|
2034
|
+
".xlsx",
|
|
2035
|
+
".ppt",
|
|
2036
|
+
".pptx",
|
|
2037
|
+
".rtf",
|
|
2038
|
+
".html",
|
|
2039
|
+
".json",
|
|
2040
|
+
".yaml",
|
|
2041
|
+
".yml",
|
|
2042
|
+
".xml",
|
|
2043
|
+
".figma",
|
|
2044
|
+
".sketch",
|
|
2045
|
+
".xd"
|
|
2046
|
+
]);
|
|
2047
|
+
var PATH_PATTERN = /(?:"([^"]+\.[\w]+)")|(?:'([^']+\.[\w]+)')|(?:((?:\/|[A-Z]:\\)(?:[^\s,;]|\\[ ])+\.[\w]+))/g;
|
|
2048
|
+
function parseAttachments(input) {
|
|
2049
|
+
const attachments = [];
|
|
2050
|
+
let imageCount = 0;
|
|
2051
|
+
let docCount = 0;
|
|
2052
|
+
let cleaned = input.replace(PATH_PATTERN, (match, quoted, singleQuoted, raw) => {
|
|
2053
|
+
const filePath = (quoted || singleQuoted || raw || "").replace(/\\ /g, " ").trim();
|
|
2054
|
+
if (!filePath) return match;
|
|
2055
|
+
const resolved = path3.resolve(filePath);
|
|
2056
|
+
if (!fs3.existsSync(resolved)) return match;
|
|
2057
|
+
const ext = path3.extname(resolved).toLowerCase();
|
|
2058
|
+
const fileName = path3.basename(resolved);
|
|
2059
|
+
let type;
|
|
2060
|
+
let label;
|
|
2061
|
+
if (IMAGE_EXTS.has(ext)) {
|
|
2062
|
+
imageCount++;
|
|
2063
|
+
type = "image";
|
|
2064
|
+
label = `[Image${imageCount}]`;
|
|
2065
|
+
} else if (DOCUMENT_EXTS.has(ext)) {
|
|
2066
|
+
docCount++;
|
|
2067
|
+
type = "document";
|
|
2068
|
+
label = `[Document${docCount}]`;
|
|
2069
|
+
} else {
|
|
2070
|
+
docCount++;
|
|
2071
|
+
type = "document";
|
|
2072
|
+
label = `[Document${docCount}]`;
|
|
2073
|
+
}
|
|
2074
|
+
attachments.push({
|
|
2075
|
+
label,
|
|
2076
|
+
originalPath: match,
|
|
2077
|
+
resolvedPath: resolved,
|
|
2078
|
+
type,
|
|
2079
|
+
ext,
|
|
2080
|
+
fileName
|
|
2081
|
+
});
|
|
2082
|
+
return label;
|
|
2083
|
+
});
|
|
2084
|
+
cleaned = cleaned.replace(/\s{2,}/g, " ").trim();
|
|
2085
|
+
return { description: cleaned, attachments };
|
|
2086
|
+
}
|
|
2087
|
+
function stageAttachments(attachments) {
|
|
2088
|
+
if (attachments.length === 0) return [];
|
|
2089
|
+
const attachDir = path3.join(process.cwd(), ".forge", "attachments");
|
|
2090
|
+
fs3.mkdirSync(attachDir, { recursive: true });
|
|
2091
|
+
const stagedPaths = [];
|
|
2092
|
+
for (const att of attachments) {
|
|
2093
|
+
const stagedName = att.label.replace(/[\[\]]/g, "").toLowerCase() + att.ext;
|
|
2094
|
+
const dest = path3.join(attachDir, stagedName);
|
|
2095
|
+
try {
|
|
2096
|
+
fs3.copyFileSync(att.resolvedPath, dest);
|
|
2097
|
+
stagedPaths.push(dest);
|
|
2098
|
+
} catch {
|
|
2099
|
+
}
|
|
2100
|
+
}
|
|
2101
|
+
return stagedPaths;
|
|
2102
|
+
}
|
|
2103
|
+
function formatAttachmentList(attachments) {
|
|
2104
|
+
if (attachments.length === 0) return "";
|
|
2105
|
+
return attachments.map((a) => ` ${a.label} ${a.fileName}`).join("\n");
|
|
2106
|
+
}
|
|
2107
|
+
function buildAttachmentPrompt(attachments) {
|
|
2108
|
+
if (attachments.length === 0) return "";
|
|
2109
|
+
const lines = attachments.map((a) => {
|
|
2110
|
+
const relPath = path3.relative(process.cwd(), a.resolvedPath);
|
|
2111
|
+
if (a.type === "image") {
|
|
2112
|
+
return `- ${a.label}: Image file at ${relPath} \u2014 read this file to see the visual reference`;
|
|
2113
|
+
}
|
|
2114
|
+
return `- ${a.label}: Document at ${relPath} \u2014 read this file for requirements/specs`;
|
|
2115
|
+
});
|
|
2116
|
+
return `
|
|
2117
|
+
ATTACHMENTS (provided by the user):
|
|
2118
|
+
${lines.join("\n")}
|
|
2119
|
+
|
|
2120
|
+
Use these attachments as reference material. For images, match the visual style/layout.
|
|
2121
|
+
For documents, extract requirements and specifications from them.
|
|
2122
|
+
`;
|
|
2123
|
+
}
|
|
2124
|
+
|
|
1907
2125
|
// src/core/pipeline/auto.ts
|
|
1908
2126
|
var AutoPipeline = class {
|
|
1909
2127
|
orchestrator;
|
|
@@ -1927,6 +2145,7 @@ var AutoPipeline = class {
|
|
|
1927
2145
|
config,
|
|
1928
2146
|
{
|
|
1929
2147
|
sandbox: options.sandbox ?? true,
|
|
2148
|
+
yes: options.yes ?? false,
|
|
1930
2149
|
workingDir: options.workingDir,
|
|
1931
2150
|
allowedDomains: options.allowedDomains
|
|
1932
2151
|
},
|
|
@@ -2027,7 +2246,11 @@ var AutoPipeline = class {
|
|
|
2027
2246
|
const errors = [];
|
|
2028
2247
|
this.startTime = Date.now();
|
|
2029
2248
|
console.log(chalk4.bold("\n forge") + chalk4.dim(" auto"));
|
|
2030
|
-
|
|
2249
|
+
const flags = [
|
|
2250
|
+
`sandbox ${this.options.sandbox !== false ? "on" : "off"}`,
|
|
2251
|
+
...this.options.yes ? ["auto-approve on"] : []
|
|
2252
|
+
].join(" \xB7 ");
|
|
2253
|
+
console.log(chalk4.dim(` ${flags} \xB7 type a message anytime to queue feedback
|
|
2031
2254
|
`));
|
|
2032
2255
|
this.startChatListener();
|
|
2033
2256
|
await this.git.ensureRepo();
|
|
@@ -2035,7 +2258,8 @@ var AutoPipeline = class {
|
|
|
2035
2258
|
const planSpinner = ora2({ text: `${this.elapsed()} Planning...`, indent: 2 }).start();
|
|
2036
2259
|
this.activeSpinner = planSpinner;
|
|
2037
2260
|
try {
|
|
2038
|
-
|
|
2261
|
+
const attachmentContext = buildAttachmentPrompt(this.options.attachments || []);
|
|
2262
|
+
this.plan = await this.orchestrator.generatePlan(description + attachmentContext);
|
|
2039
2263
|
planSpinner.succeed(`${this.elapsed()} Plan ready`);
|
|
2040
2264
|
this.activeSpinner = null;
|
|
2041
2265
|
this.displayPlan(this.plan);
|
|
@@ -2415,6 +2639,7 @@ var AutoPipeline = class {
|
|
|
2415
2639
|
}
|
|
2416
2640
|
// ── Review Gate ───────────────────────────────────────────
|
|
2417
2641
|
async reviewGate() {
|
|
2642
|
+
if (this.options.yes) return "continue";
|
|
2418
2643
|
if (!process.stdout.isTTY) return "continue";
|
|
2419
2644
|
if (!this.options.mute) {
|
|
2420
2645
|
playSound();
|
|
@@ -2801,8 +3026,12 @@ export {
|
|
|
2801
3026
|
GitManager,
|
|
2802
3027
|
stateManager,
|
|
2803
3028
|
Pipeline,
|
|
3029
|
+
parseAttachments,
|
|
3030
|
+
stageAttachments,
|
|
3031
|
+
formatAttachmentList,
|
|
3032
|
+
buildAttachmentPrompt,
|
|
2804
3033
|
AutoPipeline,
|
|
2805
3034
|
validateConfig,
|
|
2806
3035
|
loadAndValidateConfig
|
|
2807
3036
|
};
|
|
2808
|
-
//# sourceMappingURL=chunk-
|
|
3037
|
+
//# sourceMappingURL=chunk-P5CLBIGQ.js.map
|