Add debug log panel, centered modals, and fix multiple UX issues

Features:
  - Add /logs command to toggle debug log panel (20% width on right)
  - Debug panel shows API calls, streaming events, tool calls, state changes
  - Add /help submenus with detailed command explanations
  - Center all modal dialogs in the terminal window

  Bug Fixes:
  - Fix streaming content not displaying (add fallback when streaming fails)
  - Fix permission modal shortcut key mismatch ('a' → 'l' for local scope)
  - Fix agent prompt accumulation when switching agents multiple times
  - Fix permission modal using brittle index for "No" option

  Improvements:
  - Restrict git commands (add, commit, push, etc.) unless user explicitly requests
  - Unify permission options across all UI components
  - Add Ollama model selection when switching to Ollama provider
  - Store base system prompt to prevent agent prompt stacking

  New files:
  - src/tui-solid/components/debug-log-panel.tsx
  - src/tui-solid/components/centered-modal.tsx
  - src/tui-solid/components/help-menu.tsx
  - src/tui-solid/components/help-detail.tsx
  - src/constants/help-content.ts
This commit is contained in:
2026-01-29 04:53:39 -05:00
parent dfbbab2ecb
commit adfebda501
27 changed files with 1031 additions and 167 deletions

View File

@@ -1,6 +1,6 @@
import { saveSession } from "@services/session";
import { showHelp } from "@commands/components/chat/commands/show-help";
import { clearConversation } from "@commands/components/chat/history/clear-conversation";
import { appStore } from "@tui/index";
import { showContextFiles } from "@commands/components/chat/context/show-context-files";
import { removeFile } from "@commands/components/chat/context/remove-file";
import { showContext } from "@commands/components/chat/history/show-context";
@@ -24,8 +24,8 @@ const COMMAND_REGISTRY: Map<string, CommandHandler> = new Map<
string,
CommandHandler
>([
["help", () => showHelp()],
["h", () => showHelp()],
["help", () => appStore.setMode("help_menu")],
["h", () => appStore.setMode("help_menu")],
["clear", (ctx: CommandContext) => clearConversation(ctx.state)],
["c", (ctx: CommandContext) => clearConversation(ctx.state)],
["files", (ctx: CommandContext) => showContextFiles(ctx.state.contextFiles)],
@@ -106,6 +106,17 @@ const COMMAND_REGISTRY: Map<string, CommandHandler> = new Map<
},
],
["mcp", async (ctx: CommandContext) => handleMCP(ctx.args)],
[
"logs",
() => {
appStore.toggleDebugLog();
const { debugLogVisible } = appStore.getState();
appStore.addLog({
type: "system",
content: `Debug logs panel ${debugLogVisible ? "enabled" : "disabled"}`,
});
},
],
]);
export default COMMAND_REGISTRY;

View File

@@ -26,6 +26,7 @@ import { agentLoader } from "@services/agent-loader";
interface ExecuteContext {
state: ChatServiceState | null;
baseSystemPrompt: string | null;
}
const createHandleExit = (): (() => void) => (): void => {
@@ -51,16 +52,22 @@ const createHandleAgentSelect =
(ctx.state as ChatServiceState & { currentAgent?: string }).currentAgent =
agentId;
if (agent.prompt) {
const basePrompt = ctx.state.systemPrompt;
ctx.state.systemPrompt = `${agent.prompt}\n\n${basePrompt}`;
// Use the stored base prompt to avoid accumulation when switching agents
const basePrompt = ctx.baseSystemPrompt ?? ctx.state.systemPrompt;
if (
ctx.state.messages.length > 0 &&
ctx.state.messages[0].role === "system"
) {
ctx.state.messages[0].content = ctx.state.systemPrompt;
}
if (agent.prompt) {
ctx.state.systemPrompt = `${agent.prompt}\n\n${basePrompt}`;
} else {
// Reset to base prompt if agent has no custom prompt
ctx.state.systemPrompt = basePrompt;
}
// Update the system message in the conversation
if (
ctx.state.messages.length > 0 &&
ctx.state.messages[0].role === "system"
) {
ctx.state.messages[0].content = ctx.state.systemPrompt;
}
};
@@ -81,6 +88,15 @@ const createHandleProviderSelect =
const config = await getConfig();
config.set("provider", providerId as "copilot" | "ollama");
await config.save();
// Load models for the new provider and update the store
const models = await loadModels(providerId as "copilot" | "ollama");
appStore.setAvailableModels(models);
// If Ollama is selected and has models, open model selector
if (providerId === "ollama" && models.length > 0) {
appStore.setMode("model_select");
}
};
const createHandleCascadeToggle =
@@ -131,10 +147,13 @@ const createHandleSubmit =
const execute = async (options: ChatTUIOptions): Promise<void> => {
const ctx: ExecuteContext = {
state: null,
baseSystemPrompt: null,
};
const { state, session } = await initializeChatService(options);
ctx.state = state;
// Store the original system prompt before any agent modifications
ctx.baseSystemPrompt = state.systemPrompt;
if (options.printMode && options.initialPrompt) {
await executePrintMode(state, options.initialPrompt);