ai-spec-dev 0.35.0 → 0.37.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/RELEASE_LOG.md +139 -0
- package/cli/commands/config.ts +18 -0
- package/cli/commands/create.ts +16 -1
- package/cli/utils.ts +4 -0
- package/core/code-generator.ts +6 -4
- package/core/dsl-extractor.ts +9 -1
- package/core/dsl-feedback.ts +7 -1
- package/core/dsl-validator.ts +32 -0
- package/core/key-store.ts +5 -4
- package/core/provider-utils.ts +39 -4
- package/dist/cli/index.js +121 -14
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +122 -15
- package/dist/cli/index.mjs.map +1 -1
- package/dist/index.d.mts +16 -1
- package/dist/index.d.ts +16 -1
- package/dist/index.js +77 -8
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +77 -9
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/tests/code-generator.test.ts +253 -0
- package/tests/context-loader.test.ts +207 -0
- package/tests/dsl-validator.test.ts +105 -0
- package/tests/mock-server-generator.test.ts +404 -0
- package/tests/openapi-exporter.test.ts +310 -0
- package/tests/reviewer.test.ts +214 -0
- package/tests/spec-generator.test.ts +228 -0
- package/tests/spec-versioning.test.ts +205 -0
- package/tests/types-generator.test.ts +347 -0
- package/tests/vcr.test.ts +355 -0
- package/.claude/commands/add-lesson.md +0 -34
- package/.claude/commands/check-layers.md +0 -65
- package/.claude/commands/installed-deps.md +0 -35
- package/.claude/commands/recall-lessons.md +0 -40
- package/.claude/commands/scan-singletons.md +0 -45
- package/.claude/commands/verify-imports.md +0 -48
- package/.claude/settings.local.json +0 -24
package/dist/index.mjs
CHANGED
|
@@ -131,14 +131,49 @@ function classifyError(err, label) {
|
|
|
131
131
|
const e = err;
|
|
132
132
|
const status = e.status ?? e.response?.status;
|
|
133
133
|
if (status === 401 || status === 403)
|
|
134
|
-
return new ProviderError(
|
|
134
|
+
return new ProviderError(
|
|
135
|
+
`Auth error (${label}): API key is invalid or expired.
|
|
136
|
+
\u2192 Check that the correct API key is set in your environment or ~/.ai-spec-keys.json
|
|
137
|
+
\u2192 Run "ai-spec model" to reconfigure your provider and key`,
|
|
138
|
+
"auth",
|
|
139
|
+
err
|
|
140
|
+
);
|
|
135
141
|
if (status === 429)
|
|
136
|
-
return new ProviderError(
|
|
142
|
+
return new ProviderError(
|
|
143
|
+
`Rate limit hit (${label}): too many requests.
|
|
144
|
+
\u2192 Wait a few minutes and retry, or switch to a different provider/model
|
|
145
|
+
\u2192 Check your provider's billing dashboard for quota status`,
|
|
146
|
+
"rate_limit",
|
|
147
|
+
err
|
|
148
|
+
);
|
|
137
149
|
if (e._timeout || e.message?.toLowerCase().includes("timed out"))
|
|
138
150
|
return new ProviderError(`Request timed out (${label})`, "timeout", err);
|
|
139
151
|
if (e.code === "ECONNRESET" || e.code === "ENOTFOUND" || e.code === "ECONNREFUSED")
|
|
140
|
-
return new ProviderError(
|
|
141
|
-
|
|
152
|
+
return new ProviderError(
|
|
153
|
+
`Network error (${label}): ${e.message}
|
|
154
|
+
\u2192 Check your internet connection and proxy settings (HTTPS_PROXY)
|
|
155
|
+
\u2192 If behind a firewall, ensure the provider's API endpoint is reachable`,
|
|
156
|
+
"network",
|
|
157
|
+
err
|
|
158
|
+
);
|
|
159
|
+
const msg = e.message ?? "";
|
|
160
|
+
if (status === 404 || msg.includes("model") && (msg.includes("not found") || msg.includes("does not exist")))
|
|
161
|
+
return new ProviderError(
|
|
162
|
+
`Model not found (${label}): ${msg}
|
|
163
|
+
\u2192 Run "ai-spec model" to see available models for your provider
|
|
164
|
+
\u2192 The model name may have changed \u2014 check your provider's documentation`,
|
|
165
|
+
"provider",
|
|
166
|
+
err
|
|
167
|
+
);
|
|
168
|
+
if (msg.includes("insufficient") || msg.includes("quota") || msg.includes("balance"))
|
|
169
|
+
return new ProviderError(
|
|
170
|
+
`Quota/balance error (${label}): ${msg}
|
|
171
|
+
\u2192 Check your provider's billing dashboard
|
|
172
|
+
\u2192 Consider switching to a different provider with "ai-spec model"`,
|
|
173
|
+
"provider",
|
|
174
|
+
err
|
|
175
|
+
);
|
|
176
|
+
return new ProviderError(`Provider error (${label}): ${msg}`, "provider", err);
|
|
142
177
|
}
|
|
143
178
|
function isRetryable(err) {
|
|
144
179
|
const e = err;
|
|
@@ -4073,7 +4108,7 @@ ${currentSpec}`,
|
|
|
4073
4108
|
|
|
4074
4109
|
// core/code-generator.ts
|
|
4075
4110
|
import chalk8 from "chalk";
|
|
4076
|
-
import { execSync } from "child_process";
|
|
4111
|
+
import { execSync, spawnSync } from "child_process";
|
|
4077
4112
|
import * as path6 from "path";
|
|
4078
4113
|
import * as fs10 from "fs-extra";
|
|
4079
4114
|
|
|
@@ -4700,6 +4735,21 @@ function validateDsl(raw) {
|
|
|
4700
4735
|
for (let i = 0; i < Math.min(eps.length, MAX_ENDPOINTS); i++) {
|
|
4701
4736
|
validateEndpoint(eps[i], `endpoints[${i}]`, errors);
|
|
4702
4737
|
}
|
|
4738
|
+
const seenEpIds = /* @__PURE__ */ new Set();
|
|
4739
|
+
for (let i = 0; i < Math.min(eps.length, MAX_ENDPOINTS); i++) {
|
|
4740
|
+
const ep = eps[i];
|
|
4741
|
+
if (ep && typeof ep === "object" && typeof ep["id"] === "string") {
|
|
4742
|
+
const id = ep["id"];
|
|
4743
|
+
if (seenEpIds.has(id)) {
|
|
4744
|
+
errors.push({
|
|
4745
|
+
path: `endpoints[${i}].id`,
|
|
4746
|
+
message: `Duplicate endpoint id "${id}" \u2014 each endpoint must have a unique id`
|
|
4747
|
+
});
|
|
4748
|
+
} else {
|
|
4749
|
+
seenEpIds.add(id);
|
|
4750
|
+
}
|
|
4751
|
+
}
|
|
4752
|
+
}
|
|
4703
4753
|
}
|
|
4704
4754
|
if (obj["behaviors"] !== void 0) {
|
|
4705
4755
|
if (!Array.isArray(obj["behaviors"])) {
|
|
@@ -4756,6 +4806,21 @@ function validateModel(raw, path10, errors) {
|
|
|
4756
4806
|
for (let j2 = 0; j2 < Math.min(fields.length, MAX_FIELDS_PER_MODEL); j2++) {
|
|
4757
4807
|
validateModelField(fields[j2], `${path10}.fields[${j2}]`, errors);
|
|
4758
4808
|
}
|
|
4809
|
+
const seenFieldNames = /* @__PURE__ */ new Set();
|
|
4810
|
+
for (let j2 = 0; j2 < Math.min(fields.length, MAX_FIELDS_PER_MODEL); j2++) {
|
|
4811
|
+
const f = fields[j2];
|
|
4812
|
+
if (f && typeof f === "object" && typeof f["name"] === "string") {
|
|
4813
|
+
const name = f["name"];
|
|
4814
|
+
if (seenFieldNames.has(name)) {
|
|
4815
|
+
errors.push({
|
|
4816
|
+
path: `${path10}.fields[${j2}].name`,
|
|
4817
|
+
message: `Duplicate field name "${name}" \u2014 each field within a model must have a unique name`
|
|
4818
|
+
});
|
|
4819
|
+
} else {
|
|
4820
|
+
seenFieldNames.add(name);
|
|
4821
|
+
}
|
|
4822
|
+
}
|
|
4823
|
+
}
|
|
4759
4824
|
}
|
|
4760
4825
|
if (m["relations"] !== void 0) {
|
|
4761
4826
|
if (!Array.isArray(m["relations"])) {
|
|
@@ -5786,9 +5851,10 @@ ${tasks.map((t) => `${t.id} [${t.layer}] ${t.title}
|
|
|
5786
5851
|
console.log(chalk8.cyan(` \u{1F916} Auto mode: running claude -p (non-interactive)...`));
|
|
5787
5852
|
console.log(chalk8.gray(` Spec: ${specFilePath}`));
|
|
5788
5853
|
try {
|
|
5789
|
-
|
|
5854
|
+
spawnSync(claudeCmd, ["-p", promptContent], {
|
|
5790
5855
|
cwd: workingDir,
|
|
5791
|
-
stdio: "inherit"
|
|
5856
|
+
stdio: "inherit",
|
|
5857
|
+
shell: false
|
|
5792
5858
|
});
|
|
5793
5859
|
console.log(chalk8.green("\n \u2714 Claude Code completed."));
|
|
5794
5860
|
} catch {
|
|
@@ -5840,9 +5906,10 @@ Full spec is at: ${specFilePath}
|
|
|
5840
5906
|
Implement ONLY this task. Do not implement other tasks.`;
|
|
5841
5907
|
let taskStatus = "done";
|
|
5842
5908
|
try {
|
|
5843
|
-
|
|
5909
|
+
spawnSync(claudeCmd, ["-p", taskPrompt], {
|
|
5844
5910
|
cwd: workingDir,
|
|
5845
|
-
stdio: "inherit"
|
|
5911
|
+
stdio: "inherit",
|
|
5912
|
+
shell: false
|
|
5846
5913
|
});
|
|
5847
5914
|
completed++;
|
|
5848
5915
|
} catch {
|
|
@@ -6843,6 +6910,7 @@ export {
|
|
|
6843
6910
|
TaskGenerator,
|
|
6844
6911
|
buildTaskPrompt,
|
|
6845
6912
|
createProvider,
|
|
6913
|
+
extractBehavioralContract,
|
|
6846
6914
|
extractComplianceScore,
|
|
6847
6915
|
extractMissingCount,
|
|
6848
6916
|
generateSpecWithTasks,
|