mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-15 08:03:39 +00:00
fix(coding-agent): respect quietStartup on /reload while keeping diagnostics (fixes #1336)
This commit is contained in:
parent
e9f94ba6c3
commit
fe6f4d3a9d
2 changed files with 197 additions and 96 deletions
|
|
@ -858,119 +858,137 @@ export class InteractiveMode {
|
|||
return lines.join("\n");
|
||||
}
|
||||
|
||||
private showLoadedResources(options?: { extensionPaths?: string[]; force?: boolean }): void {
|
||||
const shouldShow = options?.force || this.options.verbose || !this.settingsManager.getQuietStartup();
|
||||
if (!shouldShow) {
|
||||
private showLoadedResources(options?: {
|
||||
extensionPaths?: string[];
|
||||
force?: boolean;
|
||||
showDiagnosticsWhenQuiet?: boolean;
|
||||
}): void {
|
||||
const showListing = options?.force || this.options.verbose || !this.settingsManager.getQuietStartup();
|
||||
const showDiagnostics = showListing || options?.showDiagnosticsWhenQuiet === true;
|
||||
if (!showListing && !showDiagnostics) {
|
||||
return;
|
||||
}
|
||||
|
||||
const metadata = this.session.resourceLoader.getPathMetadata();
|
||||
|
||||
const sectionHeader = (name: string, color: ThemeColor = "mdHeading") => theme.fg(color, `[${name}]`);
|
||||
|
||||
const contextFiles = this.session.resourceLoader.getAgentsFiles().agentsFiles;
|
||||
if (contextFiles.length > 0) {
|
||||
this.chatContainer.addChild(new Spacer(1));
|
||||
const contextList = contextFiles.map((f) => theme.fg("dim", ` ${this.formatDisplayPath(f.path)}`)).join("\n");
|
||||
this.chatContainer.addChild(new Text(`${sectionHeader("Context")}\n${contextList}`, 0, 0));
|
||||
this.chatContainer.addChild(new Spacer(1));
|
||||
}
|
||||
const skillsResult = this.session.resourceLoader.getSkills();
|
||||
const promptsResult = this.session.resourceLoader.getPrompts();
|
||||
const themesResult = this.session.resourceLoader.getThemes();
|
||||
|
||||
const skills = this.session.resourceLoader.getSkills().skills;
|
||||
if (skills.length > 0) {
|
||||
const skillPaths = skills.map((s) => s.filePath);
|
||||
const groups = this.buildScopeGroups(skillPaths, metadata);
|
||||
const skillList = this.formatScopeGroups(groups, {
|
||||
formatPath: (p) => this.formatDisplayPath(p),
|
||||
formatPackagePath: (p, source) => this.getShortPath(p, source),
|
||||
});
|
||||
this.chatContainer.addChild(new Text(`${sectionHeader("Skills")}\n${skillList}`, 0, 0));
|
||||
this.chatContainer.addChild(new Spacer(1));
|
||||
}
|
||||
if (showListing) {
|
||||
const contextFiles = this.session.resourceLoader.getAgentsFiles().agentsFiles;
|
||||
if (contextFiles.length > 0) {
|
||||
this.chatContainer.addChild(new Spacer(1));
|
||||
const contextList = contextFiles
|
||||
.map((f) => theme.fg("dim", ` ${this.formatDisplayPath(f.path)}`))
|
||||
.join("\n");
|
||||
this.chatContainer.addChild(new Text(`${sectionHeader("Context")}\n${contextList}`, 0, 0));
|
||||
this.chatContainer.addChild(new Spacer(1));
|
||||
}
|
||||
|
||||
const skillDiagnostics = this.session.resourceLoader.getSkills().diagnostics;
|
||||
if (skillDiagnostics.length > 0) {
|
||||
const warningLines = this.formatDiagnostics(skillDiagnostics, metadata);
|
||||
this.chatContainer.addChild(new Text(`${theme.fg("warning", "[Skill conflicts]")}\n${warningLines}`, 0, 0));
|
||||
this.chatContainer.addChild(new Spacer(1));
|
||||
}
|
||||
const skills = skillsResult.skills;
|
||||
if (skills.length > 0) {
|
||||
const skillPaths = skills.map((s) => s.filePath);
|
||||
const groups = this.buildScopeGroups(skillPaths, metadata);
|
||||
const skillList = this.formatScopeGroups(groups, {
|
||||
formatPath: (p) => this.formatDisplayPath(p),
|
||||
formatPackagePath: (p, source) => this.getShortPath(p, source),
|
||||
});
|
||||
this.chatContainer.addChild(new Text(`${sectionHeader("Skills")}\n${skillList}`, 0, 0));
|
||||
this.chatContainer.addChild(new Spacer(1));
|
||||
}
|
||||
|
||||
const templates = this.session.promptTemplates;
|
||||
if (templates.length > 0) {
|
||||
const templatePaths = templates.map((t) => t.filePath);
|
||||
const groups = this.buildScopeGroups(templatePaths, metadata);
|
||||
const templateByPath = new Map(templates.map((t) => [t.filePath, t]));
|
||||
const templateList = this.formatScopeGroups(groups, {
|
||||
formatPath: (p) => {
|
||||
const template = templateByPath.get(p);
|
||||
return template ? `/${template.name}` : this.formatDisplayPath(p);
|
||||
},
|
||||
formatPackagePath: (p) => {
|
||||
const template = templateByPath.get(p);
|
||||
return template ? `/${template.name}` : this.formatDisplayPath(p);
|
||||
},
|
||||
});
|
||||
this.chatContainer.addChild(new Text(`${sectionHeader("Prompts")}\n${templateList}`, 0, 0));
|
||||
this.chatContainer.addChild(new Spacer(1));
|
||||
}
|
||||
const templates = this.session.promptTemplates;
|
||||
if (templates.length > 0) {
|
||||
const templatePaths = templates.map((t) => t.filePath);
|
||||
const groups = this.buildScopeGroups(templatePaths, metadata);
|
||||
const templateByPath = new Map(templates.map((t) => [t.filePath, t]));
|
||||
const templateList = this.formatScopeGroups(groups, {
|
||||
formatPath: (p) => {
|
||||
const template = templateByPath.get(p);
|
||||
return template ? `/${template.name}` : this.formatDisplayPath(p);
|
||||
},
|
||||
formatPackagePath: (p) => {
|
||||
const template = templateByPath.get(p);
|
||||
return template ? `/${template.name}` : this.formatDisplayPath(p);
|
||||
},
|
||||
});
|
||||
this.chatContainer.addChild(new Text(`${sectionHeader("Prompts")}\n${templateList}`, 0, 0));
|
||||
this.chatContainer.addChild(new Spacer(1));
|
||||
}
|
||||
|
||||
const promptDiagnostics = this.session.resourceLoader.getPrompts().diagnostics;
|
||||
if (promptDiagnostics.length > 0) {
|
||||
const warningLines = this.formatDiagnostics(promptDiagnostics, metadata);
|
||||
this.chatContainer.addChild(new Text(`${theme.fg("warning", "[Prompt conflicts]")}\n${warningLines}`, 0, 0));
|
||||
this.chatContainer.addChild(new Spacer(1));
|
||||
}
|
||||
const extensionPaths = options?.extensionPaths ?? [];
|
||||
if (extensionPaths.length > 0) {
|
||||
const groups = this.buildScopeGroups(extensionPaths, metadata);
|
||||
const extList = this.formatScopeGroups(groups, {
|
||||
formatPath: (p) => this.formatDisplayPath(p),
|
||||
formatPackagePath: (p, source) => this.getShortPath(p, source),
|
||||
});
|
||||
this.chatContainer.addChild(new Text(`${sectionHeader("Extensions", "mdHeading")}\n${extList}`, 0, 0));
|
||||
this.chatContainer.addChild(new Spacer(1));
|
||||
}
|
||||
|
||||
const extensionPaths = options?.extensionPaths ?? [];
|
||||
if (extensionPaths.length > 0) {
|
||||
const groups = this.buildScopeGroups(extensionPaths, metadata);
|
||||
const extList = this.formatScopeGroups(groups, {
|
||||
formatPath: (p) => this.formatDisplayPath(p),
|
||||
formatPackagePath: (p, source) => this.getShortPath(p, source),
|
||||
});
|
||||
this.chatContainer.addChild(new Text(`${sectionHeader("Extensions", "mdHeading")}\n${extList}`, 0, 0));
|
||||
this.chatContainer.addChild(new Spacer(1));
|
||||
}
|
||||
|
||||
const extensionDiagnostics: ResourceDiagnostic[] = [];
|
||||
const extensionErrors = this.session.resourceLoader.getExtensions().errors;
|
||||
if (extensionErrors.length > 0) {
|
||||
for (const error of extensionErrors) {
|
||||
extensionDiagnostics.push({ type: "error", message: error.error, path: error.path });
|
||||
// Show loaded themes (excluding built-in)
|
||||
const loadedThemes = themesResult.themes;
|
||||
const customThemes = loadedThemes.filter((t) => t.sourcePath);
|
||||
if (customThemes.length > 0) {
|
||||
const themePaths = customThemes.map((t) => t.sourcePath!);
|
||||
const groups = this.buildScopeGroups(themePaths, metadata);
|
||||
const themeList = this.formatScopeGroups(groups, {
|
||||
formatPath: (p) => this.formatDisplayPath(p),
|
||||
formatPackagePath: (p, source) => this.getShortPath(p, source),
|
||||
});
|
||||
this.chatContainer.addChild(new Text(`${sectionHeader("Themes")}\n${themeList}`, 0, 0));
|
||||
this.chatContainer.addChild(new Spacer(1));
|
||||
}
|
||||
}
|
||||
|
||||
const commandDiagnostics = this.session.extensionRunner?.getCommandDiagnostics() ?? [];
|
||||
extensionDiagnostics.push(...commandDiagnostics);
|
||||
if (showDiagnostics) {
|
||||
const skillDiagnostics = skillsResult.diagnostics;
|
||||
if (skillDiagnostics.length > 0) {
|
||||
const warningLines = this.formatDiagnostics(skillDiagnostics, metadata);
|
||||
this.chatContainer.addChild(new Text(`${theme.fg("warning", "[Skill conflicts]")}\n${warningLines}`, 0, 0));
|
||||
this.chatContainer.addChild(new Spacer(1));
|
||||
}
|
||||
|
||||
const shortcutDiagnostics = this.session.extensionRunner?.getShortcutDiagnostics() ?? [];
|
||||
extensionDiagnostics.push(...shortcutDiagnostics);
|
||||
const promptDiagnostics = promptsResult.diagnostics;
|
||||
if (promptDiagnostics.length > 0) {
|
||||
const warningLines = this.formatDiagnostics(promptDiagnostics, metadata);
|
||||
this.chatContainer.addChild(
|
||||
new Text(`${theme.fg("warning", "[Prompt conflicts]")}\n${warningLines}`, 0, 0),
|
||||
);
|
||||
this.chatContainer.addChild(new Spacer(1));
|
||||
}
|
||||
|
||||
if (extensionDiagnostics.length > 0) {
|
||||
const warningLines = this.formatDiagnostics(extensionDiagnostics, metadata);
|
||||
this.chatContainer.addChild(new Text(`${theme.fg("warning", "[Extension issues]")}\n${warningLines}`, 0, 0));
|
||||
this.chatContainer.addChild(new Spacer(1));
|
||||
}
|
||||
const extensionDiagnostics: ResourceDiagnostic[] = [];
|
||||
const extensionErrors = this.session.resourceLoader.getExtensions().errors;
|
||||
if (extensionErrors.length > 0) {
|
||||
for (const error of extensionErrors) {
|
||||
extensionDiagnostics.push({ type: "error", message: error.error, path: error.path });
|
||||
}
|
||||
}
|
||||
|
||||
// Show loaded themes (excluding built-in)
|
||||
const loadedThemes = this.session.resourceLoader.getThemes().themes;
|
||||
const customThemes = loadedThemes.filter((t) => t.sourcePath);
|
||||
if (customThemes.length > 0) {
|
||||
const themePaths = customThemes.map((t) => t.sourcePath!);
|
||||
const groups = this.buildScopeGroups(themePaths, metadata);
|
||||
const themeList = this.formatScopeGroups(groups, {
|
||||
formatPath: (p) => this.formatDisplayPath(p),
|
||||
formatPackagePath: (p, source) => this.getShortPath(p, source),
|
||||
});
|
||||
this.chatContainer.addChild(new Text(`${sectionHeader("Themes")}\n${themeList}`, 0, 0));
|
||||
this.chatContainer.addChild(new Spacer(1));
|
||||
}
|
||||
const commandDiagnostics = this.session.extensionRunner?.getCommandDiagnostics() ?? [];
|
||||
extensionDiagnostics.push(...commandDiagnostics);
|
||||
|
||||
const themeDiagnostics = this.session.resourceLoader.getThemes().diagnostics;
|
||||
if (themeDiagnostics.length > 0) {
|
||||
const warningLines = this.formatDiagnostics(themeDiagnostics, metadata);
|
||||
this.chatContainer.addChild(new Text(`${theme.fg("warning", "[Theme conflicts]")}\n${warningLines}`, 0, 0));
|
||||
this.chatContainer.addChild(new Spacer(1));
|
||||
const shortcutDiagnostics = this.session.extensionRunner?.getShortcutDiagnostics() ?? [];
|
||||
extensionDiagnostics.push(...shortcutDiagnostics);
|
||||
|
||||
if (extensionDiagnostics.length > 0) {
|
||||
const warningLines = this.formatDiagnostics(extensionDiagnostics, metadata);
|
||||
this.chatContainer.addChild(
|
||||
new Text(`${theme.fg("warning", "[Extension issues]")}\n${warningLines}`, 0, 0),
|
||||
);
|
||||
this.chatContainer.addChild(new Spacer(1));
|
||||
}
|
||||
|
||||
const themeDiagnostics = themesResult.diagnostics;
|
||||
if (themeDiagnostics.length > 0) {
|
||||
const warningLines = this.formatDiagnostics(themeDiagnostics, metadata);
|
||||
this.chatContainer.addChild(new Text(`${theme.fg("warning", "[Theme conflicts]")}\n${warningLines}`, 0, 0));
|
||||
this.chatContainer.addChild(new Spacer(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3728,7 +3746,11 @@ export class InteractiveMode {
|
|||
}
|
||||
this.rebuildChatFromMessages();
|
||||
dismissLoader(this.editor as Component);
|
||||
this.showLoadedResources({ extensionPaths: runner?.getExtensionPaths() ?? [], force: true });
|
||||
this.showLoadedResources({
|
||||
extensionPaths: runner?.getExtensionPaths() ?? [],
|
||||
force: false,
|
||||
showDiagnosticsWhenQuiet: true,
|
||||
});
|
||||
const modelsJsonError = this.session.modelRegistry.getError();
|
||||
if (modelsJsonError) {
|
||||
this.showError(`models.json error: ${modelsJsonError}`);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,10 @@ function renderLastLine(container: Container, width = 120): string {
|
|||
return last.render(width).join("\n");
|
||||
}
|
||||
|
||||
function renderAll(container: Container, width = 120): string {
|
||||
return container.children.flatMap((child) => child.render(width)).join("\n");
|
||||
}
|
||||
|
||||
describe("InteractiveMode.showStatus", () => {
|
||||
beforeAll(() => {
|
||||
// showStatus uses the global theme instance
|
||||
|
|
@ -55,3 +59,78 @@ describe("InteractiveMode.showStatus", () => {
|
|||
expect(renderLastLine(fakeThis.chatContainer)).toContain("STATUS_TWO");
|
||||
});
|
||||
});
|
||||
|
||||
describe("InteractiveMode.showLoadedResources", () => {
|
||||
beforeAll(() => {
|
||||
initTheme("dark");
|
||||
});
|
||||
|
||||
function createShowLoadedResourcesThis(options: {
|
||||
quietStartup: boolean;
|
||||
verbose?: boolean;
|
||||
skills?: Array<{ filePath: string }>;
|
||||
skillDiagnostics?: Array<{ type: "warning" | "error" | "collision"; message: string }>;
|
||||
}) {
|
||||
const fakeThis: any = {
|
||||
options: { verbose: options.verbose ?? false },
|
||||
chatContainer: new Container(),
|
||||
settingsManager: {
|
||||
getQuietStartup: () => options.quietStartup,
|
||||
},
|
||||
session: {
|
||||
promptTemplates: [],
|
||||
extensionRunner: undefined,
|
||||
resourceLoader: {
|
||||
getPathMetadata: () => new Map(),
|
||||
getAgentsFiles: () => ({ agentsFiles: [] }),
|
||||
getSkills: () => ({
|
||||
skills: options.skills ?? [],
|
||||
diagnostics: options.skillDiagnostics ?? [],
|
||||
}),
|
||||
getPrompts: () => ({ prompts: [], diagnostics: [] }),
|
||||
getExtensions: () => ({ errors: [] }),
|
||||
getThemes: () => ({ themes: [], diagnostics: [] }),
|
||||
},
|
||||
},
|
||||
formatDisplayPath: (p: string) => p,
|
||||
buildScopeGroups: () => [],
|
||||
formatScopeGroups: () => "resource-list",
|
||||
getShortPath: (p: string) => p,
|
||||
formatDiagnostics: () => "diagnostics",
|
||||
};
|
||||
|
||||
return fakeThis;
|
||||
}
|
||||
|
||||
test("does not show verbose listing on quiet startup during reload", () => {
|
||||
const fakeThis = createShowLoadedResourcesThis({
|
||||
quietStartup: true,
|
||||
skills: [{ filePath: "/tmp/skill/SKILL.md" }],
|
||||
});
|
||||
|
||||
(InteractiveMode as any).prototype.showLoadedResources.call(fakeThis, {
|
||||
extensionPaths: ["/tmp/ext/index.ts"],
|
||||
force: false,
|
||||
showDiagnosticsWhenQuiet: true,
|
||||
});
|
||||
|
||||
expect(fakeThis.chatContainer.children).toHaveLength(0);
|
||||
});
|
||||
|
||||
test("still shows diagnostics on quiet startup when requested", () => {
|
||||
const fakeThis = createShowLoadedResourcesThis({
|
||||
quietStartup: true,
|
||||
skills: [{ filePath: "/tmp/skill/SKILL.md" }],
|
||||
skillDiagnostics: [{ type: "warning", message: "duplicate skill name" }],
|
||||
});
|
||||
|
||||
(InteractiveMode as any).prototype.showLoadedResources.call(fakeThis, {
|
||||
force: false,
|
||||
showDiagnosticsWhenQuiet: true,
|
||||
});
|
||||
|
||||
const output = renderAll(fakeThis.chatContainer);
|
||||
expect(output).toContain("[Skill conflicts]");
|
||||
expect(output).not.toContain("[Skills]");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue