mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-16 00:03:00 +00:00
feat(coding-agent): ResourceLoader, package management, and /reload command (#645)
- Add ResourceLoader interface and DefaultResourceLoader implementation - Add PackageManager for npm/git extension sources with install/remove/update - Add session.reload() and session.bindExtensions() APIs - Add /reload command in interactive mode - Add CLI flags: --skill, --theme, --prompt-template, --no-themes, --no-prompt-templates - Add pi install/remove/update commands for extension management - Refactor settings.json to use arrays for skills, prompts, themes - Remove legacy SkillsSettings source flags and filters - Update SDK examples and documentation for ResourceLoader pattern - Add theme registration and loadThemeFromPath for dynamic themes - Add getShellEnv to include bin dir in PATH for bash commands
This commit is contained in:
parent
866d21c252
commit
b846a4bfcf
51 changed files with 2724 additions and 1852 deletions
516
packages/coding-agent/src/core/resource-loader.ts
Normal file
516
packages/coding-agent/src/core/resource-loader.ts
Normal file
|
|
@ -0,0 +1,516 @@
|
|||
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
||||
import { homedir } from "node:os";
|
||||
import { join, resolve } from "node:path";
|
||||
import chalk from "chalk";
|
||||
import { CONFIG_DIR_NAME, getAgentDir } from "../config.js";
|
||||
import { loadThemeFromPath, type Theme } from "../modes/interactive/theme/theme.js";
|
||||
import { createEventBus, type EventBus } from "./event-bus.js";
|
||||
import {
|
||||
createExtensionRuntime,
|
||||
discoverAndLoadExtensions,
|
||||
loadExtensionFromFactory,
|
||||
loadExtensions,
|
||||
} from "./extensions/loader.js";
|
||||
import type { Extension, ExtensionFactory, ExtensionRuntime, LoadExtensionsResult } from "./extensions/types.js";
|
||||
import { DefaultPackageManager } from "./package-manager.js";
|
||||
import type { PromptTemplate } from "./prompt-templates.js";
|
||||
import { loadPromptTemplates } from "./prompt-templates.js";
|
||||
import { SettingsManager } from "./settings-manager.js";
|
||||
import type { Skill, SkillWarning } from "./skills.js";
|
||||
import { loadSkills } from "./skills.js";
|
||||
|
||||
export interface ResourceDiagnostic {
|
||||
type: "warning" | "error";
|
||||
message: string;
|
||||
path?: string;
|
||||
}
|
||||
|
||||
export interface ResourceLoader {
|
||||
getExtensions(): LoadExtensionsResult;
|
||||
getSkills(): { skills: Skill[]; diagnostics: ResourceDiagnostic[] };
|
||||
getPrompts(): { prompts: PromptTemplate[]; diagnostics: ResourceDiagnostic[] };
|
||||
getThemes(): { themes: Theme[]; diagnostics: ResourceDiagnostic[] };
|
||||
getAgentsFiles(): { agentsFiles: Array<{ path: string; content: string }> };
|
||||
getSystemPrompt(): string | undefined;
|
||||
getAppendSystemPrompt(): string[];
|
||||
reload(): Promise<void>;
|
||||
}
|
||||
|
||||
function resolvePromptInput(input: string | undefined, description: string): string | undefined {
|
||||
if (!input) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (existsSync(input)) {
|
||||
try {
|
||||
return readFileSync(input, "utf-8");
|
||||
} catch (error) {
|
||||
console.error(chalk.yellow(`Warning: Could not read ${description} file ${input}: ${error}`));
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
function loadContextFileFromDir(dir: string): { path: string; content: string } | null {
|
||||
const candidates = ["AGENTS.md", "CLAUDE.md"];
|
||||
for (const filename of candidates) {
|
||||
const filePath = join(dir, filename);
|
||||
if (existsSync(filePath)) {
|
||||
try {
|
||||
return {
|
||||
path: filePath,
|
||||
content: readFileSync(filePath, "utf-8"),
|
||||
};
|
||||
} catch (error) {
|
||||
console.error(chalk.yellow(`Warning: Could not read ${filePath}: ${error}`));
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function loadProjectContextFiles(
|
||||
options: { cwd?: string; agentDir?: string } = {},
|
||||
): Array<{ path: string; content: string }> {
|
||||
const resolvedCwd = options.cwd ?? process.cwd();
|
||||
const resolvedAgentDir = options.agentDir ?? getAgentDir();
|
||||
|
||||
const contextFiles: Array<{ path: string; content: string }> = [];
|
||||
const seenPaths = new Set<string>();
|
||||
|
||||
const globalContext = loadContextFileFromDir(resolvedAgentDir);
|
||||
if (globalContext) {
|
||||
contextFiles.push(globalContext);
|
||||
seenPaths.add(globalContext.path);
|
||||
}
|
||||
|
||||
const ancestorContextFiles: Array<{ path: string; content: string }> = [];
|
||||
|
||||
let currentDir = resolvedCwd;
|
||||
const root = resolve("/");
|
||||
|
||||
while (true) {
|
||||
const contextFile = loadContextFileFromDir(currentDir);
|
||||
if (contextFile && !seenPaths.has(contextFile.path)) {
|
||||
ancestorContextFiles.unshift(contextFile);
|
||||
seenPaths.add(contextFile.path);
|
||||
}
|
||||
|
||||
if (currentDir === root) break;
|
||||
|
||||
const parentDir = resolve(currentDir, "..");
|
||||
if (parentDir === currentDir) break;
|
||||
currentDir = parentDir;
|
||||
}
|
||||
|
||||
contextFiles.push(...ancestorContextFiles);
|
||||
|
||||
return contextFiles;
|
||||
}
|
||||
|
||||
export interface DefaultResourceLoaderOptions {
|
||||
cwd?: string;
|
||||
agentDir?: string;
|
||||
settingsManager?: SettingsManager;
|
||||
eventBus?: EventBus;
|
||||
additionalExtensionPaths?: string[];
|
||||
additionalSkillPaths?: string[];
|
||||
additionalPromptTemplatePaths?: string[];
|
||||
additionalThemePaths?: string[];
|
||||
extensionFactories?: ExtensionFactory[];
|
||||
noExtensions?: boolean;
|
||||
noSkills?: boolean;
|
||||
noPromptTemplates?: boolean;
|
||||
noThemes?: boolean;
|
||||
systemPrompt?: string;
|
||||
appendSystemPrompt?: string;
|
||||
extensionsOverride?: (base: LoadExtensionsResult) => LoadExtensionsResult;
|
||||
skillsOverride?: (base: { skills: Skill[]; diagnostics: ResourceDiagnostic[] }) => {
|
||||
skills: Skill[];
|
||||
diagnostics: ResourceDiagnostic[];
|
||||
};
|
||||
promptsOverride?: (base: { prompts: PromptTemplate[]; diagnostics: ResourceDiagnostic[] }) => {
|
||||
prompts: PromptTemplate[];
|
||||
diagnostics: ResourceDiagnostic[];
|
||||
};
|
||||
themesOverride?: (base: { themes: Theme[]; diagnostics: ResourceDiagnostic[] }) => {
|
||||
themes: Theme[];
|
||||
diagnostics: ResourceDiagnostic[];
|
||||
};
|
||||
agentsFilesOverride?: (base: { agentsFiles: Array<{ path: string; content: string }> }) => {
|
||||
agentsFiles: Array<{ path: string; content: string }>;
|
||||
};
|
||||
systemPromptOverride?: (base: string | undefined) => string | undefined;
|
||||
appendSystemPromptOverride?: (base: string[]) => string[];
|
||||
}
|
||||
|
||||
export class DefaultResourceLoader implements ResourceLoader {
|
||||
private cwd: string;
|
||||
private agentDir: string;
|
||||
private settingsManager: SettingsManager;
|
||||
private eventBus: EventBus;
|
||||
private packageManager: DefaultPackageManager;
|
||||
private additionalExtensionPaths: string[];
|
||||
private additionalSkillPaths: string[];
|
||||
private additionalPromptTemplatePaths: string[];
|
||||
private additionalThemePaths: string[];
|
||||
private extensionFactories: ExtensionFactory[];
|
||||
private noExtensions: boolean;
|
||||
private noSkills: boolean;
|
||||
private noPromptTemplates: boolean;
|
||||
private noThemes: boolean;
|
||||
private systemPromptSource?: string;
|
||||
private appendSystemPromptSource?: string;
|
||||
private extensionsOverride?: (base: LoadExtensionsResult) => LoadExtensionsResult;
|
||||
private skillsOverride?: (base: { skills: Skill[]; diagnostics: ResourceDiagnostic[] }) => {
|
||||
skills: Skill[];
|
||||
diagnostics: ResourceDiagnostic[];
|
||||
};
|
||||
private promptsOverride?: (base: { prompts: PromptTemplate[]; diagnostics: ResourceDiagnostic[] }) => {
|
||||
prompts: PromptTemplate[];
|
||||
diagnostics: ResourceDiagnostic[];
|
||||
};
|
||||
private themesOverride?: (base: { themes: Theme[]; diagnostics: ResourceDiagnostic[] }) => {
|
||||
themes: Theme[];
|
||||
diagnostics: ResourceDiagnostic[];
|
||||
};
|
||||
private agentsFilesOverride?: (base: { agentsFiles: Array<{ path: string; content: string }> }) => {
|
||||
agentsFiles: Array<{ path: string; content: string }>;
|
||||
};
|
||||
private systemPromptOverride?: (base: string | undefined) => string | undefined;
|
||||
private appendSystemPromptOverride?: (base: string[]) => string[];
|
||||
|
||||
private extensionsResult: LoadExtensionsResult;
|
||||
private skills: Skill[];
|
||||
private skillDiagnostics: ResourceDiagnostic[];
|
||||
private prompts: PromptTemplate[];
|
||||
private promptDiagnostics: ResourceDiagnostic[];
|
||||
private themes: Theme[];
|
||||
private themeDiagnostics: ResourceDiagnostic[];
|
||||
private agentsFiles: Array<{ path: string; content: string }>;
|
||||
private systemPrompt?: string;
|
||||
private appendSystemPrompt: string[];
|
||||
|
||||
constructor(options: DefaultResourceLoaderOptions) {
|
||||
this.cwd = options.cwd ?? process.cwd();
|
||||
this.agentDir = options.agentDir ?? getAgentDir();
|
||||
this.settingsManager = options.settingsManager ?? SettingsManager.create(this.cwd, this.agentDir);
|
||||
this.eventBus = options.eventBus ?? createEventBus();
|
||||
this.packageManager = new DefaultPackageManager({
|
||||
cwd: this.cwd,
|
||||
agentDir: this.agentDir,
|
||||
settingsManager: this.settingsManager,
|
||||
});
|
||||
this.additionalExtensionPaths = options.additionalExtensionPaths ?? [];
|
||||
this.additionalSkillPaths = options.additionalSkillPaths ?? [];
|
||||
this.additionalPromptTemplatePaths = options.additionalPromptTemplatePaths ?? [];
|
||||
this.additionalThemePaths = options.additionalThemePaths ?? [];
|
||||
this.extensionFactories = options.extensionFactories ?? [];
|
||||
this.noExtensions = options.noExtensions ?? false;
|
||||
this.noSkills = options.noSkills ?? false;
|
||||
this.noPromptTemplates = options.noPromptTemplates ?? false;
|
||||
this.noThemes = options.noThemes ?? false;
|
||||
this.systemPromptSource = options.systemPrompt;
|
||||
this.appendSystemPromptSource = options.appendSystemPrompt;
|
||||
this.extensionsOverride = options.extensionsOverride;
|
||||
this.skillsOverride = options.skillsOverride;
|
||||
this.promptsOverride = options.promptsOverride;
|
||||
this.themesOverride = options.themesOverride;
|
||||
this.agentsFilesOverride = options.agentsFilesOverride;
|
||||
this.systemPromptOverride = options.systemPromptOverride;
|
||||
this.appendSystemPromptOverride = options.appendSystemPromptOverride;
|
||||
|
||||
this.extensionsResult = { extensions: [], errors: [], runtime: createExtensionRuntime() };
|
||||
this.skills = [];
|
||||
this.skillDiagnostics = [];
|
||||
this.prompts = [];
|
||||
this.promptDiagnostics = [];
|
||||
this.themes = [];
|
||||
this.themeDiagnostics = [];
|
||||
this.agentsFiles = [];
|
||||
this.appendSystemPrompt = [];
|
||||
}
|
||||
|
||||
getExtensions(): LoadExtensionsResult {
|
||||
return this.extensionsResult;
|
||||
}
|
||||
|
||||
getSkills(): { skills: Skill[]; diagnostics: ResourceDiagnostic[] } {
|
||||
return { skills: this.skills, diagnostics: this.skillDiagnostics };
|
||||
}
|
||||
|
||||
getPrompts(): { prompts: PromptTemplate[]; diagnostics: ResourceDiagnostic[] } {
|
||||
return { prompts: this.prompts, diagnostics: this.promptDiagnostics };
|
||||
}
|
||||
|
||||
getThemes(): { themes: Theme[]; diagnostics: ResourceDiagnostic[] } {
|
||||
return { themes: this.themes, diagnostics: this.themeDiagnostics };
|
||||
}
|
||||
|
||||
getAgentsFiles(): { agentsFiles: Array<{ path: string; content: string }> } {
|
||||
return { agentsFiles: this.agentsFiles };
|
||||
}
|
||||
|
||||
getSystemPrompt(): string | undefined {
|
||||
return this.systemPrompt;
|
||||
}
|
||||
|
||||
getAppendSystemPrompt(): string[] {
|
||||
return this.appendSystemPrompt;
|
||||
}
|
||||
|
||||
async reload(): Promise<void> {
|
||||
const resolvedPaths = await this.packageManager.resolve();
|
||||
const cliExtensionPaths = await this.packageManager.resolveExtensionSources(this.additionalExtensionPaths, {
|
||||
temporary: true,
|
||||
});
|
||||
|
||||
const extensionPaths = this.noExtensions
|
||||
? cliExtensionPaths.extensions
|
||||
: this.mergePaths(resolvedPaths.extensions, cliExtensionPaths.extensions);
|
||||
|
||||
let extensionsResult: LoadExtensionsResult;
|
||||
if (this.noExtensions) {
|
||||
extensionsResult = await loadExtensions(extensionPaths, this.cwd, this.eventBus);
|
||||
} else {
|
||||
extensionsResult = await discoverAndLoadExtensions(extensionPaths, this.cwd, this.agentDir, this.eventBus);
|
||||
}
|
||||
const inlineExtensions = await this.loadExtensionFactories(extensionsResult.runtime);
|
||||
extensionsResult.extensions.push(...inlineExtensions.extensions);
|
||||
extensionsResult.errors.push(...inlineExtensions.errors);
|
||||
this.extensionsResult = this.extensionsOverride ? this.extensionsOverride(extensionsResult) : extensionsResult;
|
||||
|
||||
const skillPaths = this.noSkills
|
||||
? this.mergePaths(cliExtensionPaths.skills, this.additionalSkillPaths)
|
||||
: this.mergePaths([...resolvedPaths.skills, ...cliExtensionPaths.skills], this.additionalSkillPaths);
|
||||
|
||||
let skillsResult: { skills: Skill[]; diagnostics: ResourceDiagnostic[] };
|
||||
if (this.noSkills && skillPaths.length === 0) {
|
||||
skillsResult = { skills: [], diagnostics: [] };
|
||||
} else {
|
||||
const result = loadSkills({
|
||||
cwd: this.cwd,
|
||||
agentDir: this.agentDir,
|
||||
skillPaths,
|
||||
});
|
||||
skillsResult = { skills: result.skills, diagnostics: this.toDiagnostics(result.warnings) };
|
||||
}
|
||||
const resolvedSkills = this.skillsOverride ? this.skillsOverride(skillsResult) : skillsResult;
|
||||
this.skills = resolvedSkills.skills;
|
||||
this.skillDiagnostics = resolvedSkills.diagnostics;
|
||||
|
||||
const promptPaths = this.noPromptTemplates
|
||||
? this.mergePaths(cliExtensionPaths.prompts, this.additionalPromptTemplatePaths)
|
||||
: this.mergePaths(
|
||||
[...resolvedPaths.prompts, ...cliExtensionPaths.prompts],
|
||||
this.additionalPromptTemplatePaths,
|
||||
);
|
||||
|
||||
let promptsResult: { prompts: PromptTemplate[]; diagnostics: ResourceDiagnostic[] };
|
||||
if (this.noPromptTemplates && promptPaths.length === 0) {
|
||||
promptsResult = { prompts: [], diagnostics: [] };
|
||||
} else {
|
||||
promptsResult = {
|
||||
prompts: loadPromptTemplates({
|
||||
cwd: this.cwd,
|
||||
agentDir: this.agentDir,
|
||||
promptPaths,
|
||||
}),
|
||||
diagnostics: [],
|
||||
};
|
||||
}
|
||||
const resolvedPrompts = this.promptsOverride ? this.promptsOverride(promptsResult) : promptsResult;
|
||||
this.prompts = resolvedPrompts.prompts;
|
||||
this.promptDiagnostics = resolvedPrompts.diagnostics;
|
||||
|
||||
const themePaths = this.noThemes
|
||||
? this.mergePaths(cliExtensionPaths.themes, this.additionalThemePaths)
|
||||
: this.mergePaths([...resolvedPaths.themes, ...cliExtensionPaths.themes], this.additionalThemePaths);
|
||||
|
||||
let themesResult: { themes: Theme[]; diagnostics: ResourceDiagnostic[] };
|
||||
if (this.noThemes && themePaths.length === 0) {
|
||||
themesResult = { themes: [], diagnostics: [] };
|
||||
} else {
|
||||
themesResult = this.loadThemes(themePaths);
|
||||
}
|
||||
const resolvedThemes = this.themesOverride ? this.themesOverride(themesResult) : themesResult;
|
||||
this.themes = resolvedThemes.themes;
|
||||
this.themeDiagnostics = resolvedThemes.diagnostics;
|
||||
|
||||
const agentsFiles = { agentsFiles: loadProjectContextFiles({ cwd: this.cwd, agentDir: this.agentDir }) };
|
||||
const resolvedAgentsFiles = this.agentsFilesOverride ? this.agentsFilesOverride(agentsFiles) : agentsFiles;
|
||||
this.agentsFiles = resolvedAgentsFiles.agentsFiles;
|
||||
|
||||
const baseSystemPrompt = resolvePromptInput(
|
||||
this.systemPromptSource ?? this.discoverSystemPromptFile(),
|
||||
"system prompt",
|
||||
);
|
||||
this.systemPrompt = this.systemPromptOverride ? this.systemPromptOverride(baseSystemPrompt) : baseSystemPrompt;
|
||||
|
||||
const appendSource = this.appendSystemPromptSource ?? this.discoverAppendSystemPromptFile();
|
||||
const resolvedAppend = resolvePromptInput(appendSource, "append system prompt");
|
||||
const baseAppend = resolvedAppend ? [resolvedAppend] : [];
|
||||
this.appendSystemPrompt = this.appendSystemPromptOverride
|
||||
? this.appendSystemPromptOverride(baseAppend)
|
||||
: baseAppend;
|
||||
}
|
||||
|
||||
private mergePaths(primary: string[], additional: string[]): string[] {
|
||||
const merged: string[] = [];
|
||||
const seen = new Set<string>();
|
||||
|
||||
for (const p of [...primary, ...additional]) {
|
||||
const resolved = this.resolveResourcePath(p);
|
||||
if (seen.has(resolved)) continue;
|
||||
seen.add(resolved);
|
||||
merged.push(resolved);
|
||||
}
|
||||
|
||||
return merged;
|
||||
}
|
||||
|
||||
private resolveResourcePath(p: string): string {
|
||||
const trimmed = p.trim();
|
||||
let expanded = trimmed;
|
||||
if (trimmed === "~") {
|
||||
expanded = homedir();
|
||||
} else if (trimmed.startsWith("~/")) {
|
||||
expanded = join(homedir(), trimmed.slice(2));
|
||||
} else if (trimmed.startsWith("~")) {
|
||||
expanded = join(homedir(), trimmed.slice(1));
|
||||
}
|
||||
return resolve(this.cwd, expanded);
|
||||
}
|
||||
|
||||
private loadThemes(paths: string[]): { themes: Theme[]; diagnostics: ResourceDiagnostic[] } {
|
||||
const themes: Theme[] = [];
|
||||
const diagnostics: ResourceDiagnostic[] = [];
|
||||
const defaultDirs = [join(this.agentDir, "themes"), join(this.cwd, CONFIG_DIR_NAME, "themes")];
|
||||
|
||||
for (const dir of defaultDirs) {
|
||||
this.loadThemesFromDir(dir, themes, diagnostics);
|
||||
}
|
||||
|
||||
for (const p of paths) {
|
||||
const resolved = resolve(this.cwd, p);
|
||||
if (!existsSync(resolved)) {
|
||||
diagnostics.push({ type: "warning", message: "theme path does not exist", path: resolved });
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
const stats = statSync(resolved);
|
||||
if (stats.isDirectory()) {
|
||||
this.loadThemesFromDir(resolved, themes, diagnostics);
|
||||
} else if (stats.isFile() && resolved.endsWith(".json")) {
|
||||
this.loadThemeFromFile(resolved, themes, diagnostics);
|
||||
} else {
|
||||
diagnostics.push({ type: "warning", message: "theme path is not a json file", path: resolved });
|
||||
}
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : "failed to read theme path";
|
||||
diagnostics.push({ type: "warning", message, path: resolved });
|
||||
}
|
||||
}
|
||||
|
||||
return { themes, diagnostics };
|
||||
}
|
||||
|
||||
private loadThemesFromDir(dir: string, themes: Theme[], diagnostics: ResourceDiagnostic[]): void {
|
||||
if (!existsSync(dir)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const entries = readdirSync(dir, { withFileTypes: true });
|
||||
for (const entry of entries) {
|
||||
let isFile = entry.isFile();
|
||||
if (entry.isSymbolicLink()) {
|
||||
try {
|
||||
isFile = statSync(join(dir, entry.name)).isFile();
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!isFile) {
|
||||
continue;
|
||||
}
|
||||
if (!entry.name.endsWith(".json")) {
|
||||
continue;
|
||||
}
|
||||
this.loadThemeFromFile(join(dir, entry.name), themes, diagnostics);
|
||||
}
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : "failed to read theme directory";
|
||||
diagnostics.push({ type: "warning", message, path: dir });
|
||||
}
|
||||
}
|
||||
|
||||
private loadThemeFromFile(filePath: string, themes: Theme[], diagnostics: ResourceDiagnostic[]): void {
|
||||
try {
|
||||
themes.push(loadThemeFromPath(filePath));
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : "failed to load theme";
|
||||
diagnostics.push({ type: "warning", message, path: filePath });
|
||||
}
|
||||
}
|
||||
|
||||
private async loadExtensionFactories(runtime: ExtensionRuntime): Promise<{
|
||||
extensions: Extension[];
|
||||
errors: Array<{ path: string; error: string }>;
|
||||
}> {
|
||||
const extensions: Extension[] = [];
|
||||
const errors: Array<{ path: string; error: string }> = [];
|
||||
|
||||
for (const [index, factory] of this.extensionFactories.entries()) {
|
||||
const extensionPath = `<inline:${index + 1}>`;
|
||||
try {
|
||||
const extension = await loadExtensionFromFactory(factory, this.cwd, this.eventBus, runtime, extensionPath);
|
||||
extensions.push(extension);
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : "failed to load extension";
|
||||
errors.push({ path: extensionPath, error: message });
|
||||
}
|
||||
}
|
||||
|
||||
return { extensions, errors };
|
||||
}
|
||||
|
||||
private toDiagnostics(warnings: SkillWarning[]): ResourceDiagnostic[] {
|
||||
return warnings.map((warning) => ({
|
||||
type: "warning",
|
||||
message: warning.message,
|
||||
path: warning.skillPath,
|
||||
}));
|
||||
}
|
||||
|
||||
private discoverSystemPromptFile(): string | undefined {
|
||||
const projectPath = join(this.cwd, CONFIG_DIR_NAME, "SYSTEM.md");
|
||||
if (existsSync(projectPath)) {
|
||||
return projectPath;
|
||||
}
|
||||
|
||||
const globalPath = join(this.agentDir, "SYSTEM.md");
|
||||
if (existsSync(globalPath)) {
|
||||
return globalPath;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private discoverAppendSystemPromptFile(): string | undefined {
|
||||
const projectPath = join(this.cwd, CONFIG_DIR_NAME, "APPEND_SYSTEM.md");
|
||||
if (existsSync(projectPath)) {
|
||||
return projectPath;
|
||||
}
|
||||
|
||||
const globalPath = join(this.agentDir, "APPEND_SYSTEM.md");
|
||||
if (existsSync(globalPath)) {
|
||||
return globalPath;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue