@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.
Files changed (45) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/README.md +16 -12
  3. package/canon/manifest.yaml +1 -1
  4. package/dist/cli.js +16 -2
  5. package/dist/code-intelligence/adapters/graft.js +11 -0
  6. package/dist/code-intelligence/adapters/graphify.js +8 -0
  7. package/dist/code-intelligence/adapters/index.js +5 -0
  8. package/dist/code-intelligence/adapters/mcp.js +37 -0
  9. package/dist/code-intelligence/adapters/serena.js +11 -0
  10. package/dist/code-intelligence/adapters/types.js +1 -0
  11. package/dist/code-intelligence/contracts.js +137 -0
  12. package/dist/code-intelligence/coordinator.js +370 -0
  13. package/dist/code-intelligence/edit-plans.js +53 -0
  14. package/dist/code-intelligence/evidence.js +77 -0
  15. package/dist/code-intelligence/index.js +5 -0
  16. package/dist/code-intelligence/internal-types.js +1 -0
  17. package/dist/code-intelligence/mcp-client.js +139 -0
  18. package/dist/code-intelligence/mcp-server.js +93 -0
  19. package/dist/code-intelligence/snapshots.js +117 -0
  20. package/dist/code-intelligence/transactions.js +60 -0
  21. package/dist/dashboard/analytics.js +193 -29
  22. package/dist/dashboard/contracts.js +23 -0
  23. package/dist/dashboard/page.js +39 -94
  24. package/dist/dashboard/panels.js +85 -31
  25. package/dist/dashboard/server.js +190 -15
  26. package/dist/observability/events.js +1 -1
  27. package/dist/observability/history.js +1 -1
  28. package/dist/retrofit/command.js +3 -1
  29. package/dist/retrofit/config.js +10 -0
  30. package/dist/retrofit/gitignore.js +1 -0
  31. package/dist/retrofit/plan.js +3 -3
  32. package/dist/retrofit/planners/claude.js +3 -3
  33. package/dist/retrofit/planners/codex.js +5 -5
  34. package/dist/retrofit/planners/gemini.js +2 -2
  35. package/dist/retrofit/planners/kilo.js +2 -2
  36. package/dist/retrofit/planners/opencode.js +2 -2
  37. package/dist/retrofit/planners/pi.js +1 -1
  38. package/dist/retrofit/planners/qwen.js +2 -2
  39. package/dist/retrofit/tools.js +9 -7
  40. package/dist/setup/command.js +3 -1
  41. package/docs/CODE-INTELLIGENCE.md +36 -0
  42. package/docs/DASHBOARD-EVOLUTION.md +16 -2
  43. package/docs/DASHBOARD-OVERHAUL.md +146 -0
  44. package/gemini-extension.json +1 -1
  45. 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.13.0/docs/assets/yoke-logo.png" alt="Yoke" width="100" height="63"></h1>
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.13.0<!-- yoke:version:end -->
6
- <!-- yoke:tests:start -->1231<!-- yoke:tests:end -->
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: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](#-license)
18
18
  ![Node](https://img.shields.io/badge/node-%E2%89%A520-339933?logo=node.js&logoColor=white)
19
19
  ![TypeScript](https://img.shields.io/badge/TypeScript-3178C6?logo=typescript&logoColor=white)
20
- ![Tests](https://img.shields.io/badge/tests-1231%20defined-blue.svg)
20
+ ![Tests](https://img.shields.io/badge/tests-1268%20defined-blue.svg)
21
21
  ![Agents](https://img.shields.io/badge/agents-Claude%20%7C%20Codex%20%7C%20Gemini%20%7C%20Qwen%20%7C%20OpenCode%20%7C%20Kilo%20%7C%20Pi-8A2BE2)
22
22
  ![Built with TDD](https://img.shields.io/badge/built%20with-TDD%20%2B%20review-ff69b4.svg)
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.13.0:** first-class [OpenCode, Kilo and Pi integrations](docs/HARNESSES.md), including native headless invocation, provider/model/variant routing, retrofit artifacts, role configuration and provider telemetry. [Qwen Code hardening and explicit DeepSeek/Kimi API model profiles](docs/QWEN-MODEL-SUPPORT.md) remain available. Since 1.10.0, Yoke also includes [dashboard search, filters and period comparisons](docs/DASHBOARD-EVOLUTION.md), [batch task assessments with separate planning models](docs/CAPABILITY-ROUTING.md), and [Windows sandbox preflight and process supervision](docs/WINDOWS-RUNNER-VALIDATION.md). Existing routing settings remain authoritative. See the [changelog](CHANGELOG.md) and the [harness integration guide](docs/HARNESSES.md) for limitations and setup.
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 available worker state, per-task duration estimates, planned start offsets, input/output tokens, costs and unknown measurements. You can request a goal pause at a safe boundary.
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. Start/resume and budget changes use the CLI. Missing history appears as unknown; time ranges are empirical estimates, not exact deadlines.
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 run through Qwen | `0` · `1` invalid setup |
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). The `yoke-retrofit` skill asks and recommends based on the 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 (1231 tests)
932
+ npm test # vitest (1268 tests)
929
933
  npm run build # tsc, no emit errors
930
934
  npm run yoke -- validate canon
931
935
  ```
@@ -1,5 +1,5 @@
1
1
  name: yoke-canon
2
- version: 1.13.0
2
+ version: 1.14.0
3
3
  agents: [claude, codex, gemini, qwen, opencode, kilo, pi]
4
4
  skills:
5
5
  - { id: tdd, path: skills/tdd, kind: methodology, invocation: auto }
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
- return runRetrofit(targetDir, { loop, agents, codeGraph });
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] [--reviewer=<${AGENT_LIST}>] [--base=<ref>] [--focus="..."] | design-scan [dir] [--max=N] [--report] | flow-smoke [dir] [--url=<baseUrl>] [--label=<name>] | upgrade>`);
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,5 @@
1
+ export * from './types.js';
2
+ export * from './mcp.js';
3
+ export * from './graft.js';
4
+ export * from './graphify.js';
5
+ export * from './serena.js';
@@ -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
+ }));