ccqa 1.60.2 → 1.60.4
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/dist/bin/ccqa.mjs +42 -23
- package/dist/package.json +1 -1
- package/package.json +1 -1
package/dist/bin/ccqa.mjs
CHANGED
|
@@ -338,6 +338,14 @@ function validateTestPathTemplate(template) {
|
|
|
338
338
|
if (!template.includes("{spec}") && !template.includes("{case}")) return "testPath must contain {spec} or {case}, or every case would generate onto the same file";
|
|
339
339
|
return null;
|
|
340
340
|
}
|
|
341
|
+
/**
|
|
342
|
+
* The directory every test the template names sits under: its path up to the
|
|
343
|
+
* first placeholder's directory. Empty when the template opens with one.
|
|
344
|
+
*/
|
|
345
|
+
function testPathRoot(template) {
|
|
346
|
+
const head = template.split("{")[0];
|
|
347
|
+
return head.slice(0, head.lastIndexOf("/") + 1).replace(/\/$/, "");
|
|
348
|
+
}
|
|
341
349
|
/** What a recording beside its test is called: whose file it is, and what it is. */
|
|
342
350
|
const RECORDING_EXTENSION = ".ccqa.ir.json";
|
|
343
351
|
/**
|
|
@@ -9344,8 +9352,12 @@ const ViolationsSchema = z.object({ ruleViolations: z.array(z.unknown()) });
|
|
|
9344
9352
|
* treats it as one it could not obtain.
|
|
9345
9353
|
*/
|
|
9346
9354
|
const MAX_REVIEW_TURNS = 80;
|
|
9347
|
-
/**
|
|
9348
|
-
|
|
9355
|
+
/**
|
|
9356
|
+
* A wedged call, not a slow one. A review of a real suite reads the test, its
|
|
9357
|
+
* page objects and many of the suite's files to count conventions, which runs
|
|
9358
|
+
* past ten minutes; one that is still going after twenty is stuck.
|
|
9359
|
+
*/
|
|
9360
|
+
const REVIEW_TIMEOUT_MS = 20 * 6e4;
|
|
9349
9361
|
/** Read-only: a reviewer reads the repository and changes nothing in it. */
|
|
9350
9362
|
const REVIEW_TOOLS = [
|
|
9351
9363
|
"Read",
|
|
@@ -14788,7 +14800,7 @@ const LLM_GEN_ALLOWED_TOOLS = [
|
|
|
14788
14800
|
const LlmFileSchema = z.object({
|
|
14789
14801
|
path: z.string().min(1),
|
|
14790
14802
|
contents: z.string(),
|
|
14791
|
-
kind: z.string().
|
|
14803
|
+
kind: z.string().optional()
|
|
14792
14804
|
});
|
|
14793
14805
|
const LlmOutputSchema = z.object({
|
|
14794
14806
|
files: z.array(LlmFileSchema),
|
|
@@ -14820,21 +14832,29 @@ function parseLlmGenOutput(raw) {
|
|
|
14820
14832
|
const issues = res.error.issues.map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`);
|
|
14821
14833
|
throw new Error(`reply JSON does not match {files:[{path,contents,kind}],summary}: ${issues.join("; ")}`);
|
|
14822
14834
|
}
|
|
14823
|
-
const kindWarnings = [];
|
|
14824
14835
|
return {
|
|
14825
|
-
files: res.data.files.map((f) => {
|
|
14826
|
-
|
|
14827
|
-
|
|
14828
|
-
|
|
14829
|
-
|
|
14830
|
-
|
|
14831
|
-
|
|
14832
|
-
|
|
14833
|
-
|
|
14834
|
-
|
|
14835
|
-
|
|
14836
|
-
|
|
14837
|
-
|
|
14836
|
+
files: res.data.files.map((f) => ({
|
|
14837
|
+
path: f.path,
|
|
14838
|
+
contents: f.contents,
|
|
14839
|
+
kind: f.kind === "support" ? "support" : "test"
|
|
14840
|
+
})),
|
|
14841
|
+
summary: res.data.summary
|
|
14842
|
+
};
|
|
14843
|
+
}
|
|
14844
|
+
/**
|
|
14845
|
+
* The file at the configured test path is the test and every other file is
|
|
14846
|
+
* support — whatever the reply labelled them. The path already says which one
|
|
14847
|
+
* a file is, and a model that labels a page object "test" would otherwise fail
|
|
14848
|
+
* the whole generation over a word.
|
|
14849
|
+
*/
|
|
14850
|
+
function kindByPath(output, policy) {
|
|
14851
|
+
const testAbs = resolve(policy.cwd, policy.testPath);
|
|
14852
|
+
return {
|
|
14853
|
+
...output,
|
|
14854
|
+
files: output.files.map((f) => ({
|
|
14855
|
+
...f,
|
|
14856
|
+
kind: resolve(policy.cwd, f.path) === testAbs ? "test" : "support"
|
|
14857
|
+
}))
|
|
14838
14858
|
};
|
|
14839
14859
|
}
|
|
14840
14860
|
/**
|
|
@@ -14994,7 +15014,6 @@ async function generateWithLlmEngine(req) {
|
|
|
14994
15014
|
requireTestFile: true,
|
|
14995
15015
|
allowEmpty: false
|
|
14996
15016
|
}), "run");
|
|
14997
|
-
warnings.push(...output.kindWarnings);
|
|
14998
15017
|
return finalizeAndVerify({
|
|
14999
15018
|
ctx,
|
|
15000
15019
|
target: req.target,
|
|
@@ -15480,10 +15499,12 @@ async function identifiersInProject(ctx, writeRoots, emitted) {
|
|
|
15480
15499
|
const names = [...source.matchAll(/\bexport\s+(?:default\s+)?(?:abstract\s+)?class\s+([A-Za-z0-9_]+)/g)].map((m) => m[1]);
|
|
15481
15500
|
owners.set(rel, names);
|
|
15482
15501
|
}
|
|
15502
|
+
const testsRoot = ctx.targetConfig.testPath ? testPathRoot(ctx.targetConfig.testPath) : "";
|
|
15483
15503
|
const roots = new Set([
|
|
15484
15504
|
...writeRoots,
|
|
15485
15505
|
...ctx.resources.map((r) => "path" in r ? r.path : ""),
|
|
15486
|
-
dirname(ctx.testPath)
|
|
15506
|
+
dirname(ctx.testPath),
|
|
15507
|
+
testsRoot
|
|
15487
15508
|
].filter((r) => r.length > 0).map((r) => resolve(ctx.cwd, r)));
|
|
15488
15509
|
const found = new Map([...emitted.keys()].map((rel) => [rel, /* @__PURE__ */ new Set()]));
|
|
15489
15510
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -15559,8 +15580,7 @@ async function invokeForFiles(p) {
|
|
|
15559
15580
|
model: p.ctx.model
|
|
15560
15581
|
}, () => {});
|
|
15561
15582
|
if (isError) throw new Error(`Claude invocation failed: ${tail(result)}`);
|
|
15562
|
-
const output = parseLlmGenOutput(result);
|
|
15563
|
-
for (const w of output.kindWarnings) warn(w);
|
|
15583
|
+
const output = kindByPath(parseLlmGenOutput(result), p.policy);
|
|
15564
15584
|
const errors = validateOutput(output, p);
|
|
15565
15585
|
if (errors.length > 0) throw new Error(errors.join("; "));
|
|
15566
15586
|
return output;
|
|
@@ -15603,8 +15623,7 @@ function validateOutput(output, p) {
|
|
|
15603
15623
|
if (fileError) errors.push(fileError);
|
|
15604
15624
|
}
|
|
15605
15625
|
const tests = output.files.filter((f) => f.kind === "test");
|
|
15606
|
-
if (p.requireTestFile && tests.length === 0) errors.push(
|
|
15607
|
-
if (tests.length > 1) errors.push(`output contains ${tests.length} "kind": "test" files; a spec has exactly one`);
|
|
15626
|
+
if (p.requireTestFile && tests.length === 0) errors.push(`the test file must be written to ${p.policy.testPath}; the reply has no file there`);
|
|
15608
15627
|
return errors;
|
|
15609
15628
|
}
|
|
15610
15629
|
function toPromptResource(r) {
|
package/dist/package.json
CHANGED