fix(coding-agent): tighten skill discovery and edit diffs closes #2603
This commit is contained in:
@@ -249,9 +249,11 @@ function collectFiles(
|
||||
return files;
|
||||
}
|
||||
|
||||
type SkillDiscoveryMode = "pi" | "agents";
|
||||
|
||||
function collectSkillEntries(
|
||||
dir: string,
|
||||
includeRootFiles = true,
|
||||
mode: SkillDiscoveryMode,
|
||||
ignoreMatcher?: IgnoreMatcher,
|
||||
rootDir?: string,
|
||||
): string[] {
|
||||
@@ -264,6 +266,29 @@ function collectSkillEntries(
|
||||
|
||||
try {
|
||||
const dirEntries = readdirSync(dir, { withFileTypes: true });
|
||||
|
||||
for (const entry of dirEntries) {
|
||||
if (entry.name !== "SKILL.md") {
|
||||
continue;
|
||||
}
|
||||
|
||||
const fullPath = join(dir, entry.name);
|
||||
let isFile = entry.isFile();
|
||||
if (entry.isSymbolicLink()) {
|
||||
try {
|
||||
isFile = statSync(fullPath).isFile();
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const relPath = toPosixPath(relative(root, fullPath));
|
||||
if (isFile && !ig.ignores(relPath)) {
|
||||
entries.push(fullPath);
|
||||
return entries;
|
||||
}
|
||||
}
|
||||
|
||||
for (const entry of dirEntries) {
|
||||
if (entry.name.startsWith(".")) continue;
|
||||
if (entry.name === "node_modules") continue;
|
||||
@@ -283,18 +308,15 @@ function collectSkillEntries(
|
||||
}
|
||||
|
||||
const relPath = toPosixPath(relative(root, fullPath));
|
||||
const ignorePath = isDir ? `${relPath}/` : relPath;
|
||||
if (ig.ignores(ignorePath)) continue;
|
||||
|
||||
if (isDir) {
|
||||
entries.push(...collectSkillEntries(fullPath, false, ig, root));
|
||||
} else if (isFile) {
|
||||
const isRootMd = includeRootFiles && entry.name.endsWith(".md");
|
||||
const isSkillMd = !includeRootFiles && entry.name === "SKILL.md";
|
||||
if (isRootMd || isSkillMd) {
|
||||
entries.push(fullPath);
|
||||
}
|
||||
if (mode === "pi" && dir === root && isFile && entry.name.endsWith(".md") && !ig.ignores(relPath)) {
|
||||
entries.push(fullPath);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isDir) continue;
|
||||
if (ig.ignores(`${relPath}/`)) continue;
|
||||
|
||||
entries.push(...collectSkillEntries(fullPath, mode, ig, root));
|
||||
}
|
||||
} catch {
|
||||
// Ignore errors
|
||||
@@ -303,8 +325,8 @@ function collectSkillEntries(
|
||||
return entries;
|
||||
}
|
||||
|
||||
function collectAutoSkillEntries(dir: string, includeRootFiles = true): string[] {
|
||||
return collectSkillEntries(dir, includeRootFiles);
|
||||
function collectAutoSkillEntries(dir: string, mode: SkillDiscoveryMode): string[] {
|
||||
return collectSkillEntries(dir, mode);
|
||||
}
|
||||
|
||||
function findGitRepoRoot(startDir: string): string | null {
|
||||
@@ -516,7 +538,7 @@ function collectAutoExtensionEntries(dir: string): string[] {
|
||||
*/
|
||||
function collectResourceFiles(dir: string, resourceType: ResourceType): string[] {
|
||||
if (resourceType === "skills") {
|
||||
return collectSkillEntries(dir);
|
||||
return collectSkillEntries(dir, "pi");
|
||||
}
|
||||
if (resourceType === "extensions") {
|
||||
return collectAutoExtensionEntries(dir);
|
||||
@@ -1964,8 +1986,8 @@ export class DefaultPackageManager implements PackageManager {
|
||||
addResources(
|
||||
"skills",
|
||||
[
|
||||
...collectAutoSkillEntries(projectDirs.skills),
|
||||
...projectAgentsSkillDirs.flatMap((dir) => collectAutoSkillEntries(dir)),
|
||||
...collectAutoSkillEntries(projectDirs.skills, "pi"),
|
||||
...projectAgentsSkillDirs.flatMap((dir) => collectAutoSkillEntries(dir, "agents")),
|
||||
],
|
||||
projectMetadata,
|
||||
projectOverrides.skills,
|
||||
@@ -1995,7 +2017,7 @@ export class DefaultPackageManager implements PackageManager {
|
||||
);
|
||||
addResources(
|
||||
"skills",
|
||||
[...collectAutoSkillEntries(userDirs.skills), ...collectAutoSkillEntries(userAgentsSkillsDir)],
|
||||
[...collectAutoSkillEntries(userDirs.skills, "pi"), ...collectAutoSkillEntries(userAgentsSkillsDir, "agents")],
|
||||
userMetadata,
|
||||
userOverrides.skills,
|
||||
globalBaseDir,
|
||||
|
||||
@@ -311,46 +311,69 @@ export function generateDiffString(
|
||||
} else {
|
||||
// Context lines - only show a few before/after changes
|
||||
const nextPartIsChange = i < parts.length - 1 && (parts[i + 1].added || parts[i + 1].removed);
|
||||
const hasLeadingChange = lastWasChange;
|
||||
const hasTrailingChange = nextPartIsChange;
|
||||
|
||||
if (lastWasChange || nextPartIsChange) {
|
||||
// Show context
|
||||
let linesToShow = raw;
|
||||
let skipStart = 0;
|
||||
let skipEnd = 0;
|
||||
if (hasLeadingChange && hasTrailingChange) {
|
||||
if (raw.length <= contextLines * 2) {
|
||||
for (const line of raw) {
|
||||
const lineNum = String(oldLineNum).padStart(lineNumWidth, " ");
|
||||
output.push(` ${lineNum} ${line}`);
|
||||
oldLineNum++;
|
||||
newLineNum++;
|
||||
}
|
||||
} else {
|
||||
const leadingLines = raw.slice(0, contextLines);
|
||||
const trailingLines = raw.slice(raw.length - contextLines);
|
||||
const skippedLines = raw.length - leadingLines.length - trailingLines.length;
|
||||
|
||||
if (!lastWasChange) {
|
||||
// Show only last N lines as leading context
|
||||
skipStart = Math.max(0, raw.length - contextLines);
|
||||
linesToShow = raw.slice(skipStart);
|
||||
}
|
||||
for (const line of leadingLines) {
|
||||
const lineNum = String(oldLineNum).padStart(lineNumWidth, " ");
|
||||
output.push(` ${lineNum} ${line}`);
|
||||
oldLineNum++;
|
||||
newLineNum++;
|
||||
}
|
||||
|
||||
if (!nextPartIsChange && linesToShow.length > contextLines) {
|
||||
// Show only first N lines as trailing context
|
||||
skipEnd = linesToShow.length - contextLines;
|
||||
linesToShow = linesToShow.slice(0, contextLines);
|
||||
}
|
||||
|
||||
// Add ellipsis if we skipped lines at start
|
||||
if (skipStart > 0) {
|
||||
output.push(` ${"".padStart(lineNumWidth, " ")} ...`);
|
||||
// Update line numbers for the skipped leading context
|
||||
oldLineNum += skipStart;
|
||||
newLineNum += skipStart;
|
||||
}
|
||||
oldLineNum += skippedLines;
|
||||
newLineNum += skippedLines;
|
||||
|
||||
for (const line of linesToShow) {
|
||||
for (const line of trailingLines) {
|
||||
const lineNum = String(oldLineNum).padStart(lineNumWidth, " ");
|
||||
output.push(` ${lineNum} ${line}`);
|
||||
oldLineNum++;
|
||||
newLineNum++;
|
||||
}
|
||||
}
|
||||
} else if (hasLeadingChange) {
|
||||
const shownLines = raw.slice(0, contextLines);
|
||||
const skippedLines = raw.length - shownLines.length;
|
||||
|
||||
for (const line of shownLines) {
|
||||
const lineNum = String(oldLineNum).padStart(lineNumWidth, " ");
|
||||
output.push(` ${lineNum} ${line}`);
|
||||
oldLineNum++;
|
||||
newLineNum++;
|
||||
}
|
||||
|
||||
// Add ellipsis if we skipped lines at end
|
||||
if (skipEnd > 0) {
|
||||
if (skippedLines > 0) {
|
||||
output.push(` ${"".padStart(lineNumWidth, " ")} ...`);
|
||||
// Update line numbers for the skipped trailing context
|
||||
oldLineNum += skipEnd;
|
||||
newLineNum += skipEnd;
|
||||
oldLineNum += skippedLines;
|
||||
newLineNum += skippedLines;
|
||||
}
|
||||
} else if (hasTrailingChange) {
|
||||
const skippedLines = Math.max(0, raw.length - contextLines);
|
||||
if (skippedLines > 0) {
|
||||
output.push(` ${"".padStart(lineNumWidth, " ")} ...`);
|
||||
oldLineNum += skippedLines;
|
||||
newLineNum += skippedLines;
|
||||
}
|
||||
|
||||
for (const line of raw.slice(skippedLines)) {
|
||||
const lineNum = String(oldLineNum).padStart(lineNumWidth, " ");
|
||||
output.push(` ${lineNum} ${line}`);
|
||||
oldLineNum++;
|
||||
newLineNum++;
|
||||
}
|
||||
} else {
|
||||
// Skip these context lines entirely
|
||||
|
||||
Reference in New Issue
Block a user