@hcflabs/pi-subagent 0.0.0-stage → 27.10.7
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/CONTEXT.md +77 -0
- package/LICENSE +22 -0
- package/README.md +282 -2
- package/dist/composition.d.ts +11 -0
- package/dist/composition.js +104 -0
- package/dist/direct-herdr.d.ts +71 -0
- package/dist/direct-herdr.js +527 -0
- package/dist/discard.d.ts +51 -0
- package/dist/discard.js +218 -0
- package/dist/display-text.d.ts +5 -0
- package/dist/display-text.js +9 -0
- package/dist/ephemeral.d.ts +79 -0
- package/dist/ephemeral.js +916 -0
- package/dist/git-process.d.ts +8 -0
- package/dist/git-process.js +25 -0
- package/dist/git-runtime.d.ts +115 -0
- package/dist/git-runtime.js +489 -0
- package/dist/herdr-runtime.d.ts +147 -0
- package/dist/herdr-runtime.js +1366 -0
- package/dist/index.d.ts +170 -0
- package/dist/index.js +587 -0
- package/dist/integration-git.d.ts +63 -0
- package/dist/integration-git.js +504 -0
- package/dist/launch-runtime.d.ts +54 -0
- package/dist/launch-runtime.js +178 -0
- package/dist/mcp-role.d.ts +12 -0
- package/dist/mcp-role.js +152 -0
- package/dist/process-lease.d.ts +5 -0
- package/dist/process-lease.js +43 -0
- package/dist/process.d.ts +9 -0
- package/dist/process.js +11 -0
- package/dist/prune.d.ts +78 -0
- package/dist/prune.js +205 -0
- package/dist/review-evidence.d.ts +17 -0
- package/dist/review-evidence.js +221 -0
- package/dist/runner.d.ts +431 -0
- package/dist/runner.js +3380 -0
- package/dist/schema.d.ts +2560 -0
- package/dist/schema.js +1365 -0
- package/dist/store.d.ts +66 -0
- package/dist/store.js +394 -0
- package/dist/worktree.d.ts +65 -0
- package/dist/worktree.js +288 -0
- package/docs/orchestration-architecture.html +138 -0
- package/docs/orchestration-architecture.svg +135 -0
- package/docs/orchestration.md +577 -0
- package/docs/worker-lifecycle.html +97 -0
- package/docs/worker-lifecycle.svg +86 -0
- package/example.png +0 -0
- package/examples/roles/implementer.md +25 -0
- package/examples/roles/reviewer.md +25 -0
- package/examples/roles/scout.md +25 -0
- package/extensions/admission.ts +82 -0
- package/extensions/config.ts +19 -0
- package/extensions/git-read.ts +126 -0
- package/extensions/isolated.ts +945 -0
- package/extensions/model-class-policy.ts +1 -0
- package/extensions/result-transport.ts +236 -0
- package/extensions/role-mcp.ts +14 -0
- package/extensions/role-tools.ts +178 -0
- package/extensions/subagent-command.ts +699 -0
- package/extensions/subagent.ts +958 -0
- package/extensions/workflow.ts +336 -0
- package/package.json +84 -4
- package/prompts/review.md +20 -0
- package/skills/pi-subagent/SKILL.md +41 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
|
|
5
|
+
<title>Checked subagent lifecycle and coordinator recovery</title>
|
|
6
|
+
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
|
7
|
+
<style>
|
|
8
|
+
* { box-sizing: border-box; } body { margin: 0; padding: 32px; background: #f0eee9; color: #101828; font-family: 'Geist', sans-serif; }
|
|
9
|
+
main { max-width: 1280px; margin: auto; } h1 { font: 400 28px 'Instrument Serif', serif; margin: 0 0 16px; }
|
|
10
|
+
svg { display: block; width: 100%; height: auto; }
|
|
11
|
+
</style>
|
|
12
|
+
</head>
|
|
13
|
+
<body><main><h1>Checked subagent lifecycle and coordinator recovery</h1>
|
|
14
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="1280" height="720" viewBox="0 0 1280 720" role="img" aria-labelledby="worker-lifecycle-title worker-lifecycle-desc">
|
|
15
|
+
<title id="worker-lifecycle-title">Checked subagent lifecycle and coordinator recovery</title>
|
|
16
|
+
<desc id="worker-lifecycle-desc">Main delegates through its coordinator extension to an isolated Herdr subagent, then stages a checked candidate, validates the combined tip, and promotes only after validation passes; saved worker attention sends a follow-up while siblings may run, the wave reports separately when settled, and a coordinator interruption requires Main to run /subagent recover once and inspect saved state without automatic replay or a guaranteed follow-up.</desc>
|
|
17
|
+
<defs>
|
|
18
|
+
<style>@import url('https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap');</style>
|
|
19
|
+
<marker id="worker-arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0,8 3,0 6" fill="#4c5665"/></marker>
|
|
20
|
+
<marker id="worker-arrow-accent" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0,8 3,0 6" fill="#1d4ed8"/></marker>
|
|
21
|
+
</defs>
|
|
22
|
+
<rect width="1280" height="720" fill="#f0eee9"/>
|
|
23
|
+
<text x="48" y="60" fill="#6a7282" font-family="'Geist Mono',monospace" font-size="8" letter-spacing="1.5">PI SUBAGENT / ONE ISOLATED WORKER</text>
|
|
24
|
+
<text x="48" y="99" fill="#101828" font-family="'Instrument Serif',serif" font-size="28">A worker commits. Main decides what is integrated.</text>
|
|
25
|
+
<text x="48" y="126" fill="#4c5665" font-family="'Geist',sans-serif" font-size="12">The coordinator is extension code inside Main's Pi process, not another agent.</text>
|
|
26
|
+
<rect x="64" y="158" width="1138" height="58" rx="8" fill="#e6e4de"/>
|
|
27
|
+
<text x="84" y="181" fill="#101828" font-family="'Geist',sans-serif" font-size="12" font-weight="600">One worker shown; siblings can keep working after this candidate is ready.</text>
|
|
28
|
+
<text x="84" y="200" fill="#4c5665" font-family="'Geist',sans-serif" font-size="12">State is persisted by the coordinator. Herdr agent status alone does not prove a clean, checked candidate.</text>
|
|
29
|
+
<!-- Connectors before nodes. Primary rail is the checked path; dashed paths retain uncertainty. -->
|
|
30
|
+
<line x1="234" y1="350" x2="306" y2="350" stroke="#4c5665" marker-end="url(#worker-arrow)"/>
|
|
31
|
+
<line x1="476" y1="350" x2="548" y2="350" stroke="#4c5665" marker-end="url(#worker-arrow)"/>
|
|
32
|
+
<line x1="718" y1="350" x2="790" y2="350" stroke="#4c5665" marker-end="url(#worker-arrow)"/>
|
|
33
|
+
<line x1="960" y1="350" x2="1032" y2="350" stroke="#1d4ed8" stroke-width="1.4" marker-end="url(#worker-arrow-accent)"/>
|
|
34
|
+
<path d="M633 405 V434 Q633 442 625 442 H408 Q400 442 400 450 V512" fill="none" stroke="#4c5665" stroke-dasharray="5,4" marker-end="url(#worker-arrow)"/>
|
|
35
|
+
<line x1="500" y1="546" x2="526" y2="546" stroke="#4c5665" marker-end="url(#worker-arrow)"/>
|
|
36
|
+
<line x1="875" y1="405" x2="875" y2="512" stroke="#4c5665" stroke-dasharray="5,4" marker-end="url(#worker-arrow)"/>
|
|
37
|
+
<line x1="968" y1="546" x2="994" y2="546" stroke="#4c5665" marker-end="url(#worker-arrow)"/>
|
|
38
|
+
<!-- Transition labels sit above, not on, their strokes. -->
|
|
39
|
+
<g fill="#4c5665" font-family="'Geist Mono',monospace" font-size="8" text-anchor="middle">
|
|
40
|
+
<rect x="242" y="324" width="56" height="12" rx="2" fill="#f0eee9"/><text x="270" y="333">START</text>
|
|
41
|
+
<rect x="484" y="324" width="56" height="12" rx="2" fill="#f0eee9"/><text x="512" y="333">LAUNCH</text>
|
|
42
|
+
<rect x="726" y="324" width="56" height="12" rx="2" fill="#f0eee9"/><text x="754" y="333">CHECK</text>
|
|
43
|
+
<rect x="968" y="324" width="56" height="12" rx="2" fill="#f0eee9"/><text x="996" y="333" fill="#1d4ed8">READY</text>
|
|
44
|
+
</g>
|
|
45
|
+
<!-- Five phases, with ownership above each. -->
|
|
46
|
+
<g fill="#6a7282" font-family="'Geist Mono',monospace" font-size="8" letter-spacing="1" text-anchor="middle">
|
|
47
|
+
<text x="149" y="272">MAIN AGENT</text><text x="391" y="272">COORDINATOR</text>
|
|
48
|
+
<text x="633" y="272">HERDR SUBAGENT</text><text x="875" y="272">COORDINATOR</text>
|
|
49
|
+
<text x="1117" y="272">MAIN AGENT</text>
|
|
50
|
+
</g>
|
|
51
|
+
<g fill="#ffffff" stroke="#101828">
|
|
52
|
+
<rect x="64" y="295" width="170" height="110" rx="8"/>
|
|
53
|
+
<rect x="306" y="295" width="170" height="110" rx="8"/>
|
|
54
|
+
<rect x="548" y="295" width="170" height="110" rx="8"/>
|
|
55
|
+
<rect x="790" y="295" width="170" height="110" rx="8" fill="#1d4ed8" fill-opacity="0.08" stroke="#1d4ed8"/>
|
|
56
|
+
<rect x="1032" y="295" width="170" height="110" rx="8"/>
|
|
57
|
+
</g>
|
|
58
|
+
<g fill="#101828" font-family="'Geist',sans-serif" font-size="12" font-weight="600" text-anchor="middle">
|
|
59
|
+
<text x="149" y="339">Delegate task</text><text x="391" y="339">Save + launch</text>
|
|
60
|
+
<text x="633" y="339">Implement + commit</text><text x="875" y="339">Check + seal</text>
|
|
61
|
+
<text x="1117" y="325">Stage candidate</text>
|
|
62
|
+
</g>
|
|
63
|
+
<g fill="#4c5665" font-family="'Geist',sans-serif" font-size="11" text-anchor="middle">
|
|
64
|
+
<text x="149" y="370">Bounded request</text><text x="391" y="370">Durable ID + lease</text>
|
|
65
|
+
<text x="633" y="370">Isolated worktree</text><text x="875" y="370">Clean tip + task checks</text>
|
|
66
|
+
<text x="1117" y="350">Validate combined tip</text>
|
|
67
|
+
<text x="1117" y="374">Promote after passing</text>
|
|
68
|
+
<text x="1117" y="390">validation</text>
|
|
69
|
+
</g>
|
|
70
|
+
<!-- Recovery bands: an incomplete worker and a stopped coordinator are distinct. -->
|
|
71
|
+
<rect x="286" y="468" width="448" height="146" rx="8" fill="#e6e4de"/>
|
|
72
|
+
<rect x="756" y="468" width="446" height="146" rx="8" fill="#e6e4de"/>
|
|
73
|
+
<g fill="#6a7282" font-family="'Geist Mono',monospace" font-size="8" letter-spacing="1">
|
|
74
|
+
<text x="310" y="492">WORKER FAILURE</text><text x="778" y="492">COORDINATOR STOPS / ANY PHASE</text>
|
|
75
|
+
</g>
|
|
76
|
+
<g fill="#ffffff" stroke="#4c5665" stroke-dasharray="5,4">
|
|
77
|
+
<rect x="310" y="512" width="190" height="68" rx="8"/>
|
|
78
|
+
<rect x="526" y="512" width="192" height="68" rx="8"/>
|
|
79
|
+
<rect x="778" y="512" width="190" height="68" rx="8"/>
|
|
80
|
+
<rect x="994" y="512" width="190" height="68" rx="8"/>
|
|
81
|
+
</g>
|
|
82
|
+
<g fill="#101828" font-family="'Geist',sans-serif" font-size="12" font-weight="600" text-anchor="middle">
|
|
83
|
+
<text x="405" y="532">No checked candidate</text><text x="622" y="540">Wave result</text>
|
|
84
|
+
<text x="873" y="540">No guaranteed message</text>
|
|
85
|
+
</g>
|
|
86
|
+
<g fill="#4c5665" font-family="'Geist',sans-serif" font-size="10" text-anchor="middle">
|
|
87
|
+
<text x="405" y="550">Saved attention → follow-up</text><text x="405" y="566">Siblings may still run</text>
|
|
88
|
+
<text x="622" y="560">Report when settled</text>
|
|
89
|
+
<text x="873" y="560">Last write survives</text>
|
|
90
|
+
<text x="1089" y="540">Run <tspan font-family="'Geist Mono',monospace">/subagent recover</tspan></text>
|
|
91
|
+
<text x="1089" y="560">once; inspect saved state</text>
|
|
92
|
+
</g>
|
|
93
|
+
<line x1="48" y1="648" x2="1232" y2="648" stroke="#101828" stroke-opacity="0.12"/>
|
|
94
|
+
<text x="48" y="675" fill="#6a7282" font-family="'Geist Mono',monospace" font-size="8" letter-spacing="1">GUARD</text>
|
|
95
|
+
<text x="128" y="675" fill="#101828" font-family="'Geist',sans-serif" font-size="12">Status reports exact saved evidence; uncertain steps are never replayed automatically.</text>
|
|
96
|
+
<text x="128" y="696" fill="#4c5665" font-family="'Geist',sans-serif" font-size="11">A ready candidate may be staged while siblings run. Promotion still requires full combined validation.</text>
|
|
97
|
+
</svg></main></body></html>
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="1280" height="720" viewBox="0 0 1280 720" role="img" aria-labelledby="worker-lifecycle-title worker-lifecycle-desc" id="worker-lifecycle-root">
|
|
3
|
+
<title id="worker-lifecycle-title">Checked subagent lifecycle and coordinator recovery</title>
|
|
4
|
+
<desc id="worker-lifecycle-desc">Main delegates through its coordinator extension to an isolated Herdr subagent, then stages a checked candidate, validates the combined tip, and promotes only after validation passes; saved worker attention sends a follow-up while siblings may run, the wave reports separately when settled, and a coordinator interruption requires Main to run /subagent recover once and inspect saved state without automatic replay or a guaranteed follow-up.</desc>
|
|
5
|
+
<defs>
|
|
6
|
+
<style>#worker-lifecycle-root { color: #101828; font-family: 'Geist', sans-serif; }</style>
|
|
7
|
+
<style>@import url('https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap');</style>
|
|
8
|
+
<marker id="worker-lifecycle-worker-arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0,8 3,0 6" fill="#4c5665"/></marker>
|
|
9
|
+
<marker id="worker-lifecycle-worker-arrow-accent" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0,8 3,0 6" fill="#1d4ed8"/></marker>
|
|
10
|
+
</defs>
|
|
11
|
+
<rect width="1280" height="720" fill="#f0eee9"/>
|
|
12
|
+
<text x="48" y="60" fill="#6a7282" font-family="'Geist Mono',monospace" font-size="8" letter-spacing="1.5">PI SUBAGENT / ONE ISOLATED WORKER</text>
|
|
13
|
+
<text x="48" y="99" fill="#101828" font-family="'Instrument Serif',serif" font-size="28">A worker commits. Main decides what is integrated.</text>
|
|
14
|
+
<text x="48" y="126" fill="#4c5665" font-family="'Geist',sans-serif" font-size="12">The coordinator is extension code inside Main's Pi process, not another agent.</text>
|
|
15
|
+
<rect x="64" y="158" width="1138" height="58" rx="8" fill="#e6e4de"/>
|
|
16
|
+
<text x="84" y="181" fill="#101828" font-family="'Geist',sans-serif" font-size="12" font-weight="600">One worker shown; siblings can keep working after this candidate is ready.</text>
|
|
17
|
+
<text x="84" y="200" fill="#4c5665" font-family="'Geist',sans-serif" font-size="12">State is persisted by the coordinator. Herdr agent status alone does not prove a clean, checked candidate.</text>
|
|
18
|
+
<!-- Connectors before nodes. Primary rail is the checked path; dashed paths retain uncertainty. -->
|
|
19
|
+
<line x1="234" y1="350" x2="306" y2="350" stroke="#4c5665" marker-end="url(#worker-lifecycle-worker-arrow)"/>
|
|
20
|
+
<line x1="476" y1="350" x2="548" y2="350" stroke="#4c5665" marker-end="url(#worker-lifecycle-worker-arrow)"/>
|
|
21
|
+
<line x1="718" y1="350" x2="790" y2="350" stroke="#4c5665" marker-end="url(#worker-lifecycle-worker-arrow)"/>
|
|
22
|
+
<line x1="960" y1="350" x2="1032" y2="350" stroke="#1d4ed8" stroke-width="1.4" marker-end="url(#worker-lifecycle-worker-arrow-accent)"/>
|
|
23
|
+
<path d="M633 405 V434 Q633 442 625 442 H408 Q400 442 400 450 V512" fill="none" stroke="#4c5665" stroke-dasharray="5,4" marker-end="url(#worker-lifecycle-worker-arrow)"/>
|
|
24
|
+
<line x1="500" y1="546" x2="526" y2="546" stroke="#4c5665" marker-end="url(#worker-lifecycle-worker-arrow)"/>
|
|
25
|
+
<line x1="875" y1="405" x2="875" y2="512" stroke="#4c5665" stroke-dasharray="5,4" marker-end="url(#worker-lifecycle-worker-arrow)"/>
|
|
26
|
+
<line x1="968" y1="546" x2="994" y2="546" stroke="#4c5665" marker-end="url(#worker-lifecycle-worker-arrow)"/>
|
|
27
|
+
<!-- Transition labels sit above, not on, their strokes. -->
|
|
28
|
+
<g fill="#4c5665" font-family="'Geist Mono',monospace" font-size="8" text-anchor="middle">
|
|
29
|
+
<rect x="242" y="324" width="56" height="12" rx="2" fill="#f0eee9"/><text x="270" y="333">START</text>
|
|
30
|
+
<rect x="484" y="324" width="56" height="12" rx="2" fill="#f0eee9"/><text x="512" y="333">LAUNCH</text>
|
|
31
|
+
<rect x="726" y="324" width="56" height="12" rx="2" fill="#f0eee9"/><text x="754" y="333">CHECK</text>
|
|
32
|
+
<rect x="968" y="324" width="56" height="12" rx="2" fill="#f0eee9"/><text x="996" y="333" fill="#1d4ed8">READY</text>
|
|
33
|
+
</g>
|
|
34
|
+
<!-- Five phases, with ownership above each. -->
|
|
35
|
+
<g fill="#6a7282" font-family="'Geist Mono',monospace" font-size="8" letter-spacing="1" text-anchor="middle">
|
|
36
|
+
<text x="149" y="272">MAIN AGENT</text><text x="391" y="272">COORDINATOR</text>
|
|
37
|
+
<text x="633" y="272">HERDR SUBAGENT</text><text x="875" y="272">COORDINATOR</text>
|
|
38
|
+
<text x="1117" y="272">MAIN AGENT</text>
|
|
39
|
+
</g>
|
|
40
|
+
<g fill="#ffffff" stroke="#101828">
|
|
41
|
+
<rect x="64" y="295" width="170" height="110" rx="8"/>
|
|
42
|
+
<rect x="306" y="295" width="170" height="110" rx="8"/>
|
|
43
|
+
<rect x="548" y="295" width="170" height="110" rx="8"/>
|
|
44
|
+
<rect x="790" y="295" width="170" height="110" rx="8" fill="#1d4ed8" fill-opacity="0.08" stroke="#1d4ed8"/>
|
|
45
|
+
<rect x="1032" y="295" width="170" height="110" rx="8"/>
|
|
46
|
+
</g>
|
|
47
|
+
<g fill="#101828" font-family="'Geist',sans-serif" font-size="12" font-weight="600" text-anchor="middle">
|
|
48
|
+
<text x="149" y="339">Delegate task</text><text x="391" y="339">Save + launch</text>
|
|
49
|
+
<text x="633" y="339">Implement + commit</text><text x="875" y="339">Check + seal</text>
|
|
50
|
+
<text x="1117" y="325">Stage candidate</text>
|
|
51
|
+
</g>
|
|
52
|
+
<g fill="#4c5665" font-family="'Geist',sans-serif" font-size="11" text-anchor="middle">
|
|
53
|
+
<text x="149" y="370">Bounded request</text><text x="391" y="370">Durable ID + lease</text>
|
|
54
|
+
<text x="633" y="370">Isolated worktree</text><text x="875" y="370">Clean tip + task checks</text>
|
|
55
|
+
<text x="1117" y="350">Validate combined tip</text>
|
|
56
|
+
<text x="1117" y="374">Promote after passing</text>
|
|
57
|
+
<text x="1117" y="390">validation</text>
|
|
58
|
+
</g>
|
|
59
|
+
<!-- Recovery bands: an incomplete worker and a stopped coordinator are distinct. -->
|
|
60
|
+
<rect x="286" y="468" width="448" height="146" rx="8" fill="#e6e4de"/>
|
|
61
|
+
<rect x="756" y="468" width="446" height="146" rx="8" fill="#e6e4de"/>
|
|
62
|
+
<g fill="#6a7282" font-family="'Geist Mono',monospace" font-size="8" letter-spacing="1">
|
|
63
|
+
<text x="310" y="492">WORKER FAILURE</text><text x="778" y="492">COORDINATOR STOPS / ANY PHASE</text>
|
|
64
|
+
</g>
|
|
65
|
+
<g fill="#ffffff" stroke="#4c5665" stroke-dasharray="5,4">
|
|
66
|
+
<rect x="310" y="512" width="190" height="68" rx="8"/>
|
|
67
|
+
<rect x="526" y="512" width="192" height="68" rx="8"/>
|
|
68
|
+
<rect x="778" y="512" width="190" height="68" rx="8"/>
|
|
69
|
+
<rect x="994" y="512" width="190" height="68" rx="8"/>
|
|
70
|
+
</g>
|
|
71
|
+
<g fill="#101828" font-family="'Geist',sans-serif" font-size="12" font-weight="600" text-anchor="middle">
|
|
72
|
+
<text x="405" y="532">No checked candidate</text><text x="622" y="540">Wave result</text>
|
|
73
|
+
<text x="873" y="540">No guaranteed message</text>
|
|
74
|
+
</g>
|
|
75
|
+
<g fill="#4c5665" font-family="'Geist',sans-serif" font-size="10" text-anchor="middle">
|
|
76
|
+
<text x="405" y="550">Saved attention → follow-up</text><text x="405" y="566">Siblings may still run</text>
|
|
77
|
+
<text x="622" y="560">Report when settled</text>
|
|
78
|
+
<text x="873" y="560">Last write survives</text>
|
|
79
|
+
<text x="1089" y="540">Run <tspan font-family="'Geist Mono',monospace">/subagent recover</tspan></text>
|
|
80
|
+
<text x="1089" y="560">once; inspect saved state</text>
|
|
81
|
+
</g>
|
|
82
|
+
<line x1="48" y1="648" x2="1232" y2="648" stroke="#101828" stroke-opacity="0.12"/>
|
|
83
|
+
<text x="48" y="675" fill="#6a7282" font-family="'Geist Mono',monospace" font-size="8" letter-spacing="1">GUARD</text>
|
|
84
|
+
<text x="128" y="675" fill="#101828" font-family="'Geist',sans-serif" font-size="12">Status reports exact saved evidence; uncertain steps are never replayed automatically.</text>
|
|
85
|
+
<text x="128" y="696" fill="#4c5665" font-family="'Geist',sans-serif" font-size="11">A ready candidate may be staged while siblings run. Promotion still requires full combined validation.</text>
|
|
86
|
+
</svg>
|
package/example.png
ADDED
|
Binary file
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: implementer
|
|
3
|
+
description: Implements and validates one bounded change in the checkout selected by Main
|
|
4
|
+
tools:
|
|
5
|
+
- read
|
|
6
|
+
- bash
|
|
7
|
+
- edit
|
|
8
|
+
- write
|
|
9
|
+
- grep
|
|
10
|
+
- find
|
|
11
|
+
- ls
|
|
12
|
+
extensions: []
|
|
13
|
+
skills: []
|
|
14
|
+
mcps: []
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
Implement the bounded outcome, not a preassigned file list. Work in the assigned cwd. Read applicable repository instructions and domain context; inspect the relevant flow, callers, and tests before editing. Preserve unrelated work. Fix the root cause with the smallest complete diff using existing patterns and dependencies. Add no speculative work. Stop when complete or blocked.
|
|
18
|
+
|
|
19
|
+
Run focused checks required by the task. Before reporting, remove only task-created, non-deliverable temporary, generated, or ignored files. Preserve required deliverables, unrelated and pre-existing files, and user data. Never use `git clean` or blanket deletion. If ownership or necessity is uncertain, report the exact path as a blocker.
|
|
20
|
+
|
|
21
|
+
Access credentials or the network, create extra artifacts, or broaden scope only when the task requires it. Never invoke external LLM APIs, SDKs, agent harnesses, or model CLIs.
|
|
22
|
+
|
|
23
|
+
Follow the task's commit policy. Direct delegation may write or commit only within Main's explicitly authorized scope; otherwise leave changes uncommitted. Isolated delegation commits completed scoped changes. Do not create or manage another worktree. Leave the assigned checkout and branch intact. Never push or open a pull request without explicit authorization.
|
|
24
|
+
|
|
25
|
+
Report briefly: outcome, commit, checks, and remaining risks.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: reviewer
|
|
3
|
+
description: Reviews one bounded change for correctness without changing files
|
|
4
|
+
tools:
|
|
5
|
+
- read
|
|
6
|
+
- grep
|
|
7
|
+
- find
|
|
8
|
+
- ls
|
|
9
|
+
- git_read
|
|
10
|
+
extensions: []
|
|
11
|
+
skills: []
|
|
12
|
+
mcps: []
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
Review the supplied candidate read-only. Use only supplied requirements and named files, refs, or evidence. Use `git_read` for bounded local Git inspection to obtain exact evidence; do not prepare Git or broaden discovery. In isolated judgment, the supplied exact patch remains authoritative; inspect only referenced context at the named refs, never substitute another branch or worktree's diff. If evidence is insufficient, say so and stop.
|
|
16
|
+
|
|
17
|
+
Report only actionable correctness risks introduced by the change, not style preferences, speculative hypotheticals, or unrelated pre-existing issues. Run no tests or commands outside `git_read`. Never edit, write, commit, push, mutate Git or manage worktrees, or invoke external LLM APIs, SDKs, agent harnesses, or model CLIs.
|
|
18
|
+
|
|
19
|
+
Output exactly `PASS` when there are no findings. Otherwise output findings only, ordered by priority, then confidence. Start each finding with its priority and confidence, for example `[P1] confidence: high`, then give file:line evidence, the trigger, the impact, and the smallest valid fix:
|
|
20
|
+
|
|
21
|
+
- `P0`: breaks release or operation on ordinary input, such as data loss, a security bypass, or a crash.
|
|
22
|
+
- `P1`: wrong behavior under a realistic condition, such as a race, a missed error path, or a broken caller.
|
|
23
|
+
- `P2`: wrong behavior in a narrow edge case with limited impact.
|
|
24
|
+
|
|
25
|
+
Confidence is `high`, `medium`, or `low` that the defect is real; name the affected code path even when low. Any finding blocks approval; never combine `PASS` with findings. Stop when the supplied evidence is covered.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: scout
|
|
3
|
+
description: Maps relevant code and evidence for one bounded task without changing files
|
|
4
|
+
tools:
|
|
5
|
+
- read
|
|
6
|
+
- grep
|
|
7
|
+
- find
|
|
8
|
+
- ls
|
|
9
|
+
- git_read
|
|
10
|
+
extensions: []
|
|
11
|
+
skills: []
|
|
12
|
+
mcps: []
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
Answer only the bounded discovery questions.
|
|
16
|
+
|
|
17
|
+
Read applicable repository instructions and domain context first. Trace the relevant execution/data flow, callers, tests, and constraints only far enough to answer. Separate observed facts, supported inferences, and unknowns. Stop when answered; if blocked, state what is missing.
|
|
18
|
+
|
|
19
|
+
Do not design, recommend, implement, edit, or run shell commands. For Git inspection of commits and diffs, use only `git_read`.
|
|
20
|
+
|
|
21
|
+
Return concisely:
|
|
22
|
+
|
|
23
|
+
- a map of relevant files and symbols and how they connect;
|
|
24
|
+
- path:line evidence;
|
|
25
|
+
- uncertainties or missing context.
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { realpath } from "node:fs/promises";
|
|
2
|
+
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
3
|
+
|
|
4
|
+
// Main's own tools that never change a checkout; every other Main call may write.
|
|
5
|
+
// `codemode` mutates nothing itself: its nested tool calls are admitted by their own names.
|
|
6
|
+
const MAIN_NON_WRITING_TOOLS = new Set([
|
|
7
|
+
"read", "grep", "find", "ffgrep", "fffind", "ls", "codegraph_explore", "subagent_status", "codemode",
|
|
8
|
+
]);
|
|
9
|
+
|
|
10
|
+
async function checkoutKey(pi: ExtensionAPI, ctx: ExtensionContext): Promise<string> {
|
|
11
|
+
const cwd = await realpath(ctx.cwd);
|
|
12
|
+
const result = await pi.exec("git", ["rev-parse", "--show-toplevel"], { cwd, timeout: 5_000 });
|
|
13
|
+
if (result.code !== 0 || result.killed) return cwd;
|
|
14
|
+
const root = result.stdout.replace(/\r?\n$/, "");
|
|
15
|
+
if (!root || /[\r\n\0]/.test(root)) throw new Error("Git returned a malformed checkout root during delegation admission.");
|
|
16
|
+
return await realpath(root);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Reserve checkouts only for direct writers. Main writes can run together, but
|
|
21
|
+
* each call must finish before a direct writer can start in that checkout.
|
|
22
|
+
* External processes and trusted extension lifecycle code are not intercepted.
|
|
23
|
+
*/
|
|
24
|
+
export function registerCheckoutAdmission(pi: ExtensionAPI, directCanWrite: (input: unknown) => boolean): (toolCallId: string) => () => void {
|
|
25
|
+
const heldCalls = new Set<string>();
|
|
26
|
+
const ownerByCheckout = new Map<string, string>();
|
|
27
|
+
const calls = new Map<string, { direct: boolean; key?: string }>();
|
|
28
|
+
// One Git lookup per working directory per session; a failed lookup is retried.
|
|
29
|
+
const checkouts = new Map<string, Promise<string>>();
|
|
30
|
+
const lookup = (ctx: ExtensionContext) => {
|
|
31
|
+
const cached = checkouts.get(ctx.cwd);
|
|
32
|
+
if (cached) return cached;
|
|
33
|
+
const key = checkoutKey(pi, ctx);
|
|
34
|
+
checkouts.set(ctx.cwd, key);
|
|
35
|
+
key.catch(() => { if (checkouts.get(ctx.cwd) === key) checkouts.delete(ctx.cwd); });
|
|
36
|
+
return key;
|
|
37
|
+
};
|
|
38
|
+
const release = (toolCallId: string) => {
|
|
39
|
+
if (heldCalls.has(toolCallId)) return;
|
|
40
|
+
const call = calls.get(toolCallId);
|
|
41
|
+
calls.delete(toolCallId);
|
|
42
|
+
if (call?.key && ownerByCheckout.get(call.key) === toolCallId) ownerByCheckout.delete(call.key);
|
|
43
|
+
};
|
|
44
|
+
pi.on("tool_call", async (event, ctx) => {
|
|
45
|
+
const direct = event.toolName === "delegate_task";
|
|
46
|
+
if (direct ? !directCanWrite(event.input) : MAIN_NON_WRITING_TOOLS.has(event.toolName)) return;
|
|
47
|
+
const call: { direct: boolean; key?: string } = { direct };
|
|
48
|
+
calls.set(event.toolCallId, call);
|
|
49
|
+
const key = await lookup(ctx);
|
|
50
|
+
if (calls.get(event.toolCallId) !== call) {
|
|
51
|
+
return { block: true, reason: `Call ${event.toolCallId} already settled; use mode: isolated to keep Main free.` };
|
|
52
|
+
}
|
|
53
|
+
const owner = ownerByCheckout.get(key);
|
|
54
|
+
const main = direct ? [...calls].find(([, active]) => !active.direct && active.key === key)?.[0] : undefined;
|
|
55
|
+
if (owner || main) {
|
|
56
|
+
release(event.toolCallId);
|
|
57
|
+
return {
|
|
58
|
+
block: true,
|
|
59
|
+
reason: `Checkout ${key} has ${owner ? `an admitted direct writer (${owner})` : `a Main potentially-writing call in flight (${main})`}; retry after it stops, or use mode: isolated to keep Main free.`,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
call.key = key;
|
|
63
|
+
if (direct) ownerByCheckout.set(key, event.toolCallId);
|
|
64
|
+
});
|
|
65
|
+
pi.on("tool_result", (event) => release(event.toolCallId));
|
|
66
|
+
pi.on("tool_execution_end", (event) => release(event.toolCallId));
|
|
67
|
+
pi.on("session_start", () => checkouts.clear());
|
|
68
|
+
pi.on("session_shutdown", () => {
|
|
69
|
+
checkouts.clear();
|
|
70
|
+
// Session replacement is not proof that a held direct worker stopped.
|
|
71
|
+
for (const toolCallId of calls.keys()) release(toolCallId);
|
|
72
|
+
});
|
|
73
|
+
// The tool returns a handle before its worker stops. Release only after verified termination.
|
|
74
|
+
return (toolCallId) => {
|
|
75
|
+
const call = calls.get(toolCallId);
|
|
76
|
+
if (!call?.direct || !call.key) {
|
|
77
|
+
throw new Error(`Direct call ${toolCallId} was not admitted as a checkout writer; delegate again.`);
|
|
78
|
+
}
|
|
79
|
+
heldCalls.add(toolCallId);
|
|
80
|
+
return () => { heldCalls.delete(toolCallId); release(toolCallId); };
|
|
81
|
+
};
|
|
82
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { formatConfigIssues, type ConfigLoad } from "@hcflabs/pi-config-store";
|
|
2
|
+
import type { ExecutionPolicySnapshot } from "../dist/schema.js";
|
|
3
|
+
import type { SubagentConfig } from "@hcflabs/pi-subagent";
|
|
4
|
+
|
|
5
|
+
/** Resolve one immutable policy. A disabled config blocks delegation. */
|
|
6
|
+
export function resolveExecutionPolicy(path: string, loaded: ConfigLoad<SubagentConfig>): ExecutionPolicySnapshot {
|
|
7
|
+
if (loaded.status === "disabled") {
|
|
8
|
+
throw new Error(`${path}: ${formatConfigIssues(loaded.issues)}; delegation is blocked until this file is corrected.`);
|
|
9
|
+
}
|
|
10
|
+
const { maxSubagents, maxTurns, maxTokens, maxCorrections, timeout } = loaded.value;
|
|
11
|
+
return Object.freeze({
|
|
12
|
+
maxSubagents,
|
|
13
|
+
maxTurns,
|
|
14
|
+
...(maxTokens === undefined ? {} : { maxTokens }),
|
|
15
|
+
childIdleMs: timeout.idleMinutes * 60_000,
|
|
16
|
+
childMaxMs: timeout.maxMinutes * 60_000,
|
|
17
|
+
maxCorrections,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { StringEnum } from "@earendil-works/pi-ai";
|
|
2
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
3
|
+
import { spawnBounded } from "@hcflabs/pi-process";
|
|
4
|
+
import { ROLE_TOOL_POLICY_FLAG, roleToolPolicyFromArgv } from "@hcflabs/pi-subagent";
|
|
5
|
+
import { Type, type Static } from "typebox";
|
|
6
|
+
import { Check } from "typebox/value";
|
|
7
|
+
|
|
8
|
+
const OUTPUT_BYTES = 32 * 1024;
|
|
9
|
+
const TIMEOUT_MS = 30_000;
|
|
10
|
+
const Parameters = Type.Object({
|
|
11
|
+
operation: StringEnum(["status", "diff", "show", "log", "blame", "rev-parse"]),
|
|
12
|
+
mode: Type.Optional(StringEnum(["working", "staged", "committed"], { description: "Diff only; defaults to working (unstaged). Committed requires base and tip." })),
|
|
13
|
+
base: Type.Optional(Type.String()),
|
|
14
|
+
tip: Type.Optional(Type.String()),
|
|
15
|
+
revision: Type.Optional(Type.String({ description: "Commit ID or named ref with optional ~N/^N ancestry; defaults to HEAD. No ranges or reflog expressions." })),
|
|
16
|
+
path: Type.Optional(Type.String({ description: "One literal path relative to the assigned cwd; no absolute paths, traversal, or .git paths." })),
|
|
17
|
+
maxCount: Type.Optional(Type.Integer({ minimum: 1, maximum: 100, description: "Log only; defaults to 20." })),
|
|
18
|
+
startLine: Type.Optional(Type.Integer({ minimum: 1, maximum: 1_000_000, description: "Blame only; supply both line bounds." })),
|
|
19
|
+
endLine: Type.Optional(Type.Integer({ minimum: 1, maximum: 1_000_000 })),
|
|
20
|
+
}, { additionalProperties: false });
|
|
21
|
+
type Params = Static<typeof Parameters>;
|
|
22
|
+
const Result = Type.Object({ output: Type.String(), stderr: Type.String(), truncated: Type.Boolean() });
|
|
23
|
+
|
|
24
|
+
function parse(input: unknown): Params {
|
|
25
|
+
if (!Check(Parameters, input)) throw new Error("git_read requires a supported operation and valid structured parameters; arbitrary Git options are not accepted.");
|
|
26
|
+
const allowed: Record<Params["operation"], string[]> = {
|
|
27
|
+
status: ["path"], diff: ["mode", "base", "tip", "path"], show: ["revision", "path"],
|
|
28
|
+
log: ["revision", "path", "maxCount"], blame: ["revision", "path", "startLine", "endLine"], "rev-parse": ["revision"],
|
|
29
|
+
};
|
|
30
|
+
for (const key of Object.keys(input)) {
|
|
31
|
+
if (key !== "operation" && !allowed[input.operation].includes(key)) throw new Error(`git_read ${input.operation} does not accept ${key}.`);
|
|
32
|
+
}
|
|
33
|
+
for (const revision of [input.revision, input.base, input.tip]) {
|
|
34
|
+
if (revision === undefined) continue;
|
|
35
|
+
if (revision.length > 256 || !/^[A-Za-z0-9_][A-Za-z0-9_./-]*(?:[~^][0-9]*)*$/.test(revision)
|
|
36
|
+
|| revision.includes("..") || revision.includes("//")) {
|
|
37
|
+
throw new Error("git_read revision must be a commit ID or named ref with optional ~N/^N ancestry, not options, ranges, paths, or reflog expressions.");
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (input.path !== undefined && (!input.path || input.path.length > 4096 || /^[-/]/.test(input.path)
|
|
41
|
+
|| /[\\:\u0000-\u001f\u007f-\u009f]/.test(input.path)
|
|
42
|
+
|| input.path.split("/").some((part) => !part || part === "." || part === ".." || part.toLowerCase() === ".git"))) {
|
|
43
|
+
throw new Error("git_read path must be a literal relative path without options, traversal, control characters, or .git components.");
|
|
44
|
+
}
|
|
45
|
+
if (input.operation === "diff") {
|
|
46
|
+
if (input.mode === "committed" ? !input.base || !input.tip : input.base !== undefined || input.tip !== undefined) {
|
|
47
|
+
throw new Error("git_read committed diff requires base and tip; working/staged diff accepts neither.");
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (input.operation === "blame" && (!input.path || (input.startLine === undefined) !== (input.endLine === undefined)
|
|
51
|
+
|| (input.startLine !== undefined && input.endLine! < input.startLine))) {
|
|
52
|
+
throw new Error("git_read blame requires path and, if supplied, ordered startLine and endLine.");
|
|
53
|
+
}
|
|
54
|
+
return input;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// No pager, optional index refresh, lazy network fetch, replacements, hooks, fsmonitor,
|
|
58
|
+
// signature programs, submodule recursion, external diffs, or textconv programs.
|
|
59
|
+
const GIT = ["--no-pager", "--no-optional-locks", "--no-lazy-fetch", "--no-replace-objects", "--literal-pathspecs",
|
|
60
|
+
"-c", "core.fsmonitor=false", "-c", "core.hooksPath=/dev/null", "-c", "protocol.allow=never",
|
|
61
|
+
"-c", "submodule.recurse=false", "-c", "log.showSignature=false"];
|
|
62
|
+
const DIFF = ["--no-ext-diff", "--no-textconv", "--no-color", "--ignore-submodules=all", "--no-renames"];
|
|
63
|
+
|
|
64
|
+
async function inspect(params: Params, cwd: string, signal?: AbortSignal): Promise<Static<typeof Result>> {
|
|
65
|
+
const deadline = AbortSignal.timeout(TIMEOUT_MS);
|
|
66
|
+
const boundedSignal = signal ? AbortSignal.any([signal, deadline]) : deadline;
|
|
67
|
+
let filterOverrides: string[] = [];
|
|
68
|
+
const git = async (args: string[], tail = false) => {
|
|
69
|
+
const result = await spawnBounded("git", [...GIT, ...filterOverrides, ...args], {
|
|
70
|
+
cwd, signal: boundedSignal, timeoutMs: TIMEOUT_MS, stdoutLimitBytes: OUTPUT_BYTES,
|
|
71
|
+
...(tail ? { stdoutTailBytes: OUTPUT_BYTES } : {}), stderrLimitBytes: 8192,
|
|
72
|
+
});
|
|
73
|
+
if (result.killed || result.code !== 0) throw new Error(`git_read ${params.operation}: git ${args[0]} exited ${result.code}: ${result.stderr.trim() || "no diagnostic"}`);
|
|
74
|
+
return result;
|
|
75
|
+
};
|
|
76
|
+
const resolve = async (revision = "HEAD") => (await git(["rev-parse", "--verify", "--end-of-options", `${revision}^{commit}`])).stdout.trim();
|
|
77
|
+
let args: string[];
|
|
78
|
+
const paths = params.path === undefined ? [] : [params.path];
|
|
79
|
+
switch (params.operation) {
|
|
80
|
+
case "status": args = ["status", "--porcelain=v1", "--untracked-files=all", "--ignore-submodules=all", "--", ...paths]; break;
|
|
81
|
+
case "diff": args = ["diff", ...DIFF, ...(params.mode === "committed"
|
|
82
|
+
? [await resolve(params.base), await resolve(params.tip)] : params.mode === "staged" ? ["--cached"] : []), "--", ...paths]; break;
|
|
83
|
+
case "show": {
|
|
84
|
+
const revision = await resolve(params.revision);
|
|
85
|
+
args = params.path === undefined
|
|
86
|
+
? ["show", ...DIFF, "--format=fuller", "--no-show-signature", revision, "--"]
|
|
87
|
+
: ["show", "--no-ext-diff", "--no-textconv", `${revision}:./${params.path}`];
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
case "log": args = ["log", "--no-color", "--no-decorate", "--no-show-signature", "--format=fuller", `--max-count=${params.maxCount ?? 20}`, await resolve(params.revision), "--", ...paths]; break;
|
|
91
|
+
case "blame": args = ["blame", "--no-textconv", "--line-porcelain", ...(params.startLine === undefined ? [] : ["-L", `${params.startLine},${params.endLine}`]), await resolve(params.revision), "--", params.path!]; break;
|
|
92
|
+
case "rev-parse": return { output: `${await resolve(params.revision)}\n`, stderr: "", truncated: false };
|
|
93
|
+
default: throw new Error(`Unsupported git_read operation: ${params.operation}`);
|
|
94
|
+
}
|
|
95
|
+
// Working-tree comparison can invoke clean/process filters even with --no-textconv.
|
|
96
|
+
// Override configured filters by name rather than trusting repository attributes.
|
|
97
|
+
if (params.operation === "status" || params.operation === "diff" && params.mode !== "committed") {
|
|
98
|
+
const filters = await spawnBounded("git", [...GIT, "config", "--null", "--name-only", "--get-regexp", "^filter\\..*\\.(clean|process|required)$"], {
|
|
99
|
+
cwd, signal: boundedSignal, timeoutMs: TIMEOUT_MS, stdoutLimitBytes: OUTPUT_BYTES, stderrLimitBytes: 8192,
|
|
100
|
+
});
|
|
101
|
+
if (filters.killed || filters.code !== 0 && filters.code !== 1) throw new Error(`git_read cannot inspect clean filters: ${filters.stderr.trim()}`);
|
|
102
|
+
const keys = filters.stdout.split("\0").filter(Boolean);
|
|
103
|
+
if (keys.some((key) => key.includes("="))) throw new Error("git_read cannot safely disable filter names containing '='.");
|
|
104
|
+
filterOverrides = keys.flatMap((key) => ["-c", `${key}=${key.endsWith(".required") ? "false" : ""}`]);
|
|
105
|
+
}
|
|
106
|
+
const result = await git(args, true);
|
|
107
|
+
return { output: result.stdout, stderr: result.stderr, truncated: result.stdoutTruncated === true };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export default function gitRead(pi: ExtensionAPI): void {
|
|
111
|
+
// Like the MCP adapter, inspect argv because Pi binds extension flags after factories load.
|
|
112
|
+
if (!process.argv.includes(`--${ROLE_TOOL_POLICY_FLAG}`) || !roleToolPolicyFromArgv(process.argv).includes("git_read")) return;
|
|
113
|
+
pi.registerTool({
|
|
114
|
+
name: "git_read", label: "Git read",
|
|
115
|
+
description: "Bounded local Git evidence: status, working/staged or base-to-tip diff, show commit/file, log, blame, and rev-parse commit lookup. Fixed read-only operations; no shell/options, network, tests, or mutations. Paths are relative to cwd. Output retains at most the last 32 KiB; narrow path/ref/line scope if truncated. Each call has a 30-second timeout.",
|
|
116
|
+
parameters: Parameters,
|
|
117
|
+
outputSchema: Result,
|
|
118
|
+
async execute(_id, input, signal, _update, ctx) {
|
|
119
|
+
const result = await inspect(parse(input), ctx.cwd, signal);
|
|
120
|
+
return {
|
|
121
|
+
content: [{ type: "text", text: `${result.truncated ? "[TRUNCATED: only the last 32768 bytes follow; narrow the request.]\n" : ""}${result.output}${result.stderr ? `\nGit diagnostic: ${result.stderr}` : ""}` }],
|
|
122
|
+
structuredContent: result, details: result,
|
|
123
|
+
};
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
}
|