ai-spec-dev 0.41.0 → 0.42.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.
Files changed (37) hide show
  1. package/.ai-spec-workspace.json +17 -0
  2. package/.ai-spec.json +7 -0
  3. package/cli/pipeline/single-repo.ts +19 -10
  4. package/core/cli-ui.ts +136 -0
  5. package/core/code-generator.ts +4 -2
  6. package/core/error-feedback.ts +4 -2
  7. package/core/provider-utils.ts +8 -7
  8. package/demo-backend/.ai-spec-constitution.md +65 -0
  9. package/demo-backend/package.json +21 -0
  10. package/demo-backend/prisma/schema.prisma +22 -0
  11. package/demo-backend/specs/feature-1-bookmark-id-uuid-title-string-required-url-str-v1.dsl.json +186 -0
  12. package/demo-backend/specs/feature-1-bookmark-id-uuid-title-string-required-url-str-v1.md +211 -0
  13. package/demo-backend/src/controllers/bookmark.controller.test.ts +255 -0
  14. package/demo-backend/src/controllers/bookmark.controller.ts +187 -0
  15. package/demo-backend/src/index.ts +17 -0
  16. package/demo-backend/src/routes/bookmark.routes.test.ts +264 -0
  17. package/demo-backend/src/routes/bookmark.routes.ts +11 -0
  18. package/demo-backend/src/routes/index.ts +8 -0
  19. package/demo-backend/src/services/bookmark.service.test.ts +433 -0
  20. package/demo-backend/src/services/bookmark.service.ts +261 -0
  21. package/demo-backend/tsconfig.json +12 -0
  22. package/demo-frontend/.ai-spec-constitution.md +95 -0
  23. package/demo-frontend/package.json +23 -0
  24. package/demo-frontend/src/App.tsx +12 -0
  25. package/demo-frontend/src/main.tsx +9 -0
  26. package/demo-frontend/tsconfig.json +13 -0
  27. package/dist/cli/index.js +130 -21
  28. package/dist/cli/index.js.map +1 -1
  29. package/dist/cli/index.mjs +130 -21
  30. package/dist/cli/index.mjs.map +1 -1
  31. package/dist/index.js +80 -8
  32. package/dist/index.js.map +1 -1
  33. package/dist/index.mjs +80 -8
  34. package/dist/index.mjs.map +1 -1
  35. package/package.json +1 -1
  36. package/RELEASE_LOG.md +0 -2962
  37. package/purpose.md +0 -1434
package/dist/index.js CHANGED
@@ -182,9 +182,77 @@ CRITICAL \u2014 \u5386\u53F2\u6559\u8BAD\u5E94\u7528\uFF08Accumulated Lessons\uF
182
182
  3. \u5BF9\u4E8E\u6BCF\u6761\u76F4\u63A5\u76F8\u5173\u7684\u6559\u8BAD\uFF0C\u5728 \xA78 \u5B9E\u65BD\u8981\u70B9\u672B\u5C3E\u8FFD\u52A0\u4E00\u884C\uFF1A\u300C\u26A0 \u57FA\u4E8E\u5386\u53F2\u6559\u8BAD\uFF1A[\u7B80\u8FF0\u672C\u6B21 spec \u5982\u4F55\u89C4\u907F\u8BE5\u95EE\u9898]\u300D
183
183
  4. \u5982\u65E0\u76F8\u5173\u6559\u8BAD\uFF0C\xA78 \u4E0D\u5FC5\u8FFD\u52A0\u4EFB\u4F55\u5185\u5BB9`;
184
184
 
185
- // core/provider-utils.ts
185
+ // core/cli-ui.ts
186
186
  var import_chalk = __toESM(require("chalk"));
187
- var sleep = (ms2) => new Promise((r) => setTimeout(r, ms2));
187
+ var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
188
+ function startSpinner(text) {
189
+ const isTTY = process.stderr.isTTY;
190
+ let frame = 0;
191
+ let currentText = text;
192
+ let stopped = false;
193
+ function render() {
194
+ if (stopped) return;
195
+ const symbol = import_chalk.default.cyan(SPINNER_FRAMES[frame % SPINNER_FRAMES.length]);
196
+ if (isTTY) {
197
+ process.stderr.write(`\r ${symbol} ${currentText}${" ".repeat(10)}`);
198
+ }
199
+ frame++;
200
+ }
201
+ if (!isTTY) {
202
+ process.stderr.write(` \u2026 ${currentText}
203
+ `);
204
+ }
205
+ const timer = setInterval(render, 80);
206
+ render();
207
+ return {
208
+ update(newText) {
209
+ currentText = newText;
210
+ },
211
+ stop(finalText) {
212
+ if (stopped) return;
213
+ stopped = true;
214
+ clearInterval(timer);
215
+ if (isTTY) {
216
+ process.stderr.write(`\r${" ".repeat(currentText.length + 20)}\r`);
217
+ }
218
+ if (finalText) {
219
+ process.stderr.write(` ${finalText}
220
+ `);
221
+ }
222
+ },
223
+ succeed(successText) {
224
+ this.stop(import_chalk.default.green(`\u2714 ${successText}`));
225
+ },
226
+ fail(failText) {
227
+ this.stop(import_chalk.default.red(`\u2718 ${failText}`));
228
+ }
229
+ };
230
+ }
231
+ async function retryCountdown(opts) {
232
+ const { attempt, maxAttempts, waitMs, errorMessage, label } = opts;
233
+ const isTTY = process.stderr.isTTY;
234
+ const shortErr = errorMessage.length > 120 ? errorMessage.slice(0, 117) + "..." : errorMessage;
235
+ process.stderr.write("\n");
236
+ process.stderr.write(import_chalk.default.yellow(` \u250C\u2500 Retry ${attempt}/${maxAttempts} `) + import_chalk.default.gray(`[${label}]`) + import_chalk.default.yellow(` ${"\u2500".repeat(Math.max(1, 40 - label.length))}
237
+ `));
238
+ process.stderr.write(import_chalk.default.yellow(` \u2502 `) + import_chalk.default.white(shortErr) + "\n");
239
+ process.stderr.write(import_chalk.default.yellow(` \u2502 `) + import_chalk.default.gray(`Waiting before retry...`) + "\n");
240
+ const totalSeconds = Math.ceil(waitMs / 1e3);
241
+ for (let s = totalSeconds; s > 0; s--) {
242
+ const bar = import_chalk.default.green("\u2588".repeat(totalSeconds - s)) + import_chalk.default.gray("\u2591".repeat(s));
243
+ const line = import_chalk.default.yellow(` \u2502 `) + `${bar} ${import_chalk.default.bold.white(`${s}s`)}`;
244
+ if (isTTY) {
245
+ process.stderr.write(`\r${line}${" ".repeat(10)}`);
246
+ }
247
+ await new Promise((r) => setTimeout(r, 1e3));
248
+ }
249
+ if (isTTY) {
250
+ process.stderr.write(`\r${" ".repeat(70)}\r`);
251
+ }
252
+ process.stderr.write(import_chalk.default.yellow(` \u2514\u2500 `) + import_chalk.default.cyan(`Retrying now...`) + "\n\n");
253
+ }
254
+
255
+ // core/provider-utils.ts
188
256
  var ProviderError = class extends Error {
189
257
  constructor(message, kind, originalError) {
190
258
  super(message);
@@ -268,11 +336,14 @@ async function withReliability(fn, opts) {
268
336
  throw classifyError(err, label);
269
337
  }
270
338
  const waitMs = attempt === 0 ? 2e3 : 6e3;
271
- console.warn(
272
- import_chalk.default.yellow(` \u26A0 ${label} failed (attempt ${attempt + 1}/${retries + 1}), retrying in ${waitMs / 1e3}s`) + import_chalk.default.gray(` \u2014 ${err.message}`)
273
- );
274
339
  onRetry?.(attempt + 1, err);
275
- await sleep(waitMs);
340
+ await retryCountdown({
341
+ attempt: attempt + 1,
342
+ maxAttempts: retries + 1,
343
+ waitMs,
344
+ errorMessage: err.message ?? String(err),
345
+ label
346
+ });
276
347
  }
277
348
  }
278
349
  throw new Error("unreachable");
@@ -6463,6 +6534,7 @@ ${spec}
6463
6534
  ${constitutionSection}
6464
6535
  === ${existingContent ? "Existing content (modify and return the complete file)" : "Create this file from scratch"} ===
6465
6536
  ${existingContent || "Output only the complete file content."}`;
6537
+ const fileSpinner = startSpinner(`${prefix}Generating ${import_chalk10.default.bold(item.file)}...`);
6466
6538
  try {
6467
6539
  const raw = await this.provider.generate(codePrompt, systemPrompt);
6468
6540
  const fileContent = stripCodeFences(raw);
@@ -6470,11 +6542,11 @@ ${existingContent || "Output only the complete file content."}`;
6470
6542
  await fs10.ensureDir(path6.dirname(fullPath));
6471
6543
  await fs10.writeFile(fullPath, fileContent, "utf-8");
6472
6544
  getActiveLogger()?.fileWritten(item.file);
6473
- console.log(`${prefix}${existingContent ? import_chalk10.default.yellow("~") : import_chalk10.default.green("+")} ${import_chalk10.default.bold(item.file)} ${import_chalk10.default.green("\u2714")}`);
6545
+ fileSpinner.succeed(`${existingContent ? import_chalk10.default.yellow("~") : import_chalk10.default.green("+")} ${import_chalk10.default.bold(item.file)}`);
6474
6546
  successCount++;
6475
6547
  writtenFiles.push(item.file);
6476
6548
  } catch (err) {
6477
- console.log(`${prefix}${import_chalk10.default.red("\u2718")} ${import_chalk10.default.bold(item.file)} \u2014 ${import_chalk10.default.red(err.message)}`);
6549
+ fileSpinner.fail(`${import_chalk10.default.bold(item.file)} \u2014 ${err.message}`);
6478
6550
  }
6479
6551
  }
6480
6552
  if (!taskLabel) {