@hecer/yoke 1.14.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 +17 -0
- package/README.md +13 -9
- 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/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/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
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
|
+
|
|
3
20
|
## 1.14.0 — 2026-09-09
|
|
4
21
|
|
|
5
22
|
### 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
|
|
|
@@ -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/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
|
+
}));
|