mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-15 10:05:14 +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
|
|
@ -2,6 +2,10 @@
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- Added Claude Opus 4.6 model to the generated model catalog
|
||||
|
||||
## [0.51.6] - 2026-02-04
|
||||
|
||||
### Fixed
|
||||
|
|
|
|||
|
|
@ -2,10 +2,19 @@
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
### New Features
|
||||
|
||||
- SSH URL support for git packages. See [docs/packages.md](docs/packages.md).
|
||||
- `auth.json` API keys now support shell command resolution (`!command`) and environment variable lookup. See [docs/providers.md](docs/providers.md).
|
||||
- Model selectors now display the selected model name.
|
||||
|
||||
### Added
|
||||
|
||||
- API keys in `auth.json` now support shell command resolution (`!command`) and environment variable lookup, matching the behavior in `models.json`
|
||||
- Added `minimal-mode.ts` example extension demonstrating how to override built-in tool rendering for a minimal display mode
|
||||
- Added Claude Opus 4.6 model to the model catalog
|
||||
- Added SSH URL support for git packages ([#1287](https://github.com/badlogic/pi-mono/pull/1287) by [@markusn](https://github.com/markusn))
|
||||
- Model selectors now display the selected model name ([#1275](https://github.com/badlogic/pi-mono/pull/1275) by [@haoqixu](https://github.com/haoqixu))
|
||||
|
||||
### Fixed
|
||||
|
||||
|
|
@ -13,6 +22,8 @@
|
|||
- Fixed images being silently dropped when `prompt()` is called with both `images` and `streamingBehavior` during streaming. `steer()`, `followUp()`, and the corresponding RPC commands now accept optional images. ([#1271](https://github.com/badlogic/pi-mono/pull/1271) by [@aliou](https://github.com/aliou))
|
||||
- CLI `--help`, `--version`, `--list-models`, and `--export` now exit even if extensions keep the event loop alive ([#1285](https://github.com/badlogic/pi-mono/pull/1285) by [@ferologics](https://github.com/ferologics))
|
||||
- Fixed crash when models send malformed tool arguments (objects instead of strings) ([#1259](https://github.com/badlogic/pi-mono/issues/1259))
|
||||
- Fixed custom message expand state not being respected ([#1258](https://github.com/badlogic/pi-mono/pull/1258) by [@Gurpartap](https://github.com/Gurpartap))
|
||||
- Fixed skill loader to respect .gitignore, .ignore, and .fdignore when scanning directories
|
||||
|
||||
## [0.51.6] - 2026-02-04
|
||||
|
||||
|
|
|
|||
|
|
@ -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