feat: add /bug report command (#312)

Add `/bug` command for chat session
This commit is contained in:
Fouad Matin
2025-04-18 14:09:35 -07:00
committed by GitHub
parent 4acd7d8617
commit 8e2e77fafb
5 changed files with 152 additions and 0 deletions

View File

@@ -26,6 +26,7 @@ body:
label: Which model were you using? label: Which model were you using?
description: Like `gpt-4.1`, `o4-mini`, `o3`, etc. description: Like `gpt-4.1`, `o4-mini`, `o3`, etc.
- type: input - type: input
id: platform
attributes: attributes:
label: What platform is your computer? label: What platform is your computer?
description: | description: |

View File

@@ -45,6 +45,7 @@ export default function TerminalChatInput({
onCompact, onCompact,
interruptAgent, interruptAgent,
active, active,
items = [],
}: { }: {
isNew: boolean; isNew: boolean;
loading: boolean; loading: boolean;
@@ -65,6 +66,8 @@ export default function TerminalChatInput({
onCompact: () => void; onCompact: () => void;
interruptAgent: () => void; interruptAgent: () => void;
active: boolean; active: boolean;
// New: current conversation items so we can include them in bug reports
items?: Array<ResponseItem>;
}): React.ReactElement { }): React.ReactElement {
const app = useApp(); const app = useApp();
const [selectedSuggestion, setSelectedSuggestion] = useState<number>(0); const [selectedSuggestion, setSelectedSuggestion] = useState<number>(0);
@@ -239,6 +242,68 @@ export default function TerminalChatInput({
}, },
); );
return;
} else if (inputValue === "/bug") {
// Generate a GitHub bug report URL prefilled with session details
setInput("");
try {
// Dynamically import dependencies to avoid unnecessary bundle size
const [{ default: open }, os] = await Promise.all([
import("open"),
import("node:os"),
]);
// Lazy import CLI_VERSION to avoid circular deps
const { CLI_VERSION } = await import("../../utils/session.js");
const { buildBugReportUrl } = await import(
"../../utils/bug-report.js"
);
const url = buildBugReportUrl({
items: items ?? [],
cliVersion: CLI_VERSION,
model: loadConfig().model ?? "unknown",
platform: `${os.platform()} ${os.arch()} ${os.release()}`,
});
// Open the URL in the user's default browser
await open(url, { wait: false });
// Inform the user in the chat history
setItems((prev) => [
...prev,
{
id: `bugreport-${Date.now()}`,
type: "message",
role: "system",
content: [
{
type: "input_text",
text: "📋 Opened browser to file a bug report. Please include any context that might help us fix the issue!",
},
],
},
]);
} catch (error) {
// If anything went wrong, notify the user
setItems((prev) => [
...prev,
{
id: `bugreport-error-${Date.now()}`,
type: "message",
role: "system",
content: [
{
type: "input_text",
text: `⚠️ Failed to create bug report URL: ${error}`,
},
],
},
]);
}
return; return;
} else if (inputValue.startsWith("/")) { } else if (inputValue.startsWith("/")) {
// Handle invalid/unrecognized commands. // Handle invalid/unrecognized commands.
@@ -330,6 +395,7 @@ export default function TerminalChatInput({
openHelpOverlay, openHelpOverlay,
history, // Add history to the dependency array history, // Add history to the dependency array
onCompact, onCompact,
items,
], ],
); );

View File

@@ -516,6 +516,7 @@ export default function TerminalChat({
agent.run(inputs, lastResponseId || ""); agent.run(inputs, lastResponseId || "");
return {}; return {};
}} }}
items={items}
/> />
)} )}
{overlayMode === "history" && ( {overlayMode === "history" && (

View File

@@ -52,6 +52,9 @@ export default function HelpOverlay({
<Text> <Text>
<Text color="cyan">/clearhistory</Text> clear command history <Text color="cyan">/clearhistory</Text> clear command history
</Text> </Text>
<Text>
<Text color="cyan">/bug</Text> file a bug report with session log
</Text>
<Text> <Text>
<Text color="cyan">/compact</Text> condense context into a summary <Text color="cyan">/compact</Text> condense context into a summary
</Text> </Text>

View File

@@ -0,0 +1,81 @@
import type { ResponseItem } from "openai/resources/responses/responses.mjs";
/**
* Build a GitHub issuesnew URL that prefills the Codex 2bugreport.yml
* template with whatever structured data we can infer from the current
* session.
*/
export function buildBugReportUrl({
items,
cliVersion,
model,
platform,
}: {
/** Chat history so we can summarise user steps */
items: Array<ResponseItem>;
/** CLI revision string (e.g. output of `codex --revision`) */
cliVersion: string;
/** Active model name */
model: string;
/** Platform string e.g. `darwin arm64 23.0.0` */
platform: string;
}): string {
const params = new URLSearchParams({
template: "2-bug-report.yml",
labels: "bug",
});
// Template ids -------------------------------------------------------------
params.set("version", cliVersion);
params.set("model", model);
// The platform input has no explicit `id`, so GitHub falls back to a slug of
// the label text. For “What platform is your computer?” that slug is:
// what-platform-is-your-computer
params.set("what-platform-is-your-computer", platform);
// Build the steps bullet list ---------------------------------------------
const bullets: Array<string> = [];
for (let i = 0; i < items.length; ) {
const entry = items[i];
if (entry?.type === "message" && entry.role === "user") {
const contentArray = entry.content as
| Array<{ text?: string }>
| undefined;
const messageText = contentArray
?.map((c) => c.text ?? "")
.join(" ")
.trim();
let reasoning = 0;
let toolCalls = 0;
let j = i + 1;
while (
j < items.length &&
!(entry?.type === "message" && entry.role === "user")
) {
const it = items[j];
if (it?.type === "message" && it?.role === "assistant") {
reasoning += 1;
} else if (it?.type === "function_call") {
toolCalls += 1;
}
j++;
}
bullets.push(
`- "${messageText}"\n - \`${reasoning} reasoning steps\` | \`${toolCalls} tool calls\``,
);
i = j;
} else {
i += 1;
}
}
if (bullets.length) {
params.set("steps", bullets.join("\n"));
}
return `https://github.com/openai/codex/issues/new?${params.toString()}`;
}