mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-15 21:03:19 +00:00
fix(coding-agent): respect ignore files in skill loader
This commit is contained in:
parent
91e09765e7
commit
f89b49baeb
3 changed files with 86 additions and 3 deletions
|
|
@ -1,6 +1,7 @@
|
|||
import { existsSync, readdirSync, readFileSync, realpathSync, statSync } from "fs";
|
||||
import ignore from "ignore";
|
||||
import { homedir } from "os";
|
||||
import { basename, dirname, isAbsolute, join, resolve, sep } from "path";
|
||||
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "path";
|
||||
import { CONFIG_DIR_NAME, getAgentDir } from "../config.js";
|
||||
import { parseFrontmatter } from "../utils/frontmatter.js";
|
||||
import type { ResourceDiagnostic } from "./diagnostics.js";
|
||||
|
|
@ -11,6 +12,57 @@ const MAX_NAME_LENGTH = 64;
|
|||
/** Max description length per spec */
|
||||
const MAX_DESCRIPTION_LENGTH = 1024;
|
||||
|
||||
const IGNORE_FILE_NAMES = [".gitignore", ".ignore", ".fdignore"];
|
||||
|
||||
type IgnoreMatcher = ReturnType<typeof ignore>;
|
||||
|
||||
function toPosixPath(p: string): string {
|
||||
return p.split(sep).join("/");
|
||||
}
|
||||
|
||||
function prefixIgnorePattern(line: string, prefix: string): string | null {
|
||||
const trimmed = line.trim();
|
||||
if (!trimmed) return null;
|
||||
if (trimmed.startsWith("#") && !trimmed.startsWith("\\#")) return null;
|
||||
|
||||
let pattern = line;
|
||||
let negated = false;
|
||||
|
||||
if (pattern.startsWith("!")) {
|
||||
negated = true;
|
||||
pattern = pattern.slice(1);
|
||||
} else if (pattern.startsWith("\\!")) {
|
||||
pattern = pattern.slice(1);
|
||||
}
|
||||
|
||||
if (pattern.startsWith("/")) {
|
||||
pattern = pattern.slice(1);
|
||||
}
|
||||
|
||||
const prefixed = prefix ? `${prefix}${pattern}` : pattern;
|
||||
return negated ? `!${prefixed}` : prefixed;
|
||||
}
|
||||
|
||||
function addIgnoreRules(ig: IgnoreMatcher, dir: string, rootDir: string): void {
|
||||
const relativeDir = relative(rootDir, dir);
|
||||
const prefix = relativeDir ? `${toPosixPath(relativeDir)}/` : "";
|
||||
|
||||
for (const filename of IGNORE_FILE_NAMES) {
|
||||
const ignorePath = join(dir, filename);
|
||||
if (!existsSync(ignorePath)) continue;
|
||||
try {
|
||||
const content = readFileSync(ignorePath, "utf-8");
|
||||
const patterns = content
|
||||
.split(/\r?\n/)
|
||||
.map((line) => prefixIgnorePattern(line, prefix))
|
||||
.filter((line): line is string => Boolean(line));
|
||||
if (patterns.length > 0) {
|
||||
ig.add(patterns);
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
|
||||
export interface SkillFrontmatter {
|
||||
name?: string;
|
||||
description?: string;
|
||||
|
|
@ -96,7 +148,13 @@ export function loadSkillsFromDir(options: LoadSkillsFromDirOptions): LoadSkills
|
|||
return loadSkillsFromDirInternal(dir, source, true);
|
||||
}
|
||||
|
||||
function loadSkillsFromDirInternal(dir: string, source: string, includeRootFiles: boolean): LoadSkillsResult {
|
||||
function loadSkillsFromDirInternal(
|
||||
dir: string,
|
||||
source: string,
|
||||
includeRootFiles: boolean,
|
||||
ignoreMatcher?: IgnoreMatcher,
|
||||
rootDir?: string,
|
||||
): LoadSkillsResult {
|
||||
const skills: Skill[] = [];
|
||||
const diagnostics: ResourceDiagnostic[] = [];
|
||||
|
||||
|
|
@ -104,6 +162,10 @@ function loadSkillsFromDirInternal(dir: string, source: string, includeRootFiles
|
|||
return { skills, diagnostics };
|
||||
}
|
||||
|
||||
const root = rootDir ?? dir;
|
||||
const ig = ignoreMatcher ?? ignore();
|
||||
addIgnoreRules(ig, dir, root);
|
||||
|
||||
try {
|
||||
const entries = readdirSync(dir, { withFileTypes: true });
|
||||
|
||||
|
|
@ -133,8 +195,14 @@ function loadSkillsFromDirInternal(dir: string, source: string, includeRootFiles
|
|||
}
|
||||
}
|
||||
|
||||
const relPath = toPosixPath(relative(root, fullPath));
|
||||
const ignorePath = isDirectory ? `${relPath}/` : relPath;
|
||||
if (ig.ignores(ignorePath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isDirectory) {
|
||||
const subResult = loadSkillsFromDirInternal(fullPath, source, false);
|
||||
const subResult = loadSkillsFromDirInternal(fullPath, source, false, ig, root);
|
||||
skills.push(...subResult.skills);
|
||||
diagnostics.push(...subResult.diagnostics);
|
||||
continue;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue