@hecer/yoke 1.14.0 → 1.15.1
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/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/CHANGELOG.md +36 -0
- package/README.md +15 -9
- package/canon/AGENTS.md +7 -0
- package/canon/manifest.yaml +1 -1
- package/canon/skills/executing-plans/SKILL.md +1 -1
- package/canon/skills/requesting-code-review/SKILL.md +2 -2
- package/canon/skills/requesting-code-review/code-reviewer.md +12 -0
- package/canon/skills/subagent-driven-development/SKILL.md +7 -3
- package/canon/skills/subagent-driven-development/code-quality-reviewer-prompt.md +7 -0
- package/canon/skills/subagent-driven-development/implementer-prompt.md +7 -0
- package/canon/skills/subagent-driven-development/spec-reviewer-prompt.md +7 -0
- package/canon/skills/systematic-debugging/SKILL.md +7 -11
- package/canon/skills/systematic-debugging/condition-based-waiting.md +7 -0
- package/canon/skills/systematic-debugging/defense-in-depth.md +7 -0
- package/canon/skills/systematic-debugging/root-cause-tracing.md +9 -0
- package/canon/skills/tdd/SKILL.md +1 -1
- package/canon/skills/tdd/testing-anti-patterns.md +9 -0
- package/dist/agents/pi-telemetry.js +53 -0
- package/dist/agents/process-streams.js +13 -0
- package/dist/agents/providers.js +4 -1
- package/dist/agents/supervision.js +2 -0
- package/dist/agents/telemetry.js +22 -25
- 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/estimation/schedule.js +40 -26
- 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 +2 -2
- package/dist/retrofit/planners/qwen.js +2 -2
- package/dist/retrofit/skill-actions.js +1 -1
- package/dist/retrofit/tools.js +9 -7
- package/dist/setup/command.js +3 -1
- package/docs/AGENT-HARDENING-2026-09-16.md +32 -0
- package/docs/CODE-INTELLIGENCE.md +36 -0
- package/docs/HARNESSES.md +6 -0
- package/gemini-extension.json +1 -1
- package/package.json +1 -1
package/dist/retrofit/config.js
CHANGED
|
@@ -6,7 +6,16 @@ import { AgentSchema, PermissionProfileSchema } from '../agents/contracts.js';
|
|
|
6
6
|
import { ProjectQualityDefaultsSchema } from '../quality/types.js';
|
|
7
7
|
import { DEFAULT_OUTPUT_POLICY } from '../output/types.js';
|
|
8
8
|
import { ToolActionSchema } from '../execution/actions.js';
|
|
9
|
+
import { CodeIntelligenceModeSchema } from '../code-intelligence/contracts.js';
|
|
10
|
+
export { CodeIntelligenceModeSchema };
|
|
9
11
|
const CodeGraphSchema = z.enum(['graphify', 'serena']);
|
|
12
|
+
const CodeIntelligenceProcessSchema = z.object({ command: z.string().min(1), args: z.array(z.string()).max(32).optional() });
|
|
13
|
+
const CodeIntelligenceSchema = z.object({
|
|
14
|
+
mode: CodeIntelligenceModeSchema.default('off'), workspaceId: z.string().min(1).optional(),
|
|
15
|
+
graft: CodeIntelligenceProcessSchema.optional(), graphify: CodeIntelligenceProcessSchema.optional(), serena: CodeIntelligenceProcessSchema.optional(),
|
|
16
|
+
policy: z.object({ allowNetwork: z.boolean().default(false), excludePatterns: z.array(z.string().min(1)).max(100).default([]), maxFileBytes: z.number().int().positive().max(20_000_000).optional() }).optional(),
|
|
17
|
+
limits: z.object({ tokenBudget: z.number().int().min(128).max(16000).default(2400), timeoutMs: z.number().int().min(100).max(600000).default(10000), maxBytes: z.number().int().positive().max(10_000_000).default(2_000_000), maxBackends: z.number().int().min(1).max(3).default(3) }).optional(),
|
|
18
|
+
});
|
|
10
19
|
const SmokeFlowSchema = z.object({ name: z.string().min(1), path: z.string().min(1), landmark: z.string().optional() });
|
|
11
20
|
const SmokeSchema = z.object({ baseUrl: z.string().min(1), flows: z.array(SmokeFlowSchema).min(1) });
|
|
12
21
|
const OutputPolicySchema = z.object({
|
|
@@ -118,6 +127,7 @@ export const YokeConfigSchema = z.object({
|
|
|
118
127
|
max: z.number().int().positive().default(4),
|
|
119
128
|
}).optional(),
|
|
120
129
|
codeGraph: CodeGraphSchema.optional(),
|
|
130
|
+
codeIntelligence: CodeIntelligenceSchema.optional(),
|
|
121
131
|
smoke: SmokeSchema.optional(),
|
|
122
132
|
quality: ProjectQualityDefaultsSchema.optional(),
|
|
123
133
|
output: OutputPolicySchema.optional(),
|
package/dist/retrofit/plan.js
CHANGED
|
@@ -10,7 +10,7 @@ export function planClaudeRetrofit(canonDir, targetDir) {
|
|
|
10
10
|
return planClaude(canonDir, targetDir);
|
|
11
11
|
}
|
|
12
12
|
export const PLANNERS = {
|
|
13
|
-
claude: (c, t, cg) => planClaude(c, t, undefined, cg),
|
|
13
|
+
claude: (c, t, cg, ci) => planClaude(c, t, undefined, cg, undefined, ci),
|
|
14
14
|
codex: planCodex,
|
|
15
15
|
gemini: planGemini,
|
|
16
16
|
qwen: planQwen,
|
|
@@ -18,7 +18,7 @@ export const PLANNERS = {
|
|
|
18
18
|
kilo: planKilo,
|
|
19
19
|
pi: planPi,
|
|
20
20
|
};
|
|
21
|
-
export function planRetrofit(canonDir, targetDir, agents, codeGraph = 'graphify') {
|
|
21
|
+
export function planRetrofit(canonDir, targetDir, agents, codeGraph = 'graphify', codeIntelligence = 'off') {
|
|
22
22
|
const seen = new Set();
|
|
23
23
|
const merged = [];
|
|
24
24
|
for (const action of baseContextActions(canonDir)) {
|
|
@@ -26,7 +26,7 @@ export function planRetrofit(canonDir, targetDir, agents, codeGraph = 'graphify'
|
|
|
26
26
|
merged.push(action);
|
|
27
27
|
}
|
|
28
28
|
for (const agent of agents) {
|
|
29
|
-
for (const action of PLANNERS[agent](canonDir, targetDir, codeGraph)) {
|
|
29
|
+
for (const action of PLANNERS[agent](canonDir, targetDir, codeGraph, codeIntelligence)) {
|
|
30
30
|
if (seen.has(action.target))
|
|
31
31
|
continue;
|
|
32
32
|
seen.add(action.target);
|
|
@@ -22,7 +22,7 @@ This project uses the Yoke harness. Baseline instructions:
|
|
|
22
22
|
${rtkNote ? `\n${rtkNote}\n` : ''}${composeNote ? `\n${composeNote}\n` : ''}
|
|
23
23
|
${PRESERVE_SCAFFOLD}
|
|
24
24
|
`;
|
|
25
|
-
export function planClaude(canonDir, targetDir, wslAvailable = hasWsl(), codeGraph = 'graphify', gstackDetected = detectGstack(targetDir)) {
|
|
25
|
+
export function planClaude(canonDir, targetDir, wslAvailable = hasWsl(), codeGraph = 'graphify', gstackDetected = detectGstack(targetDir), codeIntelligence = 'off') {
|
|
26
26
|
const manifest = loadManifest(join(canonDir, 'manifest.yaml'));
|
|
27
27
|
const actions = [];
|
|
28
28
|
for (const skill of manifest.skills) {
|
|
@@ -45,8 +45,8 @@ export function planClaude(canonDir, targetDir, wslAvailable = hasWsl(), codeGra
|
|
|
45
45
|
actions.push({
|
|
46
46
|
kind: 'write',
|
|
47
47
|
target: '.mcp.json',
|
|
48
|
-
content: JSON.stringify({ mcpServers: mcpServers(codeGraph) }, null, 2) + '\n',
|
|
49
|
-
reason: 'MCP servers (code-graph + playwright)',
|
|
48
|
+
content: JSON.stringify({ mcpServers: mcpServers(codeGraph, codeIntelligence, targetDir) }, null, 2) + '\n',
|
|
49
|
+
reason: codeIntelligence === 'off' ? 'MCP servers (code-graph + playwright)' : 'Yoke code-intelligence facade + playwright',
|
|
50
50
|
});
|
|
51
51
|
if (rtkHookable) {
|
|
52
52
|
actions.push({
|
|
@@ -3,8 +3,8 @@ import { join } from 'node:path';
|
|
|
3
3
|
import { loadManifest } from '../../canon/manifest.js';
|
|
4
4
|
import { mcpServers, rtkInstruction } from '../tools.js';
|
|
5
5
|
import { skillPackageActions } from '../skill-actions.js';
|
|
6
|
-
function tomlMcp(codeGraph) {
|
|
7
|
-
const servers = mcpServers(codeGraph);
|
|
6
|
+
function tomlMcp(codeGraph, codeIntelligence, targetDir) {
|
|
7
|
+
const servers = mcpServers(codeGraph, codeIntelligence, targetDir);
|
|
8
8
|
// Codex reads MCP servers from ~/.codex/config.toml. This project-level file is a
|
|
9
9
|
// ready-to-merge snippet; users append these blocks to their global config.
|
|
10
10
|
return Object.entries(servers)
|
|
@@ -14,7 +14,7 @@ function tomlMcp(codeGraph) {
|
|
|
14
14
|
})
|
|
15
15
|
.join('\n');
|
|
16
16
|
}
|
|
17
|
-
export function planCodex(canonDir,
|
|
17
|
+
export function planCodex(canonDir, targetDir, codeGraph = 'graphify', codeIntelligence = 'off') {
|
|
18
18
|
const manifest = loadManifest(join(canonDir, 'manifest.yaml'));
|
|
19
19
|
const baseline = readFileSync(join(canonDir, 'AGENTS.md'), 'utf8');
|
|
20
20
|
const actions = manifest.skills.flatMap(skill => skillPackageActions(canonDir, skill, 'codex'));
|
|
@@ -32,8 +32,8 @@ export function planCodex(canonDir, _targetDir, codeGraph = 'graphify') {
|
|
|
32
32
|
}, {
|
|
33
33
|
kind: 'write',
|
|
34
34
|
target: '.codex/config.toml',
|
|
35
|
-
content: `# Yoke project configuration. Codex loads this in trusted repositories.\n\n[features]\nhooks = true\n\n${tomlMcp(codeGraph)}`,
|
|
36
|
-
reason: 'MCP servers (code-graph + playwright)',
|
|
35
|
+
content: `# Yoke project configuration. Codex loads this in trusted repositories.\n\n[features]\nhooks = true\n\n${tomlMcp(codeGraph, codeIntelligence, targetDir)}`,
|
|
36
|
+
reason: codeIntelligence === 'off' ? 'MCP servers (code-graph + playwright)' : 'Yoke code-intelligence facade + playwright',
|
|
37
37
|
}, {
|
|
38
38
|
kind: 'write',
|
|
39
39
|
target: '.codex/hooks.json',
|
|
@@ -8,7 +8,7 @@ import { skillPackageActions } from '../skill-actions.js';
|
|
|
8
8
|
function tomlString(s) {
|
|
9
9
|
return '"""\n' + s.replace(/\\/g, '\\\\').replace(/"""/g, '\\"\\"\\"') + '\n"""';
|
|
10
10
|
}
|
|
11
|
-
export function planGemini(canonDir,
|
|
11
|
+
export function planGemini(canonDir, targetDir, codeGraph = 'graphify', codeIntelligence = 'off') {
|
|
12
12
|
const manifest = loadManifest(join(canonDir, 'manifest.yaml'));
|
|
13
13
|
const actions = [];
|
|
14
14
|
// GEMINI.md: baseline + instruction for commands outside the rewrite hook.
|
|
@@ -56,7 +56,7 @@ export function planGemini(canonDir, _targetDir, codeGraph = 'graphify') {
|
|
|
56
56
|
target: '.gemini/settings.json',
|
|
57
57
|
merge: true,
|
|
58
58
|
content: JSON.stringify({
|
|
59
|
-
mcpServers: mcpServers(codeGraph),
|
|
59
|
+
mcpServers: mcpServers(codeGraph, codeIntelligence, targetDir),
|
|
60
60
|
context: { fileName: ['AGENTS.md', 'GEMINI.md'] },
|
|
61
61
|
hooks: { BeforeTool: [{ matcher: '^run_shell_command$', hooks: [{ name: 'yoke-rtk', type: 'command', command: 'node .gemini/hooks/gemini-rtk-hook.mjs' }] }] },
|
|
62
62
|
}, null, 2) + '\n',
|
|
@@ -15,7 +15,7 @@ permission:
|
|
|
15
15
|
|
|
16
16
|
Review the observed diff and test evidence. Do not modify files. Return only actionable findings grounded in evidence.
|
|
17
17
|
`;
|
|
18
|
-
export function planKilo(canonDir,
|
|
18
|
+
export function planKilo(canonDir, targetDir, codeGraph = 'graphify', codeIntelligence = 'off') {
|
|
19
19
|
const manifest = loadManifest(join(canonDir, 'manifest.yaml'));
|
|
20
20
|
const baseline = readFileSync(join(canonDir, 'AGENTS.md'), 'utf8');
|
|
21
21
|
const actions = manifest.skills.flatMap(skill => skillPackageActions(canonDir, skill, 'kilo'));
|
|
@@ -31,7 +31,7 @@ export function planKilo(canonDir, _targetDir, codeGraph = 'graphify') {
|
|
|
31
31
|
content: JSON.stringify({
|
|
32
32
|
$schema: 'https://app.kilo.ai/config.json',
|
|
33
33
|
instructions: ['AGENTS.md', '.yoke/context/*.md'],
|
|
34
|
-
mcp: openCodeMcpServers(codeGraph),
|
|
34
|
+
mcp: openCodeMcpServers(codeGraph, codeIntelligence, targetDir),
|
|
35
35
|
}, null, 2) + '\n',
|
|
36
36
|
reason: 'Kilo instructions + MCP servers',
|
|
37
37
|
}, {
|
|
@@ -15,7 +15,7 @@ tools:
|
|
|
15
15
|
|
|
16
16
|
Review the observed diff and test evidence. Do not modify files. Return only actionable findings grounded in evidence.
|
|
17
17
|
`;
|
|
18
|
-
export function planOpenCode(canonDir,
|
|
18
|
+
export function planOpenCode(canonDir, targetDir, codeGraph = 'graphify', codeIntelligence = 'off') {
|
|
19
19
|
const manifest = loadManifest(join(canonDir, 'manifest.yaml'));
|
|
20
20
|
const baseline = readFileSync(join(canonDir, 'AGENTS.md'), 'utf8');
|
|
21
21
|
const actions = manifest.skills.flatMap(skill => skillPackageActions(canonDir, skill, 'opencode'));
|
|
@@ -31,7 +31,7 @@ export function planOpenCode(canonDir, _targetDir, codeGraph = 'graphify') {
|
|
|
31
31
|
content: JSON.stringify({
|
|
32
32
|
$schema: 'https://opencode.ai/config.json',
|
|
33
33
|
instructions: ['AGENTS.md', '.yoke/context/*.md'],
|
|
34
|
-
mcp: openCodeMcpServers(codeGraph),
|
|
34
|
+
mcp: openCodeMcpServers(codeGraph, codeIntelligence, targetDir),
|
|
35
35
|
}, null, 2) + '\n',
|
|
36
36
|
reason: 'OpenCode instructions + MCP servers',
|
|
37
37
|
}, {
|
|
@@ -4,7 +4,7 @@ import { loadManifest } from '../../canon/manifest.js';
|
|
|
4
4
|
import { rtkInstruction } from '../tools.js';
|
|
5
5
|
import { PRESERVE_SCAFFOLD } from '../preserve.js';
|
|
6
6
|
import { skillPackageActions } from '../skill-actions.js';
|
|
7
|
-
export function planPi(canonDir, _targetDir, _codeGraph = 'graphify') {
|
|
7
|
+
export function planPi(canonDir, _targetDir, _codeGraph = 'graphify', _codeIntelligence = 'off') {
|
|
8
8
|
const manifest = loadManifest(join(canonDir, 'manifest.yaml'));
|
|
9
9
|
const baseline = readFileSync(join(canonDir, 'AGENTS.md'), 'utf8');
|
|
10
10
|
const actions = manifest.skills.flatMap(skill => skillPackageActions(canonDir, skill, 'pi'));
|
|
@@ -17,7 +17,7 @@ export function planPi(canonDir, _targetDir, _codeGraph = 'graphify') {
|
|
|
17
17
|
kind: 'write',
|
|
18
18
|
target: '.pi/settings.json',
|
|
19
19
|
merge: true,
|
|
20
|
-
content: JSON.stringify({ skills: ['
|
|
20
|
+
content: JSON.stringify({ skills: ['./skills'] }, null, 2) + '\n',
|
|
21
21
|
reason: 'Pi project skill discovery',
|
|
22
22
|
});
|
|
23
23
|
return actions;
|
|
@@ -8,7 +8,7 @@ import { skillPackageActions } from '../skill-actions.js';
|
|
|
8
8
|
function tomlString(s) {
|
|
9
9
|
return '"""\n' + s.replace(/\\/g, '\\\\').replace(/"""/g, '\\"\\"\\"') + '\n"""';
|
|
10
10
|
}
|
|
11
|
-
export function planQwen(canonDir,
|
|
11
|
+
export function planQwen(canonDir, targetDir, codeGraph = 'graphify', codeIntelligence = 'off') {
|
|
12
12
|
const manifest = loadManifest(join(canonDir, 'manifest.yaml'));
|
|
13
13
|
const actions = [];
|
|
14
14
|
// QWEN.md: baseline + instruction for commands outside the rewrite hook.
|
|
@@ -56,7 +56,7 @@ export function planQwen(canonDir, _targetDir, codeGraph = 'graphify') {
|
|
|
56
56
|
target: '.qwen/settings.json',
|
|
57
57
|
merge: true,
|
|
58
58
|
content: JSON.stringify({
|
|
59
|
-
mcpServers: mcpServers(codeGraph),
|
|
59
|
+
mcpServers: mcpServers(codeGraph, codeIntelligence, targetDir),
|
|
60
60
|
context: { fileName: ['AGENTS.md', 'QWEN.md'] },
|
|
61
61
|
hooks: { PreToolUse: [{ matcher: '^run_shell_command$', hooks: [{ name: 'yoke-rtk', type: 'command', command: 'node .qwen/hooks/qwen-rtk-hook.mjs' }] }] },
|
|
62
62
|
}, null, 2) + '\n',
|
|
@@ -52,7 +52,7 @@ export function skillPackageActions(canonDir, skill, provider) {
|
|
|
52
52
|
.map(file => ({
|
|
53
53
|
kind: 'write',
|
|
54
54
|
target: `${roots[provider]}/${skill.id}/${file.relativePath}`,
|
|
55
|
-
content: (provider === 'claude' || provider === 'qwen') && skill.invocation === 'manual' && file.relativePath === 'SKILL.md'
|
|
55
|
+
content: (provider === 'claude' || provider === 'qwen' || provider === 'pi') && skill.invocation === 'manual' && file.relativePath === 'SKILL.md'
|
|
56
56
|
? manualClaudeSkill(file.content, skill)
|
|
57
57
|
: portableContent(file),
|
|
58
58
|
executable: file.executable,
|
package/dist/retrofit/tools.js
CHANGED
|
@@ -8,15 +8,17 @@ const CODE_GRAPH_SERVERS = {
|
|
|
8
8
|
args: ['start-mcp-server', '--open-web-dashboard', 'false'],
|
|
9
9
|
},
|
|
10
10
|
};
|
|
11
|
-
export function mcpServers(codeGraph = 'graphify') {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
export function mcpServers(codeGraph = 'graphify', codeIntelligence = 'off', targetDir = '.') {
|
|
12
|
+
if (codeIntelligence !== 'off')
|
|
13
|
+
return {
|
|
14
|
+
'code-intelligence': { command: 'yoke', args: ['code-intelligence-server', `--workspace=${targetDir}`, `--mode=${codeIntelligence}`] },
|
|
15
|
+
playwright: { command: 'npx', args: ['@playwright/mcp@latest'] },
|
|
16
|
+
};
|
|
17
|
+
return { [codeGraph]: CODE_GRAPH_SERVERS[codeGraph], playwright: { command: 'npx', args: ['@playwright/mcp@latest'] } };
|
|
16
18
|
}
|
|
17
19
|
/** OpenCode-family CLIs use an object with a local command array, not Claude's mcpServers shape. */
|
|
18
|
-
export function openCodeMcpServers(codeGraph = 'graphify') {
|
|
19
|
-
return Object.fromEntries(Object.entries(mcpServers(codeGraph)).map(([name, server]) => [name, {
|
|
20
|
+
export function openCodeMcpServers(codeGraph = 'graphify', codeIntelligence = 'off', targetDir = '.') {
|
|
21
|
+
return Object.fromEntries(Object.entries(mcpServers(codeGraph, codeIntelligence, targetDir)).map(([name, server]) => [name, {
|
|
20
22
|
type: 'local',
|
|
21
23
|
command: [server.command, ...server.args],
|
|
22
24
|
enabled: true,
|
package/dist/setup/command.js
CHANGED
|
@@ -75,6 +75,7 @@ export async function runSetup(targetDir, opts = {}) {
|
|
|
75
75
|
? detected.agents
|
|
76
76
|
: modelProviders.length ? ['qwen'] : [host ?? 'claude'];
|
|
77
77
|
const defaultGraph = opts.codeGraph ?? existing?.codeGraph ?? 'graphify';
|
|
78
|
+
const defaultCodeIntelligence = opts.codeIntelligence ?? existing?.codeIntelligence?.mode ?? 'off';
|
|
78
79
|
const defaultLoop = opts.loop ?? existing?.loop.enabled ?? true;
|
|
79
80
|
const defaultRunner = opts.runner ?? existing?.runner?.agent ?? (host && defaultAgents.includes(host) ? host : defaultAgents[0] ?? host ?? 'claude');
|
|
80
81
|
const defaultPolicy = opts.decisionPolicy ?? existing?.loop.decisionPolicy ?? (existing?.loop.onAmbiguity === 'abort' ? 'critical' : 'auto');
|
|
@@ -90,6 +91,7 @@ export async function runSetup(targetDir, opts = {}) {
|
|
|
90
91
|
try {
|
|
91
92
|
let agents = defaultAgents;
|
|
92
93
|
let codeGraph = defaultGraph;
|
|
94
|
+
let codeIntelligence = defaultCodeIntelligence;
|
|
93
95
|
let loop = defaultLoop;
|
|
94
96
|
let runner = defaultRunner;
|
|
95
97
|
let decisionPolicy = defaultPolicy;
|
|
@@ -112,7 +114,7 @@ export async function runSetup(targetDir, opts = {}) {
|
|
|
112
114
|
agents = [...agents, 'qwen'];
|
|
113
115
|
if (!agents.includes(runner))
|
|
114
116
|
agents = [...agents, runner];
|
|
115
|
-
const code = runRetrofit(targetDir, { loop, agents, codeGraph, host });
|
|
117
|
+
const code = runRetrofit(targetDir, { loop, agents, codeGraph, codeIntelligence, host });
|
|
116
118
|
if (code !== 0)
|
|
117
119
|
return code;
|
|
118
120
|
applyActions(presetActions, targetDir, { backupDir: join(targetDir, '.yoke', 'backups', `model-presets-${Date.now()}`) });
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Agent and skill hardening — 2026-09-16
|
|
2
|
+
|
|
3
|
+
This is a bounded source/contract audit, not a guarantee of defect-free operation or a seven-provider model benchmark. Pi was already supported in 1.13.0. The npm release observed at the start of this audit was 1.15.0; the fixes below are included in the 1.15.1 release changeset.
|
|
4
|
+
|
|
5
|
+
## Implemented fixes
|
|
6
|
+
|
|
7
|
+
- Pi telemetry now sums finalized assistant messages across tool turns. Streaming snapshots, turn-end echoes and agent-end transcript replays are not double-counted. Model switches remain visible; missing turn measurements produce partial usage, not a complete total. Optional cost/cache fields are reported as full totals only when every finalized turn measures them. An interrupted unfinalized turn remains unknown.
|
|
8
|
+
- Pi successful tool completions and OpenCode/Kilo completed tool envelopes reset the progress watchdog. Failed tools and streaming updates do not. Total, idle and progress budgets remain enforced; no timeout defaults were relaxed.
|
|
9
|
+
- Structured results from Claude, Codex, Gemini, OpenCode and Kilo are rejected when followed by a terminal error. Pi errored/aborted assistant messages cannot supply a verdict. Claude child-agent events cannot masquerade as parent verdicts or parent usage. Qwen's existing parent/result handling remains in place.
|
|
10
|
+
- OpenCode/Kilo conflicting effort/variant aliases fail before process launch; matching aliases produce one flag.
|
|
11
|
+
- Pi settings use a skill path relative to `.pi/settings.json`. Manual-only skills receive Pi's native `disable-model-invocation` marker, just as Claude/Qwen already did.
|
|
12
|
+
- Schedule estimation simulates typical/lower/upper per-story durations with dependency and write-conflict constraints. A sparse per-story sample no longer inherits stronger confidence from unrelated pooled samples. These are empirical scenarios, not calibrated probabilities or deadlines.
|
|
13
|
+
- Eight missing supporting files are now packaged and linked: three delegation templates, a review template, three debugging guides and a testing anti-pattern guide. Shared instructions prohibit nested orchestration inside Yoke workers, explain host capability fallbacks, preserve acceptance/review gates, and distinguish budgets from time estimates. Removed unsupported claims about guaranteed subagent quality and fixed debugging speed/success rates.
|
|
14
|
+
- The Code Intelligence coordinator unit test now replaces the separate sandbox adapter too, avoiding accidental real Serena startup. It checks an actual rename in the isolated transaction output, approval gating, and that the source project remains untouched (apply does not merge).
|
|
15
|
+
|
|
16
|
+
## Compatibility and operation
|
|
17
|
+
|
|
18
|
+
Existing projects can refresh the canonical skills with `yoke retrofit . --agent=all`, or select one harness. Inspect the merge-aware retrofit diff and backups. Existing custom skill paths remain user-owned; remove an obsolete `.pi/skills` settings entry only after confirming it is the old Yoke-generated entry. The correct settings-relative entry is `./skills`.
|
|
19
|
+
|
|
20
|
+
Current Pi also requires project trust before loading project-local skills in headless runs. Trust is an explicit user decision, not something Yoke grants automatically. See [the harness guide](HARNESSES.md). Tool allowlists are not an OS sandbox: extensions and native configuration still require review.
|
|
21
|
+
|
|
22
|
+
## Evidence and limits
|
|
23
|
+
|
|
24
|
+
Final local validation: 1,281 tests passed, two platform-specific tests skipped, 140 test files passed. TypeScript lint/build, Canon validation, README metadata check and npm package dry run passed; npm audit reported zero vulnerabilities. A follow-up Canon run after the supporting-document additions passed (36 passed, one platform-specific skip). The subsequent release request targets 1.15.1; publication status is recorded by the matching GitHub release and npm registry, not inferred from these local checks.
|
|
25
|
+
|
|
26
|
+
Regression cases were observed failing before their fixes, then passing in targeted runs. Coverage includes multi-turn/chunked Pi usage, missing measurements, model changes, terminal errors, effort conflicts, sparse timing samples, manual invocation and packaged templates. Canon validation scans all registered skill packages; the content audit focused on orchestration, execution and verification instructions, not a formal proof of every skill's behavior.
|
|
27
|
+
|
|
28
|
+
The local Pi 0.85.1 CLI help confirms JSON mode, tool allowlists, provider/model/thinking selection and project-trust controls. Qwen and Kilo were not found on this machine's PATH. No paid/authenticated seven-agent task matrix, cross-platform native sandbox comparison, measured speedup or quality benchmark was performed. Faster feedback is a workflow recommendation, not a measured improvement claim. Existing full verification gates remain required.
|
|
29
|
+
|
|
30
|
+
A read-only README provenance scan found no supported C2PA structure and completed the supported scan. Cryptographic verification and signer trust remain unknown because no conforming verifier/trust policy was supplied. Metadata privacy was unsupported for this text format. Proprietary keyed watermark detection was unavailable; no authorship conclusion follows. No marks were removed.
|
|
31
|
+
|
|
32
|
+
Primary references: [Pi JSON mode](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/json.md), [Pi event types](https://github.com/earendil-works/pi/blob/main/packages/agent/src/types.ts), [Pi skills](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/skills.md), [OpenCode run implementation](https://github.com/anomalyco/opencode/blob/dev/packages/opencode/src/cli/cmd/run.ts).
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Yoke Code Intelligence
|
|
2
|
+
|
|
3
|
+
Yoke integrates Graphify, Graft and Serena as a federation. Their repositories are not merged into Yoke and their individual MCP tools are not published to agents. Yoke publishes one snapshot-bound facade with six tools:
|
|
4
|
+
|
|
5
|
+
`code_context`, `code_symbol`, `code_trace`, `code_impact`, `code_edit_preview`, and `code_edit_apply`.
|
|
6
|
+
|
|
7
|
+
## Enable it
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
yoke setup . --yes --code-intelligence=active
|
|
11
|
+
# or, for an existing project:
|
|
12
|
+
yoke retrofit . --code-intelligence=active
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
The default is `off`, which preserves the previous `codeGraph` behavior. `shadow` enables a read-only canary and keeps all edits blocked. `active` enables read operations and snapshot-bound edit previews. The generated host configuration exposes only Yoke's facade, so an agent cannot accidentally combine duplicate backend tools.
|
|
16
|
+
|
|
17
|
+
## Backend roles and tested pins
|
|
18
|
+
|
|
19
|
+
| Backend | Role | Launch command | Pin checked during integration |
|
|
20
|
+
|---|---|---|---|
|
|
21
|
+
| [Graft](https://github.com/trailhq/Graft) | structural code search, repo map, call graph | `graft mcp` | `05760b07abc0e427f5af8ad378889ee402c5afc6` / 0.17.0 |
|
|
22
|
+
| [Graphify](https://github.com/Graphify-Labs/graphify) | architecture/document knowledge graph | `python -m graphify.serve` | `67f99bd0059dd1bac9e44382907ef9f10098b39f` / 0.9.56 |
|
|
23
|
+
| [Serena](https://github.com/oraios/serena) | LSP symbols, references, implementations and semantic edits | `serena start-mcp-server --project-from-cwd --context codex` | `701e7c843f46c6a649203a488cece1bf19f1df90` / 1.7.1-dev |
|
|
24
|
+
|
|
25
|
+
Install and pin these tools in the project or developer environment before enabling the facade. Yoke never installs `latest` during a task. Missing tools produce an explicit `partial` response with backend warnings and provenance; they are never interpreted as “no references”.
|
|
26
|
+
|
|
27
|
+
## Safety contract
|
|
28
|
+
|
|
29
|
+
- Every request is tied to a workspace and a content-addressed snapshot of committed and uncommitted, non-sensitive files.
|
|
30
|
+
- Paths are workspace-relative; traversal, symlinks, `.env`/key material and policy-excluded paths are rejected.
|
|
31
|
+
- Read calls are bounded by timeout and output budgets. Results carry backend, version, source path, content hash, resolution and freshness.
|
|
32
|
+
- Edits run in a disposable Yoke sandbox. Preview writes a durable plan and diff but requires an approval record created by the Yoke control layer.
|
|
33
|
+
- Apply rechecks the snapshot, plan expiry, plan hash, an exclusive transaction lock and idempotency. It produces a managed worktree and does not commit or merge; the existing Yoke loop and gates remain authoritative for integration.
|
|
34
|
+
- Network access is not needed by the facade. Backend installation and any backend-specific network behavior remain an explicit environment concern.
|
|
35
|
+
|
|
36
|
+
For a host that has no native MCP support (currently Pi), use the normal Yoke loop and bounded CLI/skill adapter. Yoke does not invent a second MCP transport for that host.
|
package/docs/HARNESSES.md
CHANGED
|
@@ -4,6 +4,10 @@ Yoke 1.13.0 adds first-class adapters for OpenCode, Kilo and Pi coding agent. Th
|
|
|
4
4
|
|
|
5
5
|
This is a CLI integration, not an authentication bundle. Install the selected harness, log in or configure its API provider, and verify it independently before starting a Yoke loop.
|
|
6
6
|
|
|
7
|
+
For current Pi installations, the official package is `@earendil-works/pi-coding-agent` (the executable remains `pi`). Pi support has existed since Yoke 1.13.0; no additional adapter is required.
|
|
8
|
+
|
|
9
|
+
Pi project trust is separate from Yoke's permission profile. Current Pi ignores project-local settings and skills in headless mode unless the project is trusted. Review the project resources, open Pi interactively, use `/trust`, then restart before running Yoke. Yoke does not silently grant project trust or enable arbitrary project extensions. For isolated worktrees, review the applicable trust scope as well. See [Pi project trust](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/README.md#project-trust).
|
|
10
|
+
|
|
7
11
|
## Setup and retrofit
|
|
8
12
|
|
|
9
13
|
Select one of the new harnesses explicitly:
|
|
@@ -58,6 +62,8 @@ runner:
|
|
|
58
62
|
|
|
59
63
|
This becomes `--provider openai --model gpt-5.6 --thinking high`. Pi calls `variant` and `reasoningEffort` the same underlying thinking-level selection; configuring both with different values is rejected.
|
|
60
64
|
|
|
65
|
+
OpenCode and Kilo likewise map these two fields to a single `--variant`: matching values are emitted once and conflicting values are rejected before spawning a process.
|
|
66
|
+
|
|
61
67
|
The same fields are available on routing workers and quality critic/repair roles. Routing evidence is keyed by harness, provider, model, reasoning effort and variant, so a model profile does not inherit another profile's success history.
|
|
62
68
|
|
|
63
69
|
## Permission profiles and honest limits
|
package/gemini-extension.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "yoke",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.15.1",
|
|
4
4
|
"description": "Cross-agent coding harness for seven supported CLIs: curated skill canon, mechanical safety gates, autonomous loop with proof artifacts. CLI: npm i -g @hecer/yoke",
|
|
5
5
|
"contextFileName": "GEMINI-EXTENSION.md"
|
|
6
6
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hecer/yoke",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.15.1",
|
|
4
4
|
"description": "One harness, seven agents, zero trust in \"done\" — cross-agent coding harness for Claude Code, Codex CLI, Gemini CLI, Qwen Code, OpenCode, Kilo and Pi: one skill canon, mechanical safety gates, an autonomous loop with screenshot/video proofs.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|