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
- "python3 -m venv venv",
143
- "venv/bin/pip install django djangorestframework django-cors-headers python-dotenv",
144
- "venv/bin/django-admin startproject config .",
145
- "venv/bin/python manage.py startapp core"
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: "venv/bin/python manage.py check --deploy",
148
- lintCommand: "venv/bin/python -m py_compile manage.py",
149
- typecheckCommand: "echo 'Type checking skipped (Python)'",
150
- devCommand: "venv/bin/python manage.py runserver",
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: venv/bin/python manage.py makemigrations
178
- 2. Run: venv/bin/python manage.py migrate
179
- 3. Run: venv/bin/python manage.py check --deploy
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: venv/bin/pip freeze > requirements.txt
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
- switch (platform()) {
305
- case "darwin":
306
- exec("afplay /System/Library/Sounds/Blow.aiff");
307
- break;
308
- case "linux":
309
- exec(
310
- "paplay /usr/share/sounds/freedesktop/stereo/complete.oga 2>/dev/null || printf '\\x07'"
311
- );
312
- break;
313
- case "win32":
314
- exec('powershell -c "[System.Media.SystemSounds]::Exclamation.Play()"');
315
- break;
316
- default:
317
- process.stdout.write("\x07");
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, just the JSON.
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
- "Failed to parse plan. The AI returned invalid JSON. Try again with a clearer description."
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
- return text.replace(/```json\s*/g, "").replace(/```\s*/g, "").trim();
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.on("SIGTERM", handler);
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-ZZ3EDO3G.js.map
3129
+ //# sourceMappingURL=chunk-NYOV5D5J.js.map