ccqa 1.10.0 → 1.10.1
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 +99 -79
- package/dist/package.json +1 -1
- package/package.json +1 -1
package/dist/bin/ccqa.mjs
CHANGED
|
@@ -39,6 +39,26 @@ const EVIDENCE_SUBDIR = "evidence";
|
|
|
39
39
|
/** Per-spec run artifacts for external (runCommand) targets: `artifacts/<feature>__<spec>/`. */
|
|
40
40
|
const ARTIFACTS_SUBDIR = "artifacts";
|
|
41
41
|
//#endregion
|
|
42
|
+
//#region src/run/errors.ts
|
|
43
|
+
/**
|
|
44
|
+
* Usage error (bad flag combination, broken profile, failed `git diff`, …)
|
|
45
|
+
* thrown by the `run` pipeline and the helpers it calls, e.g.
|
|
46
|
+
* `collectChangedSpecs`. `executeRun` never calls `process.exit`, so each
|
|
47
|
+
* host maps this itself: the CLI action catches it and exits with
|
|
48
|
+
* `exitCode`; the hub runner records it as a run-level error.
|
|
49
|
+
*/
|
|
50
|
+
var RunUsageError = class extends Error {
|
|
51
|
+
exitCode = 2;
|
|
52
|
+
constructor(message) {
|
|
53
|
+
super(message);
|
|
54
|
+
this.name = "RunUsageError";
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
/** An error's message, whatever was thrown. Shared so the run modules report failures alike. */
|
|
58
|
+
function errMessage(err) {
|
|
59
|
+
return err instanceof Error ? err.message : String(err);
|
|
60
|
+
}
|
|
61
|
+
//#endregion
|
|
42
62
|
//#region src/runtime/env-vars.ts
|
|
43
63
|
const ENV_VAR_RE = /\$\{([A-Z_][A-Z0-9_]*)\}|\$([A-Z_][A-Z0-9_]*)/g;
|
|
44
64
|
const ANY_VAR_RE = /\$\{([A-Za-z_][A-Za-z0-9_]*)\}|\$([A-Za-z_][A-Za-z0-9_]*)/g;
|
|
@@ -399,7 +419,7 @@ function parseSpecPath(specPath) {
|
|
|
399
419
|
featureName: parts[0],
|
|
400
420
|
specName: parts[1]
|
|
401
421
|
};
|
|
402
|
-
throw new
|
|
422
|
+
throw new RunUsageError(`Invalid spec path: "${specPath}". Expected "<feature>/<spec>" or "features/<feature>/test-cases/<spec>".`);
|
|
403
423
|
}
|
|
404
424
|
function getFeatureDir(featureName, cwd) {
|
|
405
425
|
return join(getCcqaDir(cwd), "features", featureName);
|
|
@@ -4669,6 +4689,34 @@ function isWithin(rootAbs, abs) {
|
|
|
4669
4689
|
return abs === rootAbs || abs.startsWith(rootAbs + sep);
|
|
4670
4690
|
}
|
|
4671
4691
|
//#endregion
|
|
4692
|
+
//#region src/cli/usage-errors.ts
|
|
4693
|
+
/**
|
|
4694
|
+
* Turn a `RunUsageError` into `[error] <message>` plus its exit code, for a
|
|
4695
|
+
* commander action.
|
|
4696
|
+
*
|
|
4697
|
+
* The helpers shared across commands — `resolveAnalysisBase`,
|
|
4698
|
+
* `collectChangedSpecs` — signal a bad invocation this way, so every command
|
|
4699
|
+
* that calls one needs the same boundary. Without it the process dies on an
|
|
4700
|
+
* unhandled rejection and prints a stack trace: `bin/ccqa.ts` installs no
|
|
4701
|
+
* global handler, so there is nowhere else for it to land.
|
|
4702
|
+
*
|
|
4703
|
+
* Lives here rather than beside `RunUsageError` because `src/run/errors.ts` is
|
|
4704
|
+
* deliberately dependency-free, and this needs the logger.
|
|
4705
|
+
*/
|
|
4706
|
+
function withUsageErrors(fn) {
|
|
4707
|
+
return async (...args) => {
|
|
4708
|
+
try {
|
|
4709
|
+
await fn(...args);
|
|
4710
|
+
} catch (err) {
|
|
4711
|
+
if (err instanceof RunUsageError) {
|
|
4712
|
+
error(err.message);
|
|
4713
|
+
process.exit(err.exitCode);
|
|
4714
|
+
}
|
|
4715
|
+
throw err;
|
|
4716
|
+
}
|
|
4717
|
+
};
|
|
4718
|
+
}
|
|
4719
|
+
//#endregion
|
|
4672
4720
|
//#region src/runtime/profile-env.ts
|
|
4673
4721
|
/**
|
|
4674
4722
|
* Profile env vars are hub-sourced (pulled via `ccqa hub`) and merged into
|
|
@@ -5121,7 +5169,7 @@ const DraftNamingSchema = z.object({
|
|
|
5121
5169
|
//#endregion
|
|
5122
5170
|
//#region src/cli/draft.ts
|
|
5123
5171
|
const CATEGORY_LABEL = DRAFT_CATEGORY_LABEL;
|
|
5124
|
-
const draftCommand = addLanguageOption(new Command("draft").argument("[feature/spec]", "Optional spec path (e.g. tasks/create-and-complete). If omitted, Claude proposes one from your intent.").description("Interactively draft and refine a spec.yaml with Claude Code").option("--instruction <text>", "Non-interactive single-shot instruction (skips the interactive loop)").option("--apply", "Auto-apply each generated patch without [y/N] confirmation", false)).action(async (specPath, opts) => {
|
|
5172
|
+
const draftCommand = addLanguageOption(new Command("draft").argument("[feature/spec]", "Optional spec path (e.g. tasks/create-and-complete). If omitted, Claude proposes one from your intent.").description("Interactively draft and refine a spec.yaml with Claude Code").option("--instruction <text>", "Non-interactive single-shot instruction (skips the interactive loop)").option("--apply", "Auto-apply each generated patch without [y/N] confirmation", false)).action(withUsageErrors(async (specPath, opts) => {
|
|
5125
5173
|
await ensureCcqaDir();
|
|
5126
5174
|
let featureName;
|
|
5127
5175
|
let specName;
|
|
@@ -5134,7 +5182,7 @@ const draftCommand = addLanguageOption(new Command("draft").argument("[feature/s
|
|
|
5134
5182
|
prefilledIntent = intent;
|
|
5135
5183
|
}
|
|
5136
5184
|
await runDraft(featureName, specName, opts, prefilledIntent);
|
|
5137
|
-
});
|
|
5185
|
+
}));
|
|
5138
5186
|
async function runDraft(featureName, specName, opts, prefilledIntent) {
|
|
5139
5187
|
header("draft", `${featureName}/${specName}`);
|
|
5140
5188
|
const ja = useJapanesePrompts(opts.language);
|
|
@@ -6426,26 +6474,6 @@ function createDiffProvider(args) {
|
|
|
6426
6474
|
} };
|
|
6427
6475
|
}
|
|
6428
6476
|
//#endregion
|
|
6429
|
-
//#region src/run/errors.ts
|
|
6430
|
-
/**
|
|
6431
|
-
* Usage error (bad flag combination, broken profile, failed `git diff`, …)
|
|
6432
|
-
* thrown by the `run` pipeline and the helpers it calls, e.g.
|
|
6433
|
-
* `collectChangedSpecs`. `executeRun` never calls `process.exit`, so each
|
|
6434
|
-
* host maps this itself: the CLI action catches it and exits with
|
|
6435
|
-
* `exitCode`; the hub runner records it as a run-level error.
|
|
6436
|
-
*/
|
|
6437
|
-
var RunUsageError = class extends Error {
|
|
6438
|
-
exitCode = 2;
|
|
6439
|
-
constructor(message) {
|
|
6440
|
-
super(message);
|
|
6441
|
-
this.name = "RunUsageError";
|
|
6442
|
-
}
|
|
6443
|
-
};
|
|
6444
|
-
/** An error's message, whatever was thrown. Shared so the run modules report failures alike. */
|
|
6445
|
-
function errMessage(err) {
|
|
6446
|
-
return err instanceof Error ? err.message : String(err);
|
|
6447
|
-
}
|
|
6448
|
-
//#endregion
|
|
6449
6477
|
//#region src/run/git-context.ts
|
|
6450
6478
|
/** The `--failure-analysis` value that selects per-spec hub-ledger baselines. */
|
|
6451
6479
|
const LAST_GREEN = "last-green";
|
|
@@ -8214,6 +8242,14 @@ function isCcqaPath(path) {
|
|
|
8214
8242
|
return /(?:^|\/)\.ccqa\//.test(path);
|
|
8215
8243
|
}
|
|
8216
8244
|
/**
|
|
8245
|
+
* A malformed reply costs the whole selection, so it is worth one more call
|
|
8246
|
+
* before giving up. `ccqa drift` retries per spec for the same reason; this
|
|
8247
|
+
* call carries every undecided spec at once, so the blast radius is larger,
|
|
8248
|
+
* not smaller. Observed in practice: three runs over one commit produced a
|
|
8249
|
+
* parse failure, a clean answer, and a different clean answer.
|
|
8250
|
+
*/
|
|
8251
|
+
const MAX_ATTEMPTS = 2;
|
|
8252
|
+
/**
|
|
8217
8253
|
* One model call for the whole undecided set, not one per spec: the specs are
|
|
8218
8254
|
* judged against the same diff, and seeing them together is what lets the
|
|
8219
8255
|
* model tell them apart.
|
|
@@ -8224,32 +8260,44 @@ function isCcqaPath(path) {
|
|
|
8224
8260
|
*/
|
|
8225
8261
|
async function judgeWithModel(input) {
|
|
8226
8262
|
const { productChanges, undecided, cwd, base, head, model } = input;
|
|
8227
|
-
const { result, isError } = await invokeClaudeStreaming({
|
|
8228
|
-
prompt: buildSelectPrompt({
|
|
8229
|
-
changed: productChanges,
|
|
8230
|
-
specs: undecided,
|
|
8231
|
-
base,
|
|
8232
|
-
head
|
|
8233
|
-
}),
|
|
8234
|
-
systemPrompt: buildSelectSystemPrompt(),
|
|
8235
|
-
allowedTools: [
|
|
8236
|
-
"Read",
|
|
8237
|
-
"Grep",
|
|
8238
|
-
"Glob"
|
|
8239
|
-
],
|
|
8240
|
-
silenceBashLog: true,
|
|
8241
|
-
cwd,
|
|
8242
|
-
...model ? { model } : {}
|
|
8243
|
-
}, (_msg) => {});
|
|
8244
|
-
if (isError) return abandonSelection(undecided, "the selection model returned an error");
|
|
8245
|
-
const json = extractJsonBlock(result);
|
|
8246
|
-
if (!json) return abandonSelection(undecided, "the selection model returned no JSON block");
|
|
8247
8263
|
let parsed;
|
|
8248
|
-
|
|
8249
|
-
|
|
8250
|
-
|
|
8251
|
-
|
|
8264
|
+
let lastError = "";
|
|
8265
|
+
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
|
|
8266
|
+
const { result, isError } = await invokeClaudeStreaming({
|
|
8267
|
+
prompt: buildSelectPrompt({
|
|
8268
|
+
changed: productChanges,
|
|
8269
|
+
specs: undecided,
|
|
8270
|
+
base,
|
|
8271
|
+
head
|
|
8272
|
+
}),
|
|
8273
|
+
systemPrompt: buildSelectSystemPrompt(),
|
|
8274
|
+
allowedTools: [
|
|
8275
|
+
"Read",
|
|
8276
|
+
"Grep",
|
|
8277
|
+
"Glob"
|
|
8278
|
+
],
|
|
8279
|
+
silenceBashLog: true,
|
|
8280
|
+
cwd,
|
|
8281
|
+
...model ? { model } : {}
|
|
8282
|
+
}, (_msg) => {});
|
|
8283
|
+
if (isError) {
|
|
8284
|
+
lastError = "the selection model returned an error";
|
|
8285
|
+
continue;
|
|
8286
|
+
}
|
|
8287
|
+
const json = extractJsonBlock(result);
|
|
8288
|
+
if (!json) {
|
|
8289
|
+
lastError = "the selection model returned no JSON block";
|
|
8290
|
+
continue;
|
|
8291
|
+
}
|
|
8292
|
+
try {
|
|
8293
|
+
parsed = JSON.parse(json);
|
|
8294
|
+
lastError = "";
|
|
8295
|
+
break;
|
|
8296
|
+
} catch (e) {
|
|
8297
|
+
lastError = `the selection model's JSON did not parse: ${e.message}`;
|
|
8298
|
+
}
|
|
8252
8299
|
}
|
|
8300
|
+
if (lastError) return abandonSelection(undecided, `${lastError} (${MAX_ATTEMPTS} attempts)`);
|
|
8253
8301
|
const changedPaths = new Set(productChanges.map((f) => f.path));
|
|
8254
8302
|
const byUndecidedKey = new Map(undecided.map((s) => [specKey(s), s]));
|
|
8255
8303
|
const answers = /* @__PURE__ */ new Map();
|
|
@@ -14954,7 +15002,7 @@ async function confirmOverwrite(path) {
|
|
|
14954
15002
|
rl.close();
|
|
14955
15003
|
}
|
|
14956
15004
|
}
|
|
14957
|
-
const generateCommand = addHubOptions(addProfileOption(addLanguageOption(new Command("generate").argument("<feature/spec>", "Spec id in '<feature>/<spec>' form (resolves to .ccqa/features/<feature>/test-cases/<spec>/)").description("Generate test code from a spec via its target plugin. Recording-backed targets compile the existing ir.json (run `ccqa record` first); spec-input targets generate directly from the spec.").option("-m, --model <name>", "Claude model alias ('sonnet'|'opus'|'haiku') or full ID. Overrides CCQA_MODEL.").option("--target <id>", "Generate through this target instead of the spec's own — e.g. emit a Playwright spec from an agent-browser recording. The spec's `target:` stays the default for `ccqa run`.").option("--auto-fix <mode>", "Auto-fix behaviour during script generation: 'interactive' (default, prompt y/N; declines on non-TTY), 'auto' (apply without prompt, for CI), 'skip' (agent-browser: apply only high-confidence fixes; external targets like playwright/runn: no fix pass at all).", parseAutoFixFlag, "interactive").option("--max-retries <n>", "Maximum number of auto-fix retries", "3").option("--force", "Overwrite previously generated test code without warning").option("--no-snapshot", "Don't pin AGENT_BROWSER_SESSION / capture page snapshots after a failure (debug toggle)").option("--update-agent-prompt", "After generation, ask Claude to refresh the target's \"<target>.agent\" learning prompt on the hub from a summary of the run. LLM-generating targets (playwright, runn) only; requires a hub connection.").option("--cwd <path>", "Working directory containing the .ccqa/ tree (monorepo support). Defaults to the current directory.").option("--project <name>", "Project name for the hub. Defaults to the current directory's name.")))).action(async (specPath, opts) => {
|
|
15005
|
+
const generateCommand = addHubOptions(addProfileOption(addLanguageOption(new Command("generate").argument("<feature/spec>", "Spec id in '<feature>/<spec>' form (resolves to .ccqa/features/<feature>/test-cases/<spec>/)").description("Generate test code from a spec via its target plugin. Recording-backed targets compile the existing ir.json (run `ccqa record` first); spec-input targets generate directly from the spec.").option("-m, --model <name>", "Claude model alias ('sonnet'|'opus'|'haiku') or full ID. Overrides CCQA_MODEL.").option("--target <id>", "Generate through this target instead of the spec's own — e.g. emit a Playwright spec from an agent-browser recording. The spec's `target:` stays the default for `ccqa run`.").option("--auto-fix <mode>", "Auto-fix behaviour during script generation: 'interactive' (default, prompt y/N; declines on non-TTY), 'auto' (apply without prompt, for CI), 'skip' (agent-browser: apply only high-confidence fixes; external targets like playwright/runn: no fix pass at all).", parseAutoFixFlag, "interactive").option("--max-retries <n>", "Maximum number of auto-fix retries", "3").option("--force", "Overwrite previously generated test code without warning").option("--no-snapshot", "Don't pin AGENT_BROWSER_SESSION / capture page snapshots after a failure (debug toggle)").option("--update-agent-prompt", "After generation, ask Claude to refresh the target's \"<target>.agent\" learning prompt on the hub from a summary of the run. LLM-generating targets (playwright, runn) only; requires a hub connection.").option("--cwd <path>", "Working directory containing the .ccqa/ tree (monorepo support). Defaults to the current directory.").option("--project <name>", "Project name for the hub. Defaults to the current directory's name.")))).action(withUsageErrors(async (specPath, opts) => {
|
|
14958
15006
|
const { featureName, specName } = parseSpecPath(specPath);
|
|
14959
15007
|
const language = opts.language ?? "auto";
|
|
14960
15008
|
const cwd = resolveCwd(opts.cwd);
|
|
@@ -15009,14 +15057,14 @@ const generateCommand = addHubOptions(addProfileOption(addLanguageOption(new Com
|
|
|
15009
15057
|
...language ? { language } : {},
|
|
15010
15058
|
...opts.model ? { model: opts.model } : {}
|
|
15011
15059
|
});
|
|
15012
|
-
});
|
|
15060
|
+
}));
|
|
15013
15061
|
//#endregion
|
|
15014
15062
|
//#region src/cli/record.ts
|
|
15015
15063
|
const VALIDATION_MODES = ["lenient", "strict"];
|
|
15016
15064
|
const recordCommand = addHubOptions(addProfileOption(addLanguageOption(new Command("record").argument("<feature/spec>", "Spec id in '<feature>/<spec>' form (resolves to .ccqa/features/<feature>/test-cases/<spec>/)").description("Record a test from a spec: run agent-browser to collect actions (trace), then compile them into runnable code via the spec's target (generate) — a vitest test.spec.ts for agent-browser, a @playwright/test spec for the playwright target. Recording-backed targets only; spec-input targets like runn have no trace step (use `ccqa generate`), and agent-browser live specs need no recording.").option("-m, --model <name>", "Claude model alias ('sonnet'|'opus'|'haiku') or full ID. Overrides CCQA_MODEL.").option("--validation-mode <mode>", "Post-trace validation behaviour: 'lenient' (default) tags failing actions; 'strict' drops them.", (raw) => {
|
|
15017
15065
|
if (VALIDATION_MODES.includes(raw)) return raw;
|
|
15018
15066
|
throw new Error(`--validation-mode must be one of ${VALIDATION_MODES.join(" | ")}`);
|
|
15019
|
-
}, "lenient").option("--auto-fix <mode>", "Auto-fix behaviour during script generation: 'interactive' (default, prompt y/N; declines on non-TTY), 'auto' (apply without prompt, for CI), 'skip' (agent-browser: apply only high-confidence fixes; external targets like playwright/runn: no fix pass at all).", parseAutoFixFlag, "interactive").option("--max-retries <n>", "Maximum number of auto-fix retries", "3").option("--force", "Overwrite an existing test.spec.ts without warning").option("--no-snapshot", "Don't pin AGENT_BROWSER_SESSION / capture page snapshots after a failure (debug toggle)").option("--skip-trace", "Skip the trace step and run codegen against an existing ir.json").option("--skip-codegen", "Run only the trace step (do not generate test.spec.ts)").option("--update-agent-prompt", "After the trace finishes, ask Claude to refresh the \"record.agent\" prompt on the hub from a summary of the run. Requires a hub connection.").option("--cwd <path>", "Working directory containing the .ccqa/ tree (monorepo support). Defaults to the current directory.").option("--project <name>", "Project name for the hub. Defaults to the current directory's name.")))).action(async (specPath, opts) => {
|
|
15067
|
+
}, "lenient").option("--auto-fix <mode>", "Auto-fix behaviour during script generation: 'interactive' (default, prompt y/N; declines on non-TTY), 'auto' (apply without prompt, for CI), 'skip' (agent-browser: apply only high-confidence fixes; external targets like playwright/runn: no fix pass at all).", parseAutoFixFlag, "interactive").option("--max-retries <n>", "Maximum number of auto-fix retries", "3").option("--force", "Overwrite an existing test.spec.ts without warning").option("--no-snapshot", "Don't pin AGENT_BROWSER_SESSION / capture page snapshots after a failure (debug toggle)").option("--skip-trace", "Skip the trace step and run codegen against an existing ir.json").option("--skip-codegen", "Run only the trace step (do not generate test.spec.ts)").option("--update-agent-prompt", "After the trace finishes, ask Claude to refresh the \"record.agent\" prompt on the hub from a summary of the run. Requires a hub connection.").option("--cwd <path>", "Working directory containing the .ccqa/ tree (monorepo support). Defaults to the current directory.").option("--project <name>", "Project name for the hub. Defaults to the current directory's name.")))).action(withUsageErrors(async (specPath, opts) => {
|
|
15020
15068
|
const { featureName, specName } = parseSpecPath(specPath);
|
|
15021
15069
|
const language = opts.language ?? "auto";
|
|
15022
15070
|
if (opts.skipTrace && opts.skipCodegen) {
|
|
@@ -15103,7 +15151,7 @@ const recordCommand = addHubOptions(addProfileOption(addLanguageOption(new Comma
|
|
|
15103
15151
|
...language ? { language } : {},
|
|
15104
15152
|
...opts.model ? { model: opts.model } : {}
|
|
15105
15153
|
});
|
|
15106
|
-
});
|
|
15154
|
+
}));
|
|
15107
15155
|
/**
|
|
15108
15156
|
* Compact summary of the trace pass for the record agent-prompt refresh.
|
|
15109
15157
|
* Steps are reconstructed from the trace's status-line protocol (STEP_START
|
|
@@ -15416,34 +15464,6 @@ function driftResultsToReport(results, meta) {
|
|
|
15416
15464
|
};
|
|
15417
15465
|
}
|
|
15418
15466
|
//#endregion
|
|
15419
|
-
//#region src/cli/usage-errors.ts
|
|
15420
|
-
/**
|
|
15421
|
-
* Turn a `RunUsageError` into `[error] <message>` plus its exit code, for a
|
|
15422
|
-
* commander action.
|
|
15423
|
-
*
|
|
15424
|
-
* The helpers shared across commands — `resolveAnalysisBase`,
|
|
15425
|
-
* `collectChangedSpecs` — signal a bad invocation this way, so every command
|
|
15426
|
-
* that calls one needs the same boundary. Without it the process dies on an
|
|
15427
|
-
* unhandled rejection and prints a stack trace: `bin/ccqa.ts` installs no
|
|
15428
|
-
* global handler, so there is nowhere else for it to land.
|
|
15429
|
-
*
|
|
15430
|
-
* Lives here rather than beside `RunUsageError` because `src/run/errors.ts` is
|
|
15431
|
-
* deliberately dependency-free, and this needs the logger.
|
|
15432
|
-
*/
|
|
15433
|
-
function withUsageErrors(fn) {
|
|
15434
|
-
return async (...args) => {
|
|
15435
|
-
try {
|
|
15436
|
-
await fn(...args);
|
|
15437
|
-
} catch (err) {
|
|
15438
|
-
if (err instanceof RunUsageError) {
|
|
15439
|
-
error(err.message);
|
|
15440
|
-
process.exit(err.exitCode);
|
|
15441
|
-
}
|
|
15442
|
-
throw err;
|
|
15443
|
-
}
|
|
15444
|
-
};
|
|
15445
|
-
}
|
|
15446
|
-
//#endregion
|
|
15447
15467
|
//#region src/cli/drift.ts
|
|
15448
15468
|
const DEFAULT_CONCURRENCY = 3;
|
|
15449
15469
|
const driftCommand = addLanguageOption(new Command("drift").argument("[feature/spec]", "Optional spec id. If omitted, every spec under .ccqa/features/ is checked.").description("Standalone spec ↔ codebase static audit. Use for PR checks where the browser isn't run. For run-time audit with a structured report, see `ccqa run --report`.").option("--format <fmt>", "Output format: text | json | github", "text").option("--severity <level>", "Exit non-zero on this severity or higher: warn | error", "error").option("--concurrency <n>", `Parallel spec checks (default: ${DEFAULT_CONCURRENCY})`).option("-m, --model <name>", "Claude model alias ('sonnet'|'opus'|'haiku') or full ID. Overrides CCQA_MODEL.").option("--cwd <path>", "Working directory used as both the .ccqa root and the codebase Claude reads. Useful for monorepos. Defaults to process.cwd().").option("--changed", "Restrict drift checks to the specs a change reaches, decided by `ccqa select-specs` against --base (or, in CI, $GITHUB_BASE_REF). Costs one model call; specs it cannot decide are checked rather than skipped.").option("--base <ref>", "Base ref to diff against when --changed is set. Defaults to $GITHUB_BASE_REF (CI pull_request runs); required otherwise.").option("--push", "Push the drift result to a ccqa hub as a run (kind: drift).").option("--project <name>", "Logical project name for the pushed run. Defaults to the current directory's name.").option(...hubUrlOption).option(...hubTokenOption).option(...hubHeaderOption)).action(withUsageErrors(async (specPath, opts) => {
|
package/dist/package.json
CHANGED