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.
- package/.ai-spec-workspace.json +17 -0
- package/.ai-spec.json +7 -0
- package/cli/pipeline/single-repo.ts +19 -10
- package/core/cli-ui.ts +136 -0
- package/core/code-generator.ts +4 -2
- package/core/error-feedback.ts +4 -2
- package/core/provider-utils.ts +8 -7
- package/demo-backend/.ai-spec-constitution.md +65 -0
- package/demo-backend/package.json +21 -0
- package/demo-backend/prisma/schema.prisma +22 -0
- package/demo-backend/specs/feature-1-bookmark-id-uuid-title-string-required-url-str-v1.dsl.json +186 -0
- package/demo-backend/specs/feature-1-bookmark-id-uuid-title-string-required-url-str-v1.md +211 -0
- package/demo-backend/src/controllers/bookmark.controller.test.ts +255 -0
- package/demo-backend/src/controllers/bookmark.controller.ts +187 -0
- package/demo-backend/src/index.ts +17 -0
- package/demo-backend/src/routes/bookmark.routes.test.ts +264 -0
- package/demo-backend/src/routes/bookmark.routes.ts +11 -0
- package/demo-backend/src/routes/index.ts +8 -0
- package/demo-backend/src/services/bookmark.service.test.ts +433 -0
- package/demo-backend/src/services/bookmark.service.ts +261 -0
- package/demo-backend/tsconfig.json +12 -0
- package/demo-frontend/.ai-spec-constitution.md +95 -0
- package/demo-frontend/package.json +23 -0
- package/demo-frontend/src/App.tsx +12 -0
- package/demo-frontend/src/main.tsx +9 -0
- package/demo-frontend/tsconfig.json +13 -0
- package/dist/cli/index.js +130 -21
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +130 -21
- package/dist/cli/index.mjs.map +1 -1
- package/dist/index.js +80 -8
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +80 -8
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/RELEASE_LOG.md +0 -2962
- 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/
|
|
185
|
+
// core/cli-ui.ts
|
|
186
186
|
var import_chalk = __toESM(require("chalk"));
|
|
187
|
-
var
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
6549
|
+
fileSpinner.fail(`${import_chalk10.default.bold(item.file)} \u2014 ${err.message}`);
|
|
6478
6550
|
}
|
|
6479
6551
|
}
|
|
6480
6552
|
if (!taskLabel) {
|