sketchy remove

This commit is contained in:
Shawn Morreau
2025-11-20 14:04:47 -05:00
parent 96055edda7
commit ed39bda62a
6 changed files with 90 additions and 14 deletions
+1 -10
View File
@@ -21,24 +21,18 @@ export const cursor = agent({
configureCursorMcpServers({ mcpServers, cliPath });
try {
// Run cursor-agent in non-interactive mode with the prompt
// Using -p flag for prompt, --output-format text for plain text output
// and --approve-mcps to automatically approve all MCP servers
const fullPrompt = addInstructions(payload);
// Find temp directory from cliPath to set HOME for MCP config lookup
const tempDir = cliPath.split("/.local/bin/")[0];
log.info("Running Cursor CLI...");
// Use spawn to handle streaming output
// Use --print flag explicitly for non-interactive mode
return new Promise((resolve) => {
const child = spawn(
cliPath,
["--print", fullPrompt, "--output-format", "text", "--approve-mcps", "--force"],
{
cwd: process.cwd(), // Run in current working directory
cwd: process.cwd(),
env: {
...process.env,
CURSOR_API_KEY: apiKey,
@@ -52,7 +46,6 @@ export const cursor = agent({
let stdout = "";
let stderr = "";
// Log when process starts
child.on("spawn", () => {
log.debug("Cursor CLI process spawned");
});
@@ -67,12 +60,10 @@ export const cursor = agent({
child.stderr?.on("data", (data) => {
const text = data.toString();
stderr += text;
// Log errors as they come - but also write to stdout so we can see it
process.stderr.write(text);
log.warning(text);
});
// Handle process exit
child.on("close", (code, signal) => {
if (signal) {
log.warning(`Cursor CLI terminated by signal: ${signal}`);
+2 -2
View File
@@ -32,10 +32,11 @@ export const gemini = agent({
try {
const result = await spawn({
cmd: "node",
args: [cliPath, "--yolo", "--output-format", "text", "-p", sessionPrompt],
args: [cliPath, "--yolo", "--output-format=text", "-p", sessionPrompt],
env: {
GEMINI_API_KEY: apiKey,
GITHUB_INSTALLATION_TOKEN: githubInstallationToken,
GEMINI_CLI_DISABLE_SCHEMA_VALIDATION: "1",
},
onStdout: (chunk) => {
const trimmed = chunk.trim();
@@ -94,7 +95,6 @@ function configureGeminiMcpServers({ mcpServers, cliPath }: ConfigureMcpServersP
const addArgs = ["mcp", "add", serverName, command, ...args];
// Add environment variables as --env flags
for (const [key, value] of Object.entries(envVars)) {
addArgs.push("--env", `${key}=${value}`);
}
+2
View File
@@ -1,5 +1,7 @@
import { spawnSync } from "node:child_process";
import { chmodSync, createWriteStream, existsSync } from "node:fs";
import { mkdtemp } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { pipeline } from "node:stream/promises";
import type { McpStdioServerConfig } from "@anthropic-ai/claude-agent-sdk";
+1 -1
View File
@@ -1 +1 @@
create a comment on https://github.com/pullfrogai/scratch/pull/29 that says Things just got out of hand
comment on https://github.com/pullfrogai/scratch/pull/29 that writes a really funny joke about Arktype
+56
View File
@@ -36,4 +36,60 @@ addTools(server, [
GetCheckSuiteLogsTool,
]);
// intercept stdout to remove $schema fields from JSON-RPC messages
// FastMCP uses stdout for JSON-RPC communication, so we intercept here
const originalStdoutWrite = process.stdout.write.bind(process.stdout);
process.stdout.write = function (chunk: any, encoding?: any, cb?: any): boolean {
if (typeof chunk === "string" || Buffer.isBuffer(chunk)) {
try {
const text = typeof chunk === "string" ? chunk : chunk.toString("utf-8");
// MCP uses JSON-RPC over stdio, messages are JSON objects on single lines
const lines = text.split("\n");
const modifiedLines = lines.map((line) => {
const trimmed = line.trim();
// only process lines that look like JSON-RPC messages
if (trimmed.startsWith("{") && trimmed.includes('"jsonrpc"')) {
try {
const parsed = JSON.parse(trimmed);
// recursively remove $schema fields from the message
const cleaned = removeSchemaFields(parsed);
return JSON.stringify(cleaned) + "\n";
} catch {
// if parsing fails, return original line
return line + (line.endsWith("\n") ? "" : "\n");
}
}
return line + (line.endsWith("\n") ? "" : "\n");
});
const result = modifiedLines.join("");
return originalStdoutWrite(result, encoding, cb);
} catch {
// if anything fails, just pass through
}
}
return originalStdoutWrite(chunk, encoding, cb);
};
// recursively remove $schema fields from JSON objects
function removeSchemaFields(obj: any): any {
if (obj === null || typeof obj !== "object") {
return obj;
}
if (Array.isArray(obj)) {
return obj.map(removeSchemaFields);
}
const result: any = {};
for (const [key, value] of Object.entries(obj)) {
// skip $schema fields
if (key === "$schema") {
continue;
}
result[key] = removeSchemaFields(value);
}
return result;
}
server.start();
+28 -1
View File
@@ -65,9 +65,36 @@ export const tool = <const params>(toolDef: Tool<any, StandardSchemaV1<params>>)
return toolDef;
};
// recursively remove $schema fields from JSON Schema objects
function removeSchemaFields(obj: any): any {
if (obj === null || typeof obj !== "object") {
return obj;
}
if (Array.isArray(obj)) {
return obj.map(removeSchemaFields);
}
const result: any = {};
for (const [key, value] of Object.entries(obj)) {
// skip $schema fields
if (key === "$schema") {
continue;
}
result[key] = removeSchemaFields(value);
}
return result;
}
export const addTools = (server: FastMCP, tools: Tool<any, any>[]) => {
for (const tool of tools) {
server.addTool(tool);
// clone tool and remove $schema from parameters schema
const cleanedTool = {
...tool,
parameters: tool.parameters ? removeSchemaFields(tool.parameters) : undefined,
};
server.addTool(cleanedTool);
}
return server;
};