catherd-cli 1.5.0 → 2.0.0-beta.20261022113309
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/README.md +164 -208
- package/THIRD_PARTY_NOTICES.md +217 -35
- package/catalog/jev.json +27 -1
- package/catalog/models.json +56 -247
- package/package.json +6 -8
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/bin/catherd-mcp +1 -1
- package/plugin/commands/catherd.md +2 -2
- package/plugin/skills/catherd/SKILL.md +97 -421
- package/plugin/skills/catherd-setup/SKILL.md +20 -19
- package/src/adapters/all.ts +0 -4
- package/src/adapters/backend.ts +1 -2
- package/src/adapters/cursor/home.ts +1 -1
- package/src/adapters/cursor/index.ts +4 -4
- package/src/cli.ts +22 -27
- package/src/domain/bars.ts +33 -107
- package/src/domain/blocked.ts +72 -0
- package/src/domain/branch.ts +51 -0
- package/src/domain/brief.ts +21 -6
- package/src/domain/catalog.ts +69 -324
- package/src/domain/cost.ts +15 -30
- package/src/domain/errors.ts +2 -2
- package/src/domain/fail-lines.ts +26 -0
- package/src/domain/failover.ts +37 -87
- package/src/domain/findings.ts +47 -0
- package/src/domain/gate-brief.ts +1 -0
- package/src/domain/ids.ts +13 -2
- package/src/domain/jev.ts +47 -2
- package/src/domain/judge.ts +124 -0
- package/src/domain/lane.ts +22 -3
- package/src/domain/milestone-memory.ts +53 -0
- package/src/domain/notice.ts +25 -8
- package/src/domain/notify.ts +32 -0
- package/src/domain/plan-doc.ts +198 -0
- package/src/domain/plan-lint.ts +141 -0
- package/src/domain/profile-rules.ts +101 -160
- package/src/domain/profile.ts +116 -63
- package/src/domain/record.ts +53 -2
- package/src/domain/role-prompts.ts +131 -45
- package/src/domain/role-scope.ts +4 -34
- package/src/domain/role-tools.ts +2 -2
- package/src/domain/roles.ts +2 -0
- package/src/domain/route.ts +17 -0
- package/src/domain/rules.ts +33 -0
- package/src/domain/run-events.ts +298 -0
- package/src/domain/run-machine.ts +668 -0
- package/src/domain/run-state.ts +1310 -0
- package/src/domain/scorecard.ts +197 -0
- package/src/domain/select.ts +151 -146
- package/src/domain/slopalytics.ts +72 -0
- package/src/entry/answer-command.ts +39 -0
- package/src/entry/catalog-command.ts +25 -174
- package/src/entry/check-bin.ts +17 -0
- package/src/entry/cli-kit.ts +1 -1
- package/src/entry/daemon-bin.ts +22 -0
- package/src/entry/glyphs.ts +15 -2
- package/src/entry/init-command.ts +18 -68
- package/src/entry/knowledge-command.ts +45 -4
- package/src/entry/lock-command.ts +6 -7
- package/src/entry/mcp/role-server.ts +18 -0
- package/src/entry/mcp/server.ts +13 -40
- package/src/entry/mcp/session-tools.ts +149 -0
- package/src/entry/panel/openapi.ts +189 -0
- package/src/entry/panel/server.ts +410 -0
- package/src/entry/panel-command.ts +68 -0
- package/src/entry/profile-command.ts +10 -14
- package/src/entry/role-cli-command.ts +47 -3
- package/src/entry/runs-command.ts +3 -19
- package/src/entry/scorecard-command.ts +187 -0
- package/src/entry/supervise-bin.ts +1 -1
- package/src/infra/env.ts +0 -2
- package/src/infra/git.ts +64 -0
- package/src/infra/heavy-lock.ts +45 -7
- package/src/infra/launch.ts +63 -7
- package/src/infra/memory.ts +45 -0
- package/src/infra/ntfy.ts +40 -0
- package/src/infra/open-url.ts +42 -0
- package/src/infra/paths.ts +2 -2
- package/src/infra/slopalytics.ts +30 -0
- package/src/infra/store.ts +6 -1
- package/src/services/admission.ts +24 -9
- package/src/services/backends.ts +4 -6
- package/src/services/capture.ts +1 -22
- package/src/services/catalog-service.ts +155 -262
- package/src/services/credentials.ts +4 -20
- package/src/services/daemon.ts +181 -0
- package/src/services/dispatch-service.ts +5 -1
- package/src/services/doctor-backends.ts +1 -1
- package/src/services/doctor-checks.ts +47 -0
- package/src/services/doctor.ts +4 -9
- package/src/services/failover-rungs.ts +109 -0
- package/src/services/finalize.ts +12 -0
- package/src/services/gate-env.ts +1 -2
- package/src/services/gate-service.ts +21 -6
- package/src/services/jev-service.ts +2 -4
- package/src/services/lane-service.ts +34 -3
- package/src/services/milestone-check.ts +433 -0
- package/src/services/milestone-tree.ts +170 -0
- package/src/services/milestones.ts +35 -8
- package/src/services/notifier.ts +69 -2
- package/src/services/notify-service.ts +26 -0
- package/src/services/panel-setup.ts +116 -0
- package/src/services/plan-input.ts +151 -0
- package/src/services/ports.ts +30 -4
- package/src/services/preflight.ts +82 -8
- package/src/services/profile-service.ts +30 -3
- package/src/services/profile-store.ts +8 -3
- package/src/services/protocol.ts +4 -0
- package/src/services/provenance.ts +31 -90
- package/src/services/questions.ts +9 -0
- package/src/services/reconcile.ts +11 -0
- package/src/services/routing-service.ts +26 -15
- package/src/services/run-briefs.ts +477 -0
- package/src/services/run-client.ts +217 -0
- package/src/services/run-effects.ts +837 -0
- package/src/services/run-events-store.ts +174 -0
- package/src/services/run-evidence.ts +1 -1
- package/src/services/run-read.ts +221 -0
- package/src/services/run-report.ts +176 -0
- package/src/services/run-service.ts +55 -6
- package/src/services/run-store.ts +54 -7
- package/src/services/scorecard-metrics.ts +86 -0
- package/src/services/scorecard.ts +382 -0
- package/src/services/summary.ts +27 -3
- package/src/services/tree-path.ts +27 -0
- package/src/services/workspace-admission.ts +5 -13
- package/src/services/workspace-service.ts +5 -0
- package/web/dist/assets/Brand-PKlUX5TE.js +1 -0
- package/web/dist/assets/Brand-PKlUX5TE.js.br +0 -0
- package/web/dist/assets/Brand-PKlUX5TE.js.gz +0 -0
- package/web/dist/assets/Modal-Dxc5so53.js +1 -0
- package/web/dist/assets/Modal-Dxc5so53.js.br +0 -0
- package/web/dist/assets/Modal-Dxc5so53.js.gz +0 -0
- package/web/dist/assets/Pressable-C8-T6Ed_.js +1 -0
- package/web/dist/assets/StatusChip-B5xfI_Mn.js +153 -0
- package/web/dist/assets/StatusChip-B5xfI_Mn.js.br +0 -0
- package/web/dist/assets/StatusChip-B5xfI_Mn.js.gz +0 -0
- package/web/dist/assets/_app-Ea5cpyKe.js +1 -0
- package/web/dist/assets/_app-Ea5cpyKe.js.br +0 -0
- package/web/dist/assets/_app-Ea5cpyKe.js.gz +0 -0
- package/web/dist/assets/_runId-pTG0fXVo.js +1 -0
- package/web/dist/assets/_runId-pTG0fXVo.js.br +0 -0
- package/web/dist/assets/_runId-pTG0fXVo.js.gz +0 -0
- package/web/dist/assets/drawer-Du3cdlok.js +1 -0
- package/web/dist/assets/drawer-Du3cdlok.js.br +0 -0
- package/web/dist/assets/drawer-Du3cdlok.js.gz +0 -0
- package/web/dist/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
- package/web/dist/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
- package/web/dist/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
- package/web/dist/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
- package/web/dist/assets/geist-mono-cyrillic-ext-wght-normal-X_5orZeX.woff2 +0 -0
- package/web/dist/assets/geist-mono-cyrillic-wght-normal-DiZS0aHC.woff2 +0 -0
- package/web/dist/assets/geist-mono-latin-ext-wght-normal-Bwz-egvJ.woff2 +0 -0
- package/web/dist/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2 +0 -0
- package/web/dist/assets/geist-mono-symbols2-wght-normal-CO5SzqOn.woff2 +0 -0
- package/web/dist/assets/geist-mono-vietnamese-wght-normal-DadHysG0.woff2 +0 -0
- package/web/dist/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
- package/web/dist/assets/index-B6GiwDkh.js +11 -0
- package/web/dist/assets/index-B6GiwDkh.js.br +0 -0
- package/web/dist/assets/index-B6GiwDkh.js.gz +0 -0
- package/web/dist/assets/index-C5IbylvM.css +2 -0
- package/web/dist/assets/index-C5IbylvM.css.br +0 -0
- package/web/dist/assets/index-C5IbylvM.css.gz +0 -0
- package/web/dist/assets/label-DFSYBc62.js +1 -0
- package/web/dist/assets/label-DFSYBc62.js.br +0 -0
- package/web/dist/assets/label-DFSYBc62.js.gz +0 -0
- package/web/dist/assets/link-D8EcKZUt.js +1 -0
- package/web/dist/assets/link-D8EcKZUt.js.br +0 -0
- package/web/dist/assets/link-D8EcKZUt.js.gz +0 -0
- package/web/dist/assets/login-DaN5HPPW.js +1 -0
- package/web/dist/assets/login-DaN5HPPW.js.br +0 -0
- package/web/dist/assets/login-DaN5HPPW.js.gz +0 -0
- package/web/dist/assets/query-Cu--ccT3.js +1 -0
- package/web/dist/assets/questions-shoO9OVj.js +1 -0
- package/web/dist/assets/questions-shoO9OVj.js.br +0 -0
- package/web/dist/assets/questions-shoO9OVj.js.gz +0 -0
- package/web/dist/assets/react-query.gen-Bu2yV-qJ.js +12 -0
- package/web/dist/assets/react-query.gen-Bu2yV-qJ.js.br +0 -0
- package/web/dist/assets/react-query.gen-Bu2yV-qJ.js.gz +0 -0
- package/web/dist/assets/rolldown-runtime-XKyaHtl9.js +1 -0
- package/web/dist/assets/run-status-C0DRuV7U.js +1 -0
- package/web/dist/assets/runs-C3rg2zZc.js +1 -0
- package/web/dist/assets/runs-C3rg2zZc.js.br +0 -0
- package/web/dist/assets/runs-C3rg2zZc.js.gz +0 -0
- package/web/dist/assets/setup-5I4Vl9fR.js +1 -0
- package/web/dist/assets/setup-5I4Vl9fR.js.br +0 -0
- package/web/dist/assets/setup-5I4Vl9fR.js.gz +0 -0
- package/web/dist/assets/table-BOxXhqS4.js +1 -0
- package/web/dist/assets/table-BOxXhqS4.js.br +0 -0
- package/web/dist/assets/table-BOxXhqS4.js.gz +0 -0
- package/web/dist/assets/textfield-BzRFkxBF.js +1 -0
- package/web/dist/assets/textfield-BzRFkxBF.js.br +0 -0
- package/web/dist/assets/textfield-BzRFkxBF.js.gz +0 -0
- package/web/dist/assets/time--9kSQbbp.js +1 -0
- package/web/dist/assets/use-css-variable-CpnN9WtA.js +5 -0
- package/web/dist/assets/use-css-variable-CpnN9WtA.js.br +0 -0
- package/web/dist/assets/use-css-variable-CpnN9WtA.js.gz +0 -0
- package/web/dist/assets/useMutation-CVPKlR34.js +1 -0
- package/web/dist/assets/useMutation-CVPKlR34.js.br +0 -0
- package/web/dist/assets/useMutation-CVPKlR34.js.gz +0 -0
- package/web/dist/index.html +15 -0
- package/catalog/ATTRIBUTION.md +0 -44
- package/catalog/scores.json +0 -3202
- package/catalog/sources.json +0 -97
- package/src/adapters/antigravity/events.ts +0 -131
- package/src/adapters/antigravity/home.ts +0 -46
- package/src/adapters/antigravity/index.ts +0 -335
- package/src/adapters/antigravity/models.ts +0 -37
- package/src/adapters/grok/events.ts +0 -112
- package/src/adapters/grok/home.ts +0 -187
- package/src/adapters/grok/index.ts +0 -358
- package/src/adapters/grok/models.ts +0 -48
- package/src/domain/calibration.ts +0 -132
- package/src/domain/sources.ts +0 -144
- package/src/entry/mcp/dispatch-tools.ts +0 -52
- package/src/entry/mcp/lane-tools.ts +0 -131
- package/src/entry/mcp/protocol-tools.ts +0 -63
- package/src/entry/mcp/push-tools.ts +0 -16
- package/src/entry/mcp/run-tools.ts +0 -164
- package/src/entry/mcp/setup-tools.ts +0 -109
- package/src/entry/mcp/workspace-tools.ts +0 -142
- package/src/entry/tui/commands.ts +0 -563
- package/src/entry/tui/effects.ts +0 -385
- package/src/entry/tui/fixtures.ts +0 -637
- package/src/entry/tui/profile-edits.ts +0 -146
- package/src/entry/tui/profile-tree.ts +0 -429
- package/src/entry/tui/providers/app.tsx +0 -162
- package/src/entry/tui/providers/boundary.tsx +0 -57
- package/src/entry/tui/providers/data.tsx +0 -160
- package/src/entry/tui/providers/keymap.tsx +0 -166
- package/src/entry/tui/providers/root.tsx +0 -45
- package/src/entry/tui/providers/theme.tsx +0 -17
- package/src/entry/tui/providers/toast.tsx +0 -77
- package/src/entry/tui/run.tsx +0 -114
- package/src/entry/tui/state.ts +0 -400
- package/src/entry/tui/stories.ts +0 -114
- package/src/entry/tui/text.ts +0 -130
- package/src/entry/tui/theme.ts +0 -140
- package/src/entry/tui/views/app.tsx +0 -208
- package/src/entry/tui/views/dialogs.tsx +0 -41
- package/src/entry/tui/views/profile-actions.ts +0 -331
- package/src/entry/tui/views/profiles.tsx +0 -520
- package/src/entry/tui/views/runs.tsx +0 -619
- package/src/entry/tui/views/save-dialog.tsx +0 -344
- package/src/entry/tui/views/status.tsx +0 -213
- package/src/entry/tui/widgets/chrome.tsx +0 -114
- package/src/entry/tui/widgets/dialog-confirm.tsx +0 -58
- package/src/entry/tui/widgets/dialog-prompt.tsx +0 -52
- package/src/entry/tui/widgets/dialog-select.tsx +0 -212
- package/src/entry/tui/widgets/dialog.tsx +0 -68
- package/src/entry/tui/widgets/line.tsx +0 -92
- package/src/entry/tui/widgets/list.tsx +0 -197
- package/src/entry/tui/widgets/spinner.tsx +0 -49
- package/src/entry/tui/widgets/toast.tsx +0 -62
- package/src/infra/sources/arena.ts +0 -95
- package/src/infra/sources/artificial-analysis.ts +0 -119
- package/src/infra/sources/cache.ts +0 -126
- package/src/infra/sources/csv.ts +0 -38
- package/src/infra/sources/epoch.ts +0 -82
- package/src/infra/sources/http.ts +0 -75
- package/src/infra/sources/litellm.ts +0 -58
- package/src/infra/sources/models-dev.ts +0 -59
- package/src/infra/sources/openrouter-endpoints.ts +0 -52
- package/src/infra/sources/openrouter-models.ts +0 -60
- package/src/infra/sources/rows.ts +0 -38
- package/src/infra/sources/vectara.ts +0 -69
- package/src/infra/sources/zip.ts +0 -48
- package/src/services/catalog-refresh.ts +0 -201
- package/src/services/doctor-sources.ts +0 -76
- package/src/services/runs-page.ts +0 -290
- package/src/services/source-derive.ts +0 -397
- package/src/services/source-sync.ts +0 -312
- package/src/services/standins.ts +0 -238
- package/src/services/treat-likes.ts +0 -113
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
-
import { z } from "zod";
|
|
3
|
-
import { ID_PATTERN } from "../../domain/ids.ts";
|
|
4
|
-
import { ROLES } from "../../domain/roles.ts";
|
|
5
|
-
import { knownQueueCapability } from "../../infra/codex-queue.ts";
|
|
6
|
-
import { redact } from "../../infra/log.ts";
|
|
7
|
-
import { registerSavedSecrets } from "../../services/jev-service.ts";
|
|
8
|
-
import type { Deps } from "../../services/ports.ts";
|
|
9
|
-
import {
|
|
10
|
-
readKnowledge,
|
|
11
|
-
readRunFile,
|
|
12
|
-
recordAgentRun,
|
|
13
|
-
result,
|
|
14
|
-
setNext,
|
|
15
|
-
startRun,
|
|
16
|
-
writeRunFile,
|
|
17
|
-
} from "../../services/run-service.ts";
|
|
18
|
-
import { repin } from "../../services/run-pin.ts";
|
|
19
|
-
import { runsSummary, status } from "../../services/summary.ts";
|
|
20
|
-
import { handle } from "./result.ts";
|
|
21
|
-
|
|
22
|
-
export function registerRunTools(server: McpServer, deps: Deps): void {
|
|
23
|
-
server.registerTool(
|
|
24
|
-
"run_start",
|
|
25
|
-
{
|
|
26
|
-
description:
|
|
27
|
-
"Start a catherd run for a git repository: creates its run folder (outside the repo) and state.md. Returns the run id, the folder, protocol (the next step of the milestone loop and its six-line checklist), and hints when state.md could not be written yet. from: the run this one takes over (a planning run handed to the execution run in a worktree): it is closed with a pointer here, and status hides it.",
|
|
28
|
-
inputSchema: {
|
|
29
|
-
repo: z.string().min(1),
|
|
30
|
-
title: z.string().min(1),
|
|
31
|
-
a_lines: z.array(z.string().min(1)).min(1),
|
|
32
|
-
from: z.string().regex(ID_PATTERN).optional(),
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
|
-
(a) => handle(() => startRun(deps, { repo: a.repo, title: a.title, aLines: a.a_lines, from: a.from })),
|
|
36
|
-
);
|
|
37
|
-
|
|
38
|
-
server.registerTool(
|
|
39
|
-
"run_pin",
|
|
40
|
-
{
|
|
41
|
-
description:
|
|
42
|
-
"Re-pin a run to what its repo runs on now: the profile, each role's access and each backend's isolation. run_start pins them; while they differ, dispatch keeps the pinned values and status and state.md say what changed. Only on the owner's word. Returns the new pin and what it changed. Orchestrator only.",
|
|
43
|
-
inputSchema: { run: z.string().regex(ID_PATTERN) },
|
|
44
|
-
},
|
|
45
|
-
(a) => handle(() => repin(deps, a)),
|
|
46
|
-
);
|
|
47
|
-
|
|
48
|
-
server.registerTool(
|
|
49
|
-
"write_run_file",
|
|
50
|
-
{
|
|
51
|
-
description:
|
|
52
|
-
"Write a file in the run folder: plan.md, lanes/Mx.Ly.md, a dossier or notes. The path is relative to the run folder; catherd's own files are refused.",
|
|
53
|
-
inputSchema: { run: z.string(), path: z.string().min(1), content: z.string() },
|
|
54
|
-
},
|
|
55
|
-
(a) => handle(() => writeRunFile(a)),
|
|
56
|
-
);
|
|
57
|
-
|
|
58
|
-
server.registerTool(
|
|
59
|
-
"read_run_file",
|
|
60
|
-
{
|
|
61
|
-
description: "Read a file in the run folder, by a path relative to it.",
|
|
62
|
-
inputSchema: { run: z.string(), path: z.string().min(1) },
|
|
63
|
-
},
|
|
64
|
-
(a) => handle(() => readRunFile(a)),
|
|
65
|
-
);
|
|
66
|
-
|
|
67
|
-
server.registerTool(
|
|
68
|
-
"status",
|
|
69
|
-
{
|
|
70
|
-
description:
|
|
71
|
-
"catherd's version, and one screen per run: the state.md tail, live roles, unread results waiting for the orchestrator, totals, reported Claude subagents, Jev fallbacks, per backend how many dispatches ran native and isolated, budget and landed milestones. Without a run: every run with live roles or unread completions waiting for its orchestrator, else the newest. Reads only.",
|
|
72
|
-
inputSchema: { run: z.string().optional() },
|
|
73
|
-
},
|
|
74
|
-
(a) =>
|
|
75
|
-
handle(async () => {
|
|
76
|
-
registerSavedSecrets();
|
|
77
|
-
const queue =
|
|
78
|
-
deps.host.host === "codex" && !deps.host.conflict ? knownQueueCapability(process.env) : null;
|
|
79
|
-
return redact(status(deps, a.run, queue));
|
|
80
|
-
}),
|
|
81
|
-
);
|
|
82
|
-
|
|
83
|
-
server.registerTool(
|
|
84
|
-
"result",
|
|
85
|
-
{
|
|
86
|
-
description:
|
|
87
|
-
"A role's latest dispatch: its state, its record once finished, its capped reply, and hints (climb, violation, failed, limit and failover lines). Reading a finished record marks it read: catherd's messages announce records, and this is how you read one.",
|
|
88
|
-
inputSchema: { run: z.string(), name: z.string().regex(ID_PATTERN) },
|
|
89
|
-
},
|
|
90
|
-
(a) => handle(() => result(deps, a)),
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
server.registerTool(
|
|
94
|
-
"set_next",
|
|
95
|
-
{
|
|
96
|
-
description:
|
|
97
|
-
"Record the run's next step, the last line of state.md: when pausing, or when the plan changes. dispatch, climb and land write state.md themselves. Returns { state } (state.md's text), and hints ['state.md not refreshed: …'] with state null when git fails (the step is still recorded).",
|
|
98
|
-
inputSchema: { run: z.string(), next: z.string().min(1) },
|
|
99
|
-
},
|
|
100
|
-
(a) => handle(() => setNext(a)),
|
|
101
|
-
);
|
|
102
|
-
|
|
103
|
-
server.registerTool(
|
|
104
|
-
"record_agent_run",
|
|
105
|
-
{
|
|
106
|
-
description:
|
|
107
|
-
'After every native Claude subagent (Agent tool) of a run, record what the Agent result reported: total_tokens and duration_ms. The budget counts it. Pass lane (e.g. M1.L1) when the subagent worked a lane, so its time counts toward that lane\'s kind in the catalog timings. status says how the subagent\'s work ended: for a verifier it is the verdict, so pass status: "failed" when its verdict is FAIL; only a PASS is recorded ok. For a verifier, pass verdict: the first line of its reply; a VERDICT: BLOCKED: environment — <probe> (status "failed") is then a blocker to surface to the owner, not a fix round. Pass reply_status, the STATUS word its reply ends with: a reviewer that stopped partial is not the milestone review.',
|
|
108
|
-
inputSchema: {
|
|
109
|
-
run: z.string(),
|
|
110
|
-
name: z.string().regex(ID_PATTERN),
|
|
111
|
-
role: z.enum(ROLES),
|
|
112
|
-
rung: z.string().min(3),
|
|
113
|
-
total_tokens: z.number().int().nonnegative(),
|
|
114
|
-
duration_ms: z.number().nonnegative().optional(),
|
|
115
|
-
cost_usd: z.number().nonnegative().optional(),
|
|
116
|
-
status: z.enum(["ok", "failed", "cancelled"]).default("ok"),
|
|
117
|
-
lane: z.string().regex(ID_PATTERN).optional(),
|
|
118
|
-
verdict: z.string().min(1).optional(),
|
|
119
|
-
reply_status: z.enum(["complete", "partial", "blocked", "refused", "flaky"]).optional(),
|
|
120
|
-
},
|
|
121
|
-
},
|
|
122
|
-
(a) =>
|
|
123
|
-
handle(() =>
|
|
124
|
-
recordAgentRun(deps, {
|
|
125
|
-
lane: a.lane,
|
|
126
|
-
run: a.run,
|
|
127
|
-
name: a.name,
|
|
128
|
-
role: a.role,
|
|
129
|
-
rung: a.rung,
|
|
130
|
-
totalTokens: a.total_tokens,
|
|
131
|
-
durationMs: a.duration_ms,
|
|
132
|
-
costUsd: a.cost_usd,
|
|
133
|
-
status: a.status,
|
|
134
|
-
verdict: a.verdict,
|
|
135
|
-
replyStatus: a.reply_status,
|
|
136
|
-
}),
|
|
137
|
-
),
|
|
138
|
-
);
|
|
139
|
-
|
|
140
|
-
server.registerTool(
|
|
141
|
-
"read_knowledge",
|
|
142
|
-
{
|
|
143
|
-
description:
|
|
144
|
-
"What past runs of this repo learned, one line per landed milestone that said something. Read it before an architect plans a new run.",
|
|
145
|
-
inputSchema: { repo: z.string().min(1) },
|
|
146
|
-
},
|
|
147
|
-
(a) => handle(() => readKnowledge(a.repo)),
|
|
148
|
-
);
|
|
149
|
-
|
|
150
|
-
server.registerTool(
|
|
151
|
-
"runs_summary",
|
|
152
|
-
{
|
|
153
|
-
description:
|
|
154
|
-
"Per role and rung over past runs: runs, ok, refusals, climbs from that rung, seconds and tokens; reported Claude subagent runs; and what the user's harness customizations cost per run (median first-turn input, native against isolated; with a run, over that run's repo; null below 3 runs a side or when native is not dearer; none for Codex, which reports no per-request input). Reads only.",
|
|
155
|
-
inputSchema: {
|
|
156
|
-
run: z.string().optional(),
|
|
157
|
-
repo: z.string().optional(),
|
|
158
|
-
role: z.enum(ROLES).optional(),
|
|
159
|
-
since_days: z.number().positive().optional(),
|
|
160
|
-
},
|
|
161
|
-
},
|
|
162
|
-
(a) => handle(() => runsSummary({ run: a.run, repo: a.repo, role: a.role, sinceDays: a.since_days })),
|
|
163
|
-
);
|
|
164
|
-
}
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
-
import { z } from "zod";
|
|
3
|
-
import { CatherdError } from "../../domain/errors.ts";
|
|
4
|
-
import { PROFILE_NAME, ProfilePatchSchema } from "../../domain/profile.ts";
|
|
5
|
-
import { ROLES } from "../../domain/roles.ts";
|
|
6
|
-
import { gitToplevel } from "../../infra/git.ts";
|
|
7
|
-
import type { Deps } from "../../services/ports.ts";
|
|
8
|
-
import { syncSources } from "../../services/source-sync.ts";
|
|
9
|
-
import { handle } from "./result.ts";
|
|
10
|
-
|
|
11
|
-
const PROFILE = z.string().regex(PROFILE_NAME).optional();
|
|
12
|
-
const REPO = z.string().min(1).optional();
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* The git toplevel of `repo` (default: this server's directory, null outside a repository). A `repo`
|
|
16
|
-
* given outside a repository is refused, as run_start does: never the global profile in its stead.
|
|
17
|
-
*/
|
|
18
|
-
const toplevel = async (repo: string | undefined): Promise<string | null> => {
|
|
19
|
-
const top = await gitToplevel(repo ?? process.cwd());
|
|
20
|
-
if (top === null && repo !== undefined)
|
|
21
|
-
throw new CatherdError("E_IO_PATH", `${repo} is not inside a git repository`, {
|
|
22
|
-
fix: "pass the path of the repository, or leave repo out for the active profile",
|
|
23
|
-
});
|
|
24
|
-
return top;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
export function registerSetupTools(server: McpServer, deps: Deps): void {
|
|
28
|
-
server.registerTool(
|
|
29
|
-
"catalog_query",
|
|
30
|
-
{
|
|
31
|
-
description:
|
|
32
|
-
"Models catherd can place, with capabilities, the roles they can fill, their scored rungs (backend:model#effort), any 'treat like', their cost under the billing of `repo`'s profile, and whether this account's last listing offers them (`listed: false`: it does not); `enabled: false` rungs are unscored. Each score has its confidence, source and date (`from` names the rung a lent value belongs to); each model its price and speed facts, each rung catherd's own run evidence. Scored models first. opencode's models are as listed in `repo` (default: this server's directory), as route sees them there.",
|
|
33
|
-
inputSchema: {
|
|
34
|
-
repo: z.string().min(1).optional(),
|
|
35
|
-
role: z.enum(ROLES).optional(),
|
|
36
|
-
backend: z.string().optional(),
|
|
37
|
-
text: z.string().optional(),
|
|
38
|
-
scored_only: z.boolean().default(false),
|
|
39
|
-
limit: z.number().int().min(1).max(500).default(50),
|
|
40
|
-
},
|
|
41
|
-
},
|
|
42
|
-
(a) =>
|
|
43
|
-
handle(async () => {
|
|
44
|
-
// outside a git repository: the global listings and the active profile
|
|
45
|
-
const repo = (await gitToplevel(a.repo ?? process.cwd())) ?? undefined;
|
|
46
|
-
return deps.routing.catalog(
|
|
47
|
-
{ role: a.role, backend: a.backend, text: a.text, scoredOnly: a.scored_only, limit: a.limit, repo },
|
|
48
|
-
deps.profiles.forRepo(repo ?? null).billing,
|
|
49
|
-
);
|
|
50
|
-
}),
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
server.registerTool(
|
|
54
|
-
"catalog_sync",
|
|
55
|
-
{
|
|
56
|
-
description:
|
|
57
|
-
"Fetch the public model facts and scores now (models.dev, OpenRouter, LiteLLM, Arena, Vectara, Epoch AI, and Artificial Analysis with the user's key): each source at most every 12 hours unless `force`. Returns the rungs newly scored, the user's treat-likes no longer needed (their rung now has values of its own), the sources that failed (each keeps its last good answer) and each source's state. route and catalog_query read the result at once.",
|
|
58
|
-
inputSchema: { force: z.boolean().default(false) },
|
|
59
|
-
},
|
|
60
|
-
(a) =>
|
|
61
|
-
handle(async () => {
|
|
62
|
-
// (1.2 minor) a sync already running (the boot sync) answers busy at once, never a 120 s wait
|
|
63
|
-
const r = await (deps.sync ?? ((o) => syncSources(o)))({ force: a.force, wait: false });
|
|
64
|
-
return {
|
|
65
|
-
newlyScored: r.newlyScored,
|
|
66
|
-
standInsNoLongerNeeded: r.noLongerNeeded,
|
|
67
|
-
failed: r.failed,
|
|
68
|
-
sources: r.sources,
|
|
69
|
-
warnings: r.warnings,
|
|
70
|
-
...(r.busy ? { busy: "another sync was running; call catalog_sync again" } : {}),
|
|
71
|
-
};
|
|
72
|
-
}),
|
|
73
|
-
);
|
|
74
|
-
|
|
75
|
-
server.registerTool(
|
|
76
|
-
"profile_get",
|
|
77
|
-
{
|
|
78
|
-
description:
|
|
79
|
-
"A profile (without a name: the profile this repo runs on, i.e. the one bound to `repo`, default this server's directory, else the active one) as the run engine reads it, with every default filled in: per role its access mode, rungs and default rung, and whether its backend enforces the access (enforcement); billing, jev, harness isolation, failover, budget, timeouts, preflight, lock and notify. Also `active` (the global active profile), `here` (the profile this repo runs on) and every name.",
|
|
80
|
-
inputSchema: { name: PROFILE, repo: REPO, raw: z.boolean().optional() },
|
|
81
|
-
},
|
|
82
|
-
(a) =>
|
|
83
|
-
handle(async () =>
|
|
84
|
-
a.raw && deps.profiles.raw
|
|
85
|
-
? deps.profiles.raw(a.name, await toplevel(a.repo))
|
|
86
|
-
: deps.profiles.get(a.name, await toplevel(a.repo)),
|
|
87
|
-
),
|
|
88
|
-
);
|
|
89
|
-
|
|
90
|
-
server.registerTool(
|
|
91
|
-
"profile_validate",
|
|
92
|
-
{
|
|
93
|
-
description:
|
|
94
|
-
"Check a profile (without a name: the profile this repo runs on, as in profile_get). errors block a save: the worker disabled, an enabled role with no usable rung, an unscored rung without a 'treat like', a failover stand-in unscored or on the same quota, a backend catherd cannot run. warnings do not: an access mode other than the role's default, a model the backend's listing lacks, a stand-in that never runs, a quota none of whose rungs ever starts a lane, and every kind and difficulty no worker rung clears. Each has a path, a message and often a fix.",
|
|
95
|
-
inputSchema: { name: PROFILE, repo: REPO },
|
|
96
|
-
},
|
|
97
|
-
(a) => handle(async () => deps.profiles.validate(a.name, await toplevel(a.repo))),
|
|
98
|
-
);
|
|
99
|
-
|
|
100
|
-
server.registerTool(
|
|
101
|
-
"profile_set",
|
|
102
|
-
{
|
|
103
|
-
description:
|
|
104
|
-
"Apply a patch to a profile (without a name: the profile this repo runs on, as in profile_get; a new name starts from the default profile): objective, jev.use, billing, roles (enabled, access, network, rungs, defaultRung, timeouts: a role's own idleMin and wallMin over the profile's), harness isolation per backend, failover, budget, timeouts, preflight.confirm, lock.heavy and notify. Lists replace, maps merge, and null removes a key. An unknown key is refused. It validates first and writes nothing when invalid, unless the patch repairs an invalid profile: it fixes at least one of its errors and adds none, and then it saves with `errors` listing those still open. A save rewrites the agent files and relinks them. Returns saved, the errors and warnings, the diff, and newSessionNeededFor: the agents that apply from the next Claude Code session.",
|
|
105
|
-
inputSchema: { name: PROFILE, repo: REPO, patch: ProfilePatchSchema },
|
|
106
|
-
},
|
|
107
|
-
(a) => handle(async () => deps.profiles.set(a.name, a.patch, await toplevel(a.repo))),
|
|
108
|
-
);
|
|
109
|
-
}
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
-
import { z } from "zod";
|
|
3
|
-
import {
|
|
4
|
-
BaseRefSchema,
|
|
5
|
-
RELEASES,
|
|
6
|
-
WORKSPACE_LIMIT,
|
|
7
|
-
WorkspaceBudgetSchema,
|
|
8
|
-
WorkspaceIdSchema,
|
|
9
|
-
} from "../../domain/workspace.ts";
|
|
10
|
-
import { pauseWorkspace, resumeWorkspace } from "../../services/pause.ts";
|
|
11
|
-
import type { Deps } from "../../services/ports.ts";
|
|
12
|
-
import { setWorkspaceBudget, workspaceContract } from "../../services/workspace-admission.ts";
|
|
13
|
-
import {
|
|
14
|
-
inspectWorkspace,
|
|
15
|
-
startWorkspace,
|
|
16
|
-
startWorkspaceChild,
|
|
17
|
-
workspaceStatus,
|
|
18
|
-
} from "../../services/workspace-service.ts";
|
|
19
|
-
import { handle } from "./result.ts";
|
|
20
|
-
|
|
21
|
-
const id = WorkspaceIdSchema;
|
|
22
|
-
const repos = z
|
|
23
|
-
.record(id, z.string().min(1))
|
|
24
|
-
.refine((r) => Object.keys(r).length <= WORKSPACE_LIMIT)
|
|
25
|
-
.optional();
|
|
26
|
-
const aLines = z.array(z.string().min(1)).min(1).max(100);
|
|
27
|
-
|
|
28
|
-
export function registerWorkspaceTools(server: McpServer, deps: Deps): void {
|
|
29
|
-
server.registerTool(
|
|
30
|
-
"workspace_inspect",
|
|
31
|
-
{
|
|
32
|
-
description:
|
|
33
|
-
"Resolve explicit workspace members from catherd.workspace.json or a supplied repo map. The root may be a plain directory. Reads only; never scans unrelated repositories.",
|
|
34
|
-
inputSchema: { root: z.string().min(1), repos },
|
|
35
|
-
},
|
|
36
|
-
(a) => handle(() => inspectWorkspace(a.root, a.repos)),
|
|
37
|
-
);
|
|
38
|
-
server.registerTool(
|
|
39
|
-
"workspace_start",
|
|
40
|
-
{
|
|
41
|
-
description:
|
|
42
|
-
"Snapshot a workspace's selected repositories, step dependency graph and optional shared budget (no cap unless given; raise it later with workspace_budget). A step releases its dependents when its milestone lands, or with release: 'merge' and base (e.g. origin/main) once its landed commit is an ancestor of that ref, checked when a dependent asks. Returns workspace.id and dir. Child single-repo runs are created later with workspace_child_start; no commits or pushes are performed.",
|
|
43
|
-
inputSchema: {
|
|
44
|
-
root: z.string().min(1),
|
|
45
|
-
title: z.string().min(1),
|
|
46
|
-
a_lines: aLines,
|
|
47
|
-
repos,
|
|
48
|
-
steps: z
|
|
49
|
-
.array(
|
|
50
|
-
// #43 finding 6: an unknown key (dependsOn for depends_on) is refused, never dropped
|
|
51
|
-
z.strictObject({
|
|
52
|
-
id,
|
|
53
|
-
repo: id,
|
|
54
|
-
title: z.string().min(1),
|
|
55
|
-
a_lines: aLines,
|
|
56
|
-
depends_on: z.array(id).max(WORKSPACE_LIMIT).optional(),
|
|
57
|
-
milestone: id.optional(),
|
|
58
|
-
release: z.enum(RELEASES).optional(),
|
|
59
|
-
base: BaseRefSchema.optional(),
|
|
60
|
-
}),
|
|
61
|
-
)
|
|
62
|
-
.min(1)
|
|
63
|
-
.max(WORKSPACE_LIMIT),
|
|
64
|
-
budget: WorkspaceBudgetSchema.optional(),
|
|
65
|
-
},
|
|
66
|
-
},
|
|
67
|
-
(a) =>
|
|
68
|
-
handle(() =>
|
|
69
|
-
startWorkspace(deps, {
|
|
70
|
-
root: a.root,
|
|
71
|
-
title: a.title,
|
|
72
|
-
aLines: a.a_lines,
|
|
73
|
-
repos: a.repos,
|
|
74
|
-
budget: a.budget,
|
|
75
|
-
steps: a.steps.map((s) => ({
|
|
76
|
-
id: s.id,
|
|
77
|
-
repo: s.repo,
|
|
78
|
-
title: s.title,
|
|
79
|
-
aLines: s.a_lines,
|
|
80
|
-
dependsOn: s.depends_on,
|
|
81
|
-
milestone: s.milestone,
|
|
82
|
-
release: s.release,
|
|
83
|
-
base: s.base,
|
|
84
|
-
})),
|
|
85
|
-
}),
|
|
86
|
-
),
|
|
87
|
-
);
|
|
88
|
-
server.registerTool(
|
|
89
|
-
"workspace_contract",
|
|
90
|
-
{
|
|
91
|
-
description:
|
|
92
|
-
"Read or write the workspace's shared contract before any child run exists. Once a child is created the contract is frozen and copied into each child as workspace-contract.md.",
|
|
93
|
-
inputSchema: { workspace: z.string().min(1), content: z.string().optional() },
|
|
94
|
-
},
|
|
95
|
-
(a) => handle(() => workspaceContract(a)),
|
|
96
|
-
);
|
|
97
|
-
server.registerTool(
|
|
98
|
-
"workspace_child_start",
|
|
99
|
-
{
|
|
100
|
-
description:
|
|
101
|
-
"Create or return a step's single-repo run once its dependencies have landed their declared milestone, every dispatch of theirs is collected, and each 'merge' release's commit is in its base ref (fetch first; nothing is polled). Returns run, dir and the frozen contract path. Use the existing route, dispatch, verification and land tools on this child run.",
|
|
102
|
-
inputSchema: { workspace: z.string().min(1), step: id },
|
|
103
|
-
},
|
|
104
|
-
(a) => handle(() => startWorkspaceChild(deps, a)),
|
|
105
|
-
);
|
|
106
|
-
server.registerTool(
|
|
107
|
-
"workspace_pause",
|
|
108
|
-
{
|
|
109
|
-
description:
|
|
110
|
-
"Pause every child of a workspace on one blocker (a VPN, Docker down): admission refuses each dispatch and child start with E_ADMIT_PAUSED and the reason until workspace_resume. Running roles finish. status shows the pause first. Push the reason to the owner once.",
|
|
111
|
-
inputSchema: { workspace: z.string().min(1), reason: z.string().min(1) },
|
|
112
|
-
},
|
|
113
|
-
(a) => handle(() => pauseWorkspace(deps.now(), a)),
|
|
114
|
-
);
|
|
115
|
-
server.registerTool(
|
|
116
|
-
"workspace_resume",
|
|
117
|
-
{
|
|
118
|
-
description: "Lift a workspace's pause. Returns the pause it lifted, or null when none was in force.",
|
|
119
|
-
inputSchema: { workspace: z.string().min(1) },
|
|
120
|
-
},
|
|
121
|
-
(a) => handle(() => resumeWorkspace(deps.now(), a)),
|
|
122
|
-
);
|
|
123
|
-
const cap = z.number().finite().nonnegative().nullable().optional();
|
|
124
|
-
server.registerTool(
|
|
125
|
-
"workspace_budget",
|
|
126
|
-
{
|
|
127
|
-
description:
|
|
128
|
-
"Set or raise a workspace's shared budget: each of minutes, tokens and usd given as a number replaces that cap, null removes it, and one left out stays. A workspace has no cap unless one is set; its minutes count from its first child. Returns the budget, the spend and its status.",
|
|
129
|
-
inputSchema: { workspace: z.string().min(1), minutes: cap, tokens: cap, usd: cap },
|
|
130
|
-
},
|
|
131
|
-
(a) => handle(() => setWorkspaceBudget(deps, a)),
|
|
132
|
-
);
|
|
133
|
-
server.registerTool(
|
|
134
|
-
"workspace_status",
|
|
135
|
-
{
|
|
136
|
-
description:
|
|
137
|
-
"Read a workspace's step readiness, child run ids, landed milestones, aggregate spending and shared budget. Reads only; never starts children automatically.",
|
|
138
|
-
inputSchema: { workspace: z.string().min(1) },
|
|
139
|
-
},
|
|
140
|
-
(a) => handle(() => workspaceStatus(deps, a.workspace)),
|
|
141
|
-
);
|
|
142
|
-
}
|