@mirnoorata/codexa 0.2.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/LICENSE +21 -0
- package/README.md +634 -0
- package/dist/artifacts.d.ts +2 -0
- package/dist/artifacts.js +375 -0
- package/dist/artifacts.js.map +1 -0
- package/dist/autonomy.d.ts +17 -0
- package/dist/autonomy.js +124 -0
- package/dist/autonomy.js.map +1 -0
- package/dist/autoverify/policy.d.ts +5 -0
- package/dist/autoverify/policy.js +18 -0
- package/dist/autoverify/policy.js.map +1 -0
- package/dist/autoverify.d.ts +45 -0
- package/dist/autoverify.js +1041 -0
- package/dist/autoverify.js.map +1 -0
- package/dist/cache-lock.d.ts +16 -0
- package/dist/cache-lock.js +181 -0
- package/dist/cache-lock.js.map +1 -0
- package/dist/cli/hooks.d.ts +5 -0
- package/dist/cli/hooks.js +264 -0
- package/dist/cli/hooks.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +1034 -0
- package/dist/cli.js.map +1 -0
- package/dist/codex-contract.d.ts +2 -0
- package/dist/codex-contract.js +78 -0
- package/dist/codex-contract.js.map +1 -0
- package/dist/command.d.ts +34 -0
- package/dist/command.js +162 -0
- package/dist/command.js.map +1 -0
- package/dist/doctor.d.ts +112 -0
- package/dist/doctor.js +518 -0
- package/dist/doctor.js.map +1 -0
- package/dist/eval/baseline.d.ts +7 -0
- package/dist/eval/baseline.js +146 -0
- package/dist/eval/baseline.js.map +1 -0
- package/dist/eval/historical.d.ts +4 -0
- package/dist/eval/historical.js +663 -0
- package/dist/eval/historical.js.map +1 -0
- package/dist/eval/render.d.ts +2 -0
- package/dist/eval/render.js +53 -0
- package/dist/eval/render.js.map +1 -0
- package/dist/eval/scoring.d.ts +21 -0
- package/dist/eval/scoring.js +618 -0
- package/dist/eval/scoring.js.map +1 -0
- package/dist/eval/synthetic.d.ts +36 -0
- package/dist/eval/synthetic.js +107 -0
- package/dist/eval/synthetic.js.map +1 -0
- package/dist/eval/types.d.ts +36 -0
- package/dist/eval/types.js +2 -0
- package/dist/eval/types.js.map +1 -0
- package/dist/eval.d.ts +140 -0
- package/dist/eval.js +551 -0
- package/dist/eval.js.map +1 -0
- package/dist/git.d.ts +17 -0
- package/dist/git.js +189 -0
- package/dist/git.js.map +1 -0
- package/dist/github-release.d.ts +47 -0
- package/dist/github-release.js +610 -0
- package/dist/github-release.js.map +1 -0
- package/dist/github-sync.d.ts +68 -0
- package/dist/github-sync.js +345 -0
- package/dist/github-sync.js.map +1 -0
- package/dist/graph.d.ts +10 -0
- package/dist/graph.js +665 -0
- package/dist/graph.js.map +1 -0
- package/dist/indexer/aliases.d.ts +2 -0
- package/dist/indexer/aliases.js +190 -0
- package/dist/indexer/aliases.js.map +1 -0
- package/dist/indexer/artifact-writing.d.ts +3 -0
- package/dist/indexer/artifact-writing.js +79 -0
- package/dist/indexer/artifact-writing.js.map +1 -0
- package/dist/indexer/discovery.d.ts +2 -0
- package/dist/indexer/discovery.js +5 -0
- package/dist/indexer/discovery.js.map +1 -0
- package/dist/indexer/external-facts.d.ts +6 -0
- package/dist/indexer/external-facts.js +45 -0
- package/dist/indexer/external-facts.js.map +1 -0
- package/dist/indexer/freshness.d.ts +8 -0
- package/dist/indexer/freshness.js +56 -0
- package/dist/indexer/freshness.js.map +1 -0
- package/dist/indexer/graph-stage.d.ts +2 -0
- package/dist/indexer/graph-stage.js +21 -0
- package/dist/indexer/graph-stage.js.map +1 -0
- package/dist/indexer/parsing.d.ts +30 -0
- package/dist/indexer/parsing.js +177 -0
- package/dist/indexer/parsing.js.map +1 -0
- package/dist/indexer/pipeline.d.ts +5 -0
- package/dist/indexer/pipeline.js +8 -0
- package/dist/indexer/pipeline.js.map +1 -0
- package/dist/indexer/ranking.d.ts +4 -0
- package/dist/indexer/ranking.js +134 -0
- package/dist/indexer/ranking.js.map +1 -0
- package/dist/indexer.d.ts +13 -0
- package/dist/indexer.js +395 -0
- package/dist/indexer.js.map +1 -0
- package/dist/init.d.ts +24 -0
- package/dist/init.js +566 -0
- package/dist/init.js.map +1 -0
- package/dist/language.d.ts +8 -0
- package/dist/language.js +123 -0
- package/dist/language.js.map +1 -0
- package/dist/live-index.d.ts +68 -0
- package/dist/live-index.js +215 -0
- package/dist/live-index.js.map +1 -0
- package/dist/lsp/assist.d.ts +44 -0
- package/dist/lsp/assist.js +331 -0
- package/dist/lsp/assist.js.map +1 -0
- package/dist/lsp/client.d.ts +59 -0
- package/dist/lsp/client.js +208 -0
- package/dist/lsp/client.js.map +1 -0
- package/dist/mcp/compaction.d.ts +15 -0
- package/dist/mcp/compaction.js +1249 -0
- package/dist/mcp/compaction.js.map +1 -0
- package/dist/mcp/envelope.d.ts +44 -0
- package/dist/mcp/envelope.js +425 -0
- package/dist/mcp/envelope.js.map +1 -0
- package/dist/mcp/prompts.d.ts +2 -0
- package/dist/mcp/prompts.js +109 -0
- package/dist/mcp/prompts.js.map +1 -0
- package/dist/mcp/resources.d.ts +2 -0
- package/dist/mcp/resources.js +132 -0
- package/dist/mcp/resources.js.map +1 -0
- package/dist/mcp/runtime.d.ts +15 -0
- package/dist/mcp/runtime.js +122 -0
- package/dist/mcp/runtime.js.map +1 -0
- package/dist/mcp/session-memory.d.ts +3 -0
- package/dist/mcp/session-memory.js +61 -0
- package/dist/mcp/session-memory.js.map +1 -0
- package/dist/mcp/tool-registry.d.ts +269 -0
- package/dist/mcp/tool-registry.js +284 -0
- package/dist/mcp/tool-registry.js.map +1 -0
- package/dist/mcp/tools.d.ts +53 -0
- package/dist/mcp/tools.js +372 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/mcp-repo-root.d.ts +16 -0
- package/dist/mcp-repo-root.js +322 -0
- package/dist/mcp-repo-root.js.map +1 -0
- package/dist/mcp-tool-catalog.d.ts +2 -0
- package/dist/mcp-tool-catalog.js +2 -0
- package/dist/mcp-tool-catalog.js.map +1 -0
- package/dist/mcp.d.ts +11 -0
- package/dist/mcp.js +332 -0
- package/dist/mcp.js.map +1 -0
- package/dist/outcome-ranking.d.ts +5 -0
- package/dist/outcome-ranking.js +115 -0
- package/dist/outcome-ranking.js.map +1 -0
- package/dist/parser/context.d.ts +28 -0
- package/dist/parser/context.js +2 -0
- package/dist/parser/context.js.map +1 -0
- package/dist/parser/ecma.d.ts +5 -0
- package/dist/parser/ecma.js +388 -0
- package/dist/parser/ecma.js.map +1 -0
- package/dist/parser/facts.d.ts +12 -0
- package/dist/parser/facts.js +137 -0
- package/dist/parser/facts.js.map +1 -0
- package/dist/parser/json.d.ts +3 -0
- package/dist/parser/json.js +318 -0
- package/dist/parser/json.js.map +1 -0
- package/dist/parser/markdown.d.ts +3 -0
- package/dist/parser/markdown.js +180 -0
- package/dist/parser/markdown.js.map +1 -0
- package/dist/parser/nodes.d.ts +5 -0
- package/dist/parser/nodes.js +75 -0
- package/dist/parser/nodes.js.map +1 -0
- package/dist/parser/python.d.ts +2 -0
- package/dist/parser/python.js +307 -0
- package/dist/parser/python.js.map +1 -0
- package/dist/parser/references.d.ts +3 -0
- package/dist/parser/references.js +204 -0
- package/dist/parser/references.js.map +1 -0
- package/dist/parser/risks.d.ts +4 -0
- package/dist/parser/risks.js +62 -0
- package/dist/parser/risks.js.map +1 -0
- package/dist/parser/routes.d.ts +5 -0
- package/dist/parser/routes.js +97 -0
- package/dist/parser/routes.js.map +1 -0
- package/dist/parser/shallow.d.ts +3 -0
- package/dist/parser/shallow.js +545 -0
- package/dist/parser/shallow.js.map +1 -0
- package/dist/parser/source.d.ts +4 -0
- package/dist/parser/source.js +127 -0
- package/dist/parser/source.js.map +1 -0
- package/dist/parser.d.ts +2 -0
- package/dist/parser.js +2 -0
- package/dist/parser.js.map +1 -0
- package/dist/placeholder-signals.d.ts +15 -0
- package/dist/placeholder-signals.js +511 -0
- package/dist/placeholder-signals.js.map +1 -0
- package/dist/post-edit-outcomes.d.ts +167 -0
- package/dist/post-edit-outcomes.js +484 -0
- package/dist/post-edit-outcomes.js.map +1 -0
- package/dist/queries.d.ts +12 -0
- package/dist/queries.js +13 -0
- package/dist/queries.js.map +1 -0
- package/dist/query/change-plan.d.ts +48 -0
- package/dist/query/change-plan.js +858 -0
- package/dist/query/change-plan.js.map +1 -0
- package/dist/query/compact-data.d.ts +25 -0
- package/dist/query/compact-data.js +74 -0
- package/dist/query/compact-data.js.map +1 -0
- package/dist/query/context.d.ts +5 -0
- package/dist/query/context.js +1162 -0
- package/dist/query/context.js.map +1 -0
- package/dist/query/diff.d.ts +5 -0
- package/dist/query/diff.js +111 -0
- package/dist/query/diff.js.map +1 -0
- package/dist/query/edge-evidence.d.ts +3 -0
- package/dist/query/edge-evidence.js +36 -0
- package/dist/query/edge-evidence.js.map +1 -0
- package/dist/query/formatting.d.ts +14 -0
- package/dist/query/formatting.js +67 -0
- package/dist/query/formatting.js.map +1 -0
- package/dist/query/graph-traversal.d.ts +22 -0
- package/dist/query/graph-traversal.js +218 -0
- package/dist/query/graph-traversal.js.map +1 -0
- package/dist/query/graph.d.ts +14 -0
- package/dist/query/graph.js +102 -0
- package/dist/query/graph.js.map +1 -0
- package/dist/query/impact.d.ts +28 -0
- package/dist/query/impact.js +568 -0
- package/dist/query/impact.js.map +1 -0
- package/dist/query/inspection.d.ts +9 -0
- package/dist/query/inspection.js +290 -0
- package/dist/query/inspection.js.map +1 -0
- package/dist/query/next-tools.d.ts +3 -0
- package/dist/query/next-tools.js +25 -0
- package/dist/query/next-tools.js.map +1 -0
- package/dist/query/placeholders.d.ts +24 -0
- package/dist/query/placeholders.js +121 -0
- package/dist/query/placeholders.js.map +1 -0
- package/dist/query/post-edit/decision.d.ts +49 -0
- package/dist/query/post-edit/decision.js +130 -0
- package/dist/query/post-edit/decision.js.map +1 -0
- package/dist/query/post-edit/dirty-scope.d.ts +16 -0
- package/dist/query/post-edit/dirty-scope.js +21 -0
- package/dist/query/post-edit/dirty-scope.js.map +1 -0
- package/dist/query/post-edit/next-actions.d.ts +22 -0
- package/dist/query/post-edit/next-actions.js +44 -0
- package/dist/query/post-edit/next-actions.js.map +1 -0
- package/dist/query/post-edit/snapshot-contract.d.ts +8 -0
- package/dist/query/post-edit/snapshot-contract.js +111 -0
- package/dist/query/post-edit/snapshot-contract.js.map +1 -0
- package/dist/query/post-edit.d.ts +5 -0
- package/dist/query/post-edit.js +1108 -0
- package/dist/query/post-edit.js.map +1 -0
- package/dist/query/quality.d.ts +43 -0
- package/dist/query/quality.js +134 -0
- package/dist/query/quality.js.map +1 -0
- package/dist/query/raw-search.d.ts +23 -0
- package/dist/query/raw-search.js +147 -0
- package/dist/query/raw-search.js.map +1 -0
- package/dist/query/runtime.d.ts +11 -0
- package/dist/query/runtime.js +79 -0
- package/dist/query/runtime.js.map +1 -0
- package/dist/query/search.d.ts +25 -0
- package/dist/query/search.js +429 -0
- package/dist/query/search.js.map +1 -0
- package/dist/query/session-memory.d.ts +3 -0
- package/dist/query/session-memory.js +108 -0
- package/dist/query/session-memory.js.map +1 -0
- package/dist/query/session.d.ts +41 -0
- package/dist/query/session.js +90 -0
- package/dist/query/session.js.map +1 -0
- package/dist/query/targets.d.ts +25 -0
- package/dist/query/targets.js +97 -0
- package/dist/query/targets.js.map +1 -0
- package/dist/query/test-commands.d.ts +10 -0
- package/dist/query/test-commands.js +110 -0
- package/dist/query/test-commands.js.map +1 -0
- package/dist/query/test-plan.d.ts +6 -0
- package/dist/query/test-plan.js +104 -0
- package/dist/query/test-plan.js.map +1 -0
- package/dist/query/tests.d.ts +48 -0
- package/dist/query/tests.js +444 -0
- package/dist/query/tests.js.map +1 -0
- package/dist/query/verification/shell.d.ts +20 -0
- package/dist/query/verification/shell.js +164 -0
- package/dist/query/verification/shell.js.map +1 -0
- package/dist/query/verification.d.ts +47 -0
- package/dist/query/verification.js +1123 -0
- package/dist/query/verification.js.map +1 -0
- package/dist/query/workflow.d.ts +17 -0
- package/dist/query/workflow.js +252 -0
- package/dist/query/workflow.js.map +1 -0
- package/dist/query/workspace-guidance.d.ts +26 -0
- package/dist/query/workspace-guidance.js +214 -0
- package/dist/query/workspace-guidance.js.map +1 -0
- package/dist/query/worktree-state.d.ts +22 -0
- package/dist/query/worktree-state.js +32 -0
- package/dist/query/worktree-state.js.map +1 -0
- package/dist/query/worktree.d.ts +16 -0
- package/dist/query/worktree.js +194 -0
- package/dist/query/worktree.js.map +1 -0
- package/dist/query-data.d.ts +4 -0
- package/dist/query-data.js +112 -0
- package/dist/query-data.js.map +1 -0
- package/dist/repo-files.d.ts +24 -0
- package/dist/repo-files.js +105 -0
- package/dist/repo-files.js.map +1 -0
- package/dist/resolver.d.ts +9 -0
- package/dist/resolver.js +555 -0
- package/dist/resolver.js.map +1 -0
- package/dist/retrieval.d.ts +46 -0
- package/dist/retrieval.js +783 -0
- package/dist/retrieval.js.map +1 -0
- package/dist/risk-ingest.d.ts +16 -0
- package/dist/risk-ingest.js +458 -0
- package/dist/risk-ingest.js.map +1 -0
- package/dist/rules.d.ts +10 -0
- package/dist/rules.js +107 -0
- package/dist/rules.js.map +1 -0
- package/dist/semantic/python.d.ts +9 -0
- package/dist/semantic/python.js +817 -0
- package/dist/semantic/python.js.map +1 -0
- package/dist/semantic/typescript.d.ts +10 -0
- package/dist/semantic/typescript.js +714 -0
- package/dist/semantic/typescript.js.map +1 -0
- package/dist/semantic-retrieval.d.ts +53 -0
- package/dist/semantic-retrieval.js +673 -0
- package/dist/semantic-retrieval.js.map +1 -0
- package/dist/session-memory/derivation.d.ts +6 -0
- package/dist/session-memory/derivation.js +400 -0
- package/dist/session-memory/derivation.js.map +1 -0
- package/dist/session-memory/event-log.d.ts +23 -0
- package/dist/session-memory/event-log.js +126 -0
- package/dist/session-memory/event-log.js.map +1 -0
- package/dist/session-memory/formatting.d.ts +7 -0
- package/dist/session-memory/formatting.js +86 -0
- package/dist/session-memory/formatting.js.map +1 -0
- package/dist/session-memory/model.d.ts +94 -0
- package/dist/session-memory/model.js +17 -0
- package/dist/session-memory/model.js.map +1 -0
- package/dist/session-memory/runtime.d.ts +24 -0
- package/dist/session-memory/runtime.js +289 -0
- package/dist/session-memory/runtime.js.map +1 -0
- package/dist/session-memory/store.d.ts +27 -0
- package/dist/session-memory/store.js +447 -0
- package/dist/session-memory/store.js.map +1 -0
- package/dist/session-memory.d.ts +1 -0
- package/dist/session-memory.js +2 -0
- package/dist/session-memory.js.map +1 -0
- package/dist/static-analysis.d.ts +36 -0
- package/dist/static-analysis.js +505 -0
- package/dist/static-analysis.js.map +1 -0
- package/dist/symbol-report-ingest.d.ts +8 -0
- package/dist/symbol-report-ingest.js +504 -0
- package/dist/symbol-report-ingest.js.map +1 -0
- package/dist/task-snapshots.d.ts +41 -0
- package/dist/task-snapshots.js +430 -0
- package/dist/task-snapshots.js.map +1 -0
- package/dist/types.d.ts +848 -0
- package/dist/types.js +12 -0
- package/dist/types.js.map +1 -0
- package/dist/util.d.ts +11 -0
- package/dist/util.js +63 -0
- package/dist/util.js.map +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +5 -0
- package/dist/version.js.map +1 -0
- package/package.json +81 -0
- package/plugins/codexa/.codex-plugin/plugin.json +38 -0
- package/plugins/codexa/.mcp.json +20 -0
- package/plugins/codexa/scripts/codexa-mcp.js +100 -0
- package/plugins/codexa/skills/codexa/SKILL.md +48 -0
|
@@ -0,0 +1,1123 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { CURRENT_VERIFICATION_PROVENANCE } from "../types.js";
|
|
4
|
+
import { uniqueSorted } from "../util.js";
|
|
5
|
+
import { wasTestRun } from "./tests.js";
|
|
6
|
+
import { hasNonRunningCommandArg, hasNonRunningJavaScriptTestArg, hasNonRunningPythonTestArg, hasPnpmWorkspaceFlag, isNonRunningCommand, segmentTruthiness, shellQuote, shellWrappedCommand, shellWords, splitShellSequence, stripLeadingEnvironment, stripPackageManagerFlags, stripQuotes, stripShellControlWords } from "./verification/shell.js";
|
|
7
|
+
export function verificationCoverageForCommands(index, ranCommands, repoRoot = index.snapshot.repoRoot) {
|
|
8
|
+
return verificationCoverageForCommandReports(index, ranCommands, [], repoRoot);
|
|
9
|
+
}
|
|
10
|
+
export function verificationCoverageForCommandReports(index, ranCommands, ranCommandReports = [], repoRoot = index.snapshot.repoRoot) {
|
|
11
|
+
return verificationEvidenceForCommandReports(index, ranCommands, ranCommandReports, repoRoot).coverage;
|
|
12
|
+
}
|
|
13
|
+
export function verificationEvidenceForCommandReports(index, ranCommands, ranCommandReports = [], repoRoot = index.snapshot.repoRoot) {
|
|
14
|
+
const scripts = packageScriptsFromIndex(index);
|
|
15
|
+
const packageRoots = packageRootsFromIndex(index);
|
|
16
|
+
const packageNamesByRoot = new Map();
|
|
17
|
+
const envelopeContext = { repoRoot, scripts, packageRoots, packageNamesByRoot };
|
|
18
|
+
const coverage = [];
|
|
19
|
+
const commandEnvelopes = [];
|
|
20
|
+
const preparedReports = normalizeCommandReports(ranCommands, ranCommandReports, repoRoot).map((report) => {
|
|
21
|
+
const initialCwd = report.cwd ? normalizeCwd(report.cwd, repoRoot) : ".";
|
|
22
|
+
const outputSummary = commandOutputSummary(report);
|
|
23
|
+
const commandEnvelope = commandEnvelopeForReport(report, initialCwd, envelopeContext);
|
|
24
|
+
return { report, initialCwd, outputSummary, commandEnvelope };
|
|
25
|
+
});
|
|
26
|
+
const structuredSemanticKeys = new Set(preparedReports
|
|
27
|
+
.map(({ report, commandEnvelope }) => structuredRawSuppressionKey(report, commandEnvelope, envelopeContext))
|
|
28
|
+
.filter((key) => Boolean(key)));
|
|
29
|
+
for (const { report, initialCwd, outputSummary, commandEnvelope } of preparedReports) {
|
|
30
|
+
const rawSemanticKey = report.fromReport ? undefined : commandEnvelopeSemanticKey(commandEnvelope);
|
|
31
|
+
if (rawSemanticKey && structuredSemanticKeys.has(rawSemanticKey)) {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
commandEnvelopes.push(commandEnvelope);
|
|
35
|
+
const reportDetails = uniqueSorted([
|
|
36
|
+
...(report.durationMs !== undefined ? [`duration ${report.durationMs}ms`] : []),
|
|
37
|
+
...(outputSummary ? [outputSummary] : [])
|
|
38
|
+
]);
|
|
39
|
+
const addCoverage = (input) => {
|
|
40
|
+
coverage.push({
|
|
41
|
+
kind: input.kind,
|
|
42
|
+
command: input.command,
|
|
43
|
+
source: input.source,
|
|
44
|
+
confidence: input.confidence ?? "derived",
|
|
45
|
+
scope: input.scope,
|
|
46
|
+
targetPath: input.targetPath,
|
|
47
|
+
details: uniqueSorted([...redactSecretDetails(input.details ?? []), ...reportDetails]),
|
|
48
|
+
exitCode: report.exitCode,
|
|
49
|
+
durationMs: report.durationMs,
|
|
50
|
+
outputSummary,
|
|
51
|
+
commandEnvelope
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
if (report.missingExitCode) {
|
|
55
|
+
addCoverage({
|
|
56
|
+
kind: "unknown",
|
|
57
|
+
command: report.command,
|
|
58
|
+
source: "command report missing exit code",
|
|
59
|
+
confidence: "derived",
|
|
60
|
+
scope: initialCwd,
|
|
61
|
+
details: reportDetails
|
|
62
|
+
});
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (report.missingCwd) {
|
|
66
|
+
addCoverage({
|
|
67
|
+
kind: "unknown",
|
|
68
|
+
command: report.command,
|
|
69
|
+
source: "command report missing cwd",
|
|
70
|
+
confidence: "derived",
|
|
71
|
+
scope: initialCwd,
|
|
72
|
+
details: reportDetails
|
|
73
|
+
});
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if (report.exitCode !== undefined && report.exitCode !== 0) {
|
|
77
|
+
addCoverage({
|
|
78
|
+
kind: "unknown",
|
|
79
|
+
command: report.command,
|
|
80
|
+
source: `command failed with exit code ${report.exitCode}`,
|
|
81
|
+
confidence: "derived",
|
|
82
|
+
scope: initialCwd,
|
|
83
|
+
details: reportDetails
|
|
84
|
+
});
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
if (analyzeCommandEnvelope(commandEnvelope, report.command, {
|
|
88
|
+
index,
|
|
89
|
+
repoRoot,
|
|
90
|
+
scripts,
|
|
91
|
+
packageRoots,
|
|
92
|
+
packageNamesByRoot,
|
|
93
|
+
visitedScripts: new Set(),
|
|
94
|
+
addCoverage
|
|
95
|
+
})) {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
analyzeCommand(report.command, initialCwd, [report.command], {
|
|
99
|
+
index,
|
|
100
|
+
repoRoot,
|
|
101
|
+
scripts,
|
|
102
|
+
packageRoots,
|
|
103
|
+
packageNamesByRoot,
|
|
104
|
+
visitedScripts: new Set(),
|
|
105
|
+
addCoverage
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
return { coverage: dedupeCoverage(coverage), commandEnvelopes: dedupeCommandEnvelopes(commandEnvelopes) };
|
|
109
|
+
}
|
|
110
|
+
function structuredRawSuppressionKey(report, envelope, ctx) {
|
|
111
|
+
if (!report.fromReport) {
|
|
112
|
+
return undefined;
|
|
113
|
+
}
|
|
114
|
+
if (envelope.source === "reported" && !reportedEnvelopeMatchesCommand(envelope, report.command, ctx)) {
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
|
+
return commandEnvelopeSemanticKey(envelope, { requireRepoScope: false });
|
|
118
|
+
}
|
|
119
|
+
function commandEnvelopeSemanticKey(envelope, options = { requireRepoScope: true }) {
|
|
120
|
+
if (!envelope.packageManager || !envelope.scriptName) {
|
|
121
|
+
return undefined;
|
|
122
|
+
}
|
|
123
|
+
if (options.requireRepoScope !== false && envelope.scopeStatus !== "repo") {
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
return [envelope.packageManager, envelope.packageRoot ?? "", envelope.workspace ?? "", envelope.scriptName, envelope.args.join("\u0001")].join("\0");
|
|
127
|
+
}
|
|
128
|
+
function redactSecretDetails(details) {
|
|
129
|
+
let redactNext = false;
|
|
130
|
+
return details.map((detail) => {
|
|
131
|
+
if (redactNext) {
|
|
132
|
+
redactNext = false;
|
|
133
|
+
return "<redacted>";
|
|
134
|
+
}
|
|
135
|
+
if (isSecretFlag(detail) && !detail.includes("=")) {
|
|
136
|
+
redactNext = true;
|
|
137
|
+
return detail;
|
|
138
|
+
}
|
|
139
|
+
return redactSecretArg(detail);
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
function redactSecretArg(value) {
|
|
143
|
+
if (/^Bearer\s+/iu.test(value)) {
|
|
144
|
+
return "Bearer <redacted>";
|
|
145
|
+
}
|
|
146
|
+
if (isSecretFlag(value) && value.includes("=")) {
|
|
147
|
+
return value.replace(/=.*/u, "=<redacted>");
|
|
148
|
+
}
|
|
149
|
+
if (/^(?:[A-Z_]*(?:TOKEN|SECRET|PASSWORD|PASSWD|PWD|API_?KEY|ACCESS_?KEY|AUTH|CREDENTIAL|COOKIE)[A-Z0-9_]*)=/iu.test(value)) {
|
|
150
|
+
return value.replace(/=.*/u, "=<redacted>");
|
|
151
|
+
}
|
|
152
|
+
return value;
|
|
153
|
+
}
|
|
154
|
+
function isSecretFlag(value) {
|
|
155
|
+
return /^--?[a-z0-9-]*(?:token|secret|password|passwd|pwd|api-?key|access-?key|auth|credential|cookie)[a-z0-9-]*(?:=.*)?$/iu.test(value);
|
|
156
|
+
}
|
|
157
|
+
export function coverageForDisplay(index, commands, repoRoot = index.snapshot.repoRoot) {
|
|
158
|
+
return verificationCoverageForCommands(index, commands, repoRoot);
|
|
159
|
+
}
|
|
160
|
+
function normalizeCommandReports(ranCommands, ranCommandReports, repoRoot) {
|
|
161
|
+
const rawReports = [];
|
|
162
|
+
const structuredReports = [];
|
|
163
|
+
const structuredCommandScopes = new Set();
|
|
164
|
+
const structuredCommands = new Set();
|
|
165
|
+
for (const command of ranCommands) {
|
|
166
|
+
const cleanCommand = command.trim();
|
|
167
|
+
if (!cleanCommand) {
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
rawReports.push({ command: cleanCommand, fromReport: false });
|
|
171
|
+
}
|
|
172
|
+
for (const report of ranCommandReports) {
|
|
173
|
+
const cleanCommand = report.command.trim();
|
|
174
|
+
if (!cleanCommand) {
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
structuredCommands.add(cleanCommand);
|
|
178
|
+
const cleanCwd = typeof report.cwd === "string" && report.cwd.trim().length > 0 ? report.cwd.trim() : undefined;
|
|
179
|
+
const compacted = compactCommandReport({ ...report, command: cleanCommand, cwd: cleanCwd });
|
|
180
|
+
const missingCwd = cleanCwd === undefined;
|
|
181
|
+
structuredReports.push({ ...compacted, fromReport: true, missingExitCode: compacted.exitCode === undefined, missingCwd });
|
|
182
|
+
if (!missingCwd) {
|
|
183
|
+
structuredCommandScopes.add(commandScopeKey(cleanCommand, compacted.cwd, repoRoot));
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return [...structuredReports, ...rawReports.filter((report) => !structuredCommands.has(report.command) && !structuredCommandScopes.has(commandScopeKey(report.command, report.cwd, repoRoot)))];
|
|
187
|
+
}
|
|
188
|
+
function commandScopeKey(command, cwd, repoRoot) {
|
|
189
|
+
return `${cwd ? normalizeCwd(cwd, repoRoot) : "."}\0${command}`;
|
|
190
|
+
}
|
|
191
|
+
function compactCommandReport(report) {
|
|
192
|
+
return {
|
|
193
|
+
command: report.command,
|
|
194
|
+
cwd: report.cwd,
|
|
195
|
+
packageManager: clampToken(report.packageManager),
|
|
196
|
+
workspace: clampToken(report.workspace),
|
|
197
|
+
packageRoot: clampToken(report.packageRoot),
|
|
198
|
+
packageName: clampToken(report.packageName),
|
|
199
|
+
scriptName: clampToken(report.scriptName),
|
|
200
|
+
args: report.args !== undefined ? compactArgs(report.args) ?? [] : undefined,
|
|
201
|
+
exitCode: report.exitCode,
|
|
202
|
+
durationMs: report.durationMs,
|
|
203
|
+
stdoutSummary: clampSummary(report.stdoutSummary),
|
|
204
|
+
stderrSummary: clampSummary(report.stderrSummary),
|
|
205
|
+
outputSummary: clampSummary(report.outputSummary)
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
function compactArgs(args) {
|
|
209
|
+
const clean = args?.map((arg) => clampToken(arg)).filter((arg) => Boolean(arg));
|
|
210
|
+
return clean && clean.length > 0 ? clean.slice(0, 40) : undefined;
|
|
211
|
+
}
|
|
212
|
+
function clampToken(value) {
|
|
213
|
+
const clean = value?.replace(/\s+/gu, " ").trim();
|
|
214
|
+
if (!clean) {
|
|
215
|
+
return undefined;
|
|
216
|
+
}
|
|
217
|
+
return clean.length > 160 ? clean.slice(0, 160) : clean;
|
|
218
|
+
}
|
|
219
|
+
function commandOutputSummary(report) {
|
|
220
|
+
const parts = [
|
|
221
|
+
report.outputSummary ? `output: ${report.outputSummary}` : undefined,
|
|
222
|
+
report.stdoutSummary ? `stdout: ${report.stdoutSummary}` : undefined,
|
|
223
|
+
report.stderrSummary ? `stderr: ${report.stderrSummary}` : undefined
|
|
224
|
+
].filter((part) => Boolean(part));
|
|
225
|
+
return parts.length > 0 ? clampSummary(parts.join(" | ")) : undefined;
|
|
226
|
+
}
|
|
227
|
+
function commandEnvelopeForReport(report, initialCwd, ctx) {
|
|
228
|
+
const derived = deriveCommandEnvelope(report.command, initialCwd, ctx);
|
|
229
|
+
const hasReportedEnvelope = report.packageManager !== undefined ||
|
|
230
|
+
report.workspace !== undefined ||
|
|
231
|
+
report.packageRoot !== undefined ||
|
|
232
|
+
report.packageName !== undefined ||
|
|
233
|
+
report.scriptName !== undefined ||
|
|
234
|
+
report.args !== undefined;
|
|
235
|
+
const packageRoot = normalizeEnvelopePackageRoot(report.packageRoot, ctx.repoRoot) ?? derived.packageRoot ?? packageRootForCwd(initialCwd, ctx.scripts, ctx.packageRoots);
|
|
236
|
+
const normalizedCwd = report.cwd ? normalizeCwd(report.cwd, ctx.repoRoot) : undefined;
|
|
237
|
+
const packageName = clampToken(report.packageName) ?? (isRepoPackageRoot(packageRoot) ? packageNameForRoot(packageRoot, ctx.repoRoot, ctx.packageNamesByRoot) : undefined);
|
|
238
|
+
return compactCommandEnvelope({
|
|
239
|
+
command: report.command,
|
|
240
|
+
cwd: report.cwd ?? (report.fromReport ? undefined : ctx.repoRoot),
|
|
241
|
+
packageManager: clampToken(report.packageManager) ?? derived.packageManager,
|
|
242
|
+
workspace: clampToken(report.workspace) ?? derived.workspace,
|
|
243
|
+
packageRoot,
|
|
244
|
+
packageName,
|
|
245
|
+
scriptName: clampToken(report.scriptName) ?? derived.scriptName,
|
|
246
|
+
args: report.args !== undefined ? compactArgs(report.args) ?? [] : derived.args ?? [],
|
|
247
|
+
exitCode: report.exitCode,
|
|
248
|
+
durationMs: report.durationMs,
|
|
249
|
+
stdoutSummary: report.stdoutSummary,
|
|
250
|
+
stderrSummary: report.stderrSummary,
|
|
251
|
+
outputSummary: commandOutputSummary(report),
|
|
252
|
+
source: hasReportedEnvelope ? "reported" : report.fromReport ? "derived-from-report" : "derived-from-raw-command",
|
|
253
|
+
scopeStatus: commandScopeStatus(report, normalizedCwd, packageRoot),
|
|
254
|
+
classifierVersion: CURRENT_VERIFICATION_PROVENANCE.commandCoverageClassifierVersion
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
function compactCommandEnvelope(envelope) {
|
|
258
|
+
return {
|
|
259
|
+
command: envelope.command,
|
|
260
|
+
cwd: envelope.cwd,
|
|
261
|
+
packageManager: clampToken(envelope.packageManager),
|
|
262
|
+
workspace: clampToken(envelope.workspace),
|
|
263
|
+
packageRoot: clampToken(envelope.packageRoot),
|
|
264
|
+
packageName: clampToken(envelope.packageName),
|
|
265
|
+
scriptName: clampToken(envelope.scriptName),
|
|
266
|
+
args: envelope.args.map((arg) => clampToken(arg)).filter((arg) => Boolean(arg)).slice(0, 40),
|
|
267
|
+
exitCode: envelope.exitCode,
|
|
268
|
+
durationMs: envelope.durationMs,
|
|
269
|
+
stdoutSummary: clampSummary(envelope.stdoutSummary),
|
|
270
|
+
stderrSummary: clampSummary(envelope.stderrSummary),
|
|
271
|
+
outputSummary: clampSummary(envelope.outputSummary),
|
|
272
|
+
source: envelope.source,
|
|
273
|
+
scopeStatus: envelope.scopeStatus,
|
|
274
|
+
classifierVersion: clampToken(envelope.classifierVersion) ?? CURRENT_VERIFICATION_PROVENANCE.commandCoverageClassifierVersion
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
function commandScopeStatus(report, normalizedCwd, packageRoot) {
|
|
278
|
+
if (report.missingCwd) {
|
|
279
|
+
return "missing-cwd";
|
|
280
|
+
}
|
|
281
|
+
if (packageRoot?.startsWith("__outside_repo__:unresolved-package:")) {
|
|
282
|
+
return "unresolved-package";
|
|
283
|
+
}
|
|
284
|
+
if (normalizedCwd?.startsWith("__outside_repo__:") || packageRoot?.startsWith("__outside_repo__:")) {
|
|
285
|
+
return "outside-repo";
|
|
286
|
+
}
|
|
287
|
+
return packageRoot ? "repo" : "unknown";
|
|
288
|
+
}
|
|
289
|
+
function normalizeEnvelopePackageRoot(value, repoRoot) {
|
|
290
|
+
if (!value) {
|
|
291
|
+
return undefined;
|
|
292
|
+
}
|
|
293
|
+
return normalizeCwd(value, repoRoot);
|
|
294
|
+
}
|
|
295
|
+
function isRepoPackageRoot(value) {
|
|
296
|
+
return Boolean(value && !value.startsWith("__outside_repo__:"));
|
|
297
|
+
}
|
|
298
|
+
function deriveCommandEnvelope(command, initialCwd, ctx) {
|
|
299
|
+
for (const segment of splitSimpleCommand(command, initialCwd, ctx.repoRoot)) {
|
|
300
|
+
const derived = deriveSegmentEnvelope(segment.text, segment.cwd, ctx);
|
|
301
|
+
if (derived.packageManager || derived.scriptName || (derived.args?.length ?? 0) > 0 || derived.workspace) {
|
|
302
|
+
return derived;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
const packageRoot = packageRootForCwd(initialCwd, ctx.scripts, ctx.packageRoots);
|
|
306
|
+
return {
|
|
307
|
+
packageRoot,
|
|
308
|
+
packageName: isRepoPackageRoot(packageRoot) ? packageNameForRoot(packageRoot, ctx.repoRoot, ctx.packageNamesByRoot) : undefined,
|
|
309
|
+
args: []
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
function deriveSegmentEnvelope(segment, cwd, ctx) {
|
|
313
|
+
const words = stripShellControlWords(stripLeadingEnvironment(shellWords(segment)));
|
|
314
|
+
if (words.length === 0 || isNonRunningCommand(words)) {
|
|
315
|
+
return { args: [] };
|
|
316
|
+
}
|
|
317
|
+
const shellWrapped = shellWrappedCommand(words);
|
|
318
|
+
if (shellWrapped) {
|
|
319
|
+
return deriveCommandEnvelope(shellWrapped, cwd, ctx);
|
|
320
|
+
}
|
|
321
|
+
const workspace = workspaceSpecifierFromWords(words);
|
|
322
|
+
const scoped = scopedPackageCommand(words, ctx.repoRoot, ctx.packageRoots, ctx.packageNamesByRoot) ?? scopedPackageCommand(stripPackageManagerFlags(words), ctx.repoRoot, ctx.packageRoots, ctx.packageNamesByRoot);
|
|
323
|
+
if (scoped) {
|
|
324
|
+
const derived = deriveSegmentEnvelope(scoped.words.join(" "), scoped.cwd, ctx);
|
|
325
|
+
return { ...derived, workspace: derived.workspace ?? workspace };
|
|
326
|
+
}
|
|
327
|
+
const effectiveWords = stripPackageManagerFlags(words);
|
|
328
|
+
const first = effectiveWords[0];
|
|
329
|
+
const packageRoot = packageRootForCwd(cwd, ctx.scripts, ctx.packageRoots);
|
|
330
|
+
const packageName = isRepoPackageRoot(packageRoot) ? packageNameForRoot(packageRoot, ctx.repoRoot, ctx.packageNamesByRoot) : undefined;
|
|
331
|
+
const base = { packageRoot, packageName, workspace, args: [] };
|
|
332
|
+
if ((first === "npm" || first === "pnpm") && effectiveWords[1] === "run" && effectiveWords[2]) {
|
|
333
|
+
return { ...base, packageManager: first, scriptName: effectiveWords[2], args: effectiveWords.slice(3) };
|
|
334
|
+
}
|
|
335
|
+
if ((first === "npm" || first === "pnpm") && (effectiveWords[1] === "test" || effectiveWords[1] === "t")) {
|
|
336
|
+
return { ...base, packageManager: first, scriptName: "test", args: effectiveWords.slice(2) };
|
|
337
|
+
}
|
|
338
|
+
if (first === "yarn" && effectiveWords[1]) {
|
|
339
|
+
return { ...base, packageManager: "yarn", scriptName: effectiveWords[1] === "run" ? effectiveWords[2] : effectiveWords[1], args: effectiveWords.slice(effectiveWords[1] === "run" ? 3 : 2) };
|
|
340
|
+
}
|
|
341
|
+
if (first === "vitest" || first === "jest") {
|
|
342
|
+
return { ...base, packageManager: first, scriptName: first, args: effectiveWords.slice(1) };
|
|
343
|
+
}
|
|
344
|
+
if (first === "npx" && (effectiveWords[1] === "vitest" || effectiveWords[1] === "jest" || effectiveWords[1] === "tsc")) {
|
|
345
|
+
return { ...base, packageManager: effectiveWords[1], scriptName: effectiveWords[1], args: effectiveWords.slice(2) };
|
|
346
|
+
}
|
|
347
|
+
if (first === "pytest") {
|
|
348
|
+
return { ...base, packageManager: "pytest", scriptName: "pytest", args: effectiveWords.slice(1) };
|
|
349
|
+
}
|
|
350
|
+
if ((first === "python" || first === "python3") && effectiveWords[1] === "-m" && effectiveWords[2] === "pytest") {
|
|
351
|
+
return { ...base, packageManager: first, scriptName: "pytest", args: effectiveWords.slice(3) };
|
|
352
|
+
}
|
|
353
|
+
if (first === "tsc") {
|
|
354
|
+
return { ...base, packageManager: "tsc", scriptName: "tsc", args: effectiveWords.slice(1) };
|
|
355
|
+
}
|
|
356
|
+
if (first === "npm" && effectiveWords[1] === "audit") {
|
|
357
|
+
return { ...base, packageManager: "npm", scriptName: "audit", args: effectiveWords.slice(2) };
|
|
358
|
+
}
|
|
359
|
+
return { ...base, packageManager: first, args: effectiveWords.slice(1) };
|
|
360
|
+
}
|
|
361
|
+
function workspaceSpecifierFromWords(words) {
|
|
362
|
+
const first = words[0];
|
|
363
|
+
const npmWorkspace = first === "npm" ? readFlagArgument(words, 1, ["-w", "--workspace"]) : undefined;
|
|
364
|
+
if (npmWorkspace) {
|
|
365
|
+
return npmWorkspace.value;
|
|
366
|
+
}
|
|
367
|
+
const pnpmFilter = first === "pnpm" ? readFlagArgument(words, 1, ["--filter", "-F"]) : undefined;
|
|
368
|
+
if (pnpmFilter) {
|
|
369
|
+
return pnpmFilter.value;
|
|
370
|
+
}
|
|
371
|
+
if (first === "yarn" && words[1] === "workspace" && words[2]) {
|
|
372
|
+
return words[2];
|
|
373
|
+
}
|
|
374
|
+
return undefined;
|
|
375
|
+
}
|
|
376
|
+
function analyzeCommandEnvelope(envelope, commandText, ctx) {
|
|
377
|
+
if (envelope.source !== "reported" || envelope.scopeStatus !== "repo" || !envelope.packageManager) {
|
|
378
|
+
return false;
|
|
379
|
+
}
|
|
380
|
+
if (!reportedEnvelopeMatchesCommand(envelope, commandText, ctx)) {
|
|
381
|
+
ctx.addCoverage({
|
|
382
|
+
kind: "unknown",
|
|
383
|
+
command: commandText,
|
|
384
|
+
source: "reported command envelope does not match command text",
|
|
385
|
+
confidence: "derived",
|
|
386
|
+
scope: envelope.packageRoot,
|
|
387
|
+
details: [
|
|
388
|
+
envelope.packageManager ? `reported package manager ${envelope.packageManager}` : undefined,
|
|
389
|
+
envelope.scriptName ? `reported script ${envelope.scriptName}` : undefined,
|
|
390
|
+
envelope.packageRoot ? `reported package root ${envelope.packageRoot}` : undefined
|
|
391
|
+
].filter((detail) => Boolean(detail))
|
|
392
|
+
});
|
|
393
|
+
return false;
|
|
394
|
+
}
|
|
395
|
+
const cwd = envelope.packageRoot ?? (envelope.cwd ? normalizeCwd(envelope.cwd, ctx.repoRoot) : ".");
|
|
396
|
+
const manager = envelope.packageManager;
|
|
397
|
+
const scriptName = envelope.scriptName;
|
|
398
|
+
const args = envelope.args;
|
|
399
|
+
if ((manager === "npm" || manager === "pnpm" || manager === "yarn") && scriptName) {
|
|
400
|
+
expandPackageScript(scriptName, args, cwd, commandText, ctx);
|
|
401
|
+
return true;
|
|
402
|
+
}
|
|
403
|
+
if (manager === "vitest" || manager === "jest") {
|
|
404
|
+
addJavaScriptTestCoverage(args, cwd, commandText, `reported command envelope ${manager}`, ctx);
|
|
405
|
+
return true;
|
|
406
|
+
}
|
|
407
|
+
if (manager === "pytest" || scriptName === "pytest") {
|
|
408
|
+
addPythonTestCoverage(args, cwd, commandText, "reported command envelope pytest", ctx);
|
|
409
|
+
return true;
|
|
410
|
+
}
|
|
411
|
+
if (manager === "tsc" || scriptName === "tsc") {
|
|
412
|
+
ctx.addCoverage({ kind: "typescript-syntax", command: commandText, source: "reported command envelope tsc", scope: cwd, details: args });
|
|
413
|
+
return true;
|
|
414
|
+
}
|
|
415
|
+
return false;
|
|
416
|
+
}
|
|
417
|
+
function reportedEnvelopeMatchesCommand(envelope, commandText, ctx) {
|
|
418
|
+
const initialCwd = envelope.cwd ? normalizeCwd(envelope.cwd, ctx.repoRoot) : envelope.packageRoot ?? ".";
|
|
419
|
+
const derived = deriveCommandEnvelope(commandText, initialCwd, ctx);
|
|
420
|
+
if (envelope.packageManager && derived.packageManager !== envelope.packageManager) {
|
|
421
|
+
return false;
|
|
422
|
+
}
|
|
423
|
+
if (envelope.scriptName && derived.scriptName !== envelope.scriptName) {
|
|
424
|
+
return false;
|
|
425
|
+
}
|
|
426
|
+
if (envelope.packageRoot && derived.packageRoot !== envelope.packageRoot) {
|
|
427
|
+
return false;
|
|
428
|
+
}
|
|
429
|
+
if (envelope.workspace && derived.workspace !== envelope.workspace) {
|
|
430
|
+
return false;
|
|
431
|
+
}
|
|
432
|
+
if (!arrayEquals(envelope.args, derived.args ?? [])) {
|
|
433
|
+
return false;
|
|
434
|
+
}
|
|
435
|
+
return true;
|
|
436
|
+
}
|
|
437
|
+
function arrayEquals(a, b) {
|
|
438
|
+
return a.length === b.length && a.every((value, index) => value === b[index]);
|
|
439
|
+
}
|
|
440
|
+
function clampSummary(value) {
|
|
441
|
+
const clean = value?.replace(/\s+/gu, " ").trim();
|
|
442
|
+
if (!clean) {
|
|
443
|
+
return undefined;
|
|
444
|
+
}
|
|
445
|
+
return clean.length > 500 ? `${clean.slice(0, 497)}...` : clean;
|
|
446
|
+
}
|
|
447
|
+
export function packageVerificationCommands(index, repoRoot = index.snapshot.repoRoot) {
|
|
448
|
+
const scripts = [...packageScriptsFromIndex(index).values()];
|
|
449
|
+
const byRoot = new Map();
|
|
450
|
+
for (const script of scripts) {
|
|
451
|
+
const set = byRoot.get(script.packageRoot) ?? new Set();
|
|
452
|
+
set.add(script.scriptName);
|
|
453
|
+
byRoot.set(script.packageRoot, set);
|
|
454
|
+
}
|
|
455
|
+
const commands = [];
|
|
456
|
+
for (const [packageRoot, names] of [...byRoot.entries()].sort((a, b) => a[0].localeCompare(b[0]))) {
|
|
457
|
+
for (const name of ["check", "test", "typecheck", "build", "lint", "audit", "privacy"]) {
|
|
458
|
+
if (!names.has(name)) {
|
|
459
|
+
continue;
|
|
460
|
+
}
|
|
461
|
+
const prefix = packageRoot === "." ? "" : `cd ${shellQuote(path.join(repoRoot, packageRoot))} && `;
|
|
462
|
+
commands.push(`${prefix}${packageManagerRunCommand(repoRoot, packageRoot, name)}`);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
return uniqueSorted(commands);
|
|
466
|
+
}
|
|
467
|
+
export function verificationCommandsForContext(index, repoRoot, seedPaths, tests, limit = 16) {
|
|
468
|
+
const aggregateCommands = packageVerificationCommands(index, repoRoot);
|
|
469
|
+
const targetedCommandScores = new Map();
|
|
470
|
+
tests.forEach((test, index) => {
|
|
471
|
+
if (!test.command) {
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
const current = targetedCommandScores.get(test.command) ?? 0;
|
|
475
|
+
targetedCommandScores.set(test.command, Math.max(current, targetedCommandPriority(test, index)));
|
|
476
|
+
});
|
|
477
|
+
const targetedCommands = [...targetedCommandScores.keys()];
|
|
478
|
+
const packageRoots = packageRootsFromIndex(index);
|
|
479
|
+
const seedRoots = new Set(seedPaths.map((seedPath) => packageRootForPath(seedPath, packageRoots)));
|
|
480
|
+
const seedPathSet = new Set(seedPaths.map(normalizePathLike));
|
|
481
|
+
return uniqueSorted([...targetedCommands, ...aggregateCommands])
|
|
482
|
+
.sort((a, b) => (targetedCommandScores.get(b) ?? 0) - (targetedCommandScores.get(a) ?? 0) ||
|
|
483
|
+
verificationCommandScore(b, repoRoot, seedRoots, seedPathSet) - verificationCommandScore(a, repoRoot, seedRoots, seedPathSet) ||
|
|
484
|
+
a.localeCompare(b))
|
|
485
|
+
.slice(0, limit);
|
|
486
|
+
}
|
|
487
|
+
function targetedCommandPriority(test, index) {
|
|
488
|
+
const tier = test.evidenceTier === "authoritative" ? 400 : test.evidenceTier === "derived" ? 300 : test.evidenceTier === "heuristic" ? 200 : test.evidenceTier === "fallback" ? 100 : 0;
|
|
489
|
+
return 1000 + tier + test.rank * 10 - index / 100;
|
|
490
|
+
}
|
|
491
|
+
function verificationCommandScore(command, repoRoot, seedRoots, seedPaths) {
|
|
492
|
+
const normalizedCommand = normalizePathLike(command.replaceAll(repoRoot, ""));
|
|
493
|
+
let score = 0;
|
|
494
|
+
for (const seedPath of seedPaths) {
|
|
495
|
+
if (normalizedCommand.includes(seedPath)) {
|
|
496
|
+
score += 80;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
for (const root of seedRoots) {
|
|
500
|
+
if (root === "." && !/^\s*cd\s/u.test(command)) {
|
|
501
|
+
score += 20;
|
|
502
|
+
}
|
|
503
|
+
else if (root !== "." && normalizedCommand.includes(normalizePathLike(root))) {
|
|
504
|
+
score += 60;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
if (/\b(?:test|vitest|pytest|jest)\b/u.test(command)) {
|
|
508
|
+
score += 12;
|
|
509
|
+
}
|
|
510
|
+
if (/\bcheck\b/u.test(command)) {
|
|
511
|
+
score += 8;
|
|
512
|
+
}
|
|
513
|
+
return score;
|
|
514
|
+
}
|
|
515
|
+
export function verificationLedgerForPostEdit(input) {
|
|
516
|
+
const repoRoot = input.repoRoot ?? input.index.snapshot.repoRoot;
|
|
517
|
+
const evidence = verificationEvidenceForCommandReports(input.index, input.ranCommands, input.ranCommandReports ?? [], repoRoot);
|
|
518
|
+
const coverage = evidence.coverage;
|
|
519
|
+
const waiverSet = waiversForMatching(input.waivers ?? [], input.waivedChecks ?? []);
|
|
520
|
+
const ledger = [];
|
|
521
|
+
const testsNotRun = [];
|
|
522
|
+
for (const test of input.tests) {
|
|
523
|
+
const match = testVerificationEvidence(test, input.ranTests, coverage, packageRootsFromIndex(input.index), new Set(input.index.files.map((file) => file.path)));
|
|
524
|
+
const waiver = waiverSet.get(waiverKey("test", test.path)) ?? (test.command ? waiverSet.get(waiverKey("test", test.command)) : undefined);
|
|
525
|
+
const status = match.covered ? "covered" : waiver ? "waived" : "missing";
|
|
526
|
+
if (status === "missing") {
|
|
527
|
+
testsNotRun.push(test);
|
|
528
|
+
}
|
|
529
|
+
ledger.push({
|
|
530
|
+
kind: "test",
|
|
531
|
+
recommended: test.path,
|
|
532
|
+
target: test.path,
|
|
533
|
+
status,
|
|
534
|
+
evidence: status === "waived" ? [`waived: ${waiver?.reason ?? "explicit waiver"}`] : match.evidence,
|
|
535
|
+
missingReason: status === "missing" ? "no reported test path, matching command, aggregate runner, or waiver covered this recommendation" : undefined,
|
|
536
|
+
waiverReason: status === "waived" ? waiver?.reason : undefined,
|
|
537
|
+
coverageKinds: uniqueSorted(match.coverage.map((entry) => entry.kind)),
|
|
538
|
+
command: test.command,
|
|
539
|
+
source: test.commandSource
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
for (const check of input.workflowChecks ?? []) {
|
|
543
|
+
ledger.push(checkLedgerEntry("workflow", check, waiverSet));
|
|
544
|
+
}
|
|
545
|
+
for (const check of input.dependencyChecks ?? []) {
|
|
546
|
+
ledger.push(checkLedgerEntry("dependency", check, waiverSet));
|
|
547
|
+
}
|
|
548
|
+
return { coverage, commandEnvelopes: evidence.commandEnvelopes, ledger, testsNotRun };
|
|
549
|
+
}
|
|
550
|
+
export function formatVerificationCoverage(coverage) {
|
|
551
|
+
return formatVerificationCommandPlan(verificationCommandPlan(coverage));
|
|
552
|
+
}
|
|
553
|
+
export function verificationCommandPlan(coverage) {
|
|
554
|
+
const byCommand = new Map();
|
|
555
|
+
for (const entry of coverage) {
|
|
556
|
+
const command = topLevelCommand(entry.command);
|
|
557
|
+
const plan = byCommand.get(command) ??
|
|
558
|
+
{
|
|
559
|
+
command,
|
|
560
|
+
covers: [],
|
|
561
|
+
targetPaths: [],
|
|
562
|
+
scopes: [],
|
|
563
|
+
sources: [],
|
|
564
|
+
confidence: entry.confidence
|
|
565
|
+
};
|
|
566
|
+
plan.covers = uniqueSorted([...plan.covers, entry.kind]);
|
|
567
|
+
plan.targetPaths = uniqueSorted([...plan.targetPaths, ...(entry.targetPath ? [entry.targetPath] : [])]);
|
|
568
|
+
plan.scopes = uniqueSorted([...plan.scopes, ...(entry.scope ? [entry.scope] : [])]);
|
|
569
|
+
plan.sources = uniqueSorted([...plan.sources, entry.source]);
|
|
570
|
+
plan.confidence = mergeConfidence(plan.confidence, entry.confidence);
|
|
571
|
+
byCommand.set(command, plan);
|
|
572
|
+
}
|
|
573
|
+
return [...byCommand.values()].sort((a, b) => commandPlanScore(b) - commandPlanScore(a) || a.command.localeCompare(b.command));
|
|
574
|
+
}
|
|
575
|
+
export function formatVerificationCommandPlan(plan) {
|
|
576
|
+
if (plan.length === 0) {
|
|
577
|
+
return ["- none inferred from reported commands"];
|
|
578
|
+
}
|
|
579
|
+
return plan.slice(0, 16).map((entry) => {
|
|
580
|
+
const targets = entry.targetPaths.length > 0 ? `; targets ${entry.targetPaths.slice(0, 4).join(", ")}` : "";
|
|
581
|
+
const scopes = entry.scopes.length > 0 ? `; scopes ${entry.scopes.slice(0, 4).join(", ")}` : "";
|
|
582
|
+
return `- ${entry.command}: covers ${entry.covers.join(", ")}${targets}${scopes}; ${entry.confidence}; ${entry.sources.slice(0, 3).join(", ")}`;
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
export function formatVerificationLedger(ledger) {
|
|
586
|
+
if (ledger.length === 0) {
|
|
587
|
+
return ["- none"];
|
|
588
|
+
}
|
|
589
|
+
return ledger.slice(0, 30).map((entry) => {
|
|
590
|
+
const evidence = entry.evidence.length > 0 ? `; evidence: ${entry.evidence.slice(0, 3).join(" | ")}` : "";
|
|
591
|
+
const missing = entry.missingReason ? `; missing: ${entry.missingReason}` : "";
|
|
592
|
+
return `- ${entry.status}: ${entry.kind} ${entry.target}; ${entry.recommended}${evidence}${missing}`;
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
function checkLedgerEntry(kind, check, waivers) {
|
|
596
|
+
const waiver = check.status === "missing" ? waivers.get(waiverKey(kind, check.target)) : undefined;
|
|
597
|
+
const status = waiver ? "waived" : check.status;
|
|
598
|
+
return {
|
|
599
|
+
kind,
|
|
600
|
+
recommended: check.reason,
|
|
601
|
+
target: check.target,
|
|
602
|
+
status,
|
|
603
|
+
evidence: status === "covered"
|
|
604
|
+
? [`${kind} evidence present`]
|
|
605
|
+
: status === "not_applicable"
|
|
606
|
+
? ["not applicable to edited/reviewed paths"]
|
|
607
|
+
: status === "waived"
|
|
608
|
+
? [`waived: ${waiver?.reason ?? "explicit waiver"}`]
|
|
609
|
+
: [],
|
|
610
|
+
missingReason: status === "missing" ? `${kind} check has no non-edited evidence in this review packet` : undefined,
|
|
611
|
+
waiverReason: status === "waived" ? waiver?.reason : undefined,
|
|
612
|
+
notApplicableReason: status === "not_applicable" ? "required check paths did not intersect edited or reviewed paths" : undefined,
|
|
613
|
+
coverageKinds: [],
|
|
614
|
+
source: check.source
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
function testVerificationEvidence(test, ranTests, coverage, packageRoots, indexedPaths) {
|
|
618
|
+
const directEvidence = wasTestRun(test, ranTests) ? [`reported ranTests matched ${test.path}`] : [];
|
|
619
|
+
const commandCoverage = coverage.filter((entry) => coverageCoversTest(entry, test.path, packageRoots, indexedPaths));
|
|
620
|
+
const commandEvidence = commandCoverage.map((entry) => {
|
|
621
|
+
const target = entry.targetPath ? `target ${entry.targetPath}` : entry.scope ? `scope ${entry.scope}` : "repo scope";
|
|
622
|
+
return `${entry.command} covers ${entry.kind} ${target}`;
|
|
623
|
+
});
|
|
624
|
+
return {
|
|
625
|
+
covered: directEvidence.length > 0 || commandEvidence.length > 0,
|
|
626
|
+
evidence: [...directEvidence, ...commandEvidence],
|
|
627
|
+
coverage: commandCoverage
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
function coverageCoversTest(coverage, testPath, packageRoots, indexedPaths) {
|
|
631
|
+
const expected = testPath.endsWith(".py") ? "python-tests" : "javascript-tests";
|
|
632
|
+
if (coverage.kind !== expected) {
|
|
633
|
+
return false;
|
|
634
|
+
}
|
|
635
|
+
if (coverage.targetPath) {
|
|
636
|
+
return normalizePathLike(coverage.targetPath) === normalizePathLike(testPath);
|
|
637
|
+
}
|
|
638
|
+
if (!indexedPaths.has(testPath)) {
|
|
639
|
+
return false;
|
|
640
|
+
}
|
|
641
|
+
return scopeCoversPath(coverage.scope ?? ".", testPath, packageRoots);
|
|
642
|
+
}
|
|
643
|
+
function scopeCoversPath(scope, filePath, packageRoots) {
|
|
644
|
+
const normalizedScope = normalizePathLike(scope);
|
|
645
|
+
const normalizedPath = normalizePathLike(filePath);
|
|
646
|
+
if (normalizedScope === ".") {
|
|
647
|
+
return !packageRoots.some((root) => root !== "." && (normalizedPath === root || normalizedPath.startsWith(`${root}/`)));
|
|
648
|
+
}
|
|
649
|
+
return normalizedPath === normalizedScope || normalizedPath.startsWith(`${normalizedScope}/`);
|
|
650
|
+
}
|
|
651
|
+
function analyzeCommand(command, initialCwd, chain, ctx) {
|
|
652
|
+
let cwd = initialCwd;
|
|
653
|
+
let chainTruthiness = "unknown";
|
|
654
|
+
let skipUntilFi = false;
|
|
655
|
+
for (const segment of splitSimpleCommand(command, cwd, ctx.repoRoot)) {
|
|
656
|
+
cwd = segment.cwd;
|
|
657
|
+
const words = stripLeadingEnvironment(shellWords(segment.text));
|
|
658
|
+
if (skipUntilFi) {
|
|
659
|
+
if (words.includes("fi")) {
|
|
660
|
+
skipUntilFi = false;
|
|
661
|
+
}
|
|
662
|
+
continue;
|
|
663
|
+
}
|
|
664
|
+
const ifTruthiness = ifConditionTruthiness(words);
|
|
665
|
+
if (ifTruthiness) {
|
|
666
|
+
if (ifTruthiness === "false") {
|
|
667
|
+
skipUntilFi = true;
|
|
668
|
+
}
|
|
669
|
+
chainTruthiness = ifTruthiness;
|
|
670
|
+
continue;
|
|
671
|
+
}
|
|
672
|
+
if (segment.operator === "&&" && chainTruthiness === "false") {
|
|
673
|
+
continue;
|
|
674
|
+
}
|
|
675
|
+
if (segment.operator === "||" && chainTruthiness === "true") {
|
|
676
|
+
continue;
|
|
677
|
+
}
|
|
678
|
+
analyzeSegment(segment.text, cwd, chain, ctx);
|
|
679
|
+
chainTruthiness = segmentTruthiness(segment.text);
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
function ifConditionTruthiness(words) {
|
|
683
|
+
if (words[0] !== "if") {
|
|
684
|
+
return undefined;
|
|
685
|
+
}
|
|
686
|
+
return segmentTruthiness(words.slice(1).join(" "));
|
|
687
|
+
}
|
|
688
|
+
function analyzeSegment(segment, cwd, chain, ctx) {
|
|
689
|
+
const words = stripShellControlWords(stripLeadingEnvironment(shellWords(segment)));
|
|
690
|
+
if (words.length === 0 || isNonRunningCommand(words)) {
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
const commandText = [...chain, segment].join(" -> ");
|
|
694
|
+
const shellWrapped = shellWrappedCommand(words);
|
|
695
|
+
if (shellWrapped) {
|
|
696
|
+
analyzeCommand(shellWrapped, cwd, chain, ctx);
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
699
|
+
const scoped = scopedPackageCommand(words, ctx.repoRoot, ctx.packageRoots, ctx.packageNamesByRoot) ?? scopedPackageCommand(stripPackageManagerFlags(words), ctx.repoRoot, ctx.packageRoots, ctx.packageNamesByRoot);
|
|
700
|
+
if (scoped) {
|
|
701
|
+
analyzeSegment(scoped.words.join(" "), scoped.cwd, chain, ctx);
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
const effectiveWords = stripPackageManagerFlags(words);
|
|
705
|
+
const first = effectiveWords[0];
|
|
706
|
+
if ((first === "npm" || first === "pnpm") && effectiveWords[1] === "run" && effectiveWords[2]) {
|
|
707
|
+
expandPackageScript(effectiveWords[2], effectiveWords.slice(3), cwd, commandText, ctx);
|
|
708
|
+
return;
|
|
709
|
+
}
|
|
710
|
+
if (first === "pnpm" && hasPnpmWorkspaceFlag(effectiveWords)) {
|
|
711
|
+
ctx.addCoverage({ kind: "unknown", command: commandText, source: "unsupported pnpm workspace command", confidence: "heuristic", scope: cwd, details: chain });
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
714
|
+
if ((first === "npm" || first === "pnpm") && effectiveWords[1] === "exec" && (effectiveWords[2] === "vitest" || effectiveWords[2] === "jest")) {
|
|
715
|
+
addJavaScriptTestCoverage(effectiveWords.slice(3), cwd, commandText, `direct ${first} exec ${effectiveWords[2]} command`, ctx);
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
718
|
+
if ((first === "npm" || first === "pnpm") && (effectiveWords[1] === "test" || effectiveWords[1] === "t")) {
|
|
719
|
+
expandPackageScript("test", effectiveWords.slice(2), cwd, commandText, ctx);
|
|
720
|
+
return;
|
|
721
|
+
}
|
|
722
|
+
if (first === "yarn" && effectiveWords[1]) {
|
|
723
|
+
const scriptName = effectiveWords[1] === "run" ? effectiveWords[2] : effectiveWords[1];
|
|
724
|
+
if (scriptName) {
|
|
725
|
+
expandPackageScript(scriptName, effectiveWords.slice(effectiveWords[1] === "run" ? 3 : 2), cwd, commandText, ctx);
|
|
726
|
+
}
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
729
|
+
if (first === "vitest" || first === "jest" || (first === "npx" && (effectiveWords[1] === "vitest" || effectiveWords[1] === "jest"))) {
|
|
730
|
+
const runner = first === "npx" ? effectiveWords[1] : first;
|
|
731
|
+
addJavaScriptTestCoverage(effectiveWords.slice(first === "npx" ? 2 : 1), cwd, commandText, `direct ${runner} command`, ctx);
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
734
|
+
if (first === "node" && effectiveWords.includes("--test")) {
|
|
735
|
+
addJavaScriptTestCoverage(effectiveWords.slice(1), cwd, commandText, "direct node --test command", ctx);
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
738
|
+
if (first === "pytest" || (first === "uv" && effectiveWords[1] === "run" && effectiveWords[2] === "pytest")) {
|
|
739
|
+
addPythonTestCoverage(effectiveWords.slice(first === "uv" ? 3 : 1), cwd, commandText, "direct pytest command", ctx);
|
|
740
|
+
return;
|
|
741
|
+
}
|
|
742
|
+
if ((first === "python" || first === "python3") && effectiveWords[1] === "-m" && effectiveWords[2] === "pytest") {
|
|
743
|
+
addPythonTestCoverage(effectiveWords.slice(3), cwd, commandText, "direct python -m pytest command", ctx);
|
|
744
|
+
return;
|
|
745
|
+
}
|
|
746
|
+
if (first === "tsc" || (first === "npx" && effectiveWords[1] === "tsc")) {
|
|
747
|
+
ctx.addCoverage({ kind: "typescript-syntax", command: commandText, source: "direct tsc command", scope: cwd, details: chain });
|
|
748
|
+
return;
|
|
749
|
+
}
|
|
750
|
+
if (first === "npm" && effectiveWords[1] === "audit") {
|
|
751
|
+
ctx.addCoverage({ kind: "audit", command: commandText, source: "direct npm audit command", scope: cwd, details: chain });
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
function expandPackageScript(scriptName, args, cwd, commandText, ctx) {
|
|
755
|
+
const packageRoot = packageRootForCwd(cwd, ctx.scripts, ctx.packageRoots);
|
|
756
|
+
const key = `${packageRoot}\0${scriptName}`;
|
|
757
|
+
const script = ctx.scripts.get(key);
|
|
758
|
+
if (!script) {
|
|
759
|
+
ctx.addCoverage({ kind: "unknown", command: commandText, source: `${packageRoot}/package.json#scripts.${scriptName} missing`, confidence: "heuristic", scope: packageRoot });
|
|
760
|
+
return;
|
|
761
|
+
}
|
|
762
|
+
if (hasNonRunningCommandArg(args)) {
|
|
763
|
+
return;
|
|
764
|
+
}
|
|
765
|
+
if (ctx.visitedScripts.has(key)) {
|
|
766
|
+
ctx.addCoverage({ kind: "unknown", command: commandText, source: `${script.source} recursive`, confidence: "heuristic", scope: packageRoot });
|
|
767
|
+
return;
|
|
768
|
+
}
|
|
769
|
+
ctx.visitedScripts.add(key);
|
|
770
|
+
addScriptNameCoverage(script, commandText, ctx);
|
|
771
|
+
const forwardedArgs = forwardedScriptArgs(args);
|
|
772
|
+
const expanded = forwardedArgs.length > 0 ? `${script.command} ${forwardedArgs.map(shellQuote).join(" ")}` : script.command;
|
|
773
|
+
analyzeCommand(expanded, script.packageRoot, [...commandText.split(" -> "), script.source], ctx);
|
|
774
|
+
ctx.visitedScripts.delete(key);
|
|
775
|
+
}
|
|
776
|
+
function addScriptNameCoverage(script, commandText, ctx) {
|
|
777
|
+
const lowerName = script.scriptName.toLowerCase();
|
|
778
|
+
const lowerCommand = script.command.toLowerCase();
|
|
779
|
+
const hasNoEmit = /(^|\s)--noEmit(\s|$)/u.test(script.command);
|
|
780
|
+
if ((lowerName === "build" && !hasNoEmit) || /\b(vite\s+build|next\s+build)\b/u.test(lowerCommand) || (/\btsc\b/u.test(lowerCommand) && !hasNoEmit)) {
|
|
781
|
+
ctx.addCoverage({ kind: "build", command: commandText, source: script.source, scope: script.packageRoot, details: [script.command] });
|
|
782
|
+
}
|
|
783
|
+
if (lowerName.includes("type") || /\btsc\b/u.test(lowerCommand)) {
|
|
784
|
+
ctx.addCoverage({ kind: "typescript-syntax", command: commandText, source: script.source, scope: script.packageRoot, details: [script.command] });
|
|
785
|
+
}
|
|
786
|
+
if (lowerName.includes("lint") || /\b(eslint|biome|verify-source-hygiene)\b/u.test(lowerCommand)) {
|
|
787
|
+
ctx.addCoverage({ kind: "lint", command: commandText, source: script.source, scope: script.packageRoot, details: [script.command] });
|
|
788
|
+
}
|
|
789
|
+
if (lowerName.includes("privacy") || /\bverify-public-hygiene\b/u.test(lowerCommand)) {
|
|
790
|
+
ctx.addCoverage({ kind: "privacy", command: commandText, source: script.source, scope: script.packageRoot, details: [script.command] });
|
|
791
|
+
}
|
|
792
|
+
if (lowerName.includes("audit") || /\bnpm\s+audit\b/u.test(lowerCommand)) {
|
|
793
|
+
ctx.addCoverage({ kind: "audit", command: commandText, source: script.source, scope: script.packageRoot, details: [script.command] });
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
function addJavaScriptTestCoverage(args, cwd, commandText, source, ctx) {
|
|
797
|
+
if (hasNonRunningJavaScriptTestArg(args)) {
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
const targets = args.map((arg) => normalizeCandidateTarget(arg, cwd, ctx.repoRoot)).filter((arg) => Boolean(arg));
|
|
801
|
+
if (targets.length === 0) {
|
|
802
|
+
ctx.addCoverage({ kind: "javascript-tests", command: commandText, source, scope: cwd, details: args });
|
|
803
|
+
return;
|
|
804
|
+
}
|
|
805
|
+
for (const target of targets) {
|
|
806
|
+
ctx.addCoverage({ kind: "javascript-tests", command: commandText, source, scope: cwd, targetPath: target, details: args });
|
|
807
|
+
ctx.addCoverage({ kind: "targeted-test", command: commandText, source, scope: cwd, targetPath: target, details: args });
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
function addPythonTestCoverage(args, cwd, commandText, source, ctx) {
|
|
811
|
+
if (hasNonRunningPythonTestArg(args) || args.some((arg) => ["--collect-only", "--co", "--fixtures"].includes(arg))) {
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
814
|
+
const targets = args.map((arg) => normalizeCandidateTarget(arg, cwd, ctx.repoRoot)).filter((arg) => Boolean(arg));
|
|
815
|
+
const testTargets = targets.filter((target) => target.endsWith(".py") || target.startsWith("tests/"));
|
|
816
|
+
if (testTargets.length === 0) {
|
|
817
|
+
ctx.addCoverage({ kind: "python-tests", command: commandText, source, scope: cwd, details: args });
|
|
818
|
+
return;
|
|
819
|
+
}
|
|
820
|
+
for (const target of testTargets) {
|
|
821
|
+
ctx.addCoverage({ kind: "python-tests", command: commandText, source, scope: cwd, targetPath: target, details: args });
|
|
822
|
+
ctx.addCoverage({ kind: "targeted-test", command: commandText, source, scope: cwd, targetPath: target, details: args });
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
function splitSimpleCommand(command, initialCwd, repoRoot) {
|
|
826
|
+
const segments = splitShellSequence(command);
|
|
827
|
+
const result = [];
|
|
828
|
+
let cwd = initialCwd;
|
|
829
|
+
for (const segment of segments) {
|
|
830
|
+
const words = stripLeadingEnvironment(shellWords(segment.text));
|
|
831
|
+
if (words[0] === "cd" && words[1]) {
|
|
832
|
+
cwd = normalizeCwd(words[1], repoRoot);
|
|
833
|
+
continue;
|
|
834
|
+
}
|
|
835
|
+
result.push({ cwd, text: segment.text, operator: segment.operator });
|
|
836
|
+
}
|
|
837
|
+
return result;
|
|
838
|
+
}
|
|
839
|
+
function packageScriptsFromIndex(index) {
|
|
840
|
+
const scripts = new Map();
|
|
841
|
+
for (const usage of index.usageSites) {
|
|
842
|
+
if (usage.source !== "manifest" || !usage.path.endsWith("package.json") || !usage.name.startsWith("npm script ")) {
|
|
843
|
+
continue;
|
|
844
|
+
}
|
|
845
|
+
const scriptName = usage.name.replace(/^npm script\s+/u, "");
|
|
846
|
+
const packageRoot = normalizePackageRoot(path.posix.dirname(usage.path));
|
|
847
|
+
const source = `${packageRoot === "." ? "" : `${packageRoot}/`}package.json#scripts.${scriptName}`;
|
|
848
|
+
scripts.set(`${packageRoot}\0${scriptName}`, {
|
|
849
|
+
packageRoot,
|
|
850
|
+
scriptName,
|
|
851
|
+
command: usage.text,
|
|
852
|
+
source
|
|
853
|
+
});
|
|
854
|
+
}
|
|
855
|
+
return scripts;
|
|
856
|
+
}
|
|
857
|
+
function packageManagerRunCommand(repoRoot, packageRoot, scriptName) {
|
|
858
|
+
const absoluteRoot = path.join(repoRoot, packageRoot === "." ? "" : packageRoot);
|
|
859
|
+
if (existsSync(path.join(absoluteRoot, "pnpm-lock.yaml")) || (packageRoot !== "." && existsSync(path.join(repoRoot, "pnpm-lock.yaml")))) {
|
|
860
|
+
return scriptName === "test" ? "pnpm test" : `pnpm run ${scriptName}`;
|
|
861
|
+
}
|
|
862
|
+
if (existsSync(path.join(absoluteRoot, "yarn.lock")) || (packageRoot !== "." && existsSync(path.join(repoRoot, "yarn.lock")))) {
|
|
863
|
+
return scriptName === "test" ? "yarn test" : `yarn ${scriptName}`;
|
|
864
|
+
}
|
|
865
|
+
return scriptName === "test" ? "npm test" : `npm run ${scriptName}`;
|
|
866
|
+
}
|
|
867
|
+
function packageRootsFromIndex(index) {
|
|
868
|
+
return uniqueSorted(index.files.filter((file) => file.path.endsWith("package.json")).map((file) => normalizePackageRoot(path.posix.dirname(file.path))));
|
|
869
|
+
}
|
|
870
|
+
function packageRootForCwd(cwd, scripts, packageRoots) {
|
|
871
|
+
if (cwd.startsWith("__outside_repo__:")) {
|
|
872
|
+
return cwd;
|
|
873
|
+
}
|
|
874
|
+
const normalized = normalizePackageRoot(cwd);
|
|
875
|
+
if ([...scripts.keys()].some((key) => key.startsWith(`${normalized}\0`))) {
|
|
876
|
+
return normalized;
|
|
877
|
+
}
|
|
878
|
+
const candidates = uniqueSorted([...packageRoots, ...[...scripts.values()].map((script) => script.packageRoot)]).sort((a, b) => b.length - a.length);
|
|
879
|
+
return candidates.find((candidate) => candidate !== "." && (normalized === candidate || normalized.startsWith(`${candidate}/`))) ?? ".";
|
|
880
|
+
}
|
|
881
|
+
function packageRootForPath(filePath, packageRoots) {
|
|
882
|
+
const normalized = normalizePathLike(filePath);
|
|
883
|
+
return uniqueSorted(packageRoots)
|
|
884
|
+
.sort((a, b) => b.length - a.length)
|
|
885
|
+
.find((candidate) => candidate !== "." && (normalized === candidate || normalized.startsWith(`${candidate}/`))) ?? ".";
|
|
886
|
+
}
|
|
887
|
+
function scopedPackageCommand(words, repoRoot, packageRoots, packageNamesByRoot) {
|
|
888
|
+
const first = words[0];
|
|
889
|
+
if ((first === "npm" || first === "pnpm") && (words[1] === "--prefix" || words[1] === "-C" || words[1] === "--dir") && words[2]) {
|
|
890
|
+
return { cwd: normalizeCwd(words[2], repoRoot), words: [first, ...words.slice(3)] };
|
|
891
|
+
}
|
|
892
|
+
const prefixValue = flagValue(words[1], ["--prefix", "-C", "--dir"]);
|
|
893
|
+
if ((first === "npm" || first === "pnpm") && prefixValue) {
|
|
894
|
+
return { cwd: normalizeCwd(prefixValue, repoRoot), words: [first, ...words.slice(2)] };
|
|
895
|
+
}
|
|
896
|
+
const npmWorkspace = readFlagArgument(words, 1, ["-w", "--workspace"]);
|
|
897
|
+
if (first === "npm" && npmWorkspace) {
|
|
898
|
+
return {
|
|
899
|
+
cwd: resolvePackageSpecifier(npmWorkspace.value, repoRoot, packageRoots, packageNamesByRoot) ?? unresolvedPackageScope(npmWorkspace.value),
|
|
900
|
+
words: [first, ...words.slice(npmWorkspace.nextIndex)]
|
|
901
|
+
};
|
|
902
|
+
}
|
|
903
|
+
const pnpmFilter = readFlagArgument(words, 1, ["--filter", "-F"]);
|
|
904
|
+
if (first === "pnpm" && pnpmFilter) {
|
|
905
|
+
const cwd = resolvePackageSpecifier(pnpmFilter.value, repoRoot, packageRoots, packageNamesByRoot);
|
|
906
|
+
return cwd ? { cwd, words: [first, ...words.slice(pnpmFilter.nextIndex)] } : undefined;
|
|
907
|
+
}
|
|
908
|
+
if (first === "yarn" && words[1] === "--cwd" && words[2]) {
|
|
909
|
+
return { cwd: normalizeCwd(words[2], repoRoot), words: ["yarn", ...words.slice(3)] };
|
|
910
|
+
}
|
|
911
|
+
const yarnCwdValue = flagValue(words[1], ["--cwd"]);
|
|
912
|
+
if (first === "yarn" && yarnCwdValue) {
|
|
913
|
+
return { cwd: normalizeCwd(yarnCwdValue, repoRoot), words: ["yarn", ...words.slice(2)] };
|
|
914
|
+
}
|
|
915
|
+
if (first === "yarn" && words[1] === "workspace" && words[2]) {
|
|
916
|
+
return { cwd: resolvePackageSpecifier(words[2], repoRoot, packageRoots, packageNamesByRoot) ?? unresolvedPackageScope(words[2]), words: ["yarn", ...words.slice(3)] };
|
|
917
|
+
}
|
|
918
|
+
return undefined;
|
|
919
|
+
}
|
|
920
|
+
function readFlagArgument(words, start, flags) {
|
|
921
|
+
const word = words[start];
|
|
922
|
+
const inline = flagValue(word, flags);
|
|
923
|
+
if (inline) {
|
|
924
|
+
return { value: inline, nextIndex: start + 1 };
|
|
925
|
+
}
|
|
926
|
+
if (word && flags.includes(word) && words[start + 1]) {
|
|
927
|
+
return { value: words[start + 1], nextIndex: start + 2 };
|
|
928
|
+
}
|
|
929
|
+
return undefined;
|
|
930
|
+
}
|
|
931
|
+
function flagValue(word, flags) {
|
|
932
|
+
if (!word) {
|
|
933
|
+
return undefined;
|
|
934
|
+
}
|
|
935
|
+
for (const flag of flags) {
|
|
936
|
+
if (word.startsWith(`${flag}=`)) {
|
|
937
|
+
const value = word.slice(flag.length + 1);
|
|
938
|
+
return value || undefined;
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
return undefined;
|
|
942
|
+
}
|
|
943
|
+
function forwardedScriptArgs(args) {
|
|
944
|
+
const delimiter = args.indexOf("--");
|
|
945
|
+
return delimiter >= 0 ? args.slice(delimiter + 1) : args.filter((arg) => !arg.startsWith("-"));
|
|
946
|
+
}
|
|
947
|
+
function resolvePackageSpecifier(value, repoRoot, packageRoots, packageNamesByRoot) {
|
|
948
|
+
const clean = stripQuotes(value.trim());
|
|
949
|
+
if (!clean || clean.startsWith("!") || /[*{},]/u.test(clean) || clean.includes("...")) {
|
|
950
|
+
return undefined;
|
|
951
|
+
}
|
|
952
|
+
const pathCandidate = normalizeCwd(clean.replace(/^\.\//u, ""), repoRoot);
|
|
953
|
+
if (!pathCandidate.startsWith("__outside_repo__:") && existsSync(path.join(repoRoot, pathCandidate === "." ? "" : pathCandidate, "package.json"))) {
|
|
954
|
+
return pathCandidate;
|
|
955
|
+
}
|
|
956
|
+
for (const root of packageRoots) {
|
|
957
|
+
if (packageNameForRoot(root, repoRoot, packageNamesByRoot) === clean) {
|
|
958
|
+
return root;
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
return undefined;
|
|
962
|
+
}
|
|
963
|
+
function packageNameForRoot(root, repoRoot, packageNamesByRoot) {
|
|
964
|
+
if (packageNamesByRoot.has(root)) {
|
|
965
|
+
return packageNamesByRoot.get(root);
|
|
966
|
+
}
|
|
967
|
+
const manifestPath = path.join(repoRoot, root === "." ? "" : root, "package.json");
|
|
968
|
+
let packageName;
|
|
969
|
+
try {
|
|
970
|
+
const manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
|
|
971
|
+
if (typeof manifest.name === "string") {
|
|
972
|
+
packageName = manifest.name;
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
catch {
|
|
976
|
+
packageName = undefined;
|
|
977
|
+
}
|
|
978
|
+
packageNamesByRoot.set(root, packageName);
|
|
979
|
+
return packageName;
|
|
980
|
+
}
|
|
981
|
+
function unresolvedPackageScope(value) {
|
|
982
|
+
return `__outside_repo__:unresolved-package:${value}`;
|
|
983
|
+
}
|
|
984
|
+
function normalizeCandidateTarget(value, cwd, repoRoot) {
|
|
985
|
+
if (value.startsWith("-") || /^[A-Z_][A-Z0-9_]*=/u.test(value)) {
|
|
986
|
+
return undefined;
|
|
987
|
+
}
|
|
988
|
+
const clean = value.replace(/:\d+(?::\d+)?$/u, "").replace(/::.+$/u, "");
|
|
989
|
+
if (!/(\.(?:test|spec)\.[cm]?[jt]sx?|\.py)$|^tests\//u.test(clean)) {
|
|
990
|
+
return undefined;
|
|
991
|
+
}
|
|
992
|
+
const joined = path.isAbsolute(clean) ? relativeInsideRepo(clean, repoRoot) : path.posix.normalize(path.posix.join(normalizePackageRoot(cwd), clean));
|
|
993
|
+
if (!joined) {
|
|
994
|
+
return undefined;
|
|
995
|
+
}
|
|
996
|
+
return normalizePathLike(joined);
|
|
997
|
+
}
|
|
998
|
+
function normalizeCwd(value, repoRoot) {
|
|
999
|
+
const clean = stripQuotes(value.trim());
|
|
1000
|
+
if (clean.startsWith("__outside_repo__:")) {
|
|
1001
|
+
return clean;
|
|
1002
|
+
}
|
|
1003
|
+
if (path.isAbsolute(clean)) {
|
|
1004
|
+
const relative = path.relative(repoRoot, clean);
|
|
1005
|
+
if (relative === "") {
|
|
1006
|
+
return ".";
|
|
1007
|
+
}
|
|
1008
|
+
return !relative.startsWith("..") && !path.isAbsolute(relative) ? normalizePackageRoot(relative) : `__outside_repo__:${clean}`;
|
|
1009
|
+
}
|
|
1010
|
+
const absolute = path.resolve(repoRoot, clean || ".");
|
|
1011
|
+
const relative = path.relative(repoRoot, absolute);
|
|
1012
|
+
if (relative === "") {
|
|
1013
|
+
return ".";
|
|
1014
|
+
}
|
|
1015
|
+
return !relative.startsWith("..") && !path.isAbsolute(relative) ? normalizePackageRoot(relative) : `__outside_repo__:${absolute}`;
|
|
1016
|
+
}
|
|
1017
|
+
function relativeInsideRepo(value, repoRoot) {
|
|
1018
|
+
const relative = path.relative(repoRoot, value);
|
|
1019
|
+
if (relative === "") {
|
|
1020
|
+
return ".";
|
|
1021
|
+
}
|
|
1022
|
+
if (relative.startsWith("..") || path.isAbsolute(relative)) {
|
|
1023
|
+
return undefined;
|
|
1024
|
+
}
|
|
1025
|
+
return relative;
|
|
1026
|
+
}
|
|
1027
|
+
function normalizePackageRoot(value) {
|
|
1028
|
+
const normalized = normalizePathLike(value || ".");
|
|
1029
|
+
return normalized === "" ? "." : normalized;
|
|
1030
|
+
}
|
|
1031
|
+
function dedupeCoverage(coverage) {
|
|
1032
|
+
const byKey = new Map();
|
|
1033
|
+
for (const entry of coverage) {
|
|
1034
|
+
const key = [entry.kind, entry.command, entry.source, entry.scope ?? "", entry.targetPath ?? "", entry.exitCode ?? "", entry.durationMs ?? "", entry.outputSummary ?? ""].join("\0");
|
|
1035
|
+
const existing = byKey.get(key);
|
|
1036
|
+
if (existing) {
|
|
1037
|
+
existing.details = uniqueSorted([...existing.details, ...entry.details]);
|
|
1038
|
+
existing.confidence = mergeConfidence(existing.confidence, entry.confidence);
|
|
1039
|
+
existing.commandEnvelope = existing.commandEnvelope ?? entry.commandEnvelope;
|
|
1040
|
+
}
|
|
1041
|
+
else {
|
|
1042
|
+
byKey.set(key, { ...entry, details: uniqueSorted(entry.details) });
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
return [...byKey.values()].sort((a, b) => a.kind.localeCompare(b.kind) || (a.targetPath ?? "").localeCompare(b.targetPath ?? "") || a.command.localeCompare(b.command));
|
|
1046
|
+
}
|
|
1047
|
+
function dedupeCommandEnvelopes(envelopes) {
|
|
1048
|
+
const byKey = new Map();
|
|
1049
|
+
for (const envelope of envelopes) {
|
|
1050
|
+
const key = [
|
|
1051
|
+
envelope.command,
|
|
1052
|
+
envelope.cwd ?? "",
|
|
1053
|
+
envelope.packageManager ?? "",
|
|
1054
|
+
envelope.workspace ?? "",
|
|
1055
|
+
envelope.packageRoot ?? "",
|
|
1056
|
+
envelope.packageName ?? "",
|
|
1057
|
+
envelope.scriptName ?? "",
|
|
1058
|
+
envelope.args.join("\u0001"),
|
|
1059
|
+
envelope.exitCode ?? "",
|
|
1060
|
+
envelope.durationMs ?? "",
|
|
1061
|
+
envelope.stdoutSummary ?? "",
|
|
1062
|
+
envelope.stderrSummary ?? "",
|
|
1063
|
+
envelope.outputSummary ?? "",
|
|
1064
|
+
envelope.source,
|
|
1065
|
+
envelope.scopeStatus,
|
|
1066
|
+
envelope.classifierVersion
|
|
1067
|
+
].join("\0");
|
|
1068
|
+
byKey.set(key, envelope);
|
|
1069
|
+
}
|
|
1070
|
+
return [...byKey.values()].sort((a, b) => a.command.localeCompare(b.command) ||
|
|
1071
|
+
(a.cwd ?? "").localeCompare(b.cwd ?? "") ||
|
|
1072
|
+
(a.packageRoot ?? "").localeCompare(b.packageRoot ?? "") ||
|
|
1073
|
+
(a.scriptName ?? "").localeCompare(b.scriptName ?? ""));
|
|
1074
|
+
}
|
|
1075
|
+
function commandPlanScore(entry) {
|
|
1076
|
+
const covers = new Set(entry.covers);
|
|
1077
|
+
return ((covers.has("targeted-test") ? 35 : 0) +
|
|
1078
|
+
(covers.has("javascript-tests") || covers.has("python-tests") ? 40 : 0) +
|
|
1079
|
+
(covers.has("typescript-syntax") ? 12 : 0) +
|
|
1080
|
+
(covers.has("build") ? 10 : 0) +
|
|
1081
|
+
(covers.has("lint") ? 4 : 0) +
|
|
1082
|
+
(covers.has("privacy") ? 3 : 0) +
|
|
1083
|
+
(covers.has("audit") ? 2 : 0) +
|
|
1084
|
+
(entry.targetPaths.length > 0 ? 25 : 0));
|
|
1085
|
+
}
|
|
1086
|
+
function topLevelCommand(command) {
|
|
1087
|
+
return command.split(" -> ", 1)[0] ?? command;
|
|
1088
|
+
}
|
|
1089
|
+
function mergeConfidence(a, b) {
|
|
1090
|
+
if (a === "authoritative" || b === "authoritative") {
|
|
1091
|
+
return "authoritative";
|
|
1092
|
+
}
|
|
1093
|
+
if (a === "derived" || b === "derived") {
|
|
1094
|
+
return "derived";
|
|
1095
|
+
}
|
|
1096
|
+
return "heuristic";
|
|
1097
|
+
}
|
|
1098
|
+
function waiversForMatching(waivers, legacyWaivedChecks) {
|
|
1099
|
+
const result = new Map();
|
|
1100
|
+
for (const waiver of waivers) {
|
|
1101
|
+
if (!waiver.target || !waiver.reason) {
|
|
1102
|
+
continue;
|
|
1103
|
+
}
|
|
1104
|
+
result.set(waiverKey(waiver.kind, waiver.target), waiver);
|
|
1105
|
+
}
|
|
1106
|
+
for (const target of legacyWaivedChecks) {
|
|
1107
|
+
const reason = "legacy waivedChecks target";
|
|
1108
|
+
result.set(waiverKey("test", target), { kind: "test", target, reason });
|
|
1109
|
+
}
|
|
1110
|
+
return result;
|
|
1111
|
+
}
|
|
1112
|
+
function waiverKey(kind, target) {
|
|
1113
|
+
return `${kind}\0${normalizeSearchText(target)}`;
|
|
1114
|
+
}
|
|
1115
|
+
function normalizeSearchText(value) {
|
|
1116
|
+
return normalizePathLike(value).toLowerCase().replace(/[^a-z0-9./:_-]+/gu, " ").replace(/\s+/gu, " ").trim();
|
|
1117
|
+
}
|
|
1118
|
+
function normalizePathLike(value) {
|
|
1119
|
+
const normalized = value.replace(/\\/gu, "/").replace(/^\.\/+/u, "");
|
|
1120
|
+
const collapsed = path.posix.normalize(normalized);
|
|
1121
|
+
return collapsed === "." ? "." : collapsed.replace(/^\/+/u, "");
|
|
1122
|
+
}
|
|
1123
|
+
//# sourceMappingURL=verification.js.map
|