77 lines
2.4 KiB
TypeScript
77 lines
2.4 KiB
TypeScript
import path from "node:path";
|
|
|
|
import {
|
|
CLAUDE_BIN,
|
|
CLAUDE_IDENTITY_PREFIX,
|
|
type ClaudeJsonParseResult,
|
|
parseClaudeJson,
|
|
summarizeClaudeMetadata,
|
|
} from "../auto-reply/claude.js";
|
|
import type { AgentMeta, AgentSpec } from "./types.js";
|
|
|
|
function toMeta(parsed?: ClaudeJsonParseResult): AgentMeta | undefined {
|
|
if (!parsed?.parsed) return undefined;
|
|
const summary = summarizeClaudeMetadata(parsed.parsed);
|
|
const sessionId =
|
|
parsed.parsed &&
|
|
typeof parsed.parsed === "object" &&
|
|
typeof (parsed.parsed as { session_id?: unknown }).session_id === "string"
|
|
? (parsed.parsed as { session_id: string }).session_id
|
|
: undefined;
|
|
const meta: AgentMeta = {};
|
|
if (sessionId) meta.sessionId = sessionId;
|
|
if (summary) meta.extra = { summary };
|
|
return Object.keys(meta).length ? meta : undefined;
|
|
}
|
|
|
|
export const claudeSpec: AgentSpec = {
|
|
kind: "claude",
|
|
isInvocation: (argv) =>
|
|
argv.length > 0 && path.basename(argv[0]) === CLAUDE_BIN,
|
|
buildArgs: (ctx) => {
|
|
// Split around the body so we can inject flags without losing the body
|
|
// position. This keeps templated prompts intact even when we add flags.
|
|
const argv = [...ctx.argv];
|
|
const body = argv[ctx.bodyIndex] ?? "";
|
|
const beforeBody = argv.slice(0, ctx.bodyIndex);
|
|
const afterBody = argv.slice(ctx.bodyIndex + 1);
|
|
|
|
const wantsOutputFormat = typeof ctx.format === "string";
|
|
if (wantsOutputFormat) {
|
|
const hasOutputFormat = argv.some(
|
|
(part) =>
|
|
part === "--output-format" || part.startsWith("--output-format="),
|
|
);
|
|
if (!hasOutputFormat) {
|
|
const outputFormat = ctx.format ?? "json";
|
|
beforeBody.push("--output-format", outputFormat);
|
|
}
|
|
}
|
|
|
|
const hasPrintFlag = argv.some(
|
|
(part) => part === "-p" || part === "--print",
|
|
);
|
|
if (!hasPrintFlag) {
|
|
beforeBody.push("-p");
|
|
}
|
|
|
|
const shouldPrependIdentity = !(ctx.sendSystemOnce && ctx.systemSent);
|
|
const bodyWithIdentity =
|
|
shouldPrependIdentity && body
|
|
? [ctx.identityPrefix ?? CLAUDE_IDENTITY_PREFIX, body]
|
|
.filter(Boolean)
|
|
.join("\n\n")
|
|
: body;
|
|
|
|
return [...beforeBody, bodyWithIdentity, ...afterBody];
|
|
},
|
|
parseOutput: (rawStdout) => {
|
|
const parsed = parseClaudeJson(rawStdout);
|
|
const text = parsed?.text ?? rawStdout.trim();
|
|
return {
|
|
texts: text ? [text.trim()] : undefined,
|
|
meta: toMeta(parsed),
|
|
};
|
|
},
|
|
};
|