pyb-ts
Version:
PYB-CLI - Minimal AI Agent with multi-model support and CLI interface
148 lines (145 loc) • 7.48 kB
JavaScript
import { Box, Text, useInput } from "ink";
import * as React from "react";
import { useState, useCallback, useEffect } from "react";
import { getTheme } from "@utils/theme";
import { getMessagesGetter } from "@messages";
import TextInput from "./TextInput.js";
import { env } from "@utils/env";
import { getGitState, getIsGit } from "@utils/git";
import { useTerminalSize } from "@hooks/useTerminalSize";
import { getGlobalConfig } from "@utils/config";
import { API_ERROR_MESSAGE_PREFIX, queryQuick } from "@services/claude";
import { openBrowser } from "@utils/browser";
import { useExitOnCtrlCD } from "@hooks/useExitOnCtrlCD";
import { MACRO } from "@constants/macros";
import { GITHUB_ISSUES_REPO_URL } from "@constants/product";
function Bug({ onDone }) {
const [step, setStep] = useState("userInput");
const [cursorOffset, setCursorOffset] = useState(0);
const [description, setDescription] = useState("");
const [feedbackId, setFeedbackId] = useState(null);
const [error, setError] = useState(null);
const [envInfo, setEnvInfo] = useState({ isGit: false, gitState: null });
const [title, setTitle] = useState(null);
const textInputColumns = useTerminalSize().columns - 4;
const messages = getMessagesGetter()();
useEffect(() => {
async function loadEnvInfo() {
const isGit = await getIsGit();
let gitState = null;
if (isGit) {
gitState = await getGitState();
}
setEnvInfo({ isGit, gitState });
}
void loadEnvInfo();
}, []);
const exitState = useExitOnCtrlCD(() => process.exit(0));
const submitReport = useCallback(async () => {
setStep("done");
}, [description, envInfo.isGit, messages]);
useInput((input, key) => {
if (error) {
onDone("<bash-stderr>Error submitting bug report</bash-stderr>");
return;
}
if (key.escape) {
onDone("<bash-stderr>Bug report cancelled</bash-stderr>");
return;
}
if (step === "consent" && (key.return || input === " ")) {
const issueUrl = createGitHubIssueUrl(
feedbackId,
description.slice(0, 80),
description
);
void openBrowser(issueUrl);
onDone("<bash-stdout>Bug report submitted</bash-stdout>");
}
});
const theme = getTheme();
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
Box,
{
flexDirection: "column",
borderStyle: "round",
borderColor: theme.permission,
paddingX: 1,
paddingBottom: 1,
gap: 1
},
/* @__PURE__ */ React.createElement(Text, { bold: true, color: theme.permission }, "Submit Bug Report"),
step === "userInput" && /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React.createElement(Text, null, "Describe the issue below and copy/paste any errors you see:"), /* @__PURE__ */ React.createElement(
TextInput,
{
value: description,
onChange: setDescription,
columns: textInputColumns,
onSubmit: () => setStep("consent"),
onExitMessage: () => onDone("<bash-stderr>Bug report cancelled</bash-stderr>"),
cursorOffset,
onChangeCursorOffset: setCursorOffset
}
), error && /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React.createElement(Text, { color: "red" }, error), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, "Press any key to close"))),
step === "consent" && /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, /* @__PURE__ */ React.createElement(Text, null, "This report will include:"), /* @__PURE__ */ React.createElement(Box, { marginLeft: 2, flexDirection: "column" }, /* @__PURE__ */ React.createElement(Text, null, "- Your bug description: ", /* @__PURE__ */ React.createElement(Text, { dimColor: true }, description)), /* @__PURE__ */ React.createElement(Text, null, "- Environment info:", " ", /* @__PURE__ */ React.createElement(Text, { dimColor: true }, env.platform, ", ", env.terminal, ", v", MACRO.VERSION)), /* @__PURE__ */ React.createElement(Text, null, "- Model settings (no api keys)"))),
step === "submitting" && /* @__PURE__ */ React.createElement(Box, { flexDirection: "row", gap: 1 }, /* @__PURE__ */ React.createElement(Text, null, "Submitting report\u2026")),
step === "done" && /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, /* @__PURE__ */ React.createElement(Text, { color: getTheme().success }, "Thank you for your report!"), feedbackId && /* @__PURE__ */ React.createElement(Text, { dimColor: true }, "Feedback ID: ", feedbackId), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, null, "Press "), /* @__PURE__ */ React.createElement(Text, { bold: true }, "Enter "), /* @__PURE__ */ React.createElement(Text, null, "to also create a GitHub issue, or any other key to close.")))
), /* @__PURE__ */ React.createElement(Box, { marginLeft: 3 }, /* @__PURE__ */ React.createElement(Text, { dimColor: true }, exitState.pending ? /* @__PURE__ */ React.createElement(React.Fragment, null, "Press ", exitState.keyName, " again to exit") : step === "userInput" ? /* @__PURE__ */ React.createElement(React.Fragment, null, "Enter to continue \xB7 Esc to cancel") : step === "consent" ? /* @__PURE__ */ React.createElement(React.Fragment, null, "Enter to open browser to create GitHub issue \xB7 Esc to cancel") : null)));
}
function createGitHubIssueUrl(feedbackId, title, description) {
const globalConfig = getGlobalConfig();
const modelProfiles = globalConfig.modelProfiles || [];
const activeProfiles = modelProfiles.filter((p) => p.isActive);
let modelInfo = "## Models\n";
if (activeProfiles.length === 0) {
modelInfo += "- No model profiles configured\n";
} else {
activeProfiles.forEach((profile) => {
modelInfo += `- ${profile.name}
`;
modelInfo += ` - provider: ${profile.provider}
`;
modelInfo += ` - model: ${profile.modelName}
`;
modelInfo += ` - baseURL: ${profile.baseURL}
`;
modelInfo += ` - maxTokens: ${profile.maxTokens}
`;
modelInfo += ` - contextLength: ${profile.contextLength}
`;
if (profile.reasoningEffort) {
modelInfo += ` - reasoning effort: ${profile.reasoningEffort}
`;
}
});
}
const body = encodeURIComponent(`
## Bug Description
${description}
## Environment Info
- Platform: ${env.platform}
- Terminal: ${env.terminal}
- Version: ${MACRO.VERSION || "unknown"}
${modelInfo}`);
return `${GITHUB_ISSUES_REPO_URL}/new?title=${encodeURIComponent(title)}&body=${body}&labels=user-reported,bug`;
}
async function generateTitle(description) {
const response = await queryQuick({
systemPrompt: [
'Generate a concise issue title (max 80 chars) that captures the key point of this feedback. Do not include quotes or prefixes like "Feedback:" or "Issue:". If you cannot generate a title, just use "User Feedback".'
],
userPrompt: description
});
const title = response.message.content[0]?.type === "text" ? response.message.content[0].text : "Bug Report";
if (title.startsWith(API_ERROR_MESSAGE_PREFIX)) {
return `Bug Report: ${description.slice(0, 60)}${description.length > 60 ? "..." : ""}`;
}
return title;
}
async function submitFeedback(data) {
return { success: true, feedbackId: "123" };
}
export {
Bug
};
//# sourceMappingURL=Bug.js.map