Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions main.js

Large diffs are not rendered by default.

52 changes: 30 additions & 22 deletions src/scanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { homedir } from "os";
import { parseYaml } from "obsidian";
import { createHash } from "crypto";
import { TOOL_CONFIGS } from "./tool-configs";
import type { SkillItem, SkillPath, SkillType, ChopsSettings, ToolConfig } from "./types";
import type { SkillItem, SkillPath, SkillType, NamingMode, ScanPattern, ChopsSettings, ToolConfig } from "./types";

const IGNORED_FILES = new Set([
"readme.md",
Expand Down Expand Up @@ -48,23 +48,25 @@ function extractName(
frontmatter: Record<string, unknown>,
content: string,
filePath: string,
pattern: ScanPattern
namingMode: NamingMode = "auto"
): string {
if (typeof frontmatter.name === "string" && frontmatter.name) {
return frontmatter.name;
if (namingMode === "auto") {
if (typeof frontmatter.name === "string" && frontmatter.name) {
return frontmatter.name;
}
const h1 = content.match(/^#\s+(.+)$/m);
if (h1) return h1[1].trim();
}
const name = basename(filePath, extname(filePath));
if (name === "SKILL") return basename(join(filePath, ".."));
if (pattern === "flat-md" || pattern === "mdc") return name;
const h1 = content.match(/^#\s+(.+)$/m);
if (h1) return h1[1].trim();
return name;
}

function scanDirectoryWithSkillMd(
baseDir: string,
type: SkillType,
toolId: string
toolId: string,
namingMode: NamingMode = "auto"
): SkillItem[] {
if (!existsSync(baseDir)) return [];
const items: SkillItem[] = [];
Expand All @@ -76,7 +78,7 @@ function scanDirectoryWithSkillMd(
const skillFile = join(fullPath, "SKILL.md");
if (!existsSync(skillFile)) continue;

const item = parseSkillFile(skillFile, type, toolId);
const item = parseSkillFile(skillFile, type, toolId, "directory-with-skillmd", namingMode);
if (item) items.push(item);
}
return items;
Expand All @@ -85,7 +87,8 @@ function scanDirectoryWithSkillMd(
function scanFlatMd(
baseDir: string,
type: SkillType,
toolId: string
toolId: string,
namingMode: NamingMode = "auto"
): SkillItem[] {
if (!existsSync(baseDir)) return [];
const items: SkillItem[] = [];
Expand All @@ -97,7 +100,7 @@ function scanFlatMd(
if (isDir) {
const skillFile = join(fullPath, "SKILL.md");
if (existsSync(skillFile)) {
const item = parseSkillFile(skillFile, type, toolId);
const item = parseSkillFile(skillFile, type, toolId, "flat-md", namingMode);
if (item) items.push(item);
continue;
}
Expand All @@ -112,7 +115,9 @@ function scanFlatMd(
const item = parseSkillFile(
join(fullPath, preferred),
type,
toolId
toolId,
"flat-md",
namingMode
);
if (item) items.push(item);
}
Expand All @@ -121,7 +126,7 @@ function scanFlatMd(

const fname = entry.name.toLowerCase();
if (!fname.endsWith(".md") || IGNORED_FILES.has(fname)) continue;
const item = parseSkillFile(fullPath, type, toolId, "flat-md");
const item = parseSkillFile(fullPath, type, toolId, "flat-md", namingMode);
if (item) items.push(item);
}
return items;
Expand All @@ -130,15 +135,16 @@ function scanFlatMd(
function scanMdc(
baseDir: string,
type: SkillType,
toolId: string
toolId: string,
namingMode: NamingMode = "auto"
): SkillItem[] {
if (!existsSync(baseDir)) return [];
const items: SkillItem[] = [];

for (const entry of readdirSync(baseDir, { withFileTypes: true })) {
if (!entry.name.endsWith(".mdc") && !entry.name.endsWith(".md")) continue;
if (entry.isDirectory()) continue;
const item = parseSkillFile(join(baseDir, entry.name), type, toolId, "mdc");
const item = parseSkillFile(join(baseDir, entry.name), type, toolId, "mdc", namingMode);
if (item) items.push(item);
}
return items;
Expand All @@ -148,13 +154,14 @@ function parseSkillFile(
filePath: string,
type: SkillType,
toolId: string,
pattern: ScanPattern = "directory-with-skillmd"
pattern: ScanPattern = "directory-with-skillmd",
namingMode: NamingMode = "auto"
): SkillItem | null {
try {
const raw = readFileSync(filePath, "utf-8");
const stat = statSync(filePath);
const { frontmatter, content } = parseFrontmatter(raw);
const name = extractName(frontmatter, content, filePath, pattern);
const name = extractName(frontmatter, content, filePath, namingMode);
const description =
typeof frontmatter.description === "string"
? frontmatter.description
Expand Down Expand Up @@ -188,14 +195,14 @@ function parseSkillFile(
}
}

function scanPath(sp: SkillPath, toolId: string): SkillItem[] {
function scanPath(sp: SkillPath, toolId: string, namingMode: NamingMode = "auto"): SkillItem[] {
switch (sp.pattern) {
case "directory-with-skillmd":
return scanDirectoryWithSkillMd(sp.baseDir, sp.type, toolId);
return scanDirectoryWithSkillMd(sp.baseDir, sp.type, toolId, namingMode);
case "flat-md":
return scanFlatMd(sp.baseDir, sp.type, toolId);
return scanFlatMd(sp.baseDir, sp.type, toolId, namingMode);
case "mdc":
return scanMdc(sp.baseDir, sp.type, toolId);
return scanMdc(sp.baseDir, sp.type, toolId, namingMode);
}
}

Expand Down Expand Up @@ -276,6 +283,7 @@ export function getProjectName(filePath: string, projectsHomeDir: string): strin
}

export function scanAll(settings: ChopsSettings): Map<string, SkillItem> {
const namingMode = settings.namingMode || "auto";
const items = new Map<string, SkillItem>();
const nameMap = new Map<string, string>();

Expand Down Expand Up @@ -314,7 +322,7 @@ export function scanAll(settings: ChopsSettings): Map<string, SkillItem> {

const allPaths = [...tool.paths, ...tool.agentPaths];
for (const sp of allPaths) {
for (const item of scanPath(sp, tool.id)) {
for (const item of scanPath(sp, tool.id, namingMode)) {
addItem(item, tool.id);
}
}
Expand Down
17 changes: 17 additions & 0 deletions src/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,23 @@ export class AgentfilesSettingTab extends PluginSettingTab {
})
);

new Setting(containerEl)
.setName("Display names")
.setDesc("How skill and command names are displayed in the list")
.addDropdown((drop) =>
drop
.addOptions({
auto: "Auto (frontmatter / heading / filename)",
filename: "Filename only",
})
.setValue(this.plugin.settings.namingMode || "auto")
.onChange(async (value) => {
this.plugin.settings.namingMode = value as "auto" | "filename";
await this.plugin.saveSettings();
this.plugin.refreshStore();
})
);

new Setting(containerEl).setName("Marketplace").setHeading();

new Setting(containerEl)
Expand Down
4 changes: 4 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,16 @@ export type SidebarFilter =
| { kind: "collection"; name: string }
| { kind: "project"; project: string };

export type NamingMode = "auto" | "filename";

export interface ChopsSettings {
tools: Record<string, { enabled: boolean; customPaths: string[] }>;
watchEnabled: boolean;
watchDebounceMs: number;
favorites: string[];
collections: Record<string, string[]>;
customScanPaths: string[];
namingMode: NamingMode;
projectScanEnabled: boolean;
projectsHomeDir: string;
packageRunner: "auto" | "npx" | "bunx";
Expand All @@ -85,6 +88,7 @@ export const DEFAULT_SETTINGS: ChopsSettings = {
favorites: [],
collections: {},
customScanPaths: [],
namingMode: "auto",
projectScanEnabled: true,
projectsHomeDir: "",
packageRunner: "auto",
Expand Down
5 changes: 3 additions & 2 deletions styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -165,15 +165,16 @@

.as-skill-header {
display: flex;
align-items: center;
gap: 4px;
align-items: baseline;
gap: 6px;
}

.as-skill-name {
font-size: var(--font-ui-small);
font-weight: 500;
color: var(--text-normal);
flex: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
Expand Down