@hecer/yoke 1.13.0 → 1.15.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/CHANGELOG.md +39 -0
- package/README.md +16 -12
- package/canon/manifest.yaml +1 -1
- package/dist/cli.js +16 -2
- package/dist/code-intelligence/adapters/graft.js +11 -0
- package/dist/code-intelligence/adapters/graphify.js +8 -0
- package/dist/code-intelligence/adapters/index.js +5 -0
- package/dist/code-intelligence/adapters/mcp.js +37 -0
- package/dist/code-intelligence/adapters/serena.js +11 -0
- package/dist/code-intelligence/adapters/types.js +1 -0
- package/dist/code-intelligence/contracts.js +137 -0
- package/dist/code-intelligence/coordinator.js +370 -0
- package/dist/code-intelligence/edit-plans.js +53 -0
- package/dist/code-intelligence/evidence.js +77 -0
- package/dist/code-intelligence/index.js +5 -0
- package/dist/code-intelligence/internal-types.js +1 -0
- package/dist/code-intelligence/mcp-client.js +139 -0
- package/dist/code-intelligence/mcp-server.js +93 -0
- package/dist/code-intelligence/snapshots.js +117 -0
- package/dist/code-intelligence/transactions.js +60 -0
- package/dist/dashboard/analytics.js +193 -29
- package/dist/dashboard/contracts.js +23 -0
- package/dist/dashboard/page.js +39 -94
- package/dist/dashboard/panels.js +85 -31
- package/dist/dashboard/server.js +190 -15
- package/dist/observability/events.js +1 -1
- package/dist/observability/history.js +1 -1
- package/dist/retrofit/command.js +3 -1
- package/dist/retrofit/config.js +10 -0
- package/dist/retrofit/gitignore.js +1 -0
- package/dist/retrofit/plan.js +3 -3
- package/dist/retrofit/planners/claude.js +3 -3
- package/dist/retrofit/planners/codex.js +5 -5
- package/dist/retrofit/planners/gemini.js +2 -2
- package/dist/retrofit/planners/kilo.js +2 -2
- package/dist/retrofit/planners/opencode.js +2 -2
- package/dist/retrofit/planners/pi.js +1 -1
- package/dist/retrofit/planners/qwen.js +2 -2
- package/dist/retrofit/tools.js +9 -7
- package/dist/setup/command.js +3 -1
- package/docs/CODE-INTELLIGENCE.md +36 -0
- package/docs/DASHBOARD-EVOLUTION.md +16 -2
- package/docs/DASHBOARD-OVERHAUL.md +146 -0
- package/gemini-extension.json +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,44 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.15.0 — 2026-09-09
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- Add opt-in federated Code Intelligence that composes Graft, Graphify and Serena behind one Yoke-controlled MCP facade with six stable tools for context, symbols, traces, impact and edit workflows.
|
|
7
|
+
- Add pinned backend adapters, explicit coverage/provenance/freshness evidence, content-addressed workspace snapshots and bounded local policy checks.
|
|
8
|
+
- Add isolated edit previews and guarded apply transactions with approval, snapshot freshness, exclusive locking and idempotency checks; Yoke's existing review, verify and commit gates remain authoritative.
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
- Add `off`, `shadow` and `active` Code Intelligence modes to setup and retrofit. `off` preserves the legacy `codeGraph` path unchanged; `shadow` is read-only; `active` enables preview and approved edits.
|
|
12
|
+
- Keep backend runtimes external and configurable instead of vendoring or silently installing them. Pin the validated integration targets to Graft `0.17.0`, Graphify `0.9.56` and Serena `1.7.1-dev`.
|
|
13
|
+
- Add the [Code Intelligence guide](docs/CODE-INTELLIGENCE.md), including setup, backend requirements, safety boundaries, limitations and the Pi integration note.
|
|
14
|
+
|
|
15
|
+
### Migration and validation limits
|
|
16
|
+
- No migration is required. Existing projects remain on the legacy path until `yoke setup` or `yoke retrofit` is run with `--code-intelligence=shadow` or `--code-intelligence=active`.
|
|
17
|
+
- Validated with the Code Intelligence contract/coordinator/snapshot/MCP tests, TypeScript lint/build, documentation metadata, package dry run and the facade MCP handshake. The full suite retains one pre-existing provider-process timing failure; it is reproduced independently and is not caused by this release.
|
|
18
|
+
- This release does not claim that every language or backend is available in every environment. Backend failures are surfaced as partial coverage or an explicit unavailable capability, never silently treated as complete evidence.
|
|
19
|
+
|
|
20
|
+
## 1.14.0 — 2026-09-09
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
- Add a local-first workspace control room that ranks registered projects by attention, activity, recorded tokens, reported cost, acceptance, or name, with composable search, status filters, UTC scopes, and restorable view links.
|
|
24
|
+
- Add workspace and project analytics with time buckets, token/call/duration/outcome summaries, provider/model/agent/role/phase/run rankings, usage comparisons, and visible measurement coverage.
|
|
25
|
+
- Add project live operations for task and phase state, worker metadata, bounded event timelines, safe-boundary pause/resume, append-only operator notes, and queued change requests.
|
|
26
|
+
- Add a responsive dashboard shell with dark/light themes, keyboard and reduced-motion support, explicit loading/empty/error/stale states, and readable narrow-screen navigation.
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
- Base dashboard views on durable local history and versioned events while keeping unknown, partial, corrupt, unavailable, and stale telemetry explicit instead of treating it as zero.
|
|
30
|
+
- Keep dashboard controls on the existing loop/goal runner and lock boundaries; browser requests remain typed, same-origin, loopback-only, and unable to execute arbitrary shell commands.
|
|
31
|
+
|
|
32
|
+
### Fixed
|
|
33
|
+
- Scope dashboard loop verification to the intended local project and retain focused coverage for dashboard authorization, path safety, partial history, concurrency, navigation, and control behavior.
|
|
34
|
+
- Improve light-theme contrast for the active navigation state and keep long project names inside desktop and mobile navigation areas.
|
|
35
|
+
|
|
36
|
+
### Migration and validation limits
|
|
37
|
+
- No migration is required. Start the local view with `yoke dashboard --no-register`, then register projects with `yoke projects add <path>` as needed. Existing dashboard settings remain local and authoritative.
|
|
38
|
+
- Validated with the dashboard tests, TypeScript lint/build, canonical manifest validation, release metadata checks, package dry run, and real local browser screenshots at desktop and mobile sizes.
|
|
39
|
+
- Dashboard data is limited to explicitly registered local projects and retained local telemetry. It does not provide remote multi-user access, reconstruct missing history, prove requested models were used, or execute arbitrary browser-supplied commands. Pause/resume operates only at existing safe loop/goal boundaries.
|
|
40
|
+
- This release targets npm package 1.14.0; npm publication is triggered by the matching published GitHub release and verified separately.
|
|
41
|
+
|
|
3
42
|
## 1.13.0 — 2026-09-08
|
|
4
43
|
|
|
5
44
|
### Added
|
package/README.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
|
-
<h1><img src="https://raw.githubusercontent.com/HECer/yoke/v1.
|
|
3
|
+
<h1><img src="https://raw.githubusercontent.com/HECer/yoke/v1.15.0/docs/assets/yoke-logo.png" alt="Yoke" width="100" height="63"></h1>
|
|
4
4
|
|
|
5
|
-
<!-- yoke:version:start -->1.
|
|
6
|
-
<!-- yoke:tests:start -->
|
|
5
|
+
<!-- yoke:version:start -->1.15.0<!-- yoke:version:end -->
|
|
6
|
+
<!-- yoke:tests:start -->1268<!-- yoke:tests:end -->
|
|
7
7
|
<!-- yoke:skills:start -->34<!-- yoke:skills:end -->
|
|
8
8
|
<!-- yoke:agents:start -->Claude | Codex | Gemini | Qwen | OpenCode | Kilo | Pi<!-- yoke:agents:end -->
|
|
9
9
|
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
[](#-license)
|
|
18
18
|

|
|
19
19
|

|
|
20
|
-

|
|
21
21
|

|
|
22
22
|

|
|
23
23
|
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
|
|
28
28
|
> **TL;DR** — `yoke setup .` asks six questions and installs the native harness for your agent. `yoke new my-app --idea="..."` bootstraps a project and drafts its story backlog. `yoke loop run my-app --isolate --review` then implements it behind hard gates: **clean tree → acceptance criteria → your real tests green → an independent model approves → commit**. Add `--parallel=N` for dependency-aware workers, or declare a reference and add `--quality` for a bounded critic/repair gauntlet. If any blocking gate is red, nothing is committed. Proof lives in `.yoke/proof/<story>/`.
|
|
29
29
|
|
|
30
|
-
**New in 1.
|
|
30
|
+
**New in 1.15.0:** federated [Code Intelligence](docs/CODE-INTELLIGENCE.md) composes Graft, Graphify and Serena behind one Yoke-controlled MCP surface, with structural and semantic evidence, content-addressed snapshots, partial-coverage reporting and isolated edit previews. It is opt-in: use `off` for the unchanged legacy path, `shadow` for read-only canaries, or `active` for previews and approved edits. The [1.14.0 dashboard overhaul](docs/DASHBOARD-OVERHAUL.md) and first-class [OpenCode, Kilo and Pi integrations](docs/HARNESSES.md) remain available. See the [changelog](CHANGELOG.md) and [Code Intelligence guide](docs/CODE-INTELLIGENCE.md) for setup and limitations.
|
|
31
31
|
|
|
32
32
|
OpenCode, Kilo and Pi are real CLI integrations, not bundled runtimes or credentials. OpenCode/Kilo use their JSON headless modes and local MCP configuration; Pi uses JSONL and explicit tool allowlists, but has no native MCP, sub-agent or plan layer. Read the [integration guide](docs/HARNESSES.md) before selecting a permission profile.
|
|
33
33
|
|
|
@@ -40,11 +40,11 @@ yoke projects add /path/to/backend
|
|
|
40
40
|
yoke dashboard --no-register
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
-
Open the printed `http://127.0.0.1:...` URL. Each registered project has its own goals, tasks and evidence. The dashboard shows
|
|
43
|
+
Open the printed `http://127.0.0.1:...` URL. Each registered project has its own goals, tasks and evidence. The dashboard is a local control room with an overview/ranking screen, project live view, bounded **History explorer**, and Workspace analytics by UTC time bucket. It shows worker state, agent/provider/model/variant/role/phase metadata when recorded, per-task duration estimates, planned start offsets, input/output tokens, reported costs, outcomes, and explicit unknown or partial measurements. Dark and light themes, keyboard navigation, responsive layouts, and reduced-motion handling are included.
|
|
44
44
|
|
|
45
|
-
The dashboard includes attention-first project search and filters, restorable view links, and usage comparisons against the preceding period. See [dashboard behavior and measurement limits](docs/DASHBOARD-EVOLUTION.md).
|
|
45
|
+
The dashboard includes attention-first project search and filters, project ranking by attention, last activity, token usage, reported cost, acceptance, or name, restorable view links, and usage comparisons against the preceding period. From a project’s live view you can request a safe-boundary pause or resume, add an operator note, and use **Queue a change** for the next planning boundary. See [dashboard behavior and measurement limits](docs/DASHBOARD-EVOLUTION.md) and the [dashboard overhaul contract](docs/DASHBOARD-OVERHAUL.md).
|
|
46
46
|
|
|
47
|
-
Projects are registered explicitly; this version does not automatically discover every process or aggregate other computers.
|
|
47
|
+
Projects are registered explicitly; this version does not automatically discover every process or aggregate other computers. Dashboard controls call the existing goal/loop pause and resume boundaries and never execute arbitrary shell commands. Change requests are append-only pending inbox entries, not immediate code changes. The server stays loopback-only and POST actions require same-origin session authorization; the local Yoke process remains the authority for execution. Missing history appears as unknown; time ranges are empirical estimates, not exact deadlines. Read the [overhaul contract](docs/DASHBOARD-OVERHAUL.md) for data limits and non-goals.
|
|
48
48
|
|
|
49
49
|
### Verified goals and efficient execution
|
|
50
50
|
|
|
@@ -208,10 +208,11 @@ Yoke's CLI is deterministic and chainable by design: an agent (or a shell `&&`)
|
|
|
208
208
|
| `yoke projects add\|list\|remove` | Register a project, list registrations or remove a reference by ID | `0` · `2` invalid/unavailable |
|
|
209
209
|
| `yoke check [dir] [--json] [--requirement=] [--protect [--refresh]]` | Execute acceptance checks or explicitly pin their infrastructure | `0` passed/pinned · `1` failed · `2` unverified/unavailable |
|
|
210
210
|
| `yoke goal set\|run\|resume\|pause\|status\|handoff\|budget [dir]` | Durable objectives, provider handoff, protected checks and checkpoint budgets | run/resume: `0` complete · `1` unfinished · `2` unavailable |
|
|
211
|
-
| `yoke setup [dir] [--yes] [--host=] [--agent=] [--runner=] [--code-graph=] [--decision-policy=] [--loop\|--no-loop] [--routing\|--no-routing] [--model-provider=deepseek,kimi]` | Shared setup for all seven harnesses; optional DeepSeek/Kimi API profiles
|
|
211
|
+
| `yoke setup [dir] [--yes] [--host=] [--agent=] [--runner=] [--code-graph=] [--code-intelligence=off\|shadow\|active] [--decision-policy=] [--loop\|--no-loop] [--routing\|--no-routing] [--model-provider=deepseek,kimi]` | Shared setup for all seven harnesses; optional federated code intelligence and DeepSeek/Kimi API profiles | `0` · `1` invalid setup |
|
|
212
|
+
| `yoke code-intelligence-server [--workspace=] [--mode=off\|shadow\|active]` | Serve the single Yoke-controlled MCP facade for federated code intelligence | `0` · `1` invalid/unavailable |
|
|
212
213
|
| `yoke validate [canonDir]` | Validate the canon (schema, frontmatter, templates) | `0` valid · `1` errors |
|
|
213
214
|
| `yoke new <dir> [--idea=] [--agent=] [--runner=] [--loop]` | Greenfield bootstrap: git init → scaffold → retrofit → context → PRD (drafted from `--idea`) → committed | `0` · `1` usage / non-empty dir / draft failed (scaffold survives) · `2` draft agent unavailable |
|
|
214
|
-
| `yoke retrofit [dir] [--agent=claude,codex,gemini,qwen,opencode,kilo,pi\|all] [--code-graph=graphify\|serena] [--loop]` | Install/update the harness for the selected agents, non-destructively | `0` |
|
|
215
|
+
| `yoke retrofit [dir] [--agent=claude,codex,gemini,qwen,opencode,kilo,pi\|all] [--code-graph=graphify\|serena] [--code-intelligence=off\|shadow\|active] [--loop]` | Install/update the harness for the selected agents, non-destructively | `0` |
|
|
215
216
|
| `yoke prd draft [dir] --idea= [--runner=] [--force]` | Idea → 5–12 stories with testable acceptance criteria | `0` · `1` invalid/guarded · `2` agent unavailable |
|
|
216
217
|
| `yoke prd check [dir]` | PRD lint gate (schema, dependencies, cycles, duplicate ids, acceptance) | `0` valid · `1` violations |
|
|
217
218
|
| `yoke change add\|status [dir] [--idea=]` | Queue a change at any time; the loop turns it into append-only stories at the next safe boundary | `0` · `1` invalid inbox/request |
|
|
@@ -852,7 +853,7 @@ Yoke's guardrails are **mechanical, not advisory** — the loop blocks on a dirt
|
|
|
852
853
|
|
|
853
854
|
## 🧠 Choose your code-graph
|
|
854
855
|
|
|
855
|
-
`yoke retrofit --code-graph=graphify|serena` (default `graphify`, remembered per project).
|
|
856
|
+
`yoke retrofit --code-graph=graphify|serena` (default `graphify`, remembered per project) selects the legacy single graph. For complete code intelligence, enable the federated facade with `yoke retrofit --code-intelligence=active` (or `shadow` for a read-only canary). See the [Code Intelligence guide](docs/CODE-INTELLIGENCE.md).
|
|
856
857
|
|
|
857
858
|
| | **graphify** | **Serena** |
|
|
858
859
|
|---|---|---|
|
|
@@ -862,6 +863,8 @@ Yoke's guardrails are **mechanical, not advisory** — the loop blocks on a dirt
|
|
|
862
863
|
| Best for | rapid exploration / migration / onboarding | systematic refactoring in typed codebases |
|
|
863
864
|
| Caveat | heuristic edges; static index can go stale | one language server per language |
|
|
864
865
|
|
|
866
|
+
The federated mode composes both structural and semantic evidence and adds Graphify's architecture/document graph. It exposes one Yoke-controlled MCP surface, content-addressed snapshots, partial-coverage reporting and isolated edit previews; the legacy `codeGraph` setting remains valid and unchanged when code intelligence is `off`.
|
|
867
|
+
|
|
865
868
|
## 🪙 Token efficiency
|
|
866
869
|
|
|
867
870
|
Yoke attacks tokens on two complementary surfaces:
|
|
@@ -903,6 +906,7 @@ canon/ # the source of truth — harness-agnostic
|
|
|
903
906
|
AGENTS.md skills/ policy/ loop/ tools/ manifest.yaml
|
|
904
907
|
src/
|
|
905
908
|
canon/ # manifest schema + validator (yoke validate)
|
|
909
|
+
code-intelligence/ # federated MCP facade, adapters, snapshots and guarded edits
|
|
906
910
|
change/ # append-only change inbox · planning · independent coverage review
|
|
907
911
|
retrofit/ # detect · plan · apply · planners (all seven harnesses) · tools
|
|
908
912
|
loop/ # prd · gates · runner · verify · git/worktree · loop · run-command · lock · cleanup
|
|
@@ -925,7 +929,7 @@ release provenance.
|
|
|
925
929
|
## 🧪 Development
|
|
926
930
|
|
|
927
931
|
```bash
|
|
928
|
-
npm test # vitest (
|
|
932
|
+
npm test # vitest (1268 tests)
|
|
929
933
|
npm run build # tsc, no emit errors
|
|
930
934
|
npm run yoke -- validate canon
|
|
931
935
|
```
|
package/canon/manifest.yaml
CHANGED
package/dist/cli.js
CHANGED
|
@@ -22,6 +22,7 @@ import { runUpgrade } from './update/upgrade.js';
|
|
|
22
22
|
import { AGENT_LIST, isSupportedAgent, SUPPORTED_AGENTS } from './agents/catalog.js';
|
|
23
23
|
import { printAudit, runAudit } from './audit/command.js';
|
|
24
24
|
import { runSetup } from './setup/command.js';
|
|
25
|
+
import { runCodeIntelligenceServer } from './code-intelligence/mcp-server.js';
|
|
25
26
|
import { pendingChanges, queueChange } from './change/inbox.js';
|
|
26
27
|
import { answerPendingDecision, answeredDecisionResumeIsValid, clearDecisionResume, decisionProcessingExists, decisionResumeMatchesCurrent, finalizeCommittedDecisionResume, formatPendingDecision, readDecisionResume, readPendingDecision, writeDecisionResume, } from './loop/decision.js';
|
|
27
28
|
export { runRetrofit } from './retrofit/command.js';
|
|
@@ -104,6 +105,8 @@ export function parseQualityFlags(args) {
|
|
|
104
105
|
export function main(argv) {
|
|
105
106
|
const [cmd, ...rest] = argv;
|
|
106
107
|
switch (cmd) {
|
|
108
|
+
case 'code-intelligence-server':
|
|
109
|
+
return runCodeIntelligenceServer(rest).then(() => 0).catch(error => { console.error(`Code intelligence server: ${error.message}`); return 2; });
|
|
107
110
|
case 'setup': {
|
|
108
111
|
const targetDir = rest.find(a => !a.startsWith('-')) ?? '.';
|
|
109
112
|
const valid = [...SUPPORTED_AGENTS];
|
|
@@ -130,6 +133,11 @@ export function main(argv) {
|
|
|
130
133
|
console.error(`Invalid --code-graph value: ${graphArg}`);
|
|
131
134
|
return 1;
|
|
132
135
|
}
|
|
136
|
+
const intelligenceArg = rest.find(a => a.startsWith('--code-intelligence='))?.slice('--code-intelligence='.length);
|
|
137
|
+
if (intelligenceArg && !['off', 'shadow', 'active'].includes(intelligenceArg)) {
|
|
138
|
+
console.error(`Invalid --code-intelligence value: ${intelligenceArg}`);
|
|
139
|
+
return 1;
|
|
140
|
+
}
|
|
133
141
|
const policyArg = rest.find(a => a.startsWith('--decision-policy='))?.slice('--decision-policy='.length);
|
|
134
142
|
if (policyArg && policyArg !== 'auto' && policyArg !== 'critical') {
|
|
135
143
|
console.error(`Invalid --decision-policy value: ${policyArg}`);
|
|
@@ -152,6 +160,7 @@ export function main(argv) {
|
|
|
152
160
|
modelProviders: modelProviders,
|
|
153
161
|
host: hostArg, agents, runner: runnerArg,
|
|
154
162
|
codeGraph: graphArg,
|
|
163
|
+
codeIntelligence: intelligenceArg,
|
|
155
164
|
loop, routing, decisionPolicy: policyArg,
|
|
156
165
|
routingStrategy: routingStrategy,
|
|
157
166
|
routingPreset: rest.includes('--routing-preset'),
|
|
@@ -291,7 +300,12 @@ export function main(argv) {
|
|
|
291
300
|
console.error(`Invalid --code-graph value: ${cgArg} (expected graphify|serena)`);
|
|
292
301
|
return 1;
|
|
293
302
|
}
|
|
294
|
-
|
|
303
|
+
const ciArg = rest.find(a => a.startsWith('--code-intelligence='))?.slice('--code-intelligence='.length);
|
|
304
|
+
if (ciArg && !['off', 'shadow', 'active'].includes(ciArg)) {
|
|
305
|
+
console.error(`Invalid --code-intelligence value: ${ciArg} (expected off|shadow|active)`);
|
|
306
|
+
return 1;
|
|
307
|
+
}
|
|
308
|
+
return runRetrofit(targetDir, { loop, agents, codeGraph, codeIntelligence: ciArg });
|
|
295
309
|
}
|
|
296
310
|
case 'change': {
|
|
297
311
|
const sub = rest[0];
|
|
@@ -657,7 +671,7 @@ export function main(argv) {
|
|
|
657
671
|
return runUpgrade();
|
|
658
672
|
default:
|
|
659
673
|
console.log('Project workflows: yoke check [dir] [--json|--protect] | goal set|run|resume|pause|status|handoff|budget [dir] | projects add|list|remove | dashboard [dir] [--port=N]');
|
|
660
|
-
console.log(`usage: yoke <setup [dir] | new <dir> [--idea="..."] | validate [canonDir] | retrofit [targetDir] [--agent=${AGENT_LIST}|all] [--code-graph=graphify|serena] [--loop] | change <add|status> [dir] | prd <draft|check|assess> [dir] | loop <on|off|status|decision|answer|resume|run|cleanup> | context <init|status> | review [dir]
|
|
674
|
+
console.log(`usage: yoke <setup [dir] | new <dir> [--idea="..."] | validate [canonDir] | retrofit [targetDir] [--agent=${AGENT_LIST}|all] [--code-graph=graphify|serena] [--code-intelligence=off|shadow|active] [--loop] | change <add|status> [dir] | code-intelligence-server --workspace=<dir> --mode=<mode> | prd <draft|check|assess> [dir] | loop <on|off|status|decision|answer|resume|run|cleanup> | context <init|status> | review [dir] | design-scan [dir] | flow-smoke [dir] | upgrade>`);
|
|
661
675
|
return cmd ? 1 : 0;
|
|
662
676
|
}
|
|
663
677
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { McpBackendAdapter } from './mcp.js';
|
|
2
|
+
export const GRAFT_VERSION = '0.17.0';
|
|
3
|
+
export function createGraftAdapter(cwd, override) {
|
|
4
|
+
return new McpBackendAdapter({
|
|
5
|
+
name: 'graft', version: GRAFT_VERSION, cwd, command: override?.command ?? 'graft', args: override?.args ?? ['mcp'], framing: 'line', semantic: false, documents: false,
|
|
6
|
+
aliases: { context: 'graft_find_code', trace: 'graft_trace_calls', file: 'graft_file_api', freshness: 'graft_check_freshness', repo_map: 'graft_repo_map' },
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
export function graftCall(kind, args) {
|
|
10
|
+
return { tool: kind, arguments: args };
|
|
11
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { McpBackendAdapter } from './mcp.js';
|
|
2
|
+
export const GRAPHIFY_VERSION = '0.9.56';
|
|
3
|
+
export function createGraphifyAdapter(cwd, override) {
|
|
4
|
+
return new McpBackendAdapter({
|
|
5
|
+
name: 'graphify', version: GRAPHIFY_VERSION, cwd, command: override?.command ?? 'python', args: override?.args ?? ['-m', 'graphify.serve'], framing: 'content-length', semantic: false, documents: true,
|
|
6
|
+
aliases: { context: 'query_graph', symbol: 'get_node', neighbors: 'get_neighbors', trace: 'shortest_path', impact: 'get_pr_impact' },
|
|
7
|
+
});
|
|
8
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { McpStdioClient } from '../mcp-client.js';
|
|
2
|
+
function valueFromResult(value) {
|
|
3
|
+
if (!value || typeof value !== 'object')
|
|
4
|
+
return value;
|
|
5
|
+
if (value.structuredContent !== undefined)
|
|
6
|
+
return value.structuredContent;
|
|
7
|
+
const text = Array.isArray(value.content) ? value.content.find((item) => item?.type === 'text')?.text : undefined;
|
|
8
|
+
if (typeof text !== 'string')
|
|
9
|
+
return value;
|
|
10
|
+
try {
|
|
11
|
+
return JSON.parse(text);
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return { text };
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export class McpBackendAdapter {
|
|
18
|
+
name;
|
|
19
|
+
version;
|
|
20
|
+
semantic;
|
|
21
|
+
documents;
|
|
22
|
+
client;
|
|
23
|
+
aliases;
|
|
24
|
+
constructor(options) {
|
|
25
|
+
this.name = options.name;
|
|
26
|
+
this.version = options.version;
|
|
27
|
+
this.semantic = options.semantic;
|
|
28
|
+
this.documents = options.documents;
|
|
29
|
+
this.aliases = options.aliases;
|
|
30
|
+
this.client = new McpStdioClient(options.command, options.args, options.cwd, options.framing);
|
|
31
|
+
}
|
|
32
|
+
async call(request, timeoutMs) {
|
|
33
|
+
const tool = this.aliases[request.tool] ?? request.tool;
|
|
34
|
+
return valueFromResult(await this.client.call(tool, request.arguments, timeoutMs));
|
|
35
|
+
}
|
|
36
|
+
close() { return this.client.close(); }
|
|
37
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { McpBackendAdapter } from './mcp.js';
|
|
2
|
+
export const SERENA_VERSION = '1.7.1-dev';
|
|
3
|
+
export function createSerenaAdapter(cwd, override) {
|
|
4
|
+
return new McpBackendAdapter({
|
|
5
|
+
name: 'serena-lsp', version: SERENA_VERSION, cwd, command: override?.command ?? 'serena', args: override?.args ?? ['start-mcp-server', '--project-from-cwd', '--context', 'codex'], framing: 'content-length', semantic: true, documents: false,
|
|
6
|
+
aliases: {
|
|
7
|
+
symbol: 'find_symbol', references: 'find_referencing_symbols', implementations: 'find_implementations', overview: 'get_symbols_overview', diagnostics: 'get_diagnostics_for_file',
|
|
8
|
+
rename: 'rename_symbol', replace_symbol_body: 'replace_symbol_body', insert_before_symbol: 'insert_before_symbol', insert_after_symbol: 'insert_after_symbol', safe_delete: 'safe_delete_symbol',
|
|
9
|
+
},
|
|
10
|
+
});
|
|
11
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const CodeIntelligenceModeSchema = z.enum(['off', 'shadow', 'active']);
|
|
3
|
+
export const BackendNameSchema = z.enum(['graft', 'graphify', 'serena-lsp', 'serena-jetbrains', 'yoke']);
|
|
4
|
+
export const StatusSchema = z.enum(['success', 'partial', 'blocked', 'error']);
|
|
5
|
+
export const ResolutionSchema = z.enum(['resolved', 'unresolved', 'ambiguous', 'not_applicable']);
|
|
6
|
+
export const FreshnessSchema = z.enum(['current', 'stale', 'unknown']);
|
|
7
|
+
export const ProvenanceSchema = z.object({
|
|
8
|
+
backend: BackendNameSchema,
|
|
9
|
+
version: z.string().min(1),
|
|
10
|
+
source_path: z.string().nullable(),
|
|
11
|
+
content_hash: z.string().nullable(),
|
|
12
|
+
byte_range: z.object({ start: z.number().int().nonnegative(), end: z.number().int().nonnegative() }).nullable(),
|
|
13
|
+
origin: z.enum(['parser', 'resolver', 'lsp', 'ide', 'llm', 'manual']),
|
|
14
|
+
resolution: ResolutionSchema,
|
|
15
|
+
freshness: FreshnessSchema,
|
|
16
|
+
});
|
|
17
|
+
export const CoverageSchema = z.object({
|
|
18
|
+
backends_requested: z.array(BackendNameSchema),
|
|
19
|
+
backends_used: z.array(BackendNameSchema),
|
|
20
|
+
backends_missing: z.array(BackendNameSchema),
|
|
21
|
+
structural: z.enum(['complete', 'partial', 'unavailable']),
|
|
22
|
+
semantic: z.enum(['complete', 'partial', 'unavailable']),
|
|
23
|
+
documents: z.enum(['complete', 'partial', 'unavailable']),
|
|
24
|
+
});
|
|
25
|
+
export const MetricsSchema = z.object({
|
|
26
|
+
latency_ms: z.number().nonnegative(),
|
|
27
|
+
result_tokens: z.number().int().nonnegative(),
|
|
28
|
+
token_count_kind: z.enum(['exact', 'estimated']),
|
|
29
|
+
returned_bytes: z.number().int().nonnegative(),
|
|
30
|
+
});
|
|
31
|
+
export const ErrorSchema = z.object({
|
|
32
|
+
code: z.enum([
|
|
33
|
+
'INVALID_ARGUMENT', 'WORKSPACE_DENIED', 'CAPABILITY_UNAVAILABLE', 'SNAPSHOT_STALE',
|
|
34
|
+
'AMBIGUOUS_SYMBOL', 'BACKEND_TIMEOUT', 'BACKEND_UNAVAILABLE', 'BUDGET_EXCEEDED',
|
|
35
|
+
'APPROVAL_REQUIRED', 'PLAN_EXPIRED', 'SCOPE_VIOLATION', 'VALIDATION_FAILED',
|
|
36
|
+
'WRITE_CONFLICT', 'IDEMPOTENCY_CONFLICT', 'INTERNAL_ERROR',
|
|
37
|
+
]),
|
|
38
|
+
message: z.string().min(1),
|
|
39
|
+
backend: BackendNameSchema.optional(),
|
|
40
|
+
});
|
|
41
|
+
export const ItemSchema = z.object({
|
|
42
|
+
item_id: z.string().min(1),
|
|
43
|
+
kind: z.enum(['symbol', 'code', 'document', 'memory', 'summary']),
|
|
44
|
+
path: z.string().nullable(),
|
|
45
|
+
excerpt: z.string().max(12000),
|
|
46
|
+
evidence_ids: z.array(z.string().min(1)),
|
|
47
|
+
rank: z.number().nonnegative(),
|
|
48
|
+
});
|
|
49
|
+
export const SymbolSchema = z.object({
|
|
50
|
+
symbol_id: z.string().min(1),
|
|
51
|
+
name: z.string().min(1),
|
|
52
|
+
path: z.string().nullable(),
|
|
53
|
+
kind: z.string().min(1),
|
|
54
|
+
signature: z.string().nullable(),
|
|
55
|
+
evidence_ids: z.array(z.string().min(1)),
|
|
56
|
+
});
|
|
57
|
+
export const ReferenceSchema = z.object({
|
|
58
|
+
symbol_id: z.string().min(1),
|
|
59
|
+
path: z.string().nullable(),
|
|
60
|
+
line: z.number().int().positive().nullable(),
|
|
61
|
+
excerpt: z.string().max(4000),
|
|
62
|
+
evidence_ids: z.array(z.string().min(1)),
|
|
63
|
+
});
|
|
64
|
+
export const DiagnosticSchema = z.object({
|
|
65
|
+
path: z.string().nullable(),
|
|
66
|
+
line: z.number().int().positive().nullable(),
|
|
67
|
+
severity: z.enum(['error', 'warning', 'info', 'unknown']),
|
|
68
|
+
message: z.string().max(4000),
|
|
69
|
+
evidence_ids: z.array(z.string().min(1)),
|
|
70
|
+
});
|
|
71
|
+
export const EdgeSchema = z.object({
|
|
72
|
+
from: z.string().min(1),
|
|
73
|
+
to: z.string().min(1),
|
|
74
|
+
relation: z.enum(['calls', 'references', 'imports', 'implements', 'extends', 'docs', 'rationale_for', 'related_to']),
|
|
75
|
+
evidence_ids: z.array(z.string().min(1)),
|
|
76
|
+
});
|
|
77
|
+
export const ResponseBaseSchema = z.object({
|
|
78
|
+
schema_version: z.literal('0.1.0'),
|
|
79
|
+
request_id: z.string().min(1),
|
|
80
|
+
workspace_id: z.string().min(1),
|
|
81
|
+
snapshot_id: z.string().min(1),
|
|
82
|
+
status: StatusSchema,
|
|
83
|
+
coverage: CoverageSchema,
|
|
84
|
+
provenance: z.array(ProvenanceSchema),
|
|
85
|
+
warnings: z.array(z.string()),
|
|
86
|
+
metrics: MetricsSchema,
|
|
87
|
+
artifact_uri: z.string().nullable(),
|
|
88
|
+
error: ErrorSchema.nullable(),
|
|
89
|
+
});
|
|
90
|
+
const BudgetSchema = z.object({
|
|
91
|
+
token_budget: z.number().int().min(128).max(16000).default(2400),
|
|
92
|
+
timeout_ms: z.number().int().min(100).max(60000).default(5000),
|
|
93
|
+
});
|
|
94
|
+
export const ContextInputSchema = z.object({
|
|
95
|
+
workspace_id: z.string().min(1), query: z.string().min(1).max(20000), snapshot_id: z.string().min(1).optional(),
|
|
96
|
+
mode: z.enum(['orient', 'locate', 'explain']).default('orient'), paths: z.array(z.string().min(1)).max(32).default([]),
|
|
97
|
+
include_docs: z.boolean().default(false), ...BudgetSchema.shape,
|
|
98
|
+
});
|
|
99
|
+
export const SymbolInputSchema = z.object({
|
|
100
|
+
workspace_id: z.string().min(1), snapshot_id: z.string().min(1), symbol_id: z.string().min(1).optional(),
|
|
101
|
+
query: z.string().min(1).optional(), path: z.string().min(1).optional(),
|
|
102
|
+
include: z.array(z.enum(['definition', 'references', 'implementations', 'diagnostics'])).default(['definition']), ...BudgetSchema.shape,
|
|
103
|
+
}).refine(v => Boolean(v.symbol_id) !== Boolean(v.query), 'exactly one of symbol_id or query is required');
|
|
104
|
+
export const TraceInputSchema = z.object({
|
|
105
|
+
workspace_id: z.string().min(1), snapshot_id: z.string().min(1), symbol_id: z.string().min(1),
|
|
106
|
+
direction: z.enum(['in', 'out']), relations: z.array(z.enum(['calls', 'references', 'imports', 'implements', 'extends'])).min(1),
|
|
107
|
+
max_depth: z.number().int().min(1).max(6).default(2), max_nodes: z.number().int().min(1).max(2000).default(200),
|
|
108
|
+
require_resolved: z.boolean().default(false), timeout_ms: z.number().int().min(100).max(60000).default(5000),
|
|
109
|
+
});
|
|
110
|
+
const TargetSchema = z.union([z.object({ path: z.string().min(1) }), z.object({ symbol_id: z.string().min(1) })]);
|
|
111
|
+
export const ImpactInputSchema = z.object({
|
|
112
|
+
workspace_id: z.string().min(1), snapshot_id: z.string().min(1), targets: z.array(TargetSchema).max(50).optional(),
|
|
113
|
+
plan_id: z.string().min(1).optional(), include_docs: z.boolean().default(true), require_semantic: z.boolean().default(true),
|
|
114
|
+
max_depth: z.number().int().min(1).max(6).default(3), timeout_ms: z.number().int().min(100).max(60000).default(10000),
|
|
115
|
+
}).refine(v => Boolean(v.targets) !== Boolean(v.plan_id), 'exactly one of targets or plan_id is required');
|
|
116
|
+
const OperationSchema = z.discriminatedUnion('kind', [
|
|
117
|
+
z.object({ kind: z.literal('rename'), symbol_id: z.string().min(1), new_name: z.string().regex(/^[A-Za-z_$][\w$]*$/) }),
|
|
118
|
+
z.object({ kind: z.literal('replace_symbol_body'), symbol_id: z.string().min(1), content: z.string().max(500000) }),
|
|
119
|
+
z.object({ kind: z.literal('insert_before_symbol'), symbol_id: z.string().min(1), content: z.string().max(500000) }),
|
|
120
|
+
z.object({ kind: z.literal('insert_after_symbol'), symbol_id: z.string().min(1), content: z.string().max(500000) }),
|
|
121
|
+
z.object({ kind: z.literal('safe_delete'), symbol_id: z.string().min(1) }),
|
|
122
|
+
]);
|
|
123
|
+
export const EditPreviewInputSchema = z.object({
|
|
124
|
+
workspace_id: z.string().min(1), snapshot_id: z.string().min(1), operations: z.array(OperationSchema).min(1).max(20),
|
|
125
|
+
validation_profile: z.enum(['default', 'extended']), timeout_ms: z.number().int().min(100).max(600000).default(120000),
|
|
126
|
+
});
|
|
127
|
+
export const EditApplyInputSchema = z.object({
|
|
128
|
+
workspace_id: z.string().min(1), plan_id: z.string().min(1), expected_snapshot_id: z.string().min(1),
|
|
129
|
+
idempotency_key: z.string().min(8).max(128),
|
|
130
|
+
});
|
|
131
|
+
export const TOOL_NAMES = ['code_context', 'code_symbol', 'code_trace', 'code_impact', 'code_edit_preview', 'code_edit_apply'];
|
|
132
|
+
export const TOOL_DEFINITIONS = TOOL_NAMES.map(name => ({
|
|
133
|
+
name,
|
|
134
|
+
description: `Yoke-controlled ${name.replace('code_', '').replace('_', ' ')} with snapshot-bound evidence.`,
|
|
135
|
+
inputSchema: { type: 'object', additionalProperties: false },
|
|
136
|
+
annotations: { openWorldHint: false, destructiveHint: name === 'code_edit_apply', readOnlyHint: name !== 'code_edit_preview' && name !== 'code_edit_apply', idempotentHint: name !== 'code_edit_apply' },
|
|
137
|
+
}));
|