Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
99b3590
feat(stdin): Apply include and ignore patterns to stdin file lists
yamadashy Jun 21, 2025
6496923
fix(stdin): Add .gitignore/.repomixignore support and enable all tests
yamadashy Jun 22, 2025
e954df2
style: Fix linting issues
yamadashy Jun 22, 2025
a08b5ca
fix(file): Add comprehensive error handling to filterFileList function
yamadashy Jun 22, 2025
c772847
style(test): Fix linting error in error handling test
yamadashy Jun 22, 2025
98ee099
refactor(stdin): Unify file filtering logic by extending searchFiles …
yamadashy Jun 23, 2025
c5f6988
docs(rules): Add commit body guidelines to base rules
yamadashy Jun 23, 2025
cff072e
chore(ci): Update .gitignore for agent and private files
yamadashy Jun 25, 2025
54e7aa6
style(core): Format fileSearch files
yamadashy Jun 25, 2025
f9d55a6
fix(stdin): Correct predefinedFiles filtering and pattern normalization
yamadashy Jun 25, 2025
bcaf822
refactor(core): Improve fileSearch.ts readability by extracting neste…
yamadashy Jun 25, 2025
3acd9ef
style(core): Reorganize fileSearch.ts function order
yamadashy Jun 25, 2025
ce53402
refactor(stdin): Replace DI approach with direct predefinedFiles para…
yamadashy Jun 25, 2025
f8615e4
refactor(stdin): Simplify predefined files handling by merging with i…
yamadashy Jun 27, 2025
fcfdf25
fix(stdin): Always validate root directory even with predefined files
yamadashy Jun 27, 2025
c29698e
refactor(core): Simplify glob pattern normalization logic
yamadashy Jun 27, 2025
f9f2aff
refactor(stdin): Rename predefinedFiles to explicitFiles for better e…
yamadashy Jun 27, 2025
1ec8db9
fix(stdin): Correct include patterns logic when explicit files are pr…
yamadashy Jun 27, 2025
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
5 changes: 5 additions & 0 deletions .agents/rules/base.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ repomix/
- Description should be clear and concise in present tense
- Description must start with a capital letter

### Commit Body Guidelines
- Include context about what led to this commit
- Describe the conversation or problem that motivated the change
- This helps preserve the decision-making process for future reference

## Pull Request Guidelines
- All pull requests must follow the template:
```md
Expand Down
11 changes: 8 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,14 @@ repomix-output.md
# aider
.aider*

# private files
# repomix runner
.repomix/

# Agent
.cursor/rules/
.github/todo.md
.mcp.json
.agents/local/

# private files
.private/
.agents/local/
.env
10 changes: 3 additions & 7 deletions src/cli/actions/defaultAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,19 +81,15 @@ export const handleStdinProcessing = async (

spinner.update('Packing files...');

// Create a custom pack variant that uses the stdin file paths directly
// Use pack with predefined files from stdin
packResult = await pack(
[cwd],
config,
(message) => {
spinner.update(message);
},
{
searchFiles: async () => ({
filePaths: stdinResult.filePaths.map((filePath) => path.relative(cwd, filePath)),
emptyDirPaths: stdinResult.emptyDirPaths,
}),
},
{},
stdinResult.filePaths,
);
Comment thread
yamadashy marked this conversation as resolved.
} catch (error) {
spinner.fail('Error reading from stdin or during packing');
Expand Down
30 changes: 25 additions & 5 deletions src/core/file/fileSearch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,11 @@ export const normalizeGlobPattern = (pattern: string): string => {
};

// Get all file paths considering the config
export const searchFiles = async (rootDir: string, config: RepomixConfigMerged): Promise<FileSearchResult> => {
export const searchFiles = async (
rootDir: string,
config: RepomixConfigMerged,
explicitFiles?: string[],
): Promise<FileSearchResult> => {
// Check if the path exists and get its type
let pathStats: Stats;
try {
Expand Down Expand Up @@ -140,9 +144,6 @@ export const searchFiles = async (rootDir: string, config: RepomixConfigMerged):
);
}

const includePatterns =
config.include.length > 0 ? config.include.map((pattern) => escapeGlobPattern(pattern)) : ['**/*'];

try {
const [ignorePatterns, ignoreFilePatterns] = await Promise.all([
getIgnorePatterns(rootDir, config),
Expand All @@ -152,7 +153,6 @@ export const searchFiles = async (rootDir: string, config: RepomixConfigMerged):
// Normalize ignore patterns to handle trailing slashes consistently
const normalizedIgnorePatterns = ignorePatterns.map(normalizeGlobPattern);

logger.trace('Include patterns:', includePatterns);
logger.trace('Ignore patterns:', normalizedIgnorePatterns);
logger.trace('Ignore file patterns:', ignoreFilePatterns);

Expand All @@ -171,6 +171,26 @@ export const searchFiles = async (rootDir: string, config: RepomixConfigMerged):
}
}

// Start with configured include patterns
let includePatterns = config.include.map((pattern) => escapeGlobPattern(pattern));

// If explicit files are provided, add them to include patterns
if (explicitFiles) {
const relativePaths = explicitFiles.map((filePath) => {
const relativePath = path.relative(rootDir, filePath);
// Escape the path to handle special characters
return escapeGlobPattern(relativePath);
});
includePatterns = [...includePatterns, ...relativePaths];
Comment thread
yamadashy marked this conversation as resolved.
}

// If no include patterns at all, default to all files
if (includePatterns.length === 0) {
includePatterns = ['**/*'];
}

logger.trace('Include patterns with explicit files:', includePatterns);

const filePaths = await globby(includePatterns, {
cwd: rootDir,
ignore: [...adjustedIgnorePatterns],
Expand Down
3 changes: 2 additions & 1 deletion src/core/packager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export const pack = async (
config: RepomixConfigMerged,
progressCallback: RepomixProgressCallback = () => {},
overrideDeps: Partial<typeof defaultDeps> = {},
explicitFiles?: string[],
): Promise<PackResult> => {
const deps = {
...defaultDeps,
Expand All @@ -54,7 +55,7 @@ export const pack = async (
const filePathsByDir = await Promise.all(
rootDirs.map(async (rootDir) => ({
rootDir,
filePaths: (await deps.searchFiles(rootDir, config)).filePaths,
filePaths: (await deps.searchFiles(rootDir, config, explicitFiles)).filePaths,
})),
);

Expand Down
27 changes: 11 additions & 16 deletions tests/cli/actions/defaultAction.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import path from 'node:path';
import process from 'node:process';
import { globby } from 'globby';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import {
buildCliConfig,
Expand All @@ -14,6 +15,7 @@ import * as packageJsonParser from '../../../src/core/file/packageJsonParse.js';
import * as packager from '../../../src/core/packager.js';
import type { PackResult } from '../../../src/core/packager.js';

vi.mock('globby');
vi.mock('../../../src/core/packager');
vi.mock('../../../src/config/configLoad');
vi.mock('../../../src/core/file/packageJsonParse');
Expand All @@ -27,6 +29,8 @@ describe('defaultAction', () => {
vi.resetAllMocks();
vi.mocked(packageJsonParser.getVersion).mockResolvedValue('1.0.0');
vi.mocked(configLoader.loadFileConfig).mockResolvedValue({});
// Default globby mock
vi.mocked(globby).mockResolvedValue([]);
vi.mocked(configLoader.mergeConfigs).mockReturnValue({
cwd: process.cwd(),
input: {
Expand Down Expand Up @@ -684,24 +688,15 @@ describe('defaultAction', () => {

vi.mocked(fileStdin.readFilePathsFromStdin).mockResolvedValue(stdinResult);

await handleStdinProcessing(['.'], testCwd, mockConfig, mockCliOptions);
// Mock globby to return the expected filtered files (sorted by sortPaths)
vi.mocked(globby).mockResolvedValue([path.join('subdir', 'file2.txt'), 'file1.txt']);
Comment thread
yamadashy marked this conversation as resolved.

expect(packager.pack).toHaveBeenCalledWith([testCwd], mockConfig, expect.any(Function), {
searchFiles: expect.any(Function),
});
await handleStdinProcessing(['.'], testCwd, mockConfig, mockCliOptions);

// Test the searchFiles function
const packCall = vi.mocked(packager.pack).mock.calls[0];
const searchFiles = packCall[3]?.searchFiles;
expect(searchFiles).toBeDefined();

if (searchFiles) {
const searchResult = await searchFiles(testCwd, mockConfig);
expect(searchResult).toEqual({
filePaths: ['file1.txt', path.join('subdir', 'file2.txt')],
emptyDirPaths: [path.resolve(testCwd, 'emptydir')],
});
}
expect(packager.pack).toHaveBeenCalledWith([testCwd], mockConfig, expect.any(Function), {}, [
path.join(testCwd, 'file1.txt'),
path.join(testCwd, 'subdir', 'file2.txt'),
]);
Comment thread
yamadashy marked this conversation as resolved.
});

it('should propagate errors from readFilePathsFromStdin', async () => {
Expand Down
47 changes: 47 additions & 0 deletions tests/core/file/fileSearch.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -562,5 +562,52 @@ node_modules
expect(result.filePaths).toEqual(['test.js']);
expect(result.emptyDirPaths).toEqual([]);
});

test('should filter explicit files based on include and ignore patterns', async () => {
const mockConfig = createMockConfig({
include: ['**/*.ts'],
ignore: {
useGitignore: false,
useDefaultPatterns: false,
customPatterns: ['**/*.test.ts'],
},
});

const explicitFiles = [
'/test/src/file1.ts',
'/test/src/file1.test.ts',
'/test/src/file2.js',
'/test/src/file3.ts',
];

// Mock globby to return the expected filtered files
vi.mocked(globby).mockResolvedValue(['src/file1.ts', 'src/file3.ts']);
Comment thread
yamadashy marked this conversation as resolved.
Comment thread
yamadashy marked this conversation as resolved.

const result = await searchFiles('/test', mockConfig, explicitFiles);

expect(result.filePaths).toEqual(['src/file1.ts', 'src/file3.ts']);
expect(result.emptyDirPaths).toEqual([]);
});

test('should handle explicit files with ignore patterns only', async () => {
const mockConfig = createMockConfig({
include: [],
ignore: {
useGitignore: false,
useDefaultPatterns: false,
customPatterns: ['tests/**'],
},
});

const explicitFiles = ['/test/src/main.ts', '/test/tests/unit.test.ts', '/test/lib/utils.ts'];

// Mock globby to return the expected filtered files
vi.mocked(globby).mockResolvedValue(['src/main.ts', 'lib/utils.ts']);

const result = await searchFiles('/test', mockConfig, explicitFiles);

expect(result.filePaths).toEqual(['lib/utils.ts', 'src/main.ts']);
expect(result.emptyDirPaths).toEqual([]);
});
});
});
2 changes: 1 addition & 1 deletion tests/core/packager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ describe('packager', () => {
const progressCallback = vi.fn();
const result = await pack(['root'], mockConfig, progressCallback, mockDeps);

expect(mockDeps.searchFiles).toHaveBeenCalledWith('root', mockConfig);
expect(mockDeps.searchFiles).toHaveBeenCalledWith('root', mockConfig, undefined);
expect(mockDeps.collectFiles).toHaveBeenCalledWith(mockFilePaths, 'root', mockConfig, progressCallback);
expect(mockDeps.validateFileSafety).toHaveBeenCalled();
expect(mockDeps.processFiles).toHaveBeenCalled();
Expand Down
Loading