UNPKG

pyb-ts

Version:

PYB-CLI - Minimal AI Agent with multi-model support and CLI interface

148 lines (145 loc) 7.48 kB
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