@sctg/backport-agent 0.1.0 → 0.1.1-20260603131045

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/README.md CHANGED
@@ -1,3 +1,4 @@
1
+ [![Npm package version](https://badgen.net/npm/v/@sctg/backport-agent)](https://npmjs.com/package/@sctg/backport-agent)[![TypeScript](https://badgen.net/badge/icon/typescript?icon=typescript&label)](https://typescriptlang.org)
1
2
  # Backport Agent
2
3
 
3
4
  A deterministic IA powered agent for keeping a heavily customized Git fork in sync with an active upstream repository.
@@ -20,13 +21,11 @@ Backport Agent focuses on the parts that matter most:
20
21
 
21
22
  The agent works as a sync pipeline rather than a one-shot merge bot. It reads the upstream history, selects candidate commits, evaluates their risk, applies them in controlled batches, validates the result, and generates a report for review.
22
23
 
23
- It is built to support a fork that includes features such as:
24
+ It is built to support forks that include features such as:
24
25
 
25
- - the `keypoollive` provider;
26
- - encrypted vault-backed model and key discovery;
27
- - round-robin key rotation;
28
- - GitHub Actions build-time package renaming;
29
- - Mintlify documentation generation;
26
+ - custom LLM providers (e.g. `keypoollive` with encrypted vault-backed key rotation);
27
+ - build-time package renaming and CI workflow customizations;
28
+ - documentation generation pipelines;
30
29
  - a local reporting and validation workflow.
31
30
 
32
31
  ## How it is structured
@@ -56,13 +55,28 @@ npm install
56
55
  - [config.example.json](config.example.json)
57
56
  - [customizations.example.yaml](customizations.example.yaml)
58
57
 
59
- 3. Set the required vault environment variables in your shell or `.env` file.
58
+ 3. Set the required provider credentials in your shell or `.env` file.
59
+
60
+ For the **keypoollive** provider (vault-based key rotation):
60
61
 
61
62
  ```bash
62
63
  KEYPOOL_VAULT_URL=https://...
63
64
  KEYPOOL_LIVE_SECRET=...
64
65
  ```
65
66
 
67
+ For any other provider supported by `@sctg/cline-sdk`, set the corresponding API key:
68
+
69
+ ```bash
70
+ ANTHROPIC_API_KEY=sk-ant-...
71
+ # or OPENAI_API_KEY=sk-..., MISTRAL_API_KEY=..., etc.
72
+ ```
73
+
74
+ You can also override the provider or API key at runtime without editing `config.json`:
75
+
76
+ ```bash
77
+ npm start -- --provider anthropic --api-key sk-ant-...
78
+ ```
79
+
66
80
  4. Start the agent.
67
81
 
68
82
  ```bash
@@ -106,7 +120,7 @@ The repository includes both unit and integration coverage.
106
120
  - `npm run test:unit` runs fast deterministic tests.
107
121
  - `npm run test:integration` runs integration tests, including real KeypoolLive calls when your vault is configured.
108
122
 
109
- The integration suite is intentionally practical. It verifies Git behavior in temporary repositories and exercises real SDK tools against the `keypoollive` provider with the `mistral/devstral-latest` model when `.env` is available.
123
+ The integration suite is intentionally practical. It verifies Git behavior in temporary repositories and exercises real SDK tools against a configured provider (defaults to `keypoollive` with the `mistral/devstral-latest` model) when `.env` is available.
110
124
 
111
125
  ## Configuration
112
126
 
@@ -115,12 +129,87 @@ The main runtime configuration lives in a JSON file modeled after [config.exampl
115
129
  - the upstream repository and branch;
116
130
  - the fork repository and branch;
117
131
  - the working directory;
132
+ - the LLM provider and model selection (`provider`, `fast`, `specialist`, `powerful`);
118
133
  - sync limits and batching;
119
- - model selection;
120
134
  - validation tiers.
121
135
 
136
+ The `provider` field in the `models` section is required. It accepts any provider ID supported by `@sctg/cline-sdk` (e.g. `"keypoollive"`, `"anthropic"`, `"openai"`, `"mistral"`, `"gemini"`). The API key is resolved from the `apiKey` field, a `$ENV_VAR` reference, or the implicit `{PROVIDER_UPPER}_API_KEY` environment variable.
137
+
138
+ ### `sync.prNumberMatching` — Manual backport detection (optional)
139
+
140
+ By default, the agent detects already-applied commits using three signals: `git cherry` patch comparison, exact subject-line match, and the `cherry picked from commit <sha>` annotation added by `git cherry-pick -x`.
141
+
142
+ When a commit is cherry-picked manually (conflict resolution, subject rewrite, no `-x` flag), all three signals can miss it. Enabling `prNumberMatching` adds a fourth signal: if a fork commit references the same upstream PR number **and** the two subjects are similar enough (Jaccard word-token score), the commit is considered already applied.
143
+
144
+ ```json
145
+ "sync": {
146
+ "prNumberMatching": {
147
+ "enabled": true,
148
+ "minSubjectSimilarity": 0.4
149
+ }
150
+ }
151
+ ```
152
+
153
+ | Field | Default | Description |
154
+ |---|---|---|
155
+ | `enabled` | `false` | Activate PR-number-based duplicate detection. |
156
+ | `minSubjectSimilarity` | `0.4` | Minimum Jaccard word-token similarity (0–1) between the upstream subject and the matching fork subject. Lower → more permissive (risk of false positives). Higher → stricter (may miss heavily reworded backports). |
157
+
158
+ **Example:** upstream commit `Move \`sdk/apps/\` to \`apps/\` (#11200)` is detected as already applied when the fork contains `feat(backport): Move sdk/apps/ to apps/ (cline#11200)` — the PR number matches and the similarity score (~0.67) exceeds the default threshold.
159
+
160
+ Enable this only when your team consistently includes the upstream PR number in manual backport commit messages.
161
+
162
+ ### `ai` section — Quality guardrails (optional)
163
+
164
+ The optional `ai` section configures the AI quality guardrails introduced to improve backport reliability. All fields have safe defaults and the section can be omitted entirely.
165
+
166
+ ```json
167
+ "ai": {
168
+ "minAutoApplyConfidence": "medium",
169
+ "requireReviewOnSemanticRisk": false,
170
+ "enableConflictConsensus": false,
171
+ "conflictConsensusThreshold": 0.7,
172
+ "enrichCustomizationContext": true
173
+ }
174
+ ```
175
+
176
+ | Field | Default | Description |
177
+ |---|---|---|
178
+ | `minAutoApplyConfidence` | `"medium"` | Minimum AI confidence level (`"high"` or `"medium"`) to auto-apply a conflict resolution. Use `"high"` for stricter auto-apply. |
179
+ | `requireReviewOnSemanticRisk` | `false` | When `true`, any commit carrying semantic risk factors is escalated to `"review-required"` by `reconcile_ai_assessments`, regardless of the individual AI recommendations. |
180
+ | `enableConflictConsensus` | `false` | **Opt-in.** Runs a second, independent conflict resolution using `config.models.powerful` and compares both outputs with a Dice-coefficient similarity score. If the two resolutions diverge below `conflictConsensusThreshold`, confidence is downgraded to `"low"`. Enabling this roughly doubles LLM cost per conflict. |
181
+ | `conflictConsensusThreshold` | `0.7` | Minimum line-level similarity (0–1) required for consensus. Only used when `enableConflictConsensus: true`. |
182
+ | `enrichCustomizationContext` | `true` | When `true`, `check_customization_compatibility` reads up to 2 source files matching each customization glob (2 000 chars each) and injects their content into the AI prompt for richer analysis. |
183
+
184
+ ### AI sub-agent tools
185
+
186
+ The `src/ai` module exposes four tools that the main agent invokes when deterministic logic is not enough.
187
+
188
+ | Tool | Type | Purpose |
189
+ |---|---|---|
190
+ | `resolve_conflict_with_ai` | LLM call | Resolves merge conflicts in a single file using the configured `specialist` model. Returns `resolvedContent`, `confidence` (`"high"` / `"medium"` / `"low"`), and `reasoning`. Guards: conflict-marker detection, syntax balance check (JS/TS), optional dual-model consensus. |
191
+ | `analyze_commit_for_backport` | LLM call | Analyzes a commit diff to produce a summary, key changes, complexity estimate, semantic risk factors, and a backport `recommendation`. Also runs hallucination detection on referenced file paths. |
192
+ | `check_customization_compatibility` | LLM call | Checks whether a set of changes is compatible with the fork's declared customizations. Optionally enriches the prompt with actual file content when `ai.enrichCustomizationContext` is enabled. |
193
+ | `reconcile_ai_assessments` | Deterministic | **No LLM call.** Combines the outputs of the two analysis tools into a single `finalRecommendation`. Detects contradictions (e.g. analyze said "apply" but compatibility check failed), applies `requireReviewOnSemanticRisk` escalation, and always resolves ambiguity conservatively. Call this after both analysis tools have run for the same commit. |
194
+
195
+ Every LLM call is logged to the run's `.prompts.jsonl` file alongside structured quality signals (guards triggered, confidence, hallucination suspects). The detailed report includes a **Decision Quality Metrics** section summarising these signals across the full run.
196
+
197
+ #### Benchmark replay
198
+
199
+ The `src/tools/benchmark-replay.ts` script lets you compare two models side-by-side without running a full sync against a real repository. It reads an existing `.prompts.jsonl` log, replays every LLM call with the alternative model, and prints a Markdown comparison report.
200
+
201
+ ```bash
202
+ npx tsx src/tools/benchmark-replay.ts \
203
+ --log run-1780060224987.prompts.jsonl \
204
+ --model anthropic/claude-sonnet-4-5 \
205
+ --provider anthropic \
206
+ --api-key "$ANTHROPIC_API_KEY" > comparison.md
207
+ ```
208
+
122
209
  Custom fork invariants live in a YAML file modeled after [customizations.example.yaml](customizations.example.yaml). This is where you describe the areas that must not be broken by a backport run.
123
210
 
211
+
212
+
124
213
  ## For contributors
125
214
 
126
215
  Contributions are especially welcome in the following areas:
@@ -148,4 +237,4 @@ That makes the agent more useful for real maintenance work and easier for contri
148
237
 
149
238
  ## License
150
239
 
151
- MIT License. See [LICENSE.md](LICENSE.md) for details.
240
+ MIT License. See [LICENSE.md](LICENSE.md) for details.
@@ -25,13 +25,36 @@
25
25
  "batchSize": 5,
26
26
  "dryRun": true,
27
27
  "createPullRequest": true,
28
- "branchPrefix": "sync/upstream-"
28
+ "branchPrefix": "sync/upstream-",
29
+ "prNumberMatching": {
30
+ "_comment": "DISABLED BY DEFAULT. Enable to detect manually-applied backports that were reworded but kept the upstream PR number.",
31
+ "enabled": false,
32
+ "_comment_minSubjectSimilarity": "Jaccard word-token similarity threshold (0..1). Lower = more permissive (risk of false positives). Default 0.4.",
33
+ "minSubjectSimilarity": 0.4
34
+ }
29
35
  },
30
36
  "models": {
37
+ "_comment_provider": "Any provider supported by @sctg/cline-sdk: \"keypoollive\", \"anthropic\", \"openai\", \"mistral\", \"gemini\", etc.",
38
+ "provider": "keypoollive",
39
+ "_comment_apiKey": "Use \"$ENV_VAR\" to read from env, \"auto\" for keypoollive vault, or omit to auto-detect {PROVIDER_UPPER}_API_KEY",
40
+ "apiKey": "auto",
31
41
  "fast": "mistral/devstral-latest",
32
42
  "specialist": "mistral/devstral-latest",
33
43
  "powerful": "mistral/magistral-medium-latest"
34
44
  },
45
+ "ai": {
46
+ "_comment": "AI quality guardrails — all fields are optional (defaults shown). Remove this section to use defaults.",
47
+ "minAutoApplyConfidence": "medium",
48
+ "_comment_minAutoApplyConfidence": "Minimum AI confidence level to auto-apply a conflict resolution without human review. \"high\" = only apply when the AI is very certain.",
49
+ "requireReviewOnSemanticRisk": false,
50
+ "_comment_requireReviewOnSemanticRisk": "When true, any commit with semantic risk factors (e.g. API surface changes) is escalated to \"review-required\" by reconcile_ai_assessments.",
51
+ "enableConflictConsensus": false,
52
+ "_comment_enableConflictConsensus": "DISABLED BY DEFAULT. Set to true to run a second independent resolution using config.models.powerful and compare the two outputs. Consensus failures downgrade confidence to \"low\". This roughly doubles LLM cost per conflict.",
53
+ "conflictConsensusThreshold": 0.7,
54
+ "_comment_conflictConsensusThreshold": "Minimum Dice-coefficient line similarity (0..1) between the two consensus responses to consider them in agreement. Only used when enableConflictConsensus=true.",
55
+ "enrichCustomizationContext": true,
56
+ "_comment_enrichCustomizationContext": "When true, check_customization_compatibility reads up to 2 matching source files (2000 chars each) and injects their content into the AI prompt for richer context."
57
+ },
35
58
  "resolve": {
36
59
  "_comment": "Glob or /regex/flags patterns matched against repo-relative file paths",
37
60
  "ours": [