@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.
Files changed (58) hide show
  1. package/dist/cli-registry.js +99 -0
  2. package/dist/cli.bundle.cjs +112 -105
  3. package/dist/commands/compliance-score.d.ts +21 -0
  4. package/dist/commands/compliance-score.js +156 -0
  5. package/dist/commands/daily.d.ts +8 -0
  6. package/dist/commands/daily.js +21 -0
  7. package/dist/commands/index.d.ts +6 -0
  8. package/dist/commands/index.js +6 -0
  9. package/dist/commands/list-mark-done.d.ts +48 -0
  10. package/dist/commands/list-mark-done.js +213 -0
  11. package/dist/commands/parse-list.js +86 -9
  12. package/dist/commands/repo-vet.d.ts +21 -0
  13. package/dist/commands/repo-vet.js +215 -0
  14. package/dist/commands/startup.js +41 -1
  15. package/dist/core/anti-llm-policy.d.ts +42 -13
  16. package/dist/core/anti-llm-policy.js +102 -13
  17. package/dist/core/ci-analysis.d.ts +32 -1
  18. package/dist/core/ci-analysis.js +92 -0
  19. package/dist/core/ci-enforced-tools.d.ts +35 -0
  20. package/dist/core/ci-enforced-tools.js +109 -0
  21. package/dist/core/comment-decision.d.ts +72 -0
  22. package/dist/core/comment-decision.js +74 -0
  23. package/dist/core/compliance-score.d.ts +127 -0
  24. package/dist/core/compliance-score.js +277 -0
  25. package/dist/core/config-registry.js +12 -0
  26. package/dist/core/contributing.d.ts +52 -0
  27. package/dist/core/contributing.js +139 -0
  28. package/dist/core/errors.d.ts +19 -0
  29. package/dist/core/errors.js +54 -0
  30. package/dist/core/extraction-categories.d.ts +55 -0
  31. package/dist/core/extraction-categories.js +108 -0
  32. package/dist/core/follow-up-history.d.ts +41 -0
  33. package/dist/core/follow-up-history.js +71 -0
  34. package/dist/core/gist-state-store.d.ts +30 -7
  35. package/dist/core/gist-state-store.js +87 -11
  36. package/dist/core/issue-conversation.js +1 -0
  37. package/dist/core/issue-effort.d.ts +29 -0
  38. package/dist/core/issue-effort.js +41 -0
  39. package/dist/core/maintainer-hints.d.ts +23 -0
  40. package/dist/core/maintainer-hints.js +36 -0
  41. package/dist/core/pr-monitor.d.ts +1 -1
  42. package/dist/core/pr-monitor.js +31 -11
  43. package/dist/core/pr-quality-rubric.d.ts +70 -0
  44. package/dist/core/pr-quality-rubric.js +121 -0
  45. package/dist/core/repo-vet.d.ts +90 -0
  46. package/dist/core/repo-vet.js +178 -0
  47. package/dist/core/state-schema.d.ts +77 -0
  48. package/dist/core/state-schema.js +84 -0
  49. package/dist/core/state.d.ts +7 -0
  50. package/dist/core/state.js +10 -0
  51. package/dist/core/strategy.d.ts +95 -0
  52. package/dist/core/strategy.js +270 -0
  53. package/dist/core/types.d.ts +51 -0
  54. package/dist/core/workflow-state.d.ts +56 -0
  55. package/dist/core/workflow-state.js +101 -0
  56. package/dist/formatters/json.d.ts +252 -0
  57. package/dist/formatters/json.js +153 -0
  58. 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;