@oss-autopilot/core 3.4.1 → 3.6.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/dist/cli-registry.js +99 -0
- package/dist/cli.bundle.cjs +112 -105
- package/dist/commands/compliance-score.d.ts +21 -0
- package/dist/commands/compliance-score.js +156 -0
- package/dist/commands/daily.d.ts +8 -0
- package/dist/commands/daily.js +21 -0
- package/dist/commands/index.d.ts +6 -0
- package/dist/commands/index.js +6 -0
- package/dist/commands/list-mark-done.d.ts +48 -0
- package/dist/commands/list-mark-done.js +213 -0
- package/dist/commands/parse-list.js +86 -9
- package/dist/commands/repo-vet.d.ts +21 -0
- package/dist/commands/repo-vet.js +215 -0
- package/dist/commands/startup.js +41 -1
- package/dist/core/anti-llm-policy.d.ts +42 -13
- package/dist/core/anti-llm-policy.js +102 -13
- package/dist/core/ci-analysis.d.ts +32 -1
- package/dist/core/ci-analysis.js +92 -0
- package/dist/core/ci-enforced-tools.d.ts +35 -0
- package/dist/core/ci-enforced-tools.js +109 -0
- package/dist/core/comment-decision.d.ts +72 -0
- package/dist/core/comment-decision.js +74 -0
- package/dist/core/compliance-score.d.ts +127 -0
- package/dist/core/compliance-score.js +277 -0
- package/dist/core/config-registry.js +12 -0
- package/dist/core/contributing.d.ts +52 -0
- package/dist/core/contributing.js +139 -0
- package/dist/core/errors.d.ts +19 -0
- package/dist/core/errors.js +54 -0
- package/dist/core/extraction-categories.d.ts +55 -0
- package/dist/core/extraction-categories.js +108 -0
- package/dist/core/follow-up-history.d.ts +41 -0
- package/dist/core/follow-up-history.js +71 -0
- package/dist/core/gist-state-store.d.ts +30 -7
- package/dist/core/gist-state-store.js +87 -11
- package/dist/core/issue-conversation.js +1 -0
- package/dist/core/issue-effort.d.ts +29 -0
- package/dist/core/issue-effort.js +41 -0
- package/dist/core/maintainer-hints.d.ts +23 -0
- package/dist/core/maintainer-hints.js +36 -0
- package/dist/core/pr-monitor.d.ts +1 -1
- package/dist/core/pr-monitor.js +31 -11
- package/dist/core/pr-quality-rubric.d.ts +70 -0
- package/dist/core/pr-quality-rubric.js +121 -0
- package/dist/core/repo-vet.d.ts +90 -0
- package/dist/core/repo-vet.js +178 -0
- package/dist/core/state-schema.d.ts +77 -0
- package/dist/core/state-schema.js +84 -0
- package/dist/core/state.d.ts +7 -0
- package/dist/core/state.js +10 -0
- package/dist/core/strategy.d.ts +95 -0
- package/dist/core/strategy.js +270 -0
- package/dist/core/types.d.ts +51 -0
- package/dist/core/workflow-state.d.ts +56 -0
- package/dist/core/workflow-state.js +101 -0
- package/dist/formatters/json.d.ts +252 -0
- package/dist/formatters/json.js +153 -0
- package/package.json +1 -1
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CI-enforced tool detection (#1286).
|
|
3
|
+
*
|
|
4
|
+
* Extracted from `workflows/pre-commit-review.md` Steps 2b and 6a so
|
|
5
|
+
* the same logic isn't reimplemented twice in markdown. Callers
|
|
6
|
+
* supply pre-fetched config snippets (the workflow runs `cat` /
|
|
7
|
+
* `head` against the well-known files); this function does the
|
|
8
|
+
* structured parse.
|
|
9
|
+
*
|
|
10
|
+
* Pure typed helper — no I/O.
|
|
11
|
+
*/
|
|
12
|
+
export type CIToolName = 'lint' | 'format' | 'typecheck' | 'test' | 'build' | 'commit-format' | 'security-scan';
|
|
13
|
+
export interface CIEnforcedToolsInput {
|
|
14
|
+
/** `.pre-commit-config.yaml` contents, or null when the file is missing. */
|
|
15
|
+
preCommitConfigYaml: string | null;
|
|
16
|
+
/** Concatenated contents of `.github/workflows/*.yml` (or null when none). */
|
|
17
|
+
workflowYamls: string | null;
|
|
18
|
+
/** `Makefile` contents, or null when missing. */
|
|
19
|
+
makefile: string | null;
|
|
20
|
+
/** `package.json` contents — used to detect script names. */
|
|
21
|
+
packageJson: string | null;
|
|
22
|
+
}
|
|
23
|
+
export interface CIEnforcedTool {
|
|
24
|
+
tool: CIToolName;
|
|
25
|
+
source: 'pre-commit' | 'github-workflow' | 'makefile' | 'package-json';
|
|
26
|
+
/** Short snippet that surfaced this tool — useful for explainability. */
|
|
27
|
+
evidence: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Detect which class of tool each input source enforces. The output
|
|
31
|
+
* may contain duplicates (e.g., `test` enforced by both pre-commit
|
|
32
|
+
* and a GitHub workflow); callers can dedupe on `tool` or display
|
|
33
|
+
* each evidence pair separately.
|
|
34
|
+
*/
|
|
35
|
+
export declare function getCIEnforcedTools(input: CIEnforcedToolsInput): CIEnforcedTool[];
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CI-enforced tool detection (#1286).
|
|
3
|
+
*
|
|
4
|
+
* Extracted from `workflows/pre-commit-review.md` Steps 2b and 6a so
|
|
5
|
+
* the same logic isn't reimplemented twice in markdown. Callers
|
|
6
|
+
* supply pre-fetched config snippets (the workflow runs `cat` /
|
|
7
|
+
* `head` against the well-known files); this function does the
|
|
8
|
+
* structured parse.
|
|
9
|
+
*
|
|
10
|
+
* Pure typed helper — no I/O.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Detect which class of tool each input source enforces. The output
|
|
14
|
+
* may contain duplicates (e.g., `test` enforced by both pre-commit
|
|
15
|
+
* and a GitHub workflow); callers can dedupe on `tool` or display
|
|
16
|
+
* each evidence pair separately.
|
|
17
|
+
*/
|
|
18
|
+
export function getCIEnforcedTools(input) {
|
|
19
|
+
const out = [];
|
|
20
|
+
if (input.preCommitConfigYaml) {
|
|
21
|
+
detectFromHaystack(input.preCommitConfigYaml, 'pre-commit', out);
|
|
22
|
+
}
|
|
23
|
+
if (input.workflowYamls) {
|
|
24
|
+
detectFromHaystack(input.workflowYamls, 'github-workflow', out);
|
|
25
|
+
}
|
|
26
|
+
if (input.makefile) {
|
|
27
|
+
detectFromHaystack(input.makefile, 'makefile', out);
|
|
28
|
+
}
|
|
29
|
+
if (input.packageJson) {
|
|
30
|
+
detectScriptsFromPackageJson(input.packageJson, out);
|
|
31
|
+
}
|
|
32
|
+
return out;
|
|
33
|
+
}
|
|
34
|
+
function detectFromHaystack(haystack, source, out) {
|
|
35
|
+
const lower = haystack.toLowerCase();
|
|
36
|
+
const checks = [
|
|
37
|
+
{
|
|
38
|
+
tool: 'lint',
|
|
39
|
+
pattern: /\b(?:eslint|biome|ruff|flake8|rubocop|golangci-lint|clippy|pylint)\b/i,
|
|
40
|
+
evidenceFor: ['eslint', 'biome', 'ruff', 'flake8', 'rubocop', 'golangci-lint', 'clippy', 'pylint'],
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
tool: 'format',
|
|
44
|
+
pattern: /\b(?:prettier|biome[\s-]+format|black|gofmt|rustfmt|clang-format)\b/i,
|
|
45
|
+
evidenceFor: ['prettier', 'biome', 'black', 'gofmt', 'rustfmt', 'clang-format'],
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
tool: 'typecheck',
|
|
49
|
+
pattern: /\b(?:tsc|mypy|sorbet|pyright|flow)\b/i,
|
|
50
|
+
evidenceFor: ['tsc', 'mypy', 'sorbet', 'pyright', 'flow'],
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
tool: 'test',
|
|
54
|
+
pattern: /\b(?:vitest|jest|pytest|rspec|go test|cargo test|mocha)\b/i,
|
|
55
|
+
evidenceFor: ['vitest', 'jest', 'pytest', 'rspec', 'go test', 'cargo test', 'mocha'],
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
// `tsc` alone (or `tsc --noEmit`) is a typecheck, not a build artifact
|
|
59
|
+
// step — keep it out of this pattern so a workflow with only
|
|
60
|
+
// `tsc --noEmit` doesn't fire BOTH `typecheck` and `build`. Real
|
|
61
|
+
// build steps that do produce artifacts (tsup, esbuild, webpack,
|
|
62
|
+
// cargo build, go build) stay here.
|
|
63
|
+
tool: 'build',
|
|
64
|
+
pattern: /\b(?:tsup|esbuild|webpack|cargo build|go build)\b/i,
|
|
65
|
+
evidenceFor: ['tsup', 'esbuild', 'webpack', 'cargo build', 'go build'],
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
tool: 'commit-format',
|
|
69
|
+
pattern: /\b(?:commitlint|conventional-commits)\b/i,
|
|
70
|
+
evidenceFor: ['commitlint', 'conventional-commits'],
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
tool: 'security-scan',
|
|
74
|
+
pattern: /\b(?:codeql|semgrep|trivy|gitleaks|snyk)\b/i,
|
|
75
|
+
evidenceFor: ['codeql', 'semgrep', 'trivy', 'gitleaks', 'snyk'],
|
|
76
|
+
},
|
|
77
|
+
];
|
|
78
|
+
for (const c of checks) {
|
|
79
|
+
if (c.pattern.test(lower)) {
|
|
80
|
+
const evidence = c.evidenceFor.find((e) => lower.includes(e)) ?? c.tool;
|
|
81
|
+
out.push({ tool: c.tool, source, evidence });
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function detectScriptsFromPackageJson(packageJson, out) {
|
|
86
|
+
let parsed;
|
|
87
|
+
try {
|
|
88
|
+
parsed = JSON.parse(packageJson);
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const scripts = parsed.scripts ?? {};
|
|
94
|
+
const scriptToTool = [
|
|
95
|
+
{ key: /^lint(:|$)/, tool: 'lint' },
|
|
96
|
+
{ key: /^format(:|$)/, tool: 'format' },
|
|
97
|
+
{ key: /^typecheck(:|$)|^tsc(:|$)/, tool: 'typecheck' },
|
|
98
|
+
{ key: /^test(:|$)/, tool: 'test' },
|
|
99
|
+
{ key: /^build(:|$)/, tool: 'build' },
|
|
100
|
+
];
|
|
101
|
+
for (const [name] of Object.entries(scripts)) {
|
|
102
|
+
for (const m of scriptToTool) {
|
|
103
|
+
if (m.key.test(name)) {
|
|
104
|
+
out.push({ tool: m.tool, source: 'package-json', evidence: `scripts.${name}` });
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Post-push comment decision (#1286).
|
|
3
|
+
*
|
|
4
|
+
* Centralizes the "should I draft a comment, or does the diff speak
|
|
5
|
+
* for itself?" rule that was duplicated between
|
|
6
|
+
* `workflows/pre-commit-review.md` Step 7a and `agents/pr-responder.md`
|
|
7
|
+
* Step 3.
|
|
8
|
+
*
|
|
9
|
+
* Pure typed helper — no LLM, no I/O. Callers do the upstream natural-
|
|
10
|
+
* language classification (mapping a maintainer comment to a structured
|
|
11
|
+
* `FeedbackCategory` + flags) and pass in the result. This module owns
|
|
12
|
+
* the rule that decides skip vs. draft from the structured input.
|
|
13
|
+
*
|
|
14
|
+
* Same architectural shape as #1245 (compliance-score), #1242 (repo-vet),
|
|
15
|
+
* #1243 (strategy), #1252 (pr-quality-rubric), and #1264 (issue-effort /
|
|
16
|
+
* maintainer-hints).
|
|
17
|
+
*/
|
|
18
|
+
export type FeedbackCategory = 'code_request' | 'question' | 'explanation_request' | 'style_request' | 'design_discussion' | 'approval_with_nit' | 'formatting_complaint';
|
|
19
|
+
export interface FeedbackClassification {
|
|
20
|
+
category: FeedbackCategory;
|
|
21
|
+
/**
|
|
22
|
+
* Whether this individual feedback item, considered in isolation,
|
|
23
|
+
* requires a drafted comment. Aggregated by `shouldDraftResponse`
|
|
24
|
+
* across the full feedback list.
|
|
25
|
+
*/
|
|
26
|
+
needsComment: boolean;
|
|
27
|
+
/** Short rationale rendered alongside the decision. */
|
|
28
|
+
reason: string;
|
|
29
|
+
}
|
|
30
|
+
export interface CommentDecisionInput {
|
|
31
|
+
/**
|
|
32
|
+
* Pre-classified feedback items in chronological order. The caller
|
|
33
|
+
* (agent or workflow) is responsible for the natural-language
|
|
34
|
+
* categorization step; this function only consumes structured input.
|
|
35
|
+
*/
|
|
36
|
+
feedback: readonly FeedbackClassification[];
|
|
37
|
+
/**
|
|
38
|
+
* True when the contributor pushed code that addresses every
|
|
39
|
+
* feedback item that requested a code change. The agent's diff
|
|
40
|
+
* inspection determines this; the function trusts the boolean.
|
|
41
|
+
*/
|
|
42
|
+
allRequestedChangesAddressed: boolean;
|
|
43
|
+
}
|
|
44
|
+
export interface CommentDecisionResult {
|
|
45
|
+
shouldDraft: boolean;
|
|
46
|
+
reason: string;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Decide whether the contributor should draft a comment after a push.
|
|
50
|
+
*
|
|
51
|
+
* Rule (sourced verbatim from `workflows/pre-commit-review.md` Step 7a
|
|
52
|
+
* and `agents/pr-responder.md` "Comment Decision Logic"):
|
|
53
|
+
*
|
|
54
|
+
* - Skip the comment entirely when ALL of these are true:
|
|
55
|
+
* 1. Every piece of maintainer feedback that requested code changes
|
|
56
|
+
* has a corresponding code change.
|
|
57
|
+
* 2. No question was asked.
|
|
58
|
+
* 3. Nothing was intentionally left unchanged (a "yes the maintainer
|
|
59
|
+
* asked but I did not change it" item always needs explanation).
|
|
60
|
+
* 4. The diff makes the fix self-evident — modeled here as the
|
|
61
|
+
* `allRequestedChangesAddressed` flag the caller passes in.
|
|
62
|
+
*
|
|
63
|
+
* - Draft a comment if ANY of these are true:
|
|
64
|
+
* 1. The maintainer asked a question.
|
|
65
|
+
* 2. The feedback is conceptual or design-level (the diff alone
|
|
66
|
+
* cannot answer "why").
|
|
67
|
+
* 3. Something was intentionally left unchanged (`needsComment: true`
|
|
68
|
+
* on a code-request item where the contributor disagrees).
|
|
69
|
+
* 4. Only some of multiple requested changes were addressed.
|
|
70
|
+
* 5. The contributor deviated from exactly what was asked.
|
|
71
|
+
*/
|
|
72
|
+
export declare function shouldDraftResponse(input: CommentDecisionInput): CommentDecisionResult;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Post-push comment decision (#1286).
|
|
3
|
+
*
|
|
4
|
+
* Centralizes the "should I draft a comment, or does the diff speak
|
|
5
|
+
* for itself?" rule that was duplicated between
|
|
6
|
+
* `workflows/pre-commit-review.md` Step 7a and `agents/pr-responder.md`
|
|
7
|
+
* Step 3.
|
|
8
|
+
*
|
|
9
|
+
* Pure typed helper — no LLM, no I/O. Callers do the upstream natural-
|
|
10
|
+
* language classification (mapping a maintainer comment to a structured
|
|
11
|
+
* `FeedbackCategory` + flags) and pass in the result. This module owns
|
|
12
|
+
* the rule that decides skip vs. draft from the structured input.
|
|
13
|
+
*
|
|
14
|
+
* Same architectural shape as #1245 (compliance-score), #1242 (repo-vet),
|
|
15
|
+
* #1243 (strategy), #1252 (pr-quality-rubric), and #1264 (issue-effort /
|
|
16
|
+
* maintainer-hints).
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Decide whether the contributor should draft a comment after a push.
|
|
20
|
+
*
|
|
21
|
+
* Rule (sourced verbatim from `workflows/pre-commit-review.md` Step 7a
|
|
22
|
+
* and `agents/pr-responder.md` "Comment Decision Logic"):
|
|
23
|
+
*
|
|
24
|
+
* - Skip the comment entirely when ALL of these are true:
|
|
25
|
+
* 1. Every piece of maintainer feedback that requested code changes
|
|
26
|
+
* has a corresponding code change.
|
|
27
|
+
* 2. No question was asked.
|
|
28
|
+
* 3. Nothing was intentionally left unchanged (a "yes the maintainer
|
|
29
|
+
* asked but I did not change it" item always needs explanation).
|
|
30
|
+
* 4. The diff makes the fix self-evident — modeled here as the
|
|
31
|
+
* `allRequestedChangesAddressed` flag the caller passes in.
|
|
32
|
+
*
|
|
33
|
+
* - Draft a comment if ANY of these are true:
|
|
34
|
+
* 1. The maintainer asked a question.
|
|
35
|
+
* 2. The feedback is conceptual or design-level (the diff alone
|
|
36
|
+
* cannot answer "why").
|
|
37
|
+
* 3. Something was intentionally left unchanged (`needsComment: true`
|
|
38
|
+
* on a code-request item where the contributor disagrees).
|
|
39
|
+
* 4. Only some of multiple requested changes were addressed.
|
|
40
|
+
* 5. The contributor deviated from exactly what was asked.
|
|
41
|
+
*/
|
|
42
|
+
export function shouldDraftResponse(input) {
|
|
43
|
+
if (input.feedback.length === 0) {
|
|
44
|
+
return { shouldDraft: false, reason: 'no maintainer feedback to address' };
|
|
45
|
+
}
|
|
46
|
+
// Categories that always demand a written reply, regardless of the
|
|
47
|
+
// diff. The diff cannot answer a question or carry a design decision.
|
|
48
|
+
const alwaysDraftCategories = new Set(['question', 'explanation_request', 'design_discussion']);
|
|
49
|
+
for (const item of input.feedback) {
|
|
50
|
+
if (alwaysDraftCategories.has(item.category)) {
|
|
51
|
+
return {
|
|
52
|
+
shouldDraft: true,
|
|
53
|
+
reason: `${item.category.replace(/_/g, ' ')} — diff alone cannot address this`,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// Caller flagged at least one item as "I'm intentionally not making
|
|
58
|
+
// this change" or "I deviated" — needs an explanation either way.
|
|
59
|
+
const intentionalGap = input.feedback.find((item) => item.needsComment);
|
|
60
|
+
if (intentionalGap) {
|
|
61
|
+
return {
|
|
62
|
+
shouldDraft: true,
|
|
63
|
+
reason: `requires written explanation: ${intentionalGap.reason}`,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
// Caller signaled the diff covers everything that was requested.
|
|
67
|
+
if (input.allRequestedChangesAddressed) {
|
|
68
|
+
return { shouldDraft: false, reason: 'all requested changes are visible in the diff' };
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
shouldDraft: true,
|
|
72
|
+
reason: 'not all requested changes are visible in the diff yet',
|
|
73
|
+
};
|
|
74
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PR compliance scoring (#1245).
|
|
3
|
+
*
|
|
4
|
+
* Extracted from `agents/pr-compliance-checker.md`'s in-prompt scoring
|
|
5
|
+
* tables so the weights, thresholds, and per-check rules are
|
|
6
|
+
* deterministic, unit-testable, and tunable without editing markdown.
|
|
7
|
+
* Same architectural shape as success-grade (#858), linked-PR
|
|
8
|
+
* classifier (#910), and anti-AI scan (#911).
|
|
9
|
+
*
|
|
10
|
+
* The function intentionally does not fetch PR data — callers (the MCP
|
|
11
|
+
* tool, the CLI command, the agent) supply pre-fetched metadata so the
|
|
12
|
+
* score is reproducible against fixture data and the same input shape
|
|
13
|
+
* works for both live PRs and historical replay.
|
|
14
|
+
*/
|
|
15
|
+
export type ComplianceCheckStatus = 'pass' | 'warn' | 'fail';
|
|
16
|
+
export interface ComplianceCheckResult {
|
|
17
|
+
status: ComplianceCheckStatus;
|
|
18
|
+
weight: number;
|
|
19
|
+
detail: string;
|
|
20
|
+
}
|
|
21
|
+
export type ComplianceRating = 'ready' | 'minor' | 'fix_first' | 'significant_work';
|
|
22
|
+
/** Emoji surfaced alongside the rating in agent output. */
|
|
23
|
+
export type ComplianceEmoji = '🌟' | '✅' | '⚠️' | '❌';
|
|
24
|
+
export interface ComplianceScoreResult {
|
|
25
|
+
/** 0–100 weighted score across the six checks. */
|
|
26
|
+
score: number;
|
|
27
|
+
rating: ComplianceRating;
|
|
28
|
+
emoji: ComplianceEmoji;
|
|
29
|
+
checks: {
|
|
30
|
+
issueReference: ComplianceCheckResult;
|
|
31
|
+
description: ComplianceCheckResult;
|
|
32
|
+
focusedChanges: ComplianceCheckResult;
|
|
33
|
+
tests: ComplianceCheckResult;
|
|
34
|
+
title: ComplianceCheckResult;
|
|
35
|
+
branch: ComplianceCheckResult;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/** Minimum PR metadata required to compute a compliance score. */
|
|
39
|
+
export interface PRMetadata {
|
|
40
|
+
title: string;
|
|
41
|
+
body: string;
|
|
42
|
+
branch: string;
|
|
43
|
+
filesChangedCount: number;
|
|
44
|
+
additions: number;
|
|
45
|
+
deletions: number;
|
|
46
|
+
/**
|
|
47
|
+
* Filenames touched by the PR. Used by the test-detection check to
|
|
48
|
+
* decide whether the PR includes a test file.
|
|
49
|
+
*/
|
|
50
|
+
files: string[];
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Verified state of an issue referenced from a PR body. Populated by
|
|
54
|
+
* the compliance-score command (which calls the Issues API per
|
|
55
|
+
* reference) and consumed by `checkIssueReference` to fail loud on
|
|
56
|
+
* broken links (#1246 Improvement B).
|
|
57
|
+
*/
|
|
58
|
+
export interface LinkedIssueInfo {
|
|
59
|
+
/** Issue number parsed from the PR body. */
|
|
60
|
+
number: number;
|
|
61
|
+
/** Owner/repo where the issue lives — may differ from the PR's repo
|
|
62
|
+
* when a cross-repo reference like `owner/other#42` is used. */
|
|
63
|
+
repo: string;
|
|
64
|
+
/** True when the reference targeted a different repo than the PR. */
|
|
65
|
+
crossRepo: boolean;
|
|
66
|
+
/**
|
|
67
|
+
* Result of the verification API call.
|
|
68
|
+
* - `open` / `closed`: confirmed live state.
|
|
69
|
+
* - `not_found`: HTTP 404 — the referenced issue does not exist.
|
|
70
|
+
* - `unverifiable`: a non-404 failure (rate-limit, 5xx, network).
|
|
71
|
+
* The check treats this neutrally rather than as a broken link, so a
|
|
72
|
+
* GitHub API hiccup doesn't downgrade a valid PR's compliance score.
|
|
73
|
+
*/
|
|
74
|
+
state: 'open' | 'closed' | 'not_found' | 'unverifiable';
|
|
75
|
+
/** Whole days since the issue was closed, when state === 'closed'.
|
|
76
|
+
* Used to distinguish "recently closed, may still apply" from "long
|
|
77
|
+
* stale, almost certainly the wrong reference." */
|
|
78
|
+
closedDaysAgo?: number;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Optional repo context used to fine-tune individual check thresholds
|
|
82
|
+
* (#1245). All fields are optional; absent fields use safe defaults that
|
|
83
|
+
* match the original in-prompt rules.
|
|
84
|
+
*/
|
|
85
|
+
export interface RepoContext {
|
|
86
|
+
/**
|
|
87
|
+
* Whether the target repo has any visible test infrastructure
|
|
88
|
+
* (`test/`, `tests/`, `__tests__/`, `spec/`, etc.). When `false`, the
|
|
89
|
+
* tests check downgrades from `fail` to `warn` because tests aren't
|
|
90
|
+
* required by the project.
|
|
91
|
+
*/
|
|
92
|
+
hasTestInfrastructure?: boolean;
|
|
93
|
+
/**
|
|
94
|
+
* Verified state of every issue/PR reference found in the PR body
|
|
95
|
+
* (#1246 Improvement B). When provided, `checkIssueReference` will
|
|
96
|
+
* fail-loud on broken or stale references rather than passing on the
|
|
97
|
+
* regex match alone. Absent / empty array preserves original
|
|
98
|
+
* regex-only behavior.
|
|
99
|
+
*/
|
|
100
|
+
linkedIssues?: LinkedIssueInfo[];
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* After how many days a closed-issue reference flips from "warn"
|
|
104
|
+
* (probably still relevant) to "fail" (probably stale). Exported so
|
|
105
|
+
* callers can document the cutoff (#1246).
|
|
106
|
+
*/
|
|
107
|
+
export declare const CLOSED_ISSUE_RECENT_DAYS = 30;
|
|
108
|
+
/** Title byte budget — Conventional Commits style fits comfortably under 72. */
|
|
109
|
+
export { TITLE_LENGTH_BUDGET } from './pr-quality-rubric.js';
|
|
110
|
+
/** "Focused changes" thresholds. Source of truth lives in pr-quality-rubric.ts. */
|
|
111
|
+
export declare const FOCUSED_CHANGES: {
|
|
112
|
+
readonly passFiles: 10;
|
|
113
|
+
readonly passLines: 400;
|
|
114
|
+
readonly warnFiles: 20;
|
|
115
|
+
readonly warnLines: 800;
|
|
116
|
+
};
|
|
117
|
+
/** Score → rating cutoffs. */
|
|
118
|
+
export declare const RATING_CUTOFFS: {
|
|
119
|
+
readonly ready: 90;
|
|
120
|
+
readonly minor: 75;
|
|
121
|
+
readonly fixFirst: 60;
|
|
122
|
+
};
|
|
123
|
+
/**
|
|
124
|
+
* Compute a compliance score from PR metadata, optionally fine-tuned by
|
|
125
|
+
* repo context (#1245). Pure function — no I/O, no global state.
|
|
126
|
+
*/
|
|
127
|
+
export declare function computeComplianceScore(meta: PRMetadata, repoContext?: RepoContext): ComplianceScoreResult;
|