forgecraft 1.3.2 → 1.4.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.
|
@@ -134,20 +134,26 @@ public/
|
|
|
134
134
|
};
|
|
135
135
|
|
|
136
136
|
// src/core/adapters/django.ts
|
|
137
|
+
var isWin = process.platform === "win32";
|
|
138
|
+
var venvBin = isWin ? "venv\\Scripts" : "venv/bin";
|
|
139
|
+
var python = isWin ? "python" : "python3";
|
|
140
|
+
var venvPython = isWin ? `${venvBin}\\python` : `${venvBin}/python`;
|
|
141
|
+
var venvPip = isWin ? `${venvBin}\\pip` : `${venvBin}/pip`;
|
|
142
|
+
var venvDjangoAdmin = isWin ? `${venvBin}\\django-admin` : `${venvBin}/django-admin`;
|
|
137
143
|
var djangoAdapter = {
|
|
138
144
|
id: "django",
|
|
139
145
|
name: "Django",
|
|
140
146
|
language: "python",
|
|
141
147
|
scaffoldCommands: [
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
148
|
+
`${python} -m venv venv`,
|
|
149
|
+
`${venvPip} install django djangorestframework django-cors-headers python-dotenv`,
|
|
150
|
+
`${venvDjangoAdmin} startproject config .`,
|
|
151
|
+
`${venvPython} manage.py startapp core`
|
|
146
152
|
],
|
|
147
|
-
buildCommand:
|
|
148
|
-
lintCommand:
|
|
149
|
-
typecheckCommand: "echo 'Type checking skipped (Python)'",
|
|
150
|
-
devCommand:
|
|
153
|
+
buildCommand: `${venvPython} manage.py check --deploy`,
|
|
154
|
+
lintCommand: `${venvPython} -m py_compile manage.py`,
|
|
155
|
+
typecheckCommand: isWin ? "echo Type checking skipped (Python)" : "echo 'Type checking skipped (Python)'",
|
|
156
|
+
devCommand: `${venvPython} manage.py runserver`,
|
|
151
157
|
devPort: 8e3,
|
|
152
158
|
designSupport: false,
|
|
153
159
|
packageManager: "pip",
|
|
@@ -167,6 +173,11 @@ FOR DJANGO:
|
|
|
167
173
|
- Register models in admin.py for Django Admin access
|
|
168
174
|
- Use django-cors-headers for API CORS configuration
|
|
169
175
|
|
|
176
|
+
PLATFORM NOTE:
|
|
177
|
+
- On Windows, use venv\\Scripts\\python instead of venv/bin/python
|
|
178
|
+
- On Unix/macOS, use venv/bin/python
|
|
179
|
+
- Detect the platform and use the correct path
|
|
180
|
+
|
|
170
181
|
SECURITY:
|
|
171
182
|
- CSRF protection enabled by default \u2014 don't disable it
|
|
172
183
|
- Use Django's built-in auth system (User model, login/logout views)
|
|
@@ -174,12 +185,12 @@ SECURITY:
|
|
|
174
185
|
- Configure ALLOWED_HOSTS properly
|
|
175
186
|
|
|
176
187
|
AFTER WRITING CODE:
|
|
177
|
-
1. Run:
|
|
178
|
-
2. Run:
|
|
179
|
-
3. Run:
|
|
188
|
+
1. Run: ${venvPython} manage.py makemigrations
|
|
189
|
+
2. Run: ${venvPython} manage.py migrate
|
|
190
|
+
3. Run: ${venvPython} manage.py check --deploy
|
|
180
191
|
4. Fix any warnings or errors before proceeding
|
|
181
192
|
|
|
182
|
-
ALWAYS generate a requirements.txt with:
|
|
193
|
+
ALWAYS generate a requirements.txt with: ${venvPip} freeze > requirements.txt
|
|
183
194
|
`.trim(),
|
|
184
195
|
designPromptAdditions: `
|
|
185
196
|
DJANGO DESIGN:
|
|
@@ -301,20 +312,32 @@ import { exec } from "child_process";
|
|
|
301
312
|
import { platform } from "os";
|
|
302
313
|
function playSound() {
|
|
303
314
|
if (!process.stdout.isTTY) return;
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
315
|
+
try {
|
|
316
|
+
switch (platform()) {
|
|
317
|
+
case "darwin":
|
|
318
|
+
exec("afplay /System/Library/Sounds/Blow.aiff", () => {
|
|
319
|
+
});
|
|
320
|
+
break;
|
|
321
|
+
case "win32":
|
|
322
|
+
exec(
|
|
323
|
+
'powershell -NoProfile -NonInteractive -Command "[System.Media.SystemSounds]::Exclamation.Play()"',
|
|
324
|
+
{ shell: "cmd.exe" },
|
|
325
|
+
() => {
|
|
326
|
+
}
|
|
327
|
+
);
|
|
328
|
+
break;
|
|
329
|
+
case "linux":
|
|
330
|
+
exec(
|
|
331
|
+
"paplay /usr/share/sounds/freedesktop/stereo/complete.oga",
|
|
332
|
+
(err) => {
|
|
333
|
+
if (err) process.stdout.write("\x07");
|
|
334
|
+
}
|
|
335
|
+
);
|
|
336
|
+
break;
|
|
337
|
+
default:
|
|
338
|
+
process.stdout.write("\x07");
|
|
339
|
+
}
|
|
340
|
+
} catch {
|
|
318
341
|
}
|
|
319
342
|
}
|
|
320
343
|
|
|
@@ -469,15 +492,19 @@ var Orchestrator = class {
|
|
|
469
492
|
]
|
|
470
493
|
}
|
|
471
494
|
|
|
472
|
-
No explanation,
|
|
495
|
+
CRITICAL: Return ONLY the raw JSON object. No explanation, no markdown, no text before or after.
|
|
496
|
+
Your entire response must be a single valid JSON object starting with { and ending with }.
|
|
473
497
|
`;
|
|
474
498
|
const resultText = await this.runQuery(prompt, { maxTurns: 10 });
|
|
475
499
|
let rawPlan;
|
|
476
500
|
try {
|
|
477
501
|
rawPlan = JSON.parse(this.cleanJson(resultText));
|
|
478
502
|
} catch {
|
|
503
|
+
const preview = resultText.slice(0, 200).replace(/\n/g, " ");
|
|
479
504
|
throw new Error(
|
|
480
|
-
|
|
505
|
+
`Failed to parse plan \u2014 Claude returned text instead of JSON.
|
|
506
|
+
Preview: "${preview}${resultText.length > 200 ? "..." : ""}"
|
|
507
|
+
Try a more specific description, e.g.: forge auto "build a todo app with React and Express"`
|
|
481
508
|
);
|
|
482
509
|
}
|
|
483
510
|
if (!rawPlan.epics || !Array.isArray(rawPlan.epics)) {
|
|
@@ -759,7 +786,24 @@ ${context.existingFiles.map((f) => ` - ${f}`).join("\n")}` : "";
|
|
|
759
786
|
return lower.includes("401") || lower.includes("unauthorized") || lower.includes("auth") || lower.includes("token expired") || lower.includes("session expired") || lower.includes("not authenticated");
|
|
760
787
|
}
|
|
761
788
|
cleanJson(text) {
|
|
762
|
-
|
|
789
|
+
let cleaned = text.replace(/```json\s*/g, "").replace(/```\s*/g, "").trim();
|
|
790
|
+
try {
|
|
791
|
+
JSON.parse(cleaned);
|
|
792
|
+
return cleaned;
|
|
793
|
+
} catch {
|
|
794
|
+
}
|
|
795
|
+
const firstBrace = cleaned.indexOf("{");
|
|
796
|
+
const lastBrace = cleaned.lastIndexOf("}");
|
|
797
|
+
if (firstBrace !== -1 && lastBrace > firstBrace) {
|
|
798
|
+
const extracted = cleaned.slice(firstBrace, lastBrace + 1);
|
|
799
|
+
try {
|
|
800
|
+
JSON.parse(extracted);
|
|
801
|
+
return extracted;
|
|
802
|
+
} catch {
|
|
803
|
+
return extracted;
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
return cleaned;
|
|
763
807
|
}
|
|
764
808
|
};
|
|
765
809
|
|
|
@@ -1902,7 +1946,7 @@ var GitHubSync = class {
|
|
|
1902
1946
|
}
|
|
1903
1947
|
/** Check if `gh` CLI is installed and authenticated */
|
|
1904
1948
|
static isAvailable() {
|
|
1905
|
-
const result = spawnSync("gh", ["auth", "status"], { stdio: "ignore" });
|
|
1949
|
+
const result = spawnSync("gh", ["auth", "status"], { stdio: "ignore", shell: true });
|
|
1906
1950
|
return result.status === 0;
|
|
1907
1951
|
}
|
|
1908
1952
|
/** Create GitHub labels for forge story tracking */
|
|
@@ -1930,7 +1974,7 @@ var GitHubSync = class {
|
|
|
1930
1974
|
"--description",
|
|
1931
1975
|
label.description,
|
|
1932
1976
|
"--force"
|
|
1933
|
-
], { stdio: "ignore" });
|
|
1977
|
+
], { stdio: "ignore", shell: true });
|
|
1934
1978
|
}
|
|
1935
1979
|
}
|
|
1936
1980
|
/** Sync all stories in a plan to GitHub Issues */
|
|
@@ -1976,7 +2020,7 @@ var GitHubSync = class {
|
|
|
1976
2020
|
".[0].number",
|
|
1977
2021
|
"--state",
|
|
1978
2022
|
"all"
|
|
1979
|
-
], { encoding: "utf-8" });
|
|
2023
|
+
], { encoding: "utf-8", shell: true });
|
|
1980
2024
|
const existingNumber = searchResult.stdout?.trim();
|
|
1981
2025
|
if (existingNumber && /^\d+$/.test(existingNumber)) {
|
|
1982
2026
|
const args = [
|
|
@@ -2001,7 +2045,7 @@ var GitHubSync = class {
|
|
|
2001
2045
|
existingNumber,
|
|
2002
2046
|
"--repo",
|
|
2003
2047
|
this.repo
|
|
2004
|
-
], { stdio: "ignore" });
|
|
2048
|
+
], { stdio: "ignore", shell: true });
|
|
2005
2049
|
}
|
|
2006
2050
|
return "updated";
|
|
2007
2051
|
}
|
|
@@ -2218,7 +2262,9 @@ var AutoPipeline = class {
|
|
|
2218
2262
|
process.exit(130);
|
|
2219
2263
|
};
|
|
2220
2264
|
process.on("SIGINT", handler);
|
|
2221
|
-
process.
|
|
2265
|
+
if (process.platform !== "win32") {
|
|
2266
|
+
process.on("SIGTERM", handler);
|
|
2267
|
+
}
|
|
2222
2268
|
}
|
|
2223
2269
|
// ── Resume an interrupted sprint ──────────────────────────
|
|
2224
2270
|
async resume(existingPlan) {
|
|
@@ -3080,4 +3126,4 @@ export {
|
|
|
3080
3126
|
validateConfig,
|
|
3081
3127
|
loadAndValidateConfig
|
|
3082
3128
|
};
|
|
3083
|
-
//# sourceMappingURL=chunk-
|
|
3129
|
+
//# sourceMappingURL=chunk-NYOV5D5J.js.map
|