ccexp
Version:
CLI tool for exploring and managing Claude Code settings and slash commands
1,463 lines (1,452 loc) • 120 kB
JavaScript
#!/usr/bin/env node
import { __toESM, require_usingCtx } from "./usingCtx-BLgT6AjQ.js";
import { existsSync, readFileSync } from "node:fs";
import { basename, dirname, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { program } from "commander";
import { Box, Static, Text, render, useInput, useStdout } from "ink";
import { z } from "zod/v4";
import { Badge, ConfirmInput, Spinner, StatusMessage, ThemeProvider, defaultTheme, extendTheme } from "@inkjs/ui";
import React, { Component, useCallback, useEffect, useMemo, useRef, useState } from "react";
import { isError } from "es-toolkit/predicate";
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
import { groupBy, uniq } from "es-toolkit/array";
import { filter, truncate, values } from "es-toolkit/compat";
import { merge } from "es-toolkit/object";
import { open, readFile, readdir, stat } from "node:fs/promises";
import { homedir } from "node:os";
import { P, match } from "ts-pattern";
import { fdir } from "fdir";
import matter from "gray-matter";
import clipboardy from "clipboardy";
import open$1 from "open";
import openEditor from "open-editor";
import { clamp } from "es-toolkit/math";
import BigText from "ink-big-text";
import Gradient from "ink-gradient";
import { marked } from "marked";
import { markedTerminal } from "marked-terminal";
//#region src/styles/theme.ts
const theme = {
selection: {
backgroundColor: "cyan",
color: "black"
},
fileTypes: {
projectMemory: "#FF8A65",
projectMemoryLocal: "#FFAB91",
userMemory: "#FF8A65",
projectSettings: "#4DD0E1",
projectSettingsLocal: "#80DEEA",
userSettings: "#4DD0E1",
projectCommand: "#66BB6A",
personalCommand: "#66BB6A",
projectSubagent: "#C47FD5",
userSubagent: "#C47FD5",
unknown: "gray"
},
status: {
error: "red",
success: "green",
warning: "yellow",
info: "cyan"
},
ui: {
focus: "white",
appTitle: "blue",
spinner: "yellow",
sectionTitle: "cyan"
}
};
//#endregion
//#region src/components/ErrorBoundary.tsx
var ErrorBoundary = class extends Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
const errorObject = isError(error) ? error : new Error(String(error || "Unknown error"));
return {
hasError: true,
error: errorObject
};
}
componentDidCatch(error, errorInfo) {
console.error("Error caught by boundary:", error, errorInfo);
}
render() {
if (this.state.hasError) {
if (this.props.fallback) return this.props.fallback;
return /* @__PURE__ */ jsxs(Box, {
flexDirection: "column",
padding: 1,
children: [
/* @__PURE__ */ jsx(Text, {
color: theme.status.error,
bold: true,
children: "Something went wrong"
}),
/* @__PURE__ */ jsx(Text, {
color: theme.status.error,
children: this.state.error?.message || "An unexpected error occurred"
}),
/* @__PURE__ */ jsx(Text, {
dimColor: true,
children: "Please try again or check the console for more details"
})
]
});
}
return this.props.children;
}
};
//#endregion
//#region src/_consts.ts
const CLAUDE_FILE_PATTERNS = {
CLAUDE_MD: "**/CLAUDE.md",
CLAUDE_LOCAL_MD: "**/CLAUDE.local.md",
GLOBAL_CLAUDE_MD: join(homedir(), ".claude", "CLAUDE.md"),
PROJECT_SLASH_COMMANDS: "**/.claude/commands/**/*.md",
USER_SLASH_COMMANDS: join(homedir(), ".claude", "commands", "**", "*.md")
};
const FILE_SIZE_LIMITS = {
MAX_CLAUDE_MD_SIZE: 1024 * 1024,
MAX_SLASH_COMMAND_SIZE: 512 * 1024,
MAX_SUBAGENT_SIZE: 1024 * 100
};
if (import.meta.vitest != null) {
const { describe, test, expect } = import.meta.vitest;
describe("CLAUDE_FILE_PATTERNS", () => {
test("should contain expected patterns", () => {
expect(CLAUDE_FILE_PATTERNS.CLAUDE_MD).toBe("**/CLAUDE.md");
expect(CLAUDE_FILE_PATTERNS.CLAUDE_LOCAL_MD).toBe("**/CLAUDE.local.md");
expect(CLAUDE_FILE_PATTERNS.PROJECT_SLASH_COMMANDS).toBe("**/.claude/commands/**/*.md");
});
test("should include home directory paths", () => {
expect(CLAUDE_FILE_PATTERNS.GLOBAL_CLAUDE_MD).toContain(".claude");
expect(CLAUDE_FILE_PATTERNS.USER_SLASH_COMMANDS).toContain(".claude/commands");
});
});
describe("FILE_SIZE_LIMITS", () => {
test("should have reasonable size limits", () => {
expect(FILE_SIZE_LIMITS.MAX_CLAUDE_MD_SIZE).toBe(1024 * 1024);
expect(FILE_SIZE_LIMITS.MAX_SLASH_COMMAND_SIZE).toBe(512 * 1024);
});
});
}
//#endregion
//#region src/_types.ts
const createClaudeFilePath = (path) => {
if (path.length === 0) throw new Error("Path must not be empty");
return path;
};
if (import.meta.vitest != null) {
const { describe, test, expect } = import.meta.vitest;
describe("createClaudeFilePath", () => {
test("should create branded ClaudeFilePath for valid paths", () => {
const validPaths = [
"/test/CLAUDE.md",
"~/CLAUDE.md",
"./src/file.md",
"file.md"
];
for (const path of validPaths) {
const claudePath = createClaudeFilePath(path);
expect(claudePath).toBe(path);
expect(typeof claudePath).toBe("string");
}
});
test("should throw for invalid paths", () => {
expect(() => createClaudeFilePath("")).toThrow();
});
});
}
//#endregion
//#region src/_utils.ts
var import_usingCtx$3 = __toESM(require_usingCtx(), 1);
const HOME_DIR = homedir();
const parseSlashCommandName = (fileName) => {
return fileName.replace(/\.md$/, "").replace(/\//g, ":");
};
const normalizeFilePath = (filePath) => {
const normalized = filePath.startsWith("~") ? filePath.replace("~", HOME_DIR) : filePath;
try {
return createClaudeFilePath(normalized);
} catch {
throw new Error(`Invalid file path: ${filePath}`);
}
};
const getFileScope = (filePath) => {
return filePath.includes(HOME_DIR) ? "user" : "project";
};
const detectClaudeFileType = (filePath) => {
const fileName = basename(filePath);
const dirPath = dirname(filePath);
return match([fileName, dirPath]).with(["CLAUDE.md", P.when((dir) => dir === join(HOME_DIR, ".claude"))], () => "user-memory").with(["CLAUDE.md", P._], () => "project-memory").with(["CLAUDE.local.md", P._], () => "project-memory-local").with([P.when((name) => name.endsWith(".md")), P.when((dir) => dir.includes(join(HOME_DIR, ".claude", "commands")))], () => "personal-command").with([P.when((name) => name.endsWith(".md")), P.when((dir) => dir.includes(".claude/commands"))], () => "project-command").with([P.when((name) => name.endsWith(".md")), P.when((dir) => dir.includes(join(HOME_DIR, ".claude", "agents")))], () => "user-subagent").with([P.when((name) => name.endsWith(".md")), P.when((dir) => dir.includes(".claude/agents"))], () => "project-subagent").with(["settings.json", P.when((dir) => dir === join(HOME_DIR, ".claude"))], () => "user-settings").with(["settings.json", P.when((dir) => dir.endsWith("/.claude") || dir.includes("/.claude/"))], () => "project-settings").with(["settings.local.json", P.when((dir) => dir.endsWith("/.claude") || dir.includes("/.claude/"))], () => "project-settings-local").otherwise(() => "unknown");
};
const validateClaudeMdContent = (content) => {
return content.length >= 0;
};
const extractTagsFromContent = (content) => {
const tagPattern = /#(\w+)/g;
const matches = content.match(tagPattern);
return matches ? matches.map((tag) => tag.slice(1)) : [];
};
const extractCommandsFromContent = (content) => {
const commandPattern = /\/(\w+)(?:\s+(.+?))?$/gm;
const commands = [];
let match$1 = commandPattern.exec(content);
while (match$1 !== null) {
const [, name, description] = match$1;
if (!name) {
match$1 = commandPattern.exec(content);
continue;
}
commands.push({
name,
description: description?.trim(),
hasArguments: Boolean(description?.includes("<") || description?.includes("["))
});
match$1 = commandPattern.exec(content);
}
return commands;
};
const isBinaryFile = async (filePath) => {
try {
const { readFile: readFile$1 } = await import("node:fs/promises");
const buffer = await readFile$1(filePath);
const sampleSize = Math.min(1024, buffer.length);
const sample = buffer.subarray(0, sampleSize);
return sample.includes(0);
} catch {
return false;
}
};
if (import.meta.vitest != null) {
const { describe, test, expect } = import.meta.vitest;
const { createClaudeProjectFixture, testWithFixture } = await import("./test-fixture-helpers-jC6qse8O.js");
describe("parseSlashCommandName", () => {
test("should convert file path to command name", () => {
expect(parseSlashCommandName("deploy.md")).toBe("deploy");
expect(parseSlashCommandName("frontend/component.md")).toBe("frontend:component");
});
test("should handle nested paths correctly", () => {
expect(parseSlashCommandName("git/commit.md")).toBe("git:commit");
expect(parseSlashCommandName("project/test/unit.md")).toBe("project:test:unit");
});
});
describe("validateClaudeMdContent", () => {
test("should validate valid CLAUDE.md content", () => {
expect(validateClaudeMdContent("# Project Info\n## Setup")).toBe(true);
expect(validateClaudeMdContent("## Build Commands")).toBe(true);
});
test("should accept any reasonable content", () => {
expect(validateClaudeMdContent("Just plain text")).toBe(true);
expect(validateClaudeMdContent("")).toBe(true);
expect(validateClaudeMdContent("- bullet point\n- another")).toBe(true);
});
test("should accept any content size", () => {
const largeContent = "x".repeat(1e6);
expect(validateClaudeMdContent(largeContent)).toBe(true);
});
});
describe("detectClaudeFileType", () => {
test("should detect CLAUDE.md files", () => {
expect(detectClaudeFileType("/project/CLAUDE.md")).toBe("project-memory");
});
test("should detect CLAUDE.local.md files", () => {
expect(detectClaudeFileType("/project/CLAUDE.local.md")).toBe("project-memory-local");
});
test("should detect global CLAUDE.md files", () => {
const globalPath = join(HOME_DIR, ".claude", "CLAUDE.md");
expect(detectClaudeFileType(globalPath)).toBe("user-memory");
});
test("should detect project slash command files", () => {
expect(detectClaudeFileType("/project/.claude/commands/deploy.md")).toBe("project-command");
expect(detectClaudeFileType("/workspace/.claude/commands/test.md")).toBe("project-command");
});
test("should detect personal slash command files", () => {
const personalCommandPath = join(HOME_DIR, ".claude", "commands", "personal.md");
expect(detectClaudeFileType(personalCommandPath)).toBe("personal-command");
const nestedPersonalPath = join(HOME_DIR, ".claude", "commands", "git", "commit.md");
expect(detectClaudeFileType(nestedPersonalPath)).toBe("personal-command");
});
test.each([
["/project/.claude/settings.json", "project-settings"],
["/workspace/.claude/settings.json", "project-settings"],
["/project/.claude/settings.local.json", "project-settings-local"],
["/workspace/.claude/settings.local.json", "project-settings-local"]
])("should detect %s as %s", (path, expectedType) => {
expect(detectClaudeFileType(path)).toBe(expectedType);
});
test("should detect user settings.json", () => {
const userSettingsPath = join(HOME_DIR, ".claude", "settings.json");
expect(detectClaudeFileType(userSettingsPath)).toBe("user-settings");
});
test("should detect project subagent files", () => {
expect(detectClaudeFileType("/project/.claude/agents/test-agent.md")).toBe("project-subagent");
expect(detectClaudeFileType("/workspace/.claude/agents/helper.md")).toBe("project-subagent");
});
test("should detect user subagent files", () => {
const userAgentPath = join(HOME_DIR, ".claude", "agents", "personal-agent.md");
expect(detectClaudeFileType(userAgentPath)).toBe("user-subagent");
});
test("should not detect settings files outside .claude", () => {
expect(detectClaudeFileType("/project/settings.json")).toBe("unknown");
expect(detectClaudeFileType("/project/settings.local.json")).toBe("unknown");
});
});
describe("extractTagsFromContent", () => {
test("should extract hashtags from content", () => {
const content = "This is #typescript and #nextjs project";
expect(extractTagsFromContent(content)).toEqual(["typescript", "nextjs"]);
});
test("should return empty array for no tags", () => {
expect(extractTagsFromContent("No tags here")).toEqual([]);
});
});
describe("extractCommandsFromContent", () => {
test("should extract slash commands", () => {
const content = "/deploy <environment>\n/test --watch";
const commands = extractCommandsFromContent(content);
expect(commands).toHaveLength(2);
expect(commands[0]?.name).toBe("deploy");
expect(commands[0]?.hasArguments).toBe(true);
});
});
describe("getFileScope", () => {
test("should detect user scope for home directory files", () => {
expect(getFileScope(`${HOME_DIR}/.claude/CLAUDE.md`)).toBe("user");
});
test("should detect project scope for non-home files", () => {
expect(getFileScope("/project/CLAUDE.md")).toBe("project");
});
});
describe("normalizeFilePath", () => {
test("should expand ~ to home directory", () => {
expect(normalizeFilePath("~/test.md")).toBe(`${HOME_DIR}/test.md`);
expect(normalizeFilePath("~/.claude/CLAUDE.md")).toBe(`${HOME_DIR}/.claude/CLAUDE.md`);
});
test("should handle absolute paths unchanged", () => {
expect(normalizeFilePath("/absolute/path/file.md")).toBe("/absolute/path/file.md");
expect(normalizeFilePath("/Users/test/CLAUDE.md")).toBe("/Users/test/CLAUDE.md");
});
test("should handle relative paths unchanged", () => {
expect(normalizeFilePath("./relative/path.md")).toBe("./relative/path.md");
expect(normalizeFilePath("../parent/file.md")).toBe("../parent/file.md");
expect(normalizeFilePath("src/file.md")).toBe("src/file.md");
});
test("should handle paths without ~ unchanged", () => {
expect(normalizeFilePath("simple.md")).toBe("simple.md");
expect(normalizeFilePath("folder/file.md")).toBe("folder/file.md");
});
test("should throw error for invalid file paths", () => {
expect(() => normalizeFilePath("")).toThrow();
});
});
describe("isBinaryFile", () => {
test("should detect text files as non-binary", async () => {
await testWithFixture({
"test.txt": "Hello world\nThis is a text file",
"README.md": "# Project\n\nThis is markdown",
"config.json": JSON.stringify({ key: "value" }, null, 2)
}, async (f) => {
const textResult = await isBinaryFile(f.getPath("test.txt"));
expect(textResult).toBe(false);
const mdResult = await isBinaryFile(f.getPath("README.md"));
expect(mdResult).toBe(false);
const jsonResult = await isBinaryFile(f.getPath("config.json"));
expect(jsonResult).toBe(false);
});
});
test("should detect binary files with null bytes", async () => {
try {
var _usingCtx = (0, import_usingCtx$3.default)();
const { createFixture } = await import("./dist-D99sJLU7.js");
const { writeFile } = await import("node:fs/promises");
const fixture = _usingCtx.a(await createFixture({
"image.png": "",
"binary.dat": ""
}));
const pngData = Buffer.from([
137,
80,
78,
71,
13,
10,
26,
10,
0,
0
]);
const binData = Buffer.from([
0,
1,
2,
0,
4
]);
await writeFile(fixture.getPath("image.png"), pngData);
await writeFile(fixture.getPath("binary.dat"), binData);
const pngResult = await isBinaryFile(fixture.getPath("image.png"));
expect(pngResult).toBe(true);
const datResult = await isBinaryFile(fixture.getPath("binary.dat"));
expect(datResult).toBe(true);
} catch (_) {
_usingCtx.e = _;
} finally {
await _usingCtx.d();
}
});
test("should handle non-existent files gracefully", async () => {
const result = await isBinaryFile("/non/existent/file.txt");
expect(result).toBe(false);
});
test("should handle permission errors gracefully", async () => {
await testWithFixture({ "protected.txt": "Protected content" }, async (f) => {
const { chmod } = await import("node:fs/promises");
const filePath = f.getPath("protected.txt");
await chmod(filePath, 0);
try {
const result = await isBinaryFile(filePath);
expect(result).toBe(false);
} finally {
await chmod(filePath, 420).catch(() => {});
}
});
});
});
describe("validateClaudeMdContent with real files", () => {
test("should validate actual CLAUDE.md files", async () => {
try {
var _usingCtx3 = (0, import_usingCtx$3.default)();
const fixture = _usingCtx3.a(await createClaudeProjectFixture({ projectName: "validate-test" }));
const { readFile: readFile$1 } = await import("node:fs/promises");
const content = await readFile$1(fixture.getPath("validate-test/CLAUDE.md"), "utf-8");
expect(validateClaudeMdContent(content)).toBe(true);
} catch (_) {
_usingCtx3.e = _;
} finally {
await _usingCtx3.d();
}
});
});
describe("extractCommandsFromContent with real files", () => {
test("should extract commands from slash command files", async () => {
await testWithFixture({ ".claude": { commands: {
"deploy.md": "# Deploy Command\n\n/deploy <environment>\n\nDeploys to specified environment",
"test.md": "/test [--watch] [--coverage]\n\nRuns tests with optional flags",
"lint.md": "/lint\n\nRuns linting checks"
} } }, async (f) => {
const { readFile: readFile$1 } = await import("node:fs/promises");
const deployContent = await readFile$1(f.getPath(".claude/commands/deploy.md"), "utf-8");
const deployCommands = extractCommandsFromContent(deployContent);
expect(deployCommands).toHaveLength(1);
expect(deployCommands[0]?.name).toBe("deploy");
expect(deployCommands[0]?.hasArguments).toBe(true);
const testContent = await readFile$1(f.getPath(".claude/commands/test.md"), "utf-8");
const testCommands = extractCommandsFromContent(testContent);
expect(testCommands).toHaveLength(1);
expect(testCommands[0]?.name).toBe("test");
expect(testCommands[0]?.hasArguments).toBe(true);
const lintContent = await readFile$1(f.getPath(".claude/commands/lint.md"), "utf-8");
const lintCommands = extractCommandsFromContent(lintContent);
expect(lintCommands).toHaveLength(1);
expect(lintCommands[0]?.name).toBe("lint");
expect(lintCommands[0]?.hasArguments).toBe(false);
});
});
});
}
//#endregion
//#region src/base-file-scanner.ts
var BaseFileScanner = class {
async processFile(filePath) {
try {
if (!existsSync(filePath)) return null;
const stats = await stat(filePath);
if (stats.size > this.maxFileSize) {
console.warn(`${this.fileType} file too large, skipping: ${filePath}`);
return null;
}
const content = await readFile(filePath, "utf-8");
return await this.parseContent(filePath, content, stats);
} catch (error) {
console.warn(`Failed to process ${this.fileType} file ${filePath}:`, error);
return null;
}
}
};
if (import.meta.vitest != null) {
const { describe, test, expect, vi } = import.meta.vitest;
class TestScanner extends BaseFileScanner {
maxFileSize = 1024;
fileType = "test";
async parseContent(_filePath, content, _stats) {
if (content.trim() === "") return null;
return { data: content };
}
}
describe("BaseFileScanner", () => {
test("returns null for non-existent files", async () => {
const scanner$3 = new TestScanner();
const result = await scanner$3.processFile("/non/existent/file.txt");
expect(result).toBeNull();
});
test("logs warning for files exceeding size limit", async () => {
const consoleWarnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
const scanner$3 = new TestScanner();
await scanner$3.processFile("/large/file.txt");
consoleWarnSpy.mockRestore();
});
test("handles errors gracefully", async () => {
const consoleWarnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
const scanner$3 = new TestScanner();
await scanner$3.processFile("/invalid/path");
consoleWarnSpy.mockRestore();
});
});
}
//#endregion
//#region src/scan-exclusions.ts
/**
* Comprehensive exclusion patterns for secure and performant file scanning
*/
const SECURITY_EXCLUSIONS = [
".ssh",
".gnupg",
".gpg",
".pki",
".aws",
".azure",
".gcp",
".kube",
".docker",
"credentials",
"secrets",
".mozilla",
".chrome",
".chromium",
"Library/Application Support/Google/Chrome",
"Library/Application Support/Firefox",
"Library/Keychains",
".password-store",
".pass",
".certificates",
"cert",
"certs",
".openvpn",
".wireguard"
];
const DEVELOPMENT_EXCLUSIONS = [
"node_modules",
"vendor",
"bower_components",
"jspm_packages",
".pnpm",
".yarn",
"dist",
"build",
"out",
"target",
".next",
".nuxt",
".vuepress",
".docusaurus",
".cache",
".tmp",
"tmp",
"temp",
".git",
".svn",
".hg",
".bzr",
".vscode",
".idea",
".vs",
".eclipse",
".netbeans",
"__pycache__",
".pytest_cache",
"venv",
".venv",
"env",
".env",
"site-packages",
".bundle",
"gems",
"target",
"gradle",
".gradle",
"bin",
"obj",
"packages",
"vendor",
"vendor",
"coverage",
".nyc_output",
".coverage",
"htmlcov",
"_site",
"public",
".DS_Store",
"Thumbs.db",
"desktop.ini"
];
const MEDIA_EXCLUSIONS = [
"images",
"imgs",
"photos",
"videos",
"movies",
"music",
"audio",
"sounds",
"Downloads",
"Desktop",
"Documents/VirtualMachines",
"Library/Caches",
"Library/Logs",
"AppData",
"Application Support",
"VirtualBox VMs",
".vagrant",
"parallels",
"vmware",
"Dropbox",
"Google Drive",
"OneDrive",
"iCloud Drive"
];
const DEFAULT_EXCLUSIONS = [...SECURITY_EXCLUSIONS, ...DEVELOPMENT_EXCLUSIONS];
const CONSERVATIVE_EXCLUSIONS = [...DEFAULT_EXCLUSIONS, ...MEDIA_EXCLUSIONS];
/**
* Security-focused exclusions for paranoid users
*/
const PARANOID_EXCLUSIONS = [
...DEFAULT_EXCLUSIONS,
...MEDIA_EXCLUSIONS,
"Mail",
"Messages",
"Safari",
"Library/Mail",
"Library/Messages",
"Library/Safari",
"Library/Cookies",
"Library/Preferences",
"System",
"usr",
"opt",
"var",
"etc",
"proc",
"dev",
"sys"
];
/**
* Get exclusion patterns based on security level
*/
const getExclusionPatterns = (level = "default") => {
return match(level).with("conservative", () => CONSERVATIVE_EXCLUSIONS).with("paranoid", () => PARANOID_EXCLUSIONS).with("default", () => DEFAULT_EXCLUSIONS).exhaustive();
};
/**
* Check if a directory name should be excluded based on security patterns
*/
const isSecuritySensitive = (dirName) => {
return SECURITY_EXCLUSIONS.some((pattern) => dirName === pattern || dirName.includes(pattern));
};
/**
* Check if a directory is development-related
*/
const isDevelopmentDirectory = (dirName) => {
return DEVELOPMENT_EXCLUSIONS.some((pattern) => dirName === pattern || dirName.includes(pattern));
};
if (import.meta.vitest != null) {
const { describe, test, expect } = import.meta.vitest;
describe("Exclusion Patterns", () => {
test("should identify security-sensitive directories", () => {
expect(isSecuritySensitive(".ssh")).toBe(true);
expect(isSecuritySensitive(".aws")).toBe(true);
expect(isSecuritySensitive("safe-directory")).toBe(false);
});
test("should identify development directories", () => {
expect(isDevelopmentDirectory("node_modules")).toBe(true);
expect(isDevelopmentDirectory(".git")).toBe(true);
expect(isDevelopmentDirectory("src")).toBe(false);
});
test("should return correct exclusion patterns by level", () => {
const defaultExclusions = getExclusionPatterns("default");
const conservativeExclusions = getExclusionPatterns("conservative");
const paranoidExclusions = getExclusionPatterns("paranoid");
expect(defaultExclusions.length).toBeLessThan(conservativeExclusions.length);
expect(conservativeExclusions.length).toBeLessThan(paranoidExclusions.length);
expect(defaultExclusions).toContain(".ssh");
expect(defaultExclusions).toContain("node_modules");
expect(conservativeExclusions).toContain("Downloads");
expect(paranoidExclusions).toContain("Mail");
});
test("should have non-empty exclusion lists", () => {
expect(SECURITY_EXCLUSIONS.length).toBeGreaterThan(0);
expect(DEVELOPMENT_EXCLUSIONS.length).toBeGreaterThan(0);
expect(MEDIA_EXCLUSIONS.length).toBeGreaterThan(0);
});
});
}
//#endregion
//#region src/fast-scanner.ts
var import_usingCtx$2 = __toESM(require_usingCtx(), 1);
const CLAUDE_FILE_REGEX = /^CLAUDE\.(md|local\.md)$/;
/**
* Create a base crawler with common configuration
*/
const createBaseCrawler = (options$1) => {
const crawler = new fdir().withFullPaths().exclude((dirName) => {
const exclusions = DEFAULT_EXCLUSIONS;
if (exclusions.includes(dirName)) return true;
if (!options$1.includeHidden && dirName.startsWith(".") && dirName !== ".claude") return true;
return false;
});
return options$1.recursive ? crawler.withMaxDepth(options$1.maxDepth) : crawler.withMaxDepth(options$1.maxDepth);
};
/**
* Find Claude configuration files using fdir
* Fast file scanner using fdir (fastest directory crawler for Node.js)
* Can crawl 1 million files in < 1 second
*/
const findClaudeFiles = async (options$1 = {}) => {
const { path = process.cwd(), recursive = true, includeHidden = false } = options$1;
const crawler = createBaseCrawler({
includeHidden,
recursive,
maxDepth: recursive ? 20 : 1
}).filter((filePath) => {
const fileName = basename(filePath);
return CLAUDE_FILE_REGEX.test(fileName);
});
try {
const files = await crawler.crawl(path).withPromise();
return files;
} catch (error) {
console.warn(`Failed to scan Claude files in ${path}: ${error instanceof Error ? error.message : "Unknown error"}`);
return [];
}
};
/**
* Find slash command files using fdir
*/
const findSlashCommands = async (options$1 = {}) => {
const { path = process.cwd(), recursive = true, includeHidden = false } = options$1;
const crawler = createBaseCrawler({
includeHidden,
recursive,
maxDepth: recursive ? 20 : 3
}).filter((filePath) => {
return (filePath.includes("/.claude/commands/") || filePath.includes("/commands/")) && filePath.endsWith(".md");
});
try {
const files = await crawler.crawl(path).withPromise();
return files;
} catch (error) {
console.warn(`Failed to scan slash commands in ${path}: ${error instanceof Error ? error.message : "Unknown error"}`);
return [];
}
};
/**
* Find subagent files using fdir
*/
const findSubAgents = async (options$1 = {}) => {
const { path = process.cwd(), recursive = true, includeHidden = false } = options$1;
const results = [];
const projectCrawler = createBaseCrawler({
includeHidden,
recursive,
maxDepth: recursive ? 20 : 4
}).filter((filePath) => {
return filePath.includes("/.claude/agents/") && filePath.endsWith(".md");
});
try {
const projectFiles = await projectCrawler.crawl(path).withPromise();
results.push(...projectFiles);
} catch (error) {
console.warn(`Failed to scan project subagents in ${path}: ${error instanceof Error ? error.message : "Unknown error"}`);
}
const userAgentsPath = `${homedir()}/.claude/agents`;
const userCrawler = new fdir().withFullPaths().filter((filePath) => filePath.endsWith(".md"));
try {
const userFiles = await userCrawler.crawl(userAgentsPath).withPromise();
results.push(...userFiles);
} catch (_error) {}
return results;
};
const findSettingsJson = async (options$1 = {}) => {
const { path = process.cwd(), recursive = true, includeHidden = false } = options$1;
const crawler = createBaseCrawler({
includeHidden,
recursive,
maxDepth: recursive ? 20 : 4
}).filter((filePath) => {
const fileName = basename(filePath);
return filePath.includes("/.claude/") && (fileName === "settings.json" || fileName === "settings.local.json");
});
try {
const files = await crawler.crawl(path).withPromise();
return files;
} catch (error) {
console.warn(`Failed to scan settings.json files in ${path}: ${error instanceof Error ? error.message : "Unknown error"}`);
return [];
}
};
if (import.meta.vitest != null) {
/**
* Check if fdir is available (always true since it's a dependency)
* Internal function for testing only
*/
const isAvailable = async () => {
return true;
};
/**
* Get fdir version information
* Internal function for testing only
*/
const getVersion = async () => {
try {
const pkg = await import("fdir/package.json");
return `fdir ${pkg.version}`;
} catch {
return "fdir (version unknown)";
}
};
const { describe, test, expect } = import.meta.vitest;
const { createClaudeProjectFixture, createComplexProjectFixture, withTempFixture, DEFAULT_CLAUDE_MD } = await import("./test-fixture-helpers-jC6qse8O.js");
describe("fast-scanner", () => {
test("should be available after installation", async () => {
const available = await isAvailable();
expect(available).toBe(true);
});
test("should return version information", async () => {
const version = await getVersion();
expect(version).toMatch(/fdir/);
});
test("should find CLAUDE.md files with fs-fixture", async () => {
try {
var _usingCtx = (0, import_usingCtx$2.default)();
const fixture = _usingCtx.a(await createClaudeProjectFixture({
projectName: "scanner-test",
includeLocal: true
}));
const files = await findClaudeFiles({
path: fixture.getPath("scanner-test"),
recursive: false
});
expect(Array.isArray(files)).toBe(true);
expect(files.length).toBe(2);
expect(files.some((file) => file.endsWith("CLAUDE.md"))).toBe(true);
expect(files.some((file) => file.endsWith("CLAUDE.local.md"))).toBe(true);
} catch (_) {
_usingCtx.e = _;
} finally {
await _usingCtx.d();
}
});
test("should respect recursive option with nested structure", async () => {
try {
var _usingCtx3 = (0, import_usingCtx$2.default)();
const _fixture = _usingCtx3.a(await withTempFixture({ project: {
"CLAUDE.md": DEFAULT_CLAUDE_MD,
nested: { deep: { "CLAUDE.md": DEFAULT_CLAUDE_MD } }
} }, async (f) => {
const nonRecursive = await findClaudeFiles({
path: f.getPath("project"),
recursive: false
});
const recursive = await findClaudeFiles({
path: f.getPath("project"),
recursive: true
});
expect(nonRecursive.length).toBe(1);
expect(recursive.length).toBe(2);
return f;
}));
} catch (_) {
_usingCtx3.e = _;
} finally {
await _usingCtx3.d();
}
});
test("should find slash command files in complex structure", async () => {
try {
var _usingCtx4 = (0, import_usingCtx$2.default)();
const fixture = _usingCtx4.a(await createComplexProjectFixture());
const commands = await findSlashCommands({
path: fixture.getPath("my-app"),
recursive: true
});
expect(Array.isArray(commands)).toBe(true);
expect(commands.length).toBeGreaterThan(0);
expect(commands.some((cmd) => cmd.includes("test.md"))).toBe(true);
expect(commands.some((cmd) => cmd.includes("production/deploy.md"))).toBe(true);
} catch (_) {
_usingCtx4.e = _;
} finally {
await _usingCtx4.d();
}
});
test("should handle non-existent paths gracefully", async () => {
const files = await findClaudeFiles({
path: "/non/existent/path",
recursive: false
});
expect(Array.isArray(files)).toBe(true);
expect(files.length).toBe(0);
});
test("should find settings.json files", async () => {
try {
var _usingCtx5 = (0, import_usingCtx$2.default)();
const _fixture = _usingCtx5.a(await withTempFixture({
".claude": {
"settings.json": JSON.stringify({ version: "1.0" }),
"settings.local.json": JSON.stringify({ local: true })
},
project: { ".claude": { "settings.json": JSON.stringify({ project: true }) } }
}, async (f) => {
const settings = await findSettingsJson({
path: f.path,
recursive: true
});
expect(Array.isArray(settings)).toBe(true);
expect(settings.length).toBe(3);
expect(settings.some((file) => file.endsWith("settings.json"))).toBe(true);
expect(settings.some((file) => file.endsWith("settings.local.json"))).toBe(true);
return f;
}));
} catch (_) {
_usingCtx5.e = _;
} finally {
await _usingCtx5.d();
}
});
test("should respect exclude patterns", async () => {
try {
var _usingCtx6 = (0, import_usingCtx$2.default)();
const _fixture = _usingCtx6.a(await withTempFixture({ "test-project": {
"CLAUDE.md": DEFAULT_CLAUDE_MD,
node_modules: { "CLAUDE.md": "Should be excluded" },
".git": { "CLAUDE.md": "Should be excluded" }
} }, async (f) => {
const files = await findClaudeFiles({
path: f.getPath("test-project"),
recursive: true
});
expect(files.length).toBe(1);
expect(files[0]).toContain("test-project/CLAUDE.md");
expect(files[0]).not.toContain("node_modules");
expect(files[0]).not.toContain(".git");
return f;
}));
} catch (_) {
_usingCtx6.e = _;
} finally {
await _usingCtx6.d();
}
});
test("should handle large directory structures efficiently", async () => {
try {
var _usingCtx7 = (0, import_usingCtx$2.default)();
const largeStructure = {};
for (let i = 0; i < 100; i++) largeStructure[`dir-${i}`] = {
"CLAUDE.md": `Content ${i}`,
sub: { "CLAUDE.local.md": `Local ${i}` }
};
const _fixture = _usingCtx7.a(await withTempFixture(largeStructure, async (f) => {
const start = Date.now();
const files = await findClaudeFiles({
path: f.path,
recursive: true
});
const duration = Date.now() - start;
expect(files.length).toBe(200);
expect(duration).toBeLessThan(1e3);
return f;
}));
} catch (_) {
_usingCtx7.e = _;
} finally {
await _usingCtx7.d();
}
});
test.skip("should handle hidden directories correctly", async () => {
try {
var _usingCtx8 = (0, import_usingCtx$2.default)();
const _fixture = _usingCtx8.a(await withTempFixture({
".hidden": { "CLAUDE.md": DEFAULT_CLAUDE_MD },
".claude": { commands: { "test.md": "Test command" } },
visible: { "CLAUDE.md": DEFAULT_CLAUDE_MD }
}, async (f) => {
const withoutHidden = await findClaudeFiles({
path: f.path,
recursive: true,
includeHidden: false
});
const withHidden = await findClaudeFiles({
path: f.path,
recursive: true,
includeHidden: true
});
expect(withoutHidden.length).toBe(1);
expect(withHidden.length).toBe(2);
const commands = await findSlashCommands({
path: f.path,
recursive: true,
includeHidden: false
});
expect(Array.isArray(commands)).toBe(true);
expect(commands.length).toBe(1);
return f;
}));
} catch (_) {
_usingCtx8.e = _;
} finally {
await _usingCtx8.d();
}
});
});
}
//#endregion
//#region src/claude-md-scanner.ts
var import_usingCtx$1 = __toESM(require_usingCtx(), 1);
const scanClaudeFiles = async (options$1 = {}) => {
const { path = process.cwd(), recursive = true, includeHidden = false } = options$1;
try {
const files = await findClaudeFiles({
path,
recursive,
includeHidden
});
if (recursive) {
const { homedir: homedir$1 } = await import("node:os");
const homeDir = homedir$1();
if (path !== homeDir && path !== join(homeDir, ".claude")) {
const globalClaudeDir = join(homeDir, ".claude");
if (existsSync(globalClaudeDir)) {
const globalFiles = await findClaudeFiles({
path: globalClaudeDir,
recursive: true,
includeHidden
});
files.push(...globalFiles);
}
const homeClaudeFile = join(homeDir, "CLAUDE.md");
if (existsSync(homeClaudeFile)) files.push(homeClaudeFile);
try {
const homeContents = await readdir(homeDir, { withFileTypes: true });
const directoriesToSkip = new Set([
".cache",
".npm",
".yarn",
".pnpm",
"node_modules",
".git",
".svn",
".hg",
"Library",
"Applications",
".Trash",
".local",
".config",
".vscode",
".idea"
]);
const projectDirectories = new Set([
"my_programs",
"projects",
"dev",
"development",
"workspace",
"work",
"code",
"repos",
"git",
"Documents",
"Desktop",
"src",
"source"
]);
for (const entry of homeContents) if (entry.isDirectory() && !directoriesToSkip.has(entry.name) && !entry.name.startsWith(".")) {
const dirPath = join(homeDir, entry.name);
const claudeMdPath = join(dirPath, "CLAUDE.md");
if (existsSync(claudeMdPath)) files.push(claudeMdPath);
const claudeLocalPath = join(dirPath, "CLAUDE.local.md");
if (existsSync(claudeLocalPath)) files.push(claudeLocalPath);
if (projectDirectories.has(entry.name)) try {
const projectFiles = await findClaudeFiles({
path: dirPath,
recursive: true,
includeHidden: false
});
files.push(...projectFiles);
} catch (error) {
console.warn(`Failed to scan ${entry.name} subdirectories:`, error);
}
}
} catch (error) {
console.warn("Failed to scan home subdirectories:", error);
}
}
}
const uniqueFiles = uniq(files);
const fileInfos = [];
for (const filePath of uniqueFiles) try {
const fileInfo = await processClaudeFile(filePath);
if (fileInfo) fileInfos.push(fileInfo);
} catch (error) {
console.warn(`Failed to process file: ${filePath}`, error);
}
return fileInfos.sort((a$1, b$1) => b$1.lastModified.getTime() - a$1.lastModified.getTime());
} catch (error) {
throw new Error(`Failed to scan Claude files: ${isError(error) ? error.message : "Unknown error"}`);
}
};
const getSearchPatterns = (type, recursive = true) => {
const patterns = [];
const prefix = recursive ? "**/" : "";
if (!type || type === "project-memory") patterns.push(`${prefix}CLAUDE.md`);
if (!type || type === "project-memory-local") patterns.push(`${prefix}CLAUDE.local.md`);
if (!type || type === "user-memory") patterns.push(CLAUDE_FILE_PATTERNS.GLOBAL_CLAUDE_MD);
if (!type || type === "project-command") patterns.push(`${prefix}.claude/commands/**/*.md`);
if (!type || type === "personal-command") patterns.push(CLAUDE_FILE_PATTERNS.USER_SLASH_COMMANDS);
return patterns;
};
var ClaudeMdScanner = class extends BaseFileScanner {
maxFileSize = FILE_SIZE_LIMITS.MAX_CLAUDE_MD_SIZE;
fileType = "Claude.md";
async parseContent(filePath, content, stats) {
if (!validateClaudeMdContent(content)) {
console.warn(`Invalid Claude.md content, skipping: ${filePath}`);
return null;
}
const fileType = detectClaudeFileType(filePath);
const tags = extractTagsFromContent(content);
const commands = extractCommandsFromContent(content);
return {
path: createClaudeFilePath(filePath),
type: fileType,
size: stats.size,
lastModified: stats.mtime,
commands,
tags
};
}
};
const scanner$2 = new ClaudeMdScanner();
const processClaudeFile = (filePath) => scanner$2.processFile(filePath);
if (import.meta.vitest != null) {
const { describe, test, expect } = import.meta.vitest;
const { createClaudeProjectFixture, createComplexProjectFixture, withTempFixture, DEFAULT_CLAUDE_MD } = await import("./test-fixture-helpers-jC6qse8O.js");
describe("getSearchPatterns", () => {
test("should return all patterns when no type specified", () => {
const patterns = getSearchPatterns(void 0, true);
expect(patterns).toContain("**/CLAUDE.md");
expect(patterns).toContain("**/CLAUDE.local.md");
expect(patterns).toContain("**/.claude/commands/**/*.md");
});
test("should return specific pattern for project-memory type", () => {
const patterns = getSearchPatterns("project-memory", true);
expect(patterns).toContain("**/CLAUDE.md");
expect(patterns).not.toContain("**/CLAUDE.local.md");
});
test("should respect recursive option", () => {
const patterns = getSearchPatterns("project-memory", false);
expect(patterns).toContain("CLAUDE.md");
expect(patterns).not.toContain("**/CLAUDE.md");
});
test("should return user slash commands pattern for personal-command type", () => {
const patterns = getSearchPatterns("personal-command", true);
expect(patterns).toHaveLength(1);
expect(patterns[0]).toContain(".claude/commands/");
});
test("should include personal-command pattern when no type specified", () => {
const patterns = getSearchPatterns(void 0, true);
expect(patterns.some((p) => p.includes(".claude/commands/") && p.includes("/"))).toBe(true);
});
});
describe("scanClaudeFiles", () => {
test("should scan files in a fixture directory", async () => {
try {
var _usingCtx = (0, import_usingCtx$1.default)();
const fixture = _usingCtx.a(await createClaudeProjectFixture({
projectName: "test-scan",
includeLocal: true,
includeCommands: true
}));
const result = await scanClaudeFiles({
path: fixture.getPath("test-scan"),
recursive: false
});
expect(Array.isArray(result)).toBe(true);
expect(result.length).toBe(2);
const types = result.map((file) => file.type);
expect(types).toContain("project-memory");
expect(types).toContain("project-memory-local");
} catch (_) {
_usingCtx.e = _;
} finally {
await _usingCtx.d();
}
}, 1e4);
test("should handle empty directory", async () => {
try {
var _usingCtx3 = (0, import_usingCtx$1.default)();
const _fixture = _usingCtx3.a(await withTempFixture({ "empty-dir": {} }, async (f) => {
const result = await scanClaudeFiles({
path: f.getPath("empty-dir"),
recursive: false
});
expect(result).toEqual([]);
return f;
}));
} catch (_) {
_usingCtx3.e = _;
} finally {
await _usingCtx3.d();
}
});
test("should use current directory as default path", async () => {
const options$1 = {};
expect(options$1.path).toBeUndefined();
});
test("should sort files by last modified date", async () => {
try {
var _usingCtx4 = (0, import_usingCtx$1.default)();
const fixture = _usingCtx4.a(await createClaudeProjectFixture({
projectName: "sort-test",
includeLocal: true
}));
await new Promise((resolve$1) => setTimeout(resolve$1, 10));
await fixture.writeFile("sort-test/CLAUDE.local.md", `${DEFAULT_CLAUDE_MD}\n// Updated`);
const result = await scanClaudeFiles({
path: fixture.getPath("sort-test"),
recursive: false
});
expect(result[0]?.type).toBe("project-memory-local");
expect(result[1]?.type).toBe("project-memory");
} catch (_) {
_usingCtx4.e = _;
} finally {
await _usingCtx4.d();
}
});
});
describe("processClaudeFile", () => {
test("should return null for non-existent file", async () => {
const result = await processClaudeFile("/non/existent/file.md");
expect(result).toBeNull();
});
test("should process valid CLAUDE.md file", async () => {
try {
var _usingCtx5 = (0, import_usingCtx$1.default)();
const fixture = _usingCtx5.a(await createClaudeProjectFixture({ projectName: "process-test" }));
const filePath = fixture.getPath("process-test/CLAUDE.md");
const result = await processClaudeFile(filePath);
expect(result).not.toBeNull();
expect(result?.type).toBe("project-memory");
expect(result?.path).toBe(filePath);
expect(result?.size).toBeGreaterThan(0);
} catch (_) {
_usingCtx5.e = _;
} finally {
await _usingCtx5.d();
}
});
test("should extract project info", async () => {
try {
var _usingCtx6 = (0, import_usingCtx$1.default)();
const fixture = _usingCtx6.a(await createComplexProjectFixture());
const filePath = fixture.getPath("my-app/CLAUDE.md");
const result = await processClaudeFile(filePath);
expect(result).toBeDefined();
expect(result?.type).toBe("project-memory");
} catch (_) {
_usingCtx6.e = _;
} finally {
await _usingCtx6.d();
}
});
});
describe("findGlobalClaudeFiles", () => {
test("should find Claude files in complex project structure", async () => {
try {
var _usingCtx7 = (0, import_usingCtx$1.default)();
const fixture = _usingCtx7.a(await createComplexProjectFixture());
const result = await scanClaudeFiles({
path: fixture.getPath("my-app"),
recursive: false
});
expect(result.length).toBe(2);
const types = result.map((f) => f.type);
expect(types).toContain("project-memory");
expect(types).toContain("project-memory-local");
} catch (_) {
_usingCtx7.e = _;
} finally {
await _usingCtx7.d();
}
});
test("should handle includeHidden option", async () => {
try {
var _usingCtx8 = (0, import_usingCtx$1.default)();
const { createFixture } = await import("./dist-D99sJLU7.js");
const fixture = _usingCtx8.a(await createFixture({
".hidden": { "CLAUDE.md": DEFAULT_CLAUDE_MD },
visible: { "CLAUDE.md": DEFAULT_CLAUDE_MD }
}));
const withoutHidden = await scanClaudeFiles({
path: fixture.path,
recursive: false,
includeHidden: false
});
const withHidden = await scanClaudeFiles({
path: fixture.path,
recursive: false,
includeHidden: true
});
expect(withoutHidden.length).toBe(1);
expect(withHidden.length).toBe(2);
} catch (_) {
_usingCtx8.e = _;
} finally {
await _usingCtx8.d();
}
}, 1e4);
});
}
//#endregion
//#region src/settings-json-scanner.ts
/**
* Settings JSON scanner for parsing .claude/project/settings.json files
*/
var SettingsJsonScanner = class extends BaseFileScanner {
maxFileSize = 1024 * 1024;
fileType = "settings.json";
async parseContent(filePath, content, stats) {
try {
JSON.parse(content);
const tags = [];
return {
path: createClaudeFilePath(filePath),
type: detectClaudeFileType(filePath),
size: stats.size,
lastModified: stats.mtime,
commands: [],
tags
};
} catch (error) {
console.warn(`Invalid JSON in settings file ${filePath}:`, error);
return null;
}
}
};
/**
* Scan for settings.json files in .claude/project directories
*/
const scanSettingsJson = async (options$1 = {}) => {
const { path = process.cwd(), recursive = true, includeHidden = false } = options$1;
const paths = await findSettingsJson({
path,
recursive,
includeHidden
});
if (recursive) {
const { homedir: homedir$1 } = await import("node:os");
const { join: join$1 } = await import("node:path");
const globalClaudePath = join$1(homedir$1(), ".claude");
if (globalClaudePath !== path && !path.startsWith(globalClaudePath)) {
const globalFiles = await findSettingsJson({
path: globalClaudePath,
recursive: false,
includeHidden
});
paths.push(...globalFiles);
}
}
const uniquePaths = Array.from(new Set(paths));
const scanner$3 = new SettingsJsonScanner();
const results = await Promise.all(uniquePaths.map((path$1) => scanner$3.processFile(path$1)));
return results.filter((file) => file !== null);
};
if (import.meta.vitest != null) {
const { describe, test, expect } = import.meta.vitest;
const { createFixture } = await import("./dist-D99sJLU7.js");
describe("SettingsJsonScanner", () => {
test("should parse valid settings.json files", async () => {
const scanner$3 = new SettingsJsonScanner();
const fixture = await createFixture({ ".claude": { "settings.json": JSON.stringify({
version: "1.0",
features: ["feature1", "feature2"]
}) } });
try {
const result = await scanner$3.processFile(`${fixture.path}/.claude/settings.json`);
expect(result).toBeTruthy();
expect(result?.type).toBe("project-settings");
expect(result?.path).toContain("settings.json");
} finally {
await fixture.rm();
}
});
test("should handle invalid JSON gracefully", async () => {
const scanner$3 = new SettingsJsonScanner();
const fixture = await createFixture({ ".claude": { "settings.json": "{ invalid json" } });
try {
const result = await scanner$3.processFile(`${fixture.path}/.claude/settings.json`);
expect(result).toBeNull();
} finally {
await fixture.rm();
}
});
test("should handle large files", async () => {
const scanner$3 = new SettingsJsonScanner();
const largeContent = JSON.stringify({ data: "x".repeat(1048577) });
const fixture = await createFixture({ ".claude": { "settings.json": largeContent } });
try {
const result = await scanner$3.processFile(`${fixture.path}/.claude/settings.json`);
expect(result).toBeNull();
} finally {
await fixture.rm();
}
});
});
describe("scanSettingsJson", () => {
test("should scan multiple settings.json files", async () => {
const fixture = await createFixture({
project1: { ".claude": {
"settings.json": JSON.stringify({ project: "project1" }),
"settings.local.json": JSON.stringify({ local: true })
} },
project2: { ".claude": { "settings.json": JSON.stringify({ project: "project2" }) } }
});
try {
const results = await scanSettingsJson({
path: fixture.path,
recursive: false
});
expect(results).toHaveLength(3);
expect(results.filter((file) => file.type === "project-settings")).toHaveLength(2);
expect(results.filter((file) => file.type === "project-settings-local")).toHaveLength(1);
} finally {
await fixture.rm();
}
}, 1e4);
});
}
//#endregion
//#region src/slash-command-scanner.ts
const scanSlashCommands = async (options$1 = {}) => {
const { path = process.cwd(), recursive = true, includeHidden = false } = options$1;
const _patterns = getSlashCommandPatterns(recursive);
const searchPaths = [path, join(homedir(), ".claude", "commands")];
try {
const allCommands = [];
for (const searchPath of searchPaths) {
if (!existsSync(searchPath)) continue;
const files = await findS