fix(coding-agent): corrected context file preview to read from raw object

- Fixed context file preview in inspector panel to read content from raw object instead of async file reads.
- Added slash commands to extensions inspector panel for visibility and management.
- Extracted getContextFileContent helper method for type-safe content extraction.
This commit is contained in:
can1357
2026-01-06 21:55:24 +00:00
parent f224ecb72a
commit 386c4b2c8d
3 changed files with 38 additions and 20 deletions
+3
View File
@@ -1,8 +1,10 @@
# Changelog
## [Unreleased]
### Added
- Added slash commands to the extensions inspector panel for visibility and management
- Added support for file-based slash commands from `commands/` directories
- Added `$ARGUMENTS` placeholder for slash command argument substitution, aligning with Claude and Codex conventions
@@ -12,6 +14,7 @@
### Fixed
- Fixed context file preview in inspector panel to display content correctly instead of attempting async file reads
- Fixed Linux ARM64 installs failing on fresh Debian when the `sharp` module is unavailable during session image compression
## [3.20.1] - 2026-01-06
@@ -68,7 +68,7 @@ export class InspectorPanel implements Component {
switch (ext.kind) {
case "context-file":
content = this.renderFilePreview(ext.path, width);
content = this.renderFilePreview(ext.raw, width);
break;
case "tool":
content = this.renderToolArgs(ext.raw, width);
@@ -91,37 +91,40 @@ export class InspectorPanel implements Component {
return lines;
}
private renderFilePreview(path: string, width: number): string[] {
private renderFilePreview(raw: unknown, width: number): string[] {
const lines: string[] = [];
lines.push(theme.fg("muted", "Preview:"));
lines.push(theme.fg("dim", theme.boxSharp.horizontal.repeat(Math.min(width - 2, 40))));
try {
const content = Bun.file(path).text();
// Note: async call to sync context - will show empty on first render
// This is acceptable for preview which can populate on next render
if (typeof content === "object" && "then" in content) {
content.then((text: string) => {
const fileLines = text.split("\n").slice(0, 20);
const content = this.getContextFileContent(raw);
if (!content) {
lines.push(theme.fg("dim", " (no content)"));
lines.push("");
return lines;
}
for (const line of fileLines) {
const highlighted = this.highlightMarkdown(line);
lines.push(truncateToWidth(highlighted, width - 2));
}
const fileLines = content.split("\n");
for (const line of fileLines.slice(0, 20)) {
const highlighted = this.highlightMarkdown(line);
lines.push(truncateToWidth(highlighted, width - 2));
}
if (text.split("\n").length > 20) {
lines.push(theme.fg("dim", "(truncated at line 20)"));
}
});
}
} catch (err) {
lines.push(theme.fg("error", `Failed to read file: ${err instanceof Error ? err.message : String(err)}`));
if (fileLines.length > 20) {
lines.push(theme.fg("dim", "(truncated at line 20)"));
}
lines.push("");
return lines;
}
private getContextFileContent(raw: unknown): string | null {
if (raw && typeof raw === "object" && "content" in raw) {
const content = (raw as { content?: unknown }).content;
return typeof content === "string" ? content : null;
}
return null;
}
private highlightMarkdown(line: string): string {
// Basic markdown syntax highlighting
let highlighted = line;
@@ -10,6 +10,7 @@ import type { MCPServer } from "../../../../capability/mcp";
import type { Prompt } from "../../../../capability/prompt";
import type { Rule } from "../../../../capability/rule";
import type { Skill } from "../../../../capability/skill";
import type { SlashCommand } from "../../../../capability/slash-command";
import type { CustomTool } from "../../../../capability/tool";
import type { SourceMeta } from "../../../../capability/types";
import {
@@ -192,6 +193,17 @@ export function loadAllExtensions(cwd?: string, disabledIds?: string[]): Extensi
// Capability may not be registered
}
// Load slash commands
try {
const commands = loadSync<SlashCommand>("slash-commands", loadOpts);
addItems(commands.all, "slash-command", {
getDescription: () => undefined,
getTrigger: (c) => `/${c.name}`,
});
} catch {
// Capability may not be registered
}
// Load hooks
try {
const hooks = loadSync<Hook>("hooks", loadOpts);