fix(coding-agent): rehome TITLE_SYSTEM.md discovery and reload it on cwd change

This commit is contained in:
can1357
2026-06-10 09:51:46 +02:00
parent 1696047d8e
commit ffe3b6afd4
5 changed files with 27 additions and 16 deletions
+1 -1
View File
@@ -10,7 +10,7 @@
- Added `python.interpreter` to pin eval's Python backend to an explicit interpreter and skip automatic runtime discovery ([#1802](https://github.com/can1357/oh-my-pi/issues/1802)).
- Added `!command` resolution for `models.yml` provider `apiKey` values and provider/model headers ([#1888](https://github.com/can1357/oh-my-pi/issues/1888)).
- Documented the oMLX setup path through existing OpenAI-compatible local discovery ([#1957](https://github.com/can1357/oh-my-pi/issues/1957)).
- Added `TITLE_SYSTEM.md` discovery so users can override the automatic session-title generation prompt for online and local tiny title models without patching installed prompt files.
- Added `TITLE_SYSTEM.md` discovery so users can override the automatic session-title generation prompt for online and local tiny title models without patching installed prompt files. The override is re-discovered when the session working directory changes via `/cwd`.
- Added a structured memory runtime surface for extensions and UI integrations to query backend status, search memories, and save explicit memories across the configured memory backend.
- Added support for Git repositories using the `reftable` storage format by detecting `extensions.refStorage = reftable` in the repository configuration and falling back to shelling out to Git commands (`git symbolic-ref`, `git rev-parse`) for reference and HEAD resolution.
- Added `/setup providers` (also available as `/setup` or `/providers`) to reopen the interactive provider setup scene from an active TUI session, letting users sign in and choose a web search provider without rerunning the full onboarding flow.
+1 -14
View File
@@ -67,7 +67,7 @@ import {
import type { AgentSession } from "./session/agent-session";
import type { AuthStorage } from "./session/auth-storage";
import { resolveResumableSession, type SessionInfo, SessionManager } from "./session/session-manager";
import { resolvePromptInput } from "./system-prompt";
import { discoverTitleSystemPromptFile, resolvePromptInput } from "./system-prompt";
import { initTelemetryExport, isTelemetryExportEnabled } from "./telemetry-export";
import { AUTO_THINKING } from "./thinking";
import { discoverStartupLspServers, type LspStartupServerInfo } from "./tools";
@@ -721,19 +721,6 @@ function discoverAppendSystemPromptFile(): string | undefined {
return undefined;
}
/** Discover TITLE_SYSTEM.md file for automatic session-title prompt overrides */
export function discoverTitleSystemPromptFile(cwd?: string): string | undefined {
const projectPath = findConfigFile("TITLE_SYSTEM.md", { user: false, cwd });
if (projectPath) {
return projectPath;
}
const globalPath = findConfigFile("TITLE_SYSTEM.md", { user: true, cwd });
if (globalPath) {
return globalPath;
}
return undefined;
}
async function buildSessionOptions(
parsed: Args,
scopedModels: ScopedModel[],
@@ -78,6 +78,7 @@ import type { ShakeMode } from "../session/shake-types";
import { BUILTIN_SLASH_COMMAND_RESERVED_NAMES } from "../slash-commands/builtin-registry";
import { formatDuration } from "../slash-commands/helpers/format";
import { STTController, type SttState } from "../stt";
import { discoverTitleSystemPromptFile, resolvePromptInput } from "../system-prompt";
import type { LspStartupServerInfo } from "../tools";
import { normalizeLocalScheme } from "../tools/path-utils";
import { setAutoQaConsentHandler } from "../tools/report-tool-issue";
@@ -684,6 +685,13 @@ export class InteractiveMode implements InteractiveModeContext {
this.updateEditorTopBorder();
}
/** Reload the title-generation system prompt override for the provided working directory. */
async refreshTitleSystemPrompt(cwd?: string): Promise<void> {
const basePath = cwd ?? this.sessionManager.getCwd();
const titleSystemPromptSource = discoverTitleSystemPromptFile(basePath);
this.titleSystemPrompt = await resolvePromptInput(titleSystemPromptSource, "title system prompt");
}
/** Reload slash commands and autocomplete for the provided working directory. */
async refreshSlashCommandState(cwd?: string): Promise<void> {
const basePath = cwd ?? this.sessionManager.getCwd();
@@ -718,6 +726,7 @@ export class InteractiveMode implements InteractiveModeContext {
// Re-warm plugin roots, capabilities, slash commands, and the ssh tool so
// the next prompt sees everything scoped to the new project directory.
clearClaudePluginRootsCache();
await this.refreshTitleSystemPrompt(newCwd);
resetCapabilities();
await this.refreshSlashCommandState(newCwd);
await this.session.refreshSshTool({ activateIfAvailable: true });
@@ -8,6 +8,7 @@ import { $env, getGpuCachePath, getProjectDir, hasFsCode, isEnoent, logger, prom
import { $ } from "bun";
import { contextFileCapability } from "./capability/context-file";
import { systemPromptCapability } from "./capability/system-prompt";
import { findConfigFile } from "./config";
import type { SkillsSettings } from "./config/settings";
import { type ContextFile, loadCapability, type SystemPrompt as SystemPromptFile } from "./discovery";
import { expandAtImports } from "./discovery/at-imports";
@@ -208,6 +209,19 @@ async function getEnvironmentInfo(): Promise<Array<{ label: string; value: strin
return entries.filter((e): e is { label: string; value: string } => !!e.value);
}
/** Discover TITLE_SYSTEM.md file for automatic session-title prompt overrides */
export function discoverTitleSystemPromptFile(cwd?: string): string | undefined {
const projectPath = findConfigFile("TITLE_SYSTEM.md", { user: false, cwd });
if (projectPath) {
return projectPath;
}
const globalPath = findConfigFile("TITLE_SYSTEM.md", { user: true, cwd });
if (globalPath) {
return globalPath;
}
return undefined;
}
/** Resolve input as file path or literal string */
export async function resolvePromptInput(input: string | undefined, description: string): Promise<string | undefined> {
if (!input) {
@@ -2,8 +2,9 @@ import { afterEach, describe, expect, it, vi } from "bun:test";
import * as fs from "node:fs/promises";
import * as os from "node:os";
import * as path from "node:path";
import { discoverTitleSystemPromptFile, submitInteractiveInput } from "@oh-my-pi/pi-coding-agent/main";
import { submitInteractiveInput } from "@oh-my-pi/pi-coding-agent/main";
import type { SubmittedUserInput } from "@oh-my-pi/pi-coding-agent/modes/types";
import { discoverTitleSystemPromptFile } from "@oh-my-pi/pi-coding-agent/system-prompt";
const cleanupDirs: string[] = [];