@orangepro/orangepro-mcp 0.2.29 → 0.2.30

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,117 +1,126 @@
1
- # OrangePro MCP
1
+ <p align="center">
2
+ <img src="https://github.com/OrangeproAI/orangepro-mcp/raw/main/docs/logo-horizontal.svg" alt="OrangePro" width="320" />
3
+ </p>
4
+
5
+ <p align="center">
6
+ <strong>Find the behaviors your tests miss. Generate grounded tests that actually run.</strong>
7
+ </p>
8
+
9
+ <p align="center">
10
+ <a href="https://www.npmjs.com/package/@orangepro/mcp-server"><img src="https://badge.fury.io/js/@orangepro%2Fmcp-server.svg" alt="npm version" /></a>
11
+ <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green.svg" alt="MIT License" /></a>
12
+ <a href="https://www.npmjs.com/package/@orangepro/mcp-server"><img src="https://img.shields.io/npm/dw/@orangepro/mcp-server.svg" alt="npm downloads" /></a>
13
+ <a href="https://glama.ai/mcp/servers/OrangeproAI/orangepro-mcp"><img src="https://glama.ai/mcp/servers/OrangeproAI/orangepro-mcp/badges/score.svg" alt="Glama score" /></a>
14
+ <a href="https://registry.modelcontextprotocol.io/?q=orangepro"><img src="https://img.shields.io/badge/MCP_Registry-orangepro-orange.svg" alt="MCP Registry" /></a>
15
+ </p>
2
16
 
3
- [![npm version](https://badge.fury.io/js/@orangepro%2Fmcp-server.svg )](https://www.npmjs.com/package/@orangepro/mcp-server )
4
- [![MIT License](https://img.shields.io/badge/license-MIT-green.svg )](LICENSE)
5
- [![npm downloads](https://img.shields.io/npm/dw/@orangepro/mcp-server.svg )](https://www.npmjs.com/package/@orangepro/mcp-server )
6
- [![orangepro-mcp MCP server](https://glama.ai/mcp/servers/OrangeproAI/orangepro-mcp/badges/score.svg )](https://glama.ai/mcp/servers/OrangeproAI/orangepro-mcp )
7
- [![MCP Registry](https://img.shields.io/badge/MCP_Registry-ai.orangepro%2Fmcp-orange.svg )](https://registry.modelcontextprotocol.io/?q=orangepro )
8
-
9
- **Find the behaviors your tests miss. Generate grounded tests that actually run.**
10
-
11
- `opro` builds a knowledge graph from your local checkout, maps every behavior in your code, shows which ones are tested and which aren't, and generates integration-level tests grounded in real symbols — not hallucinated imports. It runs as a CLI and a local stdio MCP server.
17
+ ---
12
18
 
13
- Once you run the mcp server against a repo, you can get behavior-coverage/html
14
- **<a href="https://orangeproai.github.io/orangepro-mcp/twenty-crm-behavior-coverage.html" target="_blank">→ Live example: Twenty CRM behavior coverage report</a>**
19
+ OrangePro maps every public behavior in your codebase, scores each one by real test evidence, and shows you the structural blind spots before your users find them. Runs locally. Your code never leaves your machine.
15
20
 
16
- <img width="895" alt="OrangePro system map — entry lanes, services, evidence tiers" src="https://github.com/user-attachments/assets/1ceba779-e0ec-4ec1-99ce-001bc3589b42" />
21
+ ```bash
22
+ npx -y @orangepro/mcp-server@latest start .
23
+ ```
17
24
 
18
- *Fig 1: System map entry lanes (GraphQL, HTTP, Jobs) flowing into services, sized by traffic, colored by evidence tier, red-ringed by risk.*
25
+ <!-- TODO: Replace with a terminal GIF showing the command running and report opening -->
19
26
 
20
- <img width="818" height="1284" alt="Screenshot 2026-07-30 at 4 38 35 PM" src="https://github.com/user-attachments/assets/1c41475d-be2a-4c6d-ace6-c6546573bf97" />
27
+ ---
21
28
 
29
+ ## Table of Contents
22
30
 
23
- *Fig 2: Priority gaps — top 20 unproven behaviors ranked by blast radius, with generated test drafts and applicable testing categories.*
31
+ - [What you get](#what-you-get)
32
+ - [Evidence tiers](#evidence-tiers)
33
+ - [Quick start](#quick-start)
34
+ - [Use with your coding agent](#use-with-your-coding-agent)
35
+ - [How it works](#how-it-works)
36
+ - [Language support](#language-support)
37
+ - [Privacy](#privacy)
38
+ - [CLI reference](#cli-reference)
39
+ - [MCP tools](#mcp-tools-18-total)
40
+ - [Platform](#whats-on-the-hosted-platform)
41
+ - [Contributing](#contributing)
24
42
 
25
43
  ---
26
- Install the target repository's dependencies first, then run OrangePro from that repository:
27
44
 
28
- ```bash
29
- cd /path/to/your/repo
30
- npm install # or pnpm install / bun install / the repository's package manager
45
+ ## What you get
31
46
 
32
- # Optional: enables AI candidate links, candidate flows, and test generation.
33
- export ANTHROPIC_API_KEY="..." # or OPENAI_API_KEY / OLLAMA_BASE_URL
47
+ One command produces an interactive HTML report:
34
48
 
35
- npx -y @orangepro/mcp-server@latest start . --prompt-version v5
49
+ ```bash
50
+ npx -y @orangepro/mcp-server@latest start .
36
51
  open .orangepro/behavior-coverage.html
37
52
  ```
53
+ The report has two modes: **Simple** (integration-level blind spots, plain English) and **Expert** (full behavior list, evidence tiers, flows, system map). Toggle with the pill switch at the top.
38
54
 
39
- With no model key, the same command still performs deterministic analysis, renders the report, and dynamically proves eligible behaviors using existing tests. With a key, it also discovers AI candidate flows and drafts grounded tests for the highest-risk gaps. AI output never changes evidence tiers; only the mutation-kill oracle can mint **Dynamically Proven**.
55
+ **<a href="https://orangeproai.github.io/orangepro-mcp/twenty-crm-behavior-coverage.html" target="_blank">→ Live example: Twenty CRM (5,237 behaviors mapped)</a>**
40
56
 
41
- The command writes:
57
+ <img width="895" alt="OrangePro system map — entry lanes, services, evidence tiers" src="https://github.com/user-attachments/assets/1ceba779-e0ec-4ec1-99ce-001bc3589b42](https://github.com/user-attachments/assets/a4d85b98-4f19-4647-8dd9-db5911574f49" />
42
58
 
43
- ```
44
- .orangepro/
45
- ├── behavior-coverage.html ← open this: system map, risks, flows, behaviors
46
- ├── graph.json ← deterministic evidence graph
47
- ├── COVERAGE_REPORT.md ← coverage and gap summary
48
- ├── rtm.md ← requirements traceability matrix
49
- └── ai/ ← candidate AI links/flows when a provider is configured
59
+ *System map — entry lanes (GraphQL, HTTP, Jobs) flowing into services, sized by traffic, colored by evidence tier, red-ringed by risk.*
50
60
 
51
- orangepro_generated/ ← contained generated tests; existing source files are untouched
52
- ```
53
61
 
54
- The report opens on a **system map** of your repo — entry lanes (GraphQL/HTTP/Jobs) flowing into the services they reach, sized by traffic, colored by evidence tier, risk-ringed — identical on every run. Each completed rerun shows a **delta banner** against the previous completed run. The report discloses when bounded path enumeration prunes additional branch expansions. Every behavior gets a plain-English description; every top risk gets a deterministic context line and a state-aware next step.
62
+ <img width="818" alt="Priority gaps" src="https://github.com/user-attachments/assets/30a512b6-7830-48db-a00f-a616e7176ea8" />
55
63
 
56
- Run `opro export` when you want a machine-readable evidence pack.
64
+ *Priority gaps of another open source Project HONO top 20 unproven behaviors ranked by blast radius, with generated test drafts.*
57
65
 
58
66
  ---
59
67
 
60
- ## Install
68
+ ## Evidence tiers
61
69
 
62
- ```bash
63
- # No install needed: run the full local workflow in the current repository
64
- npx -y @orangepro/mcp-server@latest start . --prompt-version v5
70
+ Every behavior gets exactly one tier. Nothing is labeled "tested" on faith.
65
71
 
66
- # Or global install
67
- npm install -g @orangepro/orangepro-mcp
68
- opro start . --prompt-version v5
72
+ | Tier | Color | What it means |
73
+ |------|-------|---------------|
74
+ | **Dynamically Proven** | 🟢 | A real test kills a targeted mutation of this behavior |
75
+ | **Runtime-covered** | 🟢 | Coverage tool executed this code |
76
+ | **Statically Linked** | 🟡 | A test imports and calls this code — structural link, not proof |
77
+ | **Unconfirmed Candidate** | ⚪ | A similar test file exists — a lead, not evidence |
78
+ | **No Signal** | 🔴 | Nothing tests this behavior |
69
79
 
70
- # Or from source
71
- git clone https://github.com/OrangeproAI/orangepro-mcp.git
72
- cd orangepro-mcp && npm ci && npm run build && npm link
73
- ```
80
+ > **"Dynamically Proven 0" is normal on first run.** Proof requires running tests against targeted mutations. That's the trust model.
74
81
 
75
82
  ---
76
83
 
77
- ## Use with your coding agent
84
+ ## Quick start
85
+
86
+ ```bash
87
+ cd /path/to/your/repo
88
+ npm install # install the repo's own dependencies first
78
89
 
79
- OrangePro runs as an MCP server. Any MCP-compatible agent (Cursor, Claude Code, Codex, Copilot, OpenCode) can drive it.
90
+ npx -y @orangepro/mcp-server@latest start .
91
+ open .orangepro/behavior-coverage.html
92
+ ```
80
93
 
81
- ### Quick agent setup
94
+ No API key needed. The report shows your system map, evidence tiers, priority gaps, and delta since last run.
82
95
 
83
- If you already have `opro` on your PATH, print the exact config for your client:
96
+ **Want test generation?** Add a model key (BYOK):
84
97
 
85
98
  ```bash
86
- opro agent --client codex
87
- opro agent --client claude-code
88
- opro agent --client cursor
89
- opro agent --client opencode
90
- opro agent --client generic
99
+ export ANTHROPIC_API_KEY="..." # or OPENAI_API_KEY / OLLAMA_BASE_URL
100
+ npx -y @orangepro/mcp-server@latest start .
91
101
  ```
92
102
 
93
- No global install is required. These commands use the published package:
103
+ AI output never changes evidence tiers. Only the mutation-kill oracle can mint Dynamically Proven.
94
104
 
95
- ```bash
96
- # Codex
97
- npx -y @orangepro/mcp-server@latest agent --client codex
105
+ **Output:**
98
106
 
99
- # Claude Code
100
- npx -y @orangepro/mcp-server@latest agent --client claude-code
107
+ ```
108
+ .orangepro/
109
+ ├── behavior-coverage.html ← open this
110
+ ├── graph.json ← deterministic evidence graph
111
+ ├── COVERAGE_REPORT.md ← coverage and gap summary
112
+ └── ai/ ← candidate flows (when a key is configured)
101
113
 
102
- # Cursor
103
- npx -y @orangepro/mcp-server@latest agent --client cursor
114
+ orangepro_generated/ ← generated tests; your source files are never touched
115
+ ```
104
116
 
105
- # OpenCode
106
- npx -y @orangepro/mcp-server@latest agent --client opencode
117
+ Each rerun shows a **delta banner**: what entered the codebase, what moved up in risk, what got resolved.
107
118
 
108
- # Generic MCP clients, including VS Code/Copilot-style MCP settings
109
- npx -y @orangepro/mcp-server@latest agent --client generic
110
- ```
119
+ ---
111
120
 
112
- ### Manual MCP config
121
+ ## Use with your coding agent
113
122
 
114
- Add to your client's MCP config:
123
+ OrangePro runs as an MCP server. Add to your client's config:
115
124
 
116
125
  ```json
117
126
  {
@@ -124,48 +133,83 @@ Add to your client's MCP config:
124
133
  }
125
134
  ```
126
135
 
127
- | Client | Config location |
136
+ | Client | Where to put it |
128
137
  | --- | --- |
129
138
  | Claude Code | `.mcp.json` or `~/.claude.json` |
130
139
  | Cursor | `~/.cursor/mcp.json` or Settings → MCP |
131
- | Codex | Config printed by `opro agent --client codex` or `npx -y @orangepro/mcp-server@latest agent --client codex` |
132
- | VS Code / Copilot | MCP settings; use the `generic` config if your client accepts raw MCP server JSON |
133
- | OpenCode | Config printed by `opro agent --client opencode` |
134
-
135
- ### The workflow
140
+ | VS Code / Copilot | MCP settings |
141
+ | Codex / OpenCode | Run `npx -y @orangepro/mcp-server@latest agent --client codex` |
136
142
 
137
- Tell your agent:
143
+ **The workflow:** Tell your agent:
138
144
 
139
145
  > "Use `orangepro_start`, then `orangepro_generate_tests` with base_ref=main. Write each test to its suggested_path, run it, and report pass/fail."
140
146
 
141
147
  The agent writes the test, runs it, calls `orangepro_prove`, and the behavior turns Dynamically Proven. One prompt, full loop.
142
148
 
143
- ### MCP tools (18 total)
149
+ ---
144
150
 
145
- | Tool | What it does |
146
- |------|--------------|
147
- | `orangepro_start` | One-command setup: analyze + report + next actions |
148
- | `orangepro_analyze_sources` | Build/refresh the evidence graph |
149
- | `orangepro_generate_tests` | Generate grounded tests for gaps |
150
- | `orangepro_prove` | Run mutation-kill oracle on a behavior |
151
- | `orangepro_prove_loop` | Setup commands + dynamic proof + report refresh for one behavior |
152
- | `orangepro_find_test_gaps` | List behaviors with weak/missing tests, ranked by risk |
153
- | `orangepro_graph_score` | Graph readiness score (0–100) |
154
- | `orangepro_status` | Workspace state without generating anything |
155
- | `orangepro_doctor` | Recommend next evidence to improve quality |
156
- | `orangepro_rtm` | Requirements traceability matrix |
157
- | `orangepro_stats` | Aggregate statistics |
158
- | `orangepro_changed_impact` | What a diff touches (requires git + base ref) |
159
- | `orangepro_record_run` | Record a test run result |
160
- | `orangepro_explain_test` | Explain why a test was generated |
161
- | `orangepro_export_evidence_pack` | Export metadata-only evidence pack |
162
- | `orangepro_update_graph` | Incremental graph update |
163
- | `orangepro_ai_links` | Weak behavior→symbol suggestions (optional AI) |
164
- | `orangepro_ai_flows` | Candidate flow discovery (optional AI) |
151
+ ## Works with
152
+
153
+ <p>
154
+ <strong>Claude Code</strong> · <strong>Cursor</strong> · <strong>GitHub Copilot</strong> · <strong>Codex</strong> · <strong>Windsurf</strong> · <strong>OpenCode</strong> · <strong>VS Code</strong>
155
+ </p>
156
+
157
+ Any MCP-compatible agent can drive OrangePro. No vendor lock-in.
158
+
159
+ ---
160
+
161
+ ## How it works
162
+
163
+ ```
164
+ ┌─────────────┐ ┌──────────────┐ ┌─────────────┐
165
+ │ Your Code │ ──► │ Knowledge │ ──► │ Evidence │
166
+ │ (any lang) │ │ Graph │ │ Tiers │
167
+ └─────────────┘ └──────────────┘ └─────────────┘
168
+
169
+ ┌──────┴──────┐
170
+ ▼ ▼
171
+ ┌───────────┐ ┌──────────┐
172
+ │ Gap Report│ │ Generate │
173
+ │ + Risks │ │ Tests │
174
+ └───────────┘ └──────────┘
175
+ ```
176
+
177
+ | Phase | What happens | Needs a model key? |
178
+ |-------|-------------|-------------------|
179
+ | **Analyze** | AST walk → behaviors, flows, evidence tiers | No |
180
+ | **Score** | Graph readiness score (0–100) | No |
181
+ | **Generate** | Grounded tests for top gaps | Yes (BYOK) |
182
+ | **Prove** | Mutation-kill oracle confirms test breaks if behavior changes | No |
183
+
184
+ Same code = same score. Deterministic. Always.
165
185
 
166
186
  ---
167
187
 
168
- ## CLI reference
188
+ ## Language support
189
+
190
+ | Language | Static mapping | Generated tests | Dynamic proof |
191
+ |----------|:-:|:-:|:-:|
192
+ | TypeScript / JavaScript | ✓ | ✓ Jest / Vitest / Mocha | ✓ |
193
+ | Python | ✓ | ✓ pytest | ✓ |
194
+ | Go | ✓ | ✓ `*_test.go` | ✓ |
195
+ | Java | ✓ | ✓ JUnit 4/5 | ✓ |
196
+ | Kotlin, Rust, PHP, C#, Ruby, Swift, C, C++ | ✓ | planned | planned |
197
+
198
+ Static mapping works across many languages via tree-sitter. Dynamic proof is deliberately narrower — each language needs a runner, mutation locator, and sandbox profile.
199
+
200
+ ---
201
+
202
+ ## Privacy
203
+
204
+ - **No stored source.** Reads code in-process. Never uploads to an OrangePro server.
205
+ - **No existing-source mutation.** Never edits your source or test files.
206
+ - **Your keys stay yours.** Read from env at call time, never persisted.
207
+ - **BYOK is direct.** Code context goes to the model provider you configure. OrangePro is not in that path.
208
+
209
+ ---
210
+
211
+ <details>
212
+ <summary><strong>CLI reference</strong></summary>
169
213
 
170
214
  ```bash
171
215
  opro # analyze + report + agent next actions
@@ -175,39 +219,66 @@ opro score # graph readiness (0–100)
175
219
  opro gaps --limit 10 # top 10 untested behaviors
176
220
  opro generate --base main # tests for PR diff
177
221
  opro generate --single # top gap, whole repo
178
- opro prove # mutation-kill oracle (use the prove_run args returned by generate)
222
+ opro prove # mutation-kill oracle
179
223
  opro rtm # traceability matrix
180
224
  opro export # metadata-only evidence pack
181
225
  opro mcp # run as MCP server (stdio)
182
226
  opro doctor # what evidence to add next
183
- opro doctor --proof # explain why dynamic proof could not close
184
227
  opro coverage # ingest runtime coverage
185
228
  ```
186
229
 
187
230
  Add `--json` to any read command for machine output. Run `opro help` for the full reference.
188
231
 
189
- ---
232
+ </details>
233
+
234
+ <details>
235
+ <summary><strong>MCP tools (18 total)</strong></summary>
236
+
237
+ | Tool | What it does |
238
+ |------|--------------|
239
+ | `orangepro_start` | One-command setup: analyze + report + next actions |
240
+ | `orangepro_analyze_sources` | Build/refresh the evidence graph |
241
+ | `orangepro_generate_tests` | Generate grounded tests for gaps |
242
+ | `orangepro_prove` | Run mutation-kill oracle on a behavior |
243
+ | `orangepro_prove_loop` | Setup + dynamic proof + report refresh for one behavior |
244
+ | `orangepro_find_test_gaps` | List behaviors with weak/missing tests, ranked by risk |
245
+ | `orangepro_graph_score` | Graph readiness score (0–100) |
246
+ | `orangepro_status` | Workspace state without generating anything |
247
+ | `orangepro_doctor` | Recommend next evidence to improve quality |
248
+ | `orangepro_rtm` | Requirements traceability matrix |
249
+ | `orangepro_stats` | Aggregate statistics |
250
+ | `orangepro_changed_impact` | What a diff touches (requires git + base ref) |
251
+ | `orangepro_record_run` | Record a test run result |
252
+ | `orangepro_explain_test` | Explain why a test was generated |
253
+ | `orangepro_export_evidence_pack` | Export metadata-only evidence pack |
254
+ | `orangepro_update_graph` | Incremental graph update |
255
+ | `orangepro_ai_links` | Weak behavior→symbol suggestions (optional AI) |
256
+ | `orangepro_ai_flows` | Candidate flow discovery (optional AI) |
257
+
258
+ </details>
190
259
 
191
- ## PR workflow
260
+ <details>
261
+ <summary><strong>PR workflow</strong></summary>
192
262
 
193
263
  ```bash
194
264
  opro generate --base main # tests for what this branch changed
195
- opro generate --pr 1234 # checks out PR #1234 — mutates your working tree; needs gh + confirmation (prefer --base)
265
+ opro generate --pr 1234 # checks out PR #1234
196
266
  opro generate --changed # current branch diff vs main
197
267
  ```
198
268
 
199
269
  Each generated test includes:
200
270
  - **Grounding** — the real files, symbols, and existing tests it cites
201
271
  - **Run hints** — where to write it, how to run it
202
- - **Scenario bucket + technique** — what failure mode it targets and how
272
+ - **Scenario bucket** — what failure mode it targets
203
273
 
204
- If the environment can't run tests yet (dependencies not installed, runner unconfigured), rejected drafts are kept as **Manual tests** — scenario, Given/When/Then steps, synthetic test data, and expected outcome in plain English, with the exact blocker named. Install dependencies and re-run `opro start` to turn them into runnable tests. Runnable tests always replace Manual tests for the same behavior; the two are never mixed.
274
+ If dependencies aren't installed, tests are kept as **Manual tests** (Given/When/Then steps with the blocker named). Install dependencies and re-run to convert them to runnable tests.
205
275
 
206
- ---
276
+ </details>
207
277
 
208
- ## Test categories
278
+ <details>
279
+ <summary><strong>Test categories</strong></summary>
209
280
 
210
- Generation is evidence-gated. A category is produced only when the graph has supporting evidence — never padded with generic filler. These are the local generation buckets. The report additionally shows each risk's **applicable testing categories** (contract, boundary limits, integration flow, state lifecycle, failure recovery, …), derived deterministically from graph facts. Categories with generated drafts are highlighted as drafts; they are not coverage or proof, and remaining applicable categories stay outlined. Neither taxonomy changes evidence tiers.
281
+ Generation is evidence-gated. A category is produced only when the graph has supporting evidence.
211
282
 
212
283
  | Category | What it targets |
213
284
  |----------|-----------------|
@@ -218,43 +289,10 @@ Generation is evidence-gated. A category is produced only when the graph has sup
218
289
  | Security / privacy | Auth, injection, data leakage |
219
290
  | Regression | Pinning a previously-broken behavior |
220
291
 
221
- ---
222
-
223
- ## Evidence tiers
224
-
225
- Every behavior gets exactly one tier. Nothing is labeled "tested" on faith.
226
-
227
- | Tier | What it means | How you get there |
228
- |------|---------------|------------------|
229
- | **Dynamically Proven** | A real test kills a targeted mutant of this behavior | `opro prove` after writing/running a test |
230
- | **Runtime-covered** | Coverage tool executed this code | `opro start --generate-coverage` |
231
- | **Statically Linked** | A test **imports and calls** this code — a hard structural link | Automatic during analysis |
232
- | **Unconfirmed Candidate** | A lexically similar test file exists, but nothing links it — a lead, **not evidence** | Automatic; upgrade it by writing the linking test |
233
- | **No Signal** | Nothing tests this behavior yet | — |
234
-
235
- > **"Dynamically Proven 0" is normal on first run.** Static analysis always runs. Dynamic proof requires running tests against targeted mutations. That's the trust model — nothing is Dynamically Proven until a real test kills a real mutant.
292
+ </details>
236
293
 
237
- When runtime coverage is available, `opro start` also compares Runtime-covered and Dynamically Proven behaviors over the same deterministic denominator. It never compares source-line coverage with behavior proof or folds off-denominator proofs into that percentage.
238
-
239
- ---
240
-
241
- ## Language support
242
-
243
- OrangePro separates static mapping, generated tests, runtime coverage, and dynamic proof. Those are different confidence bars.
244
-
245
- | Language | Static behavior extraction | Generated tests | Runtime coverage | Dynamic proof |
246
- |----------|:--------------------------:|:---------------:|:----------------:|:-------------:|
247
- | TypeScript / JavaScript | ✓ | ✓ Jest / Vitest / Mocha / AVA-style drafts | ✓ lcov.info | ✓ Vitest / Jest / Mocha |
248
- | Python | ✓ | ✓ pytest | ✓ coverage.py / pytest-cov XML | ✓ pytest |
249
- | Go | ✓ | ✓ same-package `*_test.go` | ✓ coverprofile | ✓ `go test` |
250
- | Java | ✓ | ✓ JUnit 4/5 | ✓ JaCoCo XML | ✓ Maven/JUnit |
251
- | Kotlin, Rust, PHP, C#, Ruby, Swift, C, C++ | ✓ static behavior extraction | planned | planned where standard coverage exists | planned proof profiles |
252
-
253
- Static mapping works across many languages through tree-sitter and repo metadata. Dynamic proof is deliberately narrower: each language needs a runner, mutation locator, sandbox profile, and false-proof regressions before it can mint Dynamically Proven.
254
-
255
- ---
256
-
257
- ## Model setup (BYOK)
294
+ <details>
295
+ <summary><strong>Model setup (BYOK)</strong></summary>
258
296
 
259
297
  Analysis, scoring, and proof need no model key. Generation does.
260
298
 
@@ -268,56 +306,20 @@ Auto-detect order: OpenAI → Ollama → Anthropic. Override with `--provider` a
268
306
 
269
307
  Run `opro setup` to configure interactively. Keys stay in your environment — never written to graph, config, or artifacts.
270
308
 
271
- ---
309
+ </details>
272
310
 
273
- ## AI candidate lanes
311
+ <details>
312
+ <summary><strong>AI candidate lanes</strong></summary>
274
313
 
275
- With a provider key, OrangePro can stage weak AI behavior→symbol links and AI-suggested candidate flows. These are ready for local use as review/generation worklists, but they are not evidence:
314
+ With a provider key, OrangePro stages weak AI behavior→symbol links and AI-suggested candidate flows. These are review/generation worklists, not evidence:
276
315
 
277
316
  - AI links appear as `AI-linked` suggestions.
278
317
  - AI flows are stored separately from deterministic flows.
279
- - Neither lane changes Dynamically Proven, Runtime-covered, Statically Linked, denominator counts, or evidence tiers.
318
+ - Neither lane changes evidence tiers or denominator counts.
280
319
 
281
- Use them when you want the agent to find likely service-boundary flows faster; ignore them when you want a deterministic-only report.
320
+ Use them when you want the agent to find likely service-boundary flows faster; ignore them for a deterministic-only report.
282
321
 
283
- ---
284
-
285
- ## How it works
286
-
287
- OrangePro separates **analysis** (what your code does) from **proof** (whether tests actually verify it).
288
-
289
- ```
290
- ┌─────────────┐ ┌──────────────┐ ┌─────────────┐
291
- │ Your Code │ ──► │ Knowledge │ ──► │ Evidence │
292
- │ (any lang) │ │ Graph │ │ Tiers │
293
- └─────────────┘ └──────────────┘ └─────────────┘
294
-
295
- ┌──────┴──────┐
296
- ▼ ▼
297
- ┌───────────┐ ┌──────────┐
298
- │ Gap Report│ │ Generate │
299
- │ + Risks │ │ Tests │
300
- └───────────┘ └──────────┘
301
- ```
302
-
303
- | Phase | What happens | Needs a model key? |
304
- |-------|-------------|-------------------|
305
- | **Analyze** | AST walk → behaviors, flows, evidence tiers | No |
306
- | **Score** | Graph readiness score (0–100) with reasons | No |
307
- | **Generate** | Grounded tests for top gaps, per-behavior | Yes (BYOK) |
308
- | **Prove** | Mutation-kill oracle confirms test actually breaks if behavior changes | No |
309
-
310
- Reruns are cache-accelerated: unchanged files skip re-parsing, BYOK stages don't re-spend tokens on unchanged inputs, and proof certificates persist in a local ledger until the certified file changes. Upgrading the tool auto-invalidates caches.
311
-
312
- ---
313
-
314
- ## Privacy
315
-
316
- - **No stored source.** Reads code in-process. Never uploads to an OrangePro server.
317
- - **No existing-source mutation.** Never edits existing source or test files. Writes metadata to `.orangepro/`; keyed auto-drive may write new, reviewable tests under `orangepro_generated/`.
318
- - **Metadata-only exports.** File paths, names, hashes, scores — not raw source.
319
- - **Your keys stay yours.** Read from env at call time, never persisted.
320
- - **BYOK is direct.** When AI lanes are enabled, grounded code context is sent directly to the model provider you configure; OrangePro's hosted service is not in that path.
322
+ </details>
321
323
 
322
324
  ---
323
325
 
@@ -326,25 +328,26 @@ Reruns are cache-accelerated: unchanged files skip re-parsing, BYOK stages don't
326
328
  This repo is the free local tool. The [OrangePro platform](https://orangepro.ai) adds:
327
329
 
328
330
  - Persistent knowledge graph across PRs and repos
329
- - Managed dynamic proof at scale (larger budgets, CI workers, service setup profiles)
330
- - PR/CI policy gates over Dynamically Proven, Runtime-covered, and risk deltas
331
+ - PR/CI policy gates over evidence tiers and risk deltas
331
332
  - Jira / Confluence / TestRail / OpenAPI enrichment
332
333
  - Cross-repo intelligence and recurring-flow memory
333
334
  - Production incident correlation and regression targeting
334
- - Full test lifecycle management and team dashboards
335
+ - Team dashboards and test lifecycle management
335
336
 
336
337
  ---
337
338
 
338
339
  ## Contributing
339
340
 
340
341
  ```bash
341
- npm run build # compile to dist/
342
- npm test # vitest
343
- npm run typecheck # type check without emitting
342
+ git clone https://github.com/OrangeproAI/orangepro-mcp.git
343
+ cd orangepro-mcp && npm ci && npm run build
344
+ npm test
344
345
  ```
345
346
 
346
- See [docs/local-proof-kit.md](docs/local-proof-kit.md) for the full development reference.
347
+ PRs welcome. Please open an issue first for large changes.
347
348
 
348
- ## License
349
+ ---
349
350
 
350
- [MIT](LICENSE) © OrangePro
351
+ <p align="center">
352
+ MIT License · <a href="https://orangepro.ai">orangepro.ai</a>
353
+ </p>
@@ -26,7 +26,10 @@ const SYMBOL_EXCERPT_CONTEXT_LINES = 3;
26
26
  const MAX_EXCERPT_CHARS = 8000;
27
27
  const MAX_TARGET_TYPE_EXCERPT_CHARS = 5000;
28
28
  const STATIC_CHECK_TIMEOUT_MS = 3000;
29
- const GO_COMPILE_CHECK_TIMEOUT_MS = 20000;
29
+ // Large Go monorepos can spend tens of seconds populating a cold module cache
30
+ // before the target package is compiled. Keep the check authoritative instead
31
+ // of downgrading valid generated tests while dependencies are still downloading.
32
+ const GO_COMPILE_CHECK_TIMEOUT_MS = 120000;
30
33
  /** A source ref that names a test file (used to place a generated test next to it). */
31
34
  const TEST_REF_RE = /(\.(test|spec)\.[cm]?[jt]sx?$)|((^|\/)test\.[cm]?[jt]sx?$)|(_test\.[a-z]+$)|(_spec\.[a-z]+$)|((^|\/)test_[^/]+\.[a-z]+$)/i;
32
35
  function areaOf(relPath) {
@@ -518,6 +521,9 @@ export function gatherContext(graph, behavior, framework, fileReader) {
518
521
  // Subject imports are TS/JS lines — feeding them to a pytest/go target would
519
522
  // produce an unparseable test in the other language.
520
523
  const fwLower = framework.toLowerCase();
524
+ const goContext = fwLower.includes("go")
525
+ ? goGenerationImportContext(graph.workspace.root, [...relatedFiles, ...testFiles], fileReader)
526
+ : { go_import_paths: [], go_module_paths: [], go_target_package_paths: [] };
521
527
  const subjectImports = fwLower.includes("pytest") || fwLower.includes("python") || fwLower.includes("go") || fwLower.includes("junit") || fwLower.includes("java")
522
528
  ? []
523
529
  : subjectImportsFor(graph, testFiles);
@@ -561,6 +567,9 @@ export function gatherContext(graph, behavior, framework, fileReader) {
561
567
  // MISSING, never a re-derivation of a test that already exists.
562
568
  existing_tests: examples.slice(0, 10),
563
569
  subject_imports: subjectImports,
570
+ ...(goContext.go_import_paths.length ? { go_import_paths: goContext.go_import_paths } : {}),
571
+ ...(goContext.go_module_paths.length ? { go_module_paths: goContext.go_module_paths } : {}),
572
+ ...(goContext.go_target_package_paths.length ? { go_target_package_paths: goContext.go_target_package_paths } : {}),
564
573
  ...(flowChain ? { flow_chain: flowChain } : {})
565
574
  };
566
575
  // entity_ids are graph external_ids: the behavior plus related file paths. A
@@ -1578,10 +1587,6 @@ function goStaticIssue(body) {
1578
1587
  if (!/\bfunc\s+Test[A-Za-z0-9_]*\s*\(\s*t\s+\*testing\.T\s*\)/m.test(body)) {
1579
1588
  return "Go test is missing a func Test...(t *testing.T) entrypoint.";
1580
1589
  }
1581
- const external = imports.filter((spec) => spec.split("/")[0].includes("."));
1582
- if (external.length) {
1583
- return `Go test imports module-path package(s) ${external.join(", ")}; OrangePro cannot verify module imports resolve from the generated file. Prefer same-package or stdlib-only code.`;
1584
- }
1585
1590
  if (!commandAvailable("gofmt"))
1586
1591
  return "gofmt not found; cannot verify Go syntax.";
1587
1592
  const { dir, file } = writeTempStaticFile("go", body);
@@ -1611,26 +1616,58 @@ function findGoModuleRoot(startDir, workspaceRoot) {
1611
1616
  }
1612
1617
  return null;
1613
1618
  }
1614
- function goCompileIssue(body, workspaceRoot, relatedFiles) {
1619
+ function unusedImportLineNumbers(output, tempRel) {
1620
+ const escaped = tempRel.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1621
+ const diagnostic = new RegExp(`(?:^|\\n)(?:\\./)?${escaped}:(\\d+):\\d+:\\s+[^\\n]*\\bimported\\b[^\\n]*\\bnot used\\b`, "g");
1622
+ return new Set([...output.matchAll(diagnostic)].map((match) => Number(match[1])).filter(Number.isFinite));
1623
+ }
1624
+ function removeLines(body, lineNumbers) {
1625
+ if (!lineNumbers.size)
1626
+ return body;
1627
+ return body
1628
+ .split(/\r?\n/)
1629
+ .filter((_line, index) => !lineNumbers.has(index + 1))
1630
+ .join("\n");
1631
+ }
1632
+ function goCompile(body, workspaceRoot, relatedFiles) {
1633
+ const externalImports = goImportSpecs(body).filter((spec) => spec.split("/")[0].includes("."));
1634
+ const unverifiedImportIssue = externalImports.length
1635
+ ? `Go test imports module-path package(s) ${externalImports.join(", ")}, but OrangePro could not run the target-package compile check to verify they resolve.`
1636
+ : null;
1615
1637
  const sourceFile = relatedFiles.find((rel) => /\.go$/i.test(rel) && !TEST_REF_RE.test(rel));
1616
1638
  if (!sourceFile)
1617
- return null;
1639
+ return { body, issue: unverifiedImportIssue };
1618
1640
  const packageDir = path.resolve(workspaceRoot, path.dirname(sourceFile));
1619
1641
  if (!existsSync(packageDir) || !findGoModuleRoot(packageDir, workspaceRoot))
1620
- return null;
1642
+ return { body, issue: unverifiedImportIssue };
1621
1643
  if (!commandAvailable("go"))
1622
- return "go not found; cannot verify generated Go test compiles.";
1644
+ return { body, issue: "go not found; cannot verify generated Go test compiles." };
1623
1645
  const tempRel = `orangepro_compile_${process.pid}_${shortHash(body)}_test.go`;
1624
1646
  const tempFile = path.join(packageDir, tempRel);
1647
+ let checkedBody = body;
1625
1648
  try {
1626
- writeFileSync(tempFile, body);
1649
+ writeFileSync(tempFile, checkedBody);
1627
1650
  execFileSync("go", ["test", "-run", "^$", "."], { cwd: packageDir, stdio: "pipe", timeout: GO_COMPILE_CHECK_TIMEOUT_MS });
1628
- return null;
1651
+ return { body: checkedBody, issue: null };
1629
1652
  }
1630
1653
  catch (e) {
1631
1654
  const err = e;
1632
1655
  const output = `${err.stdout?.toString() ?? ""}${err.stderr?.toString() ?? ""}`;
1633
- return `Go compile check failed: ${shortStaticDiag(output || err.message || "unknown error")}`;
1656
+ const withoutUnusedImports = removeLines(checkedBody, unusedImportLineNumbers(output, tempRel));
1657
+ if (withoutUnusedImports !== checkedBody) {
1658
+ checkedBody = withoutUnusedImports;
1659
+ try {
1660
+ writeFileSync(tempFile, checkedBody);
1661
+ execFileSync("go", ["test", "-run", "^$", "."], { cwd: packageDir, stdio: "pipe", timeout: GO_COMPILE_CHECK_TIMEOUT_MS });
1662
+ return { body: checkedBody, issue: null };
1663
+ }
1664
+ catch (retryError) {
1665
+ const retry = retryError;
1666
+ const retryOutput = `${retry.stdout?.toString() ?? ""}${retry.stderr?.toString() ?? ""}`;
1667
+ return { body: checkedBody, issue: `Go compile check failed: ${shortStaticDiag(retryOutput || retry.message || "unknown error")}` };
1668
+ }
1669
+ }
1670
+ return { body: checkedBody, issue: `Go compile check failed: ${shortStaticDiag(output || err.message || "unknown error")}` };
1634
1671
  }
1635
1672
  finally {
1636
1673
  rmSync(tempFile, { force: true });
@@ -1659,6 +1696,37 @@ function goImportSpecs(body) {
1659
1696
  }
1660
1697
  return specs;
1661
1698
  }
1699
+ function goGenerationImportContext(workspaceRoot, files, fileReader) {
1700
+ const goFiles = files.filter((file) => /\.go$/i.test(file));
1701
+ const moduleRoots = dedupe(goFiles
1702
+ .map((file) => findGoModuleRoot(path.resolve(workspaceRoot, path.dirname(file)), workspaceRoot))
1703
+ .filter((root) => Boolean(root)));
1704
+ const modulePathByRoot = new Map(moduleRoots.map((root) => {
1705
+ try {
1706
+ return [root, readFileSync(path.join(root, "go.mod"), "utf8").match(/^\s*module\s+(\S+)/m)?.[1] ?? ""];
1707
+ }
1708
+ catch {
1709
+ return [root, ""];
1710
+ }
1711
+ }));
1712
+ const go_module_paths = dedupe([...modulePathByRoot.values()].filter(Boolean));
1713
+ const go_target_package_paths = dedupe(goFiles
1714
+ .map((file) => {
1715
+ const sourceDir = path.resolve(workspaceRoot, path.dirname(file));
1716
+ const moduleRoot = findGoModuleRoot(sourceDir, workspaceRoot);
1717
+ const modulePath = moduleRoot ? modulePathByRoot.get(moduleRoot) : "";
1718
+ if (!moduleRoot || !modulePath)
1719
+ return "";
1720
+ const packageDir = path.relative(moduleRoot, sourceDir).split(path.sep).join("/");
1721
+ return packageDir && packageDir !== "." ? `${modulePath}/${packageDir}` : modulePath;
1722
+ })
1723
+ .filter(Boolean));
1724
+ const targetPaths = new Set(go_target_package_paths);
1725
+ const go_import_paths = dedupe(goFiles
1726
+ .flatMap((file) => goImportSpecs(fileReader(file) ?? ""))
1727
+ .filter((spec) => spec.split("/")[0].includes(".") && !targetPaths.has(spec))).slice(0, 40);
1728
+ return { go_import_paths, go_module_paths, go_target_package_paths };
1729
+ }
1662
1730
  function hasBalancedBraces(body) {
1663
1731
  let depth = 0;
1664
1732
  let stringQuote = null;
@@ -2176,8 +2244,13 @@ export async function generateTests(graph, opts, provider, fileReader, clock = s
2176
2244
  body = applyPythonSrcLayoutImports(body, relatedFiles);
2177
2245
  }
2178
2246
  body = ensureFrameworkScaffold(body, framework);
2247
+ let compileIssue = null;
2248
+ if (framework.toLowerCase().includes("go")) {
2249
+ const checked = goCompile(body, graph.workspace.root, relatedFiles);
2250
+ body = checked.body;
2251
+ compileIssue = checked.issue;
2252
+ }
2179
2253
  const staticIssue = staticFormatIssue(body, framework);
2180
- const compileIssue = framework.toLowerCase().includes("go") ? goCompileIssue(body, graph.workspace.root, relatedFiles) : null;
2181
2254
  const runnable = hasAssertion(body, framework) && !staticIssue && !compileIssue;
2182
2255
  generated.push({
2183
2256
  id: `${run_id}-t${generated.length + 1}`,
@@ -2433,7 +2506,12 @@ export async function generateTests(graph, opts, provider, fileReader, clock = s
2433
2506
  }
2434
2507
  }
2435
2508
  const importErrors = isResolverFramework(framework) ? unresolvedLocalImports(body, genTestAbs, graph.workspace.root, declaredDeps) : [];
2436
- const compileIssue = framework.toLowerCase().includes("go") ? goCompileIssue(body, graph.workspace.root, relatedFiles) : null;
2509
+ let compileIssue = null;
2510
+ if (framework.toLowerCase().includes("go")) {
2511
+ const checked = goCompile(body, graph.workspace.root, relatedFiles);
2512
+ body = checked.body;
2513
+ compileIssue = checked.issue;
2514
+ }
2437
2515
  const runnable = isRunnable(body, framework, import_provenance, importErrors) && !compileIssue;
2438
2516
  if (!runnable) {
2439
2517
  const reason = unresolved_reason ?? compileIssue ?? runnableFailureReason(body, framework, import_provenance, importErrors, declaredDeps);
@@ -2651,7 +2729,12 @@ export async function generateTests(graph, opts, provider, fileReader, clock = s
2651
2729
  // from where the test will live. A test whose own import won't load is never
2652
2730
  // marked runnable.
2653
2731
  const importErrors = isResolverFramework(framework) ? unresolvedLocalImports(body, genTestAbs, graph.workspace.root, declaredDeps) : [];
2654
- const compileIssue = framework.toLowerCase().includes("go") ? goCompileIssue(body, graph.workspace.root, relatedFiles) : null;
2732
+ let compileIssue = null;
2733
+ if (framework.toLowerCase().includes("go")) {
2734
+ const checked = goCompile(body, graph.workspace.root, relatedFiles);
2735
+ body = checked.body;
2736
+ compileIssue = checked.issue;
2737
+ }
2655
2738
  const runnable = isRunnable(body, framework, import_provenance, importErrors) && !compileIssue;
2656
2739
  if (!runnable && !unresolved_reason) {
2657
2740
  unresolved_reason = compileIssue ?? runnableFailureReason(body, framework, import_provenance, importErrors, declaredDeps);
@@ -32,8 +32,10 @@ export function buildSystemPrompt() {
32
32
  "- Framework format rules:",
33
33
  " - pytest: output a valid Python file with pytest-style `def test_...` functions and `assert` statements.",
34
34
  " - Go: output a same-package `_test.go` body. Include `package <same package>`, `import \"testing\"`,",
35
- " and `func Test...(t *testing.T)`. Avoid third-party or module-path imports; use stdlib-only unless",
36
- " the evidence gives an existing same-package helper.",
35
+ " and `func Test...(t *testing.T)`. Do not import packages you do not use. Prefer stdlib and exact",
36
+ " OBSERVED GO IMPORT PATHS; never derive or invent a package subpath from a module name.",
37
+ " Do not qualify lowercase (unexported) identifiers from imported packages. Bare same-package",
38
+ " identifiers may be unexported because the generated test is rewritten into the target package.",
37
39
  " - Java/JUnit: output a complete `.java` file, not a method fragment. Include a `class <Name>Test { ... }`,",
38
40
  " the requested JUnit version's `@Test` import, and a JUnit assertion.",
39
41
  " - TS/JS: output valid framework code for the named framework and use complete imports.",
@@ -78,6 +80,21 @@ export function buildGroundedUserPrompt(ctx, bucket) {
78
80
  for (const imp of ctx.subject_imports)
79
81
  lines.push(imp);
80
82
  }
83
+ if (ctx.go_module_paths?.length) {
84
+ lines.push("GO MODULE IDENTITIES (module roots only — never guess package subpaths from these):");
85
+ for (const modulePath of ctx.go_module_paths)
86
+ lines.push(`- ${modulePath}`);
87
+ }
88
+ if (ctx.go_import_paths?.length) {
89
+ lines.push("OBSERVED GO IMPORT PATHS (exact repo-evidenced package paths; prefer these for non-stdlib imports):");
90
+ for (const importPath of ctx.go_import_paths)
91
+ lines.push(`- ${importPath}`);
92
+ }
93
+ if (ctx.go_target_package_paths?.length) {
94
+ lines.push("TARGET GO PACKAGE PATHS (do not import these; the test is rewritten into the same package):");
95
+ for (const targetPath of ctx.go_target_package_paths)
96
+ lines.push(`- ${targetPath}`);
97
+ }
81
98
  if (ctx.source_excerpts.length) {
82
99
  lines.push("SOURCE EXCERPTS:");
83
100
  lines.push("Use these for understanding only; do not copy their lines verbatim into the test body.");
@@ -97,8 +114,10 @@ export function buildGroundedUserPrompt(ctx, bucket) {
97
114
  }
98
115
  else if (fw.includes("go")) {
99
116
  lines.push("- Emit same-package Go test code only: `package ...`, `import \"testing\"`, and `func Test...(t *testing.T)`.");
100
- lines.push("- Do not import third-party or module-path packages such as `github.com/...`; prefer stdlib-only tests.");
101
- lines.push("- Do not use testify/assert/require unless an existing same-package helper in the evidence clearly requires it.");
117
+ lines.push("- Do not import packages you do not use.");
118
+ lines.push("- Prefer stdlib and exact OBSERVED GO IMPORT PATHS. Never invent a package path or derive a subpath from a GO MODULE IDENTITY.");
119
+ lines.push("- Never import a TARGET GO PACKAGE PATH; call its identifiers directly because this is a same-package test.");
120
+ lines.push("- Do not qualify lowercase (unexported) identifiers from imported packages; bare same-package identifiers may be unexported.");
102
121
  }
103
122
  else if (fw.includes("junit") || fw.includes("java")) {
104
123
  if (fw.includes("junit4")) {
@@ -33,7 +33,15 @@ export function getFrameworkRules(framework) {
33
33
  return "Python: `def test_...` with `assert`. Use `# Concern:` and `# Technique:` comments.";
34
34
  }
35
35
  if (fw.includes("go")) {
36
- return "Go: same-package `_test.go`, `func Test...(t *testing.T)`. Stdlib preferred.";
36
+ return [
37
+ "Go: same-package `_test.go` file. `func Test...(t *testing.T)`.",
38
+ "MUST start with `package <name>` matching the package under test.",
39
+ "MUST import `\"testing\"`; do not import any package you do not use.",
40
+ "Prefer stdlib and exact OBSERVED GO IMPORT PATHS. Never invent a package path or derive a subpath from a GO MODULE IDENTITY.",
41
+ "Never import a TARGET GO PACKAGE PATH; call its identifiers directly because this is a same-package test.",
42
+ "Do not qualify lowercase (unexported) identifiers from imported packages; bare same-package identifiers may be unexported.",
43
+ "Prefer stdlib `testing` over testify unless testify appears in OBSERVED GO IMPORT PATHS."
44
+ ].join(" ");
37
45
  }
38
46
  if (fw.includes("junit4") || fw.includes("java4")) {
39
47
  return "JUnit 4: `import org.junit.Test;` + `import static org.junit.Assert.*;`. Complete .java file.";
@@ -179,6 +187,21 @@ export function buildBatchGenerationUserPromptV5(ctx) {
179
187
  lines.push(`ACTORS: ${ctx.actors.join(", ")}`);
180
188
  lines.push(`FRAMEWORK: ${ctx.framework} | LAYER: ${ctx.test_layer}`);
181
189
  lines.push(`FRAMEWORK RULES: ${getFrameworkRules(ctx.framework)}`);
190
+ if (ctx.go_module_paths?.length) {
191
+ lines.push("GO MODULE IDENTITIES (module roots only — never guess package subpaths from these):");
192
+ for (const modulePath of ctx.go_module_paths)
193
+ lines.push(` ${modulePath}`);
194
+ }
195
+ if (ctx.go_import_paths?.length) {
196
+ lines.push("OBSERVED GO IMPORT PATHS (exact repo-evidenced package paths):");
197
+ for (const importPath of ctx.go_import_paths)
198
+ lines.push(` ${importPath}`);
199
+ }
200
+ if (ctx.go_target_package_paths?.length) {
201
+ lines.push("TARGET GO PACKAGE PATHS (do not import; this is a same-package test):");
202
+ for (const targetPath of ctx.go_target_package_paths)
203
+ lines.push(` ${targetPath}`);
204
+ }
182
205
  lines.push("");
183
206
  if (ctx.flow_chain?.length) {
184
207
  lines.push("FLOW CHAIN:");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orangepro/orangepro-mcp",
3
- "version": "0.2.29",
3
+ "version": "0.2.30",
4
4
  "private": false,
5
5
  "description": "OrangePro (`opro`) — a local-first, BYOK CLI + MCP server that builds an evidence graph from a local checkout, ingests runtime coverage, and generates grounded tests. Metadata-only exports; no source upload; generated tests stay local.",
6
6
  "license": "MIT",