@martinloop/mcp 0.5.1 → 0.5.5
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 +52 -14
- package/dist/server-validation.js +2 -0
- package/dist/server.js +30 -3
- package/dist/tools/run-loop.d.ts +1 -0
- package/dist/tools/run-loop.js +25 -4
- package/dist/tools/tool-response.d.ts +9 -1
- package/dist/tools/tool-response.js +7 -5
- package/dist/tools/tool-support.js +62 -271
- package/dist/vendor/adapters/claude-cli.d.ts +9 -0
- package/dist/vendor/adapters/claude-cli.js +19 -2
- package/dist/vendor/adapters/codex-capabilities.d.ts +69 -0
- package/dist/vendor/adapters/codex-capabilities.js +265 -0
- package/dist/vendor/adapters/codex-cli.d.ts +23 -0
- package/dist/vendor/adapters/codex-cli.js +72 -0
- package/dist/vendor/adapters/codex-host.d.ts +107 -0
- package/dist/vendor/adapters/codex-host.js +595 -0
- package/dist/vendor/adapters/codex-launcher.d.ts +7 -135
- package/dist/vendor/adapters/codex-launcher.js +17 -766
- package/dist/vendor/adapters/index.d.ts +4 -2
- package/dist/vendor/adapters/index.js +4 -2
- package/dist/vendor/contracts/agent-execution.d.ts +9 -0
- package/dist/vendor/contracts/agent-execution.js +14 -0
- package/dist/vendor/contracts/index.d.ts +9 -0
- package/dist/vendor/contracts/index.js +1 -0
- package/dist/vendor/core/grounding.js +42 -7
- package/dist/vendor/core/index.d.ts +5 -2
- package/dist/vendor/core/index.js +12 -3
- package/dist/vendor/presentation/governed-plan.d.ts +33 -0
- package/dist/vendor/presentation/governed-plan.js +146 -0
- package/dist/vendor/presentation/index.d.ts +7 -0
- package/dist/vendor/presentation/index.js +7 -0
- package/dist/vendor/presentation/motion.d.ts +12 -0
- package/dist/vendor/presentation/motion.js +123 -0
- package/dist/vendor/presentation/status.d.ts +8 -0
- package/dist/vendor/presentation/status.js +11 -0
- package/dist/vendor/presentation/table.d.ts +11 -0
- package/dist/vendor/presentation/table.js +47 -0
- package/dist/vendor/presentation/text.d.ts +6 -0
- package/dist/vendor/presentation/text.js +30 -0
- package/dist/vendor/presentation/theme.d.ts +31 -0
- package/dist/vendor/presentation/theme.js +82 -0
- package/dist/vendor/presentation/verified-handoff.d.ts +15 -0
- package/dist/vendor/presentation/verified-handoff.js +151 -0
- package/package.json +3 -2
- package/server.json +2 -2
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { padRightVisible, truncateVisible, visibleWidth } from "./text.js";
|
|
2
|
+
function columnWidth(rows, column) {
|
|
3
|
+
const contentWidth = Math.max(visibleWidth(column.header), ...rows.map((row) => visibleWidth(column.value(row))), column.minWidth ?? 0);
|
|
4
|
+
return Math.min(contentWidth, column.maxWidth ?? Number.POSITIVE_INFINITY);
|
|
5
|
+
}
|
|
6
|
+
function fitWidths(naturalWidths, columns, availableWidth) {
|
|
7
|
+
const widths = [...naturalWidths];
|
|
8
|
+
const floors = columns.map((column, index) => Math.min(widths[index] ?? 0, Math.max(4, visibleWidth(column.header))));
|
|
9
|
+
while (widths.reduce((sum, width) => sum + width, 0) > availableWidth) {
|
|
10
|
+
let candidate = -1;
|
|
11
|
+
let largestSlack = 0;
|
|
12
|
+
for (let index = 0; index < widths.length; index += 1) {
|
|
13
|
+
const slack = (widths[index] ?? 0) - (floors[index] ?? 0);
|
|
14
|
+
if (slack > largestSlack) {
|
|
15
|
+
candidate = index;
|
|
16
|
+
largestSlack = slack;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
if (candidate < 0) {
|
|
20
|
+
break;
|
|
21
|
+
}
|
|
22
|
+
widths[candidate] = (widths[candidate] ?? 0) - 1;
|
|
23
|
+
}
|
|
24
|
+
return widths;
|
|
25
|
+
}
|
|
26
|
+
export function renderTable(rows, columns, options = {}) {
|
|
27
|
+
if (columns.length === 0) {
|
|
28
|
+
return "";
|
|
29
|
+
}
|
|
30
|
+
const normalizedOptions = typeof options === "number" ? { width: options } : options;
|
|
31
|
+
const separator = normalizedOptions.separator ?? " │ ";
|
|
32
|
+
const naturalWidths = columns.map((column) => columnWidth(rows, column));
|
|
33
|
+
const separatorWidth = visibleWidth(separator) * (columns.length - 1);
|
|
34
|
+
const availableWidth = normalizedOptions.width === undefined
|
|
35
|
+
? Number.POSITIVE_INFINITY
|
|
36
|
+
: Math.max(columns.length * 4, normalizedOptions.width - separatorWidth);
|
|
37
|
+
const widths = Number.isFinite(availableWidth)
|
|
38
|
+
? fitWidths(naturalWidths, columns, availableWidth)
|
|
39
|
+
: naturalWidths;
|
|
40
|
+
const renderCells = (cells) => cells
|
|
41
|
+
.map((cell, index) => padRightVisible(truncateVisible(cell, widths[index] ?? 0), widths[index] ?? 0))
|
|
42
|
+
.join(separator);
|
|
43
|
+
const header = renderCells(columns.map((column) => column.header));
|
|
44
|
+
const divider = widths.map((width) => "─".repeat(width)).join("─┼─");
|
|
45
|
+
const body = rows.map((row) => renderCells(columns.map((column) => column.value(row))));
|
|
46
|
+
return [header, divider, ...body].join("\n");
|
|
47
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare function stripAnsi(value: string): string;
|
|
2
|
+
export declare function visibleWidth(value: string): number;
|
|
3
|
+
export declare function padRightVisible(value: string, width: number): string;
|
|
4
|
+
export declare function truncateVisible(value: string, width: number): string;
|
|
5
|
+
export declare function terminalWidth(width?: number): number;
|
|
6
|
+
export declare function horizontalRule(width: number, character?: string): string;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const csiPattern = /\u001B\[[0-?]*[ -/]*[@-~]/g;
|
|
2
|
+
const oscPattern = /\u001B\][^\u0007]*(?:\u0007|\u001B\\)/g;
|
|
3
|
+
export function stripAnsi(value) {
|
|
4
|
+
return value.replace(oscPattern, "").replace(csiPattern, "");
|
|
5
|
+
}
|
|
6
|
+
export function visibleWidth(value) {
|
|
7
|
+
return Array.from(stripAnsi(value)).length;
|
|
8
|
+
}
|
|
9
|
+
export function padRightVisible(value, width) {
|
|
10
|
+
return value + " ".repeat(Math.max(0, width - visibleWidth(value)));
|
|
11
|
+
}
|
|
12
|
+
export function truncateVisible(value, width) {
|
|
13
|
+
if (width <= 0) {
|
|
14
|
+
return "";
|
|
15
|
+
}
|
|
16
|
+
if (visibleWidth(value) <= width) {
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
if (width === 1) {
|
|
20
|
+
return "…";
|
|
21
|
+
}
|
|
22
|
+
return Array.from(stripAnsi(value)).slice(0, width - 1).join("") + "…";
|
|
23
|
+
}
|
|
24
|
+
export function terminalWidth(width) {
|
|
25
|
+
const resolved = width ?? process.stdout.columns ?? 80;
|
|
26
|
+
return Math.max(40, Math.min(160, Math.floor(resolved)));
|
|
27
|
+
}
|
|
28
|
+
export function horizontalRule(width, character = "─") {
|
|
29
|
+
return character.repeat(Math.max(0, width));
|
|
30
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export type MartinTone = "brand" | "success" | "danger" | "warning" | "info" | "muted" | "plain";
|
|
2
|
+
export interface RenderEnvironment {
|
|
3
|
+
readonly color?: "auto" | "always" | "never";
|
|
4
|
+
readonly isTty?: boolean;
|
|
5
|
+
readonly noColor?: boolean;
|
|
6
|
+
readonly term?: string;
|
|
7
|
+
readonly colorDepth?: number;
|
|
8
|
+
}
|
|
9
|
+
export declare const palette: {
|
|
10
|
+
readonly brand: readonly [107, 85, 198];
|
|
11
|
+
readonly success: readonly [94, 225, 115];
|
|
12
|
+
readonly danger: readonly [255, 93, 93];
|
|
13
|
+
readonly warning: readonly [244, 200, 74];
|
|
14
|
+
readonly info: readonly [85, 199, 243];
|
|
15
|
+
readonly muted: readonly [148, 163, 184];
|
|
16
|
+
readonly plain: readonly [248, 250, 252];
|
|
17
|
+
};
|
|
18
|
+
declare function rgb(red: number, green: number, blue: number): string;
|
|
19
|
+
export declare function resolveColorEnabled(env?: RenderEnvironment): boolean;
|
|
20
|
+
export declare function paint(value: string, tone: MartinTone, env?: RenderEnvironment): string;
|
|
21
|
+
export declare function bold(value: string, env?: RenderEnvironment): string;
|
|
22
|
+
export declare function dim(value: string, env?: RenderEnvironment): string;
|
|
23
|
+
export declare const terminalAnsi: {
|
|
24
|
+
readonly reset: string;
|
|
25
|
+
readonly hideCursor: string;
|
|
26
|
+
readonly showCursor: string;
|
|
27
|
+
readonly eraseLine: string;
|
|
28
|
+
readonly cursorHome: "\r";
|
|
29
|
+
readonly rgb: typeof rgb;
|
|
30
|
+
};
|
|
31
|
+
export {};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
const escape = "\u001B[";
|
|
2
|
+
const basic = {
|
|
3
|
+
reset: escape + "0m",
|
|
4
|
+
bold: escape + "1m",
|
|
5
|
+
dim: escape + "2m",
|
|
6
|
+
brand: escape + "35m",
|
|
7
|
+
success: escape + "32m",
|
|
8
|
+
danger: escape + "31m",
|
|
9
|
+
warning: escape + "33m",
|
|
10
|
+
info: escape + "36m",
|
|
11
|
+
muted: escape + "90m",
|
|
12
|
+
plain: "",
|
|
13
|
+
};
|
|
14
|
+
export const palette = {
|
|
15
|
+
brand: [107, 85, 198],
|
|
16
|
+
success: [94, 225, 115],
|
|
17
|
+
danger: [255, 93, 93],
|
|
18
|
+
warning: [244, 200, 74],
|
|
19
|
+
info: [85, 199, 243],
|
|
20
|
+
muted: [148, 163, 184],
|
|
21
|
+
plain: [248, 250, 252],
|
|
22
|
+
};
|
|
23
|
+
function rgb(red, green, blue) {
|
|
24
|
+
return escape + "38;2;" + red + ";" + green + ";" + blue + "m";
|
|
25
|
+
}
|
|
26
|
+
function resolveColorDepth() {
|
|
27
|
+
const stream = process.stdout;
|
|
28
|
+
try {
|
|
29
|
+
return stream.getColorDepth?.() ?? 1;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return 1;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export function resolveColorEnabled(env = {}) {
|
|
36
|
+
if (env.color === "always") {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
if (env.color === "never") {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
const noColor = env.noColor ?? Object.prototype.hasOwnProperty.call(process.env, "NO_COLOR");
|
|
43
|
+
if (noColor) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
const term = env.term ?? process.env.TERM;
|
|
47
|
+
if (term === "dumb") {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
return env.isTty ?? process.stdout.isTTY === true;
|
|
51
|
+
}
|
|
52
|
+
function toneCode(tone, env) {
|
|
53
|
+
if (tone === "plain") {
|
|
54
|
+
return "";
|
|
55
|
+
}
|
|
56
|
+
const depth = env.colorDepth ?? resolveColorDepth();
|
|
57
|
+
if (depth >= 24) {
|
|
58
|
+
const [red, green, blue] = palette[tone];
|
|
59
|
+
return rgb(red, green, blue);
|
|
60
|
+
}
|
|
61
|
+
return basic[tone];
|
|
62
|
+
}
|
|
63
|
+
export function paint(value, tone, env = {}) {
|
|
64
|
+
if (!resolveColorEnabled(env) || tone === "plain") {
|
|
65
|
+
return value;
|
|
66
|
+
}
|
|
67
|
+
return toneCode(tone, env) + value + basic.reset;
|
|
68
|
+
}
|
|
69
|
+
export function bold(value, env = {}) {
|
|
70
|
+
return resolveColorEnabled(env) ? basic.bold + value + basic.reset : value;
|
|
71
|
+
}
|
|
72
|
+
export function dim(value, env = {}) {
|
|
73
|
+
return resolveColorEnabled(env) ? basic.dim + value + basic.reset : value;
|
|
74
|
+
}
|
|
75
|
+
export const terminalAnsi = {
|
|
76
|
+
reset: basic.reset,
|
|
77
|
+
hideCursor: escape + "?25l",
|
|
78
|
+
showCursor: escape + "?25h",
|
|
79
|
+
eraseLine: escape + "2K",
|
|
80
|
+
cursorHome: "\r",
|
|
81
|
+
rgb,
|
|
82
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { VerifiedHandoffV1 } from "../contracts/index.js";
|
|
2
|
+
import { type RenderEnvironment } from "./theme.js";
|
|
3
|
+
export interface VerifiedHandoffRenderOptions {
|
|
4
|
+
readonly width?: number;
|
|
5
|
+
readonly environment?: RenderEnvironment;
|
|
6
|
+
}
|
|
7
|
+
export declare function renderVerifiedHandoff(handoff: VerifiedHandoffV1, options?: VerifiedHandoffRenderOptions): string;
|
|
8
|
+
/**
|
|
9
|
+
* Renders a VerifiedHandoffV1 as plain Markdown (no ANSI).
|
|
10
|
+
*
|
|
11
|
+
* CRITICAL: replicates the exact effectiveOutcome trust-authority check from
|
|
12
|
+
* renderVerifiedHandoff so that CLI and MCP surfaces always agree on
|
|
13
|
+
* VERIFIED / NEEDS REVIEW / STOPPED for the same input.
|
|
14
|
+
*/
|
|
15
|
+
export declare function renderVerifiedHandoffMarkdown(handoff: VerifiedHandoffV1, options?: VerifiedHandoffRenderOptions): string;
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { renderTable } from "./table.js";
|
|
2
|
+
import { horizontalRule, terminalWidth, truncateVisible } from "./text.js";
|
|
3
|
+
import { bold, paint, } from "./theme.js";
|
|
4
|
+
function outcomeTone(outcome) {
|
|
5
|
+
if (outcome === "VERIFIED") {
|
|
6
|
+
return "success";
|
|
7
|
+
}
|
|
8
|
+
if (outcome === "STOPPED") {
|
|
9
|
+
return "danger";
|
|
10
|
+
}
|
|
11
|
+
return "warning";
|
|
12
|
+
}
|
|
13
|
+
function evidenceTone(status) {
|
|
14
|
+
if (status === "PASSED") {
|
|
15
|
+
return "success";
|
|
16
|
+
}
|
|
17
|
+
if (status === "FAILED" || status === "CONTRADICTED") {
|
|
18
|
+
return "danger";
|
|
19
|
+
}
|
|
20
|
+
return "warning";
|
|
21
|
+
}
|
|
22
|
+
function humanize(value) {
|
|
23
|
+
return value.replaceAll("_", " ").toLowerCase();
|
|
24
|
+
}
|
|
25
|
+
function formatCost(usd, provenance) {
|
|
26
|
+
if (provenance === "unavailable") {
|
|
27
|
+
return "unavailable";
|
|
28
|
+
}
|
|
29
|
+
const amount = "$" + usd.toFixed(2);
|
|
30
|
+
if (provenance === "actual") {
|
|
31
|
+
return amount + " provider-settled actual";
|
|
32
|
+
}
|
|
33
|
+
if (provenance === "calculated") {
|
|
34
|
+
return amount + " calculated from observed usage";
|
|
35
|
+
}
|
|
36
|
+
return amount + " estimated";
|
|
37
|
+
}
|
|
38
|
+
function checkSymbol(status) {
|
|
39
|
+
if (status === "PASSED") {
|
|
40
|
+
return "✓";
|
|
41
|
+
}
|
|
42
|
+
if (status === "FAILED" || status === "CONTRADICTED") {
|
|
43
|
+
return "✕";
|
|
44
|
+
}
|
|
45
|
+
return "•";
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Single source of truth for the trust-authority outcome calculation.
|
|
49
|
+
* Both terminal and Markdown renderers call this — never duplicate.
|
|
50
|
+
* Fail-closed: omitting executionMode or governanceClaimEligible downgrades
|
|
51
|
+
* VERIFIED → NEEDS_REVIEW, so legacy handoffs cannot claim full governance.
|
|
52
|
+
*/
|
|
53
|
+
function resolveHandoffAuthority(handoff) {
|
|
54
|
+
const executionMode = handoff.executionMode ?? "simulated";
|
|
55
|
+
const governanceClaimEligible = executionMode === "governed" && handoff.governanceClaimEligible === true;
|
|
56
|
+
const effectiveOutcome = handoff.outcome === "VERIFIED" && !governanceClaimEligible
|
|
57
|
+
? "NEEDS_REVIEW"
|
|
58
|
+
: handoff.outcome;
|
|
59
|
+
return { executionMode, governanceClaimEligible, effectiveOutcome };
|
|
60
|
+
}
|
|
61
|
+
export function renderVerifiedHandoff(handoff, options = {}) {
|
|
62
|
+
const environment = options.environment ?? {};
|
|
63
|
+
const width = terminalWidth(options.width);
|
|
64
|
+
const bounded = (line) => truncateVisible(line, width);
|
|
65
|
+
const { executionMode, governanceClaimEligible, effectiveOutcome } = resolveHandoffAuthority(handoff);
|
|
66
|
+
const outcome = effectiveOutcome.replace("_", " ");
|
|
67
|
+
const checks = handoff.verification.checks.map((check) => ({
|
|
68
|
+
check: check.command,
|
|
69
|
+
status: paint(checkSymbol(check.status) + " " + check.status.replace("_", " "), evidenceTone(check.status), environment),
|
|
70
|
+
}));
|
|
71
|
+
const lines = [
|
|
72
|
+
horizontalRule(width, "━"),
|
|
73
|
+
bold("MARTINLOOP VERIFIED HANDOFF", environment) +
|
|
74
|
+
" — " +
|
|
75
|
+
paint(outcome, outcomeTone(effectiveOutcome), environment),
|
|
76
|
+
horizontalRule(width, "━"),
|
|
77
|
+
"",
|
|
78
|
+
bounded("Task".padEnd(20) + handoff.task.objective),
|
|
79
|
+
bounded("Run".padEnd(20) + handoff.loopId),
|
|
80
|
+
bounded("Execution Mode".padEnd(20) + executionMode.replaceAll("_", " ")),
|
|
81
|
+
bounded("Governed Claim".padEnd(20) + (governanceClaimEligible ? "ELIGIBLE" : "INELIGIBLE")),
|
|
82
|
+
bounded("Verification".padEnd(20) + handoff.verification.status.replace("_", " ")),
|
|
83
|
+
bounded("Scope".padEnd(20) + handoff.scope.status.replaceAll("_", " ")),
|
|
84
|
+
bounded("Test Integrity".padEnd(20) + handoff.testIntegrity.verdict),
|
|
85
|
+
bounded("Receipt Integrity".padEnd(20) + handoff.receiptIntegrity.state),
|
|
86
|
+
bounded("Attempts".padEnd(20) + handoff.usage.attempts),
|
|
87
|
+
bounded("Cost".padEnd(20) +
|
|
88
|
+
formatCost(handoff.usage.actualUsd, handoff.usage.costProvenance)),
|
|
89
|
+
];
|
|
90
|
+
if (checks.length > 0) {
|
|
91
|
+
lines.push("", bold("VERIFICATION EVIDENCE", environment), renderTable(checks, [
|
|
92
|
+
{ header: "CHECK", value: (row) => row.check, minWidth: 24 },
|
|
93
|
+
{ header: "STATUS", value: (row) => row.status, minWidth: 18 },
|
|
94
|
+
], width));
|
|
95
|
+
}
|
|
96
|
+
if (handoff.stopReason) {
|
|
97
|
+
lines.push("", bold("STOP REASON", environment), bounded(" " + paint(humanize(handoff.stopReason), "danger", environment)));
|
|
98
|
+
}
|
|
99
|
+
lines.push("", bold("UNRESOLVED", environment));
|
|
100
|
+
if (handoff.unresolvedWork.length === 0) {
|
|
101
|
+
lines.push(" None recorded.");
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
lines.push(...handoff.unresolvedWork.map((item) => bounded(" - " + item)));
|
|
105
|
+
}
|
|
106
|
+
lines.push("", bold("RECOVERY", environment), bounded(" " + handoff.recovery.summary), "", bold("NEXT", environment), bounded(" " + handoff.nextAction), horizontalRule(width, "━"));
|
|
107
|
+
return lines.map(bounded).join("\n");
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Renders a VerifiedHandoffV1 as plain Markdown (no ANSI).
|
|
111
|
+
*
|
|
112
|
+
* CRITICAL: replicates the exact effectiveOutcome trust-authority check from
|
|
113
|
+
* renderVerifiedHandoff so that CLI and MCP surfaces always agree on
|
|
114
|
+
* VERIFIED / NEEDS REVIEW / STOPPED for the same input.
|
|
115
|
+
*/
|
|
116
|
+
export function renderVerifiedHandoffMarkdown(handoff, options = {}) {
|
|
117
|
+
void options; // width/environment unused in plain Markdown
|
|
118
|
+
const { executionMode, governanceClaimEligible, effectiveOutcome } = resolveHandoffAuthority(handoff);
|
|
119
|
+
const outcome = effectiveOutcome.replace("_", " ");
|
|
120
|
+
const lines = [
|
|
121
|
+
`## MartinLoop Verified Handoff — ${outcome}`,
|
|
122
|
+
"",
|
|
123
|
+
"| Field | Value |",
|
|
124
|
+
"|-------|-------|",
|
|
125
|
+
`| Task | ${handoff.task.objective} |`,
|
|
126
|
+
`| Run | ${handoff.loopId} |`,
|
|
127
|
+
`| Execution Mode | ${executionMode.replaceAll("_", " ")} |`,
|
|
128
|
+
`| Governed Claim | ${governanceClaimEligible ? "ELIGIBLE" : "INELIGIBLE"} |`,
|
|
129
|
+
`| Verification | ${handoff.verification.status.replace("_", " ")} |`,
|
|
130
|
+
`| Scope | ${handoff.scope.status.replaceAll("_", " ")} |`,
|
|
131
|
+
`| Test Integrity | ${handoff.testIntegrity.verdict} |`,
|
|
132
|
+
`| Receipt Integrity | ${handoff.receiptIntegrity.state} |`,
|
|
133
|
+
`| Attempts | ${handoff.usage.attempts} |`,
|
|
134
|
+
`| Cost | ${formatCost(handoff.usage.actualUsd, handoff.usage.costProvenance)} |`,
|
|
135
|
+
];
|
|
136
|
+
if (handoff.verification.checks.length > 0) {
|
|
137
|
+
lines.push("", "### Verification Evidence", "", "| Check | Status |", "|-------|--------|", ...handoff.verification.checks.map((check) => `| ${check.command} | ${checkSymbol(check.status)} ${check.status.replace("_", " ")} |`));
|
|
138
|
+
}
|
|
139
|
+
if (handoff.stopReason) {
|
|
140
|
+
lines.push("", "### Stop Reason", "", handoff.stopReason.replaceAll("_", " ").toLowerCase());
|
|
141
|
+
}
|
|
142
|
+
lines.push("", "### Unresolved");
|
|
143
|
+
if (handoff.unresolvedWork.length === 0) {
|
|
144
|
+
lines.push("", "None recorded.");
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
lines.push("", ...handoff.unresolvedWork.map((item) => `- ${item}`));
|
|
148
|
+
}
|
|
149
|
+
lines.push("", "### Recovery", "", handoff.recovery.summary, "", "### Next", "", handoff.nextAction);
|
|
150
|
+
return lines.join("\n");
|
|
151
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@martinloop/mcp",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.5",
|
|
4
4
|
"mcpName": "io.github.Keesan12/martin-loop",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -75,6 +75,7 @@
|
|
|
75
75
|
},
|
|
76
76
|
"devDependencies": {
|
|
77
77
|
"@anthropic-ai/mcpb": "2.1.2",
|
|
78
|
-
"@martin/contracts": "workspace:*"
|
|
78
|
+
"@martin/contracts": "workspace:*",
|
|
79
|
+
"@martin/presentation": "workspace:*"
|
|
79
80
|
}
|
|
80
81
|
}
|
package/server.json
CHANGED
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
"url": "https://github.com/Keesan12/martin-loop",
|
|
8
8
|
"source": "github"
|
|
9
9
|
},
|
|
10
|
-
"version": "0.5.
|
|
10
|
+
"version": "0.5.5",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "npm",
|
|
14
14
|
"identifier": "@martinloop/mcp",
|
|
15
|
-
"version": "0.5.
|
|
15
|
+
"version": "0.5.5",
|
|
16
16
|
"transport": {
|
|
17
17
|
"type": "stdio"
|
|
18
18
|
}
|