Skip to content

Commit

Permalink
Merge branch 'feature/improve-cli'
Browse files Browse the repository at this point in the history
  • Loading branch information
yamadashy committed Jul 15, 2024
2 parents 2a2c416 + de2a836 commit 9a5985e
Show file tree
Hide file tree
Showing 14 changed files with 165 additions and 147 deletions.
58 changes: 35 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ Repopack is a powerful tool that packs your entire repository into a single, AI-
- **Simple to Use**: Just one command to pack your entire repository.
- **Customizable**: Easily configure what to include or exclude.
- **Git-Aware**: Automatically respects your .gitignore files.
- **Verbose Mode**: Detailed logging for debugging and understanding the packing process.

## 🛠 Installation

You can install Repopack globally using npm:

```bash
npm install -g repopack
```
Expand All @@ -26,47 +27,58 @@ Or if you prefer using Yarn:
yarn global add repopack
```

Alternatively, you can use npx to run Repopack without installing it:

```bash
npx repopack
```

## 📊 Usage

Navigate to your project directory and run:
To pack your entire repository:

```bash
repopack
```

This will create a `repopack-output.txt` file containing your entire codebase.
To pack specific files or directories:

```bash
repopack path/to/file1 path/to/directory
```

### Command Line Options

- `-o, --output <file>`: Specify the output file name (default: repopack-output.txt)
- `-i, --ignore <items>`: Comma-separated list of additional items to ignore
- `-c, --config <path>`: Path to a custom config file (default: repopack.config.js)
- `--no-default-ignore`: Disable the default ignore list
- `-o, --output <file>`: Specify the output file name
- `-i, --ignore <patterns>`: Additional ignore patterns (comma-separated)
- `-c, --config <path>`: Path to a custom config file
- `-v, --verbose`: Enable verbose logging

Example:
Examples:
```bash
repopack -o custom-output.txt -i "*.log,tmp" -v
repopack -o custom-output.txt
repopack -i "*.log,tmp" -v
repopack -c ./custom-config.json
npx repopack src tests
```

This will create a packed file containing the specified files or the entire repository.

## ⚙️ Configuration

Create a `repopack.config.js` file in your project root for custom configurations:
Create a `repopack.config.json` file in your project root for custom configurations:

```javascript
/** @type {import('repopack').RepopackConfig} */
const config = {
output: {
filePath: 'custom-output.txt',
headerText: 'Custom header information for the packed file',
```json
{
"output": {
"filePath": "custom-output.txt",
"headerText": "Custom header information for the packed file."
},
ignore: {
useDefaultPatterns: true,
customPatterns: ['additional-folder', '*.log'],
},
};

export default config;
"ignore": {
"useDefaultPatterns": true,
"customPatterns": ["additional-folder", "*.log"]
}
}
```

## 📄 Output Format
Expand Down
28 changes: 0 additions & 28 deletions repopack.config.js

This file was deleted.

10 changes: 10 additions & 0 deletions repopack.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"output": {
"filePath": "repopack-output.txt",
"headerText": "This repository contains the source code for the Repopack tool.\nRepopack is designed to pack repository contents into a single file,\nmaking it easier for AI systems to analyze and process the codebase.\n\nKey Features:\n- Configurable ignore patterns\n- Custom header text support\n- Efficient file processing and packing\n\nPlease refer to the README.md file for more detailed information on usage and configuration.\n"
},
"ignore": {
"useDefaultPatterns": true,
"customPatterns": []
}
}
115 changes: 51 additions & 64 deletions src/cli/index.ts
Original file line number Diff line number Diff line change
@@ -1,93 +1,58 @@
import { program } from 'commander';
import Spinner from '../utils/spinner.js';
import { program, OptionValues } from 'commander';
import path from 'path';
import { pack } from '../core/packager.js';
import { RepopackConfig } from '../types/index.js';
import { loadConfig, mergeConfigs } from '../config/configLoader.js';
import { RepopackConfigCli, RepopackConfigFile, RepopackConfigMerged } from '../types/index.js';
import { loadFileConfig, mergeConfigs } from '../config/configLoader.js';
import { logger } from '../utils/logger.js';
import { getVersion } from '../utils/packageJsonUtils.js';
import { handleError, RepopackError } from '../utils/errorHandler.js';
import Spinner from '../utils/spinner.js';
import pc from 'picocolors';
import { handleError } from '../utils/errorHandler.js';

export async function run() {
try {
await runInternal();
} catch (error) {
handleError(error);
}
interface CliOptions extends OptionValues {
output?: string;
ignore?: string;
config?: string;
verbose?: boolean;
}

async function runInternal() {
const version = await getVersion();

console.log(pc.dim(`\n📦 Repopack v${version}\n`));

program
.version(version)
.description('Repopack - Pack your repository into a single AI-friendly file')
.option('-o, --output <file>', 'specify the output file name (default: repopack-output.txt)')
.option('-i, --ignore <items>', 'comma-separated list of additional items to ignore')
.option('-c, --config <path>', 'path to a custom config file (default: repopack.config.js)')
.option('--no-default-ignore', 'disable the default ignore list')
.option('-v, --verbose', 'enable verbose logging for detailed output')
.addHelpText(
'after',
`
Example calls:
$ repopack
$ repopack -o custom-output.txt
$ repopack -i "*.log,tmp" -v
$ repopack -c ./custom-config.js
For more information, visit: https://github.com/yamadashy/repopack`,
)
.parse(process.argv);

const options = program.opts();

logger.setVerbose(options.verbose);

logger.trace('Command line options:', options);

const fileConfig = await loadConfig(options.config);
async function executeAction(directory: string, options: CliOptions) {
logger.setVerbose(options.verbose || false);

const fileConfig: RepopackConfigFile = await loadFileConfig(options.config ?? null);
logger.trace('Loaded file config:', fileConfig);

const cliConfig: Partial<RepopackConfig> = {
...(options.output && { output: { filePath: options.output } }),
ignore: {
useDefaultPatterns: options.defaultIgnore !== false,
customPatterns: options.ignore ? options.ignore.split(',') : undefined,
},
};

const cliConfig: RepopackConfigCli = {};
if (options.output) {
cliConfig.output = { filePath: options.output };
}
if (options.ignore) {
cliConfig.ignore = { customPatterns: options.ignore.split(',') };
}
logger.trace('CLI config:', cliConfig);

const config = mergeConfigs(fileConfig, cliConfig);
const config: RepopackConfigMerged = mergeConfigs(fileConfig, cliConfig);

logger.trace('Merged config:', config);

if (!config.output.filePath) {
throw new RepopackError(
'Output file is not specified. Please provide it in the config file or via command line option.',
);
}
// Ensure the output file is always in the current working directory
config.output.filePath = path.resolve(process.cwd(), path.basename(config.output.filePath));

console.log('');
const targetPath = path.resolve(directory);

const spinner = new Spinner('Packing files...');
spinner.start();

try {
const { totalFiles, totalCharacters } = await pack(process.cwd(), config);

const { totalFiles, totalCharacters } = await pack(targetPath, config);
spinner.succeed('Packing completed successfully!');

console.log('');
console.log(pc.white('📊 Pack Summary:'));
console.log(pc.dim('────────────────'));
console.log(`${pc.white('Total Files:')} ${pc.white(totalFiles.toString())}`);
console.log(`${pc.white('Total Chars:')} ${pc.white(totalCharacters.toString())}`);
console.log(`${pc.white(' Output:')} ${pc.white(config.output.filePath)}`);
console.log(`${pc.white('Total Files:')} ${pc.white(totalFiles.toString())}`);
console.log(`${pc.white('Total Chars:')} ${pc.white(totalCharacters.toString())}`);
console.log(`${pc.white(' Output:')} ${pc.white(config.output.filePath)}`);

console.log('');
console.log(pc.green('🎉 All Done!'));
Expand All @@ -97,3 +62,25 @@ For more information, visit: https://github.com/yamadashy/repopack`,
throw error;
}
}

export async function run() {
try {
const version = await getVersion();

console.log(pc.dim(`\n📦 Repopack v${version}\n`));

program
.version(version)
.description('Repopack - Pack your repository into a single AI-friendly file')
.arguments('[directory]')
.option('-o, --output <file>', 'specify the output file name')
.option('-i, --ignore <patterns>', 'additional ignore patterns (comma-separated)')
.option('-c, --config <path>', 'path to a custom config file')
.option('-v, --verbose', 'enable verbose logging for detailed output')
.action((directory = '.', options: CliOptions) => executeAction(directory, options));

await program.parseAsync(process.argv);
} catch (error) {
handleError(error);
}
}
20 changes: 11 additions & 9 deletions src/config/configLoader.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import path from 'path';
import { RepopackConfig } from '../types/index.js';
import { RepopackConfigCli as RepopackConfigCli, RepopackConfigFile, RepopackConfigMerged } from '../types/index.js';
import { defaultConfig } from './defaultConfig.js';
import { logger } from '../utils/logger.js';
import * as fs from 'fs/promises';
import { RepopackError } from '../utils/errorHandler.js';

const defaultConfigPath = 'repopack.config.js';
const defaultConfigPath = 'repopack.config.json';

export async function loadConfig(configPath: string | null): Promise<Partial<RepopackConfig>> {
export async function loadFileConfig(configPath: string | null): Promise<RepopackConfigFile> {
let useDefaultConfig = false;
if (!configPath) {
useDefaultConfig = true;
Expand All @@ -29,23 +30,24 @@ export async function loadConfig(configPath: string | null): Promise<Partial<Rep
);
return {};
} else {
throw new Error(`Config file not found at ${configPath}`);
throw new RepopackError(`Config file not found at ${configPath}`);
}
}

try {
const config = await import(fullPath);
return config.default || {};
const fileContent = await fs.readFile(fullPath, 'utf-8');
const config = JSON.parse(fileContent);
return config;
} catch (error) {
if (error instanceof Error) {
throw new Error(`Error loading config from ${configPath}: ${error.message}`);
throw new RepopackError(`Error loading config from ${configPath}: ${error.message}`);
} else {
throw new Error(`Error loading config from ${configPath}`);
throw new RepopackError(`Error loading config from ${configPath}`);
}
}
}

export function mergeConfigs(fileConfig: Partial<RepopackConfig>, cliConfig: Partial<RepopackConfig>): RepopackConfig {
export function mergeConfigs(fileConfig: RepopackConfigFile, cliConfig: RepopackConfigCli): RepopackConfigMerged {
return {
output: {
...defaultConfig.output,
Expand Down
4 changes: 2 additions & 2 deletions src/config/defaultConfig.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { RepopackConfig } from '../types/index.js';
import { RepopackConfigDefault } from '../types/index.js';

export const defaultConfig: RepopackConfig = {
export const defaultConfig: RepopackConfigDefault = {
output: {
filePath: 'repopack-output.txt',
},
Expand Down
2 changes: 1 addition & 1 deletion src/config/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export { defaultConfig } from './defaultConfig.js';
export { loadConfig, mergeConfigs } from './configLoader.js';
export { loadFileConfig as loadConfig, mergeConfigs } from './configLoader.js';
8 changes: 4 additions & 4 deletions src/core/outputGenerator.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { RepopackConfig } from '../types/index.js';
import { RepopackConfigMerged } from '../types/index.js';
import * as fs from 'fs/promises';
import path from 'path';

Expand All @@ -7,7 +7,7 @@ const LONG_SEPARATOR = '='.repeat(64);

export async function generateOutput(
rootDir: string,
config: RepopackConfig,
config: RepopackConfigMerged,
packedFiles: { path: string; content: string }[],
fsModule = fs,
): Promise<void> {
Expand All @@ -30,7 +30,7 @@ export async function generateOutput(
await fsModule.writeFile(outputPath, output.join('\n'));
}

export function generateFileHeader(config: RepopackConfig): string {
export function generateFileHeader(config: RepopackConfigMerged): string {
const defaultHeader = `${LONG_SEPARATOR}
REPOPACK OUTPUT FILE
${LONG_SEPARATOR}
Expand Down Expand Up @@ -74,7 +74,7 @@ For more information about Repopack, visit: https://github.com/yamadashy/repopac

let headerText = defaultHeader;

if (config.output.headerText) {
if (config.output?.headerText) {
headerText += `
Additional User-Provided Header:
--------------------------------
Expand Down
Loading

0 comments on commit 9a5985e

Please sign in to comment.